From 3565aad630864ecdbe53fdaa501ea708555b3c7c Mon Sep 17 00:00:00 2001 From: Unit 193 Date: Sun, 30 Apr 2023 18:30:36 -0400 Subject: New upstream version 3.4.4+dfsg. --- .github/FUNDING.yml | 1 + .github/workflows/lin_release.yml | 169 +- .github/workflows/mac_release.yml | 159 +- .github/workflows/remove_old_artifacts.yml | 19 + .github/workflows/sandbox_w64.yml | 105 + .github/workflows/tests.yml | 42 +- .github/workflows/update_messages.yml | 67 + .github/workflows/win32_release.yml | 264 + .github/workflows/win64_release.yml | 265 + .github/workflows/win_release.yml | 157 - .gitignore | 1 + .travis.yml | 49 - ChangeLog.md | 162 +- Plugins/ConfigMigration/ConfigMigration.pro | 19 +- Plugins/ConfigMigration/ConfigMigration_de.qm | Bin 494 -> 0 bytes Plugins/ConfigMigration/ConfigMigration_de.ts | 150 - Plugins/ConfigMigration/ConfigMigration_es.qm | Bin 23 -> 0 bytes Plugins/ConfigMigration/ConfigMigration_es.ts | 150 - Plugins/ConfigMigration/ConfigMigration_fr.qm | Bin 7804 -> 0 bytes Plugins/ConfigMigration/ConfigMigration_fr.ts | 150 - Plugins/ConfigMigration/ConfigMigration_it.qm | Bin 23 -> 0 bytes Plugins/ConfigMigration/ConfigMigration_it.ts | 150 - Plugins/ConfigMigration/ConfigMigration_pl.qm | Bin 7282 -> 0 bytes Plugins/ConfigMigration/ConfigMigration_pl.ts | 152 - Plugins/ConfigMigration/ConfigMigration_pt_BR.qm | Bin 23 -> 0 bytes Plugins/ConfigMigration/ConfigMigration_pt_BR.ts | 150 - Plugins/ConfigMigration/ConfigMigration_ro_RO.qm | Bin 30 -> 0 bytes Plugins/ConfigMigration/ConfigMigration_ro_RO.ts | 150 - Plugins/ConfigMigration/ConfigMigration_ru.qm | Bin 7193 -> 0 bytes Plugins/ConfigMigration/ConfigMigration_ru.ts | 150 - Plugins/ConfigMigration/ConfigMigration_sk.qm | Bin 27 -> 0 bytes Plugins/ConfigMigration/ConfigMigration_sk.ts | 150 - Plugins/ConfigMigration/ConfigMigration_zh_CN.qm | 1 - Plugins/ConfigMigration/ConfigMigration_zh_CN.ts | 150 - Plugins/ConfigMigration/configmigration.cpp | 6 +- Plugins/ConfigMigration/configmigration.qrc | 15 - .../translations/ConfigMigration.ts | 150 + .../translations/ConfigMigration_af_ZA.ts | 150 + .../translations/ConfigMigration_ar_SA.ts | 150 + .../translations/ConfigMigration_ca_ES.ts | 150 + .../translations/ConfigMigration_cs_CZ.ts | 150 + .../translations/ConfigMigration_da_DK.ts | 150 + .../translations/ConfigMigration_de_DE.ts | 150 + .../translations/ConfigMigration_el_GR.ts | 150 + .../translations/ConfigMigration_en_US.ts | 150 + .../translations/ConfigMigration_es_ES.ts | 150 + .../translations/ConfigMigration_fa_IR.ts | 150 + .../translations/ConfigMigration_fi_FI.ts | 150 + .../translations/ConfigMigration_fr_FR.ts | 150 + .../translations/ConfigMigration_he_IL.ts | 150 + .../translations/ConfigMigration_hu_HU.ts | 150 + .../translations/ConfigMigration_it_IT.ts | 150 + .../translations/ConfigMigration_ja_JP.ts | 150 + .../translations/ConfigMigration_kaa.ts | 150 + .../translations/ConfigMigration_ko_KR.ts | 150 + .../translations/ConfigMigration_nl_NL.ts | 150 + .../translations/ConfigMigration_no_NO.ts | 150 + .../translations/ConfigMigration_pl_PL.ts | 150 + .../translations/ConfigMigration_pt_BR.ts | 150 + .../translations/ConfigMigration_pt_PT.ts | 150 + .../translations/ConfigMigration_ro_RO.ts | 150 + .../translations/ConfigMigration_ru_RU.ts | 150 + .../translations/ConfigMigration_sk_SK.ts | 150 + .../translations/ConfigMigration_sr_SP.ts | 150 + .../translations/ConfigMigration_sv_SE.ts | 150 + .../translations/ConfigMigration_tr_TR.ts | 150 + .../translations/ConfigMigration_uk_UA.ts | 150 + .../translations/ConfigMigration_vi_VN.ts | 150 + .../translations/ConfigMigration_zh_CN.ts | 150 + .../translations/ConfigMigration_zh_TW.ts | 150 + Plugins/CsvExport/CsvExport.pro | 20 - Plugins/CsvExport/CsvExport_de.qm | Bin 23 -> 0 bytes Plugins/CsvExport/CsvExport_de.ts | 57 - Plugins/CsvExport/CsvExport_es.qm | Bin 23 -> 0 bytes Plugins/CsvExport/CsvExport_es.ts | 57 - Plugins/CsvExport/CsvExport_fr.qm | Bin 1039 -> 0 bytes Plugins/CsvExport/CsvExport_fr.ts | 57 - Plugins/CsvExport/CsvExport_it.qm | Bin 23 -> 0 bytes Plugins/CsvExport/CsvExport_it.ts | 57 - Plugins/CsvExport/CsvExport_pl.qm | Bin 953 -> 0 bytes Plugins/CsvExport/CsvExport_pl.ts | 57 - Plugins/CsvExport/CsvExport_pt_BR.qm | Bin 23 -> 0 bytes Plugins/CsvExport/CsvExport_pt_BR.ts | 57 - Plugins/CsvExport/CsvExport_ro_RO.qm | Bin 30 -> 0 bytes Plugins/CsvExport/CsvExport_ro_RO.ts | 57 - Plugins/CsvExport/CsvExport_ru.qm | Bin 996 -> 0 bytes Plugins/CsvExport/CsvExport_ru.ts | 57 - Plugins/CsvExport/CsvExport_sk.qm | Bin 919 -> 0 bytes Plugins/CsvExport/CsvExport_sk.ts | 57 - Plugins/CsvExport/CsvExport_zh_CN.qm | Bin 692 -> 0 bytes Plugins/CsvExport/CsvExport_zh_CN.ts | 57 - Plugins/CsvExport/csvexport.cpp | 4 +- Plugins/CsvExport/csvexport.qrc | 13 - Plugins/CsvExport/translations/CsvExport.ts | 57 + Plugins/CsvExport/translations/CsvExport_af_ZA.ts | 57 + Plugins/CsvExport/translations/CsvExport_ar_SA.ts | 57 + Plugins/CsvExport/translations/CsvExport_ca_ES.ts | 57 + Plugins/CsvExport/translations/CsvExport_cs_CZ.ts | 57 + Plugins/CsvExport/translations/CsvExport_da_DK.ts | 57 + Plugins/CsvExport/translations/CsvExport_de_DE.ts | 57 + Plugins/CsvExport/translations/CsvExport_el_GR.ts | 57 + Plugins/CsvExport/translations/CsvExport_en_US.ts | 57 + Plugins/CsvExport/translations/CsvExport_es_ES.ts | 57 + Plugins/CsvExport/translations/CsvExport_fa_IR.ts | 57 + Plugins/CsvExport/translations/CsvExport_fi_FI.ts | 57 + Plugins/CsvExport/translations/CsvExport_fr_FR.ts | 57 + Plugins/CsvExport/translations/CsvExport_he_IL.ts | 57 + Plugins/CsvExport/translations/CsvExport_hu_HU.ts | 57 + Plugins/CsvExport/translations/CsvExport_it_IT.ts | 57 + Plugins/CsvExport/translations/CsvExport_ja_JP.ts | 57 + Plugins/CsvExport/translations/CsvExport_kaa.ts | 57 + Plugins/CsvExport/translations/CsvExport_ko_KR.ts | 57 + Plugins/CsvExport/translations/CsvExport_nl_NL.ts | 57 + Plugins/CsvExport/translations/CsvExport_no_NO.ts | 57 + Plugins/CsvExport/translations/CsvExport_pl_PL.ts | 57 + Plugins/CsvExport/translations/CsvExport_pt_BR.ts | 57 + Plugins/CsvExport/translations/CsvExport_pt_PT.ts | 57 + Plugins/CsvExport/translations/CsvExport_ro_RO.ts | 57 + Plugins/CsvExport/translations/CsvExport_ru_RU.ts | 57 + Plugins/CsvExport/translations/CsvExport_sk_SK.ts | 57 + Plugins/CsvExport/translations/CsvExport_sr_SP.ts | 57 + Plugins/CsvExport/translations/CsvExport_sv_SE.ts | 57 + Plugins/CsvExport/translations/CsvExport_tr_TR.ts | 57 + Plugins/CsvExport/translations/CsvExport_uk_UA.ts | 57 + Plugins/CsvExport/translations/CsvExport_vi_VN.ts | 57 + Plugins/CsvExport/translations/CsvExport_zh_CN.ts | 57 + Plugins/CsvExport/translations/CsvExport_zh_TW.ts | 57 + Plugins/CsvImport/CsvImport.pro | 19 +- Plugins/CsvImport/CsvImportOptions.ui | 63 +- Plugins/CsvImport/CsvImport_de.qm | Bin 23 -> 0 bytes Plugins/CsvImport/CsvImport_de.ts | 85 - Plugins/CsvImport/CsvImport_es.qm | Bin 23 -> 0 bytes Plugins/CsvImport/CsvImport_es.ts | 85 - Plugins/CsvImport/CsvImport_fr.qm | Bin 2008 -> 0 bytes Plugins/CsvImport/CsvImport_fr.ts | 93 - Plugins/CsvImport/CsvImport_it.qm | Bin 23 -> 0 bytes Plugins/CsvImport/CsvImport_it.ts | 85 - Plugins/CsvImport/CsvImport_pl.qm | Bin 2515 -> 0 bytes Plugins/CsvImport/CsvImport_pl.ts | 97 - Plugins/CsvImport/CsvImport_pt_BR.qm | Bin 23 -> 0 bytes Plugins/CsvImport/CsvImport_pt_BR.ts | 85 - Plugins/CsvImport/CsvImport_ro_RO.qm | Bin 30 -> 0 bytes Plugins/CsvImport/CsvImport_ro_RO.ts | 85 - Plugins/CsvImport/CsvImport_ru.qm | Bin 2502 -> 0 bytes Plugins/CsvImport/CsvImport_ru.ts | 93 - Plugins/CsvImport/CsvImport_sk.qm | Bin 2035 -> 0 bytes Plugins/CsvImport/CsvImport_sk.ts | 89 - Plugins/CsvImport/CsvImport_zh_CN.qm | 1 - Plugins/CsvImport/CsvImport_zh_CN.ts | 85 - Plugins/CsvImport/csvimport.cpp | 5 +- Plugins/CsvImport/csvimport.h | 1 + Plugins/CsvImport/csvimport.qrc | 13 - Plugins/CsvImport/translations/CsvImport.ts | 95 + Plugins/CsvImport/translations/CsvImport_af_ZA.ts | 95 + Plugins/CsvImport/translations/CsvImport_ar_SA.ts | 95 + Plugins/CsvImport/translations/CsvImport_ca_ES.ts | 95 + Plugins/CsvImport/translations/CsvImport_cs_CZ.ts | 95 + Plugins/CsvImport/translations/CsvImport_da_DK.ts | 95 + Plugins/CsvImport/translations/CsvImport_de_DE.ts | 95 + Plugins/CsvImport/translations/CsvImport_el_GR.ts | 95 + Plugins/CsvImport/translations/CsvImport_en_US.ts | 95 + Plugins/CsvImport/translations/CsvImport_es_ES.ts | 95 + Plugins/CsvImport/translations/CsvImport_fa_IR.ts | 95 + Plugins/CsvImport/translations/CsvImport_fi_FI.ts | 95 + Plugins/CsvImport/translations/CsvImport_fr_FR.ts | 95 + Plugins/CsvImport/translations/CsvImport_he_IL.ts | 95 + Plugins/CsvImport/translations/CsvImport_hu_HU.ts | 95 + Plugins/CsvImport/translations/CsvImport_it_IT.ts | 95 + Plugins/CsvImport/translations/CsvImport_ja_JP.ts | 95 + Plugins/CsvImport/translations/CsvImport_kaa.ts | 95 + Plugins/CsvImport/translations/CsvImport_ko_KR.ts | 95 + Plugins/CsvImport/translations/CsvImport_nl_NL.ts | 95 + Plugins/CsvImport/translations/CsvImport_no_NO.ts | 95 + Plugins/CsvImport/translations/CsvImport_pl_PL.ts | 95 + Plugins/CsvImport/translations/CsvImport_pt_BR.ts | 95 + Plugins/CsvImport/translations/CsvImport_pt_PT.ts | 95 + Plugins/CsvImport/translations/CsvImport_ro_RO.ts | 95 + Plugins/CsvImport/translations/CsvImport_ru_RU.ts | 95 + Plugins/CsvImport/translations/CsvImport_sk_SK.ts | 95 + Plugins/CsvImport/translations/CsvImport_sr_SP.ts | 95 + Plugins/CsvImport/translations/CsvImport_sv_SE.ts | 95 + Plugins/CsvImport/translations/CsvImport_tr_TR.ts | 95 + Plugins/CsvImport/translations/CsvImport_uk_UA.ts | 95 + Plugins/CsvImport/translations/CsvImport_vi_VN.ts | 95 + Plugins/CsvImport/translations/CsvImport_zh_CN.ts | 95 + Plugins/CsvImport/translations/CsvImport_zh_TW.ts | 95 + Plugins/DbAndroid/DbAndroid.pro | 13 - Plugins/DbAndroid/DbAndroid_de.qm | Bin 23 -> 0 bytes Plugins/DbAndroid/DbAndroid_de.ts | 347 - Plugins/DbAndroid/DbAndroid_es.qm | Bin 23 -> 0 bytes Plugins/DbAndroid/DbAndroid_es.ts | 347 - Plugins/DbAndroid/DbAndroid_fr.qm | Bin 23 -> 0 bytes Plugins/DbAndroid/DbAndroid_fr.ts | 347 - Plugins/DbAndroid/DbAndroid_it.qm | Bin 23 -> 0 bytes Plugins/DbAndroid/DbAndroid_it.ts | 347 - Plugins/DbAndroid/DbAndroid_pl.qm | Bin 13141 -> 0 bytes Plugins/DbAndroid/DbAndroid_pl.ts | 352 - Plugins/DbAndroid/DbAndroid_pt_BR.qm | Bin 23 -> 0 bytes Plugins/DbAndroid/DbAndroid_pt_BR.ts | 347 - Plugins/DbAndroid/DbAndroid_ro_RO.qm | Bin 30 -> 0 bytes Plugins/DbAndroid/DbAndroid_ro_RO.ts | 347 - Plugins/DbAndroid/DbAndroid_ru.qm | Bin 34 -> 0 bytes Plugins/DbAndroid/DbAndroid_ru.ts | 347 - Plugins/DbAndroid/DbAndroid_sk.qm | Bin 27 -> 0 bytes Plugins/DbAndroid/DbAndroid_sk.ts | 347 - Plugins/DbAndroid/DbAndroid_zh_CN.qm | 1 - Plugins/DbAndroid/DbAndroid_zh_CN.ts | 347 - Plugins/DbAndroid/dbandroid.cpp | 4 +- Plugins/DbAndroid/dbandroid.json | 2 +- Plugins/DbAndroid/dbandroidinstance.cpp | 26 +- Plugins/DbAndroid/dbandroidinstance.h | 9 +- Plugins/DbAndroid/dbandroidjsonconnection.cpp | 2 +- Plugins/DbAndroid/dbandroidshellconnection.cpp | 2 +- Plugins/DbAndroid/translations/DbAndroid.ts | 347 + Plugins/DbAndroid/translations/DbAndroid_af_ZA.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_ar_SA.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_ca_ES.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_cs_CZ.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_da_DK.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_de_DE.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_el_GR.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_en_US.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_es_ES.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_fa_IR.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_fi_FI.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_fr_FR.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_he_IL.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_hu_HU.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_it_IT.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_ja_JP.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_kaa.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_ko_KR.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_nl_NL.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_no_NO.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_pl_PL.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_pt_BR.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_pt_PT.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_ro_RO.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_ru_RU.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_sk_SK.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_sr_SP.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_sv_SE.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_tr_TR.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_uk_UA.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_vi_VN.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_zh_CN.ts | 352 + Plugins/DbAndroid/translations/DbAndroid_zh_TW.ts | 352 + Plugins/DbSqliteCipher/DbSqliteCipher.pro | 29 +- Plugins/DbSqliteCipher/DbSqliteCipher_de.qm | Bin 23 -> 0 bytes Plugins/DbSqliteCipher/DbSqliteCipher_de.ts | 58 - Plugins/DbSqliteCipher/DbSqliteCipher_es.qm | Bin 23 -> 0 bytes Plugins/DbSqliteCipher/DbSqliteCipher_es.ts | 58 - Plugins/DbSqliteCipher/DbSqliteCipher_fr.qm | Bin 23 -> 0 bytes Plugins/DbSqliteCipher/DbSqliteCipher_fr.ts | 58 - Plugins/DbSqliteCipher/DbSqliteCipher_it.qm | Bin 23 -> 0 bytes Plugins/DbSqliteCipher/DbSqliteCipher_it.ts | 58 - Plugins/DbSqliteCipher/DbSqliteCipher_pl.qm | Bin 1812 -> 0 bytes Plugins/DbSqliteCipher/DbSqliteCipher_pl.ts | 58 - Plugins/DbSqliteCipher/DbSqliteCipher_pt_BR.qm | Bin 23 -> 0 bytes Plugins/DbSqliteCipher/DbSqliteCipher_pt_BR.ts | 58 - Plugins/DbSqliteCipher/DbSqliteCipher_ro_RO.qm | Bin 30 -> 0 bytes Plugins/DbSqliteCipher/DbSqliteCipher_ro_RO.ts | 58 - Plugins/DbSqliteCipher/DbSqliteCipher_ru.qm | Bin 34 -> 0 bytes Plugins/DbSqliteCipher/DbSqliteCipher_ru.ts | 58 - Plugins/DbSqliteCipher/DbSqliteCipher_sk.qm | Bin 27 -> 0 bytes Plugins/DbSqliteCipher/DbSqliteCipher_sk.ts | 58 - Plugins/DbSqliteCipher/DbSqliteCipher_zh_CN.qm | 1 - Plugins/DbSqliteCipher/DbSqliteCipher_zh_CN.ts | 58 - Plugins/DbSqliteCipher/dbsqlitecipher.cpp | 5 +- Plugins/DbSqliteCipher/dbsqlitecipher.json | 2 +- Plugins/DbSqliteCipher/dbsqlitecipherinstance.cpp | 12 +- Plugins/DbSqliteCipher/dbsqlitecipherinstance.h | 3 + Plugins/DbSqliteCipher/sqlcipher.c | 170593 ++++++++++-------- Plugins/DbSqliteCipher/sqlcipher.h | 762 +- .../DbSqliteCipher/translations/DbSqliteCipher.ts | 34 + .../translations/DbSqliteCipher_af_ZA.ts | 36 + .../translations/DbSqliteCipher_ar_SA.ts | 36 + .../translations/DbSqliteCipher_ca_ES.ts | 36 + .../translations/DbSqliteCipher_cs_CZ.ts | 36 + .../translations/DbSqliteCipher_da_DK.ts | 36 + .../translations/DbSqliteCipher_de_DE.ts | 36 + .../translations/DbSqliteCipher_el_GR.ts | 36 + .../translations/DbSqliteCipher_en_US.ts | 36 + .../translations/DbSqliteCipher_es_ES.ts | 36 + .../translations/DbSqliteCipher_fa_IR.ts | 36 + .../translations/DbSqliteCipher_fi_FI.ts | 36 + .../translations/DbSqliteCipher_fr_FR.ts | 36 + .../translations/DbSqliteCipher_he_IL.ts | 36 + .../translations/DbSqliteCipher_hu_HU.ts | 36 + .../translations/DbSqliteCipher_it_IT.ts | 36 + .../translations/DbSqliteCipher_ja_JP.ts | 36 + .../translations/DbSqliteCipher_kaa.ts | 36 + .../translations/DbSqliteCipher_ko_KR.ts | 36 + .../translations/DbSqliteCipher_nl_NL.ts | 36 + .../translations/DbSqliteCipher_no_NO.ts | 36 + .../translations/DbSqliteCipher_pl_PL.ts | 36 + .../translations/DbSqliteCipher_pt_BR.ts | 36 + .../translations/DbSqliteCipher_pt_PT.ts | 36 + .../translations/DbSqliteCipher_ro_RO.ts | 36 + .../translations/DbSqliteCipher_ru_RU.ts | 36 + .../translations/DbSqliteCipher_sk_SK.ts | 36 + .../translations/DbSqliteCipher_sr_SP.ts | 36 + .../translations/DbSqliteCipher_sv_SE.ts | 36 + .../translations/DbSqliteCipher_tr_TR.ts | 36 + .../translations/DbSqliteCipher_uk_UA.ts | 36 + .../translations/DbSqliteCipher_vi_VN.ts | 36 + .../translations/DbSqliteCipher_zh_CN.ts | 36 + .../translations/DbSqliteCipher_zh_TW.ts | 36 + Plugins/DbSqliteCipher/update_sqlite_version.tcl | 2 +- Plugins/DbSqliteWx/dbsqlitewx.json | 2 +- Plugins/DbSqliteWx/dbsqlitewxinstance.cpp | 12 +- Plugins/DbSqliteWx/dbsqlitewxinstance.h | 19 +- Plugins/DbSqliteWx/translations/DbSqliteWx.ts | 44 + .../DbSqliteWx/translations/DbSqliteWx_af_ZA.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_ar_SA.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_ca_ES.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_cs_CZ.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_da_DK.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_de_DE.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_el_GR.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_en_US.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_es_ES.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_fa_IR.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_fi_FI.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_fr_FR.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_he_IL.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_hu_HU.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_it_IT.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_ja_JP.ts | 46 + Plugins/DbSqliteWx/translations/DbSqliteWx_kaa.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_ko_KR.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_nl_NL.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_no_NO.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_pl_PL.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_pt_BR.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_pt_PT.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_ro_RO.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_ru_RU.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_sk_SK.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_sr_SP.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_sv_SE.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_tr_TR.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_uk_UA.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_vi_VN.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_zh_CN.ts | 46 + .../DbSqliteWx/translations/DbSqliteWx_zh_TW.ts | 46 + Plugins/DbSqliteWx/update_sqlite_version.tcl | 2 +- Plugins/DbSqliteWx/wxsqlite3.c | 57987 ++++-- Plugins/DbSqliteWx/wxsqlite3.h | 1205 +- Plugins/FusionDarkStyle/fusiondarkplugin.cpp | 4 +- Plugins/HtmlExport/HtmlExport.pro | 18 +- Plugins/HtmlExport/HtmlExport_de.qm | Bin 23 -> 0 bytes Plugins/HtmlExport/HtmlExport_de.ts | 173 - Plugins/HtmlExport/HtmlExport_es.qm | Bin 23 -> 0 bytes Plugins/HtmlExport/HtmlExport_es.ts | 173 - Plugins/HtmlExport/HtmlExport_fr.qm | Bin 3886 -> 0 bytes Plugins/HtmlExport/HtmlExport_fr.ts | 173 - Plugins/HtmlExport/HtmlExport_it.qm | Bin 23 -> 0 bytes Plugins/HtmlExport/HtmlExport_it.ts | 173 - Plugins/HtmlExport/HtmlExport_pl.qm | Bin 3840 -> 0 bytes Plugins/HtmlExport/HtmlExport_pl.ts | 173 - Plugins/HtmlExport/HtmlExport_pt_BR.qm | Bin 23 -> 0 bytes Plugins/HtmlExport/HtmlExport_pt_BR.ts | 173 - Plugins/HtmlExport/HtmlExport_ro_RO.qm | Bin 30 -> 0 bytes Plugins/HtmlExport/HtmlExport_ro_RO.ts | 173 - Plugins/HtmlExport/HtmlExport_ru.qm | Bin 3899 -> 0 bytes Plugins/HtmlExport/HtmlExport_ru.ts | 173 - Plugins/HtmlExport/HtmlExport_sk.qm | Bin 1039 -> 0 bytes Plugins/HtmlExport/HtmlExport_sk.ts | 173 - Plugins/HtmlExport/HtmlExport_zh_CN.qm | 1 - Plugins/HtmlExport/HtmlExport_zh_CN.ts | 173 - Plugins/HtmlExport/htmlexport.cpp | 4 +- Plugins/HtmlExport/htmlexport.qrc | 15 - Plugins/HtmlExport/translations/HtmlExport.ts | 173 + .../HtmlExport/translations/HtmlExport_af_ZA.ts | 173 + .../HtmlExport/translations/HtmlExport_ar_SA.ts | 173 + .../HtmlExport/translations/HtmlExport_ca_ES.ts | 173 + .../HtmlExport/translations/HtmlExport_cs_CZ.ts | 173 + .../HtmlExport/translations/HtmlExport_da_DK.ts | 173 + .../HtmlExport/translations/HtmlExport_de_DE.ts | 173 + .../HtmlExport/translations/HtmlExport_el_GR.ts | 173 + .../HtmlExport/translations/HtmlExport_en_US.ts | 173 + .../HtmlExport/translations/HtmlExport_es_ES.ts | 173 + .../HtmlExport/translations/HtmlExport_fa_IR.ts | 173 + .../HtmlExport/translations/HtmlExport_fi_FI.ts | 173 + .../HtmlExport/translations/HtmlExport_fr_FR.ts | 173 + .../HtmlExport/translations/HtmlExport_he_IL.ts | 173 + .../HtmlExport/translations/HtmlExport_hu_HU.ts | 173 + .../HtmlExport/translations/HtmlExport_it_IT.ts | 173 + .../HtmlExport/translations/HtmlExport_ja_JP.ts | 173 + Plugins/HtmlExport/translations/HtmlExport_kaa.ts | 173 + .../HtmlExport/translations/HtmlExport_ko_KR.ts | 173 + .../HtmlExport/translations/HtmlExport_nl_NL.ts | 173 + .../HtmlExport/translations/HtmlExport_no_NO.ts | 173 + .../HtmlExport/translations/HtmlExport_pl_PL.ts | 173 + .../HtmlExport/translations/HtmlExport_pt_BR.ts | 173 + .../HtmlExport/translations/HtmlExport_pt_PT.ts | 173 + .../HtmlExport/translations/HtmlExport_ro_RO.ts | 173 + .../HtmlExport/translations/HtmlExport_ru_RU.ts | 173 + .../HtmlExport/translations/HtmlExport_sk_SK.ts | 173 + .../HtmlExport/translations/HtmlExport_sr_SP.ts | 173 + .../HtmlExport/translations/HtmlExport_sv_SE.ts | 173 + .../HtmlExport/translations/HtmlExport_tr_TR.ts | 173 + .../HtmlExport/translations/HtmlExport_uk_UA.ts | 173 + .../HtmlExport/translations/HtmlExport_vi_VN.ts | 173 + .../HtmlExport/translations/HtmlExport_zh_CN.ts | 173 + .../HtmlExport/translations/HtmlExport_zh_TW.ts | 173 + Plugins/JsonExport/JsonExport.pro | 28 - Plugins/JsonExport/JsonExport_de.qm | Bin 23 -> 0 bytes Plugins/JsonExport/JsonExport_de.ts | 22 - Plugins/JsonExport/JsonExport_es.qm | Bin 23 -> 0 bytes Plugins/JsonExport/JsonExport_es.ts | 22 - Plugins/JsonExport/JsonExport_fr.qm | Bin 442 -> 0 bytes Plugins/JsonExport/JsonExport_fr.ts | 22 - Plugins/JsonExport/JsonExport_it.qm | Bin 23 -> 0 bytes Plugins/JsonExport/JsonExport_it.ts | 22 - Plugins/JsonExport/JsonExport_pl.qm | Bin 442 -> 0 bytes Plugins/JsonExport/JsonExport_pl.ts | 22 - Plugins/JsonExport/JsonExport_pt_BR.qm | Bin 23 -> 0 bytes Plugins/JsonExport/JsonExport_pt_BR.ts | 22 - Plugins/JsonExport/JsonExport_ro_RO.qm | Bin 30 -> 0 bytes Plugins/JsonExport/JsonExport_ro_RO.ts | 22 - Plugins/JsonExport/JsonExport_ru.qm | Bin 441 -> 0 bytes Plugins/JsonExport/JsonExport_ru.ts | 22 - Plugins/JsonExport/JsonExport_sk.qm | Bin 444 -> 0 bytes Plugins/JsonExport/JsonExport_sk.ts | 22 - Plugins/JsonExport/JsonExport_zh_CN.qm | Bin 301 -> 0 bytes Plugins/JsonExport/JsonExport_zh_CN.ts | 22 - Plugins/JsonExport/jsonexport.cpp | 7 +- Plugins/JsonExport/jsonexport.qrc | 15 - Plugins/JsonExport/translations/JsonExport.ts | 22 + .../JsonExport/translations/JsonExport_af_ZA.ts | 22 + .../JsonExport/translations/JsonExport_ar_SA.ts | 22 + .../JsonExport/translations/JsonExport_ca_ES.ts | 22 + .../JsonExport/translations/JsonExport_cs_CZ.ts | 22 + .../JsonExport/translations/JsonExport_da_DK.ts | 22 + .../JsonExport/translations/JsonExport_de_DE.ts | 22 + .../JsonExport/translations/JsonExport_el_GR.ts | 22 + .../JsonExport/translations/JsonExport_en_US.ts | 22 + .../JsonExport/translations/JsonExport_es_ES.ts | 22 + .../JsonExport/translations/JsonExport_fa_IR.ts | 22 + .../JsonExport/translations/JsonExport_fi_FI.ts | 22 + .../JsonExport/translations/JsonExport_fr_FR.ts | 22 + .../JsonExport/translations/JsonExport_he_IL.ts | 22 + .../JsonExport/translations/JsonExport_hu_HU.ts | 22 + .../JsonExport/translations/JsonExport_it_IT.ts | 22 + .../JsonExport/translations/JsonExport_ja_JP.ts | 22 + Plugins/JsonExport/translations/JsonExport_kaa.ts | 22 + .../JsonExport/translations/JsonExport_ko_KR.ts | 22 + .../JsonExport/translations/JsonExport_nl_NL.ts | 22 + .../JsonExport/translations/JsonExport_no_NO.ts | 22 + .../JsonExport/translations/JsonExport_pl_PL.ts | 22 + .../JsonExport/translations/JsonExport_pt_BR.ts | 22 + .../JsonExport/translations/JsonExport_pt_PT.ts | 22 + .../JsonExport/translations/JsonExport_ro_RO.ts | 22 + .../JsonExport/translations/JsonExport_ru_RU.ts | 22 + .../JsonExport/translations/JsonExport_sk_SK.ts | 22 + .../JsonExport/translations/JsonExport_sr_SP.ts | 22 + .../JsonExport/translations/JsonExport_sv_SE.ts | 22 + .../JsonExport/translations/JsonExport_tr_TR.ts | 22 + .../JsonExport/translations/JsonExport_uk_UA.ts | 22 + .../JsonExport/translations/JsonExport_vi_VN.ts | 22 + .../JsonExport/translations/JsonExport_zh_CN.ts | 22 + .../JsonExport/translations/JsonExport_zh_TW.ts | 22 + Plugins/MultiEditorImage/MultiEditorImage.pro | 13 - Plugins/MultiEditorImage/MultiEditorImage_de.qm | Bin 23 -> 0 bytes Plugins/MultiEditorImage/MultiEditorImage_de.ts | 81 - Plugins/MultiEditorImage/MultiEditorImage_es.qm | Bin 23 -> 0 bytes Plugins/MultiEditorImage/MultiEditorImage_es.ts | 81 - Plugins/MultiEditorImage/MultiEditorImage_fr.qm | Bin 23 -> 0 bytes Plugins/MultiEditorImage/MultiEditorImage_fr.ts | 81 - Plugins/MultiEditorImage/MultiEditorImage_it.qm | Bin 23 -> 0 bytes Plugins/MultiEditorImage/MultiEditorImage_it.ts | 81 - Plugins/MultiEditorImage/MultiEditorImage_pl.qm | Bin 2277 -> 0 bytes Plugins/MultiEditorImage/MultiEditorImage_pl.ts | 81 - Plugins/MultiEditorImage/MultiEditorImage_pt_BR.qm | Bin 23 -> 0 bytes Plugins/MultiEditorImage/MultiEditorImage_pt_BR.ts | 81 - Plugins/MultiEditorImage/MultiEditorImage_ro_RO.qm | Bin 30 -> 0 bytes Plugins/MultiEditorImage/MultiEditorImage_ro_RO.ts | 81 - Plugins/MultiEditorImage/MultiEditorImage_ru.qm | Bin 34 -> 0 bytes Plugins/MultiEditorImage/MultiEditorImage_ru.ts | 81 - Plugins/MultiEditorImage/MultiEditorImage_sk.qm | Bin 27 -> 0 bytes Plugins/MultiEditorImage/MultiEditorImage_sk.ts | 81 - Plugins/MultiEditorImage/MultiEditorImage_zh_CN.qm | 1 - Plugins/MultiEditorImage/MultiEditorImage_zh_CN.ts | 81 - Plugins/MultiEditorImage/multieditorimage.cpp | 11 +- Plugins/MultiEditorImage/multieditorimage.qrc | 12 - .../translations/MultiEditorImage.ts | 80 + .../translations/MultiEditorImage_af_ZA.ts | 80 + .../translations/MultiEditorImage_ar_SA.ts | 80 + .../translations/MultiEditorImage_ca_ES.ts | 80 + .../translations/MultiEditorImage_cs_CZ.ts | 80 + .../translations/MultiEditorImage_da_DK.ts | 80 + .../translations/MultiEditorImage_de_DE.ts | 80 + .../translations/MultiEditorImage_el_GR.ts | 80 + .../translations/MultiEditorImage_en_US.ts | 80 + .../translations/MultiEditorImage_es_ES.ts | 80 + .../translations/MultiEditorImage_fa_IR.ts | 80 + .../translations/MultiEditorImage_fi_FI.ts | 80 + .../translations/MultiEditorImage_fr_FR.ts | 80 + .../translations/MultiEditorImage_he_IL.ts | 80 + .../translations/MultiEditorImage_hu_HU.ts | 80 + .../translations/MultiEditorImage_it_IT.ts | 80 + .../translations/MultiEditorImage_ja_JP.ts | 80 + .../translations/MultiEditorImage_kaa.ts | 80 + .../translations/MultiEditorImage_ko_KR.ts | 80 + .../translations/MultiEditorImage_nl_NL.ts | 80 + .../translations/MultiEditorImage_no_NO.ts | 80 + .../translations/MultiEditorImage_pl_PL.ts | 80 + .../translations/MultiEditorImage_pt_BR.ts | 80 + .../translations/MultiEditorImage_pt_PT.ts | 80 + .../translations/MultiEditorImage_ro_RO.ts | 80 + .../translations/MultiEditorImage_ru_RU.ts | 80 + .../translations/MultiEditorImage_sk_SK.ts | 80 + .../translations/MultiEditorImage_sr_SP.ts | 80 + .../translations/MultiEditorImage_sv_SE.ts | 80 + .../translations/MultiEditorImage_tr_TR.ts | 80 + .../translations/MultiEditorImage_uk_UA.ts | 80 + .../translations/MultiEditorImage_vi_VN.ts | 80 + .../translations/MultiEditorImage_zh_CN.ts | 80 + .../translations/MultiEditorImage_zh_TW.ts | 80 + Plugins/PdfExport/PdfExport.pro | 28 - Plugins/PdfExport/PdfExport_de.qm | Bin 3953 -> 0 bytes Plugins/PdfExport/PdfExport_de.ts | 258 - Plugins/PdfExport/PdfExport_es.qm | Bin 23 -> 0 bytes Plugins/PdfExport/PdfExport_es.ts | 256 - Plugins/PdfExport/PdfExport_fr.qm | Bin 4075 -> 0 bytes Plugins/PdfExport/PdfExport_fr.ts | 256 - Plugins/PdfExport/PdfExport_it.qm | Bin 23 -> 0 bytes Plugins/PdfExport/PdfExport_it.ts | 256 - Plugins/PdfExport/PdfExport_pl.qm | Bin 3945 -> 0 bytes Plugins/PdfExport/PdfExport_pl.ts | 256 - Plugins/PdfExport/PdfExport_pt_BR.qm | Bin 23 -> 0 bytes Plugins/PdfExport/PdfExport_pt_BR.ts | 256 - Plugins/PdfExport/PdfExport_ro_RO.qm | Bin 30 -> 0 bytes Plugins/PdfExport/PdfExport_ro_RO.ts | 256 - Plugins/PdfExport/PdfExport_ru.qm | Bin 4034 -> 0 bytes Plugins/PdfExport/PdfExport_ru.ts | 256 - Plugins/PdfExport/PdfExport_sk.qm | Bin 3903 -> 0 bytes Plugins/PdfExport/PdfExport_sk.ts | 256 - Plugins/PdfExport/PdfExport_zh_CN.qm | 1 - Plugins/PdfExport/PdfExport_zh_CN.ts | 256 - Plugins/PdfExport/pdfexport.cpp | 57 +- Plugins/PdfExport/pdfexport.h | 2 +- Plugins/PdfExport/pdfexport.qrc | 15 - Plugins/PdfExport/translations/PdfExport.ts | 256 + Plugins/PdfExport/translations/PdfExport_af_ZA.ts | 256 + Plugins/PdfExport/translations/PdfExport_ar_SA.ts | 256 + Plugins/PdfExport/translations/PdfExport_ca_ES.ts | 256 + Plugins/PdfExport/translations/PdfExport_cs_CZ.ts | 256 + Plugins/PdfExport/translations/PdfExport_da_DK.ts | 256 + Plugins/PdfExport/translations/PdfExport_de_DE.ts | 257 + Plugins/PdfExport/translations/PdfExport_el_GR.ts | 256 + Plugins/PdfExport/translations/PdfExport_en_US.ts | 256 + Plugins/PdfExport/translations/PdfExport_es_ES.ts | 256 + Plugins/PdfExport/translations/PdfExport_fa_IR.ts | 256 + Plugins/PdfExport/translations/PdfExport_fi_FI.ts | 256 + Plugins/PdfExport/translations/PdfExport_fr_FR.ts | 256 + Plugins/PdfExport/translations/PdfExport_he_IL.ts | 256 + Plugins/PdfExport/translations/PdfExport_hu_HU.ts | 256 + Plugins/PdfExport/translations/PdfExport_it_IT.ts | 256 + Plugins/PdfExport/translations/PdfExport_ja_JP.ts | 256 + Plugins/PdfExport/translations/PdfExport_kaa.ts | 256 + Plugins/PdfExport/translations/PdfExport_ko_KR.ts | 256 + Plugins/PdfExport/translations/PdfExport_nl_NL.ts | 256 + Plugins/PdfExport/translations/PdfExport_no_NO.ts | 256 + Plugins/PdfExport/translations/PdfExport_pl_PL.ts | 256 + Plugins/PdfExport/translations/PdfExport_pt_BR.ts | 256 + Plugins/PdfExport/translations/PdfExport_pt_PT.ts | 256 + Plugins/PdfExport/translations/PdfExport_ro_RO.ts | 256 + Plugins/PdfExport/translations/PdfExport_ru_RU.ts | 256 + Plugins/PdfExport/translations/PdfExport_sk_SK.ts | 256 + Plugins/PdfExport/translations/PdfExport_sr_SP.ts | 256 + Plugins/PdfExport/translations/PdfExport_sv_SE.ts | 256 + Plugins/PdfExport/translations/PdfExport_tr_TR.ts | 256 + Plugins/PdfExport/translations/PdfExport_uk_UA.ts | 256 + Plugins/PdfExport/translations/PdfExport_vi_VN.ts | 256 + Plugins/PdfExport/translations/PdfExport_zh_CN.ts | 256 + Plugins/PdfExport/translations/PdfExport_zh_TW.ts | 256 + Plugins/Plugins.pro | 4 +- Plugins/Printing/Printing.pro | 28 - Plugins/Printing/Printing_de.qm | Bin 542 -> 0 bytes Plugins/Printing/Printing_de.ts | 40 - Plugins/Printing/Printing_es.qm | Bin 23 -> 0 bytes Plugins/Printing/Printing_es.ts | 40 - Plugins/Printing/Printing_fr.qm | Bin 578 -> 0 bytes Plugins/Printing/Printing_fr.ts | 40 - Plugins/Printing/Printing_it.qm | Bin 23 -> 0 bytes Plugins/Printing/Printing_it.ts | 40 - Plugins/Printing/Printing_pl.qm | Bin 514 -> 0 bytes Plugins/Printing/Printing_pl.ts | 40 - Plugins/Printing/Printing_pt_BR.qm | Bin 23 -> 0 bytes Plugins/Printing/Printing_pt_BR.ts | 40 - Plugins/Printing/Printing_ro_RO.qm | Bin 30 -> 0 bytes Plugins/Printing/Printing_ro_RO.ts | 40 - Plugins/Printing/Printing_ru.qm | Bin 515 -> 0 bytes Plugins/Printing/Printing_ru.ts | 40 - Plugins/Printing/Printing_sk.qm | Bin 27 -> 0 bytes Plugins/Printing/Printing_sk.ts | 40 - Plugins/Printing/Printing_zh_CN.qm | Bin 403 -> 0 bytes Plugins/Printing/Printing_zh_CN.ts | 40 - Plugins/Printing/printing.cpp | 4 +- Plugins/Printing/printing.qrc | 15 - Plugins/Printing/printingexport.cpp | 2 - Plugins/Printing/translations/Printing.ts | 40 + Plugins/Printing/translations/Printing_af_ZA.ts | 40 + Plugins/Printing/translations/Printing_ar_SA.ts | 40 + Plugins/Printing/translations/Printing_ca_ES.ts | 40 + Plugins/Printing/translations/Printing_cs_CZ.ts | 40 + Plugins/Printing/translations/Printing_da_DK.ts | 40 + Plugins/Printing/translations/Printing_de_DE.ts | 40 + Plugins/Printing/translations/Printing_el_GR.ts | 40 + Plugins/Printing/translations/Printing_en_US.ts | 40 + Plugins/Printing/translations/Printing_es_ES.ts | 40 + Plugins/Printing/translations/Printing_fa_IR.ts | 40 + Plugins/Printing/translations/Printing_fi_FI.ts | 40 + Plugins/Printing/translations/Printing_fr_FR.ts | 40 + Plugins/Printing/translations/Printing_he_IL.ts | 40 + Plugins/Printing/translations/Printing_hu_HU.ts | 40 + Plugins/Printing/translations/Printing_it_IT.ts | 40 + Plugins/Printing/translations/Printing_ja_JP.ts | 40 + Plugins/Printing/translations/Printing_kaa.ts | 40 + Plugins/Printing/translations/Printing_ko_KR.ts | 40 + Plugins/Printing/translations/Printing_nl_NL.ts | 40 + Plugins/Printing/translations/Printing_no_NO.ts | 40 + Plugins/Printing/translations/Printing_pl_PL.ts | 40 + Plugins/Printing/translations/Printing_pt_BR.ts | 40 + Plugins/Printing/translations/Printing_pt_PT.ts | 40 + Plugins/Printing/translations/Printing_ro_RO.ts | 40 + Plugins/Printing/translations/Printing_ru_RU.ts | 40 + Plugins/Printing/translations/Printing_sk_SK.ts | 40 + Plugins/Printing/translations/Printing_sr_SP.ts | 40 + Plugins/Printing/translations/Printing_sv_SE.ts | 40 + Plugins/Printing/translations/Printing_tr_TR.ts | 40 + Plugins/Printing/translations/Printing_uk_UA.ts | 40 + Plugins/Printing/translations/Printing_vi_VN.ts | 40 + Plugins/Printing/translations/Printing_zh_CN.ts | 40 + Plugins/Printing/translations/Printing_zh_TW.ts | 40 + .../PythonSyntaxHighlighter.pro | 20 + .../pythonsyntaxhighlighter.cpp | 298 + .../pythonsyntaxhighlighter.h | 42 + .../pythonsyntaxhighlighter.json | 7 + .../pythonsyntaxhighlighter_global.h | 12 + Plugins/RegExpImport/RegExpImport.pro | 28 - Plugins/RegExpImport/RegExpImport_de.qm | Bin 23 -> 0 bytes Plugins/RegExpImport/RegExpImport_de.ts | 83 - Plugins/RegExpImport/RegExpImport_es.qm | Bin 23 -> 0 bytes Plugins/RegExpImport/RegExpImport_es.ts | 83 - Plugins/RegExpImport/RegExpImport_fr.qm | Bin 2312 -> 0 bytes Plugins/RegExpImport/RegExpImport_fr.ts | 83 - Plugins/RegExpImport/RegExpImport_it.qm | Bin 23 -> 0 bytes Plugins/RegExpImport/RegExpImport_it.ts | 83 - Plugins/RegExpImport/RegExpImport_pl.qm | Bin 3752 -> 0 bytes Plugins/RegExpImport/RegExpImport_pl.ts | 86 - Plugins/RegExpImport/RegExpImport_pt_BR.qm | Bin 23 -> 0 bytes Plugins/RegExpImport/RegExpImport_pt_BR.ts | 83 - Plugins/RegExpImport/RegExpImport_ro_RO.qm | Bin 30 -> 0 bytes Plugins/RegExpImport/RegExpImport_ro_RO.ts | 83 - Plugins/RegExpImport/RegExpImport_ru.qm | Bin 3791 -> 0 bytes Plugins/RegExpImport/RegExpImport_ru.ts | 86 - Plugins/RegExpImport/RegExpImport_sk.qm | Bin 27 -> 0 bytes Plugins/RegExpImport/RegExpImport_sk.ts | 83 - Plugins/RegExpImport/RegExpImport_zh_CN.qm | 1 - Plugins/RegExpImport/RegExpImport_zh_CN.ts | 83 - Plugins/RegExpImport/regexpimport.cpp | 4 +- Plugins/RegExpImport/regexpimport.qrc | 15 - Plugins/RegExpImport/translations/RegExpImport.ts | 83 + .../translations/RegExpImport_af_ZA.ts | 86 + .../translations/RegExpImport_ar_SA.ts | 86 + .../translations/RegExpImport_ca_ES.ts | 86 + .../translations/RegExpImport_cs_CZ.ts | 86 + .../translations/RegExpImport_da_DK.ts | 86 + .../translations/RegExpImport_de_DE.ts | 86 + .../translations/RegExpImport_el_GR.ts | 86 + .../translations/RegExpImport_en_US.ts | 86 + .../translations/RegExpImport_es_ES.ts | 86 + .../translations/RegExpImport_fa_IR.ts | 86 + .../translations/RegExpImport_fi_FI.ts | 86 + .../translations/RegExpImport_fr_FR.ts | 86 + .../translations/RegExpImport_he_IL.ts | 86 + .../translations/RegExpImport_hu_HU.ts | 86 + .../translations/RegExpImport_it_IT.ts | 86 + .../translations/RegExpImport_ja_JP.ts | 86 + .../RegExpImport/translations/RegExpImport_kaa.ts | 86 + .../translations/RegExpImport_ko_KR.ts | 86 + .../translations/RegExpImport_nl_NL.ts | 86 + .../translations/RegExpImport_no_NO.ts | 86 + .../translations/RegExpImport_pl_PL.ts | 86 + .../translations/RegExpImport_pt_BR.ts | 86 + .../translations/RegExpImport_pt_PT.ts | 86 + .../translations/RegExpImport_ro_RO.ts | 86 + .../translations/RegExpImport_ru_RU.ts | 86 + .../translations/RegExpImport_sk_SK.ts | 86 + .../translations/RegExpImport_sr_SP.ts | 86 + .../translations/RegExpImport_sv_SE.ts | 86 + .../translations/RegExpImport_tr_TR.ts | 86 + .../translations/RegExpImport_uk_UA.ts | 86 + .../translations/RegExpImport_vi_VN.ts | 86 + .../translations/RegExpImport_zh_CN.ts | 86 + .../translations/RegExpImport_zh_TW.ts | 86 + Plugins/ScriptingPython/ScriptingPython.pro | 34 + Plugins/ScriptingPython/icon_attribution.txt | 1 + Plugins/ScriptingPython/scriptingpython.cpp | 666 + Plugins/ScriptingPython/scriptingpython.h | 96 + Plugins/ScriptingPython/scriptingpython.json | 8 + Plugins/ScriptingPython/scriptingpython.png | Bin 0 -> 559 bytes Plugins/ScriptingPython/scriptingpython.qrc | 5 + Plugins/ScriptingPython/scriptingpython_global.h | 12 + .../translations/ScriptingPython.ts | 32 + .../translations/ScriptingPython_af_ZA.ts | 32 + .../translations/ScriptingPython_ar_SA.ts | 32 + .../translations/ScriptingPython_ca_ES.ts | 32 + .../translations/ScriptingPython_cs_CZ.ts | 32 + .../translations/ScriptingPython_da_DK.ts | 32 + .../translations/ScriptingPython_de_DE.ts | 32 + .../translations/ScriptingPython_el_GR.ts | 32 + .../translations/ScriptingPython_en_US.ts | 32 + .../translations/ScriptingPython_es_ES.ts | 32 + .../translations/ScriptingPython_fa_IR.ts | 32 + .../translations/ScriptingPython_fi_FI.ts | 32 + .../translations/ScriptingPython_fr_FR.ts | 32 + .../translations/ScriptingPython_he_IL.ts | 32 + .../translations/ScriptingPython_hu_HU.ts | 32 + .../translations/ScriptingPython_it_IT.ts | 32 + .../translations/ScriptingPython_ja_JP.ts | 32 + .../translations/ScriptingPython_kaa.ts | 32 + .../translations/ScriptingPython_ko_KR.ts | 32 + .../translations/ScriptingPython_nl_NL.ts | 32 + .../translations/ScriptingPython_no_NO.ts | 32 + .../translations/ScriptingPython_pl_PL.ts | 32 + .../translations/ScriptingPython_pt_BR.ts | 32 + .../translations/ScriptingPython_pt_PT.ts | 32 + .../translations/ScriptingPython_ro_RO.ts | 32 + .../translations/ScriptingPython_ru_RU.ts | 32 + .../translations/ScriptingPython_sk_SK.ts | 32 + .../translations/ScriptingPython_sr_SP.ts | 32 + .../translations/ScriptingPython_sv_SE.ts | 32 + .../translations/ScriptingPython_tr_TR.ts | 32 + .../translations/ScriptingPython_uk_UA.ts | 32 + .../translations/ScriptingPython_vi_VN.ts | 32 + .../translations/ScriptingPython_zh_CN.ts | 32 + .../translations/ScriptingPython_zh_TW.ts | 32 + Plugins/ScriptingTcl/ScriptingTcl.pro | 58 +- Plugins/ScriptingTcl/ScriptingTcl_de.qm | Bin 23 -> 0 bytes Plugins/ScriptingTcl/ScriptingTcl_de.ts | 23 - Plugins/ScriptingTcl/ScriptingTcl_es.qm | Bin 23 -> 0 bytes Plugins/ScriptingTcl/ScriptingTcl_es.ts | 23 - Plugins/ScriptingTcl/ScriptingTcl_fr.qm | Bin 665 -> 0 bytes Plugins/ScriptingTcl/ScriptingTcl_fr.ts | 23 - Plugins/ScriptingTcl/ScriptingTcl_it.qm | Bin 23 -> 0 bytes Plugins/ScriptingTcl/ScriptingTcl_it.ts | 23 - Plugins/ScriptingTcl/ScriptingTcl_pl.qm | Bin 653 -> 0 bytes Plugins/ScriptingTcl/ScriptingTcl_pl.ts | 26 - Plugins/ScriptingTcl/ScriptingTcl_pt_BR.qm | Bin 23 -> 0 bytes Plugins/ScriptingTcl/ScriptingTcl_pt_BR.ts | 23 - Plugins/ScriptingTcl/ScriptingTcl_ro_RO.qm | Bin 30 -> 0 bytes Plugins/ScriptingTcl/ScriptingTcl_ro_RO.ts | 23 - Plugins/ScriptingTcl/ScriptingTcl_ru.qm | Bin 620 -> 0 bytes Plugins/ScriptingTcl/ScriptingTcl_ru.ts | 23 - Plugins/ScriptingTcl/ScriptingTcl_sk.qm | Bin 27 -> 0 bytes Plugins/ScriptingTcl/ScriptingTcl_sk.ts | 23 - Plugins/ScriptingTcl/ScriptingTcl_zh_CN.qm | 1 - Plugins/ScriptingTcl/ScriptingTcl_zh_CN.ts | 23 - Plugins/ScriptingTcl/scriptingtcl.cpp | 59 +- Plugins/ScriptingTcl/scriptingtcl.h | 9 +- Plugins/ScriptingTcl/scriptingtcl.qrc | 15 - Plugins/ScriptingTcl/translations/ScriptingTcl.ts | 27 + .../translations/ScriptingTcl_af_ZA.ts | 27 + .../translations/ScriptingTcl_ar_SA.ts | 27 + .../translations/ScriptingTcl_ca_ES.ts | 27 + .../translations/ScriptingTcl_cs_CZ.ts | 27 + .../translations/ScriptingTcl_da_DK.ts | 27 + .../translations/ScriptingTcl_de_DE.ts | 27 + .../translations/ScriptingTcl_el_GR.ts | 27 + .../translations/ScriptingTcl_en_US.ts | 27 + .../translations/ScriptingTcl_es_ES.ts | 27 + .../translations/ScriptingTcl_fa_IR.ts | 27 + .../translations/ScriptingTcl_fi_FI.ts | 27 + .../translations/ScriptingTcl_fr_FR.ts | 27 + .../translations/ScriptingTcl_he_IL.ts | 27 + .../translations/ScriptingTcl_hu_HU.ts | 27 + .../translations/ScriptingTcl_it_IT.ts | 27 + .../translations/ScriptingTcl_ja_JP.ts | 27 + .../ScriptingTcl/translations/ScriptingTcl_kaa.ts | 27 + .../translations/ScriptingTcl_ko_KR.ts | 27 + .../translations/ScriptingTcl_nl_NL.ts | 27 + .../translations/ScriptingTcl_no_NO.ts | 27 + .../translations/ScriptingTcl_pl_PL.ts | 27 + .../translations/ScriptingTcl_pt_BR.ts | 27 + .../translations/ScriptingTcl_pt_PT.ts | 27 + .../translations/ScriptingTcl_ro_RO.ts | 27 + .../translations/ScriptingTcl_ru_RU.ts | 27 + .../translations/ScriptingTcl_sk_SK.ts | 27 + .../translations/ScriptingTcl_sr_SP.ts | 27 + .../translations/ScriptingTcl_sv_SE.ts | 27 + .../translations/ScriptingTcl_tr_TR.ts | 27 + .../translations/ScriptingTcl_uk_UA.ts | 27 + .../translations/ScriptingTcl_vi_VN.ts | 27 + .../translations/ScriptingTcl_zh_CN.ts | 27 + .../translations/ScriptingTcl_zh_TW.ts | 27 + .../SqlEnterpriseFormatter.pro | 17 +- .../SqlEnterpriseFormatter_de.qm | Bin 23 -> 0 bytes .../SqlEnterpriseFormatter_de.ts | 228 - .../SqlEnterpriseFormatter_es.qm | Bin 23 -> 0 bytes .../SqlEnterpriseFormatter_es.ts | 228 - .../SqlEnterpriseFormatter_fr.qm | Bin 6266 -> 0 bytes .../SqlEnterpriseFormatter_fr.ts | 228 - .../SqlEnterpriseFormatter_it.qm | Bin 23 -> 0 bytes .../SqlEnterpriseFormatter_it.ts | 228 - .../SqlEnterpriseFormatter_pl.qm | Bin 6121 -> 0 bytes .../SqlEnterpriseFormatter_pl.ts | 229 - .../SqlEnterpriseFormatter_pt_BR.qm | Bin 23 -> 0 bytes .../SqlEnterpriseFormatter_pt_BR.ts | 228 - .../SqlEnterpriseFormatter_ro_RO.qm | Bin 30 -> 0 bytes .../SqlEnterpriseFormatter_ro_RO.ts | 228 - .../SqlEnterpriseFormatter_ru.qm | Bin 6230 -> 0 bytes .../SqlEnterpriseFormatter_ru.ts | 232 - .../SqlEnterpriseFormatter_sk.qm | Bin 27 -> 0 bytes .../SqlEnterpriseFormatter_sk.ts | 228 - .../SqlEnterpriseFormatter_zh_CN.qm | 1 - .../SqlEnterpriseFormatter_zh_CN.ts | 228 - Plugins/SqlEnterpriseFormatter/formatcopy.cpp | 25 - Plugins/SqlEnterpriseFormatter/formatcopy.h | 20 - .../SqlEnterpriseFormatter/formatcreatetable.cpp | 20 +- .../formatcreatevirtualtable.cpp | 1 + Plugins/SqlEnterpriseFormatter/formatdelete.cpp | 6 + Plugins/SqlEnterpriseFormatter/formatexpr.cpp | 15 + Plugins/SqlEnterpriseFormatter/formatinsert.cpp | 5 + Plugins/SqlEnterpriseFormatter/formatselect.cpp | 11 +- Plugins/SqlEnterpriseFormatter/formatstatement.cpp | 19 +- Plugins/SqlEnterpriseFormatter/formatupdate.cpp | 5 + Plugins/SqlEnterpriseFormatter/formatwith.cpp | 13 +- .../sqlenterpriseformatter.cpp | 7 +- .../sqlenterpriseformatter.qrc | 15 - .../translations/SqlEnterpriseFormatter.ts | 228 + .../translations/SqlEnterpriseFormatter_af_ZA.ts | 228 + .../translations/SqlEnterpriseFormatter_ar_SA.ts | 228 + .../translations/SqlEnterpriseFormatter_ca_ES.ts | 228 + .../translations/SqlEnterpriseFormatter_cs_CZ.ts | 228 + .../translations/SqlEnterpriseFormatter_da_DK.ts | 228 + .../translations/SqlEnterpriseFormatter_de_DE.ts | 228 + .../translations/SqlEnterpriseFormatter_el_GR.ts | 228 + .../translations/SqlEnterpriseFormatter_en_US.ts | 228 + .../translations/SqlEnterpriseFormatter_es_ES.ts | 228 + .../translations/SqlEnterpriseFormatter_fa_IR.ts | 228 + .../translations/SqlEnterpriseFormatter_fi_FI.ts | 228 + .../translations/SqlEnterpriseFormatter_fr_FR.ts | 228 + .../translations/SqlEnterpriseFormatter_he_IL.ts | 228 + .../translations/SqlEnterpriseFormatter_hu_HU.ts | 228 + .../translations/SqlEnterpriseFormatter_it_IT.ts | 228 + .../translations/SqlEnterpriseFormatter_ja_JP.ts | 228 + .../translations/SqlEnterpriseFormatter_kaa.ts | 228 + .../translations/SqlEnterpriseFormatter_ko_KR.ts | 228 + .../translations/SqlEnterpriseFormatter_nl_NL.ts | 228 + .../translations/SqlEnterpriseFormatter_no_NO.ts | 228 + .../translations/SqlEnterpriseFormatter_pl_PL.ts | 228 + .../translations/SqlEnterpriseFormatter_pt_BR.ts | 228 + .../translations/SqlEnterpriseFormatter_pt_PT.ts | 228 + .../translations/SqlEnterpriseFormatter_ro_RO.ts | 228 + .../translations/SqlEnterpriseFormatter_ru_RU.ts | 228 + .../translations/SqlEnterpriseFormatter_sk_SK.ts | 228 + .../translations/SqlEnterpriseFormatter_sr_SP.ts | 228 + .../translations/SqlEnterpriseFormatter_sv_SE.ts | 228 + .../translations/SqlEnterpriseFormatter_tr_TR.ts | 228 + .../translations/SqlEnterpriseFormatter_uk_UA.ts | 228 + .../translations/SqlEnterpriseFormatter_vi_VN.ts | 228 + .../translations/SqlEnterpriseFormatter_zh_CN.ts | 228 + .../translations/SqlEnterpriseFormatter_zh_TW.ts | 228 + Plugins/SqlExport/SqlExport.pro | 28 - Plugins/SqlExport/SqlExportCommon.ui | 22 +- Plugins/SqlExport/SqlExportQuery.ui | 62 +- Plugins/SqlExport/SqlExport_de.qm | Bin 23 -> 0 bytes Plugins/SqlExport/SqlExport_de.ts | 98 - Plugins/SqlExport/SqlExport_es.qm | Bin 23 -> 0 bytes Plugins/SqlExport/SqlExport_es.ts | 98 - Plugins/SqlExport/SqlExport_fr.qm | Bin 2914 -> 0 bytes Plugins/SqlExport/SqlExport_fr.ts | 98 - Plugins/SqlExport/SqlExport_it.qm | Bin 23 -> 0 bytes Plugins/SqlExport/SqlExport_it.ts | 98 - Plugins/SqlExport/SqlExport_pl.qm | Bin 2768 -> 0 bytes Plugins/SqlExport/SqlExport_pl.ts | 99 - Plugins/SqlExport/SqlExport_pt_BR.qm | Bin 23 -> 0 bytes Plugins/SqlExport/SqlExport_pt_BR.ts | 98 - Plugins/SqlExport/SqlExport_ro_RO.qm | Bin 30 -> 0 bytes Plugins/SqlExport/SqlExport_ro_RO.ts | 98 - Plugins/SqlExport/SqlExport_ru.qm | Bin 2991 -> 0 bytes Plugins/SqlExport/SqlExport_ru.ts | 98 - Plugins/SqlExport/SqlExport_sk.qm | Bin 27 -> 0 bytes Plugins/SqlExport/SqlExport_sk.ts | 98 - Plugins/SqlExport/SqlExport_zh_CN.qm | Bin 589 -> 0 bytes Plugins/SqlExport/SqlExport_zh_CN.ts | 98 - Plugins/SqlExport/sqlexport.cpp | 55 +- Plugins/SqlExport/sqlexport.h | 1 + Plugins/SqlExport/sqlexport.json | 2 +- Plugins/SqlExport/sqlexport.qrc | 15 - Plugins/SqlExport/translations/SqlExport.ts | 108 + Plugins/SqlExport/translations/SqlExport_af_ZA.ts | 108 + Plugins/SqlExport/translations/SqlExport_ar_SA.ts | 108 + Plugins/SqlExport/translations/SqlExport_ca_ES.ts | 108 + Plugins/SqlExport/translations/SqlExport_cs_CZ.ts | 108 + Plugins/SqlExport/translations/SqlExport_da_DK.ts | 108 + Plugins/SqlExport/translations/SqlExport_de_DE.ts | 108 + Plugins/SqlExport/translations/SqlExport_el_GR.ts | 108 + Plugins/SqlExport/translations/SqlExport_en_US.ts | 108 + Plugins/SqlExport/translations/SqlExport_es_ES.ts | 108 + Plugins/SqlExport/translations/SqlExport_fa_IR.ts | 108 + Plugins/SqlExport/translations/SqlExport_fi_FI.ts | 108 + Plugins/SqlExport/translations/SqlExport_fr_FR.ts | 108 + Plugins/SqlExport/translations/SqlExport_he_IL.ts | 108 + Plugins/SqlExport/translations/SqlExport_hu_HU.ts | 108 + Plugins/SqlExport/translations/SqlExport_it_IT.ts | 108 + Plugins/SqlExport/translations/SqlExport_ja_JP.ts | 108 + Plugins/SqlExport/translations/SqlExport_kaa.ts | 108 + Plugins/SqlExport/translations/SqlExport_ko_KR.ts | 108 + Plugins/SqlExport/translations/SqlExport_nl_NL.ts | 108 + Plugins/SqlExport/translations/SqlExport_no_NO.ts | 108 + Plugins/SqlExport/translations/SqlExport_pl_PL.ts | 108 + Plugins/SqlExport/translations/SqlExport_pt_BR.ts | 108 + Plugins/SqlExport/translations/SqlExport_pt_PT.ts | 108 + Plugins/SqlExport/translations/SqlExport_ro_RO.ts | 108 + Plugins/SqlExport/translations/SqlExport_ru_RU.ts | 108 + Plugins/SqlExport/translations/SqlExport_sk_SK.ts | 108 + Plugins/SqlExport/translations/SqlExport_sr_SP.ts | 108 + Plugins/SqlExport/translations/SqlExport_sv_SE.ts | 108 + Plugins/SqlExport/translations/SqlExport_tr_TR.ts | 108 + Plugins/SqlExport/translations/SqlExport_uk_UA.ts | 108 + Plugins/SqlExport/translations/SqlExport_vi_VN.ts | 108 + Plugins/SqlExport/translations/SqlExport_zh_CN.ts | 108 + Plugins/SqlExport/translations/SqlExport_zh_TW.ts | 108 + Plugins/SqlFormatterSimple/SqlFormatterSimple.pro | 28 - .../SqlFormatterSimple/SqlFormatterSimple_de.qm | Bin 23 -> 0 bytes .../SqlFormatterSimple/SqlFormatterSimple_de.ts | 17 - .../SqlFormatterSimple/SqlFormatterSimple_es.qm | Bin 23 -> 0 bytes .../SqlFormatterSimple/SqlFormatterSimple_es.ts | 17 - .../SqlFormatterSimple/SqlFormatterSimple_fr.qm | Bin 346 -> 0 bytes .../SqlFormatterSimple/SqlFormatterSimple_fr.ts | 17 - .../SqlFormatterSimple/SqlFormatterSimple_it.qm | Bin 23 -> 0 bytes .../SqlFormatterSimple/SqlFormatterSimple_it.ts | 17 - .../SqlFormatterSimple/SqlFormatterSimple_pl.qm | Bin 376 -> 0 bytes .../SqlFormatterSimple/SqlFormatterSimple_pl.ts | 17 - .../SqlFormatterSimple/SqlFormatterSimple_pt_BR.qm | Bin 23 -> 0 bytes .../SqlFormatterSimple/SqlFormatterSimple_pt_BR.ts | 17 - .../SqlFormatterSimple/SqlFormatterSimple_ro_RO.qm | Bin 30 -> 0 bytes .../SqlFormatterSimple/SqlFormatterSimple_ro_RO.ts | 17 - .../SqlFormatterSimple/SqlFormatterSimple_ru.qm | Bin 379 -> 0 bytes .../SqlFormatterSimple/SqlFormatterSimple_ru.ts | 17 - .../SqlFormatterSimple/SqlFormatterSimple_sk.qm | Bin 27 -> 0 bytes .../SqlFormatterSimple/SqlFormatterSimple_sk.ts | 17 - .../SqlFormatterSimple/SqlFormatterSimple_zh_CN.qm | Bin 233 -> 0 bytes .../SqlFormatterSimple/SqlFormatterSimple_zh_CN.ts | 17 - Plugins/SqlFormatterSimple/sqlformattersimple.qrc | 15 - .../sqlformattersimpleplugin.cpp | 6 +- .../translations/SqlFormatterSimple.ts | 17 + .../translations/SqlFormatterSimple_af_ZA.ts | 17 + .../translations/SqlFormatterSimple_ar_SA.ts | 17 + .../translations/SqlFormatterSimple_ca_ES.ts | 17 + .../translations/SqlFormatterSimple_cs_CZ.ts | 17 + .../translations/SqlFormatterSimple_da_DK.ts | 17 + .../translations/SqlFormatterSimple_de_DE.ts | 17 + .../translations/SqlFormatterSimple_el_GR.ts | 17 + .../translations/SqlFormatterSimple_en_US.ts | 17 + .../translations/SqlFormatterSimple_es_ES.ts | 17 + .../translations/SqlFormatterSimple_fa_IR.ts | 17 + .../translations/SqlFormatterSimple_fi_FI.ts | 17 + .../translations/SqlFormatterSimple_fr_FR.ts | 17 + .../translations/SqlFormatterSimple_he_IL.ts | 17 + .../translations/SqlFormatterSimple_hu_HU.ts | 17 + .../translations/SqlFormatterSimple_it_IT.ts | 17 + .../translations/SqlFormatterSimple_ja_JP.ts | 17 + .../translations/SqlFormatterSimple_kaa.ts | 17 + .../translations/SqlFormatterSimple_ko_KR.ts | 17 + .../translations/SqlFormatterSimple_nl_NL.ts | 17 + .../translations/SqlFormatterSimple_no_NO.ts | 17 + .../translations/SqlFormatterSimple_pl_PL.ts | 17 + .../translations/SqlFormatterSimple_pt_BR.ts | 17 + .../translations/SqlFormatterSimple_pt_PT.ts | 17 + .../translations/SqlFormatterSimple_ro_RO.ts | 17 + .../translations/SqlFormatterSimple_ru_RU.ts | 17 + .../translations/SqlFormatterSimple_sk_SK.ts | 17 + .../translations/SqlFormatterSimple_sr_SP.ts | 17 + .../translations/SqlFormatterSimple_sv_SE.ts | 17 + .../translations/SqlFormatterSimple_tr_TR.ts | 17 + .../translations/SqlFormatterSimple_uk_UA.ts | 17 + .../translations/SqlFormatterSimple_vi_VN.ts | 17 + .../translations/SqlFormatterSimple_zh_CN.ts | 17 + .../translations/SqlFormatterSimple_zh_TW.ts | 17 + Plugins/XmlExport/XmlExport.pro | 28 - Plugins/XmlExport/XmlExport_de.qm | Bin 23 -> 0 bytes Plugins/XmlExport/XmlExport_de.ts | 70 - Plugins/XmlExport/XmlExport_es.qm | Bin 23 -> 0 bytes Plugins/XmlExport/XmlExport_es.ts | 70 - Plugins/XmlExport/XmlExport_fr.qm | Bin 2681 -> 0 bytes Plugins/XmlExport/XmlExport_fr.ts | 70 - Plugins/XmlExport/XmlExport_it.qm | Bin 23 -> 0 bytes Plugins/XmlExport/XmlExport_it.ts | 70 - Plugins/XmlExport/XmlExport_pl.qm | Bin 2551 -> 0 bytes Plugins/XmlExport/XmlExport_pl.ts | 71 - Plugins/XmlExport/XmlExport_pt_BR.qm | Bin 23 -> 0 bytes Plugins/XmlExport/XmlExport_pt_BR.ts | 70 - Plugins/XmlExport/XmlExport_ro_RO.qm | Bin 30 -> 0 bytes Plugins/XmlExport/XmlExport_ro_RO.ts | 70 - Plugins/XmlExport/XmlExport_ru.qm | Bin 2658 -> 0 bytes Plugins/XmlExport/XmlExport_ru.ts | 70 - Plugins/XmlExport/XmlExport_sk.qm | Bin 27 -> 0 bytes Plugins/XmlExport/XmlExport_sk.ts | 70 - Plugins/XmlExport/XmlExport_zh_CN.qm | 1 - Plugins/XmlExport/XmlExport_zh_CN.ts | 70 - Plugins/XmlExport/translations/XmlExport.ts | 70 + Plugins/XmlExport/translations/XmlExport_af_ZA.ts | 70 + Plugins/XmlExport/translations/XmlExport_ar_SA.ts | 70 + Plugins/XmlExport/translations/XmlExport_ca_ES.ts | 70 + Plugins/XmlExport/translations/XmlExport_cs_CZ.ts | 70 + Plugins/XmlExport/translations/XmlExport_da_DK.ts | 70 + Plugins/XmlExport/translations/XmlExport_de_DE.ts | 70 + Plugins/XmlExport/translations/XmlExport_el_GR.ts | 70 + Plugins/XmlExport/translations/XmlExport_en_US.ts | 70 + Plugins/XmlExport/translations/XmlExport_es_ES.ts | 70 + Plugins/XmlExport/translations/XmlExport_fa_IR.ts | 70 + Plugins/XmlExport/translations/XmlExport_fi_FI.ts | 70 + Plugins/XmlExport/translations/XmlExport_fr_FR.ts | 70 + Plugins/XmlExport/translations/XmlExport_he_IL.ts | 70 + Plugins/XmlExport/translations/XmlExport_hu_HU.ts | 70 + Plugins/XmlExport/translations/XmlExport_it_IT.ts | 70 + Plugins/XmlExport/translations/XmlExport_ja_JP.ts | 70 + Plugins/XmlExport/translations/XmlExport_kaa.ts | 70 + Plugins/XmlExport/translations/XmlExport_ko_KR.ts | 70 + Plugins/XmlExport/translations/XmlExport_nl_NL.ts | 70 + Plugins/XmlExport/translations/XmlExport_no_NO.ts | 70 + Plugins/XmlExport/translations/XmlExport_pl_PL.ts | 70 + Plugins/XmlExport/translations/XmlExport_pt_BR.ts | 70 + Plugins/XmlExport/translations/XmlExport_pt_PT.ts | 70 + Plugins/XmlExport/translations/XmlExport_ro_RO.ts | 70 + Plugins/XmlExport/translations/XmlExport_ru_RU.ts | 70 + Plugins/XmlExport/translations/XmlExport_sk_SK.ts | 70 + Plugins/XmlExport/translations/XmlExport_sr_SP.ts | 70 + Plugins/XmlExport/translations/XmlExport_sv_SE.ts | 70 + Plugins/XmlExport/translations/XmlExport_tr_TR.ts | 70 + Plugins/XmlExport/translations/XmlExport_uk_UA.ts | 70 + Plugins/XmlExport/translations/XmlExport_vi_VN.ts | 70 + Plugins/XmlExport/translations/XmlExport_zh_CN.ts | 70 + Plugins/XmlExport/translations/XmlExport_zh_TW.ts | 70 + Plugins/XmlExport/xmlexport.cpp | 9 +- Plugins/XmlExport/xmlexport.qrc | 15 - README.md | 8 +- SQLiteStudio-installer.xml | 414 + SQLiteStudio3/SQLiteStudio3.pro | 10 +- .../CompletionHelperTest/CompletionHelperTest.pro | 2 +- SQLiteStudio3/Tests/LexerTest/tst_lexertest.cpp | 11 + SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp | 116 +- .../SelectResolverTest/tst_selectresolvertest.cpp | 78 +- SQLiteStudio3/Tests/TestUtils/TestUtils.pro | 2 +- SQLiteStudio3/Tests/TestUtils/configmock.cpp | 14 - SQLiteStudio3/Tests/TestUtils/configmock.h | 3 - .../Tests/TestUtils/extensionmanagermock.cpp | 5 + .../Tests/TestUtils/extensionmanagermock.h | 1 + SQLiteStudio3/Tests/TestUtils/test_common.pri | 4 +- SQLiteStudio3/Tests/testcommon.pri | 4 + SQLiteStudio3/Tests/testdirs.pri | 4 - SQLiteStudio3/common.pri | 62 + SQLiteStudio3/coreSQLiteStudio/Info.plist | 2 +- .../coreSQLiteStudio/chillout/LICENSE-chillout | 21 + SQLiteStudio3/coreSQLiteStudio/chillout/README | 4 + .../coreSQLiteStudio/chillout/chillout.cpp | 47 + SQLiteStudio3/coreSQLiteStudio/chillout/chillout.h | 42 + .../coreSQLiteStudio/chillout/common/common.cpp | 24 + .../coreSQLiteStudio/chillout/common/common.h | 24 + SQLiteStudio3/coreSQLiteStudio/chillout/defines.h | 16 + .../chillout/posix/posixcrashhandler.cpp | 146 + .../chillout/posix/posixcrashhandler.h | 45 + .../chillout/windows/StackWalker.cpp | 1382 + .../chillout/windows/StackWalker.h | 222 + .../chillout/windows/windowscrashhandler.cpp | 671 + .../chillout/windows/windowscrashhandler.h | 147 + .../coreSQLiteStudio/common/bistrhash.cpp | 25 + SQLiteStudio3/coreSQLiteStudio/common/bistrhash.h | 21 +- .../coreSQLiteStudio/common/compatibility.cpp | 7 + .../coreSQLiteStudio/common/compatibility.h | 3 + SQLiteStudio3/coreSQLiteStudio/common/global.h | 19 +- SQLiteStudio3/coreSQLiteStudio/common/utils.cpp | 39 +- SQLiteStudio3/coreSQLiteStudio/common/utils.h | 37 +- .../coreSQLiteStudio/common/utils_sql.cpp | 94 +- SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h | 2 + .../coreSQLiteStudio/completioncomparer.cpp | 47 +- .../coreSQLiteStudio/completioncomparer.h | 5 +- .../coreSQLiteStudio/completionhelper.cpp | 89 +- SQLiteStudio3/coreSQLiteStudio/completionhelper.h | 26 +- SQLiteStudio3/coreSQLiteStudio/config_builder.h | 18 +- .../config_builder/cfgcategory.cpp | 20 +- .../coreSQLiteStudio/config_builder/cfgentry.cpp | 9 +- .../coreSQLiteStudio/config_builder/cfgentry.h | 3 +- .../config_builder/cfglazyinitializer.cpp | 2 +- .../coreSQLiteStudio/coreSQLiteStudio.pro | 49 +- .../coreSQLiteStudio/coreSQLiteStudio.qrc | 10 +- SQLiteStudio3/coreSQLiteStudio/csvformat.h | 1 + SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp | 2 +- SQLiteStudio3/coreSQLiteStudio/datatype.cpp | 55 +- SQLiteStudio3/coreSQLiteStudio/datatype.h | 22 +- SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp | 81 +- SQLiteStudio3/coreSQLiteStudio/db/abstractdb.h | 35 +- SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h | 70 +- SQLiteStudio3/coreSQLiteStudio/db/db.cpp | 2 +- SQLiteStudio3/coreSQLiteStudio/db/db.h | 38 +- SQLiteStudio3/coreSQLiteStudio/db/dbsqlite3.cpp | 10 + SQLiteStudio3/coreSQLiteStudio/db/dbsqlite3.h | 4 +- SQLiteStudio3/coreSQLiteStudio/db/invaliddb.cpp | 22 +- SQLiteStudio3/coreSQLiteStudio/db/invaliddb.h | 12 +- .../coreSQLiteStudio/db/queryexecutor.cpp | 105 +- SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h | 31 +- .../queryexecutorsteps/queryexecutoraddrowids.cpp | 37 +- .../db/queryexecutorsteps/queryexecutoraddrowids.h | 2 +- .../queryexecutorsteps/queryexecutorcellsize.cpp | 132 - .../db/queryexecutorsteps/queryexecutorcellsize.h | 62 - .../db/queryexecutorsteps/queryexecutorcolumns.cpp | 35 +- .../queryexecutorsteps/queryexecutorcolumntype.cpp | 47 +- .../queryexecutorsteps/queryexecutorcolumntype.h | 6 +- .../queryexecutordatasources.cpp | 3 +- .../db/queryexecutorsteps/queryexecutorfilter.cpp | 26 + .../db/queryexecutorsteps/queryexecutorfilter.h | 20 + .../db/queryexecutorsteps/queryexecutorlimit.cpp | 1 - .../queryexecutorreplaceviews.cpp | 75 +- .../queryexecutorsteps/queryexecutorreplaceviews.h | 10 + .../db/queryexecutorsteps/queryexecutorstep.cpp | 3 +- .../db/queryexecutorsteps/queryexecutorstep.h | 1 - SQLiteStudio3/coreSQLiteStudio/db/sqlquery.cpp | 81 + SQLiteStudio3/coreSQLiteStudio/db/sqlquery.h | 8 + .../coreSQLiteStudio/db/stdsqlite3driver.h | 7 + .../coreSQLiteStudio/dbobjectorganizer.cpp | 71 +- SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.h | 4 +- SQLiteStudio3/coreSQLiteStudio/expectedtoken.cpp | 2 +- SQLiteStudio3/coreSQLiteStudio/importworker.cpp | 7 +- SQLiteStudio3/coreSQLiteStudio/licenses/icu.txt | 519 + SQLiteStudio3/coreSQLiteStudio/log.cpp | 5 + SQLiteStudio3/coreSQLiteStudio/log.h | 1 + .../parser/ast/sqlitealtertable.cpp | 68 +- .../coreSQLiteStudio/parser/ast/sqlitealtertable.h | 5 + .../coreSQLiteStudio/parser/ast/sqlitecopy.cpp | 92 - .../coreSQLiteStudio/parser/ast/sqlitecopy.h | 32 - .../parser/ast/sqlitecreateindex.cpp | 10 + .../parser/ast/sqlitecreateindex.h | 2 + .../parser/ast/sqlitecreatetable.cpp | 56 +- .../parser/ast/sqlitecreatetable.h | 10 +- .../parser/ast/sqlitecreatetrigger.cpp | 10 + .../parser/ast/sqlitecreatetrigger.h | 2 + .../parser/ast/sqlitecreateview.cpp | 10 + .../coreSQLiteStudio/parser/ast/sqlitecreateview.h | 2 + .../parser/ast/sqliteddlwithdbcontext.h | 3 + .../coreSQLiteStudio/parser/ast/sqlitedelete.cpp | 24 +- .../coreSQLiteStudio/parser/ast/sqlitedelete.h | 13 +- .../coreSQLiteStudio/parser/ast/sqliteexpr.cpp | 43 + .../coreSQLiteStudio/parser/ast/sqliteexpr.h | 6 + .../coreSQLiteStudio/parser/ast/sqliteinsert.cpp | 34 +- .../coreSQLiteStudio/parser/ast/sqliteinsert.h | 16 +- .../coreSQLiteStudio/parser/ast/sqliteorderby.cpp | 20 +- .../parser/ast/sqlitequerytype.cpp | 5 +- .../coreSQLiteStudio/parser/ast/sqliteselect.cpp | 24 +- .../coreSQLiteStudio/parser/ast/sqliteselect.h | 4 +- .../coreSQLiteStudio/parser/ast/sqlitestatement.h | 6 + .../coreSQLiteStudio/parser/ast/sqliteupdate.cpp | 24 +- .../coreSQLiteStudio/parser/ast/sqliteupdate.h | 3 +- .../parser/ast/sqlitewindowdefinition.cpp | 2 +- .../coreSQLiteStudio/parser/ast/sqlitewith.cpp | 22 +- .../coreSQLiteStudio/parser/ast/sqlitewith.h | 10 +- SQLiteStudio3/coreSQLiteStudio/parser/keywords.cpp | 12 +- SQLiteStudio3/coreSQLiteStudio/parser/lempar.c | 4 +- SQLiteStudio3/coreSQLiteStudio/parser/lexer.cpp | 11 +- SQLiteStudio3/coreSQLiteStudio/parser/lexer.h | 7 + .../coreSQLiteStudio/parser/lexer_low_lev.cpp | 129 +- .../coreSQLiteStudio/parser/lexer_low_lev.h | 3 +- SQLiteStudio3/coreSQLiteStudio/parser/parser.cpp | 2 +- .../parser/parser_helper_stubs.cpp | 10 + .../coreSQLiteStudio/parser/parser_helper_stubs.h | 16 + .../coreSQLiteStudio/parser/parsercontext.cpp | 14 +- .../coreSQLiteStudio/parser/sqlite3_parse.cpp | 6834 +- .../coreSQLiteStudio/parser/sqlite3_parse.h | 296 +- .../coreSQLiteStudio/parser/sqlite3_parse.y | 179 +- .../parser/statementtokenbuilder.cpp | 21 +- SQLiteStudio3/coreSQLiteStudio/parser/token.cpp | 2 + SQLiteStudio3/coreSQLiteStudio/parser/token.h | 3 +- .../coreSQLiteStudio/plugins/dbpluginsqlite3.cpp | 7 + .../coreSQLiteStudio/plugins/dbpluginsqlite3.h | 2 +- .../plugins/dbpluginstdfilebase.cpp | 6 + .../coreSQLiteStudio/plugins/populaterandom.cpp | 4 +- .../coreSQLiteStudio/plugins/populaterandom.h | 2 + .../plugins/populaterandomtext.cpp | 4 +- .../coreSQLiteStudio/plugins/populaterandomtext.h | 2 + .../coreSQLiteStudio/plugins/populatescript.cpp | 29 +- .../coreSQLiteStudio/plugins/scriptingplugin.h | 28 +- .../coreSQLiteStudio/plugins/scriptingqt.cpp | 157 +- .../coreSQLiteStudio/plugins/scriptingqt.h | 52 +- .../coreSQLiteStudio/plugins/scriptingqt.png | Bin 1750 -> 488 bytes .../plugins/scriptingqtdbproxy.cpp | 28 +- .../coreSQLiteStudio/plugins/scriptingqtdbproxy.h | 13 +- .../coreSQLiteStudio/plugins/scriptingsql.cpp | 29 +- .../coreSQLiteStudio/plugins/scriptingsql.h | 8 +- SQLiteStudio3/coreSQLiteStudio/querygenerator.cpp | 3 +- SQLiteStudio3/coreSQLiteStudio/querymodel.cpp | 3 +- SQLiteStudio3/coreSQLiteStudio/querymodel.h | 1 - SQLiteStudio3/coreSQLiteStudio/rsa/BigInt.cpp | 13 - SQLiteStudio3/coreSQLiteStudio/rsa/BigInt.h | 13 - SQLiteStudio3/coreSQLiteStudio/rsa/Key.cpp | 13 - SQLiteStudio3/coreSQLiteStudio/rsa/Key.h | 13 - SQLiteStudio3/coreSQLiteStudio/rsa/KeyPair.cpp | 13 - SQLiteStudio3/coreSQLiteStudio/rsa/KeyPair.h | 13 - .../coreSQLiteStudio/rsa/PrimeGenerator.cpp | 13 - .../coreSQLiteStudio/rsa/PrimeGenerator.h | 13 - SQLiteStudio3/coreSQLiteStudio/rsa/RSA.cpp | 13 - SQLiteStudio3/coreSQLiteStudio/rsa/RSA.h | 13 - SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp | 106 +- SQLiteStudio3/coreSQLiteStudio/schemaresolver.h | 12 +- SQLiteStudio3/coreSQLiteStudio/selectresolver.cpp | 296 +- SQLiteStudio3/coreSQLiteStudio/selectresolver.h | 58 +- .../services/codesnippetmanager.cpp | 119 + .../coreSQLiteStudio/services/codesnippetmanager.h | 42 + .../coreSQLiteStudio/services/collationmanager.h | 1 - SQLiteStudio3/coreSQLiteStudio/services/config.cpp | 59 + SQLiteStudio3/coreSQLiteStudio/services/config.h | 11 +- .../coreSQLiteStudio/services/functionmanager.h | 2 +- .../services/impl/collationmanagerimpl.cpp | 22 +- .../services/impl/collationmanagerimpl.h | 1 + .../coreSQLiteStudio/services/impl/configimpl.cpp | 115 +- .../coreSQLiteStudio/services/impl/configimpl.h | 13 +- .../services/impl/dbmanagerimpl.cpp | 30 +- .../services/impl/functionmanagerimpl.cpp | 82 +- .../services/impl/functionmanagerimpl.h | 1 + .../services/impl/pluginmanagerimpl.cpp | 44 +- .../services/impl/pluginmanagerimpl.h | 15 + .../services/impl/sqliteextensionmanagerimpl.cpp | 54 +- .../services/impl/sqliteextensionmanagerimpl.h | 3 + .../coreSQLiteStudio/services/notifymanager.cpp | 2 +- .../coreSQLiteStudio/services/notifymanager.h | 2 +- .../services/sqliteextensionmanager.h | 1 + SQLiteStudio3/coreSQLiteStudio/sqlfileexecutor.cpp | 223 + SQLiteStudio3/coreSQLiteStudio/sqlfileexecutor.h | 43 + SQLiteStudio3/coreSQLiteStudio/sqlhistorymodel.cpp | 2 - SQLiteStudio3/coreSQLiteStudio/sqlitestudio.cpp | 104 +- SQLiteStudio3/coreSQLiteStudio/sqlitestudio.h | 20 +- SQLiteStudio3/coreSQLiteStudio/tablemodifier.cpp | 74 +- SQLiteStudio3/coreSQLiteStudio/tablemodifier.h | 6 +- SQLiteStudio3/coreSQLiteStudio/translations.cpp | 19 +- .../translations/coreSQLiteStudio.ts | 1099 + .../translations/coreSQLiteStudio_af_ZA.ts | 1101 + .../translations/coreSQLiteStudio_ar_SA.ts | 1101 + .../translations/coreSQLiteStudio_ca_ES.ts | 1101 + .../translations/coreSQLiteStudio_cs_CZ.ts | 1101 + .../translations/coreSQLiteStudio_da_DK.ts | 1101 + .../translations/coreSQLiteStudio_de.qm | Bin 34589 -> 0 bytes .../translations/coreSQLiteStudio_de.ts | 1320 - .../translations/coreSQLiteStudio_de_DE.ts | 1100 + .../translations/coreSQLiteStudio_el_GR.ts | 1101 + .../translations/coreSQLiteStudio_en_US.ts | 1101 + .../translations/coreSQLiteStudio_es.qm | Bin 23 -> 0 bytes .../translations/coreSQLiteStudio_es.ts | 1146 - .../translations/coreSQLiteStudio_es_ES.ts | 1101 + .../translations/coreSQLiteStudio_fa_IR.ts | 1101 + .../translations/coreSQLiteStudio_fi_FI.ts | 1101 + .../translations/coreSQLiteStudio_fr.qm | Bin 31530 -> 0 bytes .../translations/coreSQLiteStudio_fr.ts | 1319 - .../translations/coreSQLiteStudio_fr_FR.ts | 1101 + .../translations/coreSQLiteStudio_he_IL.ts | 1101 + .../translations/coreSQLiteStudio_hu_HU.ts | 1101 + .../translations/coreSQLiteStudio_it.qm | Bin 23 -> 0 bytes .../translations/coreSQLiteStudio_it.ts | 1146 - .../translations/coreSQLiteStudio_it_IT.ts | 1101 + .../translations/coreSQLiteStudio_ja_JP.ts | 1101 + .../translations/coreSQLiteStudio_kaa.ts | 1101 + .../translations/coreSQLiteStudio_ko_KR.ts | 1101 + .../translations/coreSQLiteStudio_nl_NL.ts | 1101 + .../translations/coreSQLiteStudio_no_NO.ts | 1101 + .../translations/coreSQLiteStudio_pl.qm | Bin 34859 -> 0 bytes .../translations/coreSQLiteStudio_pl.ts | 1337 - .../translations/coreSQLiteStudio_pl_PL.ts | 1101 + .../translations/coreSQLiteStudio_pt_BR.qm | Bin 2828 -> 0 bytes .../translations/coreSQLiteStudio_pt_BR.ts | 1428 +- .../translations/coreSQLiteStudio_pt_PT.ts | 1101 + .../translations/coreSQLiteStudio_ro_RO.qm | Bin 30 -> 0 bytes .../translations/coreSQLiteStudio_ro_RO.ts | 1425 +- .../translations/coreSQLiteStudio_ru.qm | Bin 32776 -> 0 bytes .../translations/coreSQLiteStudio_ru.ts | 1318 - .../translations/coreSQLiteStudio_ru_RU.ts | 1101 + .../translations/coreSQLiteStudio_sk.qm | Bin 5124 -> 0 bytes .../translations/coreSQLiteStudio_sk.ts | 1178 - .../translations/coreSQLiteStudio_sk_SK.ts | 1101 + .../translations/coreSQLiteStudio_sr_SP.ts | 1101 + .../translations/coreSQLiteStudio_sv_SE.ts | 1101 + .../translations/coreSQLiteStudio_tr_TR.ts | 1101 + .../translations/coreSQLiteStudio_uk_UA.ts | 1101 + .../translations/coreSQLiteStudio_vi_VN.ts | 1101 + .../translations/coreSQLiteStudio_zh_CN.qm | 1 - .../translations/coreSQLiteStudio_zh_CN.ts | 1428 +- .../translations/coreSQLiteStudio_zh_TW.ts | 1101 + SQLiteStudio3/create_macosx_bundle.sh | 53 +- SQLiteStudio3/create_source_dist.sh | 4 +- SQLiteStudio3/dirs.pri | 50 - .../guiSQLiteStudio/common/datawidgetmapper.h | 4 +- .../guiSQLiteStudio/common/dbcombobox.cpp | 6 + SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.h | 5 + .../guiSQLiteStudio/common/extactioncontainer.cpp | 2 +- .../guiSQLiteStudio/common/extactioncontainer.h | 22 +- .../guiSQLiteStudio/common/extlineedit.cpp | 12 +- SQLiteStudio3/guiSQLiteStudio/common/extlineedit.h | 2 + .../guiSQLiteStudio/common/immediatetooltip.cpp | 43 + .../guiSQLiteStudio/common/immediatetooltip.h | 22 + .../guiSQLiteStudio/common/mouseshortcut.cpp | 77 + .../guiSQLiteStudio/common/mouseshortcut.h | 59 + .../guiSQLiteStudio/completer/completerview.cpp | 8 + .../guiSQLiteStudio/completer/completerwindow.cpp | 88 +- .../guiSQLiteStudio/completer/completerwindow.h | 21 +- .../guiSQLiteStudio/completer/completerwindow.ui | 66 +- SQLiteStudio3/guiSQLiteStudio/configmapper.cpp | 50 +- SQLiteStudio3/guiSQLiteStudio/configmapper.h | 11 +- .../guiSQLiteStudio/datagrid/fkcombobox.cpp | 345 + .../guiSQLiteStudio/datagrid/fkcombobox.h | 60 + .../datagrid/sqldatasourcequerymodel.cpp | 156 + .../datagrid/sqldatasourcequerymodel.h | 46 + .../guiSQLiteStudio/datagrid/sqlqueryitem.cpp | 114 +- .../guiSQLiteStudio/datagrid/sqlqueryitem.h | 43 +- .../datagrid/sqlqueryitemdelegate.cpp | 395 +- .../datagrid/sqlqueryitemdelegate.h | 40 +- .../guiSQLiteStudio/datagrid/sqlquerymodel.cpp | 152 +- .../guiSQLiteStudio/datagrid/sqlquerymodel.h | 29 +- .../datagrid/sqlquerymodelcolumn.cpp | 46 +- .../guiSQLiteStudio/datagrid/sqlquerymodelcolumn.h | 9 +- .../guiSQLiteStudio/datagrid/sqlqueryview.cpp | 182 +- .../guiSQLiteStudio/datagrid/sqlqueryview.h | 53 +- .../guiSQLiteStudio/datagrid/sqltablemodel.cpp | 123 +- .../guiSQLiteStudio/datagrid/sqltablemodel.h | 24 +- .../guiSQLiteStudio/datagrid/sqlviewmodel.cpp | 14 +- .../guiSQLiteStudio/datagrid/sqlviewmodel.h | 13 +- SQLiteStudio3/guiSQLiteStudio/dataview.cpp | 51 +- SQLiteStudio3/guiSQLiteStudio/dataview.h | 12 +- SQLiteStudio3/guiSQLiteStudio/dblistmodel.cpp | 3 + SQLiteStudio3/guiSQLiteStudio/dblistmodel.h | 3 + SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.cpp | 36 +- SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.h | 26 +- SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp | 302 +- SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h | 42 +- .../guiSQLiteStudio/dbtree/dbtreemodel.cpp | 3 +- .../guiSQLiteStudio/dbtree/dbtreeview.cpp | 9 + SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeview.h | 1 + .../guiSQLiteStudio/dialogs/aboutdialog.cpp | 49 +- .../guiSQLiteStudio/dialogs/aboutdialog.h | 3 - .../guiSQLiteStudio/dialogs/aboutdialog.ui | 134 +- .../guiSQLiteStudio/dialogs/bindparamsdialog.cpp | 7 +- .../guiSQLiteStudio/dialogs/bindparamsdialog.h | 2 +- .../guiSQLiteStudio/dialogs/columndialog.cpp | 46 +- .../guiSQLiteStudio/dialogs/columndialog.ui | 4 +- .../guiSQLiteStudio/dialogs/configdialog.cpp | 446 +- .../guiSQLiteStudio/dialogs/configdialog.h | 32 +- .../guiSQLiteStudio/dialogs/configdialog.ui | 716 +- SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp | 87 +- SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h | 6 +- SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui | 17 +- .../guiSQLiteStudio/dialogs/exportdialog.cpp | 3 +- .../guiSQLiteStudio/dialogs/importdialog.cpp | 8 +- .../guiSQLiteStudio/dialogs/indexdialog.cpp | 24 +- .../guiSQLiteStudio/dialogs/indexdialog.h | 1 + .../guiSQLiteStudio/dialogs/languagedialog.cpp | 6 + .../guiSQLiteStudio/dialogs/languagedialog.h | 3 + .../guiSQLiteStudio/dialogs/newversiondialog.ui | 4 +- .../dialogs/populateconfigdialog.cpp | 4 +- .../guiSQLiteStudio/dialogs/populatedialog.cpp | 13 +- .../dialogs/triggercolumnsdialog.cpp | 5 +- .../guiSQLiteStudio/dialogs/triggerdialog.cpp | 17 +- .../guiSQLiteStudio/dialogs/triggerdialog.h | 1 + SQLiteStudio3/guiSQLiteStudio/extendedpalette.cpp | 66 +- SQLiteStudio3/guiSQLiteStudio/extendedpalette.h | 17 +- SQLiteStudio3/guiSQLiteStudio/formmanager.cpp | 4 +- SQLiteStudio3/guiSQLiteStudio/formmanager.h | 4 +- SQLiteStudio3/guiSQLiteStudio/formview.cpp | 58 +- SQLiteStudio3/guiSQLiteStudio/formview.h | 26 +- SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.pro | 39 +- SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.qrc | 13 - SQLiteStudio3/guiSQLiteStudio/iconmanager.cpp | 2 +- SQLiteStudio3/guiSQLiteStudio/iconmanager.h | 9 +- SQLiteStudio3/guiSQLiteStudio/icons.qrc | 7 + .../img/apply_filter_txt_strict.png | Bin 0 -> 581 bytes .../guiSQLiteStudio/img/code_assistant.png | Bin 0 -> 431 bytes SQLiteStudio3/guiSQLiteStudio/img/code_snippet.png | Bin 0 -> 568 bytes .../guiSQLiteStudio/img/restore_default.png | Bin 0 -> 708 bytes .../guiSQLiteStudio/img/selection_invert.png | Bin 0 -> 352 bytes .../guiSQLiteStudio/img/sqlitestudio_256.png | Bin 0 -> 53220 bytes .../guiSQLiteStudio/img/sqlitestudio_48.png | Bin 0 -> 4061 bytes .../guiSQLiteStudio/img/sqlitestudio_installer.png | Bin 0 -> 32235 bytes .../guiSQLiteStudio/img/sqlitestudio_opt.ico | Bin 0 -> 99678 bytes .../guiSQLiteStudio/img/window_close_all_left.png | Bin 0 -> 703 bytes .../guiSQLiteStudio/img/window_close_all_right.png | Bin 0 -> 677 bytes SQLiteStudio3/guiSQLiteStudio/mainwindow.cpp | 129 +- SQLiteStudio3/guiSQLiteStudio/mainwindow.h | 49 +- SQLiteStudio3/guiSQLiteStudio/mdiarea.cpp | 46 +- SQLiteStudio3/guiSQLiteStudio/mdiarea.h | 6 + SQLiteStudio3/guiSQLiteStudio/mdichild.h | 3 + SQLiteStudio3/guiSQLiteStudio/mdiwindow.cpp | 25 +- SQLiteStudio3/guiSQLiteStudio/mdiwindow.h | 2 + .../guiSQLiteStudio/multieditor/multieditor.cpp | 25 +- .../guiSQLiteStudio/multieditor/multieditor.h | 18 +- .../multieditor/multieditorbool.cpp | 2 + .../multieditor/multieditordate.cpp | 2 + .../multieditor/multieditordatetime.cpp | 2 + .../multieditor/multieditordialog.cpp | 5 + .../multieditor/multieditordialog.h | 1 + .../guiSQLiteStudio/multieditor/multieditorfk.cpp | 49 + .../guiSQLiteStudio/multieditor/multieditorfk.h | 29 + .../guiSQLiteStudio/multieditor/multieditorhex.cpp | 1 + .../multieditor/multieditornumeric.cpp | 2 + .../multieditor/multieditortext.cpp | 2 + .../guiSQLiteStudio/multieditor/multieditortext.h | 4 +- .../multieditor/multieditortime.cpp | 2 + .../guiSQLiteStudio/qtscriptsyntaxhighlighter.cpp | 700 +- .../guiSQLiteStudio/qtscriptsyntaxhighlighter.h | 51 +- .../guiSQLiteStudio/searchtextlocator.cpp | 41 +- SQLiteStudio3/guiSQLiteStudio/searchtextlocator.h | 1 + SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp | 280 +- SQLiteStudio3/guiSQLiteStudio/sqleditor.h | 63 +- .../guiSQLiteStudio/sqlitesyntaxhighlighter.cpp | 165 +- .../guiSQLiteStudio/sqlitesyntaxhighlighter.h | 21 +- SQLiteStudio3/guiSQLiteStudio/statusfield.cpp | 17 +- SQLiteStudio3/guiSQLiteStudio/statusfield.h | 2 + SQLiteStudio3/guiSQLiteStudio/style.cpp | 59 +- SQLiteStudio3/guiSQLiteStudio/style.h | 11 + .../guiSQLiteStudio/syntaxhighlighterplugin.h | 2 + SQLiteStudio3/guiSQLiteStudio/taskbar.cpp | 4 +- SQLiteStudio3/guiSQLiteStudio/themetuner.cpp | 22 +- SQLiteStudio3/guiSQLiteStudio/themetuner.h | 6 +- .../translations/guiSQLiteStudio.ts | 7087 + .../translations/guiSQLiteStudio_af_ZA.ts | 7111 + .../translations/guiSQLiteStudio_ar_SA.ts | 7111 + .../translations/guiSQLiteStudio_ca_ES.ts | 7111 + .../translations/guiSQLiteStudio_cs_CZ.ts | 7111 + .../translations/guiSQLiteStudio_da_DK.ts | 7111 + .../translations/guiSQLiteStudio_de.qm | Bin 117878 -> 0 bytes .../translations/guiSQLiteStudio_de.ts | 7261 - .../translations/guiSQLiteStudio_de_DE.ts | 7107 + .../translations/guiSQLiteStudio_el_GR.ts | 7111 + .../translations/guiSQLiteStudio_en_US.ts | 7111 + .../translations/guiSQLiteStudio_es.qm | Bin 23 -> 0 bytes .../translations/guiSQLiteStudio_es.ts | 6612 - .../translations/guiSQLiteStudio_es_ES.ts | 7111 + .../translations/guiSQLiteStudio_fa_IR.ts | 7111 + .../translations/guiSQLiteStudio_fi_FI.ts | 7111 + .../translations/guiSQLiteStudio_fr.qm | Bin 123779 -> 0 bytes .../translations/guiSQLiteStudio_fr.ts | 7133 - .../translations/guiSQLiteStudio_fr_FR.ts | 7106 + .../translations/guiSQLiteStudio_he_IL.ts | 7111 + .../translations/guiSQLiteStudio_hu_HU.ts | 7111 + .../translations/guiSQLiteStudio_it.qm | Bin 23 -> 0 bytes .../translations/guiSQLiteStudio_it.ts | 6612 - .../translations/guiSQLiteStudio_it_IT.ts | 7108 + .../translations/guiSQLiteStudio_ja_JP.ts | 7111 + .../translations/guiSQLiteStudio_kaa.ts | 7111 + .../translations/guiSQLiteStudio_ko_KR.ts | 7111 + .../translations/guiSQLiteStudio_nl_NL.ts | 7111 + .../translations/guiSQLiteStudio_no_NO.ts | 7111 + .../translations/guiSQLiteStudio_pl.qm | Bin 170072 -> 0 bytes .../translations/guiSQLiteStudio_pl.ts | 7399 - .../translations/guiSQLiteStudio_pl_PL.ts | 7111 + .../translations/guiSQLiteStudio_pt_BR.qm | Bin 23 -> 0 bytes .../translations/guiSQLiteStudio_pt_BR.ts | 8730 +- .../translations/guiSQLiteStudio_pt_PT.ts | 7111 + .../translations/guiSQLiteStudio_ro_RO.qm | Bin 30 -> 0 bytes .../translations/guiSQLiteStudio_ro_RO.ts | 8723 +- .../translations/guiSQLiteStudio_ru.qm | Bin 152875 -> 0 bytes .../translations/guiSQLiteStudio_ru.ts | 7379 - .../translations/guiSQLiteStudio_ru_RU.ts | 7108 + .../translations/guiSQLiteStudio_sk.qm | Bin 84002 -> 0 bytes .../translations/guiSQLiteStudio_sk.ts | 7262 - .../translations/guiSQLiteStudio_sk_SK.ts | 7111 + .../translations/guiSQLiteStudio_sr_SP.ts | 7111 + .../translations/guiSQLiteStudio_sv_SE.ts | 7111 + .../translations/guiSQLiteStudio_tr_TR.ts | 7111 + .../translations/guiSQLiteStudio_uk_UA.ts | 7111 + .../translations/guiSQLiteStudio_vi_VN.ts | 7111 + .../translations/guiSQLiteStudio_zh_CN.qm | Bin 43220 -> 0 bytes .../translations/guiSQLiteStudio_zh_CN.ts | 9047 +- .../translations/guiSQLiteStudio_zh_TW.ts | 7106 + SQLiteStudio3/guiSQLiteStudio/uiconfig.cpp | 82 + SQLiteStudio3/guiSQLiteStudio/uiconfig.h | 137 +- SQLiteStudio3/guiSQLiteStudio/uiutils.cpp | 79 +- SQLiteStudio3/guiSQLiteStudio/uiutils.h | 8 +- .../guiSQLiteStudio/windows/codesnippeteditor.cpp | 350 + .../guiSQLiteStudio/windows/codesnippeteditor.h | 91 + .../guiSQLiteStudio/windows/codesnippeteditor.ui | 250 + .../windows/codesnippeteditormodel.cpp | 287 + .../windows/codesnippeteditormodel.h | 80 + .../guiSQLiteStudio/windows/collationseditor.cpp | 19 +- .../guiSQLiteStudio/windows/collationseditor.h | 6 + .../guiSQLiteStudio/windows/ddlhistorywindow.cpp | 24 +- .../guiSQLiteStudio/windows/ddlhistorywindow.h | 1 + .../guiSQLiteStudio/windows/ddlhistorywindow.ui | 18 +- .../guiSQLiteStudio/windows/editorwindow.cpp | 83 +- .../guiSQLiteStudio/windows/editorwindow.h | 29 +- .../guiSQLiteStudio/windows/editorwindow.ui | 4 +- .../guiSQLiteStudio/windows/functionseditor.cpp | 46 +- .../guiSQLiteStudio/windows/functionseditor.h | 7 +- .../guiSQLiteStudio/windows/functionseditor.ui | 25 +- .../windows/functionseditormodel.cpp | 18 +- .../guiSQLiteStudio/windows/functionseditormodel.h | 3 +- .../windows/sqliteextensioneditor.cpp | 27 +- .../windows/sqliteextensioneditor.h | 7 + .../windows/sqliteextensioneditor.ui | 12 + .../windows/sqliteextensioneditormodel.cpp | 2 +- .../windows/tablestructuremodel.cpp | 97 + .../guiSQLiteStudio/windows/tablestructuremodel.h | 1 + .../guiSQLiteStudio/windows/tablewindow.cpp | 80 +- .../guiSQLiteStudio/windows/tablewindow.h | 45 +- .../guiSQLiteStudio/windows/tablewindow.ui | 13 + .../guiSQLiteStudio/windows/viewwindow.cpp | 83 +- SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h | 28 +- SQLiteStudio3/lang.tcl | 358 +- SQLiteStudio3/plugins.pri | 6 +- SQLiteStudio3/sqlitestudio/installscript.qs | 64 - SQLiteStudio3/sqlitestudio/main.cpp | 21 +- SQLiteStudio3/sqlitestudio/register_file_types.ui | 64 - SQLiteStudio3/sqlitestudio/sqlitestudio.pro | 24 +- SQLiteStudio3/sqlitestudio/sqlitestudio.qrc | 16 +- .../sqlitestudio/translations/sqlitestudio.ts | 87 + .../translations/sqlitestudio_af_ZA.ts | 87 + .../translations/sqlitestudio_ar_SA.ts | 87 + .../translations/sqlitestudio_ca_ES.ts | 87 + .../translations/sqlitestudio_cs_CZ.ts | 87 + .../translations/sqlitestudio_da_DK.ts | 87 + .../sqlitestudio/translations/sqlitestudio_de.qm | Bin 2198 -> 0 bytes .../sqlitestudio/translations/sqlitestudio_de.ts | 88 - .../translations/sqlitestudio_de_DE.ts | 87 + .../translations/sqlitestudio_el_GR.ts | 87 + .../translations/sqlitestudio_en_US.ts | 87 + .../sqlitestudio/translations/sqlitestudio_es.qm | Bin 23 -> 0 bytes .../sqlitestudio/translations/sqlitestudio_es.ts | 82 - .../translations/sqlitestudio_es_ES.ts | 87 + .../translations/sqlitestudio_fa_IR.ts | 87 + .../translations/sqlitestudio_fi_FI.ts | 87 + .../sqlitestudio/translations/sqlitestudio_fr.qm | Bin 2323 -> 0 bytes .../sqlitestudio/translations/sqlitestudio_fr.ts | 86 - .../translations/sqlitestudio_fr_FR.ts | 87 + .../translations/sqlitestudio_he_IL.ts | 87 + .../translations/sqlitestudio_hu_HU.ts | 87 + .../sqlitestudio/translations/sqlitestudio_it.qm | Bin 23 -> 0 bytes .../sqlitestudio/translations/sqlitestudio_it.ts | 82 - .../translations/sqlitestudio_it_IT.ts | 87 + .../translations/sqlitestudio_ja_JP.ts | 87 + .../sqlitestudio/translations/sqlitestudio_kaa.ts | 87 + .../translations/sqlitestudio_ko_KR.ts | 87 + .../translations/sqlitestudio_nl_NL.ts | 87 + .../translations/sqlitestudio_no_NO.ts | 87 + .../sqlitestudio/translations/sqlitestudio_pl.qm | Bin 2810 -> 0 bytes .../sqlitestudio/translations/sqlitestudio_pl.ts | 71 - .../translations/sqlitestudio_pl_PL.ts | 87 + .../translations/sqlitestudio_pt_BR.qm | Bin 23 -> 0 bytes .../translations/sqlitestudio_pt_BR.ts | 101 +- .../translations/sqlitestudio_pt_PT.ts | 87 + .../translations/sqlitestudio_ro_RO.qm | Bin 30 -> 0 bytes .../translations/sqlitestudio_ro_RO.ts | 101 +- .../sqlitestudio/translations/sqlitestudio_ru.qm | Bin 2949 -> 0 bytes .../sqlitestudio/translations/sqlitestudio_ru.ts | 86 - .../translations/sqlitestudio_ru_RU.ts | 87 + .../sqlitestudio/translations/sqlitestudio_sk.qm | Bin 783 -> 0 bytes .../sqlitestudio/translations/sqlitestudio_sk.ts | 86 - .../translations/sqlitestudio_sk_SK.ts | 87 + .../translations/sqlitestudio_sr_SP.ts | 87 + .../translations/sqlitestudio_sv_SE.ts | 87 + .../translations/sqlitestudio_tr_TR.ts | 87 + .../translations/sqlitestudio_uk_UA.ts | 87 + .../translations/sqlitestudio_vi_VN.ts | 87 + .../translations/sqlitestudio_zh_CN.qm | Bin 361 -> 0 bytes .../translations/sqlitestudio_zh_CN.ts | 101 +- .../translations/sqlitestudio_zh_TW.ts | 87 + SQLiteStudio3/sqlitestudiocli/cli.cpp | 9 +- SQLiteStudio3/sqlitestudiocli/cli.h | 2 +- .../sqlitestudiocli/commands/clicommand.cpp | 2 +- .../sqlitestudiocli/commands/clicommandcd.cpp | 2 +- .../sqlitestudiocli/commands/clicommandclose.cpp | 6 +- .../sqlitestudiocli/commands/clicommanddblist.cpp | 2 +- .../sqlitestudiocli/commands/clicommanddesc.cpp | 6 +- .../sqlitestudiocli/commands/clicommandhelp.cpp | 5 +- .../sqlitestudiocli/commands/clicommandmode.cpp | 2 +- .../sqlitestudiocli/commands/clicommandopen.cpp | 4 +- .../sqlitestudiocli/commands/clicommandpwd.cpp | 2 +- .../sqlitestudiocli/commands/clicommandsql.cpp | 32 +- SQLiteStudio3/sqlitestudiocli/main.cpp | 106 +- SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.pro | 26 +- SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.qrc | 20 +- .../translations/sqlitestudiocli.ts | 833 + .../translations/sqlitestudiocli_af_ZA.ts | 876 + .../translations/sqlitestudiocli_ar_SA.ts | 876 + .../translations/sqlitestudiocli_ca_ES.ts | 876 + .../translations/sqlitestudiocli_cs_CZ.ts | 876 + .../translations/sqlitestudiocli_da_DK.ts | 876 + .../translations/sqlitestudiocli_de.qm | Bin 23 -> 0 bytes .../translations/sqlitestudiocli_de.ts | 788 - .../translations/sqlitestudiocli_de_DE.ts | 876 + .../translations/sqlitestudiocli_el_GR.ts | 876 + .../translations/sqlitestudiocli_en_US.ts | 876 + .../translations/sqlitestudiocli_es.qm | Bin 23 -> 0 bytes .../translations/sqlitestudiocli_es.ts | 788 - .../translations/sqlitestudiocli_es_ES.ts | 875 + .../translations/sqlitestudiocli_fa_IR.ts | 876 + .../translations/sqlitestudiocli_fi_FI.ts | 876 + .../translations/sqlitestudiocli_fr.qm | Bin 41642 -> 0 bytes .../translations/sqlitestudiocli_fr.ts | 830 - .../translations/sqlitestudiocli_fr_FR.ts | 875 + .../translations/sqlitestudiocli_he_IL.ts | 876 + .../translations/sqlitestudiocli_hu_HU.ts | 876 + .../translations/sqlitestudiocli_it.qm | Bin 23 -> 0 bytes .../translations/sqlitestudiocli_it.ts | 788 - .../translations/sqlitestudiocli_it_IT.ts | 876 + .../translations/sqlitestudiocli_ja_JP.ts | 876 + .../translations/sqlitestudiocli_kaa.ts | 876 + .../translations/sqlitestudiocli_ko_KR.ts | 876 + .../translations/sqlitestudiocli_nl_NL.ts | 876 + .../translations/sqlitestudiocli_no_NO.ts | 876 + .../translations/sqlitestudiocli_pl.qm | Bin 40792 -> 0 bytes .../translations/sqlitestudiocli_pl.ts | 690 - .../translations/sqlitestudiocli_pl_PL.ts | 874 + .../translations/sqlitestudiocli_pt_BR.qm | Bin 23 -> 0 bytes .../translations/sqlitestudiocli_pt_BR.ts | 1013 +- .../translations/sqlitestudiocli_pt_PT.ts | 876 + .../translations/sqlitestudiocli_ro_RO.qm | Bin 30 -> 0 bytes .../translations/sqlitestudiocli_ro_RO.ts | 1012 +- .../translations/sqlitestudiocli_ru.qm | Bin 41595 -> 0 bytes .../translations/sqlitestudiocli_ru.ts | 829 - .../translations/sqlitestudiocli_ru_RU.ts | 874 + .../translations/sqlitestudiocli_sk.qm | Bin 27 -> 0 bytes .../translations/sqlitestudiocli_sk.ts | 788 - .../translations/sqlitestudiocli_sk_SK.ts | 876 + .../translations/sqlitestudiocli_sr_SP.ts | 876 + .../translations/sqlitestudiocli_sv_SE.ts | 876 + .../translations/sqlitestudiocli_tr_TR.ts | 876 + .../translations/sqlitestudiocli_uk_UA.ts | 876 + .../translations/sqlitestudiocli_vi_VN.ts | 876 + .../translations/sqlitestudiocli_zh_CN.qm | Bin 1327 -> 0 bytes .../translations/sqlitestudiocli_zh_CN.ts | 1013 +- .../translations/sqlitestudiocli_zh_TW.ts | 876 + SQLiteStudio3/ts.xsd | 189 + crowdin.yml | 5 + release-checklist.md | 14 +- scripts/installer/assemble.tcl | 422 - scripts/installer/config/bg.png | Bin 218806 -> 0 bytes scripts/installer/config/config.xml | 27 - scripts/installer/config/controller.qs | 5 - scripts/installer/config/logo.png | Bin 3980 -> 0 bytes scripts/installer/config/sqlitestudio.icns | Bin 287952 -> 0 bytes scripts/installer/config/sqlitestudio.ico | Bin 297966 -> 0 bytes scripts/installer/config/watermark.png | Bin 218806 -> 0 bytes scripts/macosx/compile_build_bundle.sh | 30 +- win_deps/win32_deps.zip | Bin 0 -> 2925185 bytes win_deps/win64_deps.zip | Bin 0 -> 3890813 bytes 1645 files changed, 521810 insertions(+), 203384 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/remove_old_artifacts.yml create mode 100644 .github/workflows/sandbox_w64.yml create mode 100644 .github/workflows/update_messages.yml create mode 100644 .github/workflows/win32_release.yml create mode 100644 .github/workflows/win64_release.yml delete mode 100644 .github/workflows/win_release.yml delete mode 100644 .travis.yml delete mode 100644 Plugins/ConfigMigration/ConfigMigration_de.qm delete mode 100644 Plugins/ConfigMigration/ConfigMigration_de.ts delete mode 100644 Plugins/ConfigMigration/ConfigMigration_es.qm delete mode 100644 Plugins/ConfigMigration/ConfigMigration_es.ts delete mode 100644 Plugins/ConfigMigration/ConfigMigration_fr.qm delete mode 100644 Plugins/ConfigMigration/ConfigMigration_fr.ts delete mode 100644 Plugins/ConfigMigration/ConfigMigration_it.qm delete mode 100644 Plugins/ConfigMigration/ConfigMigration_it.ts delete mode 100644 Plugins/ConfigMigration/ConfigMigration_pl.qm delete mode 100644 Plugins/ConfigMigration/ConfigMigration_pl.ts delete mode 100644 Plugins/ConfigMigration/ConfigMigration_pt_BR.qm delete mode 100644 Plugins/ConfigMigration/ConfigMigration_pt_BR.ts delete mode 100644 Plugins/ConfigMigration/ConfigMigration_ro_RO.qm delete mode 100644 Plugins/ConfigMigration/ConfigMigration_ro_RO.ts delete mode 100644 Plugins/ConfigMigration/ConfigMigration_ru.qm delete mode 100644 Plugins/ConfigMigration/ConfigMigration_ru.ts delete mode 100644 Plugins/ConfigMigration/ConfigMigration_sk.qm delete mode 100644 Plugins/ConfigMigration/ConfigMigration_sk.ts delete mode 100644 Plugins/ConfigMigration/ConfigMigration_zh_CN.qm delete mode 100644 Plugins/ConfigMigration/ConfigMigration_zh_CN.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_af_ZA.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_ar_SA.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_ca_ES.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_cs_CZ.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_da_DK.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_de_DE.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_el_GR.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_en_US.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_es_ES.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_fa_IR.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_fi_FI.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_fr_FR.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_he_IL.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_hu_HU.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_it_IT.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_ja_JP.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_kaa.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_ko_KR.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_nl_NL.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_no_NO.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_pl_PL.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_pt_BR.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_pt_PT.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_ro_RO.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_ru_RU.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_sk_SK.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_sr_SP.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_sv_SE.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_tr_TR.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_uk_UA.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_vi_VN.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_zh_CN.ts create mode 100644 Plugins/ConfigMigration/translations/ConfigMigration_zh_TW.ts delete mode 100644 Plugins/CsvExport/CsvExport_de.qm delete mode 100644 Plugins/CsvExport/CsvExport_de.ts delete mode 100644 Plugins/CsvExport/CsvExport_es.qm delete mode 100644 Plugins/CsvExport/CsvExport_es.ts delete mode 100644 Plugins/CsvExport/CsvExport_fr.qm delete mode 100644 Plugins/CsvExport/CsvExport_fr.ts delete mode 100644 Plugins/CsvExport/CsvExport_it.qm delete mode 100644 Plugins/CsvExport/CsvExport_it.ts delete mode 100644 Plugins/CsvExport/CsvExport_pl.qm delete mode 100644 Plugins/CsvExport/CsvExport_pl.ts delete mode 100644 Plugins/CsvExport/CsvExport_pt_BR.qm delete mode 100644 Plugins/CsvExport/CsvExport_pt_BR.ts delete mode 100644 Plugins/CsvExport/CsvExport_ro_RO.qm delete mode 100644 Plugins/CsvExport/CsvExport_ro_RO.ts delete mode 100644 Plugins/CsvExport/CsvExport_ru.qm delete mode 100644 Plugins/CsvExport/CsvExport_ru.ts delete mode 100644 Plugins/CsvExport/CsvExport_sk.qm delete mode 100644 Plugins/CsvExport/CsvExport_sk.ts delete mode 100644 Plugins/CsvExport/CsvExport_zh_CN.qm delete mode 100644 Plugins/CsvExport/CsvExport_zh_CN.ts create mode 100644 Plugins/CsvExport/translations/CsvExport.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_af_ZA.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_ar_SA.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_ca_ES.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_cs_CZ.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_da_DK.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_de_DE.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_el_GR.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_en_US.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_es_ES.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_fa_IR.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_fi_FI.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_fr_FR.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_he_IL.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_hu_HU.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_it_IT.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_ja_JP.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_kaa.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_ko_KR.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_nl_NL.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_no_NO.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_pl_PL.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_pt_BR.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_pt_PT.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_ro_RO.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_ru_RU.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_sk_SK.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_sr_SP.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_sv_SE.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_tr_TR.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_uk_UA.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_vi_VN.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_zh_CN.ts create mode 100644 Plugins/CsvExport/translations/CsvExport_zh_TW.ts delete mode 100644 Plugins/CsvImport/CsvImport_de.qm delete mode 100644 Plugins/CsvImport/CsvImport_de.ts delete mode 100644 Plugins/CsvImport/CsvImport_es.qm delete mode 100644 Plugins/CsvImport/CsvImport_es.ts delete mode 100644 Plugins/CsvImport/CsvImport_fr.qm delete mode 100644 Plugins/CsvImport/CsvImport_fr.ts delete mode 100644 Plugins/CsvImport/CsvImport_it.qm delete mode 100644 Plugins/CsvImport/CsvImport_it.ts delete mode 100644 Plugins/CsvImport/CsvImport_pl.qm delete mode 100644 Plugins/CsvImport/CsvImport_pl.ts delete mode 100644 Plugins/CsvImport/CsvImport_pt_BR.qm delete mode 100644 Plugins/CsvImport/CsvImport_pt_BR.ts delete mode 100644 Plugins/CsvImport/CsvImport_ro_RO.qm delete mode 100644 Plugins/CsvImport/CsvImport_ro_RO.ts delete mode 100644 Plugins/CsvImport/CsvImport_ru.qm delete mode 100644 Plugins/CsvImport/CsvImport_ru.ts delete mode 100644 Plugins/CsvImport/CsvImport_sk.qm delete mode 100644 Plugins/CsvImport/CsvImport_sk.ts delete mode 100644 Plugins/CsvImport/CsvImport_zh_CN.qm delete mode 100644 Plugins/CsvImport/CsvImport_zh_CN.ts create mode 100644 Plugins/CsvImport/translations/CsvImport.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_af_ZA.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_ar_SA.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_ca_ES.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_cs_CZ.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_da_DK.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_de_DE.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_el_GR.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_en_US.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_es_ES.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_fa_IR.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_fi_FI.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_fr_FR.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_he_IL.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_hu_HU.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_it_IT.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_ja_JP.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_kaa.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_ko_KR.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_nl_NL.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_no_NO.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_pl_PL.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_pt_BR.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_pt_PT.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_ro_RO.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_ru_RU.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_sk_SK.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_sr_SP.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_sv_SE.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_tr_TR.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_uk_UA.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_vi_VN.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_zh_CN.ts create mode 100644 Plugins/CsvImport/translations/CsvImport_zh_TW.ts delete mode 100644 Plugins/DbAndroid/DbAndroid_de.qm delete mode 100644 Plugins/DbAndroid/DbAndroid_de.ts delete mode 100644 Plugins/DbAndroid/DbAndroid_es.qm delete mode 100644 Plugins/DbAndroid/DbAndroid_es.ts delete mode 100644 Plugins/DbAndroid/DbAndroid_fr.qm delete mode 100644 Plugins/DbAndroid/DbAndroid_fr.ts delete mode 100644 Plugins/DbAndroid/DbAndroid_it.qm delete mode 100644 Plugins/DbAndroid/DbAndroid_it.ts delete mode 100644 Plugins/DbAndroid/DbAndroid_pl.qm delete mode 100644 Plugins/DbAndroid/DbAndroid_pl.ts delete mode 100644 Plugins/DbAndroid/DbAndroid_pt_BR.qm delete mode 100644 Plugins/DbAndroid/DbAndroid_pt_BR.ts delete mode 100644 Plugins/DbAndroid/DbAndroid_ro_RO.qm delete mode 100644 Plugins/DbAndroid/DbAndroid_ro_RO.ts delete mode 100644 Plugins/DbAndroid/DbAndroid_ru.qm delete mode 100644 Plugins/DbAndroid/DbAndroid_ru.ts delete mode 100644 Plugins/DbAndroid/DbAndroid_sk.qm delete mode 100644 Plugins/DbAndroid/DbAndroid_sk.ts delete mode 100644 Plugins/DbAndroid/DbAndroid_zh_CN.qm delete mode 100644 Plugins/DbAndroid/DbAndroid_zh_CN.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_af_ZA.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_ar_SA.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_ca_ES.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_cs_CZ.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_da_DK.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_de_DE.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_el_GR.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_en_US.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_es_ES.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_fa_IR.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_fi_FI.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_fr_FR.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_he_IL.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_hu_HU.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_it_IT.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_ja_JP.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_kaa.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_ko_KR.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_nl_NL.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_no_NO.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_pl_PL.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_pt_BR.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_pt_PT.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_ro_RO.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_ru_RU.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_sk_SK.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_sr_SP.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_sv_SE.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_tr_TR.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_uk_UA.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_vi_VN.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_zh_CN.ts create mode 100644 Plugins/DbAndroid/translations/DbAndroid_zh_TW.ts delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_de.qm delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_de.ts delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_es.qm delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_es.ts delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_fr.qm delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_fr.ts delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_it.qm delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_it.ts delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_pl.qm delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_pl.ts delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_pt_BR.qm delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_pt_BR.ts delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_ro_RO.qm delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_ro_RO.ts delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_ru.qm delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_ru.ts delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_sk.qm delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_sk.ts delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_zh_CN.qm delete mode 100644 Plugins/DbSqliteCipher/DbSqliteCipher_zh_CN.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_af_ZA.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_ar_SA.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_ca_ES.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_cs_CZ.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_da_DK.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_de_DE.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_el_GR.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_en_US.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_es_ES.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_fa_IR.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_fi_FI.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_fr_FR.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_he_IL.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_hu_HU.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_it_IT.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_ja_JP.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_kaa.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_ko_KR.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_nl_NL.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_no_NO.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_pl_PL.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_pt_BR.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_pt_PT.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_ro_RO.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_ru_RU.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_sk_SK.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_sr_SP.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_sv_SE.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_tr_TR.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_uk_UA.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_vi_VN.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_zh_CN.ts create mode 100644 Plugins/DbSqliteCipher/translations/DbSqliteCipher_zh_TW.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_af_ZA.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_ar_SA.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_ca_ES.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_cs_CZ.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_da_DK.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_de_DE.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_el_GR.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_en_US.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_es_ES.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_fa_IR.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_fi_FI.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_fr_FR.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_he_IL.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_hu_HU.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_it_IT.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_ja_JP.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_kaa.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_ko_KR.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_nl_NL.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_no_NO.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_pl_PL.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_pt_BR.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_pt_PT.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_ro_RO.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_ru_RU.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_sk_SK.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_sr_SP.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_sv_SE.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_tr_TR.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_uk_UA.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_vi_VN.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_zh_CN.ts create mode 100644 Plugins/DbSqliteWx/translations/DbSqliteWx_zh_TW.ts delete mode 100644 Plugins/HtmlExport/HtmlExport_de.qm delete mode 100644 Plugins/HtmlExport/HtmlExport_de.ts delete mode 100644 Plugins/HtmlExport/HtmlExport_es.qm delete mode 100644 Plugins/HtmlExport/HtmlExport_es.ts delete mode 100644 Plugins/HtmlExport/HtmlExport_fr.qm delete mode 100644 Plugins/HtmlExport/HtmlExport_fr.ts delete mode 100644 Plugins/HtmlExport/HtmlExport_it.qm delete mode 100644 Plugins/HtmlExport/HtmlExport_it.ts delete mode 100644 Plugins/HtmlExport/HtmlExport_pl.qm delete mode 100644 Plugins/HtmlExport/HtmlExport_pl.ts delete mode 100644 Plugins/HtmlExport/HtmlExport_pt_BR.qm delete mode 100644 Plugins/HtmlExport/HtmlExport_pt_BR.ts delete mode 100644 Plugins/HtmlExport/HtmlExport_ro_RO.qm delete mode 100644 Plugins/HtmlExport/HtmlExport_ro_RO.ts delete mode 100644 Plugins/HtmlExport/HtmlExport_ru.qm delete mode 100644 Plugins/HtmlExport/HtmlExport_ru.ts delete mode 100644 Plugins/HtmlExport/HtmlExport_sk.qm delete mode 100644 Plugins/HtmlExport/HtmlExport_sk.ts delete mode 100644 Plugins/HtmlExport/HtmlExport_zh_CN.qm delete mode 100644 Plugins/HtmlExport/HtmlExport_zh_CN.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_af_ZA.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_ar_SA.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_ca_ES.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_cs_CZ.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_da_DK.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_de_DE.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_el_GR.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_en_US.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_es_ES.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_fa_IR.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_fi_FI.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_fr_FR.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_he_IL.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_hu_HU.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_it_IT.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_ja_JP.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_kaa.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_ko_KR.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_nl_NL.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_no_NO.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_pl_PL.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_pt_BR.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_pt_PT.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_ro_RO.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_ru_RU.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_sk_SK.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_sr_SP.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_sv_SE.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_tr_TR.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_uk_UA.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_vi_VN.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_zh_CN.ts create mode 100644 Plugins/HtmlExport/translations/HtmlExport_zh_TW.ts delete mode 100644 Plugins/JsonExport/JsonExport_de.qm delete mode 100644 Plugins/JsonExport/JsonExport_de.ts delete mode 100644 Plugins/JsonExport/JsonExport_es.qm delete mode 100644 Plugins/JsonExport/JsonExport_es.ts delete mode 100644 Plugins/JsonExport/JsonExport_fr.qm delete mode 100644 Plugins/JsonExport/JsonExport_fr.ts delete mode 100644 Plugins/JsonExport/JsonExport_it.qm delete mode 100644 Plugins/JsonExport/JsonExport_it.ts delete mode 100644 Plugins/JsonExport/JsonExport_pl.qm delete mode 100644 Plugins/JsonExport/JsonExport_pl.ts delete mode 100644 Plugins/JsonExport/JsonExport_pt_BR.qm delete mode 100644 Plugins/JsonExport/JsonExport_pt_BR.ts delete mode 100644 Plugins/JsonExport/JsonExport_ro_RO.qm delete mode 100644 Plugins/JsonExport/JsonExport_ro_RO.ts delete mode 100644 Plugins/JsonExport/JsonExport_ru.qm delete mode 100644 Plugins/JsonExport/JsonExport_ru.ts delete mode 100644 Plugins/JsonExport/JsonExport_sk.qm delete mode 100644 Plugins/JsonExport/JsonExport_sk.ts delete mode 100644 Plugins/JsonExport/JsonExport_zh_CN.qm delete mode 100644 Plugins/JsonExport/JsonExport_zh_CN.ts create mode 100644 Plugins/JsonExport/translations/JsonExport.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_af_ZA.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_ar_SA.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_ca_ES.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_cs_CZ.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_da_DK.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_de_DE.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_el_GR.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_en_US.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_es_ES.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_fa_IR.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_fi_FI.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_fr_FR.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_he_IL.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_hu_HU.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_it_IT.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_ja_JP.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_kaa.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_ko_KR.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_nl_NL.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_no_NO.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_pl_PL.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_pt_BR.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_pt_PT.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_ro_RO.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_ru_RU.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_sk_SK.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_sr_SP.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_sv_SE.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_tr_TR.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_uk_UA.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_vi_VN.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_zh_CN.ts create mode 100644 Plugins/JsonExport/translations/JsonExport_zh_TW.ts delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_de.qm delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_de.ts delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_es.qm delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_es.ts delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_fr.qm delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_fr.ts delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_it.qm delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_it.ts delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_pl.qm delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_pl.ts delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_pt_BR.qm delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_pt_BR.ts delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_ro_RO.qm delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_ro_RO.ts delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_ru.qm delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_ru.ts delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_sk.qm delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_sk.ts delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_zh_CN.qm delete mode 100644 Plugins/MultiEditorImage/MultiEditorImage_zh_CN.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_af_ZA.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_ar_SA.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_ca_ES.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_cs_CZ.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_da_DK.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_de_DE.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_el_GR.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_en_US.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_es_ES.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_fa_IR.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_fi_FI.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_fr_FR.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_he_IL.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_hu_HU.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_it_IT.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_ja_JP.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_kaa.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_ko_KR.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_nl_NL.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_no_NO.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_pl_PL.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_pt_BR.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_pt_PT.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_ro_RO.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_ru_RU.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_sk_SK.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_sr_SP.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_sv_SE.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_tr_TR.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_uk_UA.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_vi_VN.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_zh_CN.ts create mode 100644 Plugins/MultiEditorImage/translations/MultiEditorImage_zh_TW.ts delete mode 100644 Plugins/PdfExport/PdfExport_de.qm delete mode 100644 Plugins/PdfExport/PdfExport_de.ts delete mode 100644 Plugins/PdfExport/PdfExport_es.qm delete mode 100644 Plugins/PdfExport/PdfExport_es.ts delete mode 100644 Plugins/PdfExport/PdfExport_fr.qm delete mode 100644 Plugins/PdfExport/PdfExport_fr.ts delete mode 100644 Plugins/PdfExport/PdfExport_it.qm delete mode 100644 Plugins/PdfExport/PdfExport_it.ts delete mode 100644 Plugins/PdfExport/PdfExport_pl.qm delete mode 100644 Plugins/PdfExport/PdfExport_pl.ts delete mode 100644 Plugins/PdfExport/PdfExport_pt_BR.qm delete mode 100644 Plugins/PdfExport/PdfExport_pt_BR.ts delete mode 100644 Plugins/PdfExport/PdfExport_ro_RO.qm delete mode 100644 Plugins/PdfExport/PdfExport_ro_RO.ts delete mode 100644 Plugins/PdfExport/PdfExport_ru.qm delete mode 100644 Plugins/PdfExport/PdfExport_ru.ts delete mode 100644 Plugins/PdfExport/PdfExport_sk.qm delete mode 100644 Plugins/PdfExport/PdfExport_sk.ts delete mode 100644 Plugins/PdfExport/PdfExport_zh_CN.qm delete mode 100644 Plugins/PdfExport/PdfExport_zh_CN.ts create mode 100644 Plugins/PdfExport/translations/PdfExport.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_af_ZA.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_ar_SA.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_ca_ES.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_cs_CZ.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_da_DK.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_de_DE.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_el_GR.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_en_US.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_es_ES.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_fa_IR.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_fi_FI.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_fr_FR.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_he_IL.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_hu_HU.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_it_IT.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_ja_JP.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_kaa.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_ko_KR.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_nl_NL.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_no_NO.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_pl_PL.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_pt_BR.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_pt_PT.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_ro_RO.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_ru_RU.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_sk_SK.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_sr_SP.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_sv_SE.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_tr_TR.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_uk_UA.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_vi_VN.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_zh_CN.ts create mode 100644 Plugins/PdfExport/translations/PdfExport_zh_TW.ts delete mode 100644 Plugins/Printing/Printing_de.qm delete mode 100644 Plugins/Printing/Printing_de.ts delete mode 100644 Plugins/Printing/Printing_es.qm delete mode 100644 Plugins/Printing/Printing_es.ts delete mode 100644 Plugins/Printing/Printing_fr.qm delete mode 100644 Plugins/Printing/Printing_fr.ts delete mode 100644 Plugins/Printing/Printing_it.qm delete mode 100644 Plugins/Printing/Printing_it.ts delete mode 100644 Plugins/Printing/Printing_pl.qm delete mode 100644 Plugins/Printing/Printing_pl.ts delete mode 100644 Plugins/Printing/Printing_pt_BR.qm delete mode 100644 Plugins/Printing/Printing_pt_BR.ts delete mode 100644 Plugins/Printing/Printing_ro_RO.qm delete mode 100644 Plugins/Printing/Printing_ro_RO.ts delete mode 100644 Plugins/Printing/Printing_ru.qm delete mode 100644 Plugins/Printing/Printing_ru.ts delete mode 100644 Plugins/Printing/Printing_sk.qm delete mode 100644 Plugins/Printing/Printing_sk.ts delete mode 100644 Plugins/Printing/Printing_zh_CN.qm delete mode 100644 Plugins/Printing/Printing_zh_CN.ts create mode 100644 Plugins/Printing/translations/Printing.ts create mode 100644 Plugins/Printing/translations/Printing_af_ZA.ts create mode 100644 Plugins/Printing/translations/Printing_ar_SA.ts create mode 100644 Plugins/Printing/translations/Printing_ca_ES.ts create mode 100644 Plugins/Printing/translations/Printing_cs_CZ.ts create mode 100644 Plugins/Printing/translations/Printing_da_DK.ts create mode 100644 Plugins/Printing/translations/Printing_de_DE.ts create mode 100644 Plugins/Printing/translations/Printing_el_GR.ts create mode 100644 Plugins/Printing/translations/Printing_en_US.ts create mode 100644 Plugins/Printing/translations/Printing_es_ES.ts create mode 100644 Plugins/Printing/translations/Printing_fa_IR.ts create mode 100644 Plugins/Printing/translations/Printing_fi_FI.ts create mode 100644 Plugins/Printing/translations/Printing_fr_FR.ts create mode 100644 Plugins/Printing/translations/Printing_he_IL.ts create mode 100644 Plugins/Printing/translations/Printing_hu_HU.ts create mode 100644 Plugins/Printing/translations/Printing_it_IT.ts create mode 100644 Plugins/Printing/translations/Printing_ja_JP.ts create mode 100644 Plugins/Printing/translations/Printing_kaa.ts create mode 100644 Plugins/Printing/translations/Printing_ko_KR.ts create mode 100644 Plugins/Printing/translations/Printing_nl_NL.ts create mode 100644 Plugins/Printing/translations/Printing_no_NO.ts create mode 100644 Plugins/Printing/translations/Printing_pl_PL.ts create mode 100644 Plugins/Printing/translations/Printing_pt_BR.ts create mode 100644 Plugins/Printing/translations/Printing_pt_PT.ts create mode 100644 Plugins/Printing/translations/Printing_ro_RO.ts create mode 100644 Plugins/Printing/translations/Printing_ru_RU.ts create mode 100644 Plugins/Printing/translations/Printing_sk_SK.ts create mode 100644 Plugins/Printing/translations/Printing_sr_SP.ts create mode 100644 Plugins/Printing/translations/Printing_sv_SE.ts create mode 100644 Plugins/Printing/translations/Printing_tr_TR.ts create mode 100644 Plugins/Printing/translations/Printing_uk_UA.ts create mode 100644 Plugins/Printing/translations/Printing_vi_VN.ts create mode 100644 Plugins/Printing/translations/Printing_zh_CN.ts create mode 100644 Plugins/Printing/translations/Printing_zh_TW.ts create mode 100644 Plugins/PythonSyntaxHighlighter/PythonSyntaxHighlighter.pro create mode 100644 Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.cpp create mode 100644 Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.h create mode 100644 Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.json create mode 100644 Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter_global.h delete mode 100644 Plugins/RegExpImport/RegExpImport_de.qm delete mode 100644 Plugins/RegExpImport/RegExpImport_de.ts delete mode 100644 Plugins/RegExpImport/RegExpImport_es.qm delete mode 100644 Plugins/RegExpImport/RegExpImport_es.ts delete mode 100644 Plugins/RegExpImport/RegExpImport_fr.qm delete mode 100644 Plugins/RegExpImport/RegExpImport_fr.ts delete mode 100644 Plugins/RegExpImport/RegExpImport_it.qm delete mode 100644 Plugins/RegExpImport/RegExpImport_it.ts delete mode 100644 Plugins/RegExpImport/RegExpImport_pl.qm delete mode 100644 Plugins/RegExpImport/RegExpImport_pl.ts delete mode 100644 Plugins/RegExpImport/RegExpImport_pt_BR.qm delete mode 100644 Plugins/RegExpImport/RegExpImport_pt_BR.ts delete mode 100644 Plugins/RegExpImport/RegExpImport_ro_RO.qm delete mode 100644 Plugins/RegExpImport/RegExpImport_ro_RO.ts delete mode 100644 Plugins/RegExpImport/RegExpImport_ru.qm delete mode 100644 Plugins/RegExpImport/RegExpImport_ru.ts delete mode 100644 Plugins/RegExpImport/RegExpImport_sk.qm delete mode 100644 Plugins/RegExpImport/RegExpImport_sk.ts delete mode 100644 Plugins/RegExpImport/RegExpImport_zh_CN.qm delete mode 100644 Plugins/RegExpImport/RegExpImport_zh_CN.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_af_ZA.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_ar_SA.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_ca_ES.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_cs_CZ.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_da_DK.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_de_DE.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_el_GR.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_en_US.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_es_ES.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_fa_IR.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_fi_FI.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_fr_FR.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_he_IL.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_hu_HU.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_it_IT.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_ja_JP.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_kaa.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_ko_KR.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_nl_NL.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_no_NO.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_pl_PL.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_pt_BR.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_pt_PT.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_ro_RO.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_ru_RU.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_sk_SK.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_sr_SP.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_sv_SE.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_tr_TR.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_uk_UA.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_vi_VN.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_zh_CN.ts create mode 100644 Plugins/RegExpImport/translations/RegExpImport_zh_TW.ts create mode 100644 Plugins/ScriptingPython/ScriptingPython.pro create mode 100644 Plugins/ScriptingPython/icon_attribution.txt create mode 100644 Plugins/ScriptingPython/scriptingpython.cpp create mode 100644 Plugins/ScriptingPython/scriptingpython.h create mode 100644 Plugins/ScriptingPython/scriptingpython.json create mode 100644 Plugins/ScriptingPython/scriptingpython.png create mode 100644 Plugins/ScriptingPython/scriptingpython.qrc create mode 100644 Plugins/ScriptingPython/scriptingpython_global.h create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_af_ZA.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_ar_SA.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_ca_ES.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_cs_CZ.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_da_DK.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_de_DE.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_el_GR.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_en_US.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_es_ES.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_fa_IR.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_fi_FI.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_fr_FR.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_he_IL.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_hu_HU.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_it_IT.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_ja_JP.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_kaa.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_ko_KR.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_nl_NL.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_no_NO.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_pl_PL.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_pt_BR.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_pt_PT.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_ro_RO.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_ru_RU.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_sk_SK.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_sr_SP.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_sv_SE.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_tr_TR.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_uk_UA.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_vi_VN.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_zh_CN.ts create mode 100644 Plugins/ScriptingPython/translations/ScriptingPython_zh_TW.ts delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_de.qm delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_de.ts delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_es.qm delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_es.ts delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_fr.qm delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_fr.ts delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_it.qm delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_it.ts delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_pl.qm delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_pl.ts delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_pt_BR.qm delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_pt_BR.ts delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_ro_RO.qm delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_ro_RO.ts delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_ru.qm delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_ru.ts delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_sk.qm delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_sk.ts delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_zh_CN.qm delete mode 100644 Plugins/ScriptingTcl/ScriptingTcl_zh_CN.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_af_ZA.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_ar_SA.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_ca_ES.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_cs_CZ.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_da_DK.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_de_DE.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_el_GR.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_en_US.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_es_ES.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_fa_IR.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_fi_FI.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_fr_FR.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_he_IL.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_hu_HU.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_it_IT.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_ja_JP.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_kaa.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_ko_KR.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_nl_NL.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_no_NO.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_pl_PL.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_pt_BR.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_pt_PT.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_ro_RO.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_ru_RU.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_sk_SK.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_sr_SP.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_sv_SE.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_tr_TR.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_uk_UA.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_vi_VN.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_zh_CN.ts create mode 100644 Plugins/ScriptingTcl/translations/ScriptingTcl_zh_TW.ts delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_de.qm delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_de.ts delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_es.qm delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_es.ts delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_fr.qm delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_fr.ts delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_it.qm delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_it.ts delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pl.qm delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pl.ts delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pt_BR.qm delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pt_BR.ts delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ro_RO.qm delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ro_RO.ts delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ru.qm delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ru.ts delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_sk.qm delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_sk.ts delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_zh_CN.qm delete mode 100644 Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_zh_CN.ts delete mode 100644 Plugins/SqlEnterpriseFormatter/formatcopy.cpp delete mode 100644 Plugins/SqlEnterpriseFormatter/formatcopy.h create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_af_ZA.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ar_SA.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ca_ES.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_cs_CZ.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_da_DK.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_de_DE.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_el_GR.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_en_US.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_es_ES.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fa_IR.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fi_FI.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fr_FR.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_he_IL.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_hu_HU.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_it_IT.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ja_JP.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_kaa.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ko_KR.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_nl_NL.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_no_NO.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pl_PL.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pt_BR.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pt_PT.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ro_RO.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ru_RU.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sk_SK.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sr_SP.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sv_SE.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_tr_TR.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_uk_UA.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_vi_VN.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_zh_CN.ts create mode 100644 Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_zh_TW.ts delete mode 100644 Plugins/SqlExport/SqlExport_de.qm delete mode 100644 Plugins/SqlExport/SqlExport_de.ts delete mode 100644 Plugins/SqlExport/SqlExport_es.qm delete mode 100644 Plugins/SqlExport/SqlExport_es.ts delete mode 100644 Plugins/SqlExport/SqlExport_fr.qm delete mode 100644 Plugins/SqlExport/SqlExport_fr.ts delete mode 100644 Plugins/SqlExport/SqlExport_it.qm delete mode 100644 Plugins/SqlExport/SqlExport_it.ts delete mode 100644 Plugins/SqlExport/SqlExport_pl.qm delete mode 100644 Plugins/SqlExport/SqlExport_pl.ts delete mode 100644 Plugins/SqlExport/SqlExport_pt_BR.qm delete mode 100644 Plugins/SqlExport/SqlExport_pt_BR.ts delete mode 100644 Plugins/SqlExport/SqlExport_ro_RO.qm delete mode 100644 Plugins/SqlExport/SqlExport_ro_RO.ts delete mode 100644 Plugins/SqlExport/SqlExport_ru.qm delete mode 100644 Plugins/SqlExport/SqlExport_ru.ts delete mode 100644 Plugins/SqlExport/SqlExport_sk.qm delete mode 100644 Plugins/SqlExport/SqlExport_sk.ts delete mode 100644 Plugins/SqlExport/SqlExport_zh_CN.qm delete mode 100644 Plugins/SqlExport/SqlExport_zh_CN.ts create mode 100644 Plugins/SqlExport/translations/SqlExport.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_af_ZA.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_ar_SA.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_ca_ES.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_cs_CZ.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_da_DK.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_de_DE.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_el_GR.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_en_US.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_es_ES.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_fa_IR.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_fi_FI.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_fr_FR.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_he_IL.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_hu_HU.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_it_IT.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_ja_JP.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_kaa.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_ko_KR.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_nl_NL.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_no_NO.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_pl_PL.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_pt_BR.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_pt_PT.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_ro_RO.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_ru_RU.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_sk_SK.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_sr_SP.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_sv_SE.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_tr_TR.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_uk_UA.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_vi_VN.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_zh_CN.ts create mode 100644 Plugins/SqlExport/translations/SqlExport_zh_TW.ts delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_de.qm delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_de.ts delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_es.qm delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_es.ts delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_fr.qm delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_fr.ts delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_it.qm delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_it.ts delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_pl.qm delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_pl.ts delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_pt_BR.qm delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_pt_BR.ts delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_ro_RO.qm delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_ro_RO.ts delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_ru.qm delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_ru.ts delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_sk.qm delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_sk.ts delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_zh_CN.qm delete mode 100644 Plugins/SqlFormatterSimple/SqlFormatterSimple_zh_CN.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_af_ZA.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ar_SA.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ca_ES.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_cs_CZ.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_da_DK.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_de_DE.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_el_GR.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_en_US.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_es_ES.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fa_IR.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fi_FI.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fr_FR.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_he_IL.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_hu_HU.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_it_IT.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ja_JP.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_kaa.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ko_KR.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_nl_NL.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_no_NO.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pl_PL.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pt_BR.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pt_PT.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ro_RO.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ru_RU.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sk_SK.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sr_SP.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sv_SE.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_tr_TR.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_uk_UA.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_vi_VN.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_zh_CN.ts create mode 100644 Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_zh_TW.ts delete mode 100644 Plugins/XmlExport/XmlExport_de.qm delete mode 100644 Plugins/XmlExport/XmlExport_de.ts delete mode 100644 Plugins/XmlExport/XmlExport_es.qm delete mode 100644 Plugins/XmlExport/XmlExport_es.ts delete mode 100644 Plugins/XmlExport/XmlExport_fr.qm delete mode 100644 Plugins/XmlExport/XmlExport_fr.ts delete mode 100644 Plugins/XmlExport/XmlExport_it.qm delete mode 100644 Plugins/XmlExport/XmlExport_it.ts delete mode 100644 Plugins/XmlExport/XmlExport_pl.qm delete mode 100644 Plugins/XmlExport/XmlExport_pl.ts delete mode 100644 Plugins/XmlExport/XmlExport_pt_BR.qm delete mode 100644 Plugins/XmlExport/XmlExport_pt_BR.ts delete mode 100644 Plugins/XmlExport/XmlExport_ro_RO.qm delete mode 100644 Plugins/XmlExport/XmlExport_ro_RO.ts delete mode 100644 Plugins/XmlExport/XmlExport_ru.qm delete mode 100644 Plugins/XmlExport/XmlExport_ru.ts delete mode 100644 Plugins/XmlExport/XmlExport_sk.qm delete mode 100644 Plugins/XmlExport/XmlExport_sk.ts delete mode 100644 Plugins/XmlExport/XmlExport_zh_CN.qm delete mode 100644 Plugins/XmlExport/XmlExport_zh_CN.ts create mode 100644 Plugins/XmlExport/translations/XmlExport.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_af_ZA.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_ar_SA.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_ca_ES.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_cs_CZ.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_da_DK.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_de_DE.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_el_GR.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_en_US.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_es_ES.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_fa_IR.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_fi_FI.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_fr_FR.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_he_IL.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_hu_HU.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_it_IT.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_ja_JP.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_kaa.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_ko_KR.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_nl_NL.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_no_NO.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_pl_PL.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_pt_BR.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_pt_PT.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_ro_RO.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_ru_RU.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_sk_SK.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_sr_SP.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_sv_SE.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_tr_TR.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_uk_UA.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_vi_VN.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_zh_CN.ts create mode 100644 Plugins/XmlExport/translations/XmlExport_zh_TW.ts create mode 100644 SQLiteStudio-installer.xml create mode 100644 SQLiteStudio3/Tests/testcommon.pri delete mode 100644 SQLiteStudio3/Tests/testdirs.pri create mode 100644 SQLiteStudio3/common.pri create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/LICENSE-chillout create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/README create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/chillout.cpp create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/chillout.h create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/common/common.cpp create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/common/common.h create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/defines.h create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/posix/posixcrashhandler.cpp create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/posix/posixcrashhandler.h create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/windows/StackWalker.cpp create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/windows/StackWalker.h create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/windows/windowscrashhandler.cpp create mode 100644 SQLiteStudio3/coreSQLiteStudio/chillout/windows/windowscrashhandler.h delete mode 100644 SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.cpp delete mode 100644 SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.h create mode 100644 SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorfilter.cpp create mode 100644 SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorfilter.h create mode 100644 SQLiteStudio3/coreSQLiteStudio/licenses/icu.txt delete mode 100644 SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp delete mode 100644 SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h create mode 100644 SQLiteStudio3/coreSQLiteStudio/services/codesnippetmanager.cpp create mode 100644 SQLiteStudio3/coreSQLiteStudio/services/codesnippetmanager.h create mode 100644 SQLiteStudio3/coreSQLiteStudio/sqlfileexecutor.cpp create mode 100644 SQLiteStudio3/coreSQLiteStudio/sqlfileexecutor.h create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_af_ZA.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ar_SA.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ca_ES.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_cs_CZ.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_da_DK.ts delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de.qm delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de_DE.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_el_GR.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_en_US.ts delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es.qm delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es_ES.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fa_IR.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fi_FI.ts delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr.qm delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr_FR.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_he_IL.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_hu_HU.ts delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it.qm delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it_IT.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ja_JP.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_kaa.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ko_KR.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_nl_NL.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_no_NO.ts delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl.qm delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl_PL.ts delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_BR.qm create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_PT.ts delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ro_RO.qm delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru.qm delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru_RU.ts delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.qm delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk_SK.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sr_SP.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sv_SE.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_tr_TR.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_uk_UA.ts create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_vi_VN.ts delete mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_CN.qm create mode 100644 SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_TW.ts delete mode 100644 SQLiteStudio3/dirs.pri create mode 100644 SQLiteStudio3/guiSQLiteStudio/common/immediatetooltip.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/common/immediatetooltip.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/common/mouseshortcut.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/common/mouseshortcut.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/datagrid/fkcombobox.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/datagrid/fkcombobox.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/apply_filter_txt_strict.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/code_assistant.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/code_snippet.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/restore_default.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/selection_invert.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_256.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_48.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_installer.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_opt.ico create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/window_close_all_left.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/window_close_all_right.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorfk.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorfk.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_af_ZA.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ar_SA.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ca_ES.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_cs_CZ.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_da_DK.ts delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de.qm delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de_DE.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_el_GR.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_en_US.ts delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es.qm delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es_ES.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fa_IR.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fi_FI.ts delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr.qm delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr_FR.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_he_IL.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_hu_HU.ts delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it.qm delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it_IT.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ja_JP.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_kaa.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ko_KR.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_nl_NL.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_no_NO.ts delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl.qm delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl_PL.ts delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_BR.qm create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_PT.ts delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ro_RO.qm delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.qm delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru_RU.ts delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.qm delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk_SK.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sr_SP.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sv_SE.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_tr_TR.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_uk_UA.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_vi_VN.ts delete mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.qm create mode 100644 SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_TW.ts create mode 100644 SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.h delete mode 100644 SQLiteStudio3/sqlitestudio/installscript.qs delete mode 100644 SQLiteStudio3/sqlitestudio/register_file_types.ui create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_af_ZA.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ar_SA.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ca_ES.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_cs_CZ.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_da_DK.ts delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de.qm delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de_DE.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_el_GR.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_en_US.ts delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es.qm delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es_ES.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fa_IR.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fi_FI.ts delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr.qm delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr_FR.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_he_IL.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_hu_HU.ts delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it.qm delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it_IT.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ja_JP.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_kaa.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ko_KR.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_nl_NL.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_no_NO.ts delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl.qm delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl_PL.ts delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_BR.qm create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_PT.ts delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ro_RO.qm delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru.qm delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru_RU.ts delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk.qm delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk_SK.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sr_SP.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sv_SE.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_tr_TR.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_uk_UA.ts create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_vi_VN.ts delete mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_CN.qm create mode 100644 SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_TW.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_af_ZA.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ar_SA.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ca_ES.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_cs_CZ.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_da_DK.ts delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de.qm delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de_DE.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_el_GR.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_en_US.ts delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es.qm delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es_ES.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fa_IR.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fi_FI.ts delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr.qm delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr_FR.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_he_IL.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_hu_HU.ts delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it.qm delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it_IT.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ja_JP.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_kaa.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ko_KR.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_nl_NL.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_no_NO.ts delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl.qm delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl_PL.ts delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_BR.qm create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_PT.ts delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ro_RO.qm delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru.qm delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru_RU.ts delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk.qm delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk_SK.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sr_SP.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sv_SE.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_tr_TR.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_uk_UA.ts create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_vi_VN.ts delete mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_CN.qm create mode 100644 SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_TW.ts create mode 100644 SQLiteStudio3/ts.xsd create mode 100644 crowdin.yml delete mode 100644 scripts/installer/assemble.tcl delete mode 100644 scripts/installer/config/bg.png delete mode 100644 scripts/installer/config/config.xml delete mode 100644 scripts/installer/config/controller.qs delete mode 100644 scripts/installer/config/logo.png delete mode 100644 scripts/installer/config/sqlitestudio.icns delete mode 100644 scripts/installer/config/sqlitestudio.ico delete mode 100644 scripts/installer/config/watermark.png create mode 100644 win_deps/win32_deps.zip create mode 100644 win_deps/win64_deps.zip diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..5f49e06 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: pawelsalawa diff --git a/.github/workflows/lin_release.yml b/.github/workflows/lin_release.yml index b95453c..2f6145a 100644 --- a/.github/workflows/lin_release.yml +++ b/.github/workflows/lin_release.yml @@ -1,42 +1,86 @@ env: QT_VERSION: '5.15.2' TCL_VERSION: '8.6' - SQLITE_VERSION: '3350400' - SQLITE_RELEASE_YEAR: '2021' + SQLITE_VERSION: '3410200' + SQLITE_RELEASE_YEAR: '2023' + PYTHON_VERSION: '3.9' PORTABLE_DIR: ${{ github.workspace }}/output/portable/SQLiteStudio + INSTALLBUILDER_DIR: ../ib + INSTALLBUILDER_URL: https://releases.bitrock.com/installbuilder/installbuilder-enterprise-23.1.0-linux-x64-installer.run name: Linux release build on: + workflow_dispatch: + inputs: + use_ccache: + description: 'Use ccache (for workflow debugging only!)' + required: false + type: boolean + schedule: + - cron: '30 3 * * 1' # run at 3:30 AM UTC every Monday repository_dispatch: types: [lin_release] jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - - name: Cache Qt - id: cache-qt - uses: actions/cache@v1 + - uses: actions/setup-python@v4 with: - path: ${{ github.workspace }}/../Qt - key: ${{ runner.os }}-Qt-${{ env.QT_VERSION }}-Linux-Cache - + python-version: ${{ env.PYTHON_VERSION }} + architecture: 'x64' + + - name: Qt installation dir + id: qt-installation-dir + run: echo "DIR=$(readlink -f ${{ github.workspace }}/..)" >> $GITHUB_OUTPUT + - name: Install Qt - uses: jurplel/install-qt-action@v2 + uses: jurplel/install-qt-action@v3 with: - cached: ${{ steps.cache-qt.outputs.cache-hit }} + cache: true version: ${{ env.QT_VERSION }} host: 'linux' - dir: '${{ github.workspace }}/..' - modules: 'qtscript' - + dir: '${{ steps.qt-installation-dir.DIR }}' + aqtversion: '==3.0.*' + py7zrversion: '==0.20.*' + setup-python: 'false' + extra: '--external 7z' + + - name: Install the InstalBuilder + shell: bash + run: | + curl -L ${{ env.INSTALLBUILDER_URL }} --output ib.run + chmod +x ib.run + ./ib.run --mode unattended --prefix ${{ env.INSTALLBUILDER_DIR }} + ${{ env.INSTALLBUILDER_DIR }}/bin/builder --version + echo "INSTALLER_SRC_PREFIX=$(pwd)" >> $GITHUB_ENV + echo "INSTALLER_BIN_PREFIX=${{ env.PORTABLE_DIR }}" >> $GITHUB_ENV + - name: Clone repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ github.event.client_payload.branch }} + - name: Pre-download SQLite vanilla sourcecode + shell: bash + run: | + cd .. + curl -L http://sqlite.org/$SQLITE_RELEASE_YEAR/sqlite-src-$SQLITE_VERSION.zip --output sqlite-src-$SQLITE_VERSION.zip + + - name: Prepare ccache + if: inputs.use_ccache || false + uses: hendrikmuhs/ccache-action@v1.2.8 + with: + key: lin_release + max-size: "24M" + + - name: Configure ccache + if: inputs.use_ccache || false + run: | + echo "PATH=/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" >> $GITHUB_ENV + - name: Install SQLite3 run: | sudo rm -f /usr/lib/libsqlite* /usr/local/lib/libsqlite* /usr/include/sqlite* /usr/local/include/sqlite* /usr/lib/x86_64-linux-gnu/libsqlite* @@ -60,29 +104,63 @@ jobs: sudo cp -P libsqlite3.so* /usr/local/lib/ sudo cp *.h /usr/local/include/ + - name: Compile additional SQLite3 extensions + shell: bash + run: | + cd .. + mkdir ext + unzip sqlite-src-$SQLITE_VERSION.zip + cd sqlite-src-$SQLITE_VERSION/ext + FLAGS="-ldl -Os -fpic -shared -Imisc -I/usr/local/include -L/usr/local/lib -lsqlite3" + for f in compress; do + echo "gcc misc/$f.c $FLAGS -lz -o ../../ext/$f.so" + gcc misc/$f.c $FLAGS -lz -o ../../ext/$f.so + done + for f in csv decimal eval ieee754 percentile rot13 series sqlar uint uuid zorder; do + echo "gcc misc/$f.c $FLAGS -o ../../ext/$f.so" + gcc misc/$f.c $FLAGS -o ../../ext/$f.so + done + for f in icu; do + echo "gcc icu/$f.c $FLAGS $(pkg-config --libs --cflags icu-uc icu-io) -o ../../ext/$f.so" + gcc icu/$f.c $FLAGS `pkg-config --libs --cflags icu-uc icu-io` -o ../../ext/$f.so + done + ls -l ../../ext/ + - name: Install Tcl run: sudo apt-get install -qq libtcl$TCL_VERSION tcl$TCL_VERSION-dev - + - name: Install other tools/dependencies run: | - sudo apt install libreadline-dev libncurses5-dev patchelf + sudo apt install libreadline-dev libncurses5-dev patchelf chrpath echo "${{ github.workspace }}/../Qt/${{ env.QT_VERSION }}/gcc_64/bin" >> $GITHUB_PATH - + - name: Prepare output dir run: mkdir output output/build output/build/Plugins - + - name: Compile SQLiteStudio3 working-directory: output/build run: | - qmake CONFIG+=portable ../../SQLiteStudio3 + qmake \ + $([ ${{ inputs.use_ccache || false }} = false ] || echo "CONFIG+=ccache") \ + CONFIG+=portable \ + ../../SQLiteStudio3 make -j 2 - + - name: Compile Plugins working-directory: output/build/Plugins run: | - qmake CONFIG+=portable ../../../Plugins - make -j 2 - + qmake \ + $([ ${{ inputs.use_ccache || false }} = false ] || echo "CONFIG+=ccache") \ + CONFIG+=portable \ + "INCLUDEPATH+=$pythonLocation/include/python$PYTHON_VERSION" "LIBS += -L$pythonLocation/lib" \ + ../../../Plugins + make -j 1 + + - name: Copy SQLite extensions to output dir + shell: bash + run: | + cp -R ../ext output/SQLiteStudio/extensions + - name: Prepare portable dir working-directory: output run: | @@ -93,6 +171,20 @@ jobs: working-directory: ${{ env.PORTABLE_DIR }} run: cp -P /usr/local/lib/libsqlite3.so* lib/ + - name: Copy SQLCipher's libcrypto to portable dir + working-directory: ${{ env.PORTABLE_DIR }} + run: | + LIBCRYPTO=$(ldd plugins/libDbSqliteCipher.so | grep crypto | awk '{print $3}') + REAL_LIBCRYPTO=$(readlink -e $LIBCRYPTO) + cp -P $REAL_LIBCRYPTO lib/$(basename -- $LIBCRYPTO) + + - name: Copy Qt's libcrypto and libssl to portable dir (#4577) + run: | + wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.17_amd64.deb + dpkg-deb -xv libssl1.1_1.1.1f-1ubuntu2.17_amd64.deb . + cp ./usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.PORTABLE_DIR }}/lib/ + cp ./usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.PORTABLE_DIR }}/lib/ + - name: Copy Qt to portable dir working-directory: ${{ env.PORTABLE_DIR }} run: | @@ -102,7 +194,7 @@ jobs: cp -P ${{ env.Qt5_Dir }}/lib/libQt5Gui.so* lib/ cp -P ${{ env.Qt5_Dir }}/lib/libQt5Network.so* lib/ cp -P ${{ env.Qt5_Dir }}/lib/libQt5PrintSupport.so* lib/ - cp -P ${{ env.Qt5_Dir }}/lib/libQt5Script.so* lib/ + cp -P ${{ env.Qt5_Dir }}/lib/libQt5Qml.so* lib/ cp -P ${{ env.Qt5_Dir }}/lib/libQt5Widgets.so* lib/ cp -P ${{ env.Qt5_Dir }}/lib/libQt5Xml.so* lib/ cp -P ${{ env.Qt5_Dir }}/lib/libQt5Svg.so* lib/ @@ -114,7 +206,7 @@ jobs: - name: Copy Qt plugins to portable dir working-directory: ${{ env.PORTABLE_DIR }} run: | - mkdir platforms imageformats iconengines printsupport platformthemes + mkdir platforms imageformats iconengines printsupport platformthemes platforminputcontexts cp -P ${{ env.Qt5_Dir }}/plugins/platforms/libqxcb.so platforms/libqxcb.so cp -P ${{ env.Qt5_Dir }}/plugins/imageformats/libqgif.so imageformats/libqgif.so cp -P ${{ env.Qt5_Dir }}/plugins/imageformats/libqicns.so imageformats/libqicns.so @@ -126,6 +218,7 @@ jobs: cp -P ${{ env.Qt5_Dir }}/plugins/iconengines/libqsvgicon.so iconengines/libqsvgicon.so cp -P ${{ env.Qt5_Dir }}/plugins/printsupport/libcupsprintersupport.so printsupport/libcupsprintersupport.so cp -P ${{ env.Qt5_Dir }}/plugins/platformthemes/libqgtk3.so platformthemes/libqgtk3.so + cp -P ${{ env.Qt5_Dir }}/plugins/platforminputcontexts/libcomposeplatforminputcontextplugin.so platforminputcontexts/libcomposeplatforminputcontextplugin.so - name: Fix dependency paths working-directory: ${{ env.PORTABLE_DIR }} @@ -139,6 +232,12 @@ jobs: chrpath -l sqlitestudio chrpath -l sqlitestudiocli + - name: Final preparations for packaging + run: | + mkdir "${{ env.PORTABLE_DIR }}"/assets + cp SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_256.png "${{ env.PORTABLE_DIR }}"/assets/appicon.png + cp SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio.svg "${{ env.PORTABLE_DIR }}"/assets/appicon.svg + - name: Final preparations for packaging working-directory: ${{ env.PORTABLE_DIR }} run: | @@ -160,8 +259,26 @@ jobs: pwd ls -l + - name: Create installer package + shell: bash + env: + IB_LICENSE: ${{ secrets.INSTALLER_LICENSE }} + run: | + echo "$IB_LICENSE" > lic.xml + ${{ env.INSTALLBUILDER_DIR }}/bin/builder build SQLiteStudio-installer.xml \ + --license lic.xml \ + --setvars project.outputDirectory=$(pwd) \ + --setvars project.version=$SQLITESTUDIO_VERSION + ls -l + - name: Upload package artifact uses: actions/upload-artifact@v1 with: name: sqlitestudio-${{ env.SQLITESTUDIO_VERSION }}.tar.xz path: output/portable/sqlitestudio-${{ env.SQLITESTUDIO_VERSION }}.tar.xz + + - name: Upload installer artifact + uses: actions/upload-artifact@v1 + with: + name: SQLiteStudio-${{ env.SQLITESTUDIO_VERSION }}-linux-x64-installer.run + path: SQLiteStudio-${{ env.SQLITESTUDIO_VERSION }}-linux-x64-installer.run diff --git a/.github/workflows/mac_release.yml b/.github/workflows/mac_release.yml index 109b31b..3ba4f25 100644 --- a/.github/workflows/mac_release.yml +++ b/.github/workflows/mac_release.yml @@ -1,19 +1,31 @@ env: QT_VERSION: '5.15.2' TCL_VERSION: '8.6' - SQLITE_VERSION: '3350400' - SQLITE_RELEASE_YEAR: '2021' + SQLITE_VERSION: '3410200' + SQLITE_RELEASE_YEAR: '2023' + PYTHON_VERSION: '3.9' + ICU_VERSION: '72.1' PORTABLE_DIR: ${{ github.workspace }}/output/portable/SQLiteStudio + INSTALLBUILDER_DIR: ../ib + INSTALLBUILDER_URL: https://releases.bitrock.com/installbuilder/installbuilder-enterprise-23.1.0-osx-installer.dmg -name: MaxOSX release build +name: MacOSX release build on: + workflow_dispatch: + inputs: + use_ccache: + description: 'Use ccache (for workflow debugging only!)' + required: false + type: boolean + schedule: + - cron: '0 3 * * 1' # run at 3 AM UTC every Monday repository_dispatch: types: [mac_release] jobs: build: - runs-on: macos-latest + runs-on: macos-11 steps: # - name: Debug @@ -24,27 +36,54 @@ jobs: # ls -l /usr/local/opt/openssl/include/ # ls -l /usr/local/opt/expat/include - - name: Cache Qt - id: cache-qt - uses: actions/cache@v1 - with: - path: ${{ github.workspace }}/../Qt - key: ${{ runner.os }}-Qt-${{ env.QT_VERSION }}-Mac-Cache - + - name: Qt installation dir + id: qt-installation-dir + run: echo "DIR=$(readlink -f ${{ github.workspace }}/..)" >> $GITHUB_OUTPUT + - name: Install Qt - uses: jurplel/install-qt-action@v2 + uses: jurplel/install-qt-action@v3 with: - cached: ${{ steps.cache-qt.outputs.cache-hit }} + cache: true version: ${{ env.QT_VERSION }} host: 'mac' - dir: '${{ github.workspace }}/..' - modules: 'qtscript' - + dir: '${{ steps.qt-installation-dir.DIR }}' + aqtversion: '==3.0.*' + py7zrversion: '==0.20.*' + setup-python: 'false' + extra: '--external 7z' + + - name: Install the InstalBuilder + shell: bash + run: | + curl -L ${{ env.INSTALLBUILDER_URL }} --output ib.dmg + hdiutil attach ib.dmg + /Volumes/InstallBuilder\ Enterprise/*.app/Contents/MacOS/installbuilder.sh --mode unattended --prefix ${{ env.INSTALLBUILDER_DIR }} + ${{ env.INSTALLBUILDER_DIR }}/bin/builder --version + echo "INSTALLER_SRC_PREFIX=$(pwd)" >> $GITHUB_ENV + echo "INSTALLER_BIN_PREFIX=/Volumes/SQLiteStudio" >> $GITHUB_ENV + + - uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + architecture: 'x64' + - name: Clone repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ github.event.client_payload.branch }} + - name: Prepare ccache + if: inputs.use_ccache || false + uses: hendrikmuhs/ccache-action@v1.2.8 + with: + key: mac_release + max-size: "24M" + + - name: Configure ccache + if: inputs.use_ccache || false + run: | + echo "PATH=/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" >> $GITHUB_ENV + - name: Install SQLite3 run: | wget http://sqlite.org/$SQLITE_RELEASE_YEAR/sqlite-amalgamation-$SQLITE_VERSION.zip @@ -68,39 +107,109 @@ jobs: sudo cp *.h /usr/local/include/ echo "DYLD_LIBRARY_PATH=/usr/local/lib" >> $GITHUB_ENV + - name: Install extension dependencies + run: | + brew tap-new $USER/local-tap + brew extract --version=$ICU_VERSION icu4c $USER/local-tap + brew install icu4c@$ICU_VERSION + echo ICU_FLAGS="$(PKG_CONFIG_PATH="/usr/local/opt/icu4c@$ICU_VERSION/lib/pkgconfig" pkg-config --libs --cflags icu-uc icu-io)" \ + >> $GITHUB_ENV + + - name: Compile additional SQLite3 extensions + shell: bash + run: | + cd .. + mkdir ext + curl -L http://sqlite.org/$SQLITE_RELEASE_YEAR/sqlite-src-$SQLITE_VERSION.zip --output sqlite-src-$SQLITE_VERSION.zip + ls -l + unzip sqlite-src-$SQLITE_VERSION.zip + ls -l + cd sqlite-src-$SQLITE_VERSION/ext + ls -l + FLAGS="-ldl -Os -fpic -shared -I/usr/local/include -L/usr/local/lib -lsqlite3" + for f in compress sqlar; do + echo "gcc misc/$f.c -Imisc $FLAGS -lz -o ../../ext/$f.dylib" + gcc misc/$f.c -Imisc $FLAGS -lz -o ../../ext/$f.dylib + done + for f in csv decimal eval ieee754 percentile rot13 series uint uuid zorder; do + echo "gcc misc/$f.c -Imisc $FLAGS -o ../../ext/$f.dylib" + gcc misc/$f.c -Imisc $FLAGS -o ../../ext/$f.dylib + done + for f in icu; do + echo "gcc icu/$f.c -Iicu $ICU_FLAGS $FLAGS -o ../../ext/$f.dylib" + gcc icu/$f.c -Iicu $ICU_FLAGS $FLAGS -o ../../ext/$f.dylib + done + ls -l ../../ext/ + + - name: Install Tcl + run: | + brew install tcl-tk + echo "PATH=/usr/local/opt/tcl-tk/bin:$PATH" >> $GITHUB_ENV + - name: Prepare deps run: | mkdir ../lib ../include cp /usr/local/lib/libsqlite3* ../lib cp /usr/local/include/sqlite3* ../include - + - name: Prepare output dir run: mkdir output output/build output/build/Plugins - + - name: Compile SQLiteStudio3 working-directory: output/build run: | - qmake CONFIG+=portable ../../SQLiteStudio3 + qmake \ + $([ ${{ inputs.use_ccache || false }} = false ] || echo "CONFIG+=ccache") \ + CONFIG+=portable \ + ../../SQLiteStudio3 make -j 2 - + - name: Compile Plugins working-directory: output/build/Plugins run: | - qmake CONFIG+=portable ../../../Plugins - make -j 2 - + qmake \ + $([ ${{ inputs.use_ccache || false }} = false ] || echo "CONFIG+=ccache") \ + CONFIG+=portable \ + "INCLUDEPATH+=$pythonLocation/include/python$PYTHON_VERSION" "LIBS += -L$pythonLocation/lib" \ + ../../../Plugins + make -j 1 + + - name: Copy SQLite extensions to output dir + shell: bash + run: | + cp -R ../ext output/SQLiteStudio/SQLiteStudio.app/Contents/extensions + - name: Build packages working-directory: output/build run: | make pkg - + - name: Determine SQLiteStudio version working-directory: output/SQLiteStudio run: | echo "SQLITESTUDIO_VERSION=$(SQLiteStudio.app/Contents/MacOS/sqlitestudiocli -v | awk '{print $2}')" >> $GITHUB_ENV + - name: Create installer package + shell: bash + env: + IB_LICENSE: ${{ secrets.INSTALLER_LICENSE }} + run: | + echo "$IB_LICENSE" > lic.xml + hdiutil attach output/SQLiteStudio/sqlitestudio-${{ env.SQLITESTUDIO_VERSION }}.dmg + ${{ env.INSTALLBUILDER_DIR }}/bin/builder build SQLiteStudio-installer.xml \ + --license lic.xml \ + --setvars project.outputDirectory=$(pwd) \ + --setvars project.version=${{ env.SQLITESTUDIO_VERSION }} + ls -l + - name: Upload package artifact uses: actions/upload-artifact@v1 with: name: sqlitestudio-${{ env.SQLITESTUDIO_VERSION }}.dmg path: output/SQLiteStudio/sqlitestudio-${{ env.SQLITESTUDIO_VERSION }}.dmg + + - name: Upload installer artifact + uses: actions/upload-artifact@v1 + with: + name: SQLiteStudio-${{ env.SQLITESTUDIO_VERSION }}-osx-installer.dmg + path: SQLiteStudio-${{ env.SQLITESTUDIO_VERSION }}-osx-installer.dmg diff --git a/.github/workflows/remove_old_artifacts.yml b/.github/workflows/remove_old_artifacts.yml new file mode 100644 index 0000000..79a23c4 --- /dev/null +++ b/.github/workflows/remove_old_artifacts.yml @@ -0,0 +1,19 @@ +name: Remove old artifacts + +on: + workflow_dispatch: + inputs: + schedule: + # Every day at 1am + - cron: '0 1 * * *' + +jobs: + remove-old-artifacts: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Remove old artifacts + uses: c-hive/gha-remove-artifacts@v1 + with: + age: '10 days' # ' ', e.g. 5 days, 2 years, 90 seconds, parsed by Moment.js diff --git a/.github/workflows/sandbox_w64.yml b/.github/workflows/sandbox_w64.yml new file mode 100644 index 0000000..498b690 --- /dev/null +++ b/.github/workflows/sandbox_w64.yml @@ -0,0 +1,105 @@ +env: + QT_VERSION: '5.15.2' + SQLITE_VERSION: '3400000' + SQLITE_RELEASE_YEAR: '2022' + QT_ARCH: 'win32_mingw81' + PYTHON_VERSION: '3.9' + QT_BIN_DIR: ../Qt/5.15.2/mingw81_32/bin + +name: Sandbox/playground + +on: + workflow_dispatch: + inputs: + +jobs: + win: + runs-on: windows-2019 + + steps: + - name: Set up MinGW + uses: egor-tensin/setup-mingw@v2 + with: + platform: x86 + + - name: Cache Qt + id: cache-qt + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}\..\Qt + key: ${{ runner.os }}-${{ env.QT_VERSION }}-Qt-Cache + + - name: Install Qt + uses: jurplel/install-qt-action@v3 + with: + cache: true + version: ${{ env.QT_VERSION }} + host: 'windows' + arch: ${{ env.QT_ARCH }} + # jurplel/install-qt-action has a bug due to which we cannot use ${{ github.workspace }} for the "dir" property, because it will fail. + dir: 'D:/' + setup-python: 'false' + + - uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + architecture: 'x86' + + - name: Clone repo + uses: actions/checkout@v3 + with: + ref: ${{ github.event.client_payload.branch }} + + - name: Install dependencies + shell: bash + run: | + 7z x -o".." win_deps/win32_deps.zip + echo "../lib" >> $GITHUB_PATH + + - name: Find zlib + shell: bash + run: | + find ../../ -name "zlib.h" + + - name: Install SQLite3 + shell: bash + run: | + cd .. + curl -L http://sqlite.org/$SQLITE_RELEASE_YEAR/sqlite-amalgamation-$SQLITE_VERSION.zip --output sqlite-amalgamation-$SQLITE_VERSION.zip + 7z x sqlite-amalgamation-$SQLITE_VERSION.zip + cd sqlite-amalgamation-$SQLITE_VERSION + gcc.exe sqlite3.c -Os -fpic -DWIN32 -m32 -I. -shared -o sqlite3.dll \ + -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT \ + -DSQLITE_ENABLE_DBSTAT_VTAB \ + -DSQLITE_ENABLE_BYTECODE_VTAB \ + -DSQLITE_ENABLE_COLUMN_METADATA \ + -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ + -DSQLITE_ENABLE_FTS3 \ + -DSQLITE_ENABLE_FTS4 \ + -DSQLITE_ENABLE_FTS5 \ + -DSQLITE_ENABLE_GEOPOLY \ + -DSQLITE_ENABLE_JSON1 \ + -DSQLITE_ENABLE_RTREE \ + -DSQLITE_ENABLE_MATH_FUNCTIONS + cp -f sqlite3.dll ../lib/ + cp -f sqlite3.h ../include/ + cp -f sqlite3ext.h ../include/ + + - name: Compile additional SQLite3 extensions + shell: bash + run: | + cd .. + mkdir ext + curl -L http://sqlite.org/$SQLITE_RELEASE_YEAR/sqlite-src-$SQLITE_VERSION.zip --output sqlite-src-$SQLITE_VERSION.zip + 7z x sqlite-src-$SQLITE_VERSION.zip + cd sqlite-src-$SQLITE_VERSION/ext + FLAGS="-shared -Os -fpic -DWIN32 -m32 -Imisc -I../../include -L../../lib -lsqlite3" + for f in compress; do + echo "gcc.exe misc/$f.c $FLAGS -lzlib1 -o ../../ext/$f.dll" + gcc.exe misc/$f.c $FLAGS -lzlib1 -o ../../ext/$f.dll + done + for f in csv decimal eval ieee754 percentile rot13 series uint uuid zorder; do + echo "gcc.exe misc/$f.c $FLAGS -o ../../ext/$f.dll" + gcc.exe misc/$f.c $FLAGS -o ../../ext/$f.dll + done + ls -l ../../ext/ diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3453b57..30d2f59 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,7 +1,8 @@ env: TCL_VERSION: '8.6' - SQLITE_VERSION: '3350400' - SQLITE_RELEASE_YEAR: '2021' + SQLITE_VERSION: '3410200' + SQLITE_RELEASE_YEAR: '2023' + PYTHON_VERSION: '3.9' PORTABLE_DIR: ${{ github.workspace }}/output/portable/SQLiteStudio name: Unit tests @@ -12,6 +13,7 @@ on: - master - 3.2 - 3.3 + - 3.4 paths: - '**.cpp' - '**.hpp' @@ -20,35 +22,41 @@ on: - '**.pro' - '**.pri' - '**.ui' + - '**.qrc' - '**/tests.yml' jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 strategy: matrix: - QT_VERSION: ['5.12.10', '5.15.2'] + QT_VERSION: ['5.15.2'] steps: - - name: Cache Qt - id: cache-qt - uses: actions/cache@v1 + - uses: actions/setup-python@v4 with: - path: ${{ github.workspace }}/../Qt - key: ${{ runner.os }}-Qt-${{ matrix.QT_VERSION }}-Linux-Cache - + python-version: ${{ env.PYTHON_VERSION }} + architecture: 'x64' + + - name: Qt installation dir + id: qt-installation-dir + run: echo "DIR=$(readlink -f ${{ github.workspace }}/..)" >> $GITHUB_OUTPUT + - name: Install Qt - uses: jurplel/install-qt-action@v2 + uses: jurplel/install-qt-action@v3 with: - cached: ${{ steps.cache-qt.outputs.cache-hit }} + cache: true version: ${{ matrix.QT_VERSION }} host: 'linux' - dir: '${{ github.workspace }}/..' - modules: 'qtscript' + dir: '${{ steps.qt-installation-dir.DIR }}' + aqtversion: '==3.0.*' + py7zrversion: '==0.20.*' + setup-python: 'false' + extra: '--external 7z' - name: Clone repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ env.GITHUB_REF }} @@ -95,8 +103,8 @@ jobs: - name: Compile Plugins working-directory: output/build/Plugins run: | - qmake CONFIG+=portable CONFIG+=debug DEFINES+=tests ../../../Plugins - make -j 2 + qmake CONFIG+=portable CONFIG+=debug DEFINES+=tests "INCLUDEPATH+=$pythonLocation/include/python$PYTHON_VERSION" "LIBS += -L$pythonLocation/lib" ../../../Plugins + make -j 1 - name: Run tests working-directory: output/SQLiteStudio diff --git a/.github/workflows/update_messages.yml b/.github/workflows/update_messages.yml new file mode 100644 index 0000000..011d4e0 --- /dev/null +++ b/.github/workflows/update_messages.yml @@ -0,0 +1,67 @@ +env: + PYTHON_VERSION: '3.9' + QT_VERSION: '5.15.2' + TCL_VERSION: '8.6' + +name: Update messages + +on: + workflow_dispatch: + inputs: + schedule: + - cron: '30 5 * * *' # run at 5:30 AM UTC every day + +jobs: + build: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + architecture: 'x64' + + - name: Qt installation dir + id: qt-installation-dir + run: echo "DIR=$(readlink -f ${{ github.workspace }}/..)" >> $GITHUB_OUTPUT + + - name: Install Qt + uses: jurplel/install-qt-action@v3 + with: + cache: true + version: ${{ env.QT_VERSION }} + host: 'linux' + dir: '${{ steps.qt-installation-dir.DIR }}' + aqtversion: '==3.0.*' + py7zrversion: '==0.20.*' + setup-python: 'false' + extra: '--external 7z' + + - name: Clone repo + uses: actions/checkout@v3 + with: + ref: ${{ env.GITHUB_REF }} + + - name: Install Tcl + run: sudo apt-get install -qq libtcl$TCL_VERSION tcl$TCL_VERSION-dev + + - name: Install xmllint + run: sudo apt install libxml2-utils + + - name: Executing lang update + working-directory: SQLiteStudio3 + run: tclsh lang.tcl update + + - name: Listing Git status + run: | + git status + echo "===========================================================================" + git diff + + - name: Committing changes + run: | + git config user.name github-actions + git config user.email github-actions@github.com + git add . + git diff-index --quiet HEAD || git commit -m "Automated update of translation files." + git push diff --git a/.github/workflows/win32_release.yml b/.github/workflows/win32_release.yml new file mode 100644 index 0000000..a522d3c --- /dev/null +++ b/.github/workflows/win32_release.yml @@ -0,0 +1,264 @@ +env: + QT_VERSION: '5.15.2' + SQLITE_VERSION: '3410200' + SQLITE_RELEASE_YEAR: '2023' + QT_ARCH: 'win32_mingw81' + PYTHON_VERSION: '3.9' + ICU_VER: '72' + ICU_URL: https://mirror.msys2.org/mingw/mingw32/mingw-w64-i686-icu-72.1-1-any.pkg.tar.zst + QT_BIN_DIR: ../Qt/5.15.2/mingw81_32/bin + PORTABLE_DIR: output/portable/SQLiteStudio + INSTALLBUILDER_DIR: ../ib + MINGW_URL: https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/tools_mingw/qt.tools.win32_mingw810/8.1.0-1-202004170606i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z + INSTALLBUILDER_URL: https://releases.bitrock.com/installbuilder/installbuilder-enterprise-23.1.0-windows-installer.exe + +name: Windows 32-bit release build + +on: + workflow_dispatch: + inputs: + use_ccache: + description: 'Use ccache (for workflow debugging only!)' + required: false + type: boolean + schedule: + - cron: '30 2 * * 1' # run at 2 AM UTC every Monday + repository_dispatch: + types: [win32_release] + +jobs: + build: + runs-on: windows-2019 + + steps: + - name: Set up MinGW + uses: egor-tensin/setup-mingw@v2 + with: + platform: x86 + version: 8.1.0 + + - name: Cache Qt + id: cache-qt + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}\..\Qt + key: ${{ runner.os }}-${{ env.QT_VERSION }}-Qt-Cache + + - name: Install Qt + uses: jurplel/install-qt-action@v3 + with: + cache: true + version: ${{ env.QT_VERSION }} + host: 'windows' + arch: ${{ env.QT_ARCH }} + # jurplel/install-qt-action has a bug due to which we cannot use ${{ github.workspace }} for the "dir" property, because it will fail. + dir: 'D:/' + setup-python: 'false' + + - uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + architecture: 'x86' + + - name: Clone repo + uses: actions/checkout@v3 + with: + ref: ${{ github.event.client_payload.branch }} + + - name: Install dependencies + shell: bash + run: | + 7z x -o".." win_deps/win32_deps.zip + echo "../lib" >> $GITHUB_PATH + + - name: Prepare ccache + if: inputs.use_ccache || false + uses: hendrikmuhs/ccache-action@v1.2.8 + with: + key: win32_release + max-size: "24M" + + - name: Configure ccache (or not ccache) + shell: bash + run: | + if [ ${{ inputs.use_ccache || false }} = false ]; then + printf 'GCC_COMMAND=gcc.exe\nGXX_COMMAND=g++.exe\n' + else + printf 'GCC_COMMAND=ccache.exe gcc.exe\nGXX_COMMAND=ccache.exe g++.exe\n' + fi >> $GITHUB_ENV + + - name: Install SQLite3 + shell: bash + run: | + cd .. + curl -L http://sqlite.org/$SQLITE_RELEASE_YEAR/sqlite-amalgamation-$SQLITE_VERSION.zip --output sqlite-amalgamation-$SQLITE_VERSION.zip + 7z x sqlite-amalgamation-$SQLITE_VERSION.zip + cd sqlite-amalgamation-$SQLITE_VERSION + $GCC_COMMAND sqlite3.c -Os -fpic -DWIN32 -m32 -I. -shared -o sqlite3.dll \ + -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT \ + -DSQLITE_ENABLE_DBSTAT_VTAB \ + -DSQLITE_ENABLE_BYTECODE_VTAB \ + -DSQLITE_ENABLE_COLUMN_METADATA \ + -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ + -DSQLITE_ENABLE_FTS3 \ + -DSQLITE_ENABLE_FTS4 \ + -DSQLITE_ENABLE_FTS5 \ + -DSQLITE_ENABLE_GEOPOLY \ + -DSQLITE_ENABLE_JSON1 \ + -DSQLITE_ENABLE_RTREE \ + -DSQLITE_ENABLE_MATH_FUNCTIONS + cp -f sqlite3.dll ../lib/ + cp -f sqlite3.h ../include/ + cp -f sqlite3ext.h ../include/ + + - name: Install extension dependencies + shell: bash + run: | + cd .. + curl -L "$ICU_URL" | tar -xf - --zstd + mv mingw32 icu + + - name: Compile additional SQLite3 extensions + shell: bash + run: | + cd .. + mkdir ext + curl -L http://sqlite.org/$SQLITE_RELEASE_YEAR/sqlite-src-$SQLITE_VERSION.zip --output sqlite-src-$SQLITE_VERSION.zip + 7z x sqlite-src-$SQLITE_VERSION.zip + cd sqlite-src-$SQLITE_VERSION/ext + FLAGS="-shared -Os -fpic -DWIN32 -m32 -I../../include -L../../lib -lsqlite3" + #for f in compress sqlar; do + # echo "$GCC_COMMAND misc/$f.c -Imisc $FLAGS -lzlib1 -o ../../ext/$f.dll" + # $GCC_COMMAND misc/$f.c -Imisc $FLAGS -lzlib1 -o ../../ext/$f.dll + #done + for f in csv decimal eval ieee754 percentile rot13 series uint uuid zorder; do + echo "$GCC_COMMAND misc/$f.c -Imisc $FLAGS -o ../../ext/$f.dll" + $GCC_COMMAND misc/$f.c -Imisc $FLAGS -o ../../ext/$f.dll + done + for f in icu; do + echo "$GCC_COMMAND icu/$f.c -I../../icu/include -L../../icu/lib -licuio -licuin -licuuc -licudt $FLAGS -o ../../ext/$f.dll" + $GCC_COMMAND icu/$f.c -I../../icu/include -L../../icu/lib -licuio -licuin -licuuc -licudt $FLAGS -o ../../ext/$f.dll + done + ls -l ../../ext/ + + - name: Prepare output dir + shell: bash + run: mkdir output output/build output/build/Plugins + + - name: Compile SQLiteStudio3 + working-directory: output/build + run: | + qmake.exe ` + CONFIG+=portable ` + "QMAKE_CXX=${{ env.GXX_COMMAND }}" ` + "QMAKE_CXXFLAGS+=-m32" ` + ..\..\SQLiteStudio3 + mingw32-make.exe -j 2 + + - name: Compile Plugins + working-directory: output/build/Plugins + run: | + qmake.exe ` + "QMAKE_CXX=${{ env.GXX_COMMAND }}" ` + CONFIG+=portable ` + "INCLUDEPATH+=${{ env.pythonLocation }}/include" "LIBS += -L${{ env.pythonLocation }}" ` + "QMAKE_CXXFLAGS+=-m32" ` + ..\..\..\Plugins + mingw32-make.exe -j 1 + + - name: Copy SQLite extensions to output dir + shell: bash + run: | + cp -R ../ext output/SQLiteStudio/ + + - name: Prepare portable dir + shell: bash + working-directory: output + run: | + mkdir portable + cp -R SQLiteStudio portable/ + + - name: Clean-up portable dir + shell: bash + run: | + cd ${{ env.PORTABLE_DIR }} + rm -f *.a + rm -f plugins/*.a + rm -f styles/*.a + echo "ABSOLUTE_PORTABLE_DIR=`pwd`" >> $GITHUB_ENV + + - name: Prepare portable distro (Qt) + shell: bash + working-directory: ${{ env.Qt5_Dir }} + run: | + for f in Qt5Core Qt5Gui Qt5Network Qt5PrintSupport Qt5Qml Qt5Svg Qt5Widgets Qt5Xml libgcc_s_dw2-1 libstdc++-6 libwinpthread-1; do cp bin/$f.dll "$ABSOLUTE_PORTABLE_DIR"; done + cp bin/qt.conf "$ABSOLUTE_PORTABLE_DIR" + mkdir -p "$ABSOLUTE_PORTABLE_DIR/iconengines" + mkdir -p "$ABSOLUTE_PORTABLE_DIR/platforms" + mkdir -p "$ABSOLUTE_PORTABLE_DIR/printsupport" + mkdir -p "$ABSOLUTE_PORTABLE_DIR/styles" + mkdir -p "$ABSOLUTE_PORTABLE_DIR/imageformats" + cp plugins/iconengines/qsvgicon.dll "$ABSOLUTE_PORTABLE_DIR/iconengines" + cp plugins/platforms/qwindows.dll "$ABSOLUTE_PORTABLE_DIR/platforms" + cp plugins/styles/qwindowsvistastyle.dll "$ABSOLUTE_PORTABLE_DIR/styles" + cp plugins/printsupport/windowsprintersupport.dll "$ABSOLUTE_PORTABLE_DIR/printsupport" + for f in qgif qicns qico qjpeg qsvg qtga qtiff qwbmp; do cp plugins/imageformats/$f.dll "$ABSOLUTE_PORTABLE_DIR/imageformats"; done + + - name: Prepare portable distro (Deps) + shell: bash + run: | + cd ../lib + cp *.dll "$ABSOLUTE_PORTABLE_DIR" + cd ../icu/bin + cp libicuio$ICU_VER.dll libicuin$ICU_VER.dll libicuuc$ICU_VER.dll libicudt$ICU_VER.dll "$ABSOLUTE_PORTABLE_DIR" + + - name: Prepare portable distro (Resources) + shell: bash + run: | + cp SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio.ico "$ABSOLUTE_PORTABLE_DIR"/appicon.ico + cp SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio.svg "${{ env.PORTABLE_DIR }}"/appicon.svg + + - name: Determine SQLiteStudio version + shell: bash + run: | + cd $ABSOLUTE_PORTABLE_DIR + echo "SQLITESTUDIO_VERSION=$(./sqlitestudiocli.exe --version | cut -f 2 -d ' ')" >> $GITHUB_ENV + + - name: Assemble portable package + shell: bash + run: | + cd $ABSOLUTE_PORTABLE_DIR/.. + 7z a -r sqlitestudio-$SQLITESTUDIO_VERSION.zip SQLiteStudio + + - name: Install the InstalBuilder + shell: bash + env: + IB_LICENSE: ${{ secrets.INSTALLER_LICENSE }} + run: | + curl -L ${{ env.INSTALLBUILDER_URL }} --output ib.exe + ./ib.exe --mode unattended --prefix ${{ env.INSTALLBUILDER_DIR }} + ${{ env.INSTALLBUILDER_DIR }}/bin/builder-cli.exe --version + echo "$IB_LICENSE" > lic.xml + echo "INSTALLER_SRC_PREFIX=$(pwd)" >> $GITHUB_ENV + echo "INSTALLER_BIN_PREFIX=$ABSOLUTE_PORTABLE_DIR" >> $GITHUB_ENV + + - name: Create installer package + shell: bash + run: | + ${{ env.INSTALLBUILDER_DIR }}/bin/builder-cli.exe build SQLiteStudio-installer.xml \ + --license lic.xml \ + --setvars project.outputDirectory=$(pwd) \ + --setvars project.version=$SQLITESTUDIO_VERSION + ls -l + + - name: Upload package artifact + uses: actions/upload-artifact@v1 + with: + name: sqlitestudio-${{ env.SQLITESTUDIO_VERSION }}.zip + path: output/portable/sqlitestudio-${{ env.SQLITESTUDIO_VERSION }}.zip + + - name: Upload installer artifact + uses: actions/upload-artifact@v1 + with: + name: SQLiteStudio-${{ env.SQLITESTUDIO_VERSION }}-windows-installer.exe + path: SQLiteStudio-${{ env.SQLITESTUDIO_VERSION }}-windows-installer.exe diff --git a/.github/workflows/win64_release.yml b/.github/workflows/win64_release.yml new file mode 100644 index 0000000..2f44835 --- /dev/null +++ b/.github/workflows/win64_release.yml @@ -0,0 +1,265 @@ +env: + QT_VERSION: '5.15.2' + SQLITE_VERSION: '3410200' + SQLITE_RELEASE_YEAR: '2023' + QT_ARCH: 'win64_mingw81' + PYTHON_VERSION: '3.9' + ICU_VER: '72' + ICU_URL: https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-icu-72.1-1-any.pkg.tar.zst + MINGW_DIR: ../Qt/Tools/mingw810_64 + QT_BIN_DIR: ../Qt/5.15.2/mingw81_64/bin + PORTABLE_DIR: output/portable/SQLiteStudio + INSTALLBUILDER_DIR: ../ib + MINGW_URL: https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/tools_mingw/qt.tools.win64_mingw810/8.1.0-1-202004170606x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z + INSTALLBUILDER_URL: https://releases.bitrock.com/installbuilder/installbuilder-enterprise-23.1.0-windows-x64-installer.exe + +name: Windows 64-bit release build + +on: + workflow_dispatch: + inputs: + use_ccache: + description: 'Use ccache (for workflow debugging only!)' + required: false + type: boolean + schedule: + - cron: '0 2 * * 1' # run at 2 AM UTC every Monday + repository_dispatch: + types: [win64_release] + +jobs: + build: + runs-on: windows-2019 + + steps: + - name: Cache Qt + id: cache-qt + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}\..\Qt + key: ${{ runner.os }}-${{ env.QT_VERSION }}-Qt-Cache + + - name: Install Qt + uses: jurplel/install-qt-action@v3 + with: + cache: true + version: ${{ env.QT_VERSION }} + host: 'windows' + arch: ${{ env.QT_ARCH }} + # jurplel/install-qt-action has a bug due to which we cannot use ${{ github.workspace }} for the "dir" property, because it will fail. + dir: 'D:/' + setup-python: 'false' + + - name: Install mingw + if: steps.cache-qt.outputs.cache-hit != 'true' + shell: bash + run: | + curl -L ${{ env.MINGW_URL }} --output ../mingw.7z + 7z x -o"../Qt" ../mingw.7z + echo "${{ env.MINGW_DIR }}/bin" >> $GITHUB_PATH + + - uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + architecture: 'x64' + + - name: Clone repo + uses: actions/checkout@v3 + with: + ref: ${{ github.event.client_payload.branch }} + + - name: Install dependencies + shell: bash + run: | + 7z x -o".." win_deps/win64_deps.zip + echo "../lib" >> $GITHUB_PATH + + - name: Prepare ccache + if: inputs.use_ccache || false + uses: hendrikmuhs/ccache-action@v1.2.8 + with: + key: win64_release + max-size: "24M" + + - name: Configure ccache (or not ccache) + shell: bash + run: | + if [ ${{ inputs.use_ccache || false }} = false ]; then + printf 'GCC_COMMAND=gcc.exe\nGXX_COMMAND=g++.exe\n' + else + printf 'GCC_COMMAND=ccache.exe gcc.exe\nGXX_COMMAND=ccache.exe g++.exe\n' + fi >> $GITHUB_ENV + + - name: Install SQLite3 + shell: bash + run: | + cd .. + curl -L http://sqlite.org/$SQLITE_RELEASE_YEAR/sqlite-amalgamation-$SQLITE_VERSION.zip --output sqlite-amalgamation-$SQLITE_VERSION.zip + 7z x sqlite-amalgamation-$SQLITE_VERSION.zip + cd sqlite-amalgamation-$SQLITE_VERSION + $GCC_COMMAND sqlite3.c -Os -fpic -DWIN64 -m64 -I. -shared -o sqlite3.dll \ + -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT \ + -DSQLITE_ENABLE_DBSTAT_VTAB \ + -DSQLITE_ENABLE_BYTECODE_VTAB \ + -DSQLITE_ENABLE_COLUMN_METADATA \ + -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ + -DSQLITE_ENABLE_FTS3 \ + -DSQLITE_ENABLE_FTS4 \ + -DSQLITE_ENABLE_FTS5 \ + -DSQLITE_ENABLE_GEOPOLY \ + -DSQLITE_ENABLE_JSON1 \ + -DSQLITE_ENABLE_RTREE \ + -DSQLITE_ENABLE_MATH_FUNCTIONS + cp -f sqlite3.dll ../lib/ + cp -f sqlite3.h ../include/ + cp -f sqlite3ext.h ../include/ + + - name: Install extension dependencies + shell: bash + run: | + cd .. + curl -L "$ICU_URL" | tar -xf - --zstd + mv mingw64 icu + + - name: Compile additional SQLite3 extensions + shell: bash + run: | + cd .. + mkdir ext + curl -L http://sqlite.org/$SQLITE_RELEASE_YEAR/sqlite-src-$SQLITE_VERSION.zip --output sqlite-src-$SQLITE_VERSION.zip + 7z x sqlite-src-$SQLITE_VERSION.zip + cd sqlite-src-$SQLITE_VERSION/ext + FLAGS="-shared -Os -fpic -DWIN64 -m64 -I../../include -L../../lib -lsqlite3" + for f in compress sqlar; do + echo "$GCC_COMMAND misc/$f.c -Imisc $FLAGS -lzlib1 -o ../../ext/$f.dll" + $GCC_COMMAND misc/$f.c -Imisc $FLAGS -lzlib1 -o ../../ext/$f.dll + done + for f in csv decimal eval ieee754 percentile rot13 series uint uuid zorder; do + echo "$GCC_COMMAND misc/$f.c -Imisc $FLAGS -o ../../ext/$f.dll" + $GCC_COMMAND misc/$f.c -Imisc $FLAGS -o ../../ext/$f.dll + done + for f in icu; do + echo "$GCC_COMMAND icu/$f.c -I../../icu/include -L../../icu/lib -licuio -licuin -licuuc -licudt $FLAGS -o ../../ext/$f.dll" + $GCC_COMMAND icu/$f.c -I../../icu/include -L../../icu/lib -licuio -licuin -licuuc -licudt $FLAGS -o ../../ext/$f.dll + done + ls -l ../../ext/ + + - name: Prepare output dir + shell: bash + run: mkdir output output/build output/build/Plugins + + - name: Compile SQLiteStudio3 + working-directory: output/build + run: | + qmake.exe ` + CONFIG+=portable ` + "QMAKE_CXX=${{ env.GXX_COMMAND }}" ` + ..\..\SQLiteStudio3 + mingw32-make.exe -j 2 + + - name: Compile Plugins + working-directory: output/build/Plugins + run: | + qmake.exe ` + "QMAKE_CXX=${{ env.GXX_COMMAND }}" ` + CONFIG+=portable ` + "INCLUDEPATH+=${{ env.pythonLocation }}/include" "LIBS += -L${{ env.pythonLocation }}" ` + ..\..\..\Plugins + mingw32-make.exe -j 1 + + - name: Copy SQLite extensions to output dir + shell: bash + run: | + cp -R ../ext output/SQLiteStudio/ + + - name: Prepare portable dir + shell: bash + working-directory: output + run: | + mkdir portable + cp -R SQLiteStudio portable/ + + - name: Clean-up portable dir + shell: bash + run: | + cd ${{ env.PORTABLE_DIR }} + rm -f *.a + rm -f plugins/*.a + rm -f styles/*.a + echo "ABSOLUTE_PORTABLE_DIR=`pwd`" >> $GITHUB_ENV + + - name: Prepare portable distro (Qt) + shell: bash + working-directory: ${{ env.Qt5_Dir }} + run: | + for f in Qt5Core Qt5Gui Qt5Network Qt5PrintSupport Qt5Qml Qt5Svg Qt5Widgets Qt5Xml libgcc_s_seh-1 libstdc++-6 libwinpthread-1; do cp bin/$f.dll "$ABSOLUTE_PORTABLE_DIR"; done + cp bin/qt.conf "$ABSOLUTE_PORTABLE_DIR" + mkdir -p "$ABSOLUTE_PORTABLE_DIR/iconengines" + mkdir -p "$ABSOLUTE_PORTABLE_DIR/platforms" + mkdir -p "$ABSOLUTE_PORTABLE_DIR/printsupport" + mkdir -p "$ABSOLUTE_PORTABLE_DIR/styles" + mkdir -p "$ABSOLUTE_PORTABLE_DIR/imageformats" + cp plugins/iconengines/qsvgicon.dll "$ABSOLUTE_PORTABLE_DIR/iconengines" + cp plugins/platforms/qwindows.dll "$ABSOLUTE_PORTABLE_DIR/platforms" + cp plugins/styles/qwindowsvistastyle.dll "$ABSOLUTE_PORTABLE_DIR/styles" + cp plugins/printsupport/windowsprintersupport.dll "$ABSOLUTE_PORTABLE_DIR/printsupport" + for f in qgif qicns qico qjpeg qsvg qtga qtiff qwbmp; do cp plugins/imageformats/$f.dll "$ABSOLUTE_PORTABLE_DIR/imageformats"; done + + - name: Prepare portable distro (Deps) + shell: bash + run: | + cd ../lib + cp *.dll "$ABSOLUTE_PORTABLE_DIR" + cd ../icu/bin + cp libicuio$ICU_VER.dll libicuin$ICU_VER.dll libicuuc$ICU_VER.dll libicudt$ICU_VER.dll "$ABSOLUTE_PORTABLE_DIR" + + - name: Prepare portable distro (Resources) + shell: bash + run: | + cp SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio.ico "$ABSOLUTE_PORTABLE_DIR"/appicon.ico + cp SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio.svg "${{ env.PORTABLE_DIR }}"/appicon.svg + + - name: Determine SQLiteStudio version + shell: bash + run: | + cd $ABSOLUTE_PORTABLE_DIR + echo "SQLITESTUDIO_VERSION=$(./sqlitestudiocli.exe --version | cut -f 2 -d ' ')" >> $GITHUB_ENV + + - name: Assemble portable package + shell: bash + run: | + cd $ABSOLUTE_PORTABLE_DIR/.. + 7z a -r sqlitestudio-$SQLITESTUDIO_VERSION.zip SQLiteStudio + + - name: Install the InstalBuilder + shell: bash + env: + IB_LICENSE: ${{ secrets.INSTALLER_LICENSE }} + run: | + curl -L ${{ env.INSTALLBUILDER_URL }} --output ib.exe + ./ib.exe --mode unattended --prefix ${{ env.INSTALLBUILDER_DIR }} + ${{ env.INSTALLBUILDER_DIR }}/bin/builder-cli.exe --version + echo "$IB_LICENSE" > lic.xml + echo "INSTALLER_SRC_PREFIX=$(pwd)" >> $GITHUB_ENV + echo "INSTALLER_BIN_PREFIX=$ABSOLUTE_PORTABLE_DIR" >> $GITHUB_ENV + + - name: Create installer package + shell: bash + run: | + ${{ env.INSTALLBUILDER_DIR }}/bin/builder-cli.exe build SQLiteStudio-installer.xml \ + --license lic.xml \ + --setvars project.outputDirectory=$(pwd) \ + --setvars project.version=$SQLITESTUDIO_VERSION + ls -l + + - name: Upload package artifact + uses: actions/upload-artifact@v1 + with: + name: sqlitestudio-${{ env.SQLITESTUDIO_VERSION }}.zip + path: output/portable/sqlitestudio-${{ env.SQLITESTUDIO_VERSION }}.zip + + - name: Upload installer artifact + uses: actions/upload-artifact@v1 + with: + name: SQLiteStudio-${{ env.SQLITESTUDIO_VERSION }}-windows-x64-installer.exe + path: SQLiteStudio-${{ env.SQLITESTUDIO_VERSION }}-windows-x64-installer.exe diff --git a/.github/workflows/win_release.yml b/.github/workflows/win_release.yml deleted file mode 100644 index 1a5307d..0000000 --- a/.github/workflows/win_release.yml +++ /dev/null @@ -1,157 +0,0 @@ -env: - QT_VERSION: '5.15.2' - SQLITE_VERSION: '3350400' - SQLITE_RELEASE_YEAR: '2021' - QT_ARCH: 'win64_mingw81' - MINGW_DIR: ../Qt/Tools/mingw810_64 - QT_BIN_DIR: ../Qt/5.15.2/mingw81_64/bin - PORTABLE_DIR: output/portable/SQLiteStudio - MINGW_URL: https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/tools_mingw/qt.tools.win64_mingw810/8.1.0-1-202004170606x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z - DEPS_URL: https://www.dropbox.com/s/2dpem7vy0ee19tu/win64_deps.zip?dl=1 - -name: Windows release build - -on: - repository_dispatch: - types: [win_release] - -jobs: - build: - runs-on: windows-2019 - - steps: - - name: Cache Qt - id: cache-qt - uses: actions/cache@v1 - with: - path: ${{ github.workspace }}\..\Qt - key: ${{ runner.os }}-${{ env.QT_VERSION }}-Qt-Cache - - - name: Install Qt - uses: jurplel/install-qt-action@v2 - with: - cached: ${{ steps.cache-qt.outputs.cache-hit }} - version: ${{ env.QT_VERSION }} - host: 'windows' - arch: ${{ env.QT_ARCH }} - # jurplel/install-qt-action has a bug due to which we cannot use ${{ github.workspace }} for the "dir" property, because it will fail. - dir: 'D:/' - modules: 'qtscript' - setup-python: 'false' - - - name: Install mingw - if: steps.cache-qt.outputs.cache-hit != 'true' - shell: bash - run: | - curl -L ${{ env.MINGW_URL }} --output ../mingw.7z - 7z x -o"../Qt" ../mingw.7z - echo "${{ env.MINGW_DIR }}/bin" >> $GITHUB_PATH - - - name: Clone repo - uses: actions/checkout@v2 - with: - ref: ${{ github.event.client_payload.branch }} - - - name: Install dependencies - shell: bash - run: | - curl -L ${{ env.DEPS_URL }} --output ../win64_deps.zip - 7z x -o".." ../win64_deps.zip - echo "../lib" >> $GITHUB_PATH - - - name: Install SQLite3 - shell: bash - run: | - cd .. - curl -L http://sqlite.org/$SQLITE_RELEASE_YEAR/sqlite-amalgamation-$SQLITE_VERSION.zip --output sqlite-amalgamation-$SQLITE_VERSION.zip - 7z x sqlite-amalgamation-$SQLITE_VERSION.zip - cd sqlite-amalgamation-$SQLITE_VERSION - gcc.exe sqlite3.c -Os -fpic -DWIN64 -m64 -I. -shared -o sqlite3.dll \ - -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT \ - -DSQLITE_ENABLE_DBSTAT_VTAB \ - -DSQLITE_ENABLE_BYTECODE_VTAB \ - -DSQLITE_ENABLE_COLUMN_METADATA \ - -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ - -DSQLITE_ENABLE_FTS3 \ - -DSQLITE_ENABLE_FTS4 \ - -DSQLITE_ENABLE_FTS5 \ - -DSQLITE_ENABLE_GEOPOLY \ - -DSQLITE_ENABLE_JSON1 \ - -DSQLITE_ENABLE_RTREE \ - -DSQLITE_ENABLE_MATH_FUNCTIONS - cp -f sqlite3.dll ../lib/ - cp -f sqlite3.h ../include/ - cp -f sqlite3ext.h ../include/ - - - name: Prepare output dir - shell: bash - run: mkdir output output/build output/build/Plugins - - - name: Compile SQLiteStudio3 - working-directory: output/build - run: | - qmake.exe CONFIG+=portable ..\..\SQLiteStudio3 - mingw32-make.exe -j 2 - - - name: Compile Plugins - working-directory: output/build/Plugins - run: | - qmake.exe CONFIG+=portable ..\..\..\Plugins - mingw32-make.exe -j 2 - - - name: Prepare portable dir - shell: bash - working-directory: output - run: | - mkdir portable - cp -R SQLiteStudio portable/ - - - name: Clean-up portable dir - shell: bash - run: | - cd ${{ env.PORTABLE_DIR }} - rm -f *.a - rm -f plugins/*.a - rm -f styles/*.a - echo "ABSOLUTE_PORTABLE_DIR=`pwd`" >> $GITHUB_ENV - - - name: Prepare portable distro (Qt) - shell: bash - working-directory: ${{ env.Qt5_Dir }} - run: | - for f in Qt5Core Qt5Gui Qt5Network Qt5PrintSupport Qt5Script Qt5Svg Qt5Widgets Qt5Xml libgcc_s_seh-1 libstdc++-6 libwinpthread-1; do cp bin/$f.dll "$ABSOLUTE_PORTABLE_DIR"; done - cp bin/qt.conf "$ABSOLUTE_PORTABLE_DIR" - mkdir -p "$ABSOLUTE_PORTABLE_DIR/iconengines" - mkdir -p "$ABSOLUTE_PORTABLE_DIR/platforms" - mkdir -p "$ABSOLUTE_PORTABLE_DIR/printsupport" - mkdir -p "$ABSOLUTE_PORTABLE_DIR/styles" - mkdir -p "$ABSOLUTE_PORTABLE_DIR/imageformats" - cp plugins/iconengines/qsvgicon.dll "$ABSOLUTE_PORTABLE_DIR/iconengines" - cp plugins/platforms/qwindows.dll "$ABSOLUTE_PORTABLE_DIR/platforms" - cp plugins/styles/qwindowsvistastyle.dll "$ABSOLUTE_PORTABLE_DIR/styles" - cp plugins/printsupport/windowsprintersupport.dll "$ABSOLUTE_PORTABLE_DIR/printsupport" - for f in qgif qicns qico qjpeg qsvg qtga qtiff qwbmp; do cp plugins/imageformats/$f.dll "$ABSOLUTE_PORTABLE_DIR/imageformats"; done - - - name: Prepare portable distro (Deps) - shell: bash - run: | - cd ../lib - cp *.dll "$ABSOLUTE_PORTABLE_DIR" - - - name: Determine SQLiteStudio version - shell: bash - run: | - cd $ABSOLUTE_PORTABLE_DIR - echo "SQLITESTUDIO_VERSION=$(./sqlitestudiocli.exe --version | cut -f 2 -d ' ')" >> $GITHUB_ENV - - - name: Assemble portable package - shell: bash - run: | - cd $ABSOLUTE_PORTABLE_DIR/.. - 7z a -r sqlitestudio-$SQLITESTUDIO_VERSION.zip SQLiteStudio - - - name: Upload package artifact - uses: actions/upload-artifact@v1 - with: - name: sqlitestudio-${{ env.SQLITESTUDIO_VERSION }}.zip - path: output/portable/sqlitestudio-${{ env.SQLITESTUDIO_VERSION }}.zip diff --git a/.gitignore b/.gitignore index 25d9429..acc0476 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ **/.qmake.stash **/qrc_* SQLiteStudio3/SQLiteStudio3.pro.user.4.8-pre1 +SQLiteStudio-installer.xml.backup diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1779ae5..0000000 --- a/.travis.yml +++ /dev/null @@ -1,49 +0,0 @@ -language: cpp - -compiler: gcc - -before_install: - - sudo add-apt-repository -y ppa:beineri/opt-qt-5.10.1-xenial - - sudo apt-get update -qq - - sudo apt-get remove sqlite3 libsqlite3-0 - - sudo apt-get purge sqlite3 libsqlite3-0 - - sudo apt-get install -qq mesa-common-dev libgl1-mesa-dev qt510base qt510imageformats qt510script qt510svg qt510tools - - mkdir deps - - cd deps - - wget https://www.dropbox.com/s/y2emwb4jjeethlm/sqlite-2.8.17.tar.gz?dl=1 -O sqlite-2.8.17.tar.gz - - tar xzf sqlite-2.8.17.tar.gz - - cd sqlite-2.8.17 - - ./configure - - make - - sudo make install - - sqlite -version || true - - cd .. - - wget http://sqlite.org/2018/sqlite-autoconf-3230100.tar.gz - - tar xzf sqlite-autoconf-3230100.tar.gz - - cd sqlite-autoconf-3230100 - - ./configure --enable-fts5 --enable-json1 --enable-session - - make - - sudo make install - - sqlite3 --version - - cd .. - - sudo apt-get install -qq libtcl8.6 tcl8.6-dev - -before_script: - - mkdir -p ../output/build/Plugins - - . /opt/qt510/bin/qt510-env.sh - - qmake --version - -script: - - cd ../output/build - - test $TRAVIS_BRANCH = "master" && qmake DEFINES+=tests CONFIG+=debug ../../SQLiteStudio3 || true - - test $TRAVIS_BRANCH != "master" && qmake CONFIG+=portable ../../SQLiteStudio3 || true - - make -j 2 - - cd Plugins - - test $TRAVIS_BRANCH = "master" && qmake CONFIG+=debug ../../../Plugins || true - - test $TRAVIS_BRANCH != "master" && qmake CONFIG+=portable ../../../Plugins || true - - make -j 2 - - test $TRAVIS_BRANCH = "master" && cd ../../SQLiteStudio || true - - test $TRAVIS_BRANCH = "master" && ls -l || true - - test $TRAVIS_BRANCH = "master" && export LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH" || true - - test $TRAVIS_BRANCH = "master" && for f in tst_*; do ./$f || break 0; done || true - diff --git a/ChangeLog.md b/ChangeLog.md index c4d8433..a989352 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,163 @@ # ChangeLog +### 3.4.4 +- ADDED: #3488 SQLite ICU extension is boundled into binary packages (thanks to @tuffnatty). +- ADDED: #4697 Added a 'Restore original hotkey' buttons at keyboard shortcuts page of configuration dialog. +- ADDED: Added sqlar extension to the default set of extensions provided with binary packages. +- CHANGE: #4689 Sorting order is not cleared when table data is refreshed. To removed sorting order user can either double-click on header (once or twice) to restore no sorting for a column, or use context menu to clear any sorting. +- CHANGE: #4735 Order of functions loading has changed. Now it's: built-in, extension-provided, user-defined. The last loaded has the highest precedense. +- CHANGE: #4732 SQLite updated to 3.41.2 (except for SQLCipher, which is still on 3.39.4 at the moment). +- CHANGE: #4683 Custom syntax highlighting colors now use theme-based colors by default (to work well with dynamically changing color palette in night/day theme changes) and can be explicitly enabled in configuration. +- BUGFIX: #4676 #4716 #4693 #4701 #4669 #4721 Few critical fixes for Query Executor, fixing a frequent error: near ")": syntax error. +- BUGFIX: #4685 Fixed WINDOW/OVER/FILTER keyword handling in the SQL parser. +- BUGFIX: #4694 Fixed messages for toolbar buttons in Code Snippets window & fixed loading translations for plugins. +- BUGFIX: #4698 Fixed handling object names with '{' and '}' in their names. +- BUGFIX: #4681 Fixed risk of null reference call in the SqliteOrderBy. +- BUGFIX: #4707 Fixed database file selection dialog, so it doesn't complaint about read-only files. +- BUGFIX: #4679 Fixed issue with case sensivity of CTE alias. +- BUGFIX: #4697 Changed default hotkey for rolling back pending changes in table data from Ctrl+Backspace to Alt+Shit+Backspace. Default hotkey affects new users and will not change automatically for those who upgrade from older SQLiteStudio. +- BUGFIX: #4739 Fixed committing changes in Extensions Manager if modifying valid extension settings, while there is another invalid, yet untouched extension on the list. +- BUGFIX: #4715 Fixed the UPDATE OF column popup on MacOSX. +- BUGFIX: #4754 Fixed Windows uninstaller to not create separate uninstall entries in Windows registry per each SQLiteStudio version and added SQLiteStudio icon for the uninstall entry in Windows Add/Remove programs. +- BUGFIX: Tcl plugin compilation improved on Ubuntu 18.04 (thanks to @tuffnatty). + +### 3.4.3 +- CHANGE: #4631 Linux binaries are built again back on Ubuntu 20.04 (instead of 22.04) to make it compatible for more Linux machines. +- BUGFIX: #4660 Removed unnecessary linking to curses library in Linux build file. +- BUGFIX: #4658 Fixed crash when SQL parser stack gets overflown. + +### 3.4.2 +- ADDED: #4653 For dealing with small fractional numbers there is an option now to change Grid View representation of these numbers to a scientific notation - the option is in Configuration/Data Browsing. +- CHANGE: #4535 Improved current query highlighter, so it considers query on the left of the cursor as current in a more intuitive way. +- BUGFIX: #4602 Fixed editing data in table with Russian upper-case name. +- BUGFIX: #4105 Fixed Import Dialog state update when changing import source (CSV vs RegExp), which could cause inability to proceed with the import in some cases. +- BUGFIX: #3767 Fixed copying objects between unencrypted and encrypted databases, also fixed copying tables with generated columns. +- BUGFIX: #4422 Portable configuration also prevents SQLiteStudio from touching Windows registry. +- BUGFIX: #3759 Fixed using independent SQLiteStudio instances by different Unix users simultaneously, regardless of one's settings. +- BUGFIX: #4198 When copying/moving table with name-colliding index (or trigger) to another database, the name conflict resolution now actually works. +- BUGFIX: #3541 Standardized commit/rollback hotkeys for: functions editor, extensions editor, collations editor, snippets editor, table window structure tab, view window query tab. +- BUGFIX: #4607 Fixed bug in Query Executor, that caused selecting ROWID from a View, without expanding the view in the first place, thus causing smart execution to fail. +- BUGFIX: #4607 Fixed Query Executor to apply column sorting correctly even in Simple Execution method. +- BUGFIX: #4614 Fixed opening object links in SQL editor to be case-insensitive. +- BUGFIX: #4618 Fixed query executor when using filer on a View. +- BUGFIX: #4613 Fixed per-column filter behavior when clearing single filter using the in-line erase button. +- BUGFIX: #4613 Fixed per-column filter behavior when jumping between subsequent filter inputs, so it does not apply filter for each single input, but rather waits for user Enter or Return key to be hit. Also not applying filter immediately when clicking the in-line erase button. +- BUGFIX: #4624 Fixed (optimized) "Replace All" function in SQL Editor, reducing the "Replace All" action time from 30 seconds to 30 milliseconds in case of 380 occurrences. +- BUGFIX: #4622 Fixed Form View to open at the same row that is selected in the Grid View. +- BUGFIX: #4616 Fixed font refreshing in Grid View when changing font size with ctrl+wheel. +- BUGFIX: #4562 Fixed Config Dialog to properly show loaded/unloaded plugins in case of dependant plugins. +- BUGFIX: #4634 Added workaround for handling qt5ct style from Qt configuration utility. +- BUGFIX: #4548 Automatic indexes are now marked correctly as unique and the Index Dialog is read-only for them. +- BUGFIX: #4637 Fixed Bind Parameters dialog to apply initially a reasonable size of input editors. +- BUGFIX: #4384 Fixed DDL modifications of Foreign Key referencing table, so the FK constraint is not retained if referenced column was dropped. +- BUGFIX: #4636 Fixed importing CSV with multi-word column names in the header line of CSV. +- BUGFIX: #4365 Fixed support for triggers with same name as tables in the same database (which is allowed by SQLite). +- BUGFIX: #4641 Fixed a serious bug with filtering data grid view with more complex queries/views, including expression-based column names with multi-word aliases. +- BUGFIX: #4272 Fixed undesired code assistant after starting application with SQL Editor contents suggesting auto completion. +- BUGFIX: #4643 Fixed executing query with apostrophe (doubled) inside of a string literal value. +- BUGFIX: #4546 Fixed exporting results of a query that is a huge SQL statement. +- BUGFIX: #4642 Fixed crash when removing a last database from the application and then modifying contents of SQL Editor. +- BUGFIX: #4064 Fixed support for ":memory:" database. +- BUGFIX: #4254 Fixed few issues with CLI help messages. +- BUGFIX: #4639 Fixed typo. +- BUGFIX: Fixed refreshing invalid database state if edited & fixed its connection options. + +### 3.4.1 +- CHANGE: #4585 SQLite updated to 3.40.0 (except for SQLCipher, which has latest version 3.39.2). +- BUGFIX: #4600 #4587 Fixed very slow loading of data in tables with foreign keys. +- BUGFIX: #4549 Query executor column aliases do not need wrapping anymore, because parser in 3.4.0 does not strip wrapped aliases anymore. +- BUGFIX: #4219 Removed duplicated UI config entries generated by Printing plugin. +- BUGFIX: #4563 Installer now runs the application at final step as a regular user even if running as root. +- BUGFIX: Fixed icon path for Linux installer, so that SQLiteStudio shows with the icon in Linux application list. +- BUGFIX: #4550 Fixed the Test Connection button in Database Dialog. +- BUGFIX: #4569 Fixed casual application crash on database disconnection. +- BUGFIX: #4529 Fixed application freeze when creating table with ICU collation. +- BUGFIX: #4586 Fixed unloading & loading SQLCipher plugin during single run of application. +- BUGFIX: #4577 Fixed SSL handshake issue under Linux during updates checking. +- BUGFIX: #4590 Fixed TLS issue under Windows. +- BUGFIX: #4364 Fixed table modification script in case all existing columns of a table are deleted, but the table remains and then new columns are added. +- BUGFIX: #4098 Fixed disappearing table (not really, just in UI) when dragging it and dropping onto SQL Editor. +- BUGFIX: #4594 Initial (default) colors of current line/query in SQL Editor is fixed to not be just black. +- BUGFIX: #4140 #4343 Disabling Foreign Keys during execution SQL directly from file. +- BUGFIX: #4593 Fixed row/column (de)selection on header ctrl+click. +- BUGFIX: #4596 Fixed thread-safety issue with QtScripting (JavaScript) built-in plugin. + +### 3.4.0 +- ADDED: #4058 Added support for RETURNING syntax in INSERT/DELETE/UPDATE and MATERIALIZED syntax in CTE - introduced in SQLite 3.35. +- ADDED: #4058 Added support for STRICT tables introduced in SQLite 3.37. +- ADDED: #4058 Added support for -> and ->> operators from SQLite 3.38. +- ADDED: #4058 Added support for RIGHT, FULL OUTER JOIN, IS (NOT) DISTINCT FROM operators that were introduced in SQLite 3.39. +- ADDED: #513 Python scripting plugin added, along side with Python syntax highlighting plugin. Requires Python installed in the system to use the plugin. +- ADDED: #4050 Functions Editor allows marking functions as deterministic (thus using them for GENERATED columns). +- ADDED: #3615 console.log() function for JS scripts. +- ADDED: #3503 #3963 SQL Editor has word wrapping option in context menu. It's also available in the configuration dialog. +- ADDED: #4087 The installer packages are back (powered by VMware InstallBuilder), with proper "Start Menu" entries and SQLite file associations - across all 3 platforms! +- ADDED: #3291 Filtering in View window (thanks to @electrickite). +- ADDED: #4103 Windows 32-bit binaries, alongside with 64-bit. With version 3.3.0 Windows binaries were upgraded to 64 bit, but it turns out many people still need 32 bit binaries. +- ADDED: #3715 Dedicated hotkeys to execute either single query under cursor or all queries in editor, regardless of default execution mode configured. Default hotkeys: Ctrl+F9 (or Cmd+F9) for single query, Shift+F9 for all queries. +- ADDED: #3714 Added highlighter for current query (under cursor, to be executed), to make the user aware which query is actually going to be executed when Execute Query hotkey is used. +- ADDED: #4006 Colors configuration for syntax highlighting is reintroduced in a new, improved form. +- ADDED: #3793 Close windows on the left/right options added to the View menu and Taskbar context menu. Also renamed 'all but selected' to 'other'. +- ADDED: #4065 Code Snippets - define in dedicated window, use it from the code assistant (hit the assistant hotkey twice). +- ADDED: #4402 Added context menu option to adjust height of rows according to their contents. Also added single-row adjusting by double-clicking on the row header section on the left. +- ADDED: #3942 Added option to SQL Export plugin to add IF NOT EXISTS clause to CREATE statements. +- ADDED: #4249 Added tooltips for WITHOUT ROWID and STRICT checkboxes in the Table Window. +- ADDED: #4264 Added svg icon to portable packages. +- ADDED: #3439 Foreign Key dropdown is now available not only in the Grid View, but also in the Form View and the modal Value Editor dialog. +- ADDED: #3779 Constraint icons on the Structure tab of a Table Window now shows tooltips (on mouse hover) with details of the constraint. +- ADDED: #3956 Added button for clearing DDL history in the DDL History window. +- ADDED: #4024 Added feature to the CLI app for executing SQL files directly from the command line, using all SQLiteStudio query executor features. +- ADDED: #3515 Added option in CSV Import plugin to treat quotation character (") as regular character. +- ADDED: #4092 Added possibility to increase/decrease font size using Ctrl+MouseWheel or by (configurable) shortcuts Ctrl++ & Ctrl+-. Applicable for SQL Editor, Database List, Status Field, Data View. +- ADDED: #4217 Added the Invert Selection entry in data grid context menu. +- ADDED: #4096 Added data grid option for filtering by strict text (not just whether contains, but if actually equals). Old filtering options remain. +- ADDED: Allow drag and drop a file to the add database dialog. +- CHANGE: #4058 SQLite updated to 3.39.4 (except for SQLCipher, which has latest version 3.39.2). +- CHANGE: #3272 Named function parameters of Custom SQL functions are now passed to script code as named variables. +- CHANGE: #3337 QtScript (deprecated module) usage migrated to QML module, using QJSEngine, the EcmaScript compliant implementation. Also changed plugin language name from QtScript to JavaScript and icon from Qt icon to JS icon. +- CHANGE: #2963 Application state (session) is saved (apart from normal application exit) whenever the state changes and also during critical application crash. +- CHANGE: #4083 Added support for regexp literals and template literals in JS syntax highlighter. +- CHANGE: #4071 #3437 Data column width has now more configuration options, so that it can retain its width upon entered value, or to have header contents (column name) visible. +- CHANGE: Finalized transition to new model of translation files (using Crowdin). Generation of qm files and updating qrc files is now fully automated. +- CHANGE: #4300 Db Dialog now has just 1 browse button, but uses Qt file dialog, not native one. +- CHANGE: Unused the help button in title bar are removed. +- CHANGE: #4273 Newer opened database is selected by default in the tree. +- CHANGE: #4240 #4458 #4419 #4129 Removed mechanism that limited initial value loaded to a cell to keep the amount of memory used at safe level. While it served it purpose well, it introduced so many other issues (refer to GitHub issues mentioned for this ChangeLog item), that it was not worth it. If a user keeps lots of gigabytes in a single table and you plan to query it, the user may want to limit amount of rows you query or display at once. +- CHANGE: #4435 Changed data griv view behavior, so when user clicks once, it will select column/row and if double-clicks, the sorting order is applied. +- CHANGE: #4088 Changed F2 hotkey, so it enters inline editing of a cell (no longer it opens the config dialog, which now is under F10 key). View Window, Collation Editor, Functions Editor, and Extensions Editor got now Ctrl+S hotkey to commit pending changes. +- CHANGE: #4341 When testing or accepting database connection in Database Dialog, in case of error, the message is displayed in status field properly, but also shows up in tooltip of the connection error icon. +- CHANGE: #3439 Compact Layout (that reduces many margins to save working space) is now excluded from the Form View, as it made it only worse. +- CHANGE: #4538 Linux binaries are now built onUbuntu 22.04 (used to be 20.04). +- BUGFIX: #4218 Fixed mnemonics ampersand displayed in toolbar button tooltips (they are used for relevant menu items, but in toolbar they caused additional ampersand to be displayed). +- BUGFIX: #4095 Fixed "per column" filter to apply updated values upon leaving filter input, or resetting it. +- BUGFIX: #4113 Fixed importing from data sources having less columns than in the target table. +- BUGFIX: #4207 Fixed crash when populating table with either Random Numbers or Random Text. Big thanks to @jiangzc! +- BUGFIX: #4093 Fixed foreign key dropdown to execute query asynchronously (and not to block UI for that time) and to not overwrite randomly value that user already typed. +- BUGFIX: #4177 Fixed Populate Table dialog when there is only one table in the database. +- BUGFIX: #4148 Fixed selection loss when disconnecting from database. +- BUGFIX: #4084 Fixed View window refreshing when a dependant table was modified. +- BUGFIX: Fixed error messages in debug console when executing query with less columns in results than the previously executed query. +- BUGFIX: Fixed WITHOUT ROWID checkbox updating when table structure changes are rolled back. +- BUGFIX: Fixed updating data values for a single PK column in WITHOUT ROWID table. +- BUGFIX: Fixed a bug when a style plugin was missing and style was in use - the config window was being confusedabout it. +- BUGFIX: #4267 Path now shows familiar backslashes on Windows for Add a database and About dialog. +- BUGFIX: #4314 Fixed missing OpenSSL license exemptions. +- BUGFIX: #4433 Fixed loading translations under Linux. +- BUGFIX: #4447 Fixed AUTOINCREMENT generation of DDL statement for table-level PRIMARY KEY. +- BUGFIX: #4418 Fixed current editor window deselection after dropping table/view with a query in that editor, which (that table/view) was open in another window, that got closed with the DROP statement. +- BUGFIX: #4386 Fixed handling database password with an apostrophe. +- BUGFIX: #4369 WAL mode is now handled properly upon application quit or database disconnection. +- BUGFIX: #4340 Fixed crash when populating two columns with random text. +- BUGFIX: #4362 #4363 #4239 Fixed handling object names (i.e. tables) with a usual wrapping characters being part of their name, for example table named [someTable]. +- BUGFIX: #4356 Fixed resolving column names by SelectResolver in rare cases, when the column name is undefined and should be left for SQLite engine to determin. +- BUGFIX: #4331 Fixed handling object names with # and $ inside. +- BUGFIX: #4306 Fixed parsing BLOB literals and formatting it back to SQL statements from internal AST. +- BUGFIX: #4278 Fixed language dropdown being empty sometimes when English language was selected. +- BUGFUX: #4545 Fixed Dead Keys support under Linux. +- BUGFIX: #4529 Fixed cooperation with collations defined by ICU extension. +- BUGFIX: #4534 Configuration Dialog categories list width is now initially adjusted to fit contents. +- BUGFIX: Schema changes are now properly refreshed in SQL Editors to highlight database object names. + ### 3.3.3 - CHANGE: #4011 SQLite updated to 3.35.4. SQLite3MultipleCiphers updated to 1.2.4 (SQLite 3.35.4). This enables math functions from 3.35. SQLCipher updated to 4.4.3, which is only SQLite 3.34.1, so no math functions for SQLCipher. - CHANGE: #4028 Open databases are now expanded upon startup only if they were expanded during application shutdown. @@ -35,7 +193,7 @@ - ADDED: #3975 Database dropdowns added to table & view windows, to it's more clear to which database particular object belongs. The same database dropdown in SQL Editor is moved to the left side, to be more consistent across the application. Main toolbar ha snow way less unnecessary buttons. - CHANGE: Windows binaries are now 64-bit. - CHANGE: SQLite2 plugin removed due to harder and harder maintenance of SQLite2 library compilation. -- CHANGE: Minumum Qt version now is 5.12.0. +- CHANGE: Minimum Qt version now is 5.12.0. - CHANGE: #3470 SQLite upgraded to 3.34.1. From now on the core SQLite3 library is bounded together with application (no longer a separate library). - CHANGE: #3494 DbSqliteCipher (SQLCipher) upgraded to 4.4.2 (SQLite 3.34.0). - CHANGE: #3954 DbSqliteWx plugin migrated to SQLite3MultipleCiphers 1.1.4 (SQLite 3.34.1). @@ -1362,4 +1520,4 @@ - BUGFIX: DDL formatting in table window fixed. - BUGFIX: Fixed critical error while exporting some data as a XML. - BUGFIX: Fixed critical error while some error occured when trying to create trigger. -- BUGFIX: Fixed critical errors while customizing toolbar. \ No newline at end of file +- BUGFIX: Fixed critical errors while customizing toolbar. diff --git a/Plugins/ConfigMigration/ConfigMigration.pro b/Plugins/ConfigMigration/ConfigMigration.pro index 9bc96c3..6855ceb 100644 --- a/Plugins/ConfigMigration/ConfigMigration.pro +++ b/Plugins/ConfigMigration/ConfigMigration.pro @@ -31,16 +31,15 @@ RESOURCES += \ configmigration.qrc -TRANSLATIONS += ConfigMigration_ro_RO.ts \ - ConfigMigration_de.ts \ - ConfigMigration_it.ts \ - ConfigMigration_zh_CN.ts \ - ConfigMigration_sk.ts \ - ConfigMigration_ru.ts \ - ConfigMigration_pt_BR.ts \ - ConfigMigration_fr.ts \ - ConfigMigration_es.ts \ - ConfigMigration_pl.ts + + + + + + + + + diff --git a/Plugins/ConfigMigration/ConfigMigration_de.qm b/Plugins/ConfigMigration/ConfigMigration_de.qm deleted file mode 100644 index 0d9a495..0000000 Binary files a/Plugins/ConfigMigration/ConfigMigration_de.qm and /dev/null differ diff --git a/Plugins/ConfigMigration/ConfigMigration_de.ts b/Plugins/ConfigMigration/ConfigMigration_de.ts deleted file mode 100644 index e229e2c..0000000 --- a/Plugins/ConfigMigration/ConfigMigration_de.ts +++ /dev/null @@ -1,150 +0,0 @@ - - - - - ConfigMigration - - - A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. - - - - - Bug reports history (%1) - Fehlerprotokollverlauf (%1) - - - - Database list (%1) - Datenbankliste (%1) - - - - Custom SQL functions (%1) - Eigene SQL Funktionen (%1) - - - - SQL queries history (%1) - SQL-Abfragen Verlauf (%1) - - - - ConfigMigrationWizard - - - Configuration migration - - - - - Items to migrate - - - - - This is a list of items found in the old configuration file, which can be migrated into the current configuration. - - - - - Options - - - - - Put imported databases into separate group - - - - - Group name - - - - - Enter a non-empty name. - - - - - Top level group named '%1' already exists. Enter a group name that does not exist yet. - - - - - Could not open old configuration file in order to migrate settings from it. - - - - - Could not open current configuration file in order to migrate settings from old configuration file. - - - - - Could not commit migrated data into new configuration file: %1 - - - - - Could not read bug reports history from old configuration file in order to migrate it: %1 - - - - - Could not insert a bug reports history entry into new configuration file: %1 - - - - - Could not read database list from old configuration file in order to migrate it: %1 - - - - - Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not create containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not insert a database entry into new configuration file: %1 - - - - - Could not query for available order for next database in new configuration file in order to migrate the database list: %1 - - - - - Could not create group referencing the database in new configuration file: %1 - - - - - Could not read function list from old configuration file in order to migrate it: %1 - - - - - Could not read SQL queries history from old configuration file in order to migrate it: %1 - - - - - Could not read next ID for SQL queries history in new configuration file: %1 - - - - - Could not insert SQL history entry into new configuration file: %1 - - - - diff --git a/Plugins/ConfigMigration/ConfigMigration_es.qm b/Plugins/ConfigMigration/ConfigMigration_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/ConfigMigration/ConfigMigration_es.qm and /dev/null differ diff --git a/Plugins/ConfigMigration/ConfigMigration_es.ts b/Plugins/ConfigMigration/ConfigMigration_es.ts deleted file mode 100644 index 3854dd1..0000000 --- a/Plugins/ConfigMigration/ConfigMigration_es.ts +++ /dev/null @@ -1,150 +0,0 @@ - - - - - ConfigMigration - - - A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. - - - - - Bug reports history (%1) - - - - - Database list (%1) - - - - - Custom SQL functions (%1) - - - - - SQL queries history (%1) - - - - - ConfigMigrationWizard - - - Configuration migration - - - - - Items to migrate - - - - - This is a list of items found in the old configuration file, which can be migrated into the current configuration. - - - - - Options - - - - - Put imported databases into separate group - - - - - Group name - - - - - Enter a non-empty name. - - - - - Top level group named '%1' already exists. Enter a group name that does not exist yet. - - - - - Could not open old configuration file in order to migrate settings from it. - - - - - Could not open current configuration file in order to migrate settings from old configuration file. - - - - - Could not commit migrated data into new configuration file: %1 - - - - - Could not read bug reports history from old configuration file in order to migrate it: %1 - - - - - Could not insert a bug reports history entry into new configuration file: %1 - - - - - Could not read database list from old configuration file in order to migrate it: %1 - - - - - Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not create containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not insert a database entry into new configuration file: %1 - - - - - Could not query for available order for next database in new configuration file in order to migrate the database list: %1 - - - - - Could not create group referencing the database in new configuration file: %1 - - - - - Could not read function list from old configuration file in order to migrate it: %1 - - - - - Could not read SQL queries history from old configuration file in order to migrate it: %1 - - - - - Could not read next ID for SQL queries history in new configuration file: %1 - - - - - Could not insert SQL history entry into new configuration file: %1 - - - - diff --git a/Plugins/ConfigMigration/ConfigMigration_fr.qm b/Plugins/ConfigMigration/ConfigMigration_fr.qm deleted file mode 100644 index 452fe0d..0000000 Binary files a/Plugins/ConfigMigration/ConfigMigration_fr.qm and /dev/null differ diff --git a/Plugins/ConfigMigration/ConfigMigration_fr.ts b/Plugins/ConfigMigration/ConfigMigration_fr.ts deleted file mode 100644 index ce84df8..0000000 --- a/Plugins/ConfigMigration/ConfigMigration_fr.ts +++ /dev/null @@ -1,150 +0,0 @@ - - - - - ConfigMigration - - - A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. - Une configuration d’un ancien SQLiteStudio 2.x.x a été détectée. Voulez-vous migrer l’ancienne configuration pour la version courante ? <a href="%1">Cliquer ici pour l’exécuter</a>. - - - - Bug reports history (%1) - Historique des rappots de bug (%1) - - - - Database list (%1) - Liste des bases de données (%1) - - - - Custom SQL functions (%1) - Personnalisation des fonctions SQL (%1) - - - - SQL queries history (%1) - Historique des requêtes SQL (%1) - - - - ConfigMigrationWizard - - - Configuration migration - Migration de la configuration - - - - Items to migrate - Items à migrer - - - - This is a list of items found in the old configuration file, which can be migrated into the current configuration. - Voici la liste des items trouvés dans l’ancien fichier de configuration, pouvant être importés dans la configuration actuelle. - - - - Options - Options - - - - Put imported databases into separate group - Mettre les bases de données importées dans un groupe séparé - - - - Group name - Nom du groupe - - - - Enter a non-empty name. - Saisissez un nom valide. - - - - Top level group named '%1' already exists. Enter a group name that does not exist yet. - Le nom du groupe « %1 » existe déjà. Saissiez un nom de groupe non déjà utilisé. - - - - Could not open old configuration file in order to migrate settings from it. - Impossible d’ouvrir l’ancien fichier de configuration pour importer les préférences. - - - - Could not open current configuration file in order to migrate settings from old configuration file. - Impossible d’ouvrir l’actuel fichier de configuration pour importer les préférences de l’ancien fichier de configuration. - - - - Could not commit migrated data into new configuration file: %1 - Impossible d’enregistrer les données de migration dans le nouveau fichier de configuration : %1 - - - - Could not read bug reports history from old configuration file in order to migrate it: %1 - Impossible de lire l’historique du rapport de bug de l’ancienne configuration pour l’importée %1 - - - - Could not insert a bug reports history entry into new configuration file: %1 - Impossible d’insérer l’historique du rapport de bug dans le nouveau fichier de configuration : %1 - - - - Could not read database list from old configuration file in order to migrate it: %1 - Impossible de lire la liste des bases de données de l’ancien fichier de configuration : %1 - - - - Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 - Impossible d’exécuter la requête de tri de groupe dans le nouveau fichier de configuration pour importer la liste des bases de données : %1 - - - - Could not create containing group in new configuration file in order to migrate the database list: %1 - Impossible de créer un groupe dans le nouveau fichier de configuration pour migrer la liste des bases de données : %1 - - - - Could not insert a database entry into new configuration file: %1 - Impossible d’insérer le nom d’une base de données dans le nouveau fichier de configuration : %1 - - - - Could not query for available order for next database in new configuration file in order to migrate the database list: %1 - Impossible d’exécuter la requête de tri pour la base de données suivante dans le nouveau fichier de configuration pour importer la liste les bases de données : %1 - - - - Could not create group referencing the database in new configuration file: %1 - Impossible de créer un groupe référençant les bases de données dans le nouveau fichier de configuration : %1 - - - - Could not read function list from old configuration file in order to migrate it: %1 - Impossible de lire la liste de fonction de l’ancien fichier de configuration pour l’importer : %1 - - - - Could not read SQL queries history from old configuration file in order to migrate it: %1 - Impossible de lire l’historique des requêtes SQL de l’ancien fichier de configuration pour l’importer : %1 - - - - Could not read next ID for SQL queries history in new configuration file: %1 - Impossible de lire l’ID suivant de l’historique des requêtes dans le nouveau fichier de configuration : %1 - - - - Could not insert SQL history entry into new configuration file: %1 - Impossible d’insérer un historique d’SQL dans le nouveau fichier de configuration : %1 - - - diff --git a/Plugins/ConfigMigration/ConfigMigration_it.qm b/Plugins/ConfigMigration/ConfigMigration_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/ConfigMigration/ConfigMigration_it.qm and /dev/null differ diff --git a/Plugins/ConfigMigration/ConfigMigration_it.ts b/Plugins/ConfigMigration/ConfigMigration_it.ts deleted file mode 100644 index c71c354..0000000 --- a/Plugins/ConfigMigration/ConfigMigration_it.ts +++ /dev/null @@ -1,150 +0,0 @@ - - - - - ConfigMigration - - - A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. - - - - - Bug reports history (%1) - - - - - Database list (%1) - - - - - Custom SQL functions (%1) - - - - - SQL queries history (%1) - - - - - ConfigMigrationWizard - - - Configuration migration - - - - - Items to migrate - - - - - This is a list of items found in the old configuration file, which can be migrated into the current configuration. - - - - - Options - - - - - Put imported databases into separate group - - - - - Group name - - - - - Enter a non-empty name. - - - - - Top level group named '%1' already exists. Enter a group name that does not exist yet. - - - - - Could not open old configuration file in order to migrate settings from it. - - - - - Could not open current configuration file in order to migrate settings from old configuration file. - - - - - Could not commit migrated data into new configuration file: %1 - - - - - Could not read bug reports history from old configuration file in order to migrate it: %1 - - - - - Could not insert a bug reports history entry into new configuration file: %1 - - - - - Could not read database list from old configuration file in order to migrate it: %1 - - - - - Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not create containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not insert a database entry into new configuration file: %1 - - - - - Could not query for available order for next database in new configuration file in order to migrate the database list: %1 - - - - - Could not create group referencing the database in new configuration file: %1 - - - - - Could not read function list from old configuration file in order to migrate it: %1 - - - - - Could not read SQL queries history from old configuration file in order to migrate it: %1 - - - - - Could not read next ID for SQL queries history in new configuration file: %1 - - - - - Could not insert SQL history entry into new configuration file: %1 - - - - diff --git a/Plugins/ConfigMigration/ConfigMigration_pl.qm b/Plugins/ConfigMigration/ConfigMigration_pl.qm deleted file mode 100644 index b62b803..0000000 Binary files a/Plugins/ConfigMigration/ConfigMigration_pl.qm and /dev/null differ diff --git a/Plugins/ConfigMigration/ConfigMigration_pl.ts b/Plugins/ConfigMigration/ConfigMigration_pl.ts deleted file mode 100644 index f2ef5d8..0000000 --- a/Plugins/ConfigMigration/ConfigMigration_pl.ts +++ /dev/null @@ -1,152 +0,0 @@ - - - - - ConfigMigration - - - A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. - Wykryto ustawienia ze starej wersji SQLiteStudio 2.x.x. Czy chcesz zmigrować stare ustawienia do aktualnej wersji? <a href="%1">Kliknij, aby to zrobić</a>. - - - - Bug reports history (%1) - Historia zgÅ‚oszonych błędów (%1) - - - - Database list (%1) - Lista baz danych (%1) - - - - Custom SQL functions (%1) - WÅ‚asne funkcje SQL (%1) - - - - SQL queries history (%1) - Historia zapytaÅ„ SQL (%1) - - - - ConfigMigrationWizard - - - Configuration migration - Migracja ustawieÅ„ - - - - Items to migrate - Elementy do zmigrowania - - - - This is a list of items found in the old configuration file, which can be migrated into the current configuration. - To jest lista elementów znalezionych w starym pliku konfiguracyjnym, które mogÄ… być zmigrowane do aktualnych ustawieÅ„. - - - - Options - Opcje - - - - Put imported databases into separate group - Umieść zaimportowane bazy danych w osobnej grupie - - - - Group name - Nazwa grupy - - - - Enter a non-empty name. - Wprowadź niepustÄ… nazwÄ™. - - - - Top level group named '%1' already exists. Enter a group name that does not exist yet. - Grupa o nazwie '%1' istnieje już na najwyższym poziomie. Wprowadź nazwÄ™ grupy, która jeszcze nie istnieje. - - - - Could not open old configuration file in order to migrate settings from it. - Nie udaÅ‚o siÄ™ otworzyć starego pliku ustawieÅ„ w celu zmigtowania z niego ustawieÅ„. - - - - Could not open current configuration file in order to migrate settings from old configuration file. - Nie udaÅ‚o siÄ™ otworzyć aktualnego pliku ustawieÅ„ w celu zmigrowania do niego ustawieÅ„ ze starego pliku konfiguracyjnego. - - - - Could not commit migrated data into new configuration file: %1 - Nie udaÅ‚o siÄ™ zatwierdzenie zmigrowanych danych w nowym pliku ustawieÅ„: %1 - - - - Could not read bug reports history from old configuration file in order to migrate it: %1 - Nie udaÅ‚ siÄ™ odczyt historii zgÅ‚oszeÅ„ błędów ze starego pliku konfiguracyjnego w celu jej zmigrowania: %1 - - - - Could not insert a bug reports history entry into new configuration file: %1 - Nie udaÅ‚o siÄ™ dodać wpisów historii zgÅ‚oszeÅ„ błędów do nowego pliku ustawieÅ„: %1 - - - - Could not read database list from old configuration file in order to migrate it: %1 - Nie udaÅ‚o siÄ™ odczytać listy baz danych ze starego pliku ustawieÅ„ w celu jej zmigtowania: %1 - - - - Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 - Could query for available order for containing group in new configuration file in order to migrate the database list: %1 - Nie udaÅ‚o siÄ™ odpytać o dostÄ™pnÄ… kolejność dla grupy zawierajÄ…cej w nowym pliku ustawieÅ„, w celu zmigrowania listy baz danych: %1 - - - - Could not create containing group in new configuration file in order to migrate the database list: %1 - Nie udaÅ‚o siÄ™ stworzyć grupy w nowym pliku ustawieÅ„, w celu zmigrowania listy baz danych: %1 - - - - Could not insert a database entry into new configuration file: %1 - Nie udaÅ‚o siÄ™ dodać wpisu bazy danych do nowego pliku ustawieÅ„: %1 - - - - Could not query for available order for next database in new configuration file in order to migrate the database list: %1 - Could query for available order for next database in new configuration file in order to migrate the database list: %1 - Nie udaÅ‚o siÄ™ odpytać o dostÄ™pnÄ… kolejność dla nastÄ™pnej bazy w nowym pliku ustawieÅ„, w celu zmigrowania listy baz danych: %1 - - - - Could not create group referencing the database in new configuration file: %1 - Nie udaÅ‚o siÄ™ utworzyć grupy odwoÅ‚ujÄ…cej siÄ™ do bazy danych w nowym pliku ustawieÅ„: %1 - - - - Could not read function list from old configuration file in order to migrate it: %1 - Nie udaÅ‚o siÄ™ odczytać listy funkcji ze starego pliku ustawieÅ„ w celu jej zmigrowania: %1 - - - - Could not read SQL queries history from old configuration file in order to migrate it: %1 - Nie udaÅ‚o siÄ™ odczytać historii zapytaÅ„ SQL ze starego pliku ustawieÅ„ w celu zmigrowania jej: %1 - - - - Could not read next ID for SQL queries history in new configuration file: %1 - Nie udaÅ‚o siÄ™ odczytać nastÄ™pnego ID dla historii zapytaÅ„ SQL w nowym pliku ustawieÅ„: %1 - - - - Could not insert SQL history entry into new configuration file: %1 - Nie udaÅ‚o siÄ™ dodanie wpisu do historii SQL w nowym pliku ustawieÅ„: %1 - - - diff --git a/Plugins/ConfigMigration/ConfigMigration_pt_BR.qm b/Plugins/ConfigMigration/ConfigMigration_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/ConfigMigration/ConfigMigration_pt_BR.qm and /dev/null differ diff --git a/Plugins/ConfigMigration/ConfigMigration_pt_BR.ts b/Plugins/ConfigMigration/ConfigMigration_pt_BR.ts deleted file mode 100644 index fb23daf..0000000 --- a/Plugins/ConfigMigration/ConfigMigration_pt_BR.ts +++ /dev/null @@ -1,150 +0,0 @@ - - - - - ConfigMigration - - - A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. - - - - - Bug reports history (%1) - - - - - Database list (%1) - - - - - Custom SQL functions (%1) - - - - - SQL queries history (%1) - - - - - ConfigMigrationWizard - - - Configuration migration - - - - - Items to migrate - - - - - This is a list of items found in the old configuration file, which can be migrated into the current configuration. - - - - - Options - - - - - Put imported databases into separate group - - - - - Group name - - - - - Enter a non-empty name. - - - - - Top level group named '%1' already exists. Enter a group name that does not exist yet. - - - - - Could not open old configuration file in order to migrate settings from it. - - - - - Could not open current configuration file in order to migrate settings from old configuration file. - - - - - Could not commit migrated data into new configuration file: %1 - - - - - Could not read bug reports history from old configuration file in order to migrate it: %1 - - - - - Could not insert a bug reports history entry into new configuration file: %1 - - - - - Could not read database list from old configuration file in order to migrate it: %1 - - - - - Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not create containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not insert a database entry into new configuration file: %1 - - - - - Could not query for available order for next database in new configuration file in order to migrate the database list: %1 - - - - - Could not create group referencing the database in new configuration file: %1 - - - - - Could not read function list from old configuration file in order to migrate it: %1 - - - - - Could not read SQL queries history from old configuration file in order to migrate it: %1 - - - - - Could not read next ID for SQL queries history in new configuration file: %1 - - - - - Could not insert SQL history entry into new configuration file: %1 - - - - diff --git a/Plugins/ConfigMigration/ConfigMigration_ro_RO.qm b/Plugins/ConfigMigration/ConfigMigration_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/ConfigMigration/ConfigMigration_ro_RO.qm and /dev/null differ diff --git a/Plugins/ConfigMigration/ConfigMigration_ro_RO.ts b/Plugins/ConfigMigration/ConfigMigration_ro_RO.ts deleted file mode 100644 index 7360883..0000000 --- a/Plugins/ConfigMigration/ConfigMigration_ro_RO.ts +++ /dev/null @@ -1,150 +0,0 @@ - - - - - ConfigMigration - - - A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. - - - - - Bug reports history (%1) - - - - - Database list (%1) - - - - - Custom SQL functions (%1) - - - - - SQL queries history (%1) - - - - - ConfigMigrationWizard - - - Configuration migration - - - - - Items to migrate - - - - - This is a list of items found in the old configuration file, which can be migrated into the current configuration. - - - - - Options - - - - - Put imported databases into separate group - - - - - Group name - - - - - Enter a non-empty name. - - - - - Top level group named '%1' already exists. Enter a group name that does not exist yet. - - - - - Could not open old configuration file in order to migrate settings from it. - - - - - Could not open current configuration file in order to migrate settings from old configuration file. - - - - - Could not commit migrated data into new configuration file: %1 - - - - - Could not read bug reports history from old configuration file in order to migrate it: %1 - - - - - Could not insert a bug reports history entry into new configuration file: %1 - - - - - Could not read database list from old configuration file in order to migrate it: %1 - - - - - Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not create containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not insert a database entry into new configuration file: %1 - - - - - Could not query for available order for next database in new configuration file in order to migrate the database list: %1 - - - - - Could not create group referencing the database in new configuration file: %1 - - - - - Could not read function list from old configuration file in order to migrate it: %1 - - - - - Could not read SQL queries history from old configuration file in order to migrate it: %1 - - - - - Could not read next ID for SQL queries history in new configuration file: %1 - - - - - Could not insert SQL history entry into new configuration file: %1 - - - - diff --git a/Plugins/ConfigMigration/ConfigMigration_ru.qm b/Plugins/ConfigMigration/ConfigMigration_ru.qm deleted file mode 100644 index 57ef6f0..0000000 Binary files a/Plugins/ConfigMigration/ConfigMigration_ru.qm and /dev/null differ diff --git a/Plugins/ConfigMigration/ConfigMigration_ru.ts b/Plugins/ConfigMigration/ConfigMigration_ru.ts deleted file mode 100644 index 0337f58..0000000 --- a/Plugins/ConfigMigration/ConfigMigration_ru.ts +++ /dev/null @@ -1,150 +0,0 @@ - - - - - ConfigMigration - - - A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. - Обнаружена ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð¾Ñ‚ Ñтарой верÑии SQLiteStudio (2.x.x). Ð’Ñ‹ хотите перенеÑти Ñтарые наÑтройки в новую верÑию? <a href="%1">Ðажмите здеÑÑŒ Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа</a>. - - - - Bug reports history (%1) - ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¾Ñ‚Ñ‡Ñ‘Ñ‚Ð¾Ð² об ошибках (%1) - - - - Database list (%1) - СпиÑок баз данных (%1) - - - - Custom SQL functions (%1) - Произвольные функции SQL (%1) - - - - SQL queries history (%1) - ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов SQL (%1) - - - - ConfigMigrationWizard - - - Configuration migration - ÐŸÐµÑ€ÐµÐ½Ð¾Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¸ - - - - Items to migrate - ПереноÑимые Ñлементы - - - - This is a list of items found in the old configuration file, which can be migrated into the current configuration. - Это ÑпиÑок Ñлементов, обнаруженных в Ñтаром конфигурационном файле, которые могут быть перенеÑены в текущую конфигурацию. - - - - Options - Опции - - - - Put imported databases into separate group - ПомеÑтить импортированные базы данных в отдельную группу - - - - Group name - Ð˜Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ - - - - Enter a non-empty name. - Введите непуÑтое имÑ. - - - - Top level group named '%1' already exists. Enter a group name that does not exist yet. - Группа верхнего ÑƒÑ€Ð¾Ð²Ð½Ñ '%1' уже ÑущеÑтвует. Введите Ð¸Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹, которое ещё не занÑто. - - - - Could not open old configuration file in order to migrate settings from it. - Ðевозможно открыть Ñтарый файл конфигурации Ð´Ð»Ñ Ð¾ÑущеÑÑ‚Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа наÑтроек. - - - - Could not open current configuration file in order to migrate settings from old configuration file. - Ðевозможно открыть текущий файл конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа наÑтроек из Ñтарого файла конфигурации. - - - - Could not commit migrated data into new configuration file: %1 - Ðевозможно запиÑать перенеÑённые данные в новый файл конфигурации: %1 - - - - Could not read bug reports history from old configuration file in order to migrate it: %1 - Ðевозможно прочитать иÑторию отчётов об ошибках из Ñтарого файла конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа: %1 - - - - Could not insert a bug reports history entry into new configuration file: %1 - Ðевозможно вÑтавить иÑторию отчётов об ошибках в новый файл конфигурации: %1 - - - - Could not read database list from old configuration file in order to migrate it: %1 - Ðевозможно прочитать ÑпиÑок баз данных из Ñтарого файла конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа: %1 - - - - Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 - Ðевозможно запроÑить доÑтупное положение отдельной группы в новом файле конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа в неё ÑпиÑка баз данных: %1 - - - - Could not create containing group in new configuration file in order to migrate the database list: %1 - Ðевозможно Ñоздать отдельную группу в новом файле конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа в неё ÑпиÑка баз данных: %1 - - - - Could not insert a database entry into new configuration file: %1 - Ðевозможно вÑтавить Ñлемент ÑпиÑка баз данных в новый файл конфигурации: %1 - - - - Could not query for available order for next database in new configuration file in order to migrate the database list: %1 - Ðевозможно запроÑить доÑтупное положение Ð´Ð»Ñ Ñледующей базы данных в новом файле конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа ÑпиÑка баз данных: %1 - - - - Could not create group referencing the database in new configuration file: %1 - Ðевозможно Ñоздать группу, ÑÑылающуюÑÑ Ð½Ð° базу данных в новом файле конфигурации: %1 - - - - Could not read function list from old configuration file in order to migrate it: %1 - Ðевозможно прочитать ÑпиÑок функций из Ñтарого файла конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа: %1 - - - - Could not read SQL queries history from old configuration file in order to migrate it: %1 - Ðевозможно прочитать иÑторию запроÑов SQL из Ñтарого файла конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа: %1 - - - - Could not read next ID for SQL queries history in new configuration file: %1 - Ðевозможно Ñчитать Ñледующий ID Ð´Ð»Ñ Ð¸Ñтории запроÑов SQL в новом файле конфигурации: %1 - - - - Could not insert SQL history entry into new configuration file: %1 - Ðевозможно вÑтавить Ñлемент иÑтории запроÑов SQL в новый файл конфигурации: %1 - - - diff --git a/Plugins/ConfigMigration/ConfigMigration_sk.qm b/Plugins/ConfigMigration/ConfigMigration_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/Plugins/ConfigMigration/ConfigMigration_sk.qm and /dev/null differ diff --git a/Plugins/ConfigMigration/ConfigMigration_sk.ts b/Plugins/ConfigMigration/ConfigMigration_sk.ts deleted file mode 100644 index ffe6eef..0000000 --- a/Plugins/ConfigMigration/ConfigMigration_sk.ts +++ /dev/null @@ -1,150 +0,0 @@ - - - - - ConfigMigration - - - A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. - - - - - Bug reports history (%1) - - - - - Database list (%1) - - - - - Custom SQL functions (%1) - - - - - SQL queries history (%1) - - - - - ConfigMigrationWizard - - - Configuration migration - - - - - Items to migrate - - - - - This is a list of items found in the old configuration file, which can be migrated into the current configuration. - - - - - Options - - - - - Put imported databases into separate group - - - - - Group name - - - - - Enter a non-empty name. - - - - - Top level group named '%1' already exists. Enter a group name that does not exist yet. - - - - - Could not open old configuration file in order to migrate settings from it. - - - - - Could not open current configuration file in order to migrate settings from old configuration file. - - - - - Could not commit migrated data into new configuration file: %1 - - - - - Could not read bug reports history from old configuration file in order to migrate it: %1 - - - - - Could not insert a bug reports history entry into new configuration file: %1 - - - - - Could not read database list from old configuration file in order to migrate it: %1 - - - - - Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not create containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not insert a database entry into new configuration file: %1 - - - - - Could not query for available order for next database in new configuration file in order to migrate the database list: %1 - - - - - Could not create group referencing the database in new configuration file: %1 - - - - - Could not read function list from old configuration file in order to migrate it: %1 - - - - - Could not read SQL queries history from old configuration file in order to migrate it: %1 - - - - - Could not read next ID for SQL queries history in new configuration file: %1 - - - - - Could not insert SQL history entry into new configuration file: %1 - - - - diff --git a/Plugins/ConfigMigration/ConfigMigration_zh_CN.qm b/Plugins/ConfigMigration/ConfigMigration_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/Plugins/ConfigMigration/ConfigMigration_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/Plugins/ConfigMigration/ConfigMigration_zh_CN.ts b/Plugins/ConfigMigration/ConfigMigration_zh_CN.ts deleted file mode 100644 index 249e493..0000000 --- a/Plugins/ConfigMigration/ConfigMigration_zh_CN.ts +++ /dev/null @@ -1,150 +0,0 @@ - - - - - ConfigMigration - - - A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. - - - - - Bug reports history (%1) - - - - - Database list (%1) - - - - - Custom SQL functions (%1) - - - - - SQL queries history (%1) - - - - - ConfigMigrationWizard - - - Configuration migration - - - - - Items to migrate - - - - - This is a list of items found in the old configuration file, which can be migrated into the current configuration. - - - - - Options - - - - - Put imported databases into separate group - - - - - Group name - - - - - Enter a non-empty name. - - - - - Top level group named '%1' already exists. Enter a group name that does not exist yet. - - - - - Could not open old configuration file in order to migrate settings from it. - - - - - Could not open current configuration file in order to migrate settings from old configuration file. - - - - - Could not commit migrated data into new configuration file: %1 - - - - - Could not read bug reports history from old configuration file in order to migrate it: %1 - - - - - Could not insert a bug reports history entry into new configuration file: %1 - - - - - Could not read database list from old configuration file in order to migrate it: %1 - - - - - Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not create containing group in new configuration file in order to migrate the database list: %1 - - - - - Could not insert a database entry into new configuration file: %1 - - - - - Could not query for available order for next database in new configuration file in order to migrate the database list: %1 - - - - - Could not create group referencing the database in new configuration file: %1 - - - - - Could not read function list from old configuration file in order to migrate it: %1 - - - - - Could not read SQL queries history from old configuration file in order to migrate it: %1 - - - - - Could not read next ID for SQL queries history in new configuration file: %1 - - - - - Could not insert SQL history entry into new configuration file: %1 - - - - diff --git a/Plugins/ConfigMigration/configmigration.cpp b/Plugins/ConfigMigration/configmigration.cpp index 8d75e37..fe79062 100644 --- a/Plugins/ConfigMigration/configmigration.cpp +++ b/Plugins/ConfigMigration/configmigration.cpp @@ -17,7 +17,7 @@ ConfigMigration::ConfigMigration() bool ConfigMigration::init() { - Q_INIT_RESOURCE(configmigration); + SQLS_INIT_RESOURCE(configmigration); loadTranslation("ConfigMigration"); if (cfg.CfgMigration.Migrated.get()) @@ -47,7 +47,7 @@ bool ConfigMigration::init() void ConfigMigration::deinit() { - Q_CLEANUP_RESOURCE(configmigration); + SQLS_CLEANUP_RESOURCE(configmigration); safe_delete(db); for (ConfigMigrationItem* item : itemsToMigrate) @@ -77,7 +77,7 @@ QString ConfigMigration::findOldConfig() if (checkOldDir(dirPath, output)) return output; - if (getDistributionType() == DistributionType::OSX_BOUNDLE) + if (getDistributionType() == DistributionType::OSX_BUNDLE) { // Portable path 4 check dirPath = QCoreApplication::applicationDirPath() + "/../../sqlitestudio-cfg"; diff --git a/Plugins/ConfigMigration/configmigration.qrc b/Plugins/ConfigMigration/configmigration.qrc index 6e46607..9df7d5e 100644 --- a/Plugins/ConfigMigration/configmigration.qrc +++ b/Plugins/ConfigMigration/configmigration.qrc @@ -2,19 +2,4 @@ config_migration.png - - ConfigMigration_ro_RO.qm - ConfigMigration_de.qm - - - ConfigMigration_pl.qm - ConfigMigration_ru.qm - ConfigMigration_fr.qm - ConfigMigration_sk.qm - ConfigMigration_zh_CN.qm - - - - - diff --git a/Plugins/ConfigMigration/translations/ConfigMigration.ts b/Plugins/ConfigMigration/translations/ConfigMigration.ts new file mode 100644 index 0000000..058f44b --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + + Bug reports history (%1) + + + + + Database list (%1) + + + + + Custom SQL functions (%1) + + + + + SQL queries history (%1) + + + + + ConfigMigrationWizard + + + Configuration migration + + + + + Items to migrate + + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + + Options + + + + + Put imported databases into separate group + + + + + Group name + + + + + Enter a non-empty name. + + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + + Could not open old configuration file in order to migrate settings from it. + + + + + Could not open current configuration file in order to migrate settings from old configuration file. + + + + + Could not commit migrated data into new configuration file: %1 + + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + + Could not insert a bug reports history entry into new configuration file: %1 + + + + + Could not read database list from old configuration file in order to migrate it: %1 + + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + + Could not insert a database entry into new configuration file: %1 + + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + + Could not create group referencing the database in new configuration file: %1 + + + + + Could not read function list from old configuration file in order to migrate it: %1 + + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + + Could not read next ID for SQL queries history in new configuration file: %1 + + + + + Could not insert SQL history entry into new configuration file: %1 + + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_af_ZA.ts b/Plugins/ConfigMigration/translations/ConfigMigration_af_ZA.ts new file mode 100644 index 0000000..9b933ac --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_af_ZA.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_ar_SA.ts b/Plugins/ConfigMigration/translations/ConfigMigration_ar_SA.ts new file mode 100644 index 0000000..24c1010 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_ar_SA.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_ca_ES.ts b/Plugins/ConfigMigration/translations/ConfigMigration_ca_ES.ts new file mode 100644 index 0000000..b0238dc --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_ca_ES.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_cs_CZ.ts b/Plugins/ConfigMigration/translations/ConfigMigration_cs_CZ.ts new file mode 100644 index 0000000..31abfaa --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_cs_CZ.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_da_DK.ts b/Plugins/ConfigMigration/translations/ConfigMigration_da_DK.ts new file mode 100644 index 0000000..4587c25 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_da_DK.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + En konfiguration fra gamle SQLiteStudio 2.x.x er blevet fundet. Vil du migrere gamle indstillinger til den aktuelle version? <a href="%1">Klik her for at gøre det</a>. + + + + Bug reports history (%1) + Fejlrapport historik (%1) + + + + Database list (%1) + Database liste (%1) + + + + Custom SQL functions (%1) + Brugerdefinerede SQL funktioner (%1) + + + + SQL queries history (%1) + SQL forespørgsel historik (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Konfiguration af migration + + + + Items to migrate + Elementer at migrere + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + Dette er en liste over elementer fundet i den gamle konfigurationsfil, som kan migreres til den aktuelle konfiguration. + + + + Options + Indstillinger + + + + Put imported databases into separate group + Indsæt importerede databaser i separat gruppe + + + + Group name + Gruppenavn + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_de_DE.ts b/Plugins/ConfigMigration/translations/ConfigMigration_de_DE.ts new file mode 100644 index 0000000..c46547e --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_de_DE.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + Eine alte SQLiteStudio 2.x.x Konfiguration wurde erkannt. Möchten Sie die alten Einstellungen in die aktuelle Version migrieren? <a href="%1">Klicken Sie hier, um das zu tun</a>. + + + + Bug reports history (%1) + Fehlerprotokollverlauf (%1) + + + + Database list (%1) + Datenbankliste (%1) + + + + Custom SQL functions (%1) + Benutzerdefinierte SQL-Funktionen (%1) + + + + SQL queries history (%1) + SQL-Abfragen Verlauf (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Konfigurations-Migration + + + + Items to migrate + Zu migrierende Objekte + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + Das ist eine Liste von Elementen aus der alten Konfigurationsdatei, die in die aktuelle Konfiguration migriert werden können. + + + + Options + Optionen + + + + Put imported databases into separate group + Importierte Datenbanken in separate Gruppe setzen + + + + Group name + Gruppenname + + + + Enter a non-empty name. + Geben Sie einen nicht leeren Namen ein. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Eine Top-Level-Gruppe mit dem Namen '%1' existiert bereits. Geben Sie einen Gruppennamen ein, der noch nicht existiert. + + + + Could not open old configuration file in order to migrate settings from it. + Die alte Konfigurationsdatei konnte nicht geöffnet werden, um die Einstellungen von ihr zu migrieren. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Die aktuelle Konfigurationsdatei konnte nicht geöffnet werden, um die Einstellungen von der alten Konfigurationsdatei zu migrieren. + + + + Could not commit migrated data into new configuration file: %1 + Konnte migrierte Daten nicht in die neue Konfigurationsdatei übertragen: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Fehlerberichte konnten nicht aus der alten Konfigurationsdatei gelesen werden, um sie zu migrieren: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Konnte den Eintrag für Fehlerberichte nicht in die neue Konfigurationsdatei einfügen: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Die Datenbankliste konnte nicht von der alten Konfigurationsdatei gelesen werden, um sie zu migrieren: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Konnte nicht die verfügbare Reihenfolge für die Angabe der Gruppe in der neuen Konfigurationsdatei abfragen, um die Datenbankliste zu migrieren: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Konnte keine Gruppe in der neuen Konfigurationsdatei erstellen, um die Datenbankliste zu migrieren: %1 + + + + Could not insert a database entry into new configuration file: %1 + Konnte keinen Datenbankeintrag in die neue Konfigurationsdatei einfügen: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Konnte nicht die verfügbare Reihenfolge für die nächste Datenbank in der neuen Konfigurationsdatei abfragen, um die Datenbankliste zu migrieren: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Konnte keine Gruppenreferenzierung der Datenbank in der neuen Konfigurationsdatei erstellen: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Funktionsliste konnte nicht aus der alten Konfigurationsdatei gelesen werden, um sie zu migrieren: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Konnte den SQL-Abfrageverlauf nicht aus der alten Konfigurationsdatei lesen, um ihn zu migrieren: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Konnte die nächste ID für den SQL-Abfrageverlauf in der neuen Konfigurationsdatei nicht lesen: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Konnte SQL-Verlaufseintrag nicht in die neue Konfigurationsdatei einfügen: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_el_GR.ts b/Plugins/ConfigMigration/translations/ConfigMigration_el_GR.ts new file mode 100644 index 0000000..6725a27 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_el_GR.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_en_US.ts b/Plugins/ConfigMigration/translations/ConfigMigration_en_US.ts new file mode 100644 index 0000000..def10d9 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_en_US.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_es_ES.ts b/Plugins/ConfigMigration/translations/ConfigMigration_es_ES.ts new file mode 100644 index 0000000..821c133 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_es_ES.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + Se ha detectado una configuración de la antigua versión de SQLiteStudio 2.x.x. ¿Quieres migrar la configuración antigua a la versión actual? <a href="%1">Haz clic aquí para hacer eso</a>. + + + + Bug reports history (%1) + Historial de informes de errores (%1) + + + + Database list (%1) + Lista de base de datos (%1) + + + + Custom SQL functions (%1) + Funciones SQL personalizadas (%1) + + + + SQL queries history (%1) + Historial de consultas SQL (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Migración de la configuración + + + + Items to migrate + Elementos a migrar + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + Esta es una lista de elementos encontrados en el antiguo archivo de configuración, que puede migrarse a la configuración actual. + + + + Options + Opciones + + + + Put imported databases into separate group + Colocar bases de datos importadas en un grupo separado + + + + Group name + Nombre del Grupo + + + + Enter a non-empty name. + Introduzca un nombre no vacío. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Un grupo de nivel superior llamado '%1' ya existe. Introduzca un nombre de grupo que aún no exista. + + + + Could not open old configuration file in order to migrate settings from it. + No se pudo abrir el antiguo archivo de configuración del cual migrar los ajustes. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + No se pudo abrir el actual archivo de configuración al cual migrar los ajustes del antiguo archivo de configuración. + + + + Could not commit migrated data into new configuration file: %1 + No se pudieron registrar los datos migrados en el nuevo archivo de configuración: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + No se pudo leer el historial de informes de errores del antiguo archivo de configuración para migrarlo: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + No se pudo insertar un registro en el historial de reportes de errores en el nuevo archivo de configuración: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + La migración de la lista de bases de datos no se pudo completar porque no se pudo leer la lista de bases de datos desde el antiguo archivo de configuración: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + No se pudo consultar el orden disponible para la contención del grupo en el nuevo archivo de configuración a fin de migrar la lista de la base de datos: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + No se pudo crear un grupo de contención en el nuevo archivo de configuración para migrar la lista de la base de datos: %1 + + + + Could not insert a database entry into new configuration file: %1 + No se pudo insertar una entrada de la base de datos en el nuevo archivo de configuración: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + No se pudo consultar el orden disponible para la siguiente base de datos en el nuevo archivo de configuración con el fin de migrar la lista de bases de datos: %1 + + + + Could not create group referencing the database in new configuration file: %1 + No se pudo crear un grupo que hiciera referencia a la base de datos en el nuevo archivo de configuración: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + No se pudo migrar la lista de funciones debido a que no fue posible leerlas desde el antiguo archivo de configuración: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + No se pudo migrar el historial de consultas SQL porque no se pudieron leer desde el antiguo archivo de configuración: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + No se pudo leer el siguiente ID del historial de consultas SQL en el nuevo archivo de configuración: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + No se pudo insertar la entrada del historial SQL en el nuevo archivo de configuración: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_fa_IR.ts b/Plugins/ConfigMigration/translations/ConfigMigration_fa_IR.ts new file mode 100644 index 0000000..79a9869 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_fa_IR.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_fi_FI.ts b/Plugins/ConfigMigration/translations/ConfigMigration_fi_FI.ts new file mode 100644 index 0000000..fd79812 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_fi_FI.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_fr_FR.ts b/Plugins/ConfigMigration/translations/ConfigMigration_fr_FR.ts new file mode 100644 index 0000000..4cc2ad2 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_fr_FR.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + Une configuration d’un ancien SQLiteStudio 2.x.x a été détectée. Voulez-vous migrer l’ancienne configuration pour la version courante ? <a href="%1">Cliquer ici pour l’exécuter</a>. + + + + Bug reports history (%1) + Historique des rappots de bug (%1) + + + + Database list (%1) + Liste des bases de données (%1) + + + + Custom SQL functions (%1) + Personnalisation des fonctions SQL (%1) + + + + SQL queries history (%1) + Historique des requêtes SQL (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Migration de la configuration + + + + Items to migrate + Items à migrer + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + Voici la liste des items trouvés dans l’ancien fichier de configuration, pouvant être importés dans la configuration actuelle. + + + + Options + Options + + + + Put imported databases into separate group + Mettre les bases de données importées dans un groupe séparé + + + + Group name + Nom du groupe + + + + Enter a non-empty name. + Saisissez un nom valide. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Un groupe de niveau supérieur nommé '%1' existe déjà. Entrez un nom de groupe qui n'existe pas encore. + + + + Could not open old configuration file in order to migrate settings from it. + Impossible d’ouvrir l’ancien fichier de configuration pour importer les préférences. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Impossible d’ouvrir l’actuel fichier de configuration pour importer les préférences de l’ancien fichier de configuration. + + + + Could not commit migrated data into new configuration file: %1 + Impossible d’enregistrer les données de migration dans le nouveau fichier de configuration : %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Impossible de lire l’historique du rapport de bug de l’ancienne configuration pour l’importée %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Impossible d’insérer l’historique du rapport de bug dans le nouveau fichier de configuration : %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Impossible de lire la liste des bases de données de l’ancien fichier de configuration : %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Impossible d’exécuter la requête de tri de groupe dans le nouveau fichier de configuration pour importer la liste des bases de données : %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Impossible de créer un groupe dans le nouveau fichier de configuration pour migrer la liste des bases de données : %1 + + + + Could not insert a database entry into new configuration file: %1 + Impossible d’insérer le nom d’une base de données dans le nouveau fichier de configuration : %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Impossible d’exécuter la requête de tri pour la base de données suivante dans le nouveau fichier de configuration pour importer la liste les bases de données : %1 + + + + Could not create group referencing the database in new configuration file: %1 + Impossible de créer un groupe référençant les bases de données dans le nouveau fichier de configuration : %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Impossible de lire la liste de fonction de l’ancien fichier de configuration pour l’importer : %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Impossible de lire l’historique des requêtes SQL de l’ancien fichier de configuration pour l’importer : %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Impossible de lire l’ID suivant de l’historique des requêtes dans le nouveau fichier de configuration : %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Impossible d’insérer un historique d’SQL dans le nouveau fichier de configuration : %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_he_IL.ts b/Plugins/ConfigMigration/translations/ConfigMigration_he_IL.ts new file mode 100644 index 0000000..781d0fb --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_he_IL.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + זוהתה תצורה ישנה מגרסת SQLiteStudio 2.x.x. ×”×× ×œ×”×¡×‘ הגדרות ישנות לגרסה הנוכחית? <a href="%1">הקשה ל×ישור ביצוע</a>. + + + + Bug reports history (%1) + היסטורית דיווח ×ª×§×œ×™× (%1) + + + + Database list (%1) + רשימת מסדי × ×ª×•× ×™× (%1) + + + + Custom SQL functions (%1) + תפקודי SQK מות××ž×™× (%1) + + + + SQL queries history (%1) + היסטוריית ש×ילתות SQL (%1) + + + + ConfigMigrationWizard + + + Configuration migration + הגירת תצורה + + + + Items to migrate + ×¤×¨×™×˜×™× ×œ×”×¡×‘×” + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + זוהי רשימה של ×¤×¨×™×˜×™× ×©× ×ž×¦×ו בקובץ התצורה הישן, ××•×ª× × ×™×ª×Ÿ להסב לתצורה הנוכחית. + + + + Options + ×פשרויות + + + + Put imported databases into separate group + הכנסת מסדי × ×ª×•× ×™× ×ž×™×•×‘××™× ×œ×§×‘×•×¦×” נפרדת + + + + Group name + ×©× ×§×‘×•×¦×” + + + + Enter a non-empty name. + × × ×œ×”×–×™×Ÿ ×©× (×œ× ×¨×™×§). + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + קבוצה ברמה עליונה ×‘×©× '%1' קיימת כבר. × × ×œ×”×–×™×Ÿ ×©× ×§×‘×•×¦×” ×©×˜×¨× × ×•×¦×¨×”. + + + + Could not open old configuration file in order to migrate settings from it. + ×œ× × ×™×ª×Ÿ לפתוח קובץ תצורה ישן על מנת להסב ממנו הגדרות. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + ×œ× × ×™×ª×Ÿ לפתוח קובץ תצורה נוכחי על מנת להסב הגדרות מקובץ תצורה ישן. + + + + Could not commit migrated data into new configuration file: %1 + ×œ× × ×™×ª×Ÿ לקבע ×ת ×”× ×ª×•× ×™× ×”×ž×•×¡×‘×™× ×œ×§×•×‘×¥ תצורה חדש: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + ×œ× × ×™×ª×Ÿ ×œ×§×¨×•× ×ת היסטוריית דוחות ×”×ª×§×œ×™× ×ž×§×•×‘×¥ התצורה הישן על מנת להסב ×ותן: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + ×œ× × ×™×ª×Ÿ להכניס ×ת רשומת היסטוריית דוחות ×”×ª×§×œ×™× ×œ×§×•×‘×¥ תצורה חדש: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + ×œ× × ×™×ª×Ÿ ×œ×§×¨×•× ×ת רשימת מסדי ×”× ×ª×•× ×™× ×ž×§×•×‘×¥ התצורה הישן על מנת להסב ×ותן: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + ×œ× × ×™×ª×Ÿ לבצע ש×ילתה על קבוצת הכלה וסדר זמין בקובץ התצורה החדש, על מנת להסב ×ת רשימת מסדי הנתוני×: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + ×œ× × ×™×ª×Ÿ ליצור קבוצת הכלה בקובץ התצורה החדש, על מנת להסב ×ת רשימת מסדי הנתוני×: %1 + + + + Could not insert a database entry into new configuration file: %1 + ×œ× × ×™×ª×Ÿ להכניס ×ת רשומת מסד × ×ª×•× ×™× ×œ×§×•×‘×¥ תצורה חדש: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + ×œ× × ×™×ª×Ÿ לבצע ש×ילתה על סדר זמין של מסד ×”× ×ª×•× ×™× ×”×‘× ×‘×§×•×‘×¥ התצורה החדש, על מנת להסב ×ת רשימת מסדי הנתוני×: %1 + + + + Could not create group referencing the database in new configuration file: %1 + ×œ× × ×™×ª×Ÿ ליצור קבוצה המפנה ×ת מסד ×”× ×ª×•× ×™× ×‘×§×•×‘×¥ התצורה החדש, על מנת להעביר ×ת רשימת מסדי הנתוני×: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + ×œ× × ×™×ª×Ÿ ×œ×§×¨×•× ×¨×©×™×ž×ª פונקציות מקובץ התצורה הישן על מנת להסב ×ותן: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + ×œ× × ×™×ª×Ÿ ×œ×§×¨×•× ×ת היסטוריית ש×ילתות ×” SQL מקובץ התצורה הישן על מנת להסב ×ותן: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + ×œ× × ×™×ª×Ÿ ×œ×§×¨×•× ×ת המזהה ×”×‘× ×©×œ ש×ילתות ×” SQL מקובץ התצורה החדש: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + ×œ× × ×™×ª×Ÿ להכניס ×ת רשומת היסטוריית ש×ילתות SQL לקובץ תצורה חדש: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_hu_HU.ts b/Plugins/ConfigMigration/translations/ConfigMigration_hu_HU.ts new file mode 100644 index 0000000..79124a2 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_hu_HU.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_it_IT.ts b/Plugins/ConfigMigration/translations/ConfigMigration_it_IT.ts new file mode 100644 index 0000000..db4df30 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_it_IT.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + È stata rilevata una configurazione dal vecchio SQLiteStudio 2.x.x. Vuoi migrare le vecchie impostazioni nella versione corrente? <a href="%1">Clicca qui per farlo</a>. + + + + Bug reports history (%1) + Cronologia segnalazioni bug (%1) + + + + Database list (%1) + Lista database (%1) + + + + Custom SQL functions (%1) + Funzioni SQL personalizzate (%1) + + + + SQL queries history (%1) + Cronologia query SQL (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Migrazione configurazione + + + + Items to migrate + Oggetti da migrare + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + Questo è un elenco di elementi trovati nel vecchio file di configurazione, che può essere migrato nella configurazione corrente. + + + + Options + Opzioni + + + + Put imported databases into separate group + Mette i database importati in un gruppo separato + + + + Group name + Nome del gruppo + + + + Enter a non-empty name. + Inserisci un nome non vuoto. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Il gruppo di livello superiore chiamato '%1' esiste già. Inserisci un nome di gruppo che non esiste ancora. + + + + Could not open old configuration file in order to migrate settings from it. + Impossibile aprire il vecchio file di configurazione dal quale migrare le impostazioni. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Impossibile aprire il file di configurazione corrente per migrare le impostazioni dal vecchio file di configurazione. + + + + Could not commit migrated data into new configuration file: %1 + Impossibile registrare i dati migrati nel nuovo file di configurazione: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Impossibile leggere la cronologia delle segnalazioni di bug dal vecchio file di configurazione per migrarlo: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Impossibile inserire una voce di cronologia delle segnalazioni di bug nel nuovo file di configurazione: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Impossibile leggere l'elenco dei database dal vecchio file di configurazione per migrarlo: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Impossibile interrogare per l'ordine disponibile contenente il gruppo in un nuovo file di configurazione per migrare l'elenco del database: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Impossibile creare il gruppo contenente nel nuovo file di configurazione per migrare l'elenco del database: %1 + + + + Could not insert a database entry into new configuration file: %1 + Impossibile inserire una voce di database nel nuovo file di configurazione: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Impossibile interrogare l'ordinamento disponibile per il prossimo database in un nuovo file di configurazione per migrare l'elenco del database: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Impossibile creare il gruppo di riferimento del database nel nuovo file di configurazione: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Impossibile leggere l'elenco delle funzioni dal vecchio file di configurazione per migrarlo: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Impossibile leggere la cronologia delle interrogazioni SQL dal vecchio file di configurazione per migrarla: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Impossibile leggere il prossimo ID per la cronologia delle query SQL nel nuovo file di configurazione: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Impossibile inserire la cronologia SQL nel nuovo file di configurazione: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_ja_JP.ts b/Plugins/ConfigMigration/translations/ConfigMigration_ja_JP.ts new file mode 100644 index 0000000..68251d7 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_ja_JP.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_kaa.ts b/Plugins/ConfigMigration/translations/ConfigMigration_kaa.ts new file mode 100644 index 0000000..499a494 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_kaa.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_ko_KR.ts b/Plugins/ConfigMigration/translations/ConfigMigration_ko_KR.ts new file mode 100644 index 0000000..d05b0ea --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_ko_KR.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + ë°ì´í„°ë² ì´ìФ ëª©ë¡ (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + 설정 + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_nl_NL.ts b/Plugins/ConfigMigration/translations/ConfigMigration_nl_NL.ts new file mode 100644 index 0000000..64c0b57 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_nl_NL.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_no_NO.ts b/Plugins/ConfigMigration/translations/ConfigMigration_no_NO.ts new file mode 100644 index 0000000..a0c1d83 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_no_NO.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_pl_PL.ts b/Plugins/ConfigMigration/translations/ConfigMigration_pl_PL.ts new file mode 100644 index 0000000..8cfac57 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_pl_PL.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + Wykryto ustawienia ze starej wersji SQLiteStudio 2.x.x. Czy chcesz zmigrować stare ustawienia do aktualnej wersji? <a href="%1">Kliknij, aby to zrobić</a>. + + + + Bug reports history (%1) + Historia zgÅ‚oszonych błędów (%1) + + + + Database list (%1) + Lista baz danych (%1) + + + + Custom SQL functions (%1) + WÅ‚asne funkcje SQL (%1) + + + + SQL queries history (%1) + Historia zapytaÅ„ SQL (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Migracja ustawieÅ„ + + + + Items to migrate + Elementy do zmigrowania + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + To jest lista elementów znalezionych w starym pliku konfiguracyjnym, które mogÄ… być zmigrowane do aktualnych ustawieÅ„. + + + + Options + Opcje + + + + Put imported databases into separate group + Umieść zaimportowane bazy danych w osobnej grupie + + + + Group name + Nazwa grupy + + + + Enter a non-empty name. + Wprowadź niepustÄ… nazwÄ™. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Grupa o nazwie '%1' istnieje już na najwyższym poziomie. Wprowadź nazwÄ™ grupy, która jeszcze nie istnieje. + + + + Could not open old configuration file in order to migrate settings from it. + Nie udaÅ‚o siÄ™ otworzyć starego pliku ustawieÅ„ w celu zmigtowania z niego ustawieÅ„. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Nie udaÅ‚o siÄ™ otworzyć aktualnego pliku ustawieÅ„ w celu zmigrowania do niego ustawieÅ„ ze starego pliku konfiguracyjnego. + + + + Could not commit migrated data into new configuration file: %1 + Nie udaÅ‚o siÄ™ zatwierdzenie zmigrowanych danych w nowym pliku ustawieÅ„: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Nie udaÅ‚ siÄ™ odczyt historii zgÅ‚oszeÅ„ błędów ze starego pliku konfiguracyjnego w celu jej zmigrowania: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Nie udaÅ‚o siÄ™ dodać wpisów historii zgÅ‚oszeÅ„ błędów do nowego pliku ustawieÅ„: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Nie udaÅ‚o siÄ™ odczytać listy baz danych ze starego pliku ustawieÅ„ w celu jej zmigtowania: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Nie udaÅ‚o siÄ™ odpytać o dostÄ™pnÄ… kolejność dla grupy zawierajÄ…cej w nowym pliku ustawieÅ„, w celu zmigrowania listy baz danych: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Nie udaÅ‚o siÄ™ stworzyć grupy w nowym pliku ustawieÅ„, w celu zmigrowania listy baz danych: %1 + + + + Could not insert a database entry into new configuration file: %1 + Nie udaÅ‚o siÄ™ dodać wpisu bazy danych do nowego pliku ustawieÅ„: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Nie udaÅ‚o siÄ™ odpytać o dostÄ™pnÄ… kolejność dla nastÄ™pnej bazy w nowym pliku ustawieÅ„, w celu zmigrowania listy baz danych: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Nie udaÅ‚o siÄ™ utworzyć grupy odwoÅ‚ujÄ…cej siÄ™ do bazy danych w nowym pliku ustawieÅ„: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Nie udaÅ‚o siÄ™ odczytać listy funkcji ze starego pliku ustawieÅ„ w celu jej zmigrowania: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Nie udaÅ‚o siÄ™ odczytać historii zapytaÅ„ SQL ze starego pliku ustawieÅ„ w celu zmigrowania jej: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Nie udaÅ‚o siÄ™ odczytać nastÄ™pnego ID dla historii zapytaÅ„ SQL w nowym pliku ustawieÅ„: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Nie udaÅ‚o siÄ™ dodanie wpisu do historii SQL w nowym pliku ustawieÅ„: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_pt_BR.ts b/Plugins/ConfigMigration/translations/ConfigMigration_pt_BR.ts new file mode 100644 index 0000000..dc193f5 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_pt_BR.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + Foi detectada uma configuração do SQLiteStudio 2.x.x. Você gostaria de migrar as configurações antigas para a versão atual? <a href="%1">Clique aqui para fazer isso</a>. + + + + Bug reports history (%1) + Histórico de relatórios de erros (%1) + + + + Database list (%1) + Lista de banco de dados (%1) + + + + Custom SQL functions (%1) + Funções SQL personalizadas (%1) + + + + SQL queries history (%1) + Histórico de consultas SQL (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Migração de configuração + + + + Items to migrate + Itens para migrar + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + Esta é uma lista de itens encontrados no arquivo de configuração antigo, que podem ser migrados para a configuração atual. + + + + Options + Opções + + + + Put imported databases into separate group + Colocar bancos de dados importados em um grupo separado + + + + Group name + Nome do grupo + + + + Enter a non-empty name. + Insira um nome não vazio. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + O grupo de nível superior chamado '%1' já existe. Digite um nome de grupo que ainda não existe. + + + + Could not open old configuration file in order to migrate settings from it. + Não foi possível abrir o arquivo de configuração antigo para migrar suas configurações. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Não foi possível abrir o arquivo de configuração atual para migrar as configurações antigas. + + + + Could not commit migrated data into new configuration file: %1 + Não foi possível migrar os dados para o novo arquivo de configuração: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Não foi possível ler o relatório de erros do antigo arquivo de configuração para migrá-lo: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Não foi possível inserir uma entrada no relatório de bugs no novo arquivo de configuração: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Não foi possível ler a lista de banco de dados do antigo arquivo de configuração para migrá-lo: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Não foi possível consultar a ordem disponível para o grupo que contém o novo arquivo de configuração para migrar a lista de banco de dados: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Não foi possível criar o grupo contendo o novo arquivo de configuração, a fim de migra-lo: %1 + + + + Could not insert a database entry into new configuration file: %1 + Não foi possível inserir uma entrada no banco de dados no novo arquivo de configuração: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Não foi possível criar o grupo contendo o novo arquivo de configuração, a fim de migra-lo: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Não foi possível criar grupo referenciando a base de dados no novo arquivo de configuração: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Não foi possível ler a lista de funções do antigo arquivo de configuração para migrá-lo: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Não foi possível ler o histórico de consultas SQL do antigo arquivo de configuração para migrá-lo: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Não foi possível ler o próximo ID para o histórico de consultas SQL no novo arquivo de configuração: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Não foi possível inserir a entrada do histórico SQL no novo arquivo de configuração: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_pt_PT.ts b/Plugins/ConfigMigration/translations/ConfigMigration_pt_PT.ts new file mode 100644 index 0000000..fbe12d0 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_pt_PT.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_ro_RO.ts b/Plugins/ConfigMigration/translations/ConfigMigration_ro_RO.ts new file mode 100644 index 0000000..adbed90 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_ro_RO.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_ru_RU.ts b/Plugins/ConfigMigration/translations/ConfigMigration_ru_RU.ts new file mode 100644 index 0000000..45b4129 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_ru_RU.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + Обнаружена ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð¾Ñ‚ Ñтарой верÑии SQLiteStudio (2.x.x). Ð’Ñ‹ хотите перенеÑти Ñтарые наÑтройки в новую верÑию? <a href="%1">Ðажмите здеÑÑŒ Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа</a>. + + + + Bug reports history (%1) + ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¾Ñ‚Ñ‡Ñ‘Ñ‚Ð¾Ð² об ошибках (%1) + + + + Database list (%1) + СпиÑок баз данных (%1) + + + + Custom SQL functions (%1) + Произвольные функции SQL (%1) + + + + SQL queries history (%1) + ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов SQL (%1) + + + + ConfigMigrationWizard + + + Configuration migration + ÐŸÐµÑ€ÐµÐ½Ð¾Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¸ + + + + Items to migrate + ПереноÑимые Ñлементы + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + Это ÑпиÑок Ñлементов, обнаруженных в Ñтаром конфигурационном файле, которые могут быть перенеÑены в текущую конфигурацию. + + + + Options + Опции + + + + Put imported databases into separate group + ПомеÑтить импортированные базы данных в отдельную группу + + + + Group name + Ð˜Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ + + + + Enter a non-empty name. + Введите непуÑтое имÑ. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Группа верхнего ÑƒÑ€Ð¾Ð²Ð½Ñ '%1' уже ÑущеÑтвует. Введите Ð¸Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹, которое ещё не занÑто. + + + + Could not open old configuration file in order to migrate settings from it. + Ðевозможно открыть Ñтарый файл конфигурации Ð´Ð»Ñ Ð¾ÑущеÑÑ‚Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа наÑтроек. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Ðевозможно открыть текущий файл конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа наÑтроек из Ñтарого файла конфигурации. + + + + Could not commit migrated data into new configuration file: %1 + Ðевозможно запиÑать перенеÑённые данные в новый файл конфигурации: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Ðевозможно прочитать иÑторию отчётов об ошибках из Ñтарого файла конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Ðевозможно вÑтавить иÑторию отчётов об ошибках в новый файл конфигурации: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Ðевозможно прочитать ÑпиÑок баз данных из Ñтарого файла конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Ðевозможно запроÑить доÑтупное положение отдельной группы в новом файле конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа в неё ÑпиÑка баз данных: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Ðевозможно Ñоздать отдельную группу в новом файле конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа в неё ÑпиÑка баз данных: %1 + + + + Could not insert a database entry into new configuration file: %1 + Ðевозможно вÑтавить Ñлемент ÑпиÑка баз данных в новый файл конфигурации: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Ðевозможно запроÑить доÑтупное положение Ð´Ð»Ñ Ñледующей базы данных в новом файле конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа ÑпиÑка баз данных: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Ðевозможно Ñоздать группу, ÑÑылающуюÑÑ Ð½Ð° базу данных в новом файле конфигурации: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Ðевозможно прочитать ÑпиÑок функций из Ñтарого файла конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Ðевозможно прочитать иÑторию запроÑов SQL из Ñтарого файла конфигурации Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð¾Ñа: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Ðевозможно Ñчитать Ñледующий ID Ð´Ð»Ñ Ð¸Ñтории запроÑов SQL в новом файле конфигурации: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Ðевозможно вÑтавить Ñлемент иÑтории запроÑов SQL в новый файл конфигурации: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_sk_SK.ts b/Plugins/ConfigMigration/translations/ConfigMigration_sk_SK.ts new file mode 100644 index 0000000..a7a8cbc --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_sk_SK.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_sr_SP.ts b/Plugins/ConfigMigration/translations/ConfigMigration_sr_SP.ts new file mode 100644 index 0000000..acdc218 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_sr_SP.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_sv_SE.ts b/Plugins/ConfigMigration/translations/ConfigMigration_sv_SE.ts new file mode 100644 index 0000000..bd753a4 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_sv_SE.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Felrapporterings Historik (%1) + + + + Database list (%1) + Databaslista (%1) + + + + Custom SQL functions (%1) + Anpassade SQL-funktioner (%1) + + + + SQL queries history (%1) + SQL frÃ¥gehistorik (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Migrering av konfiguration + + + + Items to migrate + Objekt att migrera + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Alternativ + + + + Put imported databases into separate group + Sätt importerade databaser i separat grupp + + + + Group name + Gruppnamn + + + + Enter a non-empty name. + Ange ett icke-tomt namn. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + HögnivÃ¥gruppen heter '%1' finns redan. Ange ett gruppnamn som inte finns ännu. + + + + Could not open old configuration file in order to migrate settings from it. + Kunde inte öppna gamla konfigurationsfilen för att migrera inställningar frÃ¥n den. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Kunde inte öppna nuvarande konfigurationsfil för att migrera inställningar frÃ¥n gamla konfigurationsfilen. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Kunde inte läsa felrapporterings historik frÃ¥n gammal konfigurationsfil för att migrera den: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Kunde inte infoga SQL-historik i ny konfigurationsfil: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_tr_TR.ts b/Plugins/ConfigMigration/translations/ConfigMigration_tr_TR.ts new file mode 100644 index 0000000..9d04de4 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_tr_TR.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_uk_UA.ts b/Plugins/ConfigMigration/translations/ConfigMigration_uk_UA.ts new file mode 100644 index 0000000..f60c24a --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_uk_UA.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_vi_VN.ts b/Plugins/ConfigMigration/translations/ConfigMigration_vi_VN.ts new file mode 100644 index 0000000..937662a --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_vi_VN.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + + + + Bug reports history (%1) + Bug reports history (%1) + + + + Database list (%1) + Database list (%1) + + + + Custom SQL functions (%1) + Custom SQL functions (%1) + + + + SQL queries history (%1) + SQL queries history (%1) + + + + ConfigMigrationWizard + + + Configuration migration + Configuration migration + + + + Items to migrate + Items to migrate + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + + + + Options + Options + + + + Put imported databases into separate group + Put imported databases into separate group + + + + Group name + Group name + + + + Enter a non-empty name. + Enter a non-empty name. + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + Top level group named '%1' already exists. Enter a group name that does not exist yet. + + + + Could not open old configuration file in order to migrate settings from it. + Could not open old configuration file in order to migrate settings from it. + + + + Could not open current configuration file in order to migrate settings from old configuration file. + Could not open current configuration file in order to migrate settings from old configuration file. + + + + Could not commit migrated data into new configuration file: %1 + Could not commit migrated data into new configuration file: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + Could not read bug reports history from old configuration file in order to migrate it: %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + Could not insert a bug reports history entry into new configuration file: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + Could not read database list from old configuration file in order to migrate it: %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + Could not create containing group in new configuration file in order to migrate the database list: %1 + + + + Could not insert a database entry into new configuration file: %1 + Could not insert a database entry into new configuration file: %1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + + + + Could not create group referencing the database in new configuration file: %1 + Could not create group referencing the database in new configuration file: %1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + Could not read function list from old configuration file in order to migrate it: %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + Could not read SQL queries history from old configuration file in order to migrate it: %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + Could not read next ID for SQL queries history in new configuration file: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + Could not insert SQL history entry into new configuration file: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_zh_CN.ts b/Plugins/ConfigMigration/translations/ConfigMigration_zh_CN.ts new file mode 100644 index 0000000..22f7b19 --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_zh_CN.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + 检测到旧的 SQLiteStudio 2.x.x çš„é…置。 你想è¦å°†æ—§çš„设置è¿ç§»åˆ°å½“å‰ç‰ˆæœ¬å—? <a href="%1">点击这里进行è¿ç§»</a>。 + + + + Bug reports history (%1) + é”™è¯¯æŠ¥å‘ŠåŽ†å² (%1) + + + + Database list (%1) + æ•°æ®åº“列表 (%1) + + + + Custom SQL functions (%1) + 自定义 SQL 函数 (%1) + + + + SQL queries history (%1) + SQL æŸ¥è¯¢åŽ†å² (%1) + + + + ConfigMigrationWizard + + + Configuration migration + é…ç½®è¿ç§» + + + + Items to migrate + è¦è¿ç§»çš„项 + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + 这是在旧的é…置文件中找到的项列表,这些项å¯ä»¥è¿ç§»åˆ°å½“å‰é…置。 + + + + Options + 选项 + + + + Put imported databases into separate group + 将导入的数æ®åº“置于å•独的组 + + + + Group name + 组åç§° + + + + Enter a non-empty name. + 请输入一个éžç©ºçš„å称。 + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + å为 '%1' 的顶级组已存在。请输入未被å ç”¨çš„组å称。 + + + + Could not open old configuration file in order to migrate settings from it. + 无法打开旧的é…置文件以è¿ç§»å®ƒçš„设置。 + + + + Could not open current configuration file in order to migrate settings from old configuration file. + 无法打开当å‰çš„é…置文件以è¿ç§»æ—§çš„设置。 + + + + Could not commit migrated data into new configuration file: %1 + 无法将è¿ç§»çš„æ•°æ®æäº¤åˆ°æ–°çš„é…置文件: %1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + 无法从旧的é…置文件读å–错误报告历å²è®°å½•æ¥è¿›è¡Œè¿ç§»ï¼š %1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + 无法将错误报告历å²è®°å½•æ’入到新的é…置文件: %1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + 无法从旧的é…ç½®æ–‡ä»¶è¯»å–æ•°æ®åº“列表æ¥è¿›è¡Œè¿ç§»ï¼š %1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + 无法在新é…置文件中查询包å«ç»„çš„å¯ç”¨é¡ºåºä»¥è¿ç§»æ•°æ®åº“列表: %1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + 无法在新é…置文件中创建包å«ç»„以è¿ç§»æ•°æ®åº“列表: %1 + + + + Could not insert a database entry into new configuration file: %1 + 无法将数æ®åº“æ¡ç›®æ’入到新的é…置文件:%1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + 无法在新é…置文件中查询下一个数æ®åº“çš„å¯ç”¨é¡ºåºä»¥è¿ç§»æ•°æ®åº“列表:%1 + + + + Could not create group referencing the database in new configuration file: %1 + 未能在新的é…置文件中创建引用该数æ®åº“的组:%1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + 无法从旧é…置文件读å–函数列表æ¥è¿›è¡Œè¿ç§»ï¼š %1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + 无法从旧é…ç½®æ–‡ä»¶è¯»å– SQL æŸ¥è¯¢åŽ†å²æ¥è¿›è¡Œè¿ç§»ï¼š %1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + 无法在新é…ç½®æ–‡ä»¶ä¸­è¯»å– SQL 查询历å²çš„下一个ID: %1 + + + + Could not insert SQL history entry into new configuration file: %1 + 无法将 SQL åŽ†å²æ¡ç›®æ’入到新的é…置文件: %1 + + + diff --git a/Plugins/ConfigMigration/translations/ConfigMigration_zh_TW.ts b/Plugins/ConfigMigration/translations/ConfigMigration_zh_TW.ts new file mode 100644 index 0000000..5af00ec --- /dev/null +++ b/Plugins/ConfigMigration/translations/ConfigMigration_zh_TW.ts @@ -0,0 +1,150 @@ + + + + + ConfigMigration + + + A configuration from old SQLiteStudio 2.x.x has been detected. Would you like to migrate old settings into the current version? <a href="%1">Click here to do that</a>. + 嵿¸¬åˆ°èˆŠçš„ SQLiteStudio 2.x.x 的設定檔。你想è¦å°‡èˆŠçš„設定é·ç§»åˆ°ç•¶å‰ç‰ˆæœ¬å—Žï¼Ÿ<a href="%1">點é¸é€™è£¡é€²è¡Œé·ç§»</a>。 + + + + Bug reports history (%1) + éŒ¯èª¤å ±å‘Šæ­·å² (%1) + + + + Database list (%1) + 資料庫清單 (%1) + + + + Custom SQL functions (%1) + 自訂 SQL å‡½å¼ (%1) + + + + SQL queries history (%1) + SQL æŸ¥è©¢æ­·å² (%1) + + + + ConfigMigrationWizard + + + Configuration migration + 設定檔é·ç§» + + + + Items to migrate + è¦é·ç§»çš„é … + + + + This is a list of items found in the old configuration file, which can be migrated into the current configuration. + 這是在舊的設定檔檔案中找到的項清單,這些項å¯ä»¥é·ç§»åˆ°ç•¶å‰è¨­å®šæª”。 + + + + Options + é¸é … + + + + Put imported databases into separate group + 將匯入的資料庫置於單ç¨çš„組 + + + + Group name + 組å稱 + + + + Enter a non-empty name. + 請輸入一個éžç©ºçš„å稱。 + + + + Top level group named '%1' already exists. Enter a group name that does not exist yet. + å為 '%1' 的頂級組已存在。請輸入未被佔用的組å稱。 + + + + Could not open old configuration file in order to migrate settings from it. + 無法開啟舊的設定檔檔案以é·ç§»å®ƒçš„設定。 + + + + Could not open current configuration file in order to migrate settings from old configuration file. + 無法開啟當å‰çš„設定檔檔案以é·ç§»èˆŠçš„設定。 + + + + Could not commit migrated data into new configuration file: %1 + 無法將é·ç§»çš„資料æäº¤åˆ°æ–°çš„設定檔檔案:%1 + + + + Could not read bug reports history from old configuration file in order to migrate it: %1 + 無法從舊的設定檔檔案讀å–錯誤報告歷å²è¨˜éŒ„來進行é·ç§»ï¼š%1 + + + + Could not insert a bug reports history entry into new configuration file: %1 + 無法將錯誤報告歷å²è¨˜éŒ„æ’入到新的設定檔檔案:%1 + + + + Could not read database list from old configuration file in order to migrate it: %1 + 無法從舊的設定檔檔案讀å–資料庫清單來進行é·ç§»ï¼š%1 + + + + Could not query for available order for containing group in new configuration file in order to migrate the database list: %1 + 無法在新設定檔檔案中查詢包å«çµ„çš„å¯ç”¨é †åºä»¥é·ç§»è³‡æ–™åº«æ¸…單:%1 + + + + Could not create containing group in new configuration file in order to migrate the database list: %1 + 無法在新設定檔檔案中建立包å«çµ„以é·ç§»è³‡æ–™åº«æ¸…單:%1 + + + + Could not insert a database entry into new configuration file: %1 + 無法將資料庫æ¢ç›®æ’入到新的設定檔檔案:%1 + + + + Could not query for available order for next database in new configuration file in order to migrate the database list: %1 + 無法在新設定檔檔案中查詢下一個資料庫的å¯ç”¨é †åºä»¥é·ç§»è³‡æ–™åº«æ¸…單:%1 + + + + Could not create group referencing the database in new configuration file: %1 + 未能在新的設定檔檔案中建立引用該資料庫的組:%1 + + + + Could not read function list from old configuration file in order to migrate it: %1 + 無法從舊設定檔檔案讀å–函弿¸…單來進行é·ç§»ï¼š%1 + + + + Could not read SQL queries history from old configuration file in order to migrate it: %1 + ç„¡æ³•å¾žèˆŠè¨­å®šæª”æª”æ¡ˆè®€å– SQL 查詢歷å²ä¾†é€²è¡Œé·ç§»ï¼š%1 + + + + Could not read next ID for SQL queries history in new configuration file: %1 + ç„¡æ³•åœ¨æ–°è¨­å®šæª”æª”æ¡ˆä¸­è®€å– SQL 查詢歷å²çš„下一個 ID:%1 + + + + Could not insert SQL history entry into new configuration file: %1 + 無法將 SQL æ­·å²æ¢ç›®æ’入到新的設定檔檔案:%1 + + + diff --git a/Plugins/CsvExport/CsvExport.pro b/Plugins/CsvExport/CsvExport.pro index e3fe195..648c2a8 100644 --- a/Plugins/CsvExport/CsvExport.pro +++ b/Plugins/CsvExport/CsvExport.pro @@ -29,26 +29,6 @@ RESOURCES += \ -TRANSLATIONS += CsvExport_ro_RO.ts \ - CsvExport_de.ts \ - CsvExport_it.ts \ - CsvExport_zh_CN.ts \ - CsvExport_sk.ts \ - CsvExport_ru.ts \ - CsvExport_pt_BR.ts \ - CsvExport_fr.ts \ - CsvExport_es.ts \ - CsvExport_pl.ts - - - - - - - - - - diff --git a/Plugins/CsvExport/CsvExport_de.qm b/Plugins/CsvExport/CsvExport_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/CsvExport/CsvExport_de.qm and /dev/null differ diff --git a/Plugins/CsvExport/CsvExport_de.ts b/Plugins/CsvExport/CsvExport_de.ts deleted file mode 100644 index 1d07517..0000000 --- a/Plugins/CsvExport/CsvExport_de.ts +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CsvExport - - - Column names in first row - - - - - Column separator: - - - - - , (comma) - - - - - ; (semicolon) - - - - - \t (tab) - - - - - (whitespace) - - - - - Custom: - - - - - Export NULL values as: - - - - - Empty string - - - - - Enter the custom separator character. - - - - diff --git a/Plugins/CsvExport/CsvExport_es.qm b/Plugins/CsvExport/CsvExport_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/CsvExport/CsvExport_es.qm and /dev/null differ diff --git a/Plugins/CsvExport/CsvExport_es.ts b/Plugins/CsvExport/CsvExport_es.ts deleted file mode 100644 index 09fd76a..0000000 --- a/Plugins/CsvExport/CsvExport_es.ts +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CsvExport - - - Column names in first row - - - - - Column separator: - - - - - , (comma) - - - - - ; (semicolon) - - - - - \t (tab) - - - - - (whitespace) - - - - - Custom: - - - - - Export NULL values as: - - - - - Empty string - - - - - Enter the custom separator character. - - - - diff --git a/Plugins/CsvExport/CsvExport_fr.qm b/Plugins/CsvExport/CsvExport_fr.qm deleted file mode 100644 index 6512538..0000000 Binary files a/Plugins/CsvExport/CsvExport_fr.qm and /dev/null differ diff --git a/Plugins/CsvExport/CsvExport_fr.ts b/Plugins/CsvExport/CsvExport_fr.ts deleted file mode 100644 index 4b0f86b..0000000 --- a/Plugins/CsvExport/CsvExport_fr.ts +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CsvExport - - - Column names in first row - Noms des colonnes dans la 1ère ligne - - - - Column separator: - Séparateur de colonnes : - - - - , (comma) - , (virgule) - - - - ; (semicolon) - ; (point virgule) - - - - \t (tab) - \t (tabulation) - - - - (whitespace) - (espace) - - - - Custom: - personnalisation : - - - - Export NULL values as: - Exporter la valeur NULL comme : - - - - Empty string - Chaine de caractères vide - - - - Enter the custom separator character. - Saisir le caractère séparateur personnalisé. - - - diff --git a/Plugins/CsvExport/CsvExport_it.qm b/Plugins/CsvExport/CsvExport_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/CsvExport/CsvExport_it.qm and /dev/null differ diff --git a/Plugins/CsvExport/CsvExport_it.ts b/Plugins/CsvExport/CsvExport_it.ts deleted file mode 100644 index 30f4907..0000000 --- a/Plugins/CsvExport/CsvExport_it.ts +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CsvExport - - - Column names in first row - - - - - Column separator: - - - - - , (comma) - - - - - ; (semicolon) - - - - - \t (tab) - - - - - (whitespace) - - - - - Custom: - - - - - Export NULL values as: - - - - - Empty string - - - - - Enter the custom separator character. - - - - diff --git a/Plugins/CsvExport/CsvExport_pl.qm b/Plugins/CsvExport/CsvExport_pl.qm deleted file mode 100644 index 4d64a8b..0000000 Binary files a/Plugins/CsvExport/CsvExport_pl.qm and /dev/null differ diff --git a/Plugins/CsvExport/CsvExport_pl.ts b/Plugins/CsvExport/CsvExport_pl.ts deleted file mode 100644 index a608049..0000000 --- a/Plugins/CsvExport/CsvExport_pl.ts +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CsvExport - - - Column names in first row - Nazwy kolumn w pierwszym wierszu - - - - Column separator: - Separator kolumny: - - - - , (comma) - , (przecinek) - - - - ; (semicolon) - ; (Å›rednik) - - - - \t (tab) - \t (znak tabulacji) - - - - (whitespace) - (spacja) - - - - Custom: - WÅ‚asny: - - - - Export NULL values as: - Eksportuj wartoÅ›ci NULL jako: - - - - Empty string - Pusty Å‚aÅ„cuch - - - - Enter the custom separator character. - Wprowadź wÅ‚asny znak separatora. - - - diff --git a/Plugins/CsvExport/CsvExport_pt_BR.qm b/Plugins/CsvExport/CsvExport_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/CsvExport/CsvExport_pt_BR.qm and /dev/null differ diff --git a/Plugins/CsvExport/CsvExport_pt_BR.ts b/Plugins/CsvExport/CsvExport_pt_BR.ts deleted file mode 100644 index 0c8b1d9..0000000 --- a/Plugins/CsvExport/CsvExport_pt_BR.ts +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CsvExport - - - Column names in first row - - - - - Column separator: - - - - - , (comma) - - - - - ; (semicolon) - - - - - \t (tab) - - - - - (whitespace) - - - - - Custom: - - - - - Export NULL values as: - - - - - Empty string - - - - - Enter the custom separator character. - - - - diff --git a/Plugins/CsvExport/CsvExport_ro_RO.qm b/Plugins/CsvExport/CsvExport_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/CsvExport/CsvExport_ro_RO.qm and /dev/null differ diff --git a/Plugins/CsvExport/CsvExport_ro_RO.ts b/Plugins/CsvExport/CsvExport_ro_RO.ts deleted file mode 100644 index d2b0916..0000000 --- a/Plugins/CsvExport/CsvExport_ro_RO.ts +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CsvExport - - - Column names in first row - - - - - Column separator: - - - - - , (comma) - - - - - ; (semicolon) - - - - - \t (tab) - - - - - (whitespace) - - - - - Custom: - - - - - Export NULL values as: - - - - - Empty string - - - - - Enter the custom separator character. - - - - diff --git a/Plugins/CsvExport/CsvExport_ru.qm b/Plugins/CsvExport/CsvExport_ru.qm deleted file mode 100644 index d21e86a..0000000 Binary files a/Plugins/CsvExport/CsvExport_ru.qm and /dev/null differ diff --git a/Plugins/CsvExport/CsvExport_ru.ts b/Plugins/CsvExport/CsvExport_ru.ts deleted file mode 100644 index 5cf441b..0000000 --- a/Plugins/CsvExport/CsvExport_ru.ts +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CsvExport - - - Column names in first row - Имена Ñтолбцов в первой Ñтроке - - - - Column separator: - Разделитель Ñтолбцов: - - - - , (comma) - , (запÑтаÑ) - - - - ; (semicolon) - ; (точка Ñ Ð·Ð°Ð¿Ñтой) - - - - \t (tab) - \t (табулÑциÑ) - - - - (whitespace) - (пробел) - - - - Custom: - Произвольный: - - - - Export NULL values as: - ЭкÑпортировать Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL как: - - - - Empty string - ПуÑÑ‚Ð°Ñ Ñтрока - - - - Enter the custom separator character. - Введите произвольный Ñимвол разделителÑ. - - - diff --git a/Plugins/CsvExport/CsvExport_sk.qm b/Plugins/CsvExport/CsvExport_sk.qm deleted file mode 100644 index ea246cd..0000000 Binary files a/Plugins/CsvExport/CsvExport_sk.qm and /dev/null differ diff --git a/Plugins/CsvExport/CsvExport_sk.ts b/Plugins/CsvExport/CsvExport_sk.ts deleted file mode 100644 index e283fc3..0000000 --- a/Plugins/CsvExport/CsvExport_sk.ts +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CsvExport - - - Column names in first row - Názvy stĺpcov v prvom riadku - - - - Column separator: - OddeľovaÄ stĺpcov: - - - - , (comma) - , (Äiarka) - - - - ; (semicolon) - ; (bodkoÄiarka) - - - - \t (tab) - \t (tabulátor) - - - - (whitespace) - (medzera) - - - - Custom: - Iný: - - - - Export NULL values as: - ExportovaÅ¥ NULL hodnoty ako: - - - - Empty string - Prázdny reÅ¥azec - - - - Enter the custom separator character. - Zadajte vlasntý oddeľovaÄ. - - - diff --git a/Plugins/CsvExport/CsvExport_zh_CN.qm b/Plugins/CsvExport/CsvExport_zh_CN.qm deleted file mode 100644 index 8699e2c..0000000 Binary files a/Plugins/CsvExport/CsvExport_zh_CN.qm and /dev/null differ diff --git a/Plugins/CsvExport/CsvExport_zh_CN.ts b/Plugins/CsvExport/CsvExport_zh_CN.ts deleted file mode 100644 index 6d31621..0000000 --- a/Plugins/CsvExport/CsvExport_zh_CN.ts +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CsvExport - - - Column names in first row - 第一行显示列å - - - - Column separator: - 列分隔符: - - - - , (comma) - ,(逗å·ï¼‰ - - - - ; (semicolon) - ;(分å·ï¼‰ - - - - \t (tab) - \t(tabä½ï¼‰ - - - - (whitespace) - (空格) - - - - Custom: - 自定义: - - - - Export NULL values as: - NULL导出为: - - - - Empty string - 空字符串 - - - - Enter the custom separator character. - 输入自定义分隔符 。 - - - diff --git a/Plugins/CsvExport/csvexport.cpp b/Plugins/CsvExport/csvexport.cpp index c3698af..6155a12 100644 --- a/Plugins/CsvExport/csvexport.cpp +++ b/Plugins/CsvExport/csvexport.cpp @@ -188,11 +188,11 @@ void CsvExport::defineCsvFormat() bool CsvExport::init() { - Q_INIT_RESOURCE(csvexport); + SQLS_INIT_RESOURCE(csvexport); return GenericExportPlugin::init(); } void CsvExport::deinit() { - Q_CLEANUP_RESOURCE(csvexport); + SQLS_CLEANUP_RESOURCE(csvexport); } diff --git a/Plugins/CsvExport/csvexport.qrc b/Plugins/CsvExport/csvexport.qrc index 7168c99..21d7213 100644 --- a/Plugins/CsvExport/csvexport.qrc +++ b/Plugins/CsvExport/csvexport.qrc @@ -2,17 +2,4 @@ CsvExport.ui - - CsvExport_ro_RO.qm - - CsvExport_de.qm - CsvExport_pl.qm - CsvExport_ru.qm - CsvExport_fr.qm - CsvExport_sk.qm - CsvExport_zh_CN.qm - - - - diff --git a/Plugins/CsvExport/translations/CsvExport.ts b/Plugins/CsvExport/translations/CsvExport.ts new file mode 100644 index 0000000..06583b5 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + + + + + Column separator: + + + + + , (comma) + + + + + ; (semicolon) + + + + + \t (tab) + + + + + (whitespace) + + + + + Custom: + + + + + Export NULL values as: + + + + + Empty string + + + + + Enter the custom separator character. + + + + diff --git a/Plugins/CsvExport/translations/CsvExport_af_ZA.ts b/Plugins/CsvExport/translations/CsvExport_af_ZA.ts new file mode 100644 index 0000000..60b8774 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_af_ZA.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_ar_SA.ts b/Plugins/CsvExport/translations/CsvExport_ar_SA.ts new file mode 100644 index 0000000..f0053fd --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_ar_SA.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_ca_ES.ts b/Plugins/CsvExport/translations/CsvExport_ca_ES.ts new file mode 100644 index 0000000..0da0ff3 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_ca_ES.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_cs_CZ.ts b/Plugins/CsvExport/translations/CsvExport_cs_CZ.ts new file mode 100644 index 0000000..a45210a --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_cs_CZ.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (Äárka) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_da_DK.ts b/Plugins/CsvExport/translations/CsvExport_da_DK.ts new file mode 100644 index 0000000..c1b750d --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_da_DK.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_de_DE.ts b/Plugins/CsvExport/translations/CsvExport_de_DE.ts new file mode 100644 index 0000000..ad446b0 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_de_DE.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Spaltenname als erste Zeile verwenden + + + + Column separator: + Trennzeichen: + + + + , (comma) + , (Komma) + + + + ; (semicolon) + ; (Semikolon) + + + + \t (tab) + \t (Tabulator) + + + + (whitespace) + (Leerzeichen) + + + + Custom: + Benutzerdefiniert: + + + + Export NULL values as: + NULL-Werte exportieren als: + + + + Empty string + Leerer Text + + + + Enter the custom separator character. + Geben Sie ein benutzerdefiniertes Trennzeichen ein. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_el_GR.ts b/Plugins/CsvExport/translations/CsvExport_el_GR.ts new file mode 100644 index 0000000..6774d44 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_el_GR.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_en_US.ts b/Plugins/CsvExport/translations/CsvExport_en_US.ts new file mode 100644 index 0000000..92f1315 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_en_US.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_es_ES.ts b/Plugins/CsvExport/translations/CsvExport_es_ES.ts new file mode 100644 index 0000000..0fd0772 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_es_ES.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Nombres de columna en la primera fila + + + + Column separator: + Separador de columna: + + + + , (comma) + , (coma) + + + + ; (semicolon) + ; (punto y coma) + + + + \t (tab) + \t (tabulación) + + + + (whitespace) + (espacio en blanco) + + + + Custom: + Personalizado: + + + + Export NULL values as: + Exportar valores NULOS como: + + + + Empty string + Cadena vacía + + + + Enter the custom separator character. + Ingresa el carácter separador personalizado. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_fa_IR.ts b/Plugins/CsvExport/translations/CsvExport_fa_IR.ts new file mode 100644 index 0000000..c399ac6 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_fa_IR.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_fi_FI.ts b/Plugins/CsvExport/translations/CsvExport_fi_FI.ts new file mode 100644 index 0000000..fa7594f --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_fi_FI.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_fr_FR.ts b/Plugins/CsvExport/translations/CsvExport_fr_FR.ts new file mode 100644 index 0000000..0906d26 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_fr_FR.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Noms des colonnes dans la 1ère ligne + + + + Column separator: + Séparateur de colonnes : + + + + , (comma) + , (virgule) + + + + ; (semicolon) + ; (point virgule) + + + + \t (tab) + \t (tabulation) + + + + (whitespace) + (espace) + + + + Custom: + personnalisation : + + + + Export NULL values as: + Exporter la valeur NULL comme : + + + + Empty string + Chaine de caractères vide + + + + Enter the custom separator character. + Saisir le caractère séparateur personnalisé. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_he_IL.ts b/Plugins/CsvExport/translations/CsvExport_he_IL.ts new file mode 100644 index 0000000..a597c14 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_he_IL.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + שמות עמודות בשורה ר×שונה + + + + Column separator: + מפריד עמודות: + + + + , (comma) + , (פסיק) + + + + ; (semicolon) + ; (×תנח) + + + + \t (tab) + \t (ט×ב) + + + + (whitespace) + (מרחב לבן) + + + + Custom: + מות××: + + + + Export NULL values as: + ×™×¦×•× ×¢×¨×›×™ NULL ×›: + + + + Empty string + מחרוזת ריקה + + + + Enter the custom separator character. + הזנת תו מפריד מות××. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_hu_HU.ts b/Plugins/CsvExport/translations/CsvExport_hu_HU.ts new file mode 100644 index 0000000..9ed75d7 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_hu_HU.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_it_IT.ts b/Plugins/CsvExport/translations/CsvExport_it_IT.ts new file mode 100644 index 0000000..2e70235 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_it_IT.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Nomi delle colonne nella prima riga + + + + Column separator: + Separatore di colonna: + + + + , (comma) + , (virgola) + + + + ; (semicolon) + ; (punto e virgola) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (spazio bianco) + + + + Custom: + Personalizzato: + + + + Export NULL values as: + Esporta valori NULL come: + + + + Empty string + Stringa vuota + + + + Enter the custom separator character. + Inserisci il carattere del separatore personalizzato. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_ja_JP.ts b/Plugins/CsvExport/translations/CsvExport_ja_JP.ts new file mode 100644 index 0000000..78b66b3 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_ja_JP.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_kaa.ts b/Plugins/CsvExport/translations/CsvExport_kaa.ts new file mode 100644 index 0000000..e124712 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_kaa.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_ko_KR.ts b/Plugins/CsvExport/translations/CsvExport_ko_KR.ts new file mode 100644 index 0000000..fcadb9d --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_ko_KR.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_nl_NL.ts b/Plugins/CsvExport/translations/CsvExport_nl_NL.ts new file mode 100644 index 0000000..4d9d9fd --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_nl_NL.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_no_NO.ts b/Plugins/CsvExport/translations/CsvExport_no_NO.ts new file mode 100644 index 0000000..3f5cec2 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_no_NO.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_pl_PL.ts b/Plugins/CsvExport/translations/CsvExport_pl_PL.ts new file mode 100644 index 0000000..52e72b9 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_pl_PL.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Nazwy kolumn w pierwszym wierszu + + + + Column separator: + Separator kolumny: + + + + , (comma) + , (przecinek) + + + + ; (semicolon) + ; (Å›rednik) + + + + \t (tab) + \t (znak tabulacji) + + + + (whitespace) + (spacja) + + + + Custom: + WÅ‚asny: + + + + Export NULL values as: + Eksportuj wartoÅ›ci NULL jako: + + + + Empty string + Pusty Å‚aÅ„cuch + + + + Enter the custom separator character. + Wprowadź wÅ‚asny znak separatora. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_pt_BR.ts b/Plugins/CsvExport/translations/CsvExport_pt_BR.ts new file mode 100644 index 0000000..fa449f7 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_pt_BR.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Nomes das colunas na primeira linha + + + + Column separator: + Separador de coluna: + + + + , (comma) + , (vírgula) + + + + ; (semicolon) + ;(ponto-e-vírgula) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (espaço em branco) + + + + Custom: + Personalizar: + + + + Export NULL values as: + Exportar valores NULL como: + + + + Empty string + String vazia + + + + Enter the custom separator character. + Digite o caractere separador personalizado. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_pt_PT.ts b/Plugins/CsvExport/translations/CsvExport_pt_PT.ts new file mode 100644 index 0000000..2b06c60 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_pt_PT.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_ro_RO.ts b/Plugins/CsvExport/translations/CsvExport_ro_RO.ts new file mode 100644 index 0000000..e52f3e9 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_ro_RO.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_ru_RU.ts b/Plugins/CsvExport/translations/CsvExport_ru_RU.ts new file mode 100644 index 0000000..85d02d0 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_ru_RU.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Имена Ñтолбцов в первой Ñтроке + + + + Column separator: + Разделитель Ñтолбцов: + + + + , (comma) + , (запÑтаÑ) + + + + ; (semicolon) + ; (точка Ñ Ð·Ð°Ð¿Ñтой) + + + + \t (tab) + \t (табулÑциÑ) + + + + (whitespace) + (пробел) + + + + Custom: + Произвольный: + + + + Export NULL values as: + ЭкÑпортировать Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL как: + + + + Empty string + ПуÑÑ‚Ð°Ñ Ñтрока + + + + Enter the custom separator character. + Введите произвольный Ñимвол разделителÑ. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_sk_SK.ts b/Plugins/CsvExport/translations/CsvExport_sk_SK.ts new file mode 100644 index 0000000..6d5d94d --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_sk_SK.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Názvy stĺpcov v prvom riadku + + + + Column separator: + OddeľovaÄ stĺpcov: + + + + , (comma) + , (Äiarka) + + + + ; (semicolon) + ; (bodkoÄiarka) + + + + \t (tab) + \t (tabulátor) + + + + (whitespace) + (medzera) + + + + Custom: + Iný: + + + + Export NULL values as: + ExportovaÅ¥ NULL hodnoty ako: + + + + Empty string + Prázdny reÅ¥azec + + + + Enter the custom separator character. + Zadajte vlasntý oddeľovaÄ. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_sr_SP.ts b/Plugins/CsvExport/translations/CsvExport_sr_SP.ts new file mode 100644 index 0000000..d95f8f1 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_sr_SP.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_sv_SE.ts b/Plugins/CsvExport/translations/CsvExport_sv_SE.ts new file mode 100644 index 0000000..11fd2cc --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_sv_SE.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Kolumnnamn i första raden + + + + Column separator: + Kolumnseparator: + + + + , (comma) +  (Kommatecken) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (blankt) + + + + Custom: + Anpassad: + + + + Export NULL values as: + Exportera NULL-värden som: + + + + Empty string + [tom sträng] + + + + Enter the custom separator character. + Ange det anpassade separatortecknet. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_tr_TR.ts b/Plugins/CsvExport/translations/CsvExport_tr_TR.ts new file mode 100644 index 0000000..2eb77ab --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_tr_TR.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + İlk satırdaki kolon isimleri + + + + Column separator: + Kolon ayracı: + + + + , (comma) + , (virgül) + + + + ; (semicolon) + ; (noktalı virgül) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (boÅŸluk) + + + + Custom: + Özel: + + + + Export NULL values as: + NULL deÄŸerleri şöyle çıkar: + + + + Empty string + BoÅŸ string + + + + Enter the custom separator character. + Özel ayraç karakterini girin. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_uk_UA.ts b/Plugins/CsvExport/translations/CsvExport_uk_UA.ts new file mode 100644 index 0000000..31a6586 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_uk_UA.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_vi_VN.ts b/Plugins/CsvExport/translations/CsvExport_vi_VN.ts new file mode 100644 index 0000000..1070159 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_vi_VN.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + Column names in first row + + + + Column separator: + Column separator: + + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Export NULL values as: + Export NULL values as: + + + + Empty string + Empty string + + + + Enter the custom separator character. + Enter the custom separator character. + + + diff --git a/Plugins/CsvExport/translations/CsvExport_zh_CN.ts b/Plugins/CsvExport/translations/CsvExport_zh_CN.ts new file mode 100644 index 0000000..8d0b239 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_zh_CN.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + 第一行是列å + + + + Column separator: + 列分隔符: + + + + , (comma) + ,(逗å·ï¼‰ + + + + ; (semicolon) + ;(分å·ï¼‰ + + + + \t (tab) + \t(制表符) + + + + (whitespace) + (空格) + + + + Custom: + 自定义: + + + + Export NULL values as: + NULL 值导出为: + + + + Empty string + 空字符串 + + + + Enter the custom separator character. + 请输入自定义的分隔符。 + + + diff --git a/Plugins/CsvExport/translations/CsvExport_zh_TW.ts b/Plugins/CsvExport/translations/CsvExport_zh_TW.ts new file mode 100644 index 0000000..82ada89 --- /dev/null +++ b/Plugins/CsvExport/translations/CsvExport_zh_TW.ts @@ -0,0 +1,57 @@ + + + + + CsvExport + + + Column names in first row + 第一行是列å + + + + Column separator: + 列分隔符: + + + + , (comma) + , (逗號) + + + + ; (semicolon) + ; (分號) + + + + \t (tab) + \t (製表符) + + + + (whitespace) + (空格) + + + + Custom: + 自訂: + + + + Export NULL values as: + NULL 值匯出為: + + + + Empty string + 空字串 + + + + Enter the custom separator character. + 請輸入自訂的分隔符。 + + + diff --git a/Plugins/CsvImport/CsvImport.pro b/Plugins/CsvImport/CsvImport.pro index ed3eb2c..24d425b 100644 --- a/Plugins/CsvImport/CsvImport.pro +++ b/Plugins/CsvImport/CsvImport.pro @@ -28,16 +28,15 @@ RESOURCES += \ csvimport.qrc -TRANSLATIONS += CsvImport_ro_RO.ts \ - CsvImport_de.ts \ - CsvImport_it.ts \ - CsvImport_zh_CN.ts \ - CsvImport_sk.ts \ - CsvImport_ru.ts \ - CsvImport_pt_BR.ts \ - CsvImport_fr.ts \ - CsvImport_es.ts \ - CsvImport_pl.ts + + + + + + + + + diff --git a/Plugins/CsvImport/CsvImportOptions.ui b/Plugins/CsvImport/CsvImportOptions.ui index f0f2728..1ed0f49 100644 --- a/Plugins/CsvImport/CsvImportOptions.ui +++ b/Plugins/CsvImport/CsvImportOptions.ui @@ -7,13 +7,26 @@ 0 0 365 - 106 + 128 Form + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + First line represents CSV column names + + + CsvImport.FirstRowAsColumns + + + @@ -46,23 +59,33 @@ - - + + + + Column separator: + + + + + - <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + If enabled, then text value provided on the right will be interpreted as NULL - First line represents CSV column names + NULL values: - CsvImport.FirstRowAsColumns + CsvImport.NullValues - - - - Field separator: + + + + If your CSV data contains null values, define how are they represented in the CSV. + + + CsvImport.NullValueString @@ -79,26 +102,16 @@ - - + + - If enabled, then text value provided on the right will be interpreted as NULL + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> - NULL values: + Interpret " as a value quotation mark - CsvImport.NullValues - - - - - - - If your CSV data contains null values, define how are they represented in the CSV. - - - CsvImport.NullValueString + CsvImport.QuotationMark diff --git a/Plugins/CsvImport/CsvImport_de.qm b/Plugins/CsvImport/CsvImport_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/CsvImport/CsvImport_de.qm and /dev/null differ diff --git a/Plugins/CsvImport/CsvImport_de.ts b/Plugins/CsvImport/CsvImport_de.ts deleted file mode 100644 index 891bae9..0000000 --- a/Plugins/CsvImport/CsvImport_de.ts +++ /dev/null @@ -1,85 +0,0 @@ - - - - - CsvImport - - - Cannot read file %1 - - - - - Could not find any data in the file %1. - - - - - Enter the custom separator character. - - - - - CSV files (*.csv);;Text files (*.txt);;All files (*) - - - - - csvImportOptions - - - , (comma) - - - - - ; (semicolon) - - - - - \t (tab) - - - - - (whitespace) - - - - - Custom: - - - - - <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> - - - - - First line represents CSV column names - - - - - Field separator: - - - - - If enabled, then text value provided on the right will be interpreted as NULL - - - - - NULL values: - - - - - If your CSV data contains null values, define how are they represented in the CSV. - - - - diff --git a/Plugins/CsvImport/CsvImport_es.qm b/Plugins/CsvImport/CsvImport_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/CsvImport/CsvImport_es.qm and /dev/null differ diff --git a/Plugins/CsvImport/CsvImport_es.ts b/Plugins/CsvImport/CsvImport_es.ts deleted file mode 100644 index 2d62a16..0000000 --- a/Plugins/CsvImport/CsvImport_es.ts +++ /dev/null @@ -1,85 +0,0 @@ - - - - - CsvImport - - - Cannot read file %1 - - - - - Could not find any data in the file %1. - - - - - Enter the custom separator character. - - - - - CSV files (*.csv);;Text files (*.txt);;All files (*) - - - - - csvImportOptions - - - , (comma) - - - - - ; (semicolon) - - - - - \t (tab) - - - - - (whitespace) - - - - - Custom: - - - - - <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> - - - - - First line represents CSV column names - - - - - Field separator: - - - - - If enabled, then text value provided on the right will be interpreted as NULL - - - - - NULL values: - - - - - If your CSV data contains null values, define how are they represented in the CSV. - - - - diff --git a/Plugins/CsvImport/CsvImport_fr.qm b/Plugins/CsvImport/CsvImport_fr.qm deleted file mode 100644 index f83fb44..0000000 Binary files a/Plugins/CsvImport/CsvImport_fr.qm and /dev/null differ diff --git a/Plugins/CsvImport/CsvImport_fr.ts b/Plugins/CsvImport/CsvImport_fr.ts deleted file mode 100644 index fdc6108..0000000 --- a/Plugins/CsvImport/CsvImport_fr.ts +++ /dev/null @@ -1,93 +0,0 @@ - - - - - CsvImport - - - Cannot read file %1 - Impossible de lire le fichier %1 - - - - Could not find any data in the file %1. - Impossible de trouver des données dans le fichier %1. - - - - Enter the custom separator character. - Saisir le caractère séparateur. - - - Enter the value that will be interpreted as a NULL. - Saisir la valeur qui sera interprétée comme NULL. - - - - CSV files (*.csv);;Text files (*.txt);;All files (*) - Fichiers CSV (*.csv);;Fichiers texte (*.txt);;Tous les fichiers (*) - - - - csvImportOptions - - - , (comma) - , (virgule) - - - - ; (semicolon) - ; (point virgule) - - - - \t (tab) - \t (tabulation) - - - - (whitespace) - (Espace) - - - - Custom: - Personalisé : - - - - <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> - <p>Ceci est possible si la permière ligne de votre fichier CSV représente le nom des colonnes.</p> - - - - First line represents CSV column names - - - - Skip first row of data - Aller à la première ligne de données - - - - Field separator: - Champ séparateur : - - - - If enabled, then text value provided on the right will be interpreted as NULL - - - - - NULL values: - Valeurs NULL : - - - - If your CSV data contains null values, define how are they represented in the CSV. - Si votre CSV de données contient des valeurs nulles, définissez leur représentation dans le CSV. - - - diff --git a/Plugins/CsvImport/CsvImport_it.qm b/Plugins/CsvImport/CsvImport_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/CsvImport/CsvImport_it.qm and /dev/null differ diff --git a/Plugins/CsvImport/CsvImport_it.ts b/Plugins/CsvImport/CsvImport_it.ts deleted file mode 100644 index 8d6c029..0000000 --- a/Plugins/CsvImport/CsvImport_it.ts +++ /dev/null @@ -1,85 +0,0 @@ - - - - - CsvImport - - - Cannot read file %1 - - - - - Could not find any data in the file %1. - - - - - Enter the custom separator character. - - - - - CSV files (*.csv);;Text files (*.txt);;All files (*) - - - - - csvImportOptions - - - , (comma) - - - - - ; (semicolon) - - - - - \t (tab) - - - - - (whitespace) - - - - - Custom: - - - - - <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> - - - - - First line represents CSV column names - - - - - Field separator: - - - - - If enabled, then text value provided on the right will be interpreted as NULL - - - - - NULL values: - - - - - If your CSV data contains null values, define how are they represented in the CSV. - - - - diff --git a/Plugins/CsvImport/CsvImport_pl.qm b/Plugins/CsvImport/CsvImport_pl.qm deleted file mode 100644 index 6f56305..0000000 Binary files a/Plugins/CsvImport/CsvImport_pl.qm and /dev/null differ diff --git a/Plugins/CsvImport/CsvImport_pl.ts b/Plugins/CsvImport/CsvImport_pl.ts deleted file mode 100644 index d53af99..0000000 --- a/Plugins/CsvImport/CsvImport_pl.ts +++ /dev/null @@ -1,97 +0,0 @@ - - - - - CsvImport - - - Cannot read file %1 - Nie można odczytać pliku %1 - - - - Could not find any data in the file %1. - Nie znaleziono danych w pliku %1. - - - - Enter the custom separator character. - Wprowadź wÅ‚asny znak separatora. - - - Enter the value that will be interpreted as a NULL. - Wprowadź wartość, która bÄ™dzie interpretowana jako NULL. - - - - CSV files (*.csv);;Text files (*.txt);;All files (*) - Pliki CSV (*.csv);;Pliki tekstowe (*.txt);;Wszystkie pliki (*) - - - - csvImportOptions - - - , (comma) - , (przecinek) - - - - ; (semicolon) - ; (Å›rednik) - - - - \t (tab) - \t (znak tabulacji) - - - - (whitespace) - (spacja) - - - - Custom: - WÅ‚asny: - - - - <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> - <p>Włącz to, jeÅ›li pierwsza linia danych w twoim pliku CSV reprezentuje nazwy kolumn. Nie chcesz, aby nazwy kolumn zostaÅ‚y zaimportowane jako zwykÅ‚e dane.</p> - - - - First line represents CSV column names - Pierwsza linia reprezentuje nazwy kolumn CSV - - - Skip first row of data - PomiÅ„ pierwszy wiersz danych - - - First row represents column names - Pierwszy wiersz reprezentuje nazwy kolumn - - - - Field separator: - Separator pól: - - - - If enabled, then text value provided on the right will be interpreted as NULL - Kiedy włączone, to wartość tekstowa podana z prawej strony bÄ™dzie interpretowana jako NULL - - - - NULL values: - WartoÅ›ci NULL: - - - - If your CSV data contains null values, define how are they represented in the CSV. - JeÅ›li twoje dane CSV zawierajÄ… wartoÅ›ci null, zdefiniuj jak sÄ… one reprezentowane w CSV. - - - diff --git a/Plugins/CsvImport/CsvImport_pt_BR.qm b/Plugins/CsvImport/CsvImport_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/CsvImport/CsvImport_pt_BR.qm and /dev/null differ diff --git a/Plugins/CsvImport/CsvImport_pt_BR.ts b/Plugins/CsvImport/CsvImport_pt_BR.ts deleted file mode 100644 index d1e3182..0000000 --- a/Plugins/CsvImport/CsvImport_pt_BR.ts +++ /dev/null @@ -1,85 +0,0 @@ - - - - - CsvImport - - - Cannot read file %1 - - - - - Could not find any data in the file %1. - - - - - Enter the custom separator character. - - - - - CSV files (*.csv);;Text files (*.txt);;All files (*) - - - - - csvImportOptions - - - , (comma) - - - - - ; (semicolon) - - - - - \t (tab) - - - - - (whitespace) - - - - - Custom: - - - - - <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> - - - - - First line represents CSV column names - - - - - Field separator: - - - - - If enabled, then text value provided on the right will be interpreted as NULL - - - - - NULL values: - - - - - If your CSV data contains null values, define how are they represented in the CSV. - - - - diff --git a/Plugins/CsvImport/CsvImport_ro_RO.qm b/Plugins/CsvImport/CsvImport_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/CsvImport/CsvImport_ro_RO.qm and /dev/null differ diff --git a/Plugins/CsvImport/CsvImport_ro_RO.ts b/Plugins/CsvImport/CsvImport_ro_RO.ts deleted file mode 100644 index 0a33907..0000000 --- a/Plugins/CsvImport/CsvImport_ro_RO.ts +++ /dev/null @@ -1,85 +0,0 @@ - - - - - CsvImport - - - Cannot read file %1 - - - - - Could not find any data in the file %1. - - - - - Enter the custom separator character. - - - - - CSV files (*.csv);;Text files (*.txt);;All files (*) - - - - - csvImportOptions - - - , (comma) - - - - - ; (semicolon) - - - - - \t (tab) - - - - - (whitespace) - - - - - Custom: - - - - - <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> - - - - - First line represents CSV column names - - - - - Field separator: - - - - - If enabled, then text value provided on the right will be interpreted as NULL - - - - - NULL values: - - - - - If your CSV data contains null values, define how are they represented in the CSV. - - - - diff --git a/Plugins/CsvImport/CsvImport_ru.qm b/Plugins/CsvImport/CsvImport_ru.qm deleted file mode 100644 index 08fab26..0000000 Binary files a/Plugins/CsvImport/CsvImport_ru.qm and /dev/null differ diff --git a/Plugins/CsvImport/CsvImport_ru.ts b/Plugins/CsvImport/CsvImport_ru.ts deleted file mode 100644 index 0f7d633..0000000 --- a/Plugins/CsvImport/CsvImport_ru.ts +++ /dev/null @@ -1,93 +0,0 @@ - - - - - CsvImport - - - Cannot read file %1 - Ðевозможно прочитать файл %1 - - - - Could not find any data in the file %1. - Ðевозможно найти данные в файле %1. - - - - Enter the custom separator character. - Введите произвольный Ñимвол разделителÑ. - - - Enter the value that will be interpreted as a NULL. - Введите значение, которое будет интерпретировано как NULL. - - - - CSV files (*.csv);;Text files (*.txt);;All files (*) - Файлы CSV (*.csv);;ТекÑтовые файлы (*.txt);;Ð’Ñе файлы (*) - - - - csvImportOptions - - - , (comma) - , (запÑтаÑ) - - - - ; (semicolon) - ; (точка Ñ Ð·Ð°Ð¿Ñтой) - - - - \t (tab) - \t (табулÑциÑ) - - - - (whitespace) - (пробел) - - - - Custom: - Произвольный: - - - - <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> - <p>Ðктивируйте Ñту опцию, еÑли в первой Ñтроке файла CSV ÑодержатÑÑ Ð¸Ð¼ÐµÐ½Ð° Ñтолбцов. Имена Ñтолбцов не нужно импортировать как обычные данные.</p> - - - - First line represents CSV column names - Имена Ñтолбцов в первой Ñтроке - - - Skip first row of data - ПропуÑтить первую Ñтроку данных - - - - Field separator: - Разделитель полÑ: - - - - If enabled, then text value provided on the right will be interpreted as NULL - При выборе данной опции указанное в поле Ñправа текÑтовое значение будет интерпретировано как NULL - - - - NULL values: - Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL: - - - - If your CSV data contains null values, define how are they represented in the CSV. - ЕÑли данные в CSV Ñодержат Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL, укажите, как они предÑтавлены в файле CSV - - - diff --git a/Plugins/CsvImport/CsvImport_sk.qm b/Plugins/CsvImport/CsvImport_sk.qm deleted file mode 100644 index 5e88aaa..0000000 Binary files a/Plugins/CsvImport/CsvImport_sk.qm and /dev/null differ diff --git a/Plugins/CsvImport/CsvImport_sk.ts b/Plugins/CsvImport/CsvImport_sk.ts deleted file mode 100644 index f607ab5..0000000 --- a/Plugins/CsvImport/CsvImport_sk.ts +++ /dev/null @@ -1,89 +0,0 @@ - - - - - CsvImport - - - Cannot read file %1 - Nemôžem ÄítaÅ¥ súbor %1 - - - - Could not find any data in the file %1. - Nemôžem nájsÅ¥ dáta v súbore %1. - - - - Enter the custom separator character. - Zadajte vlastný oddeľovaÄ. - - - Enter the value that will be interpreted as a NULL. - Zadajte hodnotu, ktorá bude interpretovaná ako NULL. - - - - CSV files (*.csv);;Text files (*.txt);;All files (*) - CSV súbory (*.csv);;Textové súbory (*.txt);;VÅ¡etky súbory (*) - - - - csvImportOptions - - - , (comma) - , (Äiarka) - - - - ; (semicolon) - ; (bodkoÄiarka) - - - - \t (tab) - \t (tabulátor) - - - - (whitespace) - (medzera) - - - - Custom: - Iný: - - - - <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> - <p>ZaÅ¡krtnite túto voľbu ak prvý riadok v CSV súbore obsahuje názvy stĺpcov a nechcete aby boli naimportované do tabuľky</p> - - - - First line represents CSV column names - Názvy stĺpcov v prvom riadku - - - - Field separator: - OddeľovaÄ: - - - - If enabled, then text value provided on the right will be interpreted as NULL - - - - - NULL values: - NULL hodnoty: - - - - If your CSV data contains null values, define how are they represented in the CSV. - Ak váš CSV súbor obsahuje null hodnoty, zadajte ako sú reprezentované v CSV. - - - diff --git a/Plugins/CsvImport/CsvImport_zh_CN.qm b/Plugins/CsvImport/CsvImport_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/Plugins/CsvImport/CsvImport_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/Plugins/CsvImport/CsvImport_zh_CN.ts b/Plugins/CsvImport/CsvImport_zh_CN.ts deleted file mode 100644 index 9bca9fb..0000000 --- a/Plugins/CsvImport/CsvImport_zh_CN.ts +++ /dev/null @@ -1,85 +0,0 @@ - - - - - CsvImport - - - Cannot read file %1 - - - - - Could not find any data in the file %1. - - - - - Enter the custom separator character. - - - - - CSV files (*.csv);;Text files (*.txt);;All files (*) - - - - - csvImportOptions - - - , (comma) - - - - - ; (semicolon) - - - - - \t (tab) - - - - - (whitespace) - - - - - Custom: - - - - - <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> - - - - - First line represents CSV column names - - - - - Field separator: - - - - - If enabled, then text value provided on the right will be interpreted as NULL - - - - - NULL values: - - - - - If your CSV data contains null values, define how are they represented in the CSV. - - - - diff --git a/Plugins/CsvImport/csvimport.cpp b/Plugins/CsvImport/csvimport.cpp index f8edcbc..5ee71df 100644 --- a/Plugins/CsvImport/csvimport.cpp +++ b/Plugins/CsvImport/csvimport.cpp @@ -86,6 +86,7 @@ void CsvImport::defineCsvFormat() csvFormat.rowSeparators = QStringList({"\r\n", "\n", "\r"}); csvFormat.multipleRowSeparators = true; csvFormat.strictRowSeparator = true; + csvFormat.quotationMark = cfg.CsvImport.QuotationMark.get(); switch (cfg.CsvImport.Separator.get()) { @@ -192,11 +193,11 @@ QString CsvImport::getFileFilter() const bool CsvImport::init() { - Q_INIT_RESOURCE(csvimport); + SQLS_INIT_RESOURCE(csvimport); return GenericPlugin::init(); } void CsvImport::deinit() { - Q_CLEANUP_RESOURCE(csvimport); + SQLS_CLEANUP_RESOURCE(csvimport); } diff --git a/Plugins/CsvImport/csvimport.h b/Plugins/CsvImport/csvimport.h index 3980d86..e1f1f6c 100644 --- a/Plugins/CsvImport/csvimport.h +++ b/Plugins/CsvImport/csvimport.h @@ -14,6 +14,7 @@ CFG_CATEGORIES(CsvImportConfig, CFG_ENTRY(QString, CustomSeparator, QString()) CFG_ENTRY(bool, NullValues, false) CFG_ENTRY(QString, NullValueString, QString()) + CFG_ENTRY(bool, QuotationMark, true) ) ) diff --git a/Plugins/CsvImport/csvimport.qrc b/Plugins/CsvImport/csvimport.qrc index 1c27100..a071122 100644 --- a/Plugins/CsvImport/csvimport.qrc +++ b/Plugins/CsvImport/csvimport.qrc @@ -2,17 +2,4 @@ CsvImportOptions.ui - - CsvImport_ro_RO.qm - - CsvImport_de.qm - CsvImport_pl.qm - CsvImport_ru.qm - CsvImport_fr.qm - CsvImport_sk.qm - CsvImport_zh_CN.qm - - - - diff --git a/Plugins/CsvImport/translations/CsvImport.ts b/Plugins/CsvImport/translations/CsvImport.ts new file mode 100644 index 0000000..333c2f0 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + + + + + Could not find any data in the file %1. + + + + + Enter the custom separator character. + + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + + csvImportOptions + + + , (comma) + + + + + ; (semicolon) + + + + + \t (tab) + + + + + (whitespace) + + + + + Custom: + + + + + Column separator: + + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + + Interpret " as a value quotation mark + + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + + First line represents CSV column names + + + + + If enabled, then text value provided on the right will be interpreted as NULL + + + + + NULL values: + + + + + If your CSV data contains null values, define how are they represented in the CSV. + + + + diff --git a/Plugins/CsvImport/translations/CsvImport_af_ZA.ts b/Plugins/CsvImport/translations/CsvImport_af_ZA.ts new file mode 100644 index 0000000..8c3615f --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_af_ZA.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_ar_SA.ts b/Plugins/CsvImport/translations/CsvImport_ar_SA.ts new file mode 100644 index 0000000..c5ab068 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_ar_SA.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_ca_ES.ts b/Plugins/CsvImport/translations/CsvImport_ca_ES.ts new file mode 100644 index 0000000..ee1ac5e --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_ca_ES.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_cs_CZ.ts b/Plugins/CsvImport/translations/CsvImport_cs_CZ.ts new file mode 100644 index 0000000..b7f25ea --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_cs_CZ.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_da_DK.ts b/Plugins/CsvImport/translations/CsvImport_da_DK.ts new file mode 100644 index 0000000..2d0d0ba --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_da_DK.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_de_DE.ts b/Plugins/CsvImport/translations/CsvImport_de_DE.ts new file mode 100644 index 0000000..9c344b6 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_de_DE.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Kann Datei %1 nicht lesen + + + + Could not find any data in the file %1. + Konnte keine Daten in Datei %1 finden. + + + + Enter the custom separator character. + Geben Sie ein benutzerdefiniertes Trennzeichen ein. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV-Dateien (*.csv);;Textdateien (*.txt);;Alle Dateien (*) + + + + csvImportOptions + + + , (comma) + , (Komma) + + + + ; (semicolon) + ; (Semikolon) + + + + \t (tab) + \t (Tabulator) + + + + (whitespace) + (Leerzeichen) + + + + Custom: + Benutzerdefiniert: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Aktivieren, wenn die erste Zeile in Ihrer CSV-Datei Spaltennamen repräsentiert. Sie wollen keine Spaltennamen als reguläre Daten in die Tabelle importieren.</p> + + + + First line represents CSV column names + Erste Zeile repräsentiert CSV-Spaltennamen + + + + If enabled, then text value provided on the right will be interpreted as NULL + Wenn aktiviert, wird der auf der rechten Seite angegebene Textwert als NULL interpretiert + + + + NULL values: + NULL-Werte: + + + + If your CSV data contains null values, define how are they represented in the CSV. + Wenn Ihre CSV-Daten Nullwerte enthalten, legen Sie fest, wie sie im CSV dargestellt werden. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_el_GR.ts b/Plugins/CsvImport/translations/CsvImport_el_GR.ts new file mode 100644 index 0000000..36e1dab --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_el_GR.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_en_US.ts b/Plugins/CsvImport/translations/CsvImport_en_US.ts new file mode 100644 index 0000000..cb3b72b --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_en_US.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_es_ES.ts b/Plugins/CsvImport/translations/CsvImport_es_ES.ts new file mode 100644 index 0000000..73042b9 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_es_ES.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + No se puede leer el archivo %1 + + + + Could not find any data in the file %1. + No se pudo encontrar ningún dato en el archivo %1. + + + + Enter the custom separator character. + Ingresa el carácter separador personalizado. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + Archivos CSV (*.csv);;Archivos de texto (*.txt);;Todos los archivos (*) + + + + csvImportOptions + + + , (comma) + , (coma) + + + + ; (semicolon) + ; (punto y coma) + + + + \t (tab) + \t (tabulación) + + + + (whitespace) + (espacio en blanco) + + + + Custom: + Personalizado: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Activa esto si la primera línea de datos en tu archivo CSV representa los nombres de las columnas. No quieres que los nombres de las columnas sean importados en la tabla como datos regulares.</p> + + + + First line represents CSV column names + La primera línea del CSV representa los nombres de las columnas + + + + If enabled, then text value provided on the right will be interpreted as NULL + Si se activa, el texto proporcionado a la derecha se interpretará como un NULO + + + + NULL values: + Valores NULOS: + + + + If your CSV data contains null values, define how are they represented in the CSV. + Si los datos de tu CSV contienen valores nulos, define cómo se representan en el CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_fa_IR.ts b/Plugins/CsvImport/translations/CsvImport_fa_IR.ts new file mode 100644 index 0000000..ec6702a --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_fa_IR.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_fi_FI.ts b/Plugins/CsvImport/translations/CsvImport_fi_FI.ts new file mode 100644 index 0000000..35fceef --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_fi_FI.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_fr_FR.ts b/Plugins/CsvImport/translations/CsvImport_fr_FR.ts new file mode 100644 index 0000000..1c4d316 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_fr_FR.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Impossible de lire le fichier %1 + + + + Could not find any data in the file %1. + Impossible de trouver des données dans le fichier %1. + + + + Enter the custom separator character. + Saisir le caractère séparateur. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + Fichiers CSV (*.csv);;Fichiers texte (*.txt);;Tous les fichiers (*) + + + + csvImportOptions + + + , (comma) + , (virgule) + + + + ; (semicolon) + ; (point virgule) + + + + \t (tab) + \t (tabulation) + + + + (whitespace) + (Espace) + + + + Custom: + Personalisé : + + + + Column separator: + Séparateur de colonnes : + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpréter " comme un guillemet + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Activez cette option si la première ligne de données de votre fichier CSV représente les noms des colonnes. Vous ne voulez pas que les noms de colonnes soient importés dans les tableaux en tant que données régulières.</p> + + + + First line represents CSV column names + La première ligne représente les noms de colonnes CSV + + + + If enabled, then text value provided on the right will be interpreted as NULL + Si activé, alors la valeur du texte fournie à droite sera interprétée comme NULL + + + + NULL values: + Valeurs NULL : + + + + If your CSV data contains null values, define how are they represented in the CSV. + Si votre CSV de données contient des valeurs nulles, définissez leur représentation dans le CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_he_IL.ts b/Plugins/CsvImport/translations/CsvImport_he_IL.ts new file mode 100644 index 0000000..cb02bea --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_he_IL.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + ×œ× × ×™×ª×Ÿ ×œ×§×¨×•× ×§×•×‘×¥ %1 + + + + Could not find any data in the file %1. + ×œ× × ×ž×¦×ו × ×ª×•× ×™× ×‘×§×•×‘×¥ %1. + + + + Enter the custom separator character. + הזנת תו מפריד מות××. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + קובצי CSV (*.csv);;Text files (*.txt);;כל הקבצי×(*) + + + + csvImportOptions + + + , (comma) + , (פסיק) + + + + ; (semicolon) + ; (×תנח) + + + + \t (tab) + \t (ט×ב) + + + + (whitespace) + (מרחב לבן) + + + + Custom: + מות××: + + + + Column separator: + מפריד עמודות: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + לפרש " כערך מרכ×ות + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>ניתן ל×פשר במידה ושורת ×”× ×ª×•× ×™× ×”×¨×שונה בקובץ ×”- CSV מייצגת שמות עמודות. ×œ× ×¨×¦×•×™ ×œ×™×™×‘× ×©×ž×•×ª עמודות לטבלה ×›× ×ª×•× ×™× ×¨×’×™×œ×™×.</p> + + + + First line represents CSV column names + שורה ר×שונה מייצגת ×©× ×¢×ž×•×“×ª CSV + + + + If enabled, then text value provided on the right will be interpreted as NULL + ×× ×ž×•×¤×¢×œ, ערך המלל מימין יתפרש ×›- NULL + + + + NULL values: + ערך NULL: + + + + If your CSV data contains null values, define how are they represented in the CSV. + ×× × ×ª×•× ×™ ×”- CSV ×ž×›×™×œ×™× ×¢×¨×›×™ null, × × ×œ×”×’×“×™×¨ כיצד ×”× ×ž×™×•×¦×’×™× ×‘- CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_hu_HU.ts b/Plugins/CsvImport/translations/CsvImport_hu_HU.ts new file mode 100644 index 0000000..a29cfe0 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_hu_HU.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_it_IT.ts b/Plugins/CsvImport/translations/CsvImport_it_IT.ts new file mode 100644 index 0000000..0056299 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_it_IT.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Impossibile leggere il file %1 + + + + Could not find any data in the file %1. + Impossibile trovare alcun dato nel file %1. + + + + Enter the custom separator character. + Inserisci il carattere del separatore personalizzato. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + File CSV (*.csv);;File di testo (*.txt);;Tutti i file (*) + + + + csvImportOptions + + + , (comma) + , (virgola) + + + + ; (semicolon) + ; (punto e virgola) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (spazio bianco) + + + + Custom: + Personalizzato: + + + + Column separator: + Separatore di colonna: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>Se abilitato, il carattere &quot; verrà interpretato come valore standard di quotazione CSV, causando il trattamento della stringa come singolo valore fino al successivo carattere (termine) &quot;. Se disabilitato, la citazione &; verrà trattata come qualsiasi altro carattere. Di solito si vuole che ciò sia abilitato.</p></body></html> + + + + Interpret " as a value quotation mark + Interpretare le " come valore + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Abilita questa opzione se la prima riga di dati nel tuo file CSV contiene i nomi di colonna. Se non vuoi che i nomi di colonna vengano importati nella tabella come se fossero semplici dati.</p> + + + + First line represents CSV column names + La prima riga rappresenta i nomi delle colonne CSV + + + + If enabled, then text value provided on the right will be interpreted as NULL + Se abilitato, il valore del testo fornito a destra sarà interpretato come NULL + + + + NULL values: + Valori NULL: + + + + If your CSV data contains null values, define how are they represented in the CSV. + Se i tuoi dati CSV contengono valori nulli, definisci come sono rappresentati nel CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_ja_JP.ts b/Plugins/CsvImport/translations/CsvImport_ja_JP.ts new file mode 100644 index 0000000..7e98631 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_ja_JP.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_kaa.ts b/Plugins/CsvImport/translations/CsvImport_kaa.ts new file mode 100644 index 0000000..58cd542 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_kaa.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_ko_KR.ts b/Plugins/CsvImport/translations/CsvImport_ko_KR.ts new file mode 100644 index 0000000..0057458 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_ko_KR.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_nl_NL.ts b/Plugins/CsvImport/translations/CsvImport_nl_NL.ts new file mode 100644 index 0000000..baa8400 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_nl_NL.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_no_NO.ts b/Plugins/CsvImport/translations/CsvImport_no_NO.ts new file mode 100644 index 0000000..2f98869 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_no_NO.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_pl_PL.ts b/Plugins/CsvImport/translations/CsvImport_pl_PL.ts new file mode 100644 index 0000000..e552fc6 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_pl_PL.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Nie można odczytać pliku %1 + + + + Could not find any data in the file %1. + Nie znaleziono danych w pliku %1. + + + + Enter the custom separator character. + Wprowadź wÅ‚asny znak separatora. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + Pliki CSV (*.csv);;Pliki tekstowe (*.txt);;Wszystkie pliki (*) + + + + csvImportOptions + + + , (comma) + , (przecinek) + + + + ; (semicolon) + ; (Å›rednik) + + + + \t (tab) + \t (znak tabulacji) + + + + (whitespace) + (spacja) + + + + Custom: + WÅ‚asny: + + + + Column separator: + Separator kolumny: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>JeÅ›li włączone, znak &quot; bÄ™dzie interpretowany jako standardowy znak cytowania wartoÅ›ci CSV, powodujÄ…c, że ciÄ…g bÄ™dzie traktowany jako pojedyncza wartość do nastÄ™pnego (koÅ„czÄ…cego) znaku &quot;. JeÅ›li wyłączone, to znak &quot; bÄ™dzie traktowany jak każdy inny znak. Zazwyczaj chcesz, aby to byÅ‚o włączone.</p></body></html> + + + + Interpret " as a value quotation mark + Traktuj " jako znak cytowania wartoÅ›ci + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Włącz to, jeÅ›li pierwsza linia danych w twoim pliku CSV reprezentuje nazwy kolumn. Nie chcesz, aby nazwy kolumn zostaÅ‚y zaimportowane jako zwykÅ‚e dane.</p> + + + + First line represents CSV column names + Pierwsza linia reprezentuje nazwy kolumn CSV + + + + If enabled, then text value provided on the right will be interpreted as NULL + JeÅ›li włączone, tekst podany po prawej stronie bÄ™dzie interpretowany jako NULL + + + + NULL values: + WartoÅ›ci NULL: + + + + If your CSV data contains null values, define how are they represented in the CSV. + JeÅ›li twoje dane CSV zawierajÄ… wartoÅ›ci null, zdefiniuj jak sÄ… one reprezentowane w CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_pt_BR.ts b/Plugins/CsvImport/translations/CsvImport_pt_BR.ts new file mode 100644 index 0000000..781f917 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_pt_BR.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Não é possível ler o arquivo %1 + + + + Could not find any data in the file %1. + Não foi possível encontrar nenhum dado no arquivo %1. + + + + Enter the custom separator character. + Digite o caractere separador personalizado. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + Arquivos CSV (*.csv);;Arquivos de texto (*.txt);;Todos os arquivos (*) + + + + csvImportOptions + + + , (comma) + , (vírgula) + + + + ; (semicolon) + ;(ponto-e-vírgula) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (espaço em branco) + + + + Custom: + Personalizar: + + + + Column separator: + Separador de coluna: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>Se ativado, a aspa &caracter será interpretado como marca de citação padrão de valor CSV fazendo com que o texto seja tratado como um único valor até o próximo (fim) &quot; caráter. Se desativado, a aspa &; será tratada como qualquer outro caractere. Normalmente você quer que isso esteja ativado.</p></body></html> + + + + Interpret " as a value quotation mark + Interprete " como um ponto de citação de valor + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Ative esta opção se a primeira linha de dados do seu arquivo CSV representar os nomes das colunas. Os nomes das colunas não serão importados como dados normais.</p> + + + + First line represents CSV column names + A primeira linha será os nomes das colunas + + + + If enabled, then text value provided on the right will be interpreted as NULL + Se ativado, então o valor do texto fornecido à direita será interpretado como NULL + + + + NULL values: + Valores NULL: + + + + If your CSV data contains null values, define how are they represented in the CSV. + Se os seus dados CSV contiverem valores nulos, defina como eles são representados no CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_pt_PT.ts b/Plugins/CsvImport/translations/CsvImport_pt_PT.ts new file mode 100644 index 0000000..2283f93 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_pt_PT.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_ro_RO.ts b/Plugins/CsvImport/translations/CsvImport_ro_RO.ts new file mode 100644 index 0000000..7c638c4 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_ro_RO.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_ru_RU.ts b/Plugins/CsvImport/translations/CsvImport_ru_RU.ts new file mode 100644 index 0000000..8f1cd45 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_ru_RU.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Ðевозможно прочитать файл %1 + + + + Could not find any data in the file %1. + Ðевозможно найти данные в файле %1. + + + + Enter the custom separator character. + Введите произвольный Ñимвол разделителÑ. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + Файлы CSV (*.csv);;ТекÑтовые файлы (*.txt);;Ð’Ñе файлы (*) + + + + csvImportOptions + + + , (comma) + , (запÑтаÑ) + + + + ; (semicolon) + ; (точка Ñ Ð·Ð°Ð¿Ñтой) + + + + \t (tab) + \t (табулÑциÑ) + + + + (whitespace) + (пробел) + + + + Custom: + Произвольный: + + + + Column separator: + Разделитель Ñтолбцов: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>При включении данной опции Ñимвол &quot; будет интерпретирован как начало заключённого в кавычки значениÑ; вÑÑ Ñтрока до Ñледующего (закрывающего) Ñимвола &quot; будет запиÑана как одно значение. ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°, Ñимвол &quot; будет обрабатыватьÑÑ ÐºÐ°Ðº обычный Ñимвол. РекомендуетÑÑ Ð²ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒ данную опцию.</p></body></html> + + + + Interpret " as a value quotation mark + Обрабатывать " как Ñимвол Ð¾Ð±Ñ€Ð°Ð¼Ð»ÐµÐ½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹ + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Ðктивируйте Ñту опцию, еÑли в первой Ñтроке файла CSV ÑодержатÑÑ Ð¸Ð¼ÐµÐ½Ð° Ñтолбцов. Имена Ñтолбцов не нужно импортировать как обычные данные.</p> + + + + First line represents CSV column names + Имена Ñтолбцов в первой Ñтроке + + + + If enabled, then text value provided on the right will be interpreted as NULL + При выборе данной опции указанное в поле Ñправа текÑтовое значение будет интерпретировано как NULL + + + + NULL values: + Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL: + + + + If your CSV data contains null values, define how are they represented in the CSV. + ЕÑли данные в CSV Ñодержат Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL, укажите, как они предÑтавлены в файле CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_sk_SK.ts b/Plugins/CsvImport/translations/CsvImport_sk_SK.ts new file mode 100644 index 0000000..c1b0526 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_sk_SK.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Nemôžem ÄítaÅ¥ súbor %1 + + + + Could not find any data in the file %1. + Nemôžem nájsÅ¥ dáta v súbore %1. + + + + Enter the custom separator character. + Zadajte vlastný oddeľovaÄ. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV súbory (*.csv);;Textové súbory (*.txt);;VÅ¡etky súbory (*) + + + + csvImportOptions + + + , (comma) + , (Äiarka) + + + + ; (semicolon) + ; (bodkoÄiarka) + + + + \t (tab) + \t (tabulátor) + + + + (whitespace) + (medzera) + + + + Custom: + Iný: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>ZaÅ¡krtnite túto voľbu ak prvý riadok v CSV súbore obsahuje názvy stĺpcov a nechcete aby boli naimportované do tabuľky</p> + + + + First line represents CSV column names + Názvy stĺpcov v prvom riadku + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL hodnoty: + + + + If your CSV data contains null values, define how are they represented in the CSV. + Ak váš CSV súbor obsahuje null hodnoty, zadajte ako sú reprezentované v CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_sr_SP.ts b/Plugins/CsvImport/translations/CsvImport_sr_SP.ts new file mode 100644 index 0000000..d065e87 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_sr_SP.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_sv_SE.ts b/Plugins/CsvImport/translations/CsvImport_sv_SE.ts new file mode 100644 index 0000000..07124ea --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_sv_SE.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Kan inte läsa filen %1 + + + + Could not find any data in the file %1. + Kunde inte hitta nÃ¥gon data i filen %1. + + + + Enter the custom separator character. + Ange det anpassade separatortecknet. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV-filer (*.csv);;Textfiler (*.txt);;Alla filer (*) + + + + csvImportOptions + + + , (comma) +  (Kommatecken) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (blankt) + + + + Custom: + Anpassad: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + Första raden representerar CSV-kolumnnamn + + + + If enabled, then text value provided on the right will be interpreted as NULL + Om aktiverad, kommer textvärdet till höger att tolkas som NULL + + + + NULL values: + NULL-värden: + + + + If your CSV data contains null values, define how are they represented in the CSV. + Om dina CSV-data innehÃ¥ller noll värden, ange hur de representeras i CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_tr_TR.ts b/Plugins/CsvImport/translations/CsvImport_tr_TR.ts new file mode 100644 index 0000000..cee7f9d --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_tr_TR.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + %1 dosyası okunamadı + + + + Could not find any data in the file %1. + %1 dosyasında veri bulunamadı. + + + + Enter the custom separator character. + Özel ayraç karakterini girin. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV dosyaları (*.csv);;Text dosyaları (*.txt);;Bütün dosyalar (*) + + + + csvImportOptions + + + , (comma) + , (virgül) + + + + ; (semicolon) + ; (noktalı virgül) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (boÅŸluk) + + + + Custom: + Özel: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>EÄŸer CSV dosyanızın ilk satırı kolon isimlerini belirtiyorsa bunu aktif hale getiriniz. Kolon isimlerinizin normal veri gibi deÄŸerlendirilmesini istemezsiniz.</p> + + + + First line represents CSV column names + İlk satır CSV kolon isimlerini temsil eder + + + + If enabled, then text value provided on the right will be interpreted as NULL + Aktif olduÄŸunda, saÄŸ taraftaki metin NULL olarak deÄŸerlendirilir + + + + NULL values: + NULL deÄŸerler: + + + + If your CSV data contains null values, define how are they represented in the CSV. + EÄŸer CSV dosyanız null deÄŸerler içeriyorsa, CSV içinde nasıl gösterilmesi gerektiÄŸini düzenleyiniz. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_uk_UA.ts b/Plugins/CsvImport/translations/CsvImport_uk_UA.ts new file mode 100644 index 0000000..f2d9f3f --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_uk_UA.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_vi_VN.ts b/Plugins/CsvImport/translations/CsvImport_vi_VN.ts new file mode 100644 index 0000000..477579c --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_vi_VN.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + Cannot read file %1 + + + + Could not find any data in the file %1. + Could not find any data in the file %1. + + + + Enter the custom separator character. + Enter the custom separator character. + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV files (*.csv);;Text files (*.txt);;All files (*) + + + + csvImportOptions + + + , (comma) + , (comma) + + + + ; (semicolon) + ; (semicolon) + + + + \t (tab) + \t (tab) + + + + (whitespace) + (whitespace) + + + + Custom: + Custom: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + + + + First line represents CSV column names + First line represents CSV column names + + + + If enabled, then text value provided on the right will be interpreted as NULL + If enabled, then text value provided on the right will be interpreted as NULL + + + + NULL values: + NULL values: + + + + If your CSV data contains null values, define how are they represented in the CSV. + If your CSV data contains null values, define how are they represented in the CSV. + + + diff --git a/Plugins/CsvImport/translations/CsvImport_zh_CN.ts b/Plugins/CsvImport/translations/CsvImport_zh_CN.ts new file mode 100644 index 0000000..df01e44 --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_zh_CN.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + æ— æ³•è¯»å–æ–‡ä»¶ %1 + + + + Could not find any data in the file %1. + 在文件 %1 中未找到任何数æ®ã€‚ + + + + Enter the custom separator character. + 设置自定义分隔符。 + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV 文件 (*.csv);;文本文件 (*.txt);;所有文件 (*) + + + + csvImportOptions + + + , (comma) + ,(逗å·ï¼‰ + + + + ; (semicolon) + ;(分å·ï¼‰ + + + + \t (tab) + \t(制表符) + + + + (whitespace) + (空格) + + + + Custom: + 自定义: + + + + Column separator: + 列分隔符: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>如果å¯ç”¨ï¼Œ &引å·ï¼› 字符将被解释为标准的 CSV 值引å·ï¼Œ 使字符串被当作å•个值处ç†ï¼Œç›´åˆ°ä¸‹ä¸€ä¸ª (正在结æŸ) &引å·ï¼› 字符 如果ç¦ç”¨ï¼Œ &引å·å°†è¢«è§†ä¸ºä»»ä½•其他字符。 通常您想è¦å¯ç”¨æ­¤åŠŸèƒ½ã€‚</p></body></html> + + + + Interpret " as a value quotation mark + å°† " è§£é‡Šä¸ºä¸€ä¸ªå€¼å¼•å· + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>如果您的 CSV 文件中的第一行数æ®è¡¨ç¤ºåˆ—åï¼Œä¸”æ‚¨ä¸æƒ³å°†åˆ—å作为常规数æ®å¯¼å…¥è¡¨ä¸­ï¼Œå¯ç”¨æ­¤é€‰é¡¹ã€‚</p> + + + + First line represents CSV column names + 第一行表示 CSV 列å + + + + If enabled, then text value provided on the right will be interpreted as NULL + 如果å¯ç”¨ï¼Œåˆ™å³ä¾§æä¾›çš„æ–‡æœ¬å€¼å°†è¢«è§£é‡Šä¸º NULL + + + + NULL values: + NULL 值: + + + + If your CSV data contains null values, define how are they represented in the CSV. + 如果您的 CSV æ•°æ®åŒ…å«ç©ºå€¼ï¼Œè¯·å®šä¹‰å®ƒä»¬åœ¨è¯¥ CSV 中的呈现方å¼ã€‚ + + + diff --git a/Plugins/CsvImport/translations/CsvImport_zh_TW.ts b/Plugins/CsvImport/translations/CsvImport_zh_TW.ts new file mode 100644 index 0000000..37d380f --- /dev/null +++ b/Plugins/CsvImport/translations/CsvImport_zh_TW.ts @@ -0,0 +1,95 @@ + + + + + CsvImport + + + Cannot read file %1 + ç„¡æ³•è®€å–æª”案 %1 + + + + Could not find any data in the file %1. + 在檔案 %1 中未找到任何資料。 + + + + Enter the custom separator character. + 設定自訂分隔符。 + + + + CSV files (*.csv);;Text files (*.txt);;All files (*) + CSV 檔案 (*.csv);;文字檔案 (*.txt);;所有檔案 (*) + + + + csvImportOptions + + + , (comma) + , (逗號) + + + + ; (semicolon) + ; (分號) + + + + \t (tab) + \t (製表符) + + + + (whitespace) + (空格) + + + + Custom: + 自訂: + + + + Column separator: + Column separator: + + + + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + <html><head/><body><p>If enabled, the &quot; character will be interpreted as standard CSV value quotation mark, causing the string to be treated as single value until the next (ending) &quot; character. If disabled, the &quot; will be treated as any other character. Usually you want this to be enabled.</p></body></html> + + + + Interpret " as a value quotation mark + Interpret " as a value quotation mark + + + + <p>Enable this if the first data line in your CSV file represents column names. You don't want column names to be imported into the table as a regular data.</p> + <p>如果您的 CSV 檔案中的第一行資料表示列åï¼Œä¸”æ‚¨ä¸æƒ³å°‡åˆ—å作為常è¦è³‡æ–™åŒ¯å…¥è¡¨ä¸­ï¼Œå•Ÿç”¨æ­¤é¸é …。</p> + + + + First line represents CSV column names + 第一行表示 CSV 列å + + + + If enabled, then text value provided on the right will be interpreted as NULL + 如果啟用,則å³å´æä¾›çš„æ–‡å­—值將被解釋為 NULL + + + + NULL values: + NULL 值: + + + + If your CSV data contains null values, define how are they represented in the CSV. + 如果您的 CSV 資料包å«ç©ºå€¼ï¼Œè«‹å®šç¾©å®ƒå€‘在該 CSV ä¸­çš„å‘ˆç¾æ–¹å¼ã€‚ + + + diff --git a/Plugins/DbAndroid/DbAndroid.pro b/Plugins/DbAndroid/DbAndroid.pro index 49f12e5..4d54287 100644 --- a/Plugins/DbAndroid/DbAndroid.pro +++ b/Plugins/DbAndroid/DbAndroid.pro @@ -52,18 +52,5 @@ FORMS += \ RESOURCES += \ dbandroid.qrc -TRANSLATIONS += DbAndroid_ro_RO.ts \ - DbAndroid_de.ts \ - \ - DbAndroid_it.ts\ - DbAndroid_zh_CN.ts\ - DbAndroid_sk.ts\ - DbAndroid_ru.ts\ - DbAndroid_pt_BR.ts\ - DbAndroid_fr.ts\ - DbAndroid_es.ts\ - DbAndroid_pl.ts - - diff --git a/Plugins/DbAndroid/DbAndroid_de.qm b/Plugins/DbAndroid/DbAndroid_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/DbAndroid/DbAndroid_de.qm and /dev/null differ diff --git a/Plugins/DbAndroid/DbAndroid_de.ts b/Plugins/DbAndroid/DbAndroid_de.ts deleted file mode 100644 index 860252b..0000000 --- a/Plugins/DbAndroid/DbAndroid_de.ts +++ /dev/null @@ -1,347 +0,0 @@ - - - - - DbAndroid - - - Invalid or incomplete Android Database URL. - - - - - Android database URL - - - - - Select Android database - - - - - Select ADB - - - - - Using Android Debug Bridge: %1 - - - - - You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> - - - - - Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. - - - - - Invalid ADB - - - - - The selected ADB is incorrect. -Would you like to select another one, or leave it unconfigured? - - - - - Select another ADB - - - - - Leave unconfigured - - - - - Save jar file - - - - - Get Android connector JAR file - - - - - DbAndroidInstance - - - Android SQLite driver does not support loadable extensions. - - - - - Connection with Android database '%1' lost. - - - - - DbAndroidJsonConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Failed to create port forwarding for device %1 for port %2. - - - - - Could not connect to network host: %1:%2 - - - - - Cannot connect to %1:%2, because password is invalid. - - - - - Unable to execute query on Android device (connection was closed): %1 - - - - - Error while parsing response from Android: %1 - - - - - Generic error from Android: %1 - - - - - - Missing 'columns' in response from Android. - - - - - Response from Android has missing data for column '%1' in row %2. - - - - - DbAndroidPathDialog - - - Android database URL - - - - - Connection method - - - - - USB cable - port forwarding - - - - - USB cable - sqlite3 command - - - - - Network (IP address) - - - - - Device - - - - - IP address - - - - - Port - - - - - Remote access password - - - - - <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> - - - - - Application - - - - - Filter - - - - - Database - - - - - Create a new database directly on the device. - - - - - Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. - - - - - Enter valid IP address. - - - - - Pick Android device. - - - - - Pick Android database. - - - - - Selected Android application is unknown, or not debuggable. - - - - - Create new database - - - - - Please provide name for the new database. -It's the name which Android application will use to connect to the database: - - - - - - - Invalid name - - - - - Database with the same name (%1) already exists on the device. -The name must be unique. - - - - - Could not create database '%1', because could not connect to the device. - - - - - Could not create database '%1'. -Details: %2 - - - - - Delete database - - - - - Are you sure you want to delete database '%1' from %2? - - - - - - Error deleting - - - - - Could not connect to %1 in order to delete database '%2'. - - - - - Could not delete database named '%1' from the device. -Android device refused deletion, or it was impossible. - - - - - DbAndroidShellConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. - - - - - Cannot connect to device %1, because the application %2 is not debuggable. - - - - - Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 - - - - - Cannot get list of databases for application %1. Details: %2 - - - - - - Could not execute query on database '%1': %2 - - - - - QObject - - - Cannot bind argument '%1' of the query, because it's value is missing. - - - - diff --git a/Plugins/DbAndroid/DbAndroid_es.qm b/Plugins/DbAndroid/DbAndroid_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/DbAndroid/DbAndroid_es.qm and /dev/null differ diff --git a/Plugins/DbAndroid/DbAndroid_es.ts b/Plugins/DbAndroid/DbAndroid_es.ts deleted file mode 100644 index a8f76aa..0000000 --- a/Plugins/DbAndroid/DbAndroid_es.ts +++ /dev/null @@ -1,347 +0,0 @@ - - - - - DbAndroid - - - Invalid or incomplete Android Database URL. - - - - - Android database URL - - - - - Select Android database - - - - - Select ADB - - - - - Using Android Debug Bridge: %1 - - - - - You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> - - - - - Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. - - - - - Invalid ADB - - - - - The selected ADB is incorrect. -Would you like to select another one, or leave it unconfigured? - - - - - Select another ADB - - - - - Leave unconfigured - - - - - Save jar file - - - - - Get Android connector JAR file - - - - - DbAndroidInstance - - - Android SQLite driver does not support loadable extensions. - - - - - Connection with Android database '%1' lost. - - - - - DbAndroidJsonConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Failed to create port forwarding for device %1 for port %2. - - - - - Could not connect to network host: %1:%2 - - - - - Cannot connect to %1:%2, because password is invalid. - - - - - Unable to execute query on Android device (connection was closed): %1 - - - - - Error while parsing response from Android: %1 - - - - - Generic error from Android: %1 - - - - - - Missing 'columns' in response from Android. - - - - - Response from Android has missing data for column '%1' in row %2. - - - - - DbAndroidPathDialog - - - Android database URL - - - - - Connection method - - - - - USB cable - port forwarding - - - - - USB cable - sqlite3 command - - - - - Network (IP address) - - - - - Device - - - - - IP address - - - - - Port - - - - - Remote access password - - - - - <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> - - - - - Application - - - - - Filter - - - - - Database - - - - - Create a new database directly on the device. - - - - - Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. - - - - - Enter valid IP address. - - - - - Pick Android device. - - - - - Pick Android database. - - - - - Selected Android application is unknown, or not debuggable. - - - - - Create new database - - - - - Please provide name for the new database. -It's the name which Android application will use to connect to the database: - - - - - - - Invalid name - - - - - Database with the same name (%1) already exists on the device. -The name must be unique. - - - - - Could not create database '%1', because could not connect to the device. - - - - - Could not create database '%1'. -Details: %2 - - - - - Delete database - - - - - Are you sure you want to delete database '%1' from %2? - - - - - - Error deleting - - - - - Could not connect to %1 in order to delete database '%2'. - - - - - Could not delete database named '%1' from the device. -Android device refused deletion, or it was impossible. - - - - - DbAndroidShellConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. - - - - - Cannot connect to device %1, because the application %2 is not debuggable. - - - - - Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 - - - - - Cannot get list of databases for application %1. Details: %2 - - - - - - Could not execute query on database '%1': %2 - - - - - QObject - - - Cannot bind argument '%1' of the query, because it's value is missing. - - - - diff --git a/Plugins/DbAndroid/DbAndroid_fr.qm b/Plugins/DbAndroid/DbAndroid_fr.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/DbAndroid/DbAndroid_fr.qm and /dev/null differ diff --git a/Plugins/DbAndroid/DbAndroid_fr.ts b/Plugins/DbAndroid/DbAndroid_fr.ts deleted file mode 100644 index fa56f0a..0000000 --- a/Plugins/DbAndroid/DbAndroid_fr.ts +++ /dev/null @@ -1,347 +0,0 @@ - - - - - DbAndroid - - - Invalid or incomplete Android Database URL. - - - - - Android database URL - - - - - Select Android database - - - - - Select ADB - - - - - Using Android Debug Bridge: %1 - - - - - You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> - - - - - Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. - - - - - Invalid ADB - - - - - The selected ADB is incorrect. -Would you like to select another one, or leave it unconfigured? - - - - - Select another ADB - - - - - Leave unconfigured - - - - - Save jar file - - - - - Get Android connector JAR file - - - - - DbAndroidInstance - - - Android SQLite driver does not support loadable extensions. - - - - - Connection with Android database '%1' lost. - - - - - DbAndroidJsonConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Failed to create port forwarding for device %1 for port %2. - - - - - Could not connect to network host: %1:%2 - - - - - Cannot connect to %1:%2, because password is invalid. - - - - - Unable to execute query on Android device (connection was closed): %1 - - - - - Error while parsing response from Android: %1 - - - - - Generic error from Android: %1 - - - - - - Missing 'columns' in response from Android. - - - - - Response from Android has missing data for column '%1' in row %2. - - - - - DbAndroidPathDialog - - - Android database URL - - - - - Connection method - - - - - USB cable - port forwarding - - - - - USB cable - sqlite3 command - - - - - Network (IP address) - - - - - Device - - - - - IP address - - - - - Port - - - - - Remote access password - - - - - <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> - - - - - Application - - - - - Filter - - - - - Database - - - - - Create a new database directly on the device. - - - - - Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. - - - - - Enter valid IP address. - - - - - Pick Android device. - - - - - Pick Android database. - - - - - Selected Android application is unknown, or not debuggable. - - - - - Create new database - - - - - Please provide name for the new database. -It's the name which Android application will use to connect to the database: - - - - - - - Invalid name - - - - - Database with the same name (%1) already exists on the device. -The name must be unique. - - - - - Could not create database '%1', because could not connect to the device. - - - - - Could not create database '%1'. -Details: %2 - - - - - Delete database - - - - - Are you sure you want to delete database '%1' from %2? - - - - - - Error deleting - - - - - Could not connect to %1 in order to delete database '%2'. - - - - - Could not delete database named '%1' from the device. -Android device refused deletion, or it was impossible. - - - - - DbAndroidShellConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. - - - - - Cannot connect to device %1, because the application %2 is not debuggable. - - - - - Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 - - - - - Cannot get list of databases for application %1. Details: %2 - - - - - - Could not execute query on database '%1': %2 - - - - - QObject - - - Cannot bind argument '%1' of the query, because it's value is missing. - - - - diff --git a/Plugins/DbAndroid/DbAndroid_it.qm b/Plugins/DbAndroid/DbAndroid_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/DbAndroid/DbAndroid_it.qm and /dev/null differ diff --git a/Plugins/DbAndroid/DbAndroid_it.ts b/Plugins/DbAndroid/DbAndroid_it.ts deleted file mode 100644 index 3ce9c38..0000000 --- a/Plugins/DbAndroid/DbAndroid_it.ts +++ /dev/null @@ -1,347 +0,0 @@ - - - - - DbAndroid - - - Invalid or incomplete Android Database URL. - - - - - Android database URL - - - - - Select Android database - - - - - Select ADB - - - - - Using Android Debug Bridge: %1 - - - - - You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> - - - - - Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. - - - - - Invalid ADB - - - - - The selected ADB is incorrect. -Would you like to select another one, or leave it unconfigured? - - - - - Select another ADB - - - - - Leave unconfigured - - - - - Save jar file - - - - - Get Android connector JAR file - - - - - DbAndroidInstance - - - Android SQLite driver does not support loadable extensions. - - - - - Connection with Android database '%1' lost. - - - - - DbAndroidJsonConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Failed to create port forwarding for device %1 for port %2. - - - - - Could not connect to network host: %1:%2 - - - - - Cannot connect to %1:%2, because password is invalid. - - - - - Unable to execute query on Android device (connection was closed): %1 - - - - - Error while parsing response from Android: %1 - - - - - Generic error from Android: %1 - - - - - - Missing 'columns' in response from Android. - - - - - Response from Android has missing data for column '%1' in row %2. - - - - - DbAndroidPathDialog - - - Android database URL - - - - - Connection method - - - - - USB cable - port forwarding - - - - - USB cable - sqlite3 command - - - - - Network (IP address) - - - - - Device - - - - - IP address - - - - - Port - - - - - Remote access password - - - - - <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> - - - - - Application - - - - - Filter - - - - - Database - - - - - Create a new database directly on the device. - - - - - Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. - - - - - Enter valid IP address. - - - - - Pick Android device. - - - - - Pick Android database. - - - - - Selected Android application is unknown, or not debuggable. - - - - - Create new database - - - - - Please provide name for the new database. -It's the name which Android application will use to connect to the database: - - - - - - - Invalid name - - - - - Database with the same name (%1) already exists on the device. -The name must be unique. - - - - - Could not create database '%1', because could not connect to the device. - - - - - Could not create database '%1'. -Details: %2 - - - - - Delete database - - - - - Are you sure you want to delete database '%1' from %2? - - - - - - Error deleting - - - - - Could not connect to %1 in order to delete database '%2'. - - - - - Could not delete database named '%1' from the device. -Android device refused deletion, or it was impossible. - - - - - DbAndroidShellConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. - - - - - Cannot connect to device %1, because the application %2 is not debuggable. - - - - - Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 - - - - - Cannot get list of databases for application %1. Details: %2 - - - - - - Could not execute query on database '%1': %2 - - - - - QObject - - - Cannot bind argument '%1' of the query, because it's value is missing. - - - - diff --git a/Plugins/DbAndroid/DbAndroid_pl.qm b/Plugins/DbAndroid/DbAndroid_pl.qm deleted file mode 100644 index 2b74cd4..0000000 Binary files a/Plugins/DbAndroid/DbAndroid_pl.qm and /dev/null differ diff --git a/Plugins/DbAndroid/DbAndroid_pl.ts b/Plugins/DbAndroid/DbAndroid_pl.ts deleted file mode 100644 index ab196a2..0000000 --- a/Plugins/DbAndroid/DbAndroid_pl.ts +++ /dev/null @@ -1,352 +0,0 @@ - - - - - DbAndroid - - - Invalid or incomplete Android Database URL. - Niepoprawny lub niekompletny URL Bazy Androida - - - - Android database URL - URL bazy Androida - - - - Select Android database - Wybierz bazÄ™ Androida - - - - Select ADB - Wybierz ADB - - - - Using Android Debug Bridge: %1 - Używam nastÄ™pujÄ…cego Android Debug Bridge: %1 - - - - You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> - Możesz pobrać plik JAR konektora Android z menu NarzÄ™dzia. Jest on wymagany do 2 z 3 obsÅ‚ugiwanych połączeÅ„ wtyczki Android. Po wiÄ™cej szczegółów przeczytaj dokumentacjÄ™ na <a href="%1">stronie wiki SQLiteStudio.</a> - - - - Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. - Nie można znaleźć aplikacji Android Debug Bridge. <a href="%1">Kliknij tutaj</a> aby wskazać poÅ‚ożenie aplikacji ADB, inaczej wtyczka %2 nie bÄ™dzie obsÅ‚ugiwać połączeÅ„ przez kabel USB, tylko połączenia przez sieć. - - - - Invalid ADB - Niepoprawny ADB - - - - The selected ADB is incorrect. -Would you like to select another one, or leave it unconfigured? - Wybrany ADB jest niepoprawny. -Chcesz wybrać inny, czy zostawić go nieskonfigurowanego? - - - - Select another ADB - Wybierz inny ADB - - - - Leave unconfigured - Zostaw nieskonfigurowany - - - - Save jar file - Zapisz plik jar - - - - Get Android connector JAR file - Pobierz plik JAR konektora Android - - - - DbAndroidInstance - - - Android SQLite driver does not support loadable extensions. - Sterownik SQLite Android nie obsÅ‚uguje Å‚adowalnych rozszerzeÅ„. - - - - Connection with Android database '%1' lost. - Połączenie z bazÄ… Androida '%1' utracone. - - - - DbAndroidJsonConnection - - - Cannot connect to device %1, because it's not visible to your computer. - Nie można połączyć siÄ™ z urzÄ…dzeniem %1, ponieważ nie jest ono widoczne dla twojego komputera. - - - - Failed to create port forwarding for device %1 for port %2. - Nie powiodÅ‚o siÄ™ stworzenie portu przekierowujÄ…cego dla urzÄ…dzenia %1 dla portu %2. - - - - Could not connect to network host: %1:%2 - Nie udaÅ‚o siÄ™ połączyć z adresem sieciowym: %1:%2 - - - - Cannot connect to %1:%2, because password is invalid. - Nie można połączyć z %1:%2, ponieważ podano niepoprawne hasÅ‚o. - - - - Unable to execute query on Android device (connection was closed): %1 - Nie udaÅ‚o siÄ™ wykonać zapytania na urzÄ…dzeniu Android (połączenie zostaÅ‚o zamkniÄ™te): %1 - - - - Error while parsing response from Android: %1 - Błąd podczas analizy odpowiedzi z Androida: %1 - - - - Generic error from Android: %1 - Błąd ogólny z Androida: %1 - - - - - Missing 'columns' in response from Android. - Brauje 'columns' w odpowiedzi z Androida. - - - - Response from Android has missing data for column '%1' in row %2. - Brakuje danych w kolumnie '%1' w wierszy %2 w odpowiedź z Androida. - - - - DbAndroidPathDialog - - - Android database URL - URL bazy Androida - - - - Connection method - Metoda połączenia - - - - USB cable - port forwarding - Kabel USB - przekierowanie portu - - - - USB cable - sqlite3 command - Kabel USB - polecenie sqlite3 - - - - Network (IP address) - Sieć (adres IP) - - - - Device - UrzÄ…dzenie - - - - IP address - Adres IP - - - - Port - Port - - - - Remote access password - HasÅ‚o zdalnego dostÄ™pu - - - - <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> - <p>HasÅ‚o to jest konfigurowane w serwisie SQLiteStudio, który jest osadzany w aplikacji Androida.</p> - - - - Application - Aplikacja - - - - Filter - Filtr - - - - Database - Baza danych - - - - Create a new database directly on the device. - Stwórz nowÄ… bazÄ™ danych bezpoÅ›rednio na urzÄ…dzeniu. - - - - Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. - UsuÅ„ aktualnie wybranÄ… bazÄ™ danych z urzÄ…dzenia. Aktualnie wybrana baza to ta wybrana z listy po lewej stronie od tego przycisku. - - - - Enter valid IP address. - Wprowadź poprawny adres IP. - - - - Pick Android device. - Wybierz urzÄ…dzenie Android. - - - - Pick Android database. - Wybierz bazÄ™ danych Android. - - - - Selected Android application is unknown, or not debuggable. - Wybierana aplikacja Android jest nieznana, lub niedebugowalna. - - - - Create new database - Stwórz nowÄ… bazÄ™ - - - - Please provide name for the new database. -It's the name which Android application will use to connect to the database: - ProszÄ™ podać nazwÄ™ dla nowej bazy. -Jest to nazwa, którÄ… aplikacja Androida bÄ™dzie używać przy łączeniu z bazÄ… danych: - - - - - - Invalid name - Niepoprawna nazwa - - - - Database with the same name (%1) already exists on the device. -The name must be unique. - Baza danych z takÄ… samÄ… nazwÄ… (%1) już istnieje na urzÄ…dzeniu. -Nazwa musi być unikalna. - - - - Could not create database '%1', because could not connect to the device. - Nie można stworzyć bazy danych '%1', ponieważ nie udaÅ‚o siÄ™ połączyć z urzÄ…dzeniem. - - - - Could not create database '%1'. -Details: %2 - Nie można stworzyć bazy danych: '%1'. -Szczegóły: %2 - - - - Delete database - UsuÅ„ bazÄ™ danych - - - - Are you sure you want to delete database '%1' from %2? - Czy na pewno chcesz usunąć bazÄ™ danych '%1' z %2? - - - - - Error deleting - Błąd usuwania - - - - Could not connect to %1 in order to delete database '%2'. - Nie można połączyć siÄ™ z %1 w celu usuniÄ™cia bazy danych '%2'. - - - - Could not delete database named '%1' from the device. -Android device refused deletion, or it was impossible. - Nie można usunąć bazy o nazwie '%1' z urzÄ…dzenia. -UrzÄ…dzenie Android odmówiÅ‚o usuniÄ™cia, lub byÅ‚o to niemożliwe. - - - - DbAndroidShellConnection - - - Cannot connect to device %1, because it's not visible to your computer. - Nie można połączyć siÄ™ z urzÄ…dzeniem %1, ponieważ nie jest ono widoczne dla twojego komputera. - - - - Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. - Nie można połączyć siÄ™ urzÄ…dzeniem %1, ponieważ wyglÄ…da na to, że aplikacja %2 nie jest zainstalowana na tym urzÄ…dzeniu. - - - - Cannot connect to device %1, because the application %2 is not debuggable. - Nie można połączyć siÄ™ urzÄ…dzeniem %1, ponieważ aplikacja %2 nie debugowalna. - - - - Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. - Nie można połączyć siÄ™ urzÄ…dzeniem %1, ponieważ wyglÄ…da na to, że polecenie '%2' nie jest dostÄ™pne na tym urzÄ…dzeniu. - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. - Nie można połączyć siÄ™ urzÄ…dzeniem %1, ponieważ nie ma prawa dostÄ™pu do bazy danych '%2' na tym urzÄ…dzeniu. - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 - Nie można połączyć siÄ™ urzÄ…dzeniem %1, ponieważ nie ma prawa dostÄ™pu do bazy danych '%2' na tym urzÄ…dzeniu. Szczegóły: %3 - - - - Cannot get list of databases for application %1. Details: %2 - Nie można pobrać listy baz danych dla aplikacji %1. Szczegóły: %2 - - - - - Could not execute query on database '%1': %2 - Nie można wykonać polecenia na bazie '%1': %2 - - - - QObject - - - Cannot bind argument '%1' of the query, because it's value is missing. - Nie można podpiąć argumentu '%1' zapytania, ponieważ nie ma dla niego wartoÅ›ci. - - - diff --git a/Plugins/DbAndroid/DbAndroid_pt_BR.qm b/Plugins/DbAndroid/DbAndroid_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/DbAndroid/DbAndroid_pt_BR.qm and /dev/null differ diff --git a/Plugins/DbAndroid/DbAndroid_pt_BR.ts b/Plugins/DbAndroid/DbAndroid_pt_BR.ts deleted file mode 100644 index d876345..0000000 --- a/Plugins/DbAndroid/DbAndroid_pt_BR.ts +++ /dev/null @@ -1,347 +0,0 @@ - - - - - DbAndroid - - - Invalid or incomplete Android Database URL. - - - - - Android database URL - - - - - Select Android database - - - - - Select ADB - - - - - Using Android Debug Bridge: %1 - - - - - You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> - - - - - Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. - - - - - Invalid ADB - - - - - The selected ADB is incorrect. -Would you like to select another one, or leave it unconfigured? - - - - - Select another ADB - - - - - Leave unconfigured - - - - - Save jar file - - - - - Get Android connector JAR file - - - - - DbAndroidInstance - - - Android SQLite driver does not support loadable extensions. - - - - - Connection with Android database '%1' lost. - - - - - DbAndroidJsonConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Failed to create port forwarding for device %1 for port %2. - - - - - Could not connect to network host: %1:%2 - - - - - Cannot connect to %1:%2, because password is invalid. - - - - - Unable to execute query on Android device (connection was closed): %1 - - - - - Error while parsing response from Android: %1 - - - - - Generic error from Android: %1 - - - - - - Missing 'columns' in response from Android. - - - - - Response from Android has missing data for column '%1' in row %2. - - - - - DbAndroidPathDialog - - - Android database URL - - - - - Connection method - - - - - USB cable - port forwarding - - - - - USB cable - sqlite3 command - - - - - Network (IP address) - - - - - Device - - - - - IP address - - - - - Port - - - - - Remote access password - - - - - <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> - - - - - Application - - - - - Filter - - - - - Database - - - - - Create a new database directly on the device. - - - - - Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. - - - - - Enter valid IP address. - - - - - Pick Android device. - - - - - Pick Android database. - - - - - Selected Android application is unknown, or not debuggable. - - - - - Create new database - - - - - Please provide name for the new database. -It's the name which Android application will use to connect to the database: - - - - - - - Invalid name - - - - - Database with the same name (%1) already exists on the device. -The name must be unique. - - - - - Could not create database '%1', because could not connect to the device. - - - - - Could not create database '%1'. -Details: %2 - - - - - Delete database - - - - - Are you sure you want to delete database '%1' from %2? - - - - - - Error deleting - - - - - Could not connect to %1 in order to delete database '%2'. - - - - - Could not delete database named '%1' from the device. -Android device refused deletion, or it was impossible. - - - - - DbAndroidShellConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. - - - - - Cannot connect to device %1, because the application %2 is not debuggable. - - - - - Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 - - - - - Cannot get list of databases for application %1. Details: %2 - - - - - - Could not execute query on database '%1': %2 - - - - - QObject - - - Cannot bind argument '%1' of the query, because it's value is missing. - - - - diff --git a/Plugins/DbAndroid/DbAndroid_ro_RO.qm b/Plugins/DbAndroid/DbAndroid_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/DbAndroid/DbAndroid_ro_RO.qm and /dev/null differ diff --git a/Plugins/DbAndroid/DbAndroid_ro_RO.ts b/Plugins/DbAndroid/DbAndroid_ro_RO.ts deleted file mode 100644 index 2150935..0000000 --- a/Plugins/DbAndroid/DbAndroid_ro_RO.ts +++ /dev/null @@ -1,347 +0,0 @@ - - - - - DbAndroid - - - Invalid or incomplete Android Database URL. - - - - - Android database URL - - - - - Select Android database - - - - - Select ADB - - - - - Using Android Debug Bridge: %1 - - - - - You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> - - - - - Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. - - - - - Invalid ADB - - - - - The selected ADB is incorrect. -Would you like to select another one, or leave it unconfigured? - - - - - Select another ADB - - - - - Leave unconfigured - - - - - Save jar file - - - - - Get Android connector JAR file - - - - - DbAndroidInstance - - - Android SQLite driver does not support loadable extensions. - - - - - Connection with Android database '%1' lost. - - - - - DbAndroidJsonConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Failed to create port forwarding for device %1 for port %2. - - - - - Could not connect to network host: %1:%2 - - - - - Cannot connect to %1:%2, because password is invalid. - - - - - Unable to execute query on Android device (connection was closed): %1 - - - - - Error while parsing response from Android: %1 - - - - - Generic error from Android: %1 - - - - - - Missing 'columns' in response from Android. - - - - - Response from Android has missing data for column '%1' in row %2. - - - - - DbAndroidPathDialog - - - Android database URL - - - - - Connection method - - - - - USB cable - port forwarding - - - - - USB cable - sqlite3 command - - - - - Network (IP address) - - - - - Device - - - - - IP address - - - - - Port - - - - - Remote access password - - - - - <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> - - - - - Application - - - - - Filter - - - - - Database - - - - - Create a new database directly on the device. - - - - - Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. - - - - - Enter valid IP address. - - - - - Pick Android device. - - - - - Pick Android database. - - - - - Selected Android application is unknown, or not debuggable. - - - - - Create new database - - - - - Please provide name for the new database. -It's the name which Android application will use to connect to the database: - - - - - - - Invalid name - - - - - Database with the same name (%1) already exists on the device. -The name must be unique. - - - - - Could not create database '%1', because could not connect to the device. - - - - - Could not create database '%1'. -Details: %2 - - - - - Delete database - - - - - Are you sure you want to delete database '%1' from %2? - - - - - - Error deleting - - - - - Could not connect to %1 in order to delete database '%2'. - - - - - Could not delete database named '%1' from the device. -Android device refused deletion, or it was impossible. - - - - - DbAndroidShellConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. - - - - - Cannot connect to device %1, because the application %2 is not debuggable. - - - - - Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 - - - - - Cannot get list of databases for application %1. Details: %2 - - - - - - Could not execute query on database '%1': %2 - - - - - QObject - - - Cannot bind argument '%1' of the query, because it's value is missing. - - - - diff --git a/Plugins/DbAndroid/DbAndroid_ru.qm b/Plugins/DbAndroid/DbAndroid_ru.qm deleted file mode 100644 index 7431612..0000000 Binary files a/Plugins/DbAndroid/DbAndroid_ru.qm and /dev/null differ diff --git a/Plugins/DbAndroid/DbAndroid_ru.ts b/Plugins/DbAndroid/DbAndroid_ru.ts deleted file mode 100644 index 8f086e5..0000000 --- a/Plugins/DbAndroid/DbAndroid_ru.ts +++ /dev/null @@ -1,347 +0,0 @@ - - - - - DbAndroid - - - Invalid or incomplete Android Database URL. - - - - - Android database URL - - - - - Select Android database - - - - - Select ADB - - - - - Using Android Debug Bridge: %1 - - - - - You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> - - - - - Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. - - - - - Invalid ADB - - - - - The selected ADB is incorrect. -Would you like to select another one, or leave it unconfigured? - - - - - Select another ADB - - - - - Leave unconfigured - - - - - Save jar file - - - - - Get Android connector JAR file - - - - - DbAndroidInstance - - - Android SQLite driver does not support loadable extensions. - - - - - Connection with Android database '%1' lost. - - - - - DbAndroidJsonConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Failed to create port forwarding for device %1 for port %2. - - - - - Could not connect to network host: %1:%2 - - - - - Cannot connect to %1:%2, because password is invalid. - - - - - Unable to execute query on Android device (connection was closed): %1 - - - - - Error while parsing response from Android: %1 - - - - - Generic error from Android: %1 - - - - - - Missing 'columns' in response from Android. - - - - - Response from Android has missing data for column '%1' in row %2. - - - - - DbAndroidPathDialog - - - Android database URL - - - - - Connection method - - - - - USB cable - port forwarding - - - - - USB cable - sqlite3 command - - - - - Network (IP address) - - - - - Device - - - - - IP address - - - - - Port - - - - - Remote access password - - - - - <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> - - - - - Application - - - - - Filter - - - - - Database - - - - - Create a new database directly on the device. - - - - - Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. - - - - - Enter valid IP address. - - - - - Pick Android device. - - - - - Pick Android database. - - - - - Selected Android application is unknown, or not debuggable. - - - - - Create new database - - - - - Please provide name for the new database. -It's the name which Android application will use to connect to the database: - - - - - - - Invalid name - - - - - Database with the same name (%1) already exists on the device. -The name must be unique. - - - - - Could not create database '%1', because could not connect to the device. - - - - - Could not create database '%1'. -Details: %2 - - - - - Delete database - - - - - Are you sure you want to delete database '%1' from %2? - - - - - - Error deleting - - - - - Could not connect to %1 in order to delete database '%2'. - - - - - Could not delete database named '%1' from the device. -Android device refused deletion, or it was impossible. - - - - - DbAndroidShellConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. - - - - - Cannot connect to device %1, because the application %2 is not debuggable. - - - - - Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 - - - - - Cannot get list of databases for application %1. Details: %2 - - - - - - Could not execute query on database '%1': %2 - - - - - QObject - - - Cannot bind argument '%1' of the query, because it's value is missing. - - - - diff --git a/Plugins/DbAndroid/DbAndroid_sk.qm b/Plugins/DbAndroid/DbAndroid_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/Plugins/DbAndroid/DbAndroid_sk.qm and /dev/null differ diff --git a/Plugins/DbAndroid/DbAndroid_sk.ts b/Plugins/DbAndroid/DbAndroid_sk.ts deleted file mode 100644 index b5f07d0..0000000 --- a/Plugins/DbAndroid/DbAndroid_sk.ts +++ /dev/null @@ -1,347 +0,0 @@ - - - - - DbAndroid - - - Invalid or incomplete Android Database URL. - - - - - Android database URL - - - - - Select Android database - - - - - Select ADB - - - - - Using Android Debug Bridge: %1 - - - - - You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> - - - - - Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. - - - - - Invalid ADB - - - - - The selected ADB is incorrect. -Would you like to select another one, or leave it unconfigured? - - - - - Select another ADB - - - - - Leave unconfigured - - - - - Save jar file - - - - - Get Android connector JAR file - - - - - DbAndroidInstance - - - Android SQLite driver does not support loadable extensions. - - - - - Connection with Android database '%1' lost. - - - - - DbAndroidJsonConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Failed to create port forwarding for device %1 for port %2. - - - - - Could not connect to network host: %1:%2 - - - - - Cannot connect to %1:%2, because password is invalid. - - - - - Unable to execute query on Android device (connection was closed): %1 - - - - - Error while parsing response from Android: %1 - - - - - Generic error from Android: %1 - - - - - - Missing 'columns' in response from Android. - - - - - Response from Android has missing data for column '%1' in row %2. - - - - - DbAndroidPathDialog - - - Android database URL - - - - - Connection method - - - - - USB cable - port forwarding - - - - - USB cable - sqlite3 command - - - - - Network (IP address) - - - - - Device - - - - - IP address - - - - - Port - - - - - Remote access password - - - - - <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> - - - - - Application - - - - - Filter - - - - - Database - - - - - Create a new database directly on the device. - - - - - Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. - - - - - Enter valid IP address. - - - - - Pick Android device. - - - - - Pick Android database. - - - - - Selected Android application is unknown, or not debuggable. - - - - - Create new database - - - - - Please provide name for the new database. -It's the name which Android application will use to connect to the database: - - - - - - - Invalid name - - - - - Database with the same name (%1) already exists on the device. -The name must be unique. - - - - - Could not create database '%1', because could not connect to the device. - - - - - Could not create database '%1'. -Details: %2 - - - - - Delete database - - - - - Are you sure you want to delete database '%1' from %2? - - - - - - Error deleting - - - - - Could not connect to %1 in order to delete database '%2'. - - - - - Could not delete database named '%1' from the device. -Android device refused deletion, or it was impossible. - - - - - DbAndroidShellConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. - - - - - Cannot connect to device %1, because the application %2 is not debuggable. - - - - - Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 - - - - - Cannot get list of databases for application %1. Details: %2 - - - - - - Could not execute query on database '%1': %2 - - - - - QObject - - - Cannot bind argument '%1' of the query, because it's value is missing. - - - - diff --git a/Plugins/DbAndroid/DbAndroid_zh_CN.qm b/Plugins/DbAndroid/DbAndroid_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/Plugins/DbAndroid/DbAndroid_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/Plugins/DbAndroid/DbAndroid_zh_CN.ts b/Plugins/DbAndroid/DbAndroid_zh_CN.ts deleted file mode 100644 index 56ecde4..0000000 --- a/Plugins/DbAndroid/DbAndroid_zh_CN.ts +++ /dev/null @@ -1,347 +0,0 @@ - - - - - DbAndroid - - - Invalid or incomplete Android Database URL. - - - - - Android database URL - - - - - Select Android database - - - - - Select ADB - - - - - Using Android Debug Bridge: %1 - - - - - You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> - - - - - Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. - - - - - Invalid ADB - - - - - The selected ADB is incorrect. -Would you like to select another one, or leave it unconfigured? - - - - - Select another ADB - - - - - Leave unconfigured - - - - - Save jar file - - - - - Get Android connector JAR file - - - - - DbAndroidInstance - - - Android SQLite driver does not support loadable extensions. - - - - - Connection with Android database '%1' lost. - - - - - DbAndroidJsonConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Failed to create port forwarding for device %1 for port %2. - - - - - Could not connect to network host: %1:%2 - - - - - Cannot connect to %1:%2, because password is invalid. - - - - - Unable to execute query on Android device (connection was closed): %1 - - - - - Error while parsing response from Android: %1 - - - - - Generic error from Android: %1 - - - - - - Missing 'columns' in response from Android. - - - - - Response from Android has missing data for column '%1' in row %2. - - - - - DbAndroidPathDialog - - - Android database URL - - - - - Connection method - - - - - USB cable - port forwarding - - - - - USB cable - sqlite3 command - - - - - Network (IP address) - - - - - Device - - - - - IP address - - - - - Port - - - - - Remote access password - - - - - <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> - - - - - Application - - - - - Filter - - - - - Database - - - - - Create a new database directly on the device. - - - - - Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. - - - - - Enter valid IP address. - - - - - Pick Android device. - - - - - Pick Android database. - - - - - Selected Android application is unknown, or not debuggable. - - - - - Create new database - - - - - Please provide name for the new database. -It's the name which Android application will use to connect to the database: - - - - - - - Invalid name - - - - - Database with the same name (%1) already exists on the device. -The name must be unique. - - - - - Could not create database '%1', because could not connect to the device. - - - - - Could not create database '%1'. -Details: %2 - - - - - Delete database - - - - - Are you sure you want to delete database '%1' from %2? - - - - - - Error deleting - - - - - Could not connect to %1 in order to delete database '%2'. - - - - - Could not delete database named '%1' from the device. -Android device refused deletion, or it was impossible. - - - - - DbAndroidShellConnection - - - Cannot connect to device %1, because it's not visible to your computer. - - - - - Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. - - - - - Cannot connect to device %1, because the application %2 is not debuggable. - - - - - Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. - - - - - Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 - - - - - Cannot get list of databases for application %1. Details: %2 - - - - - - Could not execute query on database '%1': %2 - - - - - QObject - - - Cannot bind argument '%1' of the query, because it's value is missing. - - - - diff --git a/Plugins/DbAndroid/dbandroid.cpp b/Plugins/DbAndroid/dbandroid.cpp index 9da066d..6946d5a 100644 --- a/Plugins/DbAndroid/dbandroid.cpp +++ b/Plugins/DbAndroid/dbandroid.cpp @@ -79,7 +79,7 @@ QString DbAndroid::generateDbName(const QVariant& baseValue) bool DbAndroid::init() { - Q_INIT_RESOURCE(dbandroid); + SQLS_INIT_RESOURCE(dbandroid); qRegisterMetaType>("QList"); @@ -116,7 +116,7 @@ void DbAndroid::deinit() safe_delete(jarAction); safe_delete(connectionFactory); safe_delete(adbManager); - Q_CLEANUP_RESOURCE(dbandroid); + SQLS_CLEANUP_RESOURCE(dbandroid); } QString DbAndroid::getCurrentAdb() diff --git a/Plugins/DbAndroid/dbandroid.json b/Plugins/DbAndroid/dbandroid.json index 8bc6263..bfb4620 100644 --- a/Plugins/DbAndroid/dbandroid.json +++ b/Plugins/DbAndroid/dbandroid.json @@ -2,7 +2,7 @@ "type": "DbPlugin", "title": "Android SQLite", "description": "Provides support for remote SQLite databases on Android devices.", - "version": 10200, + "version": 10201, "author": "SalSoft", "minAppVersion": 30300, "gui": true, diff --git a/Plugins/DbAndroid/dbandroidinstance.cpp b/Plugins/DbAndroid/dbandroidinstance.cpp index 7dce61c..c1b5629 100644 --- a/Plugins/DbAndroid/dbandroidinstance.cpp +++ b/Plugins/DbAndroid/dbandroidinstance.cpp @@ -50,7 +50,7 @@ QList DbAndroidInstance::columnsForQuery(const QString& query) QList columns; AliasedColumn column; - for (const QString& colName : results->getColumnNames()) + for (QString& colName : results->getColumnNames()) { column.setAlias(colName); columns << column; @@ -63,11 +63,16 @@ SqlQueryPtr DbAndroidInstance::prepare(const QString& query) return SqlQueryPtr(new SqlQueryAndroid(this, connection, query)); } -QString DbAndroidInstance::getTypeLabel() +QString DbAndroidInstance::getTypeLabel() const { return plugin->getLabel(); } +QString DbAndroidInstance::getTypeClassName() const +{ + return "DbAndroidInstance"; +} + bool DbAndroidInstance::deregisterFunction(const QString& name, int argCount) { // Unsupported by native Android driver @@ -76,19 +81,21 @@ bool DbAndroidInstance::deregisterFunction(const QString& name, int argCount) return true; } -bool DbAndroidInstance::registerScalarFunction(const QString& name, int argCount) +bool DbAndroidInstance::registerScalarFunction(const QString& name, int argCount, bool deterministic) { // Unsupported by native Android driver UNUSED(name); UNUSED(argCount); + UNUSED(deterministic); return true; } -bool DbAndroidInstance::registerAggregateFunction(const QString& name, int argCount) +bool DbAndroidInstance::registerAggregateFunction(const QString& name, int argCount, bool deterministic) { // Unsupported by native Android driver UNUSED(name); UNUSED(argCount); + UNUSED(deterministic); return true; } @@ -112,6 +119,11 @@ bool DbAndroidInstance::isComplete(const QString& sql) const return DbSqlite3::complete(sql); } +Db* DbAndroidInstance::clone() const +{ + return new DbAndroidInstance(plugin, name, path, connOptions); +} + bool DbAndroidInstance::isOpenInternal() { return (connection && connection->isConnected()); @@ -159,6 +171,12 @@ bool DbAndroidInstance::closeInternal() return true; } +bool DbAndroidInstance::flushWalInternal() +{ + // WAL mode is not fully supporeted by Android plugin. + return true; +} + bool DbAndroidInstance::registerCollationInternal(const QString& name) { // Unsupported by native Android driver diff --git a/Plugins/DbAndroid/dbandroidinstance.h b/Plugins/DbAndroid/dbandroidinstance.h index 5ddbd85..e81bd40 100644 --- a/Plugins/DbAndroid/dbandroidinstance.h +++ b/Plugins/DbAndroid/dbandroidinstance.h @@ -21,13 +21,15 @@ class DbAndroidInstance : public AbstractDb QList columnsForQuery(const QString& query); SqlQueryPtr prepare(const QString& query); - QString getTypeLabel(); + QString getTypeLabel() const; + QString getTypeClassName() const; bool deregisterFunction(const QString& name, int argCount); - bool registerScalarFunction(const QString& name, int argCount); - bool registerAggregateFunction(const QString& name, int argCount); + bool registerScalarFunction(const QString& name, int argCount, bool deterministic); + bool registerAggregateFunction(const QString& name, int argCount, bool deterministic); bool initAfterCreated(); bool loadExtension(const QString& filePath, const QString& initFunc); bool isComplete(const QString& sql) const; + Db* clone() const; protected: bool isOpenInternal(); @@ -36,6 +38,7 @@ class DbAndroidInstance : public AbstractDb int getErrorCodeInternal(); bool openInternal(); bool closeInternal(); + bool flushWalInternal(); bool registerCollationInternal(const QString& name); bool deregisterCollationInternal(const QString& name); diff --git a/Plugins/DbAndroid/dbandroidjsonconnection.cpp b/Plugins/DbAndroid/dbandroidjsonconnection.cpp index 2c0023f..6993aa3 100644 --- a/Plugins/DbAndroid/dbandroidjsonconnection.cpp +++ b/Plugins/DbAndroid/dbandroidjsonconnection.cpp @@ -172,7 +172,7 @@ bool DbAndroidJsonConnection::connectToDevice() if (!plugin->getAdbManager()->getDevices().contains(dbUrl.getDevice())) { - notifyWarn(tr("Cannot connect to device %1, because it's not visible to your computer.").arg(dbUrl.getDevice())); + notifyWarn(tr("Cannot connect to device %1, because it's not visible from your computer.").arg(dbUrl.getDevice())); return false; } diff --git a/Plugins/DbAndroid/dbandroidshellconnection.cpp b/Plugins/DbAndroid/dbandroidshellconnection.cpp index 6ead847..64c60f8 100644 --- a/Plugins/DbAndroid/dbandroidshellconnection.cpp +++ b/Plugins/DbAndroid/dbandroidshellconnection.cpp @@ -28,7 +28,7 @@ bool DbAndroidShellConnection::connectToAndroid(const DbAndroidUrl& url) if (!adbManager->getDevices().contains(url.getDevice())) { - notifyWarn(tr("Cannot connect to device %1, because it's not visible to your computer.").arg(url.getDevice())); + notifyWarn(tr("Cannot connect to device %1, because it's not visible from your computer.").arg(url.getDevice())); return false; } diff --git a/Plugins/DbAndroid/translations/DbAndroid.ts b/Plugins/DbAndroid/translations/DbAndroid.ts new file mode 100644 index 0000000..5a0a5a4 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid.ts @@ -0,0 +1,347 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + + + + + Android database URL + + + + + Select Android database + + + + + Select ADB + + + + + Using Android Debug Bridge: %1 + + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + + Invalid ADB + + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + + Select another ADB + + + + + Leave unconfigured + + + + + Save jar file + + + + + Get Android connector JAR file + + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + + + + + Connection with Android database '%1' lost. + + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + + + + + Failed to create port forwarding for device %1 for port %2. + + + + + Could not connect to network host: %1:%2 + + + + + Cannot connect to %1:%2, because password is invalid. + + + + + Unable to execute query on Android device (connection was closed): %1 + + + + + Error while parsing response from Android: %1 + + + + + Generic error from Android: %1 + + + + + + Missing 'columns' in response from Android. + + + + + Response from Android has missing data for column '%1' in row %2. + + + + + DbAndroidPathDialog + + + Android database URL + + + + + Connection method + + + + + USB cable - port forwarding + + + + + USB cable - sqlite3 command + + + + + Network (IP address) + + + + + Device + + + + + IP address + + + + + Port + + + + + Remote access password + + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + + Application + + + + + Filter + + + + + Database + + + + + Create a new database directly on the device. + + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + + Enter valid IP address. + + + + + Pick Android device. + + + + + Pick Android database. + + + + + Selected Android application is unknown, or not debuggable. + + + + + Create new database + + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + + Invalid name + + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + + Could not create database '%1', because could not connect to the device. + + + + + Could not create database '%1'. +Details: %2 + + + + + Delete database + + + + + Are you sure you want to delete database '%1' from %2? + + + + + + Error deleting + + + + + Could not connect to %1 in order to delete database '%2'. + + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + + Cannot connect to device %1, because the application %2 is not debuggable. + + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + + Cannot get list of databases for application %1. Details: %2 + + + + + + Could not execute query on database '%1': %2 + + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_af_ZA.ts b/Plugins/DbAndroid/translations/DbAndroid_af_ZA.ts new file mode 100644 index 0000000..75da109 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_af_ZA.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_ar_SA.ts b/Plugins/DbAndroid/translations/DbAndroid_ar_SA.ts new file mode 100644 index 0000000..09b7563 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_ar_SA.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_ca_ES.ts b/Plugins/DbAndroid/translations/DbAndroid_ca_ES.ts new file mode 100644 index 0000000..7619a4d --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_ca_ES.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_cs_CZ.ts b/Plugins/DbAndroid/translations/DbAndroid_cs_CZ.ts new file mode 100644 index 0000000..db3c3fa --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_cs_CZ.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_da_DK.ts b/Plugins/DbAndroid/translations/DbAndroid_da_DK.ts new file mode 100644 index 0000000..33ba9ce --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_da_DK.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_de_DE.ts b/Plugins/DbAndroid/translations/DbAndroid_de_DE.ts new file mode 100644 index 0000000..58b2f75 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_de_DE.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Ungültige oder unvollständige Android-Datenbank-URL. + + + + Android database URL + Android-Datenbank-URL + + + + Select Android database + Android-Datenbank auswählen + + + + Select ADB + ADB auswählen + + + + Using Android Debug Bridge: %1 + Verwende Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + Sie können die Android-JAR-Datei aus dem Menü Extras aufnehmen. Es wird für 2 von 3 Verbindungen, die vom Android-Plugin unterstützt werden, benötigt. Für weitere Details lesen Sie die Plugin's Dokumentation auf <a href="%1">SQLiteStudio's Wiki-Seite.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Android Debug Bridge App konnte nicht gefunden werden. <a href="%1">Klicke hier</a> um auf den Standort der ADB-Anwendung hinzuweisen, da andernfalls das %2 Plugin keine USB-Kabelverbindungen, sondern nur die Netzwerkverbindung unterstützt.. + + + + Invalid ADB + Ungültiges ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + Das gewählte ADB ist falsch. +Möchten Sie eine andere auswählen, oder lassen Sie es unkonfiguriert? + + + + Select another ADB + Wähle ein anderes ADB + + + + Leave unconfigured + Unkonfiguriert lassen + + + + Save jar file + jar Datei speichern + + + + Get Android connector JAR file + Android-Connector JAR-Datei herunterladen + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Der Android SQLite-Treiber unterstützt keine ladbaren Erweiterungen. + + + + Connection with Android database '%1' lost. + Verbindung mit der Android-Datenbank '%1' verloren. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Portweiterleitung für Gerät %1 für Port %2 konnte nicht erstellt werden. + + + + Could not connect to network host: %1:%2 + Verbindung zum Netzwerk-Host fehlgeschlagen: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Kann nicht mit %1:%2verbinden, da das Passwort ungültig ist. + + + + Unable to execute query on Android device (connection was closed): %1 + Abfrage auf Android-Gerät konnte nicht ausgeführt werden (Verbindung wurde geschlossen): %1 + + + + Error while parsing response from Android: %1 + Fehler beim Parsen der Antwort von Android: %1 + + + + Generic error from Android: %1 + Allgemeiner Fehler von Android: %1 + + + + + Missing 'columns' in response from Android. + Fehlende 'Spalten' in Antwort von Android. + + + + Response from Android has missing data for column '%1' in row %2. + Antwort von Android hat fehlende Daten für Spalte '%1' in Zeile %2. + + + + DbAndroidPathDialog + + + Android database URL + Android-Datenbank-URL + + + + Connection method + Verbindungsmethode + + + + USB cable - port forwarding + USB-Kabel - Portweiterleitung + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Netzwerk (IP-Adresse) + + + + Device + Gerät + + + + IP address + IP-Adresse + + + + Port + Port + + + + Remote access password + Fernzugriffspasswort + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>Dies ist ein Passwort, das im SQLiteStudio Dienst konfiguriert ist, der in die Android-App eingebunden wird.</p> + + + + Application + Anwendung + + + + Filter + Filter + + + + Database + Datenbank + + + + Create a new database directly on the device. + Erstellen Sie eine neue Datenbank direkt auf dem Gerät. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Löschen Sie die aktuell ausgewählte Datenbank vom Gerät. Die aktuell ausgewählte Datenbank ist die ausgewählte in der Liste auf der linken Seite dieser Schaltfläche. + + + + Enter valid IP address. + Geben Sie eine gültige IP-Adresse ein. + + + + Pick Android device. + Wähle Android-Gerät. + + + + Pick Android database. + Wähle die Android Datenbank. + + + + Selected Android application is unknown, or not debuggable. + Ausgewählte Android-Anwendung ist unbekannt, oder nicht debugbar. + + + + Create new database + Neue Datenbank erstellen + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Bitte geben Sie den Namen für die neue Datenbank an. +Die's ist der Name, den die Android-Anwendung zur Verbindung mit der Datenbank verwenden wird: + + + + + + Invalid name + Ungültiger Name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Datenbank mit dem gleichen Namen (%1) existiert bereits auf dem Gerät. +Der Name muss eindeutig sein. + + + + Could not create database '%1', because could not connect to the device. + Datenbank '%1' konnte nicht erstellt werden, da keine Verbindung zum Gerät hergestellt werden konnte. + + + + Could not create database '%1'. +Details: %2 + Datenbank '%1' konnte nicht erstellt werden. +Details: %2 + + + + Delete database + Datenbank löschen + + + + Are you sure you want to delete database '%1' from %2? + Sind Sie sicher, dass Sie die Datenbank '%1' von %2 löschen möchten? + + + + + Error deleting + Fehler beim Löschen + + + + Could not connect to %1 in order to delete database '%2'. + Konnte keine Verbindung zu %1 herstellen, um die Datenbank '%2' zu löschen. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Datenbank mit dem Namen '%1' konnte nicht vom Gerät gelöscht werden. +Android-Gerät hat das Löschen verweigert, oder es war unmöglich. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Verbindung zu Gerät %1 nicht möglich, da die Anwendung %2 nicht auf dem Gerät installiert zu sein scheint. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Verbindung zu Gerät %1 nicht möglich, da die Anwendung %2 nicht debugbar ist. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Verbindung zu Gerät %1 nicht möglich, da die Anwendung %2 nicht auf dem Gerät installiert zu sein scheint. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Verbindung zu Gerät %1 nicht möglich, da nicht auf die '%2' Datenbank zugegriffen werden kann. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Verbindung zu Gerät %1 nicht möglich, da nicht auf die '%2' Datenbank zugegriffen werden kann. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Liste der Datenbanken für Anwendung %1 kann nicht abgerufen werden. Details: %2 + + + + + Could not execute query on database '%1': %2 + Konnte Abfrage in Datenbank '%1' nicht ausführen: %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Konnte Argument '%1' der Abfrage nicht binden, da sein Wert fehlt. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_el_GR.ts b/Plugins/DbAndroid/translations/DbAndroid_el_GR.ts new file mode 100644 index 0000000..7b03ed3 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_el_GR.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_en_US.ts b/Plugins/DbAndroid/translations/DbAndroid_en_US.ts new file mode 100644 index 0000000..e0f95a0 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_en_US.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_es_ES.ts b/Plugins/DbAndroid/translations/DbAndroid_es_ES.ts new file mode 100644 index 0000000..da56375 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_es_ES.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_fa_IR.ts b/Plugins/DbAndroid/translations/DbAndroid_fa_IR.ts new file mode 100644 index 0000000..5c5a782 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_fa_IR.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_fi_FI.ts b/Plugins/DbAndroid/translations/DbAndroid_fi_FI.ts new file mode 100644 index 0000000..4b357f0 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_fi_FI.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_fr_FR.ts b/Plugins/DbAndroid/translations/DbAndroid_fr_FR.ts new file mode 100644 index 0000000..a773c43 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_fr_FR.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + URL de base de données Android invalide ou incomplète. + + + + Android database URL + URL de la base de données Android + + + + Select Android database + Sélectionnez la base de données Android + + + + Select ADB + Sélectionner l'ADB + + + + Using Android Debug Bridge: %1 + Utilisation d'Android Debug Bridge : %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + ADB invalide + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + L'ADB sélectionné est incorrect. +Voulez-vous en sélectionner un autre, ou le laisser non configuré ? + + + + Select another ADB + Sélectionner un autre ADB + + + + Leave unconfigured + Laisser non configuré + + + + Save jar file + Enregistrer le fichier jar + + + + Get Android connector JAR file + Obtenir le fichier JAR du connecteur Android + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Le pilote Android SQLite ne supporte pas les extensions chargeables. + + + + Connection with Android database '%1' lost. + Connexion à la base de données Android '%1' perdue. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Impossible de se connecter à l'appareil %1, car il n'est pas visible depuis votre ordinateur. + + + + Failed to create port forwarding for device %1 for port %2. + Impossible de créer la redirection de port pour l'appareil %1 pour le port %2. + + + + Could not connect to network host: %1:%2 + Impossible de se connecter à l'hôte réseau : %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Impossible de se connecter à %1:%2, car le mot de passe est invalide. + + + + Unable to execute query on Android device (connection was closed): %1 + Impossible d'exécuter la requête sur l'appareil Android (la connexion a été fermée) : %1 + + + + Error while parsing response from Android: %1 + Erreur lors de l'analyse de la réponse d'Android : %1 + + + + Generic error from Android: %1 + Erreur générique d'Android : %1 + + + + + Missing 'columns' in response from Android. + Il manque des 'colonnes' en réponse d'Android. + + + + Response from Android has missing data for column '%1' in row %2. + La réponse d'Android a des données manquantes pour la colonne '%1' à la ligne %2. + + + + DbAndroidPathDialog + + + Android database URL + URL de la base de données Android + + + + Connection method + Méthode de connexion + + + + USB cable - port forwarding + Câble USB - redirection de port + + + + USB cable - sqlite3 command + Câble USB - commande sqlite3 + + + + Network (IP address) + Réseau (adresse IP) + + + + Device + Appareil + + + + IP address + Adresse IP + + + + Port + Port + + + + Remote access password + Mot de passe d'accès distant + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>Ceci est un mot de passe configuré dans le service SQLiteStudio étant intégré dans l'application Android.</p> + + + + Application + Application + + + + Filter + Filtre + + + + Database + Base de données + + + + Create a new database directly on the device. + Créer une nouvelle base de données directement sur l'appareil. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Supprimer la base de données actuellement sélectionnée par l'appareil. La base de données actuellement sélectionnée est celle sélectionnée dans la liste à gauche de ce bouton. + + + + Enter valid IP address. + Entrez une adresse IP valide. + + + + Pick Android device. + Choisir un appareil Android. + + + + Pick Android database. + Choisir la base de données Android. + + + + Selected Android application is unknown, or not debuggable. + L'application Android sélectionnée est inconnue, ou ne peut pas être déboguée. + + + + Create new database + Créer une nouvelle base de données + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Veuillez fournir un nom pour la nouvelle base de données. +C'est le nom que l'application Android utilisera pour se connecter à la base de données : + + + + + + Invalid name + Nom invalide + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Une base de données avec le même nom (%1) existe déjà sur l'appareil. +Le nom doit être unique. + + + + Could not create database '%1', because could not connect to the device. + Impossible de créer la base de données '%1', car il est impossible de se connecter à l'appareil. + + + + Could not create database '%1'. +Details: %2 + Impossible de créer la base de données '%1'. +Détails : %2 + + + + Delete database + Supprimer la base de données + + + + Are you sure you want to delete database '%1' from %2? + Êtes-vous sûr de vouloir supprimer la base de données '%1' de %2? + + + + + Error deleting + Erreur lors de la suppression + + + + Could not connect to %1 in order to delete database '%2'. + Impossible de se connecter à %1 pour supprimer la base de données '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Impossible de supprimer la base de données nommée '%1' de l'appareil. +L'appareil Android a refusé la suppression ou c'était impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Impossible de se connecter à l'appareil %1, car il n'est pas visible depuis votre ordinateur. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Impossible de se connecter à l'appareil %1, car l’application %2 ne semble pas y être installée. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Impossible de se connecter à l'appareil %1, car l'application %2 ne peut pas être débogué. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Impossible de se connecter à l'appareil %1, car la commande '%2' semble être indisponible sur cet appareil. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Impossible de se connecter à l'appareil %1, car '%2' la base de données ne peut pas être accédée sur cet appareil. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Impossible de se connecter à l'appareil %1, car '%2' la base de données n'est pas accessible sur l'appareil. Détails : %3 + + + + Cannot get list of databases for application %1. Details: %2 + Impossible d'obtenir la liste des bases de données pour l'application %1. Détails : %2 + + + + + Could not execute query on database '%1': %2 + Impossible d'exécuter la requête sur la base de données '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Impossible de lier l'argument '%1' de la requête, car la valeur est manquante. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_he_IL.ts b/Plugins/DbAndroid/translations/DbAndroid_he_IL.ts new file mode 100644 index 0000000..38a2c85 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_he_IL.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + מען URL מסד נתוני ×נדרו×יד ×œ× ×ª×§×™×Ÿ ×ו חסר. + + + + Android database URL + מען URL מסד נתוני ×נדרו×יד + + + + Select Android database + בחירת מסד נתוני ×נדרו×יד + + + + Select ADB + בחירת ADB + + + + Using Android Debug Bridge: %1 + שימוש בגשר × ×™×¤×•×™Ö¾×ª×§×œ×™× ×נדרו×יד: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + ADB ×œ× ×ª×§×™×Ÿ + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + בחר ADB נוסף + + + + Leave unconfigured + להש×יר ×œ× ×ž×ª×•×¦×¨ + + + + Save jar file + שמירת קובץ jar + + + + Get Android connector JAR file + קבלת קובץ מחבר ×נדרו×יד JAR + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + ×œ× × ×™×ª×Ÿ להתחבר למ×רח הרשת: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + מען URL מסד נתוני ×נדרו×יד + + + + Connection method + שיטת התחברות + + + + USB cable - port forwarding + כבל USB - הפנית פתחה + + + + USB cable - sqlite3 command + כקל USB - פקודת sqlite3 + + + + Network (IP address) + רשת (כתובת IP) + + + + Device + התקן + + + + IP address + כתובת IP + + + + Port + פתחה + + + + Remote access password + ססמת גישה מרחוק + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + ×™×™×©×•× + + + + Filter + מסנן + + + + Database + מסד × ×ª×•× ×™× + + + + Create a new database directly on the device. + יצירת מסד × ×ª×•× ×™× ×—×“×© ישירות על־גבי ההתקן. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + הזנת כתובת IP תקנית. + + + + Pick Android device. + בחירת מכשיר ×נדרו×יד. + + + + Pick Android database. + בחירת מסד נתוני ×נדרו×יד. + + + + Selected Android application is unknown, or not debuggable. + ×™×™×©×•× ×נדרו×יד שנבחר ×œ× ×™×“×•×¢, ×ו ×œ× ×‘×¨ ניפוי־תקלי×. + + + + Create new database + יצירת מסד × ×ª×•× ×™× ×—×“×© + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + ×©× ×œ× ×ª×§×™×Ÿ + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + מחיקת מסד × ×ª×•× ×™× + + + + Are you sure you want to delete database '%1' from %2? + ×”×× ×œ×ž×—×•×§ מסד × ×ª×•× ×™× '%1' מ־%2? + + + + + Error deleting + שגי×ת מחיקה + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_hu_HU.ts b/Plugins/DbAndroid/translations/DbAndroid_hu_HU.ts new file mode 100644 index 0000000..abde44f --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_hu_HU.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_it_IT.ts b/Plugins/DbAndroid/translations/DbAndroid_it_IT.ts new file mode 100644 index 0000000..d172b18 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_it_IT.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + URL database Android non valido o incompleto. + + + + Android database URL + URL database Android + + + + Select Android database + Seleziona database Android + + + + Select ADB + Seleziona ADB + + + + Using Android Debug Bridge: %1 + Utilizzo di Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + Puoi prelevare il file JAR del connettore Android dal menu Strumenti. Esso è necessario per 2 delle 3 connessioni supportate dal plugin Android. Per maggiori dettagli leggi la documentazione del plugin sulla pagina wiki di <a href="%1">SQLiteStudio</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Impossibile trovare l'applicazione Debug Bridge Android. <a href="%1">Clicca qui</a> per specificare la posizione dell'applicazione ADB, altrimenti il plugin %2 non supporterà le connessioni via cavo USB, solo la connessione di rete.. + + + + Invalid ADB + ADB non Valido + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + L'ADB selezionato non è corretto. +Vuoi selezionarne un altro o lasciarlo non configurato? + + + + Select another ADB + Seleziona un altro ADB + + + + Leave unconfigured + Lascialo non configurato + + + + Save jar file + Salva file jar + + + + Get Android connector JAR file + Ottieni il file JAR del connettore Android + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Il driver SQLite Android non supporta le estensioni caricabili. + + + + Connection with Android database '%1' lost. + Connessione con il database Android '%1' persa. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Impossibile connettersi al dispositivo %1, perché esso non è visibile dal tuo computer. + + + + Failed to create port forwarding for device %1 for port %2. + Creazione dell'inoltro della porta per il dispositivo %1 per la porta %2 non riuscita. + + + + Could not connect to network host: %1:%2 + Impossibile connettersi all'host di rete: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Impossibile connettersi a %1:%2, perché la password non è valida. + + + + Unable to execute query on Android device (connection was closed): %1 + Impossibile eseguire la query sul dispositivo Android (la connessione è stata chiusa): %1 + + + + Error while parsing response from Android: %1 + Errore durante l'analisi della risposta da Android: %1 + + + + Generic error from Android: %1 + Errore generico da Android: %1 + + + + + Missing 'columns' in response from Android. + Colonne mancanti in risposta da Android. + + + + Response from Android has missing data for column '%1' in row %2. + La risposta da Android ha dati mancanti per la colonna '%1' nella riga %2. + + + + DbAndroidPathDialog + + + Android database URL + Url database Android + + + + Connection method + Metodo di connessione + + + + USB cable - port forwarding + Cavo USB - inoltro porta + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Rete (indirizzo IP) + + + + Device + Dispositivo + + + + IP address + Indirizzo IP + + + + Port + Porta + + + + Remote access password + Password di accesso remoto + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>Questa è la password configurata nel servizio SQLiteStudio incorporata nell'applicazione Android.</p> + + + + Application + Applicazione + + + + Filter + Filtro + + + + Database + Database + + + + Create a new database directly on the device. + Crea un nuovo database direttamente sul dispositivo. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Elimina il database attualmente selezionato dal dispositivo. Il database attualmente selezionato è quello selezionato nella lista a sinistra di questo pulsante. + + + + Enter valid IP address. + Inserisci un indirizzo IP valido. + + + + Pick Android device. + Scegli il dispositivo Android. + + + + Pick Android database. + Scegli il database Android. + + + + Selected Android application is unknown, or not debuggable. + L'applicazione Android selezionata è sconosciuta, o non è debuggabile. + + + + Create new database + Crea nuovo database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Si prega di fornire il nome del nuovo database. +È il nome che l'applicazione Android userà per connettersi al database: + + + + + + Invalid name + Nome non valido + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Il database con lo stesso nome (%1) esiste già sul dispositivo. +Il nome deve essere univoco. + + + + Could not create database '%1', because could not connect to the device. + Impossibile creare il database '%1' perché non è stato possibile connettersi al dispositivo. + + + + Could not create database '%1'. +Details: %2 + Impossibile creare il database '%1'. +Dettagli: %2 + + + + Delete database + Elimina database + + + + Are you sure you want to delete database '%1' from %2? + Sei sicuro di voler eliminare il database '%1' da %2? + + + + + Error deleting + Errore nell'eliminazione + + + + Could not connect to %1 in order to delete database '%2'. + Impossibile connettersi a %1 per eliminare il database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Impossibile eliminare il database chiamato '%1' dal dispositivo. +Il dispositivo Android ha rifiutato la cancellazione, o era impossibile. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Impossibile connettersi al dispositivo %1, perché esso non è visibile dal tuo computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Impossibile connettersi al dispositivo %1, perché l'applicazione %2 non sembra essere installata sul dispositivo. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Impossibile connettersi al dispositivo %1 perché l'applicazione %2 non è debuggabile. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Impossibile connettersi al dispositivo %1, perché il comando '%2' non sembra essere disponibile sul dispositivo. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Impossibile connettersi al dispositivo %1 perché '%2' database non è accessibile sul dispositivo. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Impossibile connettersi al dispositivo %1 perché '%2' database non è accessibile sul dispositivo. Dettagli: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Impossibile ottenere l'elenco dei database per l'applicazione %1. Dettagli: %2 + + + + + Could not execute query on database '%1': %2 + Impossibile eseguire la query nel database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Impossibile legare l'argomento '%1' della query, perché manca il suo valore. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_ja_JP.ts b/Plugins/DbAndroid/translations/DbAndroid_ja_JP.ts new file mode 100644 index 0000000..2e37a60 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_ja_JP.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_kaa.ts b/Plugins/DbAndroid/translations/DbAndroid_kaa.ts new file mode 100644 index 0000000..f71c5ae --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_kaa.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_ko_KR.ts b/Plugins/DbAndroid/translations/DbAndroid_ko_KR.ts new file mode 100644 index 0000000..de26ef5 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_ko_KR.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_nl_NL.ts b/Plugins/DbAndroid/translations/DbAndroid_nl_NL.ts new file mode 100644 index 0000000..58e7189 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_nl_NL.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_no_NO.ts b/Plugins/DbAndroid/translations/DbAndroid_no_NO.ts new file mode 100644 index 0000000..b6cb5c1 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_no_NO.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_pl_PL.ts b/Plugins/DbAndroid/translations/DbAndroid_pl_PL.ts new file mode 100644 index 0000000..1597e0d --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_pl_PL.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + NieprawidÅ‚owy lub niekompletny adres URL bazy danych Androida. + + + + Android database URL + Adres URL bazy danych Android + + + + Select Android database + Wybierz bazÄ™ danych Androida + + + + Select ADB + Wybierz ADB + + + + Using Android Debug Bridge: %1 + Używam nastÄ™pujÄ…cego Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + Możesz pobrać plik JAR łącznik Android z menu NarzÄ™dzia. Jest on wymagany do 2 z 3 obsÅ‚ugiwanych połączeÅ„ wtyczki Android. Po wiÄ™cej szczegółów przeczytaj dokumentacjÄ™ na <a href="%1">stronie wiki SQLiteStudio.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Nie można odnaleźć aplikacji Android Debug Bridge. <a href="%1">Kliknij tutaj</a> , aby wskazać lokalizacjÄ™ aplikacji ADB, w przeciwnym razie wtyczka %2 nie bÄ™dzie obsÅ‚ugiwać połączeÅ„ przez kabel USB, a jedynie połączenia sieciowe. + + + + Invalid ADB + NieprawidÅ‚owe ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + Wybrany ADB jest nieprawidÅ‚owa. +Czy chcesz wybrać inny, czy pozostawić go nieskonfigurowanego? + + + + Select another ADB + Wybierz inny ADB + + + + Leave unconfigured + Pozostaw nieskonfigurowane + + + + Save jar file + Zapisz plik jar + + + + Get Android connector JAR file + Pobierz plik JAR łącznika Android + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Sterownik Android SQLite nie obsÅ‚uguje wczytywanych rozszerzeÅ„. + + + + Connection with Android database '%1' lost. + Połączenie z bazÄ… danych Androida '%1' zostaÅ‚o utracone. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Nie można połączyć siÄ™ z urzÄ…dzeniem %1, ponieważ nie jest ono widoczne z komputera. + + + + Failed to create port forwarding for device %1 for port %2. + Nie udaÅ‚o siÄ™ utworzyć przekierowywania portów dla urzÄ…dzenia %1 dla portu %2. + + + + Could not connect to network host: %1:%2 + Nie udaÅ‚o siÄ™ połączyć z adresem sieciowym: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Nie można połączyć siÄ™ z %1:%2, ponieważ hasÅ‚o jest nieprawidÅ‚owe. + + + + Unable to execute query on Android device (connection was closed): %1 + Nie można wykonać zapytania na urzÄ…dzeniu z Androidem (połączenie zostaÅ‚o zamkniÄ™te): %1 + + + + Error while parsing response from Android: %1 + Błąd podczas analizowania odpowiedzi z Androida: %1 + + + + Generic error from Android: %1 + Ogólny błąd z Androida: %1 + + + + + Missing 'columns' in response from Android. + Brakuje 'columns' w odpowiedzi od Androida. + + + + Response from Android has missing data for column '%1' in row %2. + Odpowiedź Androida zawiera brakujÄ…ce dane dla kolumny '%1' w wierszu %2. + + + + DbAndroidPathDialog + + + Android database URL + Adres URL bazy danych Android + + + + Connection method + Metoda połączenia + + + + USB cable - port forwarding + Kabel USB - przekierowanie portu + + + + USB cable - sqlite3 command + Kabel USB - polecenie sqlite3 + + + + Network (IP address) + Sieć (adres IP) + + + + Device + UrzÄ…dzenie + + + + IP address + Adres IP + + + + Port + Port + + + + Remote access password + HasÅ‚o zdalnego dostÄ™pu + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>To jest hasÅ‚o skonfigurowane w usÅ‚udze SQLiteStudio osadzonej w aplikacji Android.</p> + + + + Application + Aplikacja + + + + Filter + Filtr + + + + Database + Baza danych + + + + Create a new database directly on the device. + Utwórz nowÄ… bazÄ™ danych bezpoÅ›rednio na urzÄ…dzeniu. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + UsuÅ„ aktualnie wybranÄ… bazÄ™ danych z urzÄ…dzenia. Obecnie wybrana baza danych jest wybrana z listy po lewej stronie tego przycisku. + + + + Enter valid IP address. + Wprowadź poprawny adres IP. + + + + Pick Android device. + Wybierz urzÄ…dzenie Android. + + + + Pick Android database. + Wybierz bazÄ™ danych Android. + + + + Selected Android application is unknown, or not debuggable. + Wybrana aplikacja na Androida jest nieznana lub nie można jej debugować. + + + + Create new database + Utwórz nowÄ… bazÄ™ danych + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + ProszÄ™ podać nazwÄ™ dla nowej bazy. +Jest to nazwa, którÄ… aplikacja Androida bÄ™dzie używać przy łączeniu z bazÄ… danych: + + + + + + Invalid name + NieprawidÅ‚owa nazwa + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Baza danych o tej samej nazwie (%1) już istnieje na urzÄ…dzeniu. +Nazwa musi być unikalna. + + + + Could not create database '%1', because could not connect to the device. + Nie można utworzyć bazy danych '%1', ponieważ nie można połączyć siÄ™ z urzÄ…dzeniem. + + + + Could not create database '%1'. +Details: %2 + Nie można utworzyć bazy danych '%1'. +Szczegóły: %2 + + + + Delete database + UsuÅ„ bazÄ™ danych + + + + Are you sure you want to delete database '%1' from %2? + Czy na pewno chcesz usunąć bazÄ™ danych '%1' z %2? + + + + + Error deleting + Błąd podczas usuwania + + + + Could not connect to %1 in order to delete database '%2'. + Nie można połączyć siÄ™ z %1 w celu usuniÄ™cia bazy danych '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Nie można usunąć bazy danych o nazwie '%1' z urzÄ…dzenia. +UrzÄ…dzenie z systemem Android odmówiÅ‚o usuniÄ™cia lub byÅ‚o to niemożliwe. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Nie można połączyć siÄ™ z urzÄ…dzeniem %1, ponieważ nie jest ono widoczne z komputera. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Nie można połączyć siÄ™ z urzÄ…dzeniem %1, ponieważ aplikacja %2 wydaje siÄ™ nie być zainstalowana na urzÄ…dzeniu. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Nie można połączyć siÄ™ z urzÄ…dzeniem %1, ponieważ aplikacja %2 nie jest debugowalna. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Nie można połączyć siÄ™ urzÄ…dzeniem %1, ponieważ wyglÄ…da na to, że polecenie '%2' nie jest dostÄ™pne na tym urzÄ…dzeniu. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Nie można połączyć siÄ™ z urzÄ…dzeniem %1, ponieważ nie ma dostÄ™pu do bazy danych '%2' na urzÄ…dzeniu. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Nie można połączyć siÄ™ z urzÄ…dzeniem %1, ponieważ nie ma dostÄ™pu do bazy danych '%2' na urzÄ…dzeniu. Szczegóły: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Nie można pobrać listy baz danych dla aplikacji %1. Szczegóły: %2 + + + + + Could not execute query on database '%1': %2 + Nie można wykonać zapytania w bazie danych '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Nie można powiÄ…zać argumentu '%1' zapytania, ponieważ brakuje jego wartoÅ›ci. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_pt_BR.ts b/Plugins/DbAndroid/translations/DbAndroid_pt_BR.ts new file mode 100644 index 0000000..517df66 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_pt_BR.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + URL de banco de dados Android inválido ou incompleto. + + + + Android database URL + URL do banco de dados Android + + + + Select Android database + Selecionar banco de dados do Android + + + + Select ADB + Selecionar ADB + + + + Using Android Debug Bridge: %1 + Usando Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + Você pode pegar o arquivo JAR do conector Android no menu Ferramentas. Ele's necessários para 2 de 3 conexões suportadas pelo plugin Android. Para mais detalhes leia documentação do plugin's na <a href="%1">página do wiki do SQLiteStudio'.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Não foi possível encontrar o aplicativo Android Debug Bridge. <a href="%1">Clique aqui</a> para apontar o local do aplicativo ADB, caso contrário, o plugin %2 não suportará conexões de cabo USB, apenas a conexão de rede. + + + + Invalid ADB + ADB inválido + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + O ADB selecionado está incorreto. +Gostaria de selecionar outro ou deixá-lo não configurado? + + + + Select another ADB + Selecione outro ADB + + + + Leave unconfigured + Deixar não configurado + + + + Save jar file + Salvar arquivo jar + + + + Get Android connector JAR file + Obter o arquivo JAR do conector Android + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + O driver Android SQLite não suporta extensões carregáveis. + + + + Connection with Android database '%1' lost. + Conexão com banco de dados Android '%1' perdida. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Não é possível conectar-se ao dispositivo %1, porque não está visível para o seu computador. + + + + Failed to create port forwarding for device %1 for port %2. + Falha ao criar redirecionamento de porta para o dispositivo %1 para a porta %2. + + + + Could not connect to network host: %1:%2 + Não foi possível conectar ao host de rede: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Não é possível se conectar a %1:%2, porque a senha é inválida. + + + + Unable to execute query on Android device (connection was closed): %1 + Não foi possível executar consulta no dispositivo Android (a conexão foi fechada): %1 + + + + Error while parsing response from Android: %1 + Erro ao analisar a resposta do Android: %1 + + + + Generic error from Android: %1 + Erro genérico do Android: %1 + + + + + Missing 'columns' in response from Android. + Faltando 'colunas' na resposta do Android. + + + + Response from Android has missing data for column '%1' in row %2. + Resposta do Android tem dados faltando para a coluna '%1' na linha %2. + + + + DbAndroidPathDialog + + + Android database URL + URL do banco de dados Android + + + + Connection method + Método de conexão + + + + USB cable - port forwarding + Cabo USB - encaminhamento de porta + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Rede (endereço IP) + + + + Device + Dispositivo + + + + IP address + Endereço IP + + + + Port + Porta + + + + Remote access password + Senha de acesso remoto + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>Essa senha está configurada no serviço SQLiteStudio a ser enviada no aplicativo Android.</p> + + + + Application + Aplicativo + + + + Filter + Filtro + + + + Database + Banco de dados + + + + Create a new database directly on the device. + Criar um novo banco de dados diretamente no dispositivo. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Excluir banco de dados selecionado do dispositivo. O banco de dados selecionado atualmente é o escolhido na lista à esquerda deste botão. + + + + Enter valid IP address. + Digite um endereço IP válido. + + + + Pick Android device. + Escolher dispositivo Android. + + + + Pick Android database. + Escolher banco de dados Android. + + + + Selected Android application is unknown, or not debuggable. + Aplicativo Android selecionado é desconhecido ou não depurável. + + + + Create new database + Criar novo banco de dados + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Por favor, forneça o nome para o novo banco de dados. +É o nome que o aplicativo Android irá usar para conectar ao banco de dados: + + + + + + Invalid name + Nome inválido + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Banco de dados com o mesmo nome (%1) já existe no dispositivo. +O nome deve ser único. + + + + Could not create database '%1', because could not connect to the device. + Não foi possível criar o banco de dados '%1', porque não foi possível conectar ao dispositivo. + + + + Could not create database '%1'. +Details: %2 + Não foi possível criar o banco de dados '%1'. +Detalhes: %2 + + + + Delete database + Excluir banco de dados + + + + Are you sure you want to delete database '%1' from %2? + Tem certeza que deseja excluir o banco de dados '%1' de %2? + + + + + Error deleting + Erro ao excluir + + + + Could not connect to %1 in order to delete database '%2'. + Não foi possível conectar a %1 para excluir o banco de dados '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Não foi possível apagar o banco de dados com o nome '%1' do dispositivo. +O dispositivo Android recusou a exclusão ou era impossível. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Não é possível conectar-se ao dispositivo %1, porque não está visível para o seu computador. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Não é possível conectar-se ao dispositivo %1, porque o aplicativo %2 parece não estar instalado no dispositivo. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Não é possível conectar-se ao dispositivo %1, porque o app %2 não é depurável. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Não é possível conectar ao dispositivo %1, porque '%2' o comando parece não estar disponível no dispositivo. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Não é possível conectar-se ao dispositivo %1, porque '%2' banco de dados não pode ser acessado no dispositivo. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Não é possível conectar-se ao dispositivo %1, porque '%2' banco de dados não pode ser acessado no dispositivo. Detalhes: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Não é possível obter lista de bancos de dados para aplicação %1. Detalhes: %2 + + + + + Could not execute query on database '%1': %2 + Não foi possível executar a consulta no banco de dados '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Não é possível vincular o argumento '%1' da consulta, porque o valor está faltando. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_pt_PT.ts b/Plugins/DbAndroid/translations/DbAndroid_pt_PT.ts new file mode 100644 index 0000000..8f5afdd --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_pt_PT.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_ro_RO.ts b/Plugins/DbAndroid/translations/DbAndroid_ro_RO.ts new file mode 100644 index 0000000..ba14abc --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_ro_RO.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_ru_RU.ts b/Plugins/DbAndroid/translations/DbAndroid_ru_RU.ts new file mode 100644 index 0000000..c6ab15f --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_ru_RU.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Ðеверный или неполный URL к базе данных Android. + + + + Android database URL + URL к базе данных Android + + + + Select Android database + Выберите базу данных Android + + + + Select ADB + Выберите ADB + + + + Using Android Debug Bridge: %1 + ИÑпользуетÑÑ Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + JAR-файл адаптера Ð´Ð»Ñ Android можно получить в меню ИнÑтрументы. Он необходим Ð´Ð»Ñ 2 из 3 типов подключений, поддерживаемых модулем Ð´Ð»Ñ Android. ПодробноÑти Ñм. в документации к модулю <a href="%1">на вики-Ñтранице SQLiteStudio.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Ðевозможно найти приложение Android Debug Bridge. <a href="%1">Ðажмите здеÑÑŒ</a> Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ñ€Ð°ÑÐ¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ ADB, иначе модуль %2 не будет поддерживать подключение через USB-кабель, возможно будет только подключение по Ñети. + + + + Invalid ADB + Ðекорректный ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + Выбранный ADB некорректен +Хотите выбрать другой или оÑтавить ненаÑтроенным? + + + + Select another ADB + Выбрать другой ADB + + + + Leave unconfigured + ОÑтавить ненаÑтроенным + + + + Save jar file + Сохранить JAR-файл + + + + Get Android connector JAR file + Получить JAR-файл адаптера Ð´Ð»Ñ Android + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Драйвер SQLite Ð´Ð»Ñ Android не поддерживает подгружаемые раÑширениÑ. + + + + Connection with Android database '%1' lost. + Подключение к базе данных Android '%1' утерÑно. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Ðевозможно подключитьÑÑ Ðº уÑтройÑтву %1, поÑкольку оно невидимо на вашем компьютере. + + + + Failed to create port forwarding for device %1 for port %2. + Ðе удалоÑÑŒ проброÑить порт %2 на уÑтройÑтве %1. + + + + Could not connect to network host: %1:%2 + Ðевозможно подключитьÑÑ Ðº Ñетевому узлу: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Ðевозможно подключитьÑÑ Ðº %1:%2, поÑкольку указан неверный пароль. + + + + Unable to execute query on Android device (connection was closed): %1 + Ðевозможно выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° уÑтройÑтве Android (Ñоединение было закрыто): %1 + + + + Error while parsing response from Android: %1 + Ошибка при обработке ответа от Android: %1 + + + + Generic error from Android: %1 + ÐžÐ±Ñ‰Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° от Android: %1 + + + + + Missing 'columns' in response from Android. + Ð’ ответе от Android отÑутÑтвует Ñлемент 'columns'. + + + + Response from Android has missing data for column '%1' in row %2. + Ð’ ответе от Android отÑутÑтвуют данные Ñтолбца '%1' в Ñтроке %2. + + + + DbAndroidPathDialog + + + Android database URL + URL к базе данных Android + + + + Connection method + Метод Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ + + + + USB cable - port forwarding + USB-кабель - Ð¿Ñ€Ð¾Ð±Ñ€Ð¾Ñ Ð¿Ð¾Ñ€Ñ‚Ð° + + + + USB cable - sqlite3 command + USB-кабель - команда sqlite3 + + + + Network (IP address) + Сеть (IP-адреÑ) + + + + Device + УÑтройÑтво + + + + IP address + IP-Ð°Ð´Ñ€ÐµÑ + + + + Port + Порт + + + + Remote access password + Пароль удалённого доÑтупа + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>Это пароль, наÑтроенный в Ñлужбе SQLiteStudio, входÑщей в ÑоÑтав Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Android.</p> + + + + Application + Приложение + + + + Filter + Фильтр + + + + Database + База данных + + + + Create a new database directly on the device. + Создать новую базу данных непоÑредÑтвенно на уÑтройÑтве. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Удалить выбранную базу данных Ñ ÑƒÑтройÑтва. Выбранной ÑвлÑетÑÑ Ð±Ð°Ð·Ð° данных, Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð½Ð°Ñ Ð² ÑпиÑке Ñлева от Ñтой кнопки. + + + + Enter valid IP address. + Введите корректный IP-адреÑ. + + + + Pick Android device. + Выберите уÑтройÑтво Android. + + + + Pick Android database. + Выберите базу данных Android. + + + + Selected Android application is unknown, or not debuggable. + Выбранное приложение Ð´Ð»Ñ Android неизвеÑтно, или не поддерживает отладку. + + + + Create new database + Создать новую базу данных + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + ПожалуйÑта, укажите Ð¸Ð¼Ñ Ð½Ð¾Ð²Ð¾Ð¹ базы данных. +Это имÑ, которое приложение Ð´Ð»Ñ Android будет иÑпользовать Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº базе данных: + + + + + + Invalid name + Ðекорректное Ð¸Ð¼Ñ + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + База данных Ñ Ñ‚Ð°ÐºÐ¸Ð¼ именем (%1) уже ÑущеÑтвует на уÑтройÑтве. +Ð˜Ð¼Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾ быть уникальным. + + + + Could not create database '%1', because could not connect to the device. + Ðевозможно Ñоздать базу данных '%1', поÑкольку невозможно подключитьÑÑ Ðº уÑтройÑтву. + + + + Could not create database '%1'. +Details: %2 + Ðевозможно Ñоздать базу данных '%1'. +ПодробноÑти: %2 + + + + Delete database + Удалить базу данных + + + + Are you sure you want to delete database '%1' from %2? + Ð’Ñ‹ дейÑтвительно хотите удалить базу данных '%1' из %2? + + + + + Error deleting + Ошибка при удалении + + + + Could not connect to %1 in order to delete database '%2'. + Ðевозможно подключитьÑÑ Ðº %1 Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Ðевозможно удалить базу данных Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ '%1' Ñ ÑƒÑтройÑтва. +УÑтройÑтво Android запретило удаление, или оно было невозможно. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Ðевозможно подключитьÑÑ Ðº уÑтройÑтву %1, поÑкольку оно невидимо на вашем компьютере. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Ðевозможно подключитьÑÑ Ðº уÑтройÑтву %1, поÑкольку приложение %2 не уÑтановлено на уÑтройÑтве. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Ðевозможно подключитьÑÑ Ðº уÑтройÑтву %1, поÑкольку приложение %2 не поддерживает отладку. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Ðевозможно подключитьÑÑ Ðº уÑтройÑтву %1, поÑкольку команда '%2' недоÑтупна на уÑтройÑтве. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Ðевозможно подключитьÑÑ Ðº уÑтройÑтву %1, поÑкольку база данных '%2' недоÑтупна на уÑтройÑтве. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Ðевозможно подключитьÑÑ Ðº уÑтройÑтву %1, поÑкольку база данных '%2' недоÑтупна на уÑтройÑтве. ПодробноÑти: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Ðевозможно получить ÑпиÑок баз данных Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ %1. ПодробноÑти: %2 + + + + + Could not execute query on database '%1': %2 + Ðевозможно выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ðº базе данных '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Ðевозможно привÑзать параметр '%1' запроÑа, поÑкольку отÑутÑтвует его значение. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_sk_SK.ts b/Plugins/DbAndroid/translations/DbAndroid_sk_SK.ts new file mode 100644 index 0000000..17ca2bd --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_sk_SK.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_sr_SP.ts b/Plugins/DbAndroid/translations/DbAndroid_sr_SP.ts new file mode 100644 index 0000000..c5dfce0 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_sr_SP.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_sv_SE.ts b/Plugins/DbAndroid/translations/DbAndroid_sv_SE.ts new file mode 100644 index 0000000..9f61fa8 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_sv_SE.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_tr_TR.ts b/Plugins/DbAndroid/translations/DbAndroid_tr_TR.ts new file mode 100644 index 0000000..1379145 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_tr_TR.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_uk_UA.ts b/Plugins/DbAndroid/translations/DbAndroid_uk_UA.ts new file mode 100644 index 0000000..f42c8e9 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_uk_UA.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_vi_VN.ts b/Plugins/DbAndroid/translations/DbAndroid_vi_VN.ts new file mode 100644 index 0000000..76005cc --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_vi_VN.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + Invalid or incomplete Android Database URL. + + + + Android database URL + Android database URL + + + + Select Android database + Select Android database + + + + Select ADB + Select ADB + + + + Using Android Debug Bridge: %1 + Using Android Debug Bridge: %1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + + + + Invalid ADB + Invalid ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + + + + Select another ADB + Select another ADB + + + + Leave unconfigured + Leave unconfigured + + + + Save jar file + Save jar file + + + + Get Android connector JAR file + Get Android connector JAR file + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite driver does not support loadable extensions. + + + + Connection with Android database '%1' lost. + Connection with Android database '%1' lost. + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + Failed to create port forwarding for device %1 for port %2. + + + + Could not connect to network host: %1:%2 + Could not connect to network host: %1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + Cannot connect to %1:%2, because password is invalid. + + + + Unable to execute query on Android device (connection was closed): %1 + Unable to execute query on Android device (connection was closed): %1 + + + + Error while parsing response from Android: %1 + Error while parsing response from Android: %1 + + + + Generic error from Android: %1 + Generic error from Android: %1 + + + + + Missing 'columns' in response from Android. + Missing 'columns' in response from Android. + + + + Response from Android has missing data for column '%1' in row %2. + Response from Android has missing data for column '%1' in row %2. + + + + DbAndroidPathDialog + + + Android database URL + Android database URL + + + + Connection method + Connection method + + + + USB cable - port forwarding + USB cable - port forwarding + + + + USB cable - sqlite3 command + USB cable - sqlite3 command + + + + Network (IP address) + Network (IP address) + + + + Device + Device + + + + IP address + IP address + + + + Port + Port + + + + Remote access password + Remote access password + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + + + + Application + Application + + + + Filter + Filter + + + + Database + Database + + + + Create a new database directly on the device. + Create a new database directly on the device. + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + + + + Enter valid IP address. + Enter valid IP address. + + + + Pick Android device. + Pick Android device. + + + + Pick Android database. + Pick Android database. + + + + Selected Android application is unknown, or not debuggable. + Selected Android application is unknown, or not debuggable. + + + + Create new database + Create new database + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + + + + + + Invalid name + Invalid name + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + Database with the same name (%1) already exists on the device. +The name must be unique. + + + + Could not create database '%1', because could not connect to the device. + Could not create database '%1', because could not connect to the device. + + + + Could not create database '%1'. +Details: %2 + Could not create database '%1'. +Details: %2 + + + + Delete database + Delete database + + + + Are you sure you want to delete database '%1' from %2? + Are you sure you want to delete database '%1' from %2? + + + + + Error deleting + Error deleting + + + + Could not connect to %1 in order to delete database '%2'. + Could not connect to %1 in order to delete database '%2'. + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + + + + Cannot connect to device %1, because the application %2 is not debuggable. + Cannot connect to device %1, because the application %2 is not debuggable. + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + + + + Cannot get list of databases for application %1. Details: %2 + Cannot get list of databases for application %1. Details: %2 + + + + + Could not execute query on database '%1': %2 + Could not execute query on database '%1': %2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + Cannot bind argument '%1' of the query, because it's value is missing. + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_zh_CN.ts b/Plugins/DbAndroid/translations/DbAndroid_zh_CN.ts new file mode 100644 index 0000000..318f662 --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_zh_CN.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + 无效或ä¸å®Œæ•´çš„ Android æ•°æ®åº“ URL。 + + + + Android database URL + Android æ•°æ®åº“ URL + + + + Select Android database + 选择 Android æ•°æ®åº“ + + + + Select ADB + 选择 ADB + + + + Using Android Debug Bridge: %1 + 使用 Android 调试桥(ADB):%1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + 您å¯ä»¥ä»Žå·¥å…·èœå•å–å¾— Android 连接器 JAR 文件。 It's required for 2 of 3 connections supported by the Android plugin. æ›´å¤šç»†èŠ‚è§ SQLiteStudio wiki 页é¢ä¸Šçš„<a href="%1">æ’件文档</a>。 + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + 找ä¸åˆ° ADB 应用程åºã€‚<a href="%1">点击此处</a> æ¥æŒ‡å®š ADB 应用程åºçš„ä½ç½®ã€‚ å¦åˆ™ %2 æ’件将䏿”¯æŒ USB 线缆连接,åªèƒ½ä½¿ç”¨ç½‘络连接.. + + + + Invalid ADB + 无效的 ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + 选择的 ADB 䏿­£ç¡®ã€‚ +选择å¦ä¸€ä¸ªï¼Ÿæˆ–者ä¸åšé…置? + + + + Select another ADB + 选择其他 ADB + + + + Leave unconfigured + ä¸åšé…ç½® + + + + Save jar file + ä¿å­˜ jar 文件 + + + + Get Android connector JAR file + èŽ·å– Android 连接器 JAR 文件 + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite 驱动程åºä¸æ”¯æŒå¯åŠ è½½çš„æ‰©å±•ã€‚ + + + + Connection with Android database '%1' lost. + 与 Android æ•°æ®åº“ '%1' 的连接丢失。 + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + 由于'在您的机器上ä¸å¯è§ï¼Œæ— æ³•连接到设备%1 + + + + Failed to create port forwarding for device %1 for port %2. + 为设备 %1 创建 %2 端å£è½¬å‘失败。 + + + + Could not connect to network host: %1:%2 + 无法连接到网络主机:%1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + 无法连接到 %1:%2ï¼Œå¯†ç æ— æ•ˆã€‚ + + + + Unable to execute query on Android device (connection was closed): %1 + 无法在 Android 设备上执行查询(连接已关闭):%1 + + + + Error while parsing response from Android: %1 + è§£æžæ¥è‡ª Android å“应出错:%1 + + + + Generic error from Android: %1 + æ¥è‡ª Android 的一般错误:%1 + + + + + Missing 'columns' in response from Android. + æ¥è‡ª Android çš„å“应中缺少 'columns'。 + + + + Response from Android has missing data for column '%1' in row %2. + æ¥è‡ª Android çš„å“应中,缺少数æ®è¡Œ %2ã€åˆ— '%1' 的数æ®ã€‚ + + + + DbAndroidPathDialog + + + Android database URL + Android æ•°æ®åº“ URL + + + + Connection method + è¿žæŽ¥æ–¹å¼ + + + + USB cable - port forwarding + USB 线缆 - 端å£è½¬å‘ + + + + USB cable - sqlite3 command + USB 线缆 - sqlite3 命令 + + + + Network (IP address) + 网络(IP 地å€ï¼‰ + + + + Device + 设备 + + + + IP address + IP åœ°å€ + + + + Port + ç«¯å£ + + + + Remote access password + è¿œç¨‹è®¿é—®å¯†ç  + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>这是在 Android 应用程åºä¸­åµŒå…¥çš„为 SQLiteStudio æœåŠ¡é…置的密ç ã€‚</p> + + + + Application + åº”ç”¨ç¨‹åº + + + + Filter + 筛选器 + + + + Database + æ•°æ®åº“ + + + + Create a new database directly on the device. + 直接在该设备上新建一个数æ®åº“。 + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + 从设备中删除当å‰é€‰ä¸­çš„æ•°æ®åº“。当å‰é€‰ä¸­çš„æ•°æ®åº“是此按钮左侧的列表中所选择的数æ®åº“。 + + + + Enter valid IP address. + 请键入有效的 IP 地å€ã€‚ + + + + Pick Android device. + 选择 Android 设备。 + + + + Pick Android database. + 选择 Android æ•°æ®åº“。 + + + + Selected Android application is unknown, or not debuggable. + 所选的 Android åº”ç”¨ç¨‹åºæœªçŸ¥æˆ–者éžå¯è°ƒè¯•。 + + + + Create new database + 创建新数æ®åº“ + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + 请æä¾›æ–°æ•°æ®åº“çš„å称。 +这将是 Android 应用程åºç”¨æ¥è¿žæŽ¥è¯¥æ•°æ®åº“çš„å称: + + + + + + Invalid name + 无效åç§° + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + 设备上已存在相åŒå称的数æ®åº“(%1)。 +åç§°ä¸èƒ½é‡å¤ã€‚ + + + + Could not create database '%1', because could not connect to the device. + 无法创建数æ®åº“ '%1',无法连接到该设备。 + + + + Could not create database '%1'. +Details: %2 + 无法创建数æ®åº“ '%1'。 +详情:%2 + + + + Delete database + 删除数æ®åº“ + + + + Are you sure you want to delete database '%1' from %2? + 确定从 %2 中删除数æ®åº“ '%1' å—? + + + + + Error deleting + 删除出错 + + + + Could not connect to %1 in order to delete database '%2'. + 无法连接到 %1 以删除数æ®åº“ '%2'。 + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + 无法从该设备中删除数æ®åº“ '%1'。 +Android è®¾å¤‡æ‹’ç»æˆ–无法完æˆåˆ é™¤ã€‚ + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + 由于'在您的机器上ä¸å¯è§ï¼Œæ— æ³•连接到设备%1 + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + 无法连接设备 %1ï¼Œåº”ç”¨ç¨‹åº %2 似乎没有在该设备上安装。 + + + + Cannot connect to device %1, because the application %2 is not debuggable. + 无法连接设备 %1ï¼Œåº”ç”¨ç¨‹åº %2 ä¸å¯è°ƒè¯•。 + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + 无法连接设备 %1,命令 '%2' 在该设备上似乎ä¸å¯ç”¨ã€‚ + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + 无法连接设备 %1,因无法访问该设备上的 '%2' æ•°æ®åº“。 + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + 无法连接设备 %1,因无法访问该设备上的 '%2' æ•°æ®åº“。详细信æ¯ï¼š%3 + + + + Cannot get list of databases for application %1. Details: %2 + 无法获å–åº”ç”¨ç¨‹åº %1 的数æ®åº“列表。详细信æ¯ï¼š%2 + + + + + Could not execute query on database '%1': %2 + 无法在数æ®åº“ '%1' 上执行查询:%2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + æ— æ³•åœ¨æ­¤æŸ¥è¯¢ä¸­ç»‘å®šå‚æ•° '%1',因为缺少它的值。 + + + diff --git a/Plugins/DbAndroid/translations/DbAndroid_zh_TW.ts b/Plugins/DbAndroid/translations/DbAndroid_zh_TW.ts new file mode 100644 index 0000000..e39a12f --- /dev/null +++ b/Plugins/DbAndroid/translations/DbAndroid_zh_TW.ts @@ -0,0 +1,352 @@ + + + + + DbAndroid + + + Invalid or incomplete Android Database URL. + 無效或ä¸å®Œæ•´çš„ Android 資料庫 URL。 + + + + Android database URL + Android 資料庫 URL + + + + Select Android database + 鏿“‡ Android 資料庫 + + + + Select ADB + 鏿“‡ ADB + + + + Using Android Debug Bridge: %1 + 使用 Android 除錯橋 (ADB):%1 + + + + You can grab Android connector JAR file from Tools menu. It's required for 2 of 3 connections supported by the Android plugin. For more details read plugin's documentation on <a href="%1">SQLiteStudio's wiki page.</a> + 您å¯ä»¥å¾žå·¥å…·é¸å–®å–å¾— Android è¯çµå™¨ JAR 檔案。Android 外掛支æ´çš„ 3 ç¨®é€£ç·šæ–¹å¼æœ‰ 2 個需è¦å®ƒã€‚更多細節見 SQLiteStudio wiki é é¢ä¸Šçš„<a href="%1">外掛文件</a>。 + + + + Could not find Android Debug Bridge application. <a href="%1">Click here</a> to point out the location of the ADB application, otherwise the %2 plugin will not support USB cable connections, only the network connection.. + 找ä¸åˆ° ADB 應用程å¼ã€‚<a href="%1">é»žé¸æ­¤è™•</a> 來指定 ADB 應用程å¼çš„ä½ç½®ã€‚å¦å‰‡ %2 å¤–æŽ›å°‡ä¸æ”¯æ´ USB 線纜連線,åªèƒ½ä½¿ç”¨ç¶²è·¯é€£ç·š.. + + + + Invalid ADB + 無效的 ADB + + + + The selected ADB is incorrect. +Would you like to select another one, or leave it unconfigured? + 鏿“‡çš„ ADB 䏿­£ç¢ºã€‚ +鏿“‡å¦ä¸€å€‹ï¼Ÿæˆ–者ä¸åšè¨­å®šæª”? + + + + Select another ADB + 鏿“‡å…¶ä»– ADB + + + + Leave unconfigured + ä¸åšè¨­å®šæª” + + + + Save jar file + 儲存 jar 檔案 + + + + Get Android connector JAR file + ç²å– Android è¯çµå™¨ JAR 檔案 + + + + DbAndroidInstance + + + Android SQLite driver does not support loadable extensions. + Android SQLite 驅動程å¼ä¸æ”¯æ´å¯è¼‰å…¥çš„æ“´å……套件。 + + + + Connection with Android database '%1' lost. + 與 Android 資料庫 '%1' 的連線丟失。 + + + + DbAndroidJsonConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Failed to create port forwarding for device %1 for port %2. + 為è£ç½® %1 建立 %2 埠轉發失敗。 + + + + Could not connect to network host: %1:%2 + 無法連線到網路主機:%1:%2 + + + + Cannot connect to %1:%2, because password is invalid. + 無法連線到 %1:%2,密碼無效。 + + + + Unable to execute query on Android device (connection was closed): %1 + 無法在 Android è£ç½®ä¸ŠåŸ·è¡ŒæŸ¥è©¢ (連線已關閉):%1 + + + + Error while parsing response from Android: %1 + è§£æžä¾†è‡ª Android 響應出錯:%1 + + + + Generic error from Android: %1 + 來自 Android 的一般錯誤:%1 + + + + + Missing 'columns' in response from Android. + 來自 Android 的響應中缺少 'columns'。 + + + + Response from Android has missing data for column '%1' in row %2. + 來自 Android 的響應中,缺少資料行 %2ã€åˆ— '%1' 的資料。 + + + + DbAndroidPathDialog + + + Android database URL + Android 資料庫 URL + + + + Connection method + é€£ç·šæ–¹å¼ + + + + USB cable - port forwarding + USB 線纜 - 埠轉發 + + + + USB cable - sqlite3 command + USB 線纜 - sqlite3 命令 + + + + Network (IP address) + 網路 (IP 地å€) + + + + Device + è£ç½® + + + + IP address + IP åœ°å€ + + + + Port + 埠 + + + + Remote access password + é ç«¯è¨ªå•密碼 + + + + <p>This is password configured in the SQLiteStudio service being embeded in the Android application.</p> + <p>這是在 Android 應用程å¼ä¸­åµŒå…¥çš„為 SQLiteStudio æœå‹™è¨­å®šæª”的密碼。</p> + + + + Application + æ‡‰ç”¨ç¨‹å¼ + + + + Filter + 篩é¸å™¨ + + + + Database + 資料庫 + + + + Create a new database directly on the device. + 直接在該è£ç½®ä¸Šæ–°å»ºä¸€å€‹æ•¸æ“šåº«ã€‚ + + + + Delete currently selected database from the device. The currently selected database is the one picked in the list on the left of this button. + 從è£ç½®ä¸­åˆªé™¤ç•¶å‰é¸ä¸­çš„資料庫。當å‰é¸ä¸­çš„資料庫顯示在此按鈕左å´çš„æ¸…單中。 + + + + Enter valid IP address. + è«‹éµå…¥æœ‰æ•ˆçš„ IP 地å€ã€‚ + + + + Pick Android device. + 鏿“‡ Android è£ç½®ã€‚ + + + + Pick Android database. + 鏿“‡ Android 資料庫。 + + + + Selected Android application is unknown, or not debuggable. + 所é¸çš„ Android æ‡‰ç”¨ç¨‹å¼æœªçŸ¥æˆ–者ä¸å¯é™¤éŒ¯ã€‚ + + + + Create new database + 建立新資料庫 + + + + Please provide name for the new database. +It's the name which Android application will use to connect to the database: + è«‹æä¾›æ–°è³‡æ–™åº«çš„å稱。 +Android 應用程å¼å°‡ç”¨æ­¤ä¾†é€£ç·šè©²è³‡æ–™åº«ï¼š + + + + + + Invalid name + 無效å稱 + + + + Database with the same name (%1) already exists on the device. +The name must be unique. + è£ç½®ä¸Šå·²å­˜åœ¨ç›¸åŒå稱的資料庫 (%1)。 +å稱必須唯一。 + + + + Could not create database '%1', because could not connect to the device. + 無法建立資料庫 '%1',無法連線到該è£ç½®ã€‚ + + + + Could not create database '%1'. +Details: %2 + 無法建立資料庫 '%1'。 +詳細資訊:%2 + + + + Delete database + 刪除資料庫 + + + + Are you sure you want to delete database '%1' from %2? + 確定從 %2 中刪除資料庫 '%1' 嗎? + + + + + Error deleting + 刪除出錯 + + + + Could not connect to %1 in order to delete database '%2'. + 無法連線到 %1 以刪除資料庫 '%2'。 + + + + Could not delete database named '%1' from the device. +Android device refused deletion, or it was impossible. + 無法從該è£ç½®ä¸­åˆªé™¤è³‡æ–™åº« '%1'。 +Android è£ç½®æ‹’絕或無法完æˆåˆªé™¤ã€‚ + + + + DbAndroidShellConnection + + + Cannot connect to device %1, because it's not visible from your computer. + Cannot connect to device %1, because it's not visible from your computer. + + + + Cannot connect to device %1, because the application %2 doesn't seem to be installed on the device. + 無法連線è£ç½® %1ï¼Œæ‡‰ç”¨ç¨‹å¼ %2 似乎沒有在該è£ç½®ä¸Šå®‰è£ã€‚ + + + + Cannot connect to device %1, because the application %2 is not debuggable. + 無法連線è£ç½® %1ï¼Œæ‡‰ç”¨ç¨‹å¼ %2 ä¸å¯é™¤éŒ¯ã€‚ + + + + Cannot connect to device %1, because '%2' command doesn't seem to be available on the device. + 無法連線è£ç½® %1,命令 '%2' 在該è£ç½®ä¸Šä¼¼ä¹Žä¸å¯ç”¨ã€‚ + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. + 無法連線è£ç½® %1,無法訪å•該è£ç½®ä¸Šçš„ '%2' 資料庫。 + + + + Cannot connect to device %1, because '%2' database cannot be accessed on the device. Details: %3 + 無法連線è£ç½® %1,無法訪å•該è£ç½®ä¸Šçš„ '%2' 資料庫。詳細資訊:%3 + + + + Cannot get list of databases for application %1. Details: %2 + 無法ç²å–æ‡‰ç”¨ç¨‹å¼ %1 的資料庫清單。詳細資訊:%2 + + + + + Could not execute query on database '%1': %2 + 無法在資料庫 '%1' 上執行查詢:%2 + + + + QObject + + + Cannot bind argument '%1' of the query, because it's value is missing. + 無法在此查詢中繫çµå¼•數 '%1',因為缺少它的值。 + + + diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher.pro b/Plugins/DbSqliteCipher/DbSqliteCipher.pro index 1c9b041..33cd7c6 100644 --- a/Plugins/DbSqliteCipher/DbSqliteCipher.pro +++ b/Plugins/DbSqliteCipher/DbSqliteCipher.pro @@ -26,8 +26,15 @@ HEADERS += dbsqlitecipher.h \ sqlcipher.h mac: { - INCLUDEPATH += /usr/local/opt/openssl/include - LIBS += -L/usr/local/opt/openssl/lib + exists( /opt/local/include/openssl-3/openssl/crypto.h ) { + message( "Configuring OpenSSL from MacPorts" ) + INCLUDEPATH += /opt/local/include/openssl-3 + LIBS += -L/opt/local/lib/openssl-3 + } else { + message( "Configuring OpenSSL from HomeBrew" ) + INCLUDEPATH += /usr/local/opt/openssl/include + LIBS += -L/usr/local/opt/openssl/lib + } } !macx: { LIBS += -L$${PWD}/../deps/lib/$${PLATFORM}/ @@ -81,15 +88,11 @@ QMAKE_CFLAGS_WARN_ON = -Wall -Wno-unused-parameter -Wno-sign-compare -Wno-unused DISTFILES += \ openssl_lic.txt -TRANSLATIONS += DbSqliteCipher_ro_RO.ts \ - DbSqliteCipher_de.ts \ - \ - DbSqliteCipher_it.ts\ - DbSqliteCipher_zh_CN.ts\ - DbSqliteCipher_sk.ts\ - DbSqliteCipher_ru.ts\ - DbSqliteCipher_pt_BR.ts\ - DbSqliteCipher_fr.ts\ - DbSqliteCipher_es.ts\ - DbSqliteCipher_pl.ts + + + + + + + diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_de.qm b/Plugins/DbSqliteCipher/DbSqliteCipher_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/DbSqliteCipher/DbSqliteCipher_de.qm and /dev/null differ diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_de.ts b/Plugins/DbSqliteCipher/DbSqliteCipher_de.ts deleted file mode 100644 index 932d4f7..0000000 --- a/Plugins/DbSqliteCipher/DbSqliteCipher_de.ts +++ /dev/null @@ -1,58 +0,0 @@ - - - - - DbSqliteCipher - - - Password (key) - - - - - Leave empty to create or connect to decrypted database. - - - - - Encryption password - - - - - Cipher - - - - - Must be the same as the one used when creating the database. %1 is the default one. - - - - - KDF iterations - - - - - - Must be the same as the one used when creating the database. %1 is the default. - - - - - Cipher page size - - - - - 1.1 compatibility - - - - - Enabling this option disables HMAC checks introduced in SQLCipher 2.0, thus making the connection compatible with SQLCipher 1.1.x. - - - - diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_es.qm b/Plugins/DbSqliteCipher/DbSqliteCipher_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/DbSqliteCipher/DbSqliteCipher_es.qm and /dev/null differ diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_es.ts b/Plugins/DbSqliteCipher/DbSqliteCipher_es.ts deleted file mode 100644 index c9c3654..0000000 --- a/Plugins/DbSqliteCipher/DbSqliteCipher_es.ts +++ /dev/null @@ -1,58 +0,0 @@ - - - - - DbSqliteCipher - - - Password (key) - - - - - Leave empty to create or connect to decrypted database. - - - - - Encryption password - - - - - Cipher - - - - - Must be the same as the one used when creating the database. %1 is the default one. - - - - - KDF iterations - - - - - - Must be the same as the one used when creating the database. %1 is the default. - - - - - Cipher page size - - - - - 1.1 compatibility - - - - - Enabling this option disables HMAC checks introduced in SQLCipher 2.0, thus making the connection compatible with SQLCipher 1.1.x. - - - - diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_fr.qm b/Plugins/DbSqliteCipher/DbSqliteCipher_fr.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/DbSqliteCipher/DbSqliteCipher_fr.qm and /dev/null differ diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_fr.ts b/Plugins/DbSqliteCipher/DbSqliteCipher_fr.ts deleted file mode 100644 index cc3f2da..0000000 --- a/Plugins/DbSqliteCipher/DbSqliteCipher_fr.ts +++ /dev/null @@ -1,58 +0,0 @@ - - - - - DbSqliteCipher - - - Password (key) - - - - - Leave empty to create or connect to decrypted database. - - - - - Encryption password - - - - - Cipher - - - - - Must be the same as the one used when creating the database. %1 is the default one. - - - - - KDF iterations - - - - - - Must be the same as the one used when creating the database. %1 is the default. - - - - - Cipher page size - - - - - 1.1 compatibility - - - - - Enabling this option disables HMAC checks introduced in SQLCipher 2.0, thus making the connection compatible with SQLCipher 1.1.x. - - - - diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_it.qm b/Plugins/DbSqliteCipher/DbSqliteCipher_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/DbSqliteCipher/DbSqliteCipher_it.qm and /dev/null differ diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_it.ts b/Plugins/DbSqliteCipher/DbSqliteCipher_it.ts deleted file mode 100644 index 435c396..0000000 --- a/Plugins/DbSqliteCipher/DbSqliteCipher_it.ts +++ /dev/null @@ -1,58 +0,0 @@ - - - - - DbSqliteCipher - - - Password (key) - - - - - Leave empty to create or connect to decrypted database. - - - - - Encryption password - - - - - Cipher - - - - - Must be the same as the one used when creating the database. %1 is the default one. - - - - - KDF iterations - - - - - - Must be the same as the one used when creating the database. %1 is the default. - - - - - Cipher page size - - - - - 1.1 compatibility - - - - - Enabling this option disables HMAC checks introduced in SQLCipher 2.0, thus making the connection compatible with SQLCipher 1.1.x. - - - - diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_pl.qm b/Plugins/DbSqliteCipher/DbSqliteCipher_pl.qm deleted file mode 100644 index 38239cc..0000000 Binary files a/Plugins/DbSqliteCipher/DbSqliteCipher_pl.qm and /dev/null differ diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_pl.ts b/Plugins/DbSqliteCipher/DbSqliteCipher_pl.ts deleted file mode 100644 index 3e25fcf..0000000 --- a/Plugins/DbSqliteCipher/DbSqliteCipher_pl.ts +++ /dev/null @@ -1,58 +0,0 @@ - - - - - DbSqliteCipher - - - Password (key) - HasÅ‚o (klucz) - - - - Leave empty to create or connect to decrypted database. - Pozostaw puste, aby stworzyć lub połączyć siÄ™ z niezaszyfrowanÄ… bazÄ…. - - - - Encryption password - HasÅ‚o szyfrowania - - - - Cipher - Szyfr (cihper) - - - - Must be the same as the one used when creating the database. %1 is the default one. - Musi być taki sam jak ten użyty podczas tworzenia bazy. DomyÅ›lny to %1. - - - - KDF iterations - Powtórzenia KDF - - - - - Must be the same as the one used when creating the database. %1 is the default. - MuszÄ… być takie same jak te użyte podczas tworzenia bazy. DomyÅ›lnie to %1. - - - - Cipher page size - Rozmiar strony szyfru - - - - 1.1 compatibility - kompatybilność z 1.1 - - - - Enabling this option disables HMAC checks introduced in SQLCipher 2.0, thus making the connection compatible with SQLCipher 1.1.x. - Włączenie tej opcji wyłącza weryfikacje HMAC wprowadzone w SQLCipher 2.0, stwarzajÄ…c w ten sposób połączenie kompatybilne z SQLCipher 1.1.x. - - - diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_pt_BR.qm b/Plugins/DbSqliteCipher/DbSqliteCipher_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/DbSqliteCipher/DbSqliteCipher_pt_BR.qm and /dev/null differ diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_pt_BR.ts b/Plugins/DbSqliteCipher/DbSqliteCipher_pt_BR.ts deleted file mode 100644 index e656317..0000000 --- a/Plugins/DbSqliteCipher/DbSqliteCipher_pt_BR.ts +++ /dev/null @@ -1,58 +0,0 @@ - - - - - DbSqliteCipher - - - Password (key) - - - - - Leave empty to create or connect to decrypted database. - - - - - Encryption password - - - - - Cipher - - - - - Must be the same as the one used when creating the database. %1 is the default one. - - - - - KDF iterations - - - - - - Must be the same as the one used when creating the database. %1 is the default. - - - - - Cipher page size - - - - - 1.1 compatibility - - - - - Enabling this option disables HMAC checks introduced in SQLCipher 2.0, thus making the connection compatible with SQLCipher 1.1.x. - - - - diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_ro_RO.qm b/Plugins/DbSqliteCipher/DbSqliteCipher_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/DbSqliteCipher/DbSqliteCipher_ro_RO.qm and /dev/null differ diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_ro_RO.ts b/Plugins/DbSqliteCipher/DbSqliteCipher_ro_RO.ts deleted file mode 100644 index 2dc7d47..0000000 --- a/Plugins/DbSqliteCipher/DbSqliteCipher_ro_RO.ts +++ /dev/null @@ -1,58 +0,0 @@ - - - - - DbSqliteCipher - - - Password (key) - - - - - Leave empty to create or connect to decrypted database. - - - - - Encryption password - - - - - Cipher - - - - - Must be the same as the one used when creating the database. %1 is the default one. - - - - - KDF iterations - - - - - - Must be the same as the one used when creating the database. %1 is the default. - - - - - Cipher page size - - - - - 1.1 compatibility - - - - - Enabling this option disables HMAC checks introduced in SQLCipher 2.0, thus making the connection compatible with SQLCipher 1.1.x. - - - - diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_ru.qm b/Plugins/DbSqliteCipher/DbSqliteCipher_ru.qm deleted file mode 100644 index 7431612..0000000 Binary files a/Plugins/DbSqliteCipher/DbSqliteCipher_ru.qm and /dev/null differ diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_ru.ts b/Plugins/DbSqliteCipher/DbSqliteCipher_ru.ts deleted file mode 100644 index ca11a83..0000000 --- a/Plugins/DbSqliteCipher/DbSqliteCipher_ru.ts +++ /dev/null @@ -1,58 +0,0 @@ - - - - - DbSqliteCipher - - - Password (key) - Пароль (ключ) - - - - Leave empty to create or connect to decrypted database. - ОÑтавьте пуÑтым Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº раÑшифрованной базе данных - - - - Encryption password - Пароль Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ - - - - Cipher - Шифр - - - - Must be the same as the one used when creating the database. %1 is the default one. - Должен Ñовпадать Ñ Ð¸Ñпользованным при Ñоздании базы данных. По умолчанию иÑпользуетÑÑ %1. - - - - KDF iterations - КоличеÑтво итераций KDF - - - - - Must be the same as the one used when creating the database. %1 is the default. - Должно Ñовпадать Ñ Ð¸Ñпользованным при Ñоздании базы данных. По умолчанию иÑпользуетÑÑ %1. - - - - Cipher page size - Размер Ñтраницы шифра - - - - 1.1 compatibility - СовмеÑтимоÑть Ñ 1.1 - - - - Enabling this option disables HMAC checks introduced in SQLCipher 2.0, thus making the connection compatible with SQLCipher 1.1.x. - Включение Ñтой опции отключает проверки HMAC, реализованные в SQLCipher 2.0, обеÑÐ¿ÐµÑ‡Ð¸Ð²Ð°Ñ ÑовмеÑтимоÑть Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ñ SQLCipher 1.1.x. - - - diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_sk.qm b/Plugins/DbSqliteCipher/DbSqliteCipher_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/Plugins/DbSqliteCipher/DbSqliteCipher_sk.qm and /dev/null differ diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_sk.ts b/Plugins/DbSqliteCipher/DbSqliteCipher_sk.ts deleted file mode 100644 index 4f3d58b..0000000 --- a/Plugins/DbSqliteCipher/DbSqliteCipher_sk.ts +++ /dev/null @@ -1,58 +0,0 @@ - - - - - DbSqliteCipher - - - Password (key) - - - - - Leave empty to create or connect to decrypted database. - - - - - Encryption password - - - - - Cipher - - - - - Must be the same as the one used when creating the database. %1 is the default one. - - - - - KDF iterations - - - - - - Must be the same as the one used when creating the database. %1 is the default. - - - - - Cipher page size - - - - - 1.1 compatibility - - - - - Enabling this option disables HMAC checks introduced in SQLCipher 2.0, thus making the connection compatible with SQLCipher 1.1.x. - - - - diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_zh_CN.qm b/Plugins/DbSqliteCipher/DbSqliteCipher_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/Plugins/DbSqliteCipher/DbSqliteCipher_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/Plugins/DbSqliteCipher/DbSqliteCipher_zh_CN.ts b/Plugins/DbSqliteCipher/DbSqliteCipher_zh_CN.ts deleted file mode 100644 index 39bc943..0000000 --- a/Plugins/DbSqliteCipher/DbSqliteCipher_zh_CN.ts +++ /dev/null @@ -1,58 +0,0 @@ - - - - - DbSqliteCipher - - - Password (key) - - - - - Leave empty to create or connect to decrypted database. - - - - - Encryption password - - - - - Cipher - - - - - Must be the same as the one used when creating the database. %1 is the default one. - - - - - KDF iterations - - - - - - Must be the same as the one used when creating the database. %1 is the default. - - - - - Cipher page size - - - - - 1.1 compatibility - - - - - Enabling this option disables HMAC checks introduced in SQLCipher 2.0, thus making the connection compatible with SQLCipher 1.1.x. - - - - diff --git a/Plugins/DbSqliteCipher/dbsqlitecipher.cpp b/Plugins/DbSqliteCipher/dbsqlitecipher.cpp index fba9640..b2357d6 100644 --- a/Plugins/DbSqliteCipher/dbsqlitecipher.cpp +++ b/Plugins/DbSqliteCipher/dbsqlitecipher.cpp @@ -45,7 +45,7 @@ QList DbSqliteCipher::getOptionsList() const bool DbSqliteCipher::init() { - Q_INIT_RESOURCE(dbsqlitecipher); + SQLS_INIT_RESOURCE(dbsqlitecipher); if (!SQLITESTUDIO->getExtraLicenseManager()->addLicense(LICENSE_TITLE, ":/license/sqlcipher.txt")) { @@ -66,7 +66,8 @@ bool DbSqliteCipher::init() void DbSqliteCipher::deinit() { SQLITESTUDIO->getExtraLicenseManager()->removeLicense(LICENSE_TITLE); - Q_CLEANUP_RESOURCE(dbsqlitecipher); + SQLITESTUDIO->getExtraLicenseManager()->removeLicense(OPENSSL_TITLE); + SQLS_CLEANUP_RESOURCE(dbsqlitecipher); } Db *DbSqliteCipher::newInstance(const QString &name, const QString &path, const QHash &options) diff --git a/Plugins/DbSqliteCipher/dbsqlitecipher.json b/Plugins/DbSqliteCipher/dbsqlitecipher.json index 08a69bd..7ba1851 100644 --- a/Plugins/DbSqliteCipher/dbsqlitecipher.json +++ b/Plugins/DbSqliteCipher/dbsqlitecipher.json @@ -3,6 +3,6 @@ "title": "SQLCipher", "description": "Provides support for databases encrypted with SQLCipher (https://www.zetetic.net/sqlcipher/). See 'About' dialog for SQLCipher license.", "minAppVersion": 30300, - "version": 10200, + "version": 10201, "author": "SalSoft" } diff --git a/Plugins/DbSqliteCipher/dbsqlitecipherinstance.cpp b/Plugins/DbSqliteCipher/dbsqlitecipherinstance.cpp index 1f838f6..512d5dc 100644 --- a/Plugins/DbSqliteCipher/dbsqlitecipherinstance.cpp +++ b/Plugins/DbSqliteCipher/dbsqlitecipherinstance.cpp @@ -7,6 +7,16 @@ DbSqliteCipherInstance::DbSqliteCipherInstance(const QString& name, const QStrin { } +Db* DbSqliteCipherInstance::clone() const +{ + return new DbSqliteCipherInstance(name, path, connOptions); +} + +QString DbSqliteCipherInstance::getTypeClassName() const +{ + return "DbSqliteCipherInstance"; +} + void DbSqliteCipherInstance::initAfterOpen() { SqlQueryPtr res; @@ -14,7 +24,7 @@ void DbSqliteCipherInstance::initAfterOpen() QString key = connOptions[DbSqliteCipher::PASSWORD_OPT].toString(); if (!key.isEmpty()) { - res = exec(QString("PRAGMA key = '%1';").arg(key), Flag::NO_LOCK); + res = exec(QString("PRAGMA key = '%1';").arg(escapeString(key)), Flag::NO_LOCK); if (res->isError()) qWarning() << "Error while defining SQLCipher key:" << res->getErrorText(); } diff --git a/Plugins/DbSqliteCipher/dbsqlitecipherinstance.h b/Plugins/DbSqliteCipher/dbsqlitecipherinstance.h index 68b2710..21eb1dd 100644 --- a/Plugins/DbSqliteCipher/dbsqlitecipherinstance.h +++ b/Plugins/DbSqliteCipher/dbsqlitecipherinstance.h @@ -22,6 +22,9 @@ class DbSqliteCipherInstance : public AbstractDb3 public: DbSqliteCipherInstance(const QString& name, const QString& path, const QHash& connOptions); + Db* clone() const; + QString getTypeClassName() const; + protected: void initAfterOpen(); QString getAttachSql(Db* otherDb, const QString& generatedAttachName); diff --git a/Plugins/DbSqliteCipher/sqlcipher.c b/Plugins/DbSqliteCipher/sqlcipher.c index 4e39565..0a86228 100644 --- a/Plugins/DbSqliteCipher/sqlcipher.c +++ b/Plugins/DbSqliteCipher/sqlcipher.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.34.1. By combining all the individual C code files into this +** version 3.39.4. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -23,776 +23,6 @@ # define SQLITE_PRIVATE static #endif #define SQLITE_UDL_CAPABLE_PARSER 1 -/************** Begin file ctime.c *******************************************/ -/* -** 2010 February 23 -** -** 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 routines used to report what compile-time options -** SQLite was built with. -*/ - -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* IMP: R-16824-07538 */ - -/* -** Include the configuration header output by 'configure' if we're using the -** autoconf-based build -*/ -#if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) -#include "config.h" -#define SQLITECONFIG_H 1 -#endif - -/* These macros are provided to "stringify" the value of the define -** for those options in which the value is meaningful. */ -#define CTIMEOPT_VAL_(opt) #opt -#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) - -/* Like CTIMEOPT_VAL, but especially for SQLITE_DEFAULT_LOOKASIDE. This -** option requires a separate macro because legal values contain a single -** comma. e.g. (-DSQLITE_DEFAULT_LOOKASIDE="100,100") */ -#define CTIMEOPT_VAL2_(opt1,opt2) #opt1 "," #opt2 -#define CTIMEOPT_VAL2(opt) CTIMEOPT_VAL2_(opt) - -/* -** An array of names of all compile-time options. This array should -** be sorted A-Z. -** -** This array looks large, but in a typical installation actually uses -** only a handful of compile-time options, so most times this array is usually -** rather short and uses little memory space. -*/ -static const char * const sqlcipher_sqlite3azCompileOpt[] = { - -/* -** BEGIN CODE GENERATED BY tool/mkctime.tcl -*/ -#if SQLITE_32BIT_ROWID - "32BIT_ROWID", -#endif -#if SQLITE_4_BYTE_ALIGNED_MALLOC - "4_BYTE_ALIGNED_MALLOC", -#endif -#if SQLITE_64BIT_STATS - "64BIT_STATS", -#endif -#if SQLITE_ALLOW_COVERING_INDEX_SCAN - "ALLOW_COVERING_INDEX_SCAN", -#endif -#if SQLITE_ALLOW_URI_AUTHORITY - "ALLOW_URI_AUTHORITY", -#endif -#ifdef SQLITE_BITMASK_TYPE - "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE), -#endif -#if SQLITE_BUG_COMPATIBLE_20160819 - "BUG_COMPATIBLE_20160819", -#endif -#if SQLITE_CASE_SENSITIVE_LIKE - "CASE_SENSITIVE_LIKE", -#endif -#if SQLITE_CHECK_PAGES - "CHECK_PAGES", -#endif -#if defined(__clang__) && defined(__clang_major__) - "COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "." - CTIMEOPT_VAL(__clang_minor__) "." - CTIMEOPT_VAL(__clang_patchlevel__), -#elif defined(_MSC_VER) - "COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER), -#elif defined(__GNUC__) && defined(__VERSION__) - "COMPILER=gcc-" __VERSION__, -#endif -#if SQLITE_COVERAGE_TEST - "COVERAGE_TEST", -#endif -#if SQLITE_DEBUG - "DEBUG", -#endif -#if SQLITE_DEFAULT_AUTOMATIC_INDEX - "DEFAULT_AUTOMATIC_INDEX", -#endif -#if SQLITE_DEFAULT_AUTOVACUUM - "DEFAULT_AUTOVACUUM", -#endif -#ifdef SQLITE_DEFAULT_CACHE_SIZE - "DEFAULT_CACHE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_CACHE_SIZE), -#endif -#if SQLITE_DEFAULT_CKPTFULLFSYNC - "DEFAULT_CKPTFULLFSYNC", -#endif -#ifdef SQLITE_DEFAULT_FILE_FORMAT - "DEFAULT_FILE_FORMAT=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_FORMAT), -#endif -#ifdef SQLITE_DEFAULT_FILE_PERMISSIONS - "DEFAULT_FILE_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_PERMISSIONS), -#endif -#if SQLITE_DEFAULT_FOREIGN_KEYS - "DEFAULT_FOREIGN_KEYS", -#endif -#ifdef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT - "DEFAULT_JOURNAL_SIZE_LIMIT=" CTIMEOPT_VAL(SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT), -#endif -#ifdef SQLITE_DEFAULT_LOCKING_MODE - "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), -#endif -#ifdef SQLITE_DEFAULT_LOOKASIDE - "DEFAULT_LOOKASIDE=" CTIMEOPT_VAL2(SQLITE_DEFAULT_LOOKASIDE), -#endif -#if SQLITE_DEFAULT_MEMSTATUS - "DEFAULT_MEMSTATUS", -#endif -#ifdef SQLITE_DEFAULT_MMAP_SIZE - "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), -#endif -#ifdef SQLITE_DEFAULT_PAGE_SIZE - "DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_PAGE_SIZE), -#endif -#ifdef SQLITE_DEFAULT_PCACHE_INITSZ - "DEFAULT_PCACHE_INITSZ=" CTIMEOPT_VAL(SQLITE_DEFAULT_PCACHE_INITSZ), -#endif -#ifdef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS - "DEFAULT_PROXYDIR_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_PROXYDIR_PERMISSIONS), -#endif -#if SQLITE_DEFAULT_RECURSIVE_TRIGGERS - "DEFAULT_RECURSIVE_TRIGGERS", -#endif -#ifdef SQLITE_DEFAULT_ROWEST - "DEFAULT_ROWEST=" CTIMEOPT_VAL(SQLITE_DEFAULT_ROWEST), -#endif -#ifdef SQLITE_DEFAULT_SECTOR_SIZE - "DEFAULT_SECTOR_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_SECTOR_SIZE), -#endif -#ifdef SQLITE_DEFAULT_SYNCHRONOUS - "DEFAULT_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_SYNCHRONOUS), -#endif -#ifdef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT - "DEFAULT_WAL_AUTOCHECKPOINT=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_AUTOCHECKPOINT), -#endif -#ifdef SQLITE_DEFAULT_WAL_SYNCHRONOUS - "DEFAULT_WAL_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_SYNCHRONOUS), -#endif -#ifdef SQLITE_DEFAULT_WORKER_THREADS - "DEFAULT_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WORKER_THREADS), -#endif -#if SQLITE_DIRECT_OVERFLOW_READ - "DIRECT_OVERFLOW_READ", -#endif -#if SQLITE_DISABLE_DIRSYNC - "DISABLE_DIRSYNC", -#endif -#if SQLITE_DISABLE_FTS3_UNICODE - "DISABLE_FTS3_UNICODE", -#endif -#if SQLITE_DISABLE_FTS4_DEFERRED - "DISABLE_FTS4_DEFERRED", -#endif -#if SQLITE_DISABLE_INTRINSIC - "DISABLE_INTRINSIC", -#endif -#if SQLITE_DISABLE_LFS - "DISABLE_LFS", -#endif -#if SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS - "DISABLE_PAGECACHE_OVERFLOW_STATS", -#endif -#if SQLITE_DISABLE_SKIPAHEAD_DISTINCT - "DISABLE_SKIPAHEAD_DISTINCT", -#endif -#ifdef SQLITE_ENABLE_8_3_NAMES - "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES), -#endif -#if SQLITE_ENABLE_API_ARMOR - "ENABLE_API_ARMOR", -#endif -#if SQLITE_ENABLE_ATOMIC_WRITE - "ENABLE_ATOMIC_WRITE", -#endif -#if SQLITE_ENABLE_BATCH_ATOMIC_WRITE - "ENABLE_BATCH_ATOMIC_WRITE", -#endif -#if SQLITE_ENABLE_BYTECODE_VTAB - "ENABLE_BYTECODE_VTAB", -#endif -#if SQLITE_ENABLE_CEROD - "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), -#endif -#if SQLITE_ENABLE_COLUMN_METADATA - "ENABLE_COLUMN_METADATA", -#endif -#if SQLITE_ENABLE_COLUMN_USED_MASK - "ENABLE_COLUMN_USED_MASK", -#endif -#if SQLITE_ENABLE_COSTMULT - "ENABLE_COSTMULT", -#endif -#if SQLITE_ENABLE_CURSOR_HINTS - "ENABLE_CURSOR_HINTS", -#endif -#if SQLITE_ENABLE_DBSTAT_VTAB - "ENABLE_DBSTAT_VTAB", -#endif -#if SQLITE_ENABLE_EXPENSIVE_ASSERT - "ENABLE_EXPENSIVE_ASSERT", -#endif -#if SQLITE_ENABLE_FTS1 - "ENABLE_FTS1", -#endif -#if SQLITE_ENABLE_FTS2 - "ENABLE_FTS2", -#endif -#if SQLITE_ENABLE_FTS3 - "ENABLE_FTS3", -#endif -#if SQLITE_ENABLE_FTS3_PARENTHESIS - "ENABLE_FTS3_PARENTHESIS", -#endif -#if SQLITE_ENABLE_FTS3_TOKENIZER - "ENABLE_FTS3_TOKENIZER", -#endif -#if SQLITE_ENABLE_FTS4 - "ENABLE_FTS4", -#endif -#if SQLITE_ENABLE_FTS5 - "ENABLE_FTS5", -#endif -#if SQLITE_ENABLE_GEOPOLY - "ENABLE_GEOPOLY", -#endif -#if SQLITE_ENABLE_HIDDEN_COLUMNS - "ENABLE_HIDDEN_COLUMNS", -#endif -#if SQLITE_ENABLE_ICU - "ENABLE_ICU", -#endif -#if SQLITE_ENABLE_IOTRACE - "ENABLE_IOTRACE", -#endif -#if SQLITE_ENABLE_JSON1 - "ENABLE_JSON1", -#endif -#if SQLITE_ENABLE_LOAD_EXTENSION - "ENABLE_LOAD_EXTENSION", -#endif -#ifdef SQLITE_ENABLE_LOCKING_STYLE - "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), -#endif -#if SQLITE_ENABLE_MEMORY_MANAGEMENT - "ENABLE_MEMORY_MANAGEMENT", -#endif -#if SQLITE_ENABLE_MEMSYS3 - "ENABLE_MEMSYS3", -#endif -#if SQLITE_ENABLE_MEMSYS5 - "ENABLE_MEMSYS5", -#endif -#if SQLITE_ENABLE_MULTIPLEX - "ENABLE_MULTIPLEX", -#endif -#if SQLITE_ENABLE_NORMALIZE - "ENABLE_NORMALIZE", -#endif -#if SQLITE_ENABLE_NULL_TRIM - "ENABLE_NULL_TRIM", -#endif -#if SQLITE_ENABLE_OVERSIZE_CELL_CHECK - "ENABLE_OVERSIZE_CELL_CHECK", -#endif -#if SQLITE_ENABLE_PREUPDATE_HOOK - "ENABLE_PREUPDATE_HOOK", -#endif -#if SQLITE_ENABLE_QPSG - "ENABLE_QPSG", -#endif -#if SQLITE_ENABLE_RBU - "ENABLE_RBU", -#endif -#if SQLITE_ENABLE_RTREE - "ENABLE_RTREE", -#endif -#if SQLITE_ENABLE_SELECTTRACE - "ENABLE_SELECTTRACE", -#endif -#if SQLITE_ENABLE_SESSION - "ENABLE_SESSION", -#endif -#if SQLITE_ENABLE_SNAPSHOT - "ENABLE_SNAPSHOT", -#endif -#if SQLITE_ENABLE_SORTER_REFERENCES - "ENABLE_SORTER_REFERENCES", -#endif -#if SQLITE_ENABLE_SQLLOG - "ENABLE_SQLLOG", -#endif -#if defined(SQLITE_ENABLE_STAT4) - "ENABLE_STAT4", -#endif -#if SQLITE_ENABLE_STMTVTAB - "ENABLE_STMTVTAB", -#endif -#if SQLITE_ENABLE_STMT_SCANSTATUS - "ENABLE_STMT_SCANSTATUS", -#endif -#if SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION - "ENABLE_UNKNOWN_SQL_FUNCTION", -#endif -#if SQLITE_ENABLE_UNLOCK_NOTIFY - "ENABLE_UNLOCK_NOTIFY", -#endif -#if SQLITE_ENABLE_UPDATE_DELETE_LIMIT - "ENABLE_UPDATE_DELETE_LIMIT", -#endif -#if SQLITE_ENABLE_URI_00_ERROR - "ENABLE_URI_00_ERROR", -#endif -#if SQLITE_ENABLE_VFSTRACE - "ENABLE_VFSTRACE", -#endif -#if SQLITE_ENABLE_WHERETRACE - "ENABLE_WHERETRACE", -#endif -#if SQLITE_ENABLE_ZIPVFS - "ENABLE_ZIPVFS", -#endif -#if SQLITE_EXPLAIN_ESTIMATED_ROWS - "EXPLAIN_ESTIMATED_ROWS", -#endif -#if SQLITE_EXTRA_IFNULLROW - "EXTRA_IFNULLROW", -#endif -#ifdef SQLITE_EXTRA_INIT - "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), -#endif -#ifdef SQLITE_EXTRA_SHUTDOWN - "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), -#endif -#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH - "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), -#endif -#if SQLITE_FTS5_ENABLE_TEST_MI - "FTS5_ENABLE_TEST_MI", -#endif -#if SQLITE_FTS5_NO_WITHOUT_ROWID - "FTS5_NO_WITHOUT_ROWID", -#endif -/* BEGIN SQLCIPHER */ -#if SQLITE_HAS_CODEC - "HAS_CODEC", -#endif -/* END SQLCIPHER */ -#if HAVE_ISNAN || SQLITE_HAVE_ISNAN - "HAVE_ISNAN", -#endif -#if SQLITE_HOMEGROWN_RECURSIVE_MUTEX - "HOMEGROWN_RECURSIVE_MUTEX", -#endif -#if SQLITE_IGNORE_AFP_LOCK_ERRORS - "IGNORE_AFP_LOCK_ERRORS", -#endif -#if SQLITE_IGNORE_FLOCK_LOCK_ERRORS - "IGNORE_FLOCK_LOCK_ERRORS", -#endif -#if SQLITE_INLINE_MEMCPY - "INLINE_MEMCPY", -#endif -#if SQLITE_INT64_TYPE - "INT64_TYPE", -#endif -#ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX - "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX), -#endif -#if SQLITE_LIKE_DOESNT_MATCH_BLOBS - "LIKE_DOESNT_MATCH_BLOBS", -#endif -#if SQLITE_LOCK_TRACE - "LOCK_TRACE", -#endif -#if SQLITE_LOG_CACHE_SPILL - "LOG_CACHE_SPILL", -#endif -#ifdef SQLITE_MALLOC_SOFT_LIMIT - "MALLOC_SOFT_LIMIT=" CTIMEOPT_VAL(SQLITE_MALLOC_SOFT_LIMIT), -#endif -#ifdef SQLITE_MAX_ATTACHED - "MAX_ATTACHED=" CTIMEOPT_VAL(SQLITE_MAX_ATTACHED), -#endif -#ifdef SQLITE_MAX_COLUMN - "MAX_COLUMN=" CTIMEOPT_VAL(SQLITE_MAX_COLUMN), -#endif -#ifdef SQLITE_MAX_COMPOUND_SELECT - "MAX_COMPOUND_SELECT=" CTIMEOPT_VAL(SQLITE_MAX_COMPOUND_SELECT), -#endif -#ifdef SQLITE_MAX_DEFAULT_PAGE_SIZE - "MAX_DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_DEFAULT_PAGE_SIZE), -#endif -#ifdef SQLITE_MAX_EXPR_DEPTH - "MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_EXPR_DEPTH), -#endif -#ifdef SQLITE_MAX_FUNCTION_ARG - "MAX_FUNCTION_ARG=" CTIMEOPT_VAL(SQLITE_MAX_FUNCTION_ARG), -#endif -#ifdef SQLITE_MAX_LENGTH - "MAX_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LENGTH), -#endif -#ifdef SQLITE_MAX_LIKE_PATTERN_LENGTH - "MAX_LIKE_PATTERN_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LIKE_PATTERN_LENGTH), -#endif -#ifdef SQLITE_MAX_MEMORY - "MAX_MEMORY=" CTIMEOPT_VAL(SQLITE_MAX_MEMORY), -#endif -#ifdef SQLITE_MAX_MMAP_SIZE - "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE), -#endif -#ifdef SQLITE_MAX_MMAP_SIZE_ - "MAX_MMAP_SIZE_=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE_), -#endif -#ifdef SQLITE_MAX_PAGE_COUNT - "MAX_PAGE_COUNT=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_COUNT), -#endif -#ifdef SQLITE_MAX_PAGE_SIZE - "MAX_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_SIZE), -#endif -#ifdef SQLITE_MAX_SCHEMA_RETRY - "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), -#endif -#ifdef SQLITE_MAX_SQL_LENGTH - "MAX_SQL_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_SQL_LENGTH), -#endif -#ifdef SQLITE_MAX_TRIGGER_DEPTH - "MAX_TRIGGER_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_TRIGGER_DEPTH), -#endif -#ifdef SQLITE_MAX_VARIABLE_NUMBER - "MAX_VARIABLE_NUMBER=" CTIMEOPT_VAL(SQLITE_MAX_VARIABLE_NUMBER), -#endif -#ifdef SQLITE_MAX_VDBE_OP - "MAX_VDBE_OP=" CTIMEOPT_VAL(SQLITE_MAX_VDBE_OP), -#endif -#ifdef SQLITE_MAX_WORKER_THREADS - "MAX_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_MAX_WORKER_THREADS), -#endif -#if SQLITE_MEMDEBUG - "MEMDEBUG", -#endif -#if SQLITE_MIXED_ENDIAN_64BIT_FLOAT - "MIXED_ENDIAN_64BIT_FLOAT", -#endif -#if SQLITE_MMAP_READWRITE - "MMAP_READWRITE", -#endif -#if SQLITE_MUTEX_NOOP - "MUTEX_NOOP", -#endif -#if SQLITE_MUTEX_NREF - "MUTEX_NREF", -#endif -#if SQLITE_MUTEX_OMIT - "MUTEX_OMIT", -#endif -#if SQLITE_MUTEX_PTHREADS - "MUTEX_PTHREADS", -#endif -#if SQLITE_MUTEX_W32 - "MUTEX_W32", -#endif -#if SQLITE_NEED_ERR_NAME - "NEED_ERR_NAME", -#endif -#if SQLITE_NOINLINE - "NOINLINE", -#endif -#if SQLITE_NO_SYNC - "NO_SYNC", -#endif -#if SQLITE_OMIT_ALTERTABLE - "OMIT_ALTERTABLE", -#endif -#if SQLITE_OMIT_ANALYZE - "OMIT_ANALYZE", -#endif -#if SQLITE_OMIT_ATTACH - "OMIT_ATTACH", -#endif -#if SQLITE_OMIT_AUTHORIZATION - "OMIT_AUTHORIZATION", -#endif -#if SQLITE_OMIT_AUTOINCREMENT - "OMIT_AUTOINCREMENT", -#endif -#if SQLITE_OMIT_AUTOINIT - "OMIT_AUTOINIT", -#endif -#if SQLITE_OMIT_AUTOMATIC_INDEX - "OMIT_AUTOMATIC_INDEX", -#endif -#if SQLITE_OMIT_AUTORESET - "OMIT_AUTORESET", -#endif -#if SQLITE_OMIT_AUTOVACUUM - "OMIT_AUTOVACUUM", -#endif -#if SQLITE_OMIT_BETWEEN_OPTIMIZATION - "OMIT_BETWEEN_OPTIMIZATION", -#endif -#if SQLITE_OMIT_BLOB_LITERAL - "OMIT_BLOB_LITERAL", -#endif -#if SQLITE_OMIT_CAST - "OMIT_CAST", -#endif -#if SQLITE_OMIT_CHECK - "OMIT_CHECK", -#endif -#if SQLITE_OMIT_COMPLETE - "OMIT_COMPLETE", -#endif -#if SQLITE_OMIT_COMPOUND_SELECT - "OMIT_COMPOUND_SELECT", -#endif -#if SQLITE_OMIT_CONFLICT_CLAUSE - "OMIT_CONFLICT_CLAUSE", -#endif -#if SQLITE_OMIT_CTE - "OMIT_CTE", -#endif -#if SQLITE_OMIT_DATETIME_FUNCS - "OMIT_DATETIME_FUNCS", -#endif -#if SQLITE_OMIT_DECLTYPE - "OMIT_DECLTYPE", -#endif -#if SQLITE_OMIT_DEPRECATED - "OMIT_DEPRECATED", -#endif -#if SQLITE_OMIT_DISKIO - "OMIT_DISKIO", -#endif -#if SQLITE_OMIT_EXPLAIN - "OMIT_EXPLAIN", -#endif -#if SQLITE_OMIT_FLAG_PRAGMAS - "OMIT_FLAG_PRAGMAS", -#endif -#if SQLITE_OMIT_FLOATING_POINT - "OMIT_FLOATING_POINT", -#endif -#if SQLITE_OMIT_FOREIGN_KEY - "OMIT_FOREIGN_KEY", -#endif -#if SQLITE_OMIT_GET_TABLE - "OMIT_GET_TABLE", -#endif -#if SQLITE_OMIT_HEX_INTEGER - "OMIT_HEX_INTEGER", -#endif -#if SQLITE_OMIT_INCRBLOB - "OMIT_INCRBLOB", -#endif -#if SQLITE_OMIT_INTEGRITY_CHECK - "OMIT_INTEGRITY_CHECK", -#endif -#if SQLITE_OMIT_LIKE_OPTIMIZATION - "OMIT_LIKE_OPTIMIZATION", -#endif -#if SQLITE_OMIT_LOAD_EXTENSION - "OMIT_LOAD_EXTENSION", -#endif -#if SQLITE_OMIT_LOCALTIME - "OMIT_LOCALTIME", -#endif -#if SQLITE_OMIT_LOOKASIDE - "OMIT_LOOKASIDE", -#endif -#if SQLITE_OMIT_MEMORYDB - "OMIT_MEMORYDB", -#endif -#if SQLITE_OMIT_OR_OPTIMIZATION - "OMIT_OR_OPTIMIZATION", -#endif -#if SQLITE_OMIT_PAGER_PRAGMAS - "OMIT_PAGER_PRAGMAS", -#endif -#if SQLITE_OMIT_PARSER_TRACE - "OMIT_PARSER_TRACE", -#endif -#if SQLITE_OMIT_POPEN - "OMIT_POPEN", -#endif -#if SQLITE_OMIT_PRAGMA - "OMIT_PRAGMA", -#endif -#if SQLITE_OMIT_PROGRESS_CALLBACK - "OMIT_PROGRESS_CALLBACK", -#endif -#if SQLITE_OMIT_QUICKBALANCE - "OMIT_QUICKBALANCE", -#endif -#if SQLITE_OMIT_REINDEX - "OMIT_REINDEX", -#endif -#if SQLITE_OMIT_SCHEMA_PRAGMAS - "OMIT_SCHEMA_PRAGMAS", -#endif -#if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS - "OMIT_SCHEMA_VERSION_PRAGMAS", -#endif -#if SQLITE_OMIT_SHARED_CACHE - "OMIT_SHARED_CACHE", -#endif -#if SQLITE_OMIT_SHUTDOWN_DIRECTORIES - "OMIT_SHUTDOWN_DIRECTORIES", -#endif -#if SQLITE_OMIT_SUBQUERY - "OMIT_SUBQUERY", -#endif -#if SQLITE_OMIT_TCL_VARIABLE - "OMIT_TCL_VARIABLE", -#endif -#if SQLITE_OMIT_TEMPDB - "OMIT_TEMPDB", -#endif -#if SQLITE_OMIT_TEST_CONTROL - "OMIT_TEST_CONTROL", -#endif -#if SQLITE_OMIT_TRACE - "OMIT_TRACE", -#endif -#if SQLITE_OMIT_TRIGGER - "OMIT_TRIGGER", -#endif -#if SQLITE_OMIT_TRUNCATE_OPTIMIZATION - "OMIT_TRUNCATE_OPTIMIZATION", -#endif -#if SQLITE_OMIT_UTF16 - "OMIT_UTF16", -#endif -#if SQLITE_OMIT_VACUUM - "OMIT_VACUUM", -#endif -#if SQLITE_OMIT_VIEW - "OMIT_VIEW", -#endif -#if SQLITE_OMIT_VIRTUALTABLE - "OMIT_VIRTUALTABLE", -#endif -#if SQLITE_OMIT_WAL - "OMIT_WAL", -#endif -#if SQLITE_OMIT_WSD - "OMIT_WSD", -#endif -#if SQLITE_OMIT_XFER_OPT - "OMIT_XFER_OPT", -#endif -#if SQLITE_PCACHE_SEPARATE_HEADER - "PCACHE_SEPARATE_HEADER", -#endif -#if SQLITE_PERFORMANCE_TRACE - "PERFORMANCE_TRACE", -#endif -#if SQLITE_POWERSAFE_OVERWRITE - "POWERSAFE_OVERWRITE", -#endif -#if SQLITE_PREFER_PROXY_LOCKING - "PREFER_PROXY_LOCKING", -#endif -#if SQLITE_PROXY_DEBUG - "PROXY_DEBUG", -#endif -#if SQLITE_REVERSE_UNORDERED_SELECTS - "REVERSE_UNORDERED_SELECTS", -#endif -#if SQLITE_RTREE_INT_ONLY - "RTREE_INT_ONLY", -#endif -#if SQLITE_SECURE_DELETE - "SECURE_DELETE", -#endif -#if SQLITE_SMALL_STACK - "SMALL_STACK", -#endif -#ifdef SQLITE_SORTER_PMASZ - "SORTER_PMASZ=" CTIMEOPT_VAL(SQLITE_SORTER_PMASZ), -#endif -#if SQLITE_SOUNDEX - "SOUNDEX", -#endif -#ifdef SQLITE_STAT4_SAMPLES - "STAT4_SAMPLES=" CTIMEOPT_VAL(SQLITE_STAT4_SAMPLES), -#endif -#ifdef SQLITE_STMTJRNL_SPILL - "STMTJRNL_SPILL=" CTIMEOPT_VAL(SQLITE_STMTJRNL_SPILL), -#endif -#if SQLITE_SUBSTR_COMPATIBILITY - "SUBSTR_COMPATIBILITY", -#endif -#if SQLITE_SYSTEM_MALLOC - "SYSTEM_MALLOC", -#endif -#if SQLITE_TCL - "TCL", -#endif -#ifdef SQLITE_TEMP_STORE - "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), -#endif -#if SQLITE_TEST - "TEST", -#endif -#if defined(SQLITE_THREADSAFE) - "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), -#elif defined(THREADSAFE) - "THREADSAFE=" CTIMEOPT_VAL(THREADSAFE), -#else - "THREADSAFE=1", -#endif -#if SQLITE_UNLINK_AFTER_CLOSE - "UNLINK_AFTER_CLOSE", -#endif -#if SQLITE_UNTESTABLE - "UNTESTABLE", -#endif -#if SQLITE_USER_AUTHENTICATION - "USER_AUTHENTICATION", -#endif -#if SQLITE_USE_ALLOCA - "USE_ALLOCA", -#endif -#if SQLITE_USE_FCNTL_TRACE - "USE_FCNTL_TRACE", -#endif -#if SQLITE_USE_URI - "USE_URI", -#endif -#if SQLITE_VDBE_COVERAGE - "VDBE_COVERAGE", -#endif -#if SQLITE_WIN32_MALLOC - "WIN32_MALLOC", -#endif -#if SQLITE_ZERO_MALLOC - "ZERO_MALLOC", -#endif -/* -** END CODE GENERATED BY tool/mkctime.tcl -*/ -}; - -SQLITE_PRIVATE const char **sqlcipher_sqlite3CompileOptions(int *pnOpt){ - *pnOpt = sizeof(sqlcipher_sqlite3azCompileOpt) / sizeof(sqlcipher_sqlite3azCompileOpt[0]); - return (const char**)sqlcipher_sqlite3azCompileOpt; -} - -#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ - -/************** End of ctime.c ***********************************************/ /************** Begin file sqliteInt.h ***************************************/ /* ** 2001 September 15 @@ -996,6 +226,18 @@ SQLITE_PRIVATE const char **sqlcipher_sqlite3CompileOptions(int *pnOpt){ # define MSVC_VERSION 0 #endif +/* +** Some C99 functions in "math.h" are only present for MSVC when its version +** is associated with Visual Studio 2013 or higher. +*/ +#ifndef SQLITE_HAVE_C99_MATH_FUNCS +# if MSVC_VERSION==0 || MSVC_VERSION>=1800 +# define SQLITE_HAVE_C99_MATH_FUNCS (1) +# else +# define SQLITE_HAVE_C99_MATH_FUNCS (0) +# endif +#endif + /* Needed for various definitions... */ #if defined(__GNUC__) && !defined(_GNU_SOURCE) # define _GNU_SOURCE @@ -1046,6 +288,17 @@ SQLITE_PRIVATE const char **sqlcipher_sqlite3CompileOptions(int *pnOpt){ # define _USE_32BIT_TIME_T #endif +/* Optionally #include a user-defined header, whereby compilation options +** may be set prior to where they take effect, but after platform setup. +** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include +** file. +*/ +#ifdef SQLITE_CUSTOM_INCLUDE +# define INC_STRINGIFY_(f) #f +# define INC_STRINGIFY(f) INC_STRINGIFY_(f) +# include INC_STRINGIFY(SQLITE_CUSTOM_INCLUDE) +#endif + /* The public SQLite interface. The _FILE_OFFSET_BITS macro must appear ** first in QNX. Also, the _USE_32BIT_TIME_T macro must appear first for ** MinGW. @@ -1097,7 +350,30 @@ extern "C" { /* -** Provide the ability to override linkage features of the interface. +** Facilitate override of interface linkage and calling conventions. +** Be aware that these macros may not be used within this particular +** translation of the amalgamation and its associated header file. +** +** The SQLITE_EXTERN and SQLITE_API macros are used to instruct the +** compiler that the target identifier should have external linkage. +** +** The SQLITE_CDECL macro is used to set the calling convention for +** public functions that accept a variable number of arguments. +** +** The SQLITE_APICALL macro is used to set the calling convention for +** public functions that accept a fixed number of arguments. +** +** The SQLITE_STDCALL macro is no longer used and is now deprecated. +** +** The SQLITE_CALLBACK macro is used to set the calling convention for +** function pointers. +** +** The SQLITE_SYSAPI macro is used to set the calling convention for +** functions provided by the operating system. +** +** Currently, the SQLITE_CDECL, SQLITE_APICALL, SQLITE_CALLBACK, and +** SQLITE_SYSAPI macros are used only when building for environments +** that require non-default calling conventions. */ #ifndef SQLITE_EXTERN # define SQLITE_EXTERN extern @@ -1177,9 +453,9 @@ extern "C" { ** [sqlcipher_sqlite3_libversion_number()], [sqlcipher_sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.34.1" -#define SQLITE_VERSION_NUMBER 3034001 -#define SQLITE_SOURCE_ID "2021-01-20 14:10:07 10e20c0b43500cfb9bbc0eaa061c57514f715d87238f4d835880cd846b9ealt1" +#define SQLITE_VERSION "3.39.4" +#define SQLITE_VERSION_NUMBER 3039004 +#define SQLITE_SOURCE_ID "2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26alt1" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1591,12 +867,13 @@ SQLITE_API int sqlcipher_sqlite3_exec( #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) #define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8)) +#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) -#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) +#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ /* ** CAPI3REF: Flags For File Open Operations @@ -1604,6 +881,19 @@ SQLITE_API int sqlcipher_sqlite3_exec( ** These bit values are intended for use in the ** 3rd parameter to the [sqlcipher_sqlite3_open_v2()] interface and ** in the 4th parameter to the [sqlcipher_sqlite3_vfs.xOpen] method. +** +** Only those flags marked as "Ok for sqlcipher_sqlite3_open_v2()" may be +** used as the third argument to the [sqlcipher_sqlite3_open_v2()] interface. +** The other flags have historically been ignored by sqlcipher_sqlite3_open_v2(), +** though future versions of SQLite might change so that an error is +** raised if any of the disallowed bits are passed into sqlcipher_sqlite3_open_v2(). +** Applications should not depend on the historical behavior. +** +** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into +** [sqlcipher_sqlite3_open_v2()] does *not* cause the underlying database file +** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into +** [sqlcipher_sqlite3_open_v2()] has historically be a no-op and might become an +** error in future versions of SQLite. */ #define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlcipher_sqlite3_open_v2() */ #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlcipher_sqlite3_open_v2() */ @@ -1626,6 +916,7 @@ SQLITE_API int sqlcipher_sqlite3_exec( #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlcipher_sqlite3_open_v2() */ #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ #define SQLITE_OPEN_NOFOLLOW 0x01000000 /* Ok for sqlcipher_sqlite3_open_v2() */ +#define SQLITE_OPEN_EXRESCODE 0x02000000 /* Extended result codes */ /* Reserved: 0x00F00000 */ /* Legacy compatibility: */ @@ -2182,6 +1473,23 @@ struct sqlcipher_sqlite3_io_methods { ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. ** +** +**
  • [[SQLITE_FCNTL_EXTERNAL_READER]] +** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect +** whether or not there is a database client in another process with a wal-mode +** transaction open on the database or not. It is only available on unix.The +** (void*) argument passed with this file-control should be a pointer to a +** value of type (int). The integer value is set to 1 if the database is a wal +** mode database and there exists at least one client in another process that +** currently has an SQL transaction open on the database. It is set to 0 if +** the database is not a wal-mode db, or if there is no such connection in any +** other process. This opcode cannot be used to detect transactions opened +** by clients within the current process, only within other processes. +** +** +**
  • [[SQLITE_FCNTL_CKSM_FILE]] +** Used by the cksmvfs VFS module only. +** */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 @@ -2221,6 +1529,8 @@ struct sqlcipher_sqlite3_io_methods { #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 +#define SQLITE_FCNTL_EXTERNAL_READER 40 +#define SQLITE_FCNTL_CKSM_FILE 41 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -3169,7 +2479,13 @@ struct sqlcipher_sqlite3_mem_methods { ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether triggers are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in -** which case the trigger setting is not reported back. +** which case the trigger setting is not reported back. +** +**

    Originally this option disabled all triggers. ^(However, since +** SQLite version 3.35.0, TEMP triggers are still allowed even if +** this option is off. So, in other words, this option now only disables +** triggers in the main database schema or in the schemas of ATTACH-ed +** databases.)^ ** ** [[SQLITE_DBCONFIG_ENABLE_VIEW]] **

    SQLITE_DBCONFIG_ENABLE_VIEW
    @@ -3180,7 +2496,13 @@ struct sqlcipher_sqlite3_mem_methods { ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether views are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in -** which case the view setting is not reported back. +** which case the view setting is not reported back. +** +**

    Originally this option disabled all views. ^(However, since +** SQLite version 3.35.0, TEMP views are still allowed even if +** this option is off. So, in other words, this option now only disables +** views in the main database schema or in the schemas of ATTACH-ed +** databases.)^ ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] **

    SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
    @@ -3487,11 +2809,14 @@ SQLITE_API void sqlcipher_sqlite3_set_last_insert_rowid(sqlcipher_sqlite3*,sqlci ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlcipher_sqlite3 ** -** ^This function returns the number of rows modified, inserted or +** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. -** ^Executing any other type of SQL statement does not modify the value -** returned by this function. +** The two functions are identical except for the type of the return value +** and that if the number of rows modified by the most recent INSERT, UPDATE +** or DELETE is greater than the maximum value supported by type "int", then +** the return value of sqlcipher_sqlite3_changes() is undefined. ^Executing any other +** type of SQL statement does not modify the value returned by these functions. ** ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], @@ -3540,16 +2865,21 @@ SQLITE_API void sqlcipher_sqlite3_set_last_insert_rowid(sqlcipher_sqlite3*,sqlci ** */ SQLITE_API int sqlcipher_sqlite3_changes(sqlcipher_sqlite3*); +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_changes64(sqlcipher_sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified ** METHOD: sqlcipher_sqlite3 ** -** ^This function returns the total number of rows inserted, modified or +** ^These functions return the total number of rows inserted, modified or ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed ** since the database connection was opened, including those executed as -** part of trigger programs. ^Executing any other type of SQL statement -** does not affect the value returned by sqlcipher_sqlite3_total_changes(). +** part of trigger programs. The two functions are identical except for the +** type of the return value and that if the number of rows modified by the +** connection exceeds the maximum value supported by type "int", then +** the return value of sqlcipher_sqlite3_total_changes() is undefined. ^Executing +** any other type of SQL statement does not affect the value returned by +** sqlcipher_sqlite3_total_changes(). ** ** ^Changes made as part of [foreign key actions] are included in the ** count, but those made as part of REPLACE constraint resolution are @@ -3577,6 +2907,7 @@ SQLITE_API int sqlcipher_sqlite3_changes(sqlcipher_sqlite3*); ** */ SQLITE_API int sqlcipher_sqlite3_total_changes(sqlcipher_sqlite3*); +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_total_changes64(sqlcipher_sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query @@ -4406,6 +3737,14 @@ SQLITE_API void sqlcipher_sqlite3_progress_handler(sqlcipher_sqlite3*, int, int( ** the default shared cache setting provided by ** [sqlcipher_sqlite3_enable_shared_cache()].)^ ** +** [[OPEN_EXRESCODE]] ^(
    [SQLITE_OPEN_EXRESCODE]
    +**
    The database connection comes up in "extended result code mode". +** In other words, the database behaves has if +** [sqlcipher_sqlite3_extended_result_codes(db,1)] where called on the database +** connection as soon as the connection is created. In addition to setting +** the extended result code mode, this flag also causes [sqlcipher_sqlite3_open_v2()] +** to return an extended result code.
    +** ** [[OPEN_NOFOLLOW]] ^(
    [SQLITE_OPEN_NOFOLLOW]
    **
    The database filename is not allowed to be a symbolic link
    ** )^ @@ -4413,7 +3752,15 @@ SQLITE_API void sqlcipher_sqlite3_progress_handler(sqlcipher_sqlite3*, int, int( ** If the 3rd parameter to sqlcipher_sqlite3_open_v2() is not one of the ** required combinations shown above optionally combined with other ** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] -** then the behavior is undefined. +** then the behavior is undefined. Historic versions of SQLite +** have silently ignored surplus bits in the flags parameter to +** sqlcipher_sqlite3_open_v2(), however that behavior might not be carried through +** into future versions of SQLite and so applications should not rely +** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op +** for sqlcipher_sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause +** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE +** flag is intended for use by the [sqlcipher_sqlite3_vfs|VFS interface] only, and not +** by sqlcipher_sqlite3_open_v2(). ** ** ^The fourth parameter to sqlcipher_sqlite3_open_v2() is the name of the ** [sqlcipher_sqlite3_vfs] object that defines the operating system interface that @@ -4553,6 +3900,7 @@ SQLITE_API void sqlcipher_sqlite3_progress_handler(sqlcipher_sqlite3*, int, int( ** that uses dot-files in place of posix advisory locking. ** file:data.db?mode=readonly ** An error. "readonly" is not a valid option for the "mode" parameter. +** Use "ro" instead: "file:data.db?mode=ro". ** ** ** ^URI hexadecimal escape sequences (%HH) are supported within the path and @@ -4783,13 +4131,14 @@ SQLITE_API void sqlcipher_sqlite3_free_filename(char*); ** sqlcipher_sqlite3_extended_errcode() might change with each API call. ** Except, there are some interfaces that are guaranteed to never ** change the value of the error code. The error-code preserving -** interfaces are: +** interfaces include the following: ** **
      **
    • sqlcipher_sqlite3_errcode() **
    • sqlcipher_sqlite3_extended_errcode() **
    • sqlcipher_sqlite3_errmsg() **
    • sqlcipher_sqlite3_errmsg16() +**
    • sqlcipher_sqlite3_error_offset() **
    ** ** ^The sqlcipher_sqlite3_errmsg() and sqlcipher_sqlite3_errmsg16() return English-language @@ -4804,6 +4153,13 @@ SQLITE_API void sqlcipher_sqlite3_free_filename(char*); ** ^(Memory to hold the error message string is managed internally ** and must not be freed by the application)^. ** +** ^If the most recent error references a specific token in the input +** SQL, the sqlcipher_sqlite3_error_offset() interface returns the byte offset +** of the start of that token. ^The byte offset returned by +** sqlcipher_sqlite3_error_offset() assumes that the input SQL is UTF8. +** ^If the most recent error does not reference a specific token in the input +** SQL, then the sqlcipher_sqlite3_error_offset() function returns -1. +** ** When the serialized [threading mode] is in use, it might be the ** case that a second error occurs on a separate thread in between ** the time of the first error and the call to these interfaces. @@ -4823,6 +4179,7 @@ SQLITE_API int sqlcipher_sqlite3_extended_errcode(sqlcipher_sqlite3 *db); SQLITE_API const char *sqlcipher_sqlite3_errmsg(sqlcipher_sqlite3*); SQLITE_API const void *sqlcipher_sqlite3_errmsg16(sqlcipher_sqlite3*); SQLITE_API const char *sqlcipher_sqlite3_errstr(int); +SQLITE_API int sqlcipher_sqlite3_error_offset(sqlcipher_sqlite3 *db); /* ** CAPI3REF: Prepared Statement Object @@ -5180,12 +4537,17 @@ SQLITE_API int sqlcipher_sqlite3_prepare16_v3( ** are managed by SQLite and are automatically freed when the prepared ** statement is finalized. ** ^The string returned by sqlcipher_sqlite3_expanded_sql(P), on the other hand, -** is obtained from [sqlcipher_sqlite3_malloc()] and must be free by the application +** is obtained from [sqlcipher_sqlite3_malloc()] and must be freed by the application ** by passing it to [sqlcipher_sqlite3_free()]. +** +** ^The sqlcipher_sqlite3_normalized_sql() interface is only available if +** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined. */ SQLITE_API const char *sqlcipher_sqlite3_sql(sqlcipher_sqlite3_stmt *pStmt); SQLITE_API char *sqlcipher_sqlite3_expanded_sql(sqlcipher_sqlite3_stmt *pStmt); +#ifdef SQLITE_ENABLE_NORMALIZE SQLITE_API const char *sqlcipher_sqlite3_normalized_sql(sqlcipher_sqlite3_stmt *pStmt); +#endif /* ** CAPI3REF: Determine If An SQL Statement Writes The Database @@ -5220,6 +4582,19 @@ SQLITE_API const char *sqlcipher_sqlite3_normalized_sql(sqlcipher_sqlite3_stmt * ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so ** sqlcipher_sqlite3_stmt_readonly() returns false for those commands. +** +** ^This routine returns false if there is any possibility that the +** statement might change the database file. ^A false return does +** not guarantee that the statement will change the database file. +** ^For example, an UPDATE statement might have a WHERE clause that +** makes it a no-op, but the sqlcipher_sqlite3_stmt_readonly() result would still +** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a +** read-only no-op if the table already exists, but +** sqlcipher_sqlite3_stmt_readonly() still returns false for such a statement. +** +** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN] +** statement, then sqlcipher_sqlite3_stmt_readonly(X) returns the same value as +** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted. */ SQLITE_API int sqlcipher_sqlite3_stmt_readonly(sqlcipher_sqlite3_stmt *pStmt); @@ -5288,6 +4663,8 @@ SQLITE_API int sqlcipher_sqlite3_stmt_busy(sqlcipher_sqlite3_stmt*); ** ** ^The sqlcipher_sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. +** ^The sqlcipher_sqlite3_value objects returned by [sqlcipher_sqlite3_vtab_rhs_value()] +** are protected. ** ^The sqlcipher_sqlite3_value object returned by ** [sqlcipher_sqlite3_column_value()] is unprotected. ** Unprotected sqlcipher_sqlite3_value objects may only be used as arguments @@ -5389,18 +4766,22 @@ typedef struct sqlcipher_sqlite3_context sqlcipher_sqlite3_context; ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. ** -** ^The fifth argument to the BLOB and string binding interfaces -** is a destructor used to dispose of the BLOB or -** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to the bind API fails, -** except the destructor is not called if the third parameter is a NULL -** pointer or the fourth parameter is negative. -** ^If the fifth argument is -** the special value [SQLITE_STATIC], then SQLite assumes that the -** information is in static, unmanaged space and does not need to be freed. -** ^If the fifth argument has the value [SQLITE_TRANSIENT], then -** SQLite makes its own private copy of the data immediately, before -** the sqlcipher_sqlite3_bind_*() routine returns. +** ^The fifth argument to the BLOB and string binding interfaces controls +** or indicates the lifetime of the object referenced by the third parameter. +** These three options exist: +** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished +** with it may be passed. ^It is called to dispose of the BLOB or string even +** if the call to the bind API fails, except the destructor is not called if +** the third parameter is a NULL pointer or the fourth parameter is negative. +** ^ (2) The special constant, [SQLITE_STATIC], may be passsed to indicate that +** the application remains responsible for disposing of the object. ^In this +** case, the object and the provided pointer to it must remain valid until +** either the prepared statement is finalized or the same SQL parameter is +** bound to something else, whichever occurs sooner. +** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the +** object is to be copied prior to the return from sqlcipher_sqlite3_bind_*(). ^The +** object and pointer to it must remain valid until then. ^SQLite will then +** manage the lifetime of its private copy. ** ** ^The sixth argument to sqlcipher_sqlite3_bind_text64() must be one of ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] @@ -5905,6 +5286,10 @@ SQLITE_API int sqlcipher_sqlite3_data_count(sqlcipher_sqlite3_stmt *pStmt); ** even empty strings, are always zero-terminated. ^The return ** value from sqlcipher_sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. ** +** ^Strings returned by sqlcipher_sqlite3_column_text16() always have the endianness +** which is native to the platform, regardless of the text encoding set +** for the database. +** ** Warning: ^The object returned by [sqlcipher_sqlite3_column_value()] is an ** [unprotected sqlcipher_sqlite3_value] object. In a multithreaded environment, ** an unprotected sqlcipher_sqlite3_value object may only be used safely with @@ -5918,7 +5303,7 @@ SQLITE_API int sqlcipher_sqlite3_data_count(sqlcipher_sqlite3_stmt *pStmt); ** [application-defined SQL functions] or [virtual tables], not within ** top-level application code. ** -** The these routines may attempt to convert the datatype of the result. +** These routines may attempt to convert the datatype of the result. ** ^For example, if the internal representation is FLOAT and a text result ** is requested, [sqlcipher_sqlite3_snprintf()] is used internally to perform the ** conversion automatically. ^(The following table details the conversions @@ -5943,7 +5328,7 @@ SQLITE_API int sqlcipher_sqlite3_data_count(sqlcipher_sqlite3_stmt *pStmt); ** TEXT BLOB No change ** BLOB INTEGER [CAST] to INTEGER ** BLOB FLOAT [CAST] to REAL -** BLOB TEXT Add a zero terminator if needed +** BLOB TEXT [CAST] to TEXT, ensure zero terminator ** ** )^ ** @@ -6142,7 +5527,6 @@ SQLITE_API int sqlcipher_sqlite3_reset(sqlcipher_sqlite3_stmt *pStmt); ** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions, ** index expressions, or the WHERE clause of partial indexes. ** -** ** For best security, the [SQLITE_DIRECTONLY] flag is recommended for ** all application-defined SQL functions that do not need to be ** used inside of triggers, view, CHECK constraints, or other elements of @@ -6152,7 +5536,6 @@ SQLITE_API int sqlcipher_sqlite3_reset(sqlcipher_sqlite3_stmt *pStmt); ** a database file to include invocations of the function with parameters ** chosen by the attacker, which the application will then execute when ** the database file is opened and read. -** ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlcipher_sqlite3_user_data()].)^ @@ -6517,7 +5900,8 @@ SQLITE_API unsigned int sqlcipher_sqlite3_value_subtype(sqlcipher_sqlite3_value* ** object D and returns a pointer to that copy. ^The [sqlcipher_sqlite3_value] returned ** is a [protected sqlcipher_sqlite3_value] object even if the input is not. ** ^The sqlcipher_sqlite3_value_dup(V) interface returns NULL if V is NULL or if a -** memory allocation fails. +** memory allocation fails. ^If V is a [pointer value], then the result +** of sqlcipher_sqlite3_value_dup(V) is a NULL value. ** ** ^The sqlcipher_sqlite3_value_free(V) interface frees an [sqlcipher_sqlite3_value] object ** previously obtained from [sqlcipher_sqlite3_value_dup()]. ^If V is a NULL pointer @@ -7030,6 +6414,19 @@ SQLITE_API int sqlcipher_sqlite3_key_v2( ** The code to implement this API is not available in the public release ** of SQLite. */ +/* SQLCipher usage note: + + If the current database is plaintext SQLCipher will NOT encrypt it. + If the current database is encrypted and pNew==0 or nNew==0, SQLCipher + will NOT decrypt it. + + This routine will ONLY work on an already encrypted database in order + to change the key. + + Conversion from plaintext-to-encrypted or encrypted-to-plaintext should + use an ATTACHed database and the sqlcipher_export() convenience function + as per the SQLCipher Documentation. +*/ SQLITE_API int sqlcipher_sqlite3_rekey( sqlcipher_sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The new key */ @@ -7246,6 +6643,28 @@ SQLITE_API int sqlcipher_sqlite3_get_autocommit(sqlcipher_sqlite3*); */ SQLITE_API sqlcipher_sqlite3 *sqlcipher_sqlite3_db_handle(sqlcipher_sqlite3_stmt*); +/* +** CAPI3REF: Return The Schema Name For A Database Connection +** METHOD: sqlcipher_sqlite3 +** +** ^The sqlcipher_sqlite3_db_name(D,N) interface returns a pointer to the schema name +** for the N-th database on database connection D, or a NULL pointer of N is +** out of range. An N value of 0 means the main database file. An N of 1 is +** the "temp" schema. Larger values of N correspond to various ATTACH-ed +** databases. +** +** Space to hold the string that is returned by sqlcipher_sqlite3_db_name() is managed +** by SQLite itself. The string might be deallocated by any operation that +** changes the schema, including [ATTACH] or [DETACH] or calls to +** [sqlcipher_sqlite3_serialize()] or [sqlcipher_sqlite3_deserialize()], even operations that +** occur on a different thread. Applications that need to +** remember the string long-term should make their own copy. Applications that +** are accessing the same database connection simultaneously on multiple +** threads should mutex-protect calls to this API and should make their own +** private copy of the result prior to releasing the mutex. +*/ +SQLITE_API const char *sqlcipher_sqlite3_db_name(sqlcipher_sqlite3 *db, int N); + /* ** CAPI3REF: Return The Filename For A Database Connection ** METHOD: sqlcipher_sqlite3 @@ -7405,6 +6824,72 @@ SQLITE_API sqlcipher_sqlite3_stmt *sqlcipher_sqlite3_next_stmt(sqlcipher_sqlite3 SQLITE_API void *sqlcipher_sqlite3_commit_hook(sqlcipher_sqlite3*, int(*)(void*), void*); SQLITE_API void *sqlcipher_sqlite3_rollback_hook(sqlcipher_sqlite3*, void(*)(void *), void*); +/* +** CAPI3REF: Autovacuum Compaction Amount Callback +** METHOD: sqlcipher_sqlite3 +** +** ^The sqlcipher_sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback +** function C that is invoked prior to each autovacuum of the database +** file. ^The callback is passed a copy of the generic data pointer (P), +** the schema-name of the attached database that is being autovacuumed, +** the the size of the database file in pages, the number of free pages, +** and the number of bytes per page, respectively. The callback should +** return the number of free pages that should be removed by the +** autovacuum. ^If the callback returns zero, then no autovacuum happens. +** ^If the value returned is greater than or equal to the number of +** free pages, then a complete autovacuum happens. +** +**

    ^If there are multiple ATTACH-ed database files that are being +** modified as part of a transaction commit, then the autovacuum pages +** callback is invoked separately for each file. +** +**

    The callback is not reentrant. The callback function should +** not attempt to invoke any other SQLite interface. If it does, bad +** things may happen, including segmentation faults and corrupt database +** files. The callback function should be a simple function that +** does some arithmetic on its input parameters and returns a result. +** +** ^The X parameter to sqlcipher_sqlite3_autovacuum_pages(D,C,P,X) is an optional +** destructor for the P parameter. ^If X is not NULL, then X(P) is +** invoked whenever the database connection closes or when the callback +** is overwritten by another invocation of sqlcipher_sqlite3_autovacuum_pages(). +** +**

    ^There is only one autovacuum pages callback per database connection. +** ^Each call to the sqlcipher_sqlite3_autovacuum_pages() interface overrides all +** previous invocations for that database connection. ^If the callback +** argument (C) to sqlcipher_sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, +** then the autovacuum steps callback is cancelled. The return value +** from sqlcipher_sqlite3_autovacuum_pages() is normally SQLITE_OK, but might +** be some other error code if something goes wrong. The current +** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other +** return codes might be added in future releases. +** +**

    If no autovacuum pages callback is specified (the usual case) or +** a NULL pointer is provided for the callback, +** then the default behavior is to vacuum all free pages. So, in other +** words, the default behavior is the same as if the callback function +** were something like this: +** +**

    +**     unsigned int demonstration_autovac_pages_callback(
    +**       void *pClientData,
    +**       const char *zSchema,
    +**       unsigned int nDbPage,
    +**       unsigned int nFreePage,
    +**       unsigned int nBytePerPage
    +**     ){
    +**       return nFreePage;
    +**     }
    +** 
    +*/ +SQLITE_API int sqlcipher_sqlite3_autovacuum_pages( + sqlcipher_sqlite3 *db, + unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), + void*, + void(*)(void*) +); + + /* ** CAPI3REF: Data Change Notification Callbacks ** METHOD: sqlcipher_sqlite3 @@ -8046,24 +7531,56 @@ struct sqlcipher_sqlite3_index_info { ** ** These macros define the allowed values for the ** [sqlcipher_sqlite3_index_info].aConstraint[].op field. Each value represents -** an operator that is part of a constraint term in the wHERE clause of +** an operator that is part of a constraint term in the WHERE clause of ** a query that uses a [virtual table]. -*/ -#define SQLITE_INDEX_CONSTRAINT_EQ 2 -#define SQLITE_INDEX_CONSTRAINT_GT 4 -#define SQLITE_INDEX_CONSTRAINT_LE 8 -#define SQLITE_INDEX_CONSTRAINT_LT 16 -#define SQLITE_INDEX_CONSTRAINT_GE 32 -#define SQLITE_INDEX_CONSTRAINT_MATCH 64 -#define SQLITE_INDEX_CONSTRAINT_LIKE 65 -#define SQLITE_INDEX_CONSTRAINT_GLOB 66 -#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 -#define SQLITE_INDEX_CONSTRAINT_NE 68 -#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 -#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 -#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 -#define SQLITE_INDEX_CONSTRAINT_IS 72 -#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 +** +** ^The left-hand operand of the operator is given by the corresponding +** aConstraint[].iColumn field. ^An iColumn of -1 indicates the left-hand +** operand is the rowid. +** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET +** operators have no left-hand operand, and so for those operators the +** corresponding aConstraint[].iColumn is meaningless and should not be +** used. +** +** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through +** value 255 are reserved to represent functions that are overloaded +** by the [xFindFunction|xFindFunction method] of the virtual table +** implementation. +** +** The right-hand operands for each constraint might be accessible using +** the [sqlcipher_sqlite3_vtab_rhs_value()] interface. Usually the right-hand +** operand is only available if it appears as a single constant literal +** in the input SQL. If the right-hand operand is another column or an +** expression (even a constant expression) or a parameter, then the +** sqlcipher_sqlite3_vtab_rhs_value() probably will not be able to extract it. +** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and +** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand +** and hence calls to sqlcipher_sqlite3_vtab_rhs_value() for those operators will +** always return SQLITE_NOTFOUND. +** +** The collating sequence to be used for comparison can be found using +** the [sqlcipher_sqlite3_vtab_collation()] interface. For most real-world virtual +** tables, the collating sequence of constraints does not matter (for example +** because the constraints are numeric) and so the sqlcipher_sqlite3_vtab_collation() +** interface is no commonly needed. +*/ +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 +#define SQLITE_INDEX_CONSTRAINT_LIKE 65 +#define SQLITE_INDEX_CONSTRAINT_GLOB 66 +#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 +#define SQLITE_INDEX_CONSTRAINT_NE 68 +#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 +#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 +#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 +#define SQLITE_INDEX_CONSTRAINT_IS 72 +#define SQLITE_INDEX_CONSTRAINT_LIMIT 73 +#define SQLITE_INDEX_CONSTRAINT_OFFSET 74 +#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* ** CAPI3REF: Register A Virtual Table Implementation @@ -8092,7 +7609,7 @@ struct sqlcipher_sqlite3_index_info { ** destructor. ** ** ^If the third parameter (the pointer to the sqlcipher_sqlite3_module object) is -** NULL then no new module is create and any existing modules with the +** NULL then no new module is created and any existing modules with the ** same name are dropped. ** ** See also: [sqlcipher_sqlite3_drop_modules()] @@ -8866,7 +8383,10 @@ SQLITE_API int sqlcipher_sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_PRNG_SEED 28 #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 #define SQLITE_TESTCTRL_SEEK_COUNT 30 -#define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */ +#define SQLITE_TESTCTRL_TRACEFLAGS 31 +#define SQLITE_TESTCTRL_TUNE 32 +#define SQLITE_TESTCTRL_LOGEST 33 +#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking @@ -9389,6 +8909,16 @@ SQLITE_API int sqlcipher_sqlite3_stmt_status(sqlcipher_sqlite3_stmt*, int op,int ** The counter is incremented on the first [sqlcipher_sqlite3_step()] call of each ** cycle. ** +** [[SQLITE_STMTSTATUS_FILTER_MISS]] +** [[SQLITE_STMTSTATUS_FILTER HIT]] +**
    SQLITE_STMTSTATUS_FILTER_HIT
    +** SQLITE_STMTSTATUS_FILTER_MISS
    +**
    ^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join +** step was bypassed because a Bloom filter returned not-found. The +** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of +** times that the Bloom filter returned a find, and thus the join step +** had to be processed as normal. +** ** [[SQLITE_STMTSTATUS_MEMUSED]]
    SQLITE_STMTSTATUS_MEMUSED
    **
    ^This is the approximate number of bytes of heap memory ** used to store the prepared statement. ^This value is not actually @@ -9403,6 +8933,8 @@ SQLITE_API int sqlcipher_sqlite3_stmt_status(sqlcipher_sqlite3_stmt*, int op,int #define SQLITE_STMTSTATUS_VM_STEP 4 #define SQLITE_STMTSTATUS_REPREPARE 5 #define SQLITE_STMTSTATUS_RUN 6 +#define SQLITE_STMTSTATUS_FILTER_MISS 7 +#define SQLITE_STMTSTATUS_FILTER_HIT 8 #define SQLITE_STMTSTATUS_MEMUSED 99 /* @@ -10066,8 +9598,9 @@ SQLITE_API void sqlcipher_sqlite3_log(int iErrCode, const char *zFormat, ...); ** ** A single database handle may have at most a single write-ahead log callback ** registered at one time. ^Calling [sqlcipher_sqlite3_wal_hook()] replaces any -** previously registered write-ahead log callback. ^Note that the -** [sqlcipher_sqlite3_wal_autocheckpoint()] interface and the +** previously registered write-ahead log callback. ^The return value is +** a copy of the third parameter from the previous call, if any, or 0. +** ^Note that the [sqlcipher_sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] both invoke [sqlcipher_sqlite3_wal_hook()] and will ** overwrite any prior [sqlcipher_sqlite3_wal_hook()] settings. */ @@ -10370,19 +9903,276 @@ SQLITE_API int sqlcipher_sqlite3_vtab_nochange(sqlcipher_sqlite3_context*); /* ** CAPI3REF: Determine The Collation For a Virtual Table Constraint +** METHOD: sqlcipher_sqlite3_index_info ** ** This function may only be called from within a call to the [xBestIndex] -** method of a [virtual table]. +** method of a [virtual table]. This function returns a pointer to a string +** that is the name of the appropriate collation sequence to use for text +** comparisons on the constraint identified by its arguments. +** +** The first argument must be the pointer to the [sqlcipher_sqlite3_index_info] object +** that is the first parameter to the xBestIndex() method. The second argument +** must be an index into the aConstraint[] array belonging to the +** sqlcipher_sqlite3_index_info structure passed to xBestIndex. +** +** Important: +** The first parameter must be the same pointer that is passed into the +** xBestMethod() method. The first parameter may not be a pointer to a +** different [sqlcipher_sqlite3_index_info] object, even an exact copy. +** +** The return value is computed as follows: ** -** The first argument must be the sqlcipher_sqlite3_index_info object that is the -** first parameter to the xBestIndex() method. The second argument must be -** an index into the aConstraint[] array belonging to the sqlcipher_sqlite3_index_info -** structure passed to xBestIndex. This function returns a pointer to a buffer -** containing the name of the collation sequence for the corresponding -** constraint. +**
      +**
    1. If the constraint comes from a WHERE clause expression that contains +** a [COLLATE operator], then the name of the collation specified by +** that COLLATE operator is returned. +**

    2. If there is no COLLATE operator, but the column that is the subject +** of the constraint specifies an alternative collating sequence via +** a [COLLATE clause] on the column definition within the CREATE TABLE +** statement that was passed into [sqlcipher_sqlite3_declare_vtab()], then the +** name of that alternative collating sequence is returned. +**

    3. Otherwise, "BINARY" is returned. +**

    */ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlcipher_sqlite3_vtab_collation(sqlcipher_sqlite3_index_info*,int); +/* +** CAPI3REF: Determine if a virtual table query is DISTINCT +** METHOD: sqlcipher_sqlite3_index_info +** +** This API may only be used from within an [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this +** interface from outside of xBestIndex() is undefined and probably harmful. +** +** ^The sqlcipher_sqlite3_vtab_distinct() interface returns an integer between 0 and +** 3. The integer returned by sqlcipher_sqlite3_vtab_distinct() +** gives the virtual table additional information about how the query +** planner wants the output to be ordered. As long as the virtual table +** can meet the ordering requirements of the query planner, it may set +** the "orderByConsumed" flag. +** +**
    1. +** ^If the sqlcipher_sqlite3_vtab_distinct() interface returns 0, that means +** that the query planner needs the virtual table to return all rows in the +** sort order defined by the "nOrderBy" and "aOrderBy" fields of the +** [sqlcipher_sqlite3_index_info] object. This is the default expectation. If the +** virtual table outputs all rows in sorted order, then it is always safe for +** the xBestIndex method to set the "orderByConsumed" flag, regardless of +** the return value from sqlcipher_sqlite3_vtab_distinct(). +**

    2. +** ^(If the sqlcipher_sqlite3_vtab_distinct() interface returns 1, that means +** that the query planner does not need the rows to be returned in sorted order +** as long as all rows with the same values in all columns identified by the +** "aOrderBy" field are adjacent.)^ This mode is used when the query planner +** is doing a GROUP BY. +**

    3. +** ^(If the sqlcipher_sqlite3_vtab_distinct() interface returns 2, that means +** that the query planner does not need the rows returned in any particular +** order, as long as rows with the same values in all "aOrderBy" columns +** are adjacent.)^ ^(Furthermore, only a single row for each particular +** combination of values in the columns identified by the "aOrderBy" field +** needs to be returned.)^ ^It is always ok for two or more rows with the same +** values in all "aOrderBy" columns to be returned, as long as all such rows +** are adjacent. ^The virtual table may, if it chooses, omit extra rows +** that have the same value for all columns identified by "aOrderBy". +** ^However omitting the extra rows is optional. +** This mode is used for a DISTINCT query. +**

    4. +** ^(If the sqlcipher_sqlite3_vtab_distinct() interface returns 3, that means +** that the query planner needs only distinct rows but it does need the +** rows to be sorted.)^ ^The virtual table implementation is free to omit +** rows that are identical in all aOrderBy columns, if it wants to, but +** it is not required to omit any rows. This mode is used for queries +** that have both DISTINCT and ORDER BY clauses. +**

    +** +** ^For the purposes of comparing virtual table output values to see if the +** values are same value for sorting purposes, two NULL values are considered +** to be the same. In other words, the comparison operator is "IS" +** (or "IS NOT DISTINCT FROM") and not "==". +** +** If a virtual table implementation is unable to meet the requirements +** specified above, then it must not set the "orderByConsumed" flag in the +** [sqlcipher_sqlite3_index_info] object or an incorrect answer may result. +** +** ^A virtual table implementation is always free to return rows in any order +** it wants, as long as the "orderByConsumed" flag is not set. ^When the +** the "orderByConsumed" flag is unset, the query planner will add extra +** [bytecode] to ensure that the final results returned by the SQL query are +** ordered correctly. The use of the "orderByConsumed" flag and the +** sqlcipher_sqlite3_vtab_distinct() interface is merely an optimization. ^Careful +** use of the sqlcipher_sqlite3_vtab_distinct() interface and the "orderByConsumed" +** flag might help queries against a virtual table to run faster. Being +** overly aggressive and setting the "orderByConsumed" flag when it is not +** valid to do so, on the other hand, might cause SQLite to return incorrect +** results. +*/ +SQLITE_API int sqlcipher_sqlite3_vtab_distinct(sqlcipher_sqlite3_index_info*); + +/* +** CAPI3REF: Identify and handle IN constraints in xBestIndex +** +** This interface may only be used from within an +** [xBestIndex|xBestIndex() method] of a [virtual table] implementation. +** The result of invoking this interface from any other context is +** undefined and probably harmful. +** +** ^(A constraint on a virtual table of the form +** "[IN operator|column IN (...)]" is +** communicated to the xBestIndex method as a +** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use +** this constraint, it must set the corresponding +** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under +** the usual mode of handling IN operators, SQLite generates [bytecode] +** that invokes the [xFilter|xFilter() method] once for each value +** on the right-hand side of the IN operator.)^ Thus the virtual table +** only sees a single value from the right-hand side of the IN operator +** at a time. +** +** In some cases, however, it would be advantageous for the virtual +** table to see all values on the right-hand of the IN operator all at +** once. The sqlcipher_sqlite3_vtab_in() interfaces facilitates this in two ways: +** +**
      +**
    1. +** ^A call to sqlcipher_sqlite3_vtab_in(P,N,-1) will return true (non-zero) +** if and only if the [sqlcipher_sqlite3_index_info|P->aConstraint][N] constraint +** is an [IN operator] that can be processed all at once. ^In other words, +** sqlcipher_sqlite3_vtab_in() with -1 in the third argument is a mechanism +** by which the virtual table can ask SQLite if all-at-once processing +** of the IN operator is even possible. +** +**

    2. +** ^A call to sqlcipher_sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates +** to SQLite that the virtual table does or does not want to process +** the IN operator all-at-once, respectively. ^Thus when the third +** parameter (F) is non-negative, this interface is the mechanism by +** which the virtual table tells SQLite how it wants to process the +** IN operator. +**

    +** +** ^The sqlcipher_sqlite3_vtab_in(P,N,F) interface can be invoked multiple times +** within the same xBestIndex method call. ^For any given P,N pair, +** the return value from sqlcipher_sqlite3_vtab_in(P,N,F) will always be the same +** within the same xBestIndex call. ^If the interface returns true +** (non-zero), that means that the constraint is an IN operator +** that can be processed all-at-once. ^If the constraint is not an IN +** operator or cannot be processed all-at-once, then the interface returns +** false. +** +** ^(All-at-once processing of the IN operator is selected if both of the +** following conditions are met: +** +**
      +**
    1. The P->aConstraintUsage[N].argvIndex value is set to a positive +** integer. This is how the virtual table tells SQLite that it wants to +** use the N-th constraint. +** +**

    2. The last call to sqlcipher_sqlite3_vtab_in(P,N,F) for which F was +** non-negative had F>=1. +**

    )^ +** +** ^If either or both of the conditions above are false, then SQLite uses +** the traditional one-at-a-time processing strategy for the IN constraint. +** ^If both conditions are true, then the argvIndex-th parameter to the +** xFilter method will be an [sqlcipher_sqlite3_value] that appears to be NULL, +** but which can be passed to [sqlcipher_sqlite3_vtab_in_first()] and +** [sqlcipher_sqlite3_vtab_in_next()] to find all values on the right-hand side +** of the IN constraint. +*/ +SQLITE_API int sqlcipher_sqlite3_vtab_in(sqlcipher_sqlite3_index_info*, int iCons, int bHandle); + +/* +** CAPI3REF: Find all elements on the right-hand side of an IN constraint. +** +** These interfaces are only useful from within the +** [xFilter|xFilter() method] of a [virtual table] implementation. +** The result of invoking these interfaces from any other context +** is undefined and probably harmful. +** +** The X parameter in a call to sqlcipher_sqlite3_vtab_in_first(X,P) or +** sqlcipher_sqlite3_vtab_in_next(X,P) must be one of the parameters to the +** xFilter method which invokes these routines, and specifically +** a parameter that was previously selected for all-at-once IN constraint +** processing use the [sqlcipher_sqlite3_vtab_in()] interface in the +** [xBestIndex|xBestIndex method]. ^(If the X parameter is not +** an xFilter argument that was selected for all-at-once IN constraint +** processing, then these routines return [SQLITE_MISUSE])^ or perhaps +** exhibit some other undefined or harmful behavior. +** +** ^(Use these routines to access all values on the right-hand side +** of the IN constraint using code like the following: +** +**
    +**    for(rc=sqlcipher_sqlite3_vtab_in_first(pList, &pVal);
    +**        rc==SQLITE_OK && pVal
    +**        rc=sqlcipher_sqlite3_vtab_in_next(pList, &pVal)
    +**    ){
    +**      // do something with pVal
    +**    }
    +**    if( rc!=SQLITE_OK ){
    +**      // an error has occurred
    +**    }
    +** 
    )^ +** +** ^On success, the sqlcipher_sqlite3_vtab_in_first(X,P) and sqlcipher_sqlite3_vtab_in_next(X,P) +** routines return SQLITE_OK and set *P to point to the first or next value +** on the RHS of the IN constraint. ^If there are no more values on the +** right hand side of the IN constraint, then *P is set to NULL and these +** routines return [SQLITE_DONE]. ^The return value might be +** some other value, such as SQLITE_NOMEM, in the event of a malfunction. +** +** The *ppOut values returned by these routines are only valid until the +** next call to either of these routines or until the end of the xFilter +** method from which these routines were called. If the virtual table +** implementation needs to retain the *ppOut values for longer, it must make +** copies. The *ppOut values are [protected sqlcipher_sqlite3_value|protected]. +*/ +SQLITE_API int sqlcipher_sqlite3_vtab_in_first(sqlcipher_sqlite3_value *pVal, sqlcipher_sqlite3_value **ppOut); +SQLITE_API int sqlcipher_sqlite3_vtab_in_next(sqlcipher_sqlite3_value *pVal, sqlcipher_sqlite3_value **ppOut); + +/* +** CAPI3REF: Constraint values in xBestIndex() +** METHOD: sqlcipher_sqlite3_index_info +** +** This API may only be used from within the [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this interface +** from outside of an xBestIndex method are undefined and probably harmful. +** +** ^When the sqlcipher_sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within +** the [xBestIndex] method of a [virtual table] implementation, with P being +** a copy of the [sqlcipher_sqlite3_index_info] object pointer passed into xBestIndex and +** J being a 0-based index into P->aConstraint[], then this routine +** attempts to set *V to the value of the right-hand operand of +** that constraint if the right-hand operand is known. ^If the +** right-hand operand is not known, then *V is set to a NULL pointer. +** ^The sqlcipher_sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if +** and only if *V is set to a value. ^The sqlcipher_sqlite3_vtab_rhs_value(P,J,V) +** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th +** constraint is not available. ^The sqlcipher_sqlite3_vtab_rhs_value() interface +** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if +** something goes wrong. +** +** The sqlcipher_sqlite3_vtab_rhs_value() interface is usually only successful if +** the right-hand operand of a constraint is a literal value in the original +** SQL statement. If the right-hand operand is an expression or a reference +** to some other column or a [host parameter], then sqlcipher_sqlite3_vtab_rhs_value() +** will probably return [SQLITE_NOTFOUND]. +** +** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and +** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such +** constraints, sqlcipher_sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^ +** +** ^The [sqlcipher_sqlite3_value] object returned in *V is a protected sqlcipher_sqlite3_value +** and remains valid for the duration of the xBestIndex method call. +** ^When xBestIndex returns, the sqlcipher_sqlite3_value object returned by +** sqlcipher_sqlite3_vtab_rhs_value() is automatically deallocated. +** +** The "_rhs_" in the name of this routine is an abbreviation for +** "Right-Hand Side". +*/ +SQLITE_API int sqlcipher_sqlite3_vtab_rhs_value(sqlcipher_sqlite3_index_info*, int, sqlcipher_sqlite3_value **ppVal); + /* ** CAPI3REF: Conflict resolution modes ** KEYWORDS: {conflict resolution mode} @@ -10618,6 +10408,15 @@ SQLITE_API int sqlcipher_sqlite3_db_cacheflush(sqlcipher_sqlite3*); ** triggers; or 2 for changes resulting from triggers called by top-level ** triggers; and so forth. ** +** When the [sqlcipher_sqlite3_blob_write()] API is used to update a blob column, +** the pre-update hook is invoked with SQLITE_DELETE. This is because the +** in this case the new values are not available. In this case, when a +** callback made with op==SQLITE_DELETE is actuall a write using the +** sqlcipher_sqlite3_blob_write() API, the [sqlcipher_sqlite3_preupdate_blobwrite()] returns +** the index of the column being written. In other cases, where the +** pre-update hook is being invoked for some other reason, including a +** regular DELETE, sqlcipher_sqlite3_preupdate_blobwrite() returns -1. +** ** See also: [sqlcipher_sqlite3_update_hook()] */ #if defined(SQLITE_ENABLE_PREUPDATE_HOOK) @@ -10638,6 +10437,7 @@ SQLITE_API int sqlcipher_sqlite3_preupdate_old(sqlcipher_sqlite3 *, int, sqlciph SQLITE_API int sqlcipher_sqlite3_preupdate_count(sqlcipher_sqlite3 *); SQLITE_API int sqlcipher_sqlite3_preupdate_depth(sqlcipher_sqlite3 *); SQLITE_API int sqlcipher_sqlite3_preupdate_new(sqlcipher_sqlite3 *, int, sqlcipher_sqlite3_value **); +SQLITE_API int sqlcipher_sqlite3_preupdate_blobwrite(sqlcipher_sqlite3 *); #endif /* @@ -10876,8 +10676,8 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlcipher_sqlite3_snapshot_recover(sqlcipher_ ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory ** allocation error occurs. ** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_DESERIALIZE] option. +** This interface is omitted if SQLite is compiled with the +** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API unsigned char *sqlcipher_sqlite3_serialize( sqlcipher_sqlite3 *db, /* The database connection */ @@ -10924,12 +10724,16 @@ SQLITE_API unsigned char *sqlcipher_sqlite3_serialize( ** database is currently in a read transaction or is involved in a backup ** operation. ** +** It is not possible to deserialized into the TEMP database. If the +** S argument to sqlcipher_sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the +** function returns SQLITE_ERROR. +** ** If sqlcipher_sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then ** [sqlcipher_sqlite3_free()] is invoked on argument P prior to returning. ** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_DESERIALIZE] option. +** This interface is omitted if SQLite is compiled with the +** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API int sqlcipher_sqlite3_deserialize( sqlcipher_sqlite3 *db, /* The database connection */ @@ -11178,6 +10982,38 @@ SQLITE_API int sqlcipher_sqlite3session_create( */ SQLITE_API void sqlcipher_sqlite3session_delete(sqlcipher_sqlite3_session *pSession); +/* +** CAPIREF: Conigure a Session Object +** METHOD: sqlcipher_sqlite3_session +** +** This method is used to configure a session object after it has been +** created. At present the only valid value for the second parameter is +** [SQLITE_SESSION_OBJCONFIG_SIZE]. +** +** Arguments for sqlcipher_sqlite3session_object_config() +** +** The following values may passed as the the 4th parameter to +** sqlcipher_sqlite3session_object_config(). +** +**
    SQLITE_SESSION_OBJCONFIG_SIZE
    +** This option is used to set, clear or query the flag that enables +** the [sqlcipher_sqlite3session_changeset_size()] API. Because it imposes some +** computational overhead, this API is disabled by default. Argument +** pArg must point to a value of type (int). If the value is initially +** 0, then the sqlcipher_sqlite3session_changeset_size() API is disabled. If it +** is greater than 0, then the same API is enabled. Or, if the initial +** value is less than zero, no change is made. In all cases the (int) +** variable is set to 1 if the sqlcipher_sqlite3session_changeset_size() API is +** enabled following the current call, or 0 otherwise. +** +** It is an error (SQLITE_MISUSE) to attempt to modify this setting after +** the first table has been attached to the session object. +*/ +SQLITE_API int sqlcipher_sqlite3session_object_config(sqlcipher_sqlite3_session*, int op, void *pArg); + +/* +*/ +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 /* ** CAPI3REF: Enable Or Disable A Session Object @@ -11422,6 +11258,22 @@ SQLITE_API int sqlcipher_sqlite3session_changeset( void **ppChangeset /* OUT: Buffer containing changeset */ ); +/* +** CAPI3REF: Return An Upper-limit For The Size Of The Changeset +** METHOD: sqlcipher_sqlite3_session +** +** By default, this function always returns 0. For it to return +** a useful result, the sqlcipher_sqlite3_session object must have been configured +** to enable this API using sqlcipher_sqlite3session_object_config() with the +** SQLITE_SESSION_OBJCONFIG_SIZE verb. +** +** When enabled, this function returns an upper limit, in bytes, for the size +** of the changeset that might be produced if sqlcipher_sqlite3session_changeset() were +** called. The final changeset size might be equal to or smaller than the +** size in bytes returned by this function. +*/ +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3session_changeset_size(sqlcipher_sqlite3_session *pSession); + /* ** CAPI3REF: Load The Difference Between Tables Into A Session ** METHOD: sqlcipher_sqlite3_session @@ -11539,6 +11391,14 @@ SQLITE_API int sqlcipher_sqlite3session_patchset( */ SQLITE_API int sqlcipher_sqlite3session_isempty(sqlcipher_sqlite3_session *pSession); +/* +** CAPI3REF: Query for the amount of heap memory used by a session object. +** +** This API returns the total amount of heap memory in bytes currently +** used by the session object passed as the only argument. +*/ +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3session_memory_used(sqlcipher_sqlite3_session *pSession); + /* ** CAPI3REF: Create An Iterator To Traverse A Changeset ** CONSTRUCTOR: sqlcipher_sqlite3_changeset_iter @@ -11641,18 +11501,23 @@ SQLITE_API int sqlcipher_sqlite3changeset_next(sqlcipher_sqlite3_changeset_iter ** call to [sqlcipher_sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this ** is not the case, this function returns [SQLITE_MISUSE]. ** -** If argument pzTab is not NULL, then *pzTab is set to point to a -** nul-terminated utf-8 encoded string containing the name of the table -** affected by the current change. The buffer remains valid until either -** sqlcipher_sqlite3changeset_next() is called on the iterator or until the -** conflict-handler function returns. If pnCol is not NULL, then *pnCol is -** set to the number of columns in the table affected by the change. If -** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change +** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three +** outputs are set through these pointers: +** +** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], +** depending on the type of change that the iterator currently points to; +** +** *pnCol is set to the number of columns in the table affected by the change; and +** +** *pzTab is set to point to a nul-terminated utf-8 encoded string containing +** the name of the table affected by the current change. The buffer remains +** valid until either sqlcipher_sqlite3changeset_next() is called on the iterator +** or until the conflict-handler function returns. +** +** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change ** is an indirect change, or false (0) otherwise. See the documentation for ** [sqlcipher_sqlite3session_indirect()] for a description of direct and indirect -** changes. Finally, if pOp is not NULL, then *pOp is set to one of -** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the -** type of change that the iterator currently points to. +** changes. ** ** If no error occurs, SQLITE_OK is returned. If an error does occur, an ** SQLite error code is returned. The values of the output variables may not @@ -13340,12 +13205,17 @@ struct fts5_api { /************** End of sqlcipher_sqlite3.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ +/* +** Reuse the STATIC_LRU for mutex access to sqlcipher_sqlite3_temp_directory. +*/ +#define SQLITE_MUTEX_STATIC_TEMPDIR SQLITE_MUTEX_STATIC_VFS1 + /* ** Include the configuration header output by 'configure' if we're using the ** autoconf-based build */ #if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) -/* #include "config.h" */ +#include "config.h" #define SQLITECONFIG_H 1 #endif @@ -13582,9 +13452,11 @@ struct fts5_api { # define __has_extension(x) 0 /* compatibility with non-clang compilers */ #endif #if GCC_VERSION>=4007000 || __has_extension(c_atomic) +# define SQLITE_ATOMIC_INTRINSICS 1 # define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED) # define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED) #else +# define SQLITE_ATOMIC_INTRINSICS 0 # define AtomicLoad(PTR) (*(PTR)) # define AtomicStore(PTR,VAL) (*(PTR) = (VAL)) #endif @@ -13789,11 +13661,12 @@ struct fts5_api { ** is significant and used at least once. On switch statements ** where multiple cases go to the same block of code, testcase() ** can insure that all cases are evaluated. -** */ -#ifdef SQLITE_COVERAGE_TEST -SQLITE_PRIVATE void sqlcipher_sqlite3Coverage(int); -# define testcase(X) if( X ){ sqlcipher_sqlite3Coverage(__LINE__); } +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) +# ifndef SQLITE_AMALGAMATION + extern unsigned int sqlcipher_sqlite3CoverageCounter; +# endif +# define testcase(X) if( X ){ sqlcipher_sqlite3CoverageCounter += (unsigned)__LINE__; } #else # define testcase(X) #endif @@ -13823,6 +13696,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Coverage(int); # define VVA_ONLY(X) #endif +/* +** Disable ALWAYS() and NEVER() (make them pass-throughs) for coverage +** and mutation testing +*/ +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 +#endif + /* ** The ALWAYS and NEVER macros surround boolean expressions which ** are intended to always be true or false, respectively. Such @@ -13838,7 +13719,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Coverage(int); ** be true and false so that the unreachable code they specify will ** not be counted as untested code. */ -#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) @@ -13849,26 +13730,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Coverage(int); # define NEVER(X) (X) #endif -/* -** The harmless(X) macro indicates that expression X is usually false -** but can be true without causing any problems, but we don't know of -** any way to cause X to be true. -** -** In debugging and testing builds, this macro will abort if X is ever -** true. In this way, developers are alerted to a possible test case -** that causes X to be true. If a harmless macro ever fails, that is -** an opportunity to change the macro into a testcase() and add a new -** test case to the test suite. -** -** For normal production builds, harmless(X) is a no-op, since it does -** not matter whether expression X is true or false. -*/ -#ifdef SQLITE_DEBUG -# define harmless(X) assert(!(X)); -#else -# define harmless(X) -#endif - /* ** Some conditionals are optimizations only. In other words, if the ** conditionals are replaced with a constant 1 (true) or 0 (false) then @@ -13932,6 +13793,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Coverage(int); # undef SQLITE_ENABLE_EXPLAIN_COMMENTS #endif +/* +** SQLITE_OMIT_VIRTUALTABLE implies SQLITE_OMIT_ALTERTABLE +*/ +#if defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE) +# define SQLITE_OMIT_ALTERTABLE +#endif + /* ** Return true (non-zero) if the input is an integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() @@ -14044,7 +13912,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3HashClear(Hash*); /* ** Number of entries in a hash table */ -/* #define sqliteHashCount(H) ((H)->count) // NOT USED */ +#define sqliteHashCount(H) ((H)->count) #endif /* SQLITE_HASH_H */ @@ -14076,8 +13944,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3HashClear(Hash*); #define TK_LP 22 #define TK_RP 23 #define TK_AS 24 -#define TK_WITHOUT 25 -#define TK_COMMA 26 +#define TK_COMMA 25 +#define TK_WITHOUT 26 #define TK_ABORT 27 #define TK_ACTION 28 #define TK_AFTER 29 @@ -14148,90 +14016,94 @@ SQLITE_PRIVATE void sqlcipher_sqlite3HashClear(Hash*); #define TK_TIES 94 #define TK_GENERATED 95 #define TK_ALWAYS 96 -#define TK_REINDEX 97 -#define TK_RENAME 98 -#define TK_CTIME_KW 99 -#define TK_ANY 100 -#define TK_BITAND 101 -#define TK_BITOR 102 -#define TK_LSHIFT 103 -#define TK_RSHIFT 104 -#define TK_PLUS 105 -#define TK_MINUS 106 -#define TK_STAR 107 -#define TK_SLASH 108 -#define TK_REM 109 -#define TK_CONCAT 110 -#define TK_COLLATE 111 -#define TK_BITNOT 112 -#define TK_ON 113 -#define TK_INDEXED 114 -#define TK_STRING 115 -#define TK_JOIN_KW 116 -#define TK_CONSTRAINT 117 -#define TK_DEFAULT 118 -#define TK_NULL 119 -#define TK_PRIMARY 120 -#define TK_UNIQUE 121 -#define TK_CHECK 122 -#define TK_REFERENCES 123 -#define TK_AUTOINCR 124 -#define TK_INSERT 125 -#define TK_DELETE 126 -#define TK_UPDATE 127 -#define TK_SET 128 -#define TK_DEFERRABLE 129 -#define TK_FOREIGN 130 -#define TK_DROP 131 -#define TK_UNION 132 -#define TK_ALL 133 -#define TK_EXCEPT 134 -#define TK_INTERSECT 135 -#define TK_SELECT 136 -#define TK_VALUES 137 -#define TK_DISTINCT 138 -#define TK_DOT 139 -#define TK_FROM 140 -#define TK_JOIN 141 -#define TK_USING 142 -#define TK_ORDER 143 -#define TK_GROUP 144 -#define TK_HAVING 145 -#define TK_LIMIT 146 -#define TK_WHERE 147 -#define TK_INTO 148 -#define TK_NOTHING 149 -#define TK_FLOAT 150 -#define TK_BLOB 151 -#define TK_INTEGER 152 -#define TK_VARIABLE 153 -#define TK_CASE 154 -#define TK_WHEN 155 -#define TK_THEN 156 -#define TK_ELSE 157 -#define TK_INDEX 158 -#define TK_ALTER 159 -#define TK_ADD 160 -#define TK_WINDOW 161 -#define TK_OVER 162 -#define TK_FILTER 163 -#define TK_COLUMN 164 -#define TK_AGG_FUNCTION 165 -#define TK_AGG_COLUMN 166 -#define TK_TRUEFALSE 167 -#define TK_ISNOT 168 -#define TK_FUNCTION 169 -#define TK_UMINUS 170 -#define TK_UPLUS 171 -#define TK_TRUTH 172 -#define TK_REGISTER 173 -#define TK_VECTOR 174 -#define TK_SELECT_COLUMN 175 -#define TK_IF_NULL_ROW 176 -#define TK_ASTERISK 177 -#define TK_SPAN 178 -#define TK_SPACE 179 -#define TK_ILLEGAL 180 +#define TK_MATERIALIZED 97 +#define TK_REINDEX 98 +#define TK_RENAME 99 +#define TK_CTIME_KW 100 +#define TK_ANY 101 +#define TK_BITAND 102 +#define TK_BITOR 103 +#define TK_LSHIFT 104 +#define TK_RSHIFT 105 +#define TK_PLUS 106 +#define TK_MINUS 107 +#define TK_STAR 108 +#define TK_SLASH 109 +#define TK_REM 110 +#define TK_CONCAT 111 +#define TK_PTR 112 +#define TK_COLLATE 113 +#define TK_BITNOT 114 +#define TK_ON 115 +#define TK_INDEXED 116 +#define TK_STRING 117 +#define TK_JOIN_KW 118 +#define TK_CONSTRAINT 119 +#define TK_DEFAULT 120 +#define TK_NULL 121 +#define TK_PRIMARY 122 +#define TK_UNIQUE 123 +#define TK_CHECK 124 +#define TK_REFERENCES 125 +#define TK_AUTOINCR 126 +#define TK_INSERT 127 +#define TK_DELETE 128 +#define TK_UPDATE 129 +#define TK_SET 130 +#define TK_DEFERRABLE 131 +#define TK_FOREIGN 132 +#define TK_DROP 133 +#define TK_UNION 134 +#define TK_ALL 135 +#define TK_EXCEPT 136 +#define TK_INTERSECT 137 +#define TK_SELECT 138 +#define TK_VALUES 139 +#define TK_DISTINCT 140 +#define TK_DOT 141 +#define TK_FROM 142 +#define TK_JOIN 143 +#define TK_USING 144 +#define TK_ORDER 145 +#define TK_GROUP 146 +#define TK_HAVING 147 +#define TK_LIMIT 148 +#define TK_WHERE 149 +#define TK_RETURNING 150 +#define TK_INTO 151 +#define TK_NOTHING 152 +#define TK_FLOAT 153 +#define TK_BLOB 154 +#define TK_INTEGER 155 +#define TK_VARIABLE 156 +#define TK_CASE 157 +#define TK_WHEN 158 +#define TK_THEN 159 +#define TK_ELSE 160 +#define TK_INDEX 161 +#define TK_ALTER 162 +#define TK_ADD 163 +#define TK_WINDOW 164 +#define TK_OVER 165 +#define TK_FILTER 166 +#define TK_COLUMN 167 +#define TK_AGG_FUNCTION 168 +#define TK_AGG_COLUMN 169 +#define TK_TRUEFALSE 170 +#define TK_ISNOT 171 +#define TK_FUNCTION 172 +#define TK_UMINUS 173 +#define TK_UPLUS 174 +#define TK_TRUTH 175 +#define TK_REGISTER 176 +#define TK_VECTOR 177 +#define TK_SELECT_COLUMN 178 +#define TK_IF_NULL_ROW 179 +#define TK_ASTERISK 180 +#define TK_SPAN 181 +#define TK_ERROR 182 +#define TK_SPACE 183 +#define TK_ILLEGAL 184 /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -14337,7 +14209,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3HashClear(Hash*); ** number of pages. A negative number N translations means that a buffer ** of -1024*N bytes is allocated and used for as many pages as it will hold. ** -** The default value of "20" was choosen to minimize the run-time of the +** The default value of "20" was chosen to minimize the run-time of the ** speedtest1 test program with options: --shrink-memory --reprepare */ #ifndef SQLITE_DEFAULT_PCACHE_INITSZ @@ -14499,6 +14371,7 @@ typedef INT16_TYPE LogEst; # define SQLITE_PTRSIZE __SIZEOF_POINTER__ # elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(_M_ARM) || defined(__arm__) || defined(__x86) || \ + (defined(__APPLE__) && defined(__POWERPC__)) || \ (defined(__TOS_AIX__) && !defined(__64BIT__)) # define SQLITE_PTRSIZE 4 # else @@ -14580,8 +14453,19 @@ typedef INT16_TYPE LogEst; /* ** Round up a number to the next larger multiple of 8. This is used ** to force 8-byte alignment on 64-bit architectures. +** +** ROUND8() always does the rounding, for any argument. +** +** ROUND8P() assumes that the argument is already an integer number of +** pointers in size, and so it is a no-op on systems where the pointer +** size is 8. */ #define ROUND8(x) (((x)+7)&~7) +#if SQLITE_PTRSIZE==8 +# define ROUND8P(x) (x) +#else +# define ROUND8P(x) (((x)+7)&~7) +#endif /* ** Round down to the nearest multiple of 8 @@ -14644,25 +14528,38 @@ typedef INT16_TYPE LogEst; #endif /* -** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not -** the Select query generator tracing logic is turned on. +** TREETRACE_ENABLED will be either 1 or 0 depending on whether or not +** the Abstract Syntax Tree tracing logic is turned on. */ -#if defined(SQLITE_ENABLE_SELECTTRACE) -# define SELECTTRACE_ENABLED 1 -#else -# define SELECTTRACE_ENABLED 0 +#if !defined(SQLITE_AMALGAMATION) +SQLITE_PRIVATE u32 sqlcipher_sqlite3TreeTrace; #endif -#if defined(SQLITE_ENABLE_SELECTTRACE) -# define SELECTTRACE_ENABLED 1 +#if defined(SQLITE_DEBUG) \ + && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE) \ + || defined(SQLITE_ENABLE_TREETRACE)) +# define TREETRACE_ENABLED 1 # define SELECTTRACE(K,P,S,X) \ - if(sqlcipher_sqlite3_unsupported_selecttrace&(K)) \ + if(sqlcipher_sqlite3TreeTrace&(K)) \ sqlcipher_sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ sqlcipher_sqlite3DebugPrintf X #else # define SELECTTRACE(K,P,S,X) -# define SELECTTRACE_ENABLED 0 +# define TREETRACE_ENABLED 0 #endif +/* +** Macros for "wheretrace" +*/ +SQLITE_PRIVATE u32 sqlcipher_sqlite3WhereTrace; +#if defined(SQLITE_DEBUG) \ + && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE)) +# define WHERETRACE(K,X) if(sqlcipher_sqlite3WhereTrace&(K)) sqlcipher_sqlite3DebugPrintf X +# define WHERETRACE_ENABLED 1 +#else +# define WHERETRACE(K,X) +#endif + + /* ** An instance of the following structure is used to store the busy-handler ** callback for a given sqlite handle. @@ -14681,11 +14578,25 @@ struct BusyHandler { /* ** Name of table that holds the database schema. +** +** The PREFERRED names are used whereever possible. But LEGACY is also +** used for backwards compatibility. +** +** 1. Queries can use either the PREFERRED or the LEGACY names +** 2. The sqlcipher_sqlite3_set_authorizer() callback uses the LEGACY name +** 3. The PRAGMA table_list statement uses the PREFERRED name +** +** The LEGACY names are stored in the internal symbol hash table +** in support of (2). Names are translated using sqlcipher_sqlite3PreferredTableName() +** for (3). The sqlcipher_sqlite3FindTable() function takes care of translating +** names for (1). +** +** Note that "sqlite_temp_schema" can also be called "temp.sqlite_schema". */ -#define DFLT_SCHEMA_TABLE "sqlite_master" -#define DFLT_TEMP_SCHEMA_TABLE "sqlite_temp_master" -#define ALT_SCHEMA_TABLE "sqlite_schema" -#define ALT_TEMP_SCHEMA_TABLE "sqlite_temp_schema" +#define LEGACY_SCHEMA_TABLE "sqlite_master" +#define LEGACY_TEMP_SCHEMA_TABLE "sqlite_temp_master" +#define PREFERRED_SCHEMA_TABLE "sqlite_schema" +#define PREFERRED_TEMP_SCHEMA_TABLE "sqlite_temp_schema" /* @@ -14697,7 +14608,7 @@ struct BusyHandler { ** The name of the schema table. The name is different for TEMP. */ #define SCHEMA_TABLE(x) \ - ((!OMIT_TEMPDB)&&(x==1)?DFLT_TEMP_SCHEMA_TABLE:DFLT_SCHEMA_TABLE) + ((!OMIT_TEMPDB)&&(x==1)?LEGACY_TEMP_SCHEMA_TABLE:LEGACY_SCHEMA_TABLE) /* ** A convenience macro that returns the number of elements in @@ -14718,7 +14629,7 @@ struct BusyHandler { ** pointer will work here as long as it is distinct from SQLITE_STATIC ** and SQLITE_TRANSIENT. */ -#define SQLITE_DYNAMIC ((sqlcipher_sqlite3_destructor_type)sqlcipher_sqlite3OomFault) +#define SQLITE_DYNAMIC ((sqlcipher_sqlite3_destructor_type)sqlcipher_sqlite3OomClear) /* ** When SQLITE_OMIT_WSD is defined, it means that the target platform does @@ -14774,7 +14685,10 @@ typedef struct AutoincInfo AutoincInfo; typedef struct Bitvec Bitvec; typedef struct CollSeq CollSeq; typedef struct Column Column; +typedef struct Cte Cte; +typedef struct CteUse CteUse; typedef struct Db Db; +typedef struct DbFixer DbFixer; typedef struct Schema Schema; typedef struct Expr Expr; typedef struct ExprList ExprList; @@ -14791,15 +14705,19 @@ typedef struct Lookaside Lookaside; typedef struct LookasideSlot LookasideSlot; typedef struct Module Module; typedef struct NameContext NameContext; +typedef struct OnOrUsing OnOrUsing; typedef struct Parse Parse; +typedef struct ParseCleanup ParseCleanup; typedef struct PreUpdate PreUpdate; typedef struct PrintfArguments PrintfArguments; typedef struct RenameToken RenameToken; +typedef struct Returning Returning; typedef struct RowSet RowSet; typedef struct Savepoint Savepoint; typedef struct Select Select; typedef struct SQLiteThread SQLiteThread; typedef struct SelectDest SelectDest; +typedef struct SrcItem SrcItem; typedef struct SrcList SrcList; typedef struct sqlcipher_sqlite3_str StrAccum; /* Internal alias for sqlcipher_sqlite3_str */ typedef struct Table Table; @@ -14840,10 +14758,11 @@ typedef struct With With; /* ** A bit in a Bitmask */ -#define MASKBIT(n) (((Bitmask)1)<<(n)) -#define MASKBIT64(n) (((u64)1)<<(n)) -#define MASKBIT32(n) (((unsigned int)1)<<(n)) -#define ALLBITS ((Bitmask)-1) +#define MASKBIT(n) (((Bitmask)1)<<(n)) +#define MASKBIT64(n) (((u64)1)<<(n)) +#define MASKBIT32(n) (((unsigned int)1)<<(n)) +#define SMASKBIT32(n) ((n)<=31?((unsigned int)1)<<(n):0) +#define ALLBITS ((Bitmask)-1) /* A VList object records a mapping between parameters/variables/wildcards ** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer @@ -14905,14 +14824,15 @@ typedef struct Pager Pager; typedef struct PgHdr DbPage; /* -** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is +** Page number PAGER_SJ_PGNO is never used in an SQLite database (it is ** reserved for working around a windows/posix incompatibility). It is ** used in the journal to signify that the remainder of the journal file ** is devoted to storing a super-journal name - there are no more pages to ** roll back. See comments for function writeSuperJournal() in pager.c ** for details. */ -#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) +#define PAGER_SJ_PGNO_COMPUTED(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) +#define PAGER_SJ_PGNO(x) ((x)->lckPgno) /* ** Allowed values for the flags parameter to sqlcipher_sqlite3PagerOpen(). @@ -15093,7 +15013,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3PagerRekey(DbPage*, Pgno, u16); /* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) -SQLITE_PRIVATE void *sqlcipher_sqlite3PagerCodec(DbPage *); +void *sqlcipherPagerCodec(DbPage *); #endif /* END SQLCIPHER */ @@ -15243,7 +15163,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3BtreeIncrVacuum(Btree *); #define BTREE_BLOBKEY 2 /* Table has keys only - no data */ SQLITE_PRIVATE int sqlcipher_sqlite3BtreeDropTable(Btree*, int, int*); -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeClearTable(Btree*, int, int*); +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeClearTable(Btree*, int, i64*); SQLITE_PRIVATE int sqlcipher_sqlite3BtreeClearTableOfCursor(BtCursor*); SQLITE_PRIVATE int sqlcipher_sqlite3BtreeTripAllCursors(Btree*, int, int); @@ -15367,13 +15287,17 @@ SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorHint(BtCursor*, int, ...); #endif SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCloseCursor(BtCursor*); -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeMovetoUnpacked( +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeTableMoveto( BtCursor*, - UnpackedRecord *pUnKey, i64 intKey, int bias, int *pRes ); +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeIndexMoveto( + BtCursor*, + UnpackedRecord *pUnKey, + int *pRes +); SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorHasMoved(BtCursor*); SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorRestore(BtCursor*, int*); SQLITE_PRIVATE int sqlcipher_sqlite3BtreeDelete(BtCursor*, u8 flags); @@ -15382,6 +15306,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3BtreeDelete(BtCursor*, u8 flags); #define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */ #define BTREE_AUXDELETE 0x04 /* not the primary delete operation */ #define BTREE_APPEND 0x08 /* Insert is likely an append */ +#define BTREE_PREFORMAT 0x80 /* Inserted data is a preformated cell */ /* An instance of the BtreePayload object describes the content of a single ** entry in either an index or table btree. @@ -15481,6 +15406,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorList(Btree*); SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCheckpoint(Btree*, int, int *, int *); #endif +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeTransferRow(BtCursor*, BtCursor*, i64); + /* ** If we are not using shared cache, then there is no need to ** use mutexes to access the BtShared structures. So make the @@ -15593,7 +15520,6 @@ struct VdbeOp { #ifdef SQLITE_ENABLE_CURSOR_HINTS Expr *pExpr; /* Used when p4type is P4_EXPR */ #endif - int (*xAdvance)(BtCursor *, int); } p4; #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS char *zComment; /* Comment to improve readability */ @@ -15644,21 +15570,19 @@ typedef struct VdbeOpList VdbeOpList; #define P4_COLLSEQ (-2) /* P4 is a pointer to a CollSeq structure */ #define P4_INT32 (-3) /* P4 is a 32-bit signed integer */ #define P4_SUBPROGRAM (-4) /* P4 is a pointer to a SubProgram structure */ -#define P4_ADVANCE (-5) /* P4 is a pointer to BtreeNext() or BtreePrev() */ -#define P4_TABLE (-6) /* P4 is a pointer to a Table structure */ +#define P4_TABLE (-5) /* P4 is a pointer to a Table structure */ /* Above do not own any resources. Must free those below */ -#define P4_FREE_IF_LE (-7) -#define P4_DYNAMIC (-7) /* Pointer to memory from sqliteMalloc() */ -#define P4_FUNCDEF (-8) /* P4 is a pointer to a FuncDef structure */ -#define P4_KEYINFO (-9) /* P4 is a pointer to a KeyInfo structure */ -#define P4_EXPR (-10) /* P4 is a pointer to an Expr tree */ -#define P4_MEM (-11) /* P4 is a pointer to a Mem* structure */ -#define P4_VTAB (-12) /* P4 is a pointer to an sqlcipher_sqlite3_vtab structure */ -#define P4_REAL (-13) /* P4 is a 64-bit floating point value */ -#define P4_INT64 (-14) /* P4 is a 64-bit signed integer */ -#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ -#define P4_FUNCCTX (-16) /* P4 is a pointer to an sqlcipher_sqlite3_context object */ -#define P4_DYNBLOB (-17) /* Pointer to memory from sqliteMalloc() */ +#define P4_FREE_IF_LE (-6) +#define P4_DYNAMIC (-6) /* Pointer to memory from sqliteMalloc() */ +#define P4_FUNCDEF (-7) /* P4 is a pointer to a FuncDef structure */ +#define P4_KEYINFO (-8) /* P4 is a pointer to a KeyInfo structure */ +#define P4_EXPR (-9) /* P4 is a pointer to an Expr tree */ +#define P4_MEM (-10) /* P4 is a pointer to a Mem* structure */ +#define P4_VTAB (-11) /* P4 is a pointer to an sqlcipher_sqlite3_vtab structure */ +#define P4_REAL (-12) /* P4 is a 64-bit floating point value */ +#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ +#define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ +#define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlcipher_sqlite3_context object */ /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 @@ -15703,53 +15627,53 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Savepoint 0 #define OP_AutoCommit 1 #define OP_Transaction 2 -#define OP_SorterNext 3 /* jump */ -#define OP_Prev 4 /* jump */ -#define OP_Next 5 /* jump */ -#define OP_Checkpoint 6 -#define OP_JournalMode 7 -#define OP_Vacuum 8 -#define OP_VFilter 9 /* jump, synopsis: iplan=r[P3] zplan='P4' */ -#define OP_VUpdate 10 /* synopsis: data=r[P3@P2] */ -#define OP_Goto 11 /* jump */ -#define OP_Gosub 12 /* jump */ -#define OP_InitCoroutine 13 /* jump */ -#define OP_Yield 14 /* jump */ -#define OP_MustBeInt 15 /* jump */ -#define OP_Jump 16 /* jump */ -#define OP_Once 17 /* jump */ -#define OP_If 18 /* jump */ +#define OP_Checkpoint 3 +#define OP_JournalMode 4 +#define OP_Vacuum 5 +#define OP_VFilter 6 /* jump, synopsis: iplan=r[P3] zplan='P4' */ +#define OP_VUpdate 7 /* synopsis: data=r[P3@P2] */ +#define OP_Goto 8 /* jump */ +#define OP_Gosub 9 /* jump */ +#define OP_InitCoroutine 10 /* jump */ +#define OP_Yield 11 /* jump */ +#define OP_MustBeInt 12 /* jump */ +#define OP_Jump 13 /* jump */ +#define OP_Once 14 /* jump */ +#define OP_If 15 /* jump */ +#define OP_IfNot 16 /* jump */ +#define OP_IsNullOrType 17 /* jump, synopsis: if typeof(r[P1]) IN (P3,5) goto P2 */ +#define OP_IfNullRow 18 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */ #define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */ -#define OP_IfNot 20 /* jump */ -#define OP_IfNullRow 21 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */ -#define OP_SeekLT 22 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekLE 23 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekGE 24 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekGT 25 /* jump, synopsis: key=r[P3@P4] */ -#define OP_IfNotOpen 26 /* jump, synopsis: if( !csr[P1] ) goto P2 */ -#define OP_IfNoHope 27 /* jump, synopsis: key=r[P3@P4] */ -#define OP_NoConflict 28 /* jump, synopsis: key=r[P3@P4] */ -#define OP_NotFound 29 /* jump, synopsis: key=r[P3@P4] */ -#define OP_Found 30 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekRowid 31 /* jump, synopsis: intkey=r[P3] */ -#define OP_NotExists 32 /* jump, synopsis: intkey=r[P3] */ -#define OP_Last 33 /* jump */ -#define OP_IfSmaller 34 /* jump */ -#define OP_SorterSort 35 /* jump */ -#define OP_Sort 36 /* jump */ -#define OP_Rewind 37 /* jump */ -#define OP_IdxLE 38 /* jump, synopsis: key=r[P3@P4] */ -#define OP_IdxGT 39 /* jump, synopsis: key=r[P3@P4] */ -#define OP_IdxLT 40 /* jump, synopsis: key=r[P3@P4] */ -#define OP_IdxGE 41 /* jump, synopsis: key=r[P3@P4] */ -#define OP_RowSetRead 42 /* jump, synopsis: r[P3]=rowset(P1) */ +#define OP_SeekLT 20 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekLE 21 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekGE 22 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekGT 23 /* jump, synopsis: key=r[P3@P4] */ +#define OP_IfNotOpen 24 /* jump, synopsis: if( !csr[P1] ) goto P2 */ +#define OP_IfNoHope 25 /* jump, synopsis: key=r[P3@P4] */ +#define OP_NoConflict 26 /* jump, synopsis: key=r[P3@P4] */ +#define OP_NotFound 27 /* jump, synopsis: key=r[P3@P4] */ +#define OP_Found 28 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekRowid 29 /* jump, synopsis: intkey=r[P3] */ +#define OP_NotExists 30 /* jump, synopsis: intkey=r[P3] */ +#define OP_Last 31 /* jump */ +#define OP_IfSmaller 32 /* jump */ +#define OP_SorterSort 33 /* jump */ +#define OP_Sort 34 /* jump */ +#define OP_Rewind 35 /* jump */ +#define OP_SorterNext 36 /* jump */ +#define OP_Prev 37 /* jump */ +#define OP_Next 38 /* jump */ +#define OP_IdxLE 39 /* jump, synopsis: key=r[P3@P4] */ +#define OP_IdxGT 40 /* jump, synopsis: key=r[P3@P4] */ +#define OP_IdxLT 41 /* jump, synopsis: key=r[P3@P4] */ +#define OP_IdxGE 42 /* jump, synopsis: key=r[P3@P4] */ #define OP_Or 43 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */ #define OP_And 44 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */ -#define OP_RowSetTest 45 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ -#define OP_Program 46 /* jump */ -#define OP_FkIfZero 47 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ -#define OP_IfPos 48 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ -#define OP_IfNotZero 49 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ +#define OP_RowSetRead 45 /* jump, synopsis: r[P3]=rowset(P1) */ +#define OP_RowSetTest 46 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ +#define OP_Program 47 /* jump */ +#define OP_FkIfZero 48 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ +#define OP_IfPos 49 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ #define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ #define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ #define OP_Ne 52 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */ @@ -15758,125 +15682,135 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Le 55 /* jump, same as TK_LE, synopsis: IF r[P3]<=r[P1] */ #define OP_Lt 56 /* jump, same as TK_LT, synopsis: IF r[P3]=r[P1] */ -#define OP_ElseNotEq 58 /* jump, same as TK_ESCAPE */ -#define OP_DecrJumpZero 59 /* jump, synopsis: if (--r[P1])==0 goto P2 */ -#define OP_IncrVacuum 60 /* jump */ -#define OP_VNext 61 /* jump */ -#define OP_Init 62 /* jump, synopsis: Start at P2 */ -#define OP_PureFunc 63 /* synopsis: r[P3]=func(r[P2@NP]) */ -#define OP_Function 64 /* synopsis: r[P3]=func(r[P2@NP]) */ -#define OP_Return 65 -#define OP_EndCoroutine 66 -#define OP_HaltIfNull 67 /* synopsis: if r[P3]=null halt */ -#define OP_Halt 68 -#define OP_Integer 69 /* synopsis: r[P2]=P1 */ -#define OP_Int64 70 /* synopsis: r[P2]=P4 */ -#define OP_String 71 /* synopsis: r[P2]='P4' (len=P1) */ -#define OP_Null 72 /* synopsis: r[P2..P3]=NULL */ -#define OP_SoftNull 73 /* synopsis: r[P1]=NULL */ -#define OP_Blob 74 /* synopsis: r[P2]=P4 (len=P1) */ -#define OP_Variable 75 /* synopsis: r[P2]=parameter(P1,P4) */ -#define OP_Move 76 /* synopsis: r[P2@P3]=r[P1@P3] */ -#define OP_Copy 77 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ -#define OP_SCopy 78 /* synopsis: r[P2]=r[P1] */ -#define OP_IntCopy 79 /* synopsis: r[P2]=r[P1] */ -#define OP_ResultRow 80 /* synopsis: output=r[P1@P2] */ -#define OP_CollSeq 81 -#define OP_AddImm 82 /* synopsis: r[P1]=r[P1]+P2 */ -#define OP_RealAffinity 83 -#define OP_Cast 84 /* synopsis: affinity(r[P1]) */ -#define OP_Permutation 85 -#define OP_Compare 86 /* synopsis: r[P1@P3] <-> r[P2@P3] */ -#define OP_IsTrue 87 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ -#define OP_Offset 88 /* synopsis: r[P3] = sqlite_offset(P1) */ -#define OP_Column 89 /* synopsis: r[P3]=PX */ -#define OP_Affinity 90 /* synopsis: affinity(r[P1@P2]) */ -#define OP_MakeRecord 91 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ -#define OP_Count 92 /* synopsis: r[P2]=count() */ -#define OP_ReadCookie 93 -#define OP_SetCookie 94 -#define OP_ReopenIdx 95 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenRead 96 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenWrite 97 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenDup 98 -#define OP_OpenAutoindex 99 /* synopsis: nColumn=P2 */ -#define OP_OpenEphemeral 100 /* synopsis: nColumn=P2 */ -#define OP_BitAnd 101 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ -#define OP_BitOr 102 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ -#define OP_ShiftLeft 103 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ -#define OP_Add 105 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ -#define OP_Subtract 106 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ -#define OP_Multiply 107 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ -#define OP_Divide 108 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ -#define OP_Remainder 109 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ -#define OP_Concat 110 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ -#define OP_SorterOpen 111 -#define OP_BitNot 112 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ -#define OP_SequenceTest 113 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */ -#define OP_OpenPseudo 114 /* synopsis: P3 columns in r[P2] */ -#define OP_String8 115 /* same as TK_STRING, synopsis: r[P2]='P4' */ -#define OP_Close 116 -#define OP_ColumnsUsed 117 -#define OP_SeekScan 118 /* synopsis: Scan-ahead up to P1 rows */ -#define OP_SeekHit 119 /* synopsis: set P2<=seekHit<=P3 */ -#define OP_Sequence 120 /* synopsis: r[P2]=cursor[P1].ctr++ */ -#define OP_NewRowid 121 /* synopsis: r[P2]=rowid */ -#define OP_Insert 122 /* synopsis: intkey=r[P3] data=r[P2] */ -#define OP_Delete 123 -#define OP_ResetCount 124 -#define OP_SorterCompare 125 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ -#define OP_SorterData 126 /* synopsis: r[P2]=data */ -#define OP_RowData 127 /* synopsis: r[P2]=data */ -#define OP_Rowid 128 /* synopsis: r[P2]=rowid */ -#define OP_NullRow 129 -#define OP_SeekEnd 130 -#define OP_IdxInsert 131 /* synopsis: key=r[P2] */ -#define OP_SorterInsert 132 /* synopsis: key=r[P2] */ -#define OP_IdxDelete 133 /* synopsis: key=r[P2@P3] */ -#define OP_DeferredSeek 134 /* synopsis: Move P3 to P1.rowid if needed */ -#define OP_IdxRowid 135 /* synopsis: r[P2]=rowid */ -#define OP_FinishSeek 136 -#define OP_Destroy 137 -#define OP_Clear 138 -#define OP_ResetSorter 139 -#define OP_CreateBtree 140 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ -#define OP_SqlExec 141 -#define OP_ParseSchema 142 -#define OP_LoadAnalysis 143 -#define OP_DropTable 144 -#define OP_DropIndex 145 -#define OP_DropTrigger 146 -#define OP_IntegrityCk 147 -#define OP_RowSetAdd 148 /* synopsis: rowset(P1)=r[P2] */ -#define OP_Param 149 -#define OP_Real 150 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ -#define OP_FkCounter 151 /* synopsis: fkctr[P1]+=P2 */ -#define OP_MemMax 152 /* synopsis: r[P1]=max(r[P1],r[P2]) */ -#define OP_OffsetLimit 153 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ -#define OP_AggInverse 154 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ -#define OP_AggStep 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggStep1 156 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggValue 157 /* synopsis: r[P3]=value N=P2 */ -#define OP_AggFinal 158 /* synopsis: accum=r[P1] N=P2 */ -#define OP_Expire 159 -#define OP_CursorLock 160 -#define OP_CursorUnlock 161 -#define OP_TableLock 162 /* synopsis: iDb=P1 root=P2 write=P3 */ -#define OP_VBegin 163 -#define OP_VCreate 164 -#define OP_VDestroy 165 -#define OP_VOpen 166 -#define OP_VColumn 167 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VRename 168 -#define OP_Pagecount 169 -#define OP_MaxPgcnt 170 -#define OP_Trace 171 -#define OP_CursorHint 172 -#define OP_ReleaseReg 173 /* synopsis: release r[P1@P2] mask P3 */ -#define OP_Noop 174 -#define OP_Explain 175 -#define OP_Abortable 176 +#define OP_ElseEq 58 /* jump, same as TK_ESCAPE */ +#define OP_IfNotZero 59 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ +#define OP_DecrJumpZero 60 /* jump, synopsis: if (--r[P1])==0 goto P2 */ +#define OP_IncrVacuum 61 /* jump */ +#define OP_VNext 62 /* jump */ +#define OP_Filter 63 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */ +#define OP_Init 64 /* jump, synopsis: Start at P2 */ +#define OP_PureFunc 65 /* synopsis: r[P3]=func(r[P2@NP]) */ +#define OP_Function 66 /* synopsis: r[P3]=func(r[P2@NP]) */ +#define OP_Return 67 +#define OP_EndCoroutine 68 +#define OP_HaltIfNull 69 /* synopsis: if r[P3]=null halt */ +#define OP_Halt 70 +#define OP_Integer 71 /* synopsis: r[P2]=P1 */ +#define OP_Int64 72 /* synopsis: r[P2]=P4 */ +#define OP_String 73 /* synopsis: r[P2]='P4' (len=P1) */ +#define OP_BeginSubrtn 74 /* synopsis: r[P2]=NULL */ +#define OP_Null 75 /* synopsis: r[P2..P3]=NULL */ +#define OP_SoftNull 76 /* synopsis: r[P1]=NULL */ +#define OP_Blob 77 /* synopsis: r[P2]=P4 (len=P1) */ +#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1,P4) */ +#define OP_Move 79 /* synopsis: r[P2@P3]=r[P1@P3] */ +#define OP_Copy 80 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ +#define OP_SCopy 81 /* synopsis: r[P2]=r[P1] */ +#define OP_IntCopy 82 /* synopsis: r[P2]=r[P1] */ +#define OP_FkCheck 83 +#define OP_ResultRow 84 /* synopsis: output=r[P1@P2] */ +#define OP_CollSeq 85 +#define OP_AddImm 86 /* synopsis: r[P1]=r[P1]+P2 */ +#define OP_RealAffinity 87 +#define OP_Cast 88 /* synopsis: affinity(r[P1]) */ +#define OP_Permutation 89 +#define OP_Compare 90 /* synopsis: r[P1@P3] <-> r[P2@P3] */ +#define OP_IsTrue 91 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ +#define OP_ZeroOrNull 92 /* synopsis: r[P2] = 0 OR NULL */ +#define OP_Offset 93 /* synopsis: r[P3] = sqlite_offset(P1) */ +#define OP_Column 94 /* synopsis: r[P3]=PX cursor P1 column P2 */ +#define OP_TypeCheck 95 /* synopsis: typecheck(r[P1@P2]) */ +#define OP_Affinity 96 /* synopsis: affinity(r[P1@P2]) */ +#define OP_MakeRecord 97 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ +#define OP_Count 98 /* synopsis: r[P2]=count() */ +#define OP_ReadCookie 99 +#define OP_SetCookie 100 +#define OP_ReopenIdx 101 /* synopsis: root=P2 iDb=P3 */ +#define OP_BitAnd 102 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ +#define OP_BitOr 103 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ +#define OP_ShiftLeft 104 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ +#define OP_Add 106 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ +#define OP_Subtract 107 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ +#define OP_Multiply 108 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ +#define OP_Divide 109 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ +#define OP_Remainder 110 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ +#define OP_Concat 111 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ +#define OP_OpenRead 112 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenWrite 113 /* synopsis: root=P2 iDb=P3 */ +#define OP_BitNot 114 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ +#define OP_OpenDup 115 +#define OP_OpenAutoindex 116 /* synopsis: nColumn=P2 */ +#define OP_String8 117 /* same as TK_STRING, synopsis: r[P2]='P4' */ +#define OP_OpenEphemeral 118 /* synopsis: nColumn=P2 */ +#define OP_SorterOpen 119 +#define OP_SequenceTest 120 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */ +#define OP_OpenPseudo 121 /* synopsis: P3 columns in r[P2] */ +#define OP_Close 122 +#define OP_ColumnsUsed 123 +#define OP_SeekScan 124 /* synopsis: Scan-ahead up to P1 rows */ +#define OP_SeekHit 125 /* synopsis: set P2<=seekHit<=P3 */ +#define OP_Sequence 126 /* synopsis: r[P2]=cursor[P1].ctr++ */ +#define OP_NewRowid 127 /* synopsis: r[P2]=rowid */ +#define OP_Insert 128 /* synopsis: intkey=r[P3] data=r[P2] */ +#define OP_RowCell 129 +#define OP_Delete 130 +#define OP_ResetCount 131 +#define OP_SorterCompare 132 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ +#define OP_SorterData 133 /* synopsis: r[P2]=data */ +#define OP_RowData 134 /* synopsis: r[P2]=data */ +#define OP_Rowid 135 /* synopsis: r[P2]=PX rowid of P1 */ +#define OP_NullRow 136 +#define OP_SeekEnd 137 +#define OP_IdxInsert 138 /* synopsis: key=r[P2] */ +#define OP_SorterInsert 139 /* synopsis: key=r[P2] */ +#define OP_IdxDelete 140 /* synopsis: key=r[P2@P3] */ +#define OP_DeferredSeek 141 /* synopsis: Move P3 to P1.rowid if needed */ +#define OP_IdxRowid 142 /* synopsis: r[P2]=rowid */ +#define OP_FinishSeek 143 +#define OP_Destroy 144 +#define OP_Clear 145 +#define OP_ResetSorter 146 +#define OP_CreateBtree 147 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ +#define OP_SqlExec 148 +#define OP_ParseSchema 149 +#define OP_LoadAnalysis 150 +#define OP_DropTable 151 +#define OP_DropIndex 152 +#define OP_Real 153 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ +#define OP_DropTrigger 154 +#define OP_IntegrityCk 155 +#define OP_RowSetAdd 156 /* synopsis: rowset(P1)=r[P2] */ +#define OP_Param 157 +#define OP_FkCounter 158 /* synopsis: fkctr[P1]+=P2 */ +#define OP_MemMax 159 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +#define OP_OffsetLimit 160 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ +#define OP_AggInverse 161 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ +#define OP_AggStep 162 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggStep1 163 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggValue 164 /* synopsis: r[P3]=value N=P2 */ +#define OP_AggFinal 165 /* synopsis: accum=r[P1] N=P2 */ +#define OP_Expire 166 +#define OP_CursorLock 167 +#define OP_CursorUnlock 168 +#define OP_TableLock 169 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 170 +#define OP_VCreate 171 +#define OP_VDestroy 172 +#define OP_VOpen 173 +#define OP_VInitIn 174 /* synopsis: r[P2]=ValueList(P1,P3) */ +#define OP_VColumn 175 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VRename 176 +#define OP_Pagecount 177 +#define OP_MaxPgcnt 178 +#define OP_ClrSubtype 179 /* synopsis: r[P1].subtype = 0 */ +#define OP_FilterAdd 180 /* synopsis: filter(P1) += key(P3@P4) */ +#define OP_Trace 181 +#define OP_CursorHint 182 +#define OP_ReleaseReg 183 /* synopsis: release r[P1@P2] mask P3 */ +#define OP_Noop 184 +#define OP_Explain 185 +#define OP_Abortable 186 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c @@ -15889,37 +15823,38 @@ typedef struct VdbeOpList VdbeOpList; #define OPFLG_OUT2 0x10 /* out2: P2 is an output */ #define OPFLG_OUT3 0x20 /* out3: P3 is an output */ #define OPFLG_INITIALIZER {\ -/* 0 */ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x10,\ -/* 8 */ 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03,\ -/* 16 */ 0x01, 0x01, 0x03, 0x12, 0x03, 0x01, 0x09, 0x09,\ -/* 24 */ 0x09, 0x09, 0x01, 0x09, 0x09, 0x09, 0x09, 0x09,\ -/* 32 */ 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\ -/* 40 */ 0x01, 0x01, 0x23, 0x26, 0x26, 0x0b, 0x01, 0x01,\ -/* 48 */ 0x03, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ -/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00,\ -/* 64 */ 0x00, 0x02, 0x02, 0x08, 0x00, 0x10, 0x10, 0x10,\ -/* 72 */ 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,\ -/* 80 */ 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x12,\ -/* 88 */ 0x20, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\ -/* 96 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x26, 0x26,\ -/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00,\ -/* 112 */ 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ -/* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 128 */ 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10,\ -/* 136 */ 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\ -/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x10, 0x00,\ -/* 152 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 168 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 176 */ 0x00,} - -/* The sqlcipher_sqlite3P2Values() routine is able to run faster if it knows +/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,\ +/* 8 */ 0x01, 0x01, 0x01, 0x03, 0x03, 0x01, 0x01, 0x03,\ +/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x09, 0x09, 0x09, 0x09,\ +/* 24 */ 0x01, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x01,\ +/* 32 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\ +/* 40 */ 0x01, 0x01, 0x01, 0x26, 0x26, 0x23, 0x0b, 0x01,\ +/* 48 */ 0x01, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ +/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x01, 0x01, 0x01,\ +/* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\ +/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10, 0x00,\ +/* 80 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02,\ +/* 88 */ 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x00, 0x00,\ +/* 96 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x26, 0x26,\ +/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\ +/* 112 */ 0x00, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00,\ +/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\ +/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\ +/* 136 */ 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10, 0x00,\ +/* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ +/* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ +/* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,\ +/* 176 */ 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,\ +/* 184 */ 0x00, 0x00, 0x00,} + +/* The resolve3P2Values() routine is able to run faster if it knows ** the value of the largest JUMP opcode. The smaller the maximum ** JUMP opcode the better, so the mkopcodeh.tcl script that ** generated this include file strives to group all JUMP opcodes ** together near the beginning of the list. */ -#define SQLITE_MX_JUMP_OPCODE 62 /* Maximum JUMP opcode */ +#define SQLITE_MX_JUMP_OPCODE 64 /* Maximum JUMP opcode */ /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ @@ -15957,8 +15892,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VdbeVerifyNoResultRow(Vdbe *p); #endif #if defined(SQLITE_DEBUG) SQLITE_PRIVATE void sqlcipher_sqlite3VdbeVerifyAbortable(Vdbe *p, int); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeNoJumpsOutsideSubrtn(Vdbe*,int,int,int); #else # define sqlcipher_sqlite3VdbeVerifyAbortable(A,B) +# define sqlcipher_sqlite3VdbeNoJumpsOutsideSubrtn(A,B,C,D) #endif SQLITE_PRIVATE VdbeOp *sqlcipher_sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno); #ifndef SQLITE_OMIT_EXPLAIN @@ -15979,7 +15916,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExplainBreakpoint(const char*,const char* #else # define sqlcipher_sqlite3ExplainBreakpoint(A,B) /*no-op*/ #endif -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeAddParseSchemaOp(Vdbe*, int, char*, u16); SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeOpcode(Vdbe*, int addr, u8); SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); @@ -16003,7 +15940,6 @@ SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMakeLabel(Parse*); SQLITE_PRIVATE void sqlcipher_sqlite3VdbeRunOnlyOnce(Vdbe*); SQLITE_PRIVATE void sqlcipher_sqlite3VdbeReusable(Vdbe*); SQLITE_PRIVATE void sqlcipher_sqlite3VdbeDelete(Vdbe*); -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeClearObject(sqlcipher_sqlite3*,Vdbe*); SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMakeReady(Vdbe*,Parse*); SQLITE_PRIVATE int sqlcipher_sqlite3VdbeFinalize(Vdbe*); SQLITE_PRIVATE void sqlcipher_sqlite3VdbeResolveLabel(Vdbe*, int); @@ -16446,6 +16382,19 @@ SQLITE_PRIVATE int sqlcipher_sqlite3PCacheIsDirty(PCache *pCache); # define SET_FULLSYNC(x,y) #endif +/* Maximum pathname length. Note: FILENAME_MAX defined by stdio.h +*/ +#ifndef SQLITE_MAX_PATHLEN +# define SQLITE_MAX_PATHLEN FILENAME_MAX +#endif + +/* Maximum number of symlinks that will be resolved while trying to +** expand a filename in xFullPathname() in the VFS. +*/ +#ifndef SQLITE_MAX_SYMLINK +# define SQLITE_MAX_SYMLINK 200 +#endif + /* ** The default size of a disk sector */ @@ -16951,6 +16900,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CryptFunc(sqlcipher_sqlite3_context*,int,sq #endif /* SQLITE_OMIT_DEPRECATED */ #define SQLITE_TRACE_NONLEGACY_MASK 0x0f /* Normal flags */ +/* +** Maximum number of sqlcipher_sqlite3.aDb[] entries. This is the number of attached +** databases plus 2 for "main" and "temp". +*/ +#define SQLITE_MAX_DB (SQLITE_MAX_ATTACHED+2) /* ** Each database connection is an instance of the following structure. @@ -16969,9 +16923,10 @@ struct sqlcipher_sqlite3 { u32 nSchemaLock; /* Do not reset the schema when non-zero */ unsigned int openFlags; /* Flags passed to sqlcipher_sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ + int errByteOffset; /* Byte offset of error in SQL statement */ int errMask; /* & result codes with this before returning */ int iSysErrno; /* Errno value from last system error */ - u16 dbOptFlags; /* Flags to enable/disable optimizations */ + u32 dbOptFlags; /* Flags to enable/disable optimizations */ u8 enc; /* Text encoding */ u8 autoCommit; /* The auto-commit flag. */ u8 temp_store; /* 1: file 2: memory 0: default */ @@ -16985,10 +16940,10 @@ struct sqlcipher_sqlite3 { u8 mTrace; /* zero or more SQLITE_TRACE flags */ u8 noSharedCache; /* True if no shared-cache backends */ u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */ + u8 eOpenState; /* Current condition of the connection */ int nextPagesize; /* Pagesize after VACUUM if >0 */ - u32 magic; /* Magic number for detect library misuse */ - int nChange; /* Value returned by sqlcipher_sqlite3_changes() */ - int nTotalChange; /* Value returned by sqlcipher_sqlite3_total_changes() */ + i64 nChange; /* Value returned by sqlcipher_sqlite3_changes() */ + i64 nTotalChange; /* Value returned by sqlcipher_sqlite3_total_changes() */ int aLimit[SQLITE_N_LIMIT]; /* Limits */ int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ struct sqlcipher_sqlite3InitInfo { /* Information used during initialization */ @@ -16998,7 +16953,7 @@ struct sqlcipher_sqlite3 { unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ unsigned imposterTable : 1; /* Building an imposter table */ unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ - char **azInit; /* "type", "name", and "tbl_name" columns */ + const char **azInit; /* "type", "name", and "tbl_name" columns */ } init; int nVdbeActive; /* Number of VDBEs currently running */ int nVdbeRead; /* Number of active VDBEs that read or write */ @@ -17008,10 +16963,10 @@ struct sqlcipher_sqlite3 { int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ union { - void (*xLegacy)(void*,const char*); /* Legacy trace function */ - int (*xV2)(u32,void*,void*,void*); /* V2 Trace function */ + void (*xLegacy)(void*,const char*); /* mTrace==SQLITE_TRACE_LEGACY */ + int (*xV2)(u32,void*,void*,void*); /* All other mTrace values */ } trace; - void *pTraceArg; /* Argument to the trace function */ + void *pTraceArg; /* Argument to the trace function */ #ifndef SQLITE_OMIT_DEPRECATED void (*xProfile)(void*,const char*,u64); /* Profiling function */ void *pProfileArg; /* Argument to profile function */ @@ -17022,6 +16977,9 @@ struct sqlcipher_sqlite3 { void (*xRollbackCallback)(void*); /* Invoked at every commit. */ void *pUpdateArg; void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); + void *pAutovacPagesArg; /* Client argument to autovac_pages */ + void (*xAutovacDestr)(void*); /* Destructor for pAutovacPAgesArg */ + unsigned int (*xAutovacPages)(void*,const char*,u32,u32,u32); Parse *pParse; /* Current parse */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK void *pPreUpdateArg; /* First argument to xPreUpdateCallback */ @@ -17151,6 +17109,7 @@ struct sqlcipher_sqlite3 { #define SQLITE_CountRows HI(0x00001) /* Count rows changed by INSERT, */ /* DELETE, or UPDATE and return */ /* the count using a callback. */ +#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */ /* Flags used only if debugging */ #ifdef SQLITE_DEBUG @@ -17178,24 +17137,34 @@ struct sqlcipher_sqlite3 { ** sqlcipher_sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to ** selectively disable various optimizations. */ -#define SQLITE_QueryFlattener 0x0001 /* Query flattening */ -#define SQLITE_WindowFunc 0x0002 /* Use xInverse for window functions */ -#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ -#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ -#define SQLITE_DistinctOpt 0x0010 /* DISTINCT using indexes */ -#define SQLITE_CoverIdxScan 0x0020 /* Covering index scans */ -#define SQLITE_OrderByIdxJoin 0x0040 /* ORDER BY of joins via index */ -#define SQLITE_Transitive 0x0080 /* Transitive constraints */ -#define SQLITE_OmitNoopJoin 0x0100 /* Omit unused tables in joins */ -#define SQLITE_CountOfView 0x0200 /* The count-of-view optimization */ -#define SQLITE_CursorHints 0x0400 /* Add OP_CursorHint opcodes */ -#define SQLITE_Stat4 0x0800 /* Use STAT4 data */ - /* TH3 expects the Stat4 ^^^^^^ value to be 0x0800. Don't change it */ -#define SQLITE_PushDown 0x1000 /* The push-down optimization */ -#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */ -#define SQLITE_SkipScan 0x4000 /* Skip-scans */ -#define SQLITE_PropagateConst 0x8000 /* The constant propagation opt */ -#define SQLITE_AllOpts 0xffff /* All optimizations */ +#define SQLITE_QueryFlattener 0x00000001 /* Query flattening */ +#define SQLITE_WindowFunc 0x00000002 /* Use xInverse for window functions */ +#define SQLITE_GroupByOrder 0x00000004 /* GROUPBY cover of ORDERBY */ +#define SQLITE_FactorOutConst 0x00000008 /* Constant factoring */ +#define SQLITE_DistinctOpt 0x00000010 /* DISTINCT using indexes */ +#define SQLITE_CoverIdxScan 0x00000020 /* Covering index scans */ +#define SQLITE_OrderByIdxJoin 0x00000040 /* ORDER BY of joins via index */ +#define SQLITE_Transitive 0x00000080 /* Transitive constraints */ +#define SQLITE_OmitNoopJoin 0x00000100 /* Omit unused tables in joins */ +#define SQLITE_CountOfView 0x00000200 /* The count-of-view optimization */ +#define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */ +#define SQLITE_Stat4 0x00000800 /* Use STAT4 data */ + /* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */ +#define SQLITE_PushDown 0x00001000 /* The push-down optimization */ +#define SQLITE_SimplifyJoin 0x00002000 /* Convert LEFT JOIN to JOIN */ +#define SQLITE_SkipScan 0x00004000 /* Skip-scans */ +#define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */ +#define SQLITE_MinMaxOpt 0x00010000 /* The min/max optimization */ +#define SQLITE_SeekScan 0x00020000 /* The OP_SeekScan optimization */ +#define SQLITE_OmitOrderBy 0x00040000 /* Omit pointless ORDER BY */ + /* TH3 expects this value ^^^^^^^^^^ to be 0x40000. Coordinate any change */ +#define SQLITE_BloomFilter 0x00080000 /* Use a Bloom filter on searches */ +#define SQLITE_BloomPulldown 0x00100000 /* Run Bloom filters early */ +#define SQLITE_BalancedMerge 0x00200000 /* Balance multi-way merges */ +#define SQLITE_ReleaseReg 0x00400000 /* Use OP_ReleaseReg for testing */ +#define SQLITE_FlttnUnionAll 0x00800000 /* Disable the UNION ALL flattener */ + /* TH3 expects this value ^^^^^^^^^^ See flatten04.test */ +#define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* ** Macros for testing whether or not optimizations are enabled or disabled. @@ -17209,17 +17178,16 @@ struct sqlcipher_sqlite3 { */ #define ConstFactorOk(P) ((P)->okConstFactor) -/* -** Possible values for the sqlite.magic field. -** The numbers are obtained at random and have no special meaning, other -** than being distinct from one another. +/* Possible values for the sqlcipher_sqlite3.eOpenState field. +** The numbers are randomly selected such that a minimum of three bits must +** change to convert any number to another or to zero */ -#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ -#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ -#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ -#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ -#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ -#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */ +#define SQLITE_STATE_OPEN 0x76 /* Database is open */ +#define SQLITE_STATE_CLOSED 0xce /* Database is closed */ +#define SQLITE_STATE_SICK 0xba /* Error and awaiting close */ +#define SQLITE_STATE_BUSY 0x6d /* Database currently in use */ +#define SQLITE_STATE_ERROR 0xd5 /* An SQLITE_MISUSE error occurred */ +#define SQLITE_STATE_ZOMBIE 0xa7 /* Close with last statement close */ /* ** Each SQL function is defined by an instance of the following @@ -17244,7 +17212,7 @@ struct FuncDef { union { FuncDef *pHash; /* Next with a different name but the same hash */ FuncDestructor *pDestructor; /* Reference counted destructor function */ - } u; + } u; /* pHash if SQLITE_FUNC_BUILTIN, pDestructor otherwise */ }; /* @@ -17274,12 +17242,13 @@ struct FuncDestructor { ** are assert() statements in the code to verify this. ** ** Value constraints (enforced via assert()): -** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg -** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG -** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG -** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API -** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API -** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS +** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg +** SQLITE_FUNC_ANYORDER == NC_OrderAgg == SF_OrderByReqd +** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG +** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG +** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API +** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API +** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS ** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API */ #define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */ @@ -17297,13 +17266,15 @@ struct FuncDestructor { #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a ** single query - might change over time */ #define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */ -#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */ +/* 0x8000 -- available for reuse */ #define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ #define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ #define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */ #define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */ #define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */ #define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */ +#define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */ +#define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */ /* Identifier numbers for each in-line function */ #define INLINEFUNC_coalesce 0 @@ -17312,6 +17283,7 @@ struct FuncDestructor { #define INLINEFUNC_expr_compare 3 #define INLINEFUNC_affinity 4 #define INLINEFUNC_iif 5 +#define INLINEFUNC_sqlite_offset 6 #define INLINEFUNC_unlikely 99 /* Default case */ /* @@ -17351,6 +17323,9 @@ struct FuncDestructor { ** a single query. The iArg is ignored. The user-data is always set ** to a NULL pointer. The bNC parameter is not used. ** +** MFUNCTION(zName, nArg, xPtr, xFunc) +** For math-library functions. xPtr is an arbitrary pointer. +** ** PURE_DATE(zName, nArg, iArg, bNC, xFunc) ** Used for "pure" date/time functions, this macro is like DFUNCTION ** except that it does set the SQLITE_FUNC_CONSTANT flags. iArg is @@ -17363,7 +17338,7 @@ struct FuncDestructor { ** are interpreted in the same way as the first 4 parameters to ** FUNCTION(). ** -** WFUNCTION(zName, nArg, iArg, xStep, xFinal, xValue, xInverse) +** WAGGREGATE(zName, nArg, iArg, xStep, xFinal, xValue, xInverse) ** Used to create an aggregate function definition implemented by ** the C functions xStep and xFinal. The first four parameters ** are interpreted in the same way as the first 4 parameters to @@ -17378,41 +17353,55 @@ struct FuncDestructor { ** parameter. */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } +#define MFUNCTION(zName, nArg, xPtr, xFunc) \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ + xPtr, 0, xFunc, 0, 0, 0, #zName, {0} } +#define JFUNCTION(zName, nArg, iArg, xFunc) \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|\ + SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define INLINE_FUNC(zName, nArg, iArg, mFlags) \ - {nArg, SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} } #define TEST_FUNC(zName, nArg, iArg, mFlags) \ - {nArg, SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \ SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} } #define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \ 0, 0, xFunc, 0, 0, 0, #zName, {0} } #define PURE_DATE(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ (void*)&sqlcipher_sqlite3Config, 0, xFunc, 0, 0, 0, #zName, {0} } #define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ - {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ - {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ pArg, 0, xFunc, 0, 0, 0, #zName, } #define LIKEFUNC(zName, nArg, arg, flags) \ - {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ (void *)arg, 0, likeFunc, 0, 0, 0, #zName, {0} } #define WAGGREGATE(zName, nArg, arg, nc, xStep, xFinal, xValue, xInverse, f) \ - {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,xInverse,#zName, {0}} #define INTERNAL_FUNCTION(zName, nArg, xFunc) \ - {nArg, SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ 0, 0, xFunc, 0, 0, 0, #zName, {0} } @@ -17468,19 +17457,48 @@ struct Module { ** or equal to the table column index. It is ** equal if and only if there are no VIRTUAL ** columns to the left. +** +** Notes on zCnName: +** The zCnName field stores the name of the column, the datatype of the +** column, and the collating sequence for the column, in that order, all in +** a single allocation. Each string is 0x00 terminated. The datatype +** is only included if the COLFLAG_HASTYPE bit of colFlags is set and the +** collating sequence name is only included if the COLFLAG_HASCOLL bit is +** set. */ struct Column { - char *zName; /* Name of this column, \000, then the type */ - Expr *pDflt; /* Default value or GENERATED ALWAYS AS value */ - char *zColl; /* Collating sequence. If NULL, use the default */ - u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ - char affinity; /* One of the SQLITE_AFF_... values */ - u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */ - u8 hName; /* Column name hash for faster lookup */ - u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ + char *zCnName; /* Name of this column */ + unsigned notNull :4; /* An OE_ code for handling a NOT NULL constraint */ + unsigned eCType :4; /* One of the standard types */ + char affinity; /* One of the SQLITE_AFF_... values */ + u8 szEst; /* Est size of value in this column. sizeof(INT)==1 */ + u8 hName; /* Column name hash for faster lookup */ + u16 iDflt; /* 1-based index of DEFAULT. 0 means "none" */ + u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ }; -/* Allowed values for Column.colFlags: +/* Allowed values for Column.eCType. +** +** Values must match entries in the global constant arrays +** sqlcipher_sqlite3StdTypeLen[] and sqlcipher_sqlite3StdType[]. Each value is one more +** than the offset into these arrays for the corresponding name. +** Adjust the SQLITE_N_STDTYPE value if adding or removing entries. +*/ +#define COLTYPE_CUSTOM 0 /* Type appended to zName */ +#define COLTYPE_ANY 1 +#define COLTYPE_BLOB 2 +#define COLTYPE_INT 3 +#define COLTYPE_INTEGER 4 +#define COLTYPE_REAL 5 +#define COLTYPE_TEXT 6 +#define SQLITE_N_STDTYPE 6 /* Number of standard types */ + +/* Allowed values for Column.colFlags. +** +** Constraints: +** TF_HasVirtual == COLFLAG_VIRTUAL +** TF_HasStored == COLFLAG_STORED +** TF_HasHidden == COLFLAG_HIDDEN */ #define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */ #define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */ @@ -17491,6 +17509,8 @@ struct Column { #define COLFLAG_STORED 0x0040 /* GENERATED ALWAYS AS ... STORED */ #define COLFLAG_NOTAVAIL 0x0080 /* STORED column not yet calculated */ #define COLFLAG_BUSY 0x0100 /* Blocks recursion on GENERATED columns */ +#define COLFLAG_HASCOLL 0x0200 /* Has collating sequence name in zCnName */ +#define COLFLAG_NOEXPAND 0x0400 /* Omit this column when expanding "*" */ #define COLFLAG_GENERATED 0x0060 /* Combo: _STORED, _VIRTUAL */ #define COLFLAG_NOINSERT 0x0062 /* Combo: _HIDDEN, _STORED, _VIRTUAL */ @@ -17556,9 +17576,7 @@ struct CollSeq { ** operator is NULL. It is added to certain comparison operators to ** prove that the operands are always NOT NULL. */ -#define SQLITE_KEEPNULL 0x08 /* Used by vector == or <> */ #define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */ -#define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */ #define SQLITE_NULLEQ 0x80 /* NULL=NULL */ #define SQLITE_NOTNULL 0x90 /* Assert that operands are never NULL */ @@ -17622,15 +17640,13 @@ struct VTable { #define SQLITE_VTABRISK_High 2 /* -** The schema for each SQL table and view is represented in memory -** by an instance of the following structure. +** The schema for each SQL table, virtual table, and view is represented +** in memory by an instance of the following structure. */ struct Table { char *zName; /* Name of the table or view */ Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ - Select *pSelect; /* NULL for tables. Points to definition if a view. */ - FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ ExprList *pCheck; /* All CHECK constraints */ /* ... also used as column name list in a VIEW */ @@ -17646,17 +17662,25 @@ struct Table { LogEst costMult; /* Cost multiplier for using this table */ #endif u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ -#ifndef SQLITE_OMIT_ALTERTABLE - int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ -#endif -#ifndef SQLITE_OMIT_VIRTUALTABLE - int nModuleArg; /* Number of arguments to the module */ - char **azModuleArg; /* 0: module 1: schema 2: vtab name 3...: args */ - VTable *pVTable; /* List of VTable objects. */ -#endif - Trigger *pTrigger; /* List of triggers stored in pSchema */ + u8 eTabType; /* 0: normal, 1: virtual, 2: view */ + union { + struct { /* Used by ordinary tables: */ + int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ + FKey *pFKey; /* Linked list of all foreign keys in this table */ + ExprList *pDfltList; /* DEFAULT clauses on various columns. + ** Or the AS clause for generated columns. */ + } tab; + struct { /* Used by views: */ + Select *pSelect; /* View definition */ + } view; + struct { /* Used by virtual tables only: */ + int nArg; /* Number of arguments to the module */ + char **azArg; /* 0: module 1: schema 2: vtab name 3...: args */ + VTable *p; /* List of VTable objects. */ + } vtab; + } u; + Trigger *pTrigger; /* List of triggers on this object */ Schema *pSchema; /* Schema that contains this table */ - Table *pNextZombie; /* Next on the Parse.pZombieTab list */ }; /* @@ -17670,25 +17694,39 @@ struct Table { ** ** Constraints: ** -** TF_HasVirtual == COLFLAG_Virtual -** TF_HasStored == COLFLAG_Stored -*/ -#define TF_Readonly 0x0001 /* Read-only system table */ -#define TF_Ephemeral 0x0002 /* An ephemeral table */ -#define TF_HasPrimaryKey 0x0004 /* Table has a primary key */ -#define TF_Autoincrement 0x0008 /* Integer primary key is autoincrement */ -#define TF_HasStat1 0x0010 /* nRowLogEst set from sqlite_stat1 */ -#define TF_HasVirtual 0x0020 /* Has one or more VIRTUAL columns */ -#define TF_HasStored 0x0040 /* Has one or more STORED columns */ -#define TF_HasGenerated 0x0060 /* Combo: HasVirtual + HasStored */ -#define TF_WithoutRowid 0x0080 /* No rowid. PRIMARY KEY is the key */ -#define TF_StatsUsed 0x0100 /* Query planner decisions affected by +** TF_HasVirtual == COLFLAG_VIRTUAL +** TF_HasStored == COLFLAG_STORED +** TF_HasHidden == COLFLAG_HIDDEN +*/ +#define TF_Readonly 0x00000001 /* Read-only system table */ +#define TF_HasHidden 0x00000002 /* Has one or more hidden columns */ +#define TF_HasPrimaryKey 0x00000004 /* Table has a primary key */ +#define TF_Autoincrement 0x00000008 /* Integer primary key is autoincrement */ +#define TF_HasStat1 0x00000010 /* nRowLogEst set from sqlite_stat1 */ +#define TF_HasVirtual 0x00000020 /* Has one or more VIRTUAL columns */ +#define TF_HasStored 0x00000040 /* Has one or more STORED columns */ +#define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */ +#define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */ +#define TF_StatsUsed 0x00000100 /* Query planner decisions affected by ** Index.aiRowLogEst[] values */ -#define TF_NoVisibleRowid 0x0200 /* No user-visible "rowid" column */ -#define TF_OOOHidden 0x0400 /* Out-of-Order hidden columns */ -#define TF_HasNotNull 0x0800 /* Contains NOT NULL constraints */ -#define TF_Shadow 0x1000 /* True for a shadow table */ -#define TF_HasStat4 0x2000 /* STAT4 info available for this table */ +#define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */ +#define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ +#define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ +#define TF_Shadow 0x00001000 /* True for a shadow table */ +#define TF_HasStat4 0x00002000 /* STAT4 info available for this table */ +#define TF_Ephemeral 0x00004000 /* An ephemeral table */ +#define TF_Eponymous 0x00008000 /* An eponymous virtual table */ +#define TF_Strict 0x00010000 /* STRICT mode */ + +/* +** Allowed values for Table.eTabType +*/ +#define TABTYP_NORM 0 /* Ordinary table */ +#define TABTYP_VTAB 1 /* Virtual table */ +#define TABTYP_VIEW 2 /* A view */ + +#define IsView(X) ((X)->eTabType==TABTYP_VIEW) +#define IsOrdinaryTable(X) ((X)->eTabType==TABTYP_NORM) /* ** Test to see whether or not a table is a virtual table. This is @@ -17696,9 +17734,9 @@ struct Table { ** table support is omitted from the build. */ #ifndef SQLITE_OMIT_VIRTUALTABLE -# define IsVirtual(X) ((X)->nModuleArg) +# define IsVirtual(X) ((X)->eTabType==TABTYP_VTAB) # define ExprIsVtab(X) \ - ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->nModuleArg) + ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->eTabType==TABTYP_VTAB) #else # define IsVirtual(X) 0 # define ExprIsVtab(X) 0 @@ -17785,16 +17823,22 @@ struct FKey { ** is returned. REPLACE means that preexisting database rows that caused ** a UNIQUE constraint violation are removed so that the new insert or ** update can proceed. Processing continues and no error is reported. +** UPDATE applies to insert operations only and means that the insert +** is omitted and the DO UPDATE clause of an upsert is run instead. ** -** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. +** RESTRICT, SETNULL, SETDFLT, and CASCADE actions apply only to foreign keys. ** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the ** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign -** key is set to NULL. CASCADE means that a DELETE or UPDATE of the +** key is set to NULL. SETDFLT means that the foreign key is set +** to its default value. CASCADE means that a DELETE or UPDATE of the ** referenced table row is propagated into the row that holds the ** foreign key. ** +** The OE_Default value is a place holder that means to use whatever +** conflict resolution algorthm is required from context. +** ** The following symbolic values are used to record which type -** of action to take. +** of conflict resolution action to take. */ #define OE_None 0 /* There is no constraint to check */ #define OE_Rollback 1 /* Fail the operation and rollback the transaction */ @@ -17873,6 +17917,11 @@ struct KeyInfo { struct UnpackedRecord { KeyInfo *pKeyInfo; /* Collation and sort-order information */ Mem *aMem; /* Values */ + union { + char *z; /* Cache of aMem[0].z for vdbeRecordCompareString() */ + i64 i; /* Cache of aMem[0].u.i for vdbeRecordCompareInt() */ + } u; + int n; /* Cache of aMem[0].n used by vdbeRecordCompareString() */ u16 nField; /* Number of entries in apMem[] */ i8 default_rc; /* Comparison result if keys are equal */ u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */ @@ -18048,10 +18097,10 @@ struct AggInfo { FuncDef *pFunc; /* The aggregate function implementation */ int iMem; /* Memory location that acts as accumulator */ int iDistinct; /* Ephemeral table used to enforce DISTINCT */ + int iDistAddr; /* Address of OP_OpenEphemeral */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ u32 selId; /* Select to which this AggInfo belongs */ - AggInfo *pNext; /* Next in list of them all */ }; /* @@ -18081,10 +18130,10 @@ typedef int ynVar; ** tree. ** ** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, -** or TK_STRING), then Expr.token contains the text of the SQL literal. If -** the expression is a variable (TK_VARIABLE), then Expr.token contains the +** or TK_STRING), then Expr.u.zToken contains the text of the SQL literal. If +** the expression is a variable (TK_VARIABLE), then Expr.u.zToken contains the ** variable name. Finally, if the expression is an SQL function (TK_FUNCTION), -** then Expr.token contains the name of the function. +** then Expr.u.zToken contains the name of the function. ** ** Expr.pRight and Expr.pLeft are the left and right subexpressions of a ** binary operator. Either or both may be NULL. @@ -18124,7 +18173,7 @@ typedef int ynVar; ** help reduce memory requirements, sometimes an Expr object will be ** truncated. And to reduce the number of memory allocations, sometimes ** two or more Expr objects will be stored in a single memory allocation, -** together with Expr.zToken strings. +** together with Expr.u.zToken strings. ** ** If the EP_Reduced and EP_TokenOnly flags are set when ** an Expr object is truncated. When EP_Reduced is set, then all @@ -18180,7 +18229,10 @@ struct Expr { ** TK_VARIABLE: variable number (always >= 1). ** TK_SELECT_COLUMN: column of the result vector */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ - i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ + union { + int iJoin; /* If EP_OuterON or EP_InnerON, the right table */ + int iOfst; /* else: start of token from start of statement */ + } w; AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ union { Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL @@ -18193,36 +18245,35 @@ struct Expr { } y; }; -/* -** The following are the meanings of bits in the Expr.flags field. +/* The following are the meanings of bits in the Expr.flags field. ** Value restrictions: ** ** EP_Agg == NC_HasAgg == SF_HasAgg ** EP_Win == NC_HasWin */ -#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */ -#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */ -#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */ -#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */ +#define EP_OuterON 0x000001 /* Originates in ON/USING clause of outer join */ +#define EP_InnerON 0x000002 /* Originates in ON/USING of an inner join */ +#define EP_Distinct 0x000004 /* Aggregate function with DISTINCT keyword */ +#define EP_HasFunc 0x000008 /* Contains one or more functions of any kind */ #define EP_Agg 0x000010 /* Contains one or more aggregate functions */ -#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */ -#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */ -#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */ -#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */ -#define EP_Commuted 0x000200 /* Comparison operator has been commuted */ -#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */ -#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */ -#define EP_Skip 0x001000 /* Operator does not contribute to affinity */ -#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ -#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ +#define EP_FixedCol 0x000020 /* TK_Column with a known fixed value */ +#define EP_VarSelect 0x000040 /* pSelect is correlated, not constant */ +#define EP_DblQuoted 0x000080 /* token.z was originally in "..." */ +#define EP_InfixFunc 0x000100 /* True for an infix function: LIKE, GLOB, etc */ +#define EP_Collate 0x000200 /* Tree contains a TK_COLLATE operator */ +#define EP_Commuted 0x000400 /* Comparison operator has been commuted */ +#define EP_IntValue 0x000800 /* Integer value contained in u.iValue */ +#define EP_xIsSelect 0x001000 /* x.pSelect is valid (otherwise x.pList is) */ +#define EP_Skip 0x002000 /* Operator does not contribute to affinity */ +#define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ #define EP_Win 0x008000 /* Contains window functions */ -#define EP_MemToken 0x010000 /* Need to sqlcipher_sqlite3DbFree() Expr.zToken */ -#define EP_IfNullRow 0x020000 /* The TK_IF_NULL_ROW opcode */ -#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ -#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ -#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */ -#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */ -#define EP_Alias 0x400000 /* Is an alias for a result set column */ +#define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ +#define EP_MemToken 0x020000 /* Need to sqlcipher_sqlite3DbFree() Expr.zToken */ +#define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */ +#define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */ +#define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ +#define EP_CanBeNull 0x200000 /* Can be null despite NOT NULL constraint */ +#define EP_Subquery 0x400000 /* Tree contains a TK_SELECT operator */ #define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */ #define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ #define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */ @@ -18233,23 +18284,31 @@ struct Expr { #define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ /* 0x80000000 // Available */ -/* -** The EP_Propagate mask is a set of properties that automatically propagate +/* The EP_Propagate mask is a set of properties that automatically propagate ** upwards into parent nodes. */ #define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc) -/* -** These macros can be used to test, set, or clear bits in the +/* Macros can be used to test, set, or clear bits in the ** Expr.flags field. */ #define ExprHasProperty(E,P) (((E)->flags&(P))!=0) #define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P)) #define ExprSetProperty(E,P) (E)->flags|=(P) #define ExprClearProperty(E,P) (E)->flags&=~(P) -#define ExprAlwaysTrue(E) (((E)->flags&(EP_FromJoin|EP_IsTrue))==EP_IsTrue) -#define ExprAlwaysFalse(E) (((E)->flags&(EP_FromJoin|EP_IsFalse))==EP_IsFalse) +#define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue) +#define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse) +/* Macros used to ensure that the correct members of unions are accessed +** in Expr. +*/ +#define ExprUseUToken(E) (((E)->flags&EP_IntValue)==0) +#define ExprUseUValue(E) (((E)->flags&EP_IntValue)!=0) +#define ExprUseXList(E) (((E)->flags&EP_xIsSelect)==0) +#define ExprUseXSelect(E) (((E)->flags&EP_xIsSelect)!=0) +#define ExprUseYTab(E) (((E)->flags&(EP_WinFunc|EP_Subrtn))==0) +#define ExprUseYWin(E) (((E)->flags&EP_WinFunc)!=0) +#define ExprUseYSub(E) (((E)->flags&EP_Subrtn)!=0) /* Flags for use with Expr.vvaFlags */ @@ -18321,21 +18380,29 @@ struct Expr { */ struct ExprList { int nExpr; /* Number of expressions on the list */ + int nAlloc; /* Number of a[] slots allocated */ struct ExprList_item { /* For each expression in the list */ Expr *pExpr; /* The parse tree for this expression */ char *zEName; /* Token associated with this expression */ - u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */ - unsigned eEName :2; /* Meaning of zEName */ - unsigned done :1; /* A flag to indicate when processing is finished */ - unsigned reusable :1; /* Constant expression is reusable */ - unsigned bSorterRef :1; /* Defer evaluation until after sorting */ - unsigned bNulls: 1; /* True if explicit "NULLS FIRST/LAST" */ + struct { + u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */ + unsigned eEName :2; /* Meaning of zEName */ + unsigned done :1; /* Indicates when processing is finished */ + unsigned reusable :1; /* Constant expression is reusable */ + unsigned bSorterRef :1; /* Defer evaluation until after sorting */ + unsigned bNulls :1; /* True if explicit "NULLS FIRST/LAST" */ + unsigned bUsed :1; /* This column used in a SF_NestedFrom subquery */ + unsigned bUsingTerm:1; /* Term from the USING clause of a NestedFrom */ + unsigned bNoExpand: 1; /* Term is an auxiliary in NestedFrom and should + ** not be expanded by "*" in parent queries */ + } fg; union { - struct { + struct { /* Used by any ExprList other than Parse.pConsExpr */ u16 iOrderByCol; /* For ORDER BY, column number in result set */ u16 iAlias; /* Index into Parse.aAlias[] for zName */ } x; - int iConstExprReg; /* Register in which Expr value is cached */ + int iConstExprReg; /* Register in which Expr value is cached. Used only + ** by Parse.pConstExpr */ } u; } a[1]; /* One slot for each expression in the list */ }; @@ -18363,11 +18430,86 @@ struct ExprList { ** If "a" is the k-th column of table "t", then IdList.a[0].idx==k. */ struct IdList { + int nId; /* Number of identifiers on the list */ + u8 eU4; /* Which element of a.u4 is valid */ struct IdList_item { char *zName; /* Name of the identifier */ - int idx; /* Index in some Table.aCol[] of a column named zName */ - } *a; - int nId; /* Number of identifiers on the list */ + union { + int idx; /* Index in some Table.aCol[] of a column named zName */ + Expr *pExpr; /* Expr to implement a USING variable -- NOT USED */ + } u4; + } a[1]; +}; + +/* +** Allowed values for IdList.eType, which determines which value of the a.u4 +** is valid. +*/ +#define EU4_NONE 0 /* Does not use IdList.a.u4 */ +#define EU4_IDX 1 /* Uses IdList.a.u4.idx */ +#define EU4_EXPR 2 /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */ + +/* +** The SrcItem object represents a single term in the FROM clause of a query. +** The SrcList object is mostly an array of SrcItems. +** +** Union member validity: +** +** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc +** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy +** u2.pIBIndex fg.isIndexedBy && !fg.isCte +** u2.pCteUse fg.isCte && !fg.isIndexedBy +*/ +struct SrcItem { + Schema *pSchema; /* Schema to which this item is fixed */ + char *zDatabase; /* Name of database holding this table */ + char *zName; /* Name of the table */ + char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ + Table *pTab; /* An SQL table corresponding to zName */ + Select *pSelect; /* A SELECT statement used in place of a table name */ + int addrFillSub; /* Address of subroutine to manifest a subquery */ + int regReturn; /* Register holding return address of addrFillSub */ + int regResult; /* Registers holding results of a co-routine */ + struct { + u8 jointype; /* Type of join between this table and the previous */ + unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ + unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ + unsigned isTabFunc :1; /* True if table-valued-function syntax */ + unsigned isCorrelated :1; /* True if sub-query is correlated */ + unsigned isMaterialized:1; /* This is a materialized view */ + unsigned viaCoroutine :1; /* Implemented as a co-routine */ + unsigned isRecursive :1; /* True for recursive reference in WITH */ + unsigned fromDDL :1; /* Comes from sqlite_schema */ + unsigned isCte :1; /* This is a CTE */ + unsigned notCte :1; /* This item may not match a CTE */ + unsigned isUsing :1; /* u3.pUsing is valid */ + unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ + unsigned isSynthUsing :1; /* u3.pUsing is synthensized from NATURAL */ + unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ + } fg; + int iCursor; /* The VDBE cursor number used to access this table */ + union { + Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */ + IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ + } u3; + Bitmask colUsed; /* Bit N (1<" clause */ + ExprList *pFuncArg; /* Arguments to table-valued-function */ + } u1; + union { + Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ + CteUse *pCteUse; /* CTE Usage info info fg.isCte is true */ + } u2; +}; + +/* +** The OnOrUsing object represents either an ON clause or a USING clause. +** It can never be both at the same time, but it can be neither. +*/ +struct OnOrUsing { + Expr *pOn; /* The ON clause of a join */ + IdList *pUsing; /* The USING clause of a join */ }; /* @@ -18392,49 +18534,21 @@ struct IdList { struct SrcList { int nSrc; /* Number of tables or subqueries in the FROM clause */ u32 nAlloc; /* Number of entries allocated in a[] below */ - struct SrcList_item { - Schema *pSchema; /* Schema to which this item is fixed */ - char *zDatabase; /* Name of database holding this table */ - char *zName; /* Name of the table */ - char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ - Table *pTab; /* An SQL table corresponding to zName */ - Select *pSelect; /* A SELECT statement used in place of a table name */ - int addrFillSub; /* Address of subroutine to manifest a subquery */ - int regReturn; /* Register holding return address of addrFillSub */ - int regResult; /* Registers holding results of a co-routine */ - struct { - u8 jointype; /* Type of join between this table and the previous */ - unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ - unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ - unsigned isTabFunc :1; /* True if table-valued-function syntax */ - unsigned isCorrelated :1; /* True if sub-query is correlated */ - unsigned viaCoroutine :1; /* Implemented as a co-routine */ - unsigned isRecursive :1; /* True for recursive reference in WITH */ - unsigned fromDDL :1; /* Comes from sqlite_schema */ - } fg; - int iCursor; /* The VDBE cursor number used to access this table */ - Expr *pOn; /* The ON clause of a join */ - IdList *pUsing; /* The USING clause of a join */ - Bitmask colUsed; /* Bit N (1<" clause */ - ExprList *pFuncArg; /* Arguments to table-valued-function */ - } u1; - Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ - } a[1]; /* One entry for each identifier on the list */ + SrcItem a[1]; /* One entry for each identifier on the list */ }; /* ** Permitted values of the SrcList.a.jointype field */ -#define JT_INNER 0x0001 /* Any kind of inner or cross join */ -#define JT_CROSS 0x0002 /* Explicit use of the CROSS keyword */ -#define JT_NATURAL 0x0004 /* True for a "natural" join */ -#define JT_LEFT 0x0008 /* Left outer join */ -#define JT_RIGHT 0x0010 /* Right outer join */ -#define JT_OUTER 0x0020 /* The "OUTER" keyword is present */ -#define JT_ERROR 0x0040 /* unknown or unsupported join type */ - +#define JT_INNER 0x01 /* Any kind of inner or cross join */ +#define JT_CROSS 0x02 /* Explicit use of the CROSS keyword */ +#define JT_NATURAL 0x04 /* True for a "natural" join */ +#define JT_LEFT 0x08 /* Left outer join */ +#define JT_RIGHT 0x10 /* Right outer join */ +#define JT_OUTER 0x20 /* The "OUTER" keyword is present */ +#define JT_LTORJ 0x40 /* One of the LEFT operands of a RIGHT JOIN + ** Mnemonic: Left Table Of Right Join */ +#define JT_ERROR 0x80 /* unknown or unsupported join type */ /* ** Flags appropriate for the wctrlFlags parameter of sqlcipher_sqlite3WhereBegin() @@ -18455,9 +18569,9 @@ struct SrcList { #define WHERE_DISTINCTBY 0x0080 /* pOrderby is really a DISTINCT clause */ #define WHERE_WANT_DISTINCT 0x0100 /* All output needs to be distinct */ #define WHERE_SORTBYGROUP 0x0200 /* Support sqlcipher_sqlite3WhereIsSorted() */ - /* 0x0400 not currently used */ +#define WHERE_AGG_DISTINCT 0x0400 /* Query is "SELECT agg(DISTINCT ...)" */ #define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */ - /* 0x1000 not currently used */ +#define WHERE_RIGHT_JOIN 0x1000 /* Processing a RIGHT JOIN */ /* 0x2000 not currently used */ #define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */ /* 0x8000 not currently used */ @@ -18497,10 +18611,11 @@ struct NameContext { ExprList *pEList; /* Optional list of result-set columns */ AggInfo *pAggInfo; /* Information about aggregates at this level */ Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */ + int iBaseReg; /* For TK_REGISTER when parsing RETURNING */ } uNC; NameContext *pNext; /* Next outer name context. NULL for outermost */ int nRef; /* Number of names resolved by this context */ - int nErr; /* Number of errors encountered while resolving names */ + int nNcErr; /* Number of errors encountered while resolving names */ int ncFlags; /* Zero or more NC_* flags defined below */ Select *pWinSelect; /* SELECT statement for any window functions */ }; @@ -18509,29 +18624,33 @@ struct NameContext { ** Allowed values for the NameContext, ncFlags field. ** ** Value constraints (all checked via assert()): -** NC_HasAgg == SF_HasAgg == EP_Agg -** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX +** NC_HasAgg == SF_HasAgg == EP_Agg +** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX +** NC_OrderAgg == SF_OrderByReqd == SQLITE_FUNC_ANYORDER ** NC_HasWin == EP_Win ** */ -#define NC_AllowAgg 0x00001 /* Aggregate functions are allowed here */ -#define NC_PartIdx 0x00002 /* True if resolving a partial index WHERE */ -#define NC_IsCheck 0x00004 /* True if resolving a CHECK constraint */ -#define NC_GenCol 0x00008 /* True for a GENERATED ALWAYS AS clause */ -#define NC_HasAgg 0x00010 /* One or more aggregate functions seen */ -#define NC_IdxExpr 0x00020 /* True if resolving columns of CREATE INDEX */ -#define NC_SelfRef 0x0002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ -#define NC_VarSelect 0x00040 /* A correlated subquery has been seen */ -#define NC_UEList 0x00080 /* True if uNC.pEList is used */ -#define NC_UAggInfo 0x00100 /* True if uNC.pAggInfo is used */ -#define NC_UUpsert 0x00200 /* True if uNC.pUpsert is used */ -#define NC_MinMaxAgg 0x01000 /* min/max aggregates seen. See note above */ -#define NC_Complex 0x02000 /* True if a function or subquery seen */ -#define NC_AllowWin 0x04000 /* Window functions are allowed here */ -#define NC_HasWin 0x08000 /* One or more window functions seen */ -#define NC_IsDDL 0x10000 /* Resolving names in a CREATE statement */ -#define NC_InAggFunc 0x20000 /* True if analyzing arguments to an agg func */ -#define NC_FromDDL 0x40000 /* SQL text comes from sqlite_schema */ +#define NC_AllowAgg 0x000001 /* Aggregate functions are allowed here */ +#define NC_PartIdx 0x000002 /* True if resolving a partial index WHERE */ +#define NC_IsCheck 0x000004 /* True if resolving a CHECK constraint */ +#define NC_GenCol 0x000008 /* True for a GENERATED ALWAYS AS clause */ +#define NC_HasAgg 0x000010 /* One or more aggregate functions seen */ +#define NC_IdxExpr 0x000020 /* True if resolving columns of CREATE INDEX */ +#define NC_SelfRef 0x00002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ +#define NC_VarSelect 0x000040 /* A correlated subquery has been seen */ +#define NC_UEList 0x000080 /* True if uNC.pEList is used */ +#define NC_UAggInfo 0x000100 /* True if uNC.pAggInfo is used */ +#define NC_UUpsert 0x000200 /* True if uNC.pUpsert is used */ +#define NC_UBaseReg 0x000400 /* True if uNC.iBaseReg is used */ +#define NC_MinMaxAgg 0x001000 /* min/max aggregates seen. See note above */ +#define NC_Complex 0x002000 /* True if a function or subquery seen */ +#define NC_AllowWin 0x004000 /* Window functions are allowed here */ +#define NC_HasWin 0x008000 /* One or more window functions seen */ +#define NC_IsDDL 0x010000 /* Resolving names in a CREATE statement */ +#define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */ +#define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */ +#define NC_NoSelect 0x080000 /* Do not descend into sub-selects */ +#define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */ /* ** An instance of the following object describes a single ON CONFLICT @@ -18548,15 +18667,21 @@ struct NameContext { ** WHERE clause is omitted. */ struct Upsert { - ExprList *pUpsertTarget; /* Optional description of conflicting index */ + ExprList *pUpsertTarget; /* Optional description of conflict target */ Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */ ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */ Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ - /* The fields above comprise the parse tree for the upsert clause. - ** The fields below are used to transfer information from the INSERT - ** processing down into the UPDATE processing while generating code. - ** Upsert owns the memory allocated above, but not the memory below. */ - Index *pUpsertIdx; /* Constraint that pUpsertTarget identifies */ + Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ + u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */ + /* Above this point is the parse tree for the ON CONFLICT clauses. + ** The next group of fields stores intermediate data. */ + void *pToFree; /* Free memory when deleting the Upsert object */ + /* All fields above are owned by the Upsert object and must be freed + ** when the Upsert is destroyed. The fields below are used to transfer + ** information from the INSERT processing down into the UPDATE processing + ** while generating code. The fields below are owned by the INSERT + ** statement and will be freed by INSERT processing. */ + Index *pUpsertIdx; /* UNIQUE constraint specified by pUpsertTarget */ SrcList *pUpsertSrc; /* Table to be updated */ int regData; /* First register holding array of VALUES */ int iDataCur; /* Index of the data cursor */ @@ -18608,9 +18733,10 @@ struct Select { ** "Select Flag". ** ** Value constraints (all checked via assert()) -** SF_HasAgg == NC_HasAgg -** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX -** SF_FixedLimit == WHERE_USE_LIMIT +** SF_HasAgg == NC_HasAgg +** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX +** SF_OrderByReqd == NC_OrderAgg == SQLITE_FUNC_ANYORDER +** SF_FixedLimit == WHERE_USE_LIMIT */ #define SF_Distinct 0x0000001 /* Output should be DISTINCT */ #define SF_All 0x0000002 /* Includes the ALL keyword */ @@ -18635,7 +18761,14 @@ struct Select { #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ #define SF_View 0x0200000 /* SELECT statement is a view */ #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ -#define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */ +#define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ +#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */ +#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ +#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ +#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ + +/* True if S exists and has SF_NestedFrom */ +#define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0) /* ** The results of a SELECT can be distributed in several ways, as defined @@ -18806,6 +18939,17 @@ struct TriggerPrg { # define DbMaskNonZero(M) (M)!=0 #endif +/* +** An instance of the ParseCleanup object specifies an operation that +** should be performed after parsing to deallocation resources obtained +** during the parse and which are no longer needed. +*/ +struct ParseCleanup { + ParseCleanup *pNext; /* Next cleanup task */ + void *pPtr; /* Pointer to object to deallocate */ + void (*xCleanup)(sqlcipher_sqlite3*,void*); /* Deallocation routine */ +}; + /* ** An SQL parser context. A copy of this structure is passed through ** the parser and down into all the parser action routine in order to @@ -18837,6 +18981,10 @@ struct Parse { u8 okConstFactor; /* OK to factor out constants */ u8 disableLookaside; /* Number of times lookaside has been disabled */ u8 disableVtab; /* Disable all virtual tables for this parse */ + u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ +#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) + u8 earlyCleanup; /* OOM inside sqlcipher_sqlite3ParserAddCleanup() */ +#endif int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ int nErr; /* Number of errors seen */ @@ -18863,13 +19011,17 @@ struct Parse { AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ - Parse *pParentParse; /* Parent parser if this parser is nested */ - AggInfo *pAggList; /* List of all AggInfo objects */ - int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */ + TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ + ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */ + union { + int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */ + Returning *pReturning; /* The RETURNING clause */ + } u1; u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u32 oldmask; /* Mask of old.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ + u8 bReturning; /* Coding a RETURNING trigger */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ u8 disableTriggers; /* True to disable triggers */ @@ -18881,6 +19033,7 @@ struct Parse { **************************************************************************/ int aTempReg[8]; /* Holding area for temporary registers */ + Parse *pOuterParse; /* Outer Parse object when nested */ Token sNameToken; /* Token with unqualified schema object name */ /************************************************************************ @@ -18915,15 +19068,14 @@ struct Parse { Token sArg; /* Complete text of a module argument */ Table **apVtabLock; /* Pointer to virtual tables needing locking */ #endif - Table *pZombieTab; /* List of Table objects to delete after code gen */ - TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ With *pWith; /* Current WITH clause, or NULL */ - With *pWithToFree; /* Free this WITH object at the end of the parse */ #ifndef SQLITE_OMIT_ALTERTABLE RenameToken *pRename; /* Tokens subject to renaming by ALTER TABLE */ #endif }; +/* Allowed values for Parse.eParseMode +*/ #define PARSE_MODE_NORMAL 0 #define PARSE_MODE_DECLARE_VTAB 1 #define PARSE_MODE_RENAME 2 @@ -18932,7 +19084,8 @@ struct Parse { /* ** Sizes and pointers of various parts of the Parse object. */ -#define PARSE_HDR_SZ offsetof(Parse,aTempReg) /* Recursive part w/o aColCache*/ +#define PARSE_HDR(X) (((char*)(X))+offsetof(Parse,zErrMsg)) +#define PARSE_HDR_SZ (offsetof(Parse,aTempReg)-offsetof(Parse,zErrMsg)) /* Recursive part w/o aColCache*/ #define PARSE_RECURSE_SZ offsetof(Parse,sLastToken) /* Recursive part */ #define PARSE_TAIL_SZ (sizeof(Parse)-PARSE_RECURSE_SZ) /* Non-recursive part */ #define PARSE_TAIL(X) (((char*)(X))+PARSE_RECURSE_SZ) /* Pointer to tail */ @@ -18998,27 +19151,29 @@ struct AuthContext { #define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete/Insert: save cursor pos */ #define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */ #define OPFLAG_NOCHNG_MAGIC 0x6d /* OP_MakeRecord: serialtype 10 is ok */ +#define OPFLAG_PREFORMAT 0x80 /* OP_Insert uses preformatted cell */ /* - * Each trigger present in the database schema is stored as an instance of - * struct Trigger. - * - * Pointers to instances of struct Trigger are stored in two ways. - * 1. In the "trigHash" hash table (part of the sqlcipher_sqlite3* that represents the - * database). This allows Trigger structures to be retrieved by name. - * 2. All triggers associated with a single table form a linked list, using the - * pNext member of struct Trigger. A pointer to the first element of the - * linked list is stored as the "pTrigger" member of the associated - * struct Table. - * - * The "step_list" member points to the first element of a linked list - * containing the SQL statements specified as the trigger program. - */ +** Each trigger present in the database schema is stored as an instance of +** struct Trigger. +** +** Pointers to instances of struct Trigger are stored in two ways. +** 1. In the "trigHash" hash table (part of the sqlcipher_sqlite3* that represents the +** database). This allows Trigger structures to be retrieved by name. +** 2. All triggers associated with a single table form a linked list, using the +** pNext member of struct Trigger. A pointer to the first element of the +** linked list is stored as the "pTrigger" member of the associated +** struct Table. +** +** The "step_list" member points to the first element of a linked list +** containing the SQL statements specified as the trigger program. +*/ struct Trigger { char *zName; /* The name of the trigger */ char *table; /* The table or view to which the trigger applies */ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ + u8 bReturning; /* This trigger implements a RETURNING clause */ Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */ IdList *pColumns; /* If this is an UPDATE OF trigger, the is stored here */ @@ -19039,52 +19194,58 @@ struct Trigger { #define TRIGGER_AFTER 2 /* - * An instance of struct TriggerStep is used to store a single SQL statement - * that is a part of a trigger-program. - * - * Instances of struct TriggerStep are stored in a singly linked list (linked - * using the "pNext" member) referenced by the "step_list" member of the - * associated struct Trigger instance. The first element of the linked list is - * the first step of the trigger-program. - * - * The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or - * "SELECT" statement. The meanings of the other members is determined by the - * value of "op" as follows: - * - * (op == TK_INSERT) - * orconf -> stores the ON CONFLICT algorithm - * pSelect -> If this is an INSERT INTO ... SELECT ... statement, then - * this stores a pointer to the SELECT statement. Otherwise NULL. - * zTarget -> Dequoted name of the table to insert into. - * pExprList -> If this is an INSERT INTO ... VALUES ... statement, then - * this stores values to be inserted. Otherwise NULL. - * pIdList -> If this is an INSERT INTO ... () VALUES ... - * statement, then this stores the column-names to be - * inserted into. - * - * (op == TK_DELETE) - * zTarget -> Dequoted name of the table to delete from. - * pWhere -> The WHERE clause of the DELETE statement if one is specified. - * Otherwise NULL. - * - * (op == TK_UPDATE) - * zTarget -> Dequoted name of the table to update. - * pWhere -> The WHERE clause of the UPDATE statement if one is specified. - * Otherwise NULL. - * pExprList -> A list of the columns to update and the expressions to update - * them to. See sqlcipher_sqlite3Update() documentation of "pChanges" - * argument. - * - */ +** An instance of struct TriggerStep is used to store a single SQL statement +** that is a part of a trigger-program. +** +** Instances of struct TriggerStep are stored in a singly linked list (linked +** using the "pNext" member) referenced by the "step_list" member of the +** associated struct Trigger instance. The first element of the linked list is +** the first step of the trigger-program. +** +** The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or +** "SELECT" statement. The meanings of the other members is determined by the +** value of "op" as follows: +** +** (op == TK_INSERT) +** orconf -> stores the ON CONFLICT algorithm +** pSelect -> The content to be inserted - either a SELECT statement or +** a VALUES clause. +** zTarget -> Dequoted name of the table to insert into. +** pIdList -> If this is an INSERT INTO ... () VALUES ... +** statement, then this stores the column-names to be +** inserted into. +** pUpsert -> The ON CONFLICT clauses for an Upsert +** +** (op == TK_DELETE) +** zTarget -> Dequoted name of the table to delete from. +** pWhere -> The WHERE clause of the DELETE statement if one is specified. +** Otherwise NULL. +** +** (op == TK_UPDATE) +** zTarget -> Dequoted name of the table to update. +** pWhere -> The WHERE clause of the UPDATE statement if one is specified. +** Otherwise NULL. +** pExprList -> A list of the columns to update and the expressions to update +** them to. See sqlcipher_sqlite3Update() documentation of "pChanges" +** argument. +** +** (op == TK_SELECT) +** pSelect -> The SELECT statement +** +** (op == TK_RETURNING) +** pExprList -> The list of expressions that follow the RETURNING keyword. +** +*/ struct TriggerStep { - u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ + u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT, + ** or TK_RETURNING */ u8 orconf; /* OE_Rollback etc. */ Trigger *pTrig; /* The trigger that this step is a part of */ Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */ char *zTarget; /* Target table for DELETE, UPDATE, INSERT */ SrcList *pFrom; /* FROM clause for UPDATE statement (if any) */ Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ - ExprList *pExprList; /* SET clause for UPDATE */ + ExprList *pExprList; /* SET clause for UPDATE, or RETURNING clause */ IdList *pIdList; /* Column names for INSERT */ Upsert *pUpsert; /* Upsert clauses on an INSERT */ char *zSpan; /* Original SQL text of this command */ @@ -19093,18 +19254,16 @@ struct TriggerStep { }; /* -** The following structure contains information used by the sqliteFix... -** routines as they walk the parse tree to make database references -** explicit. +** Information about a RETURNING clause */ -typedef struct DbFixer DbFixer; -struct DbFixer { - Parse *pParse; /* The parsing context. Error messages written here */ - Schema *pSchema; /* Fix items to this schema */ - u8 bTemp; /* True for TEMP schema entries */ - const char *zDb; /* Make sure all objects are contained in this database */ - const char *zType; /* Type of the container - used for error messages */ - const Token *pName; /* Name of the container - used for error messages */ +struct Returning { + Parse *pParse; /* The parse that includes the RETURNING clause */ + ExprList *pReturnEL; /* List of expressions to return */ + Trigger retTrig; /* The transient trigger that implements RETURNING */ + TriggerStep retTStep; /* The trigger step */ + int iRetCur; /* Transient table holding RETURNING results */ + int nRetCol; /* Number of in pReturnEL after expansion */ + int iRetReg; /* Register array for holding a row of RETURNING */ }; /* @@ -19144,7 +19303,26 @@ typedef struct { /* ** Allowed values for mInitFlags */ -#define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */ +#define INITFLAG_AlterMask 0x0003 /* Types of ALTER */ +#define INITFLAG_AlterRename 0x0001 /* Reparse after a RENAME */ +#define INITFLAG_AlterDrop 0x0002 /* Reparse after a DROP COLUMN */ +#define INITFLAG_AlterAdd 0x0003 /* Reparse after an ADD COLUMN */ + +/* Tuning parameters are set using SQLITE_TESTCTRL_TUNE and are controlled +** on debug-builds of the CLI using ".testctrl tune ID VALUE". Tuning +** parameters are for temporary use during development, to help find +** optimial values for parameters in the query planner. The should not +** be used on trunk check-ins. They are a temporary mechanism available +** for transient development builds only. +** +** Tuning parameters are numbered starting with 1. +*/ +#define SQLITE_NTUNE 6 /* Should be zero for all trunk check-ins */ +#ifdef SQLITE_DEBUG +# define Tuning(X) (sqlcipher_sqlite3Config.aTune[(X)-1]) +#else +# define Tuning(X) 0 +#endif /* ** Structure containing global configuration data for the SQLite library. @@ -19200,16 +19378,21 @@ struct Sqlite3Config { void (*xVdbeBranch)(void*,unsigned iSrcLine,u8 eThis,u8 eMx); /* Callback */ void *pVdbeBranchArg; /* 1st argument */ #endif -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE sqlcipher_sqlite3_int64 mxMemdbSize; /* Default max memdb size */ #endif #ifndef SQLITE_UNTESTABLE int (*xTestCallback)(int); /* Invoked by sqlcipher_sqlite3FaultSim() */ #endif int bLocaltimeFault; /* True to fail localtime() calls */ + int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */ int iOnceResetThreshold; /* When to reset OP_Once counters */ u32 szSorterRef; /* Min size in bytes to use sorter-refs */ unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */ + /* vvvv--- must be last ---vvv */ +#ifdef SQLITE_DEBUG + sqlcipher_sqlite3_int64 aTune[SQLITE_NTUNE]; /* Tuning parameters */ +#endif }; /* @@ -19245,8 +19428,8 @@ struct Walker { int n; /* A counter */ int iCur; /* A cursor number */ SrcList *pSrcList; /* FROM clause */ - struct SrcCount *pSrcCount; /* Counting column references */ struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ + struct RefSrcList *pRefSrcList; /* sqlcipher_sqlite3ReferencesSrcList() */ int *aiCol; /* array of column indexes */ struct IdxCover *pIdxCover; /* Check for index coverage */ struct IdxExprTrans *pIdxTrans; /* Convert idxed expr to column */ @@ -19256,10 +19439,26 @@ struct Walker { struct WhereConst *pConst; /* WHERE clause constants */ struct RenameCtx *pRename; /* RENAME COLUMN context */ struct Table *pTab; /* Table of generated column */ - struct SrcList_item *pSrcItem; /* A single FROM clause item */ + SrcItem *pSrcItem; /* A single FROM clause item */ + DbFixer *pFix; } u; }; +/* +** The following structure contains information used by the sqliteFix... +** routines as they walk the parse tree to make database references +** explicit. +*/ +struct DbFixer { + Parse *pParse; /* The parsing context. Error messages written here */ + Walker w; /* Walker object */ + Schema *pSchema; /* Fix items to this schema */ + u8 bTemp; /* True for TEMP schema entries */ + const char *zDb; /* Make sure all objects are contained in this database */ + const char *zType; /* Type of the container - used for error messages */ + const Token *pName; /* Name of the container - used for error messages */ +}; + /* Forward declarations */ SQLITE_PRIVATE int sqlcipher_sqlite3WalkExpr(Walker*, Expr*); SQLITE_PRIVATE int sqlcipher_sqlite3WalkExprList(Walker*, ExprList*); @@ -19271,11 +19470,18 @@ SQLITE_PRIVATE int sqlcipher_sqlite3SelectWalkNoop(Walker*, Select*); SQLITE_PRIVATE int sqlcipher_sqlite3SelectWalkFail(Walker*, Select*); SQLITE_PRIVATE int sqlcipher_sqlite3WalkerDepthIncrease(Walker*,Select*); SQLITE_PRIVATE void sqlcipher_sqlite3WalkerDepthDecrease(Walker*,Select*); +SQLITE_PRIVATE void sqlcipher_sqlite3WalkWinDefnDummyCallback(Walker*,Select*); #ifdef SQLITE_DEBUG SQLITE_PRIVATE void sqlcipher_sqlite3SelectWalkAssert2(Walker*, Select*); #endif +#ifndef SQLITE_OMIT_CTE +SQLITE_PRIVATE void sqlcipher_sqlite3SelectPopWith(Walker*, Select*); +#else +# define sqlcipher_sqlite3SelectPopWith 0 +#endif + /* ** Return code from the parse-tree walking primitives and their ** callbacks. @@ -19285,20 +19491,56 @@ SQLITE_PRIVATE void sqlcipher_sqlite3SelectWalkAssert2(Walker*, Select*); #define WRC_Abort 2 /* Abandon the tree walk */ /* -** An instance of this structure represents a set of one or more CTEs -** (common table expressions) created by a single WITH clause. +** A single common table expression +*/ +struct Cte { + char *zName; /* Name of this CTE */ + ExprList *pCols; /* List of explicit column names, or NULL */ + Select *pSelect; /* The definition of this CTE */ + const char *zCteErr; /* Error message for circular references */ + CteUse *pUse; /* Usage information for this CTE */ + u8 eM10d; /* The MATERIALIZED flag */ +}; + +/* +** Allowed values for the materialized flag (eM10d): +*/ +#define M10d_Yes 0 /* AS MATERIALIZED */ +#define M10d_Any 1 /* Not specified. Query planner's choice */ +#define M10d_No 2 /* AS NOT MATERIALIZED */ + +/* +** An instance of the With object represents a WITH clause containing +** one or more CTEs (common table expressions). */ struct With { - int nCte; /* Number of CTEs in the WITH clause */ - With *pOuter; /* Containing WITH clause, or NULL */ - struct Cte { /* For each CTE in the WITH clause.... */ - char *zName; /* Name of this CTE */ - ExprList *pCols; /* List of explicit column names, or NULL */ - Select *pSelect; /* The definition of this CTE */ - const char *zCteErr; /* Error message for circular references */ - } a[1]; + int nCte; /* Number of CTEs in the WITH clause */ + int bView; /* Belongs to the outermost Select of a view */ + With *pOuter; /* Containing WITH clause, or NULL */ + Cte a[1]; /* For each CTE in the WITH clause.... */ +}; + +/* +** The Cte object is not guaranteed to persist for the entire duration +** of code generation. (The query flattener or other parser tree +** edits might delete it.) The following object records information +** about each Common Table Expression that must be preserved for the +** duration of the parse. +** +** The CteUse objects are freed using sqlcipher_sqlite3ParserAddCleanup() rather +** than sqlcipher_sqlite3SelectDelete(), which is what enables them to persist +** until the end of code generation. +*/ +struct CteUse { + int nUse; /* Number of users of this CTE */ + int addrM9e; /* Start of subroutine to compute materialization */ + int regRtn; /* Return address register for addrM9e subroutine */ + int iCur; /* Ephemeral table holding the materialization */ + LogEst nRowEst; /* Estimated number of rows in the table */ + u8 eM10d; /* The MATERIALIZED flag */ }; + #ifdef SQLITE_DEBUG /* ** An instance of the TreeView object is used for printing the content of @@ -19348,7 +19590,7 @@ struct Window { Window **ppThis; /* Pointer to this object in Select.pWin list */ Window *pNextWin; /* Next window function belonging to this SELECT */ Expr *pFilter; /* The FILTER expression */ - FuncDef *pFunc; /* The function */ + FuncDef *pWFunc; /* The function */ int iEphCsr; /* Partition buffer or Peer buffer */ int regAccum; /* Accumulator */ int regResult; /* Interim result */ @@ -19372,11 +19614,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WindowListDelete(sqlcipher_sqlite3 *db, Win SQLITE_PRIVATE Window *sqlcipher_sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); SQLITE_PRIVATE void sqlcipher_sqlite3WindowAttach(Parse*, Expr*, Window*); SQLITE_PRIVATE void sqlcipher_sqlite3WindowLink(Select *pSel, Window *pWin); -SQLITE_PRIVATE int sqlcipher_sqlite3WindowCompare(Parse*, Window*, Window*, int); +SQLITE_PRIVATE int sqlcipher_sqlite3WindowCompare(const Parse*, const Window*, const Window*, int); SQLITE_PRIVATE void sqlcipher_sqlite3WindowCodeInit(Parse*, Select*); SQLITE_PRIVATE void sqlcipher_sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int); SQLITE_PRIVATE int sqlcipher_sqlite3WindowRewrite(Parse*, Select*); -SQLITE_PRIVATE int sqlcipher_sqlite3ExpandSubquery(Parse*, struct SrcList_item*); SQLITE_PRIVATE void sqlcipher_sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*); SQLITE_PRIVATE Window *sqlcipher_sqlite3WindowDup(sqlcipher_sqlite3 *db, Expr *pOwner, Window *p); SQLITE_PRIVATE Window *sqlcipher_sqlite3WindowListDup(sqlcipher_sqlite3 *db, Window *p); @@ -19505,8 +19746,8 @@ SQLITE_PRIVATE void *sqlcipher_sqlite3DbReallocOrFree(sqlcipher_sqlite3 *, void SQLITE_PRIVATE void *sqlcipher_sqlite3DbRealloc(sqlcipher_sqlite3 *, void *, u64); SQLITE_PRIVATE void sqlcipher_sqlite3DbFree(sqlcipher_sqlite3*, void*); SQLITE_PRIVATE void sqlcipher_sqlite3DbFreeNN(sqlcipher_sqlite3*, void*); -SQLITE_PRIVATE int sqlcipher_sqlite3MallocSize(void*); -SQLITE_PRIVATE int sqlcipher_sqlite3DbMallocSize(sqlcipher_sqlite3*, void*); +SQLITE_PRIVATE int sqlcipher_sqlite3MallocSize(const void*); +SQLITE_PRIVATE int sqlcipher_sqlite3DbMallocSize(sqlcipher_sqlite3*, const void*); SQLITE_PRIVATE void *sqlcipher_sqlite3PageMalloc(int); SQLITE_PRIVATE void sqlcipher_sqlite3PageFree(void*); SQLITE_PRIVATE void sqlcipher_sqlite3MemSetDefault(void); @@ -19604,27 +19845,63 @@ SQLITE_PRIVATE void *sqlcipher_sqlite3TestTextToPtr(const char*); #endif #if defined(SQLITE_DEBUG) +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewLine(TreeView*, const char *zFormat, ...); SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewExpr(TreeView*, const Expr*, u8); SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*); SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewBareIdList(TreeView*, const IdList*, const char*); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewIdList(TreeView*, const IdList*, u8, const char*); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewColumnList(TreeView*, const Column*, int, u8); SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewSrcList(TreeView*, const SrcList*); SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewSelect(TreeView*, const Select*, u8); SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewWith(TreeView*, const With*, u8); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewUpsert(TreeView*, const Upsert*, u8); +#if TREETRACE_ENABLED +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewDelete(const With*, const SrcList*, const Expr*, + const ExprList*,const Expr*, const Trigger*); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewInsert(const With*, const SrcList*, + const IdList*, const Select*, const ExprList*, + int, const Upsert*, const Trigger*); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewUpdate(const With*, const SrcList*, const ExprList*, + const Expr*, int, const ExprList*, const Expr*, + const Upsert*, const Trigger*); +#endif +#ifndef SQLITE_OMIT_TRIGGER +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewTriggerStep(TreeView*, const TriggerStep*, u8, u8); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewTrigger(TreeView*, const Trigger*, u8, u8); +#endif #ifndef SQLITE_OMIT_WINDOWFUNC SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewWindow(TreeView*, const Window*, u8); SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewWinFunc(TreeView*, const Window*, u8); #endif +SQLITE_PRIVATE void sqlcipher_sqlite3ShowExpr(const Expr*); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowExprList(const ExprList*); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowIdList(const IdList*); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowSrcList(const SrcList*); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowSelect(const Select*); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowWith(const With*); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowUpsert(const Upsert*); +#ifndef SQLITE_OMIT_TRIGGER +SQLITE_PRIVATE void sqlcipher_sqlite3ShowTriggerStep(const TriggerStep*); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowTriggerStepList(const TriggerStep*); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowTrigger(const Trigger*); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowTriggerList(const Trigger*); +#endif +#ifndef SQLITE_OMIT_WINDOWFUNC +SQLITE_PRIVATE void sqlcipher_sqlite3ShowWindow(const Window*); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowWinFunc(const Window*); +#endif #endif - SQLITE_PRIVATE void sqlcipher_sqlite3SetString(char **, sqlcipher_sqlite3*, const char*); SQLITE_PRIVATE void sqlcipher_sqlite3ErrorMsg(Parse*, const char*, ...); SQLITE_PRIVATE int sqlcipher_sqlite3ErrorToParser(sqlcipher_sqlite3*,int); SQLITE_PRIVATE void sqlcipher_sqlite3Dequote(char*); SQLITE_PRIVATE void sqlcipher_sqlite3DequoteExpr(Expr*); +SQLITE_PRIVATE void sqlcipher_sqlite3DequoteToken(Token*); SQLITE_PRIVATE void sqlcipher_sqlite3TokenInit(Token*,char*); SQLITE_PRIVATE int sqlcipher_sqlite3KeywordCode(const unsigned char*, int); -SQLITE_PRIVATE int sqlcipher_sqlite3RunParser(Parse*, const char*, char **); +SQLITE_PRIVATE int sqlcipher_sqlite3RunParser(Parse*, const char*); SQLITE_PRIVATE void sqlcipher_sqlite3FinishCoding(Parse*); SQLITE_PRIVATE int sqlcipher_sqlite3GetTempReg(Parse*); SQLITE_PRIVATE void sqlcipher_sqlite3ReleaseTempReg(Parse*,int); @@ -19641,15 +19918,17 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3PExpr(Parse*, int, Expr*, Expr*); SQLITE_PRIVATE void sqlcipher_sqlite3PExprAddSelect(Parse*, Expr*, Select*); SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprAnd(Parse*,Expr*, Expr*); SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprSimplifiedAndOr(Expr*); -SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprFunction(Parse*,ExprList*, Token*, int); -SQLITE_PRIVATE void sqlcipher_sqlite3ExprFunctionUsable(Parse*,Expr*,FuncDef*); +SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); +SQLITE_PRIVATE void sqlcipher_sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); SQLITE_PRIVATE void sqlcipher_sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); SQLITE_PRIVATE void sqlcipher_sqlite3ExprDelete(sqlcipher_sqlite3*, Expr*); +SQLITE_PRIVATE void sqlcipher_sqlite3ExprDeferredDelete(Parse*, Expr*); SQLITE_PRIVATE void sqlcipher_sqlite3ExprUnmapAndDelete(Parse*, Expr*); SQLITE_PRIVATE ExprList *sqlcipher_sqlite3ExprListAppend(Parse*,ExprList*,Expr*); SQLITE_PRIVATE ExprList *sqlcipher_sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); +SQLITE_PRIVATE Select *sqlcipher_sqlite3ExprListToValues(Parse*, int, ExprList*); SQLITE_PRIVATE void sqlcipher_sqlite3ExprListSetSortOrder(ExprList*,int,int); -SQLITE_PRIVATE void sqlcipher_sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); +SQLITE_PRIVATE void sqlcipher_sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int); SQLITE_PRIVATE void sqlcipher_sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); SQLITE_PRIVATE void sqlcipher_sqlite3ExprListDelete(sqlcipher_sqlite3*, ExprList*); SQLITE_PRIVATE u32 sqlcipher_sqlite3ExprListFlags(const ExprList*); @@ -19665,7 +19944,12 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ResetAllSchemasOfConnection(sqlcipher_sqlit SQLITE_PRIVATE void sqlcipher_sqlite3ResetOneSchema(sqlcipher_sqlite3*,int); SQLITE_PRIVATE void sqlcipher_sqlite3CollapseDatabaseArray(sqlcipher_sqlite3*); SQLITE_PRIVATE void sqlcipher_sqlite3CommitInternalChanges(sqlcipher_sqlite3*); +SQLITE_PRIVATE void sqlcipher_sqlite3ColumnSetExpr(Parse*,Table*,Column*,Expr*); +SQLITE_PRIVATE Expr *sqlcipher_sqlite3ColumnExpr(Table*,Column*); +SQLITE_PRIVATE void sqlcipher_sqlite3ColumnSetColl(sqlcipher_sqlite3*,Column*,const char*zColl); +SQLITE_PRIVATE const char *sqlcipher_sqlite3ColumnColl(Column*); SQLITE_PRIVATE void sqlcipher_sqlite3DeleteColumnNames(sqlcipher_sqlite3*,Table*); +SQLITE_PRIVATE void sqlcipher_sqlite3GenerateColumnNames(Parse *pParse, Select *pSelect); SQLITE_PRIVATE int sqlcipher_sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); SQLITE_PRIVATE void sqlcipher_sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*,char); SQLITE_PRIVATE Table *sqlcipher_sqlite3ResultSetOfSelect(Parse*,Select*,char); @@ -19685,14 +19969,15 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ColumnPropertiesFromName(Table*, Column*) #else # define sqlcipher_sqlite3ColumnPropertiesFromName(T,C) /* no-op */ #endif -SQLITE_PRIVATE void sqlcipher_sqlite3AddColumn(Parse*,Token*,Token*); +SQLITE_PRIVATE void sqlcipher_sqlite3AddColumn(Parse*,Token,Token); SQLITE_PRIVATE void sqlcipher_sqlite3AddNotNull(Parse*, int); SQLITE_PRIVATE void sqlcipher_sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int); SQLITE_PRIVATE void sqlcipher_sqlite3AddCheckConstraint(Parse*, Expr*, const char*, const char*); SQLITE_PRIVATE void sqlcipher_sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*); SQLITE_PRIVATE void sqlcipher_sqlite3AddCollateType(Parse*, Token*); SQLITE_PRIVATE void sqlcipher_sqlite3AddGenerated(Parse*,Expr*,Token*); -SQLITE_PRIVATE void sqlcipher_sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); +SQLITE_PRIVATE void sqlcipher_sqlite3EndTable(Parse*,Token*,Token*,u32,Select*); +SQLITE_PRIVATE void sqlcipher_sqlite3AddReturning(Parse*,ExprList*); SQLITE_PRIVATE int sqlcipher_sqlite3ParseUri(const char*,const char*,unsigned int*, sqlcipher_sqlite3_vfs**,char**,char **); /* BEGIN SQLCIPHER */ @@ -19761,13 +20046,14 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListEnlarge(Parse*, SrcList*, int, i SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2); SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, - Token*, Select*, Expr*, IdList*); + Token*, Select*, OnOrUsing*); SQLITE_PRIVATE void sqlcipher_sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); SQLITE_PRIVATE void sqlcipher_sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*); -SQLITE_PRIVATE int sqlcipher_sqlite3IndexedByLookup(Parse *, struct SrcList_item *); -SQLITE_PRIVATE void sqlcipher_sqlite3SrcListShiftJoinType(SrcList*); +SQLITE_PRIVATE int sqlcipher_sqlite3IndexedByLookup(Parse *, SrcItem *); +SQLITE_PRIVATE void sqlcipher_sqlite3SrcListShiftJoinType(Parse*,SrcList*); SQLITE_PRIVATE void sqlcipher_sqlite3SrcListAssignCursors(Parse*, SrcList*); SQLITE_PRIVATE void sqlcipher_sqlite3IdListDelete(sqlcipher_sqlite3*, IdList*); +SQLITE_PRIVATE void sqlcipher_sqlite3ClearOnOrUsing(sqlcipher_sqlite3*, OnOrUsing*); SQLITE_PRIVATE void sqlcipher_sqlite3SrcListDelete(sqlcipher_sqlite3*, SrcList*); SQLITE_PRIVATE Index *sqlcipher_sqlite3AllocateIndexObject(sqlcipher_sqlite3*,i16,int,char**); SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, @@ -19783,15 +20069,18 @@ SQLITE_PRIVATE void sqlcipher_sqlite3OpenTable(Parse*, int iCur, int iDb, Table* #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) SQLITE_PRIVATE Expr *sqlcipher_sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*); #endif +SQLITE_PRIVATE void sqlcipher_sqlite3CodeChangeCount(Vdbe*,int,const char*); SQLITE_PRIVATE void sqlcipher_sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*); SQLITE_PRIVATE void sqlcipher_sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*, Upsert*); -SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int); +SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*, + ExprList*,Select*,u16,int); SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo*); SQLITE_PRIVATE LogEst sqlcipher_sqlite3WhereOutputRowCount(WhereInfo*); SQLITE_PRIVATE int sqlcipher_sqlite3WhereIsDistinct(WhereInfo*); SQLITE_PRIVATE int sqlcipher_sqlite3WhereIsOrdered(WhereInfo*); SQLITE_PRIVATE int sqlcipher_sqlite3WhereOrderByLimitOptLabel(WhereInfo*); +SQLITE_PRIVATE void sqlcipher_sqlite3WhereMinMaxOptEarlyOut(Vdbe*,WhereInfo*); SQLITE_PRIVATE int sqlcipher_sqlite3WhereIsSorted(WhereInfo*); SQLITE_PRIVATE int sqlcipher_sqlite3WhereContinueLabel(WhereInfo*); SQLITE_PRIVATE int sqlcipher_sqlite3WhereBreakLabel(WhereInfo*); @@ -19806,7 +20095,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int SQLITE_PRIVATE void sqlcipher_sqlite3ExprCodeMove(Parse*, int, int, int); SQLITE_PRIVATE void sqlcipher_sqlite3ExprCode(Parse*, Expr*, int); #ifndef SQLITE_OMIT_GENERATED_COLUMNS -SQLITE_PRIVATE void sqlcipher_sqlite3ExprCodeGeneratedColumn(Parse*, Column*, int); +SQLITE_PRIVATE void sqlcipher_sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int); #endif SQLITE_PRIVATE void sqlcipher_sqlite3ExprCodeCopy(Parse*, Expr*, int); SQLITE_PRIVATE void sqlcipher_sqlite3ExprCodeFactorable(Parse*, Expr*, int); @@ -19825,23 +20114,24 @@ SQLITE_PRIVATE Table *sqlcipher_sqlite3FindTable(sqlcipher_sqlite3*,const char*, #define LOCATE_VIEW 0x01 #define LOCATE_NOERR 0x02 SQLITE_PRIVATE Table *sqlcipher_sqlite3LocateTable(Parse*,u32 flags,const char*, const char*); -SQLITE_PRIVATE Table *sqlcipher_sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *); +SQLITE_PRIVATE const char *sqlcipher_sqlite3PreferredTableName(const char*); +SQLITE_PRIVATE Table *sqlcipher_sqlite3LocateTableItem(Parse*,u32 flags,SrcItem *); SQLITE_PRIVATE Index *sqlcipher_sqlite3FindIndex(sqlcipher_sqlite3*,const char*, const char*); SQLITE_PRIVATE void sqlcipher_sqlite3UnlinkAndDeleteTable(sqlcipher_sqlite3*,int,const char*); SQLITE_PRIVATE void sqlcipher_sqlite3UnlinkAndDeleteIndex(sqlcipher_sqlite3*,int,const char*); SQLITE_PRIVATE void sqlcipher_sqlite3Vacuum(Parse*,Token*,Expr*); SQLITE_PRIVATE int sqlcipher_sqlite3RunVacuum(char**, sqlcipher_sqlite3*, int, sqlcipher_sqlite3_value*); -SQLITE_PRIVATE char *sqlcipher_sqlite3NameFromToken(sqlcipher_sqlite3*, Token*); -SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompare(Parse*,Expr*, Expr*, int); -SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompareSkip(Expr*, Expr*, int); -SQLITE_PRIVATE int sqlcipher_sqlite3ExprListCompare(ExprList*, ExprList*, int); -SQLITE_PRIVATE int sqlcipher_sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int); +SQLITE_PRIVATE char *sqlcipher_sqlite3NameFromToken(sqlcipher_sqlite3*, const Token*); +SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompare(const Parse*,const Expr*,const Expr*, int); +SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompareSkip(Expr*,Expr*,int); +SQLITE_PRIVATE int sqlcipher_sqlite3ExprListCompare(const ExprList*,const ExprList*, int); +SQLITE_PRIVATE int sqlcipher_sqlite3ExprImpliesExpr(const Parse*,const Expr*,const Expr*, int); SQLITE_PRIVATE int sqlcipher_sqlite3ExprImpliesNonNullRow(Expr*,int); SQLITE_PRIVATE void sqlcipher_sqlite3AggInfoPersistWalkerInit(Walker*,Parse*); SQLITE_PRIVATE void sqlcipher_sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); SQLITE_PRIVATE void sqlcipher_sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); SQLITE_PRIVATE int sqlcipher_sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx); -SQLITE_PRIVATE int sqlcipher_sqlite3FunctionUsesThisSrc(Expr*, SrcList*); +SQLITE_PRIVATE int sqlcipher_sqlite3ReferencesSrcList(Parse*, Expr*, SrcList*); SQLITE_PRIVATE Vdbe *sqlcipher_sqlite3GetVdbe(Parse*); #ifndef SQLITE_UNTESTABLE SQLITE_PRIVATE void sqlcipher_sqlite3PrngSaveState(void); @@ -19863,10 +20153,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsConstantNotJoin(Expr*); SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsConstantOrFunction(Expr*, u8); SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsTableConstant(Expr*,int); +SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsTableConstraint(Expr*,const SrcItem*); #ifdef SQLITE_ENABLE_CURSOR_HINTS SQLITE_PRIVATE int sqlcipher_sqlite3ExprContainsSubquery(Expr*); #endif -SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsInteger(Expr*, int*); +SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsInteger(const Expr*, int*); SQLITE_PRIVATE int sqlcipher_sqlite3ExprCanBeNull(const Expr*); SQLITE_PRIVATE int sqlcipher_sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int sqlcipher_sqlite3IsRowid(const char*); @@ -19891,20 +20182,26 @@ SQLITE_PRIVATE void sqlcipher_sqlite3MayAbort(Parse*); SQLITE_PRIVATE void sqlcipher_sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8); SQLITE_PRIVATE void sqlcipher_sqlite3UniqueConstraint(Parse*, int, Index*); SQLITE_PRIVATE void sqlcipher_sqlite3RowidConstraint(Parse*, int, Table*); -SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprDup(sqlcipher_sqlite3*,Expr*,int); -SQLITE_PRIVATE ExprList *sqlcipher_sqlite3ExprListDup(sqlcipher_sqlite3*,ExprList*,int); -SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListDup(sqlcipher_sqlite3*,SrcList*,int); -SQLITE_PRIVATE IdList *sqlcipher_sqlite3IdListDup(sqlcipher_sqlite3*,IdList*); -SQLITE_PRIVATE Select *sqlcipher_sqlite3SelectDup(sqlcipher_sqlite3*,Select*,int); +SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprDup(sqlcipher_sqlite3*,const Expr*,int); +SQLITE_PRIVATE ExprList *sqlcipher_sqlite3ExprListDup(sqlcipher_sqlite3*,const ExprList*,int); +SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListDup(sqlcipher_sqlite3*,const SrcList*,int); +SQLITE_PRIVATE IdList *sqlcipher_sqlite3IdListDup(sqlcipher_sqlite3*,const IdList*); +SQLITE_PRIVATE Select *sqlcipher_sqlite3SelectDup(sqlcipher_sqlite3*,const Select*,int); SQLITE_PRIVATE FuncDef *sqlcipher_sqlite3FunctionSearch(int,const char*); SQLITE_PRIVATE void sqlcipher_sqlite3InsertBuiltinFuncs(FuncDef*,int); SQLITE_PRIVATE FuncDef *sqlcipher_sqlite3FindFunction(sqlcipher_sqlite3*,const char*,int,u8,u8); +SQLITE_PRIVATE void sqlcipher_sqlite3QuoteValue(StrAccum*,sqlcipher_sqlite3_value*); SQLITE_PRIVATE void sqlcipher_sqlite3RegisterBuiltinFunctions(void); SQLITE_PRIVATE void sqlcipher_sqlite3RegisterDateTimeFunctions(void); +SQLITE_PRIVATE void sqlcipher_sqlite3RegisterJsonFunctions(void); SQLITE_PRIVATE void sqlcipher_sqlite3RegisterPerConnectionBuiltinFunctions(sqlcipher_sqlite3*); +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) +SQLITE_PRIVATE int sqlcipher_sqlite3JsonTableFunctions(sqlcipher_sqlite3*); +#endif SQLITE_PRIVATE int sqlcipher_sqlite3SafetyCheckOk(sqlcipher_sqlite3*); SQLITE_PRIVATE int sqlcipher_sqlite3SafetyCheckSickOrOk(sqlcipher_sqlite3*); SQLITE_PRIVATE void sqlcipher_sqlite3ChangeCookie(Parse*, int); +SQLITE_PRIVATE With *sqlcipher_sqlite3WithDup(sqlcipher_sqlite3 *db, With *p); #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) SQLITE_PRIVATE void sqlcipher_sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int); @@ -19953,7 +20250,9 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3TriggerStepSrc(Parse*, TriggerStep*); #endif SQLITE_PRIVATE int sqlcipher_sqlite3JoinType(Parse*, Token*, Token*, Token*); -SQLITE_PRIVATE void sqlcipher_sqlite3SetJoinExpr(Expr*,int); +SQLITE_PRIVATE int sqlcipher_sqlite3ColumnIndex(Table *pTab, const char *zCol); +SQLITE_PRIVATE void sqlcipher_sqlite3SrcItemColumnUsed(SrcItem*,int); +SQLITE_PRIVATE void sqlcipher_sqlite3SetJoinExpr(Expr*,int,u32); SQLITE_PRIVATE void sqlcipher_sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int); SQLITE_PRIVATE void sqlcipher_sqlite3DeferForeignKey(Parse*, int); #ifndef SQLITE_OMIT_AUTHORIZATION @@ -19975,7 +20274,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FixInit(DbFixer*, Parse*, int, const char*, SQLITE_PRIVATE int sqlcipher_sqlite3FixSrcList(DbFixer*, SrcList*); SQLITE_PRIVATE int sqlcipher_sqlite3FixSelect(DbFixer*, Select*); SQLITE_PRIVATE int sqlcipher_sqlite3FixExpr(DbFixer*, Expr*); -SQLITE_PRIVATE int sqlcipher_sqlite3FixExprList(DbFixer*, ExprList*); SQLITE_PRIVATE int sqlcipher_sqlite3FixTriggerStep(DbFixer*, TriggerStep*); SQLITE_PRIVATE int sqlcipher_sqlite3RealSameAsInt(double,sqlcipher_sqlite3_int64); SQLITE_PRIVATE void sqlcipher_sqlite3Int64ToText(i64,char*); @@ -19990,14 +20288,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE u32 sqlcipher_sqlite3Utf8Read(const u8**); SQLITE_PRIVATE LogEst sqlcipher_sqlite3LogEst(u64); SQLITE_PRIVATE LogEst sqlcipher_sqlite3LogEstAdd(LogEst,LogEst); -#ifndef SQLITE_OMIT_VIRTUALTABLE SQLITE_PRIVATE LogEst sqlcipher_sqlite3LogEstFromDouble(double); -#endif -#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ - defined(SQLITE_ENABLE_STAT4) || \ - defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) SQLITE_PRIVATE u64 sqlcipher_sqlite3LogEstToInt(LogEst); -#endif SQLITE_PRIVATE VList *sqlcipher_sqlite3VListAdd(sqlcipher_sqlite3*,VList*,const char*,int,int); SQLITE_PRIVATE const char *sqlcipher_sqlite3VListNumToName(VList*,int); SQLITE_PRIVATE int sqlcipher_sqlite3VListNameToNum(VList*,const char*,int); @@ -20032,12 +20324,13 @@ SQLITE_PRIVATE const char *sqlcipher_sqlite3IndexAffinityStr(sqlcipher_sqlite3*, SQLITE_PRIVATE void sqlcipher_sqlite3TableAffinity(Vdbe*, Table*, int); SQLITE_PRIVATE char sqlcipher_sqlite3CompareAffinity(const Expr *pExpr, char aff2); SQLITE_PRIVATE int sqlcipher_sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity); -SQLITE_PRIVATE char sqlcipher_sqlite3TableColumnAffinity(Table*,int); +SQLITE_PRIVATE char sqlcipher_sqlite3TableColumnAffinity(const Table*,int); SQLITE_PRIVATE char sqlcipher_sqlite3ExprAffinity(const Expr *pExpr); SQLITE_PRIVATE int sqlcipher_sqlite3Atoi64(const char*, i64*, int, u8); SQLITE_PRIVATE int sqlcipher_sqlite3DecOrHexToI64(const char*, i64*); SQLITE_PRIVATE void sqlcipher_sqlite3ErrorWithMsg(sqlcipher_sqlite3*, int, const char*,...); SQLITE_PRIVATE void sqlcipher_sqlite3Error(sqlcipher_sqlite3*,int); +SQLITE_PRIVATE void sqlcipher_sqlite3ErrorClear(sqlcipher_sqlite3*); SQLITE_PRIVATE void sqlcipher_sqlite3SystemError(sqlcipher_sqlite3*,int); SQLITE_PRIVATE void *sqlcipher_sqlite3HexToBlob(sqlcipher_sqlite3*, const char *z, int n); SQLITE_PRIVATE u8 sqlcipher_sqlite3HexToInt(int h); @@ -20047,7 +20340,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3TwoPartName(Parse *, Token *, Token *, Token SQLITE_PRIVATE const char *sqlcipher_sqlite3ErrName(int); #endif -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE SQLITE_PRIVATE int sqlcipher_sqlite3MemdbInit(void); #endif @@ -20060,14 +20353,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3SetTextEncoding(sqlcipher_sqlite3 *db, u8); SQLITE_PRIVATE CollSeq *sqlcipher_sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr); SQLITE_PRIVATE CollSeq *sqlcipher_sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr); SQLITE_PRIVATE int sqlcipher_sqlite3ExprCollSeqMatch(Parse*,const Expr*,const Expr*); -SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int); -SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprAddCollateString(Parse*,Expr*,const char*); +SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprAddCollateToken(const Parse *pParse, Expr*, const Token*, int); +SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprAddCollateString(const Parse*,Expr*,const char*); SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprSkipCollate(Expr*); SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprSkipCollateAndLikely(Expr*); SQLITE_PRIVATE int sqlcipher_sqlite3CheckCollSeq(Parse *, CollSeq *); SQLITE_PRIVATE int sqlcipher_sqlite3WritableSchema(sqlcipher_sqlite3*); SQLITE_PRIVATE int sqlcipher_sqlite3CheckObjectName(Parse*, const char*,const char*,const char*); -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetChanges(sqlcipher_sqlite3 *, int); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetChanges(sqlcipher_sqlite3 *, i64); SQLITE_PRIVATE int sqlcipher_sqlite3AddInt64(i64*,i64); SQLITE_PRIVATE int sqlcipher_sqlite3SubInt64(i64*,i64); SQLITE_PRIVATE int sqlcipher_sqlite3MulInt64(i64*,i64); @@ -20092,16 +20385,22 @@ SQLITE_PRIVATE sqlcipher_sqlite3_value *sqlcipher_sqlite3ValueNew(sqlcipher_sqli #ifndef SQLITE_OMIT_UTF16 SQLITE_PRIVATE char *sqlcipher_sqlite3Utf16to8(sqlcipher_sqlite3 *, const void*, int, u8); #endif -SQLITE_PRIVATE int sqlcipher_sqlite3ValueFromExpr(sqlcipher_sqlite3 *, Expr *, u8, u8, sqlcipher_sqlite3_value **); +SQLITE_PRIVATE int sqlcipher_sqlite3ValueFromExpr(sqlcipher_sqlite3 *, const Expr *, u8, u8, sqlcipher_sqlite3_value **); SQLITE_PRIVATE void sqlcipher_sqlite3ValueApplyAffinity(sqlcipher_sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION SQLITE_PRIVATE const unsigned char sqlcipher_sqlite3OpcodeProperty[]; SQLITE_PRIVATE const char sqlcipher_sqlite3StrBINARY[]; +SQLITE_PRIVATE const unsigned char sqlcipher_sqlite3StdTypeLen[]; +SQLITE_PRIVATE const char sqlcipher_sqlite3StdTypeAffinity[]; +SQLITE_PRIVATE const char sqlcipher_sqlite3StdTypeMap[]; +SQLITE_PRIVATE const char *sqlcipher_sqlite3StdType[]; SQLITE_PRIVATE const unsigned char sqlcipher_sqlite3UpperToLower[]; +SQLITE_PRIVATE const unsigned char *sqlcipher_sqlite3aLTb; +SQLITE_PRIVATE const unsigned char *sqlcipher_sqlite3aEQb; +SQLITE_PRIVATE const unsigned char *sqlcipher_sqlite3aGTb; SQLITE_PRIVATE const unsigned char sqlcipher_sqlite3CtypeMap[]; SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlcipher_sqlite3Config; SQLITE_PRIVATE FuncDefHash sqlcipher_sqlite3BuiltinFunctions; -SQLITE_API extern u32 sqlcipher_sqlite3_unsupported_selecttrace; #ifndef SQLITE_OMIT_WSD SQLITE_PRIVATE int sqlcipher_sqlite3PendingByte; #endif @@ -20120,6 +20419,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExpirePreparedStatements(sqlcipher_sqlite3* SQLITE_PRIVATE void sqlcipher_sqlite3CodeRhsOfIN(Parse*, Expr*, int); SQLITE_PRIVATE int sqlcipher_sqlite3CodeSubselect(Parse*, Expr*); SQLITE_PRIVATE void sqlcipher_sqlite3SelectPrep(Parse*, Select*, NameContext*); +SQLITE_PRIVATE int sqlcipher_sqlite3ExpandSubquery(Parse*, SrcItem*); SQLITE_PRIVATE void sqlcipher_sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p); SQLITE_PRIVATE int sqlcipher_sqlite3MatchEName( const struct ExprList_item*, @@ -20137,8 +20437,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprLis SQLITE_PRIVATE void sqlcipher_sqlite3ColumnDefault(Vdbe *, Table *, int, int); SQLITE_PRIVATE void sqlcipher_sqlite3AlterFinishAddColumn(Parse *, Token *); SQLITE_PRIVATE void sqlcipher_sqlite3AlterBeginAddColumn(Parse *, SrcList *); -SQLITE_PRIVATE void *sqlcipher_sqlite3RenameTokenMap(Parse*, void*, Token*); -SQLITE_PRIVATE void sqlcipher_sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom); +SQLITE_PRIVATE void sqlcipher_sqlite3AlterDropColumn(Parse*, SrcList*, const Token*); +SQLITE_PRIVATE const void *sqlcipher_sqlite3RenameTokenMap(Parse*, const void*, const Token*); +SQLITE_PRIVATE void sqlcipher_sqlite3RenameTokenRemap(Parse*, const void *pTo, const void *pFrom); SQLITE_PRIVATE void sqlcipher_sqlite3RenameExprUnmap(Parse*, Expr*); SQLITE_PRIVATE void sqlcipher_sqlite3RenameExprlistUnmap(Parse*, ExprList*); SQLITE_PRIVATE CollSeq *sqlcipher_sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); @@ -20160,6 +20461,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3KeyInfoUnref(KeyInfo*); SQLITE_PRIVATE KeyInfo *sqlcipher_sqlite3KeyInfoRef(KeyInfo*); SQLITE_PRIVATE KeyInfo *sqlcipher_sqlite3KeyInfoOfIndex(Parse*, Index*); SQLITE_PRIVATE KeyInfo *sqlcipher_sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int); +SQLITE_PRIVATE const char *sqlcipher_sqlite3SelectOpName(int); SQLITE_PRIVATE int sqlcipher_sqlite3HasExplicitNulls(Parse*, ExprList*); #ifdef SQLITE_DEBUG @@ -20174,15 +20476,20 @@ SQLITE_PRIVATE int sqlcipher_sqlite3CreateFunc(sqlcipher_sqlite3 *, const char * FuncDestructor *pDestructor ); SQLITE_PRIVATE void sqlcipher_sqlite3NoopDestructor(void*); -SQLITE_PRIVATE void sqlcipher_sqlite3OomFault(sqlcipher_sqlite3*); +SQLITE_PRIVATE void *sqlcipher_sqlite3OomFault(sqlcipher_sqlite3*); SQLITE_PRIVATE void sqlcipher_sqlite3OomClear(sqlcipher_sqlite3*); SQLITE_PRIVATE int sqlcipher_sqlite3ApiExit(sqlcipher_sqlite3 *db, int); SQLITE_PRIVATE int sqlcipher_sqlite3OpenTempDatabase(Parse *); SQLITE_PRIVATE void sqlcipher_sqlite3StrAccumInit(StrAccum*, sqlcipher_sqlite3*, char*, int, int); +SQLITE_PRIVATE int sqlcipher_sqlite3StrAccumEnlarge(StrAccum*, int); SQLITE_PRIVATE char *sqlcipher_sqlite3StrAccumFinish(StrAccum*); +SQLITE_PRIVATE void sqlcipher_sqlite3StrAccumSetError(StrAccum*, u8); +SQLITE_PRIVATE void sqlcipher_sqlite3ResultStrAccum(sqlcipher_sqlite3_context*,StrAccum*); SQLITE_PRIVATE void sqlcipher_sqlite3SelectDestInit(SelectDest*,int,int); SQLITE_PRIVATE Expr *sqlcipher_sqlite3CreateColumnExpr(sqlcipher_sqlite3 *, SrcList *, int, int); +SQLITE_PRIVATE void sqlcipher_sqlite3RecordErrorByteOffset(sqlcipher_sqlite3*,const char*); +SQLITE_PRIVATE void sqlcipher_sqlite3RecordErrorOffsetOfExpr(sqlcipher_sqlite3*,const Expr*); SQLITE_PRIVATE void sqlcipher_sqlite3BackupRestart(sqlcipher_sqlite3_backup *); SQLITE_PRIVATE void sqlcipher_sqlite3BackupUpdate(sqlcipher_sqlite3_backup *, Pgno, const u8 *); @@ -20233,7 +20540,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Utf8To8(unsigned char*); #endif #ifdef SQLITE_OMIT_VIRTUALTABLE -# define sqlcipher_sqlite3VtabClear(Y) +# define sqlcipher_sqlite3VtabClear(D,T) # define sqlcipher_sqlite3VtabSync(X,Y) SQLITE_OK # define sqlcipher_sqlite3VtabRollback(X) # define sqlcipher_sqlite3VtabCommit(X) @@ -20270,9 +20577,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ReadOnlyShadowTables(sqlcipher_sqlite3 *db); #ifndef SQLITE_OMIT_VIRTUALTABLE SQLITE_PRIVATE int sqlcipher_sqlite3ShadowTableName(sqlcipher_sqlite3 *db, const char *zName); SQLITE_PRIVATE int sqlcipher_sqlite3IsShadowTableOf(sqlcipher_sqlite3*,Table*,const char*); +SQLITE_PRIVATE void sqlcipher_sqlite3MarkAllShadowTablesOf(sqlcipher_sqlite3*, Table*); #else # define sqlcipher_sqlite3ShadowTableName(A,B) 0 # define sqlcipher_sqlite3IsShadowTableOf(A,B,C) 0 +# define sqlcipher_sqlite3MarkAllShadowTablesOf(A,B) #endif SQLITE_PRIVATE int sqlcipher_sqlite3VtabEponymousTableInit(Parse*,Module*); SQLITE_PRIVATE void sqlcipher_sqlite3VtabEponymousTableClear(sqlcipher_sqlite3*,Module*); @@ -20285,11 +20594,18 @@ SQLITE_PRIVATE int sqlcipher_sqlite3VtabCallCreate(sqlcipher_sqlite3*, int, cons SQLITE_PRIVATE int sqlcipher_sqlite3VtabCallConnect(Parse*, Table*); SQLITE_PRIVATE int sqlcipher_sqlite3VtabCallDestroy(sqlcipher_sqlite3*, int, const char *); SQLITE_PRIVATE int sqlcipher_sqlite3VtabBegin(sqlcipher_sqlite3 *, VTable *); + SQLITE_PRIVATE FuncDef *sqlcipher_sqlite3VtabOverloadFunction(sqlcipher_sqlite3 *,FuncDef*, int nArg, Expr*); +#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \ + && !defined(SQLITE_OMIT_VIRTUALTABLE) +SQLITE_PRIVATE void sqlcipher_sqlite3VtabUsesAllSchemas(sqlcipher_sqlite3_index_info*); +#endif SQLITE_PRIVATE sqlcipher_sqlite3_int64 sqlcipher_sqlite3StmtCurrentTime(sqlcipher_sqlite3_context*); SQLITE_PRIVATE int sqlcipher_sqlite3VdbeParameterIndex(Vdbe*, const char*, int); SQLITE_PRIVATE int sqlcipher_sqlite3TransferBindings(sqlcipher_sqlite3_stmt *, sqlcipher_sqlite3_stmt *); -SQLITE_PRIVATE void sqlcipher_sqlite3ParserReset(Parse*); +SQLITE_PRIVATE void sqlcipher_sqlite3ParseObjectInit(Parse*,sqlcipher_sqlite3*); +SQLITE_PRIVATE void sqlcipher_sqlite3ParseObjectReset(Parse*); +SQLITE_PRIVATE void *sqlcipher_sqlite3ParserAddCleanup(Parse*,void(*)(sqlcipher_sqlite3*,void*),void*); #ifdef SQLITE_ENABLE_NORMALIZE SQLITE_PRIVATE char *sqlcipher_sqlite3Normalize(Vdbe*, const char*); #endif @@ -20304,23 +20620,32 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Checkpoint(sqlcipher_sqlite3*, int, int, i SQLITE_PRIVATE int sqlcipher_sqlite3WalDefaultHook(void*,sqlcipher_sqlite3*,const char*,int); #endif #ifndef SQLITE_OMIT_CTE -SQLITE_PRIVATE With *sqlcipher_sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*); +SQLITE_PRIVATE Cte *sqlcipher_sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); +SQLITE_PRIVATE void sqlcipher_sqlite3CteDelete(sqlcipher_sqlite3*,Cte*); +SQLITE_PRIVATE With *sqlcipher_sqlite3WithAdd(Parse*,With*,Cte*); SQLITE_PRIVATE void sqlcipher_sqlite3WithDelete(sqlcipher_sqlite3*,With*); -SQLITE_PRIVATE void sqlcipher_sqlite3WithPush(Parse*, With*, u8); +SQLITE_PRIVATE With *sqlcipher_sqlite3WithPush(Parse*, With*, u8); #else -#define sqlcipher_sqlite3WithPush(x,y,z) -#define sqlcipher_sqlite3WithDelete(x,y) +# define sqlcipher_sqlite3CteNew(P,T,E,S) ((void*)0) +# define sqlcipher_sqlite3CteDelete(D,C) +# define sqlcipher_sqlite3CteWithAdd(P,W,C) ((void*)0) +# define sqlcipher_sqlite3WithDelete(x,y) +# define sqlcipher_sqlite3WithPush(x,y,z) ((void*)0) #endif #ifndef SQLITE_OMIT_UPSERT -SQLITE_PRIVATE Upsert *sqlcipher_sqlite3UpsertNew(sqlcipher_sqlite3*,ExprList*,Expr*,ExprList*,Expr*); +SQLITE_PRIVATE Upsert *sqlcipher_sqlite3UpsertNew(sqlcipher_sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*); SQLITE_PRIVATE void sqlcipher_sqlite3UpsertDelete(sqlcipher_sqlite3*,Upsert*); SQLITE_PRIVATE Upsert *sqlcipher_sqlite3UpsertDup(sqlcipher_sqlite3*,Upsert*); SQLITE_PRIVATE int sqlcipher_sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*); SQLITE_PRIVATE void sqlcipher_sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int); +SQLITE_PRIVATE Upsert *sqlcipher_sqlite3UpsertOfIndex(Upsert*,Index*); +SQLITE_PRIVATE int sqlcipher_sqlite3UpsertNextIsIPK(Upsert*); #else -#define sqlcipher_sqlite3UpsertNew(v,w,x,y,z) ((Upsert*)0) +#define sqlcipher_sqlite3UpsertNew(u,v,w,x,y,z) ((Upsert*)0) #define sqlcipher_sqlite3UpsertDelete(x,y) -#define sqlcipher_sqlite3UpsertDup(x,y) ((Upsert*)0) +#define sqlcipher_sqlite3UpsertDup(x,y) ((Upsert*)0) +#define sqlcipher_sqlite3UpsertOfIndex(x,y) ((Upsert*)0) +#define sqlcipher_sqlite3UpsertNextIsIPK(x) 0 #endif @@ -20338,6 +20663,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FkActions(Parse*, Table*, ExprList*, int, SQLITE_PRIVATE int sqlcipher_sqlite3FkRequired(Parse*, Table*, int*, int); SQLITE_PRIVATE u32 sqlcipher_sqlite3FkOldmask(Parse*, Table*); SQLITE_PRIVATE FKey *sqlcipher_sqlite3FkReferences(Table *); +SQLITE_PRIVATE void sqlcipher_sqlite3FkClearTriggerCache(sqlcipher_sqlite3*,int); #else #define sqlcipher_sqlite3FkActions(a,b,c,d,e,f) #define sqlcipher_sqlite3FkCheck(a,b,c,d,e,f) @@ -20345,6 +20671,7 @@ SQLITE_PRIVATE FKey *sqlcipher_sqlite3FkReferences(Table *); #define sqlcipher_sqlite3FkOldmask(a,b) 0 #define sqlcipher_sqlite3FkRequired(a,b,c,d) 0 #define sqlcipher_sqlite3FkReferences(a) 0 + #define sqlcipher_sqlite3FkClearTriggerCache(a,b) #endif #ifndef SQLITE_OMIT_FOREIGN_KEY SQLITE_PRIVATE void sqlcipher_sqlite3FkDelete(sqlcipher_sqlite3 *, Table*); @@ -20402,7 +20729,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3MemJournalOpen(sqlcipher_sqlite3_file *); SQLITE_PRIVATE void sqlcipher_sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p); #if SQLITE_MAX_EXPR_DEPTH>0 -SQLITE_PRIVATE int sqlcipher_sqlite3SelectExprHeight(Select *); +SQLITE_PRIVATE int sqlcipher_sqlite3SelectExprHeight(const Select *); SQLITE_PRIVATE int sqlcipher_sqlite3ExprCheckHeight(Parse*, int); #else #define sqlcipher_sqlite3SelectExprHeight(x) 0 @@ -20473,8 +20800,8 @@ SQLITE_API SQLITE_EXTERN void (SQLITE_CDECL *sqlcipher_sqlite3IoTrace)(const cha */ #ifdef SQLITE_MEMDEBUG SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugSetType(void*,u8); -SQLITE_PRIVATE int sqlcipher_sqlite3MemdebugHasType(void*,u8); -SQLITE_PRIVATE int sqlcipher_sqlite3MemdebugNoType(void*,u8); +SQLITE_PRIVATE int sqlcipher_sqlite3MemdebugHasType(const void*,u8); +SQLITE_PRIVATE int sqlcipher_sqlite3MemdebugNoType(const void*,u8); #else # define sqlcipher_sqlite3MemdebugSetType(X,Y) /* no-op */ # define sqlcipher_sqlite3MemdebugHasType(X,Y) 1 @@ -20499,10 +20826,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3DbpageRegister(sqlcipher_sqlite3*); SQLITE_PRIVATE int sqlcipher_sqlite3DbstatRegister(sqlcipher_sqlite3*); #endif -SQLITE_PRIVATE int sqlcipher_sqlite3ExprVectorSize(Expr *pExpr); -SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsVector(Expr *pExpr); +SQLITE_PRIVATE int sqlcipher_sqlite3ExprVectorSize(const Expr *pExpr); +SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsVector(const Expr *pExpr); SQLITE_PRIVATE Expr *sqlcipher_sqlite3VectorFieldSubexpr(Expr*, int); -SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprForVectorField(Parse*,Expr*,int); +SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprForVectorField(Parse*,Expr*,int,int); SQLITE_PRIVATE void sqlcipher_sqlite3VectorErrorMsg(Parse*, Expr*); #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS @@ -20512,191 +20839,52 @@ SQLITE_PRIVATE const char **sqlcipher_sqlite3CompileOptions(int *pnOpt); #endif /* SQLITEINT_H */ /************** End of sqliteInt.h *******************************************/ -/************** Begin file crypto.c ******************************************/ +/************** Begin file os_common.h ***************************************/ /* -** SQLCipher -** http://sqlcipher.net +** 2004 May 22 ** -** Copyright (c) 2008 - 2013, ZETETIC LLC -** All rights reserved. +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of the ZETETIC LLC nor the -** names of its contributors may be used to endorse or promote products -** derived from this software without specific prior written permission. +** 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 SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY -** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY -** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************** +** +** This file contains macros and a little bit of code that is common to +** all of the platform-specific files (os_*.c) and is #included into those +** files. ** +** This file should be #included by the os_*.c files only. It is not a +** general purpose header file. */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC +#ifndef _OS_COMMON_H_ +#define _OS_COMMON_H_ -/* #include */ -/************** Include sqlcipher.h in the middle of crypto.c ****************/ -/************** Begin file sqlcipher.h ***************************************/ /* -** SQLCipher -** sqlcipher.h developed by Stephen Lombardo (Zetetic LLC) -** sjlombardo at zetetic dot net -** http://zetetic.net -** -** Copyright (c) 2008, ZETETIC LLC -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of the ZETETIC LLC nor the -** names of its contributors may be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY -** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY -** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** +** At least two bugs have slipped in because we changed the MEMORY_DEBUG +** macro to SQLITE_DEBUG and some older makefiles have not yet made the +** switch. The following code should catch this problem at compile-time. */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC -#ifndef SQLCIPHER_H -#define SQLCIPHER_H - -#define SQLCIPHER_HMAC_SHA1 0 -#define SQLCIPHER_HMAC_SHA1_LABEL "HMAC_SHA1" -#define SQLCIPHER_HMAC_SHA256 1 -#define SQLCIPHER_HMAC_SHA256_LABEL "HMAC_SHA256" -#define SQLCIPHER_HMAC_SHA512 2 -#define SQLCIPHER_HMAC_SHA512_LABEL "HMAC_SHA512" - - -#define SQLCIPHER_PBKDF2_HMAC_SHA1 0 -#define SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL "PBKDF2_HMAC_SHA1" -#define SQLCIPHER_PBKDF2_HMAC_SHA256 1 -#define SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL "PBKDF2_HMAC_SHA256" -#define SQLCIPHER_PBKDF2_HMAC_SHA512 2 -#define SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL "PBKDF2_HMAC_SHA512" - - -typedef struct { - int (*activate)(void *ctx); - int (*deactivate)(void *ctx); - const char* (*get_provider_name)(void *ctx); - int (*add_random)(void *ctx, void *buffer, int length); - int (*random)(void *ctx, void *buffer, int length); - int (*hmac)(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out); - int (*kdf)(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key); - int (*cipher)(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out); - const char* (*get_cipher)(void *ctx); - int (*get_key_sz)(void *ctx); - int (*get_iv_sz)(void *ctx); - int (*get_block_sz)(void *ctx); - int (*get_hmac_sz)(void *ctx, int algorithm); - int (*ctx_init)(void **ctx); - int (*ctx_free)(void **ctx); - int (*fips_status)(void *ctx); - const char* (*get_provider_version)(void *ctx); -} sqlcipher_provider; - -/* utility functions */ -void* sqlcipher_malloc(u64); -void sqlcipher_mlock(void *, u64); -void sqlcipher_munlock(void *, u64); -void* sqlcipher_memset(void *, unsigned char, u64); -int sqlcipher_ismemset(const void *, unsigned char, u64); -int sqlcipher_memcmp(const void *, const void *, int); -void sqlcipher_free(void *, u64); -char* sqlcipher_version(); - -/* provider interfaces */ -int sqlcipher_register_provider(sqlcipher_provider *); -sqlcipher_provider* sqlcipher_get_provider(void); - -#define SQLCIPHER_MUTEX_PROVIDER 0 -#define SQLCIPHER_MUTEX_PROVIDER_ACTIVATE 1 -#define SQLCIPHER_MUTEX_PROVIDER_RAND 2 -#define SQLCIPHER_MUTEX_RESERVED1 3 -#define SQLCIPHER_MUTEX_RESERVED2 4 -#define SQLCIPHER_MUTEX_RESERVED3 5 -#define SQLCIPHER_MUTEX_COUNT 6 - -sqlcipher_sqlite3_mutex* sqlcipher_mutex(int); - -#endif +#ifdef MEMORY_DEBUG +# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." #endif -/* END SQLCIPHER */ - -/************** End of sqlcipher.h *******************************************/ -/************** Continuing where we left off in crypto.c *********************/ -/************** Include crypto.h in the middle of crypto.c *******************/ -/************** Begin file crypto.h ******************************************/ /* -** SQLCipher -** crypto.h developed by Stephen Lombardo (Zetetic LLC) -** sjlombardo at zetetic dot net -** http://zetetic.net -** -** Copyright (c) 2008, ZETETIC LLC -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of the ZETETIC LLC nor the -** names of its contributors may be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY -** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY -** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** +** Macros for performance tracing. Normally turned off. Only works +** on i486 hardware. */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC -#ifndef CRYPTO_H -#define CRYPTO_H +#ifdef SQLITE_PERFORMANCE_TRACE -/* #include "sqliteInt.h" */ -/************** Include btreeInt.h in the middle of crypto.h *****************/ -/************** Begin file btreeInt.h ****************************************/ /* -** 2004 April 6 +** hwtime.h contains inline assembler code for implementing +** high-performance timing routines. +*/ +/************** Include hwtime.h in the middle of os_common.h ****************/ +/************** Begin file hwtime.h ******************************************/ +/* +** 2008 May 27 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -20705,4909 +20893,5250 @@ sqlcipher_sqlite3_mutex* sqlcipher_mutex(int); ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** -************************************************************************* -** This file implements an external (disk-based) database using BTrees. -** For a detailed discussion of BTrees, refer to -** -** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: -** "Sorting And Searching", pages 473-480. Addison-Wesley -** Publishing Company, Reading, Massachusetts. -** -** The basic idea is that each page of the file contains N database -** entries and N+1 pointers to subpages. -** -** ---------------------------------------------------------------- -** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) | -** ---------------------------------------------------------------- -** -** All of the keys on the page that Ptr(0) points to have values less -** than Key(0). All of the keys on page Ptr(1) and its subpages have -** values greater than Key(0) and less than Key(1). All of the keys -** on Ptr(N) and its subpages have values greater than Key(N-1). And -** so forth. -** -** Finding a particular key requires reading O(log(M)) pages from the -** disk where M is the number of entries in the tree. -** -** In this implementation, a single file can hold one or more separate -** BTrees. Each BTree is identified by the index of its root page. The -** key and data for any entry are combined to form the "payload". A -** fixed amount of payload can be carried directly on the database -** page. If the payload is larger than the preset amount then surplus -** bytes are stored on overflow pages. The payload for an entry -** and the preceding pointer are combined to form a "Cell". Each -** page has a small header which contains the Ptr(N) pointer and other -** information such as the size of key and data. -** -** FORMAT DETAILS -** -** The file is divided into pages. The first page is called page 1, -** the second is page 2, and so forth. A page number of zero indicates -** "no such page". The page size can be any power of 2 between 512 and 65536. -** Each page can be either a btree page, a freelist page, an overflow -** page, or a pointer-map page. -** -** The first page is always a btree page. The first 100 bytes of the first -** page contain a special header (the "file header") that describes the file. -** The format of the file header is as follows: -** -** OFFSET SIZE DESCRIPTION -** 0 16 Header string: "SQLite format 3\000" -** 16 2 Page size in bytes. (1 means 65536) -** 18 1 File format write version -** 19 1 File format read version -** 20 1 Bytes of unused space at the end of each page -** 21 1 Max embedded payload fraction (must be 64) -** 22 1 Min embedded payload fraction (must be 32) -** 23 1 Min leaf payload fraction (must be 32) -** 24 4 File change counter -** 28 4 Reserved for future use -** 32 4 First freelist page -** 36 4 Number of freelist pages in the file -** 40 60 15 4-byte meta values passed to higher layers -** -** 40 4 Schema cookie -** 44 4 File format of schema layer -** 48 4 Size of page cache -** 52 4 Largest root-page (auto/incr_vacuum) -** 56 4 1=UTF-8 2=UTF16le 3=UTF16be -** 60 4 User version -** 64 4 Incremental vacuum mode -** 68 4 Application-ID -** 72 20 unused -** 92 4 The version-valid-for number -** 96 4 SQLITE_VERSION_NUMBER -** -** All of the integer values are big-endian (most significant byte first). -** -** The file change counter is incremented when the database is changed -** This counter allows other processes to know when the file has changed -** and thus when they need to flush their cache. -** -** The max embedded payload fraction is the amount of the total usable -** space in a page that can be consumed by a single cell for standard -** B-tree (non-LEAFDATA) tables. A value of 255 means 100%. The default -** is to limit the maximum cell size so that at least 4 cells will fit -** on one page. Thus the default max embedded payload fraction is 64. -** -** If the payload for a cell is larger than the max payload, then extra -** payload is spilled to overflow pages. Once an overflow page is allocated, -** as many bytes as possible are moved into the overflow pages without letting -** the cell size drop below the min embedded payload fraction. -** -** The min leaf payload fraction is like the min embedded payload fraction -** except that it applies to leaf nodes in a LEAFDATA tree. The maximum -** payload fraction for a LEAFDATA tree is always 100% (or 255) and it -** not specified in the header. -** -** Each btree pages is divided into three sections: The header, the -** cell pointer array, and the cell content area. Page 1 also has a 100-byte -** file header that occurs before the page header. -** -** |----------------| -** | file header | 100 bytes. Page 1 only. -** |----------------| -** | page header | 8 bytes for leaves. 12 bytes for interior nodes -** |----------------| -** | cell pointer | | 2 bytes per cell. Sorted order. -** | array | | Grows downward -** | | v -** |----------------| -** | unallocated | -** | space | -** |----------------| ^ Grows upwards -** | cell content | | Arbitrary order interspersed with freeblocks. -** | area | | and free space fragments. -** |----------------| -** -** The page headers looks like this: -** -** OFFSET SIZE DESCRIPTION -** 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf -** 1 2 byte offset to the first freeblock -** 3 2 number of cells on this page -** 5 2 first byte of the cell content area -** 7 1 number of fragmented free bytes -** 8 4 Right child (the Ptr(N) value). Omitted on leaves. -** -** The flags define the format of this btree page. The leaf flag means that -** this page has no children. The zerodata flag means that this page carries -** only keys and no data. The intkey flag means that the key is an integer -** which is stored in the key size entry of the cell header rather than in -** the payload area. -** -** The cell pointer array begins on the first byte after the page header. -** The cell pointer array contains zero or more 2-byte numbers which are -** offsets from the beginning of the page to the cell content in the cell -** content area. The cell pointers occur in sorted order. The system strives -** to keep free space after the last cell pointer so that new cells can -** be easily added without having to defragment the page. -** -** Cell content is stored at the very end of the page and grows toward the -** beginning of the page. -** -** Unused space within the cell content area is collected into a linked list of -** freeblocks. Each freeblock is at least 4 bytes in size. The byte offset -** to the first freeblock is given in the header. Freeblocks occur in -** increasing order. Because a freeblock must be at least 4 bytes in size, -** any group of 3 or fewer unused bytes in the cell content area cannot -** exist on the freeblock chain. A group of 3 or fewer free bytes is called -** a fragment. The total number of bytes in all fragments is recorded. -** in the page header at offset 7. -** -** SIZE DESCRIPTION -** 2 Byte offset of the next freeblock -** 2 Bytes in this freeblock -** -** Cells are of variable length. Cells are stored in the cell content area at -** the end of the page. Pointers to the cells are in the cell pointer array -** that immediately follows the page header. Cells is not necessarily -** contiguous or in order, but cell pointers are contiguous and in order. -** -** Cell content makes use of variable length integers. A variable -** length integer is 1 to 9 bytes where the lower 7 bits of each -** byte are used. The integer consists of all bytes that have bit 8 set and -** the first byte with bit 8 clear. The most significant byte of the integer -** appears first. A variable-length integer may not be more than 9 bytes long. -** As a special case, all 8 bytes of the 9th byte are used as data. This -** allows a 64-bit integer to be encoded in 9 bytes. -** -** 0x00 becomes 0x00000000 -** 0x7f becomes 0x0000007f -** 0x81 0x00 becomes 0x00000080 -** 0x82 0x00 becomes 0x00000100 -** 0x80 0x7f becomes 0x0000007f -** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678 -** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081 -** -** Variable length integers are used for rowids and to hold the number of -** bytes of key and data in a btree cell. -** -** The content of a cell looks like this: -** -** SIZE DESCRIPTION -** 4 Page number of the left child. Omitted if leaf flag is set. -** var Number of bytes of data. Omitted if the zerodata flag is set. -** var Number of bytes of key. Or the key itself if intkey flag is set. -** * Payload -** 4 First page of the overflow chain. Omitted if no overflow -** -** Overflow pages form a linked list. Each page except the last is completely -** filled with data (pagesize - 4 bytes). The last page can have as little -** as 1 byte of data. -** -** SIZE DESCRIPTION -** 4 Page number of next overflow page -** * Data -** -** Freelist pages come in two subtypes: trunk pages and leaf pages. The -** file header points to the first in a linked list of trunk page. Each trunk -** page points to multiple leaf pages. The content of a leaf page is -** unspecified. A trunk page looks like this: +****************************************************************************** ** -** SIZE DESCRIPTION -** 4 Page number of next trunk page -** 4 Number of leaf pointers on this page -** * zero or more pages numbers of leaves +** This file contains inline asm code for retrieving "high-performance" +** counters for x86 and x86_64 class CPUs. */ -/* #include "sqliteInt.h" */ - +#ifndef SQLITE_HWTIME_H +#define SQLITE_HWTIME_H -/* The following value is the maximum cell size assuming a maximum page -** size give above. +/* +** The following routine only works on pentium-class (or newer) processors. +** It uses the RDTSC opcode to read the cycle count value out of the +** processor and returns that value. This can be used for high-res +** profiling. */ -#define MX_CELL_SIZE(pBt) ((int)(pBt->pageSize-8)) +#if !defined(__STRICT_ANSI__) && \ + (defined(__GNUC__) || defined(_MSC_VER)) && \ + (defined(i386) || defined(__i386__) || defined(_M_IX86)) -/* The maximum number of cells on a single page of the database. This -** assumes a minimum cell size of 6 bytes (4 bytes for the cell itself -** plus 2 bytes for the index to the cell in the page header). Such -** small cells will be rare, but they are possible. -*/ -#define MX_CELL(pBt) ((pBt->pageSize-8)/6) + #if defined(__GNUC__) -/* Forward declarations */ -typedef struct MemPage MemPage; -typedef struct BtLock BtLock; -typedef struct CellInfo CellInfo; + __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ + unsigned int lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return (sqlite_uint64)hi << 32 | lo; + } -/* -** This is a magic string that appears at the beginning of every -** SQLite database in order to identify the file as a real database. -** -** You can change this value at compile-time by specifying a -** -DSQLITE_FILE_HEADER="..." on the compiler command-line. The -** header must be exactly 16 bytes including the zero-terminator so -** the string itself should be 15 characters long. If you change -** the header, then your custom library will not be able to read -** databases generated by the standard tools and the standard tools -** will not be able to read databases created by your custom library. -*/ -#ifndef SQLITE_FILE_HEADER /* 123456789 123456 */ -# define SQLITE_FILE_HEADER "SQLite format 3" -#endif + #elif defined(_MSC_VER) -/* -** Page type flags. An ORed combination of these flags appear as the -** first byte of on-disk image of every BTree page. -*/ -#define PTF_INTKEY 0x01 -#define PTF_ZERODATA 0x02 -#define PTF_LEAFDATA 0x04 -#define PTF_LEAF 0x08 + __declspec(naked) __inline sqlite_uint64 __cdecl sqlcipher_sqlite3Hwtime(void){ + __asm { + rdtsc + ret ; return value at EDX:EAX + } + } -/* -** An instance of this object stores information about each a single database -** page that has been loaded into memory. The information in this object -** is derived from the raw on-disk page content. -** -** As each database page is loaded into memory, the pager allocats an -** instance of this object and zeros the first 8 bytes. (This is the -** "extra" information associated with each page of the pager.) -** -** Access to all fields of this structure is controlled by the mutex -** stored in MemPage.pBt->mutex. -*/ -struct MemPage { - u8 isInit; /* True if previously initialized. MUST BE FIRST! */ - u8 bBusy; /* Prevent endless loops on corrupt database files */ - u8 intKey; /* True if table b-trees. False for index b-trees */ - u8 intKeyLeaf; /* True if the leaf of an intKey table */ - Pgno pgno; /* Page number for this page */ - /* Only the first 8 bytes (above) are zeroed by pager.c when a new page - ** is allocated. All fields that follow must be initialized before use */ - u8 leaf; /* True if a leaf page */ - u8 hdrOffset; /* 100 for page 1. 0 otherwise */ - u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ - u8 max1bytePayload; /* min(maxLocal,127) */ - u8 nOverflow; /* Number of overflow cell bodies in aCell[] */ - u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */ - u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */ - u16 cellOffset; /* Index in aData of first cell pointer */ - int nFree; /* Number of free bytes on the page. -1 for unknown */ - u16 nCell; /* Number of cells on this page, local and ovfl */ - u16 maskPage; /* Mask for page offset */ - u16 aiOvfl[4]; /* Insert the i-th overflow cell before the aiOvfl-th - ** non-overflow cell */ - u8 *apOvfl[4]; /* Pointers to the body of overflow cells */ - BtShared *pBt; /* Pointer to BtShared that this page is part of */ - u8 *aData; /* Pointer to disk image of the page data */ - u8 *aDataEnd; /* One byte past the end of usable data */ - u8 *aCellIdx; /* The cell index area */ - u8 *aDataOfst; /* Same as aData for leaves. aData+4 for interior */ - DbPage *pDbPage; /* Pager page handle */ - u16 (*xCellSize)(MemPage*,u8*); /* cellSizePtr method */ - void (*xParseCell)(MemPage*,u8*,CellInfo*); /* btreeParseCell method */ -}; + #endif -/* -** A linked list of the following structures is stored at BtShared.pLock. -** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor -** is opened on the table with root page BtShared.iTable. Locks are removed -** from this list when a transaction is committed or rolled back, or when -** a btree handle is closed. -*/ -struct BtLock { - Btree *pBtree; /* Btree handle holding this lock */ - Pgno iTable; /* Root page of table */ - u8 eLock; /* READ_LOCK or WRITE_LOCK */ - BtLock *pNext; /* Next in BtShared.pLock list */ -}; +#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) -/* Candidate values for BtLock.eLock */ -#define READ_LOCK 1 -#define WRITE_LOCK 2 + __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ + unsigned long val; + __asm__ __volatile__ ("rdtsc" : "=A" (val)); + return val; + } -/* A Btree handle -** -** A database connection contains a pointer to an instance of -** this object for every database file that it has open. This structure -** is opaque to the database connection. The database connection cannot -** see the internals of this structure and only deals with pointers to -** this structure. -** -** For some database files, the same underlying database cache might be -** shared between multiple connections. In that case, each connection -** has it own instance of this object. But each instance of this object -** points to the same BtShared object. The database cache and the -** schema associated with the database file are all contained within -** the BtShared object. -** -** All fields in this structure are accessed under sqlcipher_sqlite3.mutex. -** The pBt pointer itself may not be changed while there exists cursors -** in the referenced BtShared that point back to this Btree since those -** cursors have to go through this Btree to find their BtShared and -** they often do so without holding sqlcipher_sqlite3.mutex. -*/ -struct Btree { - sqlcipher_sqlite3 *db; /* The database connection holding this btree */ - BtShared *pBt; /* Sharable content of this btree */ - u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */ - u8 sharable; /* True if we can share pBt with another db */ - u8 locked; /* True if db currently has pBt locked */ - u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */ - int wantToLock; /* Number of nested calls to sqlcipher_sqlite3BtreeEnter() */ - int nBackup; /* Number of backup operations reading this btree */ - u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */ - Btree *pNext; /* List of other sharable Btrees from the same db */ - Btree *pPrev; /* Back pointer of the same list */ -#ifdef SQLITE_DEBUG - u64 nSeek; /* Calls to sqlcipher_sqlite3BtreeMovetoUnpacked() */ -#endif -#ifndef SQLITE_OMIT_SHARED_CACHE - BtLock lock; /* Object used to lock page 1 */ -#endif -}; +#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) -/* -** Btree.inTrans may take one of the following values. -** -** If the shared-data extension is enabled, there may be multiple users -** of the Btree structure. At most one of these may open a write transaction, -** but any number may have active read transactions. -** -** These values must match SQLITE_TXN_NONE, SQLITE_TXN_READ, and -** SQLITE_TXN_WRITE -*/ -#define TRANS_NONE 0 -#define TRANS_READ 1 -#define TRANS_WRITE 2 + __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ + unsigned long long retval; + unsigned long junk; + __asm__ __volatile__ ("\n\ + 1: mftbu %1\n\ + mftb %L0\n\ + mftbu %0\n\ + cmpw %0,%1\n\ + bne 1b" + : "=r" (retval), "=r" (junk)); + return retval; + } -#if TRANS_NONE!=SQLITE_TXN_NONE -# error wrong numeric code for no-transaction -#endif -#if TRANS_READ!=SQLITE_TXN_READ -# error wrong numeric code for read-transaction -#endif -#if TRANS_WRITE!=SQLITE_TXN_WRITE -# error wrong numeric code for write-transaction -#endif +#else + /* + ** asm() is needed for hardware timing support. Without asm(), + ** disable the sqlcipher_sqlite3Hwtime() routine. + ** + ** sqlcipher_sqlite3Hwtime() is only used for some obscure debugging + ** and analysis configurations, not in any deliverable, so this + ** should not be a great loss. + */ +SQLITE_PRIVATE sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ return ((sqlite_uint64)0); } -/* -** An instance of this object represents a single database file. -** -** A single database file can be in use at the same time by two -** or more database connections. When two or more connections are -** sharing the same database file, each connection has it own -** private Btree object for the file and each of those Btrees points -** to this one BtShared object. BtShared.nRef is the number of -** connections currently sharing this database file. -** -** Fields in this structure are accessed under the BtShared.mutex -** mutex, except for nRef and pNext which are accessed under the -** global SQLITE_MUTEX_STATIC_MAIN mutex. The pPager field -** may not be modified once it is initially set as long as nRef>0. -** The pSchema field may be set once under BtShared.mutex and -** thereafter is unchanged as long as nRef>0. -** -** isPending: -** -** If a BtShared client fails to obtain a write-lock on a database -** table (because there exists one or more read-locks on the table), -** the shared-cache enters 'pending-lock' state and isPending is -** set to true. -** -** The shared-cache leaves the 'pending lock' state when either of -** the following occur: -** -** 1) The current writer (BtShared.pWriter) concludes its transaction, OR -** 2) The number of locks held by other connections drops to zero. -** -** while in the 'pending-lock' state, no connection may start a new -** transaction. -** -** This feature is included to help prevent writer-starvation. -*/ -struct BtShared { - Pager *pPager; /* The page cache */ - sqlcipher_sqlite3 *db; /* Database connection currently using this Btree */ - BtCursor *pCursor; /* A list of all open cursors */ - MemPage *pPage1; /* First page of the database */ - u8 openFlags; /* Flags to sqlcipher_sqlite3BtreeOpen() */ -#ifndef SQLITE_OMIT_AUTOVACUUM - u8 autoVacuum; /* True if auto-vacuum is enabled */ - u8 incrVacuum; /* True if incr-vacuum is enabled */ - u8 bDoTruncate; /* True to truncate db on commit */ -#endif - u8 inTransaction; /* Transaction state */ - u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ - u8 nReserveWanted; /* Desired number of extra bytes per page */ - u16 btsFlags; /* Boolean parameters. See BTS_* macros below */ - u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ - u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ - u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ - u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ - u32 pageSize; /* Total number of bytes on a page */ - u32 usableSize; /* Number of usable bytes on each page */ - int nTransaction; /* Number of open transactions (read + write) */ - u32 nPage; /* Number of pages in the database */ - void *pSchema; /* Pointer to space allocated by sqlcipher_sqlite3BtreeSchema() */ - void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ - sqlcipher_sqlite3_mutex *mutex; /* Non-recursive mutex required to access this object */ - Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ -#ifndef SQLITE_OMIT_SHARED_CACHE - int nRef; /* Number of references to this structure */ - BtShared *pNext; /* Next on a list of sharable BtShared structs */ - BtLock *pLock; /* List of locks held on this shared-btree struct */ - Btree *pWriter; /* Btree with currently open write transaction */ #endif - u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */ -}; - -/* -** Allowed values for BtShared.btsFlags -*/ -#define BTS_READ_ONLY 0x0001 /* Underlying file is readonly */ -#define BTS_PAGESIZE_FIXED 0x0002 /* Page size can no longer be changed */ -#define BTS_SECURE_DELETE 0x0004 /* PRAGMA secure_delete is enabled */ -#define BTS_OVERWRITE 0x0008 /* Overwrite deleted content with zeros */ -#define BTS_FAST_SECURE 0x000c /* Combination of the previous two */ -#define BTS_INITIALLY_EMPTY 0x0010 /* Database was empty at trans start */ -#define BTS_NO_WAL 0x0020 /* Do not open write-ahead-log files */ -#define BTS_EXCLUSIVE 0x0040 /* pWriter has an exclusive lock */ -#define BTS_PENDING 0x0080 /* Waiting for read-locks to clear */ -/* -** An instance of the following structure is used to hold information -** about a cell. The parseCellPtr() function fills in this structure -** based on information extract from the raw disk page. -*/ -struct CellInfo { - i64 nKey; /* The key for INTKEY tables, or nPayload otherwise */ - u8 *pPayload; /* Pointer to the start of payload */ - u32 nPayload; /* Bytes of payload */ - u16 nLocal; /* Amount of payload held locally, not on overflow */ - u16 nSize; /* Size of the cell content on the main b-tree page */ -}; +#endif /* !defined(SQLITE_HWTIME_H) */ -/* -** Maximum depth of an SQLite B-Tree structure. Any B-Tree deeper than -** this will be declared corrupt. This value is calculated based on a -** maximum database size of 2^31 pages a minimum fanout of 2 for a -** root-node and 3 for all other internal nodes. -** -** If a tree that appears to be taller than this is encountered, it is -** assumed that the database is corrupt. -*/ -#define BTCURSOR_MAX_DEPTH 20 +/************** End of hwtime.h **********************************************/ +/************** Continuing where we left off in os_common.h ******************/ -/* -** A cursor is a pointer to a particular entry within a particular -** b-tree within a database file. -** -** The entry is identified by its MemPage and the index in -** MemPage.aCell[] of the entry. -** -** A single database file can be shared by two more database connections, -** but cursors cannot be shared. Each cursor is associated with a -** particular database connection identified BtCursor.pBtree.db. -** -** Fields in this structure are accessed under the BtShared.mutex -** found at self->pBt->mutex. -** -** skipNext meaning: -** The meaning of skipNext depends on the value of eState: -** -** eState Meaning of skipNext -** VALID skipNext is meaningless and is ignored -** INVALID skipNext is meaningless and is ignored -** SKIPNEXT sqlcipher_sqlite3BtreeNext() is a no-op if skipNext>0 and -** sqlcipher_sqlite3BtreePrevious() is no-op if skipNext<0. -** REQUIRESEEK restoreCursorPosition() restores the cursor to -** eState=SKIPNEXT if skipNext!=0 -** FAULT skipNext holds the cursor fault error code. -*/ -struct BtCursor { - u8 eState; /* One of the CURSOR_XXX constants (see below) */ - u8 curFlags; /* zero or more BTCF_* flags defined below */ - u8 curPagerFlags; /* Flags to send to sqlcipher_sqlite3PagerGet() */ - u8 hints; /* As configured by CursorSetHints() */ - int skipNext; /* Prev() is noop if negative. Next() is noop if positive. - ** Error code if eState==CURSOR_FAULT */ - Btree *pBtree; /* The Btree to which this cursor belongs */ - Pgno *aOverflow; /* Cache of overflow page locations */ - void *pKey; /* Saved key that was cursor last known position */ - /* All fields above are zeroed when the cursor is allocated. See - ** sqlcipher_sqlite3BtreeCursorZero(). Fields that follow must be manually - ** initialized. */ -#define BTCURSOR_FIRST_UNINIT pBt /* Name of first uninitialized field */ - BtShared *pBt; /* The BtShared this cursor points to */ - BtCursor *pNext; /* Forms a linked list of all cursors */ - CellInfo info; /* A parse of the cell we are pointing at */ - i64 nKey; /* Size of pKey, or last integer key */ - Pgno pgnoRoot; /* The root page of this tree */ - i8 iPage; /* Index of current page in apPage */ - u8 curIntKey; /* Value of apPage[0]->intKey */ - u16 ix; /* Current index for apPage[iPage] */ - u16 aiIdx[BTCURSOR_MAX_DEPTH-1]; /* Current index in apPage[i] */ - struct KeyInfo *pKeyInfo; /* Arg passed to comparison function */ - MemPage *pPage; /* Current page */ - MemPage *apPage[BTCURSOR_MAX_DEPTH-1]; /* Stack of parents of current page */ -}; +static sqlite_uint64 g_start; +static sqlite_uint64 g_elapsed; +#define TIMER_START g_start=sqlcipher_sqlite3Hwtime() +#define TIMER_END g_elapsed=sqlcipher_sqlite3Hwtime()-g_start +#define TIMER_ELAPSED g_elapsed +#else +#define TIMER_START +#define TIMER_END +#define TIMER_ELAPSED ((sqlite_uint64)0) +#endif /* -** Legal values for BtCursor.curFlags +** If we compile with the SQLITE_TEST macro set, then the following block +** of code will give us the ability to simulate a disk I/O error. This +** is used for testing the I/O recovery logic. */ -#define BTCF_WriteFlag 0x01 /* True if a write cursor */ -#define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ -#define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */ -#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */ -#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */ -#define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */ -#define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */ +#if defined(SQLITE_TEST) +SQLITE_API extern int sqlcipher_sqlite3_io_error_hit; +SQLITE_API extern int sqlcipher_sqlite3_io_error_hardhit; +SQLITE_API extern int sqlcipher_sqlite3_io_error_pending; +SQLITE_API extern int sqlcipher_sqlite3_io_error_persist; +SQLITE_API extern int sqlcipher_sqlite3_io_error_benign; +SQLITE_API extern int sqlcipher_sqlite3_diskfull_pending; +SQLITE_API extern int sqlcipher_sqlite3_diskfull; +#define SimulateIOErrorBenign(X) sqlcipher_sqlite3_io_error_benign=(X) +#define SimulateIOError(CODE) \ + if( (sqlcipher_sqlite3_io_error_persist && sqlcipher_sqlite3_io_error_hit) \ + || sqlcipher_sqlite3_io_error_pending-- == 1 ) \ + { local_ioerr(); CODE; } +static void local_ioerr(){ + IOTRACE(("IOERR\n")); + sqlcipher_sqlite3_io_error_hit++; + if( !sqlcipher_sqlite3_io_error_benign ) sqlcipher_sqlite3_io_error_hardhit++; +} +#define SimulateDiskfullError(CODE) \ + if( sqlcipher_sqlite3_diskfull_pending ){ \ + if( sqlcipher_sqlite3_diskfull_pending == 1 ){ \ + local_ioerr(); \ + sqlcipher_sqlite3_diskfull = 1; \ + sqlcipher_sqlite3_io_error_hit = 1; \ + CODE; \ + }else{ \ + sqlcipher_sqlite3_diskfull_pending--; \ + } \ + } +#else +#define SimulateIOErrorBenign(X) +#define SimulateIOError(A) +#define SimulateDiskfullError(A) +#endif /* defined(SQLITE_TEST) */ /* -** Potential values for BtCursor.eState. -** -** CURSOR_INVALID: -** Cursor does not point to a valid entry. This can happen (for example) -** because the table is empty or because BtreeCursorFirst() has not been -** called. -** -** CURSOR_VALID: -** Cursor points to a valid entry. getPayload() etc. may be called. -** -** CURSOR_SKIPNEXT: -** Cursor is valid except that the Cursor.skipNext field is non-zero -** indicating that the next sqlcipher_sqlite3BtreeNext() or sqlcipher_sqlite3BtreePrevious() -** operation should be a no-op. -** -** CURSOR_REQUIRESEEK: -** The table that this cursor was opened on still exists, but has been -** modified since the cursor was last used. The cursor position is saved -** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in -** this state, restoreCursorPosition() can be called to attempt to -** seek the cursor to the saved position. -** -** CURSOR_FAULT: -** An unrecoverable error (an I/O error or a malloc failure) has occurred -** on a different connection that shares the BtShared cache with this -** cursor. The error has left the cache in an inconsistent state. -** Do nothing else with this cursor. Any attempt to use the cursor -** should return the error code stored in BtCursor.skipNext +** When testing, keep a count of the number of open files. */ -#define CURSOR_VALID 0 -#define CURSOR_INVALID 1 -#define CURSOR_SKIPNEXT 2 -#define CURSOR_REQUIRESEEK 3 -#define CURSOR_FAULT 4 +#if defined(SQLITE_TEST) +SQLITE_API extern int sqlcipher_sqlite3_open_file_count; +#define OpenCounter(X) sqlcipher_sqlite3_open_file_count+=(X) +#else +#define OpenCounter(X) +#endif /* defined(SQLITE_TEST) */ -/* -** The database page the PENDING_BYTE occupies. This page is never used. -*/ -# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt) +#endif /* !defined(_OS_COMMON_H_) */ -/* -** These macros define the location of the pointer-map entry for a -** database page. The first argument to each is the number of usable -** bytes on each page of the database (often 1024). The second is the -** page number to look up in the pointer map. -** -** PTRMAP_PAGENO returns the database page number of the pointer-map -** page that stores the required pointer. PTRMAP_PTROFFSET returns -** the offset of the requested map entry. +/************** End of os_common.h *******************************************/ +/************** Begin file ctime.c *******************************************/ +/* DO NOT EDIT! +** This file is automatically generated by the script in the canonical +** SQLite source tree at tool/mkctimec.tcl. ** -** If the pgno argument passed to PTRMAP_PAGENO is a pointer-map page, -** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be -** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements -** this test. +** To modify this header, edit any of the various lists in that script +** which specify categories of generated conditionals in this file. */ -#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno) -#define PTRMAP_PTROFFSET(pgptrmap, pgno) (5*(pgno-pgptrmap-1)) -#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno)) /* -** The pointer map is a lookup table that identifies the parent page for -** each child page in the database file. The parent page is the page that -** contains a pointer to the child. Every page in the database contains -** 0 or 1 parent pages. (In this context 'database page' refers -** to any page that is not part of the pointer map itself.) Each pointer map -** entry consists of a single byte 'type' and a 4 byte parent page number. -** The PTRMAP_XXX identifiers below are the valid types. -** -** The purpose of the pointer map is to facility moving pages from one -** position in the file to another as part of autovacuum. When a page -** is moved, the pointer in its parent must be updated to point to the -** new location. The pointer map is used to locate the parent page quickly. -** -** PTRMAP_ROOTPAGE: The database page is a root-page. The page-number is not -** used in this case. +** 2010 February 23 ** -** PTRMAP_FREEPAGE: The database page is an unused (free) page. The page-number -** is not used in this case. +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** PTRMAP_OVERFLOW1: The database page is the first page in a list of -** overflow pages. The page number identifies the page that -** contains the cell with a pointer to this overflow page. +** 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. ** -** PTRMAP_OVERFLOW2: The database page is the second or later page in a list of -** overflow pages. The page-number identifies the previous -** page in the overflow page list. +************************************************************************* ** -** PTRMAP_BTREE: The database page is a non-root btree page. The page number -** identifies the parent page in the btree. -*/ -#define PTRMAP_ROOTPAGE 1 -#define PTRMAP_FREEPAGE 2 -#define PTRMAP_OVERFLOW1 3 -#define PTRMAP_OVERFLOW2 4 -#define PTRMAP_BTREE 5 - -/* A bunch of assert() statements to check the transaction state variables -** of handle p (type Btree*) are internally consistent. +** This file implements routines used to report what compile-time options +** SQLite was built with. */ -#define btreeIntegrity(p) \ - assert( p->pBt->inTransaction!=TRANS_NONE || p->pBt->nTransaction==0 ); \ - assert( p->pBt->inTransaction>=p->inTrans ); - +#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* IMP: R-16824-07538 */ /* -** The ISAUTOVACUUM macro is used within balance_nonroot() to determine -** if the database supports auto-vacuum or not. Because it is used -** within an expression that is an argument to another macro -** (sqliteMallocRaw), it is not possible to use conditional compilation. -** So, this macro is defined instead. +** Include the configuration header output by 'configure' if we're using the +** autoconf-based build */ -#ifndef SQLITE_OMIT_AUTOVACUUM -#define ISAUTOVACUUM (pBt->autoVacuum) -#else -#define ISAUTOVACUUM 0 +#if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) +/* #include "config.h" */ +#define SQLITECONFIG_H 1 #endif +/* These macros are provided to "stringify" the value of the define +** for those options in which the value is meaningful. */ +#define CTIMEOPT_VAL_(opt) #opt +#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) + +/* Like CTIMEOPT_VAL, but especially for SQLITE_DEFAULT_LOOKASIDE. This +** option requires a separate macro because legal values contain a single +** comma. e.g. (-DSQLITE_DEFAULT_LOOKASIDE="100,100") */ +#define CTIMEOPT_VAL2_(opt1,opt2) #opt1 "," #opt2 +#define CTIMEOPT_VAL2(opt) CTIMEOPT_VAL2_(opt) +/* #include "sqliteInt.h" */ /* -** This structure is passed around through all the sanity checking routines -** in order to keep track of some global state information. +** An array of names of all compile-time options. This array should +** be sorted A-Z. ** -** The aRef[] array is allocated so that there is 1 bit for each page in -** the database. As the integrity-check proceeds, for each page used in -** the database the corresponding bit is set. This allows integrity-check to -** detect pages that are used twice and orphaned pages (both of which -** indicate corruption). -*/ -typedef struct IntegrityCk IntegrityCk; -struct IntegrityCk { - BtShared *pBt; /* The tree being checked out */ - Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ - u8 *aPgRef; /* 1 bit per page in the db (see above) */ - Pgno nPage; /* Number of pages in the database */ - int mxErr; /* Stop accumulating errors when this reaches zero */ - int nErr; /* Number of messages written to zErrMsg so far */ - int bOomFault; /* A memory allocation error has occurred */ - const char *zPfx; /* Error message prefix */ - Pgno v1; /* Value for first %u substitution in zPfx */ - int v2; /* Value for second %d substitution in zPfx */ - StrAccum errMsg; /* Accumulate the error message text here */ - u32 *heap; /* Min-heap used for analyzing cell coverage */ - sqlcipher_sqlite3 *db; /* Database connection running the check */ -}; - -/* -** Routines to read or write a two- and four-byte big-endian integer values. +** This array looks large, but in a typical installation actually uses +** only a handful of compile-time options, so most times this array is usually +** rather short and uses little memory space. */ -#define get2byte(x) ((x)[0]<<8 | (x)[1]) -#define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v)) -#define get4byte sqlcipher_sqlite3Get4byte -#define put4byte sqlcipher_sqlite3Put4byte +static const char * const sqlcipher_sqlite3azCompileOpt[] = { -/* -** get2byteAligned(), unlike get2byte(), requires that its argument point to a -** two-byte aligned address. get2bytea() is only used for accessing the -** cell addresses in a btree header. -*/ -#if SQLITE_BYTEORDER==4321 -# define get2byteAligned(x) (*(u16*)(x)) -#elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4008000 -# define get2byteAligned(x) __builtin_bswap16(*(u16*)(x)) -#elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 -# define get2byteAligned(x) _byteswap_ushort(*(u16*)(x)) -#else -# define get2byteAligned(x) ((x)[0]<<8 | (x)[1]) +#ifdef SQLITE_32BIT_ROWID + "32BIT_ROWID", #endif - -/************** End of btreeInt.h ********************************************/ -/************** Continuing where we left off in crypto.h *********************/ -/* #include "pager.h" */ - -/* extensions defined in pager.c */ -SQLITE_PRIVATE void *sqlcipher_sqlite3PagerGetCodec(Pager*); -SQLITE_PRIVATE void sqlcipher_sqlite3PagerSetCodec(Pager*, void *(*)(void*,void*,Pgno,int), void (*)(void*,int,int), void (*)(void*), void *); -SQLITE_API int sqlcipher_sqlite3pager_is_mj_pgno(Pager*, Pgno); -SQLITE_API void sqlcipher_sqlite3pager_error(Pager*, int); -SQLITE_API void sqlcipher_sqlite3pager_reset(Pager *pPager); - -#if !defined (SQLCIPHER_CRYPTO_CC) \ - && !defined (SQLCIPHER_CRYPTO_LIBTOMCRYPT) \ - && !defined (SQLCIPHER_CRYPTO_NSS) \ - && !defined (SQLCIPHER_CRYPTO_OPENSSL) -#define SQLCIPHER_CRYPTO_OPENSSL +#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC + "4_BYTE_ALIGNED_MALLOC", #endif - -#define FILE_HEADER_SZ 16 - -#define CIPHER_XSTR(s) CIPHER_STR(s) -#define CIPHER_STR(s) #s - -#ifndef CIPHER_VERSION_NUMBER -#define CIPHER_VERSION_NUMBER 4.4.3 +#ifdef SQLITE_64BIT_STATS + "64BIT_STATS", #endif - -#ifndef CIPHER_VERSION_BUILD -#define CIPHER_VERSION_BUILD community +#ifdef SQLITE_ALLOW_COVERING_INDEX_SCAN +# if SQLITE_ALLOW_COVERING_INDEX_SCAN != 1 + "ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN), +# endif #endif - -#define CIPHER_DECRYPT 0 -#define CIPHER_ENCRYPT 1 - -#define CIPHER_READ_CTX 0 -#define CIPHER_WRITE_CTX 1 -#define CIPHER_READWRITE_CTX 2 - -#ifndef PBKDF2_ITER -#define PBKDF2_ITER 256000 +#ifdef SQLITE_ALLOW_URI_AUTHORITY + "ALLOW_URI_AUTHORITY", #endif - -/* possible flags for cipher_ctx->flags */ -#define CIPHER_FLAG_HMAC 0x01 -#define CIPHER_FLAG_LE_PGNO 0x02 -#define CIPHER_FLAG_BE_PGNO 0x04 - -#ifndef DEFAULT_CIPHER_FLAGS -#define DEFAULT_CIPHER_FLAGS CIPHER_FLAG_HMAC | CIPHER_FLAG_LE_PGNO +#ifdef SQLITE_ATOMIC_INTRINSICS + "ATOMIC_INTRINSICS=" CTIMEOPT_VAL(SQLITE_ATOMIC_INTRINSICS), #endif - - -/* by default, sqlcipher will use a reduced number of iterations to generate - the HMAC key / or transform a raw cipher key - */ -#ifndef FAST_PBKDF2_ITER -#define FAST_PBKDF2_ITER 2 +#ifdef SQLITE_BITMASK_TYPE + "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE), #endif - -/* this if a fixed random array that will be xor'd with the database salt to ensure that the - salt passed to the HMAC key derivation function is not the same as that used to derive - the encryption key. This can be overridden at compile time but it will make the resulting - binary incompatible with the default builds when using HMAC. A future version of SQLcipher - will likely allow this to be defined at runtime via pragma */ -#ifndef HMAC_SALT_MASK -#define HMAC_SALT_MASK 0x3a +#ifdef SQLITE_BUG_COMPATIBLE_20160819 + "BUG_COMPATIBLE_20160819", #endif - -#ifndef CIPHER_MAX_IV_SZ -#define CIPHER_MAX_IV_SZ 16 +#ifdef SQLITE_CASE_SENSITIVE_LIKE + "CASE_SENSITIVE_LIKE", #endif - -#ifndef CIPHER_MAX_KEY_SZ -#define CIPHER_MAX_KEY_SZ 64 +#ifdef SQLITE_CHECK_PAGES + "CHECK_PAGES", #endif - -#ifdef __ANDROID__ -#include +#if defined(__clang__) && defined(__clang_major__) + "COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "." + CTIMEOPT_VAL(__clang_minor__) "." + CTIMEOPT_VAL(__clang_patchlevel__), +#elif defined(_MSC_VER) + "COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER), +#elif defined(__GNUC__) && defined(__VERSION__) + "COMPILER=gcc-" __VERSION__, #endif - -#ifdef CODEC_DEBUG -#ifdef __ANDROID__ -#define CODEC_TRACE(...) {__android_log_print(ANDROID_LOG_DEBUG, "sqlcipher", __VA_ARGS__);} -#else -#define CODEC_TRACE(...) {fprintf(stderr, __VA_ARGS__);fflush(stderr);} +#ifdef SQLITE_COVERAGE_TEST + "COVERAGE_TEST", #endif -#else -#define CODEC_TRACE(...) +#ifdef SQLITE_DEBUG + "DEBUG", #endif - -#ifdef CODEC_DEBUG_MUTEX -#define CODEC_TRACE_MUTEX(...) CODEC_TRACE(__VA_ARGS__) -#else -#define CODEC_TRACE_MUTEX(...) +#ifdef SQLITE_DEFAULT_AUTOMATIC_INDEX + "DEFAULT_AUTOMATIC_INDEX", #endif - -#ifdef CODEC_DEBUG_MEMORY -#define CODEC_TRACE_MEMORY(...) CODEC_TRACE(__VA_ARGS__) -#else -#define CODEC_TRACE_MEMORY(...) +#ifdef SQLITE_DEFAULT_AUTOVACUUM + "DEFAULT_AUTOVACUUM", #endif - -#ifdef CODEC_DEBUG_PAGEDATA -#define CODEC_HEXDUMP(DESC,BUFFER,LEN) \ - { \ - int __pctr; \ - printf(DESC); \ - for(__pctr=0; __pctr < LEN; __pctr++) { \ - if(__pctr % 16 == 0) printf("\n%05x: ",__pctr); \ - printf("%02x ",((unsigned char*) BUFFER)[__pctr]); \ - } \ - printf("\n"); \ - fflush(stdout); \ - } -#else -#define CODEC_HEXDUMP(DESC,BUFFER,LEN) +#ifdef SQLITE_DEFAULT_CACHE_SIZE + "DEFAULT_CACHE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_CACHE_SIZE), #endif - -/* end extensions defined in pager.c */ - -/* -** Simple shared routines for converting hex char strings to binary data - */ -static int cipher_hex2int(char c) { - return (c>='0' && c<='9') ? (c)-'0' : - (c>='A' && c<='F') ? (c)-'A'+10 : - (c>='a' && c<='f') ? (c)-'a'+10 : 0; -} - -static void cipher_hex2bin(const unsigned char *hex, int sz, unsigned char *out){ - int i; - for(i = 0; i < sz; i += 2){ - out[i/2] = (cipher_hex2int(hex[i])<<4) | cipher_hex2int(hex[i+1]); - } -} - -static void cipher_bin2hex(const unsigned char* in, int sz, char *out) { - int i; - for(i=0; i < sz; i++) { - sqlcipher_sqlite3_snprintf(3, out + (i*2), "%02x ", in[i]); - } -} - -static int cipher_isHex(const unsigned char *hex, int sz){ - int i; - for(i = 0; i < sz; i++) { - unsigned char c = hex[i]; - if ((c < '0' || c > '9') && - (c < 'A' || c > 'F') && - (c < 'a' || c > 'f')) { - return 0; - } - } - return 1; -} - -/* extensions defined in crypto_impl.c */ -/* the default implementation of SQLCipher uses a cipher_ctx - to keep track of read / write state separately. The following - struct and associated functions are defined here */ -typedef struct { - int derive_key; - int pass_sz; - unsigned char *key; - unsigned char *hmac_key; - unsigned char *pass; - char *keyspec; -} cipher_ctx; - - -typedef struct { - int store_pass; - int kdf_iter; - int fast_kdf_iter; - int kdf_salt_sz; - int key_sz; - int iv_sz; - int block_sz; - int page_sz; - int keyspec_sz; - int reserve_sz; - int hmac_sz; - int plaintext_header_sz; - int hmac_algorithm; - int kdf_algorithm; - unsigned int skip_read_hmac; - unsigned int need_kdf_salt; - unsigned int flags; - unsigned char *kdf_salt; - unsigned char *hmac_kdf_salt; - unsigned char *buffer; - Btree *pBt; - cipher_ctx *read_ctx; - cipher_ctx *write_ctx; - sqlcipher_provider *provider; - void *provider_ctx; -} codec_ctx ; - -/* crypto.c functions */ -int sqlcipher_codec_pragma(sqlcipher_sqlite3*, int, Parse*, const char *, const char*); -SQLITE_PRIVATE int sqlcipher_sqlite3CodecAttach(sqlcipher_sqlite3*, int, const void *, int); -SQLITE_PRIVATE void sqlcipher_sqlite3CodecGetKey(sqlcipher_sqlite3*, int, void**, int*); -void sqlcipher_exportFunc(sqlcipher_sqlite3_context *, int, sqlcipher_sqlite3_value **); - -/* crypto_impl.c functions */ - -void sqlcipher_init_memmethods(void); - -/* activation and initialization */ -void sqlcipher_activate(void); -void sqlcipher_deactivate(void); - -int sqlcipher_codec_ctx_init(codec_ctx **, Db *, Pager *, const void *, int); -void sqlcipher_codec_ctx_free(codec_ctx **); -int sqlcipher_codec_key_derive(codec_ctx *); -int sqlcipher_codec_key_copy(codec_ctx *, int); - -/* page cipher implementation */ -int sqlcipher_page_cipher(codec_ctx *, int, Pgno, int, int, unsigned char *, unsigned char *); - -/* context setters & getters */ -void sqlcipher_codec_ctx_set_error(codec_ctx *, int); - -void sqlcipher_codec_get_pass(codec_ctx *, void **, int *); -int sqlcipher_codec_ctx_set_pass(codec_ctx *, const void *, int, int); -void sqlcipher_codec_get_keyspec(codec_ctx *, void **zKey, int *nKey); - -int sqlcipher_codec_ctx_set_pagesize(codec_ctx *, int); -int sqlcipher_codec_ctx_get_pagesize(codec_ctx *); -int sqlcipher_codec_ctx_get_reservesize(codec_ctx *); - -void sqlcipher_set_default_pagesize(int page_size); -int sqlcipher_get_default_pagesize(void); - -void sqlcipher_set_default_kdf_iter(int iter); -int sqlcipher_get_default_kdf_iter(void); -int sqlcipher_codec_ctx_set_kdf_iter(codec_ctx *, int); -int sqlcipher_codec_ctx_get_kdf_iter(codec_ctx *ctx); - -int sqlcipher_codec_ctx_set_kdf_salt(codec_ctx *ctx, unsigned char *salt, int sz); -int sqlcipher_codec_ctx_get_kdf_salt(codec_ctx *ctx, void **salt); - -int sqlcipher_codec_ctx_set_fast_kdf_iter(codec_ctx *, int); -int sqlcipher_codec_ctx_get_fast_kdf_iter(codec_ctx *); - -const char* sqlcipher_codec_ctx_get_cipher(codec_ctx *ctx); - -void* sqlcipher_codec_ctx_get_data(codec_ctx *); - -void sqlcipher_set_default_use_hmac(int use); -int sqlcipher_get_default_use_hmac(void); - -void sqlcipher_set_hmac_salt_mask(unsigned char mask); -unsigned char sqlcipher_get_hmac_salt_mask(void); - -int sqlcipher_codec_ctx_set_use_hmac(codec_ctx *ctx, int use); -int sqlcipher_codec_ctx_get_use_hmac(codec_ctx *ctx); - -int sqlcipher_codec_ctx_set_flag(codec_ctx *ctx, unsigned int flag); -int sqlcipher_codec_ctx_unset_flag(codec_ctx *ctx, unsigned int flag); -int sqlcipher_codec_ctx_get_flag(codec_ctx *ctx, unsigned int flag); - -const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx); -int sqlcipher_codec_ctx_migrate(codec_ctx *ctx); -int sqlcipher_codec_add_random(codec_ctx *ctx, const char *data, int random_sz); -int sqlcipher_cipher_profile(sqlcipher_sqlite3 *db, const char *destination); -int sqlcipher_codec_get_store_pass(codec_ctx *ctx); -void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey); -void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value); -int sqlcipher_codec_fips_status(codec_ctx *ctx); -const char* sqlcipher_codec_get_provider_version(codec_ctx *ctx); - -int sqlcipher_set_default_plaintext_header_size(int size); -int sqlcipher_get_default_plaintext_header_size(void); -int sqlcipher_codec_ctx_set_plaintext_header_size(codec_ctx *ctx, int size); -int sqlcipher_codec_ctx_get_plaintext_header_size(codec_ctx *ctx); - -int sqlcipher_set_default_hmac_algorithm(int algorithm); -int sqlcipher_get_default_hmac_algorithm(void); -int sqlcipher_codec_ctx_set_hmac_algorithm(codec_ctx *ctx, int algorithm); -int sqlcipher_codec_ctx_get_hmac_algorithm(codec_ctx *ctx); - -int sqlcipher_set_default_kdf_algorithm(int algorithm); -int sqlcipher_get_default_kdf_algorithm(void); -int sqlcipher_codec_ctx_set_kdf_algorithm(codec_ctx *ctx, int algorithm); -int sqlcipher_codec_ctx_get_kdf_algorithm(codec_ctx *ctx); - -void sqlcipher_set_mem_security(int); -int sqlcipher_get_mem_security(void); - -int sqlcipher_find_db_index(sqlcipher_sqlite3 *db, const char *zDb); - -int sqlcipher_codec_ctx_integrity_check(codec_ctx *, Parse *, char *); - +#ifdef SQLITE_DEFAULT_CKPTFULLFSYNC + "DEFAULT_CKPTFULLFSYNC", +#endif +#ifdef SQLITE_DEFAULT_FILE_FORMAT + "DEFAULT_FILE_FORMAT=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_FORMAT), +#endif +#ifdef SQLITE_DEFAULT_FILE_PERMISSIONS + "DEFAULT_FILE_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_PERMISSIONS), +#endif +#ifdef SQLITE_DEFAULT_FOREIGN_KEYS + "DEFAULT_FOREIGN_KEYS", +#endif +#ifdef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT + "DEFAULT_JOURNAL_SIZE_LIMIT=" CTIMEOPT_VAL(SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT), +#endif +#ifdef SQLITE_DEFAULT_LOCKING_MODE + "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), +#endif +#ifdef SQLITE_DEFAULT_LOOKASIDE + "DEFAULT_LOOKASIDE=" CTIMEOPT_VAL2(SQLITE_DEFAULT_LOOKASIDE), +#endif +#ifdef SQLITE_DEFAULT_MEMSTATUS +# if SQLITE_DEFAULT_MEMSTATUS != 1 + "DEFAULT_MEMSTATUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_MEMSTATUS), +# endif +#endif +#ifdef SQLITE_DEFAULT_MMAP_SIZE + "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), +#endif +#ifdef SQLITE_DEFAULT_PAGE_SIZE + "DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_PAGE_SIZE), +#endif +#ifdef SQLITE_DEFAULT_PCACHE_INITSZ + "DEFAULT_PCACHE_INITSZ=" CTIMEOPT_VAL(SQLITE_DEFAULT_PCACHE_INITSZ), +#endif +#ifdef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS + "DEFAULT_PROXYDIR_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_PROXYDIR_PERMISSIONS), +#endif +#ifdef SQLITE_DEFAULT_RECURSIVE_TRIGGERS + "DEFAULT_RECURSIVE_TRIGGERS", +#endif +#ifdef SQLITE_DEFAULT_ROWEST + "DEFAULT_ROWEST=" CTIMEOPT_VAL(SQLITE_DEFAULT_ROWEST), +#endif +#ifdef SQLITE_DEFAULT_SECTOR_SIZE + "DEFAULT_SECTOR_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_SECTOR_SIZE), +#endif +#ifdef SQLITE_DEFAULT_SYNCHRONOUS + "DEFAULT_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_SYNCHRONOUS), +#endif +#ifdef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT + "DEFAULT_WAL_AUTOCHECKPOINT=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_AUTOCHECKPOINT), +#endif +#ifdef SQLITE_DEFAULT_WAL_SYNCHRONOUS + "DEFAULT_WAL_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_SYNCHRONOUS), +#endif +#ifdef SQLITE_DEFAULT_WORKER_THREADS + "DEFAULT_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WORKER_THREADS), +#endif +#ifdef SQLITE_DIRECT_OVERFLOW_READ + "DIRECT_OVERFLOW_READ", +#endif +#ifdef SQLITE_DISABLE_DIRSYNC + "DISABLE_DIRSYNC", +#endif +#ifdef SQLITE_DISABLE_FTS3_UNICODE + "DISABLE_FTS3_UNICODE", +#endif +#ifdef SQLITE_DISABLE_FTS4_DEFERRED + "DISABLE_FTS4_DEFERRED", +#endif +#ifdef SQLITE_DISABLE_INTRINSIC + "DISABLE_INTRINSIC", +#endif +#ifdef SQLITE_DISABLE_LFS + "DISABLE_LFS", +#endif +#ifdef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS + "DISABLE_PAGECACHE_OVERFLOW_STATS", +#endif +#ifdef SQLITE_DISABLE_SKIPAHEAD_DISTINCT + "DISABLE_SKIPAHEAD_DISTINCT", #endif +#ifdef SQLITE_ENABLE_8_3_NAMES + "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES), +#endif +#ifdef SQLITE_ENABLE_API_ARMOR + "ENABLE_API_ARMOR", +#endif +#ifdef SQLITE_ENABLE_ATOMIC_WRITE + "ENABLE_ATOMIC_WRITE", +#endif +#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE + "ENABLE_BATCH_ATOMIC_WRITE", +#endif +#ifdef SQLITE_ENABLE_BYTECODE_VTAB + "ENABLE_BYTECODE_VTAB", +#endif +#ifdef SQLITE_ENABLE_CEROD + "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), +#endif +#ifdef SQLITE_ENABLE_COLUMN_METADATA + "ENABLE_COLUMN_METADATA", +#endif +#ifdef SQLITE_ENABLE_COLUMN_USED_MASK + "ENABLE_COLUMN_USED_MASK", +#endif +#ifdef SQLITE_ENABLE_COSTMULT + "ENABLE_COSTMULT", +#endif +#ifdef SQLITE_ENABLE_CURSOR_HINTS + "ENABLE_CURSOR_HINTS", +#endif +#ifdef SQLITE_ENABLE_DBPAGE_VTAB + "ENABLE_DBPAGE_VTAB", +#endif +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + "ENABLE_DBSTAT_VTAB", +#endif +#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT + "ENABLE_EXPENSIVE_ASSERT", +#endif +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + "ENABLE_EXPLAIN_COMMENTS", +#endif +#ifdef SQLITE_ENABLE_FTS3 + "ENABLE_FTS3", +#endif +#ifdef SQLITE_ENABLE_FTS3_PARENTHESIS + "ENABLE_FTS3_PARENTHESIS", +#endif +#ifdef SQLITE_ENABLE_FTS3_TOKENIZER + "ENABLE_FTS3_TOKENIZER", +#endif +#ifdef SQLITE_ENABLE_FTS4 + "ENABLE_FTS4", +#endif +#ifdef SQLITE_ENABLE_FTS5 + "ENABLE_FTS5", +#endif +#ifdef SQLITE_ENABLE_GEOPOLY + "ENABLE_GEOPOLY", +#endif +#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS + "ENABLE_HIDDEN_COLUMNS", +#endif +#ifdef SQLITE_ENABLE_ICU + "ENABLE_ICU", +#endif +#ifdef SQLITE_ENABLE_IOTRACE + "ENABLE_IOTRACE", +#endif +#ifdef SQLITE_ENABLE_LOAD_EXTENSION + "ENABLE_LOAD_EXTENSION", +#endif +#ifdef SQLITE_ENABLE_LOCKING_STYLE + "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), +#endif +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + "ENABLE_MATH_FUNCTIONS", +#endif +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + "ENABLE_MEMORY_MANAGEMENT", +#endif +#ifdef SQLITE_ENABLE_MEMSYS3 + "ENABLE_MEMSYS3", +#endif +#ifdef SQLITE_ENABLE_MEMSYS5 + "ENABLE_MEMSYS5", +#endif +#ifdef SQLITE_ENABLE_MULTIPLEX + "ENABLE_MULTIPLEX", +#endif +#ifdef SQLITE_ENABLE_NORMALIZE + "ENABLE_NORMALIZE", +#endif +#ifdef SQLITE_ENABLE_NULL_TRIM + "ENABLE_NULL_TRIM", +#endif +#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC + "ENABLE_OFFSET_SQL_FUNC", +#endif +#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK + "ENABLE_OVERSIZE_CELL_CHECK", +#endif +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + "ENABLE_PREUPDATE_HOOK", +#endif +#ifdef SQLITE_ENABLE_QPSG + "ENABLE_QPSG", +#endif +#ifdef SQLITE_ENABLE_RBU + "ENABLE_RBU", +#endif +#ifdef SQLITE_ENABLE_RTREE + "ENABLE_RTREE", +#endif +#ifdef SQLITE_ENABLE_SESSION + "ENABLE_SESSION", +#endif +#ifdef SQLITE_ENABLE_SNAPSHOT + "ENABLE_SNAPSHOT", +#endif +#ifdef SQLITE_ENABLE_SORTER_REFERENCES + "ENABLE_SORTER_REFERENCES", +#endif +#ifdef SQLITE_ENABLE_SQLLOG + "ENABLE_SQLLOG", +#endif +#ifdef SQLITE_ENABLE_STAT4 + "ENABLE_STAT4", +#endif +#ifdef SQLITE_ENABLE_STMTVTAB + "ENABLE_STMTVTAB", +#endif +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + "ENABLE_STMT_SCANSTATUS", +#endif +#ifdef SQLITE_ENABLE_TREETRACE + "ENABLE_TREETRACE", +#endif +#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION + "ENABLE_UNKNOWN_SQL_FUNCTION", +#endif +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY + "ENABLE_UNLOCK_NOTIFY", +#endif +#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT + "ENABLE_UPDATE_DELETE_LIMIT", +#endif +#ifdef SQLITE_ENABLE_URI_00_ERROR + "ENABLE_URI_00_ERROR", +#endif +#ifdef SQLITE_ENABLE_VFSTRACE + "ENABLE_VFSTRACE", +#endif +#ifdef SQLITE_ENABLE_WHERETRACE + "ENABLE_WHERETRACE", +#endif +#ifdef SQLITE_ENABLE_ZIPVFS + "ENABLE_ZIPVFS", +#endif +#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS + "EXPLAIN_ESTIMATED_ROWS", +#endif +#ifdef SQLITE_EXTRA_IFNULLROW + "EXTRA_IFNULLROW", +#endif +#ifdef SQLITE_EXTRA_INIT + "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), +#endif +#ifdef SQLITE_EXTRA_SHUTDOWN + "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), +#endif +#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH + "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), +#endif +#ifdef SQLITE_FTS5_ENABLE_TEST_MI + "FTS5_ENABLE_TEST_MI", +#endif +#ifdef SQLITE_FTS5_NO_WITHOUT_ROWID + "FTS5_NO_WITHOUT_ROWID", +#endif +/* BEGIN SQLCIPHER */ +#if SQLITE_HAS_CODEC + "HAS_CODEC", #endif /* END SQLCIPHER */ - -/************** End of crypto.h **********************************************/ -/************** Continuing where we left off in crypto.c *********************/ - -#ifdef SQLCIPHER_EXT -#include "sqlcipher_ext.h" +#if HAVE_ISNAN || SQLITE_HAVE_ISNAN + "HAVE_ISNAN", #endif - -#ifdef SQLCIPHER_TEST -static int cipher_fail_next_encrypt = 0; -static int cipher_fail_next_decrypt = 0; +#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX +# if SQLITE_HOMEGROWN_RECURSIVE_MUTEX != 1 + "HOMEGROWN_RECURSIVE_MUTEX=" CTIMEOPT_VAL(SQLITE_HOMEGROWN_RECURSIVE_MUTEX), +# endif #endif - -/* Generate code to return a string value */ -static void codec_vdbe_return_string(Parse *pParse, const char *zLabel, const char *value, int value_type){ - Vdbe *v = sqlcipher_sqlite3GetVdbe(pParse); - sqlcipher_sqlite3VdbeSetNumCols(v, 1); - sqlcipher_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC); - sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, value, value_type); - sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); -} - -static int codec_set_btree_to_codec_pagesize(sqlcipher_sqlite3 *db, Db *pDb, codec_ctx *ctx) { - int rc, page_sz, reserve_sz; - - page_sz = sqlcipher_codec_ctx_get_pagesize(ctx); - reserve_sz = sqlcipher_codec_ctx_get_reservesize(ctx); - - CODEC_TRACE("codec_set_btree_to_codec_pagesize: sqlcipher_sqlite3BtreeSetPageSize() size=%d reserve=%d\n", page_sz, reserve_sz); - - CODEC_TRACE_MUTEX("codec_set_btree_to_codec_pagesize: entering database mutex %p\n", db->mutex); - sqlcipher_sqlite3_mutex_enter(db->mutex); - CODEC_TRACE_MUTEX("codec_set_btree_to_codec_pagesize: entered database mutex %p\n", db->mutex); - db->nextPagesize = page_sz; - - /* before forcing the page size we need to unset the BTS_PAGESIZE_FIXED flag, else - sqliteBtreeSetPageSize will block the change */ - pDb->pBt->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; - rc = sqlcipher_sqlite3BtreeSetPageSize(pDb->pBt, page_sz, reserve_sz, 0); - - CODEC_TRACE("codec_set_btree_to_codec_pagesize: sqlcipher_sqlite3BtreeSetPageSize returned %d\n", rc); - - CODEC_TRACE_MUTEX("codec_set_btree_to_codec_pagesize: leaving database mutex %p\n", db->mutex); - sqlcipher_sqlite3_mutex_leave(db->mutex); - CODEC_TRACE_MUTEX("codec_set_btree_to_codec_pagesize: left database mutex %p\n", db->mutex); - - return rc; -} - -static int codec_set_pass_key(sqlcipher_sqlite3* db, int nDb, const void *zKey, int nKey, int for_ctx) { - struct Db *pDb = &db->aDb[nDb]; - CODEC_TRACE("codec_set_pass_key: entered db=%p nDb=%d zKey=%s nKey=%d for_ctx=%d\n", db, nDb, (char *)zKey, nKey, for_ctx); - if(pDb->pBt) { - codec_ctx *ctx = (codec_ctx*) sqlcipher_sqlite3PagerGetCodec(pDb->pBt->pBt->pPager); - - if(ctx) return sqlcipher_codec_ctx_set_pass(ctx, zKey, nKey, for_ctx); - } - return SQLITE_ERROR; -} - -int sqlcipher_codec_pragma(sqlcipher_sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const char *zRight) { - struct Db *pDb = &db->aDb[iDb]; - codec_ctx *ctx = NULL; - int rc; - - if(pDb->pBt) { - ctx = (codec_ctx*) sqlcipher_sqlite3PagerGetCodec(pDb->pBt->pBt->pPager); - } - - CODEC_TRACE("sqlcipher_codec_pragma: entered db=%p iDb=%d pParse=%p zLeft=%s zRight=%s ctx=%p\n", db, iDb, pParse, zLeft, zRight, ctx); - -#ifdef SQLCIPHER_EXT - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher_license")==0 && zRight ){ - char *license_result = sqlcipher_sqlite3_mprintf("%d", sqlcipher_license_key(zRight)); - codec_vdbe_return_string(pParse, "cipher_license", license_result, P4_DYNAMIC); - } else - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher_license")==0 && !zRight ){ - if(ctx) { - char *license_result = sqlcipher_sqlite3_mprintf("%d", ctx - ? sqlcipher_license_key_status(ctx->provider) - : SQLITE_ERROR); - codec_vdbe_return_string(pParse, "cipher_license", license_result, P4_DYNAMIC); - } - } else +#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS + "IGNORE_AFP_LOCK_ERRORS", #endif -#ifdef SQLCIPHER_TEST - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_fail_next_encrypt")==0 ){ - if( zRight ) { - cipher_fail_next_encrypt = sqlcipher_sqlite3GetBoolean(zRight,1); - } else { - char *fail = sqlcipher_sqlite3_mprintf("%d", cipher_fail_next_encrypt); - codec_vdbe_return_string(pParse, "cipher_fail_next_encrypt", fail, P4_DYNAMIC); - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_fail_next_decrypt")==0 ){ - if( zRight ) { - cipher_fail_next_decrypt = sqlcipher_sqlite3GetBoolean(zRight,1); - } else { - char *fail = sqlcipher_sqlite3_mprintf("%d", cipher_fail_next_decrypt); - codec_vdbe_return_string(pParse, "cipher_fail_next_decrypt", fail, P4_DYNAMIC); - } - }else +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS + "IGNORE_FLOCK_LOCK_ERRORS", #endif - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher_fips_status")== 0 && !zRight ){ - if(ctx) { - char *fips_mode_status = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_fips_status(ctx)); - codec_vdbe_return_string(pParse, "cipher_fips_status", fips_mode_status, P4_DYNAMIC); - } - } else - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && zRight ) { - if(ctx) { - char *deprecation = "PRAGMA cipher_store_pass is deprecated, please remove from use"; - sqlcipher_codec_set_store_pass(ctx, sqlcipher_sqlite3GetBoolean(zRight, 1)); - codec_vdbe_return_string(pParse, "cipher_store_pass", deprecation, P4_TRANSIENT); - sqlcipher_sqlite3_log(SQLITE_WARNING, deprecation); - } - } else - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && !zRight ) { - if(ctx){ - char *store_pass_value = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_get_store_pass(ctx)); - codec_vdbe_return_string(pParse, "cipher_store_pass", store_pass_value, P4_DYNAMIC); - } - } - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher_profile")== 0 && zRight ){ - char *profile_status = sqlcipher_sqlite3_mprintf("%d", sqlcipher_cipher_profile(db, zRight)); - codec_vdbe_return_string(pParse, "cipher_profile", profile_status, P4_DYNAMIC); - } else - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher_add_random")==0 && zRight ){ - if(ctx) { - char *add_random_status = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_add_random(ctx, zRight, sqlcipher_sqlite3Strlen30(zRight))); - codec_vdbe_return_string(pParse, "cipher_add_random", add_random_status, P4_DYNAMIC); - } - } else - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher_migrate")==0 && !zRight ){ - if(ctx){ - char *migrate_status = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_ctx_migrate(ctx)); - codec_vdbe_return_string(pParse, "cipher_migrate", migrate_status, P4_DYNAMIC); - } - } else - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher_provider")==0 && !zRight ){ - if(ctx) { codec_vdbe_return_string(pParse, "cipher_provider", - sqlcipher_codec_get_cipher_provider(ctx), P4_TRANSIENT); - } - } else - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher_provider_version")==0 && !zRight){ - if(ctx) { codec_vdbe_return_string(pParse, "cipher_provider_version", - sqlcipher_codec_get_provider_version(ctx), P4_TRANSIENT); - } - } else - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher_version")==0 && !zRight ){ - codec_vdbe_return_string(pParse, "cipher_version", sqlcipher_version(), P4_DYNAMIC); - }else - if( sqlcipher_sqlite3StrICmp(zLeft, "cipher")==0 ){ - if(ctx) { - if( zRight ) { - const char* message = "PRAGMA cipher is no longer supported."; - codec_vdbe_return_string(pParse, "cipher", message, P4_TRANSIENT); - sqlcipher_sqlite3_log(SQLITE_WARNING, message); - }else { - codec_vdbe_return_string(pParse, "cipher", sqlcipher_codec_ctx_get_cipher(ctx), P4_TRANSIENT); - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft, "rekey_cipher")==0 && zRight ){ - const char* message = "PRAGMA rekey_cipher is no longer supported."; - codec_vdbe_return_string(pParse, "rekey_cipher", message, P4_TRANSIENT); - sqlcipher_sqlite3_log(SQLITE_WARNING, message); - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_default_kdf_iter")==0 ){ - if( zRight ) { - sqlcipher_set_default_kdf_iter(atoi(zRight)); /* change default KDF iterations */ - } else { - char *kdf_iter = sqlcipher_sqlite3_mprintf("%d", sqlcipher_get_default_kdf_iter()); - codec_vdbe_return_string(pParse, "cipher_default_kdf_iter", kdf_iter, P4_DYNAMIC); - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft, "kdf_iter")==0 ){ - if(ctx) { - if( zRight ) { - sqlcipher_codec_ctx_set_kdf_iter(ctx, atoi(zRight)); /* change of RW PBKDF2 iteration */ - } else { - char *kdf_iter = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_kdf_iter(ctx)); - codec_vdbe_return_string(pParse, "kdf_iter", kdf_iter, P4_DYNAMIC); - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft, "fast_kdf_iter")==0){ - if(ctx) { - if( zRight ) { - char *deprecation = "PRAGMA fast_kdf_iter is deprecated, please remove from use"; - sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, atoi(zRight)); /* change of RW PBKDF2 iteration */ - codec_vdbe_return_string(pParse, "fast_kdf_iter", deprecation, P4_TRANSIENT); - sqlcipher_sqlite3_log(SQLITE_WARNING, deprecation); - } else { - char *fast_kdf_iter = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_fast_kdf_iter(ctx)); - codec_vdbe_return_string(pParse, "fast_kdf_iter", fast_kdf_iter, P4_DYNAMIC); - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft, "rekey_kdf_iter")==0 && zRight ){ - const char* message = "PRAGMA rekey_kdf_iter is no longer supported."; - codec_vdbe_return_string(pParse, "rekey_kdf_iter", message, P4_TRANSIENT); - sqlcipher_sqlite3_log(SQLITE_WARNING, message); - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_page_size")==0 ){ - if(ctx) { - if( zRight ) { - int size = atoi(zRight); - rc = sqlcipher_codec_ctx_set_pagesize(ctx, size); - if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); - rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); - if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); - } else { - char * page_size = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_pagesize(ctx)); - codec_vdbe_return_string(pParse, "cipher_page_size", page_size, P4_DYNAMIC); - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_default_page_size")==0 ){ - if( zRight ) { - sqlcipher_set_default_pagesize(atoi(zRight)); - } else { - char *default_page_size = sqlcipher_sqlite3_mprintf("%d", sqlcipher_get_default_pagesize()); - codec_vdbe_return_string(pParse, "cipher_default_page_size", default_page_size, P4_DYNAMIC); - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_default_use_hmac")==0 ){ - if( zRight ) { - sqlcipher_set_default_use_hmac(sqlcipher_sqlite3GetBoolean(zRight,1)); - } else { - char *default_use_hmac = sqlcipher_sqlite3_mprintf("%d", sqlcipher_get_default_use_hmac()); - codec_vdbe_return_string(pParse, "cipher_default_use_hmac", default_use_hmac, P4_DYNAMIC); - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_use_hmac")==0 ){ - if(ctx) { - if( zRight ) { - rc = sqlcipher_codec_ctx_set_use_hmac(ctx, sqlcipher_sqlite3GetBoolean(zRight,1)); - if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); - /* since the use of hmac has changed, the page size may also change */ - rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); - if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); - } else { - char *hmac_flag = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_use_hmac(ctx)); - codec_vdbe_return_string(pParse, "cipher_use_hmac", hmac_flag, P4_DYNAMIC); - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_hmac_pgno")==0 ){ - if(ctx) { - if(zRight) { - char *deprecation = "PRAGMA cipher_hmac_pgno is deprecated, please remove from use"; - /* clear both pgno endian flags */ - if(sqlcipher_sqlite3StrICmp(zRight, "le") == 0) { - sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO); - sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_LE_PGNO); - } else if(sqlcipher_sqlite3StrICmp(zRight, "be") == 0) { - sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO); - sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_BE_PGNO); - } else if(sqlcipher_sqlite3StrICmp(zRight, "native") == 0) { - sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO); - sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO); - } - codec_vdbe_return_string(pParse, "cipher_hmac_pgno", deprecation, P4_TRANSIENT); - sqlcipher_sqlite3_log(SQLITE_WARNING, deprecation); - - } else { - if(sqlcipher_codec_ctx_get_flag(ctx, CIPHER_FLAG_LE_PGNO)) { - codec_vdbe_return_string(pParse, "cipher_hmac_pgno", "le", P4_TRANSIENT); - } else if(sqlcipher_codec_ctx_get_flag(ctx, CIPHER_FLAG_BE_PGNO)) { - codec_vdbe_return_string(pParse, "cipher_hmac_pgno", "be", P4_TRANSIENT); - } else { - codec_vdbe_return_string(pParse, "cipher_hmac_pgno", "native", P4_TRANSIENT); - } - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_hmac_salt_mask")==0 ){ - if(ctx) { - if(zRight) { - char *deprecation = "PRAGMA cipher_hmac_salt_mask is deprecated, please remove from use"; - if (sqlcipher_sqlite3StrNICmp(zRight ,"x'", 2) == 0 && sqlcipher_sqlite3Strlen30(zRight) == 5) { - unsigned char mask = 0; - const unsigned char *hex = (const unsigned char *)zRight+2; - cipher_hex2bin(hex,2,&mask); - sqlcipher_set_hmac_salt_mask(mask); - } - codec_vdbe_return_string(pParse, "cipher_hmac_salt_mask", deprecation, P4_TRANSIENT); - sqlcipher_sqlite3_log(SQLITE_WARNING, deprecation); - } else { - char *hmac_salt_mask = sqlcipher_sqlite3_mprintf("%02x", sqlcipher_get_hmac_salt_mask()); - codec_vdbe_return_string(pParse, "cipher_hmac_salt_mask", hmac_salt_mask, P4_DYNAMIC); - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_plaintext_header_size")==0 ){ - if(ctx) { - if( zRight ) { - int size = atoi(zRight); - /* deliberately ignore result code, if size is invalid it will be set to -1 - and trip the error later in the codec */ - sqlcipher_codec_ctx_set_plaintext_header_size(ctx, size); - } else { - char *size = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_plaintext_header_size(ctx)); - codec_vdbe_return_string(pParse, "cipher_plaintext_header_size", size, P4_DYNAMIC); - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_default_plaintext_header_size")==0 ){ - if( zRight ) { - sqlcipher_set_default_plaintext_header_size(atoi(zRight)); - } else { - char *size = sqlcipher_sqlite3_mprintf("%d", sqlcipher_get_default_plaintext_header_size()); - codec_vdbe_return_string(pParse, "cipher_default_plaintext_header_size", size, P4_DYNAMIC); - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_salt")==0 ){ - if(ctx) { - if(zRight) { - if (sqlcipher_sqlite3StrNICmp(zRight ,"x'", 2) == 0 && sqlcipher_sqlite3Strlen30(zRight) == (FILE_HEADER_SZ*2)+3) { - unsigned char *salt = (unsigned char*) sqlcipher_sqlite3_malloc(FILE_HEADER_SZ); - const unsigned char *hex = (const unsigned char *)zRight+2; - cipher_hex2bin(hex,FILE_HEADER_SZ*2,salt); - sqlcipher_codec_ctx_set_kdf_salt(ctx, salt, FILE_HEADER_SZ); - sqlcipher_sqlite3_free(salt); - } - } else { - void *salt; - char *hexsalt = (char*) sqlcipher_sqlite3_malloc((FILE_HEADER_SZ*2)+1); - if((rc = sqlcipher_codec_ctx_get_kdf_salt(ctx, &salt)) == SQLITE_OK) { - cipher_bin2hex(salt, FILE_HEADER_SZ, hexsalt); - codec_vdbe_return_string(pParse, "cipher_salt", hexsalt, P4_DYNAMIC); - } else { - sqlcipher_sqlite3_free(hexsalt); - sqlcipher_codec_ctx_set_error(ctx, rc); - } - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_hmac_algorithm")==0 ){ - if(ctx) { - if(zRight) { - rc = SQLITE_ERROR; - if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_HMAC_SHA1_LABEL) == 0) { - rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA1); - } else if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_HMAC_SHA256_LABEL) == 0) { - rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA256); - } else if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_HMAC_SHA512_LABEL) == 0) { - rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA512); - } - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - } else { - int algorithm = sqlcipher_codec_ctx_get_hmac_algorithm(ctx); - if(algorithm == SQLCIPHER_HMAC_SHA1) { - codec_vdbe_return_string(pParse, "cipher_hmac_algorithm", SQLCIPHER_HMAC_SHA1_LABEL, P4_TRANSIENT); - } else if(algorithm == SQLCIPHER_HMAC_SHA256) { - codec_vdbe_return_string(pParse, "cipher_hmac_algorithm", SQLCIPHER_HMAC_SHA256_LABEL, P4_TRANSIENT); - } else if(algorithm == SQLCIPHER_HMAC_SHA512) { - codec_vdbe_return_string(pParse, "cipher_hmac_algorithm", SQLCIPHER_HMAC_SHA512_LABEL, P4_TRANSIENT); - } - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_default_hmac_algorithm")==0 ){ - if(zRight) { - rc = SQLITE_ERROR; - if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_HMAC_SHA1_LABEL) == 0) { - rc = sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA1); - } else if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_HMAC_SHA256_LABEL) == 0) { - rc = sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA256); - } else if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_HMAC_SHA512_LABEL) == 0) { - rc = sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA512); - } - } else { - int algorithm = sqlcipher_get_default_hmac_algorithm(); - if(algorithm == SQLCIPHER_HMAC_SHA1) { - codec_vdbe_return_string(pParse, "cipher_default_hmac_algorithm", SQLCIPHER_HMAC_SHA1_LABEL, P4_TRANSIENT); - } else if(algorithm == SQLCIPHER_HMAC_SHA256) { - codec_vdbe_return_string(pParse, "cipher_default_hmac_algorithm", SQLCIPHER_HMAC_SHA256_LABEL, P4_TRANSIENT); - } else if(algorithm == SQLCIPHER_HMAC_SHA512) { - codec_vdbe_return_string(pParse, "cipher_default_hmac_algorithm", SQLCIPHER_HMAC_SHA512_LABEL, P4_TRANSIENT); - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_kdf_algorithm")==0 ){ - if(ctx) { - if(zRight) { - rc = SQLITE_ERROR; - if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL) == 0) { - rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA1); - } else if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL) == 0) { - rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA256); - } else if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL) == 0) { - rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA512); - } - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - } else { - int algorithm = sqlcipher_codec_ctx_get_kdf_algorithm(ctx); - if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA1) { - codec_vdbe_return_string(pParse, "cipher_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL, P4_TRANSIENT); - } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA256) { - codec_vdbe_return_string(pParse, "cipher_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL, P4_TRANSIENT); - } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA512) { - codec_vdbe_return_string(pParse, "cipher_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL, P4_TRANSIENT); - } - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_default_kdf_algorithm")==0 ){ - if(zRight) { - rc = SQLITE_ERROR; - if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL) == 0) { - rc = sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA1); - } else if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL) == 0) { - rc = sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA256); - } else if(sqlcipher_sqlite3StrICmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL) == 0) { - rc = sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA512); - } - } else { - int algorithm = sqlcipher_get_default_kdf_algorithm(); - if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA1) { - codec_vdbe_return_string(pParse, "cipher_default_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL, P4_TRANSIENT); - } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA256) { - codec_vdbe_return_string(pParse, "cipher_default_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL, P4_TRANSIENT); - } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA512) { - codec_vdbe_return_string(pParse, "cipher_default_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL, P4_TRANSIENT); - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_compatibility")==0 ){ - if(ctx) { - if(zRight) { - int version = atoi(zRight); - - switch(version) { - case 1: - rc = sqlcipher_codec_ctx_set_pagesize(ctx, 1024); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA1); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA1); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 4000); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 0); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - break; - - case 2: - rc = sqlcipher_codec_ctx_set_pagesize(ctx, 1024); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA1); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA1); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 4000); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 1); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - break; - - case 3: - rc = sqlcipher_codec_ctx_set_pagesize(ctx, 1024); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA1); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA1); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 64000); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 1); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - break; - - default: - rc = sqlcipher_codec_ctx_set_pagesize(ctx, 4096); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA512); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA512); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 256000); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 1); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - break; - } - - rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_default_compatibility")==0 ){ - if(zRight) { - int version = atoi(zRight); - switch(version) { - case 1: - sqlcipher_set_default_pagesize(1024); - sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA1); - sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA1); - sqlcipher_set_default_kdf_iter(4000); - sqlcipher_set_default_use_hmac(0); - break; - - case 2: - sqlcipher_set_default_pagesize(1024); - sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA1); - sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA1); - sqlcipher_set_default_kdf_iter(4000); - sqlcipher_set_default_use_hmac(1); - break; - - case 3: - sqlcipher_set_default_pagesize(1024); - sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA1); - sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA1); - sqlcipher_set_default_kdf_iter(64000); - sqlcipher_set_default_use_hmac(1); - break; - - default: - sqlcipher_set_default_pagesize(4096); - sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA512); - sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA512); - sqlcipher_set_default_kdf_iter(256000); - sqlcipher_set_default_use_hmac(1); - break; - } - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_memory_security")==0 ){ - if( zRight ) { - sqlcipher_set_mem_security(sqlcipher_sqlite3GetBoolean(zRight,1)); - } else { - char *on = sqlcipher_sqlite3_mprintf("%d", sqlcipher_get_mem_security()); - codec_vdbe_return_string(pParse, "cipher_memory_security", on, P4_DYNAMIC); - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_settings")==0 ){ - if(ctx) { - int algorithm; - char *pragma; - - pragma = sqlcipher_sqlite3_mprintf("PRAGMA kdf_iter = %d;", sqlcipher_codec_ctx_get_kdf_iter(ctx)); - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_page_size = %d;", sqlcipher_codec_ctx_get_pagesize(ctx)); - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_use_hmac = %d;", sqlcipher_codec_ctx_get_use_hmac(ctx)); - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_plaintext_header_size = %d;", sqlcipher_codec_ctx_get_plaintext_header_size(ctx)); - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - - algorithm = sqlcipher_codec_ctx_get_hmac_algorithm(ctx); - pragma = NULL; - if(algorithm == SQLCIPHER_HMAC_SHA1) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA1_LABEL); - } else if(algorithm == SQLCIPHER_HMAC_SHA256) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA256_LABEL); - } else if(algorithm == SQLCIPHER_HMAC_SHA512) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA512_LABEL); - } - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - - algorithm = sqlcipher_codec_ctx_get_kdf_algorithm(ctx); - pragma = NULL; - if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA1) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL); - } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA256) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL); - } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA512) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL); - } - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - - } - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_default_settings")==0 ){ - int algorithm; - char *pragma; - - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_kdf_iter = %d;", sqlcipher_get_default_kdf_iter()); - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_page_size = %d;", sqlcipher_get_default_pagesize()); - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_use_hmac = %d;", sqlcipher_get_default_use_hmac()); - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_plaintext_header_size = %d;", sqlcipher_get_default_plaintext_header_size()); - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - - algorithm = sqlcipher_get_default_hmac_algorithm(); - pragma = NULL; - if(algorithm == SQLCIPHER_HMAC_SHA1) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA1_LABEL); - } else if(algorithm == SQLCIPHER_HMAC_SHA256) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA256_LABEL); - } else if(algorithm == SQLCIPHER_HMAC_SHA512) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA512_LABEL); - } - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - - algorithm = sqlcipher_get_default_kdf_algorithm(); - pragma = NULL; - if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA1) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL); - } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA256) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL); - } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA512) { - pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL); - } - codec_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - }else - if( sqlcipher_sqlite3StrICmp(zLeft,"cipher_integrity_check")==0 ){ - if(ctx) { - sqlcipher_codec_ctx_integrity_check(ctx, pParse, "cipher_integrity_check"); - } - }else { - return 0; - } - return 1; -} - -/* these constants are used internally within SQLite's pager.c to differentiate between - operations on the main database or journal pages. This is important in the context - of a rekey operations, where the journal must be written using the original key - material (to allow a transactional rollback), while the new database pages are being - written with the new key material*/ -#define CODEC_READ_OP 3 -#define CODEC_WRITE_OP 6 -#define CODEC_JOURNAL_OP 7 - -/* - * sqlcipher_sqlite3Codec can be called in multiple modes. - * encrypt mode - expected to return a pointer to the - * encrypted data without altering pData. - * decrypt mode - expected to return a pointer to pData, with - * the data decrypted in the input buffer - */ -static void* sqlcipher_sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) { - codec_ctx *ctx = (codec_ctx *) iCtx; - int offset = 0, rc = 0; - int page_sz = sqlcipher_codec_ctx_get_pagesize(ctx); - unsigned char *pData = (unsigned char *) data; - void *buffer = sqlcipher_codec_ctx_get_data(ctx); - int plaintext_header_sz = sqlcipher_codec_ctx_get_plaintext_header_size(ctx); - int cctx = CIPHER_READ_CTX; - - CODEC_TRACE("sqlcipher_sqlite3Codec: entered pgno=%d, mode=%d, page_sz=%d\n", pgno, mode, page_sz); - -#ifdef SQLCIPHER_EXT - if(sqlcipher_license_check(ctx) != SQLITE_OK) return NULL; +#ifdef SQLITE_INLINE_MEMCPY + "INLINE_MEMCPY", +#endif +#ifdef SQLITE_INT64_TYPE + "INT64_TYPE", +#endif +#ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX + "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX), +#endif +#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS + "LIKE_DOESNT_MATCH_BLOBS", +#endif +#ifdef SQLITE_LOCK_TRACE + "LOCK_TRACE", +#endif +#ifdef SQLITE_LOG_CACHE_SPILL + "LOG_CACHE_SPILL", +#endif +#ifdef SQLITE_MALLOC_SOFT_LIMIT + "MALLOC_SOFT_LIMIT=" CTIMEOPT_VAL(SQLITE_MALLOC_SOFT_LIMIT), +#endif +#ifdef SQLITE_MAX_ATTACHED + "MAX_ATTACHED=" CTIMEOPT_VAL(SQLITE_MAX_ATTACHED), +#endif +#ifdef SQLITE_MAX_COLUMN + "MAX_COLUMN=" CTIMEOPT_VAL(SQLITE_MAX_COLUMN), +#endif +#ifdef SQLITE_MAX_COMPOUND_SELECT + "MAX_COMPOUND_SELECT=" CTIMEOPT_VAL(SQLITE_MAX_COMPOUND_SELECT), +#endif +#ifdef SQLITE_MAX_DEFAULT_PAGE_SIZE + "MAX_DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_DEFAULT_PAGE_SIZE), +#endif +#ifdef SQLITE_MAX_EXPR_DEPTH + "MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_EXPR_DEPTH), +#endif +#ifdef SQLITE_MAX_FUNCTION_ARG + "MAX_FUNCTION_ARG=" CTIMEOPT_VAL(SQLITE_MAX_FUNCTION_ARG), +#endif +#ifdef SQLITE_MAX_LENGTH + "MAX_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LENGTH), +#endif +#ifdef SQLITE_MAX_LIKE_PATTERN_LENGTH + "MAX_LIKE_PATTERN_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LIKE_PATTERN_LENGTH), +#endif +#ifdef SQLITE_MAX_MEMORY + "MAX_MEMORY=" CTIMEOPT_VAL(SQLITE_MAX_MEMORY), +#endif +#ifdef SQLITE_MAX_MMAP_SIZE + "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE), +#endif +#ifdef SQLITE_MAX_MMAP_SIZE_ + "MAX_MMAP_SIZE_=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE_), +#endif +#ifdef SQLITE_MAX_PAGE_COUNT + "MAX_PAGE_COUNT=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_COUNT), +#endif +#ifdef SQLITE_MAX_PAGE_SIZE + "MAX_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_SIZE), +#endif +#ifdef SQLITE_MAX_SCHEMA_RETRY + "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), +#endif +#ifdef SQLITE_MAX_SQL_LENGTH + "MAX_SQL_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_SQL_LENGTH), +#endif +#ifdef SQLITE_MAX_TRIGGER_DEPTH + "MAX_TRIGGER_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_TRIGGER_DEPTH), +#endif +#ifdef SQLITE_MAX_VARIABLE_NUMBER + "MAX_VARIABLE_NUMBER=" CTIMEOPT_VAL(SQLITE_MAX_VARIABLE_NUMBER), +#endif +#ifdef SQLITE_MAX_VDBE_OP + "MAX_VDBE_OP=" CTIMEOPT_VAL(SQLITE_MAX_VDBE_OP), +#endif +#ifdef SQLITE_MAX_WORKER_THREADS + "MAX_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_MAX_WORKER_THREADS), +#endif +#ifdef SQLITE_MEMDEBUG + "MEMDEBUG", +#endif +#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT + "MIXED_ENDIAN_64BIT_FLOAT", +#endif +#ifdef SQLITE_MMAP_READWRITE + "MMAP_READWRITE", +#endif +#ifdef SQLITE_MUTEX_NOOP + "MUTEX_NOOP", +#endif +#ifdef SQLITE_MUTEX_OMIT + "MUTEX_OMIT", +#endif +#ifdef SQLITE_MUTEX_PTHREADS + "MUTEX_PTHREADS", +#endif +#ifdef SQLITE_MUTEX_W32 + "MUTEX_W32", +#endif +#ifdef SQLITE_NEED_ERR_NAME + "NEED_ERR_NAME", +#endif +#ifdef SQLITE_NO_SYNC + "NO_SYNC", +#endif +#ifdef SQLITE_OMIT_ALTERTABLE + "OMIT_ALTERTABLE", +#endif +#ifdef SQLITE_OMIT_ANALYZE + "OMIT_ANALYZE", +#endif +#ifdef SQLITE_OMIT_ATTACH + "OMIT_ATTACH", +#endif +#ifdef SQLITE_OMIT_AUTHORIZATION + "OMIT_AUTHORIZATION", +#endif +#ifdef SQLITE_OMIT_AUTOINCREMENT + "OMIT_AUTOINCREMENT", +#endif +#ifdef SQLITE_OMIT_AUTOINIT + "OMIT_AUTOINIT", +#endif +#ifdef SQLITE_OMIT_AUTOMATIC_INDEX + "OMIT_AUTOMATIC_INDEX", +#endif +#ifdef SQLITE_OMIT_AUTORESET + "OMIT_AUTORESET", +#endif +#ifdef SQLITE_OMIT_AUTOVACUUM + "OMIT_AUTOVACUUM", +#endif +#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION + "OMIT_BETWEEN_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_BLOB_LITERAL + "OMIT_BLOB_LITERAL", +#endif +#ifdef SQLITE_OMIT_CAST + "OMIT_CAST", +#endif +#ifdef SQLITE_OMIT_CHECK + "OMIT_CHECK", +#endif +#ifdef SQLITE_OMIT_COMPLETE + "OMIT_COMPLETE", +#endif +#ifdef SQLITE_OMIT_COMPOUND_SELECT + "OMIT_COMPOUND_SELECT", +#endif +#ifdef SQLITE_OMIT_CONFLICT_CLAUSE + "OMIT_CONFLICT_CLAUSE", +#endif +#ifdef SQLITE_OMIT_CTE + "OMIT_CTE", +#endif +#if defined(SQLITE_OMIT_DATETIME_FUNCS) || defined(SQLITE_OMIT_FLOATING_POINT) + "OMIT_DATETIME_FUNCS", +#endif +#ifdef SQLITE_OMIT_DECLTYPE + "OMIT_DECLTYPE", +#endif +#ifdef SQLITE_OMIT_DEPRECATED + "OMIT_DEPRECATED", +#endif +#ifdef SQLITE_OMIT_DESERIALIZE + "OMIT_DESERIALIZE", +#endif +#ifdef SQLITE_OMIT_DISKIO + "OMIT_DISKIO", +#endif +#ifdef SQLITE_OMIT_EXPLAIN + "OMIT_EXPLAIN", +#endif +#ifdef SQLITE_OMIT_FLAG_PRAGMAS + "OMIT_FLAG_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_FLOATING_POINT + "OMIT_FLOATING_POINT", +#endif +#ifdef SQLITE_OMIT_FOREIGN_KEY + "OMIT_FOREIGN_KEY", +#endif +#ifdef SQLITE_OMIT_GET_TABLE + "OMIT_GET_TABLE", +#endif +#ifdef SQLITE_OMIT_HEX_INTEGER + "OMIT_HEX_INTEGER", +#endif +#ifdef SQLITE_OMIT_INCRBLOB + "OMIT_INCRBLOB", +#endif +#ifdef SQLITE_OMIT_INTEGRITY_CHECK + "OMIT_INTEGRITY_CHECK", +#endif +#ifdef SQLITE_OMIT_INTROSPECTION_PRAGMAS + "OMIT_INTROSPECTION_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_JSON + "OMIT_JSON", +#endif +#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION + "OMIT_LIKE_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_LOAD_EXTENSION + "OMIT_LOAD_EXTENSION", +#endif +#ifdef SQLITE_OMIT_LOCALTIME + "OMIT_LOCALTIME", +#endif +#ifdef SQLITE_OMIT_LOOKASIDE + "OMIT_LOOKASIDE", +#endif +#ifdef SQLITE_OMIT_MEMORYDB + "OMIT_MEMORYDB", +#endif +#ifdef SQLITE_OMIT_OR_OPTIMIZATION + "OMIT_OR_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_PAGER_PRAGMAS + "OMIT_PAGER_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_PARSER_TRACE + "OMIT_PARSER_TRACE", +#endif +#ifdef SQLITE_OMIT_POPEN + "OMIT_POPEN", +#endif +#ifdef SQLITE_OMIT_PRAGMA + "OMIT_PRAGMA", +#endif +#ifdef SQLITE_OMIT_PROGRESS_CALLBACK + "OMIT_PROGRESS_CALLBACK", +#endif +#ifdef SQLITE_OMIT_QUICKBALANCE + "OMIT_QUICKBALANCE", +#endif +#ifdef SQLITE_OMIT_REINDEX + "OMIT_REINDEX", +#endif +#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS + "OMIT_SCHEMA_PRAGMAS", #endif - - /* call to derive keys if not present yet */ - if((rc = sqlcipher_codec_key_derive(ctx)) != SQLITE_OK) { - sqlcipher_codec_ctx_set_error(ctx, rc); - return NULL; - } - - /* if the plaintext_header_size is negative that means an invalid size was set via - PRAGMA. We can't set the error state on the pager at that point because the pager - may not be open yet. However, this is a fatal error state, so abort the codec */ - if(plaintext_header_sz < 0) { - sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); - return NULL; - } - - if(pgno == 1) /* adjust starting pointers in data page for header offset on first page*/ - offset = plaintext_header_sz ? plaintext_header_sz : FILE_HEADER_SZ; - - - CODEC_TRACE("sqlcipher_sqlite3Codec: switch mode=%d offset=%d\n", mode, offset); - switch(mode) { - case CODEC_READ_OP: /* decrypt */ - if(pgno == 1) /* copy initial part of file header or SQLite magic to buffer */ - memcpy(buffer, plaintext_header_sz ? pData : (void *) SQLITE_FILE_HEADER, offset); - - rc = sqlcipher_page_cipher(ctx, cctx, pgno, CIPHER_DECRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset); -#ifdef SQLCIPHER_TEST - if(cipher_fail_next_decrypt) rc = SQLITE_ERROR; +#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS + "OMIT_SCHEMA_VERSION_PRAGMAS", #endif - if(rc != SQLITE_OK) { /* clear results of failed cipher operation and set error */ - sqlcipher_memset((unsigned char*) buffer+offset, 0, page_sz-offset); - sqlcipher_codec_ctx_set_error(ctx, rc); - } - memcpy(pData, buffer, page_sz); /* copy buffer data back to pData and return */ - return pData; - break; - - case CODEC_WRITE_OP: /* encrypt database page, operate on write context and fall through to case 7, so the write context is used*/ - cctx = CIPHER_WRITE_CTX; - - case CODEC_JOURNAL_OP: /* encrypt journal page, operate on read context use to get the original page data from the database */ - if(pgno == 1) { /* copy initial part of file header or salt to buffer */ - void *kdf_salt = NULL; - /* retrieve the kdf salt */ - if((rc = sqlcipher_codec_ctx_get_kdf_salt(ctx, &kdf_salt)) != SQLITE_OK) { - sqlcipher_codec_ctx_set_error(ctx, rc); - return NULL; - } - memcpy(buffer, plaintext_header_sz ? pData : kdf_salt, offset); - } - rc = sqlcipher_page_cipher(ctx, cctx, pgno, CIPHER_ENCRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset); -#ifdef SQLCIPHER_TEST - if(cipher_fail_next_encrypt) rc = SQLITE_ERROR; +#ifdef SQLITE_OMIT_SHARED_CACHE + "OMIT_SHARED_CACHE", #endif - if(rc != SQLITE_OK) { /* clear results of failed cipher operation and set error */ - sqlcipher_memset((unsigned char*)buffer+offset, 0, page_sz-offset); - sqlcipher_codec_ctx_set_error(ctx, rc); - return NULL; - } - return buffer; /* return persistent buffer data, pData remains intact */ - break; - - default: - sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); /* unsupported mode, set error */ - return pData; - break; - } -} - -static void sqlcipher_sqlite3FreeCodecArg(void *pCodecArg) { - codec_ctx *ctx = (codec_ctx *) pCodecArg; - if(pCodecArg == NULL) return; - sqlcipher_codec_ctx_free(&ctx); /* wipe and free allocated memory for the context */ - sqlcipher_deactivate(); /* cleanup related structures, OpenSSL etc, when codec is detatched */ -} - -SQLITE_PRIVATE int sqlcipher_sqlite3CodecAttach(sqlcipher_sqlite3* db, int nDb, const void *zKey, int nKey) { - struct Db *pDb = &db->aDb[nDb]; - - CODEC_TRACE("sqlcipher_sqlite3CodecAttach: entered db=%p, nDb=%d zKey=%s, nKey=%d\n", db, nDb, (char *)zKey, nKey); - - - if(nKey && zKey && pDb->pBt) { - int rc; - Pager *pPager = pDb->pBt->pBt->pPager; - sqlcipher_sqlite3_file *fd; - codec_ctx *ctx; - - /* check if the sqlcipher_sqlite3_file is open, and if not force handle to NULL */ - if((fd = sqlcipher_sqlite3PagerFile(pPager))->pMethods == 0) fd = NULL; - - CODEC_TRACE("sqlcipher_sqlite3CodecAttach: calling sqlcipher_activate()\n"); - sqlcipher_activate(); /* perform internal initialization for sqlcipher */ - - CODEC_TRACE_MUTEX("sqlcipher_sqlite3CodecAttach: entering database mutex %p\n", db->mutex); - sqlcipher_sqlite3_mutex_enter(db->mutex); - CODEC_TRACE_MUTEX("sqlcipher_sqlite3CodecAttach: entered database mutex %p\n", db->mutex); - -#ifdef SQLCIPHER_EXT - if((rc = sqlcipher_sqlite3_set_authorizer(db, sqlcipher_license_authorizer, db)) != SQLITE_OK) { - sqlcipher_sqlite3_mutex_leave(db->mutex); - return rc; - } +#ifdef SQLITE_OMIT_SHUTDOWN_DIRECTORIES + "OMIT_SHUTDOWN_DIRECTORIES", +#endif +#ifdef SQLITE_OMIT_SUBQUERY + "OMIT_SUBQUERY", +#endif +#ifdef SQLITE_OMIT_TCL_VARIABLE + "OMIT_TCL_VARIABLE", +#endif +#ifdef SQLITE_OMIT_TEMPDB + "OMIT_TEMPDB", +#endif +#ifdef SQLITE_OMIT_TEST_CONTROL + "OMIT_TEST_CONTROL", +#endif +#ifdef SQLITE_OMIT_TRACE +# if SQLITE_OMIT_TRACE != 1 + "OMIT_TRACE=" CTIMEOPT_VAL(SQLITE_OMIT_TRACE), +# endif +#endif +#ifdef SQLITE_OMIT_TRIGGER + "OMIT_TRIGGER", +#endif +#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION + "OMIT_TRUNCATE_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_UTF16 + "OMIT_UTF16", +#endif +#ifdef SQLITE_OMIT_VACUUM + "OMIT_VACUUM", +#endif +#ifdef SQLITE_OMIT_VIEW + "OMIT_VIEW", +#endif +#ifdef SQLITE_OMIT_VIRTUALTABLE + "OMIT_VIRTUALTABLE", +#endif +#ifdef SQLITE_OMIT_WAL + "OMIT_WAL", +#endif +#ifdef SQLITE_OMIT_WSD + "OMIT_WSD", +#endif +#ifdef SQLITE_OMIT_XFER_OPT + "OMIT_XFER_OPT", +#endif +#ifdef SQLITE_PCACHE_SEPARATE_HEADER + "PCACHE_SEPARATE_HEADER", +#endif +#ifdef SQLITE_PERFORMANCE_TRACE + "PERFORMANCE_TRACE", +#endif +#ifdef SQLITE_POWERSAFE_OVERWRITE +# if SQLITE_POWERSAFE_OVERWRITE != 1 + "POWERSAFE_OVERWRITE=" CTIMEOPT_VAL(SQLITE_POWERSAFE_OVERWRITE), +# endif +#endif +#ifdef SQLITE_PREFER_PROXY_LOCKING + "PREFER_PROXY_LOCKING", +#endif +#ifdef SQLITE_PROXY_DEBUG + "PROXY_DEBUG", +#endif +#ifdef SQLITE_REVERSE_UNORDERED_SELECTS + "REVERSE_UNORDERED_SELECTS", +#endif +#ifdef SQLITE_RTREE_INT_ONLY + "RTREE_INT_ONLY", +#endif +#ifdef SQLITE_SECURE_DELETE + "SECURE_DELETE", +#endif +#ifdef SQLITE_SMALL_STACK + "SMALL_STACK", +#endif +#ifdef SQLITE_SORTER_PMASZ + "SORTER_PMASZ=" CTIMEOPT_VAL(SQLITE_SORTER_PMASZ), +#endif +#ifdef SQLITE_SOUNDEX + "SOUNDEX", +#endif +#ifdef SQLITE_STAT4_SAMPLES + "STAT4_SAMPLES=" CTIMEOPT_VAL(SQLITE_STAT4_SAMPLES), +#endif +#ifdef SQLITE_STMTJRNL_SPILL + "STMTJRNL_SPILL=" CTIMEOPT_VAL(SQLITE_STMTJRNL_SPILL), +#endif +#ifdef SQLITE_SUBSTR_COMPATIBILITY + "SUBSTR_COMPATIBILITY", +#endif +#if (!defined(SQLITE_WIN32_MALLOC) \ + && !defined(SQLITE_ZERO_MALLOC) \ + && !defined(SQLITE_MEMDEBUG) \ + ) || defined(SQLITE_SYSTEM_MALLOC) + "SYSTEM_MALLOC", +#endif +#ifdef SQLITE_TCL + "TCL", +#endif +#ifdef SQLITE_TEMP_STORE + "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), +#endif +#ifdef SQLITE_TEST + "TEST", +#endif +#if defined(SQLITE_THREADSAFE) + "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), +#elif defined(THREADSAFE) + "THREADSAFE=" CTIMEOPT_VAL(THREADSAFE), +#else + "THREADSAFE=1", +#endif +#ifdef SQLITE_UNLINK_AFTER_CLOSE + "UNLINK_AFTER_CLOSE", +#endif +#ifdef SQLITE_UNTESTABLE + "UNTESTABLE", +#endif +#ifdef SQLITE_USER_AUTHENTICATION + "USER_AUTHENTICATION", +#endif +#ifdef SQLITE_USE_ALLOCA + "USE_ALLOCA", +#endif +#ifdef SQLITE_USE_FCNTL_TRACE + "USE_FCNTL_TRACE", +#endif +#ifdef SQLITE_USE_URI + "USE_URI", +#endif +#ifdef SQLITE_VDBE_COVERAGE + "VDBE_COVERAGE", +#endif +#ifdef SQLITE_WIN32_MALLOC + "WIN32_MALLOC", +#endif +#ifdef SQLITE_ZERO_MALLOC + "ZERO_MALLOC", #endif - /* point the internal codec argument against the contet to be prepared */ - CODEC_TRACE("sqlcipher_sqlite3CodecAttach: calling sqlcipher_codec_ctx_init()\n"); - rc = sqlcipher_codec_ctx_init(&ctx, pDb, pDb->pBt->pBt->pPager, zKey, nKey); - - if(rc != SQLITE_OK) { - /* initialization failed, do not attach potentially corrupted context */ - CODEC_TRACE("sqlcipher_sqlite3CodecAttach: context initialization failed with rc=%d\n", rc); - /* force an error at the pager level, such that even the upstream caller ignores the return code - the pager will be in an error state and will process no further operations */ - sqlcipher_sqlite3pager_error(pPager, rc); - pDb->pBt->pBt->db->errCode = rc; - CODEC_TRACE_MUTEX("sqlcipher_sqlite3CodecAttach: leaving database mutex %p (early return on rc=%d)\n", db->mutex, rc); - sqlcipher_sqlite3_mutex_leave(db->mutex); - CODEC_TRACE_MUTEX("sqlcipher_sqlite3CodecAttach: left database mutex %p (early return on rc=%d)\n", db->mutex, rc); - return rc; - } - - CODEC_TRACE("sqlcipher_sqlite3CodecAttach: calling sqlcipher_sqlite3PagerSetCodec()\n"); - sqlcipher_sqlite3PagerSetCodec(sqlcipher_sqlite3BtreePager(pDb->pBt), sqlcipher_sqlite3Codec, NULL, sqlcipher_sqlite3FreeCodecArg, (void *) ctx); - - CODEC_TRACE("sqlcipher_sqlite3CodecAttach: calling codec_set_btree_to_codec_pagesize()\n"); - codec_set_btree_to_codec_pagesize(db, pDb, ctx); - - /* force secure delete. This has the benefit of wiping internal data when deleted - and also ensures that all pages are written to disk (i.e. not skipped by - sqlcipher_sqlite3PagerDontWrite optimizations) */ - CODEC_TRACE("sqlcipher_sqlite3CodecAttach: calling sqlcipher_sqlite3BtreeSecureDelete()\n"); - sqlcipher_sqlite3BtreeSecureDelete(pDb->pBt, 1); - - /* if fd is null, then this is an in-memory database and - we dont' want to overwrite the AutoVacuum settings - if not null, then set to the default */ - if(fd != NULL) { - CODEC_TRACE("sqlcipher_sqlite3CodecAttach: calling sqlcipher_sqlite3BtreeSetAutoVacuum()\n"); - sqlcipher_sqlite3BtreeSetAutoVacuum(pDb->pBt, SQLITE_DEFAULT_AUTOVACUUM); - } - CODEC_TRACE_MUTEX("sqlcipher_sqlite3CodecAttach: leaving database mutex %p\n", db->mutex); - sqlcipher_sqlite3_mutex_leave(db->mutex); - CODEC_TRACE_MUTEX("sqlcipher_sqlite3CodecAttach: left database mutex %p\n", db->mutex); - } - return SQLITE_OK; -} - -int sqlcipher_find_db_index(sqlcipher_sqlite3 *db, const char *zDb) { - int db_index; - if(zDb == NULL){ - return 0; - } - for(db_index = 0; db_index < db->nDb; db_index++) { - struct Db *pDb = &db->aDb[db_index]; - if(strcmp(pDb->zDbSName, zDb) == 0) { - return db_index; - } - } - return 0; -} - -SQLITE_API void sqlcipher_sqlite3_activate_see(const char* in) { - /* do nothing, security enhancements are always active */ -} - -SQLITE_API int sqlcipher_sqlite3_key(sqlcipher_sqlite3 *db, const void *pKey, int nKey) { - CODEC_TRACE("sqlcipher_sqlite3_key entered: db=%p pKey=%s nKey=%d\n", db, (char *)pKey, nKey); - return sqlcipher_sqlite3_key_v2(db, "main", pKey, nKey); -} - -SQLITE_API int sqlcipher_sqlite3_key_v2(sqlcipher_sqlite3 *db, const char *zDb, const void *pKey, int nKey) { - CODEC_TRACE("sqlcipher_sqlite3_key_v2: entered db=%p zDb=%s pKey=%s nKey=%d\n", db, zDb, (char *)pKey, nKey); - /* attach key if db and pKey are not null and nKey is > 0 */ - if(db && pKey && nKey) { - int db_index = sqlcipher_find_db_index(db, zDb); - return sqlcipher_sqlite3CodecAttach(db, db_index, pKey, nKey); - } - return SQLITE_ERROR; -} - -SQLITE_API int sqlcipher_sqlite3_rekey(sqlcipher_sqlite3 *db, const void *pKey, int nKey) { - CODEC_TRACE("sqlcipher_sqlite3_rekey entered: db=%p pKey=%s nKey=%d\n", db, (char *)pKey, nKey); - return sqlcipher_sqlite3_rekey_v2(db, "main", pKey, nKey); -} - -/* sqlcipher_sqlite3_rekey_v2 -** Given a database, this will reencrypt the database using a new key. -** There is only one possible modes of operation - to encrypt a database -** that is already encrpyted. If the database is not already encrypted -** this should do nothing -** The proposed logic for this function follows: -** 1. Determine if the database is already encryptped -** 2. If there is NOT already a key present do nothing -** 3. If there is a key present, re-encrypt the database with the new key -*/ -SQLITE_API int sqlcipher_sqlite3_rekey_v2(sqlcipher_sqlite3 *db, const char *zDb, const void *pKey, int nKey) { - CODEC_TRACE("sqlcipher_sqlite3_rekey_v2: entered db=%p zDb=%s pKey=%s, nKey=%d\n", db, zDb, (char *)pKey, nKey); - if(db && pKey && nKey) { - int db_index = sqlcipher_find_db_index(db, zDb); - struct Db *pDb = &db->aDb[db_index]; - CODEC_TRACE("sqlcipher_sqlite3_rekey_v2: database pDb=%p db_index:%d\n", pDb, db_index); - if(pDb->pBt) { - codec_ctx *ctx; - int rc, page_count; - Pgno pgno; - PgHdr *page; - Pager *pPager = pDb->pBt->pBt->pPager; - - ctx = (codec_ctx*) sqlcipher_sqlite3PagerGetCodec(pDb->pBt->pBt->pPager); - - if(ctx == NULL) { - /* there was no codec attached to this database, so this should do nothing! */ - CODEC_TRACE("sqlcipher_sqlite3_rekey_v2: no codec attached to db, exiting\n"); - return SQLITE_OK; - } - - CODEC_TRACE_MUTEX("sqlcipher_sqlite3_rekey_v2: entering database mutex %p\n", db->mutex); - sqlcipher_sqlite3_mutex_enter(db->mutex); - CODEC_TRACE_MUTEX("sqlcipher_sqlite3_rekey_v2: entered database mutex %p\n", db->mutex); - - codec_set_pass_key(db, db_index, pKey, nKey, CIPHER_WRITE_CTX); - - /* do stuff here to rewrite the database - ** 1. Create a transaction on the database - ** 2. Iterate through each page, reading it and then writing it. - ** 3. If that goes ok then commit and put ctx->rekey into ctx->key - ** note: don't deallocate rekey since it may be used in a subsequent iteration - */ - rc = sqlcipher_sqlite3BtreeBeginTrans(pDb->pBt, 1, 0); /* begin write transaction */ - sqlcipher_sqlite3PagerPagecount(pPager, &page_count); - for(pgno = 1; rc == SQLITE_OK && pgno <= (unsigned int)page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */ - if(!sqlcipher_sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */ - rc = sqlcipher_sqlite3PagerGet(pPager, pgno, &page, 0); - if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */ - rc = sqlcipher_sqlite3PagerWrite(page); - if(rc == SQLITE_OK) { - sqlcipher_sqlite3PagerUnref(page); - } else { - CODEC_TRACE("sqlcipher_sqlite3_rekey_v2: error %d occurred writing page %d\n", rc, pgno); - } - } else { - CODEC_TRACE("sqlcipher_sqlite3_rekey_v2: error %d occurred getting page %d\n", rc, pgno); - } - } - } - - /* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */ - if(rc == SQLITE_OK) { - CODEC_TRACE("sqlcipher_sqlite3_rekey_v2: committing\n"); - rc = sqlcipher_sqlite3BtreeCommit(pDb->pBt); - sqlcipher_codec_key_copy(ctx, CIPHER_WRITE_CTX); - } else { - CODEC_TRACE("sqlcipher_sqlite3_rekey_v2: rollback\n"); - sqlcipher_sqlite3BtreeRollback(pDb->pBt, SQLITE_ABORT_ROLLBACK, 0); - } - - CODEC_TRACE_MUTEX("sqlcipher_sqlite3_rekey_v2: leaving database mutex %p\n", db->mutex); - sqlcipher_sqlite3_mutex_leave(db->mutex); - CODEC_TRACE_MUTEX("sqlcipher_sqlite3_rekey_v2: left database mutex %p\n", db->mutex); - } - return SQLITE_OK; - } - return SQLITE_ERROR; -} - -SQLITE_PRIVATE void sqlcipher_sqlite3CodecGetKey(sqlcipher_sqlite3* db, int nDb, void **zKey, int *nKey) { - struct Db *pDb = &db->aDb[nDb]; - CODEC_TRACE("sqlcipher_sqlite3CodecGetKey: entered db=%p, nDb=%d\n", db, nDb); - if( pDb->pBt ) { - codec_ctx *ctx = (codec_ctx*) sqlcipher_sqlite3PagerGetCodec(pDb->pBt->pBt->pPager); +} ; - if(ctx) { - /* pass back the keyspec from the codec, unless PRAGMA cipher_store_pass - is set or keyspec has not yet been derived, in which case pass - back the password key material */ - sqlcipher_codec_get_keyspec(ctx, zKey, nKey); - if(sqlcipher_codec_get_store_pass(ctx) == 1 || *zKey == NULL) { - sqlcipher_codec_get_pass(ctx, zKey, nKey); - } - } else { - *zKey = NULL; - *nKey = 0; - } - } +SQLITE_PRIVATE const char **sqlcipher_sqlite3CompileOptions(int *pnOpt){ + *pnOpt = sizeof(sqlcipher_sqlite3azCompileOpt) / sizeof(sqlcipher_sqlite3azCompileOpt[0]); + return (const char**)sqlcipher_sqlite3azCompileOpt; } -/* - * Implementation of an "export" function that allows a caller - * to duplicate the main database to an attached database. This is intended - * as a conveneince for users who need to: - * - * 1. migrate from an non-encrypted database to an encrypted database - * 2. move from an encrypted database to a non-encrypted database - * 3. convert beween the various flavors of encrypted databases. - * - * This implementation is based heavily on the procedure and code used - * in vacuum.c, but is exposed as a function that allows export to any - * named attached database. - */ +#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ +/************** End of ctime.c ***********************************************/ +/************** Begin file global.c ******************************************/ /* -** Finalize a prepared statement. If there was an error, store the -** text of the error message in *pzErrMsg. Return the result code. +** 2008 June 13 ** -** Based on vacuumFinalize from vacuum.c -*/ -static int sqlcipher_finalize(sqlcipher_sqlite3 *db, sqlcipher_sqlite3_stmt *pStmt, char **pzErrMsg){ - int rc; - rc = sqlcipher_sqlite3VdbeFinalize((Vdbe*)pStmt); - if( rc ){ - sqlcipher_sqlite3SetString(pzErrMsg, db, sqlcipher_sqlite3_errmsg(db)); - } - return rc; -} - -/* -** Execute zSql on database db. Return an error code. +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** Based on execSql from vacuum.c -*/ -static int sqlcipher_execSql(sqlcipher_sqlite3 *db, char **pzErrMsg, const char *zSql){ - sqlcipher_sqlite3_stmt *pStmt; - VVA_ONLY( int rc; ) - if( !zSql ){ - return SQLITE_NOMEM; - } - if( SQLITE_OK!=sqlcipher_sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){ - sqlcipher_sqlite3SetString(pzErrMsg, db, sqlcipher_sqlite3_errmsg(db)); - return sqlcipher_sqlite3_errcode(db); - } - VVA_ONLY( rc = ) sqlcipher_sqlite3_step(pStmt); - assert( rc!=SQLITE_ROW ); - return sqlcipher_finalize(db, pStmt, pzErrMsg); -} - -/* -** Execute zSql on database db. The statement returns exactly -** one column. Execute this as SQL on the same database. +** 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. ** -** Based on execExecSql from vacuum.c +************************************************************************* +** +** This file contains definitions of global variables and constants. */ -static int sqlcipher_execExecSql(sqlcipher_sqlite3 *db, char **pzErrMsg, const char *zSql){ - sqlcipher_sqlite3_stmt *pStmt; - int rc; - - rc = sqlcipher_sqlite3_prepare(db, zSql, -1, &pStmt, 0); - if( rc!=SQLITE_OK ) return rc; - - while( SQLITE_ROW==sqlcipher_sqlite3_step(pStmt) ){ - rc = sqlcipher_execSql(db, pzErrMsg, (char*)sqlcipher_sqlite3_column_text(pStmt, 0)); - if( rc!=SQLITE_OK ){ - sqlcipher_finalize(db, pStmt, pzErrMsg); - return rc; - } - } - - return sqlcipher_finalize(db, pStmt, pzErrMsg); -} +/* #include "sqliteInt.h" */ -/* - * copy database and schema from the main database to an attached database - * - * Based on sqlcipher_sqlite3RunVacuum from vacuum.c +/* An array to map all upper-case characters into their corresponding +** lower-case character. +** +** SQLite only considers US-ASCII (or EBCDIC) characters. We do not +** handle case conversions for the UTF character set since the tables +** involved are nearly as big or bigger than SQLite itself. */ -void sqlcipher_exportFunc(sqlcipher_sqlite3_context *context, int argc, sqlcipher_sqlite3_value **argv) { - sqlcipher_sqlite3 *db = sqlcipher_sqlite3_context_db_handle(context); - const char* targetDb, *sourceDb; - int targetDb_idx = 0; - u64 saved_flags = db->flags; /* Saved value of the db->flags */ - u32 saved_mDbFlags = db->mDbFlags; /* Saved value of the db->mDbFlags */ - int saved_nChange = db->nChange; /* Saved value of db->nChange */ - int saved_nTotalChange = db->nTotalChange; /* Saved value of db->nTotalChange */ - u8 saved_mTrace = db->mTrace; /* Saved value of db->mTrace */ - int rc = SQLITE_OK; /* Return code from service routines */ - char *zSql = NULL; /* SQL statements */ - char *pzErrMsg = NULL; - - if(argc != 1 && argc != 2) { - rc = SQLITE_ERROR; - pzErrMsg = sqlcipher_sqlite3_mprintf("invalid number of arguments (%d) passed to sqlcipher_export", argc); - goto end_of_export; - } - - if(sqlcipher_sqlite3_value_type(argv[0]) == SQLITE_NULL) { - rc = SQLITE_ERROR; - pzErrMsg = sqlcipher_sqlite3_mprintf("target database can't be NULL"); - goto end_of_export; - } - - targetDb = (const char*) sqlcipher_sqlite3_value_text(argv[0]); - sourceDb = "main"; - - if(argc == 2) { - if(sqlcipher_sqlite3_value_type(argv[1]) == SQLITE_NULL) { - rc = SQLITE_ERROR; - pzErrMsg = sqlcipher_sqlite3_mprintf("target database can't be NULL"); - goto end_of_export; - } - sourceDb = (char *) sqlcipher_sqlite3_value_text(argv[1]); - } - - - /* if the name of the target is not main, but the index returned is zero - there is a mismatch and we should not proceed */ - targetDb_idx = sqlcipher_find_db_index(db, targetDb); - if(targetDb_idx == 0 && targetDb != NULL && sqlcipher_sqlite3StrICmp("main", targetDb) != 0) { - rc = SQLITE_ERROR; - pzErrMsg = sqlcipher_sqlite3_mprintf("unknown database %s", targetDb); - goto end_of_export; - } - db->init.iDb = targetDb_idx; - - db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; - db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; - db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_Defensive | SQLITE_CountRows); - db->mTrace = 0; - - /* Query the schema of the main database. Create a mirror schema - ** in the temporary database. - */ - zSql = sqlcipher_sqlite3_mprintf( - "SELECT sql " - " FROM %s.sqlite_schema WHERE type='table' AND name!='sqlite_sequence'" - " AND rootpage>0" - , sourceDb); - rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql); - if( rc!=SQLITE_OK ) goto end_of_export; - sqlcipher_sqlite3_free(zSql); - - zSql = sqlcipher_sqlite3_mprintf( - "SELECT sql " - " FROM %s.sqlite_schema WHERE sql LIKE 'CREATE INDEX %%' " - , sourceDb); - rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql); - if( rc!=SQLITE_OK ) goto end_of_export; - sqlcipher_sqlite3_free(zSql); - - zSql = sqlcipher_sqlite3_mprintf( - "SELECT sql " - " FROM %s.sqlite_schema WHERE sql LIKE 'CREATE UNIQUE INDEX %%'" - , sourceDb); - rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql); - if( rc!=SQLITE_OK ) goto end_of_export; - sqlcipher_sqlite3_free(zSql); - - /* Loop through the tables in the main database. For each, do - ** an "INSERT INTO rekey_db.xxx SELECT * FROM main.xxx;" to copy - ** the contents to the temporary database. - */ - zSql = sqlcipher_sqlite3_mprintf( - "SELECT 'INSERT INTO %s.' || quote(name) " - "|| ' SELECT * FROM %s.' || quote(name) || ';'" - "FROM %s.sqlite_schema " - "WHERE type = 'table' AND name!='sqlite_sequence' " - " AND rootpage>0" - , targetDb, sourceDb, sourceDb); - rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql); - if( rc!=SQLITE_OK ) goto end_of_export; - sqlcipher_sqlite3_free(zSql); - - /* Copy over the contents of the sequence table - */ - zSql = sqlcipher_sqlite3_mprintf( - "SELECT 'INSERT INTO %s.' || quote(name) " - "|| ' SELECT * FROM %s.' || quote(name) || ';' " - "FROM %s.sqlite_schema WHERE name=='sqlite_sequence';" - , targetDb, sourceDb, targetDb); - rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql); - if( rc!=SQLITE_OK ) goto end_of_export; - sqlcipher_sqlite3_free(zSql); - - /* Copy the triggers, views, and virtual tables from the main database - ** over to the temporary database. None of these objects has any - ** associated storage, so all we have to do is copy their entries - ** from the SQLITE_MASTER table. - */ - zSql = sqlcipher_sqlite3_mprintf( - "INSERT INTO %s.sqlite_schema " - " SELECT type, name, tbl_name, rootpage, sql" - " FROM %s.sqlite_schema" - " WHERE type='view' OR type='trigger'" - " OR (type='table' AND rootpage=0)" - , targetDb, sourceDb); - rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execSql(db, &pzErrMsg, zSql); - if( rc!=SQLITE_OK ) goto end_of_export; - sqlcipher_sqlite3_free(zSql); - - zSql = NULL; -end_of_export: - db->init.iDb = 0; - db->flags = saved_flags; - db->mDbFlags = saved_mDbFlags; - db->nChange = saved_nChange; - db->nTotalChange = saved_nTotalChange; - db->mTrace = saved_mTrace; - - if(zSql) sqlcipher_sqlite3_free(zSql); - - if(rc) { - if(pzErrMsg != NULL) { - sqlcipher_sqlite3_result_error(context, pzErrMsg, -1); - sqlcipher_sqlite3DbFree(db, pzErrMsg); - } else { - sqlcipher_sqlite3_result_error(context, sqlcipher_sqlite3ErrStr(rc), -1); - } - } -} +SQLITE_PRIVATE const unsigned char sqlcipher_sqlite3UpperToLower[] = { +#ifdef SQLITE_ASCII + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, + 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107, + 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, + 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, + 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, + 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, + 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, + 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, + 252,253,254,255, #endif -/* END SQLCIPHER */ +#ifdef SQLITE_EBCDIC + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */ + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */ + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */ + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */ + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */ + 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 6x */ + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 7x */ + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */ + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 9x */ + 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */ + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */ + 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */ + 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */ + 224,225,162,163,164,165,166,167,168,169,234,235,236,237,238,239, /* Ex */ + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* Fx */ +#endif +/* All of the upper-to-lower conversion data is above. The following +** 18 integers are completely unrelated. They are appended to the +** sqlcipher_sqlite3UpperToLower[] array to avoid UBSAN warnings. Here's what is +** going on: +** +** The SQL comparison operators (<>, =, >, <=, <, and >=) are implemented +** by invoking sqlcipher_sqlite3MemCompare(A,B) which compares values A and B and +** returns negative, zero, or positive if A is less then, equal to, or +** greater than B, respectively. Then the true false results is found by +** consulting sqlcipher_sqlite3aLTb[opcode], sqlcipher_sqlite3aEQb[opcode], or +** sqlcipher_sqlite3aGTb[opcode] depending on whether the result of compare(A,B) +** is negative, zero, or positive, where opcode is the specific opcode. +** The only works because the comparison opcodes are consecutive and in +** this order: NE EQ GT LE LT GE. Various assert()s throughout the code +** ensure that is the case. +** +** These elements must be appended to another array. Otherwise the +** index (here shown as [256-OP_Ne]) would be out-of-bounds and thus +** be undefined behavior. That's goofy, but the C-standards people thought +** it was a good idea, so here we are. +*/ +/* NE EQ GT LE LT GE */ + 1, 0, 0, 1, 1, 0, /* aLTb[]: Use when compare(A,B) less than zero */ + 0, 1, 0, 1, 0, 1, /* aEQb[]: Use when compare(A,B) equals zero */ + 1, 0, 1, 0, 0, 1 /* aGTb[]: Use when compare(A,B) greater than zero*/ +}; +SQLITE_PRIVATE const unsigned char *sqlcipher_sqlite3aLTb = &sqlcipher_sqlite3UpperToLower[256-OP_Ne]; +SQLITE_PRIVATE const unsigned char *sqlcipher_sqlite3aEQb = &sqlcipher_sqlite3UpperToLower[256+6-OP_Ne]; +SQLITE_PRIVATE const unsigned char *sqlcipher_sqlite3aGTb = &sqlcipher_sqlite3UpperToLower[256+12-OP_Ne]; -/************** End of crypto.c **********************************************/ -/************** Begin file crypto_impl.c *************************************/ /* -** SQLCipher -** http://sqlcipher.net +** The following 256 byte lookup table is used to support SQLites built-in +** equivalents to the following standard library functions: ** -** Copyright (c) 2008 - 2013, ZETETIC LLC -** All rights reserved. +** isspace() 0x01 +** isalpha() 0x02 +** isdigit() 0x04 +** isalnum() 0x06 +** isxdigit() 0x08 +** toupper() 0x20 +** SQLite identifier character 0x40 +** Quote character 0x80 ** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of the ZETETIC LLC nor the -** names of its contributors may be used to endorse or promote products -** derived from this software without specific prior written permission. +** Bit 0x20 is set if the mapped character requires translation to upper +** case. i.e. if the character is a lower-case ASCII character. +** If x is a lower-case ASCII character, then its upper-case equivalent +** is (x - 0x20). Therefore toupper() can be implemented as: ** -** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY -** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY -** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** (x & ~(map[x]&0x20)) +** +** The equivalent of tolower() is implemented using the sqlcipher_sqlite3UpperToLower[] +** array. tolower() is used more often than toupper() by SQLite. ** +** Bit 0x40 is set if the character is non-alphanumeric and can be used in an +** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any +** non-ASCII UTF character. Hence the test for whether or not a character is +** part of an identifier is 0x46. */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - -/* #include "sqlcipher.h" */ -/* #include "crypto.h" */ -#ifndef OMIT_MEMLOCK -#if defined(__unix__) || defined(__APPLE__) || defined(_AIX) -#include -#include -#include -#include -#elif defined(_WIN32) -#include -#endif -#endif - -static volatile unsigned int default_flags = DEFAULT_CIPHER_FLAGS; -static volatile unsigned char hmac_salt_mask = HMAC_SALT_MASK; -static volatile int default_kdf_iter = PBKDF2_ITER; -static volatile int default_page_size = 4096; -static volatile int default_plaintext_header_sz = 0; -static volatile int default_hmac_algorithm = SQLCIPHER_HMAC_SHA512; -static volatile int default_kdf_algorithm = SQLCIPHER_PBKDF2_HMAC_SHA512; -static volatile int mem_security_on = 1; -static volatile int mem_security_initialized = 0; -static volatile int mem_security_activated = 0; -static volatile unsigned int sqlcipher_activate_count = 0; -static volatile sqlcipher_sqlite3_mem_methods default_mem_methods; -static sqlcipher_provider *default_provider = NULL; - -static sqlcipher_sqlite3_mutex* sqlcipher_static_mutex[SQLCIPHER_MUTEX_COUNT]; +SQLITE_PRIVATE const unsigned char sqlcipher_sqlite3CtypeMap[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */ + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */ + 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */ + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */ + 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */ -sqlcipher_sqlite3_mutex* sqlcipher_mutex(int mutex) { - if(mutex < 0 || mutex >= SQLCIPHER_MUTEX_COUNT) return NULL; - return sqlcipher_static_mutex[mutex]; -} + 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */ + 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */ + 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */ + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */ + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */ + 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */ -static int sqlcipher_mem_init(void *pAppData) { - return default_mem_methods.xInit(pAppData); -} -static void sqlcipher_mem_shutdown(void *pAppData) { - default_mem_methods.xShutdown(pAppData); -} -static void *sqlcipher_mem_malloc(int n) { - void *ptr = default_mem_methods.xMalloc(n); - if(mem_security_on) { - CODEC_TRACE_MEMORY("sqlcipher_mem_malloc: calling sqlcipher_mlock(%p,%d)\n", ptr, n); - sqlcipher_mlock(ptr, n); - if(!mem_security_activated) mem_security_activated = 1; - } - return ptr; -} -static int sqlcipher_mem_size(void *p) { - return default_mem_methods.xSize(p); -} -static void sqlcipher_mem_free(void *p) { - int sz; - if(mem_security_on) { - sz = sqlcipher_mem_size(p); - CODEC_TRACE_MEMORY("sqlcipher_mem_free: calling sqlcipher_memset(%p,0,%d) and sqlcipher_munlock(%p, %d) \n", p, sz, p, sz); - sqlcipher_memset(p, 0, sz); - sqlcipher_munlock(p, sz); - if(!mem_security_activated) mem_security_activated = 1; - } - default_mem_methods.xFree(p); -} -static void *sqlcipher_mem_realloc(void *p, int n) { - return default_mem_methods.xRealloc(p, n); -} -static int sqlcipher_mem_roundup(int n) { - return default_mem_methods.xRoundup(n); -} + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */ -static sqlcipher_sqlite3_mem_methods sqlcipher_mem_methods = { - sqlcipher_mem_malloc, - sqlcipher_mem_free, - sqlcipher_mem_realloc, - sqlcipher_mem_size, - sqlcipher_mem_roundup, - sqlcipher_mem_init, - sqlcipher_mem_shutdown, - 0 + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ }; -void sqlcipher_init_memmethods() { - if(mem_security_initialized) return; - if(sqlcipher_sqlite3_config(SQLITE_CONFIG_GETMALLOC, &default_mem_methods) != SQLITE_OK || - sqlcipher_sqlite3_config(SQLITE_CONFIG_MALLOC, &sqlcipher_mem_methods) != SQLITE_OK) { - mem_security_on = mem_security_activated = 0; - } - mem_security_initialized = 1; -} - -int sqlcipher_register_provider(sqlcipher_provider *p) { - CODEC_TRACE_MUTEX("sqlcipher_register_provider: entering SQLCIPHER_MUTEX_PROVIDER\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); - CODEC_TRACE_MUTEX("sqlcipher_register_provider: entered SQLCIPHER_MUTEX_PROVIDER\n"); - - if(default_provider != NULL && default_provider != p) { - /* only free the current registerd provider if it has been initialized - and it isn't a pointer to the same provider passed to the function - (i.e. protect against a caller calling register twice for the same provider) */ - sqlcipher_free(default_provider, sizeof(sqlcipher_provider)); - } - default_provider = p; - CODEC_TRACE_MUTEX("sqlcipher_register_provider: leaving SQLCIPHER_MUTEX_PROVIDER\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); - CODEC_TRACE_MUTEX("sqlcipher_register_provider: left SQLCIPHER_MUTEX_PROVIDER\n"); - - return SQLITE_OK; -} - -/* return a pointer to the currently registered provider. This will - allow an application to fetch the current registered provider and - make minor changes to it */ -sqlcipher_provider* sqlcipher_get_provider() { - return default_provider; -} - -void sqlcipher_activate() { - CODEC_TRACE_MUTEX("sqlcipher_activate: entering static master mutex\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - CODEC_TRACE_MUTEX("sqlcipher_activate: entered static master mutex\n"); - - /* allocate new mutexes */ - if(sqlcipher_activate_count == 0) { - int i; - for(i = 0; i < SQLCIPHER_MUTEX_COUNT; i++) { - sqlcipher_static_mutex[i] = sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - } - } +/* EVIDENCE-OF: R-02982-34736 In order to maintain full backwards +** compatibility for legacy applications, the URI filename capability is +** disabled by default. +** +** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled +** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options. +** +** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally +** disabled. The default value may be changed by compiling with the +** SQLITE_USE_URI symbol defined. +** +** URI filenames are enabled by default if SQLITE_HAS_CODEC is +** enabled. +*/ +#ifndef SQLITE_USE_URI +/* BEGIN SQLCIPHER */ +# ifdef SQLITE_HAS_CODEC +# define SQLITE_USE_URI 1 +# else +# define SQLITE_USE_URI 0 +# endif +/* END SQLCIPHER */ +#endif - /* check to see if there is a provider registered at this point - if there no provider registered at this point, register the - default provider */ - if(sqlcipher_get_provider() == NULL) { - sqlcipher_provider *p = sqlcipher_malloc(sizeof(sqlcipher_provider)); -#if defined (SQLCIPHER_CRYPTO_CC) - extern int sqlcipher_cc_setup(sqlcipher_provider *p); - sqlcipher_cc_setup(p); -#elif defined (SQLCIPHER_CRYPTO_LIBTOMCRYPT) - extern int sqlcipher_ltc_setup(sqlcipher_provider *p); - sqlcipher_ltc_setup(p); -#elif defined (SQLCIPHER_CRYPTO_NSS) - extern int sqlcipher_nss_setup(sqlcipher_provider *p); - sqlcipher_nss_setup(p); -#elif defined (SQLCIPHER_CRYPTO_OPENSSL) - extern int sqlcipher_openssl_setup(sqlcipher_provider *p); - sqlcipher_openssl_setup(p); +/* EVIDENCE-OF: R-38720-18127 The default setting is determined by the +** SQLITE_ALLOW_COVERING_INDEX_SCAN compile-time option, or is "on" if +** that compile-time option is omitted. +*/ +#if !defined(SQLITE_ALLOW_COVERING_INDEX_SCAN) +# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 #else -#error "NO DEFAULT SQLCIPHER CRYPTO PROVIDER DEFINED" -#endif - CODEC_TRACE("sqlcipher_activate: calling sqlcipher_register_provider(%p)\n", p); -#ifdef SQLCIPHER_EXT - sqlcipher_ext_provider_setup(p); +# if !SQLITE_ALLOW_COVERING_INDEX_SCAN +# error "Compile-time disabling of covering index scan using the\ + -DSQLITE_ALLOW_COVERING_INDEX_SCAN=0 option is deprecated.\ + Contact SQLite developers if this is a problem for you, and\ + delete this #error macro to continue with your build." +# endif #endif - sqlcipher_register_provider(p); - CODEC_TRACE("sqlcipher_activate: called sqlcipher_register_provider(%p)\n",p); - } - - sqlcipher_activate_count++; /* increment activation count */ - - CODEC_TRACE_MUTEX("sqlcipher_activate: leaving static master mutex\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - CODEC_TRACE_MUTEX("sqlcipher_activate: left static master mutex\n"); -} - -void sqlcipher_deactivate() { - CODEC_TRACE_MUTEX("sqlcipher_deactivate: entering static master mutex\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - CODEC_TRACE_MUTEX("sqlcipher_deactivate: entered static master mutex\n"); - sqlcipher_activate_count--; - /* if no connections are using sqlcipher, cleanup globals */ - if(sqlcipher_activate_count < 1) { - - CODEC_TRACE_MUTEX("sqlcipher_deactivate: entering SQLCIPHER_MUTEX_PROVIDER\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); - CODEC_TRACE_MUTEX("sqlcipher_deactivate: entered SQLCIPHER_MUTEX_PROVIDER\n"); - - if(default_provider != NULL) { - sqlcipher_free(default_provider, sizeof(sqlcipher_provider)); - default_provider = NULL; - } - - CODEC_TRACE_MUTEX("sqlcipher_deactivate: leaving SQLCIPHER_MUTEX_PROVIDER\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); - CODEC_TRACE_MUTEX("sqlcipher_deactivate: left SQLCIPHER_MUTEX_PROVIDER\n"); - -#ifdef SQLCIPHER_EXT - sqlcipher_ext_provider_destroy(); +/* The minimum PMA size is set to this value multiplied by the database +** page size in bytes. +*/ +#ifndef SQLITE_SORTER_PMASZ +# define SQLITE_SORTER_PMASZ 250 #endif - /* last connection closed, free mutexes */ - if(sqlcipher_activate_count == 0) { - int i; - for(i = 0; i < SQLCIPHER_MUTEX_COUNT; i++) { - sqlcipher_sqlite3_mutex_free(sqlcipher_static_mutex[i]); - } - } - sqlcipher_activate_count = 0; /* reset activation count */ - } - - CODEC_TRACE_MUTEX("sqlcipher_deactivate: leaving static master mutex\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - CODEC_TRACE_MUTEX("sqlcipher_deactivate: left static master mutex\n"); -} - -/* constant time memset using volitile to avoid having the memset - optimized out by the compiler. - Note: As suggested by Joachim Schipper (joachim.schipper@fox-it.com) +/* Statement journals spill to disk when their size exceeds the following +** threshold (in bytes). 0 means that statement journals are created and +** written to disk immediately (the default behavior for SQLite versions +** before 3.12.0). -1 means always keep the entire statement journal in +** memory. (The statement journal is also always held entirely in memory +** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this +** setting.) */ -void* sqlcipher_memset(void *v, unsigned char value, u64 len) { - u64 i = 0; - volatile unsigned char *a = v; - - if (v == NULL) return v; - - CODEC_TRACE_MEMORY("sqlcipher_memset: setting %p[0-%llu]=%d)\n", a, len, value); - for(i = 0; i < len; i++) { - a[i] = value; - } - - return v; -} - -/* constant time memory check tests every position of a memory segement - matches a single value (i.e. the memory is all zeros) - returns 0 if match, 1 of no match */ -int sqlcipher_ismemset(const void *v, unsigned char value, u64 len) { - const unsigned char *a = v; - u64 i = 0, result = 0; - - for(i = 0; i < len; i++) { - result |= a[i] ^ value; - } - - return (result != 0); -} - -/* constant time memory comparison routine. - returns 0 if match, 1 if no match */ -int sqlcipher_memcmp(const void *v0, const void *v1, int len) { - const unsigned char *a0 = v0, *a1 = v1; - int i = 0, result = 0; - - for(i = 0; i < len; i++) { - result |= a0[i] ^ a1[i]; - } - - return (result != 0); -} +#ifndef SQLITE_STMTJRNL_SPILL +# define SQLITE_STMTJRNL_SPILL (64*1024) +#endif -void sqlcipher_mlock(void *ptr, u64 sz) { -#ifndef OMIT_MEMLOCK -#if defined(__unix__) || defined(__APPLE__) - int rc; - unsigned long pagesize = sysconf(_SC_PAGESIZE); - unsigned long offset = (unsigned long) ptr % pagesize; +/* +** The default lookaside-configuration, the format "SZ,N". SZ is the +** number of bytes in each lookaside slot (should be a multiple of 8) +** and N is the number of slots. The lookaside-configuration can be +** changed as start-time using sqlcipher_sqlite3_config(SQLITE_CONFIG_LOOKASIDE) +** or at run-time for an individual database connection using +** sqlcipher_sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE); +** +** With the two-size-lookaside enhancement, less lookaside is required. +** The default configuration of 1200,40 actually provides 30 1200-byte slots +** and 93 128-byte slots, which is more lookaside than is available +** using the older 1200,100 configuration without two-size-lookaside. +*/ +#ifndef SQLITE_DEFAULT_LOOKASIDE +# ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE +# define SQLITE_DEFAULT_LOOKASIDE 1200,100 /* 120KB of memory */ +# else +# define SQLITE_DEFAULT_LOOKASIDE 1200,40 /* 48KB of memory */ +# endif +#endif - if(ptr == NULL || sz == 0) return; - CODEC_TRACE_MEMORY("sqlcipher_mem_lock: calling mlock(%p,%lu); _SC_PAGESIZE=%lu\n", ptr - offset, sz + offset, pagesize); - rc = mlock(ptr - offset, sz + offset); - if(rc!=0) { - CODEC_TRACE_MEMORY("sqlcipher_mem_lock: mlock(%p,%lu) returned %d errno=%d\n", ptr - offset, sz + offset, rc, errno); - } -#elif defined(_WIN32) -#if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP)) - int rc; - CODEC_TRACE("sqlcipher_mem_lock: calling VirtualLock(%p,%d)\n", ptr, sz); - rc = VirtualLock(ptr, sz); - if(rc==0) { - CODEC_TRACE("sqlcipher_mem_lock: VirtualLock(%p,%d) returned %d LastError=%d\n", ptr, sz, rc, GetLastError()); - } +/* The default maximum size of an in-memory database created using +** sqlcipher_sqlite3_deserialize() +*/ +#ifndef SQLITE_MEMDB_DEFAULT_MAXSIZE +# define SQLITE_MEMDB_DEFAULT_MAXSIZE 1073741824 #endif + +/* +** The following singleton contains the global configuration for +** the SQLite library. +*/ +SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlcipher_sqlite3Config = { + SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ + 1, /* bCoreMutex */ + SQLITE_THREADSAFE==1, /* bFullMutex */ + SQLITE_USE_URI, /* bOpenUri */ + SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ + 0, /* bSmallMalloc */ + 1, /* bExtraSchemaChecks */ + 0x7ffffffe, /* mxStrlen */ + 0, /* neverCorrupt */ + SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ + SQLITE_STMTJRNL_SPILL, /* nStmtSpill */ + {0,0,0,0,0,0,0,0}, /* m */ + {0,0,0,0,0,0,0,0,0}, /* mutex */ + {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ + (void*)0, /* pHeap */ + 0, /* nHeap */ + 0, 0, /* mnHeap, mxHeap */ + SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */ + SQLITE_MAX_MMAP_SIZE, /* mxMmap */ + (void*)0, /* pPage */ + 0, /* szPage */ + SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */ + 0, /* mxParserStack */ + 0, /* sharedCacheEnabled */ + SQLITE_SORTER_PMASZ, /* szPma */ + /* All the rest should always be initialized to zero */ + 0, /* isInit */ + 0, /* inProgress */ + 0, /* isMutexInit */ + 0, /* isMallocInit */ + 0, /* isPCacheInit */ + 0, /* nRefInitMutex */ + 0, /* pInitMutex */ + 0, /* xLog */ + 0, /* pLogArg */ +#ifdef SQLITE_ENABLE_SQLLOG + 0, /* xSqllog */ + 0, /* pSqllogArg */ #endif +#ifdef SQLITE_VDBE_COVERAGE + 0, /* xVdbeBranch */ + 0, /* pVbeBranchArg */ #endif -} - -void sqlcipher_munlock(void *ptr, u64 sz) { -#ifndef OMIT_MEMLOCK -#if defined(__unix__) || defined(__APPLE__) - int rc; - unsigned long pagesize = sysconf(_SC_PAGESIZE); - unsigned long offset = (unsigned long) ptr % pagesize; - - if(ptr == NULL || sz == 0) return; - - CODEC_TRACE_MEMORY("sqlcipher_mem_unlock: calling munlock(%p,%lu)\n", ptr - offset, sz + offset); - rc = munlock(ptr - offset, sz + offset); - if(rc!=0) { - CODEC_TRACE_MEMORY("sqlcipher_mem_unlock: munlock(%p,%lu) returned %d errno=%d\n", ptr - offset, sz + offset, rc, errno); - } -#elif defined(_WIN32) -#if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP)) - int rc; - CODEC_TRACE("sqlcipher_mem_lock: calling VirtualUnlock(%p,%d)\n", ptr, sz); - rc = VirtualUnlock(ptr, sz); - if(!rc) { - CODEC_TRACE("sqlcipher_mem_unlock: VirtualUnlock(%p,%d) returned %d LastError=%d\n", ptr, sz, rc, GetLastError()); - } +#ifndef SQLITE_OMIT_DESERIALIZE + SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ #endif +#ifndef SQLITE_UNTESTABLE + 0, /* xTestCallback */ #endif + 0, /* bLocaltimeFault */ + 0, /* xAltLocaltime */ + 0x7ffffffe, /* iOnceResetThreshold */ + SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ + 0, /* iPrngSeed */ +#ifdef SQLITE_DEBUG + {0,0,0,0,0,0} /* aTune */ #endif -} +}; -/** - * Free and wipe memory. Uses SQLites internal sqlcipher_sqlite3_free so that memory - * can be countend and memory leak detection works in the test suite. - * If ptr is not null memory will be freed. - * If sz is greater than zero, the memory will be overwritten with zero before it is freed - * If sz is > 0, and not compiled with OMIT_MEMLOCK, system will attempt to unlock the - * memory segment so it can be paged - */ -void sqlcipher_free(void *ptr, u64 sz) { - CODEC_TRACE_MEMORY("sqlcipher_free: calling sqlcipher_memset(%p,0,%llu)\n", ptr, sz); - sqlcipher_memset(ptr, 0, sz); - sqlcipher_munlock(ptr, sz); - sqlcipher_sqlite3_free(ptr); -} +/* +** Hash table for global functions - functions common to all +** database connections. After initialization, this table is +** read-only. +*/ +SQLITE_PRIVATE FuncDefHash sqlcipher_sqlite3BuiltinFunctions; -/** - * allocate memory. Uses sqlite's internall malloc wrapper so memory can be - * reference counted and leak detection works. Unless compiled with OMIT_MEMLOCK - * attempts to lock the memory pages so sensitive information won't be swapped - */ -void* sqlcipher_malloc(u64 sz) { - void *ptr; - CODEC_TRACE_MEMORY("sqlcipher_malloc: calling sqlcipher_sqlite3Malloc(%llu)\n", sz); - ptr = sqlcipher_sqlite3Malloc(sz); - CODEC_TRACE_MEMORY("sqlcipher_malloc: calling sqlcipher_memset(%p,0,%llu)\n", ptr, sz); - sqlcipher_memset(ptr, 0, sz); - sqlcipher_mlock(ptr, sz); - return ptr; -} +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) +/* +** Counter used for coverage testing. Does not come into play for +** release builds. +** +** Access to this global variable is not mutex protected. This might +** result in TSAN warnings. But as the variable does not exist in +** release builds, that should not be a concern. +*/ +SQLITE_PRIVATE unsigned int sqlcipher_sqlite3CoverageCounter; +#endif /* SQLITE_COVERAGE_TEST || SQLITE_DEBUG */ -char* sqlcipher_version() { -#ifdef CIPHER_VERSION_QUALIFIER - char *version = sqlcipher_sqlite3_mprintf("%s %s %s", CIPHER_XSTR(CIPHER_VERSION_NUMBER), CIPHER_XSTR(CIPHER_VERSION_QUALIFIER), CIPHER_XSTR(CIPHER_VERSION_BUILD)); -#else - char *version = sqlcipher_sqlite3_mprintf("%s %s", CIPHER_XSTR(CIPHER_VERSION_NUMBER), CIPHER_XSTR(CIPHER_VERSION_BUILD)); +#ifdef VDBE_PROFILE +/* +** The following performance counter can be used in place of +** sqlcipher_sqlite3Hwtime() for profiling. This is a no-op on standard builds. +*/ +SQLITE_PRIVATE sqlcipher_sqlite3_uint64 sqlcipher_sqlite3NProfileCnt = 0; #endif - return version; -} - -/** - * Initialize new cipher_ctx struct. This function will allocate memory - * for the cipher context and for the key - * - * returns SQLITE_OK if initialization was successful - * returns SQLITE_NOMEM if an error occured allocating memory - */ -static int sqlcipher_cipher_ctx_init(codec_ctx *ctx, cipher_ctx **iCtx) { - cipher_ctx *c_ctx; - CODEC_TRACE("sqlcipher_cipher_ctx_init: allocating context\n"); - *iCtx = (cipher_ctx *) sqlcipher_malloc(sizeof(cipher_ctx)); - c_ctx = *iCtx; - if(c_ctx == NULL) return SQLITE_NOMEM; - - CODEC_TRACE("sqlcipher_cipher_ctx_init: allocating key\n"); - c_ctx->key = (unsigned char *) sqlcipher_malloc(ctx->key_sz); - CODEC_TRACE("sqlcipher_cipher_ctx_init: allocating hmac_key\n"); - c_ctx->hmac_key = (unsigned char *) sqlcipher_malloc(ctx->key_sz); +/* +** The value of the "pending" byte must be 0x40000000 (1 byte past the +** 1-gibabyte boundary) in a compatible database. SQLite never uses +** the database page that contains the pending byte. It never attempts +** to read or write that page. The pending byte page is set aside +** for use by the VFS layers as space for managing file locks. +** +** During testing, it is often desirable to move the pending byte to +** a different position in the file. This allows code that has to +** deal with the pending byte to run on files that are much smaller +** than 1 GiB. The sqlcipher_sqlite3_test_control() interface can be used to +** move the pending byte. +** +** IMPORTANT: Changing the pending byte to any value other than +** 0x40000000 results in an incompatible database file format! +** Changing the pending byte during operation will result in undefined +** and incorrect behavior. +*/ +#ifndef SQLITE_OMIT_WSD +SQLITE_PRIVATE int sqlcipher_sqlite3PendingByte = 0x40000000; +#endif - if(c_ctx->key == NULL) return SQLITE_NOMEM; - if(c_ctx->hmac_key == NULL) return SQLITE_NOMEM; +/* +** Tracing flags set by SQLITE_TESTCTRL_TRACEFLAGS. +*/ +SQLITE_PRIVATE u32 sqlcipher_sqlite3TreeTrace = 0; +SQLITE_PRIVATE u32 sqlcipher_sqlite3WhereTrace = 0; - return SQLITE_OK; -} +/* #include "opcodes.h" */ +/* +** Properties of opcodes. The OPFLG_INITIALIZER macro is +** created by mkopcodeh.awk during compilation. Data is obtained +** from the comments following the "case OP_xxxx:" statements in +** the vdbe.c file. +*/ +SQLITE_PRIVATE const unsigned char sqlcipher_sqlite3OpcodeProperty[] = OPFLG_INITIALIZER; -/** - * Free and wipe memory associated with a cipher_ctx - */ -static void sqlcipher_cipher_ctx_free(codec_ctx* ctx, cipher_ctx **iCtx) { - cipher_ctx *c_ctx = *iCtx; - CODEC_TRACE("cipher_ctx_free: entered iCtx=%p\n", iCtx); - sqlcipher_free(c_ctx->key, ctx->key_sz); - sqlcipher_free(c_ctx->hmac_key, ctx->key_sz); - sqlcipher_free(c_ctx->pass, c_ctx->pass_sz); - sqlcipher_free(c_ctx->keyspec, ctx->keyspec_sz); - sqlcipher_free(c_ctx, sizeof(cipher_ctx)); -} +/* +** Name of the default collating sequence +*/ +SQLITE_PRIVATE const char sqlcipher_sqlite3StrBINARY[] = "BINARY"; -static int sqlcipher_codec_ctx_reserve_setup(codec_ctx *ctx) { - int base_reserve = ctx->iv_sz; /* base reserve size will be IV only */ - int reserve = base_reserve; +/* +** Standard typenames. These names must match the COLTYPE_* definitions. +** Adjust the SQLITE_N_STDTYPE value if adding or removing entries. +** +** sqlcipher_sqlite3StdType[] The actual names of the datatypes. +** +** sqlcipher_sqlite3StdTypeLen[] The length (in bytes) of each entry +** in sqlcipher_sqlite3StdType[]. +** +** sqlcipher_sqlite3StdTypeAffinity[] The affinity associated with each entry +** in sqlcipher_sqlite3StdType[]. +** +** sqlcipher_sqlite3StdTypeMap[] The type value (as returned from +** sqlcipher_sqlite3_column_type() or sqlcipher_sqlite3_value_type()) +** for each entry in sqlcipher_sqlite3StdType[]. +*/ +SQLITE_PRIVATE const unsigned char sqlcipher_sqlite3StdTypeLen[] = { 3, 4, 3, 7, 4, 4 }; +SQLITE_PRIVATE const char sqlcipher_sqlite3StdTypeAffinity[] = { + SQLITE_AFF_NUMERIC, + SQLITE_AFF_BLOB, + SQLITE_AFF_INTEGER, + SQLITE_AFF_INTEGER, + SQLITE_AFF_REAL, + SQLITE_AFF_TEXT +}; +SQLITE_PRIVATE const char sqlcipher_sqlite3StdTypeMap[] = { + 0, + SQLITE_BLOB, + SQLITE_INTEGER, + SQLITE_INTEGER, + SQLITE_FLOAT, + SQLITE_TEXT +}; +SQLITE_PRIVATE const char *sqlcipher_sqlite3StdType[] = { + "ANY", + "BLOB", + "INT", + "INTEGER", + "REAL", + "TEXT" +}; - ctx->hmac_sz = ctx->provider->get_hmac_sz(ctx->provider_ctx, ctx->hmac_algorithm); +/************** End of global.c **********************************************/ +/************** Begin file status.c ******************************************/ +/* +** 2008 June 18 +** +** 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 module implements the sqlcipher_sqlite3_status() interface and related +** functionality. +*/ +/* #include "sqliteInt.h" */ +/************** Include vdbeInt.h in the middle of status.c ******************/ +/************** Begin file vdbeInt.h *****************************************/ +/* +** 2003 September 6 +** +** 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 is the header file for information that is private to the +** VDBE. This information used to all be at the top of the single +** source code file "vdbe.c". When that file became too big (over +** 6000 lines long) it was split up into several smaller files and +** this header information was factored out. +*/ +#ifndef SQLITE_VDBEINT_H +#define SQLITE_VDBEINT_H - if(sqlcipher_codec_ctx_get_use_hmac(ctx)) - reserve += ctx->hmac_sz; /* if reserve will include hmac, update that size */ +/* +** The maximum number of times that a statement will try to reparse +** itself before giving up and returning SQLITE_SCHEMA. +*/ +#ifndef SQLITE_MAX_SCHEMA_RETRY +# define SQLITE_MAX_SCHEMA_RETRY 50 +#endif - /* calculate the amount of reserve needed in even increments of the cipher block size */ - reserve = ((reserve % ctx->block_sz) == 0) ? reserve : - ((reserve / ctx->block_sz) + 1) * ctx->block_sz; +/* +** VDBE_DISPLAY_P4 is true or false depending on whether or not the +** "explain" P4 display logic is enabled. +*/ +#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ + || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \ + || defined(SQLITE_ENABLE_BYTECODE_VTAB) +# define VDBE_DISPLAY_P4 1 +#else +# define VDBE_DISPLAY_P4 0 +#endif - CODEC_TRACE("sqlcipher_codec_ctx_reserve_setup: base_reserve=%d block_sz=%d md_size=%d reserve=%d\n", - base_reserve, ctx->block_sz, ctx->hmac_sz, reserve); +/* +** SQL is translated into a sequence of instructions to be +** executed by a virtual machine. Each instruction is an instance +** of the following structure. +*/ +typedef struct VdbeOp Op; - ctx->reserve_sz = reserve; +/* +** Boolean values +*/ +typedef unsigned Bool; - return SQLITE_OK; -} +/* Opaque type used by code in vdbesort.c */ +typedef struct VdbeSorter VdbeSorter; -/** - * Compare one cipher_ctx to another. - * - * returns 0 if all the parameters (except the derived key data) are the same - * returns 1 otherwise - */ -static int sqlcipher_cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) { - int are_equal = ( - c1->pass_sz == c2->pass_sz - && ( - c1->pass == c2->pass - || !sqlcipher_memcmp((const unsigned char*)c1->pass, - (const unsigned char*)c2->pass, - c1->pass_sz) - )); +/* Elements of the linked list at Vdbe.pAuxData */ +typedef struct AuxData AuxData; - CODEC_TRACE("sqlcipher_cipher_ctx_cmp: entered \ - c1=%p c2=%p \ - c1->pass_sz=%d c2->pass_sz=%d \ - c1->pass=%p c2->pass=%p \ - c1->pass=%s c2->pass=%s \ - sqlcipher_memcmp=%d \ - are_equal=%d \ - \n", - c1, c2, - c1->pass_sz, c2->pass_sz, - c1->pass, c2->pass, - c1->pass, c2->pass, - (c1->pass == NULL || c2->pass == NULL) - ? -1 : sqlcipher_memcmp( - (const unsigned char*)c1->pass, - (const unsigned char*)c2->pass, - c1->pass_sz), - are_equal - ); +/* Types of VDBE cursors */ +#define CURTYPE_BTREE 0 +#define CURTYPE_SORTER 1 +#define CURTYPE_VTAB 2 +#define CURTYPE_PSEUDO 3 - return !are_equal; /* return 0 if they are the same, 1 otherwise */ -} +/* +** A VdbeCursor is an superclass (a wrapper) for various cursor objects: +** +** * A b-tree cursor +** - In the main database or in an ephemeral database +** - On either an index or a table +** * A sorter +** * A virtual table +** * A one-row "pseudotable" stored in a single register +*/ +typedef struct VdbeCursor VdbeCursor; +struct VdbeCursor { + u8 eCurType; /* One of the CURTYPE_* values above */ + i8 iDb; /* Index of cursor database in db->aDb[] */ + u8 nullRow; /* True if pointing to a row with no data */ + u8 deferredMoveto; /* A call to sqlcipher_sqlite3BtreeMoveto() is needed */ + u8 isTable; /* True for rowid tables. False for indexes */ +#ifdef SQLITE_DEBUG + u8 seekOp; /* Most recent seek operation on this cursor */ + u8 wrFlag; /* The wrFlag argument to sqlcipher_sqlite3BtreeCursor() */ +#endif + Bool isEphemeral:1; /* True for an ephemeral table */ + Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ + Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ + Bool noReuse:1; /* OpenEphemeral may not reuse this cursor */ + u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ + union { /* pBtx for isEphermeral. pAltMap otherwise */ + Btree *pBtx; /* Separate file holding temporary table */ + u32 *aAltMap; /* Mapping from table to index column numbers */ + } ub; + i64 seqCount; /* Sequence counter */ -/** - * Copy one cipher_ctx to another. For instance, assuming that read_ctx is a - * fully initialized context, you could copy it to write_ctx and all yet data - * and pass information across - * - * returns SQLITE_OK if initialization was successful - * returns SQLITE_NOMEM if an error occured allocating memory - */ -static int sqlcipher_cipher_ctx_copy(codec_ctx *ctx, cipher_ctx *target, cipher_ctx *source) { - void *key = target->key; - void *hmac_key = target->hmac_key; + /* Cached OP_Column parse information is only valid if cacheStatus matches + ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of + ** CACHE_STALE (0) and so setting cacheStatus=CACHE_STALE guarantees that + ** the cache is out of date. */ + u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ + int seekResult; /* Result of previous sqlcipher_sqlite3BtreeMoveto() or 0 + ** if there have been no prior seeks on the cursor. */ + /* seekResult does not distinguish between "no seeks have ever occurred + ** on this cursor" and "the most recent seek was an exact match". + ** For CURTYPE_PSEUDO, seekResult is the register holding the record */ - CODEC_TRACE("sqlcipher_cipher_ctx_copy: entered target=%p, source=%p\n", target, source); - sqlcipher_free(target->pass, target->pass_sz); - sqlcipher_free(target->keyspec, ctx->keyspec_sz); - memcpy(target, source, sizeof(cipher_ctx)); + /* When a new VdbeCursor is allocated, only the fields above are zeroed. + ** The fields that follow are uninitialized, and must be individually + ** initialized prior to first use. */ + VdbeCursor *pAltCursor; /* Associated index cursor from which to read */ + union { + BtCursor *pCursor; /* CURTYPE_BTREE or _PSEUDO. Btree cursor */ + sqlcipher_sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */ + VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */ + } uc; + KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ + u32 iHdrOffset; /* Offset to next unparsed byte of the header */ + Pgno pgnoRoot; /* Root page of the open btree cursor */ + i16 nField; /* Number of fields in the header */ + u16 nHdrParsed; /* Number of header fields parsed so far */ + i64 movetoTarget; /* Argument to the deferred sqlcipher_sqlite3BtreeMoveto() */ + u32 *aOffset; /* Pointer to aType[nField] */ + const u8 *aRow; /* Data for the current row, if all on one page */ + u32 payloadSize; /* Total number of bytes in the record */ + u32 szRow; /* Byte available in aRow */ +#ifdef SQLITE_ENABLE_COLUMN_USED_MASK + u64 maskUsed; /* Mask of columns used by this cursor */ +#endif - target->key = key; /* restore pointer to previously allocated key data */ - memcpy(target->key, source->key, ctx->key_sz); + /* 2*nField extra array elements allocated for aType[], beyond the one + ** static element declared in the structure. nField total array slots for + ** aType[] and nField+1 array slots for aOffset[] */ + u32 aType[1]; /* Type values record decode. MUST BE LAST */ +}; - target->hmac_key = hmac_key; /* restore pointer to previously allocated hmac key data */ - memcpy(target->hmac_key, source->hmac_key, ctx->key_sz); +/* Return true if P is a null-only cursor +*/ +#define IsNullCursor(P) \ + ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) - if(source->pass && source->pass_sz) { - target->pass = sqlcipher_malloc(source->pass_sz); - if(target->pass == NULL) return SQLITE_NOMEM; - memcpy(target->pass, source->pass, source->pass_sz); - } - if(source->keyspec) { - target->keyspec = sqlcipher_malloc(ctx->keyspec_sz); - if(target->keyspec == NULL) return SQLITE_NOMEM; - memcpy(target->keyspec, source->keyspec, ctx->keyspec_sz); - } - return SQLITE_OK; -} -/** - * Set the keyspec for the cipher_ctx - * - * returns SQLITE_OK if assignment was successfull - * returns SQLITE_NOMEM if an error occured allocating memory - */ -static int sqlcipher_cipher_ctx_set_keyspec(codec_ctx *ctx, cipher_ctx *c_ctx, const unsigned char *key) { - /* free, zero existing pointers and size */ - sqlcipher_free(c_ctx->keyspec, ctx->keyspec_sz); - c_ctx->keyspec = NULL; +/* +** A value for VdbeCursor.cacheStatus that means the cache is always invalid. +*/ +#define CACHE_STALE 0 - c_ctx->keyspec = sqlcipher_malloc(ctx->keyspec_sz); - if(c_ctx->keyspec == NULL) return SQLITE_NOMEM; +/* +** When a sub-program is executed (OP_Program), a structure of this type +** is allocated to store the current value of the program counter, as +** well as the current memory cell array and various other frame specific +** values stored in the Vdbe struct. When the sub-program is finished, +** these values are copied back to the Vdbe from the VdbeFrame structure, +** restoring the state of the VM to as it was before the sub-program +** began executing. +** +** The memory for a VdbeFrame object is allocated and managed by a memory +** cell in the parent (calling) frame. When the memory cell is deleted or +** overwritten, the VdbeFrame object is not freed immediately. Instead, it +** is linked into the Vdbe.pDelFrame list. The contents of the Vdbe.pDelFrame +** list is deleted when the VM is reset in VdbeHalt(). The reason for doing +** this instead of deleting the VdbeFrame immediately is to avoid recursive +** calls to sqlcipher_sqlite3VdbeMemRelease() when the memory cells belonging to the +** child frame are released. +** +** The currently executing frame is stored in Vdbe.pFrame. Vdbe.pFrame is +** set to NULL if the currently executing frame is the main program. +*/ +typedef struct VdbeFrame VdbeFrame; +struct VdbeFrame { + Vdbe *v; /* VM this frame belongs to */ + VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ + Op *aOp; /* Program instructions for parent frame */ + i64 *anExec; /* Event counters from parent frame */ + Mem *aMem; /* Array of memory cells for parent frame */ + VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ + u8 *aOnce; /* Bitmask used by OP_Once */ + void *token; /* Copy of SubProgram.token */ + i64 lastRowid; /* Last insert rowid (sqlcipher_sqlite3.lastRowid) */ + AuxData *pAuxData; /* Linked list of auxdata allocations */ +#if SQLITE_DEBUG + u32 iFrameMagic; /* magic number for sanity checking */ +#endif + int nCursor; /* Number of entries in apCsr */ + int pc; /* Program Counter in parent (calling) frame */ + int nOp; /* Size of aOp array */ + int nMem; /* Number of entries in aMem */ + int nChildMem; /* Number of memory cells for child frame */ + int nChildCsr; /* Number of cursors for child frame */ + i64 nChange; /* Statement changes (Vdbe.nChange) */ + i64 nDbChange; /* Value of db->nChange */ +}; - c_ctx->keyspec[0] = 'x'; - c_ctx->keyspec[1] = '\''; - cipher_bin2hex(key, ctx->key_sz, c_ctx->keyspec + 2); - cipher_bin2hex(ctx->kdf_salt, ctx->kdf_salt_sz, c_ctx->keyspec + (ctx->key_sz * 2) + 2); - c_ctx->keyspec[ctx->keyspec_sz - 1] = '\''; - return SQLITE_OK; -} +/* Magic number for sanity checking on VdbeFrame objects */ +#define SQLITE_FRAME_MAGIC 0x879fb71e -int sqlcipher_codec_get_store_pass(codec_ctx *ctx) { - return ctx->store_pass; -} +/* +** Return a pointer to the array of registers allocated for use +** by a VdbeFrame. +*/ +#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) -void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value) { - ctx->store_pass = value; -} +/* +** Internally, the vdbe manipulates nearly all SQL values as Mem +** structures. Each Mem struct may cache multiple representations (string, +** integer etc.) of the same value. +*/ +struct sqlcipher_sqlite3_value { + union MemValue { + double r; /* Real value used when MEM_Real is set in flags */ + i64 i; /* Integer value used when MEM_Int is set in flags */ + int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */ + const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */ + FuncDef *pDef; /* Used only when flags==MEM_Agg */ + } u; + char *z; /* String or BLOB value */ + int n; /* Number of characters in string value, excluding '\0' */ + u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ + u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ + u8 eSubtype; /* Subtype for this value */ + /* ShallowCopy only needs to copy the information above */ + sqlcipher_sqlite3 *db; /* The associated database connection */ + int szMalloc; /* Size of the zMalloc allocation */ + u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */ + char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */ + void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */ +#ifdef SQLITE_DEBUG + Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ + u16 mScopyFlags; /* flags value immediately after the shallow copy */ +#endif +}; -void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey) { - *zKey = ctx->read_ctx->pass; - *nKey = ctx->read_ctx->pass_sz; -} +/* +** Size of struct Mem not including the Mem.zMalloc member or anything that +** follows. +*/ +#define MEMCELLSIZE offsetof(Mem,db) -static void sqlcipher_set_derive_key(codec_ctx *ctx, int derive) { - if(ctx->read_ctx != NULL) ctx->read_ctx->derive_key = 1; - if(ctx->write_ctx != NULL) ctx->write_ctx->derive_key = 1; -} +/* One or more of the following flags are set to indicate the +** representations of the value stored in the Mem struct. +** +** * MEM_Null An SQL NULL value +** +** * MEM_Null|MEM_Zero An SQL NULL with the virtual table +** UPDATE no-change flag set +** +** * MEM_Null|MEM_Term| An SQL NULL, but also contains a +** MEM_Subtype pointer accessible using +** sqlcipher_sqlite3_value_pointer(). +** +** * MEM_Null|MEM_Cleared Special SQL NULL that compares non-equal +** to other NULLs even using the IS operator. +** +** * MEM_Str A string, stored in Mem.z with +** length Mem.n. Zero-terminated if +** MEM_Term is set. This flag is +** incompatible with MEM_Blob and +** MEM_Null, but can appear with MEM_Int, +** MEM_Real, and MEM_IntReal. +** +** * MEM_Blob A blob, stored in Mem.z length Mem.n. +** Incompatible with MEM_Str, MEM_Null, +** MEM_Int, MEM_Real, and MEM_IntReal. +** +** * MEM_Blob|MEM_Zero A blob in Mem.z of length Mem.n plus +** MEM.u.i extra 0x00 bytes at the end. +** +** * MEM_Int Integer stored in Mem.u.i. +** +** * MEM_Real Real stored in Mem.u.r. +** +** * MEM_IntReal Real stored as an integer in Mem.u.i. +** +** If the MEM_Null flag is set, then the value is an SQL NULL value. +** For a pointer type created using sqlcipher_sqlite3_bind_pointer() or +** sqlcipher_sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set. +** +** If the MEM_Str flag is set then Mem.z points at a string representation. +** Usually this is encoded in the same unicode encoding as the main +** database (see below for exceptions). If the MEM_Term flag is also +** set, then the string is nul terminated. The MEM_Int and MEM_Real +** flags may coexist with the MEM_Str flag. +*/ +#define MEM_Undefined 0x0000 /* Value is undefined */ +#define MEM_Null 0x0001 /* Value is NULL (or a pointer) */ +#define MEM_Str 0x0002 /* Value is a string */ +#define MEM_Int 0x0004 /* Value is an integer */ +#define MEM_Real 0x0008 /* Value is a real number */ +#define MEM_Blob 0x0010 /* Value is a BLOB */ +#define MEM_IntReal 0x0020 /* MEM_Int that stringifies like MEM_Real */ +#define MEM_AffMask 0x003f /* Mask of affinity bits */ -/** - * Set the passphrase for the cipher_ctx - * - * returns SQLITE_OK if assignment was successfull - * returns SQLITE_NOMEM if an error occured allocating memory - */ -static int sqlcipher_cipher_ctx_set_pass(cipher_ctx *ctx, const void *zKey, int nKey) { - /* free, zero existing pointers and size */ - sqlcipher_free(ctx->pass, ctx->pass_sz); - ctx->pass = NULL; - ctx->pass_sz = 0; +/* Extra bits that modify the meanings of the core datatypes above +*/ +#define MEM_FromBind 0x0040 /* Value originates from sqlcipher_sqlite3_bind() */ + /* 0x0080 // Available */ +#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ +#define MEM_Term 0x0200 /* String in Mem.z is zero terminated */ +#define MEM_Zero 0x0400 /* Mem.i contains count of 0s appended to blob */ +#define MEM_Subtype 0x0800 /* Mem.eSubtype is valid */ +#define MEM_TypeMask 0x0dbf /* Mask of type bits */ - if(zKey && nKey) { /* if new password is provided, copy it */ - ctx->pass_sz = nKey; - ctx->pass = sqlcipher_malloc(nKey); - if(ctx->pass == NULL) return SQLITE_NOMEM; - memcpy(ctx->pass, zKey, nKey); - } - return SQLITE_OK; -} +/* Bits that determine the storage for Mem.z for a string or blob or +** aggregate accumulator. +*/ +#define MEM_Dyn 0x1000 /* Need to call Mem.xDel() on Mem.z */ +#define MEM_Static 0x2000 /* Mem.z points to a static string */ +#define MEM_Ephem 0x4000 /* Mem.z points to an ephemeral string */ +#define MEM_Agg 0x8000 /* Mem.z points to an agg function context */ -int sqlcipher_codec_ctx_set_pass(codec_ctx *ctx, const void *zKey, int nKey, int for_ctx) { - cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx; - int rc; +/* Return TRUE if Mem X contains dynamically allocated content - anything +** that needs to be deallocated to avoid a leak. +*/ +#define VdbeMemDynamic(X) \ + (((X)->flags&(MEM_Agg|MEM_Dyn))!=0) - if((rc = sqlcipher_cipher_ctx_set_pass(c_ctx, zKey, nKey)) != SQLITE_OK) return rc; - c_ctx->derive_key = 1; +/* +** Clear any existing type flags from a Mem and replace them with f +*/ +#define MemSetTypeFlag(p, f) \ + ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) - if(for_ctx == 2) - if((rc = sqlcipher_cipher_ctx_copy(ctx, for_ctx ? ctx->read_ctx : ctx->write_ctx, c_ctx)) != SQLITE_OK) - return rc; +/* +** True if Mem X is a NULL-nochng type. +*/ +#define MemNullNochng(X) \ + (((X)->flags&MEM_TypeMask)==(MEM_Null|MEM_Zero) \ + && (X)->n==0 && (X)->u.nZero==0) - return SQLITE_OK; -} +/* +** Return true if a memory cell has been initialized and is valid. +** is for use inside assert() statements only. +** +** A Memory cell is initialized if at least one of the +** MEM_Null, MEM_Str, MEM_Int, MEM_Real, MEM_Blob, or MEM_IntReal bits +** is set. It is "undefined" if all those bits are zero. +*/ +#ifdef SQLITE_DEBUG +#define memIsValid(M) ((M)->flags & MEM_AffMask)!=0 +#endif -const char* sqlcipher_codec_ctx_get_cipher(codec_ctx *ctx) { - return ctx->provider->get_cipher(ctx->provider_ctx); -} +/* +** Each auxiliary data pointer stored by a user defined function +** implementation calling sqlcipher_sqlite3_set_auxdata() is stored in an instance +** of this structure. All such structures associated with a single VM +** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed +** when the VM is halted (if not before). +*/ +struct AuxData { + int iAuxOp; /* Instruction number of OP_Function opcode */ + int iAuxArg; /* Index of function argument. */ + void *pAux; /* Aux data pointer */ + void (*xDeleteAux)(void*); /* Destructor for the aux data */ + AuxData *pNextAux; /* Next element in list */ +}; -/* set the global default KDF iteration */ -void sqlcipher_set_default_kdf_iter(int iter) { - default_kdf_iter = iter; -} +/* +** The "context" argument for an installable function. A pointer to an +** instance of this structure is the first argument to the routines used +** implement the SQL functions. +** +** There is a typedef for this structure in sqlite.h. So all routines, +** even the public interface to SQLite, can use a pointer to this structure. +** But this file is the only place where the internal details of this +** structure are known. +** +** This structure is defined inside of vdbeInt.h because it uses substructures +** (Mem) which are only defined there. +*/ +struct sqlcipher_sqlite3_context { + Mem *pOut; /* The return value is stored here */ + FuncDef *pFunc; /* Pointer to function information */ + Mem *pMem; /* Memory cell used to store aggregate context */ + Vdbe *pVdbe; /* The VM that owns this context */ + int iOp; /* Instruction number of OP_Function */ + int isError; /* Error code returned by the function. */ + u8 enc; /* Encoding to use for results */ + u8 skipFlag; /* Skip accumulator loading if true */ + u8 argc; /* Number of arguments */ + sqlcipher_sqlite3_value *argv[1]; /* Argument set */ +}; -int sqlcipher_get_default_kdf_iter() { - return default_kdf_iter; -} +/* A bitfield type for use inside of structures. Always follow with :N where +** N is the number of bits. +*/ +typedef unsigned bft; /* Bit Field Type */ -int sqlcipher_codec_ctx_set_kdf_iter(codec_ctx *ctx, int kdf_iter) { - ctx->kdf_iter = kdf_iter; - sqlcipher_set_derive_key(ctx, 1); - return SQLITE_OK; -} +/* The ScanStatus object holds a single value for the +** sqlcipher_sqlite3_stmt_scanstatus() interface. +*/ +typedef struct ScanStatus ScanStatus; +struct ScanStatus { + int addrExplain; /* OP_Explain for loop */ + int addrLoop; /* Address of "loops" counter */ + int addrVisit; /* Address of "rows visited" counter */ + int iSelectID; /* The "Select-ID" for this loop */ + LogEst nEst; /* Estimated output rows per loop */ + char *zName; /* Name of table or index */ +}; -int sqlcipher_codec_ctx_get_kdf_iter(codec_ctx *ctx) { - return ctx->kdf_iter; -} +/* The DblquoteStr object holds the text of a double-quoted +** string for a prepared statement. A linked list of these objects +** is constructed during statement parsing and is held on Vdbe.pDblStr. +** When computing a normalized SQL statement for an SQL statement, that +** list is consulted for each double-quoted identifier to see if the +** identifier should really be a string literal. +*/ +typedef struct DblquoteStr DblquoteStr; +struct DblquoteStr { + DblquoteStr *pNextStr; /* Next string literal in the list */ + char z[8]; /* Dequoted value for the string */ +}; -int sqlcipher_codec_ctx_set_fast_kdf_iter(codec_ctx *ctx, int fast_kdf_iter) { - ctx->fast_kdf_iter = fast_kdf_iter; - sqlcipher_set_derive_key(ctx, 1); - return SQLITE_OK; -} +/* +** An instance of the virtual machine. This structure contains the complete +** state of the virtual machine. +** +** The "sqlcipher_sqlite3_stmt" structure pointer that is returned by sqlcipher_sqlite3_prepare() +** is really a pointer to an instance of this structure. +*/ +struct Vdbe { + sqlcipher_sqlite3 *db; /* The database connection that owns this statement */ + Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ + Parse *pParse; /* Parsing context used to create this Vdbe */ + ynVar nVar; /* Number of entries in aVar[] */ + int nMem; /* Number of memory locations currently allocated */ + int nCursor; /* Number of slots in apCsr[] */ + u32 cacheCtr; /* VdbeCursor row cache generation counter */ + int pc; /* The program counter */ + int rc; /* Value to return */ + i64 nChange; /* Number of db changes made since last reset */ + int iStatement; /* Statement number (or 0 if has no opened stmt) */ + i64 iCurrentTime; /* Value of julianday('now') for this statement */ + i64 nFkConstraint; /* Number of imm. FK constraints this VM */ + i64 nStmtDefCons; /* Number of def. constraints when stmt started */ + i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */ + Mem *aMem; /* The memory locations */ + Mem **apArg; /* Arguments to currently executing user function */ + VdbeCursor **apCsr; /* One element of this array for each open cursor */ + Mem *aVar; /* Values for the OP_Variable opcode. */ -int sqlcipher_codec_ctx_get_fast_kdf_iter(codec_ctx *ctx) { - return ctx->fast_kdf_iter; -} + /* When allocating a new Vdbe object, all of the fields below should be + ** initialized to zero or NULL */ -/* set the global default flag for HMAC */ -void sqlcipher_set_default_use_hmac(int use) { - if(use) default_flags |= CIPHER_FLAG_HMAC; - else default_flags &= ~CIPHER_FLAG_HMAC; -} + Op *aOp; /* Space to hold the virtual machine's program */ + int nOp; /* Number of instructions in the program */ + int nOpAlloc; /* Slots allocated for aOp[] */ + Mem *aColName; /* Column names to return */ + Mem *pResultSet; /* Pointer to an array of results */ + char *zErrMsg; /* Error message written here */ + VList *pVList; /* Name of variables */ +#ifndef SQLITE_OMIT_TRACE + i64 startTime; /* Time when query started - used for profiling */ +#endif +#ifdef SQLITE_DEBUG + int rcApp; /* errcode set by sqlcipher_sqlite3_result_error_code() */ + u32 nWrite; /* Number of write operations that have occurred */ +#endif + u16 nResColumn; /* Number of columns in one row of the result set */ + u8 errorAction; /* Recovery action to do in case of an error */ + u8 minWriteFileFormat; /* Minimum file format for writable database files */ + u8 prepFlags; /* SQLITE_PREPARE_* flags */ + u8 eVdbeState; /* On of the VDBE_*_STATE values */ + bft expired:2; /* 1: recompile VM immediately 2: when convenient */ + bft explain:2; /* True if EXPLAIN present on SQL command */ + bft changeCntOn:1; /* True to update the change-counter */ + bft usesStmtJournal:1; /* True if uses a statement journal */ + bft readOnly:1; /* True for statements that do not write */ + bft bIsReader:1; /* True for statements that read */ + yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ + yDbMask lockMask; /* Subset of btreeMask that requires a lock */ + u32 aCounter[9]; /* Counters used by sqlcipher_sqlite3_stmt_status() */ + char *zSql; /* Text of the SQL statement that generated this */ +#ifdef SQLITE_ENABLE_NORMALIZE + char *zNormSql; /* Normalization of the associated SQL statement */ + DblquoteStr *pDblStr; /* List of double-quoted string literals */ +#endif + void *pFree; /* Free this when deleting the vdbe */ + VdbeFrame *pFrame; /* Parent frame */ + VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */ + int nFrame; /* Number of frames in pFrame list */ + u32 expmask; /* Binding to these vars invalidates VM */ + SubProgram *pProgram; /* Linked list of all sub-programs used by VM */ + AuxData *pAuxData; /* Linked list of auxdata allocations */ +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + i64 *anExec; /* Number of times each op has been executed */ + int nScan; /* Entries in aScan[] */ + ScanStatus *aScan; /* Scan definitions for sqlcipher_sqlite3_stmt_scanstatus() */ +#endif +}; -int sqlcipher_get_default_use_hmac() { - return (default_flags & CIPHER_FLAG_HMAC) != 0; -} +/* +** The following are allowed values for Vdbe.eVdbeState +*/ +#define VDBE_INIT_STATE 0 /* Prepared statement under construction */ +#define VDBE_READY_STATE 1 /* Ready to run but not yet started */ +#define VDBE_RUN_STATE 2 /* Run in progress */ +#define VDBE_HALT_STATE 3 /* Finished. Need reset() or finalize() */ -void sqlcipher_set_hmac_salt_mask(unsigned char mask) { - hmac_salt_mask = mask; -} +/* +** Structure used to store the context required by the +** sqlcipher_sqlite3_preupdate_*() API functions. +*/ +struct PreUpdate { + Vdbe *v; + VdbeCursor *pCsr; /* Cursor to read old values from */ + int op; /* One of SQLITE_INSERT, UPDATE, DELETE */ + u8 *aRecord; /* old.* database record */ + KeyInfo keyinfo; + UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ + UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ + int iNewReg; /* Register for new.* values */ + int iBlobWrite; /* Value returned by preupdate_blobwrite() */ + i64 iKey1; /* First key value passed to hook */ + i64 iKey2; /* Second key value passed to hook */ + Mem *aNew; /* Array of new.* values */ + Table *pTab; /* Schema object being upated */ + Index *pPk; /* PK index if pTab is WITHOUT ROWID */ +}; -unsigned char sqlcipher_get_hmac_salt_mask() { - return hmac_salt_mask; -} +/* +** An instance of this object is used to pass an vector of values into +** OP_VFilter, the xFilter method of a virtual table. The vector is the +** set of values on the right-hand side of an IN constraint. +** +** The value as passed into xFilter is an sqlcipher_sqlite3_value with a "pointer" +** type, such as is generated by sqlcipher_sqlite3_result_pointer() and read by +** sqlcipher_sqlite3_value_pointer. Such values have MEM_Term|MEM_Subtype|MEM_Null +** and a subtype of 'p'. The sqlcipher_sqlite3_vtab_in_first() and _next() interfaces +** know how to use this object to step through all the values in the +** right operand of the IN constraint. +*/ +typedef struct ValueList ValueList; +struct ValueList { + BtCursor *pCsr; /* An ephemeral table holding all values */ + sqlcipher_sqlite3_value *pOut; /* Register to hold each decoded output value */ +}; -/* set the codec flag for whether this individual database should be using hmac */ -int sqlcipher_codec_ctx_set_use_hmac(codec_ctx *ctx, int use) { - if(use) { - sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_HMAC); - } else { - sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_HMAC); - } +/* Size of content associated with serial types that fit into a +** single-byte varint. +*/ +#ifndef SQLITE_AMALGAMATION +SQLITE_PRIVATE const u8 sqlcipher_sqlite3SmallTypeSizes[]; +#endif - return sqlcipher_codec_ctx_reserve_setup(ctx); -} +/* +** Function prototypes +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeError(Vdbe*, const char *, ...); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeFreeCursorNN(Vdbe*,VdbeCursor*); +void sqliteVdbePopStack(Vdbe*,int); +SQLITE_PRIVATE int SQLITE_NOINLINE sqlcipher_sqlite3VdbeHandleMovedCursor(VdbeCursor *p); +SQLITE_PRIVATE int SQLITE_NOINLINE sqlcipher_sqlite3VdbeFinishMoveto(VdbeCursor*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCursorRestore(VdbeCursor*); +SQLITE_PRIVATE u32 sqlcipher_sqlite3VdbeSerialTypeLen(u32); +SQLITE_PRIVATE u8 sqlcipher_sqlite3VdbeOneByteSerialTypeLen(u8); +#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT +SQLITE_PRIVATE u64 sqlcipher_sqlite3FloatSwap(u64 in); +# define swapMixedEndianFloat(X) X = sqlcipher_sqlite3FloatSwap(X) +#else +# define swapMixedEndianFloat(X) +#endif +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeDeleteAuxData(sqlcipher_sqlite3*, AuxData**, int, int); -int sqlcipher_codec_ctx_get_use_hmac(codec_ctx *ctx) { - return (ctx->flags & CIPHER_FLAG_HMAC) != 0; -} +int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeIdxKeyCompare(sqlcipher_sqlite3*,VdbeCursor*,UnpackedRecord*,int*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeIdxRowid(sqlcipher_sqlite3*, BtCursor*, i64*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeExec(Vdbe*); +#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB) +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeNextOpcode(Vdbe*,Mem*,int,int*,int*,Op**); +SQLITE_PRIVATE char *sqlcipher_sqlite3VdbeDisplayP4(sqlcipher_sqlite3*,Op*); +#endif +#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) +SQLITE_PRIVATE char *sqlcipher_sqlite3VdbeDisplayComment(sqlcipher_sqlite3*,const Op*,const char*); +#endif +#if !defined(SQLITE_OMIT_EXPLAIN) +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeList(Vdbe*); +#endif +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeHalt(Vdbe*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeChangeEncoding(Mem *, int); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemTooBig(Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemCopy(Mem*, const Mem*); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemMove(Mem*, Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemNulTerminate(Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(*)(void*)); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetInt64(Mem*, i64); +#ifdef SQLITE_OMIT_FLOATING_POINT +# define sqlcipher_sqlite3VdbeMemSetDouble sqlcipher_sqlite3VdbeMemSetInt64 +#else +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetDouble(Mem*, double); +#endif +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetPointer(Mem*, void*, const char*, void(*)(void*)); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemInit(Mem*,sqlcipher_sqlite3*,u16); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetNull(Mem*); +#ifndef SQLITE_OMIT_INCRBLOB +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetZeroBlob(Mem*,int); +#else +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemSetZeroBlob(Mem*,int); +#endif +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemIsRowSet(const Mem*); +#endif +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemSetRowSet(Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemMakeWriteable(Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemStringify(Mem*, u8, u8); +SQLITE_PRIVATE int sqlcipher_sqlite3IntFloatCompare(i64,double); +SQLITE_PRIVATE i64 sqlcipher_sqlite3VdbeIntValue(const Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemIntegerify(Mem*); +SQLITE_PRIVATE double sqlcipher_sqlite3VdbeRealValue(Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeBooleanValue(Mem*, int ifNull); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeIntegerAffinity(Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemRealify(Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemNumerify(Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemCast(Mem*,u8,u8); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(BtCursor*,u32,Mem*); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemRelease(Mem *p); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemReleaseMalloc(Mem*p); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemFinalize(Mem*, FuncDef*); +#ifndef SQLITE_OMIT_WINDOWFUNC +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*); +#endif +#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB) +SQLITE_PRIVATE const char *sqlcipher_sqlite3OpcodeName(int); +#endif +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemClearAndResize(Mem *pMem, int n); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCloseStatement(Vdbe *, int); +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeFrameIsValid(VdbeFrame*); +#endif +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeFrameMemDel(void*); /* Destructor on Mem */ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeFrameDelete(VdbeFrame*); /* Actually deletes the Frame */ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeFrameRestore(VdbeFrame *); +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +SQLITE_PRIVATE void sqlcipher_sqlite3VdbePreUpdateHook( + Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int,int); +#endif +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeTransferError(Vdbe *p); -/* the length of plaintext header size must be: - * 1. greater than or equal to zero - * 2. a multiple of the cipher block size - * 3. less than the usable size of the first database page - */ -int sqlcipher_set_default_plaintext_header_size(int size) { - default_plaintext_header_sz = size; - return SQLITE_OK; -} +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterInit(sqlcipher_sqlite3 *, int, VdbeCursor *); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSorterReset(sqlcipher_sqlite3 *, VdbeSorter *); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSorterClose(sqlcipher_sqlite3 *, VdbeCursor *); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterNext(sqlcipher_sqlite3 *, const VdbeCursor *); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterRewind(const VdbeCursor *, int *); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); -int sqlcipher_codec_ctx_set_plaintext_header_size(codec_ctx *ctx, int size) { - if(size >= 0 && (size % ctx->block_sz) == 0 && size < (ctx->page_sz - ctx->reserve_sz)) { - ctx->plaintext_header_sz = size; - return SQLITE_OK; - } - ctx->plaintext_header_sz = -1; - return SQLITE_ERROR; -} +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeAssertAbortable(Vdbe*); +#else +# define sqlcipher_sqlite3VdbeIncrWriteCounter(V,C) +# define sqlcipher_sqlite3VdbeAssertAbortable(V) +#endif -int sqlcipher_get_default_plaintext_header_size() { - return default_plaintext_header_sz; -} +#if !defined(SQLITE_OMIT_SHARED_CACHE) +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeEnter(Vdbe*); +#else +# define sqlcipher_sqlite3VdbeEnter(X) +#endif -int sqlcipher_codec_ctx_get_plaintext_header_size(codec_ctx *ctx) { - return ctx->plaintext_header_sz; -} +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeLeave(Vdbe*); +#else +# define sqlcipher_sqlite3VdbeLeave(X) +#endif -/* manipulate HMAC algorithm */ -int sqlcipher_set_default_hmac_algorithm(int algorithm) { - default_hmac_algorithm = algorithm; - return SQLITE_OK; -} +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemAboutToChange(Vdbe*,Mem*); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCheckMemInvariants(Mem*); +#endif -int sqlcipher_codec_ctx_set_hmac_algorithm(codec_ctx *ctx, int algorithm) { - ctx->hmac_algorithm = algorithm; - return sqlcipher_codec_ctx_reserve_setup(ctx); -} +#ifndef SQLITE_OMIT_FOREIGN_KEY +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCheckFk(Vdbe *, int); +#else +# define sqlcipher_sqlite3VdbeCheckFk(p,i) 0 +#endif -int sqlcipher_get_default_hmac_algorithm() { - return default_hmac_algorithm; -} +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE void sqlcipher_sqlite3VdbePrintSql(Vdbe*); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr); +#endif +#ifndef SQLITE_OMIT_UTF16 +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemTranslate(Mem*, u8); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemHandleBom(Mem *pMem); +#endif -int sqlcipher_codec_ctx_get_hmac_algorithm(codec_ctx *ctx) { - return ctx->hmac_algorithm; -} +#ifndef SQLITE_OMIT_INCRBLOB +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemExpandBlob(Mem *); + #define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlcipher_sqlite3VdbeMemExpandBlob(P):0) +#else + #define sqlcipher_sqlite3VdbeMemExpandBlob(x) SQLITE_OK + #define ExpandBlob(P) SQLITE_OK +#endif -/* manipulate KDF algorithm */ -int sqlcipher_set_default_kdf_algorithm(int algorithm) { - default_kdf_algorithm = algorithm; - return SQLITE_OK; -} +#endif /* !defined(SQLITE_VDBEINT_H) */ -int sqlcipher_codec_ctx_set_kdf_algorithm(codec_ctx *ctx, int algorithm) { - ctx->kdf_algorithm = algorithm; - return SQLITE_OK; -} +/************** End of vdbeInt.h *********************************************/ +/************** Continuing where we left off in status.c *********************/ -int sqlcipher_get_default_kdf_algorithm() { - return default_kdf_algorithm; -} +/* +** Variables in which to record status information. +*/ +#if SQLITE_PTRSIZE>4 +typedef sqlcipher_sqlite3_int64 sqlcipher_sqlite3StatValueType; +#else +typedef u32 sqlcipher_sqlite3StatValueType; +#endif +typedef struct sqlcipher_sqlite3StatType sqlcipher_sqlite3StatType; +static SQLITE_WSD struct sqlcipher_sqlite3StatType { + sqlcipher_sqlite3StatValueType nowValue[10]; /* Current value */ + sqlcipher_sqlite3StatValueType mxValue[10]; /* Maximum value */ +} sqlcipher_sqlite3Stat = { {0,}, {0,} }; -int sqlcipher_codec_ctx_get_kdf_algorithm(codec_ctx *ctx) { - return ctx->kdf_algorithm; -} +/* +** Elements of sqlcipher_sqlite3Stat[] are protected by either the memory allocator +** mutex, or by the pcache1 mutex. The following array determines which. +*/ +static const char statMutex[] = { + 0, /* SQLITE_STATUS_MEMORY_USED */ + 1, /* SQLITE_STATUS_PAGECACHE_USED */ + 1, /* SQLITE_STATUS_PAGECACHE_OVERFLOW */ + 0, /* SQLITE_STATUS_SCRATCH_USED */ + 0, /* SQLITE_STATUS_SCRATCH_OVERFLOW */ + 0, /* SQLITE_STATUS_MALLOC_SIZE */ + 0, /* SQLITE_STATUS_PARSER_STACK */ + 1, /* SQLITE_STATUS_PAGECACHE_SIZE */ + 0, /* SQLITE_STATUS_SCRATCH_SIZE */ + 0, /* SQLITE_STATUS_MALLOC_COUNT */ +}; -int sqlcipher_codec_ctx_set_flag(codec_ctx *ctx, unsigned int flag) { - ctx->flags |= flag; - return SQLITE_OK; -} -int sqlcipher_codec_ctx_unset_flag(codec_ctx *ctx, unsigned int flag) { - ctx->flags &= ~flag; - return SQLITE_OK; -} +/* The "wsdStat" macro will resolve to the status information +** state vector. If writable static data is unsupported on the target, +** we have to locate the state vector at run-time. In the more common +** case where writable static data is supported, wsdStat can refer directly +** to the "sqlcipher_sqlite3Stat" state vector declared above. +*/ +#ifdef SQLITE_OMIT_WSD +# define wsdStatInit sqlcipher_sqlite3StatType *x = &GLOBAL(sqlcipher_sqlite3StatType,sqlcipher_sqlite3Stat) +# define wsdStat x[0] +#else +# define wsdStatInit +# define wsdStat sqlcipher_sqlite3Stat +#endif -int sqlcipher_codec_ctx_get_flag(codec_ctx *ctx, unsigned int flag) { - return (ctx->flags & flag) != 0; +/* +** Return the current value of a status parameter. The caller must +** be holding the appropriate mutex. +*/ +SQLITE_PRIVATE sqlcipher_sqlite3_int64 sqlcipher_sqlite3StatusValue(int op){ + wsdStatInit; + assert( op>=0 && op=0 && oppBt->pBt->pPager, error); - ctx->pBt->pBt->db->errCode = error; +/* +** Add N to the value of a status record. The caller must hold the +** appropriate mutex. (Locking is checked by assert()). +** +** The StatusUp() routine can accept positive or negative values for N. +** The value of N is added to the current status value and the high-water +** mark is adjusted if necessary. +** +** The StatusDown() routine lowers the current value by N. The highwater +** mark is unchanged. N must be non-negative for StatusDown(). +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3StatusUp(int op, int N){ + wsdStatInit; + assert( op>=0 && op=0 && opwsdStat.mxValue[op] ){ + wsdStat.mxValue[op] = wsdStat.nowValue[op]; + } } - -int sqlcipher_codec_ctx_get_reservesize(codec_ctx *ctx) { - return ctx->reserve_sz; +SQLITE_PRIVATE void sqlcipher_sqlite3StatusDown(int op, int N){ + wsdStatInit; + assert( N>=0 ); + assert( op>=0 && op=0 && opbuffer; +/* +** Adjust the highwater mark if necessary. +** The caller must hold the appropriate mutex. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3StatusHighwater(int op, int X){ + sqlcipher_sqlite3StatValueType newValue; + wsdStatInit; + assert( X>=0 ); + newValue = (sqlcipher_sqlite3StatValueType)X; + assert( op>=0 && op=0 && opwsdStat.mxValue[op] ){ + wsdStat.mxValue[op] = newValue; + } } -static int sqlcipher_codec_ctx_init_kdf_salt(codec_ctx *ctx) { - sqlcipher_sqlite3_file *fd = sqlcipher_sqlite3PagerFile(ctx->pBt->pBt->pPager); - - if(!ctx->need_kdf_salt) { - return SQLITE_OK; /* don't reload salt when not needed */ +/* +** Query status information. +*/ +SQLITE_API int sqlcipher_sqlite3_status64( + int op, + sqlcipher_sqlite3_int64 *pCurrent, + sqlcipher_sqlite3_int64 *pHighwater, + int resetFlag +){ + sqlcipher_sqlite3_mutex *pMutex; + wsdStatInit; + if( op<0 || op>=ArraySize(wsdStat.nowValue) ){ + return SQLITE_MISUSE_BKPT; } - - /* read salt from header, if present, otherwise generate a new random salt */ - CODEC_TRACE("sqlcipher_codec_ctx_init_kdf_salt: obtaining salt\n"); - if(fd == NULL || fd->pMethods == 0 || sqlcipher_sqlite3OsRead(fd, ctx->kdf_salt, ctx->kdf_salt_sz, 0) != SQLITE_OK) { - CODEC_TRACE("sqlcipher_codec_ctx_init_kdf_salt: unable to read salt from file header, generating random\n"); - if(ctx->provider->random(ctx->provider_ctx, ctx->kdf_salt, ctx->kdf_salt_sz) != SQLITE_OK) return SQLITE_ERROR; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT; +#endif + pMutex = statMutex[op] ? sqlcipher_sqlite3Pcache1Mutex() : sqlcipher_sqlite3MallocMutex(); + sqlcipher_sqlite3_mutex_enter(pMutex); + *pCurrent = wsdStat.nowValue[op]; + *pHighwater = wsdStat.mxValue[op]; + if( resetFlag ){ + wsdStat.mxValue[op] = wsdStat.nowValue[op]; } - ctx->need_kdf_salt = 0; + sqlcipher_sqlite3_mutex_leave(pMutex); + (void)pMutex; /* Prevent warning when SQLITE_THREADSAFE=0 */ return SQLITE_OK; } - -int sqlcipher_codec_ctx_set_kdf_salt(codec_ctx *ctx, unsigned char *salt, int size) { - if(size >= ctx->kdf_salt_sz) { - memcpy(ctx->kdf_salt, salt, ctx->kdf_salt_sz); - ctx->need_kdf_salt = 0; - return SQLITE_OK; - } - return SQLITE_ERROR; -} - -int sqlcipher_codec_ctx_get_kdf_salt(codec_ctx *ctx, void** salt) { - int rc = SQLITE_OK; - if(ctx->need_kdf_salt) { - rc = sqlcipher_codec_ctx_init_kdf_salt(ctx); +SQLITE_API int sqlcipher_sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){ + sqlcipher_sqlite3_int64 iCur = 0, iHwtr = 0; + int rc; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT; +#endif + rc = sqlcipher_sqlite3_status64(op, &iCur, &iHwtr, resetFlag); + if( rc==0 ){ + *pCurrent = (int)iCur; + *pHighwater = (int)iHwtr; } - *salt = ctx->kdf_salt; return rc; } -void sqlcipher_codec_get_keyspec(codec_ctx *ctx, void **zKey, int *nKey) { - *zKey = ctx->read_ctx->keyspec; - *nKey = ctx->keyspec_sz; -} - -int sqlcipher_codec_ctx_set_pagesize(codec_ctx *ctx, int size) { - if(!((size != 0) && ((size & (size - 1)) == 0)) || size < 512 || size > 65536) { - CODEC_TRACE(("cipher_page_size not a power of 2 and between 512 and 65536 inclusive\n")); - return SQLITE_ERROR; +/* +** Return the number of LookasideSlot elements on the linked list +*/ +static u32 countLookasideSlots(LookasideSlot *p){ + u32 cnt = 0; + while( p ){ + p = p->pNext; + cnt++; } - /* attempt to free the existing page buffer */ - sqlcipher_free(ctx->buffer,ctx->page_sz); - ctx->page_sz = size; - - /* pre-allocate a page buffer of PageSize bytes. This will - be used as a persistent buffer for encryption and decryption - operations to avoid overhead of multiple memory allocations*/ - ctx->buffer = sqlcipher_malloc(size); - if(ctx->buffer == NULL) return SQLITE_NOMEM; - - return SQLITE_OK; -} - -int sqlcipher_codec_ctx_get_pagesize(codec_ctx *ctx) { - return ctx->page_sz; -} - -void sqlcipher_set_default_pagesize(int page_size) { - default_page_size = page_size; -} - -int sqlcipher_get_default_pagesize() { - return default_page_size; -} - -void sqlcipher_set_mem_security(int on) { - mem_security_on = on; - mem_security_activated = 0; + return cnt; } -int sqlcipher_get_mem_security() { - return mem_security_on && mem_security_activated; +/* +** Count the number of slots of lookaside memory that are outstanding +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3LookasideUsed(sqlcipher_sqlite3 *db, int *pHighwater){ + u32 nInit = countLookasideSlots(db->lookaside.pInit); + u32 nFree = countLookasideSlots(db->lookaside.pFree); +#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + nInit += countLookasideSlots(db->lookaside.pSmallInit); + nFree += countLookasideSlots(db->lookaside.pSmallFree); +#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ + if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit; + return db->lookaside.nSlot - (nInit+nFree); } +/* +** Query status information for a single database connection +*/ +SQLITE_API int sqlcipher_sqlite3_db_status( + sqlcipher_sqlite3 *db, /* The database connection whose status is desired */ + int op, /* Status verb */ + int *pCurrent, /* Write current value here */ + int *pHighwater, /* Write high-water mark here */ + int resetFlag /* Reset high-water mark if true */ +){ + int rc = SQLITE_OK; /* Return code */ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlcipher_sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwater==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + sqlcipher_sqlite3_mutex_enter(db->mutex); + switch( op ){ + case SQLITE_DBSTATUS_LOOKASIDE_USED: { + *pCurrent = sqlcipher_sqlite3LookasideUsed(db, pHighwater); + if( resetFlag ){ + LookasideSlot *p = db->lookaside.pFree; + if( p ){ + while( p->pNext ) p = p->pNext; + p->pNext = db->lookaside.pInit; + db->lookaside.pInit = db->lookaside.pFree; + db->lookaside.pFree = 0; + } +#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + p = db->lookaside.pSmallFree; + if( p ){ + while( p->pNext ) p = p->pNext; + p->pNext = db->lookaside.pSmallInit; + db->lookaside.pSmallInit = db->lookaside.pSmallFree; + db->lookaside.pSmallFree = 0; + } +#endif + } + break; + } -int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, const void *zKey, int nKey) { - int rc; - codec_ctx *ctx; - - CODEC_TRACE("sqlcipher_codec_ctx_init: allocating context\n"); - - *iCtx = sqlcipher_malloc(sizeof(codec_ctx)); - ctx = *iCtx; - - if(ctx == NULL) return SQLITE_NOMEM; - - ctx->pBt = pDb->pBt; /* assign pointer to database btree structure */ - - /* allocate space for salt data. Then read the first 16 bytes - directly off the database file. This is the salt for the - key derivation function. If we get a short read allocate - a new random salt value */ - CODEC_TRACE("sqlcipher_codec_ctx_init: allocating kdf_salt\n"); - ctx->kdf_salt_sz = FILE_HEADER_SZ; - ctx->kdf_salt = sqlcipher_malloc(ctx->kdf_salt_sz); - if(ctx->kdf_salt == NULL) return SQLITE_NOMEM; - - /* allocate space for separate hmac salt data. We want the - HMAC derivation salt to be different than the encryption - key derivation salt */ - CODEC_TRACE("sqlcipher_codec_ctx_init: allocating hmac_kdf_salt\n"); - ctx->hmac_kdf_salt = sqlcipher_malloc(ctx->kdf_salt_sz); - if(ctx->hmac_kdf_salt == NULL) return SQLITE_NOMEM; - - /* setup default flags */ - ctx->flags = default_flags; - - /* defer attempt to read KDF salt until first use */ - ctx->need_kdf_salt = 1; - - /* setup the crypto provider */ - CODEC_TRACE("sqlcipher_codec_ctx_init: allocating provider\n"); - ctx->provider = (sqlcipher_provider *) sqlcipher_malloc(sizeof(sqlcipher_provider)); - if(ctx->provider == NULL) return SQLITE_NOMEM; - - /* make a copy of the provider to be used for the duration of the context */ - CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: entering SQLCIPHER_MUTEX_PROVIDER\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); - CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: entered SQLCIPHER_MUTEX_PROVIDER\n"); - - memcpy(ctx->provider, default_provider, sizeof(sqlcipher_provider)); - - CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: leaving SQLCIPHER_MUTEX_PROVIDER\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); - CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: left SQLCIPHER_MUTEX_PROVIDER\n"); - - CODEC_TRACE("sqlcipher_codec_ctx_init: calling provider ctx_init\n"); - if((rc = ctx->provider->ctx_init(&ctx->provider_ctx)) != SQLITE_OK) return rc; + case SQLITE_DBSTATUS_LOOKASIDE_HIT: + case SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE: + case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: { + testcase( op==SQLITE_DBSTATUS_LOOKASIDE_HIT ); + testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE ); + testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL ); + assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 ); + assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 ); + *pCurrent = 0; + *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT]; + if( resetFlag ){ + db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0; + } + break; + } - ctx->key_sz = ctx->provider->get_key_sz(ctx->provider_ctx); - ctx->iv_sz = ctx->provider->get_iv_sz(ctx->provider_ctx); - ctx->block_sz = ctx->provider->get_block_sz(ctx->provider_ctx); + /* + ** Return an approximation for the amount of memory currently used + ** by all pagers associated with the given database connection. The + ** highwater mark is meaningless and is returned as zero. + */ + case SQLITE_DBSTATUS_CACHE_USED_SHARED: + case SQLITE_DBSTATUS_CACHE_USED: { + int totalUsed = 0; + int i; + sqlcipher_sqlite3BtreeEnterAll(db); + for(i=0; inDb; i++){ + Btree *pBt = db->aDb[i].pBt; + if( pBt ){ + Pager *pPager = sqlcipher_sqlite3BtreePager(pBt); + int nByte = sqlcipher_sqlite3PagerMemUsed(pPager); + if( op==SQLITE_DBSTATUS_CACHE_USED_SHARED ){ + nByte = nByte / sqlcipher_sqlite3BtreeConnectionCount(pBt); + } + totalUsed += nByte; + } + } + sqlcipher_sqlite3BtreeLeaveAll(db); + *pCurrent = totalUsed; + *pHighwater = 0; + break; + } - /* establic the size for a hex-formated key specification, containing the - raw encryption key and the salt used to generate it format. will be x'hexkey...hexsalt' - so oversize by 3 bytes */ - ctx->keyspec_sz = ((ctx->key_sz + ctx->kdf_salt_sz) * 2) + 3; + /* + ** *pCurrent gets an accurate estimate of the amount of memory used + ** to store the schema for all databases (main, temp, and any ATTACHed + ** databases. *pHighwater is set to zero. + */ + case SQLITE_DBSTATUS_SCHEMA_USED: { + int i; /* Used to iterate through schemas */ + int nByte = 0; /* Used to accumulate return value */ - /* - Always overwrite page size and set to the default because the first page of the database - in encrypted and thus sqlite can't effectively determine the pagesize. this causes an issue in - cases where bytes 16 & 17 of the page header are a power of 2 as reported by John Lehman - */ - CODEC_TRACE("sqlcipher_codec_ctx_init: calling sqlcipher_codec_ctx_set_pagesize with %d\n", default_page_size); - if((rc = sqlcipher_codec_ctx_set_pagesize(ctx, default_page_size)) != SQLITE_OK) return rc; + sqlcipher_sqlite3BtreeEnterAll(db); + db->pnBytesFreed = &nByte; + for(i=0; inDb; i++){ + Schema *pSchema = db->aDb[i].pSchema; + if( ALWAYS(pSchema!=0) ){ + HashElem *p; - /* establish settings for the KDF iterations and fast (HMAC) KDF iterations */ - CODEC_TRACE("sqlcipher_codec_ctx_init: setting default_kdf_iter\n"); - if((rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, default_kdf_iter)) != SQLITE_OK) return rc; + nByte += sqlcipher_sqlite3GlobalConfig.m.xRoundup(sizeof(HashElem)) * ( + pSchema->tblHash.count + + pSchema->trigHash.count + + pSchema->idxHash.count + + pSchema->fkeyHash.count + ); + nByte += sqlcipher_sqlite3_msize(pSchema->tblHash.ht); + nByte += sqlcipher_sqlite3_msize(pSchema->trigHash.ht); + nByte += sqlcipher_sqlite3_msize(pSchema->idxHash.ht); + nByte += sqlcipher_sqlite3_msize(pSchema->fkeyHash.ht); - CODEC_TRACE("sqlcipher_codec_ctx_init: setting fast_kdf_iter\n"); - if((rc = sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, FAST_PBKDF2_ITER)) != SQLITE_OK) return rc; + for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){ + sqlcipher_sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p)); + } + for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){ + sqlcipher_sqlite3DeleteTable(db, (Table *)sqliteHashData(p)); + } + } + } + db->pnBytesFreed = 0; + sqlcipher_sqlite3BtreeLeaveAll(db); - /* set the default HMAC and KDF algorithms which will determine the reserve size */ - CODEC_TRACE("sqlcipher_codec_ctx_init: calling sqlcipher_codec_ctx_set_hmac_algorithm with %d\n", default_hmac_algorithm); - if((rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, default_hmac_algorithm)) != SQLITE_OK) return rc; + *pHighwater = 0; + *pCurrent = nByte; + break; + } - /* Note that use_hmac is a special case that requires recalculation of page size - so we call set_use_hmac to perform setup */ - CODEC_TRACE("sqlcipher_codec_ctx_init: setting use_hmac\n"); - if((rc = sqlcipher_codec_ctx_set_use_hmac(ctx, default_flags & CIPHER_FLAG_HMAC)) != SQLITE_OK) return rc; + /* + ** *pCurrent gets an accurate estimate of the amount of memory used + ** to store all prepared statements. + ** *pHighwater is set to zero. + */ + case SQLITE_DBSTATUS_STMT_USED: { + struct Vdbe *pVdbe; /* Used to iterate through VMs */ + int nByte = 0; /* Used to accumulate return value */ - CODEC_TRACE("sqlcipher_codec_ctx_init: calling sqlcipher_codec_ctx_set_kdf_algorithm with %d\n", default_kdf_algorithm); - if((rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, default_kdf_algorithm)) != SQLITE_OK) return rc; + db->pnBytesFreed = &nByte; + for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){ + sqlcipher_sqlite3VdbeDelete(pVdbe); + } + db->pnBytesFreed = 0; - /* setup the default plaintext header size */ - CODEC_TRACE("sqlcipher_codec_ctx_init: calling sqlcipher_codec_ctx_set_plaintext_header_size with %d\n", default_plaintext_header_sz); - if((rc = sqlcipher_codec_ctx_set_plaintext_header_size(ctx, default_plaintext_header_sz)) != SQLITE_OK) return rc; + *pHighwater = 0; /* IMP: R-64479-57858 */ + *pCurrent = nByte; - /* initialize the read and write sub-contexts. this must happen after key_sz is established */ - CODEC_TRACE("sqlcipher_codec_ctx_init: initializing read_ctx\n"); - if((rc = sqlcipher_cipher_ctx_init(ctx, &ctx->read_ctx)) != SQLITE_OK) return rc; + break; + } - CODEC_TRACE("sqlcipher_codec_ctx_init: initializing write_ctx\n"); - if((rc = sqlcipher_cipher_ctx_init(ctx, &ctx->write_ctx)) != SQLITE_OK) return rc; + /* + ** Set *pCurrent to the total cache hits or misses encountered by all + ** pagers the database handle is connected to. *pHighwater is always set + ** to zero. + */ + case SQLITE_DBSTATUS_CACHE_SPILL: + op = SQLITE_DBSTATUS_CACHE_WRITE+1; + /* no break */ deliberate_fall_through + case SQLITE_DBSTATUS_CACHE_HIT: + case SQLITE_DBSTATUS_CACHE_MISS: + case SQLITE_DBSTATUS_CACHE_WRITE:{ + int i; + int nRet = 0; + assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 ); + assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 ); - /* set the key material on one of the sub cipher contexts and sync them up */ - CODEC_TRACE("sqlcipher_codec_ctx_init: setting pass key\n"); - if((rc = sqlcipher_codec_ctx_set_pass(ctx, zKey, nKey, 0)) != SQLITE_OK) return rc; + for(i=0; inDb; i++){ + if( db->aDb[i].pBt ){ + Pager *pPager = sqlcipher_sqlite3BtreePager(db->aDb[i].pBt); + sqlcipher_sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet); + } + } + *pHighwater = 0; /* IMP: R-42420-56072 */ + /* IMP: R-54100-20147 */ + /* IMP: R-29431-39229 */ + *pCurrent = nRet; + break; + } - CODEC_TRACE("sqlcipher_codec_ctx_init: copying write_ctx to read_ctx\n"); - if((rc = sqlcipher_cipher_ctx_copy(ctx, ctx->write_ctx, ctx->read_ctx)) != SQLITE_OK) return rc; + /* Set *pCurrent to non-zero if there are unresolved deferred foreign + ** key constraints. Set *pCurrent to zero if all foreign key constraints + ** have been satisfied. The *pHighwater is always set to zero. + */ + case SQLITE_DBSTATUS_DEFERRED_FKS: { + *pHighwater = 0; /* IMP: R-11967-56545 */ + *pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0; + break; + } - return SQLITE_OK; + default: { + rc = SQLITE_ERROR; + } + } + sqlcipher_sqlite3_mutex_leave(db->mutex); + return rc; } -/** - * Free and wipe memory associated with a cipher_ctx, including the allocated - * read_ctx and write_ctx. - */ -void sqlcipher_codec_ctx_free(codec_ctx **iCtx) { - codec_ctx *ctx = *iCtx; - CODEC_TRACE("codec_ctx_free: entered iCtx=%p\n", iCtx); - sqlcipher_free(ctx->kdf_salt, ctx->kdf_salt_sz); - sqlcipher_free(ctx->hmac_kdf_salt, ctx->kdf_salt_sz); - sqlcipher_free(ctx->buffer, 0); - - ctx->provider->ctx_free(&ctx->provider_ctx); - sqlcipher_free(ctx->provider, sizeof(sqlcipher_provider)); +/************** End of status.c **********************************************/ +/************** Begin file date.c ********************************************/ +/* +** 2003 October 31 +** +** 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 the C functions that implement date and time +** functions for SQLite. +** +** There is only one exported symbol in this file - the function +** sqlcipher_sqlite3RegisterDateTimeFunctions() found at the bottom of the file. +** All other code has file scope. +** +** SQLite processes all times and dates as julian day numbers. The +** dates and times are stored as the number of days since noon +** in Greenwich on November 24, 4714 B.C. according to the Gregorian +** calendar system. +** +** 1970-01-01 00:00:00 is JD 2440587.5 +** 2000-01-01 00:00:00 is JD 2451544.5 +** +** This implementation requires years to be expressed as a 4-digit number +** which means that only dates between 0000-01-01 and 9999-12-31 can +** be represented, even though julian day numbers allow a much wider +** range of dates. +** +** The Gregorian calendar system is used for all dates and times, +** even those that predate the Gregorian calendar. Historians usually +** use the julian calendar for dates prior to 1582-10-15 and for some +** dates afterwards, depending on locale. Beware of this difference. +** +** The conversion algorithms are implemented based on descriptions +** in the following text: +** +** Jean Meeus +** Astronomical Algorithms, 2nd Edition, 1998 +** ISBN 0-943396-61-1 +** Willmann-Bell, Inc +** Richmond, Virginia (USA) +*/ +/* #include "sqliteInt.h" */ +/* #include */ +/* #include */ +#include - sqlcipher_cipher_ctx_free(ctx, &ctx->read_ctx); - sqlcipher_cipher_ctx_free(ctx, &ctx->write_ctx); - sqlcipher_free(ctx, sizeof(codec_ctx)); -} +#ifndef SQLITE_OMIT_DATETIME_FUNCS -/** convert a 32bit unsigned integer to little endian byte ordering */ -static void sqlcipher_put4byte_le(unsigned char *p, u32 v) { - p[0] = (u8)v; - p[1] = (u8)(v>>8); - p[2] = (u8)(v>>16); - p[3] = (u8)(v>>24); -} +/* +** The MSVC CRT on Windows CE may not have a localtime() function. +** So declare a substitute. The substitute function itself is +** defined in "os_win.c". +*/ +#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \ + (!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API) +struct tm *__cdecl localtime(const time_t *); +#endif -static int sqlcipher_page_hmac(codec_ctx *ctx, cipher_ctx *c_ctx, Pgno pgno, unsigned char *in, int in_sz, unsigned char *out) { - unsigned char pgno_raw[sizeof(pgno)]; - /* we may convert page number to consistent representation before calculating MAC for - compatibility across big-endian and little-endian platforms. +/* +** A structure for holding a single date and time. +*/ +typedef struct DateTime DateTime; +struct DateTime { + sqlcipher_sqlite3_int64 iJD; /* The julian day number times 86400000 */ + int Y, M, D; /* Year, month, and day */ + int h, m; /* Hour and minutes */ + int tz; /* Timezone offset in minutes */ + double s; /* Seconds */ + char validJD; /* True (1) if iJD is valid */ + char rawS; /* Raw numeric value stored in s */ + char validYMD; /* True (1) if Y,M,D are valid */ + char validHMS; /* True (1) if h,m,s are valid */ + char validTZ; /* True (1) if tz is valid */ + char tzSet; /* Timezone was set explicitly */ + char isError; /* An overflow has occurred */ +}; - Note: The public release of sqlcipher 2.0.0 to 2.0.6 had a bug where the bytes of pgno - were used directly in the MAC. SQLCipher convert's to little endian by default to preserve - backwards compatibility on the most popular platforms, but can optionally be configured - to use either big endian or native byte ordering via pragma. */ - if(ctx->flags & CIPHER_FLAG_LE_PGNO) { /* compute hmac using little endian pgno*/ - sqlcipher_put4byte_le(pgno_raw, pgno); - } else if(ctx->flags & CIPHER_FLAG_BE_PGNO) { /* compute hmac using big endian pgno */ - sqlcipher_sqlite3Put4byte(pgno_raw, pgno); /* sqlcipher_sqlite3Put4byte converts 32bit uint to big endian */ - } else { /* use native byte ordering */ - memcpy(pgno_raw, &pgno, sizeof(pgno)); - } +/* +** Convert zDate into one or more integers according to the conversion +** specifier zFormat. +** +** zFormat[] contains 4 characters for each integer converted, except for +** the last integer which is specified by three characters. The meaning +** of a four-character format specifiers ABCD is: +** +** A: number of digits to convert. Always "2" or "4". +** B: minimum value. Always "0" or "1". +** C: maximum value, decoded as: +** a: 12 +** b: 14 +** c: 24 +** d: 31 +** e: 59 +** f: 9999 +** D: the separator character, or \000 to indicate this is the +** last number to convert. +** +** Example: To translate an ISO-8601 date YYYY-MM-DD, the format would +** be "40f-21a-20c". The "40f-" indicates the 4-digit year followed by "-". +** The "21a-" indicates the 2-digit month followed by "-". The "20c" indicates +** the 2-digit day which is the last integer in the set. +** +** The function returns the number of successful conversions. +*/ +static int getDigits(const char *zDate, const char *zFormat, ...){ + /* The aMx[] array translates the 3rd character of each format + ** spec into a max size: a b c d e f */ + static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 }; + va_list ap; + int cnt = 0; + char nextC; + va_start(ap, zFormat); + do{ + char N = zFormat[0] - '0'; + char min = zFormat[1] - '0'; + int val = 0; + u16 max; - /* include the encrypted page data, initialization vector, and page number in HMAC. This will - prevent both tampering with the ciphertext, manipulation of the IV, or resequencing otherwise - valid pages out of order in a database */ - return ctx->provider->hmac( - ctx->provider_ctx, ctx->hmac_algorithm, c_ctx->hmac_key, - ctx->key_sz, in, - in_sz, (unsigned char*) &pgno_raw, - sizeof(pgno), out); + assert( zFormat[2]>='a' && zFormat[2]<='f' ); + max = aMx[zFormat[2] - 'a']; + nextC = zFormat[3]; + val = 0; + while( N-- ){ + if( !sqlcipher_sqlite3Isdigit(*zDate) ){ + goto end_getDigits; + } + val = val*10 + *zDate - '0'; + zDate++; + } + if( val<(int)min || val>(int)max || (nextC!=0 && nextC!=*zDate) ){ + goto end_getDigits; + } + *va_arg(ap,int*) = val; + zDate++; + cnt++; + zFormat += 4; + }while( nextC ); +end_getDigits: + va_end(ap); + return cnt; } /* - * ctx - codec context - * pgno - page number in database - * size - size in bytes of input and output buffers - * mode - 1 to encrypt, 0 to decrypt - * in - pointer to input bytes - * out - pouter to output bytes - */ -int sqlcipher_page_cipher(codec_ctx *ctx, int for_ctx, Pgno pgno, int mode, int page_sz, unsigned char *in, unsigned char *out) { - cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx; - unsigned char *iv_in, *iv_out, *hmac_in, *hmac_out, *out_start; - int size; - - /* calculate some required positions into various buffers */ - size = page_sz - ctx->reserve_sz; /* adjust size to useable size and memset reserve at end of page */ - iv_out = out + size; - iv_in = in + size; - - /* hmac will be written immediately after the initialization vector. the remainder of the page reserve will contain - random bytes. note, these pointers are only valid when using hmac */ - hmac_in = in + size + ctx->iv_sz; - hmac_out = out + size + ctx->iv_sz; - out_start = out; /* note the original position of the output buffer pointer, as out will be rewritten during encryption */ - - CODEC_TRACE("codec_cipher:entered pgno=%d, mode=%d, size=%d\n", pgno, mode, size); - CODEC_HEXDUMP("codec_cipher: input page data", in, page_sz); - - /* the key size should never be zero. If it is, error out. */ - if(ctx->key_sz == 0) { - CODEC_TRACE("codec_cipher: error possible context corruption, key_sz is zero for pgno=%d\n", pgno); - goto error; +** Parse a timezone extension on the end of a date-time. +** The extension is of the form: +** +** (+/-)HH:MM +** +** Or the "zulu" notation: +** +** Z +** +** If the parse is successful, write the number of minutes +** of change in p->tz and return 0. If a parser error occurs, +** return non-zero. +** +** A missing specifier is not considered an error. +*/ +static int parseTimezone(const char *zDate, DateTime *p){ + int sgn = 0; + int nHr, nMn; + int c; + while( sqlcipher_sqlite3Isspace(*zDate) ){ zDate++; } + p->tz = 0; + c = *zDate; + if( c=='-' ){ + sgn = -1; + }else if( c=='+' ){ + sgn = +1; + }else if( c=='Z' || c=='z' ){ + zDate++; + goto zulu_time; + }else{ + return c!=0; } - - if(mode == CIPHER_ENCRYPT) { - /* start at front of the reserve block, write random data to the end */ - if(ctx->provider->random(ctx->provider_ctx, iv_out, ctx->reserve_sz) != SQLITE_OK) goto error; - } else { /* CIPHER_DECRYPT */ - memcpy(iv_out, iv_in, ctx->iv_sz); /* copy the iv from the input to output buffer */ + zDate++; + if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){ + return 1; } + zDate += 5; + p->tz = sgn*(nMn + nHr*60); +zulu_time: + while( sqlcipher_sqlite3Isspace(*zDate) ){ zDate++; } + p->tzSet = 1; + return *zDate!=0; +} - if((ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_DECRYPT) && !ctx->skip_read_hmac) { - if(sqlcipher_page_hmac(ctx, c_ctx, pgno, in, size + ctx->iv_sz, hmac_out) != SQLITE_OK) { - CODEC_TRACE("codec_cipher: hmac operation on decrypt failed for pgno=%d\n", pgno); - goto error; +/* +** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF. +** The HH, MM, and SS must each be exactly 2 digits. The +** fractional seconds FFFF can be one or more digits. +** +** Return 1 if there is a parsing error and 0 on success. +*/ +static int parseHhMmSs(const char *zDate, DateTime *p){ + int h, m, s; + double ms = 0.0; + if( getDigits(zDate, "20c:20e", &h, &m)!=2 ){ + return 1; + } + zDate += 5; + if( *zDate==':' ){ + zDate++; + if( getDigits(zDate, "20e", &s)!=1 ){ + return 1; } - - CODEC_TRACE("codec_cipher: comparing hmac on in=%p out=%p hmac_sz=%d\n", hmac_in, hmac_out, ctx->hmac_sz); - if(sqlcipher_memcmp(hmac_in, hmac_out, ctx->hmac_sz) != 0) { /* the hmac check failed */ - if(sqlcipher_ismemset(in, 0, page_sz) == 0) { - /* first check if the entire contents of the page is zeros. If so, this page - resulted from a short read (i.e. sqlite attempted to pull a page after the end of the file. these - short read failures must be ignored for autovaccum mode to work so wipe the output buffer - and return SQLITE_OK to skip the decryption step. */ - CODEC_TRACE("codec_cipher: zeroed page (short read) for pgno %d, encryption but returning SQLITE_OK\n", pgno); - sqlcipher_memset(out, 0, page_sz); - return SQLITE_OK; - } else { - /* if the page memory is not all zeros, it means the there was data and a hmac on the page. - since the check failed, the page was either tampered with or corrupted. wipe the output buffer, - and return SQLITE_ERROR to the caller */ - CODEC_TRACE("codec_cipher: hmac check failed for pgno=%d returning SQLITE_ERROR\n", pgno); - goto error; + zDate += 2; + if( *zDate=='.' && sqlcipher_sqlite3Isdigit(zDate[1]) ){ + double rScale = 1.0; + zDate++; + while( sqlcipher_sqlite3Isdigit(*zDate) ){ + ms = ms*10.0 + *zDate - '0'; + rScale *= 10.0; + zDate++; } + ms /= rScale; } + }else{ + s = 0; } - - if(ctx->provider->cipher(ctx->provider_ctx, mode, c_ctx->key, ctx->key_sz, iv_out, in, size, out) != SQLITE_OK) { - CODEC_TRACE("codec_cipher: cipher operation mode=%d failed for pgno=%d returning SQLITE_ERROR\n", mode, pgno); - goto error; - }; - - if((ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_ENCRYPT)) { - if(sqlcipher_page_hmac(ctx, c_ctx, pgno, out_start, size + ctx->iv_sz, hmac_out) != SQLITE_OK) { - CODEC_TRACE("codec_cipher: hmac operation on encrypt failed for pgno=%d\n", pgno); - goto error; - }; - } - - CODEC_HEXDUMP("codec_cipher: output page data", out_start, page_sz); - - return SQLITE_OK; -error: - sqlcipher_memset(out, 0, page_sz); - return SQLITE_ERROR; + p->validJD = 0; + p->rawS = 0; + p->validHMS = 1; + p->h = h; + p->m = m; + p->s = s + ms; + if( parseTimezone(zDate, p) ) return 1; + p->validTZ = (p->tz!=0)?1:0; + return 0; } -/** - * Derive an encryption key for a cipher contex key based on the raw password. - * - * If the raw key data is formated as x'hex' and there are exactly enough hex chars to fill - * the key (i.e 64 hex chars for a 256 bit key) then the key data will be used directly. - - * Else, if the raw key data is formated as x'hex' and there are exactly enough hex chars to fill - * the key and the salt (i.e 92 hex chars for a 256 bit key and 16 byte salt) then it will be unpacked - * as the key followed by the salt. - * - * Otherwise, a key data will be derived using PBKDF2 - * - * returns SQLITE_OK if initialization was successful - * returns SQLITE_ERROR if the key could't be derived (for instance if pass is NULL or pass_sz is 0) - */ -static int sqlcipher_cipher_ctx_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) { - int rc; - CODEC_TRACE("cipher_ctx_key_derive: entered c_ctx->pass=%s, c_ctx->pass_sz=%d \ - ctx->kdf_salt=%p ctx->kdf_salt_sz=%d ctx->kdf_iter=%d \ - ctx->hmac_kdf_salt=%p, ctx->fast_kdf_iter=%d ctx->key_sz=%d\n", - c_ctx->pass, c_ctx->pass_sz, ctx->kdf_salt, ctx->kdf_salt_sz, ctx->kdf_iter, - ctx->hmac_kdf_salt, ctx->fast_kdf_iter, ctx->key_sz); - - - if(c_ctx->pass && c_ctx->pass_sz) { /* if key material is present on the context for derivation */ +/* +** Put the DateTime object into its error state. +*/ +static void datetimeError(DateTime *p){ + memset(p, 0, sizeof(*p)); + p->isError = 1; +} - /* if necessary, initialize the salt from the header or random source */ - if(ctx->need_kdf_salt) { - if((rc = sqlcipher_codec_ctx_init_kdf_salt(ctx)) != SQLITE_OK) return rc; - } +/* +** Convert from YYYY-MM-DD HH:MM:SS to julian day. We always assume +** that the YYYY-MM-DD is according to the Gregorian calendar. +** +** Reference: Meeus page 61 +*/ +static void computeJD(DateTime *p){ + int Y, M, D, A, B, X1, X2; - if (c_ctx->pass_sz == ((ctx->key_sz * 2) + 3) && sqlcipher_sqlite3StrNICmp((const char *)c_ctx->pass ,"x'", 2) == 0 && cipher_isHex(c_ctx->pass + 2, ctx->key_sz * 2)) { - int n = c_ctx->pass_sz - 3; /* adjust for leading x' and tailing ' */ - const unsigned char *z = c_ctx->pass + 2; /* adjust lead offset of x' */ - CODEC_TRACE("cipher_ctx_key_derive: using raw key from hex\n"); - cipher_hex2bin(z, n, c_ctx->key); - } else if (c_ctx->pass_sz == (((ctx->key_sz + ctx->kdf_salt_sz) * 2) + 3) && sqlcipher_sqlite3StrNICmp((const char *)c_ctx->pass ,"x'", 2) == 0 && cipher_isHex(c_ctx->pass + 2, (ctx->key_sz + ctx->kdf_salt_sz) * 2)) { - const unsigned char *z = c_ctx->pass + 2; /* adjust lead offset of x' */ - CODEC_TRACE("cipher_ctx_key_derive: using raw key from hex\n"); - cipher_hex2bin(z, (ctx->key_sz * 2), c_ctx->key); - cipher_hex2bin(z + (ctx->key_sz * 2), (ctx->kdf_salt_sz * 2), ctx->kdf_salt); - } else { - CODEC_TRACE("cipher_ctx_key_derive: deriving key using full PBKDF2 with %d iterations\n", ctx->kdf_iter); - if(ctx->provider->kdf(ctx->provider_ctx, ctx->kdf_algorithm, c_ctx->pass, c_ctx->pass_sz, - ctx->kdf_salt, ctx->kdf_salt_sz, ctx->kdf_iter, - ctx->key_sz, c_ctx->key) != SQLITE_OK) return SQLITE_ERROR; + if( p->validJD ) return; + if( p->validYMD ){ + Y = p->Y; + M = p->M; + D = p->D; + }else{ + Y = 2000; /* If no YMD specified, assume 2000-Jan-01 */ + M = 1; + D = 1; + } + if( Y<-4713 || Y>9999 || p->rawS ){ + datetimeError(p); + return; + } + if( M<=2 ){ + Y--; + M += 12; + } + A = Y/100; + B = 2 - A + (A/4); + X1 = 36525*(Y+4716)/100; + X2 = 306001*(M+1)/10000; + p->iJD = (sqlcipher_sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000); + p->validJD = 1; + if( p->validHMS ){ + p->iJD += p->h*3600000 + p->m*60000 + (sqlcipher_sqlite3_int64)(p->s*1000); + if( p->validTZ ){ + p->iJD -= p->tz*60000; + p->validYMD = 0; + p->validHMS = 0; + p->validTZ = 0; } + } +} - /* set the context "keyspec" containing the hex-formatted key and salt to be used when attaching databases */ - if((rc = sqlcipher_cipher_ctx_set_keyspec(ctx, c_ctx, c_ctx->key)) != SQLITE_OK) return rc; - - /* if this context is setup to use hmac checks, generate a seperate and different - key for HMAC. In this case, we use the output of the previous KDF as the input to - this KDF run. This ensures a distinct but predictable HMAC key. */ - if(ctx->flags & CIPHER_FLAG_HMAC) { - int i; - - /* start by copying the kdf key into the hmac salt slot - then XOR it with the fixed hmac salt defined at compile time - this ensures that the salt passed in to derive the hmac key, while - easy to derive and publically known, is not the same as the salt used - to generate the encryption key */ - memcpy(ctx->hmac_kdf_salt, ctx->kdf_salt, ctx->kdf_salt_sz); - for(i = 0; i < ctx->kdf_salt_sz; i++) { - ctx->hmac_kdf_salt[i] ^= hmac_salt_mask; - } - - CODEC_TRACE("cipher_ctx_key_derive: deriving hmac key from encryption key using PBKDF2 with %d iterations\n", - ctx->fast_kdf_iter); - +/* +** Parse dates of the form +** +** YYYY-MM-DD HH:MM:SS.FFF +** YYYY-MM-DD HH:MM:SS +** YYYY-MM-DD HH:MM +** YYYY-MM-DD +** +** Write the result into the DateTime structure and return 0 +** on success and 1 if the input string is not a well-formed +** date. +*/ +static int parseYyyyMmDd(const char *zDate, DateTime *p){ + int Y, M, D, neg; - if(ctx->provider->kdf(ctx->provider_ctx, ctx->kdf_algorithm, c_ctx->key, ctx->key_sz, - ctx->hmac_kdf_salt, ctx->kdf_salt_sz, ctx->fast_kdf_iter, - ctx->key_sz, c_ctx->hmac_key) != SQLITE_OK) return SQLITE_ERROR; - } + if( zDate[0]=='-' ){ + zDate++; + neg = 1; + }else{ + neg = 0; + } + if( getDigits(zDate, "40f-21a-21d", &Y, &M, &D)!=3 ){ + return 1; + } + zDate += 10; + while( sqlcipher_sqlite3Isspace(*zDate) || 'T'==*(u8*)zDate ){ zDate++; } + if( parseHhMmSs(zDate, p)==0 ){ + /* We got the time */ + }else if( *zDate==0 ){ + p->validHMS = 0; + }else{ + return 1; + } + p->validJD = 0; + p->validYMD = 1; + p->Y = neg ? -Y : Y; + p->M = M; + p->D = D; + if( p->validTZ ){ + computeJD(p); + } + return 0; +} - c_ctx->derive_key = 0; - return SQLITE_OK; - }; - return SQLITE_ERROR; +/* +** Set the time to the current time reported by the VFS. +** +** Return the number of errors. +*/ +static int setDateTimeToCurrent(sqlcipher_sqlite3_context *context, DateTime *p){ + p->iJD = sqlcipher_sqlite3StmtCurrentTime(context); + if( p->iJD>0 ){ + p->validJD = 1; + return 0; + }else{ + return 1; + } } -int sqlcipher_codec_key_derive(codec_ctx *ctx) { - /* derive key on first use if necessary */ - if(ctx->read_ctx->derive_key) { - if(sqlcipher_cipher_ctx_key_derive(ctx, ctx->read_ctx) != SQLITE_OK) return SQLITE_ERROR; +/* +** Input "r" is a numeric quantity which might be a julian day number, +** or the number of seconds since 1970. If the value if r is within +** range of a julian day number, install it as such and set validJD. +** If the value is a valid unix timestamp, put it in p->s and set p->rawS. +*/ +static void setRawDateNumber(DateTime *p, double r){ + p->s = r; + p->rawS = 1; + if( r>=0.0 && r<5373484.5 ){ + p->iJD = (sqlcipher_sqlite3_int64)(r*86400000.0 + 0.5); + p->validJD = 1; } +} - if(ctx->write_ctx->derive_key) { - if(sqlcipher_cipher_ctx_cmp(ctx->write_ctx, ctx->read_ctx) == 0) { - /* the relevant parameters are the same, just copy read key */ - if(sqlcipher_cipher_ctx_copy(ctx, ctx->write_ctx, ctx->read_ctx) != SQLITE_OK) return SQLITE_ERROR; - } else { - if(sqlcipher_cipher_ctx_key_derive(ctx, ctx->write_ctx) != SQLITE_OK) return SQLITE_ERROR; - } +/* +** Attempt to parse the given string into a julian day number. Return +** the number of errors. +** +** The following are acceptable forms for the input string: +** +** YYYY-MM-DD HH:MM:SS.FFF +/-HH:MM +** DDDD.DD +** now +** +** In the first form, the +/-HH:MM is always optional. The fractional +** seconds extension (the ".FFF") is optional. The seconds portion +** (":SS.FFF") is option. The year and date can be omitted as long +** as there is a time string. The time string can be omitted as long +** as there is a year and date. +*/ +static int parseDateOrTime( + sqlcipher_sqlite3_context *context, + const char *zDate, + DateTime *p +){ + double r; + if( parseYyyyMmDd(zDate,p)==0 ){ + return 0; + }else if( parseHhMmSs(zDate, p)==0 ){ + return 0; + }else if( sqlcipher_sqlite3StrICmp(zDate,"now")==0 && sqlcipher_sqlite3NotPureFunc(context) ){ + return setDateTimeToCurrent(context, p); + }else if( sqlcipher_sqlite3AtoF(zDate, &r, sqlcipher_sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){ + setRawDateNumber(p, r); + return 0; } + return 1; +} - /* TODO: wipe and free passphrase after key derivation */ - if(ctx->store_pass != 1) { - sqlcipher_cipher_ctx_set_pass(ctx->read_ctx, NULL, 0); - sqlcipher_cipher_ctx_set_pass(ctx->write_ctx, NULL, 0); +/* The julian day number for 9999-12-31 23:59:59.999 is 5373484.4999999. +** Multiplying this by 86400000 gives 464269060799999 as the maximum value +** for DateTime.iJD. +** +** But some older compilers (ex: gcc 4.2.1 on older Macs) cannot deal with +** such a large integer literal, so we have to encode it. +*/ +#define INT_464269060799999 ((((i64)0x1a640)<<32)|0x1072fdff) + +/* +** Return TRUE if the given julian day number is within range. +** +** The input is the JulianDay times 86400000. +*/ +static int validJulianDay(sqlcipher_sqlite3_int64 iJD){ + return iJD>=0 && iJD<=INT_464269060799999; +} + +/* +** Compute the Year, Month, and Day from the julian day number. +*/ +static void computeYMD(DateTime *p){ + int Z, A, B, C, D, E, X1; + if( p->validYMD ) return; + if( !p->validJD ){ + p->Y = 2000; + p->M = 1; + p->D = 1; + }else if( !validJulianDay(p->iJD) ){ + datetimeError(p); + return; + }else{ + Z = (int)((p->iJD + 43200000)/86400000); + A = (int)((Z - 1867216.25)/36524.25); + A = Z + 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); + X1 = (int)(30.6001*E); + p->D = B - D - X1; + p->M = E<14 ? E-1 : E-13; + p->Y = p->M>2 ? C - 4716 : C - 4715; } + p->validYMD = 1; +} - return SQLITE_OK; +/* +** Compute the Hour, Minute, and Seconds from the julian day number. +*/ +static void computeHMS(DateTime *p){ + int s; + if( p->validHMS ) return; + computeJD(p); + s = (int)((p->iJD + 43200000) % 86400000); + p->s = s/1000.0; + s = (int)p->s; + p->s -= s; + p->h = s/3600; + s -= p->h*3600; + p->m = s/60; + p->s += s - p->m*60; + p->rawS = 0; + p->validHMS = 1; } -int sqlcipher_codec_key_copy(codec_ctx *ctx, int source) { - if(source == CIPHER_READ_CTX) { - return sqlcipher_cipher_ctx_copy(ctx, ctx->write_ctx, ctx->read_ctx); - } else { - return sqlcipher_cipher_ctx_copy(ctx, ctx->read_ctx, ctx->write_ctx); - } +/* +** Compute both YMD and HMS +*/ +static void computeYMD_HMS(DateTime *p){ + computeYMD(p); + computeHMS(p); } -const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx) { - return ctx->provider->get_provider_name(ctx->provider_ctx); +/* +** Clear the YMD and HMS and the TZ +*/ +static void clearYMD_HMS_TZ(DateTime *p){ + p->validYMD = 0; + p->validHMS = 0; + p->validTZ = 0; } +#ifndef SQLITE_OMIT_LOCALTIME +/* +** On recent Windows platforms, the localtime_s() function is available +** as part of the "Secure CRT". It is essentially equivalent to +** localtime_r() available under most POSIX platforms, except that the +** order of the parameters is reversed. +** +** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx. +** +** If the user has not indicated to use localtime_r() or localtime_s() +** already, check for an MSVC build environment that provides +** localtime_s(). +*/ +#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S \ + && defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) +#undef HAVE_LOCALTIME_S +#define HAVE_LOCALTIME_S 1 +#endif -static int sqlcipher_check_connection(const char *filename, char *key, int key_sz, char *sql, int *user_version, char** journal_mode) { +/* +** The following routine implements the rough equivalent of localtime_r() +** using whatever operating-system specific localtime facility that +** is available. This routine returns 0 on success and +** non-zero on any kind of error. +** +** If the sqlcipher_sqlite3GlobalConfig.bLocaltimeFault variable is non-zero then this +** routine will always fail. If bLocaltimeFault is nonzero and +** sqlcipher_sqlite3GlobalConfig.xAltLocaltime is not NULL, then xAltLocaltime() is +** invoked in place of the OS-defined localtime() function. +** +** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C +** library function localtime_r() is used to assist in the calculation of +** local time. +*/ +static int osLocaltime(time_t *t, struct tm *pTm){ int rc; - sqlcipher_sqlite3 *db = NULL; - sqlcipher_sqlite3_stmt *statement = NULL; - char *query_journal_mode = "PRAGMA journal_mode;"; - char *query_user_version = "PRAGMA user_version;"; - - rc = sqlcipher_sqlite3_open(filename, &db); - if(rc != SQLITE_OK) goto cleanup; - - rc = sqlcipher_sqlite3_key(db, key, key_sz); - if(rc != SQLITE_OK) goto cleanup; - - rc = sqlcipher_sqlite3_exec(db, sql, NULL, NULL, NULL); - if(rc != SQLITE_OK) goto cleanup; - - /* start by querying the user version. - this will fail if the key is incorrect */ - rc = sqlcipher_sqlite3_prepare(db, query_user_version, -1, &statement, NULL); - if(rc != SQLITE_OK) goto cleanup; - - rc = sqlcipher_sqlite3_step(statement); - if(rc == SQLITE_ROW) { - *user_version = sqlcipher_sqlite3_column_int(statement, 0); - } else { - goto cleanup; +#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S + struct tm *pX; +#if SQLITE_THREADSAFE>0 + sqlcipher_sqlite3_mutex *mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); +#endif + sqlcipher_sqlite3_mutex_enter(mutex); + pX = localtime(t); +#ifndef SQLITE_UNTESTABLE + if( sqlcipher_sqlite3GlobalConfig.bLocaltimeFault ){ + if( sqlcipher_sqlite3GlobalConfig.xAltLocaltime!=0 + && 0==sqlcipher_sqlite3GlobalConfig.xAltLocaltime((const void*)t,(void*)pTm) + ){ + pX = pTm; + }else{ + pX = 0; + } } - sqlcipher_sqlite3_finalize(statement); - - rc = sqlcipher_sqlite3_prepare(db, query_journal_mode, -1, &statement, NULL); - if(rc != SQLITE_OK) goto cleanup; - - rc = sqlcipher_sqlite3_step(statement); - if(rc == SQLITE_ROW) { - *journal_mode = sqlcipher_sqlite3_mprintf("%s", sqlcipher_sqlite3_column_text(statement, 0)); - } else { - goto cleanup; +#endif + if( pX ) *pTm = *pX; +#if SQLITE_THREADSAFE>0 + sqlcipher_sqlite3_mutex_leave(mutex); +#endif + rc = pX==0; +#else +#ifndef SQLITE_UNTESTABLE + if( sqlcipher_sqlite3GlobalConfig.bLocaltimeFault ){ + if( sqlcipher_sqlite3GlobalConfig.xAltLocaltime!=0 ){ + return sqlcipher_sqlite3GlobalConfig.xAltLocaltime((const void*)t,(void*)pTm); + }else{ + return 1; + } } - rc = SQLITE_OK; - /* cleanup will finalize open statement */ - -cleanup: - if(statement) sqlcipher_sqlite3_finalize(statement); - if(db) sqlcipher_sqlite3_close(db); +#endif +#if HAVE_LOCALTIME_R + rc = localtime_r(t, pTm)==0; +#else + rc = localtime_s(pTm, t); +#endif /* HAVE_LOCALTIME_R */ +#endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */ return rc; } +#endif /* SQLITE_OMIT_LOCALTIME */ -int sqlcipher_codec_ctx_integrity_check(codec_ctx *ctx, Parse *pParse, char *column) { - Pgno page = 1; - int rc = 0; - char *result; - unsigned char *hmac_out = NULL; - sqlcipher_sqlite3_file *fd = sqlcipher_sqlite3PagerFile(ctx->pBt->pBt->pPager); - i64 file_sz; - Vdbe *v = sqlcipher_sqlite3GetVdbe(pParse); - sqlcipher_sqlite3VdbeSetNumCols(v, 1); - sqlcipher_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, column, SQLITE_STATIC); +#ifndef SQLITE_OMIT_LOCALTIME +/* +** Assuming the input DateTime is UTC, move it to its localtime equivalent. +*/ +static int toLocaltime( + DateTime *p, /* Date at which to calculate offset */ + sqlcipher_sqlite3_context *pCtx /* Write error here if one occurs */ +){ + time_t t; + struct tm sLocal; + int iYearDiff; - if(fd == NULL || fd->pMethods == 0) { - sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, "database file is undefined", P4_TRANSIENT); - sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - goto cleanup; - } + /* Initialize the contents of sLocal to avoid a compiler warning. */ + memset(&sLocal, 0, sizeof(sLocal)); - if(!(ctx->flags & CIPHER_FLAG_HMAC)) { - sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, "HMAC is not enabled, unable to integrity check", P4_TRANSIENT); - sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - goto cleanup; + computeJD(p); + if( p->iJD<2108667600*(i64)100000 /* 1970-01-01 */ + || p->iJD>2130141456*(i64)100000 /* 2038-01-18 */ + ){ + /* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only + ** works for years between 1970 and 2037. For dates outside this range, + ** SQLite attempts to map the year into an equivalent year within this + ** range, do the calculation, then map the year back. + */ + DateTime x = *p; + computeYMD_HMS(&x); + iYearDiff = (2000 + x.Y%4) - x.Y; + x.Y += iYearDiff; + x.validJD = 0; + computeJD(&x); + t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); + }else{ + iYearDiff = 0; + t = (time_t)(p->iJD/1000 - 21086676*(i64)10000); } - - if((rc = sqlcipher_codec_key_derive(ctx)) != SQLITE_OK) { - sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, "unable to derive keys", P4_TRANSIENT); - sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - goto cleanup; + if( osLocaltime(&t, &sLocal) ){ + sqlcipher_sqlite3_result_error(pCtx, "local time unavailable", -1); + return SQLITE_ERROR; } + p->Y = sLocal.tm_year + 1900 - iYearDiff; + p->M = sLocal.tm_mon + 1; + p->D = sLocal.tm_mday; + p->h = sLocal.tm_hour; + p->m = sLocal.tm_min; + p->s = sLocal.tm_sec + (p->iJD%1000)*0.001; + p->validYMD = 1; + p->validHMS = 1; + p->validJD = 0; + p->rawS = 0; + p->validTZ = 0; + p->isError = 0; + return SQLITE_OK; +} +#endif /* SQLITE_OMIT_LOCALTIME */ - sqlcipher_sqlite3OsFileSize(fd, &file_sz); - hmac_out = sqlcipher_malloc(ctx->hmac_sz); - - for(page = 1; page <= file_sz / ctx->page_sz; page++) { - int offset = (page - 1) * ctx->page_sz; - int payload_sz = ctx->page_sz - ctx->reserve_sz + ctx->iv_sz; - int read_sz = ctx->page_sz; - - /* skip integrity check on PAGER_MJ_PGNO since it will have no valid content */ - if(sqlcipher_sqlite3pager_is_mj_pgno(ctx->pBt->pBt->pPager, page)) continue; +/* +** The following table defines various date transformations of the form +** +** 'NNN days' +** +** Where NNN is an arbitrary floating-point number and "days" can be one +** of several units of time. +*/ +static const struct { + u8 nName; /* Length of the name */ + char zName[7]; /* Name of the transformation */ + float rLimit; /* Maximum NNN value for this transform */ + float rXform; /* Constant used for this transform */ +} aXformType[] = { + { 6, "second", 4.6427e+14, 1.0 }, + { 6, "minute", 7.7379e+12, 60.0 }, + { 4, "hour", 1.2897e+11, 3600.0 }, + { 3, "day", 5373485.0, 86400.0 }, + { 5, "month", 176546.0, 2592000.0 }, + { 4, "year", 14713.0, 31536000.0 }, +}; - if(page==1) { - int page1_offset = ctx->plaintext_header_sz ? ctx->plaintext_header_sz : FILE_HEADER_SZ; - read_sz = read_sz - page1_offset; - payload_sz = payload_sz - page1_offset; - offset += page1_offset; +/* +** Process a modifier to a date-time stamp. The modifiers are +** as follows: +** +** NNN days +** NNN hours +** NNN minutes +** NNN.NNNN seconds +** NNN months +** NNN years +** start of month +** start of year +** start of week +** start of day +** weekday N +** unixepoch +** localtime +** utc +** +** Return 0 on success and 1 if there is any kind of error. If the error +** is in a system call (i.e. localtime()), then an error message is written +** to context pCtx. If the error is an unrecognized modifier, no error is +** written to pCtx. +*/ +static int parseModifier( + sqlcipher_sqlite3_context *pCtx, /* Function context */ + const char *z, /* The text of the modifier */ + int n, /* Length of zMod in bytes */ + DateTime *p, /* The date/time value to be modified */ + int idx /* Parameter index of the modifier */ +){ + int rc = 1; + double r; + switch(sqlcipher_sqlite3UpperToLower[(u8)z[0]] ){ + case 'a': { + /* + ** auto + ** + ** If rawS is available, then interpret as a julian day number, or + ** a unix timestamp, depending on its magnitude. + */ + if( sqlcipher_sqlite3_stricmp(z, "auto")==0 ){ + if( idx>1 ) return 1; /* IMP: R-33611-57934 */ + if( !p->rawS || p->validJD ){ + rc = 0; + p->rawS = 0; + }else if( p->s>=-21086676*(i64)10000 /* -4713-11-24 12:00:00 */ + && p->s<=(25340230*(i64)10000)+799 /* 9999-12-31 23:59:59 */ + ){ + r = p->s*1000.0 + 210866760000000.0; + clearYMD_HMS_TZ(p); + p->iJD = (sqlcipher_sqlite3_int64)(r + 0.5); + p->validJD = 1; + p->rawS = 0; + rc = 0; + } + } + break; + } + case 'j': { + /* + ** julianday + ** + ** Always interpret the prior number as a julian-day value. If this + ** is not the first modifier, or if the prior argument is not a numeric + ** value in the allowed range of julian day numbers understood by + ** SQLite (0..5373484.5) then the result will be NULL. + */ + if( sqlcipher_sqlite3_stricmp(z, "julianday")==0 ){ + if( idx>1 ) return 1; /* IMP: R-31176-64601 */ + if( p->validJD && p->rawS ){ + rc = 0; + p->rawS = 0; + } + } + break; + } +#ifndef SQLITE_OMIT_LOCALTIME + case 'l': { + /* localtime + ** + ** Assuming the current time value is UTC (a.k.a. GMT), shift it to + ** show local time. + */ + if( sqlcipher_sqlite3_stricmp(z, "localtime")==0 && sqlcipher_sqlite3NotPureFunc(pCtx) ){ + rc = toLocaltime(p, pCtx); + } + break; } +#endif + case 'u': { + /* + ** unixepoch + ** + ** Treat the current value of p->s as the number of + ** seconds since 1970. Convert to a real julian day number. + */ + if( sqlcipher_sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){ + if( idx>1 ) return 1; /* IMP: R-49255-55373 */ + r = p->s*1000.0 + 210866760000000.0; + if( r>=0.0 && r<464269060800000.0 ){ + clearYMD_HMS_TZ(p); + p->iJD = (sqlcipher_sqlite3_int64)(r + 0.5); + p->validJD = 1; + p->rawS = 0; + rc = 0; + } + } +#ifndef SQLITE_OMIT_LOCALTIME + else if( sqlcipher_sqlite3_stricmp(z, "utc")==0 && sqlcipher_sqlite3NotPureFunc(pCtx) ){ + if( p->tzSet==0 ){ + i64 iOrigJD; /* Original localtime */ + i64 iGuess; /* Guess at the corresponding utc time */ + int cnt = 0; /* Safety to prevent infinite loop */ + int iErr; /* Guess is off by this much */ - sqlcipher_memset(ctx->buffer, 0, ctx->page_sz); - sqlcipher_memset(hmac_out, 0, ctx->hmac_sz); - if(sqlcipher_sqlite3OsRead(fd, ctx->buffer, read_sz, offset) != SQLITE_OK) { - result = sqlcipher_sqlite3_mprintf("error reading %d bytes from file page %d at offset %d\n", read_sz, page, offset); - sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, result, P4_DYNAMIC); - sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - } else if(sqlcipher_page_hmac(ctx, ctx->read_ctx, page, ctx->buffer, payload_sz, hmac_out) != SQLITE_OK) { - result = sqlcipher_sqlite3_mprintf("HMAC operation failed for page %d", page); - sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, result, P4_DYNAMIC); - sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - } else if(sqlcipher_memcmp(ctx->buffer + payload_sz, hmac_out, ctx->hmac_sz) != 0) { - result = sqlcipher_sqlite3_mprintf("HMAC verification failed for page %d", page); - sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, result, P4_DYNAMIC); - sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); + computeJD(p); + iGuess = iOrigJD = p->iJD; + iErr = 0; + do{ + DateTime new; + memset(&new, 0, sizeof(new)); + iGuess -= iErr; + new.iJD = iGuess; + new.validJD = 1; + rc = toLocaltime(&new, pCtx); + if( rc ) return rc; + computeJD(&new); + iErr = new.iJD - iOrigJD; + }while( iErr && cnt++<3 ); + memset(p, 0, sizeof(*p)); + p->iJD = iGuess; + p->validJD = 1; + p->tzSet = 1; + } + rc = SQLITE_OK; + } +#endif + break; } - } + case 'w': { + /* + ** weekday N + ** + ** Move the date to the same time on the next occurrence of + ** weekday N where 0==Sunday, 1==Monday, and so forth. If the + ** date is already on the appropriate weekday, this is a no-op. + */ + if( sqlcipher_sqlite3_strnicmp(z, "weekday ", 8)==0 + && sqlcipher_sqlite3AtoF(&z[8], &r, sqlcipher_sqlite3Strlen30(&z[8]), SQLITE_UTF8)>0 + && (n=(int)r)==r && n>=0 && r<7 ){ + sqlcipher_sqlite3_int64 Z; + computeYMD_HMS(p); + p->validTZ = 0; + p->validJD = 0; + computeJD(p); + Z = ((p->iJD + 129600000)/86400000) % 7; + if( Z>n ) Z -= 7; + p->iJD += (n - Z)*86400000; + clearYMD_HMS_TZ(p); + rc = 0; + } + break; + } + case 's': { + /* + ** start of TTTTT + ** + ** Move the date backwards to the beginning of the current day, + ** or month or year. + */ + if( sqlcipher_sqlite3_strnicmp(z, "start of ", 9)!=0 ) break; + if( !p->validJD && !p->validYMD && !p->validHMS ) break; + z += 9; + computeYMD(p); + p->validHMS = 1; + p->h = p->m = 0; + p->s = 0.0; + p->rawS = 0; + p->validTZ = 0; + p->validJD = 0; + if( sqlcipher_sqlite3_stricmp(z,"month")==0 ){ + p->D = 1; + rc = 0; + }else if( sqlcipher_sqlite3_stricmp(z,"year")==0 ){ + p->M = 1; + p->D = 1; + rc = 0; + }else if( sqlcipher_sqlite3_stricmp(z,"day")==0 ){ + rc = 0; + } + break; + } + case '+': + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + double rRounder; + int i; + for(n=1; z[n] && z[n]!=':' && !sqlcipher_sqlite3Isspace(z[n]); n++){} + if( sqlcipher_sqlite3AtoF(z, &r, n, SQLITE_UTF8)<=0 ){ + rc = 1; + break; + } + if( z[n]==':' ){ + /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the + ** specified number of hours, minutes, seconds, and fractional seconds + ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be + ** omitted. + */ + const char *z2 = z; + DateTime tx; + sqlcipher_sqlite3_int64 day; + if( !sqlcipher_sqlite3Isdigit(*z2) ) z2++; + memset(&tx, 0, sizeof(tx)); + if( parseHhMmSs(z2, &tx) ) break; + computeJD(&tx); + tx.iJD -= 43200000; + day = tx.iJD/86400000; + tx.iJD -= day*86400000; + if( z[0]=='-' ) tx.iJD = -tx.iJD; + computeJD(p); + clearYMD_HMS_TZ(p); + p->iJD += tx.iJD; + rc = 0; + break; + } - if(file_sz % ctx->page_sz != 0) { - result = sqlcipher_sqlite3_mprintf("page %d has an invalid size of %lld bytes", page, file_sz - ((file_sz / ctx->page_sz) * ctx->page_sz)); - sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, result, P4_DYNAMIC); - sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); + /* If control reaches this point, it means the transformation is + ** one of the forms like "+NNN days". */ + z += n; + while( sqlcipher_sqlite3Isspace(*z) ) z++; + n = sqlcipher_sqlite3Strlen30(z); + if( n>10 || n<3 ) break; + if( sqlcipher_sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--; + computeJD(p); + rc = 1; + rRounder = r<0 ? -0.5 : +0.5; + for(i=0; i-aXformType[i].rLimit && rM += (int)r; + x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; + p->Y += x; + p->M -= x*12; + p->validJD = 0; + r -= (int)r; + break; + } + case 5: { /* Special processing to add years */ + int y = (int)r; + assert( strcmp(aXformType[i].zName,"year")==0 ); + computeYMD_HMS(p); + p->Y += y; + p->validJD = 0; + r -= (int)r; + break; + } + } + computeJD(p); + p->iJD += (sqlcipher_sqlite3_int64)(r*1000.0*aXformType[i].rXform + rRounder); + rc = 0; + break; + } + } + clearYMD_HMS_TZ(p); + break; + } + default: { + break; + } } - -cleanup: - if(hmac_out != NULL) sqlcipher_free(hmac_out, ctx->hmac_sz); - return SQLITE_OK; + return rc; } -int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) { - int i, pass_sz, keyspec_sz, nRes, user_version, rc, oflags; - Db *pDb = 0; - sqlcipher_sqlite3 *db = ctx->pBt->db; - const char *db_filename = sqlcipher_sqlite3_db_filename(db, "main"); - char *set_user_version = NULL, *pass = NULL, *attach_command = NULL, *migrated_db_filename = NULL, *keyspec = NULL, *temp = NULL, *journal_mode = NULL, *set_journal_mode = NULL, *pragma_compat = NULL; - Btree *pDest = NULL, *pSrc = NULL; - sqlcipher_sqlite3_file *srcfile, *destfile; -#if defined(_WIN32) || defined(SQLITE_OS_WINRT) - LPWSTR w_db_filename = NULL, w_migrated_db_filename = NULL; - int w_db_filename_sz = 0, w_migrated_db_filename_sz = 0; -#endif - pass_sz = keyspec_sz = rc = user_version = 0; - - if(!db_filename || sqlcipher_sqlite3Strlen30(db_filename) < 1) - goto cleanup; /* exit immediately if this is an in memory database */ - - /* pull the provided password / key material off the current codec context */ - pass_sz = ctx->read_ctx->pass_sz; - pass = sqlcipher_malloc(pass_sz+1); - memset(pass, 0, pass_sz+1); - memcpy(pass, ctx->read_ctx->pass, pass_sz); - - /* Version 4 - current, no upgrade required, so exit immediately */ - rc = sqlcipher_check_connection(db_filename, pass, pass_sz, "", &user_version, &journal_mode); - if(rc == SQLITE_OK){ - CODEC_TRACE("No upgrade required - exiting\n"); - goto cleanup; +/* +** Process time function arguments. argv[0] is a date-time stamp. +** argv[1] and following are modifiers. Parse them all and write +** the resulting time into the DateTime structure p. Return 0 +** on success and 1 if there are any errors. +** +** If there are zero parameters (if even argv[0] is undefined) +** then assume a default value of "now" for argv[0]. +*/ +static int isDate( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv, + DateTime *p +){ + int i, n; + const unsigned char *z; + int eType; + memset(p, 0, sizeof(*p)); + if( argc==0 ){ + if( !sqlcipher_sqlite3NotPureFunc(context) ) return 1; + return setDateTimeToCurrent(context, p); } - - for(i = 3; i > 0; i--) { - pragma_compat = sqlcipher_sqlite3_mprintf("PRAGMA cipher_compatibility = %d;", i); - rc = sqlcipher_check_connection(db_filename, pass, pass_sz, pragma_compat, &user_version, &journal_mode); - if(rc == SQLITE_OK) { - CODEC_TRACE("Version %d format found\n", i); - goto migrate; + if( (eType = sqlcipher_sqlite3_value_type(argv[0]))==SQLITE_FLOAT + || eType==SQLITE_INTEGER ){ + setRawDateNumber(p, sqlcipher_sqlite3_value_double(argv[0])); + }else{ + z = sqlcipher_sqlite3_value_text(argv[0]); + if( !z || parseDateOrTime(context, (char*)z, p) ){ + return 1; } - if(pragma_compat) sqlcipher_free(pragma_compat, sqlcipher_sqlite3Strlen30(pragma_compat)); - pragma_compat = NULL; } - /* if we exit the loop normally we failed to determine the version, this is an error */ - CODEC_TRACE("Upgrade format not determined\n"); - goto handle_error; - -migrate: - - temp = sqlcipher_sqlite3_mprintf("%s-migrated", db_filename); - /* overallocate migrated_db_filename, because sqlcipher_sqlite3OsOpen will read past the null terminator - * to determine whether the filename was URI formatted */ - migrated_db_filename = sqlcipher_malloc(sqlcipher_sqlite3Strlen30(temp)+2); - memcpy(migrated_db_filename, temp, sqlcipher_sqlite3Strlen30(temp)); - sqlcipher_free(temp, sqlcipher_sqlite3Strlen30(temp)); + for(i=1; iisError || !validJulianDay(p->iJD) ) return 1; + return 0; +} - attach_command = sqlcipher_sqlite3_mprintf("ATTACH DATABASE '%s' as migrate;", migrated_db_filename, pass); - set_user_version = sqlcipher_sqlite3_mprintf("PRAGMA migrate.user_version = %d;", user_version); - rc = sqlcipher_sqlite3_exec(db, pragma_compat, NULL, NULL, NULL); - if(rc != SQLITE_OK){ - CODEC_TRACE("set compatibility mode failed, error code %d\n", rc); - goto handle_error; - } +/* +** The following routines implement the various date and time functions +** of SQLite. +*/ - /* force journal mode to DELETE, we will set it back later if different */ - rc = sqlcipher_sqlite3_exec(db, "PRAGMA journal_mode = delete;", NULL, NULL, NULL); - if(rc != SQLITE_OK){ - CODEC_TRACE("force journal mode DELETE failed, error code %d\n", rc); - goto handle_error; +/* +** julianday( TIMESTRING, MOD, MOD, ...) +** +** Return the julian day number of the date specified in the arguments +*/ +static void juliandayFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + DateTime x; + if( isDate(context, argc, argv, &x)==0 ){ + computeJD(&x); + sqlcipher_sqlite3_result_double(context, x.iJD/86400000.0); } +} - rc = sqlcipher_sqlite3_exec(db, attach_command, NULL, NULL, NULL); - if(rc != SQLITE_OK){ - CODEC_TRACE("attach failed, error code %d\n", rc); - goto handle_error; +/* +** unixepoch( TIMESTRING, MOD, MOD, ...) +** +** Return the number of seconds (including fractional seconds) since +** the unix epoch of 1970-01-01 00:00:00 GMT. +*/ +static void unixepochFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + DateTime x; + if( isDate(context, argc, argv, &x)==0 ){ + computeJD(&x); + sqlcipher_sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); } +} - rc = sqlcipher_sqlite3_key_v2(db, "migrate", pass, pass_sz); - if(rc != SQLITE_OK){ - CODEC_TRACE("keying attached database failed, error code %d\n", rc); - goto handle_error; +/* +** datetime( TIMESTRING, MOD, MOD, ...) +** +** Return YYYY-MM-DD HH:MM:SS +*/ +static void datetimeFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + DateTime x; + if( isDate(context, argc, argv, &x)==0 ){ + int Y, s; + char zBuf[24]; + computeYMD_HMS(&x); + Y = x.Y; + if( Y<0 ) Y = -Y; + zBuf[1] = '0' + (Y/1000)%10; + zBuf[2] = '0' + (Y/100)%10; + zBuf[3] = '0' + (Y/10)%10; + zBuf[4] = '0' + (Y)%10; + zBuf[5] = '-'; + zBuf[6] = '0' + (x.M/10)%10; + zBuf[7] = '0' + (x.M)%10; + zBuf[8] = '-'; + zBuf[9] = '0' + (x.D/10)%10; + zBuf[10] = '0' + (x.D)%10; + zBuf[11] = ' '; + zBuf[12] = '0' + (x.h/10)%10; + zBuf[13] = '0' + (x.h)%10; + zBuf[14] = ':'; + zBuf[15] = '0' + (x.m/10)%10; + zBuf[16] = '0' + (x.m)%10; + zBuf[17] = ':'; + s = (int)x.s; + zBuf[18] = '0' + (s/10)%10; + zBuf[19] = '0' + (s)%10; + zBuf[20] = 0; + if( x.Y<0 ){ + zBuf[0] = '-'; + sqlcipher_sqlite3_result_text(context, zBuf, 20, SQLITE_TRANSIENT); + }else{ + sqlcipher_sqlite3_result_text(context, &zBuf[1], 19, SQLITE_TRANSIENT); + } } +} - rc = sqlcipher_sqlite3_exec(db, "SELECT sqlcipher_export('migrate');", NULL, NULL, NULL); - if(rc != SQLITE_OK){ - CODEC_TRACE("sqlcipher_export failed, error code %d\n", rc); - goto handle_error; +/* +** time( TIMESTRING, MOD, MOD, ...) +** +** Return HH:MM:SS +*/ +static void timeFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + DateTime x; + if( isDate(context, argc, argv, &x)==0 ){ + int s; + char zBuf[16]; + computeHMS(&x); + zBuf[0] = '0' + (x.h/10)%10; + zBuf[1] = '0' + (x.h)%10; + zBuf[2] = ':'; + zBuf[3] = '0' + (x.m/10)%10; + zBuf[4] = '0' + (x.m)%10; + zBuf[5] = ':'; + s = (int)x.s; + zBuf[6] = '0' + (s/10)%10; + zBuf[7] = '0' + (s)%10; + zBuf[8] = 0; + sqlcipher_sqlite3_result_text(context, zBuf, 8, SQLITE_TRANSIENT); } +} - rc = sqlcipher_sqlite3_exec(db, set_user_version, NULL, NULL, NULL); - if(rc != SQLITE_OK){ - CODEC_TRACE("set user version failed, error code %d\n", rc); - goto handle_error; +/* +** date( TIMESTRING, MOD, MOD, ...) +** +** Return YYYY-MM-DD +*/ +static void dateFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + DateTime x; + if( isDate(context, argc, argv, &x)==0 ){ + int Y; + char zBuf[16]; + computeYMD(&x); + Y = x.Y; + if( Y<0 ) Y = -Y; + zBuf[1] = '0' + (Y/1000)%10; + zBuf[2] = '0' + (Y/100)%10; + zBuf[3] = '0' + (Y/10)%10; + zBuf[4] = '0' + (Y)%10; + zBuf[5] = '-'; + zBuf[6] = '0' + (x.M/10)%10; + zBuf[7] = '0' + (x.M)%10; + zBuf[8] = '-'; + zBuf[9] = '0' + (x.D/10)%10; + zBuf[10] = '0' + (x.D)%10; + zBuf[11] = 0; + if( x.Y<0 ){ + zBuf[0] = '-'; + sqlcipher_sqlite3_result_text(context, zBuf, 11, SQLITE_TRANSIENT); + }else{ + sqlcipher_sqlite3_result_text(context, &zBuf[1], 10, SQLITE_TRANSIENT); + } } +} - if( !db->autoCommit ){ - CODEC_TRACE("cannot migrate from within a transaction"); - goto handle_error; - } - if( db->nVdbeActive>1 ){ - CODEC_TRACE("cannot migrate - SQL statements in progress"); - goto handle_error; - } +/* +** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) +** +** Return a string described by FORMAT. Conversions as follows: +** +** %d day of month +** %f ** fractional seconds SS.SSS +** %H hour 00-24 +** %j day of year 000-366 +** %J ** julian day number +** %m month 01-12 +** %M minute 00-59 +** %s seconds since 1970-01-01 +** %S seconds 00-59 +** %w day of week 0-6 sunday==0 +** %W week of year 00-53 +** %Y year 0000-9999 +** %% % +*/ +static void strftimeFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + DateTime x; + size_t i,j; + sqlcipher_sqlite3 *db; + const char *zFmt; + sqlcipher_sqlite3_str sRes; - pDest = db->aDb[0].pBt; - pDb = &(db->aDb[db->nDb-1]); - pSrc = pDb->pBt; - nRes = sqlcipher_sqlite3BtreeGetRequestedReserve(pSrc); - /* unset the BTS_PAGESIZE_FIXED flag to avoid SQLITE_READONLY */ - pDest->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; - rc = sqlcipher_sqlite3BtreeSetPageSize(pDest, default_page_size, nRes, 0); - CODEC_TRACE("set btree page size to %d res %d rc %d\n", default_page_size, nRes, rc); - if( rc!=SQLITE_OK ) goto handle_error; + if( argc==0 ) return; + zFmt = (const char*)sqlcipher_sqlite3_value_text(argv[0]); + if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return; + db = sqlcipher_sqlite3_context_db_handle(context); + sqlcipher_sqlite3StrAccumInit(&sRes, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); - sqlcipher_sqlite3CodecGetKey(db, db->nDb - 1, (void**)&keyspec, &keyspec_sz); - sqlcipher_sqlite3CodecAttach(db, 0, keyspec, keyspec_sz); + computeJD(&x); + computeYMD_HMS(&x); + for(i=j=0; zFmt[i]; i++){ + if( zFmt[i]!='%' ) continue; + if( j59.999 ) s = 59.999; + sqlcipher_sqlite3_str_appendf(&sRes, "%06.3f", s); + break; + } + case 'H': { + sqlcipher_sqlite3_str_appendf(&sRes, "%02d", x.h); + break; + } + case 'W': /* Fall thru */ + case 'j': { + int nDay; /* Number of days since 1st day of year */ + DateTime y = x; + y.validJD = 0; + y.M = 1; + y.D = 1; + computeJD(&y); + nDay = (int)((x.iJD-y.iJD+43200000)/86400000); + if( zFmt[i]=='W' ){ + int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ + wd = (int)(((x.iJD+43200000)/86400000)%7); + sqlcipher_sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7); + }else{ + sqlcipher_sqlite3_str_appendf(&sRes,"%03d",nDay+1); + } + break; + } + case 'J': { + sqlcipher_sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0); + break; + } + case 'm': { + sqlcipher_sqlite3_str_appendf(&sRes,"%02d",x.M); + break; + } + case 'M': { + sqlcipher_sqlite3_str_appendf(&sRes,"%02d",x.m); + break; + } + case 's': { + i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); + sqlcipher_sqlite3_str_appendf(&sRes,"%lld",iS); + break; + } + case 'S': { + sqlcipher_sqlite3_str_appendf(&sRes,"%02d",(int)x.s); + break; + } + case 'w': { + sqlcipher_sqlite3_str_appendchar(&sRes, 1, + (char)(((x.iJD+129600000)/86400000) % 7) + '0'); + break; + } + case 'Y': { + sqlcipher_sqlite3_str_appendf(&sRes,"%04d",x.Y); + break; + } + case '%': { + sqlcipher_sqlite3_str_appendchar(&sRes, 1, '%'); + break; + } + default: { + sqlcipher_sqlite3_str_reset(&sRes); + return; + } + } + } + if( jpBt->pPager); - destfile = sqlcipher_sqlite3PagerFile(pDest->pBt->pPager); +/* +** current_time() +** +** This function returns the same value as time('now'). +*/ +static void ctimeFunc( + sqlcipher_sqlite3_context *context, + int NotUsed, + sqlcipher_sqlite3_value **NotUsed2 +){ + UNUSED_PARAMETER2(NotUsed, NotUsed2); + timeFunc(context, 0, 0); +} - sqlcipher_sqlite3OsClose(srcfile); - sqlcipher_sqlite3OsClose(destfile); +/* +** current_date() +** +** This function returns the same value as date('now'). +*/ +static void cdateFunc( + sqlcipher_sqlite3_context *context, + int NotUsed, + sqlcipher_sqlite3_value **NotUsed2 +){ + UNUSED_PARAMETER2(NotUsed, NotUsed2); + dateFunc(context, 0, 0); +} -#if defined(_WIN32) || defined(SQLITE_OS_WINRT) - CODEC_TRACE("performing windows MoveFileExA\n"); +/* +** current_timestamp() +** +** This function returns the same value as datetime('now'). +*/ +static void ctimestampFunc( + sqlcipher_sqlite3_context *context, + int NotUsed, + sqlcipher_sqlite3_value **NotUsed2 +){ + UNUSED_PARAMETER2(NotUsed, NotUsed2); + datetimeFunc(context, 0, 0); +} +#endif /* !defined(SQLITE_OMIT_DATETIME_FUNCS) */ - w_db_filename_sz = MultiByteToWideChar(CP_UTF8, 0, (LPCCH) db_filename, -1, NULL, 0); - w_db_filename = sqlcipher_malloc(w_db_filename_sz * sizeof(wchar_t)); - w_db_filename_sz = MultiByteToWideChar(CP_UTF8, 0, (LPCCH) db_filename, -1, (const LPWSTR) w_db_filename, w_db_filename_sz); +#ifdef SQLITE_OMIT_DATETIME_FUNCS +/* +** If the library is compiled to omit the full-scale date and time +** handling (to get a smaller binary), the following minimal version +** of the functions current_time(), current_date() and current_timestamp() +** are included instead. This is to support column declarations that +** include "DEFAULT CURRENT_TIME" etc. +** +** This function uses the C-library functions time(), gmtime() +** and strftime(). The format string to pass to strftime() is supplied +** as the user-data for the function. +*/ +static void currentTimeFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + time_t t; + char *zFormat = (char *)sqlcipher_sqlite3_user_data(context); + sqlcipher_sqlite3_int64 iT; + struct tm *pTm; + struct tm sNow; + char zBuf[20]; - w_migrated_db_filename_sz = MultiByteToWideChar(CP_UTF8, 0, (LPCCH) migrated_db_filename, -1, NULL, 0); - w_migrated_db_filename = sqlcipher_malloc(w_migrated_db_filename_sz * sizeof(wchar_t)); - w_migrated_db_filename_sz = MultiByteToWideChar(CP_UTF8, 0, (LPCCH) migrated_db_filename, -1, (const LPWSTR) w_migrated_db_filename, w_migrated_db_filename_sz); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); - if(!MoveFileExW(w_migrated_db_filename, w_db_filename, MOVEFILE_REPLACE_EXISTING)) { - CODEC_TRACE("move error"); - rc = SQLITE_ERROR; - CODEC_TRACE("error occurred while renaming %d\n", rc); - goto handle_error; - } + iT = sqlcipher_sqlite3StmtCurrentTime(context); + if( iT<=0 ) return; + t = iT/1000 - 10000*(sqlcipher_sqlite3_int64)21086676; +#if HAVE_GMTIME_R + pTm = gmtime_r(&t, &sNow); #else - CODEC_TRACE("performing POSIX rename\n"); - if ((rc = rename(migrated_db_filename, db_filename)) != 0) { - CODEC_TRACE("error occurred while renaming %d\n", rc); - goto handle_error; + sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)); + pTm = gmtime(&t); + if( pTm ) memcpy(&sNow, pTm, sizeof(sNow)); + sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)); +#endif + if( pTm ){ + strftime(zBuf, 20, zFormat, &sNow); + sqlcipher_sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); } +} #endif - CODEC_TRACE("renamed migration database %s to main database %s: %d\n", migrated_db_filename, db_filename, rc); - - rc = sqlcipher_sqlite3OsOpen(db->pVfs, migrated_db_filename, srcfile, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MAIN_DB, &oflags); - CODEC_TRACE("reopened migration database: %d\n", rc); - if( rc!=SQLITE_OK ) goto handle_error; - - rc = sqlcipher_sqlite3OsOpen(db->pVfs, db_filename, destfile, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MAIN_DB, &oflags); - CODEC_TRACE("reopened main database: %d\n", rc); - if( rc!=SQLITE_OK ) goto handle_error; - - sqlcipher_sqlite3pager_reset(pDest->pBt->pPager); - CODEC_TRACE("reset pager\n"); - rc = sqlcipher_sqlite3_exec(db, "DETACH DATABASE migrate;", NULL, NULL, NULL); - CODEC_TRACE("DETACH DATABASE called %d\n", rc); - if(rc != SQLITE_OK) goto cleanup; +/* +** This function registered all of the above C functions as SQL +** functions. This should be the only routine in this file with +** external linkage. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3RegisterDateTimeFunctions(void){ + static FuncDef aDateTimeFuncs[] = { +#ifndef SQLITE_OMIT_DATETIME_FUNCS + PURE_DATE(julianday, -1, 0, 0, juliandayFunc ), + PURE_DATE(unixepoch, -1, 0, 0, unixepochFunc ), + PURE_DATE(date, -1, 0, 0, dateFunc ), + PURE_DATE(time, -1, 0, 0, timeFunc ), + PURE_DATE(datetime, -1, 0, 0, datetimeFunc ), + PURE_DATE(strftime, -1, 0, 0, strftimeFunc ), + DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), + DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), + DFUNCTION(current_date, 0, 0, 0, cdateFunc ), +#else + STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc), + STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc), + STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc), +#endif + }; + sqlcipher_sqlite3InsertBuiltinFuncs(aDateTimeFuncs, ArraySize(aDateTimeFuncs)); +} - rc = sqlcipher_sqlite3OsDelete(db->pVfs, migrated_db_filename, 0); - CODEC_TRACE("deleted migration database: %d\n", rc); - if( rc!=SQLITE_OK ) goto handle_error; +/************** End of date.c ************************************************/ +/************** Begin file os.c **********************************************/ +/* +** 2005 November 29 +** +** 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 OS interface code that is common to all +** architectures. +*/ +/* #include "sqliteInt.h" */ - sqlcipher_sqlite3ResetAllSchemasOfConnection(db); - CODEC_TRACE("reset all schemas\n"); +/* +** If we compile with the SQLITE_TEST macro set, then the following block +** of code will give us the ability to simulate a disk I/O error. This +** is used for testing the I/O recovery logic. +*/ +#if defined(SQLITE_TEST) +SQLITE_API int sqlcipher_sqlite3_io_error_hit = 0; /* Total number of I/O Errors */ +SQLITE_API int sqlcipher_sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */ +SQLITE_API int sqlcipher_sqlite3_io_error_pending = 0; /* Count down to first I/O error */ +SQLITE_API int sqlcipher_sqlite3_io_error_persist = 0; /* True if I/O errors persist */ +SQLITE_API int sqlcipher_sqlite3_io_error_benign = 0; /* True if errors are benign */ +SQLITE_API int sqlcipher_sqlite3_diskfull_pending = 0; +SQLITE_API int sqlcipher_sqlite3_diskfull = 0; +#endif /* defined(SQLITE_TEST) */ - set_journal_mode = sqlcipher_sqlite3_mprintf("PRAGMA journal_mode = %s;", journal_mode); - rc = sqlcipher_sqlite3_exec(db, set_journal_mode, NULL, NULL, NULL); - CODEC_TRACE("%s: %d\n", set_journal_mode, rc); - if( rc!=SQLITE_OK ) goto handle_error; +/* +** When testing, also keep a count of the number of open files. +*/ +#if defined(SQLITE_TEST) +SQLITE_API int sqlcipher_sqlite3_open_file_count = 0; +#endif /* defined(SQLITE_TEST) */ - goto cleanup; +/* +** The default SQLite sqlcipher_sqlite3_vfs implementations do not allocate +** memory (actually, os_unix.c allocates a small amount of memory +** from within OsOpen()), but some third-party implementations may. +** So we test the effects of a malloc() failing and the sqlcipher_sqlite3OsXXX() +** function returning SQLITE_IOERR_NOMEM using the DO_OS_MALLOC_TEST macro. +** +** The following functions are instrumented for malloc() failure +** testing: +** +** sqlcipher_sqlite3OsRead() +** sqlcipher_sqlite3OsWrite() +** sqlcipher_sqlite3OsSync() +** sqlcipher_sqlite3OsFileSize() +** sqlcipher_sqlite3OsLock() +** sqlcipher_sqlite3OsCheckReservedLock() +** sqlcipher_sqlite3OsFileControl() +** sqlcipher_sqlite3OsShmMap() +** sqlcipher_sqlite3OsOpen() +** sqlcipher_sqlite3OsDelete() +** sqlcipher_sqlite3OsAccess() +** sqlcipher_sqlite3OsFullPathname() +** +*/ +#if defined(SQLITE_TEST) +SQLITE_API int sqlcipher_sqlite3_memdebug_vfs_oom_test = 1; + #define DO_OS_MALLOC_TEST(x) \ + if (sqlcipher_sqlite3_memdebug_vfs_oom_test && (!x || !sqlcipher_sqlite3JournalIsInMemory(x))) { \ + void *pTstAlloc = sqlcipher_sqlite3Malloc(10); \ + if (!pTstAlloc) return SQLITE_IOERR_NOMEM_BKPT; \ + sqlcipher_sqlite3_free(pTstAlloc); \ + } +#else + #define DO_OS_MALLOC_TEST(x) +#endif -handle_error: - CODEC_TRACE("An error occurred attempting to migrate the database - last error %d\n", rc); - rc = SQLITE_ERROR; +/* +** The following routines are convenience wrappers around methods +** of the sqlcipher_sqlite3_file object. This is mostly just syntactic sugar. All +** of this would be completely automatic if SQLite were coded using +** C++ instead of plain old C. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3OsClose(sqlcipher_sqlite3_file *pId){ + if( pId->pMethods ){ + pId->pMethods->xClose(pId); + pId->pMethods = 0; + } +} +SQLITE_PRIVATE int sqlcipher_sqlite3OsRead(sqlcipher_sqlite3_file *id, void *pBuf, int amt, i64 offset){ + DO_OS_MALLOC_TEST(id); + return id->pMethods->xRead(id, pBuf, amt, offset); +} +SQLITE_PRIVATE int sqlcipher_sqlite3OsWrite(sqlcipher_sqlite3_file *id, const void *pBuf, int amt, i64 offset){ + DO_OS_MALLOC_TEST(id); + return id->pMethods->xWrite(id, pBuf, amt, offset); +} +SQLITE_PRIVATE int sqlcipher_sqlite3OsTruncate(sqlcipher_sqlite3_file *id, i64 size){ + return id->pMethods->xTruncate(id, size); +} +SQLITE_PRIVATE int sqlcipher_sqlite3OsSync(sqlcipher_sqlite3_file *id, int flags){ + DO_OS_MALLOC_TEST(id); + return flags ? id->pMethods->xSync(id, flags) : SQLITE_OK; +} +SQLITE_PRIVATE int sqlcipher_sqlite3OsFileSize(sqlcipher_sqlite3_file *id, i64 *pSize){ + DO_OS_MALLOC_TEST(id); + return id->pMethods->xFileSize(id, pSize); +} +SQLITE_PRIVATE int sqlcipher_sqlite3OsLock(sqlcipher_sqlite3_file *id, int lockType){ + DO_OS_MALLOC_TEST(id); + return id->pMethods->xLock(id, lockType); +} +SQLITE_PRIVATE int sqlcipher_sqlite3OsUnlock(sqlcipher_sqlite3_file *id, int lockType){ + return id->pMethods->xUnlock(id, lockType); +} +SQLITE_PRIVATE int sqlcipher_sqlite3OsCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ + DO_OS_MALLOC_TEST(id); + return id->pMethods->xCheckReservedLock(id, pResOut); +} -cleanup: - if(pass) sqlcipher_free(pass, pass_sz); - if(attach_command) sqlcipher_free(attach_command, sqlcipher_sqlite3Strlen30(attach_command)); - if(migrated_db_filename) sqlcipher_free(migrated_db_filename, sqlcipher_sqlite3Strlen30(migrated_db_filename)); - if(set_user_version) sqlcipher_free(set_user_version, sqlcipher_sqlite3Strlen30(set_user_version)); - if(set_journal_mode) sqlcipher_free(set_journal_mode, sqlcipher_sqlite3Strlen30(set_journal_mode)); - if(journal_mode) sqlcipher_free(journal_mode, sqlcipher_sqlite3Strlen30(journal_mode)); - if(pragma_compat) sqlcipher_free(pragma_compat, sqlcipher_sqlite3Strlen30(pragma_compat)); -#if defined(_WIN32) || defined(SQLITE_OS_WINRT) - if(w_db_filename) sqlcipher_free(w_db_filename, w_db_filename_sz); - if(w_migrated_db_filename) sqlcipher_free(w_migrated_db_filename, w_migrated_db_filename_sz); +/* +** Use sqlcipher_sqlite3OsFileControl() when we are doing something that might fail +** and we need to know about the failures. Use sqlcipher_sqlite3OsFileControlHint() +** when simply tossing information over the wall to the VFS and we do not +** really care if the VFS receives and understands the information since it +** is only a hint and can be safely ignored. The sqlcipher_sqlite3OsFileControlHint() +** routine has no return value since the return value would be meaningless. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3OsFileControl(sqlcipher_sqlite3_file *id, int op, void *pArg){ + if( id->pMethods==0 ) return SQLITE_NOTFOUND; +#ifdef SQLITE_TEST + if( op!=SQLITE_FCNTL_COMMIT_PHASETWO + && op!=SQLITE_FCNTL_LOCK_TIMEOUT + && op!=SQLITE_FCNTL_CKPT_DONE + && op!=SQLITE_FCNTL_CKPT_START + ){ + /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite + ** is using a regular VFS, it is called after the corresponding + ** transaction has been committed. Injecting a fault at this point + ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM + ** but the transaction is committed anyway. + ** + ** The core must call OsFileControl() though, not OsFileControlHint(), + ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably + ** means the commit really has failed and an error should be returned + ** to the user. + ** + ** The CKPT_DONE and CKPT_START file-controls are write-only signals + ** to the cksumvfs. Their return code is meaningless and is ignored + ** by the SQLite core, so there is no point in simulating OOMs for them. + */ + DO_OS_MALLOC_TEST(id); + } #endif - return rc; + return id->pMethods->xFileControl(id, op, pArg); } - -int sqlcipher_codec_add_random(codec_ctx *ctx, const char *zRight, int random_sz){ - const char *suffix = &zRight[random_sz-1]; - int n = random_sz - 3; /* adjust for leading x' and tailing ' */ - if (n > 0 && - sqlcipher_sqlite3StrNICmp((const char *)zRight ,"x'", 2) == 0 && - sqlcipher_sqlite3StrNICmp(suffix, "'", 1) == 0 && - n % 2 == 0) { - int rc = 0; - int buffer_sz = n / 2; - unsigned char *random; - const unsigned char *z = (const unsigned char *)zRight + 2; /* adjust lead offset of x' */ - CODEC_TRACE("sqlcipher_codec_add_random: using raw random blob from hex\n"); - random = sqlcipher_malloc(buffer_sz); - memset(random, 0, buffer_sz); - cipher_hex2bin(z, n, random); - rc = ctx->provider->add_random(ctx->provider_ctx, random, buffer_sz); - sqlcipher_free(random, buffer_sz); - return rc; - } - return SQLITE_ERROR; +SQLITE_PRIVATE void sqlcipher_sqlite3OsFileControlHint(sqlcipher_sqlite3_file *id, int op, void *pArg){ + if( id->pMethods ) (void)id->pMethods->xFileControl(id, op, pArg); } -#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_DEPRECATED) -static void sqlcipher_profile_callback(void *file, const char *sql, sqlcipher_sqlite3_uint64 run_time){ - FILE *f = (FILE*)file; - double elapsed = run_time/1000000.0; - if(f) fprintf(f, "Elapsed time:%.3f ms - %s\n", elapsed, sql); +SQLITE_PRIVATE int sqlcipher_sqlite3OsSectorSize(sqlcipher_sqlite3_file *id){ + int (*xSectorSize)(sqlcipher_sqlite3_file*) = id->pMethods->xSectorSize; + return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE); } -#endif +SQLITE_PRIVATE int sqlcipher_sqlite3OsDeviceCharacteristics(sqlcipher_sqlite3_file *id){ + if( NEVER(id->pMethods==0) ) return 0; + return id->pMethods->xDeviceCharacteristics(id); +} +#ifndef SQLITE_OMIT_WAL +SQLITE_PRIVATE int sqlcipher_sqlite3OsShmLock(sqlcipher_sqlite3_file *id, int offset, int n, int flags){ + return id->pMethods->xShmLock(id, offset, n, flags); +} +SQLITE_PRIVATE void sqlcipher_sqlite3OsShmBarrier(sqlcipher_sqlite3_file *id){ + id->pMethods->xShmBarrier(id); +} +SQLITE_PRIVATE int sqlcipher_sqlite3OsShmUnmap(sqlcipher_sqlite3_file *id, int deleteFlag){ + return id->pMethods->xShmUnmap(id, deleteFlag); +} +SQLITE_PRIVATE int sqlcipher_sqlite3OsShmMap( + sqlcipher_sqlite3_file *id, /* Database file handle */ + int iPage, + int pgsz, + int bExtend, /* True to extend file if necessary */ + void volatile **pp /* OUT: Pointer to mapping */ +){ + DO_OS_MALLOC_TEST(id); + return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp); +} +#endif /* SQLITE_OMIT_WAL */ -int sqlcipher_cipher_profile(sqlcipher_sqlite3 *db, const char *destination){ -#if defined(SQLITE_OMIT_TRACE) || defined(SQLITE_OMIT_DEPRECATED) - return SQLITE_ERROR; -#else - FILE *f; - if(sqlcipher_sqlite3StrICmp(destination, "stdout") == 0){ - f = stdout; - }else if(sqlcipher_sqlite3StrICmp(destination, "stderr") == 0){ - f = stderr; - }else if(sqlcipher_sqlite3StrICmp(destination, "off") == 0){ - f = 0; - }else{ -#if !defined(SQLCIPHER_PROFILE_USE_FOPEN) && (defined(_WIN32) && (__STDC_VERSION__ > 199901L) || defined(SQLITE_OS_WINRT)) - if(fopen_s(&f, destination, "a") != 0) return SQLITE_ERROR; +#if SQLITE_MAX_MMAP_SIZE>0 +/* The real implementation of xFetch and xUnfetch */ +SQLITE_PRIVATE int sqlcipher_sqlite3OsFetch(sqlcipher_sqlite3_file *id, i64 iOff, int iAmt, void **pp){ + DO_OS_MALLOC_TEST(id); + return id->pMethods->xFetch(id, iOff, iAmt, pp); +} +SQLITE_PRIVATE int sqlcipher_sqlite3OsUnfetch(sqlcipher_sqlite3_file *id, i64 iOff, void *p){ + return id->pMethods->xUnfetch(id, iOff, p); +} #else - if((f = fopen(destination, "a")) == 0) return SQLITE_ERROR; -#endif - } - sqlcipher_sqlite3_profile(db, sqlcipher_profile_callback, f); +/* No-op stubs to use when memory-mapped I/O is disabled */ +SQLITE_PRIVATE int sqlcipher_sqlite3OsFetch(sqlcipher_sqlite3_file *id, i64 iOff, int iAmt, void **pp){ + *pp = 0; return SQLITE_OK; -#endif -} - -int sqlcipher_codec_fips_status(codec_ctx *ctx) { - return ctx->provider->fips_status(ctx->provider_ctx); } - -const char* sqlcipher_codec_get_provider_version(codec_ctx *ctx) { - return ctx->provider->get_provider_version(ctx->provider_ctx); +SQLITE_PRIVATE int sqlcipher_sqlite3OsUnfetch(sqlcipher_sqlite3_file *id, i64 iOff, void *p){ + return SQLITE_OK; } - #endif -/* END SQLCIPHER */ -/************** End of crypto_impl.c *****************************************/ -/************** Begin file crypto_libtomcrypt.c ******************************/ /* -** SQLCipher -** http://sqlcipher.net -** -** Copyright (c) 2008 - 2013, ZETETIC LLC -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of the ZETETIC LLC nor the -** names of its contributors may be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY -** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY -** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** +** The next group of routines are convenience wrappers around the +** VFS methods. */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC -#ifdef SQLCIPHER_CRYPTO_LIBTOMCRYPT -/* #include "sqliteInt.h" */ -/* #include "sqlcipher.h" */ -#include - -#define FORTUNA_MAX_SZ 32 -static prng_state prng; -static volatile unsigned int ltc_init = 0; -static volatile unsigned int ltc_ref_count = 0; - -#define LTC_CIPHER "rijndael" - -static int sqlcipher_ltc_add_random(void *ctx, void *buffer, int length) { - int rc = 0; - int data_to_read = length; - int block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ; - const unsigned char * data = (const unsigned char *)buffer; - - CODEC_TRACE_MUTEX("sqlcipher_ltc_add_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); - CODEC_TRACE_MUTEX("sqlcipher_ltc_add_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - - while(data_to_read > 0){ - rc = fortuna_add_entropy(data, block_sz, &prng); - rc = rc != CRYPT_OK ? SQLITE_ERROR : SQLITE_OK; - if(rc != SQLITE_OK){ - break; - } - data_to_read -= block_sz; - data += block_sz; - block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ; - } - fortuna_ready(&prng); - - CODEC_TRACE_MUTEX("sqlcipher_ltc_add_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); - CODEC_TRACE_MUTEX("sqlcipher_ltc_add_random: left SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - +SQLITE_PRIVATE int sqlcipher_sqlite3OsOpen( + sqlcipher_sqlite3_vfs *pVfs, + const char *zPath, + sqlcipher_sqlite3_file *pFile, + int flags, + int *pFlagsOut +){ + int rc; + DO_OS_MALLOC_TEST(0); + /* 0x87f7f is a mask of SQLITE_OPEN_ flags that are valid to be passed + ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, + ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before + ** reaching the VFS. */ + rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x1087f7f, pFlagsOut); + assert( rc==SQLITE_OK || pFile->pMethods==0 ); return rc; } - -static int sqlcipher_ltc_activate(void *ctx) { - unsigned char random_buffer[FORTUNA_MAX_SZ]; - - CODEC_TRACE_MUTEX("sqlcipher_ltc_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_ltc_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - - sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ); - if(ltc_init == 0) { - if(register_prng(&fortuna_desc) < 0) return SQLITE_ERROR; - if(register_cipher(&rijndael_desc) < 0) return SQLITE_ERROR; - if(register_hash(&sha512_desc) < 0) return SQLITE_ERROR; - if(register_hash(&sha256_desc) < 0) return SQLITE_ERROR; - if(register_hash(&sha1_desc) < 0) return SQLITE_ERROR; - if(fortuna_start(&prng) != CRYPT_OK) { - return SQLITE_ERROR; - } - - ltc_init = 1; - } - ltc_ref_count++; - -#ifndef SQLCIPHER_TEST - sqlcipher_sqlite3_randomness(FORTUNA_MAX_SZ, random_buffer); -#endif - - if(sqlcipher_ltc_add_random(ctx, random_buffer, FORTUNA_MAX_SZ) != SQLITE_OK) { - return SQLITE_ERROR; - } - sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ); - - CODEC_TRACE_MUTEX("sqlcipher_ltc_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_ltc_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - - return SQLITE_OK; -} - -static int sqlcipher_ltc_deactivate(void *ctx) { - CODEC_TRACE_MUTEX("sqlcipher_ltc_deactivate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_ltc_deactivate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - - ltc_ref_count--; - if(ltc_ref_count == 0){ - fortuna_done(&prng); - sqlcipher_memset((void *)&prng, 0, sizeof(prng)); - } - - CODEC_TRACE_MUTEX("sqlcipher_ltc_deactivate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_ltc_deactivate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - - return SQLITE_OK; -} - -static const char* sqlcipher_ltc_get_provider_name(void *ctx) { - return "libtomcrypt"; +SQLITE_PRIVATE int sqlcipher_sqlite3OsDelete(sqlcipher_sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + DO_OS_MALLOC_TEST(0); + assert( dirSync==0 || dirSync==1 ); + return pVfs->xDelete!=0 ? pVfs->xDelete(pVfs, zPath, dirSync) : SQLITE_OK; } - -static const char* sqlcipher_ltc_get_provider_version(void *ctx) { - return SCRYPT; +SQLITE_PRIVATE int sqlcipher_sqlite3OsAccess( + sqlcipher_sqlite3_vfs *pVfs, + const char *zPath, + int flags, + int *pResOut +){ + DO_OS_MALLOC_TEST(0); + return pVfs->xAccess(pVfs, zPath, flags, pResOut); } - -static int sqlcipher_ltc_random(void *ctx, void *buffer, int length) { - CODEC_TRACE_MUTEX("sqlcipher_ltc_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); - CODEC_TRACE_MUTEX("sqlcipher_ltc_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - - fortuna_read(buffer, length, &prng); - - CODEC_TRACE_MUTEX("sqlcipher_ltc_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); - CODEC_TRACE_MUTEX("sqlcipher_ltc_random: left SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - - return SQLITE_OK; +SQLITE_PRIVATE int sqlcipher_sqlite3OsFullPathname( + sqlcipher_sqlite3_vfs *pVfs, + const char *zPath, + int nPathOut, + char *zPathOut +){ + DO_OS_MALLOC_TEST(0); + zPathOut[0] = 0; + return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut); } - -static int sqlcipher_ltc_hmac(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out) { - int rc, hash_idx; - hmac_state hmac; - unsigned long outlen; - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - hash_idx = find_hash("sha1"); - break; - case SQLCIPHER_HMAC_SHA256: - hash_idx = find_hash("sha256"); - break; - case SQLCIPHER_HMAC_SHA512: - hash_idx = find_hash("sha512"); - break; - default: - return SQLITE_ERROR; - } - - if(hash_idx < 0) return SQLITE_ERROR; - outlen = hash_descriptor[hash_idx].hashsize; - - if(in == NULL) return SQLITE_ERROR; - if((rc = hmac_init(&hmac, hash_idx, hmac_key, key_sz)) != CRYPT_OK) return SQLITE_ERROR; - if((rc = hmac_process(&hmac, in, in_sz)) != CRYPT_OK) return SQLITE_ERROR; - if(in2 != NULL && (rc = hmac_process(&hmac, in2, in2_sz)) != CRYPT_OK) return SQLITE_ERROR; - if((rc = hmac_done(&hmac, out, &outlen)) != CRYPT_OK) return SQLITE_ERROR; - return SQLITE_OK; +#ifndef SQLITE_OMIT_LOAD_EXTENSION +SQLITE_PRIVATE void *sqlcipher_sqlite3OsDlOpen(sqlcipher_sqlite3_vfs *pVfs, const char *zPath){ + assert( zPath!=0 ); + assert( strlen(zPath)<=SQLITE_MAX_PATHLEN ); /* tag-20210611-1 */ + return pVfs->xDlOpen(pVfs, zPath); } - -static int sqlcipher_ltc_kdf(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) { - int rc, hash_idx; - unsigned long outlen = key_sz; - - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - hash_idx = find_hash("sha1"); - break; - case SQLCIPHER_HMAC_SHA256: - hash_idx = find_hash("sha256"); - break; - case SQLCIPHER_HMAC_SHA512: - hash_idx = find_hash("sha512"); - break; - default: - return SQLITE_ERROR; - } - if(hash_idx < 0) return SQLITE_ERROR; - - if((rc = pkcs_5_alg2(pass, pass_sz, salt, salt_sz, - workfactor, hash_idx, key, &outlen)) != CRYPT_OK) { - return SQLITE_ERROR; - } - return SQLITE_OK; +SQLITE_PRIVATE void sqlcipher_sqlite3OsDlError(sqlcipher_sqlite3_vfs *pVfs, int nByte, char *zBufOut){ + pVfs->xDlError(pVfs, nByte, zBufOut); } - -static const char* sqlcipher_ltc_get_cipher(void *ctx) { - return "aes-256-cbc"; +SQLITE_PRIVATE void (*sqlcipher_sqlite3OsDlSym(sqlcipher_sqlite3_vfs *pVfs, void *pHdle, const char *zSym))(void){ + return pVfs->xDlSym(pVfs, pHdle, zSym); } - -static int sqlcipher_ltc_cipher(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out) { - int rc, cipher_idx; - symmetric_CBC cbc; - - if((cipher_idx = find_cipher(LTC_CIPHER)) == -1) return SQLITE_ERROR; - if((rc = cbc_start(cipher_idx, iv, key, key_sz, 0, &cbc)) != CRYPT_OK) return SQLITE_ERROR; - rc = mode == 1 ? cbc_encrypt(in, out, in_sz, &cbc) : cbc_decrypt(in, out, in_sz, &cbc); - if(rc != CRYPT_OK) return SQLITE_ERROR; - cbc_done(&cbc); - return SQLITE_OK; +SQLITE_PRIVATE void sqlcipher_sqlite3OsDlClose(sqlcipher_sqlite3_vfs *pVfs, void *pHandle){ + pVfs->xDlClose(pVfs, pHandle); } +#endif /* SQLITE_OMIT_LOAD_EXTENSION */ +SQLITE_PRIVATE int sqlcipher_sqlite3OsRandomness(sqlcipher_sqlite3_vfs *pVfs, int nByte, char *zBufOut){ + if( sqlcipher_sqlite3Config.iPrngSeed ){ + memset(zBufOut, 0, nByte); + if( ALWAYS(nByte>(signed)sizeof(unsigned)) ) nByte = sizeof(unsigned int); + memcpy(zBufOut, &sqlcipher_sqlite3Config.iPrngSeed, nByte); + return SQLITE_OK; + }else{ + return pVfs->xRandomness(pVfs, nByte, zBufOut); + } -static int sqlcipher_ltc_get_key_sz(void *ctx) { - int cipher_idx = find_cipher(LTC_CIPHER); - return cipher_descriptor[cipher_idx].max_key_length; } - -static int sqlcipher_ltc_get_iv_sz(void *ctx) { - int cipher_idx = find_cipher(LTC_CIPHER); - return cipher_descriptor[cipher_idx].block_length; +SQLITE_PRIVATE int sqlcipher_sqlite3OsSleep(sqlcipher_sqlite3_vfs *pVfs, int nMicro){ + return pVfs->xSleep(pVfs, nMicro); } - -static int sqlcipher_ltc_get_block_sz(void *ctx) { - int cipher_idx = find_cipher(LTC_CIPHER); - return cipher_descriptor[cipher_idx].block_length; +SQLITE_PRIVATE int sqlcipher_sqlite3OsGetLastError(sqlcipher_sqlite3_vfs *pVfs){ + return pVfs->xGetLastError ? pVfs->xGetLastError(pVfs, 0, 0) : 0; } - -static int sqlcipher_ltc_get_hmac_sz(void *ctx, int algorithm) { - int hash_idx; - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - hash_idx = find_hash("sha1"); - break; - case SQLCIPHER_HMAC_SHA256: - hash_idx = find_hash("sha256"); - break; - case SQLCIPHER_HMAC_SHA512: - hash_idx = find_hash("sha512"); - break; - default: - return 0; +SQLITE_PRIVATE int sqlcipher_sqlite3OsCurrentTimeInt64(sqlcipher_sqlite3_vfs *pVfs, sqlcipher_sqlite3_int64 *pTimeOut){ + int rc; + /* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64() + ** method to get the current date and time if that method is available + ** (if iVersion is 2 or greater and the function pointer is not NULL) and + ** will fall back to xCurrentTime() if xCurrentTimeInt64() is + ** unavailable. + */ + if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){ + rc = pVfs->xCurrentTimeInt64(pVfs, pTimeOut); + }else{ + double r; + rc = pVfs->xCurrentTime(pVfs, &r); + *pTimeOut = (sqlcipher_sqlite3_int64)(r*86400000.0); } - - if(hash_idx < 0) return 0; - - return hash_descriptor[hash_idx].hashsize; + return rc; } -static int sqlcipher_ltc_ctx_init(void **ctx) { - sqlcipher_ltc_activate(NULL); - return SQLITE_OK; +SQLITE_PRIVATE int sqlcipher_sqlite3OsOpenMalloc( + sqlcipher_sqlite3_vfs *pVfs, + const char *zFile, + sqlcipher_sqlite3_file **ppFile, + int flags, + int *pOutFlags +){ + int rc; + sqlcipher_sqlite3_file *pFile; + pFile = (sqlcipher_sqlite3_file *)sqlcipher_sqlite3MallocZero(pVfs->szOsFile); + if( pFile ){ + rc = sqlcipher_sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags); + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3_free(pFile); + *ppFile = 0; + }else{ + *ppFile = pFile; + } + }else{ + *ppFile = 0; + rc = SQLITE_NOMEM_BKPT; + } + assert( *ppFile!=0 || rc!=SQLITE_OK ); + return rc; } - -static int sqlcipher_ltc_ctx_free(void **ctx) { - sqlcipher_ltc_deactivate(&ctx); - return SQLITE_OK; +SQLITE_PRIVATE void sqlcipher_sqlite3OsCloseFree(sqlcipher_sqlite3_file *pFile){ + assert( pFile ); + sqlcipher_sqlite3OsClose(pFile); + sqlcipher_sqlite3_free(pFile); } -static int sqlcipher_ltc_fips_status(void *ctx) { - return 0; +/* +** This function is a wrapper around the OS specific implementation of +** sqlcipher_sqlite3_os_init(). The purpose of the wrapper is to provide the +** ability to simulate a malloc failure, so that the handling of an +** error in sqlcipher_sqlite3_os_init() by the upper layers can be tested. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3OsInit(void){ + void *p = sqlcipher_sqlite3_malloc(10); + if( p==0 ) return SQLITE_NOMEM_BKPT; + sqlcipher_sqlite3_free(p); + return sqlcipher_sqlite3_os_init(); } -int sqlcipher_ltc_setup(sqlcipher_provider *p) { - p->activate = sqlcipher_ltc_activate; - p->deactivate = sqlcipher_ltc_deactivate; - p->get_provider_name = sqlcipher_ltc_get_provider_name; - p->random = sqlcipher_ltc_random; - p->hmac = sqlcipher_ltc_hmac; - p->kdf = sqlcipher_ltc_kdf; - p->cipher = sqlcipher_ltc_cipher; - p->get_cipher = sqlcipher_ltc_get_cipher; - p->get_key_sz = sqlcipher_ltc_get_key_sz; - p->get_iv_sz = sqlcipher_ltc_get_iv_sz; - p->get_block_sz = sqlcipher_ltc_get_block_sz; - p->get_hmac_sz = sqlcipher_ltc_get_hmac_sz; - p->ctx_init = sqlcipher_ltc_ctx_init; - p->ctx_free = sqlcipher_ltc_ctx_free; - p->add_random = sqlcipher_ltc_add_random; - p->fips_status = sqlcipher_ltc_fips_status; - p->get_provider_version = sqlcipher_ltc_get_provider_version; - return SQLITE_OK; -} +/* +** The list of all registered VFS implementations. +*/ +static sqlcipher_sqlite3_vfs * SQLITE_WSD vfsList = 0; +#define vfsList GLOBAL(sqlcipher_sqlite3_vfs *, vfsList) +/* +** Locate a VFS by name. If no name is given, simply return the +** first VFS on the list. +*/ +SQLITE_API sqlcipher_sqlite3_vfs *sqlcipher_sqlite3_vfs_find(const char *zVfs){ + sqlcipher_sqlite3_vfs *pVfs = 0; +#if SQLITE_THREADSAFE + sqlcipher_sqlite3_mutex *mutex; #endif +#ifndef SQLITE_OMIT_AUTOINIT + int rc = sqlcipher_sqlite3_initialize(); + if( rc ) return 0; #endif -/* END SQLCIPHER */ +#if SQLITE_THREADSAFE + mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); +#endif + sqlcipher_sqlite3_mutex_enter(mutex); + for(pVfs = vfsList; pVfs; pVfs=pVfs->pNext){ + if( zVfs==0 ) break; + if( strcmp(zVfs, pVfs->zName)==0 ) break; + } + sqlcipher_sqlite3_mutex_leave(mutex); + return pVfs; +} -/************** End of crypto_libtomcrypt.c **********************************/ -/************** Begin file crypto_nss.c **************************************/ /* -** SQLCipher -** http://sqlcipher.net -** -** Copyright (c) 2008 - 2013, ZETETIC LLC -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of the ZETETIC LLC nor the -** names of its contributors may be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY -** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY -** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** +** Unlink a VFS from the linked list */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC -#ifdef SQLCIPHER_CRYPTO_NSS -/* #include "crypto.h" */ -/* #include "sqlcipher.h" */ -#include -#include -#include - -static NSSInitContext* nss_init_context = NULL; -static unsigned int nss_init_count = 0; - -int sqlcipher_nss_setup(sqlcipher_provider *p); - -static int sqlcipher_nss_activate(void *ctx) { - - CODEC_TRACE_MUTEX("sqlcipher_nss_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_nss_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - if (nss_init_context == NULL) { - nss_init_context = NSS_InitContext("", "", "", "", NULL, - NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | - NSS_INIT_FORCEOPEN | NSS_INIT_OPTIMIZESPACE | NSS_INIT_NOROOTINIT); +static void vfsUnlink(sqlcipher_sqlite3_vfs *pVfs){ + assert( sqlcipher_sqlite3_mutex_held(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)) ); + if( pVfs==0 ){ + /* No-op */ + }else if( vfsList==pVfs ){ + vfsList = pVfs->pNext; + }else if( vfsList ){ + sqlcipher_sqlite3_vfs *p = vfsList; + while( p->pNext && p->pNext!=pVfs ){ + p = p->pNext; + } + if( p->pNext==pVfs ){ + p->pNext = pVfs->pNext; + } } - nss_init_count++; - CODEC_TRACE_MUTEX("sqlcipher_nss_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_nss_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - return SQLITE_OK; } -static int sqlcipher_nss_deactivate(void *ctx) { - CODEC_TRACE_MUTEX("sqlcipher_nss_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_nss_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); +/* +** Register a VFS with the system. It is harmless to register the same +** VFS multiple times. The new VFS becomes the default if makeDflt is +** true. +*/ +SQLITE_API int sqlcipher_sqlite3_vfs_register(sqlcipher_sqlite3_vfs *pVfs, int makeDflt){ + MUTEX_LOGIC(sqlcipher_sqlite3_mutex *mutex;) +#ifndef SQLITE_OMIT_AUTOINIT + int rc = sqlcipher_sqlite3_initialize(); + if( rc ) return rc; +#endif +#ifdef SQLITE_ENABLE_API_ARMOR + if( pVfs==0 ) return SQLITE_MISUSE_BKPT; +#endif - nss_init_count--; - if (nss_init_count == 0 && nss_init_context != NULL) { - NSS_ShutdownContext(nss_init_context); - nss_init_context = NULL; + MUTEX_LOGIC( mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) + sqlcipher_sqlite3_mutex_enter(mutex); + vfsUnlink(pVfs); + if( makeDflt || vfsList==0 ){ + pVfs->pNext = vfsList; + vfsList = pVfs; + }else{ + pVfs->pNext = vfsList->pNext; + vfsList->pNext = pVfs; } - - CODEC_TRACE_MUTEX("sqlcipher_nss_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_nss_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); + assert(vfsList); + sqlcipher_sqlite3_mutex_leave(mutex); return SQLITE_OK; } -static int sqlcipher_nss_add_random(void *ctx, void *buffer, int length) { +/* +** Unregister a VFS so that it is no longer accessible. +*/ +SQLITE_API int sqlcipher_sqlite3_vfs_unregister(sqlcipher_sqlite3_vfs *pVfs){ + MUTEX_LOGIC(sqlcipher_sqlite3_mutex *mutex;) +#ifndef SQLITE_OMIT_AUTOINIT + int rc = sqlcipher_sqlite3_initialize(); + if( rc ) return rc; +#endif + MUTEX_LOGIC( mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) + sqlcipher_sqlite3_mutex_enter(mutex); + vfsUnlink(pVfs); + sqlcipher_sqlite3_mutex_leave(mutex); return SQLITE_OK; } -/* generate a defined number of random bytes */ -static int sqlcipher_nss_random (void *ctx, void *buffer, int length) { - // PK11_GenerateRandom should be thread-safe. - return (PK11_GenerateRandom((unsigned char *)buffer, length) == SECSuccess) ? SQLITE_OK : SQLITE_ERROR; -} +/************** End of os.c **************************************************/ +/************** Begin file fault.c *******************************************/ +/* +** 2008 Jan 22 +** +** 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 support the concept of "benign" +** malloc failures (when the xMalloc() or xRealloc() method of the +** sqlcipher_sqlite3_mem_methods structure fails to allocate a block of memory +** and returns 0). +** +** Most malloc failures are non-benign. After they occur, SQLite +** abandons the current operation and returns an error code (usually +** SQLITE_NOMEM) to the user. However, sometimes a fault is not necessarily +** fatal. For example, if a malloc fails while resizing a hash table, this +** is completely recoverable simply by not carrying out the resize. The +** hash table will continue to function normally. So a malloc failure +** during a hash table resize is a benign fault. +*/ -static const char* sqlcipher_nss_get_provider_name(void *ctx) { - return "nss"; -} +/* #include "sqliteInt.h" */ -static const char* sqlcipher_nss_get_provider_version(void *ctx) { - return NSS_GetVersion(); -} +#ifndef SQLITE_UNTESTABLE -static const char* sqlcipher_nss_get_cipher(void *ctx) { - return "aes-256-cbc"; -} +/* +** Global variables. +*/ +typedef struct BenignMallocHooks BenignMallocHooks; +static SQLITE_WSD struct BenignMallocHooks { + void (*xBenignBegin)(void); + void (*xBenignEnd)(void); +} sqlcipher_sqlite3Hooks = { 0, 0 }; -static int sqlcipher_nss_get_key_sz(void *ctx) { - return AES_256_KEY_LENGTH; -} +/* The "wsdHooks" macro will resolve to the appropriate BenignMallocHooks +** structure. If writable static data is unsupported on the target, +** we have to locate the state vector at run-time. In the more common +** case where writable static data is supported, wsdHooks can refer directly +** to the "sqlcipher_sqlite3Hooks" state vector declared above. +*/ +#ifdef SQLITE_OMIT_WSD +# define wsdHooksInit \ + BenignMallocHooks *x = &GLOBAL(BenignMallocHooks,sqlcipher_sqlite3Hooks) +# define wsdHooks x[0] +#else +# define wsdHooksInit +# define wsdHooks sqlcipher_sqlite3Hooks +#endif -static int sqlcipher_nss_get_iv_sz(void *ctx) { - return AES_BLOCK_SIZE; -} -static int sqlcipher_nss_get_block_sz(void *ctx) { - return AES_BLOCK_SIZE; +/* +** Register hooks to call when sqlcipher_sqlite3BeginBenignMalloc() and +** sqlcipher_sqlite3EndBenignMalloc() are called, respectively. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3BenignMallocHooks( + void (*xBenignBegin)(void), + void (*xBenignEnd)(void) +){ + wsdHooksInit; + wsdHooks.xBenignBegin = xBenignBegin; + wsdHooks.xBenignEnd = xBenignEnd; } -static int sqlcipher_nss_get_hmac_sz(void *ctx, int algorithm) { - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - return SHA1_LENGTH; - break; - case SQLCIPHER_HMAC_SHA256: - return SHA256_LENGTH; - break; - case SQLCIPHER_HMAC_SHA512: - return SHA512_LENGTH; - break; - default: - return 0; +/* +** This (sqlcipher_sqlite3EndBenignMalloc()) is called by SQLite code to indicate that +** subsequent malloc failures are benign. A call to sqlcipher_sqlite3EndBenignMalloc() +** indicates that subsequent malloc failures are non-benign. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3BeginBenignMalloc(void){ + wsdHooksInit; + if( wsdHooks.xBenignBegin ){ + wsdHooks.xBenignBegin(); } } - -static int sqlcipher_nss_hmac(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out) { - int rc = SQLITE_OK; - unsigned int length; - unsigned int outLen; - PK11Context* context = NULL; - PK11SlotInfo * slot = NULL; - PK11SymKey* symKey = NULL; - if(in == NULL) goto error; - CK_MECHANISM_TYPE mech; - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - mech = CKM_SHA_1_HMAC; - break; - case SQLCIPHER_HMAC_SHA256: - mech = CKM_SHA256_HMAC; - break; - case SQLCIPHER_HMAC_SHA512: - mech = CKM_SHA512_HMAC; - break; - default: - goto error; - } - length = sqlcipher_nss_get_hmac_sz(ctx, algorithm); - slot = PK11_GetInternalSlot(); - if (slot == NULL) goto error; - SECItem keyItem; - keyItem.data = hmac_key; - keyItem.len = key_sz; - symKey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, - CKA_SIGN, &keyItem, NULL); - if (symKey == NULL) goto error; - SECItem noParams; - noParams.data = 0; - noParams.len = 0; - context = PK11_CreateContextBySymKey(mech, CKA_SIGN, symKey, &noParams); - if (context == NULL) goto error; - if (PK11_DigestBegin(context) != SECSuccess) goto error; - if (PK11_DigestOp(context, in, in_sz) != SECSuccess) goto error; - if (in2 != NULL) { - if (PK11_DigestOp(context, in2, in2_sz) != SECSuccess) goto error; +SQLITE_PRIVATE void sqlcipher_sqlite3EndBenignMalloc(void){ + wsdHooksInit; + if( wsdHooks.xBenignEnd ){ + wsdHooks.xBenignEnd(); } - if (PK11_DigestFinal(context, out, &outLen, length) != SECSuccess) goto error; - - goto cleanup; - error: - rc = SQLITE_ERROR; - cleanup: - if (context) PK11_DestroyContext(context, PR_TRUE); - if (symKey) PK11_FreeSymKey(symKey); - if (slot) PK11_FreeSlot(slot); - return rc; } -static int sqlcipher_nss_kdf(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) { - int rc = SQLITE_OK; - PK11SlotInfo * slot = NULL; - SECAlgorithmID * algid = NULL; - PK11SymKey* symKey = NULL; - SECOidTag oidtag; - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - oidtag = SEC_OID_HMAC_SHA1; - break; - case SQLCIPHER_HMAC_SHA256: - oidtag = SEC_OID_HMAC_SHA256; - break; - case SQLCIPHER_HMAC_SHA512: - oidtag = SEC_OID_HMAC_SHA512; - break; - default: - goto error; - } - SECItem secSalt; - secSalt.data = salt; - secSalt.len = salt_sz; - // Always pass SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this parameter - // is unused for key generation. It is currently only used - // for PBKDF2 authentication or key (un)wrapping when specifying an - // encryption algorithm (PBES2). - algid = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1, - oidtag, key_sz, workfactor, &secSalt); - if (algid == NULL) goto error; - slot = PK11_GetInternalSlot(); - if (slot == NULL) goto error; - SECItem pwItem; - pwItem.data = (unsigned char *) pass; // PK11_PBEKeyGen doesn't modify the key. - pwItem.len = pass_sz; - symKey = PK11_PBEKeyGen(slot, algid, &pwItem, PR_FALSE, NULL); - if (symKey == NULL) goto error; - if (PK11_ExtractKeyValue(symKey) != SECSuccess) goto error; - // No need to free keyData as it is a buffer managed by symKey. - SECItem* keyData = PK11_GetKeyData(symKey); - if (keyData == NULL) goto error; - memcpy(key, keyData->data, key_sz); +#endif /* #ifndef SQLITE_UNTESTABLE */ - goto cleanup; - error: - rc = SQLITE_ERROR; - cleanup: - if (slot) PK11_FreeSlot(slot); - if (algid) SECOID_DestroyAlgorithmID(algid, PR_TRUE); - if (symKey) PK11_FreeSymKey(symKey); - return rc; -} +/************** End of fault.c ***********************************************/ +/************** Begin file mem0.c ********************************************/ +/* +** 2008 October 28 +** +** 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 a no-op memory allocation drivers for use when +** SQLITE_ZERO_MALLOC is defined. The allocation drivers implemented +** here always fail. SQLite will not operate with these drivers. These +** are merely placeholders. Real drivers must be substituted using +** sqlcipher_sqlite3_config() before SQLite will operate. +*/ +/* #include "sqliteInt.h" */ -static int sqlcipher_nss_cipher(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out) { - int rc = SQLITE_OK; - PK11SlotInfo * slot = NULL; - PK11SymKey* symKey = NULL; - unsigned int outLen; - SECItem params; - params.data = iv; - params.len = sqlcipher_nss_get_iv_sz(ctx); - slot = PK11_GetInternalSlot(); - if (slot == NULL) goto error; - SECItem keyItem; - keyItem.data = key; - keyItem.len = key_sz; - symKey = PK11_ImportSymKey(slot, CKM_AES_CBC, PK11_OriginUnwrap, - CKA_ENCRYPT, &keyItem, NULL); - if (symKey == NULL) goto error; - SECStatus rv; - if (mode == CIPHER_ENCRYPT) { - rv = PK11_Encrypt(symKey, CKM_AES_CBC, ¶ms, out, &outLen, - in_sz + 16, in, in_sz); - } else { - rv = PK11_Decrypt(symKey, CKM_AES_CBC, ¶ms, out, &outLen, - in_sz + 16, in, in_sz); - } - if (rv != SECSuccess) goto error; +/* +** This version of the memory allocator is the default. It is +** used when no other memory allocator is specified using compile-time +** macros. +*/ +#ifdef SQLITE_ZERO_MALLOC - goto cleanup; - error: - rc = SQLITE_ERROR; - cleanup: - if (slot) PK11_FreeSlot(slot); - if (symKey) PK11_FreeSymKey(symKey); - return rc; -} +/* +** No-op versions of all memory allocation routines +*/ +static void *sqlcipher_sqlite3MemMalloc(int nByte){ return 0; } +static void sqlcipher_sqlite3MemFree(void *pPrior){ return; } +static void *sqlcipher_sqlite3MemRealloc(void *pPrior, int nByte){ return 0; } +static int sqlcipher_sqlite3MemSize(void *pPrior){ return 0; } +static int sqlcipher_sqlite3MemRoundup(int n){ return n; } +static int sqlcipher_sqlite3MemInit(void *NotUsed){ return SQLITE_OK; } +static void sqlcipher_sqlite3MemShutdown(void *NotUsed){ return; } -static int sqlcipher_nss_ctx_init(void **ctx) { - sqlcipher_nss_activate(NULL); - return SQLITE_OK; +/* +** This routine is the only routine in this file with external linkage. +** +** Populate the low-level memory allocation function pointers in +** sqlcipher_sqlite3GlobalConfig.m with pointers to the routines in this file. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3MemSetDefault(void){ + static const sqlcipher_sqlite3_mem_methods defaultMethods = { + sqlcipher_sqlite3MemMalloc, + sqlcipher_sqlite3MemFree, + sqlcipher_sqlite3MemRealloc, + sqlcipher_sqlite3MemSize, + sqlcipher_sqlite3MemRoundup, + sqlcipher_sqlite3MemInit, + sqlcipher_sqlite3MemShutdown, + 0 + }; + sqlcipher_sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); } -static int sqlcipher_nss_ctx_free(void **ctx) { - sqlcipher_nss_deactivate(NULL); - return SQLITE_OK; -} +#endif /* SQLITE_ZERO_MALLOC */ -static int sqlcipher_nss_fips_status(void *ctx) { - return 0; -} +/************** End of mem0.c ************************************************/ +/************** Begin file mem1.c ********************************************/ +/* +** 2007 August 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 file contains low-level memory allocation drivers for when +** SQLite will use the standard C-library malloc/realloc/free interface +** to obtain the memory it needs. +** +** This file contains implementations of the low-level memory allocation +** routines specified in the sqlcipher_sqlite3_mem_methods object. The content of +** this file is only used if SQLITE_SYSTEM_MALLOC is defined. The +** SQLITE_SYSTEM_MALLOC macro is defined automatically if neither the +** SQLITE_MEMDEBUG nor the SQLITE_WIN32_MALLOC macros are defined. The +** default configuration is to use memory allocation routines in this +** file. +** +** C-preprocessor macro summary: +** +** HAVE_MALLOC_USABLE_SIZE The configure script sets this symbol if +** the malloc_usable_size() interface exists +** on the target platform. Or, this symbol +** can be set manually, if desired. +** If an equivalent interface exists by +** a different name, using a separate -D +** option to rename it. +** +** SQLITE_WITHOUT_ZONEMALLOC Some older macs lack support for the zone +** memory allocator. Set this symbol to enable +** building on older macs. +** +** SQLITE_WITHOUT_MSIZE Set this symbol to disable the use of +** _msize() on windows systems. This might +** be necessary when compiling for Delphi, +** for example. +*/ +/* #include "sqliteInt.h" */ -int sqlcipher_nss_setup(sqlcipher_provider *p) { - p->activate = sqlcipher_nss_activate; - p->deactivate = sqlcipher_nss_deactivate; - p->random = sqlcipher_nss_random; - p->get_provider_name = sqlcipher_nss_get_provider_name; - p->hmac = sqlcipher_nss_hmac; - p->kdf = sqlcipher_nss_kdf; - p->cipher = sqlcipher_nss_cipher; - p->get_cipher = sqlcipher_nss_get_cipher; - p->get_key_sz = sqlcipher_nss_get_key_sz; - p->get_iv_sz = sqlcipher_nss_get_iv_sz; - p->get_block_sz = sqlcipher_nss_get_block_sz; - p->get_hmac_sz = sqlcipher_nss_get_hmac_sz; - p->ctx_init = sqlcipher_nss_ctx_init; - p->ctx_free = sqlcipher_nss_ctx_free; - p->add_random = sqlcipher_nss_add_random; - p->fips_status = sqlcipher_nss_fips_status; - p->get_provider_version = sqlcipher_nss_get_provider_version; - return SQLITE_OK; -} +/* +** This version of the memory allocator is the default. It is +** used when no other memory allocator is specified using compile-time +** macros. +*/ +#ifdef SQLITE_SYSTEM_MALLOC +#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC) +/* +** Use the zone allocator available on apple products unless the +** SQLITE_WITHOUT_ZONEMALLOC symbol is defined. +*/ +#include +#include +#ifdef SQLITE_MIGHT_BE_SINGLE_CORE +#include +#endif /* SQLITE_MIGHT_BE_SINGLE_CORE */ +static malloc_zone_t* _sqliteZone_; +#define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x)) +#define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x)); +#define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y)) +#define SQLITE_MALLOCSIZE(x) \ + (_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x)) + +#else /* if not __APPLE__ */ + +/* +** Use standard C library malloc and free on non-Apple systems. +** Also used by Apple systems if SQLITE_WITHOUT_ZONEMALLOC is defined. +*/ +#define SQLITE_MALLOC(x) malloc(x) +#define SQLITE_FREE(x) free(x) +#define SQLITE_REALLOC(x,y) realloc((x),(y)) + +/* +** The malloc.h header file is needed for malloc_usable_size() function +** on some systems (e.g. Linux). +*/ +#if HAVE_MALLOC_H && HAVE_MALLOC_USABLE_SIZE +# define SQLITE_USE_MALLOC_H 1 +# define SQLITE_USE_MALLOC_USABLE_SIZE 1 +/* +** The MSVCRT has malloc_usable_size(), but it is called _msize(). The +** use of _msize() is automatic, but can be disabled by compiling with +** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires +** the malloc.h header file. +*/ +#elif defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE) +# define SQLITE_USE_MALLOC_H +# define SQLITE_USE_MSIZE #endif -#endif -/* END SQLCIPHER */ -/************** End of crypto_nss.c ******************************************/ -/************** Begin file crypto_openssl.c **********************************/ /* -** SQLCipher -** http://sqlcipher.net -** -** Copyright (c) 2008 - 2013, ZETETIC LLC -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of the ZETETIC LLC nor the -** names of its contributors may be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY -** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY -** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** +** Include the malloc.h header file, if necessary. Also set define macro +** SQLITE_MALLOCSIZE to the appropriate function name, which is _msize() +** for MSVC and malloc_usable_size() for most other systems (e.g. Linux). +** The memory size function can always be overridden manually by defining +** the macro SQLITE_MALLOCSIZE to the desired function name. */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC -#ifdef SQLCIPHER_CRYPTO_OPENSSL -/* #include "sqliteInt.h" */ -/* #include "crypto.h" */ -/* #include "sqlcipher.h" */ -#include -#include -#include -#include -#include +#if defined(SQLITE_USE_MALLOC_H) +# include +# if defined(SQLITE_USE_MALLOC_USABLE_SIZE) +# if !defined(SQLITE_MALLOCSIZE) +# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x) +# endif +# elif defined(SQLITE_USE_MSIZE) +# if !defined(SQLITE_MALLOCSIZE) +# define SQLITE_MALLOCSIZE _msize +# endif +# endif +#endif /* defined(SQLITE_USE_MALLOC_H) */ -static unsigned int openssl_init_count = 0; +#endif /* __APPLE__ or not __APPLE__ */ -#if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) -static HMAC_CTX *HMAC_CTX_new(void) -{ - HMAC_CTX *ctx = OPENSSL_malloc(sizeof(*ctx)); - if (ctx != NULL) { - HMAC_CTX_init(ctx); +/* +** Like malloc(), but remember the size of the allocation +** so that we can find it later using sqlcipher_sqlite3MemSize(). +** +** For this low-level routine, we are guaranteed that nByte>0 because +** cases of nByte<=0 will be intercepted and dealt with by higher level +** routines. +*/ +static void *sqlcipher_sqlite3MemMalloc(int nByte){ +#ifdef SQLITE_MALLOCSIZE + void *p; + testcase( ROUND8(nByte)==nByte ); + p = SQLITE_MALLOC( nByte ); + if( p==0 ){ + testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); + sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte); } - return ctx; -} - -/* Per 1.1.0 (https://wiki.openssl.org/index.php/1.1_API_Changes) - HMAC_CTX_free should call HMAC_CTX_cleanup, then EVP_MD_CTX_Cleanup. - HMAC_CTX_cleanup internally calls EVP_MD_CTX_cleanup so these - calls are not needed. */ -static void HMAC_CTX_free(HMAC_CTX *ctx) -{ - if (ctx != NULL) { - HMAC_CTX_cleanup(ctx); - OPENSSL_free(ctx); + return p; +#else + sqlcipher_sqlite3_int64 *p; + assert( nByte>0 ); + testcase( ROUND8(nByte)!=nByte ); + p = SQLITE_MALLOC( nByte+8 ); + if( p ){ + p[0] = nByte; + p++; + }else{ + testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); + sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte); } -} + return (void *)p; #endif +} -static int sqlcipher_openssl_add_random(void *ctx, void *buffer, int length) { -#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND - CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); - CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND\n"); -#endif - RAND_add(buffer, length, 0); -#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND - CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); - CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: left SQLCIPHER_MUTEX_PROVIDER_RAND\n"); +/* +** Like free() but works for allocations obtained from sqlcipher_sqlite3MemMalloc() +** or sqlcipher_sqlite3MemRealloc(). +** +** For this low-level routine, we already know that pPrior!=0 since +** cases where pPrior==0 will have been intecepted and dealt with +** by higher-level routines. +*/ +static void sqlcipher_sqlite3MemFree(void *pPrior){ +#ifdef SQLITE_MALLOCSIZE + SQLITE_FREE(pPrior); +#else + sqlcipher_sqlite3_int64 *p = (sqlcipher_sqlite3_int64*)pPrior; + assert( pPrior!=0 ); + p--; + SQLITE_FREE(p); #endif - return SQLITE_OK; } -#define OPENSSL_CIPHER EVP_aes_256_cbc() - - -/* activate and initialize sqlcipher. Most importantly, this will automatically - intialize OpenSSL's EVP system if it hasn't already be externally. Note that - this function may be called multiple times as new codecs are intiialized. - Thus it performs some basic counting to ensure that only the last and final - sqlcipher_openssl_deactivate() will free the EVP structures. +/* +** Report the allocated size of a prior return from xMalloc() +** or xRealloc(). */ -static int sqlcipher_openssl_activate(void *ctx) { - /* initialize openssl and increment the internal init counter - but only if it hasn't been initalized outside of SQLCipher by this program - e.g. on startup */ - - CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - -#ifdef SQLCIPHER_FIPS - if(!FIPS_mode()){ - if(!FIPS_mode_set(1)){ - unsigned long err = 0; - ERR_load_crypto_strings(); -#ifdef __ANDROID__ - while((err = ERR_get_error()) != 0) { - __android_log_print(ANDROID_LOG_ERROR, "sqlcipher","error: %lx. %s.", err, ERR_error_string(err, NULL)); - } +static int sqlcipher_sqlite3MemSize(void *pPrior){ +#ifdef SQLITE_MALLOCSIZE + assert( pPrior!=0 ); + return (int)SQLITE_MALLOCSIZE(pPrior); #else - ERR_print_errors_fp(stderr); + sqlcipher_sqlite3_int64 *p; + assert( pPrior!=0 ); + p = (sqlcipher_sqlite3_int64*)pPrior; + p--; + return (int)p[0]; #endif - } +} + +/* +** Like realloc(). Resize an allocation previously obtained from +** sqlcipher_sqlite3MemMalloc(). +** +** For this low-level interface, we know that pPrior!=0. Cases where +** pPrior==0 while have been intercepted by higher-level routine and +** redirected to xMalloc. Similarly, we know that nByte>0 because +** cases where nByte<=0 will have been intercepted by higher-level +** routines and redirected to xFree. +*/ +static void *sqlcipher_sqlite3MemRealloc(void *pPrior, int nByte){ +#ifdef SQLITE_MALLOCSIZE + void *p = SQLITE_REALLOC(pPrior, nByte); + if( p==0 ){ + testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); + sqlcipher_sqlite3_log(SQLITE_NOMEM, + "failed memory resize %u to %u bytes", + SQLITE_MALLOCSIZE(pPrior), nByte); + } + return p; +#else + sqlcipher_sqlite3_int64 *p = (sqlcipher_sqlite3_int64*)pPrior; + assert( pPrior!=0 && nByte>0 ); + assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */ + p--; + p = SQLITE_REALLOC(p, nByte+8 ); + if( p ){ + p[0] = nByte; + p++; + }else{ + testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); + sqlcipher_sqlite3_log(SQLITE_NOMEM, + "failed memory resize %u to %u bytes", + sqlcipher_sqlite3MemSize(pPrior), nByte); } + return (void*)p; #endif - - openssl_init_count++; - CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - return SQLITE_OK; } -/* deactivate SQLCipher, most imporantly decremeting the activation count and - freeing the EVP structures on the final deactivation to ensure that - OpenSSL memory is cleaned up */ -static int sqlcipher_openssl_deactivate(void *ctx) { - CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - - openssl_init_count--; +/* +** Round up a request size to the next valid allocation size. +*/ +static int sqlcipher_sqlite3MemRoundup(int n){ + return ROUND8(n); +} - CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); - CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n"); +/* +** Initialize this module. +*/ +static int sqlcipher_sqlite3MemInit(void *NotUsed){ +#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC) + int cpuCount; + size_t len; + if( _sqliteZone_ ){ + return SQLITE_OK; + } + len = sizeof(cpuCount); + /* One usually wants to use hw.acctivecpu for MT decisions, but not here */ + sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0); + if( cpuCount>1 ){ + /* defer MT decisions to system malloc */ + _sqliteZone_ = malloc_default_zone(); + }else{ + /* only 1 core, use our own zone to contention over global locks, + ** e.g. we have our own dedicated locks */ + _sqliteZone_ = malloc_create_zone(4096, 0); + malloc_set_zone_name(_sqliteZone_, "Sqlite_Heap"); + } +#endif /* defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC) */ + UNUSED_PARAMETER(NotUsed); return SQLITE_OK; } -static const char* sqlcipher_openssl_get_provider_name(void *ctx) { - return "openssl"; +/* +** Deinitialize this module. +*/ +static void sqlcipher_sqlite3MemShutdown(void *NotUsed){ + UNUSED_PARAMETER(NotUsed); + return; } -static const char* sqlcipher_openssl_get_provider_version(void *ctx) { - return OPENSSL_VERSION_TEXT; +/* +** This routine is the only routine in this file with external linkage. +** +** Populate the low-level memory allocation function pointers in +** sqlcipher_sqlite3GlobalConfig.m with pointers to the routines in this file. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3MemSetDefault(void){ + static const sqlcipher_sqlite3_mem_methods defaultMethods = { + sqlcipher_sqlite3MemMalloc, + sqlcipher_sqlite3MemFree, + sqlcipher_sqlite3MemRealloc, + sqlcipher_sqlite3MemSize, + sqlcipher_sqlite3MemRoundup, + sqlcipher_sqlite3MemInit, + sqlcipher_sqlite3MemShutdown, + 0 + }; + sqlcipher_sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); } -/* generate a defined number of random bytes */ -static int sqlcipher_openssl_random (void *ctx, void *buffer, int length) { - int rc = 0; - /* concurrent calls to RAND_bytes can cause a crash under some openssl versions when a - naive application doesn't use CRYPTO_set_locking_callback and - CRYPTO_THREADID_set_callback to ensure openssl thread safety. - This is simple workaround to prevent this common crash - but a more proper solution is that applications setup platform-appropriate - thread saftey in openssl externally */ -#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND - CODEC_TRACE_MUTEX("sqlcipher_openssl_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); - CODEC_TRACE_MUTEX("sqlcipher_openssl_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND\n"); -#endif - rc = RAND_bytes((unsigned char *)buffer, length); -#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND - CODEC_TRACE_MUTEX("sqlcipher_openssl_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND\n"); - sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); - CODEC_TRACE_MUTEX("sqlcipher_openssl_random: left SQLCIPHER_MUTEX_PROVIDER_RAND\n"); -#endif - return (rc == 1) ? SQLITE_OK : SQLITE_ERROR; -} +#endif /* SQLITE_SYSTEM_MALLOC */ -static int sqlcipher_openssl_hmac(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out) { - unsigned int outlen; - int rc = SQLITE_OK; - HMAC_CTX* hctx = NULL; +/************** End of mem1.c ************************************************/ +/************** Begin file mem2.c ********************************************/ +/* +** 2007 August 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 low-level memory allocation drivers for when +** SQLite will use the standard C-library malloc/realloc/free interface +** to obtain the memory it needs while adding lots of additional debugging +** information to each allocation in order to help detect and fix memory +** leaks and memory usage errors. +** +** This file contains implementations of the low-level memory allocation +** routines specified in the sqlcipher_sqlite3_mem_methods object. +*/ +/* #include "sqliteInt.h" */ - if(in == NULL) goto error; +/* +** This version of the memory allocator is used only if the +** SQLITE_MEMDEBUG macro is defined +*/ +#ifdef SQLITE_MEMDEBUG - hctx = HMAC_CTX_new(); - if(hctx == NULL) goto error; +/* +** The backtrace functionality is only available with GLIBC +*/ +#ifdef __GLIBC__ + extern int backtrace(void**,int); + extern void backtrace_symbols_fd(void*const*,int,int); +#else +# define backtrace(A,B) 1 +# define backtrace_symbols_fd(A,B,C) +#endif +/* #include */ - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - if(!HMAC_Init_ex(hctx, hmac_key, key_sz, EVP_sha1(), NULL)) goto error; - break; - case SQLCIPHER_HMAC_SHA256: - if(!HMAC_Init_ex(hctx, hmac_key, key_sz, EVP_sha256(), NULL)) goto error; - break; - case SQLCIPHER_HMAC_SHA512: - if(!HMAC_Init_ex(hctx, hmac_key, key_sz, EVP_sha512(), NULL)) goto error; - break; - default: - goto error; - } +/* +** Each memory allocation looks like this: +** +** ------------------------------------------------------------------------ +** | Title | backtrace pointers | MemBlockHdr | allocation | EndGuard | +** ------------------------------------------------------------------------ +** +** The application code sees only a pointer to the allocation. We have +** to back up from the allocation pointer to find the MemBlockHdr. The +** MemBlockHdr tells us the size of the allocation and the number of +** backtrace pointers. There is also a guard word at the end of the +** MemBlockHdr. +*/ +struct MemBlockHdr { + i64 iSize; /* Size of this allocation */ + struct MemBlockHdr *pNext, *pPrev; /* Linked list of all unfreed memory */ + char nBacktrace; /* Number of backtraces on this alloc */ + char nBacktraceSlots; /* Available backtrace slots */ + u8 nTitle; /* Bytes of title; includes '\0' */ + u8 eType; /* Allocation type code */ + int iForeGuard; /* Guard word for sanity */ +}; - if(!HMAC_Update(hctx, in, in_sz)) goto error; - if(in2 != NULL) { - if(!HMAC_Update(hctx, in2, in2_sz)) goto error; - } - if(!HMAC_Final(hctx, out, &outlen)) goto error; +/* +** Guard words +*/ +#define FOREGUARD 0x80F5E153 +#define REARGUARD 0xE4676B53 - goto cleanup; -error: - rc = SQLITE_ERROR; -cleanup: - if(hctx) HMAC_CTX_free(hctx); - return rc; -} +/* +** Number of malloc size increments to track. +*/ +#define NCSIZE 1000 -static int sqlcipher_openssl_kdf(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) { - int rc = SQLITE_OK; +/* +** All of the static variables used by this module are collected +** into a single structure named "mem". This is to keep the +** static variables organized and to reduce namespace pollution +** when this module is combined with other in the amalgamation. +*/ +static struct { - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - if(!PKCS5_PBKDF2_HMAC((const char *)pass, pass_sz, salt, salt_sz, workfactor, EVP_sha1(), key_sz, key)) goto error; - break; - case SQLCIPHER_HMAC_SHA256: - if(!PKCS5_PBKDF2_HMAC((const char *)pass, pass_sz, salt, salt_sz, workfactor, EVP_sha256(), key_sz, key)) goto error; - break; - case SQLCIPHER_HMAC_SHA512: - if(!PKCS5_PBKDF2_HMAC((const char *)pass, pass_sz, salt, salt_sz, workfactor, EVP_sha512(), key_sz, key)) goto error; - break; - default: - return SQLITE_ERROR; - } + /* + ** Mutex to control access to the memory allocation subsystem. + */ + sqlcipher_sqlite3_mutex *mutex; - goto cleanup; -error: - rc = SQLITE_ERROR; -cleanup: - return rc; -} + /* + ** Head and tail of a linked list of all outstanding allocations + */ + struct MemBlockHdr *pFirst; + struct MemBlockHdr *pLast; -static int sqlcipher_openssl_cipher(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out) { - int tmp_csz, csz, rc = SQLITE_OK; - EVP_CIPHER_CTX* ectx = EVP_CIPHER_CTX_new(); - if(ectx == NULL) goto error; - if(!EVP_CipherInit_ex(ectx, OPENSSL_CIPHER, NULL, NULL, NULL, mode)) goto error; - if(!EVP_CIPHER_CTX_set_padding(ectx, 0)) goto error; /* no padding */ - if(!EVP_CipherInit_ex(ectx, NULL, NULL, key, iv, mode)) goto error; - if(!EVP_CipherUpdate(ectx, out, &tmp_csz, in, in_sz)) goto error; - csz = tmp_csz; - out += tmp_csz; - if(!EVP_CipherFinal_ex(ectx, out, &tmp_csz)) goto error; - csz += tmp_csz; - assert(in_sz == csz); + /* + ** The number of levels of backtrace to save in new allocations. + */ + int nBacktrace; + void (*xBacktrace)(int, int, void **); - goto cleanup; -error: - rc = SQLITE_ERROR; -cleanup: - if(ectx) EVP_CIPHER_CTX_free(ectx); - return rc; -} + /* + ** Title text to insert in front of each block + */ + int nTitle; /* Bytes of zTitle to save. Includes '\0' and padding */ + char zTitle[100]; /* The title text */ -static const char* sqlcipher_openssl_get_cipher(void *ctx) { - return OBJ_nid2sn(EVP_CIPHER_nid(OPENSSL_CIPHER)); -} + /* + ** sqlcipher_sqlite3MallocDisallow() increments the following counter. + ** sqlcipher_sqlite3MallocAllow() decrements it. + */ + int disallow; /* Do not allow memory allocation */ -static int sqlcipher_openssl_get_key_sz(void *ctx) { - return EVP_CIPHER_key_length(OPENSSL_CIPHER); -} + /* + ** Gather statistics on the sizes of memory allocations. + ** nAlloc[i] is the number of allocation attempts of i*8 + ** bytes. i==NCSIZE is the number of allocation attempts for + ** sizes more than NCSIZE*8 bytes. + */ + int nAlloc[NCSIZE]; /* Total number of allocations */ + int nCurrent[NCSIZE]; /* Current number of allocations */ + int mxCurrent[NCSIZE]; /* Highwater mark for nCurrent */ -static int sqlcipher_openssl_get_iv_sz(void *ctx) { - return EVP_CIPHER_iv_length(OPENSSL_CIPHER); -} +} mem; -static int sqlcipher_openssl_get_block_sz(void *ctx) { - return EVP_CIPHER_block_size(OPENSSL_CIPHER); -} -static int sqlcipher_openssl_get_hmac_sz(void *ctx, int algorithm) { - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - return EVP_MD_size(EVP_sha1()); - break; - case SQLCIPHER_HMAC_SHA256: - return EVP_MD_size(EVP_sha256()); - break; - case SQLCIPHER_HMAC_SHA512: - return EVP_MD_size(EVP_sha512()); - break; - default: - return 0; +/* +** Adjust memory usage statistics +*/ +static void adjustStats(int iSize, int increment){ + int i = ROUND8(iSize)/8; + if( i>NCSIZE-1 ){ + i = NCSIZE - 1; + } + if( increment>0 ){ + mem.nAlloc[i]++; + mem.nCurrent[i]++; + if( mem.nCurrent[i]>mem.mxCurrent[i] ){ + mem.mxCurrent[i] = mem.nCurrent[i]; + } + }else{ + mem.nCurrent[i]--; + assert( mem.nCurrent[i]>=0 ); } } -static int sqlcipher_openssl_ctx_init(void **ctx) { - return sqlcipher_openssl_activate(*ctx); -} +/* +** Given an allocation, find the MemBlockHdr for that allocation. +** +** This routine checks the guards at either end of the allocation and +** if they are incorrect it asserts. +*/ +static struct MemBlockHdr *sqlcipher_sqlite3MemsysGetHeader(const void *pAllocation){ + struct MemBlockHdr *p; + int *pInt; + u8 *pU8; + int nReserve; -static int sqlcipher_openssl_ctx_free(void **ctx) { - return sqlcipher_openssl_deactivate(NULL); + p = (struct MemBlockHdr*)pAllocation; + p--; + assert( p->iForeGuard==(int)FOREGUARD ); + nReserve = ROUND8(p->iSize); + pInt = (int*)pAllocation; + pU8 = (u8*)pAllocation; + assert( pInt[nReserve/sizeof(int)]==(int)REARGUARD ); + /* This checks any of the "extra" bytes allocated due + ** to rounding up to an 8 byte boundary to ensure + ** they haven't been overwritten. + */ + while( nReserve-- > p->iSize ) assert( pU8[nReserve]==0x65 ); + return p; } -static int sqlcipher_openssl_fips_status(void *ctx) { -#ifdef SQLCIPHER_FIPS - return FIPS_mode(); -#else - return 0; -#endif +/* +** Return the number of bytes currently allocated at address p. +*/ +static int sqlcipher_sqlite3MemSize(void *p){ + struct MemBlockHdr *pHdr; + if( !p ){ + return 0; + } + pHdr = sqlcipher_sqlite3MemsysGetHeader(p); + return (int)pHdr->iSize; } -int sqlcipher_openssl_setup(sqlcipher_provider *p) { - p->activate = sqlcipher_openssl_activate; - p->deactivate = sqlcipher_openssl_deactivate; - p->get_provider_name = sqlcipher_openssl_get_provider_name; - p->random = sqlcipher_openssl_random; - p->hmac = sqlcipher_openssl_hmac; - p->kdf = sqlcipher_openssl_kdf; - p->cipher = sqlcipher_openssl_cipher; - p->get_cipher = sqlcipher_openssl_get_cipher; - p->get_key_sz = sqlcipher_openssl_get_key_sz; - p->get_iv_sz = sqlcipher_openssl_get_iv_sz; - p->get_block_sz = sqlcipher_openssl_get_block_sz; - p->get_hmac_sz = sqlcipher_openssl_get_hmac_sz; - p->ctx_init = sqlcipher_openssl_ctx_init; - p->ctx_free = sqlcipher_openssl_ctx_free; - p->add_random = sqlcipher_openssl_add_random; - p->fips_status = sqlcipher_openssl_fips_status; - p->get_provider_version = sqlcipher_openssl_get_provider_version; +/* +** Initialize the memory allocation subsystem. +*/ +static int sqlcipher_sqlite3MemInit(void *NotUsed){ + UNUSED_PARAMETER(NotUsed); + assert( (sizeof(struct MemBlockHdr)&7) == 0 ); + if( !sqlcipher_sqlite3GlobalConfig.bMemstat ){ + /* If memory status is enabled, then the malloc.c wrapper will already + ** hold the STATIC_MEM mutex when the routines here are invoked. */ + mem.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); + } return SQLITE_OK; } -#endif -#endif -/* END SQLCIPHER */ - -/************** End of crypto_openssl.c **************************************/ -/************** Begin file crypto_cc.c ***************************************/ /* -** SQLCipher -** http://sqlcipher.net -** -** Copyright (c) 2008 - 2013, ZETETIC LLC -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of the ZETETIC LLC nor the -** names of its contributors may be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY -** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY -** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** +** Deinitialize the memory allocation subsystem. */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC -#ifdef SQLCIPHER_CRYPTO_CC -/* #include "crypto.h" */ -/* #include "sqlcipher.h" */ -#include -#include -#include - -int sqlcipher_cc_setup(sqlcipher_provider *p); - -static int sqlcipher_cc_add_random(void *ctx, void *buffer, int length) { - return SQLITE_OK; +static void sqlcipher_sqlite3MemShutdown(void *NotUsed){ + UNUSED_PARAMETER(NotUsed); + mem.mutex = 0; } -/* generate a defined number of random bytes */ -static int sqlcipher_cc_random (void *ctx, void *buffer, int length) { - return (SecRandomCopyBytes(kSecRandomDefault, length, (uint8_t *)buffer) == kCCSuccess) ? SQLITE_OK : SQLITE_ERROR; +/* +** Round up a request size to the next valid allocation size. +*/ +static int sqlcipher_sqlite3MemRoundup(int n){ + return ROUND8(n); } -static const char* sqlcipher_cc_get_provider_name(void *ctx) { - return "commoncrypto"; +/* +** Fill a buffer with pseudo-random bytes. This is used to preset +** the content of a new memory allocation to unpredictable values and +** to clear the content of a freed allocation to unpredictable values. +*/ +static void randomFill(char *pBuf, int nByte){ + unsigned int x, y, r; + x = SQLITE_PTR_TO_INT(pBuf); + y = nByte | 1; + while( nByte >= 4 ){ + x = (x>>1) ^ (-(int)(x&1) & 0xd0000001); + y = y*1103515245 + 12345; + r = x ^ y; + *(int*)pBuf = r; + pBuf += 4; + nByte -= 4; + } + while( nByte-- > 0 ){ + x = (x>>1) ^ (-(int)(x&1) & 0xd0000001); + y = y*1103515245 + 12345; + r = x ^ y; + *(pBuf++) = r & 0xff; + } } -static const char* sqlcipher_cc_get_provider_version(void *ctx) { -#if TARGET_OS_MAC - CFTypeRef version; - CFBundleRef bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")); - if(bundle == NULL) { - return "unknown"; +/* +** Allocate nByte bytes of memory. +*/ +static void *sqlcipher_sqlite3MemMalloc(int nByte){ + struct MemBlockHdr *pHdr; + void **pBt; + char *z; + int *pInt; + void *p = 0; + int totalSize; + int nReserve; + sqlcipher_sqlite3_mutex_enter(mem.mutex); + assert( mem.disallow==0 ); + nReserve = ROUND8(nByte); + totalSize = nReserve + sizeof(*pHdr) + sizeof(int) + + mem.nBacktrace*sizeof(void*) + mem.nTitle; + p = malloc(totalSize); + if( p ){ + z = p; + pBt = (void**)&z[mem.nTitle]; + pHdr = (struct MemBlockHdr*)&pBt[mem.nBacktrace]; + pHdr->pNext = 0; + pHdr->pPrev = mem.pLast; + if( mem.pLast ){ + mem.pLast->pNext = pHdr; + }else{ + mem.pFirst = pHdr; + } + mem.pLast = pHdr; + pHdr->iForeGuard = FOREGUARD; + pHdr->eType = MEMTYPE_HEAP; + pHdr->nBacktraceSlots = mem.nBacktrace; + pHdr->nTitle = mem.nTitle; + if( mem.nBacktrace ){ + void *aAddr[40]; + pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1; + memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*)); + assert(pBt[0]); + if( mem.xBacktrace ){ + mem.xBacktrace(nByte, pHdr->nBacktrace-1, &aAddr[1]); + } + }else{ + pHdr->nBacktrace = 0; + } + if( mem.nTitle ){ + memcpy(z, mem.zTitle, mem.nTitle); + } + pHdr->iSize = nByte; + adjustStats(nByte, +1); + pInt = (int*)&pHdr[1]; + pInt[nReserve/sizeof(int)] = REARGUARD; + randomFill((char*)pInt, nByte); + memset(((char*)pInt)+nByte, 0x65, nReserve-nByte); + p = (void*)pInt; } - version = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString")); - return CFStringGetCStringPtr(version, kCFStringEncodingUTF8); -#else - return "unknown"; -#endif + sqlcipher_sqlite3_mutex_leave(mem.mutex); + return p; } -static int sqlcipher_cc_hmac(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out) { - CCHmacContext hmac_context; - if(in == NULL) return SQLITE_ERROR; - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - CCHmacInit(&hmac_context, kCCHmacAlgSHA1, hmac_key, key_sz); - break; - case SQLCIPHER_HMAC_SHA256: - CCHmacInit(&hmac_context, kCCHmacAlgSHA256, hmac_key, key_sz); - break; - case SQLCIPHER_HMAC_SHA512: - CCHmacInit(&hmac_context, kCCHmacAlgSHA512, hmac_key, key_sz); - break; - default: - return SQLITE_ERROR; +/* +** Free memory. +*/ +static void sqlcipher_sqlite3MemFree(void *pPrior){ + struct MemBlockHdr *pHdr; + void **pBt; + char *z; + assert( sqlcipher_sqlite3GlobalConfig.bMemstat || sqlcipher_sqlite3GlobalConfig.bCoreMutex==0 + || mem.mutex!=0 ); + pHdr = sqlcipher_sqlite3MemsysGetHeader(pPrior); + pBt = (void**)pHdr; + pBt -= pHdr->nBacktraceSlots; + sqlcipher_sqlite3_mutex_enter(mem.mutex); + if( pHdr->pPrev ){ + assert( pHdr->pPrev->pNext==pHdr ); + pHdr->pPrev->pNext = pHdr->pNext; + }else{ + assert( mem.pFirst==pHdr ); + mem.pFirst = pHdr->pNext; } - CCHmacUpdate(&hmac_context, in, in_sz); - if(in2 != NULL) CCHmacUpdate(&hmac_context, in2, in2_sz); - CCHmacFinal(&hmac_context, out); - return SQLITE_OK; + if( pHdr->pNext ){ + assert( pHdr->pNext->pPrev==pHdr ); + pHdr->pNext->pPrev = pHdr->pPrev; + }else{ + assert( mem.pLast==pHdr ); + mem.pLast = pHdr->pPrev; + } + z = (char*)pBt; + z -= pHdr->nTitle; + adjustStats((int)pHdr->iSize, -1); + randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) + + (int)pHdr->iSize + sizeof(int) + pHdr->nTitle); + free(z); + sqlcipher_sqlite3_mutex_leave(mem.mutex); } -static int sqlcipher_cc_kdf(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) { - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - if(CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pass, pass_sz, salt, salt_sz, kCCPRFHmacAlgSHA1, workfactor, key, key_sz) != kCCSuccess) return SQLITE_ERROR; - break; - case SQLCIPHER_HMAC_SHA256: - if(CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pass, pass_sz, salt, salt_sz, kCCPRFHmacAlgSHA256, workfactor, key, key_sz) != kCCSuccess) return SQLITE_ERROR; - break; - case SQLCIPHER_HMAC_SHA512: - if(CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pass, pass_sz, salt, salt_sz, kCCPRFHmacAlgSHA512, workfactor, key, key_sz) != kCCSuccess) return SQLITE_ERROR; - break; - default: - return SQLITE_ERROR; +/* +** Change the size of an existing memory allocation. +** +** For this debugging implementation, we *always* make a copy of the +** allocation into a new place in memory. In this way, if the +** higher level code is using pointer to the old allocation, it is +** much more likely to break and we are much more liking to find +** the error. +*/ +static void *sqlcipher_sqlite3MemRealloc(void *pPrior, int nByte){ + struct MemBlockHdr *pOldHdr; + void *pNew; + assert( mem.disallow==0 ); + assert( (nByte & 7)==0 ); /* EV: R-46199-30249 */ + pOldHdr = sqlcipher_sqlite3MemsysGetHeader(pPrior); + pNew = sqlcipher_sqlite3MemMalloc(nByte); + if( pNew ){ + memcpy(pNew, pPrior, (int)(nByteiSize ? nByte : pOldHdr->iSize)); + if( nByte>pOldHdr->iSize ){ + randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - (int)pOldHdr->iSize); + } + sqlcipher_sqlite3MemFree(pPrior); } - return SQLITE_OK; + return pNew; } -static int sqlcipher_cc_cipher(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out) { - CCCryptorRef cryptor; - size_t tmp_csz, csz; - CCOperation op = mode == CIPHER_ENCRYPT ? kCCEncrypt : kCCDecrypt; - - if(CCCryptorCreate(op, kCCAlgorithmAES128, 0, key, kCCKeySizeAES256, iv, &cryptor) != kCCSuccess) return SQLITE_ERROR; - if(CCCryptorUpdate(cryptor, in, in_sz, out, in_sz, &tmp_csz) != kCCSuccess) return SQLITE_ERROR; - csz = tmp_csz; - out += tmp_csz; - if(CCCryptorFinal(cryptor, out, in_sz - csz, &tmp_csz) != kCCSuccess) return SQLITE_ERROR; - csz += tmp_csz; - if(CCCryptorRelease(cryptor) != kCCSuccess) return SQLITE_ERROR; - assert(in_sz == csz); - - return SQLITE_OK; +/* +** Populate the low-level memory allocation function pointers in +** sqlcipher_sqlite3GlobalConfig.m with pointers to the routines in this file. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3MemSetDefault(void){ + static const sqlcipher_sqlite3_mem_methods defaultMethods = { + sqlcipher_sqlite3MemMalloc, + sqlcipher_sqlite3MemFree, + sqlcipher_sqlite3MemRealloc, + sqlcipher_sqlite3MemSize, + sqlcipher_sqlite3MemRoundup, + sqlcipher_sqlite3MemInit, + sqlcipher_sqlite3MemShutdown, + 0 + }; + sqlcipher_sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); } -static const char* sqlcipher_cc_get_cipher(void *ctx) { - return "aes-256-cbc"; +/* +** Set the "type" of an allocation. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugSetType(void *p, u8 eType){ + if( p && sqlcipher_sqlite3GlobalConfig.m.xFree==sqlcipher_sqlite3MemFree ){ + struct MemBlockHdr *pHdr; + pHdr = sqlcipher_sqlite3MemsysGetHeader(p); + assert( pHdr->iForeGuard==FOREGUARD ); + pHdr->eType = eType; + } } -static int sqlcipher_cc_get_key_sz(void *ctx) { - return kCCKeySizeAES256; +/* +** Return TRUE if the mask of type in eType matches the type of the +** allocation p. Also return true if p==NULL. +** +** This routine is designed for use within an assert() statement, to +** verify the type of an allocation. For example: +** +** assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3MemdebugHasType(const void *p, u8 eType){ + int rc = 1; + if( p && sqlcipher_sqlite3GlobalConfig.m.xFree==sqlcipher_sqlite3MemFree ){ + struct MemBlockHdr *pHdr; + pHdr = sqlcipher_sqlite3MemsysGetHeader(p); + assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */ + if( (pHdr->eType&eType)==0 ){ + rc = 0; + } + } + return rc; } -static int sqlcipher_cc_get_iv_sz(void *ctx) { - return kCCBlockSizeAES128; +/* +** Return TRUE if the mask of type in eType matches no bits of the type of the +** allocation p. Also return true if p==NULL. +** +** This routine is designed for use within an assert() statement, to +** verify the type of an allocation. For example: +** +** assert( sqlcipher_sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3MemdebugNoType(const void *p, u8 eType){ + int rc = 1; + if( p && sqlcipher_sqlite3GlobalConfig.m.xFree==sqlcipher_sqlite3MemFree ){ + struct MemBlockHdr *pHdr; + pHdr = sqlcipher_sqlite3MemsysGetHeader(p); + assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */ + if( (pHdr->eType&eType)!=0 ){ + rc = 0; + } + } + return rc; } -static int sqlcipher_cc_get_block_sz(void *ctx) { - return kCCBlockSizeAES128; +/* +** Set the number of backtrace levels kept for each allocation. +** A value of zero turns off backtracing. The number is always rounded +** up to a multiple of 2. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugBacktrace(int depth){ + if( depth<0 ){ depth = 0; } + if( depth>20 ){ depth = 20; } + depth = (depth+1)&0xfe; + mem.nBacktrace = depth; } -static int sqlcipher_cc_get_hmac_sz(void *ctx, int algorithm) { - switch(algorithm) { - case SQLCIPHER_HMAC_SHA1: - return CC_SHA1_DIGEST_LENGTH; - break; - case SQLCIPHER_HMAC_SHA256: - return CC_SHA256_DIGEST_LENGTH; - break; - case SQLCIPHER_HMAC_SHA512: - return CC_SHA512_DIGEST_LENGTH; - break; - default: - return 0; - } +SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugBacktraceCallback(void (*xBacktrace)(int, int, void **)){ + mem.xBacktrace = xBacktrace; } -static int sqlcipher_cc_ctx_init(void **ctx) { - return SQLITE_OK; +/* +** Set the title string for subsequent allocations. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugSettitle(const char *zTitle){ + unsigned int n = sqlcipher_sqlite3Strlen30(zTitle) + 1; + sqlcipher_sqlite3_mutex_enter(mem.mutex); + if( n>=sizeof(mem.zTitle) ) n = sizeof(mem.zTitle)-1; + memcpy(mem.zTitle, zTitle, n); + mem.zTitle[n] = 0; + mem.nTitle = ROUND8(n); + sqlcipher_sqlite3_mutex_leave(mem.mutex); } -static int sqlcipher_cc_ctx_free(void **ctx) { - return SQLITE_OK; +SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugSync(){ + struct MemBlockHdr *pHdr; + for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){ + void **pBt = (void**)pHdr; + pBt -= pHdr->nBacktraceSlots; + mem.xBacktrace((int)pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]); + } } -static int sqlcipher_cc_fips_status(void *ctx) { - return 0; +/* +** Open the file indicated and write a log of all unfreed memory +** allocations into that log. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugDump(const char *zFilename){ + FILE *out; + struct MemBlockHdr *pHdr; + void **pBt; + int i; + out = fopen(zFilename, "w"); + if( out==0 ){ + fprintf(stderr, "** Unable to output memory debug output log: %s **\n", + zFilename); + return; + } + for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){ + char *z = (char*)pHdr; + z -= pHdr->nBacktraceSlots*sizeof(void*) + pHdr->nTitle; + fprintf(out, "**** %lld bytes at %p from %s ****\n", + pHdr->iSize, &pHdr[1], pHdr->nTitle ? z : "???"); + if( pHdr->nBacktrace ){ + fflush(out); + pBt = (void**)pHdr; + pBt -= pHdr->nBacktraceSlots; + backtrace_symbols_fd(pBt, pHdr->nBacktrace, fileno(out)); + fprintf(out, "\n"); + } + } + fprintf(out, "COUNTS:\n"); + for(i=0; irandom = sqlcipher_cc_random; - p->get_provider_name = sqlcipher_cc_get_provider_name; - p->hmac = sqlcipher_cc_hmac; - p->kdf = sqlcipher_cc_kdf; - p->cipher = sqlcipher_cc_cipher; - p->get_cipher = sqlcipher_cc_get_cipher; - p->get_key_sz = sqlcipher_cc_get_key_sz; - p->get_iv_sz = sqlcipher_cc_get_iv_sz; - p->get_block_sz = sqlcipher_cc_get_block_sz; - p->get_hmac_sz = sqlcipher_cc_get_hmac_sz; - p->ctx_init = sqlcipher_cc_ctx_init; - p->ctx_free = sqlcipher_cc_ctx_free; - p->add_random = sqlcipher_cc_add_random; - p->fips_status = sqlcipher_cc_fips_status; - p->get_provider_version = sqlcipher_cc_get_provider_version; - return SQLITE_OK; +/* +** Return the number of times sqlcipher_sqlite3MemMalloc() has been called. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3MemdebugMallocCount(){ + int i; + int nTotal = 0; + for(i=0; i? */ - - 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */ - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */ - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */ - 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */ - 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */ - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */ - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */ - 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */ +#define MX_SMALL 10 - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ -}; +/* +** Number of freelist hash slots +*/ +#define N_HASH 61 -/* EVIDENCE-OF: R-02982-34736 In order to maintain full backwards -** compatibility for legacy applications, the URI filename capability is -** disabled by default. +/* +** A memory allocation (also called a "chunk") consists of two or +** more blocks where each block is 8 bytes. The first 8 bytes are +** a header that is not returned to the user. ** -** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled -** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options. +** A chunk is two or more blocks that is either checked out or +** free. The first block has format u.hdr. u.hdr.size4x is 4 times the +** size of the allocation in blocks if the allocation is free. +** The u.hdr.size4x&1 bit is true if the chunk is checked out and +** false if the chunk is on the freelist. The u.hdr.size4x&2 bit +** is true if the previous chunk is checked out and false if the +** previous chunk is free. The u.hdr.prevSize field is the size of +** the previous chunk in blocks if the previous chunk is on the +** freelist. If the previous chunk is checked out, then +** u.hdr.prevSize can be part of the data for that chunk and should +** not be read or written. ** -** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally -** disabled. The default value may be changed by compiling with the -** SQLITE_USE_URI symbol defined. +** We often identify a chunk by its index in mem3.aPool[]. When +** this is done, the chunk index refers to the second block of +** the chunk. In this way, the first chunk has an index of 1. +** A chunk index of 0 means "no such chunk" and is the equivalent +** of a NULL pointer. ** -** URI filenames are enabled by default if SQLITE_HAS_CODEC is -** enabled. +** The second block of free chunks is of the form u.list. The +** two fields form a double-linked list of chunks of related sizes. +** Pointers to the head of the list are stored in mem3.aiSmall[] +** for smaller chunks and mem3.aiHash[] for larger chunks. +** +** The second block of a chunk is user data if the chunk is checked +** out. If a chunk is checked out, the user data may extend into +** the u.hdr.prevSize value of the following chunk. */ -#ifndef SQLITE_USE_URI -/* BEGIN SQLCIPHER */ -# ifdef SQLITE_HAS_CODEC -# define SQLITE_USE_URI 1 -# else -# define SQLITE_USE_URI 0 -# endif -/* END SQLCIPHER */ -#endif +typedef struct Mem3Block Mem3Block; +struct Mem3Block { + union { + struct { + u32 prevSize; /* Size of previous chunk in Mem3Block elements */ + u32 size4x; /* 4x the size of current chunk in Mem3Block elements */ + } hdr; + struct { + u32 next; /* Index in mem3.aPool[] of next free chunk */ + u32 prev; /* Index in mem3.aPool[] of previous free chunk */ + } list; + } u; +}; -/* EVIDENCE-OF: R-38720-18127 The default setting is determined by the -** SQLITE_ALLOW_COVERING_INDEX_SCAN compile-time option, or is "on" if -** that compile-time option is omitted. +/* +** All of the static variables used by this module are collected +** into a single structure named "mem3". This is to keep the +** static variables organized and to reduce namespace pollution +** when this module is combined with other in the amalgamation. */ -#if !defined(SQLITE_ALLOW_COVERING_INDEX_SCAN) -# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 -#else -# if !SQLITE_ALLOW_COVERING_INDEX_SCAN -# error "Compile-time disabling of covering index scan using the\ - -DSQLITE_ALLOW_COVERING_INDEX_SCAN=0 option is deprecated.\ - Contact SQLite developers if this is a problem for you, and\ - delete this #error macro to continue with your build." -# endif -#endif +static SQLITE_WSD struct Mem3Global { + /* + ** Memory available for allocation. nPool is the size of the array + ** (in Mem3Blocks) pointed to by aPool less 2. + */ + u32 nPool; + Mem3Block *aPool; -/* The minimum PMA size is set to this value multiplied by the database -** page size in bytes. -*/ -#ifndef SQLITE_SORTER_PMASZ -# define SQLITE_SORTER_PMASZ 250 -#endif + /* + ** True if we are evaluating an out-of-memory callback. + */ + int alarmBusy; -/* Statement journals spill to disk when their size exceeds the following -** threshold (in bytes). 0 means that statement journals are created and -** written to disk immediately (the default behavior for SQLite versions -** before 3.12.0). -1 means always keep the entire statement journal in -** memory. (The statement journal is also always held entirely in memory -** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this -** setting.) -*/ -#ifndef SQLITE_STMTJRNL_SPILL -# define SQLITE_STMTJRNL_SPILL (64*1024) -#endif + /* + ** Mutex to control access to the memory allocation subsystem. + */ + sqlcipher_sqlite3_mutex *mutex; -/* -** The default lookaside-configuration, the format "SZ,N". SZ is the -** number of bytes in each lookaside slot (should be a multiple of 8) -** and N is the number of slots. The lookaside-configuration can be -** changed as start-time using sqlcipher_sqlite3_config(SQLITE_CONFIG_LOOKASIDE) -** or at run-time for an individual database connection using -** sqlcipher_sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE); -** -** With the two-size-lookaside enhancement, less lookaside is required. -** The default configuration of 1200,40 actually provides 30 1200-byte slots -** and 93 128-byte slots, which is more lookaside than is available -** using the older 1200,100 configuration without two-size-lookaside. -*/ -#ifndef SQLITE_DEFAULT_LOOKASIDE -# ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE -# define SQLITE_DEFAULT_LOOKASIDE 1200,100 /* 120KB of memory */ -# else -# define SQLITE_DEFAULT_LOOKASIDE 1200,40 /* 48KB of memory */ -# endif -#endif + /* + ** The minimum amount of free space that we have seen. + */ + u32 mnKeyBlk; + /* + ** iKeyBlk is the index of the key chunk. Most new allocations + ** occur off of this chunk. szKeyBlk is the size (in Mem3Blocks) + ** of the current key chunk. iKeyBlk is 0 if there is no key chunk. + ** The key chunk is not in either the aiHash[] or aiSmall[]. + */ + u32 iKeyBlk; + u32 szKeyBlk; -/* The default maximum size of an in-memory database created using -** sqlcipher_sqlite3_deserialize() -*/ -#ifndef SQLITE_MEMDB_DEFAULT_MAXSIZE -# define SQLITE_MEMDB_DEFAULT_MAXSIZE 1073741824 -#endif + /* + ** Array of lists of free blocks according to the block size + ** for smaller chunks, or a hash on the block size for larger + ** chunks. + */ + u32 aiSmall[MX_SMALL-1]; /* For sizes 2 through MX_SMALL, inclusive */ + u32 aiHash[N_HASH]; /* For sizes MX_SMALL+1 and larger */ +} mem3 = { 97535575 }; + +#define mem3 GLOBAL(struct Mem3Global, mem3) /* -** The following singleton contains the global configuration for -** the SQLite library. +** Unlink the chunk at mem3.aPool[i] from list it is currently +** on. *pRoot is the list that i is a member of. */ -SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlcipher_sqlite3Config = { - SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ - 1, /* bCoreMutex */ - SQLITE_THREADSAFE==1, /* bFullMutex */ - SQLITE_USE_URI, /* bOpenUri */ - SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ - 0, /* bSmallMalloc */ - 1, /* bExtraSchemaChecks */ - 0x7ffffffe, /* mxStrlen */ - 0, /* neverCorrupt */ - SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ - SQLITE_STMTJRNL_SPILL, /* nStmtSpill */ - {0,0,0,0,0,0,0,0}, /* m */ - {0,0,0,0,0,0,0,0,0}, /* mutex */ - {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ - (void*)0, /* pHeap */ - 0, /* nHeap */ - 0, 0, /* mnHeap, mxHeap */ - SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */ - SQLITE_MAX_MMAP_SIZE, /* mxMmap */ - (void*)0, /* pPage */ - 0, /* szPage */ - SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */ - 0, /* mxParserStack */ - 0, /* sharedCacheEnabled */ - SQLITE_SORTER_PMASZ, /* szPma */ - /* All the rest should always be initialized to zero */ - 0, /* isInit */ - 0, /* inProgress */ - 0, /* isMutexInit */ - 0, /* isMallocInit */ - 0, /* isPCacheInit */ - 0, /* nRefInitMutex */ - 0, /* pInitMutex */ - 0, /* xLog */ - 0, /* pLogArg */ -#ifdef SQLITE_ENABLE_SQLLOG - 0, /* xSqllog */ - 0, /* pSqllogArg */ -#endif -#ifdef SQLITE_VDBE_COVERAGE - 0, /* xVdbeBranch */ - 0, /* pVbeBranchArg */ -#endif -#ifdef SQLITE_ENABLE_DESERIALIZE - SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ -#endif -#ifndef SQLITE_UNTESTABLE - 0, /* xTestCallback */ -#endif - 0, /* bLocaltimeFault */ - 0x7ffffffe, /* iOnceResetThreshold */ - SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ - 0, /* iPrngSeed */ -}; +static void memsys3UnlinkFromList(u32 i, u32 *pRoot){ + u32 next = mem3.aPool[i].u.list.next; + u32 prev = mem3.aPool[i].u.list.prev; + assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); + if( prev==0 ){ + *pRoot = next; + }else{ + mem3.aPool[prev].u.list.next = next; + } + if( next ){ + mem3.aPool[next].u.list.prev = prev; + } + mem3.aPool[i].u.list.next = 0; + mem3.aPool[i].u.list.prev = 0; +} /* -** Hash table for global functions - functions common to all -** database connections. After initialization, this table is -** read-only. +** Unlink the chunk at index i from +** whatever list is currently a member of. */ -SQLITE_PRIVATE FuncDefHash sqlcipher_sqlite3BuiltinFunctions; +static void memsys3Unlink(u32 i){ + u32 size, hash; + assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); + assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 ); + assert( i>=1 ); + size = mem3.aPool[i-1].u.hdr.size4x/4; + assert( size==mem3.aPool[i+size-1].u.hdr.prevSize ); + assert( size>=2 ); + if( size <= MX_SMALL ){ + memsys3UnlinkFromList(i, &mem3.aiSmall[size-2]); + }else{ + hash = size % N_HASH; + memsys3UnlinkFromList(i, &mem3.aiHash[hash]); + } +} -#ifdef VDBE_PROFILE /* -** The following performance counter can be used in place of -** sqlcipher_sqlite3Hwtime() for profiling. This is a no-op on standard builds. +** Link the chunk at mem3.aPool[i] so that is on the list rooted +** at *pRoot. */ -SQLITE_PRIVATE sqlcipher_sqlite3_uint64 sqlcipher_sqlite3NProfileCnt = 0; -#endif +static void memsys3LinkIntoList(u32 i, u32 *pRoot){ + assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); + mem3.aPool[i].u.list.next = *pRoot; + mem3.aPool[i].u.list.prev = 0; + if( *pRoot ){ + mem3.aPool[*pRoot].u.list.prev = i; + } + *pRoot = i; +} /* -** The value of the "pending" byte must be 0x40000000 (1 byte past the -** 1-gibabyte boundary) in a compatible database. SQLite never uses -** the database page that contains the pending byte. It never attempts -** to read or write that page. The pending byte page is set aside -** for use by the VFS layers as space for managing file locks. -** -** During testing, it is often desirable to move the pending byte to -** a different position in the file. This allows code that has to -** deal with the pending byte to run on files that are much smaller -** than 1 GiB. The sqlcipher_sqlite3_test_control() interface can be used to -** move the pending byte. -** -** IMPORTANT: Changing the pending byte to any value other than -** 0x40000000 results in an incompatible database file format! -** Changing the pending byte during operation will result in undefined -** and incorrect behavior. +** Link the chunk at index i into either the appropriate +** small chunk list, or into the large chunk hash table. */ -#ifndef SQLITE_OMIT_WSD -SQLITE_PRIVATE int sqlcipher_sqlite3PendingByte = 0x40000000; -#endif +static void memsys3Link(u32 i){ + u32 size, hash; + assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); + assert( i>=1 ); + assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 ); + size = mem3.aPool[i-1].u.hdr.size4x/4; + assert( size==mem3.aPool[i+size-1].u.hdr.prevSize ); + assert( size>=2 ); + if( size <= MX_SMALL ){ + memsys3LinkIntoList(i, &mem3.aiSmall[size-2]); + }else{ + hash = size % N_HASH; + memsys3LinkIntoList(i, &mem3.aiHash[hash]); + } +} /* -** Flags for select tracing and the ".selecttrace" macro of the CLI +** If the STATIC_MEM mutex is not already held, obtain it now. The mutex +** will already be held (obtained by code in malloc.c) if +** sqlcipher_sqlite3GlobalConfig.bMemStat is true. */ -SQLITE_API u32 sqlcipher_sqlite3_unsupported_selecttrace = 0; +static void memsys3Enter(void){ + if( sqlcipher_sqlite3GlobalConfig.bMemstat==0 && mem3.mutex==0 ){ + mem3.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); + } + sqlcipher_sqlite3_mutex_enter(mem3.mutex); +} +static void memsys3Leave(void){ + sqlcipher_sqlite3_mutex_leave(mem3.mutex); +} -/* #include "opcodes.h" */ /* -** Properties of opcodes. The OPFLG_INITIALIZER macro is -** created by mkopcodeh.awk during compilation. Data is obtained -** from the comments following the "case OP_xxxx:" statements in -** the vdbe.c file. +** Called when we are unable to satisfy an allocation of nBytes. */ -SQLITE_PRIVATE const unsigned char sqlcipher_sqlite3OpcodeProperty[] = OPFLG_INITIALIZER; +static void memsys3OutOfMemory(int nByte){ + if( !mem3.alarmBusy ){ + mem3.alarmBusy = 1; + assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); + sqlcipher_sqlite3_mutex_leave(mem3.mutex); + sqlcipher_sqlite3_release_memory(nByte); + sqlcipher_sqlite3_mutex_enter(mem3.mutex); + mem3.alarmBusy = 0; + } +} + /* -** Name of the default collating sequence +** Chunk i is a free chunk that has been unlinked. Adjust its +** size parameters for check-out and return a pointer to the +** user portion of the chunk. */ -SQLITE_PRIVATE const char sqlcipher_sqlite3StrBINARY[] = "BINARY"; +static void *memsys3Checkout(u32 i, u32 nBlock){ + u32 x; + assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); + assert( i>=1 ); + assert( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ); + assert( mem3.aPool[i+nBlock-1].u.hdr.prevSize==nBlock ); + x = mem3.aPool[i-1].u.hdr.size4x; + mem3.aPool[i-1].u.hdr.size4x = nBlock*4 | 1 | (x&2); + mem3.aPool[i+nBlock-1].u.hdr.prevSize = nBlock; + mem3.aPool[i+nBlock-1].u.hdr.size4x |= 2; + return &mem3.aPool[i]; +} -/************** End of global.c **********************************************/ -/************** Begin file status.c ******************************************/ /* -** 2008 June 18 -** -** 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 module implements the sqlcipher_sqlite3_status() interface and related -** functionality. +** Carve a piece off of the end of the mem3.iKeyBlk free chunk. +** Return a pointer to the new allocation. Or, if the key chunk +** is not large enough, return 0. */ -/* #include "sqliteInt.h" */ -/************** Include vdbeInt.h in the middle of status.c ******************/ -/************** Begin file vdbeInt.h *****************************************/ +static void *memsys3FromKeyBlk(u32 nBlock){ + assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); + assert( mem3.szKeyBlk>=nBlock ); + if( nBlock>=mem3.szKeyBlk-1 ){ + /* Use the entire key chunk */ + void *p = memsys3Checkout(mem3.iKeyBlk, mem3.szKeyBlk); + mem3.iKeyBlk = 0; + mem3.szKeyBlk = 0; + mem3.mnKeyBlk = 0; + return p; + }else{ + /* Split the key block. Return the tail. */ + u32 newi, x; + newi = mem3.iKeyBlk + mem3.szKeyBlk - nBlock; + assert( newi > mem3.iKeyBlk+1 ); + mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = nBlock; + mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x |= 2; + mem3.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1; + mem3.szKeyBlk -= nBlock; + mem3.aPool[newi-1].u.hdr.prevSize = mem3.szKeyBlk; + x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2; + mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x; + if( mem3.szKeyBlk < mem3.mnKeyBlk ){ + mem3.mnKeyBlk = mem3.szKeyBlk; + } + return (void*)&mem3.aPool[newi]; + } +} + /* -** 2003 September 6 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** *pRoot is the head of a list of free chunks of the same size +** or same size hash. In other words, *pRoot is an entry in either +** mem3.aiSmall[] or mem3.aiHash[]. ** -** 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 routine examines all entries on the given list and tries +** to coalesce each entries with adjacent free chunks. ** -************************************************************************* -** This is the header file for information that is private to the -** VDBE. This information used to all be at the top of the single -** source code file "vdbe.c". When that file became too big (over -** 6000 lines long) it was split up into several smaller files and -** this header information was factored out. +** If it sees a chunk that is larger than mem3.iKeyBlk, it replaces +** the current mem3.iKeyBlk with the new larger chunk. In order for +** this mem3.iKeyBlk replacement to work, the key chunk must be +** linked into the hash tables. That is not the normal state of +** affairs, of course. The calling routine must link the key +** chunk before invoking this routine, then must unlink the (possibly +** changed) key chunk once this routine has finished. */ -#ifndef SQLITE_VDBEINT_H -#define SQLITE_VDBEINT_H +static void memsys3Merge(u32 *pRoot){ + u32 iNext, prev, size, i, x; -/* -** The maximum number of times that a statement will try to reparse -** itself before giving up and returning SQLITE_SCHEMA. -*/ -#ifndef SQLITE_MAX_SCHEMA_RETRY -# define SQLITE_MAX_SCHEMA_RETRY 50 -#endif + assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); + for(i=*pRoot; i>0; i=iNext){ + iNext = mem3.aPool[i].u.list.next; + size = mem3.aPool[i-1].u.hdr.size4x; + assert( (size&1)==0 ); + if( (size&2)==0 ){ + memsys3UnlinkFromList(i, pRoot); + assert( i > mem3.aPool[i-1].u.hdr.prevSize ); + prev = i - mem3.aPool[i-1].u.hdr.prevSize; + if( prev==iNext ){ + iNext = mem3.aPool[prev].u.list.next; + } + memsys3Unlink(prev); + size = i + size/4 - prev; + x = mem3.aPool[prev-1].u.hdr.size4x & 2; + mem3.aPool[prev-1].u.hdr.size4x = size*4 | x; + mem3.aPool[prev+size-1].u.hdr.prevSize = size; + memsys3Link(prev); + i = prev; + }else{ + size /= 4; + } + if( size>mem3.szKeyBlk ){ + mem3.iKeyBlk = i; + mem3.szKeyBlk = size; + } + } +} /* -** VDBE_DISPLAY_P4 is true or false depending on whether or not the -** "explain" P4 display logic is enabled. +** Return a block of memory of at least nBytes in size. +** Return NULL if unable. +** +** This function assumes that the necessary mutexes, if any, are +** already held by the caller. Hence "Unsafe". */ -#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ - || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \ - || defined(SQLITE_ENABLE_BYTECODE_VTAB) -# define VDBE_DISPLAY_P4 1 -#else -# define VDBE_DISPLAY_P4 0 -#endif +static void *memsys3MallocUnsafe(int nByte){ + u32 i; + u32 nBlock; + u32 toFree; -/* -** SQL is translated into a sequence of instructions to be -** executed by a virtual machine. Each instruction is an instance -** of the following structure. -*/ -typedef struct VdbeOp Op; + assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); + assert( sizeof(Mem3Block)==8 ); + if( nByte<=12 ){ + nBlock = 2; + }else{ + nBlock = (nByte + 11)/8; + } + assert( nBlock>=2 ); -/* -** Boolean values -*/ -typedef unsigned Bool; + /* STEP 1: + ** Look for an entry of the correct size in either the small + ** chunk table or in the large chunk hash table. This is + ** successful most of the time (about 9 times out of 10). + */ + if( nBlock <= MX_SMALL ){ + i = mem3.aiSmall[nBlock-2]; + if( i>0 ){ + memsys3UnlinkFromList(i, &mem3.aiSmall[nBlock-2]); + return memsys3Checkout(i, nBlock); + } + }else{ + int hash = nBlock % N_HASH; + for(i=mem3.aiHash[hash]; i>0; i=mem3.aPool[i].u.list.next){ + if( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ){ + memsys3UnlinkFromList(i, &mem3.aiHash[hash]); + return memsys3Checkout(i, nBlock); + } + } + } -/* Opaque type used by code in vdbesort.c */ -typedef struct VdbeSorter VdbeSorter; + /* STEP 2: + ** Try to satisfy the allocation by carving a piece off of the end + ** of the key chunk. This step usually works if step 1 fails. + */ + if( mem3.szKeyBlk>=nBlock ){ + return memsys3FromKeyBlk(nBlock); + } -/* Elements of the linked list at Vdbe.pAuxData */ -typedef struct AuxData AuxData; -/* Types of VDBE cursors */ -#define CURTYPE_BTREE 0 -#define CURTYPE_SORTER 1 -#define CURTYPE_VTAB 2 -#define CURTYPE_PSEUDO 3 + /* STEP 3: + ** Loop through the entire memory pool. Coalesce adjacent free + ** chunks. Recompute the key chunk as the largest free chunk. + ** Then try again to satisfy the allocation by carving a piece off + ** of the end of the key chunk. This step happens very + ** rarely (we hope!) + */ + for(toFree=nBlock*16; toFree<(mem3.nPool*16); toFree *= 2){ + memsys3OutOfMemory(toFree); + if( mem3.iKeyBlk ){ + memsys3Link(mem3.iKeyBlk); + mem3.iKeyBlk = 0; + mem3.szKeyBlk = 0; + } + for(i=0; i=nBlock ){ + return memsys3FromKeyBlk(nBlock); + } + } + } + + /* If none of the above worked, then we fail. */ + return 0; +} /* -** A VdbeCursor is an superclass (a wrapper) for various cursor objects: +** Free an outstanding memory allocation. ** -** * A b-tree cursor -** - In the main database or in an ephemeral database -** - On either an index or a table -** * A sorter -** * A virtual table -** * A one-row "pseudotable" stored in a single register +** This function assumes that the necessary mutexes, if any, are +** already held by the caller. Hence "Unsafe". */ -typedef struct VdbeCursor VdbeCursor; -struct VdbeCursor { - u8 eCurType; /* One of the CURTYPE_* values above */ - i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ - u8 nullRow; /* True if pointing to a row with no data */ - u8 deferredMoveto; /* A call to sqlcipher_sqlite3BtreeMoveto() is needed */ - u8 isTable; /* True for rowid tables. False for indexes */ -#ifdef SQLITE_DEBUG - u8 seekOp; /* Most recent seek operation on this cursor */ - u8 wrFlag; /* The wrFlag argument to sqlcipher_sqlite3BtreeCursor() */ -#endif - Bool isEphemeral:1; /* True for an ephemeral table */ - Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ - Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ - u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ - Btree *pBtx; /* Separate file holding temporary table */ - i64 seqCount; /* Sequence counter */ - u32 *aAltMap; /* Mapping from table to index column numbers */ +static void memsys3FreeUnsafe(void *pOld){ + Mem3Block *p = (Mem3Block*)pOld; + int i; + u32 size, x; + assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); + assert( p>mem3.aPool && p<&mem3.aPool[mem3.nPool] ); + i = p - mem3.aPool; + assert( (mem3.aPool[i-1].u.hdr.size4x&1)==1 ); + size = mem3.aPool[i-1].u.hdr.size4x/4; + assert( i+size<=mem3.nPool+1 ); + mem3.aPool[i-1].u.hdr.size4x &= ~1; + mem3.aPool[i+size-1].u.hdr.prevSize = size; + mem3.aPool[i+size-1].u.hdr.size4x &= ~2; + memsys3Link(i); - /* Cached OP_Column parse information is only valid if cacheStatus matches - ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of - ** CACHE_STALE (0) and so setting cacheStatus=CACHE_STALE guarantees that - ** the cache is out of date. */ - u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ - int seekResult; /* Result of previous sqlcipher_sqlite3BtreeMoveto() or 0 - ** if there have been no prior seeks on the cursor. */ - /* seekResult does not distinguish between "no seeks have ever occurred - ** on this cursor" and "the most recent seek was an exact match". - ** For CURTYPE_PSEUDO, seekResult is the register holding the record */ + /* Try to expand the key using the newly freed chunk */ + if( mem3.iKeyBlk ){ + while( (mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x&2)==0 ){ + size = mem3.aPool[mem3.iKeyBlk-1].u.hdr.prevSize; + mem3.iKeyBlk -= size; + mem3.szKeyBlk += size; + memsys3Unlink(mem3.iKeyBlk); + x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2; + mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x; + mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = mem3.szKeyBlk; + } + x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2; + while( (mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x&1)==0 ){ + memsys3Unlink(mem3.iKeyBlk+mem3.szKeyBlk); + mem3.szKeyBlk += mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x/4; + mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x; + mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = mem3.szKeyBlk; + } + } +} - /* When a new VdbeCursor is allocated, only the fields above are zeroed. - ** The fields that follow are uninitialized, and must be individually - ** initialized prior to first use. */ - VdbeCursor *pAltCursor; /* Associated index cursor from which to read */ - union { - BtCursor *pCursor; /* CURTYPE_BTREE or _PSEUDO. Btree cursor */ - sqlcipher_sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */ - VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */ - } uc; - KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ - u32 iHdrOffset; /* Offset to next unparsed byte of the header */ - Pgno pgnoRoot; /* Root page of the open btree cursor */ - i16 nField; /* Number of fields in the header */ - u16 nHdrParsed; /* Number of header fields parsed so far */ - i64 movetoTarget; /* Argument to the deferred sqlcipher_sqlite3BtreeMoveto() */ - u32 *aOffset; /* Pointer to aType[nField] */ - const u8 *aRow; /* Data for the current row, if all on one page */ - u32 payloadSize; /* Total number of bytes in the record */ - u32 szRow; /* Byte available in aRow */ -#ifdef SQLITE_ENABLE_COLUMN_USED_MASK - u64 maskUsed; /* Mask of columns used by this cursor */ -#endif +/* +** Return the size of an outstanding allocation, in bytes. The +** size returned omits the 8-byte header overhead. This only +** works for chunks that are currently checked out. +*/ +static int memsys3Size(void *p){ + Mem3Block *pBlock; + assert( p!=0 ); + pBlock = (Mem3Block*)p; + assert( (pBlock[-1].u.hdr.size4x&1)!=0 ); + return (pBlock[-1].u.hdr.size4x&~3)*2 - 4; +} - /* 2*nField extra array elements allocated for aType[], beyond the one - ** static element declared in the structure. nField total array slots for - ** aType[] and nField+1 array slots for aOffset[] */ - u32 aType[1]; /* Type values record decode. MUST BE LAST */ -}; +/* +** Round up a request size to the next valid allocation size. +*/ +static int memsys3Roundup(int n){ + if( n<=12 ){ + return 12; + }else{ + return ((n+11)&~7) - 4; + } +} +/* +** Allocate nBytes of memory. +*/ +static void *memsys3Malloc(int nBytes){ + sqlcipher_sqlite3_int64 *p; + assert( nBytes>0 ); /* malloc.c filters out 0 byte requests */ + memsys3Enter(); + p = memsys3MallocUnsafe(nBytes); + memsys3Leave(); + return (void*)p; +} /* -** A value for VdbeCursor.cacheStatus that means the cache is always invalid. +** Free memory. */ -#define CACHE_STALE 0 +static void memsys3Free(void *pPrior){ + assert( pPrior ); + memsys3Enter(); + memsys3FreeUnsafe(pPrior); + memsys3Leave(); +} /* -** When a sub-program is executed (OP_Program), a structure of this type -** is allocated to store the current value of the program counter, as -** well as the current memory cell array and various other frame specific -** values stored in the Vdbe struct. When the sub-program is finished, -** these values are copied back to the Vdbe from the VdbeFrame structure, -** restoring the state of the VM to as it was before the sub-program -** began executing. -** -** The memory for a VdbeFrame object is allocated and managed by a memory -** cell in the parent (calling) frame. When the memory cell is deleted or -** overwritten, the VdbeFrame object is not freed immediately. Instead, it -** is linked into the Vdbe.pDelFrame list. The contents of the Vdbe.pDelFrame -** list is deleted when the VM is reset in VdbeHalt(). The reason for doing -** this instead of deleting the VdbeFrame immediately is to avoid recursive -** calls to sqlcipher_sqlite3VdbeMemRelease() when the memory cells belonging to the -** child frame are released. -** -** The currently executing frame is stored in Vdbe.pFrame. Vdbe.pFrame is -** set to NULL if the currently executing frame is the main program. +** Change the size of an existing memory allocation */ -typedef struct VdbeFrame VdbeFrame; -struct VdbeFrame { - Vdbe *v; /* VM this frame belongs to */ - VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ - Op *aOp; /* Program instructions for parent frame */ - i64 *anExec; /* Event counters from parent frame */ - Mem *aMem; /* Array of memory cells for parent frame */ - VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ - u8 *aOnce; /* Bitmask used by OP_Once */ - void *token; /* Copy of SubProgram.token */ - i64 lastRowid; /* Last insert rowid (sqlcipher_sqlite3.lastRowid) */ - AuxData *pAuxData; /* Linked list of auxdata allocations */ -#if SQLITE_DEBUG - u32 iFrameMagic; /* magic number for sanity checking */ -#endif - int nCursor; /* Number of entries in apCsr */ - int pc; /* Program Counter in parent (calling) frame */ - int nOp; /* Size of aOp array */ - int nMem; /* Number of entries in aMem */ - int nChildMem; /* Number of memory cells for child frame */ - int nChildCsr; /* Number of cursors for child frame */ - int nChange; /* Statement changes (Vdbe.nChange) */ - int nDbChange; /* Value of db->nChange */ -}; +static void *memsys3Realloc(void *pPrior, int nBytes){ + int nOld; + void *p; + if( pPrior==0 ){ + return sqlcipher_sqlite3_malloc(nBytes); + } + if( nBytes<=0 ){ + sqlcipher_sqlite3_free(pPrior); + return 0; + } + nOld = memsys3Size(pPrior); + if( nBytes<=nOld && nBytes>=nOld-128 ){ + return pPrior; + } + memsys3Enter(); + p = memsys3MallocUnsafe(nBytes); + if( p ){ + if( nOld0 */ - int szMalloc; /* Size of the zMalloc allocation */ - u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */ - sqlcipher_sqlite3 *db; /* The associated database connection */ - void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */ +SQLITE_PRIVATE void sqlcipher_sqlite3Memsys3Dump(const char *zFilename){ #ifdef SQLITE_DEBUG - Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ - u16 mScopyFlags; /* flags value immediately after the shallow copy */ + FILE *out; + u32 i, j; + u32 size; + if( zFilename==0 || zFilename[0]==0 ){ + out = stdout; + }else{ + out = fopen(zFilename, "w"); + if( out==0 ){ + fprintf(stderr, "** Unable to output memory debug output log: %s **\n", + zFilename); + return; + } + } + memsys3Enter(); + fprintf(out, "CHUNKS:\n"); + for(i=1; i<=mem3.nPool; i+=size/4){ + size = mem3.aPool[i-1].u.hdr.size4x; + if( size/4<=1 ){ + fprintf(out, "%p size error\n", &mem3.aPool[i]); + assert( 0 ); + break; + } + if( (size&1)==0 && mem3.aPool[i+size/4-1].u.hdr.prevSize!=size/4 ){ + fprintf(out, "%p tail size does not match\n", &mem3.aPool[i]); + assert( 0 ); + break; + } + if( ((mem3.aPool[i+size/4-1].u.hdr.size4x&2)>>1)!=(size&1) ){ + fprintf(out, "%p tail checkout bit is incorrect\n", &mem3.aPool[i]); + assert( 0 ); + break; + } + if( size&1 ){ + fprintf(out, "%p %6d bytes checked out\n", &mem3.aPool[i], (size/4)*8-8); + }else{ + fprintf(out, "%p %6d bytes free%s\n", &mem3.aPool[i], (size/4)*8-8, + i==mem3.iKeyBlk ? " **key**" : ""); + } + } + for(i=0; i0; j=mem3.aPool[j].u.list.next){ + fprintf(out, " %p(%d)", &mem3.aPool[j], + (mem3.aPool[j-1].u.hdr.size4x/4)*8-8); + } + fprintf(out, "\n"); + } + for(i=0; i0; j=mem3.aPool[j].u.list.next){ + fprintf(out, " %p(%d)", &mem3.aPool[j], + (mem3.aPool[j-1].u.hdr.size4x/4)*8-8); + } + fprintf(out, "\n"); + } + fprintf(out, "key=%d\n", mem3.iKeyBlk); + fprintf(out, "nowUsed=%d\n", mem3.nPool*8 - mem3.szKeyBlk*8); + fprintf(out, "mxUsed=%d\n", mem3.nPool*8 - mem3.mnKeyBlk*8); + sqlcipher_sqlite3_mutex_leave(mem3.mutex); + if( out==stdout ){ + fflush(stdout); + }else{ + fclose(out); + } +#else + UNUSED_PARAMETER(zFilename); #endif -}; +} /* -** Size of struct Mem not including the Mem.zMalloc member or anything that -** follows. -*/ -#define MEMCELLSIZE offsetof(Mem,zMalloc) - -/* One or more of the following flags are set to indicate the validOK -** representations of the value stored in the Mem struct. +** This routine is the only routine in this file with external +** linkage. ** -** If the MEM_Null flag is set, then the value is an SQL NULL value. -** For a pointer type created using sqlcipher_sqlite3_bind_pointer() or -** sqlcipher_sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set. +** Populate the low-level memory allocation function pointers in +** sqlcipher_sqlite3GlobalConfig.m with pointers to the routines in this file. The +** arguments specify the block of memory to manage. ** -** If the MEM_Str flag is set then Mem.z points at a string representation. -** Usually this is encoded in the same unicode encoding as the main -** database (see below for exceptions). If the MEM_Term flag is also -** set, then the string is nul terminated. The MEM_Int and MEM_Real -** flags may coexist with the MEM_Str flag. -*/ -#define MEM_Null 0x0001 /* Value is NULL (or a pointer) */ -#define MEM_Str 0x0002 /* Value is a string */ -#define MEM_Int 0x0004 /* Value is an integer */ -#define MEM_Real 0x0008 /* Value is a real number */ -#define MEM_Blob 0x0010 /* Value is a BLOB */ -#define MEM_IntReal 0x0020 /* MEM_Int that stringifies like MEM_Real */ -#define MEM_AffMask 0x003f /* Mask of affinity bits */ -#define MEM_FromBind 0x0040 /* Value originates from sqlcipher_sqlite3_bind() */ -#define MEM_Undefined 0x0080 /* Value is undefined */ -#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ -#define MEM_TypeMask 0xc1bf /* Mask of type bits */ - - -/* Whenever Mem contains a valid string or blob representation, one of -** the following flags must be set to determine the memory management -** policy for Mem.z. The MEM_Term flag tells us whether or not the -** string is \000 or \u0000 terminated -*/ -#define MEM_Term 0x0200 /* String in Mem.z is zero terminated */ -#define MEM_Dyn 0x0400 /* Need to call Mem.xDel() on Mem.z */ -#define MEM_Static 0x0800 /* Mem.z points to a static string */ -#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ -#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ -#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */ -#define MEM_Subtype 0x8000 /* Mem.eSubtype is valid */ -#ifdef SQLITE_OMIT_INCRBLOB - #undef MEM_Zero - #define MEM_Zero 0x0000 -#endif - -/* Return TRUE if Mem X contains dynamically allocated content - anything -** that needs to be deallocated to avoid a leak. -*/ -#define VdbeMemDynamic(X) \ - (((X)->flags&(MEM_Agg|MEM_Dyn))!=0) - -/* -** Clear any existing type flags from a Mem and replace them with f -*/ -#define MemSetTypeFlag(p, f) \ - ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) - -/* -** True if Mem X is a NULL-nochng type. -*/ -#define MemNullNochng(X) \ - (((X)->flags&MEM_TypeMask)==(MEM_Null|MEM_Zero) \ - && (X)->n==0 && (X)->u.nZero==0) - -/* -** Return true if a memory cell is not marked as invalid. This macro -** is for use inside assert() statements only. +** This routine is only called by sqlcipher_sqlite3_config(), and therefore +** is not required to be threadsafe (it is not). */ -#ifdef SQLITE_DEBUG -#define memIsValid(M) ((M)->flags & MEM_Undefined)==0 -#endif +SQLITE_PRIVATE const sqlcipher_sqlite3_mem_methods *sqlcipher_sqlite3MemGetMemsys3(void){ + static const sqlcipher_sqlite3_mem_methods mempoolMethods = { + memsys3Malloc, + memsys3Free, + memsys3Realloc, + memsys3Size, + memsys3Roundup, + memsys3Init, + memsys3Shutdown, + 0 + }; + return &mempoolMethods; +} -/* -** Each auxiliary data pointer stored by a user defined function -** implementation calling sqlcipher_sqlite3_set_auxdata() is stored in an instance -** of this structure. All such structures associated with a single VM -** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed -** when the VM is halted (if not before). -*/ -struct AuxData { - int iAuxOp; /* Instruction number of OP_Function opcode */ - int iAuxArg; /* Index of function argument. */ - void *pAux; /* Aux data pointer */ - void (*xDeleteAux)(void*); /* Destructor for the aux data */ - AuxData *pNextAux; /* Next element in list */ -}; +#endif /* SQLITE_ENABLE_MEMSYS3 */ +/************** End of mem3.c ************************************************/ +/************** Begin file mem5.c ********************************************/ /* -** The "context" argument for an installable function. A pointer to an -** instance of this structure is the first argument to the routines used -** implement the SQL functions. +** 2007 October 14 ** -** There is a typedef for this structure in sqlite.h. So all routines, -** even the public interface to SQLite, can use a pointer to this structure. -** But this file is the only place where the internal details of this -** structure are known. +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** This structure is defined inside of vdbeInt.h because it uses substructures -** (Mem) which are only defined there. -*/ -struct sqlcipher_sqlite3_context { - Mem *pOut; /* The return value is stored here */ - FuncDef *pFunc; /* Pointer to function information */ - Mem *pMem; /* Memory cell used to store aggregate context */ - Vdbe *pVdbe; /* The VM that owns this context */ - int iOp; /* Instruction number of OP_Function */ - int isError; /* Error code returned by the function. */ - u8 skipFlag; /* Skip accumulator loading if true */ - u8 argc; /* Number of arguments */ - sqlcipher_sqlite3_value *argv[1]; /* Argument set */ -}; - -/* A bitfield type for use inside of structures. Always follow with :N where -** N is the number of bits. -*/ -typedef unsigned bft; /* Bit Field Type */ - -/* The ScanStatus object holds a single value for the -** sqlcipher_sqlite3_stmt_scanstatus() interface. +** 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 the C functions that implement a memory +** allocation subsystem for use by SQLite. +** +** This version of the memory allocation subsystem omits all +** use of malloc(). The application gives SQLite a block of memory +** before calling sqlcipher_sqlite3_initialize() from which allocations +** are made and returned by the xMalloc() and xRealloc() +** implementations. Once sqlcipher_sqlite3_initialize() has been called, +** the amount of memory available to SQLite is fixed and cannot +** be changed. +** +** This version of the memory allocation subsystem is included +** in the build only if SQLITE_ENABLE_MEMSYS5 is defined. +** +** This memory allocator uses the following algorithm: +** +** 1. All memory allocation sizes are rounded up to a power of 2. +** +** 2. If two adjacent free blocks are the halves of a larger block, +** then the two blocks are coalesced into the single larger block. +** +** 3. New memory is allocated from the first available free block. +** +** This algorithm is described in: J. M. Robson. "Bounds for Some Functions +** Concerning Dynamic Storage Allocation". Journal of the Association for +** Computing Machinery, Volume 21, Number 8, July 1974, pages 491-499. +** +** Let n be the size of the largest allocation divided by the minimum +** allocation size (after rounding all sizes up to a power of 2.) Let M +** be the maximum amount of memory ever outstanding at one time. Let +** N be the total amount of memory available for allocation. Robson +** proved that this memory allocator will never breakdown due to +** fragmentation as long as the following constraint holds: +** +** N >= M*(1 + log2(n)/2) - n + 1 +** +** The sqlcipher_sqlite3_status() logic tracks the maximum values of n and M so +** that an application can, at any time, verify this constraint. */ -typedef struct ScanStatus ScanStatus; -struct ScanStatus { - int addrExplain; /* OP_Explain for loop */ - int addrLoop; /* Address of "loops" counter */ - int addrVisit; /* Address of "rows visited" counter */ - int iSelectID; /* The "Select-ID" for this loop */ - LogEst nEst; /* Estimated output rows per loop */ - char *zName; /* Name of table or index */ -}; +/* #include "sqliteInt.h" */ -/* The DblquoteStr object holds the text of a double-quoted -** string for a prepared statement. A linked list of these objects -** is constructed during statement parsing and is held on Vdbe.pDblStr. -** When computing a normalized SQL statement for an SQL statement, that -** list is consulted for each double-quoted identifier to see if the -** identifier should really be a string literal. +/* +** This version of the memory allocator is used only when +** SQLITE_ENABLE_MEMSYS5 is defined. */ -typedef struct DblquoteStr DblquoteStr; -struct DblquoteStr { - DblquoteStr *pNextStr; /* Next string literal in the list */ - char z[8]; /* Dequoted value for the string */ -}; +#ifdef SQLITE_ENABLE_MEMSYS5 /* -** An instance of the virtual machine. This structure contains the complete -** state of the virtual machine. +** A minimum allocation is an instance of the following structure. +** Larger allocations are an array of these structures where the +** size of the array is a power of 2. ** -** The "sqlcipher_sqlite3_stmt" structure pointer that is returned by sqlcipher_sqlite3_prepare() -** is really a pointer to an instance of this structure. +** The size of this object must be a power of two. That fact is +** verified in memsys5Init(). */ -struct Vdbe { - sqlcipher_sqlite3 *db; /* The database connection that owns this statement */ - Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ - Parse *pParse; /* Parsing context used to create this Vdbe */ - ynVar nVar; /* Number of entries in aVar[] */ - u32 magic; /* Magic number for sanity checking */ - int nMem; /* Number of memory locations currently allocated */ - int nCursor; /* Number of slots in apCsr[] */ - u32 cacheCtr; /* VdbeCursor row cache generation counter */ - int pc; /* The program counter */ - int rc; /* Value to return */ - int nChange; /* Number of db changes made since last reset */ - int iStatement; /* Statement number (or 0 if has no opened stmt) */ - i64 iCurrentTime; /* Value of julianday('now') for this statement */ - i64 nFkConstraint; /* Number of imm. FK constraints this VM */ - i64 nStmtDefCons; /* Number of def. constraints when stmt started */ - i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */ - Mem *aMem; /* The memory locations */ - Mem **apArg; /* Arguments to currently executing user function */ - VdbeCursor **apCsr; /* One element of this array for each open cursor */ - Mem *aVar; /* Values for the OP_Variable opcode. */ - - /* When allocating a new Vdbe object, all of the fields below should be - ** initialized to zero or NULL */ - - Op *aOp; /* Space to hold the virtual machine's program */ - int nOp; /* Number of instructions in the program */ - int nOpAlloc; /* Slots allocated for aOp[] */ - Mem *aColName; /* Column names to return */ - Mem *pResultSet; /* Pointer to an array of results */ - char *zErrMsg; /* Error message written here */ - VList *pVList; /* Name of variables */ -#ifndef SQLITE_OMIT_TRACE - i64 startTime; /* Time when query started - used for profiling */ -#endif -#ifdef SQLITE_DEBUG - int rcApp; /* errcode set by sqlcipher_sqlite3_result_error_code() */ - u32 nWrite; /* Number of write operations that have occurred */ -#endif - u16 nResColumn; /* Number of columns in one row of the result set */ - u8 errorAction; /* Recovery action to do in case of an error */ - u8 minWriteFileFormat; /* Minimum file format for writable database files */ - u8 prepFlags; /* SQLITE_PREPARE_* flags */ - u8 doingRerun; /* True if rerunning after an auto-reprepare */ - bft expired:2; /* 1: recompile VM immediately 2: when convenient */ - bft explain:2; /* True if EXPLAIN present on SQL command */ - bft changeCntOn:1; /* True to update the change-counter */ - bft runOnlyOnce:1; /* Automatically expire on reset */ - bft usesStmtJournal:1; /* True if uses a statement journal */ - bft readOnly:1; /* True for statements that do not write */ - bft bIsReader:1; /* True for statements that read */ - yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ - yDbMask lockMask; /* Subset of btreeMask that requires a lock */ - u32 aCounter[7]; /* Counters used by sqlcipher_sqlite3_stmt_status() */ - char *zSql; /* Text of the SQL statement that generated this */ -#ifdef SQLITE_ENABLE_NORMALIZE - char *zNormSql; /* Normalization of the associated SQL statement */ - DblquoteStr *pDblStr; /* List of double-quoted string literals */ -#endif - void *pFree; /* Free this when deleting the vdbe */ - VdbeFrame *pFrame; /* Parent frame */ - VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */ - int nFrame; /* Number of frames in pFrame list */ - u32 expmask; /* Binding to these vars invalidates VM */ - SubProgram *pProgram; /* Linked list of all sub-programs used by VM */ - AuxData *pAuxData; /* Linked list of auxdata allocations */ -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - i64 *anExec; /* Number of times each op has been executed */ - int nScan; /* Entries in aScan[] */ - ScanStatus *aScan; /* Scan definitions for sqlcipher_sqlite3_stmt_scanstatus() */ -#endif +typedef struct Mem5Link Mem5Link; +struct Mem5Link { + int next; /* Index of next free chunk */ + int prev; /* Index of previous free chunk */ }; /* -** The following are allowed values for Vdbe.magic +** Maximum size of any allocation is ((1<0 -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeLeave(Vdbe*); -#else -# define sqlcipher_sqlite3VdbeLeave(X) -#endif - -#ifdef SQLITE_DEBUG -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemAboutToChange(Vdbe*,Mem*); -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCheckMemInvariants(Mem*); -#endif +static SQLITE_WSD struct Mem5Global { + /* + ** Memory available for allocation + */ + int szAtom; /* Smallest possible allocation in bytes */ + int nBlock; /* Number of szAtom sized blocks in zPool */ + u8 *zPool; /* Memory available to be allocated */ -#ifndef SQLITE_OMIT_FOREIGN_KEY -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCheckFk(Vdbe *, int); -#else -# define sqlcipher_sqlite3VdbeCheckFk(p,i) 0 -#endif + /* + ** Mutex to control access to the memory allocation subsystem. + */ + sqlcipher_sqlite3_mutex *mutex; -#ifdef SQLITE_DEBUG -SQLITE_PRIVATE void sqlcipher_sqlite3VdbePrintSql(Vdbe*); -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr); -#endif -#ifndef SQLITE_OMIT_UTF16 -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemTranslate(Mem*, u8); -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemHandleBom(Mem *pMem); +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) + /* + ** Performance statistics + */ + u64 nAlloc; /* Total number of calls to malloc */ + u64 totalAlloc; /* Total of all malloc calls - includes internal frag */ + u64 totalExcess; /* Total internal fragmentation */ + u32 currentOut; /* Current checkout, including internal fragmentation */ + u32 currentCount; /* Current number of distinct checkouts */ + u32 maxOut; /* Maximum instantaneous currentOut */ + u32 maxCount; /* Maximum instantaneous currentCount */ + u32 maxRequest; /* Largest allocation (exclusive of internal frag) */ #endif -#ifndef SQLITE_OMIT_INCRBLOB -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemExpandBlob(Mem *); - #define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlcipher_sqlite3VdbeMemExpandBlob(P):0) -#else - #define sqlcipher_sqlite3VdbeMemExpandBlob(x) SQLITE_OK - #define ExpandBlob(P) SQLITE_OK -#endif + /* + ** Lists of free blocks. aiFreelist[0] is a list of free blocks of + ** size mem5.szAtom. aiFreelist[1] holds blocks of size szAtom*2. + ** aiFreelist[2] holds free blocks of size szAtom*4. And so forth. + */ + int aiFreelist[LOGMAX+1]; -#endif /* !defined(SQLITE_VDBEINT_H) */ + /* + ** Space for tracking which blocks are checked out and the size + ** of each block. One byte per block. + */ + u8 *aCtrl; -/************** End of vdbeInt.h *********************************************/ -/************** Continuing where we left off in status.c *********************/ +} mem5; /* -** Variables in which to record status information. +** Access the static variable through a macro for SQLITE_OMIT_WSD. */ -#if SQLITE_PTRSIZE>4 -typedef sqlcipher_sqlite3_int64 sqlcipher_sqlite3StatValueType; -#else -typedef u32 sqlcipher_sqlite3StatValueType; -#endif -typedef struct sqlcipher_sqlite3StatType sqlcipher_sqlite3StatType; -static SQLITE_WSD struct sqlcipher_sqlite3StatType { - sqlcipher_sqlite3StatValueType nowValue[10]; /* Current value */ - sqlcipher_sqlite3StatValueType mxValue[10]; /* Maximum value */ -} sqlcipher_sqlite3Stat = { {0,}, {0,} }; +#define mem5 GLOBAL(struct Mem5Global, mem5) /* -** Elements of sqlcipher_sqlite3Stat[] are protected by either the memory allocator -** mutex, or by the pcache1 mutex. The following array determines which. -*/ -static const char statMutex[] = { - 0, /* SQLITE_STATUS_MEMORY_USED */ - 1, /* SQLITE_STATUS_PAGECACHE_USED */ - 1, /* SQLITE_STATUS_PAGECACHE_OVERFLOW */ - 0, /* SQLITE_STATUS_SCRATCH_USED */ - 0, /* SQLITE_STATUS_SCRATCH_OVERFLOW */ - 0, /* SQLITE_STATUS_MALLOC_SIZE */ - 0, /* SQLITE_STATUS_PARSER_STACK */ - 1, /* SQLITE_STATUS_PAGECACHE_SIZE */ - 0, /* SQLITE_STATUS_SCRATCH_SIZE */ - 0, /* SQLITE_STATUS_MALLOC_COUNT */ -}; - - -/* The "wsdStat" macro will resolve to the status information -** state vector. If writable static data is unsupported on the target, -** we have to locate the state vector at run-time. In the more common -** case where writable static data is supported, wsdStat can refer directly -** to the "sqlcipher_sqlite3Stat" state vector declared above. +** Assuming mem5.zPool is divided up into an array of Mem5Link +** structures, return a pointer to the idx-th such link. */ -#ifdef SQLITE_OMIT_WSD -# define wsdStatInit sqlcipher_sqlite3StatType *x = &GLOBAL(sqlcipher_sqlite3StatType,sqlcipher_sqlite3Stat) -# define wsdStat x[0] -#else -# define wsdStatInit -# define wsdStat sqlcipher_sqlite3Stat -#endif +#define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.szAtom])) /* -** Return the current value of a status parameter. The caller must -** be holding the appropriate mutex. +** Unlink the chunk at mem5.aPool[i] from list it is currently +** on. It should be found on mem5.aiFreelist[iLogsize]. */ -SQLITE_PRIVATE sqlcipher_sqlite3_int64 sqlcipher_sqlite3StatusValue(int op){ - wsdStatInit; - assert( op>=0 && op=0 && op=0 && i=0 && iLogsize<=LOGMAX ); + assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize ); -/* -** Add N to the value of a status record. The caller must hold the -** appropriate mutex. (Locking is checked by assert()). -** -** The StatusUp() routine can accept positive or negative values for N. -** The value of N is added to the current status value and the high-water -** mark is adjusted if necessary. -** -** The StatusDown() routine lowers the current value by N. The highwater -** mark is unchanged. N must be non-negative for StatusDown(). -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3StatusUp(int op, int N){ - wsdStatInit; - assert( op>=0 && op=0 && opwsdStat.mxValue[op] ){ - wsdStat.mxValue[op] = wsdStat.nowValue[op]; + next = MEM5LINK(i)->next; + prev = MEM5LINK(i)->prev; + if( prev<0 ){ + mem5.aiFreelist[iLogsize] = next; + }else{ + MEM5LINK(prev)->next = next; + } + if( next>=0 ){ + MEM5LINK(next)->prev = prev; } -} -SQLITE_PRIVATE void sqlcipher_sqlite3StatusDown(int op, int N){ - wsdStatInit; - assert( N>=0 ); - assert( op>=0 && op=0 && op=0 ); - newValue = (sqlcipher_sqlite3StatValueType)X; - assert( op>=0 && op=0 && opwsdStat.mxValue[op] ){ - wsdStat.mxValue[op] = newValue; +static void memsys5Link(int i, int iLogsize){ + int x; + assert( sqlcipher_sqlite3_mutex_held(mem5.mutex) ); + assert( i>=0 && i=0 && iLogsize<=LOGMAX ); + assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize ); + + x = MEM5LINK(i)->next = mem5.aiFreelist[iLogsize]; + MEM5LINK(i)->prev = -1; + if( x>=0 ){ + assert( xprev = i; } + mem5.aiFreelist[iLogsize] = i; } /* -** Query status information. +** Obtain or release the mutex needed to access global data structures. */ -SQLITE_API int sqlcipher_sqlite3_status64( - int op, - sqlcipher_sqlite3_int64 *pCurrent, - sqlcipher_sqlite3_int64 *pHighwater, - int resetFlag -){ - sqlcipher_sqlite3_mutex *pMutex; - wsdStatInit; - if( op<0 || op>=ArraySize(wsdStat.nowValue) ){ - return SQLITE_MISUSE_BKPT; - } -#ifdef SQLITE_ENABLE_API_ARMOR - if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT; -#endif - pMutex = statMutex[op] ? sqlcipher_sqlite3Pcache1Mutex() : sqlcipher_sqlite3MallocMutex(); - sqlcipher_sqlite3_mutex_enter(pMutex); - *pCurrent = wsdStat.nowValue[op]; - *pHighwater = wsdStat.mxValue[op]; - if( resetFlag ){ - wsdStat.mxValue[op] = wsdStat.nowValue[op]; - } - sqlcipher_sqlite3_mutex_leave(pMutex); - (void)pMutex; /* Prevent warning when SQLITE_THREADSAFE=0 */ - return SQLITE_OK; +static void memsys5Enter(void){ + sqlcipher_sqlite3_mutex_enter(mem5.mutex); } -SQLITE_API int sqlcipher_sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){ - sqlcipher_sqlite3_int64 iCur = 0, iHwtr = 0; - int rc; -#ifdef SQLITE_ENABLE_API_ARMOR - if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT; -#endif - rc = sqlcipher_sqlite3_status64(op, &iCur, &iHwtr, resetFlag); - if( rc==0 ){ - *pCurrent = (int)iCur; - *pHighwater = (int)iHwtr; - } - return rc; +static void memsys5Leave(void){ + sqlcipher_sqlite3_mutex_leave(mem5.mutex); } /* -** Return the number of LookasideSlot elements on the linked list +** Return the size of an outstanding allocation, in bytes. +** This only works for chunks that are currently checked out. */ -static u32 countLookasideSlots(LookasideSlot *p){ - u32 cnt = 0; - while( p ){ - p = p->pNext; - cnt++; - } - return cnt; +static int memsys5Size(void *p){ + int iSize, i; + assert( p!=0 ); + i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom); + assert( i>=0 && ilookaside.pInit); - u32 nFree = countLookasideSlots(db->lookaside.pFree); -#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - nInit += countLookasideSlots(db->lookaside.pSmallInit); - nFree += countLookasideSlots(db->lookaside.pSmallFree); -#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ - if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit; - return db->lookaside.nSlot - (nInit+nFree); -} +static void *memsys5MallocUnsafe(int nByte){ + int i; /* Index of a mem5.aPool[] slot */ + int iBin; /* Index into mem5.aiFreelist[] */ + int iFullSz; /* Size of allocation rounded up to power of 2 */ + int iLogsize; /* Log2 of iFullSz/POW2_MIN */ -/* -** Query status information for a single database connection -*/ -SQLITE_API int sqlcipher_sqlite3_db_status( - sqlcipher_sqlite3 *db, /* The database connection whose status is desired */ - int op, /* Status verb */ - int *pCurrent, /* Write current value here */ - int *pHighwater, /* Write high-water mark here */ - int resetFlag /* Reset high-water mark if true */ -){ - int rc = SQLITE_OK; /* Return code */ -#ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlcipher_sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwater==0 ){ - return SQLITE_MISUSE_BKPT; + /* nByte must be a positive */ + assert( nByte>0 ); + + /* No more than 1GiB per allocation */ + if( nByte > 0x40000000 ) return 0; + +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) + /* Keep track of the maximum allocation request. Even unfulfilled + ** requests are counted */ + if( (u32)nByte>mem5.maxRequest ){ + mem5.maxRequest = nByte; } #endif - sqlcipher_sqlite3_mutex_enter(db->mutex); - switch( op ){ - case SQLITE_DBSTATUS_LOOKASIDE_USED: { - *pCurrent = sqlcipher_sqlite3LookasideUsed(db, pHighwater); - if( resetFlag ){ - LookasideSlot *p = db->lookaside.pFree; - if( p ){ - while( p->pNext ) p = p->pNext; - p->pNext = db->lookaside.pInit; - db->lookaside.pInit = db->lookaside.pFree; - db->lookaside.pFree = 0; - } -#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - p = db->lookaside.pSmallFree; - if( p ){ - while( p->pNext ) p = p->pNext; - p->pNext = db->lookaside.pSmallInit; - db->lookaside.pSmallInit = db->lookaside.pSmallFree; - db->lookaside.pSmallFree = 0; - } -#endif - } - break; - } - case SQLITE_DBSTATUS_LOOKASIDE_HIT: - case SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE: - case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: { - testcase( op==SQLITE_DBSTATUS_LOOKASIDE_HIT ); - testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE ); - testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL ); - assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 ); - assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 ); - *pCurrent = 0; - *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT]; - if( resetFlag ){ - db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0; - } - break; - } - /* - ** Return an approximation for the amount of memory currently used - ** by all pagers associated with the given database connection. The - ** highwater mark is meaningless and is returned as zero. - */ - case SQLITE_DBSTATUS_CACHE_USED_SHARED: - case SQLITE_DBSTATUS_CACHE_USED: { - int totalUsed = 0; - int i; - sqlcipher_sqlite3BtreeEnterAll(db); - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - Pager *pPager = sqlcipher_sqlite3BtreePager(pBt); - int nByte = sqlcipher_sqlite3PagerMemUsed(pPager); - if( op==SQLITE_DBSTATUS_CACHE_USED_SHARED ){ - nByte = nByte / sqlcipher_sqlite3BtreeConnectionCount(pBt); - } - totalUsed += nByte; - } - } - sqlcipher_sqlite3BtreeLeaveAll(db); - *pCurrent = totalUsed; - *pHighwater = 0; - break; - } + /* Round nByte up to the next valid power of two */ + for(iFullSz=mem5.szAtom,iLogsize=0; iFullSzLOGMAX ){ + testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); + sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte); + return 0; + } + i = mem5.aiFreelist[iBin]; + memsys5Unlink(i, iBin); + while( iBin>iLogsize ){ + int newSize; - sqlcipher_sqlite3BtreeEnterAll(db); - db->pnBytesFreed = &nByte; - for(i=0; inDb; i++){ - Schema *pSchema = db->aDb[i].pSchema; - if( ALWAYS(pSchema!=0) ){ - HashElem *p; + iBin--; + newSize = 1 << iBin; + mem5.aCtrl[i+newSize] = CTRL_FREE | iBin; + memsys5Link(i+newSize, iBin); + } + mem5.aCtrl[i] = iLogsize; - nByte += sqlcipher_sqlite3GlobalConfig.m.xRoundup(sizeof(HashElem)) * ( - pSchema->tblHash.count - + pSchema->trigHash.count - + pSchema->idxHash.count - + pSchema->fkeyHash.count - ); - nByte += sqlcipher_sqlite3_msize(pSchema->tblHash.ht); - nByte += sqlcipher_sqlite3_msize(pSchema->trigHash.ht); - nByte += sqlcipher_sqlite3_msize(pSchema->idxHash.ht); - nByte += sqlcipher_sqlite3_msize(pSchema->fkeyHash.ht); +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) + /* Update allocator performance statistics. */ + mem5.nAlloc++; + mem5.totalAlloc += iFullSz; + mem5.totalExcess += iFullSz - nByte; + mem5.currentCount++; + mem5.currentOut += iFullSz; + if( mem5.maxCounttrigHash); p; p=sqliteHashNext(p)){ - sqlcipher_sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p)); - } - for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){ - sqlcipher_sqlite3DeleteTable(db, (Table *)sqliteHashData(p)); - } - } - } - db->pnBytesFreed = 0; - sqlcipher_sqlite3BtreeLeaveAll(db); +#ifdef SQLITE_DEBUG + /* Make sure the allocated memory does not assume that it is set to zero + ** or retains a value from a previous allocation */ + memset(&mem5.zPool[i*mem5.szAtom], 0xAA, iFullSz); +#endif - *pHighwater = 0; - *pCurrent = nByte; - break; - } + /* Return a pointer to the allocated memory. */ + return (void*)&mem5.zPool[i*mem5.szAtom]; +} - /* - ** *pCurrent gets an accurate estimate of the amount of memory used - ** to store all prepared statements. - ** *pHighwater is set to zero. - */ - case SQLITE_DBSTATUS_STMT_USED: { - struct Vdbe *pVdbe; /* Used to iterate through VMs */ - int nByte = 0; /* Used to accumulate return value */ +/* +** Free an outstanding memory allocation. +*/ +static void memsys5FreeUnsafe(void *pOld){ + u32 size, iLogsize; + int iBlock; - db->pnBytesFreed = &nByte; - for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){ - sqlcipher_sqlite3VdbeClearObject(db, pVdbe); - sqlcipher_sqlite3DbFree(db, pVdbe); - } - db->pnBytesFreed = 0; + /* Set iBlock to the index of the block pointed to by pOld in + ** the array of mem5.szAtom byte blocks pointed to by mem5.zPool. + */ + iBlock = (int)(((u8 *)pOld-mem5.zPool)/mem5.szAtom); - *pHighwater = 0; /* IMP: R-64479-57858 */ - *pCurrent = nByte; + /* Check that the pointer pOld points to a valid, non-free block. */ + assert( iBlock>=0 && iBlocknDb; i++){ - if( db->aDb[i].pBt ){ - Pager *pPager = sqlcipher_sqlite3BtreePager(db->aDb[i].pBt); - sqlcipher_sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet); - } - } - *pHighwater = 0; /* IMP: R-42420-56072 */ - /* IMP: R-54100-20147 */ - /* IMP: R-29431-39229 */ - *pCurrent = nRet; - break; - } +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) + assert( mem5.currentCount>0 ); + assert( mem5.currentOut>=(size*mem5.szAtom) ); + mem5.currentCount--; + mem5.currentOut -= size*mem5.szAtom; + assert( mem5.currentOut>0 || mem5.currentCount==0 ); + assert( mem5.currentCount>0 || mem5.currentOut==0 ); +#endif - /* Set *pCurrent to non-zero if there are unresolved deferred foreign - ** key constraints. Set *pCurrent to zero if all foreign key constraints - ** have been satisfied. The *pHighwater is always set to zero. - */ - case SQLITE_DBSTATUS_DEFERRED_FKS: { - *pHighwater = 0; /* IMP: R-11967-56545 */ - *pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0; - break; + mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize; + while( ALWAYS(iLogsize>iLogsize) & 1 ){ + iBuddy = iBlock - size; + assert( iBuddy>=0 ); + }else{ + iBuddy = iBlock + size; + if( iBuddy>=mem5.nBlock ) break; } - - default: { - rc = SQLITE_ERROR; + if( mem5.aCtrl[iBuddy]!=(CTRL_FREE | iLogsize) ) break; + memsys5Unlink(iBuddy, iLogsize); + iLogsize++; + if( iBuddymutex); - return rc; -} - -/************** End of status.c **********************************************/ -/************** Begin file date.c ********************************************/ -/* -** 2003 October 31 -** -** 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 the C functions that implement date and time -** functions for SQLite. -** -** There is only one exported symbol in this file - the function -** sqlcipher_sqlite3RegisterDateTimeFunctions() found at the bottom of the file. -** All other code has file scope. -** -** SQLite processes all times and dates as julian day numbers. The -** dates and times are stored as the number of days since noon -** in Greenwich on November 24, 4714 B.C. according to the Gregorian -** calendar system. -** -** 1970-01-01 00:00:00 is JD 2440587.5 -** 2000-01-01 00:00:00 is JD 2451544.5 -** -** This implementation requires years to be expressed as a 4-digit number -** which means that only dates between 0000-01-01 and 9999-12-31 can -** be represented, even though julian day numbers allow a much wider -** range of dates. -** -** The Gregorian calendar system is used for all dates and times, -** even those that predate the Gregorian calendar. Historians usually -** use the julian calendar for dates prior to 1582-10-15 and for some -** dates afterwards, depending on locale. Beware of this difference. -** -** The conversion algorithms are implemented based on descriptions -** in the following text: -** -** Jean Meeus -** Astronomical Algorithms, 2nd Edition, 1998 -** ISBN 0-943396-61-1 -** Willmann-Bell, Inc -** Richmond, Virginia (USA) -*/ -/* #include "sqliteInt.h" */ -/* #include */ -/* #include */ -#include - -#ifndef SQLITE_OMIT_DATETIME_FUNCS + size *= 2; + } -/* -** The MSVC CRT on Windows CE may not have a localtime() function. -** So declare a substitute. The substitute function itself is -** defined in "os_win.c". -*/ -#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \ - (!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API) -struct tm *__cdecl localtime(const time_t *); +#ifdef SQLITE_DEBUG + /* Overwrite freed memory with the 0x55 bit pattern to verify that it is + ** not used after being freed */ + memset(&mem5.zPool[iBlock*mem5.szAtom], 0x55, size); #endif + memsys5Link(iBlock, iLogsize); +} + /* -** A structure for holding a single date and time. +** Allocate nBytes of memory. */ -typedef struct DateTime DateTime; -struct DateTime { - sqlcipher_sqlite3_int64 iJD; /* The julian day number times 86400000 */ - int Y, M, D; /* Year, month, and day */ - int h, m; /* Hour and minutes */ - int tz; /* Timezone offset in minutes */ - double s; /* Seconds */ - char validJD; /* True (1) if iJD is valid */ - char rawS; /* Raw numeric value stored in s */ - char validYMD; /* True (1) if Y,M,D are valid */ - char validHMS; /* True (1) if h,m,s are valid */ - char validTZ; /* True (1) if tz is valid */ - char tzSet; /* Timezone was set explicitly */ - char isError; /* An overflow has occurred */ -}; - +static void *memsys5Malloc(int nBytes){ + sqlcipher_sqlite3_int64 *p = 0; + if( nBytes>0 ){ + memsys5Enter(); + p = memsys5MallocUnsafe(nBytes); + memsys5Leave(); + } + return (void*)p; +} /* -** Convert zDate into one or more integers according to the conversion -** specifier zFormat. -** -** zFormat[] contains 4 characters for each integer converted, except for -** the last integer which is specified by three characters. The meaning -** of a four-character format specifiers ABCD is: -** -** A: number of digits to convert. Always "2" or "4". -** B: minimum value. Always "0" or "1". -** C: maximum value, decoded as: -** a: 12 -** b: 14 -** c: 24 -** d: 31 -** e: 59 -** f: 9999 -** D: the separator character, or \000 to indicate this is the -** last number to convert. -** -** Example: To translate an ISO-8601 date YYYY-MM-DD, the format would -** be "40f-21a-20c". The "40f-" indicates the 4-digit year followed by "-". -** The "21a-" indicates the 2-digit month followed by "-". The "20c" indicates -** the 2-digit day which is the last integer in the set. +** Free memory. ** -** The function returns the number of successful conversions. +** The outer layer memory allocator prevents this routine from +** being called with pPrior==0. */ -static int getDigits(const char *zDate, const char *zFormat, ...){ - /* The aMx[] array translates the 3rd character of each format - ** spec into a max size: a b c d e f */ - static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 }; - va_list ap; - int cnt = 0; - char nextC; - va_start(ap, zFormat); - do{ - char N = zFormat[0] - '0'; - char min = zFormat[1] - '0'; - int val = 0; - u16 max; - - assert( zFormat[2]>='a' && zFormat[2]<='f' ); - max = aMx[zFormat[2] - 'a']; - nextC = zFormat[3]; - val = 0; - while( N-- ){ - if( !sqlcipher_sqlite3Isdigit(*zDate) ){ - goto end_getDigits; - } - val = val*10 + *zDate - '0'; - zDate++; - } - if( val<(int)min || val>(int)max || (nextC!=0 && nextC!=*zDate) ){ - goto end_getDigits; - } - *va_arg(ap,int*) = val; - zDate++; - cnt++; - zFormat += 4; - }while( nextC ); -end_getDigits: - va_end(ap); - return cnt; +static void memsys5Free(void *pPrior){ + assert( pPrior!=0 ); + memsys5Enter(); + memsys5FreeUnsafe(pPrior); + memsys5Leave(); } /* -** Parse a timezone extension on the end of a date-time. -** The extension is of the form: -** -** (+/-)HH:MM -** -** Or the "zulu" notation: -** -** Z +** Change the size of an existing memory allocation. ** -** If the parse is successful, write the number of minutes -** of change in p->tz and return 0. If a parser error occurs, -** return non-zero. +** The outer layer memory allocator prevents this routine from +** being called with pPrior==0. ** -** A missing specifier is not considered an error. +** nBytes is always a value obtained from a prior call to +** memsys5Round(). Hence nBytes is always a non-negative power +** of two. If nBytes==0 that means that an oversize allocation +** (an allocation larger than 0x40000000) was requested and this +** routine should return 0 without freeing pPrior. */ -static int parseTimezone(const char *zDate, DateTime *p){ - int sgn = 0; - int nHr, nMn; - int c; - while( sqlcipher_sqlite3Isspace(*zDate) ){ zDate++; } - p->tz = 0; - c = *zDate; - if( c=='-' ){ - sgn = -1; - }else if( c=='+' ){ - sgn = +1; - }else if( c=='Z' || c=='z' ){ - zDate++; - goto zulu_time; - }else{ - return c!=0; +static void *memsys5Realloc(void *pPrior, int nBytes){ + int nOld; + void *p; + assert( pPrior!=0 ); + assert( (nBytes&(nBytes-1))==0 ); /* EV: R-46199-30249 */ + assert( nBytes>=0 ); + if( nBytes==0 ){ + return 0; } - zDate++; - if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){ - return 1; + nOld = memsys5Size(pPrior); + if( nBytes<=nOld ){ + return pPrior; } - zDate += 5; - p->tz = sgn*(nMn + nHr*60); -zulu_time: - while( sqlcipher_sqlite3Isspace(*zDate) ){ zDate++; } - p->tzSet = 1; - return *zDate!=0; + p = memsys5Malloc(nBytes); + if( p ){ + memcpy(p, pPrior, nOld); + memsys5Free(pPrior); + } + return p; } /* -** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF. -** The HH, MM, and SS must each be exactly 2 digits. The -** fractional seconds FFFF can be one or more digits. +** Round up a request size to the next valid allocation size. If +** the allocation is too large to be handled by this allocation system, +** return 0. ** -** Return 1 if there is a parsing error and 0 on success. +** All allocations must be a power of two and must be expressed by a +** 32-bit signed integer. Hence the largest allocation is 0x40000000 +** or 1073741824 bytes. */ -static int parseHhMmSs(const char *zDate, DateTime *p){ - int h, m, s; - double ms = 0.0; - if( getDigits(zDate, "20c:20e", &h, &m)!=2 ){ - return 1; - } - zDate += 5; - if( *zDate==':' ){ - zDate++; - if( getDigits(zDate, "20e", &s)!=1 ){ - return 1; - } - zDate += 2; - if( *zDate=='.' && sqlcipher_sqlite3Isdigit(zDate[1]) ){ - double rScale = 1.0; - zDate++; - while( sqlcipher_sqlite3Isdigit(*zDate) ){ - ms = ms*10.0 + *zDate - '0'; - rScale *= 10.0; - zDate++; - } - ms /= rScale; - } - }else{ - s = 0; +static int memsys5Roundup(int n){ + int iFullSz; + if( n<=mem5.szAtom*2 ){ + if( n<=mem5.szAtom ) return mem5.szAtom; + return mem5.szAtom*2; } - p->validJD = 0; - p->rawS = 0; - p->validHMS = 1; - p->h = h; - p->m = m; - p->s = s + ms; - if( parseTimezone(zDate, p) ) return 1; - p->validTZ = (p->tz!=0)?1:0; - return 0; + if( n>0x40000000 ) return 0; + for(iFullSz=mem5.szAtom*8; iFullSz=n ) return iFullSz/2; + return iFullSz; } /* -** Put the DateTime object into its error state. +** Return the ceiling of the logarithm base 2 of iValue. +** +** Examples: memsys5Log(1) -> 0 +** memsys5Log(2) -> 1 +** memsys5Log(4) -> 2 +** memsys5Log(5) -> 3 +** memsys5Log(8) -> 3 +** memsys5Log(9) -> 4 */ -static void datetimeError(DateTime *p){ - memset(p, 0, sizeof(*p)); - p->isError = 1; +static int memsys5Log(int iValue){ + int iLog; + for(iLog=0; (iLog<(int)((sizeof(int)*8)-1)) && (1<validJD ) return; - if( p->validYMD ){ - Y = p->Y; - M = p->M; - D = p->D; - }else{ - Y = 2000; /* If no YMD specified, assume 2000-Jan-01 */ - M = 1; - D = 1; - } - if( Y<-4713 || Y>9999 || p->rawS ){ - datetimeError(p); - return; + UNUSED_PARAMETER(NotUsed); + + /* For the purposes of this routine, disable the mutex */ + mem5.mutex = 0; + + /* The size of a Mem5Link object must be a power of two. Verify that + ** this is case. + */ + assert( (sizeof(Mem5Link)&(sizeof(Mem5Link)-1))==0 ); + + nByte = sqlcipher_sqlite3GlobalConfig.nHeap; + zByte = (u8*)sqlcipher_sqlite3GlobalConfig.pHeap; + assert( zByte!=0 ); /* sqlcipher_sqlite3_config() does not allow otherwise */ + + /* boundaries on sqlcipher_sqlite3GlobalConfig.mnReq are enforced in sqlcipher_sqlite3_config() */ + nMinLog = memsys5Log(sqlcipher_sqlite3GlobalConfig.mnReq); + mem5.szAtom = (1<mem5.szAtom ){ + mem5.szAtom = mem5.szAtom << 1; } - if( M<=2 ){ - Y--; - M += 12; + + mem5.nBlock = (nByte / (mem5.szAtom+sizeof(u8))); + mem5.zPool = zByte; + mem5.aCtrl = (u8 *)&mem5.zPool[mem5.nBlock*mem5.szAtom]; + + for(ii=0; ii<=LOGMAX; ii++){ + mem5.aiFreelist[ii] = -1; } - A = Y/100; - B = 2 - A + (A/4); - X1 = 36525*(Y+4716)/100; - X2 = 306001*(M+1)/10000; - p->iJD = (sqlcipher_sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000); - p->validJD = 1; - if( p->validHMS ){ - p->iJD += p->h*3600000 + p->m*60000 + (sqlcipher_sqlite3_int64)(p->s*1000); - if( p->validTZ ){ - p->iJD -= p->tz*60000; - p->validYMD = 0; - p->validHMS = 0; - p->validTZ = 0; + + iOffset = 0; + for(ii=LOGMAX; ii>=0; ii--){ + int nAlloc = (1<mem5.nBlock); + } + + /* If a mutex is required for normal operation, allocate one */ + if( sqlcipher_sqlite3GlobalConfig.bMemstat==0 ){ + mem5.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); } + + return SQLITE_OK; } /* -** Parse dates of the form -** -** YYYY-MM-DD HH:MM:SS.FFF -** YYYY-MM-DD HH:MM:SS -** YYYY-MM-DD HH:MM -** YYYY-MM-DD -** -** Write the result into the DateTime structure and return 0 -** on success and 1 if the input string is not a well-formed -** date. +** Deinitialize this module. */ -static int parseYyyyMmDd(const char *zDate, DateTime *p){ - int Y, M, D, neg; +static void memsys5Shutdown(void *NotUsed){ + UNUSED_PARAMETER(NotUsed); + mem5.mutex = 0; + return; +} - if( zDate[0]=='-' ){ - zDate++; - neg = 1; +#ifdef SQLITE_TEST +/* +** Open the file indicated and write a log of all unfreed memory +** allocations into that log. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3Memsys5Dump(const char *zFilename){ + FILE *out; + int i, j, n; + int nMinLog; + + if( zFilename==0 || zFilename[0]==0 ){ + out = stdout; }else{ - neg = 0; + out = fopen(zFilename, "w"); + if( out==0 ){ + fprintf(stderr, "** Unable to output memory debug output log: %s **\n", + zFilename); + return; + } } - if( getDigits(zDate, "40f-21a-21d", &Y, &M, &D)!=3 ){ - return 1; + memsys5Enter(); + nMinLog = memsys5Log(mem5.szAtom); + for(i=0; i<=LOGMAX && i+nMinLog<32; i++){ + for(n=0, j=mem5.aiFreelist[i]; j>=0; j = MEM5LINK(j)->next, n++){} + fprintf(out, "freelist items of size %d: %d\n", mem5.szAtom << i, n); } - zDate += 10; - while( sqlcipher_sqlite3Isspace(*zDate) || 'T'==*(u8*)zDate ){ zDate++; } - if( parseHhMmSs(zDate, p)==0 ){ - /* We got the time */ - }else if( *zDate==0 ){ - p->validHMS = 0; + fprintf(out, "mem5.nAlloc = %llu\n", mem5.nAlloc); + fprintf(out, "mem5.totalAlloc = %llu\n", mem5.totalAlloc); + fprintf(out, "mem5.totalExcess = %llu\n", mem5.totalExcess); + fprintf(out, "mem5.currentOut = %u\n", mem5.currentOut); + fprintf(out, "mem5.currentCount = %u\n", mem5.currentCount); + fprintf(out, "mem5.maxOut = %u\n", mem5.maxOut); + fprintf(out, "mem5.maxCount = %u\n", mem5.maxCount); + fprintf(out, "mem5.maxRequest = %u\n", mem5.maxRequest); + memsys5Leave(); + if( out==stdout ){ + fflush(stdout); }else{ - return 1; - } - p->validJD = 0; - p->validYMD = 1; - p->Y = neg ? -Y : Y; - p->M = M; - p->D = D; - if( p->validTZ ){ - computeJD(p); + fclose(out); } - return 0; } +#endif /* -** Set the time to the current time reported by the VFS. -** -** Return the number of errors. +** This routine is the only routine in this file with external +** linkage. It returns a pointer to a static sqlcipher_sqlite3_mem_methods +** struct populated with the memsys5 methods. */ -static int setDateTimeToCurrent(sqlcipher_sqlite3_context *context, DateTime *p){ - p->iJD = sqlcipher_sqlite3StmtCurrentTime(context); - if( p->iJD>0 ){ - p->validJD = 1; - return 0; - }else{ - return 1; - } +SQLITE_PRIVATE const sqlcipher_sqlite3_mem_methods *sqlcipher_sqlite3MemGetMemsys5(void){ + static const sqlcipher_sqlite3_mem_methods memsys5Methods = { + memsys5Malloc, + memsys5Free, + memsys5Realloc, + memsys5Size, + memsys5Roundup, + memsys5Init, + memsys5Shutdown, + 0 + }; + return &memsys5Methods; } -/* -** Input "r" is a numeric quantity which might be a julian day number, -** or the number of seconds since 1970. If the value if r is within -** range of a julian day number, install it as such and set validJD. -** If the value is a valid unix timestamp, put it in p->s and set p->rawS. -*/ -static void setRawDateNumber(DateTime *p, double r){ - p->s = r; - p->rawS = 1; - if( r>=0.0 && r<5373484.5 ){ - p->iJD = (sqlcipher_sqlite3_int64)(r*86400000.0 + 0.5); - p->validJD = 1; - } -} +#endif /* SQLITE_ENABLE_MEMSYS5 */ +/************** End of mem5.c ************************************************/ +/************** Begin file mutex.c *******************************************/ /* -** Attempt to parse the given string into a julian day number. Return -** the number of errors. +** 2007 August 14 ** -** The following are acceptable forms for the input string: +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** YYYY-MM-DD HH:MM:SS.FFF +/-HH:MM -** DDDD.DD -** now +** 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. ** -** In the first form, the +/-HH:MM is always optional. The fractional -** seconds extension (the ".FFF") is optional. The seconds portion -** (":SS.FFF") is option. The year and date can be omitted as long -** as there is a time string. The time string can be omitted as long -** as there is a year and date. -*/ -static int parseDateOrTime( - sqlcipher_sqlite3_context *context, - const char *zDate, - DateTime *p -){ - double r; - if( parseYyyyMmDd(zDate,p)==0 ){ - return 0; - }else if( parseHhMmSs(zDate, p)==0 ){ - return 0; - }else if( sqlcipher_sqlite3StrICmp(zDate,"now")==0 && sqlcipher_sqlite3NotPureFunc(context) ){ - return setDateTimeToCurrent(context, p); - }else if( sqlcipher_sqlite3AtoF(zDate, &r, sqlcipher_sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){ - setRawDateNumber(p, r); - return 0; - } - return 1; -} - -/* The julian day number for 9999-12-31 23:59:59.999 is 5373484.4999999. -** Multiplying this by 86400000 gives 464269060799999 as the maximum value -** for DateTime.iJD. +************************************************************************* +** This file contains the C functions that implement mutexes. ** -** But some older compilers (ex: gcc 4.2.1 on older Macs) cannot deal with -** such a large integer literal, so we have to encode it. +** This file contains code that is common across all mutex implementations. */ -#define INT_464269060799999 ((((i64)0x1a640)<<32)|0x1072fdff) +/* #include "sqliteInt.h" */ +#if defined(SQLITE_DEBUG) && !defined(SQLITE_MUTEX_OMIT) /* -** Return TRUE if the given julian day number is within range. -** -** The input is the JulianDay times 86400000. +** For debugging purposes, record when the mutex subsystem is initialized +** and uninitialized so that we can assert() if there is an attempt to +** allocate a mutex while the system is uninitialized. */ -static int validJulianDay(sqlcipher_sqlite3_int64 iJD){ - return iJD>=0 && iJD<=INT_464269060799999; -} +static SQLITE_WSD int mutexIsInit = 0; +#endif /* SQLITE_DEBUG && !defined(SQLITE_MUTEX_OMIT) */ + +#ifndef SQLITE_MUTEX_OMIT + +#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS /* -** Compute the Year, Month, and Day from the julian day number. +** This block (enclosed by SQLITE_ENABLE_MULTITHREADED_CHECKS) contains +** the implementation of a wrapper around the system default mutex +** implementation (sqlcipher_sqlite3DefaultMutex()). +** +** Most calls are passed directly through to the underlying default +** mutex implementation. Except, if a mutex is configured by calling +** sqlcipher_sqlite3MutexWarnOnContention() on it, then if contention is ever +** encountered within xMutexEnter() a warning is emitted via sqlcipher_sqlite3_log(). +** +** This type of mutex is used as the database handle mutex when testing +** apps that usually use SQLITE_CONFIG_MULTITHREAD mode. */ -static void computeYMD(DateTime *p){ - int Z, A, B, C, D, E, X1; - if( p->validYMD ) return; - if( !p->validJD ){ - p->Y = 2000; - p->M = 1; - p->D = 1; - }else if( !validJulianDay(p->iJD) ){ - datetimeError(p); - return; - }else{ - Z = (int)((p->iJD + 43200000)/86400000); - A = (int)((Z - 1867216.25)/36524.25); - A = Z + 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); - X1 = (int)(30.6001*E); - p->D = B - D - X1; - p->M = E<14 ? E-1 : E-13; - p->Y = p->M>2 ? C - 4716 : C - 4715; - } - p->validYMD = 1; -} /* -** Compute the Hour, Minute, and Seconds from the julian day number. +** Type for all mutexes used when SQLITE_ENABLE_MULTITHREADED_CHECKS +** is defined. Variable CheckMutex.mutex is a pointer to the real mutex +** allocated by the system mutex implementation. Variable iType is usually set +** to the type of mutex requested - SQLITE_MUTEX_RECURSIVE, SQLITE_MUTEX_FAST +** or one of the static mutex identifiers. Or, if this is a recursive mutex +** that has been configured using sqlcipher_sqlite3MutexWarnOnContention(), it is +** set to SQLITE_MUTEX_WARNONCONTENTION. */ -static void computeHMS(DateTime *p){ - int s; - if( p->validHMS ) return; - computeJD(p); - s = (int)((p->iJD + 43200000) % 86400000); - p->s = s/1000.0; - s = (int)p->s; - p->s -= s; - p->h = s/3600; - s -= p->h*3600; - p->m = s/60; - p->s += s - p->m*60; - p->rawS = 0; - p->validHMS = 1; -} +typedef struct CheckMutex CheckMutex; +struct CheckMutex { + int iType; + sqlcipher_sqlite3_mutex *mutex; +}; + +#define SQLITE_MUTEX_WARNONCONTENTION (-1) /* -** Compute both YMD and HMS +** Pointer to real mutex methods object used by the CheckMutex +** implementation. Set by checkMutexInit(). */ -static void computeYMD_HMS(DateTime *p){ - computeYMD(p); - computeHMS(p); +static SQLITE_WSD const sqlcipher_sqlite3_mutex_methods *pGlobalMutexMethods; + +#ifdef SQLITE_DEBUG +static int checkMutexHeld(sqlcipher_sqlite3_mutex *p){ + return pGlobalMutexMethods->xMutexHeld(((CheckMutex*)p)->mutex); +} +static int checkMutexNotheld(sqlcipher_sqlite3_mutex *p){ + return pGlobalMutexMethods->xMutexNotheld(((CheckMutex*)p)->mutex); } +#endif /* -** Clear the YMD and HMS and the TZ +** Initialize and deinitialize the mutex subsystem. */ -static void clearYMD_HMS_TZ(DateTime *p){ - p->validYMD = 0; - p->validHMS = 0; - p->validTZ = 0; +static int checkMutexInit(void){ + pGlobalMutexMethods = sqlcipher_sqlite3DefaultMutex(); + return SQLITE_OK; +} +static int checkMutexEnd(void){ + pGlobalMutexMethods = 0; + return SQLITE_OK; } -#ifndef SQLITE_OMIT_LOCALTIME /* -** On recent Windows platforms, the localtime_s() function is available -** as part of the "Secure CRT". It is essentially equivalent to -** localtime_r() available under most POSIX platforms, except that the -** order of the parameters is reversed. -** -** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx. -** -** If the user has not indicated to use localtime_r() or localtime_s() -** already, check for an MSVC build environment that provides -** localtime_s(). +** Allocate a mutex. */ -#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S \ - && defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) -#undef HAVE_LOCALTIME_S -#define HAVE_LOCALTIME_S 1 +static sqlcipher_sqlite3_mutex *checkMutexAlloc(int iType){ + static CheckMutex staticMutexes[] = { + {2, 0}, {3, 0}, {4, 0}, {5, 0}, + {6, 0}, {7, 0}, {8, 0}, {9, 0}, + {10, 0}, {11, 0}, {12, 0}, {13, 0} + }; + CheckMutex *p = 0; + + assert( SQLITE_MUTEX_RECURSIVE==1 && SQLITE_MUTEX_FAST==0 ); + if( iType<2 ){ + p = sqlcipher_sqlite3MallocZero(sizeof(CheckMutex)); + if( p==0 ) return 0; + p->iType = iType; + }else{ +#ifdef SQLITE_ENABLE_API_ARMOR + if( iType-2>=ArraySize(staticMutexes) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } #endif + p = &staticMutexes[iType-2]; + } + + if( p->mutex==0 ){ + p->mutex = pGlobalMutexMethods->xMutexAlloc(iType); + if( p->mutex==0 ){ + if( iType<2 ){ + sqlcipher_sqlite3_free(p); + } + p = 0; + } + } + + return (sqlcipher_sqlite3_mutex*)p; +} /* -** The following routine implements the rough equivalent of localtime_r() -** using whatever operating-system specific localtime facility that -** is available. This routine returns 0 on success and -** non-zero on any kind of error. -** -** If the sqlcipher_sqlite3GlobalConfig.bLocaltimeFault variable is true then this -** routine will always fail. -** -** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C -** library function localtime_r() is used to assist in the calculation of -** local time. +** Free a mutex. */ -static int osLocaltime(time_t *t, struct tm *pTm){ - int rc; -#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S - struct tm *pX; -#if SQLITE_THREADSAFE>0 - sqlcipher_sqlite3_mutex *mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); -#endif - sqlcipher_sqlite3_mutex_enter(mutex); - pX = localtime(t); -#ifndef SQLITE_UNTESTABLE - if( sqlcipher_sqlite3GlobalConfig.bLocaltimeFault ) pX = 0; +static void checkMutexFree(sqlcipher_sqlite3_mutex *p){ + assert( SQLITE_MUTEX_RECURSIVE<2 ); + assert( SQLITE_MUTEX_FAST<2 ); + assert( SQLITE_MUTEX_WARNONCONTENTION<2 ); + +#if SQLITE_ENABLE_API_ARMOR + if( ((CheckMutex*)p)->iType<2 ) #endif - if( pX ) *pTm = *pX; - sqlcipher_sqlite3_mutex_leave(mutex); - rc = pX==0; -#else -#ifndef SQLITE_UNTESTABLE - if( sqlcipher_sqlite3GlobalConfig.bLocaltimeFault ) return 1; + { + CheckMutex *pCheck = (CheckMutex*)p; + pGlobalMutexMethods->xMutexFree(pCheck->mutex); + sqlcipher_sqlite3_free(pCheck); + } +#ifdef SQLITE_ENABLE_API_ARMOR + else{ + (void)SQLITE_MISUSE_BKPT; + } #endif -#if HAVE_LOCALTIME_R - rc = localtime_r(t, pTm)==0; -#else - rc = localtime_s(pTm, t); -#endif /* HAVE_LOCALTIME_R */ -#endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */ - return rc; } -#endif /* SQLITE_OMIT_LOCALTIME */ +/* +** Enter the mutex. +*/ +static void checkMutexEnter(sqlcipher_sqlite3_mutex *p){ + CheckMutex *pCheck = (CheckMutex*)p; + if( pCheck->iType==SQLITE_MUTEX_WARNONCONTENTION ){ + if( SQLITE_OK==pGlobalMutexMethods->xMutexTry(pCheck->mutex) ){ + return; + } + sqlcipher_sqlite3_log(SQLITE_MISUSE, + "illegal multi-threaded access to database connection" + ); + } + pGlobalMutexMethods->xMutexEnter(pCheck->mutex); +} -#ifndef SQLITE_OMIT_LOCALTIME /* -** Compute the difference (in milliseconds) between localtime and UTC -** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs, -** return this value and set *pRc to SQLITE_OK. -** -** Or, if an error does occur, set *pRc to SQLITE_ERROR. The returned value -** is undefined in this case. +** Enter the mutex (do not block). */ -static sqlcipher_sqlite3_int64 localtimeOffset( - DateTime *p, /* Date at which to calculate offset */ - sqlcipher_sqlite3_context *pCtx, /* Write error here if one occurs */ - int *pRc /* OUT: Error code. SQLITE_OK or ERROR */ -){ - DateTime x, y; - time_t t; - struct tm sLocal; +static int checkMutexTry(sqlcipher_sqlite3_mutex *p){ + CheckMutex *pCheck = (CheckMutex*)p; + return pGlobalMutexMethods->xMutexTry(pCheck->mutex); +} - /* Initialize the contents of sLocal to avoid a compiler warning. */ - memset(&sLocal, 0, sizeof(sLocal)); +/* +** Leave the mutex. +*/ +static void checkMutexLeave(sqlcipher_sqlite3_mutex *p){ + CheckMutex *pCheck = (CheckMutex*)p; + pGlobalMutexMethods->xMutexLeave(pCheck->mutex); +} - x = *p; - computeYMD_HMS(&x); - if( x.Y<1971 || x.Y>=2038 ){ - /* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only - ** works for years between 1970 and 2037. For dates outside this range, - ** SQLite attempts to map the year into an equivalent year within this - ** range, do the calculation, then map the year back. - */ - x.Y = 2000; - x.M = 1; - x.D = 1; - x.h = 0; - x.m = 0; - x.s = 0.0; - } else { - int s = (int)(x.s + 0.5); - x.s = s; - } - x.tz = 0; - x.validJD = 0; - computeJD(&x); - t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); - if( osLocaltime(&t, &sLocal) ){ - sqlcipher_sqlite3_result_error(pCtx, "local time unavailable", -1); - *pRc = SQLITE_ERROR; - return 0; - } - y.Y = sLocal.tm_year + 1900; - y.M = sLocal.tm_mon + 1; - y.D = sLocal.tm_mday; - y.h = sLocal.tm_hour; - y.m = sLocal.tm_min; - y.s = sLocal.tm_sec; - y.validYMD = 1; - y.validHMS = 1; - y.validJD = 0; - y.rawS = 0; - y.validTZ = 0; - y.isError = 0; - computeJD(&y); - *pRc = SQLITE_OK; - return y.iJD - x.iJD; +sqlcipher_sqlite3_mutex_methods const *multiThreadedCheckMutex(void){ + static const sqlcipher_sqlite3_mutex_methods sMutex = { + checkMutexInit, + checkMutexEnd, + checkMutexAlloc, + checkMutexFree, + checkMutexEnter, + checkMutexTry, + checkMutexLeave, +#ifdef SQLITE_DEBUG + checkMutexHeld, + checkMutexNotheld +#else + 0, + 0 +#endif + }; + return &sMutex; } -#endif /* SQLITE_OMIT_LOCALTIME */ /* -** The following table defines various date transformations of the form -** -** 'NNN days' -** -** Where NNN is an arbitrary floating-point number and "days" can be one -** of several units of time. +** Mark the SQLITE_MUTEX_RECURSIVE mutex passed as the only argument as +** one on which there should be no contention. */ -static const struct { - u8 eType; /* Transformation type code */ - u8 nName; /* Length of th name */ - char *zName; /* Name of the transformation */ - double rLimit; /* Maximum NNN value for this transform */ - double rXform; /* Constant used for this transform */ -} aXformType[] = { - { 0, 6, "second", 464269060800.0, 1000.0 }, - { 0, 6, "minute", 7737817680.0, 60000.0 }, - { 0, 4, "hour", 128963628.0, 3600000.0 }, - { 0, 3, "day", 5373485.0, 86400000.0 }, - { 1, 5, "month", 176546.0, 2592000000.0 }, - { 2, 4, "year", 14713.0, 31536000000.0 }, -}; +SQLITE_PRIVATE void sqlcipher_sqlite3MutexWarnOnContention(sqlcipher_sqlite3_mutex *p){ + if( sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc==checkMutexAlloc ){ + CheckMutex *pCheck = (CheckMutex*)p; + assert( pCheck->iType==SQLITE_MUTEX_RECURSIVE ); + pCheck->iType = SQLITE_MUTEX_WARNONCONTENTION; + } +} +#endif /* ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS */ /* -** Process a modifier to a date-time stamp. The modifiers are -** as follows: -** -** NNN days -** NNN hours -** NNN minutes -** NNN.NNNN seconds -** NNN months -** NNN years -** start of month -** start of year -** start of week -** start of day -** weekday N -** unixepoch -** localtime -** utc -** -** Return 0 on success and 1 if there is any kind of error. If the error -** is in a system call (i.e. localtime()), then an error message is written -** to context pCtx. If the error is an unrecognized modifier, no error is -** written to pCtx. +** Initialize the mutex system. */ -static int parseModifier( - sqlcipher_sqlite3_context *pCtx, /* Function context */ - const char *z, /* The text of the modifier */ - int n, /* Length of zMod in bytes */ - DateTime *p /* The date/time value to be modified */ -){ - int rc = 1; - double r; - switch(sqlcipher_sqlite3UpperToLower[(u8)z[0]] ){ -#ifndef SQLITE_OMIT_LOCALTIME - case 'l': { - /* localtime - ** - ** Assuming the current time value is UTC (a.k.a. GMT), shift it to - ** show local time. - */ - if( sqlcipher_sqlite3_stricmp(z, "localtime")==0 && sqlcipher_sqlite3NotPureFunc(pCtx) ){ - computeJD(p); - p->iJD += localtimeOffset(p, pCtx, &rc); - clearYMD_HMS_TZ(p); - } - break; - } -#endif - case 'u': { - /* - ** unixepoch - ** - ** Treat the current value of p->s as the number of - ** seconds since 1970. Convert to a real julian day number. - */ - if( sqlcipher_sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){ - r = p->s*1000.0 + 210866760000000.0; - if( r>=0.0 && r<464269060800000.0 ){ - clearYMD_HMS_TZ(p); - p->iJD = (sqlcipher_sqlite3_int64)(r + 0.5); - p->validJD = 1; - p->rawS = 0; - rc = 0; - } - } -#ifndef SQLITE_OMIT_LOCALTIME - else if( sqlcipher_sqlite3_stricmp(z, "utc")==0 && sqlcipher_sqlite3NotPureFunc(pCtx) ){ - if( p->tzSet==0 ){ - sqlcipher_sqlite3_int64 c1; - computeJD(p); - c1 = localtimeOffset(p, pCtx, &rc); - if( rc==SQLITE_OK ){ - p->iJD -= c1; - clearYMD_HMS_TZ(p); - p->iJD += c1 - localtimeOffset(p, pCtx, &rc); - } - p->tzSet = 1; - }else{ - rc = SQLITE_OK; - } - } -#endif - break; - } - case 'w': { - /* - ** weekday N - ** - ** Move the date to the same time on the next occurrence of - ** weekday N where 0==Sunday, 1==Monday, and so forth. If the - ** date is already on the appropriate weekday, this is a no-op. - */ - if( sqlcipher_sqlite3_strnicmp(z, "weekday ", 8)==0 - && sqlcipher_sqlite3AtoF(&z[8], &r, sqlcipher_sqlite3Strlen30(&z[8]), SQLITE_UTF8)>0 - && (n=(int)r)==r && n>=0 && r<7 ){ - sqlcipher_sqlite3_int64 Z; - computeYMD_HMS(p); - p->validTZ = 0; - p->validJD = 0; - computeJD(p); - Z = ((p->iJD + 129600000)/86400000) % 7; - if( Z>n ) Z -= 7; - p->iJD += (n - Z)*86400000; - clearYMD_HMS_TZ(p); - rc = 0; - } - break; - } - case 's': { - /* - ** start of TTTTT - ** - ** Move the date backwards to the beginning of the current day, - ** or month or year. - */ - if( sqlcipher_sqlite3_strnicmp(z, "start of ", 9)!=0 ) break; - if( !p->validJD && !p->validYMD && !p->validHMS ) break; - z += 9; - computeYMD(p); - p->validHMS = 1; - p->h = p->m = 0; - p->s = 0.0; - p->rawS = 0; - p->validTZ = 0; - p->validJD = 0; - if( sqlcipher_sqlite3_stricmp(z,"month")==0 ){ - p->D = 1; - rc = 0; - }else if( sqlcipher_sqlite3_stricmp(z,"year")==0 ){ - p->M = 1; - p->D = 1; - rc = 0; - }else if( sqlcipher_sqlite3_stricmp(z,"day")==0 ){ - rc = 0; - } - break; - } - case '+': - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - double rRounder; - int i; - for(n=1; z[n] && z[n]!=':' && !sqlcipher_sqlite3Isspace(z[n]); n++){} - if( sqlcipher_sqlite3AtoF(z, &r, n, SQLITE_UTF8)<=0 ){ - rc = 1; - break; - } - if( z[n]==':' ){ - /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the - ** specified number of hours, minutes, seconds, and fractional seconds - ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be - ** omitted. - */ - const char *z2 = z; - DateTime tx; - sqlcipher_sqlite3_int64 day; - if( !sqlcipher_sqlite3Isdigit(*z2) ) z2++; - memset(&tx, 0, sizeof(tx)); - if( parseHhMmSs(z2, &tx) ) break; - computeJD(&tx); - tx.iJD -= 43200000; - day = tx.iJD/86400000; - tx.iJD -= day*86400000; - if( z[0]=='-' ) tx.iJD = -tx.iJD; - computeJD(p); - clearYMD_HMS_TZ(p); - p->iJD += tx.iJD; - rc = 0; - break; - } +SQLITE_PRIVATE int sqlcipher_sqlite3MutexInit(void){ + int rc = SQLITE_OK; + if( !sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc ){ + /* If the xMutexAlloc method has not been set, then the user did not + ** install a mutex implementation via sqlcipher_sqlite3_config() prior to + ** sqlcipher_sqlite3_initialize() being called. This block copies pointers to + ** the default implementation into the sqlcipher_sqlite3GlobalConfig structure. + */ + sqlcipher_sqlite3_mutex_methods const *pFrom; + sqlcipher_sqlite3_mutex_methods *pTo = &sqlcipher_sqlite3GlobalConfig.mutex; - /* If control reaches this point, it means the transformation is - ** one of the forms like "+NNN days". */ - z += n; - while( sqlcipher_sqlite3Isspace(*z) ) z++; - n = sqlcipher_sqlite3Strlen30(z); - if( n>10 || n<3 ) break; - if( sqlcipher_sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--; - computeJD(p); - rc = 1; - rRounder = r<0 ? -0.5 : +0.5; - for(i=0; i-aXformType[i].rLimit && rM += (int)r; - x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; - p->Y += x; - p->M -= x*12; - p->validJD = 0; - r -= (int)r; - break; - } - case 2: { /* Special processing to add years */ - int y = (int)r; - computeYMD_HMS(p); - p->Y += y; - p->validJD = 0; - r -= (int)r; - break; - } - } - computeJD(p); - p->iJD += (sqlcipher_sqlite3_int64)(r*aXformType[i].rXform + rRounder); - rc = 0; - break; - } - } - clearYMD_HMS_TZ(p); - break; - } - default: { - break; + if( sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ +#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS + pFrom = multiThreadedCheckMutex(); +#else + pFrom = sqlcipher_sqlite3DefaultMutex(); +#endif + }else{ + pFrom = sqlcipher_sqlite3NoopMutex(); } + pTo->xMutexInit = pFrom->xMutexInit; + pTo->xMutexEnd = pFrom->xMutexEnd; + pTo->xMutexFree = pFrom->xMutexFree; + pTo->xMutexEnter = pFrom->xMutexEnter; + pTo->xMutexTry = pFrom->xMutexTry; + pTo->xMutexLeave = pFrom->xMutexLeave; + pTo->xMutexHeld = pFrom->xMutexHeld; + pTo->xMutexNotheld = pFrom->xMutexNotheld; + sqlcipher_sqlite3MemoryBarrier(); + pTo->xMutexAlloc = pFrom->xMutexAlloc; } + assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexInit ); + rc = sqlcipher_sqlite3GlobalConfig.mutex.xMutexInit(); + +#ifdef SQLITE_DEBUG + GLOBAL(int, mutexIsInit) = 1; +#endif + + sqlcipher_sqlite3MemoryBarrier(); return rc; } /* -** Process time function arguments. argv[0] is a date-time stamp. -** argv[1] and following are modifiers. Parse them all and write -** the resulting time into the DateTime structure p. Return 0 -** on success and 1 if there are any errors. -** -** If there are zero parameters (if even argv[0] is undefined) -** then assume a default value of "now" for argv[0]. +** Shutdown the mutex system. This call frees resources allocated by +** sqlcipher_sqlite3MutexInit(). */ -static int isDate( - sqlcipher_sqlite3_context *context, - int argc, - sqlcipher_sqlite3_value **argv, - DateTime *p -){ - int i, n; - const unsigned char *z; - int eType; - memset(p, 0, sizeof(*p)); - if( argc==0 ){ - return setDateTimeToCurrent(context, p); - } - if( (eType = sqlcipher_sqlite3_value_type(argv[0]))==SQLITE_FLOAT - || eType==SQLITE_INTEGER ){ - setRawDateNumber(p, sqlcipher_sqlite3_value_double(argv[0])); - }else{ - z = sqlcipher_sqlite3_value_text(argv[0]); - if( !z || parseDateOrTime(context, (char*)z, p) ){ - return 1; - } - } - for(i=1; iisError || !validJulianDay(p->iJD) ) return 1; - return 0; -} +#ifdef SQLITE_DEBUG + GLOBAL(int, mutexIsInit) = 0; +#endif + + return rc; +} /* -** The following routines implement the various date and time functions -** of SQLite. +** Retrieve a pointer to a static mutex or allocate a new dynamic one. */ +SQLITE_API sqlcipher_sqlite3_mutex *sqlcipher_sqlite3_mutex_alloc(int id){ +#ifndef SQLITE_OMIT_AUTOINIT + if( id<=SQLITE_MUTEX_RECURSIVE && sqlcipher_sqlite3_initialize() ) return 0; + if( id>SQLITE_MUTEX_RECURSIVE && sqlcipher_sqlite3MutexInit() ) return 0; +#endif + assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc ); + return sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc(id); +} + +SQLITE_PRIVATE sqlcipher_sqlite3_mutex *sqlcipher_sqlite3MutexAlloc(int id){ + if( !sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ + return 0; + } + assert( GLOBAL(int, mutexIsInit) ); + assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc ); + return sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc(id); +} /* -** julianday( TIMESTRING, MOD, MOD, ...) -** -** Return the julian day number of the date specified in the arguments +** Free a dynamic mutex. */ -static void juliandayFunc( - sqlcipher_sqlite3_context *context, - int argc, - sqlcipher_sqlite3_value **argv -){ - DateTime x; - if( isDate(context, argc, argv, &x)==0 ){ - computeJD(&x); - sqlcipher_sqlite3_result_double(context, x.iJD/86400000.0); +SQLITE_API void sqlcipher_sqlite3_mutex_free(sqlcipher_sqlite3_mutex *p){ + if( p ){ + assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexFree ); + sqlcipher_sqlite3GlobalConfig.mutex.xMutexFree(p); } } /* -** datetime( TIMESTRING, MOD, MOD, ...) -** -** Return YYYY-MM-DD HH:MM:SS +** Obtain the mutex p. If some other thread already has the mutex, block +** until it can be obtained. */ -static void datetimeFunc( - sqlcipher_sqlite3_context *context, - int argc, - sqlcipher_sqlite3_value **argv -){ - DateTime x; - if( isDate(context, argc, argv, &x)==0 ){ - char zBuf[100]; - computeYMD_HMS(&x); - sqlcipher_sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d %02d:%02d:%02d", - x.Y, x.M, x.D, x.h, x.m, (int)(x.s)); - sqlcipher_sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); +SQLITE_API void sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3_mutex *p){ + if( p ){ + assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexEnter ); + sqlcipher_sqlite3GlobalConfig.mutex.xMutexEnter(p); } } /* -** time( TIMESTRING, MOD, MOD, ...) -** -** Return HH:MM:SS +** Obtain the mutex p. If successful, return SQLITE_OK. Otherwise, if another +** thread holds the mutex and it cannot be obtained, return SQLITE_BUSY. */ -static void timeFunc( - sqlcipher_sqlite3_context *context, - int argc, - sqlcipher_sqlite3_value **argv -){ - DateTime x; - if( isDate(context, argc, argv, &x)==0 ){ - char zBuf[100]; - computeHMS(&x); - sqlcipher_sqlite3_snprintf(sizeof(zBuf), zBuf, "%02d:%02d:%02d", x.h, x.m, (int)x.s); - sqlcipher_sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); +SQLITE_API int sqlcipher_sqlite3_mutex_try(sqlcipher_sqlite3_mutex *p){ + int rc = SQLITE_OK; + if( p ){ + assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexTry ); + return sqlcipher_sqlite3GlobalConfig.mutex.xMutexTry(p); } + return rc; } /* -** date( TIMESTRING, MOD, MOD, ...) -** -** Return YYYY-MM-DD +** The sqlcipher_sqlite3_mutex_leave() routine exits a mutex that was previously +** entered by the same thread. The behavior is undefined if the mutex +** is not currently entered. If a NULL pointer is passed as an argument +** this function is a no-op. */ -static void dateFunc( - sqlcipher_sqlite3_context *context, - int argc, - sqlcipher_sqlite3_value **argv -){ - DateTime x; - if( isDate(context, argc, argv, &x)==0 ){ - char zBuf[100]; - computeYMD(&x); - sqlcipher_sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D); - sqlcipher_sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); +SQLITE_API void sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3_mutex *p){ + if( p ){ + assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexLeave ); + sqlcipher_sqlite3GlobalConfig.mutex.xMutexLeave(p); } } +#ifndef NDEBUG /* -** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) +** The sqlcipher_sqlite3_mutex_held() and sqlcipher_sqlite3_mutex_notheld() routine are +** intended for use inside assert() statements. +*/ +SQLITE_API int sqlcipher_sqlite3_mutex_held(sqlcipher_sqlite3_mutex *p){ + assert( p==0 || sqlcipher_sqlite3GlobalConfig.mutex.xMutexHeld ); + return p==0 || sqlcipher_sqlite3GlobalConfig.mutex.xMutexHeld(p); +} +SQLITE_API int sqlcipher_sqlite3_mutex_notheld(sqlcipher_sqlite3_mutex *p){ + assert( p==0 || sqlcipher_sqlite3GlobalConfig.mutex.xMutexNotheld ); + return p==0 || sqlcipher_sqlite3GlobalConfig.mutex.xMutexNotheld(p); +} +#endif + +#endif /* !defined(SQLITE_MUTEX_OMIT) */ + +/************** End of mutex.c ***********************************************/ +/************** Begin file mutex_noop.c **************************************/ +/* +** 2008 October 07 ** -** Return a string described by FORMAT. Conversions as follows: +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** %d day of month -** %f ** fractional seconds SS.SSS -** %H hour 00-24 -** %j day of year 000-366 -** %J ** julian day number -** %m month 01-12 -** %M minute 00-59 -** %s seconds since 1970-01-01 -** %S seconds 00-59 -** %w day of week 0-6 sunday==0 -** %W week of year 00-53 -** %Y year 0000-9999 -** %% % +** 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 the C functions that implement mutexes. +** +** This implementation in this file does not provide any mutual +** exclusion and is thus suitable for use only in applications +** that use SQLite in a single thread. The routines defined +** here are place-holders. Applications can substitute working +** mutex routines at start-time using the +** +** sqlcipher_sqlite3_config(SQLITE_CONFIG_MUTEX,...) +** +** interface. +** +** If compiled with SQLITE_DEBUG, then additional logic is inserted +** that does error checking on mutexes to make sure they are being +** called correctly. */ -static void strftimeFunc( - sqlcipher_sqlite3_context *context, - int argc, - sqlcipher_sqlite3_value **argv -){ - DateTime x; - u64 n; - size_t i,j; - char *z; - sqlcipher_sqlite3 *db; - const char *zFmt; - char zBuf[100]; - if( argc==0 ) return; - zFmt = (const char*)sqlcipher_sqlite3_value_text(argv[0]); - if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return; - db = sqlcipher_sqlite3_context_db_handle(context); - for(i=0, n=1; zFmt[i]; i++, n++){ - if( zFmt[i]=='%' ){ - switch( zFmt[i+1] ){ - case 'd': - case 'H': - case 'm': - case 'M': - case 'S': - case 'W': - n++; - /* fall thru */ - case 'w': - case '%': - break; - case 'f': - n += 8; - break; - case 'j': - n += 3; - break; - case 'Y': - n += 8; - break; - case 's': - case 'J': - n += 50; - break; - default: - return; /* ERROR. return a NULL */ +/* #include "sqliteInt.h" */ + +#ifndef SQLITE_MUTEX_OMIT + +#ifndef SQLITE_DEBUG +/* +** Stub routines for all mutex methods. +** +** This routines provide no mutual exclusion or error checking. +*/ +static int noopMutexInit(void){ return SQLITE_OK; } +static int noopMutexEnd(void){ return SQLITE_OK; } +static sqlcipher_sqlite3_mutex *noopMutexAlloc(int id){ + UNUSED_PARAMETER(id); + return (sqlcipher_sqlite3_mutex*)8; +} +static void noopMutexFree(sqlcipher_sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } +static void noopMutexEnter(sqlcipher_sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } +static int noopMutexTry(sqlcipher_sqlite3_mutex *p){ + UNUSED_PARAMETER(p); + return SQLITE_OK; +} +static void noopMutexLeave(sqlcipher_sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } + +SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3NoopMutex(void){ + static const sqlcipher_sqlite3_mutex_methods sMutex = { + noopMutexInit, + noopMutexEnd, + noopMutexAlloc, + noopMutexFree, + noopMutexEnter, + noopMutexTry, + noopMutexLeave, + + 0, + 0, + }; + + return &sMutex; +} +#endif /* !SQLITE_DEBUG */ + +#ifdef SQLITE_DEBUG +/* +** In this implementation, error checking is provided for testing +** and debugging purposes. The mutexes still do not provide any +** mutual exclusion. +*/ + +/* +** The mutex object +*/ +typedef struct sqlcipher_sqlite3_debug_mutex { + int id; /* The mutex type */ + int cnt; /* Number of entries without a matching leave */ +} sqlcipher_sqlite3_debug_mutex; + +/* +** The sqlcipher_sqlite3_mutex_held() and sqlcipher_sqlite3_mutex_notheld() routine are +** intended for use inside assert() statements. +*/ +static int debugMutexHeld(sqlcipher_sqlite3_mutex *pX){ + sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; + return p==0 || p->cnt>0; +} +static int debugMutexNotheld(sqlcipher_sqlite3_mutex *pX){ + sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; + return p==0 || p->cnt==0; +} + +/* +** Initialize and deinitialize the mutex subsystem. +*/ +static int debugMutexInit(void){ return SQLITE_OK; } +static int debugMutexEnd(void){ return SQLITE_OK; } + +/* +** The sqlcipher_sqlite3_mutex_alloc() routine allocates a new +** mutex and returns a pointer to it. If it returns NULL +** that means that a mutex could not be allocated. +*/ +static sqlcipher_sqlite3_mutex *debugMutexAlloc(int id){ + static sqlcipher_sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_VFS3 - 1]; + sqlcipher_sqlite3_debug_mutex *pNew = 0; + switch( id ){ + case SQLITE_MUTEX_FAST: + case SQLITE_MUTEX_RECURSIVE: { + pNew = sqlcipher_sqlite3Malloc(sizeof(*pNew)); + if( pNew ){ + pNew->id = id; + pNew->cnt = 0; } - i++; - } - } - testcase( n==sizeof(zBuf)-1 ); - testcase( n==sizeof(zBuf) ); - testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH]+1 ); - testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ); - if( n(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - sqlcipher_sqlite3_result_error_toobig(context); - return; - }else{ - z = sqlcipher_sqlite3DbMallocRawNN(db, (int)n); - if( z==0 ){ - sqlcipher_sqlite3_result_error_nomem(context); - return; + break; } - } - computeJD(&x); - computeYMD_HMS(&x); - for(i=j=0; zFmt[i]; i++){ - if( zFmt[i]!='%' ){ - z[j++] = zFmt[i]; - }else{ - i++; - switch( zFmt[i] ){ - case 'd': sqlcipher_sqlite3_snprintf(3, &z[j],"%02d",x.D); j+=2; break; - case 'f': { - double s = x.s; - if( s>59.999 ) s = 59.999; - sqlcipher_sqlite3_snprintf(7, &z[j],"%06.3f", s); - j += sqlcipher_sqlite3Strlen30(&z[j]); - break; - } - case 'H': sqlcipher_sqlite3_snprintf(3, &z[j],"%02d",x.h); j+=2; break; - case 'W': /* Fall thru */ - case 'j': { - int nDay; /* Number of days since 1st day of year */ - DateTime y = x; - y.validJD = 0; - y.M = 1; - y.D = 1; - computeJD(&y); - nDay = (int)((x.iJD-y.iJD+43200000)/86400000); - if( zFmt[i]=='W' ){ - int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ - wd = (int)(((x.iJD+43200000)/86400000)%7); - sqlcipher_sqlite3_snprintf(3, &z[j],"%02d",(nDay+7-wd)/7); - j += 2; - }else{ - sqlcipher_sqlite3_snprintf(4, &z[j],"%03d",nDay+1); - j += 3; - } - break; - } - case 'J': { - sqlcipher_sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0); - j+=sqlcipher_sqlite3Strlen30(&z[j]); - break; - } - case 'm': sqlcipher_sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; - case 'M': sqlcipher_sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; - case 's': { - i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); - sqlcipher_sqlite3Int64ToText(iS, &z[j]); - j += sqlcipher_sqlite3Strlen30(&z[j]); - break; - } - case 'S': sqlcipher_sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break; - case 'w': { - z[j++] = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; - break; - } - case 'Y': { - sqlcipher_sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=sqlcipher_sqlite3Strlen30(&z[j]); - break; - } - default: z[j++] = '%'; break; + default: { +#ifdef SQLITE_ENABLE_API_ARMOR + if( id-2<0 || id-2>=ArraySize(aStatic) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; } +#endif + pNew = &aStatic[id-2]; + pNew->id = id; + break; } } - z[j] = 0; - sqlcipher_sqlite3_result_text(context, z, -1, - z==zBuf ? SQLITE_TRANSIENT : SQLITE_DYNAMIC); + return (sqlcipher_sqlite3_mutex*)pNew; } /* -** current_time() -** -** This function returns the same value as time('now'). +** This routine deallocates a previously allocated mutex. */ -static void ctimeFunc( - sqlcipher_sqlite3_context *context, - int NotUsed, - sqlcipher_sqlite3_value **NotUsed2 -){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - timeFunc(context, 0, 0); +static void debugMutexFree(sqlcipher_sqlite3_mutex *pX){ + sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; + assert( p->cnt==0 ); + if( p->id==SQLITE_MUTEX_RECURSIVE || p->id==SQLITE_MUTEX_FAST ){ + sqlcipher_sqlite3_free(p); + }else{ +#ifdef SQLITE_ENABLE_API_ARMOR + (void)SQLITE_MISUSE_BKPT; +#endif + } } /* -** current_date() -** -** This function returns the same value as date('now'). +** The sqlcipher_sqlite3_mutex_enter() and sqlcipher_sqlite3_mutex_try() routines attempt +** to enter a mutex. If another thread is already within the mutex, +** sqlcipher_sqlite3_mutex_enter() will block and sqlcipher_sqlite3_mutex_try() will return +** SQLITE_BUSY. The sqlcipher_sqlite3_mutex_try() interface returns SQLITE_OK +** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can +** be entered multiple times by the same thread. In such cases the, +** mutex must be exited an equal number of times before another thread +** can enter. If the same thread tries to enter any other kind of mutex +** more than once, the behavior is undefined. */ -static void cdateFunc( - sqlcipher_sqlite3_context *context, - int NotUsed, - sqlcipher_sqlite3_value **NotUsed2 -){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - dateFunc(context, 0, 0); +static void debugMutexEnter(sqlcipher_sqlite3_mutex *pX){ + sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; + assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); + p->cnt++; +} +static int debugMutexTry(sqlcipher_sqlite3_mutex *pX){ + sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; + assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); + p->cnt++; + return SQLITE_OK; } /* -** current_timestamp() -** -** This function returns the same value as datetime('now'). +** The sqlcipher_sqlite3_mutex_leave() routine exits a mutex that was +** previously entered by the same thread. The behavior +** is undefined if the mutex is not currently entered or +** is not currently allocated. SQLite will never do either. */ -static void ctimestampFunc( - sqlcipher_sqlite3_context *context, - int NotUsed, - sqlcipher_sqlite3_value **NotUsed2 -){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - datetimeFunc(context, 0, 0); +static void debugMutexLeave(sqlcipher_sqlite3_mutex *pX){ + sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; + assert( debugMutexHeld(pX) ); + p->cnt--; + assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); } -#endif /* !defined(SQLITE_OMIT_DATETIME_FUNCS) */ -#ifdef SQLITE_OMIT_DATETIME_FUNCS -/* -** If the library is compiled to omit the full-scale date and time -** handling (to get a smaller binary), the following minimal version -** of the functions current_time(), current_date() and current_timestamp() -** are included instead. This is to support column declarations that -** include "DEFAULT CURRENT_TIME" etc. -** -** This function uses the C-library functions time(), gmtime() -** and strftime(). The format string to pass to strftime() is supplied -** as the user-data for the function. -*/ -static void currentTimeFunc( - sqlcipher_sqlite3_context *context, - int argc, - sqlcipher_sqlite3_value **argv -){ - time_t t; - char *zFormat = (char *)sqlcipher_sqlite3_user_data(context); - sqlcipher_sqlite3_int64 iT; - struct tm *pTm; - struct tm sNow; - char zBuf[20]; +SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3NoopMutex(void){ + static const sqlcipher_sqlite3_mutex_methods sMutex = { + debugMutexInit, + debugMutexEnd, + debugMutexAlloc, + debugMutexFree, + debugMutexEnter, + debugMutexTry, + debugMutexLeave, - UNUSED_PARAMETER(argc); - UNUSED_PARAMETER(argv); + debugMutexHeld, + debugMutexNotheld + }; - iT = sqlcipher_sqlite3StmtCurrentTime(context); - if( iT<=0 ) return; - t = iT/1000 - 10000*(sqlcipher_sqlite3_int64)21086676; -#if HAVE_GMTIME_R - pTm = gmtime_r(&t, &sNow); -#else - sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)); - pTm = gmtime(&t); - if( pTm ) memcpy(&sNow, pTm, sizeof(sNow)); - sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)); -#endif - if( pTm ){ - strftime(zBuf, 20, zFormat, &sNow); - sqlcipher_sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); - } + return &sMutex; } -#endif +#endif /* SQLITE_DEBUG */ /* -** This function registered all of the above C functions as SQL -** functions. This should be the only routine in this file with -** external linkage. +** If compiled with SQLITE_MUTEX_NOOP, then the no-op mutex implementation +** is used regardless of the run-time threadsafety setting. */ -SQLITE_PRIVATE void sqlcipher_sqlite3RegisterDateTimeFunctions(void){ - static FuncDef aDateTimeFuncs[] = { -#ifndef SQLITE_OMIT_DATETIME_FUNCS - PURE_DATE(julianday, -1, 0, 0, juliandayFunc ), - PURE_DATE(date, -1, 0, 0, dateFunc ), - PURE_DATE(time, -1, 0, 0, timeFunc ), - PURE_DATE(datetime, -1, 0, 0, datetimeFunc ), - PURE_DATE(strftime, -1, 0, 0, strftimeFunc ), - DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), - DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), - DFUNCTION(current_date, 0, 0, 0, cdateFunc ), -#else - STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc), - STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc), - STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc), -#endif - }; - sqlcipher_sqlite3InsertBuiltinFuncs(aDateTimeFuncs, ArraySize(aDateTimeFuncs)); +#ifdef SQLITE_MUTEX_NOOP +SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3DefaultMutex(void){ + return sqlcipher_sqlite3NoopMutex(); } +#endif /* defined(SQLITE_MUTEX_NOOP) */ +#endif /* !defined(SQLITE_MUTEX_OMIT) */ -/************** End of date.c ************************************************/ -/************** Begin file os.c **********************************************/ +/************** End of mutex_noop.c ******************************************/ +/************** Begin file mutex_unix.c **************************************/ /* -** 2005 November 29 +** 2007 August 28 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -28223,432 +28001,421 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RegisterDateTimeFunctions(void){ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** -****************************************************************************** -** -** This file contains OS interface code that is common to all -** architectures. +************************************************************************* +** This file contains the C functions that implement mutexes for pthreads */ /* #include "sqliteInt.h" */ /* -** If we compile with the SQLITE_TEST macro set, then the following block -** of code will give us the ability to simulate a disk I/O error. This -** is used for testing the I/O recovery logic. +** The code in this file is only used if we are compiling threadsafe +** under unix with pthreads. +** +** Note that this implementation requires a version of pthreads that +** supports recursive mutexes. */ -#if defined(SQLITE_TEST) -SQLITE_API int sqlcipher_sqlite3_io_error_hit = 0; /* Total number of I/O Errors */ -SQLITE_API int sqlcipher_sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */ -SQLITE_API int sqlcipher_sqlite3_io_error_pending = 0; /* Count down to first I/O error */ -SQLITE_API int sqlcipher_sqlite3_io_error_persist = 0; /* True if I/O errors persist */ -SQLITE_API int sqlcipher_sqlite3_io_error_benign = 0; /* True if errors are benign */ -SQLITE_API int sqlcipher_sqlite3_diskfull_pending = 0; -SQLITE_API int sqlcipher_sqlite3_diskfull = 0; -#endif /* defined(SQLITE_TEST) */ +#ifdef SQLITE_MUTEX_PTHREADS + +#include /* -** When testing, also keep a count of the number of open files. +** The sqlcipher_sqlite3_mutex.id, sqlcipher_sqlite3_mutex.nRef, and sqlcipher_sqlite3_mutex.owner fields +** are necessary under two condidtions: (1) Debug builds and (2) using +** home-grown mutexes. Encapsulate these conditions into a single #define. */ -#if defined(SQLITE_TEST) -SQLITE_API int sqlcipher_sqlite3_open_file_count = 0; -#endif /* defined(SQLITE_TEST) */ +#if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX) +# define SQLITE_MUTEX_NREF 1 +#else +# define SQLITE_MUTEX_NREF 0 +#endif /* -** The default SQLite sqlcipher_sqlite3_vfs implementations do not allocate -** memory (actually, os_unix.c allocates a small amount of memory -** from within OsOpen()), but some third-party implementations may. -** So we test the effects of a malloc() failing and the sqlcipher_sqlite3OsXXX() -** function returning SQLITE_IOERR_NOMEM using the DO_OS_MALLOC_TEST macro. -** -** The following functions are instrumented for malloc() failure -** testing: -** -** sqlcipher_sqlite3OsRead() -** sqlcipher_sqlite3OsWrite() -** sqlcipher_sqlite3OsSync() -** sqlcipher_sqlite3OsFileSize() -** sqlcipher_sqlite3OsLock() -** sqlcipher_sqlite3OsCheckReservedLock() -** sqlcipher_sqlite3OsFileControl() -** sqlcipher_sqlite3OsShmMap() -** sqlcipher_sqlite3OsOpen() -** sqlcipher_sqlite3OsDelete() -** sqlcipher_sqlite3OsAccess() -** sqlcipher_sqlite3OsFullPathname() -** +** Each recursive mutex is an instance of the following structure. */ -#if defined(SQLITE_TEST) -SQLITE_API int sqlcipher_sqlite3_memdebug_vfs_oom_test = 1; - #define DO_OS_MALLOC_TEST(x) \ - if (sqlcipher_sqlite3_memdebug_vfs_oom_test && (!x || !sqlcipher_sqlite3JournalIsInMemory(x))) { \ - void *pTstAlloc = sqlcipher_sqlite3Malloc(10); \ - if (!pTstAlloc) return SQLITE_IOERR_NOMEM_BKPT; \ - sqlcipher_sqlite3_free(pTstAlloc); \ - } +struct sqlcipher_sqlite3_mutex { + pthread_mutex_t mutex; /* Mutex controlling the lock */ +#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) + int id; /* Mutex type */ +#endif +#if SQLITE_MUTEX_NREF + volatile int nRef; /* Number of entrances */ + volatile pthread_t owner; /* Thread that is within this mutex */ + int trace; /* True to trace changes */ +#endif +}; +#if SQLITE_MUTEX_NREF +# define SQLITE3_MUTEX_INITIALIZER(id) \ + {PTHREAD_MUTEX_INITIALIZER,id,0,(pthread_t)0,0} +#elif defined(SQLITE_ENABLE_API_ARMOR) +# define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER, id } #else - #define DO_OS_MALLOC_TEST(x) +#define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER } #endif /* -** The following routines are convenience wrappers around methods -** of the sqlcipher_sqlite3_file object. This is mostly just syntactic sugar. All -** of this would be completely automatic if SQLite were coded using -** C++ instead of plain old C. +** The sqlcipher_sqlite3_mutex_held() and sqlcipher_sqlite3_mutex_notheld() routine are +** intended for use only inside assert() statements. On some platforms, +** there might be race conditions that can cause these routines to +** deliver incorrect results. In particular, if pthread_equal() is +** not an atomic operation, then these routines might delivery +** incorrect results. On most platforms, pthread_equal() is a +** comparison of two integers and is therefore atomic. But we are +** told that HPUX is not such a platform. If so, then these routines +** will not always work correctly on HPUX. +** +** On those platforms where pthread_equal() is not atomic, SQLite +** should be compiled without -DSQLITE_DEBUG and with -DNDEBUG to +** make sure no assert() statements are evaluated and hence these +** routines are never called. */ -SQLITE_PRIVATE void sqlcipher_sqlite3OsClose(sqlcipher_sqlite3_file *pId){ - if( pId->pMethods ){ - pId->pMethods->xClose(pId); - pId->pMethods = 0; - } -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsRead(sqlcipher_sqlite3_file *id, void *pBuf, int amt, i64 offset){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xRead(id, pBuf, amt, offset); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsWrite(sqlcipher_sqlite3_file *id, const void *pBuf, int amt, i64 offset){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xWrite(id, pBuf, amt, offset); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsTruncate(sqlcipher_sqlite3_file *id, i64 size){ - return id->pMethods->xTruncate(id, size); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsSync(sqlcipher_sqlite3_file *id, int flags){ - DO_OS_MALLOC_TEST(id); - return flags ? id->pMethods->xSync(id, flags) : SQLITE_OK; -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsFileSize(sqlcipher_sqlite3_file *id, i64 *pSize){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xFileSize(id, pSize); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsLock(sqlcipher_sqlite3_file *id, int lockType){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xLock(id, lockType); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsUnlock(sqlcipher_sqlite3_file *id, int lockType){ - return id->pMethods->xUnlock(id, lockType); +#if !defined(NDEBUG) || defined(SQLITE_DEBUG) +static int pthreadMutexHeld(sqlcipher_sqlite3_mutex *p){ + return (p->nRef!=0 && pthread_equal(p->owner, pthread_self())); } -SQLITE_PRIVATE int sqlcipher_sqlite3OsCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xCheckReservedLock(id, pResOut); +static int pthreadMutexNotheld(sqlcipher_sqlite3_mutex *p){ + return p->nRef==0 || pthread_equal(p->owner, pthread_self())==0; } +#endif /* -** Use sqlcipher_sqlite3OsFileControl() when we are doing something that might fail -** and we need to know about the failures. Use sqlcipher_sqlite3OsFileControlHint() -** when simply tossing information over the wall to the VFS and we do not -** really care if the VFS receives and understands the information since it -** is only a hint and can be safely ignored. The sqlcipher_sqlite3OsFileControlHint() -** routine has no return value since the return value would be meaningless. +** Try to provide a memory barrier operation, needed for initialization +** and also for the implementation of xShmBarrier in the VFS in cases +** where SQLite is compiled without mutexes. */ -SQLITE_PRIVATE int sqlcipher_sqlite3OsFileControl(sqlcipher_sqlite3_file *id, int op, void *pArg){ - if( id->pMethods==0 ) return SQLITE_NOTFOUND; -#ifdef SQLITE_TEST - if( op!=SQLITE_FCNTL_COMMIT_PHASETWO - && op!=SQLITE_FCNTL_LOCK_TIMEOUT - ){ - /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite - ** is using a regular VFS, it is called after the corresponding - ** transaction has been committed. Injecting a fault at this point - ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM - ** but the transaction is committed anyway. - ** - ** The core must call OsFileControl() though, not OsFileControlHint(), - ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably - ** means the commit really has failed and an error should be returned - ** to the user. */ - DO_OS_MALLOC_TEST(id); - } +SQLITE_PRIVATE void sqlcipher_sqlite3MemoryBarrier(void){ +#if defined(SQLITE_MEMORY_BARRIER) + SQLITE_MEMORY_BARRIER; +#elif defined(__GNUC__) && GCC_VERSION>=4001000 + __sync_synchronize(); #endif - return id->pMethods->xFileControl(id, op, pArg); -} -SQLITE_PRIVATE void sqlcipher_sqlite3OsFileControlHint(sqlcipher_sqlite3_file *id, int op, void *pArg){ - if( id->pMethods ) (void)id->pMethods->xFileControl(id, op, pArg); } -SQLITE_PRIVATE int sqlcipher_sqlite3OsSectorSize(sqlcipher_sqlite3_file *id){ - int (*xSectorSize)(sqlcipher_sqlite3_file*) = id->pMethods->xSectorSize; - return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsDeviceCharacteristics(sqlcipher_sqlite3_file *id){ - return id->pMethods->xDeviceCharacteristics(id); -} -#ifndef SQLITE_OMIT_WAL -SQLITE_PRIVATE int sqlcipher_sqlite3OsShmLock(sqlcipher_sqlite3_file *id, int offset, int n, int flags){ - return id->pMethods->xShmLock(id, offset, n, flags); -} -SQLITE_PRIVATE void sqlcipher_sqlite3OsShmBarrier(sqlcipher_sqlite3_file *id){ - id->pMethods->xShmBarrier(id); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsShmUnmap(sqlcipher_sqlite3_file *id, int deleteFlag){ - return id->pMethods->xShmUnmap(id, deleteFlag); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsShmMap( - sqlcipher_sqlite3_file *id, /* Database file handle */ - int iPage, - int pgsz, - int bExtend, /* True to extend file if necessary */ - void volatile **pp /* OUT: Pointer to mapping */ -){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp); -} -#endif /* SQLITE_OMIT_WAL */ +/* +** Initialize and deinitialize the mutex subsystem. +*/ +static int pthreadMutexInit(void){ return SQLITE_OK; } +static int pthreadMutexEnd(void){ return SQLITE_OK; } -#if SQLITE_MAX_MMAP_SIZE>0 -/* The real implementation of xFetch and xUnfetch */ -SQLITE_PRIVATE int sqlcipher_sqlite3OsFetch(sqlcipher_sqlite3_file *id, i64 iOff, int iAmt, void **pp){ - DO_OS_MALLOC_TEST(id); - return id->pMethods->xFetch(id, iOff, iAmt, pp); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsUnfetch(sqlcipher_sqlite3_file *id, i64 iOff, void *p){ - return id->pMethods->xUnfetch(id, iOff, p); -} +/* +** The sqlcipher_sqlite3_mutex_alloc() routine allocates a new +** mutex and returns a pointer to it. If it returns NULL +** that means that a mutex could not be allocated. SQLite +** will unwind its stack and return an error. The argument +** to sqlcipher_sqlite3_mutex_alloc() is one of these integer constants: +** +**
      +**
    • SQLITE_MUTEX_FAST +**
    • SQLITE_MUTEX_RECURSIVE +**
    • SQLITE_MUTEX_STATIC_MAIN +**
    • SQLITE_MUTEX_STATIC_MEM +**
    • SQLITE_MUTEX_STATIC_OPEN +**
    • SQLITE_MUTEX_STATIC_PRNG +**
    • SQLITE_MUTEX_STATIC_LRU +**
    • SQLITE_MUTEX_STATIC_PMEM +**
    • SQLITE_MUTEX_STATIC_APP1 +**
    • SQLITE_MUTEX_STATIC_APP2 +**
    • SQLITE_MUTEX_STATIC_APP3 +**
    • SQLITE_MUTEX_STATIC_VFS1 +**
    • SQLITE_MUTEX_STATIC_VFS2 +**
    • SQLITE_MUTEX_STATIC_VFS3 +**
    +** +** The first two constants cause sqlcipher_sqlite3_mutex_alloc() to create +** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE +** is used but not necessarily so when SQLITE_MUTEX_FAST is used. +** The mutex implementation does not need to make a distinction +** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does +** not want to. But SQLite will only request a recursive mutex in +** cases where it really needs one. If a faster non-recursive mutex +** implementation is available on the host platform, the mutex subsystem +** might return such a mutex in response to SQLITE_MUTEX_FAST. +** +** The other allowed parameters to sqlcipher_sqlite3_mutex_alloc() each return +** a pointer to a static preexisting mutex. Six static mutexes are +** used by the current version of SQLite. Future versions of SQLite +** may add additional static mutexes. Static mutexes are for internal +** use by SQLite only. Applications that use SQLite mutexes should +** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or +** SQLITE_MUTEX_RECURSIVE. +** +** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST +** or SQLITE_MUTEX_RECURSIVE) is used then sqlcipher_sqlite3_mutex_alloc() +** returns a different mutex on every call. But for the static +** mutex types, the same mutex is returned on every call that has +** the same type number. +*/ +static sqlcipher_sqlite3_mutex *pthreadMutexAlloc(int iType){ + static sqlcipher_sqlite3_mutex staticMutexes[] = { + SQLITE3_MUTEX_INITIALIZER(2), + SQLITE3_MUTEX_INITIALIZER(3), + SQLITE3_MUTEX_INITIALIZER(4), + SQLITE3_MUTEX_INITIALIZER(5), + SQLITE3_MUTEX_INITIALIZER(6), + SQLITE3_MUTEX_INITIALIZER(7), + SQLITE3_MUTEX_INITIALIZER(8), + SQLITE3_MUTEX_INITIALIZER(9), + SQLITE3_MUTEX_INITIALIZER(10), + SQLITE3_MUTEX_INITIALIZER(11), + SQLITE3_MUTEX_INITIALIZER(12), + SQLITE3_MUTEX_INITIALIZER(13) + }; + sqlcipher_sqlite3_mutex *p; + switch( iType ){ + case SQLITE_MUTEX_RECURSIVE: { + p = sqlcipher_sqlite3MallocZero( sizeof(*p) ); + if( p ){ +#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX + /* If recursive mutexes are not available, we will have to + ** build our own. See below. */ + pthread_mutex_init(&p->mutex, 0); #else -/* No-op stubs to use when memory-mapped I/O is disabled */ -SQLITE_PRIVATE int sqlcipher_sqlite3OsFetch(sqlcipher_sqlite3_file *id, i64 iOff, int iAmt, void **pp){ - *pp = 0; - return SQLITE_OK; -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsUnfetch(sqlcipher_sqlite3_file *id, i64 iOff, void *p){ - return SQLITE_OK; -} + /* Use a recursive mutex if it is available */ + pthread_mutexattr_t recursiveAttr; + pthread_mutexattr_init(&recursiveAttr); + pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&p->mutex, &recursiveAttr); + pthread_mutexattr_destroy(&recursiveAttr); +#endif +#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) + p->id = SQLITE_MUTEX_RECURSIVE; +#endif + } + break; + } + case SQLITE_MUTEX_FAST: { + p = sqlcipher_sqlite3MallocZero( sizeof(*p) ); + if( p ){ + pthread_mutex_init(&p->mutex, 0); +#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) + p->id = SQLITE_MUTEX_FAST; +#endif + } + break; + } + default: { +#ifdef SQLITE_ENABLE_API_ARMOR + if( iType-2<0 || iType-2>=ArraySize(staticMutexes) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } #endif + p = &staticMutexes[iType-2]; + break; + } + } +#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) + assert( p==0 || p->id==iType ); +#endif + return p; +} + /* -** The next group of routines are convenience wrappers around the -** VFS methods. +** This routine deallocates a previously +** allocated mutex. SQLite is careful to deallocate every +** mutex that it allocates. */ -SQLITE_PRIVATE int sqlcipher_sqlite3OsOpen( - sqlcipher_sqlite3_vfs *pVfs, - const char *zPath, - sqlcipher_sqlite3_file *pFile, - int flags, - int *pFlagsOut -){ - int rc; - DO_OS_MALLOC_TEST(0); - /* 0x87f7f is a mask of SQLITE_OPEN_ flags that are valid to be passed - ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, - ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before - ** reaching the VFS. */ - rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x1087f7f, pFlagsOut); - assert( rc==SQLITE_OK || pFile->pMethods==0 ); - return rc; -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsDelete(sqlcipher_sqlite3_vfs *pVfs, const char *zPath, int dirSync){ - DO_OS_MALLOC_TEST(0); - assert( dirSync==0 || dirSync==1 ); - return pVfs->xDelete(pVfs, zPath, dirSync); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsAccess( - sqlcipher_sqlite3_vfs *pVfs, - const char *zPath, - int flags, - int *pResOut -){ - DO_OS_MALLOC_TEST(0); - return pVfs->xAccess(pVfs, zPath, flags, pResOut); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsFullPathname( - sqlcipher_sqlite3_vfs *pVfs, - const char *zPath, - int nPathOut, - char *zPathOut -){ - DO_OS_MALLOC_TEST(0); - zPathOut[0] = 0; - return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut); -} -#ifndef SQLITE_OMIT_LOAD_EXTENSION -SQLITE_PRIVATE void *sqlcipher_sqlite3OsDlOpen(sqlcipher_sqlite3_vfs *pVfs, const char *zPath){ - return pVfs->xDlOpen(pVfs, zPath); -} -SQLITE_PRIVATE void sqlcipher_sqlite3OsDlError(sqlcipher_sqlite3_vfs *pVfs, int nByte, char *zBufOut){ - pVfs->xDlError(pVfs, nByte, zBufOut); -} -SQLITE_PRIVATE void (*sqlcipher_sqlite3OsDlSym(sqlcipher_sqlite3_vfs *pVfs, void *pHdle, const char *zSym))(void){ - return pVfs->xDlSym(pVfs, pHdle, zSym); +static void pthreadMutexFree(sqlcipher_sqlite3_mutex *p){ + assert( p->nRef==0 ); +#if SQLITE_ENABLE_API_ARMOR + if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ) +#endif + { + pthread_mutex_destroy(&p->mutex); + sqlcipher_sqlite3_free(p); + } +#ifdef SQLITE_ENABLE_API_ARMOR + else{ + (void)SQLITE_MISUSE_BKPT; + } +#endif } -SQLITE_PRIVATE void sqlcipher_sqlite3OsDlClose(sqlcipher_sqlite3_vfs *pVfs, void *pHandle){ - pVfs->xDlClose(pVfs, pHandle); + +/* +** The sqlcipher_sqlite3_mutex_enter() and sqlcipher_sqlite3_mutex_try() routines attempt +** to enter a mutex. If another thread is already within the mutex, +** sqlcipher_sqlite3_mutex_enter() will block and sqlcipher_sqlite3_mutex_try() will return +** SQLITE_BUSY. The sqlcipher_sqlite3_mutex_try() interface returns SQLITE_OK +** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can +** be entered multiple times by the same thread. In such cases the, +** mutex must be exited an equal number of times before another thread +** can enter. If the same thread tries to enter any other kind of mutex +** more than once, the behavior is undefined. +*/ +static void pthreadMutexEnter(sqlcipher_sqlite3_mutex *p){ + assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) ); + +#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX + /* If recursive mutexes are not available, then we have to grow + ** our own. This implementation assumes that pthread_equal() + ** is atomic - that it cannot be deceived into thinking self + ** and p->owner are equal if p->owner changes between two values + ** that are not equal to self while the comparison is taking place. + ** This implementation also assumes a coherent cache - that + ** separate processes cannot read different values from the same + ** address at the same time. If either of these two conditions + ** are not met, then the mutexes will fail and problems will result. + */ + { + pthread_t self = pthread_self(); + if( p->nRef>0 && pthread_equal(p->owner, self) ){ + p->nRef++; + }else{ + pthread_mutex_lock(&p->mutex); + assert( p->nRef==0 ); + p->owner = self; + p->nRef = 1; + } + } +#else + /* Use the built-in recursive mutexes if they are available. + */ + pthread_mutex_lock(&p->mutex); +#if SQLITE_MUTEX_NREF + assert( p->nRef>0 || p->owner==0 ); + p->owner = pthread_self(); + p->nRef++; +#endif +#endif + +#ifdef SQLITE_DEBUG + if( p->trace ){ + printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); + } +#endif } -#endif /* SQLITE_OMIT_LOAD_EXTENSION */ -SQLITE_PRIVATE int sqlcipher_sqlite3OsRandomness(sqlcipher_sqlite3_vfs *pVfs, int nByte, char *zBufOut){ - if( sqlcipher_sqlite3Config.iPrngSeed ){ - memset(zBufOut, 0, nByte); - if( ALWAYS(nByte>(signed)sizeof(unsigned)) ) nByte = sizeof(unsigned int); - memcpy(zBufOut, &sqlcipher_sqlite3Config.iPrngSeed, nByte); - return SQLITE_OK; - }else{ - return pVfs->xRandomness(pVfs, nByte, zBufOut); +static int pthreadMutexTry(sqlcipher_sqlite3_mutex *p){ + int rc; + assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) ); + +#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX + /* If recursive mutexes are not available, then we have to grow + ** our own. This implementation assumes that pthread_equal() + ** is atomic - that it cannot be deceived into thinking self + ** and p->owner are equal if p->owner changes between two values + ** that are not equal to self while the comparison is taking place. + ** This implementation also assumes a coherent cache - that + ** separate processes cannot read different values from the same + ** address at the same time. If either of these two conditions + ** are not met, then the mutexes will fail and problems will result. + */ + { + pthread_t self = pthread_self(); + if( p->nRef>0 && pthread_equal(p->owner, self) ){ + p->nRef++; + rc = SQLITE_OK; + }else if( pthread_mutex_trylock(&p->mutex)==0 ){ + assert( p->nRef==0 ); + p->owner = self; + p->nRef = 1; + rc = SQLITE_OK; + }else{ + rc = SQLITE_BUSY; + } } - -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsSleep(sqlcipher_sqlite3_vfs *pVfs, int nMicro){ - return pVfs->xSleep(pVfs, nMicro); -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsGetLastError(sqlcipher_sqlite3_vfs *pVfs){ - return pVfs->xGetLastError ? pVfs->xGetLastError(pVfs, 0, 0) : 0; -} -SQLITE_PRIVATE int sqlcipher_sqlite3OsCurrentTimeInt64(sqlcipher_sqlite3_vfs *pVfs, sqlcipher_sqlite3_int64 *pTimeOut){ - int rc; - /* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64() - ** method to get the current date and time if that method is available - ** (if iVersion is 2 or greater and the function pointer is not NULL) and - ** will fall back to xCurrentTime() if xCurrentTimeInt64() is - ** unavailable. +#else + /* Use the built-in recursive mutexes if they are available. */ - if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){ - rc = pVfs->xCurrentTimeInt64(pVfs, pTimeOut); + if( pthread_mutex_trylock(&p->mutex)==0 ){ +#if SQLITE_MUTEX_NREF + p->owner = pthread_self(); + p->nRef++; +#endif + rc = SQLITE_OK; }else{ - double r; - rc = pVfs->xCurrentTime(pVfs, &r); - *pTimeOut = (sqlcipher_sqlite3_int64)(r*86400000.0); + rc = SQLITE_BUSY; } - return rc; -} +#endif -SQLITE_PRIVATE int sqlcipher_sqlite3OsOpenMalloc( - sqlcipher_sqlite3_vfs *pVfs, - const char *zFile, - sqlcipher_sqlite3_file **ppFile, - int flags, - int *pOutFlags -){ - int rc; - sqlcipher_sqlite3_file *pFile; - pFile = (sqlcipher_sqlite3_file *)sqlcipher_sqlite3MallocZero(pVfs->szOsFile); - if( pFile ){ - rc = sqlcipher_sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags); - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3_free(pFile); - }else{ - *ppFile = pFile; - } - }else{ - rc = SQLITE_NOMEM_BKPT; +#ifdef SQLITE_DEBUG + if( rc==SQLITE_OK && p->trace ){ + printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } +#endif return rc; } -SQLITE_PRIVATE void sqlcipher_sqlite3OsCloseFree(sqlcipher_sqlite3_file *pFile){ - assert( pFile ); - sqlcipher_sqlite3OsClose(pFile); - sqlcipher_sqlite3_free(pFile); -} - -/* -** This function is a wrapper around the OS specific implementation of -** sqlcipher_sqlite3_os_init(). The purpose of the wrapper is to provide the -** ability to simulate a malloc failure, so that the handling of an -** error in sqlcipher_sqlite3_os_init() by the upper layers can be tested. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3OsInit(void){ - void *p = sqlcipher_sqlite3_malloc(10); - if( p==0 ) return SQLITE_NOMEM_BKPT; - sqlcipher_sqlite3_free(p); - return sqlcipher_sqlite3_os_init(); -} /* -** The list of all registered VFS implementations. +** The sqlcipher_sqlite3_mutex_leave() routine exits a mutex that was +** previously entered by the same thread. The behavior +** is undefined if the mutex is not currently entered or +** is not currently allocated. SQLite will never do either. */ -static sqlcipher_sqlite3_vfs * SQLITE_WSD vfsList = 0; -#define vfsList GLOBAL(sqlcipher_sqlite3_vfs *, vfsList) +static void pthreadMutexLeave(sqlcipher_sqlite3_mutex *p){ + assert( pthreadMutexHeld(p) ); +#if SQLITE_MUTEX_NREF + p->nRef--; + if( p->nRef==0 ) p->owner = 0; +#endif + assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); -/* -** Locate a VFS by name. If no name is given, simply return the -** first VFS on the list. -*/ -SQLITE_API sqlcipher_sqlite3_vfs *sqlcipher_sqlite3_vfs_find(const char *zVfs){ - sqlcipher_sqlite3_vfs *pVfs = 0; -#if SQLITE_THREADSAFE - sqlcipher_sqlite3_mutex *mutex; +#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX + if( p->nRef==0 ){ + pthread_mutex_unlock(&p->mutex); + } +#else + pthread_mutex_unlock(&p->mutex); #endif -#ifndef SQLITE_OMIT_AUTOINIT - int rc = sqlcipher_sqlite3_initialize(); - if( rc ) return 0; + +#ifdef SQLITE_DEBUG + if( p->trace ){ + printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); + } #endif -#if SQLITE_THREADSAFE - mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); +} + +SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3DefaultMutex(void){ + static const sqlcipher_sqlite3_mutex_methods sMutex = { + pthreadMutexInit, + pthreadMutexEnd, + pthreadMutexAlloc, + pthreadMutexFree, + pthreadMutexEnter, + pthreadMutexTry, + pthreadMutexLeave, +#ifdef SQLITE_DEBUG + pthreadMutexHeld, + pthreadMutexNotheld +#else + 0, + 0 #endif - sqlcipher_sqlite3_mutex_enter(mutex); - for(pVfs = vfsList; pVfs; pVfs=pVfs->pNext){ - if( zVfs==0 ) break; - if( strcmp(zVfs, pVfs->zName)==0 ) break; - } - sqlcipher_sqlite3_mutex_leave(mutex); - return pVfs; + }; + + return &sMutex; } +#endif /* SQLITE_MUTEX_PTHREADS */ + +/************** End of mutex_unix.c ******************************************/ +/************** Begin file mutex_w32.c ***************************************/ /* -** Unlink a VFS from the linked list +** 2007 August 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 file contains the C functions that implement mutexes for Win32. */ -static void vfsUnlink(sqlcipher_sqlite3_vfs *pVfs){ - assert( sqlcipher_sqlite3_mutex_held(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)) ); - if( pVfs==0 ){ - /* No-op */ - }else if( vfsList==pVfs ){ - vfsList = pVfs->pNext; - }else if( vfsList ){ - sqlcipher_sqlite3_vfs *p = vfsList; - while( p->pNext && p->pNext!=pVfs ){ - p = p->pNext; - } - if( p->pNext==pVfs ){ - p->pNext = pVfs->pNext; - } - } -} +/* #include "sqliteInt.h" */ +#if SQLITE_OS_WIN /* -** Register a VFS with the system. It is harmless to register the same -** VFS multiple times. The new VFS becomes the default if makeDflt is -** true. +** Include code that is common to all os_*.c files */ -SQLITE_API int sqlcipher_sqlite3_vfs_register(sqlcipher_sqlite3_vfs *pVfs, int makeDflt){ - MUTEX_LOGIC(sqlcipher_sqlite3_mutex *mutex;) -#ifndef SQLITE_OMIT_AUTOINIT - int rc = sqlcipher_sqlite3_initialize(); - if( rc ) return rc; -#endif -#ifdef SQLITE_ENABLE_API_ARMOR - if( pVfs==0 ) return SQLITE_MISUSE_BKPT; -#endif - - MUTEX_LOGIC( mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) - sqlcipher_sqlite3_mutex_enter(mutex); - vfsUnlink(pVfs); - if( makeDflt || vfsList==0 ){ - pVfs->pNext = vfsList; - vfsList = pVfs; - }else{ - pVfs->pNext = vfsList->pNext; - vfsList->pNext = pVfs; - } - assert(vfsList); - sqlcipher_sqlite3_mutex_leave(mutex); - return SQLITE_OK; -} +/* #include "os_common.h" */ /* -** Unregister a VFS so that it is no longer accessible. +** Include the header file for the Windows VFS. */ -SQLITE_API int sqlcipher_sqlite3_vfs_unregister(sqlcipher_sqlite3_vfs *pVfs){ - MUTEX_LOGIC(sqlcipher_sqlite3_mutex *mutex;) -#ifndef SQLITE_OMIT_AUTOINIT - int rc = sqlcipher_sqlite3_initialize(); - if( rc ) return rc; -#endif - MUTEX_LOGIC( mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) - sqlcipher_sqlite3_mutex_enter(mutex); - vfsUnlink(pVfs); - sqlcipher_sqlite3_mutex_leave(mutex); - return SQLITE_OK; -} - -/************** End of os.c **************************************************/ -/************** Begin file fault.c *******************************************/ +/************** Include os_win.h in the middle of mutex_w32.c ****************/ +/************** Begin file os_win.h ******************************************/ /* -** 2008 Jan 22 +** 2013 November 25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -28657,150 +28424,466 @@ SQLITE_API int sqlcipher_sqlite3_vfs_unregister(sqlcipher_sqlite3_vfs *pVfs){ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** -************************************************************************* +****************************************************************************** ** -** This file contains code to support the concept of "benign" -** malloc failures (when the xMalloc() or xRealloc() method of the -** sqlcipher_sqlite3_mem_methods structure fails to allocate a block of memory -** and returns 0). +** This file contains code that is specific to Windows. +*/ +#ifndef SQLITE_OS_WIN_H +#define SQLITE_OS_WIN_H + +/* +** Include the primary Windows SDK header file. +*/ +#include "windows.h" + +#ifdef __CYGWIN__ +# include +# include /* amalgamator: dontcache */ +#endif + +/* +** Determine if we are dealing with Windows NT. ** -** Most malloc failures are non-benign. After they occur, SQLite -** abandons the current operation and returns an error code (usually -** SQLITE_NOMEM) to the user. However, sometimes a fault is not necessarily -** fatal. For example, if a malloc fails while resizing a hash table, this -** is completely recoverable simply by not carrying out the resize. The -** hash table will continue to function normally. So a malloc failure -** during a hash table resize is a benign fault. +** We ought to be able to determine if we are compiling for Windows 9x or +** Windows NT using the _WIN32_WINNT macro as follows: +** +** #if defined(_WIN32_WINNT) +** # define SQLITE_OS_WINNT 1 +** #else +** # define SQLITE_OS_WINNT 0 +** #endif +** +** However, Visual Studio 2005 does not set _WIN32_WINNT by default, as +** it ought to, so the above test does not work. We'll just assume that +** everything is Windows NT unless the programmer explicitly says otherwise +** by setting SQLITE_OS_WINNT to 0. */ +#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT) +# define SQLITE_OS_WINNT 1 +#endif -/* #include "sqliteInt.h" */ +/* +** Determine if we are dealing with Windows CE - which has a much reduced +** API. +*/ +#if defined(_WIN32_WCE) +# define SQLITE_OS_WINCE 1 +#else +# define SQLITE_OS_WINCE 0 +#endif -#ifndef SQLITE_UNTESTABLE +/* +** 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 /* -** Global variables. +** For WinCE, some API function parameters do not appear to be declared as +** volatile. */ -typedef struct BenignMallocHooks BenignMallocHooks; -static SQLITE_WSD struct BenignMallocHooks { - void (*xBenignBegin)(void); - void (*xBenignEnd)(void); -} sqlcipher_sqlite3Hooks = { 0, 0 }; +#if SQLITE_OS_WINCE +# define SQLITE_WIN32_VOLATILE +#else +# define SQLITE_WIN32_VOLATILE volatile +#endif -/* The "wsdHooks" macro will resolve to the appropriate BenignMallocHooks -** structure. If writable static data is unsupported on the target, -** we have to locate the state vector at run-time. In the more common -** case where writable static data is supported, wsdHooks can refer directly -** to the "sqlcipher_sqlite3Hooks" state vector declared above. +/* +** For some Windows sub-platforms, the _beginthreadex() / _endthreadex() +** functions are not available (e.g. those not using MSVC, Cygwin, etc). */ -#ifdef SQLITE_OMIT_WSD -# define wsdHooksInit \ - BenignMallocHooks *x = &GLOBAL(BenignMallocHooks,sqlcipher_sqlite3Hooks) -# define wsdHooks x[0] +#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ + SQLITE_THREADSAFE>0 && !defined(__CYGWIN__) +# define SQLITE_OS_WIN_THREADS 1 #else -# define wsdHooksInit -# define wsdHooks sqlcipher_sqlite3Hooks +# define SQLITE_OS_WIN_THREADS 0 +#endif + +#endif /* SQLITE_OS_WIN_H */ + +/************** End of os_win.h **********************************************/ +/************** Continuing where we left off in mutex_w32.c ******************/ #endif +/* +** The code in this file is only used if we are compiling multithreaded +** on a Win32 system. +*/ +#ifdef SQLITE_MUTEX_W32 /* -** Register hooks to call when sqlcipher_sqlite3BeginBenignMalloc() and -** sqlcipher_sqlite3EndBenignMalloc() are called, respectively. +** Each recursive mutex is an instance of the following structure. */ -SQLITE_PRIVATE void sqlcipher_sqlite3BenignMallocHooks( - void (*xBenignBegin)(void), - void (*xBenignEnd)(void) -){ - wsdHooksInit; - wsdHooks.xBenignBegin = xBenignBegin; - wsdHooks.xBenignEnd = xBenignEnd; +struct sqlcipher_sqlite3_mutex { + CRITICAL_SECTION mutex; /* Mutex controlling the lock */ + int id; /* Mutex type */ +#ifdef SQLITE_DEBUG + volatile int nRef; /* Number of enterances */ + volatile DWORD owner; /* Thread holding this mutex */ + volatile LONG trace; /* True to trace changes */ +#endif +}; + +/* +** These are the initializer values used when declaring a "static" mutex +** on Win32. It should be noted that all mutexes require initialization +** on the Win32 platform. +*/ +#define SQLITE_W32_MUTEX_INITIALIZER { 0 } + +#ifdef SQLITE_DEBUG +#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id, \ + 0L, (DWORD)0, 0 } +#else +#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id } +#endif + +#ifdef SQLITE_DEBUG +/* +** The sqlcipher_sqlite3_mutex_held() and sqlcipher_sqlite3_mutex_notheld() routine are +** intended for use only inside assert() statements. +*/ +static int winMutexHeld(sqlcipher_sqlite3_mutex *p){ + return p->nRef!=0 && p->owner==GetCurrentThreadId(); } +static int winMutexNotheld2(sqlcipher_sqlite3_mutex *p, DWORD tid){ + return p->nRef==0 || p->owner!=tid; +} + +static int winMutexNotheld(sqlcipher_sqlite3_mutex *p){ + DWORD tid = GetCurrentThreadId(); + return winMutexNotheld2(p, tid); +} +#endif + /* -** This (sqlcipher_sqlite3EndBenignMalloc()) is called by SQLite code to indicate that -** subsequent malloc failures are benign. A call to sqlcipher_sqlite3EndBenignMalloc() -** indicates that subsequent malloc failures are non-benign. +** Try to provide a memory barrier operation, needed for initialization +** and also for the xShmBarrier method of the VFS in cases when SQLite is +** compiled without mutexes (SQLITE_THREADSAFE=0). */ -SQLITE_PRIVATE void sqlcipher_sqlite3BeginBenignMalloc(void){ - wsdHooksInit; - if( wsdHooks.xBenignBegin ){ - wsdHooks.xBenignBegin(); - } +SQLITE_PRIVATE void sqlcipher_sqlite3MemoryBarrier(void){ +#if defined(SQLITE_MEMORY_BARRIER) + SQLITE_MEMORY_BARRIER; +#elif defined(__GNUC__) + __sync_synchronize(); +#elif MSVC_VERSION>=1300 + _ReadWriteBarrier(); +#elif defined(MemoryBarrier) + MemoryBarrier(); +#endif } -SQLITE_PRIVATE void sqlcipher_sqlite3EndBenignMalloc(void){ - wsdHooksInit; - if( wsdHooks.xBenignEnd ){ - wsdHooks.xBenignEnd(); + +/* +** Initialize and deinitialize the mutex subsystem. +*/ +static sqlcipher_sqlite3_mutex winMutex_staticMutexes[] = { + SQLITE3_MUTEX_INITIALIZER(2), + SQLITE3_MUTEX_INITIALIZER(3), + SQLITE3_MUTEX_INITIALIZER(4), + SQLITE3_MUTEX_INITIALIZER(5), + SQLITE3_MUTEX_INITIALIZER(6), + SQLITE3_MUTEX_INITIALIZER(7), + SQLITE3_MUTEX_INITIALIZER(8), + SQLITE3_MUTEX_INITIALIZER(9), + SQLITE3_MUTEX_INITIALIZER(10), + SQLITE3_MUTEX_INITIALIZER(11), + SQLITE3_MUTEX_INITIALIZER(12), + SQLITE3_MUTEX_INITIALIZER(13) +}; + +static int winMutex_isInit = 0; +static int winMutex_isNt = -1; /* <0 means "need to query" */ + +/* As the winMutexInit() and winMutexEnd() functions are called as part +** of the sqlcipher_sqlite3_initialize() and sqlcipher_sqlite3_shutdown() processing, the +** "interlocked" magic used here is probably not strictly necessary. +*/ +static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0; + +SQLITE_API int sqlcipher_sqlite3_win32_is_nt(void); /* os_win.c */ +SQLITE_API void sqlcipher_sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */ + +static int winMutexInit(void){ + /* The first to increment to 1 does actual initialization */ + if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){ + int i; + for(i=0; i +**
  • SQLITE_MUTEX_FAST +**
  • SQLITE_MUTEX_RECURSIVE +**
  • SQLITE_MUTEX_STATIC_MAIN +**
  • SQLITE_MUTEX_STATIC_MEM +**
  • SQLITE_MUTEX_STATIC_OPEN +**
  • SQLITE_MUTEX_STATIC_PRNG +**
  • SQLITE_MUTEX_STATIC_LRU +**
  • SQLITE_MUTEX_STATIC_PMEM +**
  • SQLITE_MUTEX_STATIC_APP1 +**
  • SQLITE_MUTEX_STATIC_APP2 +**
  • SQLITE_MUTEX_STATIC_APP3 +**
  • SQLITE_MUTEX_STATIC_VFS1 +**
  • SQLITE_MUTEX_STATIC_VFS2 +**
  • SQLITE_MUTEX_STATIC_VFS3 +** ** -** 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. +** The first two constants cause sqlcipher_sqlite3_mutex_alloc() to create +** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE +** is used but not necessarily so when SQLITE_MUTEX_FAST is used. +** The mutex implementation does not need to make a distinction +** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does +** not want to. But SQLite will only request a recursive mutex in +** cases where it really needs one. If a faster non-recursive mutex +** implementation is available on the host platform, the mutex subsystem +** might return such a mutex in response to SQLITE_MUTEX_FAST. ** -************************************************************************* +** The other allowed parameters to sqlcipher_sqlite3_mutex_alloc() each return +** a pointer to a static preexisting mutex. Six static mutexes are +** used by the current version of SQLite. Future versions of SQLite +** may add additional static mutexes. Static mutexes are for internal +** use by SQLite only. Applications that use SQLite mutexes should +** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or +** SQLITE_MUTEX_RECURSIVE. ** -** This file contains a no-op memory allocation drivers for use when -** SQLITE_ZERO_MALLOC is defined. The allocation drivers implemented -** here always fail. SQLite will not operate with these drivers. These -** are merely placeholders. Real drivers must be substituted using -** sqlcipher_sqlite3_config() before SQLite will operate. +** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST +** or SQLITE_MUTEX_RECURSIVE) is used then sqlcipher_sqlite3_mutex_alloc() +** returns a different mutex on every call. But for the static +** mutex types, the same mutex is returned on every call that has +** the same type number. */ -/* #include "sqliteInt.h" */ +static sqlcipher_sqlite3_mutex *winMutexAlloc(int iType){ + sqlcipher_sqlite3_mutex *p; + + switch( iType ){ + case SQLITE_MUTEX_FAST: + case SQLITE_MUTEX_RECURSIVE: { + p = sqlcipher_sqlite3MallocZero( sizeof(*p) ); + if( p ){ + p->id = iType; +#ifdef SQLITE_DEBUG +#ifdef SQLITE_WIN32_MUTEX_TRACE_DYNAMIC + p->trace = 1; +#endif +#endif +#if SQLITE_OS_WINRT + InitializeCriticalSectionEx(&p->mutex, 0, 0); +#else + InitializeCriticalSection(&p->mutex); +#endif + } + break; + } + default: { +#ifdef SQLITE_ENABLE_API_ARMOR + if( iType-2<0 || iType-2>=ArraySize(winMutex_staticMutexes) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif + p = &winMutex_staticMutexes[iType-2]; +#ifdef SQLITE_DEBUG +#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC + InterlockedCompareExchange(&p->trace, 1, 0); +#endif +#endif + break; + } + } + assert( p==0 || p->id==iType ); + return p; +} + /* -** This version of the memory allocator is the default. It is -** used when no other memory allocator is specified using compile-time -** macros. +** This routine deallocates a previously +** allocated mutex. SQLite is careful to deallocate every +** mutex that it allocates. */ -#ifdef SQLITE_ZERO_MALLOC +static void winMutexFree(sqlcipher_sqlite3_mutex *p){ + assert( p ); + assert( p->nRef==0 && p->owner==0 ); + if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ){ + DeleteCriticalSection(&p->mutex); + sqlcipher_sqlite3_free(p); + }else{ +#ifdef SQLITE_ENABLE_API_ARMOR + (void)SQLITE_MISUSE_BKPT; +#endif + } +} /* -** No-op versions of all memory allocation routines +** The sqlcipher_sqlite3_mutex_enter() and sqlcipher_sqlite3_mutex_try() routines attempt +** to enter a mutex. If another thread is already within the mutex, +** sqlcipher_sqlite3_mutex_enter() will block and sqlcipher_sqlite3_mutex_try() will return +** SQLITE_BUSY. The sqlcipher_sqlite3_mutex_try() interface returns SQLITE_OK +** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can +** be entered multiple times by the same thread. In such cases the, +** mutex must be exited an equal number of times before another thread +** can enter. If the same thread tries to enter any other kind of mutex +** more than once, the behavior is undefined. */ -static void *sqlcipher_sqlite3MemMalloc(int nByte){ return 0; } -static void sqlcipher_sqlite3MemFree(void *pPrior){ return; } -static void *sqlcipher_sqlite3MemRealloc(void *pPrior, int nByte){ return 0; } -static int sqlcipher_sqlite3MemSize(void *pPrior){ return 0; } -static int sqlcipher_sqlite3MemRoundup(int n){ return n; } -static int sqlcipher_sqlite3MemInit(void *NotUsed){ return SQLITE_OK; } -static void sqlcipher_sqlite3MemShutdown(void *NotUsed){ return; } +static void winMutexEnter(sqlcipher_sqlite3_mutex *p){ +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) + DWORD tid = GetCurrentThreadId(); +#endif +#ifdef SQLITE_DEBUG + assert( p ); + assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); +#else + assert( p ); +#endif + assert( winMutex_isInit==1 ); + EnterCriticalSection(&p->mutex); +#ifdef SQLITE_DEBUG + assert( p->nRef>0 || p->owner==0 ); + p->owner = tid; + p->nRef++; + if( p->trace ){ + OSTRACE(("ENTER-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n", + tid, p->id, p, p->trace, p->nRef)); + } +#endif +} + +static int winMutexTry(sqlcipher_sqlite3_mutex *p){ +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) + DWORD tid = GetCurrentThreadId(); +#endif + int rc = SQLITE_BUSY; + assert( p ); + assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); + /* + ** The sqlcipher_sqlite3_mutex_try() routine is very rarely used, and when it + ** is used it is merely an optimization. So it is OK for it to always + ** fail. + ** + ** The TryEnterCriticalSection() interface is only available on WinNT. + ** And some windows compilers complain if you try to use it without + ** first doing some #defines that prevent SQLite from building on Win98. + ** For that reason, we will omit this optimization for now. See + ** ticket #2685. + */ +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400 + assert( winMutex_isInit==1 ); + assert( winMutex_isNt>=-1 && winMutex_isNt<=1 ); + if( winMutex_isNt<0 ){ + winMutex_isNt = sqlcipher_sqlite3_win32_is_nt(); + } + assert( winMutex_isNt==0 || winMutex_isNt==1 ); + if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){ +#ifdef SQLITE_DEBUG + p->owner = tid; + p->nRef++; +#endif + rc = SQLITE_OK; + } +#else + UNUSED_PARAMETER(p); +#endif +#ifdef SQLITE_DEBUG + if( p->trace ){ + OSTRACE(("TRY-MUTEX tid=%lu, mutex(%d)=%p (%d), owner=%lu, nRef=%d, rc=%s\n", + tid, p->id, p, p->trace, p->owner, p->nRef, sqlcipher_sqlite3ErrName(rc))); + } +#endif + return rc; +} /* -** This routine is the only routine in this file with external linkage. -** -** Populate the low-level memory allocation function pointers in -** sqlcipher_sqlite3GlobalConfig.m with pointers to the routines in this file. +** The sqlcipher_sqlite3_mutex_leave() routine exits a mutex that was +** previously entered by the same thread. The behavior +** is undefined if the mutex is not currently entered or +** is not currently allocated. SQLite will never do either. */ -SQLITE_PRIVATE void sqlcipher_sqlite3MemSetDefault(void){ - static const sqlcipher_sqlite3_mem_methods defaultMethods = { - sqlcipher_sqlite3MemMalloc, - sqlcipher_sqlite3MemFree, - sqlcipher_sqlite3MemRealloc, - sqlcipher_sqlite3MemSize, - sqlcipher_sqlite3MemRoundup, - sqlcipher_sqlite3MemInit, - sqlcipher_sqlite3MemShutdown, - 0 +static void winMutexLeave(sqlcipher_sqlite3_mutex *p){ +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) + DWORD tid = GetCurrentThreadId(); +#endif + assert( p ); +#ifdef SQLITE_DEBUG + assert( p->nRef>0 ); + assert( p->owner==tid ); + p->nRef--; + if( p->nRef==0 ) p->owner = 0; + assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); +#endif + assert( winMutex_isInit==1 ); + LeaveCriticalSection(&p->mutex); +#ifdef SQLITE_DEBUG + if( p->trace ){ + OSTRACE(("LEAVE-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n", + tid, p->id, p, p->trace, p->nRef)); + } +#endif +} + +SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3DefaultMutex(void){ + static const sqlcipher_sqlite3_mutex_methods sMutex = { + winMutexInit, + winMutexEnd, + winMutexAlloc, + winMutexFree, + winMutexEnter, + winMutexTry, + winMutexLeave, +#ifdef SQLITE_DEBUG + winMutexHeld, + winMutexNotheld +#else + 0, + 0 +#endif }; - sqlcipher_sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); + return &sMutex; } -#endif /* SQLITE_ZERO_MALLOC */ +#endif /* SQLITE_MUTEX_W32 */ -/************** End of mem0.c ************************************************/ -/************** Begin file mem1.c ********************************************/ +/************** End of mutex_w32.c *******************************************/ +/************** Begin file malloc.c ******************************************/ /* -** 2007 August 14 +** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -28811,2090 +28894,2244 @@ SQLITE_PRIVATE void sqlcipher_sqlite3MemSetDefault(void){ ** ************************************************************************* ** -** This file contains low-level memory allocation drivers for when -** SQLite will use the standard C-library malloc/realloc/free interface -** to obtain the memory it needs. -** -** This file contains implementations of the low-level memory allocation -** routines specified in the sqlcipher_sqlite3_mem_methods object. The content of -** this file is only used if SQLITE_SYSTEM_MALLOC is defined. The -** SQLITE_SYSTEM_MALLOC macro is defined automatically if neither the -** SQLITE_MEMDEBUG nor the SQLITE_WIN32_MALLOC macros are defined. The -** default configuration is to use memory allocation routines in this -** file. -** -** C-preprocessor macro summary: -** -** HAVE_MALLOC_USABLE_SIZE The configure script sets this symbol if -** the malloc_usable_size() interface exists -** on the target platform. Or, this symbol -** can be set manually, if desired. -** If an equivalent interface exists by -** a different name, using a separate -D -** option to rename it. -** -** SQLITE_WITHOUT_ZONEMALLOC Some older macs lack support for the zone -** memory allocator. Set this symbol to enable -** building on older macs. -** -** SQLITE_WITHOUT_MSIZE Set this symbol to disable the use of -** _msize() on windows systems. This might -** be necessary when compiling for Delphi, -** for example. +** Memory allocation functions used throughout sqlite. */ /* #include "sqliteInt.h" */ +/* #include */ /* -** This version of the memory allocator is the default. It is -** used when no other memory allocator is specified using compile-time -** macros. +** Attempt to release up to n bytes of non-essential memory currently +** held by SQLite. An example of non-essential memory is memory used to +** cache database pages that are not currently in use. */ -#ifdef SQLITE_SYSTEM_MALLOC -#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC) +SQLITE_API int sqlcipher_sqlite3_release_memory(int n){ +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + return sqlcipher_sqlite3PcacheReleaseMemory(n); +#else + /* IMPLEMENTATION-OF: R-34391-24921 The sqlcipher_sqlite3_release_memory() routine + ** is a no-op returning zero if SQLite is not compiled with + ** SQLITE_ENABLE_MEMORY_MANAGEMENT. */ + UNUSED_PARAMETER(n); + return 0; +#endif +} /* -** Use the zone allocator available on apple products unless the -** SQLITE_WITHOUT_ZONEMALLOC symbol is defined. +** Default value of the hard heap limit. 0 means "no limit". */ -#include -#include -#ifdef SQLITE_MIGHT_BE_SINGLE_CORE -#include -#endif /* SQLITE_MIGHT_BE_SINGLE_CORE */ -static malloc_zone_t* _sqliteZone_; -#define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x)) -#define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x)); -#define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y)) -#define SQLITE_MALLOCSIZE(x) \ - (_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x)) - -#else /* if not __APPLE__ */ +#ifndef SQLITE_MAX_MEMORY +# define SQLITE_MAX_MEMORY 0 +#endif /* -** Use standard C library malloc and free on non-Apple systems. -** Also used by Apple systems if SQLITE_WITHOUT_ZONEMALLOC is defined. +** State information local to the memory allocation subsystem. */ -#define SQLITE_MALLOC(x) malloc(x) -#define SQLITE_FREE(x) free(x) -#define SQLITE_REALLOC(x,y) realloc((x),(y)) +static SQLITE_WSD struct Mem0Global { + sqlcipher_sqlite3_mutex *mutex; /* Mutex to serialize access */ + sqlcipher_sqlite3_int64 alarmThreshold; /* The soft heap limit */ + sqlcipher_sqlite3_int64 hardLimit; /* The hard upper bound on memory */ + + /* + ** True if heap is nearly "full" where "full" is defined by the + ** sqlcipher_sqlite3_soft_heap_limit() setting. + */ + int nearlyFull; +} mem0 = { 0, SQLITE_MAX_MEMORY, SQLITE_MAX_MEMORY, 0 }; + +#define mem0 GLOBAL(struct Mem0Global, mem0) /* -** The malloc.h header file is needed for malloc_usable_size() function -** on some systems (e.g. Linux). -*/ -#if HAVE_MALLOC_H && HAVE_MALLOC_USABLE_SIZE -# define SQLITE_USE_MALLOC_H 1 -# define SQLITE_USE_MALLOC_USABLE_SIZE 1 -/* -** The MSVCRT has malloc_usable_size(), but it is called _msize(). The -** use of _msize() is automatic, but can be disabled by compiling with -** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires -** the malloc.h header file. +** Return the memory allocator mutex. sqlcipher_sqlite3_status() needs it. */ -#elif defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE) -# define SQLITE_USE_MALLOC_H -# define SQLITE_USE_MSIZE -#endif +SQLITE_PRIVATE sqlcipher_sqlite3_mutex *sqlcipher_sqlite3MallocMutex(void){ + return mem0.mutex; +} +#ifndef SQLITE_OMIT_DEPRECATED /* -** Include the malloc.h header file, if necessary. Also set define macro -** SQLITE_MALLOCSIZE to the appropriate function name, which is _msize() -** for MSVC and malloc_usable_size() for most other systems (e.g. Linux). -** The memory size function can always be overridden manually by defining -** the macro SQLITE_MALLOCSIZE to the desired function name. +** Deprecated external interface. It used to set an alarm callback +** that was invoked when memory usage grew too large. Now it is a +** no-op. */ -#if defined(SQLITE_USE_MALLOC_H) -# include -# if defined(SQLITE_USE_MALLOC_USABLE_SIZE) -# if !defined(SQLITE_MALLOCSIZE) -# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x) -# endif -# elif defined(SQLITE_USE_MSIZE) -# if !defined(SQLITE_MALLOCSIZE) -# define SQLITE_MALLOCSIZE _msize -# endif -# endif -#endif /* defined(SQLITE_USE_MALLOC_H) */ - -#endif /* __APPLE__ or not __APPLE__ */ +SQLITE_API int sqlcipher_sqlite3_memory_alarm( + void(*xCallback)(void *pArg, sqlcipher_sqlite3_int64 used,int N), + void *pArg, + sqlcipher_sqlite3_int64 iThreshold +){ + (void)xCallback; + (void)pArg; + (void)iThreshold; + return SQLITE_OK; +} +#endif /* -** Like malloc(), but remember the size of the allocation -** so that we can find it later using sqlcipher_sqlite3MemSize(). +** Set the soft heap-size limit for the library. An argument of +** zero disables the limit. A negative argument is a no-op used to +** obtain the return value. ** -** For this low-level routine, we are guaranteed that nByte>0 because -** cases of nByte<=0 will be intercepted and dealt with by higher level -** routines. +** The return value is the value of the heap limit just before this +** interface was called. +** +** If the hard heap limit is enabled, then the soft heap limit cannot +** be disabled nor raised above the hard heap limit. */ -static void *sqlcipher_sqlite3MemMalloc(int nByte){ -#ifdef SQLITE_MALLOCSIZE - void *p; - testcase( ROUND8(nByte)==nByte ); - p = SQLITE_MALLOC( nByte ); - if( p==0 ){ - testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); - sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte); +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_soft_heap_limit64(sqlcipher_sqlite3_int64 n){ + sqlcipher_sqlite3_int64 priorLimit; + sqlcipher_sqlite3_int64 excess; + sqlcipher_sqlite3_int64 nUsed; +#ifndef SQLITE_OMIT_AUTOINIT + int rc = sqlcipher_sqlite3_initialize(); + if( rc ) return -1; +#endif + sqlcipher_sqlite3_mutex_enter(mem0.mutex); + priorLimit = mem0.alarmThreshold; + if( n<0 ){ + sqlcipher_sqlite3_mutex_leave(mem0.mutex); + return priorLimit; } - return p; -#else - sqlcipher_sqlite3_int64 *p; - assert( nByte>0 ); - testcase( ROUND8(nByte)!=nByte ); - p = SQLITE_MALLOC( nByte+8 ); - if( p ){ - p[0] = nByte; - p++; - }else{ - testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); - sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte); + if( mem0.hardLimit>0 && (n>mem0.hardLimit || n==0) ){ + n = mem0.hardLimit; } - return (void *)p; -#endif + mem0.alarmThreshold = n; + nUsed = sqlcipher_sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); + AtomicStore(&mem0.nearlyFull, n>0 && n<=nUsed); + sqlcipher_sqlite3_mutex_leave(mem0.mutex); + excess = sqlcipher_sqlite3_memory_used() - n; + if( excess>0 ) sqlcipher_sqlite3_release_memory((int)(excess & 0x7fffffff)); + return priorLimit; +} +SQLITE_API void sqlcipher_sqlite3_soft_heap_limit(int n){ + if( n<0 ) n = 0; + sqlcipher_sqlite3_soft_heap_limit64(n); } /* -** Like free() but works for allocations obtained from sqlcipher_sqlite3MemMalloc() -** or sqlcipher_sqlite3MemRealloc(). +** Set the hard heap-size limit for the library. An argument of zero +** disables the hard heap limit. A negative argument is a no-op used +** to obtain the return value without affecting the hard heap limit. ** -** For this low-level routine, we already know that pPrior!=0 since -** cases where pPrior==0 will have been intecepted and dealt with -** by higher-level routines. +** The return value is the value of the hard heap limit just prior to +** calling this interface. +** +** Setting the hard heap limit will also activate the soft heap limit +** and constrain the soft heap limit to be no more than the hard heap +** limit. */ -static void sqlcipher_sqlite3MemFree(void *pPrior){ -#ifdef SQLITE_MALLOCSIZE - SQLITE_FREE(pPrior); -#else - sqlcipher_sqlite3_int64 *p = (sqlcipher_sqlite3_int64*)pPrior; - assert( pPrior!=0 ); - p--; - SQLITE_FREE(p); +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_hard_heap_limit64(sqlcipher_sqlite3_int64 n){ + sqlcipher_sqlite3_int64 priorLimit; +#ifndef SQLITE_OMIT_AUTOINIT + int rc = sqlcipher_sqlite3_initialize(); + if( rc ) return -1; #endif + sqlcipher_sqlite3_mutex_enter(mem0.mutex); + priorLimit = mem0.hardLimit; + if( n>=0 ){ + mem0.hardLimit = n; + if( n0 because -** cases where nByte<=0 will have been intercepted by higher-level -** routines and redirected to xFree. +** Initialize the memory allocation subsystem. */ -static void *sqlcipher_sqlite3MemRealloc(void *pPrior, int nByte){ -#ifdef SQLITE_MALLOCSIZE - void *p = SQLITE_REALLOC(pPrior, nByte); - if( p==0 ){ - testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); - sqlcipher_sqlite3_log(SQLITE_NOMEM, - "failed memory resize %u to %u bytes", - SQLITE_MALLOCSIZE(pPrior), nByte); - } - return p; -#else - sqlcipher_sqlite3_int64 *p = (sqlcipher_sqlite3_int64*)pPrior; - assert( pPrior!=0 && nByte>0 ); - assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */ - p--; - p = SQLITE_REALLOC(p, nByte+8 ); - if( p ){ - p[0] = nByte; - p++; - }else{ - testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); - sqlcipher_sqlite3_log(SQLITE_NOMEM, - "failed memory resize %u to %u bytes", - sqlcipher_sqlite3MemSize(pPrior), nByte); +SQLITE_PRIVATE int sqlcipher_sqlite3MallocInit(void){ + int rc; + if( sqlcipher_sqlite3GlobalConfig.m.xMalloc==0 ){ + sqlcipher_sqlite3MemSetDefault(); + } + mem0.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); + if( sqlcipher_sqlite3GlobalConfig.pPage==0 || sqlcipher_sqlite3GlobalConfig.szPage<512 + || sqlcipher_sqlite3GlobalConfig.nPage<=0 ){ + sqlcipher_sqlite3GlobalConfig.pPage = 0; + sqlcipher_sqlite3GlobalConfig.szPage = 0; + } + rc = sqlcipher_sqlite3GlobalConfig.m.xInit(sqlcipher_sqlite3GlobalConfig.m.pAppData); + if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0)); +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + /* install wrapping functions for memory management + that will wipe all memory allocated by SQLite + when freed */ + if( rc==SQLITE_OK ) { + extern void sqlcipher_init_memmethods(void); + sqlcipher_init_memmethods(); } - return (void*)p; #endif +/* END SQLCIPHER */ + return rc; } /* -** Round up a request size to the next valid allocation size. +** Return true if the heap is currently under memory pressure - in other +** words if the amount of heap used is close to the limit set by +** sqlcipher_sqlite3_soft_heap_limit(). */ -static int sqlcipher_sqlite3MemRoundup(int n){ - return ROUND8(n); +SQLITE_PRIVATE int sqlcipher_sqlite3HeapNearlyFull(void){ + return AtomicLoad(&mem0.nearlyFull); } /* -** Initialize this module. +** Deinitialize the memory allocation subsystem. */ -static int sqlcipher_sqlite3MemInit(void *NotUsed){ -#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC) - int cpuCount; - size_t len; - if( _sqliteZone_ ){ - return SQLITE_OK; - } - len = sizeof(cpuCount); - /* One usually wants to use hw.acctivecpu for MT decisions, but not here */ - sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0); - if( cpuCount>1 ){ - /* defer MT decisions to system malloc */ - _sqliteZone_ = malloc_default_zone(); - }else{ - /* only 1 core, use our own zone to contention over global locks, - ** e.g. we have our own dedicated locks */ - _sqliteZone_ = malloc_create_zone(4096, 0); - malloc_set_zone_name(_sqliteZone_, "Sqlite_Heap"); +SQLITE_PRIVATE void sqlcipher_sqlite3MallocEnd(void){ + if( sqlcipher_sqlite3GlobalConfig.m.xShutdown ){ + sqlcipher_sqlite3GlobalConfig.m.xShutdown(sqlcipher_sqlite3GlobalConfig.m.pAppData); } -#endif /* defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC) */ - UNUSED_PARAMETER(NotUsed); - return SQLITE_OK; + memset(&mem0, 0, sizeof(mem0)); } /* -** Deinitialize this module. +** Return the amount of memory currently checked out. */ -static void sqlcipher_sqlite3MemShutdown(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - return; +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_memory_used(void){ + sqlcipher_sqlite3_int64 res, mx; + sqlcipher_sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, 0); + return res; } /* -** This routine is the only routine in this file with external linkage. -** -** Populate the low-level memory allocation function pointers in -** sqlcipher_sqlite3GlobalConfig.m with pointers to the routines in this file. +** Return the maximum amount of memory that has ever been +** checked out since either the beginning of this process +** or since the most recent reset. */ -SQLITE_PRIVATE void sqlcipher_sqlite3MemSetDefault(void){ - static const sqlcipher_sqlite3_mem_methods defaultMethods = { - sqlcipher_sqlite3MemMalloc, - sqlcipher_sqlite3MemFree, - sqlcipher_sqlite3MemRealloc, - sqlcipher_sqlite3MemSize, - sqlcipher_sqlite3MemRoundup, - sqlcipher_sqlite3MemInit, - sqlcipher_sqlite3MemShutdown, - 0 - }; - sqlcipher_sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_memory_highwater(int resetFlag){ + sqlcipher_sqlite3_int64 res, mx; + sqlcipher_sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, resetFlag); + return mx; } -#endif /* SQLITE_SYSTEM_MALLOC */ - -/************** End of mem1.c ************************************************/ -/************** Begin file mem2.c ********************************************/ /* -** 2007 August 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 low-level memory allocation drivers for when -** SQLite will use the standard C-library malloc/realloc/free interface -** to obtain the memory it needs while adding lots of additional debugging -** information to each allocation in order to help detect and fix memory -** leaks and memory usage errors. -** -** This file contains implementations of the low-level memory allocation -** routines specified in the sqlcipher_sqlite3_mem_methods object. +** Trigger the alarm */ -/* #include "sqliteInt.h" */ +static void sqlcipher_sqlite3MallocAlarm(int nByte){ + if( mem0.alarmThreshold<=0 ) return; + sqlcipher_sqlite3_mutex_leave(mem0.mutex); + sqlcipher_sqlite3_release_memory(nByte); + sqlcipher_sqlite3_mutex_enter(mem0.mutex); +} /* -** This version of the memory allocator is used only if the -** SQLITE_MEMDEBUG macro is defined +** Do a memory allocation with statistics and alarms. Assume the +** lock is already held. */ -#ifdef SQLITE_MEMDEBUG +static void mallocWithAlarm(int n, void **pp){ + void *p; + int nFull; + assert( sqlcipher_sqlite3_mutex_held(mem0.mutex) ); + assert( n>0 ); -/* -** The backtrace functionality is only available with GLIBC -*/ -#ifdef __GLIBC__ - extern int backtrace(void**,int); - extern void backtrace_symbols_fd(void*const*,int,int); -#else -# define backtrace(A,B) 1 -# define backtrace_symbols_fd(A,B,C) + /* In Firefox (circa 2017-02-08), xRoundup() is remapped to an internal + ** implementation of malloc_good_size(), which must be called in debug + ** mode and specifically when the DMD "Dark Matter Detector" is enabled + ** or else a crash results. Hence, do not attempt to optimize out the + ** following xRoundup() call. */ + nFull = sqlcipher_sqlite3GlobalConfig.m.xRoundup(n); + + sqlcipher_sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n); + if( mem0.alarmThreshold>0 ){ + sqlcipher_sqlite3_int64 nUsed = sqlcipher_sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); + if( nUsed >= mem0.alarmThreshold - nFull ){ + AtomicStore(&mem0.nearlyFull, 1); + sqlcipher_sqlite3MallocAlarm(nFull); + if( mem0.hardLimit ){ + nUsed = sqlcipher_sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); + if( nUsed >= mem0.hardLimit - nFull ){ + *pp = 0; + return; + } + } + }else{ + AtomicStore(&mem0.nearlyFull, 0); + } + } + p = sqlcipher_sqlite3GlobalConfig.m.xMalloc(nFull); +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + if( p==0 && mem0.alarmThreshold>0 ){ + sqlcipher_sqlite3MallocAlarm(nFull); + p = sqlcipher_sqlite3GlobalConfig.m.xMalloc(nFull); + } #endif -/* #include */ + if( p ){ + nFull = sqlcipher_sqlite3MallocSize(p); + sqlcipher_sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nFull); + sqlcipher_sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1); + } + *pp = p; +} /* -** Each memory allocation looks like this: -** -** ------------------------------------------------------------------------ -** | Title | backtrace pointers | MemBlockHdr | allocation | EndGuard | -** ------------------------------------------------------------------------ -** -** The application code sees only a pointer to the allocation. We have -** to back up from the allocation pointer to find the MemBlockHdr. The -** MemBlockHdr tells us the size of the allocation and the number of -** backtrace pointers. There is also a guard word at the end of the -** MemBlockHdr. +** Allocate memory. This routine is like sqlcipher_sqlite3_malloc() except that it +** assumes the memory subsystem has already been initialized. */ -struct MemBlockHdr { - i64 iSize; /* Size of this allocation */ - struct MemBlockHdr *pNext, *pPrev; /* Linked list of all unfreed memory */ - char nBacktrace; /* Number of backtraces on this alloc */ - char nBacktraceSlots; /* Available backtrace slots */ - u8 nTitle; /* Bytes of title; includes '\0' */ - u8 eType; /* Allocation type code */ - int iForeGuard; /* Guard word for sanity */ -}; +SQLITE_PRIVATE void *sqlcipher_sqlite3Malloc(u64 n){ + void *p; + if( n==0 || n>=0x7fffff00 ){ + /* A memory allocation of a number of bytes which is near the maximum + ** signed integer value might cause an integer overflow inside of the + ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving + ** 255 bytes of overhead. SQLite itself will never use anything near + ** this amount. The only way to reach the limit is with sqlcipher_sqlite3_malloc() */ + p = 0; + }else if( sqlcipher_sqlite3GlobalConfig.bMemstat ){ + sqlcipher_sqlite3_mutex_enter(mem0.mutex); + mallocWithAlarm((int)n, &p); + sqlcipher_sqlite3_mutex_leave(mem0.mutex); + }else{ + p = sqlcipher_sqlite3GlobalConfig.m.xMalloc((int)n); + } + assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-11148-40995 */ + return p; +} /* -** Guard words +** This version of the memory allocation is for use by the application. +** First make sure the memory subsystem is initialized, then do the +** allocation. */ -#define FOREGUARD 0x80F5E153 -#define REARGUARD 0xE4676B53 +SQLITE_API void *sqlcipher_sqlite3_malloc(int n){ +#ifndef SQLITE_OMIT_AUTOINIT + if( sqlcipher_sqlite3_initialize() ) return 0; +#endif + return n<=0 ? 0 : sqlcipher_sqlite3Malloc(n); +} +SQLITE_API void *sqlcipher_sqlite3_malloc64(sqlcipher_sqlite3_uint64 n){ +#ifndef SQLITE_OMIT_AUTOINIT + if( sqlcipher_sqlite3_initialize() ) return 0; +#endif + return sqlcipher_sqlite3Malloc(n); +} /* -** Number of malloc size increments to track. +** TRUE if p is a lookaside memory allocation from db */ -#define NCSIZE 1000 +#ifndef SQLITE_OMIT_LOOKASIDE +static int isLookaside(sqlcipher_sqlite3 *db, const void *p){ + return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd); +} +#else +#define isLookaside(A,B) 0 +#endif /* -** All of the static variables used by this module are collected -** into a single structure named "mem". This is to keep the -** static variables organized and to reduce namespace pollution -** when this module is combined with other in the amalgamation. +** Return the size of a memory allocation previously obtained from +** sqlcipher_sqlite3Malloc() or sqlcipher_sqlite3_malloc(). */ -static struct { - - /* - ** Mutex to control access to the memory allocation subsystem. - */ - sqlcipher_sqlite3_mutex *mutex; - - /* - ** Head and tail of a linked list of all outstanding allocations - */ - struct MemBlockHdr *pFirst; - struct MemBlockHdr *pLast; - - /* - ** The number of levels of backtrace to save in new allocations. - */ - int nBacktrace; - void (*xBacktrace)(int, int, void **); - - /* - ** Title text to insert in front of each block - */ - int nTitle; /* Bytes of zTitle to save. Includes '\0' and padding */ - char zTitle[100]; /* The title text */ - - /* - ** sqlcipher_sqlite3MallocDisallow() increments the following counter. - ** sqlcipher_sqlite3MallocAllow() decrements it. - */ - int disallow; /* Do not allow memory allocation */ - - /* - ** Gather statistics on the sizes of memory allocations. - ** nAlloc[i] is the number of allocation attempts of i*8 - ** bytes. i==NCSIZE is the number of allocation attempts for - ** sizes more than NCSIZE*8 bytes. - */ - int nAlloc[NCSIZE]; /* Total number of allocations */ - int nCurrent[NCSIZE]; /* Current number of allocations */ - int mxCurrent[NCSIZE]; /* Highwater mark for nCurrent */ - -} mem; - +SQLITE_PRIVATE int sqlcipher_sqlite3MallocSize(const void *p){ + assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); + return sqlcipher_sqlite3GlobalConfig.m.xSize((void*)p); +} +static int lookasideMallocSize(sqlcipher_sqlite3 *db, const void *p){ +#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + return plookaside.pMiddle ? db->lookaside.szTrue : LOOKASIDE_SMALL; +#else + return db->lookaside.szTrue; +#endif +} +SQLITE_PRIVATE int sqlcipher_sqlite3DbMallocSize(sqlcipher_sqlite3 *db, const void *p){ + assert( p!=0 ); +#ifdef SQLITE_DEBUG + if( db==0 || !isLookaside(db,p) ){ + if( db==0 ){ + assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); + assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); + }else{ + assert( sqlcipher_sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + } + } +#endif + if( db ){ + if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ +#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ + assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); + return LOOKASIDE_SMALL; + } +#endif + if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){ + assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); + return db->lookaside.szTrue; + } + } + } + return sqlcipher_sqlite3GlobalConfig.m.xSize((void*)p); +} +SQLITE_API sqlcipher_sqlite3_uint64 sqlcipher_sqlite3_msize(void *p){ + assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); + assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); + return p ? sqlcipher_sqlite3GlobalConfig.m.xSize(p) : 0; +} /* -** Adjust memory usage statistics +** Free memory previously obtained from sqlcipher_sqlite3Malloc(). */ -static void adjustStats(int iSize, int increment){ - int i = ROUND8(iSize)/8; - if( i>NCSIZE-1 ){ - i = NCSIZE - 1; - } - if( increment>0 ){ - mem.nAlloc[i]++; - mem.nCurrent[i]++; - if( mem.nCurrent[i]>mem.mxCurrent[i] ){ - mem.mxCurrent[i] = mem.nCurrent[i]; - } +SQLITE_API void sqlcipher_sqlite3_free(void *p){ + if( p==0 ) return; /* IMP: R-49053-54554 */ + assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); + assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); + if( sqlcipher_sqlite3GlobalConfig.bMemstat ){ + sqlcipher_sqlite3_mutex_enter(mem0.mutex); + sqlcipher_sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, sqlcipher_sqlite3MallocSize(p)); + sqlcipher_sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1); + sqlcipher_sqlite3GlobalConfig.m.xFree(p); + sqlcipher_sqlite3_mutex_leave(mem0.mutex); }else{ - mem.nCurrent[i]--; - assert( mem.nCurrent[i]>=0 ); + sqlcipher_sqlite3GlobalConfig.m.xFree(p); } } /* -** Given an allocation, find the MemBlockHdr for that allocation. -** -** This routine checks the guards at either end of the allocation and -** if they are incorrect it asserts. +** Add the size of memory allocation "p" to the count in +** *db->pnBytesFreed. */ -static struct MemBlockHdr *sqlcipher_sqlite3MemsysGetHeader(void *pAllocation){ - struct MemBlockHdr *p; - int *pInt; - u8 *pU8; - int nReserve; - - p = (struct MemBlockHdr*)pAllocation; - p--; - assert( p->iForeGuard==(int)FOREGUARD ); - nReserve = ROUND8(p->iSize); - pInt = (int*)pAllocation; - pU8 = (u8*)pAllocation; - assert( pInt[nReserve/sizeof(int)]==(int)REARGUARD ); - /* This checks any of the "extra" bytes allocated due - ** to rounding up to an 8 byte boundary to ensure - ** they haven't been overwritten. - */ - while( nReserve-- > p->iSize ) assert( pU8[nReserve]==0x65 ); - return p; +static SQLITE_NOINLINE void measureAllocationSize(sqlcipher_sqlite3 *db, void *p){ + *db->pnBytesFreed += sqlcipher_sqlite3DbMallocSize(db,p); } /* -** Return the number of bytes currently allocated at address p. +** Free memory that might be associated with a particular database +** connection. Calling sqlcipher_sqlite3DbFree(D,X) for X==0 is a harmless no-op. +** The sqlcipher_sqlite3DbFreeNN(D,X) version requires that X be non-NULL. */ -static int sqlcipher_sqlite3MemSize(void *p){ - struct MemBlockHdr *pHdr; - if( !p ){ - return 0; +SQLITE_PRIVATE void sqlcipher_sqlite3DbFreeNN(sqlcipher_sqlite3 *db, void *p){ + assert( db==0 || sqlcipher_sqlite3_mutex_held(db->mutex) ); + assert( p!=0 ); + if( db ){ + if( db->pnBytesFreed ){ + measureAllocationSize(db, p); + return; + } + if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ +#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ + LookasideSlot *pBuf = (LookasideSlot*)p; +#ifdef SQLITE_DEBUG + memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */ +#endif + pBuf->pNext = db->lookaside.pSmallFree; + db->lookaside.pSmallFree = pBuf; + return; + } +#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ + if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){ + LookasideSlot *pBuf = (LookasideSlot*)p; +#ifdef SQLITE_DEBUG + memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */ +#endif + pBuf->pNext = db->lookaside.pFree; + db->lookaside.pFree = pBuf; + return; + } + } } - pHdr = sqlcipher_sqlite3MemsysGetHeader(p); - return (int)pHdr->iSize; + assert( sqlcipher_sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( db!=0 || sqlcipher_sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); + sqlcipher_sqlite3MemdebugSetType(p, MEMTYPE_HEAP); + sqlcipher_sqlite3_free(p); +} +SQLITE_PRIVATE void sqlcipher_sqlite3DbFree(sqlcipher_sqlite3 *db, void *p){ + assert( db==0 || sqlcipher_sqlite3_mutex_held(db->mutex) ); + if( p ) sqlcipher_sqlite3DbFreeNN(db, p); } /* -** Initialize the memory allocation subsystem. +** Change the size of an existing memory allocation */ -static int sqlcipher_sqlite3MemInit(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - assert( (sizeof(struct MemBlockHdr)&7) == 0 ); - if( !sqlcipher_sqlite3GlobalConfig.bMemstat ){ - /* If memory status is enabled, then the malloc.c wrapper will already - ** hold the STATIC_MEM mutex when the routines here are invoked. */ - mem.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); +SQLITE_PRIVATE void *sqlcipher_sqlite3Realloc(void *pOld, u64 nBytes){ + int nOld, nNew, nDiff; + void *pNew; + assert( sqlcipher_sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) ); + assert( sqlcipher_sqlite3MemdebugNoType(pOld, (u8)~MEMTYPE_HEAP) ); + if( pOld==0 ){ + return sqlcipher_sqlite3Malloc(nBytes); /* IMP: R-04300-56712 */ } - return SQLITE_OK; + if( nBytes==0 ){ + sqlcipher_sqlite3_free(pOld); /* IMP: R-26507-47431 */ + return 0; + } + if( nBytes>=0x7fffff00 ){ + /* The 0x7ffff00 limit term is explained in comments on sqlcipher_sqlite3Malloc() */ + return 0; + } + nOld = sqlcipher_sqlite3MallocSize(pOld); + /* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second + ** argument to xRealloc is always a value returned by a prior call to + ** xRoundup. */ + nNew = sqlcipher_sqlite3GlobalConfig.m.xRoundup((int)nBytes); + if( nOld==nNew ){ + pNew = pOld; + }else if( sqlcipher_sqlite3GlobalConfig.bMemstat ){ + sqlcipher_sqlite3_int64 nUsed; + sqlcipher_sqlite3_mutex_enter(mem0.mutex); + sqlcipher_sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes); + nDiff = nNew - nOld; + if( nDiff>0 && (nUsed = sqlcipher_sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)) >= + mem0.alarmThreshold-nDiff ){ + sqlcipher_sqlite3MallocAlarm(nDiff); + if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){ + sqlcipher_sqlite3_mutex_leave(mem0.mutex); + return 0; + } + } + pNew = sqlcipher_sqlite3GlobalConfig.m.xRealloc(pOld, nNew); +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + if( pNew==0 && mem0.alarmThreshold>0 ){ + sqlcipher_sqlite3MallocAlarm((int)nBytes); + pNew = sqlcipher_sqlite3GlobalConfig.m.xRealloc(pOld, nNew); + } +#endif + if( pNew ){ + nNew = sqlcipher_sqlite3MallocSize(pNew); + sqlcipher_sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld); + } + sqlcipher_sqlite3_mutex_leave(mem0.mutex); + }else{ + pNew = sqlcipher_sqlite3GlobalConfig.m.xRealloc(pOld, nNew); + } + assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-11148-40995 */ + return pNew; } /* -** Deinitialize the memory allocation subsystem. +** The public interface to sqlcipher_sqlite3Realloc. Make sure that the memory +** subsystem is initialized prior to invoking sqliteRealloc. */ -static void sqlcipher_sqlite3MemShutdown(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - mem.mutex = 0; +SQLITE_API void *sqlcipher_sqlite3_realloc(void *pOld, int n){ +#ifndef SQLITE_OMIT_AUTOINIT + if( sqlcipher_sqlite3_initialize() ) return 0; +#endif + if( n<0 ) n = 0; /* IMP: R-26507-47431 */ + return sqlcipher_sqlite3Realloc(pOld, n); +} +SQLITE_API void *sqlcipher_sqlite3_realloc64(void *pOld, sqlcipher_sqlite3_uint64 n){ +#ifndef SQLITE_OMIT_AUTOINIT + if( sqlcipher_sqlite3_initialize() ) return 0; +#endif + return sqlcipher_sqlite3Realloc(pOld, n); } + /* -** Round up a request size to the next valid allocation size. +** Allocate and zero memory. */ -static int sqlcipher_sqlite3MemRoundup(int n){ - return ROUND8(n); +SQLITE_PRIVATE void *sqlcipher_sqlite3MallocZero(u64 n){ + void *p = sqlcipher_sqlite3Malloc(n); + if( p ){ + memset(p, 0, (size_t)n); + } + return p; } /* -** Fill a buffer with pseudo-random bytes. This is used to preset -** the content of a new memory allocation to unpredictable values and -** to clear the content of a freed allocation to unpredictable values. +** Allocate and zero memory. If the allocation fails, make +** the mallocFailed flag in the connection pointer. */ -static void randomFill(char *pBuf, int nByte){ - unsigned int x, y, r; - x = SQLITE_PTR_TO_INT(pBuf); - y = nByte | 1; - while( nByte >= 4 ){ - x = (x>>1) ^ (-(int)(x&1) & 0xd0000001); - y = y*1103515245 + 12345; - r = x ^ y; - *(int*)pBuf = r; - pBuf += 4; - nByte -= 4; - } - while( nByte-- > 0 ){ - x = (x>>1) ^ (-(int)(x&1) & 0xd0000001); - y = y*1103515245 + 12345; - r = x ^ y; - *(pBuf++) = r & 0xff; - } +SQLITE_PRIVATE void *sqlcipher_sqlite3DbMallocZero(sqlcipher_sqlite3 *db, u64 n){ + void *p; + testcase( db==0 ); + p = sqlcipher_sqlite3DbMallocRaw(db, n); + if( p ) memset(p, 0, (size_t)n); + return p; +} + + +/* Finish the work of sqlcipher_sqlite3DbMallocRawNN for the unusual and +** slower case when the allocation cannot be fulfilled using lookaside. +*/ +static SQLITE_NOINLINE void *dbMallocRawFinish(sqlcipher_sqlite3 *db, u64 n){ + void *p; + assert( db!=0 ); + p = sqlcipher_sqlite3Malloc(n); + if( !p ) sqlcipher_sqlite3OomFault(db); + sqlcipher_sqlite3MemdebugSetType(p, + (db->lookaside.bDisable==0) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP); + return p; } /* -** Allocate nByte bytes of memory. +** Allocate memory, either lookaside (if possible) or heap. +** If the allocation fails, set the mallocFailed flag in +** the connection pointer. +** +** If db!=0 and db->mallocFailed is true (indicating a prior malloc +** failure on the same database connection) then always return 0. +** Hence for a particular database connection, once malloc starts +** failing, it fails consistently until mallocFailed is reset. +** This is an important assumption. There are many places in the +** code that do things like this: +** +** int *a = (int*)sqlcipher_sqlite3DbMallocRaw(db, 100); +** int *b = (int*)sqlcipher_sqlite3DbMallocRaw(db, 200); +** if( b ) a[10] = 9; +** +** In other words, if a subsequent malloc (ex: "b") worked, it is assumed +** that all prior mallocs (ex: "a") worked too. +** +** The sqlcipher_sqlite3MallocRawNN() variant guarantees that the "db" parameter is +** not a NULL pointer. */ -static void *sqlcipher_sqlite3MemMalloc(int nByte){ - struct MemBlockHdr *pHdr; - void **pBt; - char *z; - int *pInt; - void *p = 0; - int totalSize; - int nReserve; - sqlcipher_sqlite3_mutex_enter(mem.mutex); - assert( mem.disallow==0 ); - nReserve = ROUND8(nByte); - totalSize = nReserve + sizeof(*pHdr) + sizeof(int) + - mem.nBacktrace*sizeof(void*) + mem.nTitle; - p = malloc(totalSize); - if( p ){ - z = p; - pBt = (void**)&z[mem.nTitle]; - pHdr = (struct MemBlockHdr*)&pBt[mem.nBacktrace]; - pHdr->pNext = 0; - pHdr->pPrev = mem.pLast; - if( mem.pLast ){ - mem.pLast->pNext = pHdr; - }else{ - mem.pFirst = pHdr; - } - mem.pLast = pHdr; - pHdr->iForeGuard = FOREGUARD; - pHdr->eType = MEMTYPE_HEAP; - pHdr->nBacktraceSlots = mem.nBacktrace; - pHdr->nTitle = mem.nTitle; - if( mem.nBacktrace ){ - void *aAddr[40]; - pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1; - memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*)); - assert(pBt[0]); - if( mem.xBacktrace ){ - mem.xBacktrace(nByte, pHdr->nBacktrace-1, &aAddr[1]); - } - }else{ - pHdr->nBacktrace = 0; +SQLITE_PRIVATE void *sqlcipher_sqlite3DbMallocRaw(sqlcipher_sqlite3 *db, u64 n){ + void *p; + if( db ) return sqlcipher_sqlite3DbMallocRawNN(db, n); + p = sqlcipher_sqlite3Malloc(n); + sqlcipher_sqlite3MemdebugSetType(p, MEMTYPE_HEAP); + return p; +} +SQLITE_PRIVATE void *sqlcipher_sqlite3DbMallocRawNN(sqlcipher_sqlite3 *db, u64 n){ +#ifndef SQLITE_OMIT_LOOKASIDE + LookasideSlot *pBuf; + assert( db!=0 ); + assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); + assert( db->pnBytesFreed==0 ); + if( n>db->lookaside.sz ){ + if( !db->lookaside.bDisable ){ + db->lookaside.anStat[1]++; + }else if( db->mallocFailed ){ + return 0; } - if( mem.nTitle ){ - memcpy(z, mem.zTitle, mem.nTitle); + return dbMallocRawFinish(db, n); + } +#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + if( n<=LOOKASIDE_SMALL ){ + if( (pBuf = db->lookaside.pSmallFree)!=0 ){ + db->lookaside.pSmallFree = pBuf->pNext; + db->lookaside.anStat[0]++; + return (void*)pBuf; + }else if( (pBuf = db->lookaside.pSmallInit)!=0 ){ + db->lookaside.pSmallInit = pBuf->pNext; + db->lookaside.anStat[0]++; + return (void*)pBuf; } - pHdr->iSize = nByte; - adjustStats(nByte, +1); - pInt = (int*)&pHdr[1]; - pInt[nReserve/sizeof(int)] = REARGUARD; - randomFill((char*)pInt, nByte); - memset(((char*)pInt)+nByte, 0x65, nReserve-nByte); - p = (void*)pInt; } - sqlcipher_sqlite3_mutex_leave(mem.mutex); - return p; +#endif + if( (pBuf = db->lookaside.pFree)!=0 ){ + db->lookaside.pFree = pBuf->pNext; + db->lookaside.anStat[0]++; + return (void*)pBuf; + }else if( (pBuf = db->lookaside.pInit)!=0 ){ + db->lookaside.pInit = pBuf->pNext; + db->lookaside.anStat[0]++; + return (void*)pBuf; + }else{ + db->lookaside.anStat[2]++; + } +#else + assert( db!=0 ); + assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); + assert( db->pnBytesFreed==0 ); + if( db->mallocFailed ){ + return 0; + } +#endif + return dbMallocRawFinish(db, n); } +/* Forward declaration */ +static SQLITE_NOINLINE void *dbReallocFinish(sqlcipher_sqlite3 *db, void *p, u64 n); + /* -** Free memory. +** Resize the block of memory pointed to by p to n bytes. If the +** resize fails, set the mallocFailed flag in the connection object. */ -static void sqlcipher_sqlite3MemFree(void *pPrior){ - struct MemBlockHdr *pHdr; - void **pBt; - char *z; - assert( sqlcipher_sqlite3GlobalConfig.bMemstat || sqlcipher_sqlite3GlobalConfig.bCoreMutex==0 - || mem.mutex!=0 ); - pHdr = sqlcipher_sqlite3MemsysGetHeader(pPrior); - pBt = (void**)pHdr; - pBt -= pHdr->nBacktraceSlots; - sqlcipher_sqlite3_mutex_enter(mem.mutex); - if( pHdr->pPrev ){ - assert( pHdr->pPrev->pNext==pHdr ); - pHdr->pPrev->pNext = pHdr->pNext; - }else{ - assert( mem.pFirst==pHdr ); - mem.pFirst = pHdr->pNext; +SQLITE_PRIVATE void *sqlcipher_sqlite3DbRealloc(sqlcipher_sqlite3 *db, void *p, u64 n){ + assert( db!=0 ); + if( p==0 ) return sqlcipher_sqlite3DbMallocRawNN(db, n); + assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); + if( ((uptr)p)<(uptr)db->lookaside.pEnd ){ +#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + if( ((uptr)p)>=(uptr)db->lookaside.pMiddle ){ + if( n<=LOOKASIDE_SMALL ) return p; + }else +#endif + if( ((uptr)p)>=(uptr)db->lookaside.pStart ){ + if( n<=db->lookaside.szTrue ) return p; + } } - if( pHdr->pNext ){ - assert( pHdr->pNext->pPrev==pHdr ); - pHdr->pNext->pPrev = pHdr->pPrev; - }else{ - assert( mem.pLast==pHdr ); - mem.pLast = pHdr->pPrev; + return dbReallocFinish(db, p, n); +} +static SQLITE_NOINLINE void *dbReallocFinish(sqlcipher_sqlite3 *db, void *p, u64 n){ + void *pNew = 0; + assert( db!=0 ); + assert( p!=0 ); + if( db->mallocFailed==0 ){ + if( isLookaside(db, p) ){ + pNew = sqlcipher_sqlite3DbMallocRawNN(db, n); + if( pNew ){ + memcpy(pNew, p, lookasideMallocSize(db, p)); + sqlcipher_sqlite3DbFree(db, p); + } + }else{ + assert( sqlcipher_sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + sqlcipher_sqlite3MemdebugSetType(p, MEMTYPE_HEAP); + pNew = sqlcipher_sqlite3Realloc(p, n); + if( !pNew ){ + sqlcipher_sqlite3OomFault(db); + } + sqlcipher_sqlite3MemdebugSetType(pNew, + (db->lookaside.bDisable==0 ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP)); + } } - z = (char*)pBt; - z -= pHdr->nTitle; - adjustStats((int)pHdr->iSize, -1); - randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) + - (int)pHdr->iSize + sizeof(int) + pHdr->nTitle); - free(z); - sqlcipher_sqlite3_mutex_leave(mem.mutex); + return pNew; } /* -** Change the size of an existing memory allocation. -** -** For this debugging implementation, we *always* make a copy of the -** allocation into a new place in memory. In this way, if the -** higher level code is using pointer to the old allocation, it is -** much more likely to break and we are much more liking to find -** the error. +** Attempt to reallocate p. If the reallocation fails, then free p +** and set the mallocFailed flag in the database connection. */ -static void *sqlcipher_sqlite3MemRealloc(void *pPrior, int nByte){ - struct MemBlockHdr *pOldHdr; +SQLITE_PRIVATE void *sqlcipher_sqlite3DbReallocOrFree(sqlcipher_sqlite3 *db, void *p, u64 n){ void *pNew; - assert( mem.disallow==0 ); - assert( (nByte & 7)==0 ); /* EV: R-46199-30249 */ - pOldHdr = sqlcipher_sqlite3MemsysGetHeader(pPrior); - pNew = sqlcipher_sqlite3MemMalloc(nByte); - if( pNew ){ - memcpy(pNew, pPrior, (int)(nByteiSize ? nByte : pOldHdr->iSize)); - if( nByte>pOldHdr->iSize ){ - randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - (int)pOldHdr->iSize); - } - sqlcipher_sqlite3MemFree(pPrior); + pNew = sqlcipher_sqlite3DbRealloc(db, p, n); + if( !pNew ){ + sqlcipher_sqlite3DbFree(db, p); } return pNew; } /* -** Populate the low-level memory allocation function pointers in -** sqlcipher_sqlite3GlobalConfig.m with pointers to the routines in this file. +** Make a copy of a string in memory obtained from sqliteMalloc(). These +** functions call sqlcipher_sqlite3MallocRaw() directly instead of sqliteMalloc(). This +** is because when memory debugging is turned on, these two functions are +** called via macros that record the current file and line number in the +** ThreadData structure. */ -SQLITE_PRIVATE void sqlcipher_sqlite3MemSetDefault(void){ - static const sqlcipher_sqlite3_mem_methods defaultMethods = { - sqlcipher_sqlite3MemMalloc, - sqlcipher_sqlite3MemFree, - sqlcipher_sqlite3MemRealloc, - sqlcipher_sqlite3MemSize, - sqlcipher_sqlite3MemRoundup, - sqlcipher_sqlite3MemInit, - sqlcipher_sqlite3MemShutdown, - 0 - }; - sqlcipher_sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); +SQLITE_PRIVATE char *sqlcipher_sqlite3DbStrDup(sqlcipher_sqlite3 *db, const char *z){ + char *zNew; + size_t n; + if( z==0 ){ + return 0; + } + n = strlen(z) + 1; + zNew = sqlcipher_sqlite3DbMallocRaw(db, n); + if( zNew ){ + memcpy(zNew, z, n); + } + return zNew; +} +SQLITE_PRIVATE char *sqlcipher_sqlite3DbStrNDup(sqlcipher_sqlite3 *db, const char *z, u64 n){ + char *zNew; + assert( db!=0 ); + assert( z!=0 || n==0 ); + assert( (n&0x7fffffff)==n ); + zNew = z ? sqlcipher_sqlite3DbMallocRawNN(db, n+1) : 0; + if( zNew ){ + memcpy(zNew, z, (size_t)n); + zNew[n] = 0; + } + return zNew; } /* -** Set the "type" of an allocation. +** The text between zStart and zEnd represents a phrase within a larger +** SQL statement. Make a copy of this phrase in space obtained form +** sqlcipher_sqlite3DbMalloc(). Omit leading and trailing whitespace. */ -SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugSetType(void *p, u8 eType){ - if( p && sqlcipher_sqlite3GlobalConfig.m.xFree==sqlcipher_sqlite3MemFree ){ - struct MemBlockHdr *pHdr; - pHdr = sqlcipher_sqlite3MemsysGetHeader(p); - assert( pHdr->iForeGuard==FOREGUARD ); - pHdr->eType = eType; - } +SQLITE_PRIVATE char *sqlcipher_sqlite3DbSpanDup(sqlcipher_sqlite3 *db, const char *zStart, const char *zEnd){ + int n; + while( sqlcipher_sqlite3Isspace(zStart[0]) ) zStart++; + n = (int)(zEnd - zStart); + while( ALWAYS(n>0) && sqlcipher_sqlite3Isspace(zStart[n-1]) ) n--; + return sqlcipher_sqlite3DbStrNDup(db, zStart, n); } /* -** Return TRUE if the mask of type in eType matches the type of the -** allocation p. Also return true if p==NULL. -** -** This routine is designed for use within an assert() statement, to -** verify the type of an allocation. For example: -** -** assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); +** Free any prior content in *pz and replace it with a copy of zNew. */ -SQLITE_PRIVATE int sqlcipher_sqlite3MemdebugHasType(void *p, u8 eType){ - int rc = 1; - if( p && sqlcipher_sqlite3GlobalConfig.m.xFree==sqlcipher_sqlite3MemFree ){ - struct MemBlockHdr *pHdr; - pHdr = sqlcipher_sqlite3MemsysGetHeader(p); - assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */ - if( (pHdr->eType&eType)==0 ){ - rc = 0; - } - } - return rc; +SQLITE_PRIVATE void sqlcipher_sqlite3SetString(char **pz, sqlcipher_sqlite3 *db, const char *zNew){ + char *z = sqlcipher_sqlite3DbStrDup(db, zNew); + sqlcipher_sqlite3DbFree(db, *pz); + *pz = z; } /* -** Return TRUE if the mask of type in eType matches no bits of the type of the -** allocation p. Also return true if p==NULL. +** Call this routine to record the fact that an OOM (out-of-memory) error +** has happened. This routine will set db->mallocFailed, and also +** temporarily disable the lookaside memory allocator and interrupt +** any running VDBEs. ** -** This routine is designed for use within an assert() statement, to -** verify the type of an allocation. For example: +** Always return a NULL pointer so that this routine can be invoked using ** -** assert( sqlcipher_sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); +** return sqlcipher_sqlite3OomFault(db); +** +** and thereby avoid unnecessary stack frame allocations for the overwhelmingly +** common case where no OOM occurs. */ -SQLITE_PRIVATE int sqlcipher_sqlite3MemdebugNoType(void *p, u8 eType){ - int rc = 1; - if( p && sqlcipher_sqlite3GlobalConfig.m.xFree==sqlcipher_sqlite3MemFree ){ - struct MemBlockHdr *pHdr; - pHdr = sqlcipher_sqlite3MemsysGetHeader(p); - assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */ - if( (pHdr->eType&eType)!=0 ){ - rc = 0; +SQLITE_PRIVATE void *sqlcipher_sqlite3OomFault(sqlcipher_sqlite3 *db){ + if( db->mallocFailed==0 && db->bBenignMalloc==0 ){ + db->mallocFailed = 1; + if( db->nVdbeExec>0 ){ + AtomicStore(&db->u1.isInterrupted, 1); + } + DisableLookaside; + if( db->pParse ){ + Parse *pParse; + sqlcipher_sqlite3ErrorMsg(db->pParse, "out of memory"); + db->pParse->rc = SQLITE_NOMEM_BKPT; + for(pParse=db->pParse->pOuterParse; pParse; pParse = pParse->pOuterParse){ + pParse->nErr++; + pParse->rc = SQLITE_NOMEM; + } } } - return rc; -} - -/* -** Set the number of backtrace levels kept for each allocation. -** A value of zero turns off backtracing. The number is always rounded -** up to a multiple of 2. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugBacktrace(int depth){ - if( depth<0 ){ depth = 0; } - if( depth>20 ){ depth = 20; } - depth = (depth+1)&0xfe; - mem.nBacktrace = depth; -} - -SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugBacktraceCallback(void (*xBacktrace)(int, int, void **)){ - mem.xBacktrace = xBacktrace; + return 0; } /* -** Set the title string for subsequent allocations. +** This routine reactivates the memory allocator and clears the +** db->mallocFailed flag as necessary. +** +** The memory allocator is not restarted if there are running +** VDBEs. */ -SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugSettitle(const char *zTitle){ - unsigned int n = sqlcipher_sqlite3Strlen30(zTitle) + 1; - sqlcipher_sqlite3_mutex_enter(mem.mutex); - if( n>=sizeof(mem.zTitle) ) n = sizeof(mem.zTitle)-1; - memcpy(mem.zTitle, zTitle, n); - mem.zTitle[n] = 0; - mem.nTitle = ROUND8(n); - sqlcipher_sqlite3_mutex_leave(mem.mutex); -} - -SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugSync(){ - struct MemBlockHdr *pHdr; - for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){ - void **pBt = (void**)pHdr; - pBt -= pHdr->nBacktraceSlots; - mem.xBacktrace((int)pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]); +SQLITE_PRIVATE void sqlcipher_sqlite3OomClear(sqlcipher_sqlite3 *db){ + if( db->mallocFailed && db->nVdbeExec==0 ){ + db->mallocFailed = 0; + AtomicStore(&db->u1.isInterrupted, 0); + assert( db->lookaside.bDisable>0 ); + EnableLookaside; } } /* -** Open the file indicated and write a log of all unfreed memory -** allocations into that log. +** Take actions at the end of an API call to deal with error codes. */ -SQLITE_PRIVATE void sqlcipher_sqlite3MemdebugDump(const char *zFilename){ - FILE *out; - struct MemBlockHdr *pHdr; - void **pBt; - int i; - out = fopen(zFilename, "w"); - if( out==0 ){ - fprintf(stderr, "** Unable to output memory debug output log: %s **\n", - zFilename); - return; - } - for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){ - char *z = (char*)pHdr; - z -= pHdr->nBacktraceSlots*sizeof(void*) + pHdr->nTitle; - fprintf(out, "**** %lld bytes at %p from %s ****\n", - pHdr->iSize, &pHdr[1], pHdr->nTitle ? z : "???"); - if( pHdr->nBacktrace ){ - fflush(out); - pBt = (void**)pHdr; - pBt -= pHdr->nBacktraceSlots; - backtrace_symbols_fd(pBt, pHdr->nBacktrace, fileno(out)); - fprintf(out, "\n"); - } - } - fprintf(out, "COUNTS:\n"); - for(i=0; imallocFailed || rc==SQLITE_IOERR_NOMEM ){ + sqlcipher_sqlite3OomClear(db); + sqlcipher_sqlite3Error(db, SQLITE_NOMEM); + return SQLITE_NOMEM_BKPT; } - fclose(out); + return rc & db->errMask; } /* -** Return the number of times sqlcipher_sqlite3MemMalloc() has been called. +** This function must be called before exiting any API function (i.e. +** returning control to the user) that has called sqlcipher_sqlite3_malloc or +** sqlcipher_sqlite3_realloc. +** +** The returned value is normally a copy of the second argument to this +** function. However, if a malloc() failure has occurred since the previous +** invocation SQLITE_NOMEM is returned instead. +** +** If an OOM as occurred, then the connection error-code (the value +** returned by sqlcipher_sqlite3_errcode()) is set to SQLITE_NOMEM. */ -SQLITE_PRIVATE int sqlcipher_sqlite3MemdebugMallocCount(){ - int i; - int nTotal = 0; - for(i=0; imallocFailed + ** is unsafe, as is the call to sqlcipher_sqlite3Error(). + */ + assert( db!=0 ); + assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); + if( db->mallocFailed || rc ){ + return apiHandleError(db, rc); } - return nTotal; + return rc & db->errMask; } - -#endif /* SQLITE_MEMDEBUG */ - -/************** End of mem2.c ************************************************/ -/************** Begin file mem3.c ********************************************/ +/************** End of malloc.c **********************************************/ +/************** Begin file printf.c ******************************************/ /* -** 2007 October 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 file contains the C functions that implement a memory -** allocation subsystem for use by SQLite. +** The "printf" code that follows dates from the 1980's. It is in +** the public domain. ** -** This version of the memory allocation subsystem omits all -** use of malloc(). The SQLite user supplies a block of memory -** before calling sqlcipher_sqlite3_initialize() from which allocations -** are made and returned by the xMalloc() and xRealloc() -** implementations. Once sqlcipher_sqlite3_initialize() has been called, -** the amount of memory available to SQLite is fixed and cannot -** be changed. +************************************************************************** ** -** This version of the memory allocation subsystem is included -** in the build only if SQLITE_ENABLE_MEMSYS3 is defined. +** This file contains code for a set of "printf"-like routines. These +** routines format strings much like the printf() from the standard C +** library, though the implementation here has enhancements to support +** SQLite. */ /* #include "sqliteInt.h" */ /* -** This version of the memory allocator is only built into the library -** SQLITE_ENABLE_MEMSYS3 is defined. Defining this symbol does not -** mean that the library will use a memory-pool by default, just that -** it is available. The mempool allocator is activated by calling -** sqlcipher_sqlite3_config(). +** Conversion types fall into various categories as defined by the +** following enumeration. */ -#ifdef SQLITE_ENABLE_MEMSYS3 +#define etRADIX 0 /* non-decimal integer types. %x %o */ +#define etFLOAT 1 /* Floating point. %f */ +#define etEXP 2 /* Exponentional notation. %e and %E */ +#define etGENERIC 3 /* Floating or exponential, depending on exponent. %g */ +#define etSIZE 4 /* Return number of characters processed so far. %n */ +#define etSTRING 5 /* Strings. %s */ +#define etDYNSTRING 6 /* Dynamically allocated strings. %z */ +#define etPERCENT 7 /* Percent symbol. %% */ +#define etCHARX 8 /* Characters. %c */ +/* The rest are extensions, not normally found in printf() */ +#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */ +#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '', + NULL pointers replaced by SQL NULL. %Q */ +#define etTOKEN 11 /* a pointer to a Token structure */ +#define etSRCITEM 12 /* a pointer to a SrcItem */ +#define etPOINTER 13 /* The %p conversion */ +#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */ +#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ +#define etDECIMAL 16 /* %d or %u, but not %x, %o */ -/* -** Maximum size (in Mem3Blocks) of a "small" chunk. -*/ -#define MX_SMALL 10 +#define etINVALID 17 /* Any unrecognized conversion type */ /* -** Number of freelist hash slots +** An "etByte" is an 8-bit unsigned value. */ -#define N_HASH 61 +typedef unsigned char etByte; /* -** A memory allocation (also called a "chunk") consists of two or -** more blocks where each block is 8 bytes. The first 8 bytes are -** a header that is not returned to the user. -** -** A chunk is two or more blocks that is either checked out or -** free. The first block has format u.hdr. u.hdr.size4x is 4 times the -** size of the allocation in blocks if the allocation is free. -** The u.hdr.size4x&1 bit is true if the chunk is checked out and -** false if the chunk is on the freelist. The u.hdr.size4x&2 bit -** is true if the previous chunk is checked out and false if the -** previous chunk is free. The u.hdr.prevSize field is the size of -** the previous chunk in blocks if the previous chunk is on the -** freelist. If the previous chunk is checked out, then -** u.hdr.prevSize can be part of the data for that chunk and should -** not be read or written. -** -** We often identify a chunk by its index in mem3.aPool[]. When -** this is done, the chunk index refers to the second block of -** the chunk. In this way, the first chunk has an index of 1. -** A chunk index of 0 means "no such chunk" and is the equivalent -** of a NULL pointer. -** -** The second block of free chunks is of the form u.list. The -** two fields form a double-linked list of chunks of related sizes. -** Pointers to the head of the list are stored in mem3.aiSmall[] -** for smaller chunks and mem3.aiHash[] for larger chunks. -** -** The second block of a chunk is user data if the chunk is checked -** out. If a chunk is checked out, the user data may extend into -** the u.hdr.prevSize value of the following chunk. +** Each builtin conversion character (ex: the 'd' in "%d") is described +** by an instance of the following structure */ -typedef struct Mem3Block Mem3Block; -struct Mem3Block { - union { - struct { - u32 prevSize; /* Size of previous chunk in Mem3Block elements */ - u32 size4x; /* 4x the size of current chunk in Mem3Block elements */ - } hdr; - struct { - u32 next; /* Index in mem3.aPool[] of next free chunk */ - u32 prev; /* Index in mem3.aPool[] of previous free chunk */ - } list; - } u; -}; +typedef struct et_info { /* Information about each format field */ + char fmttype; /* The format field code letter */ + etByte base; /* The base for radix conversion */ + etByte flags; /* One or more of FLAG_ constants below */ + etByte type; /* Conversion paradigm */ + etByte charset; /* Offset into aDigits[] of the digits string */ + etByte prefix; /* Offset into aPrefix[] of the prefix string */ +} et_info; /* -** All of the static variables used by this module are collected -** into a single structure named "mem3". This is to keep the -** static variables organized and to reduce namespace pollution -** when this module is combined with other in the amalgamation. +** Allowed values for et_info.flags */ -static SQLITE_WSD struct Mem3Global { - /* - ** Memory available for allocation. nPool is the size of the array - ** (in Mem3Blocks) pointed to by aPool less 2. - */ - u32 nPool; - Mem3Block *aPool; - - /* - ** True if we are evaluating an out-of-memory callback. - */ - int alarmBusy; +#define FLAG_SIGNED 1 /* True if the value to convert is signed */ +#define FLAG_STRING 4 /* Allow infinite precision */ - /* - ** Mutex to control access to the memory allocation subsystem. - */ - sqlcipher_sqlite3_mutex *mutex; - /* - ** The minimum amount of free space that we have seen. - */ - u32 mnKeyBlk; +/* +** The following table is searched linearly, so it is good to put the +** most frequently used conversion types first. +*/ +static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; +static const char aPrefix[] = "-x0\000X0"; +static const et_info fmtinfo[] = { + { 'd', 10, 1, etDECIMAL, 0, 0 }, + { 's', 0, 4, etSTRING, 0, 0 }, + { 'g', 0, 1, etGENERIC, 30, 0 }, + { 'z', 0, 4, etDYNSTRING, 0, 0 }, + { 'q', 0, 4, etSQLESCAPE, 0, 0 }, + { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, + { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, + { 'c', 0, 0, etCHARX, 0, 0 }, + { 'o', 8, 0, etRADIX, 0, 2 }, + { 'u', 10, 0, etDECIMAL, 0, 0 }, + { 'x', 16, 0, etRADIX, 16, 1 }, + { 'X', 16, 0, etRADIX, 0, 4 }, +#ifndef SQLITE_OMIT_FLOATING_POINT + { 'f', 0, 1, etFLOAT, 0, 0 }, + { 'e', 0, 1, etEXP, 30, 0 }, + { 'E', 0, 1, etEXP, 14, 0 }, + { 'G', 0, 1, etGENERIC, 14, 0 }, +#endif + { 'i', 10, 1, etDECIMAL, 0, 0 }, + { 'n', 0, 0, etSIZE, 0, 0 }, + { '%', 0, 0, etPERCENT, 0, 0 }, + { 'p', 16, 0, etPOINTER, 0, 1 }, - /* - ** iKeyBlk is the index of the key chunk. Most new allocations - ** occur off of this chunk. szKeyBlk is the size (in Mem3Blocks) - ** of the current key chunk. iKeyBlk is 0 if there is no key chunk. - ** The key chunk is not in either the aiHash[] or aiSmall[]. - */ - u32 iKeyBlk; - u32 szKeyBlk; + /* All the rest are undocumented and are for internal use only */ + { 'T', 0, 0, etTOKEN, 0, 0 }, + { 'S', 0, 0, etSRCITEM, 0, 0 }, + { 'r', 10, 1, etORDINAL, 0, 0 }, +}; - /* - ** Array of lists of free blocks according to the block size - ** for smaller chunks, or a hash on the block size for larger - ** chunks. - */ - u32 aiSmall[MX_SMALL-1]; /* For sizes 2 through MX_SMALL, inclusive */ - u32 aiHash[N_HASH]; /* For sizes MX_SMALL+1 and larger */ -} mem3 = { 97535575 }; +/* Notes: +** +** %S Takes a pointer to SrcItem. Shows name or database.name +** %!S Like %S but prefer the zName over the zAlias +*/ -#define mem3 GLOBAL(struct Mem3Global, mem3) +/* Floating point constants used for rounding */ +static const double arRound[] = { + 5.0e-01, 5.0e-02, 5.0e-03, 5.0e-04, 5.0e-05, + 5.0e-06, 5.0e-07, 5.0e-08, 5.0e-09, 5.0e-10, +}; /* -** Unlink the chunk at mem3.aPool[i] from list it is currently -** on. *pRoot is the list that i is a member of. +** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point +** conversions will work. */ -static void memsys3UnlinkFromList(u32 i, u32 *pRoot){ - u32 next = mem3.aPool[i].u.list.next; - u32 prev = mem3.aPool[i].u.list.prev; - assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); - if( prev==0 ){ - *pRoot = next; - }else{ - mem3.aPool[prev].u.list.next = next; - } - if( next ){ - mem3.aPool[next].u.list.prev = prev; - } - mem3.aPool[i].u.list.next = 0; - mem3.aPool[i].u.list.prev = 0; +#ifndef SQLITE_OMIT_FLOATING_POINT +/* +** "*val" is a double such that 0.1 <= *val < 10.0 +** Return the ascii code for the leading digit of *val, then +** multiply "*val" by 10.0 to renormalize. +** +** Example: +** input: *val = 3.14159 +** output: *val = 1.4159 function return = '3' +** +** The counter *cnt is incremented each time. After counter exceeds +** 16 (the number of significant digits in a 64-bit float) '0' is +** always returned. +*/ +static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ + int digit; + LONGDOUBLE_TYPE d; + if( (*cnt)<=0 ) return '0'; + (*cnt)--; + digit = (int)*val; + d = digit; + digit += '0'; + *val = (*val - d)*10.0; + return (char)digit; } +#endif /* SQLITE_OMIT_FLOATING_POINT */ /* -** Unlink the chunk at index i from -** whatever list is currently a member of. +** Set the StrAccum object to an error mode. */ -static void memsys3Unlink(u32 i){ - u32 size, hash; - assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); - assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 ); - assert( i>=1 ); - size = mem3.aPool[i-1].u.hdr.size4x/4; - assert( size==mem3.aPool[i+size-1].u.hdr.prevSize ); - assert( size>=2 ); - if( size <= MX_SMALL ){ - memsys3UnlinkFromList(i, &mem3.aiSmall[size-2]); - }else{ - hash = size % N_HASH; - memsys3UnlinkFromList(i, &mem3.aiHash[hash]); - } +SQLITE_PRIVATE void sqlcipher_sqlite3StrAccumSetError(StrAccum *p, u8 eError){ + assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG ); + p->accError = eError; + if( p->mxAlloc ) sqlcipher_sqlite3_str_reset(p); + if( eError==SQLITE_TOOBIG ) sqlcipher_sqlite3ErrorToParser(p->db, eError); } /* -** Link the chunk at mem3.aPool[i] so that is on the list rooted -** at *pRoot. +** Extra argument values from a PrintfArguments object */ -static void memsys3LinkIntoList(u32 i, u32 *pRoot){ - assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); - mem3.aPool[i].u.list.next = *pRoot; - mem3.aPool[i].u.list.prev = 0; - if( *pRoot ){ - mem3.aPool[*pRoot].u.list.prev = i; - } - *pRoot = i; +static sqlcipher_sqlite3_int64 getIntArg(PrintfArguments *p){ + if( p->nArg<=p->nUsed ) return 0; + return sqlcipher_sqlite3_value_int64(p->apArg[p->nUsed++]); +} +static double getDoubleArg(PrintfArguments *p){ + if( p->nArg<=p->nUsed ) return 0.0; + return sqlcipher_sqlite3_value_double(p->apArg[p->nUsed++]); +} +static char *getTextArg(PrintfArguments *p){ + if( p->nArg<=p->nUsed ) return 0; + return (char*)sqlcipher_sqlite3_value_text(p->apArg[p->nUsed++]); } /* -** Link the chunk at index i into either the appropriate -** small chunk list, or into the large chunk hash table. +** Allocate memory for a temporary buffer needed for printf rendering. +** +** If the requested size of the temp buffer is larger than the size +** of the output buffer in pAccum, then cause an SQLITE_TOOBIG error. +** Do the size check before the memory allocation to prevent rogue +** SQL from requesting large allocations using the precision or width +** field of the printf() function. */ -static void memsys3Link(u32 i){ - u32 size, hash; - assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); - assert( i>=1 ); - assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 ); - size = mem3.aPool[i-1].u.hdr.size4x/4; - assert( size==mem3.aPool[i+size-1].u.hdr.prevSize ); - assert( size>=2 ); - if( size <= MX_SMALL ){ - memsys3LinkIntoList(i, &mem3.aiSmall[size-2]); - }else{ - hash = size % N_HASH; - memsys3LinkIntoList(i, &mem3.aiHash[hash]); +static char *printfTempBuf(sqlcipher_sqlite3_str *pAccum, sqlcipher_sqlite3_int64 n){ + char *z; + if( pAccum->accError ) return 0; + if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){ + sqlcipher_sqlite3StrAccumSetError(pAccum, SQLITE_TOOBIG); + return 0; + } + z = sqlcipher_sqlite3DbMallocRaw(pAccum->db, n); + if( z==0 ){ + sqlcipher_sqlite3StrAccumSetError(pAccum, SQLITE_NOMEM); } + return z; } /* -** If the STATIC_MEM mutex is not already held, obtain it now. The mutex -** will already be held (obtained by code in malloc.c) if -** sqlcipher_sqlite3GlobalConfig.bMemStat is true. +** On machines with a small stack size, you can redefine the +** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired. */ -static void memsys3Enter(void){ - if( sqlcipher_sqlite3GlobalConfig.bMemstat==0 && mem3.mutex==0 ){ - mem3.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); - } - sqlcipher_sqlite3_mutex_enter(mem3.mutex); -} -static void memsys3Leave(void){ - sqlcipher_sqlite3_mutex_leave(mem3.mutex); -} +#ifndef SQLITE_PRINT_BUF_SIZE +# define SQLITE_PRINT_BUF_SIZE 70 +#endif +#define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */ /* -** Called when we are unable to satisfy an allocation of nBytes. +** Hard limit on the precision of floating-point conversions. */ -static void memsys3OutOfMemory(int nByte){ - if( !mem3.alarmBusy ){ - mem3.alarmBusy = 1; - assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); - sqlcipher_sqlite3_mutex_leave(mem3.mutex); - sqlcipher_sqlite3_release_memory(nByte); - sqlcipher_sqlite3_mutex_enter(mem3.mutex); - mem3.alarmBusy = 0; +#ifndef SQLITE_PRINTF_PRECISION_LIMIT +# define SQLITE_FP_PRECISION_LIMIT 100000000 +#endif + +/* +** Render a string given by "fmt" into the StrAccum object. +*/ +SQLITE_API void sqlcipher_sqlite3_str_vappendf( + sqlcipher_sqlite3_str *pAccum, /* Accumulate results here */ + const char *fmt, /* Format string */ + va_list ap /* arguments */ +){ + int c; /* Next character in the format string */ + char *bufpt; /* Pointer to the conversion buffer */ + int precision; /* Precision of the current field */ + int length; /* Length of the field */ + int idx; /* A general purpose loop counter */ + int width; /* Width of the current field */ + etByte flag_leftjustify; /* True if "-" flag is present */ + etByte flag_prefix; /* '+' or ' ' or 0 for prefix */ + etByte flag_alternateform; /* True if "#" flag is present */ + etByte flag_altform2; /* True if "!" flag is present */ + etByte flag_zeropad; /* True if field width constant starts with zero */ + etByte flag_long; /* 1 for the "l" flag, 2 for "ll", 0 by default */ + etByte done; /* Loop termination flag */ + etByte cThousand; /* Thousands separator for %d and %u */ + etByte xtype = etINVALID; /* Conversion paradigm */ + u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ + char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ + sqlite_uint64 longvalue; /* Value for integer types */ + LONGDOUBLE_TYPE realvalue; /* Value for real types */ + const et_info *infop; /* Pointer to the appropriate info structure */ + char *zOut; /* Rendering buffer */ + int nOut; /* Size of the rendering buffer */ + char *zExtra = 0; /* Malloced memory used by some conversion */ +#ifndef SQLITE_OMIT_FLOATING_POINT + int exp, e2; /* exponent of real numbers */ + int nsd; /* Number of significant digits returned */ + double rounder; /* Used for rounding floating point values */ + etByte flag_dp; /* True if decimal point should be shown */ + etByte flag_rtz; /* True if trailing zeros should be removed */ +#endif + PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ + char buf[etBUFSIZE]; /* Conversion buffer */ + + /* pAccum never starts out with an empty buffer that was obtained from + ** malloc(). This precondition is required by the mprintf("%z...") + ** optimization. */ + assert( pAccum->nChar>0 || (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 ); + + bufpt = 0; + if( (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC)!=0 ){ + pArgList = va_arg(ap, PrintfArguments*); + bArgList = 1; + }else{ + bArgList = 0; } -} + for(; (c=(*fmt))!=0; ++fmt){ + if( c!='%' ){ + bufpt = (char *)fmt; +#if HAVE_STRCHRNUL + fmt = strchrnul(fmt, '%'); +#else + do{ fmt++; }while( *fmt && *fmt != '%' ); +#endif + sqlcipher_sqlite3_str_append(pAccum, bufpt, (int)(fmt - bufpt)); + if( *fmt==0 ) break; + } + if( (c=(*++fmt))==0 ){ + sqlcipher_sqlite3_str_append(pAccum, "%", 1); + break; + } + /* Find out what flags are present */ + flag_leftjustify = flag_prefix = cThousand = + flag_alternateform = flag_altform2 = flag_zeropad = 0; + done = 0; + width = 0; + flag_long = 0; + precision = -1; + do{ + switch( c ){ + case '-': flag_leftjustify = 1; break; + case '+': flag_prefix = '+'; break; + case ' ': flag_prefix = ' '; break; + case '#': flag_alternateform = 1; break; + case '!': flag_altform2 = 1; break; + case '0': flag_zeropad = 1; break; + case ',': cThousand = ','; break; + default: done = 1; break; + case 'l': { + flag_long = 1; + c = *++fmt; + if( c=='l' ){ + c = *++fmt; + flag_long = 2; + } + done = 1; + break; + } + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': { + unsigned wx = c - '0'; + while( (c = *++fmt)>='0' && c<='9' ){ + wx = wx*10 + c - '0'; + } + testcase( wx>0x7fffffff ); + width = wx & 0x7fffffff; +#ifdef SQLITE_PRINTF_PRECISION_LIMIT + if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ + width = SQLITE_PRINTF_PRECISION_LIMIT; + } +#endif + if( c!='.' && c!='l' ){ + done = 1; + }else{ + fmt--; + } + break; + } + case '*': { + if( bArgList ){ + width = (int)getIntArg(pArgList); + }else{ + width = va_arg(ap,int); + } + if( width<0 ){ + flag_leftjustify = 1; + width = width >= -2147483647 ? -width : 0; + } +#ifdef SQLITE_PRINTF_PRECISION_LIMIT + if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ + width = SQLITE_PRINTF_PRECISION_LIMIT; + } +#endif + if( (c = fmt[1])!='.' && c!='l' ){ + c = *++fmt; + done = 1; + } + break; + } + case '.': { + c = *++fmt; + if( c=='*' ){ + if( bArgList ){ + precision = (int)getIntArg(pArgList); + }else{ + precision = va_arg(ap,int); + } + if( precision<0 ){ + precision = precision >= -2147483647 ? -precision : -1; + } + c = *++fmt; + }else{ + unsigned px = 0; + while( c>='0' && c<='9' ){ + px = px*10 + c - '0'; + c = *++fmt; + } + testcase( px>0x7fffffff ); + precision = px & 0x7fffffff; + } +#ifdef SQLITE_PRINTF_PRECISION_LIMIT + if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){ + precision = SQLITE_PRINTF_PRECISION_LIMIT; + } +#endif + if( c=='l' ){ + --fmt; + }else{ + done = 1; + } + break; + } + } + }while( !done && (c=(*++fmt))!=0 ); + + /* Fetch the info entry for the field */ + infop = &fmtinfo[0]; + xtype = etINVALID; + for(idx=0; idxtype; + break; + } + } + + /* + ** At this point, variables are initialized as follows: + ** + ** flag_alternateform TRUE if a '#' is present. + ** flag_altform2 TRUE if a '!' is present. + ** flag_prefix '+' or ' ' or zero + ** flag_leftjustify TRUE if a '-' is present or if the + ** field width was negative. + ** flag_zeropad TRUE if the width began with 0. + ** flag_long 1 for "l", 2 for "ll" + ** width The specified field width. This is + ** always non-negative. Zero is the default. + ** precision The specified precision. The default + ** is -1. + ** xtype The class of the conversion. + ** infop Pointer to the appropriate info struct. + */ + assert( width>=0 ); + assert( precision>=(-1) ); + switch( xtype ){ + case etPOINTER: + flag_long = sizeof(char*)==sizeof(i64) ? 2 : + sizeof(char*)==sizeof(long int) ? 1 : 0; + /* no break */ deliberate_fall_through + case etORDINAL: + case etRADIX: + cThousand = 0; + /* no break */ deliberate_fall_through + case etDECIMAL: + if( infop->flags & FLAG_SIGNED ){ + i64 v; + if( bArgList ){ + v = getIntArg(pArgList); + }else if( flag_long ){ + if( flag_long==2 ){ + v = va_arg(ap,i64) ; + }else{ + v = va_arg(ap,long int); + } + }else{ + v = va_arg(ap,int); + } + if( v<0 ){ + testcase( v==SMALLEST_INT64 ); + testcase( v==(-1) ); + longvalue = ~v; + longvalue++; + prefix = '-'; + }else{ + longvalue = v; + prefix = flag_prefix; + } + }else{ + if( bArgList ){ + longvalue = (u64)getIntArg(pArgList); + }else if( flag_long ){ + if( flag_long==2 ){ + longvalue = va_arg(ap,u64); + }else{ + longvalue = va_arg(ap,unsigned long int); + } + }else{ + longvalue = va_arg(ap,unsigned int); + } + prefix = 0; + } + if( longvalue==0 ) flag_alternateform = 0; + if( flag_zeropad && precision=4 || (longvalue/10)%10==1 ){ + x = 0; + } + *(--bufpt) = zOrd[x*2+1]; + *(--bufpt) = zOrd[x*2]; + } + { + const char *cset = &aDigits[infop->charset]; + u8 base = infop->base; + do{ /* Convert to ascii */ + *(--bufpt) = cset[longvalue%base]; + longvalue = longvalue/base; + }while( longvalue>0 ); + } + length = (int)(&zOut[nOut-1]-bufpt); + while( precision>length ){ + *(--bufpt) = '0'; /* Zero pad */ + length++; + } + if( cThousand ){ + int nn = (length - 1)/3; /* Number of "," to insert */ + int ix = (length - 1)%3 + 1; + bufpt -= nn; + for(idx=0; nn>0; idx++){ + bufpt[idx] = bufpt[idx+nn]; + ix--; + if( ix==0 ){ + bufpt[++idx] = cThousand; + nn--; + ix = 3; + } + } + } + if( prefix ) *(--bufpt) = prefix; /* Add sign */ + if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ + const char *pre; + char x; + pre = &aPrefix[infop->prefix]; + for(; (x=(*pre))!=0; pre++) *(--bufpt) = x; + } + length = (int)(&zOut[nOut-1]-bufpt); + break; + case etFLOAT: + case etEXP: + case etGENERIC: + if( bArgList ){ + realvalue = getDoubleArg(pArgList); + }else{ + realvalue = va_arg(ap,double); + } +#ifdef SQLITE_OMIT_FLOATING_POINT + length = 0; +#else + if( precision<0 ) precision = 6; /* Set default precision */ +#ifdef SQLITE_FP_PRECISION_LIMIT + if( precision>SQLITE_FP_PRECISION_LIMIT ){ + precision = SQLITE_FP_PRECISION_LIMIT; + } +#endif + if( realvalue<0.0 ){ + realvalue = -realvalue; + prefix = '-'; + }else{ + prefix = flag_prefix; + } + if( xtype==etGENERIC && precision>0 ) precision--; + testcase( precision>0xfff ); + idx = precision & 0xfff; + rounder = arRound[idx%10]; + while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; } + if( xtype==etFLOAT ){ + double rx = (double)realvalue; + sqlcipher_sqlite3_uint64 u; + int ex; + memcpy(&u, &rx, sizeof(u)); + ex = -1023 + (int)((u>>52)&0x7ff); + if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16; + realvalue += rounder; + } + /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ + exp = 0; + if( sqlcipher_sqlite3IsNaN((double)realvalue) ){ + bufpt = "NaN"; + length = 3; + break; + } + if( realvalue>0.0 ){ + LONGDOUBLE_TYPE scale = 1.0; + while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;} + while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; } + while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; } + realvalue /= scale; + while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; } + while( realvalue<1.0 ){ realvalue *= 10.0; exp--; } + if( exp>350 ){ + bufpt = buf; + buf[0] = prefix; + memcpy(buf+(prefix!=0),"Inf",4); + length = 3+(prefix!=0); + break; + } + } + bufpt = buf; + /* + ** If the field type is etGENERIC, then convert to either etEXP + ** or etFLOAT, as appropriate. + */ + if( xtype!=etFLOAT ){ + realvalue += rounder; + if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } + } + if( xtype==etGENERIC ){ + flag_rtz = !flag_alternateform; + if( exp<-4 || exp>precision ){ + xtype = etEXP; + }else{ + precision = precision - exp; + xtype = etFLOAT; + } + }else{ + flag_rtz = flag_altform2; + } + if( xtype==etEXP ){ + e2 = 0; + }else{ + e2 = exp; + } + { + i64 szBufNeeded; /* Size of a temporary buffer needed */ + szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; + if( szBufNeeded > etBUFSIZE ){ + bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); + if( bufpt==0 ) return; + } + } + zOut = bufpt; + nsd = 16 + flag_altform2*10; + flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2; + /* The sign in front of the number */ + if( prefix ){ + *(bufpt++) = prefix; + } + /* Digits prior to the decimal point */ + if( e2<0 ){ + *(bufpt++) = '0'; + }else{ + for(; e2>=0; e2--){ + *(bufpt++) = et_getdigit(&realvalue,&nsd); + } + } + /* The decimal point */ + if( flag_dp ){ + *(bufpt++) = '.'; + } + /* "0" digits after the decimal point but before the first + ** significant digit of the number */ + for(e2++; e2<0; precision--, e2++){ + assert( precision>0 ); + *(bufpt++) = '0'; + } + /* Significant digits after the decimal point */ + while( (precision--)>0 ){ + *(bufpt++) = et_getdigit(&realvalue,&nsd); + } + /* Remove trailing zeros and the "." if no digits follow the "." */ + if( flag_rtz && flag_dp ){ + while( bufpt[-1]=='0' ) *(--bufpt) = 0; + assert( bufpt>zOut ); + if( bufpt[-1]=='.' ){ + if( flag_altform2 ){ + *(bufpt++) = '0'; + }else{ + *(--bufpt) = 0; + } + } + } + /* Add the "eNNN" suffix */ + if( xtype==etEXP ){ + *(bufpt++) = aDigits[infop->charset]; + if( exp<0 ){ + *(bufpt++) = '-'; exp = -exp; + }else{ + *(bufpt++) = '+'; + } + if( exp>=100 ){ + *(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */ + exp %= 100; + } + *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */ + *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */ + } + *bufpt = 0; + + /* The converted number is in buf[] and zero terminated. Output it. + ** Note that the number is in the usual order, not reversed as with + ** integer conversions. */ + length = (int)(bufpt-zOut); + bufpt = zOut; + + /* Special case: Add leading zeros if the flag_zeropad flag is + ** set and we are not left justified */ + if( flag_zeropad && !flag_leftjustify && length < width){ + int i; + int nPad = width - length; + for(i=width; i>=nPad; i--){ + bufpt[i] = bufpt[i-nPad]; + } + i = prefix!=0; + while( nPad-- ) bufpt[i++] = '0'; + length = width; + } +#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */ + break; + case etSIZE: + if( !bArgList ){ + *(va_arg(ap,int*)) = pAccum->nChar; + } + length = width = 0; + break; + case etPERCENT: + buf[0] = '%'; + bufpt = buf; + length = 1; + break; + case etCHARX: + if( bArgList ){ + bufpt = getTextArg(pArgList); + length = 1; + if( bufpt ){ + buf[0] = c = *(bufpt++); + if( (c&0xc0)==0xc0 ){ + while( length<4 && (bufpt[0]&0xc0)==0x80 ){ + buf[length++] = *(bufpt++); + } + } + }else{ + buf[0] = 0; + } + }else{ + unsigned int ch = va_arg(ap,unsigned int); + if( ch<0x00080 ){ + buf[0] = ch & 0xff; + length = 1; + }else if( ch<0x00800 ){ + buf[0] = 0xc0 + (u8)((ch>>6)&0x1f); + buf[1] = 0x80 + (u8)(ch & 0x3f); + length = 2; + }else if( ch<0x10000 ){ + buf[0] = 0xe0 + (u8)((ch>>12)&0x0f); + buf[1] = 0x80 + (u8)((ch>>6) & 0x3f); + buf[2] = 0x80 + (u8)(ch & 0x3f); + length = 3; + }else{ + buf[0] = 0xf0 + (u8)((ch>>18) & 0x07); + buf[1] = 0x80 + (u8)((ch>>12) & 0x3f); + buf[2] = 0x80 + (u8)((ch>>6) & 0x3f); + buf[3] = 0x80 + (u8)(ch & 0x3f); + length = 4; + } + } + if( precision>1 ){ + width -= precision-1; + if( width>1 && !flag_leftjustify ){ + sqlcipher_sqlite3_str_appendchar(pAccum, width-1, ' '); + width = 0; + } + while( precision-- > 1 ){ + sqlcipher_sqlite3_str_append(pAccum, buf, length); + } + } + bufpt = buf; + flag_altform2 = 1; + goto adjust_width_for_utf8; + case etSTRING: + case etDYNSTRING: + if( bArgList ){ + bufpt = getTextArg(pArgList); + xtype = etSTRING; + }else{ + bufpt = va_arg(ap,char*); + } + if( bufpt==0 ){ + bufpt = ""; + }else if( xtype==etDYNSTRING ){ + if( pAccum->nChar==0 + && pAccum->mxAlloc + && width==0 + && precision<0 + && pAccum->accError==0 + ){ + /* Special optimization for sqlcipher_sqlite3_mprintf("%z..."): + ** Extend an existing memory allocation rather than creating + ** a new one. */ + assert( (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 ); + pAccum->zText = bufpt; + pAccum->nAlloc = sqlcipher_sqlite3DbMallocSize(pAccum->db, bufpt); + pAccum->nChar = 0x7fffffff & (int)strlen(bufpt); + pAccum->printfFlags |= SQLITE_PRINTF_MALLOCED; + length = 0; + break; + } + zExtra = bufpt; + } + if( precision>=0 ){ + if( flag_altform2 ){ + /* Set length to the number of bytes needed in order to display + ** precision characters */ + unsigned char *z = (unsigned char*)bufpt; + while( precision-- > 0 && z[0] ){ + SQLITE_SKIP_UTF8(z); + } + length = (int)(z - (unsigned char*)bufpt); + }else{ + for(length=0; length0 ){ + /* Adjust width to account for extra bytes in UTF-8 characters */ + int ii = length - 1; + while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++; + } + break; + case etSQLESCAPE: /* %q: Escape ' characters */ + case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */ + case etSQLESCAPE3: { /* %w: Escape " characters */ + i64 i, j, k, n; + int needQuote, isnull; + char ch; + char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ + char *escarg; + + if( bArgList ){ + escarg = getTextArg(pArgList); + }else{ + escarg = va_arg(ap,char*); + } + isnull = escarg==0; + if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); + /* For %q, %Q, and %w, the precision is the number of bytes (or + ** characters if the ! flags is present) to use from the input. + ** Because of the extra quoting characters inserted, the number + ** of output characters may be larger than the precision. + */ + k = precision; + for(i=n=0; k!=0 && (ch=escarg[i])!=0; i++, k--){ + if( ch==q ) n++; + if( flag_altform2 && (ch&0xc0)==0xc0 ){ + while( (escarg[i+1]&0xc0)==0x80 ){ i++; } + } + } + needQuote = !isnull && xtype==etSQLESCAPE2; + n += i + 3; + if( n>etBUFSIZE ){ + bufpt = zExtra = printfTempBuf(pAccum, n); + if( bufpt==0 ) return; + }else{ + bufpt = buf; + } + j = 0; + if( needQuote ) bufpt[j++] = q; + k = i; + for(i=0; iprintfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; + if( flag_alternateform ){ + /* %#T means an Expr pointer that uses Expr.u.zToken */ + Expr *pExpr = va_arg(ap,Expr*); + if( ALWAYS(pExpr) && ALWAYS(!ExprHasProperty(pExpr,EP_IntValue)) ){ + sqlcipher_sqlite3_str_appendall(pAccum, (const char*)pExpr->u.zToken); + sqlcipher_sqlite3RecordErrorOffsetOfExpr(pAccum->db, pExpr); + } + }else{ + /* %T means a Token pointer */ + Token *pToken = va_arg(ap, Token*); + assert( bArgList==0 ); + if( pToken && pToken->n ){ + sqlcipher_sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n); + sqlcipher_sqlite3RecordErrorByteOffset(pAccum->db, pToken->z); + } + } + length = width = 0; + break; + } + case etSRCITEM: { + SrcItem *pItem; + if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; + pItem = va_arg(ap, SrcItem*); + assert( bArgList==0 ); + if( pItem->zAlias && !flag_altform2 ){ + sqlcipher_sqlite3_str_appendall(pAccum, pItem->zAlias); + }else if( pItem->zName ){ + if( pItem->zDatabase ){ + sqlcipher_sqlite3_str_appendall(pAccum, pItem->zDatabase); + sqlcipher_sqlite3_str_append(pAccum, ".", 1); + } + sqlcipher_sqlite3_str_appendall(pAccum, pItem->zName); + }else if( pItem->zAlias ){ + sqlcipher_sqlite3_str_appendall(pAccum, pItem->zAlias); + }else{ + Select *pSel = pItem->pSelect; + assert( pSel!=0 ); + if( pSel->selFlags & SF_NestedFrom ){ + sqlcipher_sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); + }else{ + sqlcipher_sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId); + } + } + length = width = 0; + break; + } + default: { + assert( xtype==etINVALID ); + return; + } + }/* End switch over the format type */ + /* + ** The text of the conversion is pointed to by "bufpt" and is + ** "length" characters long. The field width is "width". Do + ** the output. Both length and width are in bytes, not characters, + ** at this point. If the "!" flag was present on string conversions + ** indicating that width and precision should be expressed in characters, + ** then the values have been translated prior to reaching this point. + */ + width -= length; + if( width>0 ){ + if( !flag_leftjustify ) sqlcipher_sqlite3_str_appendchar(pAccum, width, ' '); + sqlcipher_sqlite3_str_append(pAccum, bufpt, length); + if( flag_leftjustify ) sqlcipher_sqlite3_str_appendchar(pAccum, width, ' '); + }else{ + sqlcipher_sqlite3_str_append(pAccum, bufpt, length); + } + if( zExtra ){ + sqlcipher_sqlite3DbFree(pAccum->db, zExtra); + zExtra = 0; + } + }/* End for loop over the format string */ +} /* End of function */ -/* -** Chunk i is a free chunk that has been unlinked. Adjust its -** size parameters for check-out and return a pointer to the -** user portion of the chunk. -*/ -static void *memsys3Checkout(u32 i, u32 nBlock){ - u32 x; - assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); - assert( i>=1 ); - assert( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ); - assert( mem3.aPool[i+nBlock-1].u.hdr.prevSize==nBlock ); - x = mem3.aPool[i-1].u.hdr.size4x; - mem3.aPool[i-1].u.hdr.size4x = nBlock*4 | 1 | (x&2); - mem3.aPool[i+nBlock-1].u.hdr.prevSize = nBlock; - mem3.aPool[i+nBlock-1].u.hdr.size4x |= 2; - return &mem3.aPool[i]; -} /* -** Carve a piece off of the end of the mem3.iKeyBlk free chunk. -** Return a pointer to the new allocation. Or, if the key chunk -** is not large enough, return 0. +** The z string points to the first character of a token that is +** associated with an error. If db does not already have an error +** byte offset recorded, try to compute the error byte offset for +** z and set the error byte offset in db. */ -static void *memsys3FromKeyBlk(u32 nBlock){ - assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); - assert( mem3.szKeyBlk>=nBlock ); - if( nBlock>=mem3.szKeyBlk-1 ){ - /* Use the entire key chunk */ - void *p = memsys3Checkout(mem3.iKeyBlk, mem3.szKeyBlk); - mem3.iKeyBlk = 0; - mem3.szKeyBlk = 0; - mem3.mnKeyBlk = 0; - return p; - }else{ - /* Split the key block. Return the tail. */ - u32 newi, x; - newi = mem3.iKeyBlk + mem3.szKeyBlk - nBlock; - assert( newi > mem3.iKeyBlk+1 ); - mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = nBlock; - mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x |= 2; - mem3.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1; - mem3.szKeyBlk -= nBlock; - mem3.aPool[newi-1].u.hdr.prevSize = mem3.szKeyBlk; - x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2; - mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x; - if( mem3.szKeyBlk < mem3.mnKeyBlk ){ - mem3.mnKeyBlk = mem3.szKeyBlk; - } - return (void*)&mem3.aPool[newi]; +SQLITE_PRIVATE void sqlcipher_sqlite3RecordErrorByteOffset(sqlcipher_sqlite3 *db, const char *z){ + const Parse *pParse; + const char *zText; + const char *zEnd; + assert( z!=0 ); + if( NEVER(db==0) ) return; + if( db->errByteOffset!=(-2) ) return; + pParse = db->pParse; + if( NEVER(pParse==0) ) return; + zText =pParse->zTail; + if( NEVER(zText==0) ) return; + zEnd = &zText[strlen(zText)]; + if( SQLITE_WITHIN(z,zText,zEnd) ){ + db->errByteOffset = (int)(z-zText); } } /* -** *pRoot is the head of a list of free chunks of the same size -** or same size hash. In other words, *pRoot is an entry in either -** mem3.aiSmall[] or mem3.aiHash[]. -** -** This routine examines all entries on the given list and tries -** to coalesce each entries with adjacent free chunks. -** -** If it sees a chunk that is larger than mem3.iKeyBlk, it replaces -** the current mem3.iKeyBlk with the new larger chunk. In order for -** this mem3.iKeyBlk replacement to work, the key chunk must be -** linked into the hash tables. That is not the normal state of -** affairs, of course. The calling routine must link the key -** chunk before invoking this routine, then must unlink the (possibly -** changed) key chunk once this routine has finished. +** If pExpr has a byte offset for the start of a token, record that as +** as the error offset. */ -static void memsys3Merge(u32 *pRoot){ - u32 iNext, prev, size, i, x; - - assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); - for(i=*pRoot; i>0; i=iNext){ - iNext = mem3.aPool[i].u.list.next; - size = mem3.aPool[i-1].u.hdr.size4x; - assert( (size&1)==0 ); - if( (size&2)==0 ){ - memsys3UnlinkFromList(i, pRoot); - assert( i > mem3.aPool[i-1].u.hdr.prevSize ); - prev = i - mem3.aPool[i-1].u.hdr.prevSize; - if( prev==iNext ){ - iNext = mem3.aPool[prev].u.list.next; - } - memsys3Unlink(prev); - size = i + size/4 - prev; - x = mem3.aPool[prev-1].u.hdr.size4x & 2; - mem3.aPool[prev-1].u.hdr.size4x = size*4 | x; - mem3.aPool[prev+size-1].u.hdr.prevSize = size; - memsys3Link(prev); - i = prev; - }else{ - size /= 4; - } - if( size>mem3.szKeyBlk ){ - mem3.iKeyBlk = i; - mem3.szKeyBlk = size; - } +SQLITE_PRIVATE void sqlcipher_sqlite3RecordErrorOffsetOfExpr(sqlcipher_sqlite3 *db, const Expr *pExpr){ + while( pExpr + && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0) + ){ + pExpr = pExpr->pLeft; } + if( pExpr==0 ) return; + db->errByteOffset = pExpr->w.iOfst; } /* -** Return a block of memory of at least nBytes in size. -** Return NULL if unable. +** Enlarge the memory allocation on a StrAccum object so that it is +** able to accept at least N more bytes of text. ** -** This function assumes that the necessary mutexes, if any, are -** already held by the caller. Hence "Unsafe". +** Return the number of bytes of text that StrAccum is able to accept +** after the attempted enlargement. The value returned might be zero. */ -static void *memsys3MallocUnsafe(int nByte){ - u32 i; - u32 nBlock; - u32 toFree; - - assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); - assert( sizeof(Mem3Block)==8 ); - if( nByte<=12 ){ - nBlock = 2; - }else{ - nBlock = (nByte + 11)/8; +SQLITE_PRIVATE int sqlcipher_sqlite3StrAccumEnlarge(StrAccum *p, int N){ + char *zNew; + assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */ + if( p->accError ){ + testcase(p->accError==SQLITE_TOOBIG); + testcase(p->accError==SQLITE_NOMEM); + return 0; } - assert( nBlock>=2 ); - - /* STEP 1: - ** Look for an entry of the correct size in either the small - ** chunk table or in the large chunk hash table. This is - ** successful most of the time (about 9 times out of 10). - */ - if( nBlock <= MX_SMALL ){ - i = mem3.aiSmall[nBlock-2]; - if( i>0 ){ - memsys3UnlinkFromList(i, &mem3.aiSmall[nBlock-2]); - return memsys3Checkout(i, nBlock); - } + if( p->mxAlloc==0 ){ + sqlcipher_sqlite3StrAccumSetError(p, SQLITE_TOOBIG); + return p->nAlloc - p->nChar - 1; }else{ - int hash = nBlock % N_HASH; - for(i=mem3.aiHash[hash]; i>0; i=mem3.aPool[i].u.list.next){ - if( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ){ - memsys3UnlinkFromList(i, &mem3.aiHash[hash]); - return memsys3Checkout(i, nBlock); - } - } - } - - /* STEP 2: - ** Try to satisfy the allocation by carving a piece off of the end - ** of the key chunk. This step usually works if step 1 fails. - */ - if( mem3.szKeyBlk>=nBlock ){ - return memsys3FromKeyBlk(nBlock); - } - - - /* STEP 3: - ** Loop through the entire memory pool. Coalesce adjacent free - ** chunks. Recompute the key chunk as the largest free chunk. - ** Then try again to satisfy the allocation by carving a piece off - ** of the end of the key chunk. This step happens very - ** rarely (we hope!) - */ - for(toFree=nBlock*16; toFree<(mem3.nPool*16); toFree *= 2){ - memsys3OutOfMemory(toFree); - if( mem3.iKeyBlk ){ - memsys3Link(mem3.iKeyBlk); - mem3.iKeyBlk = 0; - mem3.szKeyBlk = 0; + char *zOld = isMalloced(p) ? p->zText : 0; + i64 szNew = p->nChar; + szNew += (sqlcipher_sqlite3_int64)N + 1; + if( szNew+p->nChar<=p->mxAlloc ){ + /* Force exponential buffer size growth as long as it does not overflow, + ** to avoid having to call this routine too often */ + szNew += p->nChar; } - for(i=0; i p->mxAlloc ){ + sqlcipher_sqlite3_str_reset(p); + sqlcipher_sqlite3StrAccumSetError(p, SQLITE_TOOBIG); + return 0; + }else{ + p->nAlloc = (int)szNew; } - for(i=0; idb ){ + zNew = sqlcipher_sqlite3DbRealloc(p->db, zOld, p->nAlloc); + }else{ + zNew = sqlcipher_sqlite3Realloc(zOld, p->nAlloc); } - if( mem3.szKeyBlk ){ - memsys3Unlink(mem3.iKeyBlk); - if( mem3.szKeyBlk>=nBlock ){ - return memsys3FromKeyBlk(nBlock); - } + if( zNew ){ + assert( p->zText!=0 || p->nChar==0 ); + if( !isMalloced(p) && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar); + p->zText = zNew; + p->nAlloc = sqlcipher_sqlite3DbMallocSize(p->db, zNew); + p->printfFlags |= SQLITE_PRINTF_MALLOCED; + }else{ + sqlcipher_sqlite3_str_reset(p); + sqlcipher_sqlite3StrAccumSetError(p, SQLITE_NOMEM); + return 0; } } - - /* If none of the above worked, then we fail. */ - return 0; + return N; } /* -** Free an outstanding memory allocation. -** -** This function assumes that the necessary mutexes, if any, are -** already held by the caller. Hence "Unsafe". +** Append N copies of character c to the given string buffer. */ -static void memsys3FreeUnsafe(void *pOld){ - Mem3Block *p = (Mem3Block*)pOld; - int i; - u32 size, x; - assert( sqlcipher_sqlite3_mutex_held(mem3.mutex) ); - assert( p>mem3.aPool && p<&mem3.aPool[mem3.nPool] ); - i = p - mem3.aPool; - assert( (mem3.aPool[i-1].u.hdr.size4x&1)==1 ); - size = mem3.aPool[i-1].u.hdr.size4x/4; - assert( i+size<=mem3.nPool+1 ); - mem3.aPool[i-1].u.hdr.size4x &= ~1; - mem3.aPool[i+size-1].u.hdr.prevSize = size; - mem3.aPool[i+size-1].u.hdr.size4x &= ~2; - memsys3Link(i); - - /* Try to expand the key using the newly freed chunk */ - if( mem3.iKeyBlk ){ - while( (mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x&2)==0 ){ - size = mem3.aPool[mem3.iKeyBlk-1].u.hdr.prevSize; - mem3.iKeyBlk -= size; - mem3.szKeyBlk += size; - memsys3Unlink(mem3.iKeyBlk); - x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2; - mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x; - mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = mem3.szKeyBlk; - } - x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2; - while( (mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x&1)==0 ){ - memsys3Unlink(mem3.iKeyBlk+mem3.szKeyBlk); - mem3.szKeyBlk += mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x/4; - mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x; - mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = mem3.szKeyBlk; - } +SQLITE_API void sqlcipher_sqlite3_str_appendchar(sqlcipher_sqlite3_str *p, int N, char c){ + testcase( p->nChar + (i64)N > 0x7fffffff ); + if( p->nChar+(i64)N >= p->nAlloc && (N = sqlcipher_sqlite3StrAccumEnlarge(p, N))<=0 ){ + return; } + while( (N--)>0 ) p->zText[p->nChar++] = c; } /* -** Return the size of an outstanding allocation, in bytes. The -** size returned omits the 8-byte header overhead. This only -** works for chunks that are currently checked out. +** The StrAccum "p" is not large enough to accept N new bytes of z[]. +** So enlarge if first, then do the append. +** +** This is a helper routine to sqlcipher_sqlite3_str_append() that does special-case +** work (enlarging the buffer) using tail recursion, so that the +** sqlcipher_sqlite3_str_append() routine can use fast calling semantics. */ -static int memsys3Size(void *p){ - Mem3Block *pBlock; - assert( p!=0 ); - pBlock = (Mem3Block*)p; - assert( (pBlock[-1].u.hdr.size4x&1)!=0 ); - return (pBlock[-1].u.hdr.size4x&~3)*2 - 4; +static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){ + N = sqlcipher_sqlite3StrAccumEnlarge(p, N); + if( N>0 ){ + memcpy(&p->zText[p->nChar], z, N); + p->nChar += N; + } } /* -** Round up a request size to the next valid allocation size. +** Append N bytes of text from z to the StrAccum object. Increase the +** size of the memory allocation for StrAccum if necessary. */ -static int memsys3Roundup(int n){ - if( n<=12 ){ - return 12; - }else{ - return ((n+11)&~7) - 4; +SQLITE_API void sqlcipher_sqlite3_str_append(sqlcipher_sqlite3_str *p, const char *z, int N){ + assert( z!=0 || N==0 ); + assert( p->zText!=0 || p->nChar==0 || p->accError ); + assert( N>=0 ); + assert( p->accError==0 || p->nAlloc==0 || p->mxAlloc==0 ); + if( p->nChar+N >= p->nAlloc ){ + enlargeAndAppend(p,z,N); + }else if( N ){ + assert( p->zText ); + p->nChar += N; + memcpy(&p->zText[p->nChar-N], z, N); } } /* -** Allocate nBytes of memory. +** Append the complete text of zero-terminated string z[] to the p string. */ -static void *memsys3Malloc(int nBytes){ - sqlcipher_sqlite3_int64 *p; - assert( nBytes>0 ); /* malloc.c filters out 0 byte requests */ - memsys3Enter(); - p = memsys3MallocUnsafe(nBytes); - memsys3Leave(); - return (void*)p; +SQLITE_API void sqlcipher_sqlite3_str_appendall(sqlcipher_sqlite3_str *p, const char *z){ + sqlcipher_sqlite3_str_append(p, z, sqlcipher_sqlite3Strlen30(z)); } -/* -** Free memory. -*/ -static void memsys3Free(void *pPrior){ - assert( pPrior ); - memsys3Enter(); - memsys3FreeUnsafe(pPrior); - memsys3Leave(); -} /* -** Change the size of an existing memory allocation +** Finish off a string by making sure it is zero-terminated. +** Return a pointer to the resulting string. Return a NULL +** pointer if any kind of error was encountered. */ -static void *memsys3Realloc(void *pPrior, int nBytes){ - int nOld; - void *p; - if( pPrior==0 ){ - return sqlcipher_sqlite3_malloc(nBytes); - } - if( nBytes<=0 ){ - sqlcipher_sqlite3_free(pPrior); - return 0; - } - nOld = memsys3Size(pPrior); - if( nBytes<=nOld && nBytes>=nOld-128 ){ - return pPrior; +static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){ + char *zText; + assert( p->mxAlloc>0 && !isMalloced(p) ); + zText = sqlcipher_sqlite3DbMallocRaw(p->db, p->nChar+1 ); + if( zText ){ + memcpy(zText, p->zText, p->nChar+1); + p->printfFlags |= SQLITE_PRINTF_MALLOCED; + }else{ + sqlcipher_sqlite3StrAccumSetError(p, SQLITE_NOMEM); } - memsys3Enter(); - p = memsys3MallocUnsafe(nBytes); - if( p ){ - if( nOldzText = zText; + return zText; +} +SQLITE_PRIVATE char *sqlcipher_sqlite3StrAccumFinish(StrAccum *p){ + if( p->zText ){ + p->zText[p->nChar] = 0; + if( p->mxAlloc>0 && !isMalloced(p) ){ + return strAccumFinishRealloc(p); } - memsys3FreeUnsafe(pPrior); } - memsys3Leave(); - return p; + return p->zText; } /* -** Initialize this module. +** Use the content of the StrAccum passed as the second argument +** as the result of an SQL function. */ -static int memsys3Init(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - if( !sqlcipher_sqlite3GlobalConfig.pHeap ){ - return SQLITE_ERROR; +SQLITE_PRIVATE void sqlcipher_sqlite3ResultStrAccum(sqlcipher_sqlite3_context *pCtx, StrAccum *p){ + if( p->accError ){ + sqlcipher_sqlite3_result_error_code(pCtx, p->accError); + sqlcipher_sqlite3_str_reset(p); + }else if( isMalloced(p) ){ + sqlcipher_sqlite3_result_text(pCtx, p->zText, p->nChar, SQLITE_DYNAMIC); + }else{ + sqlcipher_sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC); + sqlcipher_sqlite3_str_reset(p); } - - /* Store a pointer to the memory block in global structure mem3. */ - assert( sizeof(Mem3Block)==8 ); - mem3.aPool = (Mem3Block *)sqlcipher_sqlite3GlobalConfig.pHeap; - mem3.nPool = (sqlcipher_sqlite3GlobalConfig.nHeap / sizeof(Mem3Block)) - 2; - - /* Initialize the key block. */ - mem3.szKeyBlk = mem3.nPool; - mem3.mnKeyBlk = mem3.szKeyBlk; - mem3.iKeyBlk = 1; - mem3.aPool[0].u.hdr.size4x = (mem3.szKeyBlk<<2) + 2; - mem3.aPool[mem3.nPool].u.hdr.prevSize = mem3.nPool; - mem3.aPool[mem3.nPool].u.hdr.size4x = 1; - - return SQLITE_OK; } /* -** Deinitialize this module. +** This singleton is an sqlcipher_sqlite3_str object that is returned if +** sqlcipher_sqlite3_malloc() fails to provide space for a real one. This +** sqlcipher_sqlite3_str object accepts no new text and always returns +** an SQLITE_NOMEM error. */ -static void memsys3Shutdown(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - mem3.mutex = 0; - return; -} - - +static sqlcipher_sqlite3_str sqlcipher_sqlite3OomStr = { + 0, 0, 0, 0, 0, SQLITE_NOMEM, 0 +}; -/* -** Open the file indicated and write a log of all unfreed memory -** allocations into that log. +/* Finalize a string created using sqlcipher_sqlite3_str_new(). */ -SQLITE_PRIVATE void sqlcipher_sqlite3Memsys3Dump(const char *zFilename){ -#ifdef SQLITE_DEBUG - FILE *out; - u32 i, j; - u32 size; - if( zFilename==0 || zFilename[0]==0 ){ - out = stdout; - }else{ - out = fopen(zFilename, "w"); - if( out==0 ){ - fprintf(stderr, "** Unable to output memory debug output log: %s **\n", - zFilename); - return; - } - } - memsys3Enter(); - fprintf(out, "CHUNKS:\n"); - for(i=1; i<=mem3.nPool; i+=size/4){ - size = mem3.aPool[i-1].u.hdr.size4x; - if( size/4<=1 ){ - fprintf(out, "%p size error\n", &mem3.aPool[i]); - assert( 0 ); - break; - } - if( (size&1)==0 && mem3.aPool[i+size/4-1].u.hdr.prevSize!=size/4 ){ - fprintf(out, "%p tail size does not match\n", &mem3.aPool[i]); - assert( 0 ); - break; - } - if( ((mem3.aPool[i+size/4-1].u.hdr.size4x&2)>>1)!=(size&1) ){ - fprintf(out, "%p tail checkout bit is incorrect\n", &mem3.aPool[i]); - assert( 0 ); - break; - } - if( size&1 ){ - fprintf(out, "%p %6d bytes checked out\n", &mem3.aPool[i], (size/4)*8-8); - }else{ - fprintf(out, "%p %6d bytes free%s\n", &mem3.aPool[i], (size/4)*8-8, - i==mem3.iKeyBlk ? " **key**" : ""); - } - } - for(i=0; i0; j=mem3.aPool[j].u.list.next){ - fprintf(out, " %p(%d)", &mem3.aPool[j], - (mem3.aPool[j-1].u.hdr.size4x/4)*8-8); - } - fprintf(out, "\n"); - } - for(i=0; i0; j=mem3.aPool[j].u.list.next){ - fprintf(out, " %p(%d)", &mem3.aPool[j], - (mem3.aPool[j-1].u.hdr.size4x/4)*8-8); - } - fprintf(out, "\n"); - } - fprintf(out, "key=%d\n", mem3.iKeyBlk); - fprintf(out, "nowUsed=%d\n", mem3.nPool*8 - mem3.szKeyBlk*8); - fprintf(out, "mxUsed=%d\n", mem3.nPool*8 - mem3.mnKeyBlk*8); - sqlcipher_sqlite3_mutex_leave(mem3.mutex); - if( out==stdout ){ - fflush(stdout); +SQLITE_API char *sqlcipher_sqlite3_str_finish(sqlcipher_sqlite3_str *p){ + char *z; + if( p!=0 && p!=&sqlcipher_sqlite3OomStr ){ + z = sqlcipher_sqlite3StrAccumFinish(p); + sqlcipher_sqlite3_free(p); }else{ - fclose(out); + z = 0; } -#else - UNUSED_PARAMETER(zFilename); -#endif + return z; } -/* -** This routine is the only routine in this file with external -** linkage. -** -** Populate the low-level memory allocation function pointers in -** sqlcipher_sqlite3GlobalConfig.m with pointers to the routines in this file. The -** arguments specify the block of memory to manage. -** -** This routine is only called by sqlcipher_sqlite3_config(), and therefore -** is not required to be threadsafe (it is not). -*/ -SQLITE_PRIVATE const sqlcipher_sqlite3_mem_methods *sqlcipher_sqlite3MemGetMemsys3(void){ - static const sqlcipher_sqlite3_mem_methods mempoolMethods = { - memsys3Malloc, - memsys3Free, - memsys3Realloc, - memsys3Size, - memsys3Roundup, - memsys3Init, - memsys3Shutdown, - 0 - }; - return &mempoolMethods; +/* Return any error code associated with p */ +SQLITE_API int sqlcipher_sqlite3_str_errcode(sqlcipher_sqlite3_str *p){ + return p ? p->accError : SQLITE_NOMEM; } -#endif /* SQLITE_ENABLE_MEMSYS3 */ +/* Return the current length of p in bytes */ +SQLITE_API int sqlcipher_sqlite3_str_length(sqlcipher_sqlite3_str *p){ + return p ? p->nChar : 0; +} -/************** End of mem3.c ************************************************/ -/************** Begin file mem5.c ********************************************/ -/* -** 2007 October 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 file contains the C functions that implement a memory -** allocation subsystem for use by SQLite. -** -** This version of the memory allocation subsystem omits all -** use of malloc(). The application gives SQLite a block of memory -** before calling sqlcipher_sqlite3_initialize() from which allocations -** are made and returned by the xMalloc() and xRealloc() -** implementations. Once sqlcipher_sqlite3_initialize() has been called, -** the amount of memory available to SQLite is fixed and cannot -** be changed. -** -** This version of the memory allocation subsystem is included -** in the build only if SQLITE_ENABLE_MEMSYS5 is defined. -** -** This memory allocator uses the following algorithm: -** -** 1. All memory allocation sizes are rounded up to a power of 2. -** -** 2. If two adjacent free blocks are the halves of a larger block, -** then the two blocks are coalesced into the single larger block. -** -** 3. New memory is allocated from the first available free block. -** -** This algorithm is described in: J. M. Robson. "Bounds for Some Functions -** Concerning Dynamic Storage Allocation". Journal of the Association for -** Computing Machinery, Volume 21, Number 8, July 1974, pages 491-499. -** -** Let n be the size of the largest allocation divided by the minimum -** allocation size (after rounding all sizes up to a power of 2.) Let M -** be the maximum amount of memory ever outstanding at one time. Let -** N be the total amount of memory available for allocation. Robson -** proved that this memory allocator will never breakdown due to -** fragmentation as long as the following constraint holds: -** -** N >= M*(1 + log2(n)/2) - n + 1 -** -** The sqlcipher_sqlite3_status() logic tracks the maximum values of n and M so -** that an application can, at any time, verify this constraint. -*/ -/* #include "sqliteInt.h" */ +/* Return the current value for p */ +SQLITE_API char *sqlcipher_sqlite3_str_value(sqlcipher_sqlite3_str *p){ + if( p==0 || p->nChar==0 ) return 0; + p->zText[p->nChar] = 0; + return p->zText; +} /* -** This version of the memory allocator is used only when -** SQLITE_ENABLE_MEMSYS5 is defined. +** Reset an StrAccum string. Reclaim all malloced memory. */ -#ifdef SQLITE_ENABLE_MEMSYS5 +SQLITE_API void sqlcipher_sqlite3_str_reset(StrAccum *p){ + if( isMalloced(p) ){ + sqlcipher_sqlite3DbFree(p->db, p->zText); + p->printfFlags &= ~SQLITE_PRINTF_MALLOCED; + } + p->nAlloc = 0; + p->nChar = 0; + p->zText = 0; +} /* -** A minimum allocation is an instance of the following structure. -** Larger allocations are an array of these structures where the -** size of the array is a power of 2. +** Initialize a string accumulator. ** -** The size of this object must be a power of two. That fact is -** verified in memsys5Init(). -*/ -typedef struct Mem5Link Mem5Link; -struct Mem5Link { - int next; /* Index of next free chunk */ - int prev; /* Index of previous free chunk */ -}; - -/* -** Maximum size of any allocation is ((1<mallocFailed is set appropriately +** when not NULL. +** zBase: An initial buffer. May be NULL in which case the initial buffer +** is malloced. +** n: Size of zBase in bytes. If total space requirements never exceed +** n then no memory allocations ever occur. +** mx: Maximum number of bytes to accumulate. If mx==0 then no memory +** allocations will ever occur. */ -static void memsys5Unlink(int i, int iLogsize){ - int next, prev; - assert( i>=0 && i=0 && iLogsize<=LOGMAX ); - assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize ); +SQLITE_PRIVATE void sqlcipher_sqlite3StrAccumInit(StrAccum *p, sqlcipher_sqlite3 *db, char *zBase, int n, int mx){ + p->zText = zBase; + p->db = db; + p->nAlloc = n; + p->mxAlloc = mx; + p->nChar = 0; + p->accError = 0; + p->printfFlags = 0; +} - next = MEM5LINK(i)->next; - prev = MEM5LINK(i)->prev; - if( prev<0 ){ - mem5.aiFreelist[iLogsize] = next; +/* Allocate and initialize a new dynamic string object */ +SQLITE_API sqlcipher_sqlite3_str *sqlcipher_sqlite3_str_new(sqlcipher_sqlite3 *db){ + sqlcipher_sqlite3_str *p = sqlcipher_sqlite3_malloc64(sizeof(*p)); + if( p ){ + sqlcipher_sqlite3StrAccumInit(p, 0, 0, 0, + db ? db->aLimit[SQLITE_LIMIT_LENGTH] : SQLITE_MAX_LENGTH); }else{ - MEM5LINK(prev)->next = next; - } - if( next>=0 ){ - MEM5LINK(next)->prev = prev; + p = &sqlcipher_sqlite3OomStr; } + return p; } /* -** Link the chunk at mem5.aPool[i] so that is on the iLogsize -** free list. +** Print into memory obtained from sqliteMalloc(). Use the internal +** %-conversion extensions. */ -static void memsys5Link(int i, int iLogsize){ - int x; - assert( sqlcipher_sqlite3_mutex_held(mem5.mutex) ); - assert( i>=0 && i=0 && iLogsize<=LOGMAX ); - assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize ); - - x = MEM5LINK(i)->next = mem5.aiFreelist[iLogsize]; - MEM5LINK(i)->prev = -1; - if( x>=0 ){ - assert( xprev = i; +SQLITE_PRIVATE char *sqlcipher_sqlite3VMPrintf(sqlcipher_sqlite3 *db, const char *zFormat, va_list ap){ + char *z; + char zBase[SQLITE_PRINT_BUF_SIZE]; + StrAccum acc; + assert( db!=0 ); + sqlcipher_sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase), + db->aLimit[SQLITE_LIMIT_LENGTH]); + acc.printfFlags = SQLITE_PRINTF_INTERNAL; + sqlcipher_sqlite3_str_vappendf(&acc, zFormat, ap); + z = sqlcipher_sqlite3StrAccumFinish(&acc); + if( acc.accError==SQLITE_NOMEM ){ + sqlcipher_sqlite3OomFault(db); } - mem5.aiFreelist[iLogsize] = i; -} - -/* -** Obtain or release the mutex needed to access global data structures. -*/ -static void memsys5Enter(void){ - sqlcipher_sqlite3_mutex_enter(mem5.mutex); -} -static void memsys5Leave(void){ - sqlcipher_sqlite3_mutex_leave(mem5.mutex); + return z; } /* -** Return the size of an outstanding allocation, in bytes. -** This only works for chunks that are currently checked out. +** Print into memory obtained from sqliteMalloc(). Use the internal +** %-conversion extensions. */ -static int memsys5Size(void *p){ - int iSize, i; - assert( p!=0 ); - i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom); - assert( i>=0 && i0 ); - - /* No more than 1GiB per allocation */ - if( nByte > 0x40000000 ) return 0; - -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) - /* Keep track of the maximum allocation request. Even unfulfilled - ** requests are counted */ - if( (u32)nByte>mem5.maxRequest ){ - mem5.maxRequest = nByte; - } -#endif - - - /* Round nByte up to the next valid power of two */ - for(iFullSz=mem5.szAtom,iLogsize=0; iFullSzLOGMAX ){ - testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); - sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte); +#ifdef SQLITE_ENABLE_API_ARMOR + if( zFormat==0 ){ + (void)SQLITE_MISUSE_BKPT; return 0; } - i = mem5.aiFreelist[iBin]; - memsys5Unlink(i, iBin); - while( iBin>iLogsize ){ - int newSize; - - iBin--; - newSize = 1 << iBin; - mem5.aCtrl[i+newSize] = CTRL_FREE | iBin; - memsys5Link(i+newSize, iBin); - } - mem5.aCtrl[i] = iLogsize; - -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) - /* Update allocator performance statistics. */ - mem5.nAlloc++; - mem5.totalAlloc += iFullSz; - mem5.totalExcess += iFullSz - nByte; - mem5.currentCount++; - mem5.currentOut += iFullSz; - if( mem5.maxCount=0 && iBlock0 ); - assert( mem5.currentOut>=(size*mem5.szAtom) ); - mem5.currentCount--; - mem5.currentOut -= size*mem5.szAtom; - assert( mem5.currentOut>0 || mem5.currentCount==0 ); - assert( mem5.currentCount>0 || mem5.currentOut==0 ); -#endif - - mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize; - while( ALWAYS(iLogsize>iLogsize) & 1 ){ - iBuddy = iBlock - size; - assert( iBuddy>=0 ); - }else{ - iBuddy = iBlock + size; - if( iBuddy>=mem5.nBlock ) break; - } - if( mem5.aCtrl[iBuddy]!=(CTRL_FREE | iLogsize) ) break; - memsys5Unlink(iBuddy, iLogsize); - iLogsize++; - if( iBuddy0 ){ - memsys5Enter(); - p = memsys5MallocUnsafe(nBytes); - memsys5Leave(); - } - return (void*)p; -} - -/* -** Free memory. -** -** The outer layer memory allocator prevents this routine from -** being called with pPrior==0. -*/ -static void memsys5Free(void *pPrior){ - assert( pPrior!=0 ); - memsys5Enter(); - memsys5FreeUnsafe(pPrior); - memsys5Leave(); + va_start(ap, zFormat); + z = sqlcipher_sqlite3_vmprintf(zFormat, ap); + va_end(ap); + return z; } /* -** Change the size of an existing memory allocation. +** sqlcipher_sqlite3_snprintf() works like snprintf() except that it ignores the +** current locale settings. This is important for SQLite because we +** are not able to use a "," as the decimal point in place of "." as +** specified by some locales. ** -** The outer layer memory allocator prevents this routine from -** being called with pPrior==0. +** Oops: The first two arguments of sqlcipher_sqlite3_snprintf() are backwards +** from the snprintf() standard. Unfortunately, it is too late to change +** this without breaking compatibility, so we just have to live with the +** mistake. ** -** nBytes is always a value obtained from a prior call to -** memsys5Round(). Hence nBytes is always a non-negative power -** of two. If nBytes==0 that means that an oversize allocation -** (an allocation larger than 0x40000000) was requested and this -** routine should return 0 without freeing pPrior. +** sqlcipher_sqlite3_vsnprintf() is the varargs version. */ -static void *memsys5Realloc(void *pPrior, int nBytes){ - int nOld; - void *p; - assert( pPrior!=0 ); - assert( (nBytes&(nBytes-1))==0 ); /* EV: R-46199-30249 */ - assert( nBytes>=0 ); - if( nBytes==0 ){ - return 0; - } - nOld = memsys5Size(pPrior); - if( nBytes<=nOld ){ - return pPrior; - } - p = memsys5Malloc(nBytes); - if( p ){ - memcpy(p, pPrior, nOld); - memsys5Free(pPrior); +SQLITE_API char *sqlcipher_sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){ + StrAccum acc; + if( n<=0 ) return zBuf; +#ifdef SQLITE_ENABLE_API_ARMOR + if( zBuf==0 || zFormat==0 ) { + (void)SQLITE_MISUSE_BKPT; + if( zBuf ) zBuf[0] = 0; + return zBuf; } - return p; -} - -/* -** Round up a request size to the next valid allocation size. If -** the allocation is too large to be handled by this allocation system, -** return 0. -** -** All allocations must be a power of two and must be expressed by a -** 32-bit signed integer. Hence the largest allocation is 0x40000000 -** or 1073741824 bytes. -*/ -static int memsys5Roundup(int n){ - int iFullSz; - if( n > 0x40000000 ) return 0; - for(iFullSz=mem5.szAtom; iFullSz 0 -** memsys5Log(2) -> 1 -** memsys5Log(4) -> 2 -** memsys5Log(5) -> 3 -** memsys5Log(8) -> 3 -** memsys5Log(9) -> 4 -*/ -static int memsys5Log(int iValue){ - int iLog; - for(iLog=0; (iLog<(int)((sizeof(int)*8)-1)) && (1<mem5.szAtom ){ - mem5.szAtom = mem5.szAtom << 1; - } - - mem5.nBlock = (nByte / (mem5.szAtom+sizeof(u8))); - mem5.zPool = zByte; - mem5.aCtrl = (u8 *)&mem5.zPool[mem5.nBlock*mem5.szAtom]; - - for(ii=0; ii<=LOGMAX; ii++){ - mem5.aiFreelist[ii] = -1; - } - - iOffset = 0; - for(ii=LOGMAX; ii>=0; ii--){ - int nAlloc = (1<mem5.nBlock); - } - - /* If a mutex is required for normal operation, allocate one */ - if( sqlcipher_sqlite3GlobalConfig.bMemstat==0 ){ - mem5.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); - } +static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ + StrAccum acc; /* String accumulator */ + char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */ - return SQLITE_OK; + sqlcipher_sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0); + sqlcipher_sqlite3_str_vappendf(&acc, zFormat, ap); + sqlcipher_sqlite3GlobalConfig.xLog(sqlcipher_sqlite3GlobalConfig.pLogArg, iErrCode, + sqlcipher_sqlite3StrAccumFinish(&acc)); } /* -** Deinitialize this module. +** Format and write a message to the log if logging is enabled. */ -static void memsys5Shutdown(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - mem5.mutex = 0; - return; +SQLITE_API void sqlcipher_sqlite3_log(int iErrCode, const char *zFormat, ...){ + va_list ap; /* Vararg list */ + if( sqlcipher_sqlite3GlobalConfig.xLog ){ + va_start(ap, zFormat); + renderLogMsg(iErrCode, zFormat, ap); + va_end(ap); + } } -#ifdef SQLITE_TEST +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) /* -** Open the file indicated and write a log of all unfreed memory -** allocations into that log. +** A version of printf() that understands %lld. Used for debugging. +** The printf() built into some versions of windows does not understand %lld +** and segfaults if you give it a long long int. */ -SQLITE_PRIVATE void sqlcipher_sqlite3Memsys5Dump(const char *zFilename){ - FILE *out; - int i, j, n; - int nMinLog; - - if( zFilename==0 || zFilename[0]==0 ){ - out = stdout; - }else{ - out = fopen(zFilename, "w"); - if( out==0 ){ - fprintf(stderr, "** Unable to output memory debug output log: %s **\n", - zFilename); - return; - } - } - memsys5Enter(); - nMinLog = memsys5Log(mem5.szAtom); - for(i=0; i<=LOGMAX && i+nMinLog<32; i++){ - for(n=0, j=mem5.aiFreelist[i]; j>=0; j = MEM5LINK(j)->next, n++){} - fprintf(out, "freelist items of size %d: %d\n", mem5.szAtom << i, n); - } - fprintf(out, "mem5.nAlloc = %llu\n", mem5.nAlloc); - fprintf(out, "mem5.totalAlloc = %llu\n", mem5.totalAlloc); - fprintf(out, "mem5.totalExcess = %llu\n", mem5.totalExcess); - fprintf(out, "mem5.currentOut = %u\n", mem5.currentOut); - fprintf(out, "mem5.currentCount = %u\n", mem5.currentCount); - fprintf(out, "mem5.maxOut = %u\n", mem5.maxOut); - fprintf(out, "mem5.maxCount = %u\n", mem5.maxCount); - fprintf(out, "mem5.maxRequest = %u\n", mem5.maxRequest); - memsys5Leave(); - if( out==stdout ){ - fflush(stdout); - }else{ - fclose(out); +SQLITE_PRIVATE void sqlcipher_sqlite3DebugPrintf(const char *zFormat, ...){ + va_list ap; + StrAccum acc; + char zBuf[SQLITE_PRINT_BUF_SIZE*10]; + sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); + va_start(ap,zFormat); + sqlcipher_sqlite3_str_vappendf(&acc, zFormat, ap); + va_end(ap); + sqlcipher_sqlite3StrAccumFinish(&acc); +#ifdef SQLITE_OS_TRACE_PROC + { + extern void SQLITE_OS_TRACE_PROC(const char *zBuf, int nBuf); + SQLITE_OS_TRACE_PROC(zBuf, sizeof(zBuf)); } +#else + fprintf(stdout,"%s", zBuf); + fflush(stdout); +#endif } #endif + /* -** This routine is the only routine in this file with external -** linkage. It returns a pointer to a static sqlcipher_sqlite3_mem_methods -** struct populated with the memsys5 methods. +** variable-argument wrapper around sqlcipher_sqlite3_str_vappendf(). The bFlags argument +** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats. */ -SQLITE_PRIVATE const sqlcipher_sqlite3_mem_methods *sqlcipher_sqlite3MemGetMemsys5(void){ - static const sqlcipher_sqlite3_mem_methods memsys5Methods = { - memsys5Malloc, - memsys5Free, - memsys5Realloc, - memsys5Size, - memsys5Roundup, - memsys5Init, - memsys5Shutdown, - 0 - }; - return &memsys5Methods; +SQLITE_API void sqlcipher_sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ + va_list ap; + va_start(ap,zFormat); + sqlcipher_sqlite3_str_vappendf(p, zFormat, ap); + va_end(ap); } -#endif /* SQLITE_ENABLE_MEMSYS5 */ - -/************** End of mem5.c ************************************************/ -/************** Begin file mutex.c *******************************************/ +/************** End of printf.c **********************************************/ +/************** Begin file treeview.c ****************************************/ /* -** 2007 August 14 +** 2015-06-08 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -30904,361 +31141,1290 @@ SQLITE_PRIVATE const sqlcipher_sqlite3_mem_methods *sqlcipher_sqlite3MemGetMemsy ** May you share freely, never taking more than you give. ** ************************************************************************* -** This file contains the C functions that implement mutexes. ** -** This file contains code that is common across all mutex implementations. +** This file contains C code to implement the TreeView debugging routines. +** These routines print a parse tree to standard output for debugging and +** analysis. +** +** The interfaces in this file is only available when compiling +** with SQLITE_DEBUG. */ /* #include "sqliteInt.h" */ +#ifdef SQLITE_DEBUG -#if defined(SQLITE_DEBUG) && !defined(SQLITE_MUTEX_OMIT) /* -** For debugging purposes, record when the mutex subsystem is initialized -** and uninitialized so that we can assert() if there is an attempt to -** allocate a mutex while the system is uninitialized. +** Add a new subitem to the tree. The moreToFollow flag indicates that this +** is not the last item in the tree. */ -static SQLITE_WSD int mutexIsInit = 0; -#endif /* SQLITE_DEBUG && !defined(SQLITE_MUTEX_OMIT) */ - - -#ifndef SQLITE_MUTEX_OMIT +static void sqlcipher_sqlite3TreeViewPush(TreeView **pp, u8 moreToFollow){ + TreeView *p = *pp; + if( p==0 ){ + *pp = p = sqlcipher_sqlite3_malloc64( sizeof(*p) ); + if( p==0 ) return; + memset(p, 0, sizeof(*p)); + }else{ + p->iLevel++; + } + assert( moreToFollow==0 || moreToFollow==1 ); + if( p->iLevel<(int)sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow; +} -#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS /* -** This block (enclosed by SQLITE_ENABLE_MULTITHREADED_CHECKS) contains -** the implementation of a wrapper around the system default mutex -** implementation (sqlcipher_sqlite3DefaultMutex()). -** -** Most calls are passed directly through to the underlying default -** mutex implementation. Except, if a mutex is configured by calling -** sqlcipher_sqlite3MutexWarnOnContention() on it, then if contention is ever -** encountered within xMutexEnter() a warning is emitted via sqlcipher_sqlite3_log(). -** -** This type of mutex is used as the database handle mutex when testing -** apps that usually use SQLITE_CONFIG_MULTITHREAD mode. +** Finished with one layer of the tree */ +static void sqlcipher_sqlite3TreeViewPop(TreeView **pp){ + TreeView *p = *pp; + if( p==0 ) return; + p->iLevel--; + if( p->iLevel<0 ){ + sqlcipher_sqlite3_free(p); + *pp = 0; + } +} /* -** Type for all mutexes used when SQLITE_ENABLE_MULTITHREADED_CHECKS -** is defined. Variable CheckMutex.mutex is a pointer to the real mutex -** allocated by the system mutex implementation. Variable iType is usually set -** to the type of mutex requested - SQLITE_MUTEX_RECURSIVE, SQLITE_MUTEX_FAST -** or one of the static mutex identifiers. Or, if this is a recursive mutex -** that has been configured using sqlcipher_sqlite3MutexWarnOnContention(), it is -** set to SQLITE_MUTEX_WARNONCONTENTION. +** Generate a single line of output for the tree, with a prefix that contains +** all the appropriate tree lines */ -typedef struct CheckMutex CheckMutex; -struct CheckMutex { - int iType; - sqlcipher_sqlite3_mutex *mutex; -}; - -#define SQLITE_MUTEX_WARNONCONTENTION (-1) +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ + va_list ap; + int i; + StrAccum acc; + char zBuf[1000]; + sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); + if( p ){ + for(i=0; iiLevel && i<(int)sizeof(p->bLine)-1; i++){ + sqlcipher_sqlite3_str_append(&acc, p->bLine[i] ? "| " : " ", 4); + } + sqlcipher_sqlite3_str_append(&acc, p->bLine[i] ? "|-- " : "'-- ", 4); + } + if( zFormat!=0 ){ + va_start(ap, zFormat); + sqlcipher_sqlite3_str_vappendf(&acc, zFormat, ap); + va_end(ap); + assert( acc.nChar>0 || acc.accError ); + sqlcipher_sqlite3_str_append(&acc, "\n", 1); + } + sqlcipher_sqlite3StrAccumFinish(&acc); + fprintf(stdout,"%s", zBuf); + fflush(stdout); +} /* -** Pointer to real mutex methods object used by the CheckMutex -** implementation. Set by checkMutexInit(). +** Shorthand for starting a new tree item that consists of a single label */ -static SQLITE_WSD const sqlcipher_sqlite3_mutex_methods *pGlobalMutexMethods; - -#ifdef SQLITE_DEBUG -static int checkMutexHeld(sqlcipher_sqlite3_mutex *p){ - return pGlobalMutexMethods->xMutexHeld(((CheckMutex*)p)->mutex); -} -static int checkMutexNotheld(sqlcipher_sqlite3_mutex *p){ - return pGlobalMutexMethods->xMutexNotheld(((CheckMutex*)p)->mutex); +static void sqlcipher_sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){ + sqlcipher_sqlite3TreeViewPush(&p, moreFollows); + sqlcipher_sqlite3TreeViewLine(p, "%s", zLabel); } -#endif /* -** Initialize and deinitialize the mutex subsystem. +** Show a list of Column objects in tree format. */ -static int checkMutexInit(void){ - pGlobalMutexMethods = sqlcipher_sqlite3DefaultMutex(); - return SQLITE_OK; -} -static int checkMutexEnd(void){ - pGlobalMutexMethods = 0; - return SQLITE_OK; +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewColumnList( + TreeView *pView, + const Column *aCol, + int nCol, + u8 moreToFollow +){ + int i; + sqlcipher_sqlite3TreeViewPush(&pView, moreToFollow); + sqlcipher_sqlite3TreeViewLine(pView, "COLUMNS"); + for(i=0; iiType = iType; +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){ + int i; + if( pWith==0 ) return; + if( pWith->nCte==0 ) return; + if( pWith->pOuter ){ + sqlcipher_sqlite3TreeViewLine(pView, "WITH (0x%p, pOuter=0x%p)",pWith,pWith->pOuter); }else{ -#ifdef SQLITE_ENABLE_API_ARMOR - if( iType-2>=ArraySize(staticMutexes) ){ - (void)SQLITE_MISUSE_BKPT; - return 0; + sqlcipher_sqlite3TreeViewLine(pView, "WITH (0x%p)", pWith); + } + if( pWith->nCte>0 ){ + sqlcipher_sqlite3TreeViewPush(&pView, moreToFollow); + for(i=0; inCte; i++){ + StrAccum x; + char zLine[1000]; + const struct Cte *pCte = &pWith->a[i]; + sqlcipher_sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); + sqlcipher_sqlite3_str_appendf(&x, "%s", pCte->zName); + if( pCte->pCols && pCte->pCols->nExpr>0 ){ + char cSep = '('; + int j; + for(j=0; jpCols->nExpr; j++){ + sqlcipher_sqlite3_str_appendf(&x, "%c%s", cSep, pCte->pCols->a[j].zEName); + cSep = ','; + } + sqlcipher_sqlite3_str_appendf(&x, ")"); + } + if( pCte->eM10d!=M10d_Any ){ + sqlcipher_sqlite3_str_appendf(&x, " %sMATERIALIZED", + pCte->eM10d==M10d_No ? "NOT " : ""); + } + if( pCte->pUse ){ + sqlcipher_sqlite3_str_appendf(&x, " (pUse=0x%p, nUse=%d)", pCte->pUse, + pCte->pUse->nUse); + } + sqlcipher_sqlite3StrAccumFinish(&x); + sqlcipher_sqlite3TreeViewItem(pView, zLine, inCte-1); + sqlcipher_sqlite3TreeViewSelect(pView, pCte->pSelect, 0); + sqlcipher_sqlite3TreeViewPop(&pView); } -#endif - p = &staticMutexes[iType-2]; + sqlcipher_sqlite3TreeViewPop(&pView); } +} - if( p->mutex==0 ){ - p->mutex = pGlobalMutexMethods->xMutexAlloc(iType); - if( p->mutex==0 ){ - if( iType<2 ){ - sqlcipher_sqlite3_free(p); +/* +** Generate a human-readable description of a SrcList object. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){ + int i; + if( pSrc==0 ) return; + for(i=0; inSrc; i++){ + const SrcItem *pItem = &pSrc->a[i]; + StrAccum x; + int n = 0; + char zLine[1000]; + sqlcipher_sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); + x.printfFlags |= SQLITE_PRINTF_INTERNAL; + sqlcipher_sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); + if( pItem->pTab ){ + sqlcipher_sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx", + pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed); + } + if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){ + sqlcipher_sqlite3_str_appendf(&x, " FULL-OUTER-JOIN"); + }else if( pItem->fg.jointype & JT_LEFT ){ + sqlcipher_sqlite3_str_appendf(&x, " LEFT-JOIN"); + }else if( pItem->fg.jointype & JT_RIGHT ){ + sqlcipher_sqlite3_str_appendf(&x, " RIGHT-JOIN"); + }else if( pItem->fg.jointype & JT_CROSS ){ + sqlcipher_sqlite3_str_appendf(&x, " CROSS-JOIN"); + } + if( pItem->fg.jointype & JT_LTORJ ){ + sqlcipher_sqlite3_str_appendf(&x, " LTORJ"); + } + if( pItem->fg.fromDDL ){ + sqlcipher_sqlite3_str_appendf(&x, " DDL"); + } + if( pItem->fg.isCte ){ + sqlcipher_sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse); + } + if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ + sqlcipher_sqlite3_str_appendf(&x, " ON"); + } + sqlcipher_sqlite3StrAccumFinish(&x); + sqlcipher_sqlite3TreeViewItem(pView, zLine, inSrc-1); + n = 0; + if( pItem->pSelect ) n++; + if( pItem->fg.isTabFunc ) n++; + if( pItem->fg.isUsing ) n++; + if( pItem->fg.isUsing ){ + sqlcipher_sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); + } + if( pItem->pSelect ){ + if( pItem->pTab ){ + Table *pTab = pItem->pTab; + sqlcipher_sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); } - p = 0; + assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); + sqlcipher_sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0); + } + if( pItem->fg.isTabFunc ){ + sqlcipher_sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); } + sqlcipher_sqlite3TreeViewPop(&pView); } - - return (sqlcipher_sqlite3_mutex*)p; } /* -** Free a mutex. +** Generate a human-readable description of a Select object. */ -static void checkMutexFree(sqlcipher_sqlite3_mutex *p){ - assert( SQLITE_MUTEX_RECURSIVE<2 ); - assert( SQLITE_MUTEX_FAST<2 ); - assert( SQLITE_MUTEX_WARNONCONTENTION<2 ); - -#if SQLITE_ENABLE_API_ARMOR - if( ((CheckMutex*)p)->iType<2 ) -#endif - { - CheckMutex *pCheck = (CheckMutex*)p; - pGlobalMutexMethods->xMutexFree(pCheck->mutex); - sqlcipher_sqlite3_free(pCheck); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){ + int n = 0; + int cnt = 0; + if( p==0 ){ + sqlcipher_sqlite3TreeViewLine(pView, "nil-SELECT"); + return; } -#ifdef SQLITE_ENABLE_API_ARMOR - else{ - (void)SQLITE_MISUSE_BKPT; + sqlcipher_sqlite3TreeViewPush(&pView, moreToFollow); + if( p->pWith ){ + sqlcipher_sqlite3TreeViewWith(pView, p->pWith, 1); + cnt = 1; + sqlcipher_sqlite3TreeViewPush(&pView, 1); } + do{ + if( p->selFlags & SF_WhereBegin ){ + sqlcipher_sqlite3TreeViewLine(pView, "sqlcipher_sqlite3WhereBegin()"); + }else{ + sqlcipher_sqlite3TreeViewLine(pView, + "SELECT%s%s (%u/%p) selFlags=0x%x nSelectRow=%d", + ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""), + ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), + p->selId, p, p->selFlags, + (int)p->nSelectRow + ); + } + if( cnt++ ) sqlcipher_sqlite3TreeViewPop(&pView); + if( p->pPrior ){ + n = 1000; + }else{ + n = 0; + if( p->pSrc && p->pSrc->nSrc ) n++; + if( p->pWhere ) n++; + if( p->pGroupBy ) n++; + if( p->pHaving ) n++; + if( p->pOrderBy ) n++; + if( p->pLimit ) n++; +#ifndef SQLITE_OMIT_WINDOWFUNC + if( p->pWin ) n++; + if( p->pWinDefn ) n++; +#endif + } + if( p->pEList ){ + sqlcipher_sqlite3TreeViewExprList(pView, p->pEList, n>0, "result-set"); + } + n--; +#ifndef SQLITE_OMIT_WINDOWFUNC + if( p->pWin ){ + Window *pX; + sqlcipher_sqlite3TreeViewPush(&pView, (n--)>0); + sqlcipher_sqlite3TreeViewLine(pView, "window-functions"); + for(pX=p->pWin; pX; pX=pX->pNextWin){ + sqlcipher_sqlite3TreeViewWinFunc(pView, pX, pX->pNextWin!=0); + } + sqlcipher_sqlite3TreeViewPop(&pView); + } +#endif + if( p->pSrc && p->pSrc->nSrc ){ + sqlcipher_sqlite3TreeViewPush(&pView, (n--)>0); + sqlcipher_sqlite3TreeViewLine(pView, "FROM"); + sqlcipher_sqlite3TreeViewSrcList(pView, p->pSrc); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( p->pWhere ){ + sqlcipher_sqlite3TreeViewItem(pView, "WHERE", (n--)>0); + sqlcipher_sqlite3TreeViewExpr(pView, p->pWhere, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( p->pGroupBy ){ + sqlcipher_sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY"); + } + if( p->pHaving ){ + sqlcipher_sqlite3TreeViewItem(pView, "HAVING", (n--)>0); + sqlcipher_sqlite3TreeViewExpr(pView, p->pHaving, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } +#ifndef SQLITE_OMIT_WINDOWFUNC + if( p->pWinDefn ){ + Window *pX; + sqlcipher_sqlite3TreeViewItem(pView, "WINDOW", (n--)>0); + for(pX=p->pWinDefn; pX; pX=pX->pNextWin){ + sqlcipher_sqlite3TreeViewWindow(pView, pX, pX->pNextWin!=0); + } + sqlcipher_sqlite3TreeViewPop(&pView); + } #endif + if( p->pOrderBy ){ + sqlcipher_sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY"); + } + if( p->pLimit ){ + sqlcipher_sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); + sqlcipher_sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0); + if( p->pLimit->pRight ){ + sqlcipher_sqlite3TreeViewItem(pView, "OFFSET", (n--)>0); + sqlcipher_sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( p->pPrior ){ + const char *zOp = "UNION"; + switch( p->op ){ + case TK_ALL: zOp = "UNION ALL"; break; + case TK_INTERSECT: zOp = "INTERSECT"; break; + case TK_EXCEPT: zOp = "EXCEPT"; break; + } + sqlcipher_sqlite3TreeViewItem(pView, zOp, 1); + } + p = p->pPrior; + }while( p!=0 ); + sqlcipher_sqlite3TreeViewPop(&pView); } +#ifndef SQLITE_OMIT_WINDOWFUNC /* -** Enter the mutex. +** Generate a description of starting or stopping bounds */ -static void checkMutexEnter(sqlcipher_sqlite3_mutex *p){ - CheckMutex *pCheck = (CheckMutex*)p; - if( pCheck->iType==SQLITE_MUTEX_WARNONCONTENTION ){ - if( SQLITE_OK==pGlobalMutexMethods->xMutexTry(pCheck->mutex) ){ - return; +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewBound( + TreeView *pView, /* View context */ + u8 eBound, /* UNBOUNDED, CURRENT, PRECEDING, FOLLOWING */ + Expr *pExpr, /* Value for PRECEDING or FOLLOWING */ + u8 moreToFollow /* True if more to follow */ +){ + switch( eBound ){ + case TK_UNBOUNDED: { + sqlcipher_sqlite3TreeViewItem(pView, "UNBOUNDED", moreToFollow); + sqlcipher_sqlite3TreeViewPop(&pView); + break; + } + case TK_CURRENT: { + sqlcipher_sqlite3TreeViewItem(pView, "CURRENT", moreToFollow); + sqlcipher_sqlite3TreeViewPop(&pView); + break; + } + case TK_PRECEDING: { + sqlcipher_sqlite3TreeViewItem(pView, "PRECEDING", moreToFollow); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + break; + } + case TK_FOLLOWING: { + sqlcipher_sqlite3TreeViewItem(pView, "FOLLOWING", moreToFollow); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + break; } - sqlcipher_sqlite3_log(SQLITE_MISUSE, - "illegal multi-threaded access to database connection" - ); } - pGlobalMutexMethods->xMutexEnter(pCheck->mutex); } +#endif /* SQLITE_OMIT_WINDOWFUNC */ +#ifndef SQLITE_OMIT_WINDOWFUNC /* -** Enter the mutex (do not block). +** Generate a human-readable explanation for a Window object */ -static int checkMutexTry(sqlcipher_sqlite3_mutex *p){ - CheckMutex *pCheck = (CheckMutex*)p; - return pGlobalMutexMethods->xMutexTry(pCheck->mutex); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){ + int nElement = 0; + if( pWin==0 ) return; + if( pWin->pFilter ){ + sqlcipher_sqlite3TreeViewItem(pView, "FILTER", 1); + sqlcipher_sqlite3TreeViewExpr(pView, pWin->pFilter, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + sqlcipher_sqlite3TreeViewPush(&pView, more); + if( pWin->zName ){ + sqlcipher_sqlite3TreeViewLine(pView, "OVER %s (%p)", pWin->zName, pWin); + }else{ + sqlcipher_sqlite3TreeViewLine(pView, "OVER (%p)", pWin); + } + if( pWin->zBase ) nElement++; + if( pWin->pOrderBy ) nElement++; + if( pWin->eFrmType ) nElement++; + if( pWin->eExclude ) nElement++; + if( pWin->zBase ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--nElement)>0); + sqlcipher_sqlite3TreeViewLine(pView, "window: %s", pWin->zBase); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pWin->pPartition ){ + sqlcipher_sqlite3TreeViewExprList(pView, pWin->pPartition, nElement>0,"PARTITION-BY"); + } + if( pWin->pOrderBy ){ + sqlcipher_sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY"); + } + if( pWin->eFrmType ){ + char zBuf[30]; + const char *zFrmType = "ROWS"; + if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE"; + if( pWin->eFrmType==TK_GROUPS ) zFrmType = "GROUPS"; + sqlcipher_sqlite3_snprintf(sizeof(zBuf),zBuf,"%s%s",zFrmType, + pWin->bImplicitFrame ? " (implied)" : ""); + sqlcipher_sqlite3TreeViewItem(pView, zBuf, (--nElement)>0); + sqlcipher_sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1); + sqlcipher_sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pWin->eExclude ){ + char zBuf[30]; + const char *zExclude; + switch( pWin->eExclude ){ + case TK_NO: zExclude = "NO OTHERS"; break; + case TK_CURRENT: zExclude = "CURRENT ROW"; break; + case TK_GROUP: zExclude = "GROUP"; break; + case TK_TIES: zExclude = "TIES"; break; + default: + sqlcipher_sqlite3_snprintf(sizeof(zBuf),zBuf,"invalid(%d)", pWin->eExclude); + zExclude = zBuf; + break; + } + sqlcipher_sqlite3TreeViewPush(&pView, 0); + sqlcipher_sqlite3TreeViewLine(pView, "EXCLUDE %s", zExclude); + sqlcipher_sqlite3TreeViewPop(&pView); + } + sqlcipher_sqlite3TreeViewPop(&pView); } +#endif /* SQLITE_OMIT_WINDOWFUNC */ +#ifndef SQLITE_OMIT_WINDOWFUNC /* -** Leave the mutex. +** Generate a human-readable explanation for a Window Function object */ -static void checkMutexLeave(sqlcipher_sqlite3_mutex *p){ - CheckMutex *pCheck = (CheckMutex*)p; - pGlobalMutexMethods->xMutexLeave(pCheck->mutex); -} - -sqlcipher_sqlite3_mutex_methods const *multiThreadedCheckMutex(void){ - static const sqlcipher_sqlite3_mutex_methods sMutex = { - checkMutexInit, - checkMutexEnd, - checkMutexAlloc, - checkMutexFree, - checkMutexEnter, - checkMutexTry, - checkMutexLeave, -#ifdef SQLITE_DEBUG - checkMutexHeld, - checkMutexNotheld -#else - 0, - 0 -#endif - }; - return &sMutex; +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin, u8 more){ + if( pWin==0 ) return; + sqlcipher_sqlite3TreeViewPush(&pView, more); + sqlcipher_sqlite3TreeViewLine(pView, "WINFUNC %s(%d)", + pWin->pWFunc->zName, pWin->pWFunc->nArg); + sqlcipher_sqlite3TreeViewWindow(pView, pWin, 0); + sqlcipher_sqlite3TreeViewPop(&pView); } +#endif /* SQLITE_OMIT_WINDOWFUNC */ /* -** Mark the SQLITE_MUTEX_RECURSIVE mutex passed as the only argument as -** one on which there should be no contention. +** Generate a human-readable explanation of an expression tree. */ -SQLITE_PRIVATE void sqlcipher_sqlite3MutexWarnOnContention(sqlcipher_sqlite3_mutex *p){ - if( sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc==checkMutexAlloc ){ - CheckMutex *pCheck = (CheckMutex*)p; - assert( pCheck->iType==SQLITE_MUTEX_RECURSIVE ); - pCheck->iType = SQLITE_MUTEX_WARNONCONTENTION; +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ + const char *zBinOp = 0; /* Binary operator */ + const char *zUniOp = 0; /* Unary operator */ + char zFlgs[200]; + sqlcipher_sqlite3TreeViewPush(&pView, moreToFollow); + if( pExpr==0 ){ + sqlcipher_sqlite3TreeViewLine(pView, "nil"); + sqlcipher_sqlite3TreeViewPop(&pView); + return; } -} -#endif /* ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS */ + if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){ + StrAccum x; + sqlcipher_sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0); + sqlcipher_sqlite3_str_appendf(&x, " fg.af=%x.%c", + pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n'); + if( ExprHasProperty(pExpr, EP_OuterON) ){ + sqlcipher_sqlite3_str_appendf(&x, " outer.iJoin=%d", pExpr->w.iJoin); + } + if( ExprHasProperty(pExpr, EP_InnerON) ){ + sqlcipher_sqlite3_str_appendf(&x, " inner.iJoin=%d", pExpr->w.iJoin); + } + if( ExprHasProperty(pExpr, EP_FromDDL) ){ + sqlcipher_sqlite3_str_appendf(&x, " DDL"); + } + if( ExprHasVVAProperty(pExpr, EP_Immutable) ){ + sqlcipher_sqlite3_str_appendf(&x, " IMMUTABLE"); + } + sqlcipher_sqlite3StrAccumFinish(&x); + }else{ + zFlgs[0] = 0; + } + switch( pExpr->op ){ + case TK_AGG_COLUMN: { + sqlcipher_sqlite3TreeViewLine(pView, "AGG{%d:%d}%s", + pExpr->iTable, pExpr->iColumn, zFlgs); + break; + } + case TK_COLUMN: { + if( pExpr->iTable<0 ){ + /* This only happens when coding check constraints */ + char zOp2[16]; + if( pExpr->op2 ){ + sqlcipher_sqlite3_snprintf(sizeof(zOp2),zOp2," op2=0x%02x",pExpr->op2); + }else{ + zOp2[0] = 0; + } + sqlcipher_sqlite3TreeViewLine(pView, "COLUMN(%d)%s%s", + pExpr->iColumn, zFlgs, zOp2); + }else{ + assert( ExprUseYTab(pExpr) ); + sqlcipher_sqlite3TreeViewLine(pView, "{%d:%d} pTab=%p%s", + pExpr->iTable, pExpr->iColumn, + pExpr->y.pTab, zFlgs); + } + if( ExprHasProperty(pExpr, EP_FixedCol) ){ + sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + } + break; + } + case TK_INTEGER: { + if( pExpr->flags & EP_IntValue ){ + sqlcipher_sqlite3TreeViewLine(pView, "%d", pExpr->u.iValue); + }else{ + sqlcipher_sqlite3TreeViewLine(pView, "%s", pExpr->u.zToken); + } + break; + } +#ifndef SQLITE_OMIT_FLOATING_POINT + case TK_FLOAT: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlcipher_sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); + break; + } +#endif + case TK_STRING: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlcipher_sqlite3TreeViewLine(pView,"%Q", pExpr->u.zToken); + break; + } + case TK_NULL: { + sqlcipher_sqlite3TreeViewLine(pView,"NULL"); + break; + } + case TK_TRUEFALSE: { + sqlcipher_sqlite3TreeViewLine(pView,"%s%s", + sqlcipher_sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE", zFlgs); + break; + } +#ifndef SQLITE_OMIT_BLOB_LITERAL + case TK_BLOB: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlcipher_sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); + break; + } +#endif + case TK_VARIABLE: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlcipher_sqlite3TreeViewLine(pView,"VARIABLE(%s,%d)", + pExpr->u.zToken, pExpr->iColumn); + break; + } + case TK_REGISTER: { + sqlcipher_sqlite3TreeViewLine(pView,"REGISTER(%d)", pExpr->iTable); + break; + } + case TK_ID: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlcipher_sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken); + break; + } +#ifndef SQLITE_OMIT_CAST + case TK_CAST: { + /* Expressions of the form: CAST(pLeft AS token) */ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlcipher_sqlite3TreeViewLine(pView,"CAST %Q", pExpr->u.zToken); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + break; + } +#endif /* SQLITE_OMIT_CAST */ + case TK_LT: zBinOp = "LT"; break; + case TK_LE: zBinOp = "LE"; break; + case TK_GT: zBinOp = "GT"; break; + case TK_GE: zBinOp = "GE"; break; + case TK_NE: zBinOp = "NE"; break; + case TK_EQ: zBinOp = "EQ"; break; + case TK_IS: zBinOp = "IS"; break; + case TK_ISNOT: zBinOp = "ISNOT"; break; + case TK_AND: zBinOp = "AND"; break; + case TK_OR: zBinOp = "OR"; break; + case TK_PLUS: zBinOp = "ADD"; break; + case TK_STAR: zBinOp = "MUL"; break; + case TK_MINUS: zBinOp = "SUB"; break; + case TK_REM: zBinOp = "REM"; break; + case TK_BITAND: zBinOp = "BITAND"; break; + case TK_BITOR: zBinOp = "BITOR"; break; + case TK_SLASH: zBinOp = "DIV"; break; + case TK_LSHIFT: zBinOp = "LSHIFT"; break; + case TK_RSHIFT: zBinOp = "RSHIFT"; break; + case TK_CONCAT: zBinOp = "CONCAT"; break; + case TK_DOT: zBinOp = "DOT"; break; + case TK_LIMIT: zBinOp = "LIMIT"; break; -/* -** Initialize the mutex system. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3MutexInit(void){ - int rc = SQLITE_OK; - if( !sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc ){ - /* If the xMutexAlloc method has not been set, then the user did not - ** install a mutex implementation via sqlcipher_sqlite3_config() prior to - ** sqlcipher_sqlite3_initialize() being called. This block copies pointers to - ** the default implementation into the sqlcipher_sqlite3GlobalConfig structure. - */ - sqlcipher_sqlite3_mutex_methods const *pFrom; - sqlcipher_sqlite3_mutex_methods *pTo = &sqlcipher_sqlite3GlobalConfig.mutex; + case TK_UMINUS: zUniOp = "UMINUS"; break; + case TK_UPLUS: zUniOp = "UPLUS"; break; + case TK_BITNOT: zUniOp = "BITNOT"; break; + case TK_NOT: zUniOp = "NOT"; break; + case TK_ISNULL: zUniOp = "ISNULL"; break; + case TK_NOTNULL: zUniOp = "NOTNULL"; break; - if( sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ -#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS - pFrom = multiThreadedCheckMutex(); + case TK_TRUTH: { + int x; + const char *azOp[] = { + "IS-FALSE", "IS-TRUE", "IS-NOT-FALSE", "IS-NOT-TRUE" + }; + assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT ); + assert( pExpr->pRight ); + assert( sqlcipher_sqlite3ExprSkipCollate(pExpr->pRight)->op==TK_TRUEFALSE ); + x = (pExpr->op2==TK_ISNOT)*2 + sqlcipher_sqlite3ExprTruthValue(pExpr->pRight); + zUniOp = azOp[x]; + break; + } + + case TK_SPAN: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlcipher_sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + break; + } + + case TK_COLLATE: { + /* COLLATE operators without the EP_Collate flag are intended to + ** emulate collation associated with a table column. These show + ** up in the treeview output as "SOFT-COLLATE". Explicit COLLATE + ** operators that appear in the original SQL always have the + ** EP_Collate bit set and appear in treeview output as just "COLLATE" */ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlcipher_sqlite3TreeViewLine(pView, "%sCOLLATE %Q%s", + !ExprHasProperty(pExpr, EP_Collate) ? "SOFT-" : "", + pExpr->u.zToken, zFlgs); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + break; + } + + case TK_AGG_FUNCTION: + case TK_FUNCTION: { + ExprList *pFarg; /* List of function arguments */ + Window *pWin; + if( ExprHasProperty(pExpr, EP_TokenOnly) ){ + pFarg = 0; + pWin = 0; + }else{ + assert( ExprUseXList(pExpr) ); + pFarg = pExpr->x.pList; +#ifndef SQLITE_OMIT_WINDOWFUNC + pWin = ExprHasProperty(pExpr, EP_WinFunc) ? pExpr->y.pWin : 0; #else - pFrom = sqlcipher_sqlite3DefaultMutex(); + pWin = 0; #endif - }else{ - pFrom = sqlcipher_sqlite3NoopMutex(); + } + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + if( pExpr->op==TK_AGG_FUNCTION ){ + sqlcipher_sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s agg=%d[%d]/%p", + pExpr->op2, pExpr->u.zToken, zFlgs, + pExpr->pAggInfo ? pExpr->pAggInfo->selId : 0, + pExpr->iAgg, pExpr->pAggInfo); + }else if( pExpr->op2!=0 ){ + const char *zOp2; + char zBuf[8]; + sqlcipher_sqlite3_snprintf(sizeof(zBuf),zBuf,"0x%02x",pExpr->op2); + zOp2 = zBuf; + if( pExpr->op2==NC_IsCheck ) zOp2 = "NC_IsCheck"; + if( pExpr->op2==NC_IdxExpr ) zOp2 = "NC_IdxExpr"; + if( pExpr->op2==NC_PartIdx ) zOp2 = "NC_PartIdx"; + if( pExpr->op2==NC_GenCol ) zOp2 = "NC_GenCol"; + sqlcipher_sqlite3TreeViewLine(pView, "FUNCTION %Q%s op2=%s", + pExpr->u.zToken, zFlgs, zOp2); + }else{ + sqlcipher_sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs); + } + if( pFarg ){ + sqlcipher_sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0); + } +#ifndef SQLITE_OMIT_WINDOWFUNC + if( pWin ){ + sqlcipher_sqlite3TreeViewWindow(pView, pWin, 0); + } +#endif + break; + } +#ifndef SQLITE_OMIT_SUBQUERY + case TK_EXISTS: { + assert( ExprUseXSelect(pExpr) ); + sqlcipher_sqlite3TreeViewLine(pView, "EXISTS-expr flags=0x%x", pExpr->flags); + sqlcipher_sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); + break; + } + case TK_SELECT: { + assert( ExprUseXSelect(pExpr) ); + sqlcipher_sqlite3TreeViewLine(pView, "subquery-expr flags=0x%x", pExpr->flags); + sqlcipher_sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); + break; + } + case TK_IN: { + sqlcipher_sqlite3_str *pStr = sqlcipher_sqlite3_str_new(0); + char *z; + sqlcipher_sqlite3_str_appendf(pStr, "IN flags=0x%x", pExpr->flags); + if( pExpr->iTable ) sqlcipher_sqlite3_str_appendf(pStr, " iTable=%d",pExpr->iTable); + if( ExprHasProperty(pExpr, EP_Subrtn) ){ + sqlcipher_sqlite3_str_appendf(pStr, " subrtn(%d,%d)", + pExpr->y.sub.regReturn, pExpr->y.sub.iAddr); + } + z = sqlcipher_sqlite3_str_finish(pStr); + sqlcipher_sqlite3TreeViewLine(pView, z); + sqlcipher_sqlite3_free(z); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); + if( ExprUseXSelect(pExpr) ){ + sqlcipher_sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); + }else{ + sqlcipher_sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); + } + break; + } +#endif /* SQLITE_OMIT_SUBQUERY */ + + /* + ** x BETWEEN y AND z + ** + ** This is equivalent to + ** + ** x>=y AND x<=z + ** + ** X is stored in pExpr->pLeft. + ** Y is stored in pExpr->pList->a[0].pExpr. + ** Z is stored in pExpr->pList->a[1].pExpr. + */ + case TK_BETWEEN: { + const Expr *pX, *pY, *pZ; + pX = pExpr->pLeft; + assert( ExprUseXList(pExpr) ); + assert( pExpr->x.pList->nExpr==2 ); + pY = pExpr->x.pList->a[0].pExpr; + pZ = pExpr->x.pList->a[1].pExpr; + sqlcipher_sqlite3TreeViewLine(pView, "BETWEEN"); + sqlcipher_sqlite3TreeViewExpr(pView, pX, 1); + sqlcipher_sqlite3TreeViewExpr(pView, pY, 1); + sqlcipher_sqlite3TreeViewExpr(pView, pZ, 0); + break; + } + case TK_TRIGGER: { + /* If the opcode is TK_TRIGGER, then the expression is a reference + ** to a column in the new.* or old.* pseudo-tables available to + ** trigger programs. In this case Expr.iTable is set to 1 for the + ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn + ** is set to the column of the pseudo-table to read, or to -1 to + ** read the rowid field. + */ + sqlcipher_sqlite3TreeViewLine(pView, "%s(%d)", + pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn); + break; + } + case TK_CASE: { + sqlcipher_sqlite3TreeViewLine(pView, "CASE"); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); + assert( ExprUseXList(pExpr) ); + sqlcipher_sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); + break; + } +#ifndef SQLITE_OMIT_TRIGGER + case TK_RAISE: { + const char *zType = "unk"; + switch( pExpr->affExpr ){ + case OE_Rollback: zType = "rollback"; break; + case OE_Abort: zType = "abort"; break; + case OE_Fail: zType = "fail"; break; + case OE_Ignore: zType = "ignore"; break; + } + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlcipher_sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken); + break; + } +#endif + case TK_MATCH: { + sqlcipher_sqlite3TreeViewLine(pView, "MATCH {%d:%d}%s", + pExpr->iTable, pExpr->iColumn, zFlgs); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pRight, 0); + break; + } + case TK_VECTOR: { + char *z = sqlcipher_sqlite3_mprintf("VECTOR%s",zFlgs); + assert( ExprUseXList(pExpr) ); + sqlcipher_sqlite3TreeViewBareExprList(pView, pExpr->x.pList, z); + sqlcipher_sqlite3_free(z); + break; + } + case TK_SELECT_COLUMN: { + sqlcipher_sqlite3TreeViewLine(pView, "SELECT-COLUMN %d of [0..%d]%s", + pExpr->iColumn, pExpr->iTable-1, + pExpr->pRight==pExpr->pLeft ? " (SELECT-owner)" : ""); + assert( ExprUseXSelect(pExpr->pLeft) ); + sqlcipher_sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0); + break; + } + case TK_IF_NULL_ROW: { + sqlcipher_sqlite3TreeViewLine(pView, "IF-NULL-ROW %d", pExpr->iTable); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + break; + } + case TK_ERROR: { + Expr tmp; + sqlcipher_sqlite3TreeViewLine(pView, "ERROR"); + tmp = *pExpr; + tmp.op = pExpr->op2; + sqlcipher_sqlite3TreeViewExpr(pView, &tmp, 0); + break; + } + case TK_ROW: { + if( pExpr->iColumn<=0 ){ + sqlcipher_sqlite3TreeViewLine(pView, "First FROM table rowid"); + }else{ + sqlcipher_sqlite3TreeViewLine(pView, "First FROM table column %d", + pExpr->iColumn-1); + } + break; + } + default: { + sqlcipher_sqlite3TreeViewLine(pView, "op=%d", pExpr->op); + break; + } + } + if( zBinOp ){ + sqlcipher_sqlite3TreeViewLine(pView, "%s%s", zBinOp, zFlgs); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pRight, 0); + }else if( zUniOp ){ + sqlcipher_sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs); + sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + } + sqlcipher_sqlite3TreeViewPop(&pView); +} + + +/* +** Generate a human-readable explanation of an expression list. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewBareExprList( + TreeView *pView, + const ExprList *pList, + const char *zLabel +){ + if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST"; + if( pList==0 ){ + sqlcipher_sqlite3TreeViewLine(pView, "%s (empty)", zLabel); + }else{ + int i; + sqlcipher_sqlite3TreeViewLine(pView, "%s", zLabel); + for(i=0; inExpr; i++){ + int j = pList->a[i].u.x.iOrderByCol; + char *zName = pList->a[i].zEName; + int moreToFollow = inExpr - 1; + if( j || zName ){ + sqlcipher_sqlite3TreeViewPush(&pView, moreToFollow); + moreToFollow = 0; + sqlcipher_sqlite3TreeViewLine(pView, 0); + if( zName ){ + switch( pList->a[i].fg.eEName ){ + default: + fprintf(stdout, "AS %s ", zName); + break; + case ENAME_TAB: + fprintf(stdout, "TABLE-ALIAS-NAME(\"%s\") ", zName); + if( pList->a[i].fg.bUsed ) fprintf(stdout, "(used) "); + if( pList->a[i].fg.bUsingTerm ) fprintf(stdout, "(USING-term) "); + if( pList->a[i].fg.bNoExpand ) fprintf(stdout, "(NoExpand) "); + break; + case ENAME_SPAN: + fprintf(stdout, "SPAN(\"%s\") ", zName); + break; + } + } + if( j ){ + fprintf(stdout, "iOrderByCol=%d", j); + } + fprintf(stdout, "\n"); + fflush(stdout); + } + sqlcipher_sqlite3TreeViewExpr(pView, pList->a[i].pExpr, moreToFollow); + if( j || zName ){ + sqlcipher_sqlite3TreeViewPop(&pView); + } } - pTo->xMutexInit = pFrom->xMutexInit; - pTo->xMutexEnd = pFrom->xMutexEnd; - pTo->xMutexFree = pFrom->xMutexFree; - pTo->xMutexEnter = pFrom->xMutexEnter; - pTo->xMutexTry = pFrom->xMutexTry; - pTo->xMutexLeave = pFrom->xMutexLeave; - pTo->xMutexHeld = pFrom->xMutexHeld; - pTo->xMutexNotheld = pFrom->xMutexNotheld; - sqlcipher_sqlite3MemoryBarrier(); - pTo->xMutexAlloc = pFrom->xMutexAlloc; } - assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexInit ); - rc = sqlcipher_sqlite3GlobalConfig.mutex.xMutexInit(); - -#ifdef SQLITE_DEBUG - GLOBAL(int, mutexIsInit) = 1; -#endif - - sqlcipher_sqlite3MemoryBarrier(); - return rc; +} +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewExprList( + TreeView *pView, + const ExprList *pList, + u8 moreToFollow, + const char *zLabel +){ + sqlcipher_sqlite3TreeViewPush(&pView, moreToFollow); + sqlcipher_sqlite3TreeViewBareExprList(pView, pList, zLabel); + sqlcipher_sqlite3TreeViewPop(&pView); } /* -** Shutdown the mutex system. This call frees resources allocated by -** sqlcipher_sqlite3MutexInit(). +** Generate a human-readable explanation of an id-list. */ -SQLITE_PRIVATE int sqlcipher_sqlite3MutexEnd(void){ - int rc = SQLITE_OK; - if( sqlcipher_sqlite3GlobalConfig.mutex.xMutexEnd ){ - rc = sqlcipher_sqlite3GlobalConfig.mutex.xMutexEnd(); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewBareIdList( + TreeView *pView, + const IdList *pList, + const char *zLabel +){ + if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST"; + if( pList==0 ){ + sqlcipher_sqlite3TreeViewLine(pView, "%s (empty)", zLabel); + }else{ + int i; + sqlcipher_sqlite3TreeViewLine(pView, "%s", zLabel); + for(i=0; inId; i++){ + char *zName = pList->a[i].zName; + int moreToFollow = inId - 1; + if( zName==0 ) zName = "(null)"; + sqlcipher_sqlite3TreeViewPush(&pView, moreToFollow); + sqlcipher_sqlite3TreeViewLine(pView, 0); + if( pList->eU4==EU4_NONE ){ + fprintf(stdout, "%s\n", zName); + }else if( pList->eU4==EU4_IDX ){ + fprintf(stdout, "%s (%d)\n", zName, pList->a[i].u4.idx); + }else{ + assert( pList->eU4==EU4_EXPR ); + if( pList->a[i].u4.pExpr==0 ){ + fprintf(stdout, "%s (pExpr=NULL)\n", zName); + }else{ + fprintf(stdout, "%s\n", zName); + sqlcipher_sqlite3TreeViewPush(&pView, inId-1); + sqlcipher_sqlite3TreeViewExpr(pView, pList->a[i].u4.pExpr, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + } + sqlcipher_sqlite3TreeViewPop(&pView); + } } - -#ifdef SQLITE_DEBUG - GLOBAL(int, mutexIsInit) = 0; -#endif - - return rc; +} +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewIdList( + TreeView *pView, + const IdList *pList, + u8 moreToFollow, + const char *zLabel +){ + sqlcipher_sqlite3TreeViewPush(&pView, moreToFollow); + sqlcipher_sqlite3TreeViewBareIdList(pView, pList, zLabel); + sqlcipher_sqlite3TreeViewPop(&pView); } /* -** Retrieve a pointer to a static mutex or allocate a new dynamic one. +** Generate a human-readable explanation of a list of Upsert objects */ -SQLITE_API sqlcipher_sqlite3_mutex *sqlcipher_sqlite3_mutex_alloc(int id){ -#ifndef SQLITE_OMIT_AUTOINIT - if( id<=SQLITE_MUTEX_RECURSIVE && sqlcipher_sqlite3_initialize() ) return 0; - if( id>SQLITE_MUTEX_RECURSIVE && sqlcipher_sqlite3MutexInit() ) return 0; -#endif - assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc ); - return sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc(id); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewUpsert( + TreeView *pView, + const Upsert *pUpsert, + u8 moreToFollow +){ + if( pUpsert==0 ) return; + sqlcipher_sqlite3TreeViewPush(&pView, moreToFollow); + while( pUpsert ){ + int n; + sqlcipher_sqlite3TreeViewPush(&pView, pUpsert->pNextUpsert!=0 || moreToFollow); + sqlcipher_sqlite3TreeViewLine(pView, "ON CONFLICT DO %s", + pUpsert->isDoUpdate ? "UPDATE" : "NOTHING"); + n = (pUpsert->pUpsertSet!=0) + (pUpsert->pUpsertWhere!=0); + sqlcipher_sqlite3TreeViewExprList(pView, pUpsert->pUpsertTarget, (n--)>0, "TARGET"); + sqlcipher_sqlite3TreeViewExprList(pView, pUpsert->pUpsertSet, (n--)>0, "SET"); + if( pUpsert->pUpsertWhere ){ + sqlcipher_sqlite3TreeViewItem(pView, "WHERE", (n--)>0); + sqlcipher_sqlite3TreeViewExpr(pView, pUpsert->pUpsertWhere, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + sqlcipher_sqlite3TreeViewPop(&pView); + pUpsert = pUpsert->pNextUpsert; + } + sqlcipher_sqlite3TreeViewPop(&pView); } -SQLITE_PRIVATE sqlcipher_sqlite3_mutex *sqlcipher_sqlite3MutexAlloc(int id){ - if( !sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ - return 0; +#if TREETRACE_ENABLED +/* +** Generate a human-readable diagram of the data structure that go +** into generating an DELETE statement. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewDelete( + const With *pWith, + const SrcList *pTabList, + const Expr *pWhere, + const ExprList *pOrderBy, + const Expr *pLimit, + const Trigger *pTrigger +){ + int n = 0; + TreeView *pView = 0; + sqlcipher_sqlite3TreeViewPush(&pView, 0); + sqlcipher_sqlite3TreeViewLine(pView, "DELETE"); + if( pWith ) n++; + if( pTabList ) n++; + if( pWhere ) n++; + if( pOrderBy ) n++; + if( pLimit ) n++; + if( pTrigger ) n++; + if( pWith ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewWith(pView, pWith, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pTabList ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewLine(pView, "FROM"); + sqlcipher_sqlite3TreeViewSrcList(pView, pTabList); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pWhere ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewLine(pView, "WHERE"); + sqlcipher_sqlite3TreeViewExpr(pView, pWhere, 0); + sqlcipher_sqlite3TreeViewPop(&pView); } - assert( GLOBAL(int, mutexIsInit) ); - assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc ); - return sqlcipher_sqlite3GlobalConfig.mutex.xMutexAlloc(id); + if( pOrderBy ){ + sqlcipher_sqlite3TreeViewExprList(pView, pOrderBy, (--n)>0, "ORDER-BY"); + } + if( pLimit ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewLine(pView, "LIMIT"); + sqlcipher_sqlite3TreeViewExpr(pView, pLimit, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pTrigger ){ + sqlcipher_sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); + } + sqlcipher_sqlite3TreeViewPop(&pView); } +#endif /* TREETRACE_ENABLED */ +#if TREETRACE_ENABLED /* -** Free a dynamic mutex. +** Generate a human-readable diagram of the data structure that go +** into generating an INSERT statement. */ -SQLITE_API void sqlcipher_sqlite3_mutex_free(sqlcipher_sqlite3_mutex *p){ - if( p ){ - assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexFree ); - sqlcipher_sqlite3GlobalConfig.mutex.xMutexFree(p); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewInsert( + const With *pWith, + const SrcList *pTabList, + const IdList *pColumnList, + const Select *pSelect, + const ExprList *pExprList, + int onError, + const Upsert *pUpsert, + const Trigger *pTrigger +){ + TreeView *pView = 0; + int n = 0; + const char *zLabel = "INSERT"; + switch( onError ){ + case OE_Replace: zLabel = "REPLACE"; break; + case OE_Ignore: zLabel = "INSERT OR IGNORE"; break; + case OE_Rollback: zLabel = "INSERT OR ROLLBACK"; break; + case OE_Abort: zLabel = "INSERT OR ABORT"; break; + case OE_Fail: zLabel = "INSERT OR FAIL"; break; + } + sqlcipher_sqlite3TreeViewPush(&pView, 0); + sqlcipher_sqlite3TreeViewLine(pView, zLabel); + if( pWith ) n++; + if( pTabList ) n++; + if( pColumnList ) n++; + if( pSelect ) n++; + if( pExprList ) n++; + if( pUpsert ) n++; + if( pTrigger ) n++; + if( pWith ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewWith(pView, pWith, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pTabList ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewLine(pView, "INTO"); + sqlcipher_sqlite3TreeViewSrcList(pView, pTabList); + sqlcipher_sqlite3TreeViewPop(&pView); } + if( pColumnList ){ + sqlcipher_sqlite3TreeViewIdList(pView, pColumnList, (--n)>0, "COLUMNS"); + } + if( pSelect ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewLine(pView, "DATA-SOURCE"); + sqlcipher_sqlite3TreeViewSelect(pView, pSelect, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pExprList ){ + sqlcipher_sqlite3TreeViewExprList(pView, pExprList, (--n)>0, "VALUES"); + } + if( pUpsert ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewLine(pView, "UPSERT"); + sqlcipher_sqlite3TreeViewUpsert(pView, pUpsert, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pTrigger ){ + sqlcipher_sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); + } + sqlcipher_sqlite3TreeViewPop(&pView); } +#endif /* TREETRACE_ENABLED */ +#if TREETRACE_ENABLED /* -** Obtain the mutex p. If some other thread already has the mutex, block -** until it can be obtained. +** Generate a human-readable diagram of the data structure that go +** into generating an UPDATE statement. */ -SQLITE_API void sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3_mutex *p){ - if( p ){ - assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexEnter ); - sqlcipher_sqlite3GlobalConfig.mutex.xMutexEnter(p); +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewUpdate( + const With *pWith, + const SrcList *pTabList, + const ExprList *pChanges, + const Expr *pWhere, + int onError, + const ExprList *pOrderBy, + const Expr *pLimit, + const Upsert *pUpsert, + const Trigger *pTrigger +){ + int n = 0; + TreeView *pView = 0; + const char *zLabel = "UPDATE"; + switch( onError ){ + case OE_Replace: zLabel = "UPDATE OR REPLACE"; break; + case OE_Ignore: zLabel = "UPDATE OR IGNORE"; break; + case OE_Rollback: zLabel = "UPDATE OR ROLLBACK"; break; + case OE_Abort: zLabel = "UPDATE OR ABORT"; break; + case OE_Fail: zLabel = "UPDATE OR FAIL"; break; + } + sqlcipher_sqlite3TreeViewPush(&pView, 0); + sqlcipher_sqlite3TreeViewLine(pView, zLabel); + if( pWith ) n++; + if( pTabList ) n++; + if( pChanges ) n++; + if( pWhere ) n++; + if( pOrderBy ) n++; + if( pLimit ) n++; + if( pUpsert ) n++; + if( pTrigger ) n++; + if( pWith ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewWith(pView, pWith, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pTabList ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewLine(pView, "FROM"); + sqlcipher_sqlite3TreeViewSrcList(pView, pTabList); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pChanges ){ + sqlcipher_sqlite3TreeViewExprList(pView, pChanges, (--n)>0, "SET"); + } + if( pWhere ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewLine(pView, "WHERE"); + sqlcipher_sqlite3TreeViewExpr(pView, pWhere, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pOrderBy ){ + sqlcipher_sqlite3TreeViewExprList(pView, pOrderBy, (--n)>0, "ORDER-BY"); + } + if( pLimit ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewLine(pView, "LIMIT"); + sqlcipher_sqlite3TreeViewExpr(pView, pLimit, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pUpsert ){ + sqlcipher_sqlite3TreeViewPush(&pView, (--n)>0); + sqlcipher_sqlite3TreeViewLine(pView, "UPSERT"); + sqlcipher_sqlite3TreeViewUpsert(pView, pUpsert, 0); + sqlcipher_sqlite3TreeViewPop(&pView); + } + if( pTrigger ){ + sqlcipher_sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); } + sqlcipher_sqlite3TreeViewPop(&pView); } +#endif /* TREETRACE_ENABLED */ +#ifndef SQLITE_OMIT_TRIGGER /* -** Obtain the mutex p. If successful, return SQLITE_OK. Otherwise, if another -** thread holds the mutex and it cannot be obtained, return SQLITE_BUSY. +** Show a human-readable graph of a TriggerStep */ -SQLITE_API int sqlcipher_sqlite3_mutex_try(sqlcipher_sqlite3_mutex *p){ - int rc = SQLITE_OK; - if( p ){ - assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexTry ); - return sqlcipher_sqlite3GlobalConfig.mutex.xMutexTry(p); - } - return rc; +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewTriggerStep( + TreeView *pView, + const TriggerStep *pStep, + u8 moreToFollow, + u8 showFullList +){ + int cnt = 0; + if( pStep==0 ) return; + sqlcipher_sqlite3TreeViewPush(&pView, + moreToFollow || (showFullList && pStep->pNext!=0)); + do{ + if( cnt++ && pStep->pNext==0 ){ + sqlcipher_sqlite3TreeViewPop(&pView); + sqlcipher_sqlite3TreeViewPush(&pView, 0); + } + sqlcipher_sqlite3TreeViewLine(pView, "%s", pStep->zSpan ? pStep->zSpan : "RETURNING"); + }while( showFullList && (pStep = pStep->pNext)!=0 ); + sqlcipher_sqlite3TreeViewPop(&pView); } /* -** The sqlcipher_sqlite3_mutex_leave() routine exits a mutex that was previously -** entered by the same thread. The behavior is undefined if the mutex -** is not currently entered. If a NULL pointer is passed as an argument -** this function is a no-op. +** Show a human-readable graph of a Trigger */ -SQLITE_API void sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3_mutex *p){ - if( p ){ - assert( sqlcipher_sqlite3GlobalConfig.mutex.xMutexLeave ); - sqlcipher_sqlite3GlobalConfig.mutex.xMutexLeave(p); - } +SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewTrigger( + TreeView *pView, + const Trigger *pTrigger, + u8 moreToFollow, + u8 showFullList +){ + int cnt = 0; + if( pTrigger==0 ) return; + sqlcipher_sqlite3TreeViewPush(&pView, + moreToFollow || (showFullList && pTrigger->pNext!=0)); + do{ + if( cnt++ && pTrigger->pNext==0 ){ + sqlcipher_sqlite3TreeViewPop(&pView); + sqlcipher_sqlite3TreeViewPush(&pView, 0); + } + sqlcipher_sqlite3TreeViewLine(pView, "TRIGGER %s", pTrigger->zName); + sqlcipher_sqlite3TreeViewPush(&pView, 0); + sqlcipher_sqlite3TreeViewTriggerStep(pView, pTrigger->step_list, 0, 1); + sqlcipher_sqlite3TreeViewPop(&pView); + }while( showFullList && (pTrigger = pTrigger->pNext)!=0 ); + sqlcipher_sqlite3TreeViewPop(&pView); } +#endif /* SQLITE_OMIT_TRIGGER */ + -#ifndef NDEBUG /* -** The sqlcipher_sqlite3_mutex_held() and sqlcipher_sqlite3_mutex_notheld() routine are -** intended for use inside assert() statements. +** These simplified versions of the tree-view routines omit unnecessary +** parameters. These variants are intended to be used from a symbolic +** debugger, such as "gdb", during interactive debugging sessions. +** +** This routines are given external linkage so that they will always be +** accessible to the debugging, and to avoid warnings about unused +** functions. But these routines only exist in debugging builds, so they +** do not contaminate the interface. */ -SQLITE_API int sqlcipher_sqlite3_mutex_held(sqlcipher_sqlite3_mutex *p){ - assert( p==0 || sqlcipher_sqlite3GlobalConfig.mutex.xMutexHeld ); - return p==0 || sqlcipher_sqlite3GlobalConfig.mutex.xMutexHeld(p); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowExpr(const Expr *p){ sqlcipher_sqlite3TreeViewExpr(0,p,0); } +SQLITE_PRIVATE void sqlcipher_sqlite3ShowExprList(const ExprList *p){ sqlcipher_sqlite3TreeViewExprList(0,p,0,0);} +SQLITE_PRIVATE void sqlcipher_sqlite3ShowIdList(const IdList *p){ sqlcipher_sqlite3TreeViewIdList(0,p,0,0); } +SQLITE_PRIVATE void sqlcipher_sqlite3ShowSrcList(const SrcList *p){ sqlcipher_sqlite3TreeViewSrcList(0,p); } +SQLITE_PRIVATE void sqlcipher_sqlite3ShowSelect(const Select *p){ sqlcipher_sqlite3TreeViewSelect(0,p,0); } +SQLITE_PRIVATE void sqlcipher_sqlite3ShowWith(const With *p){ sqlcipher_sqlite3TreeViewWith(0,p,0); } +SQLITE_PRIVATE void sqlcipher_sqlite3ShowUpsert(const Upsert *p){ sqlcipher_sqlite3TreeViewUpsert(0,p,0); } +#ifndef SQLITE_OMIT_TRIGGER +SQLITE_PRIVATE void sqlcipher_sqlite3ShowTriggerStep(const TriggerStep *p){ + sqlcipher_sqlite3TreeViewTriggerStep(0,p,0,0); } -SQLITE_API int sqlcipher_sqlite3_mutex_notheld(sqlcipher_sqlite3_mutex *p){ - assert( p==0 || sqlcipher_sqlite3GlobalConfig.mutex.xMutexNotheld ); - return p==0 || sqlcipher_sqlite3GlobalConfig.mutex.xMutexNotheld(p); +SQLITE_PRIVATE void sqlcipher_sqlite3ShowTriggerStepList(const TriggerStep *p){ + sqlcipher_sqlite3TreeViewTriggerStep(0,p,0,1); } +SQLITE_PRIVATE void sqlcipher_sqlite3ShowTrigger(const Trigger *p){ sqlcipher_sqlite3TreeViewTrigger(0,p,0,0); } +SQLITE_PRIVATE void sqlcipher_sqlite3ShowTriggerList(const Trigger *p){ sqlcipher_sqlite3TreeViewTrigger(0,p,0,1);} +#endif +#ifndef SQLITE_OMIT_WINDOWFUNC +SQLITE_PRIVATE void sqlcipher_sqlite3ShowWindow(const Window *p){ sqlcipher_sqlite3TreeViewWindow(0,p,0); } +SQLITE_PRIVATE void sqlcipher_sqlite3ShowWinFunc(const Window *p){ sqlcipher_sqlite3TreeViewWinFunc(0,p,0); } #endif -#endif /* !defined(SQLITE_MUTEX_OMIT) */ +#endif /* SQLITE_DEBUG */ -/************** End of mutex.c ***********************************************/ -/************** Begin file mutex_noop.c **************************************/ +/************** End of treeview.c ********************************************/ +/************** Begin file random.c ******************************************/ /* -** 2008 October 07 +** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -31268,215 +32434,139 @@ SQLITE_API int sqlcipher_sqlite3_mutex_notheld(sqlcipher_sqlite3_mutex *p){ ** May you share freely, never taking more than you give. ** ************************************************************************* -** This file contains the C functions that implement mutexes. -** -** This implementation in this file does not provide any mutual -** exclusion and is thus suitable for use only in applications -** that use SQLite in a single thread. The routines defined -** here are place-holders. Applications can substitute working -** mutex routines at start-time using the -** -** sqlcipher_sqlite3_config(SQLITE_CONFIG_MUTEX,...) -** -** interface. +** This file contains code to implement a pseudo-random number +** generator (PRNG) for SQLite. ** -** If compiled with SQLITE_DEBUG, then additional logic is inserted -** that does error checking on mutexes to make sure they are being -** called correctly. +** Random numbers are used by some of the database backends in order +** to generate random integer keys for tables or random filenames. */ /* #include "sqliteInt.h" */ -#ifndef SQLITE_MUTEX_OMIT -#ifndef SQLITE_DEBUG -/* -** Stub routines for all mutex methods. -** -** This routines provide no mutual exclusion or error checking. +/* All threads share a single random number generator. +** This structure is the current state of the generator. */ -static int noopMutexInit(void){ return SQLITE_OK; } -static int noopMutexEnd(void){ return SQLITE_OK; } -static sqlcipher_sqlite3_mutex *noopMutexAlloc(int id){ - UNUSED_PARAMETER(id); - return (sqlcipher_sqlite3_mutex*)8; -} -static void noopMutexFree(sqlcipher_sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } -static void noopMutexEnter(sqlcipher_sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } -static int noopMutexTry(sqlcipher_sqlite3_mutex *p){ - UNUSED_PARAMETER(p); - return SQLITE_OK; -} -static void noopMutexLeave(sqlcipher_sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } - -SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3NoopMutex(void){ - static const sqlcipher_sqlite3_mutex_methods sMutex = { - noopMutexInit, - noopMutexEnd, - noopMutexAlloc, - noopMutexFree, - noopMutexEnter, - noopMutexTry, - noopMutexLeave, - - 0, - 0, - }; - - return &sMutex; -} -#endif /* !SQLITE_DEBUG */ +static SQLITE_WSD struct sqlcipher_sqlite3PrngType { + unsigned char isInit; /* True if initialized */ + unsigned char i, j; /* State variables */ + unsigned char s[256]; /* State variables */ +} sqlcipher_sqlite3Prng; -#ifdef SQLITE_DEBUG /* -** In this implementation, error checking is provided for testing -** and debugging purposes. The mutexes still do not provide any -** mutual exclusion. +** Return N random bytes. */ +SQLITE_API void sqlcipher_sqlite3_randomness(int N, void *pBuf){ + unsigned char t; + unsigned char *zBuf = pBuf; -/* -** The mutex object -*/ -typedef struct sqlcipher_sqlite3_debug_mutex { - int id; /* The mutex type */ - int cnt; /* Number of entries without a matching leave */ -} sqlcipher_sqlite3_debug_mutex; + /* The "wsdPrng" macro will resolve to the pseudo-random number generator + ** state vector. If writable static data is unsupported on the target, + ** we have to locate the state vector at run-time. In the more common + ** case where writable static data is supported, wsdPrng can refer directly + ** to the "sqlcipher_sqlite3Prng" state vector declared above. + */ +#ifdef SQLITE_OMIT_WSD + struct sqlcipher_sqlite3PrngType *p = &GLOBAL(struct sqlcipher_sqlite3PrngType, sqlcipher_sqlite3Prng); +# define wsdPrng p[0] +#else +# define wsdPrng sqlcipher_sqlite3Prng +#endif -/* -** The sqlcipher_sqlite3_mutex_held() and sqlcipher_sqlite3_mutex_notheld() routine are -** intended for use inside assert() statements. -*/ -static int debugMutexHeld(sqlcipher_sqlite3_mutex *pX){ - sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; - return p==0 || p->cnt>0; -} -static int debugMutexNotheld(sqlcipher_sqlite3_mutex *pX){ - sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; - return p==0 || p->cnt==0; -} +#if SQLITE_THREADSAFE + sqlcipher_sqlite3_mutex *mutex; +#endif -/* -** Initialize and deinitialize the mutex subsystem. -*/ -static int debugMutexInit(void){ return SQLITE_OK; } -static int debugMutexEnd(void){ return SQLITE_OK; } +#ifndef SQLITE_OMIT_AUTOINIT + if( sqlcipher_sqlite3_initialize() ) return; +#endif -/* -** The sqlcipher_sqlite3_mutex_alloc() routine allocates a new -** mutex and returns a pointer to it. If it returns NULL -** that means that a mutex could not be allocated. -*/ -static sqlcipher_sqlite3_mutex *debugMutexAlloc(int id){ - static sqlcipher_sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_VFS3 - 1]; - sqlcipher_sqlite3_debug_mutex *pNew = 0; - switch( id ){ - case SQLITE_MUTEX_FAST: - case SQLITE_MUTEX_RECURSIVE: { - pNew = sqlcipher_sqlite3Malloc(sizeof(*pNew)); - if( pNew ){ - pNew->id = id; - pNew->cnt = 0; - } - break; - } - default: { -#ifdef SQLITE_ENABLE_API_ARMOR - if( id-2<0 || id-2>=ArraySize(aStatic) ){ - (void)SQLITE_MISUSE_BKPT; - return 0; - } +#if SQLITE_THREADSAFE + mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG); #endif - pNew = &aStatic[id-2]; - pNew->id = id; - break; - } + + sqlcipher_sqlite3_mutex_enter(mutex); + if( N<=0 || pBuf==0 ){ + wsdPrng.isInit = 0; + sqlcipher_sqlite3_mutex_leave(mutex); + return; } - return (sqlcipher_sqlite3_mutex*)pNew; -} -/* -** This routine deallocates a previously allocated mutex. -*/ -static void debugMutexFree(sqlcipher_sqlite3_mutex *pX){ - sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; - assert( p->cnt==0 ); - if( p->id==SQLITE_MUTEX_RECURSIVE || p->id==SQLITE_MUTEX_FAST ){ - sqlcipher_sqlite3_free(p); - }else{ -#ifdef SQLITE_ENABLE_API_ARMOR - (void)SQLITE_MISUSE_BKPT; -#endif + /* Initialize the state of the random number generator once, + ** the first time this routine is called. The seed value does + ** not need to contain a lot of randomness since we are not + ** trying to do secure encryption or anything like that... + ** + ** Nothing in this file or anywhere else in SQLite does any kind of + ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random + ** number generator) not as an encryption device. + */ + if( !wsdPrng.isInit ){ + sqlcipher_sqlite3_vfs *pVfs = sqlcipher_sqlite3_vfs_find(0); + int i; + char k[256]; + wsdPrng.j = 0; + wsdPrng.i = 0; + if( NEVER(pVfs==0) ){ + memset(k, 0, sizeof(k)); + }else{ + sqlcipher_sqlite3OsRandomness(pVfs, 256, k); + } + for(i=0; i<256; i++){ + wsdPrng.s[i] = (u8)i; + } + for(i=0; i<256; i++){ + wsdPrng.j += wsdPrng.s[i] + k[i]; + t = wsdPrng.s[wsdPrng.j]; + wsdPrng.s[wsdPrng.j] = wsdPrng.s[i]; + wsdPrng.s[i] = t; + } + wsdPrng.isInit = 1; } -} -/* -** The sqlcipher_sqlite3_mutex_enter() and sqlcipher_sqlite3_mutex_try() routines attempt -** to enter a mutex. If another thread is already within the mutex, -** sqlcipher_sqlite3_mutex_enter() will block and sqlcipher_sqlite3_mutex_try() will return -** SQLITE_BUSY. The sqlcipher_sqlite3_mutex_try() interface returns SQLITE_OK -** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can -** be entered multiple times by the same thread. In such cases the, -** mutex must be exited an equal number of times before another thread -** can enter. If the same thread tries to enter any other kind of mutex -** more than once, the behavior is undefined. -*/ -static void debugMutexEnter(sqlcipher_sqlite3_mutex *pX){ - sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; - assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); - p->cnt++; -} -static int debugMutexTry(sqlcipher_sqlite3_mutex *pX){ - sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; - assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); - p->cnt++; - return SQLITE_OK; + assert( N>0 ); + do{ + wsdPrng.i++; + t = wsdPrng.s[wsdPrng.i]; + wsdPrng.j += t; + wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j]; + wsdPrng.s[wsdPrng.j] = t; + t += wsdPrng.s[wsdPrng.i]; + *(zBuf++) = wsdPrng.s[t]; + }while( --N ); + sqlcipher_sqlite3_mutex_leave(mutex); } +#ifndef SQLITE_UNTESTABLE /* -** The sqlcipher_sqlite3_mutex_leave() routine exits a mutex that was -** previously entered by the same thread. The behavior -** is undefined if the mutex is not currently entered or -** is not currently allocated. SQLite will never do either. +** For testing purposes, we sometimes want to preserve the state of +** PRNG and restore the PRNG to its saved state at a later time, or +** to reset the PRNG to its initial state. These routines accomplish +** those tasks. +** +** The sqlcipher_sqlite3_test_control() interface calls these routines to +** control the PRNG. */ -static void debugMutexLeave(sqlcipher_sqlite3_mutex *pX){ - sqlcipher_sqlite3_debug_mutex *p = (sqlcipher_sqlite3_debug_mutex*)pX; - assert( debugMutexHeld(pX) ); - p->cnt--; - assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); -} - -SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3NoopMutex(void){ - static const sqlcipher_sqlite3_mutex_methods sMutex = { - debugMutexInit, - debugMutexEnd, - debugMutexAlloc, - debugMutexFree, - debugMutexEnter, - debugMutexTry, - debugMutexLeave, - - debugMutexHeld, - debugMutexNotheld - }; - - return &sMutex; +static SQLITE_WSD struct sqlcipher_sqlite3PrngType sqlcipher_sqlite3SavedPrng; +SQLITE_PRIVATE void sqlcipher_sqlite3PrngSaveState(void){ + memcpy( + &GLOBAL(struct sqlcipher_sqlite3PrngType, sqlcipher_sqlite3SavedPrng), + &GLOBAL(struct sqlcipher_sqlite3PrngType, sqlcipher_sqlite3Prng), + sizeof(sqlcipher_sqlite3Prng) + ); } -#endif /* SQLITE_DEBUG */ - -/* -** If compiled with SQLITE_MUTEX_NOOP, then the no-op mutex implementation -** is used regardless of the run-time threadsafety setting. -*/ -#ifdef SQLITE_MUTEX_NOOP -SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3DefaultMutex(void){ - return sqlcipher_sqlite3NoopMutex(); +SQLITE_PRIVATE void sqlcipher_sqlite3PrngRestoreState(void){ + memcpy( + &GLOBAL(struct sqlcipher_sqlite3PrngType, sqlcipher_sqlite3Prng), + &GLOBAL(struct sqlcipher_sqlite3PrngType, sqlcipher_sqlite3SavedPrng), + sizeof(sqlcipher_sqlite3Prng) + ); } -#endif /* defined(SQLITE_MUTEX_NOOP) */ -#endif /* !defined(SQLITE_MUTEX_OMIT) */ +#endif /* SQLITE_UNTESTABLE */ -/************** End of mutex_noop.c ******************************************/ -/************** Begin file mutex_unix.c **************************************/ +/************** End of random.c **********************************************/ +/************** Begin file threads.c *****************************************/ /* -** 2007 August 28 +** 2012 July 21 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -31485,619 +32575,275 @@ SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3DefaultMu ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** -************************************************************************* -** This file contains the C functions that implement mutexes for pthreads -*/ -/* #include "sqliteInt.h" */ - -/* -** The code in this file is only used if we are compiling threadsafe -** under unix with pthreads. +****************************************************************************** ** -** Note that this implementation requires a version of pthreads that -** supports recursive mutexes. -*/ -#ifdef SQLITE_MUTEX_PTHREADS - -#include - -/* -** The sqlcipher_sqlite3_mutex.id, sqlcipher_sqlite3_mutex.nRef, and sqlcipher_sqlite3_mutex.owner fields -** are necessary under two condidtions: (1) Debug builds and (2) using -** home-grown mutexes. Encapsulate these conditions into a single #define. -*/ -#if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX) -# define SQLITE_MUTEX_NREF 1 -#else -# define SQLITE_MUTEX_NREF 0 -#endif - -/* -** Each recursive mutex is an instance of the following structure. -*/ -struct sqlcipher_sqlite3_mutex { - pthread_mutex_t mutex; /* Mutex controlling the lock */ -#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) - int id; /* Mutex type */ -#endif -#if SQLITE_MUTEX_NREF - volatile int nRef; /* Number of entrances */ - volatile pthread_t owner; /* Thread that is within this mutex */ - int trace; /* True to trace changes */ -#endif -}; -#if SQLITE_MUTEX_NREF -# define SQLITE3_MUTEX_INITIALIZER(id) \ - {PTHREAD_MUTEX_INITIALIZER,id,0,(pthread_t)0,0} -#elif defined(SQLITE_ENABLE_API_ARMOR) -# define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER, id } -#else -#define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER } -#endif - -/* -** The sqlcipher_sqlite3_mutex_held() and sqlcipher_sqlite3_mutex_notheld() routine are -** intended for use only inside assert() statements. On some platforms, -** there might be race conditions that can cause these routines to -** deliver incorrect results. In particular, if pthread_equal() is -** not an atomic operation, then these routines might delivery -** incorrect results. On most platforms, pthread_equal() is a -** comparison of two integers and is therefore atomic. But we are -** told that HPUX is not such a platform. If so, then these routines -** will not always work correctly on HPUX. +** This file presents a simple cross-platform threading interface for +** use internally by SQLite. ** -** On those platforms where pthread_equal() is not atomic, SQLite -** should be compiled without -DSQLITE_DEBUG and with -DNDEBUG to -** make sure no assert() statements are evaluated and hence these -** routines are never called. +** A "thread" can be created using sqlcipher_sqlite3ThreadCreate(). This thread +** runs independently of its creator until it is joined using +** sqlcipher_sqlite3ThreadJoin(), at which point it terminates. +** +** Threads do not have to be real. It could be that the work of the +** "thread" is done by the main thread at either the sqlcipher_sqlite3ThreadCreate() +** or sqlcipher_sqlite3ThreadJoin() call. This is, in fact, what happens in +** single threaded systems. Nothing in SQLite requires multiple threads. +** This interface exists so that applications that want to take advantage +** of multiple cores can do so, while also allowing applications to stay +** single-threaded if desired. */ -#if !defined(NDEBUG) || defined(SQLITE_DEBUG) -static int pthreadMutexHeld(sqlcipher_sqlite3_mutex *p){ - return (p->nRef!=0 && pthread_equal(p->owner, pthread_self())); -} -static int pthreadMutexNotheld(sqlcipher_sqlite3_mutex *p){ - return p->nRef==0 || pthread_equal(p->owner, pthread_self())==0; -} +/* #include "sqliteInt.h" */ +#if SQLITE_OS_WIN +/* # include "os_win.h" */ #endif -/* -** Try to provide a memory barrier operation, needed for initialization -** and also for the implementation of xShmBarrier in the VFS in cases -** where SQLite is compiled without mutexes. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3MemoryBarrier(void){ -#if defined(SQLITE_MEMORY_BARRIER) - SQLITE_MEMORY_BARRIER; -#elif defined(__GNUC__) && GCC_VERSION>=4001000 - __sync_synchronize(); -#endif -} +#if SQLITE_MAX_WORKER_THREADS>0 -/* -** Initialize and deinitialize the mutex subsystem. -*/ -static int pthreadMutexInit(void){ return SQLITE_OK; } -static int pthreadMutexEnd(void){ return SQLITE_OK; } +/********************************* Unix Pthreads ****************************/ +#if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0 -/* -** The sqlcipher_sqlite3_mutex_alloc() routine allocates a new -** mutex and returns a pointer to it. If it returns NULL -** that means that a mutex could not be allocated. SQLite -** will unwind its stack and return an error. The argument -** to sqlcipher_sqlite3_mutex_alloc() is one of these integer constants: -** -**
      -**
    • SQLITE_MUTEX_FAST -**
    • SQLITE_MUTEX_RECURSIVE -**
    • SQLITE_MUTEX_STATIC_MAIN -**
    • SQLITE_MUTEX_STATIC_MEM -**
    • SQLITE_MUTEX_STATIC_OPEN -**
    • SQLITE_MUTEX_STATIC_PRNG -**
    • SQLITE_MUTEX_STATIC_LRU -**
    • SQLITE_MUTEX_STATIC_PMEM -**
    • SQLITE_MUTEX_STATIC_APP1 -**
    • SQLITE_MUTEX_STATIC_APP2 -**
    • SQLITE_MUTEX_STATIC_APP3 -**
    • SQLITE_MUTEX_STATIC_VFS1 -**
    • SQLITE_MUTEX_STATIC_VFS2 -**
    • SQLITE_MUTEX_STATIC_VFS3 -**
    -** -** The first two constants cause sqlcipher_sqlite3_mutex_alloc() to create -** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE -** is used but not necessarily so when SQLITE_MUTEX_FAST is used. -** The mutex implementation does not need to make a distinction -** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does -** not want to. But SQLite will only request a recursive mutex in -** cases where it really needs one. If a faster non-recursive mutex -** implementation is available on the host platform, the mutex subsystem -** might return such a mutex in response to SQLITE_MUTEX_FAST. -** -** The other allowed parameters to sqlcipher_sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. Six static mutexes are -** used by the current version of SQLite. Future versions of SQLite -** may add additional static mutexes. Static mutexes are for internal -** use by SQLite only. Applications that use SQLite mutexes should -** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or -** SQLITE_MUTEX_RECURSIVE. -** -** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST -** or SQLITE_MUTEX_RECURSIVE) is used then sqlcipher_sqlite3_mutex_alloc() -** returns a different mutex on every call. But for the static -** mutex types, the same mutex is returned on every call that has -** the same type number. -*/ -static sqlcipher_sqlite3_mutex *pthreadMutexAlloc(int iType){ - static sqlcipher_sqlite3_mutex staticMutexes[] = { - SQLITE3_MUTEX_INITIALIZER(2), - SQLITE3_MUTEX_INITIALIZER(3), - SQLITE3_MUTEX_INITIALIZER(4), - SQLITE3_MUTEX_INITIALIZER(5), - SQLITE3_MUTEX_INITIALIZER(6), - SQLITE3_MUTEX_INITIALIZER(7), - SQLITE3_MUTEX_INITIALIZER(8), - SQLITE3_MUTEX_INITIALIZER(9), - SQLITE3_MUTEX_INITIALIZER(10), - SQLITE3_MUTEX_INITIALIZER(11), - SQLITE3_MUTEX_INITIALIZER(12), - SQLITE3_MUTEX_INITIALIZER(13) - }; - sqlcipher_sqlite3_mutex *p; - switch( iType ){ - case SQLITE_MUTEX_RECURSIVE: { - p = sqlcipher_sqlite3MallocZero( sizeof(*p) ); - if( p ){ -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX - /* If recursive mutexes are not available, we will have to - ** build our own. See below. */ - pthread_mutex_init(&p->mutex, 0); -#else - /* Use a recursive mutex if it is available */ - pthread_mutexattr_t recursiveAttr; - pthread_mutexattr_init(&recursiveAttr); - pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&p->mutex, &recursiveAttr); - pthread_mutexattr_destroy(&recursiveAttr); -#endif -#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) - p->id = SQLITE_MUTEX_RECURSIVE; -#endif - } - break; - } - case SQLITE_MUTEX_FAST: { - p = sqlcipher_sqlite3MallocZero( sizeof(*p) ); - if( p ){ - pthread_mutex_init(&p->mutex, 0); -#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) - p->id = SQLITE_MUTEX_FAST; -#endif - } - break; - } - default: { -#ifdef SQLITE_ENABLE_API_ARMOR - if( iType-2<0 || iType-2>=ArraySize(staticMutexes) ){ - (void)SQLITE_MISUSE_BKPT; - return 0; - } -#endif - p = &staticMutexes[iType-2]; - break; - } - } -#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) - assert( p==0 || p->id==iType ); -#endif - return p; -} +#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ +/* #include */ +/* A running thread */ +struct SQLiteThread { + pthread_t tid; /* Thread ID */ + int done; /* Set to true when thread finishes */ + void *pOut; /* Result returned by the thread */ + void *(*xTask)(void*); /* The thread routine */ + void *pIn; /* Argument to the thread */ +}; -/* -** This routine deallocates a previously -** allocated mutex. SQLite is careful to deallocate every -** mutex that it allocates. -*/ -static void pthreadMutexFree(sqlcipher_sqlite3_mutex *p){ - assert( p->nRef==0 ); -#if SQLITE_ENABLE_API_ARMOR - if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ) -#endif - { - pthread_mutex_destroy(&p->mutex); - sqlcipher_sqlite3_free(p); - } -#ifdef SQLITE_ENABLE_API_ARMOR - else{ - (void)SQLITE_MISUSE_BKPT; - } -#endif -} +/* Create a new thread */ +SQLITE_PRIVATE int sqlcipher_sqlite3ThreadCreate( + SQLiteThread **ppThread, /* OUT: Write the thread object here */ + void *(*xTask)(void*), /* Routine to run in a separate thread */ + void *pIn /* Argument passed into xTask() */ +){ + SQLiteThread *p; + int rc; -/* -** The sqlcipher_sqlite3_mutex_enter() and sqlcipher_sqlite3_mutex_try() routines attempt -** to enter a mutex. If another thread is already within the mutex, -** sqlcipher_sqlite3_mutex_enter() will block and sqlcipher_sqlite3_mutex_try() will return -** SQLITE_BUSY. The sqlcipher_sqlite3_mutex_try() interface returns SQLITE_OK -** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can -** be entered multiple times by the same thread. In such cases the, -** mutex must be exited an equal number of times before another thread -** can enter. If the same thread tries to enter any other kind of mutex -** more than once, the behavior is undefined. -*/ -static void pthreadMutexEnter(sqlcipher_sqlite3_mutex *p){ - assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) ); + assert( ppThread!=0 ); + assert( xTask!=0 ); + /* This routine is never used in single-threaded mode */ + assert( sqlcipher_sqlite3GlobalConfig.bCoreMutex!=0 ); -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX - /* If recursive mutexes are not available, then we have to grow - ** our own. This implementation assumes that pthread_equal() - ** is atomic - that it cannot be deceived into thinking self - ** and p->owner are equal if p->owner changes between two values - ** that are not equal to self while the comparison is taking place. - ** This implementation also assumes a coherent cache - that - ** separate processes cannot read different values from the same - ** address at the same time. If either of these two conditions - ** are not met, then the mutexes will fail and problems will result. - */ - { - pthread_t self = pthread_self(); - if( p->nRef>0 && pthread_equal(p->owner, self) ){ - p->nRef++; - }else{ - pthread_mutex_lock(&p->mutex); - assert( p->nRef==0 ); - p->owner = self; - p->nRef = 1; - } + *ppThread = 0; + p = sqlcipher_sqlite3Malloc(sizeof(*p)); + if( p==0 ) return SQLITE_NOMEM_BKPT; + memset(p, 0, sizeof(*p)); + p->xTask = xTask; + p->pIn = pIn; + /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a + ** function that returns SQLITE_ERROR when passed the argument 200, that + ** forces worker threads to run sequentially and deterministically + ** for testing purposes. */ + if( sqlcipher_sqlite3FaultSim(200) ){ + rc = 1; + }else{ + rc = pthread_create(&p->tid, 0, xTask, pIn); } -#else - /* Use the built-in recursive mutexes if they are available. - */ - pthread_mutex_lock(&p->mutex); -#if SQLITE_MUTEX_NREF - assert( p->nRef>0 || p->owner==0 ); - p->owner = pthread_self(); - p->nRef++; -#endif -#endif - -#ifdef SQLITE_DEBUG - if( p->trace ){ - printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); + if( rc ){ + p->done = 1; + p->pOut = xTask(pIn); } -#endif + *ppThread = p; + return SQLITE_OK; } -static int pthreadMutexTry(sqlcipher_sqlite3_mutex *p){ + +/* Get the results of the thread */ +SQLITE_PRIVATE int sqlcipher_sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ int rc; - assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) ); -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX - /* If recursive mutexes are not available, then we have to grow - ** our own. This implementation assumes that pthread_equal() - ** is atomic - that it cannot be deceived into thinking self - ** and p->owner are equal if p->owner changes between two values - ** that are not equal to self while the comparison is taking place. - ** This implementation also assumes a coherent cache - that - ** separate processes cannot read different values from the same - ** address at the same time. If either of these two conditions - ** are not met, then the mutexes will fail and problems will result. - */ - { - pthread_t self = pthread_self(); - if( p->nRef>0 && pthread_equal(p->owner, self) ){ - p->nRef++; - rc = SQLITE_OK; - }else if( pthread_mutex_trylock(&p->mutex)==0 ){ - assert( p->nRef==0 ); - p->owner = self; - p->nRef = 1; - rc = SQLITE_OK; - }else{ - rc = SQLITE_BUSY; - } - } -#else - /* Use the built-in recursive mutexes if they are available. - */ - if( pthread_mutex_trylock(&p->mutex)==0 ){ -#if SQLITE_MUTEX_NREF - p->owner = pthread_self(); - p->nRef++; -#endif + assert( ppOut!=0 ); + if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT; + if( p->done ){ + *ppOut = p->pOut; rc = SQLITE_OK; }else{ - rc = SQLITE_BUSY; - } -#endif - -#ifdef SQLITE_DEBUG - if( rc==SQLITE_OK && p->trace ){ - printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); + rc = pthread_join(p->tid, ppOut) ? SQLITE_ERROR : SQLITE_OK; } -#endif + sqlcipher_sqlite3_free(p); return rc; } -/* -** The sqlcipher_sqlite3_mutex_leave() routine exits a mutex that was -** previously entered by the same thread. The behavior -** is undefined if the mutex is not currently entered or -** is not currently allocated. SQLite will never do either. -*/ -static void pthreadMutexLeave(sqlcipher_sqlite3_mutex *p){ - assert( pthreadMutexHeld(p) ); -#if SQLITE_MUTEX_NREF - p->nRef--; - if( p->nRef==0 ) p->owner = 0; -#endif - assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); - -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX - if( p->nRef==0 ){ - pthread_mutex_unlock(&p->mutex); - } -#else - pthread_mutex_unlock(&p->mutex); -#endif - -#ifdef SQLITE_DEBUG - if( p->trace ){ - printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); - } -#endif -} +#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */ +/******************************** End Unix Pthreads *************************/ -SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3DefaultMutex(void){ - static const sqlcipher_sqlite3_mutex_methods sMutex = { - pthreadMutexInit, - pthreadMutexEnd, - pthreadMutexAlloc, - pthreadMutexFree, - pthreadMutexEnter, - pthreadMutexTry, - pthreadMutexLeave, -#ifdef SQLITE_DEBUG - pthreadMutexHeld, - pthreadMutexNotheld -#else - 0, - 0 -#endif - }; - return &sMutex; -} +/********************************* Win32 Threads ****************************/ +#if SQLITE_OS_WIN_THREADS -#endif /* SQLITE_MUTEX_PTHREADS */ +#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ +#include -/************** End of mutex_unix.c ******************************************/ -/************** Begin file mutex_w32.c ***************************************/ -/* -** 2007 August 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 file contains the C functions that implement mutexes for Win32. -*/ -/* #include "sqliteInt.h" */ +/* A running thread */ +struct SQLiteThread { + void *tid; /* The thread handle */ + unsigned id; /* The thread identifier */ + void *(*xTask)(void*); /* The routine to run as a thread */ + void *pIn; /* Argument to xTask */ + void *pResult; /* Result of xTask */ +}; -#if SQLITE_OS_WIN -/* -** Include code that is common to all os_*.c files -*/ -/************** Include os_common.h in the middle of mutex_w32.c *************/ -/************** Begin file os_common.h ***************************************/ -/* -** 2004 May 22 -** -** 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 macros and a little bit of code that is common to -** all of the platform-specific files (os_*.c) and is #included into those -** files. -** -** This file should be #included by the os_*.c files only. It is not a -** general purpose header file. -*/ -#ifndef _OS_COMMON_H_ -#define _OS_COMMON_H_ +/* Thread procedure Win32 compatibility shim */ +static unsigned __stdcall sqlcipher_sqlite3ThreadProc( + void *pArg /* IN: Pointer to the SQLiteThread structure */ +){ + SQLiteThread *p = (SQLiteThread *)pArg; -/* -** At least two bugs have slipped in because we changed the MEMORY_DEBUG -** macro to SQLITE_DEBUG and some older makefiles have not yet made the -** switch. The following code should catch this problem at compile-time. -*/ -#ifdef MEMORY_DEBUG -# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." + assert( p!=0 ); +#if 0 + /* + ** This assert appears to trigger spuriously on certain + ** versions of Windows, possibly due to _beginthreadex() + ** and/or CreateThread() not fully setting their thread + ** ID parameter before starting the thread. + */ + assert( p->id==GetCurrentThreadId() ); #endif + assert( p->xTask!=0 ); + p->pResult = p->xTask(p->pIn); -/* -** Macros for performance tracing. Normally turned off. Only works -** on i486 hardware. -*/ -#ifdef SQLITE_PERFORMANCE_TRACE - -/* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of os_common.h ****************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** 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 inline asm code for retrieving "high-performance" -** counters for x86 and x86_64 class CPUs. -*/ -#ifndef SQLITE_HWTIME_H -#define SQLITE_HWTIME_H - -/* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if !defined(__STRICT_ANSI__) && \ - (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) + _endthreadex(0); + return 0; /* NOT REACHED */ +} - #if defined(__GNUC__) +/* Create a new thread */ +SQLITE_PRIVATE int sqlcipher_sqlite3ThreadCreate( + SQLiteThread **ppThread, /* OUT: Write the thread object here */ + void *(*xTask)(void*), /* Routine to run in a separate thread */ + void *pIn /* Argument passed into xTask() */ +){ + SQLiteThread *p; - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; + assert( ppThread!=0 ); + assert( xTask!=0 ); + *ppThread = 0; + p = sqlcipher_sqlite3Malloc(sizeof(*p)); + if( p==0 ) return SQLITE_NOMEM_BKPT; + /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a + ** function that returns SQLITE_ERROR when passed the argument 200, that + ** forces worker threads to run sequentially and deterministically + ** (via the sqlcipher_sqlite3FaultSim() term of the conditional) for testing + ** purposes. */ + if( sqlcipher_sqlite3GlobalConfig.bCoreMutex==0 || sqlcipher_sqlite3FaultSim(200) ){ + memset(p, 0, sizeof(*p)); + }else{ + p->xTask = xTask; + p->pIn = pIn; + p->tid = (void*)_beginthreadex(0, 0, sqlcipher_sqlite3ThreadProc, p, 0, &p->id); + if( p->tid==0 ){ + memset(p, 0, sizeof(*p)); + } } - - #elif defined(_MSC_VER) - - __declspec(naked) __inline sqlite_uint64 __cdecl sqlcipher_sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } + if( p->xTask==0 ){ + p->id = GetCurrentThreadId(); + p->pResult = xTask(pIn); } + *ppThread = p; + return SQLITE_OK; +} - #endif +SQLITE_PRIVATE DWORD sqlcipher_sqlite3Win32Wait(HANDLE hObject); /* os_win.c */ -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) +/* Get the results of the thread */ +SQLITE_PRIVATE int sqlcipher_sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ + DWORD rc; + BOOL bRc; - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; + assert( ppOut!=0 ); + if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT; + if( p->xTask==0 ){ + /* assert( p->id==GetCurrentThreadId() ); */ + rc = WAIT_OBJECT_0; + assert( p->tid==0 ); + }else{ + assert( p->id!=0 && p->id!=GetCurrentThreadId() ); + rc = sqlcipher_sqlite3Win32Wait((HANDLE)p->tid); + assert( rc!=WAIT_IO_COMPLETION ); + bRc = CloseHandle((HANDLE)p->tid); + assert( bRc ); } + if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult; + sqlcipher_sqlite3_free(p); + return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR; +} -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) - - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; - } +#endif /* SQLITE_OS_WIN_THREADS */ +/******************************** End Win32 Threads *************************/ -#else - /* - ** asm() is needed for hardware timing support. Without asm(), - ** disable the sqlcipher_sqlite3Hwtime() routine. - ** - ** sqlcipher_sqlite3Hwtime() is only used for some obscure debugging - ** and analysis configurations, not in any deliverable, so this - ** should not be a great loss. - */ -SQLITE_PRIVATE sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ return ((sqlite_uint64)0); } +/********************************* Single-Threaded **************************/ +#ifndef SQLITE_THREADS_IMPLEMENTED +/* +** This implementation does not actually create a new thread. It does the +** work of the thread in the main thread, when either the thread is created +** or when it is joined +*/ -#endif +/* A running thread */ +struct SQLiteThread { + void *(*xTask)(void*); /* The routine to run as a thread */ + void *pIn; /* Argument to xTask */ + void *pResult; /* Result of xTask */ +}; -#endif /* !defined(SQLITE_HWTIME_H) */ +/* Create a new thread */ +SQLITE_PRIVATE int sqlcipher_sqlite3ThreadCreate( + SQLiteThread **ppThread, /* OUT: Write the thread object here */ + void *(*xTask)(void*), /* Routine to run in a separate thread */ + void *pIn /* Argument passed into xTask() */ +){ + SQLiteThread *p; -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in os_common.h ******************/ + assert( ppThread!=0 ); + assert( xTask!=0 ); + *ppThread = 0; + p = sqlcipher_sqlite3Malloc(sizeof(*p)); + if( p==0 ) return SQLITE_NOMEM_BKPT; + if( (SQLITE_PTR_TO_INT(p)/17)&1 ){ + p->xTask = xTask; + p->pIn = pIn; + }else{ + p->xTask = 0; + p->pResult = xTask(pIn); + } + *ppThread = p; + return SQLITE_OK; +} -static sqlite_uint64 g_start; -static sqlite_uint64 g_elapsed; -#define TIMER_START g_start=sqlcipher_sqlite3Hwtime() -#define TIMER_END g_elapsed=sqlcipher_sqlite3Hwtime()-g_start -#define TIMER_ELAPSED g_elapsed -#else -#define TIMER_START -#define TIMER_END -#define TIMER_ELAPSED ((sqlite_uint64)0) -#endif +/* Get the results of the thread */ +SQLITE_PRIVATE int sqlcipher_sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ -/* -** If we compile with the SQLITE_TEST macro set, then the following block -** of code will give us the ability to simulate a disk I/O error. This -** is used for testing the I/O recovery logic. -*/ -#if defined(SQLITE_TEST) -SQLITE_API extern int sqlcipher_sqlite3_io_error_hit; -SQLITE_API extern int sqlcipher_sqlite3_io_error_hardhit; -SQLITE_API extern int sqlcipher_sqlite3_io_error_pending; -SQLITE_API extern int sqlcipher_sqlite3_io_error_persist; -SQLITE_API extern int sqlcipher_sqlite3_io_error_benign; -SQLITE_API extern int sqlcipher_sqlite3_diskfull_pending; -SQLITE_API extern int sqlcipher_sqlite3_diskfull; -#define SimulateIOErrorBenign(X) sqlcipher_sqlite3_io_error_benign=(X) -#define SimulateIOError(CODE) \ - if( (sqlcipher_sqlite3_io_error_persist && sqlcipher_sqlite3_io_error_hit) \ - || sqlcipher_sqlite3_io_error_pending-- == 1 ) \ - { local_ioerr(); CODE; } -static void local_ioerr(){ - IOTRACE(("IOERR\n")); - sqlcipher_sqlite3_io_error_hit++; - if( !sqlcipher_sqlite3_io_error_benign ) sqlcipher_sqlite3_io_error_hardhit++; -} -#define SimulateDiskfullError(CODE) \ - if( sqlcipher_sqlite3_diskfull_pending ){ \ - if( sqlcipher_sqlite3_diskfull_pending == 1 ){ \ - local_ioerr(); \ - sqlcipher_sqlite3_diskfull = 1; \ - sqlcipher_sqlite3_io_error_hit = 1; \ - CODE; \ - }else{ \ - sqlcipher_sqlite3_diskfull_pending--; \ - } \ - } -#else -#define SimulateIOErrorBenign(X) -#define SimulateIOError(A) -#define SimulateDiskfullError(A) -#endif /* defined(SQLITE_TEST) */ + assert( ppOut!=0 ); + if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT; + if( p->xTask ){ + *ppOut = p->xTask(p->pIn); + }else{ + *ppOut = p->pResult; + } + sqlcipher_sqlite3_free(p); -/* -** When testing, keep a count of the number of open files. -*/ #if defined(SQLITE_TEST) -SQLITE_API extern int sqlcipher_sqlite3_open_file_count; -#define OpenCounter(X) sqlcipher_sqlite3_open_file_count+=(X) -#else -#define OpenCounter(X) -#endif /* defined(SQLITE_TEST) */ + { + void *pTstAlloc = sqlcipher_sqlite3Malloc(10); + if (!pTstAlloc) return SQLITE_NOMEM_BKPT; + sqlcipher_sqlite3_free(pTstAlloc); + } +#endif -#endif /* !defined(_OS_COMMON_H_) */ + return SQLITE_OK; +} -/************** End of os_common.h *******************************************/ -/************** Continuing where we left off in mutex_w32.c ******************/ +#endif /* !defined(SQLITE_THREADS_IMPLEMENTED) */ +/****************************** End Single-Threaded *************************/ +#endif /* SQLITE_MAX_WORKER_THREADS>0 */ +/************** End of threads.c *********************************************/ +/************** Begin file utf.c *********************************************/ /* -** Include the header file for the Windows VFS. -*/ -/************** Include os_win.h in the middle of mutex_w32.c ****************/ -/************** Begin file os_win.h ******************************************/ -/* -** 2013 November 25 +** 2004 April 13 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -32106,464 +32852,534 @@ SQLITE_API extern int sqlcipher_sqlite3_open_file_count; ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** -****************************************************************************** +************************************************************************* +** This file contains routines used to translate between UTF-8, +** UTF-16, UTF-16BE, and UTF-16LE. ** -** This file contains code that is specific to Windows. -*/ -#ifndef SQLITE_OS_WIN_H -#define SQLITE_OS_WIN_H - -/* -** Include the primary Windows SDK header file. -*/ -/* #include "windows.h" */ - -#ifdef __CYGWIN__ -# include -/* # include ** amalgamator: dontcache ** */ -#endif - -/* -** Determine if we are dealing with Windows NT. +** Notes on UTF-8: ** -** We ought to be able to determine if we are compiling for Windows 9x or -** Windows NT using the _WIN32_WINNT macro as follows: +** Byte-0 Byte-1 Byte-2 Byte-3 Value +** 0xxxxxxx 00000000 00000000 0xxxxxxx +** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx +** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx +** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx ** -** #if defined(_WIN32_WINNT) -** # define SQLITE_OS_WINNT 1 -** #else -** # define SQLITE_OS_WINNT 0 -** #endif ** -** However, Visual Studio 2005 does not set _WIN32_WINNT by default, as -** it ought to, so the above test does not work. We'll just assume that -** everything is Windows NT unless the programmer explicitly says otherwise -** by setting SQLITE_OS_WINNT to 0. -*/ -#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT) -# define SQLITE_OS_WINNT 1 -#endif - -/* -** Determine if we are dealing with Windows CE - which has a much reduced -** API. -*/ -#if defined(_WIN32_WCE) -# define SQLITE_OS_WINCE 1 -#else -# define SQLITE_OS_WINCE 0 -#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 - -/* -** For WinCE, some API function parameters do not appear to be declared as -** volatile. -*/ -#if SQLITE_OS_WINCE -# define SQLITE_WIN32_VOLATILE -#else -# define SQLITE_WIN32_VOLATILE volatile -#endif - -/* -** For some Windows sub-platforms, the _beginthreadex() / _endthreadex() -** functions are not available (e.g. those not using MSVC, Cygwin, etc). +** Notes on UTF-16: (with wwww+1==uuuuu) +** +** Word-0 Word-1 Value +** 110110ww wwzzzzyy 110111yy yyxxxxxx 000uuuuu zzzzyyyy yyxxxxxx +** zzzzyyyy yyxxxxxx 00000000 zzzzyyyy yyxxxxxx +** +** +** BOM or Byte Order Mark: +** 0xff 0xfe little-endian utf-16 follows +** 0xfe 0xff big-endian utf-16 follows +** */ -#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ - SQLITE_THREADSAFE>0 && !defined(__CYGWIN__) -# define SQLITE_OS_WIN_THREADS 1 -#else -# define SQLITE_OS_WIN_THREADS 0 -#endif - -#endif /* SQLITE_OS_WIN_H */ - -/************** End of os_win.h **********************************************/ -/************** Continuing where we left off in mutex_w32.c ******************/ -#endif +/* #include "sqliteInt.h" */ +/* #include */ +/* #include "vdbeInt.h" */ +#if !defined(SQLITE_AMALGAMATION) && SQLITE_BYTEORDER==0 /* -** The code in this file is only used if we are compiling multithreaded -** on a Win32 system. +** The following constant value is used by the SQLITE_BIGENDIAN and +** SQLITE_LITTLEENDIAN macros. */ -#ifdef SQLITE_MUTEX_W32 +SQLITE_PRIVATE const int sqlcipher_sqlite3one = 1; +#endif /* SQLITE_AMALGAMATION && SQLITE_BYTEORDER==0 */ /* -** Each recursive mutex is an instance of the following structure. +** This lookup table is used to help decode the first byte of +** a multi-byte UTF8 character. */ -struct sqlcipher_sqlite3_mutex { - CRITICAL_SECTION mutex; /* Mutex controlling the lock */ - int id; /* Mutex type */ -#ifdef SQLITE_DEBUG - volatile int nRef; /* Number of enterances */ - volatile DWORD owner; /* Thread holding this mutex */ - volatile LONG trace; /* True to trace changes */ -#endif +static const unsigned char sqlcipher_sqlite3Utf8Trans1[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, }; -/* -** These are the initializer values used when declaring a "static" mutex -** on Win32. It should be noted that all mutexes require initialization -** on the Win32 platform. -*/ -#define SQLITE_W32_MUTEX_INITIALIZER { 0 } - -#ifdef SQLITE_DEBUG -#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id, \ - 0L, (DWORD)0, 0 } -#else -#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id } -#endif -#ifdef SQLITE_DEBUG -/* -** The sqlcipher_sqlite3_mutex_held() and sqlcipher_sqlite3_mutex_notheld() routine are -** intended for use only inside assert() statements. -*/ -static int winMutexHeld(sqlcipher_sqlite3_mutex *p){ - return p->nRef!=0 && p->owner==GetCurrentThreadId(); +#define WRITE_UTF8(zOut, c) { \ + if( c<0x00080 ){ \ + *zOut++ = (u8)(c&0xFF); \ + } \ + else if( c<0x00800 ){ \ + *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); \ + *zOut++ = 0x80 + (u8)(c & 0x3F); \ + } \ + else if( c<0x10000 ){ \ + *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); \ + *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ + *zOut++ = 0x80 + (u8)(c & 0x3F); \ + }else{ \ + *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); \ + *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); \ + *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ + *zOut++ = 0x80 + (u8)(c & 0x3F); \ + } \ } -static int winMutexNotheld2(sqlcipher_sqlite3_mutex *p, DWORD tid){ - return p->nRef==0 || p->owner!=tid; +#define WRITE_UTF16LE(zOut, c) { \ + if( c<=0xFFFF ){ \ + *zOut++ = (u8)(c&0x00FF); \ + *zOut++ = (u8)((c>>8)&0x00FF); \ + }else{ \ + *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ + *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \ + *zOut++ = (u8)(c&0x00FF); \ + *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ + } \ } -static int winMutexNotheld(sqlcipher_sqlite3_mutex *p){ - DWORD tid = GetCurrentThreadId(); - return winMutexNotheld2(p, tid); +#define WRITE_UTF16BE(zOut, c) { \ + if( c<=0xFFFF ){ \ + *zOut++ = (u8)((c>>8)&0x00FF); \ + *zOut++ = (u8)(c&0x00FF); \ + }else{ \ + *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \ + *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ + *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ + *zOut++ = (u8)(c&0x00FF); \ + } \ } -#endif /* -** Try to provide a memory barrier operation, needed for initialization -** and also for the xShmBarrier method of the VFS in cases when SQLite is -** compiled without mutexes (SQLITE_THREADSAFE=0). +** Translate a single UTF-8 character. Return the unicode value. +** +** During translation, assume that the byte that zTerm points +** is a 0x00. +** +** Write a pointer to the next unread byte back into *pzNext. +** +** Notes On Invalid UTF-8: +** +** * This routine never allows a 7-bit character (0x00 through 0x7f) to +** be encoded as a multi-byte character. Any multi-byte character that +** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd. +** +** * This routine never allows a UTF16 surrogate value to be encoded. +** If a multi-byte character attempts to encode a value between +** 0xd800 and 0xe000 then it is rendered as 0xfffd. +** +** * Bytes in the range of 0x80 through 0xbf which occur as the first +** byte of a character are interpreted as single-byte characters +** and rendered as themselves even though they are technically +** invalid characters. +** +** * This routine accepts over-length UTF8 encodings +** for unicode values 0x80 and greater. It does not change over-length +** encodings to 0xfffd as some systems recommend. */ -SQLITE_PRIVATE void sqlcipher_sqlite3MemoryBarrier(void){ -#if defined(SQLITE_MEMORY_BARRIER) - SQLITE_MEMORY_BARRIER; -#elif defined(__GNUC__) - __sync_synchronize(); -#elif MSVC_VERSION>=1300 - _ReadWriteBarrier(); -#elif defined(MemoryBarrier) - MemoryBarrier(); -#endif +#define READ_UTF8(zIn, zTerm, c) \ + c = *(zIn++); \ + if( c>=0xc0 ){ \ + c = sqlcipher_sqlite3Utf8Trans1[c-0xc0]; \ + while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ + c = (c<<6) + (0x3f & *(zIn++)); \ + } \ + if( c<0x80 \ + || (c&0xFFFFF800)==0xD800 \ + || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ + } +SQLITE_PRIVATE u32 sqlcipher_sqlite3Utf8Read( + const unsigned char **pz /* Pointer to string from which to read char */ +){ + unsigned int c; + + /* Same as READ_UTF8() above but without the zTerm parameter. + ** For this routine, we assume the UTF8 string is always zero-terminated. + */ + c = *((*pz)++); + if( c>=0xc0 ){ + c = sqlcipher_sqlite3Utf8Trans1[c-0xc0]; + while( (*(*pz) & 0xc0)==0x80 ){ + c = (c<<6) + (0x3f & *((*pz)++)); + } + if( c<0x80 + || (c&0xFFFFF800)==0xD800 + || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } + } + return c; } + + + /* -** Initialize and deinitialize the mutex subsystem. +** If the TRANSLATE_TRACE macro is defined, the value of each Mem is +** printed on stderr on the way into and out of sqlcipher_sqlite3VdbeMemTranslate(). */ -static sqlcipher_sqlite3_mutex winMutex_staticMutexes[] = { - SQLITE3_MUTEX_INITIALIZER(2), - SQLITE3_MUTEX_INITIALIZER(3), - SQLITE3_MUTEX_INITIALIZER(4), - SQLITE3_MUTEX_INITIALIZER(5), - SQLITE3_MUTEX_INITIALIZER(6), - SQLITE3_MUTEX_INITIALIZER(7), - SQLITE3_MUTEX_INITIALIZER(8), - SQLITE3_MUTEX_INITIALIZER(9), - SQLITE3_MUTEX_INITIALIZER(10), - SQLITE3_MUTEX_INITIALIZER(11), - SQLITE3_MUTEX_INITIALIZER(12), - SQLITE3_MUTEX_INITIALIZER(13) -}; - -static int winMutex_isInit = 0; -static int winMutex_isNt = -1; /* <0 means "need to query" */ +/* #define TRANSLATE_TRACE 1 */ -/* As the winMutexInit() and winMutexEnd() functions are called as part -** of the sqlcipher_sqlite3_initialize() and sqlcipher_sqlite3_shutdown() processing, the -** "interlocked" magic used here is probably not strictly necessary. +#ifndef SQLITE_OMIT_UTF16 +/* +** This routine transforms the internal text encoding used by pMem to +** desiredEnc. It is an error if the string is already of the desired +** encoding, or if *pMem does not contain a string value. */ -static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0; +SQLITE_PRIVATE SQLITE_NOINLINE int sqlcipher_sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ + sqlcipher_sqlite3_int64 len; /* Maximum length of output string in bytes */ + unsigned char *zOut; /* Output buffer */ + unsigned char *zIn; /* Input iterator */ + unsigned char *zTerm; /* End of input */ + unsigned char *z; /* Output iterator */ + unsigned int c; -SQLITE_API int sqlcipher_sqlite3_win32_is_nt(void); /* os_win.c */ -SQLITE_API void sqlcipher_sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */ + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + assert( pMem->flags&MEM_Str ); + assert( pMem->enc!=desiredEnc ); + assert( pMem->enc!=0 ); + assert( pMem->n>=0 ); -static int winMutexInit(void){ - /* The first to increment to 1 does actual initialization */ - if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){ - int i; - for(i=0; ienc!=SQLITE_UTF8 && desiredEnc!=SQLITE_UTF8 ){ + u8 temp; + int rc; + rc = sqlcipher_sqlite3VdbeMemMakeWriteable(pMem); + if( rc!=SQLITE_OK ){ + assert( rc==SQLITE_NOMEM ); + return SQLITE_NOMEM_BKPT; } - winMutex_isInit = 1; - }else{ - /* Another thread is (in the process of) initializing the static - ** mutexes */ - while( !winMutex_isInit ){ - sqlcipher_sqlite3_win32_sleep(1); + zIn = (u8*)pMem->z; + zTerm = &zIn[pMem->n&~1]; + while( zInenc = desiredEnc; + goto translate_out; } - return SQLITE_OK; -} -static int winMutexEnd(void){ - /* The first to decrement to 0 does actual shutdown - ** (which should be the last to shutdown.) */ - if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){ - if( winMutex_isInit==1 ){ - int i; - for(i=0; in &= ~1; + len = 2 * (sqlcipher_sqlite3_int64)pMem->n + 1; + }else{ + /* When converting from UTF-8 to UTF-16 the maximum growth is caused + ** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16 + ** character. Two bytes are required in the output buffer for the + ** nul-terminator. + */ + len = 2 * (sqlcipher_sqlite3_int64)pMem->n + 2; + } + + /* Set zIn to point at the start of the input buffer and zTerm to point 1 + ** byte past the end. + ** + ** Variable zOut is set to point at the output buffer, space obtained + ** from sqlcipher_sqlite3_malloc(). + */ + zIn = (u8*)pMem->z; + zTerm = &zIn[pMem->n]; + zOut = sqlcipher_sqlite3DbMallocRaw(pMem->db, len); + if( !zOut ){ + return SQLITE_NOMEM_BKPT; + } + z = zOut; + + if( pMem->enc==SQLITE_UTF8 ){ + if( desiredEnc==SQLITE_UTF16LE ){ + /* UTF-8 -> UTF-16 Little-endian */ + while( zIn UTF-16 Big-endian */ + while( zInn = (int)(z - zOut); + *z++ = 0; + }else{ + assert( desiredEnc==SQLITE_UTF8 ); + if( pMem->enc==SQLITE_UTF16LE ){ + /* UTF-16 Little-endian -> UTF-8 */ + while( zIn=0xd800 && c<0xe000 ){ +#ifdef SQLITE_REPLACE_INVALID_UTF + if( c>=0xdc00 || zIn>=zTerm ){ + c = 0xfffd; + }else{ + int c2 = *(zIn++); + c2 += (*(zIn++))<<8; + if( c2<0xdc00 || c2>=0xe000 ){ + zIn -= 2; + c = 0xfffd; + }else{ + c = ((c&0x3ff)<<10) + (c2&0x3ff) + 0x10000; + } + } +#else + if( zIn UTF-8 */ + while( zIn=0xd800 && c<0xe000 ){ +#ifdef SQLITE_REPLACE_INVALID_UTF + if( c>=0xdc00 || zIn>=zTerm ){ + c = 0xfffd; + }else{ + int c2 = (*(zIn++))<<8; + c2 += *(zIn++); + if( c2<0xdc00 || c2>=0xe000 ){ + zIn -= 2; + c = 0xfffd; + }else{ + c = ((c&0x3ff)<<10) + (c2&0x3ff) + 0x10000; + } + } +#else + if( zInn = (int)(z - zOut); + } + *z = 0; + assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len ); + + c = MEM_Str|MEM_Term|(pMem->flags&(MEM_AffMask|MEM_Subtype)); + sqlcipher_sqlite3VdbeMemRelease(pMem); + pMem->flags = c; + pMem->enc = desiredEnc; + pMem->z = (char*)zOut; + pMem->zMalloc = pMem->z; + pMem->szMalloc = sqlcipher_sqlite3DbMallocSize(pMem->db, pMem->z); + +translate_out: +#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG) + { + StrAccum acc; + char zBuf[1000]; + sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); + sqlcipher_sqlite3VdbeMemPrettyPrint(pMem, &acc); + fprintf(stderr, "OUTPUT: %s\n", sqlcipher_sqlite3StrAccumFinish(&acc)); } +#endif return SQLITE_OK; } +#endif /* SQLITE_OMIT_UTF16 */ +#ifndef SQLITE_OMIT_UTF16 /* -** The sqlcipher_sqlite3_mutex_alloc() routine allocates a new -** mutex and returns a pointer to it. If it returns NULL -** that means that a mutex could not be allocated. SQLite -** will unwind its stack and return an error. The argument -** to sqlcipher_sqlite3_mutex_alloc() is one of these integer constants: -** -**
      -**
    • SQLITE_MUTEX_FAST -**
    • SQLITE_MUTEX_RECURSIVE -**
    • SQLITE_MUTEX_STATIC_MAIN -**
    • SQLITE_MUTEX_STATIC_MEM -**
    • SQLITE_MUTEX_STATIC_OPEN -**
    • SQLITE_MUTEX_STATIC_PRNG -**
    • SQLITE_MUTEX_STATIC_LRU -**
    • SQLITE_MUTEX_STATIC_PMEM -**
    • SQLITE_MUTEX_STATIC_APP1 -**
    • SQLITE_MUTEX_STATIC_APP2 -**
    • SQLITE_MUTEX_STATIC_APP3 -**
    • SQLITE_MUTEX_STATIC_VFS1 -**
    • SQLITE_MUTEX_STATIC_VFS2 -**
    • SQLITE_MUTEX_STATIC_VFS3 -**
    -** -** The first two constants cause sqlcipher_sqlite3_mutex_alloc() to create -** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE -** is used but not necessarily so when SQLITE_MUTEX_FAST is used. -** The mutex implementation does not need to make a distinction -** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does -** not want to. But SQLite will only request a recursive mutex in -** cases where it really needs one. If a faster non-recursive mutex -** implementation is available on the host platform, the mutex subsystem -** might return such a mutex in response to SQLITE_MUTEX_FAST. -** -** The other allowed parameters to sqlcipher_sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. Six static mutexes are -** used by the current version of SQLite. Future versions of SQLite -** may add additional static mutexes. Static mutexes are for internal -** use by SQLite only. Applications that use SQLite mutexes should -** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or -** SQLITE_MUTEX_RECURSIVE. +** This routine checks for a byte-order mark at the beginning of the +** UTF-16 string stored in *pMem. If one is present, it is removed and +** the encoding of the Mem adjusted. This routine does not do any +** byte-swapping, it just sets Mem.enc appropriately. ** -** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST -** or SQLITE_MUTEX_RECURSIVE) is used then sqlcipher_sqlite3_mutex_alloc() -** returns a different mutex on every call. But for the static -** mutex types, the same mutex is returned on every call that has -** the same type number. +** The allocation (static, dynamic etc.) and encoding of the Mem may be +** changed by this function. */ -static sqlcipher_sqlite3_mutex *winMutexAlloc(int iType){ - sqlcipher_sqlite3_mutex *p; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemHandleBom(Mem *pMem){ + int rc = SQLITE_OK; + u8 bom = 0; - switch( iType ){ - case SQLITE_MUTEX_FAST: - case SQLITE_MUTEX_RECURSIVE: { - p = sqlcipher_sqlite3MallocZero( sizeof(*p) ); - if( p ){ - p->id = iType; -#ifdef SQLITE_DEBUG -#ifdef SQLITE_WIN32_MUTEX_TRACE_DYNAMIC - p->trace = 1; -#endif -#endif -#if SQLITE_OS_WINRT - InitializeCriticalSectionEx(&p->mutex, 0, 0); -#else - InitializeCriticalSection(&p->mutex); -#endif - } - break; + assert( pMem->n>=0 ); + if( pMem->n>1 ){ + u8 b1 = *(u8 *)pMem->z; + u8 b2 = *(((u8 *)pMem->z) + 1); + if( b1==0xFE && b2==0xFF ){ + bom = SQLITE_UTF16BE; } - default: { -#ifdef SQLITE_ENABLE_API_ARMOR - if( iType-2<0 || iType-2>=ArraySize(winMutex_staticMutexes) ){ - (void)SQLITE_MISUSE_BKPT; - return 0; - } -#endif - p = &winMutex_staticMutexes[iType-2]; -#ifdef SQLITE_DEBUG -#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC - InterlockedCompareExchange(&p->trace, 1, 0); -#endif -#endif - break; + if( b1==0xFF && b2==0xFE ){ + bom = SQLITE_UTF16LE; } } - assert( p==0 || p->id==iType ); - return p; -} + if( bom ){ + rc = sqlcipher_sqlite3VdbeMemMakeWriteable(pMem); + if( rc==SQLITE_OK ){ + pMem->n -= 2; + memmove(pMem->z, &pMem->z[2], pMem->n); + pMem->z[pMem->n] = '\0'; + pMem->z[pMem->n+1] = '\0'; + pMem->flags |= MEM_Term; + pMem->enc = bom; + } + } + return rc; +} +#endif /* SQLITE_OMIT_UTF16 */ /* -** This routine deallocates a previously -** allocated mutex. SQLite is careful to deallocate every -** mutex that it allocates. +** pZ is a UTF-8 encoded unicode string. If nByte is less than zero, +** return the number of unicode characters in pZ up to (but not including) +** the first 0x00 byte. If nByte is not less than zero, return the +** number of unicode characters in the first nByte of pZ (or up to +** the first 0x00, whichever comes first). */ -static void winMutexFree(sqlcipher_sqlite3_mutex *p){ - assert( p ); - assert( p->nRef==0 && p->owner==0 ); - if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ){ - DeleteCriticalSection(&p->mutex); - sqlcipher_sqlite3_free(p); +SQLITE_PRIVATE int sqlcipher_sqlite3Utf8CharLen(const char *zIn, int nByte){ + int r = 0; + const u8 *z = (const u8*)zIn; + const u8 *zTerm; + if( nByte>=0 ){ + zTerm = &z[nByte]; }else{ -#ifdef SQLITE_ENABLE_API_ARMOR - (void)SQLITE_MISUSE_BKPT; -#endif + zTerm = (const u8*)(-1); + } + assert( z<=zTerm ); + while( *z!=0 && zid==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); -#else - assert( p ); -#endif - assert( winMutex_isInit==1 ); - EnterCriticalSection(&p->mutex); -#ifdef SQLITE_DEBUG - assert( p->nRef>0 || p->owner==0 ); - p->owner = tid; - p->nRef++; - if( p->trace ){ - OSTRACE(("ENTER-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n", - tid, p->id, p, p->trace, p->nRef)); - } -#endif -} +SQLITE_PRIVATE int sqlcipher_sqlite3Utf8To8(unsigned char *zIn){ + unsigned char *zOut = zIn; + unsigned char *zStart = zIn; + u32 c; -static int winMutexTry(sqlcipher_sqlite3_mutex *p){ -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) - DWORD tid = GetCurrentThreadId(); -#endif - int rc = SQLITE_BUSY; - assert( p ); - assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); - /* - ** The sqlcipher_sqlite3_mutex_try() routine is very rarely used, and when it - ** is used it is merely an optimization. So it is OK for it to always - ** fail. - ** - ** The TryEnterCriticalSection() interface is only available on WinNT. - ** And some windows compilers complain if you try to use it without - ** first doing some #defines that prevent SQLite from building on Win98. - ** For that reason, we will omit this optimization for now. See - ** ticket #2685. - */ -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400 - assert( winMutex_isInit==1 ); - assert( winMutex_isNt>=-1 && winMutex_isNt<=1 ); - if( winMutex_isNt<0 ){ - winMutex_isNt = sqlcipher_sqlite3_win32_is_nt(); - } - assert( winMutex_isNt==0 || winMutex_isNt==1 ); - if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){ -#ifdef SQLITE_DEBUG - p->owner = tid; - p->nRef++; -#endif - rc = SQLITE_OK; - } -#else - UNUSED_PARAMETER(p); -#endif -#ifdef SQLITE_DEBUG - if( p->trace ){ - OSTRACE(("TRY-MUTEX tid=%lu, mutex(%d)=%p (%d), owner=%lu, nRef=%d, rc=%s\n", - tid, p->id, p, p->trace, p->owner, p->nRef, sqlcipher_sqlite3ErrName(rc))); + while( zIn[0] && zOut<=zIn ){ + c = sqlcipher_sqlite3Utf8Read((const u8**)&zIn); + if( c!=0xfffd ){ + WRITE_UTF8(zOut, c); + } } -#endif - return rc; + *zOut = 0; + return (int)(zOut - zStart); } +#endif +#ifndef SQLITE_OMIT_UTF16 /* -** The sqlcipher_sqlite3_mutex_leave() routine exits a mutex that was -** previously entered by the same thread. The behavior -** is undefined if the mutex is not currently entered or -** is not currently allocated. SQLite will never do either. +** Convert a UTF-16 string in the native encoding into a UTF-8 string. +** Memory to hold the UTF-8 string is obtained from sqlcipher_sqlite3_malloc and must +** be freed by the calling function. +** +** NULL is returned if there is an allocation error. */ -static void winMutexLeave(sqlcipher_sqlite3_mutex *p){ -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) - DWORD tid = GetCurrentThreadId(); -#endif - assert( p ); -#ifdef SQLITE_DEBUG - assert( p->nRef>0 ); - assert( p->owner==tid ); - p->nRef--; - if( p->nRef==0 ) p->owner = 0; - assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); -#endif - assert( winMutex_isInit==1 ); - LeaveCriticalSection(&p->mutex); -#ifdef SQLITE_DEBUG - if( p->trace ){ - OSTRACE(("LEAVE-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n", - tid, p->id, p, p->trace, p->nRef)); +SQLITE_PRIVATE char *sqlcipher_sqlite3Utf16to8(sqlcipher_sqlite3 *db, const void *z, int nByte, u8 enc){ + Mem m; + memset(&m, 0, sizeof(m)); + m.db = db; + sqlcipher_sqlite3VdbeMemSetStr(&m, z, nByte, enc, SQLITE_STATIC); + sqlcipher_sqlite3VdbeChangeEncoding(&m, SQLITE_UTF8); + if( db->mallocFailed ){ + sqlcipher_sqlite3VdbeMemRelease(&m); + m.z = 0; } -#endif + assert( (m.flags & MEM_Term)!=0 || db->mallocFailed ); + assert( (m.flags & MEM_Str)!=0 || db->mallocFailed ); + assert( m.z || db->mallocFailed ); + return m.z; } -SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3DefaultMutex(void){ - static const sqlcipher_sqlite3_mutex_methods sMutex = { - winMutexInit, - winMutexEnd, - winMutexAlloc, - winMutexFree, - winMutexEnter, - winMutexTry, - winMutexLeave, -#ifdef SQLITE_DEBUG - winMutexHeld, - winMutexNotheld -#else - 0, - 0 -#endif - }; - return &sMutex; +/* +** zIn is a UTF-16 encoded unicode string at least nChar characters long. +** Return the number of bytes in the first nChar unicode characters +** in pZ. nChar must be non-negative. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3Utf16ByteLen(const void *zIn, int nChar){ + int c; + unsigned char const *z = zIn; + int n = 0; + + if( SQLITE_UTF16NATIVE==SQLITE_UTF16LE ) z++; + while( n=0xd8 && c<0xdc && z[0]>=0xdc && z[0]<0xe0 ) z += 2; + n++; + } + return (int)(z-(unsigned char const *)zIn) + - (SQLITE_UTF16NATIVE==SQLITE_UTF16LE); } -#endif /* SQLITE_MUTEX_W32 */ +#if defined(SQLITE_TEST) +/* +** This routine is called from the TCL test function "translate_selftest". +** It checks that the primitives for serializing and deserializing +** characters in each encoding are inverses of each other. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3UtfSelfTest(void){ + unsigned int i, t; + unsigned char zBuf[20]; + unsigned char *z; + int n; + unsigned int c; -/************** End of mutex_w32.c *******************************************/ -/************** Begin file malloc.c ******************************************/ + for(i=0; i<0x00110000; i++){ + z = zBuf; + WRITE_UTF8(z, i); + n = (int)(z-zBuf); + assert( n>0 && n<=4 ); + z[0] = 0; + z = zBuf; + c = sqlcipher_sqlite3Utf8Read((const u8**)&z); + t = i; + if( i>=0xD800 && i<=0xDFFF ) t = 0xFFFD; + if( (i&0xFFFFFFFE)==0xFFFE ) t = 0xFFFD; + assert( c==t ); + assert( (z-zBuf)==n ); + } +} +#endif /* SQLITE_TEST */ +#endif /* SQLITE_OMIT_UTF16 */ + +/************** End of utf.c *************************************************/ +/************** Begin file util.c ********************************************/ /* ** 2001 September 15 ** @@ -32575,8846 +33391,10015 @@ SQLITE_PRIVATE sqlcipher_sqlite3_mutex_methods const *sqlcipher_sqlite3DefaultMu ** May you share freely, never taking more than you give. ** ************************************************************************* +** Utility functions used throughout sqlite. +** +** This file contains functions for allocating memory, comparing +** strings, and stuff like that. ** -** Memory allocation functions used throughout sqlite. */ /* #include "sqliteInt.h" */ /* #include */ +#ifndef SQLITE_OMIT_FLOATING_POINT +#include +#endif /* -** Attempt to release up to n bytes of non-essential memory currently -** held by SQLite. An example of non-essential memory is memory used to -** cache database pages that are not currently in use. +** Calls to sqlcipher_sqlite3FaultSim() are used to simulate a failure during testing, +** or to bypass normal error detection during testing in order to let +** execute proceed futher downstream. +** +** In deployment, sqlcipher_sqlite3FaultSim() *always* return SQLITE_OK (0). The +** sqlcipher_sqlite3FaultSim() function only returns non-zero during testing. +** +** During testing, if the test harness has set a fault-sim callback using +** a call to sqlcipher_sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL), then +** each call to sqlcipher_sqlite3FaultSim() is relayed to that application-supplied +** callback and the integer return value form the application-supplied +** callback is returned by sqlcipher_sqlite3FaultSim(). +** +** The integer argument to sqlcipher_sqlite3FaultSim() is a code to identify which +** sqlcipher_sqlite3FaultSim() instance is being invoked. Each call to sqlcipher_sqlite3FaultSim() +** should have a unique code. To prevent legacy testing applications from +** breaking, the codes should not be changed or reused. */ -SQLITE_API int sqlcipher_sqlite3_release_memory(int n){ -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT - return sqlcipher_sqlite3PcacheReleaseMemory(n); -#else - /* IMPLEMENTATION-OF: R-34391-24921 The sqlcipher_sqlite3_release_memory() routine - ** is a no-op returning zero if SQLite is not compiled with - ** SQLITE_ENABLE_MEMORY_MANAGEMENT. */ - UNUSED_PARAMETER(n); - return 0; +#ifndef SQLITE_UNTESTABLE +SQLITE_PRIVATE int sqlcipher_sqlite3FaultSim(int iTest){ + int (*xCallback)(int) = sqlcipher_sqlite3GlobalConfig.xTestCallback; + return xCallback ? xCallback(iTest) : SQLITE_OK; +} #endif + +#ifndef SQLITE_OMIT_FLOATING_POINT +/* +** Return true if the floating point value is Not a Number (NaN). +** +** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN. +** Otherwise, we have our own implementation that works on most systems. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3IsNaN(double x){ + int rc; /* The value return */ +#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN + u64 y; + memcpy(&y,&x,sizeof(y)); + rc = IsNaN(y); +#else + rc = isnan(x); +#endif /* HAVE_ISNAN */ + testcase( rc ); + return rc; } +#endif /* SQLITE_OMIT_FLOATING_POINT */ /* -** Default value of the hard heap limit. 0 means "no limit". +** Compute a string length that is limited to what can be stored in +** lower 30 bits of a 32-bit signed integer. +** +** The value returned will never be negative. Nor will it ever be greater +** than the actual length of the string. For very long strings (greater +** than 1GiB) the value returned might be less than the true string length. */ -#ifndef SQLITE_MAX_MEMORY -# define SQLITE_MAX_MEMORY 0 -#endif +SQLITE_PRIVATE int sqlcipher_sqlite3Strlen30(const char *z){ + if( z==0 ) return 0; + return 0x3fffffff & (int)strlen(z); +} /* -** State information local to the memory allocation subsystem. +** Return the declared type of a column. Or return zDflt if the column +** has no declared type. +** +** The column type is an extra string stored after the zero-terminator on +** the column name if and only if the COLFLAG_HASTYPE flag is set. */ -static SQLITE_WSD struct Mem0Global { - sqlcipher_sqlite3_mutex *mutex; /* Mutex to serialize access */ - sqlcipher_sqlite3_int64 alarmThreshold; /* The soft heap limit */ - sqlcipher_sqlite3_int64 hardLimit; /* The hard upper bound on memory */ +SQLITE_PRIVATE char *sqlcipher_sqlite3ColumnType(Column *pCol, char *zDflt){ + if( pCol->colFlags & COLFLAG_HASTYPE ){ + return pCol->zCnName + strlen(pCol->zCnName) + 1; + }else if( pCol->eCType ){ + assert( pCol->eCType<=SQLITE_N_STDTYPE ); + return (char*)sqlcipher_sqlite3StdType[pCol->eCType-1]; + }else{ + return zDflt; + } +} - /* - ** True if heap is nearly "full" where "full" is defined by the - ** sqlcipher_sqlite3_soft_heap_limit() setting. - */ - int nearlyFull; -} mem0 = { 0, SQLITE_MAX_MEMORY, SQLITE_MAX_MEMORY, 0 }; +/* +** Helper function for sqlcipher_sqlite3Error() - called rarely. Broken out into +** a separate routine to avoid unnecessary register saves on entry to +** sqlcipher_sqlite3Error(). +*/ +static SQLITE_NOINLINE void sqlcipher_sqlite3ErrorFinish(sqlcipher_sqlite3 *db, int err_code){ + if( db->pErr ) sqlcipher_sqlite3ValueSetNull(db->pErr); + sqlcipher_sqlite3SystemError(db, err_code); +} -#define mem0 GLOBAL(struct Mem0Global, mem0) +/* +** Set the current error code to err_code and clear any prior error message. +** Also set iSysErrno (by calling sqlcipher_sqlite3System) if the err_code indicates +** that would be appropriate. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3Error(sqlcipher_sqlite3 *db, int err_code){ + assert( db!=0 ); + db->errCode = err_code; + if( err_code || db->pErr ){ + sqlcipher_sqlite3ErrorFinish(db, err_code); + }else{ + db->errByteOffset = -1; + } +} /* -** Return the memory allocator mutex. sqlcipher_sqlite3_status() needs it. +** The equivalent of sqlcipher_sqlite3Error(db, SQLITE_OK). Clear the error state +** and error message. */ -SQLITE_PRIVATE sqlcipher_sqlite3_mutex *sqlcipher_sqlite3MallocMutex(void){ - return mem0.mutex; +SQLITE_PRIVATE void sqlcipher_sqlite3ErrorClear(sqlcipher_sqlite3 *db){ + assert( db!=0 ); + db->errCode = SQLITE_OK; + db->errByteOffset = -1; + if( db->pErr ) sqlcipher_sqlite3ValueSetNull(db->pErr); } -#ifndef SQLITE_OMIT_DEPRECATED /* -** Deprecated external interface. It used to set an alarm callback -** that was invoked when memory usage grew too large. Now it is a -** no-op. +** Load the sqlcipher_sqlite3.iSysErrno field if that is an appropriate thing +** to do based on the SQLite error code in rc. */ -SQLITE_API int sqlcipher_sqlite3_memory_alarm( - void(*xCallback)(void *pArg, sqlcipher_sqlite3_int64 used,int N), - void *pArg, - sqlcipher_sqlite3_int64 iThreshold -){ - (void)xCallback; - (void)pArg; - (void)iThreshold; - return SQLITE_OK; +SQLITE_PRIVATE void sqlcipher_sqlite3SystemError(sqlcipher_sqlite3 *db, int rc){ + if( rc==SQLITE_IOERR_NOMEM ) return; + rc &= 0xff; + if( rc==SQLITE_CANTOPEN || rc==SQLITE_IOERR ){ + db->iSysErrno = sqlcipher_sqlite3OsGetLastError(db->pVfs); + } } -#endif /* -** Set the soft heap-size limit for the library. An argument of -** zero disables the limit. A negative argument is a no-op used to -** obtain the return value. +** Set the most recent error code and error string for the sqlite +** handle "db". The error code is set to "err_code". ** -** The return value is the value of the heap limit just before this -** interface was called. +** If it is not NULL, string zFormat specifies the format of the +** error string. zFormat and any string tokens that follow it are +** assumed to be encoded in UTF-8. ** -** If the hard heap limit is enabled, then the soft heap limit cannot -** be disabled nor raised above the hard heap limit. +** To clear the most recent error for sqlite handle "db", sqlcipher_sqlite3Error +** should be called with err_code set to SQLITE_OK and zFormat set +** to NULL. */ -SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_soft_heap_limit64(sqlcipher_sqlite3_int64 n){ - sqlcipher_sqlite3_int64 priorLimit; - sqlcipher_sqlite3_int64 excess; - sqlcipher_sqlite3_int64 nUsed; -#ifndef SQLITE_OMIT_AUTOINIT - int rc = sqlcipher_sqlite3_initialize(); - if( rc ) return -1; -#endif - sqlcipher_sqlite3_mutex_enter(mem0.mutex); - priorLimit = mem0.alarmThreshold; - if( n<0 ){ - sqlcipher_sqlite3_mutex_leave(mem0.mutex); - return priorLimit; - } - if( mem0.hardLimit>0 && (n>mem0.hardLimit || n==0) ){ - n = mem0.hardLimit; +SQLITE_PRIVATE void sqlcipher_sqlite3ErrorWithMsg(sqlcipher_sqlite3 *db, int err_code, const char *zFormat, ...){ + assert( db!=0 ); + db->errCode = err_code; + sqlcipher_sqlite3SystemError(db, err_code); + if( zFormat==0 ){ + sqlcipher_sqlite3Error(db, err_code); + }else if( db->pErr || (db->pErr = sqlcipher_sqlite3ValueNew(db))!=0 ){ + char *z; + va_list ap; + va_start(ap, zFormat); + z = sqlcipher_sqlite3VMPrintf(db, zFormat, ap); + va_end(ap); + sqlcipher_sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC); } - mem0.alarmThreshold = n; - nUsed = sqlcipher_sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); - AtomicStore(&mem0.nearlyFull, n>0 && n<=nUsed); - sqlcipher_sqlite3_mutex_leave(mem0.mutex); - excess = sqlcipher_sqlite3_memory_used() - n; - if( excess>0 ) sqlcipher_sqlite3_release_memory((int)(excess & 0x7fffffff)); - return priorLimit; -} -SQLITE_API void sqlcipher_sqlite3_soft_heap_limit(int n){ - if( n<0 ) n = 0; - sqlcipher_sqlite3_soft_heap_limit64(n); } /* -** Set the hard heap-size limit for the library. An argument of zero -** disables the hard heap limit. A negative argument is a no-op used -** to obtain the return value without affecting the hard heap limit. -** -** The return value is the value of the hard heap limit just prior to -** calling this interface. +** Add an error message to pParse->zErrMsg and increment pParse->nErr. ** -** Setting the hard heap limit will also activate the soft heap limit -** and constrain the soft heap limit to be no more than the hard heap -** limit. +** This function should be used to report any error that occurs while +** compiling an SQL statement (i.e. within sqlcipher_sqlite3_prepare()). The +** last thing the sqlcipher_sqlite3_prepare() function does is copy the error +** stored by this function into the database handle using sqlcipher_sqlite3Error(). +** Functions sqlcipher_sqlite3Error() or sqlcipher_sqlite3ErrorWithMsg() should be used +** during statement execution (sqlcipher_sqlite3_step() etc.). */ -SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_hard_heap_limit64(sqlcipher_sqlite3_int64 n){ - sqlcipher_sqlite3_int64 priorLimit; -#ifndef SQLITE_OMIT_AUTOINIT - int rc = sqlcipher_sqlite3_initialize(); - if( rc ) return -1; -#endif - sqlcipher_sqlite3_mutex_enter(mem0.mutex); - priorLimit = mem0.hardLimit; - if( n>=0 ){ - mem0.hardLimit = n; - if( ndb; + assert( db!=0 ); + assert( db->pParse==pParse || db->pParse->pToplevel==pParse ); + db->errByteOffset = -2; + va_start(ap, zFormat); + zMsg = sqlcipher_sqlite3VMPrintf(db, zFormat, ap); + va_end(ap); + if( db->errByteOffset<-1 ) db->errByteOffset = -1; + if( db->suppressErr ){ + sqlcipher_sqlite3DbFree(db, zMsg); + if( db->mallocFailed ){ + pParse->nErr++; + pParse->rc = SQLITE_NOMEM; } + }else{ + pParse->nErr++; + sqlcipher_sqlite3DbFree(db, pParse->zErrMsg); + pParse->zErrMsg = zMsg; + pParse->rc = SQLITE_ERROR; + pParse->pWith = 0; } - sqlcipher_sqlite3_mutex_leave(mem0.mutex); - return priorLimit; } +/* +** If database connection db is currently parsing SQL, then transfer +** error code errCode to that parser if the parser has not already +** encountered some other kind of error. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3ErrorToParser(sqlcipher_sqlite3 *db, int errCode){ + Parse *pParse; + if( db==0 || (pParse = db->pParse)==0 ) return errCode; + pParse->rc = errCode; + pParse->nErr++; + return errCode; +} /* -** Initialize the memory allocation subsystem. +** Convert an SQL-style quoted string into a normal string by removing +** the quote characters. The conversion is done in-place. If the +** input does not begin with a quote character, then this routine +** is a no-op. +** +** The input string must be zero-terminated. A new zero-terminator +** is added to the dequoted string. +** +** The return value is -1 if no dequoting occurs or the length of the +** dequoted string, exclusive of the zero terminator, if dequoting does +** occur. +** +** 2002-02-14: This routine is extended to remove MS-Access style +** brackets from around identifiers. For example: "[a-b-c]" becomes +** "a-b-c". */ -SQLITE_PRIVATE int sqlcipher_sqlite3MallocInit(void){ - int rc; - if( sqlcipher_sqlite3GlobalConfig.m.xMalloc==0 ){ - sqlcipher_sqlite3MemSetDefault(); - } - memset(&mem0, 0, sizeof(mem0)); - mem0.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); - if( sqlcipher_sqlite3GlobalConfig.pPage==0 || sqlcipher_sqlite3GlobalConfig.szPage<512 - || sqlcipher_sqlite3GlobalConfig.nPage<=0 ){ - sqlcipher_sqlite3GlobalConfig.pPage = 0; - sqlcipher_sqlite3GlobalConfig.szPage = 0; - } - rc = sqlcipher_sqlite3GlobalConfig.m.xInit(sqlcipher_sqlite3GlobalConfig.m.pAppData); - if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0)); -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - /* install wrapping functions for memory management - that will wipe all memory allocated by SQLite - when freed */ - if( rc==SQLITE_OK ) { - extern void sqlcipher_init_memmethods(void); - sqlcipher_init_memmethods(); +SQLITE_PRIVATE void sqlcipher_sqlite3Dequote(char *z){ + char quote; + int i, j; + if( z==0 ) return; + quote = z[0]; + if( !sqlcipher_sqlite3Isquote(quote) ) return; + if( quote=='[' ) quote = ']'; + for(i=1, j=0;; i++){ + assert( z[i] ); + if( z[i]==quote ){ + if( z[i+1]==quote ){ + z[j++] = quote; + i++; + }else{ + break; + } + }else{ + z[j++] = z[i]; + } } -#endif -/* END SQLCIPHER */ - return rc; + z[j] = 0; +} +SQLITE_PRIVATE void sqlcipher_sqlite3DequoteExpr(Expr *p){ + assert( !ExprHasProperty(p, EP_IntValue) ); + assert( sqlcipher_sqlite3Isquote(p->u.zToken[0]) ); + p->flags |= p->u.zToken[0]=='"' ? EP_Quoted|EP_DblQuoted : EP_Quoted; + sqlcipher_sqlite3Dequote(p->u.zToken); } /* -** Return true if the heap is currently under memory pressure - in other -** words if the amount of heap used is close to the limit set by -** sqlcipher_sqlite3_soft_heap_limit(). +** If the input token p is quoted, try to adjust the token to remove +** the quotes. This is not always possible: +** +** "abc" -> abc +** "ab""cd" -> (not possible because of the interior "") +** +** Remove the quotes if possible. This is a optimization. The overall +** system should still return the correct answer even if this routine +** is always a no-op. */ -SQLITE_PRIVATE int sqlcipher_sqlite3HeapNearlyFull(void){ - return AtomicLoad(&mem0.nearlyFull); +SQLITE_PRIVATE void sqlcipher_sqlite3DequoteToken(Token *p){ + unsigned int i; + if( p->n<2 ) return; + if( !sqlcipher_sqlite3Isquote(p->z[0]) ) return; + for(i=1; in-1; i++){ + if( sqlcipher_sqlite3Isquote(p->z[i]) ) return; + } + p->n -= 2; + p->z++; } /* -** Deinitialize the memory allocation subsystem. +** Generate a Token object from a string */ -SQLITE_PRIVATE void sqlcipher_sqlite3MallocEnd(void){ - if( sqlcipher_sqlite3GlobalConfig.m.xShutdown ){ - sqlcipher_sqlite3GlobalConfig.m.xShutdown(sqlcipher_sqlite3GlobalConfig.m.pAppData); - } - memset(&mem0, 0, sizeof(mem0)); +SQLITE_PRIVATE void sqlcipher_sqlite3TokenInit(Token *p, char *z){ + p->z = z; + p->n = sqlcipher_sqlite3Strlen30(z); } +/* Convenient short-hand */ +#define UpperToLower sqlcipher_sqlite3UpperToLower + /* -** Return the amount of memory currently checked out. +** Some systems have stricmp(). Others have strcasecmp(). Because +** there is no consistency, we will define our own. +** +** IMPLEMENTATION-OF: R-30243-02494 The sqlcipher_sqlite3_stricmp() and +** sqlcipher_sqlite3_strnicmp() APIs allow applications and extensions to compare +** the contents of two buffers containing UTF-8 strings in a +** case-independent fashion, using the same definition of "case +** independence" that SQLite uses internally when comparing identifiers. */ -SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_memory_used(void){ - sqlcipher_sqlite3_int64 res, mx; - sqlcipher_sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, 0); - return res; +SQLITE_API int sqlcipher_sqlite3_stricmp(const char *zLeft, const char *zRight){ + if( zLeft==0 ){ + return zRight ? -1 : 0; + }else if( zRight==0 ){ + return 1; + } + return sqlcipher_sqlite3StrICmp(zLeft, zRight); +} +SQLITE_PRIVATE int sqlcipher_sqlite3StrICmp(const char *zLeft, const char *zRight){ + unsigned char *a, *b; + int c, x; + a = (unsigned char *)zLeft; + b = (unsigned char *)zRight; + for(;;){ + c = *a; + x = *b; + if( c==x ){ + if( c==0 ) break; + }else{ + c = (int)UpperToLower[c] - (int)UpperToLower[x]; + if( c ) break; + } + a++; + b++; + } + return c; +} +SQLITE_API int sqlcipher_sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ + register unsigned char *a, *b; + if( zLeft==0 ){ + return zRight ? -1 : 0; + }else if( zRight==0 ){ + return 1; + } + a = (unsigned char *)zLeft; + b = (unsigned char *)zRight; + while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } + return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; } /* -** Return the maximum amount of memory that has ever been -** checked out since either the beginning of this process -** or since the most recent reset. +** Compute an 8-bit hash on a string that is insensitive to case differences */ -SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_memory_highwater(int resetFlag){ - sqlcipher_sqlite3_int64 res, mx; - sqlcipher_sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, resetFlag); - return mx; +SQLITE_PRIVATE u8 sqlcipher_sqlite3StrIHash(const char *z){ + u8 h = 0; + if( z==0 ) return 0; + while( z[0] ){ + h += UpperToLower[(unsigned char)z[0]]; + z++; + } + return h; } /* -** Trigger the alarm +** Compute 10 to the E-th power. Examples: E==1 results in 10. +** E==2 results in 100. E==50 results in 1.0e50. +** +** This routine only works for values of E between 1 and 341. */ -static void sqlcipher_sqlite3MallocAlarm(int nByte){ - if( mem0.alarmThreshold<=0 ) return; - sqlcipher_sqlite3_mutex_leave(mem0.mutex); - sqlcipher_sqlite3_release_memory(nByte); - sqlcipher_sqlite3_mutex_enter(mem0.mutex); +static LONGDOUBLE_TYPE sqlcipher_sqlite3Pow10(int E){ +#if defined(_MSC_VER) + static const LONGDOUBLE_TYPE x[] = { + 1.0e+001L, + 1.0e+002L, + 1.0e+004L, + 1.0e+008L, + 1.0e+016L, + 1.0e+032L, + 1.0e+064L, + 1.0e+128L, + 1.0e+256L + }; + LONGDOUBLE_TYPE r = 1.0; + int i; + assert( E>=0 && E<=307 ); + for(i=0; E!=0; i++, E >>=1){ + if( E & 1 ) r *= x[i]; + } + return r; +#else + LONGDOUBLE_TYPE x = 10.0; + LONGDOUBLE_TYPE r = 1.0; + while(1){ + if( E & 1 ) r *= x; + E >>= 1; + if( E==0 ) break; + x *= x; + } + return r; +#endif } /* -** Do a memory allocation with statistics and alarms. Assume the -** lock is already held. +** The string z[] is an text representation of a real number. +** Convert this string to a double and write it into *pResult. +** +** The string z[] is length bytes in length (bytes, not characters) and +** uses the encoding enc. The string is not necessarily zero-terminated. +** +** Return TRUE if the result is a valid real number (or integer) and FALSE +** if the string is empty or contains extraneous text. More specifically +** return +** 1 => The input string is a pure integer +** 2 or more => The input has a decimal point or eNNN clause +** 0 or less => The input string is not a valid number +** -1 => Not a valid number, but has a valid prefix which +** includes a decimal point and/or an eNNN clause +** +** Valid numbers are in one of these formats: +** +** [+-]digits[E[+-]digits] +** [+-]digits.[digits][E[+-]digits] +** [+-].digits[E[+-]digits] +** +** Leading and trailing whitespace is ignored for the purpose of determining +** validity. +** +** If some prefix of the input string is a valid number, this routine +** returns FALSE but it still converts the prefix and writes the result +** into *pResult. */ -static void mallocWithAlarm(int n, void **pp){ - void *p; - int nFull; - assert( sqlcipher_sqlite3_mutex_held(mem0.mutex) ); - assert( n>0 ); +#if defined(_MSC_VER) +#pragma warning(disable : 4756) +#endif +SQLITE_PRIVATE int sqlcipher_sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){ +#ifndef SQLITE_OMIT_FLOATING_POINT + int incr; + const char *zEnd; + /* sign * significand * (10 ^ (esign * exponent)) */ + int sign = 1; /* sign of significand */ + i64 s = 0; /* significand */ + int d = 0; /* adjust exponent for shifting decimal point */ + int esign = 1; /* sign of exponent */ + int e = 0; /* exponent */ + int eValid = 1; /* True exponent is either not used or is well-formed */ + double result; + int nDigit = 0; /* Number of digits processed */ + int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ - /* In Firefox (circa 2017-02-08), xRoundup() is remapped to an internal - ** implementation of malloc_good_size(), which must be called in debug - ** mode and specifically when the DMD "Dark Matter Detector" is enabled - ** or else a crash results. Hence, do not attempt to optimize out the - ** following xRoundup() call. */ - nFull = sqlcipher_sqlite3GlobalConfig.m.xRoundup(n); + assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); + *pResult = 0.0; /* Default return value, in case of an error */ + if( length==0 ) return 0; - sqlcipher_sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n); - if( mem0.alarmThreshold>0 ){ - sqlcipher_sqlite3_int64 nUsed = sqlcipher_sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); - if( nUsed >= mem0.alarmThreshold - nFull ){ - AtomicStore(&mem0.nearlyFull, 1); - sqlcipher_sqlite3MallocAlarm(nFull); - if( mem0.hardLimit ){ - nUsed = sqlcipher_sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); - if( nUsed >= mem0.hardLimit - nFull ){ - *pp = 0; - return; - } + if( enc==SQLITE_UTF8 ){ + incr = 1; + zEnd = z + length; + }else{ + int i; + incr = 2; + length &= ~1; + assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); + testcase( enc==SQLITE_UTF16LE ); + testcase( enc==SQLITE_UTF16BE ); + for(i=3-enc; i=zEnd ) return 0; + + /* get sign of significand */ + if( *z=='-' ){ + sign = -1; + z+=incr; + }else if( *z=='+' ){ + z+=incr; + } + + /* copy max significant digits to significand */ + while( z=((LARGEST_INT64-9)/10) ){ + /* skip non-significant significand digits + ** (increase exponent by d to shift decimal left) */ + while( z=zEnd ) goto do_atof_calc; + + /* if decimal point is present */ + if( *z=='.' ){ + z+=incr; + eType++; + /* copy digits from after decimal to significand + ** (decrease exponent by d to shift decimal right) */ + while( z0 ){ - sqlcipher_sqlite3MallocAlarm(nFull); - p = sqlcipher_sqlite3GlobalConfig.m.xMalloc(nFull); + if( z>=zEnd ) goto do_atof_calc; + + /* if exponent is present */ + if( *z=='e' || *z=='E' ){ + z+=incr; + eValid = 0; + eType++; + + /* This branch is needed to avoid a (harmless) buffer overread. The + ** special comment alerts the mutation tester that the correct answer + ** is obtained even if the branch is omitted */ + if( z>=zEnd ) goto do_atof_calc; /*PREVENTS-HARMLESS-OVERREAD*/ + + /* get sign of exponent */ + if( *z=='-' ){ + esign = -1; + z+=incr; + }else if( *z=='+' ){ + z+=incr; + } + /* copy digits to exponent */ + while( z0 ){ /*OPTIMIZATION-IF-TRUE*/ + if( esign>0 ){ + if( s>=(LARGEST_INT64/10) ) break; /*OPTIMIZATION-IF-FALSE*/ + s *= 10; + }else{ + if( s%10!=0 ) break; /*OPTIMIZATION-IF-FALSE*/ + s /= 10; + } + e--; + } + + /* adjust the sign of significand */ + s = sign<0 ? -s : s; + + if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/ + result = (double)s; + }else{ + /* attempt to handle extremely small/large numbers better */ + if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/ + if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/ + LONGDOUBLE_TYPE scale = sqlcipher_sqlite3Pow10(e-308); + if( esign<0 ){ + result = s / scale; + result /= 1.0e+308; + }else{ + result = s * scale; + result *= 1.0e+308; + } + }else{ assert( e>=342 ); + if( esign<0 ){ + result = 0.0*s; + }else{ +#ifdef INFINITY + result = INFINITY*s; +#else + result = 1e308*1e308*s; /* Infinity */ #endif - if( p ){ - nFull = sqlcipher_sqlite3MallocSize(p); - sqlcipher_sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nFull); - sqlcipher_sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1); + } + } + }else{ + LONGDOUBLE_TYPE scale = sqlcipher_sqlite3Pow10(e); + if( esign<0 ){ + result = s / scale; + }else{ + result = s * scale; + } + } + } } - *pp = p; + + /* store the result */ + *pResult = result; + + /* return true if number and no extra non-whitespace chracters after */ + if( z==zEnd && nDigit>0 && eValid && eType>0 ){ + return eType; + }else if( eType>=2 && (eType==3 || eValid) && nDigit>0 ){ + return -1; + }else{ + return 0; + } +#else + return !sqlcipher_sqlite3Atoi64(z, pResult, length, enc); +#endif /* SQLITE_OMIT_FLOATING_POINT */ } +#if defined(_MSC_VER) +#pragma warning(default : 4756) +#endif /* -** Allocate memory. This routine is like sqlcipher_sqlite3_malloc() except that it -** assumes the memory subsystem has already been initialized. +** Render an signed 64-bit integer as text. Store the result in zOut[]. +** +** The caller must ensure that zOut[] is at least 21 bytes in size. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3Malloc(u64 n){ - void *p; - if( n==0 || n>=0x7fffff00 ){ - /* A memory allocation of a number of bytes which is near the maximum - ** signed integer value might cause an integer overflow inside of the - ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving - ** 255 bytes of overhead. SQLite itself will never use anything near - ** this amount. The only way to reach the limit is with sqlcipher_sqlite3_malloc() */ - p = 0; - }else if( sqlcipher_sqlite3GlobalConfig.bMemstat ){ - sqlcipher_sqlite3_mutex_enter(mem0.mutex); - mallocWithAlarm((int)n, &p); - sqlcipher_sqlite3_mutex_leave(mem0.mutex); +SQLITE_PRIVATE void sqlcipher_sqlite3Int64ToText(i64 v, char *zOut){ + int i; + u64 x; + char zTemp[22]; + if( v<0 ){ + x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v; }else{ - p = sqlcipher_sqlite3GlobalConfig.m.xMalloc((int)n); + x = v; } - assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-11148-40995 */ - return p; + i = sizeof(zTemp)-2; + zTemp[sizeof(zTemp)-1] = 0; + do{ + zTemp[i--] = (x%10) + '0'; + x = x/10; + }while( x ); + if( v<0 ) zTemp[i--] = '-'; + memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); } /* -** This version of the memory allocation is for use by the application. -** First make sure the memory subsystem is initialized, then do the -** allocation. +** Compare the 19-character string zNum against the text representation +** value 2^63: 9223372036854775808. Return negative, zero, or positive +** if zNum is less than, equal to, or greater than the string. +** Note that zNum must contain exactly 19 characters. +** +** Unlike memcmp() this routine is guaranteed to return the difference +** in the values of the last digit if the only difference is in the +** last digit. So, for example, +** +** compare2pow63("9223372036854775800", 1) +** +** will return -8. */ -SQLITE_API void *sqlcipher_sqlite3_malloc(int n){ -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; -#endif - return n<=0 ? 0 : sqlcipher_sqlite3Malloc(n); -} -SQLITE_API void *sqlcipher_sqlite3_malloc64(sqlcipher_sqlite3_uint64 n){ -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; -#endif - return sqlcipher_sqlite3Malloc(n); +static int compare2pow63(const char *zNum, int incr){ + int c = 0; + int i; + /* 012345678901234567 */ + const char *pow63 = "922337203685477580"; + for(i=0; c==0 && i<18; i++){ + c = (zNum[i*incr]-pow63[i])*10; + } + if( c==0 ){ + c = zNum[18*incr] - '8'; + testcase( c==(-1) ); + testcase( c==0 ); + testcase( c==(+1) ); + } + return c; } /* -** TRUE if p is a lookaside memory allocation from db +** Convert zNum to a 64-bit signed integer. zNum must be decimal. This +** routine does *not* accept hexadecimal notation. +** +** Returns: +** +** -1 Not even a prefix of the input text looks like an integer +** 0 Successful transformation. Fits in a 64-bit signed integer. +** 1 Excess non-space text after the integer value +** 2 Integer too large for a 64-bit signed integer or is malformed +** 3 Special case of 9223372036854775808 +** +** length is the number of bytes in the string (bytes, not characters). +** The string is not necessarily zero-terminated. The encoding is +** given by enc. */ -#ifndef SQLITE_OMIT_LOOKASIDE -static int isLookaside(sqlcipher_sqlite3 *db, void *p){ - return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd); +SQLITE_PRIVATE int sqlcipher_sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ + int incr; + u64 u = 0; + int neg = 0; /* assume positive */ + int i; + int c = 0; + int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */ + int rc; /* Baseline return code */ + const char *zStart; + const char *zEnd = zNum + length; + assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); + if( enc==SQLITE_UTF8 ){ + incr = 1; + }else{ + incr = 2; + length &= ~1; + assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); + for(i=3-enc; i='0' && c<='9'; i+=incr){ + u = u*10 + c - '0'; + } + testcase( i==18*incr ); + testcase( i==19*incr ); + testcase( i==20*incr ); + if( u>LARGEST_INT64 ){ + /* This test and assignment is needed only to suppress UB warnings + ** from clang and -fsanitize=undefined. This test and assignment make + ** the code a little larger and slower, and no harm comes from omitting + ** them, but we must appaise the undefined-behavior pharisees. */ + *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; + }else if( neg ){ + *pNum = -(i64)u; + }else{ + *pNum = (i64)u; + } + rc = 0; + if( i==0 && zStart==zNum ){ /* No digits */ + rc = -1; + }else if( nonNum ){ /* UTF16 with high-order bytes non-zero */ + rc = 1; + }else if( &zNum[i]19*incr ? 1 : compare2pow63(zNum, incr); + if( c<0 ){ + /* zNum is less than 9223372036854775808 so it fits */ + assert( u<=LARGEST_INT64 ); + return rc; + }else{ + *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; + if( c>0 ){ + /* zNum is greater than 9223372036854775808 so it overflows */ + return 2; + }else{ + /* zNum is exactly 9223372036854775808. Fits if negative. The + ** special case 2 overflow if positive */ + assert( u-1==LARGEST_INT64 ); + return neg ? rc : 3; + } + } + } } -#else -#define isLookaside(A,B) 0 -#endif /* -** Return the size of a memory allocation previously obtained from -** sqlcipher_sqlite3Malloc() or sqlcipher_sqlite3_malloc(). +** Transform a UTF-8 integer literal, in either decimal or hexadecimal, +** into a 64-bit signed integer. This routine accepts hexadecimal literals, +** whereas sqlcipher_sqlite3Atoi64() does not. +** +** Returns: +** +** 0 Successful transformation. Fits in a 64-bit signed integer. +** 1 Excess text after the integer value +** 2 Integer too large for a 64-bit signed integer or is malformed +** 3 Special case of 9223372036854775808 */ -SQLITE_PRIVATE int sqlcipher_sqlite3MallocSize(void *p){ - assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - return sqlcipher_sqlite3GlobalConfig.m.xSize(p); -} -static int lookasideMallocSize(sqlcipher_sqlite3 *db, void *p){ -#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - return plookaside.pMiddle ? db->lookaside.szTrue : LOOKASIDE_SMALL; -#else - return db->lookaside.szTrue; -#endif +SQLITE_PRIVATE int sqlcipher_sqlite3DecOrHexToI64(const char *z, i64 *pOut){ +#ifndef SQLITE_OMIT_HEX_INTEGER + if( z[0]=='0' + && (z[1]=='x' || z[1]=='X') + ){ + u64 u = 0; + int i, k; + for(i=2; z[i]=='0'; i++){} + for(k=i; sqlcipher_sqlite3Isxdigit(z[k]); k++){ + u = u*16 + sqlcipher_sqlite3HexToInt(z[k]); + } + memcpy(pOut, &u, 8); + return (z[k]==0 && k-i<=16) ? 0 : 2; + }else +#endif /* SQLITE_OMIT_HEX_INTEGER */ + { + return sqlcipher_sqlite3Atoi64(z, pOut, sqlcipher_sqlite3Strlen30(z), SQLITE_UTF8); + } } -SQLITE_PRIVATE int sqlcipher_sqlite3DbMallocSize(sqlcipher_sqlite3 *db, void *p){ - assert( p!=0 ); -#ifdef SQLITE_DEBUG - if( db==0 || !isLookaside(db,p) ){ - if( db==0 ){ - assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); - assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); + +/* +** If zNum represents an integer that will fit in 32-bits, then set +** *pValue to that integer and return true. Otherwise return false. +** +** This routine accepts both decimal and hexadecimal notation for integers. +** +** Any non-numeric characters that following zNum are ignored. +** This is different from sqlcipher_sqlite3Atoi64() which requires the +** input number to be zero-terminated. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3GetInt32(const char *zNum, int *pValue){ + sqlite_int64 v = 0; + int i, c; + int neg = 0; + if( zNum[0]=='-' ){ + neg = 1; + zNum++; + }else if( zNum[0]=='+' ){ + zNum++; + } +#ifndef SQLITE_OMIT_HEX_INTEGER + else if( zNum[0]=='0' + && (zNum[1]=='x' || zNum[1]=='X') + && sqlcipher_sqlite3Isxdigit(zNum[2]) + ){ + u32 u = 0; + zNum += 2; + while( zNum[0]=='0' ) zNum++; + for(i=0; sqlcipher_sqlite3Isxdigit(zNum[i]) && i<8; i++){ + u = u*16 + sqlcipher_sqlite3HexToInt(zNum[i]); + } + if( (u&0x80000000)==0 && sqlcipher_sqlite3Isxdigit(zNum[i])==0 ){ + memcpy(pValue, &u, 4); + return 1; }else{ - assert( sqlcipher_sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + return 0; } } -#endif - if( db ){ - if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ -#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ - assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - return LOOKASIDE_SMALL; - } -#endif - if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){ - assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - return db->lookaside.szTrue; - } - } +#endif + if( !sqlcipher_sqlite3Isdigit(zNum[0]) ) return 0; + while( zNum[0]=='0' ) zNum++; + for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){ + v = v*10 + c; + } + + /* The longest decimal representation of a 32 bit integer is 10 digits: + ** + ** 1234567890 + ** 2^31 -> 2147483648 + */ + testcase( i==10 ); + if( i>10 ){ + return 0; + } + testcase( v-neg==2147483647 ); + if( v-neg>2147483647 ){ + return 0; + } + if( neg ){ + v = -v; } - return sqlcipher_sqlite3GlobalConfig.m.xSize(p); -} -SQLITE_API sqlcipher_sqlite3_uint64 sqlcipher_sqlite3_msize(void *p){ - assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); - assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - return p ? sqlcipher_sqlite3GlobalConfig.m.xSize(p) : 0; + *pValue = (int)v; + return 1; } /* -** Free memory previously obtained from sqlcipher_sqlite3Malloc(). +** Return a 32-bit integer value extracted from a string. If the +** string is not an integer, just return 0. */ -SQLITE_API void sqlcipher_sqlite3_free(void *p){ - if( p==0 ) return; /* IMP: R-49053-54554 */ - assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); - if( sqlcipher_sqlite3GlobalConfig.bMemstat ){ - sqlcipher_sqlite3_mutex_enter(mem0.mutex); - sqlcipher_sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, sqlcipher_sqlite3MallocSize(p)); - sqlcipher_sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1); - sqlcipher_sqlite3GlobalConfig.m.xFree(p); - sqlcipher_sqlite3_mutex_leave(mem0.mutex); - }else{ - sqlcipher_sqlite3GlobalConfig.m.xFree(p); - } +SQLITE_PRIVATE int sqlcipher_sqlite3Atoi(const char *z){ + int x = 0; + sqlcipher_sqlite3GetInt32(z, &x); + return x; } /* -** Add the size of memory allocation "p" to the count in -** *db->pnBytesFreed. +** Try to convert z into an unsigned 32-bit integer. Return true on +** success and false if there is an error. +** +** Only decimal notation is accepted. */ -static SQLITE_NOINLINE void measureAllocationSize(sqlcipher_sqlite3 *db, void *p){ - *db->pnBytesFreed += sqlcipher_sqlite3DbMallocSize(db,p); +SQLITE_PRIVATE int sqlcipher_sqlite3GetUInt32(const char *z, u32 *pI){ + u64 v = 0; + int i; + for(i=0; sqlcipher_sqlite3Isdigit(z[i]); i++){ + v = v*10 + z[i] - '0'; + if( v>4294967296LL ){ *pI = 0; return 0; } + } + if( i==0 || z[i]!=0 ){ *pI = 0; return 0; } + *pI = (u32)v; + return 1; } /* -** Free memory that might be associated with a particular database -** connection. Calling sqlcipher_sqlite3DbFree(D,X) for X==0 is a harmless no-op. -** The sqlcipher_sqlite3DbFreeNN(D,X) version requires that X be non-NULL. +** The variable-length integer encoding is as follows: +** +** KEY: +** A = 0xxxxxxx 7 bits of data and one flag bit +** B = 1xxxxxxx 7 bits of data and one flag bit +** C = xxxxxxxx 8 bits of data +** +** 7 bits - A +** 14 bits - BA +** 21 bits - BBA +** 28 bits - BBBA +** 35 bits - BBBBA +** 42 bits - BBBBBA +** 49 bits - BBBBBBA +** 56 bits - BBBBBBBA +** 64 bits - BBBBBBBBC */ -SQLITE_PRIVATE void sqlcipher_sqlite3DbFreeNN(sqlcipher_sqlite3 *db, void *p){ - assert( db==0 || sqlcipher_sqlite3_mutex_held(db->mutex) ); - assert( p!=0 ); - if( db ){ - if( db->pnBytesFreed ){ - measureAllocationSize(db, p); - return; - } - if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ -#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ - LookasideSlot *pBuf = (LookasideSlot*)p; -#ifdef SQLITE_DEBUG - memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */ -#endif - pBuf->pNext = db->lookaside.pSmallFree; - db->lookaside.pSmallFree = pBuf; - return; - } -#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ - if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){ - LookasideSlot *pBuf = (LookasideSlot*)p; -#ifdef SQLITE_DEBUG - memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */ -#endif - pBuf->pNext = db->lookaside.pFree; - db->lookaside.pFree = pBuf; - return; - } - } - } - assert( sqlcipher_sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( db!=0 || sqlcipher_sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); - sqlcipher_sqlite3MemdebugSetType(p, MEMTYPE_HEAP); - sqlcipher_sqlite3_free(p); -} -SQLITE_PRIVATE void sqlcipher_sqlite3DbFree(sqlcipher_sqlite3 *db, void *p){ - assert( db==0 || sqlcipher_sqlite3_mutex_held(db->mutex) ); - if( p ) sqlcipher_sqlite3DbFreeNN(db, p); -} /* -** Change the size of an existing memory allocation +** Write a 64-bit variable-length integer to memory starting at p[0]. +** The length of data write will be between 1 and 9 bytes. The number +** of bytes written is returned. +** +** A variable-length integer consists of the lower 7 bits of each byte +** for all bytes that have the 8th bit set and one byte with the 8th +** bit clear. Except, if we get to the 9th byte, it stores the full +** 8 bits and is the last byte. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3Realloc(void *pOld, u64 nBytes){ - int nOld, nNew, nDiff; - void *pNew; - assert( sqlcipher_sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) ); - assert( sqlcipher_sqlite3MemdebugNoType(pOld, (u8)~MEMTYPE_HEAP) ); - if( pOld==0 ){ - return sqlcipher_sqlite3Malloc(nBytes); /* IMP: R-04300-56712 */ +static int SQLITE_NOINLINE putVarint64(unsigned char *p, u64 v){ + int i, j, n; + u8 buf[10]; + if( v & (((u64)0xff000000)<<32) ){ + p[8] = (u8)v; + v >>= 8; + for(i=7; i>=0; i--){ + p[i] = (u8)((v & 0x7f) | 0x80); + v >>= 7; + } + return 9; } - if( nBytes==0 ){ - sqlcipher_sqlite3_free(pOld); /* IMP: R-26507-47431 */ - return 0; + n = 0; + do{ + buf[n++] = (u8)((v & 0x7f) | 0x80); + v >>= 7; + }while( v!=0 ); + buf[0] &= 0x7f; + assert( n<=9 ); + for(i=0, j=n-1; j>=0; j--, i++){ + p[i] = buf[j]; } - if( nBytes>=0x7fffff00 ){ - /* The 0x7ffff00 limit term is explained in comments on sqlcipher_sqlite3Malloc() */ - return 0; + return n; +} +SQLITE_PRIVATE int sqlcipher_sqlite3PutVarint(unsigned char *p, u64 v){ + if( v<=0x7f ){ + p[0] = v&0x7f; + return 1; } - nOld = sqlcipher_sqlite3MallocSize(pOld); - /* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second - ** argument to xRealloc is always a value returned by a prior call to - ** xRoundup. */ - nNew = sqlcipher_sqlite3GlobalConfig.m.xRoundup((int)nBytes); - if( nOld==nNew ){ - pNew = pOld; - }else if( sqlcipher_sqlite3GlobalConfig.bMemstat ){ - sqlcipher_sqlite3_int64 nUsed; - sqlcipher_sqlite3_mutex_enter(mem0.mutex); - sqlcipher_sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes); - nDiff = nNew - nOld; - if( nDiff>0 && (nUsed = sqlcipher_sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)) >= - mem0.alarmThreshold-nDiff ){ - sqlcipher_sqlite3MallocAlarm(nDiff); - if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){ - sqlcipher_sqlite3_mutex_leave(mem0.mutex); - return 0; - } - } - pNew = sqlcipher_sqlite3GlobalConfig.m.xRealloc(pOld, nNew); -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT - if( pNew==0 && mem0.alarmThreshold>0 ){ - sqlcipher_sqlite3MallocAlarm((int)nBytes); - pNew = sqlcipher_sqlite3GlobalConfig.m.xRealloc(pOld, nNew); - } -#endif - if( pNew ){ - nNew = sqlcipher_sqlite3MallocSize(pNew); - sqlcipher_sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld); - } - sqlcipher_sqlite3_mutex_leave(mem0.mutex); - }else{ - pNew = sqlcipher_sqlite3GlobalConfig.m.xRealloc(pOld, nNew); + if( v<=0x3fff ){ + p[0] = ((v>>7)&0x7f)|0x80; + p[1] = v&0x7f; + return 2; } - assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-11148-40995 */ - return pNew; + return putVarint64(p,v); } /* -** The public interface to sqlcipher_sqlite3Realloc. Make sure that the memory -** subsystem is initialized prior to invoking sqliteRealloc. +** Bitmasks used by sqlcipher_sqlite3GetVarint(). These precomputed constants +** are defined here rather than simply putting the constant expressions +** inline in order to work around bugs in the RVT compiler. +** +** SLOT_2_0 A mask for (0x7f<<14) | 0x7f +** +** SLOT_4_2_0 A mask for (0x7f<<28) | SLOT_2_0 */ -SQLITE_API void *sqlcipher_sqlite3_realloc(void *pOld, int n){ -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; -#endif - if( n<0 ) n = 0; /* IMP: R-26507-47431 */ - return sqlcipher_sqlite3Realloc(pOld, n); -} -SQLITE_API void *sqlcipher_sqlite3_realloc64(void *pOld, sqlcipher_sqlite3_uint64 n){ -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; -#endif - return sqlcipher_sqlite3Realloc(pOld, n); -} +#define SLOT_2_0 0x001fc07f +#define SLOT_4_2_0 0xf01fc07f /* -** Allocate and zero memory. +** Read a 64-bit variable-length integer from memory starting at p[0]. +** Return the number of bytes read. The value is stored in *v. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3MallocZero(u64 n){ - void *p = sqlcipher_sqlite3Malloc(n); - if( p ){ - memset(p, 0, (size_t)n); +SQLITE_PRIVATE u8 sqlcipher_sqlite3GetVarint(const unsigned char *p, u64 *v){ + u32 a,b,s; + + if( ((signed char*)p)[0]>=0 ){ + *v = *p; + return 1; + } + if( ((signed char*)p)[1]>=0 ){ + *v = ((u32)(p[0]&0x7f)<<7) | p[1]; + return 2; } - return p; -} -/* -** Allocate and zero memory. If the allocation fails, make -** the mallocFailed flag in the connection pointer. -*/ -SQLITE_PRIVATE void *sqlcipher_sqlite3DbMallocZero(sqlcipher_sqlite3 *db, u64 n){ - void *p; - testcase( db==0 ); - p = sqlcipher_sqlite3DbMallocRaw(db, n); - if( p ) memset(p, 0, (size_t)n); - return p; -} + /* Verify that constants are precomputed correctly */ + assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) ); + assert( SLOT_4_2_0 == ((0xfU<<28) | (0x7f<<14) | (0x7f)) ); + a = ((u32)p[0])<<14; + b = p[1]; + p += 2; + a |= *p; + /* a: p0<<14 | p2 (unmasked) */ + if (!(a&0x80)) + { + a &= SLOT_2_0; + b &= 0x7f; + b = b<<7; + a |= b; + *v = a; + return 3; + } -/* Finish the work of sqlcipher_sqlite3DbMallocRawNN for the unusual and -** slower case when the allocation cannot be fulfilled using lookaside. -*/ -static SQLITE_NOINLINE void *dbMallocRawFinish(sqlcipher_sqlite3 *db, u64 n){ - void *p; - assert( db!=0 ); - p = sqlcipher_sqlite3Malloc(n); - if( !p ) sqlcipher_sqlite3OomFault(db); - sqlcipher_sqlite3MemdebugSetType(p, - (db->lookaside.bDisable==0) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP); - return p; + /* CSE1 from below */ + a &= SLOT_2_0; + p++; + b = b<<14; + b |= *p; + /* b: p1<<14 | p3 (unmasked) */ + if (!(b&0x80)) + { + b &= SLOT_2_0; + /* moved CSE1 up */ + /* a &= (0x7f<<14)|(0x7f); */ + a = a<<7; + a |= b; + *v = a; + return 4; + } + + /* a: p0<<14 | p2 (masked) */ + /* b: p1<<14 | p3 (unmasked) */ + /* 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ + /* moved CSE1 up */ + /* a &= (0x7f<<14)|(0x7f); */ + b &= SLOT_2_0; + s = a; + /* s: p0<<14 | p2 (masked) */ + + p++; + a = a<<14; + a |= *p; + /* a: p0<<28 | p2<<14 | p4 (unmasked) */ + if (!(a&0x80)) + { + /* we can skip these cause they were (effectively) done above + ** while calculating s */ + /* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ + /* b &= (0x7f<<14)|(0x7f); */ + b = b<<7; + a |= b; + s = s>>18; + *v = ((u64)s)<<32 | a; + return 5; + } + + /* 2:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ + s = s<<7; + s |= b; + /* s: p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ + + p++; + b = b<<14; + b |= *p; + /* b: p1<<28 | p3<<14 | p5 (unmasked) */ + if (!(b&0x80)) + { + /* we can skip this cause it was (effectively) done above in calc'ing s */ + /* b &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ + a &= SLOT_2_0; + a = a<<7; + a |= b; + s = s>>18; + *v = ((u64)s)<<32 | a; + return 6; + } + + p++; + a = a<<14; + a |= *p; + /* a: p2<<28 | p4<<14 | p6 (unmasked) */ + if (!(a&0x80)) + { + a &= SLOT_4_2_0; + b &= SLOT_2_0; + b = b<<7; + a |= b; + s = s>>11; + *v = ((u64)s)<<32 | a; + return 7; + } + + /* CSE2 from below */ + a &= SLOT_2_0; + p++; + b = b<<14; + b |= *p; + /* b: p3<<28 | p5<<14 | p7 (unmasked) */ + if (!(b&0x80)) + { + b &= SLOT_4_2_0; + /* moved CSE2 up */ + /* a &= (0x7f<<14)|(0x7f); */ + a = a<<7; + a |= b; + s = s>>4; + *v = ((u64)s)<<32 | a; + return 8; + } + + p++; + a = a<<15; + a |= *p; + /* a: p4<<29 | p6<<15 | p8 (unmasked) */ + + /* moved CSE2 up */ + /* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */ + b &= SLOT_2_0; + b = b<<8; + a |= b; + + s = s<<4; + b = p[-4]; + b &= 0x7f; + b = b>>3; + s |= b; + + *v = ((u64)s)<<32 | a; + + return 9; } /* -** Allocate memory, either lookaside (if possible) or heap. -** If the allocation fails, set the mallocFailed flag in -** the connection pointer. -** -** If db!=0 and db->mallocFailed is true (indicating a prior malloc -** failure on the same database connection) then always return 0. -** Hence for a particular database connection, once malloc starts -** failing, it fails consistently until mallocFailed is reset. -** This is an important assumption. There are many places in the -** code that do things like this: -** -** int *a = (int*)sqlcipher_sqlite3DbMallocRaw(db, 100); -** int *b = (int*)sqlcipher_sqlite3DbMallocRaw(db, 200); -** if( b ) a[10] = 9; +** Read a 32-bit variable-length integer from memory starting at p[0]. +** Return the number of bytes read. The value is stored in *v. ** -** In other words, if a subsequent malloc (ex: "b") worked, it is assumed -** that all prior mallocs (ex: "a") worked too. +** If the varint stored in p[0] is larger than can fit in a 32-bit unsigned +** integer, then set *v to 0xffffffff. ** -** The sqlcipher_sqlite3MallocRawNN() variant guarantees that the "db" parameter is -** not a NULL pointer. +** A MACRO version, getVarint32, is provided which inlines the +** single-byte case. All code should use the MACRO version as +** this function assumes the single-byte case has already been handled. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3DbMallocRaw(sqlcipher_sqlite3 *db, u64 n){ - void *p; - if( db ) return sqlcipher_sqlite3DbMallocRawNN(db, n); - p = sqlcipher_sqlite3Malloc(n); - sqlcipher_sqlite3MemdebugSetType(p, MEMTYPE_HEAP); - return p; -} -SQLITE_PRIVATE void *sqlcipher_sqlite3DbMallocRawNN(sqlcipher_sqlite3 *db, u64 n){ -#ifndef SQLITE_OMIT_LOOKASIDE - LookasideSlot *pBuf; - assert( db!=0 ); - assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - assert( db->pnBytesFreed==0 ); - if( n>db->lookaside.sz ){ - if( !db->lookaside.bDisable ){ - db->lookaside.anStat[1]++; - }else if( db->mallocFailed ){ - return 0; - } - return dbMallocRawFinish(db, n); - } -#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - if( n<=LOOKASIDE_SMALL ){ - if( (pBuf = db->lookaside.pSmallFree)!=0 ){ - db->lookaside.pSmallFree = pBuf->pNext; - db->lookaside.anStat[0]++; - return (void*)pBuf; - }else if( (pBuf = db->lookaside.pSmallInit)!=0 ){ - db->lookaside.pSmallInit = pBuf->pNext; - db->lookaside.anStat[0]++; - return (void*)pBuf; - } +SQLITE_PRIVATE u8 sqlcipher_sqlite3GetVarint32(const unsigned char *p, u32 *v){ + u32 a,b; + + /* The 1-byte case. Overwhelmingly the most common. Handled inline + ** by the getVarin32() macro */ + a = *p; + /* a: p0 (unmasked) */ +#ifndef getVarint32 + if (!(a&0x80)) + { + /* Values between 0 and 127 */ + *v = a; + return 1; } #endif - if( (pBuf = db->lookaside.pFree)!=0 ){ - db->lookaside.pFree = pBuf->pNext; - db->lookaside.anStat[0]++; - return (void*)pBuf; - }else if( (pBuf = db->lookaside.pInit)!=0 ){ - db->lookaside.pInit = pBuf->pNext; - db->lookaside.anStat[0]++; - return (void*)pBuf; - }else{ - db->lookaside.anStat[2]++; + + /* The 2-byte case */ + p++; + b = *p; + /* b: p1 (unmasked) */ + if (!(b&0x80)) + { + /* Values between 128 and 16383 */ + a &= 0x7f; + a = a<<7; + *v = a | b; + return 2; } -#else - assert( db!=0 ); - assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - assert( db->pnBytesFreed==0 ); - if( db->mallocFailed ){ - return 0; + + /* The 3-byte case */ + p++; + a = a<<14; + a |= *p; + /* a: p0<<14 | p2 (unmasked) */ + if (!(a&0x80)) + { + /* Values between 16384 and 2097151 */ + a &= (0x7f<<14)|(0x7f); + b &= 0x7f; + b = b<<7; + *v = a | b; + return 3; } -#endif - return dbMallocRawFinish(db, n); -} -/* Forward declaration */ -static SQLITE_NOINLINE void *dbReallocFinish(sqlcipher_sqlite3 *db, void *p, u64 n); + /* A 32-bit varint is used to store size information in btrees. + ** Objects are rarely larger than 2MiB limit of a 3-byte varint. + ** A 3-byte varint is sufficient, for example, to record the size + ** of a 1048569-byte BLOB or string. + ** + ** We only unroll the first 1-, 2-, and 3- byte cases. The very + ** rare larger cases can be handled by the slower 64-bit varint + ** routine. + */ +#if 1 + { + u64 v64; + u8 n; -/* -** Resize the block of memory pointed to by p to n bytes. If the -** resize fails, set the mallocFailed flag in the connection object. -*/ -SQLITE_PRIVATE void *sqlcipher_sqlite3DbRealloc(sqlcipher_sqlite3 *db, void *p, u64 n){ - assert( db!=0 ); - if( p==0 ) return sqlcipher_sqlite3DbMallocRawNN(db, n); - assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - if( ((uptr)p)<(uptr)db->lookaside.pEnd ){ -#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - if( ((uptr)p)>=(uptr)db->lookaside.pMiddle ){ - if( n<=LOOKASIDE_SMALL ) return p; - }else -#endif - if( ((uptr)p)>=(uptr)db->lookaside.pStart ){ - if( n<=db->lookaside.szTrue ) return p; - } - } - return dbReallocFinish(db, p, n); -} -static SQLITE_NOINLINE void *dbReallocFinish(sqlcipher_sqlite3 *db, void *p, u64 n){ - void *pNew = 0; - assert( db!=0 ); - assert( p!=0 ); - if( db->mallocFailed==0 ){ - if( isLookaside(db, p) ){ - pNew = sqlcipher_sqlite3DbMallocRawNN(db, n); - if( pNew ){ - memcpy(pNew, p, lookasideMallocSize(db, p)); - sqlcipher_sqlite3DbFree(db, p); - } + n = sqlcipher_sqlite3GetVarint(p-2, &v64); + assert( n>3 && n<=9 ); + if( (v64 & SQLITE_MAX_U32)!=v64 ){ + *v = 0xffffffff; }else{ - assert( sqlcipher_sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( sqlcipher_sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - sqlcipher_sqlite3MemdebugSetType(p, MEMTYPE_HEAP); - pNew = sqlcipher_sqlite3Realloc(p, n); - if( !pNew ){ - sqlcipher_sqlite3OomFault(db); - } - sqlcipher_sqlite3MemdebugSetType(pNew, - (db->lookaside.bDisable==0 ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP)); + *v = (u32)v64; } + return n; } - return pNew; + +#else + /* For following code (kept for historical record only) shows an + ** unrolling for the 3- and 4-byte varint cases. This code is + ** slightly faster, but it is also larger and much harder to test. + */ + p++; + b = b<<14; + b |= *p; + /* b: p1<<14 | p3 (unmasked) */ + if (!(b&0x80)) + { + /* Values between 2097152 and 268435455 */ + b &= (0x7f<<14)|(0x7f); + a &= (0x7f<<14)|(0x7f); + a = a<<7; + *v = a | b; + return 4; + } + + p++; + a = a<<14; + a |= *p; + /* a: p0<<28 | p2<<14 | p4 (unmasked) */ + if (!(a&0x80)) + { + /* Values between 268435456 and 34359738367 */ + a &= SLOT_4_2_0; + b &= SLOT_4_2_0; + b = b<<7; + *v = a | b; + return 5; + } + + /* We can only reach this point when reading a corrupt database + ** file. In that case we are not in any hurry. Use the (relatively + ** slow) general-purpose sqlcipher_sqlite3GetVarint() routine to extract the + ** value. */ + { + u64 v64; + u8 n; + + p -= 4; + n = sqlcipher_sqlite3GetVarint(p, &v64); + assert( n>5 && n<=9 ); + *v = (u32)v64; + return n; + } +#endif } /* -** Attempt to reallocate p. If the reallocation fails, then free p -** and set the mallocFailed flag in the database connection. +** Return the number of bytes that will be needed to store the given +** 64-bit integer. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3DbReallocOrFree(sqlcipher_sqlite3 *db, void *p, u64 n){ - void *pNew; - pNew = sqlcipher_sqlite3DbRealloc(db, p, n); - if( !pNew ){ - sqlcipher_sqlite3DbFree(db, p); - } - return pNew; +SQLITE_PRIVATE int sqlcipher_sqlite3VarintLen(u64 v){ + int i; + for(i=1; (v >>= 7)!=0; i++){ assert( i<10 ); } + return i; } + /* -** Make a copy of a string in memory obtained from sqliteMalloc(). These -** functions call sqlcipher_sqlite3MallocRaw() directly instead of sqliteMalloc(). This -** is because when memory debugging is turned on, these two functions are -** called via macros that record the current file and line number in the -** ThreadData structure. +** Read or write a four-byte big-endian integer value. */ -SQLITE_PRIVATE char *sqlcipher_sqlite3DbStrDup(sqlcipher_sqlite3 *db, const char *z){ - char *zNew; - size_t n; - if( z==0 ){ - return 0; - } - n = strlen(z) + 1; - zNew = sqlcipher_sqlite3DbMallocRaw(db, n); - if( zNew ){ - memcpy(zNew, z, n); - } - return zNew; +SQLITE_PRIVATE u32 sqlcipher_sqlite3Get4byte(const u8 *p){ +#if SQLITE_BYTEORDER==4321 + u32 x; + memcpy(&x,p,4); + return x; +#elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 + u32 x; + memcpy(&x,p,4); + return __builtin_bswap32(x); +#elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 + u32 x; + memcpy(&x,p,4); + return _byteswap_ulong(x); +#else + testcase( p[0]&0x80 ); + return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; +#endif } -SQLITE_PRIVATE char *sqlcipher_sqlite3DbStrNDup(sqlcipher_sqlite3 *db, const char *z, u64 n){ - char *zNew; - assert( db!=0 ); - assert( z!=0 || n==0 ); - assert( (n&0x7fffffff)==n ); - zNew = z ? sqlcipher_sqlite3DbMallocRawNN(db, n+1) : 0; - if( zNew ){ - memcpy(zNew, z, (size_t)n); - zNew[n] = 0; - } - return zNew; +SQLITE_PRIVATE void sqlcipher_sqlite3Put4byte(unsigned char *p, u32 v){ +#if SQLITE_BYTEORDER==4321 + memcpy(p,&v,4); +#elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 + u32 x = __builtin_bswap32(v); + memcpy(p,&x,4); +#elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 + u32 x = _byteswap_ulong(v); + memcpy(p,&x,4); +#else + p[0] = (u8)(v>>24); + p[1] = (u8)(v>>16); + p[2] = (u8)(v>>8); + p[3] = (u8)v; +#endif } + + /* -** The text between zStart and zEnd represents a phrase within a larger -** SQL statement. Make a copy of this phrase in space obtained form -** sqlcipher_sqlite3DbMalloc(). Omit leading and trailing whitespace. +** Translate a single byte of Hex into an integer. +** This routine only works if h really is a valid hexadecimal +** character: 0..9a..fA..F */ -SQLITE_PRIVATE char *sqlcipher_sqlite3DbSpanDup(sqlcipher_sqlite3 *db, const char *zStart, const char *zEnd){ - int n; - while( sqlcipher_sqlite3Isspace(zStart[0]) ) zStart++; - n = (int)(zEnd - zStart); - while( ALWAYS(n>0) && sqlcipher_sqlite3Isspace(zStart[n-1]) ) n--; - return sqlcipher_sqlite3DbStrNDup(db, zStart, n); +SQLITE_PRIVATE u8 sqlcipher_sqlite3HexToInt(int h){ + assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') ); +#ifdef SQLITE_ASCII + h += 9*(1&(h>>6)); +#endif +#ifdef SQLITE_EBCDIC + h += 9*(1&~(h>>4)); +#endif + return (u8)(h & 0xf); } +/* BEGIN SQLCIPHER */ +#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) /* -** Free any prior content in *pz and replace it with a copy of zNew. +** Convert a BLOB literal of the form "x'hhhhhh'" into its binary +** value. Return a pointer to its binary value. Space to hold the +** binary value has been obtained from malloc and must be freed by +** the calling routine. */ -SQLITE_PRIVATE void sqlcipher_sqlite3SetString(char **pz, sqlcipher_sqlite3 *db, const char *zNew){ - sqlcipher_sqlite3DbFree(db, *pz); - *pz = sqlcipher_sqlite3DbStrDup(db, zNew); +SQLITE_PRIVATE void *sqlcipher_sqlite3HexToBlob(sqlcipher_sqlite3 *db, const char *z, int n){ + char *zBlob; + int i; + + zBlob = (char *)sqlcipher_sqlite3DbMallocRawNN(db, n/2 + 1); + n--; + if( zBlob ){ + for(i=0; imallocFailed, and also -** temporarily disable the lookaside memory allocator and interrupt -** any running VDBEs. +** Log an error that is an API call on a connection pointer that should +** not have been used. The "type" of connection pointer is given as the +** argument. The zType is a word like "NULL" or "closed" or "invalid". */ -SQLITE_PRIVATE void sqlcipher_sqlite3OomFault(sqlcipher_sqlite3 *db){ - if( db->mallocFailed==0 && db->bBenignMalloc==0 ){ - db->mallocFailed = 1; - if( db->nVdbeExec>0 ){ - AtomicStore(&db->u1.isInterrupted, 1); - } - DisableLookaside; - if( db->pParse ){ - db->pParse->rc = SQLITE_NOMEM_BKPT; - } - } +static void logBadConnection(const char *zType){ + sqlcipher_sqlite3_log(SQLITE_MISUSE, + "API call with %s database connection pointer", + zType + ); } /* -** This routine reactivates the memory allocator and clears the -** db->mallocFailed flag as necessary. +** Check to make sure we have a valid db pointer. This test is not +** foolproof but it does provide some measure of protection against +** misuse of the interface such as passing in db pointers that are +** NULL or which have been previously closed. If this routine returns +** 1 it means that the db pointer is valid and 0 if it should not be +** dereferenced for any reason. The calling function should invoke +** SQLITE_MISUSE immediately. ** -** The memory allocator is not restarted if there are running -** VDBEs. +** sqlcipher_sqlite3SafetyCheckOk() requires that the db pointer be valid for +** use. sqlcipher_sqlite3SafetyCheckSickOrOk() allows a db pointer that failed to +** open properly and is not fit for general use but which can be +** used as an argument to sqlcipher_sqlite3_errmsg() or sqlcipher_sqlite3_close(). */ -SQLITE_PRIVATE void sqlcipher_sqlite3OomClear(sqlcipher_sqlite3 *db){ - if( db->mallocFailed && db->nVdbeExec==0 ){ - db->mallocFailed = 0; - AtomicStore(&db->u1.isInterrupted, 0); - assert( db->lookaside.bDisable>0 ); - EnableLookaside; +SQLITE_PRIVATE int sqlcipher_sqlite3SafetyCheckOk(sqlcipher_sqlite3 *db){ + u8 eOpenState; + if( db==0 ){ + logBadConnection("NULL"); + return 0; + } + eOpenState = db->eOpenState; + if( eOpenState!=SQLITE_STATE_OPEN ){ + if( sqlcipher_sqlite3SafetyCheckSickOrOk(db) ){ + testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); + logBadConnection("unopened"); + } + return 0; + }else{ + return 1; + } +} +SQLITE_PRIVATE int sqlcipher_sqlite3SafetyCheckSickOrOk(sqlcipher_sqlite3 *db){ + u8 eOpenState; + eOpenState = db->eOpenState; + if( eOpenState!=SQLITE_STATE_SICK && + eOpenState!=SQLITE_STATE_OPEN && + eOpenState!=SQLITE_STATE_BUSY ){ + testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); + logBadConnection("invalid"); + return 0; + }else{ + return 1; } } /* -** Take actions at the end of an API call to deal with error codes. +** Attempt to add, substract, or multiply the 64-bit signed value iB against +** the other 64-bit signed integer at *pA and store the result in *pA. +** Return 0 on success. Or if the operation would have resulted in an +** overflow, leave *pA unchanged and return 1. */ -static SQLITE_NOINLINE int apiHandleError(sqlcipher_sqlite3 *db, int rc){ - if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ - sqlcipher_sqlite3OomClear(db); - sqlcipher_sqlite3Error(db, SQLITE_NOMEM); - return SQLITE_NOMEM_BKPT; +SQLITE_PRIVATE int sqlcipher_sqlite3AddInt64(i64 *pA, i64 iB){ +#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER) + return __builtin_add_overflow(*pA, iB, pA); +#else + i64 iA = *pA; + testcase( iA==0 ); testcase( iA==1 ); + testcase( iB==-1 ); testcase( iB==0 ); + if( iB>=0 ){ + testcase( iA>0 && LARGEST_INT64 - iA == iB ); + testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 ); + if( iA>0 && LARGEST_INT64 - iA < iB ) return 1; + }else{ + testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 ); + testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 ); + if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1; } - return rc & db->errMask; + *pA += iB; + return 0; +#endif +} +SQLITE_PRIVATE int sqlcipher_sqlite3SubInt64(i64 *pA, i64 iB){ +#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER) + return __builtin_sub_overflow(*pA, iB, pA); +#else + testcase( iB==SMALLEST_INT64+1 ); + if( iB==SMALLEST_INT64 ){ + testcase( (*pA)==(-1) ); testcase( (*pA)==0 ); + if( (*pA)>=0 ) return 1; + *pA -= iB; + return 0; + }else{ + return sqlcipher_sqlite3AddInt64(pA, -iB); + } +#endif +} +SQLITE_PRIVATE int sqlcipher_sqlite3MulInt64(i64 *pA, i64 iB){ +#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER) + return __builtin_mul_overflow(*pA, iB, pA); +#else + i64 iA = *pA; + if( iB>0 ){ + if( iA>LARGEST_INT64/iB ) return 1; + if( iA0 ){ + if( iBLARGEST_INT64/-iB ) return 1; + } + } + *pA = iA*iB; + return 0; +#endif } /* -** This function must be called before exiting any API function (i.e. -** returning control to the user) that has called sqlcipher_sqlite3_malloc or -** sqlcipher_sqlite3_realloc. -** -** The returned value is normally a copy of the second argument to this -** function. However, if a malloc() failure has occurred since the previous -** invocation SQLITE_NOMEM is returned instead. -** -** If an OOM as occurred, then the connection error-code (the value -** returned by sqlcipher_sqlite3_errcode()) is set to SQLITE_NOMEM. +** Compute the absolute value of a 32-bit signed integer, of possible. Or +** if the integer has a value of -2147483648, return +2147483647 */ -SQLITE_PRIVATE int sqlcipher_sqlite3ApiExit(sqlcipher_sqlite3* db, int rc){ - /* If the db handle must hold the connection handle mutex here. - ** Otherwise the read (and possible write) of db->mallocFailed - ** is unsafe, as is the call to sqlcipher_sqlite3Error(). - */ - assert( db!=0 ); - assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - if( db->mallocFailed || rc ){ - return apiHandleError(db, rc); - } - return rc & db->errMask; +SQLITE_PRIVATE int sqlcipher_sqlite3AbsInt32(int x){ + if( x>=0 ) return x; + if( x==(int)0x80000000 ) return 0x7fffffff; + return -x; } -/************** End of malloc.c **********************************************/ -/************** Begin file printf.c ******************************************/ +#ifdef SQLITE_ENABLE_8_3_NAMES /* -** The "printf" code that follows dates from the 1980's. It is in -** the public domain. +** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database +** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and +** if filename in z[] has a suffix (a.k.a. "extension") that is longer than +** three characters, then shorten the suffix on z[] to be the last three +** characters of the original suffix. ** -************************************************************************** +** If SQLITE_ENABLE_8_3_NAMES is set to 2 at compile-time, then always +** do the suffix shortening regardless of URI parameter. ** -** This file contains code for a set of "printf"-like routines. These -** routines format strings much like the printf() from the standard C -** library, though the implementation here has enhancements to support -** SQLite. -*/ -/* #include "sqliteInt.h" */ - -/* -** Conversion types fall into various categories as defined by the -** following enumeration. +** Examples: +** +** test.db-journal => test.nal +** test.db-wal => test.wal +** test.db-shm => test.shm +** test.db-mj7f3319fa => test.9fa */ -#define etRADIX 0 /* non-decimal integer types. %x %o */ -#define etFLOAT 1 /* Floating point. %f */ -#define etEXP 2 /* Exponentional notation. %e and %E */ -#define etGENERIC 3 /* Floating or exponential, depending on exponent. %g */ -#define etSIZE 4 /* Return number of characters processed so far. %n */ -#define etSTRING 5 /* Strings. %s */ -#define etDYNSTRING 6 /* Dynamically allocated strings. %z */ -#define etPERCENT 7 /* Percent symbol. %% */ -#define etCHARX 8 /* Characters. %c */ -/* The rest are extensions, not normally found in printf() */ -#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */ -#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '', - NULL pointers replaced by SQL NULL. %Q */ -#define etTOKEN 11 /* a pointer to a Token structure */ -#define etSRCLIST 12 /* a pointer to a SrcList */ -#define etPOINTER 13 /* The %p conversion */ -#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */ -#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ -#define etDECIMAL 16 /* %d or %u, but not %x, %o */ - -#define etINVALID 17 /* Any unrecognized conversion type */ - +SQLITE_PRIVATE void sqlcipher_sqlite3FileSuffix3(const char *zBaseFilename, char *z){ +#if SQLITE_ENABLE_8_3_NAMES<2 + if( sqlcipher_sqlite3_uri_boolean(zBaseFilename, "8_3_names", 0) ) +#endif + { + int i, sz; + sz = sqlcipher_sqlite3Strlen30(z); + for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} + if( z[i]=='.' && ALWAYS(sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4); + } +} +#endif /* -** An "etByte" is an 8-bit unsigned value. +** Find (an approximate) sum of two LogEst values. This computation is +** not a simple "+" operator because LogEst is stored as a logarithmic +** value. +** */ -typedef unsigned char etByte; +SQLITE_PRIVATE LogEst sqlcipher_sqlite3LogEstAdd(LogEst a, LogEst b){ + static const unsigned char x[] = { + 10, 10, /* 0,1 */ + 9, 9, /* 2,3 */ + 8, 8, /* 4,5 */ + 7, 7, 7, /* 6,7,8 */ + 6, 6, 6, /* 9,10,11 */ + 5, 5, 5, /* 12-14 */ + 4, 4, 4, 4, /* 15-18 */ + 3, 3, 3, 3, 3, 3, /* 19-24 */ + 2, 2, 2, 2, 2, 2, 2, /* 25-31 */ + }; + if( a>=b ){ + if( a>b+49 ) return a; + if( a>b+31 ) return a+1; + return a+x[a-b]; + }else{ + if( b>a+49 ) return b; + if( b>a+31 ) return b+1; + return b+x[b-a]; + } +} /* -** Each builtin conversion character (ex: the 'd' in "%d") is described -** by an instance of the following structure +** Convert an integer into a LogEst. In other words, compute an +** approximation for 10*log2(x). */ -typedef struct et_info { /* Information about each format field */ - char fmttype; /* The format field code letter */ - etByte base; /* The base for radix conversion */ - etByte flags; /* One or more of FLAG_ constants below */ - etByte type; /* Conversion paradigm */ - etByte charset; /* Offset into aDigits[] of the digits string */ - etByte prefix; /* Offset into aPrefix[] of the prefix string */ -} et_info; +SQLITE_PRIVATE LogEst sqlcipher_sqlite3LogEst(u64 x){ + static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 }; + LogEst y = 40; + if( x<8 ){ + if( x<2 ) return 0; + while( x<8 ){ y -= 10; x <<= 1; } + }else{ +#if GCC_VERSION>=5004000 + int i = 60 - __builtin_clzll(x); + y += i*10; + x >>= i; +#else + while( x>255 ){ y += 40; x >>= 4; } /*OPTIMIZATION-IF-TRUE*/ + while( x>15 ){ y += 10; x >>= 1; } +#endif + } + return a[x&7] + y - 10; +} /* -** Allowed values for et_info.flags +** Convert a double into a LogEst +** In other words, compute an approximation for 10*log2(x). */ -#define FLAG_SIGNED 1 /* True if the value to convert is signed */ -#define FLAG_STRING 4 /* Allow infinite precision */ - +SQLITE_PRIVATE LogEst sqlcipher_sqlite3LogEstFromDouble(double x){ + u64 a; + LogEst e; + assert( sizeof(x)==8 && sizeof(a)==8 ); + if( x<=1 ) return 0; + if( x<=2000000000 ) return sqlcipher_sqlite3LogEst((u64)x); + memcpy(&a, &x, 8); + e = (a>>52) - 1022; + return e*10; +} /* -** The following table is searched linearly, so it is good to put the -** most frequently used conversion types first. +** Convert a LogEst into an integer. */ -static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; -static const char aPrefix[] = "-x0\000X0"; -static const et_info fmtinfo[] = { - { 'd', 10, 1, etDECIMAL, 0, 0 }, - { 's', 0, 4, etSTRING, 0, 0 }, - { 'g', 0, 1, etGENERIC, 30, 0 }, - { 'z', 0, 4, etDYNSTRING, 0, 0 }, - { 'q', 0, 4, etSQLESCAPE, 0, 0 }, - { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, - { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, - { 'c', 0, 0, etCHARX, 0, 0 }, - { 'o', 8, 0, etRADIX, 0, 2 }, - { 'u', 10, 0, etDECIMAL, 0, 0 }, - { 'x', 16, 0, etRADIX, 16, 1 }, - { 'X', 16, 0, etRADIX, 0, 4 }, -#ifndef SQLITE_OMIT_FLOATING_POINT - { 'f', 0, 1, etFLOAT, 0, 0 }, - { 'e', 0, 1, etEXP, 30, 0 }, - { 'E', 0, 1, etEXP, 14, 0 }, - { 'G', 0, 1, etGENERIC, 14, 0 }, -#endif - { 'i', 10, 1, etDECIMAL, 0, 0 }, - { 'n', 0, 0, etSIZE, 0, 0 }, - { '%', 0, 0, etPERCENT, 0, 0 }, - { 'p', 16, 0, etPOINTER, 0, 1 }, - - /* All the rest are undocumented and are for internal use only */ - { 'T', 0, 0, etTOKEN, 0, 0 }, - { 'S', 0, 0, etSRCLIST, 0, 0 }, - { 'r', 10, 1, etORDINAL, 0, 0 }, -}; - -/* Floating point constants used for rounding */ -static const double arRound[] = { - 5.0e-01, 5.0e-02, 5.0e-03, 5.0e-04, 5.0e-05, - 5.0e-06, 5.0e-07, 5.0e-08, 5.0e-09, 5.0e-10, -}; +SQLITE_PRIVATE u64 sqlcipher_sqlite3LogEstToInt(LogEst x){ + u64 n; + n = x%10; + x /= 10; + if( n>=5 ) n -= 2; + else if( n>=1 ) n -= 1; + if( x>60 ) return (u64)LARGEST_INT64; + return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x); +} /* -** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point -** conversions will work. -*/ -#ifndef SQLITE_OMIT_FLOATING_POINT -/* -** "*val" is a double such that 0.1 <= *val < 10.0 -** Return the ascii code for the leading digit of *val, then -** multiply "*val" by 10.0 to renormalize. +** Add a new name/number pair to a VList. This might require that the +** VList object be reallocated, so return the new VList. If an OOM +** error occurs, the original VList returned and the +** db->mallocFailed flag is set. ** -** Example: -** input: *val = 3.14159 -** output: *val = 1.4159 function return = '3' +** A VList is really just an array of integers. To destroy a VList, +** simply pass it to sqlcipher_sqlite3DbFree(). ** -** The counter *cnt is incremented each time. After counter exceeds -** 16 (the number of significant digits in a 64-bit float) '0' is -** always returned. +** The first integer is the number of integers allocated for the whole +** VList. The second integer is the number of integers actually used. +** Each name/number pair is encoded by subsequent groups of 3 or more +** integers. +** +** Each name/number pair starts with two integers which are the numeric +** value for the pair and the size of the name/number pair, respectively. +** The text name overlays one or more following integers. The text name +** is always zero-terminated. +** +** Conceptually: +** +** struct VList { +** int nAlloc; // Number of allocated slots +** int nUsed; // Number of used slots +** struct VListEntry { +** int iValue; // Value for this entry +** int nSlot; // Slots used by this entry +** // ... variable name goes here +** } a[0]; +** } +** +** During code generation, pointers to the variable names within the +** VList are taken. When that happens, nAlloc is set to zero as an +** indication that the VList may never again be enlarged, since the +** accompanying realloc() would invalidate the pointers. */ -static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ - int digit; - LONGDOUBLE_TYPE d; - if( (*cnt)<=0 ) return '0'; - (*cnt)--; - digit = (int)*val; - d = digit; - digit += '0'; - *val = (*val - d)*10.0; - return (char)digit; +SQLITE_PRIVATE VList *sqlcipher_sqlite3VListAdd( + sqlcipher_sqlite3 *db, /* The database connection used for malloc() */ + VList *pIn, /* The input VList. Might be NULL */ + const char *zName, /* Name of symbol to add */ + int nName, /* Bytes of text in zName */ + int iVal /* Value to associate with zName */ +){ + int nInt; /* number of sizeof(int) objects needed for zName */ + char *z; /* Pointer to where zName will be stored */ + int i; /* Index in pIn[] where zName is stored */ + + nInt = nName/4 + 3; + assert( pIn==0 || pIn[0]>=3 ); /* Verify ok to add new elements */ + if( pIn==0 || pIn[1]+nInt > pIn[0] ){ + /* Enlarge the allocation */ + sqlcipher_sqlite3_int64 nAlloc = (pIn ? 2*(sqlcipher_sqlite3_int64)pIn[0] : 10) + nInt; + VList *pOut = sqlcipher_sqlite3DbRealloc(db, pIn, nAlloc*sizeof(int)); + if( pOut==0 ) return pIn; + if( pIn==0 ) pOut[1] = 2; + pIn = pOut; + pIn[0] = nAlloc; + } + i = pIn[1]; + pIn[i] = iVal; + pIn[i+1] = nInt; + z = (char*)&pIn[i+2]; + pIn[1] = i+nInt; + assert( pIn[1]<=pIn[0] ); + memcpy(z, zName, nName); + z[nName] = 0; + return pIn; } -#endif /* SQLITE_OMIT_FLOATING_POINT */ /* -** Set the StrAccum object to an error mode. +** Return a pointer to the name of a variable in the given VList that +** has the value iVal. Or return a NULL if there is no such variable in +** the list */ -static void setStrAccumError(StrAccum *p, u8 eError){ - assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG ); - p->accError = eError; - if( p->mxAlloc ) sqlcipher_sqlite3_str_reset(p); - if( eError==SQLITE_TOOBIG ) sqlcipher_sqlite3ErrorToParser(p->db, eError); +SQLITE_PRIVATE const char *sqlcipher_sqlite3VListNumToName(VList *pIn, int iVal){ + int i, mx; + if( pIn==0 ) return 0; + mx = pIn[1]; + i = 2; + do{ + if( pIn[i]==iVal ) return (char*)&pIn[i+2]; + i += pIn[i+1]; + }while( inArg<=p->nUsed ) return 0; - return sqlcipher_sqlite3_value_int64(p->apArg[p->nUsed++]); -} -static double getDoubleArg(PrintfArguments *p){ - if( p->nArg<=p->nUsed ) return 0.0; - return sqlcipher_sqlite3_value_double(p->apArg[p->nUsed++]); -} -static char *getTextArg(PrintfArguments *p){ - if( p->nArg<=p->nUsed ) return 0; - return (char*)sqlcipher_sqlite3_value_text(p->apArg[p->nUsed++]); +SQLITE_PRIVATE int sqlcipher_sqlite3VListNameToNum(VList *pIn, const char *zName, int nName){ + int i, mx; + if( pIn==0 ) return 0; + mx = pIn[1]; + i = 2; + do{ + const char *z = (const char*)&pIn[i+2]; + if( strncmp(z,zName,nName)==0 && z[nName]==0 ) return pIn[i]; + i += pIn[i+1]; + }while( iaccError ) return 0; - if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){ - setStrAccumError(pAccum, SQLITE_TOOBIG); - return 0; - } - z = sqlcipher_sqlite3DbMallocRaw(pAccum->db, n); - if( z==0 ){ - setStrAccumError(pAccum, SQLITE_NOMEM); - } - return z; -} +/* #include "sqliteInt.h" */ +/* #include */ -/* -** On machines with a small stack size, you can redefine the -** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired. +/* Turn bulk memory into a hash table object by initializing the +** fields of the Hash structure. +** +** "pNew" is a pointer to the hash table that is to be initialized. */ -#ifndef SQLITE_PRINT_BUF_SIZE -# define SQLITE_PRINT_BUF_SIZE 70 -#endif -#define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */ +SQLITE_PRIVATE void sqlcipher_sqlite3HashInit(Hash *pNew){ + assert( pNew!=0 ); + pNew->first = 0; + pNew->count = 0; + pNew->htsize = 0; + pNew->ht = 0; +} -/* -** Hard limit on the precision of floating-point conversions. +/* Remove all entries from a hash table. Reclaim all memory. +** Call this routine to delete a hash table or to reset a hash table +** to the empty state. */ -#ifndef SQLITE_PRINTF_PRECISION_LIMIT -# define SQLITE_FP_PRECISION_LIMIT 100000000 -#endif +SQLITE_PRIVATE void sqlcipher_sqlite3HashClear(Hash *pH){ + HashElem *elem; /* For looping over all elements of the table */ + + assert( pH!=0 ); + elem = pH->first; + pH->first = 0; + sqlcipher_sqlite3_free(pH->ht); + pH->ht = 0; + pH->htsize = 0; + while( elem ){ + HashElem *next_elem = elem->next; + sqlcipher_sqlite3_free(elem); + elem = next_elem; + } + pH->count = 0; +} /* -** Render a string given by "fmt" into the StrAccum object. +** The hashing function. */ -SQLITE_API void sqlcipher_sqlite3_str_vappendf( - sqlcipher_sqlite3_str *pAccum, /* Accumulate results here */ - const char *fmt, /* Format string */ - va_list ap /* arguments */ -){ - int c; /* Next character in the format string */ - char *bufpt; /* Pointer to the conversion buffer */ - int precision; /* Precision of the current field */ - int length; /* Length of the field */ - int idx; /* A general purpose loop counter */ - int width; /* Width of the current field */ - etByte flag_leftjustify; /* True if "-" flag is present */ - etByte flag_prefix; /* '+' or ' ' or 0 for prefix */ - etByte flag_alternateform; /* True if "#" flag is present */ - etByte flag_altform2; /* True if "!" flag is present */ - etByte flag_zeropad; /* True if field width constant starts with zero */ - etByte flag_long; /* 1 for the "l" flag, 2 for "ll", 0 by default */ - etByte done; /* Loop termination flag */ - etByte cThousand; /* Thousands separator for %d and %u */ - etByte xtype = etINVALID; /* Conversion paradigm */ - u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ - char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ - sqlite_uint64 longvalue; /* Value for integer types */ - LONGDOUBLE_TYPE realvalue; /* Value for real types */ - const et_info *infop; /* Pointer to the appropriate info structure */ - char *zOut; /* Rendering buffer */ - int nOut; /* Size of the rendering buffer */ - char *zExtra = 0; /* Malloced memory used by some conversion */ -#ifndef SQLITE_OMIT_FLOATING_POINT - int exp, e2; /* exponent of real numbers */ - int nsd; /* Number of significant digits returned */ - double rounder; /* Used for rounding floating point values */ - etByte flag_dp; /* True if decimal point should be shown */ - etByte flag_rtz; /* True if trailing zeros should be removed */ -#endif - PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ - char buf[etBUFSIZE]; /* Conversion buffer */ +static unsigned int strHash(const char *z){ + unsigned int h = 0; + unsigned char c; + while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/ + /* Knuth multiplicative hashing. (Sorting & Searching, p. 510). + ** 0x9e3779b1 is 2654435761 which is the closest prime number to + ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */ + h += sqlcipher_sqlite3UpperToLower[c]; + h *= 0x9e3779b1; + } + return h; +} - /* pAccum never starts out with an empty buffer that was obtained from - ** malloc(). This precondition is required by the mprintf("%z...") - ** optimization. */ - assert( pAccum->nChar>0 || (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 ); - bufpt = 0; - if( (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC)!=0 ){ - pArgList = va_arg(ap, PrintfArguments*); - bArgList = 1; +/* Link pNew element into the hash table pH. If pEntry!=0 then also +** insert pNew into the pEntry hash bucket. +*/ +static void insertElement( + Hash *pH, /* The complete hash table */ + struct _ht *pEntry, /* The entry into which pNew is inserted */ + HashElem *pNew /* The element to be inserted */ +){ + HashElem *pHead; /* First element already in pEntry */ + if( pEntry ){ + pHead = pEntry->count ? pEntry->chain : 0; + pEntry->count++; + pEntry->chain = pNew; }else{ - bArgList = 0; + pHead = 0; } - for(; (c=(*fmt))!=0; ++fmt){ - if( c!='%' ){ - bufpt = (char *)fmt; -#if HAVE_STRCHRNUL - fmt = strchrnul(fmt, '%'); -#else - do{ fmt++; }while( *fmt && *fmt != '%' ); -#endif - sqlcipher_sqlite3_str_append(pAccum, bufpt, (int)(fmt - bufpt)); - if( *fmt==0 ) break; - } - if( (c=(*++fmt))==0 ){ - sqlcipher_sqlite3_str_append(pAccum, "%", 1); - break; - } - /* Find out what flags are present */ - flag_leftjustify = flag_prefix = cThousand = - flag_alternateform = flag_altform2 = flag_zeropad = 0; - done = 0; - width = 0; - flag_long = 0; - precision = -1; - do{ - switch( c ){ - case '-': flag_leftjustify = 1; break; - case '+': flag_prefix = '+'; break; - case ' ': flag_prefix = ' '; break; - case '#': flag_alternateform = 1; break; - case '!': flag_altform2 = 1; break; - case '0': flag_zeropad = 1; break; - case ',': cThousand = ','; break; - default: done = 1; break; - case 'l': { - flag_long = 1; - c = *++fmt; - if( c=='l' ){ - c = *++fmt; - flag_long = 2; - } - done = 1; - break; - } - case '1': case '2': case '3': case '4': case '5': - case '6': case '7': case '8': case '9': { - unsigned wx = c - '0'; - while( (c = *++fmt)>='0' && c<='9' ){ - wx = wx*10 + c - '0'; - } - testcase( wx>0x7fffffff ); - width = wx & 0x7fffffff; -#ifdef SQLITE_PRINTF_PRECISION_LIMIT - if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ - width = SQLITE_PRINTF_PRECISION_LIMIT; - } -#endif - if( c!='.' && c!='l' ){ - done = 1; - }else{ - fmt--; - } - break; - } - case '*': { - if( bArgList ){ - width = (int)getIntArg(pArgList); - }else{ - width = va_arg(ap,int); - } - if( width<0 ){ - flag_leftjustify = 1; - width = width >= -2147483647 ? -width : 0; - } -#ifdef SQLITE_PRINTF_PRECISION_LIMIT - if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ - width = SQLITE_PRINTF_PRECISION_LIMIT; - } -#endif - if( (c = fmt[1])!='.' && c!='l' ){ - c = *++fmt; - done = 1; - } - break; - } - case '.': { - c = *++fmt; - if( c=='*' ){ - if( bArgList ){ - precision = (int)getIntArg(pArgList); - }else{ - precision = va_arg(ap,int); - } - if( precision<0 ){ - precision = precision >= -2147483647 ? -precision : -1; - } - c = *++fmt; - }else{ - unsigned px = 0; - while( c>='0' && c<='9' ){ - px = px*10 + c - '0'; - c = *++fmt; - } - testcase( px>0x7fffffff ); - precision = px & 0x7fffffff; - } -#ifdef SQLITE_PRINTF_PRECISION_LIMIT - if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){ - precision = SQLITE_PRINTF_PRECISION_LIMIT; - } -#endif - if( c=='l' ){ - --fmt; - }else{ - done = 1; - } - break; - } - } - }while( !done && (c=(*++fmt))!=0 ); + if( pHead ){ + pNew->next = pHead; + pNew->prev = pHead->prev; + if( pHead->prev ){ pHead->prev->next = pNew; } + else { pH->first = pNew; } + pHead->prev = pNew; + }else{ + pNew->next = pH->first; + if( pH->first ){ pH->first->prev = pNew; } + pNew->prev = 0; + pH->first = pNew; + } +} - /* Fetch the info entry for the field */ - infop = &fmtinfo[0]; - xtype = etINVALID; - for(idx=0; idxtype; - break; - } - } - /* - ** At this point, variables are initialized as follows: - ** - ** flag_alternateform TRUE if a '#' is present. - ** flag_altform2 TRUE if a '!' is present. - ** flag_prefix '+' or ' ' or zero - ** flag_leftjustify TRUE if a '-' is present or if the - ** field width was negative. - ** flag_zeropad TRUE if the width began with 0. - ** flag_long 1 for "l", 2 for "ll" - ** width The specified field width. This is - ** always non-negative. Zero is the default. - ** precision The specified precision. The default - ** is -1. - ** xtype The class of the conversion. - ** infop Pointer to the appropriate info struct. - */ - assert( width>=0 ); - assert( precision>=(-1) ); - switch( xtype ){ - case etPOINTER: - flag_long = sizeof(char*)==sizeof(i64) ? 2 : - sizeof(char*)==sizeof(long int) ? 1 : 0; - /* no break */ deliberate_fall_through - case etORDINAL: - case etRADIX: - cThousand = 0; - /* no break */ deliberate_fall_through - case etDECIMAL: - if( infop->flags & FLAG_SIGNED ){ - i64 v; - if( bArgList ){ - v = getIntArg(pArgList); - }else if( flag_long ){ - if( flag_long==2 ){ - v = va_arg(ap,i64) ; - }else{ - v = va_arg(ap,long int); - } - }else{ - v = va_arg(ap,int); - } - if( v<0 ){ - testcase( v==SMALLEST_INT64 ); - testcase( v==(-1) ); - longvalue = ~v; - longvalue++; - prefix = '-'; - }else{ - longvalue = v; - prefix = flag_prefix; - } - }else{ - if( bArgList ){ - longvalue = (u64)getIntArg(pArgList); - }else if( flag_long ){ - if( flag_long==2 ){ - longvalue = va_arg(ap,u64); - }else{ - longvalue = va_arg(ap,unsigned long int); - } - }else{ - longvalue = va_arg(ap,unsigned int); - } - prefix = 0; - } - if( longvalue==0 ) flag_alternateform = 0; - if( flag_zeropad && precision=4 || (longvalue/10)%10==1 ){ - x = 0; - } - *(--bufpt) = zOrd[x*2+1]; - *(--bufpt) = zOrd[x*2]; - } - { - const char *cset = &aDigits[infop->charset]; - u8 base = infop->base; - do{ /* Convert to ascii */ - *(--bufpt) = cset[longvalue%base]; - longvalue = longvalue/base; - }while( longvalue>0 ); - } - length = (int)(&zOut[nOut-1]-bufpt); - while( precision>length ){ - *(--bufpt) = '0'; /* Zero pad */ - length++; - } - if( cThousand ){ - int nn = (length - 1)/3; /* Number of "," to insert */ - int ix = (length - 1)%3 + 1; - bufpt -= nn; - for(idx=0; nn>0; idx++){ - bufpt[idx] = bufpt[idx+nn]; - ix--; - if( ix==0 ){ - bufpt[++idx] = cThousand; - nn--; - ix = 3; - } - } - } - if( prefix ) *(--bufpt) = prefix; /* Add sign */ - if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ - const char *pre; - char x; - pre = &aPrefix[infop->prefix]; - for(; (x=(*pre))!=0; pre++) *(--bufpt) = x; - } - length = (int)(&zOut[nOut-1]-bufpt); - break; - case etFLOAT: - case etEXP: - case etGENERIC: - if( bArgList ){ - realvalue = getDoubleArg(pArgList); - }else{ - realvalue = va_arg(ap,double); - } -#ifdef SQLITE_OMIT_FLOATING_POINT - length = 0; -#else - if( precision<0 ) precision = 6; /* Set default precision */ -#ifdef SQLITE_FP_PRECISION_LIMIT - if( precision>SQLITE_FP_PRECISION_LIMIT ){ - precision = SQLITE_FP_PRECISION_LIMIT; - } +/* Resize the hash table so that it cantains "new_size" buckets. +** +** The hash table might fail to resize if sqlcipher_sqlite3_malloc() fails or +** if the new size is the same as the prior size. +** Return TRUE if the resize occurs and false if not. +*/ +static int rehash(Hash *pH, unsigned int new_size){ + struct _ht *new_ht; /* The new hash table */ + HashElem *elem, *next_elem; /* For looping over existing elements */ + +#if SQLITE_MALLOC_SOFT_LIMIT>0 + if( new_size*sizeof(struct _ht)>SQLITE_MALLOC_SOFT_LIMIT ){ + new_size = SQLITE_MALLOC_SOFT_LIMIT/sizeof(struct _ht); + } + if( new_size==pH->htsize ) return 0; #endif - if( realvalue<0.0 ){ - realvalue = -realvalue; - prefix = '-'; - }else{ - prefix = flag_prefix; - } - if( xtype==etGENERIC && precision>0 ) precision--; - testcase( precision>0xfff ); - idx = precision & 0xfff; - rounder = arRound[idx%10]; - while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; } - if( xtype==etFLOAT ){ - double rx = (double)realvalue; - sqlcipher_sqlite3_uint64 u; - int ex; - memcpy(&u, &rx, sizeof(u)); - ex = -1023 + (int)((u>>52)&0x7ff); - if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16; - realvalue += rounder; - } - /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ - exp = 0; - if( sqlcipher_sqlite3IsNaN((double)realvalue) ){ - bufpt = "NaN"; - length = 3; - break; - } - if( realvalue>0.0 ){ - LONGDOUBLE_TYPE scale = 1.0; - while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;} - while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; } - while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; } - realvalue /= scale; - while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; } - while( realvalue<1.0 ){ realvalue *= 10.0; exp--; } - if( exp>350 ){ - bufpt = buf; - buf[0] = prefix; - memcpy(buf+(prefix!=0),"Inf",4); - length = 3+(prefix!=0); - break; - } - } - bufpt = buf; - /* - ** If the field type is etGENERIC, then convert to either etEXP - ** or etFLOAT, as appropriate. - */ - if( xtype!=etFLOAT ){ - realvalue += rounder; - if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } - } - if( xtype==etGENERIC ){ - flag_rtz = !flag_alternateform; - if( exp<-4 || exp>precision ){ - xtype = etEXP; - }else{ - precision = precision - exp; - xtype = etFLOAT; - } - }else{ - flag_rtz = flag_altform2; - } - if( xtype==etEXP ){ - e2 = 0; - }else{ - e2 = exp; - } - { - i64 szBufNeeded; /* Size of a temporary buffer needed */ - szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; - if( szBufNeeded > etBUFSIZE ){ - bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); - if( bufpt==0 ) return; - } - } - zOut = bufpt; - nsd = 16 + flag_altform2*10; - flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2; - /* The sign in front of the number */ - if( prefix ){ - *(bufpt++) = prefix; - } - /* Digits prior to the decimal point */ - if( e2<0 ){ - *(bufpt++) = '0'; - }else{ - for(; e2>=0; e2--){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); - } - } - /* The decimal point */ - if( flag_dp ){ - *(bufpt++) = '.'; - } - /* "0" digits after the decimal point but before the first - ** significant digit of the number */ - for(e2++; e2<0; precision--, e2++){ - assert( precision>0 ); - *(bufpt++) = '0'; - } - /* Significant digits after the decimal point */ - while( (precision--)>0 ){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); - } - /* Remove trailing zeros and the "." if no digits follow the "." */ - if( flag_rtz && flag_dp ){ - while( bufpt[-1]=='0' ) *(--bufpt) = 0; - assert( bufpt>zOut ); - if( bufpt[-1]=='.' ){ - if( flag_altform2 ){ - *(bufpt++) = '0'; - }else{ - *(--bufpt) = 0; - } - } - } - /* Add the "eNNN" suffix */ - if( xtype==etEXP ){ - *(bufpt++) = aDigits[infop->charset]; - if( exp<0 ){ - *(bufpt++) = '-'; exp = -exp; - }else{ - *(bufpt++) = '+'; - } - if( exp>=100 ){ - *(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */ - exp %= 100; - } - *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */ - *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */ - } - *bufpt = 0; - /* The converted number is in buf[] and zero terminated. Output it. - ** Note that the number is in the usual order, not reversed as with - ** integer conversions. */ - length = (int)(bufpt-zOut); - bufpt = zOut; + /* The inability to allocates space for a larger hash table is + ** a performance hit but it is not a fatal error. So mark the + ** allocation as a benign. Use sqlcipher_sqlite3Malloc()/memset(0) instead of + ** sqlcipher_sqlite3MallocZero() to make the allocation, as sqlcipher_sqlite3MallocZero() + ** only zeroes the requested number of bytes whereas this module will + ** use the actual amount of space allocated for the hash table (which + ** may be larger than the requested amount). + */ + sqlcipher_sqlite3BeginBenignMalloc(); + new_ht = (struct _ht *)sqlcipher_sqlite3Malloc( new_size*sizeof(struct _ht) ); + sqlcipher_sqlite3EndBenignMalloc(); - /* Special case: Add leading zeros if the flag_zeropad flag is - ** set and we are not left justified */ - if( flag_zeropad && !flag_leftjustify && length < width){ - int i; - int nPad = width - length; - for(i=width; i>=nPad; i--){ - bufpt[i] = bufpt[i-nPad]; - } - i = prefix!=0; - while( nPad-- ) bufpt[i++] = '0'; - length = width; - } -#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */ - break; - case etSIZE: - if( !bArgList ){ - *(va_arg(ap,int*)) = pAccum->nChar; - } - length = width = 0; - break; - case etPERCENT: - buf[0] = '%'; - bufpt = buf; - length = 1; - break; - case etCHARX: - if( bArgList ){ - bufpt = getTextArg(pArgList); - length = 1; - if( bufpt ){ - buf[0] = c = *(bufpt++); - if( (c&0xc0)==0xc0 ){ - while( length<4 && (bufpt[0]&0xc0)==0x80 ){ - buf[length++] = *(bufpt++); - } - } - }else{ - buf[0] = 0; - } - }else{ - unsigned int ch = va_arg(ap,unsigned int); - if( ch<0x00080 ){ - buf[0] = ch & 0xff; - length = 1; - }else if( ch<0x00800 ){ - buf[0] = 0xc0 + (u8)((ch>>6)&0x1f); - buf[1] = 0x80 + (u8)(ch & 0x3f); - length = 2; - }else if( ch<0x10000 ){ - buf[0] = 0xe0 + (u8)((ch>>12)&0x0f); - buf[1] = 0x80 + (u8)((ch>>6) & 0x3f); - buf[2] = 0x80 + (u8)(ch & 0x3f); - length = 3; - }else{ - buf[0] = 0xf0 + (u8)((ch>>18) & 0x07); - buf[1] = 0x80 + (u8)((ch>>12) & 0x3f); - buf[2] = 0x80 + (u8)((ch>>6) & 0x3f); - buf[3] = 0x80 + (u8)(ch & 0x3f); - length = 4; - } - } - if( precision>1 ){ - width -= precision-1; - if( width>1 && !flag_leftjustify ){ - sqlcipher_sqlite3_str_appendchar(pAccum, width-1, ' '); - width = 0; - } - while( precision-- > 1 ){ - sqlcipher_sqlite3_str_append(pAccum, buf, length); - } - } - bufpt = buf; - flag_altform2 = 1; - goto adjust_width_for_utf8; - case etSTRING: - case etDYNSTRING: - if( bArgList ){ - bufpt = getTextArg(pArgList); - xtype = etSTRING; - }else{ - bufpt = va_arg(ap,char*); - } - if( bufpt==0 ){ - bufpt = ""; - }else if( xtype==etDYNSTRING ){ - if( pAccum->nChar==0 - && pAccum->mxAlloc - && width==0 - && precision<0 - && pAccum->accError==0 - ){ - /* Special optimization for sqlcipher_sqlite3_mprintf("%z..."): - ** Extend an existing memory allocation rather than creating - ** a new one. */ - assert( (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 ); - pAccum->zText = bufpt; - pAccum->nAlloc = sqlcipher_sqlite3DbMallocSize(pAccum->db, bufpt); - pAccum->nChar = 0x7fffffff & (int)strlen(bufpt); - pAccum->printfFlags |= SQLITE_PRINTF_MALLOCED; - length = 0; - break; - } - zExtra = bufpt; - } - if( precision>=0 ){ - if( flag_altform2 ){ - /* Set length to the number of bytes needed in order to display - ** precision characters */ - unsigned char *z = (unsigned char*)bufpt; - while( precision-- > 0 && z[0] ){ - SQLITE_SKIP_UTF8(z); - } - length = (int)(z - (unsigned char*)bufpt); - }else{ - for(length=0; length0 ){ - /* Adjust width to account for extra bytes in UTF-8 characters */ - int ii = length - 1; - while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++; - } - break; - case etSQLESCAPE: /* %q: Escape ' characters */ - case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */ - case etSQLESCAPE3: { /* %w: Escape " characters */ - int i, j, k, n, isnull; - int needQuote; - char ch; - char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ - char *escarg; + if( new_ht==0 ) return 0; + sqlcipher_sqlite3_free(pH->ht); + pH->ht = new_ht; + pH->htsize = new_size = sqlcipher_sqlite3MallocSize(new_ht)/sizeof(struct _ht); + memset(new_ht, 0, new_size*sizeof(struct _ht)); + for(elem=pH->first, pH->first=0; elem; elem = next_elem){ + unsigned int h = strHash(elem->pKey) % new_size; + next_elem = elem->next; + insertElement(pH, &new_ht[h], elem); + } + return 1; +} - if( bArgList ){ - escarg = getTextArg(pArgList); - }else{ - escarg = va_arg(ap,char*); - } - isnull = escarg==0; - if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); - /* For %q, %Q, and %w, the precision is the number of bytes (or - ** characters if the ! flags is present) to use from the input. - ** Because of the extra quoting characters inserted, the number - ** of output characters may be larger than the precision. - */ - k = precision; - for(i=n=0; k!=0 && (ch=escarg[i])!=0; i++, k--){ - if( ch==q ) n++; - if( flag_altform2 && (ch&0xc0)==0xc0 ){ - while( (escarg[i+1]&0xc0)==0x80 ){ i++; } - } - } - needQuote = !isnull && xtype==etSQLESCAPE2; - n += i + 3; - if( n>etBUFSIZE ){ - bufpt = zExtra = printfTempBuf(pAccum, n); - if( bufpt==0 ) return; - }else{ - bufpt = buf; - } - j = 0; - if( needQuote ) bufpt[j++] = q; - k = i; - for(i=0; iprintfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; - pToken = va_arg(ap, Token*); - assert( bArgList==0 ); - if( pToken && pToken->n ){ - sqlcipher_sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n); - } - length = width = 0; - break; - } - case etSRCLIST: { - SrcList *pSrc; - int k; - struct SrcList_item *pItem; - if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; - pSrc = va_arg(ap, SrcList*); - k = va_arg(ap, int); - pItem = &pSrc->a[k]; - assert( bArgList==0 ); - assert( k>=0 && knSrc ); - if( pItem->zDatabase ){ - sqlcipher_sqlite3_str_appendall(pAccum, pItem->zDatabase); - sqlcipher_sqlite3_str_append(pAccum, ".", 1); - } - sqlcipher_sqlite3_str_appendall(pAccum, pItem->zName); - length = width = 0; - break; - } - default: { - assert( xtype==etINVALID ); - return; - } - }/* End switch over the format type */ - /* - ** The text of the conversion is pointed to by "bufpt" and is - ** "length" characters long. The field width is "width". Do - ** the output. Both length and width are in bytes, not characters, - ** at this point. If the "!" flag was present on string conversions - ** indicating that width and precision should be expressed in characters, - ** then the values have been translated prior to reaching this point. - */ - width -= length; - if( width>0 ){ - if( !flag_leftjustify ) sqlcipher_sqlite3_str_appendchar(pAccum, width, ' '); - sqlcipher_sqlite3_str_append(pAccum, bufpt, length); - if( flag_leftjustify ) sqlcipher_sqlite3_str_appendchar(pAccum, width, ' '); - }else{ - sqlcipher_sqlite3_str_append(pAccum, bufpt, length); - } +/* This function (for internal use only) locates an element in an +** hash table that matches the given key. If no element is found, +** a pointer to a static null element with HashElem.data==0 is returned. +** If pH is not NULL, then the hash for this key is written to *pH. +*/ +static HashElem *findElementWithHash( + const Hash *pH, /* The pH to be searched */ + const char *pKey, /* The key we are searching for */ + unsigned int *pHash /* Write the hash value here */ +){ + HashElem *elem; /* Used to loop thru the element list */ + unsigned int count; /* Number of elements left to test */ + unsigned int h; /* The computed hash */ + static HashElem nullElement = { 0, 0, 0, 0 }; - if( zExtra ){ - sqlcipher_sqlite3DbFree(pAccum->db, zExtra); - zExtra = 0; + if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/ + struct _ht *pEntry; + h = strHash(pKey) % pH->htsize; + pEntry = &pH->ht[h]; + elem = pEntry->chain; + count = pEntry->count; + }else{ + h = 0; + elem = pH->first; + count = pH->count; + } + if( pHash ) *pHash = h; + while( count-- ){ + assert( elem!=0 ); + if( sqlcipher_sqlite3StrICmp(elem->pKey,pKey)==0 ){ + return elem; } - }/* End for loop over the format string */ -} /* End of function */ + elem = elem->next; + } + return &nullElement; +} -/* -** Enlarge the memory allocation on a StrAccum object so that it is -** able to accept at least N more bytes of text. -** -** Return the number of bytes of text that StrAccum is able to accept -** after the attempted enlargement. The value returned might be zero. +/* Remove a single entry from the hash table given a pointer to that +** element and a hash on the element's key. */ -static int sqlcipher_sqlite3StrAccumEnlarge(StrAccum *p, int N){ - char *zNew; - assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */ - if( p->accError ){ - testcase(p->accError==SQLITE_TOOBIG); - testcase(p->accError==SQLITE_NOMEM); - return 0; - } - if( p->mxAlloc==0 ){ - setStrAccumError(p, SQLITE_TOOBIG); - return p->nAlloc - p->nChar - 1; +static void removeElementGivenHash( + Hash *pH, /* The pH containing "elem" */ + HashElem* elem, /* The element to be removed from the pH */ + unsigned int h /* Hash value for the element */ +){ + struct _ht *pEntry; + if( elem->prev ){ + elem->prev->next = elem->next; }else{ - char *zOld = isMalloced(p) ? p->zText : 0; - i64 szNew = p->nChar; - szNew += N + 1; - if( szNew+p->nChar<=p->mxAlloc ){ - /* Force exponential buffer size growth as long as it does not overflow, - ** to avoid having to call this routine too often */ - szNew += p->nChar; - } - if( szNew > p->mxAlloc ){ - sqlcipher_sqlite3_str_reset(p); - setStrAccumError(p, SQLITE_TOOBIG); - return 0; - }else{ - p->nAlloc = (int)szNew; + pH->first = elem->next; + } + if( elem->next ){ + elem->next->prev = elem->prev; + } + if( pH->ht ){ + pEntry = &pH->ht[h]; + if( pEntry->chain==elem ){ + pEntry->chain = elem->next; } - if( p->db ){ - zNew = sqlcipher_sqlite3DbRealloc(p->db, zOld, p->nAlloc); + assert( pEntry->count>0 ); + pEntry->count--; + } + sqlcipher_sqlite3_free( elem ); + pH->count--; + if( pH->count==0 ){ + assert( pH->first==0 ); + assert( pH->count==0 ); + sqlcipher_sqlite3HashClear(pH); + } +} + +/* Attempt to locate an element of the hash table pH with a key +** that matches pKey. Return the data for this element if it is +** found, or NULL if there is no match. +*/ +SQLITE_PRIVATE void *sqlcipher_sqlite3HashFind(const Hash *pH, const char *pKey){ + assert( pH!=0 ); + assert( pKey!=0 ); + return findElementWithHash(pH, pKey, 0)->data; +} + +/* Insert an element into the hash table pH. The key is pKey +** and the data is "data". +** +** If no element exists with a matching key, then a new +** element is created and NULL is returned. +** +** If another element already exists with the same key, then the +** new data replaces the old data and the old data is returned. +** The key is not copied in this instance. If a malloc fails, then +** the new data is returned and the hash table is unchanged. +** +** If the "data" parameter to this function is NULL, then the +** element corresponding to "key" is removed from the hash table. +*/ +SQLITE_PRIVATE void *sqlcipher_sqlite3HashInsert(Hash *pH, const char *pKey, void *data){ + unsigned int h; /* the hash of the key modulo hash table size */ + HashElem *elem; /* Used to loop thru the element list */ + HashElem *new_elem; /* New element added to the pH */ + + assert( pH!=0 ); + assert( pKey!=0 ); + elem = findElementWithHash(pH,pKey,&h); + if( elem->data ){ + void *old_data = elem->data; + if( data==0 ){ + removeElementGivenHash(pH,elem,h); }else{ - zNew = sqlcipher_sqlite3Realloc(zOld, p->nAlloc); + elem->data = data; + elem->pKey = pKey; } - if( zNew ){ - assert( p->zText!=0 || p->nChar==0 ); - if( !isMalloced(p) && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar); - p->zText = zNew; - p->nAlloc = sqlcipher_sqlite3DbMallocSize(p->db, zNew); - p->printfFlags |= SQLITE_PRINTF_MALLOCED; - }else{ - sqlcipher_sqlite3_str_reset(p); - setStrAccumError(p, SQLITE_NOMEM); - return 0; + return old_data; + } + if( data==0 ) return 0; + new_elem = (HashElem*)sqlcipher_sqlite3Malloc( sizeof(HashElem) ); + if( new_elem==0 ) return data; + new_elem->pKey = pKey; + new_elem->data = data; + pH->count++; + if( pH->count>=10 && pH->count > 2*pH->htsize ){ + if( rehash(pH, pH->count*2) ){ + assert( pH->htsize>0 ); + h = strHash(pKey) % pH->htsize; } } - return N; + insertElement(pH, pH->ht ? &pH->ht[h] : 0, new_elem); + return 0; +} + +/************** End of hash.c ************************************************/ +/************** Begin file opcodes.c *****************************************/ +/* Automatically generated. Do not edit */ +/* See the tool/mkopcodec.tcl script for details. */ +#if !defined(SQLITE_OMIT_EXPLAIN) \ + || defined(VDBE_PROFILE) \ + || defined(SQLITE_DEBUG) +#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG) +# define OpHelp(X) "\0" X +#else +# define OpHelp(X) +#endif +SQLITE_PRIVATE const char *sqlcipher_sqlite3OpcodeName(int i){ + static const char *const azName[] = { + /* 0 */ "Savepoint" OpHelp(""), + /* 1 */ "AutoCommit" OpHelp(""), + /* 2 */ "Transaction" OpHelp(""), + /* 3 */ "Checkpoint" OpHelp(""), + /* 4 */ "JournalMode" OpHelp(""), + /* 5 */ "Vacuum" OpHelp(""), + /* 6 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"), + /* 7 */ "VUpdate" OpHelp("data=r[P3@P2]"), + /* 8 */ "Goto" OpHelp(""), + /* 9 */ "Gosub" OpHelp(""), + /* 10 */ "InitCoroutine" OpHelp(""), + /* 11 */ "Yield" OpHelp(""), + /* 12 */ "MustBeInt" OpHelp(""), + /* 13 */ "Jump" OpHelp(""), + /* 14 */ "Once" OpHelp(""), + /* 15 */ "If" OpHelp(""), + /* 16 */ "IfNot" OpHelp(""), + /* 17 */ "IsNullOrType" OpHelp("if typeof(r[P1]) IN (P3,5) goto P2"), + /* 18 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"), + /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"), + /* 20 */ "SeekLT" OpHelp("key=r[P3@P4]"), + /* 21 */ "SeekLE" OpHelp("key=r[P3@P4]"), + /* 22 */ "SeekGE" OpHelp("key=r[P3@P4]"), + /* 23 */ "SeekGT" OpHelp("key=r[P3@P4]"), + /* 24 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"), + /* 25 */ "IfNoHope" OpHelp("key=r[P3@P4]"), + /* 26 */ "NoConflict" OpHelp("key=r[P3@P4]"), + /* 27 */ "NotFound" OpHelp("key=r[P3@P4]"), + /* 28 */ "Found" OpHelp("key=r[P3@P4]"), + /* 29 */ "SeekRowid" OpHelp("intkey=r[P3]"), + /* 30 */ "NotExists" OpHelp("intkey=r[P3]"), + /* 31 */ "Last" OpHelp(""), + /* 32 */ "IfSmaller" OpHelp(""), + /* 33 */ "SorterSort" OpHelp(""), + /* 34 */ "Sort" OpHelp(""), + /* 35 */ "Rewind" OpHelp(""), + /* 36 */ "SorterNext" OpHelp(""), + /* 37 */ "Prev" OpHelp(""), + /* 38 */ "Next" OpHelp(""), + /* 39 */ "IdxLE" OpHelp("key=r[P3@P4]"), + /* 40 */ "IdxGT" OpHelp("key=r[P3@P4]"), + /* 41 */ "IdxLT" OpHelp("key=r[P3@P4]"), + /* 42 */ "IdxGE" OpHelp("key=r[P3@P4]"), + /* 43 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"), + /* 44 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"), + /* 45 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), + /* 46 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), + /* 47 */ "Program" OpHelp(""), + /* 48 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), + /* 49 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), + /* 50 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), + /* 51 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), + /* 52 */ "Ne" OpHelp("IF r[P3]!=r[P1]"), + /* 53 */ "Eq" OpHelp("IF r[P3]==r[P1]"), + /* 54 */ "Gt" OpHelp("IF r[P3]>r[P1]"), + /* 55 */ "Le" OpHelp("IF r[P3]<=r[P1]"), + /* 56 */ "Lt" OpHelp("IF r[P3]=r[P1]"), + /* 58 */ "ElseEq" OpHelp(""), + /* 59 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), + /* 60 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), + /* 61 */ "IncrVacuum" OpHelp(""), + /* 62 */ "VNext" OpHelp(""), + /* 63 */ "Filter" OpHelp("if key(P3@P4) not in filter(P1) goto P2"), + /* 64 */ "Init" OpHelp("Start at P2"), + /* 65 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"), + /* 66 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"), + /* 67 */ "Return" OpHelp(""), + /* 68 */ "EndCoroutine" OpHelp(""), + /* 69 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), + /* 70 */ "Halt" OpHelp(""), + /* 71 */ "Integer" OpHelp("r[P2]=P1"), + /* 72 */ "Int64" OpHelp("r[P2]=P4"), + /* 73 */ "String" OpHelp("r[P2]='P4' (len=P1)"), + /* 74 */ "BeginSubrtn" OpHelp("r[P2]=NULL"), + /* 75 */ "Null" OpHelp("r[P2..P3]=NULL"), + /* 76 */ "SoftNull" OpHelp("r[P1]=NULL"), + /* 77 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), + /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), + /* 79 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), + /* 80 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), + /* 81 */ "SCopy" OpHelp("r[P2]=r[P1]"), + /* 82 */ "IntCopy" OpHelp("r[P2]=r[P1]"), + /* 83 */ "FkCheck" OpHelp(""), + /* 84 */ "ResultRow" OpHelp("output=r[P1@P2]"), + /* 85 */ "CollSeq" OpHelp(""), + /* 86 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), + /* 87 */ "RealAffinity" OpHelp(""), + /* 88 */ "Cast" OpHelp("affinity(r[P1])"), + /* 89 */ "Permutation" OpHelp(""), + /* 90 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), + /* 91 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), + /* 92 */ "ZeroOrNull" OpHelp("r[P2] = 0 OR NULL"), + /* 93 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), + /* 94 */ "Column" OpHelp("r[P3]=PX cursor P1 column P2"), + /* 95 */ "TypeCheck" OpHelp("typecheck(r[P1@P2])"), + /* 96 */ "Affinity" OpHelp("affinity(r[P1@P2])"), + /* 97 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), + /* 98 */ "Count" OpHelp("r[P2]=count()"), + /* 99 */ "ReadCookie" OpHelp(""), + /* 100 */ "SetCookie" OpHelp(""), + /* 101 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), + /* 102 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), + /* 103 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), + /* 104 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), + /* 106 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), + /* 107 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), + /* 108 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), + /* 109 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), + /* 110 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), + /* 111 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), + /* 112 */ "OpenRead" OpHelp("root=P2 iDb=P3"), + /* 113 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), + /* 114 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), + /* 115 */ "OpenDup" OpHelp(""), + /* 116 */ "OpenAutoindex" OpHelp("nColumn=P2"), + /* 117 */ "String8" OpHelp("r[P2]='P4'"), + /* 118 */ "OpenEphemeral" OpHelp("nColumn=P2"), + /* 119 */ "SorterOpen" OpHelp(""), + /* 120 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), + /* 121 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), + /* 122 */ "Close" OpHelp(""), + /* 123 */ "ColumnsUsed" OpHelp(""), + /* 124 */ "SeekScan" OpHelp("Scan-ahead up to P1 rows"), + /* 125 */ "SeekHit" OpHelp("set P2<=seekHit<=P3"), + /* 126 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), + /* 127 */ "NewRowid" OpHelp("r[P2]=rowid"), + /* 128 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), + /* 129 */ "RowCell" OpHelp(""), + /* 130 */ "Delete" OpHelp(""), + /* 131 */ "ResetCount" OpHelp(""), + /* 132 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), + /* 133 */ "SorterData" OpHelp("r[P2]=data"), + /* 134 */ "RowData" OpHelp("r[P2]=data"), + /* 135 */ "Rowid" OpHelp("r[P2]=PX rowid of P1"), + /* 136 */ "NullRow" OpHelp(""), + /* 137 */ "SeekEnd" OpHelp(""), + /* 138 */ "IdxInsert" OpHelp("key=r[P2]"), + /* 139 */ "SorterInsert" OpHelp("key=r[P2]"), + /* 140 */ "IdxDelete" OpHelp("key=r[P2@P3]"), + /* 141 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), + /* 142 */ "IdxRowid" OpHelp("r[P2]=rowid"), + /* 143 */ "FinishSeek" OpHelp(""), + /* 144 */ "Destroy" OpHelp(""), + /* 145 */ "Clear" OpHelp(""), + /* 146 */ "ResetSorter" OpHelp(""), + /* 147 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), + /* 148 */ "SqlExec" OpHelp(""), + /* 149 */ "ParseSchema" OpHelp(""), + /* 150 */ "LoadAnalysis" OpHelp(""), + /* 151 */ "DropTable" OpHelp(""), + /* 152 */ "DropIndex" OpHelp(""), + /* 153 */ "Real" OpHelp("r[P2]=P4"), + /* 154 */ "DropTrigger" OpHelp(""), + /* 155 */ "IntegrityCk" OpHelp(""), + /* 156 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), + /* 157 */ "Param" OpHelp(""), + /* 158 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), + /* 159 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), + /* 160 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), + /* 161 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), + /* 162 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 163 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 164 */ "AggValue" OpHelp("r[P3]=value N=P2"), + /* 165 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 166 */ "Expire" OpHelp(""), + /* 167 */ "CursorLock" OpHelp(""), + /* 168 */ "CursorUnlock" OpHelp(""), + /* 169 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 170 */ "VBegin" OpHelp(""), + /* 171 */ "VCreate" OpHelp(""), + /* 172 */ "VDestroy" OpHelp(""), + /* 173 */ "VOpen" OpHelp(""), + /* 174 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), + /* 175 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 176 */ "VRename" OpHelp(""), + /* 177 */ "Pagecount" OpHelp(""), + /* 178 */ "MaxPgcnt" OpHelp(""), + /* 179 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"), + /* 180 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), + /* 181 */ "Trace" OpHelp(""), + /* 182 */ "CursorHint" OpHelp(""), + /* 183 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), + /* 184 */ "Noop" OpHelp(""), + /* 185 */ "Explain" OpHelp(""), + /* 186 */ "Abortable" OpHelp(""), + }; + return azName[i]; } +#endif +/************** End of opcodes.c *********************************************/ +/************** Begin file os_unix.c *****************************************/ /* -** Append N copies of character c to the given string buffer. +** 2004 May 22 +** +** 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 the VFS implementation for unix-like operating systems +** include Linux, MacOSX, *BSD, QNX, VxWorks, AIX, HPUX, and others. +** +** There are actually several different VFS implementations in this file. +** The differences are in the way that file locking is done. The default +** implementation uses Posix Advisory Locks. Alternative implementations +** use flock(), dot-files, various proprietary locking schemas, or simply +** skip locking all together. +** +** This source file is organized into divisions where the logic for various +** subfunctions is contained within the appropriate division. PLEASE +** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed +** in the correct division and should be clearly labeled. +** +** The layout of divisions is as follows: +** +** * General-purpose declarations and utility functions. +** * Unique file ID logic used by VxWorks. +** * Various locking primitive implementations (all except proxy locking): +** + for Posix Advisory Locks +** + for no-op locks +** + for dot-file locks +** + for flock() locking +** + for named semaphore locks (VxWorks only) +** + for AFP filesystem locks (MacOSX only) +** * sqlcipher_sqlite3_file methods not associated with locking. +** * Definitions of sqlcipher_sqlite3_io_methods objects for all locking +** methods plus "finder" functions for each locking method. +** * sqlcipher_sqlite3_vfs method implementations. +** * Locking primitives for the proxy uber-locking-method. (MacOSX only) +** * Definitions of sqlcipher_sqlite3_vfs objects for all locking methods +** plus implementations of sqlcipher_sqlite3_os_init() and sqlcipher_sqlite3_os_end(). */ -SQLITE_API void sqlcipher_sqlite3_str_appendchar(sqlcipher_sqlite3_str *p, int N, char c){ - testcase( p->nChar + (i64)N > 0x7fffffff ); - if( p->nChar+(i64)N >= p->nAlloc && (N = sqlcipher_sqlite3StrAccumEnlarge(p, N))<=0 ){ - return; - } - while( (N--)>0 ) p->zText[p->nChar++] = c; -} +/* #include "sqliteInt.h" */ +#if SQLITE_OS_UNIX /* This file is used on unix only */ /* -** The StrAccum "p" is not large enough to accept N new bytes of z[]. -** So enlarge if first, then do the append. +** There are various methods for file locking used for concurrency +** control: ** -** This is a helper routine to sqlcipher_sqlite3_str_append() that does special-case -** work (enlarging the buffer) using tail recursion, so that the -** sqlcipher_sqlite3_str_append() routine can use fast calling semantics. +** 1. POSIX locking (the default), +** 2. No locking, +** 3. Dot-file locking, +** 4. flock() locking, +** 5. AFP locking (OSX only), +** 6. Named POSIX semaphores (VXWorks only), +** 7. proxy locking. (OSX only) +** +** Styles 4, 5, and 7 are only available of SQLITE_ENABLE_LOCKING_STYLE +** is defined to 1. The SQLITE_ENABLE_LOCKING_STYLE also enables automatic +** selection of the appropriate locking style based on the filesystem +** where the database is located. */ -static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){ - N = sqlcipher_sqlite3StrAccumEnlarge(p, N); - if( N>0 ){ - memcpy(&p->zText[p->nChar], z, N); - p->nChar += N; - } -} +#if !defined(SQLITE_ENABLE_LOCKING_STYLE) +# if defined(__APPLE__) +# define SQLITE_ENABLE_LOCKING_STYLE 1 +# else +# define SQLITE_ENABLE_LOCKING_STYLE 0 +# endif +#endif + +/* Use pread() and pwrite() if they are available */ +#if defined(__APPLE__) +# define HAVE_PREAD 1 +# define HAVE_PWRITE 1 +#endif +#if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64) +# undef USE_PREAD +# define USE_PREAD64 1 +#elif defined(HAVE_PREAD) && defined(HAVE_PWRITE) +# undef USE_PREAD64 +# define USE_PREAD 1 +#endif /* -** Append N bytes of text from z to the StrAccum object. Increase the -** size of the memory allocation for StrAccum if necessary. +** standard include files. */ -SQLITE_API void sqlcipher_sqlite3_str_append(sqlcipher_sqlite3_str *p, const char *z, int N){ - assert( z!=0 || N==0 ); - assert( p->zText!=0 || p->nChar==0 || p->accError ); - assert( N>=0 ); - assert( p->accError==0 || p->nAlloc==0 || p->mxAlloc==0 ); - if( p->nChar+N >= p->nAlloc ){ - enlargeAndAppend(p,z,N); - }else if( N ){ - assert( p->zText ); - p->nChar += N; - memcpy(&p->zText[p->nChar-N], z, N); - } -} +#include +#include +#include +#include +#include +/* #include */ +#include +#include +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 +# include +#endif + +#if SQLITE_ENABLE_LOCKING_STYLE +/* # include */ +# include +# include +#endif /* SQLITE_ENABLE_LOCKING_STYLE */ /* -** Append the complete text of zero-terminated string z[] to the p string. +** Try to determine if gethostuuid() is available based on standard +** macros. This might sometimes compute the wrong value for some +** obscure platforms. For those cases, simply compile with one of +** the following: +** +** -DHAVE_GETHOSTUUID=0 +** -DHAVE_GETHOSTUUID=1 +** +** None if this matters except when building on Apple products with +** -DSQLITE_ENABLE_LOCKING_STYLE. */ -SQLITE_API void sqlcipher_sqlite3_str_appendall(sqlcipher_sqlite3_str *p, const char *z){ - sqlcipher_sqlite3_str_append(p, z, sqlcipher_sqlite3Strlen30(z)); +#ifndef HAVE_GETHOSTUUID +# define HAVE_GETHOSTUUID 0 +# if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \ + (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000)) +# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \ + && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))\ + && (!defined(TARGET_OS_MACCATALYST) || (TARGET_OS_MACCATALYST==0)) +# undef HAVE_GETHOSTUUID +# define HAVE_GETHOSTUUID 1 +# else +# warning "gethostuuid() is disabled." +# endif +# endif +#endif + + +#if OS_VXWORKS +/* # include */ +# include +# include +#endif /* OS_VXWORKS */ + +#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE +# include +#endif + +#ifdef HAVE_UTIME +# include +#endif + +/* +** Allowed values of unixFile.fsFlags +*/ +#define SQLITE_FSFLAGS_IS_MSDOS 0x1 + +/* +** If we are to be thread-safe, include the pthreads header. +*/ +#if SQLITE_THREADSAFE +/* # include */ +#endif + +/* +** Default permissions when creating a new file +*/ +#ifndef SQLITE_DEFAULT_FILE_PERMISSIONS +# define SQLITE_DEFAULT_FILE_PERMISSIONS 0644 +#endif + +/* +** Default permissions when creating auto proxy dir +*/ +#ifndef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS +# define SQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755 +#endif + +/* +** Maximum supported path-length. +*/ +#define MAX_PATHNAME 512 + +/* +** Maximum supported symbolic links +*/ +#define SQLITE_MAX_SYMLINKS 100 + +/* Always cast the getpid() return type for compatibility with +** kernel modules in VxWorks. */ +#define osGetpid(X) (pid_t)getpid() + +/* +** Only set the lastErrno if the error code is a real error and not +** a normal expected return code of SQLITE_BUSY or SQLITE_OK +*/ +#define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY)) + +/* Forward references */ +typedef struct unixShm unixShm; /* Connection shared memory */ +typedef struct unixShmNode unixShmNode; /* Shared memory instance */ +typedef struct unixInodeInfo unixInodeInfo; /* An i-node */ +typedef struct UnixUnusedFd UnixUnusedFd; /* An unused file descriptor */ + +/* +** Sometimes, after a file handle is closed by SQLite, the file descriptor +** cannot be closed immediately. In these cases, instances of the following +** structure are used to store the file descriptor while waiting for an +** opportunity to either close or reuse it. +*/ +struct UnixUnusedFd { + int fd; /* File descriptor to close */ + int flags; /* Flags this file descriptor was opened with */ + UnixUnusedFd *pNext; /* Next unused file descriptor on same file */ +}; + +/* +** The unixFile structure is subclass of sqlcipher_sqlite3_file specific to the unix +** VFS implementations. +*/ +typedef struct unixFile unixFile; +struct unixFile { + sqlcipher_sqlite3_io_methods const *pMethod; /* Always the first entry */ + sqlcipher_sqlite3_vfs *pVfs; /* The VFS that created this unixFile */ + unixInodeInfo *pInode; /* Info about locks on this inode */ + int h; /* The file descriptor */ + unsigned char eFileLock; /* The type of lock held on this fd */ + unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ + int lastErrno; /* The unix errno from last I/O error */ + void *lockingContext; /* Locking style specific state */ + UnixUnusedFd *pPreallocatedUnused; /* Pre-allocated UnixUnusedFd */ + const char *zPath; /* Name of the file */ + unixShm *pShm; /* Shared memory segment information */ + int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ +#if SQLITE_MAX_MMAP_SIZE>0 + int nFetchOut; /* Number of outstanding xFetch refs */ + sqlcipher_sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */ + sqlcipher_sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */ + sqlcipher_sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ + void *pMapRegion; /* Memory mapped region */ +#endif + int sectorSize; /* Device sector size */ + int deviceCharacteristics; /* Precomputed device characteristics */ +#if SQLITE_ENABLE_LOCKING_STYLE + int openFlags; /* The flags specified at open() */ +#endif +#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) + unsigned fsFlags; /* cached details from statfs() */ +#endif +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + unsigned iBusyTimeout; /* Wait this many millisec on locks */ +#endif +#if OS_VXWORKS + struct vxworksFileId *pId; /* Unique file ID */ +#endif +#ifdef SQLITE_DEBUG + /* The next group of variables are used to track whether or not the + ** transaction counter in bytes 24-27 of database files are updated + ** whenever any part of the database changes. An assertion fault will + ** occur if a file is updated without also updating the transaction + ** counter. This test is made to avoid new problems similar to the + ** one described by ticket #3584. + */ + unsigned char transCntrChng; /* True if the transaction counter changed */ + unsigned char dbUpdate; /* True if any part of database file changed */ + unsigned char inNormalWrite; /* True if in a normal write operation */ + +#endif + +#ifdef SQLITE_TEST + /* In test mode, increase the size of this structure a bit so that + ** it is larger than the struct CrashFile defined in test6.c. + */ + char aPadding[32]; +#endif +}; + +/* This variable holds the process id (pid) from when the xRandomness() +** method was called. If xOpen() is called from a different process id, +** indicating that a fork() has occurred, the PRNG will be reset. +*/ +static pid_t randomnessPid = 0; + +/* +** Allowed values for the unixFile.ctrlFlags bitmask: +*/ +#define UNIXFILE_EXCL 0x01 /* Connections from one process only */ +#define UNIXFILE_RDONLY 0x02 /* Connection is read only */ +#define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ +#ifndef SQLITE_DISABLE_DIRSYNC +# define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */ +#else +# define UNIXFILE_DIRSYNC 0x00 +#endif +#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ +#define UNIXFILE_DELETE 0x20 /* Delete on close */ +#define UNIXFILE_URI 0x40 /* Filename might have query parameters */ +#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ + +/* +** Include code that is common to all os_*.c files +*/ +/* #include "os_common.h" */ + +/* +** Define various macros that are missing from some systems. +*/ +#ifndef O_LARGEFILE +# define O_LARGEFILE 0 +#endif +#ifdef SQLITE_DISABLE_LFS +# undef O_LARGEFILE +# define O_LARGEFILE 0 +#endif +#ifndef O_NOFOLLOW +# define O_NOFOLLOW 0 +#endif +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +/* +** The threadid macro resolves to the thread-id or to 0. Used for +** testing and debugging only. +*/ +#if SQLITE_THREADSAFE +#define threadid pthread_self() +#else +#define threadid 0 +#endif + +/* +** HAVE_MREMAP defaults to true on Linux and false everywhere else. +*/ +#if !defined(HAVE_MREMAP) +# if defined(__linux__) && defined(_GNU_SOURCE) +# define HAVE_MREMAP 1 +# else +# define HAVE_MREMAP 0 +# endif +#endif + +/* +** Explicitly call the 64-bit version of lseek() on Android. Otherwise, lseek() +** is the 32-bit version, even if _FILE_OFFSET_BITS=64 is defined. +*/ +#ifdef __ANDROID__ +# define lseek lseek64 +#endif + +#ifdef __linux__ +/* +** Linux-specific IOCTL magic numbers used for controlling F2FS +*/ +#define F2FS_IOCTL_MAGIC 0xf5 +#define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1) +#define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2) +#define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3) +#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5) +#define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, u32) +#define F2FS_FEATURE_ATOMIC_WRITE 0x0004 +#endif /* __linux__ */ + + +/* +** Different Unix systems declare open() in different ways. Same use +** open(const char*,int,mode_t). Others use open(const char*,int,...). +** The difference is important when using a pointer to the function. +** +** The safest way to deal with the problem is to always use this wrapper +** which always has the same well-defined interface. +*/ +static int posixOpen(const char *zFile, int flags, int mode){ + return open(zFile, flags, mode); } +/* Forward reference */ +static int openDirectory(const char*, int*); +static int unixGetpagesize(void); /* -** Finish off a string by making sure it is zero-terminated. -** Return a pointer to the resulting string. Return a NULL -** pointer if any kind of error was encountered. +** Many system calls are accessed through pointer-to-functions so that +** they may be overridden at runtime to facilitate fault injection during +** testing and sandboxing. The following array holds the names and pointers +** to all overrideable system calls. */ -static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){ - char *zText; - assert( p->mxAlloc>0 && !isMalloced(p) ); - zText = sqlcipher_sqlite3DbMallocRaw(p->db, p->nChar+1 ); - if( zText ){ - memcpy(zText, p->zText, p->nChar+1); - p->printfFlags |= SQLITE_PRINTF_MALLOCED; - }else{ - setStrAccumError(p, SQLITE_NOMEM); - } - p->zText = zText; - return zText; +static struct unix_syscall { + const char *zName; /* Name of the system call */ + sqlcipher_sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ + sqlcipher_sqlite3_syscall_ptr pDefault; /* Default value */ +} aSyscall[] = { + { "open", (sqlcipher_sqlite3_syscall_ptr)posixOpen, 0 }, +#define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent) + + { "close", (sqlcipher_sqlite3_syscall_ptr)close, 0 }, +#define osClose ((int(*)(int))aSyscall[1].pCurrent) + + { "access", (sqlcipher_sqlite3_syscall_ptr)access, 0 }, +#define osAccess ((int(*)(const char*,int))aSyscall[2].pCurrent) + + { "getcwd", (sqlcipher_sqlite3_syscall_ptr)getcwd, 0 }, +#define osGetcwd ((char*(*)(char*,size_t))aSyscall[3].pCurrent) + + { "stat", (sqlcipher_sqlite3_syscall_ptr)stat, 0 }, +#define osStat ((int(*)(const char*,struct stat*))aSyscall[4].pCurrent) + +/* +** The DJGPP compiler environment looks mostly like Unix, but it +** lacks the fcntl() system call. So redefine fcntl() to be something +** that always succeeds. This means that locking does not occur under +** DJGPP. But it is DOS - what did you expect? +*/ +#ifdef __DJGPP__ + { "fstat", 0, 0 }, +#define osFstat(a,b,c) 0 +#else + { "fstat", (sqlcipher_sqlite3_syscall_ptr)fstat, 0 }, +#define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent) +#endif + + { "ftruncate", (sqlcipher_sqlite3_syscall_ptr)ftruncate, 0 }, +#define osFtruncate ((int(*)(int,off_t))aSyscall[6].pCurrent) + + { "fcntl", (sqlcipher_sqlite3_syscall_ptr)fcntl, 0 }, +#define osFcntl ((int(*)(int,int,...))aSyscall[7].pCurrent) + + { "read", (sqlcipher_sqlite3_syscall_ptr)read, 0 }, +#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) + +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE + { "pread", (sqlcipher_sqlite3_syscall_ptr)pread, 0 }, +#else + { "pread", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osPread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].pCurrent) + +#if defined(USE_PREAD64) + { "pread64", (sqlcipher_sqlite3_syscall_ptr)pread64, 0 }, +#else + { "pread64", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osPread64 ((ssize_t(*)(int,void*,size_t,off64_t))aSyscall[10].pCurrent) + + { "write", (sqlcipher_sqlite3_syscall_ptr)write, 0 }, +#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) + +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE + { "pwrite", (sqlcipher_sqlite3_syscall_ptr)pwrite, 0 }, +#else + { "pwrite", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osPwrite ((ssize_t(*)(int,const void*,size_t,off_t))\ + aSyscall[12].pCurrent) + +#if defined(USE_PREAD64) + { "pwrite64", (sqlcipher_sqlite3_syscall_ptr)pwrite64, 0 }, +#else + { "pwrite64", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\ + aSyscall[13].pCurrent) + + { "fchmod", (sqlcipher_sqlite3_syscall_ptr)fchmod, 0 }, +#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) + +#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE + { "fallocate", (sqlcipher_sqlite3_syscall_ptr)posix_fallocate, 0 }, +#else + { "fallocate", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent) + + { "unlink", (sqlcipher_sqlite3_syscall_ptr)unlink, 0 }, +#define osUnlink ((int(*)(const char*))aSyscall[16].pCurrent) + + { "openDirectory", (sqlcipher_sqlite3_syscall_ptr)openDirectory, 0 }, +#define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent) + + { "mkdir", (sqlcipher_sqlite3_syscall_ptr)mkdir, 0 }, +#define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent) + + { "rmdir", (sqlcipher_sqlite3_syscall_ptr)rmdir, 0 }, +#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) + +#if defined(HAVE_FCHOWN) + { "fchown", (sqlcipher_sqlite3_syscall_ptr)fchown, 0 }, +#else + { "fchown", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) + +#if defined(HAVE_FCHOWN) + { "geteuid", (sqlcipher_sqlite3_syscall_ptr)geteuid, 0 }, +#else + { "geteuid", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) + +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 + { "mmap", (sqlcipher_sqlite3_syscall_ptr)mmap, 0 }, +#else + { "mmap", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent) + +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 + { "munmap", (sqlcipher_sqlite3_syscall_ptr)munmap, 0 }, +#else + { "munmap", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osMunmap ((int(*)(void*,size_t))aSyscall[23].pCurrent) + +#if HAVE_MREMAP && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) + { "mremap", (sqlcipher_sqlite3_syscall_ptr)mremap, 0 }, +#else + { "mremap", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[24].pCurrent) + +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 + { "getpagesize", (sqlcipher_sqlite3_syscall_ptr)unixGetpagesize, 0 }, +#else + { "getpagesize", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osGetpagesize ((int(*)(void))aSyscall[25].pCurrent) + +#if defined(HAVE_READLINK) + { "readlink", (sqlcipher_sqlite3_syscall_ptr)readlink, 0 }, +#else + { "readlink", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[26].pCurrent) + +#if defined(HAVE_LSTAT) + { "lstat", (sqlcipher_sqlite3_syscall_ptr)lstat, 0 }, +#else + { "lstat", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif +#define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent) + +#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) +# ifdef __ANDROID__ + { "ioctl", (sqlcipher_sqlite3_syscall_ptr)(int(*)(int, int, ...))ioctl, 0 }, +#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent) +# else + { "ioctl", (sqlcipher_sqlite3_syscall_ptr)ioctl, 0 }, +#define osIoctl ((int(*)(int,unsigned long,...))aSyscall[28].pCurrent) +# endif +#else + { "ioctl", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#endif + +}; /* End of the overrideable system calls */ + + +/* +** On some systems, calls to fchown() will trigger a message in a security +** log if they come from non-root processes. So avoid calling fchown() if +** we are not running as root. +*/ +static int robustFchown(int fd, uid_t uid, gid_t gid){ +#if defined(HAVE_FCHOWN) + return osGeteuid() ? 0 : osFchown(fd,uid,gid); +#else + return 0; +#endif } -SQLITE_PRIVATE char *sqlcipher_sqlite3StrAccumFinish(StrAccum *p){ - if( p->zText ){ - p->zText[p->nChar] = 0; - if( p->mxAlloc>0 && !isMalloced(p) ){ - return strAccumFinishRealloc(p); + +/* +** This is the xSetSystemCall() method of sqlcipher_sqlite3_vfs for all of the +** "unix" VFSes. Return SQLITE_OK opon successfully updating the +** system call pointer, or SQLITE_NOTFOUND if there is no configurable +** system call named zName. +*/ +static int unixSetSystemCall( + sqlcipher_sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ + const char *zName, /* Name of system call to override */ + sqlcipher_sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ +){ + unsigned int i; + int rc = SQLITE_NOTFOUND; + + UNUSED_PARAMETER(pNotUsed); + if( zName==0 ){ + /* If no zName is given, restore all system calls to their default + ** settings and return NULL + */ + rc = SQLITE_OK; + for(i=0; izText; + return rc; } /* -** This singleton is an sqlcipher_sqlite3_str object that is returned if -** sqlcipher_sqlite3_malloc() fails to provide space for a real one. This -** sqlcipher_sqlite3_str object accepts no new text and always returns -** an SQLITE_NOMEM error. +** Return the value of a system call. Return NULL if zName is not a +** recognized system call name. NULL is also returned if the system call +** is currently undefined. */ -static sqlcipher_sqlite3_str sqlcipher_sqlite3OomStr = { - 0, 0, 0, 0, 0, SQLITE_NOMEM, 0 -}; +static sqlcipher_sqlite3_syscall_ptr unixGetSystemCall( + sqlcipher_sqlite3_vfs *pNotUsed, + const char *zName +){ + unsigned int i; -/* Finalize a string created using sqlcipher_sqlite3_str_new(). -*/ -SQLITE_API char *sqlcipher_sqlite3_str_finish(sqlcipher_sqlite3_str *p){ - char *z; - if( p!=0 && p!=&sqlcipher_sqlite3OomStr ){ - z = sqlcipher_sqlite3StrAccumFinish(p); - sqlcipher_sqlite3_free(p); - }else{ - z = 0; + UNUSED_PARAMETER(pNotUsed); + for(i=0; iaccError : SQLITE_NOMEM; -} +/* +** Return the name of the first system call after zName. If zName==NULL +** then return the name of the first system call. Return NULL if zName +** is the last system call or if zName is not the name of a valid +** system call. +*/ +static const char *unixNextSystemCall(sqlcipher_sqlite3_vfs *p, const char *zName){ + int i = -1; -/* Return the current length of p in bytes */ -SQLITE_API int sqlcipher_sqlite3_str_length(sqlcipher_sqlite3_str *p){ - return p ? p->nChar : 0; + UNUSED_PARAMETER(p); + if( zName ){ + for(i=0; inChar==0 ) return 0; - p->zText[p->nChar] = 0; - return p->zText; -} +/* +** Do not accept any file descriptor less than this value, in order to avoid +** opening database file using file descriptors that are commonly used for +** standard input, output, and error. +*/ +#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR +# define SQLITE_MINIMUM_FILE_DESCRIPTOR 3 +#endif /* -** Reset an StrAccum string. Reclaim all malloced memory. +** Invoke open(). Do so multiple times, until it either succeeds or +** fails for some reason other than EINTR. +** +** If the file creation mode "m" is 0 then set it to the default for +** SQLite. The default is SQLITE_DEFAULT_FILE_PERMISSIONS (normally +** 0644) as modified by the system umask. If m is not 0, then +** make the file creation mode be exactly m ignoring the umask. +** +** The m parameter will be non-zero only when creating -wal, -journal, +** and -shm files. We want those files to have *exactly* the same +** permissions as their original database, unadulterated by the umask. +** In that way, if a database file is -rw-rw-rw or -rw-rw-r-, and a +** transaction crashes and leaves behind hot journals, then any +** process that is able to write to the database will also be able to +** recover the hot journals. */ -SQLITE_API void sqlcipher_sqlite3_str_reset(StrAccum *p){ - if( isMalloced(p) ){ - sqlcipher_sqlite3DbFree(p->db, p->zText); - p->printfFlags &= ~SQLITE_PRINTF_MALLOCED; +static int robust_open(const char *z, int f, mode_t m){ + int fd; + mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS; + while(1){ +#if defined(O_CLOEXEC) + fd = osOpen(z,f|O_CLOEXEC,m2); +#else + fd = osOpen(z,f,m2); +#endif + if( fd<0 ){ + if( errno==EINTR ) continue; + break; + } + if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break; + osClose(fd); + sqlcipher_sqlite3_log(SQLITE_WARNING, + "attempt to open \"%s\" as file descriptor %d", z, fd); + fd = -1; + if( osOpen("/dev/null", O_RDONLY, m)<0 ) break; } - p->nAlloc = 0; - p->nChar = 0; - p->zText = 0; + if( fd>=0 ){ + if( m!=0 ){ + struct stat statbuf; + if( osFstat(fd, &statbuf)==0 + && statbuf.st_size==0 + && (statbuf.st_mode&0777)!=m + ){ + osFchmod(fd, m); + } + } +#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) + osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); +#endif + } + return fd; } /* -** Initialize a string accumulator. +** Helper functions to obtain and relinquish the global mutex. The +** global mutex is used to protect the unixInodeInfo and +** vxworksFileId objects used by this file, all of which may be +** shared by multiple threads. ** -** p: The accumulator to be initialized. -** db: Pointer to a database connection. May be NULL. Lookaside -** memory is used if not NULL. db->mallocFailed is set appropriately -** when not NULL. -** zBase: An initial buffer. May be NULL in which case the initial buffer -** is malloced. -** n: Size of zBase in bytes. If total space requirements never exceed -** n then no memory allocations ever occur. -** mx: Maximum number of bytes to accumulate. If mx==0 then no memory -** allocations will ever occur. +** Function unixMutexHeld() is used to assert() that the global mutex +** is held when required. This function is only used as part of assert() +** statements. e.g. +** +** unixEnterMutex() +** assert( unixMutexHeld() ); +** unixEnterLeave() +** +** To prevent deadlock, the global unixBigLock must must be acquired +** before the unixInodeInfo.pLockMutex mutex, if both are held. It is +** OK to get the pLockMutex without holding unixBigLock first, but if +** that happens, the unixBigLock mutex must not be acquired until after +** pLockMutex is released. +** +** OK: enter(unixBigLock), enter(pLockInfo) +** OK: enter(unixBigLock) +** OK: enter(pLockInfo) +** ERROR: enter(pLockInfo), enter(unixBigLock) */ -SQLITE_PRIVATE void sqlcipher_sqlite3StrAccumInit(StrAccum *p, sqlcipher_sqlite3 *db, char *zBase, int n, int mx){ - p->zText = zBase; - p->db = db; - p->nAlloc = n; - p->mxAlloc = mx; - p->nChar = 0; - p->accError = 0; - p->printfFlags = 0; +static sqlcipher_sqlite3_mutex *unixBigLock = 0; +static void unixEnterMutex(void){ + assert( sqlcipher_sqlite3_mutex_notheld(unixBigLock) ); /* Not a recursive mutex */ + sqlcipher_sqlite3_mutex_enter(unixBigLock); } - -/* Allocate and initialize a new dynamic string object */ -SQLITE_API sqlcipher_sqlite3_str *sqlcipher_sqlite3_str_new(sqlcipher_sqlite3 *db){ - sqlcipher_sqlite3_str *p = sqlcipher_sqlite3_malloc64(sizeof(*p)); - if( p ){ - sqlcipher_sqlite3StrAccumInit(p, 0, 0, 0, - db ? db->aLimit[SQLITE_LIMIT_LENGTH] : SQLITE_MAX_LENGTH); - }else{ - p = &sqlcipher_sqlite3OomStr; +static void unixLeaveMutex(void){ + assert( sqlcipher_sqlite3_mutex_held(unixBigLock) ); + sqlcipher_sqlite3_mutex_leave(unixBigLock); +} +#ifdef SQLITE_DEBUG +static int unixMutexHeld(void) { + return sqlcipher_sqlite3_mutex_held(unixBigLock); +} +#endif + + +#ifdef SQLITE_HAVE_OS_TRACE +/* +** Helper function for printing out trace information from debugging +** binaries. This returns the string representation of the supplied +** integer lock-type. +*/ +static const char *azFileLock(int eFileLock){ + switch( eFileLock ){ + case NO_LOCK: return "NONE"; + case SHARED_LOCK: return "SHARED"; + case RESERVED_LOCK: return "RESERVED"; + case PENDING_LOCK: return "PENDING"; + case EXCLUSIVE_LOCK: return "EXCLUSIVE"; } - return p; + return "ERROR"; } +#endif +#ifdef SQLITE_LOCK_TRACE /* -** Print into memory obtained from sqliteMalloc(). Use the internal -** %-conversion extensions. +** Print out information about all locking operations. +** +** This routine is used for troubleshooting locks on multithreaded +** platforms. Enable by compiling with the -DSQLITE_LOCK_TRACE +** command-line option on the compiler. This code is normally +** turned off. */ -SQLITE_PRIVATE char *sqlcipher_sqlite3VMPrintf(sqlcipher_sqlite3 *db, const char *zFormat, va_list ap){ - char *z; - char zBase[SQLITE_PRINT_BUF_SIZE]; - StrAccum acc; - assert( db!=0 ); - sqlcipher_sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase), - db->aLimit[SQLITE_LIMIT_LENGTH]); - acc.printfFlags = SQLITE_PRINTF_INTERNAL; - sqlcipher_sqlite3_str_vappendf(&acc, zFormat, ap); - z = sqlcipher_sqlite3StrAccumFinish(&acc); - if( acc.accError==SQLITE_NOMEM ){ - sqlcipher_sqlite3OomFault(db); +static int lockTrace(int fd, int op, struct flock *p){ + char *zOpName, *zType; + int s; + int savedErrno; + if( op==F_GETLK ){ + zOpName = "GETLK"; + }else if( op==F_SETLK ){ + zOpName = "SETLK"; + }else{ + s = osFcntl(fd, op, p); + sqlcipher_sqlite3DebugPrintf("fcntl unknown %d %d %d\n", fd, op, s); + return s; } - return z; + if( p->l_type==F_RDLCK ){ + zType = "RDLCK"; + }else if( p->l_type==F_WRLCK ){ + zType = "WRLCK"; + }else if( p->l_type==F_UNLCK ){ + zType = "UNLCK"; + }else{ + assert( 0 ); + } + assert( p->l_whence==SEEK_SET ); + s = osFcntl(fd, op, p); + savedErrno = errno; + sqlcipher_sqlite3DebugPrintf("fcntl %d %d %s %s %d %d %d %d\n", + threadid, fd, zOpName, zType, (int)p->l_start, (int)p->l_len, + (int)p->l_pid, s); + if( s==(-1) && op==F_SETLK && (p->l_type==F_RDLCK || p->l_type==F_WRLCK) ){ + struct flock l2; + l2 = *p; + osFcntl(fd, F_GETLK, &l2); + if( l2.l_type==F_RDLCK ){ + zType = "RDLCK"; + }else if( l2.l_type==F_WRLCK ){ + zType = "WRLCK"; + }else if( l2.l_type==F_UNLCK ){ + zType = "UNLCK"; + }else{ + assert( 0 ); + } + sqlcipher_sqlite3DebugPrintf("fcntl-failure-reason: %s %d %d %d\n", + zType, (int)l2.l_start, (int)l2.l_len, (int)l2.l_pid); + } + errno = savedErrno; + return s; } +#undef osFcntl +#define osFcntl lockTrace +#endif /* SQLITE_LOCK_TRACE */ /* -** Print into memory obtained from sqliteMalloc(). Use the internal -** %-conversion extensions. +** Retry ftruncate() calls that fail due to EINTR +** +** All calls to ftruncate() within this file should be made through +** this wrapper. On the Android platform, bypassing the logic below +** could lead to a corrupt database. */ -SQLITE_PRIVATE char *sqlcipher_sqlite3MPrintf(sqlcipher_sqlite3 *db, const char *zFormat, ...){ - va_list ap; - char *z; - va_start(ap, zFormat); - z = sqlcipher_sqlite3VMPrintf(db, zFormat, ap); - va_end(ap); - return z; +static int robust_ftruncate(int h, sqlcipher_sqlite3_int64 sz){ + int rc; +#ifdef __ANDROID__ + /* On Android, ftruncate() always uses 32-bit offsets, even if + ** _FILE_OFFSET_BITS=64 is defined. This means it is unsafe to attempt to + ** truncate a file to any size larger than 2GiB. Silently ignore any + ** such attempts. */ + if( sz>(sqlcipher_sqlite3_int64)0x7FFFFFFF ){ + rc = SQLITE_OK; + }else +#endif + do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR ); + return rc; } /* -** Print into memory obtained from sqlcipher_sqlite3_malloc(). Omit the internal -** %-conversion extensions. +** This routine translates a standard POSIX errno code into something +** useful to the clients of the sqlcipher_sqlite3 functions. Specifically, it is +** intended to translate a variety of "try again" errors into SQLITE_BUSY +** and a variety of "please close the file descriptor NOW" errors into +** SQLITE_IOERR +** +** Errors during initialization of locks, or file system support for locks, +** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately. */ -SQLITE_API char *sqlcipher_sqlite3_vmprintf(const char *zFormat, va_list ap){ - char *z; - char zBase[SQLITE_PRINT_BUF_SIZE]; - StrAccum acc; +static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { + assert( (sqliteIOErr == SQLITE_IOERR_LOCK) || + (sqliteIOErr == SQLITE_IOERR_UNLOCK) || + (sqliteIOErr == SQLITE_IOERR_RDLOCK) || + (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ); + switch (posixError) { + case EACCES: + case EAGAIN: + case ETIMEDOUT: + case EBUSY: + case EINTR: + case ENOLCK: + /* random NFS retry error, unless during file system support + * introspection, in which it actually means what it says */ + return SQLITE_BUSY; -#ifdef SQLITE_ENABLE_API_ARMOR - if( zFormat==0 ){ - (void)SQLITE_MISUSE_BKPT; - return 0; + case EPERM: + return SQLITE_PERM; + + default: + return sqliteIOErr; } -#endif -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; -#endif - sqlcipher_sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH); - sqlcipher_sqlite3_str_vappendf(&acc, zFormat, ap); - z = sqlcipher_sqlite3StrAccumFinish(&acc); - return z; } + +/****************************************************************************** +****************** Begin Unique File ID Utility Used By VxWorks *************** +** +** On most versions of unix, we can get a unique ID for a file by concatenating +** the device number and the inode number. But this does not work on VxWorks. +** On VxWorks, a unique file id must be based on the canonical filename. +** +** A pointer to an instance of the following structure can be used as a +** unique file ID in VxWorks. Each instance of this structure contains +** a copy of the canonical filename. There is also a reference count. +** The structure is reclaimed when the number of pointers to it drops to +** zero. +** +** There are never very many files open at one time and lookups are not +** a performance-critical path, so it is sufficient to put these +** structures on a linked list. +*/ +struct vxworksFileId { + struct vxworksFileId *pNext; /* Next in a list of them all */ + int nRef; /* Number of references to this one */ + int nName; /* Length of the zCanonicalName[] string */ + char *zCanonicalName; /* Canonical filename */ +}; + +#if OS_VXWORKS /* -** Print into memory obtained from sqlcipher_sqlite3_malloc()(). Omit the internal -** %-conversion extensions. +** All unique filenames are held on a linked list headed by this +** variable: */ -SQLITE_API char *sqlcipher_sqlite3_mprintf(const char *zFormat, ...){ - va_list ap; - char *z; -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; -#endif - va_start(ap, zFormat); - z = sqlcipher_sqlite3_vmprintf(zFormat, ap); - va_end(ap); - return z; -} +static struct vxworksFileId *vxworksFileList = 0; /* -** sqlcipher_sqlite3_snprintf() works like snprintf() except that it ignores the -** current locale settings. This is important for SQLite because we -** are not able to use a "," as the decimal point in place of "." as -** specified by some locales. +** Simplify a filename into its canonical form +** by making the following changes: ** -** Oops: The first two arguments of sqlcipher_sqlite3_snprintf() are backwards -** from the snprintf() standard. Unfortunately, it is too late to change -** this without breaking compatibility, so we just have to live with the -** mistake. +** * removing any trailing and duplicate / +** * convert /./ into just / +** * convert /A/../ where A is any simple name into just / ** -** sqlcipher_sqlite3_vsnprintf() is the varargs version. +** Changes are made in-place. Return the new name length. +** +** The original filename is in z[0..n-1]. Return the number of +** characters in the simplified name. */ -SQLITE_API char *sqlcipher_sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){ - StrAccum acc; - if( n<=0 ) return zBuf; -#ifdef SQLITE_ENABLE_API_ARMOR - if( zBuf==0 || zFormat==0 ) { - (void)SQLITE_MISUSE_BKPT; - if( zBuf ) zBuf[0] = 0; - return zBuf; +static int vxworksSimplifyName(char *z, int n){ + int i, j; + while( n>1 && z[n-1]=='/' ){ n--; } + for(i=j=0; i0 && z[j-1]!='/' ){ j--; } + if( j>0 ){ j--; } + i += 2; + continue; + } + } + z[j++] = z[i]; } -#endif - sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, n, 0); - sqlcipher_sqlite3_str_vappendf(&acc, zFormat, ap); - zBuf[acc.nChar] = 0; - return zBuf; -} -SQLITE_API char *sqlcipher_sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ - char *z; - va_list ap; - va_start(ap,zFormat); - z = sqlcipher_sqlite3_vsnprintf(n, zBuf, zFormat, ap); - va_end(ap); - return z; + z[j] = 0; + return j; } /* -** This is the routine that actually formats the sqlcipher_sqlite3_log() message. -** We house it in a separate routine from sqlcipher_sqlite3_log() to avoid using -** stack space on small-stack systems when logging is disabled. +** Find a unique file ID for the given absolute pathname. Return +** a pointer to the vxworksFileId object. This pointer is the unique +** file ID. ** -** sqlcipher_sqlite3_log() must render into a static buffer. It cannot dynamically -** allocate memory because it might be called while the memory allocator -** mutex is held. +** The nRef field of the vxworksFileId object is incremented before +** the object is returned. A new vxworksFileId object is created +** and added to the global list if necessary. ** -** sqlcipher_sqlite3_str_vappendf() might ask for *temporary* memory allocations for -** certain format characters (%q) or for very large precisions or widths. -** Care must be taken that any sqlcipher_sqlite3_log() calls that occur while the -** memory mutex is held do not use these mechanisms. +** If a memory allocation error occurs, return NULL. */ -static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ - StrAccum acc; /* String accumulator */ - char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */ +static struct vxworksFileId *vxworksFindFileId(const char *zAbsoluteName){ + struct vxworksFileId *pNew; /* search key and new file ID */ + struct vxworksFileId *pCandidate; /* For looping over existing file IDs */ + int n; /* Length of zAbsoluteName string */ - sqlcipher_sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0); - sqlcipher_sqlite3_str_vappendf(&acc, zFormat, ap); - sqlcipher_sqlite3GlobalConfig.xLog(sqlcipher_sqlite3GlobalConfig.pLogArg, iErrCode, - sqlcipher_sqlite3StrAccumFinish(&acc)); -} + assert( zAbsoluteName[0]=='/' ); + n = (int)strlen(zAbsoluteName); + pNew = sqlcipher_sqlite3_malloc64( sizeof(*pNew) + (n+1) ); + if( pNew==0 ) return 0; + pNew->zCanonicalName = (char*)&pNew[1]; + memcpy(pNew->zCanonicalName, zAbsoluteName, n+1); + n = vxworksSimplifyName(pNew->zCanonicalName, n); -/* -** Format and write a message to the log if logging is enabled. -*/ -SQLITE_API void sqlcipher_sqlite3_log(int iErrCode, const char *zFormat, ...){ - va_list ap; /* Vararg list */ - if( sqlcipher_sqlite3GlobalConfig.xLog ){ - va_start(ap, zFormat); - renderLogMsg(iErrCode, zFormat, ap); - va_end(ap); + /* Search for an existing entry that matching the canonical name. + ** If found, increment the reference count and return a pointer to + ** the existing file ID. + */ + unixEnterMutex(); + for(pCandidate=vxworksFileList; pCandidate; pCandidate=pCandidate->pNext){ + if( pCandidate->nName==n + && memcmp(pCandidate->zCanonicalName, pNew->zCanonicalName, n)==0 + ){ + sqlcipher_sqlite3_free(pNew); + pCandidate->nRef++; + unixLeaveMutex(); + return pCandidate; + } } + + /* No match was found. We will make a new file ID */ + pNew->nRef = 1; + pNew->nName = n; + pNew->pNext = vxworksFileList; + vxworksFileList = pNew; + unixLeaveMutex(); + return pNew; } -#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) /* -** A version of printf() that understands %lld. Used for debugging. -** The printf() built into some versions of windows does not understand %lld -** and segfaults if you give it a long long int. +** Decrement the reference count on a vxworksFileId object. Free +** the object when the reference count reaches zero. */ -SQLITE_PRIVATE void sqlcipher_sqlite3DebugPrintf(const char *zFormat, ...){ - va_list ap; - StrAccum acc; - char zBuf[SQLITE_PRINT_BUF_SIZE*10]; - sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); - va_start(ap,zFormat); - sqlcipher_sqlite3_str_vappendf(&acc, zFormat, ap); - va_end(ap); - sqlcipher_sqlite3StrAccumFinish(&acc); -#ifdef SQLITE_OS_TRACE_PROC - { - extern void SQLITE_OS_TRACE_PROC(const char *zBuf, int nBuf); - SQLITE_OS_TRACE_PROC(zBuf, sizeof(zBuf)); +static void vxworksReleaseFileId(struct vxworksFileId *pId){ + unixEnterMutex(); + assert( pId->nRef>0 ); + pId->nRef--; + if( pId->nRef==0 ){ + struct vxworksFileId **pp; + for(pp=&vxworksFileList; *pp && *pp!=pId; pp = &((*pp)->pNext)){} + assert( *pp==pId ); + *pp = pId->pNext; + sqlcipher_sqlite3_free(pId); } -#else - fprintf(stdout,"%s", zBuf); - fflush(stdout); -#endif + unixLeaveMutex(); } -#endif +#endif /* OS_VXWORKS */ +/*************** End of Unique File ID Utility Used By VxWorks **************** +******************************************************************************/ + +/****************************************************************************** +*************************** Posix Advisory Locking **************************** +** +** POSIX advisory locks are broken by design. ANSI STD 1003.1 (1996) +** section 6.5.2.2 lines 483 through 490 specify that when a process +** sets or clears a lock, that operation overrides any prior locks set +** by the same process. It does not explicitly say so, but this implies +** that it overrides locks set by the same process using a different +** file descriptor. Consider this test case: +** +** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644); +** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644); +** +** Suppose ./file1 and ./file2 are really the same file (because +** one is a hard or symbolic link to the other) then if you set +** an exclusive lock on fd1, then try to get an exclusive lock +** on fd2, it works. I would have expected the second lock to +** fail since there was already a lock on the file due to fd1. +** But not so. Since both locks came from the same process, the +** second overrides the first, even though they were on different +** file descriptors opened on different file names. +** +** This means that we cannot use POSIX locks to synchronize file access +** among competing threads of the same process. POSIX locks will work fine +** to synchronize access for threads in separate processes, but not +** threads within the same process. +** +** To work around the problem, SQLite has to manage file locks internally +** on its own. Whenever a new database is opened, we have to find the +** specific inode of the database file (the inode is determined by the +** st_dev and st_ino fields of the stat structure that fstat() fills in) +** and check for locks already existing on that inode. When locks are +** created or removed, we have to look at our own internal record of the +** locks to see if another thread has previously set a lock on that same +** inode. +** +** (Aside: The use of inode numbers as unique IDs does not work on VxWorks. +** For VxWorks, we have to use the alternative unique ID system based on +** canonical filename and implemented in the previous division.) +** +** The sqlcipher_sqlite3_file structure for POSIX is no longer just an integer file +** descriptor. It is now a structure that holds the integer file +** descriptor and a pointer to a structure that describes the internal +** locks on the corresponding inode. There is one locking structure +** per inode, so if the same inode is opened twice, both unixFile structures +** point to the same locking structure. The locking structure keeps +** a reference count (so we will know when to delete it) and a "cnt" +** field that tells us its internal lock status. cnt==0 means the +** file is unlocked. cnt==-1 means the file has an exclusive lock. +** cnt>0 means there are cnt shared locks on the file. +** +** Any attempt to lock or unlock a file first checks the locking +** structure. The fcntl() system call is only invoked to set a +** POSIX lock if the internal lock structure transitions between +** a locked and an unlocked state. +** +** But wait: there are yet more problems with POSIX advisory locks. +** +** If you close a file descriptor that points to a file that has locks, +** all locks on that file that are owned by the current process are +** released. To work around this problem, each unixInodeInfo object +** maintains a count of the number of pending locks on tha inode. +** When an attempt is made to close an unixFile, if there are +** other unixFile open on the same inode that are holding locks, the call +** to close() the file descriptor is deferred until all of the locks clear. +** The unixInodeInfo structure keeps a list of file descriptors that need to +** be closed and that list is walked (and cleared) when the last lock +** clears. +** +** Yet another problem: LinuxThreads do not play well with posix locks. +** +** Many older versions of linux use the LinuxThreads library which is +** not posix compliant. Under LinuxThreads, a lock created by thread +** A cannot be modified or overridden by a different thread B. +** Only thread A can modify the lock. Locking behavior is correct +** if the appliation uses the newer Native Posix Thread Library (NPTL) +** on linux - with NPTL a lock created by thread A can override locks +** in thread B. But there is no way to know at compile-time which +** threading library is being used. So there is no way to know at +** compile-time whether or not thread A can override locks on thread B. +** One has to do a run-time check to discover the behavior of the +** current process. +** +** SQLite used to support LinuxThreads. But support for LinuxThreads +** was dropped beginning with version 3.7.0. SQLite will still work with +** LinuxThreads provided that (1) there is no more than one connection +** per database file in the same process and (2) database connections +** do not move across threads. +*/ /* -** variable-argument wrapper around sqlcipher_sqlite3_str_vappendf(). The bFlags argument -** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats. +** An instance of the following structure serves as the key used +** to locate a particular unixInodeInfo object. */ -SQLITE_API void sqlcipher_sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ - va_list ap; - va_start(ap,zFormat); - sqlcipher_sqlite3_str_vappendf(p, zFormat, ap); - va_end(ap); -} +struct unixFileId { + dev_t dev; /* Device number */ +#if OS_VXWORKS + struct vxworksFileId *pId; /* Unique file ID for vxworks. */ +#else + /* We are told that some versions of Android contain a bug that + ** sizes ino_t at only 32-bits instead of 64-bits. (See + ** https://android-review.googlesource.com/#/c/115351/3/dist/sqlcipher_sqlite3.c) + ** To work around this, always allocate 64-bits for the inode number. + ** On small machines that only have 32-bit inodes, this wastes 4 bytes, + ** but that should not be a big deal. */ + /* WAS: ino_t ino; */ + u64 ino; /* Inode number */ +#endif +}; -/************** End of printf.c **********************************************/ -/************** Begin file treeview.c ****************************************/ /* -** 2015-06-08 +** An instance of the following structure is allocated for each open +** inode. ** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** A single inode can have multiple file descriptors, so each unixFile +** structure contains a pointer to an instance of this object and this +** object keeps a count of the number of unixFile pointing to it. ** -** 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. +** Mutex rules: ** -************************************************************************* +** (1) Only the pLockMutex mutex must be held in order to read or write +** any of the locking fields: +** nShared, nLock, eFileLock, bProcessLock, pUnused ** -** This file contains C code to implement the TreeView debugging routines. -** These routines print a parse tree to standard output for debugging and -** analysis. +** (2) When nRef>0, then the following fields are unchanging and can +** be read (but not written) without holding any mutex: +** fileId, pLockMutex ** -** The interfaces in this file is only available when compiling -** with SQLITE_DEBUG. +** (3) With the exceptions above, all the fields may only be read +** or written while holding the global unixBigLock mutex. +** +** Deadlock prevention: The global unixBigLock mutex may not +** be acquired while holding the pLockMutex mutex. If both unixBigLock +** and pLockMutex are needed, then unixBigLock must be acquired first. */ -/* #include "sqliteInt.h" */ -#ifdef SQLITE_DEBUG +struct unixInodeInfo { + struct unixFileId fileId; /* The lookup key */ + sqlcipher_sqlite3_mutex *pLockMutex; /* Hold this mutex for... */ + int nShared; /* Number of SHARED locks held */ + int nLock; /* Number of outstanding file locks */ + unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ + unsigned char bProcessLock; /* An exclusive process lock is held */ + UnixUnusedFd *pUnused; /* Unused file descriptors to close */ + int nRef; /* Number of pointers to this structure */ + unixShmNode *pShmNode; /* Shared memory associated with this inode */ + unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ + unixInodeInfo *pPrev; /* .... doubly linked */ +#if SQLITE_ENABLE_LOCKING_STYLE + unsigned long long sharedByte; /* for AFP simulated shared lock */ +#endif +#if OS_VXWORKS + sem_t *pSem; /* Named POSIX semaphore */ + char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */ +#endif +}; /* -** Add a new subitem to the tree. The moreToFollow flag indicates that this -** is not the last item in the tree. +** A lists of all unixInodeInfo objects. +** +** Must hold unixBigLock in order to read or write this variable. */ -static TreeView *sqlcipher_sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){ - if( p==0 ){ - p = sqlcipher_sqlite3_malloc64( sizeof(*p) ); - if( p==0 ) return 0; - memset(p, 0, sizeof(*p)); - }else{ - p->iLevel++; - } - assert( moreToFollow==0 || moreToFollow==1 ); - if( p->iLevelbLine) ) p->bLine[p->iLevel] = moreToFollow; - return p; -} +static unixInodeInfo *inodeList = 0; /* All unixInodeInfo objects */ +#ifdef SQLITE_DEBUG /* -** Finished with one layer of the tree +** True if the inode mutex (on the unixFile.pFileMutex field) is held, or not. +** This routine is used only within assert() to help verify correct mutex +** usage. */ -static void sqlcipher_sqlite3TreeViewPop(TreeView *p){ - if( p==0 ) return; - p->iLevel--; - if( p->iLevel<0 ) sqlcipher_sqlite3_free(p); +int unixFileMutexHeld(unixFile *pFile){ + assert( pFile->pInode ); + return sqlcipher_sqlite3_mutex_held(pFile->pInode->pLockMutex); +} +int unixFileMutexNotheld(unixFile *pFile){ + assert( pFile->pInode ); + return sqlcipher_sqlite3_mutex_notheld(pFile->pInode->pLockMutex); } +#endif /* -** Generate a single line of output for the tree, with a prefix that contains -** all the appropriate tree lines +** +** This function - unixLogErrorAtLine(), is only ever called via the macro +** unixLogError(). +** +** It is invoked after an error occurs in an OS function and errno has been +** set. It logs a message using sqlcipher_sqlite3_log() containing the current value of +** errno and, if possible, the human-readable equivalent from strerror() or +** strerror_r(). +** +** The first argument passed to the macro should be the error code that +** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). +** The two subsequent arguments should be the name of the OS function that +** failed (e.g. "unlink", "open") and the associated file-system path, +** if any. */ -static void sqlcipher_sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ - va_list ap; - int i; - StrAccum acc; - char zBuf[500]; - sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); - if( p ){ - for(i=0; iiLevel && ibLine)-1; i++){ - sqlcipher_sqlite3_str_append(&acc, p->bLine[i] ? "| " : " ", 4); - } - sqlcipher_sqlite3_str_append(&acc, p->bLine[i] ? "|-- " : "'-- ", 4); - } - if( zFormat!=0 ){ - va_start(ap, zFormat); - sqlcipher_sqlite3_str_vappendf(&acc, zFormat, ap); - va_end(ap); - assert( acc.nChar>0 || acc.accError ); - sqlcipher_sqlite3_str_append(&acc, "\n", 1); - } - sqlcipher_sqlite3StrAccumFinish(&acc); - fprintf(stdout,"%s", zBuf); - fflush(stdout); +#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__) +static int unixLogErrorAtLine( + int errcode, /* SQLite error code */ + const char *zFunc, /* Name of OS function that failed */ + const char *zPath, /* File path associated with error */ + int iLine /* Source line number where error occurred */ +){ + char *zErr; /* Message from strerror() or equivalent */ + int iErrno = errno; /* Saved syscall error number */ + + /* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use + ** the strerror() function to obtain the human-readable error message + ** equivalent to errno. Otherwise, use strerror_r(). + */ +#if SQLITE_THREADSAFE && defined(HAVE_STRERROR_R) + char aErr[80]; + memset(aErr, 0, sizeof(aErr)); + zErr = aErr; + + /* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined, + ** assume that the system provides the GNU version of strerror_r() that + ** returns a pointer to a buffer containing the error message. That pointer + ** may point to aErr[], or it may point to some static storage somewhere. + ** Otherwise, assume that the system provides the POSIX version of + ** strerror_r(), which always writes an error message into aErr[]. + ** + ** If the code incorrectly assumes that it is the POSIX version that is + ** available, the error message will often be an empty string. Not a + ** huge problem. Incorrectly concluding that the GNU version is available + ** could lead to a segfault though. + */ +#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) + zErr = +# endif + strerror_r(iErrno, aErr, sizeof(aErr)-1); + +#elif SQLITE_THREADSAFE + /* This is a threadsafe build, but strerror_r() is not available. */ + zErr = ""; +#else + /* Non-threadsafe build, use strerror(). */ + zErr = strerror(iErrno); +#endif + + if( zPath==0 ) zPath = ""; + sqlcipher_sqlite3_log(errcode, + "os_unix.c:%d: (%d) %s(%s) - %s", + iLine, iErrno, zFunc, zPath, zErr + ); + + return errcode; } /* -** Shorthand for starting a new tree item that consists of a single label +** Close a file descriptor. +** +** We assume that close() almost always works, since it is only in a +** very sick application or on a very sick platform that it might fail. +** If it does fail, simply leak the file descriptor, but do log the +** error. +** +** Note that it is not safe to retry close() after EINTR since the +** file descriptor might have already been reused by another thread. +** So we don't even try to recover from an EINTR. Just log the error +** and move on. */ -static void sqlcipher_sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){ - p = sqlcipher_sqlite3TreeViewPush(p, moreFollows); - sqlcipher_sqlite3TreeViewLine(p, "%s", zLabel); +static void robust_close(unixFile *pFile, int h, int lineno){ + if( osClose(h) ){ + unixLogErrorAtLine(SQLITE_IOERR_CLOSE, "close", + pFile ? pFile->zPath : 0, lineno); + } } /* -** Generate a human-readable description of a WITH clause. +** Set the pFile->lastErrno. Do this in a subroutine as that provides +** a convenient place to set a breakpoint. */ -SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){ - int i; - if( pWith==0 ) return; - if( pWith->nCte==0 ) return; - if( pWith->pOuter ){ - sqlcipher_sqlite3TreeViewLine(pView, "WITH (0x%p, pOuter=0x%p)",pWith,pWith->pOuter); - }else{ - sqlcipher_sqlite3TreeViewLine(pView, "WITH (0x%p)", pWith); - } - if( pWith->nCte>0 ){ - pView = sqlcipher_sqlite3TreeViewPush(pView, 1); - for(i=0; inCte; i++){ - StrAccum x; - char zLine[1000]; - const struct Cte *pCte = &pWith->a[i]; - sqlcipher_sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); - sqlcipher_sqlite3_str_appendf(&x, "%s", pCte->zName); - if( pCte->pCols && pCte->pCols->nExpr>0 ){ - char cSep = '('; - int j; - for(j=0; jpCols->nExpr; j++){ - sqlcipher_sqlite3_str_appendf(&x, "%c%s", cSep, pCte->pCols->a[j].zEName); - cSep = ','; - } - sqlcipher_sqlite3_str_appendf(&x, ")"); - } - sqlcipher_sqlite3_str_appendf(&x, " AS"); - sqlcipher_sqlite3StrAccumFinish(&x); - sqlcipher_sqlite3TreeViewItem(pView, zLine, inCte-1); - sqlcipher_sqlite3TreeViewSelect(pView, pCte->pSelect, 0); - sqlcipher_sqlite3TreeViewPop(pView); - } - sqlcipher_sqlite3TreeViewPop(pView); - } +static void storeLastErrno(unixFile *pFile, int error){ + pFile->lastErrno = error; } /* -** Generate a human-readable description of a SrcList object. +** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. */ -SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){ - int i; - for(i=0; inSrc; i++){ - const struct SrcList_item *pItem = &pSrc->a[i]; - StrAccum x; - char zLine[100]; - sqlcipher_sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); - sqlcipher_sqlite3_str_appendf(&x, "{%d:*}", pItem->iCursor); - if( pItem->zDatabase ){ - sqlcipher_sqlite3_str_appendf(&x, " %s.%s", pItem->zDatabase, pItem->zName); - }else if( pItem->zName ){ - sqlcipher_sqlite3_str_appendf(&x, " %s", pItem->zName); - } - if( pItem->pTab ){ - sqlcipher_sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx", - pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed); - } - if( pItem->zAlias ){ - sqlcipher_sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias); - } - if( pItem->fg.jointype & JT_LEFT ){ - sqlcipher_sqlite3_str_appendf(&x, " LEFT-JOIN"); - } - if( pItem->fg.fromDDL ){ - sqlcipher_sqlite3_str_appendf(&x, " DDL"); - } - sqlcipher_sqlite3StrAccumFinish(&x); - sqlcipher_sqlite3TreeViewItem(pView, zLine, inSrc-1); - if( pItem->pSelect ){ - sqlcipher_sqlite3TreeViewSelect(pView, pItem->pSelect, 0); - } - if( pItem->fg.isTabFunc ){ - sqlcipher_sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); - } - sqlcipher_sqlite3TreeViewPop(pView); +static void closePendingFds(unixFile *pFile){ + unixInodeInfo *pInode = pFile->pInode; + UnixUnusedFd *p; + UnixUnusedFd *pNext; + assert( unixFileMutexHeld(pFile) ); + for(p=pInode->pUnused; p; p=pNext){ + pNext = p->pNext; + robust_close(pFile, p->fd, __LINE__); + sqlcipher_sqlite3_free(p); } + pInode->pUnused = 0; } /* -** Generate a human-readable description of a Select object. +** Release a unixInodeInfo structure previously allocated by findInodeInfo(). +** +** The global mutex must be held when this routine is called, but the mutex +** on the inode being deleted must NOT be held. */ -SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){ - int n = 0; - int cnt = 0; - if( p==0 ){ - sqlcipher_sqlite3TreeViewLine(pView, "nil-SELECT"); - return; - } - pView = sqlcipher_sqlite3TreeViewPush(pView, moreToFollow); - if( p->pWith ){ - sqlcipher_sqlite3TreeViewWith(pView, p->pWith, 1); - cnt = 1; - sqlcipher_sqlite3TreeViewPush(pView, 1); - } - do{ - if( p->selFlags & SF_WhereBegin ){ - sqlcipher_sqlite3TreeViewLine(pView, "sqlcipher_sqlite3WhereBegin()"); - }else{ - sqlcipher_sqlite3TreeViewLine(pView, - "SELECT%s%s (%u/%p) selFlags=0x%x nSelectRow=%d", - ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""), - ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), - p->selId, p, p->selFlags, - (int)p->nSelectRow - ); - } - if( cnt++ ) sqlcipher_sqlite3TreeViewPop(pView); - if( p->pPrior ){ - n = 1000; - }else{ - n = 0; - if( p->pSrc && p->pSrc->nSrc ) n++; - if( p->pWhere ) n++; - if( p->pGroupBy ) n++; - if( p->pHaving ) n++; - if( p->pOrderBy ) n++; - if( p->pLimit ) n++; -#ifndef SQLITE_OMIT_WINDOWFUNC - if( p->pWin ) n++; - if( p->pWinDefn ) n++; -#endif - } - if( p->pEList ){ - sqlcipher_sqlite3TreeViewExprList(pView, p->pEList, n>0, "result-set"); - } - n--; -#ifndef SQLITE_OMIT_WINDOWFUNC - if( p->pWin ){ - Window *pX; - pView = sqlcipher_sqlite3TreeViewPush(pView, (n--)>0); - sqlcipher_sqlite3TreeViewLine(pView, "window-functions"); - for(pX=p->pWin; pX; pX=pX->pNextWin){ - sqlcipher_sqlite3TreeViewWinFunc(pView, pX, pX->pNextWin!=0); - } - sqlcipher_sqlite3TreeViewPop(pView); - } -#endif - if( p->pSrc && p->pSrc->nSrc ){ - pView = sqlcipher_sqlite3TreeViewPush(pView, (n--)>0); - sqlcipher_sqlite3TreeViewLine(pView, "FROM"); - sqlcipher_sqlite3TreeViewSrcList(pView, p->pSrc); - sqlcipher_sqlite3TreeViewPop(pView); - } - if( p->pWhere ){ - sqlcipher_sqlite3TreeViewItem(pView, "WHERE", (n--)>0); - sqlcipher_sqlite3TreeViewExpr(pView, p->pWhere, 0); - sqlcipher_sqlite3TreeViewPop(pView); - } - if( p->pGroupBy ){ - sqlcipher_sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY"); - } - if( p->pHaving ){ - sqlcipher_sqlite3TreeViewItem(pView, "HAVING", (n--)>0); - sqlcipher_sqlite3TreeViewExpr(pView, p->pHaving, 0); - sqlcipher_sqlite3TreeViewPop(pView); - } -#ifndef SQLITE_OMIT_WINDOWFUNC - if( p->pWinDefn ){ - Window *pX; - sqlcipher_sqlite3TreeViewItem(pView, "WINDOW", (n--)>0); - for(pX=p->pWinDefn; pX; pX=pX->pNextWin){ - sqlcipher_sqlite3TreeViewWindow(pView, pX, pX->pNextWin!=0); - } - sqlcipher_sqlite3TreeViewPop(pView); - } -#endif - if( p->pOrderBy ){ - sqlcipher_sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY"); - } - if( p->pLimit ){ - sqlcipher_sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); - sqlcipher_sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0); - if( p->pLimit->pRight ){ - sqlcipher_sqlite3TreeViewItem(pView, "OFFSET", (n--)>0); - sqlcipher_sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0); - sqlcipher_sqlite3TreeViewPop(pView); +static void releaseInodeInfo(unixFile *pFile){ + unixInodeInfo *pInode = pFile->pInode; + assert( unixMutexHeld() ); + assert( unixFileMutexNotheld(pFile) ); + if( ALWAYS(pInode) ){ + pInode->nRef--; + if( pInode->nRef==0 ){ + assert( pInode->pShmNode==0 ); + sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); + closePendingFds(pFile); + sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); + if( pInode->pPrev ){ + assert( pInode->pPrev->pNext==pInode ); + pInode->pPrev->pNext = pInode->pNext; + }else{ + assert( inodeList==pInode ); + inodeList = pInode->pNext; } - sqlcipher_sqlite3TreeViewPop(pView); - } - if( p->pPrior ){ - const char *zOp = "UNION"; - switch( p->op ){ - case TK_ALL: zOp = "UNION ALL"; break; - case TK_INTERSECT: zOp = "INTERSECT"; break; - case TK_EXCEPT: zOp = "EXCEPT"; break; + if( pInode->pNext ){ + assert( pInode->pNext->pPrev==pInode ); + pInode->pNext->pPrev = pInode->pPrev; } - sqlcipher_sqlite3TreeViewItem(pView, zOp, 1); + sqlcipher_sqlite3_mutex_free(pInode->pLockMutex); + sqlcipher_sqlite3_free(pInode); } - p = p->pPrior; - }while( p!=0 ); - sqlcipher_sqlite3TreeViewPop(pView); + } } -#ifndef SQLITE_OMIT_WINDOWFUNC /* -** Generate a description of starting or stopping bounds +** Given a file descriptor, locate the unixInodeInfo object that +** describes that file descriptor. Create a new one if necessary. The +** return value might be uninitialized if an error occurs. +** +** The global mutex must held when calling this routine. +** +** Return an appropriate error code. */ -SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewBound( - TreeView *pView, /* View context */ - u8 eBound, /* UNBOUNDED, CURRENT, PRECEDING, FOLLOWING */ - Expr *pExpr, /* Value for PRECEDING or FOLLOWING */ - u8 moreToFollow /* True if more to follow */ +static int findInodeInfo( + unixFile *pFile, /* Unix file with file desc used in the key */ + unixInodeInfo **ppInode /* Return the unixInodeInfo object here */ ){ - switch( eBound ){ - case TK_UNBOUNDED: { - sqlcipher_sqlite3TreeViewItem(pView, "UNBOUNDED", moreToFollow); - sqlcipher_sqlite3TreeViewPop(pView); - break; - } - case TK_CURRENT: { - sqlcipher_sqlite3TreeViewItem(pView, "CURRENT", moreToFollow); - sqlcipher_sqlite3TreeViewPop(pView); - break; - } - case TK_PRECEDING: { - sqlcipher_sqlite3TreeViewItem(pView, "PRECEDING", moreToFollow); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr, 0); - sqlcipher_sqlite3TreeViewPop(pView); - break; + int rc; /* System call return code */ + int fd; /* The file descriptor for pFile */ + struct unixFileId fileId; /* Lookup key for the unixInodeInfo */ + struct stat statbuf; /* Low-level file information */ + unixInodeInfo *pInode = 0; /* Candidate unixInodeInfo object */ + + assert( unixMutexHeld() ); + + /* Get low-level information about the file that we can used to + ** create a unique name for the file. + */ + fd = pFile->h; + rc = osFstat(fd, &statbuf); + if( rc!=0 ){ + storeLastErrno(pFile, errno); +#if defined(EOVERFLOW) && defined(SQLITE_DISABLE_LFS) + if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS; +#endif + return SQLITE_IOERR; + } + +#ifdef __APPLE__ + /* On OS X on an msdos filesystem, the inode number is reported + ** incorrectly for zero-size files. See ticket #3260. To work + ** around this problem (we consider it a bug in OS X, not SQLite) + ** we always increase the file size to 1 by writing a single byte + ** prior to accessing the inode number. The one byte written is + ** an ASCII 'S' character which also happens to be the first byte + ** in the header of every SQLite database. In this way, if there + ** is a race condition such that another thread has already populated + ** the first page of the database, no damage is done. + */ + if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){ + do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); + if( rc!=1 ){ + storeLastErrno(pFile, errno); + return SQLITE_IOERR; } - case TK_FOLLOWING: { - sqlcipher_sqlite3TreeViewItem(pView, "FOLLOWING", moreToFollow); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr, 0); - sqlcipher_sqlite3TreeViewPop(pView); - break; + rc = osFstat(fd, &statbuf); + if( rc!=0 ){ + storeLastErrno(pFile, errno); + return SQLITE_IOERR; } } -} -#endif /* SQLITE_OMIT_WINDOWFUNC */ +#endif -#ifndef SQLITE_OMIT_WINDOWFUNC -/* -** Generate a human-readable explanation for a Window object -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){ - int nElement = 0; - if( pWin->pFilter ){ - sqlcipher_sqlite3TreeViewItem(pView, "FILTER", 1); - sqlcipher_sqlite3TreeViewExpr(pView, pWin->pFilter, 0); - sqlcipher_sqlite3TreeViewPop(pView); - } - pView = sqlcipher_sqlite3TreeViewPush(pView, more); - if( pWin->zName ){ - sqlcipher_sqlite3TreeViewLine(pView, "OVER %s (%p)", pWin->zName, pWin); - }else{ - sqlcipher_sqlite3TreeViewLine(pView, "OVER (%p)", pWin); - } - if( pWin->zBase ) nElement++; - if( pWin->pOrderBy ) nElement++; - if( pWin->eFrmType ) nElement++; - if( pWin->eExclude ) nElement++; - if( pWin->zBase ){ - sqlcipher_sqlite3TreeViewPush(pView, (--nElement)>0); - sqlcipher_sqlite3TreeViewLine(pView, "window: %s", pWin->zBase); - sqlcipher_sqlite3TreeViewPop(pView); - } - if( pWin->pPartition ){ - sqlcipher_sqlite3TreeViewExprList(pView, pWin->pPartition, nElement>0,"PARTITION-BY"); - } - if( pWin->pOrderBy ){ - sqlcipher_sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY"); - } - if( pWin->eFrmType ){ - char zBuf[30]; - const char *zFrmType = "ROWS"; - if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE"; - if( pWin->eFrmType==TK_GROUPS ) zFrmType = "GROUPS"; - sqlcipher_sqlite3_snprintf(sizeof(zBuf),zBuf,"%s%s",zFrmType, - pWin->bImplicitFrame ? " (implied)" : ""); - sqlcipher_sqlite3TreeViewItem(pView, zBuf, (--nElement)>0); - sqlcipher_sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1); - sqlcipher_sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0); - sqlcipher_sqlite3TreeViewPop(pView); + memset(&fileId, 0, sizeof(fileId)); + fileId.dev = statbuf.st_dev; +#if OS_VXWORKS + fileId.pId = pFile->pId; +#else + fileId.ino = (u64)statbuf.st_ino; +#endif + assert( unixMutexHeld() ); + pInode = inodeList; + while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){ + pInode = pInode->pNext; } - if( pWin->eExclude ){ - char zBuf[30]; - const char *zExclude; - switch( pWin->eExclude ){ - case TK_NO: zExclude = "NO OTHERS"; break; - case TK_CURRENT: zExclude = "CURRENT ROW"; break; - case TK_GROUP: zExclude = "GROUP"; break; - case TK_TIES: zExclude = "TIES"; break; - default: - sqlcipher_sqlite3_snprintf(sizeof(zBuf),zBuf,"invalid(%d)", pWin->eExclude); - zExclude = zBuf; - break; + if( pInode==0 ){ + pInode = sqlcipher_sqlite3_malloc64( sizeof(*pInode) ); + if( pInode==0 ){ + return SQLITE_NOMEM_BKPT; } - sqlcipher_sqlite3TreeViewPush(pView, 0); - sqlcipher_sqlite3TreeViewLine(pView, "EXCLUDE %s", zExclude); - sqlcipher_sqlite3TreeViewPop(pView); + memset(pInode, 0, sizeof(*pInode)); + memcpy(&pInode->fileId, &fileId, sizeof(fileId)); + if( sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ + pInode->pLockMutex = sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pInode->pLockMutex==0 ){ + sqlcipher_sqlite3_free(pInode); + return SQLITE_NOMEM_BKPT; + } + } + pInode->nRef = 1; + assert( unixMutexHeld() ); + pInode->pNext = inodeList; + pInode->pPrev = 0; + if( inodeList ) inodeList->pPrev = pInode; + inodeList = pInode; + }else{ + pInode->nRef++; } - sqlcipher_sqlite3TreeViewPop(pView); + *ppInode = pInode; + return SQLITE_OK; } -#endif /* SQLITE_OMIT_WINDOWFUNC */ -#ifndef SQLITE_OMIT_WINDOWFUNC /* -** Generate a human-readable explanation for a Window Function object +** Return TRUE if pFile has been renamed or unlinked since it was first opened. */ -SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin, u8 more){ - pView = sqlcipher_sqlite3TreeViewPush(pView, more); - sqlcipher_sqlite3TreeViewLine(pView, "WINFUNC %s(%d)", - pWin->pFunc->zName, pWin->pFunc->nArg); - sqlcipher_sqlite3TreeViewWindow(pView, pWin, 0); - sqlcipher_sqlite3TreeViewPop(pView); +static int fileHasMoved(unixFile *pFile){ +#if OS_VXWORKS + return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId; +#else + struct stat buf; + return pFile->pInode!=0 && + (osStat(pFile->zPath, &buf)!=0 + || (u64)buf.st_ino!=pFile->pInode->fileId.ino); +#endif } -#endif /* SQLITE_OMIT_WINDOWFUNC */ + /* -** Generate a human-readable explanation of an expression tree. +** Check a unixFile that is a database. Verify the following: +** +** (1) There is exactly one hard link on the file +** (2) The file is not a symbolic link +** (3) The file has not been renamed or unlinked +** +** Issue sqlcipher_sqlite3_log(SQLITE_WARNING,...) messages if anything is not right. */ -SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ - const char *zBinOp = 0; /* Binary operator */ - const char *zUniOp = 0; /* Unary operator */ - char zFlgs[200]; - pView = sqlcipher_sqlite3TreeViewPush(pView, moreToFollow); - if( pExpr==0 ){ - sqlcipher_sqlite3TreeViewLine(pView, "nil"); - sqlcipher_sqlite3TreeViewPop(pView); +static void verifyDbFile(unixFile *pFile){ + struct stat buf; + int rc; + + /* These verifications occurs for the main database only */ + if( pFile->ctrlFlags & UNIXFILE_NOLOCK ) return; + + rc = osFstat(pFile->h, &buf); + if( rc!=0 ){ + sqlcipher_sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath); return; } - if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){ - StrAccum x; - sqlcipher_sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0); - sqlcipher_sqlite3_str_appendf(&x, " fg.af=%x.%c", - pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n'); - if( ExprHasProperty(pExpr, EP_FromJoin) ){ - sqlcipher_sqlite3_str_appendf(&x, " iRJT=%d", pExpr->iRightJoinTable); - } - if( ExprHasProperty(pExpr, EP_FromDDL) ){ - sqlcipher_sqlite3_str_appendf(&x, " DDL"); - } - if( ExprHasVVAProperty(pExpr, EP_Immutable) ){ - sqlcipher_sqlite3_str_appendf(&x, " IMMUTABLE"); - } - sqlcipher_sqlite3StrAccumFinish(&x); - }else{ - zFlgs[0] = 0; + if( buf.st_nlink==0 ){ + sqlcipher_sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath); + return; } - switch( pExpr->op ){ - case TK_AGG_COLUMN: { - sqlcipher_sqlite3TreeViewLine(pView, "AGG{%d:%d}%s", - pExpr->iTable, pExpr->iColumn, zFlgs); - break; - } - case TK_COLUMN: { - if( pExpr->iTable<0 ){ - /* This only happens when coding check constraints */ - char zOp2[16]; - if( pExpr->op2 ){ - sqlcipher_sqlite3_snprintf(sizeof(zOp2),zOp2," op2=0x%02x",pExpr->op2); - }else{ - zOp2[0] = 0; - } - sqlcipher_sqlite3TreeViewLine(pView, "COLUMN(%d)%s%s", - pExpr->iColumn, zFlgs, zOp2); - }else{ - sqlcipher_sqlite3TreeViewLine(pView, "{%d:%d} pTab=%p%s", - pExpr->iTable, pExpr->iColumn, - pExpr->y.pTab, zFlgs); - } - if( ExprHasProperty(pExpr, EP_FixedCol) ){ - sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); - } - break; - } - case TK_INTEGER: { - if( pExpr->flags & EP_IntValue ){ - sqlcipher_sqlite3TreeViewLine(pView, "%d", pExpr->u.iValue); - }else{ - sqlcipher_sqlite3TreeViewLine(pView, "%s", pExpr->u.zToken); - } - break; - } -#ifndef SQLITE_OMIT_FLOATING_POINT - case TK_FLOAT: { - sqlcipher_sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); - break; - } -#endif - case TK_STRING: { - sqlcipher_sqlite3TreeViewLine(pView,"%Q", pExpr->u.zToken); - break; - } - case TK_NULL: { - sqlcipher_sqlite3TreeViewLine(pView,"NULL"); - break; - } - case TK_TRUEFALSE: { - sqlcipher_sqlite3TreeViewLine(pView, - sqlcipher_sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE"); - break; - } -#ifndef SQLITE_OMIT_BLOB_LITERAL - case TK_BLOB: { - sqlcipher_sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); - break; - } -#endif - case TK_VARIABLE: { - sqlcipher_sqlite3TreeViewLine(pView,"VARIABLE(%s,%d)", - pExpr->u.zToken, pExpr->iColumn); - break; - } - case TK_REGISTER: { - sqlcipher_sqlite3TreeViewLine(pView,"REGISTER(%d)", pExpr->iTable); - break; - } - case TK_ID: { - sqlcipher_sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken); - break; - } -#ifndef SQLITE_OMIT_CAST - case TK_CAST: { - /* Expressions of the form: CAST(pLeft AS token) */ - sqlcipher_sqlite3TreeViewLine(pView,"CAST %Q", pExpr->u.zToken); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); - break; - } -#endif /* SQLITE_OMIT_CAST */ - case TK_LT: zBinOp = "LT"; break; - case TK_LE: zBinOp = "LE"; break; - case TK_GT: zBinOp = "GT"; break; - case TK_GE: zBinOp = "GE"; break; - case TK_NE: zBinOp = "NE"; break; - case TK_EQ: zBinOp = "EQ"; break; - case TK_IS: zBinOp = "IS"; break; - case TK_ISNOT: zBinOp = "ISNOT"; break; - case TK_AND: zBinOp = "AND"; break; - case TK_OR: zBinOp = "OR"; break; - case TK_PLUS: zBinOp = "ADD"; break; - case TK_STAR: zBinOp = "MUL"; break; - case TK_MINUS: zBinOp = "SUB"; break; - case TK_REM: zBinOp = "REM"; break; - case TK_BITAND: zBinOp = "BITAND"; break; - case TK_BITOR: zBinOp = "BITOR"; break; - case TK_SLASH: zBinOp = "DIV"; break; - case TK_LSHIFT: zBinOp = "LSHIFT"; break; - case TK_RSHIFT: zBinOp = "RSHIFT"; break; - case TK_CONCAT: zBinOp = "CONCAT"; break; - case TK_DOT: zBinOp = "DOT"; break; - case TK_LIMIT: zBinOp = "LIMIT"; break; + if( buf.st_nlink>1 ){ + sqlcipher_sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath); + return; + } + if( fileHasMoved(pFile) ){ + sqlcipher_sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath); + return; + } +} - case TK_UMINUS: zUniOp = "UMINUS"; break; - case TK_UPLUS: zUniOp = "UPLUS"; break; - case TK_BITNOT: zUniOp = "BITNOT"; break; - case TK_NOT: zUniOp = "NOT"; break; - case TK_ISNULL: zUniOp = "ISNULL"; break; - case TK_NOTNULL: zUniOp = "NOTNULL"; break; - case TK_TRUTH: { - int x; - const char *azOp[] = { - "IS-FALSE", "IS-TRUE", "IS-NOT-FALSE", "IS-NOT-TRUE" - }; - assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT ); - assert( pExpr->pRight ); - assert( sqlcipher_sqlite3ExprSkipCollate(pExpr->pRight)->op==TK_TRUEFALSE ); - x = (pExpr->op2==TK_ISNOT)*2 + sqlcipher_sqlite3ExprTruthValue(pExpr->pRight); - zUniOp = azOp[x]; - break; - } +/* +** This routine checks if there is a RESERVED lock held on the specified +** file by this or any other process. If such a lock is held, set *pResOut +** to a non-zero value otherwise *pResOut is set to zero. The return value +** is set to SQLITE_OK unless an I/O error occurs during lock checking. +*/ +static int unixCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ + int rc = SQLITE_OK; + int reserved = 0; + unixFile *pFile = (unixFile*)id; - case TK_SPAN: { - sqlcipher_sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); - break; - } + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - case TK_COLLATE: { - /* COLLATE operators without the EP_Collate flag are intended to - ** emulate collation associated with a table column. These show - ** up in the treeview output as "SOFT-COLLATE". Explicit COLLATE - ** operators that appear in the original SQL always have the - ** EP_Collate bit set and appear in treeview output as just "COLLATE" */ - sqlcipher_sqlite3TreeViewLine(pView, "%sCOLLATE %Q%s", - !ExprHasProperty(pExpr, EP_Collate) ? "SOFT-" : "", - pExpr->u.zToken, zFlgs); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); - break; - } + assert( pFile ); + assert( pFile->eFileLock<=SHARED_LOCK ); + sqlcipher_sqlite3_mutex_enter(pFile->pInode->pLockMutex); - case TK_AGG_FUNCTION: - case TK_FUNCTION: { - ExprList *pFarg; /* List of function arguments */ - Window *pWin; - if( ExprHasProperty(pExpr, EP_TokenOnly) ){ - pFarg = 0; - pWin = 0; - }else{ - pFarg = pExpr->x.pList; -#ifndef SQLITE_OMIT_WINDOWFUNC - pWin = ExprHasProperty(pExpr, EP_WinFunc) ? pExpr->y.pWin : 0; -#else - pWin = 0; -#endif - } - if( pExpr->op==TK_AGG_FUNCTION ){ - sqlcipher_sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s agg=%d[%d]/%p", - pExpr->op2, pExpr->u.zToken, zFlgs, - pExpr->pAggInfo ? pExpr->pAggInfo->selId : 0, - pExpr->iAgg, pExpr->pAggInfo); - }else if( pExpr->op2!=0 ){ - const char *zOp2; - char zBuf[8]; - sqlcipher_sqlite3_snprintf(sizeof(zBuf),zBuf,"0x%02x",pExpr->op2); - zOp2 = zBuf; - if( pExpr->op2==NC_IsCheck ) zOp2 = "NC_IsCheck"; - if( pExpr->op2==NC_IdxExpr ) zOp2 = "NC_IdxExpr"; - if( pExpr->op2==NC_PartIdx ) zOp2 = "NC_PartIdx"; - if( pExpr->op2==NC_GenCol ) zOp2 = "NC_GenCol"; - sqlcipher_sqlite3TreeViewLine(pView, "FUNCTION %Q%s op2=%s", - pExpr->u.zToken, zFlgs, zOp2); - }else{ - sqlcipher_sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs); - } - if( pFarg ){ - sqlcipher_sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0); - } -#ifndef SQLITE_OMIT_WINDOWFUNC - if( pWin ){ - sqlcipher_sqlite3TreeViewWindow(pView, pWin, 0); - } -#endif - break; - } -#ifndef SQLITE_OMIT_SUBQUERY - case TK_EXISTS: { - sqlcipher_sqlite3TreeViewLine(pView, "EXISTS-expr flags=0x%x", pExpr->flags); - sqlcipher_sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); - break; - } - case TK_SELECT: { - sqlcipher_sqlite3TreeViewLine(pView, "subquery-expr flags=0x%x", pExpr->flags); - sqlcipher_sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); - break; - } - case TK_IN: { - sqlcipher_sqlite3TreeViewLine(pView, "IN flags=0x%x", pExpr->flags); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - sqlcipher_sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); - }else{ - sqlcipher_sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); - } - break; - } -#endif /* SQLITE_OMIT_SUBQUERY */ + /* Check if a thread in this process holds such a lock */ + if( pFile->pInode->eFileLock>SHARED_LOCK ){ + reserved = 1; + } - /* - ** x BETWEEN y AND z - ** - ** This is equivalent to - ** - ** x>=y AND x<=z - ** - ** X is stored in pExpr->pLeft. - ** Y is stored in pExpr->pList->a[0].pExpr. - ** Z is stored in pExpr->pList->a[1].pExpr. - */ - case TK_BETWEEN: { - Expr *pX = pExpr->pLeft; - Expr *pY = pExpr->x.pList->a[0].pExpr; - Expr *pZ = pExpr->x.pList->a[1].pExpr; - sqlcipher_sqlite3TreeViewLine(pView, "BETWEEN"); - sqlcipher_sqlite3TreeViewExpr(pView, pX, 1); - sqlcipher_sqlite3TreeViewExpr(pView, pY, 1); - sqlcipher_sqlite3TreeViewExpr(pView, pZ, 0); - break; - } - case TK_TRIGGER: { - /* If the opcode is TK_TRIGGER, then the expression is a reference - ** to a column in the new.* or old.* pseudo-tables available to - ** trigger programs. In this case Expr.iTable is set to 1 for the - ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn - ** is set to the column of the pseudo-table to read, or to -1 to - ** read the rowid field. - */ - sqlcipher_sqlite3TreeViewLine(pView, "%s(%d)", - pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn); - break; - } - case TK_CASE: { - sqlcipher_sqlite3TreeViewLine(pView, "CASE"); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); - sqlcipher_sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); - break; - } -#ifndef SQLITE_OMIT_TRIGGER - case TK_RAISE: { - const char *zType = "unk"; - switch( pExpr->affExpr ){ - case OE_Rollback: zType = "rollback"; break; - case OE_Abort: zType = "abort"; break; - case OE_Fail: zType = "fail"; break; - case OE_Ignore: zType = "ignore"; break; - } - sqlcipher_sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken); - break; - } -#endif - case TK_MATCH: { - sqlcipher_sqlite3TreeViewLine(pView, "MATCH {%d:%d}%s", - pExpr->iTable, pExpr->iColumn, zFlgs); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pRight, 0); - break; - } - case TK_VECTOR: { - char *z = sqlcipher_sqlite3_mprintf("VECTOR%s",zFlgs); - sqlcipher_sqlite3TreeViewBareExprList(pView, pExpr->x.pList, z); - sqlcipher_sqlite3_free(z); - break; - } - case TK_SELECT_COLUMN: { - sqlcipher_sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn); - sqlcipher_sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0); - break; - } - case TK_IF_NULL_ROW: { - sqlcipher_sqlite3TreeViewLine(pView, "IF-NULL-ROW %d", pExpr->iTable); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); - break; - } - default: { - sqlcipher_sqlite3TreeViewLine(pView, "op=%d", pExpr->op); - break; + /* Otherwise see if some other process holds it. + */ +#ifndef __DJGPP__ + if( !reserved && !pFile->pInode->bProcessLock ){ + struct flock lock; + lock.l_whence = SEEK_SET; + lock.l_start = RESERVED_BYTE; + lock.l_len = 1; + lock.l_type = F_WRLCK; + if( osFcntl(pFile->h, F_GETLK, &lock) ){ + rc = SQLITE_IOERR_CHECKRESERVEDLOCK; + storeLastErrno(pFile, errno); + } else if( lock.l_type!=F_UNLCK ){ + reserved = 1; } } - if( zBinOp ){ - sqlcipher_sqlite3TreeViewLine(pView, "%s%s", zBinOp, zFlgs); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pRight, 0); - }else if( zUniOp ){ - sqlcipher_sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs); - sqlcipher_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); - } - sqlcipher_sqlite3TreeViewPop(pView); +#endif + + sqlcipher_sqlite3_mutex_leave(pFile->pInode->pLockMutex); + OSTRACE(("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved)); + + *pResOut = reserved; + return rc; } +/* Forward declaration*/ +static int unixSleep(sqlcipher_sqlite3_vfs*,int); /* -** Generate a human-readable explanation of an expression list. +** Set a posix-advisory-lock. +** +** There are two versions of this routine. If compiled with +** SQLITE_ENABLE_SETLK_TIMEOUT then the routine has an extra parameter +** which is a pointer to a unixFile. If the unixFile->iBusyTimeout +** value is set, then it is the number of milliseconds to wait before +** failing the lock. The iBusyTimeout value is always reset back to +** zero on each call. +** +** If SQLITE_ENABLE_SETLK_TIMEOUT is not defined, then do a non-blocking +** attempt to set the lock. */ -SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewBareExprList( - TreeView *pView, - const ExprList *pList, - const char *zLabel +#ifndef SQLITE_ENABLE_SETLK_TIMEOUT +# define osSetPosixAdvisoryLock(h,x,t) osFcntl(h,F_SETLK,x) +#else +static int osSetPosixAdvisoryLock( + int h, /* The file descriptor on which to take the lock */ + struct flock *pLock, /* The description of the lock */ + unixFile *pFile /* Structure holding timeout value */ ){ - if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST"; - if( pList==0 ){ - sqlcipher_sqlite3TreeViewLine(pView, "%s (empty)", zLabel); - }else{ - int i; - sqlcipher_sqlite3TreeViewLine(pView, "%s", zLabel); - for(i=0; inExpr; i++){ - int j = pList->a[i].u.x.iOrderByCol; - char *zName = pList->a[i].zEName; - int moreToFollow = inExpr - 1; - if( pList->a[i].eEName!=ENAME_NAME ) zName = 0; - if( j || zName ){ - sqlcipher_sqlite3TreeViewPush(pView, moreToFollow); - moreToFollow = 0; - sqlcipher_sqlite3TreeViewLine(pView, 0); - if( zName ){ - fprintf(stdout, "AS %s ", zName); - } - if( j ){ - fprintf(stdout, "iOrderByCol=%d", j); - } - fprintf(stdout, "\n"); - fflush(stdout); - } - sqlcipher_sqlite3TreeViewExpr(pView, pList->a[i].pExpr, moreToFollow); - if( j || zName ){ - sqlcipher_sqlite3TreeViewPop(pView); - } - } + int tm = pFile->iBusyTimeout; + int rc = osFcntl(h,F_SETLK,pLock); + while( rc<0 && tm>0 ){ + /* On systems that support some kind of blocking file lock with a timeout, + ** make appropriate changes here to invoke that blocking file lock. On + ** generic posix, however, there is no such API. So we simply try the + ** lock once every millisecond until either the timeout expires, or until + ** the lock is obtained. */ + unixSleep(0,1000); + rc = osFcntl(h,F_SETLK,pLock); + tm--; } + return rc; } -SQLITE_PRIVATE void sqlcipher_sqlite3TreeViewExprList( - TreeView *pView, - const ExprList *pList, - u8 moreToFollow, - const char *zLabel -){ - pView = sqlcipher_sqlite3TreeViewPush(pView, moreToFollow); - sqlcipher_sqlite3TreeViewBareExprList(pView, pList, zLabel); - sqlcipher_sqlite3TreeViewPop(pView); -} +#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ -#endif /* SQLITE_DEBUG */ -/************** End of treeview.c ********************************************/ -/************** Begin file random.c ******************************************/ /* -** 2001 September 15 +** Attempt to set a system-lock on the file pFile. The lock is +** described by pLock. ** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** If the pFile was opened read/write from unix-excl, then the only lock +** ever obtained is an exclusive lock, and it is obtained exactly once +** the first time any lock is attempted. All subsequent system locking +** operations become no-ops. Locking operations still happen internally, +** in order to coordinate access between separate database connections +** within this process, but all of that is handled in memory and the +** operating system does not participate. ** -** 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 function is a pass-through to fcntl(F_SETLK) if pFile is using +** any VFS other than "unix-excl" or if pFile is opened on "unix-excl" +** and is read-only. ** -************************************************************************* -** This file contains code to implement a pseudo-random number -** generator (PRNG) for SQLite. +** Zero is returned if the call completes successfully, or -1 if a call +** to fcntl() fails. In this case, errno is set appropriately (by fcntl()). +*/ +static int unixFileLock(unixFile *pFile, struct flock *pLock){ + int rc; + unixInodeInfo *pInode = pFile->pInode; + assert( pInode!=0 ); + assert( sqlcipher_sqlite3_mutex_held(pInode->pLockMutex) ); + if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){ + if( pInode->bProcessLock==0 ){ + struct flock lock; + assert( pInode->nLock==0 ); + lock.l_whence = SEEK_SET; + lock.l_start = SHARED_FIRST; + lock.l_len = SHARED_SIZE; + lock.l_type = F_WRLCK; + rc = osSetPosixAdvisoryLock(pFile->h, &lock, pFile); + if( rc<0 ) return rc; + pInode->bProcessLock = 1; + pInode->nLock++; + }else{ + rc = 0; + } + }else{ + rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile); + } + return rc; +} + +/* +** Lock the file with the lock specified by parameter eFileLock - one +** of the following: ** -** Random numbers are used by some of the database backends in order -** to generate random integer keys for tables or random filenames. +** (1) SHARED_LOCK +** (2) RESERVED_LOCK +** (3) PENDING_LOCK +** (4) EXCLUSIVE_LOCK +** +** Sometimes when requesting one lock state, additional lock states +** are inserted in between. The locking might fail on one of the later +** transitions leaving the lock state different from what it started but +** still short of its goal. The following chart shows the allowed +** transitions and the inserted intermediate states: +** +** UNLOCKED -> SHARED +** SHARED -> RESERVED +** SHARED -> (PENDING) -> EXCLUSIVE +** RESERVED -> (PENDING) -> EXCLUSIVE +** PENDING -> EXCLUSIVE +** +** This routine will only increase a lock. Use the sqlcipher_sqlite3OsUnlock() +** routine to lower a locking level. */ -/* #include "sqliteInt.h" */ +static int unixLock(sqlcipher_sqlite3_file *id, int eFileLock){ + /* The following describes the implementation of the various locks and + ** lock transitions in terms of the POSIX advisory shared and exclusive + ** lock primitives (called read-locks and write-locks below, to avoid + ** confusion with SQLite lock names). The algorithms are complicated + ** slightly in order to be compatible with Windows95 systems simultaneously + ** accessing the same database file, in case that is ever required. + ** + ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved + ** byte', each single bytes at well known offsets, and the 'shared byte + ** range', a range of 510 bytes at a well known offset. + ** + ** To obtain a SHARED lock, a read-lock is obtained on the 'pending + ** byte'. If this is successful, 'shared byte range' is read-locked + ** and the lock on the 'pending byte' released. (Legacy note: When + ** SQLite was first developed, Windows95 systems were still very common, + ** and Widnows95 lacks a shared-lock capability. So on Windows95, a + ** single randomly selected by from the 'shared byte range' is locked. + ** Windows95 is now pretty much extinct, but this work-around for the + ** lack of shared-locks on Windows95 lives on, for backwards + ** compatibility.) + ** + ** A process may only obtain a RESERVED lock after it has a SHARED lock. + ** A RESERVED lock is implemented by grabbing a write-lock on the + ** 'reserved byte'. + ** + ** A process may only obtain a PENDING lock after it has obtained a + ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock + ** on the 'pending byte'. This ensures that no new SHARED locks can be + ** obtained, but existing SHARED locks are allowed to persist. A process + ** does not have to obtain a RESERVED lock on the way to a PENDING lock. + ** This property is used by the algorithm for rolling back a journal file + ** after a crash. + ** + ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is + ** implemented by obtaining a write-lock on the entire 'shared byte + ** range'. Since all other locks require a read-lock on one of the bytes + ** within this range, this ensures that no other locks are held on the + ** database. + */ + int rc = SQLITE_OK; + unixFile *pFile = (unixFile*)id; + unixInodeInfo *pInode; + struct flock lock; + int tErrno = 0; + assert( pFile ); + OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, + azFileLock(eFileLock), azFileLock(pFile->eFileLock), + azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared, + osGetpid(0))); -/* All threads share a single random number generator. -** This structure is the current state of the generator. -*/ -static SQLITE_WSD struct sqlcipher_sqlite3PrngType { - unsigned char isInit; /* True if initialized */ - unsigned char i, j; /* State variables */ - unsigned char s[256]; /* State variables */ -} sqlcipher_sqlite3Prng; + /* If there is already a lock of this type or more restrictive on the + ** unixFile, do nothing. Don't use the end_lock: exit path, as + ** unixEnterMutex() hasn't been called yet. + */ + if( pFile->eFileLock>=eFileLock ){ + OSTRACE(("LOCK %d %s ok (already held) (unix)\n", pFile->h, + azFileLock(eFileLock))); + return SQLITE_OK; + } -/* -** Return N random bytes. -*/ -SQLITE_API void sqlcipher_sqlite3_randomness(int N, void *pBuf){ - unsigned char t; - unsigned char *zBuf = pBuf; + /* Make sure the locking sequence is correct. + ** (1) We never move from unlocked to anything higher than shared lock. + ** (2) SQLite never explicitly requests a pendig lock. + ** (3) A shared lock is always held when a reserve lock is requested. + */ + assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); + assert( eFileLock!=PENDING_LOCK ); + assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK ); - /* The "wsdPrng" macro will resolve to the pseudo-random number generator - ** state vector. If writable static data is unsupported on the target, - ** we have to locate the state vector at run-time. In the more common - ** case where writable static data is supported, wsdPrng can refer directly - ** to the "sqlcipher_sqlite3Prng" state vector declared above. + /* This mutex is needed because pFile->pInode is shared across threads */ -#ifdef SQLITE_OMIT_WSD - struct sqlcipher_sqlite3PrngType *p = &GLOBAL(struct sqlcipher_sqlite3PrngType, sqlcipher_sqlite3Prng); -# define wsdPrng p[0] -#else -# define wsdPrng sqlcipher_sqlite3Prng -#endif + pInode = pFile->pInode; + sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); -#if SQLITE_THREADSAFE - sqlcipher_sqlite3_mutex *mutex; -#endif + /* If some thread using this PID has a lock via a different unixFile* + ** handle that precludes the requested lock, return BUSY. + */ + if( (pFile->eFileLock!=pInode->eFileLock && + (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) + ){ + rc = SQLITE_BUSY; + goto end_lock; + } -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return; -#endif + /* If a SHARED lock is requested, and some thread using this PID already + ** has a SHARED or RESERVED lock, then increment reference counts and + ** return SQLITE_OK. + */ + if( eFileLock==SHARED_LOCK && + (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ + assert( eFileLock==SHARED_LOCK ); + assert( pFile->eFileLock==0 ); + assert( pInode->nShared>0 ); + pFile->eFileLock = SHARED_LOCK; + pInode->nShared++; + pInode->nLock++; + goto end_lock; + } -#if SQLITE_THREADSAFE - mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG); -#endif - sqlcipher_sqlite3_mutex_enter(mutex); - if( N<=0 || pBuf==0 ){ - wsdPrng.isInit = 0; - sqlcipher_sqlite3_mutex_leave(mutex); - return; + /* A PENDING lock is needed before acquiring a SHARED lock and before + ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will + ** be released. + */ + lock.l_len = 1L; + lock.l_whence = SEEK_SET; + if( eFileLock==SHARED_LOCK + || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLocknShared==0 ); + assert( pInode->eFileLock==0 ); + assert( rc==SQLITE_OK ); + + /* Now get the read-lock */ + lock.l_start = SHARED_FIRST; + lock.l_len = SHARED_SIZE; + if( unixFileLock(pFile, &lock) ){ + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); } - for(i=0; i<256; i++){ - wsdPrng.j += wsdPrng.s[i] + k[i]; - t = wsdPrng.s[wsdPrng.j]; - wsdPrng.s[wsdPrng.j] = wsdPrng.s[i]; - wsdPrng.s[i] = t; + + /* Drop the temporary PENDING lock */ + lock.l_start = PENDING_BYTE; + lock.l_len = 1L; + lock.l_type = F_UNLCK; + if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){ + /* This could happen with a network mount */ + tErrno = errno; + rc = SQLITE_IOERR_UNLOCK; + } + + if( rc ){ + if( rc!=SQLITE_BUSY ){ + storeLastErrno(pFile, tErrno); + } + goto end_lock; + }else{ + pFile->eFileLock = SHARED_LOCK; + pInode->nLock++; + pInode->nShared = 1; + } + }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ + /* We are trying for an exclusive lock but another thread in this + ** same process is still holding a shared lock. */ + rc = SQLITE_BUSY; + }else{ + /* The request was for a RESERVED or EXCLUSIVE lock. It is + ** assumed that there is a SHARED or greater lock on the file + ** already. + */ + assert( 0!=pFile->eFileLock ); + lock.l_type = F_WRLCK; + + assert( eFileLock==RESERVED_LOCK || eFileLock==EXCLUSIVE_LOCK ); + if( eFileLock==RESERVED_LOCK ){ + lock.l_start = RESERVED_BYTE; + lock.l_len = 1L; + }else{ + lock.l_start = SHARED_FIRST; + lock.l_len = SHARED_SIZE; + } + + if( unixFileLock(pFile, &lock) ){ + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); + if( rc!=SQLITE_BUSY ){ + storeLastErrno(pFile, tErrno); + } } - wsdPrng.isInit = 1; } - assert( N>0 ); - do{ - wsdPrng.i++; - t = wsdPrng.s[wsdPrng.i]; - wsdPrng.j += t; - wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j]; - wsdPrng.s[wsdPrng.j] = t; - t += wsdPrng.s[wsdPrng.i]; - *(zBuf++) = wsdPrng.s[t]; - }while( --N ); - sqlcipher_sqlite3_mutex_leave(mutex); + +#ifdef SQLITE_DEBUG + /* Set up the transaction-counter change checking flags when + ** transitioning from a SHARED to a RESERVED lock. The change + ** from SHARED to RESERVED marks the beginning of a normal + ** write operation (not a hot journal rollback). + */ + if( rc==SQLITE_OK + && pFile->eFileLock<=SHARED_LOCK + && eFileLock==RESERVED_LOCK + ){ + pFile->transCntrChng = 0; + pFile->dbUpdate = 0; + pFile->inNormalWrite = 1; + } +#endif + + + if( rc==SQLITE_OK ){ + pFile->eFileLock = eFileLock; + pInode->eFileLock = eFileLock; + }else if( eFileLock==EXCLUSIVE_LOCK ){ + pFile->eFileLock = PENDING_LOCK; + pInode->eFileLock = PENDING_LOCK; + } + +end_lock: + sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); + OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock), + rc==SQLITE_OK ? "ok" : "failed")); + return rc; } -#ifndef SQLITE_UNTESTABLE /* -** For testing purposes, we sometimes want to preserve the state of -** PRNG and restore the PRNG to its saved state at a later time, or -** to reset the PRNG to its initial state. These routines accomplish -** those tasks. -** -** The sqlcipher_sqlite3_test_control() interface calls these routines to -** control the PRNG. +** Add the file descriptor used by file handle pFile to the corresponding +** pUnused list. */ -static SQLITE_WSD struct sqlcipher_sqlite3PrngType sqlcipher_sqlite3SavedPrng; -SQLITE_PRIVATE void sqlcipher_sqlite3PrngSaveState(void){ - memcpy( - &GLOBAL(struct sqlcipher_sqlite3PrngType, sqlcipher_sqlite3SavedPrng), - &GLOBAL(struct sqlcipher_sqlite3PrngType, sqlcipher_sqlite3Prng), - sizeof(sqlcipher_sqlite3Prng) - ); -} -SQLITE_PRIVATE void sqlcipher_sqlite3PrngRestoreState(void){ - memcpy( - &GLOBAL(struct sqlcipher_sqlite3PrngType, sqlcipher_sqlite3Prng), - &GLOBAL(struct sqlcipher_sqlite3PrngType, sqlcipher_sqlite3SavedPrng), - sizeof(sqlcipher_sqlite3Prng) - ); +static void setPendingFd(unixFile *pFile){ + unixInodeInfo *pInode = pFile->pInode; + UnixUnusedFd *p = pFile->pPreallocatedUnused; + assert( unixFileMutexHeld(pFile) ); + p->pNext = pInode->pUnused; + pInode->pUnused = p; + pFile->h = -1; + pFile->pPreallocatedUnused = 0; } -#endif /* SQLITE_UNTESTABLE */ -/************** End of random.c **********************************************/ -/************** Begin file threads.c *****************************************/ /* -** 2012 July 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 presents a simple cross-platform threading interface for -** use internally by SQLite. +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock +** must be either NO_LOCK or SHARED_LOCK. ** -** A "thread" can be created using sqlcipher_sqlite3ThreadCreate(). This thread -** runs independently of its creator until it is joined using -** sqlcipher_sqlite3ThreadJoin(), at which point it terminates. +** If the locking level of the file descriptor is already at or below +** the requested locking level, this routine is a no-op. ** -** Threads do not have to be real. It could be that the work of the -** "thread" is done by the main thread at either the sqlcipher_sqlite3ThreadCreate() -** or sqlcipher_sqlite3ThreadJoin() call. This is, in fact, what happens in -** single threaded systems. Nothing in SQLite requires multiple threads. -** This interface exists so that applications that want to take advantage -** of multiple cores can do so, while also allowing applications to stay -** single-threaded if desired. +** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED +** the byte range is divided into 2 parts and the first part is unlocked then +** set to a read lock, then the other part is simply unlocked. This works +** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to +** remove the write lock on a region when a read lock is set. */ -/* #include "sqliteInt.h" */ -#if SQLITE_OS_WIN -/* # include "os_win.h" */ -#endif - -#if SQLITE_MAX_WORKER_THREADS>0 - -/********************************* Unix Pthreads ****************************/ -#if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0 +static int posixUnlock(sqlcipher_sqlite3_file *id, int eFileLock, int handleNFSUnlock){ + unixFile *pFile = (unixFile*)id; + unixInodeInfo *pInode; + struct flock lock; + int rc = SQLITE_OK; -#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ -/* #include */ + assert( pFile ); + OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, + pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, + osGetpid(0))); -/* A running thread */ -struct SQLiteThread { - pthread_t tid; /* Thread ID */ - int done; /* Set to true when thread finishes */ - void *pOut; /* Result returned by the thread */ - void *(*xTask)(void*); /* The thread routine */ - void *pIn; /* Argument to the thread */ -}; + assert( eFileLock<=SHARED_LOCK ); + if( pFile->eFileLock<=eFileLock ){ + return SQLITE_OK; + } + pInode = pFile->pInode; + sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); + assert( pInode->nShared!=0 ); + if( pFile->eFileLock>SHARED_LOCK ){ + assert( pInode->eFileLock==pFile->eFileLock ); -/* Create a new thread */ -SQLITE_PRIVATE int sqlcipher_sqlite3ThreadCreate( - SQLiteThread **ppThread, /* OUT: Write the thread object here */ - void *(*xTask)(void*), /* Routine to run in a separate thread */ - void *pIn /* Argument passed into xTask() */ -){ - SQLiteThread *p; - int rc; +#ifdef SQLITE_DEBUG + /* When reducing a lock such that other processes can start + ** reading the database file again, make sure that the + ** transaction counter was updated if any part of the database + ** file changed. If the transaction counter is not updated, + ** other connections to the same file might not realize that + ** the file has changed and hence might not know to flush their + ** cache. The use of a stale cache can lead to database corruption. + */ + pFile->inNormalWrite = 0; +#endif - assert( ppThread!=0 ); - assert( xTask!=0 ); - /* This routine is never used in single-threaded mode */ - assert( sqlcipher_sqlite3GlobalConfig.bCoreMutex!=0 ); + /* downgrading to a shared lock on NFS involves clearing the write lock + ** before establishing the readlock - to avoid a race condition we downgrade + ** the lock in 2 blocks, so that part of the range will be covered by a + ** write lock until the rest is covered by a read lock: + ** 1: [WWWWW] + ** 2: [....W] + ** 3: [RRRRW] + ** 4: [RRRR.] + */ + if( eFileLock==SHARED_LOCK ){ +#if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE + (void)handleNFSUnlock; + assert( handleNFSUnlock==0 ); +#endif +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE + if( handleNFSUnlock ){ + int tErrno; /* Error code from system call errors */ + off_t divSize = SHARED_SIZE - 1; - *ppThread = 0; - p = sqlcipher_sqlite3Malloc(sizeof(*p)); - if( p==0 ) return SQLITE_NOMEM_BKPT; - memset(p, 0, sizeof(*p)); - p->xTask = xTask; - p->pIn = pIn; - /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a - ** function that returns SQLITE_ERROR when passed the argument 200, that - ** forces worker threads to run sequentially and deterministically - ** for testing purposes. */ - if( sqlcipher_sqlite3FaultSim(200) ){ - rc = 1; - }else{ - rc = pthread_create(&p->tid, 0, xTask, pIn); - } - if( rc ){ - p->done = 1; - p->pOut = xTask(pIn); + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = SHARED_FIRST; + lock.l_len = divSize; + if( unixFileLock(pFile, &lock)==(-1) ){ + tErrno = errno; + rc = SQLITE_IOERR_UNLOCK; + storeLastErrno(pFile, tErrno); + goto end_unlock; + } + lock.l_type = F_RDLCK; + lock.l_whence = SEEK_SET; + lock.l_start = SHARED_FIRST; + lock.l_len = divSize; + if( unixFileLock(pFile, &lock)==(-1) ){ + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); + if( IS_LOCK_ERROR(rc) ){ + storeLastErrno(pFile, tErrno); + } + goto end_unlock; + } + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = SHARED_FIRST+divSize; + lock.l_len = SHARED_SIZE-divSize; + if( unixFileLock(pFile, &lock)==(-1) ){ + tErrno = errno; + rc = SQLITE_IOERR_UNLOCK; + storeLastErrno(pFile, tErrno); + goto end_unlock; + } + }else +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ + { + lock.l_type = F_RDLCK; + lock.l_whence = SEEK_SET; + lock.l_start = SHARED_FIRST; + lock.l_len = SHARED_SIZE; + if( unixFileLock(pFile, &lock) ){ + /* In theory, the call to unixFileLock() cannot fail because another + ** process is holding an incompatible lock. If it does, this + ** indicates that the other process is not following the locking + ** protocol. If this happens, return SQLITE_IOERR_RDLOCK. Returning + ** SQLITE_BUSY would confuse the upper layer (in practice it causes + ** an assert to fail). */ + rc = SQLITE_IOERR_RDLOCK; + storeLastErrno(pFile, errno); + goto end_unlock; + } + } + } + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = PENDING_BYTE; + lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); + if( unixFileLock(pFile, &lock)==0 ){ + pInode->eFileLock = SHARED_LOCK; + }else{ + rc = SQLITE_IOERR_UNLOCK; + storeLastErrno(pFile, errno); + goto end_unlock; + } } - *ppThread = p; - return SQLITE_OK; -} + if( eFileLock==NO_LOCK ){ + /* Decrement the shared lock counter. Release the lock using an + ** OS call only when all threads in this same process have released + ** the lock. + */ + pInode->nShared--; + if( pInode->nShared==0 ){ + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = lock.l_len = 0L; + if( unixFileLock(pFile, &lock)==0 ){ + pInode->eFileLock = NO_LOCK; + }else{ + rc = SQLITE_IOERR_UNLOCK; + storeLastErrno(pFile, errno); + pInode->eFileLock = NO_LOCK; + pFile->eFileLock = NO_LOCK; + } + } -/* Get the results of the thread */ -SQLITE_PRIVATE int sqlcipher_sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ - int rc; + /* Decrement the count of locks against this same file. When the + ** count reaches zero, close any other file descriptors whose close + ** was deferred because of outstanding locks. + */ + pInode->nLock--; + assert( pInode->nLock>=0 ); + if( pInode->nLock==0 ) closePendingFds(pFile); + } - assert( ppOut!=0 ); - if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT; - if( p->done ){ - *ppOut = p->pOut; - rc = SQLITE_OK; - }else{ - rc = pthread_join(p->tid, ppOut) ? SQLITE_ERROR : SQLITE_OK; +end_unlock: + sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); + if( rc==SQLITE_OK ){ + pFile->eFileLock = eFileLock; } - sqlcipher_sqlite3_free(p); return rc; } -#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */ -/******************************** End Unix Pthreads *************************/ - - -/********************************* Win32 Threads ****************************/ -#if SQLITE_OS_WIN_THREADS - -#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ -#include - -/* A running thread */ -struct SQLiteThread { - void *tid; /* The thread handle */ - unsigned id; /* The thread identifier */ - void *(*xTask)(void*); /* The routine to run as a thread */ - void *pIn; /* Argument to xTask */ - void *pResult; /* Result of xTask */ -}; - -/* Thread procedure Win32 compatibility shim */ -static unsigned __stdcall sqlcipher_sqlite3ThreadProc( - void *pArg /* IN: Pointer to the SQLiteThread structure */ -){ - SQLiteThread *p = (SQLiteThread *)pArg; - - assert( p!=0 ); -#if 0 - /* - ** This assert appears to trigger spuriously on certain - ** versions of Windows, possibly due to _beginthreadex() - ** and/or CreateThread() not fully setting their thread - ** ID parameter before starting the thread. - */ - assert( p->id==GetCurrentThreadId() ); +/* +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock +** must be either NO_LOCK or SHARED_LOCK. +** +** If the locking level of the file descriptor is already at or below +** the requested locking level, this routine is a no-op. +*/ +static int unixUnlock(sqlcipher_sqlite3_file *id, int eFileLock){ +#if SQLITE_MAX_MMAP_SIZE>0 + assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 ); #endif - assert( p->xTask!=0 ); - p->pResult = p->xTask(p->pIn); - - _endthreadex(0); - return 0; /* NOT REACHED */ + return posixUnlock(id, eFileLock, 0); } -/* Create a new thread */ -SQLITE_PRIVATE int sqlcipher_sqlite3ThreadCreate( - SQLiteThread **ppThread, /* OUT: Write the thread object here */ - void *(*xTask)(void*), /* Routine to run in a separate thread */ - void *pIn /* Argument passed into xTask() */ -){ - SQLiteThread *p; +#if SQLITE_MAX_MMAP_SIZE>0 +static int unixMapfile(unixFile *pFd, i64 nByte); +static void unixUnmapfile(unixFile *pFd); +#endif - assert( ppThread!=0 ); - assert( xTask!=0 ); - *ppThread = 0; - p = sqlcipher_sqlite3Malloc(sizeof(*p)); - if( p==0 ) return SQLITE_NOMEM_BKPT; - /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a - ** function that returns SQLITE_ERROR when passed the argument 200, that - ** forces worker threads to run sequentially and deterministically - ** (via the sqlcipher_sqlite3FaultSim() term of the conditional) for testing - ** purposes. */ - if( sqlcipher_sqlite3GlobalConfig.bCoreMutex==0 || sqlcipher_sqlite3FaultSim(200) ){ - memset(p, 0, sizeof(*p)); - }else{ - p->xTask = xTask; - p->pIn = pIn; - p->tid = (void*)_beginthreadex(0, 0, sqlcipher_sqlite3ThreadProc, p, 0, &p->id); - if( p->tid==0 ){ - memset(p, 0, sizeof(*p)); +/* +** This function performs the parts of the "close file" operation +** common to all locking schemes. It closes the directory and file +** handles, if they are valid, and sets all fields of the unixFile +** structure to 0. +** +** It is *not* necessary to hold the mutex when this routine is called, +** even on VxWorks. A mutex will be acquired on VxWorks by the +** vxworksReleaseFileId() routine. +*/ +static int closeUnixFile(sqlcipher_sqlite3_file *id){ + unixFile *pFile = (unixFile*)id; +#if SQLITE_MAX_MMAP_SIZE>0 + unixUnmapfile(pFile); +#endif + if( pFile->h>=0 ){ + robust_close(pFile, pFile->h, __LINE__); + pFile->h = -1; + } +#if OS_VXWORKS + if( pFile->pId ){ + if( pFile->ctrlFlags & UNIXFILE_DELETE ){ + osUnlink(pFile->pId->zCanonicalName); } + vxworksReleaseFileId(pFile->pId); + pFile->pId = 0; } - if( p->xTask==0 ){ - p->id = GetCurrentThreadId(); - p->pResult = xTask(pIn); +#endif +#ifdef SQLITE_UNLINK_AFTER_CLOSE + if( pFile->ctrlFlags & UNIXFILE_DELETE ){ + osUnlink(pFile->zPath); + sqlcipher_sqlite3_free(*(char**)&pFile->zPath); + pFile->zPath = 0; } - *ppThread = p; +#endif + OSTRACE(("CLOSE %-3d\n", pFile->h)); + OpenCounter(-1); + sqlcipher_sqlite3_free(pFile->pPreallocatedUnused); + memset(pFile, 0, sizeof(unixFile)); return SQLITE_OK; } -SQLITE_PRIVATE DWORD sqlcipher_sqlite3Win32Wait(HANDLE hObject); /* os_win.c */ +/* +** Close a file. +*/ +static int unixClose(sqlcipher_sqlite3_file *id){ + int rc = SQLITE_OK; + unixFile *pFile = (unixFile *)id; + unixInodeInfo *pInode = pFile->pInode; -/* Get the results of the thread */ -SQLITE_PRIVATE int sqlcipher_sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ - DWORD rc; - BOOL bRc; + assert( pInode!=0 ); + verifyDbFile(pFile); + unixUnlock(id, NO_LOCK); + assert( unixFileMutexNotheld(pFile) ); + unixEnterMutex(); - assert( ppOut!=0 ); - if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT; - if( p->xTask==0 ){ - /* assert( p->id==GetCurrentThreadId() ); */ - rc = WAIT_OBJECT_0; - assert( p->tid==0 ); - }else{ - assert( p->id!=0 && p->id!=GetCurrentThreadId() ); - rc = sqlcipher_sqlite3Win32Wait((HANDLE)p->tid); - assert( rc!=WAIT_IO_COMPLETION ); - bRc = CloseHandle((HANDLE)p->tid); - assert( bRc ); + /* unixFile.pInode is always valid here. Otherwise, a different close + ** routine (e.g. nolockClose()) would be called instead. + */ + assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 ); + sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); + if( pInode->nLock ){ + /* If there are outstanding locks, do not actually close the file just + ** yet because that would clear those locks. Instead, add the file + ** descriptor to pInode->pUnused list. It will be automatically closed + ** when the last lock is cleared. + */ + setPendingFd(pFile); } - if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult; - sqlcipher_sqlite3_free(p); - return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR; + sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); + releaseInodeInfo(pFile); + assert( pFile->pShm==0 ); + rc = closeUnixFile(id); + unixLeaveMutex(); + return rc; } -#endif /* SQLITE_OS_WIN_THREADS */ -/******************************** End Win32 Threads *************************/ - +/************** End of the posix advisory lock implementation ***************** +******************************************************************************/ -/********************************* Single-Threaded **************************/ -#ifndef SQLITE_THREADS_IMPLEMENTED -/* -** This implementation does not actually create a new thread. It does the -** work of the thread in the main thread, when either the thread is created -** or when it is joined +/****************************************************************************** +****************************** No-op Locking ********************************** +** +** Of the various locking implementations available, this is by far the +** simplest: locking is ignored. No attempt is made to lock the database +** file for reading or writing. +** +** This locking mode is appropriate for use on read-only databases +** (ex: databases that are burned into CD-ROM, for example.) It can +** also be used if the application employs some external mechanism to +** prevent simultaneous access of the same database by two or more +** database connections. But there is a serious risk of database +** corruption if this locking mode is used in situations where multiple +** database connections are accessing the same database file at the same +** time and one or more of those connections are writing. */ -/* A running thread */ -struct SQLiteThread { - void *(*xTask)(void*); /* The routine to run as a thread */ - void *pIn; /* Argument to xTask */ - void *pResult; /* Result of xTask */ -}; - -/* Create a new thread */ -SQLITE_PRIVATE int sqlcipher_sqlite3ThreadCreate( - SQLiteThread **ppThread, /* OUT: Write the thread object here */ - void *(*xTask)(void*), /* Routine to run in a separate thread */ - void *pIn /* Argument passed into xTask() */ -){ - SQLiteThread *p; - - assert( ppThread!=0 ); - assert( xTask!=0 ); - *ppThread = 0; - p = sqlcipher_sqlite3Malloc(sizeof(*p)); - if( p==0 ) return SQLITE_NOMEM_BKPT; - if( (SQLITE_PTR_TO_INT(p)/17)&1 ){ - p->xTask = xTask; - p->pIn = pIn; - }else{ - p->xTask = 0; - p->pResult = xTask(pIn); - } - *ppThread = p; +static int nolockCheckReservedLock(sqlcipher_sqlite3_file *NotUsed, int *pResOut){ + UNUSED_PARAMETER(NotUsed); + *pResOut = 0; return SQLITE_OK; } - -/* Get the results of the thread */ -SQLITE_PRIVATE int sqlcipher_sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ - - assert( ppOut!=0 ); - if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT; - if( p->xTask ){ - *ppOut = p->xTask(p->pIn); - }else{ - *ppOut = p->pResult; - } - sqlcipher_sqlite3_free(p); - -#if defined(SQLITE_TEST) - { - void *pTstAlloc = sqlcipher_sqlite3Malloc(10); - if (!pTstAlloc) return SQLITE_NOMEM_BKPT; - sqlcipher_sqlite3_free(pTstAlloc); - } -#endif - +static int nolockLock(sqlcipher_sqlite3_file *NotUsed, int NotUsed2){ + UNUSED_PARAMETER2(NotUsed, NotUsed2); + return SQLITE_OK; +} +static int nolockUnlock(sqlcipher_sqlite3_file *NotUsed, int NotUsed2){ + UNUSED_PARAMETER2(NotUsed, NotUsed2); return SQLITE_OK; } -#endif /* !defined(SQLITE_THREADS_IMPLEMENTED) */ -/****************************** End Single-Threaded *************************/ -#endif /* SQLITE_MAX_WORKER_THREADS>0 */ - -/************** End of threads.c *********************************************/ -/************** Begin file utf.c *********************************************/ /* -** 2004 April 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 file contains routines used to translate between UTF-8, -** UTF-16, UTF-16BE, and UTF-16LE. -** -** Notes on UTF-8: -** -** Byte-0 Byte-1 Byte-2 Byte-3 Value -** 0xxxxxxx 00000000 00000000 0xxxxxxx -** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx -** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx -** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx -** +** Close the file. +*/ +static int nolockClose(sqlcipher_sqlite3_file *id) { + return closeUnixFile(id); +} + +/******************* End of the no-op lock implementation ********************* +******************************************************************************/ + +/****************************************************************************** +************************* Begin dot-file Locking ****************************** ** -** Notes on UTF-16: (with wwww+1==uuuuu) +** The dotfile locking implementation uses the existence of separate lock +** files (really a directory) to control access to the database. This works +** on just about every filesystem imaginable. But there are serious downsides: ** -** Word-0 Word-1 Value -** 110110ww wwzzzzyy 110111yy yyxxxxxx 000uuuuu zzzzyyyy yyxxxxxx -** zzzzyyyy yyxxxxxx 00000000 zzzzyyyy yyxxxxxx +** (1) There is zero concurrency. A single reader blocks all other +** connections from reading or writing the database. ** +** (2) An application crash or power loss can leave stale lock files +** sitting around that need to be cleared manually. ** -** BOM or Byte Order Mark: -** 0xff 0xfe little-endian utf-16 follows -** 0xfe 0xff big-endian utf-16 follows +** Nevertheless, a dotlock is an appropriate locking mode for use if no +** other locking strategy is available. ** +** Dotfile locking works by creating a subdirectory in the same directory as +** the database and with the same name but with a ".lock" extension added. +** The existence of a lock directory implies an EXCLUSIVE lock. All other +** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE. */ -/* #include "sqliteInt.h" */ -/* #include */ -/* #include "vdbeInt.h" */ -#if !defined(SQLITE_AMALGAMATION) && SQLITE_BYTEORDER==0 /* -** The following constant value is used by the SQLITE_BIGENDIAN and -** SQLITE_LITTLEENDIAN macros. +** The file suffix added to the data base filename in order to create the +** lock directory. */ -SQLITE_PRIVATE const int sqlcipher_sqlite3one = 1; -#endif /* SQLITE_AMALGAMATION && SQLITE_BYTEORDER==0 */ +#define DOTLOCK_SUFFIX ".lock" /* -** This lookup table is used to help decode the first byte of -** a multi-byte UTF8 character. +** This routine checks if there is a RESERVED lock held on the specified +** file by this or any other process. If such a lock is held, set *pResOut +** to a non-zero value otherwise *pResOut is set to zero. The return value +** is set to SQLITE_OK unless an I/O error occurs during lock checking. +** +** In dotfile locking, either a lock exists or it does not. So in this +** variation of CheckReservedLock(), *pResOut is set to true if any lock +** is held on the file and false if the file is unlocked. */ -static const unsigned char sqlcipher_sqlite3Utf8Trans1[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, -}; - - -#define WRITE_UTF8(zOut, c) { \ - if( c<0x00080 ){ \ - *zOut++ = (u8)(c&0xFF); \ - } \ - else if( c<0x00800 ){ \ - *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); \ - *zOut++ = 0x80 + (u8)(c & 0x3F); \ - } \ - else if( c<0x10000 ){ \ - *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); \ - *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (u8)(c & 0x3F); \ - }else{ \ - *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); \ - *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); \ - *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (u8)(c & 0x3F); \ - } \ -} +static int dotlockCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut) { + int rc = SQLITE_OK; + int reserved = 0; + unixFile *pFile = (unixFile*)id; -#define WRITE_UTF16LE(zOut, c) { \ - if( c<=0xFFFF ){ \ - *zOut++ = (u8)(c&0x00FF); \ - *zOut++ = (u8)((c>>8)&0x00FF); \ - }else{ \ - *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ - *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \ - *zOut++ = (u8)(c&0x00FF); \ - *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ - } \ -} + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); -#define WRITE_UTF16BE(zOut, c) { \ - if( c<=0xFFFF ){ \ - *zOut++ = (u8)((c>>8)&0x00FF); \ - *zOut++ = (u8)(c&0x00FF); \ - }else{ \ - *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \ - *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ - *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ - *zOut++ = (u8)(c&0x00FF); \ - } \ + assert( pFile ); + reserved = osAccess((const char*)pFile->lockingContext, 0)==0; + OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); + *pResOut = reserved; + return rc; } /* -** Translate a single UTF-8 character. Return the unicode value. -** -** During translation, assume that the byte that zTerm points -** is a 0x00. -** -** Write a pointer to the next unread byte back into *pzNext. +** Lock the file with the lock specified by parameter eFileLock - one +** of the following: ** -** Notes On Invalid UTF-8: +** (1) SHARED_LOCK +** (2) RESERVED_LOCK +** (3) PENDING_LOCK +** (4) EXCLUSIVE_LOCK ** -** * This routine never allows a 7-bit character (0x00 through 0x7f) to -** be encoded as a multi-byte character. Any multi-byte character that -** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd. +** Sometimes when requesting one lock state, additional lock states +** are inserted in between. The locking might fail on one of the later +** transitions leaving the lock state different from what it started but +** still short of its goal. The following chart shows the allowed +** transitions and the inserted intermediate states: ** -** * This routine never allows a UTF16 surrogate value to be encoded. -** If a multi-byte character attempts to encode a value between -** 0xd800 and 0xe000 then it is rendered as 0xfffd. +** UNLOCKED -> SHARED +** SHARED -> RESERVED +** SHARED -> (PENDING) -> EXCLUSIVE +** RESERVED -> (PENDING) -> EXCLUSIVE +** PENDING -> EXCLUSIVE ** -** * Bytes in the range of 0x80 through 0xbf which occur as the first -** byte of a character are interpreted as single-byte characters -** and rendered as themselves even though they are technically -** invalid characters. +** This routine will only increase a lock. Use the sqlcipher_sqlite3OsUnlock() +** routine to lower a locking level. ** -** * This routine accepts over-length UTF8 encodings -** for unicode values 0x80 and greater. It does not change over-length -** encodings to 0xfffd as some systems recommend. -*/ -#define READ_UTF8(zIn, zTerm, c) \ - c = *(zIn++); \ - if( c>=0xc0 ){ \ - c = sqlcipher_sqlite3Utf8Trans1[c-0xc0]; \ - while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ - c = (c<<6) + (0x3f & *(zIn++)); \ - } \ - if( c<0x80 \ - || (c&0xFFFFF800)==0xD800 \ - || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ - } -SQLITE_PRIVATE u32 sqlcipher_sqlite3Utf8Read( - const unsigned char **pz /* Pointer to string from which to read char */ -){ - unsigned int c; - - /* Same as READ_UTF8() above but without the zTerm parameter. - ** For this routine, we assume the UTF8 string is always zero-terminated. - */ - c = *((*pz)++); - if( c>=0xc0 ){ - c = sqlcipher_sqlite3Utf8Trans1[c-0xc0]; - while( (*(*pz) & 0xc0)==0x80 ){ - c = (c<<6) + (0x3f & *((*pz)++)); - } - if( c<0x80 - || (c&0xFFFFF800)==0xD800 - || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } - } - return c; -} - - - - -/* -** If the TRANSLATE_TRACE macro is defined, the value of each Mem is -** printed on stderr on the way into and out of sqlcipher_sqlite3VdbeMemTranslate(). -*/ -/* #define TRANSLATE_TRACE 1 */ - -#ifndef SQLITE_OMIT_UTF16 -/* -** This routine transforms the internal text encoding used by pMem to -** desiredEnc. It is an error if the string is already of the desired -** encoding, or if *pMem does not contain a string value. +** With dotfile locking, we really only support state (4): EXCLUSIVE. +** But we track the other locking levels internally. */ -SQLITE_PRIVATE SQLITE_NOINLINE int sqlcipher_sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ - sqlcipher_sqlite3_int64 len; /* Maximum length of output string in bytes */ - unsigned char *zOut; /* Output buffer */ - unsigned char *zIn; /* Input iterator */ - unsigned char *zTerm; /* End of input */ - unsigned char *z; /* Output iterator */ - unsigned int c; - - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - assert( pMem->flags&MEM_Str ); - assert( pMem->enc!=desiredEnc ); - assert( pMem->enc!=0 ); - assert( pMem->n>=0 ); - -#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG) - { - StrAccum acc; - char zBuf[1000]; - sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); - sqlcipher_sqlite3VdbeMemPrettyPrint(pMem, &acc); - fprintf(stderr, "INPUT: %s\n", sqlcipher_sqlite3StrAccumFinish(&acc)); - } -#endif - - /* If the translation is between UTF-16 little and big endian, then - ** all that is required is to swap the byte order. This case is handled - ** differently from the others. - */ - if( pMem->enc!=SQLITE_UTF8 && desiredEnc!=SQLITE_UTF8 ){ - u8 temp; - int rc; - rc = sqlcipher_sqlite3VdbeMemMakeWriteable(pMem); - if( rc!=SQLITE_OK ){ - assert( rc==SQLITE_NOMEM ); - return SQLITE_NOMEM_BKPT; - } - zIn = (u8*)pMem->z; - zTerm = &zIn[pMem->n&~1]; - while( zInenc = desiredEnc; - goto translate_out; - } +static int dotlockLock(sqlcipher_sqlite3_file *id, int eFileLock) { + unixFile *pFile = (unixFile*)id; + char *zLockFile = (char *)pFile->lockingContext; + int rc = SQLITE_OK; - /* Set len to the maximum number of bytes required in the output buffer. */ - if( desiredEnc==SQLITE_UTF8 ){ - /* When converting from UTF-16, the maximum growth results from - ** translating a 2-byte character to a 4-byte UTF-8 character. - ** A single byte is required for the output string - ** nul-terminator. - */ - pMem->n &= ~1; - len = 2 * (sqlcipher_sqlite3_int64)pMem->n + 1; - }else{ - /* When converting from UTF-8 to UTF-16 the maximum growth is caused - ** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16 - ** character. Two bytes are required in the output buffer for the - ** nul-terminator. - */ - len = 2 * (sqlcipher_sqlite3_int64)pMem->n + 2; - } - /* Set zIn to point at the start of the input buffer and zTerm to point 1 - ** byte past the end. - ** - ** Variable zOut is set to point at the output buffer, space obtained - ** from sqlcipher_sqlite3_malloc(). + /* If we have any lock, then the lock file already exists. All we have + ** to do is adjust our internal record of the lock level. */ - zIn = (u8*)pMem->z; - zTerm = &zIn[pMem->n]; - zOut = sqlcipher_sqlite3DbMallocRaw(pMem->db, len); - if( !zOut ){ - return SQLITE_NOMEM_BKPT; - } - z = zOut; - - if( pMem->enc==SQLITE_UTF8 ){ - if( desiredEnc==SQLITE_UTF16LE ){ - /* UTF-8 -> UTF-16 Little-endian */ - while( zIn UTF-16 Big-endian */ - while( zInn = (int)(z - zOut); - *z++ = 0; - }else{ - assert( desiredEnc==SQLITE_UTF8 ); - if( pMem->enc==SQLITE_UTF16LE ){ - /* UTF-16 Little-endian -> UTF-8 */ - while( zIn=0xd800 && c<0xe000 ){ -#ifdef SQLITE_REPLACE_INVALID_UTF - if( c>=0xdc00 || zIn>=zTerm ){ - c = 0xfffd; - }else{ - int c2 = *(zIn++); - c2 += (*(zIn++))<<8; - if( c2<0xdc00 || c2>=0xe000 ){ - zIn -= 2; - c = 0xfffd; - }else{ - c = ((c&0x3ff)<<10) + (c2&0x3ff) + 0x10000; - } - } -#else - if( zIn UTF-8 */ - while( zIn=0xd800 && c<0xe000 ){ -#ifdef SQLITE_REPLACE_INVALID_UTF - if( c>=0xdc00 || zIn>=zTerm ){ - c = 0xfffd; - }else{ - int c2 = (*(zIn++))<<8; - c2 += *(zIn++); - if( c2<0xdc00 || c2>=0xe000 ){ - zIn -= 2; - c = 0xfffd; - }else{ - c = ((c&0x3ff)<<10) + (c2&0x3ff) + 0x10000; - } - } + if( pFile->eFileLock > NO_LOCK ){ + pFile->eFileLock = eFileLock; + /* Always update the timestamp on the old file */ +#ifdef HAVE_UTIME + utime(zLockFile, NULL); #else - if( zInn = (int)(z - zOut); + return rc; } - *z = 0; - assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len ); - - c = MEM_Str|MEM_Term|(pMem->flags&(MEM_AffMask|MEM_Subtype)); - sqlcipher_sqlite3VdbeMemRelease(pMem); - pMem->flags = c; - pMem->enc = desiredEnc; - pMem->z = (char*)zOut; - pMem->zMalloc = pMem->z; - pMem->szMalloc = sqlcipher_sqlite3DbMallocSize(pMem->db, pMem->z); -translate_out: -#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG) - { - StrAccum acc; - char zBuf[1000]; - sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); - sqlcipher_sqlite3VdbeMemPrettyPrint(pMem, &acc); - fprintf(stderr, "OUTPUT: %s\n", sqlcipher_sqlite3StrAccumFinish(&acc)); - } -#endif - return SQLITE_OK; + /* got it, set the type and return ok */ + pFile->eFileLock = eFileLock; + return rc; } -#endif /* SQLITE_OMIT_UTF16 */ -#ifndef SQLITE_OMIT_UTF16 /* -** This routine checks for a byte-order mark at the beginning of the -** UTF-16 string stored in *pMem. If one is present, it is removed and -** the encoding of the Mem adjusted. This routine does not do any -** byte-swapping, it just sets Mem.enc appropriately. +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock +** must be either NO_LOCK or SHARED_LOCK. ** -** The allocation (static, dynamic etc.) and encoding of the Mem may be -** changed by this function. +** If the locking level of the file descriptor is already at or below +** the requested locking level, this routine is a no-op. +** +** When the locking level reaches NO_LOCK, delete the lock file. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemHandleBom(Mem *pMem){ - int rc = SQLITE_OK; - u8 bom = 0; +static int dotlockUnlock(sqlcipher_sqlite3_file *id, int eFileLock) { + unixFile *pFile = (unixFile*)id; + char *zLockFile = (char *)pFile->lockingContext; + int rc; - assert( pMem->n>=0 ); - if( pMem->n>1 ){ - u8 b1 = *(u8 *)pMem->z; - u8 b2 = *(((u8 *)pMem->z) + 1); - if( b1==0xFE && b2==0xFF ){ - bom = SQLITE_UTF16BE; - } - if( b1==0xFF && b2==0xFE ){ - bom = SQLITE_UTF16LE; - } + assert( pFile ); + OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock, + pFile->eFileLock, osGetpid(0))); + assert( eFileLock<=SHARED_LOCK ); + + /* no-op if possible */ + if( pFile->eFileLock==eFileLock ){ + return SQLITE_OK; } - if( bom ){ - rc = sqlcipher_sqlite3VdbeMemMakeWriteable(pMem); - if( rc==SQLITE_OK ){ - pMem->n -= 2; - memmove(pMem->z, &pMem->z[2], pMem->n); - pMem->z[pMem->n] = '\0'; - pMem->z[pMem->n+1] = '\0'; - pMem->flags |= MEM_Term; - pMem->enc = bom; + /* To downgrade to shared, simply update our internal notion of the + ** lock state. No need to mess with the file on disk. + */ + if( eFileLock==SHARED_LOCK ){ + pFile->eFileLock = SHARED_LOCK; + return SQLITE_OK; + } + + /* To fully unlock the database, delete the lock file */ + assert( eFileLock==NO_LOCK ); + rc = osRmdir(zLockFile); + if( rc<0 ){ + int tErrno = errno; + if( tErrno==ENOENT ){ + rc = SQLITE_OK; + }else{ + rc = SQLITE_IOERR_UNLOCK; + storeLastErrno(pFile, tErrno); } + return rc; } - return rc; + pFile->eFileLock = NO_LOCK; + return SQLITE_OK; } -#endif /* SQLITE_OMIT_UTF16 */ /* -** pZ is a UTF-8 encoded unicode string. If nByte is less than zero, -** return the number of unicode characters in pZ up to (but not including) -** the first 0x00 byte. If nByte is not less than zero, return the -** number of unicode characters in the first nByte of pZ (or up to -** the first 0x00, whichever comes first). +** Close a file. Make sure the lock has been released before closing. */ -SQLITE_PRIVATE int sqlcipher_sqlite3Utf8CharLen(const char *zIn, int nByte){ - int r = 0; - const u8 *z = (const u8*)zIn; - const u8 *zTerm; - if( nByte>=0 ){ - zTerm = &z[nByte]; - }else{ - zTerm = (const u8*)(-1); - } - assert( z<=zTerm ); - while( *z!=0 && zlockingContext); + return closeUnixFile(id); } +/****************** End of the dot-file lock implementation ******************* +******************************************************************************/ -/* This test function is not currently used by the automated test-suite. -** Hence it is only available in debug builds. -*/ -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -/* -** Translate UTF-8 to UTF-8. +/****************************************************************************** +************************** Begin flock Locking ******************************** ** -** This has the effect of making sure that the string is well-formed -** UTF-8. Miscoded characters are removed. +** Use the flock() system call to do file locking. ** -** The translation is done in-place and aborted if the output -** overruns the input. +** flock() locking is like dot-file locking in that the various +** fine-grain locking levels supported by SQLite are collapsed into +** a single exclusive lock. In other words, SHARED, RESERVED, and +** PENDING locks are the same thing as an EXCLUSIVE lock. SQLite +** still works when you do this, but concurrency is reduced since +** only a single process can be reading the database at a time. +** +** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off */ -SQLITE_PRIVATE int sqlcipher_sqlite3Utf8To8(unsigned char *zIn){ - unsigned char *zOut = zIn; - unsigned char *zStart = zIn; - u32 c; - - while( zIn[0] && zOut<=zIn ){ - c = sqlcipher_sqlite3Utf8Read((const u8**)&zIn); - if( c!=0xfffd ){ - WRITE_UTF8(zOut, c); - } - } - *zOut = 0; - return (int)(zOut - zStart); -} -#endif +#if SQLITE_ENABLE_LOCKING_STYLE -#ifndef SQLITE_OMIT_UTF16 /* -** Convert a UTF-16 string in the native encoding into a UTF-8 string. -** Memory to hold the UTF-8 string is obtained from sqlcipher_sqlite3_malloc and must -** be freed by the calling function. -** -** NULL is returned if there is an allocation error. +** Retry flock() calls that fail with EINTR */ -SQLITE_PRIVATE char *sqlcipher_sqlite3Utf16to8(sqlcipher_sqlite3 *db, const void *z, int nByte, u8 enc){ - Mem m; - memset(&m, 0, sizeof(m)); - m.db = db; - sqlcipher_sqlite3VdbeMemSetStr(&m, z, nByte, enc, SQLITE_STATIC); - sqlcipher_sqlite3VdbeChangeEncoding(&m, SQLITE_UTF8); - if( db->mallocFailed ){ - sqlcipher_sqlite3VdbeMemRelease(&m); - m.z = 0; - } - assert( (m.flags & MEM_Term)!=0 || db->mallocFailed ); - assert( (m.flags & MEM_Str)!=0 || db->mallocFailed ); - assert( m.z || db->mallocFailed ); - return m.z; +#ifdef EINTR +static int robust_flock(int fd, int op){ + int rc; + do{ rc = flock(fd,op); }while( rc<0 && errno==EINTR ); + return rc; } +#else +# define robust_flock(a,b) flock(a,b) +#endif + /* -** zIn is a UTF-16 encoded unicode string at least nChar characters long. -** Return the number of bytes in the first nChar unicode characters -** in pZ. nChar must be non-negative. +** This routine checks if there is a RESERVED lock held on the specified +** file by this or any other process. If such a lock is held, set *pResOut +** to a non-zero value otherwise *pResOut is set to zero. The return value +** is set to SQLITE_OK unless an I/O error occurs during lock checking. */ -SQLITE_PRIVATE int sqlcipher_sqlite3Utf16ByteLen(const void *zIn, int nChar){ - int c; - unsigned char const *z = zIn; - int n = 0; +static int flockCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ + int rc = SQLITE_OK; + int reserved = 0; + unixFile *pFile = (unixFile*)id; - if( SQLITE_UTF16NATIVE==SQLITE_UTF16LE ) z++; - while( n=0xd8 && c<0xdc && z[0]>=0xdc && z[0]<0xe0 ) z += 2; - n++; - } - return (int)(z-(unsigned char const *)zIn) - - (SQLITE_UTF16NATIVE==SQLITE_UTF16LE); -} + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); -#if defined(SQLITE_TEST) -/* -** This routine is called from the TCL test function "translate_selftest". -** It checks that the primitives for serializing and deserializing -** characters in each encoding are inverses of each other. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3UtfSelfTest(void){ - unsigned int i, t; - unsigned char zBuf[20]; - unsigned char *z; - int n; - unsigned int c; + assert( pFile ); - for(i=0; i<0x00110000; i++){ - z = zBuf; - WRITE_UTF8(z, i); - n = (int)(z-zBuf); - assert( n>0 && n<=4 ); - z[0] = 0; - z = zBuf; - c = sqlcipher_sqlite3Utf8Read((const u8**)&z); - t = i; - if( i>=0xD800 && i<=0xDFFF ) t = 0xFFFD; - if( (i&0xFFFFFFFE)==0xFFFE ) t = 0xFFFD; - assert( c==t ); - assert( (z-zBuf)==n ); + /* Check if a thread in this process holds such a lock */ + if( pFile->eFileLock>SHARED_LOCK ){ + reserved = 1; } -} -#endif /* SQLITE_TEST */ -#endif /* SQLITE_OMIT_UTF16 */ -/************** End of utf.c *************************************************/ -/************** Begin file util.c ********************************************/ -/* -** 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. -** -************************************************************************* -** Utility functions used throughout sqlite. -** -** This file contains functions for allocating memory, comparing -** strings, and stuff like that. -** -*/ -/* #include "sqliteInt.h" */ -/* #include */ -#ifndef SQLITE_OMIT_FLOATING_POINT -#include -#endif + /* Otherwise see if some other process holds it. */ + if( !reserved ){ + /* attempt to get the lock */ + int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); + if( !lrc ){ + /* got the lock, unlock it */ + lrc = robust_flock(pFile->h, LOCK_UN); + if ( lrc ) { + int tErrno = errno; + /* unlock failed with an error */ + lrc = SQLITE_IOERR_UNLOCK; + storeLastErrno(pFile, tErrno); + rc = lrc; + } + } else { + int tErrno = errno; + reserved = 1; + /* someone else might have it reserved */ + lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); + if( IS_LOCK_ERROR(lrc) ){ + storeLastErrno(pFile, tErrno); + rc = lrc; + } + } + } + OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); -/* -** Routine needed to support the testcase() macro. -*/ -#ifdef SQLITE_COVERAGE_TEST -SQLITE_PRIVATE void sqlcipher_sqlite3Coverage(int x){ - static unsigned dummy = 0; - dummy += (unsigned)x; +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS + if( (rc & 0xff) == SQLITE_IOERR ){ + rc = SQLITE_OK; + reserved=1; + } +#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ + *pResOut = reserved; + return rc; } -#endif /* -** Calls to sqlcipher_sqlite3FaultSim() are used to simulate a failure during testing, -** or to bypass normal error detection during testing in order to let -** execute proceed futher downstream. +** Lock the file with the lock specified by parameter eFileLock - one +** of the following: ** -** In deployment, sqlcipher_sqlite3FaultSim() *always* return SQLITE_OK (0). The -** sqlcipher_sqlite3FaultSim() function only returns non-zero during testing. +** (1) SHARED_LOCK +** (2) RESERVED_LOCK +** (3) PENDING_LOCK +** (4) EXCLUSIVE_LOCK ** -** During testing, if the test harness has set a fault-sim callback using -** a call to sqlcipher_sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL), then -** each call to sqlcipher_sqlite3FaultSim() is relayed to that application-supplied -** callback and the integer return value form the application-supplied -** callback is returned by sqlcipher_sqlite3FaultSim(). +** Sometimes when requesting one lock state, additional lock states +** are inserted in between. The locking might fail on one of the later +** transitions leaving the lock state different from what it started but +** still short of its goal. The following chart shows the allowed +** transitions and the inserted intermediate states: ** -** The integer argument to sqlcipher_sqlite3FaultSim() is a code to identify which -** sqlcipher_sqlite3FaultSim() instance is being invoked. Each call to sqlcipher_sqlite3FaultSim() -** should have a unique code. To prevent legacy testing applications from -** breaking, the codes should not be changed or reused. +** UNLOCKED -> SHARED +** SHARED -> RESERVED +** SHARED -> (PENDING) -> EXCLUSIVE +** RESERVED -> (PENDING) -> EXCLUSIVE +** PENDING -> EXCLUSIVE +** +** flock() only really support EXCLUSIVE locks. We track intermediate +** lock states in the sqlcipher_sqlite3_file structure, but all locks SHARED or +** above are really EXCLUSIVE locks and exclude all other processes from +** access the file. +** +** This routine will only increase a lock. Use the sqlcipher_sqlite3OsUnlock() +** routine to lower a locking level. */ -#ifndef SQLITE_UNTESTABLE -SQLITE_PRIVATE int sqlcipher_sqlite3FaultSim(int iTest){ - int (*xCallback)(int) = sqlcipher_sqlite3GlobalConfig.xTestCallback; - return xCallback ? xCallback(iTest) : SQLITE_OK; -} -#endif +static int flockLock(sqlcipher_sqlite3_file *id, int eFileLock) { + int rc = SQLITE_OK; + unixFile *pFile = (unixFile*)id; -#ifndef SQLITE_OMIT_FLOATING_POINT -/* -** Return true if the floating point value is Not a Number (NaN). -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3IsNaN(double x){ - u64 y; - memcpy(&y,&x,sizeof(y)); - return IsNaN(y); -} -#endif /* SQLITE_OMIT_FLOATING_POINT */ + assert( pFile ); -/* -** Compute a string length that is limited to what can be stored in -** lower 30 bits of a 32-bit signed integer. -** -** The value returned will never be negative. Nor will it ever be greater -** than the actual length of the string. For very long strings (greater -** than 1GiB) the value returned might be less than the true string length. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3Strlen30(const char *z){ - if( z==0 ) return 0; - return 0x3fffffff & (int)strlen(z); + /* if we already have a lock, it is exclusive. + ** Just adjust level and punt on outta here. */ + if (pFile->eFileLock > NO_LOCK) { + pFile->eFileLock = eFileLock; + return SQLITE_OK; + } + + /* grab an exclusive lock */ + + if (robust_flock(pFile->h, LOCK_EX | LOCK_NB)) { + int tErrno = errno; + /* didn't get, must be busy */ + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); + if( IS_LOCK_ERROR(rc) ){ + storeLastErrno(pFile, tErrno); + } + } else { + /* got it, set the type and return ok */ + pFile->eFileLock = eFileLock; + } + OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock), + rc==SQLITE_OK ? "ok" : "failed")); +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS + if( (rc & 0xff) == SQLITE_IOERR ){ + rc = SQLITE_BUSY; + } +#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ + return rc; } + /* -** Return the declared type of a column. Or return zDflt if the column -** has no declared type. +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock +** must be either NO_LOCK or SHARED_LOCK. ** -** The column type is an extra string stored after the zero-terminator on -** the column name if and only if the COLFLAG_HASTYPE flag is set. +** If the locking level of the file descriptor is already at or below +** the requested locking level, this routine is a no-op. */ -SQLITE_PRIVATE char *sqlcipher_sqlite3ColumnType(Column *pCol, char *zDflt){ - if( (pCol->colFlags & COLFLAG_HASTYPE)==0 ) return zDflt; - return pCol->zName + strlen(pCol->zName) + 1; +static int flockUnlock(sqlcipher_sqlite3_file *id, int eFileLock) { + unixFile *pFile = (unixFile*)id; + + assert( pFile ); + OSTRACE(("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, eFileLock, + pFile->eFileLock, osGetpid(0))); + assert( eFileLock<=SHARED_LOCK ); + + /* no-op if possible */ + if( pFile->eFileLock==eFileLock ){ + return SQLITE_OK; + } + + /* shared can just be set because we always have an exclusive */ + if (eFileLock==SHARED_LOCK) { + pFile->eFileLock = eFileLock; + return SQLITE_OK; + } + + /* no, really, unlock. */ + if( robust_flock(pFile->h, LOCK_UN) ){ +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS + return SQLITE_OK; +#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ + return SQLITE_IOERR_UNLOCK; + }else{ + pFile->eFileLock = NO_LOCK; + return SQLITE_OK; + } } /* -** Helper function for sqlcipher_sqlite3Error() - called rarely. Broken out into -** a separate routine to avoid unnecessary register saves on entry to -** sqlcipher_sqlite3Error(). +** Close a file. */ -static SQLITE_NOINLINE void sqlcipher_sqlite3ErrorFinish(sqlcipher_sqlite3 *db, int err_code){ - if( db->pErr ) sqlcipher_sqlite3ValueSetNull(db->pErr); - sqlcipher_sqlite3SystemError(db, err_code); +static int flockClose(sqlcipher_sqlite3_file *id) { + assert( id!=0 ); + flockUnlock(id, NO_LOCK); + return closeUnixFile(id); } -/* -** Set the current error code to err_code and clear any prior error message. -** Also set iSysErrno (by calling sqlcipher_sqlite3System) if the err_code indicates -** that would be appropriate. +#endif /* SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORK */ + +/******************* End of the flock lock implementation ********************* +******************************************************************************/ + +/****************************************************************************** +************************ Begin Named Semaphore Locking ************************ +** +** Named semaphore locking is only supported on VxWorks. +** +** Semaphore locking is like dot-lock and flock in that it really only +** supports EXCLUSIVE locking. Only a single process can read or write +** the database file at a time. This reduces potential concurrency, but +** makes the lock implementation much easier. */ -SQLITE_PRIVATE void sqlcipher_sqlite3Error(sqlcipher_sqlite3 *db, int err_code){ - assert( db!=0 ); - db->errCode = err_code; - if( err_code || db->pErr ) sqlcipher_sqlite3ErrorFinish(db, err_code); -} +#if OS_VXWORKS /* -** Load the sqlcipher_sqlite3.iSysErrno field if that is an appropriate thing -** to do based on the SQLite error code in rc. +** This routine checks if there is a RESERVED lock held on the specified +** file by this or any other process. If such a lock is held, set *pResOut +** to a non-zero value otherwise *pResOut is set to zero. The return value +** is set to SQLITE_OK unless an I/O error occurs during lock checking. */ -SQLITE_PRIVATE void sqlcipher_sqlite3SystemError(sqlcipher_sqlite3 *db, int rc){ - if( rc==SQLITE_IOERR_NOMEM ) return; - rc &= 0xff; - if( rc==SQLITE_CANTOPEN || rc==SQLITE_IOERR ){ - db->iSysErrno = sqlcipher_sqlite3OsGetLastError(db->pVfs); +static int semXCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut) { + int rc = SQLITE_OK; + int reserved = 0; + unixFile *pFile = (unixFile*)id; + + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); + + assert( pFile ); + + /* Check if a thread in this process holds such a lock */ + if( pFile->eFileLock>SHARED_LOCK ){ + reserved = 1; + } + + /* Otherwise see if some other process holds it. */ + if( !reserved ){ + sem_t *pSem = pFile->pInode->pSem; + + if( sem_trywait(pSem)==-1 ){ + int tErrno = errno; + if( EAGAIN != tErrno ){ + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); + storeLastErrno(pFile, tErrno); + } else { + /* someone else has the lock when we are in NO_LOCK */ + reserved = (pFile->eFileLock < SHARED_LOCK); + } + }else{ + /* we could have it if we want it */ + sem_post(pSem); + } } + OSTRACE(("TEST WR-LOCK %d %d %d (sem)\n", pFile->h, rc, reserved)); + + *pResOut = reserved; + return rc; } /* -** Set the most recent error code and error string for the sqlite -** handle "db". The error code is set to "err_code". +** Lock the file with the lock specified by parameter eFileLock - one +** of the following: ** -** If it is not NULL, string zFormat specifies the format of the -** error string in the style of the printf functions: The following -** format characters are allowed: +** (1) SHARED_LOCK +** (2) RESERVED_LOCK +** (3) PENDING_LOCK +** (4) EXCLUSIVE_LOCK +** +** Sometimes when requesting one lock state, additional lock states +** are inserted in between. The locking might fail on one of the later +** transitions leaving the lock state different from what it started but +** still short of its goal. The following chart shows the allowed +** transitions and the inserted intermediate states: ** -** %s Insert a string -** %z A string that should be freed after use -** %d Insert an integer -** %T Insert a token -** %S Insert the first element of a SrcList +** UNLOCKED -> SHARED +** SHARED -> RESERVED +** SHARED -> (PENDING) -> EXCLUSIVE +** RESERVED -> (PENDING) -> EXCLUSIVE +** PENDING -> EXCLUSIVE ** -** zFormat and any string tokens that follow it are assumed to be -** encoded in UTF-8. +** Semaphore locks only really support EXCLUSIVE locks. We track intermediate +** lock states in the sqlcipher_sqlite3_file structure, but all locks SHARED or +** above are really EXCLUSIVE locks and exclude all other processes from +** access the file. ** -** To clear the most recent error for sqlite handle "db", sqlcipher_sqlite3Error -** should be called with err_code set to SQLITE_OK and zFormat set -** to NULL. +** This routine will only increase a lock. Use the sqlcipher_sqlite3OsUnlock() +** routine to lower a locking level. */ -SQLITE_PRIVATE void sqlcipher_sqlite3ErrorWithMsg(sqlcipher_sqlite3 *db, int err_code, const char *zFormat, ...){ - assert( db!=0 ); - db->errCode = err_code; - sqlcipher_sqlite3SystemError(db, err_code); - if( zFormat==0 ){ - sqlcipher_sqlite3Error(db, err_code); - }else if( db->pErr || (db->pErr = sqlcipher_sqlite3ValueNew(db))!=0 ){ - char *z; - va_list ap; - va_start(ap, zFormat); - z = sqlcipher_sqlite3VMPrintf(db, zFormat, ap); - va_end(ap); - sqlcipher_sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC); +static int semXLock(sqlcipher_sqlite3_file *id, int eFileLock) { + unixFile *pFile = (unixFile*)id; + sem_t *pSem = pFile->pInode->pSem; + int rc = SQLITE_OK; + + /* if we already have a lock, it is exclusive. + ** Just adjust level and punt on outta here. */ + if (pFile->eFileLock > NO_LOCK) { + pFile->eFileLock = eFileLock; + rc = SQLITE_OK; + goto sem_end_lock; } + + /* lock semaphore now but bail out when already locked. */ + if( sem_trywait(pSem)==-1 ){ + rc = SQLITE_BUSY; + goto sem_end_lock; + } + + /* got it, set the type and return ok */ + pFile->eFileLock = eFileLock; + + sem_end_lock: + return rc; } /* -** Add an error message to pParse->zErrMsg and increment pParse->nErr. -** The following formatting characters are allowed: -** -** %s Insert a string -** %z A string that should be freed after use -** %d Insert an integer -** %T Insert a token -** %S Insert the first element of a SrcList +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock +** must be either NO_LOCK or SHARED_LOCK. ** -** This function should be used to report any error that occurs while -** compiling an SQL statement (i.e. within sqlcipher_sqlite3_prepare()). The -** last thing the sqlcipher_sqlite3_prepare() function does is copy the error -** stored by this function into the database handle using sqlcipher_sqlite3Error(). -** Functions sqlcipher_sqlite3Error() or sqlcipher_sqlite3ErrorWithMsg() should be used -** during statement execution (sqlcipher_sqlite3_step() etc.). +** If the locking level of the file descriptor is already at or below +** the requested locking level, this routine is a no-op. */ -SQLITE_PRIVATE void sqlcipher_sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ - char *zMsg; - va_list ap; - sqlcipher_sqlite3 *db = pParse->db; - va_start(ap, zFormat); - zMsg = sqlcipher_sqlite3VMPrintf(db, zFormat, ap); - va_end(ap); - if( db->suppressErr ){ - sqlcipher_sqlite3DbFree(db, zMsg); - }else{ - pParse->nErr++; - sqlcipher_sqlite3DbFree(db, pParse->zErrMsg); - pParse->zErrMsg = zMsg; - pParse->rc = SQLITE_ERROR; - pParse->pWith = 0; +static int semXUnlock(sqlcipher_sqlite3_file *id, int eFileLock) { + unixFile *pFile = (unixFile*)id; + sem_t *pSem = pFile->pInode->pSem; + + assert( pFile ); + assert( pSem ); + OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock, + pFile->eFileLock, osGetpid(0))); + assert( eFileLock<=SHARED_LOCK ); + + /* no-op if possible */ + if( pFile->eFileLock==eFileLock ){ + return SQLITE_OK; + } + + /* shared can just be set because we always have an exclusive */ + if (eFileLock==SHARED_LOCK) { + pFile->eFileLock = eFileLock; + return SQLITE_OK; + } + + /* no, really unlock. */ + if ( sem_post(pSem)==-1 ) { + int rc, tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + if( IS_LOCK_ERROR(rc) ){ + storeLastErrno(pFile, tErrno); + } + return rc; } + pFile->eFileLock = NO_LOCK; + return SQLITE_OK; } /* -** If database connection db is currently parsing SQL, then transfer -** error code errCode to that parser if the parser has not already -** encountered some other kind of error. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3ErrorToParser(sqlcipher_sqlite3 *db, int errCode){ - Parse *pParse; - if( db==0 || (pParse = db->pParse)==0 ) return errCode; - pParse->rc = errCode; - pParse->nErr++; - return errCode; + ** Close a file. + */ +static int semXClose(sqlcipher_sqlite3_file *id) { + if( id ){ + unixFile *pFile = (unixFile*)id; + semXUnlock(id, NO_LOCK); + assert( pFile ); + assert( unixFileMutexNotheld(pFile) ); + unixEnterMutex(); + releaseInodeInfo(pFile); + unixLeaveMutex(); + closeUnixFile(id); + } + return SQLITE_OK; } +#endif /* OS_VXWORKS */ /* -** Convert an SQL-style quoted string into a normal string by removing -** the quote characters. The conversion is done in-place. If the -** input does not begin with a quote character, then this routine -** is a no-op. +** Named semaphore locking is only available on VxWorks. ** -** The input string must be zero-terminated. A new zero-terminator -** is added to the dequoted string. +*************** End of the named semaphore lock implementation **************** +******************************************************************************/ + + +/****************************************************************************** +*************************** Begin AFP Locking ********************************* ** -** The return value is -1 if no dequoting occurs or the length of the -** dequoted string, exclusive of the zero terminator, if dequoting does -** occur. +** AFP is the Apple Filing Protocol. AFP is a network filesystem found +** on Apple Macintosh computers - both OS9 and OSX. ** -** 2002-02-14: This routine is extended to remove MS-Access style -** brackets from around identifiers. For example: "[a-b-c]" becomes -** "a-b-c". +** Third-party implementations of AFP are available. But this code here +** only works on OSX. */ -SQLITE_PRIVATE void sqlcipher_sqlite3Dequote(char *z){ - char quote; - int i, j; - if( z==0 ) return; - quote = z[0]; - if( !sqlcipher_sqlite3Isquote(quote) ) return; - if( quote=='[' ) quote = ']'; - for(i=1, j=0;; i++){ - assert( z[i] ); - if( z[i]==quote ){ - if( z[i+1]==quote ){ - z[j++] = quote; - i++; - }else{ - break; - } - }else{ - z[j++] = z[i]; - } - } - z[j] = 0; -} -SQLITE_PRIVATE void sqlcipher_sqlite3DequoteExpr(Expr *p){ - assert( sqlcipher_sqlite3Isquote(p->u.zToken[0]) ); - p->flags |= p->u.zToken[0]=='"' ? EP_Quoted|EP_DblQuoted : EP_Quoted; - sqlcipher_sqlite3Dequote(p->u.zToken); -} +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE /* -** Generate a Token object from a string +** The afpLockingContext structure contains all afp lock specific state */ -SQLITE_PRIVATE void sqlcipher_sqlite3TokenInit(Token *p, char *z){ - p->z = z; - p->n = sqlcipher_sqlite3Strlen30(z); -} +typedef struct afpLockingContext afpLockingContext; +struct afpLockingContext { + int reserved; + const char *dbPath; /* Name of the open file */ +}; -/* Convenient short-hand */ -#define UpperToLower sqlcipher_sqlite3UpperToLower +struct ByteRangeLockPB2 +{ + unsigned long long offset; /* offset to first byte to lock */ + unsigned long long length; /* nbr of bytes to lock */ + unsigned long long retRangeStart; /* nbr of 1st byte locked if successful */ + unsigned char unLockFlag; /* 1 = unlock, 0 = lock */ + unsigned char startEndFlag; /* 1=rel to end of fork, 0=rel to start */ + int fd; /* file desc to assoc this lock with */ +}; + +#define afpfsByteRangeLock2FSCTL _IOWR('z', 23, struct ByteRangeLockPB2) /* -** Some systems have stricmp(). Others have strcasecmp(). Because -** there is no consistency, we will define our own. +** This is a utility for setting or clearing a bit-range lock on an +** AFP filesystem. ** -** IMPLEMENTATION-OF: R-30243-02494 The sqlcipher_sqlite3_stricmp() and -** sqlcipher_sqlite3_strnicmp() APIs allow applications and extensions to compare -** the contents of two buffers containing UTF-8 strings in a -** case-independent fashion, using the same definition of "case -** independence" that SQLite uses internally when comparing identifiers. +** Return SQLITE_OK on success, SQLITE_BUSY on failure. */ -SQLITE_API int sqlcipher_sqlite3_stricmp(const char *zLeft, const char *zRight){ - if( zLeft==0 ){ - return zRight ? -1 : 0; - }else if( zRight==0 ){ - return 1; - } - return sqlcipher_sqlite3StrICmp(zLeft, zRight); -} -SQLITE_PRIVATE int sqlcipher_sqlite3StrICmp(const char *zLeft, const char *zRight){ - unsigned char *a, *b; - int c, x; - a = (unsigned char *)zLeft; - b = (unsigned char *)zRight; - for(;;){ - c = *a; - x = *b; - if( c==x ){ - if( c==0 ) break; - }else{ - c = (int)UpperToLower[c] - (int)UpperToLower[x]; - if( c ) break; +static int afpSetLock( + const char *path, /* Name of the file to be locked or unlocked */ + unixFile *pFile, /* Open file descriptor on path */ + unsigned long long offset, /* First byte to be locked */ + unsigned long long length, /* Number of bytes to lock */ + int setLockFlag /* True to set lock. False to clear lock */ +){ + struct ByteRangeLockPB2 pb; + int err; + + pb.unLockFlag = setLockFlag ? 0 : 1; + pb.startEndFlag = 0; + pb.offset = offset; + pb.length = length; + pb.fd = pFile->h; + + OSTRACE(("AFPSETLOCK [%s] for %d%s in range %llx:%llx\n", + (setLockFlag?"ON":"OFF"), pFile->h, (pb.fd==-1?"[testval-1]":""), + offset, length)); + err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0); + if ( err==-1 ) { + int rc; + int tErrno = errno; + OSTRACE(("AFPSETLOCK failed to fsctl() '%s' %d %s\n", + path, tErrno, strerror(tErrno))); +#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS + rc = SQLITE_BUSY; +#else + rc = sqliteErrorFromPosixError(tErrno, + setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK); +#endif /* SQLITE_IGNORE_AFP_LOCK_ERRORS */ + if( IS_LOCK_ERROR(rc) ){ + storeLastErrno(pFile, tErrno); } - a++; - b++; - } - return c; -} -SQLITE_API int sqlcipher_sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ - register unsigned char *a, *b; - if( zLeft==0 ){ - return zRight ? -1 : 0; - }else if( zRight==0 ){ - return 1; + return rc; + } else { + return SQLITE_OK; } - a = (unsigned char *)zLeft; - b = (unsigned char *)zRight; - while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } - return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; } /* -** Compute an 8-bit hash on a string that is insensitive to case differences +** This routine checks if there is a RESERVED lock held on the specified +** file by this or any other process. If such a lock is held, set *pResOut +** to a non-zero value otherwise *pResOut is set to zero. The return value +** is set to SQLITE_OK unless an I/O error occurs during lock checking. */ -SQLITE_PRIVATE u8 sqlcipher_sqlite3StrIHash(const char *z){ - u8 h = 0; - if( z==0 ) return 0; - while( z[0] ){ - h += UpperToLower[(unsigned char)z[0]]; - z++; - } - return h; -} +static int afpCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ + int rc = SQLITE_OK; + int reserved = 0; + unixFile *pFile = (unixFile*)id; + afpLockingContext *context; -/* -** Compute 10 to the E-th power. Examples: E==1 results in 10. -** E==2 results in 100. E==50 results in 1.0e50. -** -** This routine only works for values of E between 1 and 341. -*/ -static LONGDOUBLE_TYPE sqlcipher_sqlite3Pow10(int E){ -#if defined(_MSC_VER) - static const LONGDOUBLE_TYPE x[] = { - 1.0e+001L, - 1.0e+002L, - 1.0e+004L, - 1.0e+008L, - 1.0e+016L, - 1.0e+032L, - 1.0e+064L, - 1.0e+128L, - 1.0e+256L - }; - LONGDOUBLE_TYPE r = 1.0; - int i; - assert( E>=0 && E<=307 ); - for(i=0; E!=0; i++, E >>=1){ - if( E & 1 ) r *= x[i]; + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); + + assert( pFile ); + context = (afpLockingContext *) pFile->lockingContext; + if( context->reserved ){ + *pResOut = 1; + return SQLITE_OK; } - return r; -#else - LONGDOUBLE_TYPE x = 10.0; - LONGDOUBLE_TYPE r = 1.0; - while(1){ - if( E & 1 ) r *= x; - E >>= 1; - if( E==0 ) break; - x *= x; + sqlcipher_sqlite3_mutex_enter(pFile->pInode->pLockMutex); + /* Check if a thread in this process holds such a lock */ + if( pFile->pInode->eFileLock>SHARED_LOCK ){ + reserved = 1; } - return r; -#endif + + /* Otherwise see if some other process holds it. + */ + if( !reserved ){ + /* lock the RESERVED byte */ + int lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1); + if( SQLITE_OK==lrc ){ + /* if we succeeded in taking the reserved lock, unlock it to restore + ** the original state */ + lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0); + } else { + /* if we failed to get the lock then someone else must have it */ + reserved = 1; + } + if( IS_LOCK_ERROR(lrc) ){ + rc=lrc; + } + } + + sqlcipher_sqlite3_mutex_leave(pFile->pInode->pLockMutex); + OSTRACE(("TEST WR-LOCK %d %d %d (afp)\n", pFile->h, rc, reserved)); + + *pResOut = reserved; + return rc; } /* -** The string z[] is an text representation of a real number. -** Convert this string to a double and write it into *pResult. -** -** The string z[] is length bytes in length (bytes, not characters) and -** uses the encoding enc. The string is not necessarily zero-terminated. -** -** Return TRUE if the result is a valid real number (or integer) and FALSE -** if the string is empty or contains extraneous text. More specifically -** return -** 1 => The input string is a pure integer -** 2 or more => The input has a decimal point or eNNN clause -** 0 or less => The input string is not a valid number -** -1 => Not a valid number, but has a valid prefix which -** includes a decimal point and/or an eNNN clause +** Lock the file with the lock specified by parameter eFileLock - one +** of the following: ** -** Valid numbers are in one of these formats: +** (1) SHARED_LOCK +** (2) RESERVED_LOCK +** (3) PENDING_LOCK +** (4) EXCLUSIVE_LOCK ** -** [+-]digits[E[+-]digits] -** [+-]digits.[digits][E[+-]digits] -** [+-].digits[E[+-]digits] +** Sometimes when requesting one lock state, additional lock states +** are inserted in between. The locking might fail on one of the later +** transitions leaving the lock state different from what it started but +** still short of its goal. The following chart shows the allowed +** transitions and the inserted intermediate states: ** -** Leading and trailing whitespace is ignored for the purpose of determining -** validity. +** UNLOCKED -> SHARED +** SHARED -> RESERVED +** SHARED -> (PENDING) -> EXCLUSIVE +** RESERVED -> (PENDING) -> EXCLUSIVE +** PENDING -> EXCLUSIVE ** -** If some prefix of the input string is a valid number, this routine -** returns FALSE but it still converts the prefix and writes the result -** into *pResult. +** This routine will only increase a lock. Use the sqlcipher_sqlite3OsUnlock() +** routine to lower a locking level. */ -#if defined(_MSC_VER) -#pragma warning(disable : 4756) -#endif -SQLITE_PRIVATE int sqlcipher_sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){ -#ifndef SQLITE_OMIT_FLOATING_POINT - int incr; - const char *zEnd; - /* sign * significand * (10 ^ (esign * exponent)) */ - int sign = 1; /* sign of significand */ - i64 s = 0; /* significand */ - int d = 0; /* adjust exponent for shifting decimal point */ - int esign = 1; /* sign of exponent */ - int e = 0; /* exponent */ - int eValid = 1; /* True exponent is either not used or is well-formed */ - double result; - int nDigit = 0; /* Number of digits processed */ - int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ +static int afpLock(sqlcipher_sqlite3_file *id, int eFileLock){ + int rc = SQLITE_OK; + unixFile *pFile = (unixFile*)id; + unixInodeInfo *pInode = pFile->pInode; + afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; - assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); - *pResult = 0.0; /* Default return value, in case of an error */ - if( length==0 ) return 0; + assert( pFile ); + OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (afp)\n", pFile->h, + azFileLock(eFileLock), azFileLock(pFile->eFileLock), + azFileLock(pInode->eFileLock), pInode->nShared , osGetpid(0))); - if( enc==SQLITE_UTF8 ){ - incr = 1; - zEnd = z + length; - }else{ - int i; - incr = 2; - length &= ~1; - assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); - testcase( enc==SQLITE_UTF16LE ); - testcase( enc==SQLITE_UTF16BE ); - for(i=3-enc; ieFileLock>=eFileLock ){ + OSTRACE(("LOCK %d %s ok (already held) (afp)\n", pFile->h, + azFileLock(eFileLock))); + return SQLITE_OK; } - /* skip leading spaces */ - while( z=zEnd ) return 0; + /* Make sure the locking sequence is correct + ** (1) We never move from unlocked to anything higher than shared lock. + ** (2) SQLite never explicitly requests a pendig lock. + ** (3) A shared lock is always held when a reserve lock is requested. + */ + assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); + assert( eFileLock!=PENDING_LOCK ); + assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK ); - /* get sign of significand */ - if( *z=='-' ){ - sign = -1; - z+=incr; - }else if( *z=='+' ){ - z+=incr; + /* This mutex is needed because pFile->pInode is shared across threads + */ + pInode = pFile->pInode; + sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); + + /* If some thread using this PID has a lock via a different unixFile* + ** handle that precludes the requested lock, return BUSY. + */ + if( (pFile->eFileLock!=pInode->eFileLock && + (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) + ){ + rc = SQLITE_BUSY; + goto afp_end_lock; } - /* copy max significant digits to significand */ - while( z=((LARGEST_INT64-9)/10) ){ - /* skip non-significant significand digits - ** (increase exponent by d to shift decimal left) */ - while( zeFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ + assert( eFileLock==SHARED_LOCK ); + assert( pFile->eFileLock==0 ); + assert( pInode->nShared>0 ); + pFile->eFileLock = SHARED_LOCK; + pInode->nShared++; + pInode->nLock++; + goto afp_end_lock; } - if( z>=zEnd ) goto do_atof_calc; - /* if decimal point is present */ - if( *z=='.' ){ - z+=incr; - eType++; - /* copy digits from after decimal to significand - ** (decrease exponent by d to shift decimal right) */ - while( zeFileLockdbPath, pFile, PENDING_BYTE, 1, 1); + if (failed) { + rc = failed; + goto afp_end_lock; } } - if( z>=zEnd ) goto do_atof_calc; - /* if exponent is present */ - if( *z=='e' || *z=='E' ){ - z+=incr; - eValid = 0; - eType++; + /* If control gets to this point, then actually go ahead and make + ** operating system calls for the specified lock. + */ + if( eFileLock==SHARED_LOCK ){ + int lrc1, lrc2, lrc1Errno = 0; + long lk, mask; - /* This branch is needed to avoid a (harmless) buffer overread. The - ** special comment alerts the mutation tester that the correct answer - ** is obtained even if the branch is omitted */ - if( z>=zEnd ) goto do_atof_calc; /*PREVENTS-HARMLESS-OVERREAD*/ + assert( pInode->nShared==0 ); + assert( pInode->eFileLock==0 ); - /* get sign of exponent */ - if( *z=='-' ){ - esign = -1; - z+=incr; - }else if( *z=='+' ){ - z+=incr; - } - /* copy digits to exponent */ - while( zsharedByte = (lk & mask)%(SHARED_SIZE - 1); + lrc1 = afpSetLock(context->dbPath, pFile, + SHARED_FIRST+pInode->sharedByte, 1, 1); + if( IS_LOCK_ERROR(lrc1) ){ + lrc1Errno = pFile->lastErrno; } - } - - /* skip trailing spaces */ - while( zdbPath, pFile, PENDING_BYTE, 1, 0); - if( s==0 ) { - /* In the IEEE 754 standard, zero is signed. */ - result = sign<0 ? -(double)0 : (double)0; - } else { - /* Attempt to reduce exponent. - ** - ** Branches that are not required for the correct answer but which only - ** help to obtain the correct answer faster are marked with special - ** comments, as a hint to the mutation tester. + if( IS_LOCK_ERROR(lrc1) ) { + storeLastErrno(pFile, lrc1Errno); + rc = lrc1; + goto afp_end_lock; + } else if( IS_LOCK_ERROR(lrc2) ){ + rc = lrc2; + goto afp_end_lock; + } else if( lrc1 != SQLITE_OK ) { + rc = lrc1; + } else { + pFile->eFileLock = SHARED_LOCK; + pInode->nLock++; + pInode->nShared = 1; + } + }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ + /* We are trying for an exclusive lock but another thread in this + ** same process is still holding a shared lock. */ + rc = SQLITE_BUSY; + }else{ + /* The request was for a RESERVED or EXCLUSIVE lock. It is + ** assumed that there is a SHARED or greater lock on the file + ** already. */ - while( e>0 ){ /*OPTIMIZATION-IF-TRUE*/ - if( esign>0 ){ - if( s>=(LARGEST_INT64/10) ) break; /*OPTIMIZATION-IF-FALSE*/ - s *= 10; + int failed = 0; + assert( 0!=pFile->eFileLock ); + if (eFileLock >= RESERVED_LOCK && pFile->eFileLock < RESERVED_LOCK) { + /* Acquire a RESERVED lock */ + failed = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1); + if( !failed ){ + context->reserved = 1; + } + } + if (!failed && eFileLock == EXCLUSIVE_LOCK) { + /* Acquire an EXCLUSIVE lock */ + + /* Remove the shared lock before trying the range. we'll need to + ** reestablish the shared lock if we can't get the afpUnlock + */ + if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST + + pInode->sharedByte, 1, 0)) ){ + int failed2 = SQLITE_OK; + /* now attemmpt to get the exclusive lock range */ + failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST, + SHARED_SIZE, 1); + if( failed && (failed2 = afpSetLock(context->dbPath, pFile, + SHARED_FIRST + pInode->sharedByte, 1, 1)) ){ + /* Can't reestablish the shared lock. Sqlite can't deal, this is + ** a critical I/O error + */ + rc = ((failed & 0xff) == SQLITE_IOERR) ? failed2 : + SQLITE_IOERR_LOCK; + goto afp_end_lock; + } }else{ - if( s%10!=0 ) break; /*OPTIMIZATION-IF-FALSE*/ - s /= 10; + rc = failed; } - e--; } + if( failed ){ + rc = failed; + } + } - /* adjust the sign of significand */ - s = sign<0 ? -s : s; + if( rc==SQLITE_OK ){ + pFile->eFileLock = eFileLock; + pInode->eFileLock = eFileLock; + }else if( eFileLock==EXCLUSIVE_LOCK ){ + pFile->eFileLock = PENDING_LOCK; + pInode->eFileLock = PENDING_LOCK; + } - if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/ - result = (double)s; - }else{ - /* attempt to handle extremely small/large numbers better */ - if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/ - if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/ - LONGDOUBLE_TYPE scale = sqlcipher_sqlite3Pow10(e-308); - if( esign<0 ){ - result = s / scale; - result /= 1.0e+308; - }else{ - result = s * scale; - result *= 1.0e+308; - } - }else{ assert( e>=342 ); - if( esign<0 ){ - result = 0.0*s; - }else{ -#ifdef INFINITY - result = INFINITY*s; -#else - result = 1e308*1e308*s; /* Infinity */ +afp_end_lock: + sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); + OSTRACE(("LOCK %d %s %s (afp)\n", pFile->h, azFileLock(eFileLock), + rc==SQLITE_OK ? "ok" : "failed")); + return rc; +} + +/* +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock +** must be either NO_LOCK or SHARED_LOCK. +** +** If the locking level of the file descriptor is already at or below +** the requested locking level, this routine is a no-op. +*/ +static int afpUnlock(sqlcipher_sqlite3_file *id, int eFileLock) { + int rc = SQLITE_OK; + unixFile *pFile = (unixFile*)id; + unixInodeInfo *pInode; + afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; + int skipShared = 0; +#ifdef SQLITE_TEST + int h = pFile->h; #endif - } - } - }else{ - LONGDOUBLE_TYPE scale = sqlcipher_sqlite3Pow10(e); - if( esign<0 ){ - result = s / scale; - }else{ - result = s * scale; - } + + assert( pFile ); + OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock, + pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, + osGetpid(0))); + + assert( eFileLock<=SHARED_LOCK ); + if( pFile->eFileLock<=eFileLock ){ + return SQLITE_OK; + } + pInode = pFile->pInode; + sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); + assert( pInode->nShared!=0 ); + if( pFile->eFileLock>SHARED_LOCK ){ + assert( pInode->eFileLock==pFile->eFileLock ); + SimulateIOErrorBenign(1); + SimulateIOError( h=(-1) ) + SimulateIOErrorBenign(0); + +#ifdef SQLITE_DEBUG + /* When reducing a lock such that other processes can start + ** reading the database file again, make sure that the + ** transaction counter was updated if any part of the database + ** file changed. If the transaction counter is not updated, + ** other connections to the same file might not realize that + ** the file has changed and hence might not know to flush their + ** cache. The use of a stale cache can lead to database corruption. + */ + assert( pFile->inNormalWrite==0 + || pFile->dbUpdate==0 + || pFile->transCntrChng==1 ); + pFile->inNormalWrite = 0; +#endif + + if( pFile->eFileLock==EXCLUSIVE_LOCK ){ + rc = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0); + if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1) ){ + /* only re-establish the shared lock if necessary */ + int sharedLockByte = SHARED_FIRST+pInode->sharedByte; + rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 1); + } else { + skipShared = 1; + } + } + if( rc==SQLITE_OK && pFile->eFileLock>=PENDING_LOCK ){ + rc = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); + } + if( rc==SQLITE_OK && pFile->eFileLock>=RESERVED_LOCK && context->reserved ){ + rc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0); + if( !rc ){ + context->reserved = 0; } } + if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1)){ + pInode->eFileLock = SHARED_LOCK; + } } + if( rc==SQLITE_OK && eFileLock==NO_LOCK ){ - /* store the result */ - *pResult = result; + /* Decrement the shared lock counter. Release the lock using an + ** OS call only when all threads in this same process have released + ** the lock. + */ + unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte; + pInode->nShared--; + if( pInode->nShared==0 ){ + SimulateIOErrorBenign(1); + SimulateIOError( h=(-1) ) + SimulateIOErrorBenign(0); + if( !skipShared ){ + rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0); + } + if( !rc ){ + pInode->eFileLock = NO_LOCK; + pFile->eFileLock = NO_LOCK; + } + } + if( rc==SQLITE_OK ){ + pInode->nLock--; + assert( pInode->nLock>=0 ); + if( pInode->nLock==0 ) closePendingFds(pFile); + } + } - /* return true if number and no extra non-whitespace chracters after */ - if( z==zEnd && nDigit>0 && eValid && eType>0 ){ - return eType; - }else if( eType>=2 && (eType==3 || eValid) && nDigit>0 ){ - return -1; - }else{ - return 0; + sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); + if( rc==SQLITE_OK ){ + pFile->eFileLock = eFileLock; } -#else - return !sqlcipher_sqlite3Atoi64(z, pResult, length, enc); -#endif /* SQLITE_OMIT_FLOATING_POINT */ + return rc; } -#if defined(_MSC_VER) -#pragma warning(default : 4756) -#endif /* -** Render an signed 64-bit integer as text. Store the result in zOut[]. -** -** The caller must ensure that zOut[] is at least 21 bytes in size. +** Close a file & cleanup AFP specific locking context */ -SQLITE_PRIVATE void sqlcipher_sqlite3Int64ToText(i64 v, char *zOut){ - int i; - u64 x; - char zTemp[22]; - if( v<0 ){ - x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v; - }else{ - x = v; +static int afpClose(sqlcipher_sqlite3_file *id) { + int rc = SQLITE_OK; + unixFile *pFile = (unixFile*)id; + assert( id!=0 ); + afpUnlock(id, NO_LOCK); + assert( unixFileMutexNotheld(pFile) ); + unixEnterMutex(); + if( pFile->pInode ){ + unixInodeInfo *pInode = pFile->pInode; + sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); + if( pInode->nLock ){ + /* If there are outstanding locks, do not actually close the file just + ** yet because that would clear those locks. Instead, add the file + ** descriptor to pInode->aPending. It will be automatically closed when + ** the last lock is cleared. + */ + setPendingFd(pFile); + } + sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); } - i = sizeof(zTemp)-2; - zTemp[sizeof(zTemp)-1] = 0; - do{ - zTemp[i--] = (x%10) + '0'; - x = x/10; - }while( x ); - if( v<0 ) zTemp[i--] = '-'; - memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); + releaseInodeInfo(pFile); + sqlcipher_sqlite3_free(pFile->lockingContext); + rc = closeUnixFile(id); + unixLeaveMutex(); + return rc; } +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ /* -** Compare the 19-character string zNum against the text representation -** value 2^63: 9223372036854775808. Return negative, zero, or positive -** if zNum is less than, equal to, or greater than the string. -** Note that zNum must contain exactly 19 characters. -** -** Unlike memcmp() this routine is guaranteed to return the difference -** in the values of the last digit if the only difference is in the -** last digit. So, for example, -** -** compare2pow63("9223372036854775800", 1) +** The code above is the AFP lock implementation. The code is specific +** to MacOSX and does not work on other unix platforms. No alternative +** is available. If you don't compile for a mac, then the "unix-afp" +** VFS is not available. ** -** will return -8. -*/ -static int compare2pow63(const char *zNum, int incr){ - int c = 0; - int i; - /* 012345678901234567 */ - const char *pow63 = "922337203685477580"; - for(i=0; c==0 && i<18; i++){ - c = (zNum[i*incr]-pow63[i])*10; - } - if( c==0 ){ - c = zNum[18*incr] - '8'; - testcase( c==(-1) ); - testcase( c==0 ); - testcase( c==(+1) ); - } - return c; +********************* End of the AFP lock implementation ********************** +******************************************************************************/ + +/****************************************************************************** +*************************** Begin NFS Locking ********************************/ + +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE +/* + ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock + ** must be either NO_LOCK or SHARED_LOCK. + ** + ** If the locking level of the file descriptor is already at or below + ** the requested locking level, this routine is a no-op. + */ +static int nfsUnlock(sqlcipher_sqlite3_file *id, int eFileLock){ + return posixUnlock(id, eFileLock, 1); } +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ /* -** Convert zNum to a 64-bit signed integer. zNum must be decimal. This -** routine does *not* accept hexadecimal notation. -** -** Returns: +** The code above is the NFS lock implementation. The code is specific +** to MacOSX and does not work on other unix platforms. No alternative +** is available. ** -** -1 Not even a prefix of the input text looks like an integer -** 0 Successful transformation. Fits in a 64-bit signed integer. -** 1 Excess non-space text after the integer value -** 2 Integer too large for a 64-bit signed integer or is malformed -** 3 Special case of 9223372036854775808 +********************* End of the NFS lock implementation ********************** +******************************************************************************/ + +/****************************************************************************** +**************** Non-locking sqlcipher_sqlite3_file methods ***************************** ** -** length is the number of bytes in the string (bytes, not characters). -** The string is not necessarily zero-terminated. The encoding is -** given by enc. +** The next division contains implementations for all methods of the +** sqlcipher_sqlite3_file object other than the locking methods. The locking +** methods were defined in divisions above (one locking method per +** division). Those methods that are common to all locking modes +** are gather together into this division. */ -SQLITE_PRIVATE int sqlcipher_sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ - int incr; - u64 u = 0; - int neg = 0; /* assume positive */ - int i; - int c = 0; - int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */ - int rc; /* Baseline return code */ - const char *zStart; - const char *zEnd = zNum + length; - assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); - if( enc==SQLITE_UTF8 ){ - incr = 1; - }else{ - incr = 2; - length &= ~1; - assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); - for(i=3-enc; i='0' && c<='9'; i+=incr){ - u = u*10 + c - '0'; - } - testcase( i==18*incr ); - testcase( i==19*incr ); - testcase( i==20*incr ); - if( u>LARGEST_INT64 ){ - /* This test and assignment is needed only to suppress UB warnings - ** from clang and -fsanitize=undefined. This test and assignment make - ** the code a little larger and slower, and no harm comes from omitting - ** them, but we must appaise the undefined-behavior pharisees. */ - *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; - }else if( neg ){ - *pNum = -(i64)u; - }else{ - *pNum = (i64)u; - } - rc = 0; - if( i==0 && zStart==zNum ){ /* No digits */ - rc = -1; - }else if( nonNum ){ /* UTF16 with high-order bytes non-zero */ - rc = 1; - }else if( &zNum[i]19*incr ? 1 : compare2pow63(zNum, incr); - if( c<0 ){ - /* zNum is less than 9223372036854775808 so it fits */ - assert( u<=LARGEST_INT64 ); - return rc; - }else{ - *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; - if( c>0 ){ - /* zNum is greater than 9223372036854775808 so it overflows */ - return 2; - }else{ - /* zNum is exactly 9223372036854775808. Fits if negative. The - ** special case 2 overflow if positive */ - assert( u-1==LARGEST_INT64 ); - return neg ? rc : 3; - } - } - } -} /* -** Transform a UTF-8 integer literal, in either decimal or hexadecimal, -** into a 64-bit signed integer. This routine accepts hexadecimal literals, -** whereas sqlcipher_sqlite3Atoi64() does not. +** Seek to the offset passed as the second argument, then read cnt +** bytes into pBuf. Return the number of bytes actually read. ** -** Returns: +** NB: If you define USE_PREAD or USE_PREAD64, then it might also +** be necessary to define _XOPEN_SOURCE to be 500. This varies from +** one system to another. Since SQLite does not define USE_PREAD +** in any form by default, we will not attempt to define _XOPEN_SOURCE. +** See tickets #2741 and #2681. ** -** 0 Successful transformation. Fits in a 64-bit signed integer. -** 1 Excess text after the integer value -** 2 Integer too large for a 64-bit signed integer or is malformed -** 3 Special case of 9223372036854775808 +** To avoid stomping the errno value on a failed read the lastErrno value +** is set before returning. */ -SQLITE_PRIVATE int sqlcipher_sqlite3DecOrHexToI64(const char *z, i64 *pOut){ -#ifndef SQLITE_OMIT_HEX_INTEGER - if( z[0]=='0' - && (z[1]=='x' || z[1]=='X') - ){ - u64 u = 0; - int i, k; - for(i=2; z[i]=='0'; i++){} - for(k=i; sqlcipher_sqlite3Isxdigit(z[k]); k++){ - u = u*16 + sqlcipher_sqlite3HexToInt(z[k]); +static int seekAndRead(unixFile *id, sqlcipher_sqlite3_int64 offset, void *pBuf, int cnt){ + int got; + int prior = 0; +#if (!defined(USE_PREAD) && !defined(USE_PREAD64)) + i64 newOffset; +#endif + TIMER_START; + assert( cnt==(cnt&0x1ffff) ); + assert( id->h>2 ); + do{ +#if defined(USE_PREAD) + got = osPread(id->h, pBuf, cnt, offset); + SimulateIOError( got = -1 ); +#elif defined(USE_PREAD64) + got = osPread64(id->h, pBuf, cnt, offset); + SimulateIOError( got = -1 ); +#else + newOffset = lseek(id->h, offset, SEEK_SET); + SimulateIOError( newOffset = -1 ); + if( newOffset<0 ){ + storeLastErrno((unixFile*)id, errno); + return -1; } - memcpy(pOut, &u, 8); - return (z[k]==0 && k-i<=16) ? 0 : 2; - }else -#endif /* SQLITE_OMIT_HEX_INTEGER */ - { - return sqlcipher_sqlite3Atoi64(z, pOut, sqlcipher_sqlite3Strlen30(z), SQLITE_UTF8); - } + got = osRead(id->h, pBuf, cnt); +#endif + if( got==cnt ) break; + if( got<0 ){ + if( errno==EINTR ){ got = 1; continue; } + prior = 0; + storeLastErrno((unixFile*)id, errno); + break; + }else if( got>0 ){ + cnt -= got; + offset += got; + prior += got; + pBuf = (void*)(got + (char*)pBuf); + } + }while( got>0 ); + TIMER_END; + OSTRACE(("READ %-3d %5d %7lld %llu\n", + id->h, got+prior, offset-prior, TIMER_ELAPSED)); + return got+prior; } /* -** If zNum represents an integer that will fit in 32-bits, then set -** *pValue to that integer and return true. Otherwise return false. -** -** This routine accepts both decimal and hexadecimal notation for integers. -** -** Any non-numeric characters that following zNum are ignored. -** This is different from sqlcipher_sqlite3Atoi64() which requires the -** input number to be zero-terminated. +** Read data from a file into a buffer. Return SQLITE_OK if all +** bytes were read successfully and SQLITE_IOERR if anything goes +** wrong. */ -SQLITE_PRIVATE int sqlcipher_sqlite3GetInt32(const char *zNum, int *pValue){ - sqlite_int64 v = 0; - int i, c; - int neg = 0; - if( zNum[0]=='-' ){ - neg = 1; - zNum++; - }else if( zNum[0]=='+' ){ - zNum++; - } -#ifndef SQLITE_OMIT_HEX_INTEGER - else if( zNum[0]=='0' - && (zNum[1]=='x' || zNum[1]=='X') - && sqlcipher_sqlite3Isxdigit(zNum[2]) - ){ - u32 u = 0; - zNum += 2; - while( zNum[0]=='0' ) zNum++; - for(i=0; sqlcipher_sqlite3Isxdigit(zNum[i]) && i<8; i++){ - u = u*16 + sqlcipher_sqlite3HexToInt(zNum[i]); - } - if( (u&0x80000000)==0 && sqlcipher_sqlite3Isxdigit(zNum[i])==0 ){ - memcpy(pValue, &u, 4); - return 1; +static int unixRead( + sqlcipher_sqlite3_file *id, + void *pBuf, + int amt, + sqlcipher_sqlite3_int64 offset +){ + unixFile *pFile = (unixFile *)id; + int got; + assert( id ); + assert( offset>=0 ); + assert( amt>0 ); + + /* If this is a database file (not a journal, super-journal or temp + ** file), the bytes in the locking range should never be read or written. */ +#if 0 + assert( pFile->pPreallocatedUnused==0 + || offset>=PENDING_BYTE+512 + || offset+amt<=PENDING_BYTE + ); +#endif + +#if SQLITE_MAX_MMAP_SIZE>0 + /* Deal with as much of this read request as possible by transfering + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); + return SQLITE_OK; }else{ - return 0; + int nCopy = pFile->mmapSize - offset; + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); + pBuf = &((u8 *)pBuf)[nCopy]; + amt -= nCopy; + offset += nCopy; } } #endif - if( !sqlcipher_sqlite3Isdigit(zNum[0]) ) return 0; - while( zNum[0]=='0' ) zNum++; - for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){ - v = v*10 + c; - } - /* The longest decimal representation of a 32 bit integer is 10 digits: - ** - ** 1234567890 - ** 2^31 -> 2147483648 - */ - testcase( i==10 ); - if( i>10 ){ - return 0; - } - testcase( v-neg==2147483647 ); - if( v-neg>2147483647 ){ - return 0; - } - if( neg ){ - v = -v; + got = seekAndRead(pFile, offset, pBuf, amt); + if( got==amt ){ + return SQLITE_OK; + }else if( got<0 ){ + /* pFile->lastErrno has been set by seekAndRead(). + ** Usually we return SQLITE_IOERR_READ here, though for some + ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The + ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT + ** prior to returning to the application by the sqlcipher_sqlite3ApiExit() + ** routine. + */ + switch( pFile->lastErrno ){ + case ERANGE: + case EIO: +#ifdef ENXIO + case ENXIO: +#endif +#ifdef EDEVERR + case EDEVERR: +#endif + return SQLITE_IOERR_CORRUPTFS; + } + return SQLITE_IOERR_READ; + }else{ + storeLastErrno(pFile, 0); /* not a system error */ + /* Unread parts of the buffer must be zero-filled */ + memset(&((char*)pBuf)[got], 0, amt-got); + return SQLITE_IOERR_SHORT_READ; } - *pValue = (int)v; - return 1; } /* -** Return a 32-bit integer value extracted from a string. If the -** string is not an integer, just return 0. +** Attempt to seek the file-descriptor passed as the first argument to +** absolute offset iOff, then attempt to write nBuf bytes of data from +** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise, +** return the actual number of bytes written (which may be less than +** nBuf). */ -SQLITE_PRIVATE int sqlcipher_sqlite3Atoi(const char *z){ - int x = 0; - sqlcipher_sqlite3GetInt32(z, &x); - return x; +static int seekAndWriteFd( + int fd, /* File descriptor to write to */ + i64 iOff, /* File offset to begin writing at */ + const void *pBuf, /* Copy data from this buffer to the file */ + int nBuf, /* Size of buffer pBuf in bytes */ + int *piErrno /* OUT: Error number if error occurs */ +){ + int rc = 0; /* Value returned by system call */ + + assert( nBuf==(nBuf&0x1ffff) ); + assert( fd>2 ); + assert( piErrno!=0 ); + nBuf &= 0x1ffff; + TIMER_START; + +#if defined(USE_PREAD) + do{ rc = (int)osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR ); +#elif defined(USE_PREAD64) + do{ rc = (int)osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR); +#else + do{ + i64 iSeek = lseek(fd, iOff, SEEK_SET); + SimulateIOError( iSeek = -1 ); + if( iSeek<0 ){ + rc = -1; + break; + } + rc = osWrite(fd, pBuf, nBuf); + }while( rc<0 && errno==EINTR ); +#endif + + TIMER_END; + OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED)); + + if( rc<0 ) *piErrno = errno; + return rc; } + /* -** Try to convert z into an unsigned 32-bit integer. Return true on -** success and false if there is an error. +** Seek to the offset in id->offset then read cnt bytes into pBuf. +** Return the number of bytes actually read. Update the offset. ** -** Only decimal notation is accepted. +** To avoid stomping the errno value on a failed write the lastErrno value +** is set before returning. */ -SQLITE_PRIVATE int sqlcipher_sqlite3GetUInt32(const char *z, u32 *pI){ - u64 v = 0; - int i; - for(i=0; sqlcipher_sqlite3Isdigit(z[i]); i++){ - v = v*10 + z[i] - '0'; - if( v>4294967296LL ){ *pI = 0; return 0; } - } - if( i==0 || z[i]!=0 ){ *pI = 0; return 0; } - *pI = (u32)v; - return 1; +static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ + return seekAndWriteFd(id->h, offset, pBuf, cnt, &id->lastErrno); } -/* -** The variable-length integer encoding is as follows: -** -** KEY: -** A = 0xxxxxxx 7 bits of data and one flag bit -** B = 1xxxxxxx 7 bits of data and one flag bit -** C = xxxxxxxx 8 bits of data -** -** 7 bits - A -** 14 bits - BA -** 21 bits - BBA -** 28 bits - BBBA -** 35 bits - BBBBA -** 42 bits - BBBBBA -** 49 bits - BBBBBBA -** 56 bits - BBBBBBBA -** 64 bits - BBBBBBBBC -*/ /* -** Write a 64-bit variable-length integer to memory starting at p[0]. -** The length of data write will be between 1 and 9 bytes. The number -** of bytes written is returned. -** -** A variable-length integer consists of the lower 7 bits of each byte -** for all bytes that have the 8th bit set and one byte with the 8th -** bit clear. Except, if we get to the 9th byte, it stores the full -** 8 bits and is the last byte. +** Write data from a buffer into a file. Return SQLITE_OK on success +** or some other error code on failure. */ -static int SQLITE_NOINLINE putVarint64(unsigned char *p, u64 v){ - int i, j, n; - u8 buf[10]; - if( v & (((u64)0xff000000)<<32) ){ - p[8] = (u8)v; - v >>= 8; - for(i=7; i>=0; i--){ - p[i] = (u8)((v & 0x7f) | 0x80); - v >>= 7; +static int unixWrite( + sqlcipher_sqlite3_file *id, + const void *pBuf, + int amt, + sqlcipher_sqlite3_int64 offset +){ + unixFile *pFile = (unixFile*)id; + int wrote = 0; + assert( id ); + assert( amt>0 ); + + /* If this is a database file (not a journal, super-journal or temp + ** file), the bytes in the locking range should never be read or written. */ +#if 0 + assert( pFile->pPreallocatedUnused==0 + || offset>=PENDING_BYTE+512 + || offset+amt<=PENDING_BYTE + ); +#endif + +#ifdef SQLITE_DEBUG + /* If we are doing a normal write to a database file (as opposed to + ** doing a hot-journal rollback or a write to some file other than a + ** normal database file) then record the fact that the database + ** has changed. If the transaction counter is modified, record that + ** fact too. + */ + if( pFile->inNormalWrite ){ + pFile->dbUpdate = 1; /* The database has been modified */ + if( offset<=24 && offset+amt>=27 ){ + int rc; + char oldCntr[4]; + SimulateIOErrorBenign(1); + rc = seekAndRead(pFile, 24, oldCntr, 4); + SimulateIOErrorBenign(0); + if( rc!=4 || memcmp(oldCntr, &((char*)pBuf)[24-offset], 4)!=0 ){ + pFile->transCntrChng = 1; /* The transaction counter has changed */ + } } - return 9; } - n = 0; - do{ - buf[n++] = (u8)((v & 0x7f) | 0x80); - v >>= 7; - }while( v!=0 ); - buf[0] &= 0x7f; - assert( n<=9 ); - for(i=0, j=n-1; j>=0; j--, i++){ - p[i] = buf[j]; +#endif + +#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 + /* Deal with as much of this write request as possible by transfering + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); + return SQLITE_OK; + }else{ + int nCopy = pFile->mmapSize - offset; + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy); + pBuf = &((u8 *)pBuf)[nCopy]; + amt -= nCopy; + offset += nCopy; + } } - return n; -} -SQLITE_PRIVATE int sqlcipher_sqlite3PutVarint(unsigned char *p, u64 v){ - if( v<=0x7f ){ - p[0] = v&0x7f; - return 1; +#endif + + while( (wrote = seekAndWrite(pFile, offset, pBuf, amt))0 ){ + amt -= wrote; + offset += wrote; + pBuf = &((char*)pBuf)[wrote]; } - if( v<=0x3fff ){ - p[0] = ((v>>7)&0x7f)|0x80; - p[1] = v&0x7f; - return 2; + SimulateIOError(( wrote=(-1), amt=1 )); + SimulateDiskfullError(( wrote=0, amt=1 )); + + if( amt>wrote ){ + if( wrote<0 && pFile->lastErrno!=ENOSPC ){ + /* lastErrno set by seekAndWrite */ + return SQLITE_IOERR_WRITE; + }else{ + storeLastErrno(pFile, 0); /* not a system error */ + return SQLITE_FULL; + } } - return putVarint64(p,v); + + return SQLITE_OK; } +#ifdef SQLITE_TEST /* -** Bitmasks used by sqlcipher_sqlite3GetVarint(). These precomputed constants -** are defined here rather than simply putting the constant expressions -** inline in order to work around bugs in the RVT compiler. -** -** SLOT_2_0 A mask for (0x7f<<14) | 0x7f -** -** SLOT_4_2_0 A mask for (0x7f<<28) | SLOT_2_0 +** Count the number of fullsyncs and normal syncs. This is used to test +** that syncs and fullsyncs are occurring at the right times. */ -#define SLOT_2_0 0x001fc07f -#define SLOT_4_2_0 0xf01fc07f - +SQLITE_API int sqlcipher_sqlite3_sync_count = 0; +SQLITE_API int sqlcipher_sqlite3_fullsync_count = 0; +#endif /* -** Read a 64-bit variable-length integer from memory starting at p[0]. -** Return the number of bytes read. The value is stored in *v. +** We do not trust systems to provide a working fdatasync(). Some do. +** Others do no. To be safe, we will stick with the (slightly slower) +** fsync(). If you know that your system does support fdatasync() correctly, +** then simply compile with -Dfdatasync=fdatasync or -DHAVE_FDATASYNC */ -SQLITE_PRIVATE u8 sqlcipher_sqlite3GetVarint(const unsigned char *p, u64 *v){ - u32 a,b,s; +#if !defined(fdatasync) && !HAVE_FDATASYNC +# define fdatasync fsync +#endif - if( ((signed char*)p)[0]>=0 ){ - *v = *p; - return 1; - } - if( ((signed char*)p)[1]>=0 ){ - *v = ((u32)(p[0]&0x7f)<<7) | p[1]; - return 2; - } +/* +** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not +** the F_FULLFSYNC macro is defined. F_FULLFSYNC is currently +** only available on Mac OS X. But that could change. +*/ +#ifdef F_FULLFSYNC +# define HAVE_FULLFSYNC 1 +#else +# define HAVE_FULLFSYNC 0 +#endif - /* Verify that constants are precomputed correctly */ - assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) ); - assert( SLOT_4_2_0 == ((0xfU<<28) | (0x7f<<14) | (0x7f)) ); - a = ((u32)p[0])<<14; - b = p[1]; - p += 2; - a |= *p; - /* a: p0<<14 | p2 (unmasked) */ - if (!(a&0x80)) - { - a &= SLOT_2_0; - b &= 0x7f; - b = b<<7; - a |= b; - *v = a; - return 3; - } +/* +** The fsync() system call does not work as advertised on many +** unix systems. The following procedure is an attempt to make +** it work better. +** +** The SQLITE_NO_SYNC macro disables all fsync()s. This is useful +** for testing when we want to run through the test suite quickly. +** You are strongly advised *not* to deploy with SQLITE_NO_SYNC +** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash +** or power failure will likely corrupt the database file. +** +** SQLite sets the dataOnly flag if the size of the file is unchanged. +** The idea behind dataOnly is that it should only write the file content +** to disk, not the inode. We only set dataOnly if the file size is +** unchanged since the file size is part of the inode. However, +** Ted Ts'o tells us that fdatasync() will also write the inode if the +** file size has changed. The only real difference between fdatasync() +** and fsync(), Ted tells us, is that fdatasync() will not flush the +** inode if the mtime or owner or other inode attributes have changed. +** We only care about the file size, not the other file attributes, so +** as far as SQLite is concerned, an fdatasync() is always adequate. +** So, we always use fdatasync() if it is available, regardless of +** the value of the dataOnly flag. +*/ +static int full_fsync(int fd, int fullSync, int dataOnly){ + int rc; - /* CSE1 from below */ - a &= SLOT_2_0; - p++; - b = b<<14; - b |= *p; - /* b: p1<<14 | p3 (unmasked) */ - if (!(b&0x80)) - { - b &= SLOT_2_0; - /* moved CSE1 up */ - /* a &= (0x7f<<14)|(0x7f); */ - a = a<<7; - a |= b; - *v = a; - return 4; - } + /* The following "ifdef/elif/else/" block has the same structure as + ** the one below. It is replicated here solely to avoid cluttering + ** up the real code with the UNUSED_PARAMETER() macros. + */ +#ifdef SQLITE_NO_SYNC + UNUSED_PARAMETER(fd); + UNUSED_PARAMETER(fullSync); + UNUSED_PARAMETER(dataOnly); +#elif HAVE_FULLFSYNC + UNUSED_PARAMETER(dataOnly); +#else + UNUSED_PARAMETER(fullSync); + UNUSED_PARAMETER(dataOnly); +#endif - /* a: p0<<14 | p2 (masked) */ - /* b: p1<<14 | p3 (unmasked) */ - /* 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ - /* moved CSE1 up */ - /* a &= (0x7f<<14)|(0x7f); */ - b &= SLOT_2_0; - s = a; - /* s: p0<<14 | p2 (masked) */ + /* Record the number of times that we do a normal fsync() and + ** FULLSYNC. This is used during testing to verify that this procedure + ** gets called with the correct arguments. + */ +#ifdef SQLITE_TEST + if( fullSync ) sqlcipher_sqlite3_fullsync_count++; + sqlcipher_sqlite3_sync_count++; +#endif - p++; - a = a<<14; - a |= *p; - /* a: p0<<28 | p2<<14 | p4 (unmasked) */ - if (!(a&0x80)) + /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a + ** no-op. But go ahead and call fstat() to validate the file + ** descriptor as we need a method to provoke a failure during + ** coverate testing. + */ +#ifdef SQLITE_NO_SYNC { - /* we can skip these cause they were (effectively) done above - ** while calculating s */ - /* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ - /* b &= (0x7f<<14)|(0x7f); */ - b = b<<7; - a |= b; - s = s>>18; - *v = ((u64)s)<<32 | a; - return 5; + struct stat buf; + rc = osFstat(fd, &buf); } - - /* 2:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ - s = s<<7; - s |= b; - /* s: p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ - - p++; - b = b<<14; - b |= *p; - /* b: p1<<28 | p3<<14 | p5 (unmasked) */ - if (!(b&0x80)) - { - /* we can skip this cause it was (effectively) done above in calc'ing s */ - /* b &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ - a &= SLOT_2_0; - a = a<<7; - a |= b; - s = s>>18; - *v = ((u64)s)<<32 | a; - return 6; +#elif HAVE_FULLFSYNC + if( fullSync ){ + rc = osFcntl(fd, F_FULLFSYNC, 0); + }else{ + rc = 1; } + /* If the FULLFSYNC failed, fall back to attempting an fsync(). + ** It shouldn't be possible for fullfsync to fail on the local + ** file system (on OSX), so failure indicates that FULLFSYNC + ** isn't supported for this file system. So, attempt an fsync + ** and (for now) ignore the overhead of a superfluous fcntl call. + ** It'd be better to detect fullfsync support once and avoid + ** the fcntl call every time sync is called. + */ + if( rc ) rc = fsync(fd); - p++; - a = a<<14; - a |= *p; - /* a: p2<<28 | p4<<14 | p6 (unmasked) */ - if (!(a&0x80)) - { - a &= SLOT_4_2_0; - b &= SLOT_2_0; - b = b<<7; - a |= b; - s = s>>11; - *v = ((u64)s)<<32 | a; - return 7; +#elif defined(__APPLE__) + /* fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly + ** so currently we default to the macro that redefines fdatasync to fsync + */ + rc = fsync(fd); +#else + rc = fdatasync(fd); +#if OS_VXWORKS + if( rc==-1 && errno==ENOTSUP ){ + rc = fsync(fd); } +#endif /* OS_VXWORKS */ +#endif /* ifdef SQLITE_NO_SYNC elif HAVE_FULLFSYNC */ - /* CSE2 from below */ - a &= SLOT_2_0; - p++; - b = b<<14; - b |= *p; - /* b: p3<<28 | p5<<14 | p7 (unmasked) */ - if (!(b&0x80)) - { - b &= SLOT_4_2_0; - /* moved CSE2 up */ - /* a &= (0x7f<<14)|(0x7f); */ - a = a<<7; - a |= b; - s = s>>4; - *v = ((u64)s)<<32 | a; - return 8; + if( OS_VXWORKS && rc!= -1 ){ + rc = 0; } + return rc; +} - p++; - a = a<<15; - a |= *p; - /* a: p4<<29 | p6<<15 | p8 (unmasked) */ - - /* moved CSE2 up */ - /* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */ - b &= SLOT_2_0; - b = b<<8; - a |= b; - - s = s<<4; - b = p[-4]; - b &= 0x7f; - b = b>>3; - s |= b; - - *v = ((u64)s)<<32 | a; +/* +** Open a file descriptor to the directory containing file zFilename. +** If successful, *pFd is set to the opened file descriptor and +** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM +** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined +** value. +** +** The directory file descriptor is used for only one thing - to +** fsync() a directory to make sure file creation and deletion events +** are flushed to disk. Such fsyncs are not needed on newer +** journaling filesystems, but are required on older filesystems. +** +** This routine can be overridden using the xSetSysCall interface. +** The ability to override this routine was added in support of the +** chromium sandbox. Opening a directory is a security risk (we are +** told) so making it overrideable allows the chromium sandbox to +** replace this routine with a harmless no-op. To make this routine +** a no-op, replace it with a stub that returns SQLITE_OK but leaves +** *pFd set to a negative number. +** +** If SQLITE_OK is returned, the caller is responsible for closing +** the file descriptor *pFd using close(). +*/ +static int openDirectory(const char *zFilename, int *pFd){ + int ii; + int fd = -1; + char zDirname[MAX_PATHNAME+1]; - return 9; + sqlcipher_sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename); + for(ii=(int)strlen(zDirname); ii>0 && zDirname[ii]!='/'; ii--); + if( ii>0 ){ + zDirname[ii] = '\0'; + }else{ + if( zDirname[0]!='/' ) zDirname[0] = '.'; + zDirname[1] = 0; + } + fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); + if( fd>=0 ){ + OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); + } + *pFd = fd; + if( fd>=0 ) return SQLITE_OK; + return unixLogError(SQLITE_CANTOPEN_BKPT, "openDirectory", zDirname); } /* -** Read a 32-bit variable-length integer from memory starting at p[0]. -** Return the number of bytes read. The value is stored in *v. +** Make sure all writes to a particular file are committed to disk. ** -** If the varint stored in p[0] is larger than can fit in a 32-bit unsigned -** integer, then set *v to 0xffffffff. +** If dataOnly==0 then both the file itself and its metadata (file +** size, access time, etc) are synced. If dataOnly!=0 then only the +** file data is synced. ** -** A MACRO version, getVarint32, is provided which inlines the -** single-byte case. All code should use the MACRO version as -** this function assumes the single-byte case has already been handled. +** Under Unix, also make sure that the directory entry for the file +** has been created by fsync-ing the directory that contains the file. +** If we do not do this and we encounter a power failure, the directory +** entry for the journal might not exist after we reboot. The next +** SQLite to access the file will not know that the journal exists (because +** the directory entry for the journal was never created) and the transaction +** will not roll back - possibly leading to database corruption. */ -SQLITE_PRIVATE u8 sqlcipher_sqlite3GetVarint32(const unsigned char *p, u32 *v){ - u32 a,b; +static int unixSync(sqlcipher_sqlite3_file *id, int flags){ + int rc; + unixFile *pFile = (unixFile*)id; - /* The 1-byte case. Overwhelmingly the most common. Handled inline - ** by the getVarin32() macro */ - a = *p; - /* a: p0 (unmasked) */ -#ifndef getVarint32 - if (!(a&0x80)) - { - /* Values between 0 and 127 */ - *v = a; - return 1; - } -#endif + int isDataOnly = (flags&SQLITE_SYNC_DATAONLY); + int isFullsync = (flags&0x0F)==SQLITE_SYNC_FULL; - /* The 2-byte case */ - p++; - b = *p; - /* b: p1 (unmasked) */ - if (!(b&0x80)) - { - /* Values between 128 and 16383 */ - a &= 0x7f; - a = a<<7; - *v = a | b; - return 2; - } + /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */ + assert((flags&0x0F)==SQLITE_SYNC_NORMAL + || (flags&0x0F)==SQLITE_SYNC_FULL + ); - /* The 3-byte case */ - p++; - a = a<<14; - a |= *p; - /* a: p0<<14 | p2 (unmasked) */ - if (!(a&0x80)) - { - /* Values between 16384 and 2097151 */ - a &= (0x7f<<14)|(0x7f); - b &= 0x7f; - b = b<<7; - *v = a | b; - return 3; + /* Unix cannot, but some systems may return SQLITE_FULL from here. This + ** line is to test that doing so does not cause any problems. + */ + SimulateDiskfullError( return SQLITE_FULL ); + + assert( pFile ); + OSTRACE(("SYNC %-3d\n", pFile->h)); + rc = full_fsync(pFile->h, isFullsync, isDataOnly); + SimulateIOError( rc=1 ); + if( rc ){ + storeLastErrno(pFile, errno); + return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath); } - /* A 32-bit varint is used to store size information in btrees. - ** Objects are rarely larger than 2MiB limit of a 3-byte varint. - ** A 3-byte varint is sufficient, for example, to record the size - ** of a 1048569-byte BLOB or string. - ** - ** We only unroll the first 1-, 2-, and 3- byte cases. The very - ** rare larger cases can be handled by the slower 64-bit varint - ** routine. + /* Also fsync the directory containing the file if the DIRSYNC flag + ** is set. This is a one-time occurrence. Many systems (examples: AIX) + ** are unable to fsync a directory, so ignore errors on the fsync. */ -#if 1 - { - u64 v64; - u8 n; - - n = sqlcipher_sqlite3GetVarint(p-2, &v64); - assert( n>3 && n<=9 ); - if( (v64 & SQLITE_MAX_U32)!=v64 ){ - *v = 0xffffffff; + if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){ + int dirfd; + OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath, + HAVE_FULLFSYNC, isFullsync)); + rc = osOpenDirectory(pFile->zPath, &dirfd); + if( rc==SQLITE_OK ){ + full_fsync(dirfd, 0, 0); + robust_close(pFile, dirfd, __LINE__); }else{ - *v = (u32)v64; + assert( rc==SQLITE_CANTOPEN ); + rc = SQLITE_OK; } - return n; + pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC; } + return rc; +} -#else - /* For following code (kept for historical record only) shows an - ** unrolling for the 3- and 4-byte varint cases. This code is - ** slightly faster, but it is also larger and much harder to test. +/* +** Truncate an open file to a specified size +*/ +static int unixTruncate(sqlcipher_sqlite3_file *id, i64 nByte){ + unixFile *pFile = (unixFile *)id; + int rc; + assert( pFile ); + SimulateIOError( return SQLITE_IOERR_TRUNCATE ); + + /* If the user has configured a chunk-size for this file, truncate the + ** file so that it consists of an integer number of chunks (i.e. the + ** actual file size after the operation may be larger than the requested + ** size). */ - p++; - b = b<<14; - b |= *p; - /* b: p1<<14 | p3 (unmasked) */ - if (!(b&0x80)) - { - /* Values between 2097152 and 268435455 */ - b &= (0x7f<<14)|(0x7f); - a &= (0x7f<<14)|(0x7f); - a = a<<7; - *v = a | b; - return 4; + if( pFile->szChunk>0 ){ + nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; } - p++; - a = a<<14; - a |= *p; - /* a: p0<<28 | p2<<14 | p4 (unmasked) */ - if (!(a&0x80)) - { - /* Values between 268435456 and 34359738367 */ - a &= SLOT_4_2_0; - b &= SLOT_4_2_0; - b = b<<7; - *v = a | b; - return 5; - } + rc = robust_ftruncate(pFile->h, nByte); + if( rc ){ + storeLastErrno(pFile, errno); + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); + }else{ +#ifdef SQLITE_DEBUG + /* If we are doing a normal write to a database file (as opposed to + ** doing a hot-journal rollback or a write to some file other than a + ** normal database file) and we truncate the file to zero length, + ** that effectively updates the change counter. This might happen + ** when restoring a database using the backup API from a zero-length + ** source. + */ + if( pFile->inNormalWrite && nByte==0 ){ + pFile->transCntrChng = 1; + } +#endif - /* We can only reach this point when reading a corrupt database - ** file. In that case we are not in any hurry. Use the (relatively - ** slow) general-purpose sqlcipher_sqlite3GetVarint() routine to extract the - ** value. */ - { - u64 v64; - u8 n; +#if SQLITE_MAX_MMAP_SIZE>0 + /* If the file was just truncated to a size smaller than the currently + ** mapped region, reduce the effective mapping size as well. SQLite will + ** use read() and write() to access data beyond this point from now on. + */ + if( nBytemmapSize ){ + pFile->mmapSize = nByte; + } +#endif - p -= 4; - n = sqlcipher_sqlite3GetVarint(p, &v64); - assert( n>5 && n<=9 ); - *v = (u32)v64; - return n; + return SQLITE_OK; } -#endif } /* -** Return the number of bytes that will be needed to store the given -** 64-bit integer. +** Determine the current size of a file in bytes */ -SQLITE_PRIVATE int sqlcipher_sqlite3VarintLen(u64 v){ - int i; - for(i=1; (v >>= 7)!=0; i++){ assert( i<10 ); } - return i; -} - +static int unixFileSize(sqlcipher_sqlite3_file *id, i64 *pSize){ + int rc; + struct stat buf; + assert( id ); + rc = osFstat(((unixFile*)id)->h, &buf); + SimulateIOError( rc=1 ); + if( rc!=0 ){ + storeLastErrno((unixFile*)id, errno); + return SQLITE_IOERR_FSTAT; + } + *pSize = buf.st_size; -/* -** Read or write a four-byte big-endian integer value. -*/ -SQLITE_PRIVATE u32 sqlcipher_sqlite3Get4byte(const u8 *p){ -#if SQLITE_BYTEORDER==4321 - u32 x; - memcpy(&x,p,4); - return x; -#elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 - u32 x; - memcpy(&x,p,4); - return __builtin_bswap32(x); -#elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 - u32 x; - memcpy(&x,p,4); - return _byteswap_ulong(x); -#else - testcase( p[0]&0x80 ); - return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; -#endif -} -SQLITE_PRIVATE void sqlcipher_sqlite3Put4byte(unsigned char *p, u32 v){ -#if SQLITE_BYTEORDER==4321 - memcpy(p,&v,4); -#elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 - u32 x = __builtin_bswap32(v); - memcpy(p,&x,4); -#elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 - u32 x = _byteswap_ulong(v); - memcpy(p,&x,4); -#else - p[0] = (u8)(v>>24); - p[1] = (u8)(v>>16); - p[2] = (u8)(v>>8); - p[3] = (u8)v; -#endif -} + /* When opening a zero-size database, the findInodeInfo() procedure + ** writes a single byte into that file in order to work around a bug + ** in the OS-X msdos filesystem. In order to avoid problems with upper + ** layers, we need to report this file size as zero even though it is + ** really 1. Ticket #3260. + */ + if( *pSize==1 ) *pSize = 0; + return SQLITE_OK; +} +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) /* -** Translate a single byte of Hex into an integer. -** This routine only works if h really is a valid hexadecimal -** character: 0..9a..fA..F +** Handler for proxy-locking file-control verbs. Defined below in the +** proxying locking division. */ -SQLITE_PRIVATE u8 sqlcipher_sqlite3HexToInt(int h){ - assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') ); -#ifdef SQLITE_ASCII - h += 9*(1&(h>>6)); -#endif -#ifdef SQLITE_EBCDIC - h += 9*(1&~(h>>4)); +static int proxyFileControl(sqlcipher_sqlite3_file*,int,void*); #endif - return (u8)(h & 0xf); -} -/* BEGIN SQLCIPHER */ -#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) /* -** Convert a BLOB literal of the form "x'hhhhhh'" into its binary -** value. Return a pointer to its binary value. Space to hold the -** binary value has been obtained from malloc and must be freed by -** the calling routine. +** This function is called to handle the SQLITE_FCNTL_SIZE_HINT +** file-control operation. Enlarge the database to nBytes in size +** (rounded up to the next chunk-size). If the database is already +** nBytes or larger, this routine is a no-op. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3HexToBlob(sqlcipher_sqlite3 *db, const char *z, int n){ - char *zBlob; - int i; +static int fcntlSizeHint(unixFile *pFile, i64 nByte){ + if( pFile->szChunk>0 ){ + i64 nSize; /* Required file size */ + struct stat buf; /* Used to hold return values of fstat() */ - zBlob = (char *)sqlcipher_sqlite3DbMallocRawNN(db, n/2 + 1); - n--; - if( zBlob ){ - for(i=0; ih, &buf) ){ + return SQLITE_IOERR_FSTAT; + } + + nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; + if( nSize>(i64)buf.st_size ){ + +#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE + /* The code below is handling the return value of osFallocate() + ** correctly. posix_fallocate() is defined to "returns zero on success, + ** or an error number on failure". See the manpage for details. */ + int err; + do{ + err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); + }while( err==EINTR ); + if( err && err!=EINVAL ) return SQLITE_IOERR_WRITE; +#else + /* If the OS does not have posix_fallocate(), fake it. Write a + ** single byte to the last byte in each block that falls entirely + ** within the extended region. Then, if required, a single byte + ** at offset (nSize-1), to set the size of the file correctly. + ** This is a similar technique to that used by glibc on systems + ** that do not have a real fallocate() call. + */ + int nBlk = buf.st_blksize; /* File-system block size */ + int nWrite = 0; /* Number of bytes written by seekAndWrite */ + i64 iWrite; /* Next offset to write to */ + + iWrite = (buf.st_size/nBlk)*nBlk + nBlk - 1; + assert( iWrite>=buf.st_size ); + assert( ((iWrite+1)%nBlk)==0 ); + for(/*no-op*/; iWrite=nSize ) iWrite = nSize - 1; + nWrite = seekAndWrite(pFile, iWrite, "", 1); + if( nWrite!=1 ) return SQLITE_IOERR_WRITE; + } +#endif } - zBlob[i/2] = 0; } - return zBlob; -} -#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */ -/* END SQLCIPHER */ -/* -** Log an error that is an API call on a connection pointer that should -** not have been used. The "type" of connection pointer is given as the -** argument. The zType is a word like "NULL" or "closed" or "invalid". -*/ -static void logBadConnection(const char *zType){ - sqlcipher_sqlite3_log(SQLITE_MISUSE, - "API call with %s database connection pointer", - zType - ); +#if SQLITE_MAX_MMAP_SIZE>0 + if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){ + int rc; + if( pFile->szChunk<=0 ){ + if( robust_ftruncate(pFile->h, nByte) ){ + storeLastErrno(pFile, errno); + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); + } + } + + rc = unixMapfile(pFile, nByte); + return rc; + } +#endif + + return SQLITE_OK; } /* -** Check to make sure we have a valid db pointer. This test is not -** foolproof but it does provide some measure of protection against -** misuse of the interface such as passing in db pointers that are -** NULL or which have been previously closed. If this routine returns -** 1 it means that the db pointer is valid and 0 if it should not be -** dereferenced for any reason. The calling function should invoke -** SQLITE_MISUSE immediately. +** If *pArg is initially negative then this is a query. Set *pArg to +** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set. ** -** sqlcipher_sqlite3SafetyCheckOk() requires that the db pointer be valid for -** use. sqlcipher_sqlite3SafetyCheckSickOrOk() allows a db pointer that failed to -** open properly and is not fit for general use but which can be -** used as an argument to sqlcipher_sqlite3_errmsg() or sqlcipher_sqlite3_close(). +** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags. */ -SQLITE_PRIVATE int sqlcipher_sqlite3SafetyCheckOk(sqlcipher_sqlite3 *db){ - u32 magic; - if( db==0 ){ - logBadConnection("NULL"); - return 0; - } - magic = db->magic; - if( magic!=SQLITE_MAGIC_OPEN ){ - if( sqlcipher_sqlite3SafetyCheckSickOrOk(db) ){ - testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); - logBadConnection("unopened"); - } - return 0; - }else{ - return 1; - } -} -SQLITE_PRIVATE int sqlcipher_sqlite3SafetyCheckSickOrOk(sqlcipher_sqlite3 *db){ - u32 magic; - magic = db->magic; - if( magic!=SQLITE_MAGIC_SICK && - magic!=SQLITE_MAGIC_OPEN && - magic!=SQLITE_MAGIC_BUSY ){ - testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); - logBadConnection("invalid"); - return 0; +static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ + if( *pArg<0 ){ + *pArg = (pFile->ctrlFlags & mask)!=0; + }else if( (*pArg)==0 ){ + pFile->ctrlFlags &= ~mask; }else{ - return 1; + pFile->ctrlFlags |= mask; } } +/* Forward declaration */ +static int unixGetTempname(int nBuf, char *zBuf); +#ifndef SQLITE_OMIT_WAL + static int unixFcntlExternalReader(unixFile*, int*); +#endif + /* -** Attempt to add, substract, or multiply the 64-bit signed value iB against -** the other 64-bit signed integer at *pA and store the result in *pA. -** Return 0 on success. Or if the operation would have resulted in an -** overflow, leave *pA unchanged and return 1. +** Information and control of an open file handle. */ -SQLITE_PRIVATE int sqlcipher_sqlite3AddInt64(i64 *pA, i64 iB){ -#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER) - return __builtin_add_overflow(*pA, iB, pA); -#else - i64 iA = *pA; - testcase( iA==0 ); testcase( iA==1 ); - testcase( iB==-1 ); testcase( iB==0 ); - if( iB>=0 ){ - testcase( iA>0 && LARGEST_INT64 - iA == iB ); - testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 ); - if( iA>0 && LARGEST_INT64 - iA < iB ) return 1; - }else{ - testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 ); - testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 ); - if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1; - } - *pA += iB; - return 0; +static int unixFileControl(sqlcipher_sqlite3_file *id, int op, void *pArg){ + unixFile *pFile = (unixFile*)id; + switch( op ){ +#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) + case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: { + int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE); + return rc ? SQLITE_IOERR_BEGIN_ATOMIC : SQLITE_OK; + } + case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: { + int rc = osIoctl(pFile->h, F2FS_IOC_COMMIT_ATOMIC_WRITE); + return rc ? SQLITE_IOERR_COMMIT_ATOMIC : SQLITE_OK; + } + case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: { + int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE); + return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK; + } +#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ + + case SQLITE_FCNTL_LOCKSTATE: { + *(int*)pArg = pFile->eFileLock; + return SQLITE_OK; + } + case SQLITE_FCNTL_LAST_ERRNO: { + *(int*)pArg = pFile->lastErrno; + return SQLITE_OK; + } + case SQLITE_FCNTL_CHUNK_SIZE: { + pFile->szChunk = *(int *)pArg; + return SQLITE_OK; + } + case SQLITE_FCNTL_SIZE_HINT: { + int rc; + SimulateIOErrorBenign(1); + rc = fcntlSizeHint(pFile, *(i64 *)pArg); + SimulateIOErrorBenign(0); + return rc; + } + case SQLITE_FCNTL_PERSIST_WAL: { + unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg); + return SQLITE_OK; + } + case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { + unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg); + return SQLITE_OK; + } + case SQLITE_FCNTL_VFSNAME: { + *(char**)pArg = sqlcipher_sqlite3_mprintf("%s", pFile->pVfs->zName); + return SQLITE_OK; + } + case SQLITE_FCNTL_TEMPFILENAME: { + char *zTFile = sqlcipher_sqlite3_malloc64( pFile->pVfs->mxPathname ); + if( zTFile ){ + unixGetTempname(pFile->pVfs->mxPathname, zTFile); + *(char**)pArg = zTFile; + } + return SQLITE_OK; + } + case SQLITE_FCNTL_HAS_MOVED: { + *(int*)pArg = fileHasMoved(pFile); + return SQLITE_OK; + } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + case SQLITE_FCNTL_LOCK_TIMEOUT: { + int iOld = pFile->iBusyTimeout; + pFile->iBusyTimeout = *(int*)pArg; + *(int*)pArg = iOld; + return SQLITE_OK; + } #endif -} -SQLITE_PRIVATE int sqlcipher_sqlite3SubInt64(i64 *pA, i64 iB){ -#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER) - return __builtin_sub_overflow(*pA, iB, pA); -#else - testcase( iB==SMALLEST_INT64+1 ); - if( iB==SMALLEST_INT64 ){ - testcase( (*pA)==(-1) ); testcase( (*pA)==0 ); - if( (*pA)>=0 ) return 1; - *pA -= iB; - return 0; - }else{ - return sqlcipher_sqlite3AddInt64(pA, -iB); - } +#if SQLITE_MAX_MMAP_SIZE>0 + case SQLITE_FCNTL_MMAP_SIZE: { + i64 newLimit = *(i64*)pArg; + int rc = SQLITE_OK; + if( newLimit>sqlcipher_sqlite3GlobalConfig.mxMmap ){ + newLimit = sqlcipher_sqlite3GlobalConfig.mxMmap; + } + + /* The value of newLimit may be eventually cast to (size_t) and passed + ** to mmap(). Restrict its value to 2GB if (size_t) is not at least a + ** 64-bit type. */ + if( newLimit>0 && sizeof(size_t)<8 ){ + newLimit = (newLimit & 0x7FFFFFFF); + } + + *(i64*)pArg = pFile->mmapSizeMax; + if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ + pFile->mmapSizeMax = newLimit; + if( pFile->mmapSize>0 ){ + unixUnmapfile(pFile); + rc = unixMapfile(pFile, -1); + } + } + return rc; + } #endif -} -SQLITE_PRIVATE int sqlcipher_sqlite3MulInt64(i64 *pA, i64 iB){ -#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER) - return __builtin_mul_overflow(*pA, iB, pA); +#ifdef SQLITE_DEBUG + /* The pager calls this method to signal that it has done + ** a rollback and that the database is therefore unchanged and + ** it hence it is OK for the transaction change counter to be + ** unchanged. + */ + case SQLITE_FCNTL_DB_UNCHANGED: { + ((unixFile*)id)->dbUpdate = 0; + return SQLITE_OK; + } +#endif +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) + case SQLITE_FCNTL_SET_LOCKPROXYFILE: + case SQLITE_FCNTL_GET_LOCKPROXYFILE: { + return proxyFileControl(id,op,pArg); + } +#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ + + case SQLITE_FCNTL_EXTERNAL_READER: { +#ifndef SQLITE_OMIT_WAL + return unixFcntlExternalReader((unixFile*)id, (int*)pArg); #else - i64 iA = *pA; - if( iB>0 ){ - if( iA>LARGEST_INT64/iB ) return 1; - if( iA0 ){ - if( iBLARGEST_INT64/-iB ) return 1; + *(int*)pArg = 0; + return SQLITE_OK; +#endif } } - *pA = iA*iB; - return 0; -#endif + return SQLITE_NOTFOUND; } /* -** Compute the absolute value of a 32-bit signed integer, of possible. Or -** if the integer has a value of -2147483648, return +2147483647 +** If pFd->sectorSize is non-zero when this function is called, it is a +** no-op. Otherwise, the values of pFd->sectorSize and +** pFd->deviceCharacteristics are set according to the file-system +** characteristics. +** +** There are two versions of this function. One for QNX and one for all +** other systems. */ -SQLITE_PRIVATE int sqlcipher_sqlite3AbsInt32(int x){ - if( x>=0 ) return x; - if( x==(int)0x80000000 ) return 0x7fffffff; - return -x; +#ifndef __QNXNTO__ +static void setDeviceCharacteristics(unixFile *pFd){ + assert( pFd->deviceCharacteristics==0 || pFd->sectorSize!=0 ); + if( pFd->sectorSize==0 ){ +#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) + int res; + u32 f = 0; + + /* Check for support for F2FS atomic batch writes. */ + res = osIoctl(pFd->h, F2FS_IOC_GET_FEATURES, &f); + if( res==0 && (f & F2FS_FEATURE_ATOMIC_WRITE) ){ + pFd->deviceCharacteristics = SQLITE_IOCAP_BATCH_ATOMIC; + } +#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ + + /* Set the POWERSAFE_OVERWRITE flag if requested. */ + if( pFd->ctrlFlags & UNIXFILE_PSOW ){ + pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; + } + + pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; + } } +#else +#include +#include +static void setDeviceCharacteristics(unixFile *pFile){ + if( pFile->sectorSize == 0 ){ + struct statvfs fsInfo; -#ifdef SQLITE_ENABLE_8_3_NAMES -/* -** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database -** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and -** if filename in z[] has a suffix (a.k.a. "extension") that is longer than -** three characters, then shorten the suffix on z[] to be the last three -** characters of the original suffix. -** -** If SQLITE_ENABLE_8_3_NAMES is set to 2 at compile-time, then always -** do the suffix shortening regardless of URI parameter. -** -** Examples: -** -** test.db-journal => test.nal -** test.db-wal => test.wal -** test.db-shm => test.shm -** test.db-mj7f3319fa => test.9fa -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3FileSuffix3(const char *zBaseFilename, char *z){ -#if SQLITE_ENABLE_8_3_NAMES<2 - if( sqlcipher_sqlite3_uri_boolean(zBaseFilename, "8_3_names", 0) ) -#endif - { - int i, sz; - sz = sqlcipher_sqlite3Strlen30(z); - for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} - if( z[i]=='.' && ALWAYS(sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4); + /* Set defaults for non-supported filesystems */ + pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; + pFile->deviceCharacteristics = 0; + if( fstatvfs(pFile->h, &fsInfo) == -1 ) { + return; + } + + if( !strcmp(fsInfo.f_basetype, "tmp") ) { + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + SQLITE_IOCAP_ATOMIC4K | /* All ram filesystem writes are atomic */ + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until + ** the write succeeds */ + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; + }else if( strstr(fsInfo.f_basetype, "etfs") ){ + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + /* etfs cluster size writes are atomic */ + (pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) | + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until + ** the write succeeds */ + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; + }else if( !strcmp(fsInfo.f_basetype, "qnx6") ){ + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + SQLITE_IOCAP_ATOMIC | /* All filesystem writes are atomic */ + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until + ** the write succeeds */ + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; + }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){ + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + /* full bitset of atomics from max sector size and smaller */ + ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; + }else if( strstr(fsInfo.f_basetype, "dos") ){ + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + /* full bitset of atomics from max sector size and smaller */ + ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; + }else{ + pFile->deviceCharacteristics = + SQLITE_IOCAP_ATOMIC512 | /* blocks are atomic */ + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until + ** the write succeeds */ + 0; + } + } + /* Last chance verification. If the sector size isn't a multiple of 512 + ** then it isn't valid.*/ + if( pFile->sectorSize % 512 != 0 ){ + pFile->deviceCharacteristics = 0; + pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; } } #endif /* -** Find (an approximate) sum of two LogEst values. This computation is -** not a simple "+" operator because LogEst is stored as a logarithmic -** value. +** Return the sector size in bytes of the underlying block device for +** the specified file. This is almost always 512 bytes, but may be +** larger for some devices. ** +** SQLite code assumes this function cannot fail. It also assumes that +** if two files are created in the same file-system directory (i.e. +** a database and its journal file) that the sector size will be the +** same for both. */ -SQLITE_PRIVATE LogEst sqlcipher_sqlite3LogEstAdd(LogEst a, LogEst b){ - static const unsigned char x[] = { - 10, 10, /* 0,1 */ - 9, 9, /* 2,3 */ - 8, 8, /* 4,5 */ - 7, 7, 7, /* 6,7,8 */ - 6, 6, 6, /* 9,10,11 */ - 5, 5, 5, /* 12-14 */ - 4, 4, 4, 4, /* 15-18 */ - 3, 3, 3, 3, 3, 3, /* 19-24 */ - 2, 2, 2, 2, 2, 2, 2, /* 25-31 */ - }; - if( a>=b ){ - if( a>b+49 ) return a; - if( a>b+31 ) return a+1; - return a+x[a-b]; - }else{ - if( b>a+49 ) return b; - if( b>a+31 ) return b+1; - return b+x[b-a]; - } +static int unixSectorSize(sqlcipher_sqlite3_file *id){ + unixFile *pFd = (unixFile*)id; + setDeviceCharacteristics(pFd); + return pFd->sectorSize; } /* -** Convert an integer into a LogEst. In other words, compute an -** approximation for 10*log2(x). +** Return the device characteristics for the file. +** +** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default. +** However, that choice is controversial since technically the underlying +** file system does not always provide powersafe overwrites. (In other +** words, after a power-loss event, parts of the file that were never +** written might end up being altered.) However, non-PSOW behavior is very, +** very rare. And asserting PSOW makes a large reduction in the amount +** of required I/O for journaling, since a lot of padding is eliminated. +** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control +** available to turn it off and URI query parameter available to turn it off. */ -SQLITE_PRIVATE LogEst sqlcipher_sqlite3LogEst(u64 x){ - static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 }; - LogEst y = 40; - if( x<8 ){ - if( x<2 ) return 0; - while( x<8 ){ y -= 10; x <<= 1; } - }else{ -#if GCC_VERSION>=5004000 - int i = 60 - __builtin_clzll(x); - y += i*10; - x >>= i; -#else - while( x>255 ){ y += 40; x >>= 4; } /*OPTIMIZATION-IF-TRUE*/ - while( x>15 ){ y += 10; x >>= 1; } -#endif - } - return a[x&7] + y - 10; +static int unixDeviceCharacteristics(sqlcipher_sqlite3_file *id){ + unixFile *pFd = (unixFile*)id; + setDeviceCharacteristics(pFd); + return pFd->deviceCharacteristics; } -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* -** Convert a double into a LogEst -** In other words, compute an approximation for 10*log2(x). -*/ -SQLITE_PRIVATE LogEst sqlcipher_sqlite3LogEstFromDouble(double x){ - u64 a; - LogEst e; - assert( sizeof(x)==8 && sizeof(a)==8 ); - if( x<=1 ) return 0; - if( x<=2000000000 ) return sqlcipher_sqlite3LogEst((u64)x); - memcpy(&a, &x, 8); - e = (a>>52) - 1022; - return e*10; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 -#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ - defined(SQLITE_ENABLE_STAT4) || \ - defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) /* -** Convert a LogEst into an integer. +** Return the system page size. ** -** Note that this routine is only used when one or more of various -** non-standard compile-time options is enabled. +** This function should not be called directly by other code in this file. +** Instead, it should be called via macro osGetpagesize(). */ -SQLITE_PRIVATE u64 sqlcipher_sqlite3LogEstToInt(LogEst x){ - u64 n; - n = x%10; - x /= 10; - if( n>=5 ) n -= 2; - else if( n>=1 ) n -= 1; -#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ - defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) - if( x>60 ) return (u64)LARGEST_INT64; +static int unixGetpagesize(void){ +#if OS_VXWORKS + return 1024; +#elif defined(_BSD_SOURCE) + return getpagesize(); #else - /* If only SQLITE_ENABLE_STAT4 is on, then the largest input - ** possible to this routine is 310, resulting in a maximum x of 31 */ - assert( x<=60 ); + return (int)sysconf(_SC_PAGESIZE); #endif - return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x); } -#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */ + +#endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */ + +#ifndef SQLITE_OMIT_WAL /* -** Add a new name/number pair to a VList. This might require that the -** VList object be reallocated, so return the new VList. If an OOM -** error occurs, the original VList returned and the -** db->mallocFailed flag is set. +** Object used to represent an shared memory buffer. ** -** A VList is really just an array of integers. To destroy a VList, -** simply pass it to sqlcipher_sqlite3DbFree(). +** When multiple threads all reference the same wal-index, each thread +** has its own unixShm object, but they all point to a single instance +** of this unixShmNode object. In other words, each wal-index is opened +** only once per process. ** -** The first integer is the number of integers allocated for the whole -** VList. The second integer is the number of integers actually used. -** Each name/number pair is encoded by subsequent groups of 3 or more -** integers. +** Each unixShmNode object is connected to a single unixInodeInfo object. +** We could coalesce this object into unixInodeInfo, but that would mean +** every open file that does not use shared memory (in other words, most +** open files) would have to carry around this extra information. So +** the unixInodeInfo object contains a pointer to this unixShmNode object +** and the unixShmNode object is created only when needed. ** -** Each name/number pair starts with two integers which are the numeric -** value for the pair and the size of the name/number pair, respectively. -** The text name overlays one or more following integers. The text name -** is always zero-terminated. +** unixMutexHeld() must be true when creating or destroying +** this object or while reading or writing the following fields: ** -** Conceptually: +** nRef ** -** struct VList { -** int nAlloc; // Number of allocated slots -** int nUsed; // Number of used slots -** struct VListEntry { -** int iValue; // Value for this entry -** int nSlot; // Slots used by this entry -** // ... variable name goes here -** } a[0]; -** } +** The following fields are read-only after the object is created: ** -** During code generation, pointers to the variable names within the -** VList are taken. When that happens, nAlloc is set to zero as an -** indication that the VList may never again be enlarged, since the -** accompanying realloc() would invalidate the pointers. -*/ -SQLITE_PRIVATE VList *sqlcipher_sqlite3VListAdd( - sqlcipher_sqlite3 *db, /* The database connection used for malloc() */ - VList *pIn, /* The input VList. Might be NULL */ - const char *zName, /* Name of symbol to add */ - int nName, /* Bytes of text in zName */ - int iVal /* Value to associate with zName */ -){ - int nInt; /* number of sizeof(int) objects needed for zName */ - char *z; /* Pointer to where zName will be stored */ - int i; /* Index in pIn[] where zName is stored */ - - nInt = nName/4 + 3; - assert( pIn==0 || pIn[0]>=3 ); /* Verify ok to add new elements */ - if( pIn==0 || pIn[1]+nInt > pIn[0] ){ - /* Enlarge the allocation */ - sqlcipher_sqlite3_int64 nAlloc = (pIn ? 2*(sqlcipher_sqlite3_int64)pIn[0] : 10) + nInt; - VList *pOut = sqlcipher_sqlite3DbRealloc(db, pIn, nAlloc*sizeof(int)); - if( pOut==0 ) return pIn; - if( pIn==0 ) pOut[1] = 2; - pIn = pOut; - pIn[0] = nAlloc; - } - i = pIn[1]; - pIn[i] = iVal; - pIn[i+1] = nInt; - z = (char*)&pIn[i+2]; - pIn[1] = i+nInt; - assert( pIn[1]<=pIn[0] ); - memcpy(z, zName, nName); - z[nName] = 0; - return pIn; -} - -/* -** Return a pointer to the name of a variable in the given VList that -** has the value iVal. Or return a NULL if there is no such variable in -** the list -*/ -SQLITE_PRIVATE const char *sqlcipher_sqlite3VListNumToName(VList *pIn, int iVal){ - int i, mx; - if( pIn==0 ) return 0; - mx = pIn[1]; - i = 2; - do{ - if( pIn[i]==iVal ) return (char*)&pIn[i+2]; - i += pIn[i+1]; - }while( i */ - -/* Turn bulk memory into a hash table object by initializing the -** fields of the Hash structure. +** unixShm.pShmNode +** unixShm.id ** -** "pNew" is a pointer to the hash table that is to be initialized. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3HashInit(Hash *pNew){ - assert( pNew!=0 ); - pNew->first = 0; - pNew->count = 0; - pNew->htsize = 0; - pNew->ht = 0; -} - -/* Remove all entries from a hash table. Reclaim all memory. -** Call this routine to delete a hash table or to reset a hash table -** to the empty state. +** All other fields are read/write. The unixShm.pShmNode->pShmMutex must +** be held while accessing any read/write fields. */ -SQLITE_PRIVATE void sqlcipher_sqlite3HashClear(Hash *pH){ - HashElem *elem; /* For looping over all elements of the table */ - - assert( pH!=0 ); - elem = pH->first; - pH->first = 0; - sqlcipher_sqlite3_free(pH->ht); - pH->ht = 0; - pH->htsize = 0; - while( elem ){ - HashElem *next_elem = elem->next; - sqlcipher_sqlite3_free(elem); - elem = next_elem; - } - pH->count = 0; -} +struct unixShm { + unixShmNode *pShmNode; /* The underlying unixShmNode object */ + unixShm *pNext; /* Next unixShm with the same unixShmNode */ + u8 hasMutex; /* True if holding the unixShmNode->pShmMutex */ + u8 id; /* Id of this connection within its unixShmNode */ + u16 sharedMask; /* Mask of shared locks held */ + u16 exclMask; /* Mask of exclusive locks held */ +}; /* -** The hashing function. -*/ -static unsigned int strHash(const char *z){ - unsigned int h = 0; - unsigned char c; - while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/ - /* Knuth multiplicative hashing. (Sorting & Searching, p. 510). - ** 0x9e3779b1 is 2654435761 which is the closest prime number to - ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */ - h += sqlcipher_sqlite3UpperToLower[c]; - h *= 0x9e3779b1; - } - return h; -} - - -/* Link pNew element into the hash table pH. If pEntry!=0 then also -** insert pNew into the pEntry hash bucket. +** Constants used for locking */ -static void insertElement( - Hash *pH, /* The complete hash table */ - struct _ht *pEntry, /* The entry into which pNew is inserted */ - HashElem *pNew /* The element to be inserted */ -){ - HashElem *pHead; /* First element already in pEntry */ - if( pEntry ){ - pHead = pEntry->count ? pEntry->chain : 0; - pEntry->count++; - pEntry->chain = pNew; - }else{ - pHead = 0; - } - if( pHead ){ - pNew->next = pHead; - pNew->prev = pHead->prev; - if( pHead->prev ){ pHead->prev->next = pNew; } - else { pH->first = pNew; } - pHead->prev = pNew; - }else{ - pNew->next = pH->first; - if( pH->first ){ pH->first->prev = pNew; } - pNew->prev = 0; - pH->first = pNew; - } -} - +#define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ +#define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ -/* Resize the hash table so that it cantains "new_size" buckets. -** -** The hash table might fail to resize if sqlcipher_sqlite3_malloc() fails or -** if the new size is the same as the prior size. -** Return TRUE if the resize occurs and false if not. +/* +** Use F_GETLK to check whether or not there are any readers with open +** wal-mode transactions in other processes on database file pFile. If +** no error occurs, return SQLITE_OK and set (*piOut) to 1 if there are +** such transactions, or 0 otherwise. If an error occurs, return an +** SQLite error code. The final value of *piOut is undefined in this +** case. */ -static int rehash(Hash *pH, unsigned int new_size){ - struct _ht *new_ht; /* The new hash table */ - HashElem *elem, *next_elem; /* For looping over existing elements */ - -#if SQLITE_MALLOC_SOFT_LIMIT>0 - if( new_size*sizeof(struct _ht)>SQLITE_MALLOC_SOFT_LIMIT ){ - new_size = SQLITE_MALLOC_SOFT_LIMIT/sizeof(struct _ht); - } - if( new_size==pH->htsize ) return 0; -#endif +static int unixFcntlExternalReader(unixFile *pFile, int *piOut){ + int rc = SQLITE_OK; + *piOut = 0; + if( pFile->pShm){ + unixShmNode *pShmNode = pFile->pShm->pShmNode; + struct flock f; - /* The inability to allocates space for a larger hash table is - ** a performance hit but it is not a fatal error. So mark the - ** allocation as a benign. Use sqlcipher_sqlite3Malloc()/memset(0) instead of - ** sqlcipher_sqlite3MallocZero() to make the allocation, as sqlcipher_sqlite3MallocZero() - ** only zeroes the requested number of bytes whereas this module will - ** use the actual amount of space allocated for the hash table (which - ** may be larger than the requested amount). - */ - sqlcipher_sqlite3BeginBenignMalloc(); - new_ht = (struct _ht *)sqlcipher_sqlite3Malloc( new_size*sizeof(struct _ht) ); - sqlcipher_sqlite3EndBenignMalloc(); + memset(&f, 0, sizeof(f)); + f.l_type = F_WRLCK; + f.l_whence = SEEK_SET; + f.l_start = UNIX_SHM_BASE + 3; + f.l_len = SQLITE_SHM_NLOCK - 3; - if( new_ht==0 ) return 0; - sqlcipher_sqlite3_free(pH->ht); - pH->ht = new_ht; - pH->htsize = new_size = sqlcipher_sqlite3MallocSize(new_ht)/sizeof(struct _ht); - memset(new_ht, 0, new_size*sizeof(struct _ht)); - for(elem=pH->first, pH->first=0; elem; elem = next_elem){ - unsigned int h = strHash(elem->pKey) % new_size; - next_elem = elem->next; - insertElement(pH, &new_ht[h], elem); + sqlcipher_sqlite3_mutex_enter(pShmNode->pShmMutex); + if( osFcntl(pShmNode->hShm, F_GETLK, &f)<0 ){ + rc = SQLITE_IOERR_LOCK; + }else{ + *piOut = (f.l_type!=F_UNLCK); + } + sqlcipher_sqlite3_mutex_leave(pShmNode->pShmMutex); } - return 1; + + return rc; } -/* This function (for internal use only) locates an element in an -** hash table that matches the given key. If no element is found, -** a pointer to a static null element with HashElem.data==0 is returned. -** If pH is not NULL, then the hash for this key is written to *pH. + +/* +** Apply posix advisory locks for all bytes from ofst through ofst+n-1. +** +** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking +** otherwise. */ -static HashElem *findElementWithHash( - const Hash *pH, /* The pH to be searched */ - const char *pKey, /* The key we are searching for */ - unsigned int *pHash /* Write the hash value here */ +static int unixShmSystemLock( + unixFile *pFile, /* Open connection to the WAL file */ + int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ + int ofst, /* First byte of the locking range */ + int n /* Number of bytes to lock */ ){ - HashElem *elem; /* Used to loop thru the element list */ - unsigned int count; /* Number of elements left to test */ - unsigned int h; /* The computed hash */ - static HashElem nullElement = { 0, 0, 0, 0 }; + unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */ + struct flock f; /* The posix advisory locking structure */ + int rc = SQLITE_OK; /* Result code form fcntl() */ - if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/ - struct _ht *pEntry; - h = strHash(pKey) % pH->htsize; - pEntry = &pH->ht[h]; - elem = pEntry->chain; - count = pEntry->count; - }else{ - h = 0; - elem = pH->first; - count = pH->count; - } - if( pHash ) *pHash = h; - while( count-- ){ - assert( elem!=0 ); - if( sqlcipher_sqlite3StrICmp(elem->pKey,pKey)==0 ){ - return elem; + /* Access to the unixShmNode object is serialized by the caller */ + pShmNode = pFile->pInode->pShmNode; + assert( pShmNode->nRef==0 || sqlcipher_sqlite3_mutex_held(pShmNode->pShmMutex) ); + assert( pShmNode->nRef>0 || unixMutexHeld() ); + + /* Shared locks never span more than one byte */ + assert( n==1 || lockType!=F_RDLCK ); + + /* Locks are within range */ + assert( n>=1 && n<=SQLITE_SHM_NLOCK ); + + if( pShmNode->hShm>=0 ){ + int res; + /* Initialize the locking parameters */ + f.l_type = lockType; + f.l_whence = SEEK_SET; + f.l_start = ofst; + f.l_len = n; + res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); + if( res==-1 ){ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY); +#else + rc = SQLITE_BUSY; +#endif } - elem = elem->next; } - return &nullElement; -} -/* Remove a single entry from the hash table given a pointer to that -** element and a hash on the element's key. -*/ -static void removeElementGivenHash( - Hash *pH, /* The pH containing "elem" */ - HashElem* elem, /* The element to be removed from the pH */ - unsigned int h /* Hash value for the element */ -){ - struct _ht *pEntry; - if( elem->prev ){ - elem->prev->next = elem->next; + /* Update the global lock state and do debug tracing */ +#ifdef SQLITE_DEBUG + { u16 mask; + OSTRACE(("SHM-LOCK ")); + mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<exclMask &= ~mask; + pShmNode->sharedMask &= ~mask; + }else if( lockType==F_RDLCK ){ + OSTRACE(("read-lock %d ok", ofst)); + pShmNode->exclMask &= ~mask; + pShmNode->sharedMask |= mask; + }else{ + assert( lockType==F_WRLCK ); + OSTRACE(("write-lock %d ok", ofst)); + pShmNode->exclMask |= mask; + pShmNode->sharedMask &= ~mask; + } }else{ - pH->first = elem->next; - } - if( elem->next ){ - elem->next->prev = elem->prev; - } - if( pH->ht ){ - pEntry = &pH->ht[h]; - if( pEntry->chain==elem ){ - pEntry->chain = elem->next; + if( lockType==F_UNLCK ){ + OSTRACE(("unlock %d failed", ofst)); + }else if( lockType==F_RDLCK ){ + OSTRACE(("read-lock failed")); + }else{ + assert( lockType==F_WRLCK ); + OSTRACE(("write-lock %d failed", ofst)); } - assert( pEntry->count>0 ); - pEntry->count--; } - sqlcipher_sqlite3_free( elem ); - pH->count--; - if( pH->count==0 ){ - assert( pH->first==0 ); - assert( pH->count==0 ); - sqlcipher_sqlite3HashClear(pH); + OSTRACE((" - afterwards %03x,%03x\n", + pShmNode->sharedMask, pShmNode->exclMask)); } +#endif + + return rc; } -/* Attempt to locate an element of the hash table pH with a key -** that matches pKey. Return the data for this element if it is -** found, or NULL if there is no match. +/* +** Return the minimum number of 32KB shm regions that should be mapped at +** a time, assuming that each mapping must be an integer multiple of the +** current system page-size. +** +** Usually, this is 1. The exception seems to be systems that are configured +** to use 64KB pages - in this case each mapping must cover at least two +** shm regions. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3HashFind(const Hash *pH, const char *pKey){ - assert( pH!=0 ); - assert( pKey!=0 ); - return findElementWithHash(pH, pKey, 0)->data; +static int unixShmRegionPerMap(void){ + int shmsz = 32*1024; /* SHM region size */ + int pgsz = osGetpagesize(); /* System page size */ + assert( ((pgsz-1)&pgsz)==0 ); /* Page size must be a power of 2 */ + if( pgszpInode->pShmNode; + assert( unixMutexHeld() ); + if( p && ALWAYS(p->nRef==0) ){ + int nShmPerMap = unixShmRegionPerMap(); + int i; + assert( p->pInode==pFd->pInode ); + sqlcipher_sqlite3_mutex_free(p->pShmMutex); + for(i=0; inRegion; i+=nShmPerMap){ + if( p->hShm>=0 ){ + osMunmap(p->apRegion[i], p->szRegion); + }else{ + sqlcipher_sqlite3_free(p->apRegion[i]); + } + } + sqlcipher_sqlite3_free(p->apRegion); + if( p->hShm>=0 ){ + robust_close(pFd, p->hShm, __LINE__); + p->hShm = -1; + } + p->pInode->pShmNode = 0; + sqlcipher_sqlite3_free(p); + } +} + +/* +** The DMS lock has not yet been taken on shm file pShmNode. Attempt to +** take it now. Return SQLITE_OK if successful, or an SQLite error +** code otherwise. ** -** If the "data" parameter to this function is NULL, then the -** element corresponding to "key" is removed from the hash table. +** If the DMS cannot be locked because this is a readonly_shm=1 +** connection and no other process already holds a lock, return +** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3HashInsert(Hash *pH, const char *pKey, void *data){ - unsigned int h; /* the hash of the key modulo hash table size */ - HashElem *elem; /* Used to loop thru the element list */ - HashElem *new_elem; /* New element added to the pH */ +static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ + struct flock lock; + int rc = SQLITE_OK; - assert( pH!=0 ); - assert( pKey!=0 ); - elem = findElementWithHash(pH,pKey,&h); - if( elem->data ){ - void *old_data = elem->data; - if( data==0 ){ - removeElementGivenHash(pH,elem,h); + /* Use F_GETLK to determine the locks other processes are holding + ** on the DMS byte. If it indicates that another process is holding + ** a SHARED lock, then this process may also take a SHARED lock + ** and proceed with opening the *-shm file. + ** + ** Or, if no other process is holding any lock, then this process + ** is the first to open it. In this case take an EXCLUSIVE lock on the + ** DMS byte and truncate the *-shm file to zero bytes in size. Then + ** downgrade to a SHARED lock on the DMS byte. + ** + ** If another process is holding an EXCLUSIVE lock on the DMS byte, + ** return SQLITE_BUSY to the caller (it will try again). An earlier + ** version of this code attempted the SHARED lock at this point. But + ** this introduced a subtle race condition: if the process holding + ** EXCLUSIVE failed just before truncating the *-shm file, then this + ** process might open and use the *-shm file without truncating it. + ** And if the *-shm file has been corrupted by a power failure or + ** system crash, the database itself may also become corrupt. */ + lock.l_whence = SEEK_SET; + lock.l_start = UNIX_SHM_DMS; + lock.l_len = 1; + lock.l_type = F_WRLCK; + if( osFcntl(pShmNode->hShm, F_GETLK, &lock)!=0 ) { + rc = SQLITE_IOERR_LOCK; + }else if( lock.l_type==F_UNLCK ){ + if( pShmNode->isReadonly ){ + pShmNode->isUnlocked = 1; + rc = SQLITE_READONLY_CANTINIT; }else{ - elem->data = data; - elem->pKey = pKey; - } - return old_data; - } - if( data==0 ) return 0; - new_elem = (HashElem*)sqlcipher_sqlite3Malloc( sizeof(HashElem) ); - if( new_elem==0 ) return data; - new_elem->pKey = pKey; - new_elem->data = data; - pH->count++; - if( pH->count>=10 && pH->count > 2*pH->htsize ){ - if( rehash(pH, pH->count*2) ){ - assert( pH->htsize>0 ); - h = strHash(pKey) % pH->htsize; + rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); + /* The first connection to attach must truncate the -shm file. We + ** truncate to 3 bytes (an arbitrary small number, less than the + ** -shm header size) rather than 0 as a system debugging aid, to + ** help detect if a -shm file truncation is legitimate or is the work + ** or a rogue process. */ + if( rc==SQLITE_OK && robust_ftruncate(pShmNode->hShm, 3) ){ + rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename); + } } + }else if( lock.l_type==F_WRLCK ){ + rc = SQLITE_BUSY; } - insertElement(pH, pH->ht ? &pH->ht[h] : 0, new_elem); - return 0; -} -/************** End of hash.c ************************************************/ -/************** Begin file opcodes.c *****************************************/ -/* Automatically generated. Do not edit */ -/* See the tool/mkopcodec.tcl script for details. */ -#if !defined(SQLITE_OMIT_EXPLAIN) \ - || defined(VDBE_PROFILE) \ - || defined(SQLITE_DEBUG) -#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG) -# define OpHelp(X) "\0" X -#else -# define OpHelp(X) -#endif -SQLITE_PRIVATE const char *sqlcipher_sqlite3OpcodeName(int i){ - static const char *const azName[] = { - /* 0 */ "Savepoint" OpHelp(""), - /* 1 */ "AutoCommit" OpHelp(""), - /* 2 */ "Transaction" OpHelp(""), - /* 3 */ "SorterNext" OpHelp(""), - /* 4 */ "Prev" OpHelp(""), - /* 5 */ "Next" OpHelp(""), - /* 6 */ "Checkpoint" OpHelp(""), - /* 7 */ "JournalMode" OpHelp(""), - /* 8 */ "Vacuum" OpHelp(""), - /* 9 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"), - /* 10 */ "VUpdate" OpHelp("data=r[P3@P2]"), - /* 11 */ "Goto" OpHelp(""), - /* 12 */ "Gosub" OpHelp(""), - /* 13 */ "InitCoroutine" OpHelp(""), - /* 14 */ "Yield" OpHelp(""), - /* 15 */ "MustBeInt" OpHelp(""), - /* 16 */ "Jump" OpHelp(""), - /* 17 */ "Once" OpHelp(""), - /* 18 */ "If" OpHelp(""), - /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"), - /* 20 */ "IfNot" OpHelp(""), - /* 21 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"), - /* 22 */ "SeekLT" OpHelp("key=r[P3@P4]"), - /* 23 */ "SeekLE" OpHelp("key=r[P3@P4]"), - /* 24 */ "SeekGE" OpHelp("key=r[P3@P4]"), - /* 25 */ "SeekGT" OpHelp("key=r[P3@P4]"), - /* 26 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"), - /* 27 */ "IfNoHope" OpHelp("key=r[P3@P4]"), - /* 28 */ "NoConflict" OpHelp("key=r[P3@P4]"), - /* 29 */ "NotFound" OpHelp("key=r[P3@P4]"), - /* 30 */ "Found" OpHelp("key=r[P3@P4]"), - /* 31 */ "SeekRowid" OpHelp("intkey=r[P3]"), - /* 32 */ "NotExists" OpHelp("intkey=r[P3]"), - /* 33 */ "Last" OpHelp(""), - /* 34 */ "IfSmaller" OpHelp(""), - /* 35 */ "SorterSort" OpHelp(""), - /* 36 */ "Sort" OpHelp(""), - /* 37 */ "Rewind" OpHelp(""), - /* 38 */ "IdxLE" OpHelp("key=r[P3@P4]"), - /* 39 */ "IdxGT" OpHelp("key=r[P3@P4]"), - /* 40 */ "IdxLT" OpHelp("key=r[P3@P4]"), - /* 41 */ "IdxGE" OpHelp("key=r[P3@P4]"), - /* 42 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), - /* 43 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"), - /* 44 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"), - /* 45 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), - /* 46 */ "Program" OpHelp(""), - /* 47 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), - /* 48 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), - /* 49 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), - /* 50 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), - /* 51 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), - /* 52 */ "Ne" OpHelp("IF r[P3]!=r[P1]"), - /* 53 */ "Eq" OpHelp("IF r[P3]==r[P1]"), - /* 54 */ "Gt" OpHelp("IF r[P3]>r[P1]"), - /* 55 */ "Le" OpHelp("IF r[P3]<=r[P1]"), - /* 56 */ "Lt" OpHelp("IF r[P3]=r[P1]"), - /* 58 */ "ElseNotEq" OpHelp(""), - /* 59 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), - /* 60 */ "IncrVacuum" OpHelp(""), - /* 61 */ "VNext" OpHelp(""), - /* 62 */ "Init" OpHelp("Start at P2"), - /* 63 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"), - /* 64 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"), - /* 65 */ "Return" OpHelp(""), - /* 66 */ "EndCoroutine" OpHelp(""), - /* 67 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), - /* 68 */ "Halt" OpHelp(""), - /* 69 */ "Integer" OpHelp("r[P2]=P1"), - /* 70 */ "Int64" OpHelp("r[P2]=P4"), - /* 71 */ "String" OpHelp("r[P2]='P4' (len=P1)"), - /* 72 */ "Null" OpHelp("r[P2..P3]=NULL"), - /* 73 */ "SoftNull" OpHelp("r[P1]=NULL"), - /* 74 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), - /* 75 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), - /* 76 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), - /* 77 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), - /* 78 */ "SCopy" OpHelp("r[P2]=r[P1]"), - /* 79 */ "IntCopy" OpHelp("r[P2]=r[P1]"), - /* 80 */ "ResultRow" OpHelp("output=r[P1@P2]"), - /* 81 */ "CollSeq" OpHelp(""), - /* 82 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), - /* 83 */ "RealAffinity" OpHelp(""), - /* 84 */ "Cast" OpHelp("affinity(r[P1])"), - /* 85 */ "Permutation" OpHelp(""), - /* 86 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), - /* 87 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), - /* 88 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), - /* 89 */ "Column" OpHelp("r[P3]=PX"), - /* 90 */ "Affinity" OpHelp("affinity(r[P1@P2])"), - /* 91 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), - /* 92 */ "Count" OpHelp("r[P2]=count()"), - /* 93 */ "ReadCookie" OpHelp(""), - /* 94 */ "SetCookie" OpHelp(""), - /* 95 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), - /* 96 */ "OpenRead" OpHelp("root=P2 iDb=P3"), - /* 97 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), - /* 98 */ "OpenDup" OpHelp(""), - /* 99 */ "OpenAutoindex" OpHelp("nColumn=P2"), - /* 100 */ "OpenEphemeral" OpHelp("nColumn=P2"), - /* 101 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), - /* 102 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), - /* 103 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), - /* 105 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), - /* 106 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), - /* 107 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), - /* 108 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), - /* 109 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), - /* 110 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), - /* 111 */ "SorterOpen" OpHelp(""), - /* 112 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), - /* 113 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), - /* 114 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), - /* 115 */ "String8" OpHelp("r[P2]='P4'"), - /* 116 */ "Close" OpHelp(""), - /* 117 */ "ColumnsUsed" OpHelp(""), - /* 118 */ "SeekScan" OpHelp("Scan-ahead up to P1 rows"), - /* 119 */ "SeekHit" OpHelp("set P2<=seekHit<=P3"), - /* 120 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), - /* 121 */ "NewRowid" OpHelp("r[P2]=rowid"), - /* 122 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), - /* 123 */ "Delete" OpHelp(""), - /* 124 */ "ResetCount" OpHelp(""), - /* 125 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), - /* 126 */ "SorterData" OpHelp("r[P2]=data"), - /* 127 */ "RowData" OpHelp("r[P2]=data"), - /* 128 */ "Rowid" OpHelp("r[P2]=rowid"), - /* 129 */ "NullRow" OpHelp(""), - /* 130 */ "SeekEnd" OpHelp(""), - /* 131 */ "IdxInsert" OpHelp("key=r[P2]"), - /* 132 */ "SorterInsert" OpHelp("key=r[P2]"), - /* 133 */ "IdxDelete" OpHelp("key=r[P2@P3]"), - /* 134 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), - /* 135 */ "IdxRowid" OpHelp("r[P2]=rowid"), - /* 136 */ "FinishSeek" OpHelp(""), - /* 137 */ "Destroy" OpHelp(""), - /* 138 */ "Clear" OpHelp(""), - /* 139 */ "ResetSorter" OpHelp(""), - /* 140 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), - /* 141 */ "SqlExec" OpHelp(""), - /* 142 */ "ParseSchema" OpHelp(""), - /* 143 */ "LoadAnalysis" OpHelp(""), - /* 144 */ "DropTable" OpHelp(""), - /* 145 */ "DropIndex" OpHelp(""), - /* 146 */ "DropTrigger" OpHelp(""), - /* 147 */ "IntegrityCk" OpHelp(""), - /* 148 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), - /* 149 */ "Param" OpHelp(""), - /* 150 */ "Real" OpHelp("r[P2]=P4"), - /* 151 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), - /* 152 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), - /* 153 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), - /* 154 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), - /* 155 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 156 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 157 */ "AggValue" OpHelp("r[P3]=value N=P2"), - /* 158 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), - /* 159 */ "Expire" OpHelp(""), - /* 160 */ "CursorLock" OpHelp(""), - /* 161 */ "CursorUnlock" OpHelp(""), - /* 162 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), - /* 163 */ "VBegin" OpHelp(""), - /* 164 */ "VCreate" OpHelp(""), - /* 165 */ "VDestroy" OpHelp(""), - /* 166 */ "VOpen" OpHelp(""), - /* 167 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 168 */ "VRename" OpHelp(""), - /* 169 */ "Pagecount" OpHelp(""), - /* 170 */ "MaxPgcnt" OpHelp(""), - /* 171 */ "Trace" OpHelp(""), - /* 172 */ "CursorHint" OpHelp(""), - /* 173 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), - /* 174 */ "Noop" OpHelp(""), - /* 175 */ "Explain" OpHelp(""), - /* 176 */ "Abortable" OpHelp(""), - }; - return azName[i]; + if( rc==SQLITE_OK ){ + assert( lock.l_type==F_UNLCK || lock.l_type==F_RDLCK ); + rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1); + } + return rc; } -#endif -/************** End of opcodes.c *********************************************/ -/************** Begin file os_unix.c *****************************************/ /* -** 2004 May 22 -** -** 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 the VFS implementation for unix-like operating systems -** include Linux, MacOSX, *BSD, QNX, VxWorks, AIX, HPUX, and others. +** Open a shared-memory area associated with open database file pDbFd. +** This particular implementation uses mmapped files. ** -** There are actually several different VFS implementations in this file. -** The differences are in the way that file locking is done. The default -** implementation uses Posix Advisory Locks. Alternative implementations -** use flock(), dot-files, various proprietary locking schemas, or simply -** skip locking all together. +** The file used to implement shared-memory is in the same directory +** as the open database file and has the same name as the open database +** file with the "-shm" suffix added. For example, if the database file +** is "/home/user1/config.db" then the file that is created and mmapped +** for shared memory will be called "/home/user1/config.db-shm". ** -** This source file is organized into divisions where the logic for various -** subfunctions is contained within the appropriate division. PLEASE -** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed -** in the correct division and should be clearly labeled. +** Another approach to is to use files in /dev/shm or /dev/tmp or an +** some other tmpfs mount. But if a file in a different directory +** from the database file is used, then differing access permissions +** or a chroot() might cause two different processes on the same +** database to end up using different files for shared memory - +** meaning that their memory would not really be shared - resulting +** in database corruption. Nevertheless, this tmpfs file usage +** can be enabled at compile-time using -DSQLITE_SHM_DIRECTORY="/dev/shm" +** or the equivalent. The use of the SQLITE_SHM_DIRECTORY compile-time +** option results in an incompatible build of SQLite; builds of SQLite +** that with differing SQLITE_SHM_DIRECTORY settings attempt to use the +** same database file at the same time, database corruption will likely +** result. The SQLITE_SHM_DIRECTORY compile-time option is considered +** "unsupported" and may go away in a future SQLite release. ** -** The layout of divisions is as follows: +** When opening a new shared-memory file, if no other instances of that +** file are currently open, in this process or in other processes, then +** the file must be truncated to zero length or have its header cleared. ** -** * General-purpose declarations and utility functions. -** * Unique file ID logic used by VxWorks. -** * Various locking primitive implementations (all except proxy locking): -** + for Posix Advisory Locks -** + for no-op locks -** + for dot-file locks -** + for flock() locking -** + for named semaphore locks (VxWorks only) -** + for AFP filesystem locks (MacOSX only) -** * sqlcipher_sqlite3_file methods not associated with locking. -** * Definitions of sqlcipher_sqlite3_io_methods objects for all locking -** methods plus "finder" functions for each locking method. -** * sqlcipher_sqlite3_vfs method implementations. -** * Locking primitives for the proxy uber-locking-method. (MacOSX only) -** * Definitions of sqlcipher_sqlite3_vfs objects for all locking methods -** plus implementations of sqlcipher_sqlite3_os_init() and sqlcipher_sqlite3_os_end(). +** If the original database file (pDbFd) is using the "unix-excl" VFS +** that means that an exclusive lock is held on the database file and +** that no other processes are able to read or write the database. In +** that case, we do not really need shared memory. No shared memory +** file is created. The shared memory will be simulated with heap memory. */ -/* #include "sqliteInt.h" */ -#if SQLITE_OS_UNIX /* This file is used on unix only */ +static int unixOpenSharedMemory(unixFile *pDbFd){ + struct unixShm *p = 0; /* The connection to be opened */ + struct unixShmNode *pShmNode; /* The underlying mmapped file */ + int rc = SQLITE_OK; /* Result code */ + unixInodeInfo *pInode; /* The inode of fd */ + char *zShm; /* Name of the file used for SHM */ + int nShmFilename; /* Size of the SHM filename in bytes */ -/* -** There are various methods for file locking used for concurrency -** control: -** -** 1. POSIX locking (the default), -** 2. No locking, -** 3. Dot-file locking, -** 4. flock() locking, -** 5. AFP locking (OSX only), -** 6. Named POSIX semaphores (VXWorks only), -** 7. proxy locking. (OSX only) -** -** Styles 4, 5, and 7 are only available of SQLITE_ENABLE_LOCKING_STYLE -** is defined to 1. The SQLITE_ENABLE_LOCKING_STYLE also enables automatic -** selection of the appropriate locking style based on the filesystem -** where the database is located. -*/ -#if !defined(SQLITE_ENABLE_LOCKING_STYLE) -# if defined(__APPLE__) -# define SQLITE_ENABLE_LOCKING_STYLE 1 -# else -# define SQLITE_ENABLE_LOCKING_STYLE 0 -# endif + /* Allocate space for the new unixShm object. */ + p = sqlcipher_sqlite3_malloc64( sizeof(*p) ); + if( p==0 ) return SQLITE_NOMEM_BKPT; + memset(p, 0, sizeof(*p)); + assert( pDbFd->pShm==0 ); + + /* Check to see if a unixShmNode object already exists. Reuse an existing + ** one if present. Create a new one if necessary. + */ + assert( unixFileMutexNotheld(pDbFd) ); + unixEnterMutex(); + pInode = pDbFd->pInode; + pShmNode = pInode->pShmNode; + if( pShmNode==0 ){ + struct stat sStat; /* fstat() info for database file */ +#ifndef SQLITE_SHM_DIRECTORY + const char *zBasePath = pDbFd->zPath; #endif -/* Use pread() and pwrite() if they are available */ -#if defined(__APPLE__) -# define HAVE_PREAD 1 -# define HAVE_PWRITE 1 + /* Call fstat() to figure out the permissions on the database file. If + ** a new *-shm file is created, an attempt will be made to create it + ** with the same permissions. + */ + if( osFstat(pDbFd->h, &sStat) ){ + rc = SQLITE_IOERR_FSTAT; + goto shm_open_err; + } + +#ifdef SQLITE_SHM_DIRECTORY + nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31; +#else + nShmFilename = 6 + (int)strlen(zBasePath); #endif -#if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64) -# undef USE_PREAD -# define USE_PREAD64 1 -#elif defined(HAVE_PREAD) && defined(HAVE_PWRITE) -# undef USE_PREAD64 -# define USE_PREAD 1 + pShmNode = sqlcipher_sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename ); + if( pShmNode==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto shm_open_err; + } + memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename); + zShm = pShmNode->zFilename = (char*)&pShmNode[1]; +#ifdef SQLITE_SHM_DIRECTORY + sqlcipher_sqlite3_snprintf(nShmFilename, zShm, + SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x", + (u32)sStat.st_ino, (u32)sStat.st_dev); +#else + sqlcipher_sqlite3_snprintf(nShmFilename, zShm, "%s-shm", zBasePath); + sqlcipher_sqlite3FileSuffix3(pDbFd->zPath, zShm); #endif + pShmNode->hShm = -1; + pDbFd->pInode->pShmNode = pShmNode; + pShmNode->pInode = pDbFd->pInode; + if( sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ + pShmNode->pShmMutex = sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pShmNode->pShmMutex==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto shm_open_err; + } + } -/* -** standard include files. -*/ -#include -#include -#include -#include -/* #include */ -/* #include */ -#include -/* #include */ -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 -/* # include */ + if( pInode->bProcessLock==0 ){ + if( 0==sqlcipher_sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ + pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT|O_NOFOLLOW, + (sStat.st_mode&0777)); + } + if( pShmNode->hShm<0 ){ + pShmNode->hShm = robust_open(zShm, O_RDONLY|O_NOFOLLOW, + (sStat.st_mode&0777)); + if( pShmNode->hShm<0 ){ + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShm); + goto shm_open_err; + } + pShmNode->isReadonly = 1; + } + + /* If this process is running as root, make sure that the SHM file + ** is owned by the same user that owns the original database. Otherwise, + ** the original owner will not be able to connect. + */ + robustFchown(pShmNode->hShm, sStat.st_uid, sStat.st_gid); + + rc = unixLockSharedMemory(pDbFd, pShmNode); + if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err; + } + } + + /* Make the new connection a child of the unixShmNode */ + p->pShmNode = pShmNode; +#ifdef SQLITE_DEBUG + p->id = pShmNode->nextShmId++; #endif + pShmNode->nRef++; + pDbFd->pShm = p; + unixLeaveMutex(); -#if SQLITE_ENABLE_LOCKING_STYLE -/* # include */ -# include -# include -#endif /* SQLITE_ENABLE_LOCKING_STYLE */ + /* The reference count on pShmNode has already been incremented under + ** the cover of the unixEnterMutex() mutex and the pointer from the + ** new (struct unixShm) object to the pShmNode has been set. All that is + ** left to do is to link the new object into the linked list starting + ** at pShmNode->pFirst. This must be done while holding the + ** pShmNode->pShmMutex. + */ + sqlcipher_sqlite3_mutex_enter(pShmNode->pShmMutex); + p->pNext = pShmNode->pFirst; + pShmNode->pFirst = p; + sqlcipher_sqlite3_mutex_leave(pShmNode->pShmMutex); + return rc; + + /* Jump here on any error */ +shm_open_err: + unixShmPurge(pDbFd); /* This call frees pShmNode if required */ + sqlcipher_sqlite3_free(p); + unixLeaveMutex(); + return rc; +} /* -** Try to determine if gethostuuid() is available based on standard -** macros. This might sometimes compute the wrong value for some -** obscure platforms. For those cases, simply compile with one of -** the following: +** This function is called to obtain a pointer to region iRegion of the +** shared-memory associated with the database file fd. Shared-memory regions +** are numbered starting from zero. Each shared-memory region is szRegion +** bytes in size. ** -** -DHAVE_GETHOSTUUID=0 -** -DHAVE_GETHOSTUUID=1 +** If an error occurs, an error code is returned and *pp is set to NULL. ** -** None if this matters except when building on Apple products with -** -DSQLITE_ENABLE_LOCKING_STYLE. +** Otherwise, if the bExtend parameter is 0 and the requested shared-memory +** region has not been allocated (by any client, including one running in a +** separate process), then *pp is set to NULL and SQLITE_OK returned. If +** bExtend is non-zero and the requested shared-memory region has not yet +** been allocated, it is allocated by this function. +** +** If the shared-memory region has already been allocated or is allocated by +** this call as described above, then it is mapped into this processes +** address space (if it is not already), *pp is set to point to the mapped +** memory and SQLITE_OK returned. */ -#ifndef HAVE_GETHOSTUUID -# define HAVE_GETHOSTUUID 0 -# if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \ - (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000)) -# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \ - && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))\ - && (!defined(TARGET_OS_MACCATALYST) || (TARGET_OS_MACCATALYST==0)) -# undef HAVE_GETHOSTUUID -# define HAVE_GETHOSTUUID 1 -# else -# warning "gethostuuid() is disabled." -# endif -# endif -#endif +static int unixShmMap( + sqlcipher_sqlite3_file *fd, /* Handle open on database file */ + int iRegion, /* Region to retrieve */ + int szRegion, /* Size of regions */ + int bExtend, /* True to extend file if necessary */ + void volatile **pp /* OUT: Mapped memory */ +){ + unixFile *pDbFd = (unixFile*)fd; + unixShm *p; + unixShmNode *pShmNode; + int rc = SQLITE_OK; + int nShmPerMap = unixShmRegionPerMap(); + int nReqRegion; + /* If the shared-memory file has not yet been opened, open it now. */ + if( pDbFd->pShm==0 ){ + rc = unixOpenSharedMemory(pDbFd); + if( rc!=SQLITE_OK ) return rc; + } -#if OS_VXWORKS -/* # include */ -# include -# include -#endif /* OS_VXWORKS */ + p = pDbFd->pShm; + pShmNode = p->pShmNode; + sqlcipher_sqlite3_mutex_enter(pShmNode->pShmMutex); + if( pShmNode->isUnlocked ){ + rc = unixLockSharedMemory(pDbFd, pShmNode); + if( rc!=SQLITE_OK ) goto shmpage_out; + pShmNode->isUnlocked = 0; + } + assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); + assert( pShmNode->pInode==pDbFd->pInode ); + assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 ); + assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 ); -#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE -# include -#endif + /* Minimum number of regions required to be mapped. */ + nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap; -#ifdef HAVE_UTIME -# include -#endif + if( pShmNode->nRegionszRegion = szRegion; -/* -** If we are to be thread-safe, include the pthreads header. -*/ -#if SQLITE_THREADSAFE -/* # include */ -#endif + if( pShmNode->hShm>=0 ){ + /* The requested region is not mapped into this processes address space. + ** Check to see if it has been allocated (i.e. if the wal-index file is + ** large enough to contain the requested region). + */ + if( osFstat(pShmNode->hShm, &sStat) ){ + rc = SQLITE_IOERR_SHMSIZE; + goto shmpage_out; + } -/* -** Default permissions when creating a new file -*/ -#ifndef SQLITE_DEFAULT_FILE_PERMISSIONS -# define SQLITE_DEFAULT_FILE_PERMISSIONS 0644 -#endif + if( sStat.st_sizehShm, iPg*pgsz + pgsz-1,"",1,&x)!=1 ){ + const char *zFile = pShmNode->zFilename; + rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile); + goto shmpage_out; + } + } + } + } + } -/* -** Maximum supported symbolic links -*/ -#define SQLITE_MAX_SYMLINKS 100 + /* Map the requested memory region into this processes address space. */ + apNew = (char **)sqlcipher_sqlite3_realloc( + pShmNode->apRegion, nReqRegion*sizeof(char *) + ); + if( !apNew ){ + rc = SQLITE_IOERR_NOMEM_BKPT; + goto shmpage_out; + } + pShmNode->apRegion = apNew; + while( pShmNode->nRegionhShm>=0 ){ + pMem = osMmap(0, nMap, + pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE, + MAP_SHARED, pShmNode->hShm, szRegion*(i64)pShmNode->nRegion + ); + if( pMem==MAP_FAILED ){ + rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename); + goto shmpage_out; + } + }else{ + pMem = sqlcipher_sqlite3_malloc64(nMap); + if( pMem==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto shmpage_out; + } + memset(pMem, 0, nMap); + } -/* Always cast the getpid() return type for compatibility with -** kernel modules in VxWorks. */ -#define osGetpid(X) (pid_t)getpid() + for(i=0; iapRegion[pShmNode->nRegion+i] = &((char*)pMem)[szRegion*i]; + } + pShmNode->nRegion += nShmPerMap; + } + } + +shmpage_out: + if( pShmNode->nRegion>iRegion ){ + *pp = pShmNode->apRegion[iRegion]; + }else{ + *pp = 0; + } + if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; + sqlcipher_sqlite3_mutex_leave(pShmNode->pShmMutex); + return rc; +} /* -** Only set the lastErrno if the error code is a real error and not -** a normal expected return code of SQLITE_BUSY or SQLITE_OK +** Check that the pShmNode->aLock[] array comports with the locking bitmasks +** held by each client. Return true if it does, or false otherwise. This +** is to be used in an assert(). e.g. +** +** assert( assertLockingArrayOk(pShmNode) ); */ -#define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY)) +#ifdef SQLITE_DEBUG +static int assertLockingArrayOk(unixShmNode *pShmNode){ + unixShm *pX; + int aLock[SQLITE_SHM_NLOCK]; + assert( sqlcipher_sqlite3_mutex_held(pShmNode->pShmMutex) ); -/* Forward references */ -typedef struct unixShm unixShm; /* Connection shared memory */ -typedef struct unixShmNode unixShmNode; /* Shared memory instance */ -typedef struct unixInodeInfo unixInodeInfo; /* An i-node */ -typedef struct UnixUnusedFd UnixUnusedFd; /* An unused file descriptor */ + memset(aLock, 0, sizeof(aLock)); + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + int i; + for(i=0; iexclMask & (1<sharedMask & (1<=0 ); + aLock[i]++; + } + } + } -/* -** Sometimes, after a file handle is closed by SQLite, the file descriptor -** cannot be closed immediately. In these cases, instances of the following -** structure are used to store the file descriptor while waiting for an -** opportunity to either close or reuse it. -*/ -struct UnixUnusedFd { - int fd; /* File descriptor to close */ - int flags; /* Flags this file descriptor was opened with */ - UnixUnusedFd *pNext; /* Next unused file descriptor on same file */ -}; + assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) ); + return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0); +} +#endif /* -** The unixFile structure is subclass of sqlcipher_sqlite3_file specific to the unix -** VFS implementations. +** Change the lock state for a shared-memory segment. +** +** Note that the relationship between SHAREd and EXCLUSIVE locks is a little +** different here than in posix. In xShmLock(), one can go from unlocked +** to shared and back or from unlocked to exclusive and back. But one may +** not go from shared to exclusive or from exclusive to shared. */ -typedef struct unixFile unixFile; -struct unixFile { - sqlcipher_sqlite3_io_methods const *pMethod; /* Always the first entry */ - sqlcipher_sqlite3_vfs *pVfs; /* The VFS that created this unixFile */ - unixInodeInfo *pInode; /* Info about locks on this inode */ - int h; /* The file descriptor */ - unsigned char eFileLock; /* The type of lock held on this fd */ - unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ - int lastErrno; /* The unix errno from last I/O error */ - void *lockingContext; /* Locking style specific state */ - UnixUnusedFd *pPreallocatedUnused; /* Pre-allocated UnixUnusedFd */ - const char *zPath; /* Name of the file */ - unixShm *pShm; /* Shared memory segment information */ - int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ -#if SQLITE_MAX_MMAP_SIZE>0 - int nFetchOut; /* Number of outstanding xFetch refs */ - sqlcipher_sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */ - sqlcipher_sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */ - sqlcipher_sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ - void *pMapRegion; /* Memory mapped region */ -#endif - int sectorSize; /* Device sector size */ - int deviceCharacteristics; /* Precomputed device characteristics */ -#if SQLITE_ENABLE_LOCKING_STYLE - int openFlags; /* The flags specified at open() */ -#endif -#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) - unsigned fsFlags; /* cached details from statfs() */ -#endif -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT - unsigned iBusyTimeout; /* Wait this many millisec on locks */ -#endif -#if OS_VXWORKS - struct vxworksFileId *pId; /* Unique file ID */ -#endif -#ifdef SQLITE_DEBUG - /* The next group of variables are used to track whether or not the - ** transaction counter in bytes 24-27 of database files are updated - ** whenever any part of the database changes. An assertion fault will - ** occur if a file is updated without also updating the transaction - ** counter. This test is made to avoid new problems similar to the - ** one described by ticket #3584. - */ - unsigned char transCntrChng; /* True if the transaction counter changed */ - unsigned char dbUpdate; /* True if any part of database file changed */ - unsigned char inNormalWrite; /* True if in a normal write operation */ +static int unixShmLock( + sqlcipher_sqlite3_file *fd, /* Database file holding the shared memory */ + int ofst, /* First lock to acquire or release */ + int n, /* Number of locks to acquire or release */ + int flags /* What to do with the lock */ +){ + unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */ + unixShm *p; /* The shared memory being locked */ + unixShmNode *pShmNode; /* The underlying file iNode */ + int rc = SQLITE_OK; /* Result code */ + u16 mask; /* Mask of locks to take or release */ + int *aLock; -#endif + p = pDbFd->pShm; + if( p==0 ) return SQLITE_IOERR_SHMLOCK; + pShmNode = p->pShmNode; + if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; + aLock = pShmNode->aLock; -#ifdef SQLITE_TEST - /* In test mode, increase the size of this structure a bit so that - ** it is larger than the struct CrashFile defined in test6.c. + assert( pShmNode==pDbFd->pInode->pShmNode ); + assert( pShmNode->pInode==pDbFd->pInode ); + assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); + assert( n>=1 ); + assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); + assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); + assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 ); + assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 ); + + /* Check that, if this to be a blocking lock, no locks that occur later + ** in the following list than the lock being obtained are already held: + ** + ** 1. Checkpointer lock (ofst==1). + ** 2. Write lock (ofst==0). + ** 3. Read locks (ofst>=3 && ofstiBusyTimeout==0 || ( + (ofst!=2) /* not RECOVER */ + && (ofst!=1 || (p->exclMask|p->sharedMask)==0) + && (ofst!=0 || (p->exclMask|p->sharedMask)<3) + && (ofst<3 || (p->exclMask|p->sharedMask)<(1<1 || mask==(1<pShmMutex); + assert( assertLockingArrayOk(pShmNode) ); + if( flags & SQLITE_SHM_UNLOCK ){ + if( (p->exclMask|p->sharedMask) & mask ){ + int ii; + int bUnlock = 1; -/* -** Allowed values for the unixFile.ctrlFlags bitmask: -*/ -#define UNIXFILE_EXCL 0x01 /* Connections from one process only */ -#define UNIXFILE_RDONLY 0x02 /* Connection is read only */ -#define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ -#ifndef SQLITE_DISABLE_DIRSYNC -# define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */ -#else -# define UNIXFILE_DIRSYNC 0x00 -#endif -#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ -#define UNIXFILE_DELETE 0x20 /* Delete on close */ -#define UNIXFILE_URI 0x40 /* Filename might have query parameters */ -#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ + for(ii=ofst; ii((p->sharedMask & (1<sharedMask & (1<1 ); + aLock[ofst]--; + } -/* -** At least two bugs have slipped in because we changed the MEMORY_DEBUG -** macro to SQLITE_DEBUG and some older makefiles have not yet made the -** switch. The following code should catch this problem at compile-time. -*/ -#ifdef MEMORY_DEBUG -# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." -#endif + /* Undo the local locks */ + if( rc==SQLITE_OK ){ + p->exclMask &= ~mask; + p->sharedMask &= ~mask; + } + } + }else if( flags & SQLITE_SHM_SHARED ){ + assert( n==1 ); + assert( (p->exclMask & (1<sharedMask & mask)==0 ){ + if( aLock[ofst]<0 ){ + rc = SQLITE_BUSY; + }else if( aLock[ofst]==0 ){ + rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); + } + + /* Get the local shared locks */ + if( rc==SQLITE_OK ){ + p->sharedMask |= mask; + aLock[ofst]++; + } + } + }else{ + /* Make sure no sibling connections hold locks that will block this + ** lock. If any do, return SQLITE_BUSY right away. */ + int ii; + for(ii=ofst; iisharedMask & mask)==0 ); + if( ALWAYS((p->exclMask & (1<sharedMask & mask)==0 ); + p->exclMask |= mask; + for(ii=ofst; iipShmMutex); + OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", + p->id, osGetpid(0), p->sharedMask, p->exclMask)); + return rc; +} /* -** Macros for performance tracing. Normally turned off. Only works -** on i486 hardware. +** Implement a memory barrier or memory fence on shared memory. +** +** All loads and stores begun before the barrier must complete before +** any load or store begun after the barrier. */ -#ifdef SQLITE_PERFORMANCE_TRACE +static void unixShmBarrier( + sqlcipher_sqlite3_file *fd /* Database file holding the shared memory */ +){ + UNUSED_PARAMETER(fd); + sqlcipher_sqlite3MemoryBarrier(); /* compiler-defined memory barrier */ + assert( fd->pMethods->xLock==nolockLock + || unixFileMutexNotheld((unixFile*)fd) + ); + unixEnterMutex(); /* Also mutex, for redundancy */ + unixLeaveMutex(); +} /* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of os_common.h ****************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** 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. -** -****************************************************************************** +** Close a connection to shared-memory. Delete the underlying +** storage if deleteFlag is true. ** -** This file contains inline asm code for retrieving "high-performance" -** counters for x86 and x86_64 class CPUs. +** If there is no shared memory associated with the connection then this +** routine is a harmless no-op. */ -#ifndef SQLITE_HWTIME_H -#define SQLITE_HWTIME_H +static int unixShmUnmap( + sqlcipher_sqlite3_file *fd, /* The underlying database file */ + int deleteFlag /* Delete shared-memory if true */ +){ + unixShm *p; /* The connection to be closed */ + unixShmNode *pShmNode; /* The underlying shared-memory file */ + unixShm **pp; /* For looping over sibling connections */ + unixFile *pDbFd; /* The underlying database file */ -/* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if !defined(__STRICT_ANSI__) && \ - (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) + pDbFd = (unixFile*)fd; + p = pDbFd->pShm; + if( p==0 ) return SQLITE_OK; + pShmNode = p->pShmNode; - #if defined(__GNUC__) + assert( pShmNode==pDbFd->pInode->pShmNode ); + assert( pShmNode->pInode==pDbFd->pInode ); - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } + /* Remove connection p from the set of connections associated + ** with pShmNode */ + sqlcipher_sqlite3_mutex_enter(pShmNode->pShmMutex); + for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} + *pp = p->pNext; - #elif defined(_MSC_VER) + /* Free the connection p */ + sqlcipher_sqlite3_free(p); + pDbFd->pShm = 0; + sqlcipher_sqlite3_mutex_leave(pShmNode->pShmMutex); - __declspec(naked) __inline sqlite_uint64 __cdecl sqlcipher_sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } + /* If pShmNode->nRef has reached 0, then close the underlying + ** shared-memory file, too */ + assert( unixFileMutexNotheld(pDbFd) ); + unixEnterMutex(); + assert( pShmNode->nRef>0 ); + pShmNode->nRef--; + if( pShmNode->nRef==0 ){ + if( deleteFlag && pShmNode->hShm>=0 ){ + osUnlink(pShmNode->zFilename); + } + unixShmPurge(pDbFd); } + unixLeaveMutex(); - #endif - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) + return SQLITE_OK; +} - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; - } -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) +#else +# define unixShmMap 0 +# define unixShmLock 0 +# define unixShmBarrier 0 +# define unixShmUnmap 0 +#endif /* #ifndef SQLITE_OMIT_WAL */ - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; +#if SQLITE_MAX_MMAP_SIZE>0 +/* +** If it is currently memory mapped, unmap file pFd. +*/ +static void unixUnmapfile(unixFile *pFd){ + assert( pFd->nFetchOut==0 ); + if( pFd->pMapRegion ){ + osMunmap(pFd->pMapRegion, pFd->mmapSizeActual); + pFd->pMapRegion = 0; + pFd->mmapSize = 0; + pFd->mmapSizeActual = 0; } +} -#else +/* +** Attempt to set the size of the memory mapping maintained by file +** descriptor pFd to nNew bytes. Any existing mapping is discarded. +** +** If successful, this function sets the following variables: +** +** unixFile.pMapRegion +** unixFile.mmapSize +** unixFile.mmapSizeActual +** +** If unsuccessful, an error message is logged via sqlcipher_sqlite3_log() and +** the three variables above are zeroed. In this case SQLite should +** continue accessing the database using the xRead() and xWrite() +** methods. +*/ +static void unixRemapfile( + unixFile *pFd, /* File descriptor object */ + i64 nNew /* Required mapping size */ +){ + const char *zErr = "mmap"; + int h = pFd->h; /* File descriptor open on db file */ + u8 *pOrig = (u8 *)pFd->pMapRegion; /* Pointer to current file mapping */ + i64 nOrig = pFd->mmapSizeActual; /* Size of pOrig region in bytes */ + u8 *pNew = 0; /* Location of new mapping */ + int flags = PROT_READ; /* Flags to pass to mmap() */ - /* - ** asm() is needed for hardware timing support. Without asm(), - ** disable the sqlcipher_sqlite3Hwtime() routine. - ** - ** sqlcipher_sqlite3Hwtime() is only used for some obscure debugging - ** and analysis configurations, not in any deliverable, so this - ** should not be a great loss. - */ -SQLITE_PRIVATE sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ return ((sqlite_uint64)0); } + assert( pFd->nFetchOut==0 ); + assert( nNew>pFd->mmapSize ); + assert( nNew<=pFd->mmapSizeMax ); + assert( nNew>0 ); + assert( pFd->mmapSizeActual>=pFd->mmapSize ); + assert( MAP_FAILED!=0 ); +#ifdef SQLITE_MMAP_READWRITE + if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; #endif -#endif /* !defined(SQLITE_HWTIME_H) */ + if( pOrig ){ +#if HAVE_MREMAP + i64 nReuse = pFd->mmapSize; +#else + const int szSyspage = osGetpagesize(); + i64 nReuse = (pFd->mmapSize & ~(szSyspage-1)); +#endif + u8 *pReq = &pOrig[nReuse]; -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in os_common.h ******************/ + /* Unmap any pages of the existing mapping that cannot be reused. */ + if( nReuse!=nOrig ){ + osMunmap(pReq, nOrig-nReuse); + } -static sqlite_uint64 g_start; -static sqlite_uint64 g_elapsed; -#define TIMER_START g_start=sqlcipher_sqlite3Hwtime() -#define TIMER_END g_elapsed=sqlcipher_sqlite3Hwtime()-g_start -#define TIMER_ELAPSED g_elapsed +#if HAVE_MREMAP + pNew = osMremap(pOrig, nReuse, nNew, MREMAP_MAYMOVE); + zErr = "mremap"; #else -#define TIMER_START -#define TIMER_END -#define TIMER_ELAPSED ((sqlite_uint64)0) + pNew = osMmap(pReq, nNew-nReuse, flags, MAP_SHARED, h, nReuse); + if( pNew!=MAP_FAILED ){ + if( pNew!=pReq ){ + osMunmap(pNew, nNew - nReuse); + pNew = 0; + }else{ + pNew = pOrig; + } + } #endif -/* -** If we compile with the SQLITE_TEST macro set, then the following block -** of code will give us the ability to simulate a disk I/O error. This -** is used for testing the I/O recovery logic. -*/ -#if defined(SQLITE_TEST) -SQLITE_API extern int sqlcipher_sqlite3_io_error_hit; -SQLITE_API extern int sqlcipher_sqlite3_io_error_hardhit; -SQLITE_API extern int sqlcipher_sqlite3_io_error_pending; -SQLITE_API extern int sqlcipher_sqlite3_io_error_persist; -SQLITE_API extern int sqlcipher_sqlite3_io_error_benign; -SQLITE_API extern int sqlcipher_sqlite3_diskfull_pending; -SQLITE_API extern int sqlcipher_sqlite3_diskfull; -#define SimulateIOErrorBenign(X) sqlcipher_sqlite3_io_error_benign=(X) -#define SimulateIOError(CODE) \ - if( (sqlcipher_sqlite3_io_error_persist && sqlcipher_sqlite3_io_error_hit) \ - || sqlcipher_sqlite3_io_error_pending-- == 1 ) \ - { local_ioerr(); CODE; } -static void local_ioerr(){ - IOTRACE(("IOERR\n")); - sqlcipher_sqlite3_io_error_hit++; - if( !sqlcipher_sqlite3_io_error_benign ) sqlcipher_sqlite3_io_error_hardhit++; + /* The attempt to extend the existing mapping failed. Free it. */ + if( pNew==MAP_FAILED || pNew==0 ){ + osMunmap(pOrig, nReuse); + } + } + + /* If pNew is still NULL, try to create an entirely new mapping. */ + if( pNew==0 ){ + pNew = osMmap(0, nNew, flags, MAP_SHARED, h, 0); + } + + if( pNew==MAP_FAILED ){ + pNew = 0; + nNew = 0; + unixLogError(SQLITE_OK, zErr, pFd->zPath); + + /* If the mmap() above failed, assume that all subsequent mmap() calls + ** will probably fail too. Fall back to using xRead/xWrite exclusively + ** in this case. */ + pFd->mmapSizeMax = 0; + } + pFd->pMapRegion = (void *)pNew; + pFd->mmapSize = pFd->mmapSizeActual = nNew; } -#define SimulateDiskfullError(CODE) \ - if( sqlcipher_sqlite3_diskfull_pending ){ \ - if( sqlcipher_sqlite3_diskfull_pending == 1 ){ \ - local_ioerr(); \ - sqlcipher_sqlite3_diskfull = 1; \ - sqlcipher_sqlite3_io_error_hit = 1; \ - CODE; \ - }else{ \ - sqlcipher_sqlite3_diskfull_pending--; \ - } \ - } -#else -#define SimulateIOErrorBenign(X) -#define SimulateIOError(A) -#define SimulateDiskfullError(A) -#endif /* defined(SQLITE_TEST) */ /* -** When testing, keep a count of the number of open files. +** Memory map or remap the file opened by file-descriptor pFd (if the file +** is already mapped, the existing mapping is replaced by the new). Or, if +** there already exists a mapping for this file, and there are still +** outstanding xFetch() references to it, this function is a no-op. +** +** If parameter nByte is non-negative, then it is the requested size of +** the mapping to create. Otherwise, if nByte is less than zero, then the +** requested size is the size of the file on disk. The actual size of the +** created mapping is either the requested size or the value configured +** using SQLITE_FCNTL_MMAP_LIMIT, whichever is smaller. +** +** SQLITE_OK is returned if no error occurs (even if the mapping is not +** recreated as a result of outstanding references) or an SQLite error +** code otherwise. */ -#if defined(SQLITE_TEST) -SQLITE_API extern int sqlcipher_sqlite3_open_file_count; -#define OpenCounter(X) sqlcipher_sqlite3_open_file_count+=(X) -#else -#define OpenCounter(X) -#endif /* defined(SQLITE_TEST) */ +static int unixMapfile(unixFile *pFd, i64 nMap){ + assert( nMap>=0 || pFd->nFetchOut==0 ); + assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) ); + if( pFd->nFetchOut>0 ) return SQLITE_OK; -#endif /* !defined(_OS_COMMON_H_) */ + if( nMap<0 ){ + struct stat statbuf; /* Low-level file information */ + if( osFstat(pFd->h, &statbuf) ){ + return SQLITE_IOERR_FSTAT; + } + nMap = statbuf.st_size; + } + if( nMap>pFd->mmapSizeMax ){ + nMap = pFd->mmapSizeMax; + } -/************** End of os_common.h *******************************************/ -/************** Continuing where we left off in os_unix.c ********************/ + assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) ); + if( nMap!=pFd->mmapSize ){ + unixRemapfile(pFd, nMap); + } -/* -** Define various macros that are missing from some systems. -*/ -#ifndef O_LARGEFILE -# define O_LARGEFILE 0 -#endif -#ifdef SQLITE_DISABLE_LFS -# undef O_LARGEFILE -# define O_LARGEFILE 0 -#endif -#ifndef O_NOFOLLOW -# define O_NOFOLLOW 0 -#endif -#ifndef O_BINARY -# define O_BINARY 0 -#endif + return SQLITE_OK; +} +#endif /* SQLITE_MAX_MMAP_SIZE>0 */ /* -** The threadid macro resolves to the thread-id or to 0. Used for -** testing and debugging only. +** If possible, return a pointer to a mapping of file fd starting at offset +** iOff. The mapping must be valid for at least nAmt bytes. +** +** If such a pointer can be obtained, store it in *pp and return SQLITE_OK. +** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK. +** Finally, if an error does occur, return an SQLite error code. The final +** value of *pp is undefined in this case. +** +** If this function does return a pointer, the caller must eventually +** release the reference by calling unixUnfetch(). */ -#if SQLITE_THREADSAFE -#define threadid pthread_self() -#else -#define threadid 0 +static int unixFetch(sqlcipher_sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ +#if SQLITE_MAX_MMAP_SIZE>0 + unixFile *pFd = (unixFile *)fd; /* The underlying database file */ #endif + *pp = 0; -/* -** HAVE_MREMAP defaults to true on Linux and false everywhere else. -*/ -#if !defined(HAVE_MREMAP) -# if defined(__linux__) && defined(_GNU_SOURCE) -# define HAVE_MREMAP 1 -# else -# define HAVE_MREMAP 0 -# endif +#if SQLITE_MAX_MMAP_SIZE>0 + if( pFd->mmapSizeMax>0 ){ + if( pFd->pMapRegion==0 ){ + int rc = unixMapfile(pFd, -1); + if( rc!=SQLITE_OK ) return rc; + } + if( pFd->mmapSize >= iOff+nAmt ){ + *pp = &((u8 *)pFd->pMapRegion)[iOff]; + pFd->nFetchOut++; + } + } #endif + return SQLITE_OK; +} /* -** Explicitly call the 64-bit version of lseek() on Android. Otherwise, lseek() -** is the 32-bit version, even if _FILE_OFFSET_BITS=64 is defined. +** If the third argument is non-NULL, then this function releases a +** reference obtained by an earlier call to unixFetch(). The second +** argument passed to this function must be the same as the corresponding +** argument that was passed to the unixFetch() invocation. +** +** Or, if the third argument is NULL, then this function is being called +** to inform the VFS layer that, according to POSIX, any existing mapping +** may now be invalid and should be unmapped. */ -#ifdef __ANDROID__ -# define lseek lseek64 +static int unixUnfetch(sqlcipher_sqlite3_file *fd, i64 iOff, void *p){ +#if SQLITE_MAX_MMAP_SIZE>0 + unixFile *pFd = (unixFile *)fd; /* The underlying database file */ + UNUSED_PARAMETER(iOff); + + /* If p==0 (unmap the entire file) then there must be no outstanding + ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), + ** then there must be at least one outstanding. */ + assert( (p==0)==(pFd->nFetchOut==0) ); + + /* If p!=0, it must match the iOff value. */ + assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] ); + + if( p ){ + pFd->nFetchOut--; + }else{ + unixUnmapfile(pFd); + } + + assert( pFd->nFetchOut>=0 ); +#else + UNUSED_PARAMETER(fd); + UNUSED_PARAMETER(p); + UNUSED_PARAMETER(iOff); #endif + return SQLITE_OK; +} -#ifdef __linux__ /* -** Linux-specific IOCTL magic numbers used for controlling F2FS -*/ -#define F2FS_IOCTL_MAGIC 0xf5 -#define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1) -#define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2) -#define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3) -#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5) -#define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, u32) -#define F2FS_FEATURE_ATOMIC_WRITE 0x0004 -#endif /* __linux__ */ - +** Here ends the implementation of all sqlcipher_sqlite3_file methods. +** +********************** End sqlcipher_sqlite3_file Methods ******************************* +******************************************************************************/ /* -** Different Unix systems declare open() in different ways. Same use -** open(const char*,int,mode_t). Others use open(const char*,int,...). -** The difference is important when using a pointer to the function. +** This division contains definitions of sqlcipher_sqlite3_io_methods objects that +** implement various file locking strategies. It also contains definitions +** of "finder" functions. A finder-function is used to locate the appropriate +** sqlcipher_sqlite3_io_methods object for a particular database file. The pAppData +** field of the sqlcipher_sqlite3_vfs VFS objects are initialized to be pointers to +** the correct finder-function for that VFS. ** -** The safest way to deal with the problem is to always use this wrapper -** which always has the same well-defined interface. +** Most finder functions return a pointer to a fixed sqlcipher_sqlite3_io_methods +** object. The only interesting finder-function is autolockIoFinder, which +** looks at the filesystem type and tries to guess the best locking +** strategy from that. +** +** For finder-function F, two objects are created: +** +** (1) The real finder-function named "FImpt()". +** +** (2) A constant pointer to this function named just "F". +** +** +** A pointer to the F pointer is used as the pAppData value for VFS +** objects. We have to do this instead of letting pAppData point +** directly at the finder-function since C90 rules prevent a void* +** from be cast into a function pointer. +** +** +** Each instance of this macro generates two objects: +** +** * A constant sqlcipher_sqlite3_io_methods object call METHOD that has locking +** methods CLOSE, LOCK, UNLOCK, CKRESLOCK. +** +** * An I/O method finder function called FINDER that returns a pointer +** to the METHOD object in the previous bullet. */ -static int posixOpen(const char *zFile, int flags, int mode){ - return open(zFile, flags, mode); -} - -/* Forward reference */ -static int openDirectory(const char*, int*); -static int unixGetpagesize(void); +#define IOMETHODS(FINDER,METHOD,VERSION,CLOSE,LOCK,UNLOCK,CKLOCK,SHMMAP) \ +static const sqlcipher_sqlite3_io_methods METHOD = { \ + VERSION, /* iVersion */ \ + CLOSE, /* xClose */ \ + unixRead, /* xRead */ \ + unixWrite, /* xWrite */ \ + unixTruncate, /* xTruncate */ \ + unixSync, /* xSync */ \ + unixFileSize, /* xFileSize */ \ + LOCK, /* xLock */ \ + UNLOCK, /* xUnlock */ \ + CKLOCK, /* xCheckReservedLock */ \ + unixFileControl, /* xFileControl */ \ + unixSectorSize, /* xSectorSize */ \ + unixDeviceCharacteristics, /* xDeviceCapabilities */ \ + SHMMAP, /* xShmMap */ \ + unixShmLock, /* xShmLock */ \ + unixShmBarrier, /* xShmBarrier */ \ + unixShmUnmap, /* xShmUnmap */ \ + unixFetch, /* xFetch */ \ + unixUnfetch, /* xUnfetch */ \ +}; \ +static const sqlcipher_sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ + UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ + return &METHOD; \ +} \ +static const sqlcipher_sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ + = FINDER##Impl; /* -** Many system calls are accessed through pointer-to-functions so that -** they may be overridden at runtime to facilitate fault injection during -** testing and sandboxing. The following array holds the names and pointers -** to all overrideable system calls. +** Here are all of the sqlcipher_sqlite3_io_methods objects for each of the +** locking strategies. Functions that return pointers to these methods +** are also created. */ -static struct unix_syscall { - const char *zName; /* Name of the system call */ - sqlcipher_sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ - sqlcipher_sqlite3_syscall_ptr pDefault; /* Default value */ -} aSyscall[] = { - { "open", (sqlcipher_sqlite3_syscall_ptr)posixOpen, 0 }, -#define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent) - - { "close", (sqlcipher_sqlite3_syscall_ptr)close, 0 }, -#define osClose ((int(*)(int))aSyscall[1].pCurrent) +IOMETHODS( + posixIoFinder, /* Finder function name */ + posixIoMethods, /* sqlcipher_sqlite3_io_methods object name */ + 3, /* shared memory and mmap are enabled */ + unixClose, /* xClose method */ + unixLock, /* xLock method */ + unixUnlock, /* xUnlock method */ + unixCheckReservedLock, /* xCheckReservedLock method */ + unixShmMap /* xShmMap method */ +) +IOMETHODS( + nolockIoFinder, /* Finder function name */ + nolockIoMethods, /* sqlcipher_sqlite3_io_methods object name */ + 3, /* shared memory and mmap are enabled */ + nolockClose, /* xClose method */ + nolockLock, /* xLock method */ + nolockUnlock, /* xUnlock method */ + nolockCheckReservedLock, /* xCheckReservedLock method */ + 0 /* xShmMap method */ +) +IOMETHODS( + dotlockIoFinder, /* Finder function name */ + dotlockIoMethods, /* sqlcipher_sqlite3_io_methods object name */ + 1, /* shared memory is disabled */ + dotlockClose, /* xClose method */ + dotlockLock, /* xLock method */ + dotlockUnlock, /* xUnlock method */ + dotlockCheckReservedLock, /* xCheckReservedLock method */ + 0 /* xShmMap method */ +) - { "access", (sqlcipher_sqlite3_syscall_ptr)access, 0 }, -#define osAccess ((int(*)(const char*,int))aSyscall[2].pCurrent) +#if SQLITE_ENABLE_LOCKING_STYLE +IOMETHODS( + flockIoFinder, /* Finder function name */ + flockIoMethods, /* sqlcipher_sqlite3_io_methods object name */ + 1, /* shared memory is disabled */ + flockClose, /* xClose method */ + flockLock, /* xLock method */ + flockUnlock, /* xUnlock method */ + flockCheckReservedLock, /* xCheckReservedLock method */ + 0 /* xShmMap method */ +) +#endif - { "getcwd", (sqlcipher_sqlite3_syscall_ptr)getcwd, 0 }, -#define osGetcwd ((char*(*)(char*,size_t))aSyscall[3].pCurrent) +#if OS_VXWORKS +IOMETHODS( + semIoFinder, /* Finder function name */ + semIoMethods, /* sqlcipher_sqlite3_io_methods object name */ + 1, /* shared memory is disabled */ + semXClose, /* xClose method */ + semXLock, /* xLock method */ + semXUnlock, /* xUnlock method */ + semXCheckReservedLock, /* xCheckReservedLock method */ + 0 /* xShmMap method */ +) +#endif - { "stat", (sqlcipher_sqlite3_syscall_ptr)stat, 0 }, -#define osStat ((int(*)(const char*,struct stat*))aSyscall[4].pCurrent) +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE +IOMETHODS( + afpIoFinder, /* Finder function name */ + afpIoMethods, /* sqlcipher_sqlite3_io_methods object name */ + 1, /* shared memory is disabled */ + afpClose, /* xClose method */ + afpLock, /* xLock method */ + afpUnlock, /* xUnlock method */ + afpCheckReservedLock, /* xCheckReservedLock method */ + 0 /* xShmMap method */ +) +#endif /* -** The DJGPP compiler environment looks mostly like Unix, but it -** lacks the fcntl() system call. So redefine fcntl() to be something -** that always succeeds. This means that locking does not occur under -** DJGPP. But it is DOS - what did you expect? +** The proxy locking method is a "super-method" in the sense that it +** opens secondary file descriptors for the conch and lock files and +** it uses proxy, dot-file, AFP, and flock() locking methods on those +** secondary files. For this reason, the division that implements +** proxy locking is located much further down in the file. But we need +** to go ahead and define the sqlcipher_sqlite3_io_methods and finder function +** for proxy locking here. So we forward declare the I/O methods. */ -#ifdef __DJGPP__ - { "fstat", 0, 0 }, -#define osFstat(a,b,c) 0 -#else - { "fstat", (sqlcipher_sqlite3_syscall_ptr)fstat, 0 }, -#define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent) -#endif - - { "ftruncate", (sqlcipher_sqlite3_syscall_ptr)ftruncate, 0 }, -#define osFtruncate ((int(*)(int,off_t))aSyscall[6].pCurrent) - - { "fcntl", (sqlcipher_sqlite3_syscall_ptr)fcntl, 0 }, -#define osFcntl ((int(*)(int,int,...))aSyscall[7].pCurrent) - - { "read", (sqlcipher_sqlite3_syscall_ptr)read, 0 }, -#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) - -#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE - { "pread", (sqlcipher_sqlite3_syscall_ptr)pread, 0 }, -#else - { "pread", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE +static int proxyClose(sqlcipher_sqlite3_file*); +static int proxyLock(sqlcipher_sqlite3_file*, int); +static int proxyUnlock(sqlcipher_sqlite3_file*, int); +static int proxyCheckReservedLock(sqlcipher_sqlite3_file*, int*); +IOMETHODS( + proxyIoFinder, /* Finder function name */ + proxyIoMethods, /* sqlcipher_sqlite3_io_methods object name */ + 1, /* shared memory is disabled */ + proxyClose, /* xClose method */ + proxyLock, /* xLock method */ + proxyUnlock, /* xUnlock method */ + proxyCheckReservedLock, /* xCheckReservedLock method */ + 0 /* xShmMap method */ +) #endif -#define osPread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].pCurrent) -#if defined(USE_PREAD64) - { "pread64", (sqlcipher_sqlite3_syscall_ptr)pread64, 0 }, -#else - { "pread64", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +/* nfs lockd on OSX 10.3+ doesn't clear write locks when a read lock is set */ +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE +IOMETHODS( + nfsIoFinder, /* Finder function name */ + nfsIoMethods, /* sqlcipher_sqlite3_io_methods object name */ + 1, /* shared memory is disabled */ + unixClose, /* xClose method */ + unixLock, /* xLock method */ + nfsUnlock, /* xUnlock method */ + unixCheckReservedLock, /* xCheckReservedLock method */ + 0 /* xShmMap method */ +) #endif -#define osPread64 ((ssize_t(*)(int,void*,size_t,off64_t))aSyscall[10].pCurrent) - { "write", (sqlcipher_sqlite3_syscall_ptr)write, 0 }, -#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE +/* +** This "finder" function attempts to determine the best locking strategy +** for the database file "filePath". It then returns the sqlcipher_sqlite3_io_methods +** object that implements that strategy. +** +** This is for MacOSX only. +*/ +static const sqlcipher_sqlite3_io_methods *autolockIoFinderImpl( + const char *filePath, /* name of the database file */ + unixFile *pNew /* open file object for the database file */ +){ + static const struct Mapping { + const char *zFilesystem; /* Filesystem type name */ + const sqlcipher_sqlite3_io_methods *pMethods; /* Appropriate locking method */ + } aMap[] = { + { "hfs", &posixIoMethods }, + { "ufs", &posixIoMethods }, + { "afpfs", &afpIoMethods }, + { "smbfs", &afpIoMethods }, + { "webdav", &nolockIoMethods }, + { 0, 0 } + }; + int i; + struct statfs fsInfo; + struct flock lockInfo; -#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE - { "pwrite", (sqlcipher_sqlite3_syscall_ptr)pwrite, 0 }, -#else - { "pwrite", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, -#endif -#define osPwrite ((ssize_t(*)(int,const void*,size_t,off_t))\ - aSyscall[12].pCurrent) + if( !filePath ){ + /* If filePath==NULL that means we are dealing with a transient file + ** that does not need to be locked. */ + return &nolockIoMethods; + } + if( statfs(filePath, &fsInfo) != -1 ){ + if( fsInfo.f_flags & MNT_RDONLY ){ + return &nolockIoMethods; + } + for(i=0; aMap[i].zFilesystem; i++){ + if( strcmp(fsInfo.f_fstypename, aMap[i].zFilesystem)==0 ){ + return aMap[i].pMethods; + } + } + } -#if defined(USE_PREAD64) - { "pwrite64", (sqlcipher_sqlite3_syscall_ptr)pwrite64, 0 }, -#else - { "pwrite64", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, -#endif -#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\ - aSyscall[13].pCurrent) + /* Default case. Handles, amongst others, "nfs". + ** Test byte-range lock using fcntl(). If the call succeeds, + ** assume that the file-system supports POSIX style locks. + */ + lockInfo.l_len = 1; + lockInfo.l_start = 0; + lockInfo.l_whence = SEEK_SET; + lockInfo.l_type = F_RDLCK; + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){ + return &nfsIoMethods; + } else { + return &posixIoMethods; + } + }else{ + return &dotlockIoMethods; + } +} +static const sqlcipher_sqlite3_io_methods + *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; - { "fchmod", (sqlcipher_sqlite3_syscall_ptr)fchmod, 0 }, -#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ -#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE - { "fallocate", (sqlcipher_sqlite3_syscall_ptr)posix_fallocate, 0 }, -#else - { "fallocate", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, -#endif -#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent) +#if OS_VXWORKS +/* +** This "finder" function for VxWorks checks to see if posix advisory +** locking works. If it does, then that is what is used. If it does not +** work, then fallback to named semaphore locking. +*/ +static const sqlcipher_sqlite3_io_methods *vxworksIoFinderImpl( + const char *filePath, /* name of the database file */ + unixFile *pNew /* the open file object */ +){ + struct flock lockInfo; - { "unlink", (sqlcipher_sqlite3_syscall_ptr)unlink, 0 }, -#define osUnlink ((int(*)(const char*))aSyscall[16].pCurrent) + if( !filePath ){ + /* If filePath==NULL that means we are dealing with a transient file + ** that does not need to be locked. */ + return &nolockIoMethods; + } - { "openDirectory", (sqlcipher_sqlite3_syscall_ptr)openDirectory, 0 }, -#define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent) + /* Test if fcntl() is supported and use POSIX style locks. + ** Otherwise fall back to the named semaphore method. + */ + lockInfo.l_len = 1; + lockInfo.l_start = 0; + lockInfo.l_whence = SEEK_SET; + lockInfo.l_type = F_RDLCK; + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + return &posixIoMethods; + }else{ + return &semIoMethods; + } +} +static const sqlcipher_sqlite3_io_methods + *(*const vxworksIoFinder)(const char*,unixFile*) = vxworksIoFinderImpl; - { "mkdir", (sqlcipher_sqlite3_syscall_ptr)mkdir, 0 }, -#define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent) +#endif /* OS_VXWORKS */ - { "rmdir", (sqlcipher_sqlite3_syscall_ptr)rmdir, 0 }, -#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) +/* +** An abstract type for a pointer to an IO method finder function: +*/ +typedef const sqlcipher_sqlite3_io_methods *(*finder_type)(const char*,unixFile*); -#if defined(HAVE_FCHOWN) - { "fchown", (sqlcipher_sqlite3_syscall_ptr)fchown, 0 }, -#else - { "fchown", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, -#endif -#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) -#if defined(HAVE_FCHOWN) - { "geteuid", (sqlcipher_sqlite3_syscall_ptr)geteuid, 0 }, -#else - { "geteuid", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, -#endif -#define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) +/**************************************************************************** +**************************** sqlcipher_sqlite3_vfs methods **************************** +** +** This division contains the implementation of methods on the +** sqlcipher_sqlite3_vfs object. +*/ -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 - { "mmap", (sqlcipher_sqlite3_syscall_ptr)mmap, 0 }, -#else - { "mmap", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, -#endif -#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent) +/* +** Initialize the contents of the unixFile structure pointed to by pId. +*/ +static int fillInUnixFile( + sqlcipher_sqlite3_vfs *pVfs, /* Pointer to vfs object */ + int h, /* Open file descriptor of file being opened */ + sqlcipher_sqlite3_file *pId, /* Write to the unixFile structure here */ + const char *zFilename, /* Name of the file being opened */ + int ctrlFlags /* Zero or more UNIXFILE_* values */ +){ + const sqlcipher_sqlite3_io_methods *pLockingStyle; + unixFile *pNew = (unixFile *)pId; + int rc = SQLITE_OK; -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 - { "munmap", (sqlcipher_sqlite3_syscall_ptr)munmap, 0 }, -#else - { "munmap", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, -#endif -#define osMunmap ((int(*)(void*,size_t))aSyscall[23].pCurrent) + assert( pNew->pInode==NULL ); -#if HAVE_MREMAP && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) - { "mremap", (sqlcipher_sqlite3_syscall_ptr)mremap, 0 }, -#else - { "mremap", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, -#endif -#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[24].pCurrent) + /* No locking occurs in temporary files */ + assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 ); -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 - { "getpagesize", (sqlcipher_sqlite3_syscall_ptr)unixGetpagesize, 0 }, -#else - { "getpagesize", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, + OSTRACE(("OPEN %-3d %s\n", h, zFilename)); + pNew->h = h; + pNew->pVfs = pVfs; + pNew->zPath = zFilename; + pNew->ctrlFlags = (u8)ctrlFlags; +#if SQLITE_MAX_MMAP_SIZE>0 + pNew->mmapSizeMax = sqlcipher_sqlite3GlobalConfig.szMmap; #endif -#define osGetpagesize ((int(*)(void))aSyscall[25].pCurrent) + if( sqlcipher_sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), + "psow", SQLITE_POWERSAFE_OVERWRITE) ){ + pNew->ctrlFlags |= UNIXFILE_PSOW; + } + if( strcmp(pVfs->zName,"unix-excl")==0 ){ + pNew->ctrlFlags |= UNIXFILE_EXCL; + } -#if defined(HAVE_READLINK) - { "readlink", (sqlcipher_sqlite3_syscall_ptr)readlink, 0 }, -#else - { "readlink", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, +#if OS_VXWORKS + pNew->pId = vxworksFindFileId(zFilename); + if( pNew->pId==0 ){ + ctrlFlags |= UNIXFILE_NOLOCK; + rc = SQLITE_NOMEM_BKPT; + } #endif -#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[26].pCurrent) -#if defined(HAVE_LSTAT) - { "lstat", (sqlcipher_sqlite3_syscall_ptr)lstat, 0 }, -#else - { "lstat", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, + if( ctrlFlags & UNIXFILE_NOLOCK ){ + pLockingStyle = &nolockIoMethods; + }else{ + pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew); +#if SQLITE_ENABLE_LOCKING_STYLE + /* Cache zFilename in the locking context (AFP and dotlock override) for + ** proxyLock activation is possible (remote proxy is based on db name) + ** zFilename remains valid until file is closed, to support */ + pNew->lockingContext = (void*)zFilename; #endif -#define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent) + } -#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) -# ifdef __ANDROID__ - { "ioctl", (sqlcipher_sqlite3_syscall_ptr)(int(*)(int, int, ...))ioctl, 0 }, -#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent) -# else - { "ioctl", (sqlcipher_sqlite3_syscall_ptr)ioctl, 0 }, -#define osIoctl ((int(*)(int,unsigned long,...))aSyscall[28].pCurrent) -# endif -#else - { "ioctl", (sqlcipher_sqlite3_syscall_ptr)0, 0 }, + if( pLockingStyle == &posixIoMethods +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE + || pLockingStyle == &nfsIoMethods #endif + ){ + unixEnterMutex(); + rc = findInodeInfo(pNew, &pNew->pInode); + if( rc!=SQLITE_OK ){ + /* If an error occurred in findInodeInfo(), close the file descriptor + ** immediately, before releasing the mutex. findInodeInfo() may fail + ** in two scenarios: + ** + ** (a) A call to fstat() failed. + ** (b) A malloc failed. + ** + ** Scenario (b) may only occur if the process is holding no other + ** file descriptors open on the same file. If there were other file + ** descriptors on this file, then no malloc would be required by + ** findInodeInfo(). If this is the case, it is quite safe to close + ** handle h - as it is guaranteed that no posix locks will be released + ** by doing so. + ** + ** If scenario (a) caused the error then things are not so safe. The + ** implicit assumption here is that if fstat() fails, things are in + ** such bad shape that dropping a lock or two doesn't matter much. + */ + robust_close(pNew, h, __LINE__); + h = -1; + } + unixLeaveMutex(); + } -}; /* End of the overrideable system calls */ - - -/* -** On some systems, calls to fchown() will trigger a message in a security -** log if they come from non-root processes. So avoid calling fchown() if -** we are not running as root. -*/ -static int robustFchown(int fd, uid_t uid, gid_t gid){ -#if defined(HAVE_FCHOWN) - return osGeteuid() ? 0 : osFchown(fd,uid,gid); -#else - return 0; +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) + else if( pLockingStyle == &afpIoMethods ){ + /* AFP locking uses the file path so it needs to be included in + ** the afpLockingContext. + */ + afpLockingContext *pCtx; + pNew->lockingContext = pCtx = sqlcipher_sqlite3_malloc64( sizeof(*pCtx) ); + if( pCtx==0 ){ + rc = SQLITE_NOMEM_BKPT; + }else{ + /* NB: zFilename exists and remains valid until the file is closed + ** according to requirement F11141. So we do not need to make a + ** copy of the filename. */ + pCtx->dbPath = zFilename; + pCtx->reserved = 0; + srandomdev(); + unixEnterMutex(); + rc = findInodeInfo(pNew, &pNew->pInode); + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3_free(pNew->lockingContext); + robust_close(pNew, h, __LINE__); + h = -1; + } + unixLeaveMutex(); + } + } #endif -} -/* -** This is the xSetSystemCall() method of sqlcipher_sqlite3_vfs for all of the -** "unix" VFSes. Return SQLITE_OK opon successfully updating the -** system call pointer, or SQLITE_NOTFOUND if there is no configurable -** system call named zName. -*/ -static int unixSetSystemCall( - sqlcipher_sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ - const char *zName, /* Name of system call to override */ - sqlcipher_sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ -){ - unsigned int i; - int rc = SQLITE_NOTFOUND; + else if( pLockingStyle == &dotlockIoMethods ){ + /* Dotfile locking uses the file path so it needs to be included in + ** the dotlockLockingContext + */ + char *zLockFile; + int nFilename; + assert( zFilename!=0 ); + nFilename = (int)strlen(zFilename) + 6; + zLockFile = (char *)sqlcipher_sqlite3_malloc64(nFilename); + if( zLockFile==0 ){ + rc = SQLITE_NOMEM_BKPT; + }else{ + sqlcipher_sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename); + } + pNew->lockingContext = zLockFile; + } - UNUSED_PARAMETER(pNotUsed); - if( zName==0 ){ - /* If no zName is given, restore all system calls to their default - ** settings and return NULL +#if OS_VXWORKS + else if( pLockingStyle == &semIoMethods ){ + /* Named semaphore locking uses the file path so it needs to be + ** included in the semLockingContext */ - rc = SQLITE_OK; - for(i=0; ipInode); + if( (rc==SQLITE_OK) && (pNew->pInode->pSem==NULL) ){ + char *zSemName = pNew->pInode->aSemName; + int n; + sqlcipher_sqlite3_snprintf(MAX_PATHNAME, zSemName, "/%s.sem", + pNew->pId->zCanonicalName); + for( n=1; zSemName[n]; n++ ) + if( zSemName[n]=='/' ) zSemName[n] = '_'; + pNew->pInode->pSem = sem_open(zSemName, O_CREAT, 0666, 1); + if( pNew->pInode->pSem == SEM_FAILED ){ + rc = SQLITE_NOMEM_BKPT; + pNew->pInode->aSemName[0] = '\0'; } } + unixLeaveMutex(); + } +#endif + + storeLastErrno(pNew, 0); +#if OS_VXWORKS + if( rc!=SQLITE_OK ){ + if( h>=0 ) robust_close(pNew, h, __LINE__); + h = -1; + osUnlink(zFilename); + pNew->ctrlFlags |= UNIXFILE_DELETE; + } +#endif + if( rc!=SQLITE_OK ){ + if( h>=0 ) robust_close(pNew, h, __LINE__); }else{ - /* If zName is specified, operate on only the one system call - ** specified. - */ - for(i=0; ipMethods = pLockingStyle; + OpenCounter(+1); + verifyDbFile(pNew); } return rc; } /* -** Return the value of a system call. Return NULL if zName is not a -** recognized system call name. NULL is also returned if the system call -** is currently undefined. +** Directories to consider for temp files. */ -static sqlcipher_sqlite3_syscall_ptr unixGetSystemCall( - sqlcipher_sqlite3_vfs *pNotUsed, - const char *zName -){ - unsigned int i; - - UNUSED_PARAMETER(pNotUsed); - for(i=0; i=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break; - osClose(fd); - sqlcipher_sqlite3_log(SQLITE_WARNING, - "attempt to open \"%s\" as file descriptor %d", z, fd); - fd = -1; - if( osOpen("/dev/null", O_RDONLY, m)<0 ) break; - } - if( fd>=0 ){ - if( m!=0 ){ - struct stat statbuf; - if( osFstat(fd, &statbuf)==0 - && statbuf.st_size==0 - && (statbuf.st_mode&0777)!=m - ){ - osFchmod(fd, m); - } + if( zDir!=0 + && osStat(zDir, &buf)==0 + && S_ISDIR(buf.st_mode) + && osAccess(zDir, 03)==0 + ){ + return zDir; } -#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) - osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); -#endif + if( i>=sizeof(azTempDirs)/sizeof(azTempDirs[0]) ) break; + zDir = azTempDirs[i++]; } - return fd; + return 0; } /* -** Helper functions to obtain and relinquish the global mutex. The -** global mutex is used to protect the unixInodeInfo and -** vxworksFileId objects used by this file, all of which may be -** shared by multiple threads. -** -** Function unixMutexHeld() is used to assert() that the global mutex -** is held when required. This function is only used as part of assert() -** statements. e.g. -** -** unixEnterMutex() -** assert( unixMutexHeld() ); -** unixEnterLeave() -** -** To prevent deadlock, the global unixBigLock must must be acquired -** before the unixInodeInfo.pLockMutex mutex, if both are held. It is -** OK to get the pLockMutex without holding unixBigLock first, but if -** that happens, the unixBigLock mutex must not be acquired until after -** pLockMutex is released. -** -** OK: enter(unixBigLock), enter(pLockInfo) -** OK: enter(unixBigLock) -** OK: enter(pLockInfo) -** ERROR: enter(pLockInfo), enter(unixBigLock) +** Create a temporary file name in zBuf. zBuf must be allocated +** by the calling process and must be big enough to hold at least +** pVfs->mxPathname bytes. */ -static sqlcipher_sqlite3_mutex *unixBigLock = 0; -static void unixEnterMutex(void){ - assert( sqlcipher_sqlite3_mutex_notheld(unixBigLock) ); /* Not a recursive mutex */ - sqlcipher_sqlite3_mutex_enter(unixBigLock); -} -static void unixLeaveMutex(void){ - assert( sqlcipher_sqlite3_mutex_held(unixBigLock) ); - sqlcipher_sqlite3_mutex_leave(unixBigLock); -} -#ifdef SQLITE_DEBUG -static int unixMutexHeld(void) { - return sqlcipher_sqlite3_mutex_held(unixBigLock); -} -#endif - +static int unixGetTempname(int nBuf, char *zBuf){ + const char *zDir; + int iLimit = 0; + int rc = SQLITE_OK; -#ifdef SQLITE_HAVE_OS_TRACE -/* -** Helper function for printing out trace information from debugging -** binaries. This returns the string representation of the supplied -** integer lock-type. -*/ -static const char *azFileLock(int eFileLock){ - switch( eFileLock ){ - case NO_LOCK: return "NONE"; - case SHARED_LOCK: return "SHARED"; - case RESERVED_LOCK: return "RESERVED"; - case PENDING_LOCK: return "PENDING"; - case EXCLUSIVE_LOCK: return "EXCLUSIVE"; - } - return "ERROR"; -} -#endif + /* It's odd to simulate an io-error here, but really this is just + ** using the io-error infrastructure to test that SQLite handles this + ** function failing. + */ + zBuf[0] = 0; + SimulateIOError( return SQLITE_IOERR ); -#ifdef SQLITE_LOCK_TRACE -/* -** Print out information about all locking operations. -** -** This routine is used for troubleshooting locks on multithreaded -** platforms. Enable by compiling with the -DSQLITE_LOCK_TRACE -** command-line option on the compiler. This code is normally -** turned off. -*/ -static int lockTrace(int fd, int op, struct flock *p){ - char *zOpName, *zType; - int s; - int savedErrno; - if( op==F_GETLK ){ - zOpName = "GETLK"; - }else if( op==F_SETLK ){ - zOpName = "SETLK"; - }else{ - s = osFcntl(fd, op, p); - sqlcipher_sqlite3DebugPrintf("fcntl unknown %d %d %d\n", fd, op, s); - return s; - } - if( p->l_type==F_RDLCK ){ - zType = "RDLCK"; - }else if( p->l_type==F_WRLCK ){ - zType = "WRLCK"; - }else if( p->l_type==F_UNLCK ){ - zType = "UNLCK"; + sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + zDir = unixTempFileDir(); + if( zDir==0 ){ + rc = SQLITE_IOERR_GETTEMPPATH; }else{ - assert( 0 ); - } - assert( p->l_whence==SEEK_SET ); - s = osFcntl(fd, op, p); - savedErrno = errno; - sqlcipher_sqlite3DebugPrintf("fcntl %d %d %s %s %d %d %d %d\n", - threadid, fd, zOpName, zType, (int)p->l_start, (int)p->l_len, - (int)p->l_pid, s); - if( s==(-1) && op==F_SETLK && (p->l_type==F_RDLCK || p->l_type==F_WRLCK) ){ - struct flock l2; - l2 = *p; - osFcntl(fd, F_GETLK, &l2); - if( l2.l_type==F_RDLCK ){ - zType = "RDLCK"; - }else if( l2.l_type==F_WRLCK ){ - zType = "WRLCK"; - }else if( l2.l_type==F_UNLCK ){ - zType = "UNLCK"; - }else{ - assert( 0 ); - } - sqlcipher_sqlite3DebugPrintf("fcntl-failure-reason: %s %d %d %d\n", - zType, (int)l2.l_start, (int)l2.l_len, (int)l2.l_pid); + do{ + u64 r; + sqlcipher_sqlite3_randomness(sizeof(r), &r); + assert( nBuf>2 ); + zBuf[nBuf-2] = 0; + sqlcipher_sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c", + zDir, r, 0); + if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ){ + rc = SQLITE_ERROR; + break; + } + }while( osAccess(zBuf,0)==0 ); } - errno = savedErrno; - return s; + sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + return rc; } -#undef osFcntl -#define osFcntl lockTrace -#endif /* SQLITE_LOCK_TRACE */ +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) /* -** Retry ftruncate() calls that fail due to EINTR -** -** All calls to ftruncate() within this file should be made through -** this wrapper. On the Android platform, bypassing the logic below -** could lead to a corrupt database. +** Routine to transform a unixFile into a proxy-locking unixFile. +** Implementation in the proxy-lock division, but used by unixOpen() +** if SQLITE_PREFER_PROXY_LOCKING is defined. */ -static int robust_ftruncate(int h, sqlcipher_sqlite3_int64 sz){ - int rc; -#ifdef __ANDROID__ - /* On Android, ftruncate() always uses 32-bit offsets, even if - ** _FILE_OFFSET_BITS=64 is defined. This means it is unsafe to attempt to - ** truncate a file to any size larger than 2GiB. Silently ignore any - ** such attempts. */ - if( sz>(sqlcipher_sqlite3_int64)0x7FFFFFFF ){ - rc = SQLITE_OK; - }else +static int proxyTransformUnixFile(unixFile*, const char*); #endif - do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR ); - return rc; -} /* -** This routine translates a standard POSIX errno code into something -** useful to the clients of the sqlcipher_sqlite3 functions. Specifically, it is -** intended to translate a variety of "try again" errors into SQLITE_BUSY -** and a variety of "please close the file descriptor NOW" errors into -** SQLITE_IOERR +** Search for an unused file descriptor that was opened on the database +** file (not a journal or super-journal file) identified by pathname +** zPath with SQLITE_OPEN_XXX flags matching those passed as the second +** argument to this function. ** -** Errors during initialization of locks, or file system support for locks, -** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately. +** Such a file descriptor may exist if a database connection was closed +** but the associated file descriptor could not be closed because some +** other file descriptor open on the same file is holding a file-lock. +** Refer to comments in the unixClose() function and the lengthy comment +** describing "Posix Advisory Locking" at the start of this file for +** further details. Also, ticket #4018. +** +** If a suitable file descriptor is found, then it is returned. If no +** such file descriptor is located, -1 is returned. */ -static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { - assert( (sqliteIOErr == SQLITE_IOERR_LOCK) || - (sqliteIOErr == SQLITE_IOERR_UNLOCK) || - (sqliteIOErr == SQLITE_IOERR_RDLOCK) || - (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ); - switch (posixError) { - case EACCES: - case EAGAIN: - case ETIMEDOUT: - case EBUSY: - case EINTR: - case ENOLCK: - /* random NFS retry error, unless during file system support - * introspection, in which it actually means what it says */ - return SQLITE_BUSY; +static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ + UnixUnusedFd *pUnused = 0; - case EPERM: - return SQLITE_PERM; + /* Do not search for an unused file descriptor on vxworks. Not because + ** vxworks would not benefit from the change (it might, we're not sure), + ** but because no way to test it is currently available. It is better + ** not to risk breaking vxworks support for the sake of such an obscure + ** feature. */ +#if !OS_VXWORKS + struct stat sStat; /* Results of stat() call */ - default: - return sqliteIOErr; - } -} + unixEnterMutex(); + /* A stat() call may fail for various reasons. If this happens, it is + ** almost certain that an open() call on the same path will also fail. + ** For this reason, if an error occurs in the stat() call here, it is + ** ignored and -1 is returned. The caller will try to open a new file + ** descriptor on the same path, fail, and return an error to SQLite. + ** + ** Even if a subsequent open() call does succeed, the consequences of + ** not searching for a reusable file descriptor are not dire. */ + if( inodeList!=0 && 0==osStat(zPath, &sStat) ){ + unixInodeInfo *pInode; -/****************************************************************************** -****************** Begin Unique File ID Utility Used By VxWorks *************** -** -** On most versions of unix, we can get a unique ID for a file by concatenating -** the device number and the inode number. But this does not work on VxWorks. -** On VxWorks, a unique file id must be based on the canonical filename. -** -** A pointer to an instance of the following structure can be used as a -** unique file ID in VxWorks. Each instance of this structure contains -** a copy of the canonical filename. There is also a reference count. -** The structure is reclaimed when the number of pointers to it drops to -** zero. -** -** There are never very many files open at one time and lookups are not -** a performance-critical path, so it is sufficient to put these -** structures on a linked list. -*/ -struct vxworksFileId { - struct vxworksFileId *pNext; /* Next in a list of them all */ - int nRef; /* Number of references to this one */ - int nName; /* Length of the zCanonicalName[] string */ - char *zCanonicalName; /* Canonical filename */ -}; + pInode = inodeList; + while( pInode && (pInode->fileId.dev!=sStat.st_dev + || pInode->fileId.ino!=(u64)sStat.st_ino) ){ + pInode = pInode->pNext; + } + if( pInode ){ + UnixUnusedFd **pp; + assert( sqlcipher_sqlite3_mutex_notheld(pInode->pLockMutex) ); + sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); + flags &= (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE); + for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); + pUnused = *pp; + if( pUnused ){ + *pp = pUnused->pNext; + } + sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); + } + } + unixLeaveMutex(); +#endif /* if !OS_VXWORKS */ + return pUnused; +} -#if OS_VXWORKS /* -** All unique filenames are held on a linked list headed by this -** variable: +** Find the mode, uid and gid of file zFile. */ -static struct vxworksFileId *vxworksFileList = 0; +static int getFileMode( + const char *zFile, /* File name */ + mode_t *pMode, /* OUT: Permissions of zFile */ + uid_t *pUid, /* OUT: uid of zFile. */ + gid_t *pGid /* OUT: gid of zFile. */ +){ + struct stat sStat; /* Output of stat() on database file */ + int rc = SQLITE_OK; + if( 0==osStat(zFile, &sStat) ){ + *pMode = sStat.st_mode & 0777; + *pUid = sStat.st_uid; + *pGid = sStat.st_gid; + }else{ + rc = SQLITE_IOERR_FSTAT; + } + return rc; +} /* -** Simplify a filename into its canonical form -** by making the following changes: -** -** * removing any trailing and duplicate / -** * convert /./ into just / -** * convert /A/../ where A is any simple name into just / +** This function is called by unixOpen() to determine the unix permissions +** to create new files with. If no error occurs, then SQLITE_OK is returned +** and a value suitable for passing as the third argument to open(2) is +** written to *pMode. If an IO error occurs, an SQLite error code is +** returned and the value of *pMode is not modified. ** -** Changes are made in-place. Return the new name length. +** In most cases, this routine sets *pMode to 0, which will become +** an indication to robust_open() to create the file using +** SQLITE_DEFAULT_FILE_PERMISSIONS adjusted by the umask. +** But if the file being opened is a WAL or regular journal file, then +** this function queries the file-system for the permissions on the +** corresponding database file and sets *pMode to this value. Whenever +** possible, WAL and journal files are created using the same permissions +** as the associated database file. ** -** The original filename is in z[0..n-1]. Return the number of -** characters in the simplified name. +** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the +** original filename is unavailable. But 8_3_NAMES is only used for +** FAT filesystems and permissions do not matter there, so just use +** the default permissions. In 8_3_NAMES mode, leave *pMode set to zero. */ -static int vxworksSimplifyName(char *z, int n){ - int i, j; - while( n>1 && z[n-1]=='/' ){ n--; } - for(i=j=0; i0 && z[j-1]!='/' ){ j--; } - if( j>0 ){ j--; } - i += 2; - continue; +static int findCreateFileMode( + const char *zPath, /* Path of file (possibly) being created */ + int flags, /* Flags passed as 4th argument to xOpen() */ + mode_t *pMode, /* OUT: Permissions to open file with */ + uid_t *pUid, /* OUT: uid to set on the file */ + gid_t *pGid /* OUT: gid to set on the file */ +){ + int rc = SQLITE_OK; /* Return Code */ + *pMode = 0; + *pUid = 0; + *pGid = 0; + if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ + char zDb[MAX_PATHNAME+1]; /* Database file path */ + int nDb; /* Number of valid bytes in zDb */ + + /* zPath is a path to a WAL or journal file. The following block derives + ** the path to the associated database file from zPath. This block handles + ** the following naming conventions: + ** + ** "-journal" + ** "-wal" + ** "-journalNN" + ** "-walNN" + ** + ** where NN is a decimal number. The NN naming schemes are + ** used by the test_multiplex.c module. + ** + ** In normal operation, the journal file name will always contain + ** a '-' character. However in 8+3 filename mode, or if a corrupt + ** rollback journal specifies a super-journal with a goofy name, then + ** the '-' might be missing or the '-' might be the first character in + ** the filename. In that case, just return SQLITE_OK with *pMode==0. + */ + nDb = sqlcipher_sqlite3Strlen30(zPath) - 1; + while( nDb>0 && zPath[nDb]!='.' ){ + if( zPath[nDb]=='-' ){ + memcpy(zDb, zPath, nDb); + zDb[nDb] = '\0'; + rc = getFileMode(zDb, pMode, pUid, pGid); + break; } + nDb--; + } + }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ + *pMode = 0600; + }else if( flags & SQLITE_OPEN_URI ){ + /* If this is a main database file and the file was opened using a URI + ** filename, check for the "modeof" parameter. If present, interpret + ** its value as a filename and try to copy the mode, uid and gid from + ** that file. */ + const char *z = sqlcipher_sqlite3_uri_parameter(zPath, "modeof"); + if( z ){ + rc = getFileMode(z, pMode, pUid, pGid); } - z[j++] = z[i]; } - z[j] = 0; - return j; + return rc; } /* -** Find a unique file ID for the given absolute pathname. Return -** a pointer to the vxworksFileId object. This pointer is the unique -** file ID. +** Open the file zPath. ** -** The nRef field of the vxworksFileId object is incremented before -** the object is returned. A new vxworksFileId object is created -** and added to the global list if necessary. +** Previously, the SQLite OS layer used three functions in place of this +** one: ** -** If a memory allocation error occurs, return NULL. +** sqlcipher_sqlite3OsOpenReadWrite(); +** sqlcipher_sqlite3OsOpenReadOnly(); +** sqlcipher_sqlite3OsOpenExclusive(); +** +** These calls correspond to the following combinations of flags: +** +** ReadWrite() -> (READWRITE | CREATE) +** ReadOnly() -> (READONLY) +** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE) +** +** The old OpenExclusive() accepted a boolean argument - "delFlag". If +** true, the file was configured to be automatically deleted when the +** file handle closed. To achieve the same effect using this new +** interface, add the DELETEONCLOSE flag to those specified above for +** OpenExclusive(). */ -static struct vxworksFileId *vxworksFindFileId(const char *zAbsoluteName){ - struct vxworksFileId *pNew; /* search key and new file ID */ - struct vxworksFileId *pCandidate; /* For looping over existing file IDs */ - int n; /* Length of zAbsoluteName string */ +static int unixOpen( + sqlcipher_sqlite3_vfs *pVfs, /* The VFS for which this is the xOpen method */ + const char *zPath, /* Pathname of file to be opened */ + sqlcipher_sqlite3_file *pFile, /* The file descriptor to be filled in */ + int flags, /* Input flags to control the opening */ + int *pOutFlags /* Output flags returned to SQLite core */ +){ + unixFile *p = (unixFile *)pFile; + int fd = -1; /* File descriptor returned by open() */ + int openFlags = 0; /* Flags to pass to open() */ + int eType = flags&0x0FFF00; /* Type of file to open */ + int noLock; /* True to omit locking primitives */ + int rc = SQLITE_OK; /* Function Return Code */ + int ctrlFlags = 0; /* UNIXFILE_* flags */ - assert( zAbsoluteName[0]=='/' ); - n = (int)strlen(zAbsoluteName); - pNew = sqlcipher_sqlite3_malloc64( sizeof(*pNew) + (n+1) ); - if( pNew==0 ) return 0; - pNew->zCanonicalName = (char*)&pNew[1]; - memcpy(pNew->zCanonicalName, zAbsoluteName, n+1); - n = vxworksSimplifyName(pNew->zCanonicalName, n); + int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); + int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); + int isCreate = (flags & SQLITE_OPEN_CREATE); + int isReadonly = (flags & SQLITE_OPEN_READONLY); + int isReadWrite = (flags & SQLITE_OPEN_READWRITE); +#if SQLITE_ENABLE_LOCKING_STYLE + int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY); +#endif +#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE + struct statfs fsInfo; +#endif - /* Search for an existing entry that matching the canonical name. - ** If found, increment the reference count and return a pointer to - ** the existing file ID. + /* If creating a super- or main-file journal, this function will open + ** a file-descriptor on the directory too. The first time unixSync() + ** is called the directory file descriptor will be fsync()ed and close()d. */ - unixEnterMutex(); - for(pCandidate=vxworksFileList; pCandidate; pCandidate=pCandidate->pNext){ - if( pCandidate->nName==n - && memcmp(pCandidate->zCanonicalName, pNew->zCanonicalName, n)==0 - ){ - sqlcipher_sqlite3_free(pNew); - pCandidate->nRef++; - unixLeaveMutex(); - return pCandidate; - } + int isNewJrnl = (isCreate && ( + eType==SQLITE_OPEN_SUPER_JOURNAL + || eType==SQLITE_OPEN_MAIN_JOURNAL + || eType==SQLITE_OPEN_WAL + )); + + /* If argument zPath is a NULL pointer, this function is required to open + ** a temporary file. Use this buffer to store the file name in. + */ + char zTmpname[MAX_PATHNAME+2]; + const char *zName = zPath; + + /* Check the following statements are true: + ** + ** (a) Exactly one of the READWRITE and READONLY flags must be set, and + ** (b) if CREATE is set, then READWRITE must also be set, and + ** (c) if EXCLUSIVE is set, then CREATE must also be set. + ** (d) if DELETEONCLOSE is set, then CREATE must also be set. + */ + assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); + assert(isCreate==0 || isReadWrite); + assert(isExclusive==0 || isCreate); + assert(isDelete==0 || isCreate); + + /* The main DB, main journal, WAL file and super-journal are never + ** automatically deleted. Nor are they ever temporary files. */ + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_SUPER_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); + + /* Assert that the upper layer has set one of the "file-type" flags. */ + assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB + || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL + || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL + || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL + ); + + /* Detect a pid change and reset the PRNG. There is a race condition + ** here such that two or more threads all trying to open databases at + ** the same instant might all reset the PRNG. But multiple resets + ** are harmless. + */ + if( randomnessPid!=osGetpid(0) ){ + randomnessPid = osGetpid(0); + sqlcipher_sqlite3_randomness(0,0); } + memset(p, 0, sizeof(unixFile)); - /* No match was found. We will make a new file ID */ - pNew->nRef = 1; - pNew->nName = n; - pNew->pNext = vxworksFileList; - vxworksFileList = pNew; - unixLeaveMutex(); - return pNew; -} +#ifdef SQLITE_ASSERT_NO_FILES + /* Applications that never read or write a persistent disk files */ + assert( zName==0 ); +#endif -/* -** Decrement the reference count on a vxworksFileId object. Free -** the object when the reference count reaches zero. -*/ -static void vxworksReleaseFileId(struct vxworksFileId *pId){ - unixEnterMutex(); - assert( pId->nRef>0 ); - pId->nRef--; - if( pId->nRef==0 ){ - struct vxworksFileId **pp; - for(pp=&vxworksFileList; *pp && *pp!=pId; pp = &((*pp)->pNext)){} - assert( *pp==pId ); - *pp = pId->pNext; - sqlcipher_sqlite3_free(pId); + if( eType==SQLITE_OPEN_MAIN_DB ){ + UnixUnusedFd *pUnused; + pUnused = findReusableFd(zName, flags); + if( pUnused ){ + fd = pUnused->fd; + }else{ + pUnused = sqlcipher_sqlite3_malloc64(sizeof(*pUnused)); + if( !pUnused ){ + return SQLITE_NOMEM_BKPT; + } + } + p->pPreallocatedUnused = pUnused; + + /* Database filenames are double-zero terminated if they are not + ** URIs with parameters. Hence, they can always be passed into + ** sqlcipher_sqlite3_uri_parameter(). */ + assert( (flags & SQLITE_OPEN_URI) || zName[strlen(zName)+1]==0 ); + + }else if( !zName ){ + /* If zName is NULL, the upper layer is requesting a temp file. */ + assert(isDelete && !isNewJrnl); + rc = unixGetTempname(pVfs->mxPathname, zTmpname); + if( rc!=SQLITE_OK ){ + return rc; + } + zName = zTmpname; + + /* Generated temporary filenames are always double-zero terminated + ** for use by sqlcipher_sqlite3_uri_parameter(). */ + assert( zName[strlen(zName)+1]==0 ); } - unixLeaveMutex(); -} -#endif /* OS_VXWORKS */ -/*************** End of Unique File ID Utility Used By VxWorks **************** -******************************************************************************/ + /* Determine the value of the flags parameter passed to POSIX function + ** open(). These must be calculated even if open() is not called, as + ** they may be stored as part of the file handle and used by the + ** 'conch file' locking functions later on. */ + if( isReadonly ) openFlags |= O_RDONLY; + if( isReadWrite ) openFlags |= O_RDWR; + if( isCreate ) openFlags |= O_CREAT; + if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW); + openFlags |= (O_LARGEFILE|O_BINARY|O_NOFOLLOW); -/****************************************************************************** -*************************** Posix Advisory Locking **************************** -** -** POSIX advisory locks are broken by design. ANSI STD 1003.1 (1996) -** section 6.5.2.2 lines 483 through 490 specify that when a process -** sets or clears a lock, that operation overrides any prior locks set -** by the same process. It does not explicitly say so, but this implies -** that it overrides locks set by the same process using a different -** file descriptor. Consider this test case: -** -** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644); -** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644); -** -** Suppose ./file1 and ./file2 are really the same file (because -** one is a hard or symbolic link to the other) then if you set -** an exclusive lock on fd1, then try to get an exclusive lock -** on fd2, it works. I would have expected the second lock to -** fail since there was already a lock on the file due to fd1. -** But not so. Since both locks came from the same process, the -** second overrides the first, even though they were on different -** file descriptors opened on different file names. -** -** This means that we cannot use POSIX locks to synchronize file access -** among competing threads of the same process. POSIX locks will work fine -** to synchronize access for threads in separate processes, but not -** threads within the same process. -** -** To work around the problem, SQLite has to manage file locks internally -** on its own. Whenever a new database is opened, we have to find the -** specific inode of the database file (the inode is determined by the -** st_dev and st_ino fields of the stat structure that fstat() fills in) -** and check for locks already existing on that inode. When locks are -** created or removed, we have to look at our own internal record of the -** locks to see if another thread has previously set a lock on that same -** inode. -** -** (Aside: The use of inode numbers as unique IDs does not work on VxWorks. -** For VxWorks, we have to use the alternative unique ID system based on -** canonical filename and implemented in the previous division.) -** -** The sqlcipher_sqlite3_file structure for POSIX is no longer just an integer file -** descriptor. It is now a structure that holds the integer file -** descriptor and a pointer to a structure that describes the internal -** locks on the corresponding inode. There is one locking structure -** per inode, so if the same inode is opened twice, both unixFile structures -** point to the same locking structure. The locking structure keeps -** a reference count (so we will know when to delete it) and a "cnt" -** field that tells us its internal lock status. cnt==0 means the -** file is unlocked. cnt==-1 means the file has an exclusive lock. -** cnt>0 means there are cnt shared locks on the file. -** -** Any attempt to lock or unlock a file first checks the locking -** structure. The fcntl() system call is only invoked to set a -** POSIX lock if the internal lock structure transitions between -** a locked and an unlocked state. -** -** But wait: there are yet more problems with POSIX advisory locks. -** -** If you close a file descriptor that points to a file that has locks, -** all locks on that file that are owned by the current process are -** released. To work around this problem, each unixInodeInfo object -** maintains a count of the number of pending locks on tha inode. -** When an attempt is made to close an unixFile, if there are -** other unixFile open on the same inode that are holding locks, the call -** to close() the file descriptor is deferred until all of the locks clear. -** The unixInodeInfo structure keeps a list of file descriptors that need to -** be closed and that list is walked (and cleared) when the last lock -** clears. -** -** Yet another problem: LinuxThreads do not play well with posix locks. -** -** Many older versions of linux use the LinuxThreads library which is -** not posix compliant. Under LinuxThreads, a lock created by thread -** A cannot be modified or overridden by a different thread B. -** Only thread A can modify the lock. Locking behavior is correct -** if the appliation uses the newer Native Posix Thread Library (NPTL) -** on linux - with NPTL a lock created by thread A can override locks -** in thread B. But there is no way to know at compile-time which -** threading library is being used. So there is no way to know at -** compile-time whether or not thread A can override locks on thread B. -** One has to do a run-time check to discover the behavior of the -** current process. -** -** SQLite used to support LinuxThreads. But support for LinuxThreads -** was dropped beginning with version 3.7.0. SQLite will still work with -** LinuxThreads provided that (1) there is no more than one connection -** per database file in the same process and (2) database connections -** do not move across threads. -*/ + if( fd<0 ){ + mode_t openMode; /* Permissions to create file with */ + uid_t uid; /* Userid for the file */ + gid_t gid; /* Groupid for the file */ + rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid); + if( rc!=SQLITE_OK ){ + assert( !p->pPreallocatedUnused ); + assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); + return rc; + } + fd = robust_open(zName, openFlags, openMode); + OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags)); + assert( !isExclusive || (openFlags & O_CREAT)!=0 ); + if( fd<0 ){ + if( isNewJrnl && errno==EACCES && osAccess(zName, F_OK) ){ + /* If unable to create a journal because the directory is not + ** writable, change the error code to indicate that. */ + rc = SQLITE_READONLY_DIRECTORY; + }else if( errno!=EISDIR && isReadWrite ){ + /* Failed to open the file for read/write access. Try read-only. */ + flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); + openFlags &= ~(O_RDWR|O_CREAT); + flags |= SQLITE_OPEN_READONLY; + openFlags |= O_RDONLY; + isReadonly = 1; + fd = robust_open(zName, openFlags, openMode); + } + } + if( fd<0 ){ + int rc2 = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName); + if( rc==SQLITE_OK ) rc = rc2; + goto open_finished; + } -/* -** An instance of the following structure serves as the key used -** to locate a particular unixInodeInfo object. -*/ -struct unixFileId { - dev_t dev; /* Device number */ + /* The owner of the rollback journal or WAL file should always be the + ** same as the owner of the database file. Try to ensure that this is + ** the case. The chown() system call will be a no-op if the current + ** process lacks root privileges, be we should at least try. Without + ** this step, if a root process opens a database file, it can leave + ** behinds a journal/WAL that is owned by root and hence make the + ** database inaccessible to unprivileged processes. + ** + ** If openMode==0, then that means uid and gid are not set correctly + ** (probably because SQLite is configured to use 8+3 filename mode) and + ** in that case we do not want to attempt the chown(). + */ + if( openMode && (flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){ + robustFchown(fd, uid, gid); + } + } + assert( fd>=0 ); + if( pOutFlags ){ + *pOutFlags = flags; + } + + if( p->pPreallocatedUnused ){ + p->pPreallocatedUnused->fd = fd; + p->pPreallocatedUnused->flags = + flags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE); + } + + if( isDelete ){ #if OS_VXWORKS - struct vxworksFileId *pId; /* Unique file ID for vxworks. */ + zPath = zName; +#elif defined(SQLITE_UNLINK_AFTER_CLOSE) + zPath = sqlcipher_sqlite3_mprintf("%s", zName); + if( zPath==0 ){ + robust_close(p, fd, __LINE__); + return SQLITE_NOMEM_BKPT; + } #else - /* We are told that some versions of Android contain a bug that - ** sizes ino_t at only 32-bits instead of 64-bits. (See - ** https://android-review.googlesource.com/#/c/115351/3/dist/sqlcipher_sqlite3.c) - ** To work around this, always allocate 64-bits for the inode number. - ** On small machines that only have 32-bit inodes, this wastes 4 bytes, - ** but that should not be a big deal. */ - /* WAS: ino_t ino; */ - u64 ino; /* Inode number */ + osUnlink(zName); #endif -}; - -/* -** An instance of the following structure is allocated for each open -** inode. -** -** A single inode can have multiple file descriptors, so each unixFile -** structure contains a pointer to an instance of this object and this -** object keeps a count of the number of unixFile pointing to it. -** -** Mutex rules: -** -** (1) Only the pLockMutex mutex must be held in order to read or write -** any of the locking fields: -** nShared, nLock, eFileLock, bProcessLock, pUnused -** -** (2) When nRef>0, then the following fields are unchanging and can -** be read (but not written) without holding any mutex: -** fileId, pLockMutex -** -** (3) With the exceptions above, all the fields may only be read -** or written while holding the global unixBigLock mutex. -** -** Deadlock prevention: The global unixBigLock mutex may not -** be acquired while holding the pLockMutex mutex. If both unixBigLock -** and pLockMutex are needed, then unixBigLock must be acquired first. -*/ -struct unixInodeInfo { - struct unixFileId fileId; /* The lookup key */ - sqlcipher_sqlite3_mutex *pLockMutex; /* Hold this mutex for... */ - int nShared; /* Number of SHARED locks held */ - int nLock; /* Number of outstanding file locks */ - unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ - unsigned char bProcessLock; /* An exclusive process lock is held */ - UnixUnusedFd *pUnused; /* Unused file descriptors to close */ - int nRef; /* Number of pointers to this structure */ - unixShmNode *pShmNode; /* Shared memory associated with this inode */ - unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ - unixInodeInfo *pPrev; /* .... doubly linked */ + } #if SQLITE_ENABLE_LOCKING_STYLE - unsigned long long sharedByte; /* for AFP simulated shared lock */ -#endif -#if OS_VXWORKS - sem_t *pSem; /* Named POSIX semaphore */ - char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */ + else{ + p->openFlags = openFlags; + } #endif -}; - -/* -** A lists of all unixInodeInfo objects. -** -** Must hold unixBigLock in order to read or write this variable. -*/ -static unixInodeInfo *inodeList = 0; /* All unixInodeInfo objects */ -#ifdef SQLITE_DEBUG -/* -** True if the inode mutex (on the unixFile.pFileMutex field) is held, or not. -** This routine is used only within assert() to help verify correct mutex -** usage. -*/ -int unixFileMutexHeld(unixFile *pFile){ - assert( pFile->pInode ); - return sqlcipher_sqlite3_mutex_held(pFile->pInode->pLockMutex); -} -int unixFileMutexNotheld(unixFile *pFile){ - assert( pFile->pInode ); - return sqlcipher_sqlite3_mutex_notheld(pFile->pInode->pLockMutex); -} +#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE + if( fstatfs(fd, &fsInfo) == -1 ){ + storeLastErrno(p, errno); + robust_close(p, fd, __LINE__); + return SQLITE_IOERR_ACCESS; + } + if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { + ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; + } + if (0 == strncmp("exfat", fsInfo.f_fstypename, 5)) { + ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; + } #endif -/* -** -** This function - unixLogErrorAtLine(), is only ever called via the macro -** unixLogError(). -** -** It is invoked after an error occurs in an OS function and errno has been -** set. It logs a message using sqlcipher_sqlite3_log() containing the current value of -** errno and, if possible, the human-readable equivalent from strerror() or -** strerror_r(). -** -** The first argument passed to the macro should be the error code that -** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). -** The two subsequent arguments should be the name of the OS function that -** failed (e.g. "unlink", "open") and the associated file-system path, -** if any. -*/ -#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__) -static int unixLogErrorAtLine( - int errcode, /* SQLite error code */ - const char *zFunc, /* Name of OS function that failed */ - const char *zPath, /* File path associated with error */ - int iLine /* Source line number where error occurred */ -){ - char *zErr; /* Message from strerror() or equivalent */ - int iErrno = errno; /* Saved syscall error number */ - - /* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use - ** the strerror() function to obtain the human-readable error message - ** equivalent to errno. Otherwise, use strerror_r(). - */ -#if SQLITE_THREADSAFE && defined(HAVE_STRERROR_R) - char aErr[80]; - memset(aErr, 0, sizeof(aErr)); - zErr = aErr; - - /* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined, - ** assume that the system provides the GNU version of strerror_r() that - ** returns a pointer to a buffer containing the error message. That pointer - ** may point to aErr[], or it may point to some static storage somewhere. - ** Otherwise, assume that the system provides the POSIX version of - ** strerror_r(), which always writes an error message into aErr[]. - ** - ** If the code incorrectly assumes that it is the POSIX version that is - ** available, the error message will often be an empty string. Not a - ** huge problem. Incorrectly concluding that the GNU version is available - ** could lead to a segfault though. - */ -#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) - zErr = -# endif - strerror_r(iErrno, aErr, sizeof(aErr)-1); + /* Set up appropriate ctrlFlags */ + if( isDelete ) ctrlFlags |= UNIXFILE_DELETE; + if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY; + noLock = eType!=SQLITE_OPEN_MAIN_DB; + if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK; + if( isNewJrnl ) ctrlFlags |= UNIXFILE_DIRSYNC; + if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI; -#elif SQLITE_THREADSAFE - /* This is a threadsafe build, but strerror_r() is not available. */ - zErr = ""; -#else - /* Non-threadsafe build, use strerror(). */ - zErr = strerror(iErrno); +#if SQLITE_ENABLE_LOCKING_STYLE +#if SQLITE_PREFER_PROXY_LOCKING + isAutoProxy = 1; +#endif + if( isAutoProxy && (zPath!=NULL) && (!noLock) && pVfs->xOpen ){ + char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING"); + int useProxy = 0; + + /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means + ** never use proxy, NULL means use proxy for non-local files only. */ + if( envforce!=NULL ){ + useProxy = atoi(envforce)>0; + }else{ + useProxy = !(fsInfo.f_flags&MNT_LOCAL); + } + if( useProxy ){ + rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); + if( rc==SQLITE_OK ){ + rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); + if( rc!=SQLITE_OK ){ + /* Use unixClose to clean up the resources added in fillInUnixFile + ** and clear all the structure's references. Specifically, + ** pFile->pMethods will be NULL so sqlcipher_sqlite3OsClose will be a no-op + */ + unixClose(pFile); + return rc; + } + } + goto open_finished; + } + } #endif - if( zPath==0 ) zPath = ""; - sqlcipher_sqlite3_log(errcode, - "os_unix.c:%d: (%d) %s(%s) - %s", - iLine, iErrno, zFunc, zPath, zErr + assert( zPath==0 || zPath[0]=='/' + || eType==SQLITE_OPEN_SUPER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); + rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); - return errcode; -} - -/* -** Close a file descriptor. -** -** We assume that close() almost always works, since it is only in a -** very sick application or on a very sick platform that it might fail. -** If it does fail, simply leak the file descriptor, but do log the -** error. -** -** Note that it is not safe to retry close() after EINTR since the -** file descriptor might have already been reused by another thread. -** So we don't even try to recover from an EINTR. Just log the error -** and move on. -*/ -static void robust_close(unixFile *pFile, int h, int lineno){ - if( osClose(h) ){ - unixLogErrorAtLine(SQLITE_IOERR_CLOSE, "close", - pFile ? pFile->zPath : 0, lineno); +open_finished: + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3_free(p->pPreallocatedUnused); } + return rc; } -/* -** Set the pFile->lastErrno. Do this in a subroutine as that provides -** a convenient place to set a breakpoint. -*/ -static void storeLastErrno(unixFile *pFile, int error){ - pFile->lastErrno = error; -} /* -** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. +** Delete the file at zPath. If the dirSync argument is true, fsync() +** the directory after deleting the file. */ -static void closePendingFds(unixFile *pFile){ - unixInodeInfo *pInode = pFile->pInode; - UnixUnusedFd *p; - UnixUnusedFd *pNext; - assert( unixFileMutexHeld(pFile) ); - for(p=pInode->pUnused; p; p=pNext){ - pNext = p->pNext; - robust_close(pFile, p->fd, __LINE__); - sqlcipher_sqlite3_free(p); +static int unixDelete( + sqlcipher_sqlite3_vfs *NotUsed, /* VFS containing this as the xDelete method */ + const char *zPath, /* Name of file to be deleted */ + int dirSync /* If true, fsync() directory after deleting file */ +){ + int rc = SQLITE_OK; + UNUSED_PARAMETER(NotUsed); + SimulateIOError(return SQLITE_IOERR_DELETE); + if( osUnlink(zPath)==(-1) ){ + if( errno==ENOENT +#if OS_VXWORKS + || osAccess(zPath,0)!=0 +#endif + ){ + rc = SQLITE_IOERR_DELETE_NOENT; + }else{ + rc = unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); + } + return rc; } - pInode->pUnused = 0; -} - -/* -** Release a unixInodeInfo structure previously allocated by findInodeInfo(). -** -** The global mutex must be held when this routine is called, but the mutex -** on the inode being deleted must NOT be held. -*/ -static void releaseInodeInfo(unixFile *pFile){ - unixInodeInfo *pInode = pFile->pInode; - assert( unixMutexHeld() ); - assert( unixFileMutexNotheld(pFile) ); - if( ALWAYS(pInode) ){ - pInode->nRef--; - if( pInode->nRef==0 ){ - assert( pInode->pShmNode==0 ); - sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); - closePendingFds(pFile); - sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); - if( pInode->pPrev ){ - assert( pInode->pPrev->pNext==pInode ); - pInode->pPrev->pNext = pInode->pNext; - }else{ - assert( inodeList==pInode ); - inodeList = pInode->pNext; - } - if( pInode->pNext ){ - assert( pInode->pNext->pPrev==pInode ); - pInode->pNext->pPrev = pInode->pPrev; +#ifndef SQLITE_DISABLE_DIRSYNC + if( (dirSync & 1)!=0 ){ + int fd; + rc = osOpenDirectory(zPath, &fd); + if( rc==SQLITE_OK ){ + if( full_fsync(fd,0,0) ){ + rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath); } - sqlcipher_sqlite3_mutex_free(pInode->pLockMutex); - sqlcipher_sqlite3_free(pInode); + robust_close(0, fd, __LINE__); + }else{ + assert( rc==SQLITE_CANTOPEN ); + rc = SQLITE_OK; } } +#endif + return rc; } /* -** Given a file descriptor, locate the unixInodeInfo object that -** describes that file descriptor. Create a new one if necessary. The -** return value might be uninitialized if an error occurs. +** Test the existence of or access permissions of file zPath. The +** test performed depends on the value of flags: ** -** The global mutex must held when calling this routine. +** SQLITE_ACCESS_EXISTS: Return 1 if the file exists +** SQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable. +** SQLITE_ACCESS_READONLY: Return 1 if the file is readable. ** -** Return an appropriate error code. +** Otherwise return 0. */ -static int findInodeInfo( - unixFile *pFile, /* Unix file with file desc used in the key */ - unixInodeInfo **ppInode /* Return the unixInodeInfo object here */ +static int unixAccess( + sqlcipher_sqlite3_vfs *NotUsed, /* The VFS containing this xAccess method */ + const char *zPath, /* Path of the file to examine */ + int flags, /* What do we want to learn about the zPath file? */ + int *pResOut /* Write result boolean here */ ){ - int rc; /* System call return code */ - int fd; /* The file descriptor for pFile */ - struct unixFileId fileId; /* Lookup key for the unixInodeInfo */ - struct stat statbuf; /* Low-level file information */ - unixInodeInfo *pInode = 0; /* Candidate unixInodeInfo object */ - - assert( unixMutexHeld() ); - - /* Get low-level information about the file that we can used to - ** create a unique name for the file. - */ - fd = pFile->h; - rc = osFstat(fd, &statbuf); - if( rc!=0 ){ - storeLastErrno(pFile, errno); -#if defined(EOVERFLOW) && defined(SQLITE_DISABLE_LFS) - if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS; -#endif - return SQLITE_IOERR; - } + UNUSED_PARAMETER(NotUsed); + SimulateIOError( return SQLITE_IOERR_ACCESS; ); + assert( pResOut!=0 ); -#ifdef __APPLE__ - /* On OS X on an msdos filesystem, the inode number is reported - ** incorrectly for zero-size files. See ticket #3260. To work - ** around this problem (we consider it a bug in OS X, not SQLite) - ** we always increase the file size to 1 by writing a single byte - ** prior to accessing the inode number. The one byte written is - ** an ASCII 'S' character which also happens to be the first byte - ** in the header of every SQLite database. In this way, if there - ** is a race condition such that another thread has already populated - ** the first page of the database, no damage is done. - */ - if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){ - do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); - if( rc!=1 ){ - storeLastErrno(pFile, errno); - return SQLITE_IOERR; - } - rc = osFstat(fd, &statbuf); - if( rc!=0 ){ - storeLastErrno(pFile, errno); - return SQLITE_IOERR; - } - } -#endif + /* The spec says there are three possible values for flags. But only + ** two of them are actually used */ + assert( flags==SQLITE_ACCESS_EXISTS || flags==SQLITE_ACCESS_READWRITE ); - memset(&fileId, 0, sizeof(fileId)); - fileId.dev = statbuf.st_dev; -#if OS_VXWORKS - fileId.pId = pFile->pId; -#else - fileId.ino = (u64)statbuf.st_ino; -#endif - assert( unixMutexHeld() ); - pInode = inodeList; - while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){ - pInode = pInode->pNext; - } - if( pInode==0 ){ - pInode = sqlcipher_sqlite3_malloc64( sizeof(*pInode) ); - if( pInode==0 ){ - return SQLITE_NOMEM_BKPT; - } - memset(pInode, 0, sizeof(*pInode)); - memcpy(&pInode->fileId, &fileId, sizeof(fileId)); - if( sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ - pInode->pLockMutex = sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( pInode->pLockMutex==0 ){ - sqlcipher_sqlite3_free(pInode); - return SQLITE_NOMEM_BKPT; - } - } - pInode->nRef = 1; - assert( unixMutexHeld() ); - pInode->pNext = inodeList; - pInode->pPrev = 0; - if( inodeList ) inodeList->pPrev = pInode; - inodeList = pInode; + if( flags==SQLITE_ACCESS_EXISTS ){ + struct stat buf; + *pResOut = 0==osStat(zPath, &buf) && + (!S_ISREG(buf.st_mode) || buf.st_size>0); }else{ - pInode->nRef++; + *pResOut = osAccess(zPath, W_OK|R_OK)==0; } - *ppInode = pInode; return SQLITE_OK; } /* -** Return TRUE if pFile has been renamed or unlinked since it was first opened. +** A pathname under construction */ -static int fileHasMoved(unixFile *pFile){ -#if OS_VXWORKS - return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId; -#else - struct stat buf; - return pFile->pInode!=0 && - (osStat(pFile->zPath, &buf)!=0 - || (u64)buf.st_ino!=pFile->pInode->fileId.ino); -#endif -} +typedef struct DbPath DbPath; +struct DbPath { + int rc; /* Non-zero following any error */ + int nSymlink; /* Number of symlinks resolved */ + char *zOut; /* Write the pathname here */ + int nOut; /* Bytes of space available to zOut[] */ + int nUsed; /* Bytes of zOut[] currently being used */ +}; +/* Forward reference */ +static void appendAllPathElements(DbPath*,const char*); /* -** Check a unixFile that is a database. Verify the following: -** -** (1) There is exactly one hard link on the file -** (2) The file is not a symbolic link -** (3) The file has not been renamed or unlinked -** -** Issue sqlcipher_sqlite3_log(SQLITE_WARNING,...) messages if anything is not right. +** Append a single path element to the DbPath under construction */ -static void verifyDbFile(unixFile *pFile){ - struct stat buf; - int rc; - - /* These verifications occurs for the main database only */ - if( pFile->ctrlFlags & UNIXFILE_NOLOCK ) return; - - rc = osFstat(pFile->h, &buf); - if( rc!=0 ){ - sqlcipher_sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath); - return; - } - if( buf.st_nlink==0 ){ - sqlcipher_sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath); - return; +static void appendOnePathElement( + DbPath *pPath, /* Path under construction, to which to append zName */ + const char *zName, /* Name to append to pPath. Not zero-terminated */ + int nName /* Number of significant bytes in zName */ +){ + assert( nName>0 ); + assert( zName!=0 ); + if( zName[0]=='.' ){ + if( nName==1 ) return; + if( zName[1]=='.' && nName==2 ){ + if( pPath->nUsed<=1 ){ + pPath->rc = SQLITE_ERROR; + return; + } + assert( pPath->zOut[0]=='/' ); + while( pPath->zOut[--pPath->nUsed]!='/' ){} + return; + } } - if( buf.st_nlink>1 ){ - sqlcipher_sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath); + if( pPath->nUsed + nName + 2 >= pPath->nOut ){ + pPath->rc = SQLITE_ERROR; return; } - if( fileHasMoved(pFile) ){ - sqlcipher_sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath); - return; + pPath->zOut[pPath->nUsed++] = '/'; + memcpy(&pPath->zOut[pPath->nUsed], zName, nName); + pPath->nUsed += nName; +#if defined(HAVE_READLINK) && defined(HAVE_LSTAT) + if( pPath->rc==SQLITE_OK ){ + const char *zIn; + struct stat buf; + pPath->zOut[pPath->nUsed] = 0; + zIn = pPath->zOut; + if( osLstat(zIn, &buf)!=0 ){ + if( errno!=ENOENT ){ + pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn); + } + }else if( S_ISLNK(buf.st_mode) ){ + ssize_t got; + char zLnk[SQLITE_MAX_PATHLEN+2]; + if( pPath->nSymlink++ > SQLITE_MAX_SYMLINK ){ + pPath->rc = SQLITE_CANTOPEN_BKPT; + return; + } + got = osReadlink(zIn, zLnk, sizeof(zLnk)-2); + if( got<=0 || got>=(ssize_t)sizeof(zLnk)-2 ){ + pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn); + return; + } + zLnk[got] = 0; + if( zLnk[0]=='/' ){ + pPath->nUsed = 0; + }else{ + pPath->nUsed -= nName + 1; + } + appendAllPathElements(pPath, zLnk); + } } +#endif } - /* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. +** Append all path elements in zPath to the DbPath under construction. */ -static int unixCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ - int rc = SQLITE_OK; - int reserved = 0; - unixFile *pFile = (unixFile*)id; - - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - - assert( pFile ); - assert( pFile->eFileLock<=SHARED_LOCK ); - sqlcipher_sqlite3_mutex_enter(pFile->pInode->pLockMutex); - - /* Check if a thread in this process holds such a lock */ - if( pFile->pInode->eFileLock>SHARED_LOCK ){ - reserved = 1; - } - - /* Otherwise see if some other process holds it. - */ -#ifndef __DJGPP__ - if( !reserved && !pFile->pInode->bProcessLock ){ - struct flock lock; - lock.l_whence = SEEK_SET; - lock.l_start = RESERVED_BYTE; - lock.l_len = 1; - lock.l_type = F_WRLCK; - if( osFcntl(pFile->h, F_GETLK, &lock) ){ - rc = SQLITE_IOERR_CHECKRESERVEDLOCK; - storeLastErrno(pFile, errno); - } else if( lock.l_type!=F_UNLCK ){ - reserved = 1; +static void appendAllPathElements( + DbPath *pPath, /* Path under construction, to which to append zName */ + const char *zPath /* Path to append to pPath. Is zero-terminated */ +){ + int i = 0; + int j = 0; + do{ + while( zPath[i] && zPath[i]!='/' ){ i++; } + if( i>j ){ + appendOnePathElement(pPath, &zPath[j], i-j); } - } -#endif - - sqlcipher_sqlite3_mutex_leave(pFile->pInode->pLockMutex); - OSTRACE(("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved)); - - *pResOut = reserved; - return rc; + j = i+1; + }while( zPath[i++] ); } -/* Forward declaration*/ -static int unixSleep(sqlcipher_sqlite3_vfs*,int); - /* -** Set a posix-advisory-lock. -** -** There are two versions of this routine. If compiled with -** SQLITE_ENABLE_SETLK_TIMEOUT then the routine has an extra parameter -** which is a pointer to a unixFile. If the unixFile->iBusyTimeout -** value is set, then it is the number of milliseconds to wait before -** failing the lock. The iBusyTimeout value is always reset back to -** zero on each call. +** Turn a relative pathname into a full pathname. The relative path +** is stored as a nul-terminated string in the buffer pointed to by +** zPath. ** -** If SQLITE_ENABLE_SETLK_TIMEOUT is not defined, then do a non-blocking -** attempt to set the lock. +** zOut points to a buffer of at least sqlcipher_sqlite3_vfs.mxPathname bytes +** (in this case, MAX_PATHNAME bytes). The full-path is written to +** this buffer before returning. */ -#ifndef SQLITE_ENABLE_SETLK_TIMEOUT -# define osSetPosixAdvisoryLock(h,x,t) osFcntl(h,F_SETLK,x) -#else -static int osSetPosixAdvisoryLock( - int h, /* The file descriptor on which to take the lock */ - struct flock *pLock, /* The description of the lock */ - unixFile *pFile /* Structure holding timeout value */ +static int unixFullPathname( + sqlcipher_sqlite3_vfs *pVfs, /* Pointer to vfs object */ + const char *zPath, /* Possibly relative input path */ + int nOut, /* Size of output buffer in bytes */ + char *zOut /* Output buffer */ ){ - int tm = pFile->iBusyTimeout; - int rc = osFcntl(h,F_SETLK,pLock); - while( rc<0 && tm>0 ){ - /* On systems that support some kind of blocking file lock with a timeout, - ** make appropriate changes here to invoke that blocking file lock. On - ** generic posix, however, there is no such API. So we simply try the - ** lock once every millisecond until either the timeout expires, or until - ** the lock is obtained. */ - unixSleep(0,1000); - rc = osFcntl(h,F_SETLK,pLock); - tm--; + DbPath path; + UNUSED_PARAMETER(pVfs); + path.rc = 0; + path.nUsed = 0; + path.nSymlink = 0; + path.nOut = nOut; + path.zOut = zOut; + if( zPath[0]!='/' ){ + char zPwd[SQLITE_MAX_PATHLEN+2]; + if( osGetcwd(zPwd, sizeof(zPwd)-2)==0 ){ + return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); + } + appendAllPathElements(&path, zPwd); } - return rc; + appendAllPathElements(&path, zPath); + zOut[path.nUsed] = 0; + if( path.rc || path.nUsed<2 ) return SQLITE_CANTOPEN_BKPT; + if( path.nSymlink ) return SQLITE_OK_SYMLINK; + return SQLITE_OK; } -#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ - +#ifndef SQLITE_OMIT_LOAD_EXTENSION /* -** Attempt to set a system-lock on the file pFile. The lock is -** described by pLock. -** -** If the pFile was opened read/write from unix-excl, then the only lock -** ever obtained is an exclusive lock, and it is obtained exactly once -** the first time any lock is attempted. All subsequent system locking -** operations become no-ops. Locking operations still happen internally, -** in order to coordinate access between separate database connections -** within this process, but all of that is handled in memory and the -** operating system does not participate. -** -** This function is a pass-through to fcntl(F_SETLK) if pFile is using -** any VFS other than "unix-excl" or if pFile is opened on "unix-excl" -** and is read-only. -** -** Zero is returned if the call completes successfully, or -1 if a call -** to fcntl() fails. In this case, errno is set appropriately (by fcntl()). +** Interfaces for opening a shared library, finding entry points +** within the shared library, and closing the shared library. */ -static int unixFileLock(unixFile *pFile, struct flock *pLock){ - int rc; - unixInodeInfo *pInode = pFile->pInode; - assert( pInode!=0 ); - assert( sqlcipher_sqlite3_mutex_held(pInode->pLockMutex) ); - if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){ - if( pInode->bProcessLock==0 ){ - struct flock lock; - assert( pInode->nLock==0 ); - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - lock.l_type = F_WRLCK; - rc = osSetPosixAdvisoryLock(pFile->h, &lock, pFile); - if( rc<0 ) return rc; - pInode->bProcessLock = 1; - pInode->nLock++; - }else{ - rc = 0; - } - }else{ - rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile); - } - return rc; +#include +static void *unixDlOpen(sqlcipher_sqlite3_vfs *NotUsed, const char *zFilename){ + UNUSED_PARAMETER(NotUsed); + return dlopen(zFilename, RTLD_NOW | RTLD_GLOBAL); } /* -** Lock the file with the lock specified by parameter eFileLock - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** This routine will only increase a lock. Use the sqlcipher_sqlite3OsUnlock() -** routine to lower a locking level. +** SQLite calls this function immediately after a call to unixDlSym() or +** unixDlOpen() fails (returns a null pointer). If a more detailed error +** message is available, it is written to zBufOut. If no error message +** is available, zBufOut is left unmodified and SQLite uses a default +** error message. */ -static int unixLock(sqlcipher_sqlite3_file *id, int eFileLock){ - /* The following describes the implementation of the various locks and - ** lock transitions in terms of the POSIX advisory shared and exclusive - ** lock primitives (called read-locks and write-locks below, to avoid - ** confusion with SQLite lock names). The algorithms are complicated - ** slightly in order to be compatible with Windows95 systems simultaneously - ** accessing the same database file, in case that is ever required. - ** - ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved - ** byte', each single bytes at well known offsets, and the 'shared byte - ** range', a range of 510 bytes at a well known offset. - ** - ** To obtain a SHARED lock, a read-lock is obtained on the 'pending - ** byte'. If this is successful, 'shared byte range' is read-locked - ** and the lock on the 'pending byte' released. (Legacy note: When - ** SQLite was first developed, Windows95 systems were still very common, - ** and Widnows95 lacks a shared-lock capability. So on Windows95, a - ** single randomly selected by from the 'shared byte range' is locked. - ** Windows95 is now pretty much extinct, but this work-around for the - ** lack of shared-locks on Windows95 lives on, for backwards - ** compatibility.) - ** - ** A process may only obtain a RESERVED lock after it has a SHARED lock. - ** A RESERVED lock is implemented by grabbing a write-lock on the - ** 'reserved byte'. +static void unixDlError(sqlcipher_sqlite3_vfs *NotUsed, int nBuf, char *zBufOut){ + const char *zErr; + UNUSED_PARAMETER(NotUsed); + unixEnterMutex(); + zErr = dlerror(); + if( zErr ){ + sqlcipher_sqlite3_snprintf(nBuf, zBufOut, "%s", zErr); + } + unixLeaveMutex(); +} +static void (*unixDlSym(sqlcipher_sqlite3_vfs *NotUsed, void *p, const char*zSym))(void){ + /* + ** GCC with -pedantic-errors says that C90 does not allow a void* to be + ** cast into a pointer to a function. And yet the library dlsym() routine + ** returns a void* which is really a pointer to a function. So how do we + ** use dlsym() with -pedantic-errors? ** - ** A process may only obtain a PENDING lock after it has obtained a - ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock - ** on the 'pending byte'. This ensures that no new SHARED locks can be - ** obtained, but existing SHARED locks are allowed to persist. A process - ** does not have to obtain a RESERVED lock on the way to a PENDING lock. - ** This property is used by the algorithm for rolling back a journal file - ** after a crash. + ** Variable x below is defined to be a pointer to a function taking + ** parameters void* and const char* and returning a pointer to a function. + ** We initialize x by assigning it a pointer to the dlsym() function. + ** (That assignment requires a cast.) Then we call the function that + ** x points to. ** - ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is - ** implemented by obtaining a write-lock on the entire 'shared byte - ** range'. Since all other locks require a read-lock on one of the bytes - ** within this range, this ensures that no other locks are held on the - ** database. + ** This work-around is unlikely to work correctly on any system where + ** you really cannot cast a function pointer into void*. But then, on the + ** other hand, dlsym() will not work on such a system either, so we have + ** not really lost anything. */ - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; - unixInodeInfo *pInode; - struct flock lock; - int tErrno = 0; + void (*(*x)(void*,const char*))(void); + UNUSED_PARAMETER(NotUsed); + x = (void(*(*)(void*,const char*))(void))dlsym; + return (*x)(p, zSym); +} +static void unixDlClose(sqlcipher_sqlite3_vfs *NotUsed, void *pHandle){ + UNUSED_PARAMETER(NotUsed); + dlclose(pHandle); +} +#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ + #define unixDlOpen 0 + #define unixDlError 0 + #define unixDlSym 0 + #define unixDlClose 0 +#endif - assert( pFile ); - OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, - azFileLock(eFileLock), azFileLock(pFile->eFileLock), - azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared, - osGetpid(0))); +/* +** Write nBuf bytes of random data to the supplied buffer zBuf. +*/ +static int unixRandomness(sqlcipher_sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ + UNUSED_PARAMETER(NotUsed); + assert((size_t)nBuf>=(sizeof(time_t)+sizeof(int))); - /* If there is already a lock of this type or more restrictive on the - ** unixFile, do nothing. Don't use the end_lock: exit path, as - ** unixEnterMutex() hasn't been called yet. + /* We have to initialize zBuf to prevent valgrind from reporting + ** errors. The reports issued by valgrind are incorrect - we would + ** prefer that the randomness be increased by making use of the + ** uninitialized space in zBuf - but valgrind errors tend to worry + ** some users. Rather than argue, it seems easier just to initialize + ** the whole array and silence valgrind, even if that means less randomness + ** in the random seed. + ** + ** When testing, initializing zBuf[] to zero is all we do. That means + ** that we always use the same random number sequence. This makes the + ** tests repeatable. */ - if( pFile->eFileLock>=eFileLock ){ - OSTRACE(("LOCK %d %s ok (already held) (unix)\n", pFile->h, - azFileLock(eFileLock))); - return SQLITE_OK; + memset(zBuf, 0, nBuf); + randomnessPid = osGetpid(0); +#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) + { + int fd, got; + fd = robust_open("/dev/urandom", O_RDONLY, 0); + if( fd<0 ){ + time_t t; + time(&t); + memcpy(zBuf, &t, sizeof(t)); + memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid)); + assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf ); + nBuf = sizeof(t) + sizeof(randomnessPid); + }else{ + do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR ); + robust_close(0, fd, __LINE__); + } } +#endif + return nBuf; +} - /* Make sure the locking sequence is correct. - ** (1) We never move from unlocked to anything higher than shared lock. - ** (2) SQLite never explicitly requests a pendig lock. - ** (3) A shared lock is always held when a reserve lock is requested. - */ - assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); - assert( eFileLock!=PENDING_LOCK ); - assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK ); - /* This mutex is needed because pFile->pInode is shared across threads - */ - pInode = pFile->pInode; - sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); +/* +** Sleep for a little while. Return the amount of time slept. +** The argument is the number of microseconds we want to sleep. +** The return value is the number of microseconds of sleep actually +** requested from the underlying operating system, a number which +** might be greater than or equal to the argument, but not less +** than the argument. +*/ +static int unixSleep(sqlcipher_sqlite3_vfs *NotUsed, int microseconds){ +#if OS_VXWORKS + struct timespec sp; - /* If some thread using this PID has a lock via a different unixFile* - ** handle that precludes the requested lock, return BUSY. - */ - if( (pFile->eFileLock!=pInode->eFileLock && - (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) - ){ - rc = SQLITE_BUSY; - goto end_lock; - } + sp.tv_sec = microseconds / 1000000; + sp.tv_nsec = (microseconds % 1000000) * 1000; + nanosleep(&sp, NULL); + UNUSED_PARAMETER(NotUsed); + return microseconds; +#elif defined(HAVE_USLEEP) && HAVE_USLEEP + if( microseconds>=1000000 ) sleep(microseconds/1000000); + if( microseconds%1000000 ) usleep(microseconds%1000000); + UNUSED_PARAMETER(NotUsed); + return microseconds; +#else + int seconds = (microseconds+999999)/1000000; + sleep(seconds); + UNUSED_PARAMETER(NotUsed); + return seconds*1000000; +#endif +} - /* If a SHARED lock is requested, and some thread using this PID already - ** has a SHARED or RESERVED lock, then increment reference counts and - ** return SQLITE_OK. - */ - if( eFileLock==SHARED_LOCK && - (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ - assert( eFileLock==SHARED_LOCK ); - assert( pFile->eFileLock==0 ); - assert( pInode->nShared>0 ); - pFile->eFileLock = SHARED_LOCK; - pInode->nShared++; - pInode->nLock++; - goto end_lock; - } +/* +** The following variable, if set to a non-zero value, is interpreted as +** the number of seconds since 1970 and is used to set the result of +** sqlcipher_sqlite3OsCurrentTime() during testing. +*/ +#ifdef SQLITE_TEST +SQLITE_API int sqlcipher_sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ +#endif +/* +** Find the current time (in Universal Coordinated Time). Write into *piNow +** the current time and date as a Julian Day number times 86_400_000. In +** other words, write into *piNow the number of milliseconds since the Julian +** epoch of noon in Greenwich on November 24, 4714 B.C according to the +** proleptic Gregorian calendar. +** +** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date +** cannot be found. +*/ +static int unixCurrentTimeInt64(sqlcipher_sqlite3_vfs *NotUsed, sqlcipher_sqlite3_int64 *piNow){ + static const sqlcipher_sqlite3_int64 unixEpoch = 24405875*(sqlcipher_sqlite3_int64)8640000; + int rc = SQLITE_OK; +#if defined(NO_GETTOD) + time_t t; + time(&t); + *piNow = ((sqlcipher_sqlite3_int64)t)*1000 + unixEpoch; +#elif OS_VXWORKS + struct timespec sNow; + clock_gettime(CLOCK_REALTIME, &sNow); + *piNow = unixEpoch + 1000*(sqlcipher_sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000; +#else + struct timeval sNow; + (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */ + *piNow = unixEpoch + 1000*(sqlcipher_sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; +#endif - /* A PENDING lock is needed before acquiring a SHARED lock and before - ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will - ** be released. - */ - lock.l_len = 1L; - lock.l_whence = SEEK_SET; - if( eFileLock==SHARED_LOCK - || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLocknShared==0 ); - assert( pInode->eFileLock==0 ); - assert( rc==SQLITE_OK ); +/* +** The xGetLastError() method is designed to return a better +** low-level error message when operating-system problems come up +** during SQLite operation. Only the integer return code is currently +** used. +*/ +static int unixGetLastError(sqlcipher_sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ + UNUSED_PARAMETER(NotUsed); + UNUSED_PARAMETER(NotUsed2); + UNUSED_PARAMETER(NotUsed3); + return errno; +} - /* Now get the read-lock */ - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - if( unixFileLock(pFile, &lock) ){ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - } - /* Drop the temporary PENDING lock */ - lock.l_start = PENDING_BYTE; - lock.l_len = 1L; - lock.l_type = F_UNLCK; - if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){ - /* This could happen with a network mount */ - tErrno = errno; - rc = SQLITE_IOERR_UNLOCK; - } +/* +************************ End of sqlcipher_sqlite3_vfs methods *************************** +******************************************************************************/ - if( rc ){ - if( rc!=SQLITE_BUSY ){ - storeLastErrno(pFile, tErrno); - } - goto end_lock; - }else{ - pFile->eFileLock = SHARED_LOCK; - pInode->nLock++; - pInode->nShared = 1; - } - }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ - /* We are trying for an exclusive lock but another thread in this - ** same process is still holding a shared lock. */ - rc = SQLITE_BUSY; - }else{ - /* The request was for a RESERVED or EXCLUSIVE lock. It is - ** assumed that there is a SHARED or greater lock on the file - ** already. - */ - assert( 0!=pFile->eFileLock ); - lock.l_type = F_WRLCK; +/****************************************************************************** +************************** Begin Proxy Locking ******************************** +** +** Proxy locking is a "uber-locking-method" in this sense: It uses the +** other locking methods on secondary lock files. Proxy locking is a +** meta-layer over top of the primitive locking implemented above. For +** this reason, the division that implements of proxy locking is deferred +** until late in the file (here) after all of the other I/O methods have +** been defined - so that the primitive locking methods are available +** as services to help with the implementation of proxy locking. +** +**** +** +** The default locking schemes in SQLite use byte-range locks on the +** database file to coordinate safe, concurrent access by multiple readers +** and writers [http://sqlite.org/lockingv3.html]. The five file locking +** states (UNLOCKED, PENDING, SHARED, RESERVED, EXCLUSIVE) are implemented +** as POSIX read & write locks over fixed set of locations (via fsctl), +** on AFP and SMB only exclusive byte-range locks are available via fsctl +** with _IOWR('z', 23, struct ByteRangeLockPB2) to track the same 5 states. +** To simulate a F_RDLCK on the shared range, on AFP a randomly selected +** address in the shared range is taken for a SHARED lock, the entire +** shared range is taken for an EXCLUSIVE lock): +** +** PENDING_BYTE 0x40000000 +** RESERVED_BYTE 0x40000001 +** SHARED_RANGE 0x40000002 -> 0x40000200 +** +** This works well on the local file system, but shows a nearly 100x +** slowdown in read performance on AFP because the AFP client disables +** the read cache when byte-range locks are present. Enabling the read +** cache exposes a cache coherency problem that is present on all OS X +** supported network file systems. NFS and AFP both observe the +** close-to-open semantics for ensuring cache coherency +** [http://nfs.sourceforge.net/#faq_a8], which does not effectively +** address the requirements for concurrent database access by multiple +** readers and writers +** [http://www.nabble.com/SQLite-on-NFS-cache-coherency-td15655701.html]. +** +** To address the performance and cache coherency issues, proxy file locking +** changes the way database access is controlled by limiting access to a +** single host at a time and moving file locks off of the database file +** and onto a proxy file on the local file system. +** +** +** Using proxy locks +** ----------------- +** +** C APIs +** +** sqlcipher_sqlite3_file_control(db, dbname, SQLITE_FCNTL_SET_LOCKPROXYFILE, +** | ":auto:"); +** sqlcipher_sqlite3_file_control(db, dbname, SQLITE_FCNTL_GET_LOCKPROXYFILE, +** &); +** +** +** SQL pragmas +** +** PRAGMA [database.]lock_proxy_file= | :auto: +** PRAGMA [database.]lock_proxy_file +** +** Specifying ":auto:" means that if there is a conch file with a matching +** host ID in it, the proxy path in the conch file will be used, otherwise +** a proxy path based on the user's temp dir +** (via confstr(_CS_DARWIN_USER_TEMP_DIR,...)) will be used and the +** actual proxy file name is generated from the name and path of the +** database file. For example: +** +** For database path "/Users/me/foo.db" +** The lock path will be "/sqliteplocks/_Users_me_foo.db:auto:") +** +** Once a lock proxy is configured for a database connection, it can not +** be removed, however it may be switched to a different proxy path via +** the above APIs (assuming the conch file is not being held by another +** connection or process). +** +** +** How proxy locking works +** ----------------------- +** +** Proxy file locking relies primarily on two new supporting files: +** +** * conch file to limit access to the database file to a single host +** at a time +** +** * proxy file to act as a proxy for the advisory locks normally +** taken on the database +** +** The conch file - to use a proxy file, sqlite must first "hold the conch" +** by taking an sqlite-style shared lock on the conch file, reading the +** contents and comparing the host's unique host ID (see below) and lock +** proxy path against the values stored in the conch. The conch file is +** stored in the same directory as the database file and the file name +** is patterned after the database file name as ".-conch". +** If the conch file does not exist, or its contents do not match the +** host ID and/or proxy path, then the lock is escalated to an exclusive +** lock and the conch file contents is updated with the host ID and proxy +** path and the lock is downgraded to a shared lock again. If the conch +** is held by another process (with a shared lock), the exclusive lock +** will fail and SQLITE_BUSY is returned. +** +** The proxy file - a single-byte file used for all advisory file locks +** normally taken on the database file. This allows for safe sharing +** of the database file for multiple readers and writers on the same +** host (the conch ensures that they all use the same local lock file). +** +** Requesting the lock proxy does not immediately take the conch, it is +** only taken when the first request to lock database file is made. +** This matches the semantics of the traditional locking behavior, where +** opening a connection to a database file does not take a lock on it. +** The shared lock and an open file descriptor are maintained until +** the connection to the database is closed. +** +** The proxy file and the lock file are never deleted so they only need +** to be created the first time they are used. +** +** Configuration options +** --------------------- +** +** SQLITE_PREFER_PROXY_LOCKING +** +** Database files accessed on non-local file systems are +** automatically configured for proxy locking, lock files are +** named automatically using the same logic as +** PRAGMA lock_proxy_file=":auto:" +** +** SQLITE_PROXY_DEBUG +** +** Enables the logging of error messages during host id file +** retrieval and creation +** +** LOCKPROXYDIR +** +** Overrides the default directory used for lock proxy files that +** are named automatically via the ":auto:" setting +** +** SQLITE_DEFAULT_PROXYDIR_PERMISSIONS +** +** Permissions to use when creating a directory for storing the +** lock proxy files, only used when LOCKPROXYDIR is not set. +** +** +** As mentioned above, when compiled with SQLITE_PREFER_PROXY_LOCKING, +** setting the environment variable SQLITE_FORCE_PROXY_LOCKING to 1 will +** force proxy locking to be used for every database file opened, and 0 +** will force automatic proxy locking to be disabled for all database +** files (explicitly calling the SQLITE_FCNTL_SET_LOCKPROXYFILE pragma or +** sqlite_file_control API is not affected by SQLITE_FORCE_PROXY_LOCKING). +*/ - assert( eFileLock==RESERVED_LOCK || eFileLock==EXCLUSIVE_LOCK ); - if( eFileLock==RESERVED_LOCK ){ - lock.l_start = RESERVED_BYTE; - lock.l_len = 1L; - }else{ - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - } +/* +** Proxy locking is only available on MacOSX +*/ +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE - if( unixFileLock(pFile, &lock) ){ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( rc!=SQLITE_BUSY ){ - storeLastErrno(pFile, tErrno); - } - } - } +/* +** The proxyLockingContext has the path and file structures for the remote +** and local proxy files in it +*/ +typedef struct proxyLockingContext proxyLockingContext; +struct proxyLockingContext { + unixFile *conchFile; /* Open conch file */ + char *conchFilePath; /* Name of the conch file */ + unixFile *lockProxy; /* Open proxy lock file */ + char *lockProxyPath; /* Name of the proxy lock file */ + char *dbPath; /* Name of the open file */ + int conchHeld; /* 1 if the conch is held, -1 if lockless */ + int nFails; /* Number of conch taking failures */ + void *oldLockingContext; /* Original lockingcontext to restore on close */ + sqlcipher_sqlite3_io_methods const *pOldMethod; /* Original I/O methods for close */ +}; +/* +** The proxy lock file path for the database at dbPath is written into lPath, +** which must point to valid, writable memory large enough for a maxLen length +** file path. +*/ +static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){ + int len; + int dbLen; + int i; -#ifdef SQLITE_DEBUG - /* Set up the transaction-counter change checking flags when - ** transitioning from a SHARED to a RESERVED lock. The change - ** from SHARED to RESERVED marks the beginning of a normal - ** write operation (not a hot journal rollback). - */ - if( rc==SQLITE_OK - && pFile->eFileLock<=SHARED_LOCK - && eFileLock==RESERVED_LOCK - ){ - pFile->transCntrChng = 0; - pFile->dbUpdate = 0; - pFile->inNormalWrite = 1; +#ifdef LOCKPROXYDIR + len = strlcpy(lPath, LOCKPROXYDIR, maxLen); +#else +# ifdef _CS_DARWIN_USER_TEMP_DIR + { + if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){ + OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n", + lPath, errno, osGetpid(0))); + return SQLITE_IOERR_LOCK; + } + len = strlcat(lPath, "sqliteplocks", maxLen); } +# else + len = strlcpy(lPath, "/tmp/", maxLen); +# endif #endif - - if( rc==SQLITE_OK ){ - pFile->eFileLock = eFileLock; - pInode->eFileLock = eFileLock; - }else if( eFileLock==EXCLUSIVE_LOCK ){ - pFile->eFileLock = PENDING_LOCK; - pInode->eFileLock = PENDING_LOCK; + if( lPath[len-1]!='/' ){ + len = strlcat(lPath, "/", maxLen); } -end_lock: - sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); - OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock), - rc==SQLITE_OK ? "ok" : "failed")); - return rc; + /* transform the db path to a unique cache name */ + dbLen = (int)strlen(dbPath); + for( i=0; ipInode; - UnixUnusedFd *p = pFile->pPreallocatedUnused; - assert( unixFileMutexHeld(pFile) ); - p->pNext = pInode->pUnused; - pInode->pUnused = p; - pFile->h = -1; - pFile->pPreallocatedUnused = 0; + ** Creates the lock file and any missing directories in lockPath + */ +static int proxyCreateLockPath(const char *lockPath){ + int i, len; + char buf[MAXPATHLEN]; + int start = 0; + + assert(lockPath!=NULL); + /* try to create all the intermediate directories */ + len = (int)strlen(lockPath); + buf[0] = lockPath[0]; + for( i=1; i 0) ){ + /* only mkdir if leaf dir != "." or "/" or ".." */ + if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/') + || (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){ + buf[i]='\0'; + if( osMkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){ + int err=errno; + if( err!=EEXIST ) { + OSTRACE(("CREATELOCKPATH FAILED creating %s, " + "'%s' proxy lock path=%s pid=%d\n", + buf, strerror(err), lockPath, osGetpid(0))); + return err; + } + } + } + start=i+1; + } + buf[i] = lockPath[i]; + } + OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n",lockPath,osGetpid(0))); + return 0; } /* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. +** Create a new VFS file descriptor (stored in memory obtained from +** sqlcipher_sqlite3_malloc) and open the file named "path" in the file descriptor. ** -** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED -** the byte range is divided into 2 parts and the first part is unlocked then -** set to a read lock, then the other part is simply unlocked. This works -** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to -** remove the write lock on a region when a read lock is set. +** The caller is responsible not only for closing the file descriptor +** but also for freeing the memory associated with the file descriptor. */ -static int posixUnlock(sqlcipher_sqlite3_file *id, int eFileLock, int handleNFSUnlock){ - unixFile *pFile = (unixFile*)id; - unixInodeInfo *pInode; - struct flock lock; +static int proxyCreateUnixFile( + const char *path, /* path for the new unixFile */ + unixFile **ppFile, /* unixFile created and returned by ref */ + int islockfile /* if non zero missing dirs will be created */ +) { + int fd = -1; + unixFile *pNew; int rc = SQLITE_OK; + int openFlags = O_RDWR | O_CREAT | O_NOFOLLOW; + sqlcipher_sqlite3_vfs dummyVfs; + int terrno = 0; + UnixUnusedFd *pUnused = NULL; - assert( pFile ); - OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, - pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, - osGetpid(0))); - - assert( eFileLock<=SHARED_LOCK ); - if( pFile->eFileLock<=eFileLock ){ - return SQLITE_OK; + /* 1. first try to open/create the file + ** 2. if that fails, and this is a lock file (not-conch), try creating + ** the parent directories and then try again. + ** 3. if that fails, try to open the file read-only + ** otherwise return BUSY (if lock file) or CANTOPEN for the conch file + */ + pUnused = findReusableFd(path, openFlags); + if( pUnused ){ + fd = pUnused->fd; + }else{ + pUnused = sqlcipher_sqlite3_malloc64(sizeof(*pUnused)); + if( !pUnused ){ + return SQLITE_NOMEM_BKPT; + } } - pInode = pFile->pInode; - sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); - assert( pInode->nShared!=0 ); - if( pFile->eFileLock>SHARED_LOCK ){ - assert( pInode->eFileLock==pFile->eFileLock ); - -#ifdef SQLITE_DEBUG - /* When reducing a lock such that other processes can start - ** reading the database file again, make sure that the - ** transaction counter was updated if any part of the database - ** file changed. If the transaction counter is not updated, - ** other connections to the same file might not realize that - ** the file has changed and hence might not know to flush their - ** cache. The use of a stale cache can lead to database corruption. - */ - pFile->inNormalWrite = 0; -#endif - - /* downgrading to a shared lock on NFS involves clearing the write lock - ** before establishing the readlock - to avoid a race condition we downgrade - ** the lock in 2 blocks, so that part of the range will be covered by a - ** write lock until the rest is covered by a read lock: - ** 1: [WWWWW] - ** 2: [....W] - ** 3: [RRRRW] - ** 4: [RRRR.] - */ - if( eFileLock==SHARED_LOCK ){ -#if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE - (void)handleNFSUnlock; - assert( handleNFSUnlock==0 ); -#endif -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE - if( handleNFSUnlock ){ - int tErrno; /* Error code from system call errors */ - off_t divSize = SHARED_SIZE - 1; - - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = divSize; - if( unixFileLock(pFile, &lock)==(-1) ){ - tErrno = errno; - rc = SQLITE_IOERR_UNLOCK; - storeLastErrno(pFile, tErrno); - goto end_unlock; - } - lock.l_type = F_RDLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = divSize; - if( unixFileLock(pFile, &lock)==(-1) ){ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); - if( IS_LOCK_ERROR(rc) ){ - storeLastErrno(pFile, tErrno); - } - goto end_unlock; - } - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST+divSize; - lock.l_len = SHARED_SIZE-divSize; - if( unixFileLock(pFile, &lock)==(-1) ){ - tErrno = errno; - rc = SQLITE_IOERR_UNLOCK; - storeLastErrno(pFile, tErrno); - goto end_unlock; - } - }else -#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ - { - lock.l_type = F_RDLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - if( unixFileLock(pFile, &lock) ){ - /* In theory, the call to unixFileLock() cannot fail because another - ** process is holding an incompatible lock. If it does, this - ** indicates that the other process is not following the locking - ** protocol. If this happens, return SQLITE_IOERR_RDLOCK. Returning - ** SQLITE_BUSY would confuse the upper layer (in practice it causes - ** an assert to fail). */ - rc = SQLITE_IOERR_RDLOCK; - storeLastErrno(pFile, errno); - goto end_unlock; - } + if( fd<0 ){ + fd = robust_open(path, openFlags, 0); + terrno = errno; + if( fd<0 && errno==ENOENT && islockfile ){ + if( proxyCreateLockPath(path) == SQLITE_OK ){ + fd = robust_open(path, openFlags, 0); } } - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = PENDING_BYTE; - lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); - if( unixFileLock(pFile, &lock)==0 ){ - pInode->eFileLock = SHARED_LOCK; - }else{ - rc = SQLITE_IOERR_UNLOCK; - storeLastErrno(pFile, errno); - goto end_unlock; - } } - if( eFileLock==NO_LOCK ){ - /* Decrement the shared lock counter. Release the lock using an - ** OS call only when all threads in this same process have released - ** the lock. - */ - pInode->nShared--; - if( pInode->nShared==0 ){ - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = lock.l_len = 0L; - if( unixFileLock(pFile, &lock)==0 ){ - pInode->eFileLock = NO_LOCK; - }else{ - rc = SQLITE_IOERR_UNLOCK; - storeLastErrno(pFile, errno); - pInode->eFileLock = NO_LOCK; - pFile->eFileLock = NO_LOCK; - } + if( fd<0 ){ + openFlags = O_RDONLY | O_NOFOLLOW; + fd = robust_open(path, openFlags, 0); + terrno = errno; + } + if( fd<0 ){ + if( islockfile ){ + return SQLITE_BUSY; + } + switch (terrno) { + case EACCES: + return SQLITE_PERM; + case EIO: + return SQLITE_IOERR_LOCK; /* even though it is the conch */ + default: + return SQLITE_CANTOPEN_BKPT; } + } - /* Decrement the count of locks against this same file. When the - ** count reaches zero, close any other file descriptors whose close - ** was deferred because of outstanding locks. - */ - pInode->nLock--; - assert( pInode->nLock>=0 ); - if( pInode->nLock==0 ) closePendingFds(pFile); + pNew = (unixFile *)sqlcipher_sqlite3_malloc64(sizeof(*pNew)); + if( pNew==NULL ){ + rc = SQLITE_NOMEM_BKPT; + goto end_create_proxy; } + memset(pNew, 0, sizeof(unixFile)); + pNew->openFlags = openFlags; + memset(&dummyVfs, 0, sizeof(dummyVfs)); + dummyVfs.pAppData = (void*)&autolockIoFinder; + dummyVfs.zName = "dummy"; + pUnused->fd = fd; + pUnused->flags = openFlags; + pNew->pPreallocatedUnused = pUnused; -end_unlock: - sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); + rc = fillInUnixFile(&dummyVfs, fd, (sqlcipher_sqlite3_file*)pNew, path, 0); if( rc==SQLITE_OK ){ - pFile->eFileLock = eFileLock; + *ppFile = pNew; + return SQLITE_OK; } +end_create_proxy: + robust_close(pNew, fd, __LINE__); + sqlcipher_sqlite3_free(pNew); + sqlcipher_sqlite3_free(pUnused); return rc; } -/* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -*/ -static int unixUnlock(sqlcipher_sqlite3_file *id, int eFileLock){ -#if SQLITE_MAX_MMAP_SIZE>0 - assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 ); +#ifdef SQLITE_TEST +/* simulate multiple hosts by creating unique hostid file paths */ +SQLITE_API int sqlcipher_sqlite3_hostid_num = 0; #endif - return posixUnlock(id, eFileLock, 0); -} -#if SQLITE_MAX_MMAP_SIZE>0 -static int unixMapfile(unixFile *pFd, i64 nByte); -static void unixUnmapfile(unixFile *pFd); +#define PROXY_HOSTIDLEN 16 /* conch file host id length */ + +#if HAVE_GETHOSTUUID +/* Not always defined in the headers as it ought to be */ +extern int gethostuuid(uuid_t id, const struct timespec *wait); #endif -/* -** This function performs the parts of the "close file" operation -** common to all locking schemes. It closes the directory and file -** handles, if they are valid, and sets all fields of the unixFile -** structure to 0. -** -** It is *not* necessary to hold the mutex when this routine is called, -** even on VxWorks. A mutex will be acquired on VxWorks by the -** vxworksReleaseFileId() routine. +/* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN +** bytes of writable memory. */ -static int closeUnixFile(sqlcipher_sqlite3_file *id){ - unixFile *pFile = (unixFile*)id; -#if SQLITE_MAX_MMAP_SIZE>0 - unixUnmapfile(pFile); -#endif - if( pFile->h>=0 ){ - robust_close(pFile, pFile->h, __LINE__); - pFile->h = -1; - } -#if OS_VXWORKS - if( pFile->pId ){ - if( pFile->ctrlFlags & UNIXFILE_DELETE ){ - osUnlink(pFile->pId->zCanonicalName); +static int proxyGetHostID(unsigned char *pHostID, int *pError){ + assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); + memset(pHostID, 0, PROXY_HOSTIDLEN); +#if HAVE_GETHOSTUUID + { + struct timespec timeout = {1, 0}; /* 1 sec timeout */ + if( gethostuuid(pHostID, &timeout) ){ + int err = errno; + if( pError ){ + *pError = err; + } + return SQLITE_IOERR; } - vxworksReleaseFileId(pFile->pId); - pFile->pId = 0; } +#else + UNUSED_PARAMETER(pError); #endif -#ifdef SQLITE_UNLINK_AFTER_CLOSE - if( pFile->ctrlFlags & UNIXFILE_DELETE ){ - osUnlink(pFile->zPath); - sqlcipher_sqlite3_free(*(char**)&pFile->zPath); - pFile->zPath = 0; +#ifdef SQLITE_TEST + /* simulate multiple hosts by creating unique hostid file paths */ + if( sqlcipher_sqlite3_hostid_num != 0){ + pHostID[0] = (char)(pHostID[0] + (char)(sqlcipher_sqlite3_hostid_num & 0xFF)); } #endif - OSTRACE(("CLOSE %-3d\n", pFile->h)); - OpenCounter(-1); - sqlcipher_sqlite3_free(pFile->pPreallocatedUnused); - memset(pFile, 0, sizeof(unixFile)); + return SQLITE_OK; } +/* The conch file contains the header, host id and lock file path + */ +#define PROXY_CONCHVERSION 2 /* 1-byte header, 16-byte host id, path */ +#define PROXY_HEADERLEN 1 /* conch file header length */ +#define PROXY_PATHINDEX (PROXY_HEADERLEN+PROXY_HOSTIDLEN) +#define PROXY_MAXCONCHLEN (PROXY_HEADERLEN+PROXY_HOSTIDLEN+MAXPATHLEN) + /* -** Close a file. +** Takes an open conch file, copies the contents to a new path and then moves +** it back. The newly created file's file descriptor is assigned to the +** conch file structure and finally the original conch file descriptor is +** closed. Returns zero if successful. */ -static int unixClose(sqlcipher_sqlite3_file *id){ - int rc = SQLITE_OK; - unixFile *pFile = (unixFile *)id; - unixInodeInfo *pInode = pFile->pInode; +static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){ + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; + unixFile *conchFile = pCtx->conchFile; + char tPath[MAXPATHLEN]; + char buf[PROXY_MAXCONCHLEN]; + char *cPath = pCtx->conchFilePath; + size_t readLen = 0; + size_t pathLen = 0; + char errmsg[64] = ""; + int fd = -1; + int rc = -1; + UNUSED_PARAMETER(myHostID); - assert( pInode!=0 ); - verifyDbFile(pFile); - unixUnlock(id, NO_LOCK); - assert( unixFileMutexNotheld(pFile) ); - unixEnterMutex(); + /* create a new path by replace the trailing '-conch' with '-break' */ + pathLen = strlcpy(tPath, cPath, MAXPATHLEN); + if( pathLen>MAXPATHLEN || pathLen<6 || + (strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){ + sqlcipher_sqlite3_snprintf(sizeof(errmsg),errmsg,"path error (len %d)",(int)pathLen); + goto end_breaklock; + } + /* read the conch content */ + readLen = osPread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); + if( readLenh, __LINE__); + conchFile->h = fd; + conchFile->openFlags = O_RDWR | O_CREAT; - /* unixFile.pInode is always valid here. Otherwise, a different close - ** routine (e.g. nolockClose()) would be called instead. - */ - assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 ); - sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); - if( pInode->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->pUnused list. It will be automatically closed - ** when the last lock is cleared. - */ - setPendingFd(pFile); +end_breaklock: + if( rc ){ + if( fd>=0 ){ + osUnlink(tPath); + robust_close(pFile, fd, __LINE__); + } + fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg); } - sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); - releaseInodeInfo(pFile); - assert( pFile->pShm==0 ); - rc = closeUnixFile(id); - unixLeaveMutex(); return rc; } -/************** End of the posix advisory lock implementation ***************** -******************************************************************************/ - -/****************************************************************************** -****************************** No-op Locking ********************************** -** -** Of the various locking implementations available, this is by far the -** simplest: locking is ignored. No attempt is made to lock the database -** file for reading or writing. -** -** This locking mode is appropriate for use on read-only databases -** (ex: databases that are burned into CD-ROM, for example.) It can -** also be used if the application employs some external mechanism to -** prevent simultaneous access of the same database by two or more -** database connections. But there is a serious risk of database -** corruption if this locking mode is used in situations where multiple -** database connections are accessing the same database file at the same -** time and one or more of those connections are writing. -*/ - -static int nolockCheckReservedLock(sqlcipher_sqlite3_file *NotUsed, int *pResOut){ - UNUSED_PARAMETER(NotUsed); - *pResOut = 0; - return SQLITE_OK; -} -static int nolockLock(sqlcipher_sqlite3_file *NotUsed, int NotUsed2){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - return SQLITE_OK; -} -static int nolockUnlock(sqlcipher_sqlite3_file *NotUsed, int NotUsed2){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - return SQLITE_OK; -} - -/* -** Close the file. -*/ -static int nolockClose(sqlcipher_sqlite3_file *id) { - return closeUnixFile(id); -} - -/******************* End of the no-op lock implementation ********************* -******************************************************************************/ - -/****************************************************************************** -************************* Begin dot-file Locking ****************************** -** -** The dotfile locking implementation uses the existence of separate lock -** files (really a directory) to control access to the database. This works -** on just about every filesystem imaginable. But there are serious downsides: -** -** (1) There is zero concurrency. A single reader blocks all other -** connections from reading or writing the database. -** -** (2) An application crash or power loss can leave stale lock files -** sitting around that need to be cleared manually. -** -** Nevertheless, a dotlock is an appropriate locking mode for use if no -** other locking strategy is available. -** -** Dotfile locking works by creating a subdirectory in the same directory as -** the database and with the same name but with a ".lock" extension added. -** The existence of a lock directory implies an EXCLUSIVE lock. All other -** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE. -*/ - -/* -** The file suffix added to the data base filename in order to create the -** lock directory. -*/ -#define DOTLOCK_SUFFIX ".lock" - -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. -** -** In dotfile locking, either a lock exists or it does not. So in this -** variation of CheckReservedLock(), *pResOut is set to true if any lock -** is held on the file and false if the file is unlocked. +/* Take the requested lock on the conch file and break a stale lock if the +** host id matches. */ -static int dotlockCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut) { +static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; + unixFile *conchFile = pCtx->conchFile; int rc = SQLITE_OK; - int reserved = 0; - unixFile *pFile = (unixFile*)id; - - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); + int nTries = 0; + struct timespec conchModTime; - assert( pFile ); - reserved = osAccess((const char*)pFile->lockingContext, 0)==0; - OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); - *pResOut = reserved; - return rc; -} + memset(&conchModTime, 0, sizeof(conchModTime)); + do { + rc = conchFile->pMethod->xLock((sqlcipher_sqlite3_file*)conchFile, lockType); + nTries ++; + if( rc==SQLITE_BUSY ){ + /* If the lock failed (busy): + * 1st try: get the mod time of the conch, wait 0.5s and try again. + * 2nd try: fail if the mod time changed or host id is different, wait + * 10 sec and try again + * 3rd try: break the lock unless the mod time has changed. + */ + struct stat buf; + if( osFstat(conchFile->h, &buf) ){ + storeLastErrno(pFile, errno); + return SQLITE_IOERR_LOCK; + } -/* -** Lock the file with the lock specified by parameter eFileLock - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE -** -** This routine will only increase a lock. Use the sqlcipher_sqlite3OsUnlock() -** routine to lower a locking level. -** -** With dotfile locking, we really only support state (4): EXCLUSIVE. -** But we track the other locking levels internally. -*/ -static int dotlockLock(sqlcipher_sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; - char *zLockFile = (char *)pFile->lockingContext; - int rc = SQLITE_OK; + if( nTries==1 ){ + conchModTime = buf.st_mtimespec; + unixSleep(0,500000); /* wait 0.5 sec and try the lock again*/ + continue; + } + assert( nTries>1 ); + if( conchModTime.tv_sec != buf.st_mtimespec.tv_sec || + conchModTime.tv_nsec != buf.st_mtimespec.tv_nsec ){ + return SQLITE_BUSY; + } - /* If we have any lock, then the lock file already exists. All we have - ** to do is adjust our internal record of the lock level. - */ - if( pFile->eFileLock > NO_LOCK ){ - pFile->eFileLock = eFileLock; - /* Always update the timestamp on the old file */ -#ifdef HAVE_UTIME - utime(zLockFile, NULL); -#else - utimes(zLockFile, NULL); -#endif - return SQLITE_OK; - } + if( nTries==2 ){ + char tBuf[PROXY_MAXCONCHLEN]; + int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); + if( len<0 ){ + storeLastErrno(pFile, errno); + return SQLITE_IOERR_LOCK; + } + if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){ + /* don't break the lock if the host id doesn't match */ + if( 0!=memcmp(&tBuf[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN) ){ + return SQLITE_BUSY; + } + }else{ + /* don't break the lock on short read or a version mismatch */ + return SQLITE_BUSY; + } + unixSleep(0,10000000); /* wait 10 sec and try the lock again */ + continue; + } - /* grab an exclusive lock */ - rc = osMkdir(zLockFile, 0777); - if( rc<0 ){ - /* failed to open/create the lock directory */ - int tErrno = errno; - if( EEXIST == tErrno ){ - rc = SQLITE_BUSY; - } else { - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( rc!=SQLITE_BUSY ){ - storeLastErrno(pFile, tErrno); + assert( nTries==3 ); + if( 0==proxyBreakConchLock(pFile, myHostID) ){ + rc = SQLITE_OK; + if( lockType==EXCLUSIVE_LOCK ){ + rc = conchFile->pMethod->xLock((sqlcipher_sqlite3_file*)conchFile, SHARED_LOCK); + } + if( !rc ){ + rc = conchFile->pMethod->xLock((sqlcipher_sqlite3_file*)conchFile, lockType); + } } } - return rc; - } + } while( rc==SQLITE_BUSY && nTries<3 ); - /* got it, set the type and return ok */ - pFile->eFileLock = eFileLock; return rc; } -/* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. -** -** When the locking level reaches NO_LOCK, delete the lock file. +/* Takes the conch by taking a shared lock and read the contents conch, if +** lockPath is non-NULL, the host ID and lock file path must match. A NULL +** lockPath means that the lockPath in the conch file will be used if the +** host IDs match, or a new lock path will be generated automatically +** and written to the conch file. */ -static int dotlockUnlock(sqlcipher_sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; - char *zLockFile = (char *)pFile->lockingContext; - int rc; - - assert( pFile ); - OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock, - pFile->eFileLock, osGetpid(0))); - assert( eFileLock<=SHARED_LOCK ); +static int proxyTakeConch(unixFile *pFile){ + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - /* no-op if possible */ - if( pFile->eFileLock==eFileLock ){ + if( pCtx->conchHeld!=0 ){ return SQLITE_OK; - } + }else{ + unixFile *conchFile = pCtx->conchFile; + uuid_t myHostID; + int pError = 0; + char readBuf[PROXY_MAXCONCHLEN]; + char lockPath[MAXPATHLEN]; + char *tempLockPath = NULL; + int rc = SQLITE_OK; + int createConch = 0; + int hostIdMatch = 0; + int readLen = 0; + int tryOldLockPath = 0; + int forceNewLockPath = 0; - /* To downgrade to shared, simply update our internal notion of the - ** lock state. No need to mess with the file on disk. - */ - if( eFileLock==SHARED_LOCK ){ - pFile->eFileLock = SHARED_LOCK; - return SQLITE_OK; - } + OSTRACE(("TAKECONCH %d for %s pid=%d\n", conchFile->h, + (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), + osGetpid(0))); - /* To fully unlock the database, delete the lock file */ - assert( eFileLock==NO_LOCK ); - rc = osRmdir(zLockFile); - if( rc<0 ){ - int tErrno = errno; - if( tErrno==ENOENT ){ - rc = SQLITE_OK; - }else{ - rc = SQLITE_IOERR_UNLOCK; - storeLastErrno(pFile, tErrno); + rc = proxyGetHostID(myHostID, &pError); + if( (rc&0xff)==SQLITE_IOERR ){ + storeLastErrno(pFile, pError); + goto end_takeconch; } - return rc; - } - pFile->eFileLock = NO_LOCK; - return SQLITE_OK; -} - -/* -** Close a file. Make sure the lock has been released before closing. -*/ -static int dotlockClose(sqlcipher_sqlite3_file *id) { - unixFile *pFile = (unixFile*)id; - assert( id!=0 ); - dotlockUnlock(id, NO_LOCK); - sqlcipher_sqlite3_free(pFile->lockingContext); - return closeUnixFile(id); -} -/****************** End of the dot-file lock implementation ******************* -******************************************************************************/ - -/****************************************************************************** -************************** Begin flock Locking ******************************** -** -** Use the flock() system call to do file locking. -** -** flock() locking is like dot-file locking in that the various -** fine-grain locking levels supported by SQLite are collapsed into -** a single exclusive lock. In other words, SHARED, RESERVED, and -** PENDING locks are the same thing as an EXCLUSIVE lock. SQLite -** still works when you do this, but concurrency is reduced since -** only a single process can be reading the database at a time. -** -** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off -*/ -#if SQLITE_ENABLE_LOCKING_STYLE + rc = proxyConchLock(pFile, myHostID, SHARED_LOCK); + if( rc!=SQLITE_OK ){ + goto end_takeconch; + } + /* read the existing conch file */ + readLen = seekAndRead((unixFile*)conchFile, 0, readBuf, PROXY_MAXCONCHLEN); + if( readLen<0 ){ + /* I/O error: lastErrno set by seekAndRead */ + storeLastErrno(pFile, conchFile->lastErrno); + rc = SQLITE_IOERR_READ; + goto end_takeconch; + }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) || + readBuf[0]!=(char)PROXY_CONCHVERSION ){ + /* a short read or version format mismatch means we need to create a new + ** conch file. + */ + createConch = 1; + } + /* if the host id matches and the lock path already exists in the conch + ** we'll try to use the path there, if we can't open that path, we'll + ** retry with a new auto-generated path + */ + do { /* in case we need to try again for an :auto: named lock file */ -/* -** Retry flock() calls that fail with EINTR -*/ -#ifdef EINTR -static int robust_flock(int fd, int op){ - int rc; - do{ rc = flock(fd,op); }while( rc<0 && errno==EINTR ); - return rc; -} -#else -# define robust_flock(a,b) flock(a,b) -#endif + if( !createConch && !forceNewLockPath ){ + hostIdMatch = !memcmp(&readBuf[PROXY_HEADERLEN], myHostID, + PROXY_HOSTIDLEN); + /* if the conch has data compare the contents */ + if( !pCtx->lockProxyPath ){ + /* for auto-named local lock file, just check the host ID and we'll + ** use the local lock file path that's already in there + */ + if( hostIdMatch ){ + size_t pathLen = (readLen - PROXY_PATHINDEX); + if( pathLen>=MAXPATHLEN ){ + pathLen=MAXPATHLEN-1; + } + memcpy(lockPath, &readBuf[PROXY_PATHINDEX], pathLen); + lockPath[pathLen] = 0; + tempLockPath = lockPath; + tryOldLockPath = 1; + /* create a copy of the lock path if the conch is taken */ + goto end_takeconch; + } + }else if( hostIdMatch + && !strncmp(pCtx->lockProxyPath, &readBuf[PROXY_PATHINDEX], + readLen-PROXY_PATHINDEX) + ){ + /* conch host and lock path match */ + goto end_takeconch; + } + } -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. -*/ -static int flockCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ - int rc = SQLITE_OK; - int reserved = 0; - unixFile *pFile = (unixFile*)id; + /* if the conch isn't writable and doesn't match, we can't take it */ + if( (conchFile->openFlags&O_RDWR) == 0 ){ + rc = SQLITE_BUSY; + goto end_takeconch; + } - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); + /* either the conch didn't match or we need to create a new one */ + if( !pCtx->lockProxyPath ){ + proxyGetLockPath(pCtx->dbPath, lockPath, MAXPATHLEN); + tempLockPath = lockPath; + /* create a copy of the lock path _only_ if the conch is taken */ + } - assert( pFile ); + /* update conch with host and path (this will fail if other process + ** has a shared lock already), if the host id matches, use the big + ** stick. + */ + futimes(conchFile->h, NULL); + if( hostIdMatch && !createConch ){ + if( conchFile->pInode && conchFile->pInode->nShared>1 ){ + /* We are trying for an exclusive lock but another thread in this + ** same process is still holding a shared lock. */ + rc = SQLITE_BUSY; + } else { + rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); + } + }else{ + rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); + } + if( rc==SQLITE_OK ){ + char writeBuffer[PROXY_MAXCONCHLEN]; + int writeSize = 0; - /* Check if a thread in this process holds such a lock */ - if( pFile->eFileLock>SHARED_LOCK ){ - reserved = 1; - } + writeBuffer[0] = (char)PROXY_CONCHVERSION; + memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN); + if( pCtx->lockProxyPath!=NULL ){ + strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, + MAXPATHLEN); + }else{ + strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN); + } + writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]); + robust_ftruncate(conchFile->h, writeSize); + rc = unixWrite((sqlcipher_sqlite3_file *)conchFile, writeBuffer, writeSize, 0); + full_fsync(conchFile->h,0,0); + /* If we created a new conch file (not just updated the contents of a + ** valid conch file), try to match the permissions of the database + */ + if( rc==SQLITE_OK && createConch ){ + struct stat buf; + int err = osFstat(pFile->h, &buf); + if( err==0 ){ + mode_t cmode = buf.st_mode&(S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | + S_IROTH|S_IWOTH); + /* try to match the database file R/W permissions, ignore failure */ +#ifndef SQLITE_PROXY_DEBUG + osFchmod(conchFile->h, cmode); +#else + do{ + rc = osFchmod(conchFile->h, cmode); + }while( rc==(-1) && errno==EINTR ); + if( rc!=0 ){ + int code = errno; + fprintf(stderr, "fchmod %o FAILED with %d %s\n", + cmode, code, strerror(code)); + } else { + fprintf(stderr, "fchmod %o SUCCEDED\n",cmode); + } + }else{ + int code = errno; + fprintf(stderr, "STAT FAILED[%d] with %d %s\n", + err, code, strerror(code)); +#endif + } + } + } + conchFile->pMethod->xUnlock((sqlcipher_sqlite3_file*)conchFile, SHARED_LOCK); - /* Otherwise see if some other process holds it. */ - if( !reserved ){ - /* attempt to get the lock */ - int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); - if( !lrc ){ - /* got the lock, unlock it */ - lrc = robust_flock(pFile->h, LOCK_UN); - if ( lrc ) { - int tErrno = errno; - /* unlock failed with an error */ - lrc = SQLITE_IOERR_UNLOCK; - storeLastErrno(pFile, tErrno); - rc = lrc; + end_takeconch: + OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h)); + if( rc==SQLITE_OK && pFile->openFlags ){ + int fd; + if( pFile->h>=0 ){ + robust_close(pFile, pFile->h, __LINE__); + } + pFile->h = -1; + fd = robust_open(pCtx->dbPath, pFile->openFlags, 0); + OSTRACE(("TRANSPROXY: OPEN %d\n", fd)); + if( fd>=0 ){ + pFile->h = fd; + }else{ + rc=SQLITE_CANTOPEN_BKPT; /* SQLITE_BUSY? proxyTakeConch called + during locking */ + } } - } else { - int tErrno = errno; - reserved = 1; - /* someone else might have it reserved */ - lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(lrc) ){ - storeLastErrno(pFile, tErrno); - rc = lrc; + if( rc==SQLITE_OK && !pCtx->lockProxy ){ + char *path = tempLockPath ? tempLockPath : pCtx->lockProxyPath; + rc = proxyCreateUnixFile(path, &pCtx->lockProxy, 1); + if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && tryOldLockPath ){ + /* we couldn't create the proxy lock file with the old lock file path + ** so try again via auto-naming + */ + forceNewLockPath = 1; + tryOldLockPath = 0; + continue; /* go back to the do {} while start point, try again */ + } } - } + if( rc==SQLITE_OK ){ + /* Need to make a copy of path if we extracted the value + ** from the conch file or the path was allocated on the stack + */ + if( tempLockPath ){ + pCtx->lockProxyPath = sqlcipher_sqlite3DbStrDup(0, tempLockPath); + if( !pCtx->lockProxyPath ){ + rc = SQLITE_NOMEM_BKPT; + } + } + } + if( rc==SQLITE_OK ){ + pCtx->conchHeld = 1; + + if( pCtx->lockProxy->pMethod == &afpIoMethods ){ + afpLockingContext *afpCtx; + afpCtx = (afpLockingContext *)pCtx->lockProxy->lockingContext; + afpCtx->dbPath = pCtx->lockProxyPath; + } + } else { + conchFile->pMethod->xUnlock((sqlcipher_sqlite3_file*)conchFile, NO_LOCK); + } + OSTRACE(("TAKECONCH %d %s\n", conchFile->h, + rc==SQLITE_OK?"ok":"failed")); + return rc; + } while (1); /* in case we need to retry the :auto: lock file - + ** we should never get here except via the 'continue' call. */ } - OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); +} -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS - if( (rc & 0xff) == SQLITE_IOERR ){ - rc = SQLITE_OK; - reserved=1; +/* +** If pFile holds a lock on a conch file, then release that lock. +*/ +static int proxyReleaseConch(unixFile *pFile){ + int rc = SQLITE_OK; /* Subroutine return code */ + proxyLockingContext *pCtx; /* The locking context for the proxy lock */ + unixFile *conchFile; /* Name of the conch file */ + + pCtx = (proxyLockingContext *)pFile->lockingContext; + conchFile = pCtx->conchFile; + OSTRACE(("RELEASECONCH %d for %s pid=%d\n", conchFile->h, + (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), + osGetpid(0))); + if( pCtx->conchHeld>0 ){ + rc = conchFile->pMethod->xUnlock((sqlcipher_sqlite3_file*)conchFile, NO_LOCK); } -#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ - *pResOut = reserved; + pCtx->conchHeld = 0; + OSTRACE(("RELEASECONCH %d %s\n", conchFile->h, + (rc==SQLITE_OK ? "ok" : "failed"))); return rc; } /* -** Lock the file with the lock specified by parameter eFileLock - one -** of the following: -** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK -** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: -** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE +** Given the name of a database file, compute the name of its conch file. +** Store the conch filename in memory obtained from sqlcipher_sqlite3_malloc64(). +** Make *pConchPath point to the new name. Return SQLITE_OK on success +** or SQLITE_NOMEM if unable to obtain memory. ** -** flock() only really support EXCLUSIVE locks. We track intermediate -** lock states in the sqlcipher_sqlite3_file structure, but all locks SHARED or -** above are really EXCLUSIVE locks and exclude all other processes from -** access the file. +** The caller is responsible for ensuring that the allocated memory +** space is eventually freed. ** -** This routine will only increase a lock. Use the sqlcipher_sqlite3OsUnlock() -** routine to lower a locking level. +** *pConchPath is set to NULL if a memory allocation error occurs. */ -static int flockLock(sqlcipher_sqlite3_file *id, int eFileLock) { - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; +static int proxyCreateConchPathname(char *dbPath, char **pConchPath){ + int i; /* Loop counter */ + int len = (int)strlen(dbPath); /* Length of database filename - dbPath */ + char *conchPath; /* buffer in which to construct conch name */ - assert( pFile ); + /* Allocate space for the conch filename and initialize the name to + ** the name of the original database file. */ + *pConchPath = conchPath = (char *)sqlcipher_sqlite3_malloc64(len + 8); + if( conchPath==0 ){ + return SQLITE_NOMEM_BKPT; + } + memcpy(conchPath, dbPath, len+1); - /* if we already have a lock, it is exclusive. - ** Just adjust level and punt on outta here. */ - if (pFile->eFileLock > NO_LOCK) { - pFile->eFileLock = eFileLock; - return SQLITE_OK; + /* now insert a "." before the last / character */ + for( i=(len-1); i>=0; i-- ){ + if( conchPath[i]=='/' ){ + i++; + break; + } + } + conchPath[i]='.'; + while ( ih, LOCK_EX | LOCK_NB)) { - int tErrno = errno; - /* didn't get, must be busy */ - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ - storeLastErrno(pFile, tErrno); - } - } else { - /* got it, set the type and return ok */ - pFile->eFileLock = eFileLock; + return SQLITE_OK; +} + + +/* Takes a fully configured proxy locking-style unix file and switches +** the local lock file path +*/ +static int switchLockProxyPath(unixFile *pFile, const char *path) { + proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; + char *oldPath = pCtx->lockProxyPath; + int rc = SQLITE_OK; + + if( pFile->eFileLock!=NO_LOCK ){ + return SQLITE_BUSY; } - OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock), - rc==SQLITE_OK ? "ok" : "failed")); -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS - if( (rc & 0xff) == SQLITE_IOERR ){ - rc = SQLITE_BUSY; + + /* nothing to do if the path is NULL, :auto: or matches the existing path */ + if( !path || path[0]=='\0' || !strcmp(path, ":auto:") || + (oldPath && !strncmp(oldPath, path, MAXPATHLEN)) ){ + return SQLITE_OK; + }else{ + unixFile *lockProxy = pCtx->lockProxy; + pCtx->lockProxy=NULL; + pCtx->conchHeld = 0; + if( lockProxy!=NULL ){ + rc=lockProxy->pMethod->xClose((sqlcipher_sqlite3_file *)lockProxy); + if( rc ) return rc; + sqlcipher_sqlite3_free(lockProxy); + } + sqlcipher_sqlite3_free(oldPath); + pCtx->lockProxyPath = sqlcipher_sqlite3DbStrDup(0, path); } -#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ + return rc; } - /* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. +** pFile is a file that has been opened by a prior xOpen call. dbPath +** is a string buffer at least MAXPATHLEN+1 characters in size. ** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. +** This routine find the filename associated with pFile and writes it +** int dbPath. */ -static int flockUnlock(sqlcipher_sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; +static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){ +#if defined(__APPLE__) + if( pFile->pMethod == &afpIoMethods ){ + /* afp style keeps a reference to the db path in the filePath field + ** of the struct */ + assert( (int)strlen((char*)pFile->lockingContext)<=MAXPATHLEN ); + strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, + MAXPATHLEN); + } else +#endif + if( pFile->pMethod == &dotlockIoMethods ){ + /* dot lock style uses the locking context to store the dot lock + ** file path */ + int len = strlen((char *)pFile->lockingContext) - strlen(DOTLOCK_SUFFIX); + memcpy(dbPath, (char *)pFile->lockingContext, len + 1); + }else{ + /* all other styles use the locking context to store the db file path */ + assert( strlen((char*)pFile->lockingContext)<=MAXPATHLEN ); + strlcpy(dbPath, (char *)pFile->lockingContext, MAXPATHLEN); + } + return SQLITE_OK; +} - assert( pFile ); - OSTRACE(("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, eFileLock, - pFile->eFileLock, osGetpid(0))); - assert( eFileLock<=SHARED_LOCK ); +/* +** Takes an already filled in unix file and alters it so all file locking +** will be performed on the local proxy lock file. The following fields +** are preserved in the locking context so that they can be restored and +** the unix structure properly cleaned up at close time: +** ->lockingContext +** ->pMethod +*/ +static int proxyTransformUnixFile(unixFile *pFile, const char *path) { + proxyLockingContext *pCtx; + char dbPath[MAXPATHLEN+1]; /* Name of the database file */ + char *lockPath=NULL; + int rc = SQLITE_OK; - /* no-op if possible */ - if( pFile->eFileLock==eFileLock ){ - return SQLITE_OK; + if( pFile->eFileLock!=NO_LOCK ){ + return SQLITE_BUSY; + } + proxyGetDbPathForUnixFile(pFile, dbPath); + if( !path || path[0]=='\0' || !strcmp(path, ":auto:") ){ + lockPath=NULL; + }else{ + lockPath=(char *)path; } - /* shared can just be set because we always have an exclusive */ - if (eFileLock==SHARED_LOCK) { - pFile->eFileLock = eFileLock; - return SQLITE_OK; + OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h, + (lockPath ? lockPath : ":auto:"), osGetpid(0))); + + pCtx = sqlcipher_sqlite3_malloc64( sizeof(*pCtx) ); + if( pCtx==0 ){ + return SQLITE_NOMEM_BKPT; } + memset(pCtx, 0, sizeof(*pCtx)); - /* no, really, unlock. */ - if( robust_flock(pFile->h, LOCK_UN) ){ -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS - return SQLITE_OK; -#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ - return SQLITE_IOERR_UNLOCK; + rc = proxyCreateConchPathname(dbPath, &pCtx->conchFilePath); + if( rc==SQLITE_OK ){ + rc = proxyCreateUnixFile(pCtx->conchFilePath, &pCtx->conchFile, 0); + if( rc==SQLITE_CANTOPEN && ((pFile->openFlags&O_RDWR) == 0) ){ + /* if (a) the open flags are not O_RDWR, (b) the conch isn't there, and + ** (c) the file system is read-only, then enable no-locking access. + ** Ugh, since O_RDONLY==0x0000 we test for !O_RDWR since unixOpen asserts + ** that openFlags will have only one of O_RDONLY or O_RDWR. + */ + struct statfs fsInfo; + struct stat conchInfo; + int goLockless = 0; + + if( osStat(pCtx->conchFilePath, &conchInfo) == -1 ) { + int err = errno; + if( (err==ENOENT) && (statfs(dbPath, &fsInfo) != -1) ){ + goLockless = (fsInfo.f_flags&MNT_RDONLY) == MNT_RDONLY; + } + } + if( goLockless ){ + pCtx->conchHeld = -1; /* read only FS/ lockless */ + rc = SQLITE_OK; + } + } + } + if( rc==SQLITE_OK && lockPath ){ + pCtx->lockProxyPath = sqlcipher_sqlite3DbStrDup(0, lockPath); + } + + if( rc==SQLITE_OK ){ + pCtx->dbPath = sqlcipher_sqlite3DbStrDup(0, dbPath); + if( pCtx->dbPath==NULL ){ + rc = SQLITE_NOMEM_BKPT; + } + } + if( rc==SQLITE_OK ){ + /* all memory is allocated, proxys are created and assigned, + ** switch the locking context and pMethod then return. + */ + pCtx->oldLockingContext = pFile->lockingContext; + pFile->lockingContext = pCtx; + pCtx->pOldMethod = pFile->pMethod; + pFile->pMethod = &proxyIoMethods; }else{ - pFile->eFileLock = NO_LOCK; - return SQLITE_OK; + if( pCtx->conchFile ){ + pCtx->conchFile->pMethod->xClose((sqlcipher_sqlite3_file *)pCtx->conchFile); + sqlcipher_sqlite3_free(pCtx->conchFile); + } + sqlcipher_sqlite3DbFree(0, pCtx->lockProxyPath); + sqlcipher_sqlite3_free(pCtx->conchFilePath); + sqlcipher_sqlite3_free(pCtx); } + OSTRACE(("TRANSPROXY %d %s\n", pFile->h, + (rc==SQLITE_OK ? "ok" : "failed"))); + return rc; } + /* -** Close a file. +** This routine handles sqlcipher_sqlite3_file_control() calls that are specific +** to proxy locking. */ -static int flockClose(sqlcipher_sqlite3_file *id) { - assert( id!=0 ); - flockUnlock(id, NO_LOCK); - return closeUnixFile(id); +static int proxyFileControl(sqlcipher_sqlite3_file *id, int op, void *pArg){ + switch( op ){ + case SQLITE_FCNTL_GET_LOCKPROXYFILE: { + unixFile *pFile = (unixFile*)id; + if( pFile->pMethod == &proxyIoMethods ){ + proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; + proxyTakeConch(pFile); + if( pCtx->lockProxyPath ){ + *(const char **)pArg = pCtx->lockProxyPath; + }else{ + *(const char **)pArg = ":auto: (not held)"; + } + } else { + *(const char **)pArg = NULL; + } + return SQLITE_OK; + } + case SQLITE_FCNTL_SET_LOCKPROXYFILE: { + unixFile *pFile = (unixFile*)id; + int rc = SQLITE_OK; + int isProxyStyle = (pFile->pMethod == &proxyIoMethods); + if( pArg==NULL || (const char *)pArg==0 ){ + if( isProxyStyle ){ + /* turn off proxy locking - not supported. If support is added for + ** switching proxy locking mode off then it will need to fail if + ** the journal mode is WAL mode. + */ + rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/; + }else{ + /* turn off proxy locking - already off - NOOP */ + rc = SQLITE_OK; + } + }else{ + const char *proxyPath = (const char *)pArg; + if( isProxyStyle ){ + proxyLockingContext *pCtx = + (proxyLockingContext*)pFile->lockingContext; + if( !strcmp(pArg, ":auto:") + || (pCtx->lockProxyPath && + !strncmp(pCtx->lockProxyPath, proxyPath, MAXPATHLEN)) + ){ + rc = SQLITE_OK; + }else{ + rc = switchLockProxyPath(pFile, proxyPath); + } + }else{ + /* turn on proxy file locking */ + rc = proxyTransformUnixFile(pFile, proxyPath); + } + } + return rc; + } + default: { + assert( 0 ); /* The call assures that only valid opcodes are sent */ + } + } + /*NOTREACHED*/ assert(0); + return SQLITE_ERROR; } -#endif /* SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORK */ - -/******************* End of the flock lock implementation ********************* -******************************************************************************/ - -/****************************************************************************** -************************ Begin Named Semaphore Locking ************************ -** -** Named semaphore locking is only supported on VxWorks. -** -** Semaphore locking is like dot-lock and flock in that it really only -** supports EXCLUSIVE locking. Only a single process can read or write -** the database file at a time. This reduces potential concurrency, but -** makes the lock implementation much easier. +/* +** Within this division (the proxying locking implementation) the procedures +** above this point are all utilities. The lock-related methods of the +** proxy-locking sqlcipher_sqlite3_io_method object follow. */ -#if OS_VXWORKS + /* ** This routine checks if there is a RESERVED lock held on the specified @@ -41422,41 +43407,18 @@ static int flockClose(sqlcipher_sqlite3_file *id) { ** to a non-zero value otherwise *pResOut is set to zero. The return value ** is set to SQLITE_OK unless an I/O error occurs during lock checking. */ -static int semXCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut) { - int rc = SQLITE_OK; - int reserved = 0; +static int proxyCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut) { unixFile *pFile = (unixFile*)id; - - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - - assert( pFile ); - - /* Check if a thread in this process holds such a lock */ - if( pFile->eFileLock>SHARED_LOCK ){ - reserved = 1; - } - - /* Otherwise see if some other process holds it. */ - if( !reserved ){ - sem_t *pSem = pFile->pInode->pSem; - - if( sem_trywait(pSem)==-1 ){ - int tErrno = errno; - if( EAGAIN != tErrno ){ - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); - storeLastErrno(pFile, tErrno); - } else { - /* someone else has the lock when we are in NO_LOCK */ - reserved = (pFile->eFileLock < SHARED_LOCK); - } - }else{ - /* we could have it if we want it */ - sem_post(pSem); + int rc = proxyTakeConch(pFile); + if( rc==SQLITE_OK ){ + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; + if( pCtx->conchHeld>0 ){ + unixFile *proxy = pCtx->lockProxy; + return proxy->pMethod->xCheckReservedLock((sqlcipher_sqlite3_file*)proxy, pResOut); + }else{ /* conchHeld < 0 is lockless */ + pResOut=0; } } - OSTRACE(("TEST WR-LOCK %d %d %d (sem)\n", pFile->h, rc, reserved)); - - *pResOut = reserved; return rc; } @@ -41481,40 +43443,26 @@ static int semXCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut) { ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** -** Semaphore locks only really support EXCLUSIVE locks. We track intermediate -** lock states in the sqlcipher_sqlite3_file structure, but all locks SHARED or -** above are really EXCLUSIVE locks and exclude all other processes from -** access the file. -** ** This routine will only increase a lock. Use the sqlcipher_sqlite3OsUnlock() ** routine to lower a locking level. */ -static int semXLock(sqlcipher_sqlite3_file *id, int eFileLock) { +static int proxyLock(sqlcipher_sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; - sem_t *pSem = pFile->pInode->pSem; - int rc = SQLITE_OK; - - /* if we already have a lock, it is exclusive. - ** Just adjust level and punt on outta here. */ - if (pFile->eFileLock > NO_LOCK) { - pFile->eFileLock = eFileLock; - rc = SQLITE_OK; - goto sem_end_lock; - } - - /* lock semaphore now but bail out when already locked. */ - if( sem_trywait(pSem)==-1 ){ - rc = SQLITE_BUSY; - goto sem_end_lock; + int rc = proxyTakeConch(pFile); + if( rc==SQLITE_OK ){ + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; + if( pCtx->conchHeld>0 ){ + unixFile *proxy = pCtx->lockProxy; + rc = proxy->pMethod->xLock((sqlcipher_sqlite3_file*)proxy, eFileLock); + pFile->eFileLock = proxy->eFileLock; + }else{ + /* conchHeld < 0 is lockless */ + } } - - /* got it, set the type and return ok */ - pFile->eFileLock = eFileLock; - - sem_end_lock: return rc; } + /* ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. @@ -41522,5113 +43470,3436 @@ static int semXLock(sqlcipher_sqlite3_file *id, int eFileLock) { ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ -static int semXUnlock(sqlcipher_sqlite3_file *id, int eFileLock) { +static int proxyUnlock(sqlcipher_sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; - sem_t *pSem = pFile->pInode->pSem; - - assert( pFile ); - assert( pSem ); - OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock, - pFile->eFileLock, osGetpid(0))); - assert( eFileLock<=SHARED_LOCK ); - - /* no-op if possible */ - if( pFile->eFileLock==eFileLock ){ - return SQLITE_OK; - } - - /* shared can just be set because we always have an exclusive */ - if (eFileLock==SHARED_LOCK) { - pFile->eFileLock = eFileLock; - return SQLITE_OK; - } - - /* no, really unlock. */ - if ( sem_post(pSem)==-1 ) { - int rc, tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(rc) ){ - storeLastErrno(pFile, tErrno); + int rc = proxyTakeConch(pFile); + if( rc==SQLITE_OK ){ + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; + if( pCtx->conchHeld>0 ){ + unixFile *proxy = pCtx->lockProxy; + rc = proxy->pMethod->xUnlock((sqlcipher_sqlite3_file*)proxy, eFileLock); + pFile->eFileLock = proxy->eFileLock; + }else{ + /* conchHeld < 0 is lockless */ } - return rc; } - pFile->eFileLock = NO_LOCK; - return SQLITE_OK; + return rc; } /* - ** Close a file. - */ -static int semXClose(sqlcipher_sqlite3_file *id) { - if( id ){ +** Close a file that uses proxy locks. +*/ +static int proxyClose(sqlcipher_sqlite3_file *id) { + if( ALWAYS(id) ){ unixFile *pFile = (unixFile*)id; - semXUnlock(id, NO_LOCK); - assert( pFile ); - assert( unixFileMutexNotheld(pFile) ); - unixEnterMutex(); - releaseInodeInfo(pFile); - unixLeaveMutex(); - closeUnixFile(id); + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; + unixFile *lockProxy = pCtx->lockProxy; + unixFile *conchFile = pCtx->conchFile; + int rc = SQLITE_OK; + + if( lockProxy ){ + rc = lockProxy->pMethod->xUnlock((sqlcipher_sqlite3_file*)lockProxy, NO_LOCK); + if( rc ) return rc; + rc = lockProxy->pMethod->xClose((sqlcipher_sqlite3_file*)lockProxy); + if( rc ) return rc; + sqlcipher_sqlite3_free(lockProxy); + pCtx->lockProxy = 0; + } + if( conchFile ){ + if( pCtx->conchHeld ){ + rc = proxyReleaseConch(pFile); + if( rc ) return rc; + } + rc = conchFile->pMethod->xClose((sqlcipher_sqlite3_file*)conchFile); + if( rc ) return rc; + sqlcipher_sqlite3_free(conchFile); + } + sqlcipher_sqlite3DbFree(0, pCtx->lockProxyPath); + sqlcipher_sqlite3_free(pCtx->conchFilePath); + sqlcipher_sqlite3DbFree(0, pCtx->dbPath); + /* restore the original locking context and pMethod then close it */ + pFile->lockingContext = pCtx->oldLockingContext; + pFile->pMethod = pCtx->pOldMethod; + sqlcipher_sqlite3_free(pCtx); + return pFile->pMethod->xClose(id); } return SQLITE_OK; } -#endif /* OS_VXWORKS */ -/* -** Named semaphore locking is only available on VxWorks. -** -*************** End of the named semaphore lock implementation **************** -******************************************************************************/ -/****************************************************************************** -*************************** Begin AFP Locking ********************************* +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ +/* +** The proxy locking style is intended for use with AFP filesystems. +** And since AFP is only supported on MacOSX, the proxy locking is also +** restricted to MacOSX. ** -** AFP is the Apple Filing Protocol. AFP is a network filesystem found -** on Apple Macintosh computers - both OS9 and OSX. ** -** Third-party implementations of AFP are available. But this code here -** only works on OSX. -*/ - -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -/* -** The afpLockingContext structure contains all afp lock specific state -*/ -typedef struct afpLockingContext afpLockingContext; -struct afpLockingContext { - int reserved; - const char *dbPath; /* Name of the open file */ -}; - -struct ByteRangeLockPB2 -{ - unsigned long long offset; /* offset to first byte to lock */ - unsigned long long length; /* nbr of bytes to lock */ - unsigned long long retRangeStart; /* nbr of 1st byte locked if successful */ - unsigned char unLockFlag; /* 1 = unlock, 0 = lock */ - unsigned char startEndFlag; /* 1=rel to end of fork, 0=rel to start */ - int fd; /* file desc to assoc this lock with */ -}; - -#define afpfsByteRangeLock2FSCTL _IOWR('z', 23, struct ByteRangeLockPB2) +******************* End of the proxy lock implementation ********************** +******************************************************************************/ /* -** This is a utility for setting or clearing a bit-range lock on an -** AFP filesystem. +** Initialize the operating system interface. ** -** Return SQLITE_OK on success, SQLITE_BUSY on failure. +** This routine registers all VFS implementations for unix-like operating +** systems. This routine, and the sqlcipher_sqlite3_os_end() routine that follows, +** should be the only routines in this file that are visible from other +** files. +** +** This routine is called once during SQLite initialization and by a +** single thread. The memory allocation and mutex subsystems have not +** necessarily been initialized when this routine is called, and so they +** should not be used. */ -static int afpSetLock( - const char *path, /* Name of the file to be locked or unlocked */ - unixFile *pFile, /* Open file descriptor on path */ - unsigned long long offset, /* First byte to be locked */ - unsigned long long length, /* Number of bytes to lock */ - int setLockFlag /* True to set lock. False to clear lock */ -){ - struct ByteRangeLockPB2 pb; - int err; - - pb.unLockFlag = setLockFlag ? 0 : 1; - pb.startEndFlag = 0; - pb.offset = offset; - pb.length = length; - pb.fd = pFile->h; - - OSTRACE(("AFPSETLOCK [%s] for %d%s in range %llx:%llx\n", - (setLockFlag?"ON":"OFF"), pFile->h, (pb.fd==-1?"[testval-1]":""), - offset, length)); - err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0); - if ( err==-1 ) { - int rc; - int tErrno = errno; - OSTRACE(("AFPSETLOCK failed to fsctl() '%s' %d %s\n", - path, tErrno, strerror(tErrno))); -#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS - rc = SQLITE_BUSY; -#else - rc = sqliteErrorFromPosixError(tErrno, - setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK); -#endif /* SQLITE_IGNORE_AFP_LOCK_ERRORS */ - if( IS_LOCK_ERROR(rc) ){ - storeLastErrno(pFile, tErrno); - } - return rc; - } else { - return SQLITE_OK; +SQLITE_API int sqlcipher_sqlite3_os_init(void){ + /* + ** The following macro defines an initializer for an sqlcipher_sqlite3_vfs object. + ** The name of the VFS is NAME. The pAppData is a pointer to a pointer + ** to the "finder" function. (pAppData is a pointer to a pointer because + ** silly C90 rules prohibit a void* from being cast to a function pointer + ** and so we have to go through the intermediate pointer to avoid problems + ** when compiling with -pedantic-errors on GCC.) + ** + ** The FINDER parameter to this macro is the name of the pointer to the + ** finder-function. The finder-function returns a pointer to the + ** sqlite_io_methods object that implements the desired locking + ** behaviors. See the division above that contains the IOMETHODS + ** macro for addition information on finder-functions. + ** + ** Most finders simply return a pointer to a fixed sqlcipher_sqlite3_io_methods + ** object. But the "autolockIoFinder" available on MacOSX does a little + ** more than that; it looks at the filesystem type that hosts the + ** database file and tries to choose an locking method appropriate for + ** that filesystem time. + */ + #define UNIXVFS(VFSNAME, FINDER) { \ + 3, /* iVersion */ \ + sizeof(unixFile), /* szOsFile */ \ + MAX_PATHNAME, /* mxPathname */ \ + 0, /* pNext */ \ + VFSNAME, /* zName */ \ + (void*)&FINDER, /* pAppData */ \ + unixOpen, /* xOpen */ \ + unixDelete, /* xDelete */ \ + unixAccess, /* xAccess */ \ + unixFullPathname, /* xFullPathname */ \ + unixDlOpen, /* xDlOpen */ \ + unixDlError, /* xDlError */ \ + unixDlSym, /* xDlSym */ \ + unixDlClose, /* xDlClose */ \ + unixRandomness, /* xRandomness */ \ + unixSleep, /* xSleep */ \ + unixCurrentTime, /* xCurrentTime */ \ + unixGetLastError, /* xGetLastError */ \ + unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \ + unixSetSystemCall, /* xSetSystemCall */ \ + unixGetSystemCall, /* xGetSystemCall */ \ + unixNextSystemCall, /* xNextSystemCall */ \ } -} -/* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. -*/ -static int afpCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ - int rc = SQLITE_OK; - int reserved = 0; - unixFile *pFile = (unixFile*)id; - afpLockingContext *context; + /* + ** All default VFSes for unix are contained in the following array. + ** + ** Note that the sqlcipher_sqlite3_vfs.pNext field of the VFS object is modified + ** by the SQLite core when the VFS is registered. So the following + ** array cannot be const. + */ + static sqlcipher_sqlite3_vfs aVfs[] = { +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) + UNIXVFS("unix", autolockIoFinder ), +#elif OS_VXWORKS + UNIXVFS("unix", vxworksIoFinder ), +#else + UNIXVFS("unix", posixIoFinder ), +#endif + UNIXVFS("unix-none", nolockIoFinder ), + UNIXVFS("unix-dotfile", dotlockIoFinder ), + UNIXVFS("unix-excl", posixIoFinder ), +#if OS_VXWORKS + UNIXVFS("unix-namedsem", semIoFinder ), +#endif +#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS + UNIXVFS("unix-posix", posixIoFinder ), +#endif +#if SQLITE_ENABLE_LOCKING_STYLE + UNIXVFS("unix-flock", flockIoFinder ), +#endif +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) + UNIXVFS("unix-afp", afpIoFinder ), + UNIXVFS("unix-nfs", nfsIoFinder ), + UNIXVFS("unix-proxy", proxyIoFinder ), +#endif + }; + unsigned int i; /* Loop counter */ - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); + /* Double-check that the aSyscall[] array has been constructed + ** correctly. See ticket [bb3a86e890c8e96ab] */ + assert( ArraySize(aSyscall)==29 ); - assert( pFile ); - context = (afpLockingContext *) pFile->lockingContext; - if( context->reserved ){ - *pResOut = 1; - return SQLITE_OK; - } - sqlcipher_sqlite3_mutex_enter(pFile->pInode->pLockMutex); - /* Check if a thread in this process holds such a lock */ - if( pFile->pInode->eFileLock>SHARED_LOCK ){ - reserved = 1; + /* Register all VFSes defined in the aVfs[] array */ + for(i=0; i<(sizeof(aVfs)/sizeof(sqlcipher_sqlite3_vfs)); i++){ + sqlcipher_sqlite3_vfs_register(&aVfs[i], i==0); } + unixBigLock = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); - /* Otherwise see if some other process holds it. - */ - if( !reserved ){ - /* lock the RESERVED byte */ - int lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1); - if( SQLITE_OK==lrc ){ - /* if we succeeded in taking the reserved lock, unlock it to restore - ** the original state */ - lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0); - } else { - /* if we failed to get the lock then someone else must have it */ - reserved = 1; - } - if( IS_LOCK_ERROR(lrc) ){ - rc=lrc; - } - } +#ifndef SQLITE_OMIT_WAL + /* Validate lock assumptions */ + assert( SQLITE_SHM_NLOCK==8 ); /* Number of available locks */ + assert( UNIX_SHM_BASE==120 ); /* Start of locking area */ + /* Locks: + ** WRITE UNIX_SHM_BASE 120 + ** CKPT UNIX_SHM_BASE+1 121 + ** RECOVER UNIX_SHM_BASE+2 122 + ** READ-0 UNIX_SHM_BASE+3 123 + ** READ-1 UNIX_SHM_BASE+4 124 + ** READ-2 UNIX_SHM_BASE+5 125 + ** READ-3 UNIX_SHM_BASE+6 126 + ** READ-4 UNIX_SHM_BASE+7 127 + ** DMS UNIX_SHM_BASE+8 128 + */ + assert( UNIX_SHM_DMS==128 ); /* Byte offset of the deadman-switch */ +#endif + + /* Initialize temp file dir array. */ + unixTempFileInit(); - sqlcipher_sqlite3_mutex_leave(pFile->pInode->pLockMutex); - OSTRACE(("TEST WR-LOCK %d %d %d (afp)\n", pFile->h, rc, reserved)); + return SQLITE_OK; +} - *pResOut = reserved; - return rc; +/* +** Shutdown the operating system interface. +** +** Some operating systems might need to do some cleanup in this routine, +** to release dynamically allocated objects. But not on unix. +** This routine is a no-op for unix. +*/ +SQLITE_API int sqlcipher_sqlite3_os_end(void){ + unixBigLock = 0; + return SQLITE_OK; } +#endif /* SQLITE_OS_UNIX */ + +/************** End of os_unix.c *********************************************/ +/************** Begin file os_win.c ******************************************/ /* -** Lock the file with the lock specified by parameter eFileLock - one -** of the following: +** 2004 May 22 ** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: +** 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. ** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE +****************************************************************************** ** -** This routine will only increase a lock. Use the sqlcipher_sqlite3OsUnlock() -** routine to lower a locking level. +** This file contains code that is specific to Windows. */ -static int afpLock(sqlcipher_sqlite3_file *id, int eFileLock){ - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; - unixInodeInfo *pInode = pFile->pInode; - afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; +/* #include "sqliteInt.h" */ +#if SQLITE_OS_WIN /* This file is used for Windows only */ - assert( pFile ); - OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (afp)\n", pFile->h, - azFileLock(eFileLock), azFileLock(pFile->eFileLock), - azFileLock(pInode->eFileLock), pInode->nShared , osGetpid(0))); +/* +** Include code that is common to all os_*.c files +*/ +/* #include "os_common.h" */ - /* If there is already a lock of this type or more restrictive on the - ** unixFile, do nothing. Don't use the afp_end_lock: exit path, as - ** unixEnterMutex() hasn't been called yet. - */ - if( pFile->eFileLock>=eFileLock ){ - OSTRACE(("LOCK %d %s ok (already held) (afp)\n", pFile->h, - azFileLock(eFileLock))); - return SQLITE_OK; - } +/* +** Include the header file for the Windows VFS. +*/ +/* #include "os_win.h" */ - /* Make sure the locking sequence is correct - ** (1) We never move from unlocked to anything higher than shared lock. - ** (2) SQLite never explicitly requests a pendig lock. - ** (3) A shared lock is always held when a reserve lock is requested. - */ - assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); - assert( eFileLock!=PENDING_LOCK ); - assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK ); +/* +** Compiling and using WAL mode requires several APIs that are only +** available in Windows platforms based on the NT kernel. +*/ +#if !SQLITE_OS_WINNT && !defined(SQLITE_OMIT_WAL) +# error "WAL mode requires support from the Windows NT kernel, compile\ + with SQLITE_OMIT_WAL." +#endif - /* This mutex is needed because pFile->pInode is shared across threads - */ - pInode = pFile->pInode; - sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); +#if !SQLITE_OS_WINNT && SQLITE_MAX_MMAP_SIZE>0 +# error "Memory mapped files require support from the Windows NT kernel,\ + compile with SQLITE_MAX_MMAP_SIZE=0." +#endif - /* If some thread using this PID has a lock via a different unixFile* - ** handle that precludes the requested lock, return BUSY. - */ - if( (pFile->eFileLock!=pInode->eFileLock && - (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) - ){ - rc = SQLITE_BUSY; - goto afp_end_lock; - } +/* +** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions +** based on the sub-platform)? +*/ +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI) +# define SQLITE_WIN32_HAS_ANSI +#endif - /* If a SHARED lock is requested, and some thread using this PID already - ** has a SHARED or RESERVED lock, then increment reference counts and - ** return SQLITE_OK. - */ - if( eFileLock==SHARED_LOCK && - (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ - assert( eFileLock==SHARED_LOCK ); - assert( pFile->eFileLock==0 ); - assert( pInode->nShared>0 ); - pFile->eFileLock = SHARED_LOCK; - pInode->nShared++; - pInode->nLock++; - goto afp_end_lock; - } +/* +** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions +** based on the sub-platform)? +*/ +#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT) && \ + !defined(SQLITE_WIN32_NO_WIDE) +# define SQLITE_WIN32_HAS_WIDE +#endif - /* A PENDING lock is needed before acquiring a SHARED lock and before - ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will - ** be released. - */ - if( eFileLock==SHARED_LOCK - || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLockdbPath, pFile, PENDING_BYTE, 1, 1); - if (failed) { - rc = failed; - goto afp_end_lock; - } - } +/* +** Make sure at least one set of Win32 APIs is available. +*/ +#if !defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_WIN32_HAS_WIDE) +# error "At least one of SQLITE_WIN32_HAS_ANSI and SQLITE_WIN32_HAS_WIDE\ + must be defined." +#endif - /* If control gets to this point, then actually go ahead and make - ** operating system calls for the specified lock. - */ - if( eFileLock==SHARED_LOCK ){ - int lrc1, lrc2, lrc1Errno = 0; - long lk, mask; +/* +** Define the required Windows SDK version constants if they are not +** already available. +*/ +#ifndef NTDDI_WIN8 +# define NTDDI_WIN8 0x06020000 +#endif - assert( pInode->nShared==0 ); - assert( pInode->eFileLock==0 ); +#ifndef NTDDI_WINBLUE +# define NTDDI_WINBLUE 0x06030000 +#endif - mask = (sizeof(long)==8) ? LARGEST_INT64 : 0x7fffffff; - /* Now get the read-lock SHARED_LOCK */ - /* note that the quality of the randomness doesn't matter that much */ - lk = random(); - pInode->sharedByte = (lk & mask)%(SHARED_SIZE - 1); - lrc1 = afpSetLock(context->dbPath, pFile, - SHARED_FIRST+pInode->sharedByte, 1, 1); - if( IS_LOCK_ERROR(lrc1) ){ - lrc1Errno = pFile->lastErrno; - } - /* Drop the temporary PENDING lock */ - lrc2 = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); +#ifndef NTDDI_WINTHRESHOLD +# define NTDDI_WINTHRESHOLD 0x06040000 +#endif - if( IS_LOCK_ERROR(lrc1) ) { - storeLastErrno(pFile, lrc1Errno); - rc = lrc1; - goto afp_end_lock; - } else if( IS_LOCK_ERROR(lrc2) ){ - rc = lrc2; - goto afp_end_lock; - } else if( lrc1 != SQLITE_OK ) { - rc = lrc1; - } else { - pFile->eFileLock = SHARED_LOCK; - pInode->nLock++; - pInode->nShared = 1; - } - }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ - /* We are trying for an exclusive lock but another thread in this - ** same process is still holding a shared lock. */ - rc = SQLITE_BUSY; - }else{ - /* The request was for a RESERVED or EXCLUSIVE lock. It is - ** assumed that there is a SHARED or greater lock on the file - ** already. - */ - int failed = 0; - assert( 0!=pFile->eFileLock ); - if (eFileLock >= RESERVED_LOCK && pFile->eFileLock < RESERVED_LOCK) { - /* Acquire a RESERVED lock */ - failed = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1); - if( !failed ){ - context->reserved = 1; - } - } - if (!failed && eFileLock == EXCLUSIVE_LOCK) { - /* Acquire an EXCLUSIVE lock */ +/* +** Check to see if the GetVersionEx[AW] functions are deprecated on the +** target system. GetVersionEx was first deprecated in Win8.1. +*/ +#ifndef SQLITE_WIN32_GETVERSIONEX +# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINBLUE +# define SQLITE_WIN32_GETVERSIONEX 0 /* GetVersionEx() is deprecated */ +# else +# define SQLITE_WIN32_GETVERSIONEX 1 /* GetVersionEx() is current */ +# endif +#endif - /* Remove the shared lock before trying the range. we'll need to - ** reestablish the shared lock if we can't get the afpUnlock - */ - if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST + - pInode->sharedByte, 1, 0)) ){ - int failed2 = SQLITE_OK; - /* now attemmpt to get the exclusive lock range */ - failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST, - SHARED_SIZE, 1); - if( failed && (failed2 = afpSetLock(context->dbPath, pFile, - SHARED_FIRST + pInode->sharedByte, 1, 1)) ){ - /* Can't reestablish the shared lock. Sqlite can't deal, this is - ** a critical I/O error - */ - rc = ((failed & 0xff) == SQLITE_IOERR) ? failed2 : - SQLITE_IOERR_LOCK; - goto afp_end_lock; - } - }else{ - rc = failed; - } - } - if( failed ){ - rc = failed; - } - } +/* +** Check to see if the CreateFileMappingA function is supported on the +** target system. It is unavailable when using "mincore.lib" on Win10. +** When compiling for Windows 10, always assume "mincore.lib" is in use. +*/ +#ifndef SQLITE_WIN32_CREATEFILEMAPPINGA +# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINTHRESHOLD +# define SQLITE_WIN32_CREATEFILEMAPPINGA 0 +# else +# define SQLITE_WIN32_CREATEFILEMAPPINGA 1 +# endif +#endif - if( rc==SQLITE_OK ){ - pFile->eFileLock = eFileLock; - pInode->eFileLock = eFileLock; - }else if( eFileLock==EXCLUSIVE_LOCK ){ - pFile->eFileLock = PENDING_LOCK; - pInode->eFileLock = PENDING_LOCK; - } +/* +** This constant should already be defined (in the "WinDef.h" SDK file). +*/ +#ifndef MAX_PATH +# define MAX_PATH (260) +#endif + +/* +** Maximum pathname length (in chars) for Win32. This should normally be +** MAX_PATH. +*/ +#ifndef SQLITE_WIN32_MAX_PATH_CHARS +# define SQLITE_WIN32_MAX_PATH_CHARS (MAX_PATH) +#endif + +/* +** This constant should already be defined (in the "WinNT.h" SDK file). +*/ +#ifndef UNICODE_STRING_MAX_CHARS +# define UNICODE_STRING_MAX_CHARS (32767) +#endif + +/* +** Maximum pathname length (in chars) for WinNT. This should normally be +** UNICODE_STRING_MAX_CHARS. +*/ +#ifndef SQLITE_WINNT_MAX_PATH_CHARS +# define SQLITE_WINNT_MAX_PATH_CHARS (UNICODE_STRING_MAX_CHARS) +#endif -afp_end_lock: - sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); - OSTRACE(("LOCK %d %s %s (afp)\n", pFile->h, azFileLock(eFileLock), - rc==SQLITE_OK ? "ok" : "failed")); - return rc; -} +/* +** Maximum pathname length (in bytes) for Win32. The MAX_PATH macro is in +** characters, so we allocate 4 bytes per character assuming worst-case of +** 4-bytes-per-character for UTF8. +*/ +#ifndef SQLITE_WIN32_MAX_PATH_BYTES +# define SQLITE_WIN32_MAX_PATH_BYTES (SQLITE_WIN32_MAX_PATH_CHARS*4) +#endif /* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. +** Maximum pathname length (in bytes) for WinNT. This should normally be +** UNICODE_STRING_MAX_CHARS * sizeof(WCHAR). */ -static int afpUnlock(sqlcipher_sqlite3_file *id, int eFileLock) { - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; - unixInodeInfo *pInode; - afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; - int skipShared = 0; -#ifdef SQLITE_TEST - int h = pFile->h; +#ifndef SQLITE_WINNT_MAX_PATH_BYTES +# define SQLITE_WINNT_MAX_PATH_BYTES \ + (sizeof(WCHAR) * SQLITE_WINNT_MAX_PATH_CHARS) #endif - assert( pFile ); - OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock, - pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, - osGetpid(0))); +/* +** Maximum error message length (in chars) for WinRT. +*/ +#ifndef SQLITE_WIN32_MAX_ERRMSG_CHARS +# define SQLITE_WIN32_MAX_ERRMSG_CHARS (1024) +#endif - assert( eFileLock<=SHARED_LOCK ); - if( pFile->eFileLock<=eFileLock ){ - return SQLITE_OK; - } - pInode = pFile->pInode; - sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); - assert( pInode->nShared!=0 ); - if( pFile->eFileLock>SHARED_LOCK ){ - assert( pInode->eFileLock==pFile->eFileLock ); - SimulateIOErrorBenign(1); - SimulateIOError( h=(-1) ) - SimulateIOErrorBenign(0); +/* +** Returns non-zero if the character should be treated as a directory +** separator. +*/ +#ifndef winIsDirSep +# define winIsDirSep(a) (((a) == '/') || ((a) == '\\')) +#endif -#ifdef SQLITE_DEBUG - /* When reducing a lock such that other processes can start - ** reading the database file again, make sure that the - ** transaction counter was updated if any part of the database - ** file changed. If the transaction counter is not updated, - ** other connections to the same file might not realize that - ** the file has changed and hence might not know to flush their - ** cache. The use of a stale cache can lead to database corruption. - */ - assert( pFile->inNormalWrite==0 - || pFile->dbUpdate==0 - || pFile->transCntrChng==1 ); - pFile->inNormalWrite = 0; +/* +** This macro is used when a local variable is set to a value that is +** [sometimes] not used by the code (e.g. via conditional compilation). +*/ +#ifndef UNUSED_VARIABLE_VALUE +# define UNUSED_VARIABLE_VALUE(x) (void)(x) #endif - if( pFile->eFileLock==EXCLUSIVE_LOCK ){ - rc = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0); - if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1) ){ - /* only re-establish the shared lock if necessary */ - int sharedLockByte = SHARED_FIRST+pInode->sharedByte; - rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 1); - } else { - skipShared = 1; - } - } - if( rc==SQLITE_OK && pFile->eFileLock>=PENDING_LOCK ){ - rc = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); - } - if( rc==SQLITE_OK && pFile->eFileLock>=RESERVED_LOCK && context->reserved ){ - rc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0); - if( !rc ){ - context->reserved = 0; - } - } - if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1)){ - pInode->eFileLock = SHARED_LOCK; - } - } - if( rc==SQLITE_OK && eFileLock==NO_LOCK ){ +/* +** Returns the character that should be used as the directory separator. +*/ +#ifndef winGetDirSep +# define winGetDirSep() '\\' +#endif - /* Decrement the shared lock counter. Release the lock using an - ** OS call only when all threads in this same process have released - ** the lock. - */ - unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte; - pInode->nShared--; - if( pInode->nShared==0 ){ - SimulateIOErrorBenign(1); - SimulateIOError( h=(-1) ) - SimulateIOErrorBenign(0); - if( !skipShared ){ - rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0); - } - if( !rc ){ - pInode->eFileLock = NO_LOCK; - pFile->eFileLock = NO_LOCK; - } - } - if( rc==SQLITE_OK ){ - pInode->nLock--; - assert( pInode->nLock>=0 ); - if( pInode->nLock==0 ) closePendingFds(pFile); - } - } +/* +** Do we need to manually define the Win32 file mapping APIs for use with WAL +** mode or memory mapped files (e.g. these APIs are available in the Windows +** CE SDK; however, they are not present in the header file)? +*/ +#if SQLITE_WIN32_FILEMAPPING_API && \ + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) +/* +** Two of the file mapping APIs are different under WinRT. Figure out which +** set we need. +*/ +#if SQLITE_OS_WINRT +WINBASEAPI HANDLE WINAPI CreateFileMappingFromApp(HANDLE, \ + LPSECURITY_ATTRIBUTES, ULONG, ULONG64, LPCWSTR); - sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); - if( rc==SQLITE_OK ){ - pFile->eFileLock = eFileLock; - } - return rc; -} +WINBASEAPI LPVOID WINAPI MapViewOfFileFromApp(HANDLE, ULONG, ULONG64, SIZE_T); +#else +#if defined(SQLITE_WIN32_HAS_ANSI) +WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \ + DWORD, DWORD, DWORD, LPCSTR); +#endif /* defined(SQLITE_WIN32_HAS_ANSI) */ + +#if defined(SQLITE_WIN32_HAS_WIDE) +WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \ + DWORD, DWORD, DWORD, LPCWSTR); +#endif /* defined(SQLITE_WIN32_HAS_WIDE) */ + +WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T); +#endif /* SQLITE_OS_WINRT */ /* -** Close a file & cleanup AFP specific locking context +** These file mapping APIs are common to both Win32 and WinRT. */ -static int afpClose(sqlcipher_sqlite3_file *id) { - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; - assert( id!=0 ); - afpUnlock(id, NO_LOCK); - assert( unixFileMutexNotheld(pFile) ); - unixEnterMutex(); - if( pFile->pInode ){ - unixInodeInfo *pInode = pFile->pInode; - sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); - if( pInode->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->aPending. It will be automatically closed when - ** the last lock is cleared. - */ - setPendingFd(pFile); - } - sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); - } - releaseInodeInfo(pFile); - sqlcipher_sqlite3_free(pFile->lockingContext); - rc = closeUnixFile(id); - unixLeaveMutex(); - return rc; -} -#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ +WINBASEAPI BOOL WINAPI FlushViewOfFile(LPCVOID, SIZE_T); +WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID); +#endif /* SQLITE_WIN32_FILEMAPPING_API */ + /* -** The code above is the AFP lock implementation. The code is specific -** to MacOSX and does not work on other unix platforms. No alternative -** is available. If you don't compile for a mac, then the "unix-afp" -** VFS is not available. -** -********************* End of the AFP lock implementation ********************** -******************************************************************************/ +** Some Microsoft compilers lack this definition. +*/ +#ifndef INVALID_FILE_ATTRIBUTES +# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#endif -/****************************************************************************** -*************************** Begin NFS Locking ********************************/ +#ifndef FILE_FLAG_MASK +# define FILE_FLAG_MASK (0xFF3C0000) +#endif -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -/* - ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock - ** must be either NO_LOCK or SHARED_LOCK. - ** - ** If the locking level of the file descriptor is already at or below - ** the requested locking level, this routine is a no-op. - */ -static int nfsUnlock(sqlcipher_sqlite3_file *id, int eFileLock){ - return posixUnlock(id, eFileLock, 1); -} +#ifndef FILE_ATTRIBUTE_MASK +# define FILE_ATTRIBUTE_MASK (0x0003FFF7) +#endif -#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ -/* -** The code above is the NFS lock implementation. The code is specific -** to MacOSX and does not work on other unix platforms. No alternative -** is available. -** -********************* End of the NFS lock implementation ********************** -******************************************************************************/ +#ifndef SQLITE_OMIT_WAL +/* Forward references to structures used for WAL */ +typedef struct winShm winShm; /* A connection to shared-memory */ +typedef struct winShmNode winShmNode; /* A region of shared-memory */ +#endif -/****************************************************************************** -**************** Non-locking sqlcipher_sqlite3_file methods ***************************** -** -** The next division contains implementations for all methods of the -** sqlcipher_sqlite3_file object other than the locking methods. The locking -** methods were defined in divisions above (one locking method per -** division). Those methods that are common to all locking modes -** are gather together into this division. +/* +** WinCE lacks native support for file locking so we have to fake it +** with some code of our own. */ +#if SQLITE_OS_WINCE +typedef struct winceLock { + int nReaders; /* Number of reader locks obtained */ + BOOL bPending; /* Indicates a pending lock has been obtained */ + BOOL bReserved; /* Indicates a reserved lock has been obtained */ + BOOL bExclusive; /* Indicates an exclusive lock has been obtained */ +} winceLock; +#endif /* -** Seek to the offset passed as the second argument, then read cnt -** bytes into pBuf. Return the number of bytes actually read. -** -** NB: If you define USE_PREAD or USE_PREAD64, then it might also -** be necessary to define _XOPEN_SOURCE to be 500. This varies from -** one system to another. Since SQLite does not define USE_PREAD -** in any form by default, we will not attempt to define _XOPEN_SOURCE. -** See tickets #2741 and #2681. -** -** To avoid stomping the errno value on a failed read the lastErrno value -** is set before returning. +** The winFile structure is a subclass of sqlcipher_sqlite3_file* specific to the win32 +** portability layer. */ -static int seekAndRead(unixFile *id, sqlcipher_sqlite3_int64 offset, void *pBuf, int cnt){ - int got; - int prior = 0; -#if (!defined(USE_PREAD) && !defined(USE_PREAD64)) - i64 newOffset; +typedef struct winFile winFile; +struct winFile { + const sqlcipher_sqlite3_io_methods *pMethod; /*** Must be first ***/ + sqlcipher_sqlite3_vfs *pVfs; /* The VFS used to open this file */ + HANDLE h; /* Handle for accessing the file */ + u8 locktype; /* Type of lock currently held on this file */ + short sharedLockByte; /* Randomly chosen byte used as a shared lock */ + u8 ctrlFlags; /* Flags. See WINFILE_* below */ + DWORD lastErrno; /* The Windows errno from the last I/O error */ +#ifndef SQLITE_OMIT_WAL + winShm *pShm; /* Instance of shared memory on this file */ #endif - TIMER_START; - assert( cnt==(cnt&0x1ffff) ); - assert( id->h>2 ); - do{ -#if defined(USE_PREAD) - got = osPread(id->h, pBuf, cnt, offset); - SimulateIOError( got = -1 ); -#elif defined(USE_PREAD64) - got = osPread64(id->h, pBuf, cnt, offset); - SimulateIOError( got = -1 ); -#else - newOffset = lseek(id->h, offset, SEEK_SET); - SimulateIOError( newOffset = -1 ); - if( newOffset<0 ){ - storeLastErrno((unixFile*)id, errno); - return -1; - } - got = osRead(id->h, pBuf, cnt); + const char *zPath; /* Full pathname of this file */ + int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ +#if SQLITE_OS_WINCE + LPWSTR zDeleteOnClose; /* Name of file to delete when closing */ + HANDLE hMutex; /* Mutex used to control access to shared lock */ + HANDLE hShared; /* Shared memory segment used for locking */ + winceLock local; /* Locks obtained by this instance of winFile */ + winceLock *shared; /* Global shared lock memory for the file */ #endif - if( got==cnt ) break; - if( got<0 ){ - if( errno==EINTR ){ got = 1; continue; } - prior = 0; - storeLastErrno((unixFile*)id, errno); - break; - }else if( got>0 ){ - cnt -= got; - offset += got; - prior += got; - pBuf = (void*)(got + (char*)pBuf); - } - }while( got>0 ); - TIMER_END; - OSTRACE(("READ %-3d %5d %7lld %llu\n", - id->h, got+prior, offset-prior, TIMER_ELAPSED)); - return got+prior; -} +#if SQLITE_MAX_MMAP_SIZE>0 + int nFetchOut; /* Number of outstanding xFetch references */ + HANDLE hMap; /* Handle for accessing memory mapping */ + void *pMapRegion; /* Area memory mapped */ + sqlcipher_sqlite3_int64 mmapSize; /* Size of mapped region */ + sqlcipher_sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ +#endif +}; /* -** Read data from a file into a buffer. Return SQLITE_OK if all -** bytes were read successfully and SQLITE_IOERR if anything goes -** wrong. +** The winVfsAppData structure is used for the pAppData member for all of the +** Win32 VFS variants. */ -static int unixRead( - sqlcipher_sqlite3_file *id, - void *pBuf, - int amt, - sqlcipher_sqlite3_int64 offset -){ - unixFile *pFile = (unixFile *)id; - int got; - assert( id ); - assert( offset>=0 ); - assert( amt>0 ); +typedef struct winVfsAppData winVfsAppData; +struct winVfsAppData { + const sqlcipher_sqlite3_io_methods *pMethod; /* The file I/O methods to use. */ + void *pAppData; /* The extra pAppData, if any. */ + BOOL bNoLock; /* Non-zero if locking is disabled. */ +}; - /* If this is a database file (not a journal, super-journal or temp - ** file), the bytes in the locking range should never be read or written. */ -#if 0 - assert( pFile->pPreallocatedUnused==0 - || offset>=PENDING_BYTE+512 - || offset+amt<=PENDING_BYTE - ); -#endif +/* +** Allowed values for winFile.ctrlFlags +*/ +#define WINFILE_RDONLY 0x02 /* Connection is read only */ +#define WINFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ +#define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ -#if SQLITE_MAX_MMAP_SIZE>0 - /* Deal with as much of this read request as possible by transfering - ** data from the memory mapping using memcpy(). */ - if( offsetmmapSize ){ - if( offset+amt <= pFile->mmapSize ){ - memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); - return SQLITE_OK; - }else{ - int nCopy = pFile->mmapSize - offset; - memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); - pBuf = &((u8 *)pBuf)[nCopy]; - amt -= nCopy; - offset += nCopy; - } - } +/* + * The size of the buffer used by sqlcipher_sqlite3_win32_write_debug(). + */ +#ifndef SQLITE_WIN32_DBG_BUF_SIZE +# define SQLITE_WIN32_DBG_BUF_SIZE ((int)(4096-sizeof(DWORD))) #endif - got = seekAndRead(pFile, offset, pBuf, amt); - if( got==amt ){ - return SQLITE_OK; - }else if( got<0 ){ - /* pFile->lastErrno has been set by seekAndRead(). - ** Usually we return SQLITE_IOERR_READ here, though for some - ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The - ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT - ** prior to returning to the application by the sqlcipher_sqlite3ApiExit() - ** routine. - */ - switch( pFile->lastErrno ){ - case ERANGE: - case EIO: -#ifdef ENXIO - case ENXIO: +/* + * If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the + * various Win32 API heap functions instead of our own. + */ +#ifdef SQLITE_WIN32_MALLOC + +/* + * If this is non-zero, an isolated heap will be created by the native Win32 + * allocator subsystem; otherwise, the default process heap will be used. This + * setting has no effect when compiling for WinRT. By default, this is enabled + * and an isolated heap will be created to store all allocated data. + * + ****************************************************************************** + * WARNING: It is important to note that when this setting is non-zero and the + * winMemShutdown function is called (e.g. by the sqlcipher_sqlite3_shutdown + * function), all data that was allocated using the isolated heap will + * be freed immediately and any attempt to access any of that freed + * data will almost certainly result in an immediate access violation. + ****************************************************************************** + */ +#ifndef SQLITE_WIN32_HEAP_CREATE +# define SQLITE_WIN32_HEAP_CREATE (TRUE) #endif -#ifdef EDEVERR - case EDEVERR: + +/* + * This is the maximum possible initial size of the Win32-specific heap, in + * bytes. + */ +#ifndef SQLITE_WIN32_HEAP_MAX_INIT_SIZE +# define SQLITE_WIN32_HEAP_MAX_INIT_SIZE (4294967295U) #endif - return SQLITE_IOERR_CORRUPTFS; - } - return SQLITE_IOERR_READ; - }else{ - storeLastErrno(pFile, 0); /* not a system error */ - /* Unread parts of the buffer must be zero-filled */ - memset(&((char*)pBuf)[got], 0, amt-got); - return SQLITE_IOERR_SHORT_READ; - } -} /* -** Attempt to seek the file-descriptor passed as the first argument to -** absolute offset iOff, then attempt to write nBuf bytes of data from -** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise, -** return the actual number of bytes written (which may be less than -** nBuf). -*/ -static int seekAndWriteFd( - int fd, /* File descriptor to write to */ - i64 iOff, /* File offset to begin writing at */ - const void *pBuf, /* Copy data from this buffer to the file */ - int nBuf, /* Size of buffer pBuf in bytes */ - int *piErrno /* OUT: Error number if error occurs */ -){ - int rc = 0; /* Value returned by system call */ + * This is the extra space for the initial size of the Win32-specific heap, + * in bytes. This value may be zero. + */ +#ifndef SQLITE_WIN32_HEAP_INIT_EXTRA +# define SQLITE_WIN32_HEAP_INIT_EXTRA (4194304) +#endif - assert( nBuf==(nBuf&0x1ffff) ); - assert( fd>2 ); - assert( piErrno!=0 ); - nBuf &= 0x1ffff; - TIMER_START; +/* + * Calculate the maximum legal cache size, in pages, based on the maximum + * possible initial heap size and the default page size, setting aside the + * needed extra space. + */ +#ifndef SQLITE_WIN32_MAX_CACHE_SIZE +# define SQLITE_WIN32_MAX_CACHE_SIZE (((SQLITE_WIN32_HEAP_MAX_INIT_SIZE) - \ + (SQLITE_WIN32_HEAP_INIT_EXTRA)) / \ + (SQLITE_DEFAULT_PAGE_SIZE)) +#endif -#if defined(USE_PREAD) - do{ rc = (int)osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR ); -#elif defined(USE_PREAD64) - do{ rc = (int)osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR); -#else - do{ - i64 iSeek = lseek(fd, iOff, SEEK_SET); - SimulateIOError( iSeek = -1 ); - if( iSeek<0 ){ - rc = -1; - break; - } - rc = osWrite(fd, pBuf, nBuf); - }while( rc<0 && errno==EINTR ); +/* + * This is cache size used in the calculation of the initial size of the + * Win32-specific heap. It cannot be negative. + */ +#ifndef SQLITE_WIN32_CACHE_SIZE +# if SQLITE_DEFAULT_CACHE_SIZE>=0 +# define SQLITE_WIN32_CACHE_SIZE (SQLITE_DEFAULT_CACHE_SIZE) +# else +# define SQLITE_WIN32_CACHE_SIZE (-(SQLITE_DEFAULT_CACHE_SIZE)) +# endif #endif - TIMER_END; - OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED)); +/* + * Make sure that the calculated cache size, in pages, cannot cause the + * initial size of the Win32-specific heap to exceed the maximum amount + * of memory that can be specified in the call to HeapCreate. + */ +#if SQLITE_WIN32_CACHE_SIZE>SQLITE_WIN32_MAX_CACHE_SIZE +# undef SQLITE_WIN32_CACHE_SIZE +# define SQLITE_WIN32_CACHE_SIZE (2000) +#endif - if( rc<0 ) *piErrno = errno; - return rc; -} +/* + * The initial size of the Win32-specific heap. This value may be zero. + */ +#ifndef SQLITE_WIN32_HEAP_INIT_SIZE +# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_WIN32_CACHE_SIZE) * \ + (SQLITE_DEFAULT_PAGE_SIZE) + \ + (SQLITE_WIN32_HEAP_INIT_EXTRA)) +#endif +/* + * The maximum size of the Win32-specific heap. This value may be zero. + */ +#ifndef SQLITE_WIN32_HEAP_MAX_SIZE +# define SQLITE_WIN32_HEAP_MAX_SIZE (0) +#endif /* -** Seek to the offset in id->offset then read cnt bytes into pBuf. -** Return the number of bytes actually read. Update the offset. -** -** To avoid stomping the errno value on a failed write the lastErrno value -** is set before returning. -*/ -static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ - return seekAndWriteFd(id->h, offset, pBuf, cnt, &id->lastErrno); -} + * The extra flags to use in calls to the Win32 heap APIs. This value may be + * zero for the default behavior. + */ +#ifndef SQLITE_WIN32_HEAP_FLAGS +# define SQLITE_WIN32_HEAP_FLAGS (0) +#endif /* -** Write data from a buffer into a file. Return SQLITE_OK on success -** or some other error code on failure. +** The winMemData structure stores information required by the Win32-specific +** sqlcipher_sqlite3_mem_methods implementation. */ -static int unixWrite( - sqlcipher_sqlite3_file *id, - const void *pBuf, - int amt, - sqlcipher_sqlite3_int64 offset -){ - unixFile *pFile = (unixFile*)id; - int wrote = 0; - assert( id ); - assert( amt>0 ); +typedef struct winMemData winMemData; +struct winMemData { +#ifndef NDEBUG + u32 magic1; /* Magic number to detect structure corruption. */ +#endif + HANDLE hHeap; /* The handle to our heap. */ + BOOL bOwned; /* Do we own the heap (i.e. destroy it on shutdown)? */ +#ifndef NDEBUG + u32 magic2; /* Magic number to detect structure corruption. */ +#endif +}; - /* If this is a database file (not a journal, super-journal or temp - ** file), the bytes in the locking range should never be read or written. */ -#if 0 - assert( pFile->pPreallocatedUnused==0 - || offset>=PENDING_BYTE+512 - || offset+amt<=PENDING_BYTE - ); +#ifndef NDEBUG +#define WINMEM_MAGIC1 0x42b2830b +#define WINMEM_MAGIC2 0xbd4d7cf4 #endif -#ifdef SQLITE_DEBUG - /* If we are doing a normal write to a database file (as opposed to - ** doing a hot-journal rollback or a write to some file other than a - ** normal database file) then record the fact that the database - ** has changed. If the transaction counter is modified, record that - ** fact too. - */ - if( pFile->inNormalWrite ){ - pFile->dbUpdate = 1; /* The database has been modified */ - if( offset<=24 && offset+amt>=27 ){ - int rc; - char oldCntr[4]; - SimulateIOErrorBenign(1); - rc = seekAndRead(pFile, 24, oldCntr, 4); - SimulateIOErrorBenign(0); - if( rc!=4 || memcmp(oldCntr, &((char*)pBuf)[24-offset], 4)!=0 ){ - pFile->transCntrChng = 1; /* The transaction counter has changed */ - } - } - } +static struct winMemData win_mem_data = { +#ifndef NDEBUG + WINMEM_MAGIC1, +#endif + NULL, FALSE +#ifndef NDEBUG + ,WINMEM_MAGIC2 #endif +}; -#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 - /* Deal with as much of this write request as possible by transfering - ** data from the memory mapping using memcpy(). */ - if( offsetmmapSize ){ - if( offset+amt <= pFile->mmapSize ){ - memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); - return SQLITE_OK; - }else{ - int nCopy = pFile->mmapSize - offset; - memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy); - pBuf = &((u8 *)pBuf)[nCopy]; - amt -= nCopy; - offset += nCopy; - } - } +#ifndef NDEBUG +#define winMemAssertMagic1() assert( win_mem_data.magic1==WINMEM_MAGIC1 ) +#define winMemAssertMagic2() assert( win_mem_data.magic2==WINMEM_MAGIC2 ) +#define winMemAssertMagic() winMemAssertMagic1(); winMemAssertMagic2(); +#else +#define winMemAssertMagic() #endif - while( (wrote = seekAndWrite(pFile, offset, pBuf, amt))0 ){ - amt -= wrote; - offset += wrote; - pBuf = &((char*)pBuf)[wrote]; - } - SimulateIOError(( wrote=(-1), amt=1 )); - SimulateDiskfullError(( wrote=0, amt=1 )); +#define winMemGetDataPtr() &win_mem_data +#define winMemGetHeap() win_mem_data.hHeap +#define winMemGetOwned() win_mem_data.bOwned - if( amt>wrote ){ - if( wrote<0 && pFile->lastErrno!=ENOSPC ){ - /* lastErrno set by seekAndWrite */ - return SQLITE_IOERR_WRITE; - }else{ - storeLastErrno(pFile, 0); /* not a system error */ - return SQLITE_FULL; - } - } +static void *winMemMalloc(int nBytes); +static void winMemFree(void *pPrior); +static void *winMemRealloc(void *pPrior, int nBytes); +static int winMemSize(void *p); +static int winMemRoundup(int n); +static int winMemInit(void *pAppData); +static void winMemShutdown(void *pAppData); - return SQLITE_OK; -} +SQLITE_PRIVATE const sqlcipher_sqlite3_mem_methods *sqlcipher_sqlite3MemGetWin32(void); +#endif /* SQLITE_WIN32_MALLOC */ -#ifdef SQLITE_TEST /* -** Count the number of fullsyncs and normal syncs. This is used to test -** that syncs and fullsyncs are occurring at the right times. +** The following variable is (normally) set once and never changes +** thereafter. It records whether the operating system is Win9x +** or WinNT. +** +** 0: Operating system unknown. +** 1: Operating system is Win9x. +** 2: Operating system is WinNT. +** +** In order to facilitate testing on a WinNT system, the test fixture +** can manually set this value to 1 to emulate Win98 behavior. */ -SQLITE_API int sqlcipher_sqlite3_sync_count = 0; -SQLITE_API int sqlcipher_sqlite3_fullsync_count = 0; +#ifdef SQLITE_TEST +SQLITE_API LONG SQLITE_WIN32_VOLATILE sqlcipher_sqlite3_os_type = 0; +#else +static LONG SQLITE_WIN32_VOLATILE sqlcipher_sqlite3_os_type = 0; +#endif + +#ifndef SYSCALL +# define SYSCALL sqlcipher_sqlite3_syscall_ptr #endif /* -** We do not trust systems to provide a working fdatasync(). Some do. -** Others do no. To be safe, we will stick with the (slightly slower) -** fsync(). If you know that your system does support fdatasync() correctly, -** then simply compile with -Dfdatasync=fdatasync or -DHAVE_FDATASYNC -*/ -#if !defined(fdatasync) && !HAVE_FDATASYNC -# define fdatasync fsync +** This function is not available on Windows CE or WinRT. + */ + +#if SQLITE_OS_WINCE || SQLITE_OS_WINRT +# define osAreFileApisANSI() 1 #endif /* -** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not -** the F_FULLFSYNC macro is defined. F_FULLFSYNC is currently -** only available on Mac OS X. But that could change. +** Many system calls are accessed through pointer-to-functions so that +** they may be overridden at runtime to facilitate fault injection during +** testing and sandboxing. The following array holds the names and pointers +** to all overrideable system calls. */ -#ifdef F_FULLFSYNC -# define HAVE_FULLFSYNC 1 +static struct win_syscall { + const char *zName; /* Name of the system call */ + sqlcipher_sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ + sqlcipher_sqlite3_syscall_ptr pDefault; /* Default value */ +} aSyscall[] = { +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT + { "AreFileApisANSI", (SYSCALL)AreFileApisANSI, 0 }, #else -# define HAVE_FULLFSYNC 0 + { "AreFileApisANSI", (SYSCALL)0, 0 }, #endif +#ifndef osAreFileApisANSI +#define osAreFileApisANSI ((BOOL(WINAPI*)(VOID))aSyscall[0].pCurrent) +#endif -/* -** The fsync() system call does not work as advertised on many -** unix systems. The following procedure is an attempt to make -** it work better. -** -** The SQLITE_NO_SYNC macro disables all fsync()s. This is useful -** for testing when we want to run through the test suite quickly. -** You are strongly advised *not* to deploy with SQLITE_NO_SYNC -** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash -** or power failure will likely corrupt the database file. -** -** SQLite sets the dataOnly flag if the size of the file is unchanged. -** The idea behind dataOnly is that it should only write the file content -** to disk, not the inode. We only set dataOnly if the file size is -** unchanged since the file size is part of the inode. However, -** Ted Ts'o tells us that fdatasync() will also write the inode if the -** file size has changed. The only real difference between fdatasync() -** and fsync(), Ted tells us, is that fdatasync() will not flush the -** inode if the mtime or owner or other inode attributes have changed. -** We only care about the file size, not the other file attributes, so -** as far as SQLite is concerned, an fdatasync() is always adequate. -** So, we always use fdatasync() if it is available, regardless of -** the value of the dataOnly flag. -*/ -static int full_fsync(int fd, int fullSync, int dataOnly){ - int rc; +#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) + { "CharLowerW", (SYSCALL)CharLowerW, 0 }, +#else + { "CharLowerW", (SYSCALL)0, 0 }, +#endif - /* The following "ifdef/elif/else/" block has the same structure as - ** the one below. It is replicated here solely to avoid cluttering - ** up the real code with the UNUSED_PARAMETER() macros. - */ -#ifdef SQLITE_NO_SYNC - UNUSED_PARAMETER(fd); - UNUSED_PARAMETER(fullSync); - UNUSED_PARAMETER(dataOnly); -#elif HAVE_FULLFSYNC - UNUSED_PARAMETER(dataOnly); +#define osCharLowerW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[1].pCurrent) + +#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) + { "CharUpperW", (SYSCALL)CharUpperW, 0 }, #else - UNUSED_PARAMETER(fullSync); - UNUSED_PARAMETER(dataOnly); + { "CharUpperW", (SYSCALL)0, 0 }, #endif - /* Record the number of times that we do a normal fsync() and - ** FULLSYNC. This is used during testing to verify that this procedure - ** gets called with the correct arguments. - */ -#ifdef SQLITE_TEST - if( fullSync ) sqlcipher_sqlite3_fullsync_count++; - sqlcipher_sqlite3_sync_count++; +#define osCharUpperW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[2].pCurrent) + + { "CloseHandle", (SYSCALL)CloseHandle, 0 }, + +#define osCloseHandle ((BOOL(WINAPI*)(HANDLE))aSyscall[3].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "CreateFileA", (SYSCALL)CreateFileA, 0 }, +#else + { "CreateFileA", (SYSCALL)0, 0 }, #endif - /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a - ** no-op. But go ahead and call fstat() to validate the file - ** descriptor as we need a method to provoke a failure during - ** coverate testing. - */ -#ifdef SQLITE_NO_SYNC - { - struct stat buf; - rc = osFstat(fd, &buf); - } -#elif HAVE_FULLFSYNC - if( fullSync ){ - rc = osFcntl(fd, F_FULLFSYNC, 0); - }else{ - rc = 1; - } - /* If the FULLFSYNC failed, fall back to attempting an fsync(). - ** It shouldn't be possible for fullfsync to fail on the local - ** file system (on OSX), so failure indicates that FULLFSYNC - ** isn't supported for this file system. So, attempt an fsync - ** and (for now) ignore the overhead of a superfluous fcntl call. - ** It'd be better to detect fullfsync support once and avoid - ** the fcntl call every time sync is called. - */ - if( rc ) rc = fsync(fd); +#define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \ + LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent) -#elif defined(__APPLE__) - /* fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly - ** so currently we default to the macro that redefines fdatasync to fsync - */ - rc = fsync(fd); +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) + { "CreateFileW", (SYSCALL)CreateFileW, 0 }, #else - rc = fdatasync(fd); -#if OS_VXWORKS - if( rc==-1 && errno==ENOTSUP ){ - rc = fsync(fd); - } -#endif /* OS_VXWORKS */ -#endif /* ifdef SQLITE_NO_SYNC elif HAVE_FULLFSYNC */ + { "CreateFileW", (SYSCALL)0, 0 }, +#endif - if( OS_VXWORKS && rc!= -1 ){ - rc = 0; - } - return rc; -} +#define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \ + LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent) -/* -** Open a file descriptor to the directory containing file zFilename. -** If successful, *pFd is set to the opened file descriptor and -** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM -** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined -** value. -** -** The directory file descriptor is used for only one thing - to -** fsync() a directory to make sure file creation and deletion events -** are flushed to disk. Such fsyncs are not needed on newer -** journaling filesystems, but are required on older filesystems. -** -** This routine can be overridden using the xSetSysCall interface. -** The ability to override this routine was added in support of the -** chromium sandbox. Opening a directory is a security risk (we are -** told) so making it overrideable allows the chromium sandbox to -** replace this routine with a harmless no-op. To make this routine -** a no-op, replace it with a stub that returns SQLITE_OK but leaves -** *pFd set to a negative number. -** -** If SQLITE_OK is returned, the caller is responsible for closing -** the file descriptor *pFd using close(). -*/ -static int openDirectory(const char *zFilename, int *pFd){ - int ii; - int fd = -1; - char zDirname[MAX_PATHNAME+1]; +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \ + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) && \ + SQLITE_WIN32_CREATEFILEMAPPINGA + { "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 }, +#else + { "CreateFileMappingA", (SYSCALL)0, 0 }, +#endif - sqlcipher_sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename); - for(ii=(int)strlen(zDirname); ii>0 && zDirname[ii]!='/'; ii--); - if( ii>0 ){ - zDirname[ii] = '\0'; - }else{ - if( zDirname[0]!='/' ) zDirname[0] = '.'; - zDirname[1] = 0; - } - fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); - if( fd>=0 ){ - OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); - } - *pFd = fd; - if( fd>=0 ) return SQLITE_OK; - return unixLogError(SQLITE_CANTOPEN_BKPT, "openDirectory", zDirname); -} +#define osCreateFileMappingA ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ + DWORD,DWORD,DWORD,LPCSTR))aSyscall[6].pCurrent) -/* -** Make sure all writes to a particular file are committed to disk. -** -** If dataOnly==0 then both the file itself and its metadata (file -** size, access time, etc) are synced. If dataOnly!=0 then only the -** file data is synced. -** -** Under Unix, also make sure that the directory entry for the file -** has been created by fsync-ing the directory that contains the file. -** If we do not do this and we encounter a power failure, the directory -** entry for the journal might not exist after we reboot. The next -** SQLite to access the file will not know that the journal exists (because -** the directory entry for the journal was never created) and the transaction -** will not roll back - possibly leading to database corruption. -*/ -static int unixSync(sqlcipher_sqlite3_file *id, int flags){ - int rc; - unixFile *pFile = (unixFile*)id; +#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)) + { "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 }, +#else + { "CreateFileMappingW", (SYSCALL)0, 0 }, +#endif - int isDataOnly = (flags&SQLITE_SYNC_DATAONLY); - int isFullsync = (flags&0x0F)==SQLITE_SYNC_FULL; +#define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ + DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent) - /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */ - assert((flags&0x0F)==SQLITE_SYNC_NORMAL - || (flags&0x0F)==SQLITE_SYNC_FULL - ); +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) + { "CreateMutexW", (SYSCALL)CreateMutexW, 0 }, +#else + { "CreateMutexW", (SYSCALL)0, 0 }, +#endif - /* Unix cannot, but some systems may return SQLITE_FULL from here. This - ** line is to test that doing so does not cause any problems. - */ - SimulateDiskfullError( return SQLITE_FULL ); +#define osCreateMutexW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,BOOL, \ + LPCWSTR))aSyscall[8].pCurrent) - assert( pFile ); - OSTRACE(("SYNC %-3d\n", pFile->h)); - rc = full_fsync(pFile->h, isFullsync, isDataOnly); - SimulateIOError( rc=1 ); - if( rc ){ - storeLastErrno(pFile, errno); - return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath); - } +#if defined(SQLITE_WIN32_HAS_ANSI) + { "DeleteFileA", (SYSCALL)DeleteFileA, 0 }, +#else + { "DeleteFileA", (SYSCALL)0, 0 }, +#endif - /* Also fsync the directory containing the file if the DIRSYNC flag - ** is set. This is a one-time occurrence. Many systems (examples: AIX) - ** are unable to fsync a directory, so ignore errors on the fsync. - */ - if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){ - int dirfd; - OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath, - HAVE_FULLFSYNC, isFullsync)); - rc = osOpenDirectory(pFile->zPath, &dirfd); - if( rc==SQLITE_OK ){ - full_fsync(dirfd, 0, 0); - robust_close(pFile, dirfd, __LINE__); - }else{ - assert( rc==SQLITE_CANTOPEN ); - rc = SQLITE_OK; - } - pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC; - } - return rc; -} +#define osDeleteFileA ((BOOL(WINAPI*)(LPCSTR))aSyscall[9].pCurrent) -/* -** Truncate an open file to a specified size -*/ -static int unixTruncate(sqlcipher_sqlite3_file *id, i64 nByte){ - unixFile *pFile = (unixFile *)id; - int rc; - assert( pFile ); - SimulateIOError( return SQLITE_IOERR_TRUNCATE ); +#if defined(SQLITE_WIN32_HAS_WIDE) + { "DeleteFileW", (SYSCALL)DeleteFileW, 0 }, +#else + { "DeleteFileW", (SYSCALL)0, 0 }, +#endif - /* If the user has configured a chunk-size for this file, truncate the - ** file so that it consists of an integer number of chunks (i.e. the - ** actual file size after the operation may be larger than the requested - ** size). - */ - if( pFile->szChunk>0 ){ - nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; - } +#define osDeleteFileW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[10].pCurrent) - rc = robust_ftruncate(pFile->h, nByte); - if( rc ){ - storeLastErrno(pFile, errno); - return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); - }else{ -#ifdef SQLITE_DEBUG - /* If we are doing a normal write to a database file (as opposed to - ** doing a hot-journal rollback or a write to some file other than a - ** normal database file) and we truncate the file to zero length, - ** that effectively updates the change counter. This might happen - ** when restoring a database using the backup API from a zero-length - ** source. - */ - if( pFile->inNormalWrite && nByte==0 ){ - pFile->transCntrChng = 1; - } +#if SQLITE_OS_WINCE + { "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 }, +#else + { "FileTimeToLocalFileTime", (SYSCALL)0, 0 }, #endif -#if SQLITE_MAX_MMAP_SIZE>0 - /* If the file was just truncated to a size smaller than the currently - ** mapped region, reduce the effective mapping size as well. SQLite will - ** use read() and write() to access data beyond this point from now on. - */ - if( nBytemmapSize ){ - pFile->mmapSize = nByte; - } +#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \ + LPFILETIME))aSyscall[11].pCurrent) + +#if SQLITE_OS_WINCE + { "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 }, +#else + { "FileTimeToSystemTime", (SYSCALL)0, 0 }, #endif - return SQLITE_OK; - } -} +#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \ + LPSYSTEMTIME))aSyscall[12].pCurrent) -/* -** Determine the current size of a file in bytes -*/ -static int unixFileSize(sqlcipher_sqlite3_file *id, i64 *pSize){ - int rc; - struct stat buf; - assert( id ); - rc = osFstat(((unixFile*)id)->h, &buf); - SimulateIOError( rc=1 ); - if( rc!=0 ){ - storeLastErrno((unixFile*)id, errno); - return SQLITE_IOERR_FSTAT; - } - *pSize = buf.st_size; + { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 }, - /* When opening a zero-size database, the findInodeInfo() procedure - ** writes a single byte into that file in order to work around a bug - ** in the OS-X msdos filesystem. In order to avoid problems with upper - ** layers, we need to report this file size as zero even though it is - ** really 1. Ticket #3260. - */ - if( *pSize==1 ) *pSize = 0; +#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[13].pCurrent) +#if defined(SQLITE_WIN32_HAS_ANSI) + { "FormatMessageA", (SYSCALL)FormatMessageA, 0 }, +#else + { "FormatMessageA", (SYSCALL)0, 0 }, +#endif - return SQLITE_OK; -} +#define osFormatMessageA ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPSTR, \ + DWORD,va_list*))aSyscall[14].pCurrent) -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) -/* -** Handler for proxy-locking file-control verbs. Defined below in the -** proxying locking division. -*/ -static int proxyFileControl(sqlcipher_sqlite3_file*,int,void*); +#if defined(SQLITE_WIN32_HAS_WIDE) + { "FormatMessageW", (SYSCALL)FormatMessageW, 0 }, +#else + { "FormatMessageW", (SYSCALL)0, 0 }, #endif -/* -** This function is called to handle the SQLITE_FCNTL_SIZE_HINT -** file-control operation. Enlarge the database to nBytes in size -** (rounded up to the next chunk-size). If the database is already -** nBytes or larger, this routine is a no-op. -*/ -static int fcntlSizeHint(unixFile *pFile, i64 nByte){ - if( pFile->szChunk>0 ){ - i64 nSize; /* Required file size */ - struct stat buf; /* Used to hold return values of fstat() */ +#define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \ + DWORD,va_list*))aSyscall[15].pCurrent) - if( osFstat(pFile->h, &buf) ){ - return SQLITE_IOERR_FSTAT; - } +#if !defined(SQLITE_OMIT_LOAD_EXTENSION) + { "FreeLibrary", (SYSCALL)FreeLibrary, 0 }, +#else + { "FreeLibrary", (SYSCALL)0, 0 }, +#endif - nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; - if( nSize>(i64)buf.st_size ){ +#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[16].pCurrent) -#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE - /* The code below is handling the return value of osFallocate() - ** correctly. posix_fallocate() is defined to "returns zero on success, - ** or an error number on failure". See the manpage for details. */ - int err; - do{ - err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); - }while( err==EINTR ); - if( err && err!=EINVAL ) return SQLITE_IOERR_WRITE; + { "GetCurrentProcessId", (SYSCALL)GetCurrentProcessId, 0 }, + +#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[17].pCurrent) + +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) + { "GetDiskFreeSpaceA", (SYSCALL)GetDiskFreeSpaceA, 0 }, #else - /* If the OS does not have posix_fallocate(), fake it. Write a - ** single byte to the last byte in each block that falls entirely - ** within the extended region. Then, if required, a single byte - ** at offset (nSize-1), to set the size of the file correctly. - ** This is a similar technique to that used by glibc on systems - ** that do not have a real fallocate() call. - */ - int nBlk = buf.st_blksize; /* File-system block size */ - int nWrite = 0; /* Number of bytes written by seekAndWrite */ - i64 iWrite; /* Next offset to write to */ + { "GetDiskFreeSpaceA", (SYSCALL)0, 0 }, +#endif - iWrite = (buf.st_size/nBlk)*nBlk + nBlk - 1; - assert( iWrite>=buf.st_size ); - assert( ((iWrite+1)%nBlk)==0 ); - for(/*no-op*/; iWrite=nSize ) iWrite = nSize - 1; - nWrite = seekAndWrite(pFile, iWrite, "", 1); - if( nWrite!=1 ) return SQLITE_IOERR_WRITE; - } +#define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \ + LPDWORD))aSyscall[18].pCurrent) + +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) + { "GetDiskFreeSpaceW", (SYSCALL)GetDiskFreeSpaceW, 0 }, +#else + { "GetDiskFreeSpaceW", (SYSCALL)0, 0 }, #endif - } - } -#if SQLITE_MAX_MMAP_SIZE>0 - if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){ - int rc; - if( pFile->szChunk<=0 ){ - if( robust_ftruncate(pFile->h, nByte) ){ - storeLastErrno(pFile, errno); - return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); - } - } +#define osGetDiskFreeSpaceW ((BOOL(WINAPI*)(LPCWSTR,LPDWORD,LPDWORD,LPDWORD, \ + LPDWORD))aSyscall[19].pCurrent) - rc = unixMapfile(pFile, nByte); - return rc; - } +#if defined(SQLITE_WIN32_HAS_ANSI) + { "GetFileAttributesA", (SYSCALL)GetFileAttributesA, 0 }, +#else + { "GetFileAttributesA", (SYSCALL)0, 0 }, #endif - return SQLITE_OK; -} +#define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[20].pCurrent) -/* -** If *pArg is initially negative then this is a query. Set *pArg to -** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set. -** -** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags. -*/ -static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ - if( *pArg<0 ){ - *pArg = (pFile->ctrlFlags & mask)!=0; - }else if( (*pArg)==0 ){ - pFile->ctrlFlags &= ~mask; - }else{ - pFile->ctrlFlags |= mask; - } -} +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) + { "GetFileAttributesW", (SYSCALL)GetFileAttributesW, 0 }, +#else + { "GetFileAttributesW", (SYSCALL)0, 0 }, +#endif -/* Forward declaration */ -static int unixGetTempname(int nBuf, char *zBuf); +#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[21].pCurrent) -/* -** Information and control of an open file handle. -*/ -static int unixFileControl(sqlcipher_sqlite3_file *id, int op, void *pArg){ - unixFile *pFile = (unixFile*)id; - switch( op ){ -#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) - case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: { - int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE); - return rc ? SQLITE_IOERR_BEGIN_ATOMIC : SQLITE_OK; - } - case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: { - int rc = osIoctl(pFile->h, F2FS_IOC_COMMIT_ATOMIC_WRITE); - return rc ? SQLITE_IOERR_COMMIT_ATOMIC : SQLITE_OK; - } - case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: { - int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE); - return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK; - } -#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ +#if defined(SQLITE_WIN32_HAS_WIDE) + { "GetFileAttributesExW", (SYSCALL)GetFileAttributesExW, 0 }, +#else + { "GetFileAttributesExW", (SYSCALL)0, 0 }, +#endif - case SQLITE_FCNTL_LOCKSTATE: { - *(int*)pArg = pFile->eFileLock; - return SQLITE_OK; - } - case SQLITE_FCNTL_LAST_ERRNO: { - *(int*)pArg = pFile->lastErrno; - return SQLITE_OK; - } - case SQLITE_FCNTL_CHUNK_SIZE: { - pFile->szChunk = *(int *)pArg; - return SQLITE_OK; - } - case SQLITE_FCNTL_SIZE_HINT: { - int rc; - SimulateIOErrorBenign(1); - rc = fcntlSizeHint(pFile, *(i64 *)pArg); - SimulateIOErrorBenign(0); - return rc; - } - case SQLITE_FCNTL_PERSIST_WAL: { - unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg); - return SQLITE_OK; - } - case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { - unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg); - return SQLITE_OK; - } - case SQLITE_FCNTL_VFSNAME: { - *(char**)pArg = sqlcipher_sqlite3_mprintf("%s", pFile->pVfs->zName); - return SQLITE_OK; - } - case SQLITE_FCNTL_TEMPFILENAME: { - char *zTFile = sqlcipher_sqlite3_malloc64( pFile->pVfs->mxPathname ); - if( zTFile ){ - unixGetTempname(pFile->pVfs->mxPathname, zTFile); - *(char**)pArg = zTFile; - } - return SQLITE_OK; - } - case SQLITE_FCNTL_HAS_MOVED: { - *(int*)pArg = fileHasMoved(pFile); - return SQLITE_OK; - } -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT - case SQLITE_FCNTL_LOCK_TIMEOUT: { - int iOld = pFile->iBusyTimeout; - pFile->iBusyTimeout = *(int*)pArg; - *(int*)pArg = iOld; - return SQLITE_OK; - } +#define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \ + LPVOID))aSyscall[22].pCurrent) + +#if !SQLITE_OS_WINRT + { "GetFileSize", (SYSCALL)GetFileSize, 0 }, +#else + { "GetFileSize", (SYSCALL)0, 0 }, #endif -#if SQLITE_MAX_MMAP_SIZE>0 - case SQLITE_FCNTL_MMAP_SIZE: { - i64 newLimit = *(i64*)pArg; - int rc = SQLITE_OK; - if( newLimit>sqlcipher_sqlite3GlobalConfig.mxMmap ){ - newLimit = sqlcipher_sqlite3GlobalConfig.mxMmap; - } - /* The value of newLimit may be eventually cast to (size_t) and passed - ** to mmap(). Restrict its value to 2GB if (size_t) is not at least a - ** 64-bit type. */ - if( newLimit>0 && sizeof(size_t)<8 ){ - newLimit = (newLimit & 0x7FFFFFFF); - } +#define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[23].pCurrent) - *(i64*)pArg = pFile->mmapSizeMax; - if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ - pFile->mmapSizeMax = newLimit; - if( pFile->mmapSize>0 ){ - unixUnmapfile(pFile); - rc = unixMapfile(pFile, -1); - } - } - return rc; - } +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) + { "GetFullPathNameA", (SYSCALL)GetFullPathNameA, 0 }, +#else + { "GetFullPathNameA", (SYSCALL)0, 0 }, #endif -#ifdef SQLITE_DEBUG - /* The pager calls this method to signal that it has done - ** a rollback and that the database is therefore unchanged and - ** it hence it is OK for the transaction change counter to be - ** unchanged. - */ - case SQLITE_FCNTL_DB_UNCHANGED: { - ((unixFile*)id)->dbUpdate = 0; - return SQLITE_OK; - } + +#define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \ + LPSTR*))aSyscall[24].pCurrent) + +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) + { "GetFullPathNameW", (SYSCALL)GetFullPathNameW, 0 }, +#else + { "GetFullPathNameW", (SYSCALL)0, 0 }, #endif -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) - case SQLITE_FCNTL_SET_LOCKPROXYFILE: - case SQLITE_FCNTL_GET_LOCKPROXYFILE: { - return proxyFileControl(id,op,pArg); - } -#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ - } - return SQLITE_NOTFOUND; -} -/* -** If pFd->sectorSize is non-zero when this function is called, it is a -** no-op. Otherwise, the values of pFd->sectorSize and -** pFd->deviceCharacteristics are set according to the file-system -** characteristics. -** -** There are two versions of this function. One for QNX and one for all -** other systems. -*/ -#ifndef __QNXNTO__ -static void setDeviceCharacteristics(unixFile *pFd){ - assert( pFd->deviceCharacteristics==0 || pFd->sectorSize!=0 ); - if( pFd->sectorSize==0 ){ -#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) - int res; - u32 f = 0; +#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \ + LPWSTR*))aSyscall[25].pCurrent) - /* Check for support for F2FS atomic batch writes. */ - res = osIoctl(pFd->h, F2FS_IOC_GET_FEATURES, &f); - if( res==0 && (f & F2FS_FEATURE_ATOMIC_WRITE) ){ - pFd->deviceCharacteristics = SQLITE_IOCAP_BATCH_ATOMIC; - } -#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ + { "GetLastError", (SYSCALL)GetLastError, 0 }, - /* Set the POWERSAFE_OVERWRITE flag if requested. */ - if( pFd->ctrlFlags & UNIXFILE_PSOW ){ - pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; - } +#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent) - pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; - } -} +#if !defined(SQLITE_OMIT_LOAD_EXTENSION) +#if SQLITE_OS_WINCE + /* The GetProcAddressA() routine is only available on Windows CE. */ + { "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 }, #else -#include -#include -static void setDeviceCharacteristics(unixFile *pFile){ - if( pFile->sectorSize == 0 ){ - struct statvfs fsInfo; + /* All other Windows platforms expect GetProcAddress() to take + ** an ANSI string regardless of the _UNICODE setting */ + { "GetProcAddressA", (SYSCALL)GetProcAddress, 0 }, +#endif +#else + { "GetProcAddressA", (SYSCALL)0, 0 }, +#endif - /* Set defaults for non-supported filesystems */ - pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; - pFile->deviceCharacteristics = 0; - if( fstatvfs(pFile->h, &fsInfo) == -1 ) { - return; - } +#define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \ + LPCSTR))aSyscall[27].pCurrent) - if( !strcmp(fsInfo.f_basetype, "tmp") ) { - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - SQLITE_IOCAP_ATOMIC4K | /* All ram filesystem writes are atomic */ - SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until - ** the write succeeds */ - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; - }else if( strstr(fsInfo.f_basetype, "etfs") ){ - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - /* etfs cluster size writes are atomic */ - (pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) | - SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until - ** the write succeeds */ - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; - }else if( !strcmp(fsInfo.f_basetype, "qnx6") ){ - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - SQLITE_IOCAP_ATOMIC | /* All filesystem writes are atomic */ - SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until - ** the write succeeds */ - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; - }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){ - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - /* full bitset of atomics from max sector size and smaller */ - ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; - }else if( strstr(fsInfo.f_basetype, "dos") ){ - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - /* full bitset of atomics from max sector size and smaller */ - ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; - }else{ - pFile->deviceCharacteristics = - SQLITE_IOCAP_ATOMIC512 | /* blocks are atomic */ - SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until - ** the write succeeds */ - 0; - } - } - /* Last chance verification. If the sector size isn't a multiple of 512 - ** then it isn't valid.*/ - if( pFile->sectorSize % 512 != 0 ){ - pFile->deviceCharacteristics = 0; - pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; - } -} +#if !SQLITE_OS_WINRT + { "GetSystemInfo", (SYSCALL)GetSystemInfo, 0 }, +#else + { "GetSystemInfo", (SYSCALL)0, 0 }, #endif -/* -** Return the sector size in bytes of the underlying block device for -** the specified file. This is almost always 512 bytes, but may be -** larger for some devices. -** -** SQLite code assumes this function cannot fail. It also assumes that -** if two files are created in the same file-system directory (i.e. -** a database and its journal file) that the sector size will be the -** same for both. -*/ -static int unixSectorSize(sqlcipher_sqlite3_file *id){ - unixFile *pFd = (unixFile*)id; - setDeviceCharacteristics(pFd); - return pFd->sectorSize; -} +#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[28].pCurrent) -/* -** Return the device characteristics for the file. -** -** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default. -** However, that choice is controversial since technically the underlying -** file system does not always provide powersafe overwrites. (In other -** words, after a power-loss event, parts of the file that were never -** written might end up being altered.) However, non-PSOW behavior is very, -** very rare. And asserting PSOW makes a large reduction in the amount -** of required I/O for journaling, since a lot of padding is eliminated. -** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control -** available to turn it off and URI query parameter available to turn it off. -*/ -static int unixDeviceCharacteristics(sqlcipher_sqlite3_file *id){ - unixFile *pFd = (unixFile*)id; - setDeviceCharacteristics(pFd); - return pFd->deviceCharacteristics; -} + { "GetSystemTime", (SYSCALL)GetSystemTime, 0 }, -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 +#define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[29].pCurrent) -/* -** Return the system page size. -** -** This function should not be called directly by other code in this file. -** Instead, it should be called via macro osGetpagesize(). -*/ -static int unixGetpagesize(void){ -#if OS_VXWORKS - return 1024; -#elif defined(_BSD_SOURCE) - return getpagesize(); +#if !SQLITE_OS_WINCE + { "GetSystemTimeAsFileTime", (SYSCALL)GetSystemTimeAsFileTime, 0 }, #else - return (int)sysconf(_SC_PAGESIZE); + { "GetSystemTimeAsFileTime", (SYSCALL)0, 0 }, #endif -} - -#endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */ -#ifndef SQLITE_OMIT_WAL +#define osGetSystemTimeAsFileTime ((VOID(WINAPI*)( \ + LPFILETIME))aSyscall[30].pCurrent) -/* -** Object used to represent an shared memory buffer. -** -** When multiple threads all reference the same wal-index, each thread -** has its own unixShm object, but they all point to a single instance -** of this unixShmNode object. In other words, each wal-index is opened -** only once per process. -** -** Each unixShmNode object is connected to a single unixInodeInfo object. -** We could coalesce this object into unixInodeInfo, but that would mean -** every open file that does not use shared memory (in other words, most -** open files) would have to carry around this extra information. So -** the unixInodeInfo object contains a pointer to this unixShmNode object -** and the unixShmNode object is created only when needed. -** -** unixMutexHeld() must be true when creating or destroying -** this object or while reading or writing the following fields: -** -** nRef -** -** The following fields are read-only after the object is created: -** -** hShm -** zFilename -** -** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and -** unixMutexHeld() is true when reading or writing any other field -** in this structure. -*/ -struct unixShmNode { - unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ - sqlcipher_sqlite3_mutex *pShmMutex; /* Mutex to access this object */ - char *zFilename; /* Name of the mmapped file */ - int hShm; /* Open file descriptor */ - int szRegion; /* Size of shared-memory regions */ - u16 nRegion; /* Size of array apRegion */ - u8 isReadonly; /* True if read-only */ - u8 isUnlocked; /* True if no DMS lock held */ - char **apRegion; /* Array of mapped shared-memory regions */ - int nRef; /* Number of unixShm objects pointing to this */ - unixShm *pFirst; /* All unixShm objects pointing to this */ - int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */ -#ifdef SQLITE_DEBUG - u8 exclMask; /* Mask of exclusive locks held */ - u8 sharedMask; /* Mask of shared locks held */ - u8 nextShmId; /* Next available unixShm.id value */ +#if defined(SQLITE_WIN32_HAS_ANSI) + { "GetTempPathA", (SYSCALL)GetTempPathA, 0 }, +#else + { "GetTempPathA", (SYSCALL)0, 0 }, #endif -}; - -/* -** Structure used internally by this VFS to record the state of an -** open shared memory connection. -** -** The following fields are initialized when this object is created and -** are read-only thereafter: -** -** unixShm.pShmNode -** unixShm.id -** -** All other fields are read/write. The unixShm.pShmNode->pShmMutex must -** be held while accessing any read/write fields. -*/ -struct unixShm { - unixShmNode *pShmNode; /* The underlying unixShmNode object */ - unixShm *pNext; /* Next unixShm with the same unixShmNode */ - u8 hasMutex; /* True if holding the unixShmNode->pShmMutex */ - u8 id; /* Id of this connection within its unixShmNode */ - u16 sharedMask; /* Mask of shared locks held */ - u16 exclMask; /* Mask of exclusive locks held */ -}; -/* -** Constants used for locking -*/ -#define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ -#define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ +#define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[31].pCurrent) -/* -** Apply posix advisory locks for all bytes from ofst through ofst+n-1. -** -** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking -** otherwise. -*/ -static int unixShmSystemLock( - unixFile *pFile, /* Open connection to the WAL file */ - int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ - int ofst, /* First byte of the locking range */ - int n /* Number of bytes to lock */ -){ - unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */ - struct flock f; /* The posix advisory locking structure */ - int rc = SQLITE_OK; /* Result code form fcntl() */ +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) + { "GetTempPathW", (SYSCALL)GetTempPathW, 0 }, +#else + { "GetTempPathW", (SYSCALL)0, 0 }, +#endif - /* Access to the unixShmNode object is serialized by the caller */ - pShmNode = pFile->pInode->pShmNode; - assert( pShmNode->nRef==0 || sqlcipher_sqlite3_mutex_held(pShmNode->pShmMutex) ); - assert( pShmNode->nRef>0 || unixMutexHeld() ); +#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[32].pCurrent) - /* Shared locks never span more than one byte */ - assert( n==1 || lockType!=F_RDLCK ); +#if !SQLITE_OS_WINRT + { "GetTickCount", (SYSCALL)GetTickCount, 0 }, +#else + { "GetTickCount", (SYSCALL)0, 0 }, +#endif - /* Locks are within range */ - assert( n>=1 && n<=SQLITE_SHM_NLOCK ); +#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent) - if( pShmNode->hShm>=0 ){ - int res; - /* Initialize the locking parameters */ - f.l_type = lockType; - f.l_whence = SEEK_SET; - f.l_start = ofst; - f.l_len = n; - res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); - if( res==-1 ){ -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT - rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY); +#if defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_GETVERSIONEX + { "GetVersionExA", (SYSCALL)GetVersionExA, 0 }, #else - rc = SQLITE_BUSY; + { "GetVersionExA", (SYSCALL)0, 0 }, #endif - } - } - /* Update the global lock state and do debug tracing */ -#ifdef SQLITE_DEBUG - { u16 mask; - OSTRACE(("SHM-LOCK ")); - mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<exclMask &= ~mask; - pShmNode->sharedMask &= ~mask; - }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock %d ok", ofst)); - pShmNode->exclMask &= ~mask; - pShmNode->sharedMask |= mask; - }else{ - assert( lockType==F_WRLCK ); - OSTRACE(("write-lock %d ok", ofst)); - pShmNode->exclMask |= mask; - pShmNode->sharedMask &= ~mask; - } - }else{ - if( lockType==F_UNLCK ){ - OSTRACE(("unlock %d failed", ofst)); - }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock failed")); - }else{ - assert( lockType==F_WRLCK ); - OSTRACE(("write-lock %d failed", ofst)); - } - } - OSTRACE((" - afterwards %03x,%03x\n", - pShmNode->sharedMask, pShmNode->exclMask)); - } +#define osGetVersionExA ((BOOL(WINAPI*)( \ + LPOSVERSIONINFOA))aSyscall[34].pCurrent) + +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ + SQLITE_WIN32_GETVERSIONEX + { "GetVersionExW", (SYSCALL)GetVersionExW, 0 }, +#else + { "GetVersionExW", (SYSCALL)0, 0 }, #endif - return rc; -} +#define osGetVersionExW ((BOOL(WINAPI*)( \ + LPOSVERSIONINFOW))aSyscall[35].pCurrent) -/* -** Return the minimum number of 32KB shm regions that should be mapped at -** a time, assuming that each mapping must be an integer multiple of the -** current system page-size. -** -** Usually, this is 1. The exception seems to be systems that are configured -** to use 64KB pages - in this case each mapping must cover at least two -** shm regions. -*/ -static int unixShmRegionPerMap(void){ - int shmsz = 32*1024; /* SHM region size */ - int pgsz = osGetpagesize(); /* System page size */ - assert( ((pgsz-1)&pgsz)==0 ); /* Page size must be a power of 2 */ - if( pgszpInode->pShmNode; - assert( unixMutexHeld() ); - if( p && ALWAYS(p->nRef==0) ){ - int nShmPerMap = unixShmRegionPerMap(); - int i; - assert( p->pInode==pFd->pInode ); - sqlcipher_sqlite3_mutex_free(p->pShmMutex); - for(i=0; inRegion; i+=nShmPerMap){ - if( p->hShm>=0 ){ - osMunmap(p->apRegion[i], p->szRegion); - }else{ - sqlcipher_sqlite3_free(p->apRegion[i]); - } - } - sqlcipher_sqlite3_free(p->apRegion); - if( p->hShm>=0 ){ - robust_close(pFd, p->hShm, __LINE__); - p->hShm = -1; - } - p->pInode->pShmNode = 0; - sqlcipher_sqlite3_free(p); - } -} +#define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \ + SIZE_T))aSyscall[36].pCurrent) -/* -** The DMS lock has not yet been taken on shm file pShmNode. Attempt to -** take it now. Return SQLITE_OK if successful, or an SQLite error -** code otherwise. -** -** If the DMS cannot be locked because this is a readonly_shm=1 -** connection and no other process already holds a lock, return -** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1. -*/ -static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ - struct flock lock; - int rc = SQLITE_OK; +#if !SQLITE_OS_WINRT + { "HeapCreate", (SYSCALL)HeapCreate, 0 }, +#else + { "HeapCreate", (SYSCALL)0, 0 }, +#endif - /* Use F_GETLK to determine the locks other processes are holding - ** on the DMS byte. If it indicates that another process is holding - ** a SHARED lock, then this process may also take a SHARED lock - ** and proceed with opening the *-shm file. - ** - ** Or, if no other process is holding any lock, then this process - ** is the first to open it. In this case take an EXCLUSIVE lock on the - ** DMS byte and truncate the *-shm file to zero bytes in size. Then - ** downgrade to a SHARED lock on the DMS byte. - ** - ** If another process is holding an EXCLUSIVE lock on the DMS byte, - ** return SQLITE_BUSY to the caller (it will try again). An earlier - ** version of this code attempted the SHARED lock at this point. But - ** this introduced a subtle race condition: if the process holding - ** EXCLUSIVE failed just before truncating the *-shm file, then this - ** process might open and use the *-shm file without truncating it. - ** And if the *-shm file has been corrupted by a power failure or - ** system crash, the database itself may also become corrupt. */ - lock.l_whence = SEEK_SET; - lock.l_start = UNIX_SHM_DMS; - lock.l_len = 1; - lock.l_type = F_WRLCK; - if( osFcntl(pShmNode->hShm, F_GETLK, &lock)!=0 ) { - rc = SQLITE_IOERR_LOCK; - }else if( lock.l_type==F_UNLCK ){ - if( pShmNode->isReadonly ){ - pShmNode->isUnlocked = 1; - rc = SQLITE_READONLY_CANTINIT; - }else{ - rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); - /* The first connection to attach must truncate the -shm file. We - ** truncate to 3 bytes (an arbitrary small number, less than the - ** -shm header size) rather than 0 as a system debugging aid, to - ** help detect if a -shm file truncation is legitimate or is the work - ** or a rogue process. */ - if( rc==SQLITE_OK && robust_ftruncate(pShmNode->hShm, 3) ){ - rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename); - } - } - }else if( lock.l_type==F_WRLCK ){ - rc = SQLITE_BUSY; - } +#define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \ + SIZE_T))aSyscall[37].pCurrent) - if( rc==SQLITE_OK ){ - assert( lock.l_type==F_UNLCK || lock.l_type==F_RDLCK ); - rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1); - } - return rc; -} +#if !SQLITE_OS_WINRT + { "HeapDestroy", (SYSCALL)HeapDestroy, 0 }, +#else + { "HeapDestroy", (SYSCALL)0, 0 }, +#endif -/* -** Open a shared-memory area associated with open database file pDbFd. -** This particular implementation uses mmapped files. -** -** The file used to implement shared-memory is in the same directory -** as the open database file and has the same name as the open database -** file with the "-shm" suffix added. For example, if the database file -** is "/home/user1/config.db" then the file that is created and mmapped -** for shared memory will be called "/home/user1/config.db-shm". -** -** Another approach to is to use files in /dev/shm or /dev/tmp or an -** some other tmpfs mount. But if a file in a different directory -** from the database file is used, then differing access permissions -** or a chroot() might cause two different processes on the same -** database to end up using different files for shared memory - -** meaning that their memory would not really be shared - resulting -** in database corruption. Nevertheless, this tmpfs file usage -** can be enabled at compile-time using -DSQLITE_SHM_DIRECTORY="/dev/shm" -** or the equivalent. The use of the SQLITE_SHM_DIRECTORY compile-time -** option results in an incompatible build of SQLite; builds of SQLite -** that with differing SQLITE_SHM_DIRECTORY settings attempt to use the -** same database file at the same time, database corruption will likely -** result. The SQLITE_SHM_DIRECTORY compile-time option is considered -** "unsupported" and may go away in a future SQLite release. -** -** When opening a new shared-memory file, if no other instances of that -** file are currently open, in this process or in other processes, then -** the file must be truncated to zero length or have its header cleared. -** -** If the original database file (pDbFd) is using the "unix-excl" VFS -** that means that an exclusive lock is held on the database file and -** that no other processes are able to read or write the database. In -** that case, we do not really need shared memory. No shared memory -** file is created. The shared memory will be simulated with heap memory. -*/ -static int unixOpenSharedMemory(unixFile *pDbFd){ - struct unixShm *p = 0; /* The connection to be opened */ - struct unixShmNode *pShmNode; /* The underlying mmapped file */ - int rc = SQLITE_OK; /* Result code */ - unixInodeInfo *pInode; /* The inode of fd */ - char *zShm; /* Name of the file used for SHM */ - int nShmFilename; /* Size of the SHM filename in bytes */ +#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[38].pCurrent) - /* Allocate space for the new unixShm object. */ - p = sqlcipher_sqlite3_malloc64( sizeof(*p) ); - if( p==0 ) return SQLITE_NOMEM_BKPT; - memset(p, 0, sizeof(*p)); - assert( pDbFd->pShm==0 ); + { "HeapFree", (SYSCALL)HeapFree, 0 }, - /* Check to see if a unixShmNode object already exists. Reuse an existing - ** one if present. Create a new one if necessary. - */ - assert( unixFileMutexNotheld(pDbFd) ); - unixEnterMutex(); - pInode = pDbFd->pInode; - pShmNode = pInode->pShmNode; - if( pShmNode==0 ){ - struct stat sStat; /* fstat() info for database file */ -#ifndef SQLITE_SHM_DIRECTORY - const char *zBasePath = pDbFd->zPath; -#endif +#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[39].pCurrent) - /* Call fstat() to figure out the permissions on the database file. If - ** a new *-shm file is created, an attempt will be made to create it - ** with the same permissions. - */ - if( osFstat(pDbFd->h, &sStat) ){ - rc = SQLITE_IOERR_FSTAT; - goto shm_open_err; - } + { "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 }, -#ifdef SQLITE_SHM_DIRECTORY - nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31; +#define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \ + SIZE_T))aSyscall[40].pCurrent) + + { "HeapSize", (SYSCALL)HeapSize, 0 }, + +#define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \ + LPCVOID))aSyscall[41].pCurrent) + +#if !SQLITE_OS_WINRT + { "HeapValidate", (SYSCALL)HeapValidate, 0 }, #else - nShmFilename = 6 + (int)strlen(zBasePath); + { "HeapValidate", (SYSCALL)0, 0 }, #endif - pShmNode = sqlcipher_sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename ); - if( pShmNode==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto shm_open_err; - } - memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename); - zShm = pShmNode->zFilename = (char*)&pShmNode[1]; -#ifdef SQLITE_SHM_DIRECTORY - sqlcipher_sqlite3_snprintf(nShmFilename, zShm, - SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x", - (u32)sStat.st_ino, (u32)sStat.st_dev); + +#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \ + LPCVOID))aSyscall[42].pCurrent) + +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT + { "HeapCompact", (SYSCALL)HeapCompact, 0 }, #else - sqlcipher_sqlite3_snprintf(nShmFilename, zShm, "%s-shm", zBasePath); - sqlcipher_sqlite3FileSuffix3(pDbFd->zPath, zShm); + { "HeapCompact", (SYSCALL)0, 0 }, #endif - pShmNode->hShm = -1; - pDbFd->pInode->pShmNode = pShmNode; - pShmNode->pInode = pDbFd->pInode; - if( sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ - pShmNode->pShmMutex = sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( pShmNode->pShmMutex==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto shm_open_err; - } - } - if( pInode->bProcessLock==0 ){ - if( 0==sqlcipher_sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ - pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT|O_NOFOLLOW, - (sStat.st_mode&0777)); - } - if( pShmNode->hShm<0 ){ - pShmNode->hShm = robust_open(zShm, O_RDONLY|O_NOFOLLOW, - (sStat.st_mode&0777)); - if( pShmNode->hShm<0 ){ - rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShm); - goto shm_open_err; - } - pShmNode->isReadonly = 1; - } +#define osHeapCompact ((UINT(WINAPI*)(HANDLE,DWORD))aSyscall[43].pCurrent) - /* If this process is running as root, make sure that the SHM file - ** is owned by the same user that owns the original database. Otherwise, - ** the original owner will not be able to connect. - */ - robustFchown(pShmNode->hShm, sStat.st_uid, sStat.st_gid); +#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION) + { "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 }, +#else + { "LoadLibraryA", (SYSCALL)0, 0 }, +#endif - rc = unixLockSharedMemory(pDbFd, pShmNode); - if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err; - } - } +#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[44].pCurrent) - /* Make the new connection a child of the unixShmNode */ - p->pShmNode = pShmNode; -#ifdef SQLITE_DEBUG - p->id = pShmNode->nextShmId++; +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ + !defined(SQLITE_OMIT_LOAD_EXTENSION) + { "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 }, +#else + { "LoadLibraryW", (SYSCALL)0, 0 }, #endif - pShmNode->nRef++; - pDbFd->pShm = p; - unixLeaveMutex(); - /* The reference count on pShmNode has already been incremented under - ** the cover of the unixEnterMutex() mutex and the pointer from the - ** new (struct unixShm) object to the pShmNode has been set. All that is - ** left to do is to link the new object into the linked list starting - ** at pShmNode->pFirst. This must be done while holding the - ** pShmNode->pShmMutex. - */ - sqlcipher_sqlite3_mutex_enter(pShmNode->pShmMutex); - p->pNext = pShmNode->pFirst; - pShmNode->pFirst = p; - sqlcipher_sqlite3_mutex_leave(pShmNode->pShmMutex); - return rc; +#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[45].pCurrent) - /* Jump here on any error */ -shm_open_err: - unixShmPurge(pDbFd); /* This call frees pShmNode if required */ - sqlcipher_sqlite3_free(p); - unixLeaveMutex(); - return rc; -} +#if !SQLITE_OS_WINRT + { "LocalFree", (SYSCALL)LocalFree, 0 }, +#else + { "LocalFree", (SYSCALL)0, 0 }, +#endif -/* -** This function is called to obtain a pointer to region iRegion of the -** shared-memory associated with the database file fd. Shared-memory regions -** are numbered starting from zero. Each shared-memory region is szRegion -** bytes in size. -** -** If an error occurs, an error code is returned and *pp is set to NULL. -** -** Otherwise, if the bExtend parameter is 0 and the requested shared-memory -** region has not been allocated (by any client, including one running in a -** separate process), then *pp is set to NULL and SQLITE_OK returned. If -** bExtend is non-zero and the requested shared-memory region has not yet -** been allocated, it is allocated by this function. -** -** If the shared-memory region has already been allocated or is allocated by -** this call as described above, then it is mapped into this processes -** address space (if it is not already), *pp is set to point to the mapped -** memory and SQLITE_OK returned. -*/ -static int unixShmMap( - sqlcipher_sqlite3_file *fd, /* Handle open on database file */ - int iRegion, /* Region to retrieve */ - int szRegion, /* Size of regions */ - int bExtend, /* True to extend file if necessary */ - void volatile **pp /* OUT: Mapped memory */ -){ - unixFile *pDbFd = (unixFile*)fd; - unixShm *p; - unixShmNode *pShmNode; - int rc = SQLITE_OK; - int nShmPerMap = unixShmRegionPerMap(); - int nReqRegion; +#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent) - /* If the shared-memory file has not yet been opened, open it now. */ - if( pDbFd->pShm==0 ){ - rc = unixOpenSharedMemory(pDbFd); - if( rc!=SQLITE_OK ) return rc; - } +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT + { "LockFile", (SYSCALL)LockFile, 0 }, +#else + { "LockFile", (SYSCALL)0, 0 }, +#endif - p = pDbFd->pShm; - pShmNode = p->pShmNode; - sqlcipher_sqlite3_mutex_enter(pShmNode->pShmMutex); - if( pShmNode->isUnlocked ){ - rc = unixLockSharedMemory(pDbFd, pShmNode); - if( rc!=SQLITE_OK ) goto shmpage_out; - pShmNode->isUnlocked = 0; - } - assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); - assert( pShmNode->pInode==pDbFd->pInode ); - assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 ); - assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 ); +#ifndef osLockFile +#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ + DWORD))aSyscall[47].pCurrent) +#endif - /* Minimum number of regions required to be mapped. */ - nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap; +#if !SQLITE_OS_WINCE + { "LockFileEx", (SYSCALL)LockFileEx, 0 }, +#else + { "LockFileEx", (SYSCALL)0, 0 }, +#endif - if( pShmNode->nRegionszRegion = szRegion; +#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && \ + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)) + { "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 }, +#else + { "MapViewOfFile", (SYSCALL)0, 0 }, +#endif - if( pShmNode->hShm>=0 ){ - /* The requested region is not mapped into this processes address space. - ** Check to see if it has been allocated (i.e. if the wal-index file is - ** large enough to contain the requested region). - */ - if( osFstat(pShmNode->hShm, &sStat) ){ - rc = SQLITE_IOERR_SHMSIZE; - goto shmpage_out; - } +#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ + SIZE_T))aSyscall[49].pCurrent) - if( sStat.st_sizehShm, iPg*pgsz + pgsz-1,"",1,&x)!=1 ){ - const char *zFile = pShmNode->zFilename; - rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile); - goto shmpage_out; - } - } - } - } - } + { "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 }, - /* Map the requested memory region into this processes address space. */ - apNew = (char **)sqlcipher_sqlite3_realloc( - pShmNode->apRegion, nReqRegion*sizeof(char *) - ); - if( !apNew ){ - rc = SQLITE_IOERR_NOMEM_BKPT; - goto shmpage_out; - } - pShmNode->apRegion = apNew; - while( pShmNode->nRegionhShm>=0 ){ - pMem = osMmap(0, nMap, - pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE, - MAP_SHARED, pShmNode->hShm, szRegion*(i64)pShmNode->nRegion - ); - if( pMem==MAP_FAILED ){ - rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename); - goto shmpage_out; - } - }else{ - pMem = sqlcipher_sqlite3_malloc64(nMap); - if( pMem==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto shmpage_out; - } - memset(pMem, 0, nMap); - } +#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \ + LARGE_INTEGER*))aSyscall[51].pCurrent) - for(i=0; iapRegion[pShmNode->nRegion+i] = &((char*)pMem)[szRegion*i]; - } - pShmNode->nRegion += nShmPerMap; - } - } + { "ReadFile", (SYSCALL)ReadFile, 0 }, -shmpage_out: - if( pShmNode->nRegion>iRegion ){ - *pp = pShmNode->apRegion[iRegion]; - }else{ - *pp = 0; - } - if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; - sqlcipher_sqlite3_mutex_leave(pShmNode->pShmMutex); - return rc; -} +#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \ + LPOVERLAPPED))aSyscall[52].pCurrent) -/* -** Check that the pShmNode->aLock[] array comports with the locking bitmasks -** held by each client. Return true if it does, or false otherwise. This -** is to be used in an assert(). e.g. -** -** assert( assertLockingArrayOk(pShmNode) ); -*/ -#ifdef SQLITE_DEBUG -static int assertLockingArrayOk(unixShmNode *pShmNode){ - unixShm *pX; - int aLock[SQLITE_SHM_NLOCK]; - assert( sqlcipher_sqlite3_mutex_held(pShmNode->pShmMutex) ); + { "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 }, - memset(aLock, 0, sizeof(aLock)); - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ - int i; - for(i=0; iexclMask & (1<sharedMask & (1<=0 ); - aLock[i]++; - } - } - } +#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[53].pCurrent) - assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) ); - return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0); -} +#if !SQLITE_OS_WINRT + { "SetFilePointer", (SYSCALL)SetFilePointer, 0 }, +#else + { "SetFilePointer", (SYSCALL)0, 0 }, #endif -/* -** Change the lock state for a shared-memory segment. -** -** Note that the relationship between SHAREd and EXCLUSIVE locks is a little -** different here than in posix. In xShmLock(), one can go from unlocked -** to shared and back or from unlocked to exclusive and back. But one may -** not go from shared to exclusive or from exclusive to shared. -*/ -static int unixShmLock( - sqlcipher_sqlite3_file *fd, /* Database file holding the shared memory */ - int ofst, /* First lock to acquire or release */ - int n, /* Number of locks to acquire or release */ - int flags /* What to do with the lock */ -){ - unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */ - unixShm *p = pDbFd->pShm; /* The shared memory being locked */ - unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */ - int rc = SQLITE_OK; /* Result code */ - u16 mask; /* Mask of locks to take or release */ - int *aLock = pShmNode->aLock; - - assert( pShmNode==pDbFd->pInode->pShmNode ); - assert( pShmNode->pInode==pDbFd->pInode ); - assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); - assert( n>=1 ); - assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) - || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) - || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) - || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); - assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); - assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 ); - assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 ); +#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \ + DWORD))aSyscall[54].pCurrent) - /* Check that, if this to be a blocking lock, no locks that occur later - ** in the following list than the lock being obtained are already held: - ** - ** 1. Checkpointer lock (ofst==1). - ** 2. Write lock (ofst==0). - ** 3. Read locks (ofst>=3 && ofstiBusyTimeout==0 || ( - (ofst!=2) /* not RECOVER */ - && (ofst!=1 || (p->exclMask|p->sharedMask)==0) - && (ofst!=0 || (p->exclMask|p->sharedMask)<3) - && (ofst<3 || (p->exclMask|p->sharedMask)<(1<1 || mask==(1<pShmMutex); - assert( assertLockingArrayOk(pShmNode) ); - if( flags & SQLITE_SHM_UNLOCK ){ - if( (p->exclMask|p->sharedMask) & mask ){ - int ii; - int bUnlock = 1; +#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent) - for(ii=ofst; ii((p->sharedMask & (1<sharedMask & (1<1 ); - aLock[ofst]--; - } +#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \ + LPFILETIME))aSyscall[56].pCurrent) - /* Undo the local locks */ - if( rc==SQLITE_OK ){ - p->exclMask &= ~mask; - p->sharedMask &= ~mask; - } - } - }else if( flags & SQLITE_SHM_SHARED ){ - assert( n==1 ); - assert( (p->exclMask & (1<sharedMask & mask)==0 ){ - if( aLock[ofst]<0 ){ - rc = SQLITE_BUSY; - }else if( aLock[ofst]==0 ){ - rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); - } +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT + { "UnlockFile", (SYSCALL)UnlockFile, 0 }, +#else + { "UnlockFile", (SYSCALL)0, 0 }, +#endif - /* Get the local shared locks */ - if( rc==SQLITE_OK ){ - p->sharedMask |= mask; - aLock[ofst]++; - } - } - }else{ - /* Make sure no sibling connections hold locks that will block this - ** lock. If any do, return SQLITE_BUSY right away. */ - int ii; - for(ii=ofst; iisharedMask & mask)==0 ); - if( ALWAYS((p->exclMask & (1<sharedMask & mask)==0 ); - p->exclMask |= mask; - for(ii=ofst; iipShmMutex); - OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", - p->id, osGetpid(0), p->sharedMask, p->exclMask)); - return rc; -} +#if !SQLITE_OS_WINCE + { "UnlockFileEx", (SYSCALL)UnlockFileEx, 0 }, +#else + { "UnlockFileEx", (SYSCALL)0, 0 }, +#endif -/* -** Implement a memory barrier or memory fence on shared memory. -** -** All loads and stores begun before the barrier must complete before -** any load or store begun after the barrier. -*/ -static void unixShmBarrier( - sqlcipher_sqlite3_file *fd /* Database file holding the shared memory */ -){ - UNUSED_PARAMETER(fd); - sqlcipher_sqlite3MemoryBarrier(); /* compiler-defined memory barrier */ - assert( fd->pMethods->xLock==nolockLock - || unixFileMutexNotheld((unixFile*)fd) - ); - unixEnterMutex(); /* Also mutex, for redundancy */ - unixLeaveMutex(); -} +#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ + LPOVERLAPPED))aSyscall[58].pCurrent) -/* -** Close a connection to shared-memory. Delete the underlying -** storage if deleteFlag is true. -** -** If there is no shared memory associated with the connection then this -** routine is a harmless no-op. -*/ -static int unixShmUnmap( - sqlcipher_sqlite3_file *fd, /* The underlying database file */ - int deleteFlag /* Delete shared-memory if true */ -){ - unixShm *p; /* The connection to be closed */ - unixShmNode *pShmNode; /* The underlying shared-memory file */ - unixShm **pp; /* For looping over sibling connections */ - unixFile *pDbFd; /* The underlying database file */ +#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 + { "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 }, +#else + { "UnmapViewOfFile", (SYSCALL)0, 0 }, +#endif - pDbFd = (unixFile*)fd; - p = pDbFd->pShm; - if( p==0 ) return SQLITE_OK; - pShmNode = p->pShmNode; +#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[59].pCurrent) - assert( pShmNode==pDbFd->pInode->pShmNode ); - assert( pShmNode->pInode==pDbFd->pInode ); + { "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 }, - /* Remove connection p from the set of connections associated - ** with pShmNode */ - sqlcipher_sqlite3_mutex_enter(pShmNode->pShmMutex); - for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} - *pp = p->pNext; +#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \ + LPCSTR,LPBOOL))aSyscall[60].pCurrent) - /* Free the connection p */ - sqlcipher_sqlite3_free(p); - pDbFd->pShm = 0; - sqlcipher_sqlite3_mutex_leave(pShmNode->pShmMutex); + { "WriteFile", (SYSCALL)WriteFile, 0 }, - /* If pShmNode->nRef has reached 0, then close the underlying - ** shared-memory file, too */ - assert( unixFileMutexNotheld(pDbFd) ); - unixEnterMutex(); - assert( pShmNode->nRef>0 ); - pShmNode->nRef--; - if( pShmNode->nRef==0 ){ - if( deleteFlag && pShmNode->hShm>=0 ){ - osUnlink(pShmNode->zFilename); - } - unixShmPurge(pDbFd); - } - unixLeaveMutex(); +#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \ + LPOVERLAPPED))aSyscall[61].pCurrent) - return SQLITE_OK; -} +#if SQLITE_OS_WINRT + { "CreateEventExW", (SYSCALL)CreateEventExW, 0 }, +#else + { "CreateEventExW", (SYSCALL)0, 0 }, +#endif +#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ + DWORD,DWORD))aSyscall[62].pCurrent) +#if !SQLITE_OS_WINRT + { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, #else -# define unixShmMap 0 -# define unixShmLock 0 -# define unixShmBarrier 0 -# define unixShmUnmap 0 -#endif /* #ifndef SQLITE_OMIT_WAL */ + { "WaitForSingleObject", (SYSCALL)0, 0 }, +#endif -#if SQLITE_MAX_MMAP_SIZE>0 -/* -** If it is currently memory mapped, unmap file pFd. -*/ -static void unixUnmapfile(unixFile *pFd){ - assert( pFd->nFetchOut==0 ); - if( pFd->pMapRegion ){ - osMunmap(pFd->pMapRegion, pFd->mmapSizeActual); - pFd->pMapRegion = 0; - pFd->mmapSize = 0; - pFd->mmapSizeActual = 0; - } -} +#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ + DWORD))aSyscall[63].pCurrent) -/* -** Attempt to set the size of the memory mapping maintained by file -** descriptor pFd to nNew bytes. Any existing mapping is discarded. -** -** If successful, this function sets the following variables: -** -** unixFile.pMapRegion -** unixFile.mmapSize -** unixFile.mmapSizeActual -** -** If unsuccessful, an error message is logged via sqlcipher_sqlite3_log() and -** the three variables above are zeroed. In this case SQLite should -** continue accessing the database using the xRead() and xWrite() -** methods. -*/ -static void unixRemapfile( - unixFile *pFd, /* File descriptor object */ - i64 nNew /* Required mapping size */ -){ - const char *zErr = "mmap"; - int h = pFd->h; /* File descriptor open on db file */ - u8 *pOrig = (u8 *)pFd->pMapRegion; /* Pointer to current file mapping */ - i64 nOrig = pFd->mmapSizeActual; /* Size of pOrig region in bytes */ - u8 *pNew = 0; /* Location of new mapping */ - int flags = PROT_READ; /* Flags to pass to mmap() */ +#if !SQLITE_OS_WINCE + { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 }, +#else + { "WaitForSingleObjectEx", (SYSCALL)0, 0 }, +#endif - assert( pFd->nFetchOut==0 ); - assert( nNew>pFd->mmapSize ); - assert( nNew<=pFd->mmapSizeMax ); - assert( nNew>0 ); - assert( pFd->mmapSizeActual>=pFd->mmapSize ); - assert( MAP_FAILED!=0 ); +#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ + BOOL))aSyscall[64].pCurrent) -#ifdef SQLITE_MMAP_READWRITE - if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; +#if SQLITE_OS_WINRT + { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 }, +#else + { "SetFilePointerEx", (SYSCALL)0, 0 }, #endif - if( pOrig ){ -#if HAVE_MREMAP - i64 nReuse = pFd->mmapSize; +#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \ + PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent) + +#if SQLITE_OS_WINRT + { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 }, #else - const int szSyspage = osGetpagesize(); - i64 nReuse = (pFd->mmapSize & ~(szSyspage-1)); + { "GetFileInformationByHandleEx", (SYSCALL)0, 0 }, #endif - u8 *pReq = &pOrig[nReuse]; - /* Unmap any pages of the existing mapping that cannot be reused. */ - if( nReuse!=nOrig ){ - osMunmap(pReq, nOrig-nReuse); - } +#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \ + FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent) -#if HAVE_MREMAP - pNew = osMremap(pOrig, nReuse, nNew, MREMAP_MAYMOVE); - zErr = "mremap"; +#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) + { "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 }, #else - pNew = osMmap(pReq, nNew-nReuse, flags, MAP_SHARED, h, nReuse); - if( pNew!=MAP_FAILED ){ - if( pNew!=pReq ){ - osMunmap(pNew, nNew - nReuse); - pNew = 0; - }else{ - pNew = pOrig; - } - } + { "MapViewOfFileFromApp", (SYSCALL)0, 0 }, #endif - /* The attempt to extend the existing mapping failed. Free it. */ - if( pNew==MAP_FAILED || pNew==0 ){ - osMunmap(pOrig, nReuse); - } - } - - /* If pNew is still NULL, try to create an entirely new mapping. */ - if( pNew==0 ){ - pNew = osMmap(0, nNew, flags, MAP_SHARED, h, 0); - } +#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \ + SIZE_T))aSyscall[67].pCurrent) - if( pNew==MAP_FAILED ){ - pNew = 0; - nNew = 0; - unixLogError(SQLITE_OK, zErr, pFd->zPath); +#if SQLITE_OS_WINRT + { "CreateFile2", (SYSCALL)CreateFile2, 0 }, +#else + { "CreateFile2", (SYSCALL)0, 0 }, +#endif - /* If the mmap() above failed, assume that all subsequent mmap() calls - ** will probably fail too. Fall back to using xRead/xWrite exclusively - ** in this case. */ - pFd->mmapSizeMax = 0; - } - pFd->pMapRegion = (void *)pNew; - pFd->mmapSize = pFd->mmapSizeActual = nNew; -} +#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \ + LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent) -/* -** Memory map or remap the file opened by file-descriptor pFd (if the file -** is already mapped, the existing mapping is replaced by the new). Or, if -** there already exists a mapping for this file, and there are still -** outstanding xFetch() references to it, this function is a no-op. -** -** If parameter nByte is non-negative, then it is the requested size of -** the mapping to create. Otherwise, if nByte is less than zero, then the -** requested size is the size of the file on disk. The actual size of the -** created mapping is either the requested size or the value configured -** using SQLITE_FCNTL_MMAP_LIMIT, whichever is smaller. -** -** SQLITE_OK is returned if no error occurs (even if the mapping is not -** recreated as a result of outstanding references) or an SQLite error -** code otherwise. -*/ -static int unixMapfile(unixFile *pFd, i64 nMap){ - assert( nMap>=0 || pFd->nFetchOut==0 ); - assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) ); - if( pFd->nFetchOut>0 ) return SQLITE_OK; +#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION) + { "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 }, +#else + { "LoadPackagedLibrary", (SYSCALL)0, 0 }, +#endif - if( nMap<0 ){ - struct stat statbuf; /* Low-level file information */ - if( osFstat(pFd->h, &statbuf) ){ - return SQLITE_IOERR_FSTAT; - } - nMap = statbuf.st_size; - } - if( nMap>pFd->mmapSizeMax ){ - nMap = pFd->mmapSizeMax; - } +#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \ + DWORD))aSyscall[69].pCurrent) - assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) ); - if( nMap!=pFd->mmapSize ){ - unixRemapfile(pFd, nMap); - } +#if SQLITE_OS_WINRT + { "GetTickCount64", (SYSCALL)GetTickCount64, 0 }, +#else + { "GetTickCount64", (SYSCALL)0, 0 }, +#endif - return SQLITE_OK; -} -#endif /* SQLITE_MAX_MMAP_SIZE>0 */ +#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent) -/* -** If possible, return a pointer to a mapping of file fd starting at offset -** iOff. The mapping must be valid for at least nAmt bytes. -** -** If such a pointer can be obtained, store it in *pp and return SQLITE_OK. -** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK. -** Finally, if an error does occur, return an SQLite error code. The final -** value of *pp is undefined in this case. -** -** If this function does return a pointer, the caller must eventually -** release the reference by calling unixUnfetch(). -*/ -static int unixFetch(sqlcipher_sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ -#if SQLITE_MAX_MMAP_SIZE>0 - unixFile *pFd = (unixFile *)fd; /* The underlying database file */ +#if SQLITE_OS_WINRT + { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, +#else + { "GetNativeSystemInfo", (SYSCALL)0, 0 }, #endif - *pp = 0; -#if SQLITE_MAX_MMAP_SIZE>0 - if( pFd->mmapSizeMax>0 ){ - if( pFd->pMapRegion==0 ){ - int rc = unixMapfile(pFd, -1); - if( rc!=SQLITE_OK ) return rc; - } - if( pFd->mmapSize >= iOff+nAmt ){ - *pp = &((u8 *)pFd->pMapRegion)[iOff]; - pFd->nFetchOut++; - } - } +#define osGetNativeSystemInfo ((VOID(WINAPI*)( \ + LPSYSTEM_INFO))aSyscall[71].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 }, +#else + { "OutputDebugStringA", (SYSCALL)0, 0 }, #endif - return SQLITE_OK; -} -/* -** If the third argument is non-NULL, then this function releases a -** reference obtained by an earlier call to unixFetch(). The second -** argument passed to this function must be the same as the corresponding -** argument that was passed to the unixFetch() invocation. -** -** Or, if the third argument is NULL, then this function is being called -** to inform the VFS layer that, according to POSIX, any existing mapping -** may now be invalid and should be unmapped. -*/ -static int unixUnfetch(sqlcipher_sqlite3_file *fd, i64 iOff, void *p){ -#if SQLITE_MAX_MMAP_SIZE>0 - unixFile *pFd = (unixFile *)fd; /* The underlying database file */ - UNUSED_PARAMETER(iOff); +#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent) - /* If p==0 (unmap the entire file) then there must be no outstanding - ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), - ** then there must be at least one outstanding. */ - assert( (p==0)==(pFd->nFetchOut==0) ); +#if defined(SQLITE_WIN32_HAS_WIDE) + { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 }, +#else + { "OutputDebugStringW", (SYSCALL)0, 0 }, +#endif - /* If p!=0, it must match the iOff value. */ - assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] ); +#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent) - if( p ){ - pFd->nFetchOut--; - }else{ - unixUnmapfile(pFd); - } + { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 }, - assert( pFd->nFetchOut>=0 ); +#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent) + +#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) + { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 }, #else - UNUSED_PARAMETER(fd); - UNUSED_PARAMETER(p); - UNUSED_PARAMETER(iOff); + { "CreateFileMappingFromApp", (SYSCALL)0, 0 }, #endif - return SQLITE_OK; -} -/* -** Here ends the implementation of all sqlcipher_sqlite3_file methods. -** -********************** End sqlcipher_sqlite3_file Methods ******************************* -******************************************************************************/ +#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \ + LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[75].pCurrent) /* -** This division contains definitions of sqlcipher_sqlite3_io_methods objects that -** implement various file locking strategies. It also contains definitions -** of "finder" functions. A finder-function is used to locate the appropriate -** sqlcipher_sqlite3_io_methods object for a particular database file. The pAppData -** field of the sqlcipher_sqlite3_vfs VFS objects are initialized to be pointers to -** the correct finder-function for that VFS. -** -** Most finder functions return a pointer to a fixed sqlcipher_sqlite3_io_methods -** object. The only interesting finder-function is autolockIoFinder, which -** looks at the filesystem type and tries to guess the best locking -** strategy from that. -** -** For finder-function F, two objects are created: -** -** (1) The real finder-function named "FImpt()". -** -** (2) A constant pointer to this function named just "F". -** -** -** A pointer to the F pointer is used as the pAppData value for VFS -** objects. We have to do this instead of letting pAppData point -** directly at the finder-function since C90 rules prevent a void* -** from be cast into a function pointer. -** -** -** Each instance of this macro generates two objects: -** -** * A constant sqlcipher_sqlite3_io_methods object call METHOD that has locking -** methods CLOSE, LOCK, UNLOCK, CKRESLOCK. -** -** * An I/O method finder function called FINDER that returns a pointer -** to the METHOD object in the previous bullet. +** NOTE: On some sub-platforms, the InterlockedCompareExchange "function" +** is really just a macro that uses a compiler intrinsic (e.g. x64). +** So do not try to make this is into a redefinable interface. */ -#define IOMETHODS(FINDER,METHOD,VERSION,CLOSE,LOCK,UNLOCK,CKLOCK,SHMMAP) \ -static const sqlcipher_sqlite3_io_methods METHOD = { \ - VERSION, /* iVersion */ \ - CLOSE, /* xClose */ \ - unixRead, /* xRead */ \ - unixWrite, /* xWrite */ \ - unixTruncate, /* xTruncate */ \ - unixSync, /* xSync */ \ - unixFileSize, /* xFileSize */ \ - LOCK, /* xLock */ \ - UNLOCK, /* xUnlock */ \ - CKLOCK, /* xCheckReservedLock */ \ - unixFileControl, /* xFileControl */ \ - unixSectorSize, /* xSectorSize */ \ - unixDeviceCharacteristics, /* xDeviceCapabilities */ \ - SHMMAP, /* xShmMap */ \ - unixShmLock, /* xShmLock */ \ - unixShmBarrier, /* xShmBarrier */ \ - unixShmUnmap, /* xShmUnmap */ \ - unixFetch, /* xFetch */ \ - unixUnfetch, /* xUnfetch */ \ -}; \ -static const sqlcipher_sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ - UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ - return &METHOD; \ -} \ -static const sqlcipher_sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ - = FINDER##Impl; +#if defined(InterlockedCompareExchange) + { "InterlockedCompareExchange", (SYSCALL)0, 0 }, -/* -** Here are all of the sqlcipher_sqlite3_io_methods objects for each of the -** locking strategies. Functions that return pointers to these methods -** are also created. -*/ -IOMETHODS( - posixIoFinder, /* Finder function name */ - posixIoMethods, /* sqlcipher_sqlite3_io_methods object name */ - 3, /* shared memory and mmap are enabled */ - unixClose, /* xClose method */ - unixLock, /* xLock method */ - unixUnlock, /* xUnlock method */ - unixCheckReservedLock, /* xCheckReservedLock method */ - unixShmMap /* xShmMap method */ -) -IOMETHODS( - nolockIoFinder, /* Finder function name */ - nolockIoMethods, /* sqlcipher_sqlite3_io_methods object name */ - 3, /* shared memory and mmap are enabled */ - nolockClose, /* xClose method */ - nolockLock, /* xLock method */ - nolockUnlock, /* xUnlock method */ - nolockCheckReservedLock, /* xCheckReservedLock method */ - 0 /* xShmMap method */ -) -IOMETHODS( - dotlockIoFinder, /* Finder function name */ - dotlockIoMethods, /* sqlcipher_sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - dotlockClose, /* xClose method */ - dotlockLock, /* xLock method */ - dotlockUnlock, /* xUnlock method */ - dotlockCheckReservedLock, /* xCheckReservedLock method */ - 0 /* xShmMap method */ -) +#define osInterlockedCompareExchange InterlockedCompareExchange +#else + { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 }, -#if SQLITE_ENABLE_LOCKING_STYLE -IOMETHODS( - flockIoFinder, /* Finder function name */ - flockIoMethods, /* sqlcipher_sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - flockClose, /* xClose method */ - flockLock, /* xLock method */ - flockUnlock, /* xUnlock method */ - flockCheckReservedLock, /* xCheckReservedLock method */ - 0 /* xShmMap method */ -) +#define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \ + SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent) +#endif /* defined(InterlockedCompareExchange) */ + +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID + { "UuidCreate", (SYSCALL)UuidCreate, 0 }, +#else + { "UuidCreate", (SYSCALL)0, 0 }, #endif -#if OS_VXWORKS -IOMETHODS( - semIoFinder, /* Finder function name */ - semIoMethods, /* sqlcipher_sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - semXClose, /* xClose method */ - semXLock, /* xLock method */ - semXUnlock, /* xUnlock method */ - semXCheckReservedLock, /* xCheckReservedLock method */ - 0 /* xShmMap method */ -) +#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[77].pCurrent) + +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID + { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 }, +#else + { "UuidCreateSequential", (SYSCALL)0, 0 }, #endif -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -IOMETHODS( - afpIoFinder, /* Finder function name */ - afpIoMethods, /* sqlcipher_sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - afpClose, /* xClose method */ - afpLock, /* xLock method */ - afpUnlock, /* xUnlock method */ - afpCheckReservedLock, /* xCheckReservedLock method */ - 0 /* xShmMap method */ -) +#define osUuidCreateSequential \ + ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[78].pCurrent) + +#if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0 + { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 }, +#else + { "FlushViewOfFile", (SYSCALL)0, 0 }, #endif +#define osFlushViewOfFile \ + ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) + +}; /* End of the overrideable system calls */ + /* -** The proxy locking method is a "super-method" in the sense that it -** opens secondary file descriptors for the conch and lock files and -** it uses proxy, dot-file, AFP, and flock() locking methods on those -** secondary files. For this reason, the division that implements -** proxy locking is located much further down in the file. But we need -** to go ahead and define the sqlcipher_sqlite3_io_methods and finder function -** for proxy locking here. So we forward declare the I/O methods. +** This is the xSetSystemCall() method of sqlcipher_sqlite3_vfs for all of the +** "win32" VFSes. Return SQLITE_OK opon successfully updating the +** system call pointer, or SQLITE_NOTFOUND if there is no configurable +** system call named zName. */ -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -static int proxyClose(sqlcipher_sqlite3_file*); -static int proxyLock(sqlcipher_sqlite3_file*, int); -static int proxyUnlock(sqlcipher_sqlite3_file*, int); -static int proxyCheckReservedLock(sqlcipher_sqlite3_file*, int*); -IOMETHODS( - proxyIoFinder, /* Finder function name */ - proxyIoMethods, /* sqlcipher_sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - proxyClose, /* xClose method */ - proxyLock, /* xLock method */ - proxyUnlock, /* xUnlock method */ - proxyCheckReservedLock, /* xCheckReservedLock method */ - 0 /* xShmMap method */ -) -#endif +static int winSetSystemCall( + sqlcipher_sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ + const char *zName, /* Name of system call to override */ + sqlcipher_sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ +){ + unsigned int i; + int rc = SQLITE_NOTFOUND; -/* nfs lockd on OSX 10.3+ doesn't clear write locks when a read lock is set */ -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -IOMETHODS( - nfsIoFinder, /* Finder function name */ - nfsIoMethods, /* sqlcipher_sqlite3_io_methods object name */ - 1, /* shared memory is disabled */ - unixClose, /* xClose method */ - unixLock, /* xLock method */ - nfsUnlock, /* xUnlock method */ - unixCheckReservedLock, /* xCheckReservedLock method */ - 0 /* xShmMap method */ -) -#endif + UNUSED_PARAMETER(pNotUsed); + if( zName==0 ){ + /* If no zName is given, restore all system calls to their default + ** settings and return NULL + */ + rc = SQLITE_OK; + for(i=0; ih, F_GETLK, &lockInfo)!=-1 ) { - if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){ - return &nfsIoMethods; - } else { - return &posixIoMethods; +#ifdef SQLITE_WIN32_MALLOC +/* +** If a Win32 native heap has been configured, this function will attempt to +** compact it. Upon success, SQLITE_OK will be returned. Upon failure, one +** of SQLITE_NOMEM, SQLITE_ERROR, or SQLITE_NOTFOUND will be returned. The +** "pnLargest" argument, if non-zero, will be used to return the size of the +** largest committed free block in the heap, in bytes. +*/ +SQLITE_API int sqlcipher_sqlite3_win32_compact_heap(LPUINT pnLargest){ + int rc = SQLITE_OK; + UINT nLargest = 0; + HANDLE hHeap; + + winMemAssertMagic(); + hHeap = winMemGetHeap(); + assert( hHeap!=0 ); + assert( hHeap!=INVALID_HANDLE_VALUE ); +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); +#endif +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT + if( (nLargest=osHeapCompact(hHeap, SQLITE_WIN32_HEAP_FLAGS))==0 ){ + DWORD lastErrno = osGetLastError(); + if( lastErrno==NO_ERROR ){ + sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to HeapCompact (no space), heap=%p", + (void*)hHeap); + rc = SQLITE_NOMEM_BKPT; + }else{ + sqlcipher_sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%lu), heap=%p", + osGetLastError(), (void*)hHeap); + rc = SQLITE_ERROR; } + } +#else + sqlcipher_sqlite3_log(SQLITE_NOTFOUND, "failed to HeapCompact, heap=%p", + (void*)hHeap); + rc = SQLITE_NOTFOUND; +#endif + if( pnLargest ) *pnLargest = nLargest; + return rc; +} + +/* +** If a Win32 native heap has been configured, this function will attempt to +** destroy and recreate it. If the Win32 native heap is not isolated and/or +** the sqlcipher_sqlite3_memory_used() function does not return zero, SQLITE_BUSY will +** be returned and no changes will be made to the Win32 native heap. +*/ +SQLITE_API int sqlcipher_sqlite3_win32_reset_heap(){ + int rc; + MUTEX_LOGIC( sqlcipher_sqlite3_mutex *pMainMtx; ) /* The main static mutex */ + MUTEX_LOGIC( sqlcipher_sqlite3_mutex *pMem; ) /* The memsys static mutex */ + MUTEX_LOGIC( pMainMtx = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) + MUTEX_LOGIC( pMem = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); ) + sqlcipher_sqlite3_mutex_enter(pMainMtx); + sqlcipher_sqlite3_mutex_enter(pMem); + winMemAssertMagic(); + if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlcipher_sqlite3_memory_used()==0 ){ + /* + ** At this point, there should be no outstanding memory allocations on + ** the heap. Also, since both the main and memsys locks are currently + ** being held by us, no other function (i.e. from another thread) should + ** be able to even access the heap. Attempt to destroy and recreate our + ** isolated Win32 native heap now. + */ + assert( winMemGetHeap()!=NULL ); + assert( winMemGetOwned() ); + assert( sqlcipher_sqlite3_memory_used()==0 ); + winMemShutdown(winMemGetDataPtr()); + assert( winMemGetHeap()==NULL ); + assert( !winMemGetOwned() ); + assert( sqlcipher_sqlite3_memory_used()==0 ); + rc = winMemInit(winMemGetDataPtr()); + assert( rc!=SQLITE_OK || winMemGetHeap()!=NULL ); + assert( rc!=SQLITE_OK || winMemGetOwned() ); + assert( rc!=SQLITE_OK || sqlcipher_sqlite3_memory_used()==0 ); }else{ - return &dotlockIoMethods; + /* + ** The Win32 native heap cannot be modified because it may be in use. + */ + rc = SQLITE_BUSY; } + sqlcipher_sqlite3_mutex_leave(pMem); + sqlcipher_sqlite3_mutex_leave(pMainMtx); + return rc; } -static const sqlcipher_sqlite3_io_methods - *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; - -#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ +#endif /* SQLITE_WIN32_MALLOC */ -#if OS_VXWORKS /* -** This "finder" function for VxWorks checks to see if posix advisory -** locking works. If it does, then that is what is used. If it does not -** work, then fallback to named semaphore locking. +** This function outputs the specified (ANSI) string to the Win32 debugger +** (if available). */ -static const sqlcipher_sqlite3_io_methods *vxworksIoFinderImpl( - const char *filePath, /* name of the database file */ - unixFile *pNew /* the open file object */ -){ - struct flock lockInfo; - if( !filePath ){ - /* If filePath==NULL that means we are dealing with a transient file - ** that does not need to be locked. */ - return &nolockIoMethods; +SQLITE_API void sqlcipher_sqlite3_win32_write_debug(const char *zBuf, int nBuf){ + char zDbgBuf[SQLITE_WIN32_DBG_BUF_SIZE]; + int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */ + if( nMin<-1 ) nMin = -1; /* all negative values become -1. */ + assert( nMin==-1 || nMin==0 || nMinh, F_GETLK, &lockInfo)!=-1 ) { - return &posixIoMethods; +#endif +#if defined(SQLITE_WIN32_HAS_ANSI) + if( nMin>0 ){ + memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); + memcpy(zDbgBuf, zBuf, nMin); + osOutputDebugStringA(zDbgBuf); }else{ - return &semIoMethods; + osOutputDebugStringA(zBuf); + } +#elif defined(SQLITE_WIN32_HAS_WIDE) + memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); + if ( osMultiByteToWideChar( + osAreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, zBuf, + nMin, (LPWSTR)zDbgBuf, SQLITE_WIN32_DBG_BUF_SIZE/sizeof(WCHAR))<=0 ){ + return; + } + osOutputDebugStringW((LPCWSTR)zDbgBuf); +#else + if( nMin>0 ){ + memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); + memcpy(zDbgBuf, zBuf, nMin); + fprintf(stderr, "%s", zDbgBuf); + }else{ + fprintf(stderr, "%s", zBuf); } +#endif } -static const sqlcipher_sqlite3_io_methods - *(*const vxworksIoFinder)(const char*,unixFile*) = vxworksIoFinderImpl; - -#endif /* OS_VXWORKS */ /* -** An abstract type for a pointer to an IO method finder function: +** The following routine suspends the current thread for at least ms +** milliseconds. This is equivalent to the Win32 Sleep() interface. */ -typedef const sqlcipher_sqlite3_io_methods *(*finder_type)(const char*,unixFile*); +#if SQLITE_OS_WINRT +static HANDLE sleepObj = NULL; +#endif +SQLITE_API void sqlcipher_sqlite3_win32_sleep(DWORD milliseconds){ +#if SQLITE_OS_WINRT + if ( sleepObj==NULL ){ + sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET, + SYNCHRONIZE); + } + assert( sleepObj!=NULL ); + osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE); +#else + osSleep(milliseconds); +#endif +} -/**************************************************************************** -**************************** sqlcipher_sqlite3_vfs methods **************************** -** -** This division contains the implementation of methods on the -** sqlcipher_sqlite3_vfs object. -*/ +#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ + SQLITE_THREADSAFE>0 +SQLITE_PRIVATE DWORD sqlcipher_sqlite3Win32Wait(HANDLE hObject){ + DWORD rc; + while( (rc = osWaitForSingleObjectEx(hObject, INFINITE, + TRUE))==WAIT_IO_COMPLETION ){} + return rc; +} +#endif /* -** Initialize the contents of the unixFile structure pointed to by pId. +** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, +** or WinCE. Return false (zero) for Win95, Win98, or WinME. +** +** Here is an interesting observation: Win95, Win98, and WinME lack +** the LockFileEx() API. But we can still statically link against that +** API as long as we don't call it when running Win95/98/ME. A call to +** this routine is used to determine if the host is Win95/98/ME or +** WinNT/2K/XP so that we will know whether or not we can safely call +** the LockFileEx() API. */ -static int fillInUnixFile( - sqlcipher_sqlite3_vfs *pVfs, /* Pointer to vfs object */ - int h, /* Open file descriptor of file being opened */ - sqlcipher_sqlite3_file *pId, /* Write to the unixFile structure here */ - const char *zFilename, /* Name of the file being opened */ - int ctrlFlags /* Zero or more UNIXFILE_* values */ -){ - const sqlcipher_sqlite3_io_methods *pLockingStyle; - unixFile *pNew = (unixFile *)pId; - int rc = SQLITE_OK; - - assert( pNew->pInode==NULL ); - /* No locking occurs in temporary files */ - assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 ); - - OSTRACE(("OPEN %-3d %s\n", h, zFilename)); - pNew->h = h; - pNew->pVfs = pVfs; - pNew->zPath = zFilename; - pNew->ctrlFlags = (u8)ctrlFlags; -#if SQLITE_MAX_MMAP_SIZE>0 - pNew->mmapSizeMax = sqlcipher_sqlite3GlobalConfig.szMmap; +#if !SQLITE_WIN32_GETVERSIONEX +# define osIsNT() (1) +#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI) +# define osIsNT() (1) +#elif !defined(SQLITE_WIN32_HAS_WIDE) +# define osIsNT() (0) +#else +# define osIsNT() ((sqlcipher_sqlite3_os_type==2) || sqlcipher_sqlite3_win32_is_nt()) #endif - if( sqlcipher_sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), - "psow", SQLITE_POWERSAFE_OVERWRITE) ){ - pNew->ctrlFlags |= UNIXFILE_PSOW; - } - if( strcmp(pVfs->zName,"unix-excl")==0 ){ - pNew->ctrlFlags |= UNIXFILE_EXCL; - } -#if OS_VXWORKS - pNew->pId = vxworksFindFileId(zFilename); - if( pNew->pId==0 ){ - ctrlFlags |= UNIXFILE_NOLOCK; - rc = SQLITE_NOMEM_BKPT; +/* +** This function determines if the machine is running a version of Windows +** based on the NT kernel. +*/ +SQLITE_API int sqlcipher_sqlite3_win32_is_nt(void){ +#if SQLITE_OS_WINRT + /* + ** NOTE: The WinRT sub-platform is always assumed to be based on the NT + ** kernel. + */ + return 1; +#elif SQLITE_WIN32_GETVERSIONEX + if( osInterlockedCompareExchange(&sqlcipher_sqlite3_os_type, 0, 0)==0 ){ +#if defined(SQLITE_WIN32_HAS_ANSI) + OSVERSIONINFOA sInfo; + sInfo.dwOSVersionInfoSize = sizeof(sInfo); + osGetVersionExA(&sInfo); + osInterlockedCompareExchange(&sqlcipher_sqlite3_os_type, + (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0); +#elif defined(SQLITE_WIN32_HAS_WIDE) + OSVERSIONINFOW sInfo; + sInfo.dwOSVersionInfoSize = sizeof(sInfo); + osGetVersionExW(&sInfo); + osInterlockedCompareExchange(&sqlcipher_sqlite3_os_type, + (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0); +#endif } + return osInterlockedCompareExchange(&sqlcipher_sqlite3_os_type, 2, 2)==2; +#elif SQLITE_TEST + return osInterlockedCompareExchange(&sqlcipher_sqlite3_os_type, 2, 2)==2; +#else + /* + ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are + ** deprecated are always assumed to be based on the NT kernel. + */ + return 1; #endif +} - if( ctrlFlags & UNIXFILE_NOLOCK ){ - pLockingStyle = &nolockIoMethods; - }else{ - pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew); -#if SQLITE_ENABLE_LOCKING_STYLE - /* Cache zFilename in the locking context (AFP and dotlock override) for - ** proxyLock activation is possible (remote proxy is based on db name) - ** zFilename remains valid until file is closed, to support */ - pNew->lockingContext = (void*)zFilename; -#endif - } +#ifdef SQLITE_WIN32_MALLOC +/* +** Allocate nBytes of memory. +*/ +static void *winMemMalloc(int nBytes){ + HANDLE hHeap; + void *p; - if( pLockingStyle == &posixIoMethods -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE - || pLockingStyle == &nfsIoMethods + winMemAssertMagic(); + hHeap = winMemGetHeap(); + assert( hHeap!=0 ); + assert( hHeap!=INVALID_HANDLE_VALUE ); +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); #endif - ){ - unixEnterMutex(); - rc = findInodeInfo(pNew, &pNew->pInode); - if( rc!=SQLITE_OK ){ - /* If an error occurred in findInodeInfo(), close the file descriptor - ** immediately, before releasing the mutex. findInodeInfo() may fail - ** in two scenarios: - ** - ** (a) A call to fstat() failed. - ** (b) A malloc failed. - ** - ** Scenario (b) may only occur if the process is holding no other - ** file descriptors open on the same file. If there were other file - ** descriptors on this file, then no malloc would be required by - ** findInodeInfo(). If this is the case, it is quite safe to close - ** handle h - as it is guaranteed that no posix locks will be released - ** by doing so. - ** - ** If scenario (a) caused the error then things are not so safe. The - ** implicit assumption here is that if fstat() fails, things are in - ** such bad shape that dropping a lock or two doesn't matter much. - */ - robust_close(pNew, h, __LINE__); - h = -1; - } - unixLeaveMutex(); + assert( nBytes>=0 ); + p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); + if( !p ){ + sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%lu), heap=%p", + nBytes, osGetLastError(), (void*)hHeap); } + return p; +} -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) - else if( pLockingStyle == &afpIoMethods ){ - /* AFP locking uses the file path so it needs to be included in - ** the afpLockingContext. - */ - afpLockingContext *pCtx; - pNew->lockingContext = pCtx = sqlcipher_sqlite3_malloc64( sizeof(*pCtx) ); - if( pCtx==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - /* NB: zFilename exists and remains valid until the file is closed - ** according to requirement F11141. So we do not need to make a - ** copy of the filename. */ - pCtx->dbPath = zFilename; - pCtx->reserved = 0; - srandomdev(); - unixEnterMutex(); - rc = findInodeInfo(pNew, &pNew->pInode); - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3_free(pNew->lockingContext); - robust_close(pNew, h, __LINE__); - h = -1; - } - unixLeaveMutex(); - } - } -#endif +/* +** Free memory. +*/ +static void winMemFree(void *pPrior){ + HANDLE hHeap; - else if( pLockingStyle == &dotlockIoMethods ){ - /* Dotfile locking uses the file path so it needs to be included in - ** the dotlockLockingContext - */ - char *zLockFile; - int nFilename; - assert( zFilename!=0 ); - nFilename = (int)strlen(zFilename) + 6; - zLockFile = (char *)sqlcipher_sqlite3_malloc64(nFilename); - if( zLockFile==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - sqlcipher_sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename); - } - pNew->lockingContext = zLockFile; + winMemAssertMagic(); + hHeap = winMemGetHeap(); + assert( hHeap!=0 ); + assert( hHeap!=INVALID_HANDLE_VALUE ); +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); +#endif + if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */ + if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){ + sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%lu), heap=%p", + pPrior, osGetLastError(), (void*)hHeap); } +} -#if OS_VXWORKS - else if( pLockingStyle == &semIoMethods ){ - /* Named semaphore locking uses the file path so it needs to be - ** included in the semLockingContext - */ - unixEnterMutex(); - rc = findInodeInfo(pNew, &pNew->pInode); - if( (rc==SQLITE_OK) && (pNew->pInode->pSem==NULL) ){ - char *zSemName = pNew->pInode->aSemName; - int n; - sqlcipher_sqlite3_snprintf(MAX_PATHNAME, zSemName, "/%s.sem", - pNew->pId->zCanonicalName); - for( n=1; zSemName[n]; n++ ) - if( zSemName[n]=='/' ) zSemName[n] = '_'; - pNew->pInode->pSem = sem_open(zSemName, O_CREAT, 0666, 1); - if( pNew->pInode->pSem == SEM_FAILED ){ - rc = SQLITE_NOMEM_BKPT; - pNew->pInode->aSemName[0] = '\0'; - } - } - unixLeaveMutex(); - } -#endif +/* +** Change the size of an existing memory allocation +*/ +static void *winMemRealloc(void *pPrior, int nBytes){ + HANDLE hHeap; + void *p; - storeLastErrno(pNew, 0); -#if OS_VXWORKS - if( rc!=SQLITE_OK ){ - if( h>=0 ) robust_close(pNew, h, __LINE__); - h = -1; - osUnlink(zFilename); - pNew->ctrlFlags |= UNIXFILE_DELETE; - } + winMemAssertMagic(); + hHeap = winMemGetHeap(); + assert( hHeap!=0 ); + assert( hHeap!=INVALID_HANDLE_VALUE ); +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); #endif - if( rc!=SQLITE_OK ){ - if( h>=0 ) robust_close(pNew, h, __LINE__); + assert( nBytes>=0 ); + if( !pPrior ){ + p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); }else{ - pId->pMethods = pLockingStyle; - OpenCounter(+1); - verifyDbFile(pNew); + p = osHeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes); } - return rc; + if( !p ){ + sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%lu), heap=%p", + pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, osGetLastError(), + (void*)hHeap); + } + return p; } /* -** Return the name of a directory in which to put temporary files. -** If no suitable temporary file directory can be found, return NULL. +** Return the size of an outstanding allocation, in bytes. */ -static const char *unixTempFileDir(void){ - static const char *azDirs[] = { - 0, - 0, - "/var/tmp", - "/usr/tmp", - "/tmp", - "." - }; - unsigned int i = 0; - struct stat buf; - const char *zDir = sqlcipher_sqlite3_temp_directory; +static int winMemSize(void *p){ + HANDLE hHeap; + SIZE_T n; - if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); - if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); - while(1){ - if( zDir!=0 - && osStat(zDir, &buf)==0 - && S_ISDIR(buf.st_mode) - && osAccess(zDir, 03)==0 - ){ - return zDir; - } - if( i>=sizeof(azDirs)/sizeof(azDirs[0]) ) break; - zDir = azDirs[i++]; + winMemAssertMagic(); + hHeap = winMemGetHeap(); + assert( hHeap!=0 ); + assert( hHeap!=INVALID_HANDLE_VALUE ); +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, p) ); +#endif + if( !p ) return 0; + n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p); + if( n==(SIZE_T)-1 ){ + sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%lu), heap=%p", + p, osGetLastError(), (void*)hHeap); + return 0; } - return 0; + return (int)n; } /* -** Create a temporary file name in zBuf. zBuf must be allocated -** by the calling process and must be big enough to hold at least -** pVfs->mxPathname bytes. +** Round up a request size to the next valid allocation size. */ -static int unixGetTempname(int nBuf, char *zBuf){ - const char *zDir; - int iLimit = 0; - - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. - */ - zBuf[0] = 0; - SimulateIOError( return SQLITE_IOERR ); - - zDir = unixTempFileDir(); - if( zDir==0 ) return SQLITE_IOERR_GETTEMPPATH; - do{ - u64 r; - sqlcipher_sqlite3_randomness(sizeof(r), &r); - assert( nBuf>2 ); - zBuf[nBuf-2] = 0; - sqlcipher_sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c", - zDir, r, 0); - if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR; - }while( osAccess(zBuf,0)==0 ); - return SQLITE_OK; +static int winMemRoundup(int n){ + return n; } -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) /* -** Routine to transform a unixFile into a proxy-locking unixFile. -** Implementation in the proxy-lock division, but used by unixOpen() -** if SQLITE_PREFER_PROXY_LOCKING is defined. +** Initialize this module. */ -static int proxyTransformUnixFile(unixFile*, const char*); +static int winMemInit(void *pAppData){ + winMemData *pWinMemData = (winMemData *)pAppData; + + if( !pWinMemData ) return SQLITE_ERROR; + assert( pWinMemData->magic1==WINMEM_MAGIC1 ); + assert( pWinMemData->magic2==WINMEM_MAGIC2 ); + +#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE + if( !pWinMemData->hHeap ){ + DWORD dwInitialSize = SQLITE_WIN32_HEAP_INIT_SIZE; + DWORD dwMaximumSize = (DWORD)sqlcipher_sqlite3GlobalConfig.nHeap; + if( dwMaximumSize==0 ){ + dwMaximumSize = SQLITE_WIN32_HEAP_MAX_SIZE; + }else if( dwInitialSize>dwMaximumSize ){ + dwInitialSize = dwMaximumSize; + } + pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS, + dwInitialSize, dwMaximumSize); + if( !pWinMemData->hHeap ){ + sqlcipher_sqlite3_log(SQLITE_NOMEM, + "failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu", + osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize, + dwMaximumSize); + return SQLITE_NOMEM_BKPT; + } + pWinMemData->bOwned = TRUE; + assert( pWinMemData->bOwned ); + } +#else + pWinMemData->hHeap = osGetProcessHeap(); + if( !pWinMemData->hHeap ){ + sqlcipher_sqlite3_log(SQLITE_NOMEM, + "failed to GetProcessHeap (%lu)", osGetLastError()); + return SQLITE_NOMEM_BKPT; + } + pWinMemData->bOwned = FALSE; + assert( !pWinMemData->bOwned ); +#endif + assert( pWinMemData->hHeap!=0 ); + assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) + assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); #endif + return SQLITE_OK; +} /* -** Search for an unused file descriptor that was opened on the database -** file (not a journal or super-journal file) identified by pathname -** zPath with SQLITE_OPEN_XXX flags matching those passed as the second -** argument to this function. -** -** Such a file descriptor may exist if a database connection was closed -** but the associated file descriptor could not be closed because some -** other file descriptor open on the same file is holding a file-lock. -** Refer to comments in the unixClose() function and the lengthy comment -** describing "Posix Advisory Locking" at the start of this file for -** further details. Also, ticket #4018. -** -** If a suitable file descriptor is found, then it is returned. If no -** such file descriptor is located, -1 is returned. +** Deinitialize this module. */ -static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ - UnixUnusedFd *pUnused = 0; - - /* Do not search for an unused file descriptor on vxworks. Not because - ** vxworks would not benefit from the change (it might, we're not sure), - ** but because no way to test it is currently available. It is better - ** not to risk breaking vxworks support for the sake of such an obscure - ** feature. */ -#if !OS_VXWORKS - struct stat sStat; /* Results of stat() call */ - - unixEnterMutex(); +static void winMemShutdown(void *pAppData){ + winMemData *pWinMemData = (winMemData *)pAppData; - /* A stat() call may fail for various reasons. If this happens, it is - ** almost certain that an open() call on the same path will also fail. - ** For this reason, if an error occurs in the stat() call here, it is - ** ignored and -1 is returned. The caller will try to open a new file - ** descriptor on the same path, fail, and return an error to SQLite. - ** - ** Even if a subsequent open() call does succeed, the consequences of - ** not searching for a reusable file descriptor are not dire. */ - if( inodeList!=0 && 0==osStat(zPath, &sStat) ){ - unixInodeInfo *pInode; + if( !pWinMemData ) return; + assert( pWinMemData->magic1==WINMEM_MAGIC1 ); + assert( pWinMemData->magic2==WINMEM_MAGIC2 ); - pInode = inodeList; - while( pInode && (pInode->fileId.dev!=sStat.st_dev - || pInode->fileId.ino!=(u64)sStat.st_ino) ){ - pInode = pInode->pNext; - } - if( pInode ){ - UnixUnusedFd **pp; - assert( sqlcipher_sqlite3_mutex_notheld(pInode->pLockMutex) ); - sqlcipher_sqlite3_mutex_enter(pInode->pLockMutex); - flags &= (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE); - for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); - pUnused = *pp; - if( pUnused ){ - *pp = pUnused->pNext; + if( pWinMemData->hHeap ){ + assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) + assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); +#endif + if( pWinMemData->bOwned ){ + if( !osHeapDestroy(pWinMemData->hHeap) ){ + sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%lu), heap=%p", + osGetLastError(), (void*)pWinMemData->hHeap); } - sqlcipher_sqlite3_mutex_leave(pInode->pLockMutex); + pWinMemData->bOwned = FALSE; } + pWinMemData->hHeap = NULL; } - unixLeaveMutex(); -#endif /* if !OS_VXWORKS */ - return pUnused; } /* -** Find the mode, uid and gid of file zFile. +** Populate the low-level memory allocation function pointers in +** sqlcipher_sqlite3GlobalConfig.m with pointers to the routines in this file. The +** arguments specify the block of memory to manage. +** +** This routine is only called by sqlcipher_sqlite3_config(), and therefore +** is not required to be threadsafe (it is not). */ -static int getFileMode( - const char *zFile, /* File name */ - mode_t *pMode, /* OUT: Permissions of zFile */ - uid_t *pUid, /* OUT: uid of zFile. */ - gid_t *pGid /* OUT: gid of zFile. */ -){ - struct stat sStat; /* Output of stat() on database file */ - int rc = SQLITE_OK; - if( 0==osStat(zFile, &sStat) ){ - *pMode = sStat.st_mode & 0777; - *pUid = sStat.st_uid; - *pGid = sStat.st_gid; - }else{ - rc = SQLITE_IOERR_FSTAT; - } - return rc; +SQLITE_PRIVATE const sqlcipher_sqlite3_mem_methods *sqlcipher_sqlite3MemGetWin32(void){ + static const sqlcipher_sqlite3_mem_methods winMemMethods = { + winMemMalloc, + winMemFree, + winMemRealloc, + winMemSize, + winMemRoundup, + winMemInit, + winMemShutdown, + &win_mem_data + }; + return &winMemMethods; +} + +SQLITE_PRIVATE void sqlcipher_sqlite3MemSetDefault(void){ + sqlcipher_sqlite3_config(SQLITE_CONFIG_MALLOC, sqlcipher_sqlite3MemGetWin32()); } +#endif /* SQLITE_WIN32_MALLOC */ /* -** This function is called by unixOpen() to determine the unix permissions -** to create new files with. If no error occurs, then SQLITE_OK is returned -** and a value suitable for passing as the third argument to open(2) is -** written to *pMode. If an IO error occurs, an SQLite error code is -** returned and the value of *pMode is not modified. -** -** In most cases, this routine sets *pMode to 0, which will become -** an indication to robust_open() to create the file using -** SQLITE_DEFAULT_FILE_PERMISSIONS adjusted by the umask. -** But if the file being opened is a WAL or regular journal file, then -** this function queries the file-system for the permissions on the -** corresponding database file and sets *pMode to this value. Whenever -** possible, WAL and journal files are created using the same permissions -** as the associated database file. +** Convert a UTF-8 string to Microsoft Unicode. ** -** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the -** original filename is unavailable. But 8_3_NAMES is only used for -** FAT filesystems and permissions do not matter there, so just use -** the default permissions. In 8_3_NAMES mode, leave *pMode set to zero. +** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). */ -static int findCreateFileMode( - const char *zPath, /* Path of file (possibly) being created */ - int flags, /* Flags passed as 4th argument to xOpen() */ - mode_t *pMode, /* OUT: Permissions to open file with */ - uid_t *pUid, /* OUT: uid to set on the file */ - gid_t *pGid /* OUT: gid to set on the file */ -){ - int rc = SQLITE_OK; /* Return Code */ - *pMode = 0; - *pUid = 0; - *pGid = 0; - if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ - char zDb[MAX_PATHNAME+1]; /* Database file path */ - int nDb; /* Number of valid bytes in zDb */ - - /* zPath is a path to a WAL or journal file. The following block derives - ** the path to the associated database file from zPath. This block handles - ** the following naming conventions: - ** - ** "-journal" - ** "-wal" - ** "-journalNN" - ** "-walNN" - ** - ** where NN is a decimal number. The NN naming schemes are - ** used by the test_multiplex.c module. - */ - nDb = sqlcipher_sqlite3Strlen30(zPath) - 1; - while( zPath[nDb]!='-' ){ - /* In normal operation, the journal file name will always contain - ** a '-' character. However in 8+3 filename mode, or if a corrupt - ** rollback journal specifies a super-journal with a goofy name, then - ** the '-' might be missing. */ - if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK; - nDb--; - } - memcpy(zDb, zPath, nDb); - zDb[nDb] = '\0'; +static LPWSTR winUtf8ToUnicode(const char *zText){ + int nChar; + LPWSTR zWideText; - rc = getFileMode(zDb, pMode, pUid, pGid); - }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ - *pMode = 0600; - }else if( flags & SQLITE_OPEN_URI ){ - /* If this is a main database file and the file was opened using a URI - ** filename, check for the "modeof" parameter. If present, interpret - ** its value as a filename and try to copy the mode, uid and gid from - ** that file. */ - const char *z = sqlcipher_sqlite3_uri_parameter(zPath, "modeof"); - if( z ){ - rc = getFileMode(z, pMode, pUid, pGid); - } + nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0); + if( nChar==0 ){ + return 0; } - return rc; + zWideText = sqlcipher_sqlite3MallocZero( nChar*sizeof(WCHAR) ); + if( zWideText==0 ){ + return 0; + } + nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText, + nChar); + if( nChar==0 ){ + sqlcipher_sqlite3_free(zWideText); + zWideText = 0; + } + return zWideText; } /* -** Open the file zPath. -** -** Previously, the SQLite OS layer used three functions in place of this -** one: -** -** sqlcipher_sqlite3OsOpenReadWrite(); -** sqlcipher_sqlite3OsOpenReadOnly(); -** sqlcipher_sqlite3OsOpenExclusive(); -** -** These calls correspond to the following combinations of flags: -** -** ReadWrite() -> (READWRITE | CREATE) -** ReadOnly() -> (READONLY) -** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE) +** Convert a Microsoft Unicode string to UTF-8. ** -** The old OpenExclusive() accepted a boolean argument - "delFlag". If -** true, the file was configured to be automatically deleted when the -** file handle closed. To achieve the same effect using this new -** interface, add the DELETEONCLOSE flag to those specified above for -** OpenExclusive(). +** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). */ -static int unixOpen( - sqlcipher_sqlite3_vfs *pVfs, /* The VFS for which this is the xOpen method */ - const char *zPath, /* Pathname of file to be opened */ - sqlcipher_sqlite3_file *pFile, /* The file descriptor to be filled in */ - int flags, /* Input flags to control the opening */ - int *pOutFlags /* Output flags returned to SQLite core */ -){ - unixFile *p = (unixFile *)pFile; - int fd = -1; /* File descriptor returned by open() */ - int openFlags = 0; /* Flags to pass to open() */ - int eType = flags&0x0FFF00; /* Type of file to open */ - int noLock; /* True to omit locking primitives */ - int rc = SQLITE_OK; /* Function Return Code */ - int ctrlFlags = 0; /* UNIXFILE_* flags */ - - int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); - int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); - int isCreate = (flags & SQLITE_OPEN_CREATE); - int isReadonly = (flags & SQLITE_OPEN_READONLY); - int isReadWrite = (flags & SQLITE_OPEN_READWRITE); -#if SQLITE_ENABLE_LOCKING_STYLE - int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY); -#endif -#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE - struct statfs fsInfo; -#endif - - /* If creating a super- or main-file journal, this function will open - ** a file-descriptor on the directory too. The first time unixSync() - ** is called the directory file descriptor will be fsync()ed and close()d. - */ - int isNewJrnl = (isCreate && ( - eType==SQLITE_OPEN_SUPER_JOURNAL - || eType==SQLITE_OPEN_MAIN_JOURNAL - || eType==SQLITE_OPEN_WAL - )); - - /* If argument zPath is a NULL pointer, this function is required to open - ** a temporary file. Use this buffer to store the file name in. - */ - char zTmpname[MAX_PATHNAME+2]; - const char *zName = zPath; - - /* Check the following statements are true: - ** - ** (a) Exactly one of the READWRITE and READONLY flags must be set, and - ** (b) if CREATE is set, then READWRITE must also be set, and - ** (c) if EXCLUSIVE is set, then CREATE must also be set. - ** (d) if DELETEONCLOSE is set, then CREATE must also be set. - */ - assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); - assert(isCreate==0 || isReadWrite); - assert(isExclusive==0 || isCreate); - assert(isDelete==0 || isCreate); - - /* The main DB, main journal, WAL file and super-journal are never - ** automatically deleted. Nor are they ever temporary files. */ - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_SUPER_JOURNAL ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); - - /* Assert that the upper layer has set one of the "file-type" flags. */ - assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB - || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL - || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL - || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL - ); +static char *winUnicodeToUtf8(LPCWSTR zWideText){ + int nByte; + char *zText; - /* Detect a pid change and reset the PRNG. There is a race condition - ** here such that two or more threads all trying to open databases at - ** the same instant might all reset the PRNG. But multiple resets - ** are harmless. - */ - if( randomnessPid!=osGetpid(0) ){ - randomnessPid = osGetpid(0); - sqlcipher_sqlite3_randomness(0,0); + nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, 0, 0, 0, 0); + if( nByte == 0 ){ + return 0; } - memset(p, 0, sizeof(unixFile)); - - if( eType==SQLITE_OPEN_MAIN_DB ){ - UnixUnusedFd *pUnused; - pUnused = findReusableFd(zName, flags); - if( pUnused ){ - fd = pUnused->fd; - }else{ - pUnused = sqlcipher_sqlite3_malloc64(sizeof(*pUnused)); - if( !pUnused ){ - return SQLITE_NOMEM_BKPT; - } - } - p->pPreallocatedUnused = pUnused; - - /* Database filenames are double-zero terminated if they are not - ** URIs with parameters. Hence, they can always be passed into - ** sqlcipher_sqlite3_uri_parameter(). */ - assert( (flags & SQLITE_OPEN_URI) || zName[strlen(zName)+1]==0 ); - - }else if( !zName ){ - /* If zName is NULL, the upper layer is requesting a temp file. */ - assert(isDelete && !isNewJrnl); - rc = unixGetTempname(pVfs->mxPathname, zTmpname); - if( rc!=SQLITE_OK ){ - return rc; - } - zName = zTmpname; - - /* Generated temporary filenames are always double-zero terminated - ** for use by sqlcipher_sqlite3_uri_parameter(). */ - assert( zName[strlen(zName)+1]==0 ); + zText = sqlcipher_sqlite3MallocZero( nByte ); + if( zText==0 ){ + return 0; } + nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, zText, nByte, + 0, 0); + if( nByte == 0 ){ + sqlcipher_sqlite3_free(zText); + zText = 0; + } + return zText; +} - /* Determine the value of the flags parameter passed to POSIX function - ** open(). These must be calculated even if open() is not called, as - ** they may be stored as part of the file handle and used by the - ** 'conch file' locking functions later on. */ - if( isReadonly ) openFlags |= O_RDONLY; - if( isReadWrite ) openFlags |= O_RDWR; - if( isCreate ) openFlags |= O_CREAT; - if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW); - openFlags |= (O_LARGEFILE|O_BINARY|O_NOFOLLOW); - - if( fd<0 ){ - mode_t openMode; /* Permissions to create file with */ - uid_t uid; /* Userid for the file */ - gid_t gid; /* Groupid for the file */ - rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid); - if( rc!=SQLITE_OK ){ - assert( !p->pPreallocatedUnused ); - assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); - return rc; - } - fd = robust_open(zName, openFlags, openMode); - OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags)); - assert( !isExclusive || (openFlags & O_CREAT)!=0 ); - if( fd<0 ){ - if( isNewJrnl && errno==EACCES && osAccess(zName, F_OK) ){ - /* If unable to create a journal because the directory is not - ** writable, change the error code to indicate that. */ - rc = SQLITE_READONLY_DIRECTORY; - }else if( errno!=EISDIR && isReadWrite ){ - /* Failed to open the file for read/write access. Try read-only. */ - flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); - openFlags &= ~(O_RDWR|O_CREAT); - flags |= SQLITE_OPEN_READONLY; - openFlags |= O_RDONLY; - isReadonly = 1; - fd = robust_open(zName, openFlags, openMode); - } - } - if( fd<0 ){ - int rc2 = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName); - if( rc==SQLITE_OK ) rc = rc2; - goto open_finished; - } +/* +** Convert an ANSI string to Microsoft Unicode, using the ANSI or OEM +** code page. +** +** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). +*/ +static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){ + int nByte; + LPWSTR zMbcsText; + int codepage = useAnsi ? CP_ACP : CP_OEMCP; - /* The owner of the rollback journal or WAL file should always be the - ** same as the owner of the database file. Try to ensure that this is - ** the case. The chown() system call will be a no-op if the current - ** process lacks root privileges, be we should at least try. Without - ** this step, if a root process opens a database file, it can leave - ** behinds a journal/WAL that is owned by root and hence make the - ** database inaccessible to unprivileged processes. - ** - ** If openMode==0, then that means uid and gid are not set correctly - ** (probably because SQLite is configured to use 8+3 filename mode) and - ** in that case we do not want to attempt the chown(). - */ - if( openMode && (flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){ - robustFchown(fd, uid, gid); - } + nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL, + 0)*sizeof(WCHAR); + if( nByte==0 ){ + return 0; } - assert( fd>=0 ); - if( pOutFlags ){ - *pOutFlags = flags; + zMbcsText = sqlcipher_sqlite3MallocZero( nByte*sizeof(WCHAR) ); + if( zMbcsText==0 ){ + return 0; } - - if( p->pPreallocatedUnused ){ - p->pPreallocatedUnused->fd = fd; - p->pPreallocatedUnused->flags = - flags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE); + nByte = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText, + nByte); + if( nByte==0 ){ + sqlcipher_sqlite3_free(zMbcsText); + zMbcsText = 0; } + return zMbcsText; +} - if( isDelete ){ -#if OS_VXWORKS - zPath = zName; -#elif defined(SQLITE_UNLINK_AFTER_CLOSE) - zPath = sqlcipher_sqlite3_mprintf("%s", zName); - if( zPath==0 ){ - robust_close(p, fd, __LINE__); - return SQLITE_NOMEM_BKPT; - } -#else - osUnlink(zName); -#endif - } -#if SQLITE_ENABLE_LOCKING_STYLE - else{ - p->openFlags = openFlags; - } -#endif +/* +** Convert a Microsoft Unicode string to a multi-byte character string, +** using the ANSI or OEM code page. +** +** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). +*/ +static char *winUnicodeToMbcs(LPCWSTR zWideText, int useAnsi){ + int nByte; + char *zText; + int codepage = useAnsi ? CP_ACP : CP_OEMCP; -#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE - if( fstatfs(fd, &fsInfo) == -1 ){ - storeLastErrno(p, errno); - robust_close(p, fd, __LINE__); - return SQLITE_IOERR_ACCESS; + nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, 0, 0, 0, 0); + if( nByte == 0 ){ + return 0; } - if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { - ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; + zText = sqlcipher_sqlite3MallocZero( nByte ); + if( zText==0 ){ + return 0; } - if (0 == strncmp("exfat", fsInfo.f_fstypename, 5)) { - ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; + nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, zText, + nByte, 0, 0); + if( nByte == 0 ){ + sqlcipher_sqlite3_free(zText); + zText = 0; } -#endif - - /* Set up appropriate ctrlFlags */ - if( isDelete ) ctrlFlags |= UNIXFILE_DELETE; - if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY; - noLock = eType!=SQLITE_OPEN_MAIN_DB; - if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK; - if( isNewJrnl ) ctrlFlags |= UNIXFILE_DIRSYNC; - if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI; + return zText; +} -#if SQLITE_ENABLE_LOCKING_STYLE -#if SQLITE_PREFER_PROXY_LOCKING - isAutoProxy = 1; -#endif - if( isAutoProxy && (zPath!=NULL) && (!noLock) && pVfs->xOpen ){ - char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING"); - int useProxy = 0; +/* +** Convert a multi-byte character string to UTF-8. +** +** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). +*/ +static char *winMbcsToUtf8(const char *zText, int useAnsi){ + char *zTextUtf8; + LPWSTR zTmpWide; - /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means - ** never use proxy, NULL means use proxy for non-local files only. */ - if( envforce!=NULL ){ - useProxy = atoi(envforce)>0; - }else{ - useProxy = !(fsInfo.f_flags&MNT_LOCAL); - } - if( useProxy ){ - rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); - if( rc==SQLITE_OK ){ - rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); - if( rc!=SQLITE_OK ){ - /* Use unixClose to clean up the resources added in fillInUnixFile - ** and clear all the structure's references. Specifically, - ** pFile->pMethods will be NULL so sqlcipher_sqlite3OsClose will be a no-op - */ - unixClose(pFile); - return rc; - } - } - goto open_finished; - } + zTmpWide = winMbcsToUnicode(zText, useAnsi); + if( zTmpWide==0 ){ + return 0; } -#endif + zTextUtf8 = winUnicodeToUtf8(zTmpWide); + sqlcipher_sqlite3_free(zTmpWide); + return zTextUtf8; +} - assert( zPath==0 || zPath[0]=='/' - || eType==SQLITE_OPEN_SUPER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL - ); - rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); +/* +** Convert a UTF-8 string to a multi-byte character string. +** +** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). +*/ +static char *winUtf8ToMbcs(const char *zText, int useAnsi){ + char *zTextMbcs; + LPWSTR zTmpWide; -open_finished: - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3_free(p->pPreallocatedUnused); + zTmpWide = winUtf8ToUnicode(zText); + if( zTmpWide==0 ){ + return 0; } - return rc; + zTextMbcs = winUnicodeToMbcs(zTmpWide, useAnsi); + sqlcipher_sqlite3_free(zTmpWide); + return zTextMbcs; } - /* -** Delete the file at zPath. If the dirSync argument is true, fsync() -** the directory after deleting the file. +** This is a public wrapper for the winUtf8ToUnicode() function. */ -static int unixDelete( - sqlcipher_sqlite3_vfs *NotUsed, /* VFS containing this as the xDelete method */ - const char *zPath, /* Name of file to be deleted */ - int dirSync /* If true, fsync() directory after deleting file */ -){ - int rc = SQLITE_OK; - UNUSED_PARAMETER(NotUsed); - SimulateIOError(return SQLITE_IOERR_DELETE); - if( osUnlink(zPath)==(-1) ){ - if( errno==ENOENT -#if OS_VXWORKS - || osAccess(zPath,0)!=0 -#endif - ){ - rc = SQLITE_IOERR_DELETE_NOENT; - }else{ - rc = unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); - } - return rc; - } -#ifndef SQLITE_DISABLE_DIRSYNC - if( (dirSync & 1)!=0 ){ - int fd; - rc = osOpenDirectory(zPath, &fd); - if( rc==SQLITE_OK ){ - if( full_fsync(fd,0,0) ){ - rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath); - } - robust_close(0, fd, __LINE__); - }else{ - assert( rc==SQLITE_CANTOPEN ); - rc = SQLITE_OK; - } +SQLITE_API LPWSTR sqlcipher_sqlite3_win32_utf8_to_unicode(const char *zText){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !zText ){ + (void)SQLITE_MISUSE_BKPT; + return 0; } #endif - return rc; +#ifndef SQLITE_OMIT_AUTOINIT + if( sqlcipher_sqlite3_initialize() ) return 0; +#endif + return winUtf8ToUnicode(zText); } /* -** Test the existence of or access permissions of file zPath. The -** test performed depends on the value of flags: -** -** SQLITE_ACCESS_EXISTS: Return 1 if the file exists -** SQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable. -** SQLITE_ACCESS_READONLY: Return 1 if the file is readable. -** -** Otherwise return 0. +** This is a public wrapper for the winUnicodeToUtf8() function. */ -static int unixAccess( - sqlcipher_sqlite3_vfs *NotUsed, /* The VFS containing this xAccess method */ - const char *zPath, /* Path of the file to examine */ - int flags, /* What do we want to learn about the zPath file? */ - int *pResOut /* Write result boolean here */ -){ - UNUSED_PARAMETER(NotUsed); - SimulateIOError( return SQLITE_IOERR_ACCESS; ); - assert( pResOut!=0 ); - - /* The spec says there are three possible values for flags. But only - ** two of them are actually used */ - assert( flags==SQLITE_ACCESS_EXISTS || flags==SQLITE_ACCESS_READWRITE ); - - if( flags==SQLITE_ACCESS_EXISTS ){ - struct stat buf; - *pResOut = 0==osStat(zPath, &buf) && - (!S_ISREG(buf.st_mode) || buf.st_size>0); - }else{ - *pResOut = osAccess(zPath, W_OK|R_OK)==0; +SQLITE_API char *sqlcipher_sqlite3_win32_unicode_to_utf8(LPCWSTR zWideText){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !zWideText ){ + (void)SQLITE_MISUSE_BKPT; + return 0; } - return SQLITE_OK; +#endif +#ifndef SQLITE_OMIT_AUTOINIT + if( sqlcipher_sqlite3_initialize() ) return 0; +#endif + return winUnicodeToUtf8(zWideText); } /* -** If the last component of the pathname in z[0]..z[j-1] is something -** other than ".." then back it out and return true. If the last -** component is empty or if it is ".." then return false. +** This is a public wrapper for the winMbcsToUtf8() function. */ -static int unixBackupDir(const char *z, int *pJ){ - int j = *pJ; - int i; - if( j<=0 ) return 0; - for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){} - if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; - *pJ = i-1; - return 1; +SQLITE_API char *sqlcipher_sqlite3_win32_mbcs_to_utf8(const char *zText){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !zText ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif +#ifndef SQLITE_OMIT_AUTOINIT + if( sqlcipher_sqlite3_initialize() ) return 0; +#endif + return winMbcsToUtf8(zText, osAreFileApisANSI()); } /* -** Convert a relative pathname into a full pathname. Also -** simplify the pathname as follows: -** -** Remove all instances of /./ -** Remove all isntances of /X/../ for any X +** This is a public wrapper for the winMbcsToUtf8() function. */ -static int mkFullPathname( - const char *zPath, /* Input path */ - char *zOut, /* Output buffer */ - int nOut /* Allocated size of buffer zOut */ -){ - int nPath = sqlcipher_sqlite3Strlen30(zPath); - int iOff = 0; - int i, j; - if( zPath[0]!='/' ){ - if( osGetcwd(zOut, nOut-2)==0 ){ - return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); - } - iOff = sqlcipher_sqlite3Strlen30(zOut); - zOut[iOff++] = '/'; - } - if( (iOff+nPath+1)>nOut ){ - /* SQLite assumes that xFullPathname() nul-terminates the output buffer - ** even if it returns an error. */ - zOut[iOff] = '\0'; - return SQLITE_CANTOPEN_BKPT; +SQLITE_API char *sqlcipher_sqlite3_win32_mbcs_to_utf8_v2(const char *zText, int useAnsi){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !zText ){ + (void)SQLITE_MISUSE_BKPT; + return 0; } - sqlcipher_sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); +#endif +#ifndef SQLITE_OMIT_AUTOINIT + if( sqlcipher_sqlite3_initialize() ) return 0; +#endif + return winMbcsToUtf8(zText, useAnsi); +} - /* Remove duplicate '/' characters. Except, two // at the beginning - ** of a pathname is allowed since this is important on windows. */ - for(i=j=1; zOut[i]; i++){ - zOut[j++] = zOut[i]; - while( zOut[i]=='/' && zOut[i+1]=='/' ) i++; +/* +** This is a public wrapper for the winUtf8ToMbcs() function. +*/ +SQLITE_API char *sqlcipher_sqlite3_win32_utf8_to_mbcs(const char *zText){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !zText ){ + (void)SQLITE_MISUSE_BKPT; + return 0; } - zOut[j] = 0; - - assert( zOut[0]=='/' ); - for(i=j=0; zOut[i]; i++){ - if( zOut[i]=='/' ){ - /* Skip over internal "/." directory components */ - if( zOut[i+1]=='.' && zOut[i+2]=='/' ){ - i += 1; - continue; - } +#endif +#ifndef SQLITE_OMIT_AUTOINIT + if( sqlcipher_sqlite3_initialize() ) return 0; +#endif + return winUtf8ToMbcs(zText, osAreFileApisANSI()); +} - /* If this is a "/.." directory component then back out the - ** previous term of the directory if it is something other than "..". - */ - if( zOut[i+1]=='.' - && zOut[i+2]=='.' - && zOut[i+3]=='/' - && unixBackupDir(zOut, &j) - ){ - i += 2; - continue; - } - } - if( ALWAYS(j>=0) ) zOut[j] = zOut[i]; - j++; +/* +** This is a public wrapper for the winUtf8ToMbcs() function. +*/ +SQLITE_API char *sqlcipher_sqlite3_win32_utf8_to_mbcs_v2(const char *zText, int useAnsi){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !zText ){ + (void)SQLITE_MISUSE_BKPT; + return 0; } - if( NEVER(j==0) ) zOut[j++] = '/'; - zOut[j] = 0; - return SQLITE_OK; +#endif +#ifndef SQLITE_OMIT_AUTOINIT + if( sqlcipher_sqlite3_initialize() ) return 0; +#endif + return winUtf8ToMbcs(zText, useAnsi); } /* -** Turn a relative pathname into a full pathname. The relative path -** is stored as a nul-terminated string in the buffer pointed to by -** zPath. -** -** zOut points to a buffer of at least sqlcipher_sqlite3_vfs.mxPathname bytes -** (in this case, MAX_PATHNAME bytes). The full-path is written to -** this buffer before returning. +** This function is the same as sqlcipher_sqlite3_win32_set_directory (below); however, +** it accepts a UTF-8 string. */ -static int unixFullPathname( - sqlcipher_sqlite3_vfs *pVfs, /* Pointer to vfs object */ - const char *zPath, /* Possibly relative input path */ - int nOut, /* Size of output buffer in bytes */ - char *zOut /* Output buffer */ +SQLITE_API int sqlcipher_sqlite3_win32_set_directory8( + unsigned long type, /* Identifier for directory being set or reset */ + const char *zValue /* New value for directory being set or reset */ ){ -#if !defined(HAVE_READLINK) || !defined(HAVE_LSTAT) - return mkFullPathname(zPath, zOut, nOut); -#else - int rc = SQLITE_OK; - int nByte; - int nLink = 0; /* Number of symbolic links followed so far */ - const char *zIn = zPath; /* Input path for each iteration of loop */ - char *zDel = 0; - - assert( pVfs->mxPathname==MAX_PATHNAME ); - UNUSED_PARAMETER(pVfs); - - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. This function could fail if, for example, the - ** current working directory has been unlinked. - */ - SimulateIOError( return SQLITE_ERROR ); - - do { - - /* Call stat() on path zIn. Set bLink to true if the path is a symbolic - ** link, or false otherwise. */ - int bLink = 0; - struct stat buf; - if( osLstat(zIn, &buf)!=0 ){ - if( errno!=ENOENT ){ - rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn); - } - }else{ - bLink = S_ISLNK(buf.st_mode); - } - - if( bLink ){ - nLink++; - if( zDel==0 ){ - zDel = sqlcipher_sqlite3_malloc(nOut); - if( zDel==0 ) rc = SQLITE_NOMEM_BKPT; - }else if( nLink>=SQLITE_MAX_SYMLINKS ){ - rc = SQLITE_CANTOPEN_BKPT; - } - - if( rc==SQLITE_OK ){ - nByte = osReadlink(zIn, zDel, nOut-1); - if( nByte<0 ){ - rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn); - }else{ - if( zDel[0]!='/' ){ - int n; - for(n = sqlcipher_sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--); - if( nByte+n+1>nOut ){ - rc = SQLITE_CANTOPEN_BKPT; - }else{ - memmove(&zDel[n], zDel, nByte+1); - memcpy(zDel, zIn, n); - nByte += n; - } - } - zDel[nByte] = '\0'; - } + char **ppDirectory = 0; + int rc; +#ifndef SQLITE_OMIT_AUTOINIT + rc = sqlcipher_sqlite3_initialize(); + if( rc ) return rc; +#endif + sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){ + ppDirectory = &sqlcipher_sqlite3_data_directory; + }else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){ + ppDirectory = &sqlcipher_sqlite3_temp_directory; + } + assert( !ppDirectory || type==SQLITE_WIN32_DATA_DIRECTORY_TYPE + || type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE + ); + assert( !ppDirectory || sqlcipher_sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) ); + if( ppDirectory ){ + char *zCopy = 0; + if( zValue && zValue[0] ){ + zCopy = sqlcipher_sqlite3_mprintf("%s", zValue); + if ( zCopy==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto set_directory8_done; } - - zIn = zDel; } - - assert( rc!=SQLITE_OK || zIn!=zOut || zIn[0]=='/' ); - if( rc==SQLITE_OK && zIn!=zOut ){ - rc = mkFullPathname(zIn, zOut, nOut); - } - if( bLink==0 ) break; - zIn = zOut; - }while( rc==SQLITE_OK ); - - sqlcipher_sqlite3_free(zDel); - if( rc==SQLITE_OK && nLink ) rc = SQLITE_OK_SYMLINK; + sqlcipher_sqlite3_free(*ppDirectory); + *ppDirectory = zCopy; + rc = SQLITE_OK; + }else{ + rc = SQLITE_ERROR; + } +set_directory8_done: + sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); return rc; -#endif /* HAVE_READLINK && HAVE_LSTAT */ } - -#ifndef SQLITE_OMIT_LOAD_EXTENSION /* -** Interfaces for opening a shared library, finding entry points -** within the shared library, and closing the shared library. +** This function is the same as sqlcipher_sqlite3_win32_set_directory (below); however, +** it accepts a UTF-16 string. */ -#include -static void *unixDlOpen(sqlcipher_sqlite3_vfs *NotUsed, const char *zFilename){ - UNUSED_PARAMETER(NotUsed); - return dlopen(zFilename, RTLD_NOW | RTLD_GLOBAL); +SQLITE_API int sqlcipher_sqlite3_win32_set_directory16( + unsigned long type, /* Identifier for directory being set or reset */ + const void *zValue /* New value for directory being set or reset */ +){ + int rc; + char *zUtf8 = 0; + if( zValue ){ + zUtf8 = sqlcipher_sqlite3_win32_unicode_to_utf8(zValue); + if( zUtf8==0 ) return SQLITE_NOMEM_BKPT; + } + rc = sqlcipher_sqlite3_win32_set_directory8(type, zUtf8); + if( zUtf8 ) sqlcipher_sqlite3_free(zUtf8); + return rc; } /* -** SQLite calls this function immediately after a call to unixDlSym() or -** unixDlOpen() fails (returns a null pointer). If a more detailed error -** message is available, it is written to zBufOut. If no error message -** is available, zBufOut is left unmodified and SQLite uses a default -** error message. +** This function sets the data directory or the temporary directory based on +** the provided arguments. The type argument must be 1 in order to set the +** data directory or 2 in order to set the temporary directory. The zValue +** argument is the name of the directory to use. The return value will be +** SQLITE_OK if successful. */ -static void unixDlError(sqlcipher_sqlite3_vfs *NotUsed, int nBuf, char *zBufOut){ - const char *zErr; - UNUSED_PARAMETER(NotUsed); - unixEnterMutex(); - zErr = dlerror(); - if( zErr ){ - sqlcipher_sqlite3_snprintf(nBuf, zBufOut, "%s", zErr); - } - unixLeaveMutex(); -} -static void (*unixDlSym(sqlcipher_sqlite3_vfs *NotUsed, void *p, const char*zSym))(void){ - /* - ** GCC with -pedantic-errors says that C90 does not allow a void* to be - ** cast into a pointer to a function. And yet the library dlsym() routine - ** returns a void* which is really a pointer to a function. So how do we - ** use dlsym() with -pedantic-errors? - ** - ** Variable x below is defined to be a pointer to a function taking - ** parameters void* and const char* and returning a pointer to a function. - ** We initialize x by assigning it a pointer to the dlsym() function. - ** (That assignment requires a cast.) Then we call the function that - ** x points to. - ** - ** This work-around is unlikely to work correctly on any system where - ** you really cannot cast a function pointer into void*. But then, on the - ** other hand, dlsym() will not work on such a system either, so we have - ** not really lost anything. - */ - void (*(*x)(void*,const char*))(void); - UNUSED_PARAMETER(NotUsed); - x = (void(*(*)(void*,const char*))(void))dlsym; - return (*x)(p, zSym); -} -static void unixDlClose(sqlcipher_sqlite3_vfs *NotUsed, void *pHandle){ - UNUSED_PARAMETER(NotUsed); - dlclose(pHandle); +SQLITE_API int sqlcipher_sqlite3_win32_set_directory( + unsigned long type, /* Identifier for directory being set or reset */ + void *zValue /* New value for directory being set or reset */ +){ + return sqlcipher_sqlite3_win32_set_directory16(type, zValue); } -#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ - #define unixDlOpen 0 - #define unixDlError 0 - #define unixDlSym 0 - #define unixDlClose 0 -#endif /* -** Write nBuf bytes of random data to the supplied buffer zBuf. +** The return value of winGetLastErrorMsg +** is zero if the error message fits in the buffer, or non-zero +** otherwise (if the message was truncated). */ -static int unixRandomness(sqlcipher_sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ - UNUSED_PARAMETER(NotUsed); - assert((size_t)nBuf>=(sizeof(time_t)+sizeof(int))); - - /* We have to initialize zBuf to prevent valgrind from reporting - ** errors. The reports issued by valgrind are incorrect - we would - ** prefer that the randomness be increased by making use of the - ** uninitialized space in zBuf - but valgrind errors tend to worry - ** some users. Rather than argue, it seems easier just to initialize - ** the whole array and silence valgrind, even if that means less randomness - ** in the random seed. - ** - ** When testing, initializing zBuf[] to zero is all we do. That means - ** that we always use the same random number sequence. This makes the - ** tests repeatable. +static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ + /* FormatMessage returns 0 on failure. Otherwise it + ** returns the number of TCHARs written to the output + ** buffer, excluding the terminating null char. */ - memset(zBuf, 0, nBuf); - randomnessPid = osGetpid(0); -#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) - { - int fd, got; - fd = robust_open("/dev/urandom", O_RDONLY, 0); - if( fd<0 ){ - time_t t; - time(&t); - memcpy(zBuf, &t, sizeof(t)); - memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid)); - assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf ); - nBuf = sizeof(t) + sizeof(randomnessPid); - }else{ - do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR ); - robust_close(0, fd, __LINE__); + DWORD dwLen = 0; + char *zOut = 0; + + if( osIsNT() ){ +#if SQLITE_OS_WINRT + WCHAR zTempWide[SQLITE_WIN32_MAX_ERRMSG_CHARS+1]; + dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + lastErrno, + 0, + zTempWide, + SQLITE_WIN32_MAX_ERRMSG_CHARS, + 0); +#else + LPWSTR zTempWide = NULL; + dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + lastErrno, + 0, + (LPWSTR) &zTempWide, + 0, + 0); +#endif + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + sqlcipher_sqlite3BeginBenignMalloc(); + zOut = winUnicodeToUtf8(zTempWide); + sqlcipher_sqlite3EndBenignMalloc(); +#if !SQLITE_OS_WINRT + /* free the system buffer allocated by FormatMessage */ + osLocalFree(zTempWide); +#endif + } + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + char *zTemp = NULL; + dwLen = osFormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + lastErrno, + 0, + (LPSTR) &zTemp, + 0, + 0); + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + sqlcipher_sqlite3BeginBenignMalloc(); + zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI()); + sqlcipher_sqlite3EndBenignMalloc(); + /* free the system buffer allocated by FormatMessage */ + osLocalFree(zTemp); } } #endif - return nBuf; + if( 0 == dwLen ){ + sqlcipher_sqlite3_snprintf(nBuf, zBuf, "OsError 0x%lx (%lu)", lastErrno, lastErrno); + }else{ + /* copy a maximum of nBuf chars to output buffer */ + sqlcipher_sqlite3_snprintf(nBuf, zBuf, "%s", zOut); + /* free the UTF8 buffer */ + sqlcipher_sqlite3_free(zOut); + } + return 0; } - /* -** Sleep for a little while. Return the amount of time slept. -** The argument is the number of microseconds we want to sleep. -** The return value is the number of microseconds of sleep actually -** requested from the underlying operating system, a number which -** might be greater than or equal to the argument, but not less -** than the argument. +** +** This function - winLogErrorAtLine() - is only ever called via the macro +** winLogError(). +** +** This routine is invoked after an error occurs in an OS function. +** It logs a message using sqlcipher_sqlite3_log() containing the current value of +** error code and, if possible, the human-readable equivalent from +** FormatMessage. +** +** The first argument passed to the macro should be the error code that +** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). +** The two subsequent arguments should be the name of the OS function that +** failed and the associated file-system path, if any. */ -static int unixSleep(sqlcipher_sqlite3_vfs *NotUsed, int microseconds){ -#if OS_VXWORKS - struct timespec sp; +#define winLogError(a,b,c,d) winLogErrorAtLine(a,b,c,d,__LINE__) +static int winLogErrorAtLine( + int errcode, /* SQLite error code */ + DWORD lastErrno, /* Win32 last error */ + const char *zFunc, /* Name of OS function that failed */ + const char *zPath, /* File path associated with error */ + int iLine /* Source line number where error occurred */ +){ + char zMsg[500]; /* Human readable error text */ + int i; /* Loop counter */ - sp.tv_sec = microseconds / 1000000; - sp.tv_nsec = (microseconds % 1000000) * 1000; - nanosleep(&sp, NULL); - UNUSED_PARAMETER(NotUsed); - return microseconds; -#elif defined(HAVE_USLEEP) && HAVE_USLEEP - if( microseconds>=1000000 ) sleep(microseconds/1000000); - if( microseconds%1000000 ) usleep(microseconds%1000000); - UNUSED_PARAMETER(NotUsed); - return microseconds; -#else - int seconds = (microseconds+999999)/1000000; - sleep(seconds); - UNUSED_PARAMETER(NotUsed); - return seconds*1000000; -#endif + zMsg[0] = 0; + winGetLastErrorMsg(lastErrno, sizeof(zMsg), zMsg); + assert( errcode!=SQLITE_OK ); + if( zPath==0 ) zPath = ""; + for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){} + zMsg[i] = 0; + sqlcipher_sqlite3_log(errcode, + "os_win.c:%d: (%lu) %s(%s) - %s", + iLine, lastErrno, zFunc, zPath, zMsg + ); + + return errcode; } /* -** The following variable, if set to a non-zero value, is interpreted as -** the number of seconds since 1970 and is used to set the result of -** sqlcipher_sqlite3OsCurrentTime() during testing. +** The number of times that a ReadFile(), WriteFile(), and DeleteFile() +** will be retried following a locking error - probably caused by +** antivirus software. Also the initial delay before the first retry. +** The delay increases linearly with each retry. */ -#ifdef SQLITE_TEST -SQLITE_API int sqlcipher_sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ +#ifndef SQLITE_WIN32_IOERR_RETRY +# define SQLITE_WIN32_IOERR_RETRY 10 +#endif +#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY +# define SQLITE_WIN32_IOERR_RETRY_DELAY 25 #endif +static int winIoerrRetry = SQLITE_WIN32_IOERR_RETRY; +static int winIoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY; /* -** Find the current time (in Universal Coordinated Time). Write into *piNow -** the current time and date as a Julian Day number times 86_400_000. In -** other words, write into *piNow the number of milliseconds since the Julian -** epoch of noon in Greenwich on November 24, 4714 B.C according to the -** proleptic Gregorian calendar. +** The "winIoerrCanRetry1" macro is used to determine if a particular I/O +** error code obtained via GetLastError() is eligible to be retried. It +** must accept the error code DWORD as its only argument and should return +** non-zero if the error code is transient in nature and the operation +** responsible for generating the original error might succeed upon being +** retried. The argument to this macro should be a variable. ** -** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date -** cannot be found. +** Additionally, a macro named "winIoerrCanRetry2" may be defined. If it +** is defined, it will be consulted only when the macro "winIoerrCanRetry1" +** returns zero. The "winIoerrCanRetry2" macro is completely optional and +** may be used to include additional error codes in the set that should +** result in the failing I/O operation being retried by the caller. If +** defined, the "winIoerrCanRetry2" macro must exhibit external semantics +** identical to those of the "winIoerrCanRetry1" macro. */ -static int unixCurrentTimeInt64(sqlcipher_sqlite3_vfs *NotUsed, sqlcipher_sqlite3_int64 *piNow){ - static const sqlcipher_sqlite3_int64 unixEpoch = 24405875*(sqlcipher_sqlite3_int64)8640000; - int rc = SQLITE_OK; -#if defined(NO_GETTOD) - time_t t; - time(&t); - *piNow = ((sqlcipher_sqlite3_int64)t)*1000 + unixEpoch; -#elif OS_VXWORKS - struct timespec sNow; - clock_gettime(CLOCK_REALTIME, &sNow); - *piNow = unixEpoch + 1000*(sqlcipher_sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000; -#else - struct timeval sNow; - (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */ - *piNow = unixEpoch + 1000*(sqlcipher_sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; +#if !defined(winIoerrCanRetry1) +#define winIoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED) || \ + ((a)==ERROR_SHARING_VIOLATION) || \ + ((a)==ERROR_LOCK_VIOLATION) || \ + ((a)==ERROR_DEV_NOT_EXIST) || \ + ((a)==ERROR_NETNAME_DELETED) || \ + ((a)==ERROR_SEM_TIMEOUT) || \ + ((a)==ERROR_NETWORK_UNREACHABLE)) #endif -#ifdef SQLITE_TEST - if( sqlcipher_sqlite3_current_time ){ - *piNow = 1000*(sqlcipher_sqlite3_int64)sqlcipher_sqlite3_current_time + unixEpoch; +/* +** If a ReadFile() or WriteFile() error occurs, invoke this routine +** to see if it should be retried. Return TRUE to retry. Return FALSE +** to give up with an error. +*/ +static int winRetryIoerr(int *pnRetry, DWORD *pError){ + DWORD e = osGetLastError(); + if( *pnRetry>=winIoerrRetry ){ + if( pError ){ + *pError = e; + } + return 0; + } + if( winIoerrCanRetry1(e) ){ + sqlcipher_sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry)); + ++*pnRetry; + return 1; + } +#if defined(winIoerrCanRetry2) + else if( winIoerrCanRetry2(e) ){ + sqlcipher_sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry)); + ++*pnRetry; + return 1; } #endif - UNUSED_PARAMETER(NotUsed); - return rc; + if( pError ){ + *pError = e; + } + return 0; } -#ifndef SQLITE_OMIT_DEPRECATED /* -** Find the current time (in Universal Coordinated Time). Write the -** current time and date as a Julian Day number into *prNow and -** return 0. Return 1 if the time and date cannot be found. +** Log a I/O error retry episode. */ -static int unixCurrentTime(sqlcipher_sqlite3_vfs *NotUsed, double *prNow){ - sqlcipher_sqlite3_int64 i = 0; - int rc; - UNUSED_PARAMETER(NotUsed); - rc = unixCurrentTimeInt64(0, &i); - *prNow = i/86400000.0; - return rc; +static void winLogIoerr(int nRetry, int lineno){ + if( nRetry ){ + sqlcipher_sqlite3_log(SQLITE_NOTICE, + "delayed %dms for lock/sharing conflict at line %d", + winIoerrRetryDelay*nRetry*(nRetry+1)/2, lineno + ); + } } -#else -# define unixCurrentTime 0 -#endif /* -** The xGetLastError() method is designed to return a better -** low-level error message when operating-system problems come up -** during SQLite operation. Only the integer return code is currently -** used. +** This #if does not rely on the SQLITE_OS_WINCE define because the +** corresponding section in "date.c" cannot use it. */ -static int unixGetLastError(sqlcipher_sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ - UNUSED_PARAMETER(NotUsed); - UNUSED_PARAMETER(NotUsed2); - UNUSED_PARAMETER(NotUsed3); - return errno; +#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \ + (!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API) +/* +** The MSVC CRT on Windows CE may not have a localtime() function. +** So define a substitute. +*/ +/* # include */ +struct tm *__cdecl localtime(const time_t *t) +{ + static struct tm y; + FILETIME uTm, lTm; + SYSTEMTIME pTm; + sqlcipher_sqlite3_int64 t64; + t64 = *t; + t64 = (t64 + 11644473600)*10000000; + uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); + uTm.dwHighDateTime= (DWORD)(t64 >> 32); + osFileTimeToLocalFileTime(&uTm,&lTm); + osFileTimeToSystemTime(&lTm,&pTm); + y.tm_year = pTm.wYear - 1900; + y.tm_mon = pTm.wMonth - 1; + y.tm_wday = pTm.wDayOfWeek; + y.tm_mday = pTm.wDay; + y.tm_hour = pTm.wHour; + y.tm_min = pTm.wMinute; + y.tm_sec = pTm.wSecond; + return &y; } +#endif +#if SQLITE_OS_WINCE +/************************************************************************* +** This section contains code for WinCE only. +*/ +#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)] /* -************************ End of sqlcipher_sqlite3_vfs methods *************************** -******************************************************************************/ - -/****************************************************************************** -************************** Begin Proxy Locking ******************************** -** -** Proxy locking is a "uber-locking-method" in this sense: It uses the -** other locking methods on secondary lock files. Proxy locking is a -** meta-layer over top of the primitive locking implemented above. For -** this reason, the division that implements of proxy locking is deferred -** until late in the file (here) after all of the other I/O methods have -** been defined - so that the primitive locking methods are available -** as services to help with the implementation of proxy locking. -** -**** -** -** The default locking schemes in SQLite use byte-range locks on the -** database file to coordinate safe, concurrent access by multiple readers -** and writers [http://sqlite.org/lockingv3.html]. The five file locking -** states (UNLOCKED, PENDING, SHARED, RESERVED, EXCLUSIVE) are implemented -** as POSIX read & write locks over fixed set of locations (via fsctl), -** on AFP and SMB only exclusive byte-range locks are available via fsctl -** with _IOWR('z', 23, struct ByteRangeLockPB2) to track the same 5 states. -** To simulate a F_RDLCK on the shared range, on AFP a randomly selected -** address in the shared range is taken for a SHARED lock, the entire -** shared range is taken for an EXCLUSIVE lock): -** -** PENDING_BYTE 0x40000000 -** RESERVED_BYTE 0x40000001 -** SHARED_RANGE 0x40000002 -> 0x40000200 -** -** This works well on the local file system, but shows a nearly 100x -** slowdown in read performance on AFP because the AFP client disables -** the read cache when byte-range locks are present. Enabling the read -** cache exposes a cache coherency problem that is present on all OS X -** supported network file systems. NFS and AFP both observe the -** close-to-open semantics for ensuring cache coherency -** [http://nfs.sourceforge.net/#faq_a8], which does not effectively -** address the requirements for concurrent database access by multiple -** readers and writers -** [http://www.nabble.com/SQLite-on-NFS-cache-coherency-td15655701.html]. -** -** To address the performance and cache coherency issues, proxy file locking -** changes the way database access is controlled by limiting access to a -** single host at a time and moving file locks off of the database file -** and onto a proxy file on the local file system. -** -** -** Using proxy locks -** ----------------- -** -** C APIs -** -** sqlcipher_sqlite3_file_control(db, dbname, SQLITE_FCNTL_SET_LOCKPROXYFILE, -** | ":auto:"); -** sqlcipher_sqlite3_file_control(db, dbname, SQLITE_FCNTL_GET_LOCKPROXYFILE, -** &); -** -** -** SQL pragmas -** -** PRAGMA [database.]lock_proxy_file= | :auto: -** PRAGMA [database.]lock_proxy_file -** -** Specifying ":auto:" means that if there is a conch file with a matching -** host ID in it, the proxy path in the conch file will be used, otherwise -** a proxy path based on the user's temp dir -** (via confstr(_CS_DARWIN_USER_TEMP_DIR,...)) will be used and the -** actual proxy file name is generated from the name and path of the -** database file. For example: -** -** For database path "/Users/me/foo.db" -** The lock path will be "/sqliteplocks/_Users_me_foo.db:auto:") -** -** Once a lock proxy is configured for a database connection, it can not -** be removed, however it may be switched to a different proxy path via -** the above APIs (assuming the conch file is not being held by another -** connection or process). -** -** -** How proxy locking works -** ----------------------- -** -** Proxy file locking relies primarily on two new supporting files: -** -** * conch file to limit access to the database file to a single host -** at a time -** -** * proxy file to act as a proxy for the advisory locks normally -** taken on the database -** -** The conch file - to use a proxy file, sqlite must first "hold the conch" -** by taking an sqlite-style shared lock on the conch file, reading the -** contents and comparing the host's unique host ID (see below) and lock -** proxy path against the values stored in the conch. The conch file is -** stored in the same directory as the database file and the file name -** is patterned after the database file name as ".-conch". -** If the conch file does not exist, or its contents do not match the -** host ID and/or proxy path, then the lock is escalated to an exclusive -** lock and the conch file contents is updated with the host ID and proxy -** path and the lock is downgraded to a shared lock again. If the conch -** is held by another process (with a shared lock), the exclusive lock -** will fail and SQLITE_BUSY is returned. -** -** The proxy file - a single-byte file used for all advisory file locks -** normally taken on the database file. This allows for safe sharing -** of the database file for multiple readers and writers on the same -** host (the conch ensures that they all use the same local lock file). -** -** Requesting the lock proxy does not immediately take the conch, it is -** only taken when the first request to lock database file is made. -** This matches the semantics of the traditional locking behavior, where -** opening a connection to a database file does not take a lock on it. -** The shared lock and an open file descriptor are maintained until -** the connection to the database is closed. -** -** The proxy file and the lock file are never deleted so they only need -** to be created the first time they are used. -** -** Configuration options -** --------------------- -** -** SQLITE_PREFER_PROXY_LOCKING -** -** Database files accessed on non-local file systems are -** automatically configured for proxy locking, lock files are -** named automatically using the same logic as -** PRAGMA lock_proxy_file=":auto:" -** -** SQLITE_PROXY_DEBUG -** -** Enables the logging of error messages during host id file -** retrieval and creation -** -** LOCKPROXYDIR -** -** Overrides the default directory used for lock proxy files that -** are named automatically via the ":auto:" setting -** -** SQLITE_DEFAULT_PROXYDIR_PERMISSIONS -** -** Permissions to use when creating a directory for storing the -** lock proxy files, only used when LOCKPROXYDIR is not set. -** -** -** As mentioned above, when compiled with SQLITE_PREFER_PROXY_LOCKING, -** setting the environment variable SQLITE_FORCE_PROXY_LOCKING to 1 will -** force proxy locking to be used for every database file opened, and 0 -** will force automatic proxy locking to be disabled for all database -** files (explicitly calling the SQLITE_FCNTL_SET_LOCKPROXYFILE pragma or -** sqlite_file_control API is not affected by SQLITE_FORCE_PROXY_LOCKING). +** Acquire a lock on the handle h +*/ +static void winceMutexAcquire(HANDLE h){ + DWORD dwErr; + do { + dwErr = osWaitForSingleObject(h, INFINITE); + } while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED); +} +/* +** Release a lock acquired by winceMutexAcquire() */ +#define winceMutexRelease(h) ReleaseMutex(h) /* -** Proxy locking is only available on MacOSX +** Create the mutex and shared memory used for locking in the file +** descriptor pFile */ -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE +static int winceCreateLock(const char *zFilename, winFile *pFile){ + LPWSTR zTok; + LPWSTR zName; + DWORD lastErrno; + BOOL bLogged = FALSE; + BOOL bInit = TRUE; + + zName = winUtf8ToUnicode(zFilename); + if( zName==0 ){ + /* out of memory */ + return SQLITE_IOERR_NOMEM_BKPT; + } + + /* Initialize the local lockdata */ + memset(&pFile->local, 0, sizeof(pFile->local)); + + /* Replace the backslashes from the filename and lowercase it + ** to derive a mutex name. */ + zTok = osCharLowerW(zName); + for (;*zTok;zTok++){ + if (*zTok == '\\') *zTok = '_'; + } + + /* Create/open the named mutex */ + pFile->hMutex = osCreateMutexW(NULL, FALSE, zName); + if (!pFile->hMutex){ + pFile->lastErrno = osGetLastError(); + sqlcipher_sqlite3_free(zName); + return winLogError(SQLITE_IOERR, pFile->lastErrno, + "winceCreateLock1", zFilename); + } + + /* Acquire the mutex before continuing */ + winceMutexAcquire(pFile->hMutex); + + /* Since the names of named mutexes, semaphores, file mappings etc are + ** case-sensitive, take advantage of that by uppercasing the mutex name + ** and using that as the shared filemapping name. + */ + osCharUpperW(zName); + pFile->hShared = osCreateFileMappingW(INVALID_HANDLE_VALUE, NULL, + PAGE_READWRITE, 0, sizeof(winceLock), + zName); + + /* Set a flag that indicates we're the first to create the memory so it + ** must be zero-initialized */ + lastErrno = osGetLastError(); + if (lastErrno == ERROR_ALREADY_EXISTS){ + bInit = FALSE; + } + + sqlcipher_sqlite3_free(zName); + + /* If we succeeded in making the shared memory handle, map it. */ + if( pFile->hShared ){ + pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared, + FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock)); + /* If mapping failed, close the shared memory handle and erase it */ + if( !pFile->shared ){ + pFile->lastErrno = osGetLastError(); + winLogError(SQLITE_IOERR, pFile->lastErrno, + "winceCreateLock2", zFilename); + bLogged = TRUE; + osCloseHandle(pFile->hShared); + pFile->hShared = NULL; + } + } + + /* If shared memory could not be created, then close the mutex and fail */ + if( pFile->hShared==NULL ){ + if( !bLogged ){ + pFile->lastErrno = lastErrno; + winLogError(SQLITE_IOERR, pFile->lastErrno, + "winceCreateLock3", zFilename); + bLogged = TRUE; + } + winceMutexRelease(pFile->hMutex); + osCloseHandle(pFile->hMutex); + pFile->hMutex = NULL; + return SQLITE_IOERR; + } + + /* Initialize the shared memory if we're supposed to */ + if( bInit ){ + memset(pFile->shared, 0, sizeof(winceLock)); + } + + winceMutexRelease(pFile->hMutex); + return SQLITE_OK; +} /* -** The proxyLockingContext has the path and file structures for the remote -** and local proxy files in it +** Destroy the part of winFile that deals with wince locks */ -typedef struct proxyLockingContext proxyLockingContext; -struct proxyLockingContext { - unixFile *conchFile; /* Open conch file */ - char *conchFilePath; /* Name of the conch file */ - unixFile *lockProxy; /* Open proxy lock file */ - char *lockProxyPath; /* Name of the proxy lock file */ - char *dbPath; /* Name of the open file */ - int conchHeld; /* 1 if the conch is held, -1 if lockless */ - int nFails; /* Number of conch taking failures */ - void *oldLockingContext; /* Original lockingcontext to restore on close */ - sqlcipher_sqlite3_io_methods const *pOldMethod; /* Original I/O methods for close */ -}; +static void winceDestroyLock(winFile *pFile){ + if (pFile->hMutex){ + /* Acquire the mutex */ + winceMutexAcquire(pFile->hMutex); + + /* The following blocks should probably assert in debug mode, but they + are to cleanup in case any locks remained open */ + if (pFile->local.nReaders){ + pFile->shared->nReaders --; + } + if (pFile->local.bReserved){ + pFile->shared->bReserved = FALSE; + } + if (pFile->local.bPending){ + pFile->shared->bPending = FALSE; + } + if (pFile->local.bExclusive){ + pFile->shared->bExclusive = FALSE; + } + + /* De-reference and close our copy of the shared memory handle */ + osUnmapViewOfFile(pFile->shared); + osCloseHandle(pFile->hShared); + + /* Done with the mutex */ + winceMutexRelease(pFile->hMutex); + osCloseHandle(pFile->hMutex); + pFile->hMutex = NULL; + } +} /* -** The proxy lock file path for the database at dbPath is written into lPath, -** which must point to valid, writable memory large enough for a maxLen length -** file path. +** An implementation of the LockFile() API of Windows for CE */ -static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){ - int len; - int dbLen; - int i; +static BOOL winceLockFile( + LPHANDLE phFile, + DWORD dwFileOffsetLow, + DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToLockLow, + DWORD nNumberOfBytesToLockHigh +){ + winFile *pFile = HANDLE_TO_WINFILE(phFile); + BOOL bReturn = FALSE; -#ifdef LOCKPROXYDIR - len = strlcpy(lPath, LOCKPROXYDIR, maxLen); -#else -# ifdef _CS_DARWIN_USER_TEMP_DIR - { - if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){ - OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n", - lPath, errno, osGetpid(0))); - return SQLITE_IOERR_LOCK; + UNUSED_PARAMETER(dwFileOffsetHigh); + UNUSED_PARAMETER(nNumberOfBytesToLockHigh); + + if (!pFile->hMutex) return TRUE; + winceMutexAcquire(pFile->hMutex); + + /* Wanting an exclusive lock? */ + if (dwFileOffsetLow == (DWORD)SHARED_FIRST + && nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){ + if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0){ + pFile->shared->bExclusive = TRUE; + pFile->local.bExclusive = TRUE; + bReturn = TRUE; } - len = strlcat(lPath, "sqliteplocks", maxLen); } -# else - len = strlcpy(lPath, "/tmp/", maxLen); -# endif -#endif - if( lPath[len-1]!='/' ){ - len = strlcat(lPath, "/", maxLen); + /* Want a read-only lock? */ + else if (dwFileOffsetLow == (DWORD)SHARED_FIRST && + nNumberOfBytesToLockLow == 1){ + if (pFile->shared->bExclusive == 0){ + pFile->local.nReaders ++; + if (pFile->local.nReaders == 1){ + pFile->shared->nReaders ++; + } + bReturn = TRUE; + } } - /* transform the db path to a unique cache name */ - dbLen = (int)strlen(dbPath); - for( i=0; ishared->bPending == 0) { + pFile->shared->bPending = TRUE; + pFile->local.bPending = TRUE; + bReturn = TRUE; + } } - lPath[i+len]='\0'; - strlcat(lPath, ":auto:", maxLen); - OSTRACE(("GETLOCKPATH proxy lock path=%s pid=%d\n", lPath, osGetpid(0))); - return SQLITE_OK; -} - -/* - ** Creates the lock file and any missing directories in lockPath - */ -static int proxyCreateLockPath(const char *lockPath){ - int i, len; - char buf[MAXPATHLEN]; - int start = 0; - assert(lockPath!=NULL); - /* try to create all the intermediate directories */ - len = (int)strlen(lockPath); - buf[0] = lockPath[0]; - for( i=1; i 0) ){ - /* only mkdir if leaf dir != "." or "/" or ".." */ - if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/') - || (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){ - buf[i]='\0'; - if( osMkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){ - int err=errno; - if( err!=EEXIST ) { - OSTRACE(("CREATELOCKPATH FAILED creating %s, " - "'%s' proxy lock path=%s pid=%d\n", - buf, strerror(err), lockPath, osGetpid(0))); - return err; - } - } - } - start=i+1; + /* Want a reserved lock? */ + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE + && nNumberOfBytesToLockLow == 1){ + if (pFile->shared->bReserved == 0) { + pFile->shared->bReserved = TRUE; + pFile->local.bReserved = TRUE; + bReturn = TRUE; } - buf[i] = lockPath[i]; } - OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n",lockPath,osGetpid(0))); - return 0; + + winceMutexRelease(pFile->hMutex); + return bReturn; } /* -** Create a new VFS file descriptor (stored in memory obtained from -** sqlcipher_sqlite3_malloc) and open the file named "path" in the file descriptor. -** -** The caller is responsible not only for closing the file descriptor -** but also for freeing the memory associated with the file descriptor. +** An implementation of the UnlockFile API of Windows for CE */ -static int proxyCreateUnixFile( - const char *path, /* path for the new unixFile */ - unixFile **ppFile, /* unixFile created and returned by ref */ - int islockfile /* if non zero missing dirs will be created */ -) { - int fd = -1; - unixFile *pNew; - int rc = SQLITE_OK; - int openFlags = O_RDWR | O_CREAT | O_NOFOLLOW; - sqlcipher_sqlite3_vfs dummyVfs; - int terrno = 0; - UnixUnusedFd *pUnused = NULL; +static BOOL winceUnlockFile( + LPHANDLE phFile, + DWORD dwFileOffsetLow, + DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToUnlockLow, + DWORD nNumberOfBytesToUnlockHigh +){ + winFile *pFile = HANDLE_TO_WINFILE(phFile); + BOOL bReturn = FALSE; - /* 1. first try to open/create the file - ** 2. if that fails, and this is a lock file (not-conch), try creating - ** the parent directories and then try again. - ** 3. if that fails, try to open the file read-only - ** otherwise return BUSY (if lock file) or CANTOPEN for the conch file - */ - pUnused = findReusableFd(path, openFlags); - if( pUnused ){ - fd = pUnused->fd; - }else{ - pUnused = sqlcipher_sqlite3_malloc64(sizeof(*pUnused)); - if( !pUnused ){ - return SQLITE_NOMEM_BKPT; + UNUSED_PARAMETER(dwFileOffsetHigh); + UNUSED_PARAMETER(nNumberOfBytesToUnlockHigh); + + if (!pFile->hMutex) return TRUE; + winceMutexAcquire(pFile->hMutex); + + /* Releasing a reader lock or an exclusive lock */ + if (dwFileOffsetLow == (DWORD)SHARED_FIRST){ + /* Did we have an exclusive lock? */ + if (pFile->local.bExclusive){ + assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE); + pFile->local.bExclusive = FALSE; + pFile->shared->bExclusive = FALSE; + bReturn = TRUE; } - } - if( fd<0 ){ - fd = robust_open(path, openFlags, 0); - terrno = errno; - if( fd<0 && errno==ENOENT && islockfile ){ - if( proxyCreateLockPath(path) == SQLITE_OK ){ - fd = robust_open(path, openFlags, 0); + + /* Did we just have a reader lock? */ + else if (pFile->local.nReaders){ + assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE + || nNumberOfBytesToUnlockLow == 1); + pFile->local.nReaders --; + if (pFile->local.nReaders == 0) + { + pFile->shared->nReaders --; } + bReturn = TRUE; } } - if( fd<0 ){ - openFlags = O_RDONLY | O_NOFOLLOW; - fd = robust_open(path, openFlags, 0); - terrno = errno; - } - if( fd<0 ){ - if( islockfile ){ - return SQLITE_BUSY; + + /* Releasing a pending lock */ + else if (dwFileOffsetLow == (DWORD)PENDING_BYTE + && nNumberOfBytesToUnlockLow == 1){ + if (pFile->local.bPending){ + pFile->local.bPending = FALSE; + pFile->shared->bPending = FALSE; + bReturn = TRUE; } - switch (terrno) { - case EACCES: - return SQLITE_PERM; - case EIO: - return SQLITE_IOERR_LOCK; /* even though it is the conch */ - default: - return SQLITE_CANTOPEN_BKPT; + } + /* Releasing a reserved lock */ + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE + && nNumberOfBytesToUnlockLow == 1){ + if (pFile->local.bReserved) { + pFile->local.bReserved = FALSE; + pFile->shared->bReserved = FALSE; + bReturn = TRUE; } } - pNew = (unixFile *)sqlcipher_sqlite3_malloc64(sizeof(*pNew)); - if( pNew==NULL ){ - rc = SQLITE_NOMEM_BKPT; - goto end_create_proxy; - } - memset(pNew, 0, sizeof(unixFile)); - pNew->openFlags = openFlags; - memset(&dummyVfs, 0, sizeof(dummyVfs)); - dummyVfs.pAppData = (void*)&autolockIoFinder; - dummyVfs.zName = "dummy"; - pUnused->fd = fd; - pUnused->flags = openFlags; - pNew->pPreallocatedUnused = pUnused; + winceMutexRelease(pFile->hMutex); + return bReturn; +} +/* +** End of the special code for wince +*****************************************************************************/ +#endif /* SQLITE_OS_WINCE */ - rc = fillInUnixFile(&dummyVfs, fd, (sqlcipher_sqlite3_file*)pNew, path, 0); - if( rc==SQLITE_OK ){ - *ppFile = pNew; - return SQLITE_OK; +/* +** Lock a file region. +*/ +static BOOL winLockFile( + LPHANDLE phFile, + DWORD flags, + DWORD offsetLow, + DWORD offsetHigh, + DWORD numBytesLow, + DWORD numBytesHigh +){ +#if SQLITE_OS_WINCE + /* + ** NOTE: Windows CE is handled differently here due its lack of the Win32 + ** API LockFile. + */ + return winceLockFile(phFile, offsetLow, offsetHigh, + numBytesLow, numBytesHigh); +#else + if( osIsNT() ){ + OVERLAPPED ovlp; + memset(&ovlp, 0, sizeof(OVERLAPPED)); + ovlp.Offset = offsetLow; + ovlp.OffsetHigh = offsetHigh; + return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp); + }else{ + return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow, + numBytesHigh); } -end_create_proxy: - robust_close(pNew, fd, __LINE__); - sqlcipher_sqlite3_free(pNew); - sqlcipher_sqlite3_free(pUnused); - return rc; +#endif } -#ifdef SQLITE_TEST -/* simulate multiple hosts by creating unique hostid file paths */ -SQLITE_API int sqlcipher_sqlite3_hostid_num = 0; +/* +** Unlock a file region. + */ +static BOOL winUnlockFile( + LPHANDLE phFile, + DWORD offsetLow, + DWORD offsetHigh, + DWORD numBytesLow, + DWORD numBytesHigh +){ +#if SQLITE_OS_WINCE + /* + ** NOTE: Windows CE is handled differently here due its lack of the Win32 + ** API UnlockFile. + */ + return winceUnlockFile(phFile, offsetLow, offsetHigh, + numBytesLow, numBytesHigh); +#else + if( osIsNT() ){ + OVERLAPPED ovlp; + memset(&ovlp, 0, sizeof(OVERLAPPED)); + ovlp.Offset = offsetLow; + ovlp.OffsetHigh = offsetHigh; + return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp); + }else{ + return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow, + numBytesHigh); + } #endif +} -#define PROXY_HOSTIDLEN 16 /* conch file host id length */ +/***************************************************************************** +** The next group of routines implement the I/O methods specified +** by the sqlcipher_sqlite3_io_methods object. +******************************************************************************/ -#if HAVE_GETHOSTUUID -/* Not always defined in the headers as it ought to be */ -extern int gethostuuid(uuid_t id, const struct timespec *wait); +/* +** Some Microsoft compilers lack this definition. +*/ +#ifndef INVALID_SET_FILE_POINTER +# define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif -/* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN -** bytes of writable memory. +/* +** Move the current position of the file handle passed as the first +** argument to offset iOffset within the file. If successful, return 0. +** Otherwise, set pFile->lastErrno and return non-zero. */ -static int proxyGetHostID(unsigned char *pHostID, int *pError){ - assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); - memset(pHostID, 0, PROXY_HOSTIDLEN); -#if HAVE_GETHOSTUUID - { - struct timespec timeout = {1, 0}; /* 1 sec timeout */ - if( gethostuuid(pHostID, &timeout) ){ - int err = errno; - if( pError ){ - *pError = err; - } - return SQLITE_IOERR; - } +static int winSeekFile(winFile *pFile, sqlcipher_sqlite3_int64 iOffset){ +#if !SQLITE_OS_WINRT + LONG upperBits; /* Most sig. 32 bits of new offset */ + LONG lowerBits; /* Least sig. 32 bits of new offset */ + DWORD dwRet; /* Value returned by SetFilePointer() */ + DWORD lastErrno; /* Value returned by GetLastError() */ + + OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset)); + + upperBits = (LONG)((iOffset>>32) & 0x7fffffff); + lowerBits = (LONG)(iOffset & 0xffffffff); + + /* API oddity: If successful, SetFilePointer() returns a dword + ** containing the lower 32-bits of the new file-offset. Or, if it fails, + ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, + ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine + ** whether an error has actually occurred, it is also necessary to call + ** GetLastError(). + */ + dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); + + if( (dwRet==INVALID_SET_FILE_POINTER + && ((lastErrno = osGetLastError())!=NO_ERROR)) ){ + pFile->lastErrno = lastErrno; + winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, + "winSeekFile", pFile->zPath); + OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); + return 1; } + + OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); + return 0; #else - UNUSED_PARAMETER(pError); -#endif -#ifdef SQLITE_TEST - /* simulate multiple hosts by creating unique hostid file paths */ - if( sqlcipher_sqlite3_hostid_num != 0){ - pHostID[0] = (char)(pHostID[0] + (char)(sqlcipher_sqlite3_hostid_num & 0xFF)); + /* + ** Same as above, except that this implementation works for WinRT. + */ + + LARGE_INTEGER x; /* The new offset */ + BOOL bRet; /* Value returned by SetFilePointerEx() */ + + x.QuadPart = iOffset; + bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN); + + if(!bRet){ + pFile->lastErrno = osGetLastError(); + winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, + "winSeekFile", pFile->zPath); + OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); + return 1; } -#endif - return SQLITE_OK; + OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); + return 0; +#endif } -/* The conch file contains the header, host id and lock file path - */ -#define PROXY_CONCHVERSION 2 /* 1-byte header, 16-byte host id, path */ -#define PROXY_HEADERLEN 1 /* conch file header length */ -#define PROXY_PATHINDEX (PROXY_HEADERLEN+PROXY_HOSTIDLEN) -#define PROXY_MAXCONCHLEN (PROXY_HEADERLEN+PROXY_HOSTIDLEN+MAXPATHLEN) +#if SQLITE_MAX_MMAP_SIZE>0 +/* Forward references to VFS helper methods used for memory mapped files */ +static int winMapfile(winFile*, sqlcipher_sqlite3_int64); +static int winUnmapfile(winFile*); +#endif /* -** Takes an open conch file, copies the contents to a new path and then moves -** it back. The newly created file's file descriptor is assigned to the -** conch file structure and finally the original conch file descriptor is -** closed. Returns zero if successful. +** Close a file. +** +** It is reported that an attempt to close a handle might sometimes +** fail. This is a very unreasonable result, but Windows is notorious +** for being unreasonable so I do not doubt that it might happen. If +** the close fails, we pause for 100 milliseconds and try again. As +** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before +** giving up and returning an error. */ -static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - unixFile *conchFile = pCtx->conchFile; - char tPath[MAXPATHLEN]; - char buf[PROXY_MAXCONCHLEN]; - char *cPath = pCtx->conchFilePath; - size_t readLen = 0; - size_t pathLen = 0; - char errmsg[64] = ""; - int fd = -1; - int rc = -1; - UNUSED_PARAMETER(myHostID); +#define MX_CLOSE_ATTEMPT 3 +static int winClose(sqlcipher_sqlite3_file *id){ + int rc, cnt = 0; + winFile *pFile = (winFile*)id; - /* create a new path by replace the trailing '-conch' with '-break' */ - pathLen = strlcpy(tPath, cPath, MAXPATHLEN); - if( pathLen>MAXPATHLEN || pathLen<6 || - (strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){ - sqlcipher_sqlite3_snprintf(sizeof(errmsg),errmsg,"path error (len %d)",(int)pathLen); - goto end_breaklock; - } - /* read the conch content */ - readLen = osPread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); - if( readLenpShm==0 ); +#endif + assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE ); + OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p\n", + osGetCurrentProcessId(), pFile, pFile->h)); + +#if SQLITE_MAX_MMAP_SIZE>0 + winUnmapfile(pFile); +#endif + + do{ + rc = osCloseHandle(pFile->h); + /* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */ + }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (sqlcipher_sqlite3_win32_sleep(100), 1) ); +#if SQLITE_OS_WINCE +#define WINCE_DELETION_ATTEMPTS 3 + { + winVfsAppData *pAppData = (winVfsAppData*)pFile->pVfs->pAppData; + if( pAppData==NULL || !pAppData->bNoLock ){ + winceDestroyLock(pFile); + } } - if( rename(tPath, cPath) ){ - sqlcipher_sqlite3_snprintf(sizeof(errmsg), errmsg, "rename failed (%d)", errno); - goto end_breaklock; + if( pFile->zDeleteOnClose ){ + int cnt = 0; + while( + osDeleteFileW(pFile->zDeleteOnClose)==0 + && osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff + && cnt++ < WINCE_DELETION_ATTEMPTS + ){ + sqlcipher_sqlite3_win32_sleep(100); /* Wait a little before trying again */ + } + sqlcipher_sqlite3_free(pFile->zDeleteOnClose); } - rc = 0; - fprintf(stderr, "broke stale lock on %s\n", cPath); - robust_close(pFile, conchFile->h, __LINE__); - conchFile->h = fd; - conchFile->openFlags = O_RDWR | O_CREAT; - -end_breaklock: +#endif if( rc ){ - if( fd>=0 ){ - osUnlink(tPath); - robust_close(pFile, fd, __LINE__); - } - fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg); + pFile->h = NULL; } - return rc; + OpenCounter(-1); + OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p, rc=%s\n", + osGetCurrentProcessId(), pFile, pFile->h, rc ? "ok" : "failed")); + return rc ? SQLITE_OK + : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(), + "winClose", pFile->zPath); } -/* Take the requested lock on the conch file and break a stale lock if the -** host id matches. +/* +** Read data from a file into a buffer. Return SQLITE_OK if all +** bytes were read successfully and SQLITE_IOERR if anything goes +** wrong. */ -static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - unixFile *conchFile = pCtx->conchFile; - int rc = SQLITE_OK; - int nTries = 0; - struct timespec conchModTime; - - memset(&conchModTime, 0, sizeof(conchModTime)); - do { - rc = conchFile->pMethod->xLock((sqlcipher_sqlite3_file*)conchFile, lockType); - nTries ++; - if( rc==SQLITE_BUSY ){ - /* If the lock failed (busy): - * 1st try: get the mod time of the conch, wait 0.5s and try again. - * 2nd try: fail if the mod time changed or host id is different, wait - * 10 sec and try again - * 3rd try: break the lock unless the mod time has changed. - */ - struct stat buf; - if( osFstat(conchFile->h, &buf) ){ - storeLastErrno(pFile, errno); - return SQLITE_IOERR_LOCK; - } - - if( nTries==1 ){ - conchModTime = buf.st_mtimespec; - unixSleep(0,500000); /* wait 0.5 sec and try the lock again*/ - continue; - } - - assert( nTries>1 ); - if( conchModTime.tv_sec != buf.st_mtimespec.tv_sec || - conchModTime.tv_nsec != buf.st_mtimespec.tv_nsec ){ - return SQLITE_BUSY; - } +static int winRead( + sqlcipher_sqlite3_file *id, /* File to read from */ + void *pBuf, /* Write content into this buffer */ + int amt, /* Number of bytes to read */ + sqlcipher_sqlite3_int64 offset /* Begin reading at this offset */ +){ +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) + OVERLAPPED overlapped; /* The offset for ReadFile. */ +#endif + winFile *pFile = (winFile*)id; /* file handle */ + DWORD nRead; /* Number of bytes actually read from file */ + int nRetry = 0; /* Number of retrys */ - if( nTries==2 ){ - char tBuf[PROXY_MAXCONCHLEN]; - int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); - if( len<0 ){ - storeLastErrno(pFile, errno); - return SQLITE_IOERR_LOCK; - } - if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){ - /* don't break the lock if the host id doesn't match */ - if( 0!=memcmp(&tBuf[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN) ){ - return SQLITE_BUSY; - } - }else{ - /* don't break the lock on short read or a version mismatch */ - return SQLITE_BUSY; - } - unixSleep(0,10000000); /* wait 10 sec and try the lock again */ - continue; - } + assert( id!=0 ); + assert( amt>0 ); + assert( offset>=0 ); + SimulateIOError(return SQLITE_IOERR_READ); + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, " + "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile, + pFile->h, pBuf, amt, offset, pFile->locktype)); - assert( nTries==3 ); - if( 0==proxyBreakConchLock(pFile, myHostID) ){ - rc = SQLITE_OK; - if( lockType==EXCLUSIVE_LOCK ){ - rc = conchFile->pMethod->xLock((sqlcipher_sqlite3_file*)conchFile, SHARED_LOCK); - } - if( !rc ){ - rc = conchFile->pMethod->xLock((sqlcipher_sqlite3_file*)conchFile, lockType); - } - } +#if SQLITE_MAX_MMAP_SIZE>0 + /* Deal with as much of this read request as possible by transfering + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); + OSTRACE(("READ-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return SQLITE_OK; + }else{ + int nCopy = (int)(pFile->mmapSize - offset); + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); + pBuf = &((u8 *)pBuf)[nCopy]; + amt -= nCopy; + offset += nCopy; } - } while( rc==SQLITE_BUSY && nTries<3 ); + } +#endif - return rc; +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) + if( winSeekFile(pFile, offset) ){ + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return SQLITE_FULL; + } + while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ +#else + memset(&overlapped, 0, sizeof(OVERLAPPED)); + overlapped.Offset = (LONG)(offset & 0xffffffff); + overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); + while( !osReadFile(pFile->h, pBuf, amt, &nRead, &overlapped) && + osGetLastError()!=ERROR_HANDLE_EOF ){ +#endif + DWORD lastErrno; + if( winRetryIoerr(&nRetry, &lastErrno) ) continue; + pFile->lastErrno = lastErrno; + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_READ\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return winLogError(SQLITE_IOERR_READ, pFile->lastErrno, + "winRead", pFile->zPath); + } + winLogIoerr(nRetry, __LINE__); + if( nRead<(DWORD)amt ){ + /* Unread parts of the buffer must be zero-filled */ + memset(&((char*)pBuf)[nRead], 0, amt-nRead); + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_SHORT_READ\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return SQLITE_IOERR_SHORT_READ; + } + + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return SQLITE_OK; } -/* Takes the conch by taking a shared lock and read the contents conch, if -** lockPath is non-NULL, the host ID and lock file path must match. A NULL -** lockPath means that the lockPath in the conch file will be used if the -** host IDs match, or a new lock path will be generated automatically -** and written to the conch file. +/* +** Write data from a buffer into a file. Return SQLITE_OK on success +** or some other error code on failure. */ -static int proxyTakeConch(unixFile *pFile){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; +static int winWrite( + sqlcipher_sqlite3_file *id, /* File to write into */ + const void *pBuf, /* The bytes to be written */ + int amt, /* Number of bytes to write */ + sqlcipher_sqlite3_int64 offset /* Offset into the file to begin writing at */ +){ + int rc = 0; /* True if error has occurred, else false */ + winFile *pFile = (winFile*)id; /* File handle */ + int nRetry = 0; /* Number of retries */ - if( pCtx->conchHeld!=0 ){ - return SQLITE_OK; - }else{ - unixFile *conchFile = pCtx->conchFile; - uuid_t myHostID; - int pError = 0; - char readBuf[PROXY_MAXCONCHLEN]; - char lockPath[MAXPATHLEN]; - char *tempLockPath = NULL; - int rc = SQLITE_OK; - int createConch = 0; - int hostIdMatch = 0; - int readLen = 0; - int tryOldLockPath = 0; - int forceNewLockPath = 0; + assert( amt>0 ); + assert( pFile ); + SimulateIOError(return SQLITE_IOERR_WRITE); + SimulateDiskfullError(return SQLITE_FULL); - OSTRACE(("TAKECONCH %d for %s pid=%d\n", conchFile->h, - (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), - osGetpid(0))); + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, " + "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile, + pFile->h, pBuf, amt, offset, pFile->locktype)); - rc = proxyGetHostID(myHostID, &pError); - if( (rc&0xff)==SQLITE_IOERR ){ - storeLastErrno(pFile, pError); - goto end_takeconch; - } - rc = proxyConchLock(pFile, myHostID, SHARED_LOCK); - if( rc!=SQLITE_OK ){ - goto end_takeconch; - } - /* read the existing conch file */ - readLen = seekAndRead((unixFile*)conchFile, 0, readBuf, PROXY_MAXCONCHLEN); - if( readLen<0 ){ - /* I/O error: lastErrno set by seekAndRead */ - storeLastErrno(pFile, conchFile->lastErrno); - rc = SQLITE_IOERR_READ; - goto end_takeconch; - }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) || - readBuf[0]!=(char)PROXY_CONCHVERSION ){ - /* a short read or version format mismatch means we need to create a new - ** conch file. - */ - createConch = 1; +#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 + /* Deal with as much of this write request as possible by transfering + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); + OSTRACE(("WRITE-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return SQLITE_OK; + }else{ + int nCopy = (int)(pFile->mmapSize - offset); + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy); + pBuf = &((u8 *)pBuf)[nCopy]; + amt -= nCopy; + offset += nCopy; } - /* if the host id matches and the lock path already exists in the conch - ** we'll try to use the path there, if we can't open that path, we'll - ** retry with a new auto-generated path - */ - do { /* in case we need to try again for an :auto: named lock file */ - - if( !createConch && !forceNewLockPath ){ - hostIdMatch = !memcmp(&readBuf[PROXY_HEADERLEN], myHostID, - PROXY_HOSTIDLEN); - /* if the conch has data compare the contents */ - if( !pCtx->lockProxyPath ){ - /* for auto-named local lock file, just check the host ID and we'll - ** use the local lock file path that's already in there - */ - if( hostIdMatch ){ - size_t pathLen = (readLen - PROXY_PATHINDEX); - - if( pathLen>=MAXPATHLEN ){ - pathLen=MAXPATHLEN-1; - } - memcpy(lockPath, &readBuf[PROXY_PATHINDEX], pathLen); - lockPath[pathLen] = 0; - tempLockPath = lockPath; - tryOldLockPath = 1; - /* create a copy of the lock path if the conch is taken */ - goto end_takeconch; - } - }else if( hostIdMatch - && !strncmp(pCtx->lockProxyPath, &readBuf[PROXY_PATHINDEX], - readLen-PROXY_PATHINDEX) - ){ - /* conch host and lock path match */ - goto end_takeconch; - } - } - - /* if the conch isn't writable and doesn't match, we can't take it */ - if( (conchFile->openFlags&O_RDWR) == 0 ){ - rc = SQLITE_BUSY; - goto end_takeconch; - } + } +#endif - /* either the conch didn't match or we need to create a new one */ - if( !pCtx->lockProxyPath ){ - proxyGetLockPath(pCtx->dbPath, lockPath, MAXPATHLEN); - tempLockPath = lockPath; - /* create a copy of the lock path _only_ if the conch is taken */ - } +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) + rc = winSeekFile(pFile, offset); + if( rc==0 ){ +#else + { +#endif +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) + OVERLAPPED overlapped; /* The offset for WriteFile. */ +#endif + u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ + int nRem = amt; /* Number of bytes yet to be written */ + DWORD nWrite; /* Bytes written by each WriteFile() call */ + DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */ - /* update conch with host and path (this will fail if other process - ** has a shared lock already), if the host id matches, use the big - ** stick. - */ - futimes(conchFile->h, NULL); - if( hostIdMatch && !createConch ){ - if( conchFile->pInode && conchFile->pInode->nShared>1 ){ - /* We are trying for an exclusive lock but another thread in this - ** same process is still holding a shared lock. */ - rc = SQLITE_BUSY; - } else { - rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); - } - }else{ - rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); - } - if( rc==SQLITE_OK ){ - char writeBuffer[PROXY_MAXCONCHLEN]; - int writeSize = 0; +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) + memset(&overlapped, 0, sizeof(OVERLAPPED)); + overlapped.Offset = (LONG)(offset & 0xffffffff); + overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); +#endif - writeBuffer[0] = (char)PROXY_CONCHVERSION; - memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN); - if( pCtx->lockProxyPath!=NULL ){ - strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, - MAXPATHLEN); - }else{ - strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN); - } - writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]); - robust_ftruncate(conchFile->h, writeSize); - rc = unixWrite((sqlcipher_sqlite3_file *)conchFile, writeBuffer, writeSize, 0); - full_fsync(conchFile->h,0,0); - /* If we created a new conch file (not just updated the contents of a - ** valid conch file), try to match the permissions of the database - */ - if( rc==SQLITE_OK && createConch ){ - struct stat buf; - int err = osFstat(pFile->h, &buf); - if( err==0 ){ - mode_t cmode = buf.st_mode&(S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | - S_IROTH|S_IWOTH); - /* try to match the database file R/W permissions, ignore failure */ -#ifndef SQLITE_PROXY_DEBUG - osFchmod(conchFile->h, cmode); + while( nRem>0 ){ +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) + if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ #else - do{ - rc = osFchmod(conchFile->h, cmode); - }while( rc==(-1) && errno==EINTR ); - if( rc!=0 ){ - int code = errno; - fprintf(stderr, "fchmod %o FAILED with %d %s\n", - cmode, code, strerror(code)); - } else { - fprintf(stderr, "fchmod %o SUCCEDED\n",cmode); - } - }else{ - int code = errno; - fprintf(stderr, "STAT FAILED[%d] with %d %s\n", - err, code, strerror(code)); + if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){ #endif - } - } - } - conchFile->pMethod->xUnlock((sqlcipher_sqlite3_file*)conchFile, SHARED_LOCK); - - end_takeconch: - OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h)); - if( rc==SQLITE_OK && pFile->openFlags ){ - int fd; - if( pFile->h>=0 ){ - robust_close(pFile, pFile->h, __LINE__); - } - pFile->h = -1; - fd = robust_open(pCtx->dbPath, pFile->openFlags, 0); - OSTRACE(("TRANSPROXY: OPEN %d\n", fd)); - if( fd>=0 ){ - pFile->h = fd; - }else{ - rc=SQLITE_CANTOPEN_BKPT; /* SQLITE_BUSY? proxyTakeConch called - during locking */ - } - } - if( rc==SQLITE_OK && !pCtx->lockProxy ){ - char *path = tempLockPath ? tempLockPath : pCtx->lockProxyPath; - rc = proxyCreateUnixFile(path, &pCtx->lockProxy, 1); - if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && tryOldLockPath ){ - /* we couldn't create the proxy lock file with the old lock file path - ** so try again via auto-naming - */ - forceNewLockPath = 1; - tryOldLockPath = 0; - continue; /* go back to the do {} while start point, try again */ - } + if( winRetryIoerr(&nRetry, &lastErrno) ) continue; + break; } - if( rc==SQLITE_OK ){ - /* Need to make a copy of path if we extracted the value - ** from the conch file or the path was allocated on the stack - */ - if( tempLockPath ){ - pCtx->lockProxyPath = sqlcipher_sqlite3DbStrDup(0, tempLockPath); - if( !pCtx->lockProxyPath ){ - rc = SQLITE_NOMEM_BKPT; - } - } + assert( nWrite==0 || nWrite<=(DWORD)nRem ); + if( nWrite==0 || nWrite>(DWORD)nRem ){ + lastErrno = osGetLastError(); + break; } - if( rc==SQLITE_OK ){ - pCtx->conchHeld = 1; +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) + offset += nWrite; + overlapped.Offset = (LONG)(offset & 0xffffffff); + overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); +#endif + aRem += nWrite; + nRem -= nWrite; + } + if( nRem>0 ){ + pFile->lastErrno = lastErrno; + rc = 1; + } + } - if( pCtx->lockProxy->pMethod == &afpIoMethods ){ - afpLockingContext *afpCtx; - afpCtx = (afpLockingContext *)pCtx->lockProxy->lockingContext; - afpCtx->dbPath = pCtx->lockProxyPath; - } - } else { - conchFile->pMethod->xUnlock((sqlcipher_sqlite3_file*)conchFile, NO_LOCK); - } - OSTRACE(("TAKECONCH %d %s\n", conchFile->h, - rc==SQLITE_OK?"ok":"failed")); - return rc; - } while (1); /* in case we need to retry the :auto: lock file - - ** we should never get here except via the 'continue' call. */ + if( rc ){ + if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ) + || ( pFile->lastErrno==ERROR_DISK_FULL )){ + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return winLogError(SQLITE_FULL, pFile->lastErrno, + "winWrite1", pFile->zPath); + } + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_WRITE\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno, + "winWrite2", pFile->zPath); + }else{ + winLogIoerr(nRetry, __LINE__); } + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return SQLITE_OK; } /* -** If pFile holds a lock on a conch file, then release that lock. +** Truncate an open file to a specified size */ -static int proxyReleaseConch(unixFile *pFile){ - int rc = SQLITE_OK; /* Subroutine return code */ - proxyLockingContext *pCtx; /* The locking context for the proxy lock */ - unixFile *conchFile; /* Name of the conch file */ - - pCtx = (proxyLockingContext *)pFile->lockingContext; - conchFile = pCtx->conchFile; - OSTRACE(("RELEASECONCH %d for %s pid=%d\n", conchFile->h, - (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), - osGetpid(0))); - if( pCtx->conchHeld>0 ){ - rc = conchFile->pMethod->xUnlock((sqlcipher_sqlite3_file*)conchFile, NO_LOCK); +static int winTruncate(sqlcipher_sqlite3_file *id, sqlcipher_sqlite3_int64 nByte){ + winFile *pFile = (winFile*)id; /* File handle object */ + int rc = SQLITE_OK; /* Return code for this function */ + DWORD lastErrno; +#if SQLITE_MAX_MMAP_SIZE>0 + sqlcipher_sqlite3_int64 oldMmapSize; + if( pFile->nFetchOut>0 ){ + /* File truncation is a no-op if there are outstanding memory mapped + ** pages. This is because truncating the file means temporarily unmapping + ** the file, and that might delete memory out from under existing cursors. + ** + ** This can result in incremental vacuum not truncating the file, + ** if there is an active read cursor when the incremental vacuum occurs. + ** No real harm comes of this - the database file is not corrupted, + ** though some folks might complain that the file is bigger than it + ** needs to be. + ** + ** The only feasible work-around is to defer the truncation until after + ** all references to memory-mapped content are closed. That is doable, + ** but involves adding a few branches in the common write code path which + ** could slow down normal operations slightly. Hence, we have decided for + ** now to simply make trancations a no-op if there are pending reads. We + ** can maybe revisit this decision in the future. + */ + return SQLITE_OK; } - pCtx->conchHeld = 0; - OSTRACE(("RELEASECONCH %d %s\n", conchFile->h, - (rc==SQLITE_OK ? "ok" : "failed"))); - return rc; -} +#endif -/* -** Given the name of a database file, compute the name of its conch file. -** Store the conch filename in memory obtained from sqlcipher_sqlite3_malloc64(). -** Make *pConchPath point to the new name. Return SQLITE_OK on success -** or SQLITE_NOMEM if unable to obtain memory. -** -** The caller is responsible for ensuring that the allocated memory -** space is eventually freed. -** -** *pConchPath is set to NULL if a memory allocation error occurs. -*/ -static int proxyCreateConchPathname(char *dbPath, char **pConchPath){ - int i; /* Loop counter */ - int len = (int)strlen(dbPath); /* Length of database filename - dbPath */ - char *conchPath; /* buffer in which to construct conch name */ + assert( pFile ); + SimulateIOError(return SQLITE_IOERR_TRUNCATE); + OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n", + osGetCurrentProcessId(), pFile, pFile->h, nByte, pFile->locktype)); - /* Allocate space for the conch filename and initialize the name to - ** the name of the original database file. */ - *pConchPath = conchPath = (char *)sqlcipher_sqlite3_malloc64(len + 8); - if( conchPath==0 ){ - return SQLITE_NOMEM_BKPT; + /* If the user has configured a chunk-size for this file, truncate the + ** file so that it consists of an integer number of chunks (i.e. the + ** actual file size after the operation may be larger than the requested + ** size). + */ + if( pFile->szChunk>0 ){ + nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; } - memcpy(conchPath, dbPath, len+1); - /* now insert a "." before the last / character */ - for( i=(len-1); i>=0; i-- ){ - if( conchPath[i]=='/' ){ - i++; - break; - } - } - conchPath[i]='.'; - while ( i0 + if( pFile->pMapRegion ){ + oldMmapSize = pFile->mmapSize; + }else{ + oldMmapSize = 0; } + winUnmapfile(pFile); +#endif - /* append the "-conch" suffix to the file */ - memcpy(&conchPath[i+1], "-conch", 7); - assert( (int)strlen(conchPath) == len+7 ); - - return SQLITE_OK; -} - - -/* Takes a fully configured proxy locking-style unix file and switches -** the local lock file path -*/ -static int switchLockProxyPath(unixFile *pFile, const char *path) { - proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; - char *oldPath = pCtx->lockProxyPath; - int rc = SQLITE_OK; - - if( pFile->eFileLock!=NO_LOCK ){ - return SQLITE_BUSY; + /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ + if( winSeekFile(pFile, nByte) ){ + rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, + "winTruncate1", pFile->zPath); + }else if( 0==osSetEndOfFile(pFile->h) && + ((lastErrno = osGetLastError())!=ERROR_USER_MAPPED_FILE) ){ + pFile->lastErrno = lastErrno; + rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, + "winTruncate2", pFile->zPath); } - /* nothing to do if the path is NULL, :auto: or matches the existing path */ - if( !path || path[0]=='\0' || !strcmp(path, ":auto:") || - (oldPath && !strncmp(oldPath, path, MAXPATHLEN)) ){ - return SQLITE_OK; - }else{ - unixFile *lockProxy = pCtx->lockProxy; - pCtx->lockProxy=NULL; - pCtx->conchHeld = 0; - if( lockProxy!=NULL ){ - rc=lockProxy->pMethod->xClose((sqlcipher_sqlite3_file *)lockProxy); - if( rc ) return rc; - sqlcipher_sqlite3_free(lockProxy); +#if SQLITE_MAX_MMAP_SIZE>0 + if( rc==SQLITE_OK && oldMmapSize>0 ){ + if( oldMmapSize>nByte ){ + winMapfile(pFile, -1); + }else{ + winMapfile(pFile, oldMmapSize); } - sqlcipher_sqlite3_free(oldPath); - pCtx->lockProxyPath = sqlcipher_sqlite3DbStrDup(0, path); } +#endif + OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, rc=%s\n", + osGetCurrentProcessId(), pFile, pFile->h, sqlcipher_sqlite3ErrName(rc))); return rc; } +#ifdef SQLITE_TEST /* -** pFile is a file that has been opened by a prior xOpen call. dbPath -** is a string buffer at least MAXPATHLEN+1 characters in size. -** -** This routine find the filename associated with pFile and writes it -** int dbPath. +** Count the number of fullsyncs and normal syncs. This is used to test +** that syncs and fullsyncs are occuring at the right times. */ -static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){ -#if defined(__APPLE__) - if( pFile->pMethod == &afpIoMethods ){ - /* afp style keeps a reference to the db path in the filePath field - ** of the struct */ - assert( (int)strlen((char*)pFile->lockingContext)<=MAXPATHLEN ); - strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, - MAXPATHLEN); - } else +SQLITE_API int sqlcipher_sqlite3_sync_count = 0; +SQLITE_API int sqlcipher_sqlite3_fullsync_count = 0; #endif - if( pFile->pMethod == &dotlockIoMethods ){ - /* dot lock style uses the locking context to store the dot lock - ** file path */ - int len = strlen((char *)pFile->lockingContext) - strlen(DOTLOCK_SUFFIX); - memcpy(dbPath, (char *)pFile->lockingContext, len + 1); - }else{ - /* all other styles use the locking context to store the db file path */ - assert( strlen((char*)pFile->lockingContext)<=MAXPATHLEN ); - strlcpy(dbPath, (char *)pFile->lockingContext, MAXPATHLEN); - } - return SQLITE_OK; -} /* -** Takes an already filled in unix file and alters it so all file locking -** will be performed on the local proxy lock file. The following fields -** are preserved in the locking context so that they can be restored and -** the unix structure properly cleaned up at close time: -** ->lockingContext -** ->pMethod +** Make sure all writes to a particular file are committed to disk. */ -static int proxyTransformUnixFile(unixFile *pFile, const char *path) { - proxyLockingContext *pCtx; - char dbPath[MAXPATHLEN+1]; /* Name of the database file */ - char *lockPath=NULL; - int rc = SQLITE_OK; +static int winSync(sqlcipher_sqlite3_file *id, int flags){ +#ifndef SQLITE_NO_SYNC + /* + ** Used only when SQLITE_NO_SYNC is not defined. + */ + BOOL rc; +#endif +#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \ + defined(SQLITE_HAVE_OS_TRACE) + /* + ** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or + ** OSTRACE() macros. + */ + winFile *pFile = (winFile*)id; +#else + UNUSED_PARAMETER(id); +#endif - if( pFile->eFileLock!=NO_LOCK ){ - return SQLITE_BUSY; - } - proxyGetDbPathForUnixFile(pFile, dbPath); - if( !path || path[0]=='\0' || !strcmp(path, ":auto:") ){ - lockPath=NULL; - }else{ - lockPath=(char *)path; - } + assert( pFile ); + /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */ + assert((flags&0x0F)==SQLITE_SYNC_NORMAL + || (flags&0x0F)==SQLITE_SYNC_FULL + ); - OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h, - (lockPath ? lockPath : ":auto:"), osGetpid(0))); + /* Unix cannot, but some systems may return SQLITE_FULL from here. This + ** line is to test that doing so does not cause any problems. + */ + SimulateDiskfullError( return SQLITE_FULL ); - pCtx = sqlcipher_sqlite3_malloc64( sizeof(*pCtx) ); - if( pCtx==0 ){ - return SQLITE_NOMEM_BKPT; - } - memset(pCtx, 0, sizeof(*pCtx)); + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, flags=%x, lock=%d\n", + osGetCurrentProcessId(), pFile, pFile->h, flags, + pFile->locktype)); - rc = proxyCreateConchPathname(dbPath, &pCtx->conchFilePath); - if( rc==SQLITE_OK ){ - rc = proxyCreateUnixFile(pCtx->conchFilePath, &pCtx->conchFile, 0); - if( rc==SQLITE_CANTOPEN && ((pFile->openFlags&O_RDWR) == 0) ){ - /* if (a) the open flags are not O_RDWR, (b) the conch isn't there, and - ** (c) the file system is read-only, then enable no-locking access. - ** Ugh, since O_RDONLY==0x0000 we test for !O_RDWR since unixOpen asserts - ** that openFlags will have only one of O_RDONLY or O_RDWR. - */ - struct statfs fsInfo; - struct stat conchInfo; - int goLockless = 0; +#ifndef SQLITE_TEST + UNUSED_PARAMETER(flags); +#else + if( (flags&0x0F)==SQLITE_SYNC_FULL ){ + sqlcipher_sqlite3_fullsync_count++; + } + sqlcipher_sqlite3_sync_count++; +#endif - if( osStat(pCtx->conchFilePath, &conchInfo) == -1 ) { - int err = errno; - if( (err==ENOENT) && (statfs(dbPath, &fsInfo) != -1) ){ - goLockless = (fsInfo.f_flags&MNT_RDONLY) == MNT_RDONLY; - } - } - if( goLockless ){ - pCtx->conchHeld = -1; /* read only FS/ lockless */ - rc = SQLITE_OK; - } + /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a + ** no-op + */ +#ifdef SQLITE_NO_SYNC + OSTRACE(("SYNC-NOP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return SQLITE_OK; +#else +#if SQLITE_MAX_MMAP_SIZE>0 + if( pFile->pMapRegion ){ + if( osFlushViewOfFile(pFile->pMapRegion, 0) ){ + OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, " + "rc=SQLITE_OK\n", osGetCurrentProcessId(), + pFile, pFile->pMapRegion)); + }else{ + pFile->lastErrno = osGetLastError(); + OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, " + "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), + pFile, pFile->pMapRegion)); + return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, + "winSync1", pFile->zPath); } } - if( rc==SQLITE_OK && lockPath ){ - pCtx->lockProxyPath = sqlcipher_sqlite3DbStrDup(0, lockPath); +#endif + rc = osFlushFileBuffers(pFile->h); + SimulateIOError( rc=FALSE ); + if( rc ){ + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return SQLITE_OK; + }else{ + pFile->lastErrno = osGetLastError(); + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_FSYNC\n", + osGetCurrentProcessId(), pFile, pFile->h)); + return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno, + "winSync2", pFile->zPath); } +#endif +} - if( rc==SQLITE_OK ){ - pCtx->dbPath = sqlcipher_sqlite3DbStrDup(0, dbPath); - if( pCtx->dbPath==NULL ){ - rc = SQLITE_NOMEM_BKPT; +/* +** Determine the current size of a file in bytes +*/ +static int winFileSize(sqlcipher_sqlite3_file *id, sqlcipher_sqlite3_int64 *pSize){ + winFile *pFile = (winFile*)id; + int rc = SQLITE_OK; + + assert( id!=0 ); + assert( pSize!=0 ); + SimulateIOError(return SQLITE_IOERR_FSTAT); + OSTRACE(("SIZE file=%p, pSize=%p\n", pFile->h, pSize)); + +#if SQLITE_OS_WINRT + { + FILE_STANDARD_INFO info; + if( osGetFileInformationByHandleEx(pFile->h, FileStandardInfo, + &info, sizeof(info)) ){ + *pSize = info.EndOfFile.QuadPart; + }else{ + pFile->lastErrno = osGetLastError(); + rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno, + "winFileSize", pFile->zPath); } } - if( rc==SQLITE_OK ){ - /* all memory is allocated, proxys are created and assigned, - ** switch the locking context and pMethod then return. - */ - pCtx->oldLockingContext = pFile->lockingContext; - pFile->lockingContext = pCtx; - pCtx->pOldMethod = pFile->pMethod; - pFile->pMethod = &proxyIoMethods; - }else{ - if( pCtx->conchFile ){ - pCtx->conchFile->pMethod->xClose((sqlcipher_sqlite3_file *)pCtx->conchFile); - sqlcipher_sqlite3_free(pCtx->conchFile); +#else + { + DWORD upperBits; + DWORD lowerBits; + DWORD lastErrno; + + lowerBits = osGetFileSize(pFile->h, &upperBits); + *pSize = (((sqlcipher_sqlite3_int64)upperBits)<<32) + lowerBits; + if( (lowerBits == INVALID_FILE_SIZE) + && ((lastErrno = osGetLastError())!=NO_ERROR) ){ + pFile->lastErrno = lastErrno; + rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno, + "winFileSize", pFile->zPath); } - sqlcipher_sqlite3DbFree(0, pCtx->lockProxyPath); - sqlcipher_sqlite3_free(pCtx->conchFilePath); - sqlcipher_sqlite3_free(pCtx); } - OSTRACE(("TRANSPROXY %d %s\n", pFile->h, - (rc==SQLITE_OK ? "ok" : "failed"))); +#endif + OSTRACE(("SIZE file=%p, pSize=%p, *pSize=%lld, rc=%s\n", + pFile->h, pSize, *pSize, sqlcipher_sqlite3ErrName(rc))); return rc; } +/* +** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. +*/ +#ifndef LOCKFILE_FAIL_IMMEDIATELY +# define LOCKFILE_FAIL_IMMEDIATELY 1 +#endif + +#ifndef LOCKFILE_EXCLUSIVE_LOCK +# define LOCKFILE_EXCLUSIVE_LOCK 2 +#endif /* -** This routine handles sqlcipher_sqlite3_file_control() calls that are specific -** to proxy locking. +** Historically, SQLite has used both the LockFile and LockFileEx functions. +** When the LockFile function was used, it was always expected to fail +** immediately if the lock could not be obtained. Also, it always expected to +** obtain an exclusive lock. These flags are used with the LockFileEx function +** and reflect those expectations; therefore, they should not be changed. */ -static int proxyFileControl(sqlcipher_sqlite3_file *id, int op, void *pArg){ - switch( op ){ - case SQLITE_FCNTL_GET_LOCKPROXYFILE: { - unixFile *pFile = (unixFile*)id; - if( pFile->pMethod == &proxyIoMethods ){ - proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; - proxyTakeConch(pFile); - if( pCtx->lockProxyPath ){ - *(const char **)pArg = pCtx->lockProxyPath; - }else{ - *(const char **)pArg = ":auto: (not held)"; - } - } else { - *(const char **)pArg = NULL; - } - return SQLITE_OK; - } - case SQLITE_FCNTL_SET_LOCKPROXYFILE: { - unixFile *pFile = (unixFile*)id; - int rc = SQLITE_OK; - int isProxyStyle = (pFile->pMethod == &proxyIoMethods); - if( pArg==NULL || (const char *)pArg==0 ){ - if( isProxyStyle ){ - /* turn off proxy locking - not supported. If support is added for - ** switching proxy locking mode off then it will need to fail if - ** the journal mode is WAL mode. - */ - rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/; - }else{ - /* turn off proxy locking - already off - NOOP */ - rc = SQLITE_OK; - } - }else{ - const char *proxyPath = (const char *)pArg; - if( isProxyStyle ){ - proxyLockingContext *pCtx = - (proxyLockingContext*)pFile->lockingContext; - if( !strcmp(pArg, ":auto:") - || (pCtx->lockProxyPath && - !strncmp(pCtx->lockProxyPath, proxyPath, MAXPATHLEN)) - ){ - rc = SQLITE_OK; - }else{ - rc = switchLockProxyPath(pFile, proxyPath); - } - }else{ - /* turn on proxy file locking */ - rc = proxyTransformUnixFile(pFile, proxyPath); - } - } - return rc; - } - default: { - assert( 0 ); /* The call assures that only valid opcodes are sent */ - } - } - /*NOTREACHED*/ assert(0); - return SQLITE_ERROR; -} +#ifndef SQLITE_LOCKFILE_FLAGS +# define SQLITE_LOCKFILE_FLAGS (LOCKFILE_FAIL_IMMEDIATELY | \ + LOCKFILE_EXCLUSIVE_LOCK) +#endif /* -** Within this division (the proxying locking implementation) the procedures -** above this point are all utilities. The lock-related methods of the -** proxy-locking sqlcipher_sqlite3_io_method object follow. +** Currently, SQLite never calls the LockFileEx function without wanting the +** call to fail immediately if the lock cannot be obtained. */ +#ifndef SQLITE_LOCKFILEEX_FLAGS +# define SQLITE_LOCKFILEEX_FLAGS (LOCKFILE_FAIL_IMMEDIATELY) +#endif +/* +** Acquire a reader lock. +** Different API routines are called depending on whether or not this +** is Win9x or WinNT. +*/ +static int winGetReadLock(winFile *pFile){ + int res; + OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); + if( osIsNT() ){ +#if SQLITE_OS_WINCE + /* + ** NOTE: Windows CE is handled differently here due its lack of the Win32 + ** API LockFileEx. + */ + res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0); +#else + res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0, + SHARED_SIZE, 0); +#endif + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + int lk; + sqlcipher_sqlite3_randomness(sizeof(lk), &lk); + pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); + res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, + SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); + } +#endif + if( res == 0 ){ + pFile->lastErrno = osGetLastError(); + /* No need to log a failure to lock */ + } + OSTRACE(("READ-LOCK file=%p, result=%d\n", pFile->h, res)); + return res; +} /* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. +** Undo a readlock */ -static int proxyCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut) { - unixFile *pFile = (unixFile*)id; - int rc = proxyTakeConch(pFile); - if( rc==SQLITE_OK ){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - if( pCtx->conchHeld>0 ){ - unixFile *proxy = pCtx->lockProxy; - return proxy->pMethod->xCheckReservedLock((sqlcipher_sqlite3_file*)proxy, pResOut); - }else{ /* conchHeld < 0 is lockless */ - pResOut=0; - } +static int winUnlockReadLock(winFile *pFile){ + int res; + DWORD lastErrno; + OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); + if( osIsNT() ){ + res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); } - return rc; +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + res = winUnlockFile(&pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); + } +#endif + if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){ + pFile->lastErrno = lastErrno; + winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno, + "winUnlockReadLock", pFile->zPath); + } + OSTRACE(("READ-UNLOCK file=%p, result=%d\n", pFile->h, res)); + return res; } /* -** Lock the file with the lock specified by parameter eFileLock - one +** Lock the file with the lock specified by parameter locktype - one ** of the following: ** ** (1) SHARED_LOCK @@ -46648,34146 +46919,39453 @@ static int proxyCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut) { ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** -** This routine will only increase a lock. Use the sqlcipher_sqlite3OsUnlock() -** routine to lower a locking level. +** This routine will only increase a lock. The winUnlock() routine +** erases all locks at once and returns us immediately to locking level 0. +** It is not possible to lower the locking level one step at a time. You +** must go straight to locking level 0. */ -static int proxyLock(sqlcipher_sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; - int rc = proxyTakeConch(pFile); - if( rc==SQLITE_OK ){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - if( pCtx->conchHeld>0 ){ - unixFile *proxy = pCtx->lockProxy; - rc = proxy->pMethod->xLock((sqlcipher_sqlite3_file*)proxy, eFileLock); - pFile->eFileLock = proxy->eFileLock; +static int winLock(sqlcipher_sqlite3_file *id, int locktype){ + int rc = SQLITE_OK; /* Return code from subroutines */ + int res = 1; /* Result of a Windows lock call */ + int newLocktype; /* Set pFile->locktype to this value before exiting */ + int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ + winFile *pFile = (winFile*)id; + DWORD lastErrno = NO_ERROR; + + assert( id!=0 ); + OSTRACE(("LOCK file=%p, oldLock=%d(%d), newLock=%d\n", + pFile->h, pFile->locktype, pFile->sharedLockByte, locktype)); + + /* If there is already a lock of this type or more restrictive on the + ** OsFile, do nothing. Don't use the end_lock: exit path, as + ** sqlcipher_sqlite3OsEnterMutex() hasn't been called yet. + */ + if( pFile->locktype>=locktype ){ + OSTRACE(("LOCK-HELD file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + } + + /* Do not allow any kind of write-lock on a read-only database + */ + if( (pFile->ctrlFlags & WINFILE_RDONLY)!=0 && locktype>=RESERVED_LOCK ){ + return SQLITE_IOERR_LOCK; + } + + /* Make sure the locking sequence is correct + */ + assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); + assert( locktype!=PENDING_LOCK ); + assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); + + /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or + ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of + ** the PENDING_LOCK byte is temporary. + */ + newLocktype = pFile->locktype; + if( pFile->locktype==NO_LOCK + || (locktype==EXCLUSIVE_LOCK && pFile->locktype<=RESERVED_LOCK) + ){ + int cnt = 3; + while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, + PENDING_BYTE, 0, 1, 0))==0 ){ + /* Try 3 times to get the pending lock. This is needed to work + ** around problems caused by indexing and/or anti-virus software on + ** Windows systems. + ** If you are using this code as a model for alternative VFSes, do not + ** copy this retry logic. It is a hack intended for Windows only. + */ + lastErrno = osGetLastError(); + OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n", + pFile->h, cnt, res)); + if( lastErrno==ERROR_INVALID_HANDLE ){ + pFile->lastErrno = lastErrno; + rc = SQLITE_IOERR_LOCK; + OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n", + pFile->h, cnt, sqlcipher_sqlite3ErrName(rc))); + return rc; + } + if( cnt ) sqlcipher_sqlite3_win32_sleep(1); + } + gotPendingLock = res; + if( !res ){ + lastErrno = osGetLastError(); + } + } + + /* Acquire a shared lock + */ + if( locktype==SHARED_LOCK && res ){ + assert( pFile->locktype==NO_LOCK ); + res = winGetReadLock(pFile); + if( res ){ + newLocktype = SHARED_LOCK; }else{ - /* conchHeld < 0 is lockless */ + lastErrno = osGetLastError(); + } + } + + /* Acquire a RESERVED lock + */ + if( locktype==RESERVED_LOCK && res ){ + assert( pFile->locktype==SHARED_LOCK ); + res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0); + if( res ){ + newLocktype = RESERVED_LOCK; + }else{ + lastErrno = osGetLastError(); + } + } + + /* Acquire a PENDING lock + */ + if( locktype==EXCLUSIVE_LOCK && res ){ + newLocktype = PENDING_LOCK; + gotPendingLock = 0; + } + + /* Acquire an EXCLUSIVE lock + */ + if( locktype==EXCLUSIVE_LOCK && res ){ + assert( pFile->locktype>=SHARED_LOCK ); + res = winUnlockReadLock(pFile); + res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0, + SHARED_SIZE, 0); + if( res ){ + newLocktype = EXCLUSIVE_LOCK; + }else{ + lastErrno = osGetLastError(); + winGetReadLock(pFile); } } + + /* If we are holding a PENDING lock that ought to be released, then + ** release it now. + */ + if( gotPendingLock && locktype==SHARED_LOCK ){ + winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0); + } + + /* Update the state of the lock has held in the file descriptor then + ** return the appropriate result code. + */ + if( res ){ + rc = SQLITE_OK; + }else{ + pFile->lastErrno = lastErrno; + rc = SQLITE_BUSY; + OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n", + pFile->h, locktype, newLocktype)); + } + pFile->locktype = (u8)newLocktype; + OSTRACE(("LOCK file=%p, lock=%d, rc=%s\n", + pFile->h, pFile->locktype, sqlcipher_sqlite3ErrName(rc))); return rc; } +/* +** This routine checks if there is a RESERVED lock held on the specified +** file by this or any other process. If such a lock is held, return +** non-zero, otherwise zero. +*/ +static int winCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ + int res; + winFile *pFile = (winFile*)id; + + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); + OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p\n", pFile->h, pResOut)); + + assert( id!=0 ); + if( pFile->locktype>=RESERVED_LOCK ){ + res = 1; + OSTRACE(("TEST-WR-LOCK file=%p, result=%d (local)\n", pFile->h, res)); + }else{ + res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE,0,1,0); + if( res ){ + winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0); + } + res = !res; + OSTRACE(("TEST-WR-LOCK file=%p, result=%d (remote)\n", pFile->h, res)); + } + *pResOut = res; + OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", + pFile->h, pResOut, *pResOut)); + return SQLITE_OK; +} /* -** Lower the locking level on file descriptor pFile to eFileLock. eFileLock +** Lower the locking level on file descriptor id to locktype. locktype ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. +** +** It is not possible for this routine to fail if the second argument +** is NO_LOCK. If the second argument is SHARED_LOCK then this routine +** might return SQLITE_IOERR; */ -static int proxyUnlock(sqlcipher_sqlite3_file *id, int eFileLock) { - unixFile *pFile = (unixFile*)id; - int rc = proxyTakeConch(pFile); - if( rc==SQLITE_OK ){ - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - if( pCtx->conchHeld>0 ){ - unixFile *proxy = pCtx->lockProxy; - rc = proxy->pMethod->xUnlock((sqlcipher_sqlite3_file*)proxy, eFileLock); - pFile->eFileLock = proxy->eFileLock; - }else{ - /* conchHeld < 0 is lockless */ +static int winUnlock(sqlcipher_sqlite3_file *id, int locktype){ + int type; + winFile *pFile = (winFile*)id; + int rc = SQLITE_OK; + assert( pFile!=0 ); + assert( locktype<=SHARED_LOCK ); + OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n", + pFile->h, pFile->locktype, pFile->sharedLockByte, locktype)); + type = pFile->locktype; + if( type>=EXCLUSIVE_LOCK ){ + winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); + if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){ + /* This should never happen. We should always be able to + ** reacquire the read lock */ + rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), + "winUnlock", pFile->zPath); } } + if( type>=RESERVED_LOCK ){ + winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0); + } + if( locktype==NO_LOCK && type>=SHARED_LOCK ){ + winUnlockReadLock(pFile); + } + if( type>=PENDING_LOCK ){ + winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0); + } + pFile->locktype = (u8)locktype; + OSTRACE(("UNLOCK file=%p, lock=%d, rc=%s\n", + pFile->h, pFile->locktype, sqlcipher_sqlite3ErrName(rc))); return rc; } -/* -** Close a file that uses proxy locks. +/****************************************************************************** +****************************** No-op Locking ********************************** +** +** Of the various locking implementations available, this is by far the +** simplest: locking is ignored. No attempt is made to lock the database +** file for reading or writing. +** +** This locking mode is appropriate for use on read-only databases +** (ex: databases that are burned into CD-ROM, for example.) It can +** also be used if the application employs some external mechanism to +** prevent simultaneous access of the same database by two or more +** database connections. But there is a serious risk of database +** corruption if this locking mode is used in situations where multiple +** database connections are accessing the same database file at the same +** time and one or more of those connections are writing. */ -static int proxyClose(sqlcipher_sqlite3_file *id) { - if( ALWAYS(id) ){ - unixFile *pFile = (unixFile*)id; - proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; - unixFile *lockProxy = pCtx->lockProxy; - unixFile *conchFile = pCtx->conchFile; - int rc = SQLITE_OK; - if( lockProxy ){ - rc = lockProxy->pMethod->xUnlock((sqlcipher_sqlite3_file*)lockProxy, NO_LOCK); - if( rc ) return rc; - rc = lockProxy->pMethod->xClose((sqlcipher_sqlite3_file*)lockProxy); - if( rc ) return rc; - sqlcipher_sqlite3_free(lockProxy); - pCtx->lockProxy = 0; - } - if( conchFile ){ - if( pCtx->conchHeld ){ - rc = proxyReleaseConch(pFile); - if( rc ) return rc; - } - rc = conchFile->pMethod->xClose((sqlcipher_sqlite3_file*)conchFile); - if( rc ) return rc; - sqlcipher_sqlite3_free(conchFile); - } - sqlcipher_sqlite3DbFree(0, pCtx->lockProxyPath); - sqlcipher_sqlite3_free(pCtx->conchFilePath); - sqlcipher_sqlite3DbFree(0, pCtx->dbPath); - /* restore the original locking context and pMethod then close it */ - pFile->lockingContext = pCtx->oldLockingContext; - pFile->pMethod = pCtx->pOldMethod; - sqlcipher_sqlite3_free(pCtx); - return pFile->pMethod->xClose(id); - } +static int winNolockLock(sqlcipher_sqlite3_file *id, int locktype){ + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(locktype); return SQLITE_OK; } +static int winNolockCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(pResOut); + return SQLITE_OK; +} +static int winNolockUnlock(sqlcipher_sqlite3_file *id, int locktype){ + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(locktype); + return SQLITE_OK; +} -#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ -/* -** The proxy locking style is intended for use with AFP filesystems. -** And since AFP is only supported on MacOSX, the proxy locking is also -** restricted to MacOSX. -** -** -******************* End of the proxy lock implementation ********************** +/******************* End of the no-op lock implementation ********************* ******************************************************************************/ /* -** Initialize the operating system interface. -** -** This routine registers all VFS implementations for unix-like operating -** systems. This routine, and the sqlcipher_sqlite3_os_end() routine that follows, -** should be the only routines in this file that are visible from other -** files. +** If *pArg is initially negative then this is a query. Set *pArg to +** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set. ** -** This routine is called once during SQLite initialization and by a -** single thread. The memory allocation and mutex subsystems have not -** necessarily been initialized when this routine is called, and so they -** should not be used. +** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags. */ -SQLITE_API int sqlcipher_sqlite3_os_init(void){ - /* - ** The following macro defines an initializer for an sqlcipher_sqlite3_vfs object. - ** The name of the VFS is NAME. The pAppData is a pointer to a pointer - ** to the "finder" function. (pAppData is a pointer to a pointer because - ** silly C90 rules prohibit a void* from being cast to a function pointer - ** and so we have to go through the intermediate pointer to avoid problems - ** when compiling with -pedantic-errors on GCC.) - ** - ** The FINDER parameter to this macro is the name of the pointer to the - ** finder-function. The finder-function returns a pointer to the - ** sqlite_io_methods object that implements the desired locking - ** behaviors. See the division above that contains the IOMETHODS - ** macro for addition information on finder-functions. - ** - ** Most finders simply return a pointer to a fixed sqlcipher_sqlite3_io_methods - ** object. But the "autolockIoFinder" available on MacOSX does a little - ** more than that; it looks at the filesystem type that hosts the - ** database file and tries to choose an locking method appropriate for - ** that filesystem time. - */ - #define UNIXVFS(VFSNAME, FINDER) { \ - 3, /* iVersion */ \ - sizeof(unixFile), /* szOsFile */ \ - MAX_PATHNAME, /* mxPathname */ \ - 0, /* pNext */ \ - VFSNAME, /* zName */ \ - (void*)&FINDER, /* pAppData */ \ - unixOpen, /* xOpen */ \ - unixDelete, /* xDelete */ \ - unixAccess, /* xAccess */ \ - unixFullPathname, /* xFullPathname */ \ - unixDlOpen, /* xDlOpen */ \ - unixDlError, /* xDlError */ \ - unixDlSym, /* xDlSym */ \ - unixDlClose, /* xDlClose */ \ - unixRandomness, /* xRandomness */ \ - unixSleep, /* xSleep */ \ - unixCurrentTime, /* xCurrentTime */ \ - unixGetLastError, /* xGetLastError */ \ - unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \ - unixSetSystemCall, /* xSetSystemCall */ \ - unixGetSystemCall, /* xGetSystemCall */ \ - unixNextSystemCall, /* xNextSystemCall */ \ +static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){ + if( *pArg<0 ){ + *pArg = (pFile->ctrlFlags & mask)!=0; + }else if( (*pArg)==0 ){ + pFile->ctrlFlags &= ~mask; + }else{ + pFile->ctrlFlags |= mask; } +} - /* - ** All default VFSes for unix are contained in the following array. - ** - ** Note that the sqlcipher_sqlite3_vfs.pNext field of the VFS object is modified - ** by the SQLite core when the VFS is registered. So the following - ** array cannot be const. - */ - static sqlcipher_sqlite3_vfs aVfs[] = { -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) - UNIXVFS("unix", autolockIoFinder ), -#elif OS_VXWORKS - UNIXVFS("unix", vxworksIoFinder ), -#else - UNIXVFS("unix", posixIoFinder ), -#endif - UNIXVFS("unix-none", nolockIoFinder ), - UNIXVFS("unix-dotfile", dotlockIoFinder ), - UNIXVFS("unix-excl", posixIoFinder ), -#if OS_VXWORKS - UNIXVFS("unix-namedsem", semIoFinder ), -#endif -#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS - UNIXVFS("unix-posix", posixIoFinder ), -#endif -#if SQLITE_ENABLE_LOCKING_STYLE - UNIXVFS("unix-flock", flockIoFinder ), -#endif -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) - UNIXVFS("unix-afp", afpIoFinder ), - UNIXVFS("unix-nfs", nfsIoFinder ), - UNIXVFS("unix-proxy", proxyIoFinder ), +/* Forward references to VFS helper methods used for temporary files */ +static int winGetTempname(sqlcipher_sqlite3_vfs *, char **); +static int winIsDir(const void *); +static BOOL winIsLongPathPrefix(const char *); +static BOOL winIsDriveLetterAndColon(const char *); + +/* +** Control and query of the open file handle. +*/ +static int winFileControl(sqlcipher_sqlite3_file *id, int op, void *pArg){ + winFile *pFile = (winFile*)id; + OSTRACE(("FCNTL file=%p, op=%d, pArg=%p\n", pFile->h, op, pArg)); + switch( op ){ + case SQLITE_FCNTL_LOCKSTATE: { + *(int*)pArg = pFile->locktype; + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + } + case SQLITE_FCNTL_LAST_ERRNO: { + *(int*)pArg = (int)pFile->lastErrno; + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + } + case SQLITE_FCNTL_CHUNK_SIZE: { + pFile->szChunk = *(int *)pArg; + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + } + case SQLITE_FCNTL_SIZE_HINT: { + if( pFile->szChunk>0 ){ + sqlcipher_sqlite3_int64 oldSz; + int rc = winFileSize(id, &oldSz); + if( rc==SQLITE_OK ){ + sqlcipher_sqlite3_int64 newSz = *(sqlcipher_sqlite3_int64*)pArg; + if( newSz>oldSz ){ + SimulateIOErrorBenign(1); + rc = winTruncate(id, newSz); + SimulateIOErrorBenign(0); + } + } + OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlcipher_sqlite3ErrName(rc))); + return rc; + } + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + } + case SQLITE_FCNTL_PERSIST_WAL: { + winModeBit(pFile, WINFILE_PERSIST_WAL, (int*)pArg); + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + } + case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { + winModeBit(pFile, WINFILE_PSOW, (int*)pArg); + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + } + case SQLITE_FCNTL_VFSNAME: { + *(char**)pArg = sqlcipher_sqlite3_mprintf("%s", pFile->pVfs->zName); + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + } + case SQLITE_FCNTL_WIN32_AV_RETRY: { + int *a = (int*)pArg; + if( a[0]>0 ){ + winIoerrRetry = a[0]; + }else{ + a[0] = winIoerrRetry; + } + if( a[1]>0 ){ + winIoerrRetryDelay = a[1]; + }else{ + a[1] = winIoerrRetryDelay; + } + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + } + case SQLITE_FCNTL_WIN32_GET_HANDLE: { + LPHANDLE phFile = (LPHANDLE)pArg; + *phFile = pFile->h; + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + } +#ifdef SQLITE_TEST + case SQLITE_FCNTL_WIN32_SET_HANDLE: { + LPHANDLE phFile = (LPHANDLE)pArg; + HANDLE hOldFile = pFile->h; + pFile->h = *phFile; + *phFile = hOldFile; + OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n", + hOldFile, pFile->h)); + return SQLITE_OK; + } #endif - }; - unsigned int i; /* Loop counter */ + case SQLITE_FCNTL_TEMPFILENAME: { + char *zTFile = 0; + int rc = winGetTempname(pFile->pVfs, &zTFile); + if( rc==SQLITE_OK ){ + *(char**)pArg = zTFile; + } + OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlcipher_sqlite3ErrName(rc))); + return rc; + } +#if SQLITE_MAX_MMAP_SIZE>0 + case SQLITE_FCNTL_MMAP_SIZE: { + i64 newLimit = *(i64*)pArg; + int rc = SQLITE_OK; + if( newLimit>sqlcipher_sqlite3GlobalConfig.mxMmap ){ + newLimit = sqlcipher_sqlite3GlobalConfig.mxMmap; + } - /* Double-check that the aSyscall[] array has been constructed - ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==29 ); + /* The value of newLimit may be eventually cast to (SIZE_T) and passed + ** to MapViewOfFile(). Restrict its value to 2GB if (SIZE_T) is not at + ** least a 64-bit type. */ + if( newLimit>0 && sizeof(SIZE_T)<8 ){ + newLimit = (newLimit & 0x7FFFFFFF); + } - /* Register all VFSes defined in the aVfs[] array */ - for(i=0; i<(sizeof(aVfs)/sizeof(sqlcipher_sqlite3_vfs)); i++){ - sqlcipher_sqlite3_vfs_register(&aVfs[i], i==0); + *(i64*)pArg = pFile->mmapSizeMax; + if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ + pFile->mmapSizeMax = newLimit; + if( pFile->mmapSize>0 ){ + winUnmapfile(pFile); + rc = winMapfile(pFile, -1); + } + } + OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlcipher_sqlite3ErrName(rc))); + return rc; + } +#endif } - unixBigLock = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); - return SQLITE_OK; + OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h)); + return SQLITE_NOTFOUND; } /* -** Shutdown the operating system interface. +** Return the sector size in bytes of the underlying block device for +** the specified file. This is almost always 512 bytes, but may be +** larger for some devices. ** -** Some operating systems might need to do some cleanup in this routine, -** to release dynamically allocated objects. But not on unix. -** This routine is a no-op for unix. +** SQLite code assumes this function cannot fail. It also assumes that +** if two files are created in the same file-system directory (i.e. +** a database and its journal file) that the sector size will be the +** same for both. */ -SQLITE_API int sqlcipher_sqlite3_os_end(void){ - unixBigLock = 0; - return SQLITE_OK; +static int winSectorSize(sqlcipher_sqlite3_file *id){ + (void)id; + return SQLITE_DEFAULT_SECTOR_SIZE; } -#endif /* SQLITE_OS_UNIX */ +/* +** Return a vector of device characteristics. +*/ +static int winDeviceCharacteristics(sqlcipher_sqlite3_file *id){ + winFile *p = (winFile*)id; + return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | + ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); +} -/************** End of os_unix.c *********************************************/ -/************** Begin file os_win.c ******************************************/ /* -** 2004 May 22 -** -** 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 that is specific to Windows. +** Windows will only let you create file view mappings +** on allocation size granularity boundaries. +** During sqlcipher_sqlite3_os_init() we do a GetSystemInfo() +** to get the granularity size. */ -/* #include "sqliteInt.h" */ -#if SQLITE_OS_WIN /* This file is used for Windows only */ +static SYSTEM_INFO winSysInfo; + +#ifndef SQLITE_OMIT_WAL /* -** Include code that is common to all os_*.c files +** Helper functions to obtain and relinquish the global mutex. The +** global mutex is used to protect the winLockInfo objects used by +** this file, all of which may be shared by multiple threads. +** +** Function winShmMutexHeld() is used to assert() that the global mutex +** is held when required. This function is only used as part of assert() +** statements. e.g. +** +** winShmEnterMutex() +** assert( winShmMutexHeld() ); +** winShmLeaveMutex() */ -/************** Include os_common.h in the middle of os_win.c ****************/ -/************** Begin file os_common.h ***************************************/ +static sqlcipher_sqlite3_mutex *winBigLock = 0; +static void winShmEnterMutex(void){ + sqlcipher_sqlite3_mutex_enter(winBigLock); +} +static void winShmLeaveMutex(void){ + sqlcipher_sqlite3_mutex_leave(winBigLock); +} +#ifndef NDEBUG +static int winShmMutexHeld(void) { + return sqlcipher_sqlite3_mutex_held(winBigLock); +} +#endif + /* -** 2004 May 22 +** Object used to represent a single file opened and mmapped to provide +** shared memory. When multiple threads all reference the same +** log-summary, each thread has its own winFile object, but they all +** point to a single instance of this object. In other words, each +** log-summary is opened only once per process. ** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** winShmMutexHeld() must be true when creating or destroying +** this object or while reading or writing the following fields: ** -** 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. +** nRef +** pNext +** +** The following fields are read-only after the object is created: ** -****************************************************************************** +** fid +** zFilename ** -** This file contains macros and a little bit of code that is common to -** all of the platform-specific files (os_*.c) and is #included into those -** files. +** Either winShmNode.mutex must be held or winShmNode.nRef==0 and +** winShmMutexHeld() is true when reading or writing any other field +** in this structure. ** -** This file should be #included by the os_*.c files only. It is not a -** general purpose header file. */ -#ifndef _OS_COMMON_H_ -#define _OS_COMMON_H_ +struct winShmNode { + sqlcipher_sqlite3_mutex *mutex; /* Mutex to access this object */ + char *zFilename; /* Name of the file */ + winFile hFile; /* File handle from winOpen */ -/* -** At least two bugs have slipped in because we changed the MEMORY_DEBUG -** macro to SQLITE_DEBUG and some older makefiles have not yet made the -** switch. The following code should catch this problem at compile-time. -*/ -#ifdef MEMORY_DEBUG -# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." + int szRegion; /* Size of shared-memory regions */ + int nRegion; /* Size of array apRegion */ + u8 isReadonly; /* True if read-only */ + u8 isUnlocked; /* True if no DMS lock held */ + + struct ShmRegion { + HANDLE hMap; /* File handle from CreateFileMapping */ + void *pMap; + } *aRegion; + DWORD lastErrno; /* The Windows errno from the last I/O error */ + + int nRef; /* Number of winShm objects pointing to this */ + winShm *pFirst; /* All winShm objects pointing to this */ + winShmNode *pNext; /* Next in list of all winShmNode objects */ +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) + u8 nextShmId; /* Next available winShm.id value */ #endif +}; /* -** Macros for performance tracing. Normally turned off. Only works -** on i486 hardware. +** A global array of all winShmNode objects. +** +** The winShmMutexHeld() must be true while reading or writing this list. */ -#ifdef SQLITE_PERFORMANCE_TRACE +static winShmNode *winShmNodeList = 0; /* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of os_common.h ****************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** Structure used internally by this VFS to record the state of an +** open shared memory connection. ** -** 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. +** The following fields are initialized when this object is created and +** are read-only thereafter: ** -****************************************************************************** +** winShm.pShmNode +** winShm.id ** -** This file contains inline asm code for retrieving "high-performance" -** counters for x86 and x86_64 class CPUs. +** All other fields are read/write. The winShm.pShmNode->mutex must be held +** while accessing any read/write fields. */ -#ifndef SQLITE_HWTIME_H -#define SQLITE_HWTIME_H +struct winShm { + winShmNode *pShmNode; /* The underlying winShmNode object */ + winShm *pNext; /* Next winShm with the same winShmNode */ + u8 hasMutex; /* True if holding the winShmNode mutex */ + u16 sharedMask; /* Mask of shared locks held */ + u16 exclMask; /* Mask of exclusive locks held */ +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) + u8 id; /* Id of this connection with its winShmNode */ +#endif +}; /* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. +** Constants used for locking */ -#if !defined(__STRICT_ANSI__) && \ - (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) - - #if defined(__GNUC__) - - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } - - #elif defined(_MSC_VER) +#define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ +#define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ - __declspec(naked) __inline sqlite_uint64 __cdecl sqlcipher_sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } - } +/* +** Apply advisory locks for all n bytes beginning at ofst. +*/ +#define WINSHM_UNLCK 1 +#define WINSHM_RDLCK 2 +#define WINSHM_WRLCK 3 +static int winShmSystemLock( + winShmNode *pFile, /* Apply locks to this open shared-memory segment */ + int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */ + int ofst, /* Offset to first byte to be locked/unlocked */ + int nByte /* Number of bytes to lock or unlock */ +){ + int rc = 0; /* Result code form Lock/UnlockFileEx() */ - #endif + /* Access to the winShmNode object is serialized by the caller */ + assert( pFile->nRef==0 || sqlcipher_sqlite3_mutex_held(pFile->mutex) ); -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) + OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n", + pFile->hFile.h, lockType, ofst, nByte)); - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; + /* Release/Acquire the system-level lock */ + if( lockType==WINSHM_UNLCK ){ + rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0); + }else{ + /* Initialize the locking parameters */ + DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; + if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; + rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0); } -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) - - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; + if( rc!= 0 ){ + rc = SQLITE_OK; + }else{ + pFile->lastErrno = osGetLastError(); + rc = SQLITE_BUSY; } -#else - - /* - ** asm() is needed for hardware timing support. Without asm(), - ** disable the sqlcipher_sqlite3Hwtime() routine. - ** - ** sqlcipher_sqlite3Hwtime() is only used for some obscure debugging - ** and analysis configurations, not in any deliverable, so this - ** should not be a great loss. - */ -SQLITE_PRIVATE sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ return ((sqlite_uint64)0); } - -#endif - -#endif /* !defined(SQLITE_HWTIME_H) */ + OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n", + pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" : + "winLockFile", pFile->lastErrno, sqlcipher_sqlite3ErrName(rc))); -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in os_common.h ******************/ + return rc; +} -static sqlite_uint64 g_start; -static sqlite_uint64 g_elapsed; -#define TIMER_START g_start=sqlcipher_sqlite3Hwtime() -#define TIMER_END g_elapsed=sqlcipher_sqlite3Hwtime()-g_start -#define TIMER_ELAPSED g_elapsed -#else -#define TIMER_START -#define TIMER_END -#define TIMER_ELAPSED ((sqlite_uint64)0) -#endif +/* Forward references to VFS methods */ +static int winOpen(sqlcipher_sqlite3_vfs*,const char*,sqlcipher_sqlite3_file*,int,int*); +static int winDelete(sqlcipher_sqlite3_vfs *,const char*,int); /* -** If we compile with the SQLITE_TEST macro set, then the following block -** of code will give us the ability to simulate a disk I/O error. This -** is used for testing the I/O recovery logic. +** Purge the winShmNodeList list of all entries with winShmNode.nRef==0. +** +** This is not a VFS shared-memory method; it is a utility function called +** by VFS shared-memory methods. */ -#if defined(SQLITE_TEST) -SQLITE_API extern int sqlcipher_sqlite3_io_error_hit; -SQLITE_API extern int sqlcipher_sqlite3_io_error_hardhit; -SQLITE_API extern int sqlcipher_sqlite3_io_error_pending; -SQLITE_API extern int sqlcipher_sqlite3_io_error_persist; -SQLITE_API extern int sqlcipher_sqlite3_io_error_benign; -SQLITE_API extern int sqlcipher_sqlite3_diskfull_pending; -SQLITE_API extern int sqlcipher_sqlite3_diskfull; -#define SimulateIOErrorBenign(X) sqlcipher_sqlite3_io_error_benign=(X) -#define SimulateIOError(CODE) \ - if( (sqlcipher_sqlite3_io_error_persist && sqlcipher_sqlite3_io_error_hit) \ - || sqlcipher_sqlite3_io_error_pending-- == 1 ) \ - { local_ioerr(); CODE; } -static void local_ioerr(){ - IOTRACE(("IOERR\n")); - sqlcipher_sqlite3_io_error_hit++; - if( !sqlcipher_sqlite3_io_error_benign ) sqlcipher_sqlite3_io_error_hardhit++; +static void winShmPurge(sqlcipher_sqlite3_vfs *pVfs, int deleteFlag){ + winShmNode **pp; + winShmNode *p; + assert( winShmMutexHeld() ); + OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n", + osGetCurrentProcessId(), deleteFlag)); + pp = &winShmNodeList; + while( (p = *pp)!=0 ){ + if( p->nRef==0 ){ + int i; + if( p->mutex ){ sqlcipher_sqlite3_mutex_free(p->mutex); } + for(i=0; inRegion; i++){ + BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap); + OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n", + osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); + UNUSED_VARIABLE_VALUE(bRc); + bRc = osCloseHandle(p->aRegion[i].hMap); + OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n", + osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); + UNUSED_VARIABLE_VALUE(bRc); + } + if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){ + SimulateIOErrorBenign(1); + winClose((sqlcipher_sqlite3_file *)&p->hFile); + SimulateIOErrorBenign(0); + } + if( deleteFlag ){ + SimulateIOErrorBenign(1); + sqlcipher_sqlite3BeginBenignMalloc(); + winDelete(pVfs, p->zFilename, 0); + sqlcipher_sqlite3EndBenignMalloc(); + SimulateIOErrorBenign(0); + } + *pp = p->pNext; + sqlcipher_sqlite3_free(p->aRegion); + sqlcipher_sqlite3_free(p); + }else{ + pp = &p->pNext; + } + } } -#define SimulateDiskfullError(CODE) \ - if( sqlcipher_sqlite3_diskfull_pending ){ \ - if( sqlcipher_sqlite3_diskfull_pending == 1 ){ \ - local_ioerr(); \ - sqlcipher_sqlite3_diskfull = 1; \ - sqlcipher_sqlite3_io_error_hit = 1; \ - CODE; \ - }else{ \ - sqlcipher_sqlite3_diskfull_pending--; \ - } \ - } -#else -#define SimulateIOErrorBenign(X) -#define SimulateIOError(A) -#define SimulateDiskfullError(A) -#endif /* defined(SQLITE_TEST) */ /* -** When testing, keep a count of the number of open files. +** The DMS lock has not yet been taken on shm file pShmNode. Attempt to +** take it now. Return SQLITE_OK if successful, or an SQLite error +** code otherwise. +** +** If the DMS cannot be locked because this is a readonly_shm=1 +** connection and no other process already holds a lock, return +** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1. */ -#if defined(SQLITE_TEST) -SQLITE_API extern int sqlcipher_sqlite3_open_file_count; -#define OpenCounter(X) sqlcipher_sqlite3_open_file_count+=(X) -#else -#define OpenCounter(X) -#endif /* defined(SQLITE_TEST) */ - -#endif /* !defined(_OS_COMMON_H_) */ - -/************** End of os_common.h *******************************************/ -/************** Continuing where we left off in os_win.c *********************/ +static int winLockSharedMemory(winShmNode *pShmNode){ + int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1); -/* -** Include the header file for the Windows VFS. -*/ -/* #include "os_win.h" */ + if( rc==SQLITE_OK ){ + if( pShmNode->isReadonly ){ + pShmNode->isUnlocked = 1; + winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); + return SQLITE_READONLY_CANTINIT; + }else if( winTruncate((sqlcipher_sqlite3_file*)&pShmNode->hFile, 0) ){ + winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); + return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(), + "winLockSharedMemory", pShmNode->zFilename); + } + } -/* -** Compiling and using WAL mode requires several APIs that are only -** available in Windows platforms based on the NT kernel. -*/ -#if !SQLITE_OS_WINNT && !defined(SQLITE_OMIT_WAL) -# error "WAL mode requires support from the Windows NT kernel, compile\ - with SQLITE_OMIT_WAL." -#endif + if( rc==SQLITE_OK ){ + winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); + } -#if !SQLITE_OS_WINNT && SQLITE_MAX_MMAP_SIZE>0 -# error "Memory mapped files require support from the Windows NT kernel,\ - compile with SQLITE_MAX_MMAP_SIZE=0." -#endif + return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1); +} /* -** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions -** based on the sub-platform)? +** Open the shared-memory area associated with database file pDbFd. +** +** When opening a new shared-memory file, if no other instances of that +** file are currently open, in this process or in other processes, then +** the file must be truncated to zero length or have its header cleared. */ -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI) -# define SQLITE_WIN32_HAS_ANSI -#endif +static int winOpenSharedMemory(winFile *pDbFd){ + struct winShm *p; /* The connection to be opened */ + winShmNode *pShmNode = 0; /* The underlying mmapped file */ + int rc = SQLITE_OK; /* Result code */ + winShmNode *pNew; /* Newly allocated winShmNode */ + int nName; /* Size of zName in bytes */ -/* -** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions -** based on the sub-platform)? -*/ -#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT) && \ - !defined(SQLITE_WIN32_NO_WIDE) -# define SQLITE_WIN32_HAS_WIDE -#endif + assert( pDbFd->pShm==0 ); /* Not previously opened */ -/* -** Make sure at least one set of Win32 APIs is available. -*/ -#if !defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_WIN32_HAS_WIDE) -# error "At least one of SQLITE_WIN32_HAS_ANSI and SQLITE_WIN32_HAS_WIDE\ - must be defined." -#endif + /* Allocate space for the new sqlcipher_sqlite3_shm object. Also speculatively + ** allocate space for a new winShmNode and filename. + */ + p = sqlcipher_sqlite3MallocZero( sizeof(*p) ); + if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT; + nName = sqlcipher_sqlite3Strlen30(pDbFd->zPath); + pNew = sqlcipher_sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 ); + if( pNew==0 ){ + sqlcipher_sqlite3_free(p); + return SQLITE_IOERR_NOMEM_BKPT; + } + pNew->zFilename = (char*)&pNew[1]; + sqlcipher_sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); + sqlcipher_sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); -/* -** Define the required Windows SDK version constants if they are not -** already available. -*/ -#ifndef NTDDI_WIN8 -# define NTDDI_WIN8 0x06020000 -#endif + /* Look to see if there is an existing winShmNode that can be used. + ** If no matching winShmNode currently exists, create a new one. + */ + winShmEnterMutex(); + for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){ + /* TBD need to come up with better match here. Perhaps + ** use FILE_ID_BOTH_DIR_INFO Structure. + */ + if( sqlcipher_sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break; + } + if( pShmNode ){ + sqlcipher_sqlite3_free(pNew); + }else{ + int inFlags = SQLITE_OPEN_WAL; + int outFlags = 0; -#ifndef NTDDI_WINBLUE -# define NTDDI_WINBLUE 0x06030000 -#endif + pShmNode = pNew; + pNew = 0; + ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE; + pShmNode->pNext = winShmNodeList; + winShmNodeList = pShmNode; -#ifndef NTDDI_WINTHRESHOLD -# define NTDDI_WINTHRESHOLD 0x06040000 -#endif + if( sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ + pShmNode->mutex = sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pShmNode->mutex==0 ){ + rc = SQLITE_IOERR_NOMEM_BKPT; + goto shm_open_err; + } + } -/* -** Check to see if the GetVersionEx[AW] functions are deprecated on the -** target system. GetVersionEx was first deprecated in Win8.1. -*/ -#ifndef SQLITE_WIN32_GETVERSIONEX -# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINBLUE -# define SQLITE_WIN32_GETVERSIONEX 0 /* GetVersionEx() is deprecated */ -# else -# define SQLITE_WIN32_GETVERSIONEX 1 /* GetVersionEx() is current */ -# endif -#endif + if( 0==sqlcipher_sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ + inFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + }else{ + inFlags |= SQLITE_OPEN_READONLY; + } + rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, + (sqlcipher_sqlite3_file*)&pShmNode->hFile, + inFlags, &outFlags); + if( rc!=SQLITE_OK ){ + rc = winLogError(rc, osGetLastError(), "winOpenShm", + pShmNode->zFilename); + goto shm_open_err; + } + if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1; -/* -** Check to see if the CreateFileMappingA function is supported on the -** target system. It is unavailable when using "mincore.lib" on Win10. -** When compiling for Windows 10, always assume "mincore.lib" is in use. -*/ -#ifndef SQLITE_WIN32_CREATEFILEMAPPINGA -# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINTHRESHOLD -# define SQLITE_WIN32_CREATEFILEMAPPINGA 0 -# else -# define SQLITE_WIN32_CREATEFILEMAPPINGA 1 -# endif -#endif + rc = winLockSharedMemory(pShmNode); + if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err; + } -/* -** This constant should already be defined (in the "WinDef.h" SDK file). -*/ -#ifndef MAX_PATH -# define MAX_PATH (260) + /* Make the new connection a child of the winShmNode */ + p->pShmNode = pShmNode; +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) + p->id = pShmNode->nextShmId++; #endif + pShmNode->nRef++; + pDbFd->pShm = p; + winShmLeaveMutex(); -/* -** Maximum pathname length (in chars) for Win32. This should normally be -** MAX_PATH. -*/ -#ifndef SQLITE_WIN32_MAX_PATH_CHARS -# define SQLITE_WIN32_MAX_PATH_CHARS (MAX_PATH) -#endif + /* The reference count on pShmNode has already been incremented under + ** the cover of the winShmEnterMutex() mutex and the pointer from the + ** new (struct winShm) object to the pShmNode has been set. All that is + ** left to do is to link the new object into the linked list starting + ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex + ** mutex. + */ + sqlcipher_sqlite3_mutex_enter(pShmNode->mutex); + p->pNext = pShmNode->pFirst; + pShmNode->pFirst = p; + sqlcipher_sqlite3_mutex_leave(pShmNode->mutex); + return rc; -/* -** This constant should already be defined (in the "WinNT.h" SDK file). -*/ -#ifndef UNICODE_STRING_MAX_CHARS -# define UNICODE_STRING_MAX_CHARS (32767) -#endif + /* Jump here on any error */ +shm_open_err: + winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); + winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */ + sqlcipher_sqlite3_free(p); + sqlcipher_sqlite3_free(pNew); + winShmLeaveMutex(); + return rc; +} /* -** Maximum pathname length (in chars) for WinNT. This should normally be -** UNICODE_STRING_MAX_CHARS. +** Close a connection to shared-memory. Delete the underlying +** storage if deleteFlag is true. */ -#ifndef SQLITE_WINNT_MAX_PATH_CHARS -# define SQLITE_WINNT_MAX_PATH_CHARS (UNICODE_STRING_MAX_CHARS) -#endif +static int winShmUnmap( + sqlcipher_sqlite3_file *fd, /* Database holding shared memory */ + int deleteFlag /* Delete after closing if true */ +){ + winFile *pDbFd; /* Database holding shared-memory */ + winShm *p; /* The connection to be closed */ + winShmNode *pShmNode; /* The underlying shared-memory file */ + winShm **pp; /* For looping over sibling connections */ -/* -** Maximum pathname length (in bytes) for Win32. The MAX_PATH macro is in -** characters, so we allocate 4 bytes per character assuming worst-case of -** 4-bytes-per-character for UTF8. -*/ -#ifndef SQLITE_WIN32_MAX_PATH_BYTES -# define SQLITE_WIN32_MAX_PATH_BYTES (SQLITE_WIN32_MAX_PATH_CHARS*4) -#endif + pDbFd = (winFile*)fd; + p = pDbFd->pShm; + if( p==0 ) return SQLITE_OK; + pShmNode = p->pShmNode; -/* -** Maximum pathname length (in bytes) for WinNT. This should normally be -** UNICODE_STRING_MAX_CHARS * sizeof(WCHAR). -*/ -#ifndef SQLITE_WINNT_MAX_PATH_BYTES -# define SQLITE_WINNT_MAX_PATH_BYTES \ - (sizeof(WCHAR) * SQLITE_WINNT_MAX_PATH_CHARS) -#endif + /* Remove connection p from the set of connections associated + ** with pShmNode */ + sqlcipher_sqlite3_mutex_enter(pShmNode->mutex); + for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} + *pp = p->pNext; -/* -** Maximum error message length (in chars) for WinRT. -*/ -#ifndef SQLITE_WIN32_MAX_ERRMSG_CHARS -# define SQLITE_WIN32_MAX_ERRMSG_CHARS (1024) -#endif + /* Free the connection p */ + sqlcipher_sqlite3_free(p); + pDbFd->pShm = 0; + sqlcipher_sqlite3_mutex_leave(pShmNode->mutex); -/* -** Returns non-zero if the character should be treated as a directory -** separator. -*/ -#ifndef winIsDirSep -# define winIsDirSep(a) (((a) == '/') || ((a) == '\\')) -#endif + /* If pShmNode->nRef has reached 0, then close the underlying + ** shared-memory file, too */ + winShmEnterMutex(); + assert( pShmNode->nRef>0 ); + pShmNode->nRef--; + if( pShmNode->nRef==0 ){ + winShmPurge(pDbFd->pVfs, deleteFlag); + } + winShmLeaveMutex(); -/* -** This macro is used when a local variable is set to a value that is -** [sometimes] not used by the code (e.g. via conditional compilation). -*/ -#ifndef UNUSED_VARIABLE_VALUE -# define UNUSED_VARIABLE_VALUE(x) (void)(x) -#endif + return SQLITE_OK; +} /* -** Returns the character that should be used as the directory separator. +** Change the lock state for a shared-memory segment. */ -#ifndef winGetDirSep -# define winGetDirSep() '\\' -#endif +static int winShmLock( + sqlcipher_sqlite3_file *fd, /* Database file holding the shared memory */ + int ofst, /* First lock to acquire or release */ + int n, /* Number of locks to acquire or release */ + int flags /* What to do with the lock */ +){ + winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */ + winShm *p = pDbFd->pShm; /* The shared memory being locked */ + winShm *pX; /* For looping over all siblings */ + winShmNode *pShmNode; + int rc = SQLITE_OK; /* Result code */ + u16 mask; /* Mask of locks to take or release */ -/* -** Do we need to manually define the Win32 file mapping APIs for use with WAL -** mode or memory mapped files (e.g. these APIs are available in the Windows -** CE SDK; however, they are not present in the header file)? -*/ -#if SQLITE_WIN32_FILEMAPPING_API && \ - (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) -/* -** Two of the file mapping APIs are different under WinRT. Figure out which -** set we need. -*/ -#if SQLITE_OS_WINRT -WINBASEAPI HANDLE WINAPI CreateFileMappingFromApp(HANDLE, \ - LPSECURITY_ATTRIBUTES, ULONG, ULONG64, LPCWSTR); + if( p==0 ) return SQLITE_IOERR_SHMLOCK; + pShmNode = p->pShmNode; + if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; -WINBASEAPI LPVOID WINAPI MapViewOfFileFromApp(HANDLE, ULONG, ULONG64, SIZE_T); -#else -#if defined(SQLITE_WIN32_HAS_ANSI) -WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \ - DWORD, DWORD, DWORD, LPCSTR); -#endif /* defined(SQLITE_WIN32_HAS_ANSI) */ + assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); + assert( n>=1 ); + assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); + assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); -#if defined(SQLITE_WIN32_HAS_WIDE) -WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \ - DWORD, DWORD, DWORD, LPCWSTR); -#endif /* defined(SQLITE_WIN32_HAS_WIDE) */ + mask = (u16)((1U<<(ofst+n)) - (1U<1 || mask==(1<mutex); + if( flags & SQLITE_SHM_UNLOCK ){ + u16 allMask = 0; /* Mask of locks held by siblings */ -WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T); -#endif /* SQLITE_OS_WINRT */ + /* See if any siblings hold this same lock */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); + allMask |= pX->sharedMask; + } -/* -** These file mapping APIs are common to both Win32 and WinRT. -*/ + /* Unlock the system-level locks */ + if( (mask & allMask)==0 ){ + rc = winShmSystemLock(pShmNode, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n); + }else{ + rc = SQLITE_OK; + } -WINBASEAPI BOOL WINAPI FlushViewOfFile(LPCVOID, SIZE_T); -WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID); -#endif /* SQLITE_WIN32_FILEMAPPING_API */ + /* Undo the local locks */ + if( rc==SQLITE_OK ){ + p->exclMask &= ~mask; + p->sharedMask &= ~mask; + } + }else if( flags & SQLITE_SHM_SHARED ){ + u16 allShared = 0; /* Union of locks held by connections other than "p" */ -/* -** Some Microsoft compilers lack this definition. -*/ -#ifndef INVALID_FILE_ATTRIBUTES -# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) -#endif + /* Find out which shared locks are already held by sibling connections. + ** If any sibling already holds an exclusive lock, go ahead and return + ** SQLITE_BUSY. + */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( (pX->exclMask & mask)!=0 ){ + rc = SQLITE_BUSY; + break; + } + allShared |= pX->sharedMask; + } -#ifndef FILE_FLAG_MASK -# define FILE_FLAG_MASK (0xFF3C0000) -#endif + /* Get shared locks at the system level, if necessary */ + if( rc==SQLITE_OK ){ + if( (allShared & mask)==0 ){ + rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n); + }else{ + rc = SQLITE_OK; + } + } -#ifndef FILE_ATTRIBUTE_MASK -# define FILE_ATTRIBUTE_MASK (0x0003FFF7) -#endif + /* Get the local shared locks */ + if( rc==SQLITE_OK ){ + p->sharedMask |= mask; + } + }else{ + /* Make sure no sibling connections hold locks that will block this + ** lock. If any do, return SQLITE_BUSY right away. + */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ + rc = SQLITE_BUSY; + break; + } + } -#ifndef SQLITE_OMIT_WAL -/* Forward references to structures used for WAL */ -typedef struct winShm winShm; /* A connection to shared-memory */ -typedef struct winShmNode winShmNode; /* A region of shared-memory */ -#endif + /* Get the exclusive locks at the system level. Then if successful + ** also mark the local connection as being locked. + */ + if( rc==SQLITE_OK ){ + rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n); + if( rc==SQLITE_OK ){ + assert( (p->sharedMask & mask)==0 ); + p->exclMask |= mask; + } + } + } + sqlcipher_sqlite3_mutex_leave(pShmNode->mutex); + OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n", + osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, + sqlcipher_sqlite3ErrName(rc))); + return rc; +} /* -** WinCE lacks native support for file locking so we have to fake it -** with some code of our own. +** Implement a memory barrier or memory fence on shared memory. +** +** All loads and stores begun before the barrier must complete before +** any load or store begun after the barrier. */ -#if SQLITE_OS_WINCE -typedef struct winceLock { - int nReaders; /* Number of reader locks obtained */ - BOOL bPending; /* Indicates a pending lock has been obtained */ - BOOL bReserved; /* Indicates a reserved lock has been obtained */ - BOOL bExclusive; /* Indicates an exclusive lock has been obtained */ -} winceLock; -#endif +static void winShmBarrier( + sqlcipher_sqlite3_file *fd /* Database holding the shared memory */ +){ + UNUSED_PARAMETER(fd); + sqlcipher_sqlite3MemoryBarrier(); /* compiler-defined memory barrier */ + winShmEnterMutex(); /* Also mutex, for redundancy */ + winShmLeaveMutex(); +} /* -** The winFile structure is a subclass of sqlcipher_sqlite3_file* specific to the win32 -** portability layer. +** This function is called to obtain a pointer to region iRegion of the +** shared-memory associated with the database file fd. Shared-memory regions +** are numbered starting from zero. Each shared-memory region is szRegion +** bytes in size. +** +** If an error occurs, an error code is returned and *pp is set to NULL. +** +** Otherwise, if the isWrite parameter is 0 and the requested shared-memory +** region has not been allocated (by any client, including one running in a +** separate process), then *pp is set to NULL and SQLITE_OK returned. If +** isWrite is non-zero and the requested shared-memory region has not yet +** been allocated, it is allocated by this function. +** +** If the shared-memory region has already been allocated or is allocated by +** this call as described above, then it is mapped into this processes +** address space (if it is not already), *pp is set to point to the mapped +** memory and SQLITE_OK returned. */ -typedef struct winFile winFile; -struct winFile { - const sqlcipher_sqlite3_io_methods *pMethod; /*** Must be first ***/ - sqlcipher_sqlite3_vfs *pVfs; /* The VFS used to open this file */ - HANDLE h; /* Handle for accessing the file */ - u8 locktype; /* Type of lock currently held on this file */ - short sharedLockByte; /* Randomly chosen byte used as a shared lock */ - u8 ctrlFlags; /* Flags. See WINFILE_* below */ - DWORD lastErrno; /* The Windows errno from the last I/O error */ -#ifndef SQLITE_OMIT_WAL - winShm *pShm; /* Instance of shared memory on this file */ -#endif - const char *zPath; /* Full pathname of this file */ - int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ -#if SQLITE_OS_WINCE - LPWSTR zDeleteOnClose; /* Name of file to delete when closing */ - HANDLE hMutex; /* Mutex used to control access to shared lock */ - HANDLE hShared; /* Shared memory segment used for locking */ - winceLock local; /* Locks obtained by this instance of winFile */ - winceLock *shared; /* Global shared lock memory for the file */ -#endif -#if SQLITE_MAX_MMAP_SIZE>0 - int nFetchOut; /* Number of outstanding xFetch references */ - HANDLE hMap; /* Handle for accessing memory mapping */ - void *pMapRegion; /* Area memory mapped */ - sqlcipher_sqlite3_int64 mmapSize; /* Size of mapped region */ - sqlcipher_sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ -#endif -}; +static int winShmMap( + sqlcipher_sqlite3_file *fd, /* Handle open on database file */ + int iRegion, /* Region to retrieve */ + int szRegion, /* Size of regions */ + int isWrite, /* True to extend file if necessary */ + void volatile **pp /* OUT: Mapped memory */ +){ + winFile *pDbFd = (winFile*)fd; + winShm *pShm = pDbFd->pShm; + winShmNode *pShmNode; + DWORD protect = PAGE_READWRITE; + DWORD flags = FILE_MAP_WRITE | FILE_MAP_READ; + int rc = SQLITE_OK; -/* -** The winVfsAppData structure is used for the pAppData member for all of the -** Win32 VFS variants. -*/ -typedef struct winVfsAppData winVfsAppData; -struct winVfsAppData { - const sqlcipher_sqlite3_io_methods *pMethod; /* The file I/O methods to use. */ - void *pAppData; /* The extra pAppData, if any. */ - BOOL bNoLock; /* Non-zero if locking is disabled. */ -}; + if( !pShm ){ + rc = winOpenSharedMemory(pDbFd); + if( rc!=SQLITE_OK ) return rc; + pShm = pDbFd->pShm; + assert( pShm!=0 ); + } + pShmNode = pShm->pShmNode; -/* -** Allowed values for winFile.ctrlFlags -*/ -#define WINFILE_RDONLY 0x02 /* Connection is read only */ -#define WINFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ -#define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ + sqlcipher_sqlite3_mutex_enter(pShmNode->mutex); + if( pShmNode->isUnlocked ){ + rc = winLockSharedMemory(pShmNode); + if( rc!=SQLITE_OK ) goto shmpage_out; + pShmNode->isUnlocked = 0; + } + assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); -/* - * The size of the buffer used by sqlcipher_sqlite3_win32_write_debug(). - */ -#ifndef SQLITE_WIN32_DBG_BUF_SIZE -# define SQLITE_WIN32_DBG_BUF_SIZE ((int)(4096-sizeof(DWORD))) -#endif + if( pShmNode->nRegion<=iRegion ){ + struct ShmRegion *apNew; /* New aRegion[] array */ + int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ + sqlcipher_sqlite3_int64 sz; /* Current size of wal-index file */ -/* - * If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the - * various Win32 API heap functions instead of our own. - */ -#ifdef SQLITE_WIN32_MALLOC + pShmNode->szRegion = szRegion; -/* - * If this is non-zero, an isolated heap will be created by the native Win32 - * allocator subsystem; otherwise, the default process heap will be used. This - * setting has no effect when compiling for WinRT. By default, this is enabled - * and an isolated heap will be created to store all allocated data. - * - ****************************************************************************** - * WARNING: It is important to note that when this setting is non-zero and the - * winMemShutdown function is called (e.g. by the sqlcipher_sqlite3_shutdown - * function), all data that was allocated using the isolated heap will - * be freed immediately and any attempt to access any of that freed - * data will almost certainly result in an immediate access violation. - ****************************************************************************** - */ -#ifndef SQLITE_WIN32_HEAP_CREATE -# define SQLITE_WIN32_HEAP_CREATE (TRUE) -#endif + /* The requested region is not mapped into this processes address space. + ** Check to see if it has been allocated (i.e. if the wal-index file is + ** large enough to contain the requested region). + */ + rc = winFileSize((sqlcipher_sqlite3_file *)&pShmNode->hFile, &sz); + if( rc!=SQLITE_OK ){ + rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), + "winShmMap1", pDbFd->zPath); + goto shmpage_out; + } -/* - * This is the maximum possible initial size of the Win32-specific heap, in - * bytes. - */ -#ifndef SQLITE_WIN32_HEAP_MAX_INIT_SIZE -# define SQLITE_WIN32_HEAP_MAX_INIT_SIZE (4294967295U) -#endif + if( szhFile, nByte); + if( rc!=SQLITE_OK ){ + rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), + "winShmMap2", pDbFd->zPath); + goto shmpage_out; + } + } -/* - * This is the extra space for the initial size of the Win32-specific heap, - * in bytes. This value may be zero. - */ -#ifndef SQLITE_WIN32_HEAP_INIT_EXTRA -# define SQLITE_WIN32_HEAP_INIT_EXTRA (4194304) -#endif + /* Map the requested memory region into this processes address space. */ + apNew = (struct ShmRegion *)sqlcipher_sqlite3_realloc64( + pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) + ); + if( !apNew ){ + rc = SQLITE_IOERR_NOMEM_BKPT; + goto shmpage_out; + } + pShmNode->aRegion = apNew; -/* - * Calculate the maximum legal cache size, in pages, based on the maximum - * possible initial heap size and the default page size, setting aside the - * needed extra space. - */ -#ifndef SQLITE_WIN32_MAX_CACHE_SIZE -# define SQLITE_WIN32_MAX_CACHE_SIZE (((SQLITE_WIN32_HEAP_MAX_INIT_SIZE) - \ - (SQLITE_WIN32_HEAP_INIT_EXTRA)) / \ - (SQLITE_DEFAULT_PAGE_SIZE)) -#endif + if( pShmNode->isReadonly ){ + protect = PAGE_READONLY; + flags = FILE_MAP_READ; + } -/* - * This is cache size used in the calculation of the initial size of the - * Win32-specific heap. It cannot be negative. - */ -#ifndef SQLITE_WIN32_CACHE_SIZE -# if SQLITE_DEFAULT_CACHE_SIZE>=0 -# define SQLITE_WIN32_CACHE_SIZE (SQLITE_DEFAULT_CACHE_SIZE) -# else -# define SQLITE_WIN32_CACHE_SIZE (-(SQLITE_DEFAULT_CACHE_SIZE)) -# endif -#endif + while( pShmNode->nRegion<=iRegion ){ + HANDLE hMap = NULL; /* file-mapping handle */ + void *pMap = 0; /* Mapped memory region */ -/* - * Make sure that the calculated cache size, in pages, cannot cause the - * initial size of the Win32-specific heap to exceed the maximum amount - * of memory that can be specified in the call to HeapCreate. - */ -#if SQLITE_WIN32_CACHE_SIZE>SQLITE_WIN32_MAX_CACHE_SIZE -# undef SQLITE_WIN32_CACHE_SIZE -# define SQLITE_WIN32_CACHE_SIZE (2000) +#if SQLITE_OS_WINRT + hMap = osCreateFileMappingFromApp(pShmNode->hFile.h, + NULL, protect, nByte, NULL + ); +#elif defined(SQLITE_WIN32_HAS_WIDE) + hMap = osCreateFileMappingW(pShmNode->hFile.h, + NULL, protect, 0, nByte, NULL + ); +#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA + hMap = osCreateFileMappingA(pShmNode->hFile.h, + NULL, protect, 0, nByte, NULL + ); #endif - -/* - * The initial size of the Win32-specific heap. This value may be zero. - */ -#ifndef SQLITE_WIN32_HEAP_INIT_SIZE -# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_WIN32_CACHE_SIZE) * \ - (SQLITE_DEFAULT_PAGE_SIZE) + \ - (SQLITE_WIN32_HEAP_INIT_EXTRA)) + OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n", + osGetCurrentProcessId(), pShmNode->nRegion, nByte, + hMap ? "ok" : "failed")); + if( hMap ){ + int iOffset = pShmNode->nRegion*szRegion; + int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; +#if SQLITE_OS_WINRT + pMap = osMapViewOfFileFromApp(hMap, flags, + iOffset - iOffsetShift, szRegion + iOffsetShift + ); +#else + pMap = osMapViewOfFile(hMap, flags, + 0, iOffset - iOffsetShift, szRegion + iOffsetShift + ); #endif + OSTRACE(("SHM-MAP-MAP pid=%lu, region=%d, offset=%d, size=%d, rc=%s\n", + osGetCurrentProcessId(), pShmNode->nRegion, iOffset, + szRegion, pMap ? "ok" : "failed")); + } + if( !pMap ){ + pShmNode->lastErrno = osGetLastError(); + rc = winLogError(SQLITE_IOERR_SHMMAP, pShmNode->lastErrno, + "winShmMap3", pDbFd->zPath); + if( hMap ) osCloseHandle(hMap); + goto shmpage_out; + } + + pShmNode->aRegion[pShmNode->nRegion].pMap = pMap; + pShmNode->aRegion[pShmNode->nRegion].hMap = hMap; + pShmNode->nRegion++; + } + } + +shmpage_out: + if( pShmNode->nRegion>iRegion ){ + int iOffset = iRegion*szRegion; + int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; + char *p = (char *)pShmNode->aRegion[iRegion].pMap; + *pp = (void *)&p[iOffsetShift]; + }else{ + *pp = 0; + } + if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; + sqlcipher_sqlite3_mutex_leave(pShmNode->mutex); + return rc; +} + +#else +# define winShmMap 0 +# define winShmLock 0 +# define winShmBarrier 0 +# define winShmUnmap 0 +#endif /* #ifndef SQLITE_OMIT_WAL */ /* - * The maximum size of the Win32-specific heap. This value may be zero. - */ -#ifndef SQLITE_WIN32_HEAP_MAX_SIZE -# define SQLITE_WIN32_HEAP_MAX_SIZE (0) -#endif +** Cleans up the mapped region of the specified file, if any. +*/ +#if SQLITE_MAX_MMAP_SIZE>0 +static int winUnmapfile(winFile *pFile){ + assert( pFile!=0 ); + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, " + "mmapSize=%lld, mmapSizeMax=%lld\n", + osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion, + pFile->mmapSize, pFile->mmapSizeMax)); + if( pFile->pMapRegion ){ + if( !osUnmapViewOfFile(pFile->pMapRegion) ){ + pFile->lastErrno = osGetLastError(); + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, pMapRegion=%p, " + "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile, + pFile->pMapRegion)); + return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, + "winUnmapfile1", pFile->zPath); + } + pFile->pMapRegion = 0; + pFile->mmapSize = 0; + } + if( pFile->hMap!=NULL ){ + if( !osCloseHandle(pFile->hMap) ){ + pFile->lastErrno = osGetLastError(); + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n", + osGetCurrentProcessId(), pFile, pFile->hMap)); + return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, + "winUnmapfile2", pFile->zPath); + } + pFile->hMap = NULL; + } + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile)); + return SQLITE_OK; +} /* - * The extra flags to use in calls to the Win32 heap APIs. This value may be - * zero for the default behavior. - */ -#ifndef SQLITE_WIN32_HEAP_FLAGS -# define SQLITE_WIN32_HEAP_FLAGS (0) -#endif +** Memory map or remap the file opened by file-descriptor pFd (if the file +** is already mapped, the existing mapping is replaced by the new). Or, if +** there already exists a mapping for this file, and there are still +** outstanding xFetch() references to it, this function is a no-op. +** +** If parameter nByte is non-negative, then it is the requested size of +** the mapping to create. Otherwise, if nByte is less than zero, then the +** requested size is the size of the file on disk. The actual size of the +** created mapping is either the requested size or the value configured +** using SQLITE_FCNTL_MMAP_SIZE, whichever is smaller. +** +** SQLITE_OK is returned if no error occurs (even if the mapping is not +** recreated as a result of outstanding references) or an SQLite error +** code otherwise. +*/ +static int winMapfile(winFile *pFd, sqlcipher_sqlite3_int64 nByte){ + sqlcipher_sqlite3_int64 nMap = nByte; + int rc; + assert( nMap>=0 || pFd->nFetchOut==0 ); + OSTRACE(("MAP-FILE pid=%lu, pFile=%p, size=%lld\n", + osGetCurrentProcessId(), pFd, nByte)); -/* -** The winMemData structure stores information required by the Win32-specific -** sqlcipher_sqlite3_mem_methods implementation. -*/ -typedef struct winMemData winMemData; -struct winMemData { -#ifndef NDEBUG - u32 magic1; /* Magic number to detect structure corruption. */ -#endif - HANDLE hHeap; /* The handle to our heap. */ - BOOL bOwned; /* Do we own the heap (i.e. destroy it on shutdown)? */ -#ifndef NDEBUG - u32 magic2; /* Magic number to detect structure corruption. */ -#endif -}; + if( pFd->nFetchOut>0 ) return SQLITE_OK; -#ifndef NDEBUG -#define WINMEM_MAGIC1 0x42b2830b -#define WINMEM_MAGIC2 0xbd4d7cf4 -#endif + if( nMap<0 ){ + rc = winFileSize((sqlcipher_sqlite3_file*)pFd, &nMap); + if( rc ){ + OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_IOERR_FSTAT\n", + osGetCurrentProcessId(), pFd)); + return SQLITE_IOERR_FSTAT; + } + } + if( nMap>pFd->mmapSizeMax ){ + nMap = pFd->mmapSizeMax; + } + nMap &= ~(sqlcipher_sqlite3_int64)(winSysInfo.dwPageSize - 1); -static struct winMemData win_mem_data = { -#ifndef NDEBUG - WINMEM_MAGIC1, + if( nMap==0 && pFd->mmapSize>0 ){ + winUnmapfile(pFd); + } + if( nMap!=pFd->mmapSize ){ + void *pNew = 0; + DWORD protect = PAGE_READONLY; + DWORD flags = FILE_MAP_READ; + + winUnmapfile(pFd); +#ifdef SQLITE_MMAP_READWRITE + if( (pFd->ctrlFlags & WINFILE_RDONLY)==0 ){ + protect = PAGE_READWRITE; + flags |= FILE_MAP_WRITE; + } #endif - NULL, FALSE -#ifndef NDEBUG - ,WINMEM_MAGIC2 +#if SQLITE_OS_WINRT + pFd->hMap = osCreateFileMappingFromApp(pFd->h, NULL, protect, nMap, NULL); +#elif defined(SQLITE_WIN32_HAS_WIDE) + pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect, + (DWORD)((nMap>>32) & 0xffffffff), + (DWORD)(nMap & 0xffffffff), NULL); +#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA + pFd->hMap = osCreateFileMappingA(pFd->h, NULL, protect, + (DWORD)((nMap>>32) & 0xffffffff), + (DWORD)(nMap & 0xffffffff), NULL); #endif -}; - -#ifndef NDEBUG -#define winMemAssertMagic1() assert( win_mem_data.magic1==WINMEM_MAGIC1 ) -#define winMemAssertMagic2() assert( win_mem_data.magic2==WINMEM_MAGIC2 ) -#define winMemAssertMagic() winMemAssertMagic1(); winMemAssertMagic2(); + if( pFd->hMap==NULL ){ + pFd->lastErrno = osGetLastError(); + rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno, + "winMapfile1", pFd->zPath); + /* Log the error, but continue normal operation using xRead/xWrite */ + OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=%s\n", + osGetCurrentProcessId(), pFd, sqlcipher_sqlite3ErrName(rc))); + return SQLITE_OK; + } + assert( (nMap % winSysInfo.dwPageSize)==0 ); + assert( sizeof(SIZE_T)==sizeof(sqlcipher_sqlite3_int64) || nMap<=0xffffffff ); +#if SQLITE_OS_WINRT + pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, (SIZE_T)nMap); #else -#define winMemAssertMagic() + pNew = osMapViewOfFile(pFd->hMap, flags, 0, 0, (SIZE_T)nMap); #endif + if( pNew==NULL ){ + osCloseHandle(pFd->hMap); + pFd->hMap = NULL; + pFd->lastErrno = osGetLastError(); + rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno, + "winMapfile2", pFd->zPath); + /* Log the error, but continue normal operation using xRead/xWrite */ + OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=%s\n", + osGetCurrentProcessId(), pFd, sqlcipher_sqlite3ErrName(rc))); + return SQLITE_OK; + } + pFd->pMapRegion = pNew; + pFd->mmapSize = nMap; + } -#define winMemGetDataPtr() &win_mem_data -#define winMemGetHeap() win_mem_data.hHeap -#define winMemGetOwned() win_mem_data.bOwned - -static void *winMemMalloc(int nBytes); -static void winMemFree(void *pPrior); -static void *winMemRealloc(void *pPrior, int nBytes); -static int winMemSize(void *p); -static int winMemRoundup(int n); -static int winMemInit(void *pAppData); -static void winMemShutdown(void *pAppData); - -SQLITE_PRIVATE const sqlcipher_sqlite3_mem_methods *sqlcipher_sqlite3MemGetWin32(void); -#endif /* SQLITE_WIN32_MALLOC */ + OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFd)); + return SQLITE_OK; +} +#endif /* SQLITE_MAX_MMAP_SIZE>0 */ /* -** The following variable is (normally) set once and never changes -** thereafter. It records whether the operating system is Win9x -** or WinNT. +** If possible, return a pointer to a mapping of file fd starting at offset +** iOff. The mapping must be valid for at least nAmt bytes. ** -** 0: Operating system unknown. -** 1: Operating system is Win9x. -** 2: Operating system is WinNT. +** If such a pointer can be obtained, store it in *pp and return SQLITE_OK. +** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK. +** Finally, if an error does occur, return an SQLite error code. The final +** value of *pp is undefined in this case. ** -** In order to facilitate testing on a WinNT system, the test fixture -** can manually set this value to 1 to emulate Win98 behavior. +** If this function does return a pointer, the caller must eventually +** release the reference by calling winUnfetch(). */ -#ifdef SQLITE_TEST -SQLITE_API LONG SQLITE_WIN32_VOLATILE sqlcipher_sqlite3_os_type = 0; -#else -static LONG SQLITE_WIN32_VOLATILE sqlcipher_sqlite3_os_type = 0; -#endif - -#ifndef SYSCALL -# define SYSCALL sqlcipher_sqlite3_syscall_ptr +static int winFetch(sqlcipher_sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ +#if SQLITE_MAX_MMAP_SIZE>0 + winFile *pFd = (winFile*)fd; /* The underlying database file */ #endif + *pp = 0; -/* -** This function is not available on Windows CE or WinRT. - */ + OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n", + osGetCurrentProcessId(), fd, iOff, nAmt, pp)); -#if SQLITE_OS_WINCE || SQLITE_OS_WINRT -# define osAreFileApisANSI() 1 +#if SQLITE_MAX_MMAP_SIZE>0 + if( pFd->mmapSizeMax>0 ){ + if( pFd->pMapRegion==0 ){ + int rc = winMapfile(pFd, -1); + if( rc!=SQLITE_OK ){ + OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n", + osGetCurrentProcessId(), pFd, sqlcipher_sqlite3ErrName(rc))); + return rc; + } + } + if( pFd->mmapSize >= iOff+nAmt ){ + assert( pFd->pMapRegion!=0 ); + *pp = &((u8 *)pFd->pMapRegion)[iOff]; + pFd->nFetchOut++; + } + } #endif + OSTRACE(("FETCH pid=%lu, pFile=%p, pp=%p, *pp=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), fd, pp, *pp)); + return SQLITE_OK; +} + /* -** Many system calls are accessed through pointer-to-functions so that -** they may be overridden at runtime to facilitate fault injection during -** testing and sandboxing. The following array holds the names and pointers -** to all overrideable system calls. +** If the third argument is non-NULL, then this function releases a +** reference obtained by an earlier call to winFetch(). The second +** argument passed to this function must be the same as the corresponding +** argument that was passed to the winFetch() invocation. +** +** Or, if the third argument is NULL, then this function is being called +** to inform the VFS layer that, according to POSIX, any existing mapping +** may now be invalid and should be unmapped. */ -static struct win_syscall { - const char *zName; /* Name of the system call */ - sqlcipher_sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ - sqlcipher_sqlite3_syscall_ptr pDefault; /* Default value */ -} aSyscall[] = { -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT - { "AreFileApisANSI", (SYSCALL)AreFileApisANSI, 0 }, -#else - { "AreFileApisANSI", (SYSCALL)0, 0 }, -#endif +static int winUnfetch(sqlcipher_sqlite3_file *fd, i64 iOff, void *p){ +#if SQLITE_MAX_MMAP_SIZE>0 + winFile *pFd = (winFile*)fd; /* The underlying database file */ -#ifndef osAreFileApisANSI -#define osAreFileApisANSI ((BOOL(WINAPI*)(VOID))aSyscall[0].pCurrent) -#endif + /* If p==0 (unmap the entire file) then there must be no outstanding + ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), + ** then there must be at least one outstanding. */ + assert( (p==0)==(pFd->nFetchOut==0) ); -#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) - { "CharLowerW", (SYSCALL)CharLowerW, 0 }, -#else - { "CharLowerW", (SYSCALL)0, 0 }, -#endif + /* If p!=0, it must match the iOff value. */ + assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] ); -#define osCharLowerW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[1].pCurrent) + OSTRACE(("UNFETCH pid=%lu, pFile=%p, offset=%lld, p=%p\n", + osGetCurrentProcessId(), pFd, iOff, p)); -#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) - { "CharUpperW", (SYSCALL)CharUpperW, 0 }, -#else - { "CharUpperW", (SYSCALL)0, 0 }, + if( p ){ + pFd->nFetchOut--; + }else{ + /* FIXME: If Windows truly always prevents truncating or deleting a + ** file while a mapping is held, then the following winUnmapfile() call + ** is unnecessary can be omitted - potentially improving + ** performance. */ + winUnmapfile(pFd); + } + + assert( pFd->nFetchOut>=0 ); #endif -#define osCharUpperW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[2].pCurrent) + OSTRACE(("UNFETCH pid=%lu, pFile=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), fd)); + return SQLITE_OK; +} - { "CloseHandle", (SYSCALL)CloseHandle, 0 }, +/* +** Here ends the implementation of all sqlcipher_sqlite3_file methods. +** +********************** End sqlcipher_sqlite3_file Methods ******************************* +******************************************************************************/ -#define osCloseHandle ((BOOL(WINAPI*)(HANDLE))aSyscall[3].pCurrent) +/* +** This vector defines all the methods that can operate on an +** sqlcipher_sqlite3_file for win32. +*/ +static const sqlcipher_sqlite3_io_methods winIoMethod = { + 3, /* iVersion */ + winClose, /* xClose */ + winRead, /* xRead */ + winWrite, /* xWrite */ + winTruncate, /* xTruncate */ + winSync, /* xSync */ + winFileSize, /* xFileSize */ + winLock, /* xLock */ + winUnlock, /* xUnlock */ + winCheckReservedLock, /* xCheckReservedLock */ + winFileControl, /* xFileControl */ + winSectorSize, /* xSectorSize */ + winDeviceCharacteristics, /* xDeviceCharacteristics */ + winShmMap, /* xShmMap */ + winShmLock, /* xShmLock */ + winShmBarrier, /* xShmBarrier */ + winShmUnmap, /* xShmUnmap */ + winFetch, /* xFetch */ + winUnfetch /* xUnfetch */ +}; -#if defined(SQLITE_WIN32_HAS_ANSI) - { "CreateFileA", (SYSCALL)CreateFileA, 0 }, -#else - { "CreateFileA", (SYSCALL)0, 0 }, -#endif +/* +** This vector defines all the methods that can operate on an +** sqlcipher_sqlite3_file for win32 without performing any locking. +*/ +static const sqlcipher_sqlite3_io_methods winIoNolockMethod = { + 3, /* iVersion */ + winClose, /* xClose */ + winRead, /* xRead */ + winWrite, /* xWrite */ + winTruncate, /* xTruncate */ + winSync, /* xSync */ + winFileSize, /* xFileSize */ + winNolockLock, /* xLock */ + winNolockUnlock, /* xUnlock */ + winNolockCheckReservedLock, /* xCheckReservedLock */ + winFileControl, /* xFileControl */ + winSectorSize, /* xSectorSize */ + winDeviceCharacteristics, /* xDeviceCharacteristics */ + winShmMap, /* xShmMap */ + winShmLock, /* xShmLock */ + winShmBarrier, /* xShmBarrier */ + winShmUnmap, /* xShmUnmap */ + winFetch, /* xFetch */ + winUnfetch /* xUnfetch */ +}; -#define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \ - LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent) +static winVfsAppData winAppData = { + &winIoMethod, /* pMethod */ + 0, /* pAppData */ + 0 /* bNoLock */ +}; -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "CreateFileW", (SYSCALL)CreateFileW, 0 }, -#else - { "CreateFileW", (SYSCALL)0, 0 }, -#endif +static winVfsAppData winNolockAppData = { + &winIoNolockMethod, /* pMethod */ + 0, /* pAppData */ + 1 /* bNoLock */ +}; -#define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \ - LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent) +/**************************************************************************** +**************************** sqlcipher_sqlite3_vfs methods **************************** +** +** This division contains the implementation of methods on the +** sqlcipher_sqlite3_vfs object. +*/ -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \ - (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) && \ - SQLITE_WIN32_CREATEFILEMAPPINGA - { "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 }, -#else - { "CreateFileMappingA", (SYSCALL)0, 0 }, +#if defined(__CYGWIN__) +/* +** Convert a filename from whatever the underlying operating system +** supports for filenames into UTF-8. Space to hold the result is +** obtained from malloc and must be freed by the calling function. +*/ +static char *winConvertToUtf8Filename(const void *zFilename){ + char *zConverted = 0; + if( osIsNT() ){ + zConverted = winUnicodeToUtf8(zFilename); + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + zConverted = winMbcsToUtf8(zFilename, osAreFileApisANSI()); + } #endif - -#define osCreateFileMappingA ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ - DWORD,DWORD,DWORD,LPCSTR))aSyscall[6].pCurrent) - -#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ - (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)) - { "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 }, -#else - { "CreateFileMappingW", (SYSCALL)0, 0 }, + /* caller will handle out of memory */ + return zConverted; +} #endif -#define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ - DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent) - -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "CreateMutexW", (SYSCALL)CreateMutexW, 0 }, -#else - { "CreateMutexW", (SYSCALL)0, 0 }, +/* +** Convert a UTF-8 filename into whatever form the underlying +** operating system wants filenames in. Space to hold the result +** is obtained from malloc and must be freed by the calling +** function. +*/ +static void *winConvertFromUtf8Filename(const char *zFilename){ + void *zConverted = 0; + if( osIsNT() ){ + zConverted = winUtf8ToUnicode(zFilename); + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI()); + } #endif + /* caller will handle out of memory */ + return zConverted; +} -#define osCreateMutexW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,BOOL, \ - LPCWSTR))aSyscall[8].pCurrent) - -#if defined(SQLITE_WIN32_HAS_ANSI) - { "DeleteFileA", (SYSCALL)DeleteFileA, 0 }, -#else - { "DeleteFileA", (SYSCALL)0, 0 }, -#endif +/* +** This function returns non-zero if the specified UTF-8 string buffer +** ends with a directory separator character or one was successfully +** added to it. +*/ +static int winMakeEndInDirSep(int nBuf, char *zBuf){ + if( zBuf ){ + int nLen = sqlcipher_sqlite3Strlen30(zBuf); + if( nLen>0 ){ + if( winIsDirSep(zBuf[nLen-1]) ){ + return 1; + }else if( nLen+1mxPathname; nBuf = nMax + 2; + zBuf = sqlcipher_sqlite3MallocZero( nBuf ); + if( !zBuf ){ + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); + return SQLITE_IOERR_NOMEM_BKPT; + } -#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \ - LPFILETIME))aSyscall[11].pCurrent) + /* Figure out the effective temporary directory. First, check if one + ** has been explicitly set by the application; otherwise, use the one + ** configured by the operating system. + */ + nDir = nMax - (nPre + 15); + assert( nDir>0 ); + if( winTempDirDefined() ){ + int nDirLen = sqlcipher_sqlite3Strlen30(sqlcipher_sqlite3_temp_directory); + if( nDirLen>0 ){ + if( !winIsDirSep(sqlcipher_sqlite3_temp_directory[nDirLen-1]) ){ + nDirLen++; + } + if( nDirLen>nDir ){ + sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + sqlcipher_sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); + return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0); + } + sqlcipher_sqlite3_snprintf(nMax, zBuf, "%s", sqlcipher_sqlite3_temp_directory); + } + sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + } -#if SQLITE_OS_WINCE - { "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 }, -#else - { "FileTimeToSystemTime", (SYSCALL)0, 0 }, -#endif +#if defined(__CYGWIN__) + else{ + static const char *azDirs[] = { + 0, /* getenv("SQLITE_TMPDIR") */ + 0, /* getenv("TMPDIR") */ + 0, /* getenv("TMP") */ + 0, /* getenv("TEMP") */ + 0, /* getenv("USERPROFILE") */ + "/var/tmp", + "/usr/tmp", + "/tmp", + ".", + 0 /* List terminator */ + }; + unsigned int i; + const char *zDir = 0; -#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \ - LPSYSTEMTIME))aSyscall[12].pCurrent) + if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); + if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); + if( !azDirs[2] ) azDirs[2] = getenv("TMP"); + if( !azDirs[3] ) azDirs[3] = getenv("TEMP"); + if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE"); + for(i=0; i/etilqs_XXXXXXXXXXXXXXX\0\0" + ** + ** If not, return SQLITE_ERROR. The number 17 is used here in order to + ** account for the space used by the 15 character random suffix and the + ** two trailing NUL characters. The final directory separator character + ** has already added if it was not already present. + */ + nLen = sqlcipher_sqlite3Strlen30(zBuf); + if( (nLen + nPre + 17) > nBuf ){ + sqlcipher_sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); + return winLogError(SQLITE_ERROR, 0, "winGetTempname5", 0); + } -#if defined(SQLITE_WIN32_HAS_ANSI) - { "FormatMessageA", (SYSCALL)FormatMessageA, 0 }, -#else - { "FormatMessageA", (SYSCALL)0, 0 }, -#endif + sqlcipher_sqlite3_snprintf(nBuf-16-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX); -#define osFormatMessageA ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPSTR, \ - DWORD,va_list*))aSyscall[14].pCurrent) + j = sqlcipher_sqlite3Strlen30(zBuf); + sqlcipher_sqlite3_randomness(15, &zBuf[j]); + for(i=0; i<15; i++, j++){ + zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; + } + zBuf[j] = 0; + zBuf[j+1] = 0; + *pzBuf = zBuf; -#if defined(SQLITE_WIN32_HAS_WIDE) - { "FormatMessageW", (SYSCALL)FormatMessageW, 0 }, -#else - { "FormatMessageW", (SYSCALL)0, 0 }, -#endif + OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf)); + return SQLITE_OK; +} -#define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \ - DWORD,va_list*))aSyscall[15].pCurrent) +/* +** Return TRUE if the named file is really a directory. Return false if +** it is something other than a directory, or if there is any kind of memory +** allocation failure. +*/ +static int winIsDir(const void *zConverted){ + DWORD attr; + int rc = 0; + DWORD lastErrno; -#if !defined(SQLITE_OMIT_LOAD_EXTENSION) - { "FreeLibrary", (SYSCALL)FreeLibrary, 0 }, -#else - { "FreeLibrary", (SYSCALL)0, 0 }, + if( osIsNT() ){ + int cnt = 0; + WIN32_FILE_ATTRIBUTE_DATA sAttrData; + memset(&sAttrData, 0, sizeof(sAttrData)); + while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, + GetFileExInfoStandard, + &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} + if( !rc ){ + return 0; /* Invalid name? */ + } + attr = sAttrData.dwFileAttributes; +#if SQLITE_OS_WINCE==0 + }else{ + attr = osGetFileAttributesA((char*)zConverted); #endif + } + return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY); +} -#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[16].pCurrent) - - { "GetCurrentProcessId", (SYSCALL)GetCurrentProcessId, 0 }, - -#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[17].pCurrent) +/* forward reference */ +static int winAccess( + sqlcipher_sqlite3_vfs *pVfs, /* Not used on win32 */ + const char *zFilename, /* Name of file to check */ + int flags, /* Type of test to make on this file */ + int *pResOut /* OUT: Result */ +); -#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) - { "GetDiskFreeSpaceA", (SYSCALL)GetDiskFreeSpaceA, 0 }, -#else - { "GetDiskFreeSpaceA", (SYSCALL)0, 0 }, +/* +** Open a file. +*/ +static int winOpen( + sqlcipher_sqlite3_vfs *pVfs, /* Used to get maximum path length and AppData */ + const char *zName, /* Name of the file (UTF-8) */ + sqlcipher_sqlite3_file *id, /* Write the SQLite file handle here */ + int flags, /* Open mode flags */ + int *pOutFlags /* Status return flags */ +){ + HANDLE h; + DWORD lastErrno = 0; + DWORD dwDesiredAccess; + DWORD dwShareMode; + DWORD dwCreationDisposition; + DWORD dwFlagsAndAttributes = 0; +#if SQLITE_OS_WINCE + int isTemp = 0; #endif + winVfsAppData *pAppData; + winFile *pFile = (winFile*)id; + void *zConverted; /* Filename in OS encoding */ + const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ + int cnt = 0; -#define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \ - LPDWORD))aSyscall[18].pCurrent) + /* If argument zPath is a NULL pointer, this function is required to open + ** a temporary file. Use this buffer to store the file name in. + */ + char *zTmpname = 0; /* For temporary filename, if necessary. */ -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "GetDiskFreeSpaceW", (SYSCALL)GetDiskFreeSpaceW, 0 }, -#else - { "GetDiskFreeSpaceW", (SYSCALL)0, 0 }, + int rc = SQLITE_OK; /* Function Return Code */ +#if !defined(NDEBUG) || SQLITE_OS_WINCE + int eType = flags&0xFFFFFF00; /* Type of file to open */ #endif -#define osGetDiskFreeSpaceW ((BOOL(WINAPI*)(LPCWSTR,LPDWORD,LPDWORD,LPDWORD, \ - LPDWORD))aSyscall[19].pCurrent) + int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); + int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); + int isCreate = (flags & SQLITE_OPEN_CREATE); + int isReadonly = (flags & SQLITE_OPEN_READONLY); + int isReadWrite = (flags & SQLITE_OPEN_READWRITE); -#if defined(SQLITE_WIN32_HAS_ANSI) - { "GetFileAttributesA", (SYSCALL)GetFileAttributesA, 0 }, -#else - { "GetFileAttributesA", (SYSCALL)0, 0 }, +#ifndef NDEBUG + int isOpenJournal = (isCreate && ( + eType==SQLITE_OPEN_SUPER_JOURNAL + || eType==SQLITE_OPEN_MAIN_JOURNAL + || eType==SQLITE_OPEN_WAL + )); #endif -#define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[20].pCurrent) + OSTRACE(("OPEN name=%s, pFile=%p, flags=%x, pOutFlags=%p\n", + zUtf8Name, id, flags, pOutFlags)); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "GetFileAttributesW", (SYSCALL)GetFileAttributesW, 0 }, -#else - { "GetFileAttributesW", (SYSCALL)0, 0 }, -#endif + /* Check the following statements are true: + ** + ** (a) Exactly one of the READWRITE and READONLY flags must be set, and + ** (b) if CREATE is set, then READWRITE must also be set, and + ** (c) if EXCLUSIVE is set, then CREATE must also be set. + ** (d) if DELETEONCLOSE is set, then CREATE must also be set. + */ + assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); + assert(isCreate==0 || isReadWrite); + assert(isExclusive==0 || isCreate); + assert(isDelete==0 || isCreate); -#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[21].pCurrent) + /* The main DB, main journal, WAL file and super-journal are never + ** automatically deleted. Nor are they ever temporary files. */ + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_SUPER_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); -#if defined(SQLITE_WIN32_HAS_WIDE) - { "GetFileAttributesExW", (SYSCALL)GetFileAttributesExW, 0 }, -#else - { "GetFileAttributesExW", (SYSCALL)0, 0 }, -#endif + /* Assert that the upper layer has set one of the "file-type" flags. */ + assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB + || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL + || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL + || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL + ); -#define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \ - LPVOID))aSyscall[22].pCurrent) + assert( pFile!=0 ); + memset(pFile, 0, sizeof(winFile)); + pFile->h = INVALID_HANDLE_VALUE; -#if !SQLITE_OS_WINRT - { "GetFileSize", (SYSCALL)GetFileSize, 0 }, -#else - { "GetFileSize", (SYSCALL)0, 0 }, +#if SQLITE_OS_WINRT + if( !zUtf8Name && !sqlcipher_sqlite3_temp_directory ){ + sqlcipher_sqlite3_log(SQLITE_ERROR, + "sqlcipher_sqlite3_temp_directory variable should be set for WinRT"); + } #endif -#define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[23].pCurrent) + /* If the second argument to this function is NULL, generate a + ** temporary file name to use + */ + if( !zUtf8Name ){ + assert( isDelete && !isOpenJournal ); + rc = winGetTempname(pVfs, &zTmpname); + if( rc!=SQLITE_OK ){ + OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlcipher_sqlite3ErrName(rc))); + return rc; + } + zUtf8Name = zTmpname; + } -#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) - { "GetFullPathNameA", (SYSCALL)GetFullPathNameA, 0 }, -#else - { "GetFullPathNameA", (SYSCALL)0, 0 }, -#endif + /* Database filenames are double-zero terminated if they are not + ** URIs with parameters. Hence, they can always be passed into + ** sqlcipher_sqlite3_uri_parameter(). + */ + assert( (eType!=SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) || + zUtf8Name[sqlcipher_sqlite3Strlen30(zUtf8Name)+1]==0 ); -#define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \ - LPSTR*))aSyscall[24].pCurrent) + /* Convert the filename to the system encoding. */ + zConverted = winConvertFromUtf8Filename(zUtf8Name); + if( zConverted==0 ){ + sqlcipher_sqlite3_free(zTmpname); + OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name)); + return SQLITE_IOERR_NOMEM_BKPT; + } -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "GetFullPathNameW", (SYSCALL)GetFullPathNameW, 0 }, -#else - { "GetFullPathNameW", (SYSCALL)0, 0 }, -#endif + if( winIsDir(zConverted) ){ + sqlcipher_sqlite3_free(zConverted); + sqlcipher_sqlite3_free(zTmpname); + OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name)); + return SQLITE_CANTOPEN_ISDIR; + } -#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \ - LPWSTR*))aSyscall[25].pCurrent) + if( isReadWrite ){ + dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; + }else{ + dwDesiredAccess = GENERIC_READ; + } - { "GetLastError", (SYSCALL)GetLastError, 0 }, + /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is + ** created. SQLite doesn't use it to indicate "exclusive access" + ** as it is usually understood. + */ + if( isExclusive ){ + /* Creates a new file, only if it does not already exist. */ + /* If the file exists, it fails. */ + dwCreationDisposition = CREATE_NEW; + }else if( isCreate ){ + /* Open existing file, or create if it doesn't exist */ + dwCreationDisposition = OPEN_ALWAYS; + }else{ + /* Opens a file, only if it exists. */ + dwCreationDisposition = OPEN_EXISTING; + } -#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent) + if( 0==sqlcipher_sqlite3_uri_boolean(zName, "exclusive", 0) ){ + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + }else{ + dwShareMode = 0; + } -#if !defined(SQLITE_OMIT_LOAD_EXTENSION) + if( isDelete ){ #if SQLITE_OS_WINCE - /* The GetProcAddressA() routine is only available on Windows CE. */ - { "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 }, + dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN; + isTemp = 1; #else - /* All other Windows platforms expect GetProcAddress() to take - ** an ANSI string regardless of the _UNICODE setting */ - { "GetProcAddressA", (SYSCALL)GetProcAddress, 0 }, + dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY + | FILE_ATTRIBUTE_HIDDEN + | FILE_FLAG_DELETE_ON_CLOSE; #endif -#else - { "GetProcAddressA", (SYSCALL)0, 0 }, + }else{ + dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; + } + /* Reports from the internet are that performance is always + ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */ +#if SQLITE_OS_WINCE + dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; #endif -#define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \ - LPCSTR))aSyscall[27].pCurrent) - -#if !SQLITE_OS_WINRT - { "GetSystemInfo", (SYSCALL)GetSystemInfo, 0 }, + if( osIsNT() ){ +#if SQLITE_OS_WINRT + CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; + extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); + extendedParameters.dwFileAttributes = + dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK; + extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK; + extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; + extendedParameters.lpSecurityAttributes = NULL; + extendedParameters.hTemplateFile = NULL; + do{ + h = osCreateFile2((LPCWSTR)zConverted, + dwDesiredAccess, + dwShareMode, + dwCreationDisposition, + &extendedParameters); + if( h!=INVALID_HANDLE_VALUE ) break; + if( isReadWrite ){ + int rc2, isRO = 0; + sqlcipher_sqlite3BeginBenignMalloc(); + rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); + sqlcipher_sqlite3EndBenignMalloc(); + if( rc2==SQLITE_OK && isRO ) break; + } + }while( winRetryIoerr(&cnt, &lastErrno) ); #else - { "GetSystemInfo", (SYSCALL)0, 0 }, + do{ + h = osCreateFileW((LPCWSTR)zConverted, + dwDesiredAccess, + dwShareMode, NULL, + dwCreationDisposition, + dwFlagsAndAttributes, + NULL); + if( h!=INVALID_HANDLE_VALUE ) break; + if( isReadWrite ){ + int rc2, isRO = 0; + sqlcipher_sqlite3BeginBenignMalloc(); + rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); + sqlcipher_sqlite3EndBenignMalloc(); + if( rc2==SQLITE_OK && isRO ) break; + } + }while( winRetryIoerr(&cnt, &lastErrno) ); +#endif + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + do{ + h = osCreateFileA((LPCSTR)zConverted, + dwDesiredAccess, + dwShareMode, NULL, + dwCreationDisposition, + dwFlagsAndAttributes, + NULL); + if( h!=INVALID_HANDLE_VALUE ) break; + if( isReadWrite ){ + int rc2, isRO = 0; + sqlcipher_sqlite3BeginBenignMalloc(); + rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); + sqlcipher_sqlite3EndBenignMalloc(); + if( rc2==SQLITE_OK && isRO ) break; + } + }while( winRetryIoerr(&cnt, &lastErrno) ); + } #endif + winLogIoerr(cnt, __LINE__); -#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[28].pCurrent) + OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name, + dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); - { "GetSystemTime", (SYSCALL)GetSystemTime, 0 }, + if( h==INVALID_HANDLE_VALUE ){ + sqlcipher_sqlite3_free(zConverted); + sqlcipher_sqlite3_free(zTmpname); + if( isReadWrite && !isExclusive ){ + return winOpen(pVfs, zName, id, + ((flags|SQLITE_OPEN_READONLY) & + ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), + pOutFlags); + }else{ + pFile->lastErrno = lastErrno; + winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name); + return SQLITE_CANTOPEN_BKPT; + } + } -#define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[29].pCurrent) + if( pOutFlags ){ + if( isReadWrite ){ + *pOutFlags = SQLITE_OPEN_READWRITE; + }else{ + *pOutFlags = SQLITE_OPEN_READONLY; + } + } -#if !SQLITE_OS_WINCE - { "GetSystemTimeAsFileTime", (SYSCALL)GetSystemTimeAsFileTime, 0 }, -#else - { "GetSystemTimeAsFileTime", (SYSCALL)0, 0 }, -#endif + OSTRACE(("OPEN file=%p, name=%s, access=%lx, pOutFlags=%p, *pOutFlags=%d, " + "rc=%s\n", h, zUtf8Name, dwDesiredAccess, pOutFlags, pOutFlags ? + *pOutFlags : 0, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); -#define osGetSystemTimeAsFileTime ((VOID(WINAPI*)( \ - LPFILETIME))aSyscall[30].pCurrent) + pAppData = (winVfsAppData*)pVfs->pAppData; -#if defined(SQLITE_WIN32_HAS_ANSI) - { "GetTempPathA", (SYSCALL)GetTempPathA, 0 }, -#else - { "GetTempPathA", (SYSCALL)0, 0 }, +#if SQLITE_OS_WINCE + { + if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB + && ((pAppData==NULL) || !pAppData->bNoLock) + && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK + ){ + osCloseHandle(h); + sqlcipher_sqlite3_free(zConverted); + sqlcipher_sqlite3_free(zTmpname); + OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlcipher_sqlite3ErrName(rc))); + return rc; + } + } + if( isTemp ){ + pFile->zDeleteOnClose = zConverted; + }else #endif + { + sqlcipher_sqlite3_free(zConverted); + } -#define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[31].pCurrent) - -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) - { "GetTempPathW", (SYSCALL)GetTempPathW, 0 }, -#else - { "GetTempPathW", (SYSCALL)0, 0 }, + sqlcipher_sqlite3_free(zTmpname); + id->pMethods = pAppData ? pAppData->pMethod : &winIoMethod; + pFile->pVfs = pVfs; + pFile->h = h; + if( isReadonly ){ + pFile->ctrlFlags |= WINFILE_RDONLY; + } + if( (flags & SQLITE_OPEN_MAIN_DB) + && sqlcipher_sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) + ){ + pFile->ctrlFlags |= WINFILE_PSOW; + } + pFile->lastErrno = NO_ERROR; + pFile->zPath = zName; +#if SQLITE_MAX_MMAP_SIZE>0 + pFile->hMap = NULL; + pFile->pMapRegion = 0; + pFile->mmapSize = 0; + pFile->mmapSizeMax = sqlcipher_sqlite3GlobalConfig.szMmap; #endif -#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[32].pCurrent) + OpenCounter(+1); + return rc; +} -#if !SQLITE_OS_WINRT - { "GetTickCount", (SYSCALL)GetTickCount, 0 }, -#else - { "GetTickCount", (SYSCALL)0, 0 }, -#endif +/* +** Delete the named file. +** +** Note that Windows does not allow a file to be deleted if some other +** process has it open. Sometimes a virus scanner or indexing program +** will open a journal file shortly after it is created in order to do +** whatever it does. While this other process is holding the +** file open, we will be unable to delete it. To work around this +** problem, we delay 100 milliseconds and try to delete again. Up +** to MX_DELETION_ATTEMPTs deletion attempts are run before giving +** up and returning an error. +*/ +static int winDelete( + sqlcipher_sqlite3_vfs *pVfs, /* Not used on win32 */ + const char *zFilename, /* Name of file to delete */ + int syncDir /* Not used on win32 */ +){ + int cnt = 0; + int rc; + DWORD attr; + DWORD lastErrno = 0; + void *zConverted; + UNUSED_PARAMETER(pVfs); + UNUSED_PARAMETER(syncDir); -#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent) + SimulateIOError(return SQLITE_IOERR_DELETE); + OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir)); -#if defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_GETVERSIONEX - { "GetVersionExA", (SYSCALL)GetVersionExA, 0 }, + zConverted = winConvertFromUtf8Filename(zFilename); + if( zConverted==0 ){ + OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); + return SQLITE_IOERR_NOMEM_BKPT; + } + if( osIsNT() ){ + do { +#if SQLITE_OS_WINRT + WIN32_FILE_ATTRIBUTE_DATA sAttrData; + memset(&sAttrData, 0, sizeof(sAttrData)); + if ( osGetFileAttributesExW(zConverted, GetFileExInfoStandard, + &sAttrData) ){ + attr = sAttrData.dwFileAttributes; + }else{ + lastErrno = osGetLastError(); + if( lastErrno==ERROR_FILE_NOT_FOUND + || lastErrno==ERROR_PATH_NOT_FOUND ){ + rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ + }else{ + rc = SQLITE_ERROR; + } + break; + } #else - { "GetVersionExA", (SYSCALL)0, 0 }, + attr = osGetFileAttributesW(zConverted); #endif - -#define osGetVersionExA ((BOOL(WINAPI*)( \ - LPOSVERSIONINFOA))aSyscall[34].pCurrent) - -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ - SQLITE_WIN32_GETVERSIONEX - { "GetVersionExW", (SYSCALL)GetVersionExW, 0 }, -#else - { "GetVersionExW", (SYSCALL)0, 0 }, + if ( attr==INVALID_FILE_ATTRIBUTES ){ + lastErrno = osGetLastError(); + if( lastErrno==ERROR_FILE_NOT_FOUND + || lastErrno==ERROR_PATH_NOT_FOUND ){ + rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ + }else{ + rc = SQLITE_ERROR; + } + break; + } + if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ + rc = SQLITE_ERROR; /* Files only. */ + break; + } + if ( osDeleteFileW(zConverted) ){ + rc = SQLITE_OK; /* Deleted OK. */ + break; + } + if ( !winRetryIoerr(&cnt, &lastErrno) ){ + rc = SQLITE_ERROR; /* No more retries. */ + break; + } + } while(1); + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + do { + attr = osGetFileAttributesA(zConverted); + if ( attr==INVALID_FILE_ATTRIBUTES ){ + lastErrno = osGetLastError(); + if( lastErrno==ERROR_FILE_NOT_FOUND + || lastErrno==ERROR_PATH_NOT_FOUND ){ + rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ + }else{ + rc = SQLITE_ERROR; + } + break; + } + if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ + rc = SQLITE_ERROR; /* Files only. */ + break; + } + if ( osDeleteFileA(zConverted) ){ + rc = SQLITE_OK; /* Deleted OK. */ + break; + } + if ( !winRetryIoerr(&cnt, &lastErrno) ){ + rc = SQLITE_ERROR; /* No more retries. */ + break; + } + } while(1); + } #endif + if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){ + rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename); + }else{ + winLogIoerr(cnt, __LINE__); + } + sqlcipher_sqlite3_free(zConverted); + OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlcipher_sqlite3ErrName(rc))); + return rc; +} -#define osGetVersionExW ((BOOL(WINAPI*)( \ - LPOSVERSIONINFOW))aSyscall[35].pCurrent) - - { "HeapAlloc", (SYSCALL)HeapAlloc, 0 }, - -#define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \ - SIZE_T))aSyscall[36].pCurrent) - -#if !SQLITE_OS_WINRT - { "HeapCreate", (SYSCALL)HeapCreate, 0 }, -#else - { "HeapCreate", (SYSCALL)0, 0 }, -#endif +/* +** Check the existence and status of a file. +*/ +static int winAccess( + sqlcipher_sqlite3_vfs *pVfs, /* Not used on win32 */ + const char *zFilename, /* Name of file to check */ + int flags, /* Type of test to make on this file */ + int *pResOut /* OUT: Result */ +){ + DWORD attr; + int rc = 0; + DWORD lastErrno = 0; + void *zConverted; + UNUSED_PARAMETER(pVfs); -#define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \ - SIZE_T))aSyscall[37].pCurrent) + SimulateIOError( return SQLITE_IOERR_ACCESS; ); + OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", + zFilename, flags, pResOut)); -#if !SQLITE_OS_WINRT - { "HeapDestroy", (SYSCALL)HeapDestroy, 0 }, -#else - { "HeapDestroy", (SYSCALL)0, 0 }, + zConverted = winConvertFromUtf8Filename(zFilename); + if( zConverted==0 ){ + OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); + return SQLITE_IOERR_NOMEM_BKPT; + } + if( osIsNT() ){ + int cnt = 0; + WIN32_FILE_ATTRIBUTE_DATA sAttrData; + memset(&sAttrData, 0, sizeof(sAttrData)); + while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, + GetFileExInfoStandard, + &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} + if( rc ){ + /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file + ** as if it does not exist. + */ + if( flags==SQLITE_ACCESS_EXISTS + && sAttrData.nFileSizeHigh==0 + && sAttrData.nFileSizeLow==0 ){ + attr = INVALID_FILE_ATTRIBUTES; + }else{ + attr = sAttrData.dwFileAttributes; + } + }else{ + winLogIoerr(cnt, __LINE__); + if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ + sqlcipher_sqlite3_free(zConverted); + return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", + zFilename); + }else{ + attr = INVALID_FILE_ATTRIBUTES; + } + } + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + attr = osGetFileAttributesA((char*)zConverted); + } #endif + sqlcipher_sqlite3_free(zConverted); + switch( flags ){ + case SQLITE_ACCESS_READ: + case SQLITE_ACCESS_EXISTS: + rc = attr!=INVALID_FILE_ATTRIBUTES; + break; + case SQLITE_ACCESS_READWRITE: + rc = attr!=INVALID_FILE_ATTRIBUTES && + (attr & FILE_ATTRIBUTE_READONLY)==0; + break; + default: + assert(!"Invalid flags argument"); + } + *pResOut = rc; + OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", + zFilename, pResOut, *pResOut)); + return SQLITE_OK; +} -#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[38].pCurrent) - - { "HeapFree", (SYSCALL)HeapFree, 0 }, - -#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[39].pCurrent) +/* +** Returns non-zero if the specified path name starts with the "long path" +** prefix. +*/ +static BOOL winIsLongPathPrefix( + const char *zPathname +){ + return ( zPathname[0]=='\\' && zPathname[1]=='\\' + && zPathname[2]=='?' && zPathname[3]=='\\' ); +} - { "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 }, +/* +** Returns non-zero if the specified path name starts with a drive letter +** followed by a colon character. +*/ +static BOOL winIsDriveLetterAndColon( + const char *zPathname +){ + return ( sqlcipher_sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' ); +} -#define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \ - SIZE_T))aSyscall[40].pCurrent) +/* +** Returns non-zero if the specified path name should be used verbatim. If +** non-zero is returned from this function, the calling function must simply +** use the provided path name verbatim -OR- resolve it into a full path name +** using the GetFullPathName Win32 API function (if available). +*/ +static BOOL winIsVerbatimPathname( + const char *zPathname +){ + /* + ** If the path name starts with a forward slash or a backslash, it is either + ** a legal UNC name, a volume relative path, or an absolute path name in the + ** "Unix" format on Windows. There is no easy way to differentiate between + ** the final two cases; therefore, we return the safer return value of TRUE + ** so that callers of this function will simply use it verbatim. + */ + if ( winIsDirSep(zPathname[0]) ){ + return TRUE; + } - { "HeapSize", (SYSCALL)HeapSize, 0 }, + /* + ** If the path name starts with a letter and a colon it is either a volume + ** relative path or an absolute path. Callers of this function must not + ** attempt to treat it as a relative path name (i.e. they should simply use + ** it verbatim). + */ + if ( winIsDriveLetterAndColon(zPathname) ){ + return TRUE; + } -#define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \ - LPCVOID))aSyscall[41].pCurrent) + /* + ** If we get to this point, the path name should almost certainly be a purely + ** relative one (i.e. not a UNC name, not absolute, and not volume relative). + */ + return FALSE; +} -#if !SQLITE_OS_WINRT - { "HeapValidate", (SYSCALL)HeapValidate, 0 }, -#else - { "HeapValidate", (SYSCALL)0, 0 }, +/* +** Turn a relative pathname into a full pathname. Write the full +** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname +** bytes in size. +*/ +static int winFullPathnameNoMutex( + sqlcipher_sqlite3_vfs *pVfs, /* Pointer to vfs object */ + const char *zRelative, /* Possibly relative input path */ + int nFull, /* Size of output buffer in bytes */ + char *zFull /* Output buffer */ +){ +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) + DWORD nByte; + void *zConverted; + char *zOut; #endif -#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \ - LPCVOID))aSyscall[42].pCurrent) + /* If this path name begins with "/X:" or "\\?\", where "X" is any + ** alphabetic character, discard the initial "/" from the pathname. + */ + if( zRelative[0]=='/' && (winIsDriveLetterAndColon(zRelative+1) + || winIsLongPathPrefix(zRelative+1)) ){ + zRelative++; + } -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT - { "HeapCompact", (SYSCALL)HeapCompact, 0 }, -#else - { "HeapCompact", (SYSCALL)0, 0 }, +#if defined(__CYGWIN__) + SimulateIOError( return SQLITE_ERROR ); + UNUSED_PARAMETER(nFull); + assert( nFull>=pVfs->mxPathname ); + if ( sqlcipher_sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ + /* + ** NOTE: We are dealing with a relative path name and the data + ** directory has been set. Therefore, use it as the basis + ** for converting the relative path name to an absolute + ** one by prepending the data directory and a slash. + */ + char *zOut = sqlcipher_sqlite3MallocZero( pVfs->mxPathname+1 ); + if( !zOut ){ + return SQLITE_IOERR_NOMEM_BKPT; + } + if( cygwin_conv_path( + (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) | + CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){ + sqlcipher_sqlite3_free(zOut); + return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, + "winFullPathname1", zRelative); + }else{ + char *zUtf8 = winConvertToUtf8Filename(zOut); + if( !zUtf8 ){ + sqlcipher_sqlite3_free(zOut); + return SQLITE_IOERR_NOMEM_BKPT; + } + sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", + sqlcipher_sqlite3_data_directory, winGetDirSep(), zUtf8); + sqlcipher_sqlite3_free(zUtf8); + sqlcipher_sqlite3_free(zOut); + } + }else{ + char *zOut = sqlcipher_sqlite3MallocZero( pVfs->mxPathname+1 ); + if( !zOut ){ + return SQLITE_IOERR_NOMEM_BKPT; + } + if( cygwin_conv_path( + (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A), + zRelative, zOut, pVfs->mxPathname+1)<0 ){ + sqlcipher_sqlite3_free(zOut); + return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, + "winFullPathname2", zRelative); + }else{ + char *zUtf8 = winConvertToUtf8Filename(zOut); + if( !zUtf8 ){ + sqlcipher_sqlite3_free(zOut); + return SQLITE_IOERR_NOMEM_BKPT; + } + sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8); + sqlcipher_sqlite3_free(zUtf8); + sqlcipher_sqlite3_free(zOut); + } + } + return SQLITE_OK; #endif -#define osHeapCompact ((UINT(WINAPI*)(HANDLE,DWORD))aSyscall[43].pCurrent) - -#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION) - { "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 }, -#else - { "LoadLibraryA", (SYSCALL)0, 0 }, +#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__) + SimulateIOError( return SQLITE_ERROR ); + /* WinCE has no concept of a relative pathname, or so I am told. */ + /* WinRT has no way to convert a relative path to an absolute one. */ + if ( sqlcipher_sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ + /* + ** NOTE: We are dealing with a relative path name and the data + ** directory has been set. Therefore, use it as the basis + ** for converting the relative path name to an absolute + ** one by prepending the data directory and a backslash. + */ + sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", + sqlcipher_sqlite3_data_directory, winGetDirSep(), zRelative); + }else{ + sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative); + } + return SQLITE_OK; #endif -#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[44].pCurrent) - -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ - !defined(SQLITE_OMIT_LOAD_EXTENSION) - { "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 }, -#else - { "LoadLibraryW", (SYSCALL)0, 0 }, +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) + /* It's odd to simulate an io-error here, but really this is just + ** using the io-error infrastructure to test that SQLite handles this + ** function failing. This function could fail if, for example, the + ** current working directory has been unlinked. + */ + SimulateIOError( return SQLITE_ERROR ); + if ( sqlcipher_sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ + /* + ** NOTE: We are dealing with a relative path name and the data + ** directory has been set. Therefore, use it as the basis + ** for converting the relative path name to an absolute + ** one by prepending the data directory and a backslash. + */ + sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", + sqlcipher_sqlite3_data_directory, winGetDirSep(), zRelative); + return SQLITE_OK; + } + zConverted = winConvertFromUtf8Filename(zRelative); + if( zConverted==0 ){ + return SQLITE_IOERR_NOMEM_BKPT; + } + if( osIsNT() ){ + LPWSTR zTemp; + nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0); + if( nByte==0 ){ + sqlcipher_sqlite3_free(zConverted); + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), + "winFullPathname1", zRelative); + } + nByte += 3; + zTemp = sqlcipher_sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); + if( zTemp==0 ){ + sqlcipher_sqlite3_free(zConverted); + return SQLITE_IOERR_NOMEM_BKPT; + } + nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0); + if( nByte==0 ){ + sqlcipher_sqlite3_free(zConverted); + sqlcipher_sqlite3_free(zTemp); + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), + "winFullPathname2", zRelative); + } + sqlcipher_sqlite3_free(zConverted); + zOut = winUnicodeToUtf8(zTemp); + sqlcipher_sqlite3_free(zTemp); + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + char *zTemp; + nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0); + if( nByte==0 ){ + sqlcipher_sqlite3_free(zConverted); + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), + "winFullPathname3", zRelative); + } + nByte += 3; + zTemp = sqlcipher_sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); + if( zTemp==0 ){ + sqlcipher_sqlite3_free(zConverted); + return SQLITE_IOERR_NOMEM_BKPT; + } + nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0); + if( nByte==0 ){ + sqlcipher_sqlite3_free(zConverted); + sqlcipher_sqlite3_free(zTemp); + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), + "winFullPathname4", zRelative); + } + sqlcipher_sqlite3_free(zConverted); + zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI()); + sqlcipher_sqlite3_free(zTemp); + } #endif - -#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[45].pCurrent) - -#if !SQLITE_OS_WINRT - { "LocalFree", (SYSCALL)LocalFree, 0 }, -#else - { "LocalFree", (SYSCALL)0, 0 }, + if( zOut ){ + sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut); + sqlcipher_sqlite3_free(zOut); + return SQLITE_OK; + }else{ + return SQLITE_IOERR_NOMEM_BKPT; + } #endif +} +static int winFullPathname( + sqlcipher_sqlite3_vfs *pVfs, /* Pointer to vfs object */ + const char *zRelative, /* Possibly relative input path */ + int nFull, /* Size of output buffer in bytes */ + char *zFull /* Output buffer */ +){ + int rc; + sqlcipher_sqlite3_mutex *pMutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR); + sqlcipher_sqlite3_mutex_enter(pMutex); + rc = winFullPathnameNoMutex(pVfs, zRelative, nFull, zFull); + sqlcipher_sqlite3_mutex_leave(pMutex); + return rc; +} -#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent) - -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT - { "LockFile", (SYSCALL)LockFile, 0 }, +#ifndef SQLITE_OMIT_LOAD_EXTENSION +/* +** Interfaces for opening a shared library, finding entry points +** within the shared library, and closing the shared library. +*/ +static void *winDlOpen(sqlcipher_sqlite3_vfs *pVfs, const char *zFilename){ + HANDLE h; +#if defined(__CYGWIN__) + int nFull = pVfs->mxPathname+1; + char *zFull = sqlcipher_sqlite3MallocZero( nFull ); + void *zConverted = 0; + if( zFull==0 ){ + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); + return 0; + } + if( winFullPathname(pVfs, zFilename, nFull, zFull)!=SQLITE_OK ){ + sqlcipher_sqlite3_free(zFull); + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); + return 0; + } + zConverted = winConvertFromUtf8Filename(zFull); + sqlcipher_sqlite3_free(zFull); #else - { "LockFile", (SYSCALL)0, 0 }, -#endif - -#ifndef osLockFile -#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - DWORD))aSyscall[47].pCurrent) + void *zConverted = winConvertFromUtf8Filename(zFilename); + UNUSED_PARAMETER(pVfs); #endif - -#if !SQLITE_OS_WINCE - { "LockFileEx", (SYSCALL)LockFileEx, 0 }, + if( zConverted==0 ){ + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); + return 0; + } + if( osIsNT() ){ +#if SQLITE_OS_WINRT + h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0); #else - { "LockFileEx", (SYSCALL)0, 0 }, + h = osLoadLibraryW((LPCWSTR)zConverted); #endif - -#ifndef osLockFileEx -#define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \ - LPOVERLAPPED))aSyscall[48].pCurrent) + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + h = osLoadLibraryA((char*)zConverted); + } #endif - -#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && \ - (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)) - { "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 }, -#else - { "MapViewOfFile", (SYSCALL)0, 0 }, + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)h)); + sqlcipher_sqlite3_free(zConverted); + return (void*)h; +} +static void winDlError(sqlcipher_sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ + UNUSED_PARAMETER(pVfs); + winGetLastErrorMsg(osGetLastError(), nBuf, zBufOut); +} +static void (*winDlSym(sqlcipher_sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){ + FARPROC proc; + UNUSED_PARAMETER(pVfs); + proc = osGetProcAddressA((HANDLE)pH, zSym); + OSTRACE(("DLSYM handle=%p, symbol=%s, address=%p\n", + (void*)pH, zSym, (void*)proc)); + return (void(*)(void))proc; +} +static void winDlClose(sqlcipher_sqlite3_vfs *pVfs, void *pHandle){ + UNUSED_PARAMETER(pVfs); + osFreeLibrary((HANDLE)pHandle); + OSTRACE(("DLCLOSE handle=%p\n", (void*)pHandle)); +} +#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ + #define winDlOpen 0 + #define winDlError 0 + #define winDlSym 0 + #define winDlClose 0 #endif -#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - SIZE_T))aSyscall[49].pCurrent) - - { "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 }, - -#define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \ - int))aSyscall[50].pCurrent) - - { "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 }, - -#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \ - LARGE_INTEGER*))aSyscall[51].pCurrent) - - { "ReadFile", (SYSCALL)ReadFile, 0 }, - -#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \ - LPOVERLAPPED))aSyscall[52].pCurrent) - - { "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 }, +/* State information for the randomness gatherer. */ +typedef struct EntropyGatherer EntropyGatherer; +struct EntropyGatherer { + unsigned char *a; /* Gather entropy into this buffer */ + int na; /* Size of a[] in bytes */ + int i; /* XOR next input into a[i] */ + int nXor; /* Number of XOR operations done */ +}; -#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[53].pCurrent) +#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) +/* Mix sz bytes of entropy into p. */ +static void xorMemory(EntropyGatherer *p, unsigned char *x, int sz){ + int j, k; + for(j=0, k=p->i; ja[k++] ^= x[j]; + if( k>=p->na ) k = 0; + } + p->i = k; + p->nXor += sz; +} +#endif /* !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) */ -#if !SQLITE_OS_WINRT - { "SetFilePointer", (SYSCALL)SetFilePointer, 0 }, +/* +** Write up to nBuf bytes of randomness into zBuf. +*/ +static int winRandomness(sqlcipher_sqlite3_vfs *pVfs, int nBuf, char *zBuf){ +#if defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) + UNUSED_PARAMETER(pVfs); + memset(zBuf, 0, nBuf); + return nBuf; #else - { "SetFilePointer", (SYSCALL)0, 0 }, -#endif - -#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \ - DWORD))aSyscall[54].pCurrent) - -#if !SQLITE_OS_WINRT - { "Sleep", (SYSCALL)Sleep, 0 }, + EntropyGatherer e; + UNUSED_PARAMETER(pVfs); + memset(zBuf, 0, nBuf); + e.a = (unsigned char*)zBuf; + e.na = nBuf; + e.nXor = 0; + e.i = 0; + { + SYSTEMTIME x; + osGetSystemTime(&x); + xorMemory(&e, (unsigned char*)&x, sizeof(SYSTEMTIME)); + } + { + DWORD pid = osGetCurrentProcessId(); + xorMemory(&e, (unsigned char*)&pid, sizeof(DWORD)); + } +#if SQLITE_OS_WINRT + { + ULONGLONG cnt = osGetTickCount64(); + xorMemory(&e, (unsigned char*)&cnt, sizeof(ULONGLONG)); + } #else - { "Sleep", (SYSCALL)0, 0 }, -#endif - -#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent) - - { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 }, - -#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \ - LPFILETIME))aSyscall[56].pCurrent) + { + DWORD cnt = osGetTickCount(); + xorMemory(&e, (unsigned char*)&cnt, sizeof(DWORD)); + } +#endif /* SQLITE_OS_WINRT */ + { + LARGE_INTEGER i; + osQueryPerformanceCounter(&i); + xorMemory(&e, (unsigned char*)&i, sizeof(LARGE_INTEGER)); + } +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID + { + UUID id; + memset(&id, 0, sizeof(UUID)); + osUuidCreate(&id); + xorMemory(&e, (unsigned char*)&id, sizeof(UUID)); + memset(&id, 0, sizeof(UUID)); + osUuidCreateSequential(&id); + xorMemory(&e, (unsigned char*)&id, sizeof(UUID)); + } +#endif /* !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID */ + return e.nXor>nBuf ? nBuf : e.nXor; +#endif /* defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) */ +} -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT - { "UnlockFile", (SYSCALL)UnlockFile, 0 }, -#else - { "UnlockFile", (SYSCALL)0, 0 }, -#endif -#ifndef osUnlockFile -#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - DWORD))aSyscall[57].pCurrent) -#endif +/* +** Sleep for a little while. Return the amount of time slept. +*/ +static int winSleep(sqlcipher_sqlite3_vfs *pVfs, int microsec){ + sqlcipher_sqlite3_win32_sleep((microsec+999)/1000); + UNUSED_PARAMETER(pVfs); + return ((microsec+999)/1000)*1000; +} -#if !SQLITE_OS_WINCE - { "UnlockFileEx", (SYSCALL)UnlockFileEx, 0 }, -#else - { "UnlockFileEx", (SYSCALL)0, 0 }, +/* +** The following variable, if set to a non-zero value, is interpreted as +** the number of seconds since 1970 and is used to set the result of +** sqlcipher_sqlite3OsCurrentTime() during testing. +*/ +#ifdef SQLITE_TEST +SQLITE_API int sqlcipher_sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ #endif -#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - LPOVERLAPPED))aSyscall[58].pCurrent) - -#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 - { "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 }, -#else - { "UnmapViewOfFile", (SYSCALL)0, 0 }, +/* +** Find the current time (in Universal Coordinated Time). Write into *piNow +** the current time and date as a Julian Day number times 86_400_000. In +** other words, write into *piNow the number of milliseconds since the Julian +** epoch of noon in Greenwich on November 24, 4714 B.C according to the +** proleptic Gregorian calendar. +** +** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date +** cannot be found. +*/ +static int winCurrentTimeInt64(sqlcipher_sqlite3_vfs *pVfs, sqlcipher_sqlite3_int64 *piNow){ + /* FILETIME structure is a 64-bit value representing the number of + 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). + */ + FILETIME ft; + static const sqlcipher_sqlite3_int64 winFiletimeEpoch = 23058135*(sqlcipher_sqlite3_int64)8640000; +#ifdef SQLITE_TEST + static const sqlcipher_sqlite3_int64 unixEpoch = 24405875*(sqlcipher_sqlite3_int64)8640000; #endif + /* 2^32 - to avoid use of LL and warnings in gcc */ + static const sqlcipher_sqlite3_int64 max32BitValue = + (sqlcipher_sqlite3_int64)2000000000 + (sqlcipher_sqlite3_int64)2000000000 + + (sqlcipher_sqlite3_int64)294967296; -#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[59].pCurrent) - - { "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 }, - -#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \ - LPCSTR,LPBOOL))aSyscall[60].pCurrent) - - { "WriteFile", (SYSCALL)WriteFile, 0 }, - -#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \ - LPOVERLAPPED))aSyscall[61].pCurrent) - -#if SQLITE_OS_WINRT - { "CreateEventExW", (SYSCALL)CreateEventExW, 0 }, +#if SQLITE_OS_WINCE + SYSTEMTIME time; + osGetSystemTime(&time); + /* if SystemTimeToFileTime() fails, it returns zero. */ + if (!osSystemTimeToFileTime(&time,&ft)){ + return SQLITE_ERROR; + } #else - { "CreateEventExW", (SYSCALL)0, 0 }, + osGetSystemTimeAsFileTime( &ft ); #endif -#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ - DWORD,DWORD))aSyscall[62].pCurrent) + *piNow = winFiletimeEpoch + + ((((sqlcipher_sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + + (sqlcipher_sqlite3_int64)ft.dwLowDateTime)/(sqlcipher_sqlite3_int64)10000; -#if !SQLITE_OS_WINRT - { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, -#else - { "WaitForSingleObject", (SYSCALL)0, 0 }, +#ifdef SQLITE_TEST + if( sqlcipher_sqlite3_current_time ){ + *piNow = 1000*(sqlcipher_sqlite3_int64)sqlcipher_sqlite3_current_time + unixEpoch; + } #endif + UNUSED_PARAMETER(pVfs); + return SQLITE_OK; +} -#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ - DWORD))aSyscall[63].pCurrent) - -#if !SQLITE_OS_WINCE - { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 }, -#else - { "WaitForSingleObjectEx", (SYSCALL)0, 0 }, -#endif +/* +** Find the current time (in Universal Coordinated Time). Write the +** current time and date as a Julian Day number into *prNow and +** return 0. Return 1 if the time and date cannot be found. +*/ +static int winCurrentTime(sqlcipher_sqlite3_vfs *pVfs, double *prNow){ + int rc; + sqlcipher_sqlite3_int64 i; + rc = winCurrentTimeInt64(pVfs, &i); + if( !rc ){ + *prNow = i/86400000.0; + } + return rc; +} -#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ - BOOL))aSyscall[64].pCurrent) +/* +** The idea is that this function works like a combination of +** GetLastError() and FormatMessage() on Windows (or errno and +** strerror_r() on Unix). After an error is returned by an OS +** function, SQLite calls this function with zBuf pointing to +** a buffer of nBuf bytes. The OS layer should populate the +** buffer with a nul-terminated UTF-8 encoded error message +** describing the last IO error to have occurred within the calling +** thread. +** +** If the error message is too large for the supplied buffer, +** it should be truncated. The return value of xGetLastError +** is zero if the error message fits in the buffer, or non-zero +** otherwise (if the message was truncated). If non-zero is returned, +** then it is not necessary to include the nul-terminator character +** in the output buffer. +** +** Not supplying an error message will have no adverse effect +** on SQLite. It is fine to have an implementation that never +** returns an error message: +** +** int xGetLastError(sqlcipher_sqlite3_vfs *pVfs, int nBuf, char *zBuf){ +** assert(zBuf[0]=='\0'); +** return 0; +** } +** +** However if an error message is supplied, it will be incorporated +** by sqlite into the error message available to the user using +** sqlcipher_sqlite3_errmsg(), possibly making IO errors easier to debug. +*/ +static int winGetLastError(sqlcipher_sqlite3_vfs *pVfs, int nBuf, char *zBuf){ + DWORD e = osGetLastError(); + UNUSED_PARAMETER(pVfs); + if( nBuf>0 ) winGetLastErrorMsg(e, nBuf, zBuf); + return e; +} -#if SQLITE_OS_WINRT - { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 }, -#else - { "SetFilePointerEx", (SYSCALL)0, 0 }, +/* +** Initialize and deinitialize the operating system interface. +*/ +SQLITE_API int sqlcipher_sqlite3_os_init(void){ + static sqlcipher_sqlite3_vfs winVfs = { + 3, /* iVersion */ + sizeof(winFile), /* szOsFile */ + SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */ + 0, /* pNext */ + "win32", /* zName */ + &winAppData, /* pAppData */ + winOpen, /* xOpen */ + winDelete, /* xDelete */ + winAccess, /* xAccess */ + winFullPathname, /* xFullPathname */ + winDlOpen, /* xDlOpen */ + winDlError, /* xDlError */ + winDlSym, /* xDlSym */ + winDlClose, /* xDlClose */ + winRandomness, /* xRandomness */ + winSleep, /* xSleep */ + winCurrentTime, /* xCurrentTime */ + winGetLastError, /* xGetLastError */ + winCurrentTimeInt64, /* xCurrentTimeInt64 */ + winSetSystemCall, /* xSetSystemCall */ + winGetSystemCall, /* xGetSystemCall */ + winNextSystemCall, /* xNextSystemCall */ + }; +#if defined(SQLITE_WIN32_HAS_WIDE) + static sqlcipher_sqlite3_vfs winLongPathVfs = { + 3, /* iVersion */ + sizeof(winFile), /* szOsFile */ + SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */ + 0, /* pNext */ + "win32-longpath", /* zName */ + &winAppData, /* pAppData */ + winOpen, /* xOpen */ + winDelete, /* xDelete */ + winAccess, /* xAccess */ + winFullPathname, /* xFullPathname */ + winDlOpen, /* xDlOpen */ + winDlError, /* xDlError */ + winDlSym, /* xDlSym */ + winDlClose, /* xDlClose */ + winRandomness, /* xRandomness */ + winSleep, /* xSleep */ + winCurrentTime, /* xCurrentTime */ + winGetLastError, /* xGetLastError */ + winCurrentTimeInt64, /* xCurrentTimeInt64 */ + winSetSystemCall, /* xSetSystemCall */ + winGetSystemCall, /* xGetSystemCall */ + winNextSystemCall, /* xNextSystemCall */ + }; #endif - -#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \ - PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent) - -#if SQLITE_OS_WINRT - { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 }, -#else - { "GetFileInformationByHandleEx", (SYSCALL)0, 0 }, + static sqlcipher_sqlite3_vfs winNolockVfs = { + 3, /* iVersion */ + sizeof(winFile), /* szOsFile */ + SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */ + 0, /* pNext */ + "win32-none", /* zName */ + &winNolockAppData, /* pAppData */ + winOpen, /* xOpen */ + winDelete, /* xDelete */ + winAccess, /* xAccess */ + winFullPathname, /* xFullPathname */ + winDlOpen, /* xDlOpen */ + winDlError, /* xDlError */ + winDlSym, /* xDlSym */ + winDlClose, /* xDlClose */ + winRandomness, /* xRandomness */ + winSleep, /* xSleep */ + winCurrentTime, /* xCurrentTime */ + winGetLastError, /* xGetLastError */ + winCurrentTimeInt64, /* xCurrentTimeInt64 */ + winSetSystemCall, /* xSetSystemCall */ + winGetSystemCall, /* xGetSystemCall */ + winNextSystemCall, /* xNextSystemCall */ + }; +#if defined(SQLITE_WIN32_HAS_WIDE) + static sqlcipher_sqlite3_vfs winLongPathNolockVfs = { + 3, /* iVersion */ + sizeof(winFile), /* szOsFile */ + SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */ + 0, /* pNext */ + "win32-longpath-none", /* zName */ + &winNolockAppData, /* pAppData */ + winOpen, /* xOpen */ + winDelete, /* xDelete */ + winAccess, /* xAccess */ + winFullPathname, /* xFullPathname */ + winDlOpen, /* xDlOpen */ + winDlError, /* xDlError */ + winDlSym, /* xDlSym */ + winDlClose, /* xDlClose */ + winRandomness, /* xRandomness */ + winSleep, /* xSleep */ + winCurrentTime, /* xCurrentTime */ + winGetLastError, /* xGetLastError */ + winCurrentTimeInt64, /* xCurrentTimeInt64 */ + winSetSystemCall, /* xSetSystemCall */ + winGetSystemCall, /* xGetSystemCall */ + winNextSystemCall, /* xNextSystemCall */ + }; #endif -#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \ - FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent) + /* Double-check that the aSyscall[] array has been constructed + ** correctly. See ticket [bb3a86e890c8e96ab] */ + assert( ArraySize(aSyscall)==80 ); -#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) - { "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 }, + /* get memory map allocation granularity */ + memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); +#if SQLITE_OS_WINRT + osGetNativeSystemInfo(&winSysInfo); #else - { "MapViewOfFileFromApp", (SYSCALL)0, 0 }, + osGetSystemInfo(&winSysInfo); #endif + assert( winSysInfo.dwAllocationGranularity>0 ); + assert( winSysInfo.dwPageSize>0 ); -#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \ - SIZE_T))aSyscall[67].pCurrent) + sqlcipher_sqlite3_vfs_register(&winVfs, 1); -#if SQLITE_OS_WINRT - { "CreateFile2", (SYSCALL)CreateFile2, 0 }, -#else - { "CreateFile2", (SYSCALL)0, 0 }, +#if defined(SQLITE_WIN32_HAS_WIDE) + sqlcipher_sqlite3_vfs_register(&winLongPathVfs, 0); #endif -#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \ - LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent) + sqlcipher_sqlite3_vfs_register(&winNolockVfs, 0); -#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION) - { "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 }, -#else - { "LoadPackagedLibrary", (SYSCALL)0, 0 }, +#if defined(SQLITE_WIN32_HAS_WIDE) + sqlcipher_sqlite3_vfs_register(&winLongPathNolockVfs, 0); #endif -#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \ - DWORD))aSyscall[69].pCurrent) - -#if SQLITE_OS_WINRT - { "GetTickCount64", (SYSCALL)GetTickCount64, 0 }, -#else - { "GetTickCount64", (SYSCALL)0, 0 }, +#ifndef SQLITE_OMIT_WAL + winBigLock = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); #endif -#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent) + return SQLITE_OK; +} +SQLITE_API int sqlcipher_sqlite3_os_end(void){ #if SQLITE_OS_WINRT - { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, -#else - { "GetNativeSystemInfo", (SYSCALL)0, 0 }, + if( sleepObj!=NULL ){ + osCloseHandle(sleepObj); + sleepObj = NULL; + } #endif -#define osGetNativeSystemInfo ((VOID(WINAPI*)( \ - LPSYSTEM_INFO))aSyscall[71].pCurrent) - -#if defined(SQLITE_WIN32_HAS_ANSI) - { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 }, -#else - { "OutputDebugStringA", (SYSCALL)0, 0 }, +#ifndef SQLITE_OMIT_WAL + winBigLock = 0; #endif -#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent) + return SQLITE_OK; +} -#if defined(SQLITE_WIN32_HAS_WIDE) - { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 }, -#else - { "OutputDebugStringW", (SYSCALL)0, 0 }, -#endif +#endif /* SQLITE_OS_WIN */ -#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent) +/************** End of os_win.c **********************************************/ +/************** Begin file memdb.c *******************************************/ +/* +** 2016-09-07 +** +** 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 in-memory VFS. A database is held as a contiguous +** block of memory. +** +** This file also implements interface sqlcipher_sqlite3_serialize() and +** sqlcipher_sqlite3_deserialize(). +*/ +/* #include "sqliteInt.h" */ +#ifndef SQLITE_OMIT_DESERIALIZE - { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 }, +/* +** Forward declaration of objects used by this utility +*/ +typedef struct sqlcipher_sqlite3_vfs MemVfs; +typedef struct MemFile MemFile; +typedef struct MemStore MemStore; -#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent) +/* Access to a lower-level VFS that (might) implement dynamic loading, +** access to randomness, etc. +*/ +#define ORIGVFS(p) ((sqlcipher_sqlite3_vfs*)((p)->pAppData)) -#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) - { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 }, -#else - { "CreateFileMappingFromApp", (SYSCALL)0, 0 }, -#endif +/* Storage for a memdb file. +** +** An memdb object can be shared or separate. Shared memdb objects can be +** used by more than one database connection. Mutexes are used by shared +** memdb objects to coordinate access. Separate memdb objects are only +** connected to a single database connection and do not require additional +** mutexes. +** +** Shared memdb objects have .zFName!=0 and .pMutex!=0. They are created +** using "file:/name?vfs=memdb". The first character of the name must be +** "/" or else the object will be a separate memdb object. All shared +** memdb objects are stored in memdb_g.apMemStore[] in an arbitrary order. +** +** Separate memdb objects are created using a name that does not begin +** with "/" or using sqlcipher_sqlite3_deserialize(). +** +** Access rules for shared MemStore objects: +** +** * .zFName is initialized when the object is created and afterwards +** is unchanged until the object is destroyed. So it can be accessed +** at any time as long as we know the object is not being destroyed, +** which means while either the SQLITE_MUTEX_STATIC_VFS1 or +** .pMutex is held or the object is not part of memdb_g.apMemStore[]. +** +** * Can .pMutex can only be changed while holding the +** SQLITE_MUTEX_STATIC_VFS1 mutex or while the object is not part +** of memdb_g.apMemStore[]. +** +** * Other fields can only be changed while holding the .pMutex mutex +** or when the .nRef is less than zero and the object is not part of +** memdb_g.apMemStore[]. +** +** * The .aData pointer has the added requirement that it can can only +** be changed (for resizing) when nMmap is zero. +** +*/ +struct MemStore { + sqlcipher_sqlite3_int64 sz; /* Size of the file */ + sqlcipher_sqlite3_int64 szAlloc; /* Space allocated to aData */ + sqlcipher_sqlite3_int64 szMax; /* Maximum allowed size of the file */ + unsigned char *aData; /* content of the file */ + sqlcipher_sqlite3_mutex *pMutex; /* Used by shared stores only */ + int nMmap; /* Number of memory mapped pages */ + unsigned mFlags; /* Flags */ + int nRdLock; /* Number of readers */ + int nWrLock; /* Number of writers. (Always 0 or 1) */ + int nRef; /* Number of users of this MemStore */ + char *zFName; /* The filename for shared stores */ +}; -#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \ - LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[75].pCurrent) +/* An open file */ +struct MemFile { + sqlcipher_sqlite3_file base; /* IO methods */ + MemStore *pStore; /* The storage */ + int eLock; /* Most recent lock against this file */ +}; /* -** NOTE: On some sub-platforms, the InterlockedCompareExchange "function" -** is really just a macro that uses a compiler intrinsic (e.g. x64). -** So do not try to make this is into a redefinable interface. +** File-scope variables for holding the memdb files that are accessible +** to multiple database connections in separate threads. +** +** Must hold SQLITE_MUTEX_STATIC_VFS1 to access any part of this object. */ -#if defined(InterlockedCompareExchange) - { "InterlockedCompareExchange", (SYSCALL)0, 0 }, - -#define osInterlockedCompareExchange InterlockedCompareExchange -#else - { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 }, - -#define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \ - SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent) -#endif /* defined(InterlockedCompareExchange) */ +static struct MemFS { + int nMemStore; /* Number of shared MemStore objects */ + MemStore **apMemStore; /* Array of all shared MemStore objects */ +} memdb_g; -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID - { "UuidCreate", (SYSCALL)UuidCreate, 0 }, -#else - { "UuidCreate", (SYSCALL)0, 0 }, -#endif +/* +** Methods for MemFile +*/ +static int memdbClose(sqlcipher_sqlite3_file*); +static int memdbRead(sqlcipher_sqlite3_file*, void*, int iAmt, sqlcipher_sqlite3_int64 iOfst); +static int memdbWrite(sqlcipher_sqlite3_file*,const void*,int iAmt, sqlcipher_sqlite3_int64 iOfst); +static int memdbTruncate(sqlcipher_sqlite3_file*, sqlcipher_sqlite3_int64 size); +static int memdbSync(sqlcipher_sqlite3_file*, int flags); +static int memdbFileSize(sqlcipher_sqlite3_file*, sqlcipher_sqlite3_int64 *pSize); +static int memdbLock(sqlcipher_sqlite3_file*, int); +/* static int memdbCheckReservedLock(sqlcipher_sqlite3_file*, int *pResOut);// not used */ +static int memdbFileControl(sqlcipher_sqlite3_file*, int op, void *pArg); +/* static int memdbSectorSize(sqlcipher_sqlite3_file*); // not used */ +static int memdbDeviceCharacteristics(sqlcipher_sqlite3_file*); +static int memdbFetch(sqlcipher_sqlite3_file*, sqlcipher_sqlite3_int64 iOfst, int iAmt, void **pp); +static int memdbUnfetch(sqlcipher_sqlite3_file*, sqlcipher_sqlite3_int64 iOfst, void *p); -#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[77].pCurrent) +/* +** Methods for MemVfs +*/ +static int memdbOpen(sqlcipher_sqlite3_vfs*, const char *, sqlcipher_sqlite3_file*, int , int *); +/* static int memdbDelete(sqlcipher_sqlite3_vfs*, const char *zName, int syncDir); */ +static int memdbAccess(sqlcipher_sqlite3_vfs*, const char *zName, int flags, int *); +static int memdbFullPathname(sqlcipher_sqlite3_vfs*, const char *zName, int, char *zOut); +static void *memdbDlOpen(sqlcipher_sqlite3_vfs*, const char *zFilename); +static void memdbDlError(sqlcipher_sqlite3_vfs*, int nByte, char *zErrMsg); +static void (*memdbDlSym(sqlcipher_sqlite3_vfs *pVfs, void *p, const char*zSym))(void); +static void memdbDlClose(sqlcipher_sqlite3_vfs*, void*); +static int memdbRandomness(sqlcipher_sqlite3_vfs*, int nByte, char *zOut); +static int memdbSleep(sqlcipher_sqlite3_vfs*, int microseconds); +/* static int memdbCurrentTime(sqlcipher_sqlite3_vfs*, double*); */ +static int memdbGetLastError(sqlcipher_sqlite3_vfs*, int, char *); +static int memdbCurrentTimeInt64(sqlcipher_sqlite3_vfs*, sqlcipher_sqlite3_int64*); -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID - { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 }, -#else - { "UuidCreateSequential", (SYSCALL)0, 0 }, -#endif +static sqlcipher_sqlite3_vfs memdb_vfs = { + 2, /* iVersion */ + 0, /* szOsFile (set when registered) */ + 1024, /* mxPathname */ + 0, /* pNext */ + "memdb", /* zName */ + 0, /* pAppData (set when registered) */ + memdbOpen, /* xOpen */ + 0, /* memdbDelete, */ /* xDelete */ + memdbAccess, /* xAccess */ + memdbFullPathname, /* xFullPathname */ + memdbDlOpen, /* xDlOpen */ + memdbDlError, /* xDlError */ + memdbDlSym, /* xDlSym */ + memdbDlClose, /* xDlClose */ + memdbRandomness, /* xRandomness */ + memdbSleep, /* xSleep */ + 0, /* memdbCurrentTime, */ /* xCurrentTime */ + memdbGetLastError, /* xGetLastError */ + memdbCurrentTimeInt64, /* xCurrentTimeInt64 */ + 0, /* xSetSystemCall */ + 0, /* xGetSystemCall */ + 0, /* xNextSystemCall */ +}; -#define osUuidCreateSequential \ - ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[78].pCurrent) +static const sqlcipher_sqlite3_io_methods memdb_io_methods = { + 3, /* iVersion */ + memdbClose, /* xClose */ + memdbRead, /* xRead */ + memdbWrite, /* xWrite */ + memdbTruncate, /* xTruncate */ + memdbSync, /* xSync */ + memdbFileSize, /* xFileSize */ + memdbLock, /* xLock */ + memdbLock, /* xUnlock - same as xLock in this case */ + 0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */ + memdbFileControl, /* xFileControl */ + 0, /* memdbSectorSize,*/ /* xSectorSize */ + memdbDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmMap */ + 0, /* xShmLock */ + 0, /* xShmBarrier */ + 0, /* xShmUnmap */ + memdbFetch, /* xFetch */ + memdbUnfetch /* xUnfetch */ +}; -#if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0 - { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 }, +/* +** Enter/leave the mutex on a MemStore +*/ +#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0 +static void memdbEnter(MemStore *p){ + UNUSED_PARAMETER(p); +} +static void memdbLeave(MemStore *p){ + UNUSED_PARAMETER(p); +} #else - { "FlushViewOfFile", (SYSCALL)0, 0 }, +static void memdbEnter(MemStore *p){ + sqlcipher_sqlite3_mutex_enter(p->pMutex); +} +static void memdbLeave(MemStore *p){ + sqlcipher_sqlite3_mutex_leave(p->pMutex); +} #endif -#define osFlushViewOfFile \ - ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) -}; /* End of the overrideable system calls */ /* -** This is the xSetSystemCall() method of sqlcipher_sqlite3_vfs for all of the -** "win32" VFSes. Return SQLITE_OK opon successfully updating the -** system call pointer, or SQLITE_NOTFOUND if there is no configurable -** system call named zName. +** Close an memdb-file. +** Free the underlying MemStore object when its refcount drops to zero +** or less. */ -static int winSetSystemCall( - sqlcipher_sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ - const char *zName, /* Name of system call to override */ - sqlcipher_sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ -){ - unsigned int i; - int rc = SQLITE_NOTFOUND; - - UNUSED_PARAMETER(pNotUsed); - if( zName==0 ){ - /* If no zName is given, restore all system calls to their default - ** settings and return NULL - */ - rc = SQLITE_OK; - for(i=0; ipStore; + if( p->zFName ){ + int i; +#ifndef SQLITE_MUTEX_OMIT + sqlcipher_sqlite3_mutex *pVfsMutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); +#endif + sqlcipher_sqlite3_mutex_enter(pVfsMutex); + for(i=0; ALWAYS(inRef==1 ){ + memdb_g.apMemStore[i] = memdb_g.apMemStore[--memdb_g.nMemStore]; + if( memdb_g.nMemStore==0 ){ + sqlcipher_sqlite3_free(memdb_g.apMemStore); + memdb_g.apMemStore = 0; + } } - rc = SQLITE_OK; - if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault; - aSyscall[i].pCurrent = pNewFunc; break; } } + sqlcipher_sqlite3_mutex_leave(pVfsMutex); + }else{ + memdbEnter(p); } - return rc; + p->nRef--; + if( p->nRef<=0 ){ + if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ){ + sqlcipher_sqlite3_free(p->aData); + } + memdbLeave(p); + sqlcipher_sqlite3_mutex_free(p->pMutex); + sqlcipher_sqlite3_free(p); + }else{ + memdbLeave(p); + } + return SQLITE_OK; } /* -** Return the value of a system call. Return NULL if zName is not a -** recognized system call name. NULL is also returned if the system call -** is currently undefined. +** Read data from an memdb-file. */ -static sqlcipher_sqlite3_syscall_ptr winGetSystemCall( - sqlcipher_sqlite3_vfs *pNotUsed, - const char *zName +static int memdbRead( + sqlcipher_sqlite3_file *pFile, + void *zBuf, + int iAmt, + sqlite_int64 iOfst ){ - unsigned int i; - - UNUSED_PARAMETER(pNotUsed); - for(i=0; ipStore; + memdbEnter(p); + if( iOfst+iAmt>p->sz ){ + memset(zBuf, 0, iAmt); + if( iOfstsz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst); + memdbLeave(p); + return SQLITE_IOERR_SHORT_READ; } - return 0; + memcpy(zBuf, p->aData+iOfst, iAmt); + memdbLeave(p); + return SQLITE_OK; } /* -** Return the name of the first system call after zName. If zName==NULL -** then return the name of the first system call. Return NULL if zName -** is the last system call or if zName is not the name of a valid -** system call. +** Try to enlarge the memory allocation to hold at least sz bytes */ -static const char *winNextSystemCall(sqlcipher_sqlite3_vfs *p, const char *zName){ - int i = -1; - - UNUSED_PARAMETER(p); - if( zName ){ - for(i=0; imFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || NEVER(p->nMmap>0) ){ + return SQLITE_FULL; } - for(i++; ip->szMax ){ + return SQLITE_FULL; } - return 0; + newSz *= 2; + if( newSz>p->szMax ) newSz = p->szMax; + pNew = sqlcipher_sqlite3Realloc(p->aData, newSz); + if( pNew==0 ) return SQLITE_IOERR_NOMEM; + p->aData = pNew; + p->szAlloc = newSz; + return SQLITE_OK; } -#ifdef SQLITE_WIN32_MALLOC /* -** If a Win32 native heap has been configured, this function will attempt to -** compact it. Upon success, SQLITE_OK will be returned. Upon failure, one -** of SQLITE_NOMEM, SQLITE_ERROR, or SQLITE_NOTFOUND will be returned. The -** "pnLargest" argument, if non-zero, will be used to return the size of the -** largest committed free block in the heap, in bytes. +** Write data to an memdb-file. */ -SQLITE_API int sqlcipher_sqlite3_win32_compact_heap(LPUINT pnLargest){ - int rc = SQLITE_OK; - UINT nLargest = 0; - HANDLE hHeap; - - winMemAssertMagic(); - hHeap = winMemGetHeap(); - assert( hHeap!=0 ); - assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); -#endif -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT - if( (nLargest=osHeapCompact(hHeap, SQLITE_WIN32_HEAP_FLAGS))==0 ){ - DWORD lastErrno = osGetLastError(); - if( lastErrno==NO_ERROR ){ - sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to HeapCompact (no space), heap=%p", - (void*)hHeap); - rc = SQLITE_NOMEM_BKPT; - }else{ - sqlcipher_sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%lu), heap=%p", - osGetLastError(), (void*)hHeap); - rc = SQLITE_ERROR; +static int memdbWrite( + sqlcipher_sqlite3_file *pFile, + const void *z, + int iAmt, + sqlite_int64 iOfst +){ + MemStore *p = ((MemFile*)pFile)->pStore; + memdbEnter(p); + if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ){ + /* Can't happen: memdbLock() will return SQLITE_READONLY before + ** reaching this point */ + memdbLeave(p); + return SQLITE_IOERR_WRITE; + } + if( iOfst+iAmt>p->sz ){ + int rc; + if( iOfst+iAmt>p->szAlloc + && (rc = memdbEnlarge(p, iOfst+iAmt))!=SQLITE_OK + ){ + memdbLeave(p); + return rc; } + if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz); + p->sz = iOfst+iAmt; } -#else - sqlcipher_sqlite3_log(SQLITE_NOTFOUND, "failed to HeapCompact, heap=%p", - (void*)hHeap); - rc = SQLITE_NOTFOUND; -#endif - if( pnLargest ) *pnLargest = nLargest; - return rc; + memcpy(p->aData+iOfst, z, iAmt); + memdbLeave(p); + return SQLITE_OK; } /* -** If a Win32 native heap has been configured, this function will attempt to -** destroy and recreate it. If the Win32 native heap is not isolated and/or -** the sqlcipher_sqlite3_memory_used() function does not return zero, SQLITE_BUSY will -** be returned and no changes will be made to the Win32 native heap. +** Truncate an memdb-file. +** +** In rollback mode (which is always the case for memdb, as it does not +** support WAL mode) the truncate() method is only used to reduce +** the size of a file, never to increase the size. */ -SQLITE_API int sqlcipher_sqlite3_win32_reset_heap(){ - int rc; - MUTEX_LOGIC( sqlcipher_sqlite3_mutex *pMainMtx; ) /* The main static mutex */ - MUTEX_LOGIC( sqlcipher_sqlite3_mutex *pMem; ) /* The memsys static mutex */ - MUTEX_LOGIC( pMainMtx = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) - MUTEX_LOGIC( pMem = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); ) - sqlcipher_sqlite3_mutex_enter(pMainMtx); - sqlcipher_sqlite3_mutex_enter(pMem); - winMemAssertMagic(); - if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlcipher_sqlite3_memory_used()==0 ){ - /* - ** At this point, there should be no outstanding memory allocations on - ** the heap. Also, since both the main and memsys locks are currently - ** being held by us, no other function (i.e. from another thread) should - ** be able to even access the heap. Attempt to destroy and recreate our - ** isolated Win32 native heap now. - */ - assert( winMemGetHeap()!=NULL ); - assert( winMemGetOwned() ); - assert( sqlcipher_sqlite3_memory_used()==0 ); - winMemShutdown(winMemGetDataPtr()); - assert( winMemGetHeap()==NULL ); - assert( !winMemGetOwned() ); - assert( sqlcipher_sqlite3_memory_used()==0 ); - rc = winMemInit(winMemGetDataPtr()); - assert( rc!=SQLITE_OK || winMemGetHeap()!=NULL ); - assert( rc!=SQLITE_OK || winMemGetOwned() ); - assert( rc!=SQLITE_OK || sqlcipher_sqlite3_memory_used()==0 ); +static int memdbTruncate(sqlcipher_sqlite3_file *pFile, sqlite_int64 size){ + MemStore *p = ((MemFile*)pFile)->pStore; + int rc = SQLITE_OK; + memdbEnter(p); + if( size>p->sz ){ + /* This can only happen with a corrupt wal mode db */ + rc = SQLITE_CORRUPT; }else{ - /* - ** The Win32 native heap cannot be modified because it may be in use. - */ - rc = SQLITE_BUSY; + p->sz = size; } - sqlcipher_sqlite3_mutex_leave(pMem); - sqlcipher_sqlite3_mutex_leave(pMainMtx); + memdbLeave(p); return rc; } -#endif /* SQLITE_WIN32_MALLOC */ /* -** This function outputs the specified (ANSI) string to the Win32 debugger -** (if available). +** Sync an memdb-file. */ - -SQLITE_API void sqlcipher_sqlite3_win32_write_debug(const char *zBuf, int nBuf){ - char zDbgBuf[SQLITE_WIN32_DBG_BUF_SIZE]; - int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */ - if( nMin<-1 ) nMin = -1; /* all negative values become -1. */ - assert( nMin==-1 || nMin==0 || nMin0 ){ - memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); - memcpy(zDbgBuf, zBuf, nMin); - osOutputDebugStringA(zDbgBuf); - }else{ - osOutputDebugStringA(zBuf); - } -#elif defined(SQLITE_WIN32_HAS_WIDE) - memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); - if ( osMultiByteToWideChar( - osAreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, zBuf, - nMin, (LPWSTR)zDbgBuf, SQLITE_WIN32_DBG_BUF_SIZE/sizeof(WCHAR))<=0 ){ - return; - } - osOutputDebugStringW((LPCWSTR)zDbgBuf); -#else - if( nMin>0 ){ - memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); - memcpy(zDbgBuf, zBuf, nMin); - fprintf(stderr, "%s", zDbgBuf); - }else{ - fprintf(stderr, "%s", zBuf); - } -#endif +static int memdbSync(sqlcipher_sqlite3_file *pFile, int flags){ + UNUSED_PARAMETER(pFile); + UNUSED_PARAMETER(flags); + return SQLITE_OK; } /* -** The following routine suspends the current thread for at least ms -** milliseconds. This is equivalent to the Win32 Sleep() interface. +** Return the current file-size of an memdb-file. */ -#if SQLITE_OS_WINRT -static HANDLE sleepObj = NULL; -#endif - -SQLITE_API void sqlcipher_sqlite3_win32_sleep(DWORD milliseconds){ -#if SQLITE_OS_WINRT - if ( sleepObj==NULL ){ - sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET, - SYNCHRONIZE); - } - assert( sleepObj!=NULL ); - osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE); -#else - osSleep(milliseconds); -#endif -} - -#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ - SQLITE_THREADSAFE>0 -SQLITE_PRIVATE DWORD sqlcipher_sqlite3Win32Wait(HANDLE hObject){ - DWORD rc; - while( (rc = osWaitForSingleObjectEx(hObject, INFINITE, - TRUE))==WAIT_IO_COMPLETION ){} - return rc; +static int memdbFileSize(sqlcipher_sqlite3_file *pFile, sqlite_int64 *pSize){ + MemStore *p = ((MemFile*)pFile)->pStore; + memdbEnter(p); + *pSize = p->sz; + memdbLeave(p); + return SQLITE_OK; } -#endif /* -** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, -** or WinCE. Return false (zero) for Win95, Win98, or WinME. -** -** Here is an interesting observation: Win95, Win98, and WinME lack -** the LockFileEx() API. But we can still statically link against that -** API as long as we don't call it when running Win95/98/ME. A call to -** this routine is used to determine if the host is Win95/98/ME or -** WinNT/2K/XP so that we will know whether or not we can safely call -** the LockFileEx() API. -*/ - -#if !SQLITE_WIN32_GETVERSIONEX -# define osIsNT() (1) -#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI) -# define osIsNT() (1) -#elif !defined(SQLITE_WIN32_HAS_WIDE) -# define osIsNT() (0) -#else -# define osIsNT() ((sqlcipher_sqlite3_os_type==2) || sqlcipher_sqlite3_win32_is_nt()) -#endif - -/* -** This function determines if the machine is running a version of Windows -** based on the NT kernel. +** Lock an memdb-file. */ -SQLITE_API int sqlcipher_sqlite3_win32_is_nt(void){ -#if SQLITE_OS_WINRT - /* - ** NOTE: The WinRT sub-platform is always assumed to be based on the NT - ** kernel. - */ - return 1; -#elif SQLITE_WIN32_GETVERSIONEX - if( osInterlockedCompareExchange(&sqlcipher_sqlite3_os_type, 0, 0)==0 ){ -#if defined(SQLITE_WIN32_HAS_ANSI) - OSVERSIONINFOA sInfo; - sInfo.dwOSVersionInfoSize = sizeof(sInfo); - osGetVersionExA(&sInfo); - osInterlockedCompareExchange(&sqlcipher_sqlite3_os_type, - (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0); -#elif defined(SQLITE_WIN32_HAS_WIDE) - OSVERSIONINFOW sInfo; - sInfo.dwOSVersionInfoSize = sizeof(sInfo); - osGetVersionExW(&sInfo); - osInterlockedCompareExchange(&sqlcipher_sqlite3_os_type, - (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0); -#endif +static int memdbLock(sqlcipher_sqlite3_file *pFile, int eLock){ + MemFile *pThis = (MemFile*)pFile; + MemStore *p = pThis->pStore; + int rc = SQLITE_OK; + if( eLock==pThis->eLock ) return SQLITE_OK; + memdbEnter(p); + if( eLock>SQLITE_LOCK_SHARED ){ + if( p->mFlags & SQLITE_DESERIALIZE_READONLY ){ + rc = SQLITE_READONLY; + }else if( pThis->eLock<=SQLITE_LOCK_SHARED ){ + if( p->nWrLock ){ + rc = SQLITE_BUSY; + }else{ + p->nWrLock = 1; + } + } + }else if( eLock==SQLITE_LOCK_SHARED ){ + if( pThis->eLock > SQLITE_LOCK_SHARED ){ + assert( p->nWrLock==1 ); + p->nWrLock = 0; + }else if( p->nWrLock ){ + rc = SQLITE_BUSY; + }else{ + p->nRdLock++; + } + }else{ + assert( eLock==SQLITE_LOCK_NONE ); + if( pThis->eLock>SQLITE_LOCK_SHARED ){ + assert( p->nWrLock==1 ); + p->nWrLock = 0; + } + assert( p->nRdLock>0 ); + p->nRdLock--; } - return osInterlockedCompareExchange(&sqlcipher_sqlite3_os_type, 2, 2)==2; -#elif SQLITE_TEST - return osInterlockedCompareExchange(&sqlcipher_sqlite3_os_type, 2, 2)==2; -#else - /* - ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are - ** deprecated are always assumed to be based on the NT kernel. - */ - return 1; -#endif + if( rc==SQLITE_OK ) pThis->eLock = eLock; + memdbLeave(p); + return rc; } -#ifdef SQLITE_WIN32_MALLOC +#if 0 /* -** Allocate nBytes of memory. +** This interface is only used for crash recovery, which does not +** occur on an in-memory database. */ -static void *winMemMalloc(int nBytes){ - HANDLE hHeap; - void *p; - - winMemAssertMagic(); - hHeap = winMemGetHeap(); - assert( hHeap!=0 ); - assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); -#endif - assert( nBytes>=0 ); - p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); - if( !p ){ - sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%lu), heap=%p", - nBytes, osGetLastError(), (void*)hHeap); - } - return p; +static int memdbCheckReservedLock(sqlcipher_sqlite3_file *pFile, int *pResOut){ + *pResOut = 0; + return SQLITE_OK; } - -/* -** Free memory. -*/ -static void winMemFree(void *pPrior){ - HANDLE hHeap; - - winMemAssertMagic(); - hHeap = winMemGetHeap(); - assert( hHeap!=0 ); - assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); #endif - if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */ - if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){ - sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%lu), heap=%p", - pPrior, osGetLastError(), (void*)hHeap); - } -} + /* -** Change the size of an existing memory allocation +** File control method. For custom operations on an memdb-file. */ -static void *winMemRealloc(void *pPrior, int nBytes){ - HANDLE hHeap; - void *p; - - winMemAssertMagic(); - hHeap = winMemGetHeap(); - assert( hHeap!=0 ); - assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); -#endif - assert( nBytes>=0 ); - if( !pPrior ){ - p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); - }else{ - p = osHeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes); +static int memdbFileControl(sqlcipher_sqlite3_file *pFile, int op, void *pArg){ + MemStore *p = ((MemFile*)pFile)->pStore; + int rc = SQLITE_NOTFOUND; + memdbEnter(p); + if( op==SQLITE_FCNTL_VFSNAME ){ + *(char**)pArg = sqlcipher_sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz); + rc = SQLITE_OK; } - if( !p ){ - sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%lu), heap=%p", - pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, osGetLastError(), - (void*)hHeap); + if( op==SQLITE_FCNTL_SIZE_LIMIT ){ + sqlcipher_sqlite3_int64 iLimit = *(sqlcipher_sqlite3_int64*)pArg; + if( iLimitsz ){ + if( iLimit<0 ){ + iLimit = p->szMax; + }else{ + iLimit = p->sz; + } + } + p->szMax = iLimit; + *(sqlcipher_sqlite3_int64*)pArg = iLimit; + rc = SQLITE_OK; } - return p; + memdbLeave(p); + return rc; } +#if 0 /* Not used because of SQLITE_IOCAP_POWERSAFE_OVERWRITE */ /* -** Return the size of an outstanding allocation, in bytes. +** Return the sector-size in bytes for an memdb-file. */ -static int winMemSize(void *p){ - HANDLE hHeap; - SIZE_T n; - - winMemAssertMagic(); - hHeap = winMemGetHeap(); - assert( hHeap!=0 ); - assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, p) ); -#endif - if( !p ) return 0; - n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p); - if( n==(SIZE_T)-1 ){ - sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%lu), heap=%p", - p, osGetLastError(), (void*)hHeap); - return 0; - } - return (int)n; +static int memdbSectorSize(sqlcipher_sqlite3_file *pFile){ + return 1024; } +#endif /* -** Round up a request size to the next valid allocation size. +** Return the device characteristic flags supported by an memdb-file. */ -static int winMemRoundup(int n){ - return n; +static int memdbDeviceCharacteristics(sqlcipher_sqlite3_file *pFile){ + UNUSED_PARAMETER(pFile); + return SQLITE_IOCAP_ATOMIC | + SQLITE_IOCAP_POWERSAFE_OVERWRITE | + SQLITE_IOCAP_SAFE_APPEND | + SQLITE_IOCAP_SEQUENTIAL; } -/* -** Initialize this module. -*/ -static int winMemInit(void *pAppData){ - winMemData *pWinMemData = (winMemData *)pAppData; - - if( !pWinMemData ) return SQLITE_ERROR; - assert( pWinMemData->magic1==WINMEM_MAGIC1 ); - assert( pWinMemData->magic2==WINMEM_MAGIC2 ); - -#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE - if( !pWinMemData->hHeap ){ - DWORD dwInitialSize = SQLITE_WIN32_HEAP_INIT_SIZE; - DWORD dwMaximumSize = (DWORD)sqlcipher_sqlite3GlobalConfig.nHeap; - if( dwMaximumSize==0 ){ - dwMaximumSize = SQLITE_WIN32_HEAP_MAX_SIZE; - }else if( dwInitialSize>dwMaximumSize ){ - dwInitialSize = dwMaximumSize; - } - pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS, - dwInitialSize, dwMaximumSize); - if( !pWinMemData->hHeap ){ - sqlcipher_sqlite3_log(SQLITE_NOMEM, - "failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu", - osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize, - dwMaximumSize); - return SQLITE_NOMEM_BKPT; - } - pWinMemData->bOwned = TRUE; - assert( pWinMemData->bOwned ); - } -#else - pWinMemData->hHeap = osGetProcessHeap(); - if( !pWinMemData->hHeap ){ - sqlcipher_sqlite3_log(SQLITE_NOMEM, - "failed to GetProcessHeap (%lu)", osGetLastError()); - return SQLITE_NOMEM_BKPT; +/* Fetch a page of a memory-mapped file */ +static int memdbFetch( + sqlcipher_sqlite3_file *pFile, + sqlcipher_sqlite3_int64 iOfst, + int iAmt, + void **pp +){ + MemStore *p = ((MemFile*)pFile)->pStore; + memdbEnter(p); + if( iOfst+iAmt>p->sz || (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)!=0 ){ + *pp = 0; + }else{ + p->nMmap++; + *pp = (void*)(p->aData + iOfst); } - pWinMemData->bOwned = FALSE; - assert( !pWinMemData->bOwned ); -#endif - assert( pWinMemData->hHeap!=0 ); - assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); -#endif + memdbLeave(p); + return SQLITE_OK; +} + +/* Release a memory-mapped page */ +static int memdbUnfetch(sqlcipher_sqlite3_file *pFile, sqlcipher_sqlite3_int64 iOfst, void *pPage){ + MemStore *p = ((MemFile*)pFile)->pStore; + UNUSED_PARAMETER(iOfst); + UNUSED_PARAMETER(pPage); + memdbEnter(p); + p->nMmap--; + memdbLeave(p); return SQLITE_OK; } /* -** Deinitialize this module. +** Open an mem file handle. */ -static void winMemShutdown(void *pAppData){ - winMemData *pWinMemData = (winMemData *)pAppData; - - if( !pWinMemData ) return; - assert( pWinMemData->magic1==WINMEM_MAGIC1 ); - assert( pWinMemData->magic2==WINMEM_MAGIC2 ); +static int memdbOpen( + sqlcipher_sqlite3_vfs *pVfs, + const char *zName, + sqlcipher_sqlite3_file *pFd, + int flags, + int *pOutFlags +){ + MemFile *pFile = (MemFile*)pFd; + MemStore *p = 0; + int szName; + UNUSED_PARAMETER(pVfs); - if( pWinMemData->hHeap ){ - assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); + memset(pFile, 0, sizeof(*pFile)); + szName = sqlcipher_sqlite3Strlen30(zName); + if( szName>1 && zName[0]=='/' ){ + int i; +#ifndef SQLITE_MUTEX_OMIT + sqlcipher_sqlite3_mutex *pVfsMutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); #endif - if( pWinMemData->bOwned ){ - if( !osHeapDestroy(pWinMemData->hHeap) ){ - sqlcipher_sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%lu), heap=%p", - osGetLastError(), (void*)pWinMemData->hHeap); + sqlcipher_sqlite3_mutex_enter(pVfsMutex); + for(i=0; izFName,zName)==0 ){ + p = memdb_g.apMemStore[i]; + break; } - pWinMemData->bOwned = FALSE; } - pWinMemData->hHeap = NULL; - } -} - -/* -** Populate the low-level memory allocation function pointers in -** sqlcipher_sqlite3GlobalConfig.m with pointers to the routines in this file. The -** arguments specify the block of memory to manage. -** -** This routine is only called by sqlcipher_sqlite3_config(), and therefore -** is not required to be threadsafe (it is not). -*/ -SQLITE_PRIVATE const sqlcipher_sqlite3_mem_methods *sqlcipher_sqlite3MemGetWin32(void){ - static const sqlcipher_sqlite3_mem_methods winMemMethods = { - winMemMalloc, - winMemFree, - winMemRealloc, - winMemSize, - winMemRoundup, - winMemInit, - winMemShutdown, - &win_mem_data - }; - return &winMemMethods; -} - -SQLITE_PRIVATE void sqlcipher_sqlite3MemSetDefault(void){ - sqlcipher_sqlite3_config(SQLITE_CONFIG_MALLOC, sqlcipher_sqlite3MemGetWin32()); -} -#endif /* SQLITE_WIN32_MALLOC */ - -/* -** Convert a UTF-8 string to Microsoft Unicode. -** -** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). -*/ -static LPWSTR winUtf8ToUnicode(const char *zText){ - int nChar; - LPWSTR zWideText; - - nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0); - if( nChar==0 ){ - return 0; - } - zWideText = sqlcipher_sqlite3MallocZero( nChar*sizeof(WCHAR) ); - if( zWideText==0 ){ - return 0; - } - nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText, - nChar); - if( nChar==0 ){ - sqlcipher_sqlite3_free(zWideText); - zWideText = 0; - } - return zWideText; -} - -/* -** Convert a Microsoft Unicode string to UTF-8. -** -** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). -*/ -static char *winUnicodeToUtf8(LPCWSTR zWideText){ - int nByte; - char *zText; - - nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, 0, 0, 0, 0); - if( nByte == 0 ){ - return 0; - } - zText = sqlcipher_sqlite3MallocZero( nByte ); - if( zText==0 ){ - return 0; + if( p==0 ){ + MemStore **apNew; + p = sqlcipher_sqlite3Malloc( sizeof(*p) + szName + 3 ); + if( p==0 ){ + sqlcipher_sqlite3_mutex_leave(pVfsMutex); + return SQLITE_NOMEM; + } + apNew = sqlcipher_sqlite3Realloc(memdb_g.apMemStore, + sizeof(apNew[0])*(memdb_g.nMemStore+1) ); + if( apNew==0 ){ + sqlcipher_sqlite3_free(p); + sqlcipher_sqlite3_mutex_leave(pVfsMutex); + return SQLITE_NOMEM; + } + apNew[memdb_g.nMemStore++] = p; + memdb_g.apMemStore = apNew; + memset(p, 0, sizeof(*p)); + p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE|SQLITE_DESERIALIZE_FREEONCLOSE; + p->szMax = sqlcipher_sqlite3GlobalConfig.mxMemdbSize; + p->zFName = (char*)&p[1]; + memcpy(p->zFName, zName, szName+1); + p->pMutex = sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( p->pMutex==0 ){ + memdb_g.nMemStore--; + sqlcipher_sqlite3_free(p); + sqlcipher_sqlite3_mutex_leave(pVfsMutex); + return SQLITE_NOMEM; + } + p->nRef = 1; + memdbEnter(p); + }else{ + memdbEnter(p); + p->nRef++; + } + sqlcipher_sqlite3_mutex_leave(pVfsMutex); + }else{ + p = sqlcipher_sqlite3Malloc( sizeof(*p) ); + if( p==0 ){ + return SQLITE_NOMEM; + } + memset(p, 0, sizeof(*p)); + p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; + p->szMax = sqlcipher_sqlite3GlobalConfig.mxMemdbSize; } - nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, zText, nByte, - 0, 0); - if( nByte == 0 ){ - sqlcipher_sqlite3_free(zText); - zText = 0; + pFile->pStore = p; + if( pOutFlags!=0 ){ + *pOutFlags = flags | SQLITE_OPEN_MEMORY; } - return zText; + pFd->pMethods = &memdb_io_methods; + memdbLeave(p); + return SQLITE_OK; } +#if 0 /* Only used to delete rollback journals, super-journals, and WAL + ** files, none of which exist in memdb. So this routine is never used */ /* -** Convert an ANSI string to Microsoft Unicode, using the ANSI or OEM -** code page. -** -** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). +** Delete the file located at zPath. If the dirSync argument is true, +** ensure the file-system modifications are synced to disk before +** returning. */ -static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){ - int nByte; - LPWSTR zMbcsText; - int codepage = useAnsi ? CP_ACP : CP_OEMCP; - - nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL, - 0)*sizeof(WCHAR); - if( nByte==0 ){ - return 0; - } - zMbcsText = sqlcipher_sqlite3MallocZero( nByte*sizeof(WCHAR) ); - if( zMbcsText==0 ){ - return 0; - } - nByte = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText, - nByte); - if( nByte==0 ){ - sqlcipher_sqlite3_free(zMbcsText); - zMbcsText = 0; - } - return zMbcsText; +static int memdbDelete(sqlcipher_sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + return SQLITE_IOERR_DELETE; } +#endif /* -** Convert a Microsoft Unicode string to a multi-byte character string, -** using the ANSI or OEM code page. +** Test for access permissions. Return true if the requested permission +** is available, or false otherwise. ** -** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). +** With memdb, no files ever exist on disk. So always return false. */ -static char *winUnicodeToMbcs(LPCWSTR zWideText, int useAnsi){ - int nByte; - char *zText; - int codepage = useAnsi ? CP_ACP : CP_OEMCP; - - nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, 0, 0, 0, 0); - if( nByte == 0 ){ - return 0; - } - zText = sqlcipher_sqlite3MallocZero( nByte ); - if( zText==0 ){ - return 0; - } - nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, zText, - nByte, 0, 0); - if( nByte == 0 ){ - sqlcipher_sqlite3_free(zText); - zText = 0; - } - return zText; +static int memdbAccess( + sqlcipher_sqlite3_vfs *pVfs, + const char *zPath, + int flags, + int *pResOut +){ + UNUSED_PARAMETER(pVfs); + UNUSED_PARAMETER(zPath); + UNUSED_PARAMETER(flags); + *pResOut = 0; + return SQLITE_OK; } /* -** Convert a multi-byte character string to UTF-8. -** -** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). +** Populate buffer zOut with the full canonical pathname corresponding +** to the pathname in zPath. zOut is guaranteed to point to a buffer +** of at least (INST_MAX_PATHNAME+1) bytes. */ -static char *winMbcsToUtf8(const char *zText, int useAnsi){ - char *zTextUtf8; - LPWSTR zTmpWide; +static int memdbFullPathname( + sqlcipher_sqlite3_vfs *pVfs, + const char *zPath, + int nOut, + char *zOut +){ + UNUSED_PARAMETER(pVfs); + sqlcipher_sqlite3_snprintf(nOut, zOut, "%s", zPath); + return SQLITE_OK; +} - zTmpWide = winMbcsToUnicode(zText, useAnsi); - if( zTmpWide==0 ){ - return 0; - } - zTextUtf8 = winUnicodeToUtf8(zTmpWide); - sqlcipher_sqlite3_free(zTmpWide); - return zTextUtf8; +/* +** Open the dynamic library located at zPath and return a handle. +*/ +static void *memdbDlOpen(sqlcipher_sqlite3_vfs *pVfs, const char *zPath){ + return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath); } /* -** Convert a UTF-8 string to a multi-byte character string. -** -** Space to hold the returned string is obtained from sqlcipher_sqlite3_malloc(). +** Populate the buffer zErrMsg (size nByte bytes) with a human readable +** utf-8 string describing the most recent error encountered associated +** with dynamic libraries. */ -static char *winUtf8ToMbcs(const char *zText, int useAnsi){ - char *zTextMbcs; - LPWSTR zTmpWide; +static void memdbDlError(sqlcipher_sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ + ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg); +} - zTmpWide = winUtf8ToUnicode(zText); - if( zTmpWide==0 ){ - return 0; - } - zTextMbcs = winUnicodeToMbcs(zTmpWide, useAnsi); - sqlcipher_sqlite3_free(zTmpWide); - return zTextMbcs; +/* +** Return a pointer to the symbol zSymbol in the dynamic library pHandle. +*/ +static void (*memdbDlSym(sqlcipher_sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ + return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym); } /* -** This is a public wrapper for the winUtf8ToUnicode() function. +** Close the dynamic library handle pHandle. */ -SQLITE_API LPWSTR sqlcipher_sqlite3_win32_utf8_to_unicode(const char *zText){ -#ifdef SQLITE_ENABLE_API_ARMOR - if( !zText ){ - (void)SQLITE_MISUSE_BKPT; - return 0; - } -#endif -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; -#endif - return winUtf8ToUnicode(zText); +static void memdbDlClose(sqlcipher_sqlite3_vfs *pVfs, void *pHandle){ + ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle); } /* -** This is a public wrapper for the winUnicodeToUtf8() function. +** Populate the buffer pointed to by zBufOut with nByte bytes of +** random data. */ -SQLITE_API char *sqlcipher_sqlite3_win32_unicode_to_utf8(LPCWSTR zWideText){ -#ifdef SQLITE_ENABLE_API_ARMOR - if( !zWideText ){ - (void)SQLITE_MISUSE_BKPT; - return 0; - } -#endif -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; -#endif - return winUnicodeToUtf8(zWideText); +static int memdbRandomness(sqlcipher_sqlite3_vfs *pVfs, int nByte, char *zBufOut){ + return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut); } /* -** This is a public wrapper for the winMbcsToUtf8() function. +** Sleep for nMicro microseconds. Return the number of microseconds +** actually slept. */ -SQLITE_API char *sqlcipher_sqlite3_win32_mbcs_to_utf8(const char *zText){ -#ifdef SQLITE_ENABLE_API_ARMOR - if( !zText ){ - (void)SQLITE_MISUSE_BKPT; - return 0; - } -#endif -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; -#endif - return winMbcsToUtf8(zText, osAreFileApisANSI()); +static int memdbSleep(sqlcipher_sqlite3_vfs *pVfs, int nMicro){ + return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro); } +#if 0 /* Never used. Modern cores only call xCurrentTimeInt64() */ /* -** This is a public wrapper for the winMbcsToUtf8() function. +** Return the current time as a Julian Day number in *pTimeOut. */ -SQLITE_API char *sqlcipher_sqlite3_win32_mbcs_to_utf8_v2(const char *zText, int useAnsi){ -#ifdef SQLITE_ENABLE_API_ARMOR - if( !zText ){ - (void)SQLITE_MISUSE_BKPT; - return 0; - } -#endif -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; +static int memdbCurrentTime(sqlcipher_sqlite3_vfs *pVfs, double *pTimeOut){ + return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut); +} #endif - return winMbcsToUtf8(zText, useAnsi); + +static int memdbGetLastError(sqlcipher_sqlite3_vfs *pVfs, int a, char *b){ + return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b); +} +static int memdbCurrentTimeInt64(sqlcipher_sqlite3_vfs *pVfs, sqlcipher_sqlite3_int64 *p){ + return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p); } /* -** This is a public wrapper for the winUtf8ToMbcs() function. +** Translate a database connection pointer and schema name into a +** MemFile pointer. */ -SQLITE_API char *sqlcipher_sqlite3_win32_utf8_to_mbcs(const char *zText){ -#ifdef SQLITE_ENABLE_API_ARMOR - if( !zText ){ - (void)SQLITE_MISUSE_BKPT; - return 0; - } -#endif -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; -#endif - return winUtf8ToMbcs(zText, osAreFileApisANSI()); +static MemFile *memdbFromDbSchema(sqlcipher_sqlite3 *db, const char *zSchema){ + MemFile *p = 0; + MemStore *pStore; + int rc = sqlcipher_sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p); + if( rc ) return 0; + if( p->base.pMethods!=&memdb_io_methods ) return 0; + pStore = p->pStore; + memdbEnter(pStore); + if( pStore->zFName!=0 ) p = 0; + memdbLeave(pStore); + return p; } /* -** This is a public wrapper for the winUtf8ToMbcs() function. +** Return the serialization of a database */ -SQLITE_API char *sqlcipher_sqlite3_win32_utf8_to_mbcs_v2(const char *zText, int useAnsi){ +SQLITE_API unsigned char *sqlcipher_sqlite3_serialize( + sqlcipher_sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which database within the connection */ + sqlcipher_sqlite3_int64 *piSize, /* Write size here, if not NULL */ + unsigned int mFlags /* Maybe SQLITE_SERIALIZE_NOCOPY */ +){ + MemFile *p; + int iDb; + Btree *pBt; + sqlcipher_sqlite3_int64 sz; + int szPage = 0; + sqlcipher_sqlite3_stmt *pStmt = 0; + unsigned char *pOut; + char *zSql; + int rc; + #ifdef SQLITE_ENABLE_API_ARMOR - if( !zText ){ + if( !sqlcipher_sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif -#ifndef SQLITE_OMIT_AUTOINIT - if( sqlcipher_sqlite3_initialize() ) return 0; -#endif - return winUtf8ToMbcs(zText, useAnsi); -} -/* -** This function is the same as sqlcipher_sqlite3_win32_set_directory (below); however, -** it accepts a UTF-8 string. -*/ -SQLITE_API int sqlcipher_sqlite3_win32_set_directory8( - unsigned long type, /* Identifier for directory being set or reset */ - const char *zValue /* New value for directory being set or reset */ -){ - char **ppDirectory = 0; -#ifndef SQLITE_OMIT_AUTOINIT - int rc = sqlcipher_sqlite3_initialize(); - if( rc ) return rc; -#endif - if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){ - ppDirectory = &sqlcipher_sqlite3_data_directory; - }else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){ - ppDirectory = &sqlcipher_sqlite3_temp_directory; + if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; + p = memdbFromDbSchema(db, zSchema); + iDb = sqlcipher_sqlite3FindDbName(db, zSchema); + if( piSize ) *piSize = -1; + if( iDb<0 ) return 0; + if( p ){ + MemStore *pStore = p->pStore; + assert( pStore->pMutex==0 ); + if( piSize ) *piSize = pStore->sz; + if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ + pOut = pStore->aData; + }else{ + pOut = sqlcipher_sqlite3_malloc64( pStore->sz ); + if( pOut ) memcpy(pOut, pStore->aData, pStore->sz); + } + return pOut; } - assert( !ppDirectory || type==SQLITE_WIN32_DATA_DIRECTORY_TYPE - || type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE - ); - assert( !ppDirectory || sqlcipher_sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) ); - if( ppDirectory ){ - char *zCopy = 0; - if( zValue && zValue[0] ){ - zCopy = sqlcipher_sqlite3_mprintf("%s", zValue); - if ( zCopy==0 ){ - return SQLITE_NOMEM_BKPT; + pBt = db->aDb[iDb].pBt; + if( pBt==0 ) return 0; + szPage = sqlcipher_sqlite3BtreeGetPageSize(pBt); + zSql = sqlcipher_sqlite3_mprintf("PRAGMA \"%w\".page_count", zSchema); + rc = zSql ? sqlcipher_sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) : SQLITE_NOMEM; + sqlcipher_sqlite3_free(zSql); + if( rc ) return 0; + rc = sqlcipher_sqlite3_step(pStmt); + if( rc!=SQLITE_ROW ){ + pOut = 0; + }else{ + sz = sqlcipher_sqlite3_column_int64(pStmt, 0)*szPage; + if( piSize ) *piSize = sz; + if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ + pOut = 0; + }else{ + pOut = sqlcipher_sqlite3_malloc64( sz ); + if( pOut ){ + int nPage = sqlcipher_sqlite3_column_int(pStmt, 0); + Pager *pPager = sqlcipher_sqlite3BtreePager(pBt); + int pgno; + for(pgno=1; pgno<=nPage; pgno++){ + DbPage *pPage = 0; + unsigned char *pTo = pOut + szPage*(sqlcipher_sqlite3_int64)(pgno-1); + rc = sqlcipher_sqlite3PagerGet(pPager, pgno, (DbPage**)&pPage, 0); + if( rc==SQLITE_OK ){ + memcpy(pTo, sqlcipher_sqlite3PagerGetData(pPage), szPage); + }else{ + memset(pTo, 0, szPage); + } + sqlcipher_sqlite3PagerUnref(pPage); + } } } - sqlcipher_sqlite3_free(*ppDirectory); - *ppDirectory = zCopy; - return SQLITE_OK; } - return SQLITE_ERROR; + sqlcipher_sqlite3_finalize(pStmt); + return pOut; } -/* -** This function is the same as sqlcipher_sqlite3_win32_set_directory (below); however, -** it accepts a UTF-16 string. +/* Convert zSchema to a MemDB and initialize its content. */ -SQLITE_API int sqlcipher_sqlite3_win32_set_directory16( - unsigned long type, /* Identifier for directory being set or reset */ - const void *zValue /* New value for directory being set or reset */ +SQLITE_API int sqlcipher_sqlite3_deserialize( + sqlcipher_sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to reopen with the deserialization */ + unsigned char *pData, /* The serialized database content */ + sqlcipher_sqlite3_int64 szDb, /* Number bytes in the deserialization */ + sqlcipher_sqlite3_int64 szBuf, /* Total size of buffer pData[] */ + unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ ){ + MemFile *p; + char *zSql; + sqlcipher_sqlite3_stmt *pStmt = 0; int rc; - char *zUtf8 = 0; - if( zValue ){ - zUtf8 = sqlcipher_sqlite3_win32_unicode_to_utf8(zValue); - if( zUtf8==0 ) return SQLITE_NOMEM_BKPT; + int iDb; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlcipher_sqlite3SafetyCheckOk(db) ){ + return SQLITE_MISUSE_BKPT; } - rc = sqlcipher_sqlite3_win32_set_directory8(type, zUtf8); - if( zUtf8 ) sqlcipher_sqlite3_free(zUtf8); + if( szDb<0 ) return SQLITE_MISUSE_BKPT; + if( szBuf<0 ) return SQLITE_MISUSE_BKPT; +#endif + + sqlcipher_sqlite3_mutex_enter(db->mutex); + if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; + iDb = sqlcipher_sqlite3FindDbName(db, zSchema); + testcase( iDb==1 ); + if( iDb<2 && iDb!=0 ){ + rc = SQLITE_ERROR; + goto end_deserialize; + } + zSql = sqlcipher_sqlite3_mprintf("ATTACH x AS %Q", zSchema); + if( zSql==0 ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlcipher_sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + sqlcipher_sqlite3_free(zSql); + } + if( rc ) goto end_deserialize; + db->init.iDb = (u8)iDb; + db->init.reopenMemdb = 1; + rc = sqlcipher_sqlite3_step(pStmt); + db->init.reopenMemdb = 0; + if( rc!=SQLITE_DONE ){ + rc = SQLITE_ERROR; + goto end_deserialize; + } + p = memdbFromDbSchema(db, zSchema); + if( p==0 ){ + rc = SQLITE_ERROR; + }else{ + MemStore *pStore = p->pStore; + pStore->aData = pData; + pData = 0; + pStore->sz = szDb; + pStore->szAlloc = szBuf; + pStore->szMax = szBuf; + if( pStore->szMaxszMax = sqlcipher_sqlite3GlobalConfig.mxMemdbSize; + } + pStore->mFlags = mFlags; + rc = SQLITE_OK; + } + +end_deserialize: + sqlcipher_sqlite3_finalize(pStmt); + if( pData && (mFlags & SQLITE_DESERIALIZE_FREEONCLOSE)!=0 ){ + sqlcipher_sqlite3_free(pData); + } + sqlcipher_sqlite3_mutex_leave(db->mutex); return rc; } /* -** This function sets the data directory or the temporary directory based on -** the provided arguments. The type argument must be 1 in order to set the -** data directory or 2 in order to set the temporary directory. The zValue -** argument is the name of the directory to use. The return value will be -** SQLITE_OK if successful. +** This routine is called when the extension is loaded. +** Register the new VFS. */ -SQLITE_API int sqlcipher_sqlite3_win32_set_directory( - unsigned long type, /* Identifier for directory being set or reset */ - void *zValue /* New value for directory being set or reset */ -){ - return sqlcipher_sqlite3_win32_set_directory16(type, zValue); +SQLITE_PRIVATE int sqlcipher_sqlite3MemdbInit(void){ + sqlcipher_sqlite3_vfs *pLower = sqlcipher_sqlite3_vfs_find(0); + unsigned int sz; + if( NEVER(pLower==0) ) return SQLITE_ERROR; + sz = pLower->szOsFile; + memdb_vfs.pAppData = pLower; + /* The following conditional can only be true when compiled for + ** Windows x86 and SQLITE_MAX_MMAP_SIZE=0. We always leave + ** it in, to be safe, but it is marked as NO_TEST since there + ** is no way to reach it under most builds. */ + if( sz 0 ){ - /* allocate a buffer and convert to UTF8 */ - sqlcipher_sqlite3BeginBenignMalloc(); - zOut = winUnicodeToUtf8(zTempWide); - sqlcipher_sqlite3EndBenignMalloc(); -#if !SQLITE_OS_WINRT - /* free the system buffer allocated by FormatMessage */ - osLocalFree(zTempWide); -#endif - } - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - char *zTemp = NULL; - dwLen = osFormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - lastErrno, - 0, - (LPSTR) &zTemp, - 0, - 0); - if( dwLen > 0 ){ - /* allocate a buffer and convert to UTF8 */ - sqlcipher_sqlite3BeginBenignMalloc(); - zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI()); - sqlcipher_sqlite3EndBenignMalloc(); - /* free the system buffer allocated by FormatMessage */ - osLocalFree(zTemp); - } - } -#endif - if( 0 == dwLen ){ - sqlcipher_sqlite3_snprintf(nBuf, zBuf, "OsError 0x%lx (%lu)", lastErrno, lastErrno); - }else{ - /* copy a maximum of nBuf chars to output buffer */ - sqlcipher_sqlite3_snprintf(nBuf, zBuf, "%s", zOut); - /* free the UTF8 buffer */ - sqlcipher_sqlite3_free(zOut); - } - return 0; -} /* +** A bitmap is an instance of the following structure. ** -** This function - winLogErrorAtLine() - is only ever called via the macro -** winLogError(). +** This bitmap records the existence of zero or more bits +** with values between 1 and iSize, inclusive. ** -** This routine is invoked after an error occurs in an OS function. -** It logs a message using sqlcipher_sqlite3_log() containing the current value of -** error code and, if possible, the human-readable equivalent from -** FormatMessage. +** There are three possible representations of the bitmap. +** If iSize<=BITVEC_NBIT, then Bitvec.u.aBitmap[] is a straight +** bitmap. The least significant bit is bit 1. ** -** The first argument passed to the macro should be the error code that -** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). -** The two subsequent arguments should be the name of the OS function that -** failed and the associated file-system path, if any. +** If iSize>BITVEC_NBIT and iDivisor==0 then Bitvec.u.aHash[] is +** a hash table that will hold up to BITVEC_MXHASH distinct values. +** +** Otherwise, the value i is redirected into one of BITVEC_NPTR +** sub-bitmaps pointed to by Bitvec.u.apSub[]. Each subbitmap +** handles up to iDivisor separate values of i. apSub[0] holds +** values between 1 and iDivisor. apSub[1] holds values between +** iDivisor+1 and 2*iDivisor. apSub[N] holds values between +** N*iDivisor+1 and (N+1)*iDivisor. Each subbitmap is normalized +** to hold deal with values between 1 and iDivisor. */ -#define winLogError(a,b,c,d) winLogErrorAtLine(a,b,c,d,__LINE__) -static int winLogErrorAtLine( - int errcode, /* SQLite error code */ - DWORD lastErrno, /* Win32 last error */ - const char *zFunc, /* Name of OS function that failed */ - const char *zPath, /* File path associated with error */ - int iLine /* Source line number where error occurred */ -){ - char zMsg[500]; /* Human readable error text */ - int i; /* Loop counter */ - - zMsg[0] = 0; - winGetLastErrorMsg(lastErrno, sizeof(zMsg), zMsg); - assert( errcode!=SQLITE_OK ); - if( zPath==0 ) zPath = ""; - for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){} - zMsg[i] = 0; - sqlcipher_sqlite3_log(errcode, - "os_win.c:%d: (%lu) %s(%s) - %s", - iLine, lastErrno, zFunc, zPath, zMsg - ); - - return errcode; -} +struct Bitvec { + u32 iSize; /* Maximum bit index. Max iSize is 4,294,967,296. */ + u32 nSet; /* Number of bits that are set - only valid for aHash + ** element. Max is BITVEC_NINT. For BITVEC_SZ of 512, + ** this would be 125. */ + u32 iDivisor; /* Number of bits handled by each apSub[] entry. */ + /* Should >=0 for apSub element. */ + /* Max iDivisor is max(u32) / BITVEC_NPTR + 1. */ + /* For a BITVEC_SZ of 512, this would be 34,359,739. */ + union { + BITVEC_TELEM aBitmap[BITVEC_NELEM]; /* Bitmap representation */ + u32 aHash[BITVEC_NINT]; /* Hash table representation */ + Bitvec *apSub[BITVEC_NPTR]; /* Recursive representation */ + } u; +}; /* -** The number of times that a ReadFile(), WriteFile(), and DeleteFile() -** will be retried following a locking error - probably caused by -** antivirus software. Also the initial delay before the first retry. -** The delay increases linearly with each retry. +** Create a new bitmap object able to handle bits between 0 and iSize, +** inclusive. Return a pointer to the new object. Return NULL if +** malloc fails. */ -#ifndef SQLITE_WIN32_IOERR_RETRY -# define SQLITE_WIN32_IOERR_RETRY 10 -#endif -#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY -# define SQLITE_WIN32_IOERR_RETRY_DELAY 25 -#endif -static int winIoerrRetry = SQLITE_WIN32_IOERR_RETRY; -static int winIoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY; +SQLITE_PRIVATE Bitvec *sqlcipher_sqlite3BitvecCreate(u32 iSize){ + Bitvec *p; + assert( sizeof(*p)==BITVEC_SZ ); + p = sqlcipher_sqlite3MallocZero( sizeof(*p) ); + if( p ){ + p->iSize = iSize; + } + return p; +} /* -** The "winIoerrCanRetry1" macro is used to determine if a particular I/O -** error code obtained via GetLastError() is eligible to be retried. It -** must accept the error code DWORD as its only argument and should return -** non-zero if the error code is transient in nature and the operation -** responsible for generating the original error might succeed upon being -** retried. The argument to this macro should be a variable. -** -** Additionally, a macro named "winIoerrCanRetry2" may be defined. If it -** is defined, it will be consulted only when the macro "winIoerrCanRetry1" -** returns zero. The "winIoerrCanRetry2" macro is completely optional and -** may be used to include additional error codes in the set that should -** result in the failing I/O operation being retried by the caller. If -** defined, the "winIoerrCanRetry2" macro must exhibit external semantics -** identical to those of the "winIoerrCanRetry1" macro. +** Check to see if the i-th bit is set. Return true or false. +** If p is NULL (if the bitmap has not been created) or if +** i is out of range, then return false. */ -#if !defined(winIoerrCanRetry1) -#define winIoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED) || \ - ((a)==ERROR_SHARING_VIOLATION) || \ - ((a)==ERROR_LOCK_VIOLATION) || \ - ((a)==ERROR_DEV_NOT_EXIST) || \ - ((a)==ERROR_NETNAME_DELETED) || \ - ((a)==ERROR_SEM_TIMEOUT) || \ - ((a)==ERROR_NETWORK_UNREACHABLE)) -#endif +SQLITE_PRIVATE int sqlcipher_sqlite3BitvecTestNotNull(Bitvec *p, u32 i){ + assert( p!=0 ); + i--; + if( i>=p->iSize ) return 0; + while( p->iDivisor ){ + u32 bin = i/p->iDivisor; + i = i%p->iDivisor; + p = p->u.apSub[bin]; + if (!p) { + return 0; + } + } + if( p->iSize<=BITVEC_NBIT ){ + return (p->u.aBitmap[i/BITVEC_SZELEM] & (1<<(i&(BITVEC_SZELEM-1))))!=0; + } else{ + u32 h = BITVEC_HASH(i++); + while( p->u.aHash[h] ){ + if( p->u.aHash[h]==i ) return 1; + h = (h+1) % BITVEC_NINT; + } + return 0; + } +} +SQLITE_PRIVATE int sqlcipher_sqlite3BitvecTest(Bitvec *p, u32 i){ + return p!=0 && sqlcipher_sqlite3BitvecTestNotNull(p,i); +} /* -** If a ReadFile() or WriteFile() error occurs, invoke this routine -** to see if it should be retried. Return TRUE to retry. Return FALSE -** to give up with an error. +** Set the i-th bit. Return 0 on success and an error code if +** anything goes wrong. +** +** This routine might cause sub-bitmaps to be allocated. Failing +** to get the memory needed to hold the sub-bitmap is the only +** that can go wrong with an insert, assuming p and i are valid. +** +** The calling function must ensure that p is a valid Bitvec object +** and that the value for "i" is within range of the Bitvec object. +** Otherwise the behavior is undefined. */ -static int winRetryIoerr(int *pnRetry, DWORD *pError){ - DWORD e = osGetLastError(); - if( *pnRetry>=winIoerrRetry ){ - if( pError ){ - *pError = e; +SQLITE_PRIVATE int sqlcipher_sqlite3BitvecSet(Bitvec *p, u32 i){ + u32 h; + if( p==0 ) return SQLITE_OK; + assert( i>0 ); + assert( i<=p->iSize ); + i--; + while((p->iSize > BITVEC_NBIT) && p->iDivisor) { + u32 bin = i/p->iDivisor; + i = i%p->iDivisor; + if( p->u.apSub[bin]==0 ){ + p->u.apSub[bin] = sqlcipher_sqlite3BitvecCreate( p->iDivisor ); + if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM_BKPT; } - return 0; + p = p->u.apSub[bin]; } - if( winIoerrCanRetry1(e) ){ - sqlcipher_sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry)); - ++*pnRetry; - return 1; + if( p->iSize<=BITVEC_NBIT ){ + p->u.aBitmap[i/BITVEC_SZELEM] |= 1 << (i&(BITVEC_SZELEM-1)); + return SQLITE_OK; } -#if defined(winIoerrCanRetry2) - else if( winIoerrCanRetry2(e) ){ - sqlcipher_sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry)); - ++*pnRetry; - return 1; + h = BITVEC_HASH(i++); + /* if there wasn't a hash collision, and this doesn't */ + /* completely fill the hash, then just add it without */ + /* worring about sub-dividing and re-hashing. */ + if( !p->u.aHash[h] ){ + if (p->nSet<(BITVEC_NINT-1)) { + goto bitvec_set_end; + } else { + goto bitvec_set_rehash; + } } -#endif - if( pError ){ - *pError = e; + /* there was a collision, check to see if it's already */ + /* in hash, if not, try to find a spot for it */ + do { + if( p->u.aHash[h]==i ) return SQLITE_OK; + h++; + if( h>=BITVEC_NINT ) h = 0; + } while( p->u.aHash[h] ); + /* we didn't find it in the hash. h points to the first */ + /* available free spot. check to see if this is going to */ + /* make our hash too "full". */ +bitvec_set_rehash: + if( p->nSet>=BITVEC_MXHASH ){ + unsigned int j; + int rc; + u32 *aiValues = sqlcipher_sqlite3StackAllocRaw(0, sizeof(p->u.aHash)); + if( aiValues==0 ){ + return SQLITE_NOMEM_BKPT; + }else{ + memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); + memset(p->u.apSub, 0, sizeof(p->u.apSub)); + p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR; + rc = sqlcipher_sqlite3BitvecSet(p, i); + for(j=0; jnSet++; + p->u.aHash[h] = i; + return SQLITE_OK; } /* -** Log a I/O error retry episode. +** Clear the i-th bit. +** +** pBuf must be a pointer to at least BITVEC_SZ bytes of temporary storage +** that BitvecClear can use to rebuilt its hash table. */ -static void winLogIoerr(int nRetry, int lineno){ - if( nRetry ){ - sqlcipher_sqlite3_log(SQLITE_NOTICE, - "delayed %dms for lock/sharing conflict at line %d", - winIoerrRetryDelay*nRetry*(nRetry+1)/2, lineno - ); +SQLITE_PRIVATE void sqlcipher_sqlite3BitvecClear(Bitvec *p, u32 i, void *pBuf){ + if( p==0 ) return; + assert( i>0 ); + i--; + while( p->iDivisor ){ + u32 bin = i/p->iDivisor; + i = i%p->iDivisor; + p = p->u.apSub[bin]; + if (!p) { + return; + } + } + if( p->iSize<=BITVEC_NBIT ){ + p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1))); + }else{ + unsigned int j; + u32 *aiValues = pBuf; + memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); + memset(p->u.aHash, 0, sizeof(p->u.aHash)); + p->nSet = 0; + for(j=0; jnSet++; + while( p->u.aHash[h] ){ + h++; + if( h>=BITVEC_NINT ) h = 0; + } + p->u.aHash[h] = aiValues[j]; + } + } } } /* -** This #if does not rely on the SQLITE_OS_WINCE define because the -** corresponding section in "date.c" cannot use it. -*/ -#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \ - (!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API) -/* -** The MSVC CRT on Windows CE may not have a localtime() function. -** So define a substitute. +** Destroy a bitmap object. Reclaim all memory used. */ -/* # include */ -struct tm *__cdecl localtime(const time_t *t) -{ - static struct tm y; - FILETIME uTm, lTm; - SYSTEMTIME pTm; - sqlcipher_sqlite3_int64 t64; - t64 = *t; - t64 = (t64 + 11644473600)*10000000; - uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); - uTm.dwHighDateTime= (DWORD)(t64 >> 32); - osFileTimeToLocalFileTime(&uTm,&lTm); - osFileTimeToSystemTime(&lTm,&pTm); - y.tm_year = pTm.wYear - 1900; - y.tm_mon = pTm.wMonth - 1; - y.tm_wday = pTm.wDayOfWeek; - y.tm_mday = pTm.wDay; - y.tm_hour = pTm.wHour; - y.tm_min = pTm.wMinute; - y.tm_sec = pTm.wSecond; - return &y; +SQLITE_PRIVATE void sqlcipher_sqlite3BitvecDestroy(Bitvec *p){ + if( p==0 ) return; + if( p->iDivisor ){ + unsigned int i; + for(i=0; iu.apSub[i]); + } + } + sqlcipher_sqlite3_free(p); } -#endif - -#if SQLITE_OS_WINCE -/************************************************************************* -** This section contains code for WinCE only. -*/ -#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)] /* -** Acquire a lock on the handle h +** Return the value of the iSize parameter specified when Bitvec *p +** was created. */ -static void winceMutexAcquire(HANDLE h){ - DWORD dwErr; - do { - dwErr = osWaitForSingleObject(h, INFINITE); - } while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED); +SQLITE_PRIVATE u32 sqlcipher_sqlite3BitvecSize(Bitvec *p){ + return p->iSize; } + +#ifndef SQLITE_UNTESTABLE /* -** Release a lock acquired by winceMutexAcquire() +** Let V[] be an array of unsigned characters sufficient to hold +** up to N bits. Let I be an integer between 0 and N. 0<=I>3] |= (1<<(I&7)) +#define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7)) +#define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0 /* -** Create the mutex and shared memory used for locking in the file -** descriptor pFile +** This routine runs an extensive test of the Bitvec code. +** +** The input is an array of integers that acts as a program +** to test the Bitvec. The integers are opcodes followed +** by 0, 1, or 3 operands, depending on the opcode. Another +** opcode follows immediately after the last operand. +** +** There are 6 opcodes numbered from 0 through 5. 0 is the +** "halt" opcode and causes the test to end. +** +** 0 Halt and return the number of errors +** 1 N S X Set N bits beginning with S and incrementing by X +** 2 N S X Clear N bits beginning with S and incrementing by X +** 3 N Set N randomly chosen bits +** 4 N Clear N randomly chosen bits +** 5 N S X Set N bits from S increment X in array only, not in bitvec +** +** The opcodes 1 through 4 perform set and clear operations are performed +** on both a Bitvec object and on a linear array of bits obtained from malloc. +** Opcode 5 works on the linear array only, not on the Bitvec. +** Opcode 5 is used to deliberately induce a fault in order to +** confirm that error detection works. +** +** At the conclusion of the test the linear array is compared +** against the Bitvec object. If there are any differences, +** an error is returned. If they are the same, zero is returned. +** +** If a memory allocation error occurs, return -1. */ -static int winceCreateLock(const char *zFilename, winFile *pFile){ - LPWSTR zTok; - LPWSTR zName; - DWORD lastErrno; - BOOL bLogged = FALSE; - BOOL bInit = TRUE; - - zName = winUtf8ToUnicode(zFilename); - if( zName==0 ){ - /* out of memory */ - return SQLITE_IOERR_NOMEM_BKPT; - } - - /* Initialize the local lockdata */ - memset(&pFile->local, 0, sizeof(pFile->local)); - - /* Replace the backslashes from the filename and lowercase it - ** to derive a mutex name. */ - zTok = osCharLowerW(zName); - for (;*zTok;zTok++){ - if (*zTok == '\\') *zTok = '_'; - } - - /* Create/open the named mutex */ - pFile->hMutex = osCreateMutexW(NULL, FALSE, zName); - if (!pFile->hMutex){ - pFile->lastErrno = osGetLastError(); - sqlcipher_sqlite3_free(zName); - return winLogError(SQLITE_IOERR, pFile->lastErrno, - "winceCreateLock1", zFilename); - } - - /* Acquire the mutex before continuing */ - winceMutexAcquire(pFile->hMutex); - - /* Since the names of named mutexes, semaphores, file mappings etc are - ** case-sensitive, take advantage of that by uppercasing the mutex name - ** and using that as the shared filemapping name. - */ - osCharUpperW(zName); - pFile->hShared = osCreateFileMappingW(INVALID_HANDLE_VALUE, NULL, - PAGE_READWRITE, 0, sizeof(winceLock), - zName); +SQLITE_PRIVATE int sqlcipher_sqlite3BitvecBuiltinTest(int sz, int *aOp){ + Bitvec *pBitvec = 0; + unsigned char *pV = 0; + int rc = -1; + int i, nx, pc, op; + void *pTmpSpace; - /* Set a flag that indicates we're the first to create the memory so it - ** must be zero-initialized */ - lastErrno = osGetLastError(); - if (lastErrno == ERROR_ALREADY_EXISTS){ - bInit = FALSE; - } + /* Allocate the Bitvec to be tested and a linear array of + ** bits to act as the reference */ + pBitvec = sqlcipher_sqlite3BitvecCreate( sz ); + pV = sqlcipher_sqlite3MallocZero( (sz+7)/8 + 1 ); + pTmpSpace = sqlcipher_sqlite3_malloc64(BITVEC_SZ); + if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end; - sqlcipher_sqlite3_free(zName); + /* NULL pBitvec tests */ + sqlcipher_sqlite3BitvecSet(0, 1); + sqlcipher_sqlite3BitvecClear(0, 1, pTmpSpace); - /* If we succeeded in making the shared memory handle, map it. */ - if( pFile->hShared ){ - pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared, - FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock)); - /* If mapping failed, close the shared memory handle and erase it */ - if( !pFile->shared ){ - pFile->lastErrno = osGetLastError(); - winLogError(SQLITE_IOERR, pFile->lastErrno, - "winceCreateLock2", zFilename); - bLogged = TRUE; - osCloseHandle(pFile->hShared); - pFile->hShared = NULL; + /* Run the program */ + pc = i = 0; + while( (op = aOp[pc])!=0 ){ + switch( op ){ + case 1: + case 2: + case 5: { + nx = 4; + i = aOp[pc+2] - 1; + aOp[pc+2] += aOp[pc+3]; + break; + } + case 3: + case 4: + default: { + nx = 2; + sqlcipher_sqlite3_randomness(sizeof(i), &i); + break; + } } - } - - /* If shared memory could not be created, then close the mutex and fail */ - if( pFile->hShared==NULL ){ - if( !bLogged ){ - pFile->lastErrno = lastErrno; - winLogError(SQLITE_IOERR, pFile->lastErrno, - "winceCreateLock3", zFilename); - bLogged = TRUE; + if( (--aOp[pc+1]) > 0 ) nx = 0; + pc += nx; + i = (i & 0x7fffffff)%sz; + if( (op & 1)!=0 ){ + SETBIT(pV, (i+1)); + if( op!=5 ){ + if( sqlcipher_sqlite3BitvecSet(pBitvec, i+1) ) goto bitvec_end; + } + }else{ + CLEARBIT(pV, (i+1)); + sqlcipher_sqlite3BitvecClear(pBitvec, i+1, pTmpSpace); } - winceMutexRelease(pFile->hMutex); - osCloseHandle(pFile->hMutex); - pFile->hMutex = NULL; - return SQLITE_IOERR; - } - - /* Initialize the shared memory if we're supposed to */ - if( bInit ){ - memset(pFile->shared, 0, sizeof(winceLock)); } - winceMutexRelease(pFile->hMutex); - return SQLITE_OK; -} - -/* -** Destroy the part of winFile that deals with wince locks -*/ -static void winceDestroyLock(winFile *pFile){ - if (pFile->hMutex){ - /* Acquire the mutex */ - winceMutexAcquire(pFile->hMutex); - - /* The following blocks should probably assert in debug mode, but they - are to cleanup in case any locks remained open */ - if (pFile->local.nReaders){ - pFile->shared->nReaders --; - } - if (pFile->local.bReserved){ - pFile->shared->bReserved = FALSE; - } - if (pFile->local.bPending){ - pFile->shared->bPending = FALSE; - } - if (pFile->local.bExclusive){ - pFile->shared->bExclusive = FALSE; - } - - /* De-reference and close our copy of the shared memory handle */ - osUnmapViewOfFile(pFile->shared); - osCloseHandle(pFile->hShared); - - /* Done with the mutex */ - winceMutexRelease(pFile->hMutex); - osCloseHandle(pFile->hMutex); - pFile->hMutex = NULL; + /* Test to make sure the linear array exactly matches the + ** Bitvec object. Start with the assumption that they do + ** match (rc==0). Change rc to non-zero if a discrepancy + ** is found. + */ + rc = sqlcipher_sqlite3BitvecTest(0,0) + sqlcipher_sqlite3BitvecTest(pBitvec, sz+1) + + sqlcipher_sqlite3BitvecTest(pBitvec, 0) + + (sqlcipher_sqlite3BitvecSize(pBitvec) - sz); + for(i=1; i<=sz; i++){ + if( (TESTBIT(pV,i))!=sqlcipher_sqlite3BitvecTest(pBitvec,i) ){ + rc = i; + break; + } } + + /* Free allocated structure */ +bitvec_end: + sqlcipher_sqlite3_free(pTmpSpace); + sqlcipher_sqlite3_free(pV); + sqlcipher_sqlite3BitvecDestroy(pBitvec); + return rc; } +#endif /* SQLITE_UNTESTABLE */ +/************** End of bitvec.c **********************************************/ +/************** Begin file pcache.c ******************************************/ /* -** An implementation of the LockFile() API of Windows for CE +** 2008 August 05 +** +** 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 that page cache. */ -static BOOL winceLockFile( - LPHANDLE phFile, - DWORD dwFileOffsetLow, - DWORD dwFileOffsetHigh, - DWORD nNumberOfBytesToLockLow, - DWORD nNumberOfBytesToLockHigh -){ - winFile *pFile = HANDLE_TO_WINFILE(phFile); - BOOL bReturn = FALSE; +/* #include "sqliteInt.h" */ - UNUSED_PARAMETER(dwFileOffsetHigh); - UNUSED_PARAMETER(nNumberOfBytesToLockHigh); +/* +** A complete page cache is an instance of this structure. Every +** entry in the cache holds a single page of the database file. The +** btree layer only operates on the cached copy of the database pages. +** +** A page cache entry is "clean" if it exactly matches what is currently +** on disk. A page is "dirty" if it has been modified and needs to be +** persisted to disk. +** +** pDirty, pDirtyTail, pSynced: +** All dirty pages are linked into the doubly linked list using +** PgHdr.pDirtyNext and pDirtyPrev. The list is maintained in LRU order +** such that p was added to the list more recently than p->pDirtyNext. +** PCache.pDirty points to the first (newest) element in the list and +** pDirtyTail to the last (oldest). +** +** The PCache.pSynced variable is used to optimize searching for a dirty +** page to eject from the cache mid-transaction. It is better to eject +** a page that does not require a journal sync than one that does. +** Therefore, pSynced is maintained so that it *almost* always points +** to either the oldest page in the pDirty/pDirtyTail list that has a +** clear PGHDR_NEED_SYNC flag or to a page that is older than this one +** (so that the right page to eject can be found by following pDirtyPrev +** pointers). +*/ +struct PCache { + PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ + PgHdr *pSynced; /* Last synced page in dirty page list */ + int nRefSum; /* Sum of ref counts over all pages */ + int szCache; /* Configured cache size */ + int szSpill; /* Size before spilling occurs */ + int szPage; /* Size of every page in this cache */ + int szExtra; /* Size of extra space for each page */ + u8 bPurgeable; /* True if pages are on backing store */ + u8 eCreate; /* eCreate value for for xFetch() */ + int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */ + void *pStress; /* Argument to xStress */ + sqlcipher_sqlite3_pcache *pCache; /* Pluggable cache module */ +}; - if (!pFile->hMutex) return TRUE; - winceMutexAcquire(pFile->hMutex); +/********************************** Test and Debug Logic **********************/ +/* +** Debug tracing macros. Enable by by changing the "0" to "1" and +** recompiling. +** +** When sqlcipher_sqlite3PcacheTrace is 1, single line trace messages are issued. +** When sqlcipher_sqlite3PcacheTrace is 2, a dump of the pcache showing all cache entries +** is displayed for many operations, resulting in a lot of output. +*/ +#if defined(SQLITE_DEBUG) && 0 + int sqlcipher_sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */ + int sqlcipher_sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */ +# define pcacheTrace(X) if(sqlcipher_sqlite3PcacheTrace){sqlcipher_sqlite3DebugPrintf X;} + void pcacheDump(PCache *pCache){ + int N; + int i, j; + sqlcipher_sqlite3_pcache_page *pLower; + PgHdr *pPg; + unsigned char *a; - /* Wanting an exclusive lock? */ - if (dwFileOffsetLow == (DWORD)SHARED_FIRST - && nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){ - if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0){ - pFile->shared->bExclusive = TRUE; - pFile->local.bExclusive = TRUE; - bReturn = TRUE; + if( sqlcipher_sqlite3PcacheTrace<2 ) return; + if( pCache->pCache==0 ) return; + N = sqlcipher_sqlite3PcachePagecount(pCache); + if( N>sqlcipher_sqlite3PcacheMxDump ) N = sqlcipher_sqlite3PcacheMxDump; + for(i=1; i<=N; i++){ + pLower = sqlcipher_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0); + if( pLower==0 ) continue; + pPg = (PgHdr*)pLower->pExtra; + printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); + a = (unsigned char *)pLower->pBuf; + for(j=0; j<12; j++) printf("%02x", a[j]); + printf("\n"); + if( pPg->pPage==0 ){ + sqlcipher_sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0); + } } } + #else +# define pcacheTrace(X) +# define pcacheDump(X) +#endif - /* Want a read-only lock? */ - else if (dwFileOffsetLow == (DWORD)SHARED_FIRST && - nNumberOfBytesToLockLow == 1){ - if (pFile->shared->bExclusive == 0){ - pFile->local.nReaders ++; - if (pFile->local.nReaders == 1){ - pFile->shared->nReaders ++; - } - bReturn = TRUE; - } +/* +** Check invariants on a PgHdr entry. Return true if everything is OK. +** Return false if any invariant is violated. +** +** This routine is for use inside of assert() statements only. For +** example: +** +** assert( sqlcipher_sqlite3PcachePageSanity(pPg) ); +*/ +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlcipher_sqlite3PcachePageSanity(PgHdr *pPg){ + PCache *pCache; + assert( pPg!=0 ); + assert( pPg->pgno>0 || pPg->pPager==0 ); /* Page number is 1 or more */ + pCache = pPg->pCache; + assert( pCache!=0 ); /* Every page has an associated PCache */ + if( pPg->flags & PGHDR_CLEAN ){ + assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */ + assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */ + assert( pCache->pDirtyTail!=pPg ); } - - /* Want a pending lock? */ - else if (dwFileOffsetLow == (DWORD)PENDING_BYTE - && nNumberOfBytesToLockLow == 1){ - /* If no pending lock has been acquired, then acquire it */ - if (pFile->shared->bPending == 0) { - pFile->shared->bPending = TRUE; - pFile->local.bPending = TRUE; - bReturn = TRUE; - } + /* WRITEABLE pages must also be DIRTY */ + if( pPg->flags & PGHDR_WRITEABLE ){ + assert( pPg->flags & PGHDR_DIRTY ); /* WRITEABLE implies DIRTY */ } + /* NEED_SYNC can be set independently of WRITEABLE. This can happen, + ** for example, when using the sqlcipher_sqlite3PagerDontWrite() optimization: + ** (1) Page X is journalled, and gets WRITEABLE and NEED_SEEK. + ** (2) Page X moved to freelist, WRITEABLE is cleared + ** (3) Page X reused, WRITEABLE is set again + ** If NEED_SYNC had been cleared in step 2, then it would not be reset + ** in step 3, and page might be written into the database without first + ** syncing the rollback journal, which might cause corruption on a power + ** loss. + ** + ** Another example is when the database page size is smaller than the + ** disk sector size. When any page of a sector is journalled, all pages + ** in that sector are marked NEED_SYNC even if they are still CLEAN, just + ** in case they are later modified, since all pages in the same sector + ** must be journalled and synced before any of those pages can be safely + ** written. + */ + return 1; +} +#endif /* SQLITE_DEBUG */ - /* Want a reserved lock? */ - else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE - && nNumberOfBytesToLockLow == 1){ - if (pFile->shared->bReserved == 0) { - pFile->shared->bReserved = TRUE; - pFile->local.bReserved = TRUE; - bReturn = TRUE; - } - } - winceMutexRelease(pFile->hMutex); - return bReturn; -} +/********************************** Linked List Management ********************/ + +/* Allowed values for second argument to pcacheManageDirtyList() */ +#define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */ +#define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */ +#define PCACHE_DIRTYLIST_FRONT 3 /* Move pPage to the front of the list */ /* -** An implementation of the UnlockFile API of Windows for CE +** Manage pPage's participation on the dirty list. Bits of the addRemove +** argument determines what operation to do. The 0x01 bit means first +** remove pPage from the dirty list. The 0x02 means add pPage back to +** the dirty list. Doing both moves pPage to the front of the dirty list. */ -static BOOL winceUnlockFile( - LPHANDLE phFile, - DWORD dwFileOffsetLow, - DWORD dwFileOffsetHigh, - DWORD nNumberOfBytesToUnlockLow, - DWORD nNumberOfBytesToUnlockHigh -){ - winFile *pFile = HANDLE_TO_WINFILE(phFile); - BOOL bReturn = FALSE; - - UNUSED_PARAMETER(dwFileOffsetHigh); - UNUSED_PARAMETER(nNumberOfBytesToUnlockHigh); +static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){ + PCache *p = pPage->pCache; - if (!pFile->hMutex) return TRUE; - winceMutexAcquire(pFile->hMutex); + pcacheTrace(("%p.DIRTYLIST.%s %d\n", p, + addRemove==1 ? "REMOVE" : addRemove==2 ? "ADD" : "FRONT", + pPage->pgno)); + if( addRemove & PCACHE_DIRTYLIST_REMOVE ){ + assert( pPage->pDirtyNext || pPage==p->pDirtyTail ); + assert( pPage->pDirtyPrev || pPage==p->pDirty ); - /* Releasing a reader lock or an exclusive lock */ - if (dwFileOffsetLow == (DWORD)SHARED_FIRST){ - /* Did we have an exclusive lock? */ - if (pFile->local.bExclusive){ - assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE); - pFile->local.bExclusive = FALSE; - pFile->shared->bExclusive = FALSE; - bReturn = TRUE; + /* Update the PCache1.pSynced variable if necessary. */ + if( p->pSynced==pPage ){ + p->pSynced = pPage->pDirtyPrev; } - /* Did we just have a reader lock? */ - else if (pFile->local.nReaders){ - assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE - || nNumberOfBytesToUnlockLow == 1); - pFile->local.nReaders --; - if (pFile->local.nReaders == 0) - { - pFile->shared->nReaders --; + if( pPage->pDirtyNext ){ + pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev; + }else{ + assert( pPage==p->pDirtyTail ); + p->pDirtyTail = pPage->pDirtyPrev; + } + if( pPage->pDirtyPrev ){ + pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext; + }else{ + /* If there are now no dirty pages in the cache, set eCreate to 2. + ** This is an optimization that allows sqlcipher_sqlite3PcacheFetch() to skip + ** searching for a dirty page to eject from the cache when it might + ** otherwise have to. */ + assert( pPage==p->pDirty ); + p->pDirty = pPage->pDirtyNext; + assert( p->bPurgeable || p->eCreate==2 ); + if( p->pDirty==0 ){ /*OPTIMIZATION-IF-TRUE*/ + assert( p->bPurgeable==0 || p->eCreate==1 ); + p->eCreate = 2; } - bReturn = TRUE; } } - - /* Releasing a pending lock */ - else if (dwFileOffsetLow == (DWORD)PENDING_BYTE - && nNumberOfBytesToUnlockLow == 1){ - if (pFile->local.bPending){ - pFile->local.bPending = FALSE; - pFile->shared->bPending = FALSE; - bReturn = TRUE; + if( addRemove & PCACHE_DIRTYLIST_ADD ){ + pPage->pDirtyPrev = 0; + pPage->pDirtyNext = p->pDirty; + if( pPage->pDirtyNext ){ + assert( pPage->pDirtyNext->pDirtyPrev==0 ); + pPage->pDirtyNext->pDirtyPrev = pPage; + }else{ + p->pDirtyTail = pPage; + if( p->bPurgeable ){ + assert( p->eCreate==2 ); + p->eCreate = 1; + } } - } - /* Releasing a reserved lock */ - else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE - && nNumberOfBytesToUnlockLow == 1){ - if (pFile->local.bReserved) { - pFile->local.bReserved = FALSE; - pFile->shared->bReserved = FALSE; - bReturn = TRUE; + p->pDirty = pPage; + + /* If pSynced is NULL and this page has a clear NEED_SYNC flag, set + ** pSynced to point to it. Checking the NEED_SYNC flag is an + ** optimization, as if pSynced points to a page with the NEED_SYNC + ** flag set sqlcipher_sqlite3PcacheFetchStress() searches through all newer + ** entries of the dirty-list for a page with NEED_SYNC clear anyway. */ + if( !p->pSynced + && 0==(pPage->flags&PGHDR_NEED_SYNC) /*OPTIMIZATION-IF-FALSE*/ + ){ + p->pSynced = pPage; } } - - winceMutexRelease(pFile->hMutex); - return bReturn; + pcacheDump(p); } -/* -** End of the special code for wince -*****************************************************************************/ -#endif /* SQLITE_OS_WINCE */ /* -** Lock a file region. +** Wrapper around the pluggable caches xUnpin method. If the cache is +** being used for an in-memory database, this function is a no-op. */ -static BOOL winLockFile( - LPHANDLE phFile, - DWORD flags, - DWORD offsetLow, - DWORD offsetHigh, - DWORD numBytesLow, - DWORD numBytesHigh -){ -#if SQLITE_OS_WINCE - /* - ** NOTE: Windows CE is handled differently here due its lack of the Win32 - ** API LockFile. - */ - return winceLockFile(phFile, offsetLow, offsetHigh, - numBytesLow, numBytesHigh); -#else - if( osIsNT() ){ - OVERLAPPED ovlp; - memset(&ovlp, 0, sizeof(OVERLAPPED)); - ovlp.Offset = offsetLow; - ovlp.OffsetHigh = offsetHigh; - return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp); - }else{ - return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow, - numBytesHigh); +static void pcacheUnpin(PgHdr *p){ + if( p->pCache->bPurgeable ){ + pcacheTrace(("%p.UNPIN %d\n", p->pCache, p->pgno)); + sqlcipher_sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 0); + pcacheDump(p->pCache); } -#endif } /* -** Unlock a file region. - */ -static BOOL winUnlockFile( - LPHANDLE phFile, - DWORD offsetLow, - DWORD offsetHigh, - DWORD numBytesLow, - DWORD numBytesHigh -){ -#if SQLITE_OS_WINCE - /* - ** NOTE: Windows CE is handled differently here due its lack of the Win32 - ** API UnlockFile. - */ - return winceUnlockFile(phFile, offsetLow, offsetHigh, - numBytesLow, numBytesHigh); -#else - if( osIsNT() ){ - OVERLAPPED ovlp; - memset(&ovlp, 0, sizeof(OVERLAPPED)); - ovlp.Offset = offsetLow; - ovlp.OffsetHigh = offsetHigh; - return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp); +** Compute the number of pages of cache requested. p->szCache is the +** cache size requested by the "PRAGMA cache_size" statement. +*/ +static int numberOfCachePages(PCache *p){ + if( p->szCache>=0 ){ + /* IMPLEMENTATION-OF: R-42059-47211 If the argument N is positive then the + ** suggested cache size is set to N. */ + return p->szCache; }else{ - return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow, - numBytesHigh); + i64 n; + /* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the + ** number of cache pages is adjusted to be a number of pages that would + ** use approximately abs(N*1024) bytes of memory based on the current + ** page size. */ + n = ((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); + if( n>1000000000 ) n = 1000000000; + return (int)n; } -#endif } -/***************************************************************************** -** The next group of routines implement the I/O methods specified -** by the sqlcipher_sqlite3_io_methods object. -******************************************************************************/ +/*************************************************** General Interfaces ****** +** +** Initialize and shutdown the page cache subsystem. Neither of these +** functions are threadsafe. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PcacheInitialize(void){ + if( sqlcipher_sqlite3GlobalConfig.pcache2.xInit==0 ){ + /* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the + ** built-in default page cache is used instead of the application defined + ** page cache. */ + sqlcipher_sqlite3PCacheSetDefault(); + assert( sqlcipher_sqlite3GlobalConfig.pcache2.xInit!=0 ); + } + return sqlcipher_sqlite3GlobalConfig.pcache2.xInit(sqlcipher_sqlite3GlobalConfig.pcache2.pArg); +} +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheShutdown(void){ + if( sqlcipher_sqlite3GlobalConfig.pcache2.xShutdown ){ + /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */ + sqlcipher_sqlite3GlobalConfig.pcache2.xShutdown(sqlcipher_sqlite3GlobalConfig.pcache2.pArg); + } +} /* -** Some Microsoft compilers lack this definition. +** Return the size in bytes of a PCache object. */ -#ifndef INVALID_SET_FILE_POINTER -# define INVALID_SET_FILE_POINTER ((DWORD)-1) -#endif +SQLITE_PRIVATE int sqlcipher_sqlite3PcacheSize(void){ return sizeof(PCache); } /* -** Move the current position of the file handle passed as the first -** argument to offset iOffset within the file. If successful, return 0. -** Otherwise, set pFile->lastErrno and return non-zero. +** Create a new PCache object. Storage space to hold the object +** has already been allocated and is passed in as the p pointer. +** The caller discovers how much space needs to be allocated by +** calling sqlcipher_sqlite3PcacheSize(). +** +** szExtra is some extra space allocated for each page. The first +** 8 bytes of the extra space will be zeroed as the page is allocated, +** but remaining content will be uninitialized. Though it is opaque +** to this module, the extra space really ends up being the MemPage +** structure in the pager. */ -static int winSeekFile(winFile *pFile, sqlcipher_sqlite3_int64 iOffset){ -#if !SQLITE_OS_WINRT - LONG upperBits; /* Most sig. 32 bits of new offset */ - LONG lowerBits; /* Least sig. 32 bits of new offset */ - DWORD dwRet; /* Value returned by SetFilePointer() */ - DWORD lastErrno; /* Value returned by GetLastError() */ - - OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset)); - - upperBits = (LONG)((iOffset>>32) & 0x7fffffff); - lowerBits = (LONG)(iOffset & 0xffffffff); - - /* API oddity: If successful, SetFilePointer() returns a dword - ** containing the lower 32-bits of the new file-offset. Or, if it fails, - ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, - ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine - ** whether an error has actually occurred, it is also necessary to call - ** GetLastError(). - */ - dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); - - if( (dwRet==INVALID_SET_FILE_POINTER - && ((lastErrno = osGetLastError())!=NO_ERROR)) ){ - pFile->lastErrno = lastErrno; - winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, - "winSeekFile", pFile->zPath); - OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); - return 1; - } - - OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); - return 0; -#else - /* - ** Same as above, except that this implementation works for WinRT. - */ - - LARGE_INTEGER x; /* The new offset */ - BOOL bRet; /* Value returned by SetFilePointerEx() */ - - x.QuadPart = iOffset; - bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN); +SQLITE_PRIVATE int sqlcipher_sqlite3PcacheOpen( + int szPage, /* Size of every page */ + int szExtra, /* Extra space associated with each page */ + int bPurgeable, /* True if pages are on backing store */ + int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */ + void *pStress, /* Argument to xStress */ + PCache *p /* Preallocated space for the PCache */ +){ + memset(p, 0, sizeof(PCache)); + p->szPage = 1; + p->szExtra = szExtra; + assert( szExtra>=8 ); /* First 8 bytes will be zeroed */ + p->bPurgeable = bPurgeable; + p->eCreate = 2; + p->xStress = xStress; + p->pStress = pStress; + p->szCache = 100; + p->szSpill = 1; + pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n",p,szPage,bPurgeable)); + return sqlcipher_sqlite3PcacheSetPageSize(p, szPage); +} - if(!bRet){ - pFile->lastErrno = osGetLastError(); - winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, - "winSeekFile", pFile->zPath); - OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); - return 1; +/* +** Change the page size for PCache object. The caller must ensure that there +** are no outstanding page references when this function is called. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ + assert( pCache->nRefSum==0 && pCache->pDirty==0 ); + if( pCache->szPage ){ + sqlcipher_sqlite3_pcache *pNew; + pNew = sqlcipher_sqlite3GlobalConfig.pcache2.xCreate( + szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)), + pCache->bPurgeable + ); + if( pNew==0 ) return SQLITE_NOMEM_BKPT; + sqlcipher_sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache)); + if( pCache->pCache ){ + sqlcipher_sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); + } + pCache->pCache = pNew; + pCache->szPage = szPage; + pcacheTrace(("%p.PAGESIZE %d\n",pCache,szPage)); } - - OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); - return 0; -#endif + return SQLITE_OK; } -#if SQLITE_MAX_MMAP_SIZE>0 -/* Forward references to VFS helper methods used for memory mapped files */ -static int winMapfile(winFile*, sqlcipher_sqlite3_int64); -static int winUnmapfile(winFile*); -#endif - /* -** Close a file. +** Try to obtain a page from the cache. ** -** It is reported that an attempt to close a handle might sometimes -** fail. This is a very unreasonable result, but Windows is notorious -** for being unreasonable so I do not doubt that it might happen. If -** the close fails, we pause for 100 milliseconds and try again. As -** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before -** giving up and returning an error. +** This routine returns a pointer to an sqlcipher_sqlite3_pcache_page object if +** such an object is already in cache, or if a new one is created. +** This routine returns a NULL pointer if the object was not in cache +** and could not be created. +** +** The createFlags should be 0 to check for existing pages and should +** be 3 (not 1, but 3) to try to create a new page. +** +** If the createFlag is 0, then NULL is always returned if the page +** is not already in the cache. If createFlag is 1, then a new page +** is created only if that can be done without spilling dirty pages +** and without exceeding the cache size limit. +** +** The caller needs to invoke sqlcipher_sqlite3PcacheFetchFinish() to properly +** initialize the sqlcipher_sqlite3_pcache_page object and convert it into a +** PgHdr object. The sqlcipher_sqlite3PcacheFetch() and sqlcipher_sqlite3PcacheFetchFinish() +** routines are split this way for performance reasons. When separated +** they can both (usually) operate without having to push values to +** the stack on entry and pop them back off on exit, which saves a +** lot of pushing and popping. */ -#define MX_CLOSE_ATTEMPT 3 -static int winClose(sqlcipher_sqlite3_file *id){ - int rc, cnt = 0; - winFile *pFile = (winFile*)id; - - assert( id!=0 ); -#ifndef SQLITE_OMIT_WAL - assert( pFile->pShm==0 ); -#endif - assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE ); - OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p\n", - osGetCurrentProcessId(), pFile, pFile->h)); +SQLITE_PRIVATE sqlcipher_sqlite3_pcache_page *sqlcipher_sqlite3PcacheFetch( + PCache *pCache, /* Obtain the page from this cache */ + Pgno pgno, /* Page number to obtain */ + int createFlag /* If true, create page if it does not exist already */ +){ + int eCreate; + sqlcipher_sqlite3_pcache_page *pRes; -#if SQLITE_MAX_MMAP_SIZE>0 - winUnmapfile(pFile); -#endif + assert( pCache!=0 ); + assert( pCache->pCache!=0 ); + assert( createFlag==3 || createFlag==0 ); + assert( pCache->eCreate==((pCache->bPurgeable && pCache->pDirty) ? 1 : 2) ); - do{ - rc = osCloseHandle(pFile->h); - /* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */ - }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (sqlcipher_sqlite3_win32_sleep(100), 1) ); -#if SQLITE_OS_WINCE -#define WINCE_DELETION_ATTEMPTS 3 - { - winVfsAppData *pAppData = (winVfsAppData*)pFile->pVfs->pAppData; - if( pAppData==NULL || !pAppData->bNoLock ){ - winceDestroyLock(pFile); - } - } - if( pFile->zDeleteOnClose ){ - int cnt = 0; - while( - osDeleteFileW(pFile->zDeleteOnClose)==0 - && osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff - && cnt++ < WINCE_DELETION_ATTEMPTS - ){ - sqlcipher_sqlite3_win32_sleep(100); /* Wait a little before trying again */ - } - sqlcipher_sqlite3_free(pFile->zDeleteOnClose); - } -#endif - if( rc ){ - pFile->h = NULL; - } - OpenCounter(-1); - OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p, rc=%s\n", - osGetCurrentProcessId(), pFile, pFile->h, rc ? "ok" : "failed")); - return rc ? SQLITE_OK - : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(), - "winClose", pFile->zPath); + /* eCreate defines what to do if the page does not exist. + ** 0 Do not allocate a new page. (createFlag==0) + ** 1 Allocate a new page if doing so is inexpensive. + ** (createFlag==1 AND bPurgeable AND pDirty) + ** 2 Allocate a new page even it doing so is difficult. + ** (createFlag==1 AND !(bPurgeable AND pDirty) + */ + eCreate = createFlag & pCache->eCreate; + assert( eCreate==0 || eCreate==1 || eCreate==2 ); + assert( createFlag==0 || pCache->eCreate==eCreate ); + assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) ); + pRes = sqlcipher_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); + pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno, + createFlag?" create":"",pRes)); + return pRes; } /* -** Read data from a file into a buffer. Return SQLITE_OK if all -** bytes were read successfully and SQLITE_IOERR if anything goes -** wrong. +** If the sqlcipher_sqlite3PcacheFetch() routine is unable to allocate a new +** page because no clean pages are available for reuse and the cache +** size limit has been reached, then this routine can be invoked to +** try harder to allocate a page. This routine might invoke the stress +** callback to spill dirty pages to the journal. It will then try to +** allocate the new page and will only fail to allocate a new page on +** an OOM error. +** +** This routine should be invoked only after sqlcipher_sqlite3PcacheFetch() fails. */ -static int winRead( - sqlcipher_sqlite3_file *id, /* File to read from */ - void *pBuf, /* Write content into this buffer */ - int amt, /* Number of bytes to read */ - sqlcipher_sqlite3_int64 offset /* Begin reading at this offset */ +SQLITE_PRIVATE int sqlcipher_sqlite3PcacheFetchStress( + PCache *pCache, /* Obtain the page from this cache */ + Pgno pgno, /* Page number to obtain */ + sqlcipher_sqlite3_pcache_page **ppPage /* Write result here */ ){ -#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) - OVERLAPPED overlapped; /* The offset for ReadFile. */ -#endif - winFile *pFile = (winFile*)id; /* file handle */ - DWORD nRead; /* Number of bytes actually read from file */ - int nRetry = 0; /* Number of retrys */ - - assert( id!=0 ); - assert( amt>0 ); - assert( offset>=0 ); - SimulateIOError(return SQLITE_IOERR_READ); - OSTRACE(("READ pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, " - "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile, - pFile->h, pBuf, amt, offset, pFile->locktype)); + PgHdr *pPg; + if( pCache->eCreate==2 ) return 0; -#if SQLITE_MAX_MMAP_SIZE>0 - /* Deal with as much of this read request as possible by transfering - ** data from the memory mapping using memcpy(). */ - if( offsetmmapSize ){ - if( offset+amt <= pFile->mmapSize ){ - memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); - OSTRACE(("READ-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return SQLITE_OK; - }else{ - int nCopy = (int)(pFile->mmapSize - offset); - memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); - pBuf = &((u8 *)pBuf)[nCopy]; - amt -= nCopy; - offset += nCopy; + if( sqlcipher_sqlite3PcachePagecount(pCache)>pCache->szSpill ){ + /* Find a dirty page to write-out and recycle. First try to find a + ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC + ** cleared), but if that is not possible settle for any other + ** unreferenced dirty page. + ** + ** If the LRU page in the dirty list that has a clear PGHDR_NEED_SYNC + ** flag is currently referenced, then the following may leave pSynced + ** set incorrectly (pointing to other than the LRU page with NEED_SYNC + ** cleared). This is Ok, as pSynced is just an optimization. */ + for(pPg=pCache->pSynced; + pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); + pPg=pPg->pDirtyPrev + ); + pCache->pSynced = pPg; + if( !pPg ){ + for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev); } - } -#endif - -#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) - if( winSeekFile(pFile, offset) ){ - OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return SQLITE_FULL; - } - while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ -#else - memset(&overlapped, 0, sizeof(OVERLAPPED)); - overlapped.Offset = (LONG)(offset & 0xffffffff); - overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); - while( !osReadFile(pFile->h, pBuf, amt, &nRead, &overlapped) && - osGetLastError()!=ERROR_HANDLE_EOF ){ + if( pPg ){ + int rc; +#ifdef SQLITE_LOG_CACHE_SPILL + sqlcipher_sqlite3_log(SQLITE_FULL, + "spill page %d making room for %d - cache used: %d/%d", + pPg->pgno, pgno, + sqlcipher_sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache), + numberOfCachePages(pCache)); #endif - DWORD lastErrno; - if( winRetryIoerr(&nRetry, &lastErrno) ) continue; - pFile->lastErrno = lastErrno; - OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_READ\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return winLogError(SQLITE_IOERR_READ, pFile->lastErrno, - "winRead", pFile->zPath); - } - winLogIoerr(nRetry, __LINE__); - if( nRead<(DWORD)amt ){ - /* Unread parts of the buffer must be zero-filled */ - memset(&((char*)pBuf)[nRead], 0, amt-nRead); - OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_SHORT_READ\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return SQLITE_IOERR_SHORT_READ; + pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno)); + rc = pCache->xStress(pCache->pStress, pPg); + pcacheDump(pCache); + if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ + return rc; + } + } } + *ppPage = sqlcipher_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2); + return *ppPage==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK; +} - OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return SQLITE_OK; +/* +** This is a helper routine for sqlcipher_sqlite3PcacheFetchFinish() +** +** In the uncommon case where the page being fetched has not been +** initialized, this routine is invoked to do the initialization. +** This routine is broken out into a separate function since it +** requires extra stack manipulation that can be avoided in the common +** case. +*/ +static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit( + PCache *pCache, /* Obtain the page from this cache */ + Pgno pgno, /* Page number obtained */ + sqlcipher_sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */ +){ + PgHdr *pPgHdr; + assert( pPage!=0 ); + pPgHdr = (PgHdr*)pPage->pExtra; + assert( pPgHdr->pPage==0 ); + memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr,pDirty)); + pPgHdr->pPage = pPage; + pPgHdr->pData = pPage->pBuf; + pPgHdr->pExtra = (void *)&pPgHdr[1]; + memset(pPgHdr->pExtra, 0, 8); + pPgHdr->pCache = pCache; + pPgHdr->pgno = pgno; + pPgHdr->flags = PGHDR_CLEAN; + return sqlcipher_sqlite3PcacheFetchFinish(pCache,pgno,pPage); } /* -** Write data from a buffer into a file. Return SQLITE_OK on success -** or some other error code on failure. +** This routine converts the sqlcipher_sqlite3_pcache_page object returned by +** sqlcipher_sqlite3PcacheFetch() into an initialized PgHdr object. This routine +** must be called after sqlcipher_sqlite3PcacheFetch() in order to get a usable +** result. */ -static int winWrite( - sqlcipher_sqlite3_file *id, /* File to write into */ - const void *pBuf, /* The bytes to be written */ - int amt, /* Number of bytes to write */ - sqlcipher_sqlite3_int64 offset /* Offset into the file to begin writing at */ +SQLITE_PRIVATE PgHdr *sqlcipher_sqlite3PcacheFetchFinish( + PCache *pCache, /* Obtain the page from this cache */ + Pgno pgno, /* Page number obtained */ + sqlcipher_sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */ ){ - int rc = 0; /* True if error has occurred, else false */ - winFile *pFile = (winFile*)id; /* File handle */ - int nRetry = 0; /* Number of retries */ + PgHdr *pPgHdr; - assert( amt>0 ); - assert( pFile ); - SimulateIOError(return SQLITE_IOERR_WRITE); - SimulateDiskfullError(return SQLITE_FULL); + assert( pPage!=0 ); + pPgHdr = (PgHdr *)pPage->pExtra; - OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, " - "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile, - pFile->h, pBuf, amt, offset, pFile->locktype)); + if( !pPgHdr->pPage ){ + return pcacheFetchFinishWithInit(pCache, pgno, pPage); + } + pCache->nRefSum++; + pPgHdr->nRef++; + assert( sqlcipher_sqlite3PcachePageSanity(pPgHdr) ); + return pPgHdr; +} -#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 - /* Deal with as much of this write request as possible by transfering - ** data from the memory mapping using memcpy(). */ - if( offsetmmapSize ){ - if( offset+amt <= pFile->mmapSize ){ - memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); - OSTRACE(("WRITE-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return SQLITE_OK; +/* +** Decrement the reference count on a page. If the page is clean and the +** reference count drops to 0, then it is made eligible for recycling. +*/ +SQLITE_PRIVATE void SQLITE_NOINLINE sqlcipher_sqlite3PcacheRelease(PgHdr *p){ + assert( p->nRef>0 ); + p->pCache->nRefSum--; + if( (--p->nRef)==0 ){ + if( p->flags&PGHDR_CLEAN ){ + pcacheUnpin(p); }else{ - int nCopy = (int)(pFile->mmapSize - offset); - memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy); - pBuf = &((u8 *)pBuf)[nCopy]; - amt -= nCopy; - offset += nCopy; + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); } } -#endif - -#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) - rc = winSeekFile(pFile, offset); - if( rc==0 ){ -#else - { -#endif -#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) - OVERLAPPED overlapped; /* The offset for WriteFile. */ -#endif - u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ - int nRem = amt; /* Number of bytes yet to be written */ - DWORD nWrite; /* Bytes written by each WriteFile() call */ - DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */ +} -#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) - memset(&overlapped, 0, sizeof(OVERLAPPED)); - overlapped.Offset = (LONG)(offset & 0xffffffff); - overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); -#endif +/* +** Increase the reference count of a supplied page by 1. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheRef(PgHdr *p){ + assert(p->nRef>0); + assert( sqlcipher_sqlite3PcachePageSanity(p) ); + p->nRef++; + p->pCache->nRefSum++; +} - while( nRem>0 ){ -#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) - if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ -#else - if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){ -#endif - if( winRetryIoerr(&nRetry, &lastErrno) ) continue; - break; - } - assert( nWrite==0 || nWrite<=(DWORD)nRem ); - if( nWrite==0 || nWrite>(DWORD)nRem ){ - lastErrno = osGetLastError(); - break; - } -#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) - offset += nWrite; - overlapped.Offset = (LONG)(offset & 0xffffffff); - overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); -#endif - aRem += nWrite; - nRem -= nWrite; - } - if( nRem>0 ){ - pFile->lastErrno = lastErrno; - rc = 1; - } +/* +** Drop a page from the cache. There must be exactly one reference to the +** page. This function deletes that reference, so after it returns the +** page pointed to by p is invalid. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheDrop(PgHdr *p){ + assert( p->nRef==1 ); + assert( sqlcipher_sqlite3PcachePageSanity(p) ); + if( p->flags&PGHDR_DIRTY ){ + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); } + p->pCache->nRefSum--; + sqlcipher_sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 1); +} - if( rc ){ - if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ) - || ( pFile->lastErrno==ERROR_DISK_FULL )){ - OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return winLogError(SQLITE_FULL, pFile->lastErrno, - "winWrite1", pFile->zPath); +/* +** Make sure the page is marked as dirty. If it isn't dirty already, +** make it so. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheMakeDirty(PgHdr *p){ + assert( p->nRef>0 ); + assert( sqlcipher_sqlite3PcachePageSanity(p) ); + if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){ /*OPTIMIZATION-IF-FALSE*/ + p->flags &= ~PGHDR_DONT_WRITE; + if( p->flags & PGHDR_CLEAN ){ + p->flags ^= (PGHDR_DIRTY|PGHDR_CLEAN); + pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno)); + assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY ); + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD); } - OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_WRITE\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno, - "winWrite2", pFile->zPath); - }else{ - winLogIoerr(nRetry, __LINE__); + assert( sqlcipher_sqlite3PcachePageSanity(p) ); } - OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return SQLITE_OK; } /* -** Truncate an open file to a specified size +** Make sure the page is marked as clean. If it isn't clean already, +** make it so. */ -static int winTruncate(sqlcipher_sqlite3_file *id, sqlcipher_sqlite3_int64 nByte){ - winFile *pFile = (winFile*)id; /* File handle object */ - int rc = SQLITE_OK; /* Return code for this function */ - DWORD lastErrno; -#if SQLITE_MAX_MMAP_SIZE>0 - sqlcipher_sqlite3_int64 oldMmapSize; - if( pFile->nFetchOut>0 ){ - /* File truncation is a no-op if there are outstanding memory mapped - ** pages. This is because truncating the file means temporarily unmapping - ** the file, and that might delete memory out from under existing cursors. - ** - ** This can result in incremental vacuum not truncating the file, - ** if there is an active read cursor when the incremental vacuum occurs. - ** No real harm comes of this - the database file is not corrupted, - ** though some folks might complain that the file is bigger than it - ** needs to be. - ** - ** The only feasible work-around is to defer the truncation until after - ** all references to memory-mapped content are closed. That is doable, - ** but involves adding a few branches in the common write code path which - ** could slow down normal operations slightly. Hence, we have decided for - ** now to simply make trancations a no-op if there are pending reads. We - ** can maybe revisit this decision in the future. - */ - return SQLITE_OK; - } -#endif - - assert( pFile ); - SimulateIOError(return SQLITE_IOERR_TRUNCATE); - OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n", - osGetCurrentProcessId(), pFile, pFile->h, nByte, pFile->locktype)); - - /* If the user has configured a chunk-size for this file, truncate the - ** file so that it consists of an integer number of chunks (i.e. the - ** actual file size after the operation may be larger than the requested - ** size). - */ - if( pFile->szChunk>0 ){ - nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; - } - -#if SQLITE_MAX_MMAP_SIZE>0 - if( pFile->pMapRegion ){ - oldMmapSize = pFile->mmapSize; - }else{ - oldMmapSize = 0; - } - winUnmapfile(pFile); -#endif - - /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ - if( winSeekFile(pFile, nByte) ){ - rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, - "winTruncate1", pFile->zPath); - }else if( 0==osSetEndOfFile(pFile->h) && - ((lastErrno = osGetLastError())!=ERROR_USER_MAPPED_FILE) ){ - pFile->lastErrno = lastErrno; - rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, - "winTruncate2", pFile->zPath); - } - -#if SQLITE_MAX_MMAP_SIZE>0 - if( rc==SQLITE_OK && oldMmapSize>0 ){ - if( oldMmapSize>nByte ){ - winMapfile(pFile, -1); - }else{ - winMapfile(pFile, oldMmapSize); - } +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheMakeClean(PgHdr *p){ + assert( sqlcipher_sqlite3PcachePageSanity(p) ); + assert( (p->flags & PGHDR_DIRTY)!=0 ); + assert( (p->flags & PGHDR_CLEAN)==0 ); + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); + p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE); + p->flags |= PGHDR_CLEAN; + pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno)); + assert( sqlcipher_sqlite3PcachePageSanity(p) ); + if( p->nRef==0 ){ + pcacheUnpin(p); } -#endif - - OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, rc=%s\n", - osGetCurrentProcessId(), pFile, pFile->h, sqlcipher_sqlite3ErrName(rc))); - return rc; } -#ifdef SQLITE_TEST /* -** Count the number of fullsyncs and normal syncs. This is used to test -** that syncs and fullsyncs are occuring at the right times. +** Make every page in the cache clean. */ -SQLITE_API int sqlcipher_sqlite3_sync_count = 0; -SQLITE_API int sqlcipher_sqlite3_fullsync_count = 0; -#endif +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheCleanAll(PCache *pCache){ + PgHdr *p; + pcacheTrace(("%p.CLEAN-ALL\n",pCache)); + while( (p = pCache->pDirty)!=0 ){ + sqlcipher_sqlite3PcacheMakeClean(p); + } +} /* -** Make sure all writes to a particular file are committed to disk. +** Clear the PGHDR_NEED_SYNC and PGHDR_WRITEABLE flag from all dirty pages. */ -static int winSync(sqlcipher_sqlite3_file *id, int flags){ -#ifndef SQLITE_NO_SYNC - /* - ** Used only when SQLITE_NO_SYNC is not defined. - */ - BOOL rc; -#endif -#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \ - defined(SQLITE_HAVE_OS_TRACE) - /* - ** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or - ** OSTRACE() macros. - */ - winFile *pFile = (winFile*)id; -#else - UNUSED_PARAMETER(id); -#endif - - assert( pFile ); - /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */ - assert((flags&0x0F)==SQLITE_SYNC_NORMAL - || (flags&0x0F)==SQLITE_SYNC_FULL - ); - - /* Unix cannot, but some systems may return SQLITE_FULL from here. This - ** line is to test that doing so does not cause any problems. - */ - SimulateDiskfullError( return SQLITE_FULL ); +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheClearWritable(PCache *pCache){ + PgHdr *p; + pcacheTrace(("%p.CLEAR-WRITEABLE\n",pCache)); + for(p=pCache->pDirty; p; p=p->pDirtyNext){ + p->flags &= ~(PGHDR_NEED_SYNC|PGHDR_WRITEABLE); + } + pCache->pSynced = pCache->pDirtyTail; +} - OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, flags=%x, lock=%d\n", - osGetCurrentProcessId(), pFile, pFile->h, flags, - pFile->locktype)); +/* +** Clear the PGHDR_NEED_SYNC flag from all dirty pages. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheClearSyncFlags(PCache *pCache){ + PgHdr *p; + for(p=pCache->pDirty; p; p=p->pDirtyNext){ + p->flags &= ~PGHDR_NEED_SYNC; + } + pCache->pSynced = pCache->pDirtyTail; +} -#ifndef SQLITE_TEST - UNUSED_PARAMETER(flags); -#else - if( (flags&0x0F)==SQLITE_SYNC_FULL ){ - sqlcipher_sqlite3_fullsync_count++; +/* +** Change the page number of page p to newPgno. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheMove(PgHdr *p, Pgno newPgno){ + PCache *pCache = p->pCache; + sqlcipher_sqlite3_pcache_page *pOther; + assert( p->nRef>0 ); + assert( newPgno>0 ); + assert( sqlcipher_sqlite3PcachePageSanity(p) ); + pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno)); + pOther = sqlcipher_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, newPgno, 0); + if( pOther ){ + PgHdr *pXPage = (PgHdr*)pOther->pExtra; + assert( pXPage->nRef==0 ); + pXPage->nRef++; + pCache->nRefSum++; + sqlcipher_sqlite3PcacheDrop(pXPage); } - sqlcipher_sqlite3_sync_count++; -#endif + sqlcipher_sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); + p->pgno = newPgno; + if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); + assert( sqlcipher_sqlite3PcachePageSanity(p) ); + } +} - /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a - ** no-op - */ -#ifdef SQLITE_NO_SYNC - OSTRACE(("SYNC-NOP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return SQLITE_OK; -#else -#if SQLITE_MAX_MMAP_SIZE>0 - if( pFile->pMapRegion ){ - if( osFlushViewOfFile(pFile->pMapRegion, 0) ){ - OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, " - "rc=SQLITE_OK\n", osGetCurrentProcessId(), - pFile, pFile->pMapRegion)); - }else{ - pFile->lastErrno = osGetLastError(); - OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, " - "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), - pFile, pFile->pMapRegion)); - return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, - "winSync1", pFile->zPath); +/* +** Drop every cache entry whose page number is greater than "pgno". The +** caller must ensure that there are no outstanding references to any pages +** other than page 1 with a page number greater than pgno. +** +** If there is a reference to page 1 and the pgno parameter passed to this +** function is 0, then the data area associated with page 1 is zeroed, but +** the page object is not dropped. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ + if( pCache->pCache ){ + PgHdr *p; + PgHdr *pNext; + pcacheTrace(("%p.TRUNCATE %d\n",pCache,pgno)); + for(p=pCache->pDirty; p; p=pNext){ + pNext = p->pDirtyNext; + /* This routine never gets call with a positive pgno except right + ** after sqlcipher_sqlite3PcacheCleanAll(). So if there are dirty pages, + ** it must be that pgno==0. + */ + assert( p->pgno>0 ); + if( p->pgno>pgno ){ + assert( p->flags&PGHDR_DIRTY ); + sqlcipher_sqlite3PcacheMakeClean(p); + } } + if( pgno==0 && pCache->nRefSum ){ + sqlcipher_sqlite3_pcache_page *pPage1; + pPage1 = sqlcipher_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache,1,0); + if( ALWAYS(pPage1) ){ /* Page 1 is always available in cache, because + ** pCache->nRefSum>0 */ + memset(pPage1->pBuf, 0, pCache->szPage); + pgno = 1; + } + } + sqlcipher_sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1); } -#endif - rc = osFlushFileBuffers(pFile->h); - SimulateIOError( rc=FALSE ); - if( rc ){ - OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return SQLITE_OK; - }else{ - pFile->lastErrno = osGetLastError(); - OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_FSYNC\n", - osGetCurrentProcessId(), pFile, pFile->h)); - return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno, - "winSync2", pFile->zPath); - } -#endif } /* -** Determine the current size of a file in bytes +** Close a cache. */ -static int winFileSize(sqlcipher_sqlite3_file *id, sqlcipher_sqlite3_int64 *pSize){ - winFile *pFile = (winFile*)id; - int rc = SQLITE_OK; +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheClose(PCache *pCache){ + assert( pCache->pCache!=0 ); + pcacheTrace(("%p.CLOSE\n",pCache)); + sqlcipher_sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); +} - assert( id!=0 ); - assert( pSize!=0 ); - SimulateIOError(return SQLITE_IOERR_FSTAT); - OSTRACE(("SIZE file=%p, pSize=%p\n", pFile->h, pSize)); +/* +** Discard the contents of the cache. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheClear(PCache *pCache){ + sqlcipher_sqlite3PcacheTruncate(pCache, 0); +} -#if SQLITE_OS_WINRT - { - FILE_STANDARD_INFO info; - if( osGetFileInformationByHandleEx(pFile->h, FileStandardInfo, - &info, sizeof(info)) ){ - *pSize = info.EndOfFile.QuadPart; +/* +** Merge two lists of pages connected by pDirty and in pgno order. +** Do not bother fixing the pDirtyPrev pointers. +*/ +static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){ + PgHdr result, *pTail; + pTail = &result; + assert( pA!=0 && pB!=0 ); + for(;;){ + if( pA->pgnopgno ){ + pTail->pDirty = pA; + pTail = pA; + pA = pA->pDirty; + if( pA==0 ){ + pTail->pDirty = pB; + break; + } }else{ - pFile->lastErrno = osGetLastError(); - rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno, - "winFileSize", pFile->zPath); + pTail->pDirty = pB; + pTail = pB; + pB = pB->pDirty; + if( pB==0 ){ + pTail->pDirty = pA; + break; + } } } -#else - { - DWORD upperBits; - DWORD lowerBits; - DWORD lastErrno; + return result.pDirty; +} - lowerBits = osGetFileSize(pFile->h, &upperBits); - *pSize = (((sqlcipher_sqlite3_int64)upperBits)<<32) + lowerBits; - if( (lowerBits == INVALID_FILE_SIZE) - && ((lastErrno = osGetLastError())!=NO_ERROR) ){ - pFile->lastErrno = lastErrno; - rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno, - "winFileSize", pFile->zPath); +/* +** Sort the list of pages in accending order by pgno. Pages are +** connected by pDirty pointers. The pDirtyPrev pointers are +** corrupted by this sort. +** +** Since there cannot be more than 2^31 distinct pages in a database, +** there cannot be more than 31 buckets required by the merge sorter. +** One extra bucket is added to catch overflow in case something +** ever changes to make the previous sentence incorrect. +*/ +#define N_SORT_BUCKET 32 +static PgHdr *pcacheSortDirtyList(PgHdr *pIn){ + PgHdr *a[N_SORT_BUCKET], *p; + int i; + memset(a, 0, sizeof(a)); + while( pIn ){ + p = pIn; + pIn = p->pDirty; + p->pDirty = 0; + for(i=0; ALWAYS(ih, pSize, *pSize, sqlcipher_sqlite3ErrName(rc))); - return rc; + p = a[0]; + for(i=1; ipDirty; p; p=p->pDirtyNext){ + p->pDirty = p->pDirtyNext; + } + return pcacheSortDirtyList(pCache->pDirty); +} -#ifndef LOCKFILE_EXCLUSIVE_LOCK -# define LOCKFILE_EXCLUSIVE_LOCK 2 -#endif +/* +** Return the total number of references to all pages held by the cache. +** +** This is not the total number of pages referenced, but the sum of the +** reference count for all pages. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PcacheRefCount(PCache *pCache){ + return pCache->nRefSum; +} /* -** Historically, SQLite has used both the LockFile and LockFileEx functions. -** When the LockFile function was used, it was always expected to fail -** immediately if the lock could not be obtained. Also, it always expected to -** obtain an exclusive lock. These flags are used with the LockFileEx function -** and reflect those expectations; therefore, they should not be changed. +** Return the number of references to the page supplied as an argument. */ -#ifndef SQLITE_LOCKFILE_FLAGS -# define SQLITE_LOCKFILE_FLAGS (LOCKFILE_FAIL_IMMEDIATELY | \ - LOCKFILE_EXCLUSIVE_LOCK) -#endif +SQLITE_PRIVATE int sqlcipher_sqlite3PcachePageRefcount(PgHdr *p){ + return p->nRef; +} /* -** Currently, SQLite never calls the LockFileEx function without wanting the -** call to fail immediately if the lock cannot be obtained. +** Return the total number of pages in the cache. */ -#ifndef SQLITE_LOCKFILEEX_FLAGS -# define SQLITE_LOCKFILEEX_FLAGS (LOCKFILE_FAIL_IMMEDIATELY) +SQLITE_PRIVATE int sqlcipher_sqlite3PcachePagecount(PCache *pCache){ + assert( pCache->pCache!=0 ); + return sqlcipher_sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache); +} + +#ifdef SQLITE_TEST +/* +** Get the suggested cache-size value. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PcacheGetCachesize(PCache *pCache){ + return numberOfCachePages(pCache); +} #endif /* -** Acquire a reader lock. -** Different API routines are called depending on whether or not this -** is Win9x or WinNT. +** Set the suggested cache-size value. */ -static int winGetReadLock(winFile *pFile){ +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){ + assert( pCache->pCache!=0 ); + pCache->szCache = mxPage; + sqlcipher_sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache, + numberOfCachePages(pCache)); +} + +/* +** Set the suggested cache-spill value. Make no changes if if the +** argument is zero. Return the effective cache-spill size, which will +** be the larger of the szSpill and szCache. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PcacheSetSpillsize(PCache *p, int mxPage){ int res; - OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); - if( osIsNT() ){ -#if SQLITE_OS_WINCE - /* - ** NOTE: Windows CE is handled differently here due its lack of the Win32 - ** API LockFileEx. - */ - res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0); -#else - res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0, - SHARED_SIZE, 0); -#endif - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - int lk; - sqlcipher_sqlite3_randomness(sizeof(lk), &lk); - pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); - res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, - SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); - } -#endif - if( res == 0 ){ - pFile->lastErrno = osGetLastError(); - /* No need to log a failure to lock */ + assert( p->pCache!=0 ); + if( mxPage ){ + if( mxPage<0 ){ + mxPage = (int)((-1024*(i64)mxPage)/(p->szPage+p->szExtra)); + } + p->szSpill = mxPage; } - OSTRACE(("READ-LOCK file=%p, result=%d\n", pFile->h, res)); + res = numberOfCachePages(p); + if( resszSpill ) res = p->szSpill; return res; } /* -** Undo a readlock +** Free up as much memory as possible from the page cache. */ -static int winUnlockReadLock(winFile *pFile){ - int res; - DWORD lastErrno; - OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); - if( osIsNT() ){ - res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - res = winUnlockFile(&pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); - } +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheShrink(PCache *pCache){ + assert( pCache->pCache!=0 ); + sqlcipher_sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache); +} + +/* +** Return the size of the header added by this middleware layer +** in the page-cache hierarchy. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); } + +/* +** Return the number of dirty pages currently in the cache, as a percentage +** of the configured cache size. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PCachePercentDirty(PCache *pCache){ + PgHdr *pDirty; + int nDirty = 0; + int nCache = numberOfCachePages(pCache); + for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext) nDirty++; + return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0; +} + +#ifdef SQLITE_DIRECT_OVERFLOW_READ +/* +** Return true if there are one or more dirty pages in the cache. Else false. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PCacheIsDirty(PCache *pCache){ + return (pCache->pDirty!=0); +} #endif - if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){ - pFile->lastErrno = lastErrno; - winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno, - "winUnlockReadLock", pFile->zPath); + +#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) +/* +** For all dirty pages currently in the cache, invoke the specified +** callback. This is only used if the SQLITE_CHECK_PAGES macro is +** defined. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)){ + PgHdr *pDirty; + for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext){ + xIter(pDirty); } - OSTRACE(("READ-UNLOCK file=%p, result=%d\n", pFile->h, res)); - return res; } +#endif +/************** End of pcache.c **********************************************/ +/************** Begin file pcache1.c *****************************************/ /* -** Lock the file with the lock specified by parameter locktype - one -** of the following: +** 2008 November 05 ** -** (1) SHARED_LOCK -** (2) RESERVED_LOCK -** (3) PENDING_LOCK -** (4) EXCLUSIVE_LOCK +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** Sometimes when requesting one lock state, additional lock states -** are inserted in between. The locking might fail on one of the later -** transitions leaving the lock state different from what it started but -** still short of its goal. The following chart shows the allowed -** transitions and the inserted intermediate states: +** 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. ** -** UNLOCKED -> SHARED -** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE -** RESERVED -> (PENDING) -> EXCLUSIVE -** PENDING -> EXCLUSIVE +************************************************************************* ** -** This routine will only increase a lock. The winUnlock() routine -** erases all locks at once and returns us immediately to locking level 0. -** It is not possible to lower the locking level one step at a time. You -** must go straight to locking level 0. +** This file implements the default page cache implementation (the +** sqlcipher_sqlite3_pcache interface). It also contains part of the implementation +** of the SQLITE_CONFIG_PAGECACHE and sqlcipher_sqlite3_release_memory() features. +** If the default page cache implementation is overridden, then neither of +** these two features are available. +** +** A Page cache line looks like this: +** +** ------------------------------------------------------------- +** | database page content | PgHdr1 | MemPage | PgHdr | +** ------------------------------------------------------------- +** +** The database page content is up front (so that buffer overreads tend to +** flow harmlessly into the PgHdr1, MemPage, and PgHdr extensions). MemPage +** is the extension added by the btree.c module containing information such +** as the database page number and how that database page is used. PgHdr +** is added by the pcache.c layer and contains information used to keep track +** of which pages are "dirty". PgHdr1 is an extension added by this +** module (pcache1.c). The PgHdr1 header is a subclass of sqlcipher_sqlite3_pcache_page. +** PgHdr1 contains information needed to look up a page by its page number. +** The superclass sqlcipher_sqlite3_pcache_page.pBuf points to the start of the +** database page content and sqlcipher_sqlite3_pcache_page.pExtra points to PgHdr. +** +** The size of the extension (MemPage+PgHdr+PgHdr1) can be determined at +** runtime using sqlcipher_sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &size). The +** sizes of the extensions sum to 272 bytes on x64 for 3.8.10, but this +** size can vary according to architecture, compile-time options, and +** SQLite library version number. +** +** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained +** using a separate memory allocation from the database page content. This +** seeks to overcome the "clownshoe" problem (also called "internal +** fragmentation" in academic literature) of allocating a few bytes more +** than a power of two with the memory allocator rounding up to the next +** power of two, and leaving the rounded-up space unused. +** +** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates +** with this module. Information is passed back and forth as PgHdr1 pointers. +** +** The pcache.c and pager.c modules deal pointers to PgHdr objects. +** The btree.c module deals with pointers to MemPage objects. +** +** SOURCE OF PAGE CACHE MEMORY: +** +** Memory for a page might come from any of three sources: +** +** (1) The general-purpose memory allocator - sqlcipher_sqlite3Malloc() +** (2) Global page-cache memory provided using sqlcipher_sqlite3_config() with +** SQLITE_CONFIG_PAGECACHE. +** (3) PCache-local bulk allocation. +** +** The third case is a chunk of heap memory (defaulting to 100 pages worth) +** that is allocated when the page cache is created. The size of the local +** bulk allocation can be adjusted using +** +** sqlcipher_sqlite3_config(SQLITE_CONFIG_PAGECACHE, (void*)0, 0, N). +** +** If N is positive, then N pages worth of memory are allocated using a single +** sqlcipher_sqlite3Malloc() call and that memory is used for the first N pages allocated. +** Or if N is negative, then -1024*N bytes of memory are allocated and used +** for as many pages as can be accomodated. +** +** Only one of (2) or (3) can be used. Once the memory available to (2) or +** (3) is exhausted, subsequent allocations fail over to the general-purpose +** memory allocator (1). +** +** Earlier versions of SQLite used only methods (1) and (2). But experiments +** show that method (3) with N==100 provides about a 5% performance boost for +** common workloads. */ -static int winLock(sqlcipher_sqlite3_file *id, int locktype){ - int rc = SQLITE_OK; /* Return code from subroutines */ - int res = 1; /* Result of a Windows lock call */ - int newLocktype; /* Set pFile->locktype to this value before exiting */ - int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ - winFile *pFile = (winFile*)id; - DWORD lastErrno = NO_ERROR; - - assert( id!=0 ); - OSTRACE(("LOCK file=%p, oldLock=%d(%d), newLock=%d\n", - pFile->h, pFile->locktype, pFile->sharedLockByte, locktype)); +/* #include "sqliteInt.h" */ - /* If there is already a lock of this type or more restrictive on the - ** OsFile, do nothing. Don't use the end_lock: exit path, as - ** sqlcipher_sqlite3OsEnterMutex() hasn't been called yet. - */ - if( pFile->locktype>=locktype ){ - OSTRACE(("LOCK-HELD file=%p, rc=SQLITE_OK\n", pFile->h)); - return SQLITE_OK; - } +typedef struct PCache1 PCache1; +typedef struct PgHdr1 PgHdr1; +typedef struct PgFreeslot PgFreeslot; +typedef struct PGroup PGroup; - /* Do not allow any kind of write-lock on a read-only database - */ - if( (pFile->ctrlFlags & WINFILE_RDONLY)!=0 && locktype>=RESERVED_LOCK ){ - return SQLITE_IOERR_LOCK; - } +/* +** Each cache entry is represented by an instance of the following +** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of +** PgHdr1.pCache->szPage bytes is allocated directly before this structure +** in memory. +** +** Note: Variables isBulkLocal and isAnchor were once type "u8". That works, +** but causes a 2-byte gap in the structure for most architectures (since +** pointers must be either 4 or 8-byte aligned). As this structure is located +** in memory directly after the associated page data, if the database is +** corrupt, code at the b-tree layer may overread the page buffer and +** read part of this structure before the corruption is detected. This +** can cause a valgrind error if the unitialized gap is accessed. Using u16 +** ensures there is no such gap, and therefore no bytes of unitialized memory +** in the structure. +*/ +struct PgHdr1 { + sqlcipher_sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ + unsigned int iKey; /* Key value (page number) */ + u16 isBulkLocal; /* This page from bulk local storage */ + u16 isAnchor; /* This is the PGroup.lru element */ + PgHdr1 *pNext; /* Next in hash table chain */ + PCache1 *pCache; /* Cache that currently owns this page */ + PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ + PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ + /* NB: pLruPrev is only valid if pLruNext!=0 */ +}; - /* Make sure the locking sequence is correct - */ - assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); - assert( locktype!=PENDING_LOCK ); - assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); +/* +** A page is pinned if it is not on the LRU list. To be "pinned" means +** that the page is in active use and must not be deallocated. +*/ +#define PAGE_IS_PINNED(p) ((p)->pLruNext==0) +#define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0) - /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or - ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of - ** the PENDING_LOCK byte is temporary. - */ - newLocktype = pFile->locktype; - if( pFile->locktype==NO_LOCK - || (locktype==EXCLUSIVE_LOCK && pFile->locktype<=RESERVED_LOCK) - ){ - int cnt = 3; - while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, - PENDING_BYTE, 0, 1, 0))==0 ){ - /* Try 3 times to get the pending lock. This is needed to work - ** around problems caused by indexing and/or anti-virus software on - ** Windows systems. - ** If you are using this code as a model for alternative VFSes, do not - ** copy this retry logic. It is a hack intended for Windows only. - */ - lastErrno = osGetLastError(); - OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n", - pFile->h, cnt, res)); - if( lastErrno==ERROR_INVALID_HANDLE ){ - pFile->lastErrno = lastErrno; - rc = SQLITE_IOERR_LOCK; - OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n", - pFile->h, cnt, sqlcipher_sqlite3ErrName(rc))); - return rc; - } - if( cnt ) sqlcipher_sqlite3_win32_sleep(1); - } - gotPendingLock = res; - if( !res ){ - lastErrno = osGetLastError(); - } - } +/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set +** of one or more PCaches that are able to recycle each other's unpinned +** pages when they are under memory pressure. A PGroup is an instance of +** the following object. +** +** This page cache implementation works in one of two modes: +** +** (1) Every PCache is the sole member of its own PGroup. There is +** one PGroup per PCache. +** +** (2) There is a single global PGroup that all PCaches are a member +** of. +** +** Mode 1 uses more memory (since PCache instances are not able to rob +** unused pages from other PCaches) but it also operates without a mutex, +** and is therefore often faster. Mode 2 requires a mutex in order to be +** threadsafe, but recycles pages more efficiently. +** +** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single +** PGroup which is the pcache1.grp global variable and its mutex is +** SQLITE_MUTEX_STATIC_LRU. +*/ +struct PGroup { + sqlcipher_sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */ + unsigned int nMaxPage; /* Sum of nMax for purgeable caches */ + unsigned int nMinPage; /* Sum of nMin for purgeable caches */ + unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */ + unsigned int nPurgeable; /* Number of purgeable pages allocated */ + PgHdr1 lru; /* The beginning and end of the LRU list */ +}; - /* Acquire a shared lock +/* Each page cache is an instance of the following object. Every +** open database file (including each in-memory database and each +** temporary or transient database) has a single page cache which +** is an instance of this object. +** +** Pointers to structures of this type are cast and returned as +** opaque sqlcipher_sqlite3_pcache* handles. +*/ +struct PCache1 { + /* Cache configuration parameters. Page size (szPage) and the purgeable + ** flag (bPurgeable) and the pnPurgeable pointer are all set when the + ** cache is created and are never changed thereafter. nMax may be + ** modified at any time by a call to the pcache1Cachesize() method. + ** The PGroup mutex must be held when accessing nMax. */ - if( locktype==SHARED_LOCK && res ){ - assert( pFile->locktype==NO_LOCK ); - res = winGetReadLock(pFile); - if( res ){ - newLocktype = SHARED_LOCK; - }else{ - lastErrno = osGetLastError(); - } - } + PGroup *pGroup; /* PGroup this cache belongs to */ + unsigned int *pnPurgeable; /* Pointer to pGroup->nPurgeable */ + int szPage; /* Size of database content section */ + int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */ + int szAlloc; /* Total size of one pcache line */ + int bPurgeable; /* True if cache is purgeable */ + unsigned int nMin; /* Minimum number of pages reserved */ + unsigned int nMax; /* Configured "cache_size" value */ + unsigned int n90pct; /* nMax*9/10 */ + unsigned int iMaxKey; /* Largest key seen since xTruncate() */ + unsigned int nPurgeableDummy; /* pnPurgeable points here when not used*/ - /* Acquire a RESERVED lock + /* Hash table of all pages. The following variables may only be accessed + ** when the accessor is holding the PGroup mutex. */ - if( locktype==RESERVED_LOCK && res ){ - assert( pFile->locktype==SHARED_LOCK ); - res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0); - if( res ){ - newLocktype = RESERVED_LOCK; - }else{ - lastErrno = osGetLastError(); - } - } + unsigned int nRecyclable; /* Number of pages in the LRU list */ + unsigned int nPage; /* Total number of pages in apHash */ + unsigned int nHash; /* Number of slots in apHash[] */ + PgHdr1 **apHash; /* Hash table for fast lookup by key */ + PgHdr1 *pFree; /* List of unused pcache-local pages */ + void *pBulk; /* Bulk memory used by pcache-local */ +}; - /* Acquire a PENDING lock - */ - if( locktype==EXCLUSIVE_LOCK && res ){ - newLocktype = PENDING_LOCK; - gotPendingLock = 0; - } +/* +** Free slots in the allocator used to divide up the global page cache +** buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism. +*/ +struct PgFreeslot { + PgFreeslot *pNext; /* Next free slot */ +}; - /* Acquire an EXCLUSIVE lock - */ - if( locktype==EXCLUSIVE_LOCK && res ){ - assert( pFile->locktype>=SHARED_LOCK ); - res = winUnlockReadLock(pFile); - res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0, - SHARED_SIZE, 0); - if( res ){ - newLocktype = EXCLUSIVE_LOCK; - }else{ - lastErrno = osGetLastError(); - winGetReadLock(pFile); - } - } +/* +** Global data used by this cache. +*/ +static SQLITE_WSD struct PCacheGlobal { + PGroup grp; /* The global PGroup for mode (2) */ - /* If we are holding a PENDING lock that ought to be released, then - ** release it now. + /* Variables related to SQLITE_CONFIG_PAGECACHE settings. The + ** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all + ** fixed at sqlcipher_sqlite3_initialize() time and do not require mutex protection. + ** The nFreeSlot and pFree values do require mutex protection. */ - if( gotPendingLock && locktype==SHARED_LOCK ){ - winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0); - } + int isInit; /* True if initialized */ + int separateCache; /* Use a new PGroup for each PCache */ + int nInitPage; /* Initial bulk allocation size */ + int szSlot; /* Size of each free slot */ + int nSlot; /* The number of pcache slots */ + int nReserve; /* Try to keep nFreeSlot above this */ + void *pStart, *pEnd; /* Bounds of global page cache memory */ + /* Above requires no mutex. Use mutex below for variable that follow. */ + sqlcipher_sqlite3_mutex *mutex; /* Mutex for accessing the following: */ + PgFreeslot *pFree; /* Free page blocks */ + int nFreeSlot; /* Number of unused pcache slots */ + /* The following value requires a mutex to change. We skip the mutex on + ** reading because (1) most platforms read a 32-bit integer atomically and + ** (2) even if an incorrect value is read, no great harm is done since this + ** is really just an optimization. */ + int bUnderPressure; /* True if low on PAGECACHE memory */ +} pcache1_g; - /* Update the state of the lock has held in the file descriptor then - ** return the appropriate result code. - */ - if( res ){ - rc = SQLITE_OK; - }else{ - pFile->lastErrno = lastErrno; - rc = SQLITE_BUSY; - OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n", - pFile->h, locktype, newLocktype)); - } - pFile->locktype = (u8)newLocktype; - OSTRACE(("LOCK file=%p, lock=%d, rc=%s\n", - pFile->h, pFile->locktype, sqlcipher_sqlite3ErrName(rc))); - return rc; -} +/* +** All code in this file should access the global structure above via the +** alias "pcache1". This ensures that the WSD emulation is used when +** compiling for systems that do not support real WSD. +*/ +#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g)) /* -** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, return -** non-zero, otherwise zero. +** Macros to enter and leave the PCache LRU mutex. */ -static int winCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ - int res; - winFile *pFile = (winFile*)id; +#if !defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0 +# define pcache1EnterMutex(X) assert((X)->mutex==0) +# define pcache1LeaveMutex(X) assert((X)->mutex==0) +# define PCACHE1_MIGHT_USE_GROUP_MUTEX 0 +#else +# define pcache1EnterMutex(X) sqlcipher_sqlite3_mutex_enter((X)->mutex) +# define pcache1LeaveMutex(X) sqlcipher_sqlite3_mutex_leave((X)->mutex) +# define PCACHE1_MIGHT_USE_GROUP_MUTEX 1 +#endif - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p\n", pFile->h, pResOut)); +/******************************************************************************/ +/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/ - assert( id!=0 ); - if( pFile->locktype>=RESERVED_LOCK ){ - res = 1; - OSTRACE(("TEST-WR-LOCK file=%p, result=%d (local)\n", pFile->h, res)); - }else{ - res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE,0,1,0); - if( res ){ - winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0); - } - res = !res; - OSTRACE(("TEST-WR-LOCK file=%p, result=%d (remote)\n", pFile->h, res)); - } - *pResOut = res; - OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", - pFile->h, pResOut, *pResOut)); - return SQLITE_OK; -} /* -** Lower the locking level on file descriptor id to locktype. locktype -** must be either NO_LOCK or SHARED_LOCK. -** -** If the locking level of the file descriptor is already at or below -** the requested locking level, this routine is a no-op. +** This function is called during initialization if a static buffer is +** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE +** verb to sqlcipher_sqlite3_config(). Parameter pBuf points to an allocation large +** enough to contain 'n' buffers of 'sz' bytes each. ** -** It is not possible for this routine to fail if the second argument -** is NO_LOCK. If the second argument is SHARED_LOCK then this routine -** might return SQLITE_IOERR; +** This routine is called from sqlcipher_sqlite3_initialize() and so it is guaranteed +** to be serialized already. There is no need for further mutexing. */ -static int winUnlock(sqlcipher_sqlite3_file *id, int locktype){ - int type; - winFile *pFile = (winFile*)id; - int rc = SQLITE_OK; - assert( pFile!=0 ); - assert( locktype<=SHARED_LOCK ); - OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n", - pFile->h, pFile->locktype, pFile->sharedLockByte, locktype)); - type = pFile->locktype; - if( type>=EXCLUSIVE_LOCK ){ - winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){ - /* This should never happen. We should always be able to - ** reacquire the read lock */ - rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), - "winUnlock", pFile->zPath); +SQLITE_PRIVATE void sqlcipher_sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ + if( pcache1.isInit ){ + PgFreeslot *p; + if( pBuf==0 ) sz = n = 0; + if( n==0 ) sz = 0; + sz = ROUNDDOWN8(sz); + pcache1.szSlot = sz; + pcache1.nSlot = pcache1.nFreeSlot = n; + pcache1.nReserve = n>90 ? 10 : (n/10 + 1); + pcache1.pStart = pBuf; + pcache1.pFree = 0; + pcache1.bUnderPressure = 0; + while( n-- ){ + p = (PgFreeslot*)pBuf; + p->pNext = pcache1.pFree; + pcache1.pFree = p; + pBuf = (void*)&((char*)pBuf)[sz]; } + pcache1.pEnd = pBuf; } - if( type>=RESERVED_LOCK ){ - winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0); +} + +/* +** Try to initialize the pCache->pFree and pCache->pBulk fields. Return +** true if pCache->pFree ends up containing one or more free pages. +*/ +static int pcache1InitBulk(PCache1 *pCache){ + i64 szBulk; + char *zBulk; + if( pcache1.nInitPage==0 ) return 0; + /* Do not bother with a bulk allocation if the cache size very small */ + if( pCache->nMax<3 ) return 0; + sqlcipher_sqlite3BeginBenignMalloc(); + if( pcache1.nInitPage>0 ){ + szBulk = pCache->szAlloc * (i64)pcache1.nInitPage; + }else{ + szBulk = -1024 * (i64)pcache1.nInitPage; } - if( locktype==NO_LOCK && type>=SHARED_LOCK ){ - winUnlockReadLock(pFile); + if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){ + szBulk = pCache->szAlloc*(i64)pCache->nMax; } - if( type>=PENDING_LOCK ){ - winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0); + zBulk = pCache->pBulk = sqlcipher_sqlite3Malloc( szBulk ); + sqlcipher_sqlite3EndBenignMalloc(); + if( zBulk ){ + int nBulk = sqlcipher_sqlite3MallocSize(zBulk)/pCache->szAlloc; + do{ + PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage]; + pX->page.pBuf = zBulk; + pX->page.pExtra = &pX[1]; + pX->isBulkLocal = 1; + pX->isAnchor = 0; + pX->pNext = pCache->pFree; + pX->pLruPrev = 0; /* Initializing this saves a valgrind error */ + pCache->pFree = pX; + zBulk += pCache->szAlloc; + }while( --nBulk ); } - pFile->locktype = (u8)locktype; - OSTRACE(("UNLOCK file=%p, lock=%d, rc=%s\n", - pFile->h, pFile->locktype, sqlcipher_sqlite3ErrName(rc))); - return rc; + return pCache->pFree!=0; } -/****************************************************************************** -****************************** No-op Locking ********************************** -** -** Of the various locking implementations available, this is by far the -** simplest: locking is ignored. No attempt is made to lock the database -** file for reading or writing. +/* +** Malloc function used within this file to allocate space from the buffer +** configured using sqlcipher_sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no +** such buffer exists or there is no space left in it, this function falls +** back to sqlcipher_sqlite3Malloc(). ** -** This locking mode is appropriate for use on read-only databases -** (ex: databases that are burned into CD-ROM, for example.) It can -** also be used if the application employs some external mechanism to -** prevent simultaneous access of the same database by two or more -** database connections. But there is a serious risk of database -** corruption if this locking mode is used in situations where multiple -** database connections are accessing the same database file at the same -** time and one or more of those connections are writing. +** Multiple threads can run this routine at the same time. Global variables +** in pcache1 need to be protected via mutex. */ - -static int winNolockLock(sqlcipher_sqlite3_file *id, int locktype){ - UNUSED_PARAMETER(id); - UNUSED_PARAMETER(locktype); - return SQLITE_OK; -} - -static int winNolockCheckReservedLock(sqlcipher_sqlite3_file *id, int *pResOut){ - UNUSED_PARAMETER(id); - UNUSED_PARAMETER(pResOut); - return SQLITE_OK; +static void *pcache1Alloc(int nByte){ + void *p = 0; + assert( sqlcipher_sqlite3_mutex_notheld(pcache1.grp.mutex) ); + if( nByte<=pcache1.szSlot ){ + sqlcipher_sqlite3_mutex_enter(pcache1.mutex); + p = (PgHdr1 *)pcache1.pFree; + if( p ){ + pcache1.pFree = pcache1.pFree->pNext; + pcache1.nFreeSlot--; + pcache1.bUnderPressure = pcache1.nFreeSlot=0 ); + sqlcipher_sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); + sqlcipher_sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); + } + sqlcipher_sqlite3_mutex_leave(pcache1.mutex); + } + if( p==0 ){ + /* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool. Get + ** it from sqlcipher_sqlite3Malloc instead. + */ + p = sqlcipher_sqlite3Malloc(nByte); +#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS + if( p ){ + int sz = sqlcipher_sqlite3MallocSize(p); + sqlcipher_sqlite3_mutex_enter(pcache1.mutex); + sqlcipher_sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); + sqlcipher_sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); + sqlcipher_sqlite3_mutex_leave(pcache1.mutex); + } +#endif + sqlcipher_sqlite3MemdebugSetType(p, MEMTYPE_PCACHE); + } + return p; } -static int winNolockUnlock(sqlcipher_sqlite3_file *id, int locktype){ - UNUSED_PARAMETER(id); - UNUSED_PARAMETER(locktype); - return SQLITE_OK; +/* +** Free an allocated buffer obtained from pcache1Alloc(). +*/ +static void pcache1Free(void *p){ + if( p==0 ) return; + if( SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd) ){ + PgFreeslot *pSlot; + sqlcipher_sqlite3_mutex_enter(pcache1.mutex); + sqlcipher_sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); + pSlot = (PgFreeslot*)p; + pSlot->pNext = pcache1.pFree; + pcache1.pFree = pSlot; + pcache1.nFreeSlot++; + pcache1.bUnderPressure = pcache1.nFreeSlotctrlFlags is set. -** -** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags. +** Return the size of a pcache allocation */ -static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){ - if( *pArg<0 ){ - *pArg = (pFile->ctrlFlags & mask)!=0; - }else if( (*pArg)==0 ){ - pFile->ctrlFlags &= ~mask; +static int pcache1MemSize(void *p){ + if( p>=pcache1.pStart && pctrlFlags |= mask; + int iSize; + assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); + sqlcipher_sqlite3MemdebugSetType(p, MEMTYPE_HEAP); + iSize = sqlcipher_sqlite3MallocSize(p); + sqlcipher_sqlite3MemdebugSetType(p, MEMTYPE_PCACHE); + return iSize; } } - -/* Forward references to VFS helper methods used for temporary files */ -static int winGetTempname(sqlcipher_sqlite3_vfs *, char **); -static int winIsDir(const void *); -static BOOL winIsLongPathPrefix(const char *); -static BOOL winIsDriveLetterAndColon(const char *); +#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */ /* -** Control and query of the open file handle. +** Allocate a new page object initially associated with cache pCache. */ -static int winFileControl(sqlcipher_sqlite3_file *id, int op, void *pArg){ - winFile *pFile = (winFile*)id; - OSTRACE(("FCNTL file=%p, op=%d, pArg=%p\n", pFile->h, op, pArg)); - switch( op ){ - case SQLITE_FCNTL_LOCKSTATE: { - *(int*)pArg = pFile->locktype; - OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); - return SQLITE_OK; - } - case SQLITE_FCNTL_LAST_ERRNO: { - *(int*)pArg = (int)pFile->lastErrno; - OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); - return SQLITE_OK; - } - case SQLITE_FCNTL_CHUNK_SIZE: { - pFile->szChunk = *(int *)pArg; - OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); - return SQLITE_OK; - } - case SQLITE_FCNTL_SIZE_HINT: { - if( pFile->szChunk>0 ){ - sqlcipher_sqlite3_int64 oldSz; - int rc = winFileSize(id, &oldSz); - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3_int64 newSz = *(sqlcipher_sqlite3_int64*)pArg; - if( newSz>oldSz ){ - SimulateIOErrorBenign(1); - rc = winTruncate(id, newSz); - SimulateIOErrorBenign(0); - } - } - OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlcipher_sqlite3ErrName(rc))); - return rc; - } - OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); - return SQLITE_OK; - } - case SQLITE_FCNTL_PERSIST_WAL: { - winModeBit(pFile, WINFILE_PERSIST_WAL, (int*)pArg); - OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); - return SQLITE_OK; - } - case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { - winModeBit(pFile, WINFILE_PSOW, (int*)pArg); - OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); - return SQLITE_OK; - } - case SQLITE_FCNTL_VFSNAME: { - *(char**)pArg = sqlcipher_sqlite3_mprintf("%s", pFile->pVfs->zName); - OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); - return SQLITE_OK; - } - case SQLITE_FCNTL_WIN32_AV_RETRY: { - int *a = (int*)pArg; - if( a[0]>0 ){ - winIoerrRetry = a[0]; - }else{ - a[0] = winIoerrRetry; - } - if( a[1]>0 ){ - winIoerrRetryDelay = a[1]; - }else{ - a[1] = winIoerrRetryDelay; - } - OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); - return SQLITE_OK; - } - case SQLITE_FCNTL_WIN32_GET_HANDLE: { - LPHANDLE phFile = (LPHANDLE)pArg; - *phFile = pFile->h; - OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); - return SQLITE_OK; - } -#ifdef SQLITE_TEST - case SQLITE_FCNTL_WIN32_SET_HANDLE: { - LPHANDLE phFile = (LPHANDLE)pArg; - HANDLE hOldFile = pFile->h; - pFile->h = *phFile; - *phFile = hOldFile; - OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n", - hOldFile, pFile->h)); - return SQLITE_OK; - } -#endif - case SQLITE_FCNTL_TEMPFILENAME: { - char *zTFile = 0; - int rc = winGetTempname(pFile->pVfs, &zTFile); - if( rc==SQLITE_OK ){ - *(char**)pArg = zTFile; - } - OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlcipher_sqlite3ErrName(rc))); - return rc; - } -#if SQLITE_MAX_MMAP_SIZE>0 - case SQLITE_FCNTL_MMAP_SIZE: { - i64 newLimit = *(i64*)pArg; - int rc = SQLITE_OK; - if( newLimit>sqlcipher_sqlite3GlobalConfig.mxMmap ){ - newLimit = sqlcipher_sqlite3GlobalConfig.mxMmap; - } - - /* The value of newLimit may be eventually cast to (SIZE_T) and passed - ** to MapViewOfFile(). Restrict its value to 2GB if (SIZE_T) is not at - ** least a 64-bit type. */ - if( newLimit>0 && sizeof(SIZE_T)<8 ){ - newLimit = (newLimit & 0x7FFFFFFF); - } +static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){ + PgHdr1 *p = 0; + void *pPg; - *(i64*)pArg = pFile->mmapSizeMax; - if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ - pFile->mmapSizeMax = newLimit; - if( pFile->mmapSize>0 ){ - winUnmapfile(pFile); - rc = winMapfile(pFile, -1); - } - } - OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlcipher_sqlite3ErrName(rc))); - return rc; + assert( sqlcipher_sqlite3_mutex_held(pCache->pGroup->mutex) ); + if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){ + assert( pCache->pFree!=0 ); + p = pCache->pFree; + pCache->pFree = p->pNext; + p->pNext = 0; + }else{ +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + /* The group mutex must be released before pcache1Alloc() is called. This + ** is because it might call sqlcipher_sqlite3_release_memory(), which assumes that + ** this mutex is not held. */ + assert( pcache1.separateCache==0 ); + assert( pCache->pGroup==&pcache1.grp ); + pcache1LeaveMutex(pCache->pGroup); +#endif + if( benignMalloc ){ sqlcipher_sqlite3BeginBenignMalloc(); } +#ifdef SQLITE_PCACHE_SEPARATE_HEADER + pPg = pcache1Alloc(pCache->szPage); + p = sqlcipher_sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); + if( !pPg || !p ){ + pcache1Free(pPg); + sqlcipher_sqlite3_free(p); + pPg = 0; } +#else + pPg = pcache1Alloc(pCache->szAlloc); +#endif + if( benignMalloc ){ sqlcipher_sqlite3EndBenignMalloc(); } +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + pcache1EnterMutex(pCache->pGroup); #endif + if( pPg==0 ) return 0; +#ifndef SQLITE_PCACHE_SEPARATE_HEADER + p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; +#endif + p->page.pBuf = pPg; + p->page.pExtra = &p[1]; + p->isBulkLocal = 0; + p->isAnchor = 0; + p->pLruPrev = 0; /* Initializing this saves a valgrind error */ } - OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h)); - return SQLITE_NOTFOUND; + (*pCache->pnPurgeable)++; + return p; } /* -** Return the sector size in bytes of the underlying block device for -** the specified file. This is almost always 512 bytes, but may be -** larger for some devices. -** -** SQLite code assumes this function cannot fail. It also assumes that -** if two files are created in the same file-system directory (i.e. -** a database and its journal file) that the sector size will be the -** same for both. +** Free a page object allocated by pcache1AllocPage(). */ -static int winSectorSize(sqlcipher_sqlite3_file *id){ - (void)id; - return SQLITE_DEFAULT_SECTOR_SIZE; +static void pcache1FreePage(PgHdr1 *p){ + PCache1 *pCache; + assert( p!=0 ); + pCache = p->pCache; + assert( sqlcipher_sqlite3_mutex_held(p->pCache->pGroup->mutex) ); + if( p->isBulkLocal ){ + p->pNext = pCache->pFree; + pCache->pFree = p; + }else{ + pcache1Free(p->page.pBuf); +#ifdef SQLITE_PCACHE_SEPARATE_HEADER + sqlcipher_sqlite3_free(p); +#endif + } + (*pCache->pnPurgeable)--; } /* -** Return a vector of device characteristics. +** Malloc function used by SQLite to obtain space from the buffer configured +** using sqlcipher_sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer +** exists, this function falls back to sqlcipher_sqlite3Malloc(). */ -static int winDeviceCharacteristics(sqlcipher_sqlite3_file *id){ - winFile *p = (winFile*)id; - return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | - ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); +SQLITE_PRIVATE void *sqlcipher_sqlite3PageMalloc(int sz){ + assert( sz<=65536+8 ); /* These allocations are never very large */ + return pcache1Alloc(sz); } /* -** Windows will only let you create file view mappings -** on allocation size granularity boundaries. -** During sqlcipher_sqlite3_os_init() we do a GetSystemInfo() -** to get the granularity size. +** Free an allocated buffer obtained from sqlcipher_sqlite3PageMalloc(). */ -static SYSTEM_INFO winSysInfo; +SQLITE_PRIVATE void sqlcipher_sqlite3PageFree(void *p){ + pcache1Free(p); +} -#ifndef SQLITE_OMIT_WAL /* -** Helper functions to obtain and relinquish the global mutex. The -** global mutex is used to protect the winLockInfo objects used by -** this file, all of which may be shared by multiple threads. +** Return true if it desirable to avoid allocating a new page cache +** entry. ** -** Function winShmMutexHeld() is used to assert() that the global mutex -** is held when required. This function is only used as part of assert() -** statements. e.g. +** If memory was allocated specifically to the page cache using +** SQLITE_CONFIG_PAGECACHE but that memory has all been used, then +** it is desirable to avoid allocating a new page cache entry because +** presumably SQLITE_CONFIG_PAGECACHE was suppose to be sufficient +** for all page cache needs and we should not need to spill the +** allocation onto the heap. ** -** winShmEnterMutex() -** assert( winShmMutexHeld() ); -** winShmLeaveMutex() +** Or, the heap is used for all page cache memory but the heap is +** under memory pressure, then again it is desirable to avoid +** allocating a new page cache entry in order to avoid stressing +** the heap even further. */ -static sqlcipher_sqlite3_mutex *winBigLock = 0; -static void winShmEnterMutex(void){ - sqlcipher_sqlite3_mutex_enter(winBigLock); -} -static void winShmLeaveMutex(void){ - sqlcipher_sqlite3_mutex_leave(winBigLock); -} -#ifndef NDEBUG -static int winShmMutexHeld(void) { - return sqlcipher_sqlite3_mutex_held(winBigLock); +static int pcache1UnderMemoryPressure(PCache1 *pCache){ + if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ + return pcache1.bUnderPressure; + }else{ + return sqlcipher_sqlite3HeapNearlyFull(); + } } -#endif + +/******************************************************************************/ +/******** General Implementation Functions ************************************/ /* -** Object used to represent a single file opened and mmapped to provide -** shared memory. When multiple threads all reference the same -** log-summary, each thread has its own winFile object, but they all -** point to a single instance of this object. In other words, each -** log-summary is opened only once per process. -** -** winShmMutexHeld() must be true when creating or destroying -** this object or while reading or writing the following fields: -** -** nRef -** pNext -** -** The following fields are read-only after the object is created: -** -** fid -** zFilename -** -** Either winShmNode.mutex must be held or winShmNode.nRef==0 and -** winShmMutexHeld() is true when reading or writing any other field -** in this structure. +** This function is used to resize the hash table used by the cache passed +** as the first argument. ** +** The PCache mutex must be held when this function is called. */ -struct winShmNode { - sqlcipher_sqlite3_mutex *mutex; /* Mutex to access this object */ - char *zFilename; /* Name of the file */ - winFile hFile; /* File handle from winOpen */ +static void pcache1ResizeHash(PCache1 *p){ + PgHdr1 **apNew; + unsigned int nNew; + unsigned int i; - int szRegion; /* Size of shared-memory regions */ - int nRegion; /* Size of array apRegion */ - u8 isReadonly; /* True if read-only */ - u8 isUnlocked; /* True if no DMS lock held */ + assert( sqlcipher_sqlite3_mutex_held(p->pGroup->mutex) ); - struct ShmRegion { - HANDLE hMap; /* File handle from CreateFileMapping */ - void *pMap; - } *aRegion; - DWORD lastErrno; /* The Windows errno from the last I/O error */ + nNew = p->nHash*2; + if( nNew<256 ){ + nNew = 256; + } - int nRef; /* Number of winShm objects pointing to this */ - winShm *pFirst; /* All winShm objects pointing to this */ - winShmNode *pNext; /* Next in list of all winShmNode objects */ -#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) - u8 nextShmId; /* Next available winShm.id value */ -#endif -}; + pcache1LeaveMutex(p->pGroup); + if( p->nHash ){ sqlcipher_sqlite3BeginBenignMalloc(); } + apNew = (PgHdr1 **)sqlcipher_sqlite3MallocZero(sizeof(PgHdr1 *)*nNew); + if( p->nHash ){ sqlcipher_sqlite3EndBenignMalloc(); } + pcache1EnterMutex(p->pGroup); + if( apNew ){ + for(i=0; inHash; i++){ + PgHdr1 *pPage; + PgHdr1 *pNext = p->apHash[i]; + while( (pPage = pNext)!=0 ){ + unsigned int h = pPage->iKey % nNew; + pNext = pPage->pNext; + pPage->pNext = apNew[h]; + apNew[h] = pPage; + } + } + sqlcipher_sqlite3_free(p->apHash); + p->apHash = apNew; + p->nHash = nNew; + } +} /* -** A global array of all winShmNode objects. +** This function is used internally to remove the page pPage from the +** PGroup LRU list, if is part of it. If pPage is not part of the PGroup +** LRU list, then this function is a no-op. ** -** The winShmMutexHeld() must be true while reading or writing this list. +** The PGroup mutex must be held when this function is called. */ -static winShmNode *winShmNodeList = 0; +static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){ + assert( pPage!=0 ); + assert( PAGE_IS_UNPINNED(pPage) ); + assert( pPage->pLruNext ); + assert( pPage->pLruPrev ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pCache->pGroup->mutex) ); + pPage->pLruPrev->pLruNext = pPage->pLruNext; + pPage->pLruNext->pLruPrev = pPage->pLruPrev; + pPage->pLruNext = 0; + /* pPage->pLruPrev = 0; + ** No need to clear pLruPrev as it is never accessed if pLruNext is 0 */ + assert( pPage->isAnchor==0 ); + assert( pPage->pCache->pGroup->lru.isAnchor==1 ); + pPage->pCache->nRecyclable--; + return pPage; +} + /* -** Structure used internally by this VFS to record the state of an -** open shared memory connection. -** -** The following fields are initialized when this object is created and -** are read-only thereafter: -** -** winShm.pShmNode -** winShm.id +** Remove the page supplied as an argument from the hash table +** (PCache1.apHash structure) that it is currently stored in. +** Also free the page if freePage is true. ** -** All other fields are read/write. The winShm.pShmNode->mutex must be held -** while accessing any read/write fields. +** The PGroup mutex must be held when this function is called. */ -struct winShm { - winShmNode *pShmNode; /* The underlying winShmNode object */ - winShm *pNext; /* Next winShm with the same winShmNode */ - u8 hasMutex; /* True if holding the winShmNode mutex */ - u16 sharedMask; /* Mask of shared locks held */ - u16 exclMask; /* Mask of exclusive locks held */ -#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) - u8 id; /* Id of this connection with its winShmNode */ -#endif -}; +static void pcache1RemoveFromHash(PgHdr1 *pPage, int freeFlag){ + unsigned int h; + PCache1 *pCache = pPage->pCache; + PgHdr1 **pp; + + assert( sqlcipher_sqlite3_mutex_held(pCache->pGroup->mutex) ); + h = pPage->iKey % pCache->nHash; + for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext); + *pp = (*pp)->pNext; + + pCache->nPage--; + if( freeFlag ) pcache1FreePage(pPage); +} /* -** Constants used for locking +** If there are currently more than nMaxPage pages allocated, try +** to recycle pages to reduce the number allocated to nMaxPage. */ -#define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ -#define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ +static void pcache1EnforceMaxPage(PCache1 *pCache){ + PGroup *pGroup = pCache->pGroup; + PgHdr1 *p; + assert( sqlcipher_sqlite3_mutex_held(pGroup->mutex) ); + while( pGroup->nPurgeable>pGroup->nMaxPage + && (p=pGroup->lru.pLruPrev)->isAnchor==0 + ){ + assert( p->pCache->pGroup==pGroup ); + assert( PAGE_IS_UNPINNED(p) ); + pcache1PinPage(p); + pcache1RemoveFromHash(p, 1); + } + if( pCache->nPage==0 && pCache->pBulk ){ + sqlcipher_sqlite3_free(pCache->pBulk); + pCache->pBulk = pCache->pFree = 0; + } +} /* -** Apply advisory locks for all n bytes beginning at ofst. +** Discard all pages from cache pCache with a page number (key value) +** greater than or equal to iLimit. Any pinned pages that meet this +** criteria are unpinned before they are discarded. +** +** The PCache mutex must be held when this function is called. */ -#define WINSHM_UNLCK 1 -#define WINSHM_RDLCK 2 -#define WINSHM_WRLCK 3 -static int winShmSystemLock( - winShmNode *pFile, /* Apply locks to this open shared-memory segment */ - int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */ - int ofst, /* Offset to first byte to be locked/unlocked */ - int nByte /* Number of bytes to lock or unlock */ +static void pcache1TruncateUnsafe( + PCache1 *pCache, /* The cache to truncate */ + unsigned int iLimit /* Drop pages with this pgno or larger */ ){ - int rc = 0; /* Result code form Lock/UnlockFileEx() */ + TESTONLY( int nPage = 0; ) /* To assert pCache->nPage is correct */ + unsigned int h, iStop; + assert( sqlcipher_sqlite3_mutex_held(pCache->pGroup->mutex) ); + assert( pCache->iMaxKey >= iLimit ); + assert( pCache->nHash > 0 ); + if( pCache->iMaxKey - iLimit < pCache->nHash ){ + /* If we are just shaving the last few pages off the end of the + ** cache, then there is no point in scanning the entire hash table. + ** Only scan those hash slots that might contain pages that need to + ** be removed. */ + h = iLimit % pCache->nHash; + iStop = pCache->iMaxKey % pCache->nHash; + TESTONLY( nPage = -10; ) /* Disable the pCache->nPage validity check */ + }else{ + /* This is the general case where many pages are being removed. + ** It is necessary to scan the entire hash table */ + h = pCache->nHash/2; + iStop = h - 1; + } + for(;;){ + PgHdr1 **pp; + PgHdr1 *pPage; + assert( hnHash ); + pp = &pCache->apHash[h]; + while( (pPage = *pp)!=0 ){ + if( pPage->iKey>=iLimit ){ + pCache->nPage--; + *pp = pPage->pNext; + if( PAGE_IS_UNPINNED(pPage) ) pcache1PinPage(pPage); + pcache1FreePage(pPage); + }else{ + pp = &pPage->pNext; + TESTONLY( if( nPage>=0 ) nPage++; ) + } + } + if( h==iStop ) break; + h = (h+1) % pCache->nHash; + } + assert( nPage<0 || pCache->nPage==(unsigned)nPage ); +} - /* Access to the winShmNode object is serialized by the caller */ - assert( pFile->nRef==0 || sqlcipher_sqlite3_mutex_held(pFile->mutex) ); +/******************************************************************************/ +/******** sqlcipher_sqlite3_pcache Methods **********************************************/ - OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n", - pFile->hFile.h, lockType, ofst, nByte)); +/* +** Implementation of the sqlcipher_sqlite3_pcache.xInit method. +*/ +static int pcache1Init(void *NotUsed){ + UNUSED_PARAMETER(NotUsed); + assert( pcache1.isInit==0 ); + memset(&pcache1, 0, sizeof(pcache1)); - /* Release/Acquire the system-level lock */ - if( lockType==WINSHM_UNLCK ){ - rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0); - }else{ - /* Initialize the locking parameters */ - DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; - if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; - rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0); - } - if( rc!= 0 ){ - rc = SQLITE_OK; + /* + ** The pcache1.separateCache variable is true if each PCache has its own + ** private PGroup (mode-1). pcache1.separateCache is false if the single + ** PGroup in pcache1.grp is used for all page caches (mode-2). + ** + ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT + ** + ** * Use a unified cache in single-threaded applications that have + ** configured a start-time buffer for use as page-cache memory using + ** sqlcipher_sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL + ** pBuf argument. + ** + ** * Otherwise use separate caches (mode-1) + */ +#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) + pcache1.separateCache = 0; +#elif SQLITE_THREADSAFE + pcache1.separateCache = sqlcipher_sqlite3GlobalConfig.pPage==0 + || sqlcipher_sqlite3GlobalConfig.bCoreMutex>0; +#else + pcache1.separateCache = sqlcipher_sqlite3GlobalConfig.pPage==0; +#endif + +#if SQLITE_THREADSAFE + if( sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ + pcache1.grp.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU); + pcache1.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PMEM); + } +#endif + if( pcache1.separateCache + && sqlcipher_sqlite3GlobalConfig.nPage!=0 + && sqlcipher_sqlite3GlobalConfig.pPage==0 + ){ + pcache1.nInitPage = sqlcipher_sqlite3GlobalConfig.nPage; }else{ - pFile->lastErrno = osGetLastError(); - rc = SQLITE_BUSY; + pcache1.nInitPage = 0; } + pcache1.grp.mxPinned = 10; + pcache1.isInit = 1; + return SQLITE_OK; +} - OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n", - pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" : - "winLockFile", pFile->lastErrno, sqlcipher_sqlite3ErrName(rc))); - - return rc; +/* +** Implementation of the sqlcipher_sqlite3_pcache.xShutdown method. +** Note that the static mutex allocated in xInit does +** not need to be freed. +*/ +static void pcache1Shutdown(void *NotUsed){ + UNUSED_PARAMETER(NotUsed); + assert( pcache1.isInit!=0 ); + memset(&pcache1, 0, sizeof(pcache1)); } -/* Forward references to VFS methods */ -static int winOpen(sqlcipher_sqlite3_vfs*,const char*,sqlcipher_sqlite3_file*,int,int*); -static int winDelete(sqlcipher_sqlite3_vfs *,const char*,int); +/* forward declaration */ +static void pcache1Destroy(sqlcipher_sqlite3_pcache *p); /* -** Purge the winShmNodeList list of all entries with winShmNode.nRef==0. +** Implementation of the sqlcipher_sqlite3_pcache.xCreate method. ** -** This is not a VFS shared-memory method; it is a utility function called -** by VFS shared-memory methods. +** Allocate a new cache. */ -static void winShmPurge(sqlcipher_sqlite3_vfs *pVfs, int deleteFlag){ - winShmNode **pp; - winShmNode *p; - assert( winShmMutexHeld() ); - OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n", - osGetCurrentProcessId(), deleteFlag)); - pp = &winShmNodeList; - while( (p = *pp)!=0 ){ - if( p->nRef==0 ){ - int i; - if( p->mutex ){ sqlcipher_sqlite3_mutex_free(p->mutex); } - for(i=0; inRegion; i++){ - BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap); - OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n", - osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); - UNUSED_VARIABLE_VALUE(bRc); - bRc = osCloseHandle(p->aRegion[i].hMap); - OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n", - osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); - UNUSED_VARIABLE_VALUE(bRc); - } - if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){ - SimulateIOErrorBenign(1); - winClose((sqlcipher_sqlite3_file *)&p->hFile); - SimulateIOErrorBenign(0); - } - if( deleteFlag ){ - SimulateIOErrorBenign(1); - sqlcipher_sqlite3BeginBenignMalloc(); - winDelete(pVfs, p->zFilename, 0); - sqlcipher_sqlite3EndBenignMalloc(); - SimulateIOErrorBenign(0); - } - *pp = p->pNext; - sqlcipher_sqlite3_free(p->aRegion); - sqlcipher_sqlite3_free(p); +static sqlcipher_sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ + PCache1 *pCache; /* The newly created page cache */ + PGroup *pGroup; /* The group the new page cache will belong to */ + int sz; /* Bytes of memory required to allocate the new cache */ + + assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); + assert( szExtra < 300 ); + + sz = sizeof(PCache1) + sizeof(PGroup)*pcache1.separateCache; + pCache = (PCache1 *)sqlcipher_sqlite3MallocZero(sz); + if( pCache ){ + if( pcache1.separateCache ){ + pGroup = (PGroup*)&pCache[1]; + pGroup->mxPinned = 10; }else{ - pp = &p->pNext; + pGroup = &pcache1.grp; + } + pcache1EnterMutex(pGroup); + if( pGroup->lru.isAnchor==0 ){ + pGroup->lru.isAnchor = 1; + pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru; + } + pCache->pGroup = pGroup; + pCache->szPage = szPage; + pCache->szExtra = szExtra; + pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1)); + pCache->bPurgeable = (bPurgeable ? 1 : 0); + pcache1ResizeHash(pCache); + if( bPurgeable ){ + pCache->nMin = 10; + pGroup->nMinPage += pCache->nMin; + pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; + pCache->pnPurgeable = &pGroup->nPurgeable; + }else{ + pCache->pnPurgeable = &pCache->nPurgeableDummy; + } + pcache1LeaveMutex(pGroup); + if( pCache->nHash==0 ){ + pcache1Destroy((sqlcipher_sqlite3_pcache*)pCache); + pCache = 0; } } + return (sqlcipher_sqlite3_pcache *)pCache; } /* -** The DMS lock has not yet been taken on shm file pShmNode. Attempt to -** take it now. Return SQLITE_OK if successful, or an SQLite error -** code otherwise. +** Implementation of the sqlcipher_sqlite3_pcache.xCachesize method. ** -** If the DMS cannot be locked because this is a readonly_shm=1 -** connection and no other process already holds a lock, return -** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1. +** Configure the cache_size limit for a cache. */ -static int winLockSharedMemory(winShmNode *pShmNode){ - int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1); - - if( rc==SQLITE_OK ){ - if( pShmNode->isReadonly ){ - pShmNode->isUnlocked = 1; - winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); - return SQLITE_READONLY_CANTINIT; - }else if( winTruncate((sqlcipher_sqlite3_file*)&pShmNode->hFile, 0) ){ - winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); - return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(), - "winLockSharedMemory", pShmNode->zFilename); +static void pcache1Cachesize(sqlcipher_sqlite3_pcache *p, int nMax){ + PCache1 *pCache = (PCache1 *)p; + u32 n; + assert( nMax>=0 ); + if( pCache->bPurgeable ){ + PGroup *pGroup = pCache->pGroup; + pcache1EnterMutex(pGroup); + n = (u32)nMax; + if( n > 0x7fff0000 - pGroup->nMaxPage + pCache->nMax ){ + n = 0x7fff0000 - pGroup->nMaxPage + pCache->nMax; } + pGroup->nMaxPage += (n - pCache->nMax); + pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; + pCache->nMax = n; + pCache->n90pct = pCache->nMax*9/10; + pcache1EnforceMaxPage(pCache); + pcache1LeaveMutex(pGroup); } +} - if( rc==SQLITE_OK ){ - winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); +/* +** Implementation of the sqlcipher_sqlite3_pcache.xShrink method. +** +** Free up as much memory as possible. +*/ +static void pcache1Shrink(sqlcipher_sqlite3_pcache *p){ + PCache1 *pCache = (PCache1*)p; + if( pCache->bPurgeable ){ + PGroup *pGroup = pCache->pGroup; + unsigned int savedMaxPage; + pcache1EnterMutex(pGroup); + savedMaxPage = pGroup->nMaxPage; + pGroup->nMaxPage = 0; + pcache1EnforceMaxPage(pCache); + pGroup->nMaxPage = savedMaxPage; + pcache1LeaveMutex(pGroup); } +} - return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1); +/* +** Implementation of the sqlcipher_sqlite3_pcache.xPagecount method. +*/ +static int pcache1Pagecount(sqlcipher_sqlite3_pcache *p){ + int n; + PCache1 *pCache = (PCache1*)p; + pcache1EnterMutex(pCache->pGroup); + n = pCache->nPage; + pcache1LeaveMutex(pCache->pGroup); + return n; } + /* -** Open the shared-memory area associated with database file pDbFd. +** Implement steps 3, 4, and 5 of the pcache1Fetch() algorithm described +** in the header of the pcache1Fetch() procedure. ** -** When opening a new shared-memory file, if no other instances of that -** file are currently open, in this process or in other processes, then -** the file must be truncated to zero length or have its header cleared. +** This steps are broken out into a separate procedure because they are +** usually not needed, and by avoiding the stack initialization required +** for these steps, the main pcache1Fetch() procedure can run faster. */ -static int winOpenSharedMemory(winFile *pDbFd){ - struct winShm *p; /* The connection to be opened */ - winShmNode *pShmNode = 0; /* The underlying mmapped file */ - int rc = SQLITE_OK; /* Result code */ - winShmNode *pNew; /* Newly allocated winShmNode */ - int nName; /* Size of zName in bytes */ +static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( + PCache1 *pCache, + unsigned int iKey, + int createFlag +){ + unsigned int nPinned; + PGroup *pGroup = pCache->pGroup; + PgHdr1 *pPage = 0; - assert( pDbFd->pShm==0 ); /* Not previously opened */ + /* Step 3: Abort if createFlag is 1 but the cache is nearly full */ + assert( pCache->nPage >= pCache->nRecyclable ); + nPinned = pCache->nPage - pCache->nRecyclable; + assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage ); + assert( pCache->n90pct == pCache->nMax*9/10 ); + if( createFlag==1 && ( + nPinned>=pGroup->mxPinned + || nPinned>=pCache->n90pct + || (pcache1UnderMemoryPressure(pCache) && pCache->nRecyclablezPath); - pNew = sqlcipher_sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 ); - if( pNew==0 ){ - sqlcipher_sqlite3_free(p); - return SQLITE_IOERR_NOMEM_BKPT; + if( pCache->nPage>=pCache->nHash ) pcache1ResizeHash(pCache); + assert( pCache->nHash>0 && pCache->apHash ); + + /* Step 4. Try to recycle a page. */ + if( pCache->bPurgeable + && !pGroup->lru.pLruPrev->isAnchor + && ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache)) + ){ + PCache1 *pOther; + pPage = pGroup->lru.pLruPrev; + assert( PAGE_IS_UNPINNED(pPage) ); + pcache1RemoveFromHash(pPage, 0); + pcache1PinPage(pPage); + pOther = pPage->pCache; + if( pOther->szAlloc != pCache->szAlloc ){ + pcache1FreePage(pPage); + pPage = 0; + }else{ + pGroup->nPurgeable -= (pOther->bPurgeable - pCache->bPurgeable); + } } - pNew->zFilename = (char*)&pNew[1]; - sqlcipher_sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); - sqlcipher_sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); - /* Look to see if there is an existing winShmNode that can be used. - ** If no matching winShmNode currently exists, create a new one. + /* Step 5. If a usable page buffer has still not been found, + ** attempt to allocate a new one. */ - winShmEnterMutex(); - for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){ - /* TBD need to come up with better match here. Perhaps - ** use FILE_ID_BOTH_DIR_INFO Structure. - */ - if( sqlcipher_sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break; + if( !pPage ){ + pPage = pcache1AllocPage(pCache, createFlag==1); } - if( pShmNode ){ - sqlcipher_sqlite3_free(pNew); - }else{ - int inFlags = SQLITE_OPEN_WAL; - int outFlags = 0; - - pShmNode = pNew; - pNew = 0; - ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE; - pShmNode->pNext = winShmNodeList; - winShmNodeList = pShmNode; - if( sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ - pShmNode->mutex = sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( pShmNode->mutex==0 ){ - rc = SQLITE_IOERR_NOMEM_BKPT; - goto shm_open_err; - } + if( pPage ){ + unsigned int h = iKey % pCache->nHash; + pCache->nPage++; + pPage->iKey = iKey; + pPage->pNext = pCache->apHash[h]; + pPage->pCache = pCache; + pPage->pLruNext = 0; + /* pPage->pLruPrev = 0; + ** No need to clear pLruPrev since it is not accessed when pLruNext==0 */ + *(void **)pPage->page.pExtra = 0; + pCache->apHash[h] = pPage; + if( iKey>pCache->iMaxKey ){ + pCache->iMaxKey = iKey; } + } + return pPage; +} - if( 0==sqlcipher_sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ - inFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; +/* +** Implementation of the sqlcipher_sqlite3_pcache.xFetch method. +** +** Fetch a page by key value. +** +** Whether or not a new page may be allocated by this function depends on +** the value of the createFlag argument. 0 means do not allocate a new +** page. 1 means allocate a new page if space is easily available. 2 +** means to try really hard to allocate a new page. +** +** For a non-purgeable cache (a cache used as the storage for an in-memory +** database) there is really no difference between createFlag 1 and 2. So +** the calling function (pcache.c) will never have a createFlag of 1 on +** a non-purgeable cache. +** +** There are three different approaches to obtaining space for a page, +** depending on the value of parameter createFlag (which may be 0, 1 or 2). +** +** 1. Regardless of the value of createFlag, the cache is searched for a +** copy of the requested page. If one is found, it is returned. +** +** 2. If createFlag==0 and the page is not already in the cache, NULL is +** returned. +** +** 3. If createFlag is 1, and the page is not already in the cache, then +** return NULL (do not allocate a new page) if any of the following +** conditions are true: +** +** (a) the number of pages pinned by the cache is greater than +** PCache1.nMax, or +** +** (b) the number of pages pinned by the cache is greater than +** the sum of nMax for all purgeable caches, less the sum of +** nMin for all other purgeable caches, or +** +** 4. If none of the first three conditions apply and the cache is marked +** as purgeable, and if one of the following is true: +** +** (a) The number of pages allocated for the cache is already +** PCache1.nMax, or +** +** (b) The number of pages allocated for all purgeable caches is +** already equal to or greater than the sum of nMax for all +** purgeable caches, +** +** (c) The system is under memory pressure and wants to avoid +** unnecessary pages cache entry allocations +** +** then attempt to recycle a page from the LRU list. If it is the right +** size, return the recycled buffer. Otherwise, free the buffer and +** proceed to step 5. +** +** 5. Otherwise, allocate and return a new page buffer. +** +** There are two versions of this routine. pcache1FetchWithMutex() is +** the general case. pcache1FetchNoMutex() is a faster implementation for +** the common case where pGroup->mutex is NULL. The pcache1Fetch() wrapper +** invokes the appropriate routine. +*/ +static PgHdr1 *pcache1FetchNoMutex( + sqlcipher_sqlite3_pcache *p, + unsigned int iKey, + int createFlag +){ + PCache1 *pCache = (PCache1 *)p; + PgHdr1 *pPage = 0; + + /* Step 1: Search the hash table for an existing entry. */ + pPage = pCache->apHash[iKey % pCache->nHash]; + while( pPage && pPage->iKey!=iKey ){ pPage = pPage->pNext; } + + /* Step 2: If the page was found in the hash table, then return it. + ** If the page was not in the hash table and createFlag is 0, abort. + ** Otherwise (page not in hash and createFlag!=0) continue with + ** subsequent steps to try to create the page. */ + if( pPage ){ + if( PAGE_IS_UNPINNED(pPage) ){ + return pcache1PinPage(pPage); }else{ - inFlags |= SQLITE_OPEN_READONLY; - } - rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, - (sqlcipher_sqlite3_file*)&pShmNode->hFile, - inFlags, &outFlags); - if( rc!=SQLITE_OK ){ - rc = winLogError(rc, osGetLastError(), "winOpenShm", - pShmNode->zFilename); - goto shm_open_err; + return pPage; } - if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1; - - rc = winLockSharedMemory(pShmNode); - if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err; + }else if( createFlag ){ + /* Steps 3, 4, and 5 implemented by this subroutine */ + return pcache1FetchStage2(pCache, iKey, createFlag); + }else{ + return 0; } +} +#if PCACHE1_MIGHT_USE_GROUP_MUTEX +static PgHdr1 *pcache1FetchWithMutex( + sqlcipher_sqlite3_pcache *p, + unsigned int iKey, + int createFlag +){ + PCache1 *pCache = (PCache1 *)p; + PgHdr1 *pPage; - /* Make the new connection a child of the winShmNode */ - p->pShmNode = pShmNode; -#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) - p->id = pShmNode->nextShmId++; + pcache1EnterMutex(pCache->pGroup); + pPage = pcache1FetchNoMutex(p, iKey, createFlag); + assert( pPage==0 || pCache->iMaxKey>=iKey ); + pcache1LeaveMutex(pCache->pGroup); + return pPage; +} +#endif +static sqlcipher_sqlite3_pcache_page *pcache1Fetch( + sqlcipher_sqlite3_pcache *p, + unsigned int iKey, + int createFlag +){ +#if PCACHE1_MIGHT_USE_GROUP_MUTEX || defined(SQLITE_DEBUG) + PCache1 *pCache = (PCache1 *)p; #endif - pShmNode->nRef++; - pDbFd->pShm = p; - winShmLeaveMutex(); - - /* The reference count on pShmNode has already been incremented under - ** the cover of the winShmEnterMutex() mutex and the pointer from the - ** new (struct winShm) object to the pShmNode has been set. All that is - ** left to do is to link the new object into the linked list starting - ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex - ** mutex. - */ - sqlcipher_sqlite3_mutex_enter(pShmNode->mutex); - p->pNext = pShmNode->pFirst; - pShmNode->pFirst = p; - sqlcipher_sqlite3_mutex_leave(pShmNode->mutex); - return rc; - /* Jump here on any error */ -shm_open_err: - winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); - winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */ - sqlcipher_sqlite3_free(p); - sqlcipher_sqlite3_free(pNew); - winShmLeaveMutex(); - return rc; + assert( offsetof(PgHdr1,page)==0 ); + assert( pCache->bPurgeable || createFlag!=1 ); + assert( pCache->bPurgeable || pCache->nMin==0 ); + assert( pCache->bPurgeable==0 || pCache->nMin==10 ); + assert( pCache->nMin==0 || pCache->bPurgeable ); + assert( pCache->nHash>0 ); +#if PCACHE1_MIGHT_USE_GROUP_MUTEX + if( pCache->pGroup->mutex ){ + return (sqlcipher_sqlite3_pcache_page*)pcache1FetchWithMutex(p, iKey, createFlag); + }else +#endif + { + return (sqlcipher_sqlite3_pcache_page*)pcache1FetchNoMutex(p, iKey, createFlag); + } } -/* -** Close a connection to shared-memory. Delete the underlying -** storage if deleteFlag is true. -*/ -static int winShmUnmap( - sqlcipher_sqlite3_file *fd, /* Database holding shared memory */ - int deleteFlag /* Delete after closing if true */ -){ - winFile *pDbFd; /* Database holding shared-memory */ - winShm *p; /* The connection to be closed */ - winShmNode *pShmNode; /* The underlying shared-memory file */ - winShm **pp; /* For looping over sibling connections */ - pDbFd = (winFile*)fd; - p = pDbFd->pShm; - if( p==0 ) return SQLITE_OK; - pShmNode = p->pShmNode; +/* +** Implementation of the sqlcipher_sqlite3_pcache.xUnpin method. +** +** Mark a page as unpinned (eligible for asynchronous recycling). +*/ +static void pcache1Unpin( + sqlcipher_sqlite3_pcache *p, + sqlcipher_sqlite3_pcache_page *pPg, + int reuseUnlikely +){ + PCache1 *pCache = (PCache1 *)p; + PgHdr1 *pPage = (PgHdr1 *)pPg; + PGroup *pGroup = pCache->pGroup; - /* Remove connection p from the set of connections associated - ** with pShmNode */ - sqlcipher_sqlite3_mutex_enter(pShmNode->mutex); - for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} - *pp = p->pNext; + assert( pPage->pCache==pCache ); + pcache1EnterMutex(pGroup); - /* Free the connection p */ - sqlcipher_sqlite3_free(p); - pDbFd->pShm = 0; - sqlcipher_sqlite3_mutex_leave(pShmNode->mutex); + /* It is an error to call this function if the page is already + ** part of the PGroup LRU list. + */ + assert( pPage->pLruNext==0 ); + assert( PAGE_IS_PINNED(pPage) ); - /* If pShmNode->nRef has reached 0, then close the underlying - ** shared-memory file, too */ - winShmEnterMutex(); - assert( pShmNode->nRef>0 ); - pShmNode->nRef--; - if( pShmNode->nRef==0 ){ - winShmPurge(pDbFd->pVfs, deleteFlag); + if( reuseUnlikely || pGroup->nPurgeable>pGroup->nMaxPage ){ + pcache1RemoveFromHash(pPage, 1); + }else{ + /* Add the page to the PGroup LRU list. */ + PgHdr1 **ppFirst = &pGroup->lru.pLruNext; + pPage->pLruPrev = &pGroup->lru; + (pPage->pLruNext = *ppFirst)->pLruPrev = pPage; + *ppFirst = pPage; + pCache->nRecyclable++; } - winShmLeaveMutex(); - return SQLITE_OK; + pcache1LeaveMutex(pCache->pGroup); } /* -** Change the lock state for a shared-memory segment. +** Implementation of the sqlcipher_sqlite3_pcache.xRekey method. */ -static int winShmLock( - sqlcipher_sqlite3_file *fd, /* Database file holding the shared memory */ - int ofst, /* First lock to acquire or release */ - int n, /* Number of locks to acquire or release */ - int flags /* What to do with the lock */ +static void pcache1Rekey( + sqlcipher_sqlite3_pcache *p, + sqlcipher_sqlite3_pcache_page *pPg, + unsigned int iOld, + unsigned int iNew ){ - winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */ - winShm *p = pDbFd->pShm; /* The shared memory being locked */ - winShm *pX; /* For looping over all siblings */ - winShmNode *pShmNode = p->pShmNode; - int rc = SQLITE_OK; /* Result code */ - u16 mask; /* Mask of locks to take or release */ + PCache1 *pCache = (PCache1 *)p; + PgHdr1 *pPage = (PgHdr1 *)pPg; + PgHdr1 **pp; + unsigned int hOld, hNew; + assert( pPage->iKey==iOld ); + assert( pPage->pCache==pCache ); + assert( iOld!=iNew ); /* The page number really is changing */ - assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); - assert( n>=1 ); - assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) - || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) - || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) - || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); - assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); + pcache1EnterMutex(pCache->pGroup); - mask = (u16)((1U<<(ofst+n)) - (1U<1 || mask==(1<mutex); - if( flags & SQLITE_SHM_UNLOCK ){ - u16 allMask = 0; /* Mask of locks held by siblings */ + assert( pcache1FetchNoMutex(p, iOld, 0)==pPage ); /* pPg really is iOld */ + hOld = iOld%pCache->nHash; + pp = &pCache->apHash[hOld]; + while( (*pp)!=pPage ){ + pp = &(*pp)->pNext; + } + *pp = pPage->pNext; - /* See if any siblings hold this same lock */ - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ - if( pX==p ) continue; - assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); - allMask |= pX->sharedMask; - } + assert( pcache1FetchNoMutex(p, iNew, 0)==0 ); /* iNew not in cache */ + hNew = iNew%pCache->nHash; + pPage->iKey = iNew; + pPage->pNext = pCache->apHash[hNew]; + pCache->apHash[hNew] = pPage; + if( iNew>pCache->iMaxKey ){ + pCache->iMaxKey = iNew; + } - /* Unlock the system-level locks */ - if( (mask & allMask)==0 ){ - rc = winShmSystemLock(pShmNode, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n); - }else{ - rc = SQLITE_OK; - } + pcache1LeaveMutex(pCache->pGroup); +} - /* Undo the local locks */ - if( rc==SQLITE_OK ){ - p->exclMask &= ~mask; - p->sharedMask &= ~mask; - } - }else if( flags & SQLITE_SHM_SHARED ){ - u16 allShared = 0; /* Union of locks held by connections other than "p" */ +/* +** Implementation of the sqlcipher_sqlite3_pcache.xTruncate method. +** +** Discard all unpinned pages in the cache with a page number equal to +** or greater than parameter iLimit. Any pinned pages with a page number +** equal to or greater than iLimit are implicitly unpinned. +*/ +static void pcache1Truncate(sqlcipher_sqlite3_pcache *p, unsigned int iLimit){ + PCache1 *pCache = (PCache1 *)p; + pcache1EnterMutex(pCache->pGroup); + if( iLimit<=pCache->iMaxKey ){ + pcache1TruncateUnsafe(pCache, iLimit); + pCache->iMaxKey = iLimit-1; + } + pcache1LeaveMutex(pCache->pGroup); +} - /* Find out which shared locks are already held by sibling connections. - ** If any sibling already holds an exclusive lock, go ahead and return - ** SQLITE_BUSY. - */ - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ - if( (pX->exclMask & mask)!=0 ){ - rc = SQLITE_BUSY; - break; - } - allShared |= pX->sharedMask; - } +/* +** Implementation of the sqlcipher_sqlite3_pcache.xDestroy method. +** +** Destroy a cache allocated using pcache1Create(). +*/ +static void pcache1Destroy(sqlcipher_sqlite3_pcache *p){ + PCache1 *pCache = (PCache1 *)p; + PGroup *pGroup = pCache->pGroup; + assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); + pcache1EnterMutex(pGroup); + if( pCache->nPage ) pcache1TruncateUnsafe(pCache, 0); + assert( pGroup->nMaxPage >= pCache->nMax ); + pGroup->nMaxPage -= pCache->nMax; + assert( pGroup->nMinPage >= pCache->nMin ); + pGroup->nMinPage -= pCache->nMin; + pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; + pcache1EnforceMaxPage(pCache); + pcache1LeaveMutex(pGroup); + sqlcipher_sqlite3_free(pCache->pBulk); + sqlcipher_sqlite3_free(pCache->apHash); + sqlcipher_sqlite3_free(pCache); +} - /* Get shared locks at the system level, if necessary */ - if( rc==SQLITE_OK ){ - if( (allShared & mask)==0 ){ - rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n); - }else{ - rc = SQLITE_OK; - } - } +/* +** This function is called during initialization (sqlcipher_sqlite3_initialize()) to +** install the default pluggable cache module, assuming the user has not +** already provided an alternative. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PCacheSetDefault(void){ + static const sqlcipher_sqlite3_pcache_methods2 defaultMethods = { + 1, /* iVersion */ + 0, /* pArg */ + pcache1Init, /* xInit */ + pcache1Shutdown, /* xShutdown */ + pcache1Create, /* xCreate */ + pcache1Cachesize, /* xCachesize */ + pcache1Pagecount, /* xPagecount */ + pcache1Fetch, /* xFetch */ + pcache1Unpin, /* xUnpin */ + pcache1Rekey, /* xRekey */ + pcache1Truncate, /* xTruncate */ + pcache1Destroy, /* xDestroy */ + pcache1Shrink /* xShrink */ + }; + sqlcipher_sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods); +} - /* Get the local shared locks */ - if( rc==SQLITE_OK ){ - p->sharedMask |= mask; - } - }else{ - /* Make sure no sibling connections hold locks that will block this - ** lock. If any do, return SQLITE_BUSY right away. - */ - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ - if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ - rc = SQLITE_BUSY; - break; - } - } +/* +** Return the size of the header on each page of this PCACHE implementation. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); } - /* Get the exclusive locks at the system level. Then if successful - ** also mark the local connection as being locked. - */ - if( rc==SQLITE_OK ){ - rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n); - if( rc==SQLITE_OK ){ - assert( (p->sharedMask & mask)==0 ); - p->exclMask |= mask; - } +/* +** Return the global mutex used by this PCACHE implementation. The +** sqlcipher_sqlite3_status() routine needs access to this mutex. +*/ +SQLITE_PRIVATE sqlcipher_sqlite3_mutex *sqlcipher_sqlite3Pcache1Mutex(void){ + return pcache1.mutex; +} + +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +/* +** This function is called to free superfluous dynamically allocated memory +** held by the pager system. Memory in use by any SQLite pager allocated +** by the current thread may be sqlcipher_sqlite3_free()ed. +** +** nReq is the number of bytes of memory required. Once this much has +** been released, the function returns. The return value is the total number +** of bytes of memory released. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PcacheReleaseMemory(int nReq){ + int nFree = 0; + assert( sqlcipher_sqlite3_mutex_notheld(pcache1.grp.mutex) ); + assert( sqlcipher_sqlite3_mutex_notheld(pcache1.mutex) ); + if( sqlcipher_sqlite3GlobalConfig.pPage==0 ){ + PgHdr1 *p; + pcache1EnterMutex(&pcache1.grp); + while( (nReq<0 || nFreeisAnchor==0 + ){ + nFree += pcache1MemSize(p->page.pBuf); +#ifdef SQLITE_PCACHE_SEPARATE_HEADER + nFree += sqlcipher_sqlite3MemSize(p); +#endif + assert( PAGE_IS_UNPINNED(p) ); + pcache1PinPage(p); + pcache1RemoveFromHash(p, 1); } + pcache1LeaveMutex(&pcache1.grp); } - sqlcipher_sqlite3_mutex_leave(pShmNode->mutex); - OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n", - osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, - sqlcipher_sqlite3ErrName(rc))); - return rc; + return nFree; } +#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */ +#ifdef SQLITE_TEST /* -** Implement a memory barrier or memory fence on shared memory. -** -** All loads and stores begun before the barrier must complete before -** any load or store begun after the barrier. +** This function is used by test procedures to inspect the internal state +** of the global cache. */ -static void winShmBarrier( - sqlcipher_sqlite3_file *fd /* Database holding the shared memory */ +SQLITE_PRIVATE void sqlcipher_sqlite3PcacheStats( + int *pnCurrent, /* OUT: Total number of pages cached */ + int *pnMax, /* OUT: Global maximum cache size */ + int *pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */ + int *pnRecyclable /* OUT: Total number of pages available for recycling */ ){ - UNUSED_PARAMETER(fd); - sqlcipher_sqlite3MemoryBarrier(); /* compiler-defined memory barrier */ - winShmEnterMutex(); /* Also mutex, for redundancy */ - winShmLeaveMutex(); + PgHdr1 *p; + int nRecyclable = 0; + for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){ + assert( PAGE_IS_UNPINNED(p) ); + nRecyclable++; + } + *pnCurrent = pcache1.grp.nPurgeable; + *pnMax = (int)pcache1.grp.nMaxPage; + *pnMin = (int)pcache1.grp.nMinPage; + *pnRecyclable = nRecyclable; } +#endif +/************** End of pcache1.c *********************************************/ +/************** Begin file rowset.c ******************************************/ /* -** This function is called to obtain a pointer to region iRegion of the -** shared-memory associated with the database file fd. Shared-memory regions -** are numbered starting from zero. Each shared-memory region is szRegion -** bytes in size. +** 2008 December 3 ** -** If an error occurs, an error code is returned and *pp is set to NULL. +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** Otherwise, if the isWrite parameter is 0 and the requested shared-memory -** region has not been allocated (by any client, including one running in a -** separate process), then *pp is set to NULL and SQLITE_OK returned. If -** isWrite is non-zero and the requested shared-memory region has not yet -** been allocated, it is allocated by this function. +** 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. ** -** If the shared-memory region has already been allocated or is allocated by -** this call as described above, then it is mapped into this processes -** address space (if it is not already), *pp is set to point to the mapped -** memory and SQLITE_OK returned. +************************************************************************* +** +** This module implements an object we call a "RowSet". +** +** The RowSet object is a collection of rowids. Rowids +** are inserted into the RowSet in an arbitrary order. Inserts +** can be intermixed with tests to see if a given rowid has been +** previously inserted into the RowSet. +** +** After all inserts are finished, it is possible to extract the +** elements of the RowSet in sorted order. Once this extraction +** process has started, no new elements may be inserted. +** +** Hence, the primitive operations for a RowSet are: +** +** CREATE +** INSERT +** TEST +** SMALLEST +** DESTROY +** +** The CREATE and DESTROY primitives are the constructor and destructor, +** obviously. The INSERT primitive adds a new element to the RowSet. +** TEST checks to see if an element is already in the RowSet. SMALLEST +** extracts the least value from the RowSet. +** +** The INSERT primitive might allocate additional memory. Memory is +** allocated in chunks so most INSERTs do no allocation. There is an +** upper bound on the size of allocated memory. No memory is freed +** until DESTROY. +** +** The TEST primitive includes a "batch" number. The TEST primitive +** will only see elements that were inserted before the last change +** in the batch number. In other words, if an INSERT occurs between +** two TESTs where the TESTs have the same batch nubmer, then the +** value added by the INSERT will not be visible to the second TEST. +** The initial batch number is zero, so if the very first TEST contains +** a non-zero batch number, it will see all prior INSERTs. +** +** No INSERTs may occurs after a SMALLEST. An assertion will fail if +** that is attempted. +** +** The cost of an INSERT is roughly constant. (Sometimes new memory +** has to be allocated on an INSERT.) The cost of a TEST with a new +** batch number is O(NlogN) where N is the number of elements in the RowSet. +** The cost of a TEST using the same batch number is O(logN). The cost +** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST +** primitives are constant time. The cost of DESTROY is O(N). +** +** TEST and SMALLEST may not be used by the same RowSet. This used to +** be possible, but the feature was not used, so it was removed in order +** to simplify the code. */ -static int winShmMap( - sqlcipher_sqlite3_file *fd, /* Handle open on database file */ - int iRegion, /* Region to retrieve */ - int szRegion, /* Size of regions */ - int isWrite, /* True to extend file if necessary */ - void volatile **pp /* OUT: Mapped memory */ -){ - winFile *pDbFd = (winFile*)fd; - winShm *pShm = pDbFd->pShm; - winShmNode *pShmNode; - DWORD protect = PAGE_READWRITE; - DWORD flags = FILE_MAP_WRITE | FILE_MAP_READ; - int rc = SQLITE_OK; - - if( !pShm ){ - rc = winOpenSharedMemory(pDbFd); - if( rc!=SQLITE_OK ) return rc; - pShm = pDbFd->pShm; - assert( pShm!=0 ); - } - pShmNode = pShm->pShmNode; - - sqlcipher_sqlite3_mutex_enter(pShmNode->mutex); - if( pShmNode->isUnlocked ){ - rc = winLockSharedMemory(pShmNode); - if( rc!=SQLITE_OK ) goto shmpage_out; - pShmNode->isUnlocked = 0; - } - assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); - - if( pShmNode->nRegion<=iRegion ){ - struct ShmRegion *apNew; /* New aRegion[] array */ - int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ - sqlcipher_sqlite3_int64 sz; /* Current size of wal-index file */ +/* #include "sqliteInt.h" */ - pShmNode->szRegion = szRegion; - /* The requested region is not mapped into this processes address space. - ** Check to see if it has been allocated (i.e. if the wal-index file is - ** large enough to contain the requested region). - */ - rc = winFileSize((sqlcipher_sqlite3_file *)&pShmNode->hFile, &sz); - if( rc!=SQLITE_OK ){ - rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), - "winShmMap1", pDbFd->zPath); - goto shmpage_out; - } +/* +** Target size for allocation chunks. +*/ +#define ROWSET_ALLOCATION_SIZE 1024 - if( szhFile, nByte); - if( rc!=SQLITE_OK ){ - rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), - "winShmMap2", pDbFd->zPath); - goto shmpage_out; - } - } +/* +** The number of rowset entries per allocation chunk. +*/ +#define ROWSET_ENTRY_PER_CHUNK \ + ((ROWSET_ALLOCATION_SIZE-8)/sizeof(struct RowSetEntry)) - /* Map the requested memory region into this processes address space. */ - apNew = (struct ShmRegion *)sqlcipher_sqlite3_realloc64( - pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) - ); - if( !apNew ){ - rc = SQLITE_IOERR_NOMEM_BKPT; - goto shmpage_out; - } - pShmNode->aRegion = apNew; +/* +** Each entry in a RowSet is an instance of the following object. +** +** This same object is reused to store a linked list of trees of RowSetEntry +** objects. In that alternative use, pRight points to the next entry +** in the list, pLeft points to the tree, and v is unused. The +** RowSet.pForest value points to the head of this forest list. +*/ +struct RowSetEntry { + i64 v; /* ROWID value for this entry */ + struct RowSetEntry *pRight; /* Right subtree (larger entries) or list */ + struct RowSetEntry *pLeft; /* Left subtree (smaller entries) */ +}; - if( pShmNode->isReadonly ){ - protect = PAGE_READONLY; - flags = FILE_MAP_READ; - } +/* +** RowSetEntry objects are allocated in large chunks (instances of the +** following structure) to reduce memory allocation overhead. The +** chunks are kept on a linked list so that they can be deallocated +** when the RowSet is destroyed. +*/ +struct RowSetChunk { + struct RowSetChunk *pNextChunk; /* Next chunk on list of them all */ + struct RowSetEntry aEntry[ROWSET_ENTRY_PER_CHUNK]; /* Allocated entries */ +}; - while( pShmNode->nRegion<=iRegion ){ - HANDLE hMap = NULL; /* file-mapping handle */ - void *pMap = 0; /* Mapped memory region */ +/* +** A RowSet in an instance of the following structure. +** +** A typedef of this structure if found in sqliteInt.h. +*/ +struct RowSet { + struct RowSetChunk *pChunk; /* List of all chunk allocations */ + sqlcipher_sqlite3 *db; /* The database connection */ + struct RowSetEntry *pEntry; /* List of entries using pRight */ + struct RowSetEntry *pLast; /* Last entry on the pEntry list */ + struct RowSetEntry *pFresh; /* Source of new entry objects */ + struct RowSetEntry *pForest; /* List of binary trees of entries */ + u16 nFresh; /* Number of objects on pFresh */ + u16 rsFlags; /* Various flags */ + int iBatch; /* Current insert batch */ +}; -#if SQLITE_OS_WINRT - hMap = osCreateFileMappingFromApp(pShmNode->hFile.h, - NULL, protect, nByte, NULL - ); -#elif defined(SQLITE_WIN32_HAS_WIDE) - hMap = osCreateFileMappingW(pShmNode->hFile.h, - NULL, protect, 0, nByte, NULL - ); -#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA - hMap = osCreateFileMappingA(pShmNode->hFile.h, - NULL, protect, 0, nByte, NULL - ); -#endif - OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n", - osGetCurrentProcessId(), pShmNode->nRegion, nByte, - hMap ? "ok" : "failed")); - if( hMap ){ - int iOffset = pShmNode->nRegion*szRegion; - int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; -#if SQLITE_OS_WINRT - pMap = osMapViewOfFileFromApp(hMap, flags, - iOffset - iOffsetShift, szRegion + iOffsetShift - ); -#else - pMap = osMapViewOfFile(hMap, flags, - 0, iOffset - iOffsetShift, szRegion + iOffsetShift - ); -#endif - OSTRACE(("SHM-MAP-MAP pid=%lu, region=%d, offset=%d, size=%d, rc=%s\n", - osGetCurrentProcessId(), pShmNode->nRegion, iOffset, - szRegion, pMap ? "ok" : "failed")); - } - if( !pMap ){ - pShmNode->lastErrno = osGetLastError(); - rc = winLogError(SQLITE_IOERR_SHMMAP, pShmNode->lastErrno, - "winShmMap3", pDbFd->zPath); - if( hMap ) osCloseHandle(hMap); - goto shmpage_out; - } +/* +** Allowed values for RowSet.rsFlags +*/ +#define ROWSET_SORTED 0x01 /* True if RowSet.pEntry is sorted */ +#define ROWSET_NEXT 0x02 /* True if sqlcipher_sqlite3RowSetNext() has been called */ - pShmNode->aRegion[pShmNode->nRegion].pMap = pMap; - pShmNode->aRegion[pShmNode->nRegion].hMap = hMap; - pShmNode->nRegion++; - } +/* +** Allocate a RowSet object. Return NULL if a memory allocation +** error occurs. +*/ +SQLITE_PRIVATE RowSet *sqlcipher_sqlite3RowSetInit(sqlcipher_sqlite3 *db){ + RowSet *p = sqlcipher_sqlite3DbMallocRawNN(db, sizeof(*p)); + if( p ){ + int N = sqlcipher_sqlite3DbMallocSize(db, p); + p->pChunk = 0; + p->db = db; + p->pEntry = 0; + p->pLast = 0; + p->pForest = 0; + p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p); + p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry)); + p->rsFlags = ROWSET_SORTED; + p->iBatch = 0; } + return p; +} -shmpage_out: - if( pShmNode->nRegion>iRegion ){ - int iOffset = iRegion*szRegion; - int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; - char *p = (char *)pShmNode->aRegion[iRegion].pMap; - *pp = (void *)&p[iOffsetShift]; - }else{ - *pp = 0; +/* +** Deallocate all chunks from a RowSet. This frees all memory that +** the RowSet has allocated over its lifetime. This routine is +** the destructor for the RowSet. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3RowSetClear(void *pArg){ + RowSet *p = (RowSet*)pArg; + struct RowSetChunk *pChunk, *pNextChunk; + for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){ + pNextChunk = pChunk->pNextChunk; + sqlcipher_sqlite3DbFree(p->db, pChunk); } - if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; - sqlcipher_sqlite3_mutex_leave(pShmNode->mutex); - return rc; + p->pChunk = 0; + p->nFresh = 0; + p->pEntry = 0; + p->pLast = 0; + p->pForest = 0; + p->rsFlags = ROWSET_SORTED; } -#else -# define winShmMap 0 -# define winShmLock 0 -# define winShmBarrier 0 -# define winShmUnmap 0 -#endif /* #ifndef SQLITE_OMIT_WAL */ +/* +** Deallocate all chunks from a RowSet. This frees all memory that +** the RowSet has allocated over its lifetime. This routine is +** the destructor for the RowSet. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3RowSetDelete(void *pArg){ + sqlcipher_sqlite3RowSetClear(pArg); + sqlcipher_sqlite3DbFree(((RowSet*)pArg)->db, pArg); +} /* -** Cleans up the mapped region of the specified file, if any. +** Allocate a new RowSetEntry object that is associated with the +** given RowSet. Return a pointer to the new and completely uninitialized +** object. +** +** In an OOM situation, the RowSet.db->mallocFailed flag is set and this +** routine returns NULL. */ -#if SQLITE_MAX_MMAP_SIZE>0 -static int winUnmapfile(winFile *pFile){ - assert( pFile!=0 ); - OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, " - "mmapSize=%lld, mmapSizeMax=%lld\n", - osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion, - pFile->mmapSize, pFile->mmapSizeMax)); - if( pFile->pMapRegion ){ - if( !osUnmapViewOfFile(pFile->pMapRegion) ){ - pFile->lastErrno = osGetLastError(); - OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, pMapRegion=%p, " - "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile, - pFile->pMapRegion)); - return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, - "winUnmapfile1", pFile->zPath); - } - pFile->pMapRegion = 0; - pFile->mmapSize = 0; - } - if( pFile->hMap!=NULL ){ - if( !osCloseHandle(pFile->hMap) ){ - pFile->lastErrno = osGetLastError(); - OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n", - osGetCurrentProcessId(), pFile, pFile->hMap)); - return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, - "winUnmapfile2", pFile->zPath); +static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){ + assert( p!=0 ); + if( p->nFresh==0 ){ /*OPTIMIZATION-IF-FALSE*/ + /* We could allocate a fresh RowSetEntry each time one is needed, but it + ** is more efficient to pull a preallocated entry from the pool */ + struct RowSetChunk *pNew; + pNew = sqlcipher_sqlite3DbMallocRawNN(p->db, sizeof(*pNew)); + if( pNew==0 ){ + return 0; } - pFile->hMap = NULL; + pNew->pNextChunk = p->pChunk; + p->pChunk = pNew; + p->pFresh = pNew->aEntry; + p->nFresh = ROWSET_ENTRY_PER_CHUNK; } - OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n", - osGetCurrentProcessId(), pFile)); - return SQLITE_OK; + p->nFresh--; + return p->pFresh++; } /* -** Memory map or remap the file opened by file-descriptor pFd (if the file -** is already mapped, the existing mapping is replaced by the new). Or, if -** there already exists a mapping for this file, and there are still -** outstanding xFetch() references to it, this function is a no-op. -** -** If parameter nByte is non-negative, then it is the requested size of -** the mapping to create. Otherwise, if nByte is less than zero, then the -** requested size is the size of the file on disk. The actual size of the -** created mapping is either the requested size or the value configured -** using SQLITE_FCNTL_MMAP_SIZE, whichever is smaller. +** Insert a new value into a RowSet. ** -** SQLITE_OK is returned if no error occurs (even if the mapping is not -** recreated as a result of outstanding references) or an SQLite error -** code otherwise. +** The mallocFailed flag of the database connection is set if a +** memory allocation fails. */ -static int winMapfile(winFile *pFd, sqlcipher_sqlite3_int64 nByte){ - sqlcipher_sqlite3_int64 nMap = nByte; - int rc; - - assert( nMap>=0 || pFd->nFetchOut==0 ); - OSTRACE(("MAP-FILE pid=%lu, pFile=%p, size=%lld\n", - osGetCurrentProcessId(), pFd, nByte)); - - if( pFd->nFetchOut>0 ) return SQLITE_OK; - - if( nMap<0 ){ - rc = winFileSize((sqlcipher_sqlite3_file*)pFd, &nMap); - if( rc ){ - OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_IOERR_FSTAT\n", - osGetCurrentProcessId(), pFd)); - return SQLITE_IOERR_FSTAT; - } - } - if( nMap>pFd->mmapSizeMax ){ - nMap = pFd->mmapSizeMax; - } - nMap &= ~(sqlcipher_sqlite3_int64)(winSysInfo.dwPageSize - 1); +SQLITE_PRIVATE void sqlcipher_sqlite3RowSetInsert(RowSet *p, i64 rowid){ + struct RowSetEntry *pEntry; /* The new entry */ + struct RowSetEntry *pLast; /* The last prior entry */ - if( nMap==0 && pFd->mmapSize>0 ){ - winUnmapfile(pFd); - } - if( nMap!=pFd->mmapSize ){ - void *pNew = 0; - DWORD protect = PAGE_READONLY; - DWORD flags = FILE_MAP_READ; + /* This routine is never called after sqlcipher_sqlite3RowSetNext() */ + assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 ); - winUnmapfile(pFd); -#ifdef SQLITE_MMAP_READWRITE - if( (pFd->ctrlFlags & WINFILE_RDONLY)==0 ){ - protect = PAGE_READWRITE; - flags |= FILE_MAP_WRITE; - } -#endif -#if SQLITE_OS_WINRT - pFd->hMap = osCreateFileMappingFromApp(pFd->h, NULL, protect, nMap, NULL); -#elif defined(SQLITE_WIN32_HAS_WIDE) - pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect, - (DWORD)((nMap>>32) & 0xffffffff), - (DWORD)(nMap & 0xffffffff), NULL); -#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA - pFd->hMap = osCreateFileMappingA(pFd->h, NULL, protect, - (DWORD)((nMap>>32) & 0xffffffff), - (DWORD)(nMap & 0xffffffff), NULL); -#endif - if( pFd->hMap==NULL ){ - pFd->lastErrno = osGetLastError(); - rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno, - "winMapfile1", pFd->zPath); - /* Log the error, but continue normal operation using xRead/xWrite */ - OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=%s\n", - osGetCurrentProcessId(), pFd, sqlcipher_sqlite3ErrName(rc))); - return SQLITE_OK; - } - assert( (nMap % winSysInfo.dwPageSize)==0 ); - assert( sizeof(SIZE_T)==sizeof(sqlcipher_sqlite3_int64) || nMap<=0xffffffff ); -#if SQLITE_OS_WINRT - pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, (SIZE_T)nMap); -#else - pNew = osMapViewOfFile(pFd->hMap, flags, 0, 0, (SIZE_T)nMap); -#endif - if( pNew==NULL ){ - osCloseHandle(pFd->hMap); - pFd->hMap = NULL; - pFd->lastErrno = osGetLastError(); - rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno, - "winMapfile2", pFd->zPath); - /* Log the error, but continue normal operation using xRead/xWrite */ - OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=%s\n", - osGetCurrentProcessId(), pFd, sqlcipher_sqlite3ErrName(rc))); - return SQLITE_OK; + pEntry = rowSetEntryAlloc(p); + if( pEntry==0 ) return; + pEntry->v = rowid; + pEntry->pRight = 0; + pLast = p->pLast; + if( pLast ){ + if( rowid<=pLast->v ){ /*OPTIMIZATION-IF-FALSE*/ + /* Avoid unnecessary sorts by preserving the ROWSET_SORTED flags + ** where possible */ + p->rsFlags &= ~ROWSET_SORTED; } - pFd->pMapRegion = pNew; - pFd->mmapSize = nMap; + pLast->pRight = pEntry; + }else{ + p->pEntry = pEntry; } - - OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n", - osGetCurrentProcessId(), pFd)); - return SQLITE_OK; + p->pLast = pEntry; } -#endif /* SQLITE_MAX_MMAP_SIZE>0 */ /* -** If possible, return a pointer to a mapping of file fd starting at offset -** iOff. The mapping must be valid for at least nAmt bytes. -** -** If such a pointer can be obtained, store it in *pp and return SQLITE_OK. -** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK. -** Finally, if an error does occur, return an SQLite error code. The final -** value of *pp is undefined in this case. +** Merge two lists of RowSetEntry objects. Remove duplicates. ** -** If this function does return a pointer, the caller must eventually -** release the reference by calling winUnfetch(). +** The input lists are connected via pRight pointers and are +** assumed to each already be in sorted order. */ -static int winFetch(sqlcipher_sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ -#if SQLITE_MAX_MMAP_SIZE>0 - winFile *pFd = (winFile*)fd; /* The underlying database file */ -#endif - *pp = 0; - - OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n", - osGetCurrentProcessId(), fd, iOff, nAmt, pp)); +static struct RowSetEntry *rowSetEntryMerge( + struct RowSetEntry *pA, /* First sorted list to be merged */ + struct RowSetEntry *pB /* Second sorted list to be merged */ +){ + struct RowSetEntry head; + struct RowSetEntry *pTail; -#if SQLITE_MAX_MMAP_SIZE>0 - if( pFd->mmapSizeMax>0 ){ - if( pFd->pMapRegion==0 ){ - int rc = winMapfile(pFd, -1); - if( rc!=SQLITE_OK ){ - OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n", - osGetCurrentProcessId(), pFd, sqlcipher_sqlite3ErrName(rc))); - return rc; + pTail = &head; + assert( pA!=0 && pB!=0 ); + for(;;){ + assert( pA->pRight==0 || pA->v<=pA->pRight->v ); + assert( pB->pRight==0 || pB->v<=pB->pRight->v ); + if( pA->v<=pB->v ){ + if( pA->vv ) pTail = pTail->pRight = pA; + pA = pA->pRight; + if( pA==0 ){ + pTail->pRight = pB; + break; + } + }else{ + pTail = pTail->pRight = pB; + pB = pB->pRight; + if( pB==0 ){ + pTail->pRight = pA; + break; } - } - if( pFd->mmapSize >= iOff+nAmt ){ - assert( pFd->pMapRegion!=0 ); - *pp = &((u8 *)pFd->pMapRegion)[iOff]; - pFd->nFetchOut++; } } -#endif - - OSTRACE(("FETCH pid=%lu, pFile=%p, pp=%p, *pp=%p, rc=SQLITE_OK\n", - osGetCurrentProcessId(), fd, pp, *pp)); - return SQLITE_OK; + return head.pRight; } /* -** If the third argument is non-NULL, then this function releases a -** reference obtained by an earlier call to winFetch(). The second -** argument passed to this function must be the same as the corresponding -** argument that was passed to the winFetch() invocation. -** -** Or, if the third argument is NULL, then this function is being called -** to inform the VFS layer that, according to POSIX, any existing mapping -** may now be invalid and should be unmapped. +** Sort all elements on the list of RowSetEntry objects into order of +** increasing v. */ -static int winUnfetch(sqlcipher_sqlite3_file *fd, i64 iOff, void *p){ -#if SQLITE_MAX_MMAP_SIZE>0 - winFile *pFd = (winFile*)fd; /* The underlying database file */ - - /* If p==0 (unmap the entire file) then there must be no outstanding - ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), - ** then there must be at least one outstanding. */ - assert( (p==0)==(pFd->nFetchOut==0) ); - - /* If p!=0, it must match the iOff value. */ - assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] ); - - OSTRACE(("UNFETCH pid=%lu, pFile=%p, offset=%lld, p=%p\n", - osGetCurrentProcessId(), pFd, iOff, p)); +static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){ + unsigned int i; + struct RowSetEntry *pNext, *aBucket[40]; - if( p ){ - pFd->nFetchOut--; - }else{ - /* FIXME: If Windows truly always prevents truncating or deleting a - ** file while a mapping is held, then the following winUnmapfile() call - ** is unnecessary can be omitted - potentially improving - ** performance. */ - winUnmapfile(pFd); + memset(aBucket, 0, sizeof(aBucket)); + while( pIn ){ + pNext = pIn->pRight; + pIn->pRight = 0; + for(i=0; aBucket[i]; i++){ + pIn = rowSetEntryMerge(aBucket[i], pIn); + aBucket[i] = 0; + } + aBucket[i] = pIn; + pIn = pNext; } - - assert( pFd->nFetchOut>=0 ); -#endif - - OSTRACE(("UNFETCH pid=%lu, pFile=%p, rc=SQLITE_OK\n", - osGetCurrentProcessId(), fd)); - return SQLITE_OK; + pIn = aBucket[0]; + for(i=1; ipLeft ){ + struct RowSetEntry *p; + rowSetTreeToList(pIn->pLeft, ppFirst, &p); + p->pRight = pIn; + }else{ + *ppFirst = pIn; + } + if( pIn->pRight ){ + rowSetTreeToList(pIn->pRight, &pIn->pRight, ppLast); + }else{ + *ppLast = pIn; + } + assert( (*ppLast)->pRight==0 ); +} -/**************************************************************************** -**************************** sqlcipher_sqlite3_vfs methods **************************** -** -** This division contains the implementation of methods on the -** sqlcipher_sqlite3_vfs object. -*/ -#if defined(__CYGWIN__) /* -** Convert a filename from whatever the underlying operating system -** supports for filenames into UTF-8. Space to hold the result is -** obtained from malloc and must be freed by the calling function. +** Convert a sorted list of elements (connected by pRight) into a binary +** tree with depth of iDepth. A depth of 1 means the tree contains a single +** node taken from the head of *ppList. A depth of 2 means a tree with +** three nodes. And so forth. +** +** Use as many entries from the input list as required and update the +** *ppList to point to the unused elements of the list. If the input +** list contains too few elements, then construct an incomplete tree +** and leave *ppList set to NULL. +** +** Return a pointer to the root of the constructed binary tree. */ -static char *winConvertToUtf8Filename(const void *zFilename){ - char *zConverted = 0; - if( osIsNT() ){ - zConverted = winUnicodeToUtf8(zFilename); +static struct RowSetEntry *rowSetNDeepTree( + struct RowSetEntry **ppList, + int iDepth +){ + struct RowSetEntry *p; /* Root of the new tree */ + struct RowSetEntry *pLeft; /* Left subtree */ + if( *ppList==0 ){ /*OPTIMIZATION-IF-TRUE*/ + /* Prevent unnecessary deep recursion when we run out of entries */ + return 0; } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - zConverted = winMbcsToUtf8(zFilename, osAreFileApisANSI()); + if( iDepth>1 ){ /*OPTIMIZATION-IF-TRUE*/ + /* This branch causes a *balanced* tree to be generated. A valid tree + ** is still generated without this branch, but the tree is wildly + ** unbalanced and inefficient. */ + pLeft = rowSetNDeepTree(ppList, iDepth-1); + p = *ppList; + if( p==0 ){ /*OPTIMIZATION-IF-FALSE*/ + /* It is safe to always return here, but the resulting tree + ** would be unbalanced */ + return pLeft; + } + p->pLeft = pLeft; + *ppList = p->pRight; + p->pRight = rowSetNDeepTree(ppList, iDepth-1); + }else{ + p = *ppList; + *ppList = p->pRight; + p->pLeft = p->pRight = 0; } -#endif - /* caller will handle out of memory */ - return zConverted; + return p; } -#endif /* -** Convert a UTF-8 filename into whatever form the underlying -** operating system wants filenames in. Space to hold the result -** is obtained from malloc and must be freed by the calling -** function. +** Convert a sorted list of elements into a binary tree. Make the tree +** as deep as it needs to be in order to contain the entire list. */ -static void *winConvertFromUtf8Filename(const char *zFilename){ - void *zConverted = 0; - if( osIsNT() ){ - zConverted = winUtf8ToUnicode(zFilename); - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI()); +static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){ + int iDepth; /* Depth of the tree so far */ + struct RowSetEntry *p; /* Current tree root */ + struct RowSetEntry *pLeft; /* Left subtree */ + + assert( pList!=0 ); + p = pList; + pList = p->pRight; + p->pLeft = p->pRight = 0; + for(iDepth=1; pList; iDepth++){ + pLeft = p; + p = pList; + pList = p->pRight; + p->pLeft = pLeft; + p->pRight = rowSetNDeepTree(&pList, iDepth); } -#endif - /* caller will handle out of memory */ - return zConverted; + return p; } /* -** This function returns non-zero if the specified UTF-8 string buffer -** ends with a directory separator character or one was successfully -** added to it. +** Extract the smallest element from the RowSet. +** Write the element into *pRowid. Return 1 on success. Return +** 0 if the RowSet is already empty. +** +** After this routine has been called, the sqlcipher_sqlite3RowSetInsert() +** routine may not be called again. +** +** This routine may not be called after sqlcipher_sqlite3RowSetTest() has +** been used. Older versions of RowSet allowed that, but as the +** capability was not used by the code generator, it was removed +** for code economy. */ -static int winMakeEndInDirSep(int nBuf, char *zBuf){ - if( zBuf ){ - int nLen = sqlcipher_sqlite3Strlen30(zBuf); - if( nLen>0 ){ - if( winIsDirSep(zBuf[nLen-1]) ){ - return 1; - }else if( nLen+1pForest==0 ); /* Cannot be used with sqlcipher_sqlite3RowSetText() */ + + /* Merge the forest into a single sorted list on first call */ + if( (p->rsFlags & ROWSET_NEXT)==0 ){ /*OPTIMIZATION-IF-FALSE*/ + if( (p->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/ + p->pEntry = rowSetEntrySort(p->pEntry); } + p->rsFlags |= ROWSET_SORTED|ROWSET_NEXT; + } + + /* Return the next entry on the list */ + if( p->pEntry ){ + *pRowid = p->pEntry->v; + p->pEntry = p->pEntry->pRight; + if( p->pEntry==0 ){ /*OPTIMIZATION-IF-TRUE*/ + /* Free memory immediately, rather than waiting on sqlcipher_sqlite3_finalize() */ + sqlcipher_sqlite3RowSetClear(p); + } + return 1; + }else{ + return 0; } - return 0; } /* -** Create a temporary file name and store the resulting pointer into pzBuf. -** The pointer returned in pzBuf must be freed via sqlcipher_sqlite3_free(). +** Check to see if element iRowid was inserted into the rowset as +** part of any insert batch prior to iBatch. Return 1 or 0. +** +** If this is the first test of a new batch and if there exist entries +** on pRowSet->pEntry, then sort those entries into the forest at +** pRowSet->pForest so that they can be tested. */ -static int winGetTempname(sqlcipher_sqlite3_vfs *pVfs, char **pzBuf){ - static char zChars[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789"; - size_t i, j; - int nPre = sqlcipher_sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX); - int nMax, nBuf, nDir, nLen; - char *zBuf; - - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. - */ - SimulateIOError( return SQLITE_IOERR ); +SQLITE_PRIVATE int sqlcipher_sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlcipher_sqlite3_int64 iRowid){ + struct RowSetEntry *p, *pTree; - /* Allocate a temporary buffer to store the fully qualified file - ** name for the temporary file. If this fails, we cannot continue. - */ - nMax = pVfs->mxPathname; nBuf = nMax + 2; - zBuf = sqlcipher_sqlite3MallocZero( nBuf ); - if( !zBuf ){ - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM_BKPT; - } + /* This routine is never called after sqlcipher_sqlite3RowSetNext() */ + assert( pRowSet!=0 && (pRowSet->rsFlags & ROWSET_NEXT)==0 ); - /* Figure out the effective temporary directory. First, check if one - ** has been explicitly set by the application; otherwise, use the one - ** configured by the operating system. + /* Sort entries into the forest on the first test of a new batch. + ** To save unnecessary work, only do this when the batch number changes. */ - nDir = nMax - (nPre + 15); - assert( nDir>0 ); - if( sqlcipher_sqlite3_temp_directory ){ - int nDirLen = sqlcipher_sqlite3Strlen30(sqlcipher_sqlite3_temp_directory); - if( nDirLen>0 ){ - if( !winIsDirSep(sqlcipher_sqlite3_temp_directory[nDirLen-1]) ){ - nDirLen++; - } - if( nDirLen>nDir ){ - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); - return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0); + if( iBatch!=pRowSet->iBatch ){ /*OPTIMIZATION-IF-FALSE*/ + p = pRowSet->pEntry; + if( p ){ + struct RowSetEntry **ppPrevTree = &pRowSet->pForest; + if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/ + /* Only sort the current set of entries if they need it */ + p = rowSetEntrySort(p); } - sqlcipher_sqlite3_snprintf(nMax, zBuf, "%s", sqlcipher_sqlite3_temp_directory); - } - } -#if defined(__CYGWIN__) - else{ - static const char *azDirs[] = { - 0, /* getenv("SQLITE_TMPDIR") */ - 0, /* getenv("TMPDIR") */ - 0, /* getenv("TMP") */ - 0, /* getenv("TEMP") */ - 0, /* getenv("USERPROFILE") */ - "/var/tmp", - "/usr/tmp", - "/tmp", - ".", - 0 /* List terminator */ - }; - unsigned int i; - const char *zDir = 0; - - if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); - if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); - if( !azDirs[2] ) azDirs[2] = getenv("TMP"); - if( !azDirs[3] ) azDirs[3] = getenv("TEMP"); - if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE"); - for(i=0; ipForest; pTree; pTree=pTree->pRight){ + ppPrevTree = &pTree->pRight; + if( pTree->pLeft==0 ){ + pTree->pLeft = rowSetListToTree(p); break; + }else{ + struct RowSetEntry *pAux, *pTail; + rowSetTreeToList(pTree->pLeft, &pAux, &pTail); + pTree->pLeft = 0; + p = rowSetEntryMerge(pAux, p); } - sqlcipher_sqlite3_free(zConverted); - }else{ - zConverted = sqlcipher_sqlite3MallocZero( nMax+1 ); - if( !zConverted ){ - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM_BKPT; - } - if( cygwin_conv_path( - osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir, - zConverted, nMax+1)<0 ){ - sqlcipher_sqlite3_free(zConverted); - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n")); - return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno, - "winGetTempname2", zDir); - } - if( winIsDir(zConverted) ){ - /* At this point, we know the candidate directory exists and should - ** be used. However, we may need to convert the string containing - ** its name into UTF-8 (i.e. if it is UTF-16 right now). - */ - char *zUtf8 = winConvertToUtf8Filename(zConverted); - if( !zUtf8 ){ - sqlcipher_sqlite3_free(zConverted); - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM_BKPT; - } - sqlcipher_sqlite3_snprintf(nMax, zBuf, "%s", zUtf8); - sqlcipher_sqlite3_free(zUtf8); - sqlcipher_sqlite3_free(zConverted); - break; + } + if( pTree==0 ){ + *ppPrevTree = pTree = rowSetEntryAlloc(pRowSet); + if( pTree ){ + pTree->v = 0; + pTree->pRight = 0; + pTree->pLeft = rowSetListToTree(p); } - sqlcipher_sqlite3_free(zConverted); } + pRowSet->pEntry = 0; + pRowSet->pLast = 0; + pRowSet->rsFlags |= ROWSET_SORTED; } + pRowSet->iBatch = iBatch; } -#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__) - else if( osIsNT() ){ - char *zMulti; - LPWSTR zWidePath = sqlcipher_sqlite3MallocZero( nMax*sizeof(WCHAR) ); - if( !zWidePath ){ - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM_BKPT; - } - if( osGetTempPathW(nMax, zWidePath)==0 ){ - sqlcipher_sqlite3_free(zWidePath); - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n")); - return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(), - "winGetTempname2", 0); - } - zMulti = winUnicodeToUtf8(zWidePath); - if( zMulti ){ - sqlcipher_sqlite3_snprintf(nMax, zBuf, "%s", zMulti); - sqlcipher_sqlite3_free(zMulti); - sqlcipher_sqlite3_free(zWidePath); - }else{ - sqlcipher_sqlite3_free(zWidePath); - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM_BKPT; - } - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - char *zUtf8; - char *zMbcsPath = sqlcipher_sqlite3MallocZero( nMax ); - if( !zMbcsPath ){ - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM_BKPT; - } - if( osGetTempPathA(nMax, zMbcsPath)==0 ){ - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n")); - return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(), - "winGetTempname3", 0); - } - zUtf8 = winMbcsToUtf8(zMbcsPath, osAreFileApisANSI()); - if( zUtf8 ){ - sqlcipher_sqlite3_snprintf(nMax, zBuf, "%s", zUtf8); - sqlcipher_sqlite3_free(zUtf8); - }else{ - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM_BKPT; - } - } -#endif /* SQLITE_WIN32_HAS_ANSI */ -#endif /* !SQLITE_OS_WINRT */ - /* - ** Check to make sure the temporary directory ends with an appropriate - ** separator. If it does not and there is not enough space left to add - ** one, fail. + /* Test to see if the iRowid value appears anywhere in the forest. + ** Return 1 if it does and 0 if not. */ - if( !winMakeEndInDirSep(nDir+1, zBuf) ){ - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); - return winLogError(SQLITE_ERROR, 0, "winGetTempname4", 0); + for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){ + p = pTree->pLeft; + while( p ){ + if( p->vpRight; + }else if( p->v>iRowid ){ + p = p->pLeft; + }else{ + return 1; + } + } } + return 0; +} - /* - ** Check that the output buffer is large enough for the temporary file - ** name in the following format: - ** - ** "/etilqs_XXXXXXXXXXXXXXX\0\0" - ** - ** If not, return SQLITE_ERROR. The number 17 is used here in order to - ** account for the space used by the 15 character random suffix and the - ** two trailing NUL characters. The final directory separator character - ** has already added if it was not already present. - */ - nLen = sqlcipher_sqlite3Strlen30(zBuf); - if( (nLen + nPre + 17) > nBuf ){ - sqlcipher_sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); - return winLogError(SQLITE_ERROR, 0, "winGetTempname5", 0); - } +/************** End of rowset.c **********************************************/ +/************** Begin file pager.c *******************************************/ +/* +** 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 is the implementation of the page cache subsystem or "pager". +** +** The pager is used to access a database disk file. It implements +** atomic commit and rollback through the use of a journal file that +** is separate from the database file. The pager also implements file +** locking to prevent two processes from writing the same database +** file simultaneously, or one process from reading the database while +** another is writing. +*/ +#ifndef SQLITE_OMIT_DISKIO +/* #include "sqliteInt.h" */ +/************** Include wal.h in the middle of pager.c ***********************/ +/************** Begin file wal.h *********************************************/ +/* +** 2010 February 1 +** +** 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 header file defines the interface to the write-ahead logging +** system. Refer to the comments below and the header comment attached to +** the implementation of each function in log.c for further details. +*/ - sqlcipher_sqlite3_snprintf(nBuf-16-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX); +#ifndef SQLITE_WAL_H +#define SQLITE_WAL_H - j = sqlcipher_sqlite3Strlen30(zBuf); - sqlcipher_sqlite3_randomness(15, &zBuf[j]); - for(i=0; i<15; i++, j++){ - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; - } - zBuf[j] = 0; - zBuf[j+1] = 0; - *pzBuf = zBuf; +/* #include "sqliteInt.h" */ - OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf)); - return SQLITE_OK; -} +/* Macros for extracting appropriate sync flags for either transaction +** commits (WAL_SYNC_FLAGS(X)) or for checkpoint ops (CKPT_SYNC_FLAGS(X)): +*/ +#define WAL_SYNC_FLAGS(X) ((X)&0x03) +#define CKPT_SYNC_FLAGS(X) (((X)>>2)&0x03) -/* -** Return TRUE if the named file is really a directory. Return false if -** it is something other than a directory, or if there is any kind of memory -** allocation failure. +#ifdef SQLITE_OMIT_WAL +# define sqlcipher_sqlite3WalOpen(x,y,z) 0 +# define sqlcipher_sqlite3WalLimit(x,y) +# define sqlcipher_sqlite3WalClose(v,w,x,y,z) 0 +# define sqlcipher_sqlite3WalBeginReadTransaction(y,z) 0 +# define sqlcipher_sqlite3WalEndReadTransaction(z) +# define sqlcipher_sqlite3WalDbsize(y) 0 +# define sqlcipher_sqlite3WalBeginWriteTransaction(y) 0 +# define sqlcipher_sqlite3WalEndWriteTransaction(x) 0 +# define sqlcipher_sqlite3WalUndo(x,y,z) 0 +# define sqlcipher_sqlite3WalSavepoint(y,z) +# define sqlcipher_sqlite3WalSavepointUndo(y,z) 0 +# define sqlcipher_sqlite3WalFrames(u,v,w,x,y,z) 0 +# define sqlcipher_sqlite3WalCheckpoint(q,r,s,t,u,v,w,x,y,z) 0 +# define sqlcipher_sqlite3WalCallback(z) 0 +# define sqlcipher_sqlite3WalExclusiveMode(y,z) 0 +# define sqlcipher_sqlite3WalHeapMemory(z) 0 +# define sqlcipher_sqlite3WalFramesize(z) 0 +# define sqlcipher_sqlite3WalFindFrame(x,y,z) 0 +# define sqlcipher_sqlite3WalFile(x) 0 +#else + +#define WAL_SAVEPOINT_NDATA 4 + +/* Connection to a write-ahead log (WAL) file. +** There is one object of this type for each pager. */ -static int winIsDir(const void *zConverted){ - DWORD attr; - int rc = 0; - DWORD lastErrno; +typedef struct Wal Wal; - if( osIsNT() ){ - int cnt = 0; - WIN32_FILE_ATTRIBUTE_DATA sAttrData; - memset(&sAttrData, 0, sizeof(sAttrData)); - while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, - GetFileExInfoStandard, - &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} - if( !rc ){ - return 0; /* Invalid name? */ - } - attr = sAttrData.dwFileAttributes; -#if SQLITE_OS_WINCE==0 - }else{ - attr = osGetFileAttributesA((char*)zConverted); -#endif - } - return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY); -} +/* Open and close a connection to a write-ahead log. */ +SQLITE_PRIVATE int sqlcipher_sqlite3WalOpen(sqlcipher_sqlite3_vfs*, sqlcipher_sqlite3_file*, const char *, int, i64, Wal**); +SQLITE_PRIVATE int sqlcipher_sqlite3WalClose(Wal *pWal, sqlcipher_sqlite3*, int sync_flags, int, u8 *); -/* forward reference */ -static int winAccess( - sqlcipher_sqlite3_vfs *pVfs, /* Not used on win32 */ - const char *zFilename, /* Name of file to check */ - int flags, /* Type of test to make on this file */ - int *pResOut /* OUT: Result */ +/* Set the limiting size of a WAL file. */ +SQLITE_PRIVATE void sqlcipher_sqlite3WalLimit(Wal*, i64); + +/* Used by readers to open (lock) and close (unlock) a snapshot. A +** snapshot is like a read-transaction. It is the state of the database +** at an instant in time. sqlcipher_sqlite3WalOpenSnapshot gets a read lock and +** preserves the current state even if the other threads or processes +** write to or checkpoint the WAL. sqlcipher_sqlite3WalCloseSnapshot() closes the +** transaction and releases the lock. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3WalBeginReadTransaction(Wal *pWal, int *); +SQLITE_PRIVATE void sqlcipher_sqlite3WalEndReadTransaction(Wal *pWal); + +/* Read a page from the write-ahead log, if it is present. */ +SQLITE_PRIVATE int sqlcipher_sqlite3WalFindFrame(Wal *, Pgno, u32 *); +SQLITE_PRIVATE int sqlcipher_sqlite3WalReadFrame(Wal *, u32, int, u8 *); + +/* If the WAL is not empty, return the size of the database. */ +SQLITE_PRIVATE Pgno sqlcipher_sqlite3WalDbsize(Wal *pWal); + +/* Obtain or release the WRITER lock. */ +SQLITE_PRIVATE int sqlcipher_sqlite3WalBeginWriteTransaction(Wal *pWal); +SQLITE_PRIVATE int sqlcipher_sqlite3WalEndWriteTransaction(Wal *pWal); + +/* Undo any frames written (but not committed) to the log */ +SQLITE_PRIVATE int sqlcipher_sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx); + +/* Return an integer that records the current (uncommitted) write +** position in the WAL */ +SQLITE_PRIVATE void sqlcipher_sqlite3WalSavepoint(Wal *pWal, u32 *aWalData); + +/* Move the write position of the WAL back to iFrame. Called in +** response to a ROLLBACK TO command. */ +SQLITE_PRIVATE int sqlcipher_sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData); + +/* Write a frame or frames to the log. */ +SQLITE_PRIVATE int sqlcipher_sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int); + +/* Copy pages from the log to the database file */ +SQLITE_PRIVATE int sqlcipher_sqlite3WalCheckpoint( + Wal *pWal, /* Write-ahead log connection */ + sqlcipher_sqlite3 *db, /* Check this handle's interrupt flag */ + int eMode, /* One of PASSIVE, FULL and RESTART */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ + int sync_flags, /* Flags to sync db file with (or 0) */ + int nBuf, /* Size of buffer nBuf */ + u8 *zBuf, /* Temporary buffer to use */ + int *pnLog, /* OUT: Number of frames in WAL */ + int *pnCkpt /* OUT: Number of backfilled frames in WAL */ ); -/* -** Open a file. +/* Return the value to pass to a sqlcipher_sqlite3_wal_hook callback, the +** number of frames in the WAL at the point of the last commit since +** sqlcipher_sqlite3WalCallback() was called. If no commits have occurred since +** the last call, then return 0. */ -static int winOpen( - sqlcipher_sqlite3_vfs *pVfs, /* Used to get maximum path length and AppData */ - const char *zName, /* Name of the file (UTF-8) */ - sqlcipher_sqlite3_file *id, /* Write the SQLite file handle here */ - int flags, /* Open mode flags */ - int *pOutFlags /* Status return flags */ -){ - HANDLE h; - DWORD lastErrno = 0; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - DWORD dwFlagsAndAttributes = 0; -#if SQLITE_OS_WINCE - int isTemp = 0; -#endif - winVfsAppData *pAppData; - winFile *pFile = (winFile*)id; - void *zConverted; /* Filename in OS encoding */ - const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ - int cnt = 0; +SQLITE_PRIVATE int sqlcipher_sqlite3WalCallback(Wal *pWal); - /* If argument zPath is a NULL pointer, this function is required to open - ** a temporary file. Use this buffer to store the file name in. - */ - char *zTmpname = 0; /* For temporary filename, if necessary. */ +/* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released) +** by the pager layer on the database file. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3WalExclusiveMode(Wal *pWal, int op); - int rc = SQLITE_OK; /* Function Return Code */ -#if !defined(NDEBUG) || SQLITE_OS_WINCE - int eType = flags&0xFFFFFF00; /* Type of file to open */ +/* Return true if the argument is non-NULL and the WAL module is using +** heap-memory for the wal-index. Otherwise, if the argument is NULL or the +** WAL module is using shared-memory, return false. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3WalHeapMemory(Wal *pWal); + +#ifdef SQLITE_ENABLE_SNAPSHOT +SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotGet(Wal *pWal, sqlcipher_sqlite3_snapshot **ppSnapshot); +SQLITE_PRIVATE void sqlcipher_sqlite3WalSnapshotOpen(Wal *pWal, sqlcipher_sqlite3_snapshot *pSnapshot); +SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotRecover(Wal *pWal); +SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotCheck(Wal *pWal, sqlcipher_sqlite3_snapshot *pSnapshot); +SQLITE_PRIVATE void sqlcipher_sqlite3WalSnapshotUnlock(Wal *pWal); #endif - int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); - int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); - int isCreate = (flags & SQLITE_OPEN_CREATE); - int isReadonly = (flags & SQLITE_OPEN_READONLY); - int isReadWrite = (flags & SQLITE_OPEN_READWRITE); +#ifdef SQLITE_ENABLE_ZIPVFS +/* If the WAL file is not empty, return the number of bytes of content +** stored in each frame (i.e. the db page-size when the WAL was created). +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3WalFramesize(Wal *pWal); +#endif -#ifndef NDEBUG - int isOpenJournal = (isCreate && ( - eType==SQLITE_OPEN_SUPER_JOURNAL - || eType==SQLITE_OPEN_MAIN_JOURNAL - || eType==SQLITE_OPEN_WAL - )); +/* Return the sqlcipher_sqlite3_file object for the WAL file */ +SQLITE_PRIVATE sqlcipher_sqlite3_file *sqlcipher_sqlite3WalFile(Wal *pWal); + +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +SQLITE_PRIVATE int sqlcipher_sqlite3WalWriteLock(Wal *pWal, int bLock); +SQLITE_PRIVATE void sqlcipher_sqlite3WalDb(Wal *pWal, sqlcipher_sqlite3 *db); #endif - OSTRACE(("OPEN name=%s, pFile=%p, flags=%x, pOutFlags=%p\n", - zUtf8Name, id, flags, pOutFlags)); +#endif /* ifndef SQLITE_OMIT_WAL */ +#endif /* SQLITE_WAL_H */ - /* Check the following statements are true: - ** - ** (a) Exactly one of the READWRITE and READONLY flags must be set, and - ** (b) if CREATE is set, then READWRITE must also be set, and - ** (c) if EXCLUSIVE is set, then CREATE must also be set. - ** (d) if DELETEONCLOSE is set, then CREATE must also be set. - */ - assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); - assert(isCreate==0 || isReadWrite); - assert(isExclusive==0 || isCreate); - assert(isDelete==0 || isCreate); +/************** End of wal.h *************************************************/ +/************** Continuing where we left off in pager.c **********************/ - /* The main DB, main journal, WAL file and super-journal are never - ** automatically deleted. Nor are they ever temporary files. */ - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_SUPER_JOURNAL ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); - /* Assert that the upper layer has set one of the "file-type" flags. */ - assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB - || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL - || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL - || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL - ); +/******************* NOTES ON THE DESIGN OF THE PAGER ************************ +** +** This comment block describes invariants that hold when using a rollback +** journal. These invariants do not apply for journal_mode=WAL, +** journal_mode=MEMORY, or journal_mode=OFF. +** +** Within this comment block, a page is deemed to have been synced +** automatically as soon as it is written when PRAGMA synchronous=OFF. +** Otherwise, the page is not synced until the xSync method of the VFS +** is called successfully on the file containing the page. +** +** Definition: A page of the database file is said to be "overwriteable" if +** one or more of the following are true about the page: +** +** (a) The original content of the page as it was at the beginning of +** the transaction has been written into the rollback journal and +** synced. +** +** (b) The page was a freelist leaf page at the start of the transaction. +** +** (c) The page number is greater than the largest page that existed in +** the database file at the start of the transaction. +** +** (1) A page of the database file is never overwritten unless one of the +** following are true: +** +** (a) The page and all other pages on the same sector are overwriteable. +** +** (b) The atomic page write optimization is enabled, and the entire +** transaction other than the update of the transaction sequence +** number consists of a single page change. +** +** (2) The content of a page written into the rollback journal exactly matches +** both the content in the database when the rollback journal was written +** and the content in the database at the beginning of the current +** transaction. +** +** (3) Writes to the database file are an integer multiple of the page size +** in length and are aligned on a page boundary. +** +** (4) Reads from the database file are either aligned on a page boundary and +** an integer multiple of the page size in length or are taken from the +** first 100 bytes of the database file. +** +** (5) All writes to the database file are synced prior to the rollback journal +** being deleted, truncated, or zeroed. +** +** (6) If a super-journal file is used, then all writes to the database file +** are synced prior to the super-journal being deleted. +** +** Definition: Two databases (or the same database at two points it time) +** are said to be "logically equivalent" if they give the same answer to +** all queries. Note in particular the content of freelist leaf +** pages can be changed arbitrarily without affecting the logical equivalence +** of the database. +** +** (7) At any time, if any subset, including the empty set and the total set, +** of the unsynced changes to a rollback journal are removed and the +** journal is rolled back, the resulting database file will be logically +** equivalent to the database file at the beginning of the transaction. +** +** (8) When a transaction is rolled back, the xTruncate method of the VFS +** is called to restore the database file to the same size it was at +** the beginning of the transaction. (In some VFSes, the xTruncate +** method is a no-op, but that does not change the fact the SQLite will +** invoke it.) +** +** (9) Whenever the database file is modified, at least one bit in the range +** of bytes from 24 through 39 inclusive will be changed prior to releasing +** the EXCLUSIVE lock, thus signaling other connections on the same +** database to flush their caches. +** +** (10) The pattern of bits in bytes 24 through 39 shall not repeat in less +** than one billion transactions. +** +** (11) A database file is well-formed at the beginning and at the conclusion +** of every transaction. +** +** (12) An EXCLUSIVE lock is held on the database file when writing to +** the database file. +** +** (13) A SHARED lock is held on the database file while reading any +** content out of the database file. +** +******************************************************************************/ - assert( pFile!=0 ); - memset(pFile, 0, sizeof(winFile)); - pFile->h = INVALID_HANDLE_VALUE; +/* +** Macros for troubleshooting. Normally turned off +*/ +#if 0 +int sqlcipher_sqlite3PagerTrace=1; /* True to enable tracing */ +#define sqlcipher_sqlite3DebugPrintf printf +#define PAGERTRACE(X) if( sqlcipher_sqlite3PagerTrace ){ sqlcipher_sqlite3DebugPrintf X; } +#else +#define PAGERTRACE(X) +#endif -#if SQLITE_OS_WINRT - if( !zUtf8Name && !sqlcipher_sqlite3_temp_directory ){ - sqlcipher_sqlite3_log(SQLITE_ERROR, - "sqlcipher_sqlite3_temp_directory variable should be set for WinRT"); - } +/* +** The following two macros are used within the PAGERTRACE() macros above +** to print out file-descriptors. +** +** PAGERID() takes a pointer to a Pager struct as its argument. The +** associated file-descriptor is returned. FILEHANDLEID() takes an sqlcipher_sqlite3_file +** struct as its argument. +*/ +#define PAGERID(p) (SQLITE_PTR_TO_INT(p->fd)) +#define FILEHANDLEID(fd) (SQLITE_PTR_TO_INT(fd)) + +/* +** The Pager.eState variable stores the current 'state' of a pager. A +** pager may be in any one of the seven states shown in the following +** state diagram. +** +** OPEN <------+------+ +** | | | +** V | | +** +---------> READER-------+ | +** | | | +** | V | +** |<-------WRITER_LOCKED------> ERROR +** | | ^ +** | V | +** |<------WRITER_CACHEMOD-------->| +** | | | +** | V | +** |<-------WRITER_DBMOD---------->| +** | | | +** | V | +** +<------WRITER_FINISHED-------->+ +** +** +** List of state transitions and the C [function] that performs each: +** +** OPEN -> READER [sqlcipher_sqlite3PagerSharedLock] +** READER -> OPEN [pager_unlock] +** +** READER -> WRITER_LOCKED [sqlcipher_sqlite3PagerBegin] +** WRITER_LOCKED -> WRITER_CACHEMOD [pager_open_journal] +** WRITER_CACHEMOD -> WRITER_DBMOD [syncJournal] +** WRITER_DBMOD -> WRITER_FINISHED [sqlcipher_sqlite3PagerCommitPhaseOne] +** WRITER_*** -> READER [pager_end_transaction] +** +** WRITER_*** -> ERROR [pager_error] +** ERROR -> OPEN [pager_unlock] +** +** +** OPEN: +** +** The pager starts up in this state. Nothing is guaranteed in this +** state - the file may or may not be locked and the database size is +** unknown. The database may not be read or written. +** +** * No read or write transaction is active. +** * Any lock, or no lock at all, may be held on the database file. +** * The dbSize, dbOrigSize and dbFileSize variables may not be trusted. +** +** READER: +** +** In this state all the requirements for reading the database in +** rollback (non-WAL) mode are met. Unless the pager is (or recently +** was) in exclusive-locking mode, a user-level read transaction is +** open. The database size is known in this state. +** +** A connection running with locking_mode=normal enters this state when +** it opens a read-transaction on the database and returns to state +** OPEN after the read-transaction is completed. However a connection +** running in locking_mode=exclusive (including temp databases) remains in +** this state even after the read-transaction is closed. The only way +** a locking_mode=exclusive connection can transition from READER to OPEN +** is via the ERROR state (see below). +** +** * A read transaction may be active (but a write-transaction cannot). +** * A SHARED or greater lock is held on the database file. +** * The dbSize variable may be trusted (even if a user-level read +** transaction is not active). The dbOrigSize and dbFileSize variables +** may not be trusted at this point. +** * If the database is a WAL database, then the WAL connection is open. +** * Even if a read-transaction is not open, it is guaranteed that +** there is no hot-journal in the file-system. +** +** WRITER_LOCKED: +** +** The pager moves to this state from READER when a write-transaction +** is first opened on the database. In WRITER_LOCKED state, all locks +** required to start a write-transaction are held, but no actual +** modifications to the cache or database have taken place. +** +** In rollback mode, a RESERVED or (if the transaction was opened with +** BEGIN EXCLUSIVE) EXCLUSIVE lock is obtained on the database file when +** moving to this state, but the journal file is not written to or opened +** to in this state. If the transaction is committed or rolled back while +** in WRITER_LOCKED state, all that is required is to unlock the database +** file. +** +** IN WAL mode, WalBeginWriteTransaction() is called to lock the log file. +** If the connection is running with locking_mode=exclusive, an attempt +** is made to obtain an EXCLUSIVE lock on the database file. +** +** * A write transaction is active. +** * If the connection is open in rollback-mode, a RESERVED or greater +** lock is held on the database file. +** * If the connection is open in WAL-mode, a WAL write transaction +** is open (i.e. sqlcipher_sqlite3WalBeginWriteTransaction() has been successfully +** called). +** * The dbSize, dbOrigSize and dbFileSize variables are all valid. +** * The contents of the pager cache have not been modified. +** * The journal file may or may not be open. +** * Nothing (not even the first header) has been written to the journal. +** +** WRITER_CACHEMOD: +** +** A pager moves from WRITER_LOCKED state to this state when a page is +** first modified by the upper layer. In rollback mode the journal file +** is opened (if it is not already open) and a header written to the +** start of it. The database file on disk has not been modified. +** +** * A write transaction is active. +** * A RESERVED or greater lock is held on the database file. +** * The journal file is open and the first header has been written +** to it, but the header has not been synced to disk. +** * The contents of the page cache have been modified. +** +** WRITER_DBMOD: +** +** The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state +** when it modifies the contents of the database file. WAL connections +** never enter this state (since they do not modify the database file, +** just the log file). +** +** * A write transaction is active. +** * An EXCLUSIVE or greater lock is held on the database file. +** * The journal file is open and the first header has been written +** and synced to disk. +** * The contents of the page cache have been modified (and possibly +** written to disk). +** +** WRITER_FINISHED: +** +** It is not possible for a WAL connection to enter this state. +** +** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD +** state after the entire transaction has been successfully written into the +** database file. In this state the transaction may be committed simply +** by finalizing the journal file. Once in WRITER_FINISHED state, it is +** not possible to modify the database further. At this point, the upper +** layer must either commit or rollback the transaction. +** +** * A write transaction is active. +** * An EXCLUSIVE or greater lock is held on the database file. +** * All writing and syncing of journal and database data has finished. +** If no error occurred, all that remains is to finalize the journal to +** commit the transaction. If an error did occur, the caller will need +** to rollback the transaction. +** +** ERROR: +** +** The ERROR state is entered when an IO or disk-full error (including +** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it +** difficult to be sure that the in-memory pager state (cache contents, +** db size etc.) are consistent with the contents of the file-system. +** +** Temporary pager files may enter the ERROR state, but in-memory pagers +** cannot. +** +** For example, if an IO error occurs while performing a rollback, +** the contents of the page-cache may be left in an inconsistent state. +** At this point it would be dangerous to change back to READER state +** (as usually happens after a rollback). Any subsequent readers might +** report database corruption (due to the inconsistent cache), and if +** they upgrade to writers, they may inadvertently corrupt the database +** file. To avoid this hazard, the pager switches into the ERROR state +** instead of READER following such an error. +** +** Once it has entered the ERROR state, any attempt to use the pager +** to read or write data returns an error. Eventually, once all +** outstanding transactions have been abandoned, the pager is able to +** transition back to OPEN state, discarding the contents of the +** page-cache and any other in-memory state at the same time. Everything +** is reloaded from disk (and, if necessary, hot-journal rollback peformed) +** when a read-transaction is next opened on the pager (transitioning +** the pager into READER state). At that point the system has recovered +** from the error. +** +** Specifically, the pager jumps into the ERROR state if: +** +** 1. An error occurs while attempting a rollback. This happens in +** function sqlcipher_sqlite3PagerRollback(). +** +** 2. An error occurs while attempting to finalize a journal file +** following a commit in function sqlcipher_sqlite3PagerCommitPhaseTwo(). +** +** 3. An error occurs while attempting to write to the journal or +** database file in function pagerStress() in order to free up +** memory. +** +** In other cases, the error is returned to the b-tree layer. The b-tree +** layer then attempts a rollback operation. If the error condition +** persists, the pager enters the ERROR state via condition (1) above. +** +** Condition (3) is necessary because it can be triggered by a read-only +** statement executed within a transaction. In this case, if the error +** code were simply returned to the user, the b-tree layer would not +** automatically attempt a rollback, as it assumes that an error in a +** read-only statement cannot leave the pager in an internally inconsistent +** state. +** +** * The Pager.errCode variable is set to something other than SQLITE_OK. +** * There are one or more outstanding references to pages (after the +** last reference is dropped the pager should move back to OPEN state). +** * The pager is not an in-memory pager. +** +** +** Notes: +** +** * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the +** connection is open in WAL mode. A WAL connection is always in one +** of the first four states. +** +** * Normally, a connection open in exclusive mode is never in PAGER_OPEN +** state. There are two exceptions: immediately after exclusive-mode has +** been turned on (and before any read or write transactions are +** executed), and when the pager is leaving the "error state". +** +** * See also: assert_pager_state(). +*/ +#define PAGER_OPEN 0 +#define PAGER_READER 1 +#define PAGER_WRITER_LOCKED 2 +#define PAGER_WRITER_CACHEMOD 3 +#define PAGER_WRITER_DBMOD 4 +#define PAGER_WRITER_FINISHED 5 +#define PAGER_ERROR 6 + +/* +** The Pager.eLock variable is almost always set to one of the +** following locking-states, according to the lock currently held on +** the database file: NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK. +** This variable is kept up to date as locks are taken and released by +** the pagerLockDb() and pagerUnlockDb() wrappers. +** +** If the VFS xLock() or xUnlock() returns an error other than SQLITE_BUSY +** (i.e. one of the SQLITE_IOERR subtypes), it is not clear whether or not +** the operation was successful. In these circumstances pagerLockDb() and +** pagerUnlockDb() take a conservative approach - eLock is always updated +** when unlocking the file, and only updated when locking the file if the +** VFS call is successful. This way, the Pager.eLock variable may be set +** to a less exclusive (lower) value than the lock that is actually held +** at the system level, but it is never set to a more exclusive value. +** +** This is usually safe. If an xUnlock fails or appears to fail, there may +** be a few redundant xLock() calls or a lock may be held for longer than +** required, but nothing really goes wrong. +** +** The exception is when the database file is unlocked as the pager moves +** from ERROR to OPEN state. At this point there may be a hot-journal file +** in the file-system that needs to be rolled back (as part of an OPEN->SHARED +** transition, by the same pager or any other). If the call to xUnlock() +** fails at this point and the pager is left holding an EXCLUSIVE lock, this +** can confuse the call to xCheckReservedLock() call made later as part +** of hot-journal detection. +** +** xCheckReservedLock() is defined as returning true "if there is a RESERVED +** lock held by this process or any others". So xCheckReservedLock may +** return true because the caller itself is holding an EXCLUSIVE lock (but +** doesn't know it because of a previous error in xUnlock). If this happens +** a hot-journal may be mistaken for a journal being created by an active +** transaction in another process, causing SQLite to read from the database +** without rolling it back. +** +** To work around this, if a call to xUnlock() fails when unlocking the +** database in the ERROR state, Pager.eLock is set to UNKNOWN_LOCK. It +** is only changed back to a real locking state after a successful call +** to xLock(EXCLUSIVE). Also, the code to do the OPEN->SHARED state transition +** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK +** lock. Instead, it assumes a hot-journal exists and obtains an EXCLUSIVE +** lock on the database file before attempting to roll it back. See function +** PagerSharedLock() for more detail. +** +** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in +** PAGER_OPEN state. +*/ +#define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1) + +/* +** A macro used for invoking the codec if there is one +*/ +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC +# define CODEC1(P,D,N,X,E) \ + if( P->xCodec && P->xCodec(P->pCodec,D,N,X)==0 ){ E; } +# define CODEC2(P,D,N,X,E,O) \ + if( P->xCodec==0 ){ O=(char*)D; }else \ + if( (O=(char*)(P->xCodec(P->pCodec,D,N,X)))==0 ){ E; } +#else +# define CODEC1(P,D,N,X,E) /* NO-OP */ +# define CODEC2(P,D,N,X,E,O) O=(char*)D #endif +/* END SQLCIPHER */ - /* If the second argument to this function is NULL, generate a - ** temporary file name to use - */ - if( !zUtf8Name ){ - assert( isDelete && !isOpenJournal ); - rc = winGetTempname(pVfs, &zTmpname); - if( rc!=SQLITE_OK ){ - OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlcipher_sqlite3ErrName(rc))); - return rc; - } - zUtf8Name = zTmpname; - } +/* +** The maximum allowed sector size. 64KiB. If the xSectorsize() method +** returns a value larger than this, then MAX_SECTOR_SIZE is used instead. +** This could conceivably cause corruption following a power failure on +** such a system. This is currently an undocumented limit. +*/ +#define MAX_SECTOR_SIZE 0x10000 - /* Database filenames are double-zero terminated if they are not - ** URIs with parameters. Hence, they can always be passed into - ** sqlcipher_sqlite3_uri_parameter(). - */ - assert( (eType!=SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) || - zUtf8Name[sqlcipher_sqlite3Strlen30(zUtf8Name)+1]==0 ); - /* Convert the filename to the system encoding. */ - zConverted = winConvertFromUtf8Filename(zUtf8Name); - if( zConverted==0 ){ - sqlcipher_sqlite3_free(zTmpname); - OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name)); - return SQLITE_IOERR_NOMEM_BKPT; - } +/* +** An instance of the following structure is allocated for each active +** savepoint and statement transaction in the system. All such structures +** are stored in the Pager.aSavepoint[] array, which is allocated and +** resized using sqlcipher_sqlite3Realloc(). +** +** When a savepoint is created, the PagerSavepoint.iHdrOffset field is +** set to 0. If a journal-header is written into the main journal while +** the savepoint is active, then iHdrOffset is set to the byte offset +** immediately following the last journal record written into the main +** journal before the journal-header. This is required during savepoint +** rollback (see pagerPlaybackSavepoint()). +*/ +typedef struct PagerSavepoint PagerSavepoint; +struct PagerSavepoint { + i64 iOffset; /* Starting offset in main journal */ + i64 iHdrOffset; /* See above */ + Bitvec *pInSavepoint; /* Set of pages in this savepoint */ + Pgno nOrig; /* Original number of pages in file */ + Pgno iSubRec; /* Index of first record in sub-journal */ + int bTruncateOnRelease; /* If stmt journal may be truncated on RELEASE */ +#ifndef SQLITE_OMIT_WAL + u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */ +#endif +}; - if( winIsDir(zConverted) ){ - sqlcipher_sqlite3_free(zConverted); - sqlcipher_sqlite3_free(zTmpname); - OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name)); - return SQLITE_CANTOPEN_ISDIR; - } +/* +** Bits of the Pager.doNotSpill flag. See further description below. +*/ +#define SPILLFLAG_OFF 0x01 /* Never spill cache. Set via pragma */ +#define SPILLFLAG_ROLLBACK 0x02 /* Current rolling back, so do not spill */ +#define SPILLFLAG_NOSYNC 0x04 /* Spill is ok, but do not sync */ - if( isReadWrite ){ - dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; - }else{ - dwDesiredAccess = GENERIC_READ; - } +/* +** An open page cache is an instance of struct Pager. A description of +** some of the more important member variables follows: +** +** eState +** +** The current 'state' of the pager object. See the comment and state +** diagram above for a description of the pager state. +** +** eLock +** +** For a real on-disk database, the current lock held on the database file - +** NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK. +** +** For a temporary or in-memory database (neither of which require any +** locks), this variable is always set to EXCLUSIVE_LOCK. Since such +** databases always have Pager.exclusiveMode==1, this tricks the pager +** logic into thinking that it already has all the locks it will ever +** need (and no reason to release them). +** +** In some (obscure) circumstances, this variable may also be set to +** UNKNOWN_LOCK. See the comment above the #define of UNKNOWN_LOCK for +** details. +** +** changeCountDone +** +** This boolean variable is used to make sure that the change-counter +** (the 4-byte header field at byte offset 24 of the database file) is +** not updated more often than necessary. +** +** It is set to true when the change-counter field is updated, which +** can only happen if an exclusive lock is held on the database file. +** It is cleared (set to false) whenever an exclusive lock is +** relinquished on the database file. Each time a transaction is committed, +** The changeCountDone flag is inspected. If it is true, the work of +** updating the change-counter is omitted for the current transaction. +** +** This mechanism means that when running in exclusive mode, a connection +** need only update the change-counter once, for the first transaction +** committed. +** +** setSuper +** +** When PagerCommitPhaseOne() is called to commit a transaction, it may +** (or may not) specify a super-journal name to be written into the +** journal file before it is synced to disk. +** +** Whether or not a journal file contains a super-journal pointer affects +** the way in which the journal file is finalized after the transaction is +** committed or rolled back when running in "journal_mode=PERSIST" mode. +** If a journal file does not contain a super-journal pointer, it is +** finalized by overwriting the first journal header with zeroes. If +** it does contain a super-journal pointer the journal file is finalized +** by truncating it to zero bytes, just as if the connection were +** running in "journal_mode=truncate" mode. +** +** Journal files that contain super-journal pointers cannot be finalized +** simply by overwriting the first journal-header with zeroes, as the +** super-journal pointer could interfere with hot-journal rollback of any +** subsequently interrupted transaction that reuses the journal file. +** +** The flag is cleared as soon as the journal file is finalized (either +** by PagerCommitPhaseTwo or PagerRollback). If an IO error prevents the +** journal file from being successfully finalized, the setSuper flag +** is cleared anyway (and the pager will move to ERROR state). +** +** doNotSpill +** +** This variables control the behavior of cache-spills (calls made by +** the pcache module to the pagerStress() routine to write cached data +** to the file-system in order to free up memory). +** +** When bits SPILLFLAG_OFF or SPILLFLAG_ROLLBACK of doNotSpill are set, +** writing to the database from pagerStress() is disabled altogether. +** The SPILLFLAG_ROLLBACK case is done in a very obscure case that +** comes up during savepoint rollback that requires the pcache module +** to allocate a new page to prevent the journal file from being written +** while it is being traversed by code in pager_playback(). The SPILLFLAG_OFF +** case is a user preference. +** +** If the SPILLFLAG_NOSYNC bit is set, writing to the database from +** pagerStress() is permitted, but syncing the journal file is not. +** This flag is set by sqlcipher_sqlite3PagerWrite() when the file-system sector-size +** is larger than the database page-size in order to prevent a journal sync +** from happening in between the journalling of two pages on the same sector. +** +** subjInMemory +** +** This is a boolean variable. If true, then any required sub-journal +** is opened as an in-memory journal file. If false, then in-memory +** sub-journals are only used for in-memory pager files. +** +** This variable is updated by the upper layer each time a new +** write-transaction is opened. +** +** dbSize, dbOrigSize, dbFileSize +** +** Variable dbSize is set to the number of pages in the database file. +** It is valid in PAGER_READER and higher states (all states except for +** OPEN and ERROR). +** +** dbSize is set based on the size of the database file, which may be +** larger than the size of the database (the value stored at offset +** 28 of the database header by the btree). If the size of the file +** is not an integer multiple of the page-size, the value stored in +** dbSize is rounded down (i.e. a 5KB file with 2K page-size has dbSize==2). +** Except, any file that is greater than 0 bytes in size is considered +** to have at least one page. (i.e. a 1KB file with 2K page-size leads +** to dbSize==1). +** +** During a write-transaction, if pages with page-numbers greater than +** dbSize are modified in the cache, dbSize is updated accordingly. +** Similarly, if the database is truncated using PagerTruncateImage(), +** dbSize is updated. +** +** Variables dbOrigSize and dbFileSize are valid in states +** PAGER_WRITER_LOCKED and higher. dbOrigSize is a copy of the dbSize +** variable at the start of the transaction. It is used during rollback, +** and to determine whether or not pages need to be journalled before +** being modified. +** +** Throughout a write-transaction, dbFileSize contains the size of +** the file on disk in pages. It is set to a copy of dbSize when the +** write-transaction is first opened, and updated when VFS calls are made +** to write or truncate the database file on disk. +** +** The only reason the dbFileSize variable is required is to suppress +** unnecessary calls to xTruncate() after committing a transaction. If, +** when a transaction is committed, the dbFileSize variable indicates +** that the database file is larger than the database image (Pager.dbSize), +** pager_truncate() is called. The pager_truncate() call uses xFilesize() +** to measure the database file on disk, and then truncates it if required. +** dbFileSize is not used when rolling back a transaction. In this case +** pager_truncate() is called unconditionally (which means there may be +** a call to xFilesize() that is not strictly required). In either case, +** pager_truncate() may cause the file to become smaller or larger. +** +** dbHintSize +** +** The dbHintSize variable is used to limit the number of calls made to +** the VFS xFileControl(FCNTL_SIZE_HINT) method. +** +** dbHintSize is set to a copy of the dbSize variable when a +** write-transaction is opened (at the same time as dbFileSize and +** dbOrigSize). If the xFileControl(FCNTL_SIZE_HINT) method is called, +** dbHintSize is increased to the number of pages that correspond to the +** size-hint passed to the method call. See pager_write_pagelist() for +** details. +** +** errCode +** +** The Pager.errCode variable is only ever used in PAGER_ERROR state. It +** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode +** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX +** sub-codes. +** +** syncFlags, walSyncFlags +** +** syncFlags is either SQLITE_SYNC_NORMAL (0x02) or SQLITE_SYNC_FULL (0x03). +** syncFlags is used for rollback mode. walSyncFlags is used for WAL mode +** and contains the flags used to sync the checkpoint operations in the +** lower two bits, and sync flags used for transaction commits in the WAL +** file in bits 0x04 and 0x08. In other words, to get the correct sync flags +** for checkpoint operations, use (walSyncFlags&0x03) and to get the correct +** sync flags for transaction commit, use ((walSyncFlags>>2)&0x03). Note +** that with synchronous=NORMAL in WAL mode, transaction commit is not synced +** meaning that the 0x04 and 0x08 bits are both zero. +*/ +struct Pager { + sqlcipher_sqlite3_vfs *pVfs; /* OS functions to use for IO */ + u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ + u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */ + u8 useJournal; /* Use a rollback journal on this file */ + u8 noSync; /* Do not sync the journal if true */ + u8 fullSync; /* Do extra syncs of the journal for robustness */ + u8 extraSync; /* sync directory after journal delete */ + u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */ + u8 walSyncFlags; /* See description above */ + u8 tempFile; /* zFilename is a temporary or immutable file */ + u8 noLock; /* Do not lock (except in WAL mode) */ + u8 readOnly; /* True for a read-only database */ + u8 memDb; /* True to inhibit all file I/O */ + u8 memVfs; /* VFS-implemented memory database */ - /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is - ** created. SQLite doesn't use it to indicate "exclusive access" - ** as it is usually understood. + /************************************************************************** + ** The following block contains those class members that change during + ** routine operation. Class members not in this block are either fixed + ** when the pager is first created or else only change when there is a + ** significant mode change (such as changing the page_size, locking_mode, + ** or the journal_mode). From another view, these class members describe + ** the "state" of the pager, while other class members describe the + ** "configuration" of the pager. */ - if( isExclusive ){ - /* Creates a new file, only if it does not already exist. */ - /* If the file exists, it fails. */ - dwCreationDisposition = CREATE_NEW; - }else if( isCreate ){ - /* Open existing file, or create if it doesn't exist */ - dwCreationDisposition = OPEN_ALWAYS; - }else{ - /* Opens a file, only if it exists. */ - dwCreationDisposition = OPEN_EXISTING; - } + u8 eState; /* Pager state (OPEN, READER, WRITER_LOCKED..) */ + u8 eLock; /* Current lock held on database file */ + u8 changeCountDone; /* Set after incrementing the change-counter */ + u8 setSuper; /* Super-jrnl name is written into jrnl */ + u8 doNotSpill; /* Do not spill the cache when non-zero */ + u8 subjInMemory; /* True to use in-memory sub-journals */ + u8 bUseFetch; /* True to use xFetch() */ + u8 hasHeldSharedLock; /* True if a shared lock has ever been held */ + Pgno dbSize; /* Number of pages in the database */ + Pgno dbOrigSize; /* dbSize before the current transaction */ + Pgno dbFileSize; /* Number of pages in the database file */ + Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */ + int errCode; /* One of several kinds of errors */ + int nRec; /* Pages journalled since last j-header written */ + u32 cksumInit; /* Quasi-random value added to every checksum */ + u32 nSubRec; /* Number of records written to sub-journal */ + Bitvec *pInJournal; /* One bit for each page in the database file */ + sqlcipher_sqlite3_file *fd; /* File descriptor for database */ + sqlcipher_sqlite3_file *jfd; /* File descriptor for main journal */ + sqlcipher_sqlite3_file *sjfd; /* File descriptor for sub-journal */ + i64 journalOff; /* Current write offset in the journal file */ + i64 journalHdr; /* Byte offset to previous journal header */ + sqlcipher_sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */ + PagerSavepoint *aSavepoint; /* Array of active savepoints */ + int nSavepoint; /* Number of elements in aSavepoint[] */ + u32 iDataVersion; /* Changes whenever database content changes */ + char dbFileVers[16]; /* Changes whenever database file changes */ - if( 0==sqlcipher_sqlite3_uri_boolean(zName, "exclusive", 0) ){ - dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; - }else{ - dwShareMode = 0; - } + int nMmapOut; /* Number of mmap pages currently outstanding */ + sqlcipher_sqlite3_int64 szMmap; /* Desired maximum mmap size */ + PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */ + /* + ** End of the routinely-changing class members + ***************************************************************************/ - if( isDelete ){ -#if SQLITE_OS_WINCE - dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN; - isTemp = 1; -#else - dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY - | FILE_ATTRIBUTE_HIDDEN - | FILE_FLAG_DELETE_ON_CLOSE; + u16 nExtra; /* Add this many bytes to each in-memory page */ + i16 nReserve; /* Number of unused bytes at end of each page */ + u32 vfsFlags; /* Flags for sqlcipher_sqlite3_vfs.xOpen() */ + u32 sectorSize; /* Assumed sector size during rollback */ + Pgno mxPgno; /* Maximum allowed size of the database */ + Pgno lckPgno; /* Page number for the locking page */ + i64 pageSize; /* Number of bytes in a page */ + i64 journalSizeLimit; /* Size limit for persistent journal files */ + char *zFilename; /* Name of the database file */ + char *zJournal; /* Name of the journal file */ + int (*xBusyHandler)(void*); /* Function to call when busy */ + void *pBusyHandlerArg; /* Context argument for xBusyHandler */ + int aStat[4]; /* Total cache hits, misses, writes, spills */ +#ifdef SQLITE_TEST + int nRead; /* Database pages read */ #endif - }else{ - dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; - } - /* Reports from the internet are that performance is always - ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */ -#if SQLITE_OS_WINCE - dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; + void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ + int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */ +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ + void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ + void (*xCodecFree)(void*); /* Destructor for the codec */ + void *pCodec; /* First argument to xCodec... methods */ +#endif +/* END SQLCIPHER */ + char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ + PCache *pPCache; /* Pointer to page cache object */ +#ifndef SQLITE_OMIT_WAL + Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */ + char *zWal; /* File name for write-ahead log */ #endif +}; - if( osIsNT() ){ -#if SQLITE_OS_WINRT - CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; - extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); - extendedParameters.dwFileAttributes = - dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK; - extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK; - extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; - extendedParameters.lpSecurityAttributes = NULL; - extendedParameters.hTemplateFile = NULL; - do{ - h = osCreateFile2((LPCWSTR)zConverted, - dwDesiredAccess, - dwShareMode, - dwCreationDisposition, - &extendedParameters); - if( h!=INVALID_HANDLE_VALUE ) break; - if( isReadWrite ){ - int rc2, isRO = 0; - sqlcipher_sqlite3BeginBenignMalloc(); - rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); - sqlcipher_sqlite3EndBenignMalloc(); - if( rc2==SQLITE_OK && isRO ) break; - } - }while( winRetryIoerr(&cnt, &lastErrno) ); +/* +** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains +** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS +** or CACHE_WRITE to sqlcipher_sqlite3_db_status(). +*/ +#define PAGER_STAT_HIT 0 +#define PAGER_STAT_MISS 1 +#define PAGER_STAT_WRITE 2 +#define PAGER_STAT_SPILL 3 + +/* +** The following global variables hold counters used for +** testing purposes only. These variables do not exist in +** a non-testing build. These variables are not thread-safe. +*/ +#ifdef SQLITE_TEST +SQLITE_API int sqlcipher_sqlite3_pager_readdb_count = 0; /* Number of full pages read from DB */ +SQLITE_API int sqlcipher_sqlite3_pager_writedb_count = 0; /* Number of full pages written to DB */ +SQLITE_API int sqlcipher_sqlite3_pager_writej_count = 0; /* Number of pages written to journal */ +# define PAGER_INCR(v) v++ #else - do{ - h = osCreateFileW((LPCWSTR)zConverted, - dwDesiredAccess, - dwShareMode, NULL, - dwCreationDisposition, - dwFlagsAndAttributes, - NULL); - if( h!=INVALID_HANDLE_VALUE ) break; - if( isReadWrite ){ - int rc2, isRO = 0; - sqlcipher_sqlite3BeginBenignMalloc(); - rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); - sqlcipher_sqlite3EndBenignMalloc(); - if( rc2==SQLITE_OK && isRO ) break; - } - }while( winRetryIoerr(&cnt, &lastErrno) ); -#endif - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - do{ - h = osCreateFileA((LPCSTR)zConverted, - dwDesiredAccess, - dwShareMode, NULL, - dwCreationDisposition, - dwFlagsAndAttributes, - NULL); - if( h!=INVALID_HANDLE_VALUE ) break; - if( isReadWrite ){ - int rc2, isRO = 0; - sqlcipher_sqlite3BeginBenignMalloc(); - rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); - sqlcipher_sqlite3EndBenignMalloc(); - if( rc2==SQLITE_OK && isRO ) break; - } - }while( winRetryIoerr(&cnt, &lastErrno) ); - } +# define PAGER_INCR(v) #endif - winLogIoerr(cnt, __LINE__); - OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name, - dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); - if( h==INVALID_HANDLE_VALUE ){ - sqlcipher_sqlite3_free(zConverted); - sqlcipher_sqlite3_free(zTmpname); - if( isReadWrite && !isExclusive ){ - return winOpen(pVfs, zName, id, - ((flags|SQLITE_OPEN_READONLY) & - ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), - pOutFlags); - }else{ - pFile->lastErrno = lastErrno; - winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name); - return SQLITE_CANTOPEN_BKPT; - } - } - if( pOutFlags ){ - if( isReadWrite ){ - *pOutFlags = SQLITE_OPEN_READWRITE; - }else{ - *pOutFlags = SQLITE_OPEN_READONLY; - } - } +/* +** Journal files begin with the following magic string. The data +** was obtained from /dev/random. It is used only as a sanity check. +** +** Since version 2.8.0, the journal format contains additional sanity +** checking information. If the power fails while the journal is being +** written, semi-random garbage data might appear in the journal +** file after power is restored. If an attempt is then made +** to roll the journal back, the database could be corrupted. The additional +** sanity checking data is an attempt to discover the garbage in the +** journal and ignore it. +** +** The sanity checking information for the new journal format consists +** of a 32-bit checksum on each page of data. The checksum covers both +** the page number and the pPager->pageSize bytes of data for the page. +** This cksum is initialized to a 32-bit random value that appears in the +** journal file right after the header. The random initializer is important, +** because garbage data that appears at the end of a journal is likely +** data that was once in other files that have now been deleted. If the +** garbage data came from an obsolete journal file, the checksums might +** be correct. But by initializing the checksum to random value which +** is different for every journal, we minimize that risk. +*/ +static const unsigned char aJournalMagic[] = { + 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7, +}; - OSTRACE(("OPEN file=%p, name=%s, access=%lx, pOutFlags=%p, *pOutFlags=%d, " - "rc=%s\n", h, zUtf8Name, dwDesiredAccess, pOutFlags, pOutFlags ? - *pOutFlags : 0, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); +/* +** The size of the of each page record in the journal is given by +** the following macro. +*/ +#define JOURNAL_PG_SZ(pPager) ((pPager->pageSize) + 8) - pAppData = (winVfsAppData*)pVfs->pAppData; +/* +** The journal header size for this pager. This is usually the same +** size as a single disk sector. See also setSectorSize(). +*/ +#define JOURNAL_HDR_SZ(pPager) (pPager->sectorSize) -#if SQLITE_OS_WINCE - { - if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB - && ((pAppData==NULL) || !pAppData->bNoLock) - && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK - ){ - osCloseHandle(h); - sqlcipher_sqlite3_free(zConverted); - sqlcipher_sqlite3_free(zTmpname); - OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlcipher_sqlite3ErrName(rc))); - return rc; - } - } - if( isTemp ){ - pFile->zDeleteOnClose = zConverted; - }else +/* +** The macro MEMDB is true if we are dealing with an in-memory database. +** We do this as a macro so that if the SQLITE_OMIT_MEMORYDB macro is set, +** the value of MEMDB will be a constant and the compiler will optimize +** out code that would never execute. +*/ +#ifdef SQLITE_OMIT_MEMORYDB +# define MEMDB 0 +#else +# define MEMDB pPager->memDb #endif - { - sqlcipher_sqlite3_free(zConverted); - } - sqlcipher_sqlite3_free(zTmpname); - id->pMethods = pAppData ? pAppData->pMethod : &winIoMethod; - pFile->pVfs = pVfs; - pFile->h = h; - if( isReadonly ){ - pFile->ctrlFlags |= WINFILE_RDONLY; - } - if( (flags & SQLITE_OPEN_MAIN_DB) - && sqlcipher_sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) - ){ - pFile->ctrlFlags |= WINFILE_PSOW; - } - pFile->lastErrno = NO_ERROR; - pFile->zPath = zName; +/* +** The macro USEFETCH is true if we are allowed to use the xFetch and xUnfetch +** interfaces to access the database using memory-mapped I/O. +*/ #if SQLITE_MAX_MMAP_SIZE>0 - pFile->hMap = NULL; - pFile->pMapRegion = 0; - pFile->mmapSize = 0; - pFile->mmapSizeMax = sqlcipher_sqlite3GlobalConfig.szMmap; +# define USEFETCH(x) ((x)->bUseFetch) +#else +# define USEFETCH(x) 0 #endif - OpenCounter(+1); - return rc; -} - /* -** Delete the named file. +** The argument to this macro is a file descriptor (type sqlcipher_sqlite3_file*). +** Return 0 if it is not open, or non-zero (but not 1) if it is. ** -** Note that Windows does not allow a file to be deleted if some other -** process has it open. Sometimes a virus scanner or indexing program -** will open a journal file shortly after it is created in order to do -** whatever it does. While this other process is holding the -** file open, we will be unable to delete it. To work around this -** problem, we delay 100 milliseconds and try to delete again. Up -** to MX_DELETION_ATTEMPTs deletion attempts are run before giving -** up and returning an error. +** This is so that expressions can be written as: +** +** if( isOpen(pPager->jfd) ){ ... +** +** instead of +** +** if( pPager->jfd->pMethods ){ ... */ -static int winDelete( - sqlcipher_sqlite3_vfs *pVfs, /* Not used on win32 */ - const char *zFilename, /* Name of file to delete */ - int syncDir /* Not used on win32 */ -){ - int cnt = 0; - int rc; - DWORD attr; - DWORD lastErrno = 0; - void *zConverted; - UNUSED_PARAMETER(pVfs); - UNUSED_PARAMETER(syncDir); - - SimulateIOError(return SQLITE_IOERR_DELETE); - OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir)); +#define isOpen(pFd) ((pFd)->pMethods!=0) - zConverted = winConvertFromUtf8Filename(zFilename); - if( zConverted==0 ){ - OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); - return SQLITE_IOERR_NOMEM_BKPT; - } - if( osIsNT() ){ - do { -#if SQLITE_OS_WINRT - WIN32_FILE_ATTRIBUTE_DATA sAttrData; - memset(&sAttrData, 0, sizeof(sAttrData)); - if ( osGetFileAttributesExW(zConverted, GetFileExInfoStandard, - &sAttrData) ){ - attr = sAttrData.dwFileAttributes; - }else{ - lastErrno = osGetLastError(); - if( lastErrno==ERROR_FILE_NOT_FOUND - || lastErrno==ERROR_PATH_NOT_FOUND ){ - rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ - }else{ - rc = SQLITE_ERROR; - } - break; - } -#else - attr = osGetFileAttributesW(zConverted); +#ifdef SQLITE_DIRECT_OVERFLOW_READ +/* +** Return true if page pgno can be read directly from the database file +** by the b-tree layer. This is the case if: +** +** * the database file is open, +** * there are no dirty pages in the cache, and +** * the desired page is not currently in the wal file. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ + if( pPager->fd->pMethods==0 ) return 0; + if( sqlcipher_sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + if( pPager->xCodec!=0 ) return 0; #endif - if ( attr==INVALID_FILE_ATTRIBUTES ){ - lastErrno = osGetLastError(); - if( lastErrno==ERROR_FILE_NOT_FOUND - || lastErrno==ERROR_PATH_NOT_FOUND ){ - rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ - }else{ - rc = SQLITE_ERROR; - } - break; - } - if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ - rc = SQLITE_ERROR; /* Files only. */ - break; - } - if ( osDeleteFileW(zConverted) ){ - rc = SQLITE_OK; /* Deleted OK. */ - break; - } - if ( !winRetryIoerr(&cnt, &lastErrno) ){ - rc = SQLITE_ERROR; /* No more retries. */ - break; - } - } while(1); - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - do { - attr = osGetFileAttributesA(zConverted); - if ( attr==INVALID_FILE_ATTRIBUTES ){ - lastErrno = osGetLastError(); - if( lastErrno==ERROR_FILE_NOT_FOUND - || lastErrno==ERROR_PATH_NOT_FOUND ){ - rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ - }else{ - rc = SQLITE_ERROR; - } - break; - } - if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ - rc = SQLITE_ERROR; /* Files only. */ - break; - } - if ( osDeleteFileA(zConverted) ){ - rc = SQLITE_OK; /* Deleted OK. */ - break; - } - if ( !winRetryIoerr(&cnt, &lastErrno) ){ - rc = SQLITE_ERROR; /* No more retries. */ - break; - } - } while(1); +/* END SQLCIPHER */ +#ifndef SQLITE_OMIT_WAL + if( pPager->pWal ){ + u32 iRead = 0; + int rc; + rc = sqlcipher_sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); + return (rc==SQLITE_OK && iRead==0); } #endif - if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){ - rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename); - }else{ - winLogIoerr(cnt, __LINE__); - } - sqlcipher_sqlite3_free(zConverted); - OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlcipher_sqlite3ErrName(rc))); - return rc; + return 1; } +#endif -/* -** Check the existence and status of a file. -*/ -static int winAccess( - sqlcipher_sqlite3_vfs *pVfs, /* Not used on win32 */ - const char *zFilename, /* Name of file to check */ - int flags, /* Type of test to make on this file */ - int *pResOut /* OUT: Result */ -){ - DWORD attr; - int rc = 0; - DWORD lastErrno = 0; - void *zConverted; - UNUSED_PARAMETER(pVfs); - - SimulateIOError( return SQLITE_IOERR_ACCESS; ); - OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", - zFilename, flags, pResOut)); - - zConverted = winConvertFromUtf8Filename(zFilename); - if( zConverted==0 ){ - OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); - return SQLITE_IOERR_NOMEM_BKPT; - } - if( osIsNT() ){ - int cnt = 0; - WIN32_FILE_ATTRIBUTE_DATA sAttrData; - memset(&sAttrData, 0, sizeof(sAttrData)); - while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, - GetFileExInfoStandard, - &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} - if( rc ){ - /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file - ** as if it does not exist. - */ - if( flags==SQLITE_ACCESS_EXISTS - && sAttrData.nFileSizeHigh==0 - && sAttrData.nFileSizeLow==0 ){ - attr = INVALID_FILE_ATTRIBUTES; - }else{ - attr = sAttrData.dwFileAttributes; - } - }else{ - winLogIoerr(cnt, __LINE__); - if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ - sqlcipher_sqlite3_free(zConverted); - return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", - zFilename); - }else{ - attr = INVALID_FILE_ATTRIBUTES; - } - } - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - attr = osGetFileAttributesA((char*)zConverted); - } +#ifndef SQLITE_OMIT_WAL +# define pagerUseWal(x) ((x)->pWal!=0) +#else +# define pagerUseWal(x) 0 +# define pagerRollbackWal(x) 0 +# define pagerWalFrames(v,w,x,y) 0 +# define pagerOpenWalIfPresent(z) SQLITE_OK +# define pagerBeginReadTransaction(z) SQLITE_OK #endif - sqlcipher_sqlite3_free(zConverted); - switch( flags ){ - case SQLITE_ACCESS_READ: - case SQLITE_ACCESS_EXISTS: - rc = attr!=INVALID_FILE_ATTRIBUTES; - break; - case SQLITE_ACCESS_READWRITE: - rc = attr!=INVALID_FILE_ATTRIBUTES && - (attr & FILE_ATTRIBUTE_READONLY)==0; - break; - default: - assert(!"Invalid flags argument"); - } - *pResOut = rc; - OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", - zFilename, pResOut, *pResOut)); - return SQLITE_OK; -} +#ifndef NDEBUG /* -** Returns non-zero if the specified path name starts with the "long path" -** prefix. +** Usage: +** +** assert( assert_pager_state(pPager) ); +** +** This function runs many asserts to try to find inconsistencies in +** the internal state of the Pager object. */ -static BOOL winIsLongPathPrefix( - const char *zPathname -){ - return ( zPathname[0]=='\\' && zPathname[1]=='\\' - && zPathname[2]=='?' && zPathname[3]=='\\' ); -} +static int assert_pager_state(Pager *p){ + Pager *pPager = p; -/* -** Returns non-zero if the specified path name starts with a drive letter -** followed by a colon character. -*/ -static BOOL winIsDriveLetterAndColon( - const char *zPathname -){ - return ( sqlcipher_sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' ); -} + /* State must be valid. */ + assert( p->eState==PAGER_OPEN + || p->eState==PAGER_READER + || p->eState==PAGER_WRITER_LOCKED + || p->eState==PAGER_WRITER_CACHEMOD + || p->eState==PAGER_WRITER_DBMOD + || p->eState==PAGER_WRITER_FINISHED + || p->eState==PAGER_ERROR + ); -/* -** Returns non-zero if the specified path name should be used verbatim. If -** non-zero is returned from this function, the calling function must simply -** use the provided path name verbatim -OR- resolve it into a full path name -** using the GetFullPathName Win32 API function (if available). -*/ -static BOOL winIsVerbatimPathname( - const char *zPathname -){ - /* - ** If the path name starts with a forward slash or a backslash, it is either - ** a legal UNC name, a volume relative path, or an absolute path name in the - ** "Unix" format on Windows. There is no easy way to differentiate between - ** the final two cases; therefore, we return the safer return value of TRUE - ** so that callers of this function will simply use it verbatim. + /* Regardless of the current state, a temp-file connection always behaves + ** as if it has an exclusive lock on the database file. It never updates + ** the change-counter field, so the changeCountDone flag is always set. */ - if ( winIsDirSep(zPathname[0]) ){ - return TRUE; - } + assert( p->tempFile==0 || p->eLock==EXCLUSIVE_LOCK ); + assert( p->tempFile==0 || pPager->changeCountDone ); - /* - ** If the path name starts with a letter and a colon it is either a volume - ** relative path or an absolute path. Callers of this function must not - ** attempt to treat it as a relative path name (i.e. they should simply use - ** it verbatim). + /* If the useJournal flag is clear, the journal-mode must be "OFF". + ** And if the journal-mode is "OFF", the journal file must not be open. */ - if ( winIsDriveLetterAndColon(zPathname) ){ - return TRUE; + assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal ); + assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) ); + + /* Check that MEMDB implies noSync. And an in-memory journal. Since + ** this means an in-memory pager performs no IO at all, it cannot encounter + ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing + ** a journal file. (although the in-memory journal implementation may + ** return SQLITE_IOERR_NOMEM while the journal file is being written). It + ** is therefore not possible for an in-memory pager to enter the ERROR + ** state. + */ + if( MEMDB ){ + assert( !isOpen(p->fd) ); + assert( p->noSync ); + assert( p->journalMode==PAGER_JOURNALMODE_OFF + || p->journalMode==PAGER_JOURNALMODE_MEMORY + ); + assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN ); + assert( pagerUseWal(p)==0 ); } - /* - ** If we get to this point, the path name should almost certainly be a purely - ** relative one (i.e. not a UNC name, not absolute, and not volume relative). + /* If changeCountDone is set, a RESERVED lock or greater must be held + ** on the file. */ - return FALSE; -} + assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK ); + assert( p->eLock!=PENDING_LOCK ); -/* -** Turn a relative pathname into a full pathname. Write the full -** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname -** bytes in size. -*/ -static int winFullPathname( - sqlcipher_sqlite3_vfs *pVfs, /* Pointer to vfs object */ - const char *zRelative, /* Possibly relative input path */ - int nFull, /* Size of output buffer in bytes */ - char *zFull /* Output buffer */ -){ -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) - DWORD nByte; - void *zConverted; - char *zOut; -#endif + switch( p->eState ){ + case PAGER_OPEN: + assert( !MEMDB ); + assert( pPager->errCode==SQLITE_OK ); + assert( sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)==0 || pPager->tempFile ); + break; - /* If this path name begins with "/X:" or "\\?\", where "X" is any - ** alphabetic character, discard the initial "/" from the pathname. - */ - if( zRelative[0]=='/' && (winIsDriveLetterAndColon(zRelative+1) - || winIsLongPathPrefix(zRelative+1)) ){ - zRelative++; - } + case PAGER_READER: + assert( pPager->errCode==SQLITE_OK ); + assert( p->eLock!=UNKNOWN_LOCK ); + assert( p->eLock>=SHARED_LOCK ); + break; -#if defined(__CYGWIN__) - SimulateIOError( return SQLITE_ERROR ); - UNUSED_PARAMETER(nFull); - assert( nFull>=pVfs->mxPathname ); - if ( sqlcipher_sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ - /* - ** NOTE: We are dealing with a relative path name and the data - ** directory has been set. Therefore, use it as the basis - ** for converting the relative path name to an absolute - ** one by prepending the data directory and a slash. - */ - char *zOut = sqlcipher_sqlite3MallocZero( pVfs->mxPathname+1 ); - if( !zOut ){ - return SQLITE_IOERR_NOMEM_BKPT; - } - if( cygwin_conv_path( - (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) | - CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){ - sqlcipher_sqlite3_free(zOut); - return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, - "winFullPathname1", zRelative); - }else{ - char *zUtf8 = winConvertToUtf8Filename(zOut); - if( !zUtf8 ){ - sqlcipher_sqlite3_free(zOut); - return SQLITE_IOERR_NOMEM_BKPT; + case PAGER_WRITER_LOCKED: + assert( p->eLock!=UNKNOWN_LOCK ); + assert( pPager->errCode==SQLITE_OK ); + if( !pagerUseWal(pPager) ){ + assert( p->eLock>=RESERVED_LOCK ); } - sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", - sqlcipher_sqlite3_data_directory, winGetDirSep(), zUtf8); - sqlcipher_sqlite3_free(zUtf8); - sqlcipher_sqlite3_free(zOut); - } - }else{ - char *zOut = sqlcipher_sqlite3MallocZero( pVfs->mxPathname+1 ); - if( !zOut ){ - return SQLITE_IOERR_NOMEM_BKPT; - } - if( cygwin_conv_path( - (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A), - zRelative, zOut, pVfs->mxPathname+1)<0 ){ - sqlcipher_sqlite3_free(zOut); - return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, - "winFullPathname2", zRelative); - }else{ - char *zUtf8 = winConvertToUtf8Filename(zOut); - if( !zUtf8 ){ - sqlcipher_sqlite3_free(zOut); - return SQLITE_IOERR_NOMEM_BKPT; + assert( pPager->dbSize==pPager->dbOrigSize ); + assert( pPager->dbOrigSize==pPager->dbFileSize ); + assert( pPager->dbOrigSize==pPager->dbHintSize ); + assert( pPager->setSuper==0 ); + break; + + case PAGER_WRITER_CACHEMOD: + assert( p->eLock!=UNKNOWN_LOCK ); + assert( pPager->errCode==SQLITE_OK ); + if( !pagerUseWal(pPager) ){ + /* It is possible that if journal_mode=wal here that neither the + ** journal file nor the WAL file are open. This happens during + ** a rollback transaction that switches from journal_mode=off + ** to journal_mode=wal. + */ + assert( p->eLock>=RESERVED_LOCK ); + assert( isOpen(p->jfd) + || p->journalMode==PAGER_JOURNALMODE_OFF + || p->journalMode==PAGER_JOURNALMODE_WAL + ); } - sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8); - sqlcipher_sqlite3_free(zUtf8); - sqlcipher_sqlite3_free(zOut); - } + assert( pPager->dbOrigSize==pPager->dbFileSize ); + assert( pPager->dbOrigSize==pPager->dbHintSize ); + break; + + case PAGER_WRITER_DBMOD: + assert( p->eLock==EXCLUSIVE_LOCK ); + assert( pPager->errCode==SQLITE_OK ); + assert( !pagerUseWal(pPager) ); + assert( p->eLock>=EXCLUSIVE_LOCK ); + assert( isOpen(p->jfd) + || p->journalMode==PAGER_JOURNALMODE_OFF + || p->journalMode==PAGER_JOURNALMODE_WAL + || (sqlcipher_sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC) + ); + assert( pPager->dbOrigSize<=pPager->dbHintSize ); + break; + + case PAGER_WRITER_FINISHED: + assert( p->eLock==EXCLUSIVE_LOCK ); + assert( pPager->errCode==SQLITE_OK ); + assert( !pagerUseWal(pPager) ); + assert( isOpen(p->jfd) + || p->journalMode==PAGER_JOURNALMODE_OFF + || p->journalMode==PAGER_JOURNALMODE_WAL + || (sqlcipher_sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC) + ); + break; + + case PAGER_ERROR: + /* There must be at least one outstanding reference to the pager if + ** in ERROR state. Otherwise the pager should have already dropped + ** back to OPEN state. + */ + assert( pPager->errCode!=SQLITE_OK ); + assert( sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)>0 || pPager->tempFile ); + break; } - return SQLITE_OK; + + return 1; +} +#endif /* ifndef NDEBUG */ + +#ifdef SQLITE_DEBUG +/* +** Return a pointer to a human readable string in a static buffer +** containing the state of the Pager object passed as an argument. This +** is intended to be used within debuggers. For example, as an alternative +** to "print *pPager" in gdb: +** +** (gdb) printf "%s", print_pager_state(pPager) +** +** This routine has external linkage in order to suppress compiler warnings +** about an unused function. It is enclosed within SQLITE_DEBUG and so does +** not appear in normal builds. +*/ +char *print_pager_state(Pager *p){ + static char zRet[1024]; + + sqlcipher_sqlite3_snprintf(1024, zRet, + "Filename: %s\n" + "State: %s errCode=%d\n" + "Lock: %s\n" + "Locking mode: locking_mode=%s\n" + "Journal mode: journal_mode=%s\n" + "Backing store: tempFile=%d memDb=%d useJournal=%d\n" + "Journal: journalOff=%lld journalHdr=%lld\n" + "Size: dbsize=%d dbOrigSize=%d dbFileSize=%d\n" + , p->zFilename + , p->eState==PAGER_OPEN ? "OPEN" : + p->eState==PAGER_READER ? "READER" : + p->eState==PAGER_WRITER_LOCKED ? "WRITER_LOCKED" : + p->eState==PAGER_WRITER_CACHEMOD ? "WRITER_CACHEMOD" : + p->eState==PAGER_WRITER_DBMOD ? "WRITER_DBMOD" : + p->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" : + p->eState==PAGER_ERROR ? "ERROR" : "?error?" + , (int)p->errCode + , p->eLock==NO_LOCK ? "NO_LOCK" : + p->eLock==RESERVED_LOCK ? "RESERVED" : + p->eLock==EXCLUSIVE_LOCK ? "EXCLUSIVE" : + p->eLock==SHARED_LOCK ? "SHARED" : + p->eLock==UNKNOWN_LOCK ? "UNKNOWN" : "?error?" + , p->exclusiveMode ? "exclusive" : "normal" + , p->journalMode==PAGER_JOURNALMODE_MEMORY ? "memory" : + p->journalMode==PAGER_JOURNALMODE_OFF ? "off" : + p->journalMode==PAGER_JOURNALMODE_DELETE ? "delete" : + p->journalMode==PAGER_JOURNALMODE_PERSIST ? "persist" : + p->journalMode==PAGER_JOURNALMODE_TRUNCATE ? "truncate" : + p->journalMode==PAGER_JOURNALMODE_WAL ? "wal" : "?error?" + , (int)p->tempFile, (int)p->memDb, (int)p->useJournal + , p->journalOff, p->journalHdr + , (int)p->dbSize, (int)p->dbOrigSize, (int)p->dbFileSize + ); + + return zRet; +} #endif -#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__) - SimulateIOError( return SQLITE_ERROR ); - /* WinCE has no concept of a relative pathname, or so I am told. */ - /* WinRT has no way to convert a relative path to an absolute one. */ - if ( sqlcipher_sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ - /* - ** NOTE: We are dealing with a relative path name and the data - ** directory has been set. Therefore, use it as the basis - ** for converting the relative path name to an absolute - ** one by prepending the data directory and a backslash. - */ - sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", - sqlcipher_sqlite3_data_directory, winGetDirSep(), zRelative); - }else{ - sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative); - } - return SQLITE_OK; +/* Forward references to the various page getters */ +static int getPageNormal(Pager*,Pgno,DbPage**,int); +static int getPageError(Pager*,Pgno,DbPage**,int); +#if SQLITE_MAX_MMAP_SIZE>0 +static int getPageMMap(Pager*,Pgno,DbPage**,int); #endif -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. This function could fail if, for example, the - ** current working directory has been unlinked. - */ - SimulateIOError( return SQLITE_ERROR ); - if ( sqlcipher_sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ - /* - ** NOTE: We are dealing with a relative path name and the data - ** directory has been set. Therefore, use it as the basis - ** for converting the relative path name to an absolute - ** one by prepending the data directory and a backslash. - */ - sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", - sqlcipher_sqlite3_data_directory, winGetDirSep(), zRelative); - return SQLITE_OK; - } - zConverted = winConvertFromUtf8Filename(zRelative); - if( zConverted==0 ){ - return SQLITE_IOERR_NOMEM_BKPT; - } - if( osIsNT() ){ - LPWSTR zTemp; - nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0); - if( nByte==0 ){ - sqlcipher_sqlite3_free(zConverted); - return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), - "winFullPathname1", zRelative); - } - nByte += 3; - zTemp = sqlcipher_sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); - if( zTemp==0 ){ - sqlcipher_sqlite3_free(zConverted); - return SQLITE_IOERR_NOMEM_BKPT; - } - nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0); - if( nByte==0 ){ - sqlcipher_sqlite3_free(zConverted); - sqlcipher_sqlite3_free(zTemp); - return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), - "winFullPathname2", zRelative); - } - sqlcipher_sqlite3_free(zConverted); - zOut = winUnicodeToUtf8(zTemp); - sqlcipher_sqlite3_free(zTemp); - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - char *zTemp; - nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0); - if( nByte==0 ){ - sqlcipher_sqlite3_free(zConverted); - return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), - "winFullPathname3", zRelative); - } - nByte += 3; - zTemp = sqlcipher_sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); - if( zTemp==0 ){ - sqlcipher_sqlite3_free(zConverted); - return SQLITE_IOERR_NOMEM_BKPT; - } - nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0); - if( nByte==0 ){ - sqlcipher_sqlite3_free(zConverted); - sqlcipher_sqlite3_free(zTemp); - return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), - "winFullPathname4", zRelative); - } - sqlcipher_sqlite3_free(zConverted); - zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI()); - sqlcipher_sqlite3_free(zTemp); - } +/* +** Set the Pager.xGet method for the appropriate routine used to fetch +** content from the pager. +*/ +static void setGetterMethod(Pager *pPager){ + if( pPager->errCode ){ + pPager->xGet = getPageError; +#if SQLITE_MAX_MMAP_SIZE>0 + }else if( USEFETCH(pPager) +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + && pPager->xCodec==0 #endif - if( zOut ){ - sqlcipher_sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut); - sqlcipher_sqlite3_free(zOut); - return SQLITE_OK; +/* END SQLCIPHER */ + ){ + pPager->xGet = getPageMMap; +#endif /* SQLITE_MAX_MMAP_SIZE>0 */ }else{ - return SQLITE_IOERR_NOMEM_BKPT; + pPager->xGet = getPageNormal; } -#endif } -#ifndef SQLITE_OMIT_LOAD_EXTENSION /* -** Interfaces for opening a shared library, finding entry points -** within the shared library, and closing the shared library. +** Return true if it is necessary to write page *pPg into the sub-journal. +** A page needs to be written into the sub-journal if there exists one +** or more open savepoints for which: +** +** * The page-number is less than or equal to PagerSavepoint.nOrig, and +** * The bit corresponding to the page-number is not set in +** PagerSavepoint.pInSavepoint. */ -static void *winDlOpen(sqlcipher_sqlite3_vfs *pVfs, const char *zFilename){ - HANDLE h; -#if defined(__CYGWIN__) - int nFull = pVfs->mxPathname+1; - char *zFull = sqlcipher_sqlite3MallocZero( nFull ); - void *zConverted = 0; - if( zFull==0 ){ - OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); - return 0; - } - if( winFullPathname(pVfs, zFilename, nFull, zFull)!=SQLITE_OK ){ - sqlcipher_sqlite3_free(zFull); - OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); - return 0; - } - zConverted = winConvertFromUtf8Filename(zFull); - sqlcipher_sqlite3_free(zFull); -#else - void *zConverted = winConvertFromUtf8Filename(zFilename); - UNUSED_PARAMETER(pVfs); -#endif - if( zConverted==0 ){ - OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); - return 0; - } - if( osIsNT() ){ -#if SQLITE_OS_WINRT - h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0); -#else - h = osLoadLibraryW((LPCWSTR)zConverted); -#endif - } -#ifdef SQLITE_WIN32_HAS_ANSI - else{ - h = osLoadLibraryA((char*)zConverted); +static int subjRequiresPage(PgHdr *pPg){ + Pager *pPager = pPg->pPager; + PagerSavepoint *p; + Pgno pgno = pPg->pgno; + int i; + for(i=0; inSavepoint; i++){ + p = &pPager->aSavepoint[i]; + if( p->nOrig>=pgno && 0==sqlcipher_sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){ + for(i=i+1; inSavepoint; i++){ + pPager->aSavepoint[i].bTruncateOnRelease = 0; + } + return 1; + } } -#endif - OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)h)); - sqlcipher_sqlite3_free(zConverted); - return (void*)h; -} -static void winDlError(sqlcipher_sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ - UNUSED_PARAMETER(pVfs); - winGetLastErrorMsg(osGetLastError(), nBuf, zBufOut); -} -static void (*winDlSym(sqlcipher_sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){ - FARPROC proc; - UNUSED_PARAMETER(pVfs); - proc = osGetProcAddressA((HANDLE)pH, zSym); - OSTRACE(("DLSYM handle=%p, symbol=%s, address=%p\n", - (void*)pH, zSym, (void*)proc)); - return (void(*)(void))proc; -} -static void winDlClose(sqlcipher_sqlite3_vfs *pVfs, void *pHandle){ - UNUSED_PARAMETER(pVfs); - osFreeLibrary((HANDLE)pHandle); - OSTRACE(("DLCLOSE handle=%p\n", (void*)pHandle)); + return 0; } -#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ - #define winDlOpen 0 - #define winDlError 0 - #define winDlSym 0 - #define winDlClose 0 -#endif -/* State information for the randomness gatherer. */ -typedef struct EntropyGatherer EntropyGatherer; -struct EntropyGatherer { - unsigned char *a; /* Gather entropy into this buffer */ - int na; /* Size of a[] in bytes */ - int i; /* XOR next input into a[i] */ - int nXor; /* Number of XOR operations done */ -}; - -#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) -/* Mix sz bytes of entropy into p. */ -static void xorMemory(EntropyGatherer *p, unsigned char *x, int sz){ - int j, k; - for(j=0, k=p->i; ja[k++] ^= x[j]; - if( k>=p->na ) k = 0; - } - p->i = k; - p->nXor += sz; +#ifdef SQLITE_DEBUG +/* +** Return true if the page is already in the journal file. +*/ +static int pageInJournal(Pager *pPager, PgHdr *pPg){ + return sqlcipher_sqlite3BitvecTest(pPager->pInJournal, pPg->pgno); } -#endif /* !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) */ +#endif /* -** Write up to nBuf bytes of randomness into zBuf. +** Read a 32-bit integer from the given file descriptor. Store the integer +** that is read in *pRes. Return SQLITE_OK if everything worked, or an +** error code is something goes wrong. +** +** All values are stored on disk as big-endian. */ -static int winRandomness(sqlcipher_sqlite3_vfs *pVfs, int nBuf, char *zBuf){ -#if defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) - UNUSED_PARAMETER(pVfs); - memset(zBuf, 0, nBuf); - return nBuf; -#else - EntropyGatherer e; - UNUSED_PARAMETER(pVfs); - memset(zBuf, 0, nBuf); - e.a = (unsigned char*)zBuf; - e.na = nBuf; - e.nXor = 0; - e.i = 0; - { - SYSTEMTIME x; - osGetSystemTime(&x); - xorMemory(&e, (unsigned char*)&x, sizeof(SYSTEMTIME)); - } - { - DWORD pid = osGetCurrentProcessId(); - xorMemory(&e, (unsigned char*)&pid, sizeof(DWORD)); - } -#if SQLITE_OS_WINRT - { - ULONGLONG cnt = osGetTickCount64(); - xorMemory(&e, (unsigned char*)&cnt, sizeof(ULONGLONG)); - } -#else - { - DWORD cnt = osGetTickCount(); - xorMemory(&e, (unsigned char*)&cnt, sizeof(DWORD)); - } -#endif /* SQLITE_OS_WINRT */ - { - LARGE_INTEGER i; - osQueryPerformanceCounter(&i); - xorMemory(&e, (unsigned char*)&i, sizeof(LARGE_INTEGER)); - } -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID - { - UUID id; - memset(&id, 0, sizeof(UUID)); - osUuidCreate(&id); - xorMemory(&e, (unsigned char*)&id, sizeof(UUID)); - memset(&id, 0, sizeof(UUID)); - osUuidCreateSequential(&id); - xorMemory(&e, (unsigned char*)&id, sizeof(UUID)); +static int read32bits(sqlcipher_sqlite3_file *fd, i64 offset, u32 *pRes){ + unsigned char ac[4]; + int rc = sqlcipher_sqlite3OsRead(fd, ac, sizeof(ac), offset); + if( rc==SQLITE_OK ){ + *pRes = sqlcipher_sqlite3Get4byte(ac); } -#endif /* !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID */ - return e.nXor>nBuf ? nBuf : e.nXor; -#endif /* defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) */ + return rc; } - /* -** Sleep for a little while. Return the amount of time slept. +** Write a 32-bit integer into a string buffer in big-endian byte order. */ -static int winSleep(sqlcipher_sqlite3_vfs *pVfs, int microsec){ - sqlcipher_sqlite3_win32_sleep((microsec+999)/1000); - UNUSED_PARAMETER(pVfs); - return ((microsec+999)/1000)*1000; -} +#define put32bits(A,B) sqlcipher_sqlite3Put4byte((u8*)A,B) + /* -** The following variable, if set to a non-zero value, is interpreted as -** the number of seconds since 1970 and is used to set the result of -** sqlcipher_sqlite3OsCurrentTime() during testing. +** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK +** on success or an error code is something goes wrong. */ -#ifdef SQLITE_TEST -SQLITE_API int sqlcipher_sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ -#endif +static int write32bits(sqlcipher_sqlite3_file *fd, i64 offset, u32 val){ + char ac[4]; + put32bits(ac, val); + return sqlcipher_sqlite3OsWrite(fd, ac, 4, offset); +} /* -** Find the current time (in Universal Coordinated Time). Write into *piNow -** the current time and date as a Julian Day number times 86_400_000. In -** other words, write into *piNow the number of milliseconds since the Julian -** epoch of noon in Greenwich on November 24, 4714 B.C according to the -** proleptic Gregorian calendar. +** Unlock the database file to level eLock, which must be either NO_LOCK +** or SHARED_LOCK. Regardless of whether or not the call to xUnlock() +** succeeds, set the Pager.eLock variable to match the (attempted) new lock. ** -** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date -** cannot be found. +** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is +** called, do not modify it. See the comment above the #define of +** UNKNOWN_LOCK for an explanation of this. */ -static int winCurrentTimeInt64(sqlcipher_sqlite3_vfs *pVfs, sqlcipher_sqlite3_int64 *piNow){ - /* FILETIME structure is a 64-bit value representing the number of - 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). - */ - FILETIME ft; - static const sqlcipher_sqlite3_int64 winFiletimeEpoch = 23058135*(sqlcipher_sqlite3_int64)8640000; -#ifdef SQLITE_TEST - static const sqlcipher_sqlite3_int64 unixEpoch = 24405875*(sqlcipher_sqlite3_int64)8640000; -#endif - /* 2^32 - to avoid use of LL and warnings in gcc */ - static const sqlcipher_sqlite3_int64 max32BitValue = - (sqlcipher_sqlite3_int64)2000000000 + (sqlcipher_sqlite3_int64)2000000000 + - (sqlcipher_sqlite3_int64)294967296; - -#if SQLITE_OS_WINCE - SYSTEMTIME time; - osGetSystemTime(&time); - /* if SystemTimeToFileTime() fails, it returns zero. */ - if (!osSystemTimeToFileTime(&time,&ft)){ - return SQLITE_ERROR; - } -#else - osGetSystemTimeAsFileTime( &ft ); -#endif - - *piNow = winFiletimeEpoch + - ((((sqlcipher_sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + - (sqlcipher_sqlite3_int64)ft.dwLowDateTime)/(sqlcipher_sqlite3_int64)10000; +static int pagerUnlockDb(Pager *pPager, int eLock){ + int rc = SQLITE_OK; -#ifdef SQLITE_TEST - if( sqlcipher_sqlite3_current_time ){ - *piNow = 1000*(sqlcipher_sqlite3_int64)sqlcipher_sqlite3_current_time + unixEpoch; + assert( !pPager->exclusiveMode || pPager->eLock==eLock ); + assert( eLock==NO_LOCK || eLock==SHARED_LOCK ); + assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); + if( isOpen(pPager->fd) ){ + assert( pPager->eLock>=eLock ); + rc = pPager->noLock ? SQLITE_OK : sqlcipher_sqlite3OsUnlock(pPager->fd, eLock); + if( pPager->eLock!=UNKNOWN_LOCK ){ + pPager->eLock = (u8)eLock; + } + IOTRACE(("UNLOCK %p %d\n", pPager, eLock)) } -#endif - UNUSED_PARAMETER(pVfs); - return SQLITE_OK; + pPager->changeCountDone = pPager->tempFile; /* ticket fb3b3024ea238d5c */ + return rc; } /* -** Find the current time (in Universal Coordinated Time). Write the -** current time and date as a Julian Day number into *prNow and -** return 0. Return 1 if the time and date cannot be found. +** Lock the database file to level eLock, which must be either SHARED_LOCK, +** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the +** Pager.eLock variable to the new locking state. +** +** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is +** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. +** See the comment above the #define of UNKNOWN_LOCK for an explanation +** of this. */ -static int winCurrentTime(sqlcipher_sqlite3_vfs *pVfs, double *prNow){ - int rc; - sqlcipher_sqlite3_int64 i; - rc = winCurrentTimeInt64(pVfs, &i); - if( !rc ){ - *prNow = i/86400000.0; +static int pagerLockDb(Pager *pPager, int eLock){ + int rc = SQLITE_OK; + + assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK ); + if( pPager->eLockeLock==UNKNOWN_LOCK ){ + rc = pPager->noLock ? SQLITE_OK : sqlcipher_sqlite3OsLock(pPager->fd, eLock); + if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){ + pPager->eLock = (u8)eLock; + IOTRACE(("LOCK %p %d\n", pPager, eLock)) + } } return rc; } /* -** The idea is that this function works like a combination of -** GetLastError() and FormatMessage() on Windows (or errno and -** strerror_r() on Unix). After an error is returned by an OS -** function, SQLite calls this function with zBuf pointing to -** a buffer of nBuf bytes. The OS layer should populate the -** buffer with a nul-terminated UTF-8 encoded error message -** describing the last IO error to have occurred within the calling -** thread. +** This function determines whether or not the atomic-write or +** atomic-batch-write optimizations can be used with this pager. The +** atomic-write optimization can be used if: ** -** If the error message is too large for the supplied buffer, -** it should be truncated. The return value of xGetLastError -** is zero if the error message fits in the buffer, or non-zero -** otherwise (if the message was truncated). If non-zero is returned, -** then it is not necessary to include the nul-terminator character -** in the output buffer. +** (a) the value returned by OsDeviceCharacteristics() indicates that +** a database page may be written atomically, and +** (b) the value returned by OsSectorSize() is less than or equal +** to the page size. ** -** Not supplying an error message will have no adverse effect -** on SQLite. It is fine to have an implementation that never -** returns an error message: +** If it can be used, then the value returned is the size of the journal +** file when it contains rollback data for exactly one page. ** -** int xGetLastError(sqlcipher_sqlite3_vfs *pVfs, int nBuf, char *zBuf){ -** assert(zBuf[0]=='\0'); -** return 0; -** } +** The atomic-batch-write optimization can be used if OsDeviceCharacteristics() +** returns a value with the SQLITE_IOCAP_BATCH_ATOMIC bit set. -1 is +** returned in this case. ** -** However if an error message is supplied, it will be incorporated -** by sqlite into the error message available to the user using -** sqlcipher_sqlite3_errmsg(), possibly making IO errors easier to debug. -*/ -static int winGetLastError(sqlcipher_sqlite3_vfs *pVfs, int nBuf, char *zBuf){ - DWORD e = osGetLastError(); - UNUSED_PARAMETER(pVfs); - if( nBuf>0 ) winGetLastErrorMsg(e, nBuf, zBuf); - return e; -} - -/* -** Initialize and deinitialize the operating system interface. +** If neither optimization can be used, 0 is returned. */ -SQLITE_API int sqlcipher_sqlite3_os_init(void){ - static sqlcipher_sqlite3_vfs winVfs = { - 3, /* iVersion */ - sizeof(winFile), /* szOsFile */ - SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */ - 0, /* pNext */ - "win32", /* zName */ - &winAppData, /* pAppData */ - winOpen, /* xOpen */ - winDelete, /* xDelete */ - winAccess, /* xAccess */ - winFullPathname, /* xFullPathname */ - winDlOpen, /* xDlOpen */ - winDlError, /* xDlError */ - winDlSym, /* xDlSym */ - winDlClose, /* xDlClose */ - winRandomness, /* xRandomness */ - winSleep, /* xSleep */ - winCurrentTime, /* xCurrentTime */ - winGetLastError, /* xGetLastError */ - winCurrentTimeInt64, /* xCurrentTimeInt64 */ - winSetSystemCall, /* xSetSystemCall */ - winGetSystemCall, /* xGetSystemCall */ - winNextSystemCall, /* xNextSystemCall */ - }; -#if defined(SQLITE_WIN32_HAS_WIDE) - static sqlcipher_sqlite3_vfs winLongPathVfs = { - 3, /* iVersion */ - sizeof(winFile), /* szOsFile */ - SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */ - 0, /* pNext */ - "win32-longpath", /* zName */ - &winAppData, /* pAppData */ - winOpen, /* xOpen */ - winDelete, /* xDelete */ - winAccess, /* xAccess */ - winFullPathname, /* xFullPathname */ - winDlOpen, /* xDlOpen */ - winDlError, /* xDlError */ - winDlSym, /* xDlSym */ - winDlClose, /* xDlClose */ - winRandomness, /* xRandomness */ - winSleep, /* xSleep */ - winCurrentTime, /* xCurrentTime */ - winGetLastError, /* xGetLastError */ - winCurrentTimeInt64, /* xCurrentTimeInt64 */ - winSetSystemCall, /* xSetSystemCall */ - winGetSystemCall, /* xGetSystemCall */ - winNextSystemCall, /* xNextSystemCall */ - }; -#endif - static sqlcipher_sqlite3_vfs winNolockVfs = { - 3, /* iVersion */ - sizeof(winFile), /* szOsFile */ - SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */ - 0, /* pNext */ - "win32-none", /* zName */ - &winNolockAppData, /* pAppData */ - winOpen, /* xOpen */ - winDelete, /* xDelete */ - winAccess, /* xAccess */ - winFullPathname, /* xFullPathname */ - winDlOpen, /* xDlOpen */ - winDlError, /* xDlError */ - winDlSym, /* xDlSym */ - winDlClose, /* xDlClose */ - winRandomness, /* xRandomness */ - winSleep, /* xSleep */ - winCurrentTime, /* xCurrentTime */ - winGetLastError, /* xGetLastError */ - winCurrentTimeInt64, /* xCurrentTimeInt64 */ - winSetSystemCall, /* xSetSystemCall */ - winGetSystemCall, /* xGetSystemCall */ - winNextSystemCall, /* xNextSystemCall */ - }; -#if defined(SQLITE_WIN32_HAS_WIDE) - static sqlcipher_sqlite3_vfs winLongPathNolockVfs = { - 3, /* iVersion */ - sizeof(winFile), /* szOsFile */ - SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */ - 0, /* pNext */ - "win32-longpath-none", /* zName */ - &winNolockAppData, /* pAppData */ - winOpen, /* xOpen */ - winDelete, /* xDelete */ - winAccess, /* xAccess */ - winFullPathname, /* xFullPathname */ - winDlOpen, /* xDlOpen */ - winDlError, /* xDlError */ - winDlSym, /* xDlSym */ - winDlClose, /* xDlClose */ - winRandomness, /* xRandomness */ - winSleep, /* xSleep */ - winCurrentTime, /* xCurrentTime */ - winGetLastError, /* xGetLastError */ - winCurrentTimeInt64, /* xCurrentTimeInt64 */ - winSetSystemCall, /* xSetSystemCall */ - winGetSystemCall, /* xGetSystemCall */ - winNextSystemCall, /* xNextSystemCall */ - }; -#endif +static int jrnlBufferSize(Pager *pPager){ + assert( !MEMDB ); - /* Double-check that the aSyscall[] array has been constructed - ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==80 ); +#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ + || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) + int dc; /* Device characteristics */ - /* get memory map allocation granularity */ - memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); -#if SQLITE_OS_WINRT - osGetNativeSystemInfo(&winSysInfo); + assert( isOpen(pPager->fd) ); + dc = sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd); #else - osGetSystemInfo(&winSysInfo); + UNUSED_PARAMETER(pPager); #endif - assert( winSysInfo.dwAllocationGranularity>0 ); - assert( winSysInfo.dwPageSize>0 ); - - sqlcipher_sqlite3_vfs_register(&winVfs, 1); -#if defined(SQLITE_WIN32_HAS_WIDE) - sqlcipher_sqlite3_vfs_register(&winLongPathVfs, 0); +#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE + if( pPager->dbSize>0 && (dc&SQLITE_IOCAP_BATCH_ATOMIC) ){ + return -1; + } #endif - sqlcipher_sqlite3_vfs_register(&winNolockVfs, 0); +#ifdef SQLITE_ENABLE_ATOMIC_WRITE + { + int nSector = pPager->sectorSize; + int szPage = pPager->pageSize; -#if defined(SQLITE_WIN32_HAS_WIDE) - sqlcipher_sqlite3_vfs_register(&winLongPathNolockVfs, 0); -#endif + assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); + assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); + if( 0==(dc&(SQLITE_IOCAP_ATOMIC|(szPage>>8)) || nSector>szPage) ){ + return 0; + } + } -#ifndef SQLITE_OMIT_WAL - winBigLock = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); + return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager); #endif - return SQLITE_OK; + return 0; } -SQLITE_API int sqlcipher_sqlite3_os_end(void){ -#if SQLITE_OS_WINRT - if( sleepObj!=NULL ){ - osCloseHandle(sleepObj); - sleepObj = NULL; +/* +** If SQLITE_CHECK_PAGES is defined then we do some sanity checking +** on the cache using a hash function. This is used for testing +** and debugging only. +*/ +#ifdef SQLITE_CHECK_PAGES +/* +** Return a 32-bit hash of the page data for pPage. +*/ +static u32 pager_datahash(int nByte, unsigned char *pData){ + u32 hash = 0; + int i; + for(i=0; ipPager->pageSize, (unsigned char *)pPage->pData); +} +static void pager_set_pagehash(PgHdr *pPage){ + pPage->pageHash = pager_pagehash(pPage); +} - return SQLITE_OK; +/* +** The CHECK_PAGE macro takes a PgHdr* as an argument. If SQLITE_CHECK_PAGES +** is defined, and NDEBUG is not defined, an assert() statement checks +** that the page is either dirty or still matches the calculated page-hash. +*/ +#define CHECK_PAGE(x) checkPage(x) +static void checkPage(PgHdr *pPg){ + Pager *pPager = pPg->pPager; + assert( pPager->eState!=PAGER_ERROR ); + assert( (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) ); } -#endif /* SQLITE_OS_WIN */ +#else +#define pager_datahash(X,Y) 0 +#define pager_pagehash(X) 0 +#define pager_set_pagehash(X) +#define CHECK_PAGE(x) +#endif /* SQLITE_CHECK_PAGES */ -/************** End of os_win.c **********************************************/ -/************** Begin file memdb.c *******************************************/ /* -** 2016-09-07 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** When this is called the journal file for pager pPager must be open. +** This function attempts to read a super-journal file name from the +** end of the file and, if successful, copies it into memory supplied +** by the caller. See comments above writeSuperJournal() for the format +** used to store a super-journal file name at the end of a journal file. ** -** 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. +** zSuper must point to a buffer of at least nSuper bytes allocated by +** the caller. This should be sqlcipher_sqlite3_vfs.mxPathname+1 (to ensure there is +** enough space to write the super-journal name). If the super-journal +** name in the journal is longer than nSuper bytes (including a +** nul-terminator), then this is handled as if no super-journal name +** were present in the journal. ** -****************************************************************************** +** If a super-journal file name is present at the end of the journal +** file, then it is copied into the buffer pointed to by zSuper. A +** nul-terminator byte is appended to the buffer following the +** super-journal file name. ** -** This file implements an in-memory VFS. A database is held as a contiguous -** block of memory. +** If it is determined that no super-journal file name is present +** zSuper[0] is set to 0 and SQLITE_OK returned. ** -** This file also implements interface sqlcipher_sqlite3_serialize() and -** sqlcipher_sqlite3_deserialize(). +** If an error occurs while reading from the journal file, an SQLite +** error code is returned. */ -/* #include "sqliteInt.h" */ -#ifdef SQLITE_ENABLE_DESERIALIZE +static int readSuperJournal(sqlcipher_sqlite3_file *pJrnl, char *zSuper, u32 nSuper){ + int rc; /* Return code */ + u32 len; /* Length in bytes of super-journal name */ + i64 szJ; /* Total size in bytes of journal file pJrnl */ + u32 cksum; /* MJ checksum value read from journal */ + u32 u; /* Unsigned loop counter */ + unsigned char aMagic[8]; /* A buffer to hold the magic header */ + zSuper[0] = '\0'; + + if( SQLITE_OK!=(rc = sqlcipher_sqlite3OsFileSize(pJrnl, &szJ)) + || szJ<16 + || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len)) + || len>=nSuper + || len>szJ-16 + || len==0 + || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum)) + || SQLITE_OK!=(rc = sqlcipher_sqlite3OsRead(pJrnl, aMagic, 8, szJ-8)) + || memcmp(aMagic, aJournalMagic, 8) + || SQLITE_OK!=(rc = sqlcipher_sqlite3OsRead(pJrnl, zSuper, len, szJ-16-len)) + ){ + return rc; + } + + /* See if the checksum matches the super-journal name */ + for(u=0; ujournalOff, assuming a sector +** size of pPager->sectorSize bytes. +** +** i.e for a sector size of 512: +** +** Pager.journalOff Return value +** --------------------------------------- +** 0 0 +** 512 512 +** 100 512 +** 2000 2048 +** */ -typedef struct sqlcipher_sqlite3_vfs MemVfs; -typedef struct MemFile MemFile; +static i64 journalHdrOffset(Pager *pPager){ + i64 offset = 0; + i64 c = pPager->journalOff; + if( c ){ + offset = ((c-1)/JOURNAL_HDR_SZ(pPager) + 1) * JOURNAL_HDR_SZ(pPager); + } + assert( offset%JOURNAL_HDR_SZ(pPager)==0 ); + assert( offset>=c ); + assert( (offset-c)pAppData)) +static int zeroJournalHdr(Pager *pPager, int doTruncate){ + int rc = SQLITE_OK; /* Return code */ + assert( isOpen(pPager->jfd) ); + assert( !sqlcipher_sqlite3JournalIsInMemory(pPager->jfd) ); + if( pPager->journalOff ){ + const i64 iLimit = pPager->journalSizeLimit; /* Local cache of jsl */ -/* An open file */ -struct MemFile { - sqlcipher_sqlite3_file base; /* IO methods */ - sqlcipher_sqlite3_int64 sz; /* Size of the file */ - sqlcipher_sqlite3_int64 szAlloc; /* Space allocated to aData */ - sqlcipher_sqlite3_int64 szMax; /* Maximum allowed size of the file */ - unsigned char *aData; /* content of the file */ - int nMmap; /* Number of memory mapped pages */ - unsigned mFlags; /* Flags */ - int eLock; /* Most recent lock against this file */ -}; + IOTRACE(("JZEROHDR %p\n", pPager)) + if( doTruncate || iLimit==0 ){ + rc = sqlcipher_sqlite3OsTruncate(pPager->jfd, 0); + }else{ + static const char zeroHdr[28] = {0}; + rc = sqlcipher_sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0); + } + if( rc==SQLITE_OK && !pPager->noSync ){ + rc = sqlcipher_sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags); + } -/* -** Methods for MemFile -*/ -static int memdbClose(sqlcipher_sqlite3_file*); -static int memdbRead(sqlcipher_sqlite3_file*, void*, int iAmt, sqlcipher_sqlite3_int64 iOfst); -static int memdbWrite(sqlcipher_sqlite3_file*,const void*,int iAmt, sqlcipher_sqlite3_int64 iOfst); -static int memdbTruncate(sqlcipher_sqlite3_file*, sqlcipher_sqlite3_int64 size); -static int memdbSync(sqlcipher_sqlite3_file*, int flags); -static int memdbFileSize(sqlcipher_sqlite3_file*, sqlcipher_sqlite3_int64 *pSize); -static int memdbLock(sqlcipher_sqlite3_file*, int); -/* static int memdbCheckReservedLock(sqlcipher_sqlite3_file*, int *pResOut);// not used */ -static int memdbFileControl(sqlcipher_sqlite3_file*, int op, void *pArg); -/* static int memdbSectorSize(sqlcipher_sqlite3_file*); // not used */ -static int memdbDeviceCharacteristics(sqlcipher_sqlite3_file*); -static int memdbFetch(sqlcipher_sqlite3_file*, sqlcipher_sqlite3_int64 iOfst, int iAmt, void **pp); -static int memdbUnfetch(sqlcipher_sqlite3_file*, sqlcipher_sqlite3_int64 iOfst, void *p); + /* At this point the transaction is committed but the write lock + ** is still held on the file. If there is a size limit configured for + ** the persistent journal and the journal file currently consumes more + ** space than that limit allows for, truncate it now. There is no need + ** to sync the file following this operation. + */ + if( rc==SQLITE_OK && iLimit>0 ){ + i64 sz; + rc = sqlcipher_sqlite3OsFileSize(pPager->jfd, &sz); + if( rc==SQLITE_OK && sz>iLimit ){ + rc = sqlcipher_sqlite3OsTruncate(pPager->jfd, iLimit); + } + } + } + return rc; +} /* -** Methods for MemVfs +** The journal file must be open when this routine is called. A journal +** header (JOURNAL_HDR_SZ bytes) is written into the journal file at the +** current location. +** +** The format for the journal header is as follows: +** - 8 bytes: Magic identifying journal format. +** - 4 bytes: Number of records in journal, or -1 no-sync mode is on. +** - 4 bytes: Random number used for page hash. +** - 4 bytes: Initial database page count. +** - 4 bytes: Sector size used by the process that wrote this journal. +** - 4 bytes: Database page size. +** +** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space. */ -static int memdbOpen(sqlcipher_sqlite3_vfs*, const char *, sqlcipher_sqlite3_file*, int , int *); -/* static int memdbDelete(sqlcipher_sqlite3_vfs*, const char *zName, int syncDir); */ -static int memdbAccess(sqlcipher_sqlite3_vfs*, const char *zName, int flags, int *); -static int memdbFullPathname(sqlcipher_sqlite3_vfs*, const char *zName, int, char *zOut); -static void *memdbDlOpen(sqlcipher_sqlite3_vfs*, const char *zFilename); -static void memdbDlError(sqlcipher_sqlite3_vfs*, int nByte, char *zErrMsg); -static void (*memdbDlSym(sqlcipher_sqlite3_vfs *pVfs, void *p, const char*zSym))(void); -static void memdbDlClose(sqlcipher_sqlite3_vfs*, void*); -static int memdbRandomness(sqlcipher_sqlite3_vfs*, int nByte, char *zOut); -static int memdbSleep(sqlcipher_sqlite3_vfs*, int microseconds); -/* static int memdbCurrentTime(sqlcipher_sqlite3_vfs*, double*); */ -static int memdbGetLastError(sqlcipher_sqlite3_vfs*, int, char *); -static int memdbCurrentTimeInt64(sqlcipher_sqlite3_vfs*, sqlcipher_sqlite3_int64*); +static int writeJournalHdr(Pager *pPager){ + int rc = SQLITE_OK; /* Return code */ + char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */ + u32 nHeader = (u32)pPager->pageSize;/* Size of buffer pointed to by zHeader */ + u32 nWrite; /* Bytes of header sector written */ + int ii; /* Loop counter */ -static sqlcipher_sqlite3_vfs memdb_vfs = { - 2, /* iVersion */ - 0, /* szOsFile (set when registered) */ - 1024, /* mxPathname */ - 0, /* pNext */ - "memdb", /* zName */ - 0, /* pAppData (set when registered) */ - memdbOpen, /* xOpen */ - 0, /* memdbDelete, */ /* xDelete */ - memdbAccess, /* xAccess */ - memdbFullPathname, /* xFullPathname */ - memdbDlOpen, /* xDlOpen */ - memdbDlError, /* xDlError */ - memdbDlSym, /* xDlSym */ - memdbDlClose, /* xDlClose */ - memdbRandomness, /* xRandomness */ - memdbSleep, /* xSleep */ - 0, /* memdbCurrentTime, */ /* xCurrentTime */ - memdbGetLastError, /* xGetLastError */ - memdbCurrentTimeInt64 /* xCurrentTimeInt64 */ -}; + assert( isOpen(pPager->jfd) ); /* Journal file must be open. */ -static const sqlcipher_sqlite3_io_methods memdb_io_methods = { - 3, /* iVersion */ - memdbClose, /* xClose */ - memdbRead, /* xRead */ - memdbWrite, /* xWrite */ - memdbTruncate, /* xTruncate */ - memdbSync, /* xSync */ - memdbFileSize, /* xFileSize */ - memdbLock, /* xLock */ - memdbLock, /* xUnlock - same as xLock in this case */ - 0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */ - memdbFileControl, /* xFileControl */ - 0, /* memdbSectorSize,*/ /* xSectorSize */ - memdbDeviceCharacteristics, /* xDeviceCharacteristics */ - 0, /* xShmMap */ - 0, /* xShmLock */ - 0, /* xShmBarrier */ - 0, /* xShmUnmap */ - memdbFetch, /* xFetch */ - memdbUnfetch /* xUnfetch */ -}; + if( nHeader>JOURNAL_HDR_SZ(pPager) ){ + nHeader = JOURNAL_HDR_SZ(pPager); + } + + /* If there are active savepoints and any of them were created + ** since the most recent journal header was written, update the + ** PagerSavepoint.iHdrOffset fields now. + */ + for(ii=0; iinSavepoint; ii++){ + if( pPager->aSavepoint[ii].iHdrOffset==0 ){ + pPager->aSavepoint[ii].iHdrOffset = pPager->journalOff; + } + } + + pPager->journalHdr = pPager->journalOff = journalHdrOffset(pPager); + + /* + ** Write the nRec Field - the number of page records that follow this + ** journal header. Normally, zero is written to this value at this time. + ** After the records are added to the journal (and the journal synced, + ** if in full-sync mode), the zero is overwritten with the true number + ** of records (see syncJournal()). + ** + ** A faster alternative is to write 0xFFFFFFFF to the nRec field. When + ** reading the journal this value tells SQLite to assume that the + ** rest of the journal file contains valid page records. This assumption + ** is dangerous, as if a failure occurred whilst writing to the journal + ** file it may contain some garbage data. There are two scenarios + ** where this risk can be ignored: + ** + ** * When the pager is in no-sync mode. Corruption can follow a + ** power failure in this case anyway. + ** + ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees + ** that garbage data is never appended to the journal file. + */ + assert( isOpen(pPager->fd) || pPager->noSync ); + if( pPager->noSync || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY) + || (sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) + ){ + memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); + put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff); + }else{ + memset(zHeader, 0, sizeof(aJournalMagic)+4); + } + + /* The random check-hash initializer */ + sqlcipher_sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); + put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit); + /* The initial database size */ + put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbOrigSize); + /* The assumed sector size for this process */ + put32bits(&zHeader[sizeof(aJournalMagic)+12], pPager->sectorSize); + /* The page size */ + put32bits(&zHeader[sizeof(aJournalMagic)+16], pPager->pageSize); + /* Initializing the tail of the buffer is not necessary. Everything + ** works find if the following memset() is omitted. But initializing + ** the memory prevents valgrind from complaining, so we are willing to + ** take the performance hit. + */ + memset(&zHeader[sizeof(aJournalMagic)+20], 0, + nHeader-(sizeof(aJournalMagic)+20)); -/* -** Close an memdb-file. -** -** The pData pointer is owned by the application, so there is nothing -** to free. Unless the SQLITE_DESERIALIZE_FREEONCLOSE flag is set, -** in which case we own the pData pointer and need to free it. -*/ -static int memdbClose(sqlcipher_sqlite3_file *pFile){ - MemFile *p = (MemFile *)pFile; - if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ){ - sqlcipher_sqlite3_free(p->aData); + /* In theory, it is only necessary to write the 28 bytes that the + ** journal header consumes to the journal file here. Then increment the + ** Pager.journalOff variable by JOURNAL_HDR_SZ so that the next + ** record is written to the following sector (leaving a gap in the file + ** that will be implicitly filled in by the OS). + ** + ** However it has been discovered that on some systems this pattern can + ** be significantly slower than contiguously writing data to the file, + ** even if that means explicitly writing data to the block of + ** (JOURNAL_HDR_SZ - 28) bytes that will not be used. So that is what + ** is done. + ** + ** The loop is required here in case the sector-size is larger than the + ** database page size. Since the zHeader buffer is only Pager.pageSize + ** bytes in size, more than one call to sqlcipher_sqlite3OsWrite() may be required + ** to populate the entire journal header sector. + */ + for(nWrite=0; rc==SQLITE_OK&&nWritejournalHdr, nHeader)) + rc = sqlcipher_sqlite3OsWrite(pPager->jfd, zHeader, nHeader, pPager->journalOff); + assert( pPager->journalHdr <= pPager->journalOff ); + pPager->journalOff += nHeader; } - return SQLITE_OK; + + return rc; } /* -** Read data from an memdb-file. +** The journal file must be open when this is called. A journal header file +** (JOURNAL_HDR_SZ bytes) is read from the current location in the journal +** file. The current location in the journal file is given by +** pPager->journalOff. See comments above function writeJournalHdr() for +** a description of the journal header format. +** +** If the header is read successfully, *pNRec is set to the number of +** page records following this header and *pDbSize is set to the size of the +** database before the transaction began, in pages. Also, pPager->cksumInit +** is set to the value read from the journal header. SQLITE_OK is returned +** in this case. +** +** If the journal header file appears to be corrupted, SQLITE_DONE is +** returned and *pNRec and *PDbSize are undefined. If JOURNAL_HDR_SZ bytes +** cannot be read from the journal file an error code is returned. */ -static int memdbRead( - sqlcipher_sqlite3_file *pFile, - void *zBuf, - int iAmt, - sqlite_int64 iOfst +static int readJournalHdr( + Pager *pPager, /* Pager object */ + int isHot, + i64 journalSize, /* Size of the open journal file in bytes */ + u32 *pNRec, /* OUT: Value read from the nRec field */ + u32 *pDbSize /* OUT: Value of original database size field */ ){ - MemFile *p = (MemFile *)pFile; - if( iOfst+iAmt>p->sz ){ - memset(zBuf, 0, iAmt); - if( iOfstsz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst); - return SQLITE_IOERR_SHORT_READ; + int rc; /* Return code */ + unsigned char aMagic[8]; /* A buffer to hold the magic header */ + i64 iHdrOff; /* Offset of journal header being read */ + + assert( isOpen(pPager->jfd) ); /* Journal file must be open. */ + + /* Advance Pager.journalOff to the start of the next sector. If the + ** journal file is too small for there to be a header stored at this + ** point, return SQLITE_DONE. + */ + pPager->journalOff = journalHdrOffset(pPager); + if( pPager->journalOff+JOURNAL_HDR_SZ(pPager) > journalSize ){ + return SQLITE_DONE; } - memcpy(zBuf, p->aData+iOfst, iAmt); - return SQLITE_OK; -} + iHdrOff = pPager->journalOff; -/* -** Try to enlarge the memory allocation to hold at least sz bytes -*/ -static int memdbEnlarge(MemFile *p, sqlcipher_sqlite3_int64 newSz){ - unsigned char *pNew; - if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || p->nMmap>0 ){ - return SQLITE_FULL; + /* Read in the first 8 bytes of the journal header. If they do not match + ** the magic string found at the start of each journal header, return + ** SQLITE_DONE. If an IO error occurs, return an error code. Otherwise, + ** proceed. + */ + if( isHot || iHdrOff!=pPager->journalHdr ){ + rc = sqlcipher_sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), iHdrOff); + if( rc ){ + return rc; + } + if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){ + return SQLITE_DONE; + } } - if( newSz>p->szMax ){ - return SQLITE_FULL; + + /* Read the first three 32-bit fields of the journal header: The nRec + ** field, the checksum-initializer and the database size at the start + ** of the transaction. Return an error code if anything goes wrong. + */ + if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+8, pNRec)) + || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+12, &pPager->cksumInit)) + || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+16, pDbSize)) + ){ + return rc; } - newSz *= 2; - if( newSz>p->szMax ) newSz = p->szMax; - pNew = sqlcipher_sqlite3Realloc(p->aData, newSz); - if( pNew==0 ) return SQLITE_NOMEM; - p->aData = pNew; - p->szAlloc = newSz; - return SQLITE_OK; -} -/* -** Write data to an memdb-file. -*/ -static int memdbWrite( - sqlcipher_sqlite3_file *pFile, - const void *z, - int iAmt, - sqlite_int64 iOfst -){ - MemFile *p = (MemFile *)pFile; - if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ) return SQLITE_READONLY; - if( iOfst+iAmt>p->sz ){ - int rc; - if( iOfst+iAmt>p->szAlloc - && (rc = memdbEnlarge(p, iOfst+iAmt))!=SQLITE_OK + if( pPager->journalOff==0 ){ + u32 iPageSize; /* Page-size field of journal header */ + u32 iSectorSize; /* Sector-size field of journal header */ + + /* Read the page-size and sector-size journal header fields. */ + if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+20, &iSectorSize)) + || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+24, &iPageSize)) ){ return rc; } - if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz); - p->sz = iOfst+iAmt; + + /* Versions of SQLite prior to 3.5.8 set the page-size field of the + ** journal header to zero. In this case, assume that the Pager.pageSize + ** variable is already set to the correct page size. + */ + if( iPageSize==0 ){ + iPageSize = pPager->pageSize; + } + + /* Check that the values read from the page-size and sector-size fields + ** are within range. To be 'in range', both values need to be a power + ** of two greater than or equal to 512 or 32, and not greater than their + ** respective compile time maximum limits. + */ + if( iPageSize<512 || iSectorSize<32 + || iPageSize>SQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE + || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0 + ){ + /* If the either the page-size or sector-size in the journal-header is + ** invalid, then the process that wrote the journal-header must have + ** crashed before the header was synced. In this case stop reading + ** the journal file here. + */ + return SQLITE_DONE; + } + + /* Update the page-size to match the value read from the journal. + ** Use a testcase() macro to make sure that malloc failure within + ** PagerSetPagesize() is tested. + */ + rc = sqlcipher_sqlite3PagerSetPagesize(pPager, &iPageSize, -1); + testcase( rc!=SQLITE_OK ); + + /* Update the assumed sector-size to match the value used by + ** the process that created this journal. If this journal was + ** created by a process other than this one, then this routine + ** is being called from within pager_playback(). The local value + ** of Pager.sectorSize is restored at the end of that routine. + */ + pPager->sectorSize = iSectorSize; } - memcpy(p->aData+iOfst, z, iAmt); - return SQLITE_OK; -} -/* -** Truncate an memdb-file. -** -** In rollback mode (which is always the case for memdb, as it does not -** support WAL mode) the truncate() method is only used to reduce -** the size of a file, never to increase the size. -*/ -static int memdbTruncate(sqlcipher_sqlite3_file *pFile, sqlite_int64 size){ - MemFile *p = (MemFile *)pFile; - if( NEVER(size>p->sz) ) return SQLITE_FULL; - p->sz = size; - return SQLITE_OK; + pPager->journalOff += JOURNAL_HDR_SZ(pPager); + return rc; } -/* -** Sync an memdb-file. -*/ -static int memdbSync(sqlcipher_sqlite3_file *pFile, int flags){ - return SQLITE_OK; -} /* -** Return the current file-size of an memdb-file. +** Write the supplied super-journal name into the journal file for pager +** pPager at the current location. The super-journal name must be the last +** thing written to a journal file. If the pager is in full-sync mode, the +** journal file descriptor is advanced to the next sector boundary before +** anything is written. The format is: +** +** + 4 bytes: PAGER_SJ_PGNO. +** + N bytes: super-journal filename in utf-8. +** + 4 bytes: N (length of super-journal name in bytes, no nul-terminator). +** + 4 bytes: super-journal name checksum. +** + 8 bytes: aJournalMagic[]. +** +** The super-journal page checksum is the sum of the bytes in thesuper-journal +** name, where each byte is interpreted as a signed 8-bit integer. +** +** If zSuper is a NULL pointer (occurs for a single database transaction), +** this call is a no-op. */ -static int memdbFileSize(sqlcipher_sqlite3_file *pFile, sqlite_int64 *pSize){ - MemFile *p = (MemFile *)pFile; - *pSize = p->sz; - return SQLITE_OK; -} +static int writeSuperJournal(Pager *pPager, const char *zSuper){ + int rc; /* Return code */ + int nSuper; /* Length of string zSuper */ + i64 iHdrOff; /* Offset of header in journal file */ + i64 jrnlSize; /* Size of journal file on disk */ + u32 cksum = 0; /* Checksum of string zSuper */ -/* -** Lock an memdb-file. -*/ -static int memdbLock(sqlcipher_sqlite3_file *pFile, int eLock){ - MemFile *p = (MemFile *)pFile; - if( eLock>SQLITE_LOCK_SHARED - && (p->mFlags & SQLITE_DESERIALIZE_READONLY)!=0 + assert( pPager->setSuper==0 ); + assert( !pagerUseWal(pPager) ); + + if( !zSuper + || pPager->journalMode==PAGER_JOURNALMODE_MEMORY + || !isOpen(pPager->jfd) ){ - return SQLITE_READONLY; + return SQLITE_OK; } - p->eLock = eLock; - return SQLITE_OK; -} + pPager->setSuper = 1; + assert( pPager->journalHdr <= pPager->journalOff ); -#if 0 /* Never used because memdbAccess() always returns false */ -/* -** Check if another file-handle holds a RESERVED lock on an memdb-file. -*/ -static int memdbCheckReservedLock(sqlcipher_sqlite3_file *pFile, int *pResOut){ - *pResOut = 0; - return SQLITE_OK; -} -#endif + /* Calculate the length in bytes and the checksum of zSuper */ + for(nSuper=0; zSuper[nSuper]; nSuper++){ + cksum += zSuper[nSuper]; + } -/* -** File control method. For custom operations on an memdb-file. -*/ -static int memdbFileControl(sqlcipher_sqlite3_file *pFile, int op, void *pArg){ - MemFile *p = (MemFile *)pFile; - int rc = SQLITE_NOTFOUND; - if( op==SQLITE_FCNTL_VFSNAME ){ - *(char**)pArg = sqlcipher_sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz); - rc = SQLITE_OK; + /* If in full-sync mode, advance to the next disk sector before writing + ** the super-journal name. This is in case the previous page written to + ** the journal has already been synced. + */ + if( pPager->fullSync ){ + pPager->journalOff = journalHdrOffset(pPager); } - if( op==SQLITE_FCNTL_SIZE_LIMIT ){ - sqlcipher_sqlite3_int64 iLimit = *(sqlcipher_sqlite3_int64*)pArg; - if( iLimitsz ){ - if( iLimit<0 ){ - iLimit = p->szMax; - }else{ - iLimit = p->sz; - } - } - p->szMax = iLimit; - *(sqlcipher_sqlite3_int64*)pArg = iLimit; - rc = SQLITE_OK; + iHdrOff = pPager->journalOff; + + /* Write the super-journal data to the end of the journal file. If + ** an error occurs, return the error code to the caller. + */ + if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_SJ_PGNO(pPager)))) + || (0 != (rc = sqlcipher_sqlite3OsWrite(pPager->jfd, zSuper, nSuper, iHdrOff+4))) + || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper, nSuper))) + || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper+4, cksum))) + || (0 != (rc = sqlcipher_sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, + iHdrOff+4+nSuper+8))) + ){ + return rc; + } + pPager->journalOff += (nSuper+20); + + /* If the pager is in peristent-journal mode, then the physical + ** journal-file may extend past the end of the super-journal name + ** and 8 bytes of magic data just written to the file. This is + ** dangerous because the code to rollback a hot-journal file + ** will not be able to find the super-journal name to determine + ** whether or not the journal is hot. + ** + ** Easiest thing to do in this scenario is to truncate the journal + ** file to the required size. + */ + if( SQLITE_OK==(rc = sqlcipher_sqlite3OsFileSize(pPager->jfd, &jrnlSize)) + && jrnlSize>pPager->journalOff + ){ + rc = sqlcipher_sqlite3OsTruncate(pPager->jfd, pPager->journalOff); } return rc; } -#if 0 /* Not used because of SQLITE_IOCAP_POWERSAFE_OVERWRITE */ /* -** Return the sector-size in bytes for an memdb-file. +** Discard the entire contents of the in-memory page-cache. */ -static int memdbSectorSize(sqlcipher_sqlite3_file *pFile){ - return 1024; +static void pager_reset(Pager *pPager){ + pPager->iDataVersion++; + sqlcipher_sqlite3BackupRestart(pPager->pBackup); + sqlcipher_sqlite3PcacheClear(pPager->pPCache); } -#endif /* -** Return the device characteristic flags supported by an memdb-file. +** Return the pPager->iDataVersion value */ -static int memdbDeviceCharacteristics(sqlcipher_sqlite3_file *pFile){ - return SQLITE_IOCAP_ATOMIC | - SQLITE_IOCAP_POWERSAFE_OVERWRITE | - SQLITE_IOCAP_SAFE_APPEND | - SQLITE_IOCAP_SEQUENTIAL; -} - -/* Fetch a page of a memory-mapped file */ -static int memdbFetch( - sqlcipher_sqlite3_file *pFile, - sqlcipher_sqlite3_int64 iOfst, - int iAmt, - void **pp -){ - MemFile *p = (MemFile *)pFile; - if( iOfst+iAmt>p->sz ){ - *pp = 0; - }else{ - p->nMmap++; - *pp = (void*)(p->aData + iOfst); - } - return SQLITE_OK; -} - -/* Release a memory-mapped page */ -static int memdbUnfetch(sqlcipher_sqlite3_file *pFile, sqlcipher_sqlite3_int64 iOfst, void *pPage){ - MemFile *p = (MemFile *)pFile; - p->nMmap--; - return SQLITE_OK; +SQLITE_PRIVATE u32 sqlcipher_sqlite3PagerDataVersion(Pager *pPager){ + return pPager->iDataVersion; } /* -** Open an mem file handle. +** Free all structures in the Pager.aSavepoint[] array and set both +** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal +** if it is open and the pager is not in exclusive mode. */ -static int memdbOpen( - sqlcipher_sqlite3_vfs *pVfs, - const char *zName, - sqlcipher_sqlite3_file *pFile, - int flags, - int *pOutFlags -){ - MemFile *p = (MemFile*)pFile; - if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ - return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags); +static void releaseAllSavepoints(Pager *pPager){ + int ii; /* Iterator for looping through Pager.aSavepoint */ + for(ii=0; iinSavepoint; ii++){ + sqlcipher_sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint); } - memset(p, 0, sizeof(*p)); - p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; - assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ - *pOutFlags = flags | SQLITE_OPEN_MEMORY; - pFile->pMethods = &memdb_io_methods; - p->szMax = sqlcipher_sqlite3GlobalConfig.mxMemdbSize; - return SQLITE_OK; + if( !pPager->exclusiveMode || sqlcipher_sqlite3JournalIsInMemory(pPager->sjfd) ){ + sqlcipher_sqlite3OsClose(pPager->sjfd); + } + sqlcipher_sqlite3_free(pPager->aSavepoint); + pPager->aSavepoint = 0; + pPager->nSavepoint = 0; + pPager->nSubRec = 0; } -#if 0 /* Only used to delete rollback journals, super-journals, and WAL - ** files, none of which exist in memdb. So this routine is never used */ /* -** Delete the file located at zPath. If the dirSync argument is true, -** ensure the file-system modifications are synced to disk before -** returning. +** Set the bit number pgno in the PagerSavepoint.pInSavepoint +** bitvecs of all open savepoints. Return SQLITE_OK if successful +** or SQLITE_NOMEM if a malloc failure occurs. */ -static int memdbDelete(sqlcipher_sqlite3_vfs *pVfs, const char *zPath, int dirSync){ - return SQLITE_IOERR_DELETE; +static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){ + int ii; /* Loop counter */ + int rc = SQLITE_OK; /* Result code */ + + for(ii=0; iinSavepoint; ii++){ + PagerSavepoint *p = &pPager->aSavepoint[ii]; + if( pgno<=p->nOrig ){ + rc |= sqlcipher_sqlite3BitvecSet(p->pInSavepoint, pgno); + testcase( rc==SQLITE_NOMEM ); + assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); + } + } + return rc; } -#endif /* -** Test for access permissions. Return true if the requested permission -** is available, or false otherwise. +** This function is a no-op if the pager is in exclusive mode and not +** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN +** state. ** -** With memdb, no files ever exist on disk. So always return false. +** If the pager is not in exclusive-access mode, the database file is +** completely unlocked. If the file is unlocked and the file-system does +** not exhibit the UNDELETABLE_WHEN_OPEN property, the journal file is +** closed (if it is open). +** +** If the pager is in ERROR state when this function is called, the +** contents of the pager cache are discarded before switching back to +** the OPEN state. Regardless of whether the pager is in exclusive-mode +** or not, any journal file left in the file-system will be treated +** as a hot-journal and rolled back the next time a read-transaction +** is opened (by this or by any other connection). */ -static int memdbAccess( - sqlcipher_sqlite3_vfs *pVfs, - const char *zPath, - int flags, - int *pResOut -){ - *pResOut = 0; - return SQLITE_OK; -} +static void pager_unlock(Pager *pPager){ -/* -** Populate buffer zOut with the full canonical pathname corresponding -** to the pathname in zPath. zOut is guaranteed to point to a buffer -** of at least (INST_MAX_PATHNAME+1) bytes. -*/ -static int memdbFullPathname( - sqlcipher_sqlite3_vfs *pVfs, - const char *zPath, - int nOut, - char *zOut -){ - sqlcipher_sqlite3_snprintf(nOut, zOut, "%s", zPath); - return SQLITE_OK; -} + assert( pPager->eState==PAGER_READER + || pPager->eState==PAGER_OPEN + || pPager->eState==PAGER_ERROR + ); -/* -** Open the dynamic library located at zPath and return a handle. -*/ -static void *memdbDlOpen(sqlcipher_sqlite3_vfs *pVfs, const char *zPath){ - return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath); -} + sqlcipher_sqlite3BitvecDestroy(pPager->pInJournal); + pPager->pInJournal = 0; + releaseAllSavepoints(pPager); -/* -** Populate the buffer zErrMsg (size nByte bytes) with a human readable -** utf-8 string describing the most recent error encountered associated -** with dynamic libraries. -*/ -static void memdbDlError(sqlcipher_sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ - ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg); -} + if( pagerUseWal(pPager) ){ + assert( !isOpen(pPager->jfd) ); + sqlcipher_sqlite3WalEndReadTransaction(pPager->pWal); + pPager->eState = PAGER_OPEN; + }else if( !pPager->exclusiveMode ){ + int rc; /* Error code returned by pagerUnlockDb() */ + int iDc = isOpen(pPager->fd)?sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd):0; -/* -** Return a pointer to the symbol zSymbol in the dynamic library pHandle. -*/ -static void (*memdbDlSym(sqlcipher_sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ - return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym); -} + /* If the operating system support deletion of open files, then + ** close the journal file when dropping the database lock. Otherwise + ** another connection with journal_mode=delete might delete the file + ** out from under us. + */ + assert( (PAGER_JOURNALMODE_MEMORY & 5)!=1 ); + assert( (PAGER_JOURNALMODE_OFF & 5)!=1 ); + assert( (PAGER_JOURNALMODE_WAL & 5)!=1 ); + assert( (PAGER_JOURNALMODE_DELETE & 5)!=1 ); + assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); + assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); + if( 0==(iDc & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN) + || 1!=(pPager->journalMode & 5) + ){ + sqlcipher_sqlite3OsClose(pPager->jfd); + } -/* -** Close the dynamic library handle pHandle. -*/ -static void memdbDlClose(sqlcipher_sqlite3_vfs *pVfs, void *pHandle){ - ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle); -} + /* If the pager is in the ERROR state and the call to unlock the database + ** file fails, set the current lock to UNKNOWN_LOCK. See the comment + ** above the #define for UNKNOWN_LOCK for an explanation of why this + ** is necessary. + */ + rc = pagerUnlockDb(pPager, NO_LOCK); + if( rc!=SQLITE_OK && pPager->eState==PAGER_ERROR ){ + pPager->eLock = UNKNOWN_LOCK; + } -/* -** Populate the buffer pointed to by zBufOut with nByte bytes of -** random data. -*/ -static int memdbRandomness(sqlcipher_sqlite3_vfs *pVfs, int nByte, char *zBufOut){ - return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut); -} + /* The pager state may be changed from PAGER_ERROR to PAGER_OPEN here + ** without clearing the error code. This is intentional - the error + ** code is cleared and the cache reset in the block below. + */ + assert( pPager->errCode || pPager->eState!=PAGER_ERROR ); + pPager->eState = PAGER_OPEN; + } -/* -** Sleep for nMicro microseconds. Return the number of microseconds -** actually slept. -*/ -static int memdbSleep(sqlcipher_sqlite3_vfs *pVfs, int nMicro){ - return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro); + /* If Pager.errCode is set, the contents of the pager cache cannot be + ** trusted. Now that there are no outstanding references to the pager, + ** it can safely move back to PAGER_OPEN state. This happens in both + ** normal and exclusive-locking mode. + */ + assert( pPager->errCode==SQLITE_OK || !MEMDB ); + if( pPager->errCode ){ + if( pPager->tempFile==0 ){ + pager_reset(pPager); + pPager->changeCountDone = 0; + pPager->eState = PAGER_OPEN; + }else{ + pPager->eState = (isOpen(pPager->jfd) ? PAGER_OPEN : PAGER_READER); + } + if( USEFETCH(pPager) ) sqlcipher_sqlite3OsUnfetch(pPager->fd, 0, 0); + pPager->errCode = SQLITE_OK; + setGetterMethod(pPager); + } + + pPager->journalOff = 0; + pPager->journalHdr = 0; + pPager->setSuper = 0; } -#if 0 /* Never used. Modern cores only call xCurrentTimeInt64() */ /* -** Return the current time as a Julian Day number in *pTimeOut. +** This function is called whenever an IOERR or FULL error that requires +** the pager to transition into the ERROR state may ahve occurred. +** The first argument is a pointer to the pager structure, the second +** the error-code about to be returned by a pager API function. The +** value returned is a copy of the second argument to this function. +** +** If the second argument is SQLITE_FULL, SQLITE_IOERR or one of the +** IOERR sub-codes, the pager enters the ERROR state and the error code +** is stored in Pager.errCode. While the pager remains in the ERROR state, +** all major API calls on the Pager will immediately return Pager.errCode. +** +** The ERROR state indicates that the contents of the pager-cache +** cannot be trusted. This state can be cleared by completely discarding +** the contents of the pager-cache. If a transaction was active when +** the persistent error occurred, then the rollback journal may need +** to be replayed to restore the contents of the database file (as if +** it were a hot-journal). */ -static int memdbCurrentTime(sqlcipher_sqlite3_vfs *pVfs, double *pTimeOut){ - return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut); +static int pager_error(Pager *pPager, int rc){ + int rc2 = rc & 0xff; + assert( rc==SQLITE_OK || !MEMDB ); + assert( + pPager->errCode==SQLITE_FULL || + pPager->errCode==SQLITE_OK || + (pPager->errCode & 0xff)==SQLITE_IOERR + ); + if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){ + pPager->errCode = rc; + pPager->eState = PAGER_ERROR; + setGetterMethod(pPager); + } + return rc; } -#endif -static int memdbGetLastError(sqlcipher_sqlite3_vfs *pVfs, int a, char *b){ - return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b); -} -static int memdbCurrentTimeInt64(sqlcipher_sqlite3_vfs *pVfs, sqlcipher_sqlite3_int64 *p){ - return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p); -} +static int pager_truncate(Pager *pPager, Pgno nPage); /* -** Translate a database connection pointer and schema name into a -** MemFile pointer. +** The write transaction open on pPager is being committed (bCommit==1) +** or rolled back (bCommit==0). +** +** Return TRUE if and only if all dirty pages should be flushed to disk. +** +** Rules: +** +** * For non-TEMP databases, always sync to disk. This is necessary +** for transactions to be durable. +** +** * Sync TEMP database only on a COMMIT (not a ROLLBACK) when the backing +** file has been created already (via a spill on pagerStress()) and +** when the number of dirty pages in memory exceeds 25% of the total +** cache size. */ -static MemFile *memdbFromDbSchema(sqlcipher_sqlite3 *db, const char *zSchema){ - MemFile *p = 0; - int rc = sqlcipher_sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p); - if( rc ) return 0; - if( p->base.pMethods!=&memdb_io_methods ) return 0; - return p; +static int pagerFlushOnCommit(Pager *pPager, int bCommit){ + if( pPager->tempFile==0 ) return 1; + if( !bCommit ) return 0; + if( !isOpen(pPager->fd) ) return 0; + return (sqlcipher_sqlite3PCachePercentDirty(pPager->pPCache)>=25); } /* -** Return the serialization of a database +** This routine ends a transaction. A transaction is usually ended by +** either a COMMIT or a ROLLBACK operation. This routine may be called +** after rollback of a hot-journal, or if an error occurs while opening +** the journal file or writing the very first journal-header of a +** database transaction. +** +** This routine is never called in PAGER_ERROR state. If it is called +** in PAGER_NONE or PAGER_SHARED state and the lock held is less +** exclusive than a RESERVED lock, it is a no-op. +** +** Otherwise, any active savepoints are released. +** +** If the journal file is open, then it is "finalized". Once a journal +** file has been finalized it is not possible to use it to roll back a +** transaction. Nor will it be considered to be a hot-journal by this +** or any other database connection. Exactly how a journal is finalized +** depends on whether or not the pager is running in exclusive mode and +** the current journal-mode (Pager.journalMode value), as follows: +** +** journalMode==MEMORY +** Journal file descriptor is simply closed. This destroys an +** in-memory journal. +** +** journalMode==TRUNCATE +** Journal file is truncated to zero bytes in size. +** +** journalMode==PERSIST +** The first 28 bytes of the journal file are zeroed. This invalidates +** the first journal header in the file, and hence the entire journal +** file. An invalid journal file cannot be rolled back. +** +** journalMode==DELETE +** The journal file is closed and deleted using sqlcipher_sqlite3OsDelete(). +** +** If the pager is running in exclusive mode, this method of finalizing +** the journal file is never used. Instead, if the journalMode is +** DELETE and the pager is in exclusive mode, the method described under +** journalMode==PERSIST is used instead. +** +** After the journal is finalized, the pager moves to PAGER_READER state. +** If running in non-exclusive rollback mode, the lock on the file is +** downgraded to a SHARED_LOCK. +** +** SQLITE_OK is returned if no error occurs. If an error occurs during +** any of the IO operations to finalize the journal file or unlock the +** database then the IO error code is returned to the user. If the +** operation to finalize the journal file fails, then the code still +** tries to unlock the database file if not in exclusive mode. If the +** unlock operation fails as well, then the first error code related +** to the first error encountered (the journal finalization one) is +** returned. */ -SQLITE_API unsigned char *sqlcipher_sqlite3_serialize( - sqlcipher_sqlite3 *db, /* The database connection */ - const char *zSchema, /* Which database within the connection */ - sqlcipher_sqlite3_int64 *piSize, /* Write size here, if not NULL */ - unsigned int mFlags /* Maybe SQLITE_SERIALIZE_NOCOPY */ -){ - MemFile *p; - int iDb; - Btree *pBt; - sqlcipher_sqlite3_int64 sz; - int szPage = 0; - sqlcipher_sqlite3_stmt *pStmt = 0; - unsigned char *pOut; - char *zSql; - int rc; +static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){ + int rc = SQLITE_OK; /* Error code from journal finalization operation */ + int rc2 = SQLITE_OK; /* Error code from db file unlock operation */ -#ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlcipher_sqlite3SafetyCheckOk(db) ){ - (void)SQLITE_MISUSE_BKPT; - return 0; + /* Do nothing if the pager does not have an open write transaction + ** or at least a RESERVED lock. This function may be called when there + ** is no write-transaction active but a RESERVED or greater lock is + ** held under two circumstances: + ** + ** 1. After a successful hot-journal rollback, it is called with + ** eState==PAGER_NONE and eLock==EXCLUSIVE_LOCK. + ** + ** 2. If a connection with locking_mode=exclusive holding an EXCLUSIVE + ** lock switches back to locking_mode=normal and then executes a + ** read-transaction, this function is called with eState==PAGER_READER + ** and eLock==EXCLUSIVE_LOCK when the read-transaction is closed. + */ + assert( assert_pager_state(pPager) ); + assert( pPager->eState!=PAGER_ERROR ); + if( pPager->eStateeLockaDb[0].zDbSName; - p = memdbFromDbSchema(db, zSchema); - iDb = sqlcipher_sqlite3FindDbName(db, zSchema); - if( piSize ) *piSize = -1; - if( iDb<0 ) return 0; - if( p ){ - if( piSize ) *piSize = p->sz; - if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ - pOut = p->aData; - }else{ - pOut = sqlcipher_sqlite3_malloc64( p->sz ); - if( pOut ) memcpy(pOut, p->aData, p->sz); - } - return pOut; - } - pBt = db->aDb[iDb].pBt; - if( pBt==0 ) return 0; - szPage = sqlcipher_sqlite3BtreeGetPageSize(pBt); - zSql = sqlcipher_sqlite3_mprintf("PRAGMA \"%w\".page_count", zSchema); - rc = zSql ? sqlcipher_sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) : SQLITE_NOMEM; - sqlcipher_sqlite3_free(zSql); - if( rc ) return 0; - rc = sqlcipher_sqlite3_step(pStmt); - if( rc!=SQLITE_ROW ){ - pOut = 0; - }else{ - sz = sqlcipher_sqlite3_column_int64(pStmt, 0)*szPage; - if( piSize ) *piSize = sz; - if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ - pOut = 0; - }else{ - pOut = sqlcipher_sqlite3_malloc64( sz ); - if( pOut ){ - int nPage = sqlcipher_sqlite3_column_int(pStmt, 0); - Pager *pPager = sqlcipher_sqlite3BtreePager(pBt); - int pgno; - for(pgno=1; pgno<=nPage; pgno++){ - DbPage *pPage = 0; - unsigned char *pTo = pOut + szPage*(sqlcipher_sqlite3_int64)(pgno-1); - rc = sqlcipher_sqlite3PagerGet(pPager, pgno, (DbPage**)&pPage, 0); - if( rc==SQLITE_OK ){ - memcpy(pTo, sqlcipher_sqlite3PagerGetData(pPage), szPage); - }else{ - memset(pTo, 0, szPage); - } - sqlcipher_sqlite3PagerUnref(pPage); + releaseAllSavepoints(pPager); + assert( isOpen(pPager->jfd) || pPager->pInJournal==0 + || (sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_BATCH_ATOMIC) + ); + if( isOpen(pPager->jfd) ){ + assert( !pagerUseWal(pPager) ); + + /* Finalize the journal file. */ + if( sqlcipher_sqlite3JournalIsInMemory(pPager->jfd) ){ + /* assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); */ + sqlcipher_sqlite3OsClose(pPager->jfd); + }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ + if( pPager->journalOff==0 ){ + rc = SQLITE_OK; + }else{ + rc = sqlcipher_sqlite3OsTruncate(pPager->jfd, 0); + if( rc==SQLITE_OK && pPager->fullSync ){ + /* Make sure the new file size is written into the inode right away. + ** Otherwise the journal might resurrect following a power loss and + ** cause the last transaction to roll back. See + ** https://bugzilla.mozilla.org/show_bug.cgi?id=1072773 + */ + rc = sqlcipher_sqlite3OsSync(pPager->jfd, pPager->syncFlags); } } + pPager->journalOff = 0; + }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST + || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) + ){ + rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile); + pPager->journalOff = 0; + }else{ + /* This branch may be executed with Pager.journalMode==MEMORY if + ** a hot-journal was just rolled back. In this case the journal + ** file should be closed and deleted. If this connection writes to + ** the database file, it will do so using an in-memory journal. + */ + int bDelete = !pPager->tempFile; + assert( sqlcipher_sqlite3JournalIsInMemory(pPager->jfd)==0 ); + assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE + || pPager->journalMode==PAGER_JOURNALMODE_MEMORY + || pPager->journalMode==PAGER_JOURNALMODE_WAL + ); + sqlcipher_sqlite3OsClose(pPager->jfd); + if( bDelete ){ + rc = sqlcipher_sqlite3OsDelete(pPager->pVfs, pPager->zJournal, pPager->extraSync); + } } } - sqlcipher_sqlite3_finalize(pStmt); - return pOut; -} - -/* Convert zSchema to a MemDB and initialize its content. -*/ -SQLITE_API int sqlcipher_sqlite3_deserialize( - sqlcipher_sqlite3 *db, /* The database connection */ - const char *zSchema, /* Which DB to reopen with the deserialization */ - unsigned char *pData, /* The serialized database content */ - sqlcipher_sqlite3_int64 szDb, /* Number bytes in the deserialization */ - sqlcipher_sqlite3_int64 szBuf, /* Total size of buffer pData[] */ - unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ -){ - MemFile *p; - char *zSql; - sqlcipher_sqlite3_stmt *pStmt = 0; - int rc; - int iDb; -#ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlcipher_sqlite3SafetyCheckOk(db) ){ - return SQLITE_MISUSE_BKPT; +#ifdef SQLITE_CHECK_PAGES + sqlcipher_sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash); + if( pPager->dbSize==0 && sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)>0 ){ + PgHdr *p = sqlcipher_sqlite3PagerLookup(pPager, 1); + if( p ){ + p->pageHash = 0; + sqlcipher_sqlite3PagerUnrefNotNull(p); + } } - if( szDb<0 ) return SQLITE_MISUSE_BKPT; - if( szBuf<0 ) return SQLITE_MISUSE_BKPT; #endif - sqlcipher_sqlite3_mutex_enter(db->mutex); - if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; - iDb = sqlcipher_sqlite3FindDbName(db, zSchema); - if( iDb<0 ){ - rc = SQLITE_ERROR; - goto end_deserialize; - } - zSql = sqlcipher_sqlite3_mprintf("ATTACH x AS %Q", zSchema); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlcipher_sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - sqlcipher_sqlite3_free(zSql); + sqlcipher_sqlite3BitvecDestroy(pPager->pInJournal); + pPager->pInJournal = 0; + pPager->nRec = 0; + if( rc==SQLITE_OK ){ + if( MEMDB || pagerFlushOnCommit(pPager, bCommit) ){ + sqlcipher_sqlite3PcacheCleanAll(pPager->pPCache); + }else{ + sqlcipher_sqlite3PcacheClearWritable(pPager->pPCache); + } + sqlcipher_sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); } - if( rc ) goto end_deserialize; - db->init.iDb = (u8)iDb; - db->init.reopenMemdb = 1; - rc = sqlcipher_sqlite3_step(pStmt); - db->init.reopenMemdb = 0; - if( rc!=SQLITE_DONE ){ - rc = SQLITE_ERROR; - goto end_deserialize; + + if( pagerUseWal(pPager) ){ + /* Drop the WAL write-lock, if any. Also, if the connection was in + ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE + ** lock held on the database file. + */ + rc2 = sqlcipher_sqlite3WalEndWriteTransaction(pPager->pWal); + assert( rc2==SQLITE_OK ); + }else if( rc==SQLITE_OK && bCommit && pPager->dbFileSize>pPager->dbSize ){ + /* This branch is taken when committing a transaction in rollback-journal + ** mode if the database file on disk is larger than the database image. + ** At this point the journal has been finalized and the transaction + ** successfully committed, but the EXCLUSIVE lock is still held on the + ** file. So it is safe to truncate the database file to its minimum + ** required size. */ + assert( pPager->eLock==EXCLUSIVE_LOCK ); + rc = pager_truncate(pPager, pPager->dbSize); } - p = memdbFromDbSchema(db, zSchema); - if( p==0 ){ - rc = SQLITE_ERROR; - }else{ - p->aData = pData; - pData = 0; - p->sz = szDb; - p->szAlloc = szBuf; - p->szMax = szBuf; - if( p->szMaxszMax = sqlcipher_sqlite3GlobalConfig.mxMemdbSize; - } - p->mFlags = mFlags; - rc = SQLITE_OK; + + if( rc==SQLITE_OK && bCommit ){ + rc = sqlcipher_sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0); + if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; } -end_deserialize: - sqlcipher_sqlite3_finalize(pStmt); - if( pData && (mFlags & SQLITE_DESERIALIZE_FREEONCLOSE)!=0 ){ - sqlcipher_sqlite3_free(pData); + if( !pPager->exclusiveMode + && (!pagerUseWal(pPager) || sqlcipher_sqlite3WalExclusiveMode(pPager->pWal, 0)) + ){ + rc2 = pagerUnlockDb(pPager, SHARED_LOCK); } - sqlcipher_sqlite3_mutex_leave(db->mutex); - return rc; -} + pPager->eState = PAGER_READER; + pPager->setSuper = 0; -/* -** This routine is called when the extension is loaded. -** Register the new VFS. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3MemdbInit(void){ - sqlcipher_sqlite3_vfs *pLower = sqlcipher_sqlite3_vfs_find(0); - int sz = pLower->szOsFile; - memdb_vfs.pAppData = pLower; - /* The following conditional can only be true when compiled for - ** Windows x86 and SQLITE_MAX_MMAP_SIZE=0. We always leave - ** it in, to be safe, but it is marked as NO_TEST since there - ** is no way to reach it under most builds. */ - if( szeState!=PAGER_ERROR && pPager->eState!=PAGER_OPEN ){ + assert( assert_pager_state(pPager) ); + if( pPager->eState>=PAGER_WRITER_LOCKED ){ + sqlcipher_sqlite3BeginBenignMalloc(); + sqlcipher_sqlite3PagerRollback(pPager); + sqlcipher_sqlite3EndBenignMalloc(); + }else if( !pPager->exclusiveMode ){ + assert( pPager->eState==PAGER_READER ); + pager_end_transaction(pPager, 0, 0); + } + } + pager_unlock(pPager); +} -/* -** A bitmap is an instance of the following structure. -** -** This bitmap records the existence of zero or more bits -** with values between 1 and iSize, inclusive. +/* +** Parameter aData must point to a buffer of pPager->pageSize bytes +** of data. Compute and return a checksum based ont the contents of the +** page of data and the current value of pPager->cksumInit. ** -** There are three possible representations of the bitmap. -** If iSize<=BITVEC_NBIT, then Bitvec.u.aBitmap[] is a straight -** bitmap. The least significant bit is bit 1. +** This is not a real checksum. It is really just the sum of the +** random initial value (pPager->cksumInit) and every 200th byte +** of the page data, starting with byte offset (pPager->pageSize%200). +** Each byte is interpreted as an 8-bit unsigned integer. ** -** If iSize>BITVEC_NBIT and iDivisor==0 then Bitvec.u.aHash[] is -** a hash table that will hold up to BITVEC_MXHASH distinct values. +** Changing the formula used to compute this checksum results in an +** incompatible journal file format. ** -** Otherwise, the value i is redirected into one of BITVEC_NPTR -** sub-bitmaps pointed to by Bitvec.u.apSub[]. Each subbitmap -** handles up to iDivisor separate values of i. apSub[0] holds -** values between 1 and iDivisor. apSub[1] holds values between -** iDivisor+1 and 2*iDivisor. apSub[N] holds values between -** N*iDivisor+1 and (N+1)*iDivisor. Each subbitmap is normalized -** to hold deal with values between 1 and iDivisor. +** If journal corruption occurs due to a power failure, the most likely +** scenario is that one end or the other of the record will be changed. +** It is much less likely that the two ends of the journal record will be +** correct and the middle be corrupt. Thus, this "checksum" scheme, +** though fast and simple, catches the mostly likely kind of corruption. */ -struct Bitvec { - u32 iSize; /* Maximum bit index. Max iSize is 4,294,967,296. */ - u32 nSet; /* Number of bits that are set - only valid for aHash - ** element. Max is BITVEC_NINT. For BITVEC_SZ of 512, - ** this would be 125. */ - u32 iDivisor; /* Number of bits handled by each apSub[] entry. */ - /* Should >=0 for apSub element. */ - /* Max iDivisor is max(u32) / BITVEC_NPTR + 1. */ - /* For a BITVEC_SZ of 512, this would be 34,359,739. */ - union { - BITVEC_TELEM aBitmap[BITVEC_NELEM]; /* Bitmap representation */ - u32 aHash[BITVEC_NINT]; /* Hash table representation */ - Bitvec *apSub[BITVEC_NPTR]; /* Recursive representation */ - } u; -}; +static u32 pager_cksum(Pager *pPager, const u8 *aData){ + u32 cksum = pPager->cksumInit; /* Checksum value to return */ + int i = pPager->pageSize-200; /* Loop counter */ + while( i>0 ){ + cksum += aData[i]; + i -= 200; + } + return cksum; +} /* -** Create a new bitmap object able to handle bits between 0 and iSize, -** inclusive. Return a pointer to the new object. Return NULL if -** malloc fails. +** Report the current page size and number of reserved bytes back +** to the codec. */ -SQLITE_PRIVATE Bitvec *sqlcipher_sqlite3BitvecCreate(u32 iSize){ - Bitvec *p; - assert( sizeof(*p)==BITVEC_SZ ); - p = sqlcipher_sqlite3MallocZero( sizeof(*p) ); - if( p ){ - p->iSize = iSize; +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC +static void pagerReportSize(Pager *pPager){ + if( pPager->xCodecSizeChng ){ + pPager->xCodecSizeChng(pPager->pCodec, pPager->pageSize, + (int)pPager->nReserve); } - return p; } +#else +# define pagerReportSize(X) /* No-op if we do not support a codec */ +#endif +/* END SQLCIPHER */ +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC /* -** Check to see if the i-th bit is set. Return true or false. -** If p is NULL (if the bitmap has not been created) or if -** i is out of range, then return false. +** Make sure the number of reserved bits is the same in the destination +** pager as it is in the source. This comes up when a VACUUM changes the +** number of reserved bits to the "optimal" amount. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BitvecTestNotNull(Bitvec *p, u32 i){ - assert( p!=0 ); - i--; - if( i>=p->iSize ) return 0; - while( p->iDivisor ){ - u32 bin = i/p->iDivisor; - i = i%p->iDivisor; - p = p->u.apSub[bin]; - if (!p) { - return 0; - } - } - if( p->iSize<=BITVEC_NBIT ){ - return (p->u.aBitmap[i/BITVEC_SZELEM] & (1<<(i&(BITVEC_SZELEM-1))))!=0; - } else{ - u32 h = BITVEC_HASH(i++); - while( p->u.aHash[h] ){ - if( p->u.aHash[h]==i ) return 1; - h = (h+1) % BITVEC_NINT; - } - return 0; +SQLITE_PRIVATE void sqlcipher_sqlite3PagerAlignReserve(Pager *pDest, Pager *pSrc){ + if( pDest->nReserve!=pSrc->nReserve ){ + pDest->nReserve = pSrc->nReserve; + pagerReportSize(pDest); } } -SQLITE_PRIVATE int sqlcipher_sqlite3BitvecTest(Bitvec *p, u32 i){ - return p!=0 && sqlcipher_sqlite3BitvecTestNotNull(p,i); -} +#endif +/* END SQLCIPHER */ /* -** Set the i-th bit. Return 0 on success and an error code if -** anything goes wrong. +** Read a single page from either the journal file (if isMainJrnl==1) or +** from the sub-journal (if isMainJrnl==0) and playback that page. +** The page begins at offset *pOffset into the file. The *pOffset +** value is increased to the start of the next page in the journal. ** -** This routine might cause sub-bitmaps to be allocated. Failing -** to get the memory needed to hold the sub-bitmap is the only -** that can go wrong with an insert, assuming p and i are valid. +** The main rollback journal uses checksums - the statement journal does +** not. ** -** The calling function must ensure that p is a valid Bitvec object -** and that the value for "i" is within range of the Bitvec object. -** Otherwise the behavior is undefined. +** If the page number of the page record read from the (sub-)journal file +** is greater than the current value of Pager.dbSize, then playback is +** skipped and SQLITE_OK is returned. +** +** If pDone is not NULL, then it is a record of pages that have already +** been played back. If the page at *pOffset has already been played back +** (if the corresponding pDone bit is set) then skip the playback. +** Make sure the pDone bit corresponding to the *pOffset page is set +** prior to returning. +** +** If the page record is successfully read from the (sub-)journal file +** and played back, then SQLITE_OK is returned. If an IO error occurs +** while reading the record from the (sub-)journal file or while writing +** to the database file, then the IO error code is returned. If data +** is successfully read from the (sub-)journal file but appears to be +** corrupted, SQLITE_DONE is returned. Data is considered corrupted in +** two circumstances: +** +** * If the record page-number is illegal (0 or PAGER_SJ_PGNO), or +** * If the record is being rolled back from the main journal file +** and the checksum field does not match the record content. +** +** Neither of these two scenarios are possible during a savepoint rollback. +** +** If this is a savepoint rollback, then memory may have to be dynamically +** allocated by this function. If this is the case and an allocation fails, +** SQLITE_NOMEM is returned. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BitvecSet(Bitvec *p, u32 i){ - u32 h; - if( p==0 ) return SQLITE_OK; - assert( i>0 ); - assert( i<=p->iSize ); - i--; - while((p->iSize > BITVEC_NBIT) && p->iDivisor) { - u32 bin = i/p->iDivisor; - i = i%p->iDivisor; - if( p->u.apSub[bin]==0 ){ - p->u.apSub[bin] = sqlcipher_sqlite3BitvecCreate( p->iDivisor ); - if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM_BKPT; - } - p = p->u.apSub[bin]; +static int pager_playback_one_page( + Pager *pPager, /* The pager being played back */ + i64 *pOffset, /* Offset of record to playback */ + Bitvec *pDone, /* Bitvec of pages already played back */ + int isMainJrnl, /* 1 -> main journal. 0 -> sub-journal. */ + int isSavepnt /* True for a savepoint rollback */ +){ + int rc; + PgHdr *pPg; /* An existing page in the cache */ + Pgno pgno; /* The page number of a page in journal */ + u32 cksum; /* Checksum used for sanity checking */ + char *aData; /* Temporary storage for the page */ + sqlcipher_sqlite3_file *jfd; /* The file descriptor for the journal file */ + int isSynced; /* True if journal page is synced */ +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + /* The jrnlEnc flag is true if Journal pages should be passed through + ** the codec. It is false for pure in-memory journals. */ + const int jrnlEnc = (isMainJrnl || pPager->subjInMemory==0); +#endif +/* END SQLCIPHER */ + + assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ + assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ + assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ + assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ + + aData = pPager->pTmpSpace; + assert( aData ); /* Temp storage must have already been allocated */ + assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) ); + + /* Either the state is greater than PAGER_WRITER_CACHEMOD (a transaction + ** or savepoint rollback done at the request of the caller) or this is + ** a hot-journal rollback. If it is a hot-journal rollback, the pager + ** is in state OPEN and holds an EXCLUSIVE lock. Hot-journal rollback + ** only reads from the main journal, not the sub-journal. + */ + assert( pPager->eState>=PAGER_WRITER_CACHEMOD + || (pPager->eState==PAGER_OPEN && pPager->eLock==EXCLUSIVE_LOCK) + ); + assert( pPager->eState>=PAGER_WRITER_CACHEMOD || isMainJrnl ); + + /* Read the page number and page data from the journal or sub-journal + ** file. Return an error code to the caller if an IO error occurs. + */ + jfd = isMainJrnl ? pPager->jfd : pPager->sjfd; + rc = read32bits(jfd, *pOffset, &pgno); + if( rc!=SQLITE_OK ) return rc; + rc = sqlcipher_sqlite3OsRead(jfd, (u8*)aData, pPager->pageSize, (*pOffset)+4); + if( rc!=SQLITE_OK ) return rc; + *pOffset += pPager->pageSize + 4 + isMainJrnl*4; + + /* Sanity checking on the page. This is more important that I originally + ** thought. If a power failure occurs while the journal is being written, + ** it could cause invalid data to be written into the journal. We need to + ** detect this invalid data (with high probability) and ignore it. + */ + if( pgno==0 || pgno==PAGER_SJ_PGNO(pPager) ){ + assert( !isSavepnt ); + return SQLITE_DONE; } - if( p->iSize<=BITVEC_NBIT ){ - p->u.aBitmap[i/BITVEC_SZELEM] |= 1 << (i&(BITVEC_SZELEM-1)); + if( pgno>(Pgno)pPager->dbSize || sqlcipher_sqlite3BitvecTest(pDone, pgno) ){ return SQLITE_OK; } - h = BITVEC_HASH(i++); - /* if there wasn't a hash collision, and this doesn't */ - /* completely fill the hash, then just add it without */ - /* worring about sub-dividing and re-hashing. */ - if( !p->u.aHash[h] ){ - if (p->nSet<(BITVEC_NINT-1)) { - goto bitvec_set_end; - } else { - goto bitvec_set_rehash; + if( isMainJrnl ){ + rc = read32bits(jfd, (*pOffset)-4, &cksum); + if( rc ) return rc; + if( !isSavepnt && pager_cksum(pPager, (u8*)aData)!=cksum ){ + return SQLITE_DONE; } } - /* there was a collision, check to see if it's already */ - /* in hash, if not, try to find a spot for it */ - do { - if( p->u.aHash[h]==i ) return SQLITE_OK; - h++; - if( h>=BITVEC_NINT ) h = 0; - } while( p->u.aHash[h] ); - /* we didn't find it in the hash. h points to the first */ - /* available free spot. check to see if this is going to */ - /* make our hash too "full". */ -bitvec_set_rehash: - if( p->nSet>=BITVEC_MXHASH ){ - unsigned int j; - int rc; - u32 *aiValues = sqlcipher_sqlite3StackAllocRaw(0, sizeof(p->u.aHash)); - if( aiValues==0 ){ - return SQLITE_NOMEM_BKPT; - }else{ - memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); - memset(p->u.apSub, 0, sizeof(p->u.apSub)); - p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR; - rc = sqlcipher_sqlite3BitvecSet(p, i); - for(j=0; jnSet++; - p->u.aHash[h] = i; - return SQLITE_OK; -} -/* -** Clear the i-th bit. -** -** pBuf must be a pointer to at least BITVEC_SZ bytes of temporary storage -** that BitvecClear can use to rebuilt its hash table. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3BitvecClear(Bitvec *p, u32 i, void *pBuf){ - if( p==0 ) return; - assert( i>0 ); - i--; - while( p->iDivisor ){ - u32 bin = i/p->iDivisor; - i = i%p->iDivisor; - p = p->u.apSub[bin]; - if (!p) { - return; - } + /* When playing back page 1, restore the nReserve setting + */ + if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){ + pPager->nReserve = ((u8*)aData)[20]; + pagerReportSize(pPager); } - if( p->iSize<=BITVEC_NBIT ){ - p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1))); + + /* If the pager is in CACHEMOD state, then there must be a copy of this + ** page in the pager cache. In this case just update the pager cache, + ** not the database file. The page is left marked dirty in this case. + ** + ** An exception to the above rule: If the database is in no-sync mode + ** and a page is moved during an incremental vacuum then the page may + ** not be in the pager cache. Later: if a malloc() or IO error occurs + ** during a Movepage() call, then the page may not be in the cache + ** either. So the condition described in the above paragraph is not + ** assert()able. + ** + ** If in WRITER_DBMOD, WRITER_FINISHED or OPEN state, then we update the + ** pager cache if it exists and the main file. The page is then marked + ** not dirty. Since this code is only executed in PAGER_OPEN state for + ** a hot-journal rollback, it is guaranteed that the page-cache is empty + ** if the pager is in OPEN state. + ** + ** Ticket #1171: The statement journal might contain page content that is + ** different from the page content at the start of the transaction. + ** This occurs when a page is changed prior to the start of a statement + ** then changed again within the statement. When rolling back such a + ** statement we must not write to the original database unless we know + ** for certain that original page contents are synced into the main rollback + ** journal. Otherwise, a power loss might leave modified data in the + ** database file without an entry in the rollback journal that can + ** restore the database to its original form. Two conditions must be + ** met before writing to the database files. (1) the database must be + ** locked. (2) we know that the original page content is fully synced + ** in the main journal either because the page is not in cache or else + ** the page is marked as needSync==0. + ** + ** 2008-04-14: When attempting to vacuum a corrupt database file, it + ** is possible to fail a statement on a database that does not yet exist. + ** Do not attempt to write if database file has never been opened. + */ + if( pagerUseWal(pPager) ){ + pPg = 0; }else{ - unsigned int j; - u32 *aiValues = pBuf; - memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); - memset(p->u.aHash, 0, sizeof(p->u.aHash)); - p->nSet = 0; - for(j=0; jnSet++; - while( p->u.aHash[h] ){ - h++; - if( h>=BITVEC_NINT ) h = 0; - } - p->u.aHash[h] = aiValues[j]; - } - } + pPg = sqlcipher_sqlite3PagerLookup(pPager, pgno); } -} + assert( pPg || !MEMDB ); + assert( pPager->eState!=PAGER_OPEN || pPg==0 || pPager->tempFile ); + PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", + PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData), + (isMainJrnl?"main-journal":"sub-journal") + )); + if( isMainJrnl ){ + isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr); + }else{ + isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC)); + } + if( isOpen(pPager->fd) + && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) + && isSynced + ){ + i64 ofst = (pgno-1)*(i64)pPager->pageSize; + testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 ); + assert( !pagerUseWal(pPager) ); -/* -** Destroy a bitmap object. Reclaim all memory used. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3BitvecDestroy(Bitvec *p){ - if( p==0 ) return; - if( p->iDivisor ){ - unsigned int i; - for(i=0; iu.apSub[i]); + /* Write the data read from the journal back into the database file. + ** This is usually safe even for an encrypted database - as the data + ** was encrypted before it was written to the journal file. The exception + ** is if the data was just read from an in-memory sub-journal. In that + ** case it must be encrypted here before it is copied into the database + ** file. */ +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + if( !jrnlEnc ){ + CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData); + rc = sqlcipher_sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); + CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); + }else +#endif +/* END SQLCIPHER */ + rc = sqlcipher_sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); + + if( pgno>pPager->dbFileSize ){ + pPager->dbFileSize = pgno; + } + if( pPager->pBackup ){ +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + if( jrnlEnc ){ + CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); + sqlcipher_sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); + CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT,aData); + }else +#endif +/* END SQLCIPHER */ + sqlcipher_sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); } + }else if( !isMainJrnl && pPg==0 ){ + /* If this is a rollback of a savepoint and data was not written to + ** the database and the page is not in-memory, there is a potential + ** problem. When the page is next fetched by the b-tree layer, it + ** will be read from the database file, which may or may not be + ** current. + ** + ** There are a couple of different ways this can happen. All are quite + ** obscure. When running in synchronous mode, this can only happen + ** if the page is on the free-list at the start of the transaction, then + ** populated, then moved using sqlcipher_sqlite3PagerMovepage(). + ** + ** The solution is to add an in-memory page to the cache containing + ** the data just read from the sub-journal. Mark the page as dirty + ** and if the pager requires a journal-sync, then mark the page as + ** requiring a journal-sync before it is written. + */ + assert( isSavepnt ); + assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)==0 ); + pPager->doNotSpill |= SPILLFLAG_ROLLBACK; + rc = sqlcipher_sqlite3PagerGet(pPager, pgno, &pPg, 1); + assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 ); + pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK; + if( rc!=SQLITE_OK ) return rc; + sqlcipher_sqlite3PcacheMakeDirty(pPg); } - sqlcipher_sqlite3_free(p); -} + if( pPg ){ + /* No page should ever be explicitly rolled back that is in use, except + ** for page 1 which is held in use in order to keep the lock on the + ** database active. However such a page may be rolled back as a result + ** of an internal error resulting in an automatic call to + ** sqlcipher_sqlite3PagerRollback(). + */ + void *pData; + pData = pPg->pData; + memcpy(pData, (u8*)aData, pPager->pageSize); + pPager->xReiniter(pPg); + /* It used to be that sqlcipher_sqlite3PcacheMakeClean(pPg) was called here. But + ** that call was dangerous and had no detectable benefit since the cache + ** is normally cleaned by sqlcipher_sqlite3PcacheCleanAll() after rollback and so + ** has been removed. */ + pager_set_pagehash(pPg); -/* -** Return the value of the iSize parameter specified when Bitvec *p -** was created. -*/ -SQLITE_PRIVATE u32 sqlcipher_sqlite3BitvecSize(Bitvec *p){ - return p->iSize; -} + /* If this was page 1, then restore the value of Pager.dbFileVers. + ** Do this before any decoding. */ + if( pgno==1 ){ + memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); + } -#ifndef SQLITE_UNTESTABLE -/* -** Let V[] be an array of unsigned characters sufficient to hold -** up to N bits. Let I be an integer between 0 and N. 0<=I>3] |= (1<<(I&7)) -#define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7)) -#define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0 + /* Decode the page just read from disk */ +/* BEGIN SQLCIPHER */ +#if SQLITE_HAS_CODEC + if( jrnlEnc ){ CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT); } +#endif +/* END SQLCIPHER */ + sqlcipher_sqlite3PcacheRelease(pPg); + } + return rc; +} /* -** This routine runs an extensive test of the Bitvec code. +** Parameter zSuper is the name of a super-journal file. A single journal +** file that referred to the super-journal file has just been rolled back. +** This routine checks if it is possible to delete the super-journal file, +** and does so if it is. ** -** The input is an array of integers that acts as a program -** to test the Bitvec. The integers are opcodes followed -** by 0, 1, or 3 operands, depending on the opcode. Another -** opcode follows immediately after the last operand. +** Argument zSuper may point to Pager.pTmpSpace. So that buffer is not +** available for use within this function. ** -** There are 6 opcodes numbered from 0 through 5. 0 is the -** "halt" opcode and causes the test to end. +** When a super-journal file is created, it is populated with the names +** of all of its child journals, one after another, formatted as utf-8 +** encoded text. The end of each child journal file is marked with a +** nul-terminator byte (0x00). i.e. the entire contents of a super-journal +** file for a transaction involving two databases might be: ** -** 0 Halt and return the number of errors -** 1 N S X Set N bits beginning with S and incrementing by X -** 2 N S X Clear N bits beginning with S and incrementing by X -** 3 N Set N randomly chosen bits -** 4 N Clear N randomly chosen bits -** 5 N S X Set N bits from S increment X in array only, not in bitvec +** "/home/bill/a.db-journal\x00/home/bill/b.db-journal\x00" ** -** The opcodes 1 through 4 perform set and clear operations are performed -** on both a Bitvec object and on a linear array of bits obtained from malloc. -** Opcode 5 works on the linear array only, not on the Bitvec. -** Opcode 5 is used to deliberately induce a fault in order to -** confirm that error detection works. +** A super-journal file may only be deleted once all of its child +** journals have been rolled back. ** -** At the conclusion of the test the linear array is compared -** against the Bitvec object. If there are any differences, -** an error is returned. If they are the same, zero is returned. +** This function reads the contents of the super-journal file into +** memory and loops through each of the child journal names. For +** each child journal, it checks if: ** -** If a memory allocation error occurs, return -1. +** * if the child journal exists, and if so +** * if the child journal contains a reference to super-journal +** file zSuper +** +** If a child journal can be found that matches both of the criteria +** above, this function returns without doing anything. Otherwise, if +** no such child journal can be found, file zSuper is deleted from +** the file-system using sqlcipher_sqlite3OsDelete(). +** +** If an IO error within this function, an error code is returned. This +** function allocates memory by calling sqlcipher_sqlite3Malloc(). If an allocation +** fails, SQLITE_NOMEM is returned. Otherwise, if no IO or malloc errors +** occur, SQLITE_OK is returned. +** +** TODO: This function allocates a single block of memory to load +** the entire contents of the super-journal file. This could be +** a couple of kilobytes or so - potentially larger than the page +** size. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BitvecBuiltinTest(int sz, int *aOp){ - Bitvec *pBitvec = 0; - unsigned char *pV = 0; - int rc = -1; - int i, nx, pc, op; - void *pTmpSpace; +static int pager_delsuper(Pager *pPager, const char *zSuper){ + sqlcipher_sqlite3_vfs *pVfs = pPager->pVfs; + int rc; /* Return code */ + sqlcipher_sqlite3_file *pSuper; /* Malloc'd super-journal file descriptor */ + sqlcipher_sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ + char *zSuperJournal = 0; /* Contents of super-journal file */ + i64 nSuperJournal; /* Size of super-journal file */ + char *zJournal; /* Pointer to one journal within MJ file */ + char *zSuperPtr; /* Space to hold super-journal filename */ + char *zFree = 0; /* Free this buffer */ + int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ - /* Allocate the Bitvec to be tested and a linear array of - ** bits to act as the reference */ - pBitvec = sqlcipher_sqlite3BitvecCreate( sz ); - pV = sqlcipher_sqlite3MallocZero( (sz+7)/8 + 1 ); - pTmpSpace = sqlcipher_sqlite3_malloc64(BITVEC_SZ); - if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end; + /* Allocate space for both the pJournal and pSuper file descriptors. + ** If successful, open the super-journal file for reading. + */ + pSuper = (sqlcipher_sqlite3_file *)sqlcipher_sqlite3MallocZero(pVfs->szOsFile * 2); + if( !pSuper ){ + rc = SQLITE_NOMEM_BKPT; + pJournal = 0; + }else{ + const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL); + rc = sqlcipher_sqlite3OsOpen(pVfs, zSuper, pSuper, flags, 0); + pJournal = (sqlcipher_sqlite3_file *)(((u8 *)pSuper) + pVfs->szOsFile); + } + if( rc!=SQLITE_OK ) goto delsuper_out; - /* NULL pBitvec tests */ - sqlcipher_sqlite3BitvecSet(0, 1); - sqlcipher_sqlite3BitvecClear(0, 1, pTmpSpace); + /* Load the entire super-journal file into space obtained from + ** sqlcipher_sqlite3_malloc() and pointed to by zSuperJournal. Also obtain + ** sufficient space (in zSuperPtr) to hold the names of super-journal + ** files extracted from regular rollback-journals. + */ + rc = sqlcipher_sqlite3OsFileSize(pSuper, &nSuperJournal); + if( rc!=SQLITE_OK ) goto delsuper_out; + nSuperPtr = pVfs->mxPathname+1; + zFree = sqlcipher_sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2); + if( !zFree ){ + rc = SQLITE_NOMEM_BKPT; + goto delsuper_out; + } + zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0; + zSuperJournal = &zFree[4]; + zSuperPtr = &zSuperJournal[nSuperJournal+2]; + rc = sqlcipher_sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); + if( rc!=SQLITE_OK ) goto delsuper_out; + zSuperJournal[nSuperJournal] = 0; + zSuperJournal[nSuperJournal+1] = 0; - /* Run the program */ - pc = 0; - while( (op = aOp[pc])!=0 ){ - switch( op ){ - case 1: - case 2: - case 5: { - nx = 4; - i = aOp[pc+2] - 1; - aOp[pc+2] += aOp[pc+3]; - break; + zJournal = zSuperJournal; + while( (zJournal-zSuperJournal) 0 ) nx = 0; - pc += nx; - i = (i & 0x7fffffff)%sz; - if( (op & 1)!=0 ){ - SETBIT(pV, (i+1)); - if( op!=5 ){ - if( sqlcipher_sqlite3BitvecSet(pBitvec, i+1) ) goto bitvec_end; + + c = zSuperPtr[0]!=0 && strcmp(zSuperPtr, zSuper)==0; + if( c ){ + /* We have a match. Do not delete the super-journal file. */ + goto delsuper_out; } - }else{ - CLEARBIT(pV, (i+1)); - sqlcipher_sqlite3BitvecClear(pBitvec, i+1, pTmpSpace); } + zJournal += (sqlcipher_sqlite3Strlen30(zJournal)+1); } - /* Test to make sure the linear array exactly matches the - ** Bitvec object. Start with the assumption that they do - ** match (rc==0). Change rc to non-zero if a discrepancy - ** is found. - */ - rc = sqlcipher_sqlite3BitvecTest(0,0) + sqlcipher_sqlite3BitvecTest(pBitvec, sz+1) - + sqlcipher_sqlite3BitvecTest(pBitvec, 0) - + (sqlcipher_sqlite3BitvecSize(pBitvec) - sz); - for(i=1; i<=sz; i++){ - if( (TESTBIT(pV,i))!=sqlcipher_sqlite3BitvecTest(pBitvec,i) ){ - rc = i; - break; - } - } + sqlcipher_sqlite3OsClose(pSuper); + rc = sqlcipher_sqlite3OsDelete(pVfs, zSuper, 0); - /* Free allocated structure */ -bitvec_end: - sqlcipher_sqlite3_free(pTmpSpace); - sqlcipher_sqlite3_free(pV); - sqlcipher_sqlite3BitvecDestroy(pBitvec); +delsuper_out: + sqlcipher_sqlite3_free(zFree); + if( pSuper ){ + sqlcipher_sqlite3OsClose(pSuper); + assert( !isOpen(pJournal) ); + sqlcipher_sqlite3_free(pSuper); + } return rc; } -#endif /* SQLITE_UNTESTABLE */ -/************** End of bitvec.c **********************************************/ -/************** Begin file pcache.c ******************************************/ + /* -** 2008 August 05 +** This function is used to change the actual size of the database +** file in the file-system. This only happens when committing a transaction, +** or rolling back a transaction (including rolling back a hot-journal). ** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** If the main database file is not open, or the pager is not in either +** DBMOD or OPEN state, this function is a no-op. Otherwise, the size +** of the file is changed to nPage pages (nPage*pPager->pageSize bytes). +** If the file on disk is currently larger than nPage pages, then use the VFS +** xTruncate() method to truncate it. ** -** 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. +** Or, it might be the case that the file on disk is smaller than +** nPage pages. Some operating system implementations can get confused if +** you try to truncate a file to some size that is larger than it +** currently is, so detect this case and write a single zero byte to +** the end of the new file instead. ** -************************************************************************* -** This file implements that page cache. +** If successful, return SQLITE_OK. If an IO error occurs while modifying +** the database file, return the error code to the caller. */ -/* #include "sqliteInt.h" */ +static int pager_truncate(Pager *pPager, Pgno nPage){ + int rc = SQLITE_OK; + assert( pPager->eState!=PAGER_ERROR ); + assert( pPager->eState!=PAGER_READER ); + + if( isOpen(pPager->fd) + && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) + ){ + i64 currentSize, newSize; + int szPage = pPager->pageSize; + assert( pPager->eLock==EXCLUSIVE_LOCK ); + /* TODO: Is it safe to use Pager.dbFileSize here? */ + rc = sqlcipher_sqlite3OsFileSize(pPager->fd, ¤tSize); + newSize = szPage*(i64)nPage; + if( rc==SQLITE_OK && currentSize!=newSize ){ + if( currentSize>newSize ){ + rc = sqlcipher_sqlite3OsTruncate(pPager->fd, newSize); + }else if( (currentSize+szPage)<=newSize ){ + char *pTmp = pPager->pTmpSpace; + memset(pTmp, 0, szPage); + testcase( (newSize-szPage) == currentSize ); + testcase( (newSize-szPage) > currentSize ); + sqlcipher_sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &newSize); + rc = sqlcipher_sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); + } + if( rc==SQLITE_OK ){ + pPager->dbFileSize = nPage; + } + } + } + return rc; +} /* -** A complete page cache is an instance of this structure. Every -** entry in the cache holds a single page of the database file. The -** btree layer only operates on the cached copy of the database pages. -** -** A page cache entry is "clean" if it exactly matches what is currently -** on disk. A page is "dirty" if it has been modified and needs to be -** persisted to disk. -** -** pDirty, pDirtyTail, pSynced: -** All dirty pages are linked into the doubly linked list using -** PgHdr.pDirtyNext and pDirtyPrev. The list is maintained in LRU order -** such that p was added to the list more recently than p->pDirtyNext. -** PCache.pDirty points to the first (newest) element in the list and -** pDirtyTail to the last (oldest). -** -** The PCache.pSynced variable is used to optimize searching for a dirty -** page to eject from the cache mid-transaction. It is better to eject -** a page that does not require a journal sync than one that does. -** Therefore, pSynced is maintained so that it *almost* always points -** to either the oldest page in the pDirty/pDirtyTail list that has a -** clear PGHDR_NEED_SYNC flag or to a page that is older than this one -** (so that the right page to eject can be found by following pDirtyPrev -** pointers). +** Return a sanitized version of the sector-size of OS file pFile. The +** return value is guaranteed to lie between 32 and MAX_SECTOR_SIZE. */ -struct PCache { - PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ - PgHdr *pSynced; /* Last synced page in dirty page list */ - int nRefSum; /* Sum of ref counts over all pages */ - int szCache; /* Configured cache size */ - int szSpill; /* Size before spilling occurs */ - int szPage; /* Size of every page in this cache */ - int szExtra; /* Size of extra space for each page */ - u8 bPurgeable; /* True if pages are on backing store */ - u8 eCreate; /* eCreate value for for xFetch() */ - int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */ - void *pStress; /* Argument to xStress */ - sqlcipher_sqlite3_pcache *pCache; /* Pluggable cache module */ -}; +SQLITE_PRIVATE int sqlcipher_sqlite3SectorSize(sqlcipher_sqlite3_file *pFile){ + int iRet = sqlcipher_sqlite3OsSectorSize(pFile); + if( iRet<32 ){ + iRet = 512; + }else if( iRet>MAX_SECTOR_SIZE ){ + assert( MAX_SECTOR_SIZE>=512 ); + iRet = MAX_SECTOR_SIZE; + } + return iRet; +} -/********************************** Test and Debug Logic **********************/ /* -** Debug tracing macros. Enable by by changing the "0" to "1" and -** recompiling. +** Set the value of the Pager.sectorSize variable for the given +** pager based on the value returned by the xSectorSize method +** of the open database file. The sector size will be used +** to determine the size and alignment of journal header and +** super-journal pointers within created journal files. ** -** When sqlcipher_sqlite3PcacheTrace is 1, single line trace messages are issued. -** When sqlcipher_sqlite3PcacheTrace is 2, a dump of the pcache showing all cache entries -** is displayed for many operations, resulting in a lot of output. +** For temporary files the effective sector size is always 512 bytes. +** +** Otherwise, for non-temporary files, the effective sector size is +** the value returned by the xSectorSize() method rounded up to 32 if +** it is less than 32, or rounded down to MAX_SECTOR_SIZE if it +** is greater than MAX_SECTOR_SIZE. +** +** If the file has the SQLITE_IOCAP_POWERSAFE_OVERWRITE property, then set +** the effective sector size to its minimum value (512). The purpose of +** pPager->sectorSize is to define the "blast radius" of bytes that +** might change if a crash occurs while writing to a single byte in +** that range. But with POWERSAFE_OVERWRITE, the blast radius is zero +** (that is what POWERSAFE_OVERWRITE means), so we minimize the sector +** size. For backwards compatibility of the rollback journal file format, +** we cannot reduce the effective sector size below 512. */ -#if defined(SQLITE_DEBUG) && 0 - int sqlcipher_sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */ - int sqlcipher_sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */ -# define pcacheTrace(X) if(sqlcipher_sqlite3PcacheTrace){sqlcipher_sqlite3DebugPrintf X;} - void pcacheDump(PCache *pCache){ - int N; - int i, j; - sqlcipher_sqlite3_pcache_page *pLower; - PgHdr *pPg; - unsigned char *a; +static void setSectorSize(Pager *pPager){ + assert( isOpen(pPager->fd) || pPager->tempFile ); - if( sqlcipher_sqlite3PcacheTrace<2 ) return; - if( pCache->pCache==0 ) return; - N = sqlcipher_sqlite3PcachePagecount(pCache); - if( N>sqlcipher_sqlite3PcacheMxDump ) N = sqlcipher_sqlite3PcacheMxDump; - for(i=1; i<=N; i++){ - pLower = sqlcipher_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0); - if( pLower==0 ) continue; - pPg = (PgHdr*)pLower->pExtra; - printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); - a = (unsigned char *)pLower->pBuf; - for(j=0; j<12; j++) printf("%02x", a[j]); - printf("\n"); - if( pPg->pPage==0 ){ - sqlcipher_sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0); - } - } + if( pPager->tempFile + || (sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd) & + SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0 + ){ + /* Sector size doesn't matter for temporary files. Also, the file + ** may not have been opened yet, in which case the OsSectorSize() + ** call will segfault. */ + pPager->sectorSize = 512; + }else{ + pPager->sectorSize = sqlcipher_sqlite3SectorSize(pPager->fd); } - #else -# define pcacheTrace(X) -# define pcacheDump(X) -#endif +} /* -** Check invariants on a PgHdr entry. Return true if everything is OK. -** Return false if any invariant is violated. +** Playback the journal and thus restore the database file to +** the state it was in before we started making changes. ** -** This routine is for use inside of assert() statements only. For -** example: +** The journal file format is as follows: ** -** assert( sqlcipher_sqlite3PcachePageSanity(pPg) ); +** (1) 8 byte prefix. A copy of aJournalMagic[]. +** (2) 4 byte big-endian integer which is the number of valid page records +** in the journal. If this value is 0xffffffff, then compute the +** number of page records from the journal size. +** (3) 4 byte big-endian integer which is the initial value for the +** sanity checksum. +** (4) 4 byte integer which is the number of pages to truncate the +** database to during a rollback. +** (5) 4 byte big-endian integer which is the sector size. The header +** is this many bytes in size. +** (6) 4 byte big-endian integer which is the page size. +** (7) zero padding out to the next sector size. +** (8) Zero or more pages instances, each as follows: +** + 4 byte page number. +** + pPager->pageSize bytes of data. +** + 4 byte checksum +** +** When we speak of the journal header, we mean the first 7 items above. +** Each entry in the journal is an instance of the 8th item. +** +** Call the value from the second bullet "nRec". nRec is the number of +** valid page entries in the journal. In most cases, you can compute the +** value of nRec from the size of the journal file. But if a power +** failure occurred while the journal was being written, it could be the +** case that the size of the journal file had already been increased but +** the extra entries had not yet made it safely to disk. In such a case, +** the value of nRec computed from the file size would be too large. For +** that reason, we always use the nRec value in the header. +** +** If the nRec value is 0xffffffff it means that nRec should be computed +** from the file size. This value is used when the user selects the +** no-sync option for the journal. A power failure could lead to corruption +** in this case. But for things like temporary table (which will be +** deleted when the power is restored) we don't care. +** +** If the file opened as the journal file is not a well-formed +** journal file then all pages up to the first corrupted page are rolled +** back (or no pages if the journal header is corrupted). The journal file +** is then deleted and SQLITE_OK returned, just as if no corruption had +** been encountered. +** +** If an I/O or malloc() error occurs, the journal-file is not deleted +** and an error code is returned. +** +** The isHot parameter indicates that we are trying to rollback a journal +** that might be a hot journal. Or, it could be that the journal is +** preserved because of JOURNALMODE_PERSIST or JOURNALMODE_TRUNCATE. +** If the journal really is hot, reset the pager cache prior rolling +** back any content. If the journal is merely persistent, no reset is +** needed. */ -#ifdef SQLITE_DEBUG -SQLITE_PRIVATE int sqlcipher_sqlite3PcachePageSanity(PgHdr *pPg){ - PCache *pCache; - assert( pPg!=0 ); - assert( pPg->pgno>0 || pPg->pPager==0 ); /* Page number is 1 or more */ - pCache = pPg->pCache; - assert( pCache!=0 ); /* Every page has an associated PCache */ - if( pPg->flags & PGHDR_CLEAN ){ - assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */ - assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */ - assert( pCache->pDirtyTail!=pPg ); - } - /* WRITEABLE pages must also be DIRTY */ - if( pPg->flags & PGHDR_WRITEABLE ){ - assert( pPg->flags & PGHDR_DIRTY ); /* WRITEABLE implies DIRTY */ +static int pager_playback(Pager *pPager, int isHot){ + sqlcipher_sqlite3_vfs *pVfs = pPager->pVfs; + i64 szJ; /* Size of the journal file in bytes */ + u32 nRec; /* Number of Records in the journal */ + u32 u; /* Unsigned loop counter */ + Pgno mxPg = 0; /* Size of the original file in pages */ + int rc; /* Result code of a subroutine */ + int res = 1; /* Value returned by sqlcipher_sqlite3OsAccess() */ + char *zSuper = 0; /* Name of super-journal file if any */ + int needPagerReset; /* True to reset page prior to first page rollback */ + int nPlayback = 0; /* Total number of pages restored from journal */ + u32 savedPageSize = pPager->pageSize; + + /* Figure out how many records are in the journal. Abort early if + ** the journal is empty. + */ + assert( isOpen(pPager->jfd) ); + rc = sqlcipher_sqlite3OsFileSize(pPager->jfd, &szJ); + if( rc!=SQLITE_OK ){ + goto end_playback; } - /* NEED_SYNC can be set independently of WRITEABLE. This can happen, - ** for example, when using the sqlcipher_sqlite3PagerDontWrite() optimization: - ** (1) Page X is journalled, and gets WRITEABLE and NEED_SEEK. - ** (2) Page X moved to freelist, WRITEABLE is cleared - ** (3) Page X reused, WRITEABLE is set again - ** If NEED_SYNC had been cleared in step 2, then it would not be reset - ** in step 3, and page might be written into the database without first - ** syncing the rollback journal, which might cause corruption on a power - ** loss. + + /* Read the super-journal name from the journal, if it is present. + ** If a super-journal file name is specified, but the file is not + ** present on disk, then the journal is not hot and does not need to be + ** played back. ** - ** Another example is when the database page size is smaller than the - ** disk sector size. When any page of a sector is journalled, all pages - ** in that sector are marked NEED_SYNC even if they are still CLEAN, just - ** in case they are later modified, since all pages in the same sector - ** must be journalled and synced before any of those pages can be safely - ** written. + ** TODO: Technically the following is an error because it assumes that + ** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that + ** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c, + ** mxPathname is 512, which is the same as the minimum allowable value + ** for pageSize. */ - return 1; -} -#endif /* SQLITE_DEBUG */ - - -/********************************** Linked List Management ********************/ - -/* Allowed values for second argument to pcacheManageDirtyList() */ -#define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */ -#define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */ -#define PCACHE_DIRTYLIST_FRONT 3 /* Move pPage to the front of the list */ - -/* -** Manage pPage's participation on the dirty list. Bits of the addRemove -** argument determines what operation to do. The 0x01 bit means first -** remove pPage from the dirty list. The 0x02 means add pPage back to -** the dirty list. Doing both moves pPage to the front of the dirty list. -*/ -static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){ - PCache *p = pPage->pCache; - - pcacheTrace(("%p.DIRTYLIST.%s %d\n", p, - addRemove==1 ? "REMOVE" : addRemove==2 ? "ADD" : "FRONT", - pPage->pgno)); - if( addRemove & PCACHE_DIRTYLIST_REMOVE ){ - assert( pPage->pDirtyNext || pPage==p->pDirtyTail ); - assert( pPage->pDirtyPrev || pPage==p->pDirty ); + zSuper = pPager->pTmpSpace; + rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); + if( rc==SQLITE_OK && zSuper[0] ){ + rc = sqlcipher_sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res); + } + zSuper = 0; + if( rc!=SQLITE_OK || !res ){ + goto end_playback; + } + pPager->journalOff = 0; + needPagerReset = isHot; - /* Update the PCache1.pSynced variable if necessary. */ - if( p->pSynced==pPage ){ - p->pSynced = pPage->pDirtyPrev; + /* This loop terminates either when a readJournalHdr() or + ** pager_playback_one_page() call returns SQLITE_DONE or an IO error + ** occurs. + */ + while( 1 ){ + /* Read the next journal header from the journal file. If there are + ** not enough bytes left in the journal file for a complete header, or + ** it is corrupted, then a process must have failed while writing it. + ** This indicates nothing more needs to be rolled back. + */ + rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; + } + goto end_playback; } - if( pPage->pDirtyNext ){ - pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev; - }else{ - assert( pPage==p->pDirtyTail ); - p->pDirtyTail = pPage->pDirtyPrev; + /* If nRec is 0xffffffff, then this journal was created by a process + ** working in no-sync mode. This means that the rest of the journal + ** file consists of pages, there are no more journal headers. Compute + ** the value of nRec based on this assumption. + */ + if( nRec==0xffffffff ){ + assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ); + nRec = (int)((szJ - JOURNAL_HDR_SZ(pPager))/JOURNAL_PG_SZ(pPager)); } - if( pPage->pDirtyPrev ){ - pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext; - }else{ - /* If there are now no dirty pages in the cache, set eCreate to 2. - ** This is an optimization that allows sqlcipher_sqlite3PcacheFetch() to skip - ** searching for a dirty page to eject from the cache when it might - ** otherwise have to. */ - assert( pPage==p->pDirty ); - p->pDirty = pPage->pDirtyNext; - assert( p->bPurgeable || p->eCreate==2 ); - if( p->pDirty==0 ){ /*OPTIMIZATION-IF-TRUE*/ - assert( p->bPurgeable==0 || p->eCreate==1 ); - p->eCreate = 2; - } + + /* If nRec is 0 and this rollback is of a transaction created by this + ** process and if this is the final header in the journal, then it means + ** that this part of the journal was being filled but has not yet been + ** synced to disk. Compute the number of pages based on the remaining + ** size of the file. + ** + ** The third term of the test was added to fix ticket #2565. + ** When rolling back a hot journal, nRec==0 always means that the next + ** chunk of the journal contains zero pages to be rolled back. But + ** when doing a ROLLBACK and the nRec==0 chunk is the last chunk in + ** the journal, it means that the journal might contain additional + ** pages that need to be rolled back and that the number of pages + ** should be computed based on the journal file size. + */ + if( nRec==0 && !isHot && + pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ + nRec = (int)((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager)); } - } - if( addRemove & PCACHE_DIRTYLIST_ADD ){ - pPage->pDirtyPrev = 0; - pPage->pDirtyNext = p->pDirty; - if( pPage->pDirtyNext ){ - assert( pPage->pDirtyNext->pDirtyPrev==0 ); - pPage->pDirtyNext->pDirtyPrev = pPage; - }else{ - p->pDirtyTail = pPage; - if( p->bPurgeable ){ - assert( p->eCreate==2 ); - p->eCreate = 1; + + /* If this is the first header read from the journal, truncate the + ** database file back to its original size. + */ + if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){ + rc = pager_truncate(pPager, mxPg); + if( rc!=SQLITE_OK ){ + goto end_playback; + } + pPager->dbSize = mxPg; + if( pPager->mxPgnomxPgno = mxPg; } } - p->pDirty = pPage; - /* If pSynced is NULL and this page has a clear NEED_SYNC flag, set - ** pSynced to point to it. Checking the NEED_SYNC flag is an - ** optimization, as if pSynced points to a page with the NEED_SYNC - ** flag set sqlcipher_sqlite3PcacheFetchStress() searches through all newer - ** entries of the dirty-list for a page with NEED_SYNC clear anyway. */ - if( !p->pSynced - && 0==(pPage->flags&PGHDR_NEED_SYNC) /*OPTIMIZATION-IF-FALSE*/ - ){ - p->pSynced = pPage; + /* Copy original pages out of the journal and back into the + ** database file and/or page cache. + */ + for(u=0; ujournalOff,0,1,0); + if( rc==SQLITE_OK ){ + nPlayback++; + }else{ + if( rc==SQLITE_DONE ){ + pPager->journalOff = szJ; + break; + }else if( rc==SQLITE_IOERR_SHORT_READ ){ + /* If the journal has been truncated, simply stop reading and + ** processing the journal. This might happen if the journal was + ** not completely written and synced prior to a crash. In that + ** case, the database should have never been written in the + ** first place so it is OK to simply abandon the rollback. */ + rc = SQLITE_OK; + goto end_playback; + }else{ + /* If we are unable to rollback, quit and return the error + ** code. This will cause the pager to enter the error state + ** so that no further harm will be done. Perhaps the next + ** process to come along will be able to rollback the database. + */ + goto end_playback; + } + } } } - pcacheDump(p); -} + /*NOTREACHED*/ + assert( 0 ); -/* -** Wrapper around the pluggable caches xUnpin method. If the cache is -** being used for an in-memory database, this function is a no-op. -*/ -static void pcacheUnpin(PgHdr *p){ - if( p->pCache->bPurgeable ){ - pcacheTrace(("%p.UNPIN %d\n", p->pCache, p->pgno)); - sqlcipher_sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 0); - pcacheDump(p->pCache); +end_playback: + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3PagerSetPagesize(pPager, &savedPageSize, -1); } -} + /* Following a rollback, the database file should be back in its original + ** state prior to the start of the transaction, so invoke the + ** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the + ** assertion that the transaction counter was modified. + */ +#ifdef SQLITE_DEBUG + sqlcipher_sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0); +#endif -/* -** Compute the number of pages of cache requested. p->szCache is the -** cache size requested by the "PRAGMA cache_size" statement. -*/ -static int numberOfCachePages(PCache *p){ - if( p->szCache>=0 ){ - /* IMPLEMENTATION-OF: R-42059-47211 If the argument N is positive then the - ** suggested cache size is set to N. */ - return p->szCache; - }else{ - /* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the - ** number of cache pages is adjusted to be a number of pages that would - ** use approximately abs(N*1024) bytes of memory based on the current - ** page size. */ - return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); - } -} + /* If this playback is happening automatically as a result of an IO or + ** malloc error that occurred after the change-counter was updated but + ** before the transaction was committed, then the change-counter + ** modification may just have been reverted. If this happens in exclusive + ** mode, then subsequent transactions performed by the connection will not + ** update the change-counter at all. This may lead to cache inconsistency + ** problems for other processes at some point in the future. So, just + ** in case this has happened, clear the changeCountDone flag now. + */ + pPager->changeCountDone = pPager->tempFile; -/*************************************************** General Interfaces ****** -** -** Initialize and shutdown the page cache subsystem. Neither of these -** functions are threadsafe. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PcacheInitialize(void){ - if( sqlcipher_sqlite3GlobalConfig.pcache2.xInit==0 ){ - /* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the - ** built-in default page cache is used instead of the application defined - ** page cache. */ - sqlcipher_sqlite3PCacheSetDefault(); - assert( sqlcipher_sqlite3GlobalConfig.pcache2.xInit!=0 ); + if( rc==SQLITE_OK ){ + /* Leave 4 bytes of space before the super-journal filename in memory. + ** This is because it may end up being passed to sqlcipher_sqlite3OsOpen(), in + ** which case it requires 4 0x00 bytes in memory immediately before + ** the filename. */ + zSuper = &pPager->pTmpSpace[4]; + rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); + testcase( rc!=SQLITE_OK ); } - return sqlcipher_sqlite3GlobalConfig.pcache2.xInit(sqlcipher_sqlite3GlobalConfig.pcache2.pArg); -} -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheShutdown(void){ - if( sqlcipher_sqlite3GlobalConfig.pcache2.xShutdown ){ - /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */ - sqlcipher_sqlite3GlobalConfig.pcache2.xShutdown(sqlcipher_sqlite3GlobalConfig.pcache2.pArg); + if( rc==SQLITE_OK + && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) + ){ + rc = sqlcipher_sqlite3PagerSync(pPager, 0); + } + if( rc==SQLITE_OK ){ + rc = pager_end_transaction(pPager, zSuper[0]!='\0', 0); + testcase( rc!=SQLITE_OK ); + } + if( rc==SQLITE_OK && zSuper[0] && res ){ + /* If there was a super-journal and this routine will return success, + ** see if it is possible to delete the super-journal. + */ + assert( zSuper==&pPager->pTmpSpace[4] ); + memset(&zSuper[-4], 0, 4); + rc = pager_delsuper(pPager, zSuper); + testcase( rc!=SQLITE_OK ); + } + if( isHot && nPlayback ){ + sqlcipher_sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s", + nPlayback, pPager->zJournal); } + + /* The Pager.sectorSize variable may have been updated while rolling + ** back a journal created by a process with a different sector size + ** value. Reset it to the correct value for this process. + */ + setSectorSize(pPager); + return rc; } -/* -** Return the size in bytes of a PCache object. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PcacheSize(void){ return sizeof(PCache); } /* -** Create a new PCache object. Storage space to hold the object -** has already been allocated and is passed in as the p pointer. -** The caller discovers how much space needs to be allocated by -** calling sqlcipher_sqlite3PcacheSize(). +** Read the content for page pPg out of the database file (or out of +** the WAL if that is where the most recent copy if found) into +** pPg->pData. A shared lock or greater must be held on the database +** file before this function is called. ** -** szExtra is some extra space allocated for each page. The first -** 8 bytes of the extra space will be zeroed as the page is allocated, -** but remaining content will be uninitialized. Though it is opaque -** to this module, the extra space really ends up being the MemPage -** structure in the pager. +** If page 1 is read, then the value of Pager.dbFileVers[] is set to +** the value read from the database file. +** +** If an IO error occurs, then the IO error is returned to the caller. +** Otherwise, SQLITE_OK is returned. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PcacheOpen( - int szPage, /* Size of every page */ - int szExtra, /* Extra space associated with each page */ - int bPurgeable, /* True if pages are on backing store */ - int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */ - void *pStress, /* Argument to xStress */ - PCache *p /* Preallocated space for the PCache */ -){ - memset(p, 0, sizeof(PCache)); - p->szPage = 1; - p->szExtra = szExtra; - assert( szExtra>=8 ); /* First 8 bytes will be zeroed */ - p->bPurgeable = bPurgeable; - p->eCreate = 2; - p->xStress = xStress; - p->pStress = pStress; - p->szCache = 100; - p->szSpill = 1; - pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n",p,szPage,bPurgeable)); - return sqlcipher_sqlite3PcacheSetPageSize(p, szPage); -} +static int readDbPage(PgHdr *pPg){ + Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */ + int rc = SQLITE_OK; /* Return code */ -/* -** Change the page size for PCache object. The caller must ensure that there -** are no outstanding page references when this function is called. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ - assert( pCache->nRefSum==0 && pCache->pDirty==0 ); - if( pCache->szPage ){ - sqlcipher_sqlite3_pcache *pNew; - pNew = sqlcipher_sqlite3GlobalConfig.pcache2.xCreate( - szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)), - pCache->bPurgeable - ); - if( pNew==0 ) return SQLITE_NOMEM_BKPT; - sqlcipher_sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache)); - if( pCache->pCache ){ - sqlcipher_sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); +#ifndef SQLITE_OMIT_WAL + u32 iFrame = 0; /* Frame of WAL containing pgno */ + + assert( pPager->eState>=PAGER_READER && !MEMDB ); + assert( isOpen(pPager->fd) ); + + if( pagerUseWal(pPager) ){ + rc = sqlcipher_sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame); + if( rc ) return rc; + } + if( iFrame ){ + rc = sqlcipher_sqlite3WalReadFrame(pPager->pWal, iFrame,pPager->pageSize,pPg->pData); + }else +#endif + { + i64 iOffset = (pPg->pgno-1)*(i64)pPager->pageSize; + rc = sqlcipher_sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset); + if( rc==SQLITE_IOERR_SHORT_READ ){ + rc = SQLITE_OK; } - pCache->pCache = pNew; - pCache->szPage = szPage; - pcacheTrace(("%p.PAGESIZE %d\n",pCache,szPage)); } - return SQLITE_OK; + + if( pPg->pgno==1 ){ + if( rc ){ + /* If the read is unsuccessful, set the dbFileVers[] to something + ** that will never be a valid file version. dbFileVers[] is a copy + ** of bytes 24..39 of the database. Bytes 28..31 should always be + ** zero or the size of the database in page. Bytes 32..35 and 35..39 + ** should be page numbers which are never 0xffffffff. So filling + ** pPager->dbFileVers[] with all 0xff bytes should suffice. + ** + ** For an encrypted database, the situation is more complex: bytes + ** 24..39 of the database are white noise. But the probability of + ** white noise equaling 16 bytes of 0xff is vanishingly small so + ** we should still be ok. + */ + memset(pPager->dbFileVers, 0xff, sizeof(pPager->dbFileVers)); + }else{ + u8 *dbFileVers = &((u8*)pPg->pData)[24]; + memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); + } + } + CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = SQLITE_NOMEM_BKPT); + + PAGER_INCR(sqlcipher_sqlite3_pager_readdb_count); + PAGER_INCR(pPager->nRead); + IOTRACE(("PGIN %p %d\n", pPager, pPg->pgno)); + PAGERTRACE(("FETCH %d page %d hash(%08x)\n", + PAGERID(pPager), pPg->pgno, pager_pagehash(pPg))); + + return rc; } /* -** Try to obtain a page from the cache. -** -** This routine returns a pointer to an sqlcipher_sqlite3_pcache_page object if -** such an object is already in cache, or if a new one is created. -** This routine returns a NULL pointer if the object was not in cache -** and could not be created. -** -** The createFlags should be 0 to check for existing pages and should -** be 3 (not 1, but 3) to try to create a new page. -** -** If the createFlag is 0, then NULL is always returned if the page -** is not already in the cache. If createFlag is 1, then a new page -** is created only if that can be done without spilling dirty pages -** and without exceeding the cache size limit. +** Update the value of the change-counter at offsets 24 and 92 in +** the header and the sqlite version number at offset 96. ** -** The caller needs to invoke sqlcipher_sqlite3PcacheFetchFinish() to properly -** initialize the sqlcipher_sqlite3_pcache_page object and convert it into a -** PgHdr object. The sqlcipher_sqlite3PcacheFetch() and sqlcipher_sqlite3PcacheFetchFinish() -** routines are split this way for performance reasons. When separated -** they can both (usually) operate without having to push values to -** the stack on entry and pop them back off on exit, which saves a -** lot of pushing and popping. +** This is an unconditional update. See also the pager_incr_changecounter() +** routine which only updates the change-counter if the update is actually +** needed, as determined by the pPager->changeCountDone state variable. */ -SQLITE_PRIVATE sqlcipher_sqlite3_pcache_page *sqlcipher_sqlite3PcacheFetch( - PCache *pCache, /* Obtain the page from this cache */ - Pgno pgno, /* Page number to obtain */ - int createFlag /* If true, create page if it does not exist already */ -){ - int eCreate; - sqlcipher_sqlite3_pcache_page *pRes; +static void pager_write_changecounter(PgHdr *pPg){ + u32 change_counter; + if( NEVER(pPg==0) ) return; - assert( pCache!=0 ); - assert( pCache->pCache!=0 ); - assert( createFlag==3 || createFlag==0 ); - assert( pCache->eCreate==((pCache->bPurgeable && pCache->pDirty) ? 1 : 2) ); + /* Increment the value just read and write it back to byte 24. */ + change_counter = sqlcipher_sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1; + put32bits(((char*)pPg->pData)+24, change_counter); - /* eCreate defines what to do if the page does not exist. - ** 0 Do not allocate a new page. (createFlag==0) - ** 1 Allocate a new page if doing so is inexpensive. - ** (createFlag==1 AND bPurgeable AND pDirty) - ** 2 Allocate a new page even it doing so is difficult. - ** (createFlag==1 AND !(bPurgeable AND pDirty) - */ - eCreate = createFlag & pCache->eCreate; - assert( eCreate==0 || eCreate==1 || eCreate==2 ); - assert( createFlag==0 || pCache->eCreate==eCreate ); - assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) ); - pRes = sqlcipher_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); - pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno, - createFlag?" create":"",pRes)); - return pRes; + /* Also store the SQLite version number in bytes 96..99 and in + ** bytes 92..95 store the change counter for which the version number + ** is valid. */ + put32bits(((char*)pPg->pData)+92, change_counter); + put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER); } +#ifndef SQLITE_OMIT_WAL /* -** If the sqlcipher_sqlite3PcacheFetch() routine is unable to allocate a new -** page because no clean pages are available for reuse and the cache -** size limit has been reached, then this routine can be invoked to -** try harder to allocate a page. This routine might invoke the stress -** callback to spill dirty pages to the journal. It will then try to -** allocate the new page and will only fail to allocate a new page on -** an OOM error. +** This function is invoked once for each page that has already been +** written into the log file when a WAL transaction is rolled back. +** Parameter iPg is the page number of said page. The pCtx argument +** is actually a pointer to the Pager structure. ** -** This routine should be invoked only after sqlcipher_sqlite3PcacheFetch() fails. +** If page iPg is present in the cache, and has no outstanding references, +** it is discarded. Otherwise, if there are one or more outstanding +** references, the page content is reloaded from the database. If the +** attempt to reload content from the database is required and fails, +** return an SQLite error code. Otherwise, SQLITE_OK. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PcacheFetchStress( - PCache *pCache, /* Obtain the page from this cache */ - Pgno pgno, /* Page number to obtain */ - sqlcipher_sqlite3_pcache_page **ppPage /* Write result here */ -){ +static int pagerUndoCallback(void *pCtx, Pgno iPg){ + int rc = SQLITE_OK; + Pager *pPager = (Pager *)pCtx; PgHdr *pPg; - if( pCache->eCreate==2 ) return 0; - if( sqlcipher_sqlite3PcachePagecount(pCache)>pCache->szSpill ){ - /* Find a dirty page to write-out and recycle. First try to find a - ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC - ** cleared), but if that is not possible settle for any other - ** unreferenced dirty page. - ** - ** If the LRU page in the dirty list that has a clear PGHDR_NEED_SYNC - ** flag is currently referenced, then the following may leave pSynced - ** set incorrectly (pointing to other than the LRU page with NEED_SYNC - ** cleared). This is Ok, as pSynced is just an optimization. */ - for(pPg=pCache->pSynced; - pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); - pPg=pPg->pDirtyPrev - ); - pCache->pSynced = pPg; - if( !pPg ){ - for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev); - } - if( pPg ){ - int rc; -#ifdef SQLITE_LOG_CACHE_SPILL - sqlcipher_sqlite3_log(SQLITE_FULL, - "spill page %d making room for %d - cache used: %d/%d", - pPg->pgno, pgno, - sqlcipher_sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache), - numberOfCachePages(pCache)); -#endif - pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno)); - rc = pCache->xStress(pCache->pStress, pPg); - pcacheDump(pCache); - if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ - return rc; + assert( pagerUseWal(pPager) ); + pPg = sqlcipher_sqlite3PagerLookup(pPager, iPg); + if( pPg ){ + if( sqlcipher_sqlite3PcachePageRefcount(pPg)==1 ){ + sqlcipher_sqlite3PcacheDrop(pPg); + }else{ + rc = readDbPage(pPg); + if( rc==SQLITE_OK ){ + pPager->xReiniter(pPg); } + sqlcipher_sqlite3PagerUnrefNotNull(pPg); } } - *ppPage = sqlcipher_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2); - return *ppPage==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK; -} -/* -** This is a helper routine for sqlcipher_sqlite3PcacheFetchFinish() -** -** In the uncommon case where the page being fetched has not been -** initialized, this routine is invoked to do the initialization. -** This routine is broken out into a separate function since it -** requires extra stack manipulation that can be avoided in the common -** case. -*/ -static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit( - PCache *pCache, /* Obtain the page from this cache */ - Pgno pgno, /* Page number obtained */ - sqlcipher_sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */ -){ - PgHdr *pPgHdr; - assert( pPage!=0 ); - pPgHdr = (PgHdr*)pPage->pExtra; - assert( pPgHdr->pPage==0 ); - memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr,pDirty)); - pPgHdr->pPage = pPage; - pPgHdr->pData = pPage->pBuf; - pPgHdr->pExtra = (void *)&pPgHdr[1]; - memset(pPgHdr->pExtra, 0, 8); - pPgHdr->pCache = pCache; - pPgHdr->pgno = pgno; - pPgHdr->flags = PGHDR_CLEAN; - return sqlcipher_sqlite3PcacheFetchFinish(pCache,pgno,pPage); + /* Normally, if a transaction is rolled back, any backup processes are + ** updated as data is copied out of the rollback journal and into the + ** database. This is not generally possible with a WAL database, as + ** rollback involves simply truncating the log file. Therefore, if one + ** or more frames have already been written to the log (and therefore + ** also copied into the backup databases) as part of this transaction, + ** the backups must be restarted. + */ + sqlcipher_sqlite3BackupRestart(pPager->pBackup); + + return rc; } /* -** This routine converts the sqlcipher_sqlite3_pcache_page object returned by -** sqlcipher_sqlite3PcacheFetch() into an initialized PgHdr object. This routine -** must be called after sqlcipher_sqlite3PcacheFetch() in order to get a usable -** result. +** This function is called to rollback a transaction on a WAL database. */ -SQLITE_PRIVATE PgHdr *sqlcipher_sqlite3PcacheFetchFinish( - PCache *pCache, /* Obtain the page from this cache */ - Pgno pgno, /* Page number obtained */ - sqlcipher_sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */ -){ - PgHdr *pPgHdr; - - assert( pPage!=0 ); - pPgHdr = (PgHdr *)pPage->pExtra; +static int pagerRollbackWal(Pager *pPager){ + int rc; /* Return Code */ + PgHdr *pList; /* List of dirty pages to revert */ - if( !pPgHdr->pPage ){ - return pcacheFetchFinishWithInit(pCache, pgno, pPage); + /* For all pages in the cache that are currently dirty or have already + ** been written (but not committed) to the log file, do one of the + ** following: + ** + ** + Discard the cached page (if refcount==0), or + ** + Reload page content from the database (if refcount>0). + */ + pPager->dbSize = pPager->dbOrigSize; + rc = sqlcipher_sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager); + pList = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache); + while( pList && rc==SQLITE_OK ){ + PgHdr *pNext = pList->pDirty; + rc = pagerUndoCallback((void *)pPager, pList->pgno); + pList = pNext; } - pCache->nRefSum++; - pPgHdr->nRef++; - assert( sqlcipher_sqlite3PcachePageSanity(pPgHdr) ); - return pPgHdr; -} -/* -** Decrement the reference count on a page. If the page is clean and the -** reference count drops to 0, then it is made eligible for recycling. -*/ -SQLITE_PRIVATE void SQLITE_NOINLINE sqlcipher_sqlite3PcacheRelease(PgHdr *p){ - assert( p->nRef>0 ); - p->pCache->nRefSum--; - if( (--p->nRef)==0 ){ - if( p->flags&PGHDR_CLEAN ){ - pcacheUnpin(p); - }else{ - pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); - } - } + return rc; } /* -** Increase the reference count of a supplied page by 1. +** This function is a wrapper around sqlcipher_sqlite3WalFrames(). As well as logging +** the contents of the list of pages headed by pList (connected by pDirty), +** this function notifies any active backup processes that the pages have +** changed. +** +** The list of pages passed into this routine is always sorted by page number. +** Hence, if page 1 appears anywhere on the list, it will be the first page. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheRef(PgHdr *p){ - assert(p->nRef>0); - assert( sqlcipher_sqlite3PcachePageSanity(p) ); - p->nRef++; - p->pCache->nRefSum++; -} +static int pagerWalFrames( + Pager *pPager, /* Pager object */ + PgHdr *pList, /* List of frames to log */ + Pgno nTruncate, /* Database size after this commit */ + int isCommit /* True if this is a commit */ +){ + int rc; /* Return code */ + int nList; /* Number of pages in pList */ + PgHdr *p; /* For looping over pages */ -/* -** Drop a page from the cache. There must be exactly one reference to the -** page. This function deletes that reference, so after it returns the -** page pointed to by p is invalid. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheDrop(PgHdr *p){ - assert( p->nRef==1 ); - assert( sqlcipher_sqlite3PcachePageSanity(p) ); - if( p->flags&PGHDR_DIRTY ){ - pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); + assert( pPager->pWal ); + assert( pList ); +#ifdef SQLITE_DEBUG + /* Verify that the page list is in accending order */ + for(p=pList; p && p->pDirty; p=p->pDirty){ + assert( p->pgno < p->pDirty->pgno ); } - p->pCache->nRefSum--; - sqlcipher_sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 1); -} +#endif -/* -** Make sure the page is marked as dirty. If it isn't dirty already, -** make it so. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheMakeDirty(PgHdr *p){ - assert( p->nRef>0 ); - assert( sqlcipher_sqlite3PcachePageSanity(p) ); - if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){ /*OPTIMIZATION-IF-FALSE*/ - p->flags &= ~PGHDR_DONT_WRITE; - if( p->flags & PGHDR_CLEAN ){ - p->flags ^= (PGHDR_DIRTY|PGHDR_CLEAN); - pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno)); - assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY ); - pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD); + assert( pList->pDirty==0 || isCommit ); + if( isCommit ){ + /* If a WAL transaction is being committed, there is no point in writing + ** any pages with page numbers greater than nTruncate into the WAL file. + ** They will never be read by any client. So remove them from the pDirty + ** list here. */ + PgHdr **ppNext = &pList; + nList = 0; + for(p=pList; (*ppNext = p)!=0; p=p->pDirty){ + if( p->pgno<=nTruncate ){ + ppNext = &p->pDirty; + nList++; + } } - assert( sqlcipher_sqlite3PcachePageSanity(p) ); + assert( pList ); + }else{ + nList = 1; } -} + pPager->aStat[PAGER_STAT_WRITE] += nList; -/* -** Make sure the page is marked as clean. If it isn't clean already, -** make it so. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheMakeClean(PgHdr *p){ - assert( sqlcipher_sqlite3PcachePageSanity(p) ); - assert( (p->flags & PGHDR_DIRTY)!=0 ); - assert( (p->flags & PGHDR_CLEAN)==0 ); - pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); - p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE); - p->flags |= PGHDR_CLEAN; - pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno)); - assert( sqlcipher_sqlite3PcachePageSanity(p) ); - if( p->nRef==0 ){ - pcacheUnpin(p); + if( pList->pgno==1 ) pager_write_changecounter(pList); + rc = sqlcipher_sqlite3WalFrames(pPager->pWal, + pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags + ); + if( rc==SQLITE_OK && pPager->pBackup ){ + for(p=pList; p; p=p->pDirty){ + sqlcipher_sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData); + } } -} -/* -** Make every page in the cache clean. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheCleanAll(PCache *pCache){ - PgHdr *p; - pcacheTrace(("%p.CLEAN-ALL\n",pCache)); - while( (p = pCache->pDirty)!=0 ){ - sqlcipher_sqlite3PcacheMakeClean(p); +#ifdef SQLITE_CHECK_PAGES + pList = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache); + for(p=pList; p; p=p->pDirty){ + pager_set_pagehash(p); } +#endif + + return rc; } /* -** Clear the PGHDR_NEED_SYNC and PGHDR_WRITEABLE flag from all dirty pages. +** Begin a read transaction on the WAL. +** +** This routine used to be called "pagerOpenSnapshot()" because it essentially +** makes a snapshot of the database at the current point in time and preserves +** that snapshot for use by the reader in spite of concurrently changes by +** other writers or checkpointers. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheClearWritable(PCache *pCache){ - PgHdr *p; - pcacheTrace(("%p.CLEAR-WRITEABLE\n",pCache)); - for(p=pCache->pDirty; p; p=p->pDirtyNext){ - p->flags &= ~(PGHDR_NEED_SYNC|PGHDR_WRITEABLE); +static int pagerBeginReadTransaction(Pager *pPager){ + int rc; /* Return code */ + int changed = 0; /* True if cache must be reset */ + + assert( pagerUseWal(pPager) ); + assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); + + /* sqlcipher_sqlite3WalEndReadTransaction() was not called for the previous + ** transaction in locking_mode=EXCLUSIVE. So call it now. If we + ** are in locking_mode=NORMAL and EndRead() was previously called, + ** the duplicate call is harmless. + */ + sqlcipher_sqlite3WalEndReadTransaction(pPager->pWal); + + rc = sqlcipher_sqlite3WalBeginReadTransaction(pPager->pWal, &changed); + if( rc!=SQLITE_OK || changed ){ + pager_reset(pPager); + if( USEFETCH(pPager) ) sqlcipher_sqlite3OsUnfetch(pPager->fd, 0, 0); } - pCache->pSynced = pCache->pDirtyTail; + + return rc; } +#endif /* -** Clear the PGHDR_NEED_SYNC flag from all dirty pages. +** This function is called as part of the transition from PAGER_OPEN +** to PAGER_READER state to determine the size of the database file +** in pages (assuming the page size currently stored in Pager.pageSize). +** +** If no error occurs, SQLITE_OK is returned and the size of the database +** in pages is stored in *pnPage. Otherwise, an error code (perhaps +** SQLITE_IOERR_FSTAT) is returned and *pnPage is left unmodified. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheClearSyncFlags(PCache *pCache){ - PgHdr *p; - for(p=pCache->pDirty; p; p=p->pDirtyNext){ - p->flags &= ~PGHDR_NEED_SYNC; +static int pagerPagecount(Pager *pPager, Pgno *pnPage){ + Pgno nPage; /* Value to return via *pnPage */ + + /* Query the WAL sub-system for the database size. The WalDbsize() + ** function returns zero if the WAL is not open (i.e. Pager.pWal==0), or + ** if the database size is not available. The database size is not + ** available from the WAL sub-system if the log file is empty or + ** contains no valid committed transactions. + */ + assert( pPager->eState==PAGER_OPEN ); + assert( pPager->eLock>=SHARED_LOCK ); + assert( isOpen(pPager->fd) ); + assert( pPager->tempFile==0 ); + nPage = sqlcipher_sqlite3WalDbsize(pPager->pWal); + + /* If the number of pages in the database is not available from the + ** WAL sub-system, determine the page count based on the size of + ** the database file. If the size of the database file is not an + ** integer multiple of the page-size, round up the result. + */ + if( nPage==0 && ALWAYS(isOpen(pPager->fd)) ){ + i64 n = 0; /* Size of db file in bytes */ + int rc = sqlcipher_sqlite3OsFileSize(pPager->fd, &n); + if( rc!=SQLITE_OK ){ + return rc; + } + nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize); } - pCache->pSynced = pCache->pDirtyTail; -} -/* -** Change the page number of page p to newPgno. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheMove(PgHdr *p, Pgno newPgno){ - PCache *pCache = p->pCache; - assert( p->nRef>0 ); - assert( newPgno>0 ); - assert( sqlcipher_sqlite3PcachePageSanity(p) ); - pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno)); - sqlcipher_sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); - p->pgno = newPgno; - if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ - pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); + /* If the current number of pages in the file is greater than the + ** configured maximum pager number, increase the allowed limit so + ** that the file can be read. + */ + if( nPage>pPager->mxPgno ){ + pPager->mxPgno = (Pgno)nPage; } + + *pnPage = nPage; + return SQLITE_OK; } +#ifndef SQLITE_OMIT_WAL /* -** Drop every cache entry whose page number is greater than "pgno". The -** caller must ensure that there are no outstanding references to any pages -** other than page 1 with a page number greater than pgno. +** Check if the *-wal file that corresponds to the database opened by pPager +** exists if the database is not empy, or verify that the *-wal file does +** not exist (by deleting it) if the database file is empty. ** -** If there is a reference to page 1 and the pgno parameter passed to this -** function is 0, then the data area associated with page 1 is zeroed, but -** the page object is not dropped. +** If the database is not empty and the *-wal file exists, open the pager +** in WAL mode. If the database is empty or if no *-wal file exists and +** if no error occurs, make sure Pager.journalMode is not set to +** PAGER_JOURNALMODE_WAL. +** +** Return SQLITE_OK or an error code. +** +** The caller must hold a SHARED lock on the database file to call this +** function. Because an EXCLUSIVE lock on the db file is required to delete +** a WAL on a none-empty database, this ensures there is no race condition +** between the xAccess() below and an xDelete() being executed by some +** other connection. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ - if( pCache->pCache ){ - PgHdr *p; - PgHdr *pNext; - pcacheTrace(("%p.TRUNCATE %d\n",pCache,pgno)); - for(p=pCache->pDirty; p; p=pNext){ - pNext = p->pDirtyNext; - /* This routine never gets call with a positive pgno except right - ** after sqlcipher_sqlite3PcacheCleanAll(). So if there are dirty pages, - ** it must be that pgno==0. - */ - assert( p->pgno>0 ); - if( p->pgno>pgno ){ - assert( p->flags&PGHDR_DIRTY ); - sqlcipher_sqlite3PcacheMakeClean(p); - } - } - if( pgno==0 && pCache->nRefSum ){ - sqlcipher_sqlite3_pcache_page *pPage1; - pPage1 = sqlcipher_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache,1,0); - if( ALWAYS(pPage1) ){ /* Page 1 is always available in cache, because - ** pCache->nRefSum>0 */ - memset(pPage1->pBuf, 0, pCache->szPage); - pgno = 1; +static int pagerOpenWalIfPresent(Pager *pPager){ + int rc = SQLITE_OK; + assert( pPager->eState==PAGER_OPEN ); + assert( pPager->eLock>=SHARED_LOCK ); + + if( !pPager->tempFile ){ + int isWal; /* True if WAL file exists */ + rc = sqlcipher_sqlite3OsAccess( + pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &isWal + ); + if( rc==SQLITE_OK ){ + if( isWal ){ + Pgno nPage; /* Size of the database file */ + + rc = pagerPagecount(pPager, &nPage); + if( rc ) return rc; + if( nPage==0 ){ + rc = sqlcipher_sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0); + }else{ + testcase( sqlcipher_sqlite3PcachePagecount(pPager->pPCache)==0 ); + rc = sqlcipher_sqlite3PagerOpenWal(pPager, 0); + } + }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){ + pPager->journalMode = PAGER_JOURNALMODE_DELETE; } } - sqlcipher_sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1); } + return rc; } +#endif /* -** Close a cache. +** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback +** the entire super-journal file. The case pSavepoint==NULL occurs when +** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction +** savepoint. +** +** When pSavepoint is not NULL (meaning a non-transaction savepoint is +** being rolled back), then the rollback consists of up to three stages, +** performed in the order specified: +** +** * Pages are played back from the main journal starting at byte +** offset PagerSavepoint.iOffset and continuing to +** PagerSavepoint.iHdrOffset, or to the end of the main journal +** file if PagerSavepoint.iHdrOffset is zero. +** +** * If PagerSavepoint.iHdrOffset is not zero, then pages are played +** back starting from the journal header immediately following +** PagerSavepoint.iHdrOffset to the end of the main journal file. +** +** * Pages are then played back from the sub-journal file, starting +** with the PagerSavepoint.iSubRec and continuing to the end of +** the journal file. +** +** Throughout the rollback process, each time a page is rolled back, the +** corresponding bit is set in a bitvec structure (variable pDone in the +** implementation below). This is used to ensure that a page is only +** rolled back the first time it is encountered in either journal. +** +** If pSavepoint is NULL, then pages are only played back from the main +** journal file. There is no need for a bitvec in this case. +** +** In either case, before playback commences the Pager.dbSize variable +** is reset to the value that it held at the start of the savepoint +** (or transaction). No page with a page-number greater than this value +** is played back. If one is encountered it is simply skipped. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheClose(PCache *pCache){ - assert( pCache->pCache!=0 ); - pcacheTrace(("%p.CLOSE\n",pCache)); - sqlcipher_sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); -} +static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ + i64 szJ; /* Effective size of the main journal */ + i64 iHdrOff; /* End of first segment of main-journal records */ + int rc = SQLITE_OK; /* Return code */ + Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */ -/* -** Discard the contents of the cache. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheClear(PCache *pCache){ - sqlcipher_sqlite3PcacheTruncate(pCache, 0); -} + assert( pPager->eState!=PAGER_ERROR ); + assert( pPager->eState>=PAGER_WRITER_LOCKED ); -/* -** Merge two lists of pages connected by pDirty and in pgno order. -** Do not bother fixing the pDirtyPrev pointers. -*/ -static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){ - PgHdr result, *pTail; - pTail = &result; - assert( pA!=0 && pB!=0 ); - for(;;){ - if( pA->pgnopgno ){ - pTail->pDirty = pA; - pTail = pA; - pA = pA->pDirty; - if( pA==0 ){ - pTail->pDirty = pB; - break; - } - }else{ - pTail->pDirty = pB; - pTail = pB; - pB = pB->pDirty; - if( pB==0 ){ - pTail->pDirty = pA; - break; - } + /* Allocate a bitvec to use to store the set of pages rolled back */ + if( pSavepoint ){ + pDone = sqlcipher_sqlite3BitvecCreate(pSavepoint->nOrig); + if( !pDone ){ + return SQLITE_NOMEM_BKPT; } } - return result.pDirty; -} -/* -** Sort the list of pages in accending order by pgno. Pages are -** connected by pDirty pointers. The pDirtyPrev pointers are -** corrupted by this sort. -** -** Since there cannot be more than 2^31 distinct pages in a database, -** there cannot be more than 31 buckets required by the merge sorter. -** One extra bucket is added to catch overflow in case something -** ever changes to make the previous sentence incorrect. -*/ -#define N_SORT_BUCKET 32 -static PgHdr *pcacheSortDirtyList(PgHdr *pIn){ - PgHdr *a[N_SORT_BUCKET], *p; - int i; - memset(a, 0, sizeof(a)); - while( pIn ){ - p = pIn; - pIn = p->pDirty; - p->pDirty = 0; - for(i=0; ALWAYS(idbSize = pSavepoint ? pSavepoint->nOrig : pPager->dbOrigSize; + pPager->changeCountDone = pPager->tempFile; + + if( !pSavepoint && pagerUseWal(pPager) ){ + return pagerRollbackWal(pPager); + } + + /* Use pPager->journalOff as the effective size of the main rollback + ** journal. The actual file might be larger than this in + ** PAGER_JOURNALMODE_TRUNCATE or PAGER_JOURNALMODE_PERSIST. But anything + ** past pPager->journalOff is off-limits to us. + */ + szJ = pPager->journalOff; + assert( pagerUseWal(pPager)==0 || szJ==0 ); + + /* Begin by rolling back records from the main journal starting at + ** PagerSavepoint.iOffset and continuing to the next journal header. + ** There might be records in the main journal that have a page number + ** greater than the current database size (pPager->dbSize) but those + ** will be skipped automatically. Pages are added to pDone as they + ** are played back. + */ + if( pSavepoint && !pagerUseWal(pPager) ){ + iHdrOff = pSavepoint->iHdrOffset ? pSavepoint->iHdrOffset : szJ; + pPager->journalOff = pSavepoint->iOffset; + while( rc==SQLITE_OK && pPager->journalOffjournalOff, pDone, 1, 1); + } + assert( rc!=SQLITE_DONE ); + }else{ + pPager->journalOff = 0; + } + + /* Continue rolling back records out of the main journal starting at + ** the first journal header seen and continuing until the effective end + ** of the main journal file. Continue to skip out-of-range pages and + ** continue adding pages rolled back to pDone. + */ + while( rc==SQLITE_OK && pPager->journalOffjournalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff" + ** test is related to ticket #2565. See the discussion in the + ** pager_playback() function for additional information. + */ + if( nJRec==0 + && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff + ){ + nJRec = (u32)((szJ - pPager->journalOff)/JOURNAL_PG_SZ(pPager)); } - if( NEVER(i==N_SORT_BUCKET-1) ){ - /* To get here, there need to be 2^(N_SORT_BUCKET) elements in - ** the input list. But that is impossible. - */ - a[i] = pcacheMergeDirtyList(a[i], p); + for(ii=0; rc==SQLITE_OK && iijournalOffjournalOff, pDone, 1, 1); } + assert( rc!=SQLITE_DONE ); } - p = a[0]; - for(i=1; ijournalOff>=szJ ); + + /* Finally, rollback pages from the sub-journal. Page that were + ** previously rolled back out of the main journal (and are hence in pDone) + ** will be skipped. Out-of-range pages are also skipped. + */ + if( pSavepoint ){ + u32 ii; /* Loop counter */ + i64 offset = (i64)pSavepoint->iSubRec*(4+pPager->pageSize); + + if( pagerUseWal(pPager) ){ + rc = sqlcipher_sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->aWalData); + } + for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && iinSubRec; ii++){ + assert( offset==(i64)ii*(4+pPager->pageSize) ); + rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1); + } + assert( rc!=SQLITE_DONE ); } - return p; -} -/* -** Return a list of all dirty pages in the cache, sorted by page number. -*/ -SQLITE_PRIVATE PgHdr *sqlcipher_sqlite3PcacheDirtyList(PCache *pCache){ - PgHdr *p; - for(p=pCache->pDirty; p; p=p->pDirtyNext){ - p->pDirty = p->pDirtyNext; + sqlcipher_sqlite3BitvecDestroy(pDone); + if( rc==SQLITE_OK ){ + pPager->journalOff = szJ; } - return pcacheSortDirtyList(pCache->pDirty); -} -/* -** Return the total number of references to all pages held by the cache. -** -** This is not the total number of pages referenced, but the sum of the -** reference count for all pages. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PcacheRefCount(PCache *pCache){ - return pCache->nRefSum; + return rc; } /* -** Return the number of references to the page supplied as an argument. +** Change the maximum number of in-memory pages that are allowed +** before attempting to recycle clean and unused pages. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PcachePageRefcount(PgHdr *p){ - return p->nRef; +SQLITE_PRIVATE void sqlcipher_sqlite3PagerSetCachesize(Pager *pPager, int mxPage){ + sqlcipher_sqlite3PcacheSetCachesize(pPager->pPCache, mxPage); } /* -** Return the total number of pages in the cache. +** Change the maximum number of in-memory pages that are allowed +** before attempting to spill pages to journal. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PcachePagecount(PCache *pCache){ - assert( pCache->pCache!=0 ); - return sqlcipher_sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache); +SQLITE_PRIVATE int sqlcipher_sqlite3PagerSetSpillsize(Pager *pPager, int mxPage){ + return sqlcipher_sqlite3PcacheSetSpillsize(pPager->pPCache, mxPage); } -#ifdef SQLITE_TEST /* -** Get the suggested cache-size value. +** Invoke SQLITE_FCNTL_MMAP_SIZE based on the current value of szMmap. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PcacheGetCachesize(PCache *pCache){ - return numberOfCachePages(pCache); -} +static void pagerFixMaplimit(Pager *pPager){ +#if SQLITE_MAX_MMAP_SIZE>0 + sqlcipher_sqlite3_file *fd = pPager->fd; + if( isOpen(fd) && fd->pMethods->iVersion>=3 ){ + sqlcipher_sqlite3_int64 sz; + sz = pPager->szMmap; + pPager->bUseFetch = (sz>0); + setGetterMethod(pPager); + sqlcipher_sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz); + } #endif +} /* -** Set the suggested cache-size value. +** Change the maximum size of any memory mapping made of the database file. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){ - assert( pCache->pCache!=0 ); - pCache->szCache = mxPage; - sqlcipher_sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache, - numberOfCachePages(pCache)); +SQLITE_PRIVATE void sqlcipher_sqlite3PagerSetMmapLimit(Pager *pPager, sqlcipher_sqlite3_int64 szMmap){ + pPager->szMmap = szMmap; + pagerFixMaplimit(pPager); } /* -** Set the suggested cache-spill value. Make no changes if if the -** argument is zero. Return the effective cache-spill size, which will -** be the larger of the szSpill and szCache. +** Free as much memory as possible from the pager. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PcacheSetSpillsize(PCache *p, int mxPage){ - int res; - assert( p->pCache!=0 ); - if( mxPage ){ - if( mxPage<0 ){ - mxPage = (int)((-1024*(i64)mxPage)/(p->szPage+p->szExtra)); - } - p->szSpill = mxPage; - } - res = numberOfCachePages(p); - if( resszSpill ) res = p->szSpill; - return res; +SQLITE_PRIVATE void sqlcipher_sqlite3PagerShrink(Pager *pPager){ + sqlcipher_sqlite3PcacheShrink(pPager->pPCache); } /* -** Free up as much memory as possible from the page cache. +** Adjust settings of the pager to those specified in the pgFlags parameter. +** +** The "level" in pgFlags & PAGER_SYNCHRONOUS_MASK sets the robustness +** of the database to damage due to OS crashes or power failures by +** changing the number of syncs()s when writing the journals. +** There are four levels: +** +** OFF sqlcipher_sqlite3OsSync() is never called. This is the default +** for temporary and transient files. +** +** NORMAL The journal is synced once before writes begin on the +** database. This is normally adequate protection, but +** it is theoretically possible, though very unlikely, +** that an inopertune power failure could leave the journal +** in a state which would cause damage to the database +** when it is rolled back. +** +** FULL The journal is synced twice before writes begin on the +** database (with some additional information - the nRec field +** of the journal header - being written in between the two +** syncs). If we assume that writing a +** single disk sector is atomic, then this mode provides +** assurance that the journal will not be corrupted to the +** point of causing damage to the database during rollback. +** +** EXTRA This is like FULL except that is also syncs the directory +** that contains the rollback journal after the rollback +** journal is unlinked. +** +** The above is for a rollback-journal mode. For WAL mode, OFF continues +** to mean that no syncs ever occur. NORMAL means that the WAL is synced +** prior to the start of checkpoint and that the database file is synced +** at the conclusion of the checkpoint if the entire content of the WAL +** was written back into the database. But no sync operations occur for +** an ordinary commit in NORMAL mode with WAL. FULL means that the WAL +** file is synced following each commit operation, in addition to the +** syncs associated with NORMAL. There is no difference between FULL +** and EXTRA for WAL mode. +** +** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The +** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync +** using fcntl(F_FULLFSYNC). SQLITE_SYNC_NORMAL means to do an +** ordinary fsync() call. There is no difference between SQLITE_SYNC_FULL +** and SQLITE_SYNC_NORMAL on platforms other than MacOSX. But the +** synchronous=FULL versus synchronous=NORMAL setting determines when +** the xSync primitive is called and is relevant to all platforms. +** +** Numeric values associated with these states are OFF==1, NORMAL=2, +** and FULL=3. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheShrink(PCache *pCache){ - assert( pCache->pCache!=0 ); - sqlcipher_sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache); +#ifndef SQLITE_OMIT_PAGER_PRAGMAS +SQLITE_PRIVATE void sqlcipher_sqlite3PagerSetFlags( + Pager *pPager, /* The pager to set safety level for */ + unsigned pgFlags /* Various flags */ +){ + unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK; + if( pPager->tempFile ){ + pPager->noSync = 1; + pPager->fullSync = 0; + pPager->extraSync = 0; + }else{ + pPager->noSync = level==PAGER_SYNCHRONOUS_OFF ?1:0; + pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0; + pPager->extraSync = level==PAGER_SYNCHRONOUS_EXTRA ?1:0; + } + if( pPager->noSync ){ + pPager->syncFlags = 0; + }else if( pgFlags & PAGER_FULLFSYNC ){ + pPager->syncFlags = SQLITE_SYNC_FULL; + }else{ + pPager->syncFlags = SQLITE_SYNC_NORMAL; + } + pPager->walSyncFlags = (pPager->syncFlags<<2); + if( pPager->fullSync ){ + pPager->walSyncFlags |= pPager->syncFlags; + } + if( (pgFlags & PAGER_CKPT_FULLFSYNC) && !pPager->noSync ){ + pPager->walSyncFlags |= (SQLITE_SYNC_FULL<<2); + } + if( pgFlags & PAGER_CACHESPILL ){ + pPager->doNotSpill &= ~SPILLFLAG_OFF; + }else{ + pPager->doNotSpill |= SPILLFLAG_OFF; + } } +#endif /* -** Return the size of the header added by this middleware layer -** in the page-cache hierarchy. +** The following global variable is incremented whenever the library +** attempts to open a temporary file. This information is used for +** testing and analysis only. */ -SQLITE_PRIVATE int sqlcipher_sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); } +#ifdef SQLITE_TEST +SQLITE_API int sqlcipher_sqlite3_opentemp_count = 0; +#endif /* -** Return the number of dirty pages currently in the cache, as a percentage -** of the configured cache size. +** Open a temporary file. +** +** Write the file descriptor into *pFile. Return SQLITE_OK on success +** or some other error code if we fail. The OS will automatically +** delete the temporary file when it is closed. +** +** The flags passed to the VFS layer xOpen() call are those specified +** by parameter vfsFlags ORed with the following: +** +** SQLITE_OPEN_READWRITE +** SQLITE_OPEN_CREATE +** SQLITE_OPEN_EXCLUSIVE +** SQLITE_OPEN_DELETEONCLOSE */ -SQLITE_PRIVATE int sqlcipher_sqlite3PCachePercentDirty(PCache *pCache){ - PgHdr *pDirty; - int nDirty = 0; - int nCache = numberOfCachePages(pCache); - for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext) nDirty++; - return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0; -} +static int pagerOpentemp( + Pager *pPager, /* The pager object */ + sqlcipher_sqlite3_file *pFile, /* Write the file descriptor here */ + int vfsFlags /* Flags passed through to the VFS */ +){ + int rc; /* Return code */ -#ifdef SQLITE_DIRECT_OVERFLOW_READ -/* -** Return true if there are one or more dirty pages in the cache. Else false. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PCacheIsDirty(PCache *pCache){ - return (pCache->pDirty!=0); -} +#ifdef SQLITE_TEST + sqlcipher_sqlite3_opentemp_count++; /* Used for testing and analysis only */ #endif -#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) -/* -** For all dirty pages currently in the cache, invoke the specified -** callback. This is only used if the SQLITE_CHECK_PAGES macro is -** defined. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)){ - PgHdr *pDirty; - for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext){ - xIter(pDirty); - } + vfsFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | + SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE; + rc = sqlcipher_sqlite3OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0); + assert( rc!=SQLITE_OK || isOpen(pFile) ); + return rc; } -#endif -/************** End of pcache.c **********************************************/ -/************** Begin file pcache1.c *****************************************/ /* -** 2008 November 05 -** -** 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 the default page cache implementation (the -** sqlcipher_sqlite3_pcache interface). It also contains part of the implementation -** of the SQLITE_CONFIG_PAGECACHE and sqlcipher_sqlite3_release_memory() features. -** If the default page cache implementation is overridden, then neither of -** these two features are available. -** -** A Page cache line looks like this: -** -** ------------------------------------------------------------- -** | database page content | PgHdr1 | MemPage | PgHdr | -** ------------------------------------------------------------- -** -** The database page content is up front (so that buffer overreads tend to -** flow harmlessly into the PgHdr1, MemPage, and PgHdr extensions). MemPage -** is the extension added by the btree.c module containing information such -** as the database page number and how that database page is used. PgHdr -** is added by the pcache.c layer and contains information used to keep track -** of which pages are "dirty". PgHdr1 is an extension added by this -** module (pcache1.c). The PgHdr1 header is a subclass of sqlcipher_sqlite3_pcache_page. -** PgHdr1 contains information needed to look up a page by its page number. -** The superclass sqlcipher_sqlite3_pcache_page.pBuf points to the start of the -** database page content and sqlcipher_sqlite3_pcache_page.pExtra points to PgHdr. -** -** The size of the extension (MemPage+PgHdr+PgHdr1) can be determined at -** runtime using sqlcipher_sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &size). The -** sizes of the extensions sum to 272 bytes on x64 for 3.8.10, but this -** size can vary according to architecture, compile-time options, and -** SQLite library version number. +** Set the busy handler function. ** -** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained -** using a separate memory allocation from the database page content. This -** seeks to overcome the "clownshoe" problem (also called "internal -** fragmentation" in academic literature) of allocating a few bytes more -** than a power of two with the memory allocator rounding up to the next -** power of two, and leaving the rounded-up space unused. +** The pager invokes the busy-handler if sqlcipher_sqlite3OsLock() returns +** SQLITE_BUSY when trying to upgrade from no-lock to a SHARED lock, +** or when trying to upgrade from a RESERVED lock to an EXCLUSIVE +** lock. It does *not* invoke the busy handler when upgrading from +** SHARED to RESERVED, or when upgrading from SHARED to EXCLUSIVE +** (which occurs during hot-journal rollback). Summary: ** -** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates -** with this module. Information is passed back and forth as PgHdr1 pointers. +** Transition | Invokes xBusyHandler +** -------------------------------------------------------- +** NO_LOCK -> SHARED_LOCK | Yes +** SHARED_LOCK -> RESERVED_LOCK | No +** SHARED_LOCK -> EXCLUSIVE_LOCK | No +** RESERVED_LOCK -> EXCLUSIVE_LOCK | Yes ** -** The pcache.c and pager.c modules deal pointers to PgHdr objects. -** The btree.c module deals with pointers to MemPage objects. +** If the busy-handler callback returns non-zero, the lock is +** retried. If it returns zero, then the SQLITE_BUSY error is +** returned to the caller of the pager API function. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PagerSetBusyHandler( + Pager *pPager, /* Pager object */ + int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ + void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ +){ + void **ap; + pPager->xBusyHandler = xBusyHandler; + pPager->pBusyHandlerArg = pBusyHandlerArg; + ap = (void **)&pPager->xBusyHandler; + assert( ((int(*)(void *))(ap[0]))==xBusyHandler ); + assert( ap[1]==pBusyHandlerArg ); + sqlcipher_sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap); +} + +/* +** Change the page size used by the Pager object. The new page size +** is passed in *pPageSize. ** -** SOURCE OF PAGE CACHE MEMORY: +** If the pager is in the error state when this function is called, it +** is a no-op. The value returned is the error state error code (i.e. +** one of SQLITE_IOERR, an SQLITE_IOERR_xxx sub-code or SQLITE_FULL). ** -** Memory for a page might come from any of three sources: +** Otherwise, if all of the following are true: ** -** (1) The general-purpose memory allocator - sqlcipher_sqlite3Malloc() -** (2) Global page-cache memory provided using sqlcipher_sqlite3_config() with -** SQLITE_CONFIG_PAGECACHE. -** (3) PCache-local bulk allocation. +** * the new page size (value of *pPageSize) is valid (a power +** of two between 512 and SQLITE_MAX_PAGE_SIZE, inclusive), and ** -** The third case is a chunk of heap memory (defaulting to 100 pages worth) -** that is allocated when the page cache is created. The size of the local -** bulk allocation can be adjusted using +** * there are no outstanding page references, and ** -** sqlcipher_sqlite3_config(SQLITE_CONFIG_PAGECACHE, (void*)0, 0, N). +** * the database is either not an in-memory database or it is +** an in-memory database that currently consists of zero pages. ** -** If N is positive, then N pages worth of memory are allocated using a single -** sqlcipher_sqlite3Malloc() call and that memory is used for the first N pages allocated. -** Or if N is negative, then -1024*N bytes of memory are allocated and used -** for as many pages as can be accomodated. +** then the pager object page size is set to *pPageSize. ** -** Only one of (2) or (3) can be used. Once the memory available to (2) or -** (3) is exhausted, subsequent allocations fail over to the general-purpose -** memory allocator (1). +** If the page size is changed, then this function uses sqlcipher_sqlite3PagerMalloc() +** to obtain a new Pager.pTmpSpace buffer. If this allocation attempt +** fails, SQLITE_NOMEM is returned and the page size remains unchanged. +** In all other cases, SQLITE_OK is returned. ** -** Earlier versions of SQLite used only methods (1) and (2). But experiments -** show that method (3) with N==100 provides about a 5% performance boost for -** common workloads. +** If the page size is not changed, either because one of the enumerated +** conditions above is not true, the pager was in error state when this +** function was called, or because the memory allocation attempt failed, +** then *pPageSize is set to the old, retained page size before returning. */ -/* #include "sqliteInt.h" */ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){ + int rc = SQLITE_OK; -typedef struct PCache1 PCache1; -typedef struct PgHdr1 PgHdr1; -typedef struct PgFreeslot PgFreeslot; -typedef struct PGroup PGroup; + /* It is not possible to do a full assert_pager_state() here, as this + ** function may be called from within PagerOpen(), before the state + ** of the Pager object is internally consistent. + ** + ** At one point this function returned an error if the pager was in + ** PAGER_ERROR state. But since PAGER_ERROR state guarantees that + ** there is at least one outstanding page reference, this function + ** is a no-op for that case anyhow. + */ + + u32 pageSize = *pPageSize; + assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) ); + if( (pPager->memDb==0 || pPager->dbSize==0) + && sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)==0 + && pageSize && pageSize!=(u32)pPager->pageSize + ){ + char *pNew = NULL; /* New temp space */ + i64 nByte = 0; + + if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){ + rc = sqlcipher_sqlite3OsFileSize(pPager->fd, &nByte); + } + if( rc==SQLITE_OK ){ + /* 8 bytes of zeroed overrun space is sufficient so that the b-tree + * cell header parser will never run off the end of the allocation */ + pNew = (char *)sqlcipher_sqlite3PageMalloc(pageSize+8); + if( !pNew ){ + rc = SQLITE_NOMEM_BKPT; + }else{ + memset(pNew+pageSize, 0, 8); + } + } + + if( rc==SQLITE_OK ){ + pager_reset(pPager); + rc = sqlcipher_sqlite3PcacheSetPageSize(pPager->pPCache, pageSize); + } + if( rc==SQLITE_OK ){ + sqlcipher_sqlite3PageFree(pPager->pTmpSpace); + pPager->pTmpSpace = pNew; + pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize); + pPager->pageSize = pageSize; + pPager->lckPgno = (Pgno)(PENDING_BYTE/pageSize) + 1; + }else{ + sqlcipher_sqlite3PageFree(pNew); + } + } + + *pPageSize = pPager->pageSize; + if( rc==SQLITE_OK ){ + if( nReserve<0 ) nReserve = pPager->nReserve; + assert( nReserve>=0 && nReserve<1000 ); + pPager->nReserve = (i16)nReserve; + pagerReportSize(pPager); + pagerFixMaplimit(pPager); + } + return rc; +} /* -** Each cache entry is represented by an instance of the following -** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of -** PgHdr1.pCache->szPage bytes is allocated directly before this structure -** in memory. -** -** Note: Variables isBulkLocal and isAnchor were once type "u8". That works, -** but causes a 2-byte gap in the structure for most architectures (since -** pointers must be either 4 or 8-byte aligned). As this structure is located -** in memory directly after the associated page data, if the database is -** corrupt, code at the b-tree layer may overread the page buffer and -** read part of this structure before the corruption is detected. This -** can cause a valgrind error if the unitialized gap is accessed. Using u16 -** ensures there is no such gap, and therefore no bytes of unitialized memory -** in the structure. +** Return a pointer to the "temporary page" buffer held internally +** by the pager. This is a buffer that is big enough to hold the +** entire content of a database page. This buffer is used internally +** during rollback and will be overwritten whenever a rollback +** occurs. But other modules are free to use it too, as long as +** no rollbacks are happening. */ -struct PgHdr1 { - sqlcipher_sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ - unsigned int iKey; /* Key value (page number) */ - u16 isBulkLocal; /* This page from bulk local storage */ - u16 isAnchor; /* This is the PGroup.lru element */ - PgHdr1 *pNext; /* Next in hash table chain */ - PCache1 *pCache; /* Cache that currently owns this page */ - PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ - PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ - /* NB: pLruPrev is only valid if pLruNext!=0 */ -}; +SQLITE_PRIVATE void *sqlcipher_sqlite3PagerTempSpace(Pager *pPager){ + return pPager->pTmpSpace; +} /* -** A page is pinned if it is not on the LRU list. To be "pinned" means -** that the page is in active use and must not be deallocated. +** Attempt to set the maximum database page count if mxPage is positive. +** Make no changes if mxPage is zero or negative. And never reduce the +** maximum page count below the current size of the database. +** +** Regardless of mxPage, return the current maximum page count. */ -#define PAGE_IS_PINNED(p) ((p)->pLruNext==0) -#define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0) +SQLITE_PRIVATE Pgno sqlcipher_sqlite3PagerMaxPageCount(Pager *pPager, Pgno mxPage){ + if( mxPage>0 ){ + pPager->mxPgno = mxPage; + } + assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */ + /* assert( pPager->mxPgno>=pPager->dbSize ); */ + /* OP_MaxPgcnt ensures that the parameter passed to this function is not + ** less than the total number of valid pages in the database. But this + ** may be less than Pager.dbSize, and so the assert() above is not valid */ + return pPager->mxPgno; +} -/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set -** of one or more PCaches that are able to recycle each other's unpinned -** pages when they are under memory pressure. A PGroup is an instance of -** the following object. -** -** This page cache implementation works in one of two modes: -** -** (1) Every PCache is the sole member of its own PGroup. There is -** one PGroup per PCache. -** -** (2) There is a single global PGroup that all PCaches are a member -** of. -** -** Mode 1 uses more memory (since PCache instances are not able to rob -** unused pages from other PCaches) but it also operates without a mutex, -** and is therefore often faster. Mode 2 requires a mutex in order to be -** threadsafe, but recycles pages more efficiently. +/* +** The following set of routines are used to disable the simulated +** I/O error mechanism. These routines are used to avoid simulated +** errors in places where we do not care about errors. ** -** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single -** PGroup which is the pcache1.grp global variable and its mutex is -** SQLITE_MUTEX_STATIC_LRU. +** Unless -DSQLITE_TEST=1 is used, these routines are all no-ops +** and generate no code. */ -struct PGroup { - sqlcipher_sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */ - unsigned int nMaxPage; /* Sum of nMax for purgeable caches */ - unsigned int nMinPage; /* Sum of nMin for purgeable caches */ - unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */ - unsigned int nPurgeable; /* Number of purgeable pages allocated */ - PgHdr1 lru; /* The beginning and end of the LRU list */ -}; +#ifdef SQLITE_TEST +SQLITE_API extern int sqlcipher_sqlite3_io_error_pending; +SQLITE_API extern int sqlcipher_sqlite3_io_error_hit; +static int saved_cnt; +void disable_simulated_io_errors(void){ + saved_cnt = sqlcipher_sqlite3_io_error_pending; + sqlcipher_sqlite3_io_error_pending = -1; +} +void enable_simulated_io_errors(void){ + sqlcipher_sqlite3_io_error_pending = saved_cnt; +} +#else +# define disable_simulated_io_errors() +# define enable_simulated_io_errors() +#endif -/* Each page cache is an instance of the following object. Every -** open database file (including each in-memory database and each -** temporary or transient database) has a single page cache which -** is an instance of this object. +/* +** Read the first N bytes from the beginning of the file into memory +** that pDest points to. ** -** Pointers to structures of this type are cast and returned as -** opaque sqlcipher_sqlite3_pcache* handles. +** If the pager was opened on a transient file (zFilename==""), or +** opened on a file less than N bytes in size, the output buffer is +** zeroed and SQLITE_OK returned. The rationale for this is that this +** function is used to read database headers, and a new transient or +** zero sized database has a header than consists entirely of zeroes. +** +** If any IO error apart from SQLITE_IOERR_SHORT_READ is encountered, +** the error code is returned to the caller and the contents of the +** output buffer undefined. */ -struct PCache1 { - /* Cache configuration parameters. Page size (szPage) and the purgeable - ** flag (bPurgeable) and the pnPurgeable pointer are all set when the - ** cache is created and are never changed thereafter. nMax may be - ** modified at any time by a call to the pcache1Cachesize() method. - ** The PGroup mutex must be held when accessing nMax. - */ - PGroup *pGroup; /* PGroup this cache belongs to */ - unsigned int *pnPurgeable; /* Pointer to pGroup->nPurgeable */ - int szPage; /* Size of database content section */ - int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */ - int szAlloc; /* Total size of one pcache line */ - int bPurgeable; /* True if cache is purgeable */ - unsigned int nMin; /* Minimum number of pages reserved */ - unsigned int nMax; /* Configured "cache_size" value */ - unsigned int n90pct; /* nMax*9/10 */ - unsigned int iMaxKey; /* Largest key seen since xTruncate() */ - unsigned int nPurgeableDummy; /* pnPurgeable points here when not used*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){ + int rc = SQLITE_OK; + memset(pDest, 0, N); + assert( isOpen(pPager->fd) || pPager->tempFile ); - /* Hash table of all pages. The following variables may only be accessed - ** when the accessor is holding the PGroup mutex. + /* This routine is only called by btree immediately after creating + ** the Pager object. There has not been an opportunity to transition + ** to WAL mode yet. */ - unsigned int nRecyclable; /* Number of pages in the LRU list */ - unsigned int nPage; /* Total number of pages in apHash */ - unsigned int nHash; /* Number of slots in apHash[] */ - PgHdr1 **apHash; /* Hash table for fast lookup by key */ - PgHdr1 *pFree; /* List of unused pcache-local pages */ - void *pBulk; /* Bulk memory used by pcache-local */ -}; + assert( !pagerUseWal(pPager) ); -/* -** Free slots in the allocator used to divide up the global page cache -** buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism. -*/ -struct PgFreeslot { - PgFreeslot *pNext; /* Next free slot */ -}; + if( isOpen(pPager->fd) ){ + IOTRACE(("DBHDR %p 0 %d\n", pPager, N)) + rc = sqlcipher_sqlite3OsRead(pPager->fd, pDest, N, 0); + if( rc==SQLITE_IOERR_SHORT_READ ){ + rc = SQLITE_OK; + } + } + return rc; +} /* -** Global data used by this cache. +** This function may only be called when a read-transaction is open on +** the pager. It returns the total number of pages in the database. +** +** However, if the file is between 1 and bytes in size, then +** this is considered a 1 page file. */ -static SQLITE_WSD struct PCacheGlobal { - PGroup grp; /* The global PGroup for mode (2) */ - - /* Variables related to SQLITE_CONFIG_PAGECACHE settings. The - ** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all - ** fixed at sqlcipher_sqlite3_initialize() time and do not require mutex protection. - ** The nFreeSlot and pFree values do require mutex protection. - */ - int isInit; /* True if initialized */ - int separateCache; /* Use a new PGroup for each PCache */ - int nInitPage; /* Initial bulk allocation size */ - int szSlot; /* Size of each free slot */ - int nSlot; /* The number of pcache slots */ - int nReserve; /* Try to keep nFreeSlot above this */ - void *pStart, *pEnd; /* Bounds of global page cache memory */ - /* Above requires no mutex. Use mutex below for variable that follow. */ - sqlcipher_sqlite3_mutex *mutex; /* Mutex for accessing the following: */ - PgFreeslot *pFree; /* Free page blocks */ - int nFreeSlot; /* Number of unused pcache slots */ - /* The following value requires a mutex to change. We skip the mutex on - ** reading because (1) most platforms read a 32-bit integer atomically and - ** (2) even if an incorrect value is read, no great harm is done since this - ** is really just an optimization. */ - int bUnderPressure; /* True if low on PAGECACHE memory */ -} pcache1_g; +SQLITE_PRIVATE void sqlcipher_sqlite3PagerPagecount(Pager *pPager, int *pnPage){ + assert( pPager->eState>=PAGER_READER ); + assert( pPager->eState!=PAGER_WRITER_FINISHED ); + *pnPage = (int)pPager->dbSize; +} -/* -** All code in this file should access the global structure above via the -** alias "pcache1". This ensures that the WSD emulation is used when -** compiling for systems that do not support real WSD. -*/ -#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g)) /* -** Macros to enter and leave the PCache LRU mutex. +** Try to obtain a lock of type locktype on the database file. If +** a similar or greater lock is already held, this function is a no-op +** (returning SQLITE_OK immediately). +** +** Otherwise, attempt to obtain the lock using sqlcipher_sqlite3OsLock(). Invoke +** the busy callback if the lock is currently not available. Repeat +** until the busy callback returns false or until the attempt to +** obtain the lock succeeds. +** +** Return SQLITE_OK on success and an error code if we cannot obtain +** the lock. If the lock is obtained successfully, set the Pager.state +** variable to locktype before returning. */ -#if !defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0 -# define pcache1EnterMutex(X) assert((X)->mutex==0) -# define pcache1LeaveMutex(X) assert((X)->mutex==0) -# define PCACHE1_MIGHT_USE_GROUP_MUTEX 0 -#else -# define pcache1EnterMutex(X) sqlcipher_sqlite3_mutex_enter((X)->mutex) -# define pcache1LeaveMutex(X) sqlcipher_sqlite3_mutex_leave((X)->mutex) -# define PCACHE1_MIGHT_USE_GROUP_MUTEX 1 -#endif +static int pager_wait_on_lock(Pager *pPager, int locktype){ + int rc; /* Return code */ -/******************************************************************************/ -/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/ + /* Check that this is either a no-op (because the requested lock is + ** already held), or one of the transitions that the busy-handler + ** may be invoked during, according to the comment above + ** sqlcipher_sqlite3PagerSetBusyhandler(). + */ + assert( (pPager->eLock>=locktype) + || (pPager->eLock==NO_LOCK && locktype==SHARED_LOCK) + || (pPager->eLock==RESERVED_LOCK && locktype==EXCLUSIVE_LOCK) + ); + do { + rc = pagerLockDb(pPager, locktype); + }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) ); + return rc; +} /* -** This function is called during initialization if a static buffer is -** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE -** verb to sqlcipher_sqlite3_config(). Parameter pBuf points to an allocation large -** enough to contain 'n' buffers of 'sz' bytes each. +** Function assertTruncateConstraint(pPager) checks that one of the +** following is true for all dirty pages currently in the page-cache: ** -** This routine is called from sqlcipher_sqlite3_initialize() and so it is guaranteed -** to be serialized already. There is no need for further mutexing. +** a) The page number is less than or equal to the size of the +** current database image, in pages, OR +** +** b) if the page content were written at this time, it would not +** be necessary to write the current content out to the sub-journal. +** +** If the condition asserted by this function were not true, and the +** dirty page were to be discarded from the cache via the pagerStress() +** routine, pagerStress() would not write the current page content to +** the database file. If a savepoint transaction were rolled back after +** this happened, the correct behavior would be to restore the current +** content of the page. However, since this content is not present in either +** the database file or the portion of the rollback journal and +** sub-journal rolled back the content could not be restored and the +** database image would become corrupt. It is therefore fortunate that +** this circumstance cannot arise. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ - if( pcache1.isInit ){ - PgFreeslot *p; - if( pBuf==0 ) sz = n = 0; - if( n==0 ) sz = 0; - sz = ROUNDDOWN8(sz); - pcache1.szSlot = sz; - pcache1.nSlot = pcache1.nFreeSlot = n; - pcache1.nReserve = n>90 ? 10 : (n/10 + 1); - pcache1.pStart = pBuf; - pcache1.pFree = 0; - pcache1.bUnderPressure = 0; - while( n-- ){ - p = (PgFreeslot*)pBuf; - p->pNext = pcache1.pFree; - pcache1.pFree = p; - pBuf = (void*)&((char*)pBuf)[sz]; +#if defined(SQLITE_DEBUG) +static void assertTruncateConstraintCb(PgHdr *pPg){ + Pager *pPager = pPg->pPager; + assert( pPg->flags&PGHDR_DIRTY ); + if( pPg->pgno>pPager->dbSize ){ /* if (a) is false */ + Pgno pgno = pPg->pgno; + int i; + for(i=0; ipPager->nSavepoint; i++){ + PagerSavepoint *p = &pPager->aSavepoint[i]; + assert( p->nOrigpInSavepoint,pgno) ); } - pcache1.pEnd = pBuf; } } +static void assertTruncateConstraint(Pager *pPager){ + sqlcipher_sqlite3PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb); +} +#else +# define assertTruncateConstraint(pPager) +#endif /* -** Try to initialize the pCache->pFree and pCache->pBulk fields. Return -** true if pCache->pFree ends up containing one or more free pages. +** Truncate the in-memory database file image to nPage pages. This +** function does not actually modify the database file on disk. It +** just sets the internal state of the pager object so that the +** truncation will be done when the current transaction is committed. +** +** This function is only called right before committing a transaction. +** Once this function has been called, the transaction must either be +** rolled back or committed. It is not safe to call this function and +** then continue writing to the database. */ -static int pcache1InitBulk(PCache1 *pCache){ - i64 szBulk; - char *zBulk; - if( pcache1.nInitPage==0 ) return 0; - /* Do not bother with a bulk allocation if the cache size very small */ - if( pCache->nMax<3 ) return 0; - sqlcipher_sqlite3BeginBenignMalloc(); - if( pcache1.nInitPage>0 ){ - szBulk = pCache->szAlloc * (i64)pcache1.nInitPage; - }else{ - szBulk = -1024 * (i64)pcache1.nInitPage; - } - if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){ - szBulk = pCache->szAlloc*(i64)pCache->nMax; - } - zBulk = pCache->pBulk = sqlcipher_sqlite3Malloc( szBulk ); - sqlcipher_sqlite3EndBenignMalloc(); - if( zBulk ){ - int nBulk = sqlcipher_sqlite3MallocSize(zBulk)/pCache->szAlloc; - do{ - PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage]; - pX->page.pBuf = zBulk; - pX->page.pExtra = &pX[1]; - pX->isBulkLocal = 1; - pX->isAnchor = 0; - pX->pNext = pCache->pFree; - pX->pLruPrev = 0; /* Initializing this saves a valgrind error */ - pCache->pFree = pX; - zBulk += pCache->szAlloc; - }while( --nBulk ); - } - return pCache->pFree!=0; +SQLITE_PRIVATE void sqlcipher_sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ + assert( pPager->dbSize>=nPage || CORRUPT_DB ); + assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); + pPager->dbSize = nPage; + + /* At one point the code here called assertTruncateConstraint() to + ** ensure that all pages being truncated away by this operation are, + ** if one or more savepoints are open, present in the savepoint + ** journal so that they can be restored if the savepoint is rolled + ** back. This is no longer necessary as this function is now only + ** called right before committing a transaction. So although the + ** Pager object may still have open savepoints (Pager.nSavepoint!=0), + ** they cannot be rolled back. So the assertTruncateConstraint() call + ** is no longer correct. */ } + /* -** Malloc function used within this file to allocate space from the buffer -** configured using sqlcipher_sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no -** such buffer exists or there is no space left in it, this function falls -** back to sqlcipher_sqlite3Malloc(). +** This function is called before attempting a hot-journal rollback. It +** syncs the journal file to disk, then sets pPager->journalHdr to the +** size of the journal file so that the pager_playback() routine knows +** that the entire journal file has been synced. ** -** Multiple threads can run this routine at the same time. Global variables -** in pcache1 need to be protected via mutex. +** Syncing a hot-journal to disk before attempting to roll it back ensures +** that if a power-failure occurs during the rollback, the process that +** attempts rollback following system recovery sees the same journal +** content as this process. +** +** If everything goes as planned, SQLITE_OK is returned. Otherwise, +** an SQLite error code. */ -static void *pcache1Alloc(int nByte){ - void *p = 0; - assert( sqlcipher_sqlite3_mutex_notheld(pcache1.grp.mutex) ); - if( nByte<=pcache1.szSlot ){ - sqlcipher_sqlite3_mutex_enter(pcache1.mutex); - p = (PgHdr1 *)pcache1.pFree; - if( p ){ - pcache1.pFree = pcache1.pFree->pNext; - pcache1.nFreeSlot--; - pcache1.bUnderPressure = pcache1.nFreeSlot=0 ); - sqlcipher_sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); - sqlcipher_sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); - } - sqlcipher_sqlite3_mutex_leave(pcache1.mutex); +static int pagerSyncHotJournal(Pager *pPager){ + int rc = SQLITE_OK; + if( !pPager->noSync ){ + rc = sqlcipher_sqlite3OsSync(pPager->jfd, SQLITE_SYNC_NORMAL); } - if( p==0 ){ - /* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool. Get - ** it from sqlcipher_sqlite3Malloc instead. - */ - p = sqlcipher_sqlite3Malloc(nByte); -#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS - if( p ){ - int sz = sqlcipher_sqlite3MallocSize(p); - sqlcipher_sqlite3_mutex_enter(pcache1.mutex); - sqlcipher_sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); - sqlcipher_sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); - sqlcipher_sqlite3_mutex_leave(pcache1.mutex); - } -#endif - sqlcipher_sqlite3MemdebugSetType(p, MEMTYPE_PCACHE); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3OsFileSize(pPager->jfd, &pPager->journalHdr); } - return p; + return rc; } +#if SQLITE_MAX_MMAP_SIZE>0 /* -** Free an allocated buffer obtained from pcache1Alloc(). +** Obtain a reference to a memory mapped page object for page number pgno. +** The new object will use the pointer pData, obtained from xFetch(). +** If successful, set *ppPage to point to the new page reference +** and return SQLITE_OK. Otherwise, return an SQLite error code and set +** *ppPage to zero. +** +** Page references obtained by calling this function should be released +** by calling pagerReleaseMapPage(). */ -static void pcache1Free(void *p){ - if( p==0 ) return; - if( SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd) ){ - PgFreeslot *pSlot; - sqlcipher_sqlite3_mutex_enter(pcache1.mutex); - sqlcipher_sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); - pSlot = (PgFreeslot*)p; - pSlot->pNext = pcache1.pFree; - pcache1.pFree = pSlot; - pcache1.nFreeSlot++; - pcache1.bUnderPressure = pcache1.nFreeSlotpMmapFreelist ){ + *ppPage = p = pPager->pMmapFreelist; + pPager->pMmapFreelist = p->pDirty; + p->pDirty = 0; + assert( pPager->nExtra>=8 ); + memset(p->pExtra, 0, 8); }else{ - assert( sqlcipher_sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); - sqlcipher_sqlite3MemdebugSetType(p, MEMTYPE_HEAP); -#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS - { - int nFreed = 0; - nFreed = sqlcipher_sqlite3MallocSize(p); - sqlcipher_sqlite3_mutex_enter(pcache1.mutex); - sqlcipher_sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed); - sqlcipher_sqlite3_mutex_leave(pcache1.mutex); + *ppPage = p = (PgHdr *)sqlcipher_sqlite3MallocZero(sizeof(PgHdr) + pPager->nExtra); + if( p==0 ){ + sqlcipher_sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pData); + return SQLITE_NOMEM_BKPT; } -#endif - sqlcipher_sqlite3_free(p); + p->pExtra = (void *)&p[1]; + p->flags = PGHDR_MMAP; + p->nRef = 1; + p->pPager = pPager; } + + assert( p->pExtra==(void *)&p[1] ); + assert( p->pPage==0 ); + assert( p->flags==PGHDR_MMAP ); + assert( p->pPager==pPager ); + assert( p->nRef==1 ); + + p->pgno = pgno; + p->pData = pData; + pPager->nMmapOut++; + + return SQLITE_OK; } +#endif -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* -** Return the size of a pcache allocation +** Release a reference to page pPg. pPg must have been returned by an +** earlier call to pagerAcquireMapPage(). */ -static int pcache1MemSize(void *p){ - if( p>=pcache1.pStart && ppPager; + pPager->nMmapOut--; + pPg->pDirty = pPager->pMmapFreelist; + pPager->pMmapFreelist = pPg; + + assert( pPager->fd->pMethods->iVersion>=3 ); + sqlcipher_sqlite3OsUnfetch(pPager->fd, (i64)(pPg->pgno-1)*pPager->pageSize, pPg->pData); } -#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */ /* -** Allocate a new page object initially associated with cache pCache. +** Free all PgHdr objects stored in the Pager.pMmapFreelist list. */ -static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){ - PgHdr1 *p = 0; - void *pPg; +static void pagerFreeMapHdrs(Pager *pPager){ + PgHdr *p; + PgHdr *pNext; + for(p=pPager->pMmapFreelist; p; p=pNext){ + pNext = p->pDirty; + sqlcipher_sqlite3_free(p); + } +} - assert( sqlcipher_sqlite3_mutex_held(pCache->pGroup->mutex) ); - if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){ - assert( pCache->pFree!=0 ); - p = pCache->pFree; - pCache->pFree = p->pNext; - p->pNext = 0; - }else{ -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT - /* The group mutex must be released before pcache1Alloc() is called. This - ** is because it might call sqlcipher_sqlite3_release_memory(), which assumes that - ** this mutex is not held. */ - assert( pcache1.separateCache==0 ); - assert( pCache->pGroup==&pcache1.grp ); - pcache1LeaveMutex(pCache->pGroup); -#endif - if( benignMalloc ){ sqlcipher_sqlite3BeginBenignMalloc(); } -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - pPg = pcache1Alloc(pCache->szPage); - p = sqlcipher_sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); - if( !pPg || !p ){ - pcache1Free(pPg); - sqlcipher_sqlite3_free(p); - pPg = 0; - } -#else - pPg = pcache1Alloc(pCache->szAlloc); -#endif - if( benignMalloc ){ sqlcipher_sqlite3EndBenignMalloc(); } -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT - pcache1EnterMutex(pCache->pGroup); -#endif - if( pPg==0 ) return 0; -#ifndef SQLITE_PCACHE_SEPARATE_HEADER - p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; -#endif - p->page.pBuf = pPg; - p->page.pExtra = &p[1]; - p->isBulkLocal = 0; - p->isAnchor = 0; +/* Verify that the database file has not be deleted or renamed out from +** under the pager. Return SQLITE_OK if the database is still where it ought +** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error +** code from sqlcipher_sqlite3OsAccess()) if the database has gone missing. +*/ +static int databaseIsUnmoved(Pager *pPager){ + int bHasMoved = 0; + int rc; + + if( pPager->tempFile ) return SQLITE_OK; + if( pPager->dbSize==0 ) return SQLITE_OK; + assert( pPager->zFilename && pPager->zFilename[0] ); + rc = sqlcipher_sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved); + if( rc==SQLITE_NOTFOUND ){ + /* If the HAS_MOVED file-control is unimplemented, assume that the file + ** has not been moved. That is the historical behavior of SQLite: prior to + ** version 3.8.3, it never checked */ + rc = SQLITE_OK; + }else if( rc==SQLITE_OK && bHasMoved ){ + rc = SQLITE_READONLY_DBMOVED; } - (*pCache->pnPurgeable)++; - return p; + return rc; } + /* -** Free a page object allocated by pcache1AllocPage(). +** Shutdown the page cache. Free all memory and close all files. +** +** If a transaction was in progress when this routine is called, that +** transaction is rolled back. All outstanding pages are invalidated +** and their memory is freed. Any attempt to use a page associated +** with this page cache after this function returns will likely +** result in a coredump. +** +** This function always succeeds. If a transaction is active an attempt +** is made to roll it back. If an error occurs during the rollback +** a hot journal may be left in the filesystem but no error is returned +** to the caller. */ -static void pcache1FreePage(PgHdr1 *p){ - PCache1 *pCache; - assert( p!=0 ); - pCache = p->pCache; - assert( sqlcipher_sqlite3_mutex_held(p->pCache->pGroup->mutex) ); - if( p->isBulkLocal ){ - p->pNext = pCache->pFree; - pCache->pFree = p; - }else{ - pcache1Free(p->page.pBuf); -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - sqlcipher_sqlite3_free(p); +SQLITE_PRIVATE int sqlcipher_sqlite3PagerClose(Pager *pPager, sqlcipher_sqlite3 *db){ + u8 *pTmp = (u8*)pPager->pTmpSpace; + assert( db || pagerUseWal(pPager)==0 ); + assert( assert_pager_state(pPager) ); + disable_simulated_io_errors(); + sqlcipher_sqlite3BeginBenignMalloc(); + pagerFreeMapHdrs(pPager); + /* pPager->errCode = 0; */ + pPager->exclusiveMode = 0; +#ifndef SQLITE_OMIT_WAL + { + u8 *a = 0; + assert( db || pPager->pWal==0 ); + if( db && 0==(db->flags & SQLITE_NoCkptOnClose) + && SQLITE_OK==databaseIsUnmoved(pPager) + ){ + a = pTmp; + } + sqlcipher_sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a); + pPager->pWal = 0; + } #endif + pager_reset(pPager); + if( MEMDB ){ + pager_unlock(pPager); + }else{ + /* If it is open, sync the journal file before calling UnlockAndRollback. + ** If this is not done, then an unsynced portion of the open journal + ** file may be played back into the database. If a power failure occurs + ** while this is happening, the database could become corrupt. + ** + ** If an error occurs while trying to sync the journal, shift the pager + ** into the ERROR state. This causes UnlockAndRollback to unlock the + ** database and close the journal file without attempting to roll it + ** back or finalize it. The next database user will have to do hot-journal + ** rollback before accessing the database file. + */ + if( isOpen(pPager->jfd) ){ + pager_error(pPager, pagerSyncHotJournal(pPager)); + } + pagerUnlockAndRollback(pPager); } - (*pCache->pnPurgeable)--; + sqlcipher_sqlite3EndBenignMalloc(); + enable_simulated_io_errors(); + PAGERTRACE(("CLOSE %d\n", PAGERID(pPager))); + IOTRACE(("CLOSE %p\n", pPager)) + sqlcipher_sqlite3OsClose(pPager->jfd); + sqlcipher_sqlite3OsClose(pPager->fd); + sqlcipher_sqlite3PageFree(pTmp); + sqlcipher_sqlite3PcacheClose(pPager->pPCache); + +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); +#endif +/* END SQLCIPHER */ + + assert( !pPager->aSavepoint && !pPager->pInJournal ); + assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) ); + + sqlcipher_sqlite3_free(pPager); + return SQLITE_OK; } +#if !defined(NDEBUG) || defined(SQLITE_TEST) /* -** Malloc function used by SQLite to obtain space from the buffer configured -** using sqlcipher_sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer -** exists, this function falls back to sqlcipher_sqlite3Malloc(). +** Return the page number for page pPg. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3PageMalloc(int sz){ - assert( sz<=65536+8 ); /* These allocations are never very large */ - return pcache1Alloc(sz); +SQLITE_PRIVATE Pgno sqlcipher_sqlite3PagerPagenumber(DbPage *pPg){ + return pPg->pgno; } +#endif /* -** Free an allocated buffer obtained from sqlcipher_sqlite3PageMalloc(). +** Increment the reference count for page pPg. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PageFree(void *p){ - pcache1Free(p); +SQLITE_PRIVATE void sqlcipher_sqlite3PagerRef(DbPage *pPg){ + sqlcipher_sqlite3PcacheRef(pPg); } - /* -** Return true if it desirable to avoid allocating a new page cache -** entry. +** Sync the journal. In other words, make sure all the pages that have +** been written to the journal have actually reached the surface of the +** disk and can be restored in the event of a hot-journal rollback. ** -** If memory was allocated specifically to the page cache using -** SQLITE_CONFIG_PAGECACHE but that memory has all been used, then -** it is desirable to avoid allocating a new page cache entry because -** presumably SQLITE_CONFIG_PAGECACHE was suppose to be sufficient -** for all page cache needs and we should not need to spill the -** allocation onto the heap. +** If the Pager.noSync flag is set, then this function is a no-op. +** Otherwise, the actions required depend on the journal-mode and the +** device characteristics of the file-system, as follows: ** -** Or, the heap is used for all page cache memory but the heap is -** under memory pressure, then again it is desirable to avoid -** allocating a new page cache entry in order to avoid stressing -** the heap even further. +** * If the journal file is an in-memory journal file, no action need +** be taken. +** +** * Otherwise, if the device does not support the SAFE_APPEND property, +** then the nRec field of the most recently written journal header +** is updated to contain the number of journal records that have +** been written following it. If the pager is operating in full-sync +** mode, then the journal file is synced before this field is updated. +** +** * If the device does not support the SEQUENTIAL property, then +** journal file is synced. +** +** Or, in pseudo-code: +** +** if( NOT ){ +** if( NOT SAFE_APPEND ){ +** if( ) xSync(); +** +** } +** if( NOT SEQUENTIAL ) xSync(); +** } +** +** If successful, this routine clears the PGHDR_NEED_SYNC flag of every +** page currently held in memory before returning SQLITE_OK. If an IO +** error is encountered, then the IO error code is returned to the caller. */ -static int pcache1UnderMemoryPressure(PCache1 *pCache){ - if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ - return pcache1.bUnderPressure; - }else{ - return sqlcipher_sqlite3HeapNearlyFull(); - } -} +static int syncJournal(Pager *pPager, int newHdr){ + int rc; /* Return code */ -/******************************************************************************/ -/******** General Implementation Functions ************************************/ + assert( pPager->eState==PAGER_WRITER_CACHEMOD + || pPager->eState==PAGER_WRITER_DBMOD + ); + assert( assert_pager_state(pPager) ); + assert( !pagerUseWal(pPager) ); -/* -** This function is used to resize the hash table used by the cache passed -** as the first argument. -** -** The PCache mutex must be held when this function is called. -*/ -static void pcache1ResizeHash(PCache1 *p){ - PgHdr1 **apNew; - unsigned int nNew; - unsigned int i; + rc = sqlcipher_sqlite3PagerExclusiveLock(pPager); + if( rc!=SQLITE_OK ) return rc; - assert( sqlcipher_sqlite3_mutex_held(p->pGroup->mutex) ); + if( !pPager->noSync ){ + assert( !pPager->tempFile ); + if( isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){ + const int iDc = sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd); + assert( isOpen(pPager->jfd) ); - nNew = p->nHash*2; - if( nNew<256 ){ - nNew = 256; - } + if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ + /* This block deals with an obscure problem. If the last connection + ** that wrote to this database was operating in persistent-journal + ** mode, then the journal file may at this point actually be larger + ** than Pager.journalOff bytes. If the next thing in the journal + ** file happens to be a journal-header (written as part of the + ** previous connection's transaction), and a crash or power-failure + ** occurs after nRec is updated but before this connection writes + ** anything else to the journal file (or commits/rolls back its + ** transaction), then SQLite may become confused when doing the + ** hot-journal rollback following recovery. It may roll back all + ** of this connections data, then proceed to rolling back the old, + ** out-of-date data that follows it. Database corruption. + ** + ** To work around this, if the journal file does appear to contain + ** a valid header following Pager.journalOff, then write a 0x00 + ** byte to the start of it to prevent it from being recognized. + ** + ** Variable iNextHdrOffset is set to the offset at which this + ** problematic header will occur, if it exists. aMagic is used + ** as a temporary buffer to inspect the first couple of bytes of + ** the potential journal header. + */ + i64 iNextHdrOffset; + u8 aMagic[8]; + u8 zHeader[sizeof(aJournalMagic)+4]; - pcache1LeaveMutex(p->pGroup); - if( p->nHash ){ sqlcipher_sqlite3BeginBenignMalloc(); } - apNew = (PgHdr1 **)sqlcipher_sqlite3MallocZero(sizeof(PgHdr1 *)*nNew); - if( p->nHash ){ sqlcipher_sqlite3EndBenignMalloc(); } - pcache1EnterMutex(p->pGroup); - if( apNew ){ - for(i=0; inHash; i++){ - PgHdr1 *pPage; - PgHdr1 *pNext = p->apHash[i]; - while( (pPage = pNext)!=0 ){ - unsigned int h = pPage->iKey % nNew; - pNext = pPage->pNext; - pPage->pNext = apNew[h]; - apNew[h] = pPage; + memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); + put32bits(&zHeader[sizeof(aJournalMagic)], pPager->nRec); + + iNextHdrOffset = journalHdrOffset(pPager); + rc = sqlcipher_sqlite3OsRead(pPager->jfd, aMagic, 8, iNextHdrOffset); + if( rc==SQLITE_OK && 0==memcmp(aMagic, aJournalMagic, 8) ){ + static const u8 zerobyte = 0; + rc = sqlcipher_sqlite3OsWrite(pPager->jfd, &zerobyte, 1, iNextHdrOffset); + } + if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ + return rc; + } + + /* Write the nRec value into the journal file header. If in + ** full-synchronous mode, sync the journal first. This ensures that + ** all data has really hit the disk before nRec is updated to mark + ** it as a candidate for rollback. + ** + ** This is not required if the persistent media supports the + ** SAFE_APPEND property. Because in this case it is not possible + ** for garbage data to be appended to the file, the nRec field + ** is populated with 0xFFFFFFFF when the journal header is written + ** and never needs to be updated. + */ + if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ + PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); + IOTRACE(("JSYNC %p\n", pPager)) + rc = sqlcipher_sqlite3OsSync(pPager->jfd, pPager->syncFlags); + if( rc!=SQLITE_OK ) return rc; + } + IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr)); + rc = sqlcipher_sqlite3OsWrite( + pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr + ); + if( rc!=SQLITE_OK ) return rc; + } + if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ + PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); + IOTRACE(("JSYNC %p\n", pPager)) + rc = sqlcipher_sqlite3OsSync(pPager->jfd, pPager->syncFlags| + (pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0) + ); + if( rc!=SQLITE_OK ) return rc; + } + + pPager->journalHdr = pPager->journalOff; + if( newHdr && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ + pPager->nRec = 0; + rc = writeJournalHdr(pPager); + if( rc!=SQLITE_OK ) return rc; } + }else{ + pPager->journalHdr = pPager->journalOff; } - sqlcipher_sqlite3_free(p->apHash); - p->apHash = apNew; - p->nHash = nNew; } -} -/* -** This function is used internally to remove the page pPage from the -** PGroup LRU list, if is part of it. If pPage is not part of the PGroup -** LRU list, then this function is a no-op. -** -** The PGroup mutex must be held when this function is called. -*/ -static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){ - assert( pPage!=0 ); - assert( PAGE_IS_UNPINNED(pPage) ); - assert( pPage->pLruNext ); - assert( pPage->pLruPrev ); - assert( sqlcipher_sqlite3_mutex_held(pPage->pCache->pGroup->mutex) ); - pPage->pLruPrev->pLruNext = pPage->pLruNext; - pPage->pLruNext->pLruPrev = pPage->pLruPrev; - pPage->pLruNext = 0; - /* pPage->pLruPrev = 0; - ** No need to clear pLruPrev as it is never accessed if pLruNext is 0 */ - assert( pPage->isAnchor==0 ); - assert( pPage->pCache->pGroup->lru.isAnchor==1 ); - pPage->pCache->nRecyclable--; - return pPage; + /* Unless the pager is in noSync mode, the journal file was just + ** successfully synced. Either way, clear the PGHDR_NEED_SYNC flag on + ** all pages. + */ + sqlcipher_sqlite3PcacheClearSyncFlags(pPager->pPCache); + pPager->eState = PAGER_WRITER_DBMOD; + assert( assert_pager_state(pPager) ); + return SQLITE_OK; } - /* -** Remove the page supplied as an argument from the hash table -** (PCache1.apHash structure) that it is currently stored in. -** Also free the page if freePage is true. +** The argument is the first in a linked list of dirty pages connected +** by the PgHdr.pDirty pointer. This function writes each one of the +** in-memory pages in the list to the database file. The argument may +** be NULL, representing an empty list. In this case this function is +** a no-op. ** -** The PGroup mutex must be held when this function is called. +** The pager must hold at least a RESERVED lock when this function +** is called. Before writing anything to the database file, this lock +** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained, +** SQLITE_BUSY is returned and no data is written to the database file. +** +** If the pager is a temp-file pager and the actual file-system file +** is not yet open, it is created and opened before any data is +** written out. +** +** Once the lock has been upgraded and, if necessary, the file opened, +** the pages are written out to the database file in list order. Writing +** a page is skipped if it meets either of the following criteria: +** +** * The page number is greater than Pager.dbSize, or +** * The PGHDR_DONT_WRITE flag is set on the page. +** +** If writing out a page causes the database file to grow, Pager.dbFileSize +** is updated accordingly. If page 1 is written out, then the value cached +** in Pager.dbFileVers[] is updated to match the new value stored in +** the database file. +** +** If everything is successful, SQLITE_OK is returned. If an IO error +** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot +** be obtained, SQLITE_BUSY is returned. */ -static void pcache1RemoveFromHash(PgHdr1 *pPage, int freeFlag){ - unsigned int h; - PCache1 *pCache = pPage->pCache; - PgHdr1 **pp; +static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ + int rc = SQLITE_OK; /* Return code */ - assert( sqlcipher_sqlite3_mutex_held(pCache->pGroup->mutex) ); - h = pPage->iKey % pCache->nHash; - for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext); - *pp = (*pp)->pNext; + /* This function is only called for rollback pagers in WRITER_DBMOD state. */ + assert( !pagerUseWal(pPager) ); + assert( pPager->tempFile || pPager->eState==PAGER_WRITER_DBMOD ); + assert( pPager->eLock==EXCLUSIVE_LOCK ); + assert( isOpen(pPager->fd) || pList->pDirty==0 ); - pCache->nPage--; - if( freeFlag ) pcache1FreePage(pPage); -} + /* If the file is a temp-file has not yet been opened, open it now. It + ** is not possible for rc to be other than SQLITE_OK if this branch + ** is taken, as pager_wait_on_lock() is a no-op for temp-files. + */ + if( !isOpen(pPager->fd) ){ + assert( pPager->tempFile && rc==SQLITE_OK ); + rc = pagerOpentemp(pPager, pPager->fd, pPager->vfsFlags); + } -/* -** If there are currently more than nMaxPage pages allocated, try -** to recycle pages to reduce the number allocated to nMaxPage. -*/ -static void pcache1EnforceMaxPage(PCache1 *pCache){ - PGroup *pGroup = pCache->pGroup; - PgHdr1 *p; - assert( sqlcipher_sqlite3_mutex_held(pGroup->mutex) ); - while( pGroup->nPurgeable>pGroup->nMaxPage - && (p=pGroup->lru.pLruPrev)->isAnchor==0 + /* Before the first write, give the VFS a hint of what the final + ** file size will be. + */ + assert( rc!=SQLITE_OK || isOpen(pPager->fd) ); + if( rc==SQLITE_OK + && pPager->dbHintSizedbSize + && (pList->pDirty || pList->pgno>pPager->dbHintSize) ){ - assert( p->pCache->pGroup==pGroup ); - assert( PAGE_IS_UNPINNED(p) ); - pcache1PinPage(p); - pcache1RemoveFromHash(p, 1); + sqlcipher_sqlite3_int64 szFile = pPager->pageSize * (sqlcipher_sqlite3_int64)pPager->dbSize; + sqlcipher_sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); + pPager->dbHintSize = pPager->dbSize; } - if( pCache->nPage==0 && pCache->pBulk ){ - sqlcipher_sqlite3_free(pCache->pBulk); - pCache->pBulk = pCache->pFree = 0; + + while( rc==SQLITE_OK && pList ){ + Pgno pgno = pList->pgno; + + /* If there are dirty pages in the page cache with page numbers greater + ** than Pager.dbSize, this means sqlcipher_sqlite3PagerTruncateImage() was called to + ** make the file smaller (presumably by auto-vacuum code). Do not write + ** any such pages to the file. + ** + ** Also, do not write out any page that has the PGHDR_DONT_WRITE flag + ** set (set by sqlcipher_sqlite3PagerDontWrite()). + */ + if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){ + i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */ + char *pData; /* Data to write */ + + assert( (pList->flags&PGHDR_NEED_SYNC)==0 ); + if( pList->pgno==1 ) pager_write_changecounter(pList); + + /* Encode the database */ + CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM_BKPT, pData); + + /* Write out the page data. */ + rc = sqlcipher_sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset); + + /* If page 1 was just written, update Pager.dbFileVers to match + ** the value now stored in the database file. If writing this + ** page caused the database file to grow, update dbFileSize. + */ + if( pgno==1 ){ + memcpy(&pPager->dbFileVers, &pData[24], sizeof(pPager->dbFileVers)); + } + if( pgno>pPager->dbFileSize ){ + pPager->dbFileSize = pgno; + } + pPager->aStat[PAGER_STAT_WRITE]++; + + /* Update any backup objects copying the contents of this pager. */ + sqlcipher_sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)pList->pData); + + PAGERTRACE(("STORE %d page %d hash(%08x)\n", + PAGERID(pPager), pgno, pager_pagehash(pList))); + IOTRACE(("PGOUT %p %d\n", pPager, pgno)); + PAGER_INCR(sqlcipher_sqlite3_pager_writedb_count); + }else{ + PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno)); + } + pager_set_pagehash(pList); + pList = pList->pDirty; } + + return rc; } /* -** Discard all pages from cache pCache with a page number (key value) -** greater than or equal to iLimit. Any pinned pages that meet this -** criteria are unpinned before they are discarded. +** Ensure that the sub-journal file is open. If it is already open, this +** function is a no-op. ** -** The PCache mutex must be held when this function is called. +** SQLITE_OK is returned if everything goes according to plan. An +** SQLITE_IOERR_XXX error code is returned if a call to sqlcipher_sqlite3OsOpen() +** fails. */ -static void pcache1TruncateUnsafe( - PCache1 *pCache, /* The cache to truncate */ - unsigned int iLimit /* Drop pages with this pgno or larger */ -){ - TESTONLY( int nPage = 0; ) /* To assert pCache->nPage is correct */ - unsigned int h, iStop; - assert( sqlcipher_sqlite3_mutex_held(pCache->pGroup->mutex) ); - assert( pCache->iMaxKey >= iLimit ); - assert( pCache->nHash > 0 ); - if( pCache->iMaxKey - iLimit < pCache->nHash ){ - /* If we are just shaving the last few pages off the end of the - ** cache, then there is no point in scanning the entire hash table. - ** Only scan those hash slots that might contain pages that need to - ** be removed. */ - h = iLimit % pCache->nHash; - iStop = pCache->iMaxKey % pCache->nHash; - TESTONLY( nPage = -10; ) /* Disable the pCache->nPage validity check */ - }else{ - /* This is the general case where many pages are being removed. - ** It is necessary to scan the entire hash table */ - h = pCache->nHash/2; - iStop = h - 1; - } - for(;;){ - PgHdr1 **pp; - PgHdr1 *pPage; - assert( hnHash ); - pp = &pCache->apHash[h]; - while( (pPage = *pp)!=0 ){ - if( pPage->iKey>=iLimit ){ - pCache->nPage--; - *pp = pPage->pNext; - if( PAGE_IS_UNPINNED(pPage) ) pcache1PinPage(pPage); - pcache1FreePage(pPage); - }else{ - pp = &pPage->pNext; - TESTONLY( if( nPage>=0 ) nPage++; ) - } +static int openSubJournal(Pager *pPager){ + int rc = SQLITE_OK; + if( !isOpen(pPager->sjfd) ){ + const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE + | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE + | SQLITE_OPEN_DELETEONCLOSE; + int nStmtSpill = sqlcipher_sqlite3Config.nStmtSpill; + if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){ + nStmtSpill = -1; } - if( h==iStop ) break; - h = (h+1) % pCache->nHash; + rc = sqlcipher_sqlite3JournalOpen(pPager->pVfs, 0, pPager->sjfd, flags, nStmtSpill); } - assert( nPage<0 || pCache->nPage==(unsigned)nPage ); + return rc; } -/******************************************************************************/ -/******** sqlcipher_sqlite3_pcache Methods **********************************************/ - /* -** Implementation of the sqlcipher_sqlite3_pcache.xInit method. +** Append a record of the current state of page pPg to the sub-journal. +** +** If successful, set the bit corresponding to pPg->pgno in the bitvecs +** for all open savepoints before returning. +** +** This function returns SQLITE_OK if everything is successful, an IO +** error code if the attempt to write to the sub-journal fails, or +** SQLITE_NOMEM if a malloc fails while setting a bit in a savepoint +** bitvec. */ -static int pcache1Init(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - assert( pcache1.isInit==0 ); - memset(&pcache1, 0, sizeof(pcache1)); +static int subjournalPage(PgHdr *pPg){ + int rc = SQLITE_OK; + Pager *pPager = pPg->pPager; + if( pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ + /* Open the sub-journal, if it has not already been opened */ + assert( pPager->useJournal ); + assert( isOpen(pPager->jfd) || pagerUseWal(pPager) ); + assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 ); + assert( pagerUseWal(pPager) + || pageInJournal(pPager, pPg) + || pPg->pgno>pPager->dbOrigSize + ); + rc = openSubJournal(pPager); - /* - ** The pcache1.separateCache variable is true if each PCache has its own - ** private PGroup (mode-1). pcache1.separateCache is false if the single - ** PGroup in pcache1.grp is used for all page caches (mode-2). - ** - ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT - ** - ** * Use a unified cache in single-threaded applications that have - ** configured a start-time buffer for use as page-cache memory using - ** sqlcipher_sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL - ** pBuf argument. - ** - ** * Otherwise use separate caches (mode-1) - */ -#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) - pcache1.separateCache = 0; -#elif SQLITE_THREADSAFE - pcache1.separateCache = sqlcipher_sqlite3GlobalConfig.pPage==0 - || sqlcipher_sqlite3GlobalConfig.bCoreMutex>0; -#else - pcache1.separateCache = sqlcipher_sqlite3GlobalConfig.pPage==0; -#endif + /* If the sub-journal was opened successfully (or was already open), + ** write the journal record into the file. */ + if( rc==SQLITE_OK ){ + void *pData = pPg->pData; + i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); + char *pData2; -#if SQLITE_THREADSAFE - if( sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ - pcache1.grp.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU); - pcache1.mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PMEM); - } +/* BEGIN SQLCIPHER */ +#if SQLITE_HAS_CODEC + if( !pPager->subjInMemory ){ + CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); + }else #endif - if( pcache1.separateCache - && sqlcipher_sqlite3GlobalConfig.nPage!=0 - && sqlcipher_sqlite3GlobalConfig.pPage==0 - ){ - pcache1.nInitPage = sqlcipher_sqlite3GlobalConfig.nPage; - }else{ - pcache1.nInitPage = 0; +/* END SQLCIPHER */ + pData2 = pData; + PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); + rc = write32bits(pPager->sjfd, offset, pPg->pgno); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3OsWrite(pPager->sjfd, pData2, pPager->pageSize, offset+4); + } + } } - pcache1.grp.mxPinned = 10; - pcache1.isInit = 1; - return SQLITE_OK; + if( rc==SQLITE_OK ){ + pPager->nSubRec++; + assert( pPager->nSavepoint>0 ); + rc = addToSavepointBitvecs(pPager, pPg->pgno); + } + return rc; } - -/* -** Implementation of the sqlcipher_sqlite3_pcache.xShutdown method. -** Note that the static mutex allocated in xInit does -** not need to be freed. -*/ -static void pcache1Shutdown(void *NotUsed){ - UNUSED_PARAMETER(NotUsed); - assert( pcache1.isInit!=0 ); - memset(&pcache1, 0, sizeof(pcache1)); +static int subjournalPageIfRequired(PgHdr *pPg){ + if( subjRequiresPage(pPg) ){ + return subjournalPage(pPg); + }else{ + return SQLITE_OK; + } } -/* forward declaration */ -static void pcache1Destroy(sqlcipher_sqlite3_pcache *p); - /* -** Implementation of the sqlcipher_sqlite3_pcache.xCreate method. +** This function is called by the pcache layer when it has reached some +** soft memory limit. The first argument is a pointer to a Pager object +** (cast as a void*). The pager is always 'purgeable' (not an in-memory +** database). The second argument is a reference to a page that is +** currently dirty but has no outstanding references. The page +** is always associated with the Pager object passed as the first +** argument. ** -** Allocate a new cache. +** The job of this function is to make pPg clean by writing its contents +** out to the database file, if possible. This may involve syncing the +** journal file. +** +** If successful, sqlcipher_sqlite3PcacheMakeClean() is called on the page and +** SQLITE_OK returned. If an IO error occurs while trying to make the +** page clean, the IO error code is returned. If the page cannot be +** made clean for some other reason, but no error occurs, then SQLITE_OK +** is returned by sqlcipher_sqlite3PcacheMakeClean() is not called. */ -static sqlcipher_sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ - PCache1 *pCache; /* The newly created page cache */ - PGroup *pGroup; /* The group the new page cache will belong to */ - int sz; /* Bytes of memory required to allocate the new cache */ +static int pagerStress(void *p, PgHdr *pPg){ + Pager *pPager = (Pager *)p; + int rc = SQLITE_OK; - assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); - assert( szExtra < 300 ); + assert( pPg->pPager==pPager ); + assert( pPg->flags&PGHDR_DIRTY ); - sz = sizeof(PCache1) + sizeof(PGroup)*pcache1.separateCache; - pCache = (PCache1 *)sqlcipher_sqlite3MallocZero(sz); - if( pCache ){ - if( pcache1.separateCache ){ - pGroup = (PGroup*)&pCache[1]; - pGroup->mxPinned = 10; - }else{ - pGroup = &pcache1.grp; + /* The doNotSpill NOSYNC bit is set during times when doing a sync of + ** journal (and adding a new header) is not allowed. This occurs + ** during calls to sqlcipher_sqlite3PagerWrite() while trying to journal multiple + ** pages belonging to the same sector. + ** + ** The doNotSpill ROLLBACK and OFF bits inhibits all cache spilling + ** regardless of whether or not a sync is required. This is set during + ** a rollback or by user request, respectively. + ** + ** Spilling is also prohibited when in an error state since that could + ** lead to database corruption. In the current implementation it + ** is impossible for sqlcipher_sqlite3PcacheFetch() to be called with createFlag==3 + ** while in the error state, hence it is impossible for this routine to + ** be called in the error state. Nevertheless, we include a NEVER() + ** test for the error state as a safeguard against future changes. + */ + if( NEVER(pPager->errCode) ) return SQLITE_OK; + testcase( pPager->doNotSpill & SPILLFLAG_ROLLBACK ); + testcase( pPager->doNotSpill & SPILLFLAG_OFF ); + testcase( pPager->doNotSpill & SPILLFLAG_NOSYNC ); + if( pPager->doNotSpill + && ((pPager->doNotSpill & (SPILLFLAG_ROLLBACK|SPILLFLAG_OFF))!=0 + || (pPg->flags & PGHDR_NEED_SYNC)!=0) + ){ + return SQLITE_OK; + } + + pPager->aStat[PAGER_STAT_SPILL]++; + pPg->pDirty = 0; + if( pagerUseWal(pPager) ){ + /* Write a single frame for this page to the log. */ + rc = subjournalPageIfRequired(pPg); + if( rc==SQLITE_OK ){ + rc = pagerWalFrames(pPager, pPg, 0, 0); } - pcache1EnterMutex(pGroup); - if( pGroup->lru.isAnchor==0 ){ - pGroup->lru.isAnchor = 1; - pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru; + }else{ + +#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE + if( pPager->tempFile==0 ){ + rc = sqlcipher_sqlite3JournalCreate(pPager->jfd); + if( rc!=SQLITE_OK ) return pager_error(pPager, rc); } - pCache->pGroup = pGroup; - pCache->szPage = szPage; - pCache->szExtra = szExtra; - pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1)); - pCache->bPurgeable = (bPurgeable ? 1 : 0); - pcache1ResizeHash(pCache); - if( bPurgeable ){ - pCache->nMin = 10; - pGroup->nMinPage += pCache->nMin; - pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; - pCache->pnPurgeable = &pGroup->nPurgeable; - }else{ - pCache->pnPurgeable = &pCache->nPurgeableDummy; +#endif + + /* Sync the journal file if required. */ + if( pPg->flags&PGHDR_NEED_SYNC + || pPager->eState==PAGER_WRITER_CACHEMOD + ){ + rc = syncJournal(pPager, 1); } - pcache1LeaveMutex(pGroup); - if( pCache->nHash==0 ){ - pcache1Destroy((sqlcipher_sqlite3_pcache*)pCache); - pCache = 0; + + /* Write the contents of the page out to the database file. */ + if( rc==SQLITE_OK ){ + assert( (pPg->flags&PGHDR_NEED_SYNC)==0 ); + rc = pager_write_pagelist(pPager, pPg); } } - return (sqlcipher_sqlite3_pcache *)pCache; -} -/* -** Implementation of the sqlcipher_sqlite3_pcache.xCachesize method. -** -** Configure the cache_size limit for a cache. -*/ -static void pcache1Cachesize(sqlcipher_sqlite3_pcache *p, int nMax){ - PCache1 *pCache = (PCache1 *)p; - if( pCache->bPurgeable ){ - PGroup *pGroup = pCache->pGroup; - pcache1EnterMutex(pGroup); - pGroup->nMaxPage += (nMax - pCache->nMax); - pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; - pCache->nMax = nMax; - pCache->n90pct = pCache->nMax*9/10; - pcache1EnforceMaxPage(pCache); - pcache1LeaveMutex(pGroup); + /* Mark the page as clean. */ + if( rc==SQLITE_OK ){ + PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno)); + sqlcipher_sqlite3PcacheMakeClean(pPg); } + + return pager_error(pPager, rc); } /* -** Implementation of the sqlcipher_sqlite3_pcache.xShrink method. -** -** Free up as much memory as possible. +** Flush all unreferenced dirty pages to disk. */ -static void pcache1Shrink(sqlcipher_sqlite3_pcache *p){ - PCache1 *pCache = (PCache1*)p; - if( pCache->bPurgeable ){ - PGroup *pGroup = pCache->pGroup; - int savedMaxPage; - pcache1EnterMutex(pGroup); - savedMaxPage = pGroup->nMaxPage; - pGroup->nMaxPage = 0; - pcache1EnforceMaxPage(pCache); - pGroup->nMaxPage = savedMaxPage; - pcache1LeaveMutex(pGroup); +SQLITE_PRIVATE int sqlcipher_sqlite3PagerFlush(Pager *pPager){ + int rc = pPager->errCode; + if( !MEMDB ){ + PgHdr *pList = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache); + assert( assert_pager_state(pPager) ); + while( rc==SQLITE_OK && pList ){ + PgHdr *pNext = pList->pDirty; + if( pList->nRef==0 ){ + rc = pagerStress((void*)pPager, pList); + } + pList = pNext; + } } -} -/* -** Implementation of the sqlcipher_sqlite3_pcache.xPagecount method. -*/ -static int pcache1Pagecount(sqlcipher_sqlite3_pcache *p){ - int n; - PCache1 *pCache = (PCache1*)p; - pcache1EnterMutex(pCache->pGroup); - n = pCache->nPage; - pcache1LeaveMutex(pCache->pGroup); - return n; + return rc; } - /* -** Implement steps 3, 4, and 5 of the pcache1Fetch() algorithm described -** in the header of the pcache1Fetch() procedure. +** Allocate and initialize a new Pager object and put a pointer to it +** in *ppPager. The pager should eventually be freed by passing it +** to sqlcipher_sqlite3PagerClose(). ** -** This steps are broken out into a separate procedure because they are -** usually not needed, and by avoiding the stack initialization required -** for these steps, the main pcache1Fetch() procedure can run faster. +** The zFilename argument is the path to the database file to open. +** If zFilename is NULL then a randomly-named temporary file is created +** and used as the file to be cached. Temporary files are be deleted +** automatically when they are closed. If zFilename is ":memory:" then +** all information is held in cache. It is never written to disk. +** This can be used to implement an in-memory database. +** +** The nExtra parameter specifies the number of bytes of space allocated +** along with each page reference. This space is available to the user +** via the sqlcipher_sqlite3PagerGetExtra() API. When a new page is allocated, the +** first 8 bytes of this space are zeroed but the remainder is uninitialized. +** (The extra space is used by btree as the MemPage object.) +** +** The flags argument is used to specify properties that affect the +** operation of the pager. It should be passed some bitwise combination +** of the PAGER_* flags. +** +** The vfsFlags parameter is a bitmask to pass to the flags parameter +** of the xOpen() method of the supplied VFS when opening files. +** +** If the pager object is allocated and the specified file opened +** successfully, SQLITE_OK is returned and *ppPager set to point to +** the new pager object. If an error occurs, *ppPager is set to NULL +** and error code returned. This function may return SQLITE_NOMEM +** (sqlcipher_sqlite3Malloc() is used to allocate memory), SQLITE_CANTOPEN or +** various SQLITE_IO_XXX errors. */ -static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( - PCache1 *pCache, - unsigned int iKey, - int createFlag +SQLITE_PRIVATE int sqlcipher_sqlite3PagerOpen( + sqlcipher_sqlite3_vfs *pVfs, /* The virtual file system to use */ + Pager **ppPager, /* OUT: Return the Pager structure here */ + const char *zFilename, /* Name of the database file to open */ + int nExtra, /* Extra bytes append to each in-memory page */ + int flags, /* flags controlling this file */ + int vfsFlags, /* flags passed through to sqlcipher_sqlite3_vfs.xOpen() */ + void (*xReinit)(DbPage*) /* Function to reinitialize pages */ ){ - unsigned int nPinned; - PGroup *pGroup = pCache->pGroup; - PgHdr1 *pPage = 0; + u8 *pPtr; + Pager *pPager = 0; /* Pager object to allocate and return */ + int rc = SQLITE_OK; /* Return code */ + int tempFile = 0; /* True for temp files (incl. in-memory files) */ + int memDb = 0; /* True if this is an in-memory file */ +#ifndef SQLITE_OMIT_DESERIALIZE + int memJM = 0; /* Memory journal mode */ +#else +# define memJM 0 +#endif + int readOnly = 0; /* True if this is a read-only file */ + int journalFileSize; /* Bytes to allocate for each journal fd */ + char *zPathname = 0; /* Full path to database file */ + int nPathname = 0; /* Number of bytes in zPathname */ + int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ + int pcacheSize = sqlcipher_sqlite3PcacheSize(); /* Bytes to allocate for PCache */ + u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ + const char *zUri = 0; /* URI args to copy */ + int nUriByte = 1; /* Number of bytes of URI args at *zUri */ + int nUri = 0; /* Number of URI parameters */ - /* Step 3: Abort if createFlag is 1 but the cache is nearly full */ - assert( pCache->nPage >= pCache->nRecyclable ); - nPinned = pCache->nPage - pCache->nRecyclable; - assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage ); - assert( pCache->n90pct == pCache->nMax*9/10 ); - if( createFlag==1 && ( - nPinned>=pGroup->mxPinned - || nPinned>=pCache->n90pct - || (pcache1UnderMemoryPressure(pCache) && pCache->nRecyclablenPage>=pCache->nHash ) pcache1ResizeHash(pCache); - assert( pCache->nHash>0 && pCache->apHash ); + /* Compute and store the full pathname in an allocated buffer pointed + ** to by zPathname, length nPathname. Or, if this is a temporary file, + ** leave both nPathname and zPathname set to 0. + */ + if( zFilename && zFilename[0] ){ + const char *z; + nPathname = pVfs->mxPathname+1; + zPathname = sqlcipher_sqlite3DbMallocRaw(0, nPathname*2); + if( zPathname==0 ){ + return SQLITE_NOMEM_BKPT; + } + zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ + rc = sqlcipher_sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_OK_SYMLINK ){ + if( vfsFlags & SQLITE_OPEN_NOFOLLOW ){ + rc = SQLITE_CANTOPEN_SYMLINK; + }else{ + rc = SQLITE_OK; + } + } + } + nPathname = sqlcipher_sqlite3Strlen30(zPathname); + z = zUri = &zFilename[sqlcipher_sqlite3Strlen30(zFilename)+1]; + while( *z ){ + z += strlen(z)+1; + z += strlen(z)+1; + nUri++; + } + nUriByte = (int)(&z[1] - zUri); + assert( nUriByte>=1 ); + if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ + /* This branch is taken when the journal path required by + ** the database being opened will be more than pVfs->mxPathname + ** bytes in length. This means the database cannot be opened, + ** as it will not be possible to open the journal file or even + ** check for a hot-journal before reading. + */ + rc = SQLITE_CANTOPEN_BKPT; + } + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3DbFree(0, zPathname); + return rc; + } + } + + /* Allocate memory for the Pager structure, PCache object, the + ** three file descriptors, the database file name and the journal + ** file name. The layout in memory is as follows: + ** + ** Pager object (sizeof(Pager) bytes) + ** PCache object (sqlcipher_sqlite3PcacheSize() bytes) + ** Database file handle (pVfs->szOsFile bytes) + ** Sub-journal file handle (journalFileSize bytes) + ** Main journal file handle (journalFileSize bytes) + ** Ptr back to the Pager (sizeof(Pager*) bytes) + ** \0\0\0\0 database prefix (4 bytes) + ** Database file name (nPathname+1 bytes) + ** URI query parameters (nUriByte bytes) + ** Journal filename (nPathname+8+1 bytes) + ** WAL filename (nPathname+4+1 bytes) + ** \0\0\0 terminator (3 bytes) + ** + ** Some 3rd-party software, over which we have no control, depends on + ** the specific order of the filenames and the \0 separators between them + ** so that it can (for example) find the database filename given the WAL + ** filename without using the sqlcipher_sqlite3_filename_database() API. This is a + ** misuse of SQLite and a bug in the 3rd-party software, but the 3rd-party + ** software is in widespread use, so we try to avoid changing the filename + ** order and formatting if possible. In particular, the details of the + ** filename format expected by 3rd-party software should be as follows: + ** + ** - Main Database Path + ** - \0 + ** - Multiple URI components consisting of: + ** - Key + ** - \0 + ** - Value + ** - \0 + ** - \0 + ** - Journal Path + ** - \0 + ** - WAL Path (zWALName) + ** - \0 + ** + ** The sqlcipher_sqlite3_create_filename() interface and the databaseFilename() utility + ** that is used by sqlcipher_sqlite3_filename_database() and kin also depend on the + ** specific formatting and order of the various filenames, so if the format + ** changes here, be sure to change it there as well. + */ + pPtr = (u8 *)sqlcipher_sqlite3MallocZero( + ROUND8(sizeof(*pPager)) + /* Pager structure */ + ROUND8(pcacheSize) + /* PCache object */ + ROUND8(pVfs->szOsFile) + /* The main db file */ + journalFileSize * 2 + /* The two journal files */ + sizeof(pPager) + /* Space to hold a pointer */ + 4 + /* Database prefix */ + nPathname + 1 + /* database filename */ + nUriByte + /* query parameters */ + nPathname + 8 + 1 + /* Journal filename */ +#ifndef SQLITE_OMIT_WAL + nPathname + 4 + 1 + /* WAL filename */ +#endif + 3 /* Terminator */ + ); + assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) ); + if( !pPtr ){ + sqlcipher_sqlite3DbFree(0, zPathname); + return SQLITE_NOMEM_BKPT; + } + pPager = (Pager*)pPtr; pPtr += ROUND8(sizeof(*pPager)); + pPager->pPCache = (PCache*)pPtr; pPtr += ROUND8(pcacheSize); + pPager->fd = (sqlcipher_sqlite3_file*)pPtr; pPtr += ROUND8(pVfs->szOsFile); + pPager->sjfd = (sqlcipher_sqlite3_file*)pPtr; pPtr += journalFileSize; + pPager->jfd = (sqlcipher_sqlite3_file*)pPtr; pPtr += journalFileSize; + assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); + memcpy(pPtr, &pPager, sizeof(pPager)); pPtr += sizeof(pPager); - /* Step 4. Try to recycle a page. */ - if( pCache->bPurgeable - && !pGroup->lru.pLruPrev->isAnchor - && ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache)) - ){ - PCache1 *pOther; - pPage = pGroup->lru.pLruPrev; - assert( PAGE_IS_UNPINNED(pPage) ); - pcache1RemoveFromHash(pPage, 0); - pcache1PinPage(pPage); - pOther = pPage->pCache; - if( pOther->szAlloc != pCache->szAlloc ){ - pcache1FreePage(pPage); - pPage = 0; + /* Fill in the Pager.zFilename and pPager.zQueryParam fields */ + pPtr += 4; /* Skip zero prefix */ + pPager->zFilename = (char*)pPtr; + if( nPathname>0 ){ + memcpy(pPtr, zPathname, nPathname); pPtr += nPathname + 1; + if( zUri ){ + memcpy(pPtr, zUri, nUriByte); pPtr += nUriByte; }else{ - pGroup->nPurgeable -= (pOther->bPurgeable - pCache->bPurgeable); + pPtr++; } } - /* Step 5. If a usable page buffer has still not been found, - ** attempt to allocate a new one. - */ - if( !pPage ){ - pPage = pcache1AllocPage(pCache, createFlag==1); - } - if( pPage ){ - unsigned int h = iKey % pCache->nHash; - pCache->nPage++; - pPage->iKey = iKey; - pPage->pNext = pCache->apHash[h]; - pPage->pCache = pCache; - pPage->pLruNext = 0; - /* pPage->pLruPrev = 0; - ** No need to clear pLruPrev since it is not accessed when pLruNext==0 */ - *(void **)pPage->page.pExtra = 0; - pCache->apHash[h] = pPage; - if( iKey>pCache->iMaxKey ){ - pCache->iMaxKey = iKey; - } + /* Fill in Pager.zJournal */ + if( nPathname>0 ){ + pPager->zJournal = (char*)pPtr; + memcpy(pPtr, zPathname, nPathname); pPtr += nPathname; + memcpy(pPtr, "-journal",8); pPtr += 8 + 1; +#ifdef SQLITE_ENABLE_8_3_NAMES + sqlcipher_sqlite3FileSuffix3(zFilename,pPager->zJournal); + pPtr = (u8*)(pPager->zJournal + sqlcipher_sqlite3Strlen30(pPager->zJournal)+1); +#endif + }else{ + pPager->zJournal = 0; } - return pPage; -} - -/* -** Implementation of the sqlcipher_sqlite3_pcache.xFetch method. -** -** Fetch a page by key value. -** -** Whether or not a new page may be allocated by this function depends on -** the value of the createFlag argument. 0 means do not allocate a new -** page. 1 means allocate a new page if space is easily available. 2 -** means to try really hard to allocate a new page. -** -** For a non-purgeable cache (a cache used as the storage for an in-memory -** database) there is really no difference between createFlag 1 and 2. So -** the calling function (pcache.c) will never have a createFlag of 1 on -** a non-purgeable cache. -** -** There are three different approaches to obtaining space for a page, -** depending on the value of parameter createFlag (which may be 0, 1 or 2). -** -** 1. Regardless of the value of createFlag, the cache is searched for a -** copy of the requested page. If one is found, it is returned. -** -** 2. If createFlag==0 and the page is not already in the cache, NULL is -** returned. -** -** 3. If createFlag is 1, and the page is not already in the cache, then -** return NULL (do not allocate a new page) if any of the following -** conditions are true: -** -** (a) the number of pages pinned by the cache is greater than -** PCache1.nMax, or -** -** (b) the number of pages pinned by the cache is greater than -** the sum of nMax for all purgeable caches, less the sum of -** nMin for all other purgeable caches, or -** -** 4. If none of the first three conditions apply and the cache is marked -** as purgeable, and if one of the following is true: -** -** (a) The number of pages allocated for the cache is already -** PCache1.nMax, or -** -** (b) The number of pages allocated for all purgeable caches is -** already equal to or greater than the sum of nMax for all -** purgeable caches, -** -** (c) The system is under memory pressure and wants to avoid -** unnecessary pages cache entry allocations -** -** then attempt to recycle a page from the LRU list. If it is the right -** size, return the recycled buffer. Otherwise, free the buffer and -** proceed to step 5. -** -** 5. Otherwise, allocate and return a new page buffer. -** -** There are two versions of this routine. pcache1FetchWithMutex() is -** the general case. pcache1FetchNoMutex() is a faster implementation for -** the common case where pGroup->mutex is NULL. The pcache1Fetch() wrapper -** invokes the appropriate routine. -*/ -static PgHdr1 *pcache1FetchNoMutex( - sqlcipher_sqlite3_pcache *p, - unsigned int iKey, - int createFlag -){ - PCache1 *pCache = (PCache1 *)p; - PgHdr1 *pPage = 0; - - /* Step 1: Search the hash table for an existing entry. */ - pPage = pCache->apHash[iKey % pCache->nHash]; - while( pPage && pPage->iKey!=iKey ){ pPage = pPage->pNext; } - /* Step 2: If the page was found in the hash table, then return it. - ** If the page was not in the hash table and createFlag is 0, abort. - ** Otherwise (page not in hash and createFlag!=0) continue with - ** subsequent steps to try to create the page. */ - if( pPage ){ - if( PAGE_IS_UNPINNED(pPage) ){ - return pcache1PinPage(pPage); - }else{ - return pPage; - } - }else if( createFlag ){ - /* Steps 3, 4, and 5 implemented by this subroutine */ - return pcache1FetchStage2(pCache, iKey, createFlag); +#ifndef SQLITE_OMIT_WAL + /* Fill in Pager.zWal */ + if( nPathname>0 ){ + pPager->zWal = (char*)pPtr; + memcpy(pPtr, zPathname, nPathname); pPtr += nPathname; + memcpy(pPtr, "-wal", 4); pPtr += 4 + 1; +#ifdef SQLITE_ENABLE_8_3_NAMES + sqlcipher_sqlite3FileSuffix3(zFilename, pPager->zWal); + pPtr = (u8*)(pPager->zWal + sqlcipher_sqlite3Strlen30(pPager->zWal)+1); +#endif }else{ - return 0; + pPager->zWal = 0; } -} -#if PCACHE1_MIGHT_USE_GROUP_MUTEX -static PgHdr1 *pcache1FetchWithMutex( - sqlcipher_sqlite3_pcache *p, - unsigned int iKey, - int createFlag -){ - PCache1 *pCache = (PCache1 *)p; - PgHdr1 *pPage; - - pcache1EnterMutex(pCache->pGroup); - pPage = pcache1FetchNoMutex(p, iKey, createFlag); - assert( pPage==0 || pCache->iMaxKey>=iKey ); - pcache1LeaveMutex(pCache->pGroup); - return pPage; -} #endif -static sqlcipher_sqlite3_pcache_page *pcache1Fetch( - sqlcipher_sqlite3_pcache *p, - unsigned int iKey, - int createFlag -){ -#if PCACHE1_MIGHT_USE_GROUP_MUTEX || defined(SQLITE_DEBUG) - PCache1 *pCache = (PCache1 *)p; + (void)pPtr; /* Suppress warning about unused pPtr value */ + + if( nPathname ) sqlcipher_sqlite3DbFree(0, zPathname); + pPager->pVfs = pVfs; + pPager->vfsFlags = vfsFlags; + + /* Open the pager file. + */ + if( zFilename && zFilename[0] ){ + int fout = 0; /* VFS flags returned by xOpen() */ + rc = sqlcipher_sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); + assert( !memDb ); +#ifndef SQLITE_OMIT_DESERIALIZE + pPager->memVfs = memJM = (fout&SQLITE_OPEN_MEMORY)!=0; #endif + readOnly = (fout&SQLITE_OPEN_READONLY)!=0; - assert( offsetof(PgHdr1,page)==0 ); - assert( pCache->bPurgeable || createFlag!=1 ); - assert( pCache->bPurgeable || pCache->nMin==0 ); - assert( pCache->bPurgeable==0 || pCache->nMin==10 ); - assert( pCache->nMin==0 || pCache->bPurgeable ); - assert( pCache->nHash>0 ); -#if PCACHE1_MIGHT_USE_GROUP_MUTEX - if( pCache->pGroup->mutex ){ - return (sqlcipher_sqlite3_pcache_page*)pcache1FetchWithMutex(p, iKey, createFlag); - }else + /* If the file was successfully opened for read/write access, + ** choose a default page size in case we have to create the + ** database file. The default page size is the maximum of: + ** + ** + SQLITE_DEFAULT_PAGE_SIZE, + ** + The value returned by sqlcipher_sqlite3OsSectorSize() + ** + The largest page size that can be written atomically. + */ + if( rc==SQLITE_OK ){ + int iDc = sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd); + if( !readOnly ){ + setSectorSize(pPager); + assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE); + if( szPageDfltsectorSize ){ + if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){ + szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE; + }else{ + szPageDflt = (u32)pPager->sectorSize; + } + } +#ifdef SQLITE_ENABLE_ATOMIC_WRITE + { + int ii; + assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); + assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); + assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536); + for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){ + if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){ + szPageDflt = ii; + } + } + } #endif - { - return (sqlcipher_sqlite3_pcache_page*)pcache1FetchNoMutex(p, iKey, createFlag); + } + pPager->noLock = sqlcipher_sqlite3_uri_boolean(pPager->zFilename, "nolock", 0); + if( (iDc & SQLITE_IOCAP_IMMUTABLE)!=0 + || sqlcipher_sqlite3_uri_boolean(pPager->zFilename, "immutable", 0) ){ + vfsFlags |= SQLITE_OPEN_READONLY; + goto act_like_temp_file; + } + } + }else{ + /* If a temporary file is requested, it is not opened immediately. + ** In this case we accept the default page size and delay actually + ** opening the file until the first call to OsWrite(). + ** + ** This branch is also run for an in-memory database. An in-memory + ** database is the same as a temp-file that is never written out to + ** disk and uses an in-memory rollback journal. + ** + ** This branch also runs for files marked as immutable. + */ +act_like_temp_file: + tempFile = 1; + pPager->eState = PAGER_READER; /* Pretend we already have a lock */ + pPager->eLock = EXCLUSIVE_LOCK; /* Pretend we are in EXCLUSIVE mode */ + pPager->noLock = 1; /* Do no locking */ + readOnly = (vfsFlags&SQLITE_OPEN_READONLY); } -} - - -/* -** Implementation of the sqlcipher_sqlite3_pcache.xUnpin method. -** -** Mark a page as unpinned (eligible for asynchronous recycling). -*/ -static void pcache1Unpin( - sqlcipher_sqlite3_pcache *p, - sqlcipher_sqlite3_pcache_page *pPg, - int reuseUnlikely -){ - PCache1 *pCache = (PCache1 *)p; - PgHdr1 *pPage = (PgHdr1 *)pPg; - PGroup *pGroup = pCache->pGroup; - assert( pPage->pCache==pCache ); - pcache1EnterMutex(pGroup); - - /* It is an error to call this function if the page is already - ** part of the PGroup LRU list. + /* The following call to PagerSetPagesize() serves to set the value of + ** Pager.pageSize and to allocate the Pager.pTmpSpace buffer. */ - assert( pPage->pLruNext==0 ); - assert( PAGE_IS_PINNED(pPage) ); - - if( reuseUnlikely || pGroup->nPurgeable>pGroup->nMaxPage ){ - pcache1RemoveFromHash(pPage, 1); - }else{ - /* Add the page to the PGroup LRU list. */ - PgHdr1 **ppFirst = &pGroup->lru.pLruNext; - pPage->pLruPrev = &pGroup->lru; - (pPage->pLruNext = *ppFirst)->pLruPrev = pPage; - *ppFirst = pPage; - pCache->nRecyclable++; + if( rc==SQLITE_OK ){ + assert( pPager->memDb==0 ); + rc = sqlcipher_sqlite3PagerSetPagesize(pPager, &szPageDflt, -1); + testcase( rc!=SQLITE_OK ); } - pcache1LeaveMutex(pCache->pGroup); -} + /* Initialize the PCache object. */ + if( rc==SQLITE_OK ){ + nExtra = ROUND8(nExtra); + assert( nExtra>=8 && nExtra<1000 ); + rc = sqlcipher_sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, + !memDb?pagerStress:0, (void *)pPager, pPager->pPCache); + } -/* -** Implementation of the sqlcipher_sqlite3_pcache.xRekey method. -*/ -static void pcache1Rekey( - sqlcipher_sqlite3_pcache *p, - sqlcipher_sqlite3_pcache_page *pPg, - unsigned int iOld, - unsigned int iNew -){ - PCache1 *pCache = (PCache1 *)p; - PgHdr1 *pPage = (PgHdr1 *)pPg; - PgHdr1 **pp; - unsigned int h; - assert( pPage->iKey==iOld ); - assert( pPage->pCache==pCache ); + /* If an error occurred above, free the Pager structure and close the file. + */ + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3OsClose(pPager->fd); + sqlcipher_sqlite3PageFree(pPager->pTmpSpace); + sqlcipher_sqlite3_free(pPager); + return rc; + } - pcache1EnterMutex(pCache->pGroup); + PAGERTRACE(("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename)); + IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename)) - h = iOld%pCache->nHash; - pp = &pCache->apHash[h]; - while( (*pp)!=pPage ){ - pp = &(*pp)->pNext; + pPager->useJournal = (u8)useJournal; + /* pPager->stmtOpen = 0; */ + /* pPager->stmtInUse = 0; */ + /* pPager->nRef = 0; */ + /* pPager->stmtSize = 0; */ + /* pPager->stmtJSize = 0; */ + /* pPager->nPage = 0; */ + pPager->mxPgno = SQLITE_MAX_PAGE_COUNT; + /* pPager->state = PAGER_UNLOCK; */ + /* pPager->errMask = 0; */ + pPager->tempFile = (u8)tempFile; + assert( tempFile==PAGER_LOCKINGMODE_NORMAL + || tempFile==PAGER_LOCKINGMODE_EXCLUSIVE ); + assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 ); + pPager->exclusiveMode = (u8)tempFile; + pPager->changeCountDone = pPager->tempFile; + pPager->memDb = (u8)memDb; + pPager->readOnly = (u8)readOnly; + assert( useJournal || pPager->tempFile ); + pPager->noSync = pPager->tempFile; + if( pPager->noSync ){ + assert( pPager->fullSync==0 ); + assert( pPager->extraSync==0 ); + assert( pPager->syncFlags==0 ); + assert( pPager->walSyncFlags==0 ); + }else{ + pPager->fullSync = 1; + pPager->extraSync = 0; + pPager->syncFlags = SQLITE_SYNC_NORMAL; + pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2); } - *pp = pPage->pNext; - - h = iNew%pCache->nHash; - pPage->iKey = iNew; - pPage->pNext = pCache->apHash[h]; - pCache->apHash[h] = pPage; - if( iNew>pCache->iMaxKey ){ - pCache->iMaxKey = iNew; + /* pPager->pFirst = 0; */ + /* pPager->pFirstSynced = 0; */ + /* pPager->pLast = 0; */ + pPager->nExtra = (u16)nExtra; + pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT; + assert( isOpen(pPager->fd) || tempFile ); + setSectorSize(pPager); + if( !useJournal ){ + pPager->journalMode = PAGER_JOURNALMODE_OFF; + }else if( memDb || memJM ){ + pPager->journalMode = PAGER_JOURNALMODE_MEMORY; } + /* pPager->xBusyHandler = 0; */ + /* pPager->pBusyHandlerArg = 0; */ + pPager->xReiniter = xReinit; + setGetterMethod(pPager); + /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */ + /* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */ - pcache1LeaveMutex(pCache->pGroup); + *ppPager = pPager; + return SQLITE_OK; } /* -** Implementation of the sqlcipher_sqlite3_pcache.xTruncate method. -** -** Discard all unpinned pages in the cache with a page number equal to -** or greater than parameter iLimit. Any pinned pages with a page number -** equal to or greater than iLimit are implicitly unpinned. +** Return the sqlcipher_sqlite3_file for the main database given the name +** of the corresonding WAL or Journal name as passed into +** xOpen. */ -static void pcache1Truncate(sqlcipher_sqlite3_pcache *p, unsigned int iLimit){ - PCache1 *pCache = (PCache1 *)p; - pcache1EnterMutex(pCache->pGroup); - if( iLimit<=pCache->iMaxKey ){ - pcache1TruncateUnsafe(pCache, iLimit); - pCache->iMaxKey = iLimit-1; +SQLITE_API sqlcipher_sqlite3_file *sqlcipher_sqlite3_database_file_object(const char *zName){ + Pager *pPager; + while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){ + zName--; } - pcache1LeaveMutex(pCache->pGroup); + pPager = *(Pager**)(zName - 4 - sizeof(Pager*)); + return pPager->fd; } + /* -** Implementation of the sqlcipher_sqlite3_pcache.xDestroy method. +** This function is called after transitioning from PAGER_UNLOCK to +** PAGER_SHARED state. It tests if there is a hot journal present in +** the file-system for the given pager. A hot journal is one that +** needs to be played back. According to this function, a hot-journal +** file exists if the following criteria are met: ** -** Destroy a cache allocated using pcache1Create(). +** * The journal file exists in the file system, and +** * No process holds a RESERVED or greater lock on the database file, and +** * The database file itself is greater than 0 bytes in size, and +** * The first byte of the journal file exists and is not 0x00. +** +** If the current size of the database file is 0 but a journal file +** exists, that is probably an old journal left over from a prior +** database with the same name. In this case the journal file is +** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK +** is returned. +** +** This routine does not check if there is a super-journal filename +** at the end of the file. If there is, and that super-journal file +** does not exist, then the journal file is not really hot. In this +** case this routine will return a false-positive. The pager_playback() +** routine will discover that the journal file is not really hot and +** will not roll it back. +** +** If a hot-journal file is found to exist, *pExists is set to 1 and +** SQLITE_OK returned. If no hot-journal file is present, *pExists is +** set to 0 and SQLITE_OK returned. If an IO error occurs while trying +** to determine whether or not a hot-journal file exists, the IO error +** code is returned and the value of *pExists is undefined. */ -static void pcache1Destroy(sqlcipher_sqlite3_pcache *p){ - PCache1 *pCache = (PCache1 *)p; - PGroup *pGroup = pCache->pGroup; - assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); - pcache1EnterMutex(pGroup); - if( pCache->nPage ) pcache1TruncateUnsafe(pCache, 0); - assert( pGroup->nMaxPage >= pCache->nMax ); - pGroup->nMaxPage -= pCache->nMax; - assert( pGroup->nMinPage >= pCache->nMin ); - pGroup->nMinPage -= pCache->nMin; - pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; - pcache1EnforceMaxPage(pCache); - pcache1LeaveMutex(pGroup); - sqlcipher_sqlite3_free(pCache->pBulk); - sqlcipher_sqlite3_free(pCache->apHash); - sqlcipher_sqlite3_free(pCache); -} +static int hasHotJournal(Pager *pPager, int *pExists){ + sqlcipher_sqlite3_vfs * const pVfs = pPager->pVfs; + int rc = SQLITE_OK; /* Return code */ + int exists = 1; /* True if a journal file is present */ + int jrnlOpen = !!isOpen(pPager->jfd); -/* -** This function is called during initialization (sqlcipher_sqlite3_initialize()) to -** install the default pluggable cache module, assuming the user has not -** already provided an alternative. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PCacheSetDefault(void){ - static const sqlcipher_sqlite3_pcache_methods2 defaultMethods = { - 1, /* iVersion */ - 0, /* pArg */ - pcache1Init, /* xInit */ - pcache1Shutdown, /* xShutdown */ - pcache1Create, /* xCreate */ - pcache1Cachesize, /* xCachesize */ - pcache1Pagecount, /* xPagecount */ - pcache1Fetch, /* xFetch */ - pcache1Unpin, /* xUnpin */ - pcache1Rekey, /* xRekey */ - pcache1Truncate, /* xTruncate */ - pcache1Destroy, /* xDestroy */ - pcache1Shrink /* xShrink */ - }; - sqlcipher_sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods); -} + assert( pPager->useJournal ); + assert( isOpen(pPager->fd) ); + assert( pPager->eState==PAGER_OPEN ); -/* -** Return the size of the header on each page of this PCACHE implementation. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); } + assert( jrnlOpen==0 || ( sqlcipher_sqlite3OsDeviceCharacteristics(pPager->jfd) & + SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN + )); -/* -** Return the global mutex used by this PCACHE implementation. The -** sqlcipher_sqlite3_status() routine needs access to this mutex. -*/ -SQLITE_PRIVATE sqlcipher_sqlite3_mutex *sqlcipher_sqlite3Pcache1Mutex(void){ - return pcache1.mutex; -} + *pExists = 0; + if( !jrnlOpen ){ + rc = sqlcipher_sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); + } + if( rc==SQLITE_OK && exists ){ + int locked = 0; /* True if some process holds a RESERVED lock */ -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -/* -** This function is called to free superfluous dynamically allocated memory -** held by the pager system. Memory in use by any SQLite pager allocated -** by the current thread may be sqlcipher_sqlite3_free()ed. -** -** nReq is the number of bytes of memory required. Once this much has -** been released, the function returns. The return value is the total number -** of bytes of memory released. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PcacheReleaseMemory(int nReq){ - int nFree = 0; - assert( sqlcipher_sqlite3_mutex_notheld(pcache1.grp.mutex) ); - assert( sqlcipher_sqlite3_mutex_notheld(pcache1.mutex) ); - if( sqlcipher_sqlite3GlobalConfig.pPage==0 ){ - PgHdr1 *p; - pcache1EnterMutex(&pcache1.grp); - while( (nReq<0 || nFreeisAnchor==0 - ){ - nFree += pcache1MemSize(p->page.pBuf); -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - nFree += sqlcipher_sqlite3MemSize(p); -#endif - assert( PAGE_IS_UNPINNED(p) ); - pcache1PinPage(p); - pcache1RemoveFromHash(p, 1); + /* Race condition here: Another process might have been holding the + ** the RESERVED lock and have a journal open at the sqlcipher_sqlite3OsAccess() + ** call above, but then delete the journal and drop the lock before + ** we get to the following sqlcipher_sqlite3OsCheckReservedLock() call. If that + ** is the case, this routine might think there is a hot journal when + ** in fact there is none. This results in a false-positive which will + ** be dealt with by the playback routine. Ticket #3883. + */ + rc = sqlcipher_sqlite3OsCheckReservedLock(pPager->fd, &locked); + if( rc==SQLITE_OK && !locked ){ + Pgno nPage; /* Number of pages in database file */ + + assert( pPager->tempFile==0 ); + rc = pagerPagecount(pPager, &nPage); + if( rc==SQLITE_OK ){ + /* If the database is zero pages in size, that means that either (1) the + ** journal is a remnant from a prior database with the same name where + ** the database file but not the journal was deleted, or (2) the initial + ** transaction that populates a new database is being rolled back. + ** In either case, the journal file can be deleted. However, take care + ** not to delete the journal file if it is already open due to + ** journal_mode=PERSIST. + */ + if( nPage==0 && !jrnlOpen ){ + sqlcipher_sqlite3BeginBenignMalloc(); + if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){ + sqlcipher_sqlite3OsDelete(pVfs, pPager->zJournal, 0); + if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK); + } + sqlcipher_sqlite3EndBenignMalloc(); + }else{ + /* The journal file exists and no other connection has a reserved + ** or greater lock on the database file. Now check that there is + ** at least one non-zero bytes at the start of the journal file. + ** If there is, then we consider this journal to be hot. If not, + ** it can be ignored. + */ + if( !jrnlOpen ){ + int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL; + rc = sqlcipher_sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f); + } + if( rc==SQLITE_OK ){ + u8 first = 0; + rc = sqlcipher_sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0); + if( rc==SQLITE_IOERR_SHORT_READ ){ + rc = SQLITE_OK; + } + if( !jrnlOpen ){ + sqlcipher_sqlite3OsClose(pPager->jfd); + } + *pExists = (first!=0); + }else if( rc==SQLITE_CANTOPEN ){ + /* If we cannot open the rollback journal file in order to see if + ** it has a zero header, that might be due to an I/O error, or + ** it might be due to the race condition described above and in + ** ticket #3883. Either way, assume that the journal is hot. + ** This might be a false positive. But if it is, then the + ** automatic journal playback and recovery mechanism will deal + ** with it under an EXCLUSIVE lock where we do not need to + ** worry so much with race conditions. + */ + *pExists = 1; + rc = SQLITE_OK; + } + } + } } - pcache1LeaveMutex(&pcache1.grp); } - return nFree; -} -#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */ -#ifdef SQLITE_TEST -/* -** This function is used by test procedures to inspect the internal state -** of the global cache. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PcacheStats( - int *pnCurrent, /* OUT: Total number of pages cached */ - int *pnMax, /* OUT: Global maximum cache size */ - int *pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */ - int *pnRecyclable /* OUT: Total number of pages available for recycling */ -){ - PgHdr1 *p; - int nRecyclable = 0; - for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){ - assert( PAGE_IS_UNPINNED(p) ); - nRecyclable++; - } - *pnCurrent = pcache1.grp.nPurgeable; - *pnMax = (int)pcache1.grp.nMaxPage; - *pnMin = (int)pcache1.grp.nMinPage; - *pnRecyclable = nRecyclable; + return rc; } -#endif -/************** End of pcache1.c *********************************************/ -/************** Begin file rowset.c ******************************************/ /* -** 2008 December 3 -** -** 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 module implements an object we call a "RowSet". -** -** The RowSet object is a collection of rowids. Rowids -** are inserted into the RowSet in an arbitrary order. Inserts -** can be intermixed with tests to see if a given rowid has been -** previously inserted into the RowSet. -** -** After all inserts are finished, it is possible to extract the -** elements of the RowSet in sorted order. Once this extraction -** process has started, no new elements may be inserted. -** -** Hence, the primitive operations for a RowSet are: -** -** CREATE -** INSERT -** TEST -** SMALLEST -** DESTROY -** -** The CREATE and DESTROY primitives are the constructor and destructor, -** obviously. The INSERT primitive adds a new element to the RowSet. -** TEST checks to see if an element is already in the RowSet. SMALLEST -** extracts the least value from the RowSet. -** -** The INSERT primitive might allocate additional memory. Memory is -** allocated in chunks so most INSERTs do no allocation. There is an -** upper bound on the size of allocated memory. No memory is freed -** until DESTROY. +** This function is called to obtain a shared lock on the database file. +** It is illegal to call sqlcipher_sqlite3PagerGet() until after this function +** has been successfully called. If a shared-lock is already held when +** this function is called, it is a no-op. ** -** The TEST primitive includes a "batch" number. The TEST primitive -** will only see elements that were inserted before the last change -** in the batch number. In other words, if an INSERT occurs between -** two TESTs where the TESTs have the same batch nubmer, then the -** value added by the INSERT will not be visible to the second TEST. -** The initial batch number is zero, so if the very first TEST contains -** a non-zero batch number, it will see all prior INSERTs. +** The following operations are also performed by this function. ** -** No INSERTs may occurs after a SMALLEST. An assertion will fail if -** that is attempted. +** 1) If the pager is currently in PAGER_OPEN state (no lock held +** on the database file), then an attempt is made to obtain a +** SHARED lock on the database file. Immediately after obtaining +** the SHARED lock, the file-system is checked for a hot-journal, +** which is played back if present. Following any hot-journal +** rollback, the contents of the cache are validated by checking +** the 'change-counter' field of the database file header and +** discarded if they are found to be invalid. ** -** The cost of an INSERT is roughly constant. (Sometimes new memory -** has to be allocated on an INSERT.) The cost of a TEST with a new -** batch number is O(NlogN) where N is the number of elements in the RowSet. -** The cost of a TEST using the same batch number is O(logN). The cost -** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST -** primitives are constant time. The cost of DESTROY is O(N). +** 2) If the pager is running in exclusive-mode, and there are currently +** no outstanding references to any pages, and is in the error state, +** then an attempt is made to clear the error state by discarding +** the contents of the page cache and rolling back any open journal +** file. ** -** TEST and SMALLEST may not be used by the same RowSet. This used to -** be possible, but the feature was not used, so it was removed in order -** to simplify the code. +** If everything is successful, SQLITE_OK is returned. If an IO error +** occurs while locking the database, checking for a hot-journal file or +** rolling back a journal file, the IO error code is returned. */ -/* #include "sqliteInt.h" */ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerSharedLock(Pager *pPager){ + int rc = SQLITE_OK; /* Return code */ + /* This routine is only called from b-tree and only when there are no + ** outstanding pages. This implies that the pager state should either + ** be OPEN or READER. READER is only possible if the pager is or was in + ** exclusive access mode. */ + assert( sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)==0 ); + assert( assert_pager_state(pPager) ); + assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); + assert( pPager->errCode==SQLITE_OK ); -/* -** Target size for allocation chunks. -*/ -#define ROWSET_ALLOCATION_SIZE 1024 + if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){ + int bHotJournal = 1; /* True if there exists a hot journal-file */ -/* -** The number of rowset entries per allocation chunk. -*/ -#define ROWSET_ENTRY_PER_CHUNK \ - ((ROWSET_ALLOCATION_SIZE-8)/sizeof(struct RowSetEntry)) + assert( !MEMDB ); + assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK ); -/* -** Each entry in a RowSet is an instance of the following object. -** -** This same object is reused to store a linked list of trees of RowSetEntry -** objects. In that alternative use, pRight points to the next entry -** in the list, pLeft points to the tree, and v is unused. The -** RowSet.pForest value points to the head of this forest list. -*/ -struct RowSetEntry { - i64 v; /* ROWID value for this entry */ - struct RowSetEntry *pRight; /* Right subtree (larger entries) or list */ - struct RowSetEntry *pLeft; /* Left subtree (smaller entries) */ -}; + rc = pager_wait_on_lock(pPager, SHARED_LOCK); + if( rc!=SQLITE_OK ){ + assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); + goto failed; + } -/* -** RowSetEntry objects are allocated in large chunks (instances of the -** following structure) to reduce memory allocation overhead. The -** chunks are kept on a linked list so that they can be deallocated -** when the RowSet is destroyed. -*/ -struct RowSetChunk { - struct RowSetChunk *pNextChunk; /* Next chunk on list of them all */ - struct RowSetEntry aEntry[ROWSET_ENTRY_PER_CHUNK]; /* Allocated entries */ -}; + /* If a journal file exists, and there is no RESERVED lock on the + ** database file, then it either needs to be played back or deleted. + */ + if( pPager->eLock<=SHARED_LOCK ){ + rc = hasHotJournal(pPager, &bHotJournal); + } + if( rc!=SQLITE_OK ){ + goto failed; + } + if( bHotJournal ){ + if( pPager->readOnly ){ + rc = SQLITE_READONLY_ROLLBACK; + goto failed; + } -/* -** A RowSet in an instance of the following structure. -** -** A typedef of this structure if found in sqliteInt.h. -*/ -struct RowSet { - struct RowSetChunk *pChunk; /* List of all chunk allocations */ - sqlcipher_sqlite3 *db; /* The database connection */ - struct RowSetEntry *pEntry; /* List of entries using pRight */ - struct RowSetEntry *pLast; /* Last entry on the pEntry list */ - struct RowSetEntry *pFresh; /* Source of new entry objects */ - struct RowSetEntry *pForest; /* List of binary trees of entries */ - u16 nFresh; /* Number of objects on pFresh */ - u16 rsFlags; /* Various flags */ - int iBatch; /* Current insert batch */ -}; + /* Get an EXCLUSIVE lock on the database file. At this point it is + ** important that a RESERVED lock is not obtained on the way to the + ** EXCLUSIVE lock. If it were, another process might open the + ** database file, detect the RESERVED lock, and conclude that the + ** database is safe to read while this process is still rolling the + ** hot-journal back. + ** + ** Because the intermediate RESERVED lock is not requested, any + ** other process attempting to access the database file will get to + ** this point in the code and fail to obtain its own EXCLUSIVE lock + ** on the database file. + ** + ** Unless the pager is in locking_mode=exclusive mode, the lock is + ** downgraded to SHARED_LOCK before this function returns. + */ + rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); + if( rc!=SQLITE_OK ){ + goto failed; + } -/* -** Allowed values for RowSet.rsFlags -*/ -#define ROWSET_SORTED 0x01 /* True if RowSet.pEntry is sorted */ -#define ROWSET_NEXT 0x02 /* True if sqlcipher_sqlite3RowSetNext() has been called */ + /* If it is not already open and the file exists on disk, open the + ** journal for read/write access. Write access is required because + ** in exclusive-access mode the file descriptor will be kept open + ** and possibly used for a transaction later on. Also, write-access + ** is usually required to finalize the journal in journal_mode=persist + ** mode (and also for journal_mode=truncate on some systems). + ** + ** If the journal does not exist, it usually means that some + ** other connection managed to get in and roll it back before + ** this connection obtained the exclusive lock above. Or, it + ** may mean that the pager was in the error-state when this + ** function was called and the journal file does not exist. + */ + if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ + sqlcipher_sqlite3_vfs * const pVfs = pPager->pVfs; + int bExists; /* True if journal file exists */ + rc = sqlcipher_sqlite3OsAccess( + pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &bExists); + if( rc==SQLITE_OK && bExists ){ + int fout = 0; + int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL; + assert( !pPager->tempFile ); + rc = sqlcipher_sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout); + assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); + if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){ + rc = SQLITE_CANTOPEN_BKPT; + sqlcipher_sqlite3OsClose(pPager->jfd); + } + } + } -/* -** Allocate a RowSet object. Return NULL if a memory allocation -** error occurs. -*/ -SQLITE_PRIVATE RowSet *sqlcipher_sqlite3RowSetInit(sqlcipher_sqlite3 *db){ - RowSet *p = sqlcipher_sqlite3DbMallocRawNN(db, sizeof(*p)); - if( p ){ - int N = sqlcipher_sqlite3DbMallocSize(db, p); - p->pChunk = 0; - p->db = db; - p->pEntry = 0; - p->pLast = 0; - p->pForest = 0; - p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p); - p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry)); - p->rsFlags = ROWSET_SORTED; - p->iBatch = 0; + /* Playback and delete the journal. Drop the database write + ** lock and reacquire the read lock. Purge the cache before + ** playing back the hot-journal so that we don't end up with + ** an inconsistent cache. Sync the hot journal before playing + ** it back since the process that crashed and left the hot journal + ** probably did not sync it and we are required to always sync + ** the journal before playing it back. + */ + if( isOpen(pPager->jfd) ){ + assert( rc==SQLITE_OK ); + rc = pagerSyncHotJournal(pPager); + if( rc==SQLITE_OK ){ + rc = pager_playback(pPager, !pPager->tempFile); + pPager->eState = PAGER_OPEN; + } + }else if( !pPager->exclusiveMode ){ + pagerUnlockDb(pPager, SHARED_LOCK); + } + + if( rc!=SQLITE_OK ){ + /* This branch is taken if an error occurs while trying to open + ** or roll back a hot-journal while holding an EXCLUSIVE lock. The + ** pager_unlock() routine will be called before returning to unlock + ** the file. If the unlock attempt fails, then Pager.eLock must be + ** set to UNKNOWN_LOCK (see the comment above the #define for + ** UNKNOWN_LOCK above for an explanation). + ** + ** In order to get pager_unlock() to do this, set Pager.eState to + ** PAGER_ERROR now. This is not actually counted as a transition + ** to ERROR state in the state diagram at the top of this file, + ** since we know that the same call to pager_unlock() will very + ** shortly transition the pager object to the OPEN state. Calling + ** assert_pager_state() would fail now, as it should not be possible + ** to be in ERROR state when there are zero outstanding page + ** references. + */ + pager_error(pPager, rc); + goto failed; + } + + assert( pPager->eState==PAGER_OPEN ); + assert( (pPager->eLock==SHARED_LOCK) + || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK) + ); + } + + if( !pPager->tempFile && pPager->hasHeldSharedLock ){ + /* The shared-lock has just been acquired then check to + ** see if the database has been modified. If the database has changed, + ** flush the cache. The hasHeldSharedLock flag prevents this from + ** occurring on the very first access to a file, in order to save a + ** single unnecessary sqlcipher_sqlite3OsRead() call at the start-up. + ** + ** Database changes are detected by looking at 15 bytes beginning + ** at offset 24 into the file. The first 4 of these 16 bytes are + ** a 32-bit counter that is incremented with each change. The + ** other bytes change randomly with each file change when + ** a codec is in use. + ** + ** There is a vanishingly small chance that a change will not be + ** detected. The chance of an undetected change is so small that + ** it can be neglected. + */ + char dbFileVers[sizeof(pPager->dbFileVers)]; + + IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers))); + rc = sqlcipher_sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24); + if( rc!=SQLITE_OK ){ + if( rc!=SQLITE_IOERR_SHORT_READ ){ + goto failed; + } + memset(dbFileVers, 0, sizeof(dbFileVers)); + } + + if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ + pager_reset(pPager); + + /* Unmap the database file. It is possible that external processes + ** may have truncated the database file and then extended it back + ** to its original size while this process was not holding a lock. + ** In this case there may exist a Pager.pMap mapping that appears + ** to be the right size but is not actually valid. Avoid this + ** possibility by unmapping the db here. */ + if( USEFETCH(pPager) ){ + sqlcipher_sqlite3OsUnfetch(pPager->fd, 0, 0); + } + } + } + + /* If there is a WAL file in the file-system, open this database in WAL + ** mode. Otherwise, the following function call is a no-op. + */ + rc = pagerOpenWalIfPresent(pPager); +#ifndef SQLITE_OMIT_WAL + assert( pPager->pWal==0 || rc==SQLITE_OK ); +#endif } - return p; -} -/* -** Deallocate all chunks from a RowSet. This frees all memory that -** the RowSet has allocated over its lifetime. This routine is -** the destructor for the RowSet. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3RowSetClear(void *pArg){ - RowSet *p = (RowSet*)pArg; - struct RowSetChunk *pChunk, *pNextChunk; - for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){ - pNextChunk = pChunk->pNextChunk; - sqlcipher_sqlite3DbFree(p->db, pChunk); + if( pagerUseWal(pPager) ){ + assert( rc==SQLITE_OK ); + rc = pagerBeginReadTransaction(pPager); } - p->pChunk = 0; - p->nFresh = 0; - p->pEntry = 0; - p->pLast = 0; - p->pForest = 0; - p->rsFlags = ROWSET_SORTED; -} -/* -** Deallocate all chunks from a RowSet. This frees all memory that -** the RowSet has allocated over its lifetime. This routine is -** the destructor for the RowSet. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3RowSetDelete(void *pArg){ - sqlcipher_sqlite3RowSetClear(pArg); - sqlcipher_sqlite3DbFree(((RowSet*)pArg)->db, pArg); -} + if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ + rc = pagerPagecount(pPager, &pPager->dbSize); + } -/* -** Allocate a new RowSetEntry object that is associated with the -** given RowSet. Return a pointer to the new and completely uninitialized -** object. -** -** In an OOM situation, the RowSet.db->mallocFailed flag is set and this -** routine returns NULL. -*/ -static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){ - assert( p!=0 ); - if( p->nFresh==0 ){ /*OPTIMIZATION-IF-FALSE*/ - /* We could allocate a fresh RowSetEntry each time one is needed, but it - ** is more efficient to pull a preallocated entry from the pool */ - struct RowSetChunk *pNew; - pNew = sqlcipher_sqlite3DbMallocRawNN(p->db, sizeof(*pNew)); - if( pNew==0 ){ - return 0; - } - pNew->pNextChunk = p->pChunk; - p->pChunk = pNew; - p->pFresh = pNew->aEntry; - p->nFresh = ROWSET_ENTRY_PER_CHUNK; + failed: + if( rc!=SQLITE_OK ){ + assert( !MEMDB ); + pager_unlock(pPager); + assert( pPager->eState==PAGER_OPEN ); + }else{ + pPager->eState = PAGER_READER; + pPager->hasHeldSharedLock = 1; } - p->nFresh--; - return p->pFresh++; + return rc; } /* -** Insert a new value into a RowSet. +** If the reference count has reached zero, rollback any active +** transaction and unlock the pager. ** -** The mallocFailed flag of the database connection is set if a -** memory allocation fails. +** Except, in locking_mode=EXCLUSIVE when there is nothing to in +** the rollback journal, the unlock is not performed and there is +** nothing to rollback, so this routine is a no-op. */ -SQLITE_PRIVATE void sqlcipher_sqlite3RowSetInsert(RowSet *p, i64 rowid){ - struct RowSetEntry *pEntry; /* The new entry */ - struct RowSetEntry *pLast; /* The last prior entry */ - - /* This routine is never called after sqlcipher_sqlite3RowSetNext() */ - assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 ); - - pEntry = rowSetEntryAlloc(p); - if( pEntry==0 ) return; - pEntry->v = rowid; - pEntry->pRight = 0; - pLast = p->pLast; - if( pLast ){ - if( rowid<=pLast->v ){ /*OPTIMIZATION-IF-FALSE*/ - /* Avoid unnecessary sorts by preserving the ROWSET_SORTED flags - ** where possible */ - p->rsFlags &= ~ROWSET_SORTED; - } - pLast->pRight = pEntry; - }else{ - p->pEntry = pEntry; +static void pagerUnlockIfUnused(Pager *pPager){ + if( sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)==0 ){ + assert( pPager->nMmapOut==0 ); /* because page1 is never memory mapped */ + pagerUnlockAndRollback(pPager); } - p->pLast = pEntry; } /* -** Merge two lists of RowSetEntry objects. Remove duplicates. +** The page getter methods each try to acquire a reference to a +** page with page number pgno. If the requested reference is +** successfully obtained, it is copied to *ppPage and SQLITE_OK returned. ** -** The input lists are connected via pRight pointers and are -** assumed to each already be in sorted order. +** There are different implementations of the getter method depending +** on the current state of the pager. +** +** getPageNormal() -- The normal getter +** getPageError() -- Used if the pager is in an error state +** getPageMmap() -- Used if memory-mapped I/O is enabled +** +** If the requested page is already in the cache, it is returned. +** Otherwise, a new page object is allocated and populated with data +** read from the database file. In some cases, the pcache module may +** choose not to allocate a new page object and may reuse an existing +** object with no outstanding references. +** +** The extra data appended to a page is always initialized to zeros the +** first time a page is loaded into memory. If the page requested is +** already in the cache when this function is called, then the extra +** data is left as it was when the page object was last used. +** +** If the database image is smaller than the requested page or if +** the flags parameter contains the PAGER_GET_NOCONTENT bit and the +** requested page is not already stored in the cache, then no +** actual disk read occurs. In this case the memory image of the +** page is initialized to all zeros. +** +** If PAGER_GET_NOCONTENT is true, it means that we do not care about +** the contents of the page. This occurs in two scenarios: +** +** a) When reading a free-list leaf page from the database, and +** +** b) When a savepoint is being rolled back and we need to load +** a new page into the cache to be filled with the data read +** from the savepoint journal. +** +** If PAGER_GET_NOCONTENT is true, then the data returned is zeroed instead +** of being read from the database. Additionally, the bits corresponding +** to pgno in Pager.pInJournal (bitvec of pages already written to the +** journal file) and the PagerSavepoint.pInSavepoint bitvecs of any open +** savepoints are set. This means if the page is made writable at any +** point in the future, using a call to sqlcipher_sqlite3PagerWrite(), its contents +** will not be journaled. This saves IO. +** +** The acquisition might fail for several reasons. In all cases, +** an appropriate error code is returned and *ppPage is set to NULL. +** +** See also sqlcipher_sqlite3PagerLookup(). Both this routine and Lookup() attempt +** to find a page in the in-memory cache first. If the page is not already +** in memory, this routine goes to disk to read it in whereas Lookup() +** just returns 0. This routine acquires a read-lock the first time it +** has to go to disk, and could also playback an old journal if necessary. +** Since Lookup() never goes to disk, it never has to deal with locks +** or journal files. */ -static struct RowSetEntry *rowSetEntryMerge( - struct RowSetEntry *pA, /* First sorted list to be merged */ - struct RowSetEntry *pB /* Second sorted list to be merged */ +static int getPageNormal( + Pager *pPager, /* The pager open on the database file */ + Pgno pgno, /* Page number to fetch */ + DbPage **ppPage, /* Write a pointer to the page here */ + int flags /* PAGER_GET_XXX flags */ ){ - struct RowSetEntry head; - struct RowSetEntry *pTail; + int rc = SQLITE_OK; + PgHdr *pPg; + u8 noContent; /* True if PAGER_GET_NOCONTENT is set */ + sqlcipher_sqlite3_pcache_page *pBase; - pTail = &head; - assert( pA!=0 && pB!=0 ); - for(;;){ - assert( pA->pRight==0 || pA->v<=pA->pRight->v ); - assert( pB->pRight==0 || pB->v<=pB->pRight->v ); - if( pA->v<=pB->v ){ - if( pA->vv ) pTail = pTail->pRight = pA; - pA = pA->pRight; - if( pA==0 ){ - pTail->pRight = pB; - break; + assert( pPager->errCode==SQLITE_OK ); + assert( pPager->eState>=PAGER_READER ); + assert( assert_pager_state(pPager) ); + assert( pPager->hasHeldSharedLock==1 ); + + if( pgno==0 ) return SQLITE_CORRUPT_BKPT; + pBase = sqlcipher_sqlite3PcacheFetch(pPager->pPCache, pgno, 3); + if( pBase==0 ){ + pPg = 0; + rc = sqlcipher_sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase); + if( rc!=SQLITE_OK ) goto pager_acquire_err; + if( pBase==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto pager_acquire_err; + } + } + pPg = *ppPage = sqlcipher_sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pBase); + assert( pPg==(*ppPage) ); + assert( pPg->pgno==pgno ); + assert( pPg->pPager==pPager || pPg->pPager==0 ); + + noContent = (flags & PAGER_GET_NOCONTENT)!=0; + if( pPg->pPager && !noContent ){ + /* In this case the pcache already contains an initialized copy of + ** the page. Return without further ado. */ + assert( pgno!=PAGER_SJ_PGNO(pPager) ); + pPager->aStat[PAGER_STAT_HIT]++; + return SQLITE_OK; + + }else{ + /* The pager cache has created a new page. Its content needs to + ** be initialized. But first some error checks: + ** + ** (*) obsolete. Was: maximum page number is 2^31 + ** (2) Never try to fetch the locking page + */ + if( pgno==PAGER_SJ_PGNO(pPager) ){ + rc = SQLITE_CORRUPT_BKPT; + goto pager_acquire_err; + } + + pPg->pPager = pPager; + + assert( !isOpen(pPager->fd) || !MEMDB ); + if( !isOpen(pPager->fd) || pPager->dbSizepPager->mxPgno ){ + rc = SQLITE_FULL; + goto pager_acquire_err; + } + if( noContent ){ + /* Failure to set the bits in the InJournal bit-vectors is benign. + ** It merely means that we might do some extra work to journal a + ** page that does not need to be journaled. Nevertheless, be sure + ** to test the case where a malloc error occurs while trying to set + ** a bit in a bit vector. + */ + sqlcipher_sqlite3BeginBenignMalloc(); + if( pgno<=pPager->dbOrigSize ){ + TESTONLY( rc = ) sqlcipher_sqlite3BitvecSet(pPager->pInJournal, pgno); + testcase( rc==SQLITE_NOMEM ); + } + TESTONLY( rc = ) addToSavepointBitvecs(pPager, pgno); + testcase( rc==SQLITE_NOMEM ); + sqlcipher_sqlite3EndBenignMalloc(); } + memset(pPg->pData, 0, pPager->pageSize); + IOTRACE(("ZERO %p %d\n", pPager, pgno)); }else{ - pTail = pTail->pRight = pB; - pB = pB->pRight; - if( pB==0 ){ - pTail->pRight = pA; - break; + assert( pPg->pPager==pPager ); + pPager->aStat[PAGER_STAT_MISS]++; + rc = readDbPage(pPg); + if( rc!=SQLITE_OK ){ + goto pager_acquire_err; } } + pager_set_pagehash(pPg); } - return head.pRight; + return SQLITE_OK; + +pager_acquire_err: + assert( rc!=SQLITE_OK ); + if( pPg ){ + sqlcipher_sqlite3PcacheDrop(pPg); + } + pagerUnlockIfUnused(pPager); + *ppPage = 0; + return rc; } -/* -** Sort all elements on the list of RowSetEntry objects into order of -** increasing v. -*/ -static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){ - unsigned int i; - struct RowSetEntry *pNext, *aBucket[40]; +#if SQLITE_MAX_MMAP_SIZE>0 +/* The page getter for when memory-mapped I/O is enabled */ +static int getPageMMap( + Pager *pPager, /* The pager open on the database file */ + Pgno pgno, /* Page number to fetch */ + DbPage **ppPage, /* Write a pointer to the page here */ + int flags /* PAGER_GET_XXX flags */ +){ + int rc = SQLITE_OK; + PgHdr *pPg = 0; + u32 iFrame = 0; /* Frame to read from WAL file */ - memset(aBucket, 0, sizeof(aBucket)); - while( pIn ){ - pNext = pIn->pRight; - pIn->pRight = 0; - for(i=0; aBucket[i]; i++){ - pIn = rowSetEntryMerge(aBucket[i], pIn); - aBucket[i] = 0; + /* It is acceptable to use a read-only (mmap) page for any page except + ** page 1 if there is no write-transaction open or the ACQUIRE_READONLY + ** flag was specified by the caller. And so long as the db is not a + ** temporary or in-memory database. */ + const int bMmapOk = (pgno>1 + && (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY)) + ); + + assert( USEFETCH(pPager) ); +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + assert( pPager->xCodec==0 ); +#endif +/* END SQLCIPHER */ + + /* Optimization note: Adding the "pgno<=1" term before "pgno==0" here + ** allows the compiler optimizer to reuse the results of the "pgno>1" + ** test in the previous statement, and avoid testing pgno==0 in the + ** common case where pgno is large. */ + if( pgno<=1 && pgno==0 ){ + return SQLITE_CORRUPT_BKPT; + } + assert( pPager->eState>=PAGER_READER ); + assert( assert_pager_state(pPager) ); + assert( pPager->hasHeldSharedLock==1 ); + assert( pPager->errCode==SQLITE_OK ); + + if( bMmapOk && pagerUseWal(pPager) ){ + rc = sqlcipher_sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame); + if( rc!=SQLITE_OK ){ + *ppPage = 0; + return rc; } - aBucket[i] = pIn; - pIn = pNext; } - pIn = aBucket[0]; - for(i=1; ifd, + (i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData + ); + if( rc==SQLITE_OK && pData ){ + if( pPager->eState>PAGER_READER || pPager->tempFile ){ + pPg = sqlcipher_sqlite3PagerLookup(pPager, pgno); + } + if( pPg==0 ){ + rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg); + }else{ + sqlcipher_sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData); + } + if( pPg ){ + assert( rc==SQLITE_OK ); + *ppPage = pPg; + return SQLITE_OK; + } + } + if( rc!=SQLITE_OK ){ + *ppPage = 0; + return rc; + } } - return pIn; + return getPageNormal(pPager, pgno, ppPage, flags); } +#endif /* SQLITE_MAX_MMAP_SIZE>0 */ - -/* -** The input, pIn, is a binary tree (or subtree) of RowSetEntry objects. -** Convert this tree into a linked list connected by the pRight pointers -** and return pointers to the first and last elements of the new list. -*/ -static void rowSetTreeToList( - struct RowSetEntry *pIn, /* Root of the input tree */ - struct RowSetEntry **ppFirst, /* Write head of the output list here */ - struct RowSetEntry **ppLast /* Write tail of the output list here */ +/* The page getter method for when the pager is an error state */ +static int getPageError( + Pager *pPager, /* The pager open on the database file */ + Pgno pgno, /* Page number to fetch */ + DbPage **ppPage, /* Write a pointer to the page here */ + int flags /* PAGER_GET_XXX flags */ ){ - assert( pIn!=0 ); - if( pIn->pLeft ){ - struct RowSetEntry *p; - rowSetTreeToList(pIn->pLeft, ppFirst, &p); - p->pRight = pIn; - }else{ - *ppFirst = pIn; - } - if( pIn->pRight ){ - rowSetTreeToList(pIn->pRight, &pIn->pRight, ppLast); - }else{ - *ppLast = pIn; - } - assert( (*ppLast)->pRight==0 ); + UNUSED_PARAMETER(pgno); + UNUSED_PARAMETER(flags); + assert( pPager->errCode!=SQLITE_OK ); + *ppPage = 0; + return pPager->errCode; } -/* -** Convert a sorted list of elements (connected by pRight) into a binary -** tree with depth of iDepth. A depth of 1 means the tree contains a single -** node taken from the head of *ppList. A depth of 2 means a tree with -** three nodes. And so forth. -** -** Use as many entries from the input list as required and update the -** *ppList to point to the unused elements of the list. If the input -** list contains too few elements, then construct an incomplete tree -** and leave *ppList set to NULL. -** -** Return a pointer to the root of the constructed binary tree. +/* Dispatch all page fetch requests to the appropriate getter method. */ -static struct RowSetEntry *rowSetNDeepTree( - struct RowSetEntry **ppList, - int iDepth +SQLITE_PRIVATE int sqlcipher_sqlite3PagerGet( + Pager *pPager, /* The pager open on the database file */ + Pgno pgno, /* Page number to fetch */ + DbPage **ppPage, /* Write a pointer to the page here */ + int flags /* PAGER_GET_XXX flags */ ){ - struct RowSetEntry *p; /* Root of the new tree */ - struct RowSetEntry *pLeft; /* Left subtree */ - if( *ppList==0 ){ /*OPTIMIZATION-IF-TRUE*/ - /* Prevent unnecessary deep recursion when we run out of entries */ - return 0; - } - if( iDepth>1 ){ /*OPTIMIZATION-IF-TRUE*/ - /* This branch causes a *balanced* tree to be generated. A valid tree - ** is still generated without this branch, but the tree is wildly - ** unbalanced and inefficient. */ - pLeft = rowSetNDeepTree(ppList, iDepth-1); - p = *ppList; - if( p==0 ){ /*OPTIMIZATION-IF-FALSE*/ - /* It is safe to always return here, but the resulting tree - ** would be unbalanced */ - return pLeft; - } - p->pLeft = pLeft; - *ppList = p->pRight; - p->pRight = rowSetNDeepTree(ppList, iDepth-1); - }else{ - p = *ppList; - *ppList = p->pRight; - p->pLeft = p->pRight = 0; - } - return p; + /* printf("PAGE %u\n", pgno); fflush(stdout); */ + return pPager->xGet(pPager, pgno, ppPage, flags); } /* -** Convert a sorted list of elements into a binary tree. Make the tree -** as deep as it needs to be in order to contain the entire list. +** Acquire a page if it is already in the in-memory cache. Do +** not read the page from disk. Return a pointer to the page, +** or 0 if the page is not in cache. +** +** See also sqlcipher_sqlite3PagerGet(). The difference between this routine +** and sqlcipher_sqlite3PagerGet() is that _get() will go to the disk and read +** in the page if the page is not already in cache. This routine +** returns NULL if the page is not in cache or if a disk I/O error +** has ever happened. */ -static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){ - int iDepth; /* Depth of the tree so far */ - struct RowSetEntry *p; /* Current tree root */ - struct RowSetEntry *pLeft; /* Left subtree */ - - assert( pList!=0 ); - p = pList; - pList = p->pRight; - p->pLeft = p->pRight = 0; - for(iDepth=1; pList; iDepth++){ - pLeft = p; - p = pList; - pList = p->pRight; - p->pLeft = pLeft; - p->pRight = rowSetNDeepTree(&pList, iDepth); - } - return p; +SQLITE_PRIVATE DbPage *sqlcipher_sqlite3PagerLookup(Pager *pPager, Pgno pgno){ + sqlcipher_sqlite3_pcache_page *pPage; + assert( pPager!=0 ); + assert( pgno!=0 ); + assert( pPager->pPCache!=0 ); + pPage = sqlcipher_sqlite3PcacheFetch(pPager->pPCache, pgno, 0); + assert( pPage==0 || pPager->hasHeldSharedLock ); + if( pPage==0 ) return 0; + return sqlcipher_sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage); } /* -** Extract the smallest element from the RowSet. -** Write the element into *pRowid. Return 1 on success. Return -** 0 if the RowSet is already empty. +** Release a page reference. ** -** After this routine has been called, the sqlcipher_sqlite3RowSetInsert() -** routine may not be called again. +** The sqlcipher_sqlite3PagerUnref() and sqlcipher_sqlite3PagerUnrefNotNull() may only be +** used if we know that the page being released is not the last page. +** The btree layer always holds page1 open until the end, so these first +** to routines can be used to release any page other than BtShared.pPage1. ** -** This routine may not be called after sqlcipher_sqlite3RowSetTest() has -** been used. Older versions of RowSet allowed that, but as the -** capability was not used by the code generator, it was removed -** for code economy. +** Use sqlcipher_sqlite3PagerUnrefPageOne() to release page1. This latter routine +** checks the total number of outstanding pages and if the number of +** pages reaches zero it drops the database lock. */ -SQLITE_PRIVATE int sqlcipher_sqlite3RowSetNext(RowSet *p, i64 *pRowid){ - assert( p!=0 ); - assert( p->pForest==0 ); /* Cannot be used with sqlcipher_sqlite3RowSetText() */ - - /* Merge the forest into a single sorted list on first call */ - if( (p->rsFlags & ROWSET_NEXT)==0 ){ /*OPTIMIZATION-IF-FALSE*/ - if( (p->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/ - p->pEntry = rowSetEntrySort(p->pEntry); - } - p->rsFlags |= ROWSET_SORTED|ROWSET_NEXT; - } - - /* Return the next entry on the list */ - if( p->pEntry ){ - *pRowid = p->pEntry->v; - p->pEntry = p->pEntry->pRight; - if( p->pEntry==0 ){ /*OPTIMIZATION-IF-TRUE*/ - /* Free memory immediately, rather than waiting on sqlcipher_sqlite3_finalize() */ - sqlcipher_sqlite3RowSetClear(p); - } - return 1; +SQLITE_PRIVATE void sqlcipher_sqlite3PagerUnrefNotNull(DbPage *pPg){ + TESTONLY( Pager *pPager = pPg->pPager; ) + assert( pPg!=0 ); + if( pPg->flags & PGHDR_MMAP ){ + assert( pPg->pgno!=1 ); /* Page1 is never memory mapped */ + pagerReleaseMapPage(pPg); }else{ - return 0; + sqlcipher_sqlite3PcacheRelease(pPg); } + /* Do not use this routine to release the last reference to page1 */ + assert( sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)>0 ); +} +SQLITE_PRIVATE void sqlcipher_sqlite3PagerUnref(DbPage *pPg){ + if( pPg ) sqlcipher_sqlite3PagerUnrefNotNull(pPg); +} +SQLITE_PRIVATE void sqlcipher_sqlite3PagerUnrefPageOne(DbPage *pPg){ + Pager *pPager; + assert( pPg!=0 ); + assert( pPg->pgno==1 ); + assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */ + pPager = pPg->pPager; + sqlcipher_sqlite3PcacheRelease(pPg); + pagerUnlockIfUnused(pPager); } /* -** Check to see if element iRowid was inserted into the rowset as -** part of any insert batch prior to iBatch. Return 1 or 0. +** This function is called at the start of every write transaction. +** There must already be a RESERVED or EXCLUSIVE lock on the database +** file when this routine is called. ** -** If this is the first test of a new batch and if there exist entries -** on pRowSet->pEntry, then sort those entries into the forest at -** pRowSet->pForest so that they can be tested. +** Open the journal file for pager pPager and write a journal header +** to the start of it. If there are active savepoints, open the sub-journal +** as well. This function is only used when the journal file is being +** opened to write a rollback log for a transaction. It is not used +** when opening a hot journal file to roll it back. +** +** If the journal file is already open (as it may be in exclusive mode), +** then this function just writes a journal header to the start of the +** already open file. +** +** Whether or not the journal file is opened by this function, the +** Pager.pInJournal bitvec structure is allocated. +** +** Return SQLITE_OK if everything is successful. Otherwise, return +** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or +** an IO error code if opening or writing the journal file fails. */ -SQLITE_PRIVATE int sqlcipher_sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlcipher_sqlite3_int64 iRowid){ - struct RowSetEntry *p, *pTree; +static int pager_open_journal(Pager *pPager){ + int rc = SQLITE_OK; /* Return code */ + sqlcipher_sqlite3_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */ - /* This routine is never called after sqlcipher_sqlite3RowSetNext() */ - assert( pRowSet!=0 && (pRowSet->rsFlags & ROWSET_NEXT)==0 ); + assert( pPager->eState==PAGER_WRITER_LOCKED ); + assert( assert_pager_state(pPager) ); + assert( pPager->pInJournal==0 ); - /* Sort entries into the forest on the first test of a new batch. - ** To save unnecessary work, only do this when the batch number changes. - */ - if( iBatch!=pRowSet->iBatch ){ /*OPTIMIZATION-IF-FALSE*/ - p = pRowSet->pEntry; - if( p ){ - struct RowSetEntry **ppPrevTree = &pRowSet->pForest; - if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/ - /* Only sort the current set of entries if they need it */ - p = rowSetEntrySort(p); - } - for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){ - ppPrevTree = &pTree->pRight; - if( pTree->pLeft==0 ){ - pTree->pLeft = rowSetListToTree(p); - break; + /* If already in the error state, this function is a no-op. But on + ** the other hand, this routine is never called if we are already in + ** an error state. */ + if( NEVER(pPager->errCode) ) return pPager->errCode; + + if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ + pPager->pInJournal = sqlcipher_sqlite3BitvecCreate(pPager->dbSize); + if( pPager->pInJournal==0 ){ + return SQLITE_NOMEM_BKPT; + } + + /* Open the journal file if it is not already open. */ + if( !isOpen(pPager->jfd) ){ + if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ + sqlcipher_sqlite3MemJournalOpen(pPager->jfd); + }else{ + int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; + int nSpill; + + if( pPager->tempFile ){ + flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL); + nSpill = sqlcipher_sqlite3Config.nStmtSpill; }else{ - struct RowSetEntry *pAux, *pTail; - rowSetTreeToList(pTree->pLeft, &pAux, &pTail); - pTree->pLeft = 0; - p = rowSetEntryMerge(pAux, p); + flags |= SQLITE_OPEN_MAIN_JOURNAL; + nSpill = jrnlBufferSize(pPager); } - } - if( pTree==0 ){ - *ppPrevTree = pTree = rowSetEntryAlloc(pRowSet); - if( pTree ){ - pTree->v = 0; - pTree->pRight = 0; - pTree->pLeft = rowSetListToTree(p); + + /* Verify that the database still has the same name as it did when + ** it was originally opened. */ + rc = databaseIsUnmoved(pPager); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3JournalOpen ( + pVfs, pPager->zJournal, pPager->jfd, flags, nSpill + ); } } - pRowSet->pEntry = 0; - pRowSet->pLast = 0; - pRowSet->rsFlags |= ROWSET_SORTED; + assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); } - pRowSet->iBatch = iBatch; - } - /* Test to see if the iRowid value appears anywhere in the forest. - ** Return 1 if it does and 0 if not. - */ - for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){ - p = pTree->pLeft; - while( p ){ - if( p->vpRight; - }else if( p->v>iRowid ){ - p = p->pLeft; - }else{ - return 1; - } + + /* Write the first journal header to the journal file and open + ** the sub-journal if necessary. + */ + if( rc==SQLITE_OK ){ + /* TODO: Check if all of these are really required. */ + pPager->nRec = 0; + pPager->journalOff = 0; + pPager->setSuper = 0; + pPager->journalHdr = 0; + rc = writeJournalHdr(pPager); } } - return 0; + + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3BitvecDestroy(pPager->pInJournal); + pPager->pInJournal = 0; + pPager->journalOff = 0; + }else{ + assert( pPager->eState==PAGER_WRITER_LOCKED ); + pPager->eState = PAGER_WRITER_CACHEMOD; + } + + return rc; } -/************** End of rowset.c **********************************************/ -/************** Begin file pager.c *******************************************/ -/* -** 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 is the implementation of the page cache subsystem or "pager". -** -** The pager is used to access a database disk file. It implements -** atomic commit and rollback through the use of a journal file that -** is separate from the database file. The pager also implements file -** locking to prevent two processes from writing the same database -** file simultaneously, or one process from reading the database while -** another is writing. -*/ -#ifndef SQLITE_OMIT_DISKIO -/* #include "sqliteInt.h" */ -/************** Include wal.h in the middle of pager.c ***********************/ -/************** Begin file wal.h *********************************************/ /* -** 2010 February 1 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** Begin a write-transaction on the specified pager object. If a +** write-transaction has already been opened, this function is a no-op. ** -** 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. +** If the exFlag argument is false, then acquire at least a RESERVED +** lock on the database file. If exFlag is true, then acquire at least +** an EXCLUSIVE lock. If such a lock is already held, no locking +** functions need be called. ** -************************************************************************* -** This header file defines the interface to the write-ahead logging -** system. Refer to the comments below and the header comment attached to -** the implementation of each function in log.c for further details. +** If the subjInMemory argument is non-zero, then any sub-journal opened +** within this transaction will be opened as an in-memory file. This +** has no effect if the sub-journal is already opened (as it may be when +** running in exclusive mode) or if the transaction does not require a +** sub-journal. If the subjInMemory argument is zero, then any required +** sub-journal is implemented in-memory if pPager is an in-memory database, +** or using a temporary file otherwise. */ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ + int rc = SQLITE_OK; -#ifndef SQLITE_WAL_H -#define SQLITE_WAL_H - -/* #include "sqliteInt.h" */ + if( pPager->errCode ) return pPager->errCode; + assert( pPager->eState>=PAGER_READER && pPager->eStatesubjInMemory = (u8)subjInMemory; -/* Macros for extracting appropriate sync flags for either transaction -** commits (WAL_SYNC_FLAGS(X)) or for checkpoint ops (CKPT_SYNC_FLAGS(X)): -*/ -#define WAL_SYNC_FLAGS(X) ((X)&0x03) -#define CKPT_SYNC_FLAGS(X) (((X)>>2)&0x03) + if( pPager->eState==PAGER_READER ){ + assert( pPager->pInJournal==0 ); -#ifdef SQLITE_OMIT_WAL -# define sqlcipher_sqlite3WalOpen(x,y,z) 0 -# define sqlcipher_sqlite3WalLimit(x,y) -# define sqlcipher_sqlite3WalClose(v,w,x,y,z) 0 -# define sqlcipher_sqlite3WalBeginReadTransaction(y,z) 0 -# define sqlcipher_sqlite3WalEndReadTransaction(z) -# define sqlcipher_sqlite3WalDbsize(y) 0 -# define sqlcipher_sqlite3WalBeginWriteTransaction(y) 0 -# define sqlcipher_sqlite3WalEndWriteTransaction(x) 0 -# define sqlcipher_sqlite3WalUndo(x,y,z) 0 -# define sqlcipher_sqlite3WalSavepoint(y,z) -# define sqlcipher_sqlite3WalSavepointUndo(y,z) 0 -# define sqlcipher_sqlite3WalFrames(u,v,w,x,y,z) 0 -# define sqlcipher_sqlite3WalCheckpoint(q,r,s,t,u,v,w,x,y,z) 0 -# define sqlcipher_sqlite3WalCallback(z) 0 -# define sqlcipher_sqlite3WalExclusiveMode(y,z) 0 -# define sqlcipher_sqlite3WalHeapMemory(z) 0 -# define sqlcipher_sqlite3WalFramesize(z) 0 -# define sqlcipher_sqlite3WalFindFrame(x,y,z) 0 -# define sqlcipher_sqlite3WalFile(x) 0 -#else + if( pagerUseWal(pPager) ){ + /* If the pager is configured to use locking_mode=exclusive, and an + ** exclusive lock on the database is not already held, obtain it now. + */ + if( pPager->exclusiveMode && sqlcipher_sqlite3WalExclusiveMode(pPager->pWal, -1) ){ + rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); + if( rc!=SQLITE_OK ){ + return rc; + } + (void)sqlcipher_sqlite3WalExclusiveMode(pPager->pWal, 1); + } -#define WAL_SAVEPOINT_NDATA 4 + /* Grab the write lock on the log file. If successful, upgrade to + ** PAGER_RESERVED state. Otherwise, return an error code to the caller. + ** The busy-handler is not invoked if another connection already + ** holds the write-lock. If possible, the upper layer will call it. + */ + rc = sqlcipher_sqlite3WalBeginWriteTransaction(pPager->pWal); + }else{ + /* Obtain a RESERVED lock on the database file. If the exFlag parameter + ** is true, then immediately upgrade this to an EXCLUSIVE lock. The + ** busy-handler callback can be used when upgrading to the EXCLUSIVE + ** lock, but not when obtaining the RESERVED lock. + */ + rc = pagerLockDb(pPager, RESERVED_LOCK); + if( rc==SQLITE_OK && exFlag ){ + rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); + } + } -/* Connection to a write-ahead log (WAL) file. -** There is one object of this type for each pager. -*/ -typedef struct Wal Wal; + if( rc==SQLITE_OK ){ + /* Change to WRITER_LOCKED state. + ** + ** WAL mode sets Pager.eState to PAGER_WRITER_LOCKED or CACHEMOD + ** when it has an open transaction, but never to DBMOD or FINISHED. + ** This is because in those states the code to roll back savepoint + ** transactions may copy data from the sub-journal into the database + ** file as well as into the page cache. Which would be incorrect in + ** WAL mode. + */ + pPager->eState = PAGER_WRITER_LOCKED; + pPager->dbHintSize = pPager->dbSize; + pPager->dbFileSize = pPager->dbSize; + pPager->dbOrigSize = pPager->dbSize; + pPager->journalOff = 0; + } -/* Open and close a connection to a write-ahead log. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalOpen(sqlcipher_sqlite3_vfs*, sqlcipher_sqlite3_file*, const char *, int, i64, Wal**); -SQLITE_PRIVATE int sqlcipher_sqlite3WalClose(Wal *pWal, sqlcipher_sqlite3*, int sync_flags, int, u8 *); + assert( rc==SQLITE_OK || pPager->eState==PAGER_READER ); + assert( rc!=SQLITE_OK || pPager->eState==PAGER_WRITER_LOCKED ); + assert( assert_pager_state(pPager) ); + } -/* Set the limiting size of a WAL file. */ -SQLITE_PRIVATE void sqlcipher_sqlite3WalLimit(Wal*, i64); + PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager))); + return rc; +} -/* Used by readers to open (lock) and close (unlock) a snapshot. A -** snapshot is like a read-transaction. It is the state of the database -** at an instant in time. sqlcipher_sqlite3WalOpenSnapshot gets a read lock and -** preserves the current state even if the other threads or processes -** write to or checkpoint the WAL. sqlcipher_sqlite3WalCloseSnapshot() closes the -** transaction and releases the lock. +/* +** Write page pPg onto the end of the rollback journal. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalBeginReadTransaction(Wal *pWal, int *); -SQLITE_PRIVATE void sqlcipher_sqlite3WalEndReadTransaction(Wal *pWal); +static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ + Pager *pPager = pPg->pPager; + int rc; + u32 cksum; + char *pData2; + i64 iOff = pPager->journalOff; -/* Read a page from the write-ahead log, if it is present. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalFindFrame(Wal *, Pgno, u32 *); -SQLITE_PRIVATE int sqlcipher_sqlite3WalReadFrame(Wal *, u32, int, u8 *); + /* We should never write to the journal file the page that + ** contains the database locks. The following assert verifies + ** that we do not. */ + assert( pPg->pgno!=PAGER_SJ_PGNO(pPager) ); -/* If the WAL is not empty, return the size of the database. */ -SQLITE_PRIVATE Pgno sqlcipher_sqlite3WalDbsize(Wal *pWal); + assert( pPager->journalHdr<=pPager->journalOff ); + CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); + cksum = pager_cksum(pPager, (u8*)pData2); -/* Obtain or release the WRITER lock. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalBeginWriteTransaction(Wal *pWal); -SQLITE_PRIVATE int sqlcipher_sqlite3WalEndWriteTransaction(Wal *pWal); + /* Even if an IO or diskfull error occurs while journalling the + ** page in the block above, set the need-sync flag for the page. + ** Otherwise, when the transaction is rolled back, the logic in + ** playback_one_page() will think that the page needs to be restored + ** in the database file. And if an IO error occurs while doing so, + ** then corruption may follow. + */ + pPg->flags |= PGHDR_NEED_SYNC; -/* Undo any frames written (but not committed) to the log */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx); + rc = write32bits(pPager->jfd, iOff, pPg->pgno); + if( rc!=SQLITE_OK ) return rc; + rc = sqlcipher_sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4); + if( rc!=SQLITE_OK ) return rc; + rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum); + if( rc!=SQLITE_OK ) return rc; -/* Return an integer that records the current (uncommitted) write -** position in the WAL */ -SQLITE_PRIVATE void sqlcipher_sqlite3WalSavepoint(Wal *pWal, u32 *aWalData); + IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno, + pPager->journalOff, pPager->pageSize)); + PAGER_INCR(sqlcipher_sqlite3_pager_writej_count); + PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n", + PAGERID(pPager), pPg->pgno, + ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg))); -/* Move the write position of the WAL back to iFrame. Called in -** response to a ROLLBACK TO command. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData); + pPager->journalOff += 8 + pPager->pageSize; + pPager->nRec++; + assert( pPager->pInJournal!=0 ); + rc = sqlcipher_sqlite3BitvecSet(pPager->pInJournal, pPg->pgno); + testcase( rc==SQLITE_NOMEM ); + assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); + rc |= addToSavepointBitvecs(pPager, pPg->pgno); + assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); + return rc; +} -/* Write a frame or frames to the log. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int); +/* +** Mark a single data page as writeable. The page is written into the +** main journal or sub-journal as required. If the page is written into +** one of the journals, the corresponding bit is set in the +** Pager.pInJournal bitvec and the PagerSavepoint.pInSavepoint bitvecs +** of any open savepoints as appropriate. +*/ +static int pager_write(PgHdr *pPg){ + Pager *pPager = pPg->pPager; + int rc = SQLITE_OK; -/* Copy pages from the log to the database file */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalCheckpoint( - Wal *pWal, /* Write-ahead log connection */ - sqlcipher_sqlite3 *db, /* Check this handle's interrupt flag */ - int eMode, /* One of PASSIVE, FULL and RESTART */ - int (*xBusy)(void*), /* Function to call when busy */ - void *pBusyArg, /* Context argument for xBusyHandler */ - int sync_flags, /* Flags to sync db file with (or 0) */ - int nBuf, /* Size of buffer nBuf */ - u8 *zBuf, /* Temporary buffer to use */ - int *pnLog, /* OUT: Number of frames in WAL */ - int *pnCkpt /* OUT: Number of backfilled frames in WAL */ -); + /* This routine is not called unless a write-transaction has already + ** been started. The journal file may or may not be open at this point. + ** It is never called in the ERROR state. + */ + assert( pPager->eState==PAGER_WRITER_LOCKED + || pPager->eState==PAGER_WRITER_CACHEMOD + || pPager->eState==PAGER_WRITER_DBMOD + ); + assert( assert_pager_state(pPager) ); + assert( pPager->errCode==0 ); + assert( pPager->readOnly==0 ); + CHECK_PAGE(pPg); -/* Return the value to pass to a sqlcipher_sqlite3_wal_hook callback, the -** number of frames in the WAL at the point of the last commit since -** sqlcipher_sqlite3WalCallback() was called. If no commits have occurred since -** the last call, then return 0. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3WalCallback(Wal *pWal); + /* The journal file needs to be opened. Higher level routines have already + ** obtained the necessary locks to begin the write-transaction, but the + ** rollback journal might not yet be open. Open it now if this is the case. + ** + ** This is done before calling sqlcipher_sqlite3PcacheMakeDirty() on the page. + ** Otherwise, if it were done after calling sqlcipher_sqlite3PcacheMakeDirty(), then + ** an error might occur and the pager would end up in WRITER_LOCKED state + ** with pages marked as dirty in the cache. + */ + if( pPager->eState==PAGER_WRITER_LOCKED ){ + rc = pager_open_journal(pPager); + if( rc!=SQLITE_OK ) return rc; + } + assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); + assert( assert_pager_state(pPager) ); -/* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released) -** by the pager layer on the database file. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3WalExclusiveMode(Wal *pWal, int op); + /* Mark the page that is about to be modified as dirty. */ + sqlcipher_sqlite3PcacheMakeDirty(pPg); -/* Return true if the argument is non-NULL and the WAL module is using -** heap-memory for the wal-index. Otherwise, if the argument is NULL or the -** WAL module is using shared-memory, return false. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3WalHeapMemory(Wal *pWal); + /* If a rollback journal is in use, them make sure the page that is about + ** to change is in the rollback journal, or if the page is a new page off + ** then end of the file, make sure it is marked as PGHDR_NEED_SYNC. + */ + assert( (pPager->pInJournal!=0) == isOpen(pPager->jfd) ); + if( pPager->pInJournal!=0 + && sqlcipher_sqlite3BitvecTestNotNull(pPager->pInJournal, pPg->pgno)==0 + ){ + assert( pagerUseWal(pPager)==0 ); + if( pPg->pgno<=pPager->dbOrigSize ){ + rc = pagerAddPageToRollbackJournal(pPg); + if( rc!=SQLITE_OK ){ + return rc; + } + }else{ + if( pPager->eState!=PAGER_WRITER_DBMOD ){ + pPg->flags |= PGHDR_NEED_SYNC; + } + PAGERTRACE(("APPEND %d page %d needSync=%d\n", + PAGERID(pPager), pPg->pgno, + ((pPg->flags&PGHDR_NEED_SYNC)?1:0))); + } + } -#ifdef SQLITE_ENABLE_SNAPSHOT -SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotGet(Wal *pWal, sqlcipher_sqlite3_snapshot **ppSnapshot); -SQLITE_PRIVATE void sqlcipher_sqlite3WalSnapshotOpen(Wal *pWal, sqlcipher_sqlite3_snapshot *pSnapshot); -SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotRecover(Wal *pWal); -SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotCheck(Wal *pWal, sqlcipher_sqlite3_snapshot *pSnapshot); -SQLITE_PRIVATE void sqlcipher_sqlite3WalSnapshotUnlock(Wal *pWal); -#endif + /* The PGHDR_DIRTY bit is set above when the page was added to the dirty-list + ** and before writing the page into the rollback journal. Wait until now, + ** after the page has been successfully journalled, before setting the + ** PGHDR_WRITEABLE bit that indicates that the page can be safely modified. + */ + pPg->flags |= PGHDR_WRITEABLE; -#ifdef SQLITE_ENABLE_ZIPVFS -/* If the WAL file is not empty, return the number of bytes of content -** stored in each frame (i.e. the db page-size when the WAL was created). + /* If the statement journal is open and the page is not in it, + ** then write the page into the statement journal. + */ + if( pPager->nSavepoint>0 ){ + rc = subjournalPageIfRequired(pPg); + } + + /* Update the database size and return. */ + if( pPager->dbSizepgno ){ + pPager->dbSize = pPg->pgno; + } + return rc; +} + +/* +** This is a variant of sqlcipher_sqlite3PagerWrite() that runs when the sector size +** is larger than the page size. SQLite makes the (reasonable) assumption that +** all bytes of a sector are written together by hardware. Hence, all bytes of +** a sector need to be journalled in case of a power loss in the middle of +** a write. +** +** Usually, the sector size is less than or equal to the page size, in which +** case pages can be individually written. This routine only runs in the +** exceptional case where the page size is smaller than the sector size. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalFramesize(Wal *pWal); -#endif +static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){ + int rc = SQLITE_OK; /* Return code */ + Pgno nPageCount; /* Total number of pages in database file */ + Pgno pg1; /* First page of the sector pPg is located on. */ + int nPage = 0; /* Number of pages starting at pg1 to journal */ + int ii; /* Loop counter */ + int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */ + Pager *pPager = pPg->pPager; /* The pager that owns pPg */ + Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); -/* Return the sqlcipher_sqlite3_file object for the WAL file */ -SQLITE_PRIVATE sqlcipher_sqlite3_file *sqlcipher_sqlite3WalFile(Wal *pWal); + /* Set the doNotSpill NOSYNC bit to 1. This is because we cannot allow + ** a journal header to be written between the pages journaled by + ** this function. + */ + assert( !MEMDB ); + assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)==0 ); + pPager->doNotSpill |= SPILLFLAG_NOSYNC; -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -SQLITE_PRIVATE int sqlcipher_sqlite3WalWriteLock(Wal *pWal, int bLock); -SQLITE_PRIVATE void sqlcipher_sqlite3WalDb(Wal *pWal, sqlcipher_sqlite3 *db); -#endif + /* This trick assumes that both the page-size and sector-size are + ** an integer power of 2. It sets variable pg1 to the identifier + ** of the first page of the sector pPg is located on. + */ + pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1; -#endif /* ifndef SQLITE_OMIT_WAL */ -#endif /* SQLITE_WAL_H */ + nPageCount = pPager->dbSize; + if( pPg->pgno>nPageCount ){ + nPage = (pPg->pgno - pg1)+1; + }else if( (pg1+nPagePerSector-1)>nPageCount ){ + nPage = nPageCount+1-pg1; + }else{ + nPage = nPagePerSector; + } + assert(nPage>0); + assert(pg1<=pPg->pgno); + assert((pg1+nPage)>pPg->pgno); -/************** End of wal.h *************************************************/ -/************** Continuing where we left off in pager.c **********************/ + for(ii=0; iipgno || !sqlcipher_sqlite3BitvecTest(pPager->pInJournal, pg) ){ + if( pg!=PAGER_SJ_PGNO(pPager) ){ + rc = sqlcipher_sqlite3PagerGet(pPager, pg, &pPage, 0); + if( rc==SQLITE_OK ){ + rc = pager_write(pPage); + if( pPage->flags&PGHDR_NEED_SYNC ){ + needSync = 1; + } + sqlcipher_sqlite3PagerUnrefNotNull(pPage); + } + } + }else if( (pPage = sqlcipher_sqlite3PagerLookup(pPager, pg))!=0 ){ + if( pPage->flags&PGHDR_NEED_SYNC ){ + needSync = 1; + } + sqlcipher_sqlite3PagerUnrefNotNull(pPage); + } + } + + /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages + ** starting at pg1, then it needs to be set for all of them. Because + ** writing to any of these nPage pages may damage the others, the + ** journal file must contain sync()ed copies of all of them + ** before any of them can be written out to the database file. + */ + if( rc==SQLITE_OK && needSync ){ + assert( !MEMDB ); + for(ii=0; iiflags |= PGHDR_NEED_SYNC; + sqlcipher_sqlite3PagerUnrefNotNull(pPage); + } + } + } + assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)!=0 ); + pPager->doNotSpill &= ~SPILLFLAG_NOSYNC; + return rc; +} -/******************* NOTES ON THE DESIGN OF THE PAGER ************************ -** -** This comment block describes invariants that hold when using a rollback -** journal. These invariants do not apply for journal_mode=WAL, -** journal_mode=MEMORY, or journal_mode=OFF. -** -** Within this comment block, a page is deemed to have been synced -** automatically as soon as it is written when PRAGMA synchronous=OFF. -** Otherwise, the page is not synced until the xSync method of the VFS -** is called successfully on the file containing the page. -** -** Definition: A page of the database file is said to be "overwriteable" if -** one or more of the following are true about the page: -** -** (a) The original content of the page as it was at the beginning of -** the transaction has been written into the rollback journal and -** synced. -** -** (b) The page was a freelist leaf page at the start of the transaction. -** -** (c) The page number is greater than the largest page that existed in -** the database file at the start of the transaction. -** -** (1) A page of the database file is never overwritten unless one of the -** following are true: -** -** (a) The page and all other pages on the same sector are overwriteable. -** -** (b) The atomic page write optimization is enabled, and the entire -** transaction other than the update of the transaction sequence -** number consists of a single page change. -** -** (2) The content of a page written into the rollback journal exactly matches -** both the content in the database when the rollback journal was written -** and the content in the database at the beginning of the current -** transaction. -** -** (3) Writes to the database file are an integer multiple of the page size -** in length and are aligned on a page boundary. -** -** (4) Reads from the database file are either aligned on a page boundary and -** an integer multiple of the page size in length or are taken from the -** first 100 bytes of the database file. -** -** (5) All writes to the database file are synced prior to the rollback journal -** being deleted, truncated, or zeroed. -** -** (6) If a super-journal file is used, then all writes to the database file -** are synced prior to the super-journal being deleted. -** -** Definition: Two databases (or the same database at two points it time) -** are said to be "logically equivalent" if they give the same answer to -** all queries. Note in particular the content of freelist leaf -** pages can be changed arbitrarily without affecting the logical equivalence -** of the database. -** -** (7) At any time, if any subset, including the empty set and the total set, -** of the unsynced changes to a rollback journal are removed and the -** journal is rolled back, the resulting database file will be logically -** equivalent to the database file at the beginning of the transaction. -** -** (8) When a transaction is rolled back, the xTruncate method of the VFS -** is called to restore the database file to the same size it was at -** the beginning of the transaction. (In some VFSes, the xTruncate -** method is a no-op, but that does not change the fact the SQLite will -** invoke it.) -** -** (9) Whenever the database file is modified, at least one bit in the range -** of bytes from 24 through 39 inclusive will be changed prior to releasing -** the EXCLUSIVE lock, thus signaling other connections on the same -** database to flush their caches. -** -** (10) The pattern of bits in bytes 24 through 39 shall not repeat in less -** than one billion transactions. -** -** (11) A database file is well-formed at the beginning and at the conclusion -** of every transaction. -** -** (12) An EXCLUSIVE lock is held on the database file when writing to -** the database file. +/* +** Mark a data page as writeable. This routine must be called before +** making changes to a page. The caller must check the return value +** of this function and be careful not to change any page data unless +** this routine returns SQLITE_OK. ** -** (13) A SHARED lock is held on the database file while reading any -** content out of the database file. +** The difference between this function and pager_write() is that this +** function also deals with the special case where 2 or more pages +** fit on a single disk sector. In this case all co-resident pages +** must have been written to the journal file before returning. ** -******************************************************************************/ - -/* -** Macros for troubleshooting. Normally turned off +** If an error occurs, SQLITE_NOMEM or an IO error code is returned +** as appropriate. Otherwise, SQLITE_OK. */ -#if 0 -int sqlcipher_sqlite3PagerTrace=1; /* True to enable tracing */ -#define sqlcipher_sqlite3DebugPrintf printf -#define PAGERTRACE(X) if( sqlcipher_sqlite3PagerTrace ){ sqlcipher_sqlite3DebugPrintf X; } -#else -#define PAGERTRACE(X) -#endif +SQLITE_PRIVATE int sqlcipher_sqlite3PagerWrite(PgHdr *pPg){ + Pager *pPager = pPg->pPager; + assert( (pPg->flags & PGHDR_MMAP)==0 ); + assert( pPager->eState>=PAGER_WRITER_LOCKED ); + assert( assert_pager_state(pPager) ); + if( (pPg->flags & PGHDR_WRITEABLE)!=0 && pPager->dbSize>=pPg->pgno ){ + if( pPager->nSavepoint ) return subjournalPageIfRequired(pPg); + return SQLITE_OK; + }else if( pPager->errCode ){ + return pPager->errCode; + }else if( pPager->sectorSize > (u32)pPager->pageSize ){ + assert( pPager->tempFile==0 ); + return pagerWriteLargeSector(pPg); + }else{ + return pager_write(pPg); + } +} /* -** The following two macros are used within the PAGERTRACE() macros above -** to print out file-descriptors. -** -** PAGERID() takes a pointer to a Pager struct as its argument. The -** associated file-descriptor is returned. FILEHANDLEID() takes an sqlcipher_sqlite3_file -** struct as its argument. +** Return TRUE if the page given in the argument was previously passed +** to sqlcipher_sqlite3PagerWrite(). In other words, return TRUE if it is ok +** to change the content of the page. */ -#define PAGERID(p) (SQLITE_PTR_TO_INT(p->fd)) -#define FILEHANDLEID(fd) (SQLITE_PTR_TO_INT(fd)) +#ifndef NDEBUG +SQLITE_PRIVATE int sqlcipher_sqlite3PagerIswriteable(DbPage *pPg){ + return pPg->flags & PGHDR_WRITEABLE; +} +#endif /* -** The Pager.eState variable stores the current 'state' of a pager. A -** pager may be in any one of the seven states shown in the following -** state diagram. -** -** OPEN <------+------+ -** | | | -** V | | -** +---------> READER-------+ | -** | | | -** | V | -** |<-------WRITER_LOCKED------> ERROR -** | | ^ -** | V | -** |<------WRITER_CACHEMOD-------->| -** | | | -** | V | -** |<-------WRITER_DBMOD---------->| -** | | | -** | V | -** +<------WRITER_FINISHED-------->+ -** -** -** List of state transitions and the C [function] that performs each: -** -** OPEN -> READER [sqlcipher_sqlite3PagerSharedLock] -** READER -> OPEN [pager_unlock] -** -** READER -> WRITER_LOCKED [sqlcipher_sqlite3PagerBegin] -** WRITER_LOCKED -> WRITER_CACHEMOD [pager_open_journal] -** WRITER_CACHEMOD -> WRITER_DBMOD [syncJournal] -** WRITER_DBMOD -> WRITER_FINISHED [sqlcipher_sqlite3PagerCommitPhaseOne] -** WRITER_*** -> READER [pager_end_transaction] -** -** WRITER_*** -> ERROR [pager_error] -** ERROR -> OPEN [pager_unlock] -** -** -** OPEN: -** -** The pager starts up in this state. Nothing is guaranteed in this -** state - the file may or may not be locked and the database size is -** unknown. The database may not be read or written. -** -** * No read or write transaction is active. -** * Any lock, or no lock at all, may be held on the database file. -** * The dbSize, dbOrigSize and dbFileSize variables may not be trusted. -** -** READER: -** -** In this state all the requirements for reading the database in -** rollback (non-WAL) mode are met. Unless the pager is (or recently -** was) in exclusive-locking mode, a user-level read transaction is -** open. The database size is known in this state. -** -** A connection running with locking_mode=normal enters this state when -** it opens a read-transaction on the database and returns to state -** OPEN after the read-transaction is completed. However a connection -** running in locking_mode=exclusive (including temp databases) remains in -** this state even after the read-transaction is closed. The only way -** a locking_mode=exclusive connection can transition from READER to OPEN -** is via the ERROR state (see below). -** -** * A read transaction may be active (but a write-transaction cannot). -** * A SHARED or greater lock is held on the database file. -** * The dbSize variable may be trusted (even if a user-level read -** transaction is not active). The dbOrigSize and dbFileSize variables -** may not be trusted at this point. -** * If the database is a WAL database, then the WAL connection is open. -** * Even if a read-transaction is not open, it is guaranteed that -** there is no hot-journal in the file-system. -** -** WRITER_LOCKED: -** -** The pager moves to this state from READER when a write-transaction -** is first opened on the database. In WRITER_LOCKED state, all locks -** required to start a write-transaction are held, but no actual -** modifications to the cache or database have taken place. -** -** In rollback mode, a RESERVED or (if the transaction was opened with -** BEGIN EXCLUSIVE) EXCLUSIVE lock is obtained on the database file when -** moving to this state, but the journal file is not written to or opened -** to in this state. If the transaction is committed or rolled back while -** in WRITER_LOCKED state, all that is required is to unlock the database -** file. -** -** IN WAL mode, WalBeginWriteTransaction() is called to lock the log file. -** If the connection is running with locking_mode=exclusive, an attempt -** is made to obtain an EXCLUSIVE lock on the database file. -** -** * A write transaction is active. -** * If the connection is open in rollback-mode, a RESERVED or greater -** lock is held on the database file. -** * If the connection is open in WAL-mode, a WAL write transaction -** is open (i.e. sqlcipher_sqlite3WalBeginWriteTransaction() has been successfully -** called). -** * The dbSize, dbOrigSize and dbFileSize variables are all valid. -** * The contents of the pager cache have not been modified. -** * The journal file may or may not be open. -** * Nothing (not even the first header) has been written to the journal. -** -** WRITER_CACHEMOD: -** -** A pager moves from WRITER_LOCKED state to this state when a page is -** first modified by the upper layer. In rollback mode the journal file -** is opened (if it is not already open) and a header written to the -** start of it. The database file on disk has not been modified. -** -** * A write transaction is active. -** * A RESERVED or greater lock is held on the database file. -** * The journal file is open and the first header has been written -** to it, but the header has not been synced to disk. -** * The contents of the page cache have been modified. -** -** WRITER_DBMOD: -** -** The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state -** when it modifies the contents of the database file. WAL connections -** never enter this state (since they do not modify the database file, -** just the log file). -** -** * A write transaction is active. -** * An EXCLUSIVE or greater lock is held on the database file. -** * The journal file is open and the first header has been written -** and synced to disk. -** * The contents of the page cache have been modified (and possibly -** written to disk). -** -** WRITER_FINISHED: -** -** It is not possible for a WAL connection to enter this state. -** -** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD -** state after the entire transaction has been successfully written into the -** database file. In this state the transaction may be committed simply -** by finalizing the journal file. Once in WRITER_FINISHED state, it is -** not possible to modify the database further. At this point, the upper -** layer must either commit or rollback the transaction. -** -** * A write transaction is active. -** * An EXCLUSIVE or greater lock is held on the database file. -** * All writing and syncing of journal and database data has finished. -** If no error occurred, all that remains is to finalize the journal to -** commit the transaction. If an error did occur, the caller will need -** to rollback the transaction. -** -** ERROR: -** -** The ERROR state is entered when an IO or disk-full error (including -** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it -** difficult to be sure that the in-memory pager state (cache contents, -** db size etc.) are consistent with the contents of the file-system. -** -** Temporary pager files may enter the ERROR state, but in-memory pagers -** cannot. -** -** For example, if an IO error occurs while performing a rollback, -** the contents of the page-cache may be left in an inconsistent state. -** At this point it would be dangerous to change back to READER state -** (as usually happens after a rollback). Any subsequent readers might -** report database corruption (due to the inconsistent cache), and if -** they upgrade to writers, they may inadvertently corrupt the database -** file. To avoid this hazard, the pager switches into the ERROR state -** instead of READER following such an error. -** -** Once it has entered the ERROR state, any attempt to use the pager -** to read or write data returns an error. Eventually, once all -** outstanding transactions have been abandoned, the pager is able to -** transition back to OPEN state, discarding the contents of the -** page-cache and any other in-memory state at the same time. Everything -** is reloaded from disk (and, if necessary, hot-journal rollback peformed) -** when a read-transaction is next opened on the pager (transitioning -** the pager into READER state). At that point the system has recovered -** from the error. -** -** Specifically, the pager jumps into the ERROR state if: -** -** 1. An error occurs while attempting a rollback. This happens in -** function sqlcipher_sqlite3PagerRollback(). -** -** 2. An error occurs while attempting to finalize a journal file -** following a commit in function sqlcipher_sqlite3PagerCommitPhaseTwo(). -** -** 3. An error occurs while attempting to write to the journal or -** database file in function pagerStress() in order to free up -** memory. -** -** In other cases, the error is returned to the b-tree layer. The b-tree -** layer then attempts a rollback operation. If the error condition -** persists, the pager enters the ERROR state via condition (1) above. -** -** Condition (3) is necessary because it can be triggered by a read-only -** statement executed within a transaction. In this case, if the error -** code were simply returned to the user, the b-tree layer would not -** automatically attempt a rollback, as it assumes that an error in a -** read-only statement cannot leave the pager in an internally inconsistent -** state. -** -** * The Pager.errCode variable is set to something other than SQLITE_OK. -** * There are one or more outstanding references to pages (after the -** last reference is dropped the pager should move back to OPEN state). -** * The pager is not an in-memory pager. -** -** -** Notes: +** A call to this routine tells the pager that it is not necessary to +** write the information on page pPg back to the disk, even though +** that page might be marked as dirty. This happens, for example, when +** the page has been added as a leaf of the freelist and so its +** content no longer matters. ** -** * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the -** connection is open in WAL mode. A WAL connection is always in one -** of the first four states. +** The overlying software layer calls this routine when all of the data +** on the given page is unused. The pager marks the page as clean so +** that it does not get written to disk. ** -** * Normally, a connection open in exclusive mode is never in PAGER_OPEN -** state. There are two exceptions: immediately after exclusive-mode has -** been turned on (and before any read or write transactions are -** executed), and when the pager is leaving the "error state". +** Tests show that this optimization can quadruple the speed of large +** DELETE operations. ** -** * See also: assert_pager_state(). +** This optimization cannot be used with a temp-file, as the page may +** have been dirty at the start of the transaction. In that case, if +** memory pressure forces page pPg out of the cache, the data does need +** to be written out to disk so that it may be read back in if the +** current transaction is rolled back. */ -#define PAGER_OPEN 0 -#define PAGER_READER 1 -#define PAGER_WRITER_LOCKED 2 -#define PAGER_WRITER_CACHEMOD 3 -#define PAGER_WRITER_DBMOD 4 -#define PAGER_WRITER_FINISHED 5 -#define PAGER_ERROR 6 +SQLITE_PRIVATE void sqlcipher_sqlite3PagerDontWrite(PgHdr *pPg){ + Pager *pPager = pPg->pPager; + if( !pPager->tempFile && (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ + PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager))); + IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno)) + pPg->flags |= PGHDR_DONT_WRITE; + pPg->flags &= ~PGHDR_WRITEABLE; + testcase( pPg->flags & PGHDR_NEED_SYNC ); + pager_set_pagehash(pPg); + } +} /* -** The Pager.eLock variable is almost always set to one of the -** following locking-states, according to the lock currently held on -** the database file: NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK. -** This variable is kept up to date as locks are taken and released by -** the pagerLockDb() and pagerUnlockDb() wrappers. -** -** If the VFS xLock() or xUnlock() returns an error other than SQLITE_BUSY -** (i.e. one of the SQLITE_IOERR subtypes), it is not clear whether or not -** the operation was successful. In these circumstances pagerLockDb() and -** pagerUnlockDb() take a conservative approach - eLock is always updated -** when unlocking the file, and only updated when locking the file if the -** VFS call is successful. This way, the Pager.eLock variable may be set -** to a less exclusive (lower) value than the lock that is actually held -** at the system level, but it is never set to a more exclusive value. -** -** This is usually safe. If an xUnlock fails or appears to fail, there may -** be a few redundant xLock() calls or a lock may be held for longer than -** required, but nothing really goes wrong. -** -** The exception is when the database file is unlocked as the pager moves -** from ERROR to OPEN state. At this point there may be a hot-journal file -** in the file-system that needs to be rolled back (as part of an OPEN->SHARED -** transition, by the same pager or any other). If the call to xUnlock() -** fails at this point and the pager is left holding an EXCLUSIVE lock, this -** can confuse the call to xCheckReservedLock() call made later as part -** of hot-journal detection. +** This routine is called to increment the value of the database file +** change-counter, stored as a 4-byte big-endian integer starting at +** byte offset 24 of the pager file. The secondary change counter at +** 92 is also updated, as is the SQLite version number at offset 96. ** -** xCheckReservedLock() is defined as returning true "if there is a RESERVED -** lock held by this process or any others". So xCheckReservedLock may -** return true because the caller itself is holding an EXCLUSIVE lock (but -** doesn't know it because of a previous error in xUnlock). If this happens -** a hot-journal may be mistaken for a journal being created by an active -** transaction in another process, causing SQLite to read from the database -** without rolling it back. +** But this only happens if the pPager->changeCountDone flag is false. +** To avoid excess churning of page 1, the update only happens once. +** See also the pager_write_changecounter() routine that does an +** unconditional update of the change counters. ** -** To work around this, if a call to xUnlock() fails when unlocking the -** database in the ERROR state, Pager.eLock is set to UNKNOWN_LOCK. It -** is only changed back to a real locking state after a successful call -** to xLock(EXCLUSIVE). Also, the code to do the OPEN->SHARED state transition -** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK -** lock. Instead, it assumes a hot-journal exists and obtains an EXCLUSIVE -** lock on the database file before attempting to roll it back. See function -** PagerSharedLock() for more detail. +** If the isDirectMode flag is zero, then this is done by calling +** sqlcipher_sqlite3PagerWrite() on page 1, then modifying the contents of the +** page data. In this case the file will be updated when the current +** transaction is committed. ** -** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in -** PAGER_OPEN state. +** The isDirectMode flag may only be non-zero if the library was compiled +** with the SQLITE_ENABLE_ATOMIC_WRITE macro defined. In this case, +** if isDirect is non-zero, then the database file is updated directly +** by writing an updated version of page 1 using a call to the +** sqlcipher_sqlite3OsWrite() function. */ -#define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1) +static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ + int rc = SQLITE_OK; -/* -** A macro used for invoking the codec if there is one -*/ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC -# define CODEC1(P,D,N,X,E) \ - if( P->xCodec && P->xCodec(P->pCodec,D,N,X)==0 ){ E; } -# define CODEC2(P,D,N,X,E,O) \ - if( P->xCodec==0 ){ O=(char*)D; }else \ - if( (O=(char*)(P->xCodec(P->pCodec,D,N,X)))==0 ){ E; } + assert( pPager->eState==PAGER_WRITER_CACHEMOD + || pPager->eState==PAGER_WRITER_DBMOD + ); + assert( assert_pager_state(pPager) ); + + /* Declare and initialize constant integer 'isDirect'. If the + ** atomic-write optimization is enabled in this build, then isDirect + ** is initialized to the value passed as the isDirectMode parameter + ** to this function. Otherwise, it is always set to zero. + ** + ** The idea is that if the atomic-write optimization is not + ** enabled at compile time, the compiler can omit the tests of + ** 'isDirect' below, as well as the block enclosed in the + ** "if( isDirect )" condition. + */ +#ifndef SQLITE_ENABLE_ATOMIC_WRITE +# define DIRECT_MODE 0 + assert( isDirectMode==0 ); + UNUSED_PARAMETER(isDirectMode); #else -# define CODEC1(P,D,N,X,E) /* NO-OP */ -# define CODEC2(P,D,N,X,E,O) O=(char*)D +# define DIRECT_MODE isDirectMode #endif -/* END SQLCIPHER */ -/* -** The maximum allowed sector size. 64KiB. If the xSectorsize() method -** returns a value larger than this, then MAX_SECTOR_SIZE is used instead. -** This could conceivably cause corruption following a power failure on -** such a system. This is currently an undocumented limit. -*/ -#define MAX_SECTOR_SIZE 0x10000 + if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){ + PgHdr *pPgHdr; /* Reference to page 1 */ + + assert( !pPager->tempFile && isOpen(pPager->fd) ); + + /* Open page 1 of the file for writing. */ + rc = sqlcipher_sqlite3PagerGet(pPager, 1, &pPgHdr, 0); + assert( pPgHdr==0 || rc==SQLITE_OK ); + /* If page one was fetched successfully, and this function is not + ** operating in direct-mode, make page 1 writable. When not in + ** direct mode, page 1 is always held in cache and hence the PagerGet() + ** above is always successful - hence the ALWAYS on rc==SQLITE_OK. + */ + if( !DIRECT_MODE && ALWAYS(rc==SQLITE_OK) ){ + rc = sqlcipher_sqlite3PagerWrite(pPgHdr); + } + + if( rc==SQLITE_OK ){ + /* Actually do the update of the change counter */ + pager_write_changecounter(pPgHdr); + + /* If running in direct mode, write the contents of page 1 to the file. */ + if( DIRECT_MODE ){ + const void *zBuf; + assert( pPager->dbFileSize>0 ); + CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM_BKPT, zBuf); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); + pPager->aStat[PAGER_STAT_WRITE]++; + } + if( rc==SQLITE_OK ){ + /* Update the pager's copy of the change-counter. Otherwise, the + ** next time a read transaction is opened the cache will be + ** flushed (as the change-counter values will not match). */ + const void *pCopy = (const void *)&((const char *)zBuf)[24]; + memcpy(&pPager->dbFileVers, pCopy, sizeof(pPager->dbFileVers)); + pPager->changeCountDone = 1; + } + }else{ + pPager->changeCountDone = 1; + } + } + + /* Release the page reference. */ + sqlcipher_sqlite3PagerUnref(pPgHdr); + } + return rc; +} /* -** An instance of the following structure is allocated for each active -** savepoint and statement transaction in the system. All such structures -** are stored in the Pager.aSavepoint[] array, which is allocated and -** resized using sqlcipher_sqlite3Realloc(). +** Sync the database file to disk. This is a no-op for in-memory databases +** or pages with the Pager.noSync flag set. ** -** When a savepoint is created, the PagerSavepoint.iHdrOffset field is -** set to 0. If a journal-header is written into the main journal while -** the savepoint is active, then iHdrOffset is set to the byte offset -** immediately following the last journal record written into the main -** journal before the journal-header. This is required during savepoint -** rollback (see pagerPlaybackSavepoint()). +** If successful, or if called on a pager for which it is a no-op, this +** function returns SQLITE_OK. Otherwise, an IO error code is returned. */ -typedef struct PagerSavepoint PagerSavepoint; -struct PagerSavepoint { - i64 iOffset; /* Starting offset in main journal */ - i64 iHdrOffset; /* See above */ - Bitvec *pInSavepoint; /* Set of pages in this savepoint */ - Pgno nOrig; /* Original number of pages in file */ - Pgno iSubRec; /* Index of first record in sub-journal */ -#ifndef SQLITE_OMIT_WAL - u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */ -#endif -}; +SQLITE_PRIVATE int sqlcipher_sqlite3PagerSync(Pager *pPager, const char *zSuper){ + int rc = SQLITE_OK; + void *pArg = (void*)zSuper; + rc = sqlcipher_sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg); + if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; + if( rc==SQLITE_OK && !pPager->noSync ){ + assert( !MEMDB ); + rc = sqlcipher_sqlite3OsSync(pPager->fd, pPager->syncFlags); + } + return rc; +} /* -** Bits of the Pager.doNotSpill flag. See further description below. +** This function may only be called while a write-transaction is active in +** rollback. If the connection is in WAL mode, this call is a no-op. +** Otherwise, if the connection does not already have an EXCLUSIVE lock on +** the database file, an attempt is made to obtain one. +** +** If the EXCLUSIVE lock is already held or the attempt to obtain it is +** successful, or the connection is in WAL mode, SQLITE_OK is returned. +** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is +** returned. */ -#define SPILLFLAG_OFF 0x01 /* Never spill cache. Set via pragma */ -#define SPILLFLAG_ROLLBACK 0x02 /* Current rolling back, so do not spill */ -#define SPILLFLAG_NOSYNC 0x04 /* Spill is ok, but do not sync */ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerExclusiveLock(Pager *pPager){ + int rc = pPager->errCode; + assert( assert_pager_state(pPager) ); + if( rc==SQLITE_OK ){ + assert( pPager->eState==PAGER_WRITER_CACHEMOD + || pPager->eState==PAGER_WRITER_DBMOD + || pPager->eState==PAGER_WRITER_LOCKED + ); + assert( assert_pager_state(pPager) ); + if( 0==pagerUseWal(pPager) ){ + rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); + } + } + return rc; +} /* -** An open page cache is an instance of struct Pager. A description of -** some of the more important member variables follows: -** -** eState -** -** The current 'state' of the pager object. See the comment and state -** diagram above for a description of the pager state. -** -** eLock -** -** For a real on-disk database, the current lock held on the database file - -** NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK. -** -** For a temporary or in-memory database (neither of which require any -** locks), this variable is always set to EXCLUSIVE_LOCK. Since such -** databases always have Pager.exclusiveMode==1, this tricks the pager -** logic into thinking that it already has all the locks it will ever -** need (and no reason to release them). -** -** In some (obscure) circumstances, this variable may also be set to -** UNKNOWN_LOCK. See the comment above the #define of UNKNOWN_LOCK for -** details. -** -** changeCountDone -** -** This boolean variable is used to make sure that the change-counter -** (the 4-byte header field at byte offset 24 of the database file) is -** not updated more often than necessary. -** -** It is set to true when the change-counter field is updated, which -** can only happen if an exclusive lock is held on the database file. -** It is cleared (set to false) whenever an exclusive lock is -** relinquished on the database file. Each time a transaction is committed, -** The changeCountDone flag is inspected. If it is true, the work of -** updating the change-counter is omitted for the current transaction. -** -** This mechanism means that when running in exclusive mode, a connection -** need only update the change-counter once, for the first transaction -** committed. -** -** setSuper -** -** When PagerCommitPhaseOne() is called to commit a transaction, it may -** (or may not) specify a super-journal name to be written into the -** journal file before it is synced to disk. -** -** Whether or not a journal file contains a super-journal pointer affects -** the way in which the journal file is finalized after the transaction is -** committed or rolled back when running in "journal_mode=PERSIST" mode. -** If a journal file does not contain a super-journal pointer, it is -** finalized by overwriting the first journal header with zeroes. If -** it does contain a super-journal pointer the journal file is finalized -** by truncating it to zero bytes, just as if the connection were -** running in "journal_mode=truncate" mode. -** -** Journal files that contain super-journal pointers cannot be finalized -** simply by overwriting the first journal-header with zeroes, as the -** super-journal pointer could interfere with hot-journal rollback of any -** subsequently interrupted transaction that reuses the journal file. -** -** The flag is cleared as soon as the journal file is finalized (either -** by PagerCommitPhaseTwo or PagerRollback). If an IO error prevents the -** journal file from being successfully finalized, the setSuper flag -** is cleared anyway (and the pager will move to ERROR state). -** -** doNotSpill -** -** This variables control the behavior of cache-spills (calls made by -** the pcache module to the pagerStress() routine to write cached data -** to the file-system in order to free up memory). -** -** When bits SPILLFLAG_OFF or SPILLFLAG_ROLLBACK of doNotSpill are set, -** writing to the database from pagerStress() is disabled altogether. -** The SPILLFLAG_ROLLBACK case is done in a very obscure case that -** comes up during savepoint rollback that requires the pcache module -** to allocate a new page to prevent the journal file from being written -** while it is being traversed by code in pager_playback(). The SPILLFLAG_OFF -** case is a user preference. -** -** If the SPILLFLAG_NOSYNC bit is set, writing to the database from -** pagerStress() is permitted, but syncing the journal file is not. -** This flag is set by sqlcipher_sqlite3PagerWrite() when the file-system sector-size -** is larger than the database page-size in order to prevent a journal sync -** from happening in between the journalling of two pages on the same sector. -** -** subjInMemory -** -** This is a boolean variable. If true, then any required sub-journal -** is opened as an in-memory journal file. If false, then in-memory -** sub-journals are only used for in-memory pager files. -** -** This variable is updated by the upper layer each time a new -** write-transaction is opened. -** -** dbSize, dbOrigSize, dbFileSize -** -** Variable dbSize is set to the number of pages in the database file. -** It is valid in PAGER_READER and higher states (all states except for -** OPEN and ERROR). -** -** dbSize is set based on the size of the database file, which may be -** larger than the size of the database (the value stored at offset -** 28 of the database header by the btree). If the size of the file -** is not an integer multiple of the page-size, the value stored in -** dbSize is rounded down (i.e. a 5KB file with 2K page-size has dbSize==2). -** Except, any file that is greater than 0 bytes in size is considered -** to have at least one page. (i.e. a 1KB file with 2K page-size leads -** to dbSize==1). -** -** During a write-transaction, if pages with page-numbers greater than -** dbSize are modified in the cache, dbSize is updated accordingly. -** Similarly, if the database is truncated using PagerTruncateImage(), -** dbSize is updated. -** -** Variables dbOrigSize and dbFileSize are valid in states -** PAGER_WRITER_LOCKED and higher. dbOrigSize is a copy of the dbSize -** variable at the start of the transaction. It is used during rollback, -** and to determine whether or not pages need to be journalled before -** being modified. -** -** Throughout a write-transaction, dbFileSize contains the size of -** the file on disk in pages. It is set to a copy of dbSize when the -** write-transaction is first opened, and updated when VFS calls are made -** to write or truncate the database file on disk. -** -** The only reason the dbFileSize variable is required is to suppress -** unnecessary calls to xTruncate() after committing a transaction. If, -** when a transaction is committed, the dbFileSize variable indicates -** that the database file is larger than the database image (Pager.dbSize), -** pager_truncate() is called. The pager_truncate() call uses xFilesize() -** to measure the database file on disk, and then truncates it if required. -** dbFileSize is not used when rolling back a transaction. In this case -** pager_truncate() is called unconditionally (which means there may be -** a call to xFilesize() that is not strictly required). In either case, -** pager_truncate() may cause the file to become smaller or larger. -** -** dbHintSize -** -** The dbHintSize variable is used to limit the number of calls made to -** the VFS xFileControl(FCNTL_SIZE_HINT) method. +** Sync the database file for the pager pPager. zSuper points to the name +** of a super-journal file that should be written into the individual +** journal file. zSuper may be NULL, which is interpreted as no +** super-journal (a single database transaction). ** -** dbHintSize is set to a copy of the dbSize variable when a -** write-transaction is opened (at the same time as dbFileSize and -** dbOrigSize). If the xFileControl(FCNTL_SIZE_HINT) method is called, -** dbHintSize is increased to the number of pages that correspond to the -** size-hint passed to the method call. See pager_write_pagelist() for -** details. +** This routine ensures that: ** -** errCode +** * The database file change-counter is updated, +** * the journal is synced (unless the atomic-write optimization is used), +** * all dirty pages are written to the database file, +** * the database file is truncated (if required), and +** * the database file synced. ** -** The Pager.errCode variable is only ever used in PAGER_ERROR state. It -** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode -** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX -** sub-codes. +** The only thing that remains to commit the transaction is to finalize +** (delete, truncate or zero the first part of) the journal file (or +** delete the super-journal file if specified). ** -** syncFlags, walSyncFlags +** Note that if zSuper==NULL, this does not overwrite a previous value +** passed to an sqlcipher_sqlite3PagerCommitPhaseOne() call. ** -** syncFlags is either SQLITE_SYNC_NORMAL (0x02) or SQLITE_SYNC_FULL (0x03). -** syncFlags is used for rollback mode. walSyncFlags is used for WAL mode -** and contains the flags used to sync the checkpoint operations in the -** lower two bits, and sync flags used for transaction commits in the WAL -** file in bits 0x04 and 0x08. In other words, to get the correct sync flags -** for checkpoint operations, use (walSyncFlags&0x03) and to get the correct -** sync flags for transaction commit, use ((walSyncFlags>>2)&0x03). Note -** that with synchronous=NORMAL in WAL mode, transaction commit is not synced -** meaning that the 0x04 and 0x08 bits are both zero. +** If the final parameter - noSync - is true, then the database file itself +** is not synced. The caller must call sqlcipher_sqlite3PagerSync() directly to +** sync the database file before calling CommitPhaseTwo() to delete the +** journal file in this case. */ -struct Pager { - sqlcipher_sqlite3_vfs *pVfs; /* OS functions to use for IO */ - u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ - u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */ - u8 useJournal; /* Use a rollback journal on this file */ - u8 noSync; /* Do not sync the journal if true */ - u8 fullSync; /* Do extra syncs of the journal for robustness */ - u8 extraSync; /* sync directory after journal delete */ - u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */ - u8 walSyncFlags; /* See description above */ - u8 tempFile; /* zFilename is a temporary or immutable file */ - u8 noLock; /* Do not lock (except in WAL mode) */ - u8 readOnly; /* True for a read-only database */ - u8 memDb; /* True to inhibit all file I/O */ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerCommitPhaseOne( + Pager *pPager, /* Pager object */ + const char *zSuper, /* If not NULL, the super-journal name */ + int noSync /* True to omit the xSync on the db file */ +){ + int rc = SQLITE_OK; /* Return code */ - /************************************************************************** - ** The following block contains those class members that change during - ** routine operation. Class members not in this block are either fixed - ** when the pager is first created or else only change when there is a - ** significant mode change (such as changing the page_size, locking_mode, - ** or the journal_mode). From another view, these class members describe - ** the "state" of the pager, while other class members describe the - ** "configuration" of the pager. - */ - u8 eState; /* Pager state (OPEN, READER, WRITER_LOCKED..) */ - u8 eLock; /* Current lock held on database file */ - u8 changeCountDone; /* Set after incrementing the change-counter */ - u8 setSuper; /* Super-jrnl name is written into jrnl */ - u8 doNotSpill; /* Do not spill the cache when non-zero */ - u8 subjInMemory; /* True to use in-memory sub-journals */ - u8 bUseFetch; /* True to use xFetch() */ - u8 hasHeldSharedLock; /* True if a shared lock has ever been held */ - Pgno dbSize; /* Number of pages in the database */ - Pgno dbOrigSize; /* dbSize before the current transaction */ - Pgno dbFileSize; /* Number of pages in the database file */ - Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */ - int errCode; /* One of several kinds of errors */ - int nRec; /* Pages journalled since last j-header written */ - u32 cksumInit; /* Quasi-random value added to every checksum */ - u32 nSubRec; /* Number of records written to sub-journal */ - Bitvec *pInJournal; /* One bit for each page in the database file */ - sqlcipher_sqlite3_file *fd; /* File descriptor for database */ - sqlcipher_sqlite3_file *jfd; /* File descriptor for main journal */ - sqlcipher_sqlite3_file *sjfd; /* File descriptor for sub-journal */ - i64 journalOff; /* Current write offset in the journal file */ - i64 journalHdr; /* Byte offset to previous journal header */ - sqlcipher_sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */ - PagerSavepoint *aSavepoint; /* Array of active savepoints */ - int nSavepoint; /* Number of elements in aSavepoint[] */ - u32 iDataVersion; /* Changes whenever database content changes */ - char dbFileVers[16]; /* Changes whenever database file changes */ + assert( pPager->eState==PAGER_WRITER_LOCKED + || pPager->eState==PAGER_WRITER_CACHEMOD + || pPager->eState==PAGER_WRITER_DBMOD + || pPager->eState==PAGER_ERROR + ); + assert( assert_pager_state(pPager) ); - int nMmapOut; /* Number of mmap pages currently outstanding */ - sqlcipher_sqlite3_int64 szMmap; /* Desired maximum mmap size */ - PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */ - /* - ** End of the routinely-changing class members - ***************************************************************************/ + /* If a prior error occurred, report that error again. */ + if( NEVER(pPager->errCode) ) return pPager->errCode; + + /* Provide the ability to easily simulate an I/O error during testing */ + if( sqlcipher_sqlite3FaultSim(400) ) return SQLITE_IOERR; + + PAGERTRACE(("DATABASE SYNC: File=%s zSuper=%s nSize=%d\n", + pPager->zFilename, zSuper, pPager->dbSize)); + + /* If no database changes have been made, return early. */ + if( pPager->eStatetempFile ); + assert( isOpen(pPager->fd) || pPager->tempFile ); + if( 0==pagerFlushOnCommit(pPager, 1) ){ + /* If this is an in-memory db, or no pages have been written to, or this + ** function has already been called, it is mostly a no-op. However, any + ** backup in progress needs to be restarted. */ + sqlcipher_sqlite3BackupRestart(pPager->pBackup); + }else{ + PgHdr *pList; + if( pagerUseWal(pPager) ){ + PgHdr *pPageOne = 0; + pList = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache); + if( pList==0 ){ + /* Must have at least one page for the WAL commit flag. + ** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */ + rc = sqlcipher_sqlite3PagerGet(pPager, 1, &pPageOne, 0); + pList = pPageOne; + pList->pDirty = 0; + } + assert( rc==SQLITE_OK ); + if( ALWAYS(pList) ){ + rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1); + } + sqlcipher_sqlite3PagerUnref(pPageOne); + if( rc==SQLITE_OK ){ + sqlcipher_sqlite3PcacheCleanAll(pPager->pPCache); + } + }else{ + /* The bBatch boolean is true if the batch-atomic-write commit method + ** should be used. No rollback journal is created if batch-atomic-write + ** is enabled. + */ +#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE + sqlcipher_sqlite3_file *fd = pPager->fd; + int bBatch = zSuper==0 /* An SQLITE_IOCAP_BATCH_ATOMIC commit */ + && (sqlcipher_sqlite3OsDeviceCharacteristics(fd) & SQLITE_IOCAP_BATCH_ATOMIC) + && !pPager->noSync + && sqlcipher_sqlite3JournalIsInMemory(pPager->jfd); +#else +# define bBatch 0 +#endif + +#ifdef SQLITE_ENABLE_ATOMIC_WRITE + /* The following block updates the change-counter. Exactly how it + ** does this depends on whether or not the atomic-update optimization + ** was enabled at compile time, and if this transaction meets the + ** runtime criteria to use the operation: + ** + ** * The file-system supports the atomic-write property for + ** blocks of size page-size, and + ** * This commit is not part of a multi-file transaction, and + ** * Exactly one page has been modified and store in the journal file. + ** + ** If the optimization was not enabled at compile time, then the + ** pager_incr_changecounter() function is called to update the change + ** counter in 'indirect-mode'. If the optimization is compiled in but + ** is not applicable to this transaction, call sqlcipher_sqlite3JournalCreate() + ** to make sure the journal file has actually been created, then call + ** pager_incr_changecounter() to update the change-counter in indirect + ** mode. + ** + ** Otherwise, if the optimization is both enabled and applicable, + ** then call pager_incr_changecounter() to update the change-counter + ** in 'direct' mode. In this case the journal file will never be + ** created for this transaction. + */ + if( bBatch==0 ){ + PgHdr *pPg; + assert( isOpen(pPager->jfd) + || pPager->journalMode==PAGER_JOURNALMODE_OFF + || pPager->journalMode==PAGER_JOURNALMODE_WAL + ); + if( !zSuper && isOpen(pPager->jfd) + && pPager->journalOff==jrnlBufferSize(pPager) + && pPager->dbSize>=pPager->dbOrigSize + && (!(pPg = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty) + ){ + /* Update the db file change counter via the direct-write method. The + ** following call will modify the in-memory representation of page 1 + ** to include the updated change counter and then write page 1 + ** directly to the database file. Because of the atomic-write + ** property of the host file-system, this is safe. + */ + rc = pager_incr_changecounter(pPager, 1); + }else{ + rc = sqlcipher_sqlite3JournalCreate(pPager->jfd); + if( rc==SQLITE_OK ){ + rc = pager_incr_changecounter(pPager, 0); + } + } + } +#else /* SQLITE_ENABLE_ATOMIC_WRITE */ +#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE + if( zSuper ){ + rc = sqlcipher_sqlite3JournalCreate(pPager->jfd); + if( rc!=SQLITE_OK ) goto commit_phase_one_exit; + assert( bBatch==0 ); + } +#endif + rc = pager_incr_changecounter(pPager, 0); +#endif /* !SQLITE_ENABLE_ATOMIC_WRITE */ + if( rc!=SQLITE_OK ) goto commit_phase_one_exit; + + /* Write the super-journal name into the journal file. If a + ** super-journal file name has already been written to the journal file, + ** or if zSuper is NULL (no super-journal), then this call is a no-op. + */ + rc = writeSuperJournal(pPager, zSuper); + if( rc!=SQLITE_OK ) goto commit_phase_one_exit; + + /* Sync the journal file and write all dirty pages to the database. + ** If the atomic-update optimization is being used, this sync will not + ** create the journal file or perform any real IO. + ** + ** Because the change-counter page was just modified, unless the + ** atomic-update optimization is used it is almost certain that the + ** journal requires a sync here. However, in locking_mode=exclusive + ** on a system under memory pressure it is just possible that this is + ** not the case. In this case it is likely enough that the redundant + ** xSync() call will be changed to a no-op by the OS anyhow. + */ + rc = syncJournal(pPager, 0); + if( rc!=SQLITE_OK ) goto commit_phase_one_exit; + + pList = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache); +#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE + if( bBatch ){ + rc = sqlcipher_sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0); + if( rc==SQLITE_OK ){ + rc = pager_write_pagelist(pPager, pList); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0); + } + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3OsFileControlHint(fd, SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, 0); + } + } + + if( (rc&0xFF)==SQLITE_IOERR && rc!=SQLITE_IOERR_NOMEM ){ + rc = sqlcipher_sqlite3JournalCreate(pPager->jfd); + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3OsClose(pPager->jfd); + goto commit_phase_one_exit; + } + bBatch = 0; + }else{ + sqlcipher_sqlite3OsClose(pPager->jfd); + } + } +#endif /* SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ - u16 nExtra; /* Add this many bytes to each in-memory page */ - i16 nReserve; /* Number of unused bytes at end of each page */ - u32 vfsFlags; /* Flags for sqlcipher_sqlite3_vfs.xOpen() */ - u32 sectorSize; /* Assumed sector size during rollback */ - int pageSize; /* Number of bytes in a page */ - Pgno mxPgno; /* Maximum allowed size of the database */ - i64 journalSizeLimit; /* Size limit for persistent journal files */ - char *zFilename; /* Name of the database file */ - char *zJournal; /* Name of the journal file */ - int (*xBusyHandler)(void*); /* Function to call when busy */ - void *pBusyHandlerArg; /* Context argument for xBusyHandler */ - int aStat[4]; /* Total cache hits, misses, writes, spills */ -#ifdef SQLITE_TEST - int nRead; /* Database pages read */ -#endif - void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ - int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ - void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ - void (*xCodecFree)(void*); /* Destructor for the codec */ - void *pCodec; /* First argument to xCodec... methods */ -#endif -/* END SQLCIPHER */ - char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ - PCache *pPCache; /* Pointer to page cache object */ -#ifndef SQLITE_OMIT_WAL - Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */ - char *zWal; /* File name for write-ahead log */ -#endif -}; + if( bBatch==0 ){ + rc = pager_write_pagelist(pPager, pList); + } + if( rc!=SQLITE_OK ){ + assert( rc!=SQLITE_IOERR_BLOCKED ); + goto commit_phase_one_exit; + } + sqlcipher_sqlite3PcacheCleanAll(pPager->pPCache); -/* -** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains -** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS -** or CACHE_WRITE to sqlcipher_sqlite3_db_status(). -*/ -#define PAGER_STAT_HIT 0 -#define PAGER_STAT_MISS 1 -#define PAGER_STAT_WRITE 2 -#define PAGER_STAT_SPILL 3 + /* If the file on disk is smaller than the database image, use + ** pager_truncate to grow the file here. This can happen if the database + ** image was extended as part of the current transaction and then the + ** last page in the db image moved to the free-list. In this case the + ** last page is never written out to disk, leaving the database file + ** undersized. Fix this now if it is the case. */ + if( pPager->dbSize>pPager->dbFileSize ){ + Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_SJ_PGNO(pPager)); + assert( pPager->eState==PAGER_WRITER_DBMOD ); + rc = pager_truncate(pPager, nNew); + if( rc!=SQLITE_OK ) goto commit_phase_one_exit; + } -/* -** The following global variables hold counters used for -** testing purposes only. These variables do not exist in -** a non-testing build. These variables are not thread-safe. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlcipher_sqlite3_pager_readdb_count = 0; /* Number of full pages read from DB */ -SQLITE_API int sqlcipher_sqlite3_pager_writedb_count = 0; /* Number of full pages written to DB */ -SQLITE_API int sqlcipher_sqlite3_pager_writej_count = 0; /* Number of pages written to journal */ -# define PAGER_INCR(v) v++ -#else -# define PAGER_INCR(v) -#endif + /* Finally, sync the database file. */ + if( !noSync ){ + rc = sqlcipher_sqlite3PagerSync(pPager, zSuper); + } + IOTRACE(("DBSYNC %p\n", pPager)) + } + } +commit_phase_one_exit: + if( rc==SQLITE_OK && !pagerUseWal(pPager) ){ + pPager->eState = PAGER_WRITER_FINISHED; + } + return rc; +} /* -** Journal files begin with the following magic string. The data -** was obtained from /dev/random. It is used only as a sanity check. +** When this function is called, the database file has been completely +** updated to reflect the changes made by the current transaction and +** synced to disk. The journal file still exists in the file-system +** though, and if a failure occurs at this point it will eventually +** be used as a hot-journal and the current transaction rolled back. ** -** Since version 2.8.0, the journal format contains additional sanity -** checking information. If the power fails while the journal is being -** written, semi-random garbage data might appear in the journal -** file after power is restored. If an attempt is then made -** to roll the journal back, the database could be corrupted. The additional -** sanity checking data is an attempt to discover the garbage in the -** journal and ignore it. +** This function finalizes the journal file, either by deleting, +** truncating or partially zeroing it, so that it cannot be used +** for hot-journal rollback. Once this is done the transaction is +** irrevocably committed. ** -** The sanity checking information for the new journal format consists -** of a 32-bit checksum on each page of data. The checksum covers both -** the page number and the pPager->pageSize bytes of data for the page. -** This cksum is initialized to a 32-bit random value that appears in the -** journal file right after the header. The random initializer is important, -** because garbage data that appears at the end of a journal is likely -** data that was once in other files that have now been deleted. If the -** garbage data came from an obsolete journal file, the checksums might -** be correct. But by initializing the checksum to random value which -** is different for every journal, we minimize that risk. +** If an error occurs, an IO error code is returned and the pager +** moves into the error state. Otherwise, SQLITE_OK is returned. */ -static const unsigned char aJournalMagic[] = { - 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7, -}; +SQLITE_PRIVATE int sqlcipher_sqlite3PagerCommitPhaseTwo(Pager *pPager){ + int rc = SQLITE_OK; /* Return code */ -/* -** The size of the of each page record in the journal is given by -** the following macro. -*/ -#define JOURNAL_PG_SZ(pPager) ((pPager->pageSize) + 8) + /* This routine should not be called if a prior error has occurred. + ** But if (due to a coding error elsewhere in the system) it does get + ** called, just return the same error code without doing anything. */ + if( NEVER(pPager->errCode) ) return pPager->errCode; + pPager->iDataVersion++; -/* -** The journal header size for this pager. This is usually the same -** size as a single disk sector. See also setSectorSize(). -*/ -#define JOURNAL_HDR_SZ(pPager) (pPager->sectorSize) + assert( pPager->eState==PAGER_WRITER_LOCKED + || pPager->eState==PAGER_WRITER_FINISHED + || (pagerUseWal(pPager) && pPager->eState==PAGER_WRITER_CACHEMOD) + ); + assert( assert_pager_state(pPager) ); -/* -** The macro MEMDB is true if we are dealing with an in-memory database. -** We do this as a macro so that if the SQLITE_OMIT_MEMORYDB macro is set, -** the value of MEMDB will be a constant and the compiler will optimize -** out code that would never execute. -*/ -#ifdef SQLITE_OMIT_MEMORYDB -# define MEMDB 0 -#else -# define MEMDB pPager->memDb -#endif + /* An optimization. If the database was not actually modified during + ** this transaction, the pager is running in exclusive-mode and is + ** using persistent journals, then this function is a no-op. + ** + ** The start of the journal file currently contains a single journal + ** header with the nRec field set to 0. If such a journal is used as + ** a hot-journal during hot-journal rollback, 0 changes will be made + ** to the database file. So there is no need to zero the journal + ** header. Since the pager is in exclusive mode, there is no need + ** to drop any locks either. + */ + if( pPager->eState==PAGER_WRITER_LOCKED + && pPager->exclusiveMode + && pPager->journalMode==PAGER_JOURNALMODE_PERSIST + ){ + assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff ); + pPager->eState = PAGER_READER; + return SQLITE_OK; + } -/* -** The macro USEFETCH is true if we are allowed to use the xFetch and xUnfetch -** interfaces to access the database using memory-mapped I/O. -*/ -#if SQLITE_MAX_MMAP_SIZE>0 -# define USEFETCH(x) ((x)->bUseFetch) -#else -# define USEFETCH(x) 0 -#endif + PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); + rc = pager_end_transaction(pPager, pPager->setSuper, 1); + return pager_error(pPager, rc); +} /* -** The argument to this macro is a file descriptor (type sqlcipher_sqlite3_file*). -** Return 0 if it is not open, or non-zero (but not 1) if it is. -** -** This is so that expressions can be written as: +** If a write transaction is open, then all changes made within the +** transaction are reverted and the current write-transaction is closed. +** The pager falls back to PAGER_READER state if successful, or PAGER_ERROR +** state if an error occurs. ** -** if( isOpen(pPager->jfd) ){ ... +** If the pager is already in PAGER_ERROR state when this function is called, +** it returns Pager.errCode immediately. No work is performed in this case. ** -** instead of +** Otherwise, in rollback mode, this function performs two functions: ** -** if( pPager->jfd->pMethods ){ ... -*/ -#define isOpen(pFd) ((pFd)->pMethods!=0) - -#ifdef SQLITE_DIRECT_OVERFLOW_READ -/* -** Return true if page pgno can be read directly from the database file -** by the b-tree layer. This is the case if: +** 1) It rolls back the journal file, restoring all database file and +** in-memory cache pages to the state they were in when the transaction +** was opened, and ** -** * the database file is open, -** * there are no dirty pages in the cache, and -** * the desired page is not currently in the wal file. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ - if( pPager->fd->pMethods==0 ) return 0; - if( sqlcipher_sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - if( pPager->xCodec!=0 ) return 0; -#endif -/* END SQLCIPHER */ -#ifndef SQLITE_OMIT_WAL - if( pPager->pWal ){ - u32 iRead = 0; - int rc; - rc = sqlcipher_sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); - return (rc==SQLITE_OK && iRead==0); - } -#endif - return 1; -} -#endif - -#ifndef SQLITE_OMIT_WAL -# define pagerUseWal(x) ((x)->pWal!=0) -#else -# define pagerUseWal(x) 0 -# define pagerRollbackWal(x) 0 -# define pagerWalFrames(v,w,x,y) 0 -# define pagerOpenWalIfPresent(z) SQLITE_OK -# define pagerBeginReadTransaction(z) SQLITE_OK -#endif - -#ifndef NDEBUG -/* -** Usage: +** 2) It finalizes the journal file, so that it is not used for hot +** rollback at any point in the future. ** -** assert( assert_pager_state(pPager) ); +** Finalization of the journal file (task 2) is only performed if the +** rollback is successful. ** -** This function runs many asserts to try to find inconsistencies in -** the internal state of the Pager object. +** In WAL mode, all cache-entries containing data modified within the +** current transaction are either expelled from the cache or reverted to +** their pre-transaction state by re-reading data from the database or +** WAL files. The WAL transaction is then closed. */ -static int assert_pager_state(Pager *p){ - Pager *pPager = p; - - /* State must be valid. */ - assert( p->eState==PAGER_OPEN - || p->eState==PAGER_READER - || p->eState==PAGER_WRITER_LOCKED - || p->eState==PAGER_WRITER_CACHEMOD - || p->eState==PAGER_WRITER_DBMOD - || p->eState==PAGER_WRITER_FINISHED - || p->eState==PAGER_ERROR - ); - - /* Regardless of the current state, a temp-file connection always behaves - ** as if it has an exclusive lock on the database file. It never updates - ** the change-counter field, so the changeCountDone flag is always set. - */ - assert( p->tempFile==0 || p->eLock==EXCLUSIVE_LOCK ); - assert( p->tempFile==0 || pPager->changeCountDone ); +SQLITE_PRIVATE int sqlcipher_sqlite3PagerRollback(Pager *pPager){ + int rc = SQLITE_OK; /* Return code */ + PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager))); - /* If the useJournal flag is clear, the journal-mode must be "OFF". - ** And if the journal-mode is "OFF", the journal file must not be open. + /* PagerRollback() is a no-op if called in READER or OPEN state. If + ** the pager is already in the ERROR state, the rollback is not + ** attempted here. Instead, the error code is returned to the caller. */ - assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal ); - assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) ); + assert( assert_pager_state(pPager) ); + if( pPager->eState==PAGER_ERROR ) return pPager->errCode; + if( pPager->eState<=PAGER_READER ) return SQLITE_OK; - /* Check that MEMDB implies noSync. And an in-memory journal. Since - ** this means an in-memory pager performs no IO at all, it cannot encounter - ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing - ** a journal file. (although the in-memory journal implementation may - ** return SQLITE_IOERR_NOMEM while the journal file is being written). It - ** is therefore not possible for an in-memory pager to enter the ERROR - ** state. - */ - if( MEMDB ){ - assert( !isOpen(p->fd) ); - assert( p->noSync ); - assert( p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_MEMORY - ); - assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN ); - assert( pagerUseWal(p)==0 ); + if( pagerUseWal(pPager) ){ + int rc2; + rc = sqlcipher_sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1); + rc2 = pager_end_transaction(pPager, pPager->setSuper, 0); + if( rc==SQLITE_OK ) rc = rc2; + }else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){ + int eState = pPager->eState; + rc = pager_end_transaction(pPager, 0, 0); + if( !MEMDB && eState>PAGER_WRITER_LOCKED ){ + /* This can happen using journal_mode=off. Move the pager to the error + ** state to indicate that the contents of the cache may not be trusted. + ** Any active readers will get SQLITE_ABORT. + */ + pPager->errCode = SQLITE_ABORT; + pPager->eState = PAGER_ERROR; + setGetterMethod(pPager); + return rc; + } + }else{ + rc = pager_playback(pPager, 0); } - /* If changeCountDone is set, a RESERVED lock or greater must be held - ** on the file. - */ - assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK ); - assert( p->eLock!=PENDING_LOCK ); - - switch( p->eState ){ - case PAGER_OPEN: - assert( !MEMDB ); - assert( pPager->errCode==SQLITE_OK ); - assert( sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)==0 || pPager->tempFile ); - break; - - case PAGER_READER: - assert( pPager->errCode==SQLITE_OK ); - assert( p->eLock!=UNKNOWN_LOCK ); - assert( p->eLock>=SHARED_LOCK ); - break; - - case PAGER_WRITER_LOCKED: - assert( p->eLock!=UNKNOWN_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - if( !pagerUseWal(pPager) ){ - assert( p->eLock>=RESERVED_LOCK ); - } - assert( pPager->dbSize==pPager->dbOrigSize ); - assert( pPager->dbOrigSize==pPager->dbFileSize ); - assert( pPager->dbOrigSize==pPager->dbHintSize ); - assert( pPager->setSuper==0 ); - break; - - case PAGER_WRITER_CACHEMOD: - assert( p->eLock!=UNKNOWN_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - if( !pagerUseWal(pPager) ){ - /* It is possible that if journal_mode=wal here that neither the - ** journal file nor the WAL file are open. This happens during - ** a rollback transaction that switches from journal_mode=off - ** to journal_mode=wal. - */ - assert( p->eLock>=RESERVED_LOCK ); - assert( isOpen(p->jfd) - || p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_WAL - ); - } - assert( pPager->dbOrigSize==pPager->dbFileSize ); - assert( pPager->dbOrigSize==pPager->dbHintSize ); - break; - - case PAGER_WRITER_DBMOD: - assert( p->eLock==EXCLUSIVE_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - assert( !pagerUseWal(pPager) ); - assert( p->eLock>=EXCLUSIVE_LOCK ); - assert( isOpen(p->jfd) - || p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_WAL - || (sqlcipher_sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC) - ); - assert( pPager->dbOrigSize<=pPager->dbHintSize ); - break; - - case PAGER_WRITER_FINISHED: - assert( p->eLock==EXCLUSIVE_LOCK ); - assert( pPager->errCode==SQLITE_OK ); - assert( !pagerUseWal(pPager) ); - assert( isOpen(p->jfd) - || p->journalMode==PAGER_JOURNALMODE_OFF - || p->journalMode==PAGER_JOURNALMODE_WAL - || (sqlcipher_sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC) - ); - break; + assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK ); + assert( rc==SQLITE_OK || rc==SQLITE_FULL || rc==SQLITE_CORRUPT + || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR + || rc==SQLITE_CANTOPEN + ); - case PAGER_ERROR: - /* There must be at least one outstanding reference to the pager if - ** in ERROR state. Otherwise the pager should have already dropped - ** back to OPEN state. - */ - assert( pPager->errCode!=SQLITE_OK ); - assert( sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)>0 || pPager->tempFile ); - break; - } + /* If an error occurs during a ROLLBACK, we can no longer trust the pager + ** cache. So call pager_error() on the way out to make any error persistent. + */ + return pager_error(pPager, rc); +} - return 1; +/* +** Return TRUE if the database file is opened read-only. Return FALSE +** if the database is (in theory) writable. +*/ +SQLITE_PRIVATE u8 sqlcipher_sqlite3PagerIsreadonly(Pager *pPager){ + return pPager->readOnly; } -#endif /* ifndef NDEBUG */ #ifdef SQLITE_DEBUG /* -** Return a pointer to a human readable string in a static buffer -** containing the state of the Pager object passed as an argument. This -** is intended to be used within debuggers. For example, as an alternative -** to "print *pPager" in gdb: -** -** (gdb) printf "%s", print_pager_state(pPager) -** -** This routine has external linkage in order to suppress compiler warnings -** about an unused function. It is enclosed within SQLITE_DEBUG and so does -** not appear in normal builds. +** Return the sum of the reference counts for all pages held by pPager. */ -char *print_pager_state(Pager *p){ - static char zRet[1024]; - - sqlcipher_sqlite3_snprintf(1024, zRet, - "Filename: %s\n" - "State: %s errCode=%d\n" - "Lock: %s\n" - "Locking mode: locking_mode=%s\n" - "Journal mode: journal_mode=%s\n" - "Backing store: tempFile=%d memDb=%d useJournal=%d\n" - "Journal: journalOff=%lld journalHdr=%lld\n" - "Size: dbsize=%d dbOrigSize=%d dbFileSize=%d\n" - , p->zFilename - , p->eState==PAGER_OPEN ? "OPEN" : - p->eState==PAGER_READER ? "READER" : - p->eState==PAGER_WRITER_LOCKED ? "WRITER_LOCKED" : - p->eState==PAGER_WRITER_CACHEMOD ? "WRITER_CACHEMOD" : - p->eState==PAGER_WRITER_DBMOD ? "WRITER_DBMOD" : - p->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" : - p->eState==PAGER_ERROR ? "ERROR" : "?error?" - , (int)p->errCode - , p->eLock==NO_LOCK ? "NO_LOCK" : - p->eLock==RESERVED_LOCK ? "RESERVED" : - p->eLock==EXCLUSIVE_LOCK ? "EXCLUSIVE" : - p->eLock==SHARED_LOCK ? "SHARED" : - p->eLock==UNKNOWN_LOCK ? "UNKNOWN" : "?error?" - , p->exclusiveMode ? "exclusive" : "normal" - , p->journalMode==PAGER_JOURNALMODE_MEMORY ? "memory" : - p->journalMode==PAGER_JOURNALMODE_OFF ? "off" : - p->journalMode==PAGER_JOURNALMODE_DELETE ? "delete" : - p->journalMode==PAGER_JOURNALMODE_PERSIST ? "persist" : - p->journalMode==PAGER_JOURNALMODE_TRUNCATE ? "truncate" : - p->journalMode==PAGER_JOURNALMODE_WAL ? "wal" : "?error?" - , (int)p->tempFile, (int)p->memDb, (int)p->useJournal - , p->journalOff, p->journalHdr - , (int)p->dbSize, (int)p->dbOrigSize, (int)p->dbFileSize - ); - - return zRet; +SQLITE_PRIVATE int sqlcipher_sqlite3PagerRefcount(Pager *pPager){ + return sqlcipher_sqlite3PcacheRefCount(pPager->pPCache); } #endif -/* Forward references to the various page getters */ -static int getPageNormal(Pager*,Pgno,DbPage**,int); -static int getPageError(Pager*,Pgno,DbPage**,int); -#if SQLITE_MAX_MMAP_SIZE>0 -static int getPageMMap(Pager*,Pgno,DbPage**,int); -#endif - /* -** Set the Pager.xGet method for the appropriate routine used to fetch -** content from the pager. +** Return the approximate number of bytes of memory currently +** used by the pager and its associated cache. */ -static void setGetterMethod(Pager *pPager){ - if( pPager->errCode ){ - pPager->xGet = getPageError; -#if SQLITE_MAX_MMAP_SIZE>0 - }else if( USEFETCH(pPager) -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - && pPager->xCodec==0 -#endif -/* END SQLCIPHER */ - ){ - pPager->xGet = getPageMMap; -#endif /* SQLITE_MAX_MMAP_SIZE>0 */ - }else{ - pPager->xGet = getPageNormal; - } +SQLITE_PRIVATE int sqlcipher_sqlite3PagerMemUsed(Pager *pPager){ + int perPageSize = pPager->pageSize + pPager->nExtra + + (int)(sizeof(PgHdr) + 5*sizeof(void*)); + return perPageSize*sqlcipher_sqlite3PcachePagecount(pPager->pPCache) + + sqlcipher_sqlite3MallocSize(pPager) + + pPager->pageSize; } /* -** Return true if it is necessary to write page *pPg into the sub-journal. -** A page needs to be written into the sub-journal if there exists one -** or more open savepoints for which: -** -** * The page-number is less than or equal to PagerSavepoint.nOrig, and -** * The bit corresponding to the page-number is not set in -** PagerSavepoint.pInSavepoint. +** Return the number of references to the specified page. */ -static int subjRequiresPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - PagerSavepoint *p; - Pgno pgno = pPg->pgno; - int i; - for(i=0; inSavepoint; i++){ - p = &pPager->aSavepoint[i]; - if( p->nOrig>=pgno && 0==sqlcipher_sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){ - return 1; - } - } - return 0; +SQLITE_PRIVATE int sqlcipher_sqlite3PagerPageRefcount(DbPage *pPage){ + return sqlcipher_sqlite3PcachePageRefcount(pPage); } -#ifdef SQLITE_DEBUG +#ifdef SQLITE_TEST /* -** Return true if the page is already in the journal file. +** This routine is used for testing and analysis only. */ -static int pageInJournal(Pager *pPager, PgHdr *pPg){ - return sqlcipher_sqlite3BitvecTest(pPager->pInJournal, pPg->pgno); +SQLITE_PRIVATE int *sqlcipher_sqlite3PagerStats(Pager *pPager){ + static int a[11]; + a[0] = sqlcipher_sqlite3PcacheRefCount(pPager->pPCache); + a[1] = sqlcipher_sqlite3PcachePagecount(pPager->pPCache); + a[2] = sqlcipher_sqlite3PcacheGetCachesize(pPager->pPCache); + a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; + a[4] = pPager->eState; + a[5] = pPager->errCode; + a[6] = pPager->aStat[PAGER_STAT_HIT]; + a[7] = pPager->aStat[PAGER_STAT_MISS]; + a[8] = 0; /* Used to be pPager->nOvfl */ + a[9] = pPager->nRead; + a[10] = pPager->aStat[PAGER_STAT_WRITE]; + return a; } #endif /* -** Read a 32-bit integer from the given file descriptor. Store the integer -** that is read in *pRes. Return SQLITE_OK if everything worked, or an -** error code is something goes wrong. +** Parameter eStat must be one of SQLITE_DBSTATUS_CACHE_HIT, _MISS, _WRITE, +** or _WRITE+1. The SQLITE_DBSTATUS_CACHE_WRITE+1 case is a translation +** of SQLITE_DBSTATUS_CACHE_SPILL. The _SPILL case is not contiguous because +** it was added later. ** -** All values are stored on disk as big-endian. +** Before returning, *pnVal is incremented by the +** current cache hit or miss count, according to the value of eStat. If the +** reset parameter is non-zero, the cache hit or miss count is zeroed before +** returning. */ -static int read32bits(sqlcipher_sqlite3_file *fd, i64 offset, u32 *pRes){ - unsigned char ac[4]; - int rc = sqlcipher_sqlite3OsRead(fd, ac, sizeof(ac), offset); - if( rc==SQLITE_OK ){ - *pRes = sqlcipher_sqlite3Get4byte(ac); - } - return rc; -} +SQLITE_PRIVATE void sqlcipher_sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){ -/* -** Write a 32-bit integer into a string buffer in big-endian byte order. -*/ -#define put32bits(A,B) sqlcipher_sqlite3Put4byte((u8*)A,B) + assert( eStat==SQLITE_DBSTATUS_CACHE_HIT + || eStat==SQLITE_DBSTATUS_CACHE_MISS + || eStat==SQLITE_DBSTATUS_CACHE_WRITE + || eStat==SQLITE_DBSTATUS_CACHE_WRITE+1 + ); + + assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS ); + assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE ); + assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 + && PAGER_STAT_WRITE==2 && PAGER_STAT_SPILL==3 ); + eStat -= SQLITE_DBSTATUS_CACHE_HIT; + *pnVal += pPager->aStat[eStat]; + if( reset ){ + pPager->aStat[eStat] = 0; + } +} /* -** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK -** on success or an error code is something goes wrong. +** Return true if this is an in-memory or temp-file backed pager. */ -static int write32bits(sqlcipher_sqlite3_file *fd, i64 offset, u32 val){ - char ac[4]; - put32bits(ac, val); - return sqlcipher_sqlite3OsWrite(fd, ac, 4, offset); +SQLITE_PRIVATE int sqlcipher_sqlite3PagerIsMemdb(Pager *pPager){ + return pPager->tempFile || pPager->memVfs; } /* -** Unlock the database file to level eLock, which must be either NO_LOCK -** or SHARED_LOCK. Regardless of whether or not the call to xUnlock() -** succeeds, set the Pager.eLock variable to match the (attempted) new lock. +** Check that there are at least nSavepoint savepoints open. If there are +** currently less than nSavepoints open, then open one or more savepoints +** to make up the difference. If the number of savepoints is already +** equal to nSavepoint, then this function is a no-op. ** -** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is -** called, do not modify it. See the comment above the #define of -** UNKNOWN_LOCK for an explanation of this. +** If a memory allocation fails, SQLITE_NOMEM is returned. If an error +** occurs while opening the sub-journal file, then an IO error code is +** returned. Otherwise, SQLITE_OK. */ -static int pagerUnlockDb(Pager *pPager, int eLock){ - int rc = SQLITE_OK; +static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){ + int rc = SQLITE_OK; /* Return code */ + int nCurrent = pPager->nSavepoint; /* Current number of savepoints */ + int ii; /* Iterator variable */ + PagerSavepoint *aNew; /* New Pager.aSavepoint array */ - assert( !pPager->exclusiveMode || pPager->eLock==eLock ); - assert( eLock==NO_LOCK || eLock==SHARED_LOCK ); - assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); - if( isOpen(pPager->fd) ){ - assert( pPager->eLock>=eLock ); - rc = pPager->noLock ? SQLITE_OK : sqlcipher_sqlite3OsUnlock(pPager->fd, eLock); - if( pPager->eLock!=UNKNOWN_LOCK ){ - pPager->eLock = (u8)eLock; + assert( pPager->eState>=PAGER_WRITER_LOCKED ); + assert( assert_pager_state(pPager) ); + assert( nSavepoint>nCurrent && pPager->useJournal ); + + /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM + ** if the allocation fails. Otherwise, zero the new portion in case a + ** malloc failure occurs while populating it in the for(...) loop below. + */ + aNew = (PagerSavepoint *)sqlcipher_sqlite3Realloc( + pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint + ); + if( !aNew ){ + return SQLITE_NOMEM_BKPT; + } + memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint)); + pPager->aSavepoint = aNew; + + /* Populate the PagerSavepoint structures just allocated. */ + for(ii=nCurrent; iidbSize; + if( isOpen(pPager->jfd) && pPager->journalOff>0 ){ + aNew[ii].iOffset = pPager->journalOff; + }else{ + aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager); } - IOTRACE(("UNLOCK %p %d\n", pPager, eLock)) + aNew[ii].iSubRec = pPager->nSubRec; + aNew[ii].pInSavepoint = sqlcipher_sqlite3BitvecCreate(pPager->dbSize); + aNew[ii].bTruncateOnRelease = 1; + if( !aNew[ii].pInSavepoint ){ + return SQLITE_NOMEM_BKPT; + } + if( pagerUseWal(pPager) ){ + sqlcipher_sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData); + } + pPager->nSavepoint = ii+1; } - pPager->changeCountDone = pPager->tempFile; /* ticket fb3b3024ea238d5c */ + assert( pPager->nSavepoint==nSavepoint ); + assertTruncateConstraint(pPager); return rc; } +SQLITE_PRIVATE int sqlcipher_sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ + assert( pPager->eState>=PAGER_WRITER_LOCKED ); + assert( assert_pager_state(pPager) ); -/* -** Lock the database file to level eLock, which must be either SHARED_LOCK, -** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the -** Pager.eLock variable to the new locking state. -** -** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is -** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. -** See the comment above the #define of UNKNOWN_LOCK for an explanation -** of this. -*/ -static int pagerLockDb(Pager *pPager, int eLock){ - int rc = SQLITE_OK; - - assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK ); - if( pPager->eLockeLock==UNKNOWN_LOCK ){ - rc = pPager->noLock ? SQLITE_OK : sqlcipher_sqlite3OsLock(pPager->fd, eLock); - if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){ - pPager->eLock = (u8)eLock; - IOTRACE(("LOCK %p %d\n", pPager, eLock)) - } + if( nSavepoint>pPager->nSavepoint && pPager->useJournal ){ + return pagerOpenSavepoint(pPager, nSavepoint); + }else{ + return SQLITE_OK; } - return rc; } + /* -** This function determines whether or not the atomic-write or -** atomic-batch-write optimizations can be used with this pager. The -** atomic-write optimization can be used if: +** This function is called to rollback or release (commit) a savepoint. +** The savepoint to release or rollback need not be the most recently +** created savepoint. ** -** (a) the value returned by OsDeviceCharacteristics() indicates that -** a database page may be written atomically, and -** (b) the value returned by OsSectorSize() is less than or equal -** to the page size. +** Parameter op is always either SAVEPOINT_ROLLBACK or SAVEPOINT_RELEASE. +** If it is SAVEPOINT_RELEASE, then release and destroy the savepoint with +** index iSavepoint. If it is SAVEPOINT_ROLLBACK, then rollback all changes +** that have occurred since the specified savepoint was created. ** -** If it can be used, then the value returned is the size of the journal -** file when it contains rollback data for exactly one page. +** The savepoint to rollback or release is identified by parameter +** iSavepoint. A value of 0 means to operate on the outermost savepoint +** (the first created). A value of (Pager.nSavepoint-1) means operate +** on the most recently created savepoint. If iSavepoint is greater than +** (Pager.nSavepoint-1), then this function is a no-op. ** -** The atomic-batch-write optimization can be used if OsDeviceCharacteristics() -** returns a value with the SQLITE_IOCAP_BATCH_ATOMIC bit set. -1 is -** returned in this case. +** If a negative value is passed to this function, then the current +** transaction is rolled back. This is different to calling +** sqlcipher_sqlite3PagerRollback() because this function does not terminate +** the transaction or unlock the database, it just restores the +** contents of the database to its original state. ** -** If neither optimization can be used, 0 is returned. +** In any case, all savepoints with an index greater than iSavepoint +** are destroyed. If this is a release operation (op==SAVEPOINT_RELEASE), +** then savepoint iSavepoint is also destroyed. +** +** This function may return SQLITE_NOMEM if a memory allocation fails, +** or an IO error code if an IO error occurs while rolling back a +** savepoint. If no errors occur, SQLITE_OK is returned. */ -static int jrnlBufferSize(Pager *pPager){ - assert( !MEMDB ); - -#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ - || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) - int dc; /* Device characteristics */ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ + int rc = pPager->errCode; - assert( isOpen(pPager->fd) ); - dc = sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd); -#else - UNUSED_PARAMETER(pPager); +#ifdef SQLITE_ENABLE_ZIPVFS + if( op==SAVEPOINT_RELEASE ) rc = SQLITE_OK; #endif -#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE - if( pPager->dbSize>0 && (dc&SQLITE_IOCAP_BATCH_ATOMIC) ){ - return -1; - } -#endif + assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); + assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK ); -#ifdef SQLITE_ENABLE_ATOMIC_WRITE - { - int nSector = pPager->sectorSize; - int szPage = pPager->pageSize; + if( rc==SQLITE_OK && iSavepointnSavepoint ){ + int ii; /* Iterator variable */ + int nNew; /* Number of remaining savepoints after this op. */ - assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); - assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); - if( 0==(dc&(SQLITE_IOCAP_ATOMIC|(szPage>>8)) || nSector>szPage) ){ - return 0; + /* Figure out how many savepoints will still be active after this + ** operation. Store this value in nNew. Then free resources associated + ** with any savepoints that are destroyed by this operation. + */ + nNew = iSavepoint + (( op==SAVEPOINT_RELEASE ) ? 0 : 1); + for(ii=nNew; iinSavepoint; ii++){ + sqlcipher_sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint); } - } + pPager->nSavepoint = nNew; - return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager); + /* Truncate the sub-journal so that it only includes the parts + ** that are still in use. */ + if( op==SAVEPOINT_RELEASE ){ + PagerSavepoint *pRel = &pPager->aSavepoint[nNew]; + if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){ + /* Only truncate if it is an in-memory sub-journal. */ + if( sqlcipher_sqlite3JournalIsInMemory(pPager->sjfd) ){ + i64 sz = (pPager->pageSize+4)*(i64)pRel->iSubRec; + rc = sqlcipher_sqlite3OsTruncate(pPager->sjfd, sz); + assert( rc==SQLITE_OK ); + } + pPager->nSubRec = pRel->iSubRec; + } + } + /* Else this is a rollback operation, playback the specified savepoint. + ** If this is a temp-file, it is possible that the journal file has + ** not yet been opened. In this case there have been no changes to + ** the database file, so the playback operation can be skipped. + */ + else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){ + PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1]; + rc = pagerPlaybackSavepoint(pPager, pSavepoint); + assert(rc!=SQLITE_DONE); + } + +#ifdef SQLITE_ENABLE_ZIPVFS + /* If the cache has been modified but the savepoint cannot be rolled + ** back journal_mode=off, put the pager in the error state. This way, + ** if the VFS used by this pager includes ZipVFS, the entire transaction + ** can be rolled back at the ZipVFS level. */ + else if( + pPager->journalMode==PAGER_JOURNALMODE_OFF + && pPager->eState>=PAGER_WRITER_CACHEMOD + ){ + pPager->errCode = SQLITE_ABORT; + pPager->eState = PAGER_ERROR; + setGetterMethod(pPager); + } #endif + } - return 0; + return rc; } /* -** If SQLITE_CHECK_PAGES is defined then we do some sanity checking -** on the cache using a hash function. This is used for testing -** and debugging only. +** Return the full pathname of the database file. +** +** Except, if the pager is in-memory only, then return an empty string if +** nullIfMemDb is true. This routine is called with nullIfMemDb==1 when +** used to report the filename to the user, for compatibility with legacy +** behavior. But when the Btree needs to know the filename for matching to +** shared cache, it uses nullIfMemDb==0 so that in-memory databases can +** participate in shared-cache. +** +** The return value to this routine is always safe to use with +** sqlcipher_sqlite3_uri_parameter() and sqlcipher_sqlite3_filename_database() and friends. */ -#ifdef SQLITE_CHECK_PAGES +SQLITE_PRIVATE const char *sqlcipher_sqlite3PagerFilename(const Pager *pPager, int nullIfMemDb){ + static const char zFake[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + return (nullIfMemDb && pPager->memDb) ? &zFake[4] : pPager->zFilename; +} + /* -** Return a 32-bit hash of the page data for pPage. +** Return the VFS structure for the pager. */ -static u32 pager_datahash(int nByte, unsigned char *pData){ - u32 hash = 0; - int i; - for(i=0; ipPager->pageSize, (unsigned char *)pPage->pData); -} -static void pager_set_pagehash(PgHdr *pPage){ - pPage->pageHash = pager_pagehash(pPage); +SQLITE_PRIVATE sqlcipher_sqlite3_vfs *sqlcipher_sqlite3PagerVfs(Pager *pPager){ + return pPager->pVfs; } /* -** The CHECK_PAGE macro takes a PgHdr* as an argument. If SQLITE_CHECK_PAGES -** is defined, and NDEBUG is not defined, an assert() statement checks -** that the page is either dirty or still matches the calculated page-hash. +** Return the file handle for the database file associated +** with the pager. This might return NULL if the file has +** not yet been opened. */ -#define CHECK_PAGE(x) checkPage(x) -static void checkPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - assert( pPager->eState!=PAGER_ERROR ); - assert( (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) ); +SQLITE_PRIVATE sqlcipher_sqlite3_file *sqlcipher_sqlite3PagerFile(Pager *pPager){ + return pPager->fd; } -#else -#define pager_datahash(X,Y) 0 -#define pager_pagehash(X) 0 -#define pager_set_pagehash(X) -#define CHECK_PAGE(x) -#endif /* SQLITE_CHECK_PAGES */ - /* -** When this is called the journal file for pager pPager must be open. -** This function attempts to read a super-journal file name from the -** end of the file and, if successful, copies it into memory supplied -** by the caller. See comments above writeSuperJournal() for the format -** used to store a super-journal file name at the end of a journal file. -** -** zSuper must point to a buffer of at least nSuper bytes allocated by -** the caller. This should be sqlcipher_sqlite3_vfs.mxPathname+1 (to ensure there is -** enough space to write the super-journal name). If the super-journal -** name in the journal is longer than nSuper bytes (including a -** nul-terminator), then this is handled as if no super-journal name -** were present in the journal. -** -** If a super-journal file name is present at the end of the journal -** file, then it is copied into the buffer pointed to by zSuper. A -** nul-terminator byte is appended to the buffer following the -** super-journal file name. -** -** If it is determined that no super-journal file name is present -** zSuper[0] is set to 0 and SQLITE_OK returned. -** -** If an error occurs while reading from the journal file, an SQLite -** error code is returned. +** Return the file handle for the journal file (if it exists). +** This will be either the rollback journal or the WAL file. */ -static int readSuperJournal(sqlcipher_sqlite3_file *pJrnl, char *zSuper, u32 nSuper){ - int rc; /* Return code */ - u32 len; /* Length in bytes of super-journal name */ - i64 szJ; /* Total size in bytes of journal file pJrnl */ - u32 cksum; /* MJ checksum value read from journal */ - u32 u; /* Unsigned loop counter */ - unsigned char aMagic[8]; /* A buffer to hold the magic header */ - zSuper[0] = '\0'; - - if( SQLITE_OK!=(rc = sqlcipher_sqlite3OsFileSize(pJrnl, &szJ)) - || szJ<16 - || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len)) - || len>=nSuper - || len>szJ-16 - || len==0 - || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum)) - || SQLITE_OK!=(rc = sqlcipher_sqlite3OsRead(pJrnl, aMagic, 8, szJ-8)) - || memcmp(aMagic, aJournalMagic, 8) - || SQLITE_OK!=(rc = sqlcipher_sqlite3OsRead(pJrnl, zSuper, len, szJ-16-len)) - ){ - return rc; - } - - /* See if the checksum matches the super-journal name */ - for(u=0; ujfd; +#else + return pPager->pWal ? sqlcipher_sqlite3WalFile(pPager->pWal) : pPager->jfd; +#endif } /* -** Return the offset of the sector boundary at or immediately -** following the value in pPager->journalOff, assuming a sector -** size of pPager->sectorSize bytes. -** -** i.e for a sector size of 512: -** -** Pager.journalOff Return value -** --------------------------------------- -** 0 0 -** 512 512 -** 100 512 -** 2000 2048 -** +** Return the full pathname of the journal file. */ -static i64 journalHdrOffset(Pager *pPager){ - i64 offset = 0; - i64 c = pPager->journalOff; - if( c ){ - offset = ((c-1)/JOURNAL_HDR_SZ(pPager) + 1) * JOURNAL_HDR_SZ(pPager); - } - assert( offset%JOURNAL_HDR_SZ(pPager)==0 ); - assert( offset>=c ); - assert( (offset-c)zJournal; } +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC /* -** The journal file must be open when this function is called. -** -** This function is a no-op if the journal file has not been written to -** within the current transaction (i.e. if Pager.journalOff==0). -** -** If doTruncate is non-zero or the Pager.journalSizeLimit variable is -** set to 0, then truncate the journal file to zero bytes in size. Otherwise, -** zero the 28-byte header at the start of the journal file. In either case, -** if the pager is not in no-sync mode, sync the journal file immediately -** after writing or truncating it. -** -** If Pager.journalSizeLimit is set to a positive, non-zero value, and -** following the truncation or zeroing described above the size of the -** journal file in bytes is larger than this value, then truncate the -** journal file to Pager.journalSizeLimit bytes. The journal file does -** not need to be synced following this operation. -** -** If an IO error occurs, abandon processing and return the IO error code. -** Otherwise, return SQLITE_OK. +** Set or retrieve the codec for this pager */ -static int zeroJournalHdr(Pager *pPager, int doTruncate){ - int rc = SQLITE_OK; /* Return code */ - assert( isOpen(pPager->jfd) ); - assert( !sqlcipher_sqlite3JournalIsInMemory(pPager->jfd) ); - if( pPager->journalOff ){ - const i64 iLimit = pPager->journalSizeLimit; /* Local cache of jsl */ - - IOTRACE(("JZEROHDR %p\n", pPager)) - if( doTruncate || iLimit==0 ){ - rc = sqlcipher_sqlite3OsTruncate(pPager->jfd, 0); - }else{ - static const char zeroHdr[28] = {0}; - rc = sqlcipher_sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0); - } - if( rc==SQLITE_OK && !pPager->noSync ){ - rc = sqlcipher_sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags); - } - - /* At this point the transaction is committed but the write lock - ** is still held on the file. If there is a size limit configured for - ** the persistent journal and the journal file currently consumes more - ** space than that limit allows for, truncate it now. There is no need - ** to sync the file following this operation. - */ - if( rc==SQLITE_OK && iLimit>0 ){ - i64 sz; - rc = sqlcipher_sqlite3OsFileSize(pPager->jfd, &sz); - if( rc==SQLITE_OK && sz>iLimit ){ - rc = sqlcipher_sqlite3OsTruncate(pPager->jfd, iLimit); - } - } +void sqlcipherPagerSetCodec( + Pager *pPager, + void *(*xCodec)(void*,void*,Pgno,int), + void (*xCodecSizeChng)(void*,int,int), + void (*xCodecFree)(void*), + void *pCodec +){ + if( pPager->xCodecFree ){ + pPager->xCodecFree(pPager->pCodec); + }else{ + pager_reset(pPager); } - return rc; + pPager->xCodec = pPager->memDb ? 0 : xCodec; + pPager->xCodecSizeChng = xCodecSizeChng; + pPager->xCodecFree = xCodecFree; + pPager->pCodec = pCodec; + setGetterMethod(pPager); + pagerReportSize(pPager); +} +void *sqlcipherPagerGetCodec(Pager *pPager){ + return pPager->pCodec; } /* -** The journal file must be open when this routine is called. A journal -** header (JOURNAL_HDR_SZ bytes) is written into the journal file at the -** current location. -** -** The format for the journal header is as follows: -** - 8 bytes: Magic identifying journal format. -** - 4 bytes: Number of records in journal, or -1 no-sync mode is on. -** - 4 bytes: Random number used for page hash. -** - 4 bytes: Initial database page count. -** - 4 bytes: Sector size used by the process that wrote this journal. -** - 4 bytes: Database page size. +** This function is called by the wal module when writing page content +** into the log file. ** -** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space. +** This function returns a pointer to a buffer containing the encrypted +** page content. If a malloc fails, this function may return NULL. */ -static int writeJournalHdr(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */ - u32 nHeader = (u32)pPager->pageSize;/* Size of buffer pointed to by zHeader */ - u32 nWrite; /* Bytes of header sector written */ - int ii; /* Loop counter */ +void *sqlcipherPagerCodec(PgHdr *pPg){ + void *aData = 0; + CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData); + return aData; +} +#endif /* SQLITE_HAS_CODEC */ +/* END SQLCIPHER */ - assert( isOpen(pPager->jfd) ); /* Journal file must be open. */ +#ifndef SQLITE_OMIT_AUTOVACUUM +/* +** Move the page pPg to location pgno in the file. +** +** There must be no references to the page previously located at +** pgno (which we call pPgOld) though that page is allowed to be +** in cache. If the page previously located at pgno is not already +** in the rollback journal, it is not put there by by this routine. +** +** References to the page pPg remain valid. Updating any +** meta-data associated with pPg (i.e. data stored in the nExtra bytes +** allocated along with the page) is the responsibility of the caller. +** +** A transaction must be active when this routine is called. It used to be +** required that a statement transaction was not active, but this restriction +** has been removed (CREATE INDEX needs to move a page when a statement +** transaction is active). +** +** If the fourth argument, isCommit, is non-zero, then this page is being +** moved as part of a database reorganization just before the transaction +** is being committed. In this case, it is guaranteed that the database page +** pPg refers to will not be written to again within this transaction. +** +** This function may return SQLITE_NOMEM or an IO error code if an error +** occurs. Otherwise, it returns SQLITE_OK. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ + PgHdr *pPgOld; /* The page being overwritten. */ + Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */ + int rc; /* Return code */ + Pgno origPgno; /* The original page number */ - if( nHeader>JOURNAL_HDR_SZ(pPager) ){ - nHeader = JOURNAL_HDR_SZ(pPager); - } + assert( pPg->nRef>0 ); + assert( pPager->eState==PAGER_WRITER_CACHEMOD + || pPager->eState==PAGER_WRITER_DBMOD + ); + assert( assert_pager_state(pPager) ); - /* If there are active savepoints and any of them were created - ** since the most recent journal header was written, update the - ** PagerSavepoint.iHdrOffset fields now. + /* In order to be able to rollback, an in-memory database must journal + ** the page we are moving from. */ - for(ii=0; iinSavepoint; ii++){ - if( pPager->aSavepoint[ii].iHdrOffset==0 ){ - pPager->aSavepoint[ii].iHdrOffset = pPager->journalOff; - } + assert( pPager->tempFile || !MEMDB ); + if( pPager->tempFile ){ + rc = sqlcipher_sqlite3PagerWrite(pPg); + if( rc ) return rc; } - pPager->journalHdr = pPager->journalOff = journalHdrOffset(pPager); - - /* - ** Write the nRec Field - the number of page records that follow this - ** journal header. Normally, zero is written to this value at this time. - ** After the records are added to the journal (and the journal synced, - ** if in full-sync mode), the zero is overwritten with the true number - ** of records (see syncJournal()). + /* If the page being moved is dirty and has not been saved by the latest + ** savepoint, then save the current contents of the page into the + ** sub-journal now. This is required to handle the following scenario: ** - ** A faster alternative is to write 0xFFFFFFFF to the nRec field. When - ** reading the journal this value tells SQLite to assume that the - ** rest of the journal file contains valid page records. This assumption - ** is dangerous, as if a failure occurred whilst writing to the journal - ** file it may contain some garbage data. There are two scenarios - ** where this risk can be ignored: + ** BEGIN; + ** + ** SAVEPOINT one; + ** + ** ROLLBACK TO one; ** - ** * When the pager is in no-sync mode. Corruption can follow a - ** power failure in this case anyway. + ** If page X were not written to the sub-journal here, it would not + ** be possible to restore its contents when the "ROLLBACK TO one" + ** statement were is processed. ** - ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees - ** that garbage data is never appended to the journal file. + ** subjournalPage() may need to allocate space to store pPg->pgno into + ** one or more savepoint bitvecs. This is the reason this function + ** may return SQLITE_NOMEM. */ - assert( isOpen(pPager->fd) || pPager->noSync ); - if( pPager->noSync || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY) - || (sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) + if( (pPg->flags & PGHDR_DIRTY)!=0 + && SQLITE_OK!=(rc = subjournalPageIfRequired(pPg)) ){ - memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); - put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff); - }else{ - memset(zHeader, 0, sizeof(aJournalMagic)+4); + return rc; } - /* The random check-hash initializer */ - sqlcipher_sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); - put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit); - /* The initial database size */ - put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbOrigSize); - /* The assumed sector size for this process */ - put32bits(&zHeader[sizeof(aJournalMagic)+12], pPager->sectorSize); - - /* The page size */ - put32bits(&zHeader[sizeof(aJournalMagic)+16], pPager->pageSize); - - /* Initializing the tail of the buffer is not necessary. Everything - ** works find if the following memset() is omitted. But initializing - ** the memory prevents valgrind from complaining, so we are willing to - ** take the performance hit. - */ - memset(&zHeader[sizeof(aJournalMagic)+20], 0, - nHeader-(sizeof(aJournalMagic)+20)); + PAGERTRACE(("MOVE %d page %d (needSync=%d) moves to %d\n", + PAGERID(pPager), pPg->pgno, (pPg->flags&PGHDR_NEED_SYNC)?1:0, pgno)); + IOTRACE(("MOVE %p %d %d\n", pPager, pPg->pgno, pgno)) - /* In theory, it is only necessary to write the 28 bytes that the - ** journal header consumes to the journal file here. Then increment the - ** Pager.journalOff variable by JOURNAL_HDR_SZ so that the next - ** record is written to the following sector (leaving a gap in the file - ** that will be implicitly filled in by the OS). - ** - ** However it has been discovered that on some systems this pattern can - ** be significantly slower than contiguously writing data to the file, - ** even if that means explicitly writing data to the block of - ** (JOURNAL_HDR_SZ - 28) bytes that will not be used. So that is what - ** is done. + /* If the journal needs to be sync()ed before page pPg->pgno can + ** be written to, store pPg->pgno in local variable needSyncPgno. ** - ** The loop is required here in case the sector-size is larger than the - ** database page size. Since the zHeader buffer is only Pager.pageSize - ** bytes in size, more than one call to sqlcipher_sqlite3OsWrite() may be required - ** to populate the entire journal header sector. - */ - for(nWrite=0; rc==SQLITE_OK&&nWritejournalHdr, nHeader)) - rc = sqlcipher_sqlite3OsWrite(pPager->jfd, zHeader, nHeader, pPager->journalOff); - assert( pPager->journalHdr <= pPager->journalOff ); - pPager->journalOff += nHeader; - } - - return rc; -} - -/* -** The journal file must be open when this is called. A journal header file -** (JOURNAL_HDR_SZ bytes) is read from the current location in the journal -** file. The current location in the journal file is given by -** pPager->journalOff. See comments above function writeJournalHdr() for -** a description of the journal header format. -** -** If the header is read successfully, *pNRec is set to the number of -** page records following this header and *pDbSize is set to the size of the -** database before the transaction began, in pages. Also, pPager->cksumInit -** is set to the value read from the journal header. SQLITE_OK is returned -** in this case. -** -** If the journal header file appears to be corrupted, SQLITE_DONE is -** returned and *pNRec and *PDbSize are undefined. If JOURNAL_HDR_SZ bytes -** cannot be read from the journal file an error code is returned. -*/ -static int readJournalHdr( - Pager *pPager, /* Pager object */ - int isHot, - i64 journalSize, /* Size of the open journal file in bytes */ - u32 *pNRec, /* OUT: Value read from the nRec field */ - u32 *pDbSize /* OUT: Value of original database size field */ -){ - int rc; /* Return code */ - unsigned char aMagic[8]; /* A buffer to hold the magic header */ - i64 iHdrOff; /* Offset of journal header being read */ - - assert( isOpen(pPager->jfd) ); /* Journal file must be open. */ - - /* Advance Pager.journalOff to the start of the next sector. If the - ** journal file is too small for there to be a header stored at this - ** point, return SQLITE_DONE. + ** If the isCommit flag is set, there is no need to remember that + ** the journal needs to be sync()ed before database page pPg->pgno + ** can be written to. The caller has already promised not to write to it. */ - pPager->journalOff = journalHdrOffset(pPager); - if( pPager->journalOff+JOURNAL_HDR_SZ(pPager) > journalSize ){ - return SQLITE_DONE; + if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){ + needSyncPgno = pPg->pgno; + assert( pPager->journalMode==PAGER_JOURNALMODE_OFF || + pageInJournal(pPager, pPg) || pPg->pgno>pPager->dbOrigSize ); + assert( pPg->flags&PGHDR_DIRTY ); } - iHdrOff = pPager->journalOff; - /* Read in the first 8 bytes of the journal header. If they do not match - ** the magic string found at the start of each journal header, return - ** SQLITE_DONE. If an IO error occurs, return an error code. Otherwise, - ** proceed. + /* If the cache contains a page with page-number pgno, remove it + ** from its hash chain. Also, if the PGHDR_NEED_SYNC flag was set for + ** page pgno before the 'move' operation, it needs to be retained + ** for the page moved there. */ - if( isHot || iHdrOff!=pPager->journalHdr ){ - rc = sqlcipher_sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), iHdrOff); - if( rc ){ - return rc; + pPg->flags &= ~PGHDR_NEED_SYNC; + pPgOld = sqlcipher_sqlite3PagerLookup(pPager, pgno); + assert( !pPgOld || pPgOld->nRef==1 || CORRUPT_DB ); + if( pPgOld ){ + if( NEVER(pPgOld->nRef>1) ){ + sqlcipher_sqlite3PagerUnrefNotNull(pPgOld); + return SQLITE_CORRUPT_BKPT; } - if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){ - return SQLITE_DONE; + pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); + if( pPager->tempFile ){ + /* Do not discard pages from an in-memory database since we might + ** need to rollback later. Just move the page out of the way. */ + sqlcipher_sqlite3PcacheMove(pPgOld, pPager->dbSize+1); + }else{ + sqlcipher_sqlite3PcacheDrop(pPgOld); } } - /* Read the first three 32-bit fields of the journal header: The nRec - ** field, the checksum-initializer and the database size at the start - ** of the transaction. Return an error code if anything goes wrong. + origPgno = pPg->pgno; + sqlcipher_sqlite3PcacheMove(pPg, pgno); + sqlcipher_sqlite3PcacheMakeDirty(pPg); + + /* For an in-memory database, make sure the original page continues + ** to exist, in case the transaction needs to roll back. Use pPgOld + ** as the original page since it has already been allocated. */ - if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+8, pNRec)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+12, &pPager->cksumInit)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+16, pDbSize)) - ){ - return rc; + if( pPager->tempFile && pPgOld ){ + sqlcipher_sqlite3PcacheMove(pPgOld, origPgno); + sqlcipher_sqlite3PagerUnrefNotNull(pPgOld); } - if( pPager->journalOff==0 ){ - u32 iPageSize; /* Page-size field of journal header */ - u32 iSectorSize; /* Sector-size field of journal header */ - - /* Read the page-size and sector-size journal header fields. */ - if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+20, &iSectorSize)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+24, &iPageSize)) - ){ - return rc; - } - - /* Versions of SQLite prior to 3.5.8 set the page-size field of the - ** journal header to zero. In this case, assume that the Pager.pageSize - ** variable is already set to the correct page size. + if( needSyncPgno ){ + /* If needSyncPgno is non-zero, then the journal file needs to be + ** sync()ed before any data is written to database file page needSyncPgno. + ** Currently, no such page exists in the page-cache and the + ** "is journaled" bitvec flag has been set. This needs to be remedied by + ** loading the page into the pager-cache and setting the PGHDR_NEED_SYNC + ** flag. + ** + ** If the attempt to load the page into the page-cache fails, (due + ** to a malloc() or IO failure), clear the bit in the pInJournal[] + ** array. Otherwise, if the page is loaded and written again in + ** this transaction, it may be written to the database file before + ** it is synced into the journal file. This way, it may end up in + ** the journal file twice, but that is not a problem. */ - if( iPageSize==0 ){ - iPageSize = pPager->pageSize; + PgHdr *pPgHdr; + rc = sqlcipher_sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr, 0); + if( rc!=SQLITE_OK ){ + if( needSyncPgno<=pPager->dbOrigSize ){ + assert( pPager->pTmpSpace!=0 ); + sqlcipher_sqlite3BitvecClear(pPager->pInJournal, needSyncPgno, pPager->pTmpSpace); + } + return rc; } + pPgHdr->flags |= PGHDR_NEED_SYNC; + sqlcipher_sqlite3PcacheMakeDirty(pPgHdr); + sqlcipher_sqlite3PagerUnrefNotNull(pPgHdr); + } - /* Check that the values read from the page-size and sector-size fields - ** are within range. To be 'in range', both values need to be a power - ** of two greater than or equal to 512 or 32, and not greater than their - ** respective compile time maximum limits. - */ - if( iPageSize<512 || iSectorSize<32 - || iPageSize>SQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE - || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0 - ){ - /* If the either the page-size or sector-size in the journal-header is - ** invalid, then the process that wrote the journal-header must have - ** crashed before the header was synced. In this case stop reading - ** the journal file here. - */ - return SQLITE_DONE; - } + return SQLITE_OK; +} +#endif - /* Update the page-size to match the value read from the journal. - ** Use a testcase() macro to make sure that malloc failure within - ** PagerSetPagesize() is tested. - */ - rc = sqlcipher_sqlite3PagerSetPagesize(pPager, &iPageSize, -1); - testcase( rc!=SQLITE_OK ); +/* +** The page handle passed as the first argument refers to a dirty page +** with a page number other than iNew. This function changes the page's +** page number to iNew and sets the value of the PgHdr.flags field to +** the value passed as the third parameter. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PagerRekey(DbPage *pPg, Pgno iNew, u16 flags){ + assert( pPg->pgno!=iNew ); + pPg->flags = flags; + sqlcipher_sqlite3PcacheMove(pPg, iNew); +} - /* Update the assumed sector-size to match the value used by - ** the process that created this journal. If this journal was - ** created by a process other than this one, then this routine - ** is being called from within pager_playback(). The local value - ** of Pager.sectorSize is restored at the end of that routine. - */ - pPager->sectorSize = iSectorSize; - } +/* +** Return a pointer to the data for the specified page. +*/ +SQLITE_PRIVATE void *sqlcipher_sqlite3PagerGetData(DbPage *pPg){ + assert( pPg->nRef>0 || pPg->pPager->memDb ); + return pPg->pData; +} - pPager->journalOff += JOURNAL_HDR_SZ(pPager); - return rc; +/* +** Return a pointer to the Pager.nExtra bytes of "extra" space +** allocated along with the specified page. +*/ +SQLITE_PRIVATE void *sqlcipher_sqlite3PagerGetExtra(DbPage *pPg){ + return pPg->pExtra; } +/* +** Get/set the locking-mode for this pager. Parameter eMode must be one +** of PAGER_LOCKINGMODE_QUERY, PAGER_LOCKINGMODE_NORMAL or +** PAGER_LOCKINGMODE_EXCLUSIVE. If the parameter is not _QUERY, then +** the locking-mode is set to the value specified. +** +** The returned value is either PAGER_LOCKINGMODE_NORMAL or +** PAGER_LOCKINGMODE_EXCLUSIVE, indicating the current (possibly updated) +** locking-mode. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerLockingMode(Pager *pPager, int eMode){ + assert( eMode==PAGER_LOCKINGMODE_QUERY + || eMode==PAGER_LOCKINGMODE_NORMAL + || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); + assert( PAGER_LOCKINGMODE_QUERY<0 ); + assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 ); + assert( pPager->exclusiveMode || 0==sqlcipher_sqlite3WalHeapMemory(pPager->pWal) ); + if( eMode>=0 && !pPager->tempFile && !sqlcipher_sqlite3WalHeapMemory(pPager->pWal) ){ + pPager->exclusiveMode = (u8)eMode; + } + return (int)pPager->exclusiveMode; +} /* -** Write the supplied super-journal name into the journal file for pager -** pPager at the current location. The super-journal name must be the last -** thing written to a journal file. If the pager is in full-sync mode, the -** journal file descriptor is advanced to the next sector boundary before -** anything is written. The format is: +** Set the journal-mode for this pager. Parameter eMode must be one of: ** -** + 4 bytes: PAGER_MJ_PGNO. -** + N bytes: super-journal filename in utf-8. -** + 4 bytes: N (length of super-journal name in bytes, no nul-terminator). -** + 4 bytes: super-journal name checksum. -** + 8 bytes: aJournalMagic[]. +** PAGER_JOURNALMODE_DELETE +** PAGER_JOURNALMODE_TRUNCATE +** PAGER_JOURNALMODE_PERSIST +** PAGER_JOURNALMODE_OFF +** PAGER_JOURNALMODE_MEMORY +** PAGER_JOURNALMODE_WAL ** -** The super-journal page checksum is the sum of the bytes in thesuper-journal -** name, where each byte is interpreted as a signed 8-bit integer. +** The journalmode is set to the value specified if the change is allowed. +** The change may be disallowed for the following reasons: ** -** If zSuper is a NULL pointer (occurs for a single database transaction), -** this call is a no-op. +** * An in-memory database can only have its journal_mode set to _OFF +** or _MEMORY. +** +** * Temporary databases cannot have _WAL journalmode. +** +** The returned indicate the current (possibly updated) journal-mode. */ -static int writeSuperJournal(Pager *pPager, const char *zSuper){ - int rc; /* Return code */ - int nSuper; /* Length of string zSuper */ - i64 iHdrOff; /* Offset of header in journal file */ - i64 jrnlSize; /* Size of journal file on disk */ - u32 cksum = 0; /* Checksum of string zSuper */ - - assert( pPager->setSuper==0 ); - assert( !pagerUseWal(pPager) ); - - if( !zSuper - || pPager->journalMode==PAGER_JOURNALMODE_MEMORY - || !isOpen(pPager->jfd) - ){ - return SQLITE_OK; - } - pPager->setSuper = 1; - assert( pPager->journalHdr <= pPager->journalOff ); +SQLITE_PRIVATE int sqlcipher_sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ + u8 eOld = pPager->journalMode; /* Prior journalmode */ - /* Calculate the length in bytes and the checksum of zSuper */ - for(nSuper=0; zSuper[nSuper]; nSuper++){ - cksum += zSuper[nSuper]; - } + /* The eMode parameter is always valid */ + assert( eMode==PAGER_JOURNALMODE_DELETE /* 0 */ + || eMode==PAGER_JOURNALMODE_PERSIST /* 1 */ + || eMode==PAGER_JOURNALMODE_OFF /* 2 */ + || eMode==PAGER_JOURNALMODE_TRUNCATE /* 3 */ + || eMode==PAGER_JOURNALMODE_MEMORY /* 4 */ + || eMode==PAGER_JOURNALMODE_WAL /* 5 */ ); - /* If in full-sync mode, advance to the next disk sector before writing - ** the super-journal name. This is in case the previous page written to - ** the journal has already been synced. + /* This routine is only called from the OP_JournalMode opcode, and + ** the logic there will never allow a temporary file to be changed + ** to WAL mode. */ - if( pPager->fullSync ){ - pPager->journalOff = journalHdrOffset(pPager); - } - iHdrOff = pPager->journalOff; + assert( pPager->tempFile==0 || eMode!=PAGER_JOURNALMODE_WAL ); - /* Write the super-journal data to the end of the journal file. If - ** an error occurs, return the error code to the caller. + /* Do allow the journalmode of an in-memory database to be set to + ** anything other than MEMORY or OFF */ - if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_MJ_PGNO(pPager)))) - || (0 != (rc = sqlcipher_sqlite3OsWrite(pPager->jfd, zSuper, nSuper, iHdrOff+4))) - || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper, nSuper))) - || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper+4, cksum))) - || (0 != (rc = sqlcipher_sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, - iHdrOff+4+nSuper+8))) - ){ - return rc; + if( MEMDB ){ + assert( eOld==PAGER_JOURNALMODE_MEMORY || eOld==PAGER_JOURNALMODE_OFF ); + if( eMode!=PAGER_JOURNALMODE_MEMORY && eMode!=PAGER_JOURNALMODE_OFF ){ + eMode = eOld; + } } - pPager->journalOff += (nSuper+20); - /* If the pager is in peristent-journal mode, then the physical - ** journal-file may extend past the end of the super-journal name - ** and 8 bytes of magic data just written to the file. This is - ** dangerous because the code to rollback a hot-journal file - ** will not be able to find the super-journal name to determine - ** whether or not the journal is hot. - ** - ** Easiest thing to do in this scenario is to truncate the journal - ** file to the required size. - */ - if( SQLITE_OK==(rc = sqlcipher_sqlite3OsFileSize(pPager->jfd, &jrnlSize)) - && jrnlSize>pPager->journalOff - ){ - rc = sqlcipher_sqlite3OsTruncate(pPager->jfd, pPager->journalOff); + if( eMode!=eOld ){ + + /* Change the journal mode. */ + assert( pPager->eState!=PAGER_ERROR ); + pPager->journalMode = (u8)eMode; + + /* When transistioning from TRUNCATE or PERSIST to any other journal + ** mode except WAL, unless the pager is in locking_mode=exclusive mode, + ** delete the journal file. + */ + assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); + assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); + assert( (PAGER_JOURNALMODE_DELETE & 5)==0 ); + assert( (PAGER_JOURNALMODE_MEMORY & 5)==4 ); + assert( (PAGER_JOURNALMODE_OFF & 5)==0 ); + assert( (PAGER_JOURNALMODE_WAL & 5)==5 ); + + assert( isOpen(pPager->fd) || pPager->exclusiveMode ); + if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){ + /* In this case we would like to delete the journal file. If it is + ** not possible, then that is not a problem. Deleting the journal file + ** here is an optimization only. + ** + ** Before deleting the journal file, obtain a RESERVED lock on the + ** database file. This ensures that the journal file is not deleted + ** while it is in use by some other client. + */ + sqlcipher_sqlite3OsClose(pPager->jfd); + if( pPager->eLock>=RESERVED_LOCK ){ + sqlcipher_sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); + }else{ + int rc = SQLITE_OK; + int state = pPager->eState; + assert( state==PAGER_OPEN || state==PAGER_READER ); + if( state==PAGER_OPEN ){ + rc = sqlcipher_sqlite3PagerSharedLock(pPager); + } + if( pPager->eState==PAGER_READER ){ + assert( rc==SQLITE_OK ); + rc = pagerLockDb(pPager, RESERVED_LOCK); + } + if( rc==SQLITE_OK ){ + sqlcipher_sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); + } + if( rc==SQLITE_OK && state==PAGER_READER ){ + pagerUnlockDb(pPager, SHARED_LOCK); + }else if( state==PAGER_OPEN ){ + pager_unlock(pPager); + } + assert( state==pPager->eState ); + } + }else if( eMode==PAGER_JOURNALMODE_OFF ){ + sqlcipher_sqlite3OsClose(pPager->jfd); + } } - return rc; + + /* Return the new journal mode */ + return (int)pPager->journalMode; } /* -** Discard the entire contents of the in-memory page-cache. +** Return the current journal mode. */ -static void pager_reset(Pager *pPager){ - pPager->iDataVersion++; - sqlcipher_sqlite3BackupRestart(pPager->pBackup); - sqlcipher_sqlite3PcacheClear(pPager->pPCache); +SQLITE_PRIVATE int sqlcipher_sqlite3PagerGetJournalMode(Pager *pPager){ + return (int)pPager->journalMode; } /* -** Return the pPager->iDataVersion value +** Return TRUE if the pager is in a state where it is OK to change the +** journalmode. Journalmode changes can only happen when the database +** is unmodified. */ -SQLITE_PRIVATE u32 sqlcipher_sqlite3PagerDataVersion(Pager *pPager){ - return pPager->iDataVersion; +SQLITE_PRIVATE int sqlcipher_sqlite3PagerOkToChangeJournalMode(Pager *pPager){ + assert( assert_pager_state(pPager) ); + if( pPager->eState>=PAGER_WRITER_CACHEMOD ) return 0; + if( isOpen(pPager->jfd) && pPager->journalOff>0 ) return 0; + return 1; } /* -** Free all structures in the Pager.aSavepoint[] array and set both -** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal -** if it is open and the pager is not in exclusive mode. +** Get/set the size-limit used for persistent journal files. +** +** Setting the size limit to -1 means no limit is enforced. +** An attempt to set a limit smaller than -1 is a no-op. */ -static void releaseAllSavepoints(Pager *pPager){ - int ii; /* Iterator for looping through Pager.aSavepoint */ - for(ii=0; iinSavepoint; ii++){ - sqlcipher_sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint); - } - if( !pPager->exclusiveMode || sqlcipher_sqlite3JournalIsInMemory(pPager->sjfd) ){ - sqlcipher_sqlite3OsClose(pPager->sjfd); +SQLITE_PRIVATE i64 sqlcipher_sqlite3PagerJournalSizeLimit(Pager *pPager, i64 iLimit){ + if( iLimit>=-1 ){ + pPager->journalSizeLimit = iLimit; + sqlcipher_sqlite3WalLimit(pPager->pWal, iLimit); } - sqlcipher_sqlite3_free(pPager->aSavepoint); - pPager->aSavepoint = 0; - pPager->nSavepoint = 0; - pPager->nSubRec = 0; + return pPager->journalSizeLimit; } /* -** Set the bit number pgno in the PagerSavepoint.pInSavepoint -** bitvecs of all open savepoints. Return SQLITE_OK if successful -** or SQLITE_NOMEM if a malloc failure occurs. +** Return a pointer to the pPager->pBackup variable. The backup module +** in backup.c maintains the content of this variable. This module +** uses it opaquely as an argument to sqlcipher_sqlite3BackupRestart() and +** sqlcipher_sqlite3BackupUpdate() only. */ -static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){ - int ii; /* Loop counter */ - int rc = SQLITE_OK; /* Result code */ +SQLITE_PRIVATE sqlcipher_sqlite3_backup **sqlcipher_sqlite3PagerBackupPtr(Pager *pPager){ + return &pPager->pBackup; +} - for(ii=0; iinSavepoint; ii++){ - PagerSavepoint *p = &pPager->aSavepoint[ii]; - if( pgno<=p->nOrig ){ - rc |= sqlcipher_sqlite3BitvecSet(p->pInSavepoint, pgno); - testcase( rc==SQLITE_NOMEM ); - assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); - } +#ifndef SQLITE_OMIT_VACUUM +/* +** Unless this is an in-memory or temporary database, clear the pager cache. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PagerClearCache(Pager *pPager){ + assert( MEMDB==0 || pPager->tempFile ); + if( pPager->tempFile==0 ) pager_reset(pPager); +} +#endif + + +#ifndef SQLITE_OMIT_WAL +/* +** This function is called when the user invokes "PRAGMA wal_checkpoint", +** "PRAGMA wal_blocking_checkpoint" or calls the sqlcipher_sqlite3_wal_checkpoint() +** or wal_blocking_checkpoint() API functions. +** +** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerCheckpoint( + Pager *pPager, /* Checkpoint on this pager */ + sqlcipher_sqlite3 *db, /* Db handle used to check for interrupts */ + int eMode, /* Type of checkpoint */ + int *pnLog, /* OUT: Final number of frames in log */ + int *pnCkpt /* OUT: Final number of checkpointed frames */ +){ + int rc = SQLITE_OK; + if( pPager->pWal==0 && pPager->journalMode==PAGER_JOURNALMODE_WAL ){ + /* This only happens when a database file is zero bytes in size opened and + ** then "PRAGMA journal_mode=WAL" is run and then sqlcipher_sqlite3_wal_checkpoint() + ** is invoked without any intervening transactions. We need to start + ** a transaction to initialize pWal. The PRAGMA table_list statement is + ** used for this since it starts transactions on every database file, + ** including all ATTACHed databases. This seems expensive for a single + ** sqlcipher_sqlite3_wal_checkpoint() call, but it happens very rarely. + ** https://sqlite.org/forum/forumpost/fd0f19d229156939 + */ + sqlcipher_sqlite3_exec(db, "PRAGMA table_list",0,0,0); + } + if( pPager->pWal ){ + rc = sqlcipher_sqlite3WalCheckpoint(pPager->pWal, db, eMode, + (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler), + pPager->pBusyHandlerArg, + pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, + pnLog, pnCkpt + ); } return rc; } +SQLITE_PRIVATE int sqlcipher_sqlite3PagerWalCallback(Pager *pPager){ + return sqlcipher_sqlite3WalCallback(pPager->pWal); +} + /* -** This function is a no-op if the pager is in exclusive mode and not -** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN -** state. -** -** If the pager is not in exclusive-access mode, the database file is -** completely unlocked. If the file is unlocked and the file-system does -** not exhibit the UNDELETABLE_WHEN_OPEN property, the journal file is -** closed (if it is open). -** -** If the pager is in ERROR state when this function is called, the -** contents of the pager cache are discarded before switching back to -** the OPEN state. Regardless of whether the pager is in exclusive-mode -** or not, any journal file left in the file-system will be treated -** as a hot-journal and rolled back the next time a read-transaction -** is opened (by this or by any other connection). +** Return true if the underlying VFS for the given pager supports the +** primitives necessary for write-ahead logging. */ -static void pager_unlock(Pager *pPager){ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerWalSupported(Pager *pPager){ + const sqlcipher_sqlite3_io_methods *pMethods = pPager->fd->pMethods; + if( pPager->noLock ) return 0; + return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap); +} - assert( pPager->eState==PAGER_READER - || pPager->eState==PAGER_OPEN - || pPager->eState==PAGER_ERROR - ); +/* +** Attempt to take an exclusive lock on the database file. If a PENDING lock +** is obtained instead, immediately release it. +*/ +static int pagerExclusiveLock(Pager *pPager){ + int rc; /* Return code */ - sqlcipher_sqlite3BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; - releaseAllSavepoints(pPager); + assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); + rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); + if( rc!=SQLITE_OK ){ + /* If the attempt to grab the exclusive lock failed, release the + ** pending lock that may have been obtained instead. */ + pagerUnlockDb(pPager, SHARED_LOCK); + } - if( pagerUseWal(pPager) ){ - assert( !isOpen(pPager->jfd) ); - sqlcipher_sqlite3WalEndReadTransaction(pPager->pWal); - pPager->eState = PAGER_OPEN; - }else if( !pPager->exclusiveMode ){ - int rc; /* Error code returned by pagerUnlockDb() */ - int iDc = isOpen(pPager->fd)?sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd):0; + return rc; +} - /* If the operating system support deletion of open files, then - ** close the journal file when dropping the database lock. Otherwise - ** another connection with journal_mode=delete might delete the file - ** out from under us. - */ - assert( (PAGER_JOURNALMODE_MEMORY & 5)!=1 ); - assert( (PAGER_JOURNALMODE_OFF & 5)!=1 ); - assert( (PAGER_JOURNALMODE_WAL & 5)!=1 ); - assert( (PAGER_JOURNALMODE_DELETE & 5)!=1 ); - assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); - assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); - if( 0==(iDc & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN) - || 1!=(pPager->journalMode & 5) - ){ - sqlcipher_sqlite3OsClose(pPager->jfd); - } +/* +** Call sqlcipher_sqlite3WalOpen() to open the WAL handle. If the pager is in +** exclusive-locking mode when this function is called, take an EXCLUSIVE +** lock on the database file and use heap-memory to store the wal-index +** in. Otherwise, use the normal shared-memory. +*/ +static int pagerOpenWal(Pager *pPager){ + int rc = SQLITE_OK; - /* If the pager is in the ERROR state and the call to unlock the database - ** file fails, set the current lock to UNKNOWN_LOCK. See the comment - ** above the #define for UNKNOWN_LOCK for an explanation of why this - ** is necessary. - */ - rc = pagerUnlockDb(pPager, NO_LOCK); - if( rc!=SQLITE_OK && pPager->eState==PAGER_ERROR ){ - pPager->eLock = UNKNOWN_LOCK; - } + assert( pPager->pWal==0 && pPager->tempFile==0 ); + assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); - /* The pager state may be changed from PAGER_ERROR to PAGER_OPEN here - ** without clearing the error code. This is intentional - the error - ** code is cleared and the cache reset in the block below. - */ - assert( pPager->errCode || pPager->eState!=PAGER_ERROR ); - pPager->eState = PAGER_OPEN; + /* If the pager is already in exclusive-mode, the WAL module will use + ** heap-memory for the wal-index instead of the VFS shared-memory + ** implementation. Take the exclusive lock now, before opening the WAL + ** file, to make sure this is safe. + */ + if( pPager->exclusiveMode ){ + rc = pagerExclusiveLock(pPager); } - /* If Pager.errCode is set, the contents of the pager cache cannot be - ** trusted. Now that there are no outstanding references to the pager, - ** it can safely move back to PAGER_OPEN state. This happens in both - ** normal and exclusive-locking mode. + /* Open the connection to the log file. If this operation fails, + ** (e.g. due to malloc() failure), return an error code. */ - assert( pPager->errCode==SQLITE_OK || !MEMDB ); - if( pPager->errCode ){ - if( pPager->tempFile==0 ){ - pager_reset(pPager); - pPager->changeCountDone = 0; - pPager->eState = PAGER_OPEN; - }else{ - pPager->eState = (isOpen(pPager->jfd) ? PAGER_OPEN : PAGER_READER); - } - if( USEFETCH(pPager) ) sqlcipher_sqlite3OsUnfetch(pPager->fd, 0, 0); - pPager->errCode = SQLITE_OK; - setGetterMethod(pPager); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3WalOpen(pPager->pVfs, + pPager->fd, pPager->zWal, pPager->exclusiveMode, + pPager->journalSizeLimit, &pPager->pWal + ); } + pagerFixMaplimit(pPager); - pPager->journalOff = 0; - pPager->journalHdr = 0; - pPager->setSuper = 0; + return rc; } + /* -** This function is called whenever an IOERR or FULL error that requires -** the pager to transition into the ERROR state may ahve occurred. -** The first argument is a pointer to the pager structure, the second -** the error-code about to be returned by a pager API function. The -** value returned is a copy of the second argument to this function. +** The caller must be holding a SHARED lock on the database file to call +** this function. ** -** If the second argument is SQLITE_FULL, SQLITE_IOERR or one of the -** IOERR sub-codes, the pager enters the ERROR state and the error code -** is stored in Pager.errCode. While the pager remains in the ERROR state, -** all major API calls on the Pager will immediately return Pager.errCode. +** If the pager passed as the first argument is open on a real database +** file (not a temp file or an in-memory database), and the WAL file +** is not already open, make an attempt to open it now. If successful, +** return SQLITE_OK. If an error occurs or the VFS used by the pager does +** not support the xShmXXX() methods, return an error code. *pbOpen is +** not modified in either case. ** -** The ERROR state indicates that the contents of the pager-cache -** cannot be trusted. This state can be cleared by completely discarding -** the contents of the pager-cache. If a transaction was active when -** the persistent error occurred, then the rollback journal may need -** to be replayed to restore the contents of the database file (as if -** it were a hot-journal). +** If the pager is open on a temp-file (or in-memory database), or if +** the WAL file is already open, set *pbOpen to 1 and return SQLITE_OK +** without doing anything. */ -static int pager_error(Pager *pPager, int rc){ - int rc2 = rc & 0xff; - assert( rc==SQLITE_OK || !MEMDB ); - assert( - pPager->errCode==SQLITE_FULL || - pPager->errCode==SQLITE_OK || - (pPager->errCode & 0xff)==SQLITE_IOERR - ); - if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){ - pPager->errCode = rc; - pPager->eState = PAGER_ERROR; - setGetterMethod(pPager); +SQLITE_PRIVATE int sqlcipher_sqlite3PagerOpenWal( + Pager *pPager, /* Pager object */ + int *pbOpen /* OUT: Set to true if call is a no-op */ +){ + int rc = SQLITE_OK; /* Return code */ + + assert( assert_pager_state(pPager) ); + assert( pPager->eState==PAGER_OPEN || pbOpen ); + assert( pPager->eState==PAGER_READER || !pbOpen ); + assert( pbOpen==0 || *pbOpen==0 ); + assert( pbOpen!=0 || (!pPager->tempFile && !pPager->pWal) ); + + if( !pPager->tempFile && !pPager->pWal ){ + if( !sqlcipher_sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; + + /* Close any rollback journal previously open */ + sqlcipher_sqlite3OsClose(pPager->jfd); + + rc = pagerOpenWal(pPager); + if( rc==SQLITE_OK ){ + pPager->journalMode = PAGER_JOURNALMODE_WAL; + pPager->eState = PAGER_OPEN; + } + }else{ + *pbOpen = 1; } + return rc; } -static int pager_truncate(Pager *pPager, Pgno nPage); - /* -** The write transaction open on pPager is being committed (bCommit==1) -** or rolled back (bCommit==0). -** -** Return TRUE if and only if all dirty pages should be flushed to disk. -** -** Rules: -** -** * For non-TEMP databases, always sync to disk. This is necessary -** for transactions to be durable. +** This function is called to close the connection to the log file prior +** to switching from WAL to rollback mode. ** -** * Sync TEMP database only on a COMMIT (not a ROLLBACK) when the backing -** file has been created already (via a spill on pagerStress()) and -** when the number of dirty pages in memory exceeds 25% of the total -** cache size. +** Before closing the log file, this function attempts to take an +** EXCLUSIVE lock on the database file. If this cannot be obtained, an +** error (SQLITE_BUSY) is returned and the log connection is not closed. +** If successful, the EXCLUSIVE lock is not released before returning. */ -static int pagerFlushOnCommit(Pager *pPager, int bCommit){ - if( pPager->tempFile==0 ) return 1; - if( !bCommit ) return 0; - if( !isOpen(pPager->fd) ) return 0; - return (sqlcipher_sqlite3PCachePercentDirty(pPager->pPCache)>=25); -} +SQLITE_PRIVATE int sqlcipher_sqlite3PagerCloseWal(Pager *pPager, sqlcipher_sqlite3 *db){ + int rc = SQLITE_OK; -/* -** This routine ends a transaction. A transaction is usually ended by -** either a COMMIT or a ROLLBACK operation. This routine may be called -** after rollback of a hot-journal, or if an error occurs while opening -** the journal file or writing the very first journal-header of a -** database transaction. -** -** This routine is never called in PAGER_ERROR state. If it is called -** in PAGER_NONE or PAGER_SHARED state and the lock held is less -** exclusive than a RESERVED lock, it is a no-op. -** -** Otherwise, any active savepoints are released. -** -** If the journal file is open, then it is "finalized". Once a journal -** file has been finalized it is not possible to use it to roll back a -** transaction. Nor will it be considered to be a hot-journal by this -** or any other database connection. Exactly how a journal is finalized -** depends on whether or not the pager is running in exclusive mode and -** the current journal-mode (Pager.journalMode value), as follows: -** -** journalMode==MEMORY -** Journal file descriptor is simply closed. This destroys an -** in-memory journal. -** -** journalMode==TRUNCATE -** Journal file is truncated to zero bytes in size. -** -** journalMode==PERSIST -** The first 28 bytes of the journal file are zeroed. This invalidates -** the first journal header in the file, and hence the entire journal -** file. An invalid journal file cannot be rolled back. -** -** journalMode==DELETE -** The journal file is closed and deleted using sqlcipher_sqlite3OsDelete(). -** -** If the pager is running in exclusive mode, this method of finalizing -** the journal file is never used. Instead, if the journalMode is -** DELETE and the pager is in exclusive mode, the method described under -** journalMode==PERSIST is used instead. -** -** After the journal is finalized, the pager moves to PAGER_READER state. -** If running in non-exclusive rollback mode, the lock on the file is -** downgraded to a SHARED_LOCK. -** -** SQLITE_OK is returned if no error occurs. If an error occurs during -** any of the IO operations to finalize the journal file or unlock the -** database then the IO error code is returned to the user. If the -** operation to finalize the journal file fails, then the code still -** tries to unlock the database file if not in exclusive mode. If the -** unlock operation fails as well, then the first error code related -** to the first error encountered (the journal finalization one) is -** returned. -*/ -static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){ - int rc = SQLITE_OK; /* Error code from journal finalization operation */ - int rc2 = SQLITE_OK; /* Error code from db file unlock operation */ + assert( pPager->journalMode==PAGER_JOURNALMODE_WAL ); - /* Do nothing if the pager does not have an open write transaction - ** or at least a RESERVED lock. This function may be called when there - ** is no write-transaction active but a RESERVED or greater lock is - ** held under two circumstances: - ** - ** 1. After a successful hot-journal rollback, it is called with - ** eState==PAGER_NONE and eLock==EXCLUSIVE_LOCK. - ** - ** 2. If a connection with locking_mode=exclusive holding an EXCLUSIVE - ** lock switches back to locking_mode=normal and then executes a - ** read-transaction, this function is called with eState==PAGER_READER - ** and eLock==EXCLUSIVE_LOCK when the read-transaction is closed. + /* If the log file is not already open, but does exist in the file-system, + ** it may need to be checkpointed before the connection can switch to + ** rollback mode. Open it now so this can happen. */ - assert( assert_pager_state(pPager) ); - assert( pPager->eState!=PAGER_ERROR ); - if( pPager->eStateeLockjfd) || pPager->pInJournal==0 - || (sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_BATCH_ATOMIC) - ); - if( isOpen(pPager->jfd) ){ - assert( !pagerUseWal(pPager) ); - - /* Finalize the journal file. */ - if( sqlcipher_sqlite3JournalIsInMemory(pPager->jfd) ){ - /* assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); */ - sqlcipher_sqlite3OsClose(pPager->jfd); - }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ - if( pPager->journalOff==0 ){ - rc = SQLITE_OK; - }else{ - rc = sqlcipher_sqlite3OsTruncate(pPager->jfd, 0); - if( rc==SQLITE_OK && pPager->fullSync ){ - /* Make sure the new file size is written into the inode right away. - ** Otherwise the journal might resurrect following a power loss and - ** cause the last transaction to roll back. See - ** https://bugzilla.mozilla.org/show_bug.cgi?id=1072773 - */ - rc = sqlcipher_sqlite3OsSync(pPager->jfd, pPager->syncFlags); - } - } - pPager->journalOff = 0; - }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST - || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) - ){ - rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile); - pPager->journalOff = 0; - }else{ - /* This branch may be executed with Pager.journalMode==MEMORY if - ** a hot-journal was just rolled back. In this case the journal - ** file should be closed and deleted. If this connection writes to - ** the database file, it will do so using an in-memory journal. - */ - int bDelete = !pPager->tempFile; - assert( sqlcipher_sqlite3JournalIsInMemory(pPager->jfd)==0 ); - assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE - || pPager->journalMode==PAGER_JOURNALMODE_MEMORY - || pPager->journalMode==PAGER_JOURNALMODE_WAL + if( !pPager->pWal ){ + int logexists = 0; + rc = pagerLockDb(pPager, SHARED_LOCK); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3OsAccess( + pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists ); - sqlcipher_sqlite3OsClose(pPager->jfd); - if( bDelete ){ - rc = sqlcipher_sqlite3OsDelete(pPager->pVfs, pPager->zJournal, pPager->extraSync); - } } - } - -#ifdef SQLITE_CHECK_PAGES - sqlcipher_sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash); - if( pPager->dbSize==0 && sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)>0 ){ - PgHdr *p = sqlcipher_sqlite3PagerLookup(pPager, 1); - if( p ){ - p->pageHash = 0; - sqlcipher_sqlite3PagerUnrefNotNull(p); + if( rc==SQLITE_OK && logexists ){ + rc = pagerOpenWal(pPager); } } -#endif - sqlcipher_sqlite3BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; - pPager->nRec = 0; - if( rc==SQLITE_OK ){ - if( MEMDB || pagerFlushOnCommit(pPager, bCommit) ){ - sqlcipher_sqlite3PcacheCleanAll(pPager->pPCache); - }else{ - sqlcipher_sqlite3PcacheClearWritable(pPager->pPCache); + /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on + ** the database file, the log and log-summary files will be deleted. + */ + if( rc==SQLITE_OK && pPager->pWal ){ + rc = pagerExclusiveLock(pPager); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, + pPager->pageSize, (u8*)pPager->pTmpSpace); + pPager->pWal = 0; + pagerFixMaplimit(pPager); + if( rc && !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK); } - sqlcipher_sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); } + return rc; +} - if( pagerUseWal(pPager) ){ - /* Drop the WAL write-lock, if any. Also, if the connection was in - ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE - ** lock held on the database file. - */ - rc2 = sqlcipher_sqlite3WalEndWriteTransaction(pPager->pWal); - assert( rc2==SQLITE_OK ); - }else if( rc==SQLITE_OK && bCommit && pPager->dbFileSize>pPager->dbSize ){ - /* This branch is taken when committing a transaction in rollback-journal - ** mode if the database file on disk is larger than the database image. - ** At this point the journal has been finalized and the transaction - ** successfully committed, but the EXCLUSIVE lock is still held on the - ** file. So it is safe to truncate the database file to its minimum - ** required size. */ - assert( pPager->eLock==EXCLUSIVE_LOCK ); - rc = pager_truncate(pPager, pPager->dbSize); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +/* +** If pager pPager is a wal-mode database not in exclusive locking mode, +** invoke the sqlcipher_sqlite3WalWriteLock() function on the associated Wal object +** with the same db and bLock parameters as were passed to this function. +** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerWalWriteLock(Pager *pPager, int bLock){ + int rc = SQLITE_OK; + if( pagerUseWal(pPager) && pPager->exclusiveMode==0 ){ + rc = sqlcipher_sqlite3WalWriteLock(pPager->pWal, bLock); } + return rc; +} - if( rc==SQLITE_OK && bCommit ){ - rc = sqlcipher_sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0); - if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; +/* +** Set the database handle used by the wal layer to determine if +** blocking locks are required. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3PagerWalDb(Pager *pPager, sqlcipher_sqlite3 *db){ + if( pagerUseWal(pPager) ){ + sqlcipher_sqlite3WalDb(pPager->pWal, db); } +} +#endif - if( !pPager->exclusiveMode - && (!pagerUseWal(pPager) || sqlcipher_sqlite3WalExclusiveMode(pPager->pWal, 0)) - ){ - rc2 = pagerUnlockDb(pPager, SHARED_LOCK); +#ifdef SQLITE_ENABLE_SNAPSHOT +/* +** If this is a WAL database, obtain a snapshot handle for the snapshot +** currently open. Otherwise, return an error. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerSnapshotGet(Pager *pPager, sqlcipher_sqlite3_snapshot **ppSnapshot){ + int rc = SQLITE_ERROR; + if( pPager->pWal ){ + rc = sqlcipher_sqlite3WalSnapshotGet(pPager->pWal, ppSnapshot); } - pPager->eState = PAGER_READER; - pPager->setSuper = 0; + return rc; +} - return (rc==SQLITE_OK?rc2:rc); +/* +** If this is a WAL database, store a pointer to pSnapshot. Next time a +** read transaction is opened, attempt to read from the snapshot it +** identifies. If this is not a WAL database, return an error. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerSnapshotOpen( + Pager *pPager, + sqlcipher_sqlite3_snapshot *pSnapshot +){ + int rc = SQLITE_OK; + if( pPager->pWal ){ + sqlcipher_sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot); + }else{ + rc = SQLITE_ERROR; + } + return rc; } /* -** Execute a rollback if a transaction is active and unlock the -** database file. -** -** If the pager has already entered the ERROR state, do not attempt -** the rollback at this time. Instead, pager_unlock() is called. The -** call to pager_unlock() will discard all in-memory pages, unlock -** the database file and move the pager back to OPEN state. If this -** means that there is a hot-journal left in the file-system, the next -** connection to obtain a shared lock on the pager (which may be this one) -** will roll it back. -** -** If the pager has not already entered the ERROR state, but an IO or -** malloc error occurs during a rollback, then this will itself cause -** the pager to enter the ERROR state. Which will be cleared by the -** call to pager_unlock(), as described above. +** If this is a WAL database, call sqlcipher_sqlite3WalSnapshotRecover(). If this +** is not a WAL database, return an error. */ -static void pagerUnlockAndRollback(Pager *pPager){ - if( pPager->eState!=PAGER_ERROR && pPager->eState!=PAGER_OPEN ){ - assert( assert_pager_state(pPager) ); - if( pPager->eState>=PAGER_WRITER_LOCKED ){ - sqlcipher_sqlite3BeginBenignMalloc(); - sqlcipher_sqlite3PagerRollback(pPager); - sqlcipher_sqlite3EndBenignMalloc(); - }else if( !pPager->exclusiveMode ){ - assert( pPager->eState==PAGER_READER ); - pager_end_transaction(pPager, 0, 0); - } +SQLITE_PRIVATE int sqlcipher_sqlite3PagerSnapshotRecover(Pager *pPager){ + int rc; + if( pPager->pWal ){ + rc = sqlcipher_sqlite3WalSnapshotRecover(pPager->pWal); + }else{ + rc = SQLITE_ERROR; } - pager_unlock(pPager); + return rc; } /* -** Parameter aData must point to a buffer of pPager->pageSize bytes -** of data. Compute and return a checksum based ont the contents of the -** page of data and the current value of pPager->cksumInit. -** -** This is not a real checksum. It is really just the sum of the -** random initial value (pPager->cksumInit) and every 200th byte -** of the page data, starting with byte offset (pPager->pageSize%200). -** Each byte is interpreted as an 8-bit unsigned integer. -** -** Changing the formula used to compute this checksum results in an -** incompatible journal file format. +** The caller currently has a read transaction open on the database. +** If this is not a WAL database, SQLITE_ERROR is returned. Otherwise, +** this function takes a SHARED lock on the CHECKPOINTER slot and then +** checks if the snapshot passed as the second argument is still +** available. If so, SQLITE_OK is returned. ** -** If journal corruption occurs due to a power failure, the most likely -** scenario is that one end or the other of the record will be changed. -** It is much less likely that the two ends of the journal record will be -** correct and the middle be corrupt. Thus, this "checksum" scheme, -** though fast and simple, catches the mostly likely kind of corruption. +** If the snapshot is not available, SQLITE_ERROR is returned. Or, if +** the CHECKPOINTER lock cannot be obtained, SQLITE_BUSY. If any error +** occurs (any value other than SQLITE_OK is returned), the CHECKPOINTER +** lock is released before returning. */ -static u32 pager_cksum(Pager *pPager, const u8 *aData){ - u32 cksum = pPager->cksumInit; /* Checksum value to return */ - int i = pPager->pageSize-200; /* Loop counter */ - while( i>0 ){ - cksum += aData[i]; - i -= 200; +SQLITE_PRIVATE int sqlcipher_sqlite3PagerSnapshotCheck(Pager *pPager, sqlcipher_sqlite3_snapshot *pSnapshot){ + int rc; + if( pPager->pWal ){ + rc = sqlcipher_sqlite3WalSnapshotCheck(pPager->pWal, pSnapshot); + }else{ + rc = SQLITE_ERROR; } - return cksum; + return rc; } /* -** Report the current page size and number of reserved bytes back -** to the codec. +** Release a lock obtained by an earlier successful call to +** sqlcipher_sqlite3PagerSnapshotCheck(). */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC -static void pagerReportSize(Pager *pPager){ - if( pPager->xCodecSizeChng ){ - pPager->xCodecSizeChng(pPager->pCodec, pPager->pageSize, - (int)pPager->nReserve); - } +SQLITE_PRIVATE void sqlcipher_sqlite3PagerSnapshotUnlock(Pager *pPager){ + assert( pPager->pWal ); + sqlcipher_sqlite3WalSnapshotUnlock(pPager->pWal); +} + +#endif /* SQLITE_ENABLE_SNAPSHOT */ +#endif /* !SQLITE_OMIT_WAL */ + +#ifdef SQLITE_ENABLE_ZIPVFS +/* +** A read-lock must be held on the pager when this function is called. If +** the pager is in WAL mode and the WAL file currently contains one or more +** frames, return the size in bytes of the page images stored within the +** WAL frames. Otherwise, if this is not a WAL database or the WAL file +** is empty, return 0. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3PagerWalFramesize(Pager *pPager){ + assert( pPager->eState>=PAGER_READER ); + return sqlcipher_sqlite3WalFramesize(pPager->pWal); } -#else -# define pagerReportSize(X) /* No-op if we do not support a codec */ #endif -/* END SQLCIPHER */ + +#endif /* SQLITE_OMIT_DISKIO */ /* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC -/* -** Make sure the number of reserved bits is the same in the destination -** pager as it is in the source. This comes up when a VACUUM changes the -** number of reserved bits to the "optimal" amount. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerAlignReserve(Pager *pDest, Pager *pSrc){ - if( pDest->nReserve!=pSrc->nReserve ){ - pDest->nReserve = pSrc->nReserve; - pagerReportSize(pDest); - } + +SQLITE_API int sqlcipher_sqlite3pager_is_sj_pgno(Pager *pPager, Pgno pgno) { + return (PAGER_SJ_PGNO(pPager) == pgno) ? 1 : 0; +} + +SQLITE_API void sqlcipher_sqlite3pager_error(Pager *pPager, int error) { + pPager->errCode = error; + pPager->eState = PAGER_ERROR; + setGetterMethod(pPager); +} + +SQLITE_API void sqlcipher_sqlite3pager_reset(Pager *pPager){ + pager_reset(pPager); } + #endif /* END SQLCIPHER */ + +/************** End of pager.c ***********************************************/ +/************** Begin file wal.c *********************************************/ /* -** Read a single page from either the journal file (if isMainJrnl==1) or -** from the sub-journal (if isMainJrnl==0) and playback that page. -** The page begins at offset *pOffset into the file. The *pOffset -** value is increased to the start of the next page in the journal. +** 2010 February 1 ** -** The main rollback journal uses checksums - the statement journal does -** not. +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** If the page number of the page record read from the (sub-)journal file -** is greater than the current value of Pager.dbSize, then playback is -** skipped and SQLITE_OK is returned. +** 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. ** -** If pDone is not NULL, then it is a record of pages that have already -** been played back. If the page at *pOffset has already been played back -** (if the corresponding pDone bit is set) then skip the playback. -** Make sure the pDone bit corresponding to the *pOffset page is set -** prior to returning. +************************************************************************* ** -** If the page record is successfully read from the (sub-)journal file -** and played back, then SQLITE_OK is returned. If an IO error occurs -** while reading the record from the (sub-)journal file or while writing -** to the database file, then the IO error code is returned. If data -** is successfully read from the (sub-)journal file but appears to be -** corrupted, SQLITE_DONE is returned. Data is considered corrupted in -** two circumstances: +** This file contains the implementation of a write-ahead log (WAL) used in +** "journal_mode=WAL" mode. ** -** * If the record page-number is illegal (0 or PAGER_MJ_PGNO), or -** * If the record is being rolled back from the main journal file -** and the checksum field does not match the record content. +** WRITE-AHEAD LOG (WAL) FILE FORMAT ** -** Neither of these two scenarios are possible during a savepoint rollback. +** A WAL file consists of a header followed by zero or more "frames". +** Each frame records the revised content of a single page from the +** database file. All changes to the database are recorded by writing +** frames into the WAL. Transactions commit when a frame is written that +** contains a commit marker. A single WAL can and usually does record +** multiple transactions. Periodically, the content of the WAL is +** transferred back into the database file in an operation called a +** "checkpoint". ** -** If this is a savepoint rollback, then memory may have to be dynamically -** allocated by this function. If this is the case and an allocation fails, -** SQLITE_NOMEM is returned. +** A single WAL file can be used multiple times. In other words, the +** WAL can fill up with frames and then be checkpointed and then new +** frames can overwrite the old ones. A WAL always grows from beginning +** toward the end. Checksums and counters attached to each frame are +** used to determine which frames within the WAL are valid and which +** are leftovers from prior checkpoints. +** +** The WAL header is 32 bytes in size and consists of the following eight +** big-endian 32-bit unsigned integer values: +** +** 0: Magic number. 0x377f0682 or 0x377f0683 +** 4: File format version. Currently 3007000 +** 8: Database page size. Example: 1024 +** 12: Checkpoint sequence number +** 16: Salt-1, random integer incremented with each checkpoint +** 20: Salt-2, a different random integer changing with each ckpt +** 24: Checksum-1 (first part of checksum for first 24 bytes of header). +** 28: Checksum-2 (second part of checksum for first 24 bytes of header). +** +** Immediately following the wal-header are zero or more frames. Each +** frame consists of a 24-byte frame-header followed by a bytes +** of page data. The frame-header is six big-endian 32-bit unsigned +** integer values, as follows: +** +** 0: Page number. +** 4: For commit records, the size of the database image in pages +** after the commit. For all other records, zero. +** 8: Salt-1 (copied from the header) +** 12: Salt-2 (copied from the header) +** 16: Checksum-1. +** 20: Checksum-2. +** +** A frame is considered valid if and only if the following conditions are +** true: +** +** (1) The salt-1 and salt-2 values in the frame-header match +** salt values in the wal-header +** +** (2) The checksum values in the final 8 bytes of the frame-header +** exactly match the checksum computed consecutively on the +** WAL header and the first 8 bytes and the content of all frames +** up to and including the current frame. +** +** The checksum is computed using 32-bit big-endian integers if the +** magic number in the first 4 bytes of the WAL is 0x377f0683 and it +** is computed using little-endian if the magic number is 0x377f0682. +** The checksum values are always stored in the frame header in a +** big-endian format regardless of which byte order is used to compute +** the checksum. The checksum is computed by interpreting the input as +** an even number of unsigned 32-bit integers: x[0] through x[N]. The +** algorithm used for the checksum is as follows: +** +** for i from 0 to n-1 step 2: +** s0 += x[i] + s1; +** s1 += x[i+1] + s0; +** endfor +** +** Note that s0 and s1 are both weighted checksums using fibonacci weights +** in reverse order (the largest fibonacci weight occurs on the first element +** of the sequence being summed.) The s1 value spans all 32-bit +** terms of the sequence whereas s0 omits the final term. +** +** On a checkpoint, the WAL is first VFS.xSync-ed, then valid content of the +** WAL is transferred into the database, then the database is VFS.xSync-ed. +** The VFS.xSync operations serve as write barriers - all writes launched +** before the xSync must complete before any write that launches after the +** xSync begins. +** +** After each checkpoint, the salt-1 value is incremented and the salt-2 +** value is randomized. This prevents old and new frames in the WAL from +** being considered valid at the same time and being checkpointing together +** following a crash. +** +** READER ALGORITHM +** +** To read a page from the database (call it page number P), a reader +** first checks the WAL to see if it contains page P. If so, then the +** last valid instance of page P that is a followed by a commit frame +** or is a commit frame itself becomes the value read. If the WAL +** contains no copies of page P that are valid and which are a commit +** frame or are followed by a commit frame, then page P is read from +** the database file. +** +** To start a read transaction, the reader records the index of the last +** valid frame in the WAL. The reader uses this recorded "mxFrame" value +** for all subsequent read operations. New transactions can be appended +** to the WAL, but as long as the reader uses its original mxFrame value +** and ignores the newly appended content, it will see a consistent snapshot +** of the database from a single point in time. This technique allows +** multiple concurrent readers to view different versions of the database +** content simultaneously. +** +** The reader algorithm in the previous paragraphs works correctly, but +** because frames for page P can appear anywhere within the WAL, the +** reader has to scan the entire WAL looking for page P frames. If the +** WAL is large (multiple megabytes is typical) that scan can be slow, +** and read performance suffers. To overcome this problem, a separate +** data structure called the wal-index is maintained to expedite the +** search for frames of a particular page. +** +** WAL-INDEX FORMAT +** +** Conceptually, the wal-index is shared memory, though VFS implementations +** might choose to implement the wal-index using a mmapped file. Because +** the wal-index is shared memory, SQLite does not support journal_mode=WAL +** on a network filesystem. All users of the database must be able to +** share memory. +** +** In the default unix and windows implementation, the wal-index is a mmapped +** file whose name is the database name with a "-shm" suffix added. For that +** reason, the wal-index is sometimes called the "shm" file. +** +** The wal-index is transient. After a crash, the wal-index can (and should +** be) reconstructed from the original WAL file. In fact, the VFS is required +** to either truncate or zero the header of the wal-index when the last +** connection to it closes. Because the wal-index is transient, it can +** use an architecture-specific format; it does not have to be cross-platform. +** Hence, unlike the database and WAL file formats which store all values +** as big endian, the wal-index can store multi-byte values in the native +** byte order of the host computer. +** +** The purpose of the wal-index is to answer this question quickly: Given +** a page number P and a maximum frame index M, return the index of the +** last frame in the wal before frame M for page P in the WAL, or return +** NULL if there are no frames for page P in the WAL prior to M. +** +** The wal-index consists of a header region, followed by an one or +** more index blocks. +** +** The wal-index header contains the total number of frames within the WAL +** in the mxFrame field. +** +** Each index block except for the first contains information on +** HASHTABLE_NPAGE frames. The first index block contains information on +** HASHTABLE_NPAGE_ONE frames. The values of HASHTABLE_NPAGE_ONE and +** HASHTABLE_NPAGE are selected so that together the wal-index header and +** first index block are the same size as all other index blocks in the +** wal-index. The values are: +** +** HASHTABLE_NPAGE 4096 +** HASHTABLE_NPAGE_ONE 4062 +** +** Each index block contains two sections, a page-mapping that contains the +** database page number associated with each wal frame, and a hash-table +** that allows readers to query an index block for a specific page number. +** The page-mapping is an array of HASHTABLE_NPAGE (or HASHTABLE_NPAGE_ONE +** for the first index block) 32-bit page numbers. The first entry in the +** first index-block contains the database page number corresponding to the +** first frame in the WAL file. The first entry in the second index block +** in the WAL file corresponds to the (HASHTABLE_NPAGE_ONE+1)th frame in +** the log, and so on. +** +** The last index block in a wal-index usually contains less than the full +** complement of HASHTABLE_NPAGE (or HASHTABLE_NPAGE_ONE) page-numbers, +** depending on the contents of the WAL file. This does not change the +** allocated size of the page-mapping array - the page-mapping array merely +** contains unused entries. +** +** Even without using the hash table, the last frame for page P +** can be found by scanning the page-mapping sections of each index block +** starting with the last index block and moving toward the first, and +** within each index block, starting at the end and moving toward the +** beginning. The first entry that equals P corresponds to the frame +** holding the content for that page. +** +** The hash table consists of HASHTABLE_NSLOT 16-bit unsigned integers. +** HASHTABLE_NSLOT = 2*HASHTABLE_NPAGE, and there is one entry in the +** hash table for each page number in the mapping section, so the hash +** table is never more than half full. The expected number of collisions +** prior to finding a match is 1. Each entry of the hash table is an +** 1-based index of an entry in the mapping section of the same +** index block. Let K be the 1-based index of the largest entry in +** the mapping section. (For index blocks other than the last, K will +** always be exactly HASHTABLE_NPAGE (4096) and for the last index block +** K will be (mxFrame%HASHTABLE_NPAGE).) Unused slots of the hash table +** contain a value of 0. +** +** To look for page P in the hash table, first compute a hash iKey on +** P as follows: +** +** iKey = (P * 383) % HASHTABLE_NSLOT +** +** Then start scanning entries of the hash table, starting with iKey +** (wrapping around to the beginning when the end of the hash table is +** reached) until an unused hash slot is found. Let the first unused slot +** be at index iUnused. (iUnused might be less than iKey if there was +** wrap-around.) Because the hash table is never more than half full, +** the search is guaranteed to eventually hit an unused entry. Let +** iMax be the value between iKey and iUnused, closest to iUnused, +** where aHash[iMax]==P. If there is no iMax entry (if there exists +** no hash slot such that aHash[i]==p) then page P is not in the +** current index block. Otherwise the iMax-th mapping entry of the +** current index block corresponds to the last entry that references +** page P. +** +** A hash search begins with the last index block and moves toward the +** first index block, looking for entries corresponding to page P. On +** average, only two or three slots in each index block need to be +** examined in order to either find the last entry for page P, or to +** establish that no such entry exists in the block. Each index block +** holds over 4000 entries. So two or three index blocks are sufficient +** to cover a typical 10 megabyte WAL file, assuming 1K pages. 8 or 10 +** comparisons (on average) suffice to either locate a frame in the +** WAL or to establish that the frame does not exist in the WAL. This +** is much faster than scanning the entire 10MB WAL. +** +** Note that entries are added in order of increasing K. Hence, one +** reader might be using some value K0 and a second reader that started +** at a later time (after additional transactions were added to the WAL +** and to the wal-index) might be using a different value K1, where K1>K0. +** Both readers can use the same hash table and mapping section to get +** the correct result. There may be entries in the hash table with +** K>K0 but to the first reader, those entries will appear to be unused +** slots in the hash table and so the first reader will get an answer as +** if no values greater than K0 had ever been inserted into the hash table +** in the first place - which is what reader one wants. Meanwhile, the +** second reader using K1 will see additional values that were inserted +** later, which is exactly what reader two wants. +** +** When a rollback occurs, the value of K is decreased. Hash table entries +** that correspond to frames greater than the new K value are removed +** from the hash table at this point. */ -static int pager_playback_one_page( - Pager *pPager, /* The pager being played back */ - i64 *pOffset, /* Offset of record to playback */ - Bitvec *pDone, /* Bitvec of pages already played back */ - int isMainJrnl, /* 1 -> main journal. 0 -> sub-journal. */ - int isSavepnt /* True for a savepoint rollback */ -){ - int rc; - PgHdr *pPg; /* An existing page in the cache */ - Pgno pgno; /* The page number of a page in journal */ - u32 cksum; /* Checksum used for sanity checking */ - char *aData; /* Temporary storage for the page */ - sqlcipher_sqlite3_file *jfd; /* The file descriptor for the journal file */ - int isSynced; /* True if journal page is synced */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - /* The jrnlEnc flag is true if Journal pages should be passed through - ** the codec. It is false for pure in-memory journals. */ - const int jrnlEnc = (isMainJrnl || pPager->subjInMemory==0); -#endif -/* END SQLCIPHER */ - - assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ - assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ - assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ - assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ - - aData = pPager->pTmpSpace; - assert( aData ); /* Temp storage must have already been allocated */ - assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) ); - - /* Either the state is greater than PAGER_WRITER_CACHEMOD (a transaction - ** or savepoint rollback done at the request of the caller) or this is - ** a hot-journal rollback. If it is a hot-journal rollback, the pager - ** is in state OPEN and holds an EXCLUSIVE lock. Hot-journal rollback - ** only reads from the main journal, not the sub-journal. - */ - assert( pPager->eState>=PAGER_WRITER_CACHEMOD - || (pPager->eState==PAGER_OPEN && pPager->eLock==EXCLUSIVE_LOCK) - ); - assert( pPager->eState>=PAGER_WRITER_CACHEMOD || isMainJrnl ); - - /* Read the page number and page data from the journal or sub-journal - ** file. Return an error code to the caller if an IO error occurs. - */ - jfd = isMainJrnl ? pPager->jfd : pPager->sjfd; - rc = read32bits(jfd, *pOffset, &pgno); - if( rc!=SQLITE_OK ) return rc; - rc = sqlcipher_sqlite3OsRead(jfd, (u8*)aData, pPager->pageSize, (*pOffset)+4); - if( rc!=SQLITE_OK ) return rc; - *pOffset += pPager->pageSize + 4 + isMainJrnl*4; - - /* Sanity checking on the page. This is more important that I originally - ** thought. If a power failure occurs while the journal is being written, - ** it could cause invalid data to be written into the journal. We need to - ** detect this invalid data (with high probability) and ignore it. - */ - if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ - assert( !isSavepnt ); - return SQLITE_DONE; - } - if( pgno>(Pgno)pPager->dbSize || sqlcipher_sqlite3BitvecTest(pDone, pgno) ){ - return SQLITE_OK; - } - if( isMainJrnl ){ - rc = read32bits(jfd, (*pOffset)-4, &cksum); - if( rc ) return rc; - if( !isSavepnt && pager_cksum(pPager, (u8*)aData)!=cksum ){ - return SQLITE_DONE; - } - } +#ifndef SQLITE_OMIT_WAL - /* If this page has already been played back before during the current - ** rollback, then don't bother to play it back again. - */ - if( pDone && (rc = sqlcipher_sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){ - return rc; - } +/* #include "wal.h" */ - /* When playing back page 1, restore the nReserve setting - */ - if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){ - pPager->nReserve = ((u8*)aData)[20]; - pagerReportSize(pPager); - } +/* +** Trace output macros +*/ +#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlcipher_sqlite3WalTrace = 0; +# define WALTRACE(X) if(sqlcipher_sqlite3WalTrace) sqlcipher_sqlite3DebugPrintf X +#else +# define WALTRACE(X) +#endif - /* If the pager is in CACHEMOD state, then there must be a copy of this - ** page in the pager cache. In this case just update the pager cache, - ** not the database file. The page is left marked dirty in this case. - ** - ** An exception to the above rule: If the database is in no-sync mode - ** and a page is moved during an incremental vacuum then the page may - ** not be in the pager cache. Later: if a malloc() or IO error occurs - ** during a Movepage() call, then the page may not be in the cache - ** either. So the condition described in the above paragraph is not - ** assert()able. - ** - ** If in WRITER_DBMOD, WRITER_FINISHED or OPEN state, then we update the - ** pager cache if it exists and the main file. The page is then marked - ** not dirty. Since this code is only executed in PAGER_OPEN state for - ** a hot-journal rollback, it is guaranteed that the page-cache is empty - ** if the pager is in OPEN state. - ** - ** Ticket #1171: The statement journal might contain page content that is - ** different from the page content at the start of the transaction. - ** This occurs when a page is changed prior to the start of a statement - ** then changed again within the statement. When rolling back such a - ** statement we must not write to the original database unless we know - ** for certain that original page contents are synced into the main rollback - ** journal. Otherwise, a power loss might leave modified data in the - ** database file without an entry in the rollback journal that can - ** restore the database to its original form. Two conditions must be - ** met before writing to the database files. (1) the database must be - ** locked. (2) we know that the original page content is fully synced - ** in the main journal either because the page is not in cache or else - ** the page is marked as needSync==0. - ** - ** 2008-04-14: When attempting to vacuum a corrupt database file, it - ** is possible to fail a statement on a database that does not yet exist. - ** Do not attempt to write if database file has never been opened. - */ - if( pagerUseWal(pPager) ){ - pPg = 0; - }else{ - pPg = sqlcipher_sqlite3PagerLookup(pPager, pgno); - } - assert( pPg || !MEMDB ); - assert( pPager->eState!=PAGER_OPEN || pPg==0 || pPager->tempFile ); - PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", - PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData), - (isMainJrnl?"main-journal":"sub-journal") - )); - if( isMainJrnl ){ - isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr); - }else{ - isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC)); - } - if( isOpen(pPager->fd) - && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) - && isSynced - ){ - i64 ofst = (pgno-1)*(i64)pPager->pageSize; - testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 ); - assert( !pagerUseWal(pPager) ); +/* +** The maximum (and only) versions of the wal and wal-index formats +** that may be interpreted by this version of SQLite. +** +** If a client begins recovering a WAL file and finds that (a) the checksum +** values in the wal-header are correct and (b) the version field is not +** WAL_MAX_VERSION, recovery fails and SQLite returns SQLITE_CANTOPEN. +** +** Similarly, if a client successfully reads a wal-index header (i.e. the +** checksum test is successful) and finds that the version field is not +** WALINDEX_MAX_VERSION, then no read-transaction is opened and SQLite +** returns SQLITE_CANTOPEN. +*/ +#define WAL_MAX_VERSION 3007000 +#define WALINDEX_MAX_VERSION 3007000 - /* Write the data read from the journal back into the database file. - ** This is usually safe even for an encrypted database - as the data - ** was encrypted before it was written to the journal file. The exception - ** is if the data was just read from an in-memory sub-journal. In that - ** case it must be encrypted here before it is copied into the database - ** file. */ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - if( !jrnlEnc ){ - CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData); - rc = sqlcipher_sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); - CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); - }else -#endif -/* END SQLCIPHER */ - rc = sqlcipher_sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); +/* +** Index numbers for various locking bytes. WAL_NREADER is the number +** of available reader locks and should be at least 3. The default +** is SQLITE_SHM_NLOCK==8 and WAL_NREADER==5. +** +** Technically, the various VFSes are free to implement these locks however +** they see fit. However, compatibility is encouraged so that VFSes can +** interoperate. The standard implemention used on both unix and windows +** is for the index number to indicate a byte offset into the +** WalCkptInfo.aLock[] array in the wal-index header. In other words, all +** locks are on the shm file. The WALINDEX_LOCK_OFFSET constant (which +** should be 120) is the location in the shm file for the first locking +** byte. +*/ +#define WAL_WRITE_LOCK 0 +#define WAL_ALL_BUT_WRITE 1 +#define WAL_CKPT_LOCK 1 +#define WAL_RECOVER_LOCK 2 +#define WAL_READ_LOCK(I) (3+(I)) +#define WAL_NREADER (SQLITE_SHM_NLOCK-3) - if( pgno>pPager->dbFileSize ){ - pPager->dbFileSize = pgno; - } - if( pPager->pBackup ){ -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - if( jrnlEnc ){ - CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); - sqlcipher_sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); - CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT,aData); - }else -#endif -/* END SQLCIPHER */ - sqlcipher_sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); - } - }else if( !isMainJrnl && pPg==0 ){ - /* If this is a rollback of a savepoint and data was not written to - ** the database and the page is not in-memory, there is a potential - ** problem. When the page is next fetched by the b-tree layer, it - ** will be read from the database file, which may or may not be - ** current. - ** - ** There are a couple of different ways this can happen. All are quite - ** obscure. When running in synchronous mode, this can only happen - ** if the page is on the free-list at the start of the transaction, then - ** populated, then moved using sqlcipher_sqlite3PagerMovepage(). - ** - ** The solution is to add an in-memory page to the cache containing - ** the data just read from the sub-journal. Mark the page as dirty - ** and if the pager requires a journal-sync, then mark the page as - ** requiring a journal-sync before it is written. - */ - assert( isSavepnt ); - assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)==0 ); - pPager->doNotSpill |= SPILLFLAG_ROLLBACK; - rc = sqlcipher_sqlite3PagerGet(pPager, pgno, &pPg, 1); - assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 ); - pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK; - if( rc!=SQLITE_OK ) return rc; - sqlcipher_sqlite3PcacheMakeDirty(pPg); - } - if( pPg ){ - /* No page should ever be explicitly rolled back that is in use, except - ** for page 1 which is held in use in order to keep the lock on the - ** database active. However such a page may be rolled back as a result - ** of an internal error resulting in an automatic call to - ** sqlcipher_sqlite3PagerRollback(). - */ - void *pData; - pData = pPg->pData; - memcpy(pData, (u8*)aData, pPager->pageSize); - pPager->xReiniter(pPg); - /* It used to be that sqlcipher_sqlite3PcacheMakeClean(pPg) was called here. But - ** that call was dangerous and had no detectable benefit since the cache - ** is normally cleaned by sqlcipher_sqlite3PcacheCleanAll() after rollback and so - ** has been removed. */ - pager_set_pagehash(pPg); - /* If this was page 1, then restore the value of Pager.dbFileVers. - ** Do this before any decoding. */ - if( pgno==1 ){ - memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); - } +/* Object declarations */ +typedef struct WalIndexHdr WalIndexHdr; +typedef struct WalIterator WalIterator; +typedef struct WalCkptInfo WalCkptInfo; - /* Decode the page just read from disk */ -/* BEGIN SQLCIPHER */ -#if SQLITE_HAS_CODEC - if( jrnlEnc ){ CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT); } -#endif -/* END SQLCIPHER */ - sqlcipher_sqlite3PcacheRelease(pPg); - } - return rc; -} /* -** Parameter zSuper is the name of a super-journal file. A single journal -** file that referred to the super-journal file has just been rolled back. -** This routine checks if it is possible to delete the super-journal file, -** and does so if it is. +** The following object holds a copy of the wal-index header content. ** -** Argument zSuper may point to Pager.pTmpSpace. So that buffer is not -** available for use within this function. +** The actual header in the wal-index consists of two copies of this +** object followed by one instance of the WalCkptInfo object. +** For all versions of SQLite through 3.10.0 and probably beyond, +** the locking bytes (WalCkptInfo.aLock) start at offset 120 and +** the total header size is 136 bytes. ** -** When a super-journal file is created, it is populated with the names -** of all of its child journals, one after another, formatted as utf-8 -** encoded text. The end of each child journal file is marked with a -** nul-terminator byte (0x00). i.e. the entire contents of a super-journal -** file for a transaction involving two databases might be: +** The szPage value can be any power of 2 between 512 and 32768, inclusive. +** Or it can be 1 to represent a 65536-byte page. The latter case was +** added in 3.7.1 when support for 64K pages was added. +*/ +struct WalIndexHdr { + u32 iVersion; /* Wal-index version */ + u32 unused; /* Unused (padding) field */ + u32 iChange; /* Counter incremented each transaction */ + u8 isInit; /* 1 when initialized */ + u8 bigEndCksum; /* True if checksums in WAL are big-endian */ + u16 szPage; /* Database page size in bytes. 1==64K */ + u32 mxFrame; /* Index of last valid frame in the WAL */ + u32 nPage; /* Size of database in pages */ + u32 aFrameCksum[2]; /* Checksum of last frame in log */ + u32 aSalt[2]; /* Two salt values copied from WAL header */ + u32 aCksum[2]; /* Checksum over all prior fields */ +}; + +/* +** A copy of the following object occurs in the wal-index immediately +** following the second copy of the WalIndexHdr. This object stores +** information used by checkpoint. ** -** "/home/bill/a.db-journal\x00/home/bill/b.db-journal\x00" +** nBackfill is the number of frames in the WAL that have been written +** back into the database. (We call the act of moving content from WAL to +** database "backfilling".) The nBackfill number is never greater than +** WalIndexHdr.mxFrame. nBackfill can only be increased by threads +** holding the WAL_CKPT_LOCK lock (which includes a recovery thread). +** However, a WAL_WRITE_LOCK thread can move the value of nBackfill from +** mxFrame back to zero when the WAL is reset. ** -** A super-journal file may only be deleted once all of its child -** journals have been rolled back. +** nBackfillAttempted is the largest value of nBackfill that a checkpoint +** has attempted to achieve. Normally nBackfill==nBackfillAtempted, however +** the nBackfillAttempted is set before any backfilling is done and the +** nBackfill is only set after all backfilling completes. So if a checkpoint +** crashes, nBackfillAttempted might be larger than nBackfill. The +** WalIndexHdr.mxFrame must never be less than nBackfillAttempted. ** -** This function reads the contents of the super-journal file into -** memory and loops through each of the child journal names. For -** each child journal, it checks if: +** The aLock[] field is a set of bytes used for locking. These bytes should +** never be read or written. ** -** * if the child journal exists, and if so -** * if the child journal contains a reference to super-journal -** file zSuper +** There is one entry in aReadMark[] for each reader lock. If a reader +** holds read-lock K, then the value in aReadMark[K] is no greater than +** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff) +** for any aReadMark[] means that entry is unused. aReadMark[0] is +** a special case; its value is never used and it exists as a place-holder +** to avoid having to offset aReadMark[] indexs by one. Readers holding +** WAL_READ_LOCK(0) always ignore the entire WAL and read all content +** directly from the database. ** -** If a child journal can be found that matches both of the criteria -** above, this function returns without doing anything. Otherwise, if -** no such child journal can be found, file zSuper is deleted from -** the file-system using sqlcipher_sqlite3OsDelete(). +** The value of aReadMark[K] may only be changed by a thread that +** is holding an exclusive lock on WAL_READ_LOCK(K). Thus, the value of +** aReadMark[K] cannot changed while there is a reader is using that mark +** since the reader will be holding a shared lock on WAL_READ_LOCK(K). ** -** If an IO error within this function, an error code is returned. This -** function allocates memory by calling sqlcipher_sqlite3Malloc(). If an allocation -** fails, SQLITE_NOMEM is returned. Otherwise, if no IO or malloc errors -** occur, SQLITE_OK is returned. +** The checkpointer may only transfer frames from WAL to database where +** the frame numbers are less than or equal to every aReadMark[] that is +** in use (that is, every aReadMark[j] for which there is a corresponding +** WAL_READ_LOCK(j)). New readers (usually) pick the aReadMark[] with the +** largest value and will increase an unused aReadMark[] to mxFrame if there +** is not already an aReadMark[] equal to mxFrame. The exception to the +** previous sentence is when nBackfill equals mxFrame (meaning that everything +** in the WAL has been backfilled into the database) then new readers +** will choose aReadMark[0] which has value 0 and hence such reader will +** get all their all content directly from the database file and ignore +** the WAL. ** -** TODO: This function allocates a single block of memory to load -** the entire contents of the super-journal file. This could be -** a couple of kilobytes or so - potentially larger than the page -** size. +** Writers normally append new frames to the end of the WAL. However, +** if nBackfill equals mxFrame (meaning that all WAL content has been +** written back into the database) and if no readers are using the WAL +** (in other words, if there are no WAL_READ_LOCK(i) where i>0) then +** the writer will first "reset" the WAL back to the beginning and start +** writing new content beginning at frame 1. +** +** We assume that 32-bit loads are atomic and so no locks are needed in +** order to read from any aReadMark[] entries. */ -static int pager_delsuper(Pager *pPager, const char *zSuper){ - sqlcipher_sqlite3_vfs *pVfs = pPager->pVfs; - int rc; /* Return code */ - sqlcipher_sqlite3_file *pSuper; /* Malloc'd super-journal file descriptor */ - sqlcipher_sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ - char *zSuperJournal = 0; /* Contents of super-journal file */ - i64 nSuperJournal; /* Size of super-journal file */ - char *zJournal; /* Pointer to one journal within MJ file */ - char *zSuperPtr; /* Space to hold super-journal filename */ - char *zFree = 0; /* Free this buffer */ - int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ +struct WalCkptInfo { + u32 nBackfill; /* Number of WAL frames backfilled into DB */ + u32 aReadMark[WAL_NREADER]; /* Reader marks */ + u8 aLock[SQLITE_SHM_NLOCK]; /* Reserved space for locks */ + u32 nBackfillAttempted; /* WAL frames perhaps written, or maybe not */ + u32 notUsed0; /* Available for future enhancements */ +}; +#define READMARK_NOT_USED 0xffffffff - /* Allocate space for both the pJournal and pSuper file descriptors. - ** If successful, open the super-journal file for reading. - */ - pSuper = (sqlcipher_sqlite3_file *)sqlcipher_sqlite3MallocZero(pVfs->szOsFile * 2); - if( !pSuper ){ - rc = SQLITE_NOMEM_BKPT; - pJournal = 0; - }else{ - const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL); - rc = sqlcipher_sqlite3OsOpen(pVfs, zSuper, pSuper, flags, 0); - pJournal = (sqlcipher_sqlite3_file *)(((u8 *)pSuper) + pVfs->szOsFile); - } - if( rc!=SQLITE_OK ) goto delsuper_out; +/* +** This is a schematic view of the complete 136-byte header of the +** wal-index file (also known as the -shm file): +** +** +-----------------------------+ +** 0: | iVersion | \ +** +-----------------------------+ | +** 4: | (unused padding) | | +** +-----------------------------+ | +** 8: | iChange | | +** +-------+-------+-------------+ | +** 12: | bInit | bBig | szPage | | +** +-------+-------+-------------+ | +** 16: | mxFrame | | First copy of the +** +-----------------------------+ | WalIndexHdr object +** 20: | nPage | | +** +-----------------------------+ | +** 24: | aFrameCksum | | +** | | | +** +-----------------------------+ | +** 32: | aSalt | | +** | | | +** +-----------------------------+ | +** 40: | aCksum | | +** | | / +** +-----------------------------+ +** 48: | iVersion | \ +** +-----------------------------+ | +** 52: | (unused padding) | | +** +-----------------------------+ | +** 56: | iChange | | +** +-------+-------+-------------+ | +** 60: | bInit | bBig | szPage | | +** +-------+-------+-------------+ | Second copy of the +** 64: | mxFrame | | WalIndexHdr +** +-----------------------------+ | +** 68: | nPage | | +** +-----------------------------+ | +** 72: | aFrameCksum | | +** | | | +** +-----------------------------+ | +** 80: | aSalt | | +** | | | +** +-----------------------------+ | +** 88: | aCksum | | +** | | / +** +-----------------------------+ +** 96: | nBackfill | +** +-----------------------------+ +** 100: | 5 read marks | +** | | +** | | +** | | +** | | +** +-------+-------+------+------+ +** 120: | Write | Ckpt | Rcvr | Rd0 | \ +** +-------+-------+------+------+ ) 8 lock bytes +** | Read1 | Read2 | Rd3 | Rd4 | / +** +-------+-------+------+------+ +** 128: | nBackfillAttempted | +** +-----------------------------+ +** 132: | (unused padding) | +** +-----------------------------+ +*/ - /* Load the entire super-journal file into space obtained from - ** sqlcipher_sqlite3_malloc() and pointed to by zSuperJournal. Also obtain - ** sufficient space (in zSuperPtr) to hold the names of super-journal - ** files extracted from regular rollback-journals. - */ - rc = sqlcipher_sqlite3OsFileSize(pSuper, &nSuperJournal); - if( rc!=SQLITE_OK ) goto delsuper_out; - nSuperPtr = pVfs->mxPathname+1; - zFree = sqlcipher_sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2); - if( !zFree ){ - rc = SQLITE_NOMEM_BKPT; - goto delsuper_out; - } - zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0; - zSuperJournal = &zFree[4]; - zSuperPtr = &zSuperJournal[nSuperJournal+2]; - rc = sqlcipher_sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); - if( rc!=SQLITE_OK ) goto delsuper_out; - zSuperJournal[nSuperJournal] = 0; - zSuperJournal[nSuperJournal+1] = 0; +/* A block of WALINDEX_LOCK_RESERVED bytes beginning at +** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems +** only support mandatory file-locks, we do not read or write data +** from the region of the file on which locks are applied. +*/ +#define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2+offsetof(WalCkptInfo,aLock)) +#define WALINDEX_HDR_SIZE (sizeof(WalIndexHdr)*2+sizeof(WalCkptInfo)) - zJournal = zSuperJournal; - while( (zJournal-zSuperJournal)pageSize bytes). -** If the file on disk is currently larger than nPage pages, then use the VFS -** xTruncate() method to truncate it. +** The internals of this structure are only accessed by: ** -** Or, it might be the case that the file on disk is smaller than -** nPage pages. Some operating system implementations can get confused if -** you try to truncate a file to some size that is larger than it -** currently is, so detect this case and write a single zero byte to -** the end of the new file instead. +** walIteratorInit() - Create a new iterator, +** walIteratorNext() - Step an iterator, +** walIteratorFree() - Free an iterator. ** -** If successful, return SQLITE_OK. If an IO error occurs while modifying -** the database file, return the error code to the caller. +** This functionality is used by the checkpoint code (see walCheckpoint()). */ -static int pager_truncate(Pager *pPager, Pgno nPage){ - int rc = SQLITE_OK; - assert( pPager->eState!=PAGER_ERROR ); - assert( pPager->eState!=PAGER_READER ); - - if( isOpen(pPager->fd) - && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) - ){ - i64 currentSize, newSize; - int szPage = pPager->pageSize; - assert( pPager->eLock==EXCLUSIVE_LOCK ); - /* TODO: Is it safe to use Pager.dbFileSize here? */ - rc = sqlcipher_sqlite3OsFileSize(pPager->fd, ¤tSize); - newSize = szPage*(i64)nPage; - if( rc==SQLITE_OK && currentSize!=newSize ){ - if( currentSize>newSize ){ - rc = sqlcipher_sqlite3OsTruncate(pPager->fd, newSize); - }else if( (currentSize+szPage)<=newSize ){ - char *pTmp = pPager->pTmpSpace; - memset(pTmp, 0, szPage); - testcase( (newSize-szPage) == currentSize ); - testcase( (newSize-szPage) > currentSize ); - rc = sqlcipher_sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); - } - if( rc==SQLITE_OK ){ - pPager->dbFileSize = nPage; - } - } - } - return rc; -} +struct WalIterator { + u32 iPrior; /* Last result returned from the iterator */ + int nSegment; /* Number of entries in aSegment[] */ + struct WalSegment { + int iNext; /* Next slot in aIndex[] not yet returned */ + ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ + u32 *aPgno; /* Array of page numbers. */ + int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ + int iZero; /* Frame number associated with aPgno[0] */ + } aSegment[1]; /* One for every 32KB page in the wal-index */ +}; /* -** Return a sanitized version of the sector-size of OS file pFile. The -** return value is guaranteed to lie between 32 and MAX_SECTOR_SIZE. +** Define the parameters of the hash tables in the wal-index file. There +** is a hash-table following every HASHTABLE_NPAGE page numbers in the +** wal-index. +** +** Changing any of these constants will alter the wal-index format and +** create incompatibilities. */ -SQLITE_PRIVATE int sqlcipher_sqlite3SectorSize(sqlcipher_sqlite3_file *pFile){ - int iRet = sqlcipher_sqlite3OsSectorSize(pFile); - if( iRet<32 ){ - iRet = 512; - }else if( iRet>MAX_SECTOR_SIZE ){ - assert( MAX_SECTOR_SIZE>=512 ); - iRet = MAX_SECTOR_SIZE; - } - return iRet; -} +#define HASHTABLE_NPAGE 4096 /* Must be power of 2 */ +#define HASHTABLE_HASH_1 383 /* Should be prime */ +#define HASHTABLE_NSLOT (HASHTABLE_NPAGE*2) /* Must be a power of 2 */ /* -** Set the value of the Pager.sectorSize variable for the given -** pager based on the value returned by the xSectorSize method -** of the open database file. The sector size will be used -** to determine the size and alignment of journal header and -** super-journal pointers within created journal files. -** -** For temporary files the effective sector size is always 512 bytes. -** -** Otherwise, for non-temporary files, the effective sector size is -** the value returned by the xSectorSize() method rounded up to 32 if -** it is less than 32, or rounded down to MAX_SECTOR_SIZE if it -** is greater than MAX_SECTOR_SIZE. -** -** If the file has the SQLITE_IOCAP_POWERSAFE_OVERWRITE property, then set -** the effective sector size to its minimum value (512). The purpose of -** pPager->sectorSize is to define the "blast radius" of bytes that -** might change if a crash occurs while writing to a single byte in -** that range. But with POWERSAFE_OVERWRITE, the blast radius is zero -** (that is what POWERSAFE_OVERWRITE means), so we minimize the sector -** size. For backwards compatibility of the rollback journal file format, -** we cannot reduce the effective sector size below 512. +** The block of page numbers associated with the first hash-table in a +** wal-index is smaller than usual. This is so that there is a complete +** hash-table on each aligned 32KB page of the wal-index. */ -static void setSectorSize(Pager *pPager){ - assert( isOpen(pPager->fd) || pPager->tempFile ); +#define HASHTABLE_NPAGE_ONE (HASHTABLE_NPAGE - (WALINDEX_HDR_SIZE/sizeof(u32))) - if( pPager->tempFile - || (sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd) & - SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0 - ){ - /* Sector size doesn't matter for temporary files. Also, the file - ** may not have been opened yet, in which case the OsSectorSize() - ** call will segfault. */ - pPager->sectorSize = 512; - }else{ - pPager->sectorSize = sqlcipher_sqlite3SectorSize(pPager->fd); - } -} +/* The wal-index is divided into pages of WALINDEX_PGSZ bytes each. */ +#define WALINDEX_PGSZ ( \ + sizeof(ht_slot)*HASHTABLE_NSLOT + HASHTABLE_NPAGE*sizeof(u32) \ +) /* -** Playback the journal and thus restore the database file to -** the state it was in before we started making changes. -** -** The journal file format is as follows: -** -** (1) 8 byte prefix. A copy of aJournalMagic[]. -** (2) 4 byte big-endian integer which is the number of valid page records -** in the journal. If this value is 0xffffffff, then compute the -** number of page records from the journal size. -** (3) 4 byte big-endian integer which is the initial value for the -** sanity checksum. -** (4) 4 byte integer which is the number of pages to truncate the -** database to during a rollback. -** (5) 4 byte big-endian integer which is the sector size. The header -** is this many bytes in size. -** (6) 4 byte big-endian integer which is the page size. -** (7) zero padding out to the next sector size. -** (8) Zero or more pages instances, each as follows: -** + 4 byte page number. -** + pPager->pageSize bytes of data. -** + 4 byte checksum -** -** When we speak of the journal header, we mean the first 7 items above. -** Each entry in the journal is an instance of the 8th item. -** -** Call the value from the second bullet "nRec". nRec is the number of -** valid page entries in the journal. In most cases, you can compute the -** value of nRec from the size of the journal file. But if a power -** failure occurred while the journal was being written, it could be the -** case that the size of the journal file had already been increased but -** the extra entries had not yet made it safely to disk. In such a case, -** the value of nRec computed from the file size would be too large. For -** that reason, we always use the nRec value in the header. +** Obtain a pointer to the iPage'th page of the wal-index. The wal-index +** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are +** numbered from zero. ** -** If the nRec value is 0xffffffff it means that nRec should be computed -** from the file size. This value is used when the user selects the -** no-sync option for the journal. A power failure could lead to corruption -** in this case. But for things like temporary table (which will be -** deleted when the power is restored) we don't care. +** If the wal-index is currently smaller the iPage pages then the size +** of the wal-index might be increased, but only if it is safe to do +** so. It is safe to enlarge the wal-index if pWal->writeLock is true +** or pWal->exclusiveMode==WAL_HEAPMEMORY_MODE. ** -** If the file opened as the journal file is not a well-formed -** journal file then all pages up to the first corrupted page are rolled -** back (or no pages if the journal header is corrupted). The journal file -** is then deleted and SQLITE_OK returned, just as if no corruption had -** been encountered. +** Three possible result scenarios: ** -** If an I/O or malloc() error occurs, the journal-file is not deleted -** and an error code is returned. +** (1) rc==SQLITE_OK and *ppPage==Requested-Wal-Index-Page +** (2) rc>=SQLITE_ERROR and *ppPage==NULL +** (3) rc==SQLITE_OK and *ppPage==NULL // only if iPage==0 ** -** The isHot parameter indicates that we are trying to rollback a journal -** that might be a hot journal. Or, it could be that the journal is -** preserved because of JOURNALMODE_PERSIST or JOURNALMODE_TRUNCATE. -** If the journal really is hot, reset the pager cache prior rolling -** back any content. If the journal is merely persistent, no reset is -** needed. +** Scenario (3) can only occur when pWal->writeLock is false and iPage==0 */ -static int pager_playback(Pager *pPager, int isHot){ - sqlcipher_sqlite3_vfs *pVfs = pPager->pVfs; - i64 szJ; /* Size of the journal file in bytes */ - u32 nRec; /* Number of Records in the journal */ - u32 u; /* Unsigned loop counter */ - Pgno mxPg = 0; /* Size of the original file in pages */ - int rc; /* Result code of a subroutine */ - int res = 1; /* Value returned by sqlcipher_sqlite3OsAccess() */ - char *zSuper = 0; /* Name of super-journal file if any */ - int needPagerReset; /* True to reset page prior to first page rollback */ - int nPlayback = 0; /* Total number of pages restored from journal */ - u32 savedPageSize = pPager->pageSize; - - /* Figure out how many records are in the journal. Abort early if - ** the journal is empty. - */ - assert( isOpen(pPager->jfd) ); - rc = sqlcipher_sqlite3OsFileSize(pPager->jfd, &szJ); - if( rc!=SQLITE_OK ){ - goto end_playback; - } +static SQLITE_NOINLINE int walIndexPageRealloc( + Wal *pWal, /* The WAL context */ + int iPage, /* The page we seek */ + volatile u32 **ppPage /* Write the page pointer here */ +){ + int rc = SQLITE_OK; - /* Read the super-journal name from the journal, if it is present. - ** If a super-journal file name is specified, but the file is not - ** present on disk, then the journal is not hot and does not need to be - ** played back. - ** - ** TODO: Technically the following is an error because it assumes that - ** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that - ** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c, - ** mxPathname is 512, which is the same as the minimum allowable value - ** for pageSize. - */ - zSuper = pPager->pTmpSpace; - rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); - if( rc==SQLITE_OK && zSuper[0] ){ - rc = sqlcipher_sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res); - } - zSuper = 0; - if( rc!=SQLITE_OK || !res ){ - goto end_playback; + /* Enlarge the pWal->apWiData[] array if required */ + if( pWal->nWiData<=iPage ){ + sqlcipher_sqlite3_int64 nByte = sizeof(u32*)*(iPage+1); + volatile u32 **apNew; + apNew = (volatile u32 **)sqlcipher_sqlite3Realloc((void *)pWal->apWiData, nByte); + if( !apNew ){ + *ppPage = 0; + return SQLITE_NOMEM_BKPT; + } + memset((void*)&apNew[pWal->nWiData], 0, + sizeof(u32*)*(iPage+1-pWal->nWiData)); + pWal->apWiData = apNew; + pWal->nWiData = iPage+1; } - pPager->journalOff = 0; - needPagerReset = isHot; - /* This loop terminates either when a readJournalHdr() or - ** pager_playback_one_page() call returns SQLITE_DONE or an IO error - ** occurs. - */ - while( 1 ){ - /* Read the next journal header from the journal file. If there are - ** not enough bytes left in the journal file for a complete header, or - ** it is corrupted, then a process must have failed while writing it. - ** This indicates nothing more needs to be rolled back. - */ - rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_DONE ){ + /* Request a pointer to the required page from the VFS */ + assert( pWal->apWiData[iPage]==0 ); + if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ + pWal->apWiData[iPage] = (u32 volatile *)sqlcipher_sqlite3MallocZero(WALINDEX_PGSZ); + if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT; + }else{ + rc = sqlcipher_sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, + pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] + ); + assert( pWal->apWiData[iPage]!=0 + || rc!=SQLITE_OK + || (pWal->writeLock==0 && iPage==0) ); + testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); + if( rc==SQLITE_OK ){ + if( iPage>0 && sqlcipher_sqlite3FaultSim(600) ) rc = SQLITE_NOMEM; + }else if( (rc&0xff)==SQLITE_READONLY ){ + pWal->readOnly |= WAL_SHM_RDONLY; + if( rc==SQLITE_READONLY ){ rc = SQLITE_OK; } - goto end_playback; - } - - /* If nRec is 0xffffffff, then this journal was created by a process - ** working in no-sync mode. This means that the rest of the journal - ** file consists of pages, there are no more journal headers. Compute - ** the value of nRec based on this assumption. - */ - if( nRec==0xffffffff ){ - assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ); - nRec = (int)((szJ - JOURNAL_HDR_SZ(pPager))/JOURNAL_PG_SZ(pPager)); - } - - /* If nRec is 0 and this rollback is of a transaction created by this - ** process and if this is the final header in the journal, then it means - ** that this part of the journal was being filled but has not yet been - ** synced to disk. Compute the number of pages based on the remaining - ** size of the file. - ** - ** The third term of the test was added to fix ticket #2565. - ** When rolling back a hot journal, nRec==0 always means that the next - ** chunk of the journal contains zero pages to be rolled back. But - ** when doing a ROLLBACK and the nRec==0 chunk is the last chunk in - ** the journal, it means that the journal might contain additional - ** pages that need to be rolled back and that the number of pages - ** should be computed based on the journal file size. - */ - if( nRec==0 && !isHot && - pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ - nRec = (int)((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager)); - } - - /* If this is the first header read from the journal, truncate the - ** database file back to its original size. - */ - if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){ - rc = pager_truncate(pPager, mxPg); - if( rc!=SQLITE_OK ){ - goto end_playback; - } - pPager->dbSize = mxPg; - } - - /* Copy original pages out of the journal and back into the - ** database file and/or page cache. - */ - for(u=0; ujournalOff,0,1,0); - if( rc==SQLITE_OK ){ - nPlayback++; - }else{ - if( rc==SQLITE_DONE ){ - pPager->journalOff = szJ; - break; - }else if( rc==SQLITE_IOERR_SHORT_READ ){ - /* If the journal has been truncated, simply stop reading and - ** processing the journal. This might happen if the journal was - ** not completely written and synced prior to a crash. In that - ** case, the database should have never been written in the - ** first place so it is OK to simply abandon the rollback. */ - rc = SQLITE_OK; - goto end_playback; - }else{ - /* If we are unable to rollback, quit and return the error - ** code. This will cause the pager to enter the error state - ** so that no further harm will be done. Perhaps the next - ** process to come along will be able to rollback the database. - */ - goto end_playback; - } - } } } - /*NOTREACHED*/ - assert( 0 ); -end_playback: - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3PagerSetPagesize(pPager, &savedPageSize, -1); + *ppPage = pWal->apWiData[iPage]; + assert( iPage==0 || *ppPage || rc!=SQLITE_OK ); + return rc; +} +static int walIndexPage( + Wal *pWal, /* The WAL context */ + int iPage, /* The page we seek */ + volatile u32 **ppPage /* Write the page pointer here */ +){ + if( pWal->nWiData<=iPage || (*ppPage = pWal->apWiData[iPage])==0 ){ + return walIndexPageRealloc(pWal, iPage, ppPage); } - /* Following a rollback, the database file should be back in its original - ** state prior to the start of the transaction, so invoke the - ** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the - ** assertion that the transaction counter was modified. - */ -#ifdef SQLITE_DEBUG - sqlcipher_sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0); -#endif - - /* If this playback is happening automatically as a result of an IO or - ** malloc error that occurred after the change-counter was updated but - ** before the transaction was committed, then the change-counter - ** modification may just have been reverted. If this happens in exclusive - ** mode, then subsequent transactions performed by the connection will not - ** update the change-counter at all. This may lead to cache inconsistency - ** problems for other processes at some point in the future. So, just - ** in case this has happened, clear the changeCountDone flag now. - */ - pPager->changeCountDone = pPager->tempFile; + return SQLITE_OK; +} - if( rc==SQLITE_OK ){ - /* Leave 4 bytes of space before the super-journal filename in memory. - ** This is because it may end up being passed to sqlcipher_sqlite3OsOpen(), in - ** which case it requires 4 0x00 bytes in memory immediately before - ** the filename. */ - zSuper = &pPager->pTmpSpace[4]; - rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); - testcase( rc!=SQLITE_OK ); - } - if( rc==SQLITE_OK - && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) - ){ - rc = sqlcipher_sqlite3PagerSync(pPager, 0); - } - if( rc==SQLITE_OK ){ - rc = pager_end_transaction(pPager, zSuper[0]!='\0', 0); - testcase( rc!=SQLITE_OK ); - } - if( rc==SQLITE_OK && zSuper[0] && res ){ - /* If there was a super-journal and this routine will return success, - ** see if it is possible to delete the super-journal. - */ - assert( zSuper==&pPager->pTmpSpace[4] ); - memset(&zSuper[-4], 0, 4); - rc = pager_delsuper(pPager, zSuper); - testcase( rc!=SQLITE_OK ); - } - if( isHot && nPlayback ){ - sqlcipher_sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s", - nPlayback, pPager->zJournal); - } +/* +** Return a pointer to the WalCkptInfo structure in the wal-index. +*/ +static volatile WalCkptInfo *walCkptInfo(Wal *pWal){ + assert( pWal->nWiData>0 && pWal->apWiData[0] ); + return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]); +} - /* The Pager.sectorSize variable may have been updated while rolling - ** back a journal created by a process with a different sector size - ** value. Reset it to the correct value for this process. - */ - setSectorSize(pPager); - return rc; +/* +** Return a pointer to the WalIndexHdr structure in the wal-index. +*/ +static volatile WalIndexHdr *walIndexHdr(Wal *pWal){ + assert( pWal->nWiData>0 && pWal->apWiData[0] ); + return (volatile WalIndexHdr*)pWal->apWiData[0]; } +/* +** The argument to this macro must be of type u32. On a little-endian +** architecture, it returns the u32 value that results from interpreting +** the 4 bytes as a big-endian value. On a big-endian architecture, it +** returns the value that would be produced by interpreting the 4 bytes +** of the input value as a little-endian integer. +*/ +#define BYTESWAP32(x) ( \ + (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \ + + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \ +) /* -** Read the content for page pPg out of the database file (or out of -** the WAL if that is where the most recent copy if found) into -** pPg->pData. A shared lock or greater must be held on the database -** file before this function is called. +** Generate or extend an 8 byte checksum based on the data in +** array aByte[] and the initial values of aIn[0] and aIn[1] (or +** initial values of 0 and 0 if aIn==NULL). ** -** If page 1 is read, then the value of Pager.dbFileVers[] is set to -** the value read from the database file. +** The checksum is written back into aOut[] before returning. ** -** If an IO error occurs, then the IO error is returned to the caller. -** Otherwise, SQLITE_OK is returned. +** nByte must be a positive multiple of 8. */ -static int readDbPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */ - int rc = SQLITE_OK; /* Return code */ +static void walChecksumBytes( + int nativeCksum, /* True for native byte-order, false for non-native */ + u8 *a, /* Content to be checksummed */ + int nByte, /* Bytes of content in a[]. Must be a multiple of 8. */ + const u32 *aIn, /* Initial checksum value input */ + u32 *aOut /* OUT: Final checksum value output */ +){ + u32 s1, s2; + u32 *aData = (u32 *)a; + u32 *aEnd = (u32 *)&a[nByte]; -#ifndef SQLITE_OMIT_WAL - u32 iFrame = 0; /* Frame of WAL containing pgno */ + if( aIn ){ + s1 = aIn[0]; + s2 = aIn[1]; + }else{ + s1 = s2 = 0; + } - assert( pPager->eState>=PAGER_READER && !MEMDB ); - assert( isOpen(pPager->fd) ); + assert( nByte>=8 ); + assert( (nByte&0x00000007)==0 ); + assert( nByte<=65536 ); - if( pagerUseWal(pPager) ){ - rc = sqlcipher_sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame); - if( rc ) return rc; - } - if( iFrame ){ - rc = sqlcipher_sqlite3WalReadFrame(pPager->pWal, iFrame,pPager->pageSize,pPg->pData); - }else -#endif - { - i64 iOffset = (pPg->pgno-1)*(i64)pPager->pageSize; - rc = sqlcipher_sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset); - if( rc==SQLITE_IOERR_SHORT_READ ){ - rc = SQLITE_OK; - } + if( nativeCksum ){ + do { + s1 += *aData++ + s2; + s2 += *aData++ + s1; + }while( aDatapgno==1 ){ - if( rc ){ - /* If the read is unsuccessful, set the dbFileVers[] to something - ** that will never be a valid file version. dbFileVers[] is a copy - ** of bytes 24..39 of the database. Bytes 28..31 should always be - ** zero or the size of the database in page. Bytes 32..35 and 35..39 - ** should be page numbers which are never 0xffffffff. So filling - ** pPager->dbFileVers[] with all 0xff bytes should suffice. - ** - ** For an encrypted database, the situation is more complex: bytes - ** 24..39 of the database are white noise. But the probability of - ** white noise equaling 16 bytes of 0xff is vanishingly small so - ** we should still be ok. - */ - memset(pPager->dbFileVers, 0xff, sizeof(pPager->dbFileVers)); - }else{ - u8 *dbFileVers = &((u8*)pPg->pData)[24]; - memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); - } + aOut[0] = s1; + aOut[1] = s2; +} + +/* +** If there is the possibility of concurrent access to the SHM file +** from multiple threads and/or processes, then do a memory barrier. +*/ +static void walShmBarrier(Wal *pWal){ + if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){ + sqlcipher_sqlite3OsShmBarrier(pWal->pDbFd); } - CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = SQLITE_NOMEM_BKPT); +} - PAGER_INCR(sqlcipher_sqlite3_pager_readdb_count); - PAGER_INCR(pPager->nRead); - IOTRACE(("PGIN %p %d\n", pPager, pPg->pgno)); - PAGERTRACE(("FETCH %d page %d hash(%08x)\n", - PAGERID(pPager), pPg->pgno, pager_pagehash(pPg))); +/* +** Add the SQLITE_NO_TSAN as part of the return-type of a function +** definition as a hint that the function contains constructs that +** might give false-positive TSAN warnings. +** +** See tag-20200519-1. +*/ +#if defined(__clang__) && !defined(SQLITE_NO_TSAN) +# define SQLITE_NO_TSAN __attribute__((no_sanitize_thread)) +#else +# define SQLITE_NO_TSAN +#endif - return rc; +/* +** Write the header information in pWal->hdr into the wal-index. +** +** The checksum on pWal->hdr is updated before it is written. +*/ +static SQLITE_NO_TSAN void walIndexWriteHdr(Wal *pWal){ + volatile WalIndexHdr *aHdr = walIndexHdr(pWal); + const int nCksum = offsetof(WalIndexHdr, aCksum); + + assert( pWal->writeLock ); + pWal->hdr.isInit = 1; + pWal->hdr.iVersion = WALINDEX_MAX_VERSION; + walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); + /* Possible TSAN false-positive. See tag-20200519-1 */ + memcpy((void*)&aHdr[1], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); + walShmBarrier(pWal); + memcpy((void*)&aHdr[0], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); } /* -** Update the value of the change-counter at offsets 24 and 92 in -** the header and the sqlite version number at offset 96. +** This function encodes a single frame header and writes it to a buffer +** supplied by the caller. A frame-header is made up of a series of +** 4-byte big-endian integers, as follows: ** -** This is an unconditional update. See also the pager_incr_changecounter() -** routine which only updates the change-counter if the update is actually -** needed, as determined by the pPager->changeCountDone state variable. +** 0: Page number. +** 4: For commit records, the size of the database image in pages +** after the commit. For all other records, zero. +** 8: Salt-1 (copied from the wal-header) +** 12: Salt-2 (copied from the wal-header) +** 16: Checksum-1. +** 20: Checksum-2. */ -static void pager_write_changecounter(PgHdr *pPg){ - u32 change_counter; +static void walEncodeFrame( + Wal *pWal, /* The write-ahead log */ + u32 iPage, /* Database page number for frame */ + u32 nTruncate, /* New db size (or 0 for non-commit frames) */ + u8 *aData, /* Pointer to page data */ + u8 *aFrame /* OUT: Write encoded frame here */ +){ + int nativeCksum; /* True for native byte-order checksums */ + u32 *aCksum = pWal->hdr.aFrameCksum; + assert( WAL_FRAME_HDRSIZE==24 ); + sqlcipher_sqlite3Put4byte(&aFrame[0], iPage); + sqlcipher_sqlite3Put4byte(&aFrame[4], nTruncate); + if( pWal->iReCksum==0 ){ + memcpy(&aFrame[8], pWal->hdr.aSalt, 8); - /* Increment the value just read and write it back to byte 24. */ - change_counter = sqlcipher_sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1; - put32bits(((char*)pPg->pData)+24, change_counter); + nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN); + walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); + walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); - /* Also store the SQLite version number in bytes 96..99 and in - ** bytes 92..95 store the change counter for which the version number - ** is valid. */ - put32bits(((char*)pPg->pData)+92, change_counter); - put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER); + sqlcipher_sqlite3Put4byte(&aFrame[16], aCksum[0]); + sqlcipher_sqlite3Put4byte(&aFrame[20], aCksum[1]); + }else{ + memset(&aFrame[8], 0, 16); + } } -#ifndef SQLITE_OMIT_WAL /* -** This function is invoked once for each page that has already been -** written into the log file when a WAL transaction is rolled back. -** Parameter iPg is the page number of said page. The pCtx argument -** is actually a pointer to the Pager structure. -** -** If page iPg is present in the cache, and has no outstanding references, -** it is discarded. Otherwise, if there are one or more outstanding -** references, the page content is reloaded from the database. If the -** attempt to reload content from the database is required and fails, -** return an SQLite error code. Otherwise, SQLITE_OK. +** Check to see if the frame with header in aFrame[] and content +** in aData[] is valid. If it is a valid frame, fill *piPage and +** *pnTruncate and return true. Return if the frame is not valid. */ -static int pagerUndoCallback(void *pCtx, Pgno iPg){ - int rc = SQLITE_OK; - Pager *pPager = (Pager *)pCtx; - PgHdr *pPg; +static int walDecodeFrame( + Wal *pWal, /* The write-ahead log */ + u32 *piPage, /* OUT: Database page number for frame */ + u32 *pnTruncate, /* OUT: New db size (or 0 if not commit) */ + u8 *aData, /* Pointer to page data (for checksum) */ + u8 *aFrame /* Frame data */ +){ + int nativeCksum; /* True for native byte-order checksums */ + u32 *aCksum = pWal->hdr.aFrameCksum; + u32 pgno; /* Page number of the frame */ + assert( WAL_FRAME_HDRSIZE==24 ); - assert( pagerUseWal(pPager) ); - pPg = sqlcipher_sqlite3PagerLookup(pPager, iPg); - if( pPg ){ - if( sqlcipher_sqlite3PcachePageRefcount(pPg)==1 ){ - sqlcipher_sqlite3PcacheDrop(pPg); - }else{ - rc = readDbPage(pPg); - if( rc==SQLITE_OK ){ - pPager->xReiniter(pPg); - } - sqlcipher_sqlite3PagerUnrefNotNull(pPg); - } + /* A frame is only valid if the salt values in the frame-header + ** match the salt values in the wal-header. + */ + if( memcmp(&pWal->hdr.aSalt, &aFrame[8], 8)!=0 ){ + return 0; } - /* Normally, if a transaction is rolled back, any backup processes are - ** updated as data is copied out of the rollback journal and into the - ** database. This is not generally possible with a WAL database, as - ** rollback involves simply truncating the log file. Therefore, if one - ** or more frames have already been written to the log (and therefore - ** also copied into the backup databases) as part of this transaction, - ** the backups must be restarted. + /* A frame is only valid if the page number is creater than zero. */ - sqlcipher_sqlite3BackupRestart(pPager->pBackup); + pgno = sqlcipher_sqlite3Get4byte(&aFrame[0]); + if( pgno==0 ){ + return 0; + } - return rc; + /* A frame is only valid if a checksum of the WAL header, + ** all prior frams, the first 16 bytes of this frame-header, + ** and the frame-data matches the checksum in the last 8 + ** bytes of this frame-header. + */ + nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN); + walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); + walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); + if( aCksum[0]!=sqlcipher_sqlite3Get4byte(&aFrame[16]) + || aCksum[1]!=sqlcipher_sqlite3Get4byte(&aFrame[20]) + ){ + /* Checksum failed. */ + return 0; + } + + /* If we reach this point, the frame is valid. Return the page number + ** and the new database size. + */ + *piPage = pgno; + *pnTruncate = sqlcipher_sqlite3Get4byte(&aFrame[4]); + return 1; } + +#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) /* -** This function is called to rollback a transaction on a WAL database. +** Names of locks. This routine is used to provide debugging output and is not +** a part of an ordinary build. */ -static int pagerRollbackWal(Pager *pPager){ - int rc; /* Return Code */ - PgHdr *pList; /* List of dirty pages to revert */ - - /* For all pages in the cache that are currently dirty or have already - ** been written (but not committed) to the log file, do one of the - ** following: - ** - ** + Discard the cached page (if refcount==0), or - ** + Reload page content from the database (if refcount>0). - */ - pPager->dbSize = pPager->dbOrigSize; - rc = sqlcipher_sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager); - pList = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache); - while( pList && rc==SQLITE_OK ){ - PgHdr *pNext = pList->pDirty; - rc = pagerUndoCallback((void *)pPager, pList->pgno); - pList = pNext; +static const char *walLockName(int lockIdx){ + if( lockIdx==WAL_WRITE_LOCK ){ + return "WRITE-LOCK"; + }else if( lockIdx==WAL_CKPT_LOCK ){ + return "CKPT-LOCK"; + }else if( lockIdx==WAL_RECOVER_LOCK ){ + return "RECOVER-LOCK"; + }else{ + static char zName[15]; + sqlcipher_sqlite3_snprintf(sizeof(zName), zName, "READ-LOCK[%d]", + lockIdx-WAL_READ_LOCK(0)); + return zName; } +} +#endif /*defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */ + +/* +** Set or release locks on the WAL. Locks are either shared or exclusive. +** A lock cannot be moved directly between shared and exclusive - it must go +** through the unlocked state first. +** +** In locking_mode=EXCLUSIVE, all of these routines become no-ops. +*/ +static int walLockShared(Wal *pWal, int lockIdx){ + int rc; + if( pWal->exclusiveMode ) return SQLITE_OK; + rc = sqlcipher_sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, + SQLITE_SHM_LOCK | SQLITE_SHM_SHARED); + WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal, + walLockName(lockIdx), rc ? "failed" : "ok")); + VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) + return rc; +} +static void walUnlockShared(Wal *pWal, int lockIdx){ + if( pWal->exclusiveMode ) return; + (void)sqlcipher_sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, + SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED); + WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx))); +} +static int walLockExclusive(Wal *pWal, int lockIdx, int n){ + int rc; + if( pWal->exclusiveMode ) return SQLITE_OK; + rc = sqlcipher_sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, + SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); + WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, + walLockName(lockIdx), n, rc ? "failed" : "ok")); + VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) return rc; } +static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ + if( pWal->exclusiveMode ) return; + (void)sqlcipher_sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, + SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE); + WALTRACE(("WAL%p: release EXCLUSIVE-%s cnt=%d\n", pWal, + walLockName(lockIdx), n)); +} /* -** This function is a wrapper around sqlcipher_sqlite3WalFrames(). As well as logging -** the contents of the list of pages headed by pList (connected by pDirty), -** this function notifies any active backup processes that the pages have -** changed. +** Compute a hash on a page number. The resulting hash value must land +** between 0 and (HASHTABLE_NSLOT-1). The walHashNext() function advances +** the hash to the next value in the event of a collision. +*/ +static int walHash(u32 iPage){ + assert( iPage>0 ); + assert( (HASHTABLE_NSLOT & (HASHTABLE_NSLOT-1))==0 ); + return (iPage*HASHTABLE_HASH_1) & (HASHTABLE_NSLOT-1); +} +static int walNextHash(int iPriorHash){ + return (iPriorHash+1)&(HASHTABLE_NSLOT-1); +} + +/* +** An instance of the WalHashLoc object is used to describe the location +** of a page hash table in the wal-index. This becomes the return value +** from walHashGet(). +*/ +typedef struct WalHashLoc WalHashLoc; +struct WalHashLoc { + volatile ht_slot *aHash; /* Start of the wal-index hash table */ + volatile u32 *aPgno; /* aPgno[1] is the page of first frame indexed */ + u32 iZero; /* One less than the frame number of first indexed*/ +}; + +/* +** Return pointers to the hash table and page number array stored on +** page iHash of the wal-index. The wal-index is broken into 32KB pages +** numbered starting from 0. ** -** The list of pages passed into this routine is always sorted by page number. -** Hence, if page 1 appears anywhere on the list, it will be the first page. +** Set output variable pLoc->aHash to point to the start of the hash table +** in the wal-index file. Set pLoc->iZero to one less than the frame +** number of the first frame indexed by this hash table. If a +** slot in the hash table is set to N, it refers to frame number +** (pLoc->iZero+N) in the log. +** +** Finally, set pLoc->aPgno so that pLoc->aPgno[0] is the page number of the +** first frame indexed by the hash table, frame (pLoc->iZero). */ -static int pagerWalFrames( - Pager *pPager, /* Pager object */ - PgHdr *pList, /* List of frames to log */ - Pgno nTruncate, /* Database size after this commit */ - int isCommit /* True if this is a commit */ +static int walHashGet( + Wal *pWal, /* WAL handle */ + int iHash, /* Find the iHash'th table */ + WalHashLoc *pLoc /* OUT: Hash table location */ ){ int rc; /* Return code */ - int nList; /* Number of pages in pList */ - PgHdr *p; /* For looping over pages */ - - assert( pPager->pWal ); - assert( pList ); -#ifdef SQLITE_DEBUG - /* Verify that the page list is in accending order */ - for(p=pList; p && p->pDirty; p=p->pDirty){ - assert( p->pgno < p->pDirty->pgno ); - } -#endif - assert( pList->pDirty==0 || isCommit ); - if( isCommit ){ - /* If a WAL transaction is being committed, there is no point in writing - ** any pages with page numbers greater than nTruncate into the WAL file. - ** They will never be read by any client. So remove them from the pDirty - ** list here. */ - PgHdr **ppNext = &pList; - nList = 0; - for(p=pList; (*ppNext = p)!=0; p=p->pDirty){ - if( p->pgno<=nTruncate ){ - ppNext = &p->pDirty; - nList++; - } - } - assert( pList ); - }else{ - nList = 1; - } - pPager->aStat[PAGER_STAT_WRITE] += nList; + rc = walIndexPage(pWal, iHash, &pLoc->aPgno); + assert( rc==SQLITE_OK || iHash>0 ); - if( pList->pgno==1 ) pager_write_changecounter(pList); - rc = sqlcipher_sqlite3WalFrames(pPager->pWal, - pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags - ); - if( rc==SQLITE_OK && pPager->pBackup ){ - for(p=pList; p; p=p->pDirty){ - sqlcipher_sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData); + if( pLoc->aPgno ){ + pLoc->aHash = (volatile ht_slot *)&pLoc->aPgno[HASHTABLE_NPAGE]; + if( iHash==0 ){ + pLoc->aPgno = &pLoc->aPgno[WALINDEX_HDR_SIZE/sizeof(u32)]; + pLoc->iZero = 0; + }else{ + pLoc->iZero = HASHTABLE_NPAGE_ONE + (iHash-1)*HASHTABLE_NPAGE; } + }else if( NEVER(rc==SQLITE_OK) ){ + rc = SQLITE_ERROR; } - -#ifdef SQLITE_CHECK_PAGES - pList = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache); - for(p=pList; p; p=p->pDirty){ - pager_set_pagehash(p); - } -#endif - return rc; } /* -** Begin a read transaction on the WAL. -** -** This routine used to be called "pagerOpenSnapshot()" because it essentially -** makes a snapshot of the database at the current point in time and preserves -** that snapshot for use by the reader in spite of concurrently changes by -** other writers or checkpointers. +** Return the number of the wal-index page that contains the hash-table +** and page-number array that contain entries corresponding to WAL frame +** iFrame. The wal-index is broken up into 32KB pages. Wal-index pages +** are numbered starting from 0. */ -static int pagerBeginReadTransaction(Pager *pPager){ - int rc; /* Return code */ - int changed = 0; /* True if cache must be reset */ - - assert( pagerUseWal(pPager) ); - assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); - - /* sqlcipher_sqlite3WalEndReadTransaction() was not called for the previous - ** transaction in locking_mode=EXCLUSIVE. So call it now. If we - ** are in locking_mode=NORMAL and EndRead() was previously called, - ** the duplicate call is harmless. - */ - sqlcipher_sqlite3WalEndReadTransaction(pPager->pWal); +static int walFramePage(u32 iFrame){ + int iHash = (iFrame+HASHTABLE_NPAGE-HASHTABLE_NPAGE_ONE-1) / HASHTABLE_NPAGE; + assert( (iHash==0 || iFrame>HASHTABLE_NPAGE_ONE) + && (iHash>=1 || iFrame<=HASHTABLE_NPAGE_ONE) + && (iHash<=1 || iFrame>(HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE)) + && (iHash>=2 || iFrame<=HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE) + && (iHash<=2 || iFrame>(HASHTABLE_NPAGE_ONE+2*HASHTABLE_NPAGE)) + ); + assert( iHash>=0 ); + return iHash; +} - rc = sqlcipher_sqlite3WalBeginReadTransaction(pPager->pWal, &changed); - if( rc!=SQLITE_OK || changed ){ - pager_reset(pPager); - if( USEFETCH(pPager) ) sqlcipher_sqlite3OsUnfetch(pPager->fd, 0, 0); +/* +** Return the page number associated with frame iFrame in this WAL. +*/ +static u32 walFramePgno(Wal *pWal, u32 iFrame){ + int iHash = walFramePage(iFrame); + if( iHash==0 ){ + return pWal->apWiData[0][WALINDEX_HDR_SIZE/sizeof(u32) + iFrame - 1]; } - - return rc; + return pWal->apWiData[iHash][(iFrame-1-HASHTABLE_NPAGE_ONE)%HASHTABLE_NPAGE]; } -#endif /* -** This function is called as part of the transition from PAGER_OPEN -** to PAGER_READER state to determine the size of the database file -** in pages (assuming the page size currently stored in Pager.pageSize). +** Remove entries from the hash table that point to WAL slots greater +** than pWal->hdr.mxFrame. ** -** If no error occurs, SQLITE_OK is returned and the size of the database -** in pages is stored in *pnPage. Otherwise, an error code (perhaps -** SQLITE_IOERR_FSTAT) is returned and *pnPage is left unmodified. +** This function is called whenever pWal->hdr.mxFrame is decreased due +** to a rollback or savepoint. +** +** At most only the hash table containing pWal->hdr.mxFrame needs to be +** updated. Any later hash tables will be automatically cleared when +** pWal->hdr.mxFrame advances to the point where those hash tables are +** actually needed. */ -static int pagerPagecount(Pager *pPager, Pgno *pnPage){ - Pgno nPage; /* Value to return via *pnPage */ +static void walCleanupHash(Wal *pWal){ + WalHashLoc sLoc; /* Hash table location */ + int iLimit = 0; /* Zero values greater than this */ + int nByte; /* Number of bytes to zero in aPgno[] */ + int i; /* Used to iterate through aHash[] */ - /* Query the WAL sub-system for the database size. The WalDbsize() - ** function returns zero if the WAL is not open (i.e. Pager.pWal==0), or - ** if the database size is not available. The database size is not - ** available from the WAL sub-system if the log file is empty or - ** contains no valid committed transactions. - */ - assert( pPager->eState==PAGER_OPEN ); - assert( pPager->eLock>=SHARED_LOCK ); - assert( isOpen(pPager->fd) ); - assert( pPager->tempFile==0 ); - nPage = sqlcipher_sqlite3WalDbsize(pPager->pWal); + assert( pWal->writeLock ); + testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 ); + testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE ); + testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE+1 ); - /* If the number of pages in the database is not available from the - ** WAL sub-system, determine the page count based on the size of - ** the database file. If the size of the database file is not an - ** integer multiple of the page-size, round up the result. + if( pWal->hdr.mxFrame==0 ) return; + + /* Obtain pointers to the hash-table and page-number array containing + ** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed + ** that the page said hash-table and array reside on is already mapped.(1) */ - if( nPage==0 && ALWAYS(isOpen(pPager->fd)) ){ - i64 n = 0; /* Size of db file in bytes */ - int rc = sqlcipher_sqlite3OsFileSize(pPager->fd, &n); - if( rc!=SQLITE_OK ){ - return rc; + assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) ); + assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] ); + i = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); + if( NEVER(i) ) return; /* Defense-in-depth, in case (1) above is wrong */ + + /* Zero all hash-table entries that correspond to frame numbers greater + ** than pWal->hdr.mxFrame. + */ + iLimit = pWal->hdr.mxFrame - sLoc.iZero; + assert( iLimit>0 ); + for(i=0; iiLimit ){ + sLoc.aHash[i] = 0; } - nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize); } - /* If the current number of pages in the file is greater than the - ** configured maximum pager number, increase the allowed limit so - ** that the file can be read. + /* Zero the entries in the aPgno array that correspond to frames with + ** frame numbers greater than pWal->hdr.mxFrame. */ - if( nPage>pPager->mxPgno ){ - pPager->mxPgno = (Pgno)nPage; - } + nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit]); + assert( nByte>=0 ); + memset((void *)&sLoc.aPgno[iLimit], 0, nByte); - *pnPage = nPage; - return SQLITE_OK; +#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT + /* Verify that the every entry in the mapping region is still reachable + ** via the hash table even after the cleanup. + */ + if( iLimit ){ + int j; /* Loop counter */ + int iKey; /* Hash key */ + for(j=0; jeState==PAGER_OPEN ); - assert( pPager->eLock>=SHARED_LOCK ); +static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ + int rc; /* Return code */ + WalHashLoc sLoc; /* Wal-index hash table location */ - if( !pPager->tempFile ){ - int isWal; /* True if WAL file exists */ - rc = sqlcipher_sqlite3OsAccess( - pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &isWal - ); - if( rc==SQLITE_OK ){ - if( isWal ){ - Pgno nPage; /* Size of the database file */ + rc = walHashGet(pWal, walFramePage(iFrame), &sLoc); - rc = pagerPagecount(pPager, &nPage); - if( rc ) return rc; - if( nPage==0 ){ - rc = sqlcipher_sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0); - }else{ - testcase( sqlcipher_sqlite3PcachePagecount(pPager->pPCache)==0 ); - rc = sqlcipher_sqlite3PagerOpenWal(pPager, 0); + /* Assuming the wal-index file was successfully mapped, populate the + ** page number array and hash table entry. + */ + if( rc==SQLITE_OK ){ + int iKey; /* Hash table key */ + int idx; /* Value to write to hash-table slot */ + int nCollide; /* Number of hash collisions */ + + idx = iFrame - sLoc.iZero; + assert( idx <= HASHTABLE_NSLOT/2 + 1 ); + + /* If this is the first entry to be added to this hash-table, zero the + ** entire hash table and aPgno[] array before proceeding. + */ + if( idx==1 ){ + int nByte = (int)((u8*)&sLoc.aHash[HASHTABLE_NSLOT] - (u8*)sLoc.aPgno); + assert( nByte>=0 ); + memset((void*)sLoc.aPgno, 0, nByte); + } + + /* If the entry in aPgno[] is already set, then the previous writer + ** must have exited unexpectedly in the middle of a transaction (after + ** writing one or more dirty pages to the WAL to free up memory). + ** Remove the remnants of that writers uncommitted transaction from + ** the hash-table before writing any new entries. + */ + if( sLoc.aPgno[idx-1] ){ + walCleanupHash(pWal); + assert( !sLoc.aPgno[idx-1] ); + } + + /* Write the aPgno[] array entry and the hash-table slot. */ + nCollide = idx; + for(iKey=walHash(iPage); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){ + if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT; + } + sLoc.aPgno[idx-1] = iPage; + AtomicStore(&sLoc.aHash[iKey], (ht_slot)idx); + +#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT + /* Verify that the number of entries in the hash table exactly equals + ** the number of entries in the mapping region. + */ + { + int i; /* Loop counter */ + int nEntry = 0; /* Number of entries in the hash table */ + for(i=0; ijournalMode==PAGER_JOURNALMODE_WAL ){ - pPager->journalMode = PAGER_JOURNALMODE_DELETE; + assert( sLoc.aHash[iKey]==i+1 ); } } +#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */ } + return rc; } -#endif + /* -** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback -** the entire super-journal file. The case pSavepoint==NULL occurs when -** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction -** savepoint. -** -** When pSavepoint is not NULL (meaning a non-transaction savepoint is -** being rolled back), then the rollback consists of up to three stages, -** performed in the order specified: -** -** * Pages are played back from the main journal starting at byte -** offset PagerSavepoint.iOffset and continuing to -** PagerSavepoint.iHdrOffset, or to the end of the main journal -** file if PagerSavepoint.iHdrOffset is zero. -** -** * If PagerSavepoint.iHdrOffset is not zero, then pages are played -** back starting from the journal header immediately following -** PagerSavepoint.iHdrOffset to the end of the main journal file. -** -** * Pages are then played back from the sub-journal file, starting -** with the PagerSavepoint.iSubRec and continuing to the end of -** the journal file. -** -** Throughout the rollback process, each time a page is rolled back, the -** corresponding bit is set in a bitvec structure (variable pDone in the -** implementation below). This is used to ensure that a page is only -** rolled back the first time it is encountered in either journal. -** -** If pSavepoint is NULL, then pages are only played back from the main -** journal file. There is no need for a bitvec in this case. +** Recover the wal-index by reading the write-ahead log file. ** -** In either case, before playback commences the Pager.dbSize variable -** is reset to the value that it held at the start of the savepoint -** (or transaction). No page with a page-number greater than this value -** is played back. If one is encountered it is simply skipped. +** This routine first tries to establish an exclusive lock on the +** wal-index to prevent other threads/processes from doing anything +** with the WAL or wal-index while recovery is running. The +** WAL_RECOVER_LOCK is also held so that other threads will know +** that this thread is running recovery. If unable to establish +** the necessary locks, this routine returns SQLITE_BUSY. */ -static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ - i64 szJ; /* Effective size of the main journal */ - i64 iHdrOff; /* End of first segment of main-journal records */ - int rc = SQLITE_OK; /* Return code */ - Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */ - - assert( pPager->eState!=PAGER_ERROR ); - assert( pPager->eState>=PAGER_WRITER_LOCKED ); +static int walIndexRecover(Wal *pWal){ + int rc; /* Return Code */ + i64 nSize; /* Size of log file */ + u32 aFrameCksum[2] = {0, 0}; + int iLock; /* Lock offset to lock for checkpoint */ - /* Allocate a bitvec to use to store the set of pages rolled back */ - if( pSavepoint ){ - pDone = sqlcipher_sqlite3BitvecCreate(pSavepoint->nOrig); - if( !pDone ){ - return SQLITE_NOMEM_BKPT; - } + /* Obtain an exclusive lock on all byte in the locking range not already + ** locked by the caller. The caller is guaranteed to have locked the + ** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte. + ** If successful, the same bytes that are locked here are unlocked before + ** this function returns. + */ + assert( pWal->ckptLock==1 || pWal->ckptLock==0 ); + assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); + assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); + assert( pWal->writeLock ); + iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; + rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); + if( rc ){ + return rc; } - /* Set the database size back to the value it was before the savepoint - ** being reverted was opened. - */ - pPager->dbSize = pSavepoint ? pSavepoint->nOrig : pPager->dbOrigSize; - pPager->changeCountDone = pPager->tempFile; + WALTRACE(("WAL%p: recovery begin...\n", pWal)); - if( !pSavepoint && pagerUseWal(pPager) ){ - return pagerRollbackWal(pPager); + memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); + + rc = sqlcipher_sqlite3OsFileSize(pWal->pWalFd, &nSize); + if( rc!=SQLITE_OK ){ + goto recovery_error; } - /* Use pPager->journalOff as the effective size of the main rollback - ** journal. The actual file might be larger than this in - ** PAGER_JOURNALMODE_TRUNCATE or PAGER_JOURNALMODE_PERSIST. But anything - ** past pPager->journalOff is off-limits to us. - */ - szJ = pPager->journalOff; - assert( pagerUseWal(pPager)==0 || szJ==0 ); + if( nSize>WAL_HDRSIZE ){ + u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ + u32 *aPrivate = 0; /* Heap copy of *-shm hash being populated */ + u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ + int szFrame; /* Number of bytes in buffer aFrame[] */ + u8 *aData; /* Pointer to data part of aFrame buffer */ + int szPage; /* Page size according to the log */ + u32 magic; /* Magic value read from WAL header */ + u32 version; /* Magic value read from WAL header */ + int isValid; /* True if this frame is valid */ + u32 iPg; /* Current 32KB wal-index page */ + u32 iLastFrame; /* Last frame in wal, based on nSize alone */ - /* Begin by rolling back records from the main journal starting at - ** PagerSavepoint.iOffset and continuing to the next journal header. - ** There might be records in the main journal that have a page number - ** greater than the current database size (pPager->dbSize) but those - ** will be skipped automatically. Pages are added to pDone as they - ** are played back. - */ - if( pSavepoint && !pagerUseWal(pPager) ){ - iHdrOff = pSavepoint->iHdrOffset ? pSavepoint->iHdrOffset : szJ; - pPager->journalOff = pSavepoint->iOffset; - while( rc==SQLITE_OK && pPager->journalOffjournalOff, pDone, 1, 1); + /* Read in the WAL header. */ + rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); + if( rc!=SQLITE_OK ){ + goto recovery_error; } - assert( rc!=SQLITE_DONE ); - }else{ - pPager->journalOff = 0; - } - - /* Continue rolling back records out of the main journal starting at - ** the first journal header seen and continuing until the effective end - ** of the main journal file. Continue to skip out-of-range pages and - ** continue adding pages rolled back to pDone. - */ - while( rc==SQLITE_OK && pPager->journalOffjournalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff" - ** test is related to ticket #2565. See the discussion in the - ** pager_playback() function for additional information. + /* If the database page size is not a power of two, or is greater than + ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid + ** data. Similarly, if the 'magic' value is invalid, ignore the whole + ** WAL file. */ - if( nJRec==0 - && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff + magic = sqlcipher_sqlite3Get4byte(&aBuf[0]); + szPage = sqlcipher_sqlite3Get4byte(&aBuf[8]); + if( (magic&0xFFFFFFFE)!=WAL_MAGIC + || szPage&(szPage-1) + || szPage>SQLITE_MAX_PAGE_SIZE + || szPage<512 ){ - nJRec = (u32)((szJ - pPager->journalOff)/JOURNAL_PG_SZ(pPager)); + goto finished; } - for(ii=0; rc==SQLITE_OK && iijournalOffjournalOff, pDone, 1, 1); + pWal->hdr.bigEndCksum = (u8)(magic&0x00000001); + pWal->szPage = szPage; + pWal->nCkpt = sqlcipher_sqlite3Get4byte(&aBuf[12]); + memcpy(&pWal->hdr.aSalt, &aBuf[16], 8); + + /* Verify that the WAL header checksum is correct */ + walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, + aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum + ); + if( pWal->hdr.aFrameCksum[0]!=sqlcipher_sqlite3Get4byte(&aBuf[24]) + || pWal->hdr.aFrameCksum[1]!=sqlcipher_sqlite3Get4byte(&aBuf[28]) + ){ + goto finished; } - assert( rc!=SQLITE_DONE ); - } - assert( rc!=SQLITE_OK || pPager->journalOff>=szJ ); - /* Finally, rollback pages from the sub-journal. Page that were - ** previously rolled back out of the main journal (and are hence in pDone) - ** will be skipped. Out-of-range pages are also skipped. - */ - if( pSavepoint ){ - u32 ii; /* Loop counter */ - i64 offset = (i64)pSavepoint->iSubRec*(4+pPager->pageSize); + /* Verify that the version number on the WAL format is one that + ** are able to understand */ + version = sqlcipher_sqlite3Get4byte(&aBuf[4]); + if( version!=WAL_MAX_VERSION ){ + rc = SQLITE_CANTOPEN_BKPT; + goto finished; + } - if( pagerUseWal(pPager) ){ - rc = sqlcipher_sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->aWalData); + /* Malloc a buffer to read frames into. */ + szFrame = szPage + WAL_FRAME_HDRSIZE; + aFrame = (u8 *)sqlcipher_sqlite3_malloc64(szFrame + WALINDEX_PGSZ); + if( !aFrame ){ + rc = SQLITE_NOMEM_BKPT; + goto recovery_error; } - for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && iinSubRec; ii++){ - assert( offset==(i64)ii*(4+pPager->pageSize) ); - rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1); + aData = &aFrame[WAL_FRAME_HDRSIZE]; + aPrivate = (u32*)&aData[szPage]; + + /* Read all frames from the log file. */ + iLastFrame = (nSize - WAL_HDRSIZE) / szFrame; + for(iPg=0; iPg<=(u32)walFramePage(iLastFrame); iPg++){ + u32 *aShare; + u32 iFrame; /* Index of last frame read */ + u32 iLast = MIN(iLastFrame, HASHTABLE_NPAGE_ONE+iPg*HASHTABLE_NPAGE); + u32 iFirst = 1 + (iPg==0?0:HASHTABLE_NPAGE_ONE+(iPg-1)*HASHTABLE_NPAGE); + u32 nHdr, nHdr32; + rc = walIndexPage(pWal, iPg, (volatile u32**)&aShare); + assert( aShare!=0 || rc!=SQLITE_OK ); + if( aShare==0 ) break; + pWal->apWiData[iPg] = aPrivate; + + for(iFrame=iFirst; iFrame<=iLast; iFrame++){ + i64 iOffset = walFrameOffset(iFrame, szPage); + u32 pgno; /* Database page number for frame */ + u32 nTruncate; /* dbsize field from frame header */ + + /* Read and decode the next log frame. */ + rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); + if( rc!=SQLITE_OK ) break; + isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); + if( !isValid ) break; + rc = walIndexAppend(pWal, iFrame, pgno); + if( NEVER(rc!=SQLITE_OK) ) break; + + /* If nTruncate is non-zero, this is a commit record. */ + if( nTruncate ){ + pWal->hdr.mxFrame = iFrame; + pWal->hdr.nPage = nTruncate; + pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); + testcase( szPage<=32768 ); + testcase( szPage>=65536 ); + aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; + aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; + } + } + pWal->apWiData[iPg] = aShare; + nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0); + nHdr32 = nHdr / sizeof(u32); +#ifndef SQLITE_SAFER_WALINDEX_RECOVERY + /* Memcpy() should work fine here, on all reasonable implementations. + ** Technically, memcpy() might change the destination to some + ** intermediate value before setting to the final value, and that might + ** cause a concurrent reader to malfunction. Memcpy() is allowed to + ** do that, according to the spec, but no memcpy() implementation that + ** we know of actually does that, which is why we say that memcpy() + ** is safe for this. Memcpy() is certainly a lot faster. + */ + memcpy(&aShare[nHdr32], &aPrivate[nHdr32], WALINDEX_PGSZ-nHdr); +#else + /* In the event that some platform is found for which memcpy() + ** changes the destination to some intermediate value before + ** setting the final value, this alternative copy routine is + ** provided. + */ + { + int i; + for(i=nHdr32; ijournalOff = szJ; + sqlcipher_sqlite3_free(aFrame); } - return rc; -} - -/* -** Change the maximum number of in-memory pages that are allowed -** before attempting to recycle clean and unused pages. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerSetCachesize(Pager *pPager, int mxPage){ - sqlcipher_sqlite3PcacheSetCachesize(pPager->pPCache, mxPage); -} +finished: + if( rc==SQLITE_OK ){ + volatile WalCkptInfo *pInfo; + int i; + pWal->hdr.aFrameCksum[0] = aFrameCksum[0]; + pWal->hdr.aFrameCksum[1] = aFrameCksum[1]; + walIndexWriteHdr(pWal); -/* -** Change the maximum number of in-memory pages that are allowed -** before attempting to spill pages to journal. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerSetSpillsize(Pager *pPager, int mxPage){ - return sqlcipher_sqlite3PcacheSetSpillsize(pPager->pPCache, mxPage); -} + /* Reset the checkpoint-header. This is safe because this thread is + ** currently holding locks that exclude all other writers and + ** checkpointers. Then set the values of read-mark slots 1 through N. + */ + pInfo = walCkptInfo(pWal); + pInfo->nBackfill = 0; + pInfo->nBackfillAttempted = pWal->hdr.mxFrame; + pInfo->aReadMark[0] = 0; + for(i=1; ihdr.mxFrame ){ + pInfo->aReadMark[i] = pWal->hdr.mxFrame; + }else{ + pInfo->aReadMark[i] = READMARK_NOT_USED; + } + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); + }else if( rc!=SQLITE_BUSY ){ + goto recovery_error; + } + } -/* -** Invoke SQLITE_FCNTL_MMAP_SIZE based on the current value of szMmap. -*/ -static void pagerFixMaplimit(Pager *pPager){ -#if SQLITE_MAX_MMAP_SIZE>0 - sqlcipher_sqlite3_file *fd = pPager->fd; - if( isOpen(fd) && fd->pMethods->iVersion>=3 ){ - sqlcipher_sqlite3_int64 sz; - sz = pPager->szMmap; - pPager->bUseFetch = (sz>0); - setGetterMethod(pPager); - sqlcipher_sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz); + /* If more than one frame was recovered from the log file, report an + ** event via sqlcipher_sqlite3_log(). This is to help with identifying performance + ** problems caused by applications routinely shutting down without + ** checkpointing the log file. + */ + if( pWal->hdr.nPage ){ + sqlcipher_sqlite3_log(SQLITE_NOTICE_RECOVER_WAL, + "recovered %d frames from WAL file %s", + pWal->hdr.mxFrame, pWal->zWalName + ); + } } -#endif -} -/* -** Change the maximum size of any memory mapping made of the database file. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerSetMmapLimit(Pager *pPager, sqlcipher_sqlite3_int64 szMmap){ - pPager->szMmap = szMmap; - pagerFixMaplimit(pPager); +recovery_error: + WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok")); + walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); + return rc; } /* -** Free as much memory as possible from the pager. +** Close an open wal-index. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerShrink(Pager *pPager){ - sqlcipher_sqlite3PcacheShrink(pPager->pPCache); +static void walIndexClose(Wal *pWal, int isDelete){ + if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE || pWal->bShmUnreliable ){ + int i; + for(i=0; inWiData; i++){ + sqlcipher_sqlite3_free((void *)pWal->apWiData[i]); + pWal->apWiData[i] = 0; + } + } + if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){ + sqlcipher_sqlite3OsShmUnmap(pWal->pDbFd, isDelete); + } } /* -** Adjust settings of the pager to those specified in the pgFlags parameter. -** -** The "level" in pgFlags & PAGER_SYNCHRONOUS_MASK sets the robustness -** of the database to damage due to OS crashes or power failures by -** changing the number of syncs()s when writing the journals. -** There are four levels: -** -** OFF sqlcipher_sqlite3OsSync() is never called. This is the default -** for temporary and transient files. -** -** NORMAL The journal is synced once before writes begin on the -** database. This is normally adequate protection, but -** it is theoretically possible, though very unlikely, -** that an inopertune power failure could leave the journal -** in a state which would cause damage to the database -** when it is rolled back. -** -** FULL The journal is synced twice before writes begin on the -** database (with some additional information - the nRec field -** of the journal header - being written in between the two -** syncs). If we assume that writing a -** single disk sector is atomic, then this mode provides -** assurance that the journal will not be corrupted to the -** point of causing damage to the database during rollback. -** -** EXTRA This is like FULL except that is also syncs the directory -** that contains the rollback journal after the rollback -** journal is unlinked. -** -** The above is for a rollback-journal mode. For WAL mode, OFF continues -** to mean that no syncs ever occur. NORMAL means that the WAL is synced -** prior to the start of checkpoint and that the database file is synced -** at the conclusion of the checkpoint if the entire content of the WAL -** was written back into the database. But no sync operations occur for -** an ordinary commit in NORMAL mode with WAL. FULL means that the WAL -** file is synced following each commit operation, in addition to the -** syncs associated with NORMAL. There is no difference between FULL -** and EXTRA for WAL mode. +** Open a connection to the WAL file zWalName. The database file must +** already be opened on connection pDbFd. The buffer that zWalName points +** to must remain valid for the lifetime of the returned Wal* handle. ** -** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The -** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync -** using fcntl(F_FULLFSYNC). SQLITE_SYNC_NORMAL means to do an -** ordinary fsync() call. There is no difference between SQLITE_SYNC_FULL -** and SQLITE_SYNC_NORMAL on platforms other than MacOSX. But the -** synchronous=FULL versus synchronous=NORMAL setting determines when -** the xSync primitive is called and is relevant to all platforms. +** A SHARED lock should be held on the database file when this function +** is called. The purpose of this SHARED lock is to prevent any other +** client from unlinking the WAL or wal-index file. If another process +** were to do this just after this client opened one of these files, the +** system would be badly broken. ** -** Numeric values associated with these states are OFF==1, NORMAL=2, -** and FULL=3. +** If the log file is successfully opened, SQLITE_OK is returned and +** *ppWal is set to point to a new WAL handle. If an error occurs, +** an SQLite error code is returned and *ppWal is left unmodified. */ -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -SQLITE_PRIVATE void sqlcipher_sqlite3PagerSetFlags( - Pager *pPager, /* The pager to set safety level for */ - unsigned pgFlags /* Various flags */ +SQLITE_PRIVATE int sqlcipher_sqlite3WalOpen( + sqlcipher_sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ + sqlcipher_sqlite3_file *pDbFd, /* The open database file */ + const char *zWalName, /* Name of the WAL file */ + int bNoShm, /* True to run in heap-memory mode */ + i64 mxWalSize, /* Truncate WAL to this size on reset */ + Wal **ppWal /* OUT: Allocated Wal handle */ ){ - unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK; - if( pPager->tempFile ){ - pPager->noSync = 1; - pPager->fullSync = 0; - pPager->extraSync = 0; - }else{ - pPager->noSync = level==PAGER_SYNCHRONOUS_OFF ?1:0; - pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0; - pPager->extraSync = level==PAGER_SYNCHRONOUS_EXTRA ?1:0; - } - if( pPager->noSync ){ - pPager->syncFlags = 0; - }else if( pgFlags & PAGER_FULLFSYNC ){ - pPager->syncFlags = SQLITE_SYNC_FULL; - }else{ - pPager->syncFlags = SQLITE_SYNC_NORMAL; - } - pPager->walSyncFlags = (pPager->syncFlags<<2); - if( pPager->fullSync ){ - pPager->walSyncFlags |= pPager->syncFlags; + int rc; /* Return Code */ + Wal *pRet; /* Object to allocate and return */ + int flags; /* Flags passed to OsOpen() */ + + assert( zWalName && zWalName[0] ); + assert( pDbFd ); + + /* Verify the values of various constants. Any changes to the values + ** of these constants would result in an incompatible on-disk format + ** for the -shm file. Any change that causes one of these asserts to + ** fail is a backward compatibility problem, even if the change otherwise + ** works. + ** + ** This table also serves as a helpful cross-reference when trying to + ** interpret hex dumps of the -shm file. + */ + assert( 48 == sizeof(WalIndexHdr) ); + assert( 40 == sizeof(WalCkptInfo) ); + assert( 120 == WALINDEX_LOCK_OFFSET ); + assert( 136 == WALINDEX_HDR_SIZE ); + assert( 4096 == HASHTABLE_NPAGE ); + assert( 4062 == HASHTABLE_NPAGE_ONE ); + assert( 8192 == HASHTABLE_NSLOT ); + assert( 383 == HASHTABLE_HASH_1 ); + assert( 32768 == WALINDEX_PGSZ ); + assert( 8 == SQLITE_SHM_NLOCK ); + assert( 5 == WAL_NREADER ); + assert( 24 == WAL_FRAME_HDRSIZE ); + assert( 32 == WAL_HDRSIZE ); + assert( 120 == WALINDEX_LOCK_OFFSET + WAL_WRITE_LOCK ); + assert( 121 == WALINDEX_LOCK_OFFSET + WAL_CKPT_LOCK ); + assert( 122 == WALINDEX_LOCK_OFFSET + WAL_RECOVER_LOCK ); + assert( 123 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(0) ); + assert( 124 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(1) ); + assert( 125 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(2) ); + assert( 126 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(3) ); + assert( 127 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(4) ); + + /* In the amalgamation, the os_unix.c and os_win.c source files come before + ** this source file. Verify that the #defines of the locking byte offsets + ** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value. + ** For that matter, if the lock offset ever changes from its initial design + ** value of 120, we need to know that so there is an assert() to check it. + */ +#ifdef WIN_SHM_BASE + assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET ); +#endif +#ifdef UNIX_SHM_BASE + assert( UNIX_SHM_BASE==WALINDEX_LOCK_OFFSET ); +#endif + + + /* Allocate an instance of struct Wal to return. */ + *ppWal = 0; + pRet = (Wal*)sqlcipher_sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile); + if( !pRet ){ + return SQLITE_NOMEM_BKPT; } - if( (pgFlags & PAGER_CKPT_FULLFSYNC) && !pPager->noSync ){ - pPager->walSyncFlags |= (SQLITE_SYNC_FULL<<2); + + pRet->pVfs = pVfs; + pRet->pWalFd = (sqlcipher_sqlite3_file *)&pRet[1]; + pRet->pDbFd = pDbFd; + pRet->readLock = -1; + pRet->mxWalSize = mxWalSize; + pRet->zWalName = zWalName; + pRet->syncHeader = 1; + pRet->padToSectorBoundary = 1; + pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE); + + /* Open file handle on the write-ahead log file. */ + flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); + rc = sqlcipher_sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); + if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ + pRet->readOnly = WAL_RDONLY; } - if( pgFlags & PAGER_CACHESPILL ){ - pPager->doNotSpill &= ~SPILLFLAG_OFF; + + if( rc!=SQLITE_OK ){ + walIndexClose(pRet, 0); + sqlcipher_sqlite3OsClose(pRet->pWalFd); + sqlcipher_sqlite3_free(pRet); }else{ - pPager->doNotSpill |= SPILLFLAG_OFF; + int iDC = sqlcipher_sqlite3OsDeviceCharacteristics(pDbFd); + if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; } + if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){ + pRet->padToSectorBoundary = 0; + } + *ppWal = pRet; + WALTRACE(("WAL%d: opened\n", pRet)); } + return rc; } -#endif /* -** The following global variable is incremented whenever the library -** attempts to open a temporary file. This information is used for -** testing and analysis only. +** Change the size to which the WAL file is trucated on each reset. */ -#ifdef SQLITE_TEST -SQLITE_API int sqlcipher_sqlite3_opentemp_count = 0; -#endif +SQLITE_PRIVATE void sqlcipher_sqlite3WalLimit(Wal *pWal, i64 iLimit){ + if( pWal ) pWal->mxWalSize = iLimit; +} /* -** Open a temporary file. -** -** Write the file descriptor into *pFile. Return SQLITE_OK on success -** or some other error code if we fail. The OS will automatically -** delete the temporary file when it is closed. -** -** The flags passed to the VFS layer xOpen() call are those specified -** by parameter vfsFlags ORed with the following: +** Find the smallest page number out of all pages held in the WAL that +** has not been returned by any prior invocation of this method on the +** same WalIterator object. Write into *piFrame the frame index where +** that page was last written into the WAL. Write into *piPage the page +** number. ** -** SQLITE_OPEN_READWRITE -** SQLITE_OPEN_CREATE -** SQLITE_OPEN_EXCLUSIVE -** SQLITE_OPEN_DELETEONCLOSE +** Return 0 on success. If there are no pages in the WAL with a page +** number larger than *piPage, then return 1. */ -static int pagerOpentemp( - Pager *pPager, /* The pager object */ - sqlcipher_sqlite3_file *pFile, /* Write the file descriptor here */ - int vfsFlags /* Flags passed through to the VFS */ +static int walIteratorNext( + WalIterator *p, /* Iterator */ + u32 *piPage, /* OUT: The page number of the next page */ + u32 *piFrame /* OUT: Wal frame index of next page */ ){ - int rc; /* Return code */ - -#ifdef SQLITE_TEST - sqlcipher_sqlite3_opentemp_count++; /* Used for testing and analysis only */ -#endif + u32 iMin; /* Result pgno must be greater than iMin */ + u32 iRet = 0xFFFFFFFF; /* 0xffffffff is never a valid page number */ + int i; /* For looping through segments */ - vfsFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | - SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE; - rc = sqlcipher_sqlite3OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0); - assert( rc!=SQLITE_OK || isOpen(pFile) ); - return rc; -} + iMin = p->iPrior; + assert( iMin<0xffffffff ); + for(i=p->nSegment-1; i>=0; i--){ + struct WalSegment *pSegment = &p->aSegment[i]; + while( pSegment->iNextnEntry ){ + u32 iPg = pSegment->aPgno[pSegment->aIndex[pSegment->iNext]]; + if( iPg>iMin ){ + if( iPgiZero + pSegment->aIndex[pSegment->iNext]; + } + break; + } + pSegment->iNext++; + } + } -/* -** Set the busy handler function. -** -** The pager invokes the busy-handler if sqlcipher_sqlite3OsLock() returns -** SQLITE_BUSY when trying to upgrade from no-lock to a SHARED lock, -** or when trying to upgrade from a RESERVED lock to an EXCLUSIVE -** lock. It does *not* invoke the busy handler when upgrading from -** SHARED to RESERVED, or when upgrading from SHARED to EXCLUSIVE -** (which occurs during hot-journal rollback). Summary: -** -** Transition | Invokes xBusyHandler -** -------------------------------------------------------- -** NO_LOCK -> SHARED_LOCK | Yes -** SHARED_LOCK -> RESERVED_LOCK | No -** SHARED_LOCK -> EXCLUSIVE_LOCK | No -** RESERVED_LOCK -> EXCLUSIVE_LOCK | Yes -** -** If the busy-handler callback returns non-zero, the lock is -** retried. If it returns zero, then the SQLITE_BUSY error is -** returned to the caller of the pager API function. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerSetBusyHandler( - Pager *pPager, /* Pager object */ - int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ - void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ -){ - void **ap; - pPager->xBusyHandler = xBusyHandler; - pPager->pBusyHandlerArg = pBusyHandlerArg; - ap = (void **)&pPager->xBusyHandler; - assert( ((int(*)(void *))(ap[0]))==xBusyHandler ); - assert( ap[1]==pBusyHandlerArg ); - sqlcipher_sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap); + *piPage = p->iPrior = iRet; + return (iRet==0xFFFFFFFF); } /* -** Change the page size used by the Pager object. The new page size -** is passed in *pPageSize. -** -** If the pager is in the error state when this function is called, it -** is a no-op. The value returned is the error state error code (i.e. -** one of SQLITE_IOERR, an SQLITE_IOERR_xxx sub-code or SQLITE_FULL). -** -** Otherwise, if all of the following are true: +** This function merges two sorted lists into a single sorted list. ** -** * the new page size (value of *pPageSize) is valid (a power -** of two between 512 and SQLITE_MAX_PAGE_SIZE, inclusive), and +** aLeft[] and aRight[] are arrays of indices. The sort key is +** aContent[aLeft[]] and aContent[aRight[]]. Upon entry, the following +** is guaranteed for all J=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) ); - if( (pPager->memDb==0 || pPager->dbSize==0) - && sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)==0 - && pageSize && pageSize!=(u32)pPager->pageSize - ){ - char *pNew = NULL; /* New temp space */ - i64 nByte = 0; +static void walMerge( + const u32 *aContent, /* Pages in wal - keys for the sort */ + ht_slot *aLeft, /* IN: Left hand input list */ + int nLeft, /* IN: Elements in array *paLeft */ + ht_slot **paRight, /* IN/OUT: Right hand input list */ + int *pnRight, /* IN/OUT: Elements in *paRight */ + ht_slot *aTmp /* Temporary buffer */ +){ + int iLeft = 0; /* Current index in aLeft */ + int iRight = 0; /* Current index in aRight */ + int iOut = 0; /* Current index in output buffer */ + int nRight = *pnRight; + ht_slot *aRight = *paRight; - if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){ - rc = sqlcipher_sqlite3OsFileSize(pPager->fd, &nByte); - } - if( rc==SQLITE_OK ){ - /* 8 bytes of zeroed overrun space is sufficient so that the b-tree - * cell header parser will never run off the end of the allocation */ - pNew = (char *)sqlcipher_sqlite3PageMalloc(pageSize+8); - if( !pNew ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - memset(pNew+pageSize, 0, 8); - } - } + assert( nLeft>0 && nRight>0 ); + while( iRightpPCache, pageSize); - } - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3PageFree(pPager->pTmpSpace); - pPager->pTmpSpace = pNew; - pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize); - pPager->pageSize = pageSize; + if( (iLeft=nRight || aContent[aLeft[iLeft]]pageSize; - if( rc==SQLITE_OK ){ - if( nReserve<0 ) nReserve = pPager->nReserve; - assert( nReserve>=0 && nReserve<1000 ); - pPager->nReserve = (i16)nReserve; - pagerReportSize(pPager); - pagerFixMaplimit(pPager); + aTmp[iOut++] = logpage; + if( iLeft=nLeft || aContent[aLeft[iLeft]]>dbpage ); + assert( iRight>=nRight || aContent[aRight[iRight]]>dbpage ); } - return rc; -} -/* -** Return a pointer to the "temporary page" buffer held internally -** by the pager. This is a buffer that is big enough to hold the -** entire content of a database page. This buffer is used internally -** during rollback and will be overwritten whenever a rollback -** occurs. But other modules are free to use it too, as long as -** no rollbacks are happening. -*/ -SQLITE_PRIVATE void *sqlcipher_sqlite3PagerTempSpace(Pager *pPager){ - return pPager->pTmpSpace; + *paRight = aLeft; + *pnRight = iOut; + memcpy(aLeft, aTmp, sizeof(aTmp[0])*iOut); } /* -** Attempt to set the maximum database page count if mxPage is positive. -** Make no changes if mxPage is zero or negative. And never reduce the -** maximum page count below the current size of the database. +** Sort the elements in list aList using aContent[] as the sort key. +** Remove elements with duplicate keys, preferring to keep the +** larger aList[] values. ** -** Regardless of mxPage, return the current maximum page count. -*/ -SQLITE_PRIVATE Pgno sqlcipher_sqlite3PagerMaxPageCount(Pager *pPager, Pgno mxPage){ - if( mxPage>0 ){ - pPager->mxPgno = mxPage; - } - assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */ - /* assert( pPager->mxPgno>=pPager->dbSize ); */ - /* OP_MaxPgcnt ensures that the parameter passed to this function is not - ** less than the total number of valid pages in the database. But this - ** may be less than Pager.dbSize, and so the assert() above is not valid */ - return pPager->mxPgno; -} - -/* -** The following set of routines are used to disable the simulated -** I/O error mechanism. These routines are used to avoid simulated -** errors in places where we do not care about errors. +** The aList[] entries are indices into aContent[]. The values in +** aList[] are to be sorted so that for all Jfd) || pPager->tempFile ); +static void walMergesort( + const u32 *aContent, /* Pages in wal */ + ht_slot *aBuffer, /* Buffer of at least *pnList items to use */ + ht_slot *aList, /* IN/OUT: List to sort */ + int *pnList /* IN/OUT: Number of elements in aList[] */ +){ + struct Sublist { + int nList; /* Number of elements in aList */ + ht_slot *aList; /* Pointer to sub-list content */ + }; - /* This routine is only called by btree immediately after creating - ** the Pager object. There has not been an opportunity to transition - ** to WAL mode yet. - */ - assert( !pagerUseWal(pPager) ); + const int nList = *pnList; /* Size of input list */ + int nMerge = 0; /* Number of elements in list aMerge */ + ht_slot *aMerge = 0; /* List to be merged */ + int iList; /* Index into input list */ + u32 iSub = 0; /* Index into aSub array */ + struct Sublist aSub[13]; /* Array of sub-lists */ - if( isOpen(pPager->fd) ){ - IOTRACE(("DBHDR %p 0 %d\n", pPager, N)) - rc = sqlcipher_sqlite3OsRead(pPager->fd, pDest, N, 0); - if( rc==SQLITE_IOERR_SHORT_READ ){ - rc = SQLITE_OK; + memset(aSub, 0, sizeof(aSub)); + assert( nList<=HASHTABLE_NPAGE && nList>0 ); + assert( HASHTABLE_NPAGE==(1<<(ArraySize(aSub)-1)) ); + + for(iList=0; iListaList && p->nList<=(1<aList==&aList[iList&~((2<aList, p->nList, &aMerge, &nMerge, aBuffer); } + aSub[iSub].aList = aMerge; + aSub[iSub].nList = nMerge; } - return rc; -} -/* -** This function may only be called when a read-transaction is open on -** the pager. It returns the total number of pages in the database. -** -** However, if the file is between 1 and bytes in size, then -** this is considered a 1 page file. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerPagecount(Pager *pPager, int *pnPage){ - assert( pPager->eState>=PAGER_READER ); - assert( pPager->eState!=PAGER_WRITER_FINISHED ); - *pnPage = (int)pPager->dbSize; + for(iSub++; iSubnList<=(1<aList==&aList[nList&~((2<aList, p->nList, &aMerge, &nMerge, aBuffer); + } + } + assert( aMerge==aList ); + *pnList = nMerge; + +#ifdef SQLITE_DEBUG + { + int i; + for(i=1; i<*pnList; i++){ + assert( aContent[aList[i]] > aContent[aList[i-1]] ); + } + } +#endif } +/* +** Free an iterator allocated by walIteratorInit(). +*/ +static void walIteratorFree(WalIterator *p){ + sqlcipher_sqlite3_free(p); +} /* -** Try to obtain a lock of type locktype on the database file. If -** a similar or greater lock is already held, this function is a no-op -** (returning SQLITE_OK immediately). +** Construct a WalInterator object that can be used to loop over all +** pages in the WAL following frame nBackfill in ascending order. Frames +** nBackfill or earlier may be included - excluding them is an optimization +** only. The caller must hold the checkpoint lock. ** -** Otherwise, attempt to obtain the lock using sqlcipher_sqlite3OsLock(). Invoke -** the busy callback if the lock is currently not available. Repeat -** until the busy callback returns false or until the attempt to -** obtain the lock succeeds. +** On success, make *pp point to the newly allocated WalInterator object +** return SQLITE_OK. Otherwise, return an error code. If this routine +** returns an error, the value of *pp is undefined. ** -** Return SQLITE_OK on success and an error code if we cannot obtain -** the lock. If the lock is obtained successfully, set the Pager.state -** variable to locktype before returning. +** The calling routine should invoke walIteratorFree() to destroy the +** WalIterator object when it has finished with it. */ -static int pager_wait_on_lock(Pager *pPager, int locktype){ - int rc; /* Return code */ +static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ + WalIterator *p; /* Return value */ + int nSegment; /* Number of segments to merge */ + u32 iLast; /* Last frame in log */ + sqlcipher_sqlite3_int64 nByte; /* Number of bytes to allocate */ + int i; /* Iterator variable */ + ht_slot *aTmp; /* Temp space used by merge-sort */ + int rc = SQLITE_OK; /* Return Code */ - /* Check that this is either a no-op (because the requested lock is - ** already held), or one of the transitions that the busy-handler - ** may be invoked during, according to the comment above - ** sqlcipher_sqlite3PagerSetBusyhandler(). + /* This routine only runs while holding the checkpoint lock. And + ** it only runs if there is actually content in the log (mxFrame>0). */ - assert( (pPager->eLock>=locktype) - || (pPager->eLock==NO_LOCK && locktype==SHARED_LOCK) - || (pPager->eLock==RESERVED_LOCK && locktype==EXCLUSIVE_LOCK) - ); + assert( pWal->ckptLock && pWal->hdr.mxFrame>0 ); + iLast = pWal->hdr.mxFrame; - do { - rc = pagerLockDb(pPager, locktype); - }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) ); - return rc; -} + /* Allocate space for the WalIterator object. */ + nSegment = walFramePage(iLast) + 1; + nByte = sizeof(WalIterator) + + (nSegment-1)*sizeof(struct WalSegment) + + iLast*sizeof(ht_slot); + p = (WalIterator *)sqlcipher_sqlite3_malloc64(nByte); + if( !p ){ + return SQLITE_NOMEM_BKPT; + } + memset(p, 0, nByte); + p->nSegment = nSegment; -/* -** Function assertTruncateConstraint(pPager) checks that one of the -** following is true for all dirty pages currently in the page-cache: -** -** a) The page number is less than or equal to the size of the -** current database image, in pages, OR -** -** b) if the page content were written at this time, it would not -** be necessary to write the current content out to the sub-journal -** (as determined by function subjRequiresPage()). -** -** If the condition asserted by this function were not true, and the -** dirty page were to be discarded from the cache via the pagerStress() -** routine, pagerStress() would not write the current page content to -** the database file. If a savepoint transaction were rolled back after -** this happened, the correct behavior would be to restore the current -** content of the page. However, since this content is not present in either -** the database file or the portion of the rollback journal and -** sub-journal rolled back the content could not be restored and the -** database image would become corrupt. It is therefore fortunate that -** this circumstance cannot arise. -*/ -#if defined(SQLITE_DEBUG) -static void assertTruncateConstraintCb(PgHdr *pPg){ - assert( pPg->flags&PGHDR_DIRTY ); - assert( !subjRequiresPage(pPg) || pPg->pgno<=pPg->pPager->dbSize ); -} -static void assertTruncateConstraint(Pager *pPager){ - sqlcipher_sqlite3PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb); -} -#else -# define assertTruncateConstraint(pPager) -#endif + /* Allocate temporary space used by the merge-sort routine. This block + ** of memory will be freed before this function returns. + */ + aTmp = (ht_slot *)sqlcipher_sqlite3_malloc64( + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) + ); + if( !aTmp ){ + rc = SQLITE_NOMEM_BKPT; + } -/* -** Truncate the in-memory database file image to nPage pages. This -** function does not actually modify the database file on disk. It -** just sets the internal state of the pager object so that the -** truncation will be done when the current transaction is committed. -** -** This function is only called right before committing a transaction. -** Once this function has been called, the transaction must either be -** rolled back or committed. It is not safe to call this function and -** then continue writing to the database. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ - assert( pPager->dbSize>=nPage ); - assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); - pPager->dbSize = nPage; + for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && iaSegment[p->nSegment])[sLoc.iZero]; + sLoc.iZero++; -/* -** This function is called before attempting a hot-journal rollback. It -** syncs the journal file to disk, then sets pPager->journalHdr to the -** size of the journal file so that the pager_playback() routine knows -** that the entire journal file has been synced. -** -** Syncing a hot-journal to disk before attempting to roll it back ensures -** that if a power-failure occurs during the rollback, the process that -** attempts rollback following system recovery sees the same journal -** content as this process. -** -** If everything goes as planned, SQLITE_OK is returned. Otherwise, -** an SQLite error code. -*/ -static int pagerSyncHotJournal(Pager *pPager){ - int rc = SQLITE_OK; - if( !pPager->noSync ){ - rc = sqlcipher_sqlite3OsSync(pPager->jfd, SQLITE_SYNC_NORMAL); + for(j=0; jaSegment[i].iZero = sLoc.iZero; + p->aSegment[i].nEntry = nEntry; + p->aSegment[i].aIndex = aIndex; + p->aSegment[i].aPgno = (u32 *)sLoc.aPgno; + } } - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3OsFileSize(pPager->jfd, &pPager->journalHdr); + sqlcipher_sqlite3_free(aTmp); + + if( rc!=SQLITE_OK ){ + walIteratorFree(p); + p = 0; } + *pp = p; return rc; } -#if SQLITE_MAX_MMAP_SIZE>0 +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT /* -** Obtain a reference to a memory mapped page object for page number pgno. -** The new object will use the pointer pData, obtained from xFetch(). -** If successful, set *ppPage to point to the new page reference -** and return SQLITE_OK. Otherwise, return an SQLite error code and set -** *ppPage to zero. -** -** Page references obtained by calling this function should be released -** by calling pagerReleaseMapPage(). +** Attempt to enable blocking locks. Blocking locks are enabled only if (a) +** they are supported by the VFS, and (b) the database handle is configured +** with a busy-timeout. Return 1 if blocking locks are successfully enabled, +** or 0 otherwise. */ -static int pagerAcquireMapPage( - Pager *pPager, /* Pager object */ - Pgno pgno, /* Page number */ - void *pData, /* xFetch()'d data for this page */ - PgHdr **ppPage /* OUT: Acquired page object */ -){ - PgHdr *p; /* Memory mapped page to return */ - - if( pPager->pMmapFreelist ){ - *ppPage = p = pPager->pMmapFreelist; - pPager->pMmapFreelist = p->pDirty; - p->pDirty = 0; - assert( pPager->nExtra>=8 ); - memset(p->pExtra, 0, 8); - }else{ - *ppPage = p = (PgHdr *)sqlcipher_sqlite3MallocZero(sizeof(PgHdr) + pPager->nExtra); - if( p==0 ){ - sqlcipher_sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pData); - return SQLITE_NOMEM_BKPT; +static int walEnableBlocking(Wal *pWal){ + int res = 0; + if( pWal->db ){ + int tmout = pWal->db->busyTimeout; + if( tmout ){ + int rc; + rc = sqlcipher_sqlite3OsFileControl( + pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout + ); + res = (rc==SQLITE_OK); } - p->pExtra = (void *)&p[1]; - p->flags = PGHDR_MMAP; - p->nRef = 1; - p->pPager = pPager; } - - assert( p->pExtra==(void *)&p[1] ); - assert( p->pPage==0 ); - assert( p->flags==PGHDR_MMAP ); - assert( p->pPager==pPager ); - assert( p->nRef==1 ); - - p->pgno = pgno; - p->pData = pData; - pPager->nMmapOut++; - - return SQLITE_OK; + return res; } -#endif /* -** Release a reference to page pPg. pPg must have been returned by an -** earlier call to pagerAcquireMapPage(). +** Disable blocking locks. */ -static void pagerReleaseMapPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - pPager->nMmapOut--; - pPg->pDirty = pPager->pMmapFreelist; - pPager->pMmapFreelist = pPg; - - assert( pPager->fd->pMethods->iVersion>=3 ); - sqlcipher_sqlite3OsUnfetch(pPager->fd, (i64)(pPg->pgno-1)*pPager->pageSize, pPg->pData); +static void walDisableBlocking(Wal *pWal){ + int tmout = 0; + sqlcipher_sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout); } /* -** Free all PgHdr objects stored in the Pager.pMmapFreelist list. +** If parameter bLock is true, attempt to enable blocking locks, take +** the WRITER lock, and then disable blocking locks. If blocking locks +** cannot be enabled, no attempt to obtain the WRITER lock is made. Return +** an SQLite error code if an error occurs, or SQLITE_OK otherwise. It is not +** an error if blocking locks can not be enabled. +** +** If the bLock parameter is false and the WRITER lock is held, release it. */ -static void pagerFreeMapHdrs(Pager *pPager){ - PgHdr *p; - PgHdr *pNext; - for(p=pPager->pMmapFreelist; p; p=pNext){ - pNext = p->pDirty; - sqlcipher_sqlite3_free(p); +SQLITE_PRIVATE int sqlcipher_sqlite3WalWriteLock(Wal *pWal, int bLock){ + int rc = SQLITE_OK; + assert( pWal->readLock<0 || bLock==0 ); + if( bLock ){ + assert( pWal->db ); + if( walEnableBlocking(pWal) ){ + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); + if( rc==SQLITE_OK ){ + pWal->writeLock = 1; + } + walDisableBlocking(pWal); + } + }else if( pWal->writeLock ){ + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); + pWal->writeLock = 0; } + return rc; } -/* Verify that the database file has not be deleted or renamed out from -** under the pager. Return SQLITE_OK if the database is still where it ought -** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error -** code from sqlcipher_sqlite3OsAccess()) if the database has gone missing. +/* +** Set the database handle used to determine if blocking locks are required. */ -static int databaseIsUnmoved(Pager *pPager){ - int bHasMoved = 0; - int rc; +SQLITE_PRIVATE void sqlcipher_sqlite3WalDb(Wal *pWal, sqlcipher_sqlite3 *db){ + pWal->db = db; +} - if( pPager->tempFile ) return SQLITE_OK; - if( pPager->dbSize==0 ) return SQLITE_OK; - assert( pPager->zFilename && pPager->zFilename[0] ); - rc = sqlcipher_sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved); - if( rc==SQLITE_NOTFOUND ){ - /* If the HAS_MOVED file-control is unimplemented, assume that the file - ** has not been moved. That is the historical behavior of SQLite: prior to - ** version 3.8.3, it never checked */ - rc = SQLITE_OK; - }else if( rc==SQLITE_OK && bHasMoved ){ - rc = SQLITE_READONLY_DBMOVED; - } +/* +** Take an exclusive WRITE lock. Blocking if so configured. +*/ +static int walLockWriter(Wal *pWal){ + int rc; + walEnableBlocking(pWal); + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); + walDisableBlocking(pWal); return rc; } +#else +# define walEnableBlocking(x) 0 +# define walDisableBlocking(x) +# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1) +# define sqlcipher_sqlite3WalDb(pWal, db) +#endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */ /* -** Shutdown the page cache. Free all memory and close all files. -** -** If a transaction was in progress when this routine is called, that -** transaction is rolled back. All outstanding pages are invalidated -** and their memory is freed. Any attempt to use a page associated -** with this page cache after this function returns will likely -** result in a coredump. -** -** This function always succeeds. If a transaction is active an attempt -** is made to roll it back. If an error occurs during the rollback -** a hot journal may be left in the filesystem but no error is returned -** to the caller. +** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and +** n. If the attempt fails and parameter xBusy is not NULL, then it is a +** busy-handler function. Invoke it and retry the lock until either the +** lock is successfully obtained or the busy-handler returns 0. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerClose(Pager *pPager, sqlcipher_sqlite3 *db){ - u8 *pTmp = (u8*)pPager->pTmpSpace; - assert( db || pagerUseWal(pPager)==0 ); - assert( assert_pager_state(pPager) ); - disable_simulated_io_errors(); - sqlcipher_sqlite3BeginBenignMalloc(); - pagerFreeMapHdrs(pPager); - /* pPager->errCode = 0; */ - pPager->exclusiveMode = 0; -#ifndef SQLITE_OMIT_WAL - { - u8 *a = 0; - assert( db || pPager->pWal==0 ); - if( db && 0==(db->flags & SQLITE_NoCkptOnClose) - && SQLITE_OK==databaseIsUnmoved(pPager) - ){ - a = pTmp; - } - sqlcipher_sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a); - pPager->pWal = 0; - } -#endif - pager_reset(pPager); - if( MEMDB ){ - pager_unlock(pPager); - }else{ - /* If it is open, sync the journal file before calling UnlockAndRollback. - ** If this is not done, then an unsynced portion of the open journal - ** file may be played back into the database. If a power failure occurs - ** while this is happening, the database could become corrupt. - ** - ** If an error occurs while trying to sync the journal, shift the pager - ** into the ERROR state. This causes UnlockAndRollback to unlock the - ** database and close the journal file without attempting to roll it - ** back or finalize it. The next database user will have to do hot-journal - ** rollback before accessing the database file. - */ - if( isOpen(pPager->jfd) ){ - pager_error(pPager, pagerSyncHotJournal(pPager)); - } - pagerUnlockAndRollback(pPager); +static int walBusyLock( + Wal *pWal, /* WAL connection */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ + int lockIdx, /* Offset of first byte to lock */ + int n /* Number of bytes to lock */ +){ + int rc; + do { + rc = walLockExclusive(pWal, lockIdx, n); + }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( rc==SQLITE_BUSY_TIMEOUT ){ + walDisableBlocking(pWal); + rc = SQLITE_BUSY; } - sqlcipher_sqlite3EndBenignMalloc(); - enable_simulated_io_errors(); - PAGERTRACE(("CLOSE %d\n", PAGERID(pPager))); - IOTRACE(("CLOSE %p\n", pPager)) - sqlcipher_sqlite3OsClose(pPager->jfd); - sqlcipher_sqlite3OsClose(pPager->fd); - sqlcipher_sqlite3PageFree(pTmp); - sqlcipher_sqlite3PcacheClose(pPager->pPCache); - -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); #endif -/* END SQLCIPHER */ - - assert( !pPager->aSavepoint && !pPager->pInJournal ); - assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) ); - - sqlcipher_sqlite3_free(pPager); - return SQLITE_OK; + return rc; } -#if !defined(NDEBUG) || defined(SQLITE_TEST) /* -** Return the page number for page pPg. +** The cache of the wal-index header must be valid to call this function. +** Return the page-size in bytes used by the database. */ -SQLITE_PRIVATE Pgno sqlcipher_sqlite3PagerPagenumber(DbPage *pPg){ - return pPg->pgno; +static int walPagesize(Wal *pWal){ + return (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); } -#endif /* -** Increment the reference count for page pPg. +** The following is guaranteed when this function is called: +** +** a) the WRITER lock is held, +** b) the entire log file has been checkpointed, and +** c) any existing readers are reading exclusively from the database +** file - there are no readers that may attempt to read a frame from +** the log file. +** +** This function updates the shared-memory structures so that the next +** client to write to the database (which may be this one) does so by +** writing frames into the start of the log file. +** +** The value of parameter salt1 is used as the aSalt[1] value in the +** new wal-index header. It should be passed a pseudo-random value (i.e. +** one obtained from sqlcipher_sqlite3_randomness()). */ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerRef(DbPage *pPg){ - sqlcipher_sqlite3PcacheRef(pPg); +static void walRestartHdr(Wal *pWal, u32 salt1){ + volatile WalCkptInfo *pInfo = walCkptInfo(pWal); + int i; /* Loop counter */ + u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */ + pWal->nCkpt++; + pWal->hdr.mxFrame = 0; + sqlcipher_sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlcipher_sqlite3Get4byte((u8*)&aSalt[0])); + memcpy(&pWal->hdr.aSalt[1], &salt1, 4); + walIndexWriteHdr(pWal); + AtomicStore(&pInfo->nBackfill, 0); + pInfo->nBackfillAttempted = 0; + pInfo->aReadMark[1] = 0; + for(i=2; iaReadMark[i] = READMARK_NOT_USED; + assert( pInfo->aReadMark[0]==0 ); } /* -** Sync the journal. In other words, make sure all the pages that have -** been written to the journal have actually reached the surface of the -** disk and can be restored in the event of a hot-journal rollback. -** -** If the Pager.noSync flag is set, then this function is a no-op. -** Otherwise, the actions required depend on the journal-mode and the -** device characteristics of the file-system, as follows: +** Copy as much content as we can from the WAL back into the database file +** in response to an sqlcipher_sqlite3_wal_checkpoint() request or the equivalent. ** -** * If the journal file is an in-memory journal file, no action need -** be taken. +** The amount of information copies from WAL to database might be limited +** by active readers. This routine will never overwrite a database page +** that a concurrent reader might be using. ** -** * Otherwise, if the device does not support the SAFE_APPEND property, -** then the nRec field of the most recently written journal header -** is updated to contain the number of journal records that have -** been written following it. If the pager is operating in full-sync -** mode, then the journal file is synced before this field is updated. +** All I/O barrier operations (a.k.a fsyncs) occur in this routine when +** SQLite is in WAL-mode in synchronous=NORMAL. That means that if +** checkpoints are always run by a background thread or background +** process, foreground threads will never block on a lengthy fsync call. ** -** * If the device does not support the SEQUENTIAL property, then -** journal file is synced. +** Fsync is called on the WAL before writing content out of the WAL and +** into the database. This ensures that if the new content is persistent +** in the WAL and can be recovered following a power-loss or hard reset. ** -** Or, in pseudo-code: +** Fsync is also called on the database file if (and only if) the entire +** WAL content is copied into the database file. This second fsync makes +** it safe to delete the WAL since the new content will persist in the +** database file. ** -** if( NOT ){ -** if( NOT SAFE_APPEND ){ -** if( ) xSync(); -** -** } -** if( NOT SEQUENTIAL ) xSync(); -** } +** This routine uses and updates the nBackfill field of the wal-index header. +** This is the only routine that will increase the value of nBackfill. +** (A WAL reset or recovery will revert nBackfill to zero, but not increase +** its value.) ** -** If successful, this routine clears the PGHDR_NEED_SYNC flag of every -** page currently held in memory before returning SQLITE_OK. If an IO -** error is encountered, then the IO error code is returned to the caller. +** The caller must be holding sufficient locks to ensure that no other +** checkpoint is running (in any other thread or process) at the same +** time. */ -static int syncJournal(Pager *pPager, int newHdr){ - int rc; /* Return code */ +static int walCheckpoint( + Wal *pWal, /* Wal connection */ + sqlcipher_sqlite3 *db, /* Check for interrupts on this handle */ + int eMode, /* One of PASSIVE, FULL or RESTART */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ + int sync_flags, /* Flags for OsSync() (or 0) */ + u8 *zBuf /* Temporary buffer to use */ +){ + int rc = SQLITE_OK; /* Return code */ + int szPage; /* Database page-size */ + WalIterator *pIter = 0; /* Wal iterator context */ + u32 iDbpage = 0; /* Next database page to write */ + u32 iFrame = 0; /* Wal frame containing data for iDbpage */ + u32 mxSafeFrame; /* Max frame that can be backfilled */ + u32 mxPage; /* Max database page to write */ + int i; /* Loop counter */ + volatile WalCkptInfo *pInfo; /* The checkpoint status information */ - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); - assert( !pagerUseWal(pPager) ); + szPage = walPagesize(pWal); + testcase( szPage<=32768 ); + testcase( szPage>=65536 ); + pInfo = walCkptInfo(pWal); + if( pInfo->nBackfillhdr.mxFrame ){ - rc = sqlcipher_sqlite3PagerExclusiveLock(pPager); - if( rc!=SQLITE_OK ) return rc; + /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked + ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ + assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); - if( !pPager->noSync ){ - assert( !pPager->tempFile ); - if( isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){ - const int iDc = sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd); - assert( isOpen(pPager->jfd) ); + /* Compute in mxSafeFrame the index of the last frame of the WAL that is + ** safe to write into the database. Frames beyond mxSafeFrame might + ** overwrite database pages that are in use by active readers and thus + ** cannot be backfilled from the WAL. + */ + mxSafeFrame = pWal->hdr.mxFrame; + mxPage = pWal->hdr.nPage; + for(i=1; iaReadMark+i); + if( mxSafeFrame>y ){ + assert( y<=pWal->hdr.mxFrame ); + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); + if( rc==SQLITE_OK ){ + u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED); + AtomicStore(pInfo->aReadMark+i, iMark); + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); + }else if( rc==SQLITE_BUSY ){ + mxSafeFrame = y; + xBusy = 0; + }else{ + goto walcheckpoint_out; + } + } + } - if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ - /* This block deals with an obscure problem. If the last connection - ** that wrote to this database was operating in persistent-journal - ** mode, then the journal file may at this point actually be larger - ** than Pager.journalOff bytes. If the next thing in the journal - ** file happens to be a journal-header (written as part of the - ** previous connection's transaction), and a crash or power-failure - ** occurs after nRec is updated but before this connection writes - ** anything else to the journal file (or commits/rolls back its - ** transaction), then SQLite may become confused when doing the - ** hot-journal rollback following recovery. It may roll back all - ** of this connections data, then proceed to rolling back the old, - ** out-of-date data that follows it. Database corruption. - ** - ** To work around this, if the journal file does appear to contain - ** a valid header following Pager.journalOff, then write a 0x00 - ** byte to the start of it to prevent it from being recognized. - ** - ** Variable iNextHdrOffset is set to the offset at which this - ** problematic header will occur, if it exists. aMagic is used - ** as a temporary buffer to inspect the first couple of bytes of - ** the potential journal header. - */ - i64 iNextHdrOffset; - u8 aMagic[8]; - u8 zHeader[sizeof(aJournalMagic)+4]; + /* Allocate the iterator */ + if( pInfo->nBackfillnBackfill, &pIter); + assert( rc==SQLITE_OK || pIter==0 ); + } - memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); - put32bits(&zHeader[sizeof(aJournalMagic)], pPager->nRec); + if( pIter + && (rc = walBusyLock(pWal,xBusy,pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK + ){ + u32 nBackfill = pInfo->nBackfill; - iNextHdrOffset = journalHdrOffset(pPager); - rc = sqlcipher_sqlite3OsRead(pPager->jfd, aMagic, 8, iNextHdrOffset); - if( rc==SQLITE_OK && 0==memcmp(aMagic, aJournalMagic, 8) ){ - static const u8 zerobyte = 0; - rc = sqlcipher_sqlite3OsWrite(pPager->jfd, &zerobyte, 1, iNextHdrOffset); - } - if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ - return rc; - } + pInfo->nBackfillAttempted = mxSafeFrame; - /* Write the nRec value into the journal file header. If in - ** full-synchronous mode, sync the journal first. This ensures that - ** all data has really hit the disk before nRec is updated to mark - ** it as a candidate for rollback. - ** - ** This is not required if the persistent media supports the - ** SAFE_APPEND property. Because in this case it is not possible - ** for garbage data to be appended to the file, the nRec field - ** is populated with 0xFFFFFFFF when the journal header is written - ** and never needs to be updated. - */ - if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ - PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); - IOTRACE(("JSYNC %p\n", pPager)) - rc = sqlcipher_sqlite3OsSync(pPager->jfd, pPager->syncFlags); - if( rc!=SQLITE_OK ) return rc; + /* Sync the WAL to disk */ + rc = sqlcipher_sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); + + /* If the database may grow as a result of this checkpoint, hint + ** about the eventual size of the db file to the VFS layer. + */ + if( rc==SQLITE_OK ){ + i64 nReq = ((i64)mxPage * szPage); + i64 nSize; /* Current size of database file */ + sqlcipher_sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0); + rc = sqlcipher_sqlite3OsFileSize(pWal->pDbFd, &nSize); + if( rc==SQLITE_OK && nSizehdr.mxFrame*szPage)pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq); + } } - IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr)); - rc = sqlcipher_sqlite3OsWrite( - pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr - ); - if( rc!=SQLITE_OK ) return rc; + } - if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ - PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); - IOTRACE(("JSYNC %p\n", pPager)) - rc = sqlcipher_sqlite3OsSync(pPager->jfd, pPager->syncFlags| - (pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0) - ); - if( rc!=SQLITE_OK ) return rc; + + /* Iterate through the contents of the WAL, copying data to the db file */ + while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ + i64 iOffset; + assert( walFramePgno(pWal, iFrame)==iDbpage ); + if( AtomicLoad(&db->u1.isInterrupted) ){ + rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; + break; + } + if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){ + continue; + } + iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; + /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ + rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); + if( rc!=SQLITE_OK ) break; + iOffset = (iDbpage-1)*(i64)szPage; + testcase( IS_BIG_INT(iOffset) ); + rc = sqlcipher_sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); + if( rc!=SQLITE_OK ) break; } + sqlcipher_sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0); - pPager->journalHdr = pPager->journalOff; - if( newHdr && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ - pPager->nRec = 0; - rc = writeJournalHdr(pPager); - if( rc!=SQLITE_OK ) return rc; + /* If work was actually accomplished... */ + if( rc==SQLITE_OK ){ + if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ + i64 szDb = pWal->hdr.nPage*(i64)szPage; + testcase( IS_BIG_INT(szDb) ); + rc = sqlcipher_sqlite3OsTruncate(pWal->pDbFd, szDb); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags)); + } + } + if( rc==SQLITE_OK ){ + AtomicStore(&pInfo->nBackfill, mxSafeFrame); + } } - }else{ - pPager->journalHdr = pPager->journalOff; + + /* Release the reader lock held while backfilling */ + walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); + } + + if( rc==SQLITE_BUSY ){ + /* Reset the return code so as not to report a checkpoint failure + ** just because there are active readers. */ + rc = SQLITE_OK; } } - /* Unless the pager is in noSync mode, the journal file was just - ** successfully synced. Either way, clear the PGHDR_NEED_SYNC flag on - ** all pages. + /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the + ** entire wal file has been copied into the database file, then block + ** until all readers have finished using the wal file. This ensures that + ** the next process to write to the database restarts the wal file. */ - sqlcipher_sqlite3PcacheClearSyncFlags(pPager->pPCache); - pPager->eState = PAGER_WRITER_DBMOD; - assert( assert_pager_state(pPager) ); - return SQLITE_OK; + if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){ + assert( pWal->writeLock ); + if( pInfo->nBackfillhdr.mxFrame ){ + rc = SQLITE_BUSY; + }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ + u32 salt1; + sqlcipher_sqlite3_randomness(4, &salt1); + assert( pInfo->nBackfill==pWal->hdr.mxFrame ); + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); + if( rc==SQLITE_OK ){ + if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){ + /* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as + ** SQLITE_CHECKPOINT_RESTART with the addition that it also + ** truncates the log file to zero bytes just prior to a + ** successful return. + ** + ** In theory, it might be safe to do this without updating the + ** wal-index header in shared memory, as all subsequent reader or + ** writer clients should see that the entire log file has been + ** checkpointed and behave accordingly. This seems unsafe though, + ** as it would leave the system in a state where the contents of + ** the wal-index header do not match the contents of the + ** file-system. To avoid this, update the wal-index header to + ** indicate that the log file contains zero valid frames. */ + walRestartHdr(pWal, salt1); + rc = sqlcipher_sqlite3OsTruncate(pWal->pWalFd, 0); + } + walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); + } + } + } + + walcheckpoint_out: + walIteratorFree(pIter); + return rc; } /* -** The argument is the first in a linked list of dirty pages connected -** by the PgHdr.pDirty pointer. This function writes each one of the -** in-memory pages in the list to the database file. The argument may -** be NULL, representing an empty list. In this case this function is -** a no-op. -** -** The pager must hold at least a RESERVED lock when this function -** is called. Before writing anything to the database file, this lock -** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained, -** SQLITE_BUSY is returned and no data is written to the database file. -** -** If the pager is a temp-file pager and the actual file-system file -** is not yet open, it is created and opened before any data is -** written out. -** -** Once the lock has been upgraded and, if necessary, the file opened, -** the pages are written out to the database file in list order. Writing -** a page is skipped if it meets either of the following criteria: -** -** * The page number is greater than Pager.dbSize, or -** * The PGHDR_DONT_WRITE flag is set on the page. -** -** If writing out a page causes the database file to grow, Pager.dbFileSize -** is updated accordingly. If page 1 is written out, then the value cached -** in Pager.dbFileVers[] is updated to match the new value stored in -** the database file. -** -** If everything is successful, SQLITE_OK is returned. If an IO error -** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot -** be obtained, SQLITE_BUSY is returned. +** If the WAL file is currently larger than nMax bytes in size, truncate +** it to exactly nMax bytes. If an error occurs while doing so, ignore it. */ -static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ - int rc = SQLITE_OK; /* Return code */ - - /* This function is only called for rollback pagers in WRITER_DBMOD state. */ - assert( !pagerUseWal(pPager) ); - assert( pPager->tempFile || pPager->eState==PAGER_WRITER_DBMOD ); - assert( pPager->eLock==EXCLUSIVE_LOCK ); - assert( isOpen(pPager->fd) || pList->pDirty==0 ); - - /* If the file is a temp-file has not yet been opened, open it now. It - ** is not possible for rc to be other than SQLITE_OK if this branch - ** is taken, as pager_wait_on_lock() is a no-op for temp-files. - */ - if( !isOpen(pPager->fd) ){ - assert( pPager->tempFile && rc==SQLITE_OK ); - rc = pagerOpentemp(pPager, pPager->fd, pPager->vfsFlags); +static void walLimitSize(Wal *pWal, i64 nMax){ + i64 sz; + int rx; + sqlcipher_sqlite3BeginBenignMalloc(); + rx = sqlcipher_sqlite3OsFileSize(pWal->pWalFd, &sz); + if( rx==SQLITE_OK && (sz > nMax ) ){ + rx = sqlcipher_sqlite3OsTruncate(pWal->pWalFd, nMax); } - - /* Before the first write, give the VFS a hint of what the final - ** file size will be. - */ - assert( rc!=SQLITE_OK || isOpen(pPager->fd) ); - if( rc==SQLITE_OK - && pPager->dbHintSizedbSize - && (pList->pDirty || pList->pgno>pPager->dbHintSize) - ){ - sqlcipher_sqlite3_int64 szFile = pPager->pageSize * (sqlcipher_sqlite3_int64)pPager->dbSize; - sqlcipher_sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); - pPager->dbHintSize = pPager->dbSize; + sqlcipher_sqlite3EndBenignMalloc(); + if( rx ){ + sqlcipher_sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName); } +} - while( rc==SQLITE_OK && pList ){ - Pgno pgno = pList->pgno; +/* +** Close a connection to a log file. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3WalClose( + Wal *pWal, /* Wal to close */ + sqlcipher_sqlite3 *db, /* For interrupt flag */ + int sync_flags, /* Flags to pass to OsSync() (or 0) */ + int nBuf, + u8 *zBuf /* Buffer of at least nBuf bytes */ +){ + int rc = SQLITE_OK; + if( pWal ){ + int isDelete = 0; /* True to unlink wal and wal-index files */ - /* If there are dirty pages in the page cache with page numbers greater - ** than Pager.dbSize, this means sqlcipher_sqlite3PagerTruncateImage() was called to - ** make the file smaller (presumably by auto-vacuum code). Do not write - ** any such pages to the file. + /* If an EXCLUSIVE lock can be obtained on the database file (using the + ** ordinary, rollback-mode locking methods, this guarantees that the + ** connection associated with this log file is the only connection to + ** the database. In this case checkpoint the database and unlink both + ** the wal and wal-index files. ** - ** Also, do not write out any page that has the PGHDR_DONT_WRITE flag - ** set (set by sqlcipher_sqlite3PagerDontWrite()). + ** The EXCLUSIVE lock is not released before returning. */ - if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){ - i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */ - char *pData; /* Data to write */ - - assert( (pList->flags&PGHDR_NEED_SYNC)==0 ); - if( pList->pgno==1 ) pager_write_changecounter(pList); - - /* Encode the database */ - CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM_BKPT, pData); - - /* Write out the page data. */ - rc = sqlcipher_sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset); - - /* If page 1 was just written, update Pager.dbFileVers to match - ** the value now stored in the database file. If writing this - ** page caused the database file to grow, update dbFileSize. - */ - if( pgno==1 ){ - memcpy(&pPager->dbFileVers, &pData[24], sizeof(pPager->dbFileVers)); + if( zBuf!=0 + && SQLITE_OK==(rc = sqlcipher_sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE)) + ){ + if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ + pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } - if( pgno>pPager->dbFileSize ){ - pPager->dbFileSize = pgno; + rc = sqlcipher_sqlite3WalCheckpoint(pWal, db, + SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0 + ); + if( rc==SQLITE_OK ){ + int bPersist = -1; + sqlcipher_sqlite3OsFileControlHint( + pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist + ); + if( bPersist!=1 ){ + /* Try to delete the WAL file if the checkpoint completed and + ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal + ** mode (!bPersist) */ + isDelete = 1; + }else if( pWal->mxWalSize>=0 ){ + /* Try to truncate the WAL file to zero bytes if the checkpoint + ** completed and fsynced (rc==SQLITE_OK) and we are in persistent + ** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a + ** non-negative value (pWal->mxWalSize>=0). Note that we truncate + ** to zero bytes as truncating to the journal_size_limit might + ** leave a corrupt WAL file on disk. */ + walLimitSize(pWal, 0); + } } - pPager->aStat[PAGER_STAT_WRITE]++; - - /* Update any backup objects copying the contents of this pager. */ - sqlcipher_sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)pList->pData); + } - PAGERTRACE(("STORE %d page %d hash(%08x)\n", - PAGERID(pPager), pgno, pager_pagehash(pList))); - IOTRACE(("PGOUT %p %d\n", pPager, pgno)); - PAGER_INCR(sqlcipher_sqlite3_pager_writedb_count); - }else{ - PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno)); + walIndexClose(pWal, isDelete); + sqlcipher_sqlite3OsClose(pWal->pWalFd); + if( isDelete ){ + sqlcipher_sqlite3BeginBenignMalloc(); + sqlcipher_sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0); + sqlcipher_sqlite3EndBenignMalloc(); } - pager_set_pagehash(pList); - pList = pList->pDirty; + WALTRACE(("WAL%p: closed\n", pWal)); + sqlcipher_sqlite3_free((void *)pWal->apWiData); + sqlcipher_sqlite3_free(pWal); } - return rc; } /* -** Ensure that the sub-journal file is open. If it is already open, this -** function is a no-op. +** Try to read the wal-index header. Return 0 on success and 1 if +** there is a problem. ** -** SQLITE_OK is returned if everything goes according to plan. An -** SQLITE_IOERR_XXX error code is returned if a call to sqlcipher_sqlite3OsOpen() -** fails. +** The wal-index is in shared memory. Another thread or process might +** be writing the header at the same time this procedure is trying to +** read it, which might result in inconsistency. A dirty read is detected +** by verifying that both copies of the header are the same and also by +** a checksum on the header. +** +** If and only if the read is consistent and the header is different from +** pWal->hdr, then pWal->hdr is updated to the content of the new header +** and *pChanged is set to 1. +** +** If the checksum cannot be verified return non-zero. If the header +** is read successfully and the checksum verified, return zero. */ -static int openSubJournal(Pager *pPager){ - int rc = SQLITE_OK; - if( !isOpen(pPager->sjfd) ){ - const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE - | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE - | SQLITE_OPEN_DELETEONCLOSE; - int nStmtSpill = sqlcipher_sqlite3Config.nStmtSpill; - if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){ - nStmtSpill = -1; - } - rc = sqlcipher_sqlite3JournalOpen(pPager->pVfs, 0, pPager->sjfd, flags, nStmtSpill); +static SQLITE_NO_TSAN int walIndexTryHdr(Wal *pWal, int *pChanged){ + u32 aCksum[2]; /* Checksum on the header content */ + WalIndexHdr h1, h2; /* Two copies of the header content */ + WalIndexHdr volatile *aHdr; /* Header in shared memory */ + + /* The first page of the wal-index must be mapped at this point. */ + assert( pWal->nWiData>0 && pWal->apWiData[0] ); + + /* Read the header. This might happen concurrently with a write to the + ** same area of shared memory on a different CPU in a SMP, + ** meaning it is possible that an inconsistent snapshot is read + ** from the file. If this happens, return non-zero. + ** + ** tag-20200519-1: + ** There are two copies of the header at the beginning of the wal-index. + ** When reading, read [0] first then [1]. Writes are in the reverse order. + ** Memory barriers are used to prevent the compiler or the hardware from + ** reordering the reads and writes. TSAN and similar tools can sometimes + ** give false-positive warnings about these accesses because the tools do not + ** account for the double-read and the memory barrier. The use of mutexes + ** here would be problematic as the memory being accessed is potentially + ** shared among multiple processes and not all mutex implementions work + ** reliably in that environment. + */ + aHdr = walIndexHdr(pWal); + memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); /* Possible TSAN false-positive */ + walShmBarrier(pWal); + memcpy(&h2, (void *)&aHdr[1], sizeof(h2)); + + if( memcmp(&h1, &h2, sizeof(h1))!=0 ){ + return 1; /* Dirty read */ } - return rc; + if( h1.isInit==0 ){ + return 1; /* Malformed header - probably all zeros */ + } + walChecksumBytes(1, (u8*)&h1, sizeof(h1)-sizeof(h1.aCksum), 0, aCksum); + if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){ + return 1; /* Checksum does not match */ + } + + if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){ + *pChanged = 1; + memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr)); + pWal->szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); + testcase( pWal->szPage<=32768 ); + testcase( pWal->szPage>=65536 ); + } + + /* The header was successfully read. Return zero. */ + return 0; } /* -** Append a record of the current state of page pPg to the sub-journal. +** This is the value that walTryBeginRead returns when it needs to +** be retried. +*/ +#define WAL_RETRY (-1) + +/* +** Read the wal-index header from the wal-index and into pWal->hdr. +** If the wal-header appears to be corrupt, try to reconstruct the +** wal-index from the WAL before returning. ** -** If successful, set the bit corresponding to pPg->pgno in the bitvecs -** for all open savepoints before returning. +** Set *pChanged to 1 if the wal-index header value in pWal->hdr is +** changed by this operation. If pWal->hdr is unchanged, set *pChanged +** to 0. ** -** This function returns SQLITE_OK if everything is successful, an IO -** error code if the attempt to write to the sub-journal fails, or -** SQLITE_NOMEM if a malloc fails while setting a bit in a savepoint -** bitvec. +** If the wal-index header is successfully read, return SQLITE_OK. +** Otherwise an SQLite error code. */ -static int subjournalPage(PgHdr *pPg){ - int rc = SQLITE_OK; - Pager *pPager = pPg->pPager; - if( pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ +static int walIndexReadHdr(Wal *pWal, int *pChanged){ + int rc; /* Return code */ + int badHdr; /* True if a header read failed */ + volatile u32 *page0; /* Chunk of wal-index containing header */ - /* Open the sub-journal, if it has not already been opened */ - assert( pPager->useJournal ); - assert( isOpen(pPager->jfd) || pagerUseWal(pPager) ); - assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 ); - assert( pagerUseWal(pPager) - || pageInJournal(pPager, pPg) - || pPg->pgno>pPager->dbOrigSize - ); - rc = openSubJournal(pPager); + /* Ensure that page 0 of the wal-index (the page that contains the + ** wal-index header) is mapped. Return early if an error occurs here. + */ + assert( pChanged ); + rc = walIndexPage(pWal, 0, &page0); + if( rc!=SQLITE_OK ){ + assert( rc!=SQLITE_READONLY ); /* READONLY changed to OK in walIndexPage */ + if( rc==SQLITE_READONLY_CANTINIT ){ + /* The SQLITE_READONLY_CANTINIT return means that the shared-memory + ** was openable but is not writable, and this thread is unable to + ** confirm that another write-capable connection has the shared-memory + ** open, and hence the content of the shared-memory is unreliable, + ** since the shared-memory might be inconsistent with the WAL file + ** and there is no writer on hand to fix it. */ + assert( page0==0 ); + assert( pWal->writeLock==0 ); + assert( pWal->readOnly & WAL_SHM_RDONLY ); + pWal->bShmUnreliable = 1; + pWal->exclusiveMode = WAL_HEAPMEMORY_MODE; + *pChanged = 1; + }else{ + return rc; /* Any other non-OK return is just an error */ + } + }else{ + /* page0 can be NULL if the SHM is zero bytes in size and pWal->writeLock + ** is zero, which prevents the SHM from growing */ + testcase( page0!=0 ); + } + assert( page0!=0 || pWal->writeLock==0 ); - /* If the sub-journal was opened successfully (or was already open), - ** write the journal record into the file. */ - if( rc==SQLITE_OK ){ - void *pData = pPg->pData; - i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); - char *pData2; + /* If the first page of the wal-index has been mapped, try to read the + ** wal-index header immediately, without holding any lock. This usually + ** works, but may fail if the wal-index header is corrupt or currently + ** being modified by another thread or process. + */ + badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1); -/* BEGIN SQLCIPHER */ -#if SQLITE_HAS_CODEC - if( !pPager->subjInMemory ){ - CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); - }else -#endif -/* END SQLCIPHER */ - pData2 = pData; - PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); - rc = write32bits(pPager->sjfd, offset, pPg->pgno); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3OsWrite(pPager->sjfd, pData2, pPager->pageSize, offset+4); + /* If the first attempt failed, it might have been due to a race + ** with a writer. So get a WRITE lock and try again. + */ + if( badHdr ){ + if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){ + if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ + walUnlockShared(pWal, WAL_WRITE_LOCK); + rc = SQLITE_READONLY_RECOVERY; + } + }else{ + int bWriteLock = pWal->writeLock; + if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){ + pWal->writeLock = 1; + if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ + badHdr = walIndexTryHdr(pWal, pChanged); + if( badHdr ){ + /* If the wal-index header is still malformed even while holding + ** a WRITE lock, it can only mean that the header is corrupted and + ** needs to be reconstructed. So run recovery to do exactly that. + */ + rc = walIndexRecover(pWal); + *pChanged = 1; + } + } + if( bWriteLock==0 ){ + pWal->writeLock = 0; + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); + } } } } - if( rc==SQLITE_OK ){ - pPager->nSubRec++; - assert( pPager->nSavepoint>0 ); - rc = addToSavepointBitvecs(pPager, pPg->pgno); + + /* If the header is read successfully, check the version number to make + ** sure the wal-index was not constructed with some future format that + ** this version of SQLite cannot understand. + */ + if( badHdr==0 && pWal->hdr.iVersion!=WALINDEX_MAX_VERSION ){ + rc = SQLITE_CANTOPEN_BKPT; } - return rc; -} -static int subjournalPageIfRequired(PgHdr *pPg){ - if( subjRequiresPage(pPg) ){ - return subjournalPage(pPg); - }else{ - return SQLITE_OK; + if( pWal->bShmUnreliable ){ + if( rc!=SQLITE_OK ){ + walIndexClose(pWal, 0); + pWal->bShmUnreliable = 0; + assert( pWal->nWiData>0 && pWal->apWiData[0]==0 ); + /* walIndexRecover() might have returned SHORT_READ if a concurrent + ** writer truncated the WAL out from under it. If that happens, it + ** indicates that a writer has fixed the SHM file for us, so retry */ + if( rc==SQLITE_IOERR_SHORT_READ ) rc = WAL_RETRY; + } + pWal->exclusiveMode = WAL_NORMAL_MODE; } + + return rc; } /* -** This function is called by the pcache layer when it has reached some -** soft memory limit. The first argument is a pointer to a Pager object -** (cast as a void*). The pager is always 'purgeable' (not an in-memory -** database). The second argument is a reference to a page that is -** currently dirty but has no outstanding references. The page -** is always associated with the Pager object passed as the first -** argument. +** Open a transaction in a connection where the shared-memory is read-only +** and where we cannot verify that there is a separate write-capable connection +** on hand to keep the shared-memory up-to-date with the WAL file. ** -** The job of this function is to make pPg clean by writing its contents -** out to the database file, if possible. This may involve syncing the -** journal file. +** This can happen, for example, when the shared-memory is implemented by +** memory-mapping a *-shm file, where a prior writer has shut down and +** left the *-shm file on disk, and now the present connection is trying +** to use that database but lacks write permission on the *-shm file. +** Other scenarios are also possible, depending on the VFS implementation. ** -** If successful, sqlcipher_sqlite3PcacheMakeClean() is called on the page and -** SQLITE_OK returned. If an IO error occurs while trying to make the -** page clean, the IO error code is returned. If the page cannot be -** made clean for some other reason, but no error occurs, then SQLITE_OK -** is returned by sqlcipher_sqlite3PcacheMakeClean() is not called. +** Precondition: +** +** The *-wal file has been read and an appropriate wal-index has been +** constructed in pWal->apWiData[] using heap memory instead of shared +** memory. +** +** If this function returns SQLITE_OK, then the read transaction has +** been successfully opened. In this case output variable (*pChanged) +** is set to true before returning if the caller should discard the +** contents of the page cache before proceeding. Or, if it returns +** WAL_RETRY, then the heap memory wal-index has been discarded and +** the caller should retry opening the read transaction from the +** beginning (including attempting to map the *-shm file). +** +** If an error occurs, an SQLite error code is returned. */ -static int pagerStress(void *p, PgHdr *pPg){ - Pager *pPager = (Pager *)p; - int rc = SQLITE_OK; +static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ + i64 szWal; /* Size of wal file on disk in bytes */ + i64 iOffset; /* Current offset when reading wal file */ + u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ + u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ + int szFrame; /* Number of bytes in buffer aFrame[] */ + u8 *aData; /* Pointer to data part of aFrame buffer */ + volatile void *pDummy; /* Dummy argument for xShmMap */ + int rc; /* Return code */ + u32 aSaveCksum[2]; /* Saved copy of pWal->hdr.aFrameCksum */ - assert( pPg->pPager==pPager ); - assert( pPg->flags&PGHDR_DIRTY ); + assert( pWal->bShmUnreliable ); + assert( pWal->readOnly & WAL_SHM_RDONLY ); + assert( pWal->nWiData>0 && pWal->apWiData[0] ); - /* The doNotSpill NOSYNC bit is set during times when doing a sync of - ** journal (and adding a new header) is not allowed. This occurs - ** during calls to sqlcipher_sqlite3PagerWrite() while trying to journal multiple - ** pages belonging to the same sector. + /* Take WAL_READ_LOCK(0). This has the effect of preventing any + ** writers from running a checkpoint, but does not stop them + ** from running recovery. */ + rc = walLockShared(pWal, WAL_READ_LOCK(0)); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_BUSY ) rc = WAL_RETRY; + goto begin_unreliable_shm_out; + } + pWal->readLock = 0; + + /* Check to see if a separate writer has attached to the shared-memory area, + ** thus making the shared-memory "reliable" again. Do this by invoking + ** the xShmMap() routine of the VFS and looking to see if the return + ** is SQLITE_READONLY instead of SQLITE_READONLY_CANTINIT. ** - ** The doNotSpill ROLLBACK and OFF bits inhibits all cache spilling - ** regardless of whether or not a sync is required. This is set during - ** a rollback or by user request, respectively. + ** If the shared-memory is now "reliable" return WAL_RETRY, which will + ** cause the heap-memory WAL-index to be discarded and the actual + ** shared memory to be used in its place. ** - ** Spilling is also prohibited when in an error state since that could - ** lead to database corruption. In the current implementation it - ** is impossible for sqlcipher_sqlite3PcacheFetch() to be called with createFlag==3 - ** while in the error state, hence it is impossible for this routine to - ** be called in the error state. Nevertheless, we include a NEVER() - ** test for the error state as a safeguard against future changes. - */ - if( NEVER(pPager->errCode) ) return SQLITE_OK; - testcase( pPager->doNotSpill & SPILLFLAG_ROLLBACK ); - testcase( pPager->doNotSpill & SPILLFLAG_OFF ); - testcase( pPager->doNotSpill & SPILLFLAG_NOSYNC ); - if( pPager->doNotSpill - && ((pPager->doNotSpill & (SPILLFLAG_ROLLBACK|SPILLFLAG_OFF))!=0 - || (pPg->flags & PGHDR_NEED_SYNC)!=0) - ){ - return SQLITE_OK; + ** This step is important because, even though this connection is holding + ** the WAL_READ_LOCK(0) which prevents a checkpoint, a writer might + ** have already checkpointed the WAL file and, while the current + ** is active, wrap the WAL and start overwriting frames that this + ** process wants to use. + ** + ** Once sqlcipher_sqlite3OsShmMap() has been called for an sqlcipher_sqlite3_file and has + ** returned any SQLITE_READONLY value, it must return only SQLITE_READONLY + ** or SQLITE_READONLY_CANTINIT or some error for all subsequent invocations, + ** even if some external agent does a "chmod" to make the shared-memory + ** writable by us, until sqlcipher_sqlite3OsShmUnmap() has been called. + ** This is a requirement on the VFS implementation. + */ + rc = sqlcipher_sqlite3OsShmMap(pWal->pDbFd, 0, WALINDEX_PGSZ, 0, &pDummy); + assert( rc!=SQLITE_OK ); /* SQLITE_OK not possible for read-only connection */ + if( rc!=SQLITE_READONLY_CANTINIT ){ + rc = (rc==SQLITE_READONLY ? WAL_RETRY : rc); + goto begin_unreliable_shm_out; } - pPager->aStat[PAGER_STAT_SPILL]++; - pPg->pDirty = 0; - if( pagerUseWal(pPager) ){ - /* Write a single frame for this page to the log. */ - rc = subjournalPageIfRequired(pPg); - if( rc==SQLITE_OK ){ - rc = pagerWalFrames(pPager, pPg, 0, 0); - } - }else{ - -#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE - if( pPager->tempFile==0 ){ - rc = sqlcipher_sqlite3JournalCreate(pPager->jfd); - if( rc!=SQLITE_OK ) return pager_error(pPager, rc); - } -#endif + /* We reach this point only if the real shared-memory is still unreliable. + ** Assume the in-memory WAL-index substitute is correct and load it + ** into pWal->hdr. + */ + memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr)); - /* Sync the journal file if required. */ - if( pPg->flags&PGHDR_NEED_SYNC - || pPager->eState==PAGER_WRITER_CACHEMOD - ){ - rc = syncJournal(pPager, 1); - } + /* Make sure some writer hasn't come in and changed the WAL file out + ** from under us, then disconnected, while we were not looking. + */ + rc = sqlcipher_sqlite3OsFileSize(pWal->pWalFd, &szWal); + if( rc!=SQLITE_OK ){ + goto begin_unreliable_shm_out; + } + if( szWalhdr.mxFrame==0 ? SQLITE_OK : WAL_RETRY); + goto begin_unreliable_shm_out; + } - /* Write the contents of the page out to the database file. */ - if( rc==SQLITE_OK ){ - assert( (pPg->flags&PGHDR_NEED_SYNC)==0 ); - rc = pager_write_pagelist(pPager, pPg); - } + /* Check the salt keys at the start of the wal file still match. */ + rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); + if( rc!=SQLITE_OK ){ + goto begin_unreliable_shm_out; + } + if( memcmp(&pWal->hdr.aSalt, &aBuf[16], 8) ){ + /* Some writer has wrapped the WAL file while we were not looking. + ** Return WAL_RETRY which will cause the in-memory WAL-index to be + ** rebuilt. */ + rc = WAL_RETRY; + goto begin_unreliable_shm_out; } - /* Mark the page as clean. */ - if( rc==SQLITE_OK ){ - PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno)); - sqlcipher_sqlite3PcacheMakeClean(pPg); + /* Allocate a buffer to read frames into */ + assert( (pWal->szPage & (pWal->szPage-1))==0 ); + assert( pWal->szPage>=512 && pWal->szPage<=65536 ); + szFrame = pWal->szPage + WAL_FRAME_HDRSIZE; + aFrame = (u8 *)sqlcipher_sqlite3_malloc64(szFrame); + if( aFrame==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto begin_unreliable_shm_out; } + aData = &aFrame[WAL_FRAME_HDRSIZE]; - return pager_error(pPager, rc); -} + /* Check to see if a complete transaction has been appended to the + ** wal file since the heap-memory wal-index was created. If so, the + ** heap-memory wal-index is discarded and WAL_RETRY returned to + ** the caller. */ + aSaveCksum[0] = pWal->hdr.aFrameCksum[0]; + aSaveCksum[1] = pWal->hdr.aFrameCksum[1]; + for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->szPage); + iOffset+szFrame<=szWal; + iOffset+=szFrame + ){ + u32 pgno; /* Database page number for frame */ + u32 nTruncate; /* dbsize field from frame header */ -/* -** Flush all unreferenced dirty pages to disk. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerFlush(Pager *pPager){ - int rc = pPager->errCode; - if( !MEMDB ){ - PgHdr *pList = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache); - assert( assert_pager_state(pPager) ); - while( rc==SQLITE_OK && pList ){ - PgHdr *pNext = pList->pDirty; - if( pList->nRef==0 ){ - rc = pagerStress((void*)pPager, pList); - } - pList = pNext; + /* Read and decode the next log frame. */ + rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); + if( rc!=SQLITE_OK ) break; + if( !walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame) ) break; + + /* If nTruncate is non-zero, then a complete transaction has been + ** appended to this wal file. Set rc to WAL_RETRY and break out of + ** the loop. */ + if( nTruncate ){ + rc = WAL_RETRY; + break; } } + pWal->hdr.aFrameCksum[0] = aSaveCksum[0]; + pWal->hdr.aFrameCksum[1] = aSaveCksum[1]; + begin_unreliable_shm_out: + sqlcipher_sqlite3_free(aFrame); + if( rc!=SQLITE_OK ){ + int i; + for(i=0; inWiData; i++){ + sqlcipher_sqlite3_free((void*)pWal->apWiData[i]); + pWal->apWiData[i] = 0; + } + pWal->bShmUnreliable = 0; + sqlcipher_sqlite3WalEndReadTransaction(pWal); + *pChanged = 1; + } return rc; } /* -** Allocate and initialize a new Pager object and put a pointer to it -** in *ppPager. The pager should eventually be freed by passing it -** to sqlcipher_sqlite3PagerClose(). +** Attempt to start a read transaction. This might fail due to a race or +** other transient condition. When that happens, it returns WAL_RETRY to +** indicate to the caller that it is safe to retry immediately. ** -** The zFilename argument is the path to the database file to open. -** If zFilename is NULL then a randomly-named temporary file is created -** and used as the file to be cached. Temporary files are be deleted -** automatically when they are closed. If zFilename is ":memory:" then -** all information is held in cache. It is never written to disk. -** This can be used to implement an in-memory database. +** On success return SQLITE_OK. On a permanent failure (such an +** I/O error or an SQLITE_BUSY because another process is running +** recovery) return a positive error code. ** -** The nExtra parameter specifies the number of bytes of space allocated -** along with each page reference. This space is available to the user -** via the sqlcipher_sqlite3PagerGetExtra() API. When a new page is allocated, the -** first 8 bytes of this space are zeroed but the remainder is uninitialized. -** (The extra space is used by btree as the MemPage object.) +** The useWal parameter is true to force the use of the WAL and disable +** the case where the WAL is bypassed because it has been completely +** checkpointed. If useWal==0 then this routine calls walIndexReadHdr() +** to make a copy of the wal-index header into pWal->hdr. If the +** wal-index header has changed, *pChanged is set to 1 (as an indication +** to the caller that the local page cache is obsolete and needs to be +** flushed.) When useWal==1, the wal-index header is assumed to already +** be loaded and the pChanged parameter is unused. ** -** The flags argument is used to specify properties that affect the -** operation of the pager. It should be passed some bitwise combination -** of the PAGER_* flags. +** The caller must set the cnt parameter to the number of prior calls to +** this routine during the current read attempt that returned WAL_RETRY. +** This routine will start taking more aggressive measures to clear the +** race conditions after multiple WAL_RETRY returns, and after an excessive +** number of errors will ultimately return SQLITE_PROTOCOL. The +** SQLITE_PROTOCOL return indicates that some other process has gone rogue +** and is not honoring the locking protocol. There is a vanishingly small +** chance that SQLITE_PROTOCOL could be returned because of a run of really +** bad luck when there is lots of contention for the wal-index, but that +** possibility is so small that it can be safely neglected, we believe. ** -** The vfsFlags parameter is a bitmask to pass to the flags parameter -** of the xOpen() method of the supplied VFS when opening files. +** On success, this routine obtains a read lock on +** WAL_READ_LOCK(pWal->readLock). The pWal->readLock integer is +** in the range 0 <= pWal->readLock < WAL_NREADER. If pWal->readLock==(-1) +** that means the Wal does not hold any read lock. The reader must not +** access any database page that is modified by a WAL frame up to and +** including frame number aReadMark[pWal->readLock]. The reader will +** use WAL frames up to and including pWal->hdr.mxFrame if pWal->readLock>0 +** Or if pWal->readLock==0, then the reader will ignore the WAL +** completely and get all content directly from the database file. +** If the useWal parameter is 1 then the WAL will never be ignored and +** this routine will always set pWal->readLock>0 on success. +** When the read transaction is completed, the caller must release the +** lock on WAL_READ_LOCK(pWal->readLock) and set pWal->readLock to -1. ** -** If the pager object is allocated and the specified file opened -** successfully, SQLITE_OK is returned and *ppPager set to point to -** the new pager object. If an error occurs, *ppPager is set to NULL -** and error code returned. This function may return SQLITE_NOMEM -** (sqlcipher_sqlite3Malloc() is used to allocate memory), SQLITE_CANTOPEN or -** various SQLITE_IO_XXX errors. +** This routine uses the nBackfill and aReadMark[] fields of the header +** to select a particular WAL_READ_LOCK() that strives to let the +** checkpoint process do as much work as possible. This routine might +** update values of the aReadMark[] array in the header, but if it does +** so it takes care to hold an exclusive lock on the corresponding +** WAL_READ_LOCK() while changing values. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerOpen( - sqlcipher_sqlite3_vfs *pVfs, /* The virtual file system to use */ - Pager **ppPager, /* OUT: Return the Pager structure here */ - const char *zFilename, /* Name of the database file to open */ - int nExtra, /* Extra bytes append to each in-memory page */ - int flags, /* flags controlling this file */ - int vfsFlags, /* flags passed through to sqlcipher_sqlite3_vfs.xOpen() */ - void (*xReinit)(DbPage*) /* Function to reinitialize pages */ -){ - u8 *pPtr; - Pager *pPager = 0; /* Pager object to allocate and return */ - int rc = SQLITE_OK; /* Return code */ - int tempFile = 0; /* True for temp files (incl. in-memory files) */ - int memDb = 0; /* True if this is an in-memory file */ -#ifdef SQLITE_ENABLE_DESERIALIZE - int memJM = 0; /* Memory journal mode */ -#else -# define memJM 0 -#endif - int readOnly = 0; /* True if this is a read-only file */ - int journalFileSize; /* Bytes to allocate for each journal fd */ - char *zPathname = 0; /* Full path to database file */ - int nPathname = 0; /* Number of bytes in zPathname */ - int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ - int pcacheSize = sqlcipher_sqlite3PcacheSize(); /* Bytes to allocate for PCache */ - u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ - const char *zUri = 0; /* URI args to copy */ - int nUriByte = 1; /* Number of bytes of URI args at *zUri */ - int nUri = 0; /* Number of URI parameters */ +static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ + volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ + u32 mxReadMark; /* Largest aReadMark[] value */ + int mxI; /* Index of largest aReadMark[] value */ + int i; /* Loop counter */ + int rc = SQLITE_OK; /* Return code */ + u32 mxFrame; /* Wal frame to lock to */ - /* Figure out how much space is required for each journal file-handle - ** (there are two of them, the main journal and the sub-journal). */ - journalFileSize = ROUND8(sqlcipher_sqlite3JournalSize(pVfs)); + assert( pWal->readLock<0 ); /* Not currently locked */ - /* Set the output variable to NULL in case an error occurs. */ - *ppPager = 0; + /* useWal may only be set for read/write connections */ + assert( (pWal->readOnly & WAL_SHM_RDONLY)==0 || useWal==0 ); -#ifndef SQLITE_OMIT_MEMORYDB - if( flags & PAGER_MEMORY ){ - memDb = 1; - if( zFilename && zFilename[0] ){ - zPathname = sqlcipher_sqlite3DbStrDup(0, zFilename); - if( zPathname==0 ) return SQLITE_NOMEM_BKPT; - nPathname = sqlcipher_sqlite3Strlen30(zPathname); - zFilename = 0; + /* Take steps to avoid spinning forever if there is a protocol error. + ** + ** Circumstances that cause a RETRY should only last for the briefest + ** instances of time. No I/O or other system calls are done while the + ** locks are held, so the locks should not be held for very long. But + ** if we are unlucky, another process that is holding a lock might get + ** paged out or take a page-fault that is time-consuming to resolve, + ** during the few nanoseconds that it is holding the lock. In that case, + ** it might take longer than normal for the lock to free. + ** + ** After 5 RETRYs, we begin calling sqlcipher_sqlite3OsSleep(). The first few + ** calls to sqlcipher_sqlite3OsSleep() have a delay of 1 microsecond. Really this + ** is more of a scheduler yield than an actual delay. But on the 10th + ** an subsequent retries, the delays start becoming longer and longer, + ** so that on the 100th (and last) RETRY we delay for 323 milliseconds. + ** The total delay time before giving up is less than 10 seconds. + */ + if( cnt>5 ){ + int nDelay = 1; /* Pause time in microseconds */ + if( cnt>100 ){ + VVA_ONLY( pWal->lockError = 1; ) + return SQLITE_PROTOCOL; } + if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; + sqlcipher_sqlite3OsSleep(pWal->pVfs, nDelay); } -#endif - /* Compute and store the full pathname in an allocated buffer pointed - ** to by zPathname, length nPathname. Or, if this is a temporary file, - ** leave both nPathname and zPathname set to 0. - */ - if( zFilename && zFilename[0] ){ - const char *z; - nPathname = pVfs->mxPathname+1; - zPathname = sqlcipher_sqlite3DbMallocRaw(0, nPathname*2); - if( zPathname==0 ){ - return SQLITE_NOMEM_BKPT; - } - zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ - rc = sqlcipher_sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_OK_SYMLINK ){ - if( vfsFlags & SQLITE_OPEN_NOFOLLOW ){ - rc = SQLITE_CANTOPEN_SYMLINK; - }else{ - rc = SQLITE_OK; - } - } - } - nPathname = sqlcipher_sqlite3Strlen30(zPathname); - z = zUri = &zFilename[sqlcipher_sqlite3Strlen30(zFilename)+1]; - while( *z ){ - z += strlen(z)+1; - z += strlen(z)+1; - nUri++; + if( !useWal ){ + assert( rc==SQLITE_OK ); + if( pWal->bShmUnreliable==0 ){ + rc = walIndexReadHdr(pWal, pChanged); } - nUriByte = (int)(&z[1] - zUri); - assert( nUriByte>=1 ); - if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ - /* This branch is taken when the journal path required by - ** the database being opened will be more than pVfs->mxPathname - ** bytes in length. This means the database cannot be opened, - ** as it will not be possible to open the journal file or even - ** check for a hot-journal before reading. + if( rc==SQLITE_BUSY ){ + /* If there is not a recovery running in another thread or process + ** then convert BUSY errors to WAL_RETRY. If recovery is known to + ** be running, convert BUSY to BUSY_RECOVERY. There is a race here + ** which might cause WAL_RETRY to be returned even if BUSY_RECOVERY + ** would be technically correct. But the race is benign since with + ** WAL_RETRY this routine will be called again and will probably be + ** right on the second iteration. */ - rc = SQLITE_CANTOPEN_BKPT; + if( pWal->apWiData[0]==0 ){ + /* This branch is taken when the xShmMap() method returns SQLITE_BUSY. + ** We assume this is a transient condition, so return WAL_RETRY. The + ** xShmMap() implementation used by the default unix and win32 VFS + ** modules may return SQLITE_BUSY due to a race condition in the + ** code that determines whether or not the shared-memory region + ** must be zeroed before the requested page is returned. + */ + rc = WAL_RETRY; + }else if( SQLITE_OK==(rc = walLockShared(pWal, WAL_RECOVER_LOCK)) ){ + walUnlockShared(pWal, WAL_RECOVER_LOCK); + rc = WAL_RETRY; + }else if( rc==SQLITE_BUSY ){ + rc = SQLITE_BUSY_RECOVERY; + } } if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3DbFree(0, zPathname); return rc; } + else if( pWal->bShmUnreliable ){ + return walBeginShmUnreliable(pWal, pChanged); + } } - /* Allocate memory for the Pager structure, PCache object, the - ** three file descriptors, the database file name and the journal - ** file name. The layout in memory is as follows: - ** - ** Pager object (sizeof(Pager) bytes) - ** PCache object (sqlcipher_sqlite3PcacheSize() bytes) - ** Database file handle (pVfs->szOsFile bytes) - ** Sub-journal file handle (journalFileSize bytes) - ** Main journal file handle (journalFileSize bytes) - ** Ptr back to the Pager (sizeof(Pager*) bytes) - ** \0\0\0\0 database prefix (4 bytes) - ** Database file name (nPathname+1 bytes) - ** URI query parameters (nUriByte bytes) - ** Journal filename (nPathname+8+1 bytes) - ** WAL filename (nPathname+4+1 bytes) - ** \0\0\0 terminator (3 bytes) - ** - ** Some 3rd-party software, over which we have no control, depends on - ** the specific order of the filenames and the \0 separators between them - ** so that it can (for example) find the database filename given the WAL - ** filename without using the sqlcipher_sqlite3_filename_database() API. This is a - ** misuse of SQLite and a bug in the 3rd-party software, but the 3rd-party - ** software is in widespread use, so we try to avoid changing the filename - ** order and formatting if possible. In particular, the details of the - ** filename format expected by 3rd-party software should be as follows: - ** - ** - Main Database Path - ** - \0 - ** - Multiple URI components consisting of: - ** - Key - ** - \0 - ** - Value - ** - \0 - ** - \0 - ** - Journal Path - ** - \0 - ** - WAL Path (zWALName) - ** - \0 - ** - ** The sqlcipher_sqlite3_create_filename() interface and the databaseFilename() utility - ** that is used by sqlcipher_sqlite3_filename_database() and kin also depend on the - ** specific formatting and order of the various filenames, so if the format - ** changes here, be sure to change it there as well. - */ - pPtr = (u8 *)sqlcipher_sqlite3MallocZero( - ROUND8(sizeof(*pPager)) + /* Pager structure */ - ROUND8(pcacheSize) + /* PCache object */ - ROUND8(pVfs->szOsFile) + /* The main db file */ - journalFileSize * 2 + /* The two journal files */ - sizeof(pPager) + /* Space to hold a pointer */ - 4 + /* Database prefix */ - nPathname + 1 + /* database filename */ - nUriByte + /* query parameters */ - nPathname + 8 + 1 + /* Journal filename */ -#ifndef SQLITE_OMIT_WAL - nPathname + 4 + 1 + /* WAL filename */ + assert( pWal->nWiData>0 ); + assert( pWal->apWiData[0]!=0 ); + pInfo = walCkptInfo(pWal); + if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame +#ifdef SQLITE_ENABLE_SNAPSHOT + && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) #endif - 3 /* Terminator */ - ); - assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) ); - if( !pPtr ){ - sqlcipher_sqlite3DbFree(0, zPathname); - return SQLITE_NOMEM_BKPT; - } - pPager = (Pager*)pPtr; pPtr += ROUND8(sizeof(*pPager)); - pPager->pPCache = (PCache*)pPtr; pPtr += ROUND8(pcacheSize); - pPager->fd = (sqlcipher_sqlite3_file*)pPtr; pPtr += ROUND8(pVfs->szOsFile); - pPager->sjfd = (sqlcipher_sqlite3_file*)pPtr; pPtr += journalFileSize; - pPager->jfd = (sqlcipher_sqlite3_file*)pPtr; pPtr += journalFileSize; - assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); - memcpy(pPtr, &pPager, sizeof(pPager)); pPtr += sizeof(pPager); - - /* Fill in the Pager.zFilename and pPager.zQueryParam fields */ - pPtr += 4; /* Skip zero prefix */ - pPager->zFilename = (char*)pPtr; - if( nPathname>0 ){ - memcpy(pPtr, zPathname, nPathname); pPtr += nPathname + 1; - if( zUri ){ - memcpy(pPtr, zUri, nUriByte); pPtr += nUriByte; - }else{ - pPtr++; + ){ + /* The WAL has been completely backfilled (or it is empty). + ** and can be safely ignored. + */ + rc = walLockShared(pWal, WAL_READ_LOCK(0)); + walShmBarrier(pWal); + if( rc==SQLITE_OK ){ + if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ + /* It is not safe to allow the reader to continue here if frames + ** may have been appended to the log before READ_LOCK(0) was obtained. + ** When holding READ_LOCK(0), the reader ignores the entire log file, + ** which implies that the database file contains a trustworthy + ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from + ** happening, this is usually correct. + ** + ** However, if frames have been appended to the log (or if the log + ** is wrapped and written for that matter) before the READ_LOCK(0) + ** is obtained, that is not necessarily true. A checkpointer may + ** have started to backfill the appended frames but crashed before + ** it finished. Leaving a corrupt image in the database file. + */ + walUnlockShared(pWal, WAL_READ_LOCK(0)); + return WAL_RETRY; + } + pWal->readLock = 0; + return SQLITE_OK; + }else if( rc!=SQLITE_BUSY ){ + return rc; } } - - /* Fill in Pager.zJournal */ - if( nPathname>0 ){ - pPager->zJournal = (char*)pPtr; - memcpy(pPtr, zPathname, nPathname); pPtr += nPathname; - memcpy(pPtr, "-journal",8); pPtr += 8 + 1; -#ifdef SQLITE_ENABLE_8_3_NAMES - sqlcipher_sqlite3FileSuffix3(zFilename,pPager->zJournal); - pPtr = (u8*)(pPager->zJournal + sqlcipher_sqlite3Strlen30(pPager->zJournal)+1); -#endif - }else{ - pPager->zJournal = 0; + /* If we get this far, it means that the reader will want to use + ** the WAL to get at content from recent commits. The job now is + ** to select one of the aReadMark[] entries that is closest to + ** but not exceeding pWal->hdr.mxFrame and lock that entry. + */ + mxReadMark = 0; + mxI = 0; + mxFrame = pWal->hdr.mxFrame; +#ifdef SQLITE_ENABLE_SNAPSHOT + if( pWal->pSnapshot && pWal->pSnapshot->mxFramepSnapshot->mxFrame; } - -#ifndef SQLITE_OMIT_WAL - /* Fill in Pager.zWal */ - if( nPathname>0 ){ - pPager->zWal = (char*)pPtr; - memcpy(pPtr, zPathname, nPathname); pPtr += nPathname; - memcpy(pPtr, "-wal", 4); pPtr += 4 + 1; -#ifdef SQLITE_ENABLE_8_3_NAMES - sqlcipher_sqlite3FileSuffix3(zFilename, pPager->zWal); - pPtr = (u8*)(pPager->zWal + sqlcipher_sqlite3Strlen30(pPager->zWal)+1); #endif - }else{ - pPager->zWal = 0; + for(i=1; iaReadMark+i); + if( mxReadMark<=thisMark && thisMark<=mxFrame ){ + assert( thisMark!=READMARK_NOT_USED ); + mxReadMark = thisMark; + mxI = i; + } } -#endif - - if( nPathname ) sqlcipher_sqlite3DbFree(0, zPathname); - pPager->pVfs = pVfs; - pPager->vfsFlags = vfsFlags; - - /* Open the pager file. - */ - if( zFilename && zFilename[0] ){ - int fout = 0; /* VFS flags returned by xOpen() */ - rc = sqlcipher_sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); - assert( !memDb ); -#ifdef SQLITE_ENABLE_DESERIALIZE - memJM = (fout&SQLITE_OPEN_MEMORY)!=0; -#endif - readOnly = (fout&SQLITE_OPEN_READONLY)!=0; - - /* If the file was successfully opened for read/write access, - ** choose a default page size in case we have to create the - ** database file. The default page size is the maximum of: - ** - ** + SQLITE_DEFAULT_PAGE_SIZE, - ** + The value returned by sqlcipher_sqlite3OsSectorSize() - ** + The largest page size that can be written atomically. - */ - if( rc==SQLITE_OK ){ - int iDc = sqlcipher_sqlite3OsDeviceCharacteristics(pPager->fd); - if( !readOnly ){ - setSectorSize(pPager); - assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE); - if( szPageDfltsectorSize ){ - if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){ - szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE; - }else{ - szPageDflt = (u32)pPager->sectorSize; - } - } -#ifdef SQLITE_ENABLE_ATOMIC_WRITE - { - int ii; - assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); - assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); - assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536); - for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){ - if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){ - szPageDflt = ii; - } - } - } -#endif - } - pPager->noLock = sqlcipher_sqlite3_uri_boolean(pPager->zFilename, "nolock", 0); - if( (iDc & SQLITE_IOCAP_IMMUTABLE)!=0 - || sqlcipher_sqlite3_uri_boolean(pPager->zFilename, "immutable", 0) ){ - vfsFlags |= SQLITE_OPEN_READONLY; - goto act_like_temp_file; + if( (pWal->readOnly & WAL_SHM_RDONLY)==0 + && (mxReadMarkaReadMark+i,mxFrame); + mxReadMark = mxFrame; + mxI = i; + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); + break; + }else if( rc!=SQLITE_BUSY ){ + return rc; } } - }else{ - /* If a temporary file is requested, it is not opened immediately. - ** In this case we accept the default page size and delay actually - ** opening the file until the first call to OsWrite(). - ** - ** This branch is also run for an in-memory database. An in-memory - ** database is the same as a temp-file that is never written out to - ** disk and uses an in-memory rollback journal. - ** - ** This branch also runs for files marked as immutable. - */ -act_like_temp_file: - tempFile = 1; - pPager->eState = PAGER_READER; /* Pretend we already have a lock */ - pPager->eLock = EXCLUSIVE_LOCK; /* Pretend we are in EXCLUSIVE mode */ - pPager->noLock = 1; /* Do no locking */ - readOnly = (vfsFlags&SQLITE_OPEN_READONLY); } - - /* The following call to PagerSetPagesize() serves to set the value of - ** Pager.pageSize and to allocate the Pager.pTmpSpace buffer. - */ - if( rc==SQLITE_OK ){ - assert( pPager->memDb==0 ); - rc = sqlcipher_sqlite3PagerSetPagesize(pPager, &szPageDflt, -1); - testcase( rc!=SQLITE_OK ); + if( mxI==0 ){ + assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); + return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; } - /* Initialize the PCache object. */ - if( rc==SQLITE_OK ){ - nExtra = ROUND8(nExtra); - assert( nExtra>=8 && nExtra<1000 ); - rc = sqlcipher_sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, - !memDb?pagerStress:0, (void *)pPager, pPager->pPCache); + rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); + if( rc ){ + return rc==SQLITE_BUSY ? WAL_RETRY : rc; } - - /* If an error occurred above, free the Pager structure and close the file. + /* Now that the read-lock has been obtained, check that neither the + ** value in the aReadMark[] array or the contents of the wal-index + ** header have changed. + ** + ** It is necessary to check that the wal-index header did not change + ** between the time it was read and when the shared-lock was obtained + ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility + ** that the log file may have been wrapped by a writer, or that frames + ** that occur later in the log than pWal->hdr.mxFrame may have been + ** copied into the database by a checkpointer. If either of these things + ** happened, then reading the database with the current value of + ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry + ** instead. + ** + ** Before checking that the live wal-index header has not changed + ** since it was read, set Wal.minFrame to the first frame in the wal + ** file that has not yet been checkpointed. This client will not need + ** to read any frames earlier than minFrame from the wal file - they + ** can be safely read directly from the database file. + ** + ** Because a ShmBarrier() call is made between taking the copy of + ** nBackfill and checking that the wal-header in shared-memory still + ** matches the one cached in pWal->hdr, it is guaranteed that the + ** checkpointer that set nBackfill was not working with a wal-index + ** header newer than that cached in pWal->hdr. If it were, that could + ** cause a problem. The checkpointer could omit to checkpoint + ** a version of page X that lies before pWal->minFrame (call that version + ** A) on the basis that there is a newer version (version B) of the same + ** page later in the wal file. But if version B happens to like past + ** frame pWal->hdr.mxFrame - then the client would incorrectly assume + ** that it can read version A from the database file. However, since + ** we can guarantee that the checkpointer that set nBackfill could not + ** see any pages past pWal->hdr.mxFrame, this problem does not come up. */ - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3OsClose(pPager->fd); - sqlcipher_sqlite3PageFree(pPager->pTmpSpace); - sqlcipher_sqlite3_free(pPager); - return rc; - } - - PAGERTRACE(("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename)); - IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename)) - - pPager->useJournal = (u8)useJournal; - /* pPager->stmtOpen = 0; */ - /* pPager->stmtInUse = 0; */ - /* pPager->nRef = 0; */ - /* pPager->stmtSize = 0; */ - /* pPager->stmtJSize = 0; */ - /* pPager->nPage = 0; */ - pPager->mxPgno = SQLITE_MAX_PAGE_COUNT; - /* pPager->state = PAGER_UNLOCK; */ - /* pPager->errMask = 0; */ - pPager->tempFile = (u8)tempFile; - assert( tempFile==PAGER_LOCKINGMODE_NORMAL - || tempFile==PAGER_LOCKINGMODE_EXCLUSIVE ); - assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 ); - pPager->exclusiveMode = (u8)tempFile; - pPager->changeCountDone = pPager->tempFile; - pPager->memDb = (u8)memDb; - pPager->readOnly = (u8)readOnly; - assert( useJournal || pPager->tempFile ); - pPager->noSync = pPager->tempFile; - if( pPager->noSync ){ - assert( pPager->fullSync==0 ); - assert( pPager->extraSync==0 ); - assert( pPager->syncFlags==0 ); - assert( pPager->walSyncFlags==0 ); + pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; + walShmBarrier(pWal); + if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark + || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) + ){ + walUnlockShared(pWal, WAL_READ_LOCK(mxI)); + return WAL_RETRY; }else{ - pPager->fullSync = 1; - pPager->extraSync = 0; - pPager->syncFlags = SQLITE_SYNC_NORMAL; - pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2); - } - /* pPager->pFirst = 0; */ - /* pPager->pFirstSynced = 0; */ - /* pPager->pLast = 0; */ - pPager->nExtra = (u16)nExtra; - pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT; - assert( isOpen(pPager->fd) || tempFile ); - setSectorSize(pPager); - if( !useJournal ){ - pPager->journalMode = PAGER_JOURNALMODE_OFF; - }else if( memDb || memJM ){ - pPager->journalMode = PAGER_JOURNALMODE_MEMORY; - } - /* pPager->xBusyHandler = 0; */ - /* pPager->pBusyHandlerArg = 0; */ - pPager->xReiniter = xReinit; - setGetterMethod(pPager); - /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */ - /* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */ - - *ppPager = pPager; - return SQLITE_OK; -} - -/* -** Return the sqlcipher_sqlite3_file for the main database given the name -** of the corresonding WAL or Journal name as passed into -** xOpen. -*/ -SQLITE_API sqlcipher_sqlite3_file *sqlcipher_sqlite3_database_file_object(const char *zName){ - Pager *pPager; - while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){ - zName--; + assert( mxReadMark<=pWal->hdr.mxFrame ); + pWal->readLock = (i16)mxI; } - pPager = *(Pager**)(zName - 4 - sizeof(Pager*)); - return pPager->fd; + return rc; } - +#ifdef SQLITE_ENABLE_SNAPSHOT /* -** This function is called after transitioning from PAGER_UNLOCK to -** PAGER_SHARED state. It tests if there is a hot journal present in -** the file-system for the given pager. A hot journal is one that -** needs to be played back. According to this function, a hot-journal -** file exists if the following criteria are met: -** -** * The journal file exists in the file system, and -** * No process holds a RESERVED or greater lock on the database file, and -** * The database file itself is greater than 0 bytes in size, and -** * The first byte of the journal file exists and is not 0x00. -** -** If the current size of the database file is 0 but a journal file -** exists, that is probably an old journal left over from a prior -** database with the same name. In this case the journal file is -** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK -** is returned. +** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted +** variable so that older snapshots can be accessed. To do this, loop +** through all wal frames from nBackfillAttempted to (nBackfill+1), +** comparing their content to the corresponding page with the database +** file, if any. Set nBackfillAttempted to the frame number of the +** first frame for which the wal file content matches the db file. ** -** This routine does not check if there is a super-journal filename -** at the end of the file. If there is, and that super-journal file -** does not exist, then the journal file is not really hot. In this -** case this routine will return a false-positive. The pager_playback() -** routine will discover that the journal file is not really hot and -** will not roll it back. +** This is only really safe if the file-system is such that any page +** writes made by earlier checkpointers were atomic operations, which +** is not always true. It is also possible that nBackfillAttempted +** may be left set to a value larger than expected, if a wal frame +** contains content that duplicate of an earlier version of the same +** page. ** -** If a hot-journal file is found to exist, *pExists is set to 1 and -** SQLITE_OK returned. If no hot-journal file is present, *pExists is -** set to 0 and SQLITE_OK returned. If an IO error occurs while trying -** to determine whether or not a hot-journal file exists, the IO error -** code is returned and the value of *pExists is undefined. +** SQLITE_OK is returned if successful, or an SQLite error code if an +** error occurs. It is not an error if nBackfillAttempted cannot be +** decreased at all. */ -static int hasHotJournal(Pager *pPager, int *pExists){ - sqlcipher_sqlite3_vfs * const pVfs = pPager->pVfs; - int rc = SQLITE_OK; /* Return code */ - int exists = 1; /* True if a journal file is present */ - int jrnlOpen = !!isOpen(pPager->jfd); +SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotRecover(Wal *pWal){ + int rc; - assert( pPager->useJournal ); - assert( isOpen(pPager->fd) ); - assert( pPager->eState==PAGER_OPEN ); + assert( pWal->readLock>=0 ); + rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); + if( rc==SQLITE_OK ){ + volatile WalCkptInfo *pInfo = walCkptInfo(pWal); + int szPage = (int)pWal->szPage; + i64 szDb; /* Size of db file in bytes */ - assert( jrnlOpen==0 || ( sqlcipher_sqlite3OsDeviceCharacteristics(pPager->jfd) & - SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN - )); + rc = sqlcipher_sqlite3OsFileSize(pWal->pDbFd, &szDb); + if( rc==SQLITE_OK ){ + void *pBuf1 = sqlcipher_sqlite3_malloc(szPage); + void *pBuf2 = sqlcipher_sqlite3_malloc(szPage); + if( pBuf1==0 || pBuf2==0 ){ + rc = SQLITE_NOMEM; + }else{ + u32 i = pInfo->nBackfillAttempted; + for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){ + WalHashLoc sLoc; /* Hash table location */ + u32 pgno; /* Page number in db file */ + i64 iDbOff; /* Offset of db file entry */ + i64 iWalOff; /* Offset of wal file entry */ - *pExists = 0; - if( !jrnlOpen ){ - rc = sqlcipher_sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); - } - if( rc==SQLITE_OK && exists ){ - int locked = 0; /* True if some process holds a RESERVED lock */ + rc = walHashGet(pWal, walFramePage(i), &sLoc); + if( rc!=SQLITE_OK ) break; + assert( i - sLoc.iZero - 1 >=0 ); + pgno = sLoc.aPgno[i-sLoc.iZero-1]; + iDbOff = (i64)(pgno-1) * szPage; - /* Race condition here: Another process might have been holding the - ** the RESERVED lock and have a journal open at the sqlcipher_sqlite3OsAccess() - ** call above, but then delete the journal and drop the lock before - ** we get to the following sqlcipher_sqlite3OsCheckReservedLock() call. If that - ** is the case, this routine might think there is a hot journal when - ** in fact there is none. This results in a false-positive which will - ** be dealt with by the playback routine. Ticket #3883. - */ - rc = sqlcipher_sqlite3OsCheckReservedLock(pPager->fd, &locked); - if( rc==SQLITE_OK && !locked ){ - Pgno nPage; /* Number of pages in database file */ + if( iDbOff+szPage<=szDb ){ + iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE; + rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff); - assert( pPager->tempFile==0 ); - rc = pagerPagecount(pPager, &nPage); - if( rc==SQLITE_OK ){ - /* If the database is zero pages in size, that means that either (1) the - ** journal is a remnant from a prior database with the same name where - ** the database file but not the journal was deleted, or (2) the initial - ** transaction that populates a new database is being rolled back. - ** In either case, the journal file can be deleted. However, take care - ** not to delete the journal file if it is already open due to - ** journal_mode=PERSIST. - */ - if( nPage==0 && !jrnlOpen ){ - sqlcipher_sqlite3BeginBenignMalloc(); - if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){ - sqlcipher_sqlite3OsDelete(pVfs, pPager->zJournal, 0); - if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK); - } - sqlcipher_sqlite3EndBenignMalloc(); - }else{ - /* The journal file exists and no other connection has a reserved - ** or greater lock on the database file. Now check that there is - ** at least one non-zero bytes at the start of the journal file. - ** If there is, then we consider this journal to be hot. If not, - ** it can be ignored. - */ - if( !jrnlOpen ){ - int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL; - rc = sqlcipher_sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f); - } - if( rc==SQLITE_OK ){ - u8 first = 0; - rc = sqlcipher_sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0); - if( rc==SQLITE_IOERR_SHORT_READ ){ - rc = SQLITE_OK; + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff); } - if( !jrnlOpen ){ - sqlcipher_sqlite3OsClose(pPager->jfd); + + if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){ + break; } - *pExists = (first!=0); - }else if( rc==SQLITE_CANTOPEN ){ - /* If we cannot open the rollback journal file in order to see if - ** it has a zero header, that might be due to an I/O error, or - ** it might be due to the race condition described above and in - ** ticket #3883. Either way, assume that the journal is hot. - ** This might be a false positive. But if it is, then the - ** automatic journal playback and recovery mechanism will deal - ** with it under an EXCLUSIVE lock where we do not need to - ** worry so much with race conditions. - */ - *pExists = 1; - rc = SQLITE_OK; } + + pInfo->nBackfillAttempted = i-1; } } + + sqlcipher_sqlite3_free(pBuf1); + sqlcipher_sqlite3_free(pBuf2); } + walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); } return rc; } +#endif /* SQLITE_ENABLE_SNAPSHOT */ /* -** This function is called to obtain a shared lock on the database file. -** It is illegal to call sqlcipher_sqlite3PagerGet() until after this function -** has been successfully called. If a shared-lock is already held when -** this function is called, it is a no-op. -** -** The following operations are also performed by this function. -** -** 1) If the pager is currently in PAGER_OPEN state (no lock held -** on the database file), then an attempt is made to obtain a -** SHARED lock on the database file. Immediately after obtaining -** the SHARED lock, the file-system is checked for a hot-journal, -** which is played back if present. Following any hot-journal -** rollback, the contents of the cache are validated by checking -** the 'change-counter' field of the database file header and -** discarded if they are found to be invalid. +** Begin a read transaction on the database. ** -** 2) If the pager is running in exclusive-mode, and there are currently -** no outstanding references to any pages, and is in the error state, -** then an attempt is made to clear the error state by discarding -** the contents of the page cache and rolling back any open journal -** file. +** This routine used to be called sqlcipher_sqlite3OpenSnapshot() and with good reason: +** it takes a snapshot of the state of the WAL and wal-index for the current +** instant in time. The current thread will continue to use this snapshot. +** Other threads might append new content to the WAL and wal-index but +** that extra content is ignored by the current thread. ** -** If everything is successful, SQLITE_OK is returned. If an IO error -** occurs while locking the database, checking for a hot-journal file or -** rolling back a journal file, the IO error code is returned. +** If the database contents have changes since the previous read +** transaction, then *pChanged is set to 1 before returning. The +** Pager layer will use this to know that its cache is stale and +** needs to be flushed. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerSharedLock(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ +SQLITE_PRIVATE int sqlcipher_sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ + int rc; /* Return code */ + int cnt = 0; /* Number of TryBeginRead attempts */ +#ifdef SQLITE_ENABLE_SNAPSHOT + int bChanged = 0; + WalIndexHdr *pSnapshot = pWal->pSnapshot; +#endif - /* This routine is only called from b-tree and only when there are no - ** outstanding pages. This implies that the pager state should either - ** be OPEN or READER. READER is only possible if the pager is or was in - ** exclusive access mode. */ - assert( sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)==0 ); - assert( assert_pager_state(pPager) ); - assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); - assert( pPager->errCode==SQLITE_OK ); + assert( pWal->ckptLock==0 ); - if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){ - int bHotJournal = 1; /* True if there exists a hot journal-file */ +#ifdef SQLITE_ENABLE_SNAPSHOT + if( pSnapshot ){ + if( memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){ + bChanged = 1; + } - assert( !MEMDB ); - assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK ); + /* It is possible that there is a checkpointer thread running + ** concurrent with this code. If this is the case, it may be that the + ** checkpointer has already determined that it will checkpoint + ** snapshot X, where X is later in the wal file than pSnapshot, but + ** has not yet set the pInfo->nBackfillAttempted variable to indicate + ** its intent. To avoid the race condition this leads to, ensure that + ** there is no checkpointer process by taking a shared CKPT lock + ** before checking pInfo->nBackfillAttempted. */ + (void)walEnableBlocking(pWal); + rc = walLockShared(pWal, WAL_CKPT_LOCK); + walDisableBlocking(pWal); - rc = pager_wait_on_lock(pPager, SHARED_LOCK); if( rc!=SQLITE_OK ){ - assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); - goto failed; + return rc; } + pWal->ckptLock = 1; + } +#endif - /* If a journal file exists, and there is no RESERVED lock on the - ** database file, then it either needs to be played back or deleted. - */ - if( pPager->eLock<=SHARED_LOCK ){ - rc = hasHotJournal(pPager, &bHotJournal); - } - if( rc!=SQLITE_OK ){ - goto failed; - } - if( bHotJournal ){ - if( pPager->readOnly ){ - rc = SQLITE_READONLY_ROLLBACK; - goto failed; - } + do{ + rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); + }while( rc==WAL_RETRY ); + testcase( (rc&0xff)==SQLITE_BUSY ); + testcase( (rc&0xff)==SQLITE_IOERR ); + testcase( rc==SQLITE_PROTOCOL ); + testcase( rc==SQLITE_OK ); - /* Get an EXCLUSIVE lock on the database file. At this point it is - ** important that a RESERVED lock is not obtained on the way to the - ** EXCLUSIVE lock. If it were, another process might open the - ** database file, detect the RESERVED lock, and conclude that the - ** database is safe to read while this process is still rolling the - ** hot-journal back. - ** - ** Because the intermediate RESERVED lock is not requested, any - ** other process attempting to access the database file will get to - ** this point in the code and fail to obtain its own EXCLUSIVE lock - ** on the database file. +#ifdef SQLITE_ENABLE_SNAPSHOT + if( rc==SQLITE_OK ){ + if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){ + /* At this point the client has a lock on an aReadMark[] slot holding + ** a value equal to or smaller than pSnapshot->mxFrame, but pWal->hdr + ** is populated with the wal-index header corresponding to the head + ** of the wal file. Verify that pSnapshot is still valid before + ** continuing. Reasons why pSnapshot might no longer be valid: ** - ** Unless the pager is in locking_mode=exclusive mode, the lock is - ** downgraded to SHARED_LOCK before this function returns. - */ - rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); - if( rc!=SQLITE_OK ){ - goto failed; - } - - /* If it is not already open and the file exists on disk, open the - ** journal for read/write access. Write access is required because - ** in exclusive-access mode the file descriptor will be kept open - ** and possibly used for a transaction later on. Also, write-access - ** is usually required to finalize the journal in journal_mode=persist - ** mode (and also for journal_mode=truncate on some systems). + ** (1) The WAL file has been reset since the snapshot was taken. + ** In this case, the salt will have changed. ** - ** If the journal does not exist, it usually means that some - ** other connection managed to get in and roll it back before - ** this connection obtained the exclusive lock above. Or, it - ** may mean that the pager was in the error-state when this - ** function was called and the journal file does not exist. + ** (2) A checkpoint as been attempted that wrote frames past + ** pSnapshot->mxFrame into the database file. Note that the + ** checkpoint need not have completed for this to cause problems. */ - if( !isOpen(pPager->jfd) ){ - sqlcipher_sqlite3_vfs * const pVfs = pPager->pVfs; - int bExists; /* True if journal file exists */ - rc = sqlcipher_sqlite3OsAccess( - pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &bExists); - if( rc==SQLITE_OK && bExists ){ - int fout = 0; - int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL; - assert( !pPager->tempFile ); - rc = sqlcipher_sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout); - assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); - if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){ - rc = SQLITE_CANTOPEN_BKPT; - sqlcipher_sqlite3OsClose(pPager->jfd); - } - } - } + volatile WalCkptInfo *pInfo = walCkptInfo(pWal); - /* Playback and delete the journal. Drop the database write - ** lock and reacquire the read lock. Purge the cache before - ** playing back the hot-journal so that we don't end up with - ** an inconsistent cache. Sync the hot journal before playing - ** it back since the process that crashed and left the hot journal - ** probably did not sync it and we are required to always sync - ** the journal before playing it back. - */ - if( isOpen(pPager->jfd) ){ - assert( rc==SQLITE_OK ); - rc = pagerSyncHotJournal(pPager); - if( rc==SQLITE_OK ){ - rc = pager_playback(pPager, !pPager->tempFile); - pPager->eState = PAGER_OPEN; - } - }else if( !pPager->exclusiveMode ){ - pagerUnlockDb(pPager, SHARED_LOCK); - } + assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 ); + assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame ); - if( rc!=SQLITE_OK ){ - /* This branch is taken if an error occurs while trying to open - ** or roll back a hot-journal while holding an EXCLUSIVE lock. The - ** pager_unlock() routine will be called before returning to unlock - ** the file. If the unlock attempt fails, then Pager.eLock must be - ** set to UNKNOWN_LOCK (see the comment above the #define for - ** UNKNOWN_LOCK above for an explanation). - ** - ** In order to get pager_unlock() to do this, set Pager.eState to - ** PAGER_ERROR now. This is not actually counted as a transition - ** to ERROR state in the state diagram at the top of this file, - ** since we know that the same call to pager_unlock() will very - ** shortly transition the pager object to the OPEN state. Calling - ** assert_pager_state() would fail now, as it should not be possible - ** to be in ERROR state when there are zero outstanding page - ** references. - */ - pager_error(pPager, rc); - goto failed; + /* Check that the wal file has not been wrapped. Assuming that it has + ** not, also check that no checkpointer has attempted to checkpoint any + ** frames beyond pSnapshot->mxFrame. If either of these conditions are + ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr + ** with *pSnapshot and set *pChanged as appropriate for opening the + ** snapshot. */ + if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) + && pSnapshot->mxFrame>=pInfo->nBackfillAttempted + ){ + assert( pWal->readLock>0 ); + memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr)); + *pChanged = bChanged; + }else{ + rc = SQLITE_ERROR_SNAPSHOT; } - assert( pPager->eState==PAGER_OPEN ); - assert( (pPager->eLock==SHARED_LOCK) - || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK) - ); - } - - if( !pPager->tempFile && pPager->hasHeldSharedLock ){ - /* The shared-lock has just been acquired then check to - ** see if the database has been modified. If the database has changed, - ** flush the cache. The hasHeldSharedLock flag prevents this from - ** occurring on the very first access to a file, in order to save a - ** single unnecessary sqlcipher_sqlite3OsRead() call at the start-up. - ** - ** Database changes are detected by looking at 15 bytes beginning - ** at offset 24 into the file. The first 4 of these 16 bytes are - ** a 32-bit counter that is incremented with each change. The - ** other bytes change randomly with each file change when - ** a codec is in use. - ** - ** There is a vanishingly small chance that a change will not be - ** detected. The chance of an undetected change is so small that - ** it can be neglected. - */ - char dbFileVers[sizeof(pPager->dbFileVers)]; + /* A client using a non-current snapshot may not ignore any frames + ** from the start of the wal file. This is because, for a system + ** where (minFrame < iSnapshot < maxFrame), a checkpointer may + ** have omitted to checkpoint a frame earlier than minFrame in + ** the file because there exists a frame after iSnapshot that + ** is the same database page. */ + pWal->minFrame = 1; - IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers))); - rc = sqlcipher_sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24); if( rc!=SQLITE_OK ){ - if( rc!=SQLITE_IOERR_SHORT_READ ){ - goto failed; - } - memset(dbFileVers, 0, sizeof(dbFileVers)); - } - - if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ - pager_reset(pPager); - - /* Unmap the database file. It is possible that external processes - ** may have truncated the database file and then extended it back - ** to its original size while this process was not holding a lock. - ** In this case there may exist a Pager.pMap mapping that appears - ** to be the right size but is not actually valid. Avoid this - ** possibility by unmapping the db here. */ - if( USEFETCH(pPager) ){ - sqlcipher_sqlite3OsUnfetch(pPager->fd, 0, 0); - } + sqlcipher_sqlite3WalEndReadTransaction(pWal); } } - - /* If there is a WAL file in the file-system, open this database in WAL - ** mode. Otherwise, the following function call is a no-op. - */ - rc = pagerOpenWalIfPresent(pPager); -#ifndef SQLITE_OMIT_WAL - assert( pPager->pWal==0 || rc==SQLITE_OK ); -#endif - } - - if( pagerUseWal(pPager) ){ - assert( rc==SQLITE_OK ); - rc = pagerBeginReadTransaction(pPager); - } - - if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ - rc = pagerPagecount(pPager, &pPager->dbSize); } - failed: - if( rc!=SQLITE_OK ){ - assert( !MEMDB ); - pager_unlock(pPager); - assert( pPager->eState==PAGER_OPEN ); - }else{ - pPager->eState = PAGER_READER; - pPager->hasHeldSharedLock = 1; + /* Release the shared CKPT lock obtained above. */ + if( pWal->ckptLock ){ + assert( pSnapshot ); + walUnlockShared(pWal, WAL_CKPT_LOCK); + pWal->ckptLock = 0; } +#endif return rc; } /* -** If the reference count has reached zero, rollback any active -** transaction and unlock the pager. -** -** Except, in locking_mode=EXCLUSIVE when there is nothing to in -** the rollback journal, the unlock is not performed and there is -** nothing to rollback, so this routine is a no-op. +** Finish with a read transaction. All this does is release the +** read-lock. */ -static void pagerUnlockIfUnused(Pager *pPager){ - if( sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)==0 ){ - assert( pPager->nMmapOut==0 ); /* because page1 is never memory mapped */ - pagerUnlockAndRollback(pPager); +SQLITE_PRIVATE void sqlcipher_sqlite3WalEndReadTransaction(Wal *pWal){ + sqlcipher_sqlite3WalEndWriteTransaction(pWal); + if( pWal->readLock>=0 ){ + walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); + pWal->readLock = -1; } } /* -** The page getter methods each try to acquire a reference to a -** page with page number pgno. If the requested reference is -** successfully obtained, it is copied to *ppPage and SQLITE_OK returned. -** -** There are different implementations of the getter method depending -** on the current state of the pager. -** -** getPageNormal() -- The normal getter -** getPageError() -- Used if the pager is in an error state -** getPageMmap() -- Used if memory-mapped I/O is enabled -** -** If the requested page is already in the cache, it is returned. -** Otherwise, a new page object is allocated and populated with data -** read from the database file. In some cases, the pcache module may -** choose not to allocate a new page object and may reuse an existing -** object with no outstanding references. -** -** The extra data appended to a page is always initialized to zeros the -** first time a page is loaded into memory. If the page requested is -** already in the cache when this function is called, then the extra -** data is left as it was when the page object was last used. -** -** If the database image is smaller than the requested page or if -** the flags parameter contains the PAGER_GET_NOCONTENT bit and the -** requested page is not already stored in the cache, then no -** actual disk read occurs. In this case the memory image of the -** page is initialized to all zeros. -** -** If PAGER_GET_NOCONTENT is true, it means that we do not care about -** the contents of the page. This occurs in two scenarios: -** -** a) When reading a free-list leaf page from the database, and -** -** b) When a savepoint is being rolled back and we need to load -** a new page into the cache to be filled with the data read -** from the savepoint journal. -** -** If PAGER_GET_NOCONTENT is true, then the data returned is zeroed instead -** of being read from the database. Additionally, the bits corresponding -** to pgno in Pager.pInJournal (bitvec of pages already written to the -** journal file) and the PagerSavepoint.pInSavepoint bitvecs of any open -** savepoints are set. This means if the page is made writable at any -** point in the future, using a call to sqlcipher_sqlite3PagerWrite(), its contents -** will not be journaled. This saves IO. -** -** The acquisition might fail for several reasons. In all cases, -** an appropriate error code is returned and *ppPage is set to NULL. +** Search the wal file for page pgno. If found, set *piRead to the frame that +** contains the page. Otherwise, if pgno is not in the wal file, set *piRead +** to zero. ** -** See also sqlcipher_sqlite3PagerLookup(). Both this routine and Lookup() attempt -** to find a page in the in-memory cache first. If the page is not already -** in memory, this routine goes to disk to read it in whereas Lookup() -** just returns 0. This routine acquires a read-lock the first time it -** has to go to disk, and could also playback an old journal if necessary. -** Since Lookup() never goes to disk, it never has to deal with locks -** or journal files. +** Return SQLITE_OK if successful, or an error code if an error occurs. If an +** error does occur, the final value of *piRead is undefined. */ -static int getPageNormal( - Pager *pPager, /* The pager open on the database file */ - Pgno pgno, /* Page number to fetch */ - DbPage **ppPage, /* Write a pointer to the page here */ - int flags /* PAGER_GET_XXX flags */ +SQLITE_PRIVATE int sqlcipher_sqlite3WalFindFrame( + Wal *pWal, /* WAL handle */ + Pgno pgno, /* Database page number to read data for */ + u32 *piRead /* OUT: Frame number (or zero) */ ){ - int rc = SQLITE_OK; - PgHdr *pPg; - u8 noContent; /* True if PAGER_GET_NOCONTENT is set */ - sqlcipher_sqlite3_pcache_page *pBase; - - assert( pPager->errCode==SQLITE_OK ); - assert( pPager->eState>=PAGER_READER ); - assert( assert_pager_state(pPager) ); - assert( pPager->hasHeldSharedLock==1 ); + u32 iRead = 0; /* If !=0, WAL frame to return data from */ + u32 iLast = pWal->hdr.mxFrame; /* Last page in WAL for this reader */ + int iHash; /* Used to loop through N hash tables */ + int iMinHash; - if( pgno==0 ) return SQLITE_CORRUPT_BKPT; - pBase = sqlcipher_sqlite3PcacheFetch(pPager->pPCache, pgno, 3); - if( pBase==0 ){ - pPg = 0; - rc = sqlcipher_sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase); - if( rc!=SQLITE_OK ) goto pager_acquire_err; - if( pBase==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto pager_acquire_err; - } - } - pPg = *ppPage = sqlcipher_sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pBase); - assert( pPg==(*ppPage) ); - assert( pPg->pgno==pgno ); - assert( pPg->pPager==pPager || pPg->pPager==0 ); + /* This routine is only be called from within a read transaction. */ + assert( pWal->readLock>=0 || pWal->lockError ); - noContent = (flags & PAGER_GET_NOCONTENT)!=0; - if( pPg->pPager && !noContent ){ - /* In this case the pcache already contains an initialized copy of - ** the page. Return without further ado. */ - assert( pgno!=PAGER_MJ_PGNO(pPager) ); - pPager->aStat[PAGER_STAT_HIT]++; + /* If the "last page" field of the wal-index header snapshot is 0, then + ** no data will be read from the wal under any circumstances. Return early + ** in this case as an optimization. Likewise, if pWal->readLock==0, + ** then the WAL is ignored by the reader so return early, as if the + ** WAL were empty. + */ + if( iLast==0 || (pWal->readLock==0 && pWal->bShmUnreliable==0) ){ + *piRead = 0; return SQLITE_OK; - - }else{ - /* The pager cache has created a new page. Its content needs to - ** be initialized. But first some error checks: - ** - ** (*) obsolete. Was: maximum page number is 2^31 - ** (2) Never try to fetch the locking page - */ - if( pgno==PAGER_MJ_PGNO(pPager) ){ - rc = SQLITE_CORRUPT_BKPT; - goto pager_acquire_err; - } - - pPg->pPager = pPager; - - assert( !isOpen(pPager->fd) || !MEMDB ); - if( !isOpen(pPager->fd) || pPager->dbSizepPager->mxPgno ){ - rc = SQLITE_FULL; - goto pager_acquire_err; - } - if( noContent ){ - /* Failure to set the bits in the InJournal bit-vectors is benign. - ** It merely means that we might do some extra work to journal a - ** page that does not need to be journaled. Nevertheless, be sure - ** to test the case where a malloc error occurs while trying to set - ** a bit in a bit vector. - */ - sqlcipher_sqlite3BeginBenignMalloc(); - if( pgno<=pPager->dbOrigSize ){ - TESTONLY( rc = ) sqlcipher_sqlite3BitvecSet(pPager->pInJournal, pgno); - testcase( rc==SQLITE_NOMEM ); - } - TESTONLY( rc = ) addToSavepointBitvecs(pPager, pgno); - testcase( rc==SQLITE_NOMEM ); - sqlcipher_sqlite3EndBenignMalloc(); - } - memset(pPg->pData, 0, pPager->pageSize); - IOTRACE(("ZERO %p %d\n", pPager, pgno)); - }else{ - assert( pPg->pPager==pPager ); - pPager->aStat[PAGER_STAT_MISS]++; - rc = readDbPage(pPg); - if( rc!=SQLITE_OK ){ - goto pager_acquire_err; - } - } - pager_set_pagehash(pPg); - } - return SQLITE_OK; - -pager_acquire_err: - assert( rc!=SQLITE_OK ); - if( pPg ){ - sqlcipher_sqlite3PcacheDrop(pPg); } - pagerUnlockIfUnused(pPager); - *ppPage = 0; - return rc; -} - -#if SQLITE_MAX_MMAP_SIZE>0 -/* The page getter for when memory-mapped I/O is enabled */ -static int getPageMMap( - Pager *pPager, /* The pager open on the database file */ - Pgno pgno, /* Page number to fetch */ - DbPage **ppPage, /* Write a pointer to the page here */ - int flags /* PAGER_GET_XXX flags */ -){ - int rc = SQLITE_OK; - PgHdr *pPg = 0; - u32 iFrame = 0; /* Frame to read from WAL file */ - - /* It is acceptable to use a read-only (mmap) page for any page except - ** page 1 if there is no write-transaction open or the ACQUIRE_READONLY - ** flag was specified by the caller. And so long as the db is not a - ** temporary or in-memory database. */ - const int bMmapOk = (pgno>1 - && (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY)) - ); - - assert( USEFETCH(pPager) ); -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - assert( pPager->xCodec==0 ); -#endif -/* END SQLCIPHER */ - /* Optimization note: Adding the "pgno<=1" term before "pgno==0" here - ** allows the compiler optimizer to reuse the results of the "pgno>1" - ** test in the previous statement, and avoid testing pgno==0 in the - ** common case where pgno is large. */ - if( pgno<=1 && pgno==0 ){ - return SQLITE_CORRUPT_BKPT; - } - assert( pPager->eState>=PAGER_READER ); - assert( assert_pager_state(pPager) ); - assert( pPager->hasHeldSharedLock==1 ); - assert( pPager->errCode==SQLITE_OK ); + /* Search the hash table or tables for an entry matching page number + ** pgno. Each iteration of the following for() loop searches one + ** hash table (each hash table indexes up to HASHTABLE_NPAGE frames). + ** + ** This code might run concurrently to the code in walIndexAppend() + ** that adds entries to the wal-index (and possibly to this hash + ** table). This means the value just read from the hash + ** slot (aHash[iKey]) may have been added before or after the + ** current read transaction was opened. Values added after the + ** read transaction was opened may have been written incorrectly - + ** i.e. these slots may contain garbage data. However, we assume + ** that any slots written before the current read transaction was + ** opened remain unmodified. + ** + ** For the reasons above, the if(...) condition featured in the inner + ** loop of the following block is more stringent that would be required + ** if we had exclusive access to the hash-table: + ** + ** (aPgno[iFrame]==pgno): + ** This condition filters out normal hash-table collisions. + ** + ** (iFrame<=iLast): + ** This condition filters out entries that were added to the hash + ** table after the current read-transaction had started. + */ + iMinHash = walFramePage(pWal->minFrame); + for(iHash=walFramePage(iLast); iHash>=iMinHash; iHash--){ + WalHashLoc sLoc; /* Hash table location */ + int iKey; /* Hash slot index */ + int nCollide; /* Number of hash collisions remaining */ + int rc; /* Error code */ + u32 iH; - if( bMmapOk && pagerUseWal(pPager) ){ - rc = sqlcipher_sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame); + rc = walHashGet(pWal, iHash, &sLoc); if( rc!=SQLITE_OK ){ - *ppPage = 0; return rc; } - } - if( bMmapOk && iFrame==0 ){ - void *pData = 0; - rc = sqlcipher_sqlite3OsFetch(pPager->fd, - (i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData - ); - if( rc==SQLITE_OK && pData ){ - if( pPager->eState>PAGER_READER || pPager->tempFile ){ - pPg = sqlcipher_sqlite3PagerLookup(pPager, pgno); - } - if( pPg==0 ){ - rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg); - }else{ - sqlcipher_sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData); + nCollide = HASHTABLE_NSLOT; + iKey = walHash(pgno); + while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ + u32 iFrame = iH + sLoc.iZero; + if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){ + assert( iFrame>iRead || CORRUPT_DB ); + iRead = iFrame; } - if( pPg ){ - assert( rc==SQLITE_OK ); - *ppPage = pPg; - return SQLITE_OK; + if( (nCollide--)==0 ){ + return SQLITE_CORRUPT_BKPT; } + iKey = walNextHash(iKey); } - if( rc!=SQLITE_OK ){ - *ppPage = 0; - return rc; - } + if( iRead ) break; } - return getPageNormal(pPager, pgno, ppPage, flags); -} -#endif /* SQLITE_MAX_MMAP_SIZE>0 */ - -/* The page getter method for when the pager is an error state */ -static int getPageError( - Pager *pPager, /* The pager open on the database file */ - Pgno pgno, /* Page number to fetch */ - DbPage **ppPage, /* Write a pointer to the page here */ - int flags /* PAGER_GET_XXX flags */ -){ - UNUSED_PARAMETER(pgno); - UNUSED_PARAMETER(flags); - assert( pPager->errCode!=SQLITE_OK ); - *ppPage = 0; - return pPager->errCode; -} +#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT + /* If expensive assert() statements are available, do a linear search + ** of the wal-index file content. Make sure the results agree with the + ** result obtained using the hash indexes above. */ + { + u32 iRead2 = 0; + u32 iTest; + assert( pWal->bShmUnreliable || pWal->minFrame>0 ); + for(iTest=iLast; iTest>=pWal->minFrame && iTest>0; iTest--){ + if( walFramePgno(pWal, iTest)==pgno ){ + iRead2 = iTest; + break; + } + } + assert( iRead==iRead2 ); + } +#endif -/* Dispatch all page fetch requests to the appropriate getter method. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerGet( - Pager *pPager, /* The pager open on the database file */ - Pgno pgno, /* Page number to fetch */ - DbPage **ppPage, /* Write a pointer to the page here */ - int flags /* PAGER_GET_XXX flags */ -){ - return pPager->xGet(pPager, pgno, ppPage, flags); + *piRead = iRead; + return SQLITE_OK; } /* -** Acquire a page if it is already in the in-memory cache. Do -** not read the page from disk. Return a pointer to the page, -** or 0 if the page is not in cache. -** -** See also sqlcipher_sqlite3PagerGet(). The difference between this routine -** and sqlcipher_sqlite3PagerGet() is that _get() will go to the disk and read -** in the page if the page is not already in cache. This routine -** returns NULL if the page is not in cache or if a disk I/O error -** has ever happened. +** Read the contents of frame iRead from the wal file into buffer pOut +** (which is nOut bytes in size). Return SQLITE_OK if successful, or an +** error code otherwise. */ -SQLITE_PRIVATE DbPage *sqlcipher_sqlite3PagerLookup(Pager *pPager, Pgno pgno){ - sqlcipher_sqlite3_pcache_page *pPage; - assert( pPager!=0 ); - assert( pgno!=0 ); - assert( pPager->pPCache!=0 ); - pPage = sqlcipher_sqlite3PcacheFetch(pPager->pPCache, pgno, 0); - assert( pPage==0 || pPager->hasHeldSharedLock ); - if( pPage==0 ) return 0; - return sqlcipher_sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage); +SQLITE_PRIVATE int sqlcipher_sqlite3WalReadFrame( + Wal *pWal, /* WAL handle */ + u32 iRead, /* Frame to read */ + int nOut, /* Size of buffer pOut in bytes */ + u8 *pOut /* Buffer to write page data to */ +){ + int sz; + i64 iOffset; + sz = pWal->hdr.szPage; + sz = (sz&0xfe00) + ((sz&0x0001)<<16); + testcase( sz<=32768 ); + testcase( sz>=65536 ); + iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE; + /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ + return sqlcipher_sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset); } /* -** Release a page reference. -** -** The sqlcipher_sqlite3PagerUnref() and sqlcipher_sqlite3PagerUnrefNotNull() may only be -** used if we know that the page being released is not the last page. -** The btree layer always holds page1 open until the end, so these first -** to routines can be used to release any page other than BtShared.pPage1. -** -** Use sqlcipher_sqlite3PagerUnrefPageOne() to release page1. This latter routine -** checks the total number of outstanding pages and if the number of -** pages reaches zero it drops the database lock. +** Return the size of the database in pages (or zero, if unknown). */ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerUnrefNotNull(DbPage *pPg){ - TESTONLY( Pager *pPager = pPg->pPager; ) - assert( pPg!=0 ); - if( pPg->flags & PGHDR_MMAP ){ - assert( pPg->pgno!=1 ); /* Page1 is never memory mapped */ - pagerReleaseMapPage(pPg); - }else{ - sqlcipher_sqlite3PcacheRelease(pPg); +SQLITE_PRIVATE Pgno sqlcipher_sqlite3WalDbsize(Wal *pWal){ + if( pWal && ALWAYS(pWal->readLock>=0) ){ + return pWal->hdr.nPage; } - /* Do not use this routine to release the last reference to page1 */ - assert( sqlcipher_sqlite3PcacheRefCount(pPager->pPCache)>0 ); -} -SQLITE_PRIVATE void sqlcipher_sqlite3PagerUnref(DbPage *pPg){ - if( pPg ) sqlcipher_sqlite3PagerUnrefNotNull(pPg); -} -SQLITE_PRIVATE void sqlcipher_sqlite3PagerUnrefPageOne(DbPage *pPg){ - Pager *pPager; - assert( pPg!=0 ); - assert( pPg->pgno==1 ); - assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */ - pPager = pPg->pPager; - sqlcipher_sqlite3PcacheRelease(pPg); - pagerUnlockIfUnused(pPager); + return 0; } + /* -** This function is called at the start of every write transaction. -** There must already be a RESERVED or EXCLUSIVE lock on the database -** file when this routine is called. -** -** Open the journal file for pager pPager and write a journal header -** to the start of it. If there are active savepoints, open the sub-journal -** as well. This function is only used when the journal file is being -** opened to write a rollback log for a transaction. It is not used -** when opening a hot journal file to roll it back. +** This function starts a write transaction on the WAL. ** -** If the journal file is already open (as it may be in exclusive mode), -** then this function just writes a journal header to the start of the -** already open file. +** A read transaction must have already been started by a prior call +** to sqlcipher_sqlite3WalBeginReadTransaction(). ** -** Whether or not the journal file is opened by this function, the -** Pager.pInJournal bitvec structure is allocated. +** If another thread or process has written into the database since +** the read transaction was started, then it is not possible for this +** thread to write as doing so would cause a fork. So this routine +** returns SQLITE_BUSY in that case and no write transaction is started. ** -** Return SQLITE_OK if everything is successful. Otherwise, return -** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or -** an IO error code if opening or writing the journal file fails. +** There can only be a single writer active at a time. */ -static int pager_open_journal(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - sqlcipher_sqlite3_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */ - - assert( pPager->eState==PAGER_WRITER_LOCKED ); - assert( assert_pager_state(pPager) ); - assert( pPager->pInJournal==0 ); - - /* If already in the error state, this function is a no-op. But on - ** the other hand, this routine is never called if we are already in - ** an error state. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - - if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ - pPager->pInJournal = sqlcipher_sqlite3BitvecCreate(pPager->dbSize); - if( pPager->pInJournal==0 ){ - return SQLITE_NOMEM_BKPT; - } - - /* Open the journal file if it is not already open. */ - if( !isOpen(pPager->jfd) ){ - if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ - sqlcipher_sqlite3MemJournalOpen(pPager->jfd); - }else{ - int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; - int nSpill; +SQLITE_PRIVATE int sqlcipher_sqlite3WalBeginWriteTransaction(Wal *pWal){ + int rc; - if( pPager->tempFile ){ - flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL); - nSpill = sqlcipher_sqlite3Config.nStmtSpill; - }else{ - flags |= SQLITE_OPEN_MAIN_JOURNAL; - nSpill = jrnlBufferSize(pPager); - } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + /* If the write-lock is already held, then it was obtained before the + ** read-transaction was even opened, making this call a no-op. + ** Return early. */ + if( pWal->writeLock ){ + assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) ); + return SQLITE_OK; + } +#endif - /* Verify that the database still has the same name as it did when - ** it was originally opened. */ - rc = databaseIsUnmoved(pPager); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3JournalOpen ( - pVfs, pPager->zJournal, pPager->jfd, flags, nSpill - ); - } - } - assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); - } + /* Cannot start a write transaction without first holding a read + ** transaction. */ + assert( pWal->readLock>=0 ); + assert( pWal->writeLock==0 && pWal->iReCksum==0 ); + if( pWal->readOnly ){ + return SQLITE_READONLY; + } - /* Write the first journal header to the journal file and open - ** the sub-journal if necessary. - */ - if( rc==SQLITE_OK ){ - /* TODO: Check if all of these are really required. */ - pPager->nRec = 0; - pPager->journalOff = 0; - pPager->setSuper = 0; - pPager->journalHdr = 0; - rc = writeJournalHdr(pPager); - } + /* Only one writer allowed at a time. Get the write lock. Return + ** SQLITE_BUSY if unable. + */ + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); + if( rc ){ + return rc; } + pWal->writeLock = 1; - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; - }else{ - assert( pPager->eState==PAGER_WRITER_LOCKED ); - pPager->eState = PAGER_WRITER_CACHEMOD; + /* If another connection has written to the database file since the + ** time the read transaction on this connection was started, then + ** the write is disallowed. + */ + if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){ + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); + pWal->writeLock = 0; + rc = SQLITE_BUSY_SNAPSHOT; } return rc; } /* -** Begin a write-transaction on the specified pager object. If a -** write-transaction has already been opened, this function is a no-op. +** End a write transaction. The commit has already been done. This +** routine merely releases the lock. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3WalEndWriteTransaction(Wal *pWal){ + if( pWal->writeLock ){ + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); + pWal->writeLock = 0; + pWal->iReCksum = 0; + pWal->truncateOnCommit = 0; + } + return SQLITE_OK; +} + +/* +** If any data has been written (but not committed) to the log file, this +** function moves the write-pointer back to the start of the transaction. ** -** If the exFlag argument is false, then acquire at least a RESERVED -** lock on the database file. If exFlag is true, then acquire at least -** an EXCLUSIVE lock. If such a lock is already held, no locking -** functions need be called. +** Additionally, the callback function is invoked for each frame written +** to the WAL since the start of the transaction. If the callback returns +** other than SQLITE_OK, it is not invoked again and the error code is +** returned to the caller. ** -** If the subjInMemory argument is non-zero, then any sub-journal opened -** within this transaction will be opened as an in-memory file. This -** has no effect if the sub-journal is already opened (as it may be when -** running in exclusive mode) or if the transaction does not require a -** sub-journal. If the subjInMemory argument is zero, then any required -** sub-journal is implemented in-memory if pPager is an in-memory database, -** or using a temporary file otherwise. +** Otherwise, if the callback function does not return an error, this +** function returns SQLITE_OK. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ +SQLITE_PRIVATE int sqlcipher_sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){ int rc = SQLITE_OK; + if( ALWAYS(pWal->writeLock) ){ + Pgno iMax = pWal->hdr.mxFrame; + Pgno iFrame; - if( pPager->errCode ) return pPager->errCode; - assert( pPager->eState>=PAGER_READER && pPager->eStatesubjInMemory = (u8)subjInMemory; - - if( ALWAYS(pPager->eState==PAGER_READER) ){ - assert( pPager->pInJournal==0 ); - - if( pagerUseWal(pPager) ){ - /* If the pager is configured to use locking_mode=exclusive, and an - ** exclusive lock on the database is not already held, obtain it now. - */ - if( pPager->exclusiveMode && sqlcipher_sqlite3WalExclusiveMode(pPager->pWal, -1) ){ - rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); - if( rc!=SQLITE_OK ){ - return rc; - } - (void)sqlcipher_sqlite3WalExclusiveMode(pPager->pWal, 1); - } - - /* Grab the write lock on the log file. If successful, upgrade to - ** PAGER_RESERVED state. Otherwise, return an error code to the caller. - ** The busy-handler is not invoked if another connection already - ** holds the write-lock. If possible, the upper layer will call it. - */ - rc = sqlcipher_sqlite3WalBeginWriteTransaction(pPager->pWal); - }else{ - /* Obtain a RESERVED lock on the database file. If the exFlag parameter - ** is true, then immediately upgrade this to an EXCLUSIVE lock. The - ** busy-handler callback can be used when upgrading to the EXCLUSIVE - ** lock, but not when obtaining the RESERVED lock. - */ - rc = pagerLockDb(pPager, RESERVED_LOCK); - if( rc==SQLITE_OK && exFlag ){ - rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); - } - } + /* Restore the clients cache of the wal-index header to the state it + ** was in before the client began writing to the database. + */ + memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr)); - if( rc==SQLITE_OK ){ - /* Change to WRITER_LOCKED state. - ** - ** WAL mode sets Pager.eState to PAGER_WRITER_LOCKED or CACHEMOD - ** when it has an open transaction, but never to DBMOD or FINISHED. - ** This is because in those states the code to roll back savepoint - ** transactions may copy data from the sub-journal into the database - ** file as well as into the page cache. Which would be incorrect in - ** WAL mode. + for(iFrame=pWal->hdr.mxFrame+1; + ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; + iFrame++ + ){ + /* This call cannot fail. Unless the page for which the page number + ** is passed as the second argument is (a) in the cache and + ** (b) has an outstanding reference, then xUndo is either a no-op + ** (if (a) is false) or simply expels the page from the cache (if (b) + ** is false). + ** + ** If the upper layer is doing a rollback, it is guaranteed that there + ** are no outstanding references to any page other than page 1. And + ** page 1 is never written to the log until the transaction is + ** committed. As a result, the call to xUndo may not fail. */ - pPager->eState = PAGER_WRITER_LOCKED; - pPager->dbHintSize = pPager->dbSize; - pPager->dbFileSize = pPager->dbSize; - pPager->dbOrigSize = pPager->dbSize; - pPager->journalOff = 0; + assert( walFramePgno(pWal, iFrame)!=1 ); + rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); } - - assert( rc==SQLITE_OK || pPager->eState==PAGER_READER ); - assert( rc!=SQLITE_OK || pPager->eState==PAGER_WRITER_LOCKED ); - assert( assert_pager_state(pPager) ); + if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal); } - - PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager))); return rc; } /* -** Write page pPg onto the end of the rollback journal. +** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32 +** values. This function populates the array with values required to +** "rollback" the write position of the WAL handle back to the current +** point in the event of a savepoint rollback (via WalSavepointUndo()). */ -static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - int rc; - u32 cksum; - char *pData2; - i64 iOff = pPager->journalOff; - - /* We should never write to the journal file the page that - ** contains the database locks. The following assert verifies - ** that we do not. */ - assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); +SQLITE_PRIVATE void sqlcipher_sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){ + assert( pWal->writeLock ); + aWalData[0] = pWal->hdr.mxFrame; + aWalData[1] = pWal->hdr.aFrameCksum[0]; + aWalData[2] = pWal->hdr.aFrameCksum[1]; + aWalData[3] = pWal->nCkpt; +} - assert( pPager->journalHdr<=pPager->journalOff ); - CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); - cksum = pager_cksum(pPager, (u8*)pData2); +/* +** Move the write position of the WAL back to the point identified by +** the values in the aWalData[] array. aWalData must point to an array +** of WAL_SAVEPOINT_NDATA u32 values that has been previously populated +** by a call to WalSavepoint(). +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){ + int rc = SQLITE_OK; - /* Even if an IO or diskfull error occurs while journalling the - ** page in the block above, set the need-sync flag for the page. - ** Otherwise, when the transaction is rolled back, the logic in - ** playback_one_page() will think that the page needs to be restored - ** in the database file. And if an IO error occurs while doing so, - ** then corruption may follow. - */ - pPg->flags |= PGHDR_NEED_SYNC; + assert( pWal->writeLock ); + assert( aWalData[3]!=pWal->nCkpt || aWalData[0]<=pWal->hdr.mxFrame ); - rc = write32bits(pPager->jfd, iOff, pPg->pgno); - if( rc!=SQLITE_OK ) return rc; - rc = sqlcipher_sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4); - if( rc!=SQLITE_OK ) return rc; - rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum); - if( rc!=SQLITE_OK ) return rc; + if( aWalData[3]!=pWal->nCkpt ){ + /* This savepoint was opened immediately after the write-transaction + ** was started. Right after that, the writer decided to wrap around + ** to the start of the log. Update the savepoint values to match. + */ + aWalData[0] = 0; + aWalData[3] = pWal->nCkpt; + } - IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno, - pPager->journalOff, pPager->pageSize)); - PAGER_INCR(sqlcipher_sqlite3_pager_writej_count); - PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n", - PAGERID(pPager), pPg->pgno, - ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg))); + if( aWalData[0]hdr.mxFrame ){ + pWal->hdr.mxFrame = aWalData[0]; + pWal->hdr.aFrameCksum[0] = aWalData[1]; + pWal->hdr.aFrameCksum[1] = aWalData[2]; + walCleanupHash(pWal); + } - pPager->journalOff += 8 + pPager->pageSize; - pPager->nRec++; - assert( pPager->pInJournal!=0 ); - rc = sqlcipher_sqlite3BitvecSet(pPager->pInJournal, pPg->pgno); - testcase( rc==SQLITE_NOMEM ); - assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); - rc |= addToSavepointBitvecs(pPager, pPg->pgno); - assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); return rc; } /* -** Mark a single data page as writeable. The page is written into the -** main journal or sub-journal as required. If the page is written into -** one of the journals, the corresponding bit is set in the -** Pager.pInJournal bitvec and the PagerSavepoint.pInSavepoint bitvecs -** of any open savepoints as appropriate. +** This function is called just before writing a set of frames to the log +** file (see sqlcipher_sqlite3WalFrames()). It checks to see if, instead of appending +** to the current log file, it is possible to overwrite the start of the +** existing log file with the new frames (i.e. "reset" the log). If so, +** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left +** unchanged. +** +** SQLITE_OK is returned if no error is encountered (regardless of whether +** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned +** if an error occurs. */ -static int pager_write(PgHdr *pPg){ - Pager *pPager = pPg->pPager; +static int walRestartLog(Wal *pWal){ int rc = SQLITE_OK; + int cnt; - /* This routine is not called unless a write-transaction has already - ** been started. The journal file may or may not be open at this point. - ** It is never called in the ERROR state. - */ - assert( pPager->eState==PAGER_WRITER_LOCKED - || pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); - assert( pPager->errCode==0 ); - assert( pPager->readOnly==0 ); - CHECK_PAGE(pPg); - - /* The journal file needs to be opened. Higher level routines have already - ** obtained the necessary locks to begin the write-transaction, but the - ** rollback journal might not yet be open. Open it now if this is the case. - ** - ** This is done before calling sqlcipher_sqlite3PcacheMakeDirty() on the page. - ** Otherwise, if it were done after calling sqlcipher_sqlite3PcacheMakeDirty(), then - ** an error might occur and the pager would end up in WRITER_LOCKED state - ** with pages marked as dirty in the cache. - */ - if( pPager->eState==PAGER_WRITER_LOCKED ){ - rc = pager_open_journal(pPager); - if( rc!=SQLITE_OK ) return rc; - } - assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); - assert( assert_pager_state(pPager) ); - - /* Mark the page that is about to be modified as dirty. */ - sqlcipher_sqlite3PcacheMakeDirty(pPg); - - /* If a rollback journal is in use, them make sure the page that is about - ** to change is in the rollback journal, or if the page is a new page off - ** then end of the file, make sure it is marked as PGHDR_NEED_SYNC. - */ - assert( (pPager->pInJournal!=0) == isOpen(pPager->jfd) ); - if( pPager->pInJournal!=0 - && sqlcipher_sqlite3BitvecTestNotNull(pPager->pInJournal, pPg->pgno)==0 - ){ - assert( pagerUseWal(pPager)==0 ); - if( pPg->pgno<=pPager->dbOrigSize ){ - rc = pagerAddPageToRollbackJournal(pPg); - if( rc!=SQLITE_OK ){ + if( pWal->readLock==0 ){ + volatile WalCkptInfo *pInfo = walCkptInfo(pWal); + assert( pInfo->nBackfill==pWal->hdr.mxFrame ); + if( pInfo->nBackfill>0 ){ + u32 salt1; + sqlcipher_sqlite3_randomness(4, &salt1); + rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); + if( rc==SQLITE_OK ){ + /* If all readers are using WAL_READ_LOCK(0) (in other words if no + ** readers are currently using the WAL), then the transactions + ** frames will overwrite the start of the existing log. Update the + ** wal-index header to reflect this. + ** + ** In theory it would be Ok to update the cache of the header only + ** at this point. But updating the actual wal-index header is also + ** safe and means there is no special case for sqlcipher_sqlite3WalUndo() + ** to handle if this transaction is rolled back. */ + walRestartHdr(pWal, salt1); + walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); + }else if( rc!=SQLITE_BUSY ){ return rc; } - }else{ - if( pPager->eState!=PAGER_WRITER_DBMOD ){ - pPg->flags |= PGHDR_NEED_SYNC; - } - PAGERTRACE(("APPEND %d page %d needSync=%d\n", - PAGERID(pPager), pPg->pgno, - ((pPg->flags&PGHDR_NEED_SYNC)?1:0))); } + walUnlockShared(pWal, WAL_READ_LOCK(0)); + pWal->readLock = -1; + cnt = 0; + do{ + int notUsed; + rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); + }while( rc==WAL_RETRY ); + assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */ + testcase( (rc&0xff)==SQLITE_IOERR ); + testcase( rc==SQLITE_PROTOCOL ); + testcase( rc==SQLITE_OK ); } + return rc; +} - /* The PGHDR_DIRTY bit is set above when the page was added to the dirty-list - ** and before writing the page into the rollback journal. Wait until now, - ** after the page has been successfully journalled, before setting the - ** PGHDR_WRITEABLE bit that indicates that the page can be safely modified. - */ - pPg->flags |= PGHDR_WRITEABLE; +/* +** Information about the current state of the WAL file and where +** the next fsync should occur - passed from sqlcipher_sqlite3WalFrames() into +** walWriteToLog(). +*/ +typedef struct WalWriter { + Wal *pWal; /* The complete WAL information */ + sqlcipher_sqlite3_file *pFd; /* The WAL file to which we write */ + sqlcipher_sqlite3_int64 iSyncPoint; /* Fsync at this offset */ + int syncFlags; /* Flags for the fsync */ + int szPage; /* Size of one page */ +} WalWriter; - /* If the statement journal is open and the page is not in it, - ** then write the page into the statement journal. - */ - if( pPager->nSavepoint>0 ){ - rc = subjournalPageIfRequired(pPg); +/* +** Write iAmt bytes of content into the WAL file beginning at iOffset. +** Do a sync when crossing the p->iSyncPoint boundary. +** +** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt, +** first write the part before iSyncPoint, then sync, then write the +** rest. +*/ +static int walWriteToLog( + WalWriter *p, /* WAL to write to */ + void *pContent, /* Content to be written */ + int iAmt, /* Number of bytes to write */ + sqlcipher_sqlite3_int64 iOffset /* Start writing at this offset */ +){ + int rc; + if( iOffsetiSyncPoint && iOffset+iAmt>=p->iSyncPoint ){ + int iFirstAmt = (int)(p->iSyncPoint - iOffset); + rc = sqlcipher_sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset); + if( rc ) return rc; + iOffset += iFirstAmt; + iAmt -= iFirstAmt; + pContent = (void*)(iFirstAmt + (char*)pContent); + assert( WAL_SYNC_FLAGS(p->syncFlags)!=0 ); + rc = sqlcipher_sqlite3OsSync(p->pFd, WAL_SYNC_FLAGS(p->syncFlags)); + if( iAmt==0 || rc ) return rc; } + rc = sqlcipher_sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset); + return rc; +} - /* Update the database size and return. */ - if( pPager->dbSizepgno ){ - pPager->dbSize = pPg->pgno; - } +/* +** Write out a single frame of the WAL +*/ +static int walWriteOneFrame( + WalWriter *p, /* Where to write the frame */ + PgHdr *pPage, /* The page of the frame to be written */ + int nTruncate, /* The commit flag. Usually 0. >0 for commit */ + sqlcipher_sqlite3_int64 iOffset /* Byte offset at which to write */ +){ + int rc; /* Result code from subfunctions */ + void *pData; /* Data actually written */ + u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */ +#if defined(SQLITE_HAS_CODEC) + if( (pData = sqlcipherPagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT; +#else + pData = pPage->pData; +#endif + walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame); + rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset); + if( rc ) return rc; + /* Write the page data */ + rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame)); return rc; } /* -** This is a variant of sqlcipher_sqlite3PagerWrite() that runs when the sector size -** is larger than the page size. SQLite makes the (reasonable) assumption that -** all bytes of a sector are written together by hardware. Hence, all bytes of -** a sector need to be journalled in case of a power loss in the middle of -** a write. +** This function is called as part of committing a transaction within which +** one or more frames have been overwritten. It updates the checksums for +** all frames written to the wal file by the current transaction starting +** with the earliest to have been overwritten. ** -** Usually, the sector size is less than or equal to the page size, in which -** case pages can be individually written. This routine only runs in the -** exceptional case where the page size is smaller than the sector size. +** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ -static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){ - int rc = SQLITE_OK; /* Return code */ - Pgno nPageCount; /* Total number of pages in database file */ - Pgno pg1; /* First page of the sector pPg is located on. */ - int nPage = 0; /* Number of pages starting at pg1 to journal */ - int ii; /* Loop counter */ - int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */ - Pager *pPager = pPg->pPager; /* The pager that owns pPg */ - Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); - - /* Set the doNotSpill NOSYNC bit to 1. This is because we cannot allow - ** a journal header to be written between the pages journaled by - ** this function. - */ - assert( !MEMDB ); - assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)==0 ); - pPager->doNotSpill |= SPILLFLAG_NOSYNC; +static int walRewriteChecksums(Wal *pWal, u32 iLast){ + const int szPage = pWal->szPage;/* Database page size */ + int rc = SQLITE_OK; /* Return code */ + u8 *aBuf; /* Buffer to load data from wal file into */ + u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-headers in */ + u32 iRead; /* Next frame to read from wal file */ + i64 iCksumOff; - /* This trick assumes that both the page-size and sector-size are - ** an integer power of 2. It sets variable pg1 to the identifier - ** of the first page of the sector pPg is located on. - */ - pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1; + aBuf = sqlcipher_sqlite3_malloc(szPage + WAL_FRAME_HDRSIZE); + if( aBuf==0 ) return SQLITE_NOMEM_BKPT; - nPageCount = pPager->dbSize; - if( pPg->pgno>nPageCount ){ - nPage = (pPg->pgno - pg1)+1; - }else if( (pg1+nPagePerSector-1)>nPageCount ){ - nPage = nPageCount+1-pg1; + /* Find the checksum values to use as input for the recalculating the + ** first checksum. If the first frame is frame 1 (implying that the current + ** transaction restarted the wal file), these values must be read from the + ** wal-file header. Otherwise, read them from the frame header of the + ** previous frame. */ + assert( pWal->iReCksum>0 ); + if( pWal->iReCksum==1 ){ + iCksumOff = 24; }else{ - nPage = nPagePerSector; + iCksumOff = walFrameOffset(pWal->iReCksum-1, szPage) + 16; } - assert(nPage>0); - assert(pg1<=pPg->pgno); - assert((pg1+nPage)>pPg->pgno); + rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aBuf, sizeof(u32)*2, iCksumOff); + pWal->hdr.aFrameCksum[0] = sqlcipher_sqlite3Get4byte(aBuf); + pWal->hdr.aFrameCksum[1] = sqlcipher_sqlite3Get4byte(&aBuf[sizeof(u32)]); - for(ii=0; iipgno || !sqlcipher_sqlite3BitvecTest(pPager->pInJournal, pg) ){ - if( pg!=PAGER_MJ_PGNO(pPager) ){ - rc = sqlcipher_sqlite3PagerGet(pPager, pg, &pPage, 0); - if( rc==SQLITE_OK ){ - rc = pager_write(pPage); - if( pPage->flags&PGHDR_NEED_SYNC ){ - needSync = 1; - } - sqlcipher_sqlite3PagerUnrefNotNull(pPage); - } - } - }else if( (pPage = sqlcipher_sqlite3PagerLookup(pPager, pg))!=0 ){ - if( pPage->flags&PGHDR_NEED_SYNC ){ - needSync = 1; - } - sqlcipher_sqlite3PagerUnrefNotNull(pPage); - } - } + iRead = pWal->iReCksum; + pWal->iReCksum = 0; + for(; rc==SQLITE_OK && iRead<=iLast; iRead++){ + i64 iOff = walFrameOffset(iRead, szPage); + rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aBuf, szPage+WAL_FRAME_HDRSIZE, iOff); + if( rc==SQLITE_OK ){ + u32 iPgno, nDbSize; + iPgno = sqlcipher_sqlite3Get4byte(aBuf); + nDbSize = sqlcipher_sqlite3Get4byte(&aBuf[4]); - /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages - ** starting at pg1, then it needs to be set for all of them. Because - ** writing to any of these nPage pages may damage the others, the - ** journal file must contain sync()ed copies of all of them - ** before any of them can be written out to the database file. - */ - if( rc==SQLITE_OK && needSync ){ - assert( !MEMDB ); - for(ii=0; iiflags |= PGHDR_NEED_SYNC; - sqlcipher_sqlite3PagerUnrefNotNull(pPage); - } + walEncodeFrame(pWal, iPgno, nDbSize, &aBuf[WAL_FRAME_HDRSIZE], aFrame); + rc = sqlcipher_sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOff); } } - assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)!=0 ); - pPager->doNotSpill &= ~SPILLFLAG_NOSYNC; + sqlcipher_sqlite3_free(aBuf); return rc; } /* -** Mark a data page as writeable. This routine must be called before -** making changes to a page. The caller must check the return value -** of this function and be careful not to change any page data unless -** this routine returns SQLITE_OK. -** -** The difference between this function and pager_write() is that this -** function also deals with the special case where 2 or more pages -** fit on a single disk sector. In this case all co-resident pages -** must have been written to the journal file before returning. -** -** If an error occurs, SQLITE_NOMEM or an IO error code is returned -** as appropriate. Otherwise, SQLITE_OK. +** Write a set of frames to the log. The caller must hold the write-lock +** on the log file (obtained using sqlcipher_sqlite3WalBeginWriteTransaction()). */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerWrite(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - assert( (pPg->flags & PGHDR_MMAP)==0 ); - assert( pPager->eState>=PAGER_WRITER_LOCKED ); - assert( assert_pager_state(pPager) ); - if( (pPg->flags & PGHDR_WRITEABLE)!=0 && pPager->dbSize>=pPg->pgno ){ - if( pPager->nSavepoint ) return subjournalPageIfRequired(pPg); - return SQLITE_OK; - }else if( pPager->errCode ){ - return pPager->errCode; - }else if( pPager->sectorSize > (u32)pPager->pageSize ){ - assert( pPager->tempFile==0 ); - return pagerWriteLargeSector(pPg); - }else{ - return pager_write(pPg); - } -} +SQLITE_PRIVATE int sqlcipher_sqlite3WalFrames( + Wal *pWal, /* Wal handle to write to */ + int szPage, /* Database page-size in bytes */ + PgHdr *pList, /* List of dirty pages to write */ + Pgno nTruncate, /* Database size after this commit */ + int isCommit, /* True if this is a commit */ + int sync_flags /* Flags to pass to OsSync() (or 0) */ +){ + int rc; /* Used to catch return codes */ + u32 iFrame; /* Next frame address */ + PgHdr *p; /* Iterator to run through pList with. */ + PgHdr *pLast = 0; /* Last frame in list */ + int nExtra = 0; /* Number of extra copies of last page */ + int szFrame; /* The size of a single frame */ + i64 iOffset; /* Next byte to write in WAL file */ + WalWriter w; /* The writer */ + u32 iFirst = 0; /* First frame that may be overwritten */ + WalIndexHdr *pLive; /* Pointer to shared header */ -/* -** Return TRUE if the page given in the argument was previously passed -** to sqlcipher_sqlite3PagerWrite(). In other words, return TRUE if it is ok -** to change the content of the page. -*/ -#ifndef NDEBUG -SQLITE_PRIVATE int sqlcipher_sqlite3PagerIswriteable(DbPage *pPg){ - return pPg->flags & PGHDR_WRITEABLE; -} -#endif + assert( pList ); + assert( pWal->writeLock ); -/* -** A call to this routine tells the pager that it is not necessary to -** write the information on page pPg back to the disk, even though -** that page might be marked as dirty. This happens, for example, when -** the page has been added as a leaf of the freelist and so its -** content no longer matters. -** -** The overlying software layer calls this routine when all of the data -** on the given page is unused. The pager marks the page as clean so -** that it does not get written to disk. -** -** Tests show that this optimization can quadruple the speed of large -** DELETE operations. -** -** This optimization cannot be used with a temp-file, as the page may -** have been dirty at the start of the transaction. In that case, if -** memory pressure forces page pPg out of the cache, the data does need -** to be written out to disk so that it may be read back in if the -** current transaction is rolled back. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerDontWrite(PgHdr *pPg){ - Pager *pPager = pPg->pPager; - if( !pPager->tempFile && (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ - PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager))); - IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno)) - pPg->flags |= PGHDR_DONT_WRITE; - pPg->flags &= ~PGHDR_WRITEABLE; - testcase( pPg->flags & PGHDR_NEED_SYNC ); - pager_set_pagehash(pPg); + /* If this frame set completes a transaction, then nTruncate>0. If + ** nTruncate==0 then this frame set does not complete the transaction. */ + assert( (isCommit!=0)==(nTruncate!=0) ); + +#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) + { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){} + WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n", + pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill")); } -} +#endif -/* -** This routine is called to increment the value of the database file -** change-counter, stored as a 4-byte big-endian integer starting at -** byte offset 24 of the pager file. The secondary change counter at -** 92 is also updated, as is the SQLite version number at offset 96. -** -** But this only happens if the pPager->changeCountDone flag is false. -** To avoid excess churning of page 1, the update only happens once. -** See also the pager_write_changecounter() routine that does an -** unconditional update of the change counters. -** -** If the isDirectMode flag is zero, then this is done by calling -** sqlcipher_sqlite3PagerWrite() on page 1, then modifying the contents of the -** page data. In this case the file will be updated when the current -** transaction is committed. -** -** The isDirectMode flag may only be non-zero if the library was compiled -** with the SQLITE_ENABLE_ATOMIC_WRITE macro defined. In this case, -** if isDirect is non-zero, then the database file is updated directly -** by writing an updated version of page 1 using a call to the -** sqlcipher_sqlite3OsWrite() function. -*/ -static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ - int rc = SQLITE_OK; + pLive = (WalIndexHdr*)walIndexHdr(pWal); + if( memcmp(&pWal->hdr, (void *)pLive, sizeof(WalIndexHdr))!=0 ){ + iFirst = pLive->mxFrame+1; + } - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); + /* See if it is possible to write these frames into the start of the + ** log file, instead of appending to it at pWal->hdr.mxFrame. + */ + if( SQLITE_OK!=(rc = walRestartLog(pWal)) ){ + return rc; + } - /* Declare and initialize constant integer 'isDirect'. If the - ** atomic-write optimization is enabled in this build, then isDirect - ** is initialized to the value passed as the isDirectMode parameter - ** to this function. Otherwise, it is always set to zero. - ** - ** The idea is that if the atomic-write optimization is not - ** enabled at compile time, the compiler can omit the tests of - ** 'isDirect' below, as well as the block enclosed in the - ** "if( isDirect )" condition. + /* If this is the first frame written into the log, write the WAL + ** header to the start of the WAL file. See comments at the top of + ** this source file for a description of the WAL header format. */ -#ifndef SQLITE_ENABLE_ATOMIC_WRITE -# define DIRECT_MODE 0 - assert( isDirectMode==0 ); - UNUSED_PARAMETER(isDirectMode); -#else -# define DIRECT_MODE isDirectMode -#endif + iFrame = pWal->hdr.mxFrame; + if( iFrame==0 ){ + u8 aWalHdr[WAL_HDRSIZE]; /* Buffer to assemble wal-header in */ + u32 aCksum[2]; /* Checksum for wal-header */ - if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){ - PgHdr *pPgHdr; /* Reference to page 1 */ + sqlcipher_sqlite3Put4byte(&aWalHdr[0], (WAL_MAGIC | SQLITE_BIGENDIAN)); + sqlcipher_sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION); + sqlcipher_sqlite3Put4byte(&aWalHdr[8], szPage); + sqlcipher_sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt); + if( pWal->nCkpt==0 ) sqlcipher_sqlite3_randomness(8, pWal->hdr.aSalt); + memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8); + walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum); + sqlcipher_sqlite3Put4byte(&aWalHdr[24], aCksum[0]); + sqlcipher_sqlite3Put4byte(&aWalHdr[28], aCksum[1]); - assert( !pPager->tempFile && isOpen(pPager->fd) ); + pWal->szPage = szPage; + pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN; + pWal->hdr.aFrameCksum[0] = aCksum[0]; + pWal->hdr.aFrameCksum[1] = aCksum[1]; + pWal->truncateOnCommit = 1; - /* Open page 1 of the file for writing. */ - rc = sqlcipher_sqlite3PagerGet(pPager, 1, &pPgHdr, 0); - assert( pPgHdr==0 || rc==SQLITE_OK ); + rc = sqlcipher_sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0); + WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok")); + if( rc!=SQLITE_OK ){ + return rc; + } - /* If page one was fetched successfully, and this function is not - ** operating in direct-mode, make page 1 writable. When not in - ** direct mode, page 1 is always held in cache and hence the PagerGet() - ** above is always successful - hence the ALWAYS on rc==SQLITE_OK. + /* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless + ** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise + ** an out-of-order write following a WAL restart could result in + ** database corruption. See the ticket: + ** + ** https://sqlite.org/src/info/ff5be73dee */ - if( !DIRECT_MODE && ALWAYS(rc==SQLITE_OK) ){ - rc = sqlcipher_sqlite3PagerWrite(pPgHdr); + if( pWal->syncHeader ){ + rc = sqlcipher_sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); + if( rc ) return rc; } + } + assert( (int)pWal->szPage==szPage ); - if( rc==SQLITE_OK ){ - /* Actually do the update of the change counter */ - pager_write_changecounter(pPgHdr); + /* Setup information needed to write frames into the WAL */ + w.pWal = pWal; + w.pFd = pWal->pWalFd; + w.iSyncPoint = 0; + w.syncFlags = sync_flags; + w.szPage = szPage; + iOffset = walFrameOffset(iFrame+1, szPage); + szFrame = szPage + WAL_FRAME_HDRSIZE; - /* If running in direct mode, write the contents of page 1 to the file. */ - if( DIRECT_MODE ){ - const void *zBuf; - assert( pPager->dbFileSize>0 ); - CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM_BKPT, zBuf); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); - pPager->aStat[PAGER_STAT_WRITE]++; - } - if( rc==SQLITE_OK ){ - /* Update the pager's copy of the change-counter. Otherwise, the - ** next time a read transaction is opened the cache will be - ** flushed (as the change-counter values will not match). */ - const void *pCopy = (const void *)&((const char *)zBuf)[24]; - memcpy(&pPager->dbFileVers, pCopy, sizeof(pPager->dbFileVers)); - pPager->changeCountDone = 1; + /* Write all frames into the log file exactly once */ + for(p=pList; p; p=p->pDirty){ + int nDbSize; /* 0 normally. Positive == commit flag */ + + /* Check if this page has already been written into the wal file by + ** the current transaction. If so, overwrite the existing frame and + ** set Wal.writeLock to WAL_WRITELOCK_RECKSUM - indicating that + ** checksums must be recomputed when the transaction is committed. */ + if( iFirst && (p->pDirty || isCommit==0) ){ + u32 iWrite = 0; + VVA_ONLY(rc =) sqlcipher_sqlite3WalFindFrame(pWal, p->pgno, &iWrite); + assert( rc==SQLITE_OK || iWrite==0 ); + if( iWrite>=iFirst ){ + i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE; + void *pData; + if( pWal->iReCksum==0 || iWriteiReCksum ){ + pWal->iReCksum = iWrite; } - }else{ - pPager->changeCountDone = 1; +#if defined(SQLITE_HAS_CODEC) + if( (pData = sqlcipherPagerCodec(p))==0 ) return SQLITE_NOMEM; +#else + pData = p->pData; +#endif + rc = sqlcipher_sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOff); + if( rc ) return rc; + p->flags &= ~PGHDR_WAL_APPEND; + continue; } } - /* Release the page reference. */ - sqlcipher_sqlite3PagerUnref(pPgHdr); + iFrame++; + assert( iOffset==walFrameOffset(iFrame, szPage) ); + nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0; + rc = walWriteOneFrame(&w, p, nDbSize, iOffset); + if( rc ) return rc; + pLast = p; + iOffset += szFrame; + p->flags |= PGHDR_WAL_APPEND; } - return rc; -} -/* -** Sync the database file to disk. This is a no-op for in-memory databases -** or pages with the Pager.noSync flag set. -** -** If successful, or if called on a pager for which it is a no-op, this -** function returns SQLITE_OK. Otherwise, an IO error code is returned. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerSync(Pager *pPager, const char *zSuper){ - int rc = SQLITE_OK; - void *pArg = (void*)zSuper; - rc = sqlcipher_sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg); - if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; - if( rc==SQLITE_OK && !pPager->noSync ){ - assert( !MEMDB ); - rc = sqlcipher_sqlite3OsSync(pPager->fd, pPager->syncFlags); + /* Recalculate checksums within the wal file if required. */ + if( isCommit && pWal->iReCksum ){ + rc = walRewriteChecksums(pWal, iFrame); + if( rc ) return rc; + } + + /* If this is the end of a transaction, then we might need to pad + ** the transaction and/or sync the WAL file. + ** + ** Padding and syncing only occur if this set of frames complete a + ** transaction and if PRAGMA synchronous=FULL. If synchronous==NORMAL + ** or synchronous==OFF, then no padding or syncing are needed. + ** + ** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not + ** needed and only the sync is done. If padding is needed, then the + ** final frame is repeated (with its commit mark) until the next sector + ** boundary is crossed. Only the part of the WAL prior to the last + ** sector boundary is synced; the part of the last frame that extends + ** past the sector boundary is written after the sync. + */ + if( isCommit && WAL_SYNC_FLAGS(sync_flags)!=0 ){ + int bSync = 1; + if( pWal->padToSectorBoundary ){ + int sectorSize = sqlcipher_sqlite3SectorSize(pWal->pWalFd); + w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize; + bSync = (w.iSyncPoint==iOffset); + testcase( bSync ); + while( iOffsettruncateOnCommit && pWal->mxWalSize>=0 ){ + i64 sz = pWal->mxWalSize; + if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){ + sz = walFrameOffset(iFrame+nExtra+1, szPage); + } + walLimitSize(pWal, sz); + pWal->truncateOnCommit = 0; + } + + /* Append data to the wal-index. It is not necessary to lock the + ** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index + ** guarantees that there are no other writers, and no data that may + ** be in use by existing readers is being overwritten. + */ + iFrame = pWal->hdr.mxFrame; + for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){ + if( (p->flags & PGHDR_WAL_APPEND)==0 ) continue; + iFrame++; + rc = walIndexAppend(pWal, iFrame, p->pgno); + } + assert( pLast!=0 || nExtra==0 ); + while( rc==SQLITE_OK && nExtra>0 ){ + iFrame++; + nExtra--; + rc = walIndexAppend(pWal, iFrame, pLast->pgno); } - return rc; -} -/* -** This function may only be called while a write-transaction is active in -** rollback. If the connection is in WAL mode, this call is a no-op. -** Otherwise, if the connection does not already have an EXCLUSIVE lock on -** the database file, an attempt is made to obtain one. -** -** If the EXCLUSIVE lock is already held or the attempt to obtain it is -** successful, or the connection is in WAL mode, SQLITE_OK is returned. -** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is -** returned. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerExclusiveLock(Pager *pPager){ - int rc = pPager->errCode; - assert( assert_pager_state(pPager) ); if( rc==SQLITE_OK ){ - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - || pPager->eState==PAGER_WRITER_LOCKED - ); - assert( assert_pager_state(pPager) ); - if( 0==pagerUseWal(pPager) ){ - rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); + /* Update the private copy of the header. */ + pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); + testcase( szPage<=32768 ); + testcase( szPage>=65536 ); + pWal->hdr.mxFrame = iFrame; + if( isCommit ){ + pWal->hdr.iChange++; + pWal->hdr.nPage = nTruncate; + } + /* If this is a commit, update the wal-index header too. */ + if( isCommit ){ + walIndexWriteHdr(pWal); + pWal->iCallback = iFrame; } } + + WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok")); return rc; } /* -** Sync the database file for the pager pPager. zSuper points to the name -** of a super-journal file that should be written into the individual -** journal file. zSuper may be NULL, which is interpreted as no -** super-journal (a single database transaction). -** -** This routine ensures that: -** -** * The database file change-counter is updated, -** * the journal is synced (unless the atomic-write optimization is used), -** * all dirty pages are written to the database file, -** * the database file is truncated (if required), and -** * the database file synced. -** -** The only thing that remains to commit the transaction is to finalize -** (delete, truncate or zero the first part of) the journal file (or -** delete the super-journal file if specified). +** This routine is called to implement sqlcipher_sqlite3_wal_checkpoint() and +** related interfaces. ** -** Note that if zSuper==NULL, this does not overwrite a previous value -** passed to an sqlcipher_sqlite3PagerCommitPhaseOne() call. +** Obtain a CHECKPOINT lock and then backfill as much information as +** we can from WAL into the database. ** -** If the final parameter - noSync - is true, then the database file itself -** is not synced. The caller must call sqlcipher_sqlite3PagerSync() directly to -** sync the database file before calling CommitPhaseTwo() to delete the -** journal file in this case. +** If parameter xBusy is not NULL, it is a pointer to a busy-handler +** callback. In this case this function runs a blocking checkpoint. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerCommitPhaseOne( - Pager *pPager, /* Pager object */ - const char *zSuper, /* If not NULL, the super-journal name */ - int noSync /* True to omit the xSync on the db file */ +SQLITE_PRIVATE int sqlcipher_sqlite3WalCheckpoint( + Wal *pWal, /* Wal connection */ + sqlcipher_sqlite3 *db, /* Check this handle's interrupt flag */ + int eMode, /* PASSIVE, FULL, RESTART, or TRUNCATE */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ + int sync_flags, /* Flags to sync db file with (or 0) */ + int nBuf, /* Size of temporary buffer */ + u8 *zBuf, /* Temporary buffer to use */ + int *pnLog, /* OUT: Number of frames in WAL */ + int *pnCkpt /* OUT: Number of backfilled frames in WAL */ ){ - int rc = SQLITE_OK; /* Return code */ - - assert( pPager->eState==PAGER_WRITER_LOCKED - || pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - || pPager->eState==PAGER_ERROR - ); - assert( assert_pager_state(pPager) ); - - /* If a prior error occurred, report that error again. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - - /* Provide the ability to easily simulate an I/O error during testing */ - if( sqlcipher_sqlite3FaultSim(400) ) return SQLITE_IOERR; - - PAGERTRACE(("DATABASE SYNC: File=%s zSuper=%s nSize=%d\n", - pPager->zFilename, zSuper, pPager->dbSize)); - - /* If no database changes have been made, return early. */ - if( pPager->eStatetempFile ); - assert( isOpen(pPager->fd) || pPager->tempFile ); - if( 0==pagerFlushOnCommit(pPager, 1) ){ - /* If this is an in-memory db, or no pages have been written to, or this - ** function has already been called, it is mostly a no-op. However, any - ** backup in progress needs to be restarted. */ - sqlcipher_sqlite3BackupRestart(pPager->pBackup); - }else{ - PgHdr *pList; - if( pagerUseWal(pPager) ){ - PgHdr *pPageOne = 0; - pList = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache); - if( pList==0 ){ - /* Must have at least one page for the WAL commit flag. - ** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */ - rc = sqlcipher_sqlite3PagerGet(pPager, 1, &pPageOne, 0); - pList = pPageOne; - pList->pDirty = 0; - } - assert( rc==SQLITE_OK ); - if( ALWAYS(pList) ){ - rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1); - } - sqlcipher_sqlite3PagerUnref(pPageOne); - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3PcacheCleanAll(pPager->pPCache); - } - }else{ - /* The bBatch boolean is true if the batch-atomic-write commit method - ** should be used. No rollback journal is created if batch-atomic-write - ** is enabled. - */ -#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE - sqlcipher_sqlite3_file *fd = pPager->fd; - int bBatch = zSuper==0 /* An SQLITE_IOCAP_BATCH_ATOMIC commit */ - && (sqlcipher_sqlite3OsDeviceCharacteristics(fd) & SQLITE_IOCAP_BATCH_ATOMIC) - && !pPager->noSync - && sqlcipher_sqlite3JournalIsInMemory(pPager->jfd); -#else -# define bBatch 0 -#endif - -#ifdef SQLITE_ENABLE_ATOMIC_WRITE - /* The following block updates the change-counter. Exactly how it - ** does this depends on whether or not the atomic-update optimization - ** was enabled at compile time, and if this transaction meets the - ** runtime criteria to use the operation: - ** - ** * The file-system supports the atomic-write property for - ** blocks of size page-size, and - ** * This commit is not part of a multi-file transaction, and - ** * Exactly one page has been modified and store in the journal file. - ** - ** If the optimization was not enabled at compile time, then the - ** pager_incr_changecounter() function is called to update the change - ** counter in 'indirect-mode'. If the optimization is compiled in but - ** is not applicable to this transaction, call sqlcipher_sqlite3JournalCreate() - ** to make sure the journal file has actually been created, then call - ** pager_incr_changecounter() to update the change-counter in indirect - ** mode. - ** - ** Otherwise, if the optimization is both enabled and applicable, - ** then call pager_incr_changecounter() to update the change-counter - ** in 'direct' mode. In this case the journal file will never be - ** created for this transaction. - */ - if( bBatch==0 ){ - PgHdr *pPg; - assert( isOpen(pPager->jfd) - || pPager->journalMode==PAGER_JOURNALMODE_OFF - || pPager->journalMode==PAGER_JOURNALMODE_WAL - ); - if( !zSuper && isOpen(pPager->jfd) - && pPager->journalOff==jrnlBufferSize(pPager) - && pPager->dbSize>=pPager->dbOrigSize - && (!(pPg = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty) - ){ - /* Update the db file change counter via the direct-write method. The - ** following call will modify the in-memory representation of page 1 - ** to include the updated change counter and then write page 1 - ** directly to the database file. Because of the atomic-write - ** property of the host file-system, this is safe. - */ - rc = pager_incr_changecounter(pPager, 1); - }else{ - rc = sqlcipher_sqlite3JournalCreate(pPager->jfd); - if( rc==SQLITE_OK ){ - rc = pager_incr_changecounter(pPager, 0); - } - } - } -#else /* SQLITE_ENABLE_ATOMIC_WRITE */ -#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE - if( zSuper ){ - rc = sqlcipher_sqlite3JournalCreate(pPager->jfd); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - assert( bBatch==0 ); - } -#endif - rc = pager_incr_changecounter(pPager, 0); -#endif /* !SQLITE_ENABLE_ATOMIC_WRITE */ - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - - /* Write the super-journal name into the journal file. If a - ** super-journal file name has already been written to the journal file, - ** or if zSuper is NULL (no super-journal), then this call is a no-op. - */ - rc = writeSuperJournal(pPager, zSuper); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - - /* Sync the journal file and write all dirty pages to the database. - ** If the atomic-update optimization is being used, this sync will not - ** create the journal file or perform any real IO. - ** - ** Because the change-counter page was just modified, unless the - ** atomic-update optimization is used it is almost certain that the - ** journal requires a sync here. However, in locking_mode=exclusive - ** on a system under memory pressure it is just possible that this is - ** not the case. In this case it is likely enough that the redundant - ** xSync() call will be changed to a no-op by the OS anyhow. - */ - rc = syncJournal(pPager, 0); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; + int rc; /* Return code */ + int isChanged = 0; /* True if a new wal-index header is loaded */ + int eMode2 = eMode; /* Mode to pass to walCheckpoint() */ + int (*xBusy2)(void*) = xBusy; /* Busy handler for eMode2 */ - pList = sqlcipher_sqlite3PcacheDirtyList(pPager->pPCache); -#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE - if( bBatch ){ - rc = sqlcipher_sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0); - if( rc==SQLITE_OK ){ - rc = pager_write_pagelist(pPager, pList); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0); - } - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3OsFileControlHint(fd, SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, 0); - } - } + assert( pWal->ckptLock==0 ); + assert( pWal->writeLock==0 ); - if( (rc&0xFF)==SQLITE_IOERR && rc!=SQLITE_IOERR_NOMEM ){ - rc = sqlcipher_sqlite3JournalCreate(pPager->jfd); - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3OsClose(pPager->jfd); - goto commit_phase_one_exit; - } - bBatch = 0; - }else{ - sqlcipher_sqlite3OsClose(pPager->jfd); - } - } -#endif /* SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ + /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked + ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ + assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); - if( bBatch==0 ){ - rc = pager_write_pagelist(pPager, pList); - } - if( rc!=SQLITE_OK ){ - assert( rc!=SQLITE_IOERR_BLOCKED ); - goto commit_phase_one_exit; - } - sqlcipher_sqlite3PcacheCleanAll(pPager->pPCache); + if( pWal->readOnly ) return SQLITE_READONLY; + WALTRACE(("WAL%p: checkpoint begins\n", pWal)); - /* If the file on disk is smaller than the database image, use - ** pager_truncate to grow the file here. This can happen if the database - ** image was extended as part of the current transaction and then the - ** last page in the db image moved to the free-list. In this case the - ** last page is never written out to disk, leaving the database file - ** undersized. Fix this now if it is the case. */ - if( pPager->dbSize>pPager->dbFileSize ){ - Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); - assert( pPager->eState==PAGER_WRITER_DBMOD ); - rc = pager_truncate(pPager, nNew); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - } + /* Enable blocking locks, if possible. If blocking locks are successfully + ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */ + sqlcipher_sqlite3WalDb(pWal, db); + (void)walEnableBlocking(pWal); - /* Finally, sync the database file. */ - if( !noSync ){ - rc = sqlcipher_sqlite3PagerSync(pPager, zSuper); + /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive + ** "checkpoint" lock on the database file. + ** EVIDENCE-OF: R-10421-19736 If any other process is running a + ** checkpoint operation at the same time, the lock cannot be obtained and + ** SQLITE_BUSY is returned. + ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured, + ** it will not be invoked in this case. + */ + rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); + testcase( rc==SQLITE_BUSY ); + testcase( rc!=SQLITE_OK && xBusy2!=0 ); + if( rc==SQLITE_OK ){ + pWal->ckptLock = 1; + + /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and + ** TRUNCATE modes also obtain the exclusive "writer" lock on the database + ** file. + ** + ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained + ** immediately, and a busy-handler is configured, it is invoked and the + ** writer lock retried until either the busy-handler returns 0 or the + ** lock is successfully obtained. + */ + if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ + rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1); + if( rc==SQLITE_OK ){ + pWal->writeLock = 1; + }else if( rc==SQLITE_BUSY ){ + eMode2 = SQLITE_CHECKPOINT_PASSIVE; + xBusy2 = 0; + rc = SQLITE_OK; } - IOTRACE(("DBSYNC %p\n", pPager)) } } -commit_phase_one_exit: - if( rc==SQLITE_OK && !pagerUseWal(pPager) ){ - pPager->eState = PAGER_WRITER_FINISHED; + + /* Read the wal-index header. */ + if( rc==SQLITE_OK ){ + walDisableBlocking(pWal); + rc = walIndexReadHdr(pWal, &isChanged); + (void)walEnableBlocking(pWal); + if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ + sqlcipher_sqlite3OsUnfetch(pWal->pDbFd, 0, 0); + } } - return rc; -} + /* Copy data from the log to the database file. */ + if( rc==SQLITE_OK ){ -/* -** When this function is called, the database file has been completely -** updated to reflect the changes made by the current transaction and -** synced to disk. The journal file still exists in the file-system -** though, and if a failure occurs at this point it will eventually -** be used as a hot-journal and the current transaction rolled back. -** -** This function finalizes the journal file, either by deleting, -** truncating or partially zeroing it, so that it cannot be used -** for hot-journal rollback. Once this is done the transaction is -** irrevocably committed. -** -** If an error occurs, an IO error code is returned and the pager -** moves into the error state. Otherwise, SQLITE_OK is returned. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerCommitPhaseTwo(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ + if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf); + } - /* This routine should not be called if a prior error has occurred. - ** But if (due to a coding error elsewhere in the system) it does get - ** called, just return the same error code without doing anything. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - pPager->iDataVersion++; + /* If no error occurred, set the output variables. */ + if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ + if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; + if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); + } + } - assert( pPager->eState==PAGER_WRITER_LOCKED - || pPager->eState==PAGER_WRITER_FINISHED - || (pagerUseWal(pPager) && pPager->eState==PAGER_WRITER_CACHEMOD) - ); - assert( assert_pager_state(pPager) ); + if( isChanged ){ + /* If a new wal-index header was loaded before the checkpoint was + ** performed, then the pager-cache associated with pWal is now + ** out of date. So zero the cached wal-index header to ensure that + ** next time the pager opens a snapshot on this database it knows that + ** the cache needs to be reset. + */ + memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); + } - /* An optimization. If the database was not actually modified during - ** this transaction, the pager is running in exclusive-mode and is - ** using persistent journals, then this function is a no-op. - ** - ** The start of the journal file currently contains a single journal - ** header with the nRec field set to 0. If such a journal is used as - ** a hot-journal during hot-journal rollback, 0 changes will be made - ** to the database file. So there is no need to zero the journal - ** header. Since the pager is in exclusive mode, there is no need - ** to drop any locks either. - */ - if( pPager->eState==PAGER_WRITER_LOCKED - && pPager->exclusiveMode - && pPager->journalMode==PAGER_JOURNALMODE_PERSIST - ){ - assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff ); - pPager->eState = PAGER_READER; - return SQLITE_OK; + walDisableBlocking(pWal); + sqlcipher_sqlite3WalDb(pWal, 0); + + /* Release the locks. */ + sqlcipher_sqlite3WalEndWriteTransaction(pWal); + if( pWal->ckptLock ){ + walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); + pWal->ckptLock = 0; } + WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; +#endif + return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc); +} - PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); - rc = pager_end_transaction(pPager, pPager->setSuper, 1); - return pager_error(pPager, rc); +/* Return the value to pass to a sqlcipher_sqlite3_wal_hook callback, the +** number of frames in the WAL at the point of the last commit since +** sqlcipher_sqlite3WalCallback() was called. If no commits have occurred since +** the last call, then return 0. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3WalCallback(Wal *pWal){ + u32 ret = 0; + if( pWal ){ + ret = pWal->iCallback; + pWal->iCallback = 0; + } + return (int)ret; } /* -** If a write transaction is open, then all changes made within the -** transaction are reverted and the current write-transaction is closed. -** The pager falls back to PAGER_READER state if successful, or PAGER_ERROR -** state if an error occurs. -** -** If the pager is already in PAGER_ERROR state when this function is called, -** it returns Pager.errCode immediately. No work is performed in this case. -** -** Otherwise, in rollback mode, this function performs two functions: -** -** 1) It rolls back the journal file, restoring all database file and -** in-memory cache pages to the state they were in when the transaction -** was opened, and +** This function is called to change the WAL subsystem into or out +** of locking_mode=EXCLUSIVE. ** -** 2) It finalizes the journal file, so that it is not used for hot -** rollback at any point in the future. +** If op is zero, then attempt to change from locking_mode=EXCLUSIVE +** into locking_mode=NORMAL. This means that we must acquire a lock +** on the pWal->readLock byte. If the WAL is already in locking_mode=NORMAL +** or if the acquisition of the lock fails, then return 0. If the +** transition out of exclusive-mode is successful, return 1. This +** operation must occur while the pager is still holding the exclusive +** lock on the main database file. ** -** Finalization of the journal file (task 2) is only performed if the -** rollback is successful. +** If op is one, then change from locking_mode=NORMAL into +** locking_mode=EXCLUSIVE. This means that the pWal->readLock must +** be released. Return 1 if the transition is made and 0 if the +** WAL is already in exclusive-locking mode - meaning that this +** routine is a no-op. The pager must already hold the exclusive lock +** on the main database file before invoking this operation. ** -** In WAL mode, all cache-entries containing data modified within the -** current transaction are either expelled from the cache or reverted to -** their pre-transaction state by re-reading data from the database or -** WAL files. The WAL transaction is then closed. +** If op is negative, then do a dry-run of the op==1 case but do +** not actually change anything. The pager uses this to see if it +** should acquire the database exclusive lock prior to invoking +** the op==1 case. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerRollback(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager))); +SQLITE_PRIVATE int sqlcipher_sqlite3WalExclusiveMode(Wal *pWal, int op){ + int rc; + assert( pWal->writeLock==0 ); + assert( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE || op==-1 ); - /* PagerRollback() is a no-op if called in READER or OPEN state. If - ** the pager is already in the ERROR state, the rollback is not - ** attempted here. Instead, the error code is returned to the caller. + /* pWal->readLock is usually set, but might be -1 if there was a + ** prior error while attempting to acquire are read-lock. This cannot + ** happen if the connection is actually in exclusive mode (as no xShmLock + ** locks are taken in this case). Nor should the pager attempt to + ** upgrade to exclusive-mode following such an error. */ - assert( assert_pager_state(pPager) ); - if( pPager->eState==PAGER_ERROR ) return pPager->errCode; - if( pPager->eState<=PAGER_READER ) return SQLITE_OK; + assert( pWal->readLock>=0 || pWal->lockError ); + assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) ); - if( pagerUseWal(pPager) ){ - int rc2; - rc = sqlcipher_sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1); - rc2 = pager_end_transaction(pPager, pPager->setSuper, 0); - if( rc==SQLITE_OK ) rc = rc2; - }else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){ - int eState = pPager->eState; - rc = pager_end_transaction(pPager, 0, 0); - if( !MEMDB && eState>PAGER_WRITER_LOCKED ){ - /* This can happen using journal_mode=off. Move the pager to the error - ** state to indicate that the contents of the cache may not be trusted. - ** Any active readers will get SQLITE_ABORT. - */ - pPager->errCode = SQLITE_ABORT; - pPager->eState = PAGER_ERROR; - setGetterMethod(pPager); - return rc; + if( op==0 ){ + if( pWal->exclusiveMode!=WAL_NORMAL_MODE ){ + pWal->exclusiveMode = WAL_NORMAL_MODE; + if( walLockShared(pWal, WAL_READ_LOCK(pWal->readLock))!=SQLITE_OK ){ + pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; + } + rc = pWal->exclusiveMode==WAL_NORMAL_MODE; + }else{ + /* Already in locking_mode=NORMAL */ + rc = 0; } + }else if( op>0 ){ + assert( pWal->exclusiveMode==WAL_NORMAL_MODE ); + assert( pWal->readLock>=0 ); + walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); + pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; + rc = 1; }else{ - rc = pager_playback(pPager, 0); + rc = pWal->exclusiveMode==WAL_NORMAL_MODE; } + return rc; +} - assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK ); - assert( rc==SQLITE_OK || rc==SQLITE_FULL || rc==SQLITE_CORRUPT - || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR - || rc==SQLITE_CANTOPEN - ); +/* +** Return true if the argument is non-NULL and the WAL module is using +** heap-memory for the wal-index. Otherwise, if the argument is NULL or the +** WAL module is using shared-memory, return false. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3WalHeapMemory(Wal *pWal){ + return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ); +} - /* If an error occurs during a ROLLBACK, we can no longer trust the pager - ** cache. So call pager_error() on the way out to make any error persistent. - */ - return pager_error(pPager, rc); +#ifdef SQLITE_ENABLE_SNAPSHOT +/* Create a snapshot object. The content of a snapshot is opaque to +** every other subsystem, so the WAL module can put whatever it needs +** in the object. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotGet(Wal *pWal, sqlcipher_sqlite3_snapshot **ppSnapshot){ + int rc = SQLITE_OK; + WalIndexHdr *pRet; + static const u32 aZero[4] = { 0, 0, 0, 0 }; + + assert( pWal->readLock>=0 && pWal->writeLock==0 ); + + if( memcmp(&pWal->hdr.aFrameCksum[0],aZero,16)==0 ){ + *ppSnapshot = 0; + return SQLITE_ERROR; + } + pRet = (WalIndexHdr*)sqlcipher_sqlite3_malloc(sizeof(WalIndexHdr)); + if( pRet==0 ){ + rc = SQLITE_NOMEM_BKPT; + }else{ + memcpy(pRet, &pWal->hdr, sizeof(WalIndexHdr)); + *ppSnapshot = (sqlcipher_sqlite3_snapshot*)pRet; + } + + return rc; } -/* -** Return TRUE if the database file is opened read-only. Return FALSE -** if the database is (in theory) writable. +/* Try to open on pSnapshot when the next read-transaction starts */ -SQLITE_PRIVATE u8 sqlcipher_sqlite3PagerIsreadonly(Pager *pPager){ - return pPager->readOnly; +SQLITE_PRIVATE void sqlcipher_sqlite3WalSnapshotOpen( + Wal *pWal, + sqlcipher_sqlite3_snapshot *pSnapshot +){ + pWal->pSnapshot = (WalIndexHdr*)pSnapshot; } -#ifdef SQLITE_DEBUG /* -** Return the sum of the reference counts for all pages held by pPager. +** Return a +ve value if snapshot p1 is newer than p2. A -ve value if +** p1 is older than p2 and zero if p1 and p2 are the same snapshot. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerRefcount(Pager *pPager){ - return sqlcipher_sqlite3PcacheRefCount(pPager->pPCache); +SQLITE_API int sqlcipher_sqlite3_snapshot_cmp(sqlcipher_sqlite3_snapshot *p1, sqlcipher_sqlite3_snapshot *p2){ + WalIndexHdr *pHdr1 = (WalIndexHdr*)p1; + WalIndexHdr *pHdr2 = (WalIndexHdr*)p2; + + /* aSalt[0] is a copy of the value stored in the wal file header. It + ** is incremented each time the wal file is restarted. */ + if( pHdr1->aSalt[0]aSalt[0] ) return -1; + if( pHdr1->aSalt[0]>pHdr2->aSalt[0] ) return +1; + if( pHdr1->mxFramemxFrame ) return -1; + if( pHdr1->mxFrame>pHdr2->mxFrame ) return +1; + return 0; } -#endif /* -** Return the approximate number of bytes of memory currently -** used by the pager and its associated cache. +** The caller currently has a read transaction open on the database. +** This function takes a SHARED lock on the CHECKPOINTER slot and then +** checks if the snapshot passed as the second argument is still +** available. If so, SQLITE_OK is returned. +** +** If the snapshot is not available, SQLITE_ERROR is returned. Or, if +** the CHECKPOINTER lock cannot be obtained, SQLITE_BUSY. If any error +** occurs (any value other than SQLITE_OK is returned), the CHECKPOINTER +** lock is released before returning. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerMemUsed(Pager *pPager){ - int perPageSize = pPager->pageSize + pPager->nExtra + sizeof(PgHdr) - + 5*sizeof(void*); - return perPageSize*sqlcipher_sqlite3PcachePagecount(pPager->pPCache) - + sqlcipher_sqlite3MallocSize(pPager) - + pPager->pageSize; +SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotCheck(Wal *pWal, sqlcipher_sqlite3_snapshot *pSnapshot){ + int rc; + rc = walLockShared(pWal, WAL_CKPT_LOCK); + if( rc==SQLITE_OK ){ + WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot; + if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) + || pNew->mxFramenBackfillAttempted + ){ + rc = SQLITE_ERROR_SNAPSHOT; + walUnlockShared(pWal, WAL_CKPT_LOCK); + } + } + return rc; } /* -** Return the number of references to the specified page. +** Release a lock obtained by an earlier successful call to +** sqlcipher_sqlite3WalSnapshotCheck(). */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerPageRefcount(DbPage *pPage){ - return sqlcipher_sqlite3PcachePageRefcount(pPage); +SQLITE_PRIVATE void sqlcipher_sqlite3WalSnapshotUnlock(Wal *pWal){ + assert( pWal ); + walUnlockShared(pWal, WAL_CKPT_LOCK); } -#ifdef SQLITE_TEST + +#endif /* SQLITE_ENABLE_SNAPSHOT */ + +#ifdef SQLITE_ENABLE_ZIPVFS /* -** This routine is used for testing and analysis only. +** If the argument is not NULL, it points to a Wal object that holds a +** read-lock. This function returns the database page-size if it is known, +** or zero if it is not (or if pWal is NULL). */ -SQLITE_PRIVATE int *sqlcipher_sqlite3PagerStats(Pager *pPager){ - static int a[11]; - a[0] = sqlcipher_sqlite3PcacheRefCount(pPager->pPCache); - a[1] = sqlcipher_sqlite3PcachePagecount(pPager->pPCache); - a[2] = sqlcipher_sqlite3PcacheGetCachesize(pPager->pPCache); - a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; - a[4] = pPager->eState; - a[5] = pPager->errCode; - a[6] = pPager->aStat[PAGER_STAT_HIT]; - a[7] = pPager->aStat[PAGER_STAT_MISS]; - a[8] = 0; /* Used to be pPager->nOvfl */ - a[9] = pPager->nRead; - a[10] = pPager->aStat[PAGER_STAT_WRITE]; - return a; +SQLITE_PRIVATE int sqlcipher_sqlite3WalFramesize(Wal *pWal){ + assert( pWal==0 || pWal->readLock>=0 ); + return (pWal ? pWal->szPage : 0); } #endif +/* Return the sqlcipher_sqlite3_file object for the WAL file +*/ +SQLITE_PRIVATE sqlcipher_sqlite3_file *sqlcipher_sqlite3WalFile(Wal *pWal){ + return pWal->pWalFd; +} + +#endif /* #ifndef SQLITE_OMIT_WAL */ + +/************** End of wal.c *************************************************/ +/************** Begin file btmutex.c *****************************************/ /* -** Parameter eStat must be one of SQLITE_DBSTATUS_CACHE_HIT, _MISS, _WRITE, -** or _WRITE+1. The SQLITE_DBSTATUS_CACHE_WRITE+1 case is a translation -** of SQLITE_DBSTATUS_CACHE_SPILL. The _SPILL case is not contiguous because -** it was added later. +** 2007 August 27 ** -** Before returning, *pnVal is incremented by the -** current cache hit or miss count, according to the value of eStat. If the -** reset parameter is non-zero, the cache hit or miss count is zeroed before -** returning. +** 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 used to implement mutexes on Btree objects. +** This code really belongs in btree.c. But btree.c is getting too +** big and we want to break it down some. This packaged seemed like +** a good breakout. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){ +/************** Include btreeInt.h in the middle of btmutex.c ****************/ +/************** Begin file btreeInt.h ****************************************/ +/* +** 2004 April 6 +** +** 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 external (disk-based) database using BTrees. +** For a detailed discussion of BTrees, refer to +** +** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: +** "Sorting And Searching", pages 473-480. Addison-Wesley +** Publishing Company, Reading, Massachusetts. +** +** The basic idea is that each page of the file contains N database +** entries and N+1 pointers to subpages. +** +** ---------------------------------------------------------------- +** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) | +** ---------------------------------------------------------------- +** +** All of the keys on the page that Ptr(0) points to have values less +** than Key(0). All of the keys on page Ptr(1) and its subpages have +** values greater than Key(0) and less than Key(1). All of the keys +** on Ptr(N) and its subpages have values greater than Key(N-1). And +** so forth. +** +** Finding a particular key requires reading O(log(M)) pages from the +** disk where M is the number of entries in the tree. +** +** In this implementation, a single file can hold one or more separate +** BTrees. Each BTree is identified by the index of its root page. The +** key and data for any entry are combined to form the "payload". A +** fixed amount of payload can be carried directly on the database +** page. If the payload is larger than the preset amount then surplus +** bytes are stored on overflow pages. The payload for an entry +** and the preceding pointer are combined to form a "Cell". Each +** page has a small header which contains the Ptr(N) pointer and other +** information such as the size of key and data. +** +** FORMAT DETAILS +** +** The file is divided into pages. The first page is called page 1, +** the second is page 2, and so forth. A page number of zero indicates +** "no such page". The page size can be any power of 2 between 512 and 65536. +** Each page can be either a btree page, a freelist page, an overflow +** page, or a pointer-map page. +** +** The first page is always a btree page. The first 100 bytes of the first +** page contain a special header (the "file header") that describes the file. +** The format of the file header is as follows: +** +** OFFSET SIZE DESCRIPTION +** 0 16 Header string: "SQLite format 3\000" +** 16 2 Page size in bytes. (1 means 65536) +** 18 1 File format write version +** 19 1 File format read version +** 20 1 Bytes of unused space at the end of each page +** 21 1 Max embedded payload fraction (must be 64) +** 22 1 Min embedded payload fraction (must be 32) +** 23 1 Min leaf payload fraction (must be 32) +** 24 4 File change counter +** 28 4 Reserved for future use +** 32 4 First freelist page +** 36 4 Number of freelist pages in the file +** 40 60 15 4-byte meta values passed to higher layers +** +** 40 4 Schema cookie +** 44 4 File format of schema layer +** 48 4 Size of page cache +** 52 4 Largest root-page (auto/incr_vacuum) +** 56 4 1=UTF-8 2=UTF16le 3=UTF16be +** 60 4 User version +** 64 4 Incremental vacuum mode +** 68 4 Application-ID +** 72 20 unused +** 92 4 The version-valid-for number +** 96 4 SQLITE_VERSION_NUMBER +** +** All of the integer values are big-endian (most significant byte first). +** +** The file change counter is incremented when the database is changed +** This counter allows other processes to know when the file has changed +** and thus when they need to flush their cache. +** +** The max embedded payload fraction is the amount of the total usable +** space in a page that can be consumed by a single cell for standard +** B-tree (non-LEAFDATA) tables. A value of 255 means 100%. The default +** is to limit the maximum cell size so that at least 4 cells will fit +** on one page. Thus the default max embedded payload fraction is 64. +** +** If the payload for a cell is larger than the max payload, then extra +** payload is spilled to overflow pages. Once an overflow page is allocated, +** as many bytes as possible are moved into the overflow pages without letting +** the cell size drop below the min embedded payload fraction. +** +** The min leaf payload fraction is like the min embedded payload fraction +** except that it applies to leaf nodes in a LEAFDATA tree. The maximum +** payload fraction for a LEAFDATA tree is always 100% (or 255) and it +** not specified in the header. +** +** Each btree pages is divided into three sections: The header, the +** cell pointer array, and the cell content area. Page 1 also has a 100-byte +** file header that occurs before the page header. +** +** |----------------| +** | file header | 100 bytes. Page 1 only. +** |----------------| +** | page header | 8 bytes for leaves. 12 bytes for interior nodes +** |----------------| +** | cell pointer | | 2 bytes per cell. Sorted order. +** | array | | Grows downward +** | | v +** |----------------| +** | unallocated | +** | space | +** |----------------| ^ Grows upwards +** | cell content | | Arbitrary order interspersed with freeblocks. +** | area | | and free space fragments. +** |----------------| +** +** The page headers looks like this: +** +** OFFSET SIZE DESCRIPTION +** 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf +** 1 2 byte offset to the first freeblock +** 3 2 number of cells on this page +** 5 2 first byte of the cell content area +** 7 1 number of fragmented free bytes +** 8 4 Right child (the Ptr(N) value). Omitted on leaves. +** +** The flags define the format of this btree page. The leaf flag means that +** this page has no children. The zerodata flag means that this page carries +** only keys and no data. The intkey flag means that the key is an integer +** which is stored in the key size entry of the cell header rather than in +** the payload area. +** +** The cell pointer array begins on the first byte after the page header. +** The cell pointer array contains zero or more 2-byte numbers which are +** offsets from the beginning of the page to the cell content in the cell +** content area. The cell pointers occur in sorted order. The system strives +** to keep free space after the last cell pointer so that new cells can +** be easily added without having to defragment the page. +** +** Cell content is stored at the very end of the page and grows toward the +** beginning of the page. +** +** Unused space within the cell content area is collected into a linked list of +** freeblocks. Each freeblock is at least 4 bytes in size. The byte offset +** to the first freeblock is given in the header. Freeblocks occur in +** increasing order. Because a freeblock must be at least 4 bytes in size, +** any group of 3 or fewer unused bytes in the cell content area cannot +** exist on the freeblock chain. A group of 3 or fewer free bytes is called +** a fragment. The total number of bytes in all fragments is recorded. +** in the page header at offset 7. +** +** SIZE DESCRIPTION +** 2 Byte offset of the next freeblock +** 2 Bytes in this freeblock +** +** Cells are of variable length. Cells are stored in the cell content area at +** the end of the page. Pointers to the cells are in the cell pointer array +** that immediately follows the page header. Cells is not necessarily +** contiguous or in order, but cell pointers are contiguous and in order. +** +** Cell content makes use of variable length integers. A variable +** length integer is 1 to 9 bytes where the lower 7 bits of each +** byte are used. The integer consists of all bytes that have bit 8 set and +** the first byte with bit 8 clear. The most significant byte of the integer +** appears first. A variable-length integer may not be more than 9 bytes long. +** As a special case, all 8 bytes of the 9th byte are used as data. This +** allows a 64-bit integer to be encoded in 9 bytes. +** +** 0x00 becomes 0x00000000 +** 0x7f becomes 0x0000007f +** 0x81 0x00 becomes 0x00000080 +** 0x82 0x00 becomes 0x00000100 +** 0x80 0x7f becomes 0x0000007f +** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678 +** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081 +** +** Variable length integers are used for rowids and to hold the number of +** bytes of key and data in a btree cell. +** +** The content of a cell looks like this: +** +** SIZE DESCRIPTION +** 4 Page number of the left child. Omitted if leaf flag is set. +** var Number of bytes of data. Omitted if the zerodata flag is set. +** var Number of bytes of key. Or the key itself if intkey flag is set. +** * Payload +** 4 First page of the overflow chain. Omitted if no overflow +** +** Overflow pages form a linked list. Each page except the last is completely +** filled with data (pagesize - 4 bytes). The last page can have as little +** as 1 byte of data. +** +** SIZE DESCRIPTION +** 4 Page number of next overflow page +** * Data +** +** Freelist pages come in two subtypes: trunk pages and leaf pages. The +** file header points to the first in a linked list of trunk page. Each trunk +** page points to multiple leaf pages. The content of a leaf page is +** unspecified. A trunk page looks like this: +** +** SIZE DESCRIPTION +** 4 Page number of next trunk page +** 4 Number of leaf pointers on this page +** * zero or more pages numbers of leaves +*/ +/* #include "sqliteInt.h" */ - assert( eStat==SQLITE_DBSTATUS_CACHE_HIT - || eStat==SQLITE_DBSTATUS_CACHE_MISS - || eStat==SQLITE_DBSTATUS_CACHE_WRITE - || eStat==SQLITE_DBSTATUS_CACHE_WRITE+1 - ); - assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS ); - assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE ); - assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 - && PAGER_STAT_WRITE==2 && PAGER_STAT_SPILL==3 ); +/* The following value is the maximum cell size assuming a maximum page +** size give above. +*/ +#define MX_CELL_SIZE(pBt) ((int)(pBt->pageSize-8)) - eStat -= SQLITE_DBSTATUS_CACHE_HIT; - *pnVal += pPager->aStat[eStat]; - if( reset ){ - pPager->aStat[eStat] = 0; - } -} +/* The maximum number of cells on a single page of the database. This +** assumes a minimum cell size of 6 bytes (4 bytes for the cell itself +** plus 2 bytes for the index to the cell in the page header). Such +** small cells will be rare, but they are possible. +*/ +#define MX_CELL(pBt) ((pBt->pageSize-8)/6) + +/* Forward declarations */ +typedef struct MemPage MemPage; +typedef struct BtLock BtLock; +typedef struct CellInfo CellInfo; /* -** Return true if this is an in-memory or temp-file backed pager. +** This is a magic string that appears at the beginning of every +** SQLite database in order to identify the file as a real database. +** +** You can change this value at compile-time by specifying a +** -DSQLITE_FILE_HEADER="..." on the compiler command-line. The +** header must be exactly 16 bytes including the zero-terminator so +** the string itself should be 15 characters long. If you change +** the header, then your custom library will not be able to read +** databases generated by the standard tools and the standard tools +** will not be able to read databases created by your custom library. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerIsMemdb(Pager *pPager){ - return pPager->tempFile; -} +#ifndef SQLITE_FILE_HEADER /* 123456789 123456 */ +# define SQLITE_FILE_HEADER "SQLite format 3" +#endif /* -** Check that there are at least nSavepoint savepoints open. If there are -** currently less than nSavepoints open, then open one or more savepoints -** to make up the difference. If the number of savepoints is already -** equal to nSavepoint, then this function is a no-op. +** Page type flags. An ORed combination of these flags appear as the +** first byte of on-disk image of every BTree page. +*/ +#define PTF_INTKEY 0x01 +#define PTF_ZERODATA 0x02 +#define PTF_LEAFDATA 0x04 +#define PTF_LEAF 0x08 + +/* +** An instance of this object stores information about each a single database +** page that has been loaded into memory. The information in this object +** is derived from the raw on-disk page content. ** -** If a memory allocation fails, SQLITE_NOMEM is returned. If an error -** occurs while opening the sub-journal file, then an IO error code is -** returned. Otherwise, SQLITE_OK. +** As each database page is loaded into memory, the pager allocats an +** instance of this object and zeros the first 8 bytes. (This is the +** "extra" information associated with each page of the pager.) +** +** Access to all fields of this structure is controlled by the mutex +** stored in MemPage.pBt->mutex. */ -static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){ - int rc = SQLITE_OK; /* Return code */ - int nCurrent = pPager->nSavepoint; /* Current number of savepoints */ - int ii; /* Iterator variable */ - PagerSavepoint *aNew; /* New Pager.aSavepoint array */ +struct MemPage { + u8 isInit; /* True if previously initialized. MUST BE FIRST! */ + u8 intKey; /* True if table b-trees. False for index b-trees */ + u8 intKeyLeaf; /* True if the leaf of an intKey table */ + Pgno pgno; /* Page number for this page */ + /* Only the first 8 bytes (above) are zeroed by pager.c when a new page + ** is allocated. All fields that follow must be initialized before use */ + u8 leaf; /* True if a leaf page */ + u8 hdrOffset; /* 100 for page 1. 0 otherwise */ + u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ + u8 max1bytePayload; /* min(maxLocal,127) */ + u8 nOverflow; /* Number of overflow cell bodies in aCell[] */ + u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */ + u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */ + u16 cellOffset; /* Index in aData of first cell pointer */ + int nFree; /* Number of free bytes on the page. -1 for unknown */ + u16 nCell; /* Number of cells on this page, local and ovfl */ + u16 maskPage; /* Mask for page offset */ + u16 aiOvfl[4]; /* Insert the i-th overflow cell before the aiOvfl-th + ** non-overflow cell */ + u8 *apOvfl[4]; /* Pointers to the body of overflow cells */ + BtShared *pBt; /* Pointer to BtShared that this page is part of */ + u8 *aData; /* Pointer to disk image of the page data */ + u8 *aDataEnd; /* One byte past the end of the entire page - not just + ** the usable space, the entire page. Used to prevent + ** corruption-induced buffer overflow. */ + u8 *aCellIdx; /* The cell index area */ + u8 *aDataOfst; /* Same as aData for leaves. aData+4 for interior */ + DbPage *pDbPage; /* Pager page handle */ + u16 (*xCellSize)(MemPage*,u8*); /* cellSizePtr method */ + void (*xParseCell)(MemPage*,u8*,CellInfo*); /* btreeParseCell method */ +}; - assert( pPager->eState>=PAGER_WRITER_LOCKED ); - assert( assert_pager_state(pPager) ); - assert( nSavepoint>nCurrent && pPager->useJournal ); +/* +** A linked list of the following structures is stored at BtShared.pLock. +** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor +** is opened on the table with root page BtShared.iTable. Locks are removed +** from this list when a transaction is committed or rolled back, or when +** a btree handle is closed. +*/ +struct BtLock { + Btree *pBtree; /* Btree handle holding this lock */ + Pgno iTable; /* Root page of table */ + u8 eLock; /* READ_LOCK or WRITE_LOCK */ + BtLock *pNext; /* Next in BtShared.pLock list */ +}; - /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM - ** if the allocation fails. Otherwise, zero the new portion in case a - ** malloc failure occurs while populating it in the for(...) loop below. - */ - aNew = (PagerSavepoint *)sqlcipher_sqlite3Realloc( - pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint - ); - if( !aNew ){ - return SQLITE_NOMEM_BKPT; - } - memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint)); - pPager->aSavepoint = aNew; +/* Candidate values for BtLock.eLock */ +#define READ_LOCK 1 +#define WRITE_LOCK 2 - /* Populate the PagerSavepoint structures just allocated. */ - for(ii=nCurrent; iidbSize; - if( isOpen(pPager->jfd) && pPager->journalOff>0 ){ - aNew[ii].iOffset = pPager->journalOff; - }else{ - aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager); - } - aNew[ii].iSubRec = pPager->nSubRec; - aNew[ii].pInSavepoint = sqlcipher_sqlite3BitvecCreate(pPager->dbSize); - if( !aNew[ii].pInSavepoint ){ - return SQLITE_NOMEM_BKPT; - } - if( pagerUseWal(pPager) ){ - sqlcipher_sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData); - } - pPager->nSavepoint = ii+1; - } - assert( pPager->nSavepoint==nSavepoint ); - assertTruncateConstraint(pPager); - return rc; -} -SQLITE_PRIVATE int sqlcipher_sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ - assert( pPager->eState>=PAGER_WRITER_LOCKED ); - assert( assert_pager_state(pPager) ); +/* A Btree handle +** +** A database connection contains a pointer to an instance of +** this object for every database file that it has open. This structure +** is opaque to the database connection. The database connection cannot +** see the internals of this structure and only deals with pointers to +** this structure. +** +** For some database files, the same underlying database cache might be +** shared between multiple connections. In that case, each connection +** has it own instance of this object. But each instance of this object +** points to the same BtShared object. The database cache and the +** schema associated with the database file are all contained within +** the BtShared object. +** +** All fields in this structure are accessed under sqlcipher_sqlite3.mutex. +** The pBt pointer itself may not be changed while there exists cursors +** in the referenced BtShared that point back to this Btree since those +** cursors have to go through this Btree to find their BtShared and +** they often do so without holding sqlcipher_sqlite3.mutex. +*/ +struct Btree { + sqlcipher_sqlite3 *db; /* The database connection holding this btree */ + BtShared *pBt; /* Sharable content of this btree */ + u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */ + u8 sharable; /* True if we can share pBt with another db */ + u8 locked; /* True if db currently has pBt locked */ + u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */ + int wantToLock; /* Number of nested calls to sqlcipher_sqlite3BtreeEnter() */ + int nBackup; /* Number of backup operations reading this btree */ + u32 iBDataVersion; /* Combines with pBt->pPager->iDataVersion */ + Btree *pNext; /* List of other sharable Btrees from the same db */ + Btree *pPrev; /* Back pointer of the same list */ +#ifdef SQLITE_DEBUG + u64 nSeek; /* Calls to sqlcipher_sqlite3BtreeMovetoUnpacked() */ +#endif +#ifndef SQLITE_OMIT_SHARED_CACHE + BtLock lock; /* Object used to lock page 1 */ +#endif +}; - if( nSavepoint>pPager->nSavepoint && pPager->useJournal ){ - return pagerOpenSavepoint(pPager, nSavepoint); - }else{ - return SQLITE_OK; - } -} +/* +** Btree.inTrans may take one of the following values. +** +** If the shared-data extension is enabled, there may be multiple users +** of the Btree structure. At most one of these may open a write transaction, +** but any number may have active read transactions. +** +** These values must match SQLITE_TXN_NONE, SQLITE_TXN_READ, and +** SQLITE_TXN_WRITE +*/ +#define TRANS_NONE 0 +#define TRANS_READ 1 +#define TRANS_WRITE 2 + +#if TRANS_NONE!=SQLITE_TXN_NONE +# error wrong numeric code for no-transaction +#endif +#if TRANS_READ!=SQLITE_TXN_READ +# error wrong numeric code for read-transaction +#endif +#if TRANS_WRITE!=SQLITE_TXN_WRITE +# error wrong numeric code for write-transaction +#endif + + +/* +** An instance of this object represents a single database file. +** +** A single database file can be in use at the same time by two +** or more database connections. When two or more connections are +** sharing the same database file, each connection has it own +** private Btree object for the file and each of those Btrees points +** to this one BtShared object. BtShared.nRef is the number of +** connections currently sharing this database file. +** +** Fields in this structure are accessed under the BtShared.mutex +** mutex, except for nRef and pNext which are accessed under the +** global SQLITE_MUTEX_STATIC_MAIN mutex. The pPager field +** may not be modified once it is initially set as long as nRef>0. +** The pSchema field may be set once under BtShared.mutex and +** thereafter is unchanged as long as nRef>0. +** +** isPending: +** +** If a BtShared client fails to obtain a write-lock on a database +** table (because there exists one or more read-locks on the table), +** the shared-cache enters 'pending-lock' state and isPending is +** set to true. +** +** The shared-cache leaves the 'pending lock' state when either of +** the following occur: +** +** 1) The current writer (BtShared.pWriter) concludes its transaction, OR +** 2) The number of locks held by other connections drops to zero. +** +** while in the 'pending-lock' state, no connection may start a new +** transaction. +** +** This feature is included to help prevent writer-starvation. +*/ +struct BtShared { + Pager *pPager; /* The page cache */ + sqlcipher_sqlite3 *db; /* Database connection currently using this Btree */ + BtCursor *pCursor; /* A list of all open cursors */ + MemPage *pPage1; /* First page of the database */ + u8 openFlags; /* Flags to sqlcipher_sqlite3BtreeOpen() */ +#ifndef SQLITE_OMIT_AUTOVACUUM + u8 autoVacuum; /* True if auto-vacuum is enabled */ + u8 incrVacuum; /* True if incr-vacuum is enabled */ + u8 bDoTruncate; /* True to truncate db on commit */ +#endif + u8 inTransaction; /* Transaction state */ + u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ + u8 nReserveWanted; /* Desired number of extra bytes per page */ + u16 btsFlags; /* Boolean parameters. See BTS_* macros below */ + u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ + u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ + u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ + u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ + u32 pageSize; /* Total number of bytes on a page */ + u32 usableSize; /* Number of usable bytes on each page */ + int nTransaction; /* Number of open transactions (read + write) */ + u32 nPage; /* Number of pages in the database */ + void *pSchema; /* Pointer to space allocated by sqlcipher_sqlite3BtreeSchema() */ + void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ + sqlcipher_sqlite3_mutex *mutex; /* Non-recursive mutex required to access this object */ + Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ +#ifndef SQLITE_OMIT_SHARED_CACHE + int nRef; /* Number of references to this structure */ + BtShared *pNext; /* Next on a list of sharable BtShared structs */ + BtLock *pLock; /* List of locks held on this shared-btree struct */ + Btree *pWriter; /* Btree with currently open write transaction */ +#endif + u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */ + int nPreformatSize; /* Size of last cell written by TransferRow() */ +}; + +/* +** Allowed values for BtShared.btsFlags +*/ +#define BTS_READ_ONLY 0x0001 /* Underlying file is readonly */ +#define BTS_PAGESIZE_FIXED 0x0002 /* Page size can no longer be changed */ +#define BTS_SECURE_DELETE 0x0004 /* PRAGMA secure_delete is enabled */ +#define BTS_OVERWRITE 0x0008 /* Overwrite deleted content with zeros */ +#define BTS_FAST_SECURE 0x000c /* Combination of the previous two */ +#define BTS_INITIALLY_EMPTY 0x0010 /* Database was empty at trans start */ +#define BTS_NO_WAL 0x0020 /* Do not open write-ahead-log files */ +#define BTS_EXCLUSIVE 0x0040 /* pWriter has an exclusive lock */ +#define BTS_PENDING 0x0080 /* Waiting for read-locks to clear */ + +/* +** An instance of the following structure is used to hold information +** about a cell. The parseCellPtr() function fills in this structure +** based on information extract from the raw disk page. +*/ +struct CellInfo { + i64 nKey; /* The key for INTKEY tables, or nPayload otherwise */ + u8 *pPayload; /* Pointer to the start of payload */ + u32 nPayload; /* Bytes of payload */ + u16 nLocal; /* Amount of payload held locally, not on overflow */ + u16 nSize; /* Size of the cell content on the main b-tree page */ +}; + +/* +** Maximum depth of an SQLite B-Tree structure. Any B-Tree deeper than +** this will be declared corrupt. This value is calculated based on a +** maximum database size of 2^31 pages a minimum fanout of 2 for a +** root-node and 3 for all other internal nodes. +** +** If a tree that appears to be taller than this is encountered, it is +** assumed that the database is corrupt. +*/ +#define BTCURSOR_MAX_DEPTH 20 + +/* +** A cursor is a pointer to a particular entry within a particular +** b-tree within a database file. +** +** The entry is identified by its MemPage and the index in +** MemPage.aCell[] of the entry. +** +** A single database file can be shared by two more database connections, +** but cursors cannot be shared. Each cursor is associated with a +** particular database connection identified BtCursor.pBtree.db. +** +** Fields in this structure are accessed under the BtShared.mutex +** found at self->pBt->mutex. +** +** skipNext meaning: +** The meaning of skipNext depends on the value of eState: +** +** eState Meaning of skipNext +** VALID skipNext is meaningless and is ignored +** INVALID skipNext is meaningless and is ignored +** SKIPNEXT sqlcipher_sqlite3BtreeNext() is a no-op if skipNext>0 and +** sqlcipher_sqlite3BtreePrevious() is no-op if skipNext<0. +** REQUIRESEEK restoreCursorPosition() restores the cursor to +** eState=SKIPNEXT if skipNext!=0 +** FAULT skipNext holds the cursor fault error code. +*/ +struct BtCursor { + u8 eState; /* One of the CURSOR_XXX constants (see below) */ + u8 curFlags; /* zero or more BTCF_* flags defined below */ + u8 curPagerFlags; /* Flags to send to sqlcipher_sqlite3PagerGet() */ + u8 hints; /* As configured by CursorSetHints() */ + int skipNext; /* Prev() is noop if negative. Next() is noop if positive. + ** Error code if eState==CURSOR_FAULT */ + Btree *pBtree; /* The Btree to which this cursor belongs */ + Pgno *aOverflow; /* Cache of overflow page locations */ + void *pKey; /* Saved key that was cursor last known position */ + /* All fields above are zeroed when the cursor is allocated. See + ** sqlcipher_sqlite3BtreeCursorZero(). Fields that follow must be manually + ** initialized. */ +#define BTCURSOR_FIRST_UNINIT pBt /* Name of first uninitialized field */ + BtShared *pBt; /* The BtShared this cursor points to */ + BtCursor *pNext; /* Forms a linked list of all cursors */ + CellInfo info; /* A parse of the cell we are pointing at */ + i64 nKey; /* Size of pKey, or last integer key */ + Pgno pgnoRoot; /* The root page of this tree */ + i8 iPage; /* Index of current page in apPage */ + u8 curIntKey; /* Value of apPage[0]->intKey */ + u16 ix; /* Current index for apPage[iPage] */ + u16 aiIdx[BTCURSOR_MAX_DEPTH-1]; /* Current index in apPage[i] */ + struct KeyInfo *pKeyInfo; /* Arg passed to comparison function */ + MemPage *pPage; /* Current page */ + MemPage *apPage[BTCURSOR_MAX_DEPTH-1]; /* Stack of parents of current page */ +}; +/* +** Legal values for BtCursor.curFlags +*/ +#define BTCF_WriteFlag 0x01 /* True if a write cursor */ +#define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ +#define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */ +#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */ +#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */ +#define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */ +#define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */ /* -** This function is called to rollback or release (commit) a savepoint. -** The savepoint to release or rollback need not be the most recently -** created savepoint. +** Potential values for BtCursor.eState. ** -** Parameter op is always either SAVEPOINT_ROLLBACK or SAVEPOINT_RELEASE. -** If it is SAVEPOINT_RELEASE, then release and destroy the savepoint with -** index iSavepoint. If it is SAVEPOINT_ROLLBACK, then rollback all changes -** that have occurred since the specified savepoint was created. +** CURSOR_INVALID: +** Cursor does not point to a valid entry. This can happen (for example) +** because the table is empty or because BtreeCursorFirst() has not been +** called. ** -** The savepoint to rollback or release is identified by parameter -** iSavepoint. A value of 0 means to operate on the outermost savepoint -** (the first created). A value of (Pager.nSavepoint-1) means operate -** on the most recently created savepoint. If iSavepoint is greater than -** (Pager.nSavepoint-1), then this function is a no-op. +** CURSOR_VALID: +** Cursor points to a valid entry. getPayload() etc. may be called. ** -** If a negative value is passed to this function, then the current -** transaction is rolled back. This is different to calling -** sqlcipher_sqlite3PagerRollback() because this function does not terminate -** the transaction or unlock the database, it just restores the -** contents of the database to its original state. +** CURSOR_SKIPNEXT: +** Cursor is valid except that the Cursor.skipNext field is non-zero +** indicating that the next sqlcipher_sqlite3BtreeNext() or sqlcipher_sqlite3BtreePrevious() +** operation should be a no-op. ** -** In any case, all savepoints with an index greater than iSavepoint -** are destroyed. If this is a release operation (op==SAVEPOINT_RELEASE), -** then savepoint iSavepoint is also destroyed. +** CURSOR_REQUIRESEEK: +** The table that this cursor was opened on still exists, but has been +** modified since the cursor was last used. The cursor position is saved +** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in +** this state, restoreCursorPosition() can be called to attempt to +** seek the cursor to the saved position. ** -** This function may return SQLITE_NOMEM if a memory allocation fails, -** or an IO error code if an IO error occurs while rolling back a -** savepoint. If no errors occur, SQLITE_OK is returned. +** CURSOR_FAULT: +** An unrecoverable error (an I/O error or a malloc failure) has occurred +** on a different connection that shares the BtShared cache with this +** cursor. The error has left the cache in an inconsistent state. +** Do nothing else with this cursor. Any attempt to use the cursor +** should return the error code stored in BtCursor.skipNext */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ - int rc = pPager->errCode; - -#ifdef SQLITE_ENABLE_ZIPVFS - if( op==SAVEPOINT_RELEASE ) rc = SQLITE_OK; -#endif - - assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); - assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK ); - - if( rc==SQLITE_OK && iSavepointnSavepoint ){ - int ii; /* Iterator variable */ - int nNew; /* Number of remaining savepoints after this op. */ - - /* Figure out how many savepoints will still be active after this - ** operation. Store this value in nNew. Then free resources associated - ** with any savepoints that are destroyed by this operation. - */ - nNew = iSavepoint + (( op==SAVEPOINT_RELEASE ) ? 0 : 1); - for(ii=nNew; iinSavepoint; ii++){ - sqlcipher_sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint); - } - pPager->nSavepoint = nNew; - - /* If this is a release of the outermost savepoint, truncate - ** the sub-journal to zero bytes in size. */ - if( op==SAVEPOINT_RELEASE ){ - if( nNew==0 && isOpen(pPager->sjfd) ){ - /* Only truncate if it is an in-memory sub-journal. */ - if( sqlcipher_sqlite3JournalIsInMemory(pPager->sjfd) ){ - rc = sqlcipher_sqlite3OsTruncate(pPager->sjfd, 0); - assert( rc==SQLITE_OK ); - } - pPager->nSubRec = 0; - } - } - /* Else this is a rollback operation, playback the specified savepoint. - ** If this is a temp-file, it is possible that the journal file has - ** not yet been opened. In this case there have been no changes to - ** the database file, so the playback operation can be skipped. - */ - else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){ - PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1]; - rc = pagerPlaybackSavepoint(pPager, pSavepoint); - assert(rc!=SQLITE_DONE); - } - -#ifdef SQLITE_ENABLE_ZIPVFS - /* If the cache has been modified but the savepoint cannot be rolled - ** back journal_mode=off, put the pager in the error state. This way, - ** if the VFS used by this pager includes ZipVFS, the entire transaction - ** can be rolled back at the ZipVFS level. */ - else if( - pPager->journalMode==PAGER_JOURNALMODE_OFF - && pPager->eState>=PAGER_WRITER_CACHEMOD - ){ - pPager->errCode = SQLITE_ABORT; - pPager->eState = PAGER_ERROR; - setGetterMethod(pPager); - } -#endif - } +#define CURSOR_VALID 0 +#define CURSOR_INVALID 1 +#define CURSOR_SKIPNEXT 2 +#define CURSOR_REQUIRESEEK 3 +#define CURSOR_FAULT 4 - return rc; -} +/* +** The database page the PENDING_BYTE occupies. This page is never used. +*/ +#define PENDING_BYTE_PAGE(pBt) ((Pgno)((PENDING_BYTE/((pBt)->pageSize))+1)) /* -** Return the full pathname of the database file. +** These macros define the location of the pointer-map entry for a +** database page. The first argument to each is the number of usable +** bytes on each page of the database (often 1024). The second is the +** page number to look up in the pointer map. ** -** Except, if the pager is in-memory only, then return an empty string if -** nullIfMemDb is true. This routine is called with nullIfMemDb==1 when -** used to report the filename to the user, for compatibility with legacy -** behavior. But when the Btree needs to know the filename for matching to -** shared cache, it uses nullIfMemDb==0 so that in-memory databases can -** participate in shared-cache. +** PTRMAP_PAGENO returns the database page number of the pointer-map +** page that stores the required pointer. PTRMAP_PTROFFSET returns +** the offset of the requested map entry. ** -** The return value to this routine is always safe to use with -** sqlcipher_sqlite3_uri_parameter() and sqlcipher_sqlite3_filename_database() and friends. +** If the pgno argument passed to PTRMAP_PAGENO is a pointer-map page, +** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be +** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements +** this test. */ -SQLITE_PRIVATE const char *sqlcipher_sqlite3PagerFilename(const Pager *pPager, int nullIfMemDb){ - static const char zFake[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - return (nullIfMemDb && pPager->memDb) ? &zFake[4] : pPager->zFilename; -} +#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno) +#define PTRMAP_PTROFFSET(pgptrmap, pgno) (5*(pgno-pgptrmap-1)) +#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno)) /* -** Return the VFS structure for the pager. +** The pointer map is a lookup table that identifies the parent page for +** each child page in the database file. The parent page is the page that +** contains a pointer to the child. Every page in the database contains +** 0 or 1 parent pages. (In this context 'database page' refers +** to any page that is not part of the pointer map itself.) Each pointer map +** entry consists of a single byte 'type' and a 4 byte parent page number. +** The PTRMAP_XXX identifiers below are the valid types. +** +** The purpose of the pointer map is to facility moving pages from one +** position in the file to another as part of autovacuum. When a page +** is moved, the pointer in its parent must be updated to point to the +** new location. The pointer map is used to locate the parent page quickly. +** +** PTRMAP_ROOTPAGE: The database page is a root-page. The page-number is not +** used in this case. +** +** PTRMAP_FREEPAGE: The database page is an unused (free) page. The page-number +** is not used in this case. +** +** PTRMAP_OVERFLOW1: The database page is the first page in a list of +** overflow pages. The page number identifies the page that +** contains the cell with a pointer to this overflow page. +** +** PTRMAP_OVERFLOW2: The database page is the second or later page in a list of +** overflow pages. The page-number identifies the previous +** page in the overflow page list. +** +** PTRMAP_BTREE: The database page is a non-root btree page. The page number +** identifies the parent page in the btree. */ -SQLITE_PRIVATE sqlcipher_sqlite3_vfs *sqlcipher_sqlite3PagerVfs(Pager *pPager){ - return pPager->pVfs; -} +#define PTRMAP_ROOTPAGE 1 +#define PTRMAP_FREEPAGE 2 +#define PTRMAP_OVERFLOW1 3 +#define PTRMAP_OVERFLOW2 4 +#define PTRMAP_BTREE 5 -/* -** Return the file handle for the database file associated -** with the pager. This might return NULL if the file has -** not yet been opened. +/* A bunch of assert() statements to check the transaction state variables +** of handle p (type Btree*) are internally consistent. */ -SQLITE_PRIVATE sqlcipher_sqlite3_file *sqlcipher_sqlite3PagerFile(Pager *pPager){ - return pPager->fd; -} +#define btreeIntegrity(p) \ + assert( p->pBt->inTransaction!=TRANS_NONE || p->pBt->nTransaction==0 ); \ + assert( p->pBt->inTransaction>=p->inTrans ); + /* -** Return the file handle for the journal file (if it exists). -** This will be either the rollback journal or the WAL file. +** The ISAUTOVACUUM macro is used within balance_nonroot() to determine +** if the database supports auto-vacuum or not. Because it is used +** within an expression that is an argument to another macro +** (sqliteMallocRaw), it is not possible to use conditional compilation. +** So, this macro is defined instead. */ -SQLITE_PRIVATE sqlcipher_sqlite3_file *sqlcipher_sqlite3PagerJrnlFile(Pager *pPager){ -#if SQLITE_OMIT_WAL - return pPager->jfd; +#ifndef SQLITE_OMIT_AUTOVACUUM +#define ISAUTOVACUUM (pBt->autoVacuum) #else - return pPager->pWal ? sqlcipher_sqlite3WalFile(pPager->pWal) : pPager->jfd; +#define ISAUTOVACUUM 0 #endif -} + /* -** Return the full pathname of the journal file. +** This structure is passed around through all the sanity checking routines +** in order to keep track of some global state information. +** +** The aRef[] array is allocated so that there is 1 bit for each page in +** the database. As the integrity-check proceeds, for each page used in +** the database the corresponding bit is set. This allows integrity-check to +** detect pages that are used twice and orphaned pages (both of which +** indicate corruption). */ -SQLITE_PRIVATE const char *sqlcipher_sqlite3PagerJournalname(Pager *pPager){ - return pPager->zJournal; -} +typedef struct IntegrityCk IntegrityCk; +struct IntegrityCk { + BtShared *pBt; /* The tree being checked out */ + Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ + u8 *aPgRef; /* 1 bit per page in the db (see above) */ + Pgno nPage; /* Number of pages in the database */ + int mxErr; /* Stop accumulating errors when this reaches zero */ + int nErr; /* Number of messages written to zErrMsg so far */ + int bOomFault; /* A memory allocation error has occurred */ + const char *zPfx; /* Error message prefix */ + Pgno v1; /* Value for first %u substitution in zPfx */ + int v2; /* Value for second %d substitution in zPfx */ + StrAccum errMsg; /* Accumulate the error message text here */ + u32 *heap; /* Min-heap used for analyzing cell coverage */ + sqlcipher_sqlite3 *db; /* Database connection running the check */ +}; -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC /* -** Set or retrieve the codec for this pager +** Routines to read or write a two- and four-byte big-endian integer values. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerSetCodec( - Pager *pPager, - void *(*xCodec)(void*,void*,Pgno,int), - void (*xCodecSizeChng)(void*,int,int), - void (*xCodecFree)(void*), - void *pCodec -){ - if( pPager->xCodecFree ){ - pPager->xCodecFree(pPager->pCodec); - }else{ - pager_reset(pPager); - } - pPager->xCodec = pPager->memDb ? 0 : xCodec; - pPager->xCodecSizeChng = xCodecSizeChng; - pPager->xCodecFree = xCodecFree; - pPager->pCodec = pCodec; - setGetterMethod(pPager); - pagerReportSize(pPager); -} -SQLITE_PRIVATE void *sqlcipher_sqlite3PagerGetCodec(Pager *pPager){ - return pPager->pCodec; -} +#define get2byte(x) ((x)[0]<<8 | (x)[1]) +#define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v)) +#define get4byte sqlcipher_sqlite3Get4byte +#define put4byte sqlcipher_sqlite3Put4byte /* -** This function is called by the wal module when writing page content -** into the log file. -** -** This function returns a pointer to a buffer containing the encrypted -** page content. If a malloc fails, this function may return NULL. +** get2byteAligned(), unlike get2byte(), requires that its argument point to a +** two-byte aligned address. get2bytea() is only used for accessing the +** cell addresses in a btree header. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3PagerCodec(PgHdr *pPg){ - void *aData = 0; - CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData); - return aData; +#if SQLITE_BYTEORDER==4321 +# define get2byteAligned(x) (*(u16*)(x)) +#elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4008000 +# define get2byteAligned(x) __builtin_bswap16(*(u16*)(x)) +#elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 +# define get2byteAligned(x) _byteswap_ushort(*(u16*)(x)) +#else +# define get2byteAligned(x) ((x)[0]<<8 | (x)[1]) +#endif + +/************** End of btreeInt.h ********************************************/ +/************** Continuing where we left off in btmutex.c ********************/ +#ifndef SQLITE_OMIT_SHARED_CACHE +#if SQLITE_THREADSAFE + +/* +** Obtain the BtShared mutex associated with B-Tree handle p. Also, +** set BtShared.db to the database handle associated with p and the +** p->locked boolean to true. +*/ +static void lockBtreeMutex(Btree *p){ + assert( p->locked==0 ); + assert( sqlcipher_sqlite3_mutex_notheld(p->pBt->mutex) ); + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + + sqlcipher_sqlite3_mutex_enter(p->pBt->mutex); + p->pBt->db = p->db; + p->locked = 1; } /* -** Return the current pager state +** Release the BtShared mutex associated with B-Tree handle p and +** clear the p->locked boolean. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerState(Pager *pPager){ - return pPager->eState; +static void SQLITE_NOINLINE unlockBtreeMutex(Btree *p){ + BtShared *pBt = p->pBt; + assert( p->locked==1 ); + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + assert( p->db==pBt->db ); + + sqlcipher_sqlite3_mutex_leave(pBt->mutex); + p->locked = 0; } -#endif /* SQLITE_HAS_CODEC */ -/* END SQLCIPHER */ -#ifndef SQLITE_OMIT_AUTOVACUUM +/* Forward reference */ +static void SQLITE_NOINLINE btreeLockCarefully(Btree *p); + /* -** Move the page pPg to location pgno in the file. -** -** There must be no references to the page previously located at -** pgno (which we call pPgOld) though that page is allowed to be -** in cache. If the page previously located at pgno is not already -** in the rollback journal, it is not put there by by this routine. -** -** References to the page pPg remain valid. Updating any -** meta-data associated with pPg (i.e. data stored in the nExtra bytes -** allocated along with the page) is the responsibility of the caller. -** -** A transaction must be active when this routine is called. It used to be -** required that a statement transaction was not active, but this restriction -** has been removed (CREATE INDEX needs to move a page when a statement -** transaction is active). +** Enter a mutex on the given BTree object. ** -** If the fourth argument, isCommit, is non-zero, then this page is being -** moved as part of a database reorganization just before the transaction -** is being committed. In this case, it is guaranteed that the database page -** pPg refers to will not be written to again within this transaction. +** If the object is not sharable, then no mutex is ever required +** and this routine is a no-op. The underlying mutex is non-recursive. +** But we keep a reference count in Btree.wantToLock so the behavior +** of this interface is recursive. ** -** This function may return SQLITE_NOMEM or an IO error code if an error -** occurs. Otherwise, it returns SQLITE_OK. +** To avoid deadlocks, multiple Btrees are locked in the same order +** by all database connections. The p->pNext is a list of other +** Btrees belonging to the same database connection as the p Btree +** which need to be locked after p. If we cannot get a lock on +** p, then first unlock all of the others on p->pNext, then wait +** for the lock to become available on p, then relock all of the +** subsequent Btrees that desire a lock. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ - PgHdr *pPgOld; /* The page being overwritten. */ - Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */ - int rc; /* Return code */ - Pgno origPgno; /* The original page number */ +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeEnter(Btree *p){ + /* Some basic sanity checking on the Btree. The list of Btrees + ** connected by pNext and pPrev should be in sorted order by + ** Btree.pBt value. All elements of the list should belong to + ** the same connection. Only shared Btrees are on the list. */ + assert( p->pNext==0 || p->pNext->pBt>p->pBt ); + assert( p->pPrev==0 || p->pPrev->pBtpBt ); + assert( p->pNext==0 || p->pNext->db==p->db ); + assert( p->pPrev==0 || p->pPrev->db==p->db ); + assert( p->sharable || (p->pNext==0 && p->pPrev==0) ); - assert( pPg->nRef>0 ); - assert( pPager->eState==PAGER_WRITER_CACHEMOD - || pPager->eState==PAGER_WRITER_DBMOD - ); - assert( assert_pager_state(pPager) ); + /* Check for locking consistency */ + assert( !p->locked || p->wantToLock>0 ); + assert( p->sharable || p->wantToLock==0 ); - /* In order to be able to rollback, an in-memory database must journal - ** the page we are moving from. - */ - assert( pPager->tempFile || !MEMDB ); - if( pPager->tempFile ){ - rc = sqlcipher_sqlite3PagerWrite(pPg); - if( rc ) return rc; - } + /* We should already hold a lock on the database connection */ + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - /* If the page being moved is dirty and has not been saved by the latest - ** savepoint, then save the current contents of the page into the - ** sub-journal now. This is required to handle the following scenario: - ** - ** BEGIN; - ** - ** SAVEPOINT one; - ** - ** ROLLBACK TO one; - ** - ** If page X were not written to the sub-journal here, it would not - ** be possible to restore its contents when the "ROLLBACK TO one" - ** statement were is processed. - ** - ** subjournalPage() may need to allocate space to store pPg->pgno into - ** one or more savepoint bitvecs. This is the reason this function - ** may return SQLITE_NOMEM. - */ - if( (pPg->flags & PGHDR_DIRTY)!=0 - && SQLITE_OK!=(rc = subjournalPageIfRequired(pPg)) - ){ - return rc; - } + /* Unless the database is sharable and unlocked, then BtShared.db + ** should already be set correctly. */ + assert( (p->locked==0 && p->sharable) || p->pBt->db==p->db ); - PAGERTRACE(("MOVE %d page %d (needSync=%d) moves to %d\n", - PAGERID(pPager), pPg->pgno, (pPg->flags&PGHDR_NEED_SYNC)?1:0, pgno)); - IOTRACE(("MOVE %p %d %d\n", pPager, pPg->pgno, pgno)) + if( !p->sharable ) return; + p->wantToLock++; + if( p->locked ) return; + btreeLockCarefully(p); +} - /* If the journal needs to be sync()ed before page pPg->pgno can - ** be written to, store pPg->pgno in local variable needSyncPgno. - ** - ** If the isCommit flag is set, there is no need to remember that - ** the journal needs to be sync()ed before database page pPg->pgno - ** can be written to. The caller has already promised not to write to it. +/* This is a helper function for sqlcipher_sqlite3BtreeLock(). By moving +** complex, but seldom used logic, out of sqlcipher_sqlite3BtreeLock() and +** into this routine, we avoid unnecessary stack pointer changes +** and thus help the sqlcipher_sqlite3BtreeLock() routine to run much faster +** in the common case. +*/ +static void SQLITE_NOINLINE btreeLockCarefully(Btree *p){ + Btree *pLater; + + /* In most cases, we should be able to acquire the lock we + ** want without having to go through the ascending lock + ** procedure that follows. Just be sure not to block. */ - if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){ - needSyncPgno = pPg->pgno; - assert( pPager->journalMode==PAGER_JOURNALMODE_OFF || - pageInJournal(pPager, pPg) || pPg->pgno>pPager->dbOrigSize ); - assert( pPg->flags&PGHDR_DIRTY ); + if( sqlcipher_sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){ + p->pBt->db = p->db; + p->locked = 1; + return; } - /* If the cache contains a page with page-number pgno, remove it - ** from its hash chain. Also, if the PGHDR_NEED_SYNC flag was set for - ** page pgno before the 'move' operation, it needs to be retained - ** for the page moved there. + /* To avoid deadlock, first release all locks with a larger + ** BtShared address. Then acquire our lock. Then reacquire + ** the other BtShared locks that we used to hold in ascending + ** order. */ - pPg->flags &= ~PGHDR_NEED_SYNC; - pPgOld = sqlcipher_sqlite3PagerLookup(pPager, pgno); - assert( !pPgOld || pPgOld->nRef==1 || CORRUPT_DB ); - if( pPgOld ){ - if( pPgOld->nRef>1 ){ - sqlcipher_sqlite3PagerUnrefNotNull(pPgOld); - return SQLITE_CORRUPT_BKPT; + for(pLater=p->pNext; pLater; pLater=pLater->pNext){ + assert( pLater->sharable ); + assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt ); + assert( !pLater->locked || pLater->wantToLock>0 ); + if( pLater->locked ){ + unlockBtreeMutex(pLater); } - pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); - if( pPager->tempFile ){ - /* Do not discard pages from an in-memory database since we might - ** need to rollback later. Just move the page out of the way. */ - sqlcipher_sqlite3PcacheMove(pPgOld, pPager->dbSize+1); - }else{ - sqlcipher_sqlite3PcacheDrop(pPgOld); + } + lockBtreeMutex(p); + for(pLater=p->pNext; pLater; pLater=pLater->pNext){ + if( pLater->wantToLock ){ + lockBtreeMutex(pLater); } } +} - origPgno = pPg->pgno; - sqlcipher_sqlite3PcacheMove(pPg, pgno); - sqlcipher_sqlite3PcacheMakeDirty(pPg); - - /* For an in-memory database, make sure the original page continues - ** to exist, in case the transaction needs to roll back. Use pPgOld - ** as the original page since it has already been allocated. - */ - if( pPager->tempFile && pPgOld ){ - sqlcipher_sqlite3PcacheMove(pPgOld, origPgno); - sqlcipher_sqlite3PagerUnrefNotNull(pPgOld); - } - if( needSyncPgno ){ - /* If needSyncPgno is non-zero, then the journal file needs to be - ** sync()ed before any data is written to database file page needSyncPgno. - ** Currently, no such page exists in the page-cache and the - ** "is journaled" bitvec flag has been set. This needs to be remedied by - ** loading the page into the pager-cache and setting the PGHDR_NEED_SYNC - ** flag. - ** - ** If the attempt to load the page into the page-cache fails, (due - ** to a malloc() or IO failure), clear the bit in the pInJournal[] - ** array. Otherwise, if the page is loaded and written again in - ** this transaction, it may be written to the database file before - ** it is synced into the journal file. This way, it may end up in - ** the journal file twice, but that is not a problem. - */ - PgHdr *pPgHdr; - rc = sqlcipher_sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr, 0); - if( rc!=SQLITE_OK ){ - if( needSyncPgno<=pPager->dbOrigSize ){ - assert( pPager->pTmpSpace!=0 ); - sqlcipher_sqlite3BitvecClear(pPager->pInJournal, needSyncPgno, pPager->pTmpSpace); - } - return rc; +/* +** Exit the recursive mutex on a Btree. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeLeave(Btree *p){ + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + if( p->sharable ){ + assert( p->wantToLock>0 ); + p->wantToLock--; + if( p->wantToLock==0 ){ + unlockBtreeMutex(p); } - pPgHdr->flags |= PGHDR_NEED_SYNC; - sqlcipher_sqlite3PcacheMakeDirty(pPgHdr); - sqlcipher_sqlite3PagerUnrefNotNull(pPgHdr); } +} - return SQLITE_OK; +#ifndef NDEBUG +/* +** Return true if the BtShared mutex is held on the btree, or if the +** B-Tree is not marked as sharable. +** +** This routine is used only from within assert() statements. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeHoldsMutex(Btree *p){ + assert( p->sharable==0 || p->locked==0 || p->wantToLock>0 ); + assert( p->sharable==0 || p->locked==0 || p->db==p->pBt->db ); + assert( p->sharable==0 || p->locked==0 || sqlcipher_sqlite3_mutex_held(p->pBt->mutex) ); + assert( p->sharable==0 || p->locked==0 || sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + + return (p->sharable==0 || p->locked); } #endif + /* -** The page handle passed as the first argument refers to a dirty page -** with a page number other than iNew. This function changes the page's -** page number to iNew and sets the value of the PgHdr.flags field to -** the value passed as the third parameter. +** Enter the mutex on every Btree associated with a database +** connection. This is needed (for example) prior to parsing +** a statement since we will be comparing table and column names +** against all schemas and we do not want those schemas being +** reset out from under us. +** +** There is a corresponding leave-all procedures. +** +** Enter the mutexes in accending order by BtShared pointer address +** to avoid the possibility of deadlock when two threads with +** two or more btrees in common both try to lock all their btrees +** at the same instant. */ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerRekey(DbPage *pPg, Pgno iNew, u16 flags){ - assert( pPg->pgno!=iNew ); - pPg->flags = flags; - sqlcipher_sqlite3PcacheMove(pPg, iNew); +static void SQLITE_NOINLINE btreeEnterAll(sqlcipher_sqlite3 *db){ + int i; + int skipOk = 1; + Btree *p; + assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); + for(i=0; inDb; i++){ + p = db->aDb[i].pBt; + if( p && p->sharable ){ + sqlcipher_sqlite3BtreeEnter(p); + skipOk = 0; + } + } + db->noSharedCache = skipOk; +} +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeEnterAll(sqlcipher_sqlite3 *db){ + if( db->noSharedCache==0 ) btreeEnterAll(db); +} +static void SQLITE_NOINLINE btreeLeaveAll(sqlcipher_sqlite3 *db){ + int i; + Btree *p; + assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); + for(i=0; inDb; i++){ + p = db->aDb[i].pBt; + if( p ) sqlcipher_sqlite3BtreeLeave(p); + } +} +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeLeaveAll(sqlcipher_sqlite3 *db){ + if( db->noSharedCache==0 ) btreeLeaveAll(db); } +#ifndef NDEBUG /* -** Return a pointer to the data for the specified page. +** Return true if the current thread holds the database connection +** mutex and all required BtShared mutexes. +** +** This routine is used inside assert() statements only. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3PagerGetData(DbPage *pPg){ - assert( pPg->nRef>0 || pPg->pPager->memDb ); - return pPg->pData; +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeHoldsAllMutexes(sqlcipher_sqlite3 *db){ + int i; + if( !sqlcipher_sqlite3_mutex_held(db->mutex) ){ + return 0; + } + for(i=0; inDb; i++){ + Btree *p; + p = db->aDb[i].pBt; + if( p && p->sharable && + (p->wantToLock==0 || !sqlcipher_sqlite3_mutex_held(p->pBt->mutex)) ){ + return 0; + } + } + return 1; } +#endif /* NDEBUG */ +#ifndef NDEBUG /* -** Return a pointer to the Pager.nExtra bytes of "extra" space -** allocated along with the specified page. +** Return true if the correct mutexes are held for accessing the +** db->aDb[iDb].pSchema structure. The mutexes required for schema +** access are: +** +** (1) The mutex on db +** (2) if iDb!=1, then the mutex on db->aDb[iDb].pBt. +** +** If pSchema is not NULL, then iDb is computed from pSchema and +** db using sqlcipher_sqlite3SchemaToIndex(). */ -SQLITE_PRIVATE void *sqlcipher_sqlite3PagerGetExtra(DbPage *pPg){ - return pPg->pExtra; +SQLITE_PRIVATE int sqlcipher_sqlite3SchemaMutexHeld(sqlcipher_sqlite3 *db, int iDb, Schema *pSchema){ + Btree *p; + assert( db!=0 ); + if( pSchema ) iDb = sqlcipher_sqlite3SchemaToIndex(db, pSchema); + assert( iDb>=0 && iDbnDb ); + if( !sqlcipher_sqlite3_mutex_held(db->mutex) ) return 0; + if( iDb==1 ) return 1; + p = db->aDb[iDb].pBt; + assert( p!=0 ); + return p->sharable==0 || p->locked==1; } +#endif /* NDEBUG */ +#else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */ /* -** Get/set the locking-mode for this pager. Parameter eMode must be one -** of PAGER_LOCKINGMODE_QUERY, PAGER_LOCKINGMODE_NORMAL or -** PAGER_LOCKINGMODE_EXCLUSIVE. If the parameter is not _QUERY, then -** the locking-mode is set to the value specified. +** The following are special cases for mutex enter routines for use +** in single threaded applications that use shared cache. Except for +** these two routines, all mutex operations are no-ops in that case and +** are null #defines in btree.h. ** -** The returned value is either PAGER_LOCKINGMODE_NORMAL or -** PAGER_LOCKINGMODE_EXCLUSIVE, indicating the current (possibly updated) -** locking-mode. +** If shared cache is disabled, then all btree mutex routines, including +** the ones below, are no-ops and are null #defines in btree.h. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerLockingMode(Pager *pPager, int eMode){ - assert( eMode==PAGER_LOCKINGMODE_QUERY - || eMode==PAGER_LOCKINGMODE_NORMAL - || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); - assert( PAGER_LOCKINGMODE_QUERY<0 ); - assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 ); - assert( pPager->exclusiveMode || 0==sqlcipher_sqlite3WalHeapMemory(pPager->pWal) ); - if( eMode>=0 && !pPager->tempFile && !sqlcipher_sqlite3WalHeapMemory(pPager->pWal) ){ - pPager->exclusiveMode = (u8)eMode; + +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeEnter(Btree *p){ + p->pBt->db = p->db; +} +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeEnterAll(sqlcipher_sqlite3 *db){ + int i; + for(i=0; inDb; i++){ + Btree *p = db->aDb[i].pBt; + if( p ){ + p->pBt->db = p->db; + } } - return (int)pPager->exclusiveMode; } +#endif /* if SQLITE_THREADSAFE */ +#ifndef SQLITE_OMIT_INCRBLOB /* -** Set the journal-mode for this pager. Parameter eMode must be one of: -** -** PAGER_JOURNALMODE_DELETE -** PAGER_JOURNALMODE_TRUNCATE -** PAGER_JOURNALMODE_PERSIST -** PAGER_JOURNALMODE_OFF -** PAGER_JOURNALMODE_MEMORY -** PAGER_JOURNALMODE_WAL +** Enter a mutex on a Btree given a cursor owned by that Btree. ** -** The journalmode is set to the value specified if the change is allowed. -** The change may be disallowed for the following reasons: +** These entry points are used by incremental I/O only. Enter() is required +** any time OMIT_SHARED_CACHE is not defined, regardless of whether or not +** the build is threadsafe. Leave() is only required by threadsafe builds. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeEnterCursor(BtCursor *pCur){ + sqlcipher_sqlite3BtreeEnter(pCur->pBtree); +} +# if SQLITE_THREADSAFE +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeLeaveCursor(BtCursor *pCur){ + sqlcipher_sqlite3BtreeLeave(pCur->pBtree); +} +# endif +#endif /* ifndef SQLITE_OMIT_INCRBLOB */ + +#endif /* ifndef SQLITE_OMIT_SHARED_CACHE */ + +/************** End of btmutex.c *********************************************/ +/************** Begin file btree.c *******************************************/ +/* +** 2004 April 6 ** -** * An in-memory database can only have its journal_mode set to _OFF -** or _MEMORY. +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** * Temporary databases cannot have _WAL journalmode. +** 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. ** -** The returned indicate the current (possibly updated) journal-mode. +************************************************************************* +** This file implements an external (disk-based) database using BTrees. +** See the header comment on "btreeInt.h" for additional information. +** Including a description of file format and an overview of operation. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ - u8 eOld = pPager->journalMode; /* Prior journalmode */ - - /* The eMode parameter is always valid */ - assert( eMode==PAGER_JOURNALMODE_DELETE - || eMode==PAGER_JOURNALMODE_TRUNCATE - || eMode==PAGER_JOURNALMODE_PERSIST - || eMode==PAGER_JOURNALMODE_OFF - || eMode==PAGER_JOURNALMODE_WAL - || eMode==PAGER_JOURNALMODE_MEMORY ); - - /* This routine is only called from the OP_JournalMode opcode, and - ** the logic there will never allow a temporary file to be changed - ** to WAL mode. - */ - assert( pPager->tempFile==0 || eMode!=PAGER_JOURNALMODE_WAL ); - - /* Do allow the journalmode of an in-memory database to be set to - ** anything other than MEMORY or OFF - */ - if( MEMDB ){ - assert( eOld==PAGER_JOURNALMODE_MEMORY || eOld==PAGER_JOURNALMODE_OFF ); - if( eMode!=PAGER_JOURNALMODE_MEMORY && eMode!=PAGER_JOURNALMODE_OFF ){ - eMode = eOld; - } - } - - if( eMode!=eOld ){ - - /* Change the journal mode. */ - assert( pPager->eState!=PAGER_ERROR ); - pPager->journalMode = (u8)eMode; +/* #include "btreeInt.h" */ - /* When transistioning from TRUNCATE or PERSIST to any other journal - ** mode except WAL, unless the pager is in locking_mode=exclusive mode, - ** delete the journal file. - */ - assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); - assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); - assert( (PAGER_JOURNALMODE_DELETE & 5)==0 ); - assert( (PAGER_JOURNALMODE_MEMORY & 5)==4 ); - assert( (PAGER_JOURNALMODE_OFF & 5)==0 ); - assert( (PAGER_JOURNALMODE_WAL & 5)==5 ); +/* +** The header string that appears at the beginning of every +** SQLite database. +*/ +static const char zMagicHeader[] = SQLITE_FILE_HEADER; - assert( isOpen(pPager->fd) || pPager->exclusiveMode ); - if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){ +/* +** Set this global variable to 1 to enable tracing using the TRACE +** macro. +*/ +#if 0 +int sqlcipher_sqlite3BtreeTrace=1; /* True to enable tracing */ +# define TRACE(X) if(sqlcipher_sqlite3BtreeTrace){printf X;fflush(stdout);} +#else +# define TRACE(X) +#endif - /* In this case we would like to delete the journal file. If it is - ** not possible, then that is not a problem. Deleting the journal file - ** here is an optimization only. - ** - ** Before deleting the journal file, obtain a RESERVED lock on the - ** database file. This ensures that the journal file is not deleted - ** while it is in use by some other client. - */ - sqlcipher_sqlite3OsClose(pPager->jfd); - if( pPager->eLock>=RESERVED_LOCK ){ - sqlcipher_sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); - }else{ - int rc = SQLITE_OK; - int state = pPager->eState; - assert( state==PAGER_OPEN || state==PAGER_READER ); - if( state==PAGER_OPEN ){ - rc = sqlcipher_sqlite3PagerSharedLock(pPager); - } - if( pPager->eState==PAGER_READER ){ - assert( rc==SQLITE_OK ); - rc = pagerLockDb(pPager, RESERVED_LOCK); - } - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); - } - if( rc==SQLITE_OK && state==PAGER_READER ){ - pagerUnlockDb(pPager, SHARED_LOCK); - }else if( state==PAGER_OPEN ){ - pager_unlock(pPager); - } - assert( state==pPager->eState ); - } - }else if( eMode==PAGER_JOURNALMODE_OFF ){ - sqlcipher_sqlite3OsClose(pPager->jfd); - } - } +/* +** Extract a 2-byte big-endian integer from an array of unsigned bytes. +** But if the value is zero, make it 65536. +** +** This routine is used to extract the "offset to cell content area" value +** from the header of a btree page. If the page size is 65536 and the page +** is empty, the offset should be 65536, but the 2-byte value stores zero. +** This routine makes the necessary adjustment to 65536. +*/ +#define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1) - /* Return the new journal mode */ - return (int)pPager->journalMode; -} +/* +** Values passed as the 5th argument to allocateBtreePage() +*/ +#define BTALLOC_ANY 0 /* Allocate any page */ +#define BTALLOC_EXACT 1 /* Allocate exact page if possible */ +#define BTALLOC_LE 2 /* Allocate any page <= the parameter */ /* -** Return the current journal mode. +** Macro IfNotOmitAV(x) returns (x) if SQLITE_OMIT_AUTOVACUUM is not +** defined, or 0 if it is. For example: +** +** bIncrVacuum = IfNotOmitAV(pBtShared->incrVacuum); */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerGetJournalMode(Pager *pPager){ - return (int)pPager->journalMode; -} +#ifndef SQLITE_OMIT_AUTOVACUUM +#define IfNotOmitAV(expr) (expr) +#else +#define IfNotOmitAV(expr) 0 +#endif +#ifndef SQLITE_OMIT_SHARED_CACHE /* -** Return TRUE if the pager is in a state where it is OK to change the -** journalmode. Journalmode changes can only happen when the database -** is unmodified. +** A list of BtShared objects that are eligible for participation +** in shared cache. This variable has file scope during normal builds, +** but the test harness needs to access it so we make it global for +** test builds. +** +** Access to this variable is protected by SQLITE_MUTEX_STATIC_MAIN. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerOkToChangeJournalMode(Pager *pPager){ - assert( assert_pager_state(pPager) ); - if( pPager->eState>=PAGER_WRITER_CACHEMOD ) return 0; - if( NEVER(isOpen(pPager->jfd) && pPager->journalOff>0) ) return 0; - return 1; -} +#ifdef SQLITE_TEST +SQLITE_PRIVATE BtShared *SQLITE_WSD sqlcipher_sqlite3SharedCacheList = 0; +#else +static BtShared *SQLITE_WSD sqlcipher_sqlite3SharedCacheList = 0; +#endif +#endif /* SQLITE_OMIT_SHARED_CACHE */ +#ifndef SQLITE_OMIT_SHARED_CACHE /* -** Get/set the size-limit used for persistent journal files. +** Enable or disable the shared pager and schema features. ** -** Setting the size limit to -1 means no limit is enforced. -** An attempt to set a limit smaller than -1 is a no-op. +** This routine has no effect on existing database connections. +** The shared cache setting effects only future calls to +** sqlcipher_sqlite3_open(), sqlcipher_sqlite3_open16(), or sqlcipher_sqlite3_open_v2(). */ -SQLITE_PRIVATE i64 sqlcipher_sqlite3PagerJournalSizeLimit(Pager *pPager, i64 iLimit){ - if( iLimit>=-1 ){ - pPager->journalSizeLimit = iLimit; - sqlcipher_sqlite3WalLimit(pPager->pWal, iLimit); - } - return pPager->journalSizeLimit; +SQLITE_API int sqlcipher_sqlite3_enable_shared_cache(int enable){ + sqlcipher_sqlite3GlobalConfig.sharedCacheEnabled = enable; + return SQLITE_OK; } +#endif + + + +#ifdef SQLITE_OMIT_SHARED_CACHE + /* + ** The functions querySharedCacheTableLock(), setSharedCacheTableLock(), + ** and clearAllSharedCacheTableLocks() + ** manipulate entries in the BtShared.pLock linked list used to store + ** shared-cache table level locks. If the library is compiled with the + ** shared-cache feature disabled, then there is only ever one user + ** of each BtShared structure and so this locking is not necessary. + ** So define the lock related functions as no-ops. + */ + #define querySharedCacheTableLock(a,b,c) SQLITE_OK + #define setSharedCacheTableLock(a,b,c) SQLITE_OK + #define clearAllSharedCacheTableLocks(a) + #define downgradeAllSharedCacheTableLocks(a) + #define hasSharedCacheTableLock(a,b,c,d) 1 + #define hasReadConflicts(a, b) 0 +#endif +#ifdef SQLITE_DEBUG /* -** Return a pointer to the pPager->pBackup variable. The backup module -** in backup.c maintains the content of this variable. This module -** uses it opaquely as an argument to sqlcipher_sqlite3BackupRestart() and -** sqlcipher_sqlite3BackupUpdate() only. +** Return and reset the seek counter for a Btree object. */ -SQLITE_PRIVATE sqlcipher_sqlite3_backup **sqlcipher_sqlite3PagerBackupPtr(Pager *pPager){ - return &pPager->pBackup; +SQLITE_PRIVATE sqlcipher_sqlite3_uint64 sqlcipher_sqlite3BtreeSeekCount(Btree *pBt){ + u64 n = pBt->nSeek; + pBt->nSeek = 0; + return n; } +#endif -#ifndef SQLITE_OMIT_VACUUM /* -** Unless this is an in-memory or temporary database, clear the pager cache. +** Implementation of the SQLITE_CORRUPT_PAGE() macro. Takes a single +** (MemPage*) as an argument. The (MemPage*) must not be NULL. +** +** If SQLITE_DEBUG is not defined, then this macro is equivalent to +** SQLITE_CORRUPT_BKPT. Or, if SQLITE_DEBUG is set, then the log message +** normally produced as a side-effect of SQLITE_CORRUPT_BKPT is augmented +** with the page number and filename associated with the (MemPage*). */ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerClearCache(Pager *pPager){ - assert( MEMDB==0 || pPager->tempFile ); - if( pPager->tempFile==0 ) pager_reset(pPager); +#ifdef SQLITE_DEBUG +int corruptPageError(int lineno, MemPage *p){ + char *zMsg; + sqlcipher_sqlite3BeginBenignMalloc(); + zMsg = sqlcipher_sqlite3_mprintf("database corruption page %d of %s", + (int)p->pgno, sqlcipher_sqlite3PagerFilename(p->pBt->pPager, 0) + ); + sqlcipher_sqlite3EndBenignMalloc(); + if( zMsg ){ + sqlcipher_sqlite3ReportError(SQLITE_CORRUPT, lineno, zMsg); + } + sqlcipher_sqlite3_free(zMsg); + return SQLITE_CORRUPT_BKPT; } +# define SQLITE_CORRUPT_PAGE(pMemPage) corruptPageError(__LINE__, pMemPage) +#else +# define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno) #endif +#ifndef SQLITE_OMIT_SHARED_CACHE -#ifndef SQLITE_OMIT_WAL +#ifdef SQLITE_DEBUG /* -** This function is called when the user invokes "PRAGMA wal_checkpoint", -** "PRAGMA wal_blocking_checkpoint" or calls the sqlcipher_sqlite3_wal_checkpoint() -** or wal_blocking_checkpoint() API functions. +**** This function is only used as part of an assert() statement. *** ** -** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. +** Check to see if pBtree holds the required locks to read or write to the +** table with root page iRoot. Return 1 if it does and 0 if not. +** +** For example, when writing to a table with root-page iRoot via +** Btree connection pBtree: +** +** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) ); +** +** When writing to an index that resides in a sharable database, the +** caller should have first obtained a lock specifying the root page of +** the corresponding table. This makes things a bit more complicated, +** as this module treats each table as a separate structure. To determine +** the table corresponding to the index being written, this +** function has to search through the database schema. +** +** Instead of a lock on the table/index rooted at page iRoot, the caller may +** hold a write-lock on the schema table (root page 1). This is also +** acceptable. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerCheckpoint( - Pager *pPager, /* Checkpoint on this pager */ - sqlcipher_sqlite3 *db, /* Db handle used to check for interrupts */ - int eMode, /* Type of checkpoint */ - int *pnLog, /* OUT: Final number of frames in log */ - int *pnCkpt /* OUT: Final number of checkpointed frames */ +static int hasSharedCacheTableLock( + Btree *pBtree, /* Handle that must hold lock */ + Pgno iRoot, /* Root page of b-tree */ + int isIndex, /* True if iRoot is the root of an index b-tree */ + int eLockType /* Required lock type (READ_LOCK or WRITE_LOCK) */ ){ - int rc = SQLITE_OK; - if( pPager->pWal ){ - rc = sqlcipher_sqlite3WalCheckpoint(pPager->pWal, db, eMode, - (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler), - pPager->pBusyHandlerArg, - pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, - pnLog, pnCkpt - ); + Schema *pSchema = (Schema *)pBtree->pBt->pSchema; + Pgno iTab = 0; + BtLock *pLock; + + /* If this database is not shareable, or if the client is reading + ** and has the read-uncommitted flag set, then no lock is required. + ** Return true immediately. + */ + if( (pBtree->sharable==0) + || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommit)) + ){ + return 1; + } + + /* If the client is reading or writing an index and the schema is + ** not loaded, then it is too difficult to actually check to see if + ** the correct locks are held. So do not bother - just return true. + ** This case does not come up very often anyhow. + */ + if( isIndex && (!pSchema || (pSchema->schemaFlags&DB_SchemaLoaded)==0) ){ + return 1; + } + + /* Figure out the root-page that the lock should be held on. For table + ** b-trees, this is just the root page of the b-tree being read or + ** written. For index b-trees, it is the root page of the associated + ** table. */ + if( isIndex ){ + HashElem *p; + int bSeen = 0; + for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ + Index *pIdx = (Index *)sqliteHashData(p); + if( pIdx->tnum==iRoot ){ + if( bSeen ){ + /* Two or more indexes share the same root page. There must + ** be imposter tables. So just return true. The assert is not + ** useful in that case. */ + return 1; + } + iTab = pIdx->pTable->tnum; + bSeen = 1; + } + } + }else{ + iTab = iRoot; } - return rc; -} -SQLITE_PRIVATE int sqlcipher_sqlite3PagerWalCallback(Pager *pPager){ - return sqlcipher_sqlite3WalCallback(pPager->pWal); -} + /* Search for the required lock. Either a write-lock on root-page iTab, a + ** write-lock on the schema table, or (if the client is reading) a + ** read-lock on iTab will suffice. Return 1 if any of these are found. */ + for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){ + if( pLock->pBtree==pBtree + && (pLock->iTable==iTab || (pLock->eLock==WRITE_LOCK && pLock->iTable==1)) + && pLock->eLock>=eLockType + ){ + return 1; + } + } -/* -** Return true if the underlying VFS for the given pager supports the -** primitives necessary for write-ahead logging. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerWalSupported(Pager *pPager){ - const sqlcipher_sqlite3_io_methods *pMethods = pPager->fd->pMethods; - if( pPager->noLock ) return 0; - return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap); + /* Failed to find the required lock. */ + return 0; } +#endif /* SQLITE_DEBUG */ +#ifdef SQLITE_DEBUG /* -** Attempt to take an exclusive lock on the database file. If a PENDING lock -** is obtained instead, immediately release it. +**** This function may be used as part of assert() statements only. **** +** +** Return true if it would be illegal for pBtree to write into the +** table or index rooted at iRoot because other shared connections are +** simultaneously reading that same table or index. +** +** It is illegal for pBtree to write if some other Btree object that +** shares the same BtShared object is currently reading or writing +** the iRoot table. Except, if the other Btree object has the +** read-uncommitted flag set, then it is OK for the other object to +** have a read cursor. +** +** For example, before writing to any part of the table or index +** rooted at page iRoot, one should call: +** +** assert( !hasReadConflicts(pBtree, iRoot) ); */ -static int pagerExclusiveLock(Pager *pPager){ - int rc; /* Return code */ - - assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); - rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); - if( rc!=SQLITE_OK ){ - /* If the attempt to grab the exclusive lock failed, release the - ** pending lock that may have been obtained instead. */ - pagerUnlockDb(pPager, SHARED_LOCK); +static int hasReadConflicts(Btree *pBtree, Pgno iRoot){ + BtCursor *p; + for(p=pBtree->pBt->pCursor; p; p=p->pNext){ + if( p->pgnoRoot==iRoot + && p->pBtree!=pBtree + && 0==(p->pBtree->db->flags & SQLITE_ReadUncommit) + ){ + return 1; + } } - - return rc; + return 0; } +#endif /* #ifdef SQLITE_DEBUG */ /* -** Call sqlcipher_sqlite3WalOpen() to open the WAL handle. If the pager is in -** exclusive-locking mode when this function is called, take an EXCLUSIVE -** lock on the database file and use heap-memory to store the wal-index -** in. Otherwise, use the normal shared-memory. +** Query to see if Btree handle p may obtain a lock of type eLock +** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return +** SQLITE_OK if the lock may be obtained (by calling +** setSharedCacheTableLock()), or SQLITE_LOCKED if not. */ -static int pagerOpenWal(Pager *pPager){ - int rc = SQLITE_OK; +static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){ + BtShared *pBt = p->pBt; + BtLock *pIter; - assert( pPager->pWal==0 && pPager->tempFile==0 ); - assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); + assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); + assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); + assert( p->db!=0 ); + assert( !(p->db->flags&SQLITE_ReadUncommit)||eLock==WRITE_LOCK||iTab==1 ); - /* If the pager is already in exclusive-mode, the WAL module will use - ** heap-memory for the wal-index instead of the VFS shared-memory - ** implementation. Take the exclusive lock now, before opening the WAL - ** file, to make sure this is safe. + /* If requesting a write-lock, then the Btree must have an open write + ** transaction on this file. And, obviously, for this to be so there + ** must be an open write transaction on the file itself. */ - if( pPager->exclusiveMode ){ - rc = pagerExclusiveLock(pPager); + assert( eLock==READ_LOCK || (p==pBt->pWriter && p->inTrans==TRANS_WRITE) ); + assert( eLock==READ_LOCK || pBt->inTransaction==TRANS_WRITE ); + + /* This routine is a no-op if the shared-cache is not enabled */ + if( !p->sharable ){ + return SQLITE_OK; } - /* Open the connection to the log file. If this operation fails, - ** (e.g. due to malloc() failure), return an error code. + /* If some other connection is holding an exclusive lock, the + ** requested lock may not be obtained. */ - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3WalOpen(pPager->pVfs, - pPager->fd, pPager->zWal, pPager->exclusiveMode, - pPager->journalSizeLimit, &pPager->pWal - ); + if( pBt->pWriter!=p && (pBt->btsFlags & BTS_EXCLUSIVE)!=0 ){ + sqlcipher_sqlite3ConnectionBlocked(p->db, pBt->pWriter->db); + return SQLITE_LOCKED_SHAREDCACHE; } - pagerFixMaplimit(pPager); - return rc; + for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ + /* The condition (pIter->eLock!=eLock) in the following if(...) + ** statement is a simplification of: + ** + ** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK) + ** + ** since we know that if eLock==WRITE_LOCK, then no other connection + ** may hold a WRITE_LOCK on any table in this file (since there can + ** only be a single writer). + */ + assert( pIter->eLock==READ_LOCK || pIter->eLock==WRITE_LOCK ); + assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); + if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ + sqlcipher_sqlite3ConnectionBlocked(p->db, pIter->pBtree->db); + if( eLock==WRITE_LOCK ){ + assert( p==pBt->pWriter ); + pBt->btsFlags |= BTS_PENDING; + } + return SQLITE_LOCKED_SHAREDCACHE; + } + } + return SQLITE_OK; } +#endif /* !SQLITE_OMIT_SHARED_CACHE */ - +#ifndef SQLITE_OMIT_SHARED_CACHE /* -** The caller must be holding a SHARED lock on the database file to call -** this function. +** Add a lock on the table with root-page iTable to the shared-btree used +** by Btree handle p. Parameter eLock must be either READ_LOCK or +** WRITE_LOCK. ** -** If the pager passed as the first argument is open on a real database -** file (not a temp file or an in-memory database), and the WAL file -** is not already open, make an attempt to open it now. If successful, -** return SQLITE_OK. If an error occurs or the VFS used by the pager does -** not support the xShmXXX() methods, return an error code. *pbOpen is -** not modified in either case. +** This function assumes the following: ** -** If the pager is open on a temp-file (or in-memory database), or if -** the WAL file is already open, set *pbOpen to 1 and return SQLITE_OK -** without doing anything. +** (a) The specified Btree object p is connected to a sharable +** database (one with the BtShared.sharable flag set), and +** +** (b) No other Btree objects hold a lock that conflicts +** with the requested lock (i.e. querySharedCacheTableLock() has +** already been called and returned SQLITE_OK). +** +** SQLITE_OK is returned if the lock is added successfully. SQLITE_NOMEM +** is returned if a malloc attempt fails. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerOpenWal( - Pager *pPager, /* Pager object */ - int *pbOpen /* OUT: Set to true if call is a no-op */ -){ - int rc = SQLITE_OK; /* Return code */ +static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ + BtShared *pBt = p->pBt; + BtLock *pLock = 0; + BtLock *pIter; - assert( assert_pager_state(pPager) ); - assert( pPager->eState==PAGER_OPEN || pbOpen ); - assert( pPager->eState==PAGER_READER || !pbOpen ); - assert( pbOpen==0 || *pbOpen==0 ); - assert( pbOpen!=0 || (!pPager->tempFile && !pPager->pWal) ); + assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); + assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); + assert( p->db!=0 ); - if( !pPager->tempFile && !pPager->pWal ){ - if( !sqlcipher_sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; + /* A connection with the read-uncommitted flag set will never try to + ** obtain a read-lock using this function. The only read-lock obtained + ** by a connection in read-uncommitted mode is on the sqlite_schema + ** table, and that lock is obtained in BtreeBeginTrans(). */ + assert( 0==(p->db->flags&SQLITE_ReadUncommit) || eLock==WRITE_LOCK ); - /* Close any rollback journal previously open */ - sqlcipher_sqlite3OsClose(pPager->jfd); + /* This function should only be called on a sharable b-tree after it + ** has been determined that no other b-tree holds a conflicting lock. */ + assert( p->sharable ); + assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) ); - rc = pagerOpenWal(pPager); - if( rc==SQLITE_OK ){ - pPager->journalMode = PAGER_JOURNALMODE_WAL; - pPager->eState = PAGER_OPEN; + /* First search the list for an existing lock on this table. */ + for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ + if( pIter->iTable==iTable && pIter->pBtree==p ){ + pLock = pIter; + break; } - }else{ - *pbOpen = 1; } - return rc; + /* If the above search did not find a BtLock struct associating Btree p + ** with table iTable, allocate one and link it into the list. + */ + if( !pLock ){ + pLock = (BtLock *)sqlcipher_sqlite3MallocZero(sizeof(BtLock)); + if( !pLock ){ + return SQLITE_NOMEM_BKPT; + } + pLock->iTable = iTable; + pLock->pBtree = p; + pLock->pNext = pBt->pLock; + pBt->pLock = pLock; + } + + /* Set the BtLock.eLock variable to the maximum of the current lock + ** and the requested lock. This means if a write-lock was already held + ** and a read-lock requested, we don't incorrectly downgrade the lock. + */ + assert( WRITE_LOCK>READ_LOCK ); + if( eLock>pLock->eLock ){ + pLock->eLock = eLock; + } + + return SQLITE_OK; } +#endif /* !SQLITE_OMIT_SHARED_CACHE */ +#ifndef SQLITE_OMIT_SHARED_CACHE /* -** This function is called to close the connection to the log file prior -** to switching from WAL to rollback mode. +** Release all the table locks (locks obtained via calls to +** the setSharedCacheTableLock() procedure) held by Btree object p. ** -** Before closing the log file, this function attempts to take an -** EXCLUSIVE lock on the database file. If this cannot be obtained, an -** error (SQLITE_BUSY) is returned and the log connection is not closed. -** If successful, the EXCLUSIVE lock is not released before returning. +** This function assumes that Btree p has an open read or write +** transaction. If it does not, then the BTS_PENDING flag +** may be incorrectly cleared. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerCloseWal(Pager *pPager, sqlcipher_sqlite3 *db){ - int rc = SQLITE_OK; +static void clearAllSharedCacheTableLocks(Btree *p){ + BtShared *pBt = p->pBt; + BtLock **ppIter = &pBt->pLock; - assert( pPager->journalMode==PAGER_JOURNALMODE_WAL ); + assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); + assert( p->sharable || 0==*ppIter ); + assert( p->inTrans>0 ); - /* If the log file is not already open, but does exist in the file-system, - ** it may need to be checkpointed before the connection can switch to - ** rollback mode. Open it now so this can happen. - */ - if( !pPager->pWal ){ - int logexists = 0; - rc = pagerLockDb(pPager, SHARED_LOCK); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3OsAccess( - pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists - ); - } - if( rc==SQLITE_OK && logexists ){ - rc = pagerOpenWal(pPager); + while( *ppIter ){ + BtLock *pLock = *ppIter; + assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); + assert( pLock->pBtree->inTrans>=pLock->eLock ); + if( pLock->pBtree==p ){ + *ppIter = pLock->pNext; + assert( pLock->iTable!=1 || pLock==&p->lock ); + if( pLock->iTable!=1 ){ + sqlcipher_sqlite3_free(pLock); + } + }else{ + ppIter = &pLock->pNext; } } - /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on - ** the database file, the log and log-summary files will be deleted. - */ - if( rc==SQLITE_OK && pPager->pWal ){ - rc = pagerExclusiveLock(pPager); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, - pPager->pageSize, (u8*)pPager->pTmpSpace); - pPager->pWal = 0; - pagerFixMaplimit(pPager); - if( rc && !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK); - } + assert( (pBt->btsFlags & BTS_PENDING)==0 || pBt->pWriter ); + if( pBt->pWriter==p ){ + pBt->pWriter = 0; + pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); + }else if( pBt->nTransaction==2 ){ + /* This function is called when Btree p is concluding its + ** transaction. If there currently exists a writer, and p is not + ** that writer, then the number of locks held by connections other + ** than the writer must be about to drop to zero. In this case + ** set the BTS_PENDING flag to 0. + ** + ** If there is not currently a writer, then BTS_PENDING must + ** be zero already. So this next line is harmless in that case. + */ + pBt->btsFlags &= ~BTS_PENDING; } - return rc; } -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT /* -** If pager pPager is a wal-mode database not in exclusive locking mode, -** invoke the sqlcipher_sqlite3WalWriteLock() function on the associated Wal object -** with the same db and bLock parameters as were passed to this function. -** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise. +** This function changes all write-locks held by Btree p into read-locks. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerWalWriteLock(Pager *pPager, int bLock){ - int rc = SQLITE_OK; - if( pagerUseWal(pPager) && pPager->exclusiveMode==0 ){ - rc = sqlcipher_sqlite3WalWriteLock(pPager->pWal, bLock); +static void downgradeAllSharedCacheTableLocks(Btree *p){ + BtShared *pBt = p->pBt; + if( pBt->pWriter==p ){ + BtLock *pLock; + pBt->pWriter = 0; + pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); + for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ + assert( pLock->eLock==READ_LOCK || pLock->pBtree==p ); + pLock->eLock = READ_LOCK; + } } - return rc; } +#endif /* SQLITE_OMIT_SHARED_CACHE */ + +static void releasePage(MemPage *pPage); /* Forward reference */ +static void releasePageOne(MemPage *pPage); /* Forward reference */ +static void releasePageNotNull(MemPage *pPage); /* Forward reference */ + /* -** Set the database handle used by the wal layer to determine if -** blocking locks are required. +***** This routine is used inside of assert() only **** +** +** Verify that the cursor holds the mutex on its BtShared */ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerWalDb(Pager *pPager, sqlcipher_sqlite3 *db){ - if( pagerUseWal(pPager) ){ - sqlcipher_sqlite3WalDb(pPager->pWal, db); - } +#ifdef SQLITE_DEBUG +static int cursorHoldsMutex(BtCursor *p){ + return sqlcipher_sqlite3_mutex_held(p->pBt->mutex); } -#endif -#ifdef SQLITE_ENABLE_SNAPSHOT -/* -** If this is a WAL database, obtain a snapshot handle for the snapshot -** currently open. Otherwise, return an error. +/* Verify that the cursor and the BtShared agree about what is the current +** database connetion. This is important in shared-cache mode. If the database +** connection pointers get out-of-sync, it is possible for routines like +** btreeInitPage() to reference an stale connection pointer that references a +** a connection that has already closed. This routine is used inside assert() +** statements only and for the purpose of double-checking that the btree code +** does keep the database connection pointers up-to-date. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerSnapshotGet(Pager *pPager, sqlcipher_sqlite3_snapshot **ppSnapshot){ - int rc = SQLITE_ERROR; - if( pPager->pWal ){ - rc = sqlcipher_sqlite3WalSnapshotGet(pPager->pWal, ppSnapshot); - } - return rc; +static int cursorOwnsBtShared(BtCursor *p){ + assert( cursorHoldsMutex(p) ); + return (p->pBtree->db==p->pBt->db); } +#endif /* -** If this is a WAL database, store a pointer to pSnapshot. Next time a -** read transaction is opened, attempt to read from the snapshot it -** identifies. If this is not a WAL database, return an error. +** Invalidate the overflow cache of the cursor passed as the first argument. +** on the shared btree structure pBt. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerSnapshotOpen( - Pager *pPager, - sqlcipher_sqlite3_snapshot *pSnapshot -){ - int rc = SQLITE_OK; - if( pPager->pWal ){ - sqlcipher_sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot); - }else{ - rc = SQLITE_ERROR; - } - return rc; -} +#define invalidateOverflowCache(pCur) (pCur->curFlags &= ~BTCF_ValidOvfl) /* -** If this is a WAL database, call sqlcipher_sqlite3WalSnapshotRecover(). If this -** is not a WAL database, return an error. +** Invalidate the overflow page-list cache for all cursors opened +** on the shared btree structure pBt. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerSnapshotRecover(Pager *pPager){ - int rc; - if( pPager->pWal ){ - rc = sqlcipher_sqlite3WalSnapshotRecover(pPager->pWal); - }else{ - rc = SQLITE_ERROR; +static void invalidateAllOverflowCache(BtShared *pBt){ + BtCursor *p; + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + for(p=pBt->pCursor; p; p=p->pNext){ + invalidateOverflowCache(p); } - return rc; } +#ifndef SQLITE_OMIT_INCRBLOB /* -** The caller currently has a read transaction open on the database. -** If this is not a WAL database, SQLITE_ERROR is returned. Otherwise, -** this function takes a SHARED lock on the CHECKPOINTER slot and then -** checks if the snapshot passed as the second argument is still -** available. If so, SQLITE_OK is returned. +** This function is called before modifying the contents of a table +** to invalidate any incrblob cursors that are open on the +** row or one of the rows being modified. ** -** If the snapshot is not available, SQLITE_ERROR is returned. Or, if -** the CHECKPOINTER lock cannot be obtained, SQLITE_BUSY. If any error -** occurs (any value other than SQLITE_OK is returned), the CHECKPOINTER -** lock is released before returning. +** If argument isClearTable is true, then the entire contents of the +** table is about to be deleted. In this case invalidate all incrblob +** cursors open on any row within the table with root-page pgnoRoot. +** +** Otherwise, if argument isClearTable is false, then the row with +** rowid iRow is being replaced or deleted. In this case invalidate +** only those incrblob cursors open on that specific row. */ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerSnapshotCheck(Pager *pPager, sqlcipher_sqlite3_snapshot *pSnapshot){ - int rc; - if( pPager->pWal ){ - rc = sqlcipher_sqlite3WalSnapshotCheck(pPager->pWal, pSnapshot); - }else{ - rc = SQLITE_ERROR; +static void invalidateIncrblobCursors( + Btree *pBtree, /* The database file to check */ + Pgno pgnoRoot, /* The table that might be changing */ + i64 iRow, /* The rowid that might be changing */ + int isClearTable /* True if all rows are being deleted */ +){ + BtCursor *p; + assert( pBtree->hasIncrblobCur ); + assert( sqlcipher_sqlite3BtreeHoldsMutex(pBtree) ); + pBtree->hasIncrblobCur = 0; + for(p=pBtree->pBt->pCursor; p; p=p->pNext){ + if( (p->curFlags & BTCF_Incrblob)!=0 ){ + pBtree->hasIncrblobCur = 1; + if( p->pgnoRoot==pgnoRoot && (isClearTable || p->info.nKey==iRow) ){ + p->eState = CURSOR_INVALID; + } + } } - return rc; -} - -/* -** Release a lock obtained by an earlier successful call to -** sqlcipher_sqlite3PagerSnapshotCheck(). -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3PagerSnapshotUnlock(Pager *pPager){ - assert( pPager->pWal ); - sqlcipher_sqlite3WalSnapshotUnlock(pPager->pWal); -} - -#endif /* SQLITE_ENABLE_SNAPSHOT */ -#endif /* !SQLITE_OMIT_WAL */ - -#ifdef SQLITE_ENABLE_ZIPVFS -/* -** A read-lock must be held on the pager when this function is called. If -** the pager is in WAL mode and the WAL file currently contains one or more -** frames, return the size in bytes of the page images stored within the -** WAL frames. Otherwise, if this is not a WAL database or the WAL file -** is empty, return 0. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3PagerWalFramesize(Pager *pPager){ - assert( pPager->eState>=PAGER_READER ); - return sqlcipher_sqlite3WalFramesize(pPager->pWal); -} -#endif - -#endif /* SQLITE_OMIT_DISKIO */ - -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - -SQLITE_API int sqlcipher_sqlite3pager_is_mj_pgno(Pager *pPager, Pgno pgno) { - return (PAGER_MJ_PGNO(pPager) == pgno) ? 1 : 0; -} - -SQLITE_API void sqlcipher_sqlite3pager_error(Pager *pPager, int error) { - pPager->errCode = error; - pPager->eState = PAGER_ERROR; - setGetterMethod(pPager); -} - -SQLITE_API void sqlcipher_sqlite3pager_reset(Pager *pPager){ - pager_reset(pPager); } -#endif -/* END SQLCIPHER */ - +#else + /* Stub function when INCRBLOB is omitted */ + #define invalidateIncrblobCursors(w,x,y,z) +#endif /* SQLITE_OMIT_INCRBLOB */ -/************** End of pager.c ***********************************************/ -/************** Begin file wal.c *********************************************/ /* -** 2010 February 1 -** -** 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 the implementation of a write-ahead log (WAL) used in -** "journal_mode=WAL" mode. -** -** WRITE-AHEAD LOG (WAL) FILE FORMAT -** -** A WAL file consists of a header followed by zero or more "frames". -** Each frame records the revised content of a single page from the -** database file. All changes to the database are recorded by writing -** frames into the WAL. Transactions commit when a frame is written that -** contains a commit marker. A single WAL can and usually does record -** multiple transactions. Periodically, the content of the WAL is -** transferred back into the database file in an operation called a -** "checkpoint". -** -** A single WAL file can be used multiple times. In other words, the -** WAL can fill up with frames and then be checkpointed and then new -** frames can overwrite the old ones. A WAL always grows from beginning -** toward the end. Checksums and counters attached to each frame are -** used to determine which frames within the WAL are valid and which -** are leftovers from prior checkpoints. -** -** The WAL header is 32 bytes in size and consists of the following eight -** big-endian 32-bit unsigned integer values: -** -** 0: Magic number. 0x377f0682 or 0x377f0683 -** 4: File format version. Currently 3007000 -** 8: Database page size. Example: 1024 -** 12: Checkpoint sequence number -** 16: Salt-1, random integer incremented with each checkpoint -** 20: Salt-2, a different random integer changing with each ckpt -** 24: Checksum-1 (first part of checksum for first 24 bytes of header). -** 28: Checksum-2 (second part of checksum for first 24 bytes of header). -** -** Immediately following the wal-header are zero or more frames. Each -** frame consists of a 24-byte frame-header followed by a bytes -** of page data. The frame-header is six big-endian 32-bit unsigned -** integer values, as follows: -** -** 0: Page number. -** 4: For commit records, the size of the database image in pages -** after the commit. For all other records, zero. -** 8: Salt-1 (copied from the header) -** 12: Salt-2 (copied from the header) -** 16: Checksum-1. -** 20: Checksum-2. -** -** A frame is considered valid if and only if the following conditions are -** true: -** -** (1) The salt-1 and salt-2 values in the frame-header match -** salt values in the wal-header -** -** (2) The checksum values in the final 8 bytes of the frame-header -** exactly match the checksum computed consecutively on the -** WAL header and the first 8 bytes and the content of all frames -** up to and including the current frame. -** -** The checksum is computed using 32-bit big-endian integers if the -** magic number in the first 4 bytes of the WAL is 0x377f0683 and it -** is computed using little-endian if the magic number is 0x377f0682. -** The checksum values are always stored in the frame header in a -** big-endian format regardless of which byte order is used to compute -** the checksum. The checksum is computed by interpreting the input as -** an even number of unsigned 32-bit integers: x[0] through x[N]. The -** algorithm used for the checksum is as follows: -** -** for i from 0 to n-1 step 2: -** s0 += x[i] + s1; -** s1 += x[i+1] + s0; -** endfor -** -** Note that s0 and s1 are both weighted checksums using fibonacci weights -** in reverse order (the largest fibonacci weight occurs on the first element -** of the sequence being summed.) The s1 value spans all 32-bit -** terms of the sequence whereas s0 omits the final term. -** -** On a checkpoint, the WAL is first VFS.xSync-ed, then valid content of the -** WAL is transferred into the database, then the database is VFS.xSync-ed. -** The VFS.xSync operations serve as write barriers - all writes launched -** before the xSync must complete before any write that launches after the -** xSync begins. -** -** After each checkpoint, the salt-1 value is incremented and the salt-2 -** value is randomized. This prevents old and new frames in the WAL from -** being considered valid at the same time and being checkpointing together -** following a crash. -** -** READER ALGORITHM -** -** To read a page from the database (call it page number P), a reader -** first checks the WAL to see if it contains page P. If so, then the -** last valid instance of page P that is a followed by a commit frame -** or is a commit frame itself becomes the value read. If the WAL -** contains no copies of page P that are valid and which are a commit -** frame or are followed by a commit frame, then page P is read from -** the database file. -** -** To start a read transaction, the reader records the index of the last -** valid frame in the WAL. The reader uses this recorded "mxFrame" value -** for all subsequent read operations. New transactions can be appended -** to the WAL, but as long as the reader uses its original mxFrame value -** and ignores the newly appended content, it will see a consistent snapshot -** of the database from a single point in time. This technique allows -** multiple concurrent readers to view different versions of the database -** content simultaneously. -** -** The reader algorithm in the previous paragraphs works correctly, but -** because frames for page P can appear anywhere within the WAL, the -** reader has to scan the entire WAL looking for page P frames. If the -** WAL is large (multiple megabytes is typical) that scan can be slow, -** and read performance suffers. To overcome this problem, a separate -** data structure called the wal-index is maintained to expedite the -** search for frames of a particular page. -** -** WAL-INDEX FORMAT -** -** Conceptually, the wal-index is shared memory, though VFS implementations -** might choose to implement the wal-index using a mmapped file. Because -** the wal-index is shared memory, SQLite does not support journal_mode=WAL -** on a network filesystem. All users of the database must be able to -** share memory. -** -** In the default unix and windows implementation, the wal-index is a mmapped -** file whose name is the database name with a "-shm" suffix added. For that -** reason, the wal-index is sometimes called the "shm" file. -** -** The wal-index is transient. After a crash, the wal-index can (and should -** be) reconstructed from the original WAL file. In fact, the VFS is required -** to either truncate or zero the header of the wal-index when the last -** connection to it closes. Because the wal-index is transient, it can -** use an architecture-specific format; it does not have to be cross-platform. -** Hence, unlike the database and WAL file formats which store all values -** as big endian, the wal-index can store multi-byte values in the native -** byte order of the host computer. -** -** The purpose of the wal-index is to answer this question quickly: Given -** a page number P and a maximum frame index M, return the index of the -** last frame in the wal before frame M for page P in the WAL, or return -** NULL if there are no frames for page P in the WAL prior to M. -** -** The wal-index consists of a header region, followed by an one or -** more index blocks. -** -** The wal-index header contains the total number of frames within the WAL -** in the mxFrame field. -** -** Each index block except for the first contains information on -** HASHTABLE_NPAGE frames. The first index block contains information on -** HASHTABLE_NPAGE_ONE frames. The values of HASHTABLE_NPAGE_ONE and -** HASHTABLE_NPAGE are selected so that together the wal-index header and -** first index block are the same size as all other index blocks in the -** wal-index. -** -** Each index block contains two sections, a page-mapping that contains the -** database page number associated with each wal frame, and a hash-table -** that allows readers to query an index block for a specific page number. -** The page-mapping is an array of HASHTABLE_NPAGE (or HASHTABLE_NPAGE_ONE -** for the first index block) 32-bit page numbers. The first entry in the -** first index-block contains the database page number corresponding to the -** first frame in the WAL file. The first entry in the second index block -** in the WAL file corresponds to the (HASHTABLE_NPAGE_ONE+1)th frame in -** the log, and so on. -** -** The last index block in a wal-index usually contains less than the full -** complement of HASHTABLE_NPAGE (or HASHTABLE_NPAGE_ONE) page-numbers, -** depending on the contents of the WAL file. This does not change the -** allocated size of the page-mapping array - the page-mapping array merely -** contains unused entries. -** -** Even without using the hash table, the last frame for page P -** can be found by scanning the page-mapping sections of each index block -** starting with the last index block and moving toward the first, and -** within each index block, starting at the end and moving toward the -** beginning. The first entry that equals P corresponds to the frame -** holding the content for that page. -** -** The hash table consists of HASHTABLE_NSLOT 16-bit unsigned integers. -** HASHTABLE_NSLOT = 2*HASHTABLE_NPAGE, and there is one entry in the -** hash table for each page number in the mapping section, so the hash -** table is never more than half full. The expected number of collisions -** prior to finding a match is 1. Each entry of the hash table is an -** 1-based index of an entry in the mapping section of the same -** index block. Let K be the 1-based index of the largest entry in -** the mapping section. (For index blocks other than the last, K will -** always be exactly HASHTABLE_NPAGE (4096) and for the last index block -** K will be (mxFrame%HASHTABLE_NPAGE).) Unused slots of the hash table -** contain a value of 0. -** -** To look for page P in the hash table, first compute a hash iKey on -** P as follows: +** Set bit pgno of the BtShared.pHasContent bitvec. This is called +** when a page that previously contained data becomes a free-list leaf +** page. ** -** iKey = (P * 383) % HASHTABLE_NSLOT +** The BtShared.pHasContent bitvec exists to work around an obscure +** bug caused by the interaction of two useful IO optimizations surrounding +** free-list leaf pages: ** -** Then start scanning entries of the hash table, starting with iKey -** (wrapping around to the beginning when the end of the hash table is -** reached) until an unused hash slot is found. Let the first unused slot -** be at index iUnused. (iUnused might be less than iKey if there was -** wrap-around.) Because the hash table is never more than half full, -** the search is guaranteed to eventually hit an unused entry. Let -** iMax be the value between iKey and iUnused, closest to iUnused, -** where aHash[iMax]==P. If there is no iMax entry (if there exists -** no hash slot such that aHash[i]==p) then page P is not in the -** current index block. Otherwise the iMax-th mapping entry of the -** current index block corresponds to the last entry that references -** page P. +** 1) When all data is deleted from a page and the page becomes +** a free-list leaf page, the page is not written to the database +** (as free-list leaf pages contain no meaningful data). Sometimes +** such a page is not even journalled (as it will not be modified, +** why bother journalling it?). ** -** A hash search begins with the last index block and moves toward the -** first index block, looking for entries corresponding to page P. On -** average, only two or three slots in each index block need to be -** examined in order to either find the last entry for page P, or to -** establish that no such entry exists in the block. Each index block -** holds over 4000 entries. So two or three index blocks are sufficient -** to cover a typical 10 megabyte WAL file, assuming 1K pages. 8 or 10 -** comparisons (on average) suffice to either locate a frame in the -** WAL or to establish that the frame does not exist in the WAL. This -** is much faster than scanning the entire 10MB WAL. +** 2) When a free-list leaf page is reused, its content is not read +** from the database or written to the journal file (why should it +** be, if it is not at all meaningful?). ** -** Note that entries are added in order of increasing K. Hence, one -** reader might be using some value K0 and a second reader that started -** at a later time (after additional transactions were added to the WAL -** and to the wal-index) might be using a different value K1, where K1>K0. -** Both readers can use the same hash table and mapping section to get -** the correct result. There may be entries in the hash table with -** K>K0 but to the first reader, those entries will appear to be unused -** slots in the hash table and so the first reader will get an answer as -** if no values greater than K0 had ever been inserted into the hash table -** in the first place - which is what reader one wants. Meanwhile, the -** second reader using K1 will see additional values that were inserted -** later, which is exactly what reader two wants. +** By themselves, these optimizations work fine and provide a handy +** performance boost to bulk delete or insert operations. However, if +** a page is moved to the free-list and then reused within the same +** transaction, a problem comes up. If the page is not journalled when +** it is moved to the free-list and it is also not journalled when it +** is extracted from the free-list and reused, then the original data +** may be lost. In the event of a rollback, it may not be possible +** to restore the database to its original configuration. ** -** When a rollback occurs, the value of K is decreased. Hash table entries -** that correspond to frames greater than the new K value are removed -** from the hash table at this point. +** The solution is the BtShared.pHasContent bitvec. Whenever a page is +** moved to become a free-list leaf page, the corresponding bit is +** set in the bitvec. Whenever a leaf page is extracted from the free-list, +** optimization 2 above is omitted if the corresponding bit is already +** set in BtShared.pHasContent. The contents of the bitvec are cleared +** at the end of every transaction. */ -#ifndef SQLITE_OMIT_WAL - -/* #include "wal.h" */ +static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ + int rc = SQLITE_OK; + if( !pBt->pHasContent ){ + assert( pgno<=pBt->nPage ); + pBt->pHasContent = sqlcipher_sqlite3BitvecCreate(pBt->nPage); + if( !pBt->pHasContent ){ + rc = SQLITE_NOMEM_BKPT; + } + } + if( rc==SQLITE_OK && pgno<=sqlcipher_sqlite3BitvecSize(pBt->pHasContent) ){ + rc = sqlcipher_sqlite3BitvecSet(pBt->pHasContent, pgno); + } + return rc; +} /* -** Trace output macros +** Query the BtShared.pHasContent vector. +** +** This function is called when a free-list leaf page is removed from the +** free-list for reuse. It returns false if it is safe to retrieve the +** page from the pager layer with the 'no-content' flag set. True otherwise. */ -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -SQLITE_PRIVATE int sqlcipher_sqlite3WalTrace = 0; -# define WALTRACE(X) if(sqlcipher_sqlite3WalTrace) sqlcipher_sqlite3DebugPrintf X -#else -# define WALTRACE(X) -#endif +static int btreeGetHasContent(BtShared *pBt, Pgno pgno){ + Bitvec *p = pBt->pHasContent; + return p && (pgno>sqlcipher_sqlite3BitvecSize(p) || sqlcipher_sqlite3BitvecTestNotNull(p, pgno)); +} /* -** The maximum (and only) versions of the wal and wal-index formats -** that may be interpreted by this version of SQLite. -** -** If a client begins recovering a WAL file and finds that (a) the checksum -** values in the wal-header are correct and (b) the version field is not -** WAL_MAX_VERSION, recovery fails and SQLite returns SQLITE_CANTOPEN. -** -** Similarly, if a client successfully reads a wal-index header (i.e. the -** checksum test is successful) and finds that the version field is not -** WALINDEX_MAX_VERSION, then no read-transaction is opened and SQLite -** returns SQLITE_CANTOPEN. +** Clear (destroy) the BtShared.pHasContent bitvec. This should be +** invoked at the conclusion of each write-transaction. */ -#define WAL_MAX_VERSION 3007000 -#define WALINDEX_MAX_VERSION 3007000 +static void btreeClearHasContent(BtShared *pBt){ + sqlcipher_sqlite3BitvecDestroy(pBt->pHasContent); + pBt->pHasContent = 0; +} /* -** Index numbers for various locking bytes. WAL_NREADER is the number -** of available reader locks and should be at least 3. The default -** is SQLITE_SHM_NLOCK==8 and WAL_NREADER==5. -** -** Technically, the various VFSes are free to implement these locks however -** they see fit. However, compatibility is encouraged so that VFSes can -** interoperate. The standard implemention used on both unix and windows -** is for the index number to indicate a byte offset into the -** WalCkptInfo.aLock[] array in the wal-index header. In other words, all -** locks are on the shm file. The WALINDEX_LOCK_OFFSET constant (which -** should be 120) is the location in the shm file for the first locking -** byte. +** Release all of the apPage[] pages for a cursor. */ -#define WAL_WRITE_LOCK 0 -#define WAL_ALL_BUT_WRITE 1 -#define WAL_CKPT_LOCK 1 -#define WAL_RECOVER_LOCK 2 -#define WAL_READ_LOCK(I) (3+(I)) -#define WAL_NREADER (SQLITE_SHM_NLOCK-3) - - -/* Object declarations */ -typedef struct WalIndexHdr WalIndexHdr; -typedef struct WalIterator WalIterator; -typedef struct WalCkptInfo WalCkptInfo; - +static void btreeReleaseAllCursorPages(BtCursor *pCur){ + int i; + if( pCur->iPage>=0 ){ + for(i=0; iiPage; i++){ + releasePageNotNull(pCur->apPage[i]); + } + releasePageNotNull(pCur->pPage); + pCur->iPage = -1; + } +} /* -** The following object holds a copy of the wal-index header content. -** -** The actual header in the wal-index consists of two copies of this -** object followed by one instance of the WalCkptInfo object. -** For all versions of SQLite through 3.10.0 and probably beyond, -** the locking bytes (WalCkptInfo.aLock) start at offset 120 and -** the total header size is 136 bytes. +** The cursor passed as the only argument must point to a valid entry +** when this function is called (i.e. have eState==CURSOR_VALID). This +** function saves the current cursor key in variables pCur->nKey and +** pCur->pKey. SQLITE_OK is returned if successful or an SQLite error +** code otherwise. ** -** The szPage value can be any power of 2 between 512 and 32768, inclusive. -** Or it can be 1 to represent a 65536-byte page. The latter case was -** added in 3.7.1 when support for 64K pages was added. +** If the cursor is open on an intkey table, then the integer key +** (the rowid) is stored in pCur->nKey and pCur->pKey is left set to +** NULL. If the cursor is open on a non-intkey table, then pCur->pKey is +** set to point to a malloced buffer pCur->nKey bytes in size containing +** the key. */ -struct WalIndexHdr { - u32 iVersion; /* Wal-index version */ - u32 unused; /* Unused (padding) field */ - u32 iChange; /* Counter incremented each transaction */ - u8 isInit; /* 1 when initialized */ - u8 bigEndCksum; /* True if checksums in WAL are big-endian */ - u16 szPage; /* Database page size in bytes. 1==64K */ - u32 mxFrame; /* Index of last valid frame in the WAL */ - u32 nPage; /* Size of database in pages */ - u32 aFrameCksum[2]; /* Checksum of last frame in log */ - u32 aSalt[2]; /* Two salt values copied from WAL header */ - u32 aCksum[2]; /* Checksum over all prior fields */ -}; +static int saveCursorKey(BtCursor *pCur){ + int rc = SQLITE_OK; + assert( CURSOR_VALID==pCur->eState ); + assert( 0==pCur->pKey ); + assert( cursorHoldsMutex(pCur) ); + + if( pCur->curIntKey ){ + /* Only the rowid is required for a table btree */ + pCur->nKey = sqlcipher_sqlite3BtreeIntegerKey(pCur); + }else{ + /* For an index btree, save the complete key content. It is possible + ** that the current key is corrupt. In that case, it is possible that + ** the sqlcipher_sqlite3VdbeRecordUnpack() function may overread the buffer by + ** up to the size of 1 varint plus 1 8-byte value when the cursor + ** position is restored. Hence the 17 bytes of padding allocated + ** below. */ + void *pKey; + pCur->nKey = sqlcipher_sqlite3BtreePayloadSize(pCur); + pKey = sqlcipher_sqlite3Malloc( pCur->nKey + 9 + 8 ); + if( pKey ){ + rc = sqlcipher_sqlite3BtreePayload(pCur, 0, (int)pCur->nKey, pKey); + if( rc==SQLITE_OK ){ + memset(((u8*)pKey)+pCur->nKey, 0, 9+8); + pCur->pKey = pKey; + }else{ + sqlcipher_sqlite3_free(pKey); + } + }else{ + rc = SQLITE_NOMEM_BKPT; + } + } + assert( !pCur->curIntKey || !pCur->pKey ); + return rc; +} /* -** A copy of the following object occurs in the wal-index immediately -** following the second copy of the WalIndexHdr. This object stores -** information used by checkpoint. -** -** nBackfill is the number of frames in the WAL that have been written -** back into the database. (We call the act of moving content from WAL to -** database "backfilling".) The nBackfill number is never greater than -** WalIndexHdr.mxFrame. nBackfill can only be increased by threads -** holding the WAL_CKPT_LOCK lock (which includes a recovery thread). -** However, a WAL_WRITE_LOCK thread can move the value of nBackfill from -** mxFrame back to zero when the WAL is reset. -** -** nBackfillAttempted is the largest value of nBackfill that a checkpoint -** has attempted to achieve. Normally nBackfill==nBackfillAtempted, however -** the nBackfillAttempted is set before any backfilling is done and the -** nBackfill is only set after all backfilling completes. So if a checkpoint -** crashes, nBackfillAttempted might be larger than nBackfill. The -** WalIndexHdr.mxFrame must never be less than nBackfillAttempted. -** -** The aLock[] field is a set of bytes used for locking. These bytes should -** never be read or written. -** -** There is one entry in aReadMark[] for each reader lock. If a reader -** holds read-lock K, then the value in aReadMark[K] is no greater than -** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff) -** for any aReadMark[] means that entry is unused. aReadMark[0] is -** a special case; its value is never used and it exists as a place-holder -** to avoid having to offset aReadMark[] indexs by one. Readers holding -** WAL_READ_LOCK(0) always ignore the entire WAL and read all content -** directly from the database. -** -** The value of aReadMark[K] may only be changed by a thread that -** is holding an exclusive lock on WAL_READ_LOCK(K). Thus, the value of -** aReadMark[K] cannot changed while there is a reader is using that mark -** since the reader will be holding a shared lock on WAL_READ_LOCK(K). -** -** The checkpointer may only transfer frames from WAL to database where -** the frame numbers are less than or equal to every aReadMark[] that is -** in use (that is, every aReadMark[j] for which there is a corresponding -** WAL_READ_LOCK(j)). New readers (usually) pick the aReadMark[] with the -** largest value and will increase an unused aReadMark[] to mxFrame if there -** is not already an aReadMark[] equal to mxFrame. The exception to the -** previous sentence is when nBackfill equals mxFrame (meaning that everything -** in the WAL has been backfilled into the database) then new readers -** will choose aReadMark[0] which has value 0 and hence such reader will -** get all their all content directly from the database file and ignore -** the WAL. -** -** Writers normally append new frames to the end of the WAL. However, -** if nBackfill equals mxFrame (meaning that all WAL content has been -** written back into the database) and if no readers are using the WAL -** (in other words, if there are no WAL_READ_LOCK(i) where i>0) then -** the writer will first "reset" the WAL back to the beginning and start -** writing new content beginning at frame 1. +** Save the current cursor position in the variables BtCursor.nKey +** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. ** -** We assume that 32-bit loads are atomic and so no locks are needed in -** order to read from any aReadMark[] entries. +** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID) +** prior to calling this routine. */ -struct WalCkptInfo { - u32 nBackfill; /* Number of WAL frames backfilled into DB */ - u32 aReadMark[WAL_NREADER]; /* Reader marks */ - u8 aLock[SQLITE_SHM_NLOCK]; /* Reserved space for locks */ - u32 nBackfillAttempted; /* WAL frames perhaps written, or maybe not */ - u32 notUsed0; /* Available for future enhancements */ -}; -#define READMARK_NOT_USED 0xffffffff +static int saveCursorPosition(BtCursor *pCur){ + int rc; + assert( CURSOR_VALID==pCur->eState || CURSOR_SKIPNEXT==pCur->eState ); + assert( 0==pCur->pKey ); + assert( cursorHoldsMutex(pCur) ); -/* A block of WALINDEX_LOCK_RESERVED bytes beginning at -** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems -** only support mandatory file-locks, we do not read or write data -** from the region of the file on which locks are applied. -*/ -#define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2+offsetof(WalCkptInfo,aLock)) -#define WALINDEX_HDR_SIZE (sizeof(WalIndexHdr)*2+sizeof(WalCkptInfo)) + if( pCur->curFlags & BTCF_Pinned ){ + return SQLITE_CONSTRAINT_PINNED; + } + if( pCur->eState==CURSOR_SKIPNEXT ){ + pCur->eState = CURSOR_VALID; + }else{ + pCur->skipNext = 0; + } + + rc = saveCursorKey(pCur); + if( rc==SQLITE_OK ){ + btreeReleaseAllCursorPages(pCur); + pCur->eState = CURSOR_REQUIRESEEK; + } -/* Size of header before each frame in wal */ -#define WAL_FRAME_HDRSIZE 24 + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl|BTCF_AtLast); + return rc; +} -/* Size of write ahead log header, including checksum. */ -#define WAL_HDRSIZE 32 +/* Forward reference */ +static int SQLITE_NOINLINE saveCursorsOnList(BtCursor*,Pgno,BtCursor*); -/* WAL magic value. Either this value, or the same value with the least -** significant bit also set (WAL_MAGIC | 0x00000001) is stored in 32-bit -** big-endian format in the first 4 bytes of a WAL file. +/* +** Save the positions of all cursors (except pExcept) that are open on +** the table with root-page iRoot. "Saving the cursor position" means that +** the location in the btree is remembered in such a way that it can be +** moved back to the same spot after the btree has been modified. This +** routine is called just before cursor pExcept is used to modify the +** table, for example in BtreeDelete() or BtreeInsert(). ** -** If the LSB is set, then the checksums for each frame within the WAL -** file are calculated by treating all data as an array of 32-bit -** big-endian words. Otherwise, they are calculated by interpreting -** all data as 32-bit little-endian words. +** If there are two or more cursors on the same btree, then all such +** cursors should have their BTCF_Multiple flag set. The btreeCursor() +** routine enforces that rule. This routine only needs to be called in +** the uncommon case when pExpect has the BTCF_Multiple flag set. +** +** If pExpect!=NULL and if no other cursors are found on the same root-page, +** then the BTCF_Multiple flag on pExpect is cleared, to avoid another +** pointless call to this routine. +** +** Implementation note: This routine merely checks to see if any cursors +** need to be saved. It calls out to saveCursorsOnList() in the (unusual) +** event that cursors are in need to being saved. */ -#define WAL_MAGIC 0x377f0682 +static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ + BtCursor *p; + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + assert( pExcept==0 || pExcept->pBt==pBt ); + for(p=pBt->pCursor; p; p=p->pNext){ + if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ) break; + } + if( p ) return saveCursorsOnList(p, iRoot, pExcept); + if( pExcept ) pExcept->curFlags &= ~BTCF_Multiple; + return SQLITE_OK; +} + +/* This helper routine to saveAllCursors does the actual work of saving +** the cursors if and when a cursor is found that actually requires saving. +** The common case is that no cursors need to be saved, so this routine is +** broken out from its caller to avoid unnecessary stack pointer movement. +*/ +static int SQLITE_NOINLINE saveCursorsOnList( + BtCursor *p, /* The first cursor that needs saving */ + Pgno iRoot, /* Only save cursor with this iRoot. Save all if zero */ + BtCursor *pExcept /* Do not save this cursor */ +){ + do{ + if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ){ + if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){ + int rc = saveCursorPosition(p); + if( SQLITE_OK!=rc ){ + return rc; + } + }else{ + testcase( p->iPage>=0 ); + btreeReleaseAllCursorPages(p); + } + } + p = p->pNext; + }while( p ); + return SQLITE_OK; +} /* -** Return the offset of frame iFrame in the write-ahead log file, -** assuming a database page size of szPage bytes. The offset returned -** is to the start of the write-ahead log frame-header. +** Clear the current cursor position. */ -#define walFrameOffset(iFrame, szPage) ( \ - WAL_HDRSIZE + ((iFrame)-1)*(i64)((szPage)+WAL_FRAME_HDRSIZE) \ -) +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeClearCursor(BtCursor *pCur){ + assert( cursorHoldsMutex(pCur) ); + sqlcipher_sqlite3_free(pCur->pKey); + pCur->pKey = 0; + pCur->eState = CURSOR_INVALID; +} /* -** An open write-ahead log file is represented by an instance of the -** following object. +** In this version of BtreeMoveto, pKey is a packed index record +** such as is generated by the OP_MakeRecord opcode. Unpack the +** record and then call sqlcipher_sqlite3BtreeIndexMoveto() to do the work. */ -struct Wal { - sqlcipher_sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ - sqlcipher_sqlite3_file *pDbFd; /* File handle for the database file */ - sqlcipher_sqlite3_file *pWalFd; /* File handle for WAL file */ - u32 iCallback; /* Value to pass to log callback (or 0) */ - i64 mxWalSize; /* Truncate WAL to this size upon reset */ - int nWiData; /* Size of array apWiData */ - int szFirstBlock; /* Size of first block written to WAL file */ - volatile u32 **apWiData; /* Pointer to wal-index content in memory */ - u32 szPage; /* Database page size */ - i16 readLock; /* Which read lock is being held. -1 for none */ - u8 syncFlags; /* Flags to use to sync header writes */ - u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ - u8 writeLock; /* True if in a write transaction */ - u8 ckptLock; /* True if holding a checkpoint lock */ - u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */ - u8 truncateOnCommit; /* True to truncate WAL file on commit */ - u8 syncHeader; /* Fsync the WAL header if true */ - u8 padToSectorBoundary; /* Pad transactions out to the next sector */ - u8 bShmUnreliable; /* SHM content is read-only and unreliable */ - WalIndexHdr hdr; /* Wal-index header for current transaction */ - u32 minFrame; /* Ignore wal frames before this one */ - u32 iReCksum; /* On commit, recalculate checksums from here */ - const char *zWalName; /* Name of WAL file */ - u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ -#ifdef SQLITE_DEBUG - u8 lockError; /* True if a locking error has occurred */ -#endif -#ifdef SQLITE_ENABLE_SNAPSHOT - WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */ -#endif -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT - sqlcipher_sqlite3 *db; -#endif -}; +static int btreeMoveto( + BtCursor *pCur, /* Cursor open on the btree to be searched */ + const void *pKey, /* Packed key if the btree is an index */ + i64 nKey, /* Integer key for tables. Size of pKey for indices */ + int bias, /* Bias search to the high end */ + int *pRes /* Write search results here */ +){ + int rc; /* Status code */ + UnpackedRecord *pIdxKey; /* Unpacked index key */ + + if( pKey ){ + KeyInfo *pKeyInfo = pCur->pKeyInfo; + assert( nKey==(i64)(int)nKey ); + pIdxKey = sqlcipher_sqlite3VdbeAllocUnpackedRecord(pKeyInfo); + if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT; + sqlcipher_sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey); + if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + rc = sqlcipher_sqlite3BtreeIndexMoveto(pCur, pIdxKey, pRes); + } + sqlcipher_sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey); + }else{ + pIdxKey = 0; + rc = sqlcipher_sqlite3BtreeTableMoveto(pCur, nKey, bias, pRes); + } + return rc; +} /* -** Candidate values for Wal.exclusiveMode. +** Restore the cursor to the position it was in (or as close to as possible) +** when saveCursorPosition() was called. Note that this call deletes the +** saved position info stored by saveCursorPosition(), so there can be +** at most one effective restoreCursorPosition() call after each +** saveCursorPosition(). */ -#define WAL_NORMAL_MODE 0 -#define WAL_EXCLUSIVE_MODE 1 -#define WAL_HEAPMEMORY_MODE 2 +static int btreeRestoreCursorPosition(BtCursor *pCur){ + int rc; + int skipNext = 0; + assert( cursorOwnsBtShared(pCur) ); + assert( pCur->eState>=CURSOR_REQUIRESEEK ); + if( pCur->eState==CURSOR_FAULT ){ + return pCur->skipNext; + } + pCur->eState = CURSOR_INVALID; + if( sqlcipher_sqlite3FaultSim(410) ){ + rc = SQLITE_IOERR; + }else{ + rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext); + } + if( rc==SQLITE_OK ){ + sqlcipher_sqlite3_free(pCur->pKey); + pCur->pKey = 0; + assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); + if( skipNext ) pCur->skipNext = skipNext; + if( pCur->skipNext && pCur->eState==CURSOR_VALID ){ + pCur->eState = CURSOR_SKIPNEXT; + } + } + return rc; +} + +#define restoreCursorPosition(p) \ + (p->eState>=CURSOR_REQUIRESEEK ? \ + btreeRestoreCursorPosition(p) : \ + SQLITE_OK) /* -** Possible values for WAL.readOnly +** Determine whether or not a cursor has moved from the position where +** it was last placed, or has been invalidated for any other reason. +** Cursors can move when the row they are pointing at is deleted out +** from under them, for example. Cursor might also move if a btree +** is rebalanced. +** +** Calling this routine with a NULL cursor pointer returns false. +** +** Use the separate sqlcipher_sqlite3BtreeCursorRestore() routine to restore a cursor +** back to where it ought to be if this routine returns true. */ -#define WAL_RDWR 0 /* Normal read/write connection */ -#define WAL_RDONLY 1 /* The WAL file is readonly */ -#define WAL_SHM_RDONLY 2 /* The SHM file is readonly */ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorHasMoved(BtCursor *pCur){ + assert( EIGHT_BYTE_ALIGNMENT(pCur) + || pCur==sqlcipher_sqlite3BtreeFakeValidCursor() ); + assert( offsetof(BtCursor, eState)==0 ); + assert( sizeof(pCur->eState)==1 ); + return CURSOR_VALID != *(u8*)pCur; +} /* -** Each page of the wal-index mapping contains a hash-table made up of -** an array of HASHTABLE_NSLOT elements of the following type. +** Return a pointer to a fake BtCursor object that will always answer +** false to the sqlcipher_sqlite3BtreeCursorHasMoved() routine above. The fake +** cursor returned must not be used with any other Btree interface. */ -typedef u16 ht_slot; +SQLITE_PRIVATE BtCursor *sqlcipher_sqlite3BtreeFakeValidCursor(void){ + static u8 fakeCursor = CURSOR_VALID; + assert( offsetof(BtCursor, eState)==0 ); + return (BtCursor*)&fakeCursor; +} /* -** This structure is used to implement an iterator that loops through -** all frames in the WAL in database page order. Where two or more frames -** correspond to the same database page, the iterator visits only the -** frame most recently written to the WAL (in other words, the frame with -** the largest index). -** -** The internals of this structure are only accessed by: +** This routine restores a cursor back to its original position after it +** has been moved by some outside activity (such as a btree rebalance or +** a row having been deleted out from under the cursor). ** -** walIteratorInit() - Create a new iterator, -** walIteratorNext() - Step an iterator, -** walIteratorFree() - Free an iterator. +** On success, the *pDifferentRow parameter is false if the cursor is left +** pointing at exactly the same row. *pDifferntRow is the row the cursor +** was pointing to has been deleted, forcing the cursor to point to some +** nearby row. ** -** This functionality is used by the checkpoint code (see walCheckpoint()). +** This routine should only be called for a cursor that just returned +** TRUE from sqlcipher_sqlite3BtreeCursorHasMoved(). */ -struct WalIterator { - u32 iPrior; /* Last result returned from the iterator */ - int nSegment; /* Number of entries in aSegment[] */ - struct WalSegment { - int iNext; /* Next slot in aIndex[] not yet returned */ - ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ - u32 *aPgno; /* Array of page numbers. */ - int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ - int iZero; /* Frame number associated with aPgno[0] */ - } aSegment[1]; /* One for every 32KB page in the wal-index */ -}; +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow){ + int rc; + + assert( pCur!=0 ); + assert( pCur->eState!=CURSOR_VALID ); + rc = restoreCursorPosition(pCur); + if( rc ){ + *pDifferentRow = 1; + return rc; + } + if( pCur->eState!=CURSOR_VALID ){ + *pDifferentRow = 1; + }else{ + *pDifferentRow = 0; + } + return SQLITE_OK; +} +#ifdef SQLITE_ENABLE_CURSOR_HINTS /* -** Define the parameters of the hash tables in the wal-index file. There -** is a hash-table following every HASHTABLE_NPAGE page numbers in the -** wal-index. -** -** Changing any of these constants will alter the wal-index format and -** create incompatibilities. +** Provide hints to the cursor. The particular hint given (and the type +** and number of the varargs parameters) is determined by the eHintType +** parameter. See the definitions of the BTREE_HINT_* macros for details. */ -#define HASHTABLE_NPAGE 4096 /* Must be power of 2 */ -#define HASHTABLE_HASH_1 383 /* Should be prime */ -#define HASHTABLE_NSLOT (HASHTABLE_NPAGE*2) /* Must be a power of 2 */ +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){ + /* Used only by system that substitute their own storage engine */ +} +#endif /* -** The block of page numbers associated with the first hash-table in a -** wal-index is smaller than usual. This is so that there is a complete -** hash-table on each aligned 32KB page of the wal-index. +** Provide flag hints to the cursor. */ -#define HASHTABLE_NPAGE_ONE (HASHTABLE_NPAGE - (WALINDEX_HDR_SIZE/sizeof(u32))) +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){ + assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 ); + pCur->hints = x; +} -/* The wal-index is divided into pages of WALINDEX_PGSZ bytes each. */ -#define WALINDEX_PGSZ ( \ - sizeof(ht_slot)*HASHTABLE_NSLOT + HASHTABLE_NPAGE*sizeof(u32) \ -) +#ifndef SQLITE_OMIT_AUTOVACUUM /* -** Obtain a pointer to the iPage'th page of the wal-index. The wal-index -** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are -** numbered from zero. +** Given a page number of a regular database page, return the page +** number for the pointer-map page that contains the entry for the +** input page number. ** -** If the wal-index is currently smaller the iPage pages then the size -** of the wal-index might be increased, but only if it is safe to do -** so. It is safe to enlarge the wal-index if pWal->writeLock is true -** or pWal->exclusiveMode==WAL_HEAPMEMORY_MODE. +** Return 0 (not a valid page) for pgno==1 since there is +** no pointer map associated with page 1. The integrity_check logic +** requires that ptrmapPageno(*,1)!=1. +*/ +static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){ + int nPagesPerMapPage; + Pgno iPtrMap, ret; + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + if( pgno<2 ) return 0; + nPagesPerMapPage = (pBt->usableSize/5)+1; + iPtrMap = (pgno-2)/nPagesPerMapPage; + ret = (iPtrMap*nPagesPerMapPage) + 2; + if( ret==PENDING_BYTE_PAGE(pBt) ){ + ret++; + } + return ret; +} + +/* +** Write an entry into the pointer map. +** +** This routine updates the pointer map entry for page number 'key' +** so that it maps to type 'eType' and parent page number 'pgno'. ** -** If this call is successful, *ppPage is set to point to the wal-index -** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs, -** then an SQLite error code is returned and *ppPage is set to 0. +** If *pRC is initially non-zero (non-SQLITE_OK) then this routine is +** a no-op. If an error occurs, the appropriate error code is written +** into *pRC. */ -static SQLITE_NOINLINE int walIndexPageRealloc( - Wal *pWal, /* The WAL context */ - int iPage, /* The page we seek */ - volatile u32 **ppPage /* Write the page pointer here */ -){ - int rc = SQLITE_OK; +static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ + DbPage *pDbPage; /* The pointer map page */ + u8 *pPtrmap; /* The pointer map data */ + Pgno iPtrmap; /* The pointer map page number */ + int offset; /* Offset in pointer map page */ + int rc; /* Return code from subfunctions */ - /* Enlarge the pWal->apWiData[] array if required */ - if( pWal->nWiData<=iPage ){ - sqlcipher_sqlite3_int64 nByte = sizeof(u32*)*(iPage+1); - volatile u32 **apNew; - apNew = (volatile u32 **)sqlcipher_sqlite3Realloc((void *)pWal->apWiData, nByte); - if( !apNew ){ - *ppPage = 0; - return SQLITE_NOMEM_BKPT; - } - memset((void*)&apNew[pWal->nWiData], 0, - sizeof(u32*)*(iPage+1-pWal->nWiData)); - pWal->apWiData = apNew; - pWal->nWiData = iPage+1; + if( *pRC ) return; + + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + /* The super-journal page number must never be used as a pointer map page */ + assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); + + assert( pBt->autoVacuum ); + if( key==0 ){ + *pRC = SQLITE_CORRUPT_BKPT; + return; + } + iPtrmap = PTRMAP_PAGENO(pBt, key); + rc = sqlcipher_sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage, 0); + if( rc!=SQLITE_OK ){ + *pRC = rc; + return; + } + if( ((char*)sqlcipher_sqlite3PagerGetExtra(pDbPage))[0]!=0 ){ + /* The first byte of the extra data is the MemPage.isInit byte. + ** If that byte is set, it means this page is also being used + ** as a btree page. */ + *pRC = SQLITE_CORRUPT_BKPT; + goto ptrmap_exit; + } + offset = PTRMAP_PTROFFSET(iPtrmap, key); + if( offset<0 ){ + *pRC = SQLITE_CORRUPT_BKPT; + goto ptrmap_exit; } + assert( offset <= (int)pBt->usableSize-5 ); + pPtrmap = (u8 *)sqlcipher_sqlite3PagerGetData(pDbPage); - /* Request a pointer to the required page from the VFS */ - assert( pWal->apWiData[iPage]==0 ); - if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ - pWal->apWiData[iPage] = (u32 volatile *)sqlcipher_sqlite3MallocZero(WALINDEX_PGSZ); - if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT; - }else{ - rc = sqlcipher_sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, - pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] - ); - assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); - testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); + if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ + TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); + *pRC= rc = sqlcipher_sqlite3PagerWrite(pDbPage); if( rc==SQLITE_OK ){ - if( iPage>0 && sqlcipher_sqlite3FaultSim(600) ) rc = SQLITE_NOMEM; - }else if( (rc&0xff)==SQLITE_READONLY ){ - pWal->readOnly |= WAL_SHM_RDONLY; - if( rc==SQLITE_READONLY ){ - rc = SQLITE_OK; - } + pPtrmap[offset] = eType; + put4byte(&pPtrmap[offset+1], parent); } } - *ppPage = pWal->apWiData[iPage]; - assert( iPage==0 || *ppPage || rc!=SQLITE_OK ); - return rc; +ptrmap_exit: + sqlcipher_sqlite3PagerUnref(pDbPage); } -static int walIndexPage( - Wal *pWal, /* The WAL context */ - int iPage, /* The page we seek */ - volatile u32 **ppPage /* Write the page pointer here */ -){ - if( pWal->nWiData<=iPage || (*ppPage = pWal->apWiData[iPage])==0 ){ - return walIndexPageRealloc(pWal, iPage, ppPage); + +/* +** Read an entry from the pointer map. +** +** This routine retrieves the pointer map entry for page 'key', writing +** the type and parent page number to *pEType and *pPgno respectively. +** An error code is returned if something goes wrong, otherwise SQLITE_OK. +*/ +static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ + DbPage *pDbPage; /* The pointer map page */ + int iPtrmap; /* Pointer map page index */ + u8 *pPtrmap; /* Pointer map page data */ + int offset; /* Offset of entry in pointer map */ + int rc; + + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + + iPtrmap = PTRMAP_PAGENO(pBt, key); + rc = sqlcipher_sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage, 0); + if( rc!=0 ){ + return rc; + } + pPtrmap = (u8 *)sqlcipher_sqlite3PagerGetData(pDbPage); + + offset = PTRMAP_PTROFFSET(iPtrmap, key); + if( offset<0 ){ + sqlcipher_sqlite3PagerUnref(pDbPage); + return SQLITE_CORRUPT_BKPT; } + assert( offset <= (int)pBt->usableSize-5 ); + assert( pEType!=0 ); + *pEType = pPtrmap[offset]; + if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]); + + sqlcipher_sqlite3PagerUnref(pDbPage); + if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_PGNO(iPtrmap); return SQLITE_OK; } +#else /* if defined SQLITE_OMIT_AUTOVACUUM */ + #define ptrmapPut(w,x,y,z,rc) + #define ptrmapGet(w,x,y,z) SQLITE_OK + #define ptrmapPutOvflPtr(x, y, z, rc) +#endif + /* -** Return a pointer to the WalCkptInfo structure in the wal-index. +** Given a btree page and a cell index (0 means the first cell on +** the page, 1 means the second cell, and so forth) return a pointer +** to the cell content. +** +** findCellPastPtr() does the same except it skips past the initial +** 4-byte child pointer found on interior pages, if there is one. +** +** This routine works only for pages that do not contain overflow cells. */ -static volatile WalCkptInfo *walCkptInfo(Wal *pWal){ - assert( pWal->nWiData>0 && pWal->apWiData[0] ); - return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]); -} +#define findCell(P,I) \ + ((P)->aData + ((P)->maskPage & get2byteAligned(&(P)->aCellIdx[2*(I)]))) +#define findCellPastPtr(P,I) \ + ((P)->aDataOfst + ((P)->maskPage & get2byteAligned(&(P)->aCellIdx[2*(I)]))) + /* -** Return a pointer to the WalIndexHdr structure in the wal-index. +** This is common tail processing for btreeParseCellPtr() and +** btreeParseCellPtrIndex() for the case when the cell does not fit entirely +** on a single B-tree page. Make necessary adjustments to the CellInfo +** structure. */ -static volatile WalIndexHdr *walIndexHdr(Wal *pWal){ - assert( pWal->nWiData>0 && pWal->apWiData[0] ); - return (volatile WalIndexHdr*)pWal->apWiData[0]; +static SQLITE_NOINLINE void btreeParseCellAdjustSizeForOverflow( + MemPage *pPage, /* Page containing the cell */ + u8 *pCell, /* Pointer to the cell text. */ + CellInfo *pInfo /* Fill in this structure */ +){ + /* If the payload will not fit completely on the local page, we have + ** to decide how much to store locally and how much to spill onto + ** overflow pages. The strategy is to minimize the amount of unused + ** space on overflow pages while keeping the amount of local storage + ** in between minLocal and maxLocal. + ** + ** Warning: changing the way overflow payload is distributed in any + ** way will result in an incompatible file format. + */ + int minLocal; /* Minimum amount of payload held locally */ + int maxLocal; /* Maximum amount of payload held locally */ + int surplus; /* Overflow payload available for local storage */ + + minLocal = pPage->minLocal; + maxLocal = pPage->maxLocal; + surplus = minLocal + (pInfo->nPayload - minLocal)%(pPage->pBt->usableSize-4); + testcase( surplus==maxLocal ); + testcase( surplus==maxLocal+1 ); + if( surplus <= maxLocal ){ + pInfo->nLocal = (u16)surplus; + }else{ + pInfo->nLocal = (u16)minLocal; + } + pInfo->nSize = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell) + 4; } /* -** The argument to this macro must be of type u32. On a little-endian -** architecture, it returns the u32 value that results from interpreting -** the 4 bytes as a big-endian value. On a big-endian architecture, it -** returns the value that would be produced by interpreting the 4 bytes -** of the input value as a little-endian integer. +** Given a record with nPayload bytes of payload stored within btree +** page pPage, return the number of bytes of payload stored locally. */ -#define BYTESWAP32(x) ( \ - (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \ - + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \ -) +static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){ + int maxLocal; /* Maximum amount of payload held locally */ + maxLocal = pPage->maxLocal; + if( nPayload<=maxLocal ){ + return nPayload; + }else{ + int minLocal; /* Minimum amount of payload held locally */ + int surplus; /* Overflow payload available for local storage */ + minLocal = pPage->minLocal; + surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize-4); + return ( surplus <= maxLocal ) ? surplus : minLocal; + } +} /* -** Generate or extend an 8 byte checksum based on the data in -** array aByte[] and the initial values of aIn[0] and aIn[1] (or -** initial values of 0 and 0 if aIn==NULL). +** The following routines are implementations of the MemPage.xParseCell() +** method. ** -** The checksum is written back into aOut[] before returning. +** Parse a cell content block and fill in the CellInfo structure. ** -** nByte must be a positive multiple of 8. +** btreeParseCellPtr() => table btree leaf nodes +** btreeParseCellNoPayload() => table btree internal nodes +** btreeParseCellPtrIndex() => index btree nodes +** +** There is also a wrapper function btreeParseCell() that works for +** all MemPage types and that references the cell by index rather than +** by pointer. */ -static void walChecksumBytes( - int nativeCksum, /* True for native byte-order, false for non-native */ - u8 *a, /* Content to be checksummed */ - int nByte, /* Bytes of content in a[]. Must be a multiple of 8. */ - const u32 *aIn, /* Initial checksum value input */ - u32 *aOut /* OUT: Final checksum value output */ +static void btreeParseCellPtrNoPayload( + MemPage *pPage, /* Page containing the cell */ + u8 *pCell, /* Pointer to the cell text. */ + CellInfo *pInfo /* Fill in this structure */ ){ - u32 s1, s2; - u32 *aData = (u32 *)a; - u32 *aEnd = (u32 *)&a[nByte]; + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->leaf==0 ); + assert( pPage->childPtrSize==4 ); +#ifndef SQLITE_DEBUG + UNUSED_PARAMETER(pPage); +#endif + pInfo->nSize = 4 + getVarint(&pCell[4], (u64*)&pInfo->nKey); + pInfo->nPayload = 0; + pInfo->nLocal = 0; + pInfo->pPayload = 0; + return; +} +static void btreeParseCellPtr( + MemPage *pPage, /* Page containing the cell */ + u8 *pCell, /* Pointer to the cell text. */ + CellInfo *pInfo /* Fill in this structure */ +){ + u8 *pIter; /* For scanning through pCell */ + u32 nPayload; /* Number of bytes of cell payload */ + u64 iKey; /* Extracted Key value */ - if( aIn ){ - s1 = aIn[0]; - s2 = aIn[1]; - }else{ - s1 = s2 = 0; + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->leaf==0 || pPage->leaf==1 ); + assert( pPage->intKeyLeaf ); + assert( pPage->childPtrSize==0 ); + pIter = pCell; + + /* The next block of code is equivalent to: + ** + ** pIter += getVarint32(pIter, nPayload); + ** + ** The code is inlined to avoid a function call. + */ + nPayload = *pIter; + if( nPayload>=0x80 ){ + u8 *pEnd = &pIter[8]; + nPayload &= 0x7f; + do{ + nPayload = (nPayload<<7) | (*++pIter & 0x7f); + }while( (*pIter)>=0x80 && pIter=8 ); - assert( (nByte&0x00000007)==0 ); - assert( nByte<=65536 ); + /* The next block of code is equivalent to: + ** + ** pIter += getVarint(pIter, (u64*)&pInfo->nKey); + ** + ** The code is inlined and the loop is unrolled for performance. + ** This routine is a high-runner. + */ + iKey = *pIter; + if( iKey>=0x80 ){ + u8 x; + iKey = ((iKey&0x7f)<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x =*++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<8) | (*++pIter); + } + } + } + } + } + } + } + } + pIter++; - if( nativeCksum ){ - do { - s1 += *aData++ + s2; - s2 += *aData++ + s1; - }while( aDatanKey = *(i64*)&iKey; + pInfo->nPayload = nPayload; + pInfo->pPayload = pIter; + testcase( nPayload==pPage->maxLocal ); + testcase( nPayload==(u32)pPage->maxLocal+1 ); + if( nPayload<=pPage->maxLocal ){ + /* This is the (easy) common case where the entire payload fits + ** on the local page. No overflow is required. + */ + pInfo->nSize = nPayload + (u16)(pIter - pCell); + if( pInfo->nSize<4 ) pInfo->nSize = 4; + pInfo->nLocal = (u16)nPayload; }else{ - do { - s1 += BYTESWAP32(aData[0]) + s2; - s2 += BYTESWAP32(aData[1]) + s1; - aData += 2; - }while( aDataexclusiveMode!=WAL_HEAPMEMORY_MODE ){ - sqlcipher_sqlite3OsShmBarrier(pWal->pDbFd); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->leaf==0 || pPage->leaf==1 ); + assert( pPage->intKeyLeaf==0 ); + pIter = pCell + pPage->childPtrSize; + nPayload = *pIter; + if( nPayload>=0x80 ){ + u8 *pEnd = &pIter[8]; + nPayload &= 0x7f; + do{ + nPayload = (nPayload<<7) | (*++pIter & 0x7f); + }while( *(pIter)>=0x80 && pIternKey = nPayload; + pInfo->nPayload = nPayload; + pInfo->pPayload = pIter; + testcase( nPayload==pPage->maxLocal ); + testcase( nPayload==(u32)pPage->maxLocal+1 ); + if( nPayload<=pPage->maxLocal ){ + /* This is the (easy) common case where the entire payload fits + ** on the local page. No overflow is required. + */ + pInfo->nSize = nPayload + (u16)(pIter - pCell); + if( pInfo->nSize<4 ) pInfo->nSize = 4; + pInfo->nLocal = (u16)nPayload; + }else{ + btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo); } } +static void btreeParseCell( + MemPage *pPage, /* Page containing the cell */ + int iCell, /* The cell index. First cell is 0 */ + CellInfo *pInfo /* Fill in this structure */ +){ + pPage->xParseCell(pPage, findCell(pPage, iCell), pInfo); +} /* -** Add the SQLITE_NO_TSAN as part of the return-type of a function -** definition as a hint that the function contains constructs that -** might give false-positive TSAN warnings. +** The following routines are implementations of the MemPage.xCellSize +** method. ** -** See tag-20200519-1. +** Compute the total number of bytes that a Cell needs in the cell +** data area of the btree-page. The return number includes the cell +** data header and the local payload, but not any overflow page or +** the space used by the cell pointer. +** +** cellSizePtrNoPayload() => table internal nodes +** cellSizePtrTableLeaf() => table leaf nodes +** cellSizePtr() => all index nodes & table leaf nodes */ -#if defined(__clang__) && !defined(SQLITE_NO_TSAN) -# define SQLITE_NO_TSAN __attribute__((no_sanitize_thread)) +static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ + u8 *pIter = pCell + pPage->childPtrSize; /* For looping over bytes of pCell */ + u8 *pEnd; /* End mark for a varint */ + u32 nSize; /* Size value to return */ + +#ifdef SQLITE_DEBUG + /* The value returned by this function should always be the same as + ** the (CellInfo.nSize) value found by doing a full parse of the + ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of + ** this function verifies that this invariant is not violated. */ + CellInfo debuginfo; + pPage->xParseCell(pPage, pCell, &debuginfo); +#endif + + nSize = *pIter; + if( nSize>=0x80 ){ + pEnd = &pIter[8]; + nSize &= 0x7f; + do{ + nSize = (nSize<<7) | (*++pIter & 0x7f); + }while( *(pIter)>=0x80 && pItermaxLocal ); + testcase( nSize==(u32)pPage->maxLocal+1 ); + if( nSize<=pPage->maxLocal ){ + nSize += (u32)(pIter - pCell); + if( nSize<4 ) nSize = 4; + }else{ + int minLocal = pPage->minLocal; + nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); + testcase( nSize==pPage->maxLocal ); + testcase( nSize==(u32)pPage->maxLocal+1 ); + if( nSize>pPage->maxLocal ){ + nSize = minLocal; + } + nSize += 4 + (u16)(pIter - pCell); + } + assert( nSize==debuginfo.nSize || CORRUPT_DB ); + return (u16)nSize; +} +static u16 cellSizePtrNoPayload(MemPage *pPage, u8 *pCell){ + u8 *pIter = pCell + 4; /* For looping over bytes of pCell */ + u8 *pEnd; /* End mark for a varint */ + +#ifdef SQLITE_DEBUG + /* The value returned by this function should always be the same as + ** the (CellInfo.nSize) value found by doing a full parse of the + ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of + ** this function verifies that this invariant is not violated. */ + CellInfo debuginfo; + pPage->xParseCell(pPage, pCell, &debuginfo); #else -# define SQLITE_NO_TSAN + UNUSED_PARAMETER(pPage); #endif -/* -** Write the header information in pWal->hdr into the wal-index. -** -** The checksum on pWal->hdr is updated before it is written. -*/ -static SQLITE_NO_TSAN void walIndexWriteHdr(Wal *pWal){ - volatile WalIndexHdr *aHdr = walIndexHdr(pWal); - const int nCksum = offsetof(WalIndexHdr, aCksum); + assert( pPage->childPtrSize==4 ); + pEnd = pIter + 9; + while( (*pIter++)&0x80 && pIterwriteLock ); - pWal->hdr.isInit = 1; - pWal->hdr.iVersion = WALINDEX_MAX_VERSION; - walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); - /* Possible TSAN false-positive. See tag-20200519-1 */ - memcpy((void*)&aHdr[1], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); - walShmBarrier(pWal); - memcpy((void*)&aHdr[0], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); +#ifdef SQLITE_DEBUG + /* The value returned by this function should always be the same as + ** the (CellInfo.nSize) value found by doing a full parse of the + ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of + ** this function verifies that this invariant is not violated. */ + CellInfo debuginfo; + pPage->xParseCell(pPage, pCell, &debuginfo); +#endif + + nSize = *pIter; + if( nSize>=0x80 ){ + pEnd = &pIter[8]; + nSize &= 0x7f; + do{ + nSize = (nSize<<7) | (*++pIter & 0x7f); + }while( *(pIter)>=0x80 && pItermaxLocal ); + testcase( nSize==(u32)pPage->maxLocal+1 ); + if( nSize<=pPage->maxLocal ){ + nSize += (u32)(pIter - pCell); + if( nSize<4 ) nSize = 4; + }else{ + int minLocal = pPage->minLocal; + nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); + testcase( nSize==pPage->maxLocal ); + testcase( nSize==(u32)pPage->maxLocal+1 ); + if( nSize>pPage->maxLocal ){ + nSize = minLocal; + } + nSize += 4 + (u16)(pIter - pCell); + } + assert( nSize==debuginfo.nSize || CORRUPT_DB ); + return (u16)nSize; } -/* -** This function encodes a single frame header and writes it to a buffer -** supplied by the caller. A frame-header is made up of a series of -** 4-byte big-endian integers, as follows: -** -** 0: Page number. -** 4: For commit records, the size of the database image in pages -** after the commit. For all other records, zero. -** 8: Salt-1 (copied from the wal-header) -** 12: Salt-2 (copied from the wal-header) -** 16: Checksum-1. -** 20: Checksum-2. -*/ -static void walEncodeFrame( - Wal *pWal, /* The write-ahead log */ - u32 iPage, /* Database page number for frame */ - u32 nTruncate, /* New db size (or 0 for non-commit frames) */ - u8 *aData, /* Pointer to page data */ - u8 *aFrame /* OUT: Write encoded frame here */ -){ - int nativeCksum; /* True for native byte-order checksums */ - u32 *aCksum = pWal->hdr.aFrameCksum; - assert( WAL_FRAME_HDRSIZE==24 ); - sqlcipher_sqlite3Put4byte(&aFrame[0], iPage); - sqlcipher_sqlite3Put4byte(&aFrame[4], nTruncate); - if( pWal->iReCksum==0 ){ - memcpy(&aFrame[8], pWal->hdr.aSalt, 8); - nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN); - walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); - walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); +#ifdef SQLITE_DEBUG +/* This variation on cellSizePtr() is used inside of assert() statements +** only. */ +static u16 cellSize(MemPage *pPage, int iCell){ + return pPage->xCellSize(pPage, findCell(pPage, iCell)); +} +#endif - sqlcipher_sqlite3Put4byte(&aFrame[16], aCksum[0]); - sqlcipher_sqlite3Put4byte(&aFrame[20], aCksum[1]); - }else{ - memset(&aFrame[8], 0, 16); +#ifndef SQLITE_OMIT_AUTOVACUUM +/* +** The cell pCell is currently part of page pSrc but will ultimately be part +** of pPage. (pSrc and pPage are often the same.) If pCell contains a +** pointer to an overflow page, insert an entry into the pointer-map for +** the overflow page that will be valid after pCell has been moved to pPage. +*/ +static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){ + CellInfo info; + if( *pRC ) return; + assert( pCell!=0 ); + pPage->xParseCell(pPage, pCell, &info); + if( info.nLocalaDataEnd, pCell, pCell+info.nLocal) ){ + testcase( pSrc!=pPage ); + *pRC = SQLITE_CORRUPT_BKPT; + return; + } + ovfl = get4byte(&pCell[info.nSize-4]); + ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC); } } +#endif + /* -** Check to see if the frame with header in aFrame[] and content -** in aData[] is valid. If it is a valid frame, fill *piPage and -** *pnTruncate and return true. Return if the frame is not valid. +** Defragment the page given. This routine reorganizes cells within the +** page so that there are no free-blocks on the free-block list. +** +** Parameter nMaxFrag is the maximum amount of fragmented space that may be +** present in the page after this routine returns. +** +** EVIDENCE-OF: R-44582-60138 SQLite may from time to time reorganize a +** b-tree page so that there are no freeblocks or fragment bytes, all +** unused bytes are contained in the unallocated space region, and all +** cells are packed tightly at the end of the page. */ -static int walDecodeFrame( - Wal *pWal, /* The write-ahead log */ - u32 *piPage, /* OUT: Database page number for frame */ - u32 *pnTruncate, /* OUT: New db size (or 0 if not commit) */ - u8 *aData, /* Pointer to page data (for checksum) */ - u8 *aFrame /* Frame data */ -){ - int nativeCksum; /* True for native byte-order checksums */ - u32 *aCksum = pWal->hdr.aFrameCksum; - u32 pgno; /* Page number of the frame */ - assert( WAL_FRAME_HDRSIZE==24 ); +static int defragmentPage(MemPage *pPage, int nMaxFrag){ + int i; /* Loop counter */ + int pc; /* Address of the i-th cell */ + int hdr; /* Offset to the page header */ + int size; /* Size of a cell */ + int usableSize; /* Number of usable bytes on a page */ + int cellOffset; /* Offset to the cell pointer array */ + int cbrk; /* Offset to the cell content area */ + int nCell; /* Number of cells on the page */ + unsigned char *data; /* The page data */ + unsigned char *temp; /* Temp area for cell content */ + unsigned char *src; /* Source of content */ + int iCellFirst; /* First allowable cell index */ + int iCellLast; /* Last possible cell index */ + int iCellStart; /* First cell offset in input */ - /* A frame is only valid if the salt values in the frame-header - ** match the salt values in the wal-header. - */ - if( memcmp(&pWal->hdr.aSalt, &aFrame[8], 8)!=0 ){ - return 0; - } + assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); + assert( pPage->pBt!=0 ); + assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); + assert( pPage->nOverflow==0 ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + temp = 0; + src = data = pPage->aData; + hdr = pPage->hdrOffset; + cellOffset = pPage->cellOffset; + nCell = pPage->nCell; + assert( nCell==get2byte(&data[hdr+3]) || CORRUPT_DB ); + iCellFirst = cellOffset + 2*nCell; + usableSize = pPage->pBt->usableSize; - /* A frame is only valid if the page number is creater than zero. - */ - pgno = sqlcipher_sqlite3Get4byte(&aFrame[0]); - if( pgno==0 ){ - return 0; + /* This block handles pages with two or fewer free blocks and nMaxFrag + ** or fewer fragmented bytes. In this case it is faster to move the + ** two (or one) blocks of cells using memmove() and add the required + ** offsets to each pointer in the cell-pointer array than it is to + ** reconstruct the entire page. */ + if( (int)data[hdr+7]<=nMaxFrag ){ + int iFree = get2byte(&data[hdr+1]); + if( iFree>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage); + if( iFree ){ + int iFree2 = get2byte(&data[iFree]); + if( iFree2>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage); + if( 0==iFree2 || (data[iFree2]==0 && data[iFree2+1]==0) ){ + u8 *pEnd = &data[cellOffset + nCell*2]; + u8 *pAddr; + int sz2 = 0; + int sz = get2byte(&data[iFree+2]); + int top = get2byte(&data[hdr+5]); + if( top>=iFree ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + if( iFree2 ){ + if( iFree+sz>iFree2 ) return SQLITE_CORRUPT_PAGE(pPage); + sz2 = get2byte(&data[iFree2+2]); + if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage); + memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz)); + sz += sz2; + }else if( iFree+sz>usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + + cbrk = top+sz; + assert( cbrk+(iFree-top) <= usableSize ); + memmove(&data[cbrk], &data[top], iFree-top); + for(pAddr=&data[cellOffset]; pAddrhdr.bigEndCksum==SQLITE_BIGENDIAN); - walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); - walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); - if( aCksum[0]!=sqlcipher_sqlite3Get4byte(&aFrame[16]) - || aCksum[1]!=sqlcipher_sqlite3Get4byte(&aFrame[20]) - ){ - /* Checksum failed. */ - return 0; + cbrk = usableSize; + iCellLast = usableSize - 4; + iCellStart = get2byte(&data[hdr+5]); + for(i=0; iiCellLast ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + assert( pc>=iCellStart && pc<=iCellLast ); + size = pPage->xCellSize(pPage, &src[pc]); + cbrk -= size; + if( cbrkusableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + assert( cbrk+size<=usableSize && cbrk>=iCellStart ); + testcase( cbrk+size==usableSize ); + testcase( pc+size==usableSize ); + put2byte(pAddr, cbrk); + if( temp==0 ){ + if( cbrk==pc ) continue; + temp = sqlcipher_sqlite3PagerTempSpace(pPage->pBt->pPager); + memcpy(&temp[iCellStart], &data[iCellStart], usableSize - iCellStart); + src = temp; + } + memcpy(&data[cbrk], &src[pc], size); } + data[hdr+7] = 0; - /* If we reach this point, the frame is valid. Return the page number - ** and the new database size. - */ - *piPage = pgno; - *pnTruncate = sqlcipher_sqlite3Get4byte(&aFrame[4]); - return 1; -} - - -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -/* -** Names of locks. This routine is used to provide debugging output and is not -** a part of an ordinary build. -*/ -static const char *walLockName(int lockIdx){ - if( lockIdx==WAL_WRITE_LOCK ){ - return "WRITE-LOCK"; - }else if( lockIdx==WAL_CKPT_LOCK ){ - return "CKPT-LOCK"; - }else if( lockIdx==WAL_RECOVER_LOCK ){ - return "RECOVER-LOCK"; - }else{ - static char zName[15]; - sqlcipher_sqlite3_snprintf(sizeof(zName), zName, "READ-LOCK[%d]", - lockIdx-WAL_READ_LOCK(0)); - return zName; + defragment_out: + assert( pPage->nFree>=0 ); + if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){ + return SQLITE_CORRUPT_PAGE(pPage); } + assert( cbrk>=iCellFirst ); + put2byte(&data[hdr+5], cbrk); + data[hdr+1] = 0; + data[hdr+2] = 0; + memset(&data[iCellFirst], 0, cbrk-iCellFirst); + assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); + return SQLITE_OK; } -#endif /*defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */ - /* -** Set or release locks on the WAL. Locks are either shared or exclusive. -** A lock cannot be moved directly between shared and exclusive - it must go -** through the unlocked state first. +** Search the free-list on page pPg for space to store a cell nByte bytes in +** size. If one can be found, return a pointer to the space and remove it +** from the free-list. ** -** In locking_mode=EXCLUSIVE, all of these routines become no-ops. -*/ -static int walLockShared(Wal *pWal, int lockIdx){ - int rc; - if( pWal->exclusiveMode ) return SQLITE_OK; - rc = sqlcipher_sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, - SQLITE_SHM_LOCK | SQLITE_SHM_SHARED); - WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal, - walLockName(lockIdx), rc ? "failed" : "ok")); - VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) - return rc; -} -static void walUnlockShared(Wal *pWal, int lockIdx){ - if( pWal->exclusiveMode ) return; - (void)sqlcipher_sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, - SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED); - WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx))); -} -static int walLockExclusive(Wal *pWal, int lockIdx, int n){ - int rc; - if( pWal->exclusiveMode ) return SQLITE_OK; - rc = sqlcipher_sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, - SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); - WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, - walLockName(lockIdx), n, rc ? "failed" : "ok")); - VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) - return rc; -} -static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ - if( pWal->exclusiveMode ) return; - (void)sqlcipher_sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, - SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE); - WALTRACE(("WAL%p: release EXCLUSIVE-%s cnt=%d\n", pWal, - walLockName(lockIdx), n)); -} - -/* -** Compute a hash on a page number. The resulting hash value must land -** between 0 and (HASHTABLE_NSLOT-1). The walHashNext() function advances -** the hash to the next value in the event of a collision. -*/ -static int walHash(u32 iPage){ - assert( iPage>0 ); - assert( (HASHTABLE_NSLOT & (HASHTABLE_NSLOT-1))==0 ); - return (iPage*HASHTABLE_HASH_1) & (HASHTABLE_NSLOT-1); -} -static int walNextHash(int iPriorHash){ - return (iPriorHash+1)&(HASHTABLE_NSLOT-1); -} - -/* -** An instance of the WalHashLoc object is used to describe the location -** of a page hash table in the wal-index. This becomes the return value -** from walHashGet(). -*/ -typedef struct WalHashLoc WalHashLoc; -struct WalHashLoc { - volatile ht_slot *aHash; /* Start of the wal-index hash table */ - volatile u32 *aPgno; /* aPgno[1] is the page of first frame indexed */ - u32 iZero; /* One less than the frame number of first indexed*/ -}; - -/* -** Return pointers to the hash table and page number array stored on -** page iHash of the wal-index. The wal-index is broken into 32KB pages -** numbered starting from 0. +** If no suitable space can be found on the free-list, return NULL. ** -** Set output variable pLoc->aHash to point to the start of the hash table -** in the wal-index file. Set pLoc->iZero to one less than the frame -** number of the first frame indexed by this hash table. If a -** slot in the hash table is set to N, it refers to frame number -** (pLoc->iZero+N) in the log. +** This function may detect corruption within pPg. If corruption is +** detected then *pRc is set to SQLITE_CORRUPT and NULL is returned. ** -** Finally, set pLoc->aPgno so that pLoc->aPgno[1] is the page number of the -** first frame indexed by the hash table, frame (pLoc->iZero+1). +** Slots on the free list that are between 1 and 3 bytes larger than nByte +** will be ignored if adding the extra space to the fragmentation count +** causes the fragmentation count to exceed 60. */ -static int walHashGet( - Wal *pWal, /* WAL handle */ - int iHash, /* Find the iHash'th table */ - WalHashLoc *pLoc /* OUT: Hash table location */ -){ - int rc; /* Return code */ +static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ + const int hdr = pPg->hdrOffset; /* Offset to page header */ + u8 * const aData = pPg->aData; /* Page data */ + int iAddr = hdr + 1; /* Address of ptr to pc */ + u8 *pTmp = &aData[iAddr]; /* Temporary ptr into aData[] */ + int pc = get2byte(pTmp); /* Address of a free slot */ + int x; /* Excess size of the slot */ + int maxPC = pPg->pBt->usableSize - nByte; /* Max address for a usable slot */ + int size; /* Size of the free slot */ - rc = walIndexPage(pWal, iHash, &pLoc->aPgno); - assert( rc==SQLITE_OK || iHash>0 ); + assert( pc>0 ); + while( pc<=maxPC ){ + /* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each + ** freeblock form a big-endian integer which is the size of the freeblock + ** in bytes, including the 4-byte header. */ + pTmp = &aData[pc+2]; + size = get2byte(pTmp); + if( (x = size - nByte)>=0 ){ + testcase( x==4 ); + testcase( x==3 ); + if( x<4 ){ + /* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total + ** number of bytes in fragments may not exceed 60. */ + if( aData[hdr+7]>57 ) return 0; - if( rc==SQLITE_OK ){ - pLoc->aHash = (volatile ht_slot *)&pLoc->aPgno[HASHTABLE_NPAGE]; - if( iHash==0 ){ - pLoc->aPgno = &pLoc->aPgno[WALINDEX_HDR_SIZE/sizeof(u32)]; - pLoc->iZero = 0; - }else{ - pLoc->iZero = HASHTABLE_NPAGE_ONE + (iHash-1)*HASHTABLE_NPAGE; + /* Remove the slot from the free-list. Update the number of + ** fragmented bytes within the page. */ + memcpy(&aData[iAddr], &aData[pc], 2); + aData[hdr+7] += (u8)x; + testcase( pc+x>maxPC ); + return &aData[pc]; + }else if( x+pc > maxPC ){ + /* This slot extends off the end of the usable part of the page */ + *pRc = SQLITE_CORRUPT_PAGE(pPg); + return 0; + }else{ + /* The slot remains on the free-list. Reduce its size to account + ** for the portion used by the new allocation. */ + put2byte(&aData[pc+2], x); + } + return &aData[pc + x]; + } + iAddr = pc; + pTmp = &aData[pc]; + pc = get2byte(pTmp); + if( pc<=iAddr+size ){ + if( pc ){ + /* The next slot in the chain is not past the end of the current slot */ + *pRc = SQLITE_CORRUPT_PAGE(pPg); + } + return 0; } - pLoc->aPgno = &pLoc->aPgno[-1]; } - return rc; -} - -/* -** Return the number of the wal-index page that contains the hash-table -** and page-number array that contain entries corresponding to WAL frame -** iFrame. The wal-index is broken up into 32KB pages. Wal-index pages -** are numbered starting from 0. -*/ -static int walFramePage(u32 iFrame){ - int iHash = (iFrame+HASHTABLE_NPAGE-HASHTABLE_NPAGE_ONE-1) / HASHTABLE_NPAGE; - assert( (iHash==0 || iFrame>HASHTABLE_NPAGE_ONE) - && (iHash>=1 || iFrame<=HASHTABLE_NPAGE_ONE) - && (iHash<=1 || iFrame>(HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE)) - && (iHash>=2 || iFrame<=HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE) - && (iHash<=2 || iFrame>(HASHTABLE_NPAGE_ONE+2*HASHTABLE_NPAGE)) - ); - assert( iHash>=0 ); - return iHash; -} - -/* -** Return the page number associated with frame iFrame in this WAL. -*/ -static u32 walFramePgno(Wal *pWal, u32 iFrame){ - int iHash = walFramePage(iFrame); - if( iHash==0 ){ - return pWal->apWiData[0][WALINDEX_HDR_SIZE/sizeof(u32) + iFrame - 1]; + if( pc>maxPC+nByte-4 ){ + /* The free slot chain extends off the end of the page */ + *pRc = SQLITE_CORRUPT_PAGE(pPg); } - return pWal->apWiData[iHash][(iFrame-1-HASHTABLE_NPAGE_ONE)%HASHTABLE_NPAGE]; + return 0; } /* -** Remove entries from the hash table that point to WAL slots greater -** than pWal->hdr.mxFrame. -** -** This function is called whenever pWal->hdr.mxFrame is decreased due -** to a rollback or savepoint. +** Allocate nByte bytes of space from within the B-Tree page passed +** as the first argument. Write into *pIdx the index into pPage->aData[] +** of the first byte of allocated space. Return either SQLITE_OK or +** an error code (usually SQLITE_CORRUPT). ** -** At most only the hash table containing pWal->hdr.mxFrame needs to be -** updated. Any later hash tables will be automatically cleared when -** pWal->hdr.mxFrame advances to the point where those hash tables are -** actually needed. +** The caller guarantees that there is sufficient space to make the +** allocation. This routine might need to defragment in order to bring +** all the space together, however. This routine will avoid using +** the first two bytes past the cell pointer area since presumably this +** allocation is being made in order to insert a new cell, so we will +** also end up needing a new cell pointer. */ -static void walCleanupHash(Wal *pWal){ - WalHashLoc sLoc; /* Hash table location */ - int iLimit = 0; /* Zero values greater than this */ - int nByte; /* Number of bytes to zero in aPgno[] */ - int i; /* Used to iterate through aHash[] */ - int rc; /* Return code form walHashGet() */ - - assert( pWal->writeLock ); - testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 ); - testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE ); - testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE+1 ); +static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ + const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */ + u8 * const data = pPage->aData; /* Local cache of pPage->aData */ + int top; /* First byte of cell content area */ + int rc = SQLITE_OK; /* Integer return code */ + u8 *pTmp; /* Temp ptr into data[] */ + int gap; /* First byte of gap between cell pointers and cell content */ - if( pWal->hdr.mxFrame==0 ) return; + assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); + assert( pPage->pBt ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( nByte>=0 ); /* Minimum cell size is 4 */ + assert( pPage->nFree>=nByte ); + assert( pPage->nOverflow==0 ); + assert( nByte < (int)(pPage->pBt->usableSize-8) ); - /* Obtain pointers to the hash-table and page-number array containing - ** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed - ** that the page said hash-table and array reside on is already mapped.(1) - */ - assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) ); - assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] ); - rc = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); - if( NEVER(rc) ) return; /* Defense-in-depth, in case (1) above is wrong */ + assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf ); + gap = pPage->cellOffset + 2*pPage->nCell; + assert( gap<=65536 ); + /* EVIDENCE-OF: R-29356-02391 If the database uses a 65536-byte page size + ** and the reserved space is zero (the usual value for reserved space) + ** then the cell content offset of an empty page wants to be 65536. + ** However, that integer is too large to be stored in a 2-byte unsigned + ** integer, so a value of 0 is used in its place. */ + pTmp = &data[hdr+5]; + top = get2byte(pTmp); + assert( top<=(int)pPage->pBt->usableSize ); /* by btreeComputeFreeSpace() */ + if( gap>top ){ + if( top==0 && pPage->pBt->usableSize==65536 ){ + top = 65536; + }else{ + return SQLITE_CORRUPT_PAGE(pPage); + } + } - /* Zero all hash-table entries that correspond to frame numbers greater - ** than pWal->hdr.mxFrame. + /* If there is enough space between gap and top for one more cell pointer, + ** and if the freelist is not empty, then search the + ** freelist looking for a slot big enough to satisfy the request. */ - iLimit = pWal->hdr.mxFrame - sLoc.iZero; - assert( iLimit>0 ); - for(i=0; iiLimit ){ - sLoc.aHash[i] = 0; + testcase( gap+2==top ); + testcase( gap+1==top ); + testcase( gap==top ); + if( (data[hdr+2] || data[hdr+1]) && gap+2<=top ){ + u8 *pSpace = pageFindSlot(pPage, nByte, &rc); + if( pSpace ){ + int g2; + assert( pSpace+nByte<=data+pPage->pBt->usableSize ); + *pIdx = g2 = (int)(pSpace-data); + if( g2<=gap ){ + return SQLITE_CORRUPT_PAGE(pPage); + }else{ + return SQLITE_OK; + } + }else if( rc ){ + return rc; } } - /* Zero the entries in the aPgno array that correspond to frames with - ** frame numbers greater than pWal->hdr.mxFrame. + /* The request could not be fulfilled using a freelist slot. Check + ** to see if defragmentation is necessary. */ - nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit+1]); - memset((void *)&sLoc.aPgno[iLimit+1], 0, nByte); + testcase( gap+2+nByte==top ); + if( gap+2+nByte>top ){ + assert( pPage->nCell>0 || CORRUPT_DB ); + assert( pPage->nFree>=0 ); + rc = defragmentPage(pPage, MIN(4, pPage->nFree - (2+nByte))); + if( rc ) return rc; + top = get2byteNotZero(&data[hdr+5]); + assert( gap+2+nByte<=top ); + } -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT - /* Verify that the every entry in the mapping region is still reachable - ** via the hash table even after the cleanup. + + /* Allocate memory from the gap in between the cell pointer array + ** and the cell content area. The btreeComputeFreeSpace() call has already + ** validated the freelist. Given that the freelist is valid, there + ** is no way that the allocation can extend off the end of the page. + ** The assert() below verifies the previous sentence. */ - if( iLimit ){ - int j; /* Loop counter */ - int iKey; /* Hash key */ - for(j=1; j<=iLimit; j++){ - for(iKey=walHash(sLoc.aPgno[j]);sLoc.aHash[iKey];iKey=walNextHash(iKey)){ - if( sLoc.aHash[iKey]==j ) break; - } - assert( sLoc.aHash[iKey]==j ); - } - } -#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */ + top -= nByte; + put2byte(&data[hdr+5], top); + assert( top+nByte <= (int)pPage->pBt->usableSize ); + *pIdx = top; + return SQLITE_OK; } - /* -** Set an entry in the wal-index that will map database page number -** pPage into WAL frame iFrame. +** Return a section of the pPage->aData to the freelist. +** The first byte of the new free block is pPage->aData[iStart] +** and the size of the block is iSize bytes. +** +** Adjacent freeblocks are coalesced. +** +** Even though the freeblock list was checked by btreeComputeFreeSpace(), +** that routine will not detect overlap between cells or freeblocks. Nor +** does it detect cells or freeblocks that encrouch into the reserved bytes +** at the end of the page. So do additional corruption checks inside this +** routine and return SQLITE_CORRUPT if any problems are found. */ -static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ - int rc; /* Return code */ - WalHashLoc sLoc; /* Wal-index hash table location */ +static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ + u16 iPtr; /* Address of ptr to next freeblock */ + u16 iFreeBlk; /* Address of the next freeblock */ + u8 hdr; /* Page header size. 0 or 100 */ + u8 nFrag = 0; /* Reduction in fragmentation */ + u16 iOrigSize = iSize; /* Original value of iSize */ + u16 x; /* Offset to cell content area */ + u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */ + unsigned char *data = pPage->aData; /* Page content */ + u8 *pTmp; /* Temporary ptr into data[] */ - rc = walHashGet(pWal, walFramePage(iFrame), &sLoc); + assert( pPage->pBt!=0 ); + assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); + assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize ); + assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( iSize>=4 ); /* Minimum cell size is 4 */ + assert( iStart<=pPage->pBt->usableSize-4 ); - /* Assuming the wal-index file was successfully mapped, populate the - ** page number array and hash table entry. + /* The list of freeblocks must be in ascending order. Find the + ** spot on the list where iStart should be inserted. */ - if( rc==SQLITE_OK ){ - int iKey; /* Hash table key */ - int idx; /* Value to write to hash-table slot */ - int nCollide; /* Number of hash collisions */ - - idx = iFrame - sLoc.iZero; - assert( idx <= HASHTABLE_NSLOT/2 + 1 ); - - /* If this is the first entry to be added to this hash-table, zero the - ** entire hash table and aPgno[] array before proceeding. - */ - if( idx==1 ){ - int nByte = (int)((u8 *)&sLoc.aHash[HASHTABLE_NSLOT] - - (u8 *)&sLoc.aPgno[1]); - memset((void*)&sLoc.aPgno[1], 0, nByte); - } - - /* If the entry in aPgno[] is already set, then the previous writer - ** must have exited unexpectedly in the middle of a transaction (after - ** writing one or more dirty pages to the WAL to free up memory). - ** Remove the remnants of that writers uncommitted transaction from - ** the hash-table before writing any new entries. - */ - if( sLoc.aPgno[idx] ){ - walCleanupHash(pWal); - assert( !sLoc.aPgno[idx] ); + hdr = pPage->hdrOffset; + iPtr = hdr + 1; + if( data[iPtr+1]==0 && data[iPtr]==0 ){ + iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */ + }else{ + while( (iFreeBlk = get2byte(&data[iPtr]))pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */ + return SQLITE_CORRUPT_PAGE(pPage); } - sLoc.aPgno[idx] = iPage; - AtomicStore(&sLoc.aHash[iKey], (ht_slot)idx); + assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB ); -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT - /* Verify that the number of entries in the hash table exactly equals - ** the number of entries in the mapping region. + /* At this point: + ** iFreeBlk: First freeblock after iStart, or zero if none + ** iPtr: The address of a pointer to iFreeBlk + ** + ** Check to see if iFreeBlk should be coalesced onto the end of iStart. */ - { - int i; /* Loop counter */ - int nEntry = 0; /* Number of entries in the hash table */ - for(i=0; i=iFreeBlk ){ + nFrag = iFreeBlk - iEnd; + if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage); + iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]); + if( iEnd > pPage->pBt->usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + iSize = iEnd - iStart; + iFreeBlk = get2byte(&data[iFreeBlk]); } - /* Verify that the every entry in the mapping region is reachable - ** via the hash table. This turns out to be a really, really expensive - ** thing to check, so only do this occasionally - not on every - ** iteration. + /* If iPtr is another freeblock (that is, if iPtr is not the freelist + ** pointer in the page header) then check to see if iStart should be + ** coalesced onto the end of iPtr. */ - if( (idx&0x3ff)==0 ){ - int i; /* Loop counter */ - for(i=1; i<=idx; i++){ - for(iKey=walHash(sLoc.aPgno[i]); - sLoc.aHash[iKey]; - iKey=walNextHash(iKey)){ - if( sLoc.aHash[iKey]==i ) break; - } - assert( sLoc.aHash[iKey]==i ); + if( iPtr>hdr+1 ){ + int iPtrEnd = iPtr + get2byte(&data[iPtr+2]); + if( iPtrEnd+3>=iStart ){ + if( iPtrEnd>iStart ) return SQLITE_CORRUPT_PAGE(pPage); + nFrag += iStart - iPtrEnd; + iSize = iEnd - iPtr; + iStart = iPtr; } } -#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */ + if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage); + data[hdr+7] -= nFrag; } - - - return rc; + pTmp = &data[hdr+5]; + x = get2byte(pTmp); + if( iStart<=x ){ + /* The new freeblock is at the beginning of the cell content area, + ** so just extend the cell content area rather than create another + ** freelist entry */ + if( iStartpBt->btsFlags & BTS_FAST_SECURE ){ + /* Overwrite deleted information with zeros when the secure_delete + ** option is enabled */ + memset(&data[iStart], 0, iSize); + } + put2byte(&data[iStart], iFreeBlk); + put2byte(&data[iStart+2], iSize); + pPage->nFree += iOrigSize; + return SQLITE_OK; } - /* -** Recover the wal-index by reading the write-ahead log file. +** Decode the flags byte (the first byte of the header) for a page +** and initialize fields of the MemPage structure accordingly. ** -** This routine first tries to establish an exclusive lock on the -** wal-index to prevent other threads/processes from doing anything -** with the WAL or wal-index while recovery is running. The -** WAL_RECOVER_LOCK is also held so that other threads will know -** that this thread is running recovery. If unable to establish -** the necessary locks, this routine returns SQLITE_BUSY. +** Only the following combinations are supported. Anything different +** indicates a corrupt database files: +** +** PTF_ZERODATA +** PTF_ZERODATA | PTF_LEAF +** PTF_LEAFDATA | PTF_INTKEY +** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF */ -static int walIndexRecover(Wal *pWal){ - int rc; /* Return Code */ - i64 nSize; /* Size of log file */ - u32 aFrameCksum[2] = {0, 0}; - int iLock; /* Lock offset to lock for checkpoint */ - - /* Obtain an exclusive lock on all byte in the locking range not already - ** locked by the caller. The caller is guaranteed to have locked the - ** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte. - ** If successful, the same bytes that are locked here are unlocked before - ** this function returns. - */ - assert( pWal->ckptLock==1 || pWal->ckptLock==0 ); - assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); - assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); - assert( pWal->writeLock ); - iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; - rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); - if( rc ){ - return rc; - } - - WALTRACE(("WAL%p: recovery begin...\n", pWal)); - - memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); - - rc = sqlcipher_sqlite3OsFileSize(pWal->pWalFd, &nSize); - if( rc!=SQLITE_OK ){ - goto recovery_error; - } - - if( nSize>WAL_HDRSIZE ){ - u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ - u32 *aPrivate = 0; /* Heap copy of *-shm hash being populated */ - u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ - int szFrame; /* Number of bytes in buffer aFrame[] */ - u8 *aData; /* Pointer to data part of aFrame buffer */ - int szPage; /* Page size according to the log */ - u32 magic; /* Magic value read from WAL header */ - u32 version; /* Magic value read from WAL header */ - int isValid; /* True if this frame is valid */ - u32 iPg; /* Current 32KB wal-index page */ - u32 iLastFrame; /* Last frame in wal, based on nSize alone */ - - /* Read in the WAL header. */ - rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); - if( rc!=SQLITE_OK ){ - goto recovery_error; - } - - /* If the database page size is not a power of two, or is greater than - ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid - ** data. Similarly, if the 'magic' value is invalid, ignore the whole - ** WAL file. - */ - magic = sqlcipher_sqlite3Get4byte(&aBuf[0]); - szPage = sqlcipher_sqlite3Get4byte(&aBuf[8]); - if( (magic&0xFFFFFFFE)!=WAL_MAGIC - || szPage&(szPage-1) - || szPage>SQLITE_MAX_PAGE_SIZE - || szPage<512 - ){ - goto finished; - } - pWal->hdr.bigEndCksum = (u8)(magic&0x00000001); - pWal->szPage = szPage; - pWal->nCkpt = sqlcipher_sqlite3Get4byte(&aBuf[12]); - memcpy(&pWal->hdr.aSalt, &aBuf[16], 8); - - /* Verify that the WAL header checksum is correct */ - walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, - aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum - ); - if( pWal->hdr.aFrameCksum[0]!=sqlcipher_sqlite3Get4byte(&aBuf[24]) - || pWal->hdr.aFrameCksum[1]!=sqlcipher_sqlite3Get4byte(&aBuf[28]) - ){ - goto finished; - } - - /* Verify that the version number on the WAL format is one that - ** are able to understand */ - version = sqlcipher_sqlite3Get4byte(&aBuf[4]); - if( version!=WAL_MAX_VERSION ){ - rc = SQLITE_CANTOPEN_BKPT; - goto finished; - } +static int decodeFlags(MemPage *pPage, int flagByte){ + BtShared *pBt; /* A copy of pPage->pBt */ - /* Malloc a buffer to read frames into. */ - szFrame = szPage + WAL_FRAME_HDRSIZE; - aFrame = (u8 *)sqlcipher_sqlite3_malloc64(szFrame + WALINDEX_PGSZ); - if( !aFrame ){ - rc = SQLITE_NOMEM_BKPT; - goto recovery_error; + assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 ); + flagByte &= ~PTF_LEAF; + pPage->childPtrSize = 4-4*pPage->leaf; + pBt = pPage->pBt; + if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ + /* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an + ** interior table b-tree page. */ + assert( (PTF_LEAFDATA|PTF_INTKEY)==5 ); + /* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a + ** leaf table b-tree page. */ + assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 ); + pPage->intKey = 1; + if( pPage->leaf ){ + pPage->intKeyLeaf = 1; + pPage->xCellSize = cellSizePtrTableLeaf; + pPage->xParseCell = btreeParseCellPtr; + }else{ + pPage->intKeyLeaf = 0; + pPage->xCellSize = cellSizePtrNoPayload; + pPage->xParseCell = btreeParseCellPtrNoPayload; } - aData = &aFrame[WAL_FRAME_HDRSIZE]; - aPrivate = (u32*)&aData[szPage]; + pPage->maxLocal = pBt->maxLeaf; + pPage->minLocal = pBt->minLeaf; + }else if( flagByte==PTF_ZERODATA ){ + /* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an + ** interior index b-tree page. */ + assert( (PTF_ZERODATA)==2 ); + /* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a + ** leaf index b-tree page. */ + assert( (PTF_ZERODATA|PTF_LEAF)==10 ); + pPage->intKey = 0; + pPage->intKeyLeaf = 0; + pPage->xCellSize = cellSizePtr; + pPage->xParseCell = btreeParseCellPtrIndex; + pPage->maxLocal = pBt->maxLocal; + pPage->minLocal = pBt->minLocal; + }else{ + /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is + ** an error. */ + pPage->intKey = 0; + pPage->intKeyLeaf = 0; + pPage->xCellSize = cellSizePtr; + pPage->xParseCell = btreeParseCellPtrIndex; + return SQLITE_CORRUPT_PAGE(pPage); + } + pPage->max1bytePayload = pBt->max1bytePayload; + return SQLITE_OK; +} - /* Read all frames from the log file. */ - iLastFrame = (nSize - WAL_HDRSIZE) / szFrame; - for(iPg=0; iPg<=(u32)walFramePage(iLastFrame); iPg++){ - u32 *aShare; - u32 iFrame; /* Index of last frame read */ - u32 iLast = MIN(iLastFrame, HASHTABLE_NPAGE_ONE+iPg*HASHTABLE_NPAGE); - u32 iFirst = 1 + (iPg==0?0:HASHTABLE_NPAGE_ONE+(iPg-1)*HASHTABLE_NPAGE); - u32 nHdr, nHdr32; - rc = walIndexPage(pWal, iPg, (volatile u32**)&aShare); - if( rc ) break; - pWal->apWiData[iPg] = aPrivate; +/* +** Compute the amount of freespace on the page. In other words, fill +** in the pPage->nFree field. +*/ +static int btreeComputeFreeSpace(MemPage *pPage){ + int pc; /* Address of a freeblock within pPage->aData[] */ + u8 hdr; /* Offset to beginning of page header */ + u8 *data; /* Equal to pPage->aData */ + int usableSize; /* Amount of usable space on each page */ + int nFree; /* Number of unused bytes on the page */ + int top; /* First byte of the cell content area */ + int iCellFirst; /* First allowable cell or freeblock offset */ + int iCellLast; /* Last possible cell or freeblock offset */ - for(iFrame=iFirst; iFrame<=iLast; iFrame++){ - i64 iOffset = walFrameOffset(iFrame, szPage); - u32 pgno; /* Database page number for frame */ - u32 nTruncate; /* dbsize field from frame header */ + assert( pPage->pBt!=0 ); + assert( pPage->pBt->db!=0 ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->pgno==sqlcipher_sqlite3PagerPagenumber(pPage->pDbPage) ); + assert( pPage == sqlcipher_sqlite3PagerGetExtra(pPage->pDbPage) ); + assert( pPage->aData == sqlcipher_sqlite3PagerGetData(pPage->pDbPage) ); + assert( pPage->isInit==1 ); + assert( pPage->nFree<0 ); - /* Read and decode the next log frame. */ - rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); - if( rc!=SQLITE_OK ) break; - isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); - if( !isValid ) break; - rc = walIndexAppend(pWal, iFrame, pgno); - if( NEVER(rc!=SQLITE_OK) ) break; + usableSize = pPage->pBt->usableSize; + hdr = pPage->hdrOffset; + data = pPage->aData; + /* EVIDENCE-OF: R-58015-48175 The two-byte integer at offset 5 designates + ** the start of the cell content area. A zero value for this integer is + ** interpreted as 65536. */ + top = get2byteNotZero(&data[hdr+5]); + iCellFirst = hdr + 8 + pPage->childPtrSize + 2*pPage->nCell; + iCellLast = usableSize - 4; - /* If nTruncate is non-zero, this is a commit record. */ - if( nTruncate ){ - pWal->hdr.mxFrame = iFrame; - pWal->hdr.nPage = nTruncate; - pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); - testcase( szPage<=32768 ); - testcase( szPage>=65536 ); - aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; - aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; - } - } - pWal->apWiData[iPg] = aShare; - nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0); - nHdr32 = nHdr / sizeof(u32); -#ifndef SQLITE_SAFER_WALINDEX_RECOVERY - /* Memcpy() should work fine here, on all reasonable implementations. - ** Technically, memcpy() might change the destination to some - ** intermediate value before setting to the final value, and that might - ** cause a concurrent reader to malfunction. Memcpy() is allowed to - ** do that, according to the spec, but no memcpy() implementation that - ** we know of actually does that, which is why we say that memcpy() - ** is safe for this. Memcpy() is certainly a lot faster. - */ - memcpy(&aShare[nHdr32], &aPrivate[nHdr32], WALINDEX_PGSZ-nHdr); -#else - /* In the event that some platform is found for which memcpy() - ** changes the destination to some intermediate value before - ** setting the final value, this alternative copy routine is - ** provided. + /* Compute the total free space on the page + ** EVIDENCE-OF: R-23588-34450 The two-byte integer at offset 1 gives the + ** start of the first freeblock on the page, or is zero if there are no + ** freeblocks. */ + pc = get2byte(&data[hdr+1]); + nFree = data[hdr+7] + top; /* Init nFree to non-freeblock free space */ + if( pc>0 ){ + u32 next, size; + if( pchdr.aFrameCksum[0] = aFrameCksum[0]; - pWal->hdr.aFrameCksum[1] = aFrameCksum[1]; - walIndexWriteHdr(pWal); - - /* Reset the checkpoint-header. This is safe because this thread is - ** currently holding locks that exclude all other writers and - ** checkpointers. Then set the values of read-mark slots 1 through N. - */ - pInfo = walCkptInfo(pWal); - pInfo->nBackfill = 0; - pInfo->nBackfillAttempted = pWal->hdr.mxFrame; - pInfo->aReadMark[0] = 0; - for(i=1; ihdr.mxFrame ){ - pInfo->aReadMark[i] = pWal->hdr.mxFrame; - }else{ - pInfo->aReadMark[i] = READMARK_NOT_USED; - } - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); - }else if( rc!=SQLITE_BUSY ){ - goto recovery_error; + while( 1 ){ + if( pc>iCellLast ){ + /* Freeblock off the end of the page */ + return SQLITE_CORRUPT_PAGE(pPage); } + next = get2byte(&data[pc]); + size = get2byte(&data[pc+2]); + nFree = nFree + size; + if( next<=pc+size+3 ) break; + pc = next; } - - /* If more than one frame was recovered from the log file, report an - ** event via sqlcipher_sqlite3_log(). This is to help with identifying performance - ** problems caused by applications routinely shutting down without - ** checkpointing the log file. - */ - if( pWal->hdr.nPage ){ - sqlcipher_sqlite3_log(SQLITE_NOTICE_RECOVER_WAL, - "recovered %d frames from WAL file %s", - pWal->hdr.mxFrame, pWal->zWalName - ); + if( next>0 ){ + /* Freeblock not in ascending order */ + return SQLITE_CORRUPT_PAGE(pPage); + } + if( pc+size>(unsigned int)usableSize ){ + /* Last freeblock extends past page end */ + return SQLITE_CORRUPT_PAGE(pPage); } } -recovery_error: - WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok")); - walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); - return rc; + /* At this point, nFree contains the sum of the offset to the start + ** of the cell-content area plus the number of free bytes within + ** the cell-content area. If this is greater than the usable-size + ** of the page, then the page must be corrupted. This check also + ** serves to verify that the offset to the start of the cell-content + ** area, according to the page header, lies within the page. + */ + if( nFree>usableSize || nFreenFree = (u16)(nFree - iCellFirst); + return SQLITE_OK; } /* -** Close an open wal-index. +** Do additional sanity check after btreeInitPage() if +** PRAGMA cell_size_check=ON */ -static void walIndexClose(Wal *pWal, int isDelete){ - if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE || pWal->bShmUnreliable ){ - int i; - for(i=0; inWiData; i++){ - sqlcipher_sqlite3_free((void *)pWal->apWiData[i]); - pWal->apWiData[i] = 0; +static SQLITE_NOINLINE int btreeCellSizeCheck(MemPage *pPage){ + int iCellFirst; /* First allowable cell or freeblock offset */ + int iCellLast; /* Last possible cell or freeblock offset */ + int i; /* Index into the cell pointer array */ + int sz; /* Size of a cell */ + int pc; /* Address of a freeblock within pPage->aData[] */ + u8 *data; /* Equal to pPage->aData */ + int usableSize; /* Maximum usable space on the page */ + int cellOffset; /* Start of cell content area */ + + iCellFirst = pPage->cellOffset + 2*pPage->nCell; + usableSize = pPage->pBt->usableSize; + iCellLast = usableSize - 4; + data = pPage->aData; + cellOffset = pPage->cellOffset; + if( !pPage->leaf ) iCellLast--; + for(i=0; inCell; i++){ + pc = get2byteAligned(&data[cellOffset+i*2]); + testcase( pc==iCellFirst ); + testcase( pc==iCellLast ); + if( pciCellLast ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + sz = pPage->xCellSize(pPage, &data[pc]); + testcase( pc+sz==usableSize ); + if( pc+sz>usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); } } - if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){ - sqlcipher_sqlite3OsShmUnmap(pWal->pDbFd, isDelete); - } + return SQLITE_OK; } /* -** Open a connection to the WAL file zWalName. The database file must -** already be opened on connection pDbFd. The buffer that zWalName points -** to must remain valid for the lifetime of the returned Wal* handle. -** -** A SHARED lock should be held on the database file when this function -** is called. The purpose of this SHARED lock is to prevent any other -** client from unlinking the WAL or wal-index file. If another process -** were to do this just after this client opened one of these files, the -** system would be badly broken. +** Initialize the auxiliary information for a disk block. ** -** If the log file is successfully opened, SQLITE_OK is returned and -** *ppWal is set to point to a new WAL handle. If an error occurs, -** an SQLite error code is returned and *ppWal is left unmodified. +** Return SQLITE_OK on success. If we see that the page does +** not contain a well-formed database page, then return +** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not +** guarantee that the page is well-formed. It only shows that +** we failed to detect any corruption. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalOpen( - sqlcipher_sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ - sqlcipher_sqlite3_file *pDbFd, /* The open database file */ - const char *zWalName, /* Name of the WAL file */ - int bNoShm, /* True to run in heap-memory mode */ - i64 mxWalSize, /* Truncate WAL to this size on reset */ - Wal **ppWal /* OUT: Allocated Wal handle */ -){ - int rc; /* Return Code */ - Wal *pRet; /* Object to allocate and return */ - int flags; /* Flags passed to OsOpen() */ - - assert( zWalName && zWalName[0] ); - assert( pDbFd ); - - /* In the amalgamation, the os_unix.c and os_win.c source files come before - ** this source file. Verify that the #defines of the locking byte offsets - ** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value. - ** For that matter, if the lock offset ever changes from its initial design - ** value of 120, we need to know that so there is an assert() to check it. - */ - assert( 120==WALINDEX_LOCK_OFFSET ); - assert( 136==WALINDEX_HDR_SIZE ); -#ifdef WIN_SHM_BASE - assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET ); -#endif -#ifdef UNIX_SHM_BASE - assert( UNIX_SHM_BASE==WALINDEX_LOCK_OFFSET ); -#endif +static int btreeInitPage(MemPage *pPage){ + u8 *data; /* Equal to pPage->aData */ + BtShared *pBt; /* The main btree structure */ + assert( pPage->pBt!=0 ); + assert( pPage->pBt->db!=0 ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->pgno==sqlcipher_sqlite3PagerPagenumber(pPage->pDbPage) ); + assert( pPage == sqlcipher_sqlite3PagerGetExtra(pPage->pDbPage) ); + assert( pPage->aData == sqlcipher_sqlite3PagerGetData(pPage->pDbPage) ); + assert( pPage->isInit==0 ); - /* Allocate an instance of struct Wal to return. */ - *ppWal = 0; - pRet = (Wal*)sqlcipher_sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile); - if( !pRet ){ - return SQLITE_NOMEM_BKPT; + pBt = pPage->pBt; + data = pPage->aData + pPage->hdrOffset; + /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating + ** the b-tree page type. */ + if( decodeFlags(pPage, data[0]) ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); + pPage->maskPage = (u16)(pBt->pageSize - 1); + pPage->nOverflow = 0; + pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize; + pPage->aCellIdx = data + pPage->childPtrSize + 8; + pPage->aDataEnd = pPage->aData + pBt->pageSize; + pPage->aDataOfst = pPage->aData + pPage->childPtrSize; + /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the + ** number of cells on the page. */ + pPage->nCell = get2byte(&data[3]); + if( pPage->nCell>MX_CELL(pBt) ){ + /* To many cells for a single page. The page must be corrupt */ + return SQLITE_CORRUPT_PAGE(pPage); + } + testcase( pPage->nCell==MX_CELL(pBt) ); + /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only + ** possible for a root page of a table that contains no rows) then the + ** offset to the cell content area will equal the page size minus the + ** bytes of reserved space. */ + assert( pPage->nCell>0 + || get2byteNotZero(&data[5])==(int)pBt->usableSize + || CORRUPT_DB ); + pPage->nFree = -1; /* Indicate that this value is yet uncomputed */ + pPage->isInit = 1; + if( pBt->db->flags & SQLITE_CellSizeCk ){ + return btreeCellSizeCheck(pPage); } + return SQLITE_OK; +} - pRet->pVfs = pVfs; - pRet->pWalFd = (sqlcipher_sqlite3_file *)&pRet[1]; - pRet->pDbFd = pDbFd; - pRet->readLock = -1; - pRet->mxWalSize = mxWalSize; - pRet->zWalName = zWalName; - pRet->syncHeader = 1; - pRet->padToSectorBoundary = 1; - pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE); +/* +** Set up a raw page so that it looks like a database page holding +** no entries. +*/ +static void zeroPage(MemPage *pPage, int flags){ + unsigned char *data = pPage->aData; + BtShared *pBt = pPage->pBt; + u8 hdr = pPage->hdrOffset; + u16 first; - /* Open file handle on the write-ahead log file. */ - flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); - rc = sqlcipher_sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); - if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ - pRet->readOnly = WAL_RDONLY; + assert( sqlcipher_sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB ); + assert( sqlcipher_sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); + assert( sqlcipher_sqlite3PagerGetData(pPage->pDbPage) == data ); + assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + if( pBt->btsFlags & BTS_FAST_SECURE ){ + memset(&data[hdr], 0, pBt->usableSize - hdr); } + data[hdr] = (char)flags; + first = hdr + ((flags&PTF_LEAF)==0 ? 12 : 8); + memset(&data[hdr+1], 0, 4); + data[hdr+7] = 0; + put2byte(&data[hdr+5], pBt->usableSize); + pPage->nFree = (u16)(pBt->usableSize - first); + decodeFlags(pPage, flags); + pPage->cellOffset = first; + pPage->aDataEnd = &data[pBt->pageSize]; + pPage->aCellIdx = &data[first]; + pPage->aDataOfst = &data[pPage->childPtrSize]; + pPage->nOverflow = 0; + assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); + pPage->maskPage = (u16)(pBt->pageSize - 1); + pPage->nCell = 0; + pPage->isInit = 1; +} - if( rc!=SQLITE_OK ){ - walIndexClose(pRet, 0); - sqlcipher_sqlite3OsClose(pRet->pWalFd); - sqlcipher_sqlite3_free(pRet); - }else{ - int iDC = sqlcipher_sqlite3OsDeviceCharacteristics(pDbFd); - if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; } - if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){ - pRet->padToSectorBoundary = 0; - } - *ppWal = pRet; - WALTRACE(("WAL%d: opened\n", pRet)); + +/* +** Convert a DbPage obtained from the pager into a MemPage used by +** the btree layer. +*/ +static MemPage *btreePageFromDbPage(DbPage *pDbPage, Pgno pgno, BtShared *pBt){ + MemPage *pPage = (MemPage*)sqlcipher_sqlite3PagerGetExtra(pDbPage); + if( pgno!=pPage->pgno ){ + pPage->aData = sqlcipher_sqlite3PagerGetData(pDbPage); + pPage->pDbPage = pDbPage; + pPage->pBt = pBt; + pPage->pgno = pgno; + pPage->hdrOffset = pgno==1 ? 100 : 0; } - return rc; + assert( pPage->aData==sqlcipher_sqlite3PagerGetData(pDbPage) ); + return pPage; } /* -** Change the size to which the WAL file is trucated on each reset. +** Get a page from the pager. Initialize the MemPage.pBt and +** MemPage.aData elements if needed. See also: btreeGetUnusedPage(). +** +** If the PAGER_GET_NOCONTENT flag is set, it means that we do not care +** about the content of the page at this time. So do not go to the disk +** to fetch the content. Just fill in the content with zeros for now. +** If in the future we call sqlcipher_sqlite3PagerWrite() on this page, that +** means we have started to be concerned about content and the disk +** read should occur at that point. */ -SQLITE_PRIVATE void sqlcipher_sqlite3WalLimit(Wal *pWal, i64 iLimit){ - if( pWal ) pWal->mxWalSize = iLimit; +static int btreeGetPage( + BtShared *pBt, /* The btree */ + Pgno pgno, /* Number of the page to fetch */ + MemPage **ppPage, /* Return the page in this parameter */ + int flags /* PAGER_GET_NOCONTENT or PAGER_GET_READONLY */ +){ + int rc; + DbPage *pDbPage; + + assert( flags==0 || flags==PAGER_GET_NOCONTENT || flags==PAGER_GET_READONLY ); + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + rc = sqlcipher_sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, flags); + if( rc ) return rc; + *ppPage = btreePageFromDbPage(pDbPage, pgno, pBt); + return SQLITE_OK; } /* -** Find the smallest page number out of all pages held in the WAL that -** has not been returned by any prior invocation of this method on the -** same WalIterator object. Write into *piFrame the frame index where -** that page was last written into the WAL. Write into *piPage the page -** number. -** -** Return 0 on success. If there are no pages in the WAL with a page -** number larger than *piPage, then return 1. +** Retrieve a page from the pager cache. If the requested page is not +** already in the pager cache return NULL. Initialize the MemPage.pBt and +** MemPage.aData elements if needed. */ -static int walIteratorNext( - WalIterator *p, /* Iterator */ - u32 *piPage, /* OUT: The page number of the next page */ - u32 *piFrame /* OUT: Wal frame index of next page */ -){ - u32 iMin; /* Result pgno must be greater than iMin */ - u32 iRet = 0xFFFFFFFF; /* 0xffffffff is never a valid page number */ - int i; /* For looping through segments */ - - iMin = p->iPrior; - assert( iMin<0xffffffff ); - for(i=p->nSegment-1; i>=0; i--){ - struct WalSegment *pSegment = &p->aSegment[i]; - while( pSegment->iNextnEntry ){ - u32 iPg = pSegment->aPgno[pSegment->aIndex[pSegment->iNext]]; - if( iPg>iMin ){ - if( iPgiZero + pSegment->aIndex[pSegment->iNext]; - } - break; - } - pSegment->iNext++; - } +static MemPage *btreePageLookup(BtShared *pBt, Pgno pgno){ + DbPage *pDbPage; + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + pDbPage = sqlcipher_sqlite3PagerLookup(pBt->pPager, pgno); + if( pDbPage ){ + return btreePageFromDbPage(pDbPage, pgno, pBt); } + return 0; +} - *piPage = p->iPrior = iRet; - return (iRet==0xFFFFFFFF); +/* +** Return the size of the database file in pages. If there is any kind of +** error, return ((unsigned int)-1). +*/ +static Pgno btreePagecount(BtShared *pBt){ + return pBt->nPage; +} +SQLITE_PRIVATE Pgno sqlcipher_sqlite3BtreeLastPage(Btree *p){ + assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); + return btreePagecount(p->pBt); } /* -** This function merges two sorted lists into a single sorted list. -** -** aLeft[] and aRight[] are arrays of indices. The sort key is -** aContent[aLeft[]] and aContent[aRight[]]. Upon entry, the following -** is guaranteed for all JiPage. ** -** aLeft[X]!=aRight[Y] && aContent[aLeft[X]] == aContent[aRight[Y]] +** The page is fetched as read-write unless pCur is not NULL and is +** a read-only cursor. ** -** When that happens, omit the aLeft[X] and use the aRight[Y] index. +** If an error occurs, then *ppPage is undefined. It +** may remain unchanged, or it may be set to an invalid value. */ -static void walMerge( - const u32 *aContent, /* Pages in wal - keys for the sort */ - ht_slot *aLeft, /* IN: Left hand input list */ - int nLeft, /* IN: Elements in array *paLeft */ - ht_slot **paRight, /* IN/OUT: Right hand input list */ - int *pnRight, /* IN/OUT: Elements in *paRight */ - ht_slot *aTmp /* Temporary buffer */ +static int getAndInitPage( + BtShared *pBt, /* The database file */ + Pgno pgno, /* Number of the page to get */ + MemPage **ppPage, /* Write the page pointer here */ + BtCursor *pCur, /* Cursor to receive the page, or NULL */ + int bReadOnly /* True for a read-only page */ ){ - int iLeft = 0; /* Current index in aLeft */ - int iRight = 0; /* Current index in aRight */ - int iOut = 0; /* Current index in output buffer */ - int nRight = *pnRight; - ht_slot *aRight = *paRight; - - assert( nLeft>0 && nRight>0 ); - while( iRightmutex) ); + assert( pCur==0 || ppPage==&pCur->pPage ); + assert( pCur==0 || bReadOnly==pCur->curPagerFlags ); + assert( pCur==0 || pCur->iPage>0 ); - if( (iLeft=nRight || aContent[aLeft[iLeft]]btreePagecount(pBt) ){ + rc = SQLITE_CORRUPT_BKPT; + goto getAndInitPage_error1; + } + rc = sqlcipher_sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly); + if( rc ){ + goto getAndInitPage_error1; + } + *ppPage = (MemPage*)sqlcipher_sqlite3PagerGetExtra(pDbPage); + if( (*ppPage)->isInit==0 ){ + btreePageFromDbPage(pDbPage, pgno, pBt); + rc = btreeInitPage(*ppPage); + if( rc!=SQLITE_OK ){ + goto getAndInitPage_error2; } - dbpage = aContent[logpage]; - - aTmp[iOut++] = logpage; - if( iLeftpgno==pgno || CORRUPT_DB ); + assert( (*ppPage)->aData==sqlcipher_sqlite3PagerGetData(pDbPage) ); - assert( iLeft>=nLeft || aContent[aLeft[iLeft]]>dbpage ); - assert( iRight>=nRight || aContent[aRight[iRight]]>dbpage ); + /* If obtaining a child page for a cursor, we must verify that the page is + ** compatible with the root page. */ + if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){ + rc = SQLITE_CORRUPT_PGNO(pgno); + goto getAndInitPage_error2; } + return SQLITE_OK; - *paRight = aLeft; - *pnRight = iOut; - memcpy(aLeft, aTmp, sizeof(aTmp[0])*iOut); +getAndInitPage_error2: + releasePage(*ppPage); +getAndInitPage_error1: + if( pCur ){ + pCur->iPage--; + pCur->pPage = pCur->apPage[pCur->iPage]; + } + testcase( pgno==0 ); + assert( pgno!=0 || rc==SQLITE_CORRUPT + || rc==SQLITE_IOERR_NOMEM + || rc==SQLITE_NOMEM ); + return rc; } /* -** Sort the elements in list aList using aContent[] as the sort key. -** Remove elements with duplicate keys, preferring to keep the -** larger aList[] values. -** -** The aList[] entries are indices into aContent[]. The values in -** aList[] are to be sorted so that for all JaData ); + assert( pPage->pBt ); + assert( pPage->pDbPage!=0 ); + assert( sqlcipher_sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); + assert( sqlcipher_sqlite3PagerGetData(pPage->pDbPage)==pPage->aData ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + sqlcipher_sqlite3PagerUnrefNotNull(pPage->pDbPage); +} +static void releasePage(MemPage *pPage){ + if( pPage ) releasePageNotNull(pPage); +} +static void releasePageOne(MemPage *pPage){ + assert( pPage!=0 ); + assert( pPage->aData ); + assert( pPage->pBt ); + assert( pPage->pDbPage!=0 ); + assert( sqlcipher_sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); + assert( sqlcipher_sqlite3PagerGetData(pPage->pDbPage)==pPage->aData ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + sqlcipher_sqlite3PagerUnrefPageOne(pPage->pDbPage); +} + +/* +** Get an unused page. ** -** aContent[aList[X]] == aContent[aList[Y]] +** This works just like btreeGetPage() with the addition: ** -** Keep the larger of the two values aList[X] and aList[Y] and discard -** the smaller. +** * If the page is already in use for some other purpose, immediately +** release it and return an SQLITE_CURRUPT error. +** * Make sure the isInit flag is clear */ -static void walMergesort( - const u32 *aContent, /* Pages in wal */ - ht_slot *aBuffer, /* Buffer of at least *pnList items to use */ - ht_slot *aList, /* IN/OUT: List to sort */ - int *pnList /* IN/OUT: Number of elements in aList[] */ +static int btreeGetUnusedPage( + BtShared *pBt, /* The btree */ + Pgno pgno, /* Number of the page to fetch */ + MemPage **ppPage, /* Return the page in this parameter */ + int flags /* PAGER_GET_NOCONTENT or PAGER_GET_READONLY */ ){ - struct Sublist { - int nList; /* Number of elements in aList */ - ht_slot *aList; /* Pointer to sub-list content */ - }; - - const int nList = *pnList; /* Size of input list */ - int nMerge = 0; /* Number of elements in list aMerge */ - ht_slot *aMerge = 0; /* List to be merged */ - int iList; /* Index into input list */ - u32 iSub = 0; /* Index into aSub array */ - struct Sublist aSub[13]; /* Array of sub-lists */ - - memset(aSub, 0, sizeof(aSub)); - assert( nList<=HASHTABLE_NPAGE && nList>0 ); - assert( HASHTABLE_NPAGE==(1<<(ArraySize(aSub)-1)) ); - - for(iList=0; iListaList && p->nList<=(1<aList==&aList[iList&~((2<aList, p->nList, &aMerge, &nMerge, aBuffer); + int rc = btreeGetPage(pBt, pgno, ppPage, flags); + if( rc==SQLITE_OK ){ + if( sqlcipher_sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){ + releasePage(*ppPage); + *ppPage = 0; + return SQLITE_CORRUPT_BKPT; } - aSub[iSub].aList = aMerge; - aSub[iSub].nList = nMerge; + (*ppPage)->isInit = 0; + }else{ + *ppPage = 0; } + return rc; +} - for(iSub++; iSubnList<=(1<aList==&aList[nList&~((2<aList, p->nList, &aMerge, &nMerge, aBuffer); - } - } - assert( aMerge==aList ); - *pnList = nMerge; -#ifdef SQLITE_DEBUG - { - int i; - for(i=1; i<*pnList; i++){ - assert( aContent[aList[i]] > aContent[aList[i-1]] ); +/* +** During a rollback, when the pager reloads information into the cache +** so that the cache is restored to its original state at the start of +** the transaction, for each page restored this routine is called. +** +** This routine needs to reset the extra data section at the end of the +** page to agree with the restored data. +*/ +static void pageReinit(DbPage *pData){ + MemPage *pPage; + pPage = (MemPage *)sqlcipher_sqlite3PagerGetExtra(pData); + assert( sqlcipher_sqlite3PagerPageRefcount(pData)>0 ); + if( pPage->isInit ){ + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + pPage->isInit = 0; + if( sqlcipher_sqlite3PagerPageRefcount(pData)>1 ){ + /* pPage might not be a btree page; it might be an overflow page + ** or ptrmap page or a free page. In those cases, the following + ** call to btreeInitPage() will likely return SQLITE_CORRUPT. + ** But no harm is done by this. And it is very important that + ** btreeInitPage() be called on every btree page so we make + ** the call for every page that comes in for re-initing. */ + btreeInitPage(pPage); } } -#endif } /* -** Free an iterator allocated by walIteratorInit(). +** Invoke the busy handler for a btree. */ -static void walIteratorFree(WalIterator *p){ - sqlcipher_sqlite3_free(p); +static int btreeInvokeBusyHandler(void *pArg){ + BtShared *pBt = (BtShared*)pArg; + assert( pBt->db ); + assert( sqlcipher_sqlite3_mutex_held(pBt->db->mutex) ); + return sqlcipher_sqlite3InvokeBusyHandler(&pBt->db->busyHandler); } /* -** Construct a WalInterator object that can be used to loop over all -** pages in the WAL following frame nBackfill in ascending order. Frames -** nBackfill or earlier may be included - excluding them is an optimization -** only. The caller must hold the checkpoint lock. +** Open a database file. ** -** On success, make *pp point to the newly allocated WalInterator object -** return SQLITE_OK. Otherwise, return an error code. If this routine -** returns an error, the value of *pp is undefined. +** zFilename is the name of the database file. If zFilename is NULL +** then an ephemeral database is created. The ephemeral database might +** be exclusively in memory, or it might use a disk-based memory cache. +** Either way, the ephemeral database will be automatically deleted +** when sqlcipher_sqlite3BtreeClose() is called. ** -** The calling routine should invoke walIteratorFree() to destroy the -** WalIterator object when it has finished with it. +** If zFilename is ":memory:" then an in-memory database is created +** that is automatically destroyed when it is closed. +** +** The "flags" parameter is a bitmask that might contain bits like +** BTREE_OMIT_JOURNAL and/or BTREE_MEMORY. +** +** If the database is already opened in the same database connection +** and we are in shared cache mode, then the open will fail with an +** SQLITE_CONSTRAINT error. We cannot allow two or more BtShared +** objects in the same database connection since doing so will lead +** to problems with locking. */ -static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ - WalIterator *p; /* Return value */ - int nSegment; /* Number of segments to merge */ - u32 iLast; /* Last frame in log */ - sqlcipher_sqlite3_int64 nByte; /* Number of bytes to allocate */ - int i; /* Iterator variable */ - ht_slot *aTmp; /* Temp space used by merge-sort */ - int rc = SQLITE_OK; /* Return Code */ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeOpen( + sqlcipher_sqlite3_vfs *pVfs, /* VFS to use for this b-tree */ + const char *zFilename, /* Name of the file containing the BTree database */ + sqlcipher_sqlite3 *db, /* Associated database handle */ + Btree **ppBtree, /* Pointer to new Btree object written here */ + int flags, /* Options */ + int vfsFlags /* Flags passed through to sqlcipher_sqlite3_vfs.xOpen() */ +){ + BtShared *pBt = 0; /* Shared part of btree structure */ + Btree *p; /* Handle to return */ + sqlcipher_sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */ + int rc = SQLITE_OK; /* Result code from this function */ + u8 nReserve; /* Byte of unused space on each page */ + unsigned char zDbHeader[100]; /* Database header content */ - /* This routine only runs while holding the checkpoint lock. And - ** it only runs if there is actually content in the log (mxFrame>0). + /* True if opening an ephemeral, temporary database */ + const int isTempDb = zFilename==0 || zFilename[0]==0; + + /* Set the variable isMemdb to true for an in-memory database, or + ** false for a file-based database. */ - assert( pWal->ckptLock && pWal->hdr.mxFrame>0 ); - iLast = pWal->hdr.mxFrame; +#ifdef SQLITE_OMIT_MEMORYDB + const int isMemdb = 0; +#else + const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0) + || (isTempDb && sqlcipher_sqlite3TempInMemory(db)) + || (vfsFlags & SQLITE_OPEN_MEMORY)!=0; +#endif - /* Allocate space for the WalIterator object. */ - nSegment = walFramePage(iLast) + 1; - nByte = sizeof(WalIterator) - + (nSegment-1)*sizeof(struct WalSegment) - + iLast*sizeof(ht_slot); - p = (WalIterator *)sqlcipher_sqlite3_malloc64(nByte); + assert( db!=0 ); + assert( pVfs!=0 ); + assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); + assert( (flags&0xff)==flags ); /* flags fit in 8 bits */ + + /* Only a BTREE_SINGLE database can be BTREE_UNORDERED */ + assert( (flags & BTREE_UNORDERED)==0 || (flags & BTREE_SINGLE)!=0 ); + + /* A BTREE_SINGLE database is always a temporary and/or ephemeral */ + assert( (flags & BTREE_SINGLE)==0 || isTempDb ); + + if( isMemdb ){ + flags |= BTREE_MEMORY; + } + if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){ + vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB; + } + p = sqlcipher_sqlite3MallocZero(sizeof(Btree)); if( !p ){ return SQLITE_NOMEM_BKPT; } - memset(p, 0, nByte); - p->nSegment = nSegment; + p->inTrans = TRANS_NONE; + p->db = db; +#ifndef SQLITE_OMIT_SHARED_CACHE + p->lock.pBtree = p; + p->lock.iTable = 1; +#endif - /* Allocate temporary space used by the merge-sort routine. This block - ** of memory will be freed before this function returns. +#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) + /* + ** If this Btree is a candidate for shared cache, try to find an + ** existing BtShared object that we can share with */ - aTmp = (ht_slot *)sqlcipher_sqlite3_malloc64( - sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) - ); - if( !aTmp ){ - rc = SQLITE_NOMEM_BKPT; - } + if( isTempDb==0 && (isMemdb==0 || (vfsFlags&SQLITE_OPEN_URI)!=0) ){ + if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ + int nFilename = sqlcipher_sqlite3Strlen30(zFilename)+1; + int nFullPathname = pVfs->mxPathname+1; + char *zFullPathname = sqlcipher_sqlite3Malloc(MAX(nFullPathname,nFilename)); + MUTEX_LOGIC( sqlcipher_sqlite3_mutex *mutexShared; ) - for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && isharable = 1; + if( !zFullPathname ){ + sqlcipher_sqlite3_free(p); + return SQLITE_NOMEM_BKPT; + } + if( isMemdb ){ + memcpy(zFullPathname, zFilename, nFilename); + }else{ + rc = sqlcipher_sqlite3OsFullPathname(pVfs, zFilename, + nFullPathname, zFullPathname); + if( rc ){ + if( rc==SQLITE_OK_SYMLINK ){ + rc = SQLITE_OK; + }else{ + sqlcipher_sqlite3_free(zFullPathname); + sqlcipher_sqlite3_free(p); + return rc; + } + } + } +#if SQLITE_THREADSAFE + mutexOpen = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN); + sqlcipher_sqlite3_mutex_enter(mutexOpen); + mutexShared = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); + sqlcipher_sqlite3_mutex_enter(mutexShared); +#endif + for(pBt=GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList); pBt; pBt=pBt->pNext){ + assert( pBt->nRef>0 ); + if( 0==strcmp(zFullPathname, sqlcipher_sqlite3PagerFilename(pBt->pPager, 0)) + && sqlcipher_sqlite3PagerVfs(pBt->pPager)==pVfs ){ + int iDb; + for(iDb=db->nDb-1; iDb>=0; iDb--){ + Btree *pExisting = db->aDb[iDb].pBt; + if( pExisting && pExisting->pBt==pBt ){ + sqlcipher_sqlite3_mutex_leave(mutexShared); + sqlcipher_sqlite3_mutex_leave(mutexOpen); + sqlcipher_sqlite3_free(zFullPathname); + sqlcipher_sqlite3_free(p); + return SQLITE_CONSTRAINT; + } + } + p->pBt = pBt; + pBt->nRef++; + break; + } + } + sqlcipher_sqlite3_mutex_leave(mutexShared); + sqlcipher_sqlite3_free(zFullPathname); + } +#ifdef SQLITE_DEBUG + else{ + /* In debug mode, we mark all persistent databases as sharable + ** even when they are not. This exercises the locking code and + ** gives more opportunity for asserts(sqlcipher_sqlite3_mutex_held()) + ** statements to find locking problems. + */ + p->sharable = 1; + } +#endif + } +#endif + if( pBt==0 ){ + /* + ** The following asserts make sure that structures used by the btree are + ** the right size. This is to guard against size changes that result + ** when compiling on a different architecture. + */ + assert( sizeof(i64)==8 ); + assert( sizeof(u64)==8 ); + assert( sizeof(u32)==4 ); + assert( sizeof(u16)==2 ); + assert( sizeof(Pgno)==4 ); - rc = walHashGet(pWal, i, &sLoc); + pBt = sqlcipher_sqlite3MallocZero( sizeof(*pBt) ); + if( pBt==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto btree_open_out; + } + rc = sqlcipher_sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, + sizeof(MemPage), flags, vfsFlags, pageReinit); if( rc==SQLITE_OK ){ - int j; /* Counter variable */ - int nEntry; /* Number of entries in this segment */ - ht_slot *aIndex; /* Sorted index for this segment */ + sqlcipher_sqlite3PagerSetMmapLimit(pBt->pPager, db->szMmap); + rc = sqlcipher_sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); + } + if( rc!=SQLITE_OK ){ + goto btree_open_out; + } + pBt->openFlags = (u8)flags; + pBt->db = db; + sqlcipher_sqlite3PagerSetBusyHandler(pBt->pPager, btreeInvokeBusyHandler, pBt); + p->pBt = pBt; - sLoc.aPgno++; - if( (i+1)==nSegment ){ - nEntry = (int)(iLast - sLoc.iZero); - }else{ - nEntry = (int)((u32*)sLoc.aHash - (u32*)sLoc.aPgno); + pBt->pCursor = 0; + pBt->pPage1 = 0; + if( sqlcipher_sqlite3PagerIsreadonly(pBt->pPager) ) pBt->btsFlags |= BTS_READ_ONLY; +#if defined(SQLITE_SECURE_DELETE) + pBt->btsFlags |= BTS_SECURE_DELETE; +#elif defined(SQLITE_FAST_SECURE_DELETE) + pBt->btsFlags |= BTS_OVERWRITE; +#endif + /* EVIDENCE-OF: R-51873-39618 The page size for a database file is + ** determined by the 2-byte integer located at an offset of 16 bytes from + ** the beginning of the database file. */ + pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16); + if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE + || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){ + pBt->pageSize = 0; +#ifndef SQLITE_OMIT_AUTOVACUUM + /* If the magic name ":memory:" will create an in-memory database, then + ** leave the autoVacuum mode at 0 (do not auto-vacuum), even if + ** SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if + ** SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a + ** regular file-name. In this case the auto-vacuum applies as per normal. + */ + if( zFilename && !isMemdb ){ + pBt->autoVacuum = (SQLITE_DEFAULT_AUTOVACUUM ? 1 : 0); + pBt->incrVacuum = (SQLITE_DEFAULT_AUTOVACUUM==2 ? 1 : 0); } - aIndex = &((ht_slot *)&p->aSegment[p->nSegment])[sLoc.iZero]; - sLoc.iZero++; +#endif + nReserve = 0; + }else{ + /* EVIDENCE-OF: R-37497-42412 The size of the reserved region is + ** determined by the one-byte unsigned integer found at an offset of 20 + ** into the database file header. */ + nReserve = zDbHeader[20]; + pBt->btsFlags |= BTS_PAGESIZE_FIXED; +#ifndef SQLITE_OMIT_AUTOVACUUM + pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0); + pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0); +#endif + } + rc = sqlcipher_sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve); + if( rc ) goto btree_open_out; + pBt->usableSize = pBt->pageSize - nReserve; + assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */ - for(j=0; jnRef = 1; + if( p->sharable ){ + MUTEX_LOGIC( sqlcipher_sqlite3_mutex *mutexShared; ) + MUTEX_LOGIC( mutexShared = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);) + if( SQLITE_THREADSAFE && sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ + pBt->mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_FAST); + if( pBt->mutex==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto btree_open_out; + } } - walMergesort((u32 *)sLoc.aPgno, aTmp, aIndex, &nEntry); - p->aSegment[i].iZero = sLoc.iZero; - p->aSegment[i].nEntry = nEntry; - p->aSegment[i].aIndex = aIndex; - p->aSegment[i].aPgno = (u32 *)sLoc.aPgno; + sqlcipher_sqlite3_mutex_enter(mutexShared); + pBt->pNext = GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList); + GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList) = pBt; + sqlcipher_sqlite3_mutex_leave(mutexShared); } +#endif } - sqlcipher_sqlite3_free(aTmp); - if( rc!=SQLITE_OK ){ - walIteratorFree(p); - p = 0; +#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) + /* If the new Btree uses a sharable pBtShared, then link the new + ** Btree into the list of all sharable Btrees for the same connection. + ** The list is kept in ascending order by pBt address. + */ + if( p->sharable ){ + int i; + Btree *pSib; + for(i=0; inDb; i++){ + if( (pSib = db->aDb[i].pBt)!=0 && pSib->sharable ){ + while( pSib->pPrev ){ pSib = pSib->pPrev; } + if( (uptr)p->pBt<(uptr)pSib->pBt ){ + p->pNext = pSib; + p->pPrev = 0; + pSib->pPrev = p; + }else{ + while( pSib->pNext && (uptr)pSib->pNext->pBt<(uptr)p->pBt ){ + pSib = pSib->pNext; + } + p->pNext = pSib->pNext; + p->pPrev = pSib; + if( p->pNext ){ + p->pNext->pPrev = p; + } + pSib->pNext = p; + } + break; + } + } } - *pp = p; - return rc; -} +#endif + *ppBtree = p; -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -/* -** Attempt to enable blocking locks. Blocking locks are enabled only if (a) -** they are supported by the VFS, and (b) the database handle is configured -** with a busy-timeout. Return 1 if blocking locks are successfully enabled, -** or 0 otherwise. -*/ -static int walEnableBlocking(Wal *pWal){ - int res = 0; - if( pWal->db ){ - int tmout = pWal->db->busyTimeout; - if( tmout ){ - int rc; - rc = sqlcipher_sqlite3OsFileControl( - pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout - ); - res = (rc==SQLITE_OK); +btree_open_out: + if( rc!=SQLITE_OK ){ + if( pBt && pBt->pPager ){ + sqlcipher_sqlite3PagerClose(pBt->pPager, 0); } - } - return res; -} + sqlcipher_sqlite3_free(pBt); + sqlcipher_sqlite3_free(p); + *ppBtree = 0; + }else{ + sqlcipher_sqlite3_file *pFile; -/* -** Disable blocking locks. -*/ -static void walDisableBlocking(Wal *pWal){ - int tmout = 0; - sqlcipher_sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout); -} + /* If the B-Tree was successfully opened, set the pager-cache size to the + ** default value. Except, when opening on an existing shared pager-cache, + ** do not change the pager-cache size. + */ + if( sqlcipher_sqlite3BtreeSchema(p, 0, 0)==0 ){ + sqlcipher_sqlite3BtreeSetCacheSize(p, SQLITE_DEFAULT_CACHE_SIZE); + } -/* -** If parameter bLock is true, attempt to enable blocking locks, take -** the WRITER lock, and then disable blocking locks. If blocking locks -** cannot be enabled, no attempt to obtain the WRITER lock is made. Return -** an SQLite error code if an error occurs, or SQLITE_OK otherwise. It is not -** an error if blocking locks can not be enabled. -** -** If the bLock parameter is false and the WRITER lock is held, release it. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3WalWriteLock(Wal *pWal, int bLock){ - int rc = SQLITE_OK; - assert( pWal->readLock<0 || bLock==0 ); - if( bLock ){ - assert( pWal->db ); - if( walEnableBlocking(pWal) ){ - rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); - if( rc==SQLITE_OK ){ - pWal->writeLock = 1; - } - walDisableBlocking(pWal); + pFile = sqlcipher_sqlite3PagerFile(pBt->pPager); + if( pFile->pMethods ){ + sqlcipher_sqlite3OsFileControlHint(pFile, SQLITE_FCNTL_PDB, (void*)&pBt->db); } - }else if( pWal->writeLock ){ - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); - pWal->writeLock = 0; } + if( mutexOpen ){ + assert( sqlcipher_sqlite3_mutex_held(mutexOpen) ); + sqlcipher_sqlite3_mutex_leave(mutexOpen); + } + assert( rc!=SQLITE_OK || sqlcipher_sqlite3BtreeConnectionCount(*ppBtree)>0 ); return rc; } /* -** Set the database handle used to determine if blocking locks are required. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3WalDb(Wal *pWal, sqlcipher_sqlite3 *db){ - pWal->db = db; -} - -/* -** Take an exclusive WRITE lock. Blocking if so configured. +** Decrement the BtShared.nRef counter. When it reaches zero, +** remove the BtShared structure from the sharing list. Return +** true if the BtShared.nRef counter reaches zero and return +** false if it is still positive. */ -static int walLockWriter(Wal *pWal){ - int rc; - walEnableBlocking(pWal); - rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); - walDisableBlocking(pWal); - return rc; -} -#else -# define walEnableBlocking(x) 0 -# define walDisableBlocking(x) -# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1) -# define sqlcipher_sqlite3WalDb(pWal, db) -#endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */ - +static int removeFromSharingList(BtShared *pBt){ +#ifndef SQLITE_OMIT_SHARED_CACHE + MUTEX_LOGIC( sqlcipher_sqlite3_mutex *pMainMtx; ) + BtShared *pList; + int removed = 0; -/* -** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and -** n. If the attempt fails and parameter xBusy is not NULL, then it is a -** busy-handler function. Invoke it and retry the lock until either the -** lock is successfully obtained or the busy-handler returns 0. -*/ -static int walBusyLock( - Wal *pWal, /* WAL connection */ - int (*xBusy)(void*), /* Function to call when busy */ - void *pBusyArg, /* Context argument for xBusyHandler */ - int lockIdx, /* Offset of first byte to lock */ - int n /* Number of bytes to lock */ -){ - int rc; - do { - rc = walLockExclusive(pWal, lockIdx, n); - }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT - if( rc==SQLITE_BUSY_TIMEOUT ){ - walDisableBlocking(pWal); - rc = SQLITE_BUSY; + assert( sqlcipher_sqlite3_mutex_notheld(pBt->mutex) ); + MUTEX_LOGIC( pMainMtx = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) + sqlcipher_sqlite3_mutex_enter(pMainMtx); + pBt->nRef--; + if( pBt->nRef<=0 ){ + if( GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList)==pBt ){ + GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList) = pBt->pNext; + }else{ + pList = GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList); + while( ALWAYS(pList) && pList->pNext!=pBt ){ + pList=pList->pNext; + } + if( ALWAYS(pList) ){ + pList->pNext = pBt->pNext; + } + } + if( SQLITE_THREADSAFE ){ + sqlcipher_sqlite3_mutex_free(pBt->mutex); + } + removed = 1; } + sqlcipher_sqlite3_mutex_leave(pMainMtx); + return removed; +#else + return 1; #endif - return rc; } /* -** The cache of the wal-index header must be valid to call this function. -** Return the page-size in bytes used by the database. +** Make sure pBt->pTmpSpace points to an allocation of +** MX_CELL_SIZE(pBt) bytes with a 4-byte prefix for a left-child +** pointer. */ -static int walPagesize(Wal *pWal){ - return (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); +static SQLITE_NOINLINE int allocateTempSpace(BtShared *pBt){ + assert( pBt!=0 ); + assert( pBt->pTmpSpace==0 ); + /* This routine is called only by btreeCursor() when allocating the + ** first write cursor for the BtShared object */ + assert( pBt->pCursor!=0 && (pBt->pCursor->curFlags & BTCF_WriteFlag)!=0 ); + pBt->pTmpSpace = sqlcipher_sqlite3PageMalloc( pBt->pageSize ); + if( pBt->pTmpSpace==0 ){ + BtCursor *pCur = pBt->pCursor; + pBt->pCursor = pCur->pNext; /* Unlink the cursor */ + memset(pCur, 0, sizeof(*pCur)); + return SQLITE_NOMEM_BKPT; + } + + /* One of the uses of pBt->pTmpSpace is to format cells before + ** inserting them into a leaf page (function fillInCell()). If + ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes + ** by the various routines that manipulate binary cells. Which + ** can mean that fillInCell() only initializes the first 2 or 3 + ** bytes of pTmpSpace, but that the first 4 bytes are copied from + ** it into a database page. This is not actually a problem, but it + ** does cause a valgrind error when the 1 or 2 bytes of unitialized + ** data is passed to system call write(). So to avoid this error, + ** zero the first 4 bytes of temp space here. + ** + ** Also: Provide four bytes of initialized space before the + ** beginning of pTmpSpace as an area available to prepend the + ** left-child pointer to the beginning of a cell. + */ + memset(pBt->pTmpSpace, 0, 8); + pBt->pTmpSpace += 4; + return SQLITE_OK; } /* -** The following is guaranteed when this function is called: -** -** a) the WRITER lock is held, -** b) the entire log file has been checkpointed, and -** c) any existing readers are reading exclusively from the database -** file - there are no readers that may attempt to read a frame from -** the log file. -** -** This function updates the shared-memory structures so that the next -** client to write to the database (which may be this one) does so by -** writing frames into the start of the log file. -** -** The value of parameter salt1 is used as the aSalt[1] value in the -** new wal-index header. It should be passed a pseudo-random value (i.e. -** one obtained from sqlcipher_sqlite3_randomness()). +** Free the pBt->pTmpSpace allocation */ -static void walRestartHdr(Wal *pWal, u32 salt1){ - volatile WalCkptInfo *pInfo = walCkptInfo(pWal); - int i; /* Loop counter */ - u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */ - pWal->nCkpt++; - pWal->hdr.mxFrame = 0; - sqlcipher_sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlcipher_sqlite3Get4byte((u8*)&aSalt[0])); - memcpy(&pWal->hdr.aSalt[1], &salt1, 4); - walIndexWriteHdr(pWal); - AtomicStore(&pInfo->nBackfill, 0); - pInfo->nBackfillAttempted = 0; - pInfo->aReadMark[1] = 0; - for(i=2; iaReadMark[i] = READMARK_NOT_USED; - assert( pInfo->aReadMark[0]==0 ); +static void freeTempSpace(BtShared *pBt){ + if( pBt->pTmpSpace ){ + pBt->pTmpSpace -= 4; + sqlcipher_sqlite3PageFree(pBt->pTmpSpace); + pBt->pTmpSpace = 0; + } } /* -** Copy as much content as we can from the WAL back into the database file -** in response to an sqlcipher_sqlite3_wal_checkpoint() request or the equivalent. -** -** The amount of information copies from WAL to database might be limited -** by active readers. This routine will never overwrite a database page -** that a concurrent reader might be using. -** -** All I/O barrier operations (a.k.a fsyncs) occur in this routine when -** SQLite is in WAL-mode in synchronous=NORMAL. That means that if -** checkpoints are always run by a background thread or background -** process, foreground threads will never block on a lengthy fsync call. -** -** Fsync is called on the WAL before writing content out of the WAL and -** into the database. This ensures that if the new content is persistent -** in the WAL and can be recovered following a power-loss or hard reset. -** -** Fsync is also called on the database file if (and only if) the entire -** WAL content is copied into the database file. This second fsync makes -** it safe to delete the WAL since the new content will persist in the -** database file. -** -** This routine uses and updates the nBackfill field of the wal-index header. -** This is the only routine that will increase the value of nBackfill. -** (A WAL reset or recovery will revert nBackfill to zero, but not increase -** its value.) -** -** The caller must be holding sufficient locks to ensure that no other -** checkpoint is running (in any other thread or process) at the same -** time. +** Close an open database and invalidate all cursors. */ -static int walCheckpoint( - Wal *pWal, /* Wal connection */ - sqlcipher_sqlite3 *db, /* Check for interrupts on this handle */ - int eMode, /* One of PASSIVE, FULL or RESTART */ - int (*xBusy)(void*), /* Function to call when busy */ - void *pBusyArg, /* Context argument for xBusyHandler */ - int sync_flags, /* Flags for OsSync() (or 0) */ - u8 *zBuf /* Temporary buffer to use */ -){ - int rc = SQLITE_OK; /* Return code */ - int szPage; /* Database page-size */ - WalIterator *pIter = 0; /* Wal iterator context */ - u32 iDbpage = 0; /* Next database page to write */ - u32 iFrame = 0; /* Wal frame containing data for iDbpage */ - u32 mxSafeFrame; /* Max frame that can be backfilled */ - u32 mxPage; /* Max database page to write */ - int i; /* Loop counter */ - volatile WalCkptInfo *pInfo; /* The checkpoint status information */ - - szPage = walPagesize(pWal); - testcase( szPage<=32768 ); - testcase( szPage>=65536 ); - pInfo = walCkptInfo(pWal); - if( pInfo->nBackfillhdr.mxFrame ){ - - /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked - ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ - assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); - - /* Compute in mxSafeFrame the index of the last frame of the WAL that is - ** safe to write into the database. Frames beyond mxSafeFrame might - ** overwrite database pages that are in use by active readers and thus - ** cannot be backfilled from the WAL. - */ - mxSafeFrame = pWal->hdr.mxFrame; - mxPage = pWal->hdr.nPage; - for(i=1; iaReadMark+i); - if( mxSafeFrame>y ){ - assert( y<=pWal->hdr.mxFrame ); - rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); - if( rc==SQLITE_OK ){ - u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED); - AtomicStore(pInfo->aReadMark+i, iMark); - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); - }else if( rc==SQLITE_BUSY ){ - mxSafeFrame = y; - xBusy = 0; - }else{ - goto walcheckpoint_out; - } - } - } - - /* Allocate the iterator */ - if( pInfo->nBackfillnBackfill, &pIter); - assert( rc==SQLITE_OK || pIter==0 ); - } - - if( pIter - && (rc = walBusyLock(pWal,xBusy,pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK - ){ - u32 nBackfill = pInfo->nBackfill; - - pInfo->nBackfillAttempted = mxSafeFrame; - - /* Sync the WAL to disk */ - rc = sqlcipher_sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); - - /* If the database may grow as a result of this checkpoint, hint - ** about the eventual size of the db file to the VFS layer. - */ - if( rc==SQLITE_OK ){ - i64 nReq = ((i64)mxPage * szPage); - i64 nSize; /* Current size of database file */ - sqlcipher_sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0); - rc = sqlcipher_sqlite3OsFileSize(pWal->pDbFd, &nSize); - if( rc==SQLITE_OK && nSizehdr.mxFrame*szPage)pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq); - } - } - - } - - /* Iterate through the contents of the WAL, copying data to the db file */ - while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ - i64 iOffset; - assert( walFramePgno(pWal, iFrame)==iDbpage ); - if( AtomicLoad(&db->u1.isInterrupted) ){ - rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; - break; - } - if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){ - continue; - } - iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; - /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ - rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); - if( rc!=SQLITE_OK ) break; - iOffset = (iDbpage-1)*(i64)szPage; - testcase( IS_BIG_INT(iOffset) ); - rc = sqlcipher_sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); - if( rc!=SQLITE_OK ) break; - } - sqlcipher_sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0); +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeClose(Btree *p){ + BtShared *pBt = p->pBt; - /* If work was actually accomplished... */ - if( rc==SQLITE_OK ){ - if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ - i64 szDb = pWal->hdr.nPage*(i64)szPage; - testcase( IS_BIG_INT(szDb) ); - rc = sqlcipher_sqlite3OsTruncate(pWal->pDbFd, szDb); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags)); - } - } - if( rc==SQLITE_OK ){ - AtomicStore(&pInfo->nBackfill, mxSafeFrame); - } - } + /* Close all cursors opened via this handle. */ + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + sqlcipher_sqlite3BtreeEnter(p); - /* Release the reader lock held while backfilling */ - walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); - } + /* Verify that no other cursors have this Btree open */ +#ifdef SQLITE_DEBUG + { + BtCursor *pCur = pBt->pCursor; + while( pCur ){ + BtCursor *pTmp = pCur; + pCur = pCur->pNext; + assert( pTmp->pBtree!=p ); - if( rc==SQLITE_BUSY ){ - /* Reset the return code so as not to report a checkpoint failure - ** just because there are active readers. */ - rc = SQLITE_OK; } } +#endif - /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the - ** entire wal file has been copied into the database file, then block - ** until all readers have finished using the wal file. This ensures that - ** the next process to write to the database restarts the wal file. + /* Rollback any active transaction and free the handle structure. + ** The call to sqlcipher_sqlite3BtreeRollback() drops any table-locks held by + ** this handle. */ - if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){ - assert( pWal->writeLock ); - if( pInfo->nBackfillhdr.mxFrame ){ - rc = SQLITE_BUSY; - }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ - u32 salt1; - sqlcipher_sqlite3_randomness(4, &salt1); - assert( pInfo->nBackfill==pWal->hdr.mxFrame ); - rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); - if( rc==SQLITE_OK ){ - if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){ - /* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as - ** SQLITE_CHECKPOINT_RESTART with the addition that it also - ** truncates the log file to zero bytes just prior to a - ** successful return. - ** - ** In theory, it might be safe to do this without updating the - ** wal-index header in shared memory, as all subsequent reader or - ** writer clients should see that the entire log file has been - ** checkpointed and behave accordingly. This seems unsafe though, - ** as it would leave the system in a state where the contents of - ** the wal-index header do not match the contents of the - ** file-system. To avoid this, update the wal-index header to - ** indicate that the log file contains zero valid frames. */ - walRestartHdr(pWal, salt1); - rc = sqlcipher_sqlite3OsTruncate(pWal->pWalFd, 0); - } - walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); - } + sqlcipher_sqlite3BtreeRollback(p, SQLITE_OK, 0); + sqlcipher_sqlite3BtreeLeave(p); + + /* If there are still other outstanding references to the shared-btree + ** structure, return now. The remainder of this procedure cleans + ** up the shared-btree. + */ + assert( p->wantToLock==0 && p->locked==0 ); + if( !p->sharable || removeFromSharingList(pBt) ){ + /* The pBt is no longer on the sharing list, so we can access + ** it without having to hold the mutex. + ** + ** Clean out and delete the BtShared object. + */ + assert( !pBt->pCursor ); + sqlcipher_sqlite3PagerClose(pBt->pPager, p->db); + if( pBt->xFreeSchema && pBt->pSchema ){ + pBt->xFreeSchema(pBt->pSchema); } + sqlcipher_sqlite3DbFree(0, pBt->pSchema); + freeTempSpace(pBt); + sqlcipher_sqlite3_free(pBt); } - walcheckpoint_out: - walIteratorFree(pIter); - return rc; +#ifndef SQLITE_OMIT_SHARED_CACHE + assert( p->wantToLock==0 ); + assert( p->locked==0 ); + if( p->pPrev ) p->pPrev->pNext = p->pNext; + if( p->pNext ) p->pNext->pPrev = p->pPrev; +#endif + + sqlcipher_sqlite3_free(p); + return SQLITE_OK; } /* -** If the WAL file is currently larger than nMax bytes in size, truncate -** it to exactly nMax bytes. If an error occurs while doing so, ignore it. +** Change the "soft" limit on the number of pages in the cache. +** Unused and unmodified pages will be recycled when the number of +** pages in the cache exceeds this soft limit. But the size of the +** cache is allowed to grow larger than this limit if it contains +** dirty pages or pages still in active use. */ -static void walLimitSize(Wal *pWal, i64 nMax){ - i64 sz; - int rx; - sqlcipher_sqlite3BeginBenignMalloc(); - rx = sqlcipher_sqlite3OsFileSize(pWal->pWalFd, &sz); - if( rx==SQLITE_OK && (sz > nMax ) ){ - rx = sqlcipher_sqlite3OsTruncate(pWal->pWalFd, nMax); - } - sqlcipher_sqlite3EndBenignMalloc(); - if( rx ){ - sqlcipher_sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName); - } +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetCacheSize(Btree *p, int mxPage){ + BtShared *pBt = p->pBt; + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + sqlcipher_sqlite3BtreeEnter(p); + sqlcipher_sqlite3PagerSetCachesize(pBt->pPager, mxPage); + sqlcipher_sqlite3BtreeLeave(p); + return SQLITE_OK; } /* -** Close a connection to a log file. +** Change the "spill" limit on the number of pages in the cache. +** If the number of pages exceeds this limit during a write transaction, +** the pager might attempt to "spill" pages to the journal early in +** order to free up memory. +** +** The value returned is the current spill size. If zero is passed +** as an argument, no changes are made to the spill size setting, so +** using mxPage of 0 is a way to query the current spill size. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalClose( - Wal *pWal, /* Wal to close */ - sqlcipher_sqlite3 *db, /* For interrupt flag */ - int sync_flags, /* Flags to pass to OsSync() (or 0) */ - int nBuf, - u8 *zBuf /* Buffer of at least nBuf bytes */ -){ - int rc = SQLITE_OK; - if( pWal ){ - int isDelete = 0; /* True to unlink wal and wal-index files */ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetSpillSize(Btree *p, int mxPage){ + BtShared *pBt = p->pBt; + int res; + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + sqlcipher_sqlite3BtreeEnter(p); + res = sqlcipher_sqlite3PagerSetSpillsize(pBt->pPager, mxPage); + sqlcipher_sqlite3BtreeLeave(p); + return res; +} - /* If an EXCLUSIVE lock can be obtained on the database file (using the - ** ordinary, rollback-mode locking methods, this guarantees that the - ** connection associated with this log file is the only connection to - ** the database. In this case checkpoint the database and unlink both - ** the wal and wal-index files. - ** - ** The EXCLUSIVE lock is not released before returning. - */ - if( zBuf!=0 - && SQLITE_OK==(rc = sqlcipher_sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE)) - ){ - if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ - pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; - } - rc = sqlcipher_sqlite3WalCheckpoint(pWal, db, - SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0 - ); - if( rc==SQLITE_OK ){ - int bPersist = -1; - sqlcipher_sqlite3OsFileControlHint( - pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist - ); - if( bPersist!=1 ){ - /* Try to delete the WAL file if the checkpoint completed and - ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal - ** mode (!bPersist) */ - isDelete = 1; - }else if( pWal->mxWalSize>=0 ){ - /* Try to truncate the WAL file to zero bytes if the checkpoint - ** completed and fsynced (rc==SQLITE_OK) and we are in persistent - ** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a - ** non-negative value (pWal->mxWalSize>=0). Note that we truncate - ** to zero bytes as truncating to the journal_size_limit might - ** leave a corrupt WAL file on disk. */ - walLimitSize(pWal, 0); - } - } - } +#if SQLITE_MAX_MMAP_SIZE>0 +/* +** Change the limit on the amount of the database file that may be +** memory mapped. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetMmapLimit(Btree *p, sqlcipher_sqlite3_int64 szMmap){ + BtShared *pBt = p->pBt; + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + sqlcipher_sqlite3BtreeEnter(p); + sqlcipher_sqlite3PagerSetMmapLimit(pBt->pPager, szMmap); + sqlcipher_sqlite3BtreeLeave(p); + return SQLITE_OK; +} +#endif /* SQLITE_MAX_MMAP_SIZE>0 */ - walIndexClose(pWal, isDelete); - sqlcipher_sqlite3OsClose(pWal->pWalFd); - if( isDelete ){ - sqlcipher_sqlite3BeginBenignMalloc(); - sqlcipher_sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0); - sqlcipher_sqlite3EndBenignMalloc(); - } - WALTRACE(("WAL%p: closed\n", pWal)); - sqlcipher_sqlite3_free((void *)pWal->apWiData); - sqlcipher_sqlite3_free(pWal); +/* +** Change the way data is synced to disk in order to increase or decrease +** how well the database resists damage due to OS crashes and power +** failures. Level 1 is the same as asynchronous (no syncs() occur and +** there is a high probability of damage) Level 2 is the default. There +** is a very low but non-zero probability of damage. Level 3 reduces the +** probability of damage to near zero but with a write performance reduction. +*/ +#ifndef SQLITE_OMIT_PAGER_PRAGMAS +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetPagerFlags( + Btree *p, /* The btree to set the safety level on */ + unsigned pgFlags /* Various PAGER_* flags */ +){ + BtShared *pBt = p->pBt; + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + sqlcipher_sqlite3BtreeEnter(p); + sqlcipher_sqlite3PagerSetFlags(pBt->pPager, pgFlags); + sqlcipher_sqlite3BtreeLeave(p); + return SQLITE_OK; +} +#endif + +/* +** Change the default pages size and the number of reserved bytes per page. +** Or, if the page size has already been fixed, return SQLITE_READONLY +** without changing anything. +** +** The page size must be a power of 2 between 512 and 65536. If the page +** size supplied does not meet this constraint then the page size is not +** changed. +** +** Page sizes are constrained to be a power of two so that the region +** of the database file used for locking (beginning at PENDING_BYTE, +** the first byte past the 1GB boundary, 0x40000000) needs to occur +** at the beginning of a page. +** +** If parameter nReserve is less than zero, then the number of reserved +** bytes per page is left unchanged. +** +** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size +** and autovacuum mode can no longer be changed. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){ + int rc = SQLITE_OK; + int x; + BtShared *pBt = p->pBt; + assert( nReserve>=0 && nReserve<=255 ); + sqlcipher_sqlite3BtreeEnter(p); + pBt->nReserveWanted = nReserve; + x = pBt->pageSize - pBt->usableSize; + if( nReservebtsFlags & BTS_PAGESIZE_FIXED ){ + sqlcipher_sqlite3BtreeLeave(p); + return SQLITE_READONLY; + } + assert( nReserve>=0 && nReserve<=255 ); + if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && + ((pageSize-1)&pageSize)==0 ){ + assert( (pageSize & 7)==0 ); + assert( !pBt->pCursor ); + if( nReserve>32 && pageSize==512 ) pageSize = 1024; + pBt->pageSize = (u32)pageSize; + freeTempSpace(pBt); } + rc = sqlcipher_sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve); + pBt->usableSize = pBt->pageSize - (u16)nReserve; + if( iFix ) pBt->btsFlags |= BTS_PAGESIZE_FIXED; + sqlcipher_sqlite3BtreeLeave(p); return rc; } /* -** Try to read the wal-index header. Return 0 on success and 1 if -** there is a problem. -** -** The wal-index is in shared memory. Another thread or process might -** be writing the header at the same time this procedure is trying to -** read it, which might result in inconsistency. A dirty read is detected -** by verifying that both copies of the header are the same and also by -** a checksum on the header. +** Return the currently defined page size +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeGetPageSize(Btree *p){ + return p->pBt->pageSize; +} + +/* +** This function is similar to sqlcipher_sqlite3BtreeGetReserve(), except that it +** may only be called if it is guaranteed that the b-tree mutex is already +** held. ** -** If and only if the read is consistent and the header is different from -** pWal->hdr, then pWal->hdr is updated to the content of the new header -** and *pChanged is set to 1. +** This is useful in one special case in the backup API code where it is +** known that the shared b-tree mutex is held, but the mutex on the +** database handle that owns *p is not. In this case if sqlcipher_sqlite3BtreeEnter() +** were to be called, it might collide with some other operation on the +** database handle that owns *p, causing undefined behavior. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeGetReserveNoMutex(Btree *p){ + int n; + assert( sqlcipher_sqlite3_mutex_held(p->pBt->mutex) ); + n = p->pBt->pageSize - p->pBt->usableSize; + return n; +} + +/* +** Return the number of bytes of space at the end of every page that +** are intentually left unused. This is the "reserved" space that is +** sometimes used by extensions. ** -** If the checksum cannot be verified return non-zero. If the header -** is read successfully and the checksum verified, return zero. +** The value returned is the larger of the current reserve size and +** the latest reserve size requested by SQLITE_FILECTRL_RESERVE_BYTES. +** The amount of reserve can only grow - never shrink. */ -static SQLITE_NO_TSAN int walIndexTryHdr(Wal *pWal, int *pChanged){ - u32 aCksum[2]; /* Checksum on the header content */ - WalIndexHdr h1, h2; /* Two copies of the header content */ - WalIndexHdr volatile *aHdr; /* Header in shared memory */ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeGetRequestedReserve(Btree *p){ + int n1, n2; + sqlcipher_sqlite3BtreeEnter(p); + n1 = (int)p->pBt->nReserveWanted; + n2 = sqlcipher_sqlite3BtreeGetReserveNoMutex(p); + sqlcipher_sqlite3BtreeLeave(p); + return n1>n2 ? n1 : n2; +} - /* The first page of the wal-index must be mapped at this point. */ - assert( pWal->nWiData>0 && pWal->apWiData[0] ); - /* Read the header. This might happen concurrently with a write to the - ** same area of shared memory on a different CPU in a SMP, - ** meaning it is possible that an inconsistent snapshot is read - ** from the file. If this happens, return non-zero. - ** - ** tag-20200519-1: - ** There are two copies of the header at the beginning of the wal-index. - ** When reading, read [0] first then [1]. Writes are in the reverse order. - ** Memory barriers are used to prevent the compiler or the hardware from - ** reordering the reads and writes. TSAN and similar tools can sometimes - ** give false-positive warnings about these accesses because the tools do not - ** account for the double-read and the memory barrier. The use of mutexes - ** here would be problematic as the memory being accessed is potentially - ** shared among multiple processes and not all mutex implementions work - ** reliably in that environment. - */ - aHdr = walIndexHdr(pWal); - memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); /* Possible TSAN false-positive */ - walShmBarrier(pWal); - memcpy(&h2, (void *)&aHdr[1], sizeof(h2)); +/* +** Set the maximum page count for a database if mxPage is positive. +** No changes are made if mxPage is 0 or negative. +** Regardless of the value of mxPage, return the maximum page count. +*/ +SQLITE_PRIVATE Pgno sqlcipher_sqlite3BtreeMaxPageCount(Btree *p, Pgno mxPage){ + Pgno n; + sqlcipher_sqlite3BtreeEnter(p); + n = sqlcipher_sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage); + sqlcipher_sqlite3BtreeLeave(p); + return n; +} - if( memcmp(&h1, &h2, sizeof(h1))!=0 ){ - return 1; /* Dirty read */ - } - if( h1.isInit==0 ){ - return 1; /* Malformed header - probably all zeros */ - } - walChecksumBytes(1, (u8*)&h1, sizeof(h1)-sizeof(h1.aCksum), 0, aCksum); - if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){ - return 1; /* Checksum does not match */ +/* +** Change the values for the BTS_SECURE_DELETE and BTS_OVERWRITE flags: +** +** newFlag==0 Both BTS_SECURE_DELETE and BTS_OVERWRITE are cleared +** newFlag==1 BTS_SECURE_DELETE set and BTS_OVERWRITE is cleared +** newFlag==2 BTS_SECURE_DELETE cleared and BTS_OVERWRITE is set +** newFlag==(-1) No changes +** +** This routine acts as a query if newFlag is less than zero +** +** With BTS_OVERWRITE set, deleted content is overwritten by zeros, but +** freelist leaf pages are not written back to the database. Thus in-page +** deleted content is cleared, but freelist deleted content is not. +** +** With BTS_SECURE_DELETE, operation is like BTS_OVERWRITE with the addition +** that freelist leaf pages are written back into the database, increasing +** the amount of disk I/O. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSecureDelete(Btree *p, int newFlag){ + int b; + if( p==0 ) return 0; + sqlcipher_sqlite3BtreeEnter(p); + assert( BTS_OVERWRITE==BTS_SECURE_DELETE*2 ); + assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) ); + if( newFlag>=0 ){ + p->pBt->btsFlags &= ~BTS_FAST_SECURE; + p->pBt->btsFlags |= BTS_SECURE_DELETE*newFlag; } + b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE; + sqlcipher_sqlite3BtreeLeave(p); + return b; +} - if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){ - *pChanged = 1; - memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr)); - pWal->szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); - testcase( pWal->szPage<=32768 ); - testcase( pWal->szPage>=65536 ); +/* +** Change the 'auto-vacuum' property of the database. If the 'autoVacuum' +** parameter is non-zero, then auto-vacuum mode is enabled. If zero, it +** is disabled. The default value for the auto-vacuum property is +** determined by the SQLITE_DEFAULT_AUTOVACUUM macro. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){ +#ifdef SQLITE_OMIT_AUTOVACUUM + return SQLITE_READONLY; +#else + BtShared *pBt = p->pBt; + int rc = SQLITE_OK; + u8 av = (u8)autoVacuum; + + sqlcipher_sqlite3BtreeEnter(p); + if( (pBt->btsFlags & BTS_PAGESIZE_FIXED)!=0 && (av ?1:0)!=pBt->autoVacuum ){ + rc = SQLITE_READONLY; + }else{ + pBt->autoVacuum = av ?1:0; + pBt->incrVacuum = av==2 ?1:0; } + sqlcipher_sqlite3BtreeLeave(p); + return rc; +#endif +} - /* The header was successfully read. Return zero. */ - return 0; +/* +** Return the value of the 'auto-vacuum' property. If auto-vacuum is +** enabled 1 is returned. Otherwise 0. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeGetAutoVacuum(Btree *p){ +#ifdef SQLITE_OMIT_AUTOVACUUM + return BTREE_AUTOVACUUM_NONE; +#else + int rc; + sqlcipher_sqlite3BtreeEnter(p); + rc = ( + (!p->pBt->autoVacuum)?BTREE_AUTOVACUUM_NONE: + (!p->pBt->incrVacuum)?BTREE_AUTOVACUUM_FULL: + BTREE_AUTOVACUUM_INCR + ); + sqlcipher_sqlite3BtreeLeave(p); + return rc; +#endif } /* -** This is the value that walTryBeginRead returns when it needs to -** be retried. +** If the user has not set the safety-level for this database connection +** using "PRAGMA synchronous", and if the safety-level is not already +** set to the value passed to this function as the second parameter, +** set it so. */ -#define WAL_RETRY (-1) +#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS \ + && !defined(SQLITE_OMIT_WAL) +static void setDefaultSyncFlag(BtShared *pBt, u8 safety_level){ + sqlcipher_sqlite3 *db; + Db *pDb; + if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){ + while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; } + if( pDb->bSyncSet==0 + && pDb->safety_level!=safety_level + && pDb!=&db->aDb[1] + ){ + pDb->safety_level = safety_level; + sqlcipher_sqlite3PagerSetFlags(pBt->pPager, + pDb->safety_level | (db->flags & PAGER_FLAGS_MASK)); + } + } +} +#else +# define setDefaultSyncFlag(pBt,safety_level) +#endif + +/* Forward declaration */ +static int newDatabase(BtShared*); + /* -** Read the wal-index header from the wal-index and into pWal->hdr. -** If the wal-header appears to be corrupt, try to reconstruct the -** wal-index from the WAL before returning. -** -** Set *pChanged to 1 if the wal-index header value in pWal->hdr is -** changed by this operation. If pWal->hdr is unchanged, set *pChanged -** to 0. +** Get a reference to pPage1 of the database file. This will +** also acquire a readlock on that file. ** -** If the wal-index header is successfully read, return SQLITE_OK. -** Otherwise an SQLite error code. +** SQLITE_OK is returned on success. If the file is not a +** well-formed database file, then SQLITE_CORRUPT is returned. +** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM +** is returned if we run out of memory. */ -static int walIndexReadHdr(Wal *pWal, int *pChanged){ - int rc; /* Return code */ - int badHdr; /* True if a header read failed */ - volatile u32 *page0; /* Chunk of wal-index containing header */ +static int lockBtree(BtShared *pBt){ + int rc; /* Result code from subfunctions */ + MemPage *pPage1; /* Page 1 of the database file */ + u32 nPage; /* Number of pages in the database */ + u32 nPageFile = 0; /* Number of pages in the database file */ - /* Ensure that page 0 of the wal-index (the page that contains the - ** wal-index header) is mapped. Return early if an error occurs here. + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + assert( pBt->pPage1==0 ); + rc = sqlcipher_sqlite3PagerSharedLock(pBt->pPager); + if( rc!=SQLITE_OK ) return rc; + rc = btreeGetPage(pBt, 1, &pPage1, 0); + if( rc!=SQLITE_OK ) return rc; + + /* Do some checking to help insure the file we opened really is + ** a valid database file. */ - assert( pChanged ); - rc = walIndexPage(pWal, 0, &page0); - if( rc!=SQLITE_OK ){ - assert( rc!=SQLITE_READONLY ); /* READONLY changed to OK in walIndexPage */ - if( rc==SQLITE_READONLY_CANTINIT ){ - /* The SQLITE_READONLY_CANTINIT return means that the shared-memory - ** was openable but is not writable, and this thread is unable to - ** confirm that another write-capable connection has the shared-memory - ** open, and hence the content of the shared-memory is unreliable, - ** since the shared-memory might be inconsistent with the WAL file - ** and there is no writer on hand to fix it. */ - assert( page0==0 ); - assert( pWal->writeLock==0 ); - assert( pWal->readOnly & WAL_SHM_RDONLY ); - pWal->bShmUnreliable = 1; - pWal->exclusiveMode = WAL_HEAPMEMORY_MODE; - *pChanged = 1; - }else{ - return rc; /* Any other non-OK return is just an error */ - } - }else{ - /* page0 can be NULL if the SHM is zero bytes in size and pWal->writeLock - ** is zero, which prevents the SHM from growing */ - testcase( page0!=0 ); + nPage = get4byte(28+(u8*)pPage1->aData); + sqlcipher_sqlite3PagerPagecount(pBt->pPager, (int*)&nPageFile); + if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){ + nPage = nPageFile; } - assert( page0!=0 || pWal->writeLock==0 ); + if( (pBt->db->flags & SQLITE_ResetDatabase)!=0 ){ + nPage = 0; + } + if( nPage>0 ){ + u32 pageSize; + u32 usableSize; + u8 *page1 = pPage1->aData; + rc = SQLITE_NOTADB; + /* EVIDENCE-OF: R-43737-39999 Every valid SQLite database file begins + ** with the following 16 bytes (in hex): 53 51 4c 69 74 65 20 66 6f 72 6d + ** 61 74 20 33 00. */ + if( memcmp(page1, zMagicHeader, 16)!=0 ){ + goto page1_init_failed; + } - /* If the first page of the wal-index has been mapped, try to read the - ** wal-index header immediately, without holding any lock. This usually - ** works, but may fail if the wal-index header is corrupt or currently - ** being modified by another thread or process. - */ - badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1); +#ifdef SQLITE_OMIT_WAL + if( page1[18]>1 ){ + pBt->btsFlags |= BTS_READ_ONLY; + } + if( page1[19]>1 ){ + goto page1_init_failed; + } +#else + if( page1[18]>2 ){ + pBt->btsFlags |= BTS_READ_ONLY; + } + if( page1[19]>2 ){ + goto page1_init_failed; + } - /* If the first attempt failed, it might have been due to a race - ** with a writer. So get a WRITE lock and try again. - */ - if( badHdr ){ - if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){ - if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ - walUnlockShared(pWal, WAL_WRITE_LOCK); - rc = SQLITE_READONLY_RECOVERY; + /* If the read version is set to 2, this database should be accessed + ** in WAL mode. If the log is not already open, open it now. Then + ** return SQLITE_OK and return without populating BtShared.pPage1. + ** The caller detects this and calls this function again. This is + ** required as the version of page 1 currently in the page1 buffer + ** may not be the latest version - there may be a newer one in the log + ** file. + */ + if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){ + int isOpen = 0; + rc = sqlcipher_sqlite3PagerOpenWal(pBt->pPager, &isOpen); + if( rc!=SQLITE_OK ){ + goto page1_init_failed; + }else{ + setDefaultSyncFlag(pBt, SQLITE_DEFAULT_WAL_SYNCHRONOUS+1); + if( isOpen==0 ){ + releasePageOne(pPage1); + return SQLITE_OK; + } } + rc = SQLITE_NOTADB; }else{ - int bWriteLock = pWal->writeLock; - if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){ - pWal->writeLock = 1; - if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ - badHdr = walIndexTryHdr(pWal, pChanged); - if( badHdr ){ - /* If the wal-index header is still malformed even while holding - ** a WRITE lock, it can only mean that the header is corrupted and - ** needs to be reconstructed. So run recovery to do exactly that. - */ - rc = walIndexRecover(pWal); - *pChanged = 1; - } - } - if( bWriteLock==0 ){ - pWal->writeLock = 0; - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); - } + setDefaultSyncFlag(pBt, SQLITE_DEFAULT_SYNCHRONOUS+1); + } +#endif + + /* EVIDENCE-OF: R-15465-20813 The maximum and minimum embedded payload + ** fractions and the leaf payload fraction values must be 64, 32, and 32. + ** + ** The original design allowed these amounts to vary, but as of + ** version 3.6.0, we require them to be fixed. + */ + if( memcmp(&page1[21], "\100\040\040",3)!=0 ){ + goto page1_init_failed; + } + /* EVIDENCE-OF: R-51873-39618 The page size for a database file is + ** determined by the 2-byte integer located at an offset of 16 bytes from + ** the beginning of the database file. */ + pageSize = (page1[16]<<8) | (page1[17]<<16); + /* EVIDENCE-OF: R-25008-21688 The size of a page is a power of two + ** between 512 and 65536 inclusive. */ + if( ((pageSize-1)&pageSize)!=0 + || pageSize>SQLITE_MAX_PAGE_SIZE + || pageSize<=256 + ){ + goto page1_init_failed; + } + pBt->btsFlags |= BTS_PAGESIZE_FIXED; + assert( (pageSize & 7)==0 ); + /* EVIDENCE-OF: R-59310-51205 The "reserved space" size in the 1-byte + ** integer at offset 20 is the number of bytes of space at the end of + ** each page to reserve for extensions. + ** + ** EVIDENCE-OF: R-37497-42412 The size of the reserved region is + ** determined by the one-byte unsigned integer found at an offset of 20 + ** into the database file header. */ + usableSize = pageSize - page1[20]; + if( (u32)pageSize!=pBt->pageSize ){ + /* After reading the first page of the database assuming a page size + ** of BtShared.pageSize, we have discovered that the page-size is + ** actually pageSize. Unlock the database, leave pBt->pPage1 at + ** zero and return SQLITE_OK. The caller will call this function + ** again with the correct page-size. + */ + releasePageOne(pPage1); + pBt->usableSize = usableSize; + pBt->pageSize = pageSize; + freeTempSpace(pBt); + rc = sqlcipher_sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, + pageSize-usableSize); + return rc; + } + if( nPage>nPageFile ){ + if( sqlcipher_sqlite3WritableSchema(pBt->db)==0 ){ + rc = SQLITE_CORRUPT_BKPT; + goto page1_init_failed; + }else{ + nPage = nPageFile; } } + /* EVIDENCE-OF: R-28312-64704 However, the usable size is not allowed to + ** be less than 480. In other words, if the page size is 512, then the + ** reserved space size cannot exceed 32. */ + if( usableSize<480 ){ + goto page1_init_failed; + } + pBt->pageSize = pageSize; + pBt->usableSize = usableSize; +#ifndef SQLITE_OMIT_AUTOVACUUM + pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0); + pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0); +#endif } - /* If the header is read successfully, check the version number to make - ** sure the wal-index was not constructed with some future format that - ** this version of SQLite cannot understand. + /* maxLocal is the maximum amount of payload to store locally for + ** a cell. Make sure it is small enough so that at least minFanout + ** cells can will fit on one page. We assume a 10-byte page header. + ** Besides the payload, the cell must store: + ** 2-byte pointer to the cell + ** 4-byte child pointer + ** 9-byte nKey value + ** 4-byte nData value + ** 4-byte overflow page pointer + ** So a cell consists of a 2-byte pointer, a header which is as much as + ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow + ** page pointer. */ - if( badHdr==0 && pWal->hdr.iVersion!=WALINDEX_MAX_VERSION ){ - rc = SQLITE_CANTOPEN_BKPT; - } - if( pWal->bShmUnreliable ){ - if( rc!=SQLITE_OK ){ - walIndexClose(pWal, 0); - pWal->bShmUnreliable = 0; - assert( pWal->nWiData>0 && pWal->apWiData[0]==0 ); - /* walIndexRecover() might have returned SHORT_READ if a concurrent - ** writer truncated the WAL out from under it. If that happens, it - ** indicates that a writer has fixed the SHM file for us, so retry */ - if( rc==SQLITE_IOERR_SHORT_READ ) rc = WAL_RETRY; - } - pWal->exclusiveMode = WAL_NORMAL_MODE; + pBt->maxLocal = (u16)((pBt->usableSize-12)*64/255 - 23); + pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23); + pBt->maxLeaf = (u16)(pBt->usableSize - 35); + pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23); + if( pBt->maxLocal>127 ){ + pBt->max1bytePayload = 127; + }else{ + pBt->max1bytePayload = (u8)pBt->maxLocal; } + assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); + pBt->pPage1 = pPage1; + pBt->nPage = nPage; + return SQLITE_OK; +page1_init_failed: + releasePageOne(pPage1); + pBt->pPage1 = 0; return rc; } +#ifndef NDEBUG /* -** Open a transaction in a connection where the shared-memory is read-only -** and where we cannot verify that there is a separate write-capable connection -** on hand to keep the shared-memory up-to-date with the WAL file. -** -** This can happen, for example, when the shared-memory is implemented by -** memory-mapping a *-shm file, where a prior writer has shut down and -** left the *-shm file on disk, and now the present connection is trying -** to use that database but lacks write permission on the *-shm file. -** Other scenarios are also possible, depending on the VFS implementation. -** -** Precondition: -** -** The *-wal file has been read and an appropriate wal-index has been -** constructed in pWal->apWiData[] using heap memory instead of shared -** memory. +** Return the number of cursors open on pBt. This is for use +** in assert() expressions, so it is only compiled if NDEBUG is not +** defined. ** -** If this function returns SQLITE_OK, then the read transaction has -** been successfully opened. In this case output variable (*pChanged) -** is set to true before returning if the caller should discard the -** contents of the page cache before proceeding. Or, if it returns -** WAL_RETRY, then the heap memory wal-index has been discarded and -** the caller should retry opening the read transaction from the -** beginning (including attempting to map the *-shm file). +** Only write cursors are counted if wrOnly is true. If wrOnly is +** false then all cursors are counted. ** -** If an error occurs, an SQLite error code is returned. +** For the purposes of this routine, a cursor is any cursor that +** is capable of reading or writing to the database. Cursors that +** have been tripped into the CURSOR_FAULT state are not counted. */ -static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ - i64 szWal; /* Size of wal file on disk in bytes */ - i64 iOffset; /* Current offset when reading wal file */ - u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ - u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ - int szFrame; /* Number of bytes in buffer aFrame[] */ - u8 *aData; /* Pointer to data part of aFrame buffer */ - volatile void *pDummy; /* Dummy argument for xShmMap */ - int rc; /* Return code */ - u32 aSaveCksum[2]; /* Saved copy of pWal->hdr.aFrameCksum */ - - assert( pWal->bShmUnreliable ); - assert( pWal->readOnly & WAL_SHM_RDONLY ); - assert( pWal->nWiData>0 && pWal->apWiData[0] ); - - /* Take WAL_READ_LOCK(0). This has the effect of preventing any - ** writers from running a checkpoint, but does not stop them - ** from running recovery. */ - rc = walLockShared(pWal, WAL_READ_LOCK(0)); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_BUSY ) rc = WAL_RETRY; - goto begin_unreliable_shm_out; - } - pWal->readLock = 0; - - /* Check to see if a separate writer has attached to the shared-memory area, - ** thus making the shared-memory "reliable" again. Do this by invoking - ** the xShmMap() routine of the VFS and looking to see if the return - ** is SQLITE_READONLY instead of SQLITE_READONLY_CANTINIT. - ** - ** If the shared-memory is now "reliable" return WAL_RETRY, which will - ** cause the heap-memory WAL-index to be discarded and the actual - ** shared memory to be used in its place. - ** - ** This step is important because, even though this connection is holding - ** the WAL_READ_LOCK(0) which prevents a checkpoint, a writer might - ** have already checkpointed the WAL file and, while the current - ** is active, wrap the WAL and start overwriting frames that this - ** process wants to use. - ** - ** Once sqlcipher_sqlite3OsShmMap() has been called for an sqlcipher_sqlite3_file and has - ** returned any SQLITE_READONLY value, it must return only SQLITE_READONLY - ** or SQLITE_READONLY_CANTINIT or some error for all subsequent invocations, - ** even if some external agent does a "chmod" to make the shared-memory - ** writable by us, until sqlcipher_sqlite3OsShmUnmap() has been called. - ** This is a requirement on the VFS implementation. - */ - rc = sqlcipher_sqlite3OsShmMap(pWal->pDbFd, 0, WALINDEX_PGSZ, 0, &pDummy); - assert( rc!=SQLITE_OK ); /* SQLITE_OK not possible for read-only connection */ - if( rc!=SQLITE_READONLY_CANTINIT ){ - rc = (rc==SQLITE_READONLY ? WAL_RETRY : rc); - goto begin_unreliable_shm_out; - } - - /* We reach this point only if the real shared-memory is still unreliable. - ** Assume the in-memory WAL-index substitute is correct and load it - ** into pWal->hdr. - */ - memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr)); - - /* Make sure some writer hasn't come in and changed the WAL file out - ** from under us, then disconnected, while we were not looking. - */ - rc = sqlcipher_sqlite3OsFileSize(pWal->pWalFd, &szWal); - if( rc!=SQLITE_OK ){ - goto begin_unreliable_shm_out; - } - if( szWalhdr.mxFrame==0 ? SQLITE_OK : WAL_RETRY); - goto begin_unreliable_shm_out; - } - - /* Check the salt keys at the start of the wal file still match. */ - rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); - if( rc!=SQLITE_OK ){ - goto begin_unreliable_shm_out; - } - if( memcmp(&pWal->hdr.aSalt, &aBuf[16], 8) ){ - /* Some writer has wrapped the WAL file while we were not looking. - ** Return WAL_RETRY which will cause the in-memory WAL-index to be - ** rebuilt. */ - rc = WAL_RETRY; - goto begin_unreliable_shm_out; +static int countValidCursors(BtShared *pBt, int wrOnly){ + BtCursor *pCur; + int r = 0; + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + if( (wrOnly==0 || (pCur->curFlags & BTCF_WriteFlag)!=0) + && pCur->eState!=CURSOR_FAULT ) r++; } + return r; +} +#endif - /* Allocate a buffer to read frames into */ - szFrame = pWal->hdr.szPage + WAL_FRAME_HDRSIZE; - aFrame = (u8 *)sqlcipher_sqlite3_malloc64(szFrame); - if( aFrame==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto begin_unreliable_shm_out; +/* +** If there are no outstanding cursors and we are not in the middle +** of a transaction but there is a read lock on the database, then +** this routine unrefs the first page of the database file which +** has the effect of releasing the read lock. +** +** If there is a transaction in progress, this routine is a no-op. +*/ +static void unlockBtreeIfUnused(BtShared *pBt){ + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + assert( countValidCursors(pBt,0)==0 || pBt->inTransaction>TRANS_NONE ); + if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){ + MemPage *pPage1 = pBt->pPage1; + assert( pPage1->aData ); + assert( sqlcipher_sqlite3PagerRefcount(pBt->pPager)==1 ); + pBt->pPage1 = 0; + releasePageOne(pPage1); } - aData = &aFrame[WAL_FRAME_HDRSIZE]; - - /* Check to see if a complete transaction has been appended to the - ** wal file since the heap-memory wal-index was created. If so, the - ** heap-memory wal-index is discarded and WAL_RETRY returned to - ** the caller. */ - aSaveCksum[0] = pWal->hdr.aFrameCksum[0]; - aSaveCksum[1] = pWal->hdr.aFrameCksum[1]; - for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->hdr.szPage); - iOffset+szFrame<=szWal; - iOffset+=szFrame - ){ - u32 pgno; /* Database page number for frame */ - u32 nTruncate; /* dbsize field from frame header */ +} - /* Read and decode the next log frame. */ - rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); - if( rc!=SQLITE_OK ) break; - if( !walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame) ) break; +/* +** If pBt points to an empty file then convert that empty file +** into a new empty database by initializing the first page of +** the database. +*/ +static int newDatabase(BtShared *pBt){ + MemPage *pP1; + unsigned char *data; + int rc; - /* If nTruncate is non-zero, then a complete transaction has been - ** appended to this wal file. Set rc to WAL_RETRY and break out of - ** the loop. */ - if( nTruncate ){ - rc = WAL_RETRY; - break; - } + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + if( pBt->nPage>0 ){ + return SQLITE_OK; } - pWal->hdr.aFrameCksum[0] = aSaveCksum[0]; - pWal->hdr.aFrameCksum[1] = aSaveCksum[1]; + pP1 = pBt->pPage1; + assert( pP1!=0 ); + data = pP1->aData; + rc = sqlcipher_sqlite3PagerWrite(pP1->pDbPage); + if( rc ) return rc; + memcpy(data, zMagicHeader, sizeof(zMagicHeader)); + assert( sizeof(zMagicHeader)==16 ); + data[16] = (u8)((pBt->pageSize>>8)&0xff); + data[17] = (u8)((pBt->pageSize>>16)&0xff); + data[18] = 1; + data[19] = 1; + assert( pBt->usableSize<=pBt->pageSize && pBt->usableSize+255>=pBt->pageSize); + data[20] = (u8)(pBt->pageSize - pBt->usableSize); + data[21] = 64; + data[22] = 32; + data[23] = 32; + memset(&data[24], 0, 100-24); + zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA ); + pBt->btsFlags |= BTS_PAGESIZE_FIXED; +#ifndef SQLITE_OMIT_AUTOVACUUM + assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 ); + assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 ); + put4byte(&data[36 + 4*4], pBt->autoVacuum); + put4byte(&data[36 + 7*4], pBt->incrVacuum); +#endif + pBt->nPage = 1; + data[31] = 1; + return SQLITE_OK; +} - begin_unreliable_shm_out: - sqlcipher_sqlite3_free(aFrame); - if( rc!=SQLITE_OK ){ - int i; - for(i=0; inWiData; i++){ - sqlcipher_sqlite3_free((void*)pWal->apWiData[i]); - pWal->apWiData[i] = 0; - } - pWal->bShmUnreliable = 0; - sqlcipher_sqlite3WalEndReadTransaction(pWal); - *pChanged = 1; - } +/* +** Initialize the first page of the database file (creating a database +** consisting of a single page and no schema objects). Return SQLITE_OK +** if successful, or an SQLite error code otherwise. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeNewDb(Btree *p){ + int rc; + sqlcipher_sqlite3BtreeEnter(p); + p->pBt->nPage = 0; + rc = newDatabase(p->pBt); + sqlcipher_sqlite3BtreeLeave(p); return rc; } /* -** Attempt to start a read transaction. This might fail due to a race or -** other transient condition. When that happens, it returns WAL_RETRY to -** indicate to the caller that it is safe to retry immediately. -** -** On success return SQLITE_OK. On a permanent failure (such an -** I/O error or an SQLITE_BUSY because another process is running -** recovery) return a positive error code. -** -** The useWal parameter is true to force the use of the WAL and disable -** the case where the WAL is bypassed because it has been completely -** checkpointed. If useWal==0 then this routine calls walIndexReadHdr() -** to make a copy of the wal-index header into pWal->hdr. If the -** wal-index header has changed, *pChanged is set to 1 (as an indication -** to the caller that the local page cache is obsolete and needs to be -** flushed.) When useWal==1, the wal-index header is assumed to already -** be loaded and the pChanged parameter is unused. +** Attempt to start a new transaction. A write-transaction +** is started if the second argument is nonzero, otherwise a read- +** transaction. If the second argument is 2 or more and exclusive +** transaction is started, meaning that no other process is allowed +** to access the database. A preexisting transaction may not be +** upgraded to exclusive by calling this routine a second time - the +** exclusivity flag only works for a new transaction. ** -** The caller must set the cnt parameter to the number of prior calls to -** this routine during the current read attempt that returned WAL_RETRY. -** This routine will start taking more aggressive measures to clear the -** race conditions after multiple WAL_RETRY returns, and after an excessive -** number of errors will ultimately return SQLITE_PROTOCOL. The -** SQLITE_PROTOCOL return indicates that some other process has gone rogue -** and is not honoring the locking protocol. There is a vanishingly small -** chance that SQLITE_PROTOCOL could be returned because of a run of really -** bad luck when there is lots of contention for the wal-index, but that -** possibility is so small that it can be safely neglected, we believe. +** A write-transaction must be started before attempting any +** changes to the database. None of the following routines +** will work unless a transaction is started first: ** -** On success, this routine obtains a read lock on -** WAL_READ_LOCK(pWal->readLock). The pWal->readLock integer is -** in the range 0 <= pWal->readLock < WAL_NREADER. If pWal->readLock==(-1) -** that means the Wal does not hold any read lock. The reader must not -** access any database page that is modified by a WAL frame up to and -** including frame number aReadMark[pWal->readLock]. The reader will -** use WAL frames up to and including pWal->hdr.mxFrame if pWal->readLock>0 -** Or if pWal->readLock==0, then the reader will ignore the WAL -** completely and get all content directly from the database file. -** If the useWal parameter is 1 then the WAL will never be ignored and -** this routine will always set pWal->readLock>0 on success. -** When the read transaction is completed, the caller must release the -** lock on WAL_READ_LOCK(pWal->readLock) and set pWal->readLock to -1. +** sqlcipher_sqlite3BtreeCreateTable() +** sqlcipher_sqlite3BtreeCreateIndex() +** sqlcipher_sqlite3BtreeClearTable() +** sqlcipher_sqlite3BtreeDropTable() +** sqlcipher_sqlite3BtreeInsert() +** sqlcipher_sqlite3BtreeDelete() +** sqlcipher_sqlite3BtreeUpdateMeta() ** -** This routine uses the nBackfill and aReadMark[] fields of the header -** to select a particular WAL_READ_LOCK() that strives to let the -** checkpoint process do as much work as possible. This routine might -** update values of the aReadMark[] array in the header, but if it does -** so it takes care to hold an exclusive lock on the corresponding -** WAL_READ_LOCK() while changing values. +** If an initial attempt to acquire the lock fails because of lock contention +** and the database was previously unlocked, then invoke the busy handler +** if there is one. But if there was previously a read-lock, do not +** invoke the busy handler - just return SQLITE_BUSY. SQLITE_BUSY is +** returned when there is already a read-lock in order to avoid a deadlock. +** +** Suppose there are two processes A and B. A has a read lock and B has +** a reserved lock. B tries to promote to exclusive but is blocked because +** of A's read lock. A tries to promote to reserved but is blocked by B. +** One or the other of the two processes must give way or there can be +** no progress. By returning SQLITE_BUSY and not invoking the busy callback +** when A already has a read lock, we encourage A to give up and let B +** proceed. */ -static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ - volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ - u32 mxReadMark; /* Largest aReadMark[] value */ - int mxI; /* Index of largest aReadMark[] value */ - int i; /* Loop counter */ - int rc = SQLITE_OK; /* Return code */ - u32 mxFrame; /* Wal frame to lock to */ - - assert( pWal->readLock<0 ); /* Not currently locked */ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ + BtShared *pBt = p->pBt; + Pager *pPager = pBt->pPager; + int rc = SQLITE_OK; - /* useWal may only be set for read/write connections */ - assert( (pWal->readOnly & WAL_SHM_RDONLY)==0 || useWal==0 ); + sqlcipher_sqlite3BtreeEnter(p); + btreeIntegrity(p); - /* Take steps to avoid spinning forever if there is a protocol error. - ** - ** Circumstances that cause a RETRY should only last for the briefest - ** instances of time. No I/O or other system calls are done while the - ** locks are held, so the locks should not be held for very long. But - ** if we are unlucky, another process that is holding a lock might get - ** paged out or take a page-fault that is time-consuming to resolve, - ** during the few nanoseconds that it is holding the lock. In that case, - ** it might take longer than normal for the lock to free. - ** - ** After 5 RETRYs, we begin calling sqlcipher_sqlite3OsSleep(). The first few - ** calls to sqlcipher_sqlite3OsSleep() have a delay of 1 microsecond. Really this - ** is more of a scheduler yield than an actual delay. But on the 10th - ** an subsequent retries, the delays start becoming longer and longer, - ** so that on the 100th (and last) RETRY we delay for 323 milliseconds. - ** The total delay time before giving up is less than 10 seconds. + /* If the btree is already in a write-transaction, or it + ** is already in a read-transaction and a read-transaction + ** is requested, this is a no-op. */ - if( cnt>5 ){ - int nDelay = 1; /* Pause time in microseconds */ - if( cnt>100 ){ - VVA_ONLY( pWal->lockError = 1; ) - return SQLITE_PROTOCOL; - } - if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; - sqlcipher_sqlite3OsSleep(pWal->pVfs, nDelay); + if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){ + goto trans_begun; } + assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 ); - if( !useWal ){ - assert( rc==SQLITE_OK ); - if( pWal->bShmUnreliable==0 ){ - rc = walIndexReadHdr(pWal, pChanged); - } - if( rc==SQLITE_BUSY ){ - /* If there is not a recovery running in another thread or process - ** then convert BUSY errors to WAL_RETRY. If recovery is known to - ** be running, convert BUSY to BUSY_RECOVERY. There is a race here - ** which might cause WAL_RETRY to be returned even if BUSY_RECOVERY - ** would be technically correct. But the race is benign since with - ** WAL_RETRY this routine will be called again and will probably be - ** right on the second iteration. - */ - if( pWal->apWiData[0]==0 ){ - /* This branch is taken when the xShmMap() method returns SQLITE_BUSY. - ** We assume this is a transient condition, so return WAL_RETRY. The - ** xShmMap() implementation used by the default unix and win32 VFS - ** modules may return SQLITE_BUSY due to a race condition in the - ** code that determines whether or not the shared-memory region - ** must be zeroed before the requested page is returned. - */ - rc = WAL_RETRY; - }else if( SQLITE_OK==(rc = walLockShared(pWal, WAL_RECOVER_LOCK)) ){ - walUnlockShared(pWal, WAL_RECOVER_LOCK); - rc = WAL_RETRY; - }else if( rc==SQLITE_BUSY ){ - rc = SQLITE_BUSY_RECOVERY; + if( (p->db->flags & SQLITE_ResetDatabase) + && sqlcipher_sqlite3PagerIsreadonly(pPager)==0 + ){ + pBt->btsFlags &= ~BTS_READ_ONLY; + } + + /* Write transactions are not possible on a read-only database */ + if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){ + rc = SQLITE_READONLY; + goto trans_begun; + } + +#ifndef SQLITE_OMIT_SHARED_CACHE + { + sqlcipher_sqlite3 *pBlock = 0; + /* If another database handle has already opened a write transaction + ** on this shared-btree structure and a second write transaction is + ** requested, return SQLITE_LOCKED. + */ + if( (wrflag && pBt->inTransaction==TRANS_WRITE) + || (pBt->btsFlags & BTS_PENDING)!=0 + ){ + pBlock = pBt->pWriter->db; + }else if( wrflag>1 ){ + BtLock *pIter; + for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ + if( pIter->pBtree!=p ){ + pBlock = pIter->pBtree->db; + break; + } } } - if( rc!=SQLITE_OK ){ - return rc; - } - else if( pWal->bShmUnreliable ){ - return walBeginShmUnreliable(pWal, pChanged); + if( pBlock ){ + sqlcipher_sqlite3ConnectionBlocked(p->db, pBlock); + rc = SQLITE_LOCKED_SHAREDCACHE; + goto trans_begun; } } +#endif - assert( pWal->nWiData>0 ); - assert( pWal->apWiData[0]!=0 ); - pInfo = walCkptInfo(pWal); - if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame -#ifdef SQLITE_ENABLE_SNAPSHOT - && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) + /* Any read-only or read-write transaction implies a read-lock on + ** page 1. So if some other shared-cache client already has a write-lock + ** on page 1, the transaction cannot be opened. */ + rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK); + if( SQLITE_OK!=rc ) goto trans_begun; + + pBt->btsFlags &= ~BTS_INITIALLY_EMPTY; + if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY; + do { + sqlcipher_sqlite3PagerWalDb(pPager, p->db); + +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + /* If transitioning from no transaction directly to a write transaction, + ** block for the WRITER lock first if possible. */ + if( pBt->pPage1==0 && wrflag ){ + assert( pBt->inTransaction==TRANS_NONE ); + rc = sqlcipher_sqlite3PagerWalWriteLock(pPager, 1); + if( rc!=SQLITE_BUSY && rc!=SQLITE_OK ) break; + } #endif - ){ - /* The WAL has been completely backfilled (or it is empty). - ** and can be safely ignored. + + /* Call lockBtree() until either pBt->pPage1 is populated or + ** lockBtree() returns something other than SQLITE_OK. lockBtree() + ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after + ** reading page 1 it discovers that the page-size of the database + ** file is not pBt->pageSize. In this case lockBtree() will update + ** pBt->pageSize to the page-size of the file on disk. */ - rc = walLockShared(pWal, WAL_READ_LOCK(0)); - walShmBarrier(pWal); - if( rc==SQLITE_OK ){ - if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ - /* It is not safe to allow the reader to continue here if frames - ** may have been appended to the log before READ_LOCK(0) was obtained. - ** When holding READ_LOCK(0), the reader ignores the entire log file, - ** which implies that the database file contains a trustworthy - ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from - ** happening, this is usually correct. - ** - ** However, if frames have been appended to the log (or if the log - ** is wrapped and written for that matter) before the READ_LOCK(0) - ** is obtained, that is not necessarily true. A checkpointer may - ** have started to backfill the appended frames but crashed before - ** it finished. Leaving a corrupt image in the database file. - */ - walUnlockShared(pWal, WAL_READ_LOCK(0)); - return WAL_RETRY; + while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) ); + + if( rc==SQLITE_OK && wrflag ){ + if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){ + rc = SQLITE_READONLY; + }else{ + rc = sqlcipher_sqlite3PagerBegin(pPager, wrflag>1, sqlcipher_sqlite3TempInMemory(p->db)); + if( rc==SQLITE_OK ){ + rc = newDatabase(pBt); + }else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){ + /* if there was no transaction opened when this function was + ** called and SQLITE_BUSY_SNAPSHOT is returned, change the error + ** code to SQLITE_BUSY. */ + rc = SQLITE_BUSY; + } } - pWal->readLock = 0; - return SQLITE_OK; - }else if( rc!=SQLITE_BUSY ){ - return rc; } - } - /* If we get this far, it means that the reader will want to use - ** the WAL to get at content from recent commits. The job now is - ** to select one of the aReadMark[] entries that is closest to - ** but not exceeding pWal->hdr.mxFrame and lock that entry. - */ - mxReadMark = 0; - mxI = 0; - mxFrame = pWal->hdr.mxFrame; -#ifdef SQLITE_ENABLE_SNAPSHOT - if( pWal->pSnapshot && pWal->pSnapshot->mxFramepSnapshot->mxFrame; - } + if( rc!=SQLITE_OK ){ + (void)sqlcipher_sqlite3PagerWalWriteLock(pPager, 0); + unlockBtreeIfUnused(pBt); + } + }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && + btreeInvokeBusyHandler(pBt) ); + sqlcipher_sqlite3PagerWalDb(pPager, 0); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; +#endif + + if( rc==SQLITE_OK ){ + if( p->inTrans==TRANS_NONE ){ + pBt->nTransaction++; +#ifndef SQLITE_OMIT_SHARED_CACHE + if( p->sharable ){ + assert( p->lock.pBtree==p && p->lock.iTable==1 ); + p->lock.eLock = READ_LOCK; + p->lock.pNext = pBt->pLock; + pBt->pLock = &p->lock; + } #endif - for(i=1; iaReadMark+i); - if( mxReadMark<=thisMark && thisMark<=mxFrame ){ - assert( thisMark!=READMARK_NOT_USED ); - mxReadMark = thisMark; - mxI = i; } - } - if( (pWal->readOnly & WAL_SHM_RDONLY)==0 - && (mxReadMarkaReadMark+i,mxFrame); - mxReadMark = mxFrame; - mxI = i; - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); - break; - }else if( rc!=SQLITE_BUSY ){ - return rc; + p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); + if( p->inTrans>pBt->inTransaction ){ + pBt->inTransaction = p->inTrans; + } + if( wrflag ){ + MemPage *pPage1 = pBt->pPage1; +#ifndef SQLITE_OMIT_SHARED_CACHE + assert( !pBt->pWriter ); + pBt->pWriter = p; + pBt->btsFlags &= ~BTS_EXCLUSIVE; + if( wrflag>1 ) pBt->btsFlags |= BTS_EXCLUSIVE; +#endif + + /* If the db-size header field is incorrect (as it may be if an old + ** client has been writing the database file), update it now. Doing + ** this sooner rather than later means the database size can safely + ** re-read the database size from page 1 if a savepoint or transaction + ** rollback occurs within the transaction. + */ + if( pBt->nPage!=get4byte(&pPage1->aData[28]) ){ + rc = sqlcipher_sqlite3PagerWrite(pPage1->pDbPage); + if( rc==SQLITE_OK ){ + put4byte(&pPage1->aData[28], pBt->nPage); + } } } } - if( mxI==0 ){ - assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); - return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; + +trans_begun: + if( rc==SQLITE_OK ){ + if( pSchemaVersion ){ + *pSchemaVersion = get4byte(&pBt->pPage1->aData[40]); + } + if( wrflag ){ + /* This call makes sure that the pager has the correct number of + ** open savepoints. If the second parameter is greater than 0 and + ** the sub-journal is not already open, then it will be opened here. + */ + rc = sqlcipher_sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint); + } } - rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); - if( rc ){ - return rc==SQLITE_BUSY ? WAL_RETRY : rc; + btreeIntegrity(p); + sqlcipher_sqlite3BtreeLeave(p); + return rc; +} + +#ifndef SQLITE_OMIT_AUTOVACUUM + +/* +** Set the pointer-map entries for all children of page pPage. Also, if +** pPage contains cells that point to overflow pages, set the pointer +** map entries for the overflow pages as well. +*/ +static int setChildPtrmaps(MemPage *pPage){ + int i; /* Counter variable */ + int nCell; /* Number of cells in page pPage */ + int rc; /* Return code */ + BtShared *pBt = pPage->pBt; + Pgno pgno = pPage->pgno; + + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + rc = pPage->isInit ? SQLITE_OK : btreeInitPage(pPage); + if( rc!=SQLITE_OK ) return rc; + nCell = pPage->nCell; + + for(i=0; ileaf ){ + Pgno childPgno = get4byte(pCell); + ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); + } } - /* Now that the read-lock has been obtained, check that neither the - ** value in the aReadMark[] array or the contents of the wal-index - ** header have changed. - ** - ** It is necessary to check that the wal-index header did not change - ** between the time it was read and when the shared-lock was obtained - ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility - ** that the log file may have been wrapped by a writer, or that frames - ** that occur later in the log than pWal->hdr.mxFrame may have been - ** copied into the database by a checkpointer. If either of these things - ** happened, then reading the database with the current value of - ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry - ** instead. - ** - ** Before checking that the live wal-index header has not changed - ** since it was read, set Wal.minFrame to the first frame in the wal - ** file that has not yet been checkpointed. This client will not need - ** to read any frames earlier than minFrame from the wal file - they - ** can be safely read directly from the database file. - ** - ** Because a ShmBarrier() call is made between taking the copy of - ** nBackfill and checking that the wal-header in shared-memory still - ** matches the one cached in pWal->hdr, it is guaranteed that the - ** checkpointer that set nBackfill was not working with a wal-index - ** header newer than that cached in pWal->hdr. If it were, that could - ** cause a problem. The checkpointer could omit to checkpoint - ** a version of page X that lies before pWal->minFrame (call that version - ** A) on the basis that there is a newer version (version B) of the same - ** page later in the wal file. But if version B happens to like past - ** frame pWal->hdr.mxFrame - then the client would incorrectly assume - ** that it can read version A from the database file. However, since - ** we can guarantee that the checkpointer that set nBackfill could not - ** see any pages past pWal->hdr.mxFrame, this problem does not come up. - */ - pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; - walShmBarrier(pWal); - if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark - || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) - ){ - walUnlockShared(pWal, WAL_READ_LOCK(mxI)); - return WAL_RETRY; - }else{ - assert( mxReadMark<=pWal->hdr.mxFrame ); - pWal->readLock = (i16)mxI; + + if( !pPage->leaf ){ + Pgno childPgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); + ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); } + return rc; } -#ifdef SQLITE_ENABLE_SNAPSHOT /* -** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted -** variable so that older snapshots can be accessed. To do this, loop -** through all wal frames from nBackfillAttempted to (nBackfill+1), -** comparing their content to the corresponding page with the database -** file, if any. Set nBackfillAttempted to the frame number of the -** first frame for which the wal file content matches the db file. +** Somewhere on pPage is a pointer to page iFrom. Modify this pointer so +** that it points to iTo. Parameter eType describes the type of pointer to +** be modified, as follows: ** -** This is only really safe if the file-system is such that any page -** writes made by earlier checkpointers were atomic operations, which -** is not always true. It is also possible that nBackfillAttempted -** may be left set to a value larger than expected, if a wal frame -** contains content that duplicate of an earlier version of the same -** page. +** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child +** page of pPage. ** -** SQLITE_OK is returned if successful, or an SQLite error code if an -** error occurs. It is not an error if nBackfillAttempted cannot be -** decreased at all. +** PTRMAP_OVERFLOW1: pPage is a btree-page. The pointer points at an overflow +** page pointed to by one of the cells on pPage. +** +** PTRMAP_OVERFLOW2: pPage is an overflow-page. The pointer points at the next +** overflow page in the list. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotRecover(Wal *pWal){ - int rc; +static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); + if( eType==PTRMAP_OVERFLOW2 ){ + /* The pointer is always the first 4 bytes of the page in this case. */ + if( get4byte(pPage->aData)!=iFrom ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + put4byte(pPage->aData, iTo); + }else{ + int i; + int nCell; + int rc; - assert( pWal->readLock>=0 ); - rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); - if( rc==SQLITE_OK ){ - volatile WalCkptInfo *pInfo = walCkptInfo(pWal); - int szPage = (int)pWal->szPage; - i64 szDb; /* Size of db file in bytes */ + rc = pPage->isInit ? SQLITE_OK : btreeInitPage(pPage); + if( rc ) return rc; + nCell = pPage->nCell; - rc = sqlcipher_sqlite3OsFileSize(pWal->pDbFd, &szDb); - if( rc==SQLITE_OK ){ - void *pBuf1 = sqlcipher_sqlite3_malloc(szPage); - void *pBuf2 = sqlcipher_sqlite3_malloc(szPage); - if( pBuf1==0 || pBuf2==0 ){ - rc = SQLITE_NOMEM; + for(i=0; ixParseCell(pPage, pCell, &info); + if( info.nLocal pPage->aData+pPage->pBt->usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + if( iFrom==get4byte(pCell+info.nSize-4) ){ + put4byte(pCell+info.nSize-4, iTo); + break; + } + } }else{ - u32 i = pInfo->nBackfillAttempted; - for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){ - WalHashLoc sLoc; /* Hash table location */ - u32 pgno; /* Page number in db file */ - i64 iDbOff; /* Offset of db file entry */ - i64 iWalOff; /* Offset of wal file entry */ + if( get4byte(pCell)==iFrom ){ + put4byte(pCell, iTo); + break; + } + } + } - rc = walHashGet(pWal, walFramePage(i), &sLoc); - if( rc!=SQLITE_OK ) break; - pgno = sLoc.aPgno[i-sLoc.iZero]; - iDbOff = (i64)(pgno-1) * szPage; + if( i==nCell ){ + if( eType!=PTRMAP_BTREE || + get4byte(&pPage->aData[pPage->hdrOffset+8])!=iFrom ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + put4byte(&pPage->aData[pPage->hdrOffset+8], iTo); + } + } + return SQLITE_OK; +} - if( iDbOff+szPage<=szDb ){ - iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE; - rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff); - } +/* +** Move the open database page pDbPage to location iFreePage in the +** database. The pDbPage reference remains valid. +** +** The isCommit flag indicates that there is no need to remember that +** the journal needs to be sync()ed before database page pDbPage->pgno +** can be written to. The caller has already promised not to write to that +** page. +*/ +static int relocatePage( + BtShared *pBt, /* Btree */ + MemPage *pDbPage, /* Open page to move */ + u8 eType, /* Pointer map 'type' entry for pDbPage */ + Pgno iPtrPage, /* Pointer map 'page-no' entry for pDbPage */ + Pgno iFreePage, /* The location to move pDbPage to */ + int isCommit /* isCommit flag passed to sqlcipher_sqlite3PagerMovepage */ +){ + MemPage *pPtrPage; /* The page that contains a pointer to pDbPage */ + Pgno iDbPage = pDbPage->pgno; + Pager *pPager = pBt->pPager; + int rc; - if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){ - break; - } - } + assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 || + eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ); + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + assert( pDbPage->pBt==pBt ); + if( iDbPage<3 ) return SQLITE_CORRUPT_BKPT; - pInfo->nBackfillAttempted = i-1; - } - } + /* Move page iDbPage from its current location to page number iFreePage */ + TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", + iDbPage, iFreePage, iPtrPage, eType)); + rc = sqlcipher_sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage, isCommit); + if( rc!=SQLITE_OK ){ + return rc; + } + pDbPage->pgno = iFreePage; - sqlcipher_sqlite3_free(pBuf1); - sqlcipher_sqlite3_free(pBuf2); + /* If pDbPage was a btree-page, then it may have child pages and/or cells + ** that point to overflow pages. The pointer map entries for all these + ** pages need to be changed. + ** + ** If pDbPage is an overflow page, then the first 4 bytes may store a + ** pointer to a subsequent overflow page. If this is the case, then + ** the pointer map needs to be updated for the subsequent overflow page. + */ + if( eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ){ + rc = setChildPtrmaps(pDbPage); + if( rc!=SQLITE_OK ){ + return rc; + } + }else{ + Pgno nextOvfl = get4byte(pDbPage->aData); + if( nextOvfl!=0 ){ + ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage, &rc); + if( rc!=SQLITE_OK ){ + return rc; + } } - walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); } + /* Fix the database pointer on page iPtrPage that pointed at iDbPage so + ** that it points at iFreePage. Also fix the pointer map entry for + ** iPtrPage. + */ + if( eType!=PTRMAP_ROOTPAGE ){ + rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = sqlcipher_sqlite3PagerWrite(pPtrPage->pDbPage); + if( rc!=SQLITE_OK ){ + releasePage(pPtrPage); + return rc; + } + rc = modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType); + releasePage(pPtrPage); + if( rc==SQLITE_OK ){ + ptrmapPut(pBt, iFreePage, eType, iPtrPage, &rc); + } + } return rc; } -#endif /* SQLITE_ENABLE_SNAPSHOT */ + +/* Forward declaration required by incrVacuumStep(). */ +static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8); /* -** Begin a read transaction on the database. +** Perform a single step of an incremental-vacuum. If successful, return +** SQLITE_OK. If there is no work to do (and therefore no point in +** calling this function again), return SQLITE_DONE. Or, if an error +** occurs, return some other error code. ** -** This routine used to be called sqlcipher_sqlite3OpenSnapshot() and with good reason: -** it takes a snapshot of the state of the WAL and wal-index for the current -** instant in time. The current thread will continue to use this snapshot. -** Other threads might append new content to the WAL and wal-index but -** that extra content is ignored by the current thread. +** More specifically, this function attempts to re-organize the database so +** that the last page of the file currently in use is no longer in use. ** -** If the database contents have changes since the previous read -** transaction, then *pChanged is set to 1 before returning. The -** Pager layer will use this to know that its cache is stale and -** needs to be flushed. +** Parameter nFin is the number of pages that this database would contain +** were this function called until it returns SQLITE_DONE. +** +** If the bCommit parameter is non-zero, this function assumes that the +** caller will keep calling incrVacuumStep() until it returns SQLITE_DONE +** or an error. bCommit is passed true for an auto-vacuum-on-commit +** operation, or false for an incremental vacuum. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ - int rc; /* Return code */ - int cnt = 0; /* Number of TryBeginRead attempts */ -#ifdef SQLITE_ENABLE_SNAPSHOT - int bChanged = 0; - WalIndexHdr *pSnapshot = pWal->pSnapshot; -#endif +static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){ + Pgno nFreeList; /* Number of pages still on the free-list */ + int rc; - assert( pWal->ckptLock==0 ); + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + assert( iLastPg>nFin ); -#ifdef SQLITE_ENABLE_SNAPSHOT - if( pSnapshot ){ - if( memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){ - bChanged = 1; - } + if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){ + u8 eType; + Pgno iPtrPage; - /* It is possible that there is a checkpointer thread running - ** concurrent with this code. If this is the case, it may be that the - ** checkpointer has already determined that it will checkpoint - ** snapshot X, where X is later in the wal file than pSnapshot, but - ** has not yet set the pInfo->nBackfillAttempted variable to indicate - ** its intent. To avoid the race condition this leads to, ensure that - ** there is no checkpointer process by taking a shared CKPT lock - ** before checking pInfo->nBackfillAttempted. */ - (void)walEnableBlocking(pWal); - rc = walLockShared(pWal, WAL_CKPT_LOCK); - walDisableBlocking(pWal); + nFreeList = get4byte(&pBt->pPage1->aData[36]); + if( nFreeList==0 ){ + return SQLITE_DONE; + } + rc = ptrmapGet(pBt, iLastPg, &eType, &iPtrPage); if( rc!=SQLITE_OK ){ return rc; } - pWal->ckptLock = 1; - } -#endif + if( eType==PTRMAP_ROOTPAGE ){ + return SQLITE_CORRUPT_BKPT; + } - do{ - rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); - }while( rc==WAL_RETRY ); - testcase( (rc&0xff)==SQLITE_BUSY ); - testcase( (rc&0xff)==SQLITE_IOERR ); - testcase( rc==SQLITE_PROTOCOL ); - testcase( rc==SQLITE_OK ); + if( eType==PTRMAP_FREEPAGE ){ + if( bCommit==0 ){ + /* Remove the page from the files free-list. This is not required + ** if bCommit is non-zero. In that case, the free-list will be + ** truncated to zero after this function returns, so it doesn't + ** matter if it still contains some garbage entries. + */ + Pgno iFreePg; + MemPage *pFreePg; + rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, BTALLOC_EXACT); + if( rc!=SQLITE_OK ){ + return rc; + } + assert( iFreePg==iLastPg ); + releasePage(pFreePg); + } + } else { + Pgno iFreePg; /* Index of free page to move pLastPg to */ + MemPage *pLastPg; + u8 eMode = BTALLOC_ANY; /* Mode parameter for allocateBtreePage() */ + Pgno iNear = 0; /* nearby parameter for allocateBtreePage() */ -#ifdef SQLITE_ENABLE_SNAPSHOT - if( rc==SQLITE_OK ){ - if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){ - /* At this point the client has a lock on an aReadMark[] slot holding - ** a value equal to or smaller than pSnapshot->mxFrame, but pWal->hdr - ** is populated with the wal-index header corresponding to the head - ** of the wal file. Verify that pSnapshot is still valid before - ** continuing. Reasons why pSnapshot might no longer be valid: - ** - ** (1) The WAL file has been reset since the snapshot was taken. - ** In this case, the salt will have changed. + rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0); + if( rc!=SQLITE_OK ){ + return rc; + } + + /* If bCommit is zero, this loop runs exactly once and page pLastPg + ** is swapped with the first free page pulled off the free list. ** - ** (2) A checkpoint as been attempted that wrote frames past - ** pSnapshot->mxFrame into the database file. Note that the - ** checkpoint need not have completed for this to cause problems. + ** On the other hand, if bCommit is greater than zero, then keep + ** looping until a free-page located within the first nFin pages + ** of the file is found. */ - volatile WalCkptInfo *pInfo = walCkptInfo(pWal); - - assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 ); - assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame ); - - /* Check that the wal file has not been wrapped. Assuming that it has - ** not, also check that no checkpointer has attempted to checkpoint any - ** frames beyond pSnapshot->mxFrame. If either of these conditions are - ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr - ** with *pSnapshot and set *pChanged as appropriate for opening the - ** snapshot. */ - if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) - && pSnapshot->mxFrame>=pInfo->nBackfillAttempted - ){ - assert( pWal->readLock>0 ); - memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr)); - *pChanged = bChanged; - }else{ - rc = SQLITE_ERROR_SNAPSHOT; + if( bCommit==0 ){ + eMode = BTALLOC_LE; + iNear = nFin; } + do { + MemPage *pFreePg; + Pgno dbSize = btreePagecount(pBt); + rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode); + if( rc!=SQLITE_OK ){ + releasePage(pLastPg); + return rc; + } + releasePage(pFreePg); + if( iFreePg>dbSize ){ + releasePage(pLastPg); + return SQLITE_CORRUPT_BKPT; + } + }while( bCommit && iFreePg>nFin ); + assert( iFreePgminFrame = 1; - + rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, bCommit); + releasePage(pLastPg); if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3WalEndReadTransaction(pWal); + return rc; } } } - /* Release the shared CKPT lock obtained above. */ - if( pWal->ckptLock ){ - assert( pSnapshot ); - walUnlockShared(pWal, WAL_CKPT_LOCK); - pWal->ckptLock = 0; + if( bCommit==0 ){ + do { + iLastPg--; + }while( iLastPg==PENDING_BYTE_PAGE(pBt) || PTRMAP_ISPAGE(pBt, iLastPg) ); + pBt->bDoTruncate = 1; + pBt->nPage = iLastPg; } -#endif - return rc; + return SQLITE_OK; } /* -** Finish with a read transaction. All this does is release the -** read-lock. +** The database opened by the first argument is an auto-vacuum database +** nOrig pages in size containing nFree free pages. Return the expected +** size of the database in pages following an auto-vacuum operation. */ -SQLITE_PRIVATE void sqlcipher_sqlite3WalEndReadTransaction(Wal *pWal){ - sqlcipher_sqlite3WalEndWriteTransaction(pWal); - if( pWal->readLock>=0 ){ - walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); - pWal->readLock = -1; +static Pgno finalDbSize(BtShared *pBt, Pgno nOrig, Pgno nFree){ + int nEntry; /* Number of entries on one ptrmap page */ + Pgno nPtrmap; /* Number of PtrMap pages to be freed */ + Pgno nFin; /* Return value */ + + nEntry = pBt->usableSize/5; + nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry; + nFin = nOrig - nFree - nPtrmap; + if( nOrig>PENDING_BYTE_PAGE(pBt) && nFinhdr.mxFrame; /* Last page in WAL for this reader */ - int iHash; /* Used to loop through N hash tables */ - int iMinHash; +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeIncrVacuum(Btree *p){ + int rc; + BtShared *pBt = p->pBt; - /* This routine is only be called from within a read transaction. */ - assert( pWal->readLock>=0 || pWal->lockError ); + sqlcipher_sqlite3BtreeEnter(p); + assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE ); + if( !pBt->autoVacuum ){ + rc = SQLITE_DONE; + }else{ + Pgno nOrig = btreePagecount(pBt); + Pgno nFree = get4byte(&pBt->pPage1->aData[36]); + Pgno nFin = finalDbSize(pBt, nOrig, nFree); - /* If the "last page" field of the wal-index header snapshot is 0, then - ** no data will be read from the wal under any circumstances. Return early - ** in this case as an optimization. Likewise, if pWal->readLock==0, - ** then the WAL is ignored by the reader so return early, as if the - ** WAL were empty. - */ - if( iLast==0 || (pWal->readLock==0 && pWal->bShmUnreliable==0) ){ - *piRead = 0; - return SQLITE_OK; + if( nOrig=nOrig ){ + rc = SQLITE_CORRUPT_BKPT; + }else if( nFree>0 ){ + rc = saveAllCursors(pBt, 0, 0); + if( rc==SQLITE_OK ){ + invalidateAllOverflowCache(pBt); + rc = incrVacuumStep(pBt, nFin, nOrig, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3PagerWrite(pBt->pPage1->pDbPage); + put4byte(&pBt->pPage1->aData[28], pBt->nPage); + } + }else{ + rc = SQLITE_DONE; + } } + sqlcipher_sqlite3BtreeLeave(p); + return rc; +} - /* Search the hash table or tables for an entry matching page number - ** pgno. Each iteration of the following for() loop searches one - ** hash table (each hash table indexes up to HASHTABLE_NPAGE frames). - ** - ** This code might run concurrently to the code in walIndexAppend() - ** that adds entries to the wal-index (and possibly to this hash - ** table). This means the value just read from the hash - ** slot (aHash[iKey]) may have been added before or after the - ** current read transaction was opened. Values added after the - ** read transaction was opened may have been written incorrectly - - ** i.e. these slots may contain garbage data. However, we assume - ** that any slots written before the current read transaction was - ** opened remain unmodified. - ** - ** For the reasons above, the if(...) condition featured in the inner - ** loop of the following block is more stringent that would be required - ** if we had exclusive access to the hash-table: - ** - ** (aPgno[iFrame]==pgno): - ** This condition filters out normal hash-table collisions. - ** - ** (iFrame<=iLast): - ** This condition filters out entries that were added to the hash - ** table after the current read-transaction had started. - */ - iMinHash = walFramePage(pWal->minFrame); - for(iHash=walFramePage(iLast); iHash>=iMinHash; iHash--){ - WalHashLoc sLoc; /* Hash table location */ - int iKey; /* Hash slot index */ - int nCollide; /* Number of hash collisions remaining */ - int rc; /* Error code */ - u32 iH; +/* +** This routine is called prior to sqlcipher_sqlite3PagerCommit when a transaction +** is committed for an auto-vacuum database. +*/ +static int autoVacuumCommit(Btree *p){ + int rc = SQLITE_OK; + Pager *pPager; + BtShared *pBt; + sqlcipher_sqlite3 *db; + VVA_ONLY( int nRef ); - rc = walHashGet(pWal, iHash, &sLoc); - if( rc!=SQLITE_OK ){ - return rc; + assert( p!=0 ); + pBt = p->pBt; + pPager = pBt->pPager; + VVA_ONLY( nRef = sqlcipher_sqlite3PagerRefcount(pPager); ) + + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + invalidateAllOverflowCache(pBt); + assert(pBt->autoVacuum); + if( !pBt->incrVacuum ){ + Pgno nFin; /* Number of pages in database after autovacuuming */ + Pgno nFree; /* Number of pages on the freelist initially */ + Pgno nVac; /* Number of pages to vacuum */ + Pgno iFree; /* The next page to be freed */ + Pgno nOrig; /* Database size before freeing */ + + nOrig = btreePagecount(pBt); + if( PTRMAP_ISPAGE(pBt, nOrig) || nOrig==PENDING_BYTE_PAGE(pBt) ){ + /* It is not possible to create a database for which the final page + ** is either a pointer-map page or the pending-byte page. If one + ** is encountered, this indicates corruption. + */ + return SQLITE_CORRUPT_BKPT; } - nCollide = HASHTABLE_NSLOT; - iKey = walHash(pgno); - while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ - u32 iFrame = iH + sLoc.iZero; - if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH]==pgno ){ - assert( iFrame>iRead || CORRUPT_DB ); - iRead = iFrame; + + nFree = get4byte(&pBt->pPage1->aData[36]); + db = p->db; + if( db->xAutovacPages ){ + int iDb; + for(iDb=0; ALWAYS(iDbnDb); iDb++){ + if( db->aDb[iDb].pBt==p ) break; + } + nVac = db->xAutovacPages( + db->pAutovacPagesArg, + db->aDb[iDb].zDbSName, + nOrig, + nFree, + pBt->pageSize + ); + if( nVac>nFree ){ + nVac = nFree; } - if( (nCollide--)==0 ){ - return SQLITE_CORRUPT_BKPT; + if( nVac==0 ){ + return SQLITE_OK; } - iKey = walNextHash(iKey); + }else{ + nVac = nFree; } - if( iRead ) break; - } - -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT - /* If expensive assert() statements are available, do a linear search - ** of the wal-index file content. Make sure the results agree with the - ** result obtained using the hash indexes above. */ - { - u32 iRead2 = 0; - u32 iTest; - assert( pWal->bShmUnreliable || pWal->minFrame>0 ); - for(iTest=iLast; iTest>=pWal->minFrame && iTest>0; iTest--){ - if( walFramePgno(pWal, iTest)==pgno ){ - iRead2 = iTest; - break; + nFin = finalDbSize(pBt, nOrig, nVac); + if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT; + if( nFinnFin && rc==SQLITE_OK; iFree--){ + rc = incrVacuumStep(pBt, nFin, iFree, nVac==nFree); + } + if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){ + rc = sqlcipher_sqlite3PagerWrite(pBt->pPage1->pDbPage); + if( nVac==nFree ){ + put4byte(&pBt->pPage1->aData[32], 0); + put4byte(&pBt->pPage1->aData[36], 0); } + put4byte(&pBt->pPage1->aData[28], nFin); + pBt->bDoTruncate = 1; + pBt->nPage = nFin; + } + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3PagerRollback(pPager); } - assert( iRead==iRead2 ); } -#endif - *piRead = iRead; - return SQLITE_OK; + assert( nRef>=sqlcipher_sqlite3PagerRefcount(pPager) ); + return rc; } +#else /* ifndef SQLITE_OMIT_AUTOVACUUM */ +# define setChildPtrmaps(x) SQLITE_OK +#endif + /* -** Read the contents of frame iRead from the wal file into buffer pOut -** (which is nOut bytes in size). Return SQLITE_OK if successful, or an -** error code otherwise. +** This routine does the first phase of a two-phase commit. This routine +** causes a rollback journal to be created (if it does not already exist) +** and populated with enough information so that if a power loss occurs +** the database can be restored to its original state by playing back +** the journal. Then the contents of the journal are flushed out to +** the disk. After the journal is safely on oxide, the changes to the +** database are written into the database file and flushed to oxide. +** At the end of this call, the rollback journal still exists on the +** disk and we are still holding all locks, so the transaction has not +** committed. See sqlcipher_sqlite3BtreeCommitPhaseTwo() for the second phase of the +** commit process. +** +** This call is a no-op if no write-transaction is currently active on pBt. +** +** Otherwise, sync the database file for the btree pBt. zSuperJrnl points to +** the name of a super-journal file that should be written into the +** individual journal file, or is NULL, indicating no super-journal file +** (single database transaction). +** +** When this is called, the super-journal should already have been +** created, populated with this journal pointer and synced to disk. +** +** Once this is routine has returned, the only thing required to commit +** the write-transaction for this database file is to delete the journal. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalReadFrame( - Wal *pWal, /* WAL handle */ - u32 iRead, /* Frame to read */ - int nOut, /* Size of buffer pOut in bytes */ - u8 *pOut /* Buffer to write page data to */ -){ - int sz; - i64 iOffset; - sz = pWal->hdr.szPage; - sz = (sz&0xfe00) + ((sz&0x0001)<<16); - testcase( sz<=32768 ); - testcase( sz>=65536 ); - iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE; - /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ - return sqlcipher_sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset); +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){ + int rc = SQLITE_OK; + if( p->inTrans==TRANS_WRITE ){ + BtShared *pBt = p->pBt; + sqlcipher_sqlite3BtreeEnter(p); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + rc = autoVacuumCommit(p); + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3BtreeLeave(p); + return rc; + } + } + if( pBt->bDoTruncate ){ + sqlcipher_sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage); + } +#endif + rc = sqlcipher_sqlite3PagerCommitPhaseOne(pBt->pPager, zSuperJrnl, 0); + sqlcipher_sqlite3BtreeLeave(p); + } + return rc; } -/* -** Return the size of the database in pages (or zero, if unknown). -*/ -SQLITE_PRIVATE Pgno sqlcipher_sqlite3WalDbsize(Wal *pWal){ - if( pWal && ALWAYS(pWal->readLock>=0) ){ - return pWal->hdr.nPage; +/* +** This function is called from both BtreeCommitPhaseTwo() and BtreeRollback() +** at the conclusion of a transaction. +*/ +static void btreeEndTransaction(Btree *p){ + BtShared *pBt = p->pBt; + sqlcipher_sqlite3 *db = p->db; + assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); + +#ifndef SQLITE_OMIT_AUTOVACUUM + pBt->bDoTruncate = 0; +#endif + if( p->inTrans>TRANS_NONE && db->nVdbeRead>1 ){ + /* If there are other active statements that belong to this database + ** handle, downgrade to a read-only transaction. The other statements + ** may still be reading from the database. */ + downgradeAllSharedCacheTableLocks(p); + p->inTrans = TRANS_READ; + }else{ + /* If the handle had any kind of transaction open, decrement the + ** transaction count of the shared btree. If the transaction count + ** reaches 0, set the shared state to TRANS_NONE. The unlockBtreeIfUnused() + ** call below will unlock the pager. */ + if( p->inTrans!=TRANS_NONE ){ + clearAllSharedCacheTableLocks(p); + pBt->nTransaction--; + if( 0==pBt->nTransaction ){ + pBt->inTransaction = TRANS_NONE; + } + } + + /* Set the current transaction state to TRANS_NONE and unlock the + ** pager if this call closed the only read or write transaction. */ + p->inTrans = TRANS_NONE; + unlockBtreeIfUnused(pBt); } - return 0; -} + btreeIntegrity(p); +} /* -** This function starts a write transaction on the WAL. +** Commit the transaction currently in progress. ** -** A read transaction must have already been started by a prior call -** to sqlcipher_sqlite3WalBeginReadTransaction(). +** This routine implements the second phase of a 2-phase commit. The +** sqlcipher_sqlite3BtreeCommitPhaseOne() routine does the first phase and should +** be invoked prior to calling this routine. The sqlcipher_sqlite3BtreeCommitPhaseOne() +** routine did all the work of writing information out to disk and flushing the +** contents so that they are written onto the disk platter. All this +** routine has to do is delete or truncate or zero the header in the +** the rollback journal (which causes the transaction to commit) and +** drop locks. ** -** If another thread or process has written into the database since -** the read transaction was started, then it is not possible for this -** thread to write as doing so would cause a fork. So this routine -** returns SQLITE_BUSY in that case and no write transaction is started. +** Normally, if an error occurs while the pager layer is attempting to +** finalize the underlying journal file, this function returns an error and +** the upper layer will attempt a rollback. However, if the second argument +** is non-zero then this b-tree transaction is part of a multi-file +** transaction. In this case, the transaction has already been committed +** (by deleting a super-journal file) and the caller will ignore this +** functions return code. So, even if an error occurs in the pager layer, +** reset the b-tree objects internal state to indicate that the write +** transaction has been closed. This is quite safe, as the pager will have +** transitioned to the error state. ** -** There can only be a single writer active at a time. +** This will release the write lock on the database file. If there +** are no active cursors, it also releases the read lock. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalBeginWriteTransaction(Wal *pWal){ - int rc; - -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT - /* If the write-lock is already held, then it was obtained before the - ** read-transaction was even opened, making this call a no-op. - ** Return early. */ - if( pWal->writeLock ){ - assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) ); - return SQLITE_OK; - } -#endif - - /* Cannot start a write transaction without first holding a read - ** transaction. */ - assert( pWal->readLock>=0 ); - assert( pWal->writeLock==0 && pWal->iReCksum==0 ); - - if( pWal->readOnly ){ - return SQLITE_READONLY; - } +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){ - /* Only one writer allowed at a time. Get the write lock. Return - ** SQLITE_BUSY if unable. - */ - rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); - if( rc ){ - return rc; - } - pWal->writeLock = 1; + if( p->inTrans==TRANS_NONE ) return SQLITE_OK; + sqlcipher_sqlite3BtreeEnter(p); + btreeIntegrity(p); - /* If another connection has written to the database file since the - ** time the read transaction on this connection was started, then - ** the write is disallowed. + /* If the handle has a write-transaction open, commit the shared-btrees + ** transaction and set the shared state to TRANS_READ. */ - if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){ - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); - pWal->writeLock = 0; - rc = SQLITE_BUSY_SNAPSHOT; + if( p->inTrans==TRANS_WRITE ){ + int rc; + BtShared *pBt = p->pBt; + assert( pBt->inTransaction==TRANS_WRITE ); + assert( pBt->nTransaction>0 ); + rc = sqlcipher_sqlite3PagerCommitPhaseTwo(pBt->pPager); + if( rc!=SQLITE_OK && bCleanup==0 ){ + sqlcipher_sqlite3BtreeLeave(p); + return rc; + } + p->iBDataVersion--; /* Compensate for pPager->iDataVersion++; */ + pBt->inTransaction = TRANS_READ; + btreeClearHasContent(pBt); } - return rc; + btreeEndTransaction(p); + sqlcipher_sqlite3BtreeLeave(p); + return SQLITE_OK; } /* -** End a write transaction. The commit has already been done. This -** routine merely releases the lock. +** Do both phases of a commit. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalEndWriteTransaction(Wal *pWal){ - if( pWal->writeLock ){ - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); - pWal->writeLock = 0; - pWal->iReCksum = 0; - pWal->truncateOnCommit = 0; +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCommit(Btree *p){ + int rc; + sqlcipher_sqlite3BtreeEnter(p); + rc = sqlcipher_sqlite3BtreeCommitPhaseOne(p, 0); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3BtreeCommitPhaseTwo(p, 0); } - return SQLITE_OK; + sqlcipher_sqlite3BtreeLeave(p); + return rc; } /* -** If any data has been written (but not committed) to the log file, this -** function moves the write-pointer back to the start of the transaction. +** This routine sets the state to CURSOR_FAULT and the error +** code to errCode for every cursor on any BtShared that pBtree +** references. Or if the writeOnly flag is set to 1, then only +** trip write cursors and leave read cursors unchanged. ** -** Additionally, the callback function is invoked for each frame written -** to the WAL since the start of the transaction. If the callback returns -** other than SQLITE_OK, it is not invoked again and the error code is -** returned to the caller. +** Every cursor is a candidate to be tripped, including cursors +** that belong to other database connections that happen to be +** sharing the cache with pBtree. ** -** Otherwise, if the callback function does not return an error, this -** function returns SQLITE_OK. +** This routine gets called when a rollback occurs. If the writeOnly +** flag is true, then only write-cursors need be tripped - read-only +** cursors save their current positions so that they may continue +** following the rollback. Or, if writeOnly is false, all cursors are +** tripped. In general, writeOnly is false if the transaction being +** rolled back modified the database schema. In this case b-tree root +** pages may be moved or deleted from the database altogether, making +** it unsafe for read cursors to continue. +** +** If the writeOnly flag is true and an error is encountered while +** saving the current position of a read-only cursor, all cursors, +** including all read-cursors are tripped. +** +** SQLITE_OK is returned if successful, or if an error occurs while +** saving a cursor position, an SQLite error code. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){ + BtCursor *p; int rc = SQLITE_OK; - if( ALWAYS(pWal->writeLock) ){ - Pgno iMax = pWal->hdr.mxFrame; - Pgno iFrame; - - /* Restore the clients cache of the wal-index header to the state it - ** was in before the client began writing to the database. - */ - memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr)); - for(iFrame=pWal->hdr.mxFrame+1; - ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; - iFrame++ - ){ - /* This call cannot fail. Unless the page for which the page number - ** is passed as the second argument is (a) in the cache and - ** (b) has an outstanding reference, then xUndo is either a no-op - ** (if (a) is false) or simply expels the page from the cache (if (b) - ** is false). - ** - ** If the upper layer is doing a rollback, it is guaranteed that there - ** are no outstanding references to any page other than page 1. And - ** page 1 is never written to the log until the transaction is - ** committed. As a result, the call to xUndo may not fail. - */ - assert( walFramePgno(pWal, iFrame)!=1 ); - rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); + assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 ); + if( pBtree ){ + sqlcipher_sqlite3BtreeEnter(pBtree); + for(p=pBtree->pBt->pCursor; p; p=p->pNext){ + if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){ + if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){ + rc = saveCursorPosition(p); + if( rc!=SQLITE_OK ){ + (void)sqlcipher_sqlite3BtreeTripAllCursors(pBtree, rc, 0); + break; + } + } + }else{ + sqlcipher_sqlite3BtreeClearCursor(p); + p->eState = CURSOR_FAULT; + p->skipNext = errCode; + } + btreeReleaseAllCursorPages(p); } - if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal); + sqlcipher_sqlite3BtreeLeave(pBtree); } return rc; } /* -** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32 -** values. This function populates the array with values required to -** "rollback" the write position of the WAL handle back to the current -** point in the event of a savepoint rollback (via WalSavepointUndo()). +** Set the pBt->nPage field correctly, according to the current +** state of the database. Assume pBt->pPage1 is valid. */ -SQLITE_PRIVATE void sqlcipher_sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){ - assert( pWal->writeLock ); - aWalData[0] = pWal->hdr.mxFrame; - aWalData[1] = pWal->hdr.aFrameCksum[0]; - aWalData[2] = pWal->hdr.aFrameCksum[1]; - aWalData[3] = pWal->nCkpt; +static void btreeSetNPage(BtShared *pBt, MemPage *pPage1){ + int nPage = get4byte(&pPage1->aData[28]); + testcase( nPage==0 ); + if( nPage==0 ) sqlcipher_sqlite3PagerPagecount(pBt->pPager, &nPage); + testcase( pBt->nPage!=(u32)nPage ); + pBt->nPage = nPage; } /* -** Move the write position of the WAL back to the point identified by -** the values in the aWalData[] array. aWalData must point to an array -** of WAL_SAVEPOINT_NDATA u32 values that has been previously populated -** by a call to WalSavepoint(). +** Rollback the transaction in progress. +** +** If tripCode is not SQLITE_OK then cursors will be invalidated (tripped). +** Only write cursors are tripped if writeOnly is true but all cursors are +** tripped if writeOnly is false. Any attempt to use +** a tripped cursor will result in an error. +** +** This will release the write lock on the database file. If there +** are no active cursors, it also releases the read lock. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){ - int rc = SQLITE_OK; - - assert( pWal->writeLock ); - assert( aWalData[3]!=pWal->nCkpt || aWalData[0]<=pWal->hdr.mxFrame ); +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly){ + int rc; + BtShared *pBt = p->pBt; + MemPage *pPage1; - if( aWalData[3]!=pWal->nCkpt ){ - /* This savepoint was opened immediately after the write-transaction - ** was started. Right after that, the writer decided to wrap around - ** to the start of the log. Update the savepoint values to match. - */ - aWalData[0] = 0; - aWalData[3] = pWal->nCkpt; + assert( writeOnly==1 || writeOnly==0 ); + assert( tripCode==SQLITE_ABORT_ROLLBACK || tripCode==SQLITE_OK ); + sqlcipher_sqlite3BtreeEnter(p); + if( tripCode==SQLITE_OK ){ + rc = tripCode = saveAllCursors(pBt, 0, 0); + if( rc ) writeOnly = 0; + }else{ + rc = SQLITE_OK; } - - if( aWalData[0]hdr.mxFrame ){ - pWal->hdr.mxFrame = aWalData[0]; - pWal->hdr.aFrameCksum[0] = aWalData[1]; - pWal->hdr.aFrameCksum[1] = aWalData[2]; - walCleanupHash(pWal); + if( tripCode ){ + int rc2 = sqlcipher_sqlite3BtreeTripAllCursors(p, tripCode, writeOnly); + assert( rc==SQLITE_OK || (writeOnly==0 && rc2==SQLITE_OK) ); + if( rc2!=SQLITE_OK ) rc = rc2; } + btreeIntegrity(p); - return rc; -} + if( p->inTrans==TRANS_WRITE ){ + int rc2; -/* -** This function is called just before writing a set of frames to the log -** file (see sqlcipher_sqlite3WalFrames()). It checks to see if, instead of appending -** to the current log file, it is possible to overwrite the start of the -** existing log file with the new frames (i.e. "reset" the log). If so, -** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left -** unchanged. -** -** SQLITE_OK is returned if no error is encountered (regardless of whether -** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned -** if an error occurs. -*/ -static int walRestartLog(Wal *pWal){ - int rc = SQLITE_OK; - int cnt; + assert( TRANS_WRITE==pBt->inTransaction ); + rc2 = sqlcipher_sqlite3PagerRollback(pBt->pPager); + if( rc2!=SQLITE_OK ){ + rc = rc2; + } - if( pWal->readLock==0 ){ - volatile WalCkptInfo *pInfo = walCkptInfo(pWal); - assert( pInfo->nBackfill==pWal->hdr.mxFrame ); - if( pInfo->nBackfill>0 ){ - u32 salt1; - sqlcipher_sqlite3_randomness(4, &salt1); - rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); - if( rc==SQLITE_OK ){ - /* If all readers are using WAL_READ_LOCK(0) (in other words if no - ** readers are currently using the WAL), then the transactions - ** frames will overwrite the start of the existing log. Update the - ** wal-index header to reflect this. - ** - ** In theory it would be Ok to update the cache of the header only - ** at this point. But updating the actual wal-index header is also - ** safe and means there is no special case for sqlcipher_sqlite3WalUndo() - ** to handle if this transaction is rolled back. */ - walRestartHdr(pWal, salt1); - walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); - }else if( rc!=SQLITE_BUSY ){ - return rc; - } + /* The rollback may have destroyed the pPage1->aData value. So + ** call btreeGetPage() on page 1 again to make + ** sure pPage1->aData is set correctly. */ + if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ + btreeSetNPage(pBt, pPage1); + releasePageOne(pPage1); } - walUnlockShared(pWal, WAL_READ_LOCK(0)); - pWal->readLock = -1; - cnt = 0; - do{ - int notUsed; - rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); - }while( rc==WAL_RETRY ); - assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */ - testcase( (rc&0xff)==SQLITE_IOERR ); - testcase( rc==SQLITE_PROTOCOL ); - testcase( rc==SQLITE_OK ); + assert( countValidCursors(pBt, 1)==0 ); + pBt->inTransaction = TRANS_READ; + btreeClearHasContent(pBt); } + + btreeEndTransaction(p); + sqlcipher_sqlite3BtreeLeave(p); return rc; } /* -** Information about the current state of the WAL file and where -** the next fsync should occur - passed from sqlcipher_sqlite3WalFrames() into -** walWriteToLog(). -*/ -typedef struct WalWriter { - Wal *pWal; /* The complete WAL information */ - sqlcipher_sqlite3_file *pFd; /* The WAL file to which we write */ - sqlcipher_sqlite3_int64 iSyncPoint; /* Fsync at this offset */ - int syncFlags; /* Flags for the fsync */ - int szPage; /* Size of one page */ -} WalWriter; - -/* -** Write iAmt bytes of content into the WAL file beginning at iOffset. -** Do a sync when crossing the p->iSyncPoint boundary. +** Start a statement subtransaction. The subtransaction can be rolled +** back independently of the main transaction. You must start a transaction +** before starting a subtransaction. The subtransaction is ended automatically +** if the main transaction commits or rolls back. ** -** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt, -** first write the part before iSyncPoint, then sync, then write the -** rest. +** Statement subtransactions are used around individual SQL statements +** that are contained within a BEGIN...COMMIT block. If a constraint +** error occurs within the statement, the effect of that one statement +** can be rolled back without having to rollback the entire transaction. +** +** A statement sub-transaction is implemented as an anonymous savepoint. The +** value passed as the second parameter is the total number of savepoints, +** including the new anonymous savepoint, open on the B-Tree. i.e. if there +** are no active savepoints and no other statement-transactions open, +** iStatement is 1. This anonymous savepoint can be released or rolled back +** using the sqlcipher_sqlite3BtreeSavepoint() function. */ -static int walWriteToLog( - WalWriter *p, /* WAL to write to */ - void *pContent, /* Content to be written */ - int iAmt, /* Number of bytes to write */ - sqlcipher_sqlite3_int64 iOffset /* Start writing at this offset */ -){ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeBeginStmt(Btree *p, int iStatement){ int rc; - if( iOffsetiSyncPoint && iOffset+iAmt>=p->iSyncPoint ){ - int iFirstAmt = (int)(p->iSyncPoint - iOffset); - rc = sqlcipher_sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset); - if( rc ) return rc; - iOffset += iFirstAmt; - iAmt -= iFirstAmt; - pContent = (void*)(iFirstAmt + (char*)pContent); - assert( WAL_SYNC_FLAGS(p->syncFlags)!=0 ); - rc = sqlcipher_sqlite3OsSync(p->pFd, WAL_SYNC_FLAGS(p->syncFlags)); - if( iAmt==0 || rc ) return rc; - } - rc = sqlcipher_sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset); + BtShared *pBt = p->pBt; + sqlcipher_sqlite3BtreeEnter(p); + assert( p->inTrans==TRANS_WRITE ); + assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); + assert( iStatement>0 ); + assert( iStatement>p->db->nSavepoint ); + assert( pBt->inTransaction==TRANS_WRITE ); + /* At the pager level, a statement transaction is a savepoint with + ** an index greater than all savepoints created explicitly using + ** SQL statements. It is illegal to open, release or rollback any + ** such savepoints while the statement transaction savepoint is active. + */ + rc = sqlcipher_sqlite3PagerOpenSavepoint(pBt->pPager, iStatement); + sqlcipher_sqlite3BtreeLeave(p); return rc; } /* -** Write out a single frame of the WAL +** The second argument to this function, op, is always SAVEPOINT_ROLLBACK +** or SAVEPOINT_RELEASE. This function either releases or rolls back the +** savepoint identified by parameter iSavepoint, depending on the value +** of op. +** +** Normally, iSavepoint is greater than or equal to zero. However, if op is +** SAVEPOINT_ROLLBACK, then iSavepoint may also be -1. In this case the +** contents of the entire transaction are rolled back. This is different +** from a normal transaction rollback, as no locks are released and the +** transaction remains open. */ -static int walWriteOneFrame( - WalWriter *p, /* Where to write the frame */ - PgHdr *pPage, /* The page of the frame to be written */ - int nTruncate, /* The commit flag. Usually 0. >0 for commit */ - sqlcipher_sqlite3_int64 iOffset /* Byte offset at which to write */ -){ - int rc; /* Result code from subfunctions */ - void *pData; /* Data actually written */ - u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */ -#if defined(SQLITE_HAS_CODEC) - if( (pData = sqlcipher_sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT; -#else - pData = pPage->pData; -#endif - walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame); - rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset); - if( rc ) return rc; - /* Write the page data */ - rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame)); +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ + int rc = SQLITE_OK; + if( p && p->inTrans==TRANS_WRITE ){ + BtShared *pBt = p->pBt; + assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); + assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); + sqlcipher_sqlite3BtreeEnter(p); + if( op==SAVEPOINT_ROLLBACK ){ + rc = saveAllCursors(pBt, 0, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); + } + if( rc==SQLITE_OK ){ + if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){ + pBt->nPage = 0; + } + rc = newDatabase(pBt); + btreeSetNPage(pBt, pBt->pPage1); + + /* pBt->nPage might be zero if the database was corrupt when + ** the transaction was started. Otherwise, it must be at least 1. */ + assert( CORRUPT_DB || pBt->nPage>0 ); + } + sqlcipher_sqlite3BtreeLeave(p); + } return rc; } /* -** This function is called as part of committing a transaction within which -** one or more frames have been overwritten. It updates the checksums for -** all frames written to the wal file by the current transaction starting -** with the earliest to have been overwritten. +** Create a new cursor for the BTree whose root is on the page +** iTable. If a read-only cursor is requested, it is assumed that +** the caller already has at least a read-only transaction open +** on the database already. If a write-cursor is requested, then +** the caller is assumed to have an open write transaction. ** -** SQLITE_OK is returned if successful, or an SQLite error code otherwise. +** If the BTREE_WRCSR bit of wrFlag is clear, then the cursor can only +** be used for reading. If the BTREE_WRCSR bit is set, then the cursor +** can be used for reading or for writing if other conditions for writing +** are also met. These are the conditions that must be met in order +** for writing to be allowed: +** +** 1: The cursor must have been opened with wrFlag containing BTREE_WRCSR +** +** 2: Other database connections that share the same pager cache +** but which are not in the READ_UNCOMMITTED state may not have +** cursors open with wrFlag==0 on the same table. Otherwise +** the changes made by this write cursor would be visible to +** the read cursors in the other database connection. +** +** 3: The database must be writable (not on read-only media) +** +** 4: There must be an active transaction. +** +** The BTREE_FORDELETE bit of wrFlag may optionally be set if BTREE_WRCSR +** is set. If FORDELETE is set, that is a hint to the implementation that +** this cursor will only be used to seek to and delete entries of an index +** as part of a larger DELETE statement. The FORDELETE hint is not used by +** this implementation. But in a hypothetical alternative storage engine +** in which index entries are automatically deleted when corresponding table +** rows are deleted, the FORDELETE flag is a hint that all SEEK and DELETE +** operations on this cursor can be no-ops and all READ operations can +** return a null row (2-bytes: 0x01 0x00). +** +** No checking is done to make sure that page iTable really is the +** root page of a b-tree. If it is not, then the cursor acquired +** will not work correctly. +** +** It is assumed that the sqlcipher_sqlite3BtreeCursorZero() has been called +** on pCur to initialize the memory space prior to invoking this routine. */ -static int walRewriteChecksums(Wal *pWal, u32 iLast){ - const int szPage = pWal->szPage;/* Database page size */ - int rc = SQLITE_OK; /* Return code */ - u8 *aBuf; /* Buffer to load data from wal file into */ - u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-headers in */ - u32 iRead; /* Next frame to read from wal file */ - i64 iCksumOff; +static int btreeCursor( + Btree *p, /* The btree */ + Pgno iTable, /* Root page of table to open */ + int wrFlag, /* 1 to write. 0 read-only */ + struct KeyInfo *pKeyInfo, /* First arg to comparison function */ + BtCursor *pCur /* Space for new cursor */ +){ + BtShared *pBt = p->pBt; /* Shared b-tree handle */ + BtCursor *pX; /* Looping over other all cursors */ - aBuf = sqlcipher_sqlite3_malloc(szPage + WAL_FRAME_HDRSIZE); - if( aBuf==0 ) return SQLITE_NOMEM_BKPT; + assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); + assert( wrFlag==0 + || wrFlag==BTREE_WRCSR + || wrFlag==(BTREE_WRCSR|BTREE_FORDELETE) + ); - /* Find the checksum values to use as input for the recalculating the - ** first checksum. If the first frame is frame 1 (implying that the current - ** transaction restarted the wal file), these values must be read from the - ** wal-file header. Otherwise, read them from the frame header of the - ** previous frame. */ - assert( pWal->iReCksum>0 ); - if( pWal->iReCksum==1 ){ - iCksumOff = 24; - }else{ - iCksumOff = walFrameOffset(pWal->iReCksum-1, szPage) + 16; - } - rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aBuf, sizeof(u32)*2, iCksumOff); - pWal->hdr.aFrameCksum[0] = sqlcipher_sqlite3Get4byte(aBuf); - pWal->hdr.aFrameCksum[1] = sqlcipher_sqlite3Get4byte(&aBuf[sizeof(u32)]); + /* The following assert statements verify that if this is a sharable + ** b-tree database, the connection is holding the required table locks, + ** and that no other connection has any open cursor that conflicts with + ** this lock. The iTable<1 term disables the check for corrupt schemas. */ + assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, (wrFlag?2:1)) + || iTable<1 ); + assert( wrFlag==0 || !hasReadConflicts(p, iTable) ); - iRead = pWal->iReCksum; - pWal->iReCksum = 0; - for(; rc==SQLITE_OK && iRead<=iLast; iRead++){ - i64 iOff = walFrameOffset(iRead, szPage); - rc = sqlcipher_sqlite3OsRead(pWal->pWalFd, aBuf, szPage+WAL_FRAME_HDRSIZE, iOff); - if( rc==SQLITE_OK ){ - u32 iPgno, nDbSize; - iPgno = sqlcipher_sqlite3Get4byte(aBuf); - nDbSize = sqlcipher_sqlite3Get4byte(&aBuf[4]); + /* Assert that the caller has opened the required transaction. */ + assert( p->inTrans>TRANS_NONE ); + assert( wrFlag==0 || p->inTrans==TRANS_WRITE ); + assert( pBt->pPage1 && pBt->pPage1->aData ); + assert( wrFlag==0 || (pBt->btsFlags & BTS_READ_ONLY)==0 ); - walEncodeFrame(pWal, iPgno, nDbSize, &aBuf[WAL_FRAME_HDRSIZE], aFrame); - rc = sqlcipher_sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOff); + if( iTable<=1 ){ + if( iTable<1 ){ + return SQLITE_CORRUPT_BKPT; + }else if( btreePagecount(pBt)==0 ){ + assert( wrFlag==0 ); + iTable = 0; } } - sqlcipher_sqlite3_free(aBuf); + /* Now that no other errors can occur, finish filling in the BtCursor + ** variables and link the cursor into the BtShared list. */ + pCur->pgnoRoot = iTable; + pCur->iPage = -1; + pCur->pKeyInfo = pKeyInfo; + pCur->pBtree = p; + pCur->pBt = pBt; + pCur->curFlags = 0; + /* If there are two or more cursors on the same btree, then all such + ** cursors *must* have the BTCF_Multiple flag set. */ + for(pX=pBt->pCursor; pX; pX=pX->pNext){ + if( pX->pgnoRoot==iTable ){ + pX->curFlags |= BTCF_Multiple; + pCur->curFlags = BTCF_Multiple; + } + } + pCur->eState = CURSOR_INVALID; + pCur->pNext = pBt->pCursor; + pBt->pCursor = pCur; + if( wrFlag ){ + pCur->curFlags |= BTCF_WriteFlag; + pCur->curPagerFlags = 0; + if( pBt->pTmpSpace==0 ) return allocateTempSpace(pBt); + }else{ + pCur->curPagerFlags = PAGER_GET_READONLY; + } + return SQLITE_OK; +} +static int btreeCursorWithLock( + Btree *p, /* The btree */ + Pgno iTable, /* Root page of table to open */ + int wrFlag, /* 1 to write. 0 read-only */ + struct KeyInfo *pKeyInfo, /* First arg to comparison function */ + BtCursor *pCur /* Space for new cursor */ +){ + int rc; + sqlcipher_sqlite3BtreeEnter(p); + rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur); + sqlcipher_sqlite3BtreeLeave(p); return rc; } +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursor( + Btree *p, /* The btree */ + Pgno iTable, /* Root page of table to open */ + int wrFlag, /* 1 to write. 0 read-only */ + struct KeyInfo *pKeyInfo, /* First arg to xCompare() */ + BtCursor *pCur /* Write new cursor here */ +){ + if( p->sharable ){ + return btreeCursorWithLock(p, iTable, wrFlag, pKeyInfo, pCur); + }else{ + return btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur); + } +} /* -** Write a set of frames to the log. The caller must hold the write-lock -** on the log file (obtained using sqlcipher_sqlite3WalBeginWriteTransaction()). +** Return the size of a BtCursor object in bytes. +** +** This interfaces is needed so that users of cursors can preallocate +** sufficient storage to hold a cursor. The BtCursor object is opaque +** to users so they cannot do the sizeof() themselves - they must call +** this routine. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalFrames( - Wal *pWal, /* Wal handle to write to */ - int szPage, /* Database page-size in bytes */ - PgHdr *pList, /* List of dirty pages to write */ - Pgno nTruncate, /* Database size after this commit */ - int isCommit, /* True if this is a commit */ - int sync_flags /* Flags to pass to OsSync() (or 0) */ -){ - int rc; /* Used to catch return codes */ - u32 iFrame; /* Next frame address */ - PgHdr *p; /* Iterator to run through pList with. */ - PgHdr *pLast = 0; /* Last frame in list */ - int nExtra = 0; /* Number of extra copies of last page */ - int szFrame; /* The size of a single frame */ - i64 iOffset; /* Next byte to write in WAL file */ - WalWriter w; /* The writer */ - u32 iFirst = 0; /* First frame that may be overwritten */ - WalIndexHdr *pLive; /* Pointer to shared header */ - - assert( pList ); - assert( pWal->writeLock ); +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorSize(void){ + return ROUND8(sizeof(BtCursor)); +} - /* If this frame set completes a transaction, then nTruncate>0. If - ** nTruncate==0 then this frame set does not complete the transaction. */ - assert( (isCommit!=0)==(nTruncate!=0) ); +/* +** Initialize memory that will be converted into a BtCursor object. +** +** The simple approach here would be to memset() the entire object +** to zero. But it turns out that the apPage[] and aiIdx[] arrays +** do not need to be zeroed and they are large, so we can save a lot +** of run-time by skipping the initialization of those elements. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorZero(BtCursor *p){ + memset(p, 0, offsetof(BtCursor, BTCURSOR_FIRST_UNINIT)); +} -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) - { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){} - WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n", - pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill")); +/* +** Close a cursor. The read lock on the database file is released +** when the last cursor is closed. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCloseCursor(BtCursor *pCur){ + Btree *pBtree = pCur->pBtree; + if( pBtree ){ + BtShared *pBt = pCur->pBt; + sqlcipher_sqlite3BtreeEnter(pBtree); + assert( pBt->pCursor!=0 ); + if( pBt->pCursor==pCur ){ + pBt->pCursor = pCur->pNext; + }else{ + BtCursor *pPrev = pBt->pCursor; + do{ + if( pPrev->pNext==pCur ){ + pPrev->pNext = pCur->pNext; + break; + } + pPrev = pPrev->pNext; + }while( ALWAYS(pPrev) ); + } + btreeReleaseAllCursorPages(pCur); + unlockBtreeIfUnused(pBt); + sqlcipher_sqlite3_free(pCur->aOverflow); + sqlcipher_sqlite3_free(pCur->pKey); + if( (pBt->openFlags & BTREE_SINGLE) && pBt->pCursor==0 ){ + /* Since the BtShared is not sharable, there is no need to + ** worry about the missing sqlcipher_sqlite3BtreeLeave() call here. */ + assert( pBtree->sharable==0 ); + sqlcipher_sqlite3BtreeClose(pBtree); + }else{ + sqlcipher_sqlite3BtreeLeave(pBtree); + } + pCur->pBtree = 0; } -#endif + return SQLITE_OK; +} - pLive = (WalIndexHdr*)walIndexHdr(pWal); - if( memcmp(&pWal->hdr, (void *)pLive, sizeof(WalIndexHdr))!=0 ){ - iFirst = pLive->mxFrame+1; +/* +** Make sure the BtCursor* given in the argument has a valid +** BtCursor.info structure. If it is not already valid, call +** btreeParseCell() to fill it in. +** +** BtCursor.info is a cache of the information in the current cell. +** Using this cache reduces the number of calls to btreeParseCell(). +*/ +#ifndef NDEBUG + static int cellInfoEqual(CellInfo *a, CellInfo *b){ + if( a->nKey!=b->nKey ) return 0; + if( a->pPayload!=b->pPayload ) return 0; + if( a->nPayload!=b->nPayload ) return 0; + if( a->nLocal!=b->nLocal ) return 0; + if( a->nSize!=b->nSize ) return 0; + return 1; } - - /* See if it is possible to write these frames into the start of the - ** log file, instead of appending to it at pWal->hdr.mxFrame. - */ - if( SQLITE_OK!=(rc = walRestartLog(pWal)) ){ - return rc; + static void assertCellInfo(BtCursor *pCur){ + CellInfo info; + memset(&info, 0, sizeof(info)); + btreeParseCell(pCur->pPage, pCur->ix, &info); + assert( CORRUPT_DB || cellInfoEqual(&info, &pCur->info) ); } +#else + #define assertCellInfo(x) +#endif +static SQLITE_NOINLINE void getCellInfo(BtCursor *pCur){ + if( pCur->info.nSize==0 ){ + pCur->curFlags |= BTCF_ValidNKey; + btreeParseCell(pCur->pPage,pCur->ix,&pCur->info); + }else{ + assertCellInfo(pCur); + } +} - /* If this is the first frame written into the log, write the WAL - ** header to the start of the WAL file. See comments at the top of - ** this source file for a description of the WAL header format. - */ - iFrame = pWal->hdr.mxFrame; - if( iFrame==0 ){ - u8 aWalHdr[WAL_HDRSIZE]; /* Buffer to assemble wal-header in */ - u32 aCksum[2]; /* Checksum for wal-header */ - - sqlcipher_sqlite3Put4byte(&aWalHdr[0], (WAL_MAGIC | SQLITE_BIGENDIAN)); - sqlcipher_sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION); - sqlcipher_sqlite3Put4byte(&aWalHdr[8], szPage); - sqlcipher_sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt); - if( pWal->nCkpt==0 ) sqlcipher_sqlite3_randomness(8, pWal->hdr.aSalt); - memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8); - walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum); - sqlcipher_sqlite3Put4byte(&aWalHdr[24], aCksum[0]); - sqlcipher_sqlite3Put4byte(&aWalHdr[28], aCksum[1]); - - pWal->szPage = szPage; - pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN; - pWal->hdr.aFrameCksum[0] = aCksum[0]; - pWal->hdr.aFrameCksum[1] = aCksum[1]; - pWal->truncateOnCommit = 1; +#ifndef NDEBUG /* The next routine used only within assert() statements */ +/* +** Return true if the given BtCursor is valid. A valid cursor is one +** that is currently pointing to a row in a (non-empty) table. +** This is a verification routine is used only within assert() statements. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorIsValid(BtCursor *pCur){ + return pCur && pCur->eState==CURSOR_VALID; +} +#endif /* NDEBUG */ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorIsValidNN(BtCursor *pCur){ + assert( pCur!=0 ); + return pCur->eState==CURSOR_VALID; +} - rc = sqlcipher_sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0); - WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok")); - if( rc!=SQLITE_OK ){ - return rc; - } +/* +** Return the value of the integer key or "rowid" for a table btree. +** This routine is only valid for a cursor that is pointing into a +** ordinary table btree. If the cursor points to an index btree or +** is invalid, the result of this routine is undefined. +*/ +SQLITE_PRIVATE i64 sqlcipher_sqlite3BtreeIntegerKey(BtCursor *pCur){ + assert( cursorHoldsMutex(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->curIntKey ); + getCellInfo(pCur); + return pCur->info.nKey; +} - /* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless - ** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise - ** an out-of-order write following a WAL restart could result in - ** database corruption. See the ticket: - ** - ** https://sqlite.org/src/info/ff5be73dee - */ - if( pWal->syncHeader ){ - rc = sqlcipher_sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); - if( rc ) return rc; - } - } - assert( (int)pWal->szPage==szPage ); +/* +** Pin or unpin a cursor. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorPin(BtCursor *pCur){ + assert( (pCur->curFlags & BTCF_Pinned)==0 ); + pCur->curFlags |= BTCF_Pinned; +} +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorUnpin(BtCursor *pCur){ + assert( (pCur->curFlags & BTCF_Pinned)!=0 ); + pCur->curFlags &= ~BTCF_Pinned; +} - /* Setup information needed to write frames into the WAL */ - w.pWal = pWal; - w.pFd = pWal->pWalFd; - w.iSyncPoint = 0; - w.syncFlags = sync_flags; - w.szPage = szPage; - iOffset = walFrameOffset(iFrame+1, szPage); - szFrame = szPage + WAL_FRAME_HDRSIZE; +#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC +/* +** Return the offset into the database file for the start of the +** payload to which the cursor is pointing. +*/ +SQLITE_PRIVATE i64 sqlcipher_sqlite3BtreeOffset(BtCursor *pCur){ + assert( cursorHoldsMutex(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + getCellInfo(pCur); + return (i64)pCur->pBt->pageSize*((i64)pCur->pPage->pgno - 1) + + (i64)(pCur->info.pPayload - pCur->pPage->aData); +} +#endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */ - /* Write all frames into the log file exactly once */ - for(p=pList; p; p=p->pDirty){ - int nDbSize; /* 0 normally. Positive == commit flag */ +/* +** Return the number of bytes of payload for the entry that pCur is +** currently pointing to. For table btrees, this will be the amount +** of data. For index btrees, this will be the size of the key. +** +** The caller must guarantee that the cursor is pointing to a non-NULL +** valid entry. In other words, the calling procedure must guarantee +** that the cursor has Cursor.eState==CURSOR_VALID. +*/ +SQLITE_PRIVATE u32 sqlcipher_sqlite3BtreePayloadSize(BtCursor *pCur){ + assert( cursorHoldsMutex(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + getCellInfo(pCur); + return pCur->info.nPayload; +} - /* Check if this page has already been written into the wal file by - ** the current transaction. If so, overwrite the existing frame and - ** set Wal.writeLock to WAL_WRITELOCK_RECKSUM - indicating that - ** checksums must be recomputed when the transaction is committed. */ - if( iFirst && (p->pDirty || isCommit==0) ){ - u32 iWrite = 0; - VVA_ONLY(rc =) sqlcipher_sqlite3WalFindFrame(pWal, p->pgno, &iWrite); - assert( rc==SQLITE_OK || iWrite==0 ); - if( iWrite>=iFirst ){ - i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE; - void *pData; - if( pWal->iReCksum==0 || iWriteiReCksum ){ - pWal->iReCksum = iWrite; - } -#if defined(SQLITE_HAS_CODEC) - if( (pData = sqlcipher_sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM; -#else - pData = p->pData; -#endif - rc = sqlcipher_sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOff); - if( rc ) return rc; - p->flags &= ~PGHDR_WAL_APPEND; - continue; - } - } +/* +** Return an upper bound on the size of any record for the table +** that the cursor is pointing into. +** +** This is an optimization. Everything will still work if this +** routine always returns 2147483647 (which is the largest record +** that SQLite can handle) or more. But returning a smaller value might +** prevent large memory allocations when trying to interpret a +** corrupt datrabase. +** +** The current implementation merely returns the size of the underlying +** database file. +*/ +SQLITE_PRIVATE sqlcipher_sqlite3_int64 sqlcipher_sqlite3BtreeMaxRecordSize(BtCursor *pCur){ + assert( cursorHoldsMutex(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + return pCur->pBt->pageSize * (sqlcipher_sqlite3_int64)pCur->pBt->nPage; +} - iFrame++; - assert( iOffset==walFrameOffset(iFrame, szPage) ); - nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0; - rc = walWriteOneFrame(&w, p, nDbSize, iOffset); - if( rc ) return rc; - pLast = p; - iOffset += szFrame; - p->flags |= PGHDR_WAL_APPEND; - } +/* +** Given the page number of an overflow page in the database (parameter +** ovfl), this function finds the page number of the next page in the +** linked list of overflow pages. If possible, it uses the auto-vacuum +** pointer-map data instead of reading the content of page ovfl to do so. +** +** If an error occurs an SQLite error code is returned. Otherwise: +** +** The page number of the next overflow page in the linked list is +** written to *pPgnoNext. If page ovfl is the last page in its linked +** list, *pPgnoNext is set to zero. +** +** If ppPage is not NULL, and a reference to the MemPage object corresponding +** to page number pOvfl was obtained, then *ppPage is set to point to that +** reference. It is the responsibility of the caller to call releasePage() +** on *ppPage to free the reference. In no reference was obtained (because +** the pointer-map was used to obtain the value for *pPgnoNext), then +** *ppPage is set to zero. +*/ +static int getOverflowPage( + BtShared *pBt, /* The database file */ + Pgno ovfl, /* Current overflow page number */ + MemPage **ppPage, /* OUT: MemPage handle (may be NULL) */ + Pgno *pPgnoNext /* OUT: Next overflow page number */ +){ + Pgno next = 0; + MemPage *pPage = 0; + int rc = SQLITE_OK; - /* Recalculate checksums within the wal file if required. */ - if( isCommit && pWal->iReCksum ){ - rc = walRewriteChecksums(pWal, iFrame); - if( rc ) return rc; - } + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + assert(pPgnoNext); - /* If this is the end of a transaction, then we might need to pad - ** the transaction and/or sync the WAL file. - ** - ** Padding and syncing only occur if this set of frames complete a - ** transaction and if PRAGMA synchronous=FULL. If synchronous==NORMAL - ** or synchronous==OFF, then no padding or syncing are needed. - ** - ** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not - ** needed and only the sync is done. If padding is needed, then the - ** final frame is repeated (with its commit mark) until the next sector - ** boundary is crossed. Only the part of the WAL prior to the last - ** sector boundary is synced; the part of the last frame that extends - ** past the sector boundary is written after the sync. +#ifndef SQLITE_OMIT_AUTOVACUUM + /* Try to find the next page in the overflow list using the + ** autovacuum pointer-map pages. Guess that the next page in + ** the overflow list is page number (ovfl+1). If that guess turns + ** out to be wrong, fall back to loading the data of page + ** number ovfl to determine the next page number. */ - if( isCommit && WAL_SYNC_FLAGS(sync_flags)!=0 ){ - int bSync = 1; - if( pWal->padToSectorBoundary ){ - int sectorSize = sqlcipher_sqlite3SectorSize(pWal->pWalFd); - w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize; - bSync = (w.iSyncPoint==iOffset); - testcase( bSync ); - while( iOffsetautoVacuum ){ + Pgno pgno; + Pgno iGuess = ovfl+1; + u8 eType; - /* If this frame set completes the first transaction in the WAL and - ** if PRAGMA journal_size_limit is set, then truncate the WAL to the - ** journal size limit, if possible. - */ - if( isCommit && pWal->truncateOnCommit && pWal->mxWalSize>=0 ){ - i64 sz = pWal->mxWalSize; - if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){ - sz = walFrameOffset(iFrame+nExtra+1, szPage); + while( PTRMAP_ISPAGE(pBt, iGuess) || iGuess==PENDING_BYTE_PAGE(pBt) ){ + iGuess++; } - walLimitSize(pWal, sz); - pWal->truncateOnCommit = 0; - } - /* Append data to the wal-index. It is not necessary to lock the - ** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index - ** guarantees that there are no other writers, and no data that may - ** be in use by existing readers is being overwritten. - */ - iFrame = pWal->hdr.mxFrame; - for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){ - if( (p->flags & PGHDR_WAL_APPEND)==0 ) continue; - iFrame++; - rc = walIndexAppend(pWal, iFrame, p->pgno); - } - assert( pLast!=0 || nExtra==0 ); - while( rc==SQLITE_OK && nExtra>0 ){ - iFrame++; - nExtra--; - rc = walIndexAppend(pWal, iFrame, pLast->pgno); + if( iGuess<=btreePagecount(pBt) ){ + rc = ptrmapGet(pBt, iGuess, &eType, &pgno); + if( rc==SQLITE_OK && eType==PTRMAP_OVERFLOW2 && pgno==ovfl ){ + next = iGuess; + rc = SQLITE_DONE; + } + } } +#endif + assert( next==0 || rc==SQLITE_DONE ); if( rc==SQLITE_OK ){ - /* Update the private copy of the header. */ - pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); - testcase( szPage<=32768 ); - testcase( szPage>=65536 ); - pWal->hdr.mxFrame = iFrame; - if( isCommit ){ - pWal->hdr.iChange++; - pWal->hdr.nPage = nTruncate; - } - /* If this is a commit, update the wal-index header too. */ - if( isCommit ){ - walIndexWriteHdr(pWal); - pWal->iCallback = iFrame; + rc = btreeGetPage(pBt, ovfl, &pPage, (ppPage==0) ? PAGER_GET_READONLY : 0); + assert( rc==SQLITE_OK || pPage==0 ); + if( rc==SQLITE_OK ){ + next = get4byte(pPage->aData); } } - WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok")); - return rc; + *pPgnoNext = next; + if( ppPage ){ + *ppPage = pPage; + }else{ + releasePage(pPage); + } + return (rc==SQLITE_DONE ? SQLITE_OK : rc); } /* -** This routine is called to implement sqlcipher_sqlite3_wal_checkpoint() and -** related interfaces. +** Copy data from a buffer to a page, or from a page to a buffer. ** -** Obtain a CHECKPOINT lock and then backfill as much information as -** we can from WAL into the database. +** pPayload is a pointer to data stored on database page pDbPage. +** If argument eOp is false, then nByte bytes of data are copied +** from pPayload to the buffer pointed at by pBuf. If eOp is true, +** then sqlcipher_sqlite3PagerWrite() is called on pDbPage and nByte bytes +** of data are copied from the buffer pBuf to pPayload. ** -** If parameter xBusy is not NULL, it is a pointer to a busy-handler -** callback. In this case this function runs a blocking checkpoint. +** SQLITE_OK is returned on success, otherwise an error code. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalCheckpoint( - Wal *pWal, /* Wal connection */ - sqlcipher_sqlite3 *db, /* Check this handle's interrupt flag */ - int eMode, /* PASSIVE, FULL, RESTART, or TRUNCATE */ - int (*xBusy)(void*), /* Function to call when busy */ - void *pBusyArg, /* Context argument for xBusyHandler */ - int sync_flags, /* Flags to sync db file with (or 0) */ - int nBuf, /* Size of temporary buffer */ - u8 *zBuf, /* Temporary buffer to use */ - int *pnLog, /* OUT: Number of frames in WAL */ - int *pnCkpt /* OUT: Number of backfilled frames in WAL */ +static int copyPayload( + void *pPayload, /* Pointer to page data */ + void *pBuf, /* Pointer to buffer */ + int nByte, /* Number of bytes to copy */ + int eOp, /* 0 -> copy from page, 1 -> copy to page */ + DbPage *pDbPage /* Page containing pPayload */ ){ - int rc; /* Return code */ - int isChanged = 0; /* True if a new wal-index header is loaded */ - int eMode2 = eMode; /* Mode to pass to walCheckpoint() */ - int (*xBusy2)(void*) = xBusy; /* Busy handler for eMode2 */ - - assert( pWal->ckptLock==0 ); - assert( pWal->writeLock==0 ); - - /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked - ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ - assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); + if( eOp ){ + /* Copy data from buffer to page (a write operation) */ + int rc = sqlcipher_sqlite3PagerWrite(pDbPage); + if( rc!=SQLITE_OK ){ + return rc; + } + memcpy(pPayload, pBuf, nByte); + }else{ + /* Copy data from page to buffer (a read operation) */ + memcpy(pBuf, pPayload, nByte); + } + return SQLITE_OK; +} - if( pWal->readOnly ) return SQLITE_READONLY; - WALTRACE(("WAL%p: checkpoint begins\n", pWal)); +/* +** This function is used to read or overwrite payload information +** for the entry that the pCur cursor is pointing to. The eOp +** argument is interpreted as follows: +** +** 0: The operation is a read. Populate the overflow cache. +** 1: The operation is a write. Populate the overflow cache. +** +** A total of "amt" bytes are read or written beginning at "offset". +** Data is read to or from the buffer pBuf. +** +** The content being read or written might appear on the main page +** or be scattered out on multiple overflow pages. +** +** If the current cursor entry uses one or more overflow pages +** this function may allocate space for and lazily populate +** the overflow page-list cache array (BtCursor.aOverflow). +** Subsequent calls use this cache to make seeking to the supplied offset +** more efficient. +** +** Once an overflow page-list cache has been allocated, it must be +** invalidated if some other cursor writes to the same table, or if +** the cursor is moved to a different row. Additionally, in auto-vacuum +** mode, the following events may invalidate an overflow page-list cache. +** +** * An incremental vacuum, +** * A commit in auto_vacuum="full" mode, +** * Creating a table (may require moving an overflow page). +*/ +static int accessPayload( + BtCursor *pCur, /* Cursor pointing to entry to read from */ + u32 offset, /* Begin reading this far into payload */ + u32 amt, /* Read this many bytes */ + unsigned char *pBuf, /* Write the bytes into this buffer */ + int eOp /* zero to read. non-zero to write. */ +){ + unsigned char *aPayload; + int rc = SQLITE_OK; + int iIdx = 0; + MemPage *pPage = pCur->pPage; /* Btree page of current entry */ + BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */ +#ifdef SQLITE_DIRECT_OVERFLOW_READ + unsigned char * const pBufStart = pBuf; /* Start of original out buffer */ +#endif - /* Enable blocking locks, if possible. If blocking locks are successfully - ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */ - sqlcipher_sqlite3WalDb(pWal, db); - (void)walEnableBlocking(pWal); + assert( pPage ); + assert( eOp==0 || eOp==1 ); + assert( pCur->eState==CURSOR_VALID ); + if( pCur->ix>=pPage->nCell ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + assert( cursorHoldsMutex(pCur) ); - /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive - ** "checkpoint" lock on the database file. - ** EVIDENCE-OF: R-10421-19736 If any other process is running a - ** checkpoint operation at the same time, the lock cannot be obtained and - ** SQLITE_BUSY is returned. - ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured, - ** it will not be invoked in this case. - */ - rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); - testcase( rc==SQLITE_BUSY ); - testcase( rc!=SQLITE_OK && xBusy2!=0 ); - if( rc==SQLITE_OK ){ - pWal->ckptLock = 1; + getCellInfo(pCur); + aPayload = pCur->info.pPayload; + assert( offset+amt <= pCur->info.nPayload ); - /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and - ** TRUNCATE modes also obtain the exclusive "writer" lock on the database - ** file. - ** - ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained - ** immediately, and a busy-handler is configured, it is invoked and the - ** writer lock retried until either the busy-handler returns 0 or the - ** lock is successfully obtained. + assert( aPayload > pPage->aData ); + if( (uptr)(aPayload - pPage->aData) > (pBt->usableSize - pCur->info.nLocal) ){ + /* Trying to read or write past the end of the data is an error. The + ** conditional above is really: + ** &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] + ** but is recast into its current form to avoid integer overflow problems */ - if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ - rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1); - if( rc==SQLITE_OK ){ - pWal->writeLock = 1; - }else if( rc==SQLITE_BUSY ){ - eMode2 = SQLITE_CHECKPOINT_PASSIVE; - xBusy2 = 0; - rc = SQLITE_OK; - } - } + return SQLITE_CORRUPT_PAGE(pPage); } - - /* Read the wal-index header. */ - if( rc==SQLITE_OK ){ - walDisableBlocking(pWal); - rc = walIndexReadHdr(pWal, &isChanged); - (void)walEnableBlocking(pWal); - if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ - sqlcipher_sqlite3OsUnfetch(pWal->pDbFd, 0, 0); + /* Check if data must be read/written to/from the btree page itself. */ + if( offsetinfo.nLocal ){ + int a = amt; + if( a+offset>pCur->info.nLocal ){ + a = pCur->info.nLocal - offset; } + rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage); + offset = 0; + pBuf += a; + amt -= a; + }else{ + offset -= pCur->info.nLocal; } - /* Copy data from the log to the database file. */ - if( rc==SQLITE_OK ){ - if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ - rc = SQLITE_CORRUPT_BKPT; + if( rc==SQLITE_OK && amt>0 ){ + const u32 ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */ + Pgno nextPage; + + nextPage = get4byte(&aPayload[pCur->info.nLocal]); + + /* If the BtCursor.aOverflow[] has not been allocated, allocate it now. + ** + ** The aOverflow[] array is sized at one entry for each overflow page + ** in the overflow chain. The page number of the first overflow page is + ** stored in aOverflow[0], etc. A value of 0 in the aOverflow[] array + ** means "not yet known" (the cache is lazily populated). + */ + if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){ + int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; + if( pCur->aOverflow==0 + || nOvfl*(int)sizeof(Pgno) > sqlcipher_sqlite3MallocSize(pCur->aOverflow) + ){ + Pgno *aNew = (Pgno*)sqlcipher_sqlite3Realloc( + pCur->aOverflow, nOvfl*2*sizeof(Pgno) + ); + if( aNew==0 ){ + return SQLITE_NOMEM_BKPT; + }else{ + pCur->aOverflow = aNew; + } + } + memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno)); + pCur->curFlags |= BTCF_ValidOvfl; }else{ - rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf); + /* If the overflow page-list cache has been allocated and the + ** entry for the first required overflow page is valid, skip + ** directly to it. + */ + if( pCur->aOverflow[offset/ovflSize] ){ + iIdx = (offset/ovflSize); + nextPage = pCur->aOverflow[iIdx]; + offset = (offset%ovflSize); + } } - /* If no error occurred, set the output variables. */ - if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ - if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; - if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); + assert( rc==SQLITE_OK && amt>0 ); + while( nextPage ){ + /* If required, populate the overflow page-list cache. */ + if( nextPage > pBt->nPage ) return SQLITE_CORRUPT_BKPT; + assert( pCur->aOverflow[iIdx]==0 + || pCur->aOverflow[iIdx]==nextPage + || CORRUPT_DB ); + pCur->aOverflow[iIdx] = nextPage; + + if( offset>=ovflSize ){ + /* The only reason to read this page is to obtain the page + ** number for the next page in the overflow chain. The page + ** data is not required. So first try to lookup the overflow + ** page-list cache, if any, then fall back to the getOverflowPage() + ** function. + */ + assert( pCur->curFlags & BTCF_ValidOvfl ); + assert( pCur->pBtree->db==pBt->db ); + if( pCur->aOverflow[iIdx+1] ){ + nextPage = pCur->aOverflow[iIdx+1]; + }else{ + rc = getOverflowPage(pBt, nextPage, 0, &nextPage); + } + offset -= ovflSize; + }else{ + /* Need to read this page properly. It contains some of the + ** range of data that is being read (eOp==0) or written (eOp!=0). + */ + int a = amt; + if( a + offset > ovflSize ){ + a = ovflSize - offset; + } + +#ifdef SQLITE_DIRECT_OVERFLOW_READ + /* If all the following are true: + ** + ** 1) this is a read operation, and + ** 2) data is required from the start of this overflow page, and + ** 3) there are no dirty pages in the page-cache + ** 4) the database is file-backed, and + ** 5) the page is not in the WAL file + ** 6) at least 4 bytes have already been read into the output buffer + ** + ** then data can be read directly from the database file into the + ** output buffer, bypassing the page-cache altogether. This speeds + ** up loading large records that span many overflow pages. + */ + if( eOp==0 /* (1) */ + && offset==0 /* (2) */ + && sqlcipher_sqlite3PagerDirectReadOk(pBt->pPager, nextPage) /* (3,4,5) */ + && &pBuf[-4]>=pBufStart /* (6) */ + ){ + sqlcipher_sqlite3_file *fd = sqlcipher_sqlite3PagerFile(pBt->pPager); + u8 aSave[4]; + u8 *aWrite = &pBuf[-4]; + assert( aWrite>=pBufStart ); /* due to (6) */ + memcpy(aSave, aWrite, 4); + rc = sqlcipher_sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); + if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT; + nextPage = get4byte(aWrite); + memcpy(aWrite, aSave, 4); + }else +#endif + + { + DbPage *pDbPage; + rc = sqlcipher_sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage, + (eOp==0 ? PAGER_GET_READONLY : 0) + ); + if( rc==SQLITE_OK ){ + aPayload = sqlcipher_sqlite3PagerGetData(pDbPage); + nextPage = get4byte(aPayload); + rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage); + sqlcipher_sqlite3PagerUnref(pDbPage); + offset = 0; + } + } + amt -= a; + if( amt==0 ) return rc; + pBuf += a; + } + if( rc ) break; + iIdx++; } } - if( isChanged ){ - /* If a new wal-index header was loaded before the checkpoint was - ** performed, then the pager-cache associated with pWal is now - ** out of date. So zero the cached wal-index header to ensure that - ** next time the pager opens a snapshot on this database it knows that - ** the cache needs to be reset. - */ - memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); + if( rc==SQLITE_OK && amt>0 ){ + /* Overflow chain ends prematurely */ + return SQLITE_CORRUPT_PAGE(pPage); } + return rc; +} - walDisableBlocking(pWal); - sqlcipher_sqlite3WalDb(pWal, 0); +/* +** Read part of the payload for the row at which that cursor pCur is currently +** pointing. "amt" bytes will be transferred into pBuf[]. The transfer +** begins at "offset". +** +** pCur can be pointing to either a table or an index b-tree. +** If pointing to a table btree, then the content section is read. If +** pCur is pointing to an index b-tree then the key section is read. +** +** For sqlcipher_sqlite3BtreePayload(), the caller must ensure that pCur is pointing +** to a valid row in the table. For sqlcipher_sqlite3BtreePayloadChecked(), the +** cursor might be invalid or might need to be restored before being read. +** +** Return SQLITE_OK on success or an error code if anything goes +** wrong. An error is returned if "offset+amt" is larger than +** the available payload. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreePayload(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ + assert( cursorHoldsMutex(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->iPage>=0 && pCur->pPage ); + return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); +} - /* Release the locks. */ - sqlcipher_sqlite3WalEndWriteTransaction(pWal); - if( pWal->ckptLock ){ - walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); - pWal->ckptLock = 0; +/* +** This variant of sqlcipher_sqlite3BtreePayload() works even if the cursor has not +** in the CURSOR_VALID state. It is only used by the sqlcipher_sqlite3_blob_read() +** interface. +*/ +#ifndef SQLITE_OMIT_INCRBLOB +static SQLITE_NOINLINE int accessPayloadChecked( + BtCursor *pCur, + u32 offset, + u32 amt, + void *pBuf +){ + int rc; + if ( pCur->eState==CURSOR_INVALID ){ + return SQLITE_ABORT; + } + assert( cursorOwnsBtShared(pCur) ); + rc = btreeRestoreCursorPosition(pCur); + return rc ? rc : accessPayload(pCur, offset, amt, pBuf, 0); +} +SQLITE_PRIVATE int sqlcipher_sqlite3BtreePayloadChecked(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ + if( pCur->eState==CURSOR_VALID ){ + assert( cursorOwnsBtShared(pCur) ); + return accessPayload(pCur, offset, amt, pBuf, 0); + }else{ + return accessPayloadChecked(pCur, offset, amt, pBuf); } - WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT - if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; -#endif - return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc); } +#endif /* SQLITE_OMIT_INCRBLOB */ -/* Return the value to pass to a sqlcipher_sqlite3_wal_hook callback, the -** number of frames in the WAL at the point of the last commit since -** sqlcipher_sqlite3WalCallback() was called. If no commits have occurred since -** the last call, then return 0. +/* +** Return a pointer to payload information from the entry that the +** pCur cursor is pointing to. The pointer is to the beginning of +** the key if index btrees (pPage->intKey==0) and is the data for +** table btrees (pPage->intKey==1). The number of bytes of available +** key/data is written into *pAmt. If *pAmt==0, then the value +** returned will not be a valid pointer. +** +** This routine is an optimization. It is common for the entire key +** and data to fit on the local page and for there to be no overflow +** pages. When that is so, this routine can be used to access the +** key and data without making a copy. If the key and/or data spills +** onto overflow pages, then accessPayload() must be used to reassemble +** the key/data and copy it into a preallocated buffer. +** +** The pointer returned by this routine looks directly into the cached +** page of the database. The data might change or move the next time +** any btree routine is called. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalCallback(Wal *pWal){ - u32 ret = 0; - if( pWal ){ - ret = pWal->iCallback; - pWal->iCallback = 0; +static const void *fetchPayload( + BtCursor *pCur, /* Cursor pointing to entry to read from */ + u32 *pAmt /* Write the number of available bytes here */ +){ + int amt; + assert( pCur!=0 && pCur->iPage>=0 && pCur->pPage); + assert( pCur->eState==CURSOR_VALID ); + assert( sqlcipher_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + assert( cursorOwnsBtShared(pCur) ); + assert( pCur->ixpPage->nCell || CORRUPT_DB ); + assert( pCur->info.nSize>0 ); + assert( pCur->info.pPayload>pCur->pPage->aData || CORRUPT_DB ); + assert( pCur->info.pPayloadpPage->aDataEnd ||CORRUPT_DB); + amt = pCur->info.nLocal; + if( amt>(int)(pCur->pPage->aDataEnd - pCur->info.pPayload) ){ + /* There is too little space on the page for the expected amount + ** of local content. Database must be corrupt. */ + assert( CORRUPT_DB ); + amt = MAX(0, (int)(pCur->pPage->aDataEnd - pCur->info.pPayload)); } - return (int)ret; + *pAmt = (u32)amt; + return (void*)pCur->info.pPayload; } + /* -** This function is called to change the WAL subsystem into or out -** of locking_mode=EXCLUSIVE. +** For the entry that cursor pCur is point to, return as +** many bytes of the key or data as are available on the local +** b-tree page. Write the number of available bytes into *pAmt. ** -** If op is zero, then attempt to change from locking_mode=EXCLUSIVE -** into locking_mode=NORMAL. This means that we must acquire a lock -** on the pWal->readLock byte. If the WAL is already in locking_mode=NORMAL -** or if the acquisition of the lock fails, then return 0. If the -** transition out of exclusive-mode is successful, return 1. This -** operation must occur while the pager is still holding the exclusive -** lock on the main database file. +** The pointer returned is ephemeral. The key/data may move +** or be destroyed on the next call to any Btree routine, +** including calls from other threads against the same cache. +** Hence, a mutex on the BtShared should be held prior to calling +** this routine. ** -** If op is one, then change from locking_mode=NORMAL into -** locking_mode=EXCLUSIVE. This means that the pWal->readLock must -** be released. Return 1 if the transition is made and 0 if the -** WAL is already in exclusive-locking mode - meaning that this -** routine is a no-op. The pager must already hold the exclusive lock -** on the main database file before invoking this operation. +** These routines is used to get quick access to key and data +** in the common case where no overflow pages are used. +*/ +SQLITE_PRIVATE const void *sqlcipher_sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){ + return fetchPayload(pCur, pAmt); +} + + +/* +** Move the cursor down to a new child page. The newPgno argument is the +** page number of the child page to move to. ** -** If op is negative, then do a dry-run of the op==1 case but do -** not actually change anything. The pager uses this to see if it -** should acquire the database exclusive lock prior to invoking -** the op==1 case. +** This function returns SQLITE_CORRUPT if the page-header flags field of +** the new child page does not match the flags field of the parent (i.e. +** if an intkey page appears to be the parent of a non-intkey page, or +** vice-versa). */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalExclusiveMode(Wal *pWal, int op){ - int rc; - assert( pWal->writeLock==0 ); - assert( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE || op==-1 ); +static int moveToChild(BtCursor *pCur, u32 newPgno){ + BtShared *pBt = pCur->pBt; - /* pWal->readLock is usually set, but might be -1 if there was a - ** prior error while attempting to acquire are read-lock. This cannot - ** happen if the connection is actually in exclusive mode (as no xShmLock - ** locks are taken in this case). Nor should the pager attempt to - ** upgrade to exclusive-mode following such an error. - */ - assert( pWal->readLock>=0 || pWal->lockError ); - assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) ); + assert( cursorOwnsBtShared(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->iPageiPage>=0 ); + if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){ + return SQLITE_CORRUPT_BKPT; + } + pCur->info.nSize = 0; + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); + pCur->aiIdx[pCur->iPage] = pCur->ix; + pCur->apPage[pCur->iPage] = pCur->pPage; + pCur->ix = 0; + pCur->iPage++; + return getAndInitPage(pBt, newPgno, &pCur->pPage, pCur, pCur->curPagerFlags); +} - if( op==0 ){ - if( pWal->exclusiveMode!=WAL_NORMAL_MODE ){ - pWal->exclusiveMode = WAL_NORMAL_MODE; - if( walLockShared(pWal, WAL_READ_LOCK(pWal->readLock))!=SQLITE_OK ){ - pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; - } - rc = pWal->exclusiveMode==WAL_NORMAL_MODE; - }else{ - /* Already in locking_mode=NORMAL */ - rc = 0; - } - }else if( op>0 ){ - assert( pWal->exclusiveMode==WAL_NORMAL_MODE ); - assert( pWal->readLock>=0 ); - walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); - pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; - rc = 1; +#ifdef SQLITE_DEBUG +/* +** Page pParent is an internal (non-leaf) tree page. This function +** asserts that page number iChild is the left-child if the iIdx'th +** cell in page pParent. Or, if iIdx is equal to the total number of +** cells in pParent, that page number iChild is the right-child of +** the page. +*/ +static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){ + if( CORRUPT_DB ) return; /* The conditions tested below might not be true + ** in a corrupt database */ + assert( iIdx<=pParent->nCell ); + if( iIdx==pParent->nCell ){ + assert( get4byte(&pParent->aData[pParent->hdrOffset+8])==iChild ); }else{ - rc = pWal->exclusiveMode==WAL_NORMAL_MODE; + assert( get4byte(findCell(pParent, iIdx))==iChild ); } - return rc; } +#else +# define assertParentIndex(x,y,z) +#endif /* -** Return true if the argument is non-NULL and the WAL module is using -** heap-memory for the wal-index. Otherwise, if the argument is NULL or the -** WAL module is using shared-memory, return false. +** Move the cursor up to the parent page. +** +** pCur->idx is set to the cell index that contains the pointer +** to the page we are coming from. If we are coming from the +** right-most child page then pCur->idx is set to one more than +** the largest cell index. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalHeapMemory(Wal *pWal){ - return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ); +static void moveToParent(BtCursor *pCur){ + MemPage *pLeaf; + assert( cursorOwnsBtShared(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->iPage>0 ); + assert( pCur->pPage ); + assertParentIndex( + pCur->apPage[pCur->iPage-1], + pCur->aiIdx[pCur->iPage-1], + pCur->pPage->pgno + ); + testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell ); + pCur->info.nSize = 0; + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); + pCur->ix = pCur->aiIdx[pCur->iPage-1]; + pLeaf = pCur->pPage; + pCur->pPage = pCur->apPage[--pCur->iPage]; + releasePageNotNull(pLeaf); } -#ifdef SQLITE_ENABLE_SNAPSHOT -/* Create a snapshot object. The content of a snapshot is opaque to -** every other subsystem, so the WAL module can put whatever it needs -** in the object. +/* +** Move the cursor to point to the root page of its b-tree structure. +** +** If the table has a virtual root page, then the cursor is moved to point +** to the virtual root page instead of the actual root page. A table has a +** virtual root page when the actual root page contains no cells and a +** single child page. This can only happen with the table rooted at page 1. +** +** If the b-tree structure is empty, the cursor state is set to +** CURSOR_INVALID and this routine returns SQLITE_EMPTY. Otherwise, +** the cursor is set to point to the first cell located on the root +** (or virtual root) page and the cursor state is set to CURSOR_VALID. +** +** If this function returns successfully, it may be assumed that the +** page-header flags indicate that the [virtual] root-page is the expected +** kind of b-tree page (i.e. if when opening the cursor the caller did not +** specify a KeyInfo structure the flags byte is set to 0x05 or 0x0D, +** indicating a table b-tree, or if the caller did specify a KeyInfo +** structure the flags byte is set to 0x02 or 0x0A, indicating an index +** b-tree). */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotGet(Wal *pWal, sqlcipher_sqlite3_snapshot **ppSnapshot){ +static int moveToRoot(BtCursor *pCur){ + MemPage *pRoot; int rc = SQLITE_OK; - WalIndexHdr *pRet; - static const u32 aZero[4] = { 0, 0, 0, 0 }; - assert( pWal->readLock>=0 && pWal->writeLock==0 ); + assert( cursorOwnsBtShared(pCur) ); + assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); + assert( CURSOR_VALID < CURSOR_REQUIRESEEK ); + assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); + assert( pCur->eState < CURSOR_REQUIRESEEK || pCur->iPage<0 ); + assert( pCur->pgnoRoot>0 || pCur->iPage<0 ); - if( memcmp(&pWal->hdr.aFrameCksum[0],aZero,16)==0 ){ - *ppSnapshot = 0; - return SQLITE_ERROR; - } - pRet = (WalIndexHdr*)sqlcipher_sqlite3_malloc(sizeof(WalIndexHdr)); - if( pRet==0 ){ - rc = SQLITE_NOMEM_BKPT; + if( pCur->iPage>=0 ){ + if( pCur->iPage ){ + releasePageNotNull(pCur->pPage); + while( --pCur->iPage ){ + releasePageNotNull(pCur->apPage[pCur->iPage]); + } + pRoot = pCur->pPage = pCur->apPage[0]; + goto skip_init; + } + }else if( pCur->pgnoRoot==0 ){ + pCur->eState = CURSOR_INVALID; + return SQLITE_EMPTY; }else{ - memcpy(pRet, &pWal->hdr, sizeof(WalIndexHdr)); - *ppSnapshot = (sqlcipher_sqlite3_snapshot*)pRet; + assert( pCur->iPage==(-1) ); + if( pCur->eState>=CURSOR_REQUIRESEEK ){ + if( pCur->eState==CURSOR_FAULT ){ + assert( pCur->skipNext!=SQLITE_OK ); + return pCur->skipNext; + } + sqlcipher_sqlite3BtreeClearCursor(pCur); + } + rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->pPage, + 0, pCur->curPagerFlags); + if( rc!=SQLITE_OK ){ + pCur->eState = CURSOR_INVALID; + return rc; + } + pCur->iPage = 0; + pCur->curIntKey = pCur->pPage->intKey; + } + pRoot = pCur->pPage; + assert( pRoot->pgno==pCur->pgnoRoot || CORRUPT_DB ); + + /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor + ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is + ** NULL, the caller expects a table b-tree. If this is not the case, + ** return an SQLITE_CORRUPT error. + ** + ** Earlier versions of SQLite assumed that this test could not fail + ** if the root page was already loaded when this function was called (i.e. + ** if pCur->iPage>=0). But this is not so if the database is corrupted + ** in such a way that page pRoot is linked into a second b-tree table + ** (or the freelist). */ + assert( pRoot->intKey==1 || pRoot->intKey==0 ); + if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){ + return SQLITE_CORRUPT_PAGE(pCur->pPage); } +skip_init: + pCur->ix = 0; + pCur->info.nSize = 0; + pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl); + + if( pRoot->nCell>0 ){ + pCur->eState = CURSOR_VALID; + }else if( !pRoot->leaf ){ + Pgno subpage; + if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT; + subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); + pCur->eState = CURSOR_VALID; + rc = moveToChild(pCur, subpage); + }else{ + pCur->eState = CURSOR_INVALID; + rc = SQLITE_EMPTY; + } return rc; } -/* Try to open on pSnapshot when the next read-transaction starts +/* +** Move the cursor down to the left-most leaf entry beneath the +** entry to which it is currently pointing. +** +** The left-most leaf is the one with the smallest key - the first +** in ascending order. */ -SQLITE_PRIVATE void sqlcipher_sqlite3WalSnapshotOpen( - Wal *pWal, - sqlcipher_sqlite3_snapshot *pSnapshot -){ - pWal->pSnapshot = (WalIndexHdr*)pSnapshot; +static int moveToLeftmost(BtCursor *pCur){ + Pgno pgno; + int rc = SQLITE_OK; + MemPage *pPage; + + assert( cursorOwnsBtShared(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){ + assert( pCur->ixnCell ); + pgno = get4byte(findCell(pPage, pCur->ix)); + rc = moveToChild(pCur, pgno); + } + return rc; } /* -** Return a +ve value if snapshot p1 is newer than p2. A -ve value if -** p1 is older than p2 and zero if p1 and p2 are the same snapshot. +** Move the cursor down to the right-most leaf entry beneath the +** page to which it is currently pointing. Notice the difference +** between moveToLeftmost() and moveToRightmost(). moveToLeftmost() +** finds the left-most entry beneath the *entry* whereas moveToRightmost() +** finds the right-most entry beneath the *page*. +** +** The right-most entry is the one with the largest key - the last +** key in ascending order. */ -SQLITE_API int sqlcipher_sqlite3_snapshot_cmp(sqlcipher_sqlite3_snapshot *p1, sqlcipher_sqlite3_snapshot *p2){ - WalIndexHdr *pHdr1 = (WalIndexHdr*)p1; - WalIndexHdr *pHdr2 = (WalIndexHdr*)p2; +static int moveToRightmost(BtCursor *pCur){ + Pgno pgno; + int rc = SQLITE_OK; + MemPage *pPage = 0; - /* aSalt[0] is a copy of the value stored in the wal file header. It - ** is incremented each time the wal file is restarted. */ - if( pHdr1->aSalt[0]aSalt[0] ) return -1; - if( pHdr1->aSalt[0]>pHdr2->aSalt[0] ) return +1; - if( pHdr1->mxFramemxFrame ) return -1; - if( pHdr1->mxFrame>pHdr2->mxFrame ) return +1; - return 0; + assert( cursorOwnsBtShared(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + while( !(pPage = pCur->pPage)->leaf ){ + pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); + pCur->ix = pPage->nCell; + rc = moveToChild(pCur, pgno); + if( rc ) return rc; + } + pCur->ix = pPage->nCell-1; + assert( pCur->info.nSize==0 ); + assert( (pCur->curFlags & BTCF_ValidNKey)==0 ); + return SQLITE_OK; } -/* -** The caller currently has a read transaction open on the database. -** This function takes a SHARED lock on the CHECKPOINTER slot and then -** checks if the snapshot passed as the second argument is still -** available. If so, SQLITE_OK is returned. -** -** If the snapshot is not available, SQLITE_ERROR is returned. Or, if -** the CHECKPOINTER lock cannot be obtained, SQLITE_BUSY. If any error -** occurs (any value other than SQLITE_OK is returned), the CHECKPOINTER -** lock is released before returning. +/* Move the cursor to the first entry in the table. Return SQLITE_OK +** on success. Set *pRes to 0 if the cursor actually points to something +** or set *pRes to 1 if the table is empty. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WalSnapshotCheck(Wal *pWal, sqlcipher_sqlite3_snapshot *pSnapshot){ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ int rc; - rc = walLockShared(pWal, WAL_CKPT_LOCK); + + assert( cursorOwnsBtShared(pCur) ); + assert( sqlcipher_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + rc = moveToRoot(pCur); if( rc==SQLITE_OK ){ - WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot; - if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) - || pNew->mxFramenBackfillAttempted - ){ - rc = SQLITE_ERROR_SNAPSHOT; - walUnlockShared(pWal, WAL_CKPT_LOCK); - } + assert( pCur->pPage->nCell>0 ); + *pRes = 0; + rc = moveToLeftmost(pCur); + }else if( rc==SQLITE_EMPTY ){ + assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); + *pRes = 1; + rc = SQLITE_OK; } return rc; } -/* -** Release a lock obtained by an earlier successful call to -** sqlcipher_sqlite3WalSnapshotCheck(). +/* Move the cursor to the last entry in the table. Return SQLITE_OK +** on success. Set *pRes to 0 if the cursor actually points to something +** or set *pRes to 1 if the table is empty. */ -SQLITE_PRIVATE void sqlcipher_sqlite3WalSnapshotUnlock(Wal *pWal){ - assert( pWal ); - walUnlockShared(pWal, WAL_CKPT_LOCK); -} - +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeLast(BtCursor *pCur, int *pRes){ + int rc; -#endif /* SQLITE_ENABLE_SNAPSHOT */ + assert( cursorOwnsBtShared(pCur) ); + assert( sqlcipher_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); -#ifdef SQLITE_ENABLE_ZIPVFS -/* -** If the argument is not NULL, it points to a Wal object that holds a -** read-lock. This function returns the database page-size if it is known, -** or zero if it is not (or if pWal is NULL). -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3WalFramesize(Wal *pWal){ - assert( pWal==0 || pWal->readLock>=0 ); - return (pWal ? pWal->szPage : 0); -} + /* If the cursor already points to the last entry, this is a no-op. */ + if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ +#ifdef SQLITE_DEBUG + /* This block serves to assert() that the cursor really does point + ** to the last entry in the b-tree. */ + int ii; + for(ii=0; iiiPage; ii++){ + assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); + } + assert( pCur->ix==pCur->pPage->nCell-1 || CORRUPT_DB ); + testcase( pCur->ix!=pCur->pPage->nCell-1 ); + /* ^-- dbsqlfuzz b92b72e4de80b5140c30ab71372ca719b8feb618 */ + assert( pCur->pPage->leaf ); #endif + *pRes = 0; + return SQLITE_OK; + } -/* Return the sqlcipher_sqlite3_file object for the WAL file -*/ -SQLITE_PRIVATE sqlcipher_sqlite3_file *sqlcipher_sqlite3WalFile(Wal *pWal){ - return pWal->pWalFd; + rc = moveToRoot(pCur); + if( rc==SQLITE_OK ){ + assert( pCur->eState==CURSOR_VALID ); + *pRes = 0; + rc = moveToRightmost(pCur); + if( rc==SQLITE_OK ){ + pCur->curFlags |= BTCF_AtLast; + }else{ + pCur->curFlags &= ~BTCF_AtLast; + } + }else if( rc==SQLITE_EMPTY ){ + assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); + *pRes = 1; + rc = SQLITE_OK; + } + return rc; } -#endif /* #ifndef SQLITE_OMIT_WAL */ - -/************** End of wal.c *************************************************/ -/************** Begin file btmutex.c *****************************************/ -/* -** 2007 August 27 +/* Move the cursor so that it points to an entry in a table (a.k.a INTKEY) +** table near the key intKey. Return a success code. ** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** If an exact match is not found, then the cursor is always +** left pointing at a leaf page which would hold the entry if it +** were present. The cursor might point to an entry that comes +** before or after the key. ** -** 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. +** An integer is written into *pRes which is the result of +** comparing the key with the entry to which the cursor is +** pointing. The meaning of the integer written into +** *pRes is as follows: ** -************************************************************************* +** *pRes<0 The cursor is left pointing at an entry that +** is smaller than intKey or if the table is empty +** and the cursor is therefore left point to nothing. ** -** This file contains code used to implement mutexes on Btree objects. -** This code really belongs in btree.c. But btree.c is getting too -** big and we want to break it down some. This packaged seemed like -** a good breakout. +** *pRes==0 The cursor is left pointing at an entry that +** exactly matches intKey. +** +** *pRes>0 The cursor is left pointing at an entry that +** is larger than intKey. */ -/* #include "btreeInt.h" */ -#ifndef SQLITE_OMIT_SHARED_CACHE -#if SQLITE_THREADSAFE +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeTableMoveto( + BtCursor *pCur, /* The cursor to be moved */ + i64 intKey, /* The table key */ + int biasRight, /* If true, bias the search to the high end */ + int *pRes /* Write search results here */ +){ + int rc; -/* -** Obtain the BtShared mutex associated with B-Tree handle p. Also, -** set BtShared.db to the database handle associated with p and the -** p->locked boolean to true. -*/ -static void lockBtreeMutex(Btree *p){ - assert( p->locked==0 ); - assert( sqlcipher_sqlite3_mutex_notheld(p->pBt->mutex) ); - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + assert( cursorOwnsBtShared(pCur) ); + assert( sqlcipher_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + assert( pRes ); + assert( pCur->pKeyInfo==0 ); + assert( pCur->eState!=CURSOR_VALID || pCur->curIntKey!=0 ); - sqlcipher_sqlite3_mutex_enter(p->pBt->mutex); - p->pBt->db = p->db; - p->locked = 1; + /* If the cursor is already positioned at the point we are trying + ** to move to, then just return without doing any work */ + if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){ + if( pCur->info.nKey==intKey ){ + *pRes = 0; + return SQLITE_OK; + } + if( pCur->info.nKeycurFlags & BTCF_AtLast)!=0 ){ + *pRes = -1; + return SQLITE_OK; + } + /* If the requested key is one more than the previous key, then + ** try to get there using sqlcipher_sqlite3BtreeNext() rather than a full + ** binary search. This is an optimization only. The correct answer + ** is still obtained without this case, only a little more slowely */ + if( pCur->info.nKey+1==intKey ){ + *pRes = 0; + rc = sqlcipher_sqlite3BtreeNext(pCur, 0); + if( rc==SQLITE_OK ){ + getCellInfo(pCur); + if( pCur->info.nKey==intKey ){ + return SQLITE_OK; + } + }else if( rc!=SQLITE_DONE ){ + return rc; + } + } + } + } + +#ifdef SQLITE_DEBUG + pCur->pBtree->nSeek++; /* Performance measurement during testing */ +#endif + + rc = moveToRoot(pCur); + if( rc ){ + if( rc==SQLITE_EMPTY ){ + assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); + *pRes = -1; + return SQLITE_OK; + } + return rc; + } + assert( pCur->pPage ); + assert( pCur->pPage->isInit ); + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->pPage->nCell > 0 ); + assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey ); + assert( pCur->curIntKey ); + + for(;;){ + int lwr, upr, idx, c; + Pgno chldPg; + MemPage *pPage = pCur->pPage; + u8 *pCell; /* Pointer to current cell in pPage */ + + /* pPage->nCell must be greater than zero. If this is the root-page + ** the cursor would have been INVALID above and this for(;;) loop + ** not run. If this is not the root-page, then the moveToChild() routine + ** would have already detected db corruption. Similarly, pPage must + ** be the right kind (index or table) of b-tree page. Otherwise + ** a moveToChild() or moveToRoot() call would have detected corruption. */ + assert( pPage->nCell>0 ); + assert( pPage->intKey ); + lwr = 0; + upr = pPage->nCell-1; + assert( biasRight==0 || biasRight==1 ); + idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ + for(;;){ + i64 nCellKey; + pCell = findCellPastPtr(pPage, idx); + if( pPage->intKeyLeaf ){ + while( 0x80 <= *(pCell++) ){ + if( pCell>=pPage->aDataEnd ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + } + } + getVarint(pCell, (u64*)&nCellKey); + if( nCellKeyupr ){ c = -1; break; } + }else if( nCellKey>intKey ){ + upr = idx-1; + if( lwr>upr ){ c = +1; break; } + }else{ + assert( nCellKey==intKey ); + pCur->ix = (u16)idx; + if( !pPage->leaf ){ + lwr = idx; + goto moveto_table_next_layer; + }else{ + pCur->curFlags |= BTCF_ValidNKey; + pCur->info.nKey = nCellKey; + pCur->info.nSize = 0; + *pRes = 0; + return SQLITE_OK; + } + } + assert( lwr+upr>=0 ); + idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */ + } + assert( lwr==upr+1 || !pPage->leaf ); + assert( pPage->isInit ); + if( pPage->leaf ){ + assert( pCur->ixpPage->nCell ); + pCur->ix = (u16)idx; + *pRes = c; + rc = SQLITE_OK; + goto moveto_table_finish; + } +moveto_table_next_layer: + if( lwr>=pPage->nCell ){ + chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); + }else{ + chldPg = get4byte(findCell(pPage, lwr)); + } + pCur->ix = (u16)lwr; + rc = moveToChild(pCur, chldPg); + if( rc ) break; + } +moveto_table_finish: + pCur->info.nSize = 0; + assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); + return rc; } /* -** Release the BtShared mutex associated with B-Tree handle p and -** clear the p->locked boolean. +** Compare the "idx"-th cell on the page the cursor pCur is currently +** pointing to to pIdxKey using xRecordCompare. Return negative or +** zero if the cell is less than or equal pIdxKey. Return positive +** if unknown. +** +** Return value negative: Cell at pCur[idx] less than pIdxKey +** +** Return value is zero: Cell at pCur[idx] equals pIdxKey +** +** Return value positive: Nothing is known about the relationship +** of the cell at pCur[idx] and pIdxKey. +** +** This routine is part of an optimization. It is always safe to return +** a positive value as that will cause the optimization to be skipped. */ -static void SQLITE_NOINLINE unlockBtreeMutex(Btree *p){ - BtShared *pBt = p->pBt; - assert( p->locked==1 ); - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - assert( p->db==pBt->db ); - - sqlcipher_sqlite3_mutex_leave(pBt->mutex); - p->locked = 0; +static int indexCellCompare( + BtCursor *pCur, + int idx, + UnpackedRecord *pIdxKey, + RecordCompare xRecordCompare +){ + MemPage *pPage = pCur->pPage; + int c; + int nCell; /* Size of the pCell cell in bytes */ + u8 *pCell = findCellPastPtr(pPage, idx); + + nCell = pCell[0]; + if( nCell<=pPage->max1bytePayload ){ + /* This branch runs if the record-size field of the cell is a + ** single byte varint and the record fits entirely on the main + ** b-tree page. */ + testcase( pCell+nCell+1==pPage->aDataEnd ); + c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); + }else if( !(pCell[1] & 0x80) + && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal + ){ + /* The record-size field is a 2 byte varint and the record + ** fits entirely on the main b-tree page. */ + testcase( pCell+nCell+2==pPage->aDataEnd ); + c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); + }else{ + /* If the record extends into overflow pages, do not attempt + ** the optimization. */ + c = 99; + } + return c; } -/* Forward reference */ -static void SQLITE_NOINLINE btreeLockCarefully(Btree *p); - /* -** Enter a mutex on the given BTree object. +** Return true (non-zero) if pCur is current pointing to the last +** page of a table. +*/ +static int cursorOnLastPage(BtCursor *pCur){ + int i; + assert( pCur->eState==CURSOR_VALID ); + for(i=0; iiPage; i++){ + MemPage *pPage = pCur->apPage[i]; + if( pCur->aiIdx[i]nCell ) return 0; + } + return 1; +} + +/* Move the cursor so that it points to an entry in an index table +** near the key pIdxKey. Return a success code. ** -** If the object is not sharable, then no mutex is ever required -** and this routine is a no-op. The underlying mutex is non-recursive. -** But we keep a reference count in Btree.wantToLock so the behavior -** of this interface is recursive. +** If an exact match is not found, then the cursor is always +** left pointing at a leaf page which would hold the entry if it +** were present. The cursor might point to an entry that comes +** before or after the key. ** -** To avoid deadlocks, multiple Btrees are locked in the same order -** by all database connections. The p->pNext is a list of other -** Btrees belonging to the same database connection as the p Btree -** which need to be locked after p. If we cannot get a lock on -** p, then first unlock all of the others on p->pNext, then wait -** for the lock to become available on p, then relock all of the -** subsequent Btrees that desire a lock. +** An integer is written into *pRes which is the result of +** comparing the key with the entry to which the cursor is +** pointing. The meaning of the integer written into +** *pRes is as follows: +** +** *pRes<0 The cursor is left pointing at an entry that +** is smaller than pIdxKey or if the table is empty +** and the cursor is therefore left point to nothing. +** +** *pRes==0 The cursor is left pointing at an entry that +** exactly matches pIdxKey. +** +** *pRes>0 The cursor is left pointing at an entry that +** is larger than pIdxKey. +** +** The pIdxKey->eqSeen field is set to 1 if there +** exists an entry in the table that exactly matches pIdxKey. */ -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeEnter(Btree *p){ - /* Some basic sanity checking on the Btree. The list of Btrees - ** connected by pNext and pPrev should be in sorted order by - ** Btree.pBt value. All elements of the list should belong to - ** the same connection. Only shared Btrees are on the list. */ - assert( p->pNext==0 || p->pNext->pBt>p->pBt ); - assert( p->pPrev==0 || p->pPrev->pBtpBt ); - assert( p->pNext==0 || p->pNext->db==p->db ); - assert( p->pPrev==0 || p->pPrev->db==p->db ); - assert( p->sharable || (p->pNext==0 && p->pPrev==0) ); - - /* Check for locking consistency */ - assert( !p->locked || p->wantToLock>0 ); - assert( p->sharable || p->wantToLock==0 ); +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeIndexMoveto( + BtCursor *pCur, /* The cursor to be moved */ + UnpackedRecord *pIdxKey, /* Unpacked index key */ + int *pRes /* Write search results here */ +){ + int rc; + RecordCompare xRecordCompare; - /* We should already hold a lock on the database connection */ - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + assert( cursorOwnsBtShared(pCur) ); + assert( sqlcipher_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + assert( pRes ); + assert( pCur->pKeyInfo!=0 ); - /* Unless the database is sharable and unlocked, then BtShared.db - ** should already be set correctly. */ - assert( (p->locked==0 && p->sharable) || p->pBt->db==p->db ); +#ifdef SQLITE_DEBUG + pCur->pBtree->nSeek++; /* Performance measurement during testing */ +#endif - if( !p->sharable ) return; - p->wantToLock++; - if( p->locked ) return; - btreeLockCarefully(p); -} + xRecordCompare = sqlcipher_sqlite3VdbeFindCompare(pIdxKey); + pIdxKey->errCode = 0; + assert( pIdxKey->default_rc==1 + || pIdxKey->default_rc==0 + || pIdxKey->default_rc==-1 + ); -/* This is a helper function for sqlcipher_sqlite3BtreeLock(). By moving -** complex, but seldom used logic, out of sqlcipher_sqlite3BtreeLock() and -** into this routine, we avoid unnecessary stack pointer changes -** and thus help the sqlcipher_sqlite3BtreeLock() routine to run much faster -** in the common case. -*/ -static void SQLITE_NOINLINE btreeLockCarefully(Btree *p){ - Btree *pLater; - /* In most cases, we should be able to acquire the lock we - ** want without having to go through the ascending lock - ** procedure that follows. Just be sure not to block. - */ - if( sqlcipher_sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){ - p->pBt->db = p->db; - p->locked = 1; - return; + /* Check to see if we can skip a lot of work. Two cases: + ** + ** (1) If the cursor is already pointing to the very last cell + ** in the table and the pIdxKey search key is greater than or + ** equal to that last cell, then no movement is required. + ** + ** (2) If the cursor is on the last page of the table and the first + ** cell on that last page is less than or equal to the pIdxKey + ** search key, then we can start the search on the current page + ** without needing to go back to root. + */ + if( pCur->eState==CURSOR_VALID + && pCur->pPage->leaf + && cursorOnLastPage(pCur) + ){ + int c; + if( pCur->ix==pCur->pPage->nCell-1 + && (c = indexCellCompare(pCur, pCur->ix, pIdxKey, xRecordCompare))<=0 + && pIdxKey->errCode==SQLITE_OK + ){ + *pRes = c; + return SQLITE_OK; /* Cursor already pointing at the correct spot */ + } + if( pCur->iPage>0 + && indexCellCompare(pCur, 0, pIdxKey, xRecordCompare)<=0 + && pIdxKey->errCode==SQLITE_OK + ){ + pCur->curFlags &= ~BTCF_ValidOvfl; + if( !pCur->pPage->isInit ){ + return SQLITE_CORRUPT_BKPT; + } + goto bypass_moveto_root; /* Start search on the current page */ + } + pIdxKey->errCode = SQLITE_OK; } - /* To avoid deadlock, first release all locks with a larger - ** BtShared address. Then acquire our lock. Then reacquire - ** the other BtShared locks that we used to hold in ascending - ** order. - */ - for(pLater=p->pNext; pLater; pLater=pLater->pNext){ - assert( pLater->sharable ); - assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt ); - assert( !pLater->locked || pLater->wantToLock>0 ); - if( pLater->locked ){ - unlockBtreeMutex(pLater); + rc = moveToRoot(pCur); + if( rc ){ + if( rc==SQLITE_EMPTY ){ + assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); + *pRes = -1; + return SQLITE_OK; } + return rc; } - lockBtreeMutex(p); - for(pLater=p->pNext; pLater; pLater=pLater->pNext){ - if( pLater->wantToLock ){ - lockBtreeMutex(pLater); + +bypass_moveto_root: + assert( pCur->pPage ); + assert( pCur->pPage->isInit ); + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->pPage->nCell > 0 ); + assert( pCur->curIntKey==0 ); + assert( pIdxKey!=0 ); + for(;;){ + int lwr, upr, idx, c; + Pgno chldPg; + MemPage *pPage = pCur->pPage; + u8 *pCell; /* Pointer to current cell in pPage */ + + /* pPage->nCell must be greater than zero. If this is the root-page + ** the cursor would have been INVALID above and this for(;;) loop + ** not run. If this is not the root-page, then the moveToChild() routine + ** would have already detected db corruption. Similarly, pPage must + ** be the right kind (index or table) of b-tree page. Otherwise + ** a moveToChild() or moveToRoot() call would have detected corruption. */ + assert( pPage->nCell>0 ); + assert( pPage->intKey==0 ); + lwr = 0; + upr = pPage->nCell-1; + idx = upr>>1; /* idx = (lwr+upr)/2; */ + for(;;){ + int nCell; /* Size of the pCell cell in bytes */ + pCell = findCellPastPtr(pPage, idx); + + /* The maximum supported page-size is 65536 bytes. This means that + ** the maximum number of record bytes stored on an index B-Tree + ** page is less than 16384 bytes and may be stored as a 2-byte + ** varint. This information is used to attempt to avoid parsing + ** the entire cell by checking for the cases where the record is + ** stored entirely within the b-tree page by inspecting the first + ** 2 bytes of the cell. + */ + nCell = pCell[0]; + if( nCell<=pPage->max1bytePayload ){ + /* This branch runs if the record-size field of the cell is a + ** single byte varint and the record fits entirely on the main + ** b-tree page. */ + testcase( pCell+nCell+1==pPage->aDataEnd ); + c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); + }else if( !(pCell[1] & 0x80) + && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal + ){ + /* The record-size field is a 2 byte varint and the record + ** fits entirely on the main b-tree page. */ + testcase( pCell+nCell+2==pPage->aDataEnd ); + c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); + }else{ + /* The record flows over onto one or more overflow pages. In + ** this case the whole cell needs to be parsed, a buffer allocated + ** and accessPayload() used to retrieve the record into the + ** buffer before VdbeRecordCompare() can be called. + ** + ** If the record is corrupt, the xRecordCompare routine may read + ** up to two varints past the end of the buffer. An extra 18 + ** bytes of padding is allocated at the end of the buffer in + ** case this happens. */ + void *pCellKey; + u8 * const pCellBody = pCell - pPage->childPtrSize; + const int nOverrun = 18; /* Size of the overrun padding */ + pPage->xParseCell(pPage, pCellBody, &pCur->info); + nCell = (int)pCur->info.nKey; + testcase( nCell<0 ); /* True if key size is 2^32 or more */ + testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ + testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ + testcase( nCell==2 ); /* Minimum legal index key size */ + if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){ + rc = SQLITE_CORRUPT_PAGE(pPage); + goto moveto_index_finish; + } + pCellKey = sqlcipher_sqlite3Malloc( nCell+nOverrun ); + if( pCellKey==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto moveto_index_finish; + } + pCur->ix = (u16)idx; + rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); + memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */ + pCur->curFlags &= ~BTCF_ValidOvfl; + if( rc ){ + sqlcipher_sqlite3_free(pCellKey); + goto moveto_index_finish; + } + c = sqlcipher_sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); + sqlcipher_sqlite3_free(pCellKey); + } + assert( + (pIdxKey->errCode!=SQLITE_CORRUPT || c==0) + && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed) + ); + if( c<0 ){ + lwr = idx+1; + }else if( c>0 ){ + upr = idx-1; + }else{ + assert( c==0 ); + *pRes = 0; + rc = SQLITE_OK; + pCur->ix = (u16)idx; + if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT; + goto moveto_index_finish; + } + if( lwr>upr ) break; + assert( lwr+upr>=0 ); + idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */ + } + assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) ); + assert( pPage->isInit ); + if( pPage->leaf ){ + assert( pCur->ixpPage->nCell || CORRUPT_DB ); + pCur->ix = (u16)idx; + *pRes = c; + rc = SQLITE_OK; + goto moveto_index_finish; + } + if( lwr>=pPage->nCell ){ + chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); + }else{ + chldPg = get4byte(findCell(pPage, lwr)); } + pCur->ix = (u16)lwr; + rc = moveToChild(pCur, chldPg); + if( rc ) break; } +moveto_index_finish: + pCur->info.nSize = 0; + assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); + return rc; } /* -** Exit the recursive mutex on a Btree. +** Return TRUE if the cursor is not pointing at an entry of the table. +** +** TRUE will be returned after a call to sqlcipher_sqlite3BtreeNext() moves +** past the last entry in the table or sqlcipher_sqlite3BtreePrev() moves past +** the first entry. TRUE is also returned if the table is empty. */ -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeLeave(Btree *p){ - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - if( p->sharable ){ - assert( p->wantToLock>0 ); - p->wantToLock--; - if( p->wantToLock==0 ){ - unlockBtreeMutex(p); - } - } +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeEof(BtCursor *pCur){ + /* TODO: What if the cursor is in CURSOR_REQUIRESEEK but all table entries + ** have been deleted? This API will need to change to return an error code + ** as well as the boolean result value. + */ + return (CURSOR_VALID!=pCur->eState); } -#ifndef NDEBUG /* -** Return true if the BtShared mutex is held on the btree, or if the -** B-Tree is not marked as sharable. -** -** This routine is used only from within assert() statements. +** Return an estimate for the number of rows in the table that pCur is +** pointing to. Return a negative number if no estimate is currently +** available. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeHoldsMutex(Btree *p){ - assert( p->sharable==0 || p->locked==0 || p->wantToLock>0 ); - assert( p->sharable==0 || p->locked==0 || p->db==p->pBt->db ); - assert( p->sharable==0 || p->locked==0 || sqlcipher_sqlite3_mutex_held(p->pBt->mutex) ); - assert( p->sharable==0 || p->locked==0 || sqlcipher_sqlite3_mutex_held(p->db->mutex) ); +SQLITE_PRIVATE i64 sqlcipher_sqlite3BtreeRowCountEst(BtCursor *pCur){ + i64 n; + u8 i; - return (p->sharable==0 || p->locked); -} -#endif + assert( cursorOwnsBtShared(pCur) ); + assert( sqlcipher_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + + /* Currently this interface is only called by the OP_IfSmaller + ** opcode, and it that case the cursor will always be valid and + ** will always point to a leaf node. */ + if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1; + if( NEVER(pCur->pPage->leaf==0) ) return -1; + n = pCur->pPage->nCell; + for(i=0; iiPage; i++){ + n *= pCur->apPage[i]->nCell; + } + return n; +} /* -** Enter the mutex on every Btree associated with a database -** connection. This is needed (for example) prior to parsing -** a statement since we will be comparing table and column names -** against all schemas and we do not want those schemas being -** reset out from under us. +** Advance the cursor to the next entry in the database. +** Return value: ** -** There is a corresponding leave-all procedures. +** SQLITE_OK success +** SQLITE_DONE cursor is already pointing at the last element +** otherwise some kind of error occurred ** -** Enter the mutexes in accending order by BtShared pointer address -** to avoid the possibility of deadlock when two threads with -** two or more btrees in common both try to lock all their btrees -** at the same instant. +** The main entry point is sqlcipher_sqlite3BtreeNext(). That routine is optimized +** for the common case of merely incrementing the cell counter BtCursor.aiIdx +** to the next cell on the current page. The (slower) btreeNext() helper +** routine is called when it is necessary to move to a different page or +** to restore the cursor. +** +** If bit 0x01 of the F argument in sqlcipher_sqlite3BtreeNext(C,F) is 1, then the +** cursor corresponds to an SQL index and this routine could have been +** skipped if the SQL index had been a unique index. The F argument +** is a hint to the implement. SQLite btree implementation does not use +** this hint, but COMDB2 does. */ -static void SQLITE_NOINLINE btreeEnterAll(sqlcipher_sqlite3 *db){ - int i; - int skipOk = 1; - Btree *p; - assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - for(i=0; inDb; i++){ - p = db->aDb[i].pBt; - if( p && p->sharable ){ - sqlcipher_sqlite3BtreeEnter(p); - skipOk = 0; +static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ + int rc; + int idx; + MemPage *pPage; + + assert( cursorOwnsBtShared(pCur) ); + if( pCur->eState!=CURSOR_VALID ){ + assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); + rc = restoreCursorPosition(pCur); + if( rc!=SQLITE_OK ){ + return rc; + } + if( CURSOR_INVALID==pCur->eState ){ + return SQLITE_DONE; + } + if( pCur->eState==CURSOR_SKIPNEXT ){ + pCur->eState = CURSOR_VALID; + if( pCur->skipNext>0 ) return SQLITE_OK; } } - db->noSharedCache = skipOk; -} -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeEnterAll(sqlcipher_sqlite3 *db){ - if( db->noSharedCache==0 ) btreeEnterAll(db); -} -static void SQLITE_NOINLINE btreeLeaveAll(sqlcipher_sqlite3 *db){ - int i; - Btree *p; - assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - for(i=0; inDb; i++){ - p = db->aDb[i].pBt; - if( p ) sqlcipher_sqlite3BtreeLeave(p); - } -} -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeLeaveAll(sqlcipher_sqlite3 *db){ - if( db->noSharedCache==0 ) btreeLeaveAll(db); -} -#ifndef NDEBUG -/* -** Return true if the current thread holds the database connection -** mutex and all required BtShared mutexes. -** -** This routine is used inside assert() statements only. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeHoldsAllMutexes(sqlcipher_sqlite3 *db){ - int i; - if( !sqlcipher_sqlite3_mutex_held(db->mutex) ){ - return 0; + pPage = pCur->pPage; + idx = ++pCur->ix; + if( !pPage->isInit || sqlcipher_sqlite3FaultSim(412) ){ + /* The only known way for this to happen is for there to be a + ** recursive SQL function that does a DELETE operation as part of a + ** SELECT which deletes content out from under an active cursor + ** in a corrupt database file where the table being DELETE-ed from + ** has pages in common with the table being queried. See TH3 + ** module cov1/btree78.test testcase 220 (2018-06-08) for an + ** example. */ + return SQLITE_CORRUPT_BKPT; } - for(i=0; inDb; i++){ - Btree *p; - p = db->aDb[i].pBt; - if( p && p->sharable && - (p->wantToLock==0 || !sqlcipher_sqlite3_mutex_held(p->pBt->mutex)) ){ - return 0; + + if( idx>=pPage->nCell ){ + if( !pPage->leaf ){ + rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); + if( rc ) return rc; + return moveToLeftmost(pCur); + } + do{ + if( pCur->iPage==0 ){ + pCur->eState = CURSOR_INVALID; + return SQLITE_DONE; + } + moveToParent(pCur); + pPage = pCur->pPage; + }while( pCur->ix>=pPage->nCell ); + if( pPage->intKey ){ + return sqlcipher_sqlite3BtreeNext(pCur, 0); + }else{ + return SQLITE_OK; } } - return 1; + if( pPage->leaf ){ + return SQLITE_OK; + }else{ + return moveToLeftmost(pCur); + } +} +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeNext(BtCursor *pCur, int flags){ + MemPage *pPage; + UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ + assert( cursorOwnsBtShared(pCur) ); + assert( flags==0 || flags==1 ); + pCur->info.nSize = 0; + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); + if( pCur->eState!=CURSOR_VALID ) return btreeNext(pCur); + pPage = pCur->pPage; + if( (++pCur->ix)>=pPage->nCell ){ + pCur->ix--; + return btreeNext(pCur); + } + if( pPage->leaf ){ + return SQLITE_OK; + }else{ + return moveToLeftmost(pCur); + } } -#endif /* NDEBUG */ -#ifndef NDEBUG /* -** Return true if the correct mutexes are held for accessing the -** db->aDb[iDb].pSchema structure. The mutexes required for schema -** access are: +** Step the cursor to the back to the previous entry in the database. +** Return values: ** -** (1) The mutex on db -** (2) if iDb!=1, then the mutex on db->aDb[iDb].pBt. +** SQLITE_OK success +** SQLITE_DONE the cursor is already on the first element of the table +** otherwise some kind of error occurred ** -** If pSchema is not NULL, then iDb is computed from pSchema and -** db using sqlcipher_sqlite3SchemaToIndex(). -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3SchemaMutexHeld(sqlcipher_sqlite3 *db, int iDb, Schema *pSchema){ - Btree *p; - assert( db!=0 ); - if( pSchema ) iDb = sqlcipher_sqlite3SchemaToIndex(db, pSchema); - assert( iDb>=0 && iDbnDb ); - if( !sqlcipher_sqlite3_mutex_held(db->mutex) ) return 0; - if( iDb==1 ) return 1; - p = db->aDb[iDb].pBt; - assert( p!=0 ); - return p->sharable==0 || p->locked==1; -} -#endif /* NDEBUG */ - -#else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */ -/* -** The following are special cases for mutex enter routines for use -** in single threaded applications that use shared cache. Except for -** these two routines, all mutex operations are no-ops in that case and -** are null #defines in btree.h. +** The main entry point is sqlcipher_sqlite3BtreePrevious(). That routine is optimized +** for the common case of merely decrementing the cell counter BtCursor.aiIdx +** to the previous cell on the current page. The (slower) btreePrevious() +** helper routine is called when it is necessary to move to a different page +** or to restore the cursor. ** -** If shared cache is disabled, then all btree mutex routines, including -** the ones below, are no-ops and are null #defines in btree.h. +** If bit 0x01 of the F argument to sqlcipher_sqlite3BtreePrevious(C,F) is 1, then +** the cursor corresponds to an SQL index and this routine could have been +** skipped if the SQL index had been a unique index. The F argument is a +** hint to the implement. The native SQLite btree implementation does not +** use this hint, but COMDB2 does. */ +static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ + int rc; + MemPage *pPage; -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeEnter(Btree *p){ - p->pBt->db = p->db; -} -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeEnterAll(sqlcipher_sqlite3 *db){ - int i; - for(i=0; inDb; i++){ - Btree *p = db->aDb[i].pBt; - if( p ){ - p->pBt->db = p->db; + assert( cursorOwnsBtShared(pCur) ); + assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 ); + assert( pCur->info.nSize==0 ); + if( pCur->eState!=CURSOR_VALID ){ + rc = restoreCursorPosition(pCur); + if( rc!=SQLITE_OK ){ + return rc; + } + if( CURSOR_INVALID==pCur->eState ){ + return SQLITE_DONE; + } + if( CURSOR_SKIPNEXT==pCur->eState ){ + pCur->eState = CURSOR_VALID; + if( pCur->skipNext<0 ) return SQLITE_OK; } } -} -#endif /* if SQLITE_THREADSAFE */ -#ifndef SQLITE_OMIT_INCRBLOB -/* -** Enter a mutex on a Btree given a cursor owned by that Btree. -** -** These entry points are used by incremental I/O only. Enter() is required -** any time OMIT_SHARED_CACHE is not defined, regardless of whether or not -** the build is threadsafe. Leave() is only required by threadsafe builds. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeEnterCursor(BtCursor *pCur){ - sqlcipher_sqlite3BtreeEnter(pCur->pBtree); + pPage = pCur->pPage; + assert( pPage->isInit ); + if( !pPage->leaf ){ + int idx = pCur->ix; + rc = moveToChild(pCur, get4byte(findCell(pPage, idx))); + if( rc ) return rc; + rc = moveToRightmost(pCur); + }else{ + while( pCur->ix==0 ){ + if( pCur->iPage==0 ){ + pCur->eState = CURSOR_INVALID; + return SQLITE_DONE; + } + moveToParent(pCur); + } + assert( pCur->info.nSize==0 ); + assert( (pCur->curFlags & (BTCF_ValidOvfl))==0 ); + + pCur->ix--; + pPage = pCur->pPage; + if( pPage->intKey && !pPage->leaf ){ + rc = sqlcipher_sqlite3BtreePrevious(pCur, 0); + }else{ + rc = SQLITE_OK; + } + } + return rc; } -# if SQLITE_THREADSAFE -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeLeaveCursor(BtCursor *pCur){ - sqlcipher_sqlite3BtreeLeave(pCur->pBtree); +SQLITE_PRIVATE int sqlcipher_sqlite3BtreePrevious(BtCursor *pCur, int flags){ + assert( cursorOwnsBtShared(pCur) ); + assert( flags==0 || flags==1 ); + UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ + pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey); + pCur->info.nSize = 0; + if( pCur->eState!=CURSOR_VALID + || pCur->ix==0 + || pCur->pPage->leaf==0 + ){ + return btreePrevious(pCur); + } + pCur->ix--; + return SQLITE_OK; } -# endif -#endif /* ifndef SQLITE_OMIT_INCRBLOB */ - -#endif /* ifndef SQLITE_OMIT_SHARED_CACHE */ -/************** End of btmutex.c *********************************************/ -/************** Begin file btree.c *******************************************/ /* -** 2004 April 6 +** Allocate a new page from the database file. ** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** The new page is marked as dirty. (In other words, sqlcipher_sqlite3PagerWrite() +** has already been called on the new page.) The new page has also +** been referenced and the calling routine is responsible for calling +** sqlcipher_sqlite3PagerUnref() on the new page when it is done. ** -** 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. +** SQLITE_OK is returned on success. Any other return value indicates +** an error. *ppPage is set to NULL in the event of an error. ** -************************************************************************* -** This file implements an external (disk-based) database using BTrees. -** See the header comment on "btreeInt.h" for additional information. -** Including a description of file format and an overview of operation. -*/ -/* #include "btreeInt.h" */ - -/* -** The header string that appears at the beginning of every -** SQLite database. -*/ -static const char zMagicHeader[] = SQLITE_FILE_HEADER; - -/* -** Set this global variable to 1 to enable tracing using the TRACE -** macro. -*/ -#if 0 -int sqlcipher_sqlite3BtreeTrace=1; /* True to enable tracing */ -# define TRACE(X) if(sqlcipher_sqlite3BtreeTrace){printf X;fflush(stdout);} -#else -# define TRACE(X) -#endif - -/* -** Extract a 2-byte big-endian integer from an array of unsigned bytes. -** But if the value is zero, make it 65536. +** If the "nearby" parameter is not 0, then an effort is made to +** locate a page close to the page number "nearby". This can be used in an +** attempt to keep related pages close to each other in the database file, +** which in turn can make database access faster. ** -** This routine is used to extract the "offset to cell content area" value -** from the header of a btree page. If the page size is 65536 and the page -** is empty, the offset should be 65536, but the 2-byte value stores zero. -** This routine makes the necessary adjustment to 65536. +** If the eMode parameter is BTALLOC_EXACT and the nearby page exists +** anywhere on the free-list, then it is guaranteed to be returned. If +** eMode is BTALLOC_LT then the page returned will be less than or equal +** to nearby if any such page exists. If eMode is BTALLOC_ANY then there +** are no restrictions on which page is returned. */ -#define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1) +static int allocateBtreePage( + BtShared *pBt, /* The btree */ + MemPage **ppPage, /* Store pointer to the allocated page here */ + Pgno *pPgno, /* Store the page number here */ + Pgno nearby, /* Search for a page near this one */ + u8 eMode /* BTALLOC_EXACT, BTALLOC_LT, or BTALLOC_ANY */ +){ + MemPage *pPage1; + int rc; + u32 n; /* Number of pages on the freelist */ + u32 k; /* Number of leaves on the trunk of the freelist */ + MemPage *pTrunk = 0; + MemPage *pPrevTrunk = 0; + Pgno mxPage; /* Total size of the database file */ -/* -** Values passed as the 5th argument to allocateBtreePage() -*/ -#define BTALLOC_ANY 0 /* Allocate any page */ -#define BTALLOC_EXACT 1 /* Allocate exact page if possible */ -#define BTALLOC_LE 2 /* Allocate any page <= the parameter */ + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) ); + pPage1 = pBt->pPage1; + mxPage = btreePagecount(pBt); + /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36 + ** stores stores the total number of pages on the freelist. */ + n = get4byte(&pPage1->aData[36]); + testcase( n==mxPage-1 ); + if( n>=mxPage ){ + return SQLITE_CORRUPT_BKPT; + } + if( n>0 ){ + /* There are pages on the freelist. Reuse one of those pages. */ + Pgno iTrunk; + u8 searchList = 0; /* If the free-list must be searched for 'nearby' */ + u32 nSearch = 0; /* Count of the number of search attempts */ -/* -** Macro IfNotOmitAV(x) returns (x) if SQLITE_OMIT_AUTOVACUUM is not -** defined, or 0 if it is. For example: -** -** bIncrVacuum = IfNotOmitAV(pBtShared->incrVacuum); -*/ + /* If eMode==BTALLOC_EXACT and a query of the pointer-map + ** shows that the page 'nearby' is somewhere on the free-list, then + ** the entire-list will be searched for that page. + */ #ifndef SQLITE_OMIT_AUTOVACUUM -#define IfNotOmitAV(expr) (expr) -#else -#define IfNotOmitAV(expr) 0 + if( eMode==BTALLOC_EXACT ){ + if( nearby<=mxPage ){ + u8 eType; + assert( nearby>0 ); + assert( pBt->autoVacuum ); + rc = ptrmapGet(pBt, nearby, &eType, 0); + if( rc ) return rc; + if( eType==PTRMAP_FREEPAGE ){ + searchList = 1; + } + } + }else if( eMode==BTALLOC_LE ){ + searchList = 1; + } #endif -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** A list of BtShared objects that are eligible for participation -** in shared cache. This variable has file scope during normal builds, -** but the test harness needs to access it so we make it global for -** test builds. -** -** Access to this variable is protected by SQLITE_MUTEX_STATIC_MAIN. -*/ -#ifdef SQLITE_TEST -SQLITE_PRIVATE BtShared *SQLITE_WSD sqlcipher_sqlite3SharedCacheList = 0; -#else -static BtShared *SQLITE_WSD sqlcipher_sqlite3SharedCacheList = 0; -#endif -#endif /* SQLITE_OMIT_SHARED_CACHE */ + /* Decrement the free-list count by 1. Set iTrunk to the index of the + ** first free-list trunk page. iPrevTrunk is initially 1. + */ + rc = sqlcipher_sqlite3PagerWrite(pPage1->pDbPage); + if( rc ) return rc; + put4byte(&pPage1->aData[36], n-1); -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** Enable or disable the shared pager and schema features. -** -** This routine has no effect on existing database connections. -** The shared cache setting effects only future calls to -** sqlcipher_sqlite3_open(), sqlcipher_sqlite3_open16(), or sqlcipher_sqlite3_open_v2(). -*/ -SQLITE_API int sqlcipher_sqlite3_enable_shared_cache(int enable){ - sqlcipher_sqlite3GlobalConfig.sharedCacheEnabled = enable; - return SQLITE_OK; -} + /* The code within this loop is run only once if the 'searchList' variable + ** is not true. Otherwise, it runs once for each trunk-page on the + ** free-list until the page 'nearby' is located (eMode==BTALLOC_EXACT) + ** or until a page less than 'nearby' is located (eMode==BTALLOC_LT) + */ + do { + pPrevTrunk = pTrunk; + if( pPrevTrunk ){ + /* EVIDENCE-OF: R-01506-11053 The first integer on a freelist trunk page + ** is the page number of the next freelist trunk page in the list or + ** zero if this is the last freelist trunk page. */ + iTrunk = get4byte(&pPrevTrunk->aData[0]); + }else{ + /* EVIDENCE-OF: R-59841-13798 The 4-byte big-endian integer at offset 32 + ** stores the page number of the first page of the freelist, or zero if + ** the freelist is empty. */ + iTrunk = get4byte(&pPage1->aData[32]); + } + testcase( iTrunk==mxPage ); + if( iTrunk>mxPage || nSearch++ > n ){ + rc = SQLITE_CORRUPT_PGNO(pPrevTrunk ? pPrevTrunk->pgno : 1); + }else{ + rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0); + } + if( rc ){ + pTrunk = 0; + goto end_allocate_page; + } + assert( pTrunk!=0 ); + assert( pTrunk->aData!=0 ); + /* EVIDENCE-OF: R-13523-04394 The second integer on a freelist trunk page + ** is the number of leaf page pointers to follow. */ + k = get4byte(&pTrunk->aData[4]); + if( k==0 && !searchList ){ + /* The trunk has no leaves and the list is not being searched. + ** So extract the trunk page itself and use it as the newly + ** allocated page */ + assert( pPrevTrunk==0 ); + rc = sqlcipher_sqlite3PagerWrite(pTrunk->pDbPage); + if( rc ){ + goto end_allocate_page; + } + *pPgno = iTrunk; + memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); + *ppPage = pTrunk; + pTrunk = 0; + TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); + }else if( k>(u32)(pBt->usableSize/4 - 2) ){ + /* Value of k is out of range. Database corruption */ + rc = SQLITE_CORRUPT_PGNO(iTrunk); + goto end_allocate_page; +#ifndef SQLITE_OMIT_AUTOVACUUM + }else if( searchList + && (nearby==iTrunk || (iTrunkpDbPage); + if( rc ){ + goto end_allocate_page; + } + if( k==0 ){ + if( !pPrevTrunk ){ + memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); + }else{ + rc = sqlcipher_sqlite3PagerWrite(pPrevTrunk->pDbPage); + if( rc!=SQLITE_OK ){ + goto end_allocate_page; + } + memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4); + } + }else{ + /* The trunk page is required by the caller but it contains + ** pointers to free-list leaves. The first leaf becomes a trunk + ** page in this case. + */ + MemPage *pNewTrunk; + Pgno iNewTrunk = get4byte(&pTrunk->aData[8]); + if( iNewTrunk>mxPage ){ + rc = SQLITE_CORRUPT_PGNO(iTrunk); + goto end_allocate_page; + } + testcase( iNewTrunk==mxPage ); + rc = btreeGetUnusedPage(pBt, iNewTrunk, &pNewTrunk, 0); + if( rc!=SQLITE_OK ){ + goto end_allocate_page; + } + rc = sqlcipher_sqlite3PagerWrite(pNewTrunk->pDbPage); + if( rc!=SQLITE_OK ){ + releasePage(pNewTrunk); + goto end_allocate_page; + } + memcpy(&pNewTrunk->aData[0], &pTrunk->aData[0], 4); + put4byte(&pNewTrunk->aData[4], k-1); + memcpy(&pNewTrunk->aData[8], &pTrunk->aData[12], (k-1)*4); + releasePage(pNewTrunk); + if( !pPrevTrunk ){ + assert( sqlcipher_sqlite3PagerIswriteable(pPage1->pDbPage) ); + put4byte(&pPage1->aData[32], iNewTrunk); + }else{ + rc = sqlcipher_sqlite3PagerWrite(pPrevTrunk->pDbPage); + if( rc ){ + goto end_allocate_page; + } + put4byte(&pPrevTrunk->aData[0], iNewTrunk); + } + } + pTrunk = 0; + TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); #endif + }else if( k>0 ){ + /* Extract a leaf from the trunk */ + u32 closest; + Pgno iPage; + unsigned char *aData = pTrunk->aData; + if( nearby>0 ){ + u32 i; + closest = 0; + if( eMode==BTALLOC_LE ){ + for(i=0; imxPage || iPage<2 ){ + rc = SQLITE_CORRUPT_PGNO(iTrunk); + goto end_allocate_page; + } + testcase( iPage==mxPage ); + if( !searchList + || (iPage==nearby || (iPagepgno, n-1)); + rc = sqlcipher_sqlite3PagerWrite(pTrunk->pDbPage); + if( rc ) goto end_allocate_page; + if( closestpDbPage); + if( rc!=SQLITE_OK ){ + releasePage(*ppPage); + *ppPage = 0; + } + } + searchList = 0; + } + } + releasePage(pPrevTrunk); + pPrevTrunk = 0; + }while( searchList ); + }else{ + /* There are no pages on the freelist, so append a new page to the + ** database image. + ** + ** Normally, new pages allocated by this block can be requested from the + ** pager layer with the 'no-content' flag set. This prevents the pager + ** from trying to read the pages content from disk. However, if the + ** current transaction has already run one or more incremental-vacuum + ** steps, then the page we are about to allocate may contain content + ** that is required in the event of a rollback. In this case, do + ** not set the no-content flag. This causes the pager to load and journal + ** the current page content before overwriting it. + ** + ** Note that the pager will not actually attempt to load or journal + ** content for any page that really does lie past the end of the database + ** file on disk. So the effects of disabling the no-content optimization + ** here are confined to those pages that lie between the end of the + ** database image and the end of the database file. + */ + int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate))? PAGER_GET_NOCONTENT:0; + rc = sqlcipher_sqlite3PagerWrite(pBt->pPage1->pDbPage); + if( rc ) return rc; + pBt->nPage++; + if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ) pBt->nPage++; -#ifdef SQLITE_OMIT_SHARED_CACHE - /* - ** The functions querySharedCacheTableLock(), setSharedCacheTableLock(), - ** and clearAllSharedCacheTableLocks() - ** manipulate entries in the BtShared.pLock linked list used to store - ** shared-cache table level locks. If the library is compiled with the - ** shared-cache feature disabled, then there is only ever one user - ** of each BtShared structure and so this locking is not necessary. - ** So define the lock related functions as no-ops. - */ - #define querySharedCacheTableLock(a,b,c) SQLITE_OK - #define setSharedCacheTableLock(a,b,c) SQLITE_OK - #define clearAllSharedCacheTableLocks(a) - #define downgradeAllSharedCacheTableLocks(a) - #define hasSharedCacheTableLock(a,b,c,d) 1 - #define hasReadConflicts(a, b) 0 +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, pBt->nPage) ){ + /* If *pPgno refers to a pointer-map page, allocate two new pages + ** at the end of the file instead of one. The first allocated page + ** becomes a new pointer-map page, the second is used by the caller. + */ + MemPage *pPg = 0; + TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage)); + assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) ); + rc = btreeGetUnusedPage(pBt, pBt->nPage, &pPg, bNoContent); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3PagerWrite(pPg->pDbPage); + releasePage(pPg); + } + if( rc ) return rc; + pBt->nPage++; + if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ){ pBt->nPage++; } + } #endif + put4byte(28 + (u8*)pBt->pPage1->aData, pBt->nPage); + *pPgno = pBt->nPage; -#ifdef SQLITE_DEBUG -/* -** Return and reset the seek counter for a Btree object. -*/ -SQLITE_PRIVATE sqlcipher_sqlite3_uint64 sqlcipher_sqlite3BtreeSeekCount(Btree *pBt){ - u64 n = pBt->nSeek; - pBt->nSeek = 0; - return n; + assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); + rc = btreeGetUnusedPage(pBt, *pPgno, ppPage, bNoContent); + if( rc ) return rc; + rc = sqlcipher_sqlite3PagerWrite((*ppPage)->pDbPage); + if( rc!=SQLITE_OK ){ + releasePage(*ppPage); + *ppPage = 0; + } + TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); + } + + assert( CORRUPT_DB || *pPgno!=PENDING_BYTE_PAGE(pBt) ); + +end_allocate_page: + releasePage(pTrunk); + releasePage(pPrevTrunk); + assert( rc!=SQLITE_OK || sqlcipher_sqlite3PagerPageRefcount((*ppPage)->pDbPage)<=1 ); + assert( rc!=SQLITE_OK || (*ppPage)->isInit==0 ); + return rc; } -#endif /* -** Implementation of the SQLITE_CORRUPT_PAGE() macro. Takes a single -** (MemPage*) as an argument. The (MemPage*) must not be NULL. +** This function is used to add page iPage to the database file free-list. +** It is assumed that the page is not already a part of the free-list. ** -** If SQLITE_DEBUG is not defined, then this macro is equivalent to -** SQLITE_CORRUPT_BKPT. Or, if SQLITE_DEBUG is set, then the log message -** normally produced as a side-effect of SQLITE_CORRUPT_BKPT is augmented -** with the page number and filename associated with the (MemPage*). +** The value passed as the second argument to this function is optional. +** If the caller happens to have a pointer to the MemPage object +** corresponding to page iPage handy, it may pass it as the second value. +** Otherwise, it may pass NULL. +** +** If a pointer to a MemPage object is passed as the second argument, +** its reference count is not altered by this function. */ -#ifdef SQLITE_DEBUG -int corruptPageError(int lineno, MemPage *p){ - char *zMsg; - sqlcipher_sqlite3BeginBenignMalloc(); - zMsg = sqlcipher_sqlite3_mprintf("database corruption page %d of %s", - (int)p->pgno, sqlcipher_sqlite3PagerFilename(p->pBt->pPager, 0) - ); - sqlcipher_sqlite3EndBenignMalloc(); - if( zMsg ){ - sqlcipher_sqlite3ReportError(SQLITE_CORRUPT, lineno, zMsg); +static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ + MemPage *pTrunk = 0; /* Free-list trunk page */ + Pgno iTrunk = 0; /* Page number of free-list trunk page */ + MemPage *pPage1 = pBt->pPage1; /* Local reference to page 1 */ + MemPage *pPage; /* Page being freed. May be NULL. */ + int rc; /* Return Code */ + u32 nFree; /* Initial number of pages on free-list */ + + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + assert( CORRUPT_DB || iPage>1 ); + assert( !pMemPage || pMemPage->pgno==iPage ); + + if( iPage<2 || iPage>pBt->nPage ){ + return SQLITE_CORRUPT_BKPT; + } + if( pMemPage ){ + pPage = pMemPage; + sqlcipher_sqlite3PagerRef(pPage->pDbPage); + }else{ + pPage = btreePageLookup(pBt, iPage); } - sqlcipher_sqlite3_free(zMsg); - return SQLITE_CORRUPT_BKPT; -} -# define SQLITE_CORRUPT_PAGE(pMemPage) corruptPageError(__LINE__, pMemPage) -#else -# define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno) -#endif -#ifndef SQLITE_OMIT_SHARED_CACHE + /* Increment the free page count on pPage1 */ + rc = sqlcipher_sqlite3PagerWrite(pPage1->pDbPage); + if( rc ) goto freepage_out; + nFree = get4byte(&pPage1->aData[36]); + put4byte(&pPage1->aData[36], nFree+1); -#ifdef SQLITE_DEBUG -/* -**** This function is only used as part of an assert() statement. *** -** -** Check to see if pBtree holds the required locks to read or write to the -** table with root page iRoot. Return 1 if it does and 0 if not. -** -** For example, when writing to a table with root-page iRoot via -** Btree connection pBtree: -** -** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) ); -** -** When writing to an index that resides in a sharable database, the -** caller should have first obtained a lock specifying the root page of -** the corresponding table. This makes things a bit more complicated, -** as this module treats each table as a separate structure. To determine -** the table corresponding to the index being written, this -** function has to search through the database schema. -** -** Instead of a lock on the table/index rooted at page iRoot, the caller may -** hold a write-lock on the schema table (root page 1). This is also -** acceptable. -*/ -static int hasSharedCacheTableLock( - Btree *pBtree, /* Handle that must hold lock */ - Pgno iRoot, /* Root page of b-tree */ - int isIndex, /* True if iRoot is the root of an index b-tree */ - int eLockType /* Required lock type (READ_LOCK or WRITE_LOCK) */ -){ - Schema *pSchema = (Schema *)pBtree->pBt->pSchema; - Pgno iTab = 0; - BtLock *pLock; + if( pBt->btsFlags & BTS_SECURE_DELETE ){ + /* If the secure_delete option is enabled, then + ** always fully overwrite deleted information with zeros. + */ + if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0) ) + || ((rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage))!=0) + ){ + goto freepage_out; + } + memset(pPage->aData, 0, pPage->pBt->pageSize); + } - /* If this database is not shareable, or if the client is reading - ** and has the read-uncommitted flag set, then no lock is required. - ** Return true immediately. + /* If the database supports auto-vacuum, write an entry in the pointer-map + ** to indicate that the page is free. */ - if( (pBtree->sharable==0) - || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommit)) - ){ - return 1; + if( ISAUTOVACUUM ){ + ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc); + if( rc ) goto freepage_out; } - /* If the client is reading or writing an index and the schema is - ** not loaded, then it is too difficult to actually check to see if - ** the correct locks are held. So do not bother - just return true. - ** This case does not come up very often anyhow. + /* Now manipulate the actual database free-list structure. There are two + ** possibilities. If the free-list is currently empty, or if the first + ** trunk page in the free-list is full, then this page will become a + ** new free-list trunk page. Otherwise, it will become a leaf of the + ** first trunk page in the current free-list. This block tests if it + ** is possible to add the page as a new free-list leaf. */ - if( isIndex && (!pSchema || (pSchema->schemaFlags&DB_SchemaLoaded)==0) ){ - return 1; - } + if( nFree!=0 ){ + u32 nLeaf; /* Initial number of leaf cells on trunk page */ - /* Figure out the root-page that the lock should be held on. For table - ** b-trees, this is just the root page of the b-tree being read or - ** written. For index b-trees, it is the root page of the associated - ** table. */ - if( isIndex ){ - HashElem *p; - int bSeen = 0; - for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ - Index *pIdx = (Index *)sqliteHashData(p); - if( pIdx->tnum==(int)iRoot ){ - if( bSeen ){ - /* Two or more indexes share the same root page. There must - ** be imposter tables. So just return true. The assert is not - ** useful in that case. */ - return 1; + iTrunk = get4byte(&pPage1->aData[32]); + if( iTrunk>btreePagecount(pBt) ){ + rc = SQLITE_CORRUPT_BKPT; + goto freepage_out; + } + rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); + if( rc!=SQLITE_OK ){ + goto freepage_out; + } + + nLeaf = get4byte(&pTrunk->aData[4]); + assert( pBt->usableSize>32 ); + if( nLeaf > (u32)pBt->usableSize/4 - 2 ){ + rc = SQLITE_CORRUPT_BKPT; + goto freepage_out; + } + if( nLeaf < (u32)pBt->usableSize/4 - 8 ){ + /* In this case there is room on the trunk page to insert the page + ** being freed as a new leaf. + ** + ** Note that the trunk page is not really full until it contains + ** usableSize/4 - 2 entries, not usableSize/4 - 8 entries as we have + ** coded. But due to a coding error in versions of SQLite prior to + ** 3.6.0, databases with freelist trunk pages holding more than + ** usableSize/4 - 8 entries will be reported as corrupt. In order + ** to maintain backwards compatibility with older versions of SQLite, + ** we will continue to restrict the number of entries to usableSize/4 - 8 + ** for now. At some point in the future (once everyone has upgraded + ** to 3.6.0 or later) we should consider fixing the conditional above + ** to read "usableSize/4-2" instead of "usableSize/4-8". + ** + ** EVIDENCE-OF: R-19920-11576 However, newer versions of SQLite still + ** avoid using the last six entries in the freelist trunk page array in + ** order that database files created by newer versions of SQLite can be + ** read by older versions of SQLite. + */ + rc = sqlcipher_sqlite3PagerWrite(pTrunk->pDbPage); + if( rc==SQLITE_OK ){ + put4byte(&pTrunk->aData[4], nLeaf+1); + put4byte(&pTrunk->aData[8+nLeaf*4], iPage); + if( pPage && (pBt->btsFlags & BTS_SECURE_DELETE)==0 ){ + sqlcipher_sqlite3PagerDontWrite(pPage->pDbPage); } - iTab = pIdx->pTable->tnum; - bSeen = 1; + rc = btreeSetHasContent(pBt, iPage); } + TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); + goto freepage_out; } - }else{ - iTab = iRoot; } - /* Search for the required lock. Either a write-lock on root-page iTab, a - ** write-lock on the schema table, or (if the client is reading) a - ** read-lock on iTab will suffice. Return 1 if any of these are found. */ - for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){ - if( pLock->pBtree==pBtree - && (pLock->iTable==iTab || (pLock->eLock==WRITE_LOCK && pLock->iTable==1)) - && pLock->eLock>=eLockType - ){ - return 1; - } + /* If control flows to this point, then it was not possible to add the + ** the page being freed as a leaf page of the first trunk in the free-list. + ** Possibly because the free-list is empty, or possibly because the + ** first trunk in the free-list is full. Either way, the page being freed + ** will become the new first trunk page in the free-list. + */ + if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0)) ){ + goto freepage_out; + } + rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage); + if( rc!=SQLITE_OK ){ + goto freepage_out; } + put4byte(pPage->aData, iTrunk); + put4byte(&pPage->aData[4], 0); + put4byte(&pPage1->aData[32], iPage); + TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", pPage->pgno, iTrunk)); - /* Failed to find the required lock. */ - return 0; +freepage_out: + if( pPage ){ + pPage->isInit = 0; + } + releasePage(pPage); + releasePage(pTrunk); + return rc; +} +static void freePage(MemPage *pPage, int *pRC){ + if( (*pRC)==SQLITE_OK ){ + *pRC = freePage2(pPage->pBt, pPage, pPage->pgno); + } } -#endif /* SQLITE_DEBUG */ -#ifdef SQLITE_DEBUG /* -**** This function may be used as part of assert() statements only. **** -** -** Return true if it would be illegal for pBtree to write into the -** table or index rooted at iRoot because other shared connections are -** simultaneously reading that same table or index. -** -** It is illegal for pBtree to write if some other Btree object that -** shares the same BtShared object is currently reading or writing -** the iRoot table. Except, if the other Btree object has the -** read-uncommitted flag set, then it is OK for the other object to -** have a read cursor. -** -** For example, before writing to any part of the table or index -** rooted at page iRoot, one should call: -** -** assert( !hasReadConflicts(pBtree, iRoot) ); +** Free the overflow pages associated with the given Cell. */ -static int hasReadConflicts(Btree *pBtree, Pgno iRoot){ - BtCursor *p; - for(p=pBtree->pBt->pCursor; p; p=p->pNext){ - if( p->pgnoRoot==iRoot - && p->pBtree!=pBtree - && 0==(p->pBtree->db->flags & SQLITE_ReadUncommit) +static SQLITE_NOINLINE int clearCellOverflow( + MemPage *pPage, /* The page that contains the Cell */ + unsigned char *pCell, /* First byte of the Cell */ + CellInfo *pInfo /* Size information about the cell */ +){ + BtShared *pBt; + Pgno ovflPgno; + int rc; + int nOvfl; + u32 ovflPageSize; + + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pInfo->nLocal!=pInfo->nPayload ); + testcase( pCell + pInfo->nSize == pPage->aDataEnd ); + testcase( pCell + (pInfo->nSize-1) == pPage->aDataEnd ); + if( pCell + pInfo->nSize > pPage->aDataEnd ){ + /* Cell extends past end of page */ + return SQLITE_CORRUPT_PAGE(pPage); + } + ovflPgno = get4byte(pCell + pInfo->nSize - 4); + pBt = pPage->pBt; + assert( pBt->usableSize > 4 ); + ovflPageSize = pBt->usableSize - 4; + nOvfl = (pInfo->nPayload - pInfo->nLocal + ovflPageSize - 1)/ovflPageSize; + assert( nOvfl>0 || + (CORRUPT_DB && (pInfo->nPayload + ovflPageSize)btreePagecount(pBt) ){ + /* 0 is not a legal page number and page 1 cannot be an + ** overflow page. Therefore if ovflPgno<2 or past the end of the + ** file the database must be corrupt. */ + return SQLITE_CORRUPT_BKPT; + } + if( nOvfl ){ + rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext); + if( rc ) return rc; + } + + if( ( pOvfl || ((pOvfl = btreePageLookup(pBt, ovflPgno))!=0) ) + && sqlcipher_sqlite3PagerPageRefcount(pOvfl->pDbPage)!=1 ){ - return 1; + /* There is no reason any cursor should have an outstanding reference + ** to an overflow page belonging to a cell that is being deleted/updated. + ** So if there exists more than one reference to this page, then it + ** must not really be an overflow page and the database must be corrupt. + ** It is helpful to detect this before calling freePage2(), as + ** freePage2() may zero the page contents if secure-delete mode is + ** enabled. If this 'overflow' page happens to be a page that the + ** caller is iterating through or using in some other way, this + ** can be problematic. + */ + rc = SQLITE_CORRUPT_BKPT; + }else{ + rc = freePage2(pBt, pOvfl, ovflPgno); } + + if( pOvfl ){ + sqlcipher_sqlite3PagerUnref(pOvfl->pDbPage); + } + if( rc ) return rc; + ovflPgno = iNext; } - return 0; + return SQLITE_OK; } -#endif /* #ifdef SQLITE_DEBUG */ + +/* Call xParseCell to compute the size of a cell. If the cell contains +** overflow, then invoke cellClearOverflow to clear out that overflow. +** STore the result code (SQLITE_OK or some error code) in rc. +** +** Implemented as macro to force inlining for performance. +*/ +#define BTREE_CLEAR_CELL(rc, pPage, pCell, sInfo) \ + pPage->xParseCell(pPage, pCell, &sInfo); \ + if( sInfo.nLocal!=sInfo.nPayload ){ \ + rc = clearCellOverflow(pPage, pCell, &sInfo); \ + }else{ \ + rc = SQLITE_OK; \ + } + /* -** Query to see if Btree handle p may obtain a lock of type eLock -** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return -** SQLITE_OK if the lock may be obtained (by calling -** setSharedCacheTableLock()), or SQLITE_LOCKED if not. +** Create the byte sequence used to represent a cell on page pPage +** and write that byte sequence into pCell[]. Overflow pages are +** allocated and filled in as necessary. The calling procedure +** is responsible for making sure sufficient space has been allocated +** for pCell[]. +** +** Note that pCell does not necessary need to point to the pPage->aData +** area. pCell might point to some temporary storage. The cell will +** be constructed in this temporary area then copied into pPage->aData +** later. */ -static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){ - BtShared *pBt = p->pBt; - BtLock *pIter; +static int fillInCell( + MemPage *pPage, /* The page that contains the cell */ + unsigned char *pCell, /* Complete text of the cell */ + const BtreePayload *pX, /* Payload with which to construct the cell */ + int *pnSize /* Write cell size here */ +){ + int nPayload; + const u8 *pSrc; + int nSrc, n, rc, mn; + int spaceLeft; + MemPage *pToRelease; + unsigned char *pPrior; + unsigned char *pPayload; + BtShared *pBt; + Pgno pgnoOvfl; + int nHeader; - assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); - assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); - assert( p->db!=0 ); - assert( !(p->db->flags&SQLITE_ReadUncommit)||eLock==WRITE_LOCK||iTab==1 ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - /* If requesting a write-lock, then the Btree must have an open write - ** transaction on this file. And, obviously, for this to be so there - ** must be an open write transaction on the file itself. - */ - assert( eLock==READ_LOCK || (p==pBt->pWriter && p->inTrans==TRANS_WRITE) ); - assert( eLock==READ_LOCK || pBt->inTransaction==TRANS_WRITE ); + /* pPage is not necessarily writeable since pCell might be auxiliary + ** buffer space that is separate from the pPage buffer area */ + assert( pCellaData || pCell>=&pPage->aData[pPage->pBt->pageSize] + || sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); - /* This routine is a no-op if the shared-cache is not enabled */ - if( !p->sharable ){ + /* Fill in the header. */ + nHeader = pPage->childPtrSize; + if( pPage->intKey ){ + nPayload = pX->nData + pX->nZero; + pSrc = pX->pData; + nSrc = pX->nData; + assert( pPage->intKeyLeaf ); /* fillInCell() only called for leaves */ + nHeader += putVarint32(&pCell[nHeader], nPayload); + nHeader += putVarint(&pCell[nHeader], *(u64*)&pX->nKey); + }else{ + assert( pX->nKey<=0x7fffffff && pX->pKey!=0 ); + nSrc = nPayload = (int)pX->nKey; + pSrc = pX->pKey; + nHeader += putVarint32(&pCell[nHeader], nPayload); + } + + /* Fill in the payload */ + pPayload = &pCell[nHeader]; + if( nPayload<=pPage->maxLocal ){ + /* This is the common case where everything fits on the btree page + ** and no overflow pages are required. */ + n = nHeader + nPayload; + testcase( n==3 ); + testcase( n==4 ); + if( n<4 ) n = 4; + *pnSize = n; + assert( nSrc<=nPayload ); + testcase( nSrcpWriter!=p && (pBt->btsFlags & BTS_EXCLUSIVE)!=0 ){ - sqlcipher_sqlite3ConnectionBlocked(p->db, pBt->pWriter->db); - return SQLITE_LOCKED_SHAREDCACHE; + mn = pPage->minLocal; + n = mn + (nPayload - mn) % (pPage->pBt->usableSize - 4); + testcase( n==pPage->maxLocal ); + testcase( n==pPage->maxLocal+1 ); + if( n > pPage->maxLocal ) n = mn; + spaceLeft = n; + *pnSize = n + nHeader + 4; + pPrior = &pCell[nHeader+n]; + pToRelease = 0; + pgnoOvfl = 0; + pBt = pPage->pBt; + + /* At this point variables should be set as follows: + ** + ** nPayload Total payload size in bytes + ** pPayload Begin writing payload here + ** spaceLeft Space available at pPayload. If nPayload>spaceLeft, + ** that means content must spill into overflow pages. + ** *pnSize Size of the local cell (not counting overflow pages) + ** pPrior Where to write the pgno of the first overflow page + ** + ** Use a call to btreeParseCellPtr() to verify that the values above + ** were computed correctly. + */ +#ifdef SQLITE_DEBUG + { + CellInfo info; + pPage->xParseCell(pPage, pCell, &info); + assert( nHeader==(int)(info.pPayload - pCell) ); + assert( info.nKey==pX->nKey ); + assert( *pnSize == info.nSize ); + assert( spaceLeft == info.nLocal ); } +#endif - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - /* The condition (pIter->eLock!=eLock) in the following if(...) - ** statement is a simplification of: - ** - ** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK) - ** - ** since we know that if eLock==WRITE_LOCK, then no other connection - ** may hold a WRITE_LOCK on any table in this file (since there can - ** only be a single writer). - */ - assert( pIter->eLock==READ_LOCK || pIter->eLock==WRITE_LOCK ); - assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); - if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ - sqlcipher_sqlite3ConnectionBlocked(p->db, pIter->pBtree->db); - if( eLock==WRITE_LOCK ){ - assert( p==pBt->pWriter ); - pBt->btsFlags |= BTS_PENDING; + /* Write the payload into the local Cell and any extra into overflow pages */ + while( 1 ){ + n = nPayload; + if( n>spaceLeft ) n = spaceLeft; + + /* If pToRelease is not zero than pPayload points into the data area + ** of pToRelease. Make sure pToRelease is still writeable. */ + assert( pToRelease==0 || sqlcipher_sqlite3PagerIswriteable(pToRelease->pDbPage) ); + + /* If pPayload is part of the data area of pPage, then make sure pPage + ** is still writeable */ + assert( pPayloadaData || pPayload>=&pPage->aData[pBt->pageSize] + || sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); + + if( nSrc>=n ){ + memcpy(pPayload, pSrc, n); + }else if( nSrc>0 ){ + n = nSrc; + memcpy(pPayload, pSrc, n); + }else{ + memset(pPayload, 0, n); + } + nPayload -= n; + if( nPayload<=0 ) break; + pPayload += n; + pSrc += n; + nSrc -= n; + spaceLeft -= n; + if( spaceLeft==0 ){ + MemPage *pOvfl = 0; +#ifndef SQLITE_OMIT_AUTOVACUUM + Pgno pgnoPtrmap = pgnoOvfl; /* Overflow page pointer-map entry page */ + if( pBt->autoVacuum ){ + do{ + pgnoOvfl++; + } while( + PTRMAP_ISPAGE(pBt, pgnoOvfl) || pgnoOvfl==PENDING_BYTE_PAGE(pBt) + ); } - return SQLITE_LOCKED_SHAREDCACHE; +#endif + rc = allocateBtreePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl, 0); +#ifndef SQLITE_OMIT_AUTOVACUUM + /* If the database supports auto-vacuum, and the second or subsequent + ** overflow page is being allocated, add an entry to the pointer-map + ** for that page now. + ** + ** If this is the first overflow page, then write a partial entry + ** to the pointer-map. If we write nothing to this pointer-map slot, + ** then the optimistic overflow chain processing in clearCell() + ** may misinterpret the uninitialized values and delete the + ** wrong pages from the database. + */ + if( pBt->autoVacuum && rc==SQLITE_OK ){ + u8 eType = (pgnoPtrmap?PTRMAP_OVERFLOW2:PTRMAP_OVERFLOW1); + ptrmapPut(pBt, pgnoOvfl, eType, pgnoPtrmap, &rc); + if( rc ){ + releasePage(pOvfl); + } + } +#endif + if( rc ){ + releasePage(pToRelease); + return rc; + } + + /* If pToRelease is not zero than pPrior points into the data area + ** of pToRelease. Make sure pToRelease is still writeable. */ + assert( pToRelease==0 || sqlcipher_sqlite3PagerIswriteable(pToRelease->pDbPage) ); + + /* If pPrior is part of the data area of pPage, then make sure pPage + ** is still writeable */ + assert( pPrioraData || pPrior>=&pPage->aData[pBt->pageSize] + || sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); + + put4byte(pPrior, pgnoOvfl); + releasePage(pToRelease); + pToRelease = pOvfl; + pPrior = pOvfl->aData; + put4byte(pPrior, 0); + pPayload = &pOvfl->aData[4]; + spaceLeft = pBt->usableSize - 4; } } + releasePage(pToRelease); return SQLITE_OK; } -#endif /* !SQLITE_OMIT_SHARED_CACHE */ -#ifndef SQLITE_OMIT_SHARED_CACHE /* -** Add a lock on the table with root-page iTable to the shared-btree used -** by Btree handle p. Parameter eLock must be either READ_LOCK or -** WRITE_LOCK. -** -** This function assumes the following: -** -** (a) The specified Btree object p is connected to a sharable -** database (one with the BtShared.sharable flag set), and -** -** (b) No other Btree objects hold a lock that conflicts -** with the requested lock (i.e. querySharedCacheTableLock() has -** already been called and returned SQLITE_OK). +** Remove the i-th cell from pPage. This routine effects pPage only. +** The cell content is not freed or deallocated. It is assumed that +** the cell content has been copied someplace else. This routine just +** removes the reference to the cell from pPage. ** -** SQLITE_OK is returned if the lock is added successfully. SQLITE_NOMEM -** is returned if a malloc attempt fails. +** "sz" must be the number of bytes in the cell. */ -static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ - BtShared *pBt = p->pBt; - BtLock *pLock = 0; - BtLock *pIter; - - assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); - assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); - assert( p->db!=0 ); - - /* A connection with the read-uncommitted flag set will never try to - ** obtain a read-lock using this function. The only read-lock obtained - ** by a connection in read-uncommitted mode is on the sqlite_schema - ** table, and that lock is obtained in BtreeBeginTrans(). */ - assert( 0==(p->db->flags&SQLITE_ReadUncommit) || eLock==WRITE_LOCK ); - - /* This function should only be called on a sharable b-tree after it - ** has been determined that no other b-tree holds a conflicting lock. */ - assert( p->sharable ); - assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) ); +static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ + u32 pc; /* Offset to cell content of cell being deleted */ + u8 *data; /* pPage->aData */ + u8 *ptr; /* Used to move bytes around within data[] */ + int rc; /* The return code */ + int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ - /* First search the list for an existing lock on this table. */ - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - if( pIter->iTable==iTable && pIter->pBtree==p ){ - pLock = pIter; - break; - } + if( *pRC ) return; + assert( idx>=0 ); + assert( idxnCell ); + assert( CORRUPT_DB || sz==cellSize(pPage, idx) ); + assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->nFree>=0 ); + data = pPage->aData; + ptr = &pPage->aCellIdx[2*idx]; + assert( pPage->pBt->usableSize > (u32)(ptr-data) ); + pc = get2byte(ptr); + hdr = pPage->hdrOffset; +#if 0 /* Not required. Omit for efficiency */ + if( pcnCell*2 ){ + *pRC = SQLITE_CORRUPT_BKPT; + return; } - - /* If the above search did not find a BtLock struct associating Btree p - ** with table iTable, allocate one and link it into the list. - */ - if( !pLock ){ - pLock = (BtLock *)sqlcipher_sqlite3MallocZero(sizeof(BtLock)); - if( !pLock ){ - return SQLITE_NOMEM_BKPT; - } - pLock->iTable = iTable; - pLock->pBtree = p; - pLock->pNext = pBt->pLock; - pBt->pLock = pLock; +#endif + testcase( pc==(u32)get2byte(&data[hdr+5]) ); + testcase( pc+sz==pPage->pBt->usableSize ); + if( pc+sz > pPage->pBt->usableSize ){ + *pRC = SQLITE_CORRUPT_BKPT; + return; } - - /* Set the BtLock.eLock variable to the maximum of the current lock - ** and the requested lock. This means if a write-lock was already held - ** and a read-lock requested, we don't incorrectly downgrade the lock. - */ - assert( WRITE_LOCK>READ_LOCK ); - if( eLock>pLock->eLock ){ - pLock->eLock = eLock; + rc = freeSpace(pPage, pc, sz); + if( rc ){ + *pRC = rc; + return; + } + pPage->nCell--; + if( pPage->nCell==0 ){ + memset(&data[hdr+1], 0, 4); + data[hdr+7] = 0; + put2byte(&data[hdr+5], pPage->pBt->usableSize); + pPage->nFree = pPage->pBt->usableSize - pPage->hdrOffset + - pPage->childPtrSize - 8; + }else{ + memmove(ptr, ptr+2, 2*(pPage->nCell - idx)); + put2byte(&data[hdr+3], pPage->nCell); + pPage->nFree += 2; } - - return SQLITE_OK; } -#endif /* !SQLITE_OMIT_SHARED_CACHE */ -#ifndef SQLITE_OMIT_SHARED_CACHE /* -** Release all the table locks (locks obtained via calls to -** the setSharedCacheTableLock() procedure) held by Btree object p. +** Insert a new cell on pPage at cell index "i". pCell points to the +** content of the cell. ** -** This function assumes that Btree p has an open read or write -** transaction. If it does not, then the BTS_PENDING flag -** may be incorrectly cleared. +** If the cell content will fit on the page, then put it there. If it +** will not fit, then make a copy of the cell content into pTemp if +** pTemp is not null. Regardless of pTemp, allocate a new entry +** in pPage->apOvfl[] and make it point to the cell content (either +** in pTemp or the original pCell) and also record its index. +** Allocating a new entry in pPage->aCell[] implies that +** pPage->nOverflow is incremented. +** +** *pRC must be SQLITE_OK when this routine is called. */ -static void clearAllSharedCacheTableLocks(Btree *p){ - BtShared *pBt = p->pBt; - BtLock **ppIter = &pBt->pLock; - - assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); - assert( p->sharable || 0==*ppIter ); - assert( p->inTrans>0 ); +static void insertCell( + MemPage *pPage, /* Page into which we are copying */ + int i, /* New cell becomes the i-th cell of the page */ + u8 *pCell, /* Content of the new cell */ + int sz, /* Bytes of content in pCell */ + u8 *pTemp, /* Temp storage space for pCell, if needed */ + Pgno iChild, /* If non-zero, replace first 4 bytes with this value */ + int *pRC /* Read and write return code from here */ +){ + int idx = 0; /* Where to write new cell content in data[] */ + int j; /* Loop counter */ + u8 *data; /* The content of the whole page */ + u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */ - while( *ppIter ){ - BtLock *pLock = *ppIter; - assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); - assert( pLock->pBtree->inTrans>=pLock->eLock ); - if( pLock->pBtree==p ){ - *ppIter = pLock->pNext; - assert( pLock->iTable!=1 || pLock==&p->lock ); - if( pLock->iTable!=1 ){ - sqlcipher_sqlite3_free(pLock); - } - }else{ - ppIter = &pLock->pNext; + assert( *pRC==SQLITE_OK ); + assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); + assert( MX_CELL(pPage->pBt)<=10921 ); + assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB ); + assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) ); + assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) ); + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB ); + assert( pPage->nFree>=0 ); + if( pPage->nOverflow || sz+2>pPage->nFree ){ + if( pTemp ){ + memcpy(pTemp, pCell, sz); + pCell = pTemp; } - } + if( iChild ){ + put4byte(pCell, iChild); + } + j = pPage->nOverflow++; + /* Comparison against ArraySize-1 since we hold back one extra slot + ** as a contingency. In other words, never need more than 3 overflow + ** slots but 4 are allocated, just to be safe. */ + assert( j < ArraySize(pPage->apOvfl)-1 ); + pPage->apOvfl[j] = pCell; + pPage->aiOvfl[j] = (u16)i; - assert( (pBt->btsFlags & BTS_PENDING)==0 || pBt->pWriter ); - if( pBt->pWriter==p ){ - pBt->pWriter = 0; - pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); - }else if( pBt->nTransaction==2 ){ - /* This function is called when Btree p is concluding its - ** transaction. If there currently exists a writer, and p is not - ** that writer, then the number of locks held by connections other - ** than the writer must be about to drop to zero. In this case - ** set the BTS_PENDING flag to 0. - ** - ** If there is not currently a writer, then BTS_PENDING must - ** be zero already. So this next line is harmless in that case. + /* When multiple overflows occur, they are always sequential and in + ** sorted order. This invariants arise because multiple overflows can + ** only occur when inserting divider cells into the parent page during + ** balancing, and the dividers are adjacent and sorted. */ - pBt->btsFlags &= ~BTS_PENDING; + assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */ + assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */ + }else{ + int rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage); + if( rc!=SQLITE_OK ){ + *pRC = rc; + return; + } + assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); + data = pPage->aData; + assert( &data[pPage->cellOffset]==pPage->aCellIdx ); + rc = allocateSpace(pPage, sz, &idx); + if( rc ){ *pRC = rc; return; } + /* The allocateSpace() routine guarantees the following properties + ** if it returns successfully */ + assert( idx >= 0 ); + assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB ); + assert( idx+sz <= (int)pPage->pBt->usableSize ); + pPage->nFree -= (u16)(2 + sz); + if( iChild ){ + /* In a corrupt database where an entry in the cell index section of + ** a btree page has a value of 3 or less, the pCell value might point + ** as many as 4 bytes in front of the start of the aData buffer for + ** the source page. Make sure this does not cause problems by not + ** reading the first 4 bytes */ + memcpy(&data[idx+4], pCell+4, sz-4); + put4byte(&data[idx], iChild); + }else{ + memcpy(&data[idx], pCell, sz); + } + pIns = pPage->aCellIdx + i*2; + memmove(pIns+2, pIns, 2*(pPage->nCell - i)); + put2byte(pIns, idx); + pPage->nCell++; + /* increment the cell count */ + if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++; + assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB ); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pPage->pBt->autoVacuum ){ + /* The cell may contain a pointer to an overflow page. If so, write + ** the entry for the overflow page into the pointer map. + */ + ptrmapPutOvflPtr(pPage, pPage, pCell, pRC); + } +#endif } } /* -** This function changes all write-locks held by Btree p into read-locks. +** The following parameters determine how many adjacent pages get involved +** in a balancing operation. NN is the number of neighbors on either side +** of the page that participate in the balancing operation. NB is the +** total number of pages that participate, including the target page and +** NN neighbors on either side. +** +** The minimum value of NN is 1 (of course). Increasing NN above 1 +** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance +** in exchange for a larger degradation in INSERT and UPDATE performance. +** The value of NN appears to give the best results overall. +** +** (Later:) The description above makes it seem as if these values are +** tunable - as if you could change them and recompile and it would all work. +** But that is unlikely. NB has been 3 since the inception of SQLite and +** we have never tested any other value. */ -static void downgradeAllSharedCacheTableLocks(Btree *p){ - BtShared *pBt = p->pBt; - if( pBt->pWriter==p ){ - BtLock *pLock; - pBt->pWriter = 0; - pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING); - for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ - assert( pLock->eLock==READ_LOCK || pLock->pBtree==p ); - pLock->eLock = READ_LOCK; - } - } -} - -#endif /* SQLITE_OMIT_SHARED_CACHE */ - -static void releasePage(MemPage *pPage); /* Forward reference */ -static void releasePageOne(MemPage *pPage); /* Forward reference */ -static void releasePageNotNull(MemPage *pPage); /* Forward reference */ +#define NN 1 /* Number of neighbors on either side of pPage */ +#define NB 3 /* (NN*2+1): Total pages involved in the balance */ /* -***** This routine is used inside of assert() only **** +** A CellArray object contains a cache of pointers and sizes for a +** consecutive sequence of cells that might be held on multiple pages. ** -** Verify that the cursor holds the mutex on its BtShared +** The cells in this array are the divider cell or cells from the pParent +** page plus up to three child pages. There are a total of nCell cells. +** +** pRef is a pointer to one of the pages that contributes cells. This is +** used to access information such as MemPage.intKey and MemPage.pBt->pageSize +** which should be common to all pages that contribute cells to this array. +** +** apCell[] and szCell[] hold, respectively, pointers to the start of each +** cell and the size of each cell. Some of the apCell[] pointers might refer +** to overflow cells. In other words, some apCel[] pointers might not point +** to content area of the pages. +** +** A szCell[] of zero means the size of that cell has not yet been computed. +** +** The cells come from as many as four different pages: +** +** ----------- +** | Parent | +** ----------- +** / | \ +** / | \ +** --------- --------- --------- +** |Child-1| |Child-2| |Child-3| +** --------- --------- --------- +** +** The order of cells is in the array is for an index btree is: +** +** 1. All cells from Child-1 in order +** 2. The first divider cell from Parent +** 3. All cells from Child-2 in order +** 4. The second divider cell from Parent +** 5. All cells from Child-3 in order +** +** For a table-btree (with rowids) the items 2 and 4 are empty because +** content exists only in leaves and there are no divider cells. +** +** For an index btree, the apEnd[] array holds pointer to the end of page +** for Child-1, the Parent, Child-2, the Parent (again), and Child-3, +** respectively. The ixNx[] array holds the number of cells contained in +** each of these 5 stages, and all stages to the left. Hence: +** +** ixNx[0] = Number of cells in Child-1. +** ixNx[1] = Number of cells in Child-1 plus 1 for first divider. +** ixNx[2] = Number of cells in Child-1 and Child-2 + 1 for 1st divider. +** ixNx[3] = Number of cells in Child-1 and Child-2 + both divider cells +** ixNx[4] = Total number of cells. +** +** For a table-btree, the concept is similar, except only apEnd[0]..apEnd[2] +** are used and they point to the leaf pages only, and the ixNx value are: +** +** ixNx[0] = Number of cells in Child-1. +** ixNx[1] = Number of cells in Child-1 and Child-2. +** ixNx[2] = Total number of cells. +** +** Sometimes when deleting, a child page can have zero cells. In those +** cases, ixNx[] entries with higher indexes, and the corresponding apEnd[] +** entries, shift down. The end result is that each ixNx[] entry should +** be larger than the previous */ -#ifdef SQLITE_DEBUG -static int cursorHoldsMutex(BtCursor *p){ - return sqlcipher_sqlite3_mutex_held(p->pBt->mutex); -} +typedef struct CellArray CellArray; +struct CellArray { + int nCell; /* Number of cells in apCell[] */ + MemPage *pRef; /* Reference page */ + u8 **apCell; /* All cells begin balanced */ + u16 *szCell; /* Local size of all cells in apCell[] */ + u8 *apEnd[NB*2]; /* MemPage.aDataEnd values */ + int ixNx[NB*2]; /* Index of at which we move to the next apEnd[] */ +}; -/* Verify that the cursor and the BtShared agree about what is the current -** database connetion. This is important in shared-cache mode. If the database -** connection pointers get out-of-sync, it is possible for routines like -** btreeInitPage() to reference an stale connection pointer that references a -** a connection that has already closed. This routine is used inside assert() -** statements only and for the purpose of double-checking that the btree code -** does keep the database connection pointers up-to-date. +/* +** Make sure the cell sizes at idx, idx+1, ..., idx+N-1 have been +** computed. */ -static int cursorOwnsBtShared(BtCursor *p){ - assert( cursorHoldsMutex(p) ); - return (p->pBtree->db==p->pBt->db); +static void populateCellCache(CellArray *p, int idx, int N){ + assert( idx>=0 && idx+N<=p->nCell ); + while( N>0 ){ + assert( p->apCell[idx]!=0 ); + if( p->szCell[idx]==0 ){ + p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]); + }else{ + assert( CORRUPT_DB || + p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) ); + } + idx++; + N--; + } } -#endif /* -** Invalidate the overflow cache of the cursor passed as the first argument. -** on the shared btree structure pBt. +** Return the size of the Nth element of the cell array */ -#define invalidateOverflowCache(pCur) (pCur->curFlags &= ~BTCF_ValidOvfl) +static SQLITE_NOINLINE u16 computeCellSize(CellArray *p, int N){ + assert( N>=0 && NnCell ); + assert( p->szCell[N]==0 ); + p->szCell[N] = p->pRef->xCellSize(p->pRef, p->apCell[N]); + return p->szCell[N]; +} +static u16 cachedCellSize(CellArray *p, int N){ + assert( N>=0 && NnCell ); + if( p->szCell[N] ) return p->szCell[N]; + return computeCellSize(p, N); +} /* -** Invalidate the overflow page-list cache for all cursors opened -** on the shared btree structure pBt. +** Array apCell[] contains pointers to nCell b-tree page cells. The +** szCell[] array contains the size in bytes of each cell. This function +** replaces the current contents of page pPg with the contents of the cell +** array. +** +** Some of the cells in apCell[] may currently be stored in pPg. This +** function works around problems caused by this by making a copy of any +** such cells before overwriting the page data. +** +** The MemPage.nFree field is invalidated by this function. It is the +** responsibility of the caller to set it correctly. */ -static void invalidateAllOverflowCache(BtShared *pBt){ - BtCursor *p; - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - for(p=pBt->pCursor; p; p=p->pNext){ - invalidateOverflowCache(p); +static int rebuildPage( + CellArray *pCArray, /* Content to be added to page pPg */ + int iFirst, /* First cell in pCArray to use */ + int nCell, /* Final number of cells on page */ + MemPage *pPg /* The page to be reconstructed */ +){ + const int hdr = pPg->hdrOffset; /* Offset of header on pPg */ + u8 * const aData = pPg->aData; /* Pointer to data for pPg */ + const int usableSize = pPg->pBt->usableSize; + u8 * const pEnd = &aData[usableSize]; + int i = iFirst; /* Which cell to copy from pCArray*/ + u32 j; /* Start of cell content area */ + int iEnd = i+nCell; /* Loop terminator */ + u8 *pCellptr = pPg->aCellIdx; + u8 *pTmp = sqlcipher_sqlite3PagerTempSpace(pPg->pBt->pPager); + u8 *pData; + int k; /* Current slot in pCArray->apEnd[] */ + u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */ + + assert( i(u32)usableSize ){ j = 0; } + memcpy(&pTmp[j], &aData[j], usableSize - j); + + for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kapEnd[k]; + + pData = pEnd; + while( 1/*exit by break*/ ){ + u8 *pCell = pCArray->apCell[i]; + u16 sz = pCArray->szCell[i]; + assert( sz>0 ); + if( SQLITE_WITHIN(pCell,aData+j,pEnd) ){ + if( ((uptr)(pCell+sz))>(uptr)pEnd ) return SQLITE_CORRUPT_BKPT; + pCell = &pTmp[pCell - aData]; + }else if( (uptr)(pCell+sz)>(uptr)pSrcEnd + && (uptr)(pCell)<(uptr)pSrcEnd + ){ + return SQLITE_CORRUPT_BKPT; + } + + pData -= sz; + put2byte(pCellptr, (pData - aData)); + pCellptr += 2; + if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT; + memmove(pData, pCell, sz); + assert( sz==pPg->xCellSize(pPg, pCell) || CORRUPT_DB ); + i++; + if( i>=iEnd ) break; + if( pCArray->ixNx[k]<=i ){ + k++; + pSrcEnd = pCArray->apEnd[k]; + } } + + /* The pPg->nFree field is now set incorrectly. The caller will fix it. */ + pPg->nCell = nCell; + pPg->nOverflow = 0; + + put2byte(&aData[hdr+1], 0); + put2byte(&aData[hdr+3], pPg->nCell); + put2byte(&aData[hdr+5], pData - aData); + aData[hdr+7] = 0x00; + return SQLITE_OK; } -#ifndef SQLITE_OMIT_INCRBLOB /* -** This function is called before modifying the contents of a table -** to invalidate any incrblob cursors that are open on the -** row or one of the rows being modified. +** The pCArray objects contains pointers to b-tree cells and the cell sizes. +** This function attempts to add the cells stored in the array to page pPg. +** If it cannot (because the page needs to be defragmented before the cells +** will fit), non-zero is returned. Otherwise, if the cells are added +** successfully, zero is returned. ** -** If argument isClearTable is true, then the entire contents of the -** table is about to be deleted. In this case invalidate all incrblob -** cursors open on any row within the table with root-page pgnoRoot. +** Argument pCellptr points to the first entry in the cell-pointer array +** (part of page pPg) to populate. After cell apCell[0] is written to the +** page body, a 16-bit offset is written to pCellptr. And so on, for each +** cell in the array. It is the responsibility of the caller to ensure +** that it is safe to overwrite this part of the cell-pointer array. ** -** Otherwise, if argument isClearTable is false, then the row with -** rowid iRow is being replaced or deleted. In this case invalidate -** only those incrblob cursors open on that specific row. +** When this function is called, *ppData points to the start of the +** content area on page pPg. If the size of the content area is extended, +** *ppData is updated to point to the new start of the content area +** before returning. +** +** Finally, argument pBegin points to the byte immediately following the +** end of the space required by this page for the cell-pointer area (for +** all cells - not just those inserted by the current call). If the content +** area must be extended to before this point in order to accomodate all +** cells in apCell[], then the cells do not fit and non-zero is returned. */ -static void invalidateIncrblobCursors( - Btree *pBtree, /* The database file to check */ - Pgno pgnoRoot, /* The table that might be changing */ - i64 iRow, /* The rowid that might be changing */ - int isClearTable /* True if all rows are being deleted */ +static int pageInsertArray( + MemPage *pPg, /* Page to add cells to */ + u8 *pBegin, /* End of cell-pointer array */ + u8 **ppData, /* IN/OUT: Page content-area pointer */ + u8 *pCellptr, /* Pointer to cell-pointer area */ + int iFirst, /* Index of first cell to add */ + int nCell, /* Number of cells to add to pPg */ + CellArray *pCArray /* Array of cells */ ){ - BtCursor *p; - if( pBtree->hasIncrblobCur==0 ) return; - assert( sqlcipher_sqlite3BtreeHoldsMutex(pBtree) ); - pBtree->hasIncrblobCur = 0; - for(p=pBtree->pBt->pCursor; p; p=p->pNext){ - if( (p->curFlags & BTCF_Incrblob)!=0 ){ - pBtree->hasIncrblobCur = 1; - if( p->pgnoRoot==pgnoRoot && (isClearTable || p->info.nKey==iRow) ){ - p->eState = CURSOR_INVALID; - } + int i = iFirst; /* Loop counter - cell index to insert */ + u8 *aData = pPg->aData; /* Complete page */ + u8 *pData = *ppData; /* Content area. A subset of aData[] */ + int iEnd = iFirst + nCell; /* End of loop. One past last cell to ins */ + int k; /* Current slot in pCArray->apEnd[] */ + u8 *pEnd; /* Maximum extent of cell data */ + assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */ + if( iEnd<=iFirst ) return 0; + for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kapEnd[k]; + while( 1 /*Exit by break*/ ){ + int sz, rc; + u8 *pSlot; + assert( pCArray->szCell[i]!=0 ); + sz = pCArray->szCell[i]; + if( (aData[1]==0 && aData[2]==0) || (pSlot = pageFindSlot(pPg,sz,&rc))==0 ){ + if( (pData - pBegin)apCell[i] will never overlap on a well-formed + ** database. But they might for a corrupt database. Hence use memmove() + ** since memcpy() sends SIGABORT with overlapping buffers on OpenBSD */ + assert( (pSlot+sz)<=pCArray->apCell[i] + || pSlot>=(pCArray->apCell[i]+sz) + || CORRUPT_DB ); + if( (uptr)(pCArray->apCell[i]+sz)>(uptr)pEnd + && (uptr)(pCArray->apCell[i])<(uptr)pEnd + ){ + assert( CORRUPT_DB ); + (void)SQLITE_CORRUPT_BKPT; + return 1; + } + memmove(pSlot, pCArray->apCell[i], sz); + put2byte(pCellptr, (pSlot - aData)); + pCellptr += 2; + i++; + if( i>=iEnd ) break; + if( pCArray->ixNx[k]<=i ){ + k++; + pEnd = pCArray->apEnd[k]; } } -} - -#else - /* Stub function when INCRBLOB is omitted */ - #define invalidateIncrblobCursors(w,x,y,z) -#endif /* SQLITE_OMIT_INCRBLOB */ + *ppData = pData; + return 0; +} /* -** Set bit pgno of the BtShared.pHasContent bitvec. This is called -** when a page that previously contained data becomes a free-list leaf -** page. -** -** The BtShared.pHasContent bitvec exists to work around an obscure -** bug caused by the interaction of two useful IO optimizations surrounding -** free-list leaf pages: -** -** 1) When all data is deleted from a page and the page becomes -** a free-list leaf page, the page is not written to the database -** (as free-list leaf pages contain no meaningful data). Sometimes -** such a page is not even journalled (as it will not be modified, -** why bother journalling it?). -** -** 2) When a free-list leaf page is reused, its content is not read -** from the database or written to the journal file (why should it -** be, if it is not at all meaningful?). +** The pCArray object contains pointers to b-tree cells and their sizes. ** -** By themselves, these optimizations work fine and provide a handy -** performance boost to bulk delete or insert operations. However, if -** a page is moved to the free-list and then reused within the same -** transaction, a problem comes up. If the page is not journalled when -** it is moved to the free-list and it is also not journalled when it -** is extracted from the free-list and reused, then the original data -** may be lost. In the event of a rollback, it may not be possible -** to restore the database to its original configuration. +** This function adds the space associated with each cell in the array +** that is currently stored within the body of pPg to the pPg free-list. +** The cell-pointers and other fields of the page are not updated. ** -** The solution is the BtShared.pHasContent bitvec. Whenever a page is -** moved to become a free-list leaf page, the corresponding bit is -** set in the bitvec. Whenever a leaf page is extracted from the free-list, -** optimization 2 above is omitted if the corresponding bit is already -** set in BtShared.pHasContent. The contents of the bitvec are cleared -** at the end of every transaction. +** This function returns the total number of cells added to the free-list. */ -static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ - int rc = SQLITE_OK; - if( !pBt->pHasContent ){ - assert( pgno<=pBt->nPage ); - pBt->pHasContent = sqlcipher_sqlite3BitvecCreate(pBt->nPage); - if( !pBt->pHasContent ){ - rc = SQLITE_NOMEM_BKPT; +static int pageFreeArray( + MemPage *pPg, /* Page to edit */ + int iFirst, /* First cell to delete */ + int nCell, /* Cells to delete */ + CellArray *pCArray /* Array of cells */ +){ + u8 * const aData = pPg->aData; + u8 * const pEnd = &aData[pPg->pBt->usableSize]; + u8 * const pStart = &aData[pPg->hdrOffset + 8 + pPg->childPtrSize]; + int nRet = 0; + int i; + int iEnd = iFirst + nCell; + u8 *pFree = 0; + int szFree = 0; + + for(i=iFirst; iapCell[i]; + if( SQLITE_WITHIN(pCell, pStart, pEnd) ){ + int sz; + /* No need to use cachedCellSize() here. The sizes of all cells that + ** are to be freed have already been computing while deciding which + ** cells need freeing */ + sz = pCArray->szCell[i]; assert( sz>0 ); + if( pFree!=(pCell + sz) ){ + if( pFree ){ + assert( pFree>aData && (pFree - aData)<65536 ); + freeSpace(pPg, (u16)(pFree - aData), szFree); + } + pFree = pCell; + szFree = sz; + if( pFree+sz>pEnd ){ + return 0; + } + }else{ + pFree = pCell; + szFree += sz; + } + nRet++; } } - if( rc==SQLITE_OK && pgno<=sqlcipher_sqlite3BitvecSize(pBt->pHasContent) ){ - rc = sqlcipher_sqlite3BitvecSet(pBt->pHasContent, pgno); + if( pFree ){ + assert( pFree>aData && (pFree - aData)<65536 ); + freeSpace(pPg, (u16)(pFree - aData), szFree); } - return rc; + return nRet; } /* -** Query the BtShared.pHasContent vector. +** pCArray contains pointers to and sizes of all cells in the page being +** balanced. The current page, pPg, has pPg->nCell cells starting with +** pCArray->apCell[iOld]. After balancing, this page should hold nNew cells +** starting at apCell[iNew]. ** -** This function is called when a free-list leaf page is removed from the -** free-list for reuse. It returns false if it is safe to retrieve the -** page from the pager layer with the 'no-content' flag set. True otherwise. +** This routine makes the necessary adjustments to pPg so that it contains +** the correct cells after being balanced. +** +** The pPg->nFree field is invalid when this function returns. It is the +** responsibility of the caller to set it correctly. */ -static int btreeGetHasContent(BtShared *pBt, Pgno pgno){ - Bitvec *p = pBt->pHasContent; - return p && (pgno>sqlcipher_sqlite3BitvecSize(p) || sqlcipher_sqlite3BitvecTestNotNull(p, pgno)); -} +static int editPage( + MemPage *pPg, /* Edit this page */ + int iOld, /* Index of first cell currently on page */ + int iNew, /* Index of new first cell on page */ + int nNew, /* Final number of cells on page */ + CellArray *pCArray /* Array of cells and sizes */ +){ + u8 * const aData = pPg->aData; + const int hdr = pPg->hdrOffset; + u8 *pBegin = &pPg->aCellIdx[nNew * 2]; + int nCell = pPg->nCell; /* Cells stored on pPg */ + u8 *pData; + u8 *pCellptr; + int i; + int iOldEnd = iOld + pPg->nCell + pPg->nOverflow; + int iNewEnd = iNew + nNew; -/* -** Clear (destroy) the BtShared.pHasContent bitvec. This should be -** invoked at the conclusion of each write-transaction. -*/ -static void btreeClearHasContent(BtShared *pBt){ - sqlcipher_sqlite3BitvecDestroy(pBt->pHasContent); - pBt->pHasContent = 0; -} +#ifdef SQLITE_DEBUG + u8 *pTmp = sqlcipher_sqlite3PagerTempSpace(pPg->pBt->pPager); + memcpy(pTmp, aData, pPg->pBt->usableSize); +#endif -/* -** Release all of the apPage[] pages for a cursor. -*/ -static void btreeReleaseAllCursorPages(BtCursor *pCur){ - int i; - if( pCur->iPage>=0 ){ - for(i=0; iiPage; i++){ - releasePageNotNull(pCur->apPage[i]); - } - releasePageNotNull(pCur->pPage); - pCur->iPage = -1; + /* Remove cells from the start and end of the page */ + assert( nCell>=0 ); + if( iOldnCell) ) return SQLITE_CORRUPT_BKPT; + memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2); + nCell -= nShift; + } + if( iNewEnd < iOldEnd ){ + int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); + assert( nCell>=nTail ); + nCell -= nTail; } -} -/* -** The cursor passed as the only argument must point to a valid entry -** when this function is called (i.e. have eState==CURSOR_VALID). This -** function saves the current cursor key in variables pCur->nKey and -** pCur->pKey. SQLITE_OK is returned if successful or an SQLite error -** code otherwise. -** -** If the cursor is open on an intkey table, then the integer key -** (the rowid) is stored in pCur->nKey and pCur->pKey is left set to -** NULL. If the cursor is open on a non-intkey table, then pCur->pKey is -** set to point to a malloced buffer pCur->nKey bytes in size containing -** the key. -*/ -static int saveCursorKey(BtCursor *pCur){ - int rc = SQLITE_OK; - assert( CURSOR_VALID==pCur->eState ); - assert( 0==pCur->pKey ); - assert( cursorHoldsMutex(pCur) ); + pData = &aData[get2byteNotZero(&aData[hdr+5])]; + if( pDatapPg->aDataEnd ) goto editpage_fail; - if( pCur->curIntKey ){ - /* Only the rowid is required for a table btree */ - pCur->nKey = sqlcipher_sqlite3BtreeIntegerKey(pCur); - }else{ - /* For an index btree, save the complete key content. It is possible - ** that the current key is corrupt. In that case, it is possible that - ** the sqlcipher_sqlite3VdbeRecordUnpack() function may overread the buffer by - ** up to the size of 1 varint plus 1 8-byte value when the cursor - ** position is restored. Hence the 17 bytes of padding allocated - ** below. */ - void *pKey; - pCur->nKey = sqlcipher_sqlite3BtreePayloadSize(pCur); - pKey = sqlcipher_sqlite3Malloc( pCur->nKey + 9 + 8 ); - if( pKey ){ - rc = sqlcipher_sqlite3BtreePayload(pCur, 0, (int)pCur->nKey, pKey); - if( rc==SQLITE_OK ){ - memset(((u8*)pKey)+pCur->nKey, 0, 9+8); - pCur->pKey = pKey; - }else{ - sqlcipher_sqlite3_free(pKey); + /* Add cells to the start of the page */ + if( iNew=0 ); + pCellptr = pPg->aCellIdx; + memmove(&pCellptr[nAdd*2], pCellptr, nCell*2); + if( pageInsertArray( + pPg, pBegin, &pData, pCellptr, + iNew, nAdd, pCArray + ) ) goto editpage_fail; + nCell += nAdd; + } + + /* Add any overflow cells */ + for(i=0; inOverflow; i++){ + int iCell = (iOld + pPg->aiOvfl[i]) - iNew; + if( iCell>=0 && iCellaCellIdx[iCell * 2]; + if( nCell>iCell ){ + memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2); } - }else{ - rc = SQLITE_NOMEM_BKPT; + nCell++; + cachedCellSize(pCArray, iCell+iNew); + if( pageInsertArray( + pPg, pBegin, &pData, pCellptr, + iCell+iNew, 1, pCArray + ) ) goto editpage_fail; } } - assert( !pCur->curIntKey || !pCur->pKey ); - return rc; -} -/* -** Save the current cursor position in the variables BtCursor.nKey -** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. -** -** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID) -** prior to calling this routine. -*/ -static int saveCursorPosition(BtCursor *pCur){ - int rc; + /* Append cells to the end of the page */ + assert( nCell>=0 ); + pCellptr = &pPg->aCellIdx[nCell*2]; + if( pageInsertArray( + pPg, pBegin, &pData, pCellptr, + iNew+nCell, nNew-nCell, pCArray + ) ) goto editpage_fail; - assert( CURSOR_VALID==pCur->eState || CURSOR_SKIPNEXT==pCur->eState ); - assert( 0==pCur->pKey ); - assert( cursorHoldsMutex(pCur) ); + pPg->nCell = nNew; + pPg->nOverflow = 0; - if( pCur->curFlags & BTCF_Pinned ){ - return SQLITE_CONSTRAINT_PINNED; - } - if( pCur->eState==CURSOR_SKIPNEXT ){ - pCur->eState = CURSOR_VALID; - }else{ - pCur->skipNext = 0; - } + put2byte(&aData[hdr+3], pPg->nCell); + put2byte(&aData[hdr+5], pData - aData); - rc = saveCursorKey(pCur); - if( rc==SQLITE_OK ){ - btreeReleaseAllCursorPages(pCur); - pCur->eState = CURSOR_REQUIRESEEK; +#ifdef SQLITE_DEBUG + for(i=0; iapCell[i+iNew]; + int iOff = get2byteAligned(&pPg->aCellIdx[i*2]); + if( SQLITE_WITHIN(pCell, aData, &aData[pPg->pBt->usableSize]) ){ + pCell = &pTmp[pCell - aData]; + } + assert( 0==memcmp(pCell, &aData[iOff], + pCArray->pRef->xCellSize(pCArray->pRef, pCArray->apCell[i+iNew])) ); } +#endif - pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl|BTCF_AtLast); - return rc; + return SQLITE_OK; + editpage_fail: + /* Unable to edit this page. Rebuild it from scratch instead. */ + populateCellCache(pCArray, iNew, nNew); + return rebuildPage(pCArray, iNew, nNew, pPg); } -/* Forward reference */ -static int SQLITE_NOINLINE saveCursorsOnList(BtCursor*,Pgno,BtCursor*); +#ifndef SQLITE_OMIT_QUICKBALANCE /* -** Save the positions of all cursors (except pExcept) that are open on -** the table with root-page iRoot. "Saving the cursor position" means that -** the location in the btree is remembered in such a way that it can be -** moved back to the same spot after the btree has been modified. This -** routine is called just before cursor pExcept is used to modify the -** table, for example in BtreeDelete() or BtreeInsert(). +** This version of balance() handles the common special case where +** a new entry is being inserted on the extreme right-end of the +** tree, in other words, when the new entry will become the largest +** entry in the tree. ** -** If there are two or more cursors on the same btree, then all such -** cursors should have their BTCF_Multiple flag set. The btreeCursor() -** routine enforces that rule. This routine only needs to be called in -** the uncommon case when pExpect has the BTCF_Multiple flag set. +** Instead of trying to balance the 3 right-most leaf pages, just add +** a new page to the right-hand side and put the one new entry in +** that page. This leaves the right side of the tree somewhat +** unbalanced. But odds are that we will be inserting new entries +** at the end soon afterwards so the nearly empty page will quickly +** fill up. On average. ** -** If pExpect!=NULL and if no other cursors are found on the same root-page, -** then the BTCF_Multiple flag on pExpect is cleared, to avoid another -** pointless call to this routine. +** pPage is the leaf page which is the right-most page in the tree. +** pParent is its parent. pPage must have a single overflow entry +** which is also the right-most entry on the page. ** -** Implementation note: This routine merely checks to see if any cursors -** need to be saved. It calls out to saveCursorsOnList() in the (unusual) -** event that cursors are in need to being saved. +** The pSpace buffer is used to store a temporary copy of the divider +** cell that will be inserted into pParent. Such a cell consists of a 4 +** byte page number followed by a variable length integer. In other +** words, at most 13 bytes. Hence the pSpace buffer must be at +** least 13 bytes in size. */ -static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ - BtCursor *p; - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - assert( pExcept==0 || pExcept->pBt==pBt ); - for(p=pBt->pCursor; p; p=p->pNext){ - if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ) break; - } - if( p ) return saveCursorsOnList(p, iRoot, pExcept); - if( pExcept ) pExcept->curFlags &= ~BTCF_Multiple; - return SQLITE_OK; -} +static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ + BtShared *const pBt = pPage->pBt; /* B-Tree Database */ + MemPage *pNew; /* Newly allocated page */ + int rc; /* Return Code */ + Pgno pgnoNew; /* Page number of pNew */ -/* This helper routine to saveAllCursors does the actual work of saving -** the cursors if and when a cursor is found that actually requires saving. -** The common case is that no cursors need to be saved, so this routine is -** broken out from its caller to avoid unnecessary stack pointer movement. -*/ -static int SQLITE_NOINLINE saveCursorsOnList( - BtCursor *p, /* The first cursor that needs saving */ - Pgno iRoot, /* Only save cursor with this iRoot. Save all if zero */ - BtCursor *pExcept /* Do not save this cursor */ -){ - do{ - if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ){ - if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){ - int rc = saveCursorPosition(p); - if( SQLITE_OK!=rc ){ - return rc; - } - }else{ - testcase( p->iPage>=0 ); - btreeReleaseAllCursorPages(p); - } + assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( sqlcipher_sqlite3PagerIswriteable(pParent->pDbPage) ); + assert( pPage->nOverflow==1 ); + + if( pPage->nCell==0 ) return SQLITE_CORRUPT_BKPT; /* dbfuzz001.test */ + assert( pPage->nFree>=0 ); + assert( pParent->nFree>=0 ); + + /* Allocate a new page. This page will become the right-sibling of + ** pPage. Make the parent page writable, so that the new divider cell + ** may be inserted. If both these operations are successful, proceed. + */ + rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); + + if( rc==SQLITE_OK ){ + + u8 *pOut = &pSpace[4]; + u8 *pCell = pPage->apOvfl[0]; + u16 szCell = pPage->xCellSize(pPage, pCell); + u8 *pStop; + CellArray b; + + assert( sqlcipher_sqlite3PagerIswriteable(pNew->pDbPage) ); + assert( CORRUPT_DB || pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) ); + zeroPage(pNew, PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF); + b.nCell = 1; + b.pRef = pPage; + b.apCell = &pCell; + b.szCell = &szCell; + b.apEnd[0] = pPage->aDataEnd; + b.ixNx[0] = 2; + rc = rebuildPage(&b, 0, 1, pNew); + if( NEVER(rc) ){ + releasePage(pNew); + return rc; } - p = p->pNext; - }while( p ); - return SQLITE_OK; -} + pNew->nFree = pBt->usableSize - pNew->cellOffset - 2 - szCell; -/* -** Clear the current cursor position. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeClearCursor(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - sqlcipher_sqlite3_free(pCur->pKey); - pCur->pKey = 0; - pCur->eState = CURSOR_INVALID; -} + /* If this is an auto-vacuum database, update the pointer map + ** with entries for the new page, and any pointer from the + ** cell on the page to an overflow page. If either of these + ** operations fails, the return code is set, but the contents + ** of the parent page are still manipulated by thh code below. + ** That is Ok, at this point the parent page is guaranteed to + ** be marked as dirty. Returning an error code will cause a + ** rollback, undoing any changes made to the parent page. + */ + if( ISAUTOVACUUM ){ + ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc); + if( szCell>pNew->minLocal ){ + ptrmapPutOvflPtr(pNew, pNew, pCell, &rc); + } + } -/* -** In this version of BtreeMoveto, pKey is a packed index record -** such as is generated by the OP_MakeRecord opcode. Unpack the -** record and then call BtreeMovetoUnpacked() to do the work. -*/ -static int btreeMoveto( - BtCursor *pCur, /* Cursor open on the btree to be searched */ - const void *pKey, /* Packed key if the btree is an index */ - i64 nKey, /* Integer key for tables. Size of pKey for indices */ - int bias, /* Bias search to the high end */ - int *pRes /* Write search results here */ -){ - int rc; /* Status code */ - UnpackedRecord *pIdxKey; /* Unpacked index key */ + /* Create a divider cell to insert into pParent. The divider cell + ** consists of a 4-byte page number (the page number of pPage) and + ** a variable length key value (which must be the same value as the + ** largest key on pPage). + ** + ** To find the largest key value on pPage, first find the right-most + ** cell on pPage. The first two fields of this cell are the + ** record-length (a variable length integer at most 32-bits in size) + ** and the key value (a variable length integer, may have any value). + ** The first of the while(...) loops below skips over the record-length + ** field. The second while(...) loop copies the key value from the + ** cell on pPage into the pSpace buffer. + */ + pCell = findCell(pPage, pPage->nCell-1); + pStop = &pCell[9]; + while( (*(pCell++)&0x80) && pCellpKeyInfo; - assert( nKey==(i64)(int)nKey ); - pIdxKey = sqlcipher_sqlite3VdbeAllocUnpackedRecord(pKeyInfo); - if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT; - sqlcipher_sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey); - if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){ - rc = SQLITE_CORRUPT_BKPT; - goto moveto_done; + /* Insert the new divider cell into pParent. */ + if( rc==SQLITE_OK ){ + insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), + 0, pPage->pgno, &rc); } - }else{ - pIdxKey = 0; - } - rc = sqlcipher_sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); -moveto_done: - if( pIdxKey ){ - sqlcipher_sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey); + + /* Set the right-child pointer of pParent to point to the new page. */ + put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew); + + /* Release the reference to the new page. */ + releasePage(pNew); } + return rc; } +#endif /* SQLITE_OMIT_QUICKBALANCE */ +#if 0 /* -** Restore the cursor to the position it was in (or as close to as possible) -** when saveCursorPosition() was called. Note that this call deletes the -** saved position info stored by saveCursorPosition(), so there can be -** at most one effective restoreCursorPosition() call after each -** saveCursorPosition(). +** This function does not contribute anything to the operation of SQLite. +** it is sometimes activated temporarily while debugging code responsible +** for setting pointer-map entries. */ -static int btreeRestoreCursorPosition(BtCursor *pCur){ - int rc; - int skipNext = 0; - assert( cursorOwnsBtShared(pCur) ); - assert( pCur->eState>=CURSOR_REQUIRESEEK ); - if( pCur->eState==CURSOR_FAULT ){ - return pCur->skipNext; - } - pCur->eState = CURSOR_INVALID; - if( sqlcipher_sqlite3FaultSim(410) ){ - rc = SQLITE_IOERR; - }else{ - rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext); - } - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3_free(pCur->pKey); - pCur->pKey = 0; - assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); - if( skipNext ) pCur->skipNext = skipNext; - if( pCur->skipNext && pCur->eState==CURSOR_VALID ){ - pCur->eState = CURSOR_SKIPNEXT; +static int ptrmapCheckPages(MemPage **apPage, int nPage){ + int i, j; + for(i=0; ipBt; + assert( pPage->isInit ); + + for(j=0; jnCell; j++){ + CellInfo info; + u8 *z; + + z = findCell(pPage, j); + pPage->xParseCell(pPage, z, &info); + if( info.nLocalpgno && e==PTRMAP_OVERFLOW1 ); + } + if( !pPage->leaf ){ + Pgno child = get4byte(z); + ptrmapGet(pBt, child, &e, &n); + assert( n==pPage->pgno && e==PTRMAP_BTREE ); + } + } + if( !pPage->leaf ){ + Pgno child = get4byte(&pPage->aData[pPage->hdrOffset+8]); + ptrmapGet(pBt, child, &e, &n); + assert( n==pPage->pgno && e==PTRMAP_BTREE ); } } - return rc; + return 1; } - -#define restoreCursorPosition(p) \ - (p->eState>=CURSOR_REQUIRESEEK ? \ - btreeRestoreCursorPosition(p) : \ - SQLITE_OK) +#endif /* -** Determine whether or not a cursor has moved from the position where -** it was last placed, or has been invalidated for any other reason. -** Cursors can move when the row they are pointing at is deleted out -** from under them, for example. Cursor might also move if a btree -** is rebalanced. +** This function is used to copy the contents of the b-tree node stored +** on page pFrom to page pTo. If page pFrom was not a leaf page, then +** the pointer-map entries for each child page are updated so that the +** parent page stored in the pointer map is page pTo. If pFrom contained +** any cells with overflow page pointers, then the corresponding pointer +** map entries are also updated so that the parent page is page pTo. ** -** Calling this routine with a NULL cursor pointer returns false. +** If pFrom is currently carrying any overflow cells (entries in the +** MemPage.apOvfl[] array), they are not copied to pTo. ** -** Use the separate sqlcipher_sqlite3BtreeCursorRestore() routine to restore a cursor -** back to where it ought to be if this routine returns true. +** Before returning, page pTo is reinitialized using btreeInitPage(). +** +** The performance of this function is not critical. It is only used by +** the balance_shallower() and balance_deeper() procedures, neither of +** which are called often under normal circumstances. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorHasMoved(BtCursor *pCur){ - assert( EIGHT_BYTE_ALIGNMENT(pCur) - || pCur==sqlcipher_sqlite3BtreeFakeValidCursor() ); - assert( offsetof(BtCursor, eState)==0 ); - assert( sizeof(pCur->eState)==1 ); - return CURSOR_VALID != *(u8*)pCur; -} +static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ + if( (*pRC)==SQLITE_OK ){ + BtShared * const pBt = pFrom->pBt; + u8 * const aFrom = pFrom->aData; + u8 * const aTo = pTo->aData; + int const iFromHdr = pFrom->hdrOffset; + int const iToHdr = ((pTo->pgno==1) ? 100 : 0); + int rc; + int iData; -/* -** Return a pointer to a fake BtCursor object that will always answer -** false to the sqlcipher_sqlite3BtreeCursorHasMoved() routine above. The fake -** cursor returned must not be used with any other Btree interface. -*/ -SQLITE_PRIVATE BtCursor *sqlcipher_sqlite3BtreeFakeValidCursor(void){ - static u8 fakeCursor = CURSOR_VALID; - assert( offsetof(BtCursor, eState)==0 ); - return (BtCursor*)&fakeCursor; + + assert( pFrom->isInit ); + assert( pFrom->nFree>=iToHdr ); + assert( get2byte(&aFrom[iFromHdr+5]) <= (int)pBt->usableSize ); + + /* Copy the b-tree node content from page pFrom to page pTo. */ + iData = get2byte(&aFrom[iFromHdr+5]); + memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData); + memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell); + + /* Reinitialize page pTo so that the contents of the MemPage structure + ** match the new data. The initialization of pTo can actually fail under + ** fairly obscure circumstances, even though it is a copy of initialized + ** page pFrom. + */ + pTo->isInit = 0; + rc = btreeInitPage(pTo); + if( rc==SQLITE_OK ) rc = btreeComputeFreeSpace(pTo); + if( rc!=SQLITE_OK ){ + *pRC = rc; + return; + } + + /* If this is an auto-vacuum database, update the pointer-map entries + ** for any b-tree or overflow pages that pTo now contains the pointers to. + */ + if( ISAUTOVACUUM ){ + *pRC = setChildPtrmaps(pTo); + } + } } /* -** This routine restores a cursor back to its original position after it -** has been moved by some outside activity (such as a btree rebalance or -** a row having been deleted out from under the cursor). +** This routine redistributes cells on the iParentIdx'th child of pParent +** (hereafter "the page") and up to 2 siblings so that all pages have about the +** same amount of free space. Usually a single sibling on either side of the +** page are used in the balancing, though both siblings might come from one +** side if the page is the first or last child of its parent. If the page +** has fewer than 2 siblings (something which can only happen if the page +** is a root page or a child of a root page) then all available siblings +** participate in the balancing. ** -** On success, the *pDifferentRow parameter is false if the cursor is left -** pointing at exactly the same row. *pDifferntRow is the row the cursor -** was pointing to has been deleted, forcing the cursor to point to some -** nearby row. +** The number of siblings of the page might be increased or decreased by +** one or two in an effort to keep pages nearly full but not over full. ** -** This routine should only be called for a cursor that just returned -** TRUE from sqlcipher_sqlite3BtreeCursorHasMoved(). +** Note that when this routine is called, some of the cells on the page +** might not actually be stored in MemPage.aData[]. This can happen +** if the page is overfull. This routine ensures that all cells allocated +** to the page and its siblings fit into MemPage.aData[] before returning. +** +** In the course of balancing the page and its siblings, cells may be +** inserted into or removed from the parent page (pParent). Doing so +** may cause the parent page to become overfull or underfull. If this +** happens, it is the responsibility of the caller to invoke the correct +** balancing routine to fix this problem (see the balance() routine). +** +** If this routine fails for any reason, it might leave the database +** in a corrupted state. So if this routine fails, the database should +** be rolled back. +** +** The third argument to this function, aOvflSpace, is a pointer to a +** buffer big enough to hold one page. If while inserting cells into the parent +** page (pParent) the parent page becomes overfull, this buffer is +** used to store the parent's overflow cells. Because this function inserts +** a maximum of four divider cells into the parent page, and the maximum +** size of a cell stored within an internal node is always less than 1/4 +** of the page-size, the aOvflSpace[] buffer is guaranteed to be large +** enough for all overflow cells. +** +** If aOvflSpace is set to a null pointer, this function returns +** SQLITE_NOMEM. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow){ - int rc; +static int balance_nonroot( + MemPage *pParent, /* Parent page of siblings being balanced */ + int iParentIdx, /* Index of "the page" in pParent */ + u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */ + int isRoot, /* True if pParent is a root-page */ + int bBulk /* True if this call is part of a bulk load */ +){ + BtShared *pBt; /* The whole database */ + int nMaxCells = 0; /* Allocated size of apCell, szCell, aFrom. */ + int nNew = 0; /* Number of pages in apNew[] */ + int nOld; /* Number of pages in apOld[] */ + int i, j, k; /* Loop counters */ + int nxDiv; /* Next divider slot in pParent->aCell[] */ + int rc = SQLITE_OK; /* The return code */ + u16 leafCorrection; /* 4 if pPage is a leaf. 0 if not */ + int leafData; /* True if pPage is a leaf of a LEAFDATA tree */ + int usableSpace; /* Bytes in pPage beyond the header */ + int pageFlags; /* Value of pPage->aData[0] */ + int iSpace1 = 0; /* First unused byte of aSpace1[] */ + int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */ + int szScratch; /* Size of scratch memory requested */ + MemPage *apOld[NB]; /* pPage and up to two siblings */ + MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */ + u8 *pRight; /* Location in parent of right-sibling pointer */ + u8 *apDiv[NB-1]; /* Divider cells in pParent */ + int cntNew[NB+2]; /* Index in b.paCell[] of cell after i-th page */ + int cntOld[NB+2]; /* Old index in b.apCell[] */ + int szNew[NB+2]; /* Combined size of cells placed on i-th page */ + u8 *aSpace1; /* Space for copies of dividers cells */ + Pgno pgno; /* Temp var to store a page number in */ + u8 abDone[NB+2]; /* True after i'th new page is populated */ + Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */ + CellArray b; /* Parsed information on cells being balanced */ - assert( pCur!=0 ); - assert( pCur->eState!=CURSOR_VALID ); - rc = restoreCursorPosition(pCur); - if( rc ){ - *pDifferentRow = 1; - return rc; + memset(abDone, 0, sizeof(abDone)); + memset(&b, 0, sizeof(b)); + pBt = pParent->pBt; + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + assert( sqlcipher_sqlite3PagerIswriteable(pParent->pDbPage) ); + + /* At this point pParent may have at most one overflow cell. And if + ** this overflow cell is present, it must be the cell with + ** index iParentIdx. This scenario comes about when this function + ** is called (indirectly) from sqlcipher_sqlite3BtreeDelete(). + */ + assert( pParent->nOverflow==0 || pParent->nOverflow==1 ); + assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx ); + + if( !aOvflSpace ){ + return SQLITE_NOMEM_BKPT; } - if( pCur->eState!=CURSOR_VALID ){ - *pDifferentRow = 1; + assert( pParent->nFree>=0 ); + + /* Find the sibling pages to balance. Also locate the cells in pParent + ** that divide the siblings. An attempt is made to find NN siblings on + ** either side of pPage. More siblings are taken from one side, however, + ** if there are fewer than NN siblings on the other side. If pParent + ** has NB or fewer children then all children of pParent are taken. + ** + ** This loop also drops the divider cells from the parent page. This + ** way, the remainder of the function does not have to deal with any + ** overflow cells in the parent page, since if any existed they will + ** have already been removed. + */ + i = pParent->nOverflow + pParent->nCell; + if( i<2 ){ + nxDiv = 0; }else{ - *pDifferentRow = 0; + assert( bBulk==0 || bBulk==1 ); + if( iParentIdx==0 ){ + nxDiv = 0; + }else if( iParentIdx==i ){ + nxDiv = i-2+bBulk; + }else{ + nxDiv = iParentIdx-1; + } + i = 2-bBulk; } - return SQLITE_OK; -} + nOld = i+1; + if( (i+nxDiv-pParent->nOverflow)==pParent->nCell ){ + pRight = &pParent->aData[pParent->hdrOffset+8]; + }else{ + pRight = findCell(pParent, i+nxDiv-pParent->nOverflow); + } + pgno = get4byte(pRight); + while( 1 ){ + if( rc==SQLITE_OK ){ + rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0); + } + if( rc ){ + memset(apOld, 0, (i+1)*sizeof(MemPage*)); + goto balance_cleanup; + } + if( apOld[i]->nFree<0 ){ + rc = btreeComputeFreeSpace(apOld[i]); + if( rc ){ + memset(apOld, 0, (i)*sizeof(MemPage*)); + goto balance_cleanup; + } + } + nMaxCells += apOld[i]->nCell + ArraySize(pParent->apOvfl); + if( (i--)==0 ) break; -#ifdef SQLITE_ENABLE_CURSOR_HINTS -/* -** Provide hints to the cursor. The particular hint given (and the type -** and number of the varargs parameters) is determined by the eHintType -** parameter. See the definitions of the BTREE_HINT_* macros for details. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){ - /* Used only by system that substitute their own storage engine */ -} -#endif + if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){ + apDiv[i] = pParent->apOvfl[0]; + pgno = get4byte(apDiv[i]); + szNew[i] = pParent->xCellSize(pParent, apDiv[i]); + pParent->nOverflow = 0; + }else{ + apDiv[i] = findCell(pParent, i+nxDiv-pParent->nOverflow); + pgno = get4byte(apDiv[i]); + szNew[i] = pParent->xCellSize(pParent, apDiv[i]); -/* -** Provide flag hints to the cursor. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){ - assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 ); - pCur->hints = x; -} + /* Drop the cell from the parent page. apDiv[i] still points to + ** the cell within the parent, even though it has been dropped. + ** This is safe because dropping a cell only overwrites the first + ** four bytes of it, and this function does not need the first + ** four bytes of the divider cell. So the pointer is safe to use + ** later on. + ** + ** But not if we are in secure-delete mode. In secure-delete mode, + ** the dropCell() routine will overwrite the entire cell with zeroes. + ** In this case, temporarily copy the cell into the aOvflSpace[] + ** buffer. It will be copied out again as soon as the aSpace[] buffer + ** is allocated. */ + if( pBt->btsFlags & BTS_FAST_SECURE ){ + int iOff; + /* If the following if() condition is not true, the db is corrupted. + ** The call to dropCell() below will detect this. */ + iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); + if( (iOff+szNew[i])<=(int)pBt->usableSize ){ + memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]); + apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; + } + } + dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc); + } + } -#ifndef SQLITE_OMIT_AUTOVACUUM -/* -** Given a page number of a regular database page, return the page -** number for the pointer-map page that contains the entry for the -** input page number. -** -** Return 0 (not a valid page) for pgno==1 since there is -** no pointer map associated with page 1. The integrity_check logic -** requires that ptrmapPageno(*,1)!=1. -*/ -static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){ - int nPagesPerMapPage; - Pgno iPtrMap, ret; - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - if( pgno<2 ) return 0; - nPagesPerMapPage = (pBt->usableSize/5)+1; - iPtrMap = (pgno-2)/nPagesPerMapPage; - ret = (iPtrMap*nPagesPerMapPage) + 2; - if( ret==PENDING_BYTE_PAGE(pBt) ){ - ret++; + /* Make nMaxCells a multiple of 4 in order to preserve 8-byte + ** alignment */ + nMaxCells = (nMaxCells + 3)&~3; + + /* + ** Allocate space for memory structures + */ + szScratch = + nMaxCells*sizeof(u8*) /* b.apCell */ + + nMaxCells*sizeof(u16) /* b.szCell */ + + pBt->pageSize; /* aSpace1 */ + + assert( szScratch<=7*(int)pBt->pageSize ); + b.apCell = sqlcipher_sqlite3StackAllocRaw(0, szScratch ); + if( b.apCell==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto balance_cleanup; } - return ret; -} + b.szCell = (u16*)&b.apCell[nMaxCells]; + aSpace1 = (u8*)&b.szCell[nMaxCells]; + assert( EIGHT_BYTE_ALIGNMENT(aSpace1) ); -/* -** Write an entry into the pointer map. -** -** This routine updates the pointer map entry for page number 'key' -** so that it maps to type 'eType' and parent page number 'pgno'. -** -** If *pRC is initially non-zero (non-SQLITE_OK) then this routine is -** a no-op. If an error occurs, the appropriate error code is written -** into *pRC. -*/ -static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ - DbPage *pDbPage; /* The pointer map page */ - u8 *pPtrmap; /* The pointer map data */ - Pgno iPtrmap; /* The pointer map page number */ - int offset; /* Offset in pointer map page */ - int rc; /* Return code from subfunctions */ + /* + ** Load pointers to all cells on sibling pages and the divider cells + ** into the local b.apCell[] array. Make copies of the divider cells + ** into space obtained from aSpace1[]. The divider cells have already + ** been removed from pParent. + ** + ** If the siblings are on leaf pages, then the child pointers of the + ** divider cells are stripped from the cells before they are copied + ** into aSpace1[]. In this way, all cells in b.apCell[] are without + ** child pointers. If siblings are not leaves, then all cell in + ** b.apCell[] include child pointers. Either way, all cells in b.apCell[] + ** are alike. + ** + ** leafCorrection: 4 if pPage is a leaf. 0 if pPage is not a leaf. + ** leafData: 1 if pPage holds key+data and pParent holds only keys. + */ + b.pRef = apOld[0]; + leafCorrection = b.pRef->leaf*4; + leafData = b.pRef->intKeyLeaf; + for(i=0; inCell; + u8 *aData = pOld->aData; + u16 maskPage = pOld->maskPage; + u8 *piCell = aData + pOld->cellOffset; + u8 *piEnd; + VVA_ONLY( int nCellAtStart = b.nCell; ) - if( *pRC ) return; + /* Verify that all sibling pages are of the same "type" (table-leaf, + ** table-interior, index-leaf, or index-interior). + */ + if( pOld->aData[0]!=apOld[0]->aData[0] ){ + rc = SQLITE_CORRUPT_BKPT; + goto balance_cleanup; + } - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - /* The super-journal page number must never be used as a pointer map page */ - assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); + /* Load b.apCell[] with pointers to all cells in pOld. If pOld + ** contains overflow cells, include them in the b.apCell[] array + ** in the correct spot. + ** + ** Note that when there are multiple overflow cells, it is always the + ** case that they are sequential and adjacent. This invariant arises + ** because multiple overflows can only occurs when inserting divider + ** cells into a parent on a prior balance, and divider cells are always + ** adjacent and are inserted in order. There is an assert() tagged + ** with "NOTE 1" in the overflow cell insertion loop to prove this + ** invariant. + ** + ** This must be done in advance. Once the balance starts, the cell + ** offset section of the btree page will be overwritten and we will no + ** long be able to find the cells if a pointer to each cell is not saved + ** first. + */ + memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow)); + if( pOld->nOverflow>0 ){ + if( NEVER(limitaiOvfl[0]) ){ + rc = SQLITE_CORRUPT_BKPT; + goto balance_cleanup; + } + limit = pOld->aiOvfl[0]; + for(j=0; jnOverflow; k++){ + assert( k==0 || pOld->aiOvfl[k-1]+1==pOld->aiOvfl[k] );/* NOTE 1 */ + b.apCell[b.nCell] = pOld->apOvfl[k]; + b.nCell++; + } + } + piEnd = aData + pOld->cellOffset + 2*pOld->nCell; + while( piCellnCell+pOld->nOverflow) ); - assert( pBt->autoVacuum ); - if( key==0 ){ - *pRC = SQLITE_CORRUPT_BKPT; - return; - } - iPtrmap = PTRMAP_PAGENO(pBt, key); - rc = sqlcipher_sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage, 0); - if( rc!=SQLITE_OK ){ - *pRC = rc; - return; + cntOld[i] = b.nCell; + if( imaxLocal+23 ); + assert( iSpace1 <= (int)pBt->pageSize ); + memcpy(pTemp, apDiv[i], sz); + b.apCell[b.nCell] = pTemp+leafCorrection; + assert( leafCorrection==0 || leafCorrection==4 ); + b.szCell[b.nCell] = b.szCell[b.nCell] - leafCorrection; + if( !pOld->leaf ){ + assert( leafCorrection==0 ); + assert( pOld->hdrOffset==0 || CORRUPT_DB ); + /* The right pointer of the child page pOld becomes the left + ** pointer of the divider cell */ + memcpy(b.apCell[b.nCell], &pOld->aData[8], 4); + }else{ + assert( leafCorrection==4 ); + while( b.szCell[b.nCell]<4 ){ + /* Do not allow any cells smaller than 4 bytes. If a smaller cell + ** does exist, pad it with 0x00 bytes. */ + assert( b.szCell[b.nCell]==3 || CORRUPT_DB ); + assert( b.apCell[b.nCell]==&aSpace1[iSpace1-3] || CORRUPT_DB ); + aSpace1[iSpace1++] = 0x00; + b.szCell[b.nCell]++; + } + } + b.nCell++; + } } - if( ((char*)sqlcipher_sqlite3PagerGetExtra(pDbPage))[0]!=0 ){ - /* The first byte of the extra data is the MemPage.isInit byte. - ** If that byte is set, it means this page is also being used - ** as a btree page. */ - *pRC = SQLITE_CORRUPT_BKPT; - goto ptrmap_exit; + + /* + ** Figure out the number of pages needed to hold all b.nCell cells. + ** Store this number in "k". Also compute szNew[] which is the total + ** size of all cells on the i-th page and cntNew[] which is the index + ** in b.apCell[] of the cell that divides page i from page i+1. + ** cntNew[k] should equal b.nCell. + ** + ** Values computed by this block: + ** + ** k: The total number of sibling pages + ** szNew[i]: Spaced used on the i-th sibling page. + ** cntNew[i]: Index in b.apCell[] and b.szCell[] for the first cell to + ** the right of the i-th sibling page. + ** usableSpace: Number of bytes of space available on each sibling. + ** + */ + usableSpace = pBt->usableSize - 12 + leafCorrection; + for(i=k=0; iaDataEnd; + b.ixNx[k] = cntOld[i]; + if( k && b.ixNx[k]==b.ixNx[k-1] ){ + k--; /* Omit b.ixNx[] entry for child pages with no cells */ + } + if( !leafData ){ + k++; + b.apEnd[k] = pParent->aDataEnd; + b.ixNx[k] = cntOld[i]+1; + } + assert( p->nFree>=0 ); + szNew[i] = usableSpace - p->nFree; + for(j=0; jnOverflow; j++){ + szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]); + } + cntNew[i] = cntOld[i]; } - offset = PTRMAP_PTROFFSET(iPtrmap, key); - if( offset<0 ){ - *pRC = SQLITE_CORRUPT_BKPT; - goto ptrmap_exit; + k = nOld; + for(i=0; iusableSpace ){ + if( i+1>=k ){ + k = i+2; + if( k>NB+2 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; } + szNew[k-1] = 0; + cntNew[k-1] = b.nCell; + } + sz = 2 + cachedCellSize(&b, cntNew[i]-1); + szNew[i] -= sz; + if( !leafData ){ + if( cntNew[i]usableSpace ) break; + szNew[i] += sz; + cntNew[i]++; + if( !leafData ){ + if( cntNew[i]=b.nCell ){ + k = i+1; + }else if( cntNew[i] <= (i>0 ? cntNew[i-1] : 0) ){ + rc = SQLITE_CORRUPT_BKPT; + goto balance_cleanup; + } } - assert( offset <= (int)pBt->usableSize-5 ); - pPtrmap = (u8 *)sqlcipher_sqlite3PagerGetData(pDbPage); - if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ - TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); - *pRC= rc = sqlcipher_sqlite3PagerWrite(pDbPage); - if( rc==SQLITE_OK ){ - pPtrmap[offset] = eType; - put4byte(&pPtrmap[offset+1], parent); + /* + ** The packing computed by the previous block is biased toward the siblings + ** on the left side (siblings with smaller keys). The left siblings are + ** always nearly full, while the right-most sibling might be nearly empty. + ** The next block of code attempts to adjust the packing of siblings to + ** get a better balance. + ** + ** This adjustment is more than an optimization. The packing above might + ** be so out of balance as to be illegal. For example, the right-most + ** sibling might be completely empty. This adjustment is not optional. + */ + for(i=k-1; i>0; i--){ + int szRight = szNew[i]; /* Size of sibling on the right */ + int szLeft = szNew[i-1]; /* Size of sibling on the left */ + int r; /* Index of right-most cell in left sibling */ + int d; /* Index of first cell to the left of right sibling */ + + r = cntNew[i-1] - 1; + d = r + 1 - leafData; + (void)cachedCellSize(&b, d); + do{ + assert( d szLeft-(b.szCell[r]+(i==k-1?0:2)))){ + break; + } + szRight += b.szCell[d] + 2; + szLeft -= b.szCell[r] + 2; + cntNew[i-1] = r; + r--; + d--; + }while( r>=0 ); + szNew[i] = szRight; + szNew[i-1] = szLeft; + if( cntNew[i-1] <= (i>1 ? cntNew[i-2] : 0) ){ + rc = SQLITE_CORRUPT_BKPT; + goto balance_cleanup; } } -ptrmap_exit: - sqlcipher_sqlite3PagerUnref(pDbPage); -} - -/* -** Read an entry from the pointer map. -** -** This routine retrieves the pointer map entry for page 'key', writing -** the type and parent page number to *pEType and *pPgno respectively. -** An error code is returned if something goes wrong, otherwise SQLITE_OK. -*/ -static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ - DbPage *pDbPage; /* The pointer map page */ - int iPtrmap; /* Pointer map page index */ - u8 *pPtrmap; /* Pointer map page data */ - int offset; /* Offset of entry in pointer map */ - int rc; + /* Sanity check: For a non-corrupt database file one of the follwing + ** must be true: + ** (1) We found one or more cells (cntNew[0])>0), or + ** (2) pPage is a virtual root page. A virtual root page is when + ** the real root page is page 1 and we are the only child of + ** that page. + */ + assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) || CORRUPT_DB); + TRACE(("BALANCE: old: %d(nc=%d) %d(nc=%d) %d(nc=%d)\n", + apOld[0]->pgno, apOld[0]->nCell, + nOld>=2 ? apOld[1]->pgno : 0, nOld>=2 ? apOld[1]->nCell : 0, + nOld>=3 ? apOld[2]->pgno : 0, nOld>=3 ? apOld[2]->nCell : 0 + )); - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + /* + ** Allocate k new pages. Reuse old pages where possible. + */ + pageFlags = apOld[0]->aData[0]; + for(i=0; ipDbPage); + nNew++; + if( sqlcipher_sqlite3PagerPageRefcount(pNew->pDbPage)!=1+(i==(iParentIdx-nxDiv)) + && rc==SQLITE_OK + ){ + rc = SQLITE_CORRUPT_BKPT; + } + if( rc ) goto balance_cleanup; + }else{ + assert( i>0 ); + rc = allocateBtreePage(pBt, &pNew, &pgno, (bBulk ? 1 : pgno), 0); + if( rc ) goto balance_cleanup; + zeroPage(pNew, pageFlags); + apNew[i] = pNew; + nNew++; + cntOld[i] = b.nCell; - iPtrmap = PTRMAP_PAGENO(pBt, key); - rc = sqlcipher_sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage, 0); - if( rc!=0 ){ - return rc; + /* Set the pointer-map entry for the new sibling page. */ + if( ISAUTOVACUUM ){ + ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc); + if( rc!=SQLITE_OK ){ + goto balance_cleanup; + } + } + } } - pPtrmap = (u8 *)sqlcipher_sqlite3PagerGetData(pDbPage); - offset = PTRMAP_PTROFFSET(iPtrmap, key); - if( offset<0 ){ - sqlcipher_sqlite3PagerUnref(pDbPage); - return SQLITE_CORRUPT_BKPT; + /* + ** Reassign page numbers so that the new pages are in ascending order. + ** This helps to keep entries in the disk file in order so that a scan + ** of the table is closer to a linear scan through the file. That in turn + ** helps the operating system to deliver pages from the disk more rapidly. + ** + ** An O(N*N) sort algorithm is used, but since N is never more than NB+2 + ** (5), that is not a performance concern. + ** + ** When NB==3, this one optimization makes the database about 25% faster + ** for large insertions and deletions. + */ + for(i=0; ipgno; + assert( apNew[i]->pDbPage->flags & PGHDR_WRITEABLE ); + assert( apNew[i]->pDbPage->flags & PGHDR_DIRTY ); } - assert( offset <= (int)pBt->usableSize-5 ); - assert( pEType!=0 ); - *pEType = pPtrmap[offset]; - if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]); + for(i=0; ipgno < apNew[iB]->pgno ) iB = j; + } - sqlcipher_sqlite3PagerUnref(pDbPage); - if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_PGNO(iPtrmap); - return SQLITE_OK; -} + /* If apNew[i] has a page number that is bigger than any of the + ** subsequence apNew[i] entries, then swap apNew[i] with the subsequent + ** entry that has the smallest page number (which we know to be + ** entry apNew[iB]). + */ + if( iB!=i ){ + Pgno pgnoA = apNew[i]->pgno; + Pgno pgnoB = apNew[iB]->pgno; + Pgno pgnoTemp = (PENDING_BYTE/pBt->pageSize)+1; + u16 fgA = apNew[i]->pDbPage->flags; + u16 fgB = apNew[iB]->pDbPage->flags; + sqlcipher_sqlite3PagerRekey(apNew[i]->pDbPage, pgnoTemp, fgB); + sqlcipher_sqlite3PagerRekey(apNew[iB]->pDbPage, pgnoA, fgA); + sqlcipher_sqlite3PagerRekey(apNew[i]->pDbPage, pgnoB, fgB); + apNew[i]->pgno = pgnoB; + apNew[iB]->pgno = pgnoA; + } + } -#else /* if defined SQLITE_OMIT_AUTOVACUUM */ - #define ptrmapPut(w,x,y,z,rc) - #define ptrmapGet(w,x,y,z) SQLITE_OK - #define ptrmapPutOvflPtr(x, y, z, rc) -#endif + TRACE(("BALANCE: new: %d(%d nc=%d) %d(%d nc=%d) %d(%d nc=%d) " + "%d(%d nc=%d) %d(%d nc=%d)\n", + apNew[0]->pgno, szNew[0], cntNew[0], + nNew>=2 ? apNew[1]->pgno : 0, nNew>=2 ? szNew[1] : 0, + nNew>=2 ? cntNew[1] - cntNew[0] - !leafData : 0, + nNew>=3 ? apNew[2]->pgno : 0, nNew>=3 ? szNew[2] : 0, + nNew>=3 ? cntNew[2] - cntNew[1] - !leafData : 0, + nNew>=4 ? apNew[3]->pgno : 0, nNew>=4 ? szNew[3] : 0, + nNew>=4 ? cntNew[3] - cntNew[2] - !leafData : 0, + nNew>=5 ? apNew[4]->pgno : 0, nNew>=5 ? szNew[4] : 0, + nNew>=5 ? cntNew[4] - cntNew[3] - !leafData : 0 + )); -/* -** Given a btree page and a cell index (0 means the first cell on -** the page, 1 means the second cell, and so forth) return a pointer -** to the cell content. -** -** findCellPastPtr() does the same except it skips past the initial -** 4-byte child pointer found on interior pages, if there is one. -** -** This routine works only for pages that do not contain overflow cells. -*/ -#define findCell(P,I) \ - ((P)->aData + ((P)->maskPage & get2byteAligned(&(P)->aCellIdx[2*(I)]))) -#define findCellPastPtr(P,I) \ - ((P)->aDataOfst + ((P)->maskPage & get2byteAligned(&(P)->aCellIdx[2*(I)]))) + assert( sqlcipher_sqlite3PagerIswriteable(pParent->pDbPage) ); + assert( nNew>=1 && nNew<=ArraySize(apNew) ); + assert( apNew[nNew-1]!=0 ); + put4byte(pRight, apNew[nNew-1]->pgno); + /* If the sibling pages are not leaves, ensure that the right-child pointer + ** of the right-most new sibling page is set to the value that was + ** originally in the same field of the right-most old sibling page. */ + if( (pageFlags & PTF_LEAF)==0 && nOld!=nNew ){ + MemPage *pOld = (nNew>nOld ? apNew : apOld)[nOld-1]; + memcpy(&apNew[nNew-1]->aData[8], &pOld->aData[8], 4); + } -/* -** This is common tail processing for btreeParseCellPtr() and -** btreeParseCellPtrIndex() for the case when the cell does not fit entirely -** on a single B-tree page. Make necessary adjustments to the CellInfo -** structure. -*/ -static SQLITE_NOINLINE void btreeParseCellAdjustSizeForOverflow( - MemPage *pPage, /* Page containing the cell */ - u8 *pCell, /* Pointer to the cell text. */ - CellInfo *pInfo /* Fill in this structure */ -){ - /* If the payload will not fit completely on the local page, we have - ** to decide how much to store locally and how much to spill onto - ** overflow pages. The strategy is to minimize the amount of unused - ** space on overflow pages while keeping the amount of local storage - ** in between minLocal and maxLocal. + /* Make any required updates to pointer map entries associated with + ** cells stored on sibling pages following the balance operation. Pointer + ** map entries associated with divider cells are set by the insertCell() + ** routine. The associated pointer map entries are: ** - ** Warning: changing the way overflow payload is distributed in any - ** way will result in an incompatible file format. + ** a) if the cell contains a reference to an overflow chain, the + ** entry associated with the first page in the overflow chain, and + ** + ** b) if the sibling pages are not leaves, the child page associated + ** with the cell. + ** + ** If the sibling pages are not leaves, then the pointer map entry + ** associated with the right-child of each sibling may also need to be + ** updated. This happens below, after the sibling pages have been + ** populated, not here. */ - int minLocal; /* Minimum amount of payload held locally */ - int maxLocal; /* Maximum amount of payload held locally */ - int surplus; /* Overflow payload available for local storage */ + if( ISAUTOVACUUM ){ + MemPage *pOld; + MemPage *pNew = pOld = apNew[0]; + int cntOldNext = pNew->nCell + pNew->nOverflow; + int iNew = 0; + int iOld = 0; - minLocal = pPage->minLocal; - maxLocal = pPage->maxLocal; - surplus = minLocal + (pInfo->nPayload - minLocal)%(pPage->pBt->usableSize-4); - testcase( surplus==maxLocal ); - testcase( surplus==maxLocal+1 ); - if( surplus <= maxLocal ){ - pInfo->nLocal = (u16)surplus; - }else{ - pInfo->nLocal = (u16)minLocal; + for(i=0; i=0 && iOldnCell + pOld->nOverflow + !leafData; + } + if( i==cntNew[iNew] ){ + pNew = apNew[++iNew]; + if( !leafData ) continue; + } + + /* Cell pCell is destined for new sibling page pNew. Originally, it + ** was either part of sibling page iOld (possibly an overflow cell), + ** or else the divider cell to the left of sibling page iOld. So, + ** if sibling page iOld had the same page number as pNew, and if + ** pCell really was a part of sibling page iOld (not a divider or + ** overflow cell), we can skip updating the pointer map entries. */ + if( iOld>=nNew + || pNew->pgno!=aPgno[iOld] + || !SQLITE_WITHIN(pCell,pOld->aData,pOld->aDataEnd) + ){ + if( !leafCorrection ){ + ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc); + } + if( cachedCellSize(&b,i)>pNew->minLocal ){ + ptrmapPutOvflPtr(pNew, pOld, pCell, &rc); + } + if( rc ) goto balance_cleanup; + } + } } - pInfo->nSize = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell) + 4; -} -/* -** The following routines are implementations of the MemPage.xParseCell() -** method. -** -** Parse a cell content block and fill in the CellInfo structure. -** -** btreeParseCellPtr() => table btree leaf nodes -** btreeParseCellNoPayload() => table btree internal nodes -** btreeParseCellPtrIndex() => index btree nodes -** -** There is also a wrapper function btreeParseCell() that works for -** all MemPage types and that references the cell by index rather than -** by pointer. -*/ -static void btreeParseCellPtrNoPayload( - MemPage *pPage, /* Page containing the cell */ - u8 *pCell, /* Pointer to the cell text. */ - CellInfo *pInfo /* Fill in this structure */ -){ - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( pPage->leaf==0 ); - assert( pPage->childPtrSize==4 ); -#ifndef SQLITE_DEBUG - UNUSED_PARAMETER(pPage); -#endif - pInfo->nSize = 4 + getVarint(&pCell[4], (u64*)&pInfo->nKey); - pInfo->nPayload = 0; - pInfo->nLocal = 0; - pInfo->pPayload = 0; - return; -} -static void btreeParseCellPtr( - MemPage *pPage, /* Page containing the cell */ - u8 *pCell, /* Pointer to the cell text. */ - CellInfo *pInfo /* Fill in this structure */ -){ - u8 *pIter; /* For scanning through pCell */ - u32 nPayload; /* Number of bytes of cell payload */ - u64 iKey; /* Extracted Key value */ + /* Insert new divider cells into pParent. */ + for(i=0; ipBt->mutex) ); - assert( pPage->leaf==0 || pPage->leaf==1 ); - assert( pPage->intKeyLeaf ); - assert( pPage->childPtrSize==0 ); - pIter = pCell; + assert( jleaf ){ + memcpy(&pNew->aData[8], pCell, 4); + }else if( leafData ){ + /* If the tree is a leaf-data tree, and the siblings are leaves, + ** then there is no divider cell in b.apCell[]. Instead, the divider + ** cell consists of the integer key for the right-most cell of + ** the sibling-page assembled above only. + */ + CellInfo info; + j--; + pNew->xParseCell(pNew, b.apCell[j], &info); + pCell = pTemp; + sz = 4 + putVarint(&pCell[4], info.nKey); + pTemp = 0; + }else{ + pCell -= 4; + /* Obscure case for non-leaf-data trees: If the cell at pCell was + ** previously stored on a leaf node, and its reported size was 4 + ** bytes, then it may actually be smaller than this + ** (see btreeParseCellPtr(), 4 bytes is the minimum size of + ** any cell). But it is important to pass the correct size to + ** insertCell(), so reparse the cell now. + ** + ** This can only happen for b-trees used to evaluate "IN (SELECT ...)" + ** and WITHOUT ROWID tables with exactly one column which is the + ** primary key. + */ + if( b.szCell[j]==4 ){ + assert(leafCorrection==4); + sz = pParent->xCellSize(pParent, pCell); + } + } + iOvflSpace += sz; + assert( sz<=pBt->maxLocal+23 ); + assert( iOvflSpace <= (int)pBt->pageSize ); + for(k=0; b.ixNx[k]<=j && ALWAYS(kpgno, &rc); + if( rc!=SQLITE_OK ) goto balance_cleanup; + assert( sqlcipher_sqlite3PagerIswriteable(pParent->pDbPage) ); + } - /* The next block of code is equivalent to: + /* Now update the actual sibling pages. The order in which they are updated + ** is important, as this code needs to avoid disrupting any page from which + ** cells may still to be read. In practice, this means: ** - ** pIter += getVarint32(pIter, nPayload); + ** (1) If cells are moving left (from apNew[iPg] to apNew[iPg-1]) + ** then it is not safe to update page apNew[iPg] until after + ** the left-hand sibling apNew[iPg-1] has been updated. ** - ** The code is inlined to avoid a function call. - */ - nPayload = *pIter; - if( nPayload>=0x80 ){ - u8 *pEnd = &pIter[8]; - nPayload &= 0x7f; - do{ - nPayload = (nPayload<<7) | (*++pIter & 0x7f); - }while( (*pIter)>=0x80 && pIternKey); + ** If neither of the above apply, the page is safe to update. ** - ** The code is inlined to avoid a function call. + ** The iPg value in the following loop starts at nNew-1 goes down + ** to 0, then back up to nNew-1 again, thus making two passes over + ** the pages. On the initial downward pass, only condition (1) above + ** needs to be tested because (2) will always be true from the previous + ** step. On the upward pass, both conditions are always true, so the + ** upwards pass simply processes pages that were missed on the downward + ** pass. */ - iKey = *pIter; - if( iKey>=0x80 ){ - u8 *pEnd = &pIter[7]; - iKey &= 0x7f; - while(1){ - iKey = (iKey<<7) | (*++pIter & 0x7f); - if( (*pIter)<0x80 ) break; - if( pIter>=pEnd ){ - iKey = (iKey<<8) | *++pIter; - break; + for(i=1-nNew; i=0 && iPg=0 /* On the upwards pass, or... */ + || cntOld[iPg-1]>=cntNew[iPg-1] /* Condition (1) is true */ + ){ + int iNew; + int iOld; + int nNewCell; + + /* Verify condition (1): If cells are moving left, update iPg + ** only after iPg-1 has already been updated. */ + assert( iPg==0 || cntOld[iPg-1]>=cntNew[iPg-1] || abDone[iPg-1] ); + + /* Verify condition (2): If cells are moving right, update iPg + ** only after iPg+1 has already been updated. */ + assert( cntNew[iPg]>=cntOld[iPg] || abDone[iPg+1] ); + + if( iPg==0 ){ + iNew = iOld = 0; + nNewCell = cntNew[0]; + }else{ + iOld = iPgnFree = usableSpace-szNew[iPg]; + assert( apNew[iPg]->nOverflow==0 ); + assert( apNew[iPg]->nCell==nNewCell ); } } - pIter++; - pInfo->nKey = *(i64*)&iKey; - pInfo->nPayload = nPayload; - pInfo->pPayload = pIter; - testcase( nPayload==pPage->maxLocal ); - testcase( nPayload==pPage->maxLocal+1 ); - if( nPayload<=pPage->maxLocal ){ - /* This is the (easy) common case where the entire payload fits - ** on the local page. No overflow is required. + /* All pages have been processed exactly once */ + assert( memcmp(abDone, "\01\01\01\01\01", nNew)==0 ); + + assert( nOld>0 ); + assert( nNew>0 ); + + if( isRoot && pParent->nCell==0 && pParent->hdrOffset<=apNew[0]->nFree ){ + /* The root page of the b-tree now contains no cells. The only sibling + ** page is the right-child of the parent. Copy the contents of the + ** child page into the parent, decreasing the overall height of the + ** b-tree structure by one. This is described as the "balance-shallower" + ** sub-algorithm in some documentation. + ** + ** If this is an auto-vacuum database, the call to copyNodeContent() + ** sets all pointer-map entries corresponding to database image pages + ** for which the pointer is stored within the content being copied. + ** + ** It is critical that the child page be defragmented before being + ** copied into the parent, because if the parent is page 1 then it will + ** by smaller than the child due to the database header, and so all the + ** free space needs to be up front. */ - pInfo->nSize = nPayload + (u16)(pIter - pCell); - if( pInfo->nSize<4 ) pInfo->nSize = 4; - pInfo->nLocal = (u16)nPayload; - }else{ - btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo); + assert( nNew==1 || CORRUPT_DB ); + rc = defragmentPage(apNew[0], -1); + testcase( rc!=SQLITE_OK ); + assert( apNew[0]->nFree == + (get2byteNotZero(&apNew[0]->aData[5]) - apNew[0]->cellOffset + - apNew[0]->nCell*2) + || rc!=SQLITE_OK + ); + copyNodeContent(apNew[0], pParent, &rc); + freePage(apNew[0], &rc); + }else if( ISAUTOVACUUM && !leafCorrection ){ + /* Fix the pointer map entries associated with the right-child of each + ** sibling page. All other pointer map entries have already been taken + ** care of. */ + for(i=0; iaData[8]); + ptrmapPut(pBt, key, PTRMAP_BTREE, apNew[i]->pgno, &rc); + } } -} -static void btreeParseCellPtrIndex( - MemPage *pPage, /* Page containing the cell */ - u8 *pCell, /* Pointer to the cell text. */ - CellInfo *pInfo /* Fill in this structure */ -){ - u8 *pIter; /* For scanning through pCell */ - u32 nPayload; /* Number of bytes of cell payload */ - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( pPage->leaf==0 || pPage->leaf==1 ); - assert( pPage->intKeyLeaf==0 ); - pIter = pCell + pPage->childPtrSize; - nPayload = *pIter; - if( nPayload>=0x80 ){ - u8 *pEnd = &pIter[8]; - nPayload &= 0x7f; - do{ - nPayload = (nPayload<<7) | (*++pIter & 0x7f); - }while( *(pIter)>=0x80 && pIterisInit ); + TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n", + nOld, nNew, b.nCell)); + + /* Free any old pages that were not reused as new pages. + */ + for(i=nNew; inKey = nPayload; - pInfo->nPayload = nPayload; - pInfo->pPayload = pIter; - testcase( nPayload==pPage->maxLocal ); - testcase( nPayload==pPage->maxLocal+1 ); - if( nPayload<=pPage->maxLocal ){ - /* This is the (easy) common case where the entire payload fits - ** on the local page. No overflow is required. - */ - pInfo->nSize = nPayload + (u16)(pIter - pCell); - if( pInfo->nSize<4 ) pInfo->nSize = 4; - pInfo->nLocal = (u16)nPayload; - }else{ - btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo); + +#if 0 + if( ISAUTOVACUUM && rc==SQLITE_OK && apNew[0]->isInit ){ + /* The ptrmapCheckPages() contains assert() statements that verify that + ** all pointer map pages are set correctly. This is helpful while + ** debugging. This is usually disabled because a corrupt database may + ** cause an assert() statement to fail. */ + ptrmapCheckPages(apNew, nNew); + ptrmapCheckPages(&pParent, 1); } -} -static void btreeParseCell( - MemPage *pPage, /* Page containing the cell */ - int iCell, /* The cell index. First cell is 0 */ - CellInfo *pInfo /* Fill in this structure */ -){ - pPage->xParseCell(pPage, findCell(pPage, iCell), pInfo); +#endif + + /* + ** Cleanup before returning. + */ +balance_cleanup: + sqlcipher_sqlite3StackFree(0, b.apCell); + for(i=0; i table internal nodes -** cellSizePtr() => all index nodes & table leaf nodes +** Before returning, all pointer-map entries corresponding to pages +** that the new child-page now contains pointers to are updated. The +** entry corresponding to the new right-child pointer of the root +** page is also updated. +** +** If successful, *ppChild is set to contain a reference to the child +** page and SQLITE_OK is returned. In this case the caller is required +** to call releasePage() on *ppChild exactly once. If an error occurs, +** an error code is returned and *ppChild is set to 0. */ -static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ - u8 *pIter = pCell + pPage->childPtrSize; /* For looping over bytes of pCell */ - u8 *pEnd; /* End mark for a varint */ - u32 nSize; /* Size value to return */ +static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ + int rc; /* Return value from subprocedures */ + MemPage *pChild = 0; /* Pointer to a new child page */ + Pgno pgnoChild = 0; /* Page number of the new child page */ + BtShared *pBt = pRoot->pBt; /* The BTree */ -#ifdef SQLITE_DEBUG - /* The value returned by this function should always be the same as - ** the (CellInfo.nSize) value found by doing a full parse of the - ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of - ** this function verifies that this invariant is not violated. */ - CellInfo debuginfo; - pPage->xParseCell(pPage, pCell, &debuginfo); -#endif + assert( pRoot->nOverflow>0 ); + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - nSize = *pIter; - if( nSize>=0x80 ){ - pEnd = &pIter[8]; - nSize &= 0x7f; - do{ - nSize = (nSize<<7) | (*++pIter & 0x7f); - }while( *(pIter)>=0x80 && pIterintKey ){ - /* pIter now points at the 64-bit integer key value, a variable length - ** integer. The following block moves pIter to point at the first byte - ** past the end of the key value. */ - pEnd = &pIter[9]; - while( (*pIter++)&0x80 && pItermaxLocal ); - testcase( nSize==pPage->maxLocal+1 ); - if( nSize<=pPage->maxLocal ){ - nSize += (u32)(pIter - pCell); - if( nSize<4 ) nSize = 4; - }else{ - int minLocal = pPage->minLocal; - nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); - testcase( nSize==pPage->maxLocal ); - testcase( nSize==pPage->maxLocal+1 ); - if( nSize>pPage->maxLocal ){ - nSize = minLocal; + /* Make pRoot, the root page of the b-tree, writable. Allocate a new + ** page that will become the new right-child of pPage. Copy the contents + ** of the node stored on pRoot into the new child page. + */ + rc = sqlcipher_sqlite3PagerWrite(pRoot->pDbPage); + if( rc==SQLITE_OK ){ + rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0); + copyNodeContent(pRoot, pChild, &rc); + if( ISAUTOVACUUM ){ + ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc); } - nSize += 4 + (u16)(pIter - pCell); } - assert( nSize==debuginfo.nSize || CORRUPT_DB ); - return (u16)nSize; -} -static u16 cellSizePtrNoPayload(MemPage *pPage, u8 *pCell){ - u8 *pIter = pCell + 4; /* For looping over bytes of pCell */ - u8 *pEnd; /* End mark for a varint */ + if( rc ){ + *ppChild = 0; + releasePage(pChild); + return rc; + } + assert( sqlcipher_sqlite3PagerIswriteable(pChild->pDbPage) ); + assert( sqlcipher_sqlite3PagerIswriteable(pRoot->pDbPage) ); + assert( pChild->nCell==pRoot->nCell || CORRUPT_DB ); -#ifdef SQLITE_DEBUG - /* The value returned by this function should always be the same as - ** the (CellInfo.nSize) value found by doing a full parse of the - ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of - ** this function verifies that this invariant is not violated. */ - CellInfo debuginfo; - pPage->xParseCell(pPage, pCell, &debuginfo); -#else - UNUSED_PARAMETER(pPage); -#endif + TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno)); - assert( pPage->childPtrSize==4 ); - pEnd = pIter + 9; - while( (*pIter++)&0x80 && pIteraiOvfl, pRoot->aiOvfl, + pRoot->nOverflow*sizeof(pRoot->aiOvfl[0])); + memcpy(pChild->apOvfl, pRoot->apOvfl, + pRoot->nOverflow*sizeof(pRoot->apOvfl[0])); + pChild->nOverflow = pRoot->nOverflow; + /* Zero the contents of pRoot. Then install pChild as the right-child. */ + zeroPage(pRoot, pChild->aData[0] & ~PTF_LEAF); + put4byte(&pRoot->aData[pRoot->hdrOffset+8], pgnoChild); -#ifdef SQLITE_DEBUG -/* This variation on cellSizePtr() is used inside of assert() statements -** only. */ -static u16 cellSize(MemPage *pPage, int iCell){ - return pPage->xCellSize(pPage, findCell(pPage, iCell)); + *ppChild = pChild; + return SQLITE_OK; } -#endif -#ifndef SQLITE_OMIT_AUTOVACUUM /* -** The cell pCell is currently part of page pSrc but will ultimately be part -** of pPage. (pSrc and pPager are often the same.) If pCell contains a -** pointer to an overflow page, insert an entry into the pointer-map for -** the overflow page that will be valid after pCell has been moved to pPage. +** Return SQLITE_CORRUPT if any cursor other than pCur is currently valid +** on the same B-tree as pCur. +** +** This can occur if a database is corrupt with two or more SQL tables +** pointing to the same b-tree. If an insert occurs on one SQL table +** and causes a BEFORE TRIGGER to do a secondary insert on the other SQL +** table linked to the same b-tree. If the secondary insert causes a +** rebalance, that can change content out from under the cursor on the +** first SQL table, violating invariants on the first insert. */ -static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){ - CellInfo info; - if( *pRC ) return; - assert( pCell!=0 ); - pPage->xParseCell(pPage, pCell, &info); - if( info.nLocalaDataEnd, pCell, pCell+info.nLocal) ){ - testcase( pSrc!=pPage ); - *pRC = SQLITE_CORRUPT_BKPT; - return; +static int anotherValidCursor(BtCursor *pCur){ + BtCursor *pOther; + for(pOther=pCur->pBt->pCursor; pOther; pOther=pOther->pNext){ + if( pOther!=pCur + && pOther->eState==CURSOR_VALID + && pOther->pPage==pCur->pPage + ){ + return SQLITE_CORRUPT_BKPT; } - ovfl = get4byte(&pCell[info.nSize-4]); - ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC); } + return SQLITE_OK; } -#endif - /* -** Defragment the page given. This routine reorganizes cells within the -** page so that there are no free-blocks on the free-block list. -** -** Parameter nMaxFrag is the maximum amount of fragmented space that may be -** present in the page after this routine returns. +** The page that pCur currently points to has just been modified in +** some way. This function figures out if this modification means the +** tree needs to be balanced, and if so calls the appropriate balancing +** routine. Balancing routines are: ** -** EVIDENCE-OF: R-44582-60138 SQLite may from time to time reorganize a -** b-tree page so that there are no freeblocks or fragment bytes, all -** unused bytes are contained in the unallocated space region, and all -** cells are packed tightly at the end of the page. +** balance_quick() +** balance_deeper() +** balance_nonroot() */ -static int defragmentPage(MemPage *pPage, int nMaxFrag){ - int i; /* Loop counter */ - int pc; /* Address of the i-th cell */ - int hdr; /* Offset to the page header */ - int size; /* Size of a cell */ - int usableSize; /* Number of usable bytes on a page */ - int cellOffset; /* Offset to the cell pointer array */ - int cbrk; /* Offset to the cell content area */ - int nCell; /* Number of cells on the page */ - unsigned char *data; /* The page data */ - unsigned char *temp; /* Temp area for cell content */ - unsigned char *src; /* Source of content */ - int iCellFirst; /* First allowable cell index */ - int iCellLast; /* Last possible cell index */ +static int balance(BtCursor *pCur){ + int rc = SQLITE_OK; + u8 aBalanceQuickSpace[13]; + u8 *pFree = 0; - assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( pPage->pBt!=0 ); - assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); - assert( pPage->nOverflow==0 ); - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - temp = 0; - src = data = pPage->aData; - hdr = pPage->hdrOffset; - cellOffset = pPage->cellOffset; - nCell = pPage->nCell; - assert( nCell==get2byte(&data[hdr+3]) || CORRUPT_DB ); - iCellFirst = cellOffset + 2*nCell; - usableSize = pPage->pBt->usableSize; + VVA_ONLY( int balance_quick_called = 0 ); + VVA_ONLY( int balance_deeper_called = 0 ); - /* This block handles pages with two or fewer free blocks and nMaxFrag - ** or fewer fragmented bytes. In this case it is faster to move the - ** two (or one) blocks of cells using memmove() and add the required - ** offsets to each pointer in the cell-pointer array than it is to - ** reconstruct the entire page. */ - if( (int)data[hdr+7]<=nMaxFrag ){ - int iFree = get2byte(&data[hdr+1]); - if( iFree>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage); - if( iFree ){ - int iFree2 = get2byte(&data[iFree]); - if( iFree2>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage); - if( 0==iFree2 || (data[iFree2]==0 && data[iFree2+1]==0) ){ - u8 *pEnd = &data[cellOffset + nCell*2]; - u8 *pAddr; - int sz2 = 0; - int sz = get2byte(&data[iFree+2]); - int top = get2byte(&data[hdr+5]); - if( top>=iFree ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - if( iFree2 ){ - if( iFree+sz>iFree2 ) return SQLITE_CORRUPT_PAGE(pPage); - sz2 = get2byte(&data[iFree2+2]); - if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage); - memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz)); - sz += sz2; - }else if( NEVER(iFree+sz>usableSize) ){ - return SQLITE_CORRUPT_PAGE(pPage); + do { + int iPage; + MemPage *pPage = pCur->pPage; + + if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break; + if( pPage->nOverflow==0 && pPage->nFree*3<=(int)pCur->pBt->usableSize*2 ){ + /* No rebalance required as long as: + ** (1) There are no overflow cells + ** (2) The amount of free space on the page is less than 2/3rds of + ** the total usable space on the page. */ + break; + }else if( (iPage = pCur->iPage)==0 ){ + if( pPage->nOverflow && (rc = anotherValidCursor(pCur))==SQLITE_OK ){ + /* The root page of the b-tree is overfull. In this case call the + ** balance_deeper() function to create a new child for the root-page + ** and copy the current contents of the root-page to it. The + ** next iteration of the do-loop will balance the child page. + */ + assert( balance_deeper_called==0 ); + VVA_ONLY( balance_deeper_called++ ); + rc = balance_deeper(pPage, &pCur->apPage[1]); + if( rc==SQLITE_OK ){ + pCur->iPage = 1; + pCur->ix = 0; + pCur->aiIdx[0] = 0; + pCur->apPage[0] = pPage; + pCur->pPage = pCur->apPage[1]; + assert( pCur->pPage->nOverflow ); } + }else{ + break; + } + }else{ + MemPage * const pParent = pCur->apPage[iPage-1]; + int const iIdx = pCur->aiIdx[iPage-1]; - cbrk = top+sz; - assert( cbrk+(iFree-top) <= usableSize ); - memmove(&data[cbrk], &data[top], iFree-top); - for(pAddr=&data[cellOffset]; pAddrpDbPage); + if( rc==SQLITE_OK && pParent->nFree<0 ){ + rc = btreeComputeFreeSpace(pParent); + } + if( rc==SQLITE_OK ){ +#ifndef SQLITE_OMIT_QUICKBALANCE + if( pPage->intKeyLeaf + && pPage->nOverflow==1 + && pPage->aiOvfl[0]==pPage->nCell + && pParent->pgno!=1 + && pParent->nCell==iIdx + ){ + /* Call balance_quick() to create a new sibling of pPage on which + ** to store the overflow cell. balance_quick() inserts a new cell + ** into pParent, which may cause pParent overflow. If this + ** happens, the next iteration of the do-loop will balance pParent + ** use either balance_nonroot() or balance_deeper(). Until this + ** happens, the overflow cell is stored in the aBalanceQuickSpace[] + ** buffer. + ** + ** The purpose of the following assert() is to check that only a + ** single call to balance_quick() is made for each call to this + ** function. If this were not verified, a subtle bug involving reuse + ** of the aBalanceQuickSpace[] might sneak in. + */ + assert( balance_quick_called==0 ); + VVA_ONLY( balance_quick_called++ ); + rc = balance_quick(pParent, pPage, aBalanceQuickSpace); + }else +#endif + { + /* In this case, call balance_nonroot() to redistribute cells + ** between pPage and up to 2 of its sibling pages. This involves + ** modifying the contents of pParent, which may cause pParent to + ** become overfull or underfull. The next iteration of the do-loop + ** will balance the parent page to correct this. + ** + ** If the parent page becomes overfull, the overflow cell or cells + ** are stored in the pSpace buffer allocated immediately below. + ** A subsequent iteration of the do-loop will deal with this by + ** calling balance_nonroot() (balance_deeper() may be called first, + ** but it doesn't deal with overflow cells - just moves them to a + ** different page). Once this subsequent call to balance_nonroot() + ** has completed, it is safe to release the pSpace buffer used by + ** the previous call, as the overflow cell data will have been + ** copied either into the body of a database page or into the new + ** pSpace buffer passed to the latter call to balance_nonroot(). + */ + u8 *pSpace = sqlcipher_sqlite3PageMalloc(pCur->pBt->pageSize); + rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1, + pCur->hints&BTREE_BULKLOAD); + if( pFree ){ + /* If pFree is not NULL, it points to the pSpace buffer used + ** by a previous call to balance_nonroot(). Its contents are + ** now stored either on real database pages or within the + ** new pSpace buffer, so it may be safely freed here. */ + sqlcipher_sqlite3PageFree(pFree); + } + + /* The pSpace buffer will be freed after the next call to + ** balance_nonroot(), or just before this function returns, whichever + ** comes first. */ + pFree = pSpace; } - goto defragment_out; } + + pPage->nOverflow = 0; + + /* The next iteration of the do-loop balances the parent page. */ + releasePage(pPage); + pCur->iPage--; + assert( pCur->iPage>=0 ); + pCur->pPage = pCur->apPage[pCur->iPage]; } + }while( rc==SQLITE_OK ); + + if( pFree ){ + sqlcipher_sqlite3PageFree(pFree); } + return rc; +} - cbrk = usableSize; - iCellLast = usableSize - 4; - for(i=0; iiCellLast ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - assert( pc>=iCellFirst && pc<=iCellLast ); - size = pPage->xCellSize(pPage, &src[pc]); - cbrk -= size; - if( cbrkusableSize ){ - return SQLITE_CORRUPT_PAGE(pPage); +/* Overwrite content from pX into pDest. Only do the write if the +** content is different from what is already there. +*/ +static int btreeOverwriteContent( + MemPage *pPage, /* MemPage on which writing will occur */ + u8 *pDest, /* Pointer to the place to start writing */ + const BtreePayload *pX, /* Source of data to write */ + int iOffset, /* Offset of first byte to write */ + int iAmt /* Number of bytes to be written */ +){ + int nData = pX->nData - iOffset; + if( nData<=0 ){ + /* Overwritting with zeros */ + int i; + for(i=0; ipDbPage); + if( rc ) return rc; + memset(pDest + i, 0, iAmt - i); } - assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); - testcase( cbrk+size==usableSize ); - testcase( pc+size==usableSize ); - put2byte(pAddr, cbrk); - if( temp==0 ){ - int x; - if( cbrk==pc ) continue; - temp = sqlcipher_sqlite3PagerTempSpace(pPage->pBt->pPager); - x = get2byte(&data[hdr+5]); - memcpy(&temp[x], &data[x], (cbrk+size) - x); - src = temp; + }else{ + if( nDatapData) + iOffset, iAmt)!=0 ){ + int rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage); + if( rc ) return rc; + /* In a corrupt database, it is possible for the source and destination + ** buffers to overlap. This is harmless since the database is already + ** corrupt but it does cause valgrind and ASAN warnings. So use + ** memmove(). */ + memmove(pDest, ((u8*)pX->pData) + iOffset, iAmt); } - memcpy(&data[cbrk], &src[pc], size); - } - data[hdr+7] = 0; - - defragment_out: - assert( pPage->nFree>=0 ); - if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){ - return SQLITE_CORRUPT_PAGE(pPage); } - assert( cbrk>=iCellFirst ); - put2byte(&data[hdr+5], cbrk); - data[hdr+1] = 0; - data[hdr+2] = 0; - memset(&data[iCellFirst], 0, cbrk-iCellFirst); - assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); return SQLITE_OK; } /* -** Search the free-list on page pPg for space to store a cell nByte bytes in -** size. If one can be found, return a pointer to the space and remove it -** from the free-list. -** -** If no suitable space can be found on the free-list, return NULL. -** -** This function may detect corruption within pPg. If corruption is -** detected then *pRc is set to SQLITE_CORRUPT and NULL is returned. -** -** Slots on the free list that are between 1 and 3 bytes larger than nByte -** will be ignored if adding the extra space to the fragmentation count -** causes the fragmentation count to exceed 60. +** Overwrite the cell that cursor pCur is pointing to with fresh content +** contained in pX. */ -static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ - const int hdr = pPg->hdrOffset; /* Offset to page header */ - u8 * const aData = pPg->aData; /* Page data */ - int iAddr = hdr + 1; /* Address of ptr to pc */ - int pc = get2byte(&aData[iAddr]); /* Address of a free slot */ - int x; /* Excess size of the slot */ - int maxPC = pPg->pBt->usableSize - nByte; /* Max address for a usable slot */ - int size; /* Size of the free slot */ +static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ + int iOffset; /* Next byte of pX->pData to write */ + int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */ + int rc; /* Return code */ + MemPage *pPage = pCur->pPage; /* Page being written */ + BtShared *pBt; /* Btree */ + Pgno ovflPgno; /* Next overflow page to write */ + u32 ovflPageSize; /* Size to write on overflow page */ - assert( pc>0 ); - while( pc<=maxPC ){ - /* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each - ** freeblock form a big-endian integer which is the size of the freeblock - ** in bytes, including the 4-byte header. */ - size = get2byte(&aData[pc+2]); - if( (x = size - nByte)>=0 ){ - testcase( x==4 ); - testcase( x==3 ); - if( x<4 ){ - /* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total - ** number of bytes in fragments may not exceed 60. */ - if( aData[hdr+7]>57 ) return 0; + if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd + || pCur->info.pPayload < pPage->aData + pPage->cellOffset + ){ + return SQLITE_CORRUPT_BKPT; + } + /* Overwrite the local portion first */ + rc = btreeOverwriteContent(pPage, pCur->info.pPayload, pX, + 0, pCur->info.nLocal); + if( rc ) return rc; + if( pCur->info.nLocal==nTotal ) return SQLITE_OK; - /* Remove the slot from the free-list. Update the number of - ** fragmented bytes within the page. */ - memcpy(&aData[iAddr], &aData[pc], 2); - aData[hdr+7] += (u8)x; - }else if( x+pc > maxPC ){ - /* This slot extends off the end of the usable part of the page */ - *pRc = SQLITE_CORRUPT_PAGE(pPg); - return 0; + /* Now overwrite the overflow pages */ + iOffset = pCur->info.nLocal; + assert( nTotal>=0 ); + assert( iOffset>=0 ); + ovflPgno = get4byte(pCur->info.pPayload + iOffset); + pBt = pPage->pBt; + ovflPageSize = pBt->usableSize - 4; + do{ + rc = btreeGetPage(pBt, ovflPgno, &pPage, 0); + if( rc ) return rc; + if( sqlcipher_sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + if( iOffset+ovflPageSize<(u32)nTotal ){ + ovflPgno = get4byte(pPage->aData); }else{ - /* The slot remains on the free-list. Reduce its size to account - ** for the portion used by the new allocation. */ - put2byte(&aData[pc+2], x); - } - return &aData[pc + x]; - } - iAddr = pc; - pc = get2byte(&aData[pc]); - if( pc<=iAddr+size ){ - if( pc ){ - /* The next slot in the chain is not past the end of the current slot */ - *pRc = SQLITE_CORRUPT_PAGE(pPg); + ovflPageSize = nTotal - iOffset; } - return 0; + rc = btreeOverwriteContent(pPage, pPage->aData+4, pX, + iOffset, ovflPageSize); } - } - if( pc>maxPC+nByte-4 ){ - /* The free slot chain extends off the end of the page */ - *pRc = SQLITE_CORRUPT_PAGE(pPg); - } - return 0; + sqlcipher_sqlite3PagerUnref(pPage->pDbPage); + if( rc ) return rc; + iOffset += ovflPageSize; + }while( iOffsetaData[] -** of the first byte of allocated space. Return either SQLITE_OK or -** an error code (usually SQLITE_CORRUPT). +** Insert a new record into the BTree. The content of the new record +** is described by the pX object. The pCur cursor is used only to +** define what table the record should be inserted into, and is left +** pointing at a random location. ** -** The caller guarantees that there is sufficient space to make the -** allocation. This routine might need to defragment in order to bring -** all the space together, however. This routine will avoid using -** the first two bytes past the cell pointer area since presumably this -** allocation is being made in order to insert a new cell, so we will -** also end up needing a new cell pointer. +** For a table btree (used for rowid tables), only the pX.nKey value of +** the key is used. The pX.pKey value must be NULL. The pX.nKey is the +** rowid or INTEGER PRIMARY KEY of the row. The pX.nData,pData,nZero fields +** hold the content of the row. +** +** For an index btree (used for indexes and WITHOUT ROWID tables), the +** key is an arbitrary byte sequence stored in pX.pKey,nKey. The +** pX.pData,nData,nZero fields must be zero. +** +** If the seekResult parameter is non-zero, then a successful call to +** sqlcipher_sqlite3BtreeIndexMoveto() to seek cursor pCur to (pKey,nKey) has already +** been performed. In other words, if seekResult!=0 then the cursor +** is currently pointing to a cell that will be adjacent to the cell +** to be inserted. If seekResult<0 then pCur points to a cell that is +** smaller then (pKey,nKey). If seekResult>0 then pCur points to a cell +** that is larger than (pKey,nKey). +** +** If seekResult==0, that means pCur is pointing at some unknown location. +** In that case, this routine must seek the cursor to the correct insertion +** point for (pKey,nKey) before doing the insertion. For index btrees, +** if pX->nMem is non-zero, then pX->aMem contains pointers to the unpacked +** key values and pX->aMem can be used instead of pX->pKey to avoid having +** to decode the key. */ -static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ - const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */ - u8 * const data = pPage->aData; /* Local cache of pPage->aData */ - int top; /* First byte of cell content area */ - int rc = SQLITE_OK; /* Integer return code */ - int gap; /* First byte of gap between cell pointers and cell content */ - - assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( pPage->pBt ); - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( nByte>=0 ); /* Minimum cell size is 4 */ - assert( pPage->nFree>=nByte ); - assert( pPage->nOverflow==0 ); - assert( nByte < (int)(pPage->pBt->usableSize-8) ); +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeInsert( + BtCursor *pCur, /* Insert data into the table of this cursor */ + const BtreePayload *pX, /* Content of the row to be inserted */ + int flags, /* True if this is likely an append */ + int seekResult /* Result of prior IndexMoveto() call */ +){ + int rc; + int loc = seekResult; /* -1: before desired location +1: after */ + int szNew = 0; + int idx; + MemPage *pPage; + Btree *p = pCur->pBtree; + BtShared *pBt = p->pBt; + unsigned char *oldCell; + unsigned char *newCell = 0; - assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf ); - gap = pPage->cellOffset + 2*pPage->nCell; - assert( gap<=65536 ); - /* EVIDENCE-OF: R-29356-02391 If the database uses a 65536-byte page size - ** and the reserved space is zero (the usual value for reserved space) - ** then the cell content offset of an empty page wants to be 65536. - ** However, that integer is too large to be stored in a 2-byte unsigned - ** integer, so a value of 0 is used in its place. */ - top = get2byte(&data[hdr+5]); - assert( top<=(int)pPage->pBt->usableSize ); /* by btreeComputeFreeSpace() */ - if( gap>top ){ - if( top==0 && pPage->pBt->usableSize==65536 ){ - top = 65536; - }else{ - return SQLITE_CORRUPT_PAGE(pPage); - } - } + assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND|BTREE_PREFORMAT))==flags ); + assert( (flags & BTREE_PREFORMAT)==0 || seekResult || pCur->pKeyInfo==0 ); - /* If there is enough space between gap and top for one more cell pointer, - ** and if the freelist is not empty, then search the - ** freelist looking for a slot big enough to satisfy the request. + /* Save the positions of any other cursors open on this table. + ** + ** In some cases, the call to btreeMoveto() below is a no-op. For + ** example, when inserting data into a table with auto-generated integer + ** keys, the VDBE layer invokes sqlcipher_sqlite3BtreeLast() to figure out the + ** integer key to use. It then calls this function to actually insert the + ** data into the intkey B-Tree. In this case btreeMoveto() recognizes + ** that the cursor is already where it needs to be and returns without + ** doing any work. To avoid thwarting these optimizations, it is important + ** not to clear the cursor here. */ - testcase( gap+2==top ); - testcase( gap+1==top ); - testcase( gap==top ); - if( (data[hdr+2] || data[hdr+1]) && gap+2<=top ){ - u8 *pSpace = pageFindSlot(pPage, nByte, &rc); - if( pSpace ){ - int g2; - assert( pSpace+nByte<=data+pPage->pBt->usableSize ); - *pIdx = g2 = (int)(pSpace-data); - if( NEVER(g2<=gap) ){ - return SQLITE_CORRUPT_PAGE(pPage); - }else{ - return SQLITE_OK; - } - }else if( rc ){ - return rc; + if( pCur->curFlags & BTCF_Multiple ){ + rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); + if( rc ) return rc; + if( loc && pCur->iPage<0 ){ + /* This can only happen if the schema is corrupt such that there is more + ** than one table or index with the same root page as used by the cursor. + ** Which can only happen if the SQLITE_NoSchemaError flag was set when + ** the schema was loaded. This cannot be asserted though, as a user might + ** set the flag, load the schema, and then unset the flag. */ + return SQLITE_CORRUPT_BKPT; } } - /* The request could not be fulfilled using a freelist slot. Check - ** to see if defragmentation is necessary. + /* Ensure that the cursor is not in the CURSOR_FAULT state and that it + ** points to a valid cell. */ - testcase( gap+2+nByte==top ); - if( gap+2+nByte>top ){ - assert( pPage->nCell>0 || CORRUPT_DB ); - assert( pPage->nFree>=0 ); - rc = defragmentPage(pPage, MIN(4, pPage->nFree - (2+nByte))); - if( rc ) return rc; - top = get2byteNotZero(&data[hdr+5]); - assert( gap+2+nByte<=top ); + if( pCur->eState>=CURSOR_REQUIRESEEK ){ + testcase( pCur->eState==CURSOR_REQUIRESEEK ); + testcase( pCur->eState==CURSOR_FAULT ); + rc = moveToRoot(pCur); + if( rc && rc!=SQLITE_EMPTY ) return rc; } + assert( cursorOwnsBtShared(pCur) ); + assert( (pCur->curFlags & BTCF_WriteFlag)!=0 + && pBt->inTransaction==TRANS_WRITE + && (pBt->btsFlags & BTS_READ_ONLY)==0 ); + assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); - /* Allocate memory from the gap in between the cell pointer array - ** and the cell content area. The btreeComputeFreeSpace() call has already - ** validated the freelist. Given that the freelist is valid, there - ** is no way that the allocation can extend off the end of the page. - ** The assert() below verifies the previous sentence. - */ - top -= nByte; - put2byte(&data[hdr+5], top); - assert( top+nByte <= (int)pPage->pBt->usableSize ); - *pIdx = top; - return SQLITE_OK; -} + /* Assert that the caller has been consistent. If this cursor was opened + ** expecting an index b-tree, then the caller should be inserting blob + ** keys with no associated data. If the cursor was opened expecting an + ** intkey table, the caller should be inserting integer keys with a + ** blob of associated data. */ + assert( (flags & BTREE_PREFORMAT) || (pX->pKey==0)==(pCur->pKeyInfo==0) ); -/* -** Return a section of the pPage->aData to the freelist. -** The first byte of the new free block is pPage->aData[iStart] -** and the size of the block is iSize bytes. -** -** Adjacent freeblocks are coalesced. -** -** Even though the freeblock list was checked by btreeComputeFreeSpace(), -** that routine will not detect overlap between cells or freeblocks. Nor -** does it detect cells or freeblocks that encrouch into the reserved bytes -** at the end of the page. So do additional corruption checks inside this -** routine and return SQLITE_CORRUPT if any problems are found. -*/ -static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ - u16 iPtr; /* Address of ptr to next freeblock */ - u16 iFreeBlk; /* Address of the next freeblock */ - u8 hdr; /* Page header size. 0 or 100 */ - u8 nFrag = 0; /* Reduction in fragmentation */ - u16 iOrigSize = iSize; /* Original value of iSize */ - u16 x; /* Offset to cell content area */ - u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */ - unsigned char *data = pPage->aData; /* Page content */ + if( pCur->pKeyInfo==0 ){ + assert( pX->pKey==0 ); + /* If this is an insert into a table b-tree, invalidate any incrblob + ** cursors open on the row being replaced */ + if( p->hasIncrblobCur ){ + invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0); + } - assert( pPage->pBt!=0 ); - assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize ); - assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize ); - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( iSize>=4 ); /* Minimum cell size is 4 */ - assert( iStart<=pPage->pBt->usableSize-4 ); + /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing + ** to a row with the same key as the new entry being inserted. + */ +#ifdef SQLITE_DEBUG + if( flags & BTREE_SAVEPOSITION ){ + assert( pCur->curFlags & BTCF_ValidNKey ); + assert( pX->nKey==pCur->info.nKey ); + assert( loc==0 ); + } +#endif - /* The list of freeblocks must be in ascending order. Find the - ** spot on the list where iStart should be inserted. - */ - hdr = pPage->hdrOffset; - iPtr = hdr + 1; - if( data[iPtr+1]==0 && data[iPtr]==0 ){ - iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */ - }else{ - while( (iFreeBlk = get2byte(&data[iPtr]))curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey ){ + /* The cursor is pointing to the entry that is to be + ** overwritten */ + assert( pX->nData>=0 && pX->nZero>=0 ); + if( pCur->info.nSize!=0 + && pCur->info.nPayload==(u32)pX->nData+pX->nZero + ){ + /* New entry is the same size as the old. Do an overwrite */ + return btreeOverwriteCell(pCur, pX); } - iPtr = iFreeBlk; - } - if( iFreeBlk>pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */ - return SQLITE_CORRUPT_PAGE(pPage); + assert( loc==0 ); + }else if( loc==0 ){ + /* The cursor is *not* pointing to the cell to be overwritten, nor + ** to an adjacent cell. Move the cursor so that it is pointing either + ** to the cell to be overwritten or an adjacent cell. + */ + rc = sqlcipher_sqlite3BtreeTableMoveto(pCur, pX->nKey, + (flags & BTREE_APPEND)!=0, &loc); + if( rc ) return rc; } - assert( iFreeBlk>iPtr || iFreeBlk==0 ); + }else{ + /* This is an index or a WITHOUT ROWID table */ - /* At this point: - ** iFreeBlk: First freeblock after iStart, or zero if none - ** iPtr: The address of a pointer to iFreeBlk - ** - ** Check to see if iFreeBlk should be coalesced onto the end of iStart. + /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing + ** to a row with the same key as the new entry being inserted. */ - if( iFreeBlk && iEnd+3>=iFreeBlk ){ - nFrag = iFreeBlk - iEnd; - if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage); - iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]); - if( iEnd > pPage->pBt->usableSize ){ - return SQLITE_CORRUPT_PAGE(pPage); + assert( (flags & BTREE_SAVEPOSITION)==0 || loc==0 ); + + /* If the cursor is not already pointing either to the cell to be + ** overwritten, or if a new cell is being inserted, if the cursor is + ** not pointing to an immediately adjacent cell, then move the cursor + ** so that it does. + */ + if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){ + if( pX->nMem ){ + UnpackedRecord r; + r.pKeyInfo = pCur->pKeyInfo; + r.aMem = pX->aMem; + r.nField = pX->nMem; + r.default_rc = 0; + r.eqSeen = 0; + rc = sqlcipher_sqlite3BtreeIndexMoveto(pCur, &r, &loc); + }else{ + rc = btreeMoveto(pCur, pX->pKey, pX->nKey, + (flags & BTREE_APPEND)!=0, &loc); } - iSize = iEnd - iStart; - iFreeBlk = get2byte(&data[iFreeBlk]); + if( rc ) return rc; } - /* If iPtr is another freeblock (that is, if iPtr is not the freelist - ** pointer in the page header) then check to see if iStart should be - ** coalesced onto the end of iPtr. + /* If the cursor is currently pointing to an entry to be overwritten + ** and the new content is the same as as the old, then use the + ** overwrite optimization. */ - if( iPtr>hdr+1 ){ - int iPtrEnd = iPtr + get2byte(&data[iPtr+2]); - if( iPtrEnd+3>=iStart ){ - if( iPtrEnd>iStart ) return SQLITE_CORRUPT_PAGE(pPage); - nFrag += iStart - iPtrEnd; - iSize = iEnd - iPtr; - iStart = iPtr; + if( loc==0 ){ + getCellInfo(pCur); + if( pCur->info.nKey==pX->nKey ){ + BtreePayload x2; + x2.pData = pX->pKey; + x2.nData = pX->nKey; + x2.nZero = 0; + return btreeOverwriteCell(pCur, &x2); } } - if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage); - data[hdr+7] -= nFrag; - } - x = get2byte(&data[hdr+5]); - if( iStart<=x ){ - /* The new freeblock is at the beginning of the cell content area, - ** so just extend the cell content area rather than create another - ** freelist entry */ - if( iStartpBt->btsFlags & BTS_FAST_SECURE ){ - /* Overwrite deleted information with zeros when the secure_delete - ** option is enabled */ - memset(&data[iStart], 0, iSize); } - put2byte(&data[iStart], iFreeBlk); - put2byte(&data[iStart+2], iSize); - pPage->nFree += iOrigSize; - return SQLITE_OK; -} - -/* -** Decode the flags byte (the first byte of the header) for a page -** and initialize fields of the MemPage structure accordingly. -** -** Only the following combinations are supported. Anything different -** indicates a corrupt database files: -** -** PTF_ZERODATA -** PTF_ZERODATA | PTF_LEAF -** PTF_LEAFDATA | PTF_INTKEY -** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF -*/ -static int decodeFlags(MemPage *pPage, int flagByte){ - BtShared *pBt; /* A copy of pPage->pBt */ + assert( pCur->eState==CURSOR_VALID + || (pCur->eState==CURSOR_INVALID && loc) ); - assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) ); - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 ); - flagByte &= ~PTF_LEAF; - pPage->childPtrSize = 4-4*pPage->leaf; - pPage->xCellSize = cellSizePtr; - pBt = pPage->pBt; - if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ - /* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an - ** interior table b-tree page. */ - assert( (PTF_LEAFDATA|PTF_INTKEY)==5 ); - /* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a - ** leaf table b-tree page. */ - assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 ); - pPage->intKey = 1; - if( pPage->leaf ){ - pPage->intKeyLeaf = 1; - pPage->xParseCell = btreeParseCellPtr; + pPage = pCur->pPage; + assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) ); + assert( pPage->leaf || !pPage->intKey ); + if( pPage->nFree<0 ){ + if( NEVER(pCur->eState>CURSOR_INVALID) ){ + /* ^^^^^--- due to the moveToRoot() call above */ + rc = SQLITE_CORRUPT_BKPT; }else{ - pPage->intKeyLeaf = 0; - pPage->xCellSize = cellSizePtrNoPayload; - pPage->xParseCell = btreeParseCellPtrNoPayload; + rc = btreeComputeFreeSpace(pPage); } - pPage->maxLocal = pBt->maxLeaf; - pPage->minLocal = pBt->minLeaf; - }else if( flagByte==PTF_ZERODATA ){ - /* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an - ** interior index b-tree page. */ - assert( (PTF_ZERODATA)==2 ); - /* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a - ** leaf index b-tree page. */ - assert( (PTF_ZERODATA|PTF_LEAF)==10 ); - pPage->intKey = 0; - pPage->intKeyLeaf = 0; - pPage->xParseCell = btreeParseCellPtrIndex; - pPage->maxLocal = pBt->maxLocal; - pPage->minLocal = pBt->minLocal; - }else{ - /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is - ** an error. */ - return SQLITE_CORRUPT_PAGE(pPage); + if( rc ) return rc; } - pPage->max1bytePayload = pBt->max1bytePayload; - return SQLITE_OK; -} - -/* -** Compute the amount of freespace on the page. In other words, fill -** in the pPage->nFree field. -*/ -static int btreeComputeFreeSpace(MemPage *pPage){ - int pc; /* Address of a freeblock within pPage->aData[] */ - u8 hdr; /* Offset to beginning of page header */ - u8 *data; /* Equal to pPage->aData */ - int usableSize; /* Amount of usable space on each page */ - int nFree; /* Number of unused bytes on the page */ - int top; /* First byte of the cell content area */ - int iCellFirst; /* First allowable cell or freeblock offset */ - int iCellLast; /* Last possible cell or freeblock offset */ - - assert( pPage->pBt!=0 ); - assert( pPage->pBt->db!=0 ); - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( pPage->pgno==sqlcipher_sqlite3PagerPagenumber(pPage->pDbPage) ); - assert( pPage == sqlcipher_sqlite3PagerGetExtra(pPage->pDbPage) ); - assert( pPage->aData == sqlcipher_sqlite3PagerGetData(pPage->pDbPage) ); - assert( pPage->isInit==1 ); - assert( pPage->nFree<0 ); - - usableSize = pPage->pBt->usableSize; - hdr = pPage->hdrOffset; - data = pPage->aData; - /* EVIDENCE-OF: R-58015-48175 The two-byte integer at offset 5 designates - ** the start of the cell content area. A zero value for this integer is - ** interpreted as 65536. */ - top = get2byteNotZero(&data[hdr+5]); - iCellFirst = hdr + 8 + pPage->childPtrSize + 2*pPage->nCell; - iCellLast = usableSize - 4; - /* Compute the total free space on the page - ** EVIDENCE-OF: R-23588-34450 The two-byte integer at offset 1 gives the - ** start of the first freeblock on the page, or is zero if there are no - ** freeblocks. */ - pc = get2byte(&data[hdr+1]); - nFree = data[hdr+7] + top; /* Init nFree to non-freeblock free space */ - if( pc>0 ){ - u32 next, size; - if( pciCellLast ){ - /* Freeblock off the end of the page */ - return SQLITE_CORRUPT_PAGE(pPage); + TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", + pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, + loc==0 ? "overwrite" : "new entry")); + assert( pPage->isInit || CORRUPT_DB ); + newCell = pBt->pTmpSpace; + assert( newCell!=0 ); + if( flags & BTREE_PREFORMAT ){ + rc = SQLITE_OK; + szNew = pBt->nPreformatSize; + if( szNew<4 ) szNew = 4; + if( ISAUTOVACUUM && szNew>pPage->maxLocal ){ + CellInfo info; + pPage->xParseCell(pPage, newCell, &info); + if( info.nPayload!=info.nLocal ){ + Pgno ovfl = get4byte(&newCell[szNew-4]); + ptrmapPut(pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc); } - next = get2byte(&data[pc]); - size = get2byte(&data[pc+2]); - nFree = nFree + size; - if( next<=pc+size+3 ) break; - pc = next; } - if( next>0 ){ - /* Freeblock not in ascending order */ - return SQLITE_CORRUPT_PAGE(pPage); + }else{ + rc = fillInCell(pPage, newCell, pX, &szNew); + } + if( rc ) goto end_insert; + assert( szNew==pPage->xCellSize(pPage, newCell) ); + assert( szNew <= MX_CELL_SIZE(pBt) ); + idx = pCur->ix; + if( loc==0 ){ + CellInfo info; + assert( idx>=0 ); + if( idx>=pPage->nCell ){ + return SQLITE_CORRUPT_BKPT; } - if( pc+size>(unsigned int)usableSize ){ - /* Last freeblock extends past page end */ - return SQLITE_CORRUPT_PAGE(pPage); + rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage); + if( rc ){ + goto end_insert; + } + oldCell = findCell(pPage, idx); + if( !pPage->leaf ){ + memcpy(newCell, oldCell, 4); + } + BTREE_CLEAR_CELL(rc, pPage, oldCell, info); + testcase( pCur->curFlags & BTCF_ValidOvfl ); + invalidateOverflowCache(pCur); + if( info.nSize==szNew && info.nLocal==info.nPayload + && (!ISAUTOVACUUM || szNewminLocal) + ){ + /* Overwrite the old cell with the new if they are the same size. + ** We could also try to do this if the old cell is smaller, then add + ** the leftover space to the free list. But experiments show that + ** doing that is no faster then skipping this optimization and just + ** calling dropCell() and insertCell(). + ** + ** This optimization cannot be used on an autovacuum database if the + ** new entry uses overflow pages, as the insertCell() call below is + ** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */ + assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */ + if( oldCell < pPage->aData+pPage->hdrOffset+10 ){ + return SQLITE_CORRUPT_BKPT; + } + if( oldCell+szNew > pPage->aDataEnd ){ + return SQLITE_CORRUPT_BKPT; + } + memcpy(oldCell, newCell, szNew); + return SQLITE_OK; } + dropCell(pPage, idx, info.nSize, &rc); + if( rc ) goto end_insert; + }else if( loc<0 && pPage->nCell>0 ){ + assert( pPage->leaf ); + idx = ++pCur->ix; + pCur->curFlags &= ~BTCF_ValidNKey; + }else{ + assert( pPage->leaf ); } + insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); + assert( pPage->nOverflow==0 || rc==SQLITE_OK ); + assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); - /* At this point, nFree contains the sum of the offset to the start - ** of the cell-content area plus the number of free bytes within - ** the cell-content area. If this is greater than the usable-size - ** of the page, then the page must be corrupted. This check also - ** serves to verify that the offset to the start of the cell-content - ** area, according to the page header, lies within the page. + /* If no error has occurred and pPage has an overflow cell, call balance() + ** to redistribute the cells within the tree. Since balance() may move + ** the cursor, zero the BtCursor.info.nSize and BTCF_ValidNKey + ** variables. + ** + ** Previous versions of SQLite called moveToRoot() to move the cursor + ** back to the root page as balance() used to invalidate the contents + ** of BtCursor.apPage[] and BtCursor.aiIdx[]. Instead of doing that, + ** set the cursor state to "invalid". This makes common insert operations + ** slightly faster. + ** + ** There is a subtle but important optimization here too. When inserting + ** multiple records into an intkey b-tree using a single cursor (as can + ** happen while processing an "INSERT INTO ... SELECT" statement), it + ** is advantageous to leave the cursor pointing to the last entry in + ** the b-tree if possible. If the cursor is left pointing to the last + ** entry in the table, and the next row inserted has an integer key + ** larger than the largest existing key, it is possible to insert the + ** row without seeking the cursor. This can be a big performance boost. */ - if( nFree>usableSize || nFreeinfo.nSize = 0; + if( pPage->nOverflow ){ + assert( rc==SQLITE_OK ); + pCur->curFlags &= ~(BTCF_ValidNKey); + rc = balance(pCur); + + /* Must make sure nOverflow is reset to zero even if the balance() + ** fails. Internal data structure corruption will result otherwise. + ** Also, set the cursor state to invalid. This stops saveCursorPosition() + ** from trying to save the current position of the cursor. */ + pCur->pPage->nOverflow = 0; + pCur->eState = CURSOR_INVALID; + if( (flags & BTREE_SAVEPOSITION) && rc==SQLITE_OK ){ + btreeReleaseAllCursorPages(pCur); + if( pCur->pKeyInfo ){ + assert( pCur->pKey==0 ); + pCur->pKey = sqlcipher_sqlite3Malloc( pX->nKey ); + if( pCur->pKey==0 ){ + rc = SQLITE_NOMEM; + }else{ + memcpy(pCur->pKey, pX->pKey, pX->nKey); + } + } + pCur->eState = CURSOR_REQUIRESEEK; + pCur->nKey = pX->nKey; + } } - pPage->nFree = (u16)(nFree - iCellFirst); - return SQLITE_OK; + assert( pCur->iPage<0 || pCur->pPage->nOverflow==0 ); + +end_insert: + return rc; } /* -** Do additional sanity check after btreeInitPage() if -** PRAGMA cell_size_check=ON +** This function is used as part of copying the current row from cursor +** pSrc into cursor pDest. If the cursors are open on intkey tables, then +** parameter iKey is used as the rowid value when the record is copied +** into pDest. Otherwise, the record is copied verbatim. +** +** This function does not actually write the new value to cursor pDest. +** Instead, it creates and populates any required overflow pages and +** writes the data for the new cell into the BtShared.pTmpSpace buffer +** for the destination database. The size of the cell, in bytes, is left +** in BtShared.nPreformatSize. The caller completes the insertion by +** calling sqlcipher_sqlite3BtreeInsert() with the BTREE_PREFORMAT flag specified. +** +** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ -static SQLITE_NOINLINE int btreeCellSizeCheck(MemPage *pPage){ - int iCellFirst; /* First allowable cell or freeblock offset */ - int iCellLast; /* Last possible cell or freeblock offset */ - int i; /* Index into the cell pointer array */ - int sz; /* Size of a cell */ - int pc; /* Address of a freeblock within pPage->aData[] */ - u8 *data; /* Equal to pPage->aData */ - int usableSize; /* Maximum usable space on the page */ - int cellOffset; /* Start of cell content area */ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){ + int rc = SQLITE_OK; + BtShared *pBt = pDest->pBt; + u8 *aOut = pBt->pTmpSpace; /* Pointer to next output buffer */ + const u8 *aIn; /* Pointer to next input buffer */ + u32 nIn; /* Size of input buffer aIn[] */ + u32 nRem; /* Bytes of data still to copy */ + + getCellInfo(pSrc); + if( pSrc->info.nPayload<0x80 ){ + *(aOut++) = pSrc->info.nPayload; + }else{ + aOut += sqlcipher_sqlite3PutVarint(aOut, pSrc->info.nPayload); + } + if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey); + nIn = pSrc->info.nLocal; + aIn = pSrc->info.pPayload; + if( aIn+nIn>pSrc->pPage->aDataEnd ){ + return SQLITE_CORRUPT_BKPT; + } + nRem = pSrc->info.nPayload; + if( nIn==nRem && nInpPage->maxLocal ){ + memcpy(aOut, aIn, nIn); + pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace); + }else{ + Pager *pSrcPager = pSrc->pBt->pPager; + u8 *pPgnoOut = 0; + Pgno ovflIn = 0; + DbPage *pPageIn = 0; + MemPage *pPageOut = 0; + u32 nOut; /* Size of output buffer aOut[] */ - iCellFirst = pPage->cellOffset + 2*pPage->nCell; - usableSize = pPage->pBt->usableSize; - iCellLast = usableSize - 4; - data = pPage->aData; - cellOffset = pPage->cellOffset; - if( !pPage->leaf ) iCellLast--; - for(i=0; inCell; i++){ - pc = get2byteAligned(&data[cellOffset+i*2]); - testcase( pc==iCellFirst ); - testcase( pc==iCellLast ); - if( pciCellLast ){ - return SQLITE_CORRUPT_PAGE(pPage); + nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload); + pBt->nPreformatSize = nOut + (aOut - pBt->pTmpSpace); + if( nOutinfo.nPayload ){ + pPgnoOut = &aOut[nOut]; + pBt->nPreformatSize += 4; } - sz = pPage->xCellSize(pPage, &data[pc]); - testcase( pc+sz==usableSize ); - if( pc+sz>usableSize ){ - return SQLITE_CORRUPT_PAGE(pPage); + + if( nRem>nIn ){ + if( aIn+nIn+4>pSrc->pPage->aDataEnd ){ + return SQLITE_CORRUPT_BKPT; + } + ovflIn = get4byte(&pSrc->info.pPayload[nIn]); } - } - return SQLITE_OK; -} -/* -** Initialize the auxiliary information for a disk block. -** -** Return SQLITE_OK on success. If we see that the page does -** not contain a well-formed database page, then return -** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not -** guarantee that the page is well-formed. It only shows that -** we failed to detect any corruption. -*/ -static int btreeInitPage(MemPage *pPage){ - u8 *data; /* Equal to pPage->aData */ - BtShared *pBt; /* The main btree structure */ + do { + nRem -= nOut; + do{ + assert( nOut>0 ); + if( nIn>0 ){ + int nCopy = MIN(nOut, nIn); + memcpy(aOut, aIn, nCopy); + nOut -= nCopy; + nIn -= nCopy; + aOut += nCopy; + aIn += nCopy; + } + if( nOut>0 ){ + sqlcipher_sqlite3PagerUnref(pPageIn); + pPageIn = 0; + rc = sqlcipher_sqlite3PagerGet(pSrcPager, ovflIn, &pPageIn, PAGER_GET_READONLY); + if( rc==SQLITE_OK ){ + aIn = (const u8*)sqlcipher_sqlite3PagerGetData(pPageIn); + ovflIn = get4byte(aIn); + aIn += 4; + nIn = pSrc->pBt->usableSize - 4; + } + } + }while( rc==SQLITE_OK && nOut>0 ); - assert( pPage->pBt!=0 ); - assert( pPage->pBt->db!=0 ); - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( pPage->pgno==sqlcipher_sqlite3PagerPagenumber(pPage->pDbPage) ); - assert( pPage == sqlcipher_sqlite3PagerGetExtra(pPage->pDbPage) ); - assert( pPage->aData == sqlcipher_sqlite3PagerGetData(pPage->pDbPage) ); - assert( pPage->isInit==0 ); + if( rc==SQLITE_OK && nRem>0 && ALWAYS(pPgnoOut) ){ + Pgno pgnoNew; + MemPage *pNew = 0; + rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); + put4byte(pPgnoOut, pgnoNew); + if( ISAUTOVACUUM && pPageOut ){ + ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc); + } + releasePage(pPageOut); + pPageOut = pNew; + if( pPageOut ){ + pPgnoOut = pPageOut->aData; + put4byte(pPgnoOut, 0); + aOut = &pPgnoOut[4]; + nOut = MIN(pBt->usableSize - 4, nRem); + } + } + }while( nRem>0 && rc==SQLITE_OK ); - pBt = pPage->pBt; - data = pPage->aData + pPage->hdrOffset; - /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating - ** the b-tree page type. */ - if( decodeFlags(pPage, data[0]) ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); - pPage->maskPage = (u16)(pBt->pageSize - 1); - pPage->nOverflow = 0; - pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize; - pPage->aCellIdx = data + pPage->childPtrSize + 8; - pPage->aDataEnd = pPage->aData + pBt->usableSize; - pPage->aDataOfst = pPage->aData + pPage->childPtrSize; - /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the - ** number of cells on the page. */ - pPage->nCell = get2byte(&data[3]); - if( pPage->nCell>MX_CELL(pBt) ){ - /* To many cells for a single page. The page must be corrupt */ - return SQLITE_CORRUPT_PAGE(pPage); - } - testcase( pPage->nCell==MX_CELL(pBt) ); - /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only - ** possible for a root page of a table that contains no rows) then the - ** offset to the cell content area will equal the page size minus the - ** bytes of reserved space. */ - assert( pPage->nCell>0 - || get2byteNotZero(&data[5])==(int)pBt->usableSize - || CORRUPT_DB ); - pPage->nFree = -1; /* Indicate that this value is yet uncomputed */ - pPage->isInit = 1; - if( pBt->db->flags & SQLITE_CellSizeCk ){ - return btreeCellSizeCheck(pPage); + releasePage(pPageOut); + sqlcipher_sqlite3PagerUnref(pPageIn); } - return SQLITE_OK; + + return rc; } /* -** Set up a raw page so that it looks like a database page holding -** no entries. +** Delete the entry that the cursor is pointing to. +** +** If the BTREE_SAVEPOSITION bit of the flags parameter is zero, then +** the cursor is left pointing at an arbitrary location after the delete. +** But if that bit is set, then the cursor is left in a state such that +** the next call to BtreeNext() or BtreePrev() moves it to the same row +** as it would have been on if the call to BtreeDelete() had been omitted. +** +** The BTREE_AUXDELETE bit of flags indicates that is one of several deletes +** associated with a single table entry and its indexes. Only one of those +** deletes is considered the "primary" delete. The primary delete occurs +** on a cursor that is not a BTREE_FORDELETE cursor. All but one delete +** operation on non-FORDELETE cursors is tagged with the AUXDELETE flag. +** The BTREE_AUXDELETE bit is a hint that is not used by this implementation, +** but which might be used by alternative storage engines. */ -static void zeroPage(MemPage *pPage, int flags){ - unsigned char *data = pPage->aData; - BtShared *pBt = pPage->pBt; - u8 hdr = pPage->hdrOffset; - u16 first; +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ + Btree *p = pCur->pBtree; + BtShared *pBt = p->pBt; + int rc; /* Return code */ + MemPage *pPage; /* Page to delete cell from */ + unsigned char *pCell; /* Pointer to cell to delete */ + int iCellIdx; /* Index of cell to delete */ + int iCellDepth; /* Depth of node containing pCell */ + CellInfo info; /* Size of the cell being deleted */ + u8 bPreserve; /* Keep cursor valid. 2 for CURSOR_SKIPNEXT */ - assert( sqlcipher_sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno ); - assert( sqlcipher_sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); - assert( sqlcipher_sqlite3PagerGetData(pPage->pDbPage) == data ); - assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - if( pBt->btsFlags & BTS_FAST_SECURE ){ - memset(&data[hdr], 0, pBt->usableSize - hdr); + assert( cursorOwnsBtShared(pCur) ); + assert( pBt->inTransaction==TRANS_WRITE ); + assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); + assert( pCur->curFlags & BTCF_WriteFlag ); + assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); + assert( !hasReadConflicts(p, pCur->pgnoRoot) ); + assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); + if( pCur->eState!=CURSOR_VALID ){ + if( pCur->eState>=CURSOR_REQUIRESEEK ){ + rc = btreeRestoreCursorPosition(pCur); + assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID ); + if( rc || pCur->eState!=CURSOR_VALID ) return rc; + }else{ + return SQLITE_CORRUPT_BKPT; + } } - data[hdr] = (char)flags; - first = hdr + ((flags&PTF_LEAF)==0 ? 12 : 8); - memset(&data[hdr+1], 0, 4); - data[hdr+7] = 0; - put2byte(&data[hdr+5], pBt->usableSize); - pPage->nFree = (u16)(pBt->usableSize - first); - decodeFlags(pPage, flags); - pPage->cellOffset = first; - pPage->aDataEnd = &data[pBt->usableSize]; - pPage->aCellIdx = &data[first]; - pPage->aDataOfst = &data[pPage->childPtrSize]; - pPage->nOverflow = 0; - assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); - pPage->maskPage = (u16)(pBt->pageSize - 1); - pPage->nCell = 0; - pPage->isInit = 1; -} + assert( pCur->eState==CURSOR_VALID ); + iCellDepth = pCur->iPage; + iCellIdx = pCur->ix; + pPage = pCur->pPage; + if( pPage->nCell<=iCellIdx ){ + return SQLITE_CORRUPT_BKPT; + } + pCell = findCell(pPage, iCellIdx); + if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){ + return SQLITE_CORRUPT_BKPT; + } -/* -** Convert a DbPage obtained from the pager into a MemPage used by -** the btree layer. -*/ -static MemPage *btreePageFromDbPage(DbPage *pDbPage, Pgno pgno, BtShared *pBt){ - MemPage *pPage = (MemPage*)sqlcipher_sqlite3PagerGetExtra(pDbPage); - if( pgno!=pPage->pgno ){ - pPage->aData = sqlcipher_sqlite3PagerGetData(pDbPage); - pPage->pDbPage = pDbPage; - pPage->pBt = pBt; - pPage->pgno = pgno; - pPage->hdrOffset = pgno==1 ? 100 : 0; + /* If the BTREE_SAVEPOSITION bit is on, then the cursor position must + ** be preserved following this delete operation. If the current delete + ** will cause a b-tree rebalance, then this is done by saving the cursor + ** key and leaving the cursor in CURSOR_REQUIRESEEK state before + ** returning. + ** + ** If the current delete will not cause a rebalance, then the cursor + ** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately + ** before or after the deleted entry. + ** + ** The bPreserve value records which path is required: + ** + ** bPreserve==0 Not necessary to save the cursor position + ** bPreserve==1 Use CURSOR_REQUIRESEEK to save the cursor position + ** bPreserve==2 Cursor won't move. Set CURSOR_SKIPNEXT. + */ + bPreserve = (flags & BTREE_SAVEPOSITION)!=0; + if( bPreserve ){ + if( !pPage->leaf + || (pPage->nFree+pPage->xCellSize(pPage,pCell)+2) > + (int)(pBt->usableSize*2/3) + || pPage->nCell==1 /* See dbfuzz001.test for a test case */ + ){ + /* A b-tree rebalance will be required after deleting this entry. + ** Save the cursor key. */ + rc = saveCursorKey(pCur); + if( rc ) return rc; + }else{ + bPreserve = 2; + } } - assert( pPage->aData==sqlcipher_sqlite3PagerGetData(pDbPage) ); - return pPage; -} -/* -** Get a page from the pager. Initialize the MemPage.pBt and -** MemPage.aData elements if needed. See also: btreeGetUnusedPage(). -** -** If the PAGER_GET_NOCONTENT flag is set, it means that we do not care -** about the content of the page at this time. So do not go to the disk -** to fetch the content. Just fill in the content with zeros for now. -** If in the future we call sqlcipher_sqlite3PagerWrite() on this page, that -** means we have started to be concerned about content and the disk -** read should occur at that point. -*/ -static int btreeGetPage( - BtShared *pBt, /* The btree */ - Pgno pgno, /* Number of the page to fetch */ - MemPage **ppPage, /* Return the page in this parameter */ - int flags /* PAGER_GET_NOCONTENT or PAGER_GET_READONLY */ -){ - int rc; - DbPage *pDbPage; + /* If the page containing the entry to delete is not a leaf page, move + ** the cursor to the largest entry in the tree that is smaller than + ** the entry being deleted. This cell will replace the cell being deleted + ** from the internal node. The 'previous' entry is used for this instead + ** of the 'next' entry, as the previous entry is always a part of the + ** sub-tree headed by the child page of the cell being deleted. This makes + ** balancing the tree following the delete operation easier. */ + if( !pPage->leaf ){ + rc = sqlcipher_sqlite3BtreePrevious(pCur, 0); + assert( rc!=SQLITE_DONE ); + if( rc ) return rc; + } - assert( flags==0 || flags==PAGER_GET_NOCONTENT || flags==PAGER_GET_READONLY ); - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - rc = sqlcipher_sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, flags); - if( rc ) return rc; - *ppPage = btreePageFromDbPage(pDbPage, pgno, pBt); - return SQLITE_OK; -} + /* Save the positions of any other cursors open on this table before + ** making any modifications. */ + if( pCur->curFlags & BTCF_Multiple ){ + rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); + if( rc ) return rc; + } -/* -** Retrieve a page from the pager cache. If the requested page is not -** already in the pager cache return NULL. Initialize the MemPage.pBt and -** MemPage.aData elements if needed. -*/ -static MemPage *btreePageLookup(BtShared *pBt, Pgno pgno){ - DbPage *pDbPage; - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - pDbPage = sqlcipher_sqlite3PagerLookup(pBt->pPager, pgno); - if( pDbPage ){ - return btreePageFromDbPage(pDbPage, pgno, pBt); + /* If this is a delete operation to remove a row from a table b-tree, + ** invalidate any incrblob cursors open on the row being deleted. */ + if( pCur->pKeyInfo==0 && p->hasIncrblobCur ){ + invalidateIncrblobCursors(p, pCur->pgnoRoot, pCur->info.nKey, 0); } - return 0; -} -/* -** Return the size of the database file in pages. If there is any kind of -** error, return ((unsigned int)-1). -*/ -static Pgno btreePagecount(BtShared *pBt){ - return pBt->nPage; -} -SQLITE_PRIVATE Pgno sqlcipher_sqlite3BtreeLastPage(Btree *p){ - assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); - return btreePagecount(p->pBt); -} + /* Make the page containing the entry to be deleted writable. Then free any + ** overflow pages associated with the entry and finally remove the cell + ** itself from within the page. */ + rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage); + if( rc ) return rc; + BTREE_CLEAR_CELL(rc, pPage, pCell, info); + dropCell(pPage, iCellIdx, info.nSize, &rc); + if( rc ) return rc; -/* -** Get a page from the pager and initialize it. -** -** If pCur!=0 then the page is being fetched as part of a moveToChild() -** call. Do additional sanity checking on the page in this case. -** And if the fetch fails, this routine must decrement pCur->iPage. -** -** The page is fetched as read-write unless pCur is not NULL and is -** a read-only cursor. -** -** If an error occurs, then *ppPage is undefined. It -** may remain unchanged, or it may be set to an invalid value. -*/ -static int getAndInitPage( - BtShared *pBt, /* The database file */ - Pgno pgno, /* Number of the page to get */ - MemPage **ppPage, /* Write the page pointer here */ - BtCursor *pCur, /* Cursor to receive the page, or NULL */ - int bReadOnly /* True for a read-only page */ -){ - int rc; - DbPage *pDbPage; - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - assert( pCur==0 || ppPage==&pCur->pPage ); - assert( pCur==0 || bReadOnly==pCur->curPagerFlags ); - assert( pCur==0 || pCur->iPage>0 ); + /* If the cell deleted was not located on a leaf page, then the cursor + ** is currently pointing to the largest entry in the sub-tree headed + ** by the child-page of the cell that was just deleted from an internal + ** node. The cell from the leaf node needs to be moved to the internal + ** node to replace the deleted cell. */ + if( !pPage->leaf ){ + MemPage *pLeaf = pCur->pPage; + int nCell; + Pgno n; + unsigned char *pTmp; - if( pgno>btreePagecount(pBt) ){ - rc = SQLITE_CORRUPT_BKPT; - goto getAndInitPage_error1; - } - rc = sqlcipher_sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly); - if( rc ){ - goto getAndInitPage_error1; - } - *ppPage = (MemPage*)sqlcipher_sqlite3PagerGetExtra(pDbPage); - if( (*ppPage)->isInit==0 ){ - btreePageFromDbPage(pDbPage, pgno, pBt); - rc = btreeInitPage(*ppPage); - if( rc!=SQLITE_OK ){ - goto getAndInitPage_error2; + if( pLeaf->nFree<0 ){ + rc = btreeComputeFreeSpace(pLeaf); + if( rc ) return rc; + } + if( iCellDepthiPage-1 ){ + n = pCur->apPage[iCellDepth+1]->pgno; + }else{ + n = pCur->pPage->pgno; + } + pCell = findCell(pLeaf, pLeaf->nCell-1); + if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT; + nCell = pLeaf->xCellSize(pLeaf, pCell); + assert( MX_CELL_SIZE(pBt) >= nCell ); + pTmp = pBt->pTmpSpace; + assert( pTmp!=0 ); + rc = sqlcipher_sqlite3PagerWrite(pLeaf->pDbPage); + if( rc==SQLITE_OK ){ + insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); } + dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc); + if( rc ) return rc; } - assert( (*ppPage)->pgno==pgno ); - assert( (*ppPage)->aData==sqlcipher_sqlite3PagerGetData(pDbPage) ); - /* If obtaining a child page for a cursor, we must verify that the page is - ** compatible with the root page. */ - if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){ - rc = SQLITE_CORRUPT_PGNO(pgno); - goto getAndInitPage_error2; + /* Balance the tree. If the entry deleted was located on a leaf page, + ** then the cursor still points to that page. In this case the first + ** call to balance() repairs the tree, and the if(...) condition is + ** never true. + ** + ** Otherwise, if the entry deleted was on an internal node page, then + ** pCur is pointing to the leaf page from which a cell was removed to + ** replace the cell deleted from the internal node. This is slightly + ** tricky as the leaf node may be underfull, and the internal node may + ** be either under or overfull. In this case run the balancing algorithm + ** on the leaf node first. If the balance proceeds far enough up the + ** tree that we can be sure that any problem in the internal node has + ** been corrected, so be it. Otherwise, after balancing the leaf node, + ** walk the cursor up the tree to the internal node and balance it as + ** well. */ + assert( pCur->pPage->nOverflow==0 ); + assert( pCur->pPage->nFree>=0 ); + if( pCur->pPage->nFree*3<=(int)pCur->pBt->usableSize*2 ){ + /* Optimization: If the free space is less than 2/3rds of the page, + ** then balance() will always be a no-op. No need to invoke it. */ + rc = SQLITE_OK; + }else{ + rc = balance(pCur); } - return SQLITE_OK; - -getAndInitPage_error2: - releasePage(*ppPage); -getAndInitPage_error1: - if( pCur ){ + if( rc==SQLITE_OK && pCur->iPage>iCellDepth ){ + releasePageNotNull(pCur->pPage); pCur->iPage--; + while( pCur->iPage>iCellDepth ){ + releasePage(pCur->apPage[pCur->iPage--]); + } pCur->pPage = pCur->apPage[pCur->iPage]; + rc = balance(pCur); } - testcase( pgno==0 ); - assert( pgno!=0 || rc==SQLITE_CORRUPT ); - return rc; -} - -/* -** Release a MemPage. This should be called once for each prior -** call to btreeGetPage. -** -** Page1 is a special case and must be released using releasePageOne(). -*/ -static void releasePageNotNull(MemPage *pPage){ - assert( pPage->aData ); - assert( pPage->pBt ); - assert( pPage->pDbPage!=0 ); - assert( sqlcipher_sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); - assert( sqlcipher_sqlite3PagerGetData(pPage->pDbPage)==pPage->aData ); - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - sqlcipher_sqlite3PagerUnrefNotNull(pPage->pDbPage); -} -static void releasePage(MemPage *pPage){ - if( pPage ) releasePageNotNull(pPage); -} -static void releasePageOne(MemPage *pPage){ - assert( pPage!=0 ); - assert( pPage->aData ); - assert( pPage->pBt ); - assert( pPage->pDbPage!=0 ); - assert( sqlcipher_sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); - assert( sqlcipher_sqlite3PagerGetData(pPage->pDbPage)==pPage->aData ); - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - sqlcipher_sqlite3PagerUnrefPageOne(pPage->pDbPage); -} -/* -** Get an unused page. -** -** This works just like btreeGetPage() with the addition: -** -** * If the page is already in use for some other purpose, immediately -** release it and return an SQLITE_CURRUPT error. -** * Make sure the isInit flag is clear -*/ -static int btreeGetUnusedPage( - BtShared *pBt, /* The btree */ - Pgno pgno, /* Number of the page to fetch */ - MemPage **ppPage, /* Return the page in this parameter */ - int flags /* PAGER_GET_NOCONTENT or PAGER_GET_READONLY */ -){ - int rc = btreeGetPage(pBt, pgno, ppPage, flags); if( rc==SQLITE_OK ){ - if( sqlcipher_sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){ - releasePage(*ppPage); - *ppPage = 0; - return SQLITE_CORRUPT_BKPT; + if( bPreserve>1 ){ + assert( (pCur->iPage==iCellDepth || CORRUPT_DB) ); + assert( pPage==pCur->pPage || CORRUPT_DB ); + assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell ); + pCur->eState = CURSOR_SKIPNEXT; + if( iCellIdx>=pPage->nCell ){ + pCur->skipNext = -1; + pCur->ix = pPage->nCell-1; + }else{ + pCur->skipNext = 1; + } + }else{ + rc = moveToRoot(pCur); + if( bPreserve ){ + btreeReleaseAllCursorPages(pCur); + pCur->eState = CURSOR_REQUIRESEEK; + } + if( rc==SQLITE_EMPTY ) rc = SQLITE_OK; } - (*ppPage)->isInit = 0; - }else{ - *ppPage = 0; } return rc; } - -/* -** During a rollback, when the pager reloads information into the cache -** so that the cache is restored to its original state at the start of -** the transaction, for each page restored this routine is called. -** -** This routine needs to reset the extra data section at the end of the -** page to agree with the restored data. -*/ -static void pageReinit(DbPage *pData){ - MemPage *pPage; - pPage = (MemPage *)sqlcipher_sqlite3PagerGetExtra(pData); - assert( sqlcipher_sqlite3PagerPageRefcount(pData)>0 ); - if( pPage->isInit ){ - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - pPage->isInit = 0; - if( sqlcipher_sqlite3PagerPageRefcount(pData)>1 ){ - /* pPage might not be a btree page; it might be an overflow page - ** or ptrmap page or a free page. In those cases, the following - ** call to btreeInitPage() will likely return SQLITE_CORRUPT. - ** But no harm is done by this. And it is very important that - ** btreeInitPage() be called on every btree page so we make - ** the call for every page that comes in for re-initing. */ - btreeInitPage(pPage); - } - } -} - -/* -** Invoke the busy handler for a btree. -*/ -static int btreeInvokeBusyHandler(void *pArg){ - BtShared *pBt = (BtShared*)pArg; - assert( pBt->db ); - assert( sqlcipher_sqlite3_mutex_held(pBt->db->mutex) ); - return sqlcipher_sqlite3InvokeBusyHandler(&pBt->db->busyHandler); -} - /* -** Open a database file. -** -** zFilename is the name of the database file. If zFilename is NULL -** then an ephemeral database is created. The ephemeral database might -** be exclusively in memory, or it might use a disk-based memory cache. -** Either way, the ephemeral database will be automatically deleted -** when sqlcipher_sqlite3BtreeClose() is called. -** -** If zFilename is ":memory:" then an in-memory database is created -** that is automatically destroyed when it is closed. +** Create a new BTree table. Write into *piTable the page +** number for the root page of the new table. ** -** The "flags" parameter is a bitmask that might contain bits like -** BTREE_OMIT_JOURNAL and/or BTREE_MEMORY. +** The type of type is determined by the flags parameter. Only the +** following values of flags are currently in use. Other values for +** flags might not work: ** -** If the database is already opened in the same database connection -** and we are in shared cache mode, then the open will fail with an -** SQLITE_CONSTRAINT error. We cannot allow two or more BtShared -** objects in the same database connection since doing so will lead -** to problems with locking. +** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys +** BTREE_ZERODATA Used for SQL indices */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeOpen( - sqlcipher_sqlite3_vfs *pVfs, /* VFS to use for this b-tree */ - const char *zFilename, /* Name of the file containing the BTree database */ - sqlcipher_sqlite3 *db, /* Associated database handle */ - Btree **ppBtree, /* Pointer to new Btree object written here */ - int flags, /* Options */ - int vfsFlags /* Flags passed through to sqlcipher_sqlite3_vfs.xOpen() */ -){ - BtShared *pBt = 0; /* Shared part of btree structure */ - Btree *p; /* Handle to return */ - sqlcipher_sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */ - int rc = SQLITE_OK; /* Result code from this function */ - u8 nReserve; /* Byte of unused space on each page */ - unsigned char zDbHeader[100]; /* Database header content */ - - /* True if opening an ephemeral, temporary database */ - const int isTempDb = zFilename==0 || zFilename[0]==0; - - /* Set the variable isMemdb to true for an in-memory database, or - ** false for a file-based database. - */ -#ifdef SQLITE_OMIT_MEMORYDB - const int isMemdb = 0; -#else - const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0) - || (isTempDb && sqlcipher_sqlite3TempInMemory(db)) - || (vfsFlags & SQLITE_OPEN_MEMORY)!=0; -#endif - - assert( db!=0 ); - assert( pVfs!=0 ); - assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - assert( (flags&0xff)==flags ); /* flags fit in 8 bits */ - - /* Only a BTREE_SINGLE database can be BTREE_UNORDERED */ - assert( (flags & BTREE_UNORDERED)==0 || (flags & BTREE_SINGLE)!=0 ); +static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ + BtShared *pBt = p->pBt; + MemPage *pRoot; + Pgno pgnoRoot; + int rc; + int ptfFlags; /* Page-type flage for the root page of new table */ - /* A BTREE_SINGLE database is always a temporary and/or ephemeral */ - assert( (flags & BTREE_SINGLE)==0 || isTempDb ); + assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); + assert( pBt->inTransaction==TRANS_WRITE ); + assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); - if( isMemdb ){ - flags |= BTREE_MEMORY; - } - if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){ - vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB; - } - p = sqlcipher_sqlite3MallocZero(sizeof(Btree)); - if( !p ){ - return SQLITE_NOMEM_BKPT; +#ifdef SQLITE_OMIT_AUTOVACUUM + rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); + if( rc ){ + return rc; } - p->inTrans = TRANS_NONE; - p->db = db; -#ifndef SQLITE_OMIT_SHARED_CACHE - p->lock.pBtree = p; - p->lock.iTable = 1; -#endif - -#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - /* - ** If this Btree is a candidate for shared cache, try to find an - ** existing BtShared object that we can share with - */ - if( isTempDb==0 && (isMemdb==0 || (vfsFlags&SQLITE_OPEN_URI)!=0) ){ - if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ - int nFilename = sqlcipher_sqlite3Strlen30(zFilename)+1; - int nFullPathname = pVfs->mxPathname+1; - char *zFullPathname = sqlcipher_sqlite3Malloc(MAX(nFullPathname,nFilename)); - MUTEX_LOGIC( sqlcipher_sqlite3_mutex *mutexShared; ) +#else + if( pBt->autoVacuum ){ + Pgno pgnoMove; /* Move a page here to make room for the root-page */ + MemPage *pPageMove; /* The page to move to. */ - p->sharable = 1; - if( !zFullPathname ){ - sqlcipher_sqlite3_free(p); - return SQLITE_NOMEM_BKPT; - } - if( isMemdb ){ - memcpy(zFullPathname, zFilename, nFilename); - }else{ - rc = sqlcipher_sqlite3OsFullPathname(pVfs, zFilename, - nFullPathname, zFullPathname); - if( rc ){ - if( rc==SQLITE_OK_SYMLINK ){ - rc = SQLITE_OK; - }else{ - sqlcipher_sqlite3_free(zFullPathname); - sqlcipher_sqlite3_free(p); - return rc; - } - } - } -#if SQLITE_THREADSAFE - mutexOpen = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN); - sqlcipher_sqlite3_mutex_enter(mutexOpen); - mutexShared = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); - sqlcipher_sqlite3_mutex_enter(mutexShared); -#endif - for(pBt=GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList); pBt; pBt=pBt->pNext){ - assert( pBt->nRef>0 ); - if( 0==strcmp(zFullPathname, sqlcipher_sqlite3PagerFilename(pBt->pPager, 0)) - && sqlcipher_sqlite3PagerVfs(pBt->pPager)==pVfs ){ - int iDb; - for(iDb=db->nDb-1; iDb>=0; iDb--){ - Btree *pExisting = db->aDb[iDb].pBt; - if( pExisting && pExisting->pBt==pBt ){ - sqlcipher_sqlite3_mutex_leave(mutexShared); - sqlcipher_sqlite3_mutex_leave(mutexOpen); - sqlcipher_sqlite3_free(zFullPathname); - sqlcipher_sqlite3_free(p); - return SQLITE_CONSTRAINT; - } - } - p->pBt = pBt; - pBt->nRef++; - break; - } - } - sqlcipher_sqlite3_mutex_leave(mutexShared); - sqlcipher_sqlite3_free(zFullPathname); - } -#ifdef SQLITE_DEBUG - else{ - /* In debug mode, we mark all persistent databases as sharable - ** even when they are not. This exercises the locking code and - ** gives more opportunity for asserts(sqlcipher_sqlite3_mutex_held()) - ** statements to find locking problems. - */ - p->sharable = 1; - } -#endif - } -#endif - if( pBt==0 ){ - /* - ** The following asserts make sure that structures used by the btree are - ** the right size. This is to guard against size changes that result - ** when compiling on a different architecture. + /* Creating a new table may probably require moving an existing database + ** to make room for the new tables root page. In case this page turns + ** out to be an overflow page, delete all overflow page-map caches + ** held by open cursors. */ - assert( sizeof(i64)==8 ); - assert( sizeof(u64)==8 ); - assert( sizeof(u32)==4 ); - assert( sizeof(u16)==2 ); - assert( sizeof(Pgno)==4 ); + invalidateAllOverflowCache(pBt); - pBt = sqlcipher_sqlite3MallocZero( sizeof(*pBt) ); - if( pBt==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto btree_open_out; + /* Read the value of meta[3] from the database to determine where the + ** root page of the new table should go. meta[3] is the largest root-page + ** created so far, so the new root-page is (meta[3]+1). + */ + sqlcipher_sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); + if( pgnoRoot>btreePagecount(pBt) ){ + return SQLITE_CORRUPT_BKPT; } - rc = sqlcipher_sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, - sizeof(MemPage), flags, vfsFlags, pageReinit); - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3PagerSetMmapLimit(pBt->pPager, db->szMmap); - rc = sqlcipher_sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); + pgnoRoot++; + + /* The new root-page may not be allocated on a pointer-map page, or the + ** PENDING_BYTE page. + */ + while( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || + pgnoRoot==PENDING_BYTE_PAGE(pBt) ){ + pgnoRoot++; } + assert( pgnoRoot>=3 ); + + /* Allocate a page. The page that currently resides at pgnoRoot will + ** be moved to the allocated page (unless the allocated page happens + ** to reside at pgnoRoot). + */ + rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, BTALLOC_EXACT); if( rc!=SQLITE_OK ){ - goto btree_open_out; + return rc; } - pBt->openFlags = (u8)flags; - pBt->db = db; - sqlcipher_sqlite3PagerSetBusyHandler(pBt->pPager, btreeInvokeBusyHandler, pBt); - p->pBt = pBt; - pBt->pCursor = 0; - pBt->pPage1 = 0; - if( sqlcipher_sqlite3PagerIsreadonly(pBt->pPager) ) pBt->btsFlags |= BTS_READ_ONLY; -#if defined(SQLITE_SECURE_DELETE) - pBt->btsFlags |= BTS_SECURE_DELETE; -#elif defined(SQLITE_FAST_SECURE_DELETE) - pBt->btsFlags |= BTS_OVERWRITE; -#endif - /* EVIDENCE-OF: R-51873-39618 The page size for a database file is - ** determined by the 2-byte integer located at an offset of 16 bytes from - ** the beginning of the database file. */ - pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16); - if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE - || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){ - pBt->pageSize = 0; -#ifndef SQLITE_OMIT_AUTOVACUUM - /* If the magic name ":memory:" will create an in-memory database, then - ** leave the autoVacuum mode at 0 (do not auto-vacuum), even if - ** SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if - ** SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a - ** regular file-name. In this case the auto-vacuum applies as per normal. + if( pgnoMove!=pgnoRoot ){ + /* pgnoRoot is the page that will be used for the root-page of + ** the new table (assuming an error did not occur). But we were + ** allocated pgnoMove. If required (i.e. if it was not allocated + ** by extending the file), the current page at position pgnoMove + ** is already journaled. */ - if( zFilename && !isMemdb ){ - pBt->autoVacuum = (SQLITE_DEFAULT_AUTOVACUUM ? 1 : 0); - pBt->incrVacuum = (SQLITE_DEFAULT_AUTOVACUUM==2 ? 1 : 0); + u8 eType = 0; + Pgno iPtrPage = 0; + + /* Save the positions of any open cursors. This is required in + ** case they are holding a reference to an xFetch reference + ** corresponding to page pgnoRoot. */ + rc = saveAllCursors(pBt, 0, 0); + releasePage(pPageMove); + if( rc!=SQLITE_OK ){ + return rc; } -#endif - nReserve = 0; - }else{ - /* EVIDENCE-OF: R-37497-42412 The size of the reserved region is - ** determined by the one-byte unsigned integer found at an offset of 20 - ** into the database file header. */ - nReserve = zDbHeader[20]; - pBt->btsFlags |= BTS_PAGESIZE_FIXED; -#ifndef SQLITE_OMIT_AUTOVACUUM - pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0); - pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0); -#endif - } - rc = sqlcipher_sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve); - if( rc ) goto btree_open_out; - pBt->usableSize = pBt->pageSize - nReserve; - assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */ -#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - /* Add the new BtShared object to the linked list sharable BtShareds. - */ - pBt->nRef = 1; - if( p->sharable ){ - MUTEX_LOGIC( sqlcipher_sqlite3_mutex *mutexShared; ) - MUTEX_LOGIC( mutexShared = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);) - if( SQLITE_THREADSAFE && sqlcipher_sqlite3GlobalConfig.bCoreMutex ){ - pBt->mutex = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_FAST); - if( pBt->mutex==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto btree_open_out; - } + /* Move the page currently at pgnoRoot to pgnoMove. */ + rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); + if( rc!=SQLITE_OK ){ + return rc; } - sqlcipher_sqlite3_mutex_enter(mutexShared); - pBt->pNext = GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList); - GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList) = pBt; - sqlcipher_sqlite3_mutex_leave(mutexShared); - } -#endif - } + rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); + if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ + rc = SQLITE_CORRUPT_BKPT; + } + if( rc!=SQLITE_OK ){ + releasePage(pRoot); + return rc; + } + assert( eType!=PTRMAP_ROOTPAGE ); + assert( eType!=PTRMAP_FREEPAGE ); + rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove, 0); + releasePage(pRoot); -#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - /* If the new Btree uses a sharable pBtShared, then link the new - ** Btree into the list of all sharable Btrees for the same connection. - ** The list is kept in ascending order by pBt address. - */ - if( p->sharable ){ - int i; - Btree *pSib; - for(i=0; inDb; i++){ - if( (pSib = db->aDb[i].pBt)!=0 && pSib->sharable ){ - while( pSib->pPrev ){ pSib = pSib->pPrev; } - if( (uptr)p->pBt<(uptr)pSib->pBt ){ - p->pNext = pSib; - p->pPrev = 0; - pSib->pPrev = p; - }else{ - while( pSib->pNext && (uptr)pSib->pNext->pBt<(uptr)p->pBt ){ - pSib = pSib->pNext; - } - p->pNext = pSib->pNext; - p->pPrev = pSib; - if( p->pNext ){ - p->pNext->pPrev = p; - } - pSib->pNext = p; - } - break; + /* Obtain the page at pgnoRoot */ + if( rc!=SQLITE_OK ){ + return rc; + } + rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = sqlcipher_sqlite3PagerWrite(pRoot->pDbPage); + if( rc!=SQLITE_OK ){ + releasePage(pRoot); + return rc; } + }else{ + pRoot = pPageMove; } - } -#endif - *ppBtree = p; -btree_open_out: - if( rc!=SQLITE_OK ){ - if( pBt && pBt->pPager ){ - sqlcipher_sqlite3PagerClose(pBt->pPager, 0); + /* Update the pointer-map and meta-data with the new root-page number. */ + ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0, &rc); + if( rc ){ + releasePage(pRoot); + return rc; } - sqlcipher_sqlite3_free(pBt); - sqlcipher_sqlite3_free(p); - *ppBtree = 0; - }else{ - sqlcipher_sqlite3_file *pFile; - /* If the B-Tree was successfully opened, set the pager-cache size to the - ** default value. Except, when opening on an existing shared pager-cache, - ** do not change the pager-cache size. + /* When the new root page was allocated, page 1 was made writable in + ** order either to increase the database filesize, or to decrement the + ** freelist count. Hence, the sqlcipher_sqlite3BtreeUpdateMeta() call cannot fail. */ - if( sqlcipher_sqlite3BtreeSchema(p, 0, 0)==0 ){ - sqlcipher_sqlite3BtreeSetCacheSize(p, SQLITE_DEFAULT_CACHE_SIZE); + assert( sqlcipher_sqlite3PagerIswriteable(pBt->pPage1->pDbPage) ); + rc = sqlcipher_sqlite3BtreeUpdateMeta(p, 4, pgnoRoot); + if( NEVER(rc) ){ + releasePage(pRoot); + return rc; } - pFile = sqlcipher_sqlite3PagerFile(pBt->pPager); - if( pFile->pMethods ){ - sqlcipher_sqlite3OsFileControlHint(pFile, SQLITE_FCNTL_PDB, (void*)&pBt->db); - } + }else{ + rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); + if( rc ) return rc; } - if( mutexOpen ){ - assert( sqlcipher_sqlite3_mutex_held(mutexOpen) ); - sqlcipher_sqlite3_mutex_leave(mutexOpen); +#endif + assert( sqlcipher_sqlite3PagerIswriteable(pRoot->pDbPage) ); + if( createTabFlags & BTREE_INTKEY ){ + ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF; + }else{ + ptfFlags = PTF_ZERODATA | PTF_LEAF; } - assert( rc!=SQLITE_OK || sqlcipher_sqlite3BtreeConnectionCount(*ppBtree)>0 ); + zeroPage(pRoot, ptfFlags); + sqlcipher_sqlite3PagerUnref(pRoot->pDbPage); + assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); + *piTable = pgnoRoot; + return SQLITE_OK; +} +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCreateTable(Btree *p, Pgno *piTable, int flags){ + int rc; + sqlcipher_sqlite3BtreeEnter(p); + rc = btreeCreateTable(p, piTable, flags); + sqlcipher_sqlite3BtreeLeave(p); return rc; } /* -** Decrement the BtShared.nRef counter. When it reaches zero, -** remove the BtShared structure from the sharing list. Return -** true if the BtShared.nRef counter reaches zero and return -** false if it is still positive. +** Erase the given database page and all its children. Return +** the page to the freelist. */ -static int removeFromSharingList(BtShared *pBt){ -#ifndef SQLITE_OMIT_SHARED_CACHE - MUTEX_LOGIC( sqlcipher_sqlite3_mutex *pMainMtx; ) - BtShared *pList; - int removed = 0; +static int clearDatabasePage( + BtShared *pBt, /* The BTree that contains the table */ + Pgno pgno, /* Page number to clear */ + int freePageFlag, /* Deallocate page if true */ + i64 *pnChange /* Add number of Cells freed to this counter */ +){ + MemPage *pPage; + int rc; + unsigned char *pCell; + int i; + int hdr; + CellInfo info; - assert( sqlcipher_sqlite3_mutex_notheld(pBt->mutex) ); - MUTEX_LOGIC( pMainMtx = sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) - sqlcipher_sqlite3_mutex_enter(pMainMtx); - pBt->nRef--; - if( pBt->nRef<=0 ){ - if( GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList)==pBt ){ - GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList) = pBt->pNext; - }else{ - pList = GLOBAL(BtShared*,sqlcipher_sqlite3SharedCacheList); - while( ALWAYS(pList) && pList->pNext!=pBt ){ - pList=pList->pNext; - } - if( ALWAYS(pList) ){ - pList->pNext = pBt->pNext; - } - } - if( SQLITE_THREADSAFE ){ - sqlcipher_sqlite3_mutex_free(pBt->mutex); - } - removed = 1; + assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); + if( pgno>btreePagecount(pBt) ){ + return SQLITE_CORRUPT_BKPT; } - sqlcipher_sqlite3_mutex_leave(pMainMtx); - return removed; -#else - return 1; -#endif -} - -/* -** Make sure pBt->pTmpSpace points to an allocation of -** MX_CELL_SIZE(pBt) bytes with a 4-byte prefix for a left-child -** pointer. -*/ -static void allocateTempSpace(BtShared *pBt){ - if( !pBt->pTmpSpace ){ - pBt->pTmpSpace = sqlcipher_sqlite3PageMalloc( pBt->pageSize ); - - /* One of the uses of pBt->pTmpSpace is to format cells before - ** inserting them into a leaf page (function fillInCell()). If - ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes - ** by the various routines that manipulate binary cells. Which - ** can mean that fillInCell() only initializes the first 2 or 3 - ** bytes of pTmpSpace, but that the first 4 bytes are copied from - ** it into a database page. This is not actually a problem, but it - ** does cause a valgrind error when the 1 or 2 bytes of unitialized - ** data is passed to system call write(). So to avoid this error, - ** zero the first 4 bytes of temp space here. - ** - ** Also: Provide four bytes of initialized space before the - ** beginning of pTmpSpace as an area available to prepend the - ** left-child pointer to the beginning of a cell. - */ - if( pBt->pTmpSpace ){ - memset(pBt->pTmpSpace, 0, 8); - pBt->pTmpSpace += 4; + rc = getAndInitPage(pBt, pgno, &pPage, 0, 0); + if( rc ) return rc; + if( (pBt->openFlags & BTREE_SINGLE)==0 + && sqlcipher_sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1)) + ){ + rc = SQLITE_CORRUPT_BKPT; + goto cleardatabasepage_out; + } + hdr = pPage->hdrOffset; + for(i=0; inCell; i++){ + pCell = findCell(pPage, i); + if( !pPage->leaf ){ + rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange); + if( rc ) goto cleardatabasepage_out; } + BTREE_CLEAR_CELL(rc, pPage, pCell, info); + if( rc ) goto cleardatabasepage_out; } -} - -/* -** Free the pBt->pTmpSpace allocation -*/ -static void freeTempSpace(BtShared *pBt){ - if( pBt->pTmpSpace ){ - pBt->pTmpSpace -= 4; - sqlcipher_sqlite3PageFree(pBt->pTmpSpace); - pBt->pTmpSpace = 0; + if( !pPage->leaf ){ + rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange); + if( rc ) goto cleardatabasepage_out; + if( pPage->intKey ) pnChange = 0; } -} - -/* -** Close an open database and invalidate all cursors. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeClose(Btree *p){ - BtShared *pBt = p->pBt; - BtCursor *pCur; - - /* Close all cursors opened via this handle. */ - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - sqlcipher_sqlite3BtreeEnter(p); - pCur = pBt->pCursor; - while( pCur ){ - BtCursor *pTmp = pCur; - pCur = pCur->pNext; - if( pTmp->pBtree==p ){ - sqlcipher_sqlite3BtreeCloseCursor(pTmp); - } + if( pnChange ){ + testcase( !pPage->intKey ); + *pnChange += pPage->nCell; } - - /* Rollback any active transaction and free the handle structure. - ** The call to sqlcipher_sqlite3BtreeRollback() drops any table-locks held by - ** this handle. - */ - sqlcipher_sqlite3BtreeRollback(p, SQLITE_OK, 0); - sqlcipher_sqlite3BtreeLeave(p); - - /* If there are still other outstanding references to the shared-btree - ** structure, return now. The remainder of this procedure cleans - ** up the shared-btree. - */ - assert( p->wantToLock==0 && p->locked==0 ); - if( !p->sharable || removeFromSharingList(pBt) ){ - /* The pBt is no longer on the sharing list, so we can access - ** it without having to hold the mutex. - ** - ** Clean out and delete the BtShared object. - */ - assert( !pBt->pCursor ); - sqlcipher_sqlite3PagerClose(pBt->pPager, p->db); - if( pBt->xFreeSchema && pBt->pSchema ){ - pBt->xFreeSchema(pBt->pSchema); - } - sqlcipher_sqlite3DbFree(0, pBt->pSchema); - freeTempSpace(pBt); - sqlcipher_sqlite3_free(pBt); + if( freePageFlag ){ + freePage(pPage, &rc); + }else if( (rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage))==0 ){ + zeroPage(pPage, pPage->aData[hdr] | PTF_LEAF); } -#ifndef SQLITE_OMIT_SHARED_CACHE - assert( p->wantToLock==0 ); - assert( p->locked==0 ); - if( p->pPrev ) p->pPrev->pNext = p->pNext; - if( p->pNext ) p->pNext->pPrev = p->pPrev; -#endif - - sqlcipher_sqlite3_free(p); - return SQLITE_OK; -} - -/* -** Change the "soft" limit on the number of pages in the cache. -** Unused and unmodified pages will be recycled when the number of -** pages in the cache exceeds this soft limit. But the size of the -** cache is allowed to grow larger than this limit if it contains -** dirty pages or pages still in active use. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetCacheSize(Btree *p, int mxPage){ - BtShared *pBt = p->pBt; - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - sqlcipher_sqlite3BtreeEnter(p); - sqlcipher_sqlite3PagerSetCachesize(pBt->pPager, mxPage); - sqlcipher_sqlite3BtreeLeave(p); - return SQLITE_OK; +cleardatabasepage_out: + releasePage(pPage); + return rc; } /* -** Change the "spill" limit on the number of pages in the cache. -** If the number of pages exceeds this limit during a write transaction, -** the pager might attempt to "spill" pages to the journal early in -** order to free up memory. +** Delete all information from a single table in the database. iTable is +** the page number of the root of the table. After this routine returns, +** the root page is empty, but still exists. ** -** The value returned is the current spill size. If zero is passed -** as an argument, no changes are made to the spill size setting, so -** using mxPage of 0 is a way to query the current spill size. +** This routine will fail with SQLITE_LOCKED if there are any open +** read cursors on the table. Open write cursors are moved to the +** root of the table. +** +** If pnChange is not NULL, then the integer value pointed to by pnChange +** is incremented by the number of entries in the table. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetSpillSize(Btree *p, int mxPage){ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeClearTable(Btree *p, int iTable, i64 *pnChange){ + int rc; BtShared *pBt = p->pBt; - int res; - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); sqlcipher_sqlite3BtreeEnter(p); - res = sqlcipher_sqlite3PagerSetSpillsize(pBt->pPager, mxPage); - sqlcipher_sqlite3BtreeLeave(p); - return res; -} + assert( p->inTrans==TRANS_WRITE ); -#if SQLITE_MAX_MMAP_SIZE>0 -/* -** Change the limit on the amount of the database file that may be -** memory mapped. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetMmapLimit(Btree *p, sqlcipher_sqlite3_int64 szMmap){ - BtShared *pBt = p->pBt; - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - sqlcipher_sqlite3BtreeEnter(p); - sqlcipher_sqlite3PagerSetMmapLimit(pBt->pPager, szMmap); + rc = saveAllCursors(pBt, (Pgno)iTable, 0); + + if( SQLITE_OK==rc ){ + /* Invalidate all incrblob cursors open on table iTable (assuming iTable + ** is the root of a table b-tree - if it is not, the following call is + ** a no-op). */ + if( p->hasIncrblobCur ){ + invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1); + } + rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); + } sqlcipher_sqlite3BtreeLeave(p); - return SQLITE_OK; + return rc; } -#endif /* SQLITE_MAX_MMAP_SIZE>0 */ /* -** Change the way data is synced to disk in order to increase or decrease -** how well the database resists damage due to OS crashes and power -** failures. Level 1 is the same as asynchronous (no syncs() occur and -** there is a high probability of damage) Level 2 is the default. There -** is a very low but non-zero probability of damage. Level 3 reduces the -** probability of damage to near zero but with a write performance reduction. +** Delete all information from the single table that pCur is open on. +** +** This routine only work for pCur on an ephemeral table. */ -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetPagerFlags( - Btree *p, /* The btree to set the safety level on */ - unsigned pgFlags /* Various PAGER_* flags */ -){ - BtShared *pBt = p->pBt; - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - sqlcipher_sqlite3BtreeEnter(p); - sqlcipher_sqlite3PagerSetFlags(pBt->pPager, pgFlags); - sqlcipher_sqlite3BtreeLeave(p); - return SQLITE_OK; +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeClearTableOfCursor(BtCursor *pCur){ + return sqlcipher_sqlite3BtreeClearTable(pCur->pBtree, pCur->pgnoRoot, 0); } -#endif /* -** Change the default pages size and the number of reserved bytes per page. -** Or, if the page size has already been fixed, return SQLITE_READONLY -** without changing anything. -** -** The page size must be a power of 2 between 512 and 65536. If the page -** size supplied does not meet this constraint then the page size is not -** changed. -** -** Page sizes are constrained to be a power of two so that the region -** of the database file used for locking (beginning at PENDING_BYTE, -** the first byte past the 1GB boundary, 0x40000000) needs to occur -** at the beginning of a page. +** Erase all information in a table and add the root of the table to +** the freelist. Except, the root of the principle table (the one on +** page 1) is never added to the freelist. ** -** If parameter nReserve is less than zero, then the number of reserved -** bytes per page is left unchanged. +** This routine will fail with SQLITE_LOCKED if there are any open +** cursors on the table. ** -** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size -** and autovacuum mode can no longer be changed. +** If AUTOVACUUM is enabled and the page at iTable is not the last +** root page in the database file, then the last root page +** in the database file is moved into the slot formerly occupied by +** iTable and that last slot formerly occupied by the last root page +** is added to the freelist instead of iTable. In this say, all +** root pages are kept at the beginning of the database file, which +** is necessary for AUTOVACUUM to work right. *piMoved is set to the +** page number that used to be the last root page in the file before +** the move. If no page gets moved, *piMoved is set to 0. +** The last root page is recorded in meta[3] and the value of +** meta[3] is updated by this procedure. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){ - int rc = SQLITE_OK; - int x; +static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ + int rc; + MemPage *pPage = 0; BtShared *pBt = p->pBt; - assert( nReserve>=0 && nReserve<=255 ); - sqlcipher_sqlite3BtreeEnter(p); - pBt->nReserveWanted = nReserve; - x = pBt->pageSize - pBt->usableSize; - if( nReservebtsFlags & BTS_PAGESIZE_FIXED ){ - sqlcipher_sqlite3BtreeLeave(p); - return SQLITE_READONLY; + + assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); + assert( p->inTrans==TRANS_WRITE ); + assert( iTable>=2 ); + if( iTable>btreePagecount(pBt) ){ + return SQLITE_CORRUPT_BKPT; } - assert( nReserve>=0 && nReserve<=255 ); - if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && - ((pageSize-1)&pageSize)==0 ){ - assert( (pageSize & 7)==0 ); - assert( !pBt->pCursor ); - pBt->pageSize = (u32)pageSize; - freeTempSpace(pBt); + + rc = sqlcipher_sqlite3BtreeClearTable(p, iTable, 0); + if( rc ) return rc; + rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); + if( NEVER(rc) ){ + releasePage(pPage); + return rc; } - rc = sqlcipher_sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve); - pBt->usableSize = pBt->pageSize - (u16)nReserve; - if( iFix ) pBt->btsFlags |= BTS_PAGESIZE_FIXED; - sqlcipher_sqlite3BtreeLeave(p); - return rc; -} -/* -** Return the currently defined page size -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeGetPageSize(Btree *p){ - return p->pBt->pageSize; -} + *piMoved = 0; -/* -** This function is similar to sqlcipher_sqlite3BtreeGetReserve(), except that it -** may only be called if it is guaranteed that the b-tree mutex is already -** held. -** -** This is useful in one special case in the backup API code where it is -** known that the shared b-tree mutex is held, but the mutex on the -** database handle that owns *p is not. In this case if sqlcipher_sqlite3BtreeEnter() -** were to be called, it might collide with some other operation on the -** database handle that owns *p, causing undefined behavior. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeGetReserveNoMutex(Btree *p){ - int n; - assert( sqlcipher_sqlite3_mutex_held(p->pBt->mutex) ); - n = p->pBt->pageSize - p->pBt->usableSize; - return n; -} +#ifdef SQLITE_OMIT_AUTOVACUUM + freePage(pPage, &rc); + releasePage(pPage); +#else + if( pBt->autoVacuum ){ + Pgno maxRootPgno; + sqlcipher_sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); -/* -** Return the number of bytes of space at the end of every page that -** are intentually left unused. This is the "reserved" space that is -** sometimes used by extensions. -** -** The value returned is the larger of the current reserve size and -** the latest reserve size requested by SQLITE_FILECTRL_RESERVE_BYTES. -** The amount of reserve can only grow - never shrink. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeGetRequestedReserve(Btree *p){ - int n1, n2; - sqlcipher_sqlite3BtreeEnter(p); - n1 = (int)p->pBt->nReserveWanted; - n2 = sqlcipher_sqlite3BtreeGetReserveNoMutex(p); - sqlcipher_sqlite3BtreeLeave(p); - return n1>n2 ? n1 : n2; -} + if( iTable==maxRootPgno ){ + /* If the table being dropped is the table with the largest root-page + ** number in the database, put the root page on the free list. + */ + freePage(pPage, &rc); + releasePage(pPage); + if( rc!=SQLITE_OK ){ + return rc; + } + }else{ + /* The table being dropped does not have the largest root-page + ** number in the database. So move the page that does into the + ** gap left by the deleted root-page. + */ + MemPage *pMove; + releasePage(pPage); + rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0); + releasePage(pMove); + if( rc!=SQLITE_OK ){ + return rc; + } + pMove = 0; + rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); + freePage(pMove, &rc); + releasePage(pMove); + if( rc!=SQLITE_OK ){ + return rc; + } + *piMoved = maxRootPgno; + } + /* Set the new 'max-root-page' value in the database header. This + ** is the old value less one, less one more if that happens to + ** be a root-page number, less one again if that is the + ** PENDING_BYTE_PAGE. + */ + maxRootPgno--; + while( maxRootPgno==PENDING_BYTE_PAGE(pBt) + || PTRMAP_ISPAGE(pBt, maxRootPgno) ){ + maxRootPgno--; + } + assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) ); -/* -** Set the maximum page count for a database if mxPage is positive. -** No changes are made if mxPage is 0 or negative. -** Regardless of the value of mxPage, return the maximum page count. -*/ -SQLITE_PRIVATE Pgno sqlcipher_sqlite3BtreeMaxPageCount(Btree *p, Pgno mxPage){ - Pgno n; + rc = sqlcipher_sqlite3BtreeUpdateMeta(p, 4, maxRootPgno); + }else{ + freePage(pPage, &rc); + releasePage(pPage); + } +#endif + return rc; +} +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){ + int rc; sqlcipher_sqlite3BtreeEnter(p); - n = sqlcipher_sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage); + rc = btreeDropTable(p, iTable, piMoved); sqlcipher_sqlite3BtreeLeave(p); - return n; + return rc; } + /* -** Change the values for the BTS_SECURE_DELETE and BTS_OVERWRITE flags: -** -** newFlag==0 Both BTS_SECURE_DELETE and BTS_OVERWRITE are cleared -** newFlag==1 BTS_SECURE_DELETE set and BTS_OVERWRITE is cleared -** newFlag==2 BTS_SECURE_DELETE cleared and BTS_OVERWRITE is set -** newFlag==(-1) No changes +** This function may only be called if the b-tree connection already +** has a read or write transaction open on the database. ** -** This routine acts as a query if newFlag is less than zero +** Read the meta-information out of a database file. Meta[0] +** is the number of free pages currently in the database. Meta[1] +** through meta[15] are available for use by higher layers. Meta[0] +** is read-only, the others are read/write. ** -** With BTS_OVERWRITE set, deleted content is overwritten by zeros, but -** freelist leaf pages are not written back to the database. Thus in-page -** deleted content is cleared, but freelist deleted content is not. +** The schema layer numbers meta values differently. At the schema +** layer (and the SetCookie and ReadCookie opcodes) the number of +** free pages is not visible. So Cookie[0] is the same as Meta[1]. ** -** With BTS_SECURE_DELETE, operation is like BTS_OVERWRITE with the addition -** that freelist leaf pages are written back into the database, increasing -** the amount of disk I/O. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSecureDelete(Btree *p, int newFlag){ - int b; - if( p==0 ) return 0; - sqlcipher_sqlite3BtreeEnter(p); - assert( BTS_OVERWRITE==BTS_SECURE_DELETE*2 ); - assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) ); - if( newFlag>=0 ){ - p->pBt->btsFlags &= ~BTS_FAST_SECURE; - p->pBt->btsFlags |= BTS_SECURE_DELETE*newFlag; - } - b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE; - sqlcipher_sqlite3BtreeLeave(p); - return b; -} - -/* -** Change the 'auto-vacuum' property of the database. If the 'autoVacuum' -** parameter is non-zero, then auto-vacuum mode is enabled. If zero, it -** is disabled. The default value for the auto-vacuum property is -** determined by the SQLITE_DEFAULT_AUTOVACUUM macro. +** This routine treats Meta[BTREE_DATA_VERSION] as a special case. Instead +** of reading the value out of the header, it instead loads the "DataVersion" +** from the pager. The BTREE_DATA_VERSION value is not actually stored in the +** database file. It is a number computed by the pager. But its access +** pattern is the same as header meta values, and so it is convenient to +** read it from this routine. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){ -#ifdef SQLITE_OMIT_AUTOVACUUM - return SQLITE_READONLY; -#else +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ BtShared *pBt = p->pBt; - int rc = SQLITE_OK; - u8 av = (u8)autoVacuum; sqlcipher_sqlite3BtreeEnter(p); - if( (pBt->btsFlags & BTS_PAGESIZE_FIXED)!=0 && (av ?1:0)!=pBt->autoVacuum ){ - rc = SQLITE_READONLY; + assert( p->inTrans>TRANS_NONE ); + assert( SQLITE_OK==querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK) ); + assert( pBt->pPage1 ); + assert( idx>=0 && idx<=15 ); + + if( idx==BTREE_DATA_VERSION ){ + *pMeta = sqlcipher_sqlite3PagerDataVersion(pBt->pPager) + p->iBDataVersion; }else{ - pBt->autoVacuum = av ?1:0; - pBt->incrVacuum = av==2 ?1:0; + *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); } - sqlcipher_sqlite3BtreeLeave(p); - return rc; -#endif -} -/* -** Return the value of the 'auto-vacuum' property. If auto-vacuum is -** enabled 1 is returned. Otherwise 0. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeGetAutoVacuum(Btree *p){ + /* If auto-vacuum is disabled in this build and this is an auto-vacuum + ** database, mark the database as read-only. */ #ifdef SQLITE_OMIT_AUTOVACUUM - return BTREE_AUTOVACUUM_NONE; -#else - int rc; - sqlcipher_sqlite3BtreeEnter(p); - rc = ( - (!p->pBt->autoVacuum)?BTREE_AUTOVACUUM_NONE: - (!p->pBt->incrVacuum)?BTREE_AUTOVACUUM_FULL: - BTREE_AUTOVACUUM_INCR - ); - sqlcipher_sqlite3BtreeLeave(p); - return rc; + if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){ + pBt->btsFlags |= BTS_READ_ONLY; + } #endif + + sqlcipher_sqlite3BtreeLeave(p); } /* -** If the user has not set the safety-level for this database connection -** using "PRAGMA synchronous", and if the safety-level is not already -** set to the value passed to this function as the second parameter, -** set it so. +** Write meta-information back into the database. Meta[0] is +** read-only and may not be written. */ -#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS \ - && !defined(SQLITE_OMIT_WAL) -static void setDefaultSyncFlag(BtShared *pBt, u8 safety_level){ - sqlcipher_sqlite3 *db; - Db *pDb; - if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){ - while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; } - if( pDb->bSyncSet==0 - && pDb->safety_level!=safety_level - && pDb!=&db->aDb[1] - ){ - pDb->safety_level = safety_level; - sqlcipher_sqlite3PagerSetFlags(pBt->pPager, - pDb->safety_level | (db->flags & PAGER_FLAGS_MASK)); +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){ + BtShared *pBt = p->pBt; + unsigned char *pP1; + int rc; + assert( idx>=1 && idx<=15 ); + sqlcipher_sqlite3BtreeEnter(p); + assert( p->inTrans==TRANS_WRITE ); + assert( pBt->pPage1!=0 ); + pP1 = pBt->pPage1->aData; + rc = sqlcipher_sqlite3PagerWrite(pBt->pPage1->pDbPage); + if( rc==SQLITE_OK ){ + put4byte(&pP1[36 + idx*4], iMeta); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( idx==BTREE_INCR_VACUUM ){ + assert( pBt->autoVacuum || iMeta==0 ); + assert( iMeta==0 || iMeta==1 ); + pBt->incrVacuum = (u8)iMeta; } +#endif } + sqlcipher_sqlite3BtreeLeave(p); + return rc; } -#else -# define setDefaultSyncFlag(pBt,safety_level) -#endif - -/* Forward declaration */ -static int newDatabase(BtShared*); - /* -** Get a reference to pPage1 of the database file. This will -** also acquire a readlock on that file. +** The first argument, pCur, is a cursor opened on some b-tree. Count the +** number of entries in the b-tree and write the result to *pnEntry. ** -** SQLITE_OK is returned on success. If the file is not a -** well-formed database file, then SQLITE_CORRUPT is returned. -** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM -** is returned if we run out of memory. +** SQLITE_OK is returned if the operation is successfully executed. +** Otherwise, if an error is encountered (i.e. an IO error or database +** corruption) an SQLite error code is returned. */ -static int lockBtree(BtShared *pBt){ - int rc; /* Result code from subfunctions */ - MemPage *pPage1; /* Page 1 of the database file */ - u32 nPage; /* Number of pages in the database */ - u32 nPageFile = 0; /* Number of pages in the database file */ - u32 nPageHeader; /* Number of pages in the database according to hdr */ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCount(sqlcipher_sqlite3 *db, BtCursor *pCur, i64 *pnEntry){ + i64 nEntry = 0; /* Value to return in *pnEntry */ + int rc; /* Return code */ - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - assert( pBt->pPage1==0 ); - rc = sqlcipher_sqlite3PagerSharedLock(pBt->pPager); - if( rc!=SQLITE_OK ) return rc; - rc = btreeGetPage(pBt, 1, &pPage1, 0); - if( rc!=SQLITE_OK ) return rc; + rc = moveToRoot(pCur); + if( rc==SQLITE_EMPTY ){ + *pnEntry = 0; + return SQLITE_OK; + } - /* Do some checking to help insure the file we opened really is - ** a valid database file. + /* Unless an error occurs, the following loop runs one iteration for each + ** page in the B-Tree structure (not including overflow pages). */ - nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); - sqlcipher_sqlite3PagerPagecount(pBt->pPager, (int*)&nPageFile); - if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){ - nPage = nPageFile; - } - if( (pBt->db->flags & SQLITE_ResetDatabase)!=0 ){ - nPage = 0; - } - if( nPage>0 ){ - u32 pageSize; - u32 usableSize; - u8 *page1 = pPage1->aData; - rc = SQLITE_NOTADB; - /* EVIDENCE-OF: R-43737-39999 Every valid SQLite database file begins - ** with the following 16 bytes (in hex): 53 51 4c 69 74 65 20 66 6f 72 6d - ** 61 74 20 33 00. */ - if( memcmp(page1, zMagicHeader, 16)!=0 ){ - goto page1_init_failed; - } + while( rc==SQLITE_OK && !AtomicLoad(&db->u1.isInterrupted) ){ + int iIdx; /* Index of child node in parent */ + MemPage *pPage; /* Current page of the b-tree */ -#ifdef SQLITE_OMIT_WAL - if( page1[18]>1 ){ - pBt->btsFlags |= BTS_READ_ONLY; - } - if( page1[19]>1 ){ - goto page1_init_failed; - } -#else - if( page1[18]>2 ){ - pBt->btsFlags |= BTS_READ_ONLY; - } - if( page1[19]>2 ){ - goto page1_init_failed; + /* If this is a leaf page or the tree is not an int-key tree, then + ** this page contains countable entries. Increment the entry counter + ** accordingly. + */ + pPage = pCur->pPage; + if( pPage->leaf || !pPage->intKey ){ + nEntry += pPage->nCell; } - /* If the write version is set to 2, this database should be accessed - ** in WAL mode. If the log is not already open, open it now. Then - ** return SQLITE_OK and return without populating BtShared.pPage1. - ** The caller detects this and calls this function again. This is - ** required as the version of page 1 currently in the page1 buffer - ** may not be the latest version - there may be a newer one in the log - ** file. + /* pPage is a leaf node. This loop navigates the cursor so that it + ** points to the first interior cell that it points to the parent of + ** the next page in the tree that has not yet been visited. The + ** pCur->aiIdx[pCur->iPage] value is set to the index of the parent cell + ** of the page, or to the number of cells in the page if the next page + ** to visit is the right-child of its parent. + ** + ** If all pages in the tree have been visited, return SQLITE_OK to the + ** caller. */ - if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){ - int isOpen = 0; - rc = sqlcipher_sqlite3PagerOpenWal(pBt->pPager, &isOpen); - if( rc!=SQLITE_OK ){ - goto page1_init_failed; - }else{ - setDefaultSyncFlag(pBt, SQLITE_DEFAULT_WAL_SYNCHRONOUS+1); - if( isOpen==0 ){ - releasePageOne(pPage1); - return SQLITE_OK; + if( pPage->leaf ){ + do { + if( pCur->iPage==0 ){ + /* All pages of the b-tree have been visited. Return successfully. */ + *pnEntry = nEntry; + return moveToRoot(pCur); } - } - rc = SQLITE_NOTADB; - }else{ - setDefaultSyncFlag(pBt, SQLITE_DEFAULT_SYNCHRONOUS+1); + moveToParent(pCur); + }while ( pCur->ix>=pCur->pPage->nCell ); + + pCur->ix++; + pPage = pCur->pPage; } -#endif - /* EVIDENCE-OF: R-15465-20813 The maximum and minimum embedded payload - ** fractions and the leaf payload fraction values must be 64, 32, and 32. - ** - ** The original design allowed these amounts to vary, but as of - ** version 3.6.0, we require them to be fixed. + /* Descend to the child node of the cell that the cursor currently + ** points at. This is the right-child if (iIdx==pPage->nCell). */ - if( memcmp(&page1[21], "\100\040\040",3)!=0 ){ - goto page1_init_failed; - } - /* EVIDENCE-OF: R-51873-39618 The page size for a database file is - ** determined by the 2-byte integer located at an offset of 16 bytes from - ** the beginning of the database file. */ - pageSize = (page1[16]<<8) | (page1[17]<<16); - /* EVIDENCE-OF: R-25008-21688 The size of a page is a power of two - ** between 512 and 65536 inclusive. */ - if( ((pageSize-1)&pageSize)!=0 - || pageSize>SQLITE_MAX_PAGE_SIZE - || pageSize<=256 - ){ - goto page1_init_failed; - } - pBt->btsFlags |= BTS_PAGESIZE_FIXED; - assert( (pageSize & 7)==0 ); - /* EVIDENCE-OF: R-59310-51205 The "reserved space" size in the 1-byte - ** integer at offset 20 is the number of bytes of space at the end of - ** each page to reserve for extensions. - ** - ** EVIDENCE-OF: R-37497-42412 The size of the reserved region is - ** determined by the one-byte unsigned integer found at an offset of 20 - ** into the database file header. */ - usableSize = pageSize - page1[20]; - if( (u32)pageSize!=pBt->pageSize ){ - /* After reading the first page of the database assuming a page size - ** of BtShared.pageSize, we have discovered that the page-size is - ** actually pageSize. Unlock the database, leave pBt->pPage1 at - ** zero and return SQLITE_OK. The caller will call this function - ** again with the correct page-size. - */ - releasePageOne(pPage1); - pBt->usableSize = usableSize; - pBt->pageSize = pageSize; - freeTempSpace(pBt); - rc = sqlcipher_sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, - pageSize-usableSize); - return rc; - } - if( sqlcipher_sqlite3WritableSchema(pBt->db)==0 && nPage>nPageFile ){ - rc = SQLITE_CORRUPT_BKPT; - goto page1_init_failed; - } - /* EVIDENCE-OF: R-28312-64704 However, the usable size is not allowed to - ** be less than 480. In other words, if the page size is 512, then the - ** reserved space size cannot exceed 32. */ - if( usableSize<480 ){ - goto page1_init_failed; + iIdx = pCur->ix; + if( iIdx==pPage->nCell ){ + rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); + }else{ + rc = moveToChild(pCur, get4byte(findCell(pPage, iIdx))); } - pBt->pageSize = pageSize; - pBt->usableSize = usableSize; -#ifndef SQLITE_OMIT_AUTOVACUUM - pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0); - pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0); -#endif - } - - /* maxLocal is the maximum amount of payload to store locally for - ** a cell. Make sure it is small enough so that at least minFanout - ** cells can will fit on one page. We assume a 10-byte page header. - ** Besides the payload, the cell must store: - ** 2-byte pointer to the cell - ** 4-byte child pointer - ** 9-byte nKey value - ** 4-byte nData value - ** 4-byte overflow page pointer - ** So a cell consists of a 2-byte pointer, a header which is as much as - ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow - ** page pointer. - */ - pBt->maxLocal = (u16)((pBt->usableSize-12)*64/255 - 23); - pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23); - pBt->maxLeaf = (u16)(pBt->usableSize - 35); - pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23); - if( pBt->maxLocal>127 ){ - pBt->max1bytePayload = 127; - }else{ - pBt->max1bytePayload = (u8)pBt->maxLocal; } - assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); - pBt->pPage1 = pPage1; - pBt->nPage = nPage; - return SQLITE_OK; -page1_init_failed: - releasePageOne(pPage1); - pBt->pPage1 = 0; + /* An error has occurred. Return an error code. */ return rc; } -#ifndef NDEBUG /* -** Return the number of cursors open on pBt. This is for use -** in assert() expressions, so it is only compiled if NDEBUG is not -** defined. -** -** Only write cursors are counted if wrOnly is true. If wrOnly is -** false then all cursors are counted. -** -** For the purposes of this routine, a cursor is any cursor that -** is capable of reading or writing to the database. Cursors that -** have been tripped into the CURSOR_FAULT state are not counted. +** Return the pager associated with a BTree. This routine is used for +** testing and debugging only. */ -static int countValidCursors(BtShared *pBt, int wrOnly){ - BtCursor *pCur; - int r = 0; - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - if( (wrOnly==0 || (pCur->curFlags & BTCF_WriteFlag)!=0) - && pCur->eState!=CURSOR_FAULT ) r++; +SQLITE_PRIVATE Pager *sqlcipher_sqlite3BtreePager(Btree *p){ + return p->pBt->pPager; +} + +#ifndef SQLITE_OMIT_INTEGRITY_CHECK +/* +** Append a message to the error message string. +*/ +static void checkAppendMsg( + IntegrityCk *pCheck, + const char *zFormat, + ... +){ + va_list ap; + if( !pCheck->mxErr ) return; + pCheck->mxErr--; + pCheck->nErr++; + va_start(ap, zFormat); + if( pCheck->errMsg.nChar ){ + sqlcipher_sqlite3_str_append(&pCheck->errMsg, "\n", 1); + } + if( pCheck->zPfx ){ + sqlcipher_sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2); + } + sqlcipher_sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap); + va_end(ap); + if( pCheck->errMsg.accError==SQLITE_NOMEM ){ + pCheck->bOomFault = 1; } - return r; } -#endif +#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ + +#ifndef SQLITE_OMIT_INTEGRITY_CHECK /* -** If there are no outstanding cursors and we are not in the middle -** of a transaction but there is a read lock on the database, then -** this routine unrefs the first page of the database file which -** has the effect of releasing the read lock. +** Return non-zero if the bit in the IntegrityCk.aPgRef[] array that +** corresponds to page iPg is already set. +*/ +static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){ + assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); + return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07))); +} + +/* +** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg. +*/ +static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){ + assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); + pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07)); +} + + +/* +** Add 1 to the reference count for page iPage. If this is the second +** reference to the page, add an error message to pCheck->zErrMsg. +** Return 1 if there are 2 or more references to the page and 0 if +** if this is the first reference to the page. ** -** If there is a transaction in progress, this routine is a no-op. +** Also check that the page number is in bounds. */ -static void unlockBtreeIfUnused(BtShared *pBt){ - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - assert( countValidCursors(pBt,0)==0 || pBt->inTransaction>TRANS_NONE ); - if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){ - MemPage *pPage1 = pBt->pPage1; - assert( pPage1->aData ); - assert( sqlcipher_sqlite3PagerRefcount(pBt->pPager)==1 ); - pBt->pPage1 = 0; - releasePageOne(pPage1); +static int checkRef(IntegrityCk *pCheck, Pgno iPage){ + if( iPage>pCheck->nPage || iPage==0 ){ + checkAppendMsg(pCheck, "invalid page number %d", iPage); + return 1; + } + if( getPageReferenced(pCheck, iPage) ){ + checkAppendMsg(pCheck, "2nd reference to page %d", iPage); + return 1; } + if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1; + setPageReferenced(pCheck, iPage); + return 0; } +#ifndef SQLITE_OMIT_AUTOVACUUM /* -** If pBt points to an empty file then convert that empty file -** into a new empty database by initializing the first page of -** the database. +** Check that the entry in the pointer-map for page iChild maps to +** page iParent, pointer type ptrType. If not, append an error message +** to pCheck. */ -static int newDatabase(BtShared *pBt){ - MemPage *pP1; - unsigned char *data; +static void checkPtrmap( + IntegrityCk *pCheck, /* Integrity check context */ + Pgno iChild, /* Child page number */ + u8 eType, /* Expected pointer map type */ + Pgno iParent /* Expected pointer map parent page number */ +){ int rc; + u8 ePtrmapType; + Pgno iPtrmapParent; - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - if( pBt->nPage>0 ){ - return SQLITE_OK; + rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->bOomFault = 1; + checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild); + return; + } + + if( ePtrmapType!=eType || iPtrmapParent!=iParent ){ + checkAppendMsg(pCheck, + "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", + iChild, eType, iParent, ePtrmapType, iPtrmapParent); } - pP1 = pBt->pPage1; - assert( pP1!=0 ); - data = pP1->aData; - rc = sqlcipher_sqlite3PagerWrite(pP1->pDbPage); - if( rc ) return rc; - memcpy(data, zMagicHeader, sizeof(zMagicHeader)); - assert( sizeof(zMagicHeader)==16 ); - data[16] = (u8)((pBt->pageSize>>8)&0xff); - data[17] = (u8)((pBt->pageSize>>16)&0xff); - data[18] = 1; - data[19] = 1; - assert( pBt->usableSize<=pBt->pageSize && pBt->usableSize+255>=pBt->pageSize); - data[20] = (u8)(pBt->pageSize - pBt->usableSize); - data[21] = 64; - data[22] = 32; - data[23] = 32; - memset(&data[24], 0, 100-24); - zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA ); - pBt->btsFlags |= BTS_PAGESIZE_FIXED; -#ifndef SQLITE_OMIT_AUTOVACUUM - assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 ); - assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 ); - put4byte(&data[36 + 4*4], pBt->autoVacuum); - put4byte(&data[36 + 7*4], pBt->incrVacuum); -#endif - pBt->nPage = 1; - data[31] = 1; - return SQLITE_OK; } +#endif /* -** Initialize the first page of the database file (creating a database -** consisting of a single page and no schema objects). Return SQLITE_OK -** if successful, or an SQLite error code otherwise. +** Check the integrity of the freelist or of an overflow page list. +** Verify that the number of pages on the list is N. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeNewDb(Btree *p){ - int rc; - sqlcipher_sqlite3BtreeEnter(p); - p->pBt->nPage = 0; - rc = newDatabase(p->pBt); - sqlcipher_sqlite3BtreeLeave(p); - return rc; +static void checkList( + IntegrityCk *pCheck, /* Integrity checking context */ + int isFreeList, /* True for a freelist. False for overflow page list */ + Pgno iPage, /* Page number for first page in the list */ + u32 N /* Expected number of pages in the list */ +){ + int i; + u32 expected = N; + int nErrAtStart = pCheck->nErr; + while( iPage!=0 && pCheck->mxErr ){ + DbPage *pOvflPage; + unsigned char *pOvflData; + if( checkRef(pCheck, iPage) ) break; + N--; + if( sqlcipher_sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage, 0) ){ + checkAppendMsg(pCheck, "failed to get page %d", iPage); + break; + } + pOvflData = (unsigned char *)sqlcipher_sqlite3PagerGetData(pOvflPage); + if( isFreeList ){ + u32 n = (u32)get4byte(&pOvflData[4]); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pCheck->pBt->autoVacuum ){ + checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0); + } +#endif + if( n>pCheck->pBt->usableSize/4-2 ){ + checkAppendMsg(pCheck, + "freelist leaf count too big on page %d", iPage); + N--; + }else{ + for(i=0; i<(int)n; i++){ + Pgno iFreePage = get4byte(&pOvflData[8+i*4]); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pCheck->pBt->autoVacuum ){ + checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0); + } +#endif + checkRef(pCheck, iFreePage); + } + N -= n; + } + } +#ifndef SQLITE_OMIT_AUTOVACUUM + else{ + /* If this database supports auto-vacuum and iPage is not the last + ** page in this overflow list, check that the pointer-map entry for + ** the following page matches iPage. + */ + if( pCheck->pBt->autoVacuum && N>0 ){ + i = get4byte(pOvflData); + checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage); + } + } +#endif + iPage = get4byte(pOvflData); + sqlcipher_sqlite3PagerUnref(pOvflPage); + } + if( N && nErrAtStart==pCheck->nErr ){ + checkAppendMsg(pCheck, + "%s is %d but should be %d", + isFreeList ? "size" : "overflow list length", + expected-N, expected); + } } +#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ /* -** Attempt to start a new transaction. A write-transaction -** is started if the second argument is nonzero, otherwise a read- -** transaction. If the second argument is 2 or more and exclusive -** transaction is started, meaning that no other process is allowed -** to access the database. A preexisting transaction may not be -** upgraded to exclusive by calling this routine a second time - the -** exclusivity flag only works for a new transaction. +** An implementation of a min-heap. ** -** A write-transaction must be started before attempting any -** changes to the database. None of the following routines -** will work unless a transaction is started first: +** aHeap[0] is the number of elements on the heap. aHeap[1] is the +** root element. The daughter nodes of aHeap[N] are aHeap[N*2] +** and aHeap[N*2+1]. ** -** sqlcipher_sqlite3BtreeCreateTable() -** sqlcipher_sqlite3BtreeCreateIndex() -** sqlcipher_sqlite3BtreeClearTable() -** sqlcipher_sqlite3BtreeDropTable() -** sqlcipher_sqlite3BtreeInsert() -** sqlcipher_sqlite3BtreeDelete() -** sqlcipher_sqlite3BtreeUpdateMeta() +** The heap property is this: Every node is less than or equal to both +** of its daughter nodes. A consequence of the heap property is that the +** root node aHeap[1] is always the minimum value currently in the heap. ** -** If an initial attempt to acquire the lock fails because of lock contention -** and the database was previously unlocked, then invoke the busy handler -** if there is one. But if there was previously a read-lock, do not -** invoke the busy handler - just return SQLITE_BUSY. SQLITE_BUSY is -** returned when there is already a read-lock in order to avoid a deadlock. +** The btreeHeapInsert() routine inserts an unsigned 32-bit number onto +** the heap, preserving the heap property. The btreeHeapPull() routine +** removes the root element from the heap (the minimum value in the heap) +** and then moves other nodes around as necessary to preserve the heap +** property. ** -** Suppose there are two processes A and B. A has a read lock and B has -** a reserved lock. B tries to promote to exclusive but is blocked because -** of A's read lock. A tries to promote to reserved but is blocked by B. -** One or the other of the two processes must give way or there can be -** no progress. By returning SQLITE_BUSY and not invoking the busy callback -** when A already has a read lock, we encourage A to give up and let B -** proceed. +** This heap is used for cell overlap and coverage testing. Each u32 +** entry represents the span of a cell or freeblock on a btree page. +** The upper 16 bits are the index of the first byte of a range and the +** lower 16 bits are the index of the last byte of that range. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ - BtShared *pBt = p->pBt; - Pager *pPager = pBt->pPager; - int rc = SQLITE_OK; +static void btreeHeapInsert(u32 *aHeap, u32 x){ + u32 j, i = ++aHeap[0]; + aHeap[i] = x; + while( (j = i/2)>0 && aHeap[j]>aHeap[i] ){ + x = aHeap[j]; + aHeap[j] = aHeap[i]; + aHeap[i] = x; + i = j; + } +} +static int btreeHeapPull(u32 *aHeap, u32 *pOut){ + u32 j, i, x; + if( (x = aHeap[0])==0 ) return 0; + *pOut = aHeap[1]; + aHeap[1] = aHeap[x]; + aHeap[x] = 0xffffffff; + aHeap[0]--; + i = 1; + while( (j = i*2)<=aHeap[0] ){ + if( aHeap[j]>aHeap[j+1] ) j++; + if( aHeap[i]zPfx; + int saved_v1 = pCheck->v1; + int saved_v2 = pCheck->v2; + u8 savedIsInit = 0; - /* If the btree is already in a write-transaction, or it - ** is already in a read-transaction and a read-transaction - ** is requested, this is a no-op. + /* Check that the page exists */ - if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){ - goto trans_begun; + pBt = pCheck->pBt; + usableSize = pBt->usableSize; + if( iPage==0 ) return 0; + if( checkRef(pCheck, iPage) ) return 0; + pCheck->zPfx = "Page %u: "; + pCheck->v1 = iPage; + if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){ + checkAppendMsg(pCheck, + "unable to get the page. error code=%d", rc); + goto end_of_check; } - assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 ); - if( (p->db->flags & SQLITE_ResetDatabase) - && sqlcipher_sqlite3PagerIsreadonly(pPager)==0 - ){ - pBt->btsFlags &= ~BTS_READ_ONLY; + /* Clear MemPage.isInit to make sure the corruption detection code in + ** btreeInitPage() is executed. */ + savedIsInit = pPage->isInit; + pPage->isInit = 0; + if( (rc = btreeInitPage(pPage))!=0 ){ + assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */ + checkAppendMsg(pCheck, + "btreeInitPage() returns error code %d", rc); + goto end_of_check; } - - /* Write transactions are not possible on a read-only database */ - if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){ - rc = SQLITE_READONLY; - goto trans_begun; + if( (rc = btreeComputeFreeSpace(pPage))!=0 ){ + assert( rc==SQLITE_CORRUPT ); + checkAppendMsg(pCheck, "free space corruption", rc); + goto end_of_check; } + data = pPage->aData; + hdr = pPage->hdrOffset; -#ifndef SQLITE_OMIT_SHARED_CACHE - { - sqlcipher_sqlite3 *pBlock = 0; - /* If another database handle has already opened a write transaction - ** on this shared-btree structure and a second write transaction is - ** requested, return SQLITE_LOCKED. - */ - if( (wrflag && pBt->inTransaction==TRANS_WRITE) - || (pBt->btsFlags & BTS_PENDING)!=0 - ){ - pBlock = pBt->pWriter->db; - }else if( wrflag>1 ){ - BtLock *pIter; - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - if( pIter->pBtree!=p ){ - pBlock = pIter->pBtree->db; - break; - } - } - } - if( pBlock ){ - sqlcipher_sqlite3ConnectionBlocked(p->db, pBlock); - rc = SQLITE_LOCKED_SHAREDCACHE; - goto trans_begun; + /* Set up for cell analysis */ + pCheck->zPfx = "On tree page %u cell %d: "; + contentOffset = get2byteNotZero(&data[hdr+5]); + assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ + + /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the + ** number of cells on the page. */ + nCell = get2byte(&data[hdr+3]); + assert( pPage->nCell==nCell ); + + /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page + ** immediately follows the b-tree page header. */ + cellStart = hdr + 12 - 4*pPage->leaf; + assert( pPage->aCellIdx==&data[cellStart] ); + pCellIdx = &data[cellStart + 2*(nCell-1)]; + + if( !pPage->leaf ){ + /* Analyze the right-child page of internal pages */ + pgno = get4byte(&data[hdr+8]); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + pCheck->zPfx = "On page %u at right child: "; + checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage); } - } #endif + depth = checkTreePage(pCheck, pgno, &maxKey, maxKey); + keyCanBeEqual = 0; + }else{ + /* For leaf pages, the coverage check will occur in the same loop + ** as the other cell checks, so initialize the heap. */ + heap = pCheck->heap; + heap[0] = 0; + } - /* Any read-only or read-write transaction implies a read-lock on - ** page 1. So if some other shared-cache client already has a write-lock - ** on page 1, the transaction cannot be opened. */ - rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK); - if( SQLITE_OK!=rc ) goto trans_begun; + /* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte + ** integer offsets to the cell contents. */ + for(i=nCell-1; i>=0 && pCheck->mxErr; i--){ + CellInfo info; - pBt->btsFlags &= ~BTS_INITIALLY_EMPTY; - if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY; - do { - sqlcipher_sqlite3PagerWalDb(pPager, p->db); + /* Check cell size */ + pCheck->v2 = i; + assert( pCellIdx==&data[cellStart + i*2] ); + pc = get2byteAligned(pCellIdx); + pCellIdx -= 2; + if( pcusableSize-4 ){ + checkAppendMsg(pCheck, "Offset %d out of range %d..%d", + pc, contentOffset, usableSize-4); + doCoverageCheck = 0; + continue; + } + pCell = &data[pc]; + pPage->xParseCell(pPage, pCell, &info); + if( pc+info.nSize>usableSize ){ + checkAppendMsg(pCheck, "Extends off end of page"); + doCoverageCheck = 0; + continue; + } -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT - /* If transitioning from no transaction directly to a write transaction, - ** block for the WRITER lock first if possible. */ - if( pBt->pPage1==0 && wrflag ){ - assert( pBt->inTransaction==TRANS_NONE ); - rc = sqlcipher_sqlite3PagerWalWriteLock(pPager, 1); - if( rc!=SQLITE_BUSY && rc!=SQLITE_OK ) break; + /* Check for integer primary key out of range */ + if( pPage->intKey ){ + if( keyCanBeEqual ? (info.nKey > maxKey) : (info.nKey >= maxKey) ){ + checkAppendMsg(pCheck, "Rowid %lld out of order", info.nKey); + } + maxKey = info.nKey; + keyCanBeEqual = 0; /* Only the first key on the page may ==maxKey */ } + + /* Check the content overflow list */ + if( info.nPayload>info.nLocal ){ + u32 nPage; /* Number of pages on the overflow chain */ + Pgno pgnoOvfl; /* First page of the overflow chain */ + assert( pc + info.nSize - 4 <= usableSize ); + nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4); + pgnoOvfl = get4byte(&pCell[info.nSize - 4]); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage); + } #endif + checkList(pCheck, 0, pgnoOvfl, nPage); + } - /* Call lockBtree() until either pBt->pPage1 is populated or - ** lockBtree() returns something other than SQLITE_OK. lockBtree() - ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after - ** reading page 1 it discovers that the page-size of the database - ** file is not pBt->pageSize. In this case lockBtree() will update - ** pBt->pageSize to the page-size of the file on disk. - */ - while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) ); + if( !pPage->leaf ){ + /* Check sanity of left child page for internal pages */ + pgno = get4byte(pCell); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage); + } +#endif + d2 = checkTreePage(pCheck, pgno, &maxKey, maxKey); + keyCanBeEqual = 0; + if( d2!=depth ){ + checkAppendMsg(pCheck, "Child page depth differs"); + depth = d2; + } + }else{ + /* Populate the coverage-checking heap for leaf pages */ + btreeHeapInsert(heap, (pc<<16)|(pc+info.nSize-1)); + } + } + *piMinKey = maxKey; - if( rc==SQLITE_OK && wrflag ){ - if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){ - rc = SQLITE_READONLY; + /* Check for complete coverage of the page + */ + pCheck->zPfx = 0; + if( doCoverageCheck && pCheck->mxErr>0 ){ + /* For leaf pages, the min-heap has already been initialized and the + ** cells have already been inserted. But for internal pages, that has + ** not yet been done, so do it now */ + if( !pPage->leaf ){ + heap = pCheck->heap; + heap[0] = 0; + for(i=nCell-1; i>=0; i--){ + u32 size; + pc = get2byteAligned(&data[cellStart+i*2]); + size = pPage->xCellSize(pPage, &data[pc]); + btreeHeapInsert(heap, (pc<<16)|(pc+size-1)); + } + } + /* Add the freeblocks to the min-heap + ** + ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header + ** is the offset of the first freeblock, or zero if there are no + ** freeblocks on the page. + */ + i = get2byte(&data[hdr+1]); + while( i>0 ){ + int size, j; + assert( (u32)i<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */ + size = get2byte(&data[i+2]); + assert( (u32)(i+size)<=usableSize ); /* due to btreeComputeFreeSpace() */ + btreeHeapInsert(heap, (((u32)i)<<16)|(i+size-1)); + /* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a + ** big-endian integer which is the offset in the b-tree page of the next + ** freeblock in the chain, or zero if the freeblock is the last on the + ** chain. */ + j = get2byte(&data[i]); + /* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of + ** increasing offset. */ + assert( j==0 || j>i+size ); /* Enforced by btreeComputeFreeSpace() */ + assert( (u32)j<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */ + i = j; + } + /* Analyze the min-heap looking for overlap between cells and/or + ** freeblocks, and counting the number of untracked bytes in nFrag. + ** + ** Each min-heap entry is of the form: (start_address<<16)|end_address. + ** There is an implied first entry the covers the page header, the cell + ** pointer index, and the gap between the cell pointer index and the start + ** of cell content. + ** + ** The loop below pulls entries from the min-heap in order and compares + ** the start_address against the previous end_address. If there is an + ** overlap, that means bytes are used multiple times. If there is a gap, + ** that gap is added to the fragmentation count. + */ + nFrag = 0; + prev = contentOffset - 1; /* Implied first min-heap entry */ + while( btreeHeapPull(heap,&x) ){ + if( (prev&0xffff)>=(x>>16) ){ + checkAppendMsg(pCheck, + "Multiple uses for byte %u of page %u", x>>16, iPage); + break; }else{ - rc = sqlcipher_sqlite3PagerBegin(pPager, wrflag>1, sqlcipher_sqlite3TempInMemory(p->db)); - if( rc==SQLITE_OK ){ - rc = newDatabase(pBt); - }else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){ - /* if there was no transaction opened when this function was - ** called and SQLITE_BUSY_SNAPSHOT is returned, change the error - ** code to SQLITE_BUSY. */ - rc = SQLITE_BUSY; - } + nFrag += (x>>16) - (prev&0xffff) - 1; + prev = x; } } + nFrag += usableSize - (prev&0xffff) - 1; + /* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments + ** is stored in the fifth field of the b-tree page header. + ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the + ** number of fragmented free bytes within the cell content area. + */ + if( heap[0]==0 && nFrag!=data[hdr+7] ){ + checkAppendMsg(pCheck, + "Fragmentation of %d bytes reported as %d on page %u", + nFrag, data[hdr+7], iPage); + } + } + +end_of_check: + if( !doCoverageCheck ) pPage->isInit = savedIsInit; + releasePage(pPage); + pCheck->zPfx = saved_zPfx; + pCheck->v1 = saved_v1; + pCheck->v2 = saved_v2; + return depth+1; +} +#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ + +#ifndef SQLITE_OMIT_INTEGRITY_CHECK +/* +** This routine does a complete check of the given BTree file. aRoot[] is +** an array of pages numbers were each page number is the root page of +** a table. nRoot is the number of entries in aRoot. +** +** A read-only or read-write transaction must be opened before calling +** this function. +** +** Write the number of error seen in *pnErr. Except for some memory +** allocation errors, an error message held in memory obtained from +** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is +** returned. If a memory allocation error occurs, NULL is returned. +** +** If the first entry in aRoot[] is 0, that indicates that the list of +** root pages is incomplete. This is a "partial integrity-check". This +** happens when performing an integrity check on a single table. The +** zero is skipped, of course. But in addition, the freelist checks +** and the checks to make sure every page is referenced are also skipped, +** since obviously it is not possible to know which pages are covered by +** the unverified btrees. Except, if aRoot[1] is 1, then the freelist +** checks are still performed. +*/ +SQLITE_PRIVATE char *sqlcipher_sqlite3BtreeIntegrityCheck( + sqlcipher_sqlite3 *db, /* Database connection that is running the check */ + Btree *p, /* The btree to be checked */ + Pgno *aRoot, /* An array of root pages numbers for individual trees */ + int nRoot, /* Number of entries in aRoot[] */ + int mxErr, /* Stop reporting errors after this many */ + int *pnErr /* Write number of errors seen to this variable */ +){ + Pgno i; + IntegrityCk sCheck; + BtShared *pBt = p->pBt; + u64 savedDbFlags = pBt->db->flags; + char zErr[100]; + int bPartial = 0; /* True if not checking all btrees */ + int bCkFreelist = 1; /* True to scan the freelist */ + VVA_ONLY( int nRef ); + assert( nRoot>0 ); + + /* aRoot[0]==0 means this is a partial check */ + if( aRoot[0]==0 ){ + assert( nRoot>1 ); + bPartial = 1; + if( aRoot[1]!=1 ) bCkFreelist = 0; + } + + sqlcipher_sqlite3BtreeEnter(p); + assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); + VVA_ONLY( nRef = sqlcipher_sqlite3PagerRefcount(pBt->pPager) ); + assert( nRef>=0 ); + sCheck.db = db; + sCheck.pBt = pBt; + sCheck.pPager = pBt->pPager; + sCheck.nPage = btreePagecount(sCheck.pBt); + sCheck.mxErr = mxErr; + sCheck.nErr = 0; + sCheck.bOomFault = 0; + sCheck.zPfx = 0; + sCheck.v1 = 0; + sCheck.v2 = 0; + sCheck.aPgRef = 0; + sCheck.heap = 0; + sqlcipher_sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); + sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL; + if( sCheck.nPage==0 ){ + goto integrity_ck_cleanup; + } + + sCheck.aPgRef = sqlcipher_sqlite3MallocZero((sCheck.nPage / 8)+ 1); + if( !sCheck.aPgRef ){ + sCheck.bOomFault = 1; + goto integrity_ck_cleanup; + } + sCheck.heap = (u32*)sqlcipher_sqlite3PageMalloc( pBt->pageSize ); + if( sCheck.heap==0 ){ + sCheck.bOomFault = 1; + goto integrity_ck_cleanup; + } - if( rc!=SQLITE_OK ){ - (void)sqlcipher_sqlite3PagerWalWriteLock(pPager, 0); - unlockBtreeIfUnused(pBt); - } - }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && - btreeInvokeBusyHandler(pBt) ); - sqlcipher_sqlite3PagerWalDb(pPager, 0); -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT - if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; -#endif + i = PENDING_BYTE_PAGE(pBt); + if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); - if( rc==SQLITE_OK ){ - if( p->inTrans==TRANS_NONE ){ - pBt->nTransaction++; -#ifndef SQLITE_OMIT_SHARED_CACHE - if( p->sharable ){ - assert( p->lock.pBtree==p && p->lock.iTable==1 ); - p->lock.eLock = READ_LOCK; - p->lock.pNext = pBt->pLock; - pBt->pLock = &p->lock; + /* Check the integrity of the freelist + */ + if( bCkFreelist ){ + sCheck.zPfx = "Main freelist: "; + checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), + get4byte(&pBt->pPage1->aData[36])); + sCheck.zPfx = 0; + } + + /* Check all the tables. + */ +#ifndef SQLITE_OMIT_AUTOVACUUM + if( !bPartial ){ + if( pBt->autoVacuum ){ + Pgno mx = 0; + Pgno mxInHdr; + for(i=0; (int)ipPage1->aData[52]); + if( mx!=mxInHdr ){ + checkAppendMsg(&sCheck, + "max rootpage (%d) disagrees with header (%d)", + mx, mxInHdr + ); } -#endif + }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){ + checkAppendMsg(&sCheck, + "incremental_vacuum enabled with a max rootpage of zero" + ); } - p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); - if( p->inTrans>pBt->inTransaction ){ - pBt->inTransaction = p->inTrans; + } +#endif + testcase( pBt->db->flags & SQLITE_CellSizeCk ); + pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; + for(i=0; (int)iautoVacuum && aRoot[i]>1 && !bPartial ){ + checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); } - if( wrflag ){ - MemPage *pPage1 = pBt->pPage1; -#ifndef SQLITE_OMIT_SHARED_CACHE - assert( !pBt->pWriter ); - pBt->pWriter = p; - pBt->btsFlags &= ~BTS_EXCLUSIVE; - if( wrflag>1 ) pBt->btsFlags |= BTS_EXCLUSIVE; #endif + checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); + } + pBt->db->flags = savedDbFlags; - /* If the db-size header field is incorrect (as it may be if an old - ** client has been writing the database file), update it now. Doing - ** this sooner rather than later means the database size can safely - ** re-read the database size from page 1 if a savepoint or transaction - ** rollback occurs within the transaction. + /* Make sure every page in the file is referenced + */ + if( !bPartial ){ + for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ +#ifdef SQLITE_OMIT_AUTOVACUUM + if( getPageReferenced(&sCheck, i)==0 ){ + checkAppendMsg(&sCheck, "Page %d is never used", i); + } +#else + /* If the database supports auto-vacuum, make sure no tables contain + ** references to pointer-map pages. */ - if( pBt->nPage!=get4byte(&pPage1->aData[28]) ){ - rc = sqlcipher_sqlite3PagerWrite(pPage1->pDbPage); - if( rc==SQLITE_OK ){ - put4byte(&pPage1->aData[28], pBt->nPage); - } + if( getPageReferenced(&sCheck, i)==0 && + (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ + checkAppendMsg(&sCheck, "Page %d is never used", i); + } + if( getPageReferenced(&sCheck, i)!=0 && + (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ + checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i); } +#endif } } -trans_begun: - if( rc==SQLITE_OK ){ - if( pSchemaVersion ){ - *pSchemaVersion = get4byte(&pBt->pPage1->aData[40]); - } - if( wrflag ){ - /* This call makes sure that the pager has the correct number of - ** open savepoints. If the second parameter is greater than 0 and - ** the sub-journal is not already open, then it will be opened here. - */ - rc = sqlcipher_sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint); - } + /* Clean up and report errors. + */ +integrity_ck_cleanup: + sqlcipher_sqlite3PageFree(sCheck.heap); + sqlcipher_sqlite3_free(sCheck.aPgRef); + if( sCheck.bOomFault ){ + sqlcipher_sqlite3_str_reset(&sCheck.errMsg); + sCheck.nErr++; } - - btreeIntegrity(p); + *pnErr = sCheck.nErr; + if( sCheck.nErr==0 ) sqlcipher_sqlite3_str_reset(&sCheck.errMsg); + /* Make sure this analysis did not leave any unref() pages. */ + assert( nRef==sqlcipher_sqlite3PagerRefcount(pBt->pPager) ); sqlcipher_sqlite3BtreeLeave(p); - return rc; + return sqlcipher_sqlite3StrAccumFinish(&sCheck.errMsg); } - -#ifndef SQLITE_OMIT_AUTOVACUUM +#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ /* -** Set the pointer-map entries for all children of page pPage. Also, if -** pPage contains cells that point to overflow pages, set the pointer -** map entries for the overflow pages as well. +** Return the full pathname of the underlying database file. Return +** an empty string if the database is in-memory or a TEMP database. +** +** The pager filename is invariant as long as the pager is +** open so it is safe to access without the BtShared mutex. */ -static int setChildPtrmaps(MemPage *pPage){ - int i; /* Counter variable */ - int nCell; /* Number of cells in page pPage */ - int rc; /* Return code */ - BtShared *pBt = pPage->pBt; - Pgno pgno = pPage->pgno; - - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - rc = pPage->isInit ? SQLITE_OK : btreeInitPage(pPage); - if( rc!=SQLITE_OK ) return rc; - nCell = pPage->nCell; +SQLITE_PRIVATE const char *sqlcipher_sqlite3BtreeGetFilename(Btree *p){ + assert( p->pBt->pPager!=0 ); + return sqlcipher_sqlite3PagerFilename(p->pBt->pPager, 1); +} - for(i=0; ipBt->pPager!=0 ); + return sqlcipher_sqlite3PagerJournalname(p->pBt->pPager); +} - ptrmapPutOvflPtr(pPage, pPage, pCell, &rc); +/* +** Return one of SQLITE_TXN_NONE, SQLITE_TXN_READ, or SQLITE_TXN_WRITE +** to describe the current transaction state of Btree p. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeTxnState(Btree *p){ + assert( p==0 || sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + return p ? p->inTrans : 0; +} - if( !pPage->leaf ){ - Pgno childPgno = get4byte(pCell); - ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); +#ifndef SQLITE_OMIT_WAL +/* +** Run a checkpoint on the Btree passed as the first argument. +** +** Return SQLITE_LOCKED if this or any other connection has an open +** transaction on the shared-cache the argument Btree is connected to. +** +** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){ + int rc = SQLITE_OK; + if( p ){ + BtShared *pBt = p->pBt; + sqlcipher_sqlite3BtreeEnter(p); + if( pBt->inTransaction!=TRANS_NONE ){ + rc = SQLITE_LOCKED; + }else{ + rc = sqlcipher_sqlite3PagerCheckpoint(pBt->pPager, p->db, eMode, pnLog, pnCkpt); } + sqlcipher_sqlite3BtreeLeave(p); } - - if( !pPage->leaf ){ - Pgno childPgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); - } - return rc; } +#endif /* -** Somewhere on pPage is a pointer to page iFrom. Modify this pointer so -** that it points to iTo. Parameter eType describes the type of pointer to -** be modified, as follows: +** Return true if there is currently a backup running on Btree p. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeIsInBackup(Btree *p){ + assert( p ); + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + return p->nBackup!=0; +} + +/* +** This function returns a pointer to a blob of memory associated with +** a single shared-btree. The memory is used by client code for its own +** purposes (for example, to store a high-level schema associated with +** the shared-btree). The btree layer manages reference counting issues. ** -** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child -** page of pPage. +** The first time this is called on a shared-btree, nBytes bytes of memory +** are allocated, zeroed, and returned to the caller. For each subsequent +** call the nBytes parameter is ignored and a pointer to the same blob +** of memory returned. ** -** PTRMAP_OVERFLOW1: pPage is a btree-page. The pointer points at an overflow -** page pointed to by one of the cells on pPage. +** If the nBytes parameter is 0 and the blob of memory has not yet been +** allocated, a null pointer is returned. If the blob has already been +** allocated, it is returned as normal. ** -** PTRMAP_OVERFLOW2: pPage is an overflow-page. The pointer points at the next -** overflow page in the list. +** Just before the shared-btree is closed, the function passed as the +** xFree argument when the memory allocation was made is invoked on the +** blob of allocated memory. The xFree function should not call sqlcipher_sqlite3_free() +** on the memory, the btree layer does that. */ -static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); - if( eType==PTRMAP_OVERFLOW2 ){ - /* The pointer is always the first 4 bytes of the page in this case. */ - if( get4byte(pPage->aData)!=iFrom ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - put4byte(pPage->aData, iTo); - }else{ - int i; - int nCell; - int rc; +SQLITE_PRIVATE void *sqlcipher_sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ + BtShared *pBt = p->pBt; + sqlcipher_sqlite3BtreeEnter(p); + if( !pBt->pSchema && nBytes ){ + pBt->pSchema = sqlcipher_sqlite3DbMallocZero(0, nBytes); + pBt->xFreeSchema = xFree; + } + sqlcipher_sqlite3BtreeLeave(p); + return pBt->pSchema; +} - rc = pPage->isInit ? SQLITE_OK : btreeInitPage(pPage); - if( rc ) return rc; - nCell = pPage->nCell; +/* +** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared +** btree as the argument handle holds an exclusive lock on the +** sqlite_schema table. Otherwise SQLITE_OK. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSchemaLocked(Btree *p){ + int rc; + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + sqlcipher_sqlite3BtreeEnter(p); + rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK); + assert( rc==SQLITE_OK || rc==SQLITE_LOCKED_SHAREDCACHE ); + sqlcipher_sqlite3BtreeLeave(p); + return rc; +} - for(i=0; ixParseCell(pPage, pCell, &info); - if( info.nLocal pPage->aData+pPage->pBt->usableSize ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - if( iFrom==get4byte(pCell+info.nSize-4) ){ - put4byte(pCell+info.nSize-4, iTo); - break; - } - } - }else{ - if( get4byte(pCell)==iFrom ){ - put4byte(pCell, iTo); - break; - } - } - } - if( i==nCell ){ - if( eType!=PTRMAP_BTREE || - get4byte(&pPage->aData[pPage->hdrOffset+8])!=iFrom ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - put4byte(&pPage->aData[pPage->hdrOffset+8], iTo); +#ifndef SQLITE_OMIT_SHARED_CACHE +/* +** Obtain a lock on the table whose root page is iTab. The +** lock is a write lock if isWritelock is true or a read lock +** if it is false. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){ + int rc = SQLITE_OK; + assert( p->inTrans!=TRANS_NONE ); + if( p->sharable ){ + u8 lockType = READ_LOCK + isWriteLock; + assert( READ_LOCK+1==WRITE_LOCK ); + assert( isWriteLock==0 || isWriteLock==1 ); + + sqlcipher_sqlite3BtreeEnter(p); + rc = querySharedCacheTableLock(p, iTab, lockType); + if( rc==SQLITE_OK ){ + rc = setSharedCacheTableLock(p, iTab, lockType); } + sqlcipher_sqlite3BtreeLeave(p); } - return SQLITE_OK; + return rc; } +#endif - +#ifndef SQLITE_OMIT_INCRBLOB /* -** Move the open database page pDbPage to location iFreePage in the -** database. The pDbPage reference remains valid. +** Argument pCsr must be a cursor opened for writing on an +** INTKEY table currently pointing at a valid table entry. +** This function modifies the data stored as part of that entry. ** -** The isCommit flag indicates that there is no need to remember that -** the journal needs to be sync()ed before database page pDbPage->pgno -** can be written to. The caller has already promised not to write to that -** page. +** Only the data content may only be modified, it is not possible to +** change the length of the data stored. If this function is called with +** parameters that attempt to write past the end of the existing data, +** no modifications are made and SQLITE_CORRUPT is returned. */ -static int relocatePage( - BtShared *pBt, /* Btree */ - MemPage *pDbPage, /* Open page to move */ - u8 eType, /* Pointer map 'type' entry for pDbPage */ - Pgno iPtrPage, /* Pointer map 'page-no' entry for pDbPage */ - Pgno iFreePage, /* The location to move pDbPage to */ - int isCommit /* isCommit flag passed to sqlcipher_sqlite3PagerMovepage */ -){ - MemPage *pPtrPage; /* The page that contains a pointer to pDbPage */ - Pgno iDbPage = pDbPage->pgno; - Pager *pPager = pBt->pPager; +SQLITE_PRIVATE int sqlcipher_sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ int rc; + assert( cursorOwnsBtShared(pCsr) ); + assert( sqlcipher_sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); + assert( pCsr->curFlags & BTCF_Incrblob ); - assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 || - eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ); - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - assert( pDbPage->pBt==pBt ); - if( iDbPage<3 ) return SQLITE_CORRUPT_BKPT; - - /* Move page iDbPage from its current location to page number iFreePage */ - TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", - iDbPage, iFreePage, iPtrPage, eType)); - rc = sqlcipher_sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage, isCommit); + rc = restoreCursorPosition(pCsr); if( rc!=SQLITE_OK ){ return rc; } - pDbPage->pgno = iFreePage; + assert( pCsr->eState!=CURSOR_REQUIRESEEK ); + if( pCsr->eState!=CURSOR_VALID ){ + return SQLITE_ABORT; + } - /* If pDbPage was a btree-page, then it may have child pages and/or cells - ** that point to overflow pages. The pointer map entries for all these - ** pages need to be changed. + /* Save the positions of all other cursors open on this table. This is + ** required in case any of them are holding references to an xFetch + ** version of the b-tree page modified by the accessPayload call below. ** - ** If pDbPage is an overflow page, then the first 4 bytes may store a - ** pointer to a subsequent overflow page. If this is the case, then - ** the pointer map needs to be updated for the subsequent overflow page. + ** Note that pCsr must be open on a INTKEY table and saveCursorPosition() + ** and hence saveAllCursors() cannot fail on a BTREE_INTKEY table, hence + ** saveAllCursors can only return SQLITE_OK. */ - if( eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ){ - rc = setChildPtrmaps(pDbPage); - if( rc!=SQLITE_OK ){ - return rc; - } - }else{ - Pgno nextOvfl = get4byte(pDbPage->aData); - if( nextOvfl!=0 ){ - ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage, &rc); - if( rc!=SQLITE_OK ){ - return rc; - } - } - } + VVA_ONLY(rc =) saveAllCursors(pCsr->pBt, pCsr->pgnoRoot, pCsr); + assert( rc==SQLITE_OK ); - /* Fix the database pointer on page iPtrPage that pointed at iDbPage so - ** that it points at iFreePage. Also fix the pointer map entry for - ** iPtrPage. + /* Check some assumptions: + ** (a) the cursor is open for writing, + ** (b) there is a read/write transaction open, + ** (c) the connection holds a write-lock on the table (if required), + ** (d) there are no conflicting read-locks, and + ** (e) the cursor points at a valid row of an intKey table. */ - if( eType!=PTRMAP_ROOTPAGE ){ - rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = sqlcipher_sqlite3PagerWrite(pPtrPage->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(pPtrPage); - return rc; - } - rc = modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType); - releasePage(pPtrPage); - if( rc==SQLITE_OK ){ - ptrmapPut(pBt, iFreePage, eType, iPtrPage, &rc); - } + if( (pCsr->curFlags & BTCF_WriteFlag)==0 ){ + return SQLITE_READONLY; } - return rc; -} + assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0 + && pCsr->pBt->inTransaction==TRANS_WRITE ); + assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) ); + assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) ); + assert( pCsr->pPage->intKey ); -/* Forward declaration required by incrVacuumStep(). */ -static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8); + return accessPayload(pCsr, offset, amt, (unsigned char *)z, 1); +} /* -** Perform a single step of an incremental-vacuum. If successful, return -** SQLITE_OK. If there is no work to do (and therefore no point in -** calling this function again), return SQLITE_DONE. Or, if an error -** occurs, return some other error code. -** -** More specifically, this function attempts to re-organize the database so -** that the last page of the file currently in use is no longer in use. -** -** Parameter nFin is the number of pages that this database would contain -** were this function called until it returns SQLITE_DONE. -** -** If the bCommit parameter is non-zero, this function assumes that the -** caller will keep calling incrVacuumStep() until it returns SQLITE_DONE -** or an error. bCommit is passed true for an auto-vacuum-on-commit -** operation, or false for an incremental vacuum. +** Mark this cursor as an incremental blob cursor. */ -static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){ - Pgno nFreeList; /* Number of pages still on the free-list */ - int rc; - - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - assert( iLastPg>nFin ); - - if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){ - u8 eType; - Pgno iPtrPage; - - nFreeList = get4byte(&pBt->pPage1->aData[36]); - if( nFreeList==0 ){ - return SQLITE_DONE; - } +SQLITE_PRIVATE void sqlcipher_sqlite3BtreeIncrblobCursor(BtCursor *pCur){ + pCur->curFlags |= BTCF_Incrblob; + pCur->pBtree->hasIncrblobCur = 1; +} +#endif - rc = ptrmapGet(pBt, iLastPg, &eType, &iPtrPage); - if( rc!=SQLITE_OK ){ - return rc; - } - if( eType==PTRMAP_ROOTPAGE ){ - return SQLITE_CORRUPT_BKPT; - } +/* +** Set both the "read version" (single byte at byte offset 18) and +** "write version" (single byte at byte offset 19) fields in the database +** header to iVersion. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ + BtShared *pBt = pBtree->pBt; + int rc; /* Return code */ - if( eType==PTRMAP_FREEPAGE ){ - if( bCommit==0 ){ - /* Remove the page from the files free-list. This is not required - ** if bCommit is non-zero. In that case, the free-list will be - ** truncated to zero after this function returns, so it doesn't - ** matter if it still contains some garbage entries. - */ - Pgno iFreePg; - MemPage *pFreePg; - rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, BTALLOC_EXACT); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( iFreePg==iLastPg ); - releasePage(pFreePg); - } - } else { - Pgno iFreePg; /* Index of free page to move pLastPg to */ - MemPage *pLastPg; - u8 eMode = BTALLOC_ANY; /* Mode parameter for allocateBtreePage() */ - Pgno iNear = 0; /* nearby parameter for allocateBtreePage() */ + assert( iVersion==1 || iVersion==2 ); - rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0); - if( rc!=SQLITE_OK ){ - return rc; - } + /* If setting the version fields to 1, do not automatically open the + ** WAL connection, even if the version fields are currently set to 2. + */ + pBt->btsFlags &= ~BTS_NO_WAL; + if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL; - /* If bCommit is zero, this loop runs exactly once and page pLastPg - ** is swapped with the first free page pulled off the free list. - ** - ** On the other hand, if bCommit is greater than zero, then keep - ** looping until a free-page located within the first nFin pages - ** of the file is found. - */ - if( bCommit==0 ){ - eMode = BTALLOC_LE; - iNear = nFin; - } - do { - MemPage *pFreePg; - rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode); - if( rc!=SQLITE_OK ){ - releasePage(pLastPg); - return rc; + rc = sqlcipher_sqlite3BtreeBeginTrans(pBtree, 0, 0); + if( rc==SQLITE_OK ){ + u8 *aData = pBt->pPage1->aData; + if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){ + rc = sqlcipher_sqlite3BtreeBeginTrans(pBtree, 2, 0); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3PagerWrite(pBt->pPage1->pDbPage); + if( rc==SQLITE_OK ){ + aData[18] = (u8)iVersion; + aData[19] = (u8)iVersion; } - releasePage(pFreePg); - }while( bCommit && iFreePg>nFin ); - assert( iFreePgbDoTruncate = 1; - pBt->nPage = iLastPg; - } - return SQLITE_OK; + pBt->btsFlags &= ~BTS_NO_WAL; + return rc; } /* -** The database opened by the first argument is an auto-vacuum database -** nOrig pages in size containing nFree free pages. Return the expected -** size of the database in pages following an auto-vacuum operation. +** Return true if the cursor has a hint specified. This routine is +** only used from within assert() statements */ -static Pgno finalDbSize(BtShared *pBt, Pgno nOrig, Pgno nFree){ - int nEntry; /* Number of entries on one ptrmap page */ - Pgno nPtrmap; /* Number of PtrMap pages to be freed */ - Pgno nFin; /* Return value */ - - nEntry = pBt->usableSize/5; - nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry; - nFin = nOrig - nFree - nPtrmap; - if( nOrig>PENDING_BYTE_PAGE(pBt) && nFinhints & mask)!=0; +} - return nFin; +/* +** Return true if the given Btree is read-only. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeIsReadonly(Btree *p){ + return (p->pBt->btsFlags & BTS_READ_ONLY)!=0; } /* -** A write-transaction must be opened before calling this function. -** It performs a single unit of work towards an incremental vacuum. -** -** If the incremental vacuum is finished after this function has run, -** SQLITE_DONE is returned. If it is not finished, but no error occurred, -** SQLITE_OK is returned. Otherwise an SQLite error code. +** Return the size of the header added to each page by this module. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeIncrVacuum(Btree *p){ - int rc; - BtShared *pBt = p->pBt; +SQLITE_PRIVATE int sqlcipher_sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } - sqlcipher_sqlite3BtreeEnter(p); - assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE ); - if( !pBt->autoVacuum ){ - rc = SQLITE_DONE; - }else{ - Pgno nOrig = btreePagecount(pBt); - Pgno nFree = get4byte(&pBt->pPage1->aData[36]); - Pgno nFin = finalDbSize(pBt, nOrig, nFree); +#if !defined(SQLITE_OMIT_SHARED_CACHE) +/* +** Return true if the Btree passed as the only argument is sharable. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSharable(Btree *p){ + return p->sharable; +} - if( nOrig=nOrig ){ - rc = SQLITE_CORRUPT_BKPT; - }else if( nFree>0 ){ - rc = saveAllCursors(pBt, 0, 0); - if( rc==SQLITE_OK ){ - invalidateAllOverflowCache(pBt); - rc = incrVacuumStep(pBt, nFin, nOrig, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3PagerWrite(pBt->pPage1->pDbPage); - put4byte(&pBt->pPage1->aData[28], pBt->nPage); - } - }else{ - rc = SQLITE_DONE; - } - } - sqlcipher_sqlite3BtreeLeave(p); - return rc; +/* +** Return the number of connections to the BtShared object accessed by +** the Btree handle passed as the only argument. For private caches +** this is always 1. For shared caches it may be 1 or greater. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeConnectionCount(Btree *p){ + testcase( p->sharable ); + return p->pBt->nRef; } +#endif +/************** End of btree.c ***********************************************/ +/************** Begin file backup.c ******************************************/ /* -** This routine is called prior to sqlcipher_sqlite3PagerCommit when a transaction -** is committed for an auto-vacuum database. +** 2009 January 28 +** +** 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. ** -** If SQLITE_OK is returned, then *pnTrunc is set to the number of pages -** the database file should be truncated to during the commit process. -** i.e. the database has been reorganized so that only the first *pnTrunc -** pages are in use. +************************************************************************* +** This file contains the implementation of the sqlcipher_sqlite3_backup_XXX() +** API functions and the related features. */ -static int autoVacuumCommit(BtShared *pBt){ - int rc = SQLITE_OK; - Pager *pPager = pBt->pPager; - VVA_ONLY( int nRef = sqlcipher_sqlite3PagerRefcount(pPager); ) +/* #include "sqliteInt.h" */ +/* #include "btreeInt.h" */ - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - invalidateAllOverflowCache(pBt); - assert(pBt->autoVacuum); - if( !pBt->incrVacuum ){ - Pgno nFin; /* Number of pages in database after autovacuuming */ - Pgno nFree; /* Number of pages on the freelist initially */ - Pgno iFree; /* The next page to be freed */ - Pgno nOrig; /* Database size before freeing */ +/* +** Structure allocated for each backup operation. +*/ +struct sqlcipher_sqlite3_backup { + sqlcipher_sqlite3* pDestDb; /* Destination database handle */ + Btree *pDest; /* Destination b-tree file */ + u32 iDestSchema; /* Original schema cookie in destination */ + int bDestLocked; /* True once a write-transaction is open on pDest */ - nOrig = btreePagecount(pBt); - if( PTRMAP_ISPAGE(pBt, nOrig) || nOrig==PENDING_BYTE_PAGE(pBt) ){ - /* It is not possible to create a database for which the final page - ** is either a pointer-map page or the pending-byte page. If one - ** is encountered, this indicates corruption. - */ - return SQLITE_CORRUPT_BKPT; - } + Pgno iNext; /* Page number of the next source page to copy */ + sqlcipher_sqlite3* pSrcDb; /* Source database handle */ + Btree *pSrc; /* Source b-tree file */ - nFree = get4byte(&pBt->pPage1->aData[36]); - nFin = finalDbSize(pBt, nOrig, nFree); - if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT; - if( nFinnFin && rc==SQLITE_OK; iFree--){ - rc = incrVacuumStep(pBt, nFin, iFree, 1); - } - if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){ - rc = sqlcipher_sqlite3PagerWrite(pBt->pPage1->pDbPage); - put4byte(&pBt->pPage1->aData[32], 0); - put4byte(&pBt->pPage1->aData[36], 0); - put4byte(&pBt->pPage1->aData[28], nFin); - pBt->bDoTruncate = 1; - pBt->nPage = nFin; - } - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3PagerRollback(pPager); - } - } + int rc; /* Backup process error code */ - assert( nRef>=sqlcipher_sqlite3PagerRefcount(pPager) ); - return rc; -} + /* These two variables are set by every call to backup_step(). They are + ** read by calls to backup_remaining() and backup_pagecount(). + */ + Pgno nRemaining; /* Number of pages left to copy */ + Pgno nPagecount; /* Total number of pages to copy */ -#else /* ifndef SQLITE_OMIT_AUTOVACUUM */ -# define setChildPtrmaps(x) SQLITE_OK -#endif + int isAttached; /* True once backup has been registered with pager */ + sqlcipher_sqlite3_backup *pNext; /* Next backup associated with source pager */ +}; /* -** This routine does the first phase of a two-phase commit. This routine -** causes a rollback journal to be created (if it does not already exist) -** and populated with enough information so that if a power loss occurs -** the database can be restored to its original state by playing back -** the journal. Then the contents of the journal are flushed out to -** the disk. After the journal is safely on oxide, the changes to the -** database are written into the database file and flushed to oxide. -** At the end of this call, the rollback journal still exists on the -** disk and we are still holding all locks, so the transaction has not -** committed. See sqlcipher_sqlite3BtreeCommitPhaseTwo() for the second phase of the -** commit process. +** THREAD SAFETY NOTES: ** -** This call is a no-op if no write-transaction is currently active on pBt. +** Once it has been created using backup_init(), a single sqlcipher_sqlite3_backup +** structure may be accessed via two groups of thread-safe entry points: ** -** Otherwise, sync the database file for the btree pBt. zSuperJrnl points to -** the name of a super-journal file that should be written into the -** individual journal file, or is NULL, indicating no super-journal file -** (single database transaction). +** * Via the sqlcipher_sqlite3_backup_XXX() API function backup_step() and +** backup_finish(). Both these functions obtain the source database +** handle mutex and the mutex associated with the source BtShared +** structure, in that order. ** -** When this is called, the super-journal should already have been -** created, populated with this journal pointer and synced to disk. +** * Via the BackupUpdate() and BackupRestart() functions, which are +** invoked by the pager layer to report various state changes in +** the page cache associated with the source database. The mutex +** associated with the source database BtShared structure will always +** be held when either of these functions are invoked. ** -** Once this is routine has returned, the only thing required to commit -** the write-transaction for this database file is to delete the journal. +** The other sqlcipher_sqlite3_backup_XXX() API functions, backup_remaining() and +** backup_pagecount() are not thread-safe functions. If they are called +** while some other thread is calling backup_step() or backup_finish(), +** the values returned may be invalid. There is no way for a call to +** BackupUpdate() or BackupRestart() to interfere with backup_remaining() +** or backup_pagecount(). +** +** Depending on the SQLite configuration, the database handles and/or +** the Btree objects may have their own mutexes that require locking. +** Non-sharable Btrees (in-memory databases for example), do not have +** associated mutexes. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){ - int rc = SQLITE_OK; - if( p->inTrans==TRANS_WRITE ){ - BtShared *pBt = p->pBt; - sqlcipher_sqlite3BtreeEnter(p); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - rc = autoVacuumCommit(pBt); - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3BtreeLeave(p); - return rc; - } + +/* +** Return a pointer corresponding to database zDb (i.e. "main", "temp") +** in connection handle pDb. If such a database cannot be found, return +** a NULL pointer and write an error message to pErrorDb. +** +** If the "temp" database is requested, it may need to be opened by this +** function. If an error occurs while doing so, return 0 and write an +** error message to pErrorDb. +*/ +static Btree *findBtree(sqlcipher_sqlite3 *pErrorDb, sqlcipher_sqlite3 *pDb, const char *zDb){ + int i = sqlcipher_sqlite3FindDbName(pDb, zDb); + + if( i==1 ){ + Parse sParse; + int rc = 0; + sqlcipher_sqlite3ParseObjectInit(&sParse,pDb); + if( sqlcipher_sqlite3OpenTempDatabase(&sParse) ){ + sqlcipher_sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg); + rc = SQLITE_ERROR; } - if( pBt->bDoTruncate ){ - sqlcipher_sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage); + sqlcipher_sqlite3DbFree(pErrorDb, sParse.zErrMsg); + sqlcipher_sqlite3ParseObjectReset(&sParse); + if( rc ){ + return 0; } -#endif - rc = sqlcipher_sqlite3PagerCommitPhaseOne(pBt->pPager, zSuperJrnl, 0); - sqlcipher_sqlite3BtreeLeave(p); } + + if( i<0 ){ + sqlcipher_sqlite3ErrorWithMsg(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb); + return 0; + } + + return pDb->aDb[i].pBt; +} + +/* +** Attempt to set the page size of the destination to match the page size +** of the source. +*/ +static int setDestPgsz(sqlcipher_sqlite3_backup *p){ + int rc; + rc = sqlcipher_sqlite3BtreeSetPageSize(p->pDest,sqlcipher_sqlite3BtreeGetPageSize(p->pSrc),0,0); return rc; } /* -** This function is called from both BtreeCommitPhaseTwo() and BtreeRollback() -** at the conclusion of a transaction. +** Check that there is no open read-transaction on the b-tree passed as the +** second argument. If there is not, return SQLITE_OK. Otherwise, if there +** is an open read-transaction, return SQLITE_ERROR and leave an error +** message in database handle db. */ -static void btreeEndTransaction(Btree *p){ - BtShared *pBt = p->pBt; - sqlcipher_sqlite3 *db = p->db; - assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); +static int checkReadTransaction(sqlcipher_sqlite3 *db, Btree *p){ + if( sqlcipher_sqlite3BtreeTxnState(p)!=SQLITE_TXN_NONE ){ + sqlcipher_sqlite3ErrorWithMsg(db, SQLITE_ERROR, "destination database is in use"); + return SQLITE_ERROR; + } + return SQLITE_OK; +} -#ifndef SQLITE_OMIT_AUTOVACUUM - pBt->bDoTruncate = 0; +/* +** Create an sqlcipher_sqlite3_backup process to copy the contents of zSrcDb from +** connection handle pSrcDb to zDestDb in pDestDb. If successful, return +** a pointer to the new sqlcipher_sqlite3_backup object. +** +** If an error occurs, NULL is returned and an error code and error message +** stored in database handle pDestDb. +*/ +SQLITE_API sqlcipher_sqlite3_backup *sqlcipher_sqlite3_backup_init( + sqlcipher_sqlite3* pDestDb, /* Database to write to */ + const char *zDestDb, /* Name of database within pDestDb */ + sqlcipher_sqlite3* pSrcDb, /* Database connection to read from */ + const char *zSrcDb /* Name of database within pSrcDb */ +){ + sqlcipher_sqlite3_backup *p; /* Value to return */ + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlcipher_sqlite3SafetyCheckOk(pSrcDb)||!sqlcipher_sqlite3SafetyCheckOk(pDestDb) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } #endif - if( p->inTrans>TRANS_NONE && db->nVdbeRead>1 ){ - /* If there are other active statements that belong to this database - ** handle, downgrade to a read-only transaction. The other statements - ** may still be reading from the database. */ - downgradeAllSharedCacheTableLocks(p); - p->inTrans = TRANS_READ; - }else{ - /* If the handle had any kind of transaction open, decrement the - ** transaction count of the shared btree. If the transaction count - ** reaches 0, set the shared state to TRANS_NONE. The unlockBtreeIfUnused() - ** call below will unlock the pager. */ - if( p->inTrans!=TRANS_NONE ){ - clearAllSharedCacheTableLocks(p); - pBt->nTransaction--; - if( 0==pBt->nTransaction ){ - pBt->inTransaction = TRANS_NONE; - } + +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + { + extern int sqlcipher_find_db_index(sqlcipher_sqlite3*, const char*); + extern void sqlcipherCodecGetKey(sqlcipher_sqlite3*, int, void**, int*); + int srcNKey, destNKey; + void *zKey; + + sqlcipherCodecGetKey(pSrcDb, sqlcipher_find_db_index(pSrcDb, zSrcDb), &zKey, &srcNKey); + sqlcipherCodecGetKey(pDestDb, sqlcipher_find_db_index(pDestDb, zDestDb), &zKey, &destNKey); + zKey = NULL; + + /* either both databases must be plaintext, or both must be encrypted */ + if((srcNKey == 0 && destNKey > 0) || (srcNKey > 0 && destNKey == 0)) { + sqlcipher_sqlite3ErrorWithMsg(pDestDb, SQLITE_ERROR, "backup is not supported with encrypted databases"); + return NULL; } + } +#endif +/* END SQLCIPHER */ - /* Set the current transaction state to TRANS_NONE and unlock the - ** pager if this call closed the only read or write transaction. */ - p->inTrans = TRANS_NONE; - unlockBtreeIfUnused(pBt); + /* Lock the source database handle. The destination database + ** handle is not locked in this routine, but it is locked in + ** sqlcipher_sqlite3_backup_step(). The user is required to ensure that no + ** other thread accesses the destination handle for the duration + ** of the backup operation. Any attempt to use the destination + ** database connection while a backup is in progress may cause + ** a malfunction or a deadlock. + */ + sqlcipher_sqlite3_mutex_enter(pSrcDb->mutex); + sqlcipher_sqlite3_mutex_enter(pDestDb->mutex); + + if( pSrcDb==pDestDb ){ + sqlcipher_sqlite3ErrorWithMsg( + pDestDb, SQLITE_ERROR, "source and destination must be distinct" + ); + p = 0; + }else { + /* Allocate space for a new sqlcipher_sqlite3_backup object... + ** EVIDENCE-OF: R-64852-21591 The sqlcipher_sqlite3_backup object is created by a + ** call to sqlcipher_sqlite3_backup_init() and is destroyed by a call to + ** sqlcipher_sqlite3_backup_finish(). */ + p = (sqlcipher_sqlite3_backup *)sqlcipher_sqlite3MallocZero(sizeof(sqlcipher_sqlite3_backup)); + if( !p ){ + sqlcipher_sqlite3Error(pDestDb, SQLITE_NOMEM_BKPT); + } } - btreeIntegrity(p); + /* If the allocation succeeded, populate the new object. */ + if( p ){ + p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb); + p->pDest = findBtree(pDestDb, pDestDb, zDestDb); + p->pDestDb = pDestDb; + p->pSrcDb = pSrcDb; + p->iNext = 1; + p->isAttached = 0; + + if( 0==p->pSrc || 0==p->pDest + || checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK + ){ + /* One (or both) of the named databases did not exist or an OOM + ** error was hit. Or there is a transaction open on the destination + ** database. The error has already been written into the pDestDb + ** handle. All that is left to do here is free the sqlcipher_sqlite3_backup + ** structure. */ + sqlcipher_sqlite3_free(p); + p = 0; + } + } + if( p ){ + p->pSrc->nBackup++; + } + + sqlcipher_sqlite3_mutex_leave(pDestDb->mutex); + sqlcipher_sqlite3_mutex_leave(pSrcDb->mutex); + return p; } /* -** Commit the transaction currently in progress. -** -** This routine implements the second phase of a 2-phase commit. The -** sqlcipher_sqlite3BtreeCommitPhaseOne() routine does the first phase and should -** be invoked prior to calling this routine. The sqlcipher_sqlite3BtreeCommitPhaseOne() -** routine did all the work of writing information out to disk and flushing the -** contents so that they are written onto the disk platter. All this -** routine has to do is delete or truncate or zero the header in the -** the rollback journal (which causes the transaction to commit) and -** drop locks. -** -** Normally, if an error occurs while the pager layer is attempting to -** finalize the underlying journal file, this function returns an error and -** the upper layer will attempt a rollback. However, if the second argument -** is non-zero then this b-tree transaction is part of a multi-file -** transaction. In this case, the transaction has already been committed -** (by deleting a super-journal file) and the caller will ignore this -** functions return code. So, even if an error occurs in the pager layer, -** reset the b-tree objects internal state to indicate that the write -** transaction has been closed. This is quite safe, as the pager will have -** transitioned to the error state. -** -** This will release the write lock on the database file. If there -** are no active cursors, it also releases the read lock. +** Argument rc is an SQLite error code. Return true if this error is +** considered fatal if encountered during a backup operation. All errors +** are considered fatal except for SQLITE_BUSY and SQLITE_LOCKED. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){ +static int isFatalError(int rc){ + return (rc!=SQLITE_OK && rc!=SQLITE_BUSY && ALWAYS(rc!=SQLITE_LOCKED)); +} + +/* +** Parameter zSrcData points to a buffer containing the data for +** page iSrcPg from the source database. Copy this data into the +** destination database. +*/ +static int backupOnePage( + sqlcipher_sqlite3_backup *p, /* Backup handle */ + Pgno iSrcPg, /* Source database page to backup */ + const u8 *zSrcData, /* Source database page data */ + int bUpdate /* True for an update, false otherwise */ +){ + Pager * const pDestPager = sqlcipher_sqlite3BtreePager(p->pDest); + const int nSrcPgsz = sqlcipher_sqlite3BtreeGetPageSize(p->pSrc); + int nDestPgsz = sqlcipher_sqlite3BtreeGetPageSize(p->pDest); + const int nCopy = MIN(nSrcPgsz, nDestPgsz); + const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + extern void *sqlcipherPagerGetCodec(Pager*); + /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is + ** guaranteed that the shared-mutex is held by this thread, handle + ** p->pSrc may not actually be the owner. */ + int nSrcReserve = sqlcipher_sqlite3BtreeGetReserveNoMutex(p->pSrc); + int nDestReserve = sqlcipher_sqlite3BtreeGetRequestedReserve(p->pDest); +#endif +/* END SQLCIPHER */ + int rc = SQLITE_OK; + i64 iOff; + + assert( sqlcipher_sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 ); + assert( p->bDestLocked ); + assert( !isFatalError(p->rc) ); + assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); + assert( zSrcData ); + + /* Catch the case where the destination is an in-memory database and the + ** page sizes of the source and destination differ. + */ + if( nSrcPgsz!=nDestPgsz && sqlcipher_sqlite3PagerIsMemdb(pDestPager) ){ + rc = SQLITE_READONLY; + } - if( p->inTrans==TRANS_NONE ) return SQLITE_OK; - sqlcipher_sqlite3BtreeEnter(p); - btreeIntegrity(p); +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + /* Backup is not possible if the page size of the destination is changing + ** and a codec is in use. + */ + if( nSrcPgsz!=nDestPgsz && sqlcipherPagerGetCodec(pDestPager)!=0 ){ + rc = SQLITE_READONLY; + } - /* If the handle has a write-transaction open, commit the shared-btrees - ** transaction and set the shared state to TRANS_READ. + /* Backup is not possible if the number of bytes of reserve space differ + ** between source and destination. If there is a difference, try to + ** fix the destination to agree with the source. If that is not possible, + ** then the backup cannot proceed. */ - if( p->inTrans==TRANS_WRITE ){ - int rc; - BtShared *pBt = p->pBt; - assert( pBt->inTransaction==TRANS_WRITE ); - assert( pBt->nTransaction>0 ); - rc = sqlcipher_sqlite3PagerCommitPhaseTwo(pBt->pPager); - if( rc!=SQLITE_OK && bCleanup==0 ){ - sqlcipher_sqlite3BtreeLeave(p); - return rc; - } - p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */ - pBt->inTransaction = TRANS_READ; - btreeClearHasContent(pBt); + if( nSrcReserve!=nDestReserve ){ + u32 newPgsz = nSrcPgsz; + rc = sqlcipher_sqlite3PagerSetPagesize(pDestPager, &newPgsz, nSrcReserve); + if( rc==SQLITE_OK && newPgsz!=(u32)nSrcPgsz ) rc = SQLITE_READONLY; } +#endif +/* END SQLCIPHER */ - btreeEndTransaction(p); - sqlcipher_sqlite3BtreeLeave(p); - return SQLITE_OK; -} + /* This loop runs once for each destination page spanned by the source + ** page. For each iteration, variable iOff is set to the byte offset + ** of the destination page. + */ + for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOffpDest->pBt) ) continue; + if( SQLITE_OK==(rc = sqlcipher_sqlite3PagerGet(pDestPager, iDest, &pDestPg, 0)) + && SQLITE_OK==(rc = sqlcipher_sqlite3PagerWrite(pDestPg)) + ){ + const u8 *zIn = &zSrcData[iOff%nSrcPgsz]; + u8 *zDestData = sqlcipher_sqlite3PagerGetData(pDestPg); + u8 *zOut = &zDestData[iOff%nDestPgsz]; -/* -** Do both phases of a commit. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCommit(Btree *p){ - int rc; - sqlcipher_sqlite3BtreeEnter(p); - rc = sqlcipher_sqlite3BtreeCommitPhaseOne(p, 0); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3BtreeCommitPhaseTwo(p, 0); + /* Copy the data from the source page into the destination page. + ** Then clear the Btree layer MemPage.isInit flag. Both this module + ** and the pager code use this trick (clearing the first byte + ** of the page 'extra' space to invalidate the Btree layers + ** cached parse of the page). MemPage.isInit is marked + ** "MUST BE FIRST" for this purpose. + */ + memcpy(zOut, zIn, nCopy); + ((u8 *)sqlcipher_sqlite3PagerGetExtra(pDestPg))[0] = 0; + if( iOff==0 && bUpdate==0 ){ + sqlcipher_sqlite3Put4byte(&zOut[28], sqlcipher_sqlite3BtreeLastPage(p->pSrc)); + } + } + sqlcipher_sqlite3PagerUnref(pDestPg); } - sqlcipher_sqlite3BtreeLeave(p); + return rc; } /* -** This routine sets the state to CURSOR_FAULT and the error -** code to errCode for every cursor on any BtShared that pBtree -** references. Or if the writeOnly flag is set to 1, then only -** trip write cursors and leave read cursors unchanged. -** -** Every cursor is a candidate to be tripped, including cursors -** that belong to other database connections that happen to be -** sharing the cache with pBtree. -** -** This routine gets called when a rollback occurs. If the writeOnly -** flag is true, then only write-cursors need be tripped - read-only -** cursors save their current positions so that they may continue -** following the rollback. Or, if writeOnly is false, all cursors are -** tripped. In general, writeOnly is false if the transaction being -** rolled back modified the database schema. In this case b-tree root -** pages may be moved or deleted from the database altogether, making -** it unsafe for read cursors to continue. -** -** If the writeOnly flag is true and an error is encountered while -** saving the current position of a read-only cursor, all cursors, -** including all read-cursors are tripped. +** If pFile is currently larger than iSize bytes, then truncate it to +** exactly iSize bytes. If pFile is not larger than iSize bytes, then +** this function is a no-op. ** -** SQLITE_OK is returned if successful, or if an error occurs while -** saving a cursor position, an SQLite error code. +** Return SQLITE_OK if everything is successful, or an SQLite error +** code if an error occurs. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){ - BtCursor *p; - int rc = SQLITE_OK; - - assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 ); - if( pBtree ){ - sqlcipher_sqlite3BtreeEnter(pBtree); - for(p=pBtree->pBt->pCursor; p; p=p->pNext){ - if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){ - if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){ - rc = saveCursorPosition(p); - if( rc!=SQLITE_OK ){ - (void)sqlcipher_sqlite3BtreeTripAllCursors(pBtree, rc, 0); - break; - } - } - }else{ - sqlcipher_sqlite3BtreeClearCursor(p); - p->eState = CURSOR_FAULT; - p->skipNext = errCode; - } - btreeReleaseAllCursorPages(p); - } - sqlcipher_sqlite3BtreeLeave(pBtree); +static int backupTruncateFile(sqlcipher_sqlite3_file *pFile, i64 iSize){ + i64 iCurrent; + int rc = sqlcipher_sqlite3OsFileSize(pFile, &iCurrent); + if( rc==SQLITE_OK && iCurrent>iSize ){ + rc = sqlcipher_sqlite3OsTruncate(pFile, iSize); } return rc; } /* -** Set the pBt->nPage field correctly, according to the current -** state of the database. Assume pBt->pPage1 is valid. +** Register this backup object with the associated source pager for +** callbacks when pages are changed or the cache invalidated. */ -static void btreeSetNPage(BtShared *pBt, MemPage *pPage1){ - int nPage = get4byte(&pPage1->aData[28]); - testcase( nPage==0 ); - if( nPage==0 ) sqlcipher_sqlite3PagerPagecount(pBt->pPager, &nPage); - testcase( pBt->nPage!=nPage ); - pBt->nPage = nPage; +static void attachBackupObject(sqlcipher_sqlite3_backup *p){ + sqlcipher_sqlite3_backup **pp; + assert( sqlcipher_sqlite3BtreeHoldsMutex(p->pSrc) ); + pp = sqlcipher_sqlite3PagerBackupPtr(sqlcipher_sqlite3BtreePager(p->pSrc)); + p->pNext = *pp; + *pp = p; + p->isAttached = 1; } /* -** Rollback the transaction in progress. -** -** If tripCode is not SQLITE_OK then cursors will be invalidated (tripped). -** Only write cursors are tripped if writeOnly is true but all cursors are -** tripped if writeOnly is false. Any attempt to use -** a tripped cursor will result in an error. -** -** This will release the write lock on the database file. If there -** are no active cursors, it also releases the read lock. +** Copy nPage pages from the source b-tree to the destination. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly){ +SQLITE_API int sqlcipher_sqlite3_backup_step(sqlcipher_sqlite3_backup *p, int nPage){ int rc; - BtShared *pBt = p->pBt; - MemPage *pPage1; + int destMode; /* Destination journal mode */ + int pgszSrc = 0; /* Source page size */ + int pgszDest = 0; /* Destination page size */ - assert( writeOnly==1 || writeOnly==0 ); - assert( tripCode==SQLITE_ABORT_ROLLBACK || tripCode==SQLITE_OK ); - sqlcipher_sqlite3BtreeEnter(p); - if( tripCode==SQLITE_OK ){ - rc = tripCode = saveAllCursors(pBt, 0, 0); - if( rc ) writeOnly = 0; - }else{ - rc = SQLITE_OK; - } - if( tripCode ){ - int rc2 = sqlcipher_sqlite3BtreeTripAllCursors(p, tripCode, writeOnly); - assert( rc==SQLITE_OK || (writeOnly==0 && rc2==SQLITE_OK) ); - if( rc2!=SQLITE_OK ) rc = rc2; +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ) return SQLITE_MISUSE_BKPT; +#endif + sqlcipher_sqlite3_mutex_enter(p->pSrcDb->mutex); + sqlcipher_sqlite3BtreeEnter(p->pSrc); + if( p->pDestDb ){ + sqlcipher_sqlite3_mutex_enter(p->pDestDb->mutex); } - btreeIntegrity(p); - if( p->inTrans==TRANS_WRITE ){ - int rc2; + rc = p->rc; + if( !isFatalError(rc) ){ + Pager * const pSrcPager = sqlcipher_sqlite3BtreePager(p->pSrc); /* Source pager */ + Pager * const pDestPager = sqlcipher_sqlite3BtreePager(p->pDest); /* Dest pager */ + int ii; /* Iterator variable */ + int nSrcPage = -1; /* Size of source db in pages */ + int bCloseTrans = 0; /* True if src db requires unlocking */ - assert( TRANS_WRITE==pBt->inTransaction ); - rc2 = sqlcipher_sqlite3PagerRollback(pBt->pPager); - if( rc2!=SQLITE_OK ){ - rc = rc2; + /* If the source pager is currently in a write-transaction, return + ** SQLITE_BUSY immediately. + */ + if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){ + rc = SQLITE_BUSY; + }else{ + rc = SQLITE_OK; } - /* The rollback may have destroyed the pPage1->aData value. So - ** call btreeGetPage() on page 1 again to make - ** sure pPage1->aData is set correctly. */ - if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ - btreeSetNPage(pBt, pPage1); - releasePageOne(pPage1); + /* If there is no open read-transaction on the source database, open + ** one now. If a transaction is opened here, then it will be closed + ** before this function exits. + */ + if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlcipher_sqlite3BtreeTxnState(p->pSrc) ){ + rc = sqlcipher_sqlite3BtreeBeginTrans(p->pSrc, 0, 0); + bCloseTrans = 1; } - assert( countValidCursors(pBt, 1)==0 ); - pBt->inTransaction = TRANS_READ; - btreeClearHasContent(pBt); - } - btreeEndTransaction(p); - sqlcipher_sqlite3BtreeLeave(p); - return rc; -} + /* If the destination database has not yet been locked (i.e. if this + ** is the first call to backup_step() for the current backup operation), + ** try to set its page size to the same as the source database. This + ** is especially important on ZipVFS systems, as in that case it is + ** not possible to create a database file that uses one page size by + ** writing to it with another. */ + if( p->bDestLocked==0 && rc==SQLITE_OK && setDestPgsz(p)==SQLITE_NOMEM ){ + rc = SQLITE_NOMEM; + } -/* -** Start a statement subtransaction. The subtransaction can be rolled -** back independently of the main transaction. You must start a transaction -** before starting a subtransaction. The subtransaction is ended automatically -** if the main transaction commits or rolls back. -** -** Statement subtransactions are used around individual SQL statements -** that are contained within a BEGIN...COMMIT block. If a constraint -** error occurs within the statement, the effect of that one statement -** can be rolled back without having to rollback the entire transaction. -** -** A statement sub-transaction is implemented as an anonymous savepoint. The -** value passed as the second parameter is the total number of savepoints, -** including the new anonymous savepoint, open on the B-Tree. i.e. if there -** are no active savepoints and no other statement-transactions open, -** iStatement is 1. This anonymous savepoint can be released or rolled back -** using the sqlcipher_sqlite3BtreeSavepoint() function. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeBeginStmt(Btree *p, int iStatement){ - int rc; - BtShared *pBt = p->pBt; - sqlcipher_sqlite3BtreeEnter(p); - assert( p->inTrans==TRANS_WRITE ); - assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); - assert( iStatement>0 ); - assert( iStatement>p->db->nSavepoint ); - assert( pBt->inTransaction==TRANS_WRITE ); - /* At the pager level, a statement transaction is a savepoint with - ** an index greater than all savepoints created explicitly using - ** SQL statements. It is illegal to open, release or rollback any - ** such savepoints while the statement transaction savepoint is active. - */ - rc = sqlcipher_sqlite3PagerOpenSavepoint(pBt->pPager, iStatement); - sqlcipher_sqlite3BtreeLeave(p); - return rc; -} + /* Lock the destination database, if it is not locked already. */ + if( SQLITE_OK==rc && p->bDestLocked==0 + && SQLITE_OK==(rc = sqlcipher_sqlite3BtreeBeginTrans(p->pDest, 2, + (int*)&p->iDestSchema)) + ){ + p->bDestLocked = 1; + } -/* -** The second argument to this function, op, is always SAVEPOINT_ROLLBACK -** or SAVEPOINT_RELEASE. This function either releases or rolls back the -** savepoint identified by parameter iSavepoint, depending on the value -** of op. -** -** Normally, iSavepoint is greater than or equal to zero. However, if op is -** SAVEPOINT_ROLLBACK, then iSavepoint may also be -1. In this case the -** contents of the entire transaction are rolled back. This is different -** from a normal transaction rollback, as no locks are released and the -** transaction remains open. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ - int rc = SQLITE_OK; - if( p && p->inTrans==TRANS_WRITE ){ - BtShared *pBt = p->pBt; - assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); - assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); - sqlcipher_sqlite3BtreeEnter(p); - if( op==SAVEPOINT_ROLLBACK ){ - rc = saveAllCursors(pBt, 0, 0); + /* Do not allow backup if the destination database is in WAL mode + ** and the page sizes are different between source and destination */ + pgszSrc = sqlcipher_sqlite3BtreeGetPageSize(p->pSrc); + pgszDest = sqlcipher_sqlite3BtreeGetPageSize(p->pDest); + destMode = sqlcipher_sqlite3PagerGetJournalMode(sqlcipher_sqlite3BtreePager(p->pDest)); + if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ + rc = SQLITE_READONLY; } - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); + + /* Now that there is a read-lock on the source database, query the + ** source pager for the number of pages in the database. + */ + nSrcPage = (int)sqlcipher_sqlite3BtreeLastPage(p->pSrc); + assert( nSrcPage>=0 ); + for(ii=0; (nPage<0 || iiiNext<=(Pgno)nSrcPage && !rc; ii++){ + const Pgno iSrcPg = p->iNext; /* Source page number */ + if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){ + DbPage *pSrcPg; /* Source page object */ + rc = sqlcipher_sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg,PAGER_GET_READONLY); + if( rc==SQLITE_OK ){ + rc = backupOnePage(p, iSrcPg, sqlcipher_sqlite3PagerGetData(pSrcPg), 0); + sqlcipher_sqlite3PagerUnref(pSrcPg); + } + } + p->iNext++; } if( rc==SQLITE_OK ){ - if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){ - pBt->nPage = 0; + p->nPagecount = nSrcPage; + p->nRemaining = nSrcPage+1-p->iNext; + if( p->iNext>(Pgno)nSrcPage ){ + rc = SQLITE_DONE; + }else if( !p->isAttached ){ + attachBackupObject(p); } - rc = newDatabase(pBt); - btreeSetNPage(pBt, pBt->pPage1); - - /* pBt->nPage might be zero if the database was corrupt when - ** the transaction was started. Otherwise, it must be at least 1. */ - assert( CORRUPT_DB || pBt->nPage>0 ); } - sqlcipher_sqlite3BtreeLeave(p); - } - return rc; -} -/* -** Create a new cursor for the BTree whose root is on the page -** iTable. If a read-only cursor is requested, it is assumed that -** the caller already has at least a read-only transaction open -** on the database already. If a write-cursor is requested, then -** the caller is assumed to have an open write transaction. -** -** If the BTREE_WRCSR bit of wrFlag is clear, then the cursor can only -** be used for reading. If the BTREE_WRCSR bit is set, then the cursor -** can be used for reading or for writing if other conditions for writing -** are also met. These are the conditions that must be met in order -** for writing to be allowed: -** -** 1: The cursor must have been opened with wrFlag containing BTREE_WRCSR -** -** 2: Other database connections that share the same pager cache -** but which are not in the READ_UNCOMMITTED state may not have -** cursors open with wrFlag==0 on the same table. Otherwise -** the changes made by this write cursor would be visible to -** the read cursors in the other database connection. -** -** 3: The database must be writable (not on read-only media) -** -** 4: There must be an active transaction. -** -** The BTREE_FORDELETE bit of wrFlag may optionally be set if BTREE_WRCSR -** is set. If FORDELETE is set, that is a hint to the implementation that -** this cursor will only be used to seek to and delete entries of an index -** as part of a larger DELETE statement. The FORDELETE hint is not used by -** this implementation. But in a hypothetical alternative storage engine -** in which index entries are automatically deleted when corresponding table -** rows are deleted, the FORDELETE flag is a hint that all SEEK and DELETE -** operations on this cursor can be no-ops and all READ operations can -** return a null row (2-bytes: 0x01 0x00). -** -** No checking is done to make sure that page iTable really is the -** root page of a b-tree. If it is not, then the cursor acquired -** will not work correctly. -** -** It is assumed that the sqlcipher_sqlite3BtreeCursorZero() has been called -** on pCur to initialize the memory space prior to invoking this routine. -*/ -static int btreeCursor( - Btree *p, /* The btree */ - Pgno iTable, /* Root page of table to open */ - int wrFlag, /* 1 to write. 0 read-only */ - struct KeyInfo *pKeyInfo, /* First arg to comparison function */ - BtCursor *pCur /* Space for new cursor */ -){ - BtShared *pBt = p->pBt; /* Shared b-tree handle */ - BtCursor *pX; /* Looping over other all cursors */ + /* Update the schema version field in the destination database. This + ** is to make sure that the schema-version really does change in + ** the case where the source and destination databases have the + ** same schema version. + */ + if( rc==SQLITE_DONE ){ + if( nSrcPage==0 ){ + rc = sqlcipher_sqlite3BtreeNewDb(p->pDest); + nSrcPage = 1; + } + if( rc==SQLITE_OK || rc==SQLITE_DONE ){ + rc = sqlcipher_sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1); + } + if( rc==SQLITE_OK ){ + if( p->pDestDb ){ + sqlcipher_sqlite3ResetAllSchemasOfConnection(p->pDestDb); + } + if( destMode==PAGER_JOURNALMODE_WAL ){ + rc = sqlcipher_sqlite3BtreeSetVersion(p->pDest, 2); + } + } + if( rc==SQLITE_OK ){ + int nDestTruncate; + /* Set nDestTruncate to the final number of pages in the destination + ** database. The complication here is that the destination page + ** size may be different to the source page size. + ** + ** If the source page size is smaller than the destination page size, + ** round up. In this case the call to sqlcipher_sqlite3OsTruncate() below will + ** fix the size of the file. However it is important to call + ** sqlcipher_sqlite3PagerTruncateImage() here so that any pages in the + ** destination file that lie beyond the nDestTruncate page mark are + ** journalled by PagerCommitPhaseOne() before they are destroyed + ** by the file truncation. + */ + assert( pgszSrc==sqlcipher_sqlite3BtreeGetPageSize(p->pSrc) ); + assert( pgszDest==sqlcipher_sqlite3BtreeGetPageSize(p->pDest) ); + if( pgszSrcpDest->pBt) ){ + nDestTruncate--; + } + }else{ + nDestTruncate = nSrcPage * (pgszSrc/pgszDest); + } + assert( nDestTruncate>0 ); - assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); - assert( wrFlag==0 - || wrFlag==BTREE_WRCSR - || wrFlag==(BTREE_WRCSR|BTREE_FORDELETE) - ); + if( pgszSrc= iSize || ( + nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1) + && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest + )); - /* Assert that the caller has opened the required transaction. */ - assert( p->inTrans>TRANS_NONE ); - assert( wrFlag==0 || p->inTrans==TRANS_WRITE ); - assert( pBt->pPage1 && pBt->pPage1->aData ); - assert( wrFlag==0 || (pBt->btsFlags & BTS_READ_ONLY)==0 ); + /* This block ensures that all data required to recreate the original + ** database has been stored in the journal for pDestPager and the + ** journal synced to disk. So at this point we may safely modify + ** the database file in any way, knowing that if a power failure + ** occurs, the original database will be reconstructed from the + ** journal file. */ + sqlcipher_sqlite3PagerPagecount(pDestPager, &nDstPage); + for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){ + if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){ + DbPage *pPg; + rc = sqlcipher_sqlite3PagerGet(pDestPager, iPg, &pPg, 0); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3PagerWrite(pPg); + sqlcipher_sqlite3PagerUnref(pPg); + } + } + } + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3PagerCommitPhaseOne(pDestPager, 0, 1); + } - if( wrFlag ){ - allocateTempSpace(pBt); - if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM_BKPT; - } - if( iTable<=1 ){ - if( iTable<1 ){ - return SQLITE_CORRUPT_BKPT; - }else if( btreePagecount(pBt)==0 ){ - assert( wrFlag==0 ); - iTable = 0; + /* Write the extra pages and truncate the database file as required */ + iEnd = MIN(PENDING_BYTE + pgszDest, iSize); + for( + iOff=PENDING_BYTE+pgszSrc; + rc==SQLITE_OK && iOffpDest, 0)) + ){ + rc = SQLITE_DONE; + } + } } - } - /* Now that no other errors can occur, finish filling in the BtCursor - ** variables and link the cursor into the BtShared list. */ - pCur->pgnoRoot = iTable; - pCur->iPage = -1; - pCur->pKeyInfo = pKeyInfo; - pCur->pBtree = p; - pCur->pBt = pBt; - pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0; - pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY; - /* If there are two or more cursors on the same btree, then all such - ** cursors *must* have the BTCF_Multiple flag set. */ - for(pX=pBt->pCursor; pX; pX=pX->pNext){ - if( pX->pgnoRoot==iTable ){ - pX->curFlags |= BTCF_Multiple; - pCur->curFlags |= BTCF_Multiple; + /* If bCloseTrans is true, then this function opened a read transaction + ** on the source database. Close the read transaction here. There is + ** no need to check the return values of the btree methods here, as + ** "committing" a read-only transaction cannot fail. + */ + if( bCloseTrans ){ + TESTONLY( int rc2 ); + TESTONLY( rc2 = ) sqlcipher_sqlite3BtreeCommitPhaseOne(p->pSrc, 0); + TESTONLY( rc2 |= ) sqlcipher_sqlite3BtreeCommitPhaseTwo(p->pSrc, 0); + assert( rc2==SQLITE_OK ); + } + + if( rc==SQLITE_IOERR_NOMEM ){ + rc = SQLITE_NOMEM_BKPT; } + p->rc = rc; } - pCur->pNext = pBt->pCursor; - pBt->pCursor = pCur; - pCur->eState = CURSOR_INVALID; - return SQLITE_OK; -} -static int btreeCursorWithLock( - Btree *p, /* The btree */ - Pgno iTable, /* Root page of table to open */ - int wrFlag, /* 1 to write. 0 read-only */ - struct KeyInfo *pKeyInfo, /* First arg to comparison function */ - BtCursor *pCur /* Space for new cursor */ -){ - int rc; - sqlcipher_sqlite3BtreeEnter(p); - rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur); - sqlcipher_sqlite3BtreeLeave(p); - return rc; -} -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursor( - Btree *p, /* The btree */ - Pgno iTable, /* Root page of table to open */ - int wrFlag, /* 1 to write. 0 read-only */ - struct KeyInfo *pKeyInfo, /* First arg to xCompare() */ - BtCursor *pCur /* Write new cursor here */ -){ - if( p->sharable ){ - return btreeCursorWithLock(p, iTable, wrFlag, pKeyInfo, pCur); - }else{ - return btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur); + if( p->pDestDb ){ + sqlcipher_sqlite3_mutex_leave(p->pDestDb->mutex); } + sqlcipher_sqlite3BtreeLeave(p->pSrc); + sqlcipher_sqlite3_mutex_leave(p->pSrcDb->mutex); + return rc; } /* -** Return the size of a BtCursor object in bytes. -** -** This interfaces is needed so that users of cursors can preallocate -** sufficient storage to hold a cursor. The BtCursor object is opaque -** to users so they cannot do the sizeof() themselves - they must call -** this routine. +** Release all resources associated with an sqlcipher_sqlite3_backup* handle. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorSize(void){ - return ROUND8(sizeof(BtCursor)); -} +SQLITE_API int sqlcipher_sqlite3_backup_finish(sqlcipher_sqlite3_backup *p){ + sqlcipher_sqlite3_backup **pp; /* Ptr to head of pagers backup list */ + sqlcipher_sqlite3 *pSrcDb; /* Source database connection */ + int rc; /* Value to return */ -/* -** Initialize memory that will be converted into a BtCursor object. -** -** The simple approach here would be to memset() the entire object -** to zero. But it turns out that the apPage[] and aiIdx[] arrays -** do not need to be zeroed and they are large, so we can save a lot -** of run-time by skipping the initialization of those elements. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorZero(BtCursor *p){ - memset(p, 0, offsetof(BtCursor, BTCURSOR_FIRST_UNINIT)); -} + /* Enter the mutexes */ + if( p==0 ) return SQLITE_OK; + pSrcDb = p->pSrcDb; + sqlcipher_sqlite3_mutex_enter(pSrcDb->mutex); + sqlcipher_sqlite3BtreeEnter(p->pSrc); + if( p->pDestDb ){ + sqlcipher_sqlite3_mutex_enter(p->pDestDb->mutex); + } -/* -** Close a cursor. The read lock on the database file is released -** when the last cursor is closed. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCloseCursor(BtCursor *pCur){ - Btree *pBtree = pCur->pBtree; - if( pBtree ){ - BtShared *pBt = pCur->pBt; - sqlcipher_sqlite3BtreeEnter(pBtree); - assert( pBt->pCursor!=0 ); - if( pBt->pCursor==pCur ){ - pBt->pCursor = pCur->pNext; - }else{ - BtCursor *pPrev = pBt->pCursor; - do{ - if( pPrev->pNext==pCur ){ - pPrev->pNext = pCur->pNext; - break; - } - pPrev = pPrev->pNext; - }while( ALWAYS(pPrev) ); + /* Detach this backup from the source pager. */ + if( p->pDestDb ){ + p->pSrc->nBackup--; + } + if( p->isAttached ){ + pp = sqlcipher_sqlite3PagerBackupPtr(sqlcipher_sqlite3BtreePager(p->pSrc)); + assert( pp!=0 ); + while( *pp!=p ){ + pp = &(*pp)->pNext; + assert( pp!=0 ); } - btreeReleaseAllCursorPages(pCur); - unlockBtreeIfUnused(pBt); - sqlcipher_sqlite3_free(pCur->aOverflow); - sqlcipher_sqlite3_free(pCur->pKey); - sqlcipher_sqlite3BtreeLeave(pBtree); - pCur->pBtree = 0; + *pp = p->pNext; } - return SQLITE_OK; -} -/* -** Make sure the BtCursor* given in the argument has a valid -** BtCursor.info structure. If it is not already valid, call -** btreeParseCell() to fill it in. -** -** BtCursor.info is a cache of the information in the current cell. -** Using this cache reduces the number of calls to btreeParseCell(). -*/ -#ifndef NDEBUG - static int cellInfoEqual(CellInfo *a, CellInfo *b){ - if( a->nKey!=b->nKey ) return 0; - if( a->pPayload!=b->pPayload ) return 0; - if( a->nPayload!=b->nPayload ) return 0; - if( a->nLocal!=b->nLocal ) return 0; - if( a->nSize!=b->nSize ) return 0; - return 1; - } - static void assertCellInfo(BtCursor *pCur){ - CellInfo info; - memset(&info, 0, sizeof(info)); - btreeParseCell(pCur->pPage, pCur->ix, &info); - assert( CORRUPT_DB || cellInfoEqual(&info, &pCur->info) ); + /* If a transaction is still open on the Btree, roll it back. */ + sqlcipher_sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0); + + /* Set the error code of the destination database handle. */ + rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc; + if( p->pDestDb ){ + sqlcipher_sqlite3Error(p->pDestDb, rc); + + /* Exit the mutexes and free the backup context structure. */ + sqlcipher_sqlite3LeaveMutexAndCloseZombie(p->pDestDb); } -#else - #define assertCellInfo(x) -#endif -static SQLITE_NOINLINE void getCellInfo(BtCursor *pCur){ - if( pCur->info.nSize==0 ){ - pCur->curFlags |= BTCF_ValidNKey; - btreeParseCell(pCur->pPage,pCur->ix,&pCur->info); - }else{ - assertCellInfo(pCur); + sqlcipher_sqlite3BtreeLeave(p->pSrc); + if( p->pDestDb ){ + /* EVIDENCE-OF: R-64852-21591 The sqlcipher_sqlite3_backup object is created by a + ** call to sqlcipher_sqlite3_backup_init() and is destroyed by a call to + ** sqlcipher_sqlite3_backup_finish(). */ + sqlcipher_sqlite3_free(p); } + sqlcipher_sqlite3LeaveMutexAndCloseZombie(pSrcDb); + return rc; } -#ifndef NDEBUG /* The next routine used only within assert() statements */ /* -** Return true if the given BtCursor is valid. A valid cursor is one -** that is currently pointing to a row in a (non-empty) table. -** This is a verification routine is used only within assert() statements. +** Return the number of pages still to be backed up as of the most recent +** call to sqlcipher_sqlite3_backup_step(). */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorIsValid(BtCursor *pCur){ - return pCur && pCur->eState==CURSOR_VALID; -} -#endif /* NDEBUG */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorIsValidNN(BtCursor *pCur){ - assert( pCur!=0 ); - return pCur->eState==CURSOR_VALID; +SQLITE_API int sqlcipher_sqlite3_backup_remaining(sqlcipher_sqlite3_backup *p){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif + return p->nRemaining; } /* -** Return the value of the integer key or "rowid" for a table btree. -** This routine is only valid for a cursor that is pointing into a -** ordinary table btree. If the cursor points to an index btree or -** is invalid, the result of this routine is undefined. +** Return the total number of pages in the source database as of the most +** recent call to sqlcipher_sqlite3_backup_step(). */ -SQLITE_PRIVATE i64 sqlcipher_sqlite3BtreeIntegerKey(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->curIntKey ); - getCellInfo(pCur); - return pCur->info.nKey; +SQLITE_API int sqlcipher_sqlite3_backup_pagecount(sqlcipher_sqlite3_backup *p){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif + return p->nPagecount; } /* -** Pin or unpin a cursor. +** This function is called after the contents of page iPage of the +** source database have been modified. If page iPage has already been +** copied into the destination database, then the data written to the +** destination is now invalidated. The destination copy of iPage needs +** to be updated with the new data before the backup operation is +** complete. +** +** It is assumed that the mutex associated with the BtShared object +** corresponding to the source database is held when this function is +** called. */ -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorPin(BtCursor *pCur){ - assert( (pCur->curFlags & BTCF_Pinned)==0 ); - pCur->curFlags |= BTCF_Pinned; +static SQLITE_NOINLINE void backupUpdate( + sqlcipher_sqlite3_backup *p, + Pgno iPage, + const u8 *aData +){ + assert( p!=0 ); + do{ + assert( sqlcipher_sqlite3_mutex_held(p->pSrc->pBt->mutex) ); + if( !isFatalError(p->rc) && iPageiNext ){ + /* The backup process p has already copied page iPage. But now it + ** has been modified by a transaction on the source pager. Copy + ** the new data into the backup. + */ + int rc; + assert( p->pDestDb ); + sqlcipher_sqlite3_mutex_enter(p->pDestDb->mutex); + rc = backupOnePage(p, iPage, aData, 1); + sqlcipher_sqlite3_mutex_leave(p->pDestDb->mutex); + assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED ); + if( rc!=SQLITE_OK ){ + p->rc = rc; + } + } + }while( (p = p->pNext)!=0 ); } -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeCursorUnpin(BtCursor *pCur){ - assert( (pCur->curFlags & BTCF_Pinned)!=0 ); - pCur->curFlags &= ~BTCF_Pinned; +SQLITE_PRIVATE void sqlcipher_sqlite3BackupUpdate(sqlcipher_sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){ + if( pBackup ) backupUpdate(pBackup, iPage, aData); } -#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC /* -** Return the offset into the database file for the start of the -** payload to which the cursor is pointing. +** Restart the backup process. This is called when the pager layer +** detects that the database has been modified by an external database +** connection. In this case there is no way of knowing which of the +** pages that have been copied into the destination database are still +** valid and which are not, so the entire process needs to be restarted. +** +** It is assumed that the mutex associated with the BtShared object +** corresponding to the source database is held when this function is +** called. */ -SQLITE_PRIVATE i64 sqlcipher_sqlite3BtreeOffset(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - getCellInfo(pCur); - return (i64)pCur->pBt->pageSize*((i64)pCur->pPage->pgno - 1) + - (i64)(pCur->info.pPayload - pCur->pPage->aData); +SQLITE_PRIVATE void sqlcipher_sqlite3BackupRestart(sqlcipher_sqlite3_backup *pBackup){ + sqlcipher_sqlite3_backup *p; /* Iterator variable */ + for(p=pBackup; p; p=p->pNext){ + assert( sqlcipher_sqlite3_mutex_held(p->pSrc->pBt->mutex) ); + p->iNext = 1; + } } -#endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */ +#ifndef SQLITE_OMIT_VACUUM /* -** Return the number of bytes of payload for the entry that pCur is -** currently pointing to. For table btrees, this will be the amount -** of data. For index btrees, this will be the size of the key. +** Copy the complete content of pBtFrom into pBtTo. A transaction +** must be active for both files. ** -** The caller must guarantee that the cursor is pointing to a non-NULL -** valid entry. In other words, the calling procedure must guarantee -** that the cursor has Cursor.eState==CURSOR_VALID. +** The size of file pTo may be reduced by this operation. If anything +** goes wrong, the transaction on pTo is rolled back. If successful, the +** transaction is committed before returning. */ -SQLITE_PRIVATE u32 sqlcipher_sqlite3BtreePayloadSize(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - getCellInfo(pCur); - return pCur->info.nPayload; +SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ + int rc; + sqlcipher_sqlite3_file *pFd; /* File descriptor for database pTo */ + sqlcipher_sqlite3_backup b; + sqlcipher_sqlite3BtreeEnter(pTo); + sqlcipher_sqlite3BtreeEnter(pFrom); + + assert( sqlcipher_sqlite3BtreeTxnState(pTo)==SQLITE_TXN_WRITE ); + pFd = sqlcipher_sqlite3PagerFile(sqlcipher_sqlite3BtreePager(pTo)); + if( pFd->pMethods ){ + i64 nByte = sqlcipher_sqlite3BtreeGetPageSize(pFrom)*(i64)sqlcipher_sqlite3BtreeLastPage(pFrom); + rc = sqlcipher_sqlite3OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte); + if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; + if( rc ) goto copy_finished; + } + + /* Set up an sqlcipher_sqlite3_backup object. sqlcipher_sqlite3_backup.pDestDb must be set + ** to 0. This is used by the implementations of sqlcipher_sqlite3_backup_step() + ** and sqlcipher_sqlite3_backup_finish() to detect that they are being called + ** from this function, not directly by the user. + */ + memset(&b, 0, sizeof(b)); + b.pSrcDb = pFrom->db; + b.pSrc = pFrom; + b.pDest = pTo; + b.iNext = 1; + +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + sqlcipher_sqlite3PagerAlignReserve(sqlcipher_sqlite3BtreePager(pTo), sqlcipher_sqlite3BtreePager(pFrom)); +#endif +/* END SQLCIPHER */ + + /* 0x7FFFFFFF is the hard limit for the number of pages in a database + ** file. By passing this as the number of pages to copy to + ** sqlcipher_sqlite3_backup_step(), we can guarantee that the copy finishes + ** within a single call (unless an error occurs). The assert() statement + ** checks this assumption - (p->rc) should be set to either SQLITE_DONE + ** or an error code. */ + sqlcipher_sqlite3_backup_step(&b, 0x7FFFFFFF); + assert( b.rc!=SQLITE_OK ); + + rc = sqlcipher_sqlite3_backup_finish(&b); + if( rc==SQLITE_OK ){ + pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; + }else{ + sqlcipher_sqlite3PagerClearCache(sqlcipher_sqlite3BtreePager(b.pDest)); + } + + assert( sqlcipher_sqlite3BtreeTxnState(pTo)!=SQLITE_TXN_WRITE ); +copy_finished: + sqlcipher_sqlite3BtreeLeave(pFrom); + sqlcipher_sqlite3BtreeLeave(pTo); + return rc; } +#endif /* SQLITE_OMIT_VACUUM */ +/************** End of backup.c **********************************************/ +/************** Begin file vdbemem.c *****************************************/ /* -** Return an upper bound on the size of any record for the table -** that the cursor is pointing into. +** 2004 May 26 ** -** This is an optimization. Everything will still work if this -** routine always returns 2147483647 (which is the largest record -** that SQLite can handle) or more. But returning a smaller value might -** prevent large memory allocations when trying to interpret a -** corrupt datrabase. +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** The current implementation merely returns the size of the underlying -** database file. +** 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 use to manipulate "Mem" structure. A "Mem" +** stores a single value in the VDBE. Mem is an opaque structure visible +** only within the VDBE. Interface routines refer to a Mem using the +** name sqlite_value */ -SQLITE_PRIVATE sqlcipher_sqlite3_int64 sqlcipher_sqlite3BtreeMaxRecordSize(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - return pCur->pBt->pageSize * (sqlcipher_sqlite3_int64)pCur->pBt->nPage; -} +/* #include "sqliteInt.h" */ +/* #include "vdbeInt.h" */ + +/* True if X is a power of two. 0 is considered a power of two here. +** In other words, return true if X has at most one bit set. +*/ +#define ISPOWEROF2(X) (((X)&((X)-1))==0) +#ifdef SQLITE_DEBUG /* -** Given the page number of an overflow page in the database (parameter -** ovfl), this function finds the page number of the next page in the -** linked list of overflow pages. If possible, it uses the auto-vacuum -** pointer-map data instead of reading the content of page ovfl to do so. -** -** If an error occurs an SQLite error code is returned. Otherwise: -** -** The page number of the next overflow page in the linked list is -** written to *pPgnoNext. If page ovfl is the last page in its linked -** list, *pPgnoNext is set to zero. +** Check invariants on a Mem object. ** -** If ppPage is not NULL, and a reference to the MemPage object corresponding -** to page number pOvfl was obtained, then *ppPage is set to point to that -** reference. It is the responsibility of the caller to call releasePage() -** on *ppPage to free the reference. In no reference was obtained (because -** the pointer-map was used to obtain the value for *pPgnoNext), then -** *ppPage is set to zero. +** This routine is intended for use inside of assert() statements, like +** this: assert( sqlcipher_sqlite3VdbeCheckMemInvariants(pMem) ); */ -static int getOverflowPage( - BtShared *pBt, /* The database file */ - Pgno ovfl, /* Current overflow page number */ - MemPage **ppPage, /* OUT: MemPage handle (may be NULL) */ - Pgno *pPgnoNext /* OUT: Next overflow page number */ -){ - Pgno next = 0; - MemPage *pPage = 0; - int rc = SQLITE_OK; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCheckMemInvariants(Mem *p){ + /* If MEM_Dyn is set then Mem.xDel!=0. + ** Mem.xDel might not be initialized if MEM_Dyn is clear. + */ + assert( (p->flags & MEM_Dyn)==0 || p->xDel!=0 ); - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - assert(pPgnoNext); + /* MEM_Dyn may only be set if Mem.szMalloc==0. In this way we + ** ensure that if Mem.szMalloc>0 then it is safe to do + ** Mem.z = Mem.zMalloc without having to check Mem.flags&MEM_Dyn. + ** That saves a few cycles in inner loops. */ + assert( (p->flags & MEM_Dyn)==0 || p->szMalloc==0 ); -#ifndef SQLITE_OMIT_AUTOVACUUM - /* Try to find the next page in the overflow list using the - ** autovacuum pointer-map pages. Guess that the next page in - ** the overflow list is page number (ovfl+1). If that guess turns - ** out to be wrong, fall back to loading the data of page - ** number ovfl to determine the next page number. - */ - if( pBt->autoVacuum ){ - Pgno pgno; - Pgno iGuess = ovfl+1; - u8 eType; + /* Cannot have more than one of MEM_Int, MEM_Real, or MEM_IntReal */ + assert( ISPOWEROF2(p->flags & (MEM_Int|MEM_Real|MEM_IntReal)) ); - while( PTRMAP_ISPAGE(pBt, iGuess) || iGuess==PENDING_BYTE_PAGE(pBt) ){ - iGuess++; - } + if( p->flags & MEM_Null ){ + /* Cannot be both MEM_Null and some other type */ + assert( (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob|MEM_Agg))==0 ); + + /* If MEM_Null is set, then either the value is a pure NULL (the usual + ** case) or it is a pointer set using sqlcipher_sqlite3_bind_pointer() or + ** sqlcipher_sqlite3_result_pointer(). If a pointer, then MEM_Term must also be + ** set. + */ + if( (p->flags & (MEM_Term|MEM_Subtype))==(MEM_Term|MEM_Subtype) ){ + /* This is a pointer type. There may be a flag to indicate what to + ** do with the pointer. */ + assert( ((p->flags&MEM_Dyn)!=0 ? 1 : 0) + + ((p->flags&MEM_Ephem)!=0 ? 1 : 0) + + ((p->flags&MEM_Static)!=0 ? 1 : 0) <= 1 ); - if( iGuess<=btreePagecount(pBt) ){ - rc = ptrmapGet(pBt, iGuess, &eType, &pgno); - if( rc==SQLITE_OK && eType==PTRMAP_OVERFLOW2 && pgno==ovfl ){ - next = iGuess; - rc = SQLITE_DONE; - } + /* No other bits set */ + assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype|MEM_FromBind + |MEM_Dyn|MEM_Ephem|MEM_Static))==0 ); + }else{ + /* A pure NULL might have other flags, such as MEM_Static, MEM_Dyn, + ** MEM_Ephem, MEM_Cleared, or MEM_Subtype */ } + }else{ + /* The MEM_Cleared bit is only allowed on NULLs */ + assert( (p->flags & MEM_Cleared)==0 ); } -#endif - assert( next==0 || rc==SQLITE_DONE ); - if( rc==SQLITE_OK ){ - rc = btreeGetPage(pBt, ovfl, &pPage, (ppPage==0) ? PAGER_GET_READONLY : 0); - assert( rc==SQLITE_OK || pPage==0 ); - if( rc==SQLITE_OK ){ - next = get4byte(pPage->aData); - } - } + /* The szMalloc field holds the correct memory allocation size */ + assert( p->szMalloc==0 + || (p->flags==MEM_Undefined + && p->szMalloc<=sqlcipher_sqlite3DbMallocSize(p->db,p->zMalloc)) + || p->szMalloc==sqlcipher_sqlite3DbMallocSize(p->db,p->zMalloc)); - *pPgnoNext = next; - if( ppPage ){ - *ppPage = pPage; - }else{ - releasePage(pPage); + /* If p holds a string or blob, the Mem.z must point to exactly + ** one of the following: + ** + ** (1) Memory in Mem.zMalloc and managed by the Mem object + ** (2) Memory to be freed using Mem.xDel + ** (3) An ephemeral string or blob + ** (4) A static string or blob + */ + if( (p->flags & (MEM_Str|MEM_Blob)) && p->n>0 ){ + assert( + ((p->szMalloc>0 && p->z==p->zMalloc)? 1 : 0) + + ((p->flags&MEM_Dyn)!=0 ? 1 : 0) + + ((p->flags&MEM_Ephem)!=0 ? 1 : 0) + + ((p->flags&MEM_Static)!=0 ? 1 : 0) == 1 + ); } - return (rc==SQLITE_DONE ? SQLITE_OK : rc); + return 1; } +#endif /* -** Copy data from a buffer to a page, or from a page to a buffer. -** -** pPayload is a pointer to data stored on database page pDbPage. -** If argument eOp is false, then nByte bytes of data are copied -** from pPayload to the buffer pointed at by pBuf. If eOp is true, -** then sqlcipher_sqlite3PagerWrite() is called on pDbPage and nByte bytes -** of data are copied from the buffer pBuf to pPayload. -** -** SQLITE_OK is returned on success, otherwise an error code. +** Render a Mem object which is one of MEM_Int, MEM_Real, or MEM_IntReal +** into a buffer. */ -static int copyPayload( - void *pPayload, /* Pointer to page data */ - void *pBuf, /* Pointer to buffer */ - int nByte, /* Number of bytes to copy */ - int eOp, /* 0 -> copy from page, 1 -> copy to page */ - DbPage *pDbPage /* Page containing pPayload */ -){ - if( eOp ){ - /* Copy data from buffer to page (a write operation) */ - int rc = sqlcipher_sqlite3PagerWrite(pDbPage); - if( rc!=SQLITE_OK ){ - return rc; - } - memcpy(pPayload, pBuf, nByte); +static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ + StrAccum acc; + assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) ); + assert( sz>22 ); + if( p->flags & MEM_Int ){ +#if GCC_VERSION>=7000000 + /* Work-around for GCC bug + ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96270 */ + i64 x; + assert( (p->flags&MEM_Int)*2==sizeof(x) ); + memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2); + sqlcipher_sqlite3Int64ToText(x, zBuf); +#else + sqlcipher_sqlite3Int64ToText(p->u.i, zBuf); +#endif }else{ - /* Copy data from page to buffer (a read operation) */ - memcpy(pBuf, pPayload, nByte); + sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); + sqlcipher_sqlite3_str_appendf(&acc, "%!.15g", + (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r); + assert( acc.zText==zBuf && acc.mxAlloc<=0 ); + zBuf[acc.nChar] = 0; /* Fast version of sqlcipher_sqlite3StrAccumFinish(&acc) */ } - return SQLITE_OK; } +#ifdef SQLITE_DEBUG /* -** This function is used to read or overwrite payload information -** for the entry that the pCur cursor is pointing to. The eOp -** argument is interpreted as follows: +** Validity checks on pMem. pMem holds a string. ** -** 0: The operation is a read. Populate the overflow cache. -** 1: The operation is a write. Populate the overflow cache. +** (1) Check that string value of pMem agrees with its integer or real value. +** (2) Check that the string is correctly zero terminated ** -** A total of "amt" bytes are read or written beginning at "offset". -** Data is read to or from the buffer pBuf. +** A single int or real value always converts to the same strings. But +** many different strings can be converted into the same int or real. +** If a table contains a numeric value and an index is based on the +** corresponding string value, then it is important that the string be +** derived from the numeric value, not the other way around, to ensure +** that the index and table are consistent. See ticket +** https://www.sqlite.org/src/info/343634942dd54ab (2018-01-31) for +** an example. ** -** The content being read or written might appear on the main page -** or be scattered out on multiple overflow pages. +** This routine looks at pMem to verify that if it has both a numeric +** representation and a string representation then the string rep has +** been derived from the numeric and not the other way around. It returns +** true if everything is ok and false if there is a problem. ** -** If the current cursor entry uses one or more overflow pages -** this function may allocate space for and lazily populate -** the overflow page-list cache array (BtCursor.aOverflow). -** Subsequent calls use this cache to make seeking to the supplied offset -** more efficient. +** This routine is for use inside of assert() statements only. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemValidStrRep(Mem *p){ + char zBuf[100]; + char *z; + int i, j, incr; + if( (p->flags & MEM_Str)==0 ) return 1; + if( p->flags & MEM_Term ){ + /* Insure that the string is properly zero-terminated. Pay particular + ** attention to the case where p->n is odd */ + if( p->szMalloc>0 && p->z==p->zMalloc ){ + assert( p->enc==SQLITE_UTF8 || p->szMalloc >= ((p->n+1)&~1)+2 ); + assert( p->enc!=SQLITE_UTF8 || p->szMalloc >= p->n+1 ); + } + assert( p->z[p->n]==0 ); + assert( p->enc==SQLITE_UTF8 || p->z[(p->n+1)&~1]==0 ); + assert( p->enc==SQLITE_UTF8 || p->z[((p->n+1)&~1)+1]==0 ); + } + if( (p->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ) return 1; + vdbeMemRenderNum(sizeof(zBuf), zBuf, p); + z = p->z; + i = j = 0; + incr = 1; + if( p->enc!=SQLITE_UTF8 ){ + incr = 2; + if( p->enc==SQLITE_UTF16BE ) z++; + } + while( zBuf[j] ){ + if( zBuf[j++]!=z[i] ) return 0; + i += incr; + } + return 1; +} +#endif /* SQLITE_DEBUG */ + +/* +** If pMem is an object with a valid string representation, this routine +** ensures the internal encoding for the string representation is +** 'desiredEnc', one of SQLITE_UTF8, SQLITE_UTF16LE or SQLITE_UTF16BE. ** -** Once an overflow page-list cache has been allocated, it must be -** invalidated if some other cursor writes to the same table, or if -** the cursor is moved to a different row. Additionally, in auto-vacuum -** mode, the following events may invalidate an overflow page-list cache. +** If pMem is not a string object, or the encoding of the string +** representation is already stored using the requested encoding, then this +** routine is a no-op. ** -** * An incremental vacuum, -** * A commit in auto_vacuum="full" mode, -** * Creating a table (may require moving an overflow page). +** SQLITE_OK is returned if the conversion is successful (or not required). +** SQLITE_NOMEM may be returned if a malloc() fails during conversion +** between formats. */ -static int accessPayload( - BtCursor *pCur, /* Cursor pointing to entry to read from */ - u32 offset, /* Begin reading this far into payload */ - u32 amt, /* Read this many bytes */ - unsigned char *pBuf, /* Write the bytes into this buffer */ - int eOp /* zero to read. non-zero to write. */ -){ - unsigned char *aPayload; - int rc = SQLITE_OK; - int iIdx = 0; - MemPage *pPage = pCur->pPage; /* Btree page of current entry */ - BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */ -#ifdef SQLITE_DIRECT_OVERFLOW_READ - unsigned char * const pBufStart = pBuf; /* Start of original out buffer */ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ +#ifndef SQLITE_OMIT_UTF16 + int rc; #endif - - assert( pPage ); - assert( eOp==0 || eOp==1 ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->ixnCell ); - assert( cursorHoldsMutex(pCur) ); - - getCellInfo(pCur); - aPayload = pCur->info.pPayload; - assert( offset+amt <= pCur->info.nPayload ); - - assert( aPayload > pPage->aData ); - if( (uptr)(aPayload - pPage->aData) > (pBt->usableSize - pCur->info.nLocal) ){ - /* Trying to read or write past the end of the data is an error. The - ** conditional above is really: - ** &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] - ** but is recast into its current form to avoid integer overflow problems - */ - return SQLITE_CORRUPT_PAGE(pPage); + assert( pMem!=0 ); + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); + assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE + || desiredEnc==SQLITE_UTF16BE ); + if( !(pMem->flags&MEM_Str) ){ + pMem->enc = desiredEnc; + return SQLITE_OK; } - - /* Check if data must be read/written to/from the btree page itself. */ - if( offsetinfo.nLocal ){ - int a = amt; - if( a+offset>pCur->info.nLocal ){ - a = pCur->info.nLocal - offset; - } - rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage); - offset = 0; - pBuf += a; - amt -= a; - }else{ - offset -= pCur->info.nLocal; + if( pMem->enc==desiredEnc ){ + return SQLITE_OK; } + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); +#ifdef SQLITE_OMIT_UTF16 + return SQLITE_ERROR; +#else + /* MemTranslate() may return SQLITE_OK or SQLITE_NOMEM. If NOMEM is returned, + ** then the encoding of the value may not have changed. + */ + rc = sqlcipher_sqlite3VdbeMemTranslate(pMem, (u8)desiredEnc); + assert(rc==SQLITE_OK || rc==SQLITE_NOMEM); + assert(rc==SQLITE_OK || pMem->enc!=desiredEnc); + assert(rc==SQLITE_NOMEM || pMem->enc==desiredEnc); + return rc; +#endif +} - if( rc==SQLITE_OK && amt>0 ){ - const u32 ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */ - Pgno nextPage; +/* +** Make sure pMem->z points to a writable allocation of at least n bytes. +** +** If the bPreserve argument is true, then copy of the content of +** pMem->z into the new allocation. pMem must be either a string or +** blob if bPreserve is true. If bPreserve is false, any prior content +** in pMem->z is discarded. +*/ +SQLITE_PRIVATE SQLITE_NOINLINE int sqlcipher_sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){ + assert( sqlcipher_sqlite3VdbeCheckMemInvariants(pMem) ); + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); + testcase( pMem->db==0 ); - nextPage = get4byte(&aPayload[pCur->info.nLocal]); + /* If the bPreserve flag is set to true, then the memory cell must already + ** contain a valid string or blob value. */ + assert( bPreserve==0 || pMem->flags&(MEM_Blob|MEM_Str) ); + testcase( bPreserve && pMem->z==0 ); - /* If the BtCursor.aOverflow[] has not been allocated, allocate it now. - ** - ** The aOverflow[] array is sized at one entry for each overflow page - ** in the overflow chain. The page number of the first overflow page is - ** stored in aOverflow[0], etc. A value of 0 in the aOverflow[] array - ** means "not yet known" (the cache is lazily populated). - */ - if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){ - int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; - if( pCur->aOverflow==0 - || nOvfl*(int)sizeof(Pgno) > sqlcipher_sqlite3MallocSize(pCur->aOverflow) - ){ - Pgno *aNew = (Pgno*)sqlcipher_sqlite3Realloc( - pCur->aOverflow, nOvfl*2*sizeof(Pgno) - ); - if( aNew==0 ){ - return SQLITE_NOMEM_BKPT; - }else{ - pCur->aOverflow = aNew; - } - } - memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno)); - pCur->curFlags |= BTCF_ValidOvfl; + assert( pMem->szMalloc==0 + || (pMem->flags==MEM_Undefined + && pMem->szMalloc<=sqlcipher_sqlite3DbMallocSize(pMem->db,pMem->zMalloc)) + || pMem->szMalloc==sqlcipher_sqlite3DbMallocSize(pMem->db,pMem->zMalloc)); + if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){ + if( pMem->db ){ + pMem->z = pMem->zMalloc = sqlcipher_sqlite3DbReallocOrFree(pMem->db, pMem->z, n); }else{ - /* If the overflow page-list cache has been allocated and the - ** entry for the first required overflow page is valid, skip - ** directly to it. - */ - if( pCur->aOverflow[offset/ovflSize] ){ - iIdx = (offset/ovflSize); - nextPage = pCur->aOverflow[iIdx]; - offset = (offset%ovflSize); - } + pMem->zMalloc = sqlcipher_sqlite3Realloc(pMem->z, n); + if( pMem->zMalloc==0 ) sqlcipher_sqlite3_free(pMem->z); + pMem->z = pMem->zMalloc; } + bPreserve = 0; + }else{ + if( pMem->szMalloc>0 ) sqlcipher_sqlite3DbFreeNN(pMem->db, pMem->zMalloc); + pMem->zMalloc = sqlcipher_sqlite3DbMallocRaw(pMem->db, n); + } + if( pMem->zMalloc==0 ){ + sqlcipher_sqlite3VdbeMemSetNull(pMem); + pMem->z = 0; + pMem->szMalloc = 0; + return SQLITE_NOMEM_BKPT; + }else{ + pMem->szMalloc = sqlcipher_sqlite3DbMallocSize(pMem->db, pMem->zMalloc); + } - assert( rc==SQLITE_OK && amt>0 ); - while( nextPage ){ - /* If required, populate the overflow page-list cache. */ - if( nextPage > pBt->nPage ) return SQLITE_CORRUPT_BKPT; - assert( pCur->aOverflow[iIdx]==0 - || pCur->aOverflow[iIdx]==nextPage - || CORRUPT_DB ); - pCur->aOverflow[iIdx] = nextPage; - - if( offset>=ovflSize ){ - /* The only reason to read this page is to obtain the page - ** number for the next page in the overflow chain. The page - ** data is not required. So first try to lookup the overflow - ** page-list cache, if any, then fall back to the getOverflowPage() - ** function. - */ - assert( pCur->curFlags & BTCF_ValidOvfl ); - assert( pCur->pBtree->db==pBt->db ); - if( pCur->aOverflow[iIdx+1] ){ - nextPage = pCur->aOverflow[iIdx+1]; - }else{ - rc = getOverflowPage(pBt, nextPage, 0, &nextPage); - } - offset -= ovflSize; - }else{ - /* Need to read this page properly. It contains some of the - ** range of data that is being read (eOp==0) or written (eOp!=0). - */ - int a = amt; - if( a + offset > ovflSize ){ - a = ovflSize - offset; - } + if( bPreserve && pMem->z ){ + assert( pMem->z!=pMem->zMalloc ); + memcpy(pMem->zMalloc, pMem->z, pMem->n); + } + if( (pMem->flags&MEM_Dyn)!=0 ){ + assert( pMem->xDel!=0 && pMem->xDel!=SQLITE_DYNAMIC ); + pMem->xDel((void *)(pMem->z)); + } -#ifdef SQLITE_DIRECT_OVERFLOW_READ - /* If all the following are true: - ** - ** 1) this is a read operation, and - ** 2) data is required from the start of this overflow page, and - ** 3) there are no dirty pages in the page-cache - ** 4) the database is file-backed, and - ** 5) the page is not in the WAL file - ** 6) at least 4 bytes have already been read into the output buffer - ** - ** then data can be read directly from the database file into the - ** output buffer, bypassing the page-cache altogether. This speeds - ** up loading large records that span many overflow pages. - */ - if( eOp==0 /* (1) */ - && offset==0 /* (2) */ - && sqlcipher_sqlite3PagerDirectReadOk(pBt->pPager, nextPage) /* (3,4,5) */ - && &pBuf[-4]>=pBufStart /* (6) */ - ){ - sqlcipher_sqlite3_file *fd = sqlcipher_sqlite3PagerFile(pBt->pPager); - u8 aSave[4]; - u8 *aWrite = &pBuf[-4]; - assert( aWrite>=pBufStart ); /* due to (6) */ - memcpy(aSave, aWrite, 4); - rc = sqlcipher_sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); - if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT; - nextPage = get4byte(aWrite); - memcpy(aWrite, aSave, 4); - }else -#endif + pMem->z = pMem->zMalloc; + pMem->flags &= ~(MEM_Dyn|MEM_Ephem|MEM_Static); + return SQLITE_OK; +} - { - DbPage *pDbPage; - rc = sqlcipher_sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage, - (eOp==0 ? PAGER_GET_READONLY : 0) - ); - if( rc==SQLITE_OK ){ - aPayload = sqlcipher_sqlite3PagerGetData(pDbPage); - nextPage = get4byte(aPayload); - rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage); - sqlcipher_sqlite3PagerUnref(pDbPage); - offset = 0; - } - } - amt -= a; - if( amt==0 ) return rc; - pBuf += a; - } - if( rc ) break; - iIdx++; - } +/* +** Change the pMem->zMalloc allocation to be at least szNew bytes. +** If pMem->zMalloc already meets or exceeds the requested size, this +** routine is a no-op. +** +** Any prior string or blob content in the pMem object may be discarded. +** The pMem->xDel destructor is called, if it exists. Though MEM_Str +** and MEM_Blob values may be discarded, MEM_Int, MEM_Real, MEM_IntReal, +** and MEM_Null values are preserved. +** +** Return SQLITE_OK on success or an error code (probably SQLITE_NOMEM) +** if unable to complete the resizing. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){ + assert( CORRUPT_DB || szNew>0 ); + assert( (pMem->flags & MEM_Dyn)==0 || pMem->szMalloc==0 ); + if( pMem->szMallocflags & MEM_Dyn)==0 ); + pMem->z = pMem->zMalloc; + pMem->flags &= (MEM_Null|MEM_Int|MEM_Real|MEM_IntReal); + return SQLITE_OK; +} - if( rc==SQLITE_OK && amt>0 ){ - /* Overflow chain ends prematurely */ - return SQLITE_CORRUPT_PAGE(pPage); +/* +** It is already known that pMem contains an unterminated string. +** Add the zero terminator. +** +** Three bytes of zero are added. In this way, there is guaranteed +** to be a double-zero byte at an even byte boundary in order to +** terminate a UTF16 string, even if the initial size of the buffer +** is an odd number of bytes. +*/ +static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){ + if( sqlcipher_sqlite3VdbeMemGrow(pMem, pMem->n+3, 1) ){ + return SQLITE_NOMEM_BKPT; } - return rc; + pMem->z[pMem->n] = 0; + pMem->z[pMem->n+1] = 0; + pMem->z[pMem->n+2] = 0; + pMem->flags |= MEM_Term; + return SQLITE_OK; } /* -** Read part of the payload for the row at which that cursor pCur is currently -** pointing. "amt" bytes will be transferred into pBuf[]. The transfer -** begins at "offset". -** -** pCur can be pointing to either a table or an index b-tree. -** If pointing to a table btree, then the content section is read. If -** pCur is pointing to an index b-tree then the key section is read. -** -** For sqlcipher_sqlite3BtreePayload(), the caller must ensure that pCur is pointing -** to a valid row in the table. For sqlcipher_sqlite3BtreePayloadChecked(), the -** cursor might be invalid or might need to be restored before being read. +** Change pMem so that its MEM_Str or MEM_Blob value is stored in +** MEM.zMalloc, where it can be safely written. ** -** Return SQLITE_OK on success or an error code if anything goes -** wrong. An error is returned if "offset+amt" is larger than -** the available payload. +** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreePayload(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>=0 && pCur->pPage ); - assert( pCur->ixpPage->nCell ); - return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemMakeWriteable(Mem *pMem){ + assert( pMem!=0 ); + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); + if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){ + if( ExpandBlob(pMem) ) return SQLITE_NOMEM; + if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){ + int rc = vdbeMemAddTerminator(pMem); + if( rc ) return rc; + } + } + pMem->flags &= ~MEM_Ephem; +#ifdef SQLITE_DEBUG + pMem->pScopyFrom = 0; +#endif + + return SQLITE_OK; } /* -** This variant of sqlcipher_sqlite3BtreePayload() works even if the cursor has not -** in the CURSOR_VALID state. It is only used by the sqlcipher_sqlite3_blob_read() -** interface. +** If the given Mem* has a zero-filled tail, turn it into an ordinary +** blob stored in dynamically allocated space. */ #ifndef SQLITE_OMIT_INCRBLOB -static SQLITE_NOINLINE int accessPayloadChecked( - BtCursor *pCur, - u32 offset, - u32 amt, - void *pBuf -){ - int rc; - if ( pCur->eState==CURSOR_INVALID ){ - return SQLITE_ABORT; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemExpandBlob(Mem *pMem){ + int nByte; + assert( pMem!=0 ); + assert( pMem->flags & MEM_Zero ); + assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) ); + testcase( sqlcipher_sqlite3_value_nochange(pMem) ); + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + + /* Set nByte to the number of bytes required to store the expanded blob. */ + nByte = pMem->n + pMem->u.nZero; + if( nByte<=0 ){ + if( (pMem->flags & MEM_Blob)==0 ) return SQLITE_OK; + nByte = 1; } - assert( cursorOwnsBtShared(pCur) ); - rc = btreeRestoreCursorPosition(pCur); - return rc ? rc : accessPayload(pCur, offset, amt, pBuf, 0); + if( sqlcipher_sqlite3VdbeMemGrow(pMem, nByte, 1) ){ + return SQLITE_NOMEM_BKPT; + } + assert( pMem->z!=0 ); + assert( sqlcipher_sqlite3DbMallocSize(pMem->db,pMem->z) >= nByte ); + + memset(&pMem->z[pMem->n], 0, pMem->u.nZero); + pMem->n += pMem->u.nZero; + pMem->flags &= ~(MEM_Zero|MEM_Term); + return SQLITE_OK; } -SQLITE_PRIVATE int sqlcipher_sqlite3BtreePayloadChecked(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - if( pCur->eState==CURSOR_VALID ){ - assert( cursorOwnsBtShared(pCur) ); - return accessPayload(pCur, offset, amt, pBuf, 0); +#endif + +/* +** Make sure the given Mem is \u0000 terminated. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemNulTerminate(Mem *pMem){ + assert( pMem!=0 ); + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + testcase( (pMem->flags & (MEM_Term|MEM_Str))==(MEM_Term|MEM_Str) ); + testcase( (pMem->flags & (MEM_Term|MEM_Str))==0 ); + if( (pMem->flags & (MEM_Term|MEM_Str))!=MEM_Str ){ + return SQLITE_OK; /* Nothing to do */ }else{ - return accessPayloadChecked(pCur, offset, amt, pBuf); + return vdbeMemAddTerminator(pMem); } } -#endif /* SQLITE_OMIT_INCRBLOB */ /* -** Return a pointer to payload information from the entry that the -** pCur cursor is pointing to. The pointer is to the beginning of -** the key if index btrees (pPage->intKey==0) and is the data for -** table btrees (pPage->intKey==1). The number of bytes of available -** key/data is written into *pAmt. If *pAmt==0, then the value -** returned will not be a valid pointer. +** Add MEM_Str to the set of representations for the given Mem. This +** routine is only called if pMem is a number of some kind, not a NULL +** or a BLOB. ** -** This routine is an optimization. It is common for the entire key -** and data to fit on the local page and for there to be no overflow -** pages. When that is so, this routine can be used to access the -** key and data without making a copy. If the key and/or data spills -** onto overflow pages, then accessPayload() must be used to reassemble -** the key/data and copy it into a preallocated buffer. +** Existing representations MEM_Int, MEM_Real, or MEM_IntReal are invalidated +** if bForce is true but are retained if bForce is false. ** -** The pointer returned by this routine looks directly into the cached -** page of the database. The data might change or move the next time -** any btree routine is called. +** A MEM_Null value will never be passed to this function. This function is +** used for converting values to text for returning to the user (i.e. via +** sqlcipher_sqlite3_value_text()), or for ensuring that values to be used as btree +** keys are strings. In the former case a NULL pointer is returned the +** user and the latter is an internal programming error. */ -static const void *fetchPayload( - BtCursor *pCur, /* Cursor pointing to entry to read from */ - u32 *pAmt /* Write the number of available bytes here */ -){ - int amt; - assert( pCur!=0 && pCur->iPage>=0 && pCur->pPage); - assert( pCur->eState==CURSOR_VALID ); - assert( sqlcipher_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - assert( cursorOwnsBtShared(pCur) ); - assert( pCur->ixpPage->nCell ); - assert( pCur->info.nSize>0 ); - assert( pCur->info.pPayload>pCur->pPage->aData || CORRUPT_DB ); - assert( pCur->info.pPayloadpPage->aDataEnd ||CORRUPT_DB); - amt = pCur->info.nLocal; - if( amt>(int)(pCur->pPage->aDataEnd - pCur->info.pPayload) ){ - /* There is too little space on the page for the expected amount - ** of local content. Database must be corrupt. */ - assert( CORRUPT_DB ); - amt = MAX(0, (int)(pCur->pPage->aDataEnd - pCur->info.pPayload)); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ + const int nByte = 32; + + assert( pMem!=0 ); + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + assert( !(pMem->flags&MEM_Zero) ); + assert( !(pMem->flags&(MEM_Str|MEM_Blob)) ); + assert( pMem->flags&(MEM_Int|MEM_Real|MEM_IntReal) ); + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + + + if( sqlcipher_sqlite3VdbeMemClearAndResize(pMem, nByte) ){ + pMem->enc = 0; + return SQLITE_NOMEM_BKPT; } - *pAmt = (u32)amt; - return (void*)pCur->info.pPayload; -} + vdbeMemRenderNum(nByte, pMem->z, pMem); + assert( pMem->z!=0 ); + pMem->n = sqlcipher_sqlite3Strlen30NN(pMem->z); + pMem->enc = SQLITE_UTF8; + pMem->flags |= MEM_Str|MEM_Term; + if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); + sqlcipher_sqlite3VdbeChangeEncoding(pMem, enc); + return SQLITE_OK; +} /* -** For the entry that cursor pCur is point to, return as -** many bytes of the key or data as are available on the local -** b-tree page. Write the number of available bytes into *pAmt. -** -** The pointer returned is ephemeral. The key/data may move -** or be destroyed on the next call to any Btree routine, -** including calls from other threads against the same cache. -** Hence, a mutex on the BtShared should be held prior to calling -** this routine. +** Memory cell pMem contains the context of an aggregate function. +** This routine calls the finalize method for that function. The +** result of the aggregate is stored back into pMem. ** -** These routines is used to get quick access to key and data -** in the common case where no overflow pages are used. +** Return SQLITE_ERROR if the finalizer reports an error. SQLITE_OK +** otherwise. */ -SQLITE_PRIVATE const void *sqlcipher_sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){ - return fetchPayload(pCur, pAmt); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){ + sqlcipher_sqlite3_context ctx; + Mem t; + assert( pFunc!=0 ); + assert( pMem!=0 ); + assert( pMem->db!=0 ); + assert( pFunc->xFinalize!=0 ); + assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef ); + assert( sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + memset(&ctx, 0, sizeof(ctx)); + memset(&t, 0, sizeof(t)); + t.flags = MEM_Null; + t.db = pMem->db; + ctx.pOut = &t; + ctx.pMem = pMem; + ctx.pFunc = pFunc; + ctx.enc = ENC(t.db); + pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */ + assert( (pMem->flags & MEM_Dyn)==0 ); + if( pMem->szMalloc>0 ) sqlcipher_sqlite3DbFreeNN(pMem->db, pMem->zMalloc); + memcpy(pMem, &t, sizeof(t)); + return ctx.isError; } - /* -** Move the cursor down to a new child page. The newPgno argument is the -** page number of the child page to move to. +** Memory cell pAccum contains the context of an aggregate function. +** This routine calls the xValue method for that function and stores +** the results in memory cell pMem. ** -** This function returns SQLITE_CORRUPT if the page-header flags field of -** the new child page does not match the flags field of the parent (i.e. -** if an intkey page appears to be the parent of a non-intkey page, or -** vice-versa). +** SQLITE_ERROR is returned if xValue() reports an error. SQLITE_OK +** otherwise. */ -static int moveToChild(BtCursor *pCur, u32 newPgno){ - BtShared *pBt = pCur->pBt; - - assert( cursorOwnsBtShared(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPageiPage>=0 ); - if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){ - return SQLITE_CORRUPT_BKPT; - } - pCur->info.nSize = 0; - pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); - pCur->aiIdx[pCur->iPage] = pCur->ix; - pCur->apPage[pCur->iPage] = pCur->pPage; - pCur->ix = 0; - pCur->iPage++; - return getAndInitPage(pBt, newPgno, &pCur->pPage, pCur, pCur->curPagerFlags); +#ifndef SQLITE_OMIT_WINDOWFUNC +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pFunc){ + sqlcipher_sqlite3_context ctx; + assert( pFunc!=0 ); + assert( pFunc->xValue!=0 ); + assert( (pAccum->flags & MEM_Null)!=0 || pFunc==pAccum->u.pDef ); + assert( pAccum->db!=0 ); + assert( sqlcipher_sqlite3_mutex_held(pAccum->db->mutex) ); + memset(&ctx, 0, sizeof(ctx)); + sqlcipher_sqlite3VdbeMemSetNull(pOut); + ctx.pOut = pOut; + ctx.pMem = pAccum; + ctx.pFunc = pFunc; + ctx.enc = ENC(pAccum->db); + pFunc->xValue(&ctx); + return ctx.isError; } +#endif /* SQLITE_OMIT_WINDOWFUNC */ -#ifdef SQLITE_DEBUG /* -** Page pParent is an internal (non-leaf) tree page. This function -** asserts that page number iChild is the left-child if the iIdx'th -** cell in page pParent. Or, if iIdx is equal to the total number of -** cells in pParent, that page number iChild is the right-child of -** the page. +** If the memory cell contains a value that must be freed by +** invoking the external callback in Mem.xDel, then this routine +** will free that value. It also sets Mem.flags to MEM_Null. +** +** This is a helper routine for sqlcipher_sqlite3VdbeMemSetNull() and +** for sqlcipher_sqlite3VdbeMemRelease(). Use those other routines as the +** entry point for releasing Mem resources. */ -static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){ - if( CORRUPT_DB ) return; /* The conditions tested below might not be true - ** in a corrupt database */ - assert( iIdx<=pParent->nCell ); - if( iIdx==pParent->nCell ){ - assert( get4byte(&pParent->aData[pParent->hdrOffset+8])==iChild ); - }else{ - assert( get4byte(findCell(pParent, iIdx))==iChild ); +static SQLITE_NOINLINE void vdbeMemClearExternAndSetNull(Mem *p){ + assert( p->db==0 || sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + assert( VdbeMemDynamic(p) ); + if( p->flags&MEM_Agg ){ + sqlcipher_sqlite3VdbeMemFinalize(p, p->u.pDef); + assert( (p->flags & MEM_Agg)==0 ); + testcase( p->flags & MEM_Dyn ); + } + if( p->flags&MEM_Dyn ){ + assert( p->xDel!=SQLITE_DYNAMIC && p->xDel!=0 ); + p->xDel((void *)p->z); } + p->flags = MEM_Null; } -#else -# define assertParentIndex(x,y,z) -#endif /* -** Move the cursor up to the parent page. +** Release memory held by the Mem p, both external memory cleared +** by p->xDel and memory in p->zMalloc. ** -** pCur->idx is set to the cell index that contains the pointer -** to the page we are coming from. If we are coming from the -** right-most child page then pCur->idx is set to one more than -** the largest cell index. +** This is a helper routine invoked by sqlcipher_sqlite3VdbeMemRelease() in +** the unusual case where there really is memory in p that needs +** to be freed. */ -static void moveToParent(BtCursor *pCur){ - MemPage *pLeaf; - assert( cursorOwnsBtShared(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>0 ); - assert( pCur->pPage ); - assertParentIndex( - pCur->apPage[pCur->iPage-1], - pCur->aiIdx[pCur->iPage-1], - pCur->pPage->pgno - ); - testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell ); - pCur->info.nSize = 0; - pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); - pCur->ix = pCur->aiIdx[pCur->iPage-1]; - pLeaf = pCur->pPage; - pCur->pPage = pCur->apPage[--pCur->iPage]; - releasePageNotNull(pLeaf); +static SQLITE_NOINLINE void vdbeMemClear(Mem *p){ + if( VdbeMemDynamic(p) ){ + vdbeMemClearExternAndSetNull(p); + } + if( p->szMalloc ){ + sqlcipher_sqlite3DbFreeNN(p->db, p->zMalloc); + p->szMalloc = 0; + } + p->z = 0; } /* -** Move the cursor to point to the root page of its b-tree structure. -** -** If the table has a virtual root page, then the cursor is moved to point -** to the virtual root page instead of the actual root page. A table has a -** virtual root page when the actual root page contains no cells and a -** single child page. This can only happen with the table rooted at page 1. +** Release any memory resources held by the Mem. Both the memory that is +** free by Mem.xDel and the Mem.zMalloc allocation are freed. ** -** If the b-tree structure is empty, the cursor state is set to -** CURSOR_INVALID and this routine returns SQLITE_EMPTY. Otherwise, -** the cursor is set to point to the first cell located on the root -** (or virtual root) page and the cursor state is set to CURSOR_VALID. +** Use this routine prior to clean up prior to abandoning a Mem, or to +** reset a Mem back to its minimum memory utilization. ** -** If this function returns successfully, it may be assumed that the -** page-header flags indicate that the [virtual] root-page is the expected -** kind of b-tree page (i.e. if when opening the cursor the caller did not -** specify a KeyInfo structure the flags byte is set to 0x05 or 0x0D, -** indicating a table b-tree, or if the caller did specify a KeyInfo -** structure the flags byte is set to 0x02 or 0x0A, indicating an index -** b-tree). +** Use sqlcipher_sqlite3VdbeMemSetNull() to release just the Mem.xDel space +** prior to inserting new content into the Mem. */ -static int moveToRoot(BtCursor *pCur){ - MemPage *pRoot; - int rc = SQLITE_OK; - - assert( cursorOwnsBtShared(pCur) ); - assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); - assert( CURSOR_VALID < CURSOR_REQUIRESEEK ); - assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); - assert( pCur->eState < CURSOR_REQUIRESEEK || pCur->iPage<0 ); - assert( pCur->pgnoRoot>0 || pCur->iPage<0 ); - - if( pCur->iPage>=0 ){ - if( pCur->iPage ){ - releasePageNotNull(pCur->pPage); - while( --pCur->iPage ){ - releasePageNotNull(pCur->apPage[pCur->iPage]); - } - pCur->pPage = pCur->apPage[0]; - goto skip_init; - } - }else if( pCur->pgnoRoot==0 ){ - pCur->eState = CURSOR_INVALID; - return SQLITE_EMPTY; - }else{ - assert( pCur->iPage==(-1) ); - if( pCur->eState>=CURSOR_REQUIRESEEK ){ - if( pCur->eState==CURSOR_FAULT ){ - assert( pCur->skipNext!=SQLITE_OK ); - return pCur->skipNext; - } - sqlcipher_sqlite3BtreeClearCursor(pCur); - } - rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->pPage, - 0, pCur->curPagerFlags); - if( rc!=SQLITE_OK ){ - pCur->eState = CURSOR_INVALID; - return rc; - } - pCur->iPage = 0; - pCur->curIntKey = pCur->pPage->intKey; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemRelease(Mem *p){ + assert( sqlcipher_sqlite3VdbeCheckMemInvariants(p) ); + if( VdbeMemDynamic(p) || p->szMalloc ){ + vdbeMemClear(p); } - pRoot = pCur->pPage; - assert( pRoot->pgno==pCur->pgnoRoot ); +} - /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor - ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is - ** NULL, the caller expects a table b-tree. If this is not the case, - ** return an SQLITE_CORRUPT error. - ** - ** Earlier versions of SQLite assumed that this test could not fail - ** if the root page was already loaded when this function was called (i.e. - ** if pCur->iPage>=0). But this is not so if the database is corrupted - ** in such a way that page pRoot is linked into a second b-tree table - ** (or the freelist). */ - assert( pRoot->intKey==1 || pRoot->intKey==0 ); - if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){ - return SQLITE_CORRUPT_PAGE(pCur->pPage); - } +/* Like sqlcipher_sqlite3VdbeMemRelease() but faster for cases where we +** know in advance that the Mem is not MEM_Dyn or MEM_Agg. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemReleaseMalloc(Mem *p){ + assert( !VdbeMemDynamic(p) ); + if( p->szMalloc ) vdbeMemClear(p); +} -skip_init: - pCur->ix = 0; - pCur->info.nSize = 0; - pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl); +/* +** Convert a 64-bit IEEE double into a 64-bit signed integer. +** If the double is out of range of a 64-bit signed integer then +** return the closest available 64-bit signed integer. +*/ +static SQLITE_NOINLINE i64 doubleToInt64(double r){ +#ifdef SQLITE_OMIT_FLOATING_POINT + /* When floating-point is omitted, double and int64 are the same thing */ + return r; +#else + /* + ** Many compilers we encounter do not define constants for the + ** minimum and maximum 64-bit integers, or they define them + ** inconsistently. And many do not understand the "LL" notation. + ** So we define our own static constants here using nothing + ** larger than a 32-bit integer constant. + */ + static const i64 maxInt = LARGEST_INT64; + static const i64 minInt = SMALLEST_INT64; - pRoot = pCur->pPage; - if( pRoot->nCell>0 ){ - pCur->eState = CURSOR_VALID; - }else if( !pRoot->leaf ){ - Pgno subpage; - if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT; - subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); - pCur->eState = CURSOR_VALID; - rc = moveToChild(pCur, subpage); + if( r<=(double)minInt ){ + return minInt; + }else if( r>=(double)maxInt ){ + return maxInt; }else{ - pCur->eState = CURSOR_INVALID; - rc = SQLITE_EMPTY; + return (i64)r; } - return rc; +#endif } /* -** Move the cursor down to the left-most leaf entry beneath the -** entry to which it is currently pointing. +** Return some kind of integer value which is the best we can do +** at representing the value that *pMem describes as an integer. +** If pMem is an integer, then the value is exact. If pMem is +** a floating-point then the value returned is the integer part. +** If pMem is a string or blob, then we make an attempt to convert +** it into an integer and return that. If pMem represents an +** an SQL-NULL value, return 0. ** -** The left-most leaf is the one with the smallest key - the first -** in ascending order. +** If pMem represents a string value, its encoding might be changed. */ -static int moveToLeftmost(BtCursor *pCur){ - Pgno pgno; - int rc = SQLITE_OK; - MemPage *pPage; +static SQLITE_NOINLINE i64 memIntValue(const Mem *pMem){ + i64 value = 0; + sqlcipher_sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); + return value; +} +SQLITE_PRIVATE i64 sqlcipher_sqlite3VdbeIntValue(const Mem *pMem){ + int flags; + assert( pMem!=0 ); + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + flags = pMem->flags; + if( flags & (MEM_Int|MEM_IntReal) ){ + testcase( flags & MEM_IntReal ); + return pMem->u.i; + }else if( flags & MEM_Real ){ + return doubleToInt64(pMem->u.r); + }else if( (flags & (MEM_Str|MEM_Blob))!=0 && pMem->z!=0 ){ + return memIntValue(pMem); + }else{ + return 0; + } +} - assert( cursorOwnsBtShared(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){ - assert( pCur->ixnCell ); - pgno = get4byte(findCell(pPage, pCur->ix)); - rc = moveToChild(pCur, pgno); +/* +** Return the best representation of pMem that we can get into a +** double. If pMem is already a double or an integer, return its +** value. If it is a string or blob, try to convert it to a double. +** If it is a NULL, return 0.0. +*/ +static SQLITE_NOINLINE double memRealValue(Mem *pMem){ + /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ + double val = (double)0; + sqlcipher_sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc); + return val; +} +SQLITE_PRIVATE double sqlcipher_sqlite3VdbeRealValue(Mem *pMem){ + assert( pMem!=0 ); + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + if( pMem->flags & MEM_Real ){ + return pMem->u.r; + }else if( pMem->flags & (MEM_Int|MEM_IntReal) ){ + testcase( pMem->flags & MEM_IntReal ); + return (double)pMem->u.i; + }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ + return memRealValue(pMem); + }else{ + /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ + return (double)0; } - return rc; } /* -** Move the cursor down to the right-most leaf entry beneath the -** page to which it is currently pointing. Notice the difference -** between moveToLeftmost() and moveToRightmost(). moveToLeftmost() -** finds the left-most entry beneath the *entry* whereas moveToRightmost() -** finds the right-most entry beneath the *page*. -** -** The right-most entry is the one with the largest key - the last -** key in ascending order. +** Return 1 if pMem represents true, and return 0 if pMem represents false. +** Return the value ifNull if pMem is NULL. */ -static int moveToRightmost(BtCursor *pCur){ - Pgno pgno; - int rc = SQLITE_OK; - MemPage *pPage = 0; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){ + testcase( pMem->flags & MEM_IntReal ); + if( pMem->flags & (MEM_Int|MEM_IntReal) ) return pMem->u.i!=0; + if( pMem->flags & MEM_Null ) return ifNull; + return sqlcipher_sqlite3VdbeRealValue(pMem)!=0.0; +} - assert( cursorOwnsBtShared(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - while( !(pPage = pCur->pPage)->leaf ){ - pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - pCur->ix = pPage->nCell; - rc = moveToChild(pCur, pgno); - if( rc ) return rc; +/* +** The MEM structure is already a MEM_Real. Try to also make it a +** MEM_Int if we can. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeIntegerAffinity(Mem *pMem){ + i64 ix; + assert( pMem!=0 ); + assert( pMem->flags & MEM_Real ); + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + + ix = doubleToInt64(pMem->u.r); + + /* Only mark the value as an integer if + ** + ** (1) the round-trip conversion real->int->real is a no-op, and + ** (2) The integer is neither the largest nor the smallest + ** possible integer (ticket #3922) + ** + ** The second and third terms in the following conditional enforces + ** the second condition under the assumption that addition overflow causes + ** values to wrap around. + */ + if( pMem->u.r==ix && ix>SMALLEST_INT64 && ixu.i = ix; + MemSetTypeFlag(pMem, MEM_Int); } - pCur->ix = pPage->nCell-1; - assert( pCur->info.nSize==0 ); - assert( (pCur->curFlags & BTCF_ValidNKey)==0 ); +} + +/* +** Convert pMem to type integer. Invalidate any prior representations. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemIntegerify(Mem *pMem){ + assert( pMem!=0 ); + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + + pMem->u.i = sqlcipher_sqlite3VdbeIntValue(pMem); + MemSetTypeFlag(pMem, MEM_Int); return SQLITE_OK; } -/* Move the cursor to the first entry in the table. Return SQLITE_OK -** on success. Set *pRes to 0 if the cursor actually points to something -** or set *pRes to 1 if the table is empty. +/* +** Convert pMem so that it is of type MEM_Real. +** Invalidate any prior representations. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ - int rc; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemRealify(Mem *pMem){ + assert( pMem!=0 ); + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - assert( cursorOwnsBtShared(pCur) ); - assert( sqlcipher_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - rc = moveToRoot(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->pPage->nCell>0 ); - *pRes = 0; - rc = moveToLeftmost(pCur); - }else if( rc==SQLITE_EMPTY ){ - assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); - *pRes = 1; - rc = SQLITE_OK; - } - return rc; + pMem->u.r = sqlcipher_sqlite3VdbeRealValue(pMem); + MemSetTypeFlag(pMem, MEM_Real); + return SQLITE_OK; } -/* Move the cursor to the last entry in the table. Return SQLITE_OK -** on success. Set *pRes to 0 if the cursor actually points to something -** or set *pRes to 1 if the table is empty. +/* Compare a floating point value to an integer. Return true if the two +** values are the same within the precision of the floating point value. +** +** This function assumes that i was obtained by assignment from r1. +** +** For some versions of GCC on 32-bit machines, if you do the more obvious +** comparison of "r1==(double)i" you sometimes get an answer of false even +** though the r1 and (double)i values are bit-for-bit the same. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeLast(BtCursor *pCur, int *pRes){ - int rc; - - assert( cursorOwnsBtShared(pCur) ); - assert( sqlcipher_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); +SQLITE_PRIVATE int sqlcipher_sqlite3RealSameAsInt(double r1, sqlcipher_sqlite3_int64 i){ + double r2 = (double)i; + return r1==0.0 + || (memcmp(&r1, &r2, sizeof(r1))==0 + && i >= -2251799813685248LL && i < 2251799813685248LL); +} - /* If the cursor already points to the last entry, this is a no-op. */ - if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ -#ifdef SQLITE_DEBUG - /* This block serves to assert() that the cursor really does point - ** to the last entry in the b-tree. */ - int ii; - for(ii=0; iiiPage; ii++){ - assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); +/* +** Convert pMem so that it has type MEM_Real or MEM_Int. +** Invalidate any prior representations. +** +** Every effort is made to force the conversion, even if the input +** is a string that does not look completely like a number. Convert +** as much of the string as we can and ignore the rest. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemNumerify(Mem *pMem){ + assert( pMem!=0 ); + testcase( pMem->flags & MEM_Int ); + testcase( pMem->flags & MEM_Real ); + testcase( pMem->flags & MEM_IntReal ); + testcase( pMem->flags & MEM_Null ); + if( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null))==0 ){ + int rc; + sqlcipher_sqlite3_int64 ix; + assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + rc = sqlcipher_sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); + if( ((rc==0 || rc==1) && sqlcipher_sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1) + || sqlcipher_sqlite3RealSameAsInt(pMem->u.r, (ix = (i64)pMem->u.r)) + ){ + pMem->u.i = ix; + MemSetTypeFlag(pMem, MEM_Int); + }else{ + MemSetTypeFlag(pMem, MEM_Real); } - assert( pCur->ix==pCur->pPage->nCell-1 ); - assert( pCur->pPage->leaf ); -#endif - *pRes = 0; - return SQLITE_OK; } + assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null))!=0 ); + pMem->flags &= ~(MEM_Str|MEM_Blob|MEM_Zero); + return SQLITE_OK; +} - rc = moveToRoot(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->eState==CURSOR_VALID ); - *pRes = 0; - rc = moveToRightmost(pCur); - if( rc==SQLITE_OK ){ - pCur->curFlags |= BTCF_AtLast; - }else{ - pCur->curFlags &= ~BTCF_AtLast; +/* +** Cast the datatype of the value in pMem according to the affinity +** "aff". Casting is different from applying affinity in that a cast +** is forced. In other words, the value is converted into the desired +** affinity even if that results in loss of data. This routine is +** used (for example) to implement the SQL "cast()" operator. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){ + if( pMem->flags & MEM_Null ) return SQLITE_OK; + switch( aff ){ + case SQLITE_AFF_BLOB: { /* Really a cast to BLOB */ + if( (pMem->flags & MEM_Blob)==0 ){ + sqlcipher_sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding); + assert( pMem->flags & MEM_Str || pMem->db->mallocFailed ); + if( pMem->flags & MEM_Str ) MemSetTypeFlag(pMem, MEM_Blob); + }else{ + pMem->flags &= ~(MEM_TypeMask&~MEM_Blob); + } + break; + } + case SQLITE_AFF_NUMERIC: { + sqlcipher_sqlite3VdbeMemNumerify(pMem); + break; + } + case SQLITE_AFF_INTEGER: { + sqlcipher_sqlite3VdbeMemIntegerify(pMem); + break; + } + case SQLITE_AFF_REAL: { + sqlcipher_sqlite3VdbeMemRealify(pMem); + break; + } + default: { + assert( aff==SQLITE_AFF_TEXT ); + assert( MEM_Str==(MEM_Blob>>3) ); + pMem->flags |= (pMem->flags&MEM_Blob)>>3; + sqlcipher_sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding); + assert( pMem->flags & MEM_Str || pMem->db->mallocFailed ); + pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal|MEM_Blob|MEM_Zero); + return sqlcipher_sqlite3VdbeChangeEncoding(pMem, encoding); } - }else if( rc==SQLITE_EMPTY ){ - assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); - *pRes = 1; - rc = SQLITE_OK; } - return rc; + return SQLITE_OK; } -/* Move the cursor so that it points to an entry near the key -** specified by pIdxKey or intKey. Return a success code. -** -** For INTKEY tables, the intKey parameter is used. pIdxKey -** must be NULL. For index tables, pIdxKey is used and intKey -** is ignored. -** -** If an exact match is not found, then the cursor is always -** left pointing at a leaf page which would hold the entry if it -** were present. The cursor might point to an entry that comes -** before or after the key. -** -** An integer is written into *pRes which is the result of -** comparing the key with the entry to which the cursor is -** pointing. The meaning of the integer written into -** *pRes is as follows: +/* +** Initialize bulk memory to be a consistent Mem object. ** -** *pRes<0 The cursor is left pointing at an entry that -** is smaller than intKey/pIdxKey or if the table is empty -** and the cursor is therefore left point to nothing. +** The minimum amount of initialization feasible is performed. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemInit(Mem *pMem, sqlcipher_sqlite3 *db, u16 flags){ + assert( (flags & ~MEM_TypeMask)==0 ); + pMem->flags = flags; + pMem->db = db; + pMem->szMalloc = 0; +} + + +/* +** Delete any previous value and set the value stored in *pMem to NULL. ** -** *pRes==0 The cursor is left pointing at an entry that -** exactly matches intKey/pIdxKey. +** This routine calls the Mem.xDel destructor to dispose of values that +** require the destructor. But it preserves the Mem.zMalloc memory allocation. +** To free all resources, use sqlcipher_sqlite3VdbeMemRelease(), which both calls this +** routine to invoke the destructor and deallocates Mem.zMalloc. ** -** *pRes>0 The cursor is left pointing at an entry that -** is larger than intKey/pIdxKey. +** Use this routine to reset the Mem prior to insert a new value. ** -** For index tables, the pIdxKey->eqSeen field is set to 1 if there -** exists an entry in the table that exactly matches pIdxKey. +** Use sqlcipher_sqlite3VdbeMemRelease() to complete erase the Mem prior to abandoning it. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeMovetoUnpacked( - BtCursor *pCur, /* The cursor to be moved */ - UnpackedRecord *pIdxKey, /* Unpacked index key */ - i64 intKey, /* The table key */ - int biasRight, /* If true, bias the search to the high end */ - int *pRes /* Write search results here */ -){ - int rc; - RecordCompare xRecordCompare; - - assert( cursorOwnsBtShared(pCur) ); - assert( sqlcipher_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - assert( pRes ); - assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); - assert( pCur->eState!=CURSOR_VALID || (pIdxKey==0)==(pCur->curIntKey!=0) ); - - /* If the cursor is already positioned at the point we are trying - ** to move to, then just return without doing any work */ - if( pIdxKey==0 - && pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 - ){ - if( pCur->info.nKey==intKey ){ - *pRes = 0; - return SQLITE_OK; - } - if( pCur->info.nKeycurFlags & BTCF_AtLast)!=0 ){ - *pRes = -1; - return SQLITE_OK; - } - /* If the requested key is one more than the previous key, then - ** try to get there using sqlcipher_sqlite3BtreeNext() rather than a full - ** binary search. This is an optimization only. The correct answer - ** is still obtained without this case, only a little more slowely */ - if( pCur->info.nKey+1==intKey ){ - *pRes = 0; - rc = sqlcipher_sqlite3BtreeNext(pCur, 0); - if( rc==SQLITE_OK ){ - getCellInfo(pCur); - if( pCur->info.nKey==intKey ){ - return SQLITE_OK; - } - }else if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - }else{ - return rc; - } - } - } +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetNull(Mem *pMem){ + if( VdbeMemDynamic(pMem) ){ + vdbeMemClearExternAndSetNull(pMem); + }else{ + pMem->flags = MEM_Null; } +} +SQLITE_PRIVATE void sqlcipher_sqlite3ValueSetNull(sqlcipher_sqlite3_value *p){ + sqlcipher_sqlite3VdbeMemSetNull((Mem*)p); +} -#ifdef SQLITE_DEBUG - pCur->pBtree->nSeek++; /* Performance measurement during testing */ +/* +** Delete any previous value and set the value to be a BLOB of length +** n containing all zeros. +*/ +#ifndef SQLITE_OMIT_INCRBLOB +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ + sqlcipher_sqlite3VdbeMemRelease(pMem); + pMem->flags = MEM_Blob|MEM_Zero; + pMem->n = 0; + if( n<0 ) n = 0; + pMem->u.nZero = n; + pMem->enc = SQLITE_UTF8; + pMem->z = 0; +} +#else +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ + int nByte = n>0?n:1; + if( sqlcipher_sqlite3VdbeMemGrow(pMem, nByte, 0) ){ + return SQLITE_NOMEM_BKPT; + } + assert( pMem->z!=0 ); + assert( sqlcipher_sqlite3DbMallocSize(pMem->db, pMem->z)>=nByte ); + memset(pMem->z, 0, nByte); + pMem->n = n>0?n:0; + pMem->flags = MEM_Blob; + pMem->enc = SQLITE_UTF8; + return SQLITE_OK; +} #endif - if( pIdxKey ){ - xRecordCompare = sqlcipher_sqlite3VdbeFindCompare(pIdxKey); - pIdxKey->errCode = 0; - assert( pIdxKey->default_rc==1 - || pIdxKey->default_rc==0 - || pIdxKey->default_rc==-1 - ); +/* +** The pMem is known to contain content that needs to be destroyed prior +** to a value change. So invoke the destructor, then set the value to +** a 64-bit integer. +*/ +static SQLITE_NOINLINE void vdbeReleaseAndSetInt64(Mem *pMem, i64 val){ + sqlcipher_sqlite3VdbeMemSetNull(pMem); + pMem->u.i = val; + pMem->flags = MEM_Int; +} + +/* +** Delete any previous value and set the value stored in *pMem to val, +** manifest type INTEGER. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){ + if( VdbeMemDynamic(pMem) ){ + vdbeReleaseAndSetInt64(pMem, val); }else{ - xRecordCompare = 0; /* All keys are integers */ + pMem->u.i = val; + pMem->flags = MEM_Int; } +} - rc = moveToRoot(pCur); - if( rc ){ - if( rc==SQLITE_EMPTY ){ - assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); - *pRes = -1; - return SQLITE_OK; - } - return rc; - } - assert( pCur->pPage ); - assert( pCur->pPage->isInit ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->pPage->nCell > 0 ); - assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey ); - assert( pCur->curIntKey || pIdxKey ); - for(;;){ - int lwr, upr, idx, c; - Pgno chldPg; - MemPage *pPage = pCur->pPage; - u8 *pCell; /* Pointer to current cell in pPage */ +/* A no-op destructor */ +SQLITE_PRIVATE void sqlcipher_sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); } - /* pPage->nCell must be greater than zero. If this is the root-page - ** the cursor would have been INVALID above and this for(;;) loop - ** not run. If this is not the root-page, then the moveToChild() routine - ** would have already detected db corruption. Similarly, pPage must - ** be the right kind (index or table) of b-tree page. Otherwise - ** a moveToChild() or moveToRoot() call would have detected corruption. */ - assert( pPage->nCell>0 ); - assert( pPage->intKey==(pIdxKey==0) ); - lwr = 0; - upr = pPage->nCell-1; - assert( biasRight==0 || biasRight==1 ); - idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ - pCur->ix = (u16)idx; - if( xRecordCompare==0 ){ - for(;;){ - i64 nCellKey; - pCell = findCellPastPtr(pPage, idx); - if( pPage->intKeyLeaf ){ - while( 0x80 <= *(pCell++) ){ - if( pCell>=pPage->aDataEnd ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - } - } - getVarint(pCell, (u64*)&nCellKey); - if( nCellKeyupr ){ c = -1; break; } - }else if( nCellKey>intKey ){ - upr = idx-1; - if( lwr>upr ){ c = +1; break; } - }else{ - assert( nCellKey==intKey ); - pCur->ix = (u16)idx; - if( !pPage->leaf ){ - lwr = idx; - goto moveto_next_layer; - }else{ - pCur->curFlags |= BTCF_ValidNKey; - pCur->info.nKey = nCellKey; - pCur->info.nSize = 0; - *pRes = 0; - return SQLITE_OK; - } - } - assert( lwr+upr>=0 ); - idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */ - } - }else{ - for(;;){ - int nCell; /* Size of the pCell cell in bytes */ - pCell = findCellPastPtr(pPage, idx); - - /* The maximum supported page-size is 65536 bytes. This means that - ** the maximum number of record bytes stored on an index B-Tree - ** page is less than 16384 bytes and may be stored as a 2-byte - ** varint. This information is used to attempt to avoid parsing - ** the entire cell by checking for the cases where the record is - ** stored entirely within the b-tree page by inspecting the first - ** 2 bytes of the cell. - */ - nCell = pCell[0]; - if( nCell<=pPage->max1bytePayload ){ - /* This branch runs if the record-size field of the cell is a - ** single byte varint and the record fits entirely on the main - ** b-tree page. */ - testcase( pCell+nCell+1==pPage->aDataEnd ); - c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); - }else if( !(pCell[1] & 0x80) - && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal - ){ - /* The record-size field is a 2 byte varint and the record - ** fits entirely on the main b-tree page. */ - testcase( pCell+nCell+2==pPage->aDataEnd ); - c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); - }else{ - /* The record flows over onto one or more overflow pages. In - ** this case the whole cell needs to be parsed, a buffer allocated - ** and accessPayload() used to retrieve the record into the - ** buffer before VdbeRecordCompare() can be called. - ** - ** If the record is corrupt, the xRecordCompare routine may read - ** up to two varints past the end of the buffer. An extra 18 - ** bytes of padding is allocated at the end of the buffer in - ** case this happens. */ - void *pCellKey; - u8 * const pCellBody = pCell - pPage->childPtrSize; - const int nOverrun = 18; /* Size of the overrun padding */ - pPage->xParseCell(pPage, pCellBody, &pCur->info); - nCell = (int)pCur->info.nKey; - testcase( nCell<0 ); /* True if key size is 2^32 or more */ - testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ - testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ - testcase( nCell==2 ); /* Minimum legal index key size */ - if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){ - rc = SQLITE_CORRUPT_PAGE(pPage); - goto moveto_finish; - } - pCellKey = sqlcipher_sqlite3Malloc( nCell+nOverrun ); - if( pCellKey==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto moveto_finish; - } - pCur->ix = (u16)idx; - rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); - memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */ - pCur->curFlags &= ~BTCF_ValidOvfl; - if( rc ){ - sqlcipher_sqlite3_free(pCellKey); - goto moveto_finish; - } - c = sqlcipher_sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); - sqlcipher_sqlite3_free(pCellKey); - } - assert( - (pIdxKey->errCode!=SQLITE_CORRUPT || c==0) - && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed) - ); - if( c<0 ){ - lwr = idx+1; - }else if( c>0 ){ - upr = idx-1; - }else{ - assert( c==0 ); - *pRes = 0; - rc = SQLITE_OK; - pCur->ix = (u16)idx; - if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT; - goto moveto_finish; - } - if( lwr>upr ) break; - assert( lwr+upr>=0 ); - idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */ - } - } - assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) ); - assert( pPage->isInit ); - if( pPage->leaf ){ - assert( pCur->ixpPage->nCell ); - pCur->ix = (u16)idx; - *pRes = c; - rc = SQLITE_OK; - goto moveto_finish; - } -moveto_next_layer: - if( lwr>=pPage->nCell ){ - chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); - }else{ - chldPg = get4byte(findCell(pPage, lwr)); - } - pCur->ix = (u16)lwr; - rc = moveToChild(pCur, chldPg); - if( rc ) break; +/* +** Set the value stored in *pMem should already be a NULL. +** Also store a pointer to go with it. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetPointer( + Mem *pMem, + void *pPtr, + const char *zPType, + void (*xDestructor)(void*) +){ + assert( pMem->flags==MEM_Null ); + vdbeMemClear(pMem); + pMem->u.zPType = zPType ? zPType : ""; + pMem->z = pPtr; + pMem->flags = MEM_Null|MEM_Dyn|MEM_Subtype|MEM_Term; + pMem->eSubtype = 'p'; + pMem->xDel = xDestructor ? xDestructor : sqlcipher_sqlite3NoopDestructor; +} + +#ifndef SQLITE_OMIT_FLOATING_POINT +/* +** Delete any previous value and set the value stored in *pMem to val, +** manifest type REAL. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetDouble(Mem *pMem, double val){ + sqlcipher_sqlite3VdbeMemSetNull(pMem); + if( !sqlcipher_sqlite3IsNaN(val) ){ + pMem->u.r = val; + pMem->flags = MEM_Real; } -moveto_finish: - pCur->info.nSize = 0; - assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); - return rc; } +#endif +#ifdef SQLITE_DEBUG +/* +** Return true if the Mem holds a RowSet object. This routine is intended +** for use inside of assert() statements. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemIsRowSet(const Mem *pMem){ + return (pMem->flags&(MEM_Blob|MEM_Dyn))==(MEM_Blob|MEM_Dyn) + && pMem->xDel==sqlcipher_sqlite3RowSetDelete; +} +#endif /* -** Return TRUE if the cursor is not pointing at an entry of the table. +** Delete any previous value and set the value of pMem to be an +** empty boolean index. ** -** TRUE will be returned after a call to sqlcipher_sqlite3BtreeNext() moves -** past the last entry in the table or sqlcipher_sqlite3BtreePrev() moves past -** the first entry. TRUE is also returned if the table is empty. +** Return SQLITE_OK on success and SQLITE_NOMEM if a memory allocation +** error occurs. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeEof(BtCursor *pCur){ - /* TODO: What if the cursor is in CURSOR_REQUIRESEEK but all table entries - ** have been deleted? This API will need to change to return an error code - ** as well as the boolean result value. - */ - return (CURSOR_VALID!=pCur->eState); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemSetRowSet(Mem *pMem){ + sqlcipher_sqlite3 *db = pMem->db; + RowSet *p; + assert( db!=0 ); + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); + sqlcipher_sqlite3VdbeMemRelease(pMem); + p = sqlcipher_sqlite3RowSetInit(db); + if( p==0 ) return SQLITE_NOMEM; + pMem->z = (char*)p; + pMem->flags = MEM_Blob|MEM_Dyn; + pMem->xDel = sqlcipher_sqlite3RowSetDelete; + return SQLITE_OK; } /* -** Return an estimate for the number of rows in the table that pCur is -** pointing to. Return a negative number if no estimate is currently -** available. +** Return true if the Mem object contains a TEXT or BLOB that is +** too large - whose size exceeds SQLITE_MAX_LENGTH. */ -SQLITE_PRIVATE i64 sqlcipher_sqlite3BtreeRowCountEst(BtCursor *pCur){ - i64 n; - u8 i; - - assert( cursorOwnsBtShared(pCur) ); - assert( sqlcipher_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - - /* Currently this interface is only called by the OP_IfSmaller - ** opcode, and it that case the cursor will always be valid and - ** will always point to a leaf node. */ - if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1; - if( NEVER(pCur->pPage->leaf==0) ) return -1; - - n = pCur->pPage->nCell; - for(i=0; iiPage; i++){ - n *= pCur->apPage[i]->nCell; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemTooBig(Mem *p){ + assert( p->db!=0 ); + if( p->flags & (MEM_Str|MEM_Blob) ){ + int n = p->n; + if( p->flags & MEM_Zero ){ + n += p->u.nZero; + } + return n>p->db->aLimit[SQLITE_LIMIT_LENGTH]; } - return n; + return 0; } +#ifdef SQLITE_DEBUG /* -** Advance the cursor to the next entry in the database. -** Return value: -** -** SQLITE_OK success -** SQLITE_DONE cursor is already pointing at the last element -** otherwise some kind of error occurred -** -** The main entry point is sqlcipher_sqlite3BtreeNext(). That routine is optimized -** for the common case of merely incrementing the cell counter BtCursor.aiIdx -** to the next cell on the current page. The (slower) btreeNext() helper -** routine is called when it is necessary to move to a different page or -** to restore the cursor. +** This routine prepares a memory cell for modification by breaking +** its link to a shallow copy and by marking any current shallow +** copies of this cell as invalid. ** -** If bit 0x01 of the F argument in sqlcipher_sqlite3BtreeNext(C,F) is 1, then the -** cursor corresponds to an SQL index and this routine could have been -** skipped if the SQL index had been a unique index. The F argument -** is a hint to the implement. SQLite btree implementation does not use -** this hint, but COMDB2 does. +** This is used for testing and debugging only - to help ensure that shallow +** copies (created by OP_SCopy) are not misused. */ -static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ - int rc; - int idx; - MemPage *pPage; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ + int i; + Mem *pX; + for(i=1, pX=pVdbe->aMem+1; inMem; i++, pX++){ + if( pX->pScopyFrom==pMem ){ + u16 mFlags; + if( pVdbe->db->flags & SQLITE_VdbeTrace ){ + sqlcipher_sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n", + (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem)); + } + /* If pX is marked as a shallow copy of pMem, then try to verify that + ** no significant changes have been made to pX since the OP_SCopy. + ** A significant change would indicated a missed call to this + ** function for pX. Minor changes, such as adding or removing a + ** dual type, are allowed, as long as the underlying value is the + ** same. */ + mFlags = pMem->flags & pX->flags & pX->mScopyFlags; + assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i ); - assert( cursorOwnsBtShared(pCur) ); - if( pCur->eState!=CURSOR_VALID ){ - assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); - rc = restoreCursorPosition(pCur); - if( rc!=SQLITE_OK ){ - return rc; - } - if( CURSOR_INVALID==pCur->eState ){ - return SQLITE_DONE; - } - if( pCur->eState==CURSOR_SKIPNEXT ){ - pCur->eState = CURSOR_VALID; - if( pCur->skipNext>0 ) return SQLITE_OK; + /* pMem is the register that is changing. But also mark pX as + ** undefined so that we can quickly detect the shallow-copy error */ + pX->flags = MEM_Undefined; + pX->pScopyFrom = 0; } } + pMem->pScopyFrom = 0; +} +#endif /* SQLITE_DEBUG */ - pPage = pCur->pPage; - idx = ++pCur->ix; - if( !pPage->isInit || sqlcipher_sqlite3FaultSim(412) ){ - /* The only known way for this to happen is for there to be a - ** recursive SQL function that does a DELETE operation as part of a - ** SELECT which deletes content out from under an active cursor - ** in a corrupt database file where the table being DELETE-ed from - ** has pages in common with the table being queried. See TH3 - ** module cov1/btree78.test testcase 220 (2018-06-08) for an - ** example. */ - return SQLITE_CORRUPT_BKPT; - } - - /* If the database file is corrupt, it is possible for the value of idx - ** to be invalid here. This can only occur if a second cursor modifies - ** the page while cursor pCur is holding a reference to it. Which can - ** only happen if the database is corrupt in such a way as to link the - ** page into more than one b-tree structure. - ** - ** Update 2019-12-23: appears to long longer be possible after the - ** addition of anotherValidCursor() condition on balance_deeper(). */ - harmless( idx>pPage->nCell ); - - if( idx>=pPage->nCell ){ - if( !pPage->leaf ){ - rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); - if( rc ) return rc; - return moveToLeftmost(pCur); - } - do{ - if( pCur->iPage==0 ){ - pCur->eState = CURSOR_INVALID; - return SQLITE_DONE; - } - moveToParent(pCur); - pPage = pCur->pPage; - }while( pCur->ix>=pPage->nCell ); - if( pPage->intKey ){ - return sqlcipher_sqlite3BtreeNext(pCur, 0); - }else{ - return SQLITE_OK; - } - } - if( pPage->leaf ){ - return SQLITE_OK; - }else{ - return moveToLeftmost(pCur); - } +/* +** Make an shallow copy of pFrom into pTo. Prior contents of +** pTo are freed. The pFrom->z field is not duplicated. If +** pFrom->z is used, then pTo->z points to the same thing as pFrom->z +** and flags gets srcType (either MEM_Ephem or MEM_Static). +*/ +static SQLITE_NOINLINE void vdbeClrCopy(Mem *pTo, const Mem *pFrom, int eType){ + vdbeMemClearExternAndSetNull(pTo); + assert( !VdbeMemDynamic(pTo) ); + sqlcipher_sqlite3VdbeMemShallowCopy(pTo, pFrom, eType); } -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeNext(BtCursor *pCur, int flags){ - MemPage *pPage; - UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ - assert( cursorOwnsBtShared(pCur) ); - assert( flags==0 || flags==1 ); - pCur->info.nSize = 0; - pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); - if( pCur->eState!=CURSOR_VALID ) return btreeNext(pCur); - pPage = pCur->pPage; - if( (++pCur->ix)>=pPage->nCell ){ - pCur->ix--; - return btreeNext(pCur); - } - if( pPage->leaf ){ - return SQLITE_OK; - }else{ - return moveToLeftmost(pCur); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){ + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pFrom) ); + assert( pTo->db==pFrom->db ); + if( VdbeMemDynamic(pTo) ){ vdbeClrCopy(pTo,pFrom,srcType); return; } + memcpy(pTo, pFrom, MEMCELLSIZE); + if( (pFrom->flags&MEM_Static)==0 ){ + pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Ephem); + assert( srcType==MEM_Ephem || srcType==MEM_Static ); + pTo->flags |= srcType; } } /* -** Step the cursor to the back to the previous entry in the database. -** Return values: -** -** SQLITE_OK success -** SQLITE_DONE the cursor is already on the first element of the table -** otherwise some kind of error occurred -** -** The main entry point is sqlcipher_sqlite3BtreePrevious(). That routine is optimized -** for the common case of merely decrementing the cell counter BtCursor.aiIdx -** to the previous cell on the current page. The (slower) btreePrevious() -** helper routine is called when it is necessary to move to a different page -** or to restore the cursor. -** -** If bit 0x01 of the F argument to sqlcipher_sqlite3BtreePrevious(C,F) is 1, then -** the cursor corresponds to an SQL index and this routine could have been -** skipped if the SQL index had been a unique index. The F argument is a -** hint to the implement. The native SQLite btree implementation does not -** use this hint, but COMDB2 does. +** Make a full copy of pFrom into pTo. Prior contents of pTo are +** freed before the copy is made. */ -static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ - int rc; - MemPage *pPage; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){ + int rc = SQLITE_OK; - assert( cursorOwnsBtShared(pCur) ); - assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 ); - assert( pCur->info.nSize==0 ); - if( pCur->eState!=CURSOR_VALID ){ - rc = restoreCursorPosition(pCur); - if( rc!=SQLITE_OK ){ - return rc; - } - if( CURSOR_INVALID==pCur->eState ){ - return SQLITE_DONE; - } - if( CURSOR_SKIPNEXT==pCur->eState ){ - pCur->eState = CURSOR_VALID; - if( pCur->skipNext<0 ) return SQLITE_OK; + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pFrom) ); + if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo); + memcpy(pTo, pFrom, MEMCELLSIZE); + pTo->flags &= ~MEM_Dyn; + if( pTo->flags&(MEM_Str|MEM_Blob) ){ + if( 0==(pFrom->flags&MEM_Static) ){ + pTo->flags |= MEM_Ephem; + rc = sqlcipher_sqlite3VdbeMemMakeWriteable(pTo); } } - pPage = pCur->pPage; - assert( pPage->isInit ); - if( !pPage->leaf ){ - int idx = pCur->ix; - rc = moveToChild(pCur, get4byte(findCell(pPage, idx))); - if( rc ) return rc; - rc = moveToRightmost(pCur); - }else{ - while( pCur->ix==0 ){ - if( pCur->iPage==0 ){ - pCur->eState = CURSOR_INVALID; - return SQLITE_DONE; - } - moveToParent(pCur); - } - assert( pCur->info.nSize==0 ); - assert( (pCur->curFlags & (BTCF_ValidOvfl))==0 ); - - pCur->ix--; - pPage = pCur->pPage; - if( pPage->intKey && !pPage->leaf ){ - rc = sqlcipher_sqlite3BtreePrevious(pCur, 0); - }else{ - rc = SQLITE_OK; - } - } return rc; } -SQLITE_PRIVATE int sqlcipher_sqlite3BtreePrevious(BtCursor *pCur, int flags){ - assert( cursorOwnsBtShared(pCur) ); - assert( flags==0 || flags==1 ); - UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ - pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey); - pCur->info.nSize = 0; - if( pCur->eState!=CURSOR_VALID - || pCur->ix==0 - || pCur->pPage->leaf==0 - ){ - return btreePrevious(pCur); - } - pCur->ix--; - return SQLITE_OK; + +/* +** Transfer the contents of pFrom to pTo. Any existing value in pTo is +** freed. If pFrom contains ephemeral data, a copy is made. +** +** pFrom contains an SQL NULL when this routine returns. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){ + assert( pFrom->db==0 || sqlcipher_sqlite3_mutex_held(pFrom->db->mutex) ); + assert( pTo->db==0 || sqlcipher_sqlite3_mutex_held(pTo->db->mutex) ); + assert( pFrom->db==0 || pTo->db==0 || pFrom->db==pTo->db ); + + sqlcipher_sqlite3VdbeMemRelease(pTo); + memcpy(pTo, pFrom, sizeof(Mem)); + pFrom->flags = MEM_Null; + pFrom->szMalloc = 0; } /* -** Allocate a new page from the database file. +** Change the value of a Mem to be a string or a BLOB. ** -** The new page is marked as dirty. (In other words, sqlcipher_sqlite3PagerWrite() -** has already been called on the new page.) The new page has also -** been referenced and the calling routine is responsible for calling -** sqlcipher_sqlite3PagerUnref() on the new page when it is done. +** The memory management strategy depends on the value of the xDel +** parameter. If the value passed is SQLITE_TRANSIENT, then the +** string is copied into a (possibly existing) buffer managed by the +** Mem structure. Otherwise, any existing buffer is freed and the +** pointer copied. ** -** SQLITE_OK is returned on success. Any other return value indicates -** an error. *ppPage is set to NULL in the event of an error. +** If the string is too large (if it exceeds the SQLITE_LIMIT_LENGTH +** size limit) then no memory allocation occurs. If the string can be +** stored without allocating memory, then it is. If a memory allocation +** is required to store the string, then value of pMem is unchanged. In +** either case, SQLITE_TOOBIG is returned. ** -** If the "nearby" parameter is not 0, then an effort is made to -** locate a page close to the page number "nearby". This can be used in an -** attempt to keep related pages close to each other in the database file, -** which in turn can make database access faster. +** The "enc" parameter is the text encoding for the string, or zero +** to store a blob. ** -** If the eMode parameter is BTALLOC_EXACT and the nearby page exists -** anywhere on the free-list, then it is guaranteed to be returned. If -** eMode is BTALLOC_LT then the page returned will be less than or equal -** to nearby if any such page exists. If eMode is BTALLOC_ANY then there -** are no restrictions on which page is returned. +** If n is negative, then the string consists of all bytes up to but +** excluding the first zero character. The n parameter must be +** non-negative for blobs. */ -static int allocateBtreePage( - BtShared *pBt, /* The btree */ - MemPage **ppPage, /* Store pointer to the allocated page here */ - Pgno *pPgno, /* Store the page number here */ - Pgno nearby, /* Search for a page near this one */ - u8 eMode /* BTALLOC_EXACT, BTALLOC_LT, or BTALLOC_ANY */ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemSetStr( + Mem *pMem, /* Memory cell to set to string value */ + const char *z, /* String pointer */ + i64 n, /* Bytes in string, or negative */ + u8 enc, /* Encoding of z. 0 for BLOBs */ + void (*xDel)(void*) /* Destructor function */ ){ - MemPage *pPage1; - int rc; - u32 n; /* Number of pages on the freelist */ - u32 k; /* Number of leaves on the trunk of the freelist */ - MemPage *pTrunk = 0; - MemPage *pPrevTrunk = 0; - Pgno mxPage; /* Total size of the database file */ - - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) ); - pPage1 = pBt->pPage1; - mxPage = btreePagecount(pBt); - /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36 - ** stores stores the total number of pages on the freelist. */ - n = get4byte(&pPage1->aData[36]); - testcase( n==mxPage-1 ); - if( n>=mxPage ){ - return SQLITE_CORRUPT_BKPT; - } - if( n>0 ){ - /* There are pages on the freelist. Reuse one of those pages. */ - Pgno iTrunk; - u8 searchList = 0; /* If the free-list must be searched for 'nearby' */ - u32 nSearch = 0; /* Count of the number of search attempts */ - - /* If eMode==BTALLOC_EXACT and a query of the pointer-map - ** shows that the page 'nearby' is somewhere on the free-list, then - ** the entire-list will be searched for that page. - */ -#ifndef SQLITE_OMIT_AUTOVACUUM - if( eMode==BTALLOC_EXACT ){ - if( nearby<=mxPage ){ - u8 eType; - assert( nearby>0 ); - assert( pBt->autoVacuum ); - rc = ptrmapGet(pBt, nearby, &eType, 0); - if( rc ) return rc; - if( eType==PTRMAP_FREEPAGE ){ - searchList = 1; - } - } - }else if( eMode==BTALLOC_LE ){ - searchList = 1; - } -#endif - - /* Decrement the free-list count by 1. Set iTrunk to the index of the - ** first free-list trunk page. iPrevTrunk is initially 1. - */ - rc = sqlcipher_sqlite3PagerWrite(pPage1->pDbPage); - if( rc ) return rc; - put4byte(&pPage1->aData[36], n-1); - - /* The code within this loop is run only once if the 'searchList' variable - ** is not true. Otherwise, it runs once for each trunk-page on the - ** free-list until the page 'nearby' is located (eMode==BTALLOC_EXACT) - ** or until a page less than 'nearby' is located (eMode==BTALLOC_LT) - */ - do { - pPrevTrunk = pTrunk; - if( pPrevTrunk ){ - /* EVIDENCE-OF: R-01506-11053 The first integer on a freelist trunk page - ** is the page number of the next freelist trunk page in the list or - ** zero if this is the last freelist trunk page. */ - iTrunk = get4byte(&pPrevTrunk->aData[0]); - }else{ - /* EVIDENCE-OF: R-59841-13798 The 4-byte big-endian integer at offset 32 - ** stores the page number of the first page of the freelist, or zero if - ** the freelist is empty. */ - iTrunk = get4byte(&pPage1->aData[32]); - } - testcase( iTrunk==mxPage ); - if( iTrunk>mxPage || nSearch++ > n ){ - rc = SQLITE_CORRUPT_PGNO(pPrevTrunk ? pPrevTrunk->pgno : 1); - }else{ - rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0); - } - if( rc ){ - pTrunk = 0; - goto end_allocate_page; - } - assert( pTrunk!=0 ); - assert( pTrunk->aData!=0 ); - /* EVIDENCE-OF: R-13523-04394 The second integer on a freelist trunk page - ** is the number of leaf page pointers to follow. */ - k = get4byte(&pTrunk->aData[4]); - if( k==0 && !searchList ){ - /* The trunk has no leaves and the list is not being searched. - ** So extract the trunk page itself and use it as the newly - ** allocated page */ - assert( pPrevTrunk==0 ); - rc = sqlcipher_sqlite3PagerWrite(pTrunk->pDbPage); - if( rc ){ - goto end_allocate_page; - } - *pPgno = iTrunk; - memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); - *ppPage = pTrunk; - pTrunk = 0; - TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); - }else if( k>(u32)(pBt->usableSize/4 - 2) ){ - /* Value of k is out of range. Database corruption */ - rc = SQLITE_CORRUPT_PGNO(iTrunk); - goto end_allocate_page; -#ifndef SQLITE_OMIT_AUTOVACUUM - }else if( searchList - && (nearby==iTrunk || (iTrunkpDbPage); - if( rc ){ - goto end_allocate_page; - } - if( k==0 ){ - if( !pPrevTrunk ){ - memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); - }else{ - rc = sqlcipher_sqlite3PagerWrite(pPrevTrunk->pDbPage); - if( rc!=SQLITE_OK ){ - goto end_allocate_page; - } - memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4); - } - }else{ - /* The trunk page is required by the caller but it contains - ** pointers to free-list leaves. The first leaf becomes a trunk - ** page in this case. - */ - MemPage *pNewTrunk; - Pgno iNewTrunk = get4byte(&pTrunk->aData[8]); - if( iNewTrunk>mxPage ){ - rc = SQLITE_CORRUPT_PGNO(iTrunk); - goto end_allocate_page; - } - testcase( iNewTrunk==mxPage ); - rc = btreeGetUnusedPage(pBt, iNewTrunk, &pNewTrunk, 0); - if( rc!=SQLITE_OK ){ - goto end_allocate_page; - } - rc = sqlcipher_sqlite3PagerWrite(pNewTrunk->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(pNewTrunk); - goto end_allocate_page; - } - memcpy(&pNewTrunk->aData[0], &pTrunk->aData[0], 4); - put4byte(&pNewTrunk->aData[4], k-1); - memcpy(&pNewTrunk->aData[8], &pTrunk->aData[12], (k-1)*4); - releasePage(pNewTrunk); - if( !pPrevTrunk ){ - assert( sqlcipher_sqlite3PagerIswriteable(pPage1->pDbPage) ); - put4byte(&pPage1->aData[32], iNewTrunk); - }else{ - rc = sqlcipher_sqlite3PagerWrite(pPrevTrunk->pDbPage); - if( rc ){ - goto end_allocate_page; - } - put4byte(&pPrevTrunk->aData[0], iNewTrunk); - } - } - pTrunk = 0; - TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); -#endif - }else if( k>0 ){ - /* Extract a leaf from the trunk */ - u32 closest; - Pgno iPage; - unsigned char *aData = pTrunk->aData; - if( nearby>0 ){ - u32 i; - closest = 0; - if( eMode==BTALLOC_LE ){ - for(i=0; imxPage ){ - rc = SQLITE_CORRUPT_PGNO(iTrunk); - goto end_allocate_page; - } - testcase( iPage==mxPage ); - if( !searchList - || (iPage==nearby || (iPagepgno, n-1)); - rc = sqlcipher_sqlite3PagerWrite(pTrunk->pDbPage); - if( rc ) goto end_allocate_page; - if( closestpDbPage); - if( rc!=SQLITE_OK ){ - releasePage(*ppPage); - *ppPage = 0; - } - } - searchList = 0; - } - } - releasePage(pPrevTrunk); - pPrevTrunk = 0; - }while( searchList ); - }else{ - /* There are no pages on the freelist, so append a new page to the - ** database image. - ** - ** Normally, new pages allocated by this block can be requested from the - ** pager layer with the 'no-content' flag set. This prevents the pager - ** from trying to read the pages content from disk. However, if the - ** current transaction has already run one or more incremental-vacuum - ** steps, then the page we are about to allocate may contain content - ** that is required in the event of a rollback. In this case, do - ** not set the no-content flag. This causes the pager to load and journal - ** the current page content before overwriting it. - ** - ** Note that the pager will not actually attempt to load or journal - ** content for any page that really does lie past the end of the database - ** file on disk. So the effects of disabling the no-content optimization - ** here are confined to those pages that lie between the end of the - ** database image and the end of the database file. - */ - int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate))? PAGER_GET_NOCONTENT:0; + i64 nByte = n; /* New value for pMem->n */ + int iLimit; /* Maximum allowed string or blob size */ + u16 flags; /* New value for pMem->flags */ - rc = sqlcipher_sqlite3PagerWrite(pBt->pPage1->pDbPage); - if( rc ) return rc; - pBt->nPage++; - if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ) pBt->nPage++; + assert( pMem!=0 ); + assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); + assert( enc!=0 || n>=0 ); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, pBt->nPage) ){ - /* If *pPgno refers to a pointer-map page, allocate two new pages - ** at the end of the file instead of one. The first allocated page - ** becomes a new pointer-map page, the second is used by the caller. - */ - MemPage *pPg = 0; - TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage)); - assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) ); - rc = btreeGetUnusedPage(pBt, pBt->nPage, &pPg, bNoContent); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3PagerWrite(pPg->pDbPage); - releasePage(pPg); + /* If z is a NULL pointer, set pMem to contain an SQL NULL. */ + if( !z ){ + sqlcipher_sqlite3VdbeMemSetNull(pMem); + return SQLITE_OK; + } + + if( pMem->db ){ + iLimit = pMem->db->aLimit[SQLITE_LIMIT_LENGTH]; + }else{ + iLimit = SQLITE_MAX_LENGTH; + } + if( nByte<0 ){ + assert( enc!=0 ); + if( enc==SQLITE_UTF8 ){ + nByte = strlen(z); + }else{ + for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} + } + flags= MEM_Str|MEM_Term; + }else if( enc==0 ){ + flags = MEM_Blob; + enc = SQLITE_UTF8; + }else{ + flags = MEM_Str; + } + if( nByte>iLimit ){ + if( xDel && xDel!=SQLITE_TRANSIENT ){ + if( xDel==SQLITE_DYNAMIC ){ + sqlcipher_sqlite3DbFree(pMem->db, (void*)z); + }else{ + xDel((void*)z); } - if( rc ) return rc; - pBt->nPage++; - if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ){ pBt->nPage++; } } -#endif - put4byte(28 + (u8*)pBt->pPage1->aData, pBt->nPage); - *pPgno = pBt->nPage; + sqlcipher_sqlite3VdbeMemSetNull(pMem); + return sqlcipher_sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); + } - assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); - rc = btreeGetUnusedPage(pBt, *pPgno, ppPage, bNoContent); - if( rc ) return rc; - rc = sqlcipher_sqlite3PagerWrite((*ppPage)->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(*ppPage); - *ppPage = 0; + /* The following block sets the new values of Mem.z and Mem.xDel. It + ** also sets a flag in local variable "flags" to indicate the memory + ** management (one of MEM_Dyn or MEM_Static). + */ + if( xDel==SQLITE_TRANSIENT ){ + i64 nAlloc = nByte; + if( flags&MEM_Term ){ + nAlloc += (enc==SQLITE_UTF8?1:2); + } + testcase( nAlloc==0 ); + testcase( nAlloc==31 ); + testcase( nAlloc==32 ); + if( sqlcipher_sqlite3VdbeMemClearAndResize(pMem, (int)MAX(nAlloc,32)) ){ + return SQLITE_NOMEM_BKPT; + } + memcpy(pMem->z, z, nAlloc); + }else{ + sqlcipher_sqlite3VdbeMemRelease(pMem); + pMem->z = (char *)z; + if( xDel==SQLITE_DYNAMIC ){ + pMem->zMalloc = pMem->z; + pMem->szMalloc = sqlcipher_sqlite3DbMallocSize(pMem->db, pMem->zMalloc); + }else{ + pMem->xDel = xDel; + flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn); } - TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); } - assert( CORRUPT_DB || *pPgno!=PENDING_BYTE_PAGE(pBt) ); + pMem->n = (int)(nByte & 0x7fffffff); + pMem->flags = flags; + pMem->enc = enc; -end_allocate_page: - releasePage(pTrunk); - releasePage(pPrevTrunk); - assert( rc!=SQLITE_OK || sqlcipher_sqlite3PagerPageRefcount((*ppPage)->pDbPage)<=1 ); - assert( rc!=SQLITE_OK || (*ppPage)->isInit==0 ); - return rc; +#ifndef SQLITE_OMIT_UTF16 + if( enc>SQLITE_UTF8 && sqlcipher_sqlite3VdbeMemHandleBom(pMem) ){ + return SQLITE_NOMEM_BKPT; + } +#endif + + + return SQLITE_OK; } /* -** This function is used to add page iPage to the database file free-list. -** It is assumed that the page is not already a part of the free-list. +** Move data out of a btree key or data field and into a Mem structure. +** The data is payload from the entry that pCur is currently pointing +** to. offset and amt determine what portion of the data or key to retrieve. +** The result is written into the pMem element. ** -** The value passed as the second argument to this function is optional. -** If the caller happens to have a pointer to the MemPage object -** corresponding to page iPage handy, it may pass it as the second value. -** Otherwise, it may pass NULL. +** The pMem object must have been initialized. This routine will use +** pMem->zMalloc to hold the content from the btree, if possible. New +** pMem->zMalloc space will be allocated if necessary. The calling routine +** is responsible for making sure that the pMem object is eventually +** destroyed. ** -** If a pointer to a MemPage object is passed as the second argument, -** its reference count is not altered by this function. +** If this routine fails for any reason (malloc returns NULL or unable +** to read from the disk) then the pMem is left in an inconsistent state. */ -static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ - MemPage *pTrunk = 0; /* Free-list trunk page */ - Pgno iTrunk = 0; /* Page number of free-list trunk page */ - MemPage *pPage1 = pBt->pPage1; /* Local reference to page 1 */ - MemPage *pPage; /* Page being freed. May be NULL. */ - int rc; /* Return Code */ - u32 nFree; /* Initial number of pages on free-list */ - - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - assert( CORRUPT_DB || iPage>1 ); - assert( !pMemPage || pMemPage->pgno==iPage ); - - if( iPage<2 || iPage>pBt->nPage ){ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemFromBtree( + BtCursor *pCur, /* Cursor pointing at record to retrieve. */ + u32 offset, /* Offset from the start of data to return bytes from. */ + u32 amt, /* Number of bytes to return. */ + Mem *pMem /* OUT: Return data in this Mem structure. */ +){ + int rc; + pMem->flags = MEM_Null; + if( sqlcipher_sqlite3BtreeMaxRecordSize(pCur)pDbPage); - }else{ - pPage = btreePageLookup(pBt, iPage); + if( SQLITE_OK==(rc = sqlcipher_sqlite3VdbeMemClearAndResize(pMem, amt+1)) ){ + rc = sqlcipher_sqlite3BtreePayload(pCur, offset, amt, pMem->z); + if( rc==SQLITE_OK ){ + pMem->z[amt] = 0; /* Overrun area used when reading malformed records */ + pMem->flags = MEM_Blob; + pMem->n = (int)amt; + }else{ + sqlcipher_sqlite3VdbeMemRelease(pMem); + } } + return rc; +} +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset( + BtCursor *pCur, /* Cursor pointing at record to retrieve. */ + u32 amt, /* Number of bytes to return. */ + Mem *pMem /* OUT: Return data in this Mem structure. */ +){ + u32 available = 0; /* Number of bytes available on the local btree page */ + int rc = SQLITE_OK; /* Return code */ - /* Increment the free page count on pPage1 */ - rc = sqlcipher_sqlite3PagerWrite(pPage1->pDbPage); - if( rc ) goto freepage_out; - nFree = get4byte(&pPage1->aData[36]); - put4byte(&pPage1->aData[36], nFree+1); + assert( sqlcipher_sqlite3BtreeCursorIsValid(pCur) ); + assert( !VdbeMemDynamic(pMem) ); - if( pBt->btsFlags & BTS_SECURE_DELETE ){ - /* If the secure_delete option is enabled, then - ** always fully overwrite deleted information with zeros. - */ - if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0) ) - || ((rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage))!=0) - ){ - goto freepage_out; - } - memset(pPage->aData, 0, pPage->pBt->pageSize); - } + /* Note: the calls to BtreeKeyFetch() and DataFetch() below assert() + ** that both the BtShared and database handle mutexes are held. */ + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); + pMem->z = (char *)sqlcipher_sqlite3BtreePayloadFetch(pCur, &available); + assert( pMem->z!=0 ); - /* If the database supports auto-vacuum, write an entry in the pointer-map - ** to indicate that the page is free. - */ - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc); - if( rc ) goto freepage_out; + if( amt<=available ){ + pMem->flags = MEM_Blob|MEM_Ephem; + pMem->n = (int)amt; + }else{ + rc = sqlcipher_sqlite3VdbeMemFromBtree(pCur, 0, amt, pMem); } - /* Now manipulate the actual database free-list structure. There are two - ** possibilities. If the free-list is currently empty, or if the first - ** trunk page in the free-list is full, then this page will become a - ** new free-list trunk page. Otherwise, it will become a leaf of the - ** first trunk page in the current free-list. This block tests if it - ** is possible to add the page as a new free-list leaf. - */ - if( nFree!=0 ){ - u32 nLeaf; /* Initial number of leaf cells on trunk page */ - - iTrunk = get4byte(&pPage1->aData[32]); - if( iTrunk>btreePagecount(pBt) ){ - rc = SQLITE_CORRUPT_BKPT; - goto freepage_out; - } - rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); - if( rc!=SQLITE_OK ){ - goto freepage_out; - } + return rc; +} - nLeaf = get4byte(&pTrunk->aData[4]); - assert( pBt->usableSize>32 ); - if( nLeaf > (u32)pBt->usableSize/4 - 2 ){ - rc = SQLITE_CORRUPT_BKPT; - goto freepage_out; +/* +** The pVal argument is known to be a value other than NULL. +** Convert it into a string with encoding enc and return a pointer +** to a zero-terminated version of that string. +*/ +static SQLITE_NOINLINE const void *valueToText(sqlcipher_sqlite3_value* pVal, u8 enc){ + assert( pVal!=0 ); + assert( pVal->db==0 || sqlcipher_sqlite3_mutex_held(pVal->db->mutex) ); + assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) ); + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pVal) ); + assert( (pVal->flags & (MEM_Null))==0 ); + if( pVal->flags & (MEM_Blob|MEM_Str) ){ + if( ExpandBlob(pVal) ) return 0; + pVal->flags |= MEM_Str; + if( pVal->enc != (enc & ~SQLITE_UTF16_ALIGNED) ){ + sqlcipher_sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED); } - if( nLeaf < (u32)pBt->usableSize/4 - 8 ){ - /* In this case there is room on the trunk page to insert the page - ** being freed as a new leaf. - ** - ** Note that the trunk page is not really full until it contains - ** usableSize/4 - 2 entries, not usableSize/4 - 8 entries as we have - ** coded. But due to a coding error in versions of SQLite prior to - ** 3.6.0, databases with freelist trunk pages holding more than - ** usableSize/4 - 8 entries will be reported as corrupt. In order - ** to maintain backwards compatibility with older versions of SQLite, - ** we will continue to restrict the number of entries to usableSize/4 - 8 - ** for now. At some point in the future (once everyone has upgraded - ** to 3.6.0 or later) we should consider fixing the conditional above - ** to read "usableSize/4-2" instead of "usableSize/4-8". - ** - ** EVIDENCE-OF: R-19920-11576 However, newer versions of SQLite still - ** avoid using the last six entries in the freelist trunk page array in - ** order that database files created by newer versions of SQLite can be - ** read by older versions of SQLite. - */ - rc = sqlcipher_sqlite3PagerWrite(pTrunk->pDbPage); - if( rc==SQLITE_OK ){ - put4byte(&pTrunk->aData[4], nLeaf+1); - put4byte(&pTrunk->aData[8+nLeaf*4], iPage); - if( pPage && (pBt->btsFlags & BTS_SECURE_DELETE)==0 ){ - sqlcipher_sqlite3PagerDontWrite(pPage->pDbPage); - } - rc = btreeSetHasContent(pBt, iPage); + if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){ + assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 ); + if( sqlcipher_sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){ + return 0; } - TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); - goto freepage_out; } + sqlcipher_sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-31275-44060 */ + }else{ + sqlcipher_sqlite3VdbeMemStringify(pVal, enc, 0); + assert( 0==(1&SQLITE_PTR_TO_INT(pVal->z)) ); + } + assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0 + || pVal->db->mallocFailed ); + if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){ + assert( sqlcipher_sqlite3VdbeMemValidStrRep(pVal) ); + return pVal->z; + }else{ + return 0; } +} - /* If control flows to this point, then it was not possible to add the - ** the page being freed as a leaf page of the first trunk in the free-list. - ** Possibly because the free-list is empty, or possibly because the - ** first trunk in the free-list is full. Either way, the page being freed - ** will become the new first trunk page in the free-list. - */ - if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0)) ){ - goto freepage_out; +/* This function is only available internally, it is not part of the +** external API. It works in a similar way to sqlcipher_sqlite3_value_text(), +** except the data returned is in the encoding specified by the second +** parameter, which must be one of SQLITE_UTF16BE, SQLITE_UTF16LE or +** SQLITE_UTF8. +** +** (2006-02-16:) The enc value can be or-ed with SQLITE_UTF16_ALIGNED. +** If that is the case, then the result must be aligned on an even byte +** boundary. +*/ +SQLITE_PRIVATE const void *sqlcipher_sqlite3ValueText(sqlcipher_sqlite3_value* pVal, u8 enc){ + if( !pVal ) return 0; + assert( pVal->db==0 || sqlcipher_sqlite3_mutex_held(pVal->db->mutex) ); + assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) ); + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pVal) ); + if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){ + assert( sqlcipher_sqlite3VdbeMemValidStrRep(pVal) ); + return pVal->z; } - rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage); - if( rc!=SQLITE_OK ){ - goto freepage_out; + if( pVal->flags&MEM_Null ){ + return 0; } - put4byte(pPage->aData, iTrunk); - put4byte(&pPage->aData[4], 0); - put4byte(&pPage1->aData[32], iPage); - TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", pPage->pgno, iTrunk)); + return valueToText(pVal, enc); +} -freepage_out: - if( pPage ){ - pPage->isInit = 0; +/* +** Create a new sqlcipher_sqlite3_value object. +*/ +SQLITE_PRIVATE sqlcipher_sqlite3_value *sqlcipher_sqlite3ValueNew(sqlcipher_sqlite3 *db){ + Mem *p = sqlcipher_sqlite3DbMallocZero(db, sizeof(*p)); + if( p ){ + p->flags = MEM_Null; + p->db = db; } - releasePage(pPage); - releasePage(pTrunk); - return rc; + return p; } -static void freePage(MemPage *pPage, int *pRC){ - if( (*pRC)==SQLITE_OK ){ - *pRC = freePage2(pPage->pBt, pPage, pPage->pgno); + +/* +** Context object passed by sqlcipher_sqlite3Stat4ProbeSetValue() through to +** valueNew(). See comments above valueNew() for details. +*/ +struct ValueNewStat4Ctx { + Parse *pParse; + Index *pIdx; + UnpackedRecord **ppRec; + int iVal; +}; + +/* +** Allocate and return a pointer to a new sqlcipher_sqlite3_value object. If +** the second argument to this function is NULL, the object is allocated +** by calling sqlcipher_sqlite3ValueNew(). +** +** Otherwise, if the second argument is non-zero, then this function is +** being called indirectly by sqlcipher_sqlite3Stat4ProbeSetValue(). If it has not +** already been allocated, allocate the UnpackedRecord structure that +** that function will return to its caller here. Then return a pointer to +** an sqlcipher_sqlite3_value within the UnpackedRecord.a[] array. +*/ +static sqlcipher_sqlite3_value *valueNew(sqlcipher_sqlite3 *db, struct ValueNewStat4Ctx *p){ +#ifdef SQLITE_ENABLE_STAT4 + if( p ){ + UnpackedRecord *pRec = p->ppRec[0]; + + if( pRec==0 ){ + Index *pIdx = p->pIdx; /* Index being probed */ + int nByte; /* Bytes of space to allocate */ + int i; /* Counter variable */ + int nCol = pIdx->nColumn; /* Number of index columns including rowid */ + + nByte = sizeof(Mem) * nCol + ROUND8(sizeof(UnpackedRecord)); + pRec = (UnpackedRecord*)sqlcipher_sqlite3DbMallocZero(db, nByte); + if( pRec ){ + pRec->pKeyInfo = sqlcipher_sqlite3KeyInfoOfIndex(p->pParse, pIdx); + if( pRec->pKeyInfo ){ + assert( pRec->pKeyInfo->nAllField==nCol ); + assert( pRec->pKeyInfo->enc==ENC(db) ); + pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord))); + for(i=0; iaMem[i].flags = MEM_Null; + pRec->aMem[i].db = db; + } + }else{ + sqlcipher_sqlite3DbFreeNN(db, pRec); + pRec = 0; + } + } + if( pRec==0 ) return 0; + p->ppRec[0] = pRec; + } + + pRec->nField = p->iVal+1; + return &pRec->aMem[p->iVal]; } +#else + UNUSED_PARAMETER(p); +#endif /* defined(SQLITE_ENABLE_STAT4) */ + return sqlcipher_sqlite3ValueNew(db); } /* -** Free any overflow pages associated with the given Cell. Store -** size information about the cell in pInfo. +** The expression object indicated by the second argument is guaranteed +** to be a scalar SQL function. If +** +** * all function arguments are SQL literals, +** * one of the SQLITE_FUNC_CONSTANT or _SLOCHNG function flags is set, and +** * the SQLITE_FUNC_NEEDCOLL function flag is not set, +** +** then this routine attempts to invoke the SQL function. Assuming no +** error occurs, output parameter (*ppVal) is set to point to a value +** object containing the result before returning SQLITE_OK. +** +** Affinity aff is applied to the result of the function before returning. +** If the result is a text value, the sqlcipher_sqlite3_value object uses encoding +** enc. +** +** If the conditions above are not met, this function returns SQLITE_OK +** and sets (*ppVal) to NULL. Or, if an error occurs, (*ppVal) is set to +** NULL and an SQLite error code returned. */ -static int clearCell( - MemPage *pPage, /* The page that contains the Cell */ - unsigned char *pCell, /* First byte of the Cell */ - CellInfo *pInfo /* Size information about the cell */ +#ifdef SQLITE_ENABLE_STAT4 +static int valueFromFunction( + sqlcipher_sqlite3 *db, /* The database connection */ + const Expr *p, /* The expression to evaluate */ + u8 enc, /* Encoding to use */ + u8 aff, /* Affinity to use */ + sqlcipher_sqlite3_value **ppVal, /* Write the new value here */ + struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */ ){ - BtShared *pBt; - Pgno ovflPgno; - int rc; - int nOvfl; - u32 ovflPageSize; + sqlcipher_sqlite3_context ctx; /* Context object for function invocation */ + sqlcipher_sqlite3_value **apVal = 0; /* Function arguments */ + int nVal = 0; /* Size of apVal[] array */ + FuncDef *pFunc = 0; /* Function definition */ + sqlcipher_sqlite3_value *pVal = 0; /* New value */ + int rc = SQLITE_OK; /* Return code */ + ExprList *pList = 0; /* Function arguments */ + int i; /* Iterator variable */ - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - pPage->xParseCell(pPage, pCell, pInfo); - if( pInfo->nLocal==pInfo->nPayload ){ - return SQLITE_OK; /* No overflow pages. Return without doing anything */ - } - testcase( pCell + pInfo->nSize == pPage->aDataEnd ); - testcase( pCell + (pInfo->nSize-1) == pPage->aDataEnd ); - if( pCell + pInfo->nSize > pPage->aDataEnd ){ - /* Cell extends past end of page */ - return SQLITE_CORRUPT_PAGE(pPage); + assert( pCtx!=0 ); + assert( (p->flags & EP_TokenOnly)==0 ); + assert( ExprUseXList(p) ); + pList = p->x.pList; + if( pList ) nVal = pList->nExpr; + assert( !ExprHasProperty(p, EP_IntValue) ); + pFunc = sqlcipher_sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0); + assert( pFunc ); + if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 + || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) + ){ + return SQLITE_OK; } - ovflPgno = get4byte(pCell + pInfo->nSize - 4); - pBt = pPage->pBt; - assert( pBt->usableSize > 4 ); - ovflPageSize = pBt->usableSize - 4; - nOvfl = (pInfo->nPayload - pInfo->nLocal + ovflPageSize - 1)/ovflPageSize; - assert( nOvfl>0 || - (CORRUPT_DB && (pInfo->nPayload + ovflPageSize)btreePagecount(pBt) ){ - /* 0 is not a legal page number and page 1 cannot be an - ** overflow page. Therefore if ovflPgno<2 or past the end of the - ** file the database must be corrupt. */ - return SQLITE_CORRUPT_BKPT; + + if( pList ){ + apVal = (sqlcipher_sqlite3_value**)sqlcipher_sqlite3DbMallocZero(db, sizeof(apVal[0]) * nVal); + if( apVal==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto value_from_function_out; } - if( nOvfl ){ - rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext); - if( rc ) return rc; + for(i=0; ia[i].pExpr, enc, aff, &apVal[i]); + if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out; } + } - if( ( pOvfl || ((pOvfl = btreePageLookup(pBt, ovflPgno))!=0) ) - && sqlcipher_sqlite3PagerPageRefcount(pOvfl->pDbPage)!=1 - ){ - /* There is no reason any cursor should have an outstanding reference - ** to an overflow page belonging to a cell that is being deleted/updated. - ** So if there exists more than one reference to this page, then it - ** must not really be an overflow page and the database must be corrupt. - ** It is helpful to detect this before calling freePage2(), as - ** freePage2() may zero the page contents if secure-delete mode is - ** enabled. If this 'overflow' page happens to be a page that the - ** caller is iterating through or using in some other way, this - ** can be problematic. - */ - rc = SQLITE_CORRUPT_BKPT; - }else{ - rc = freePage2(pBt, pOvfl, ovflPgno); + pVal = valueNew(db, pCtx); + if( pVal==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto value_from_function_out; + } + + testcase( pCtx->pParse->rc==SQLITE_ERROR ); + testcase( pCtx->pParse->rc==SQLITE_OK ); + memset(&ctx, 0, sizeof(ctx)); + ctx.pOut = pVal; + ctx.pFunc = pFunc; + ctx.enc = ENC(db); + pFunc->xSFunc(&ctx, nVal, apVal); + if( ctx.isError ){ + rc = ctx.isError; + sqlcipher_sqlite3ErrorMsg(pCtx->pParse, "%s", sqlcipher_sqlite3_value_text(pVal)); + }else{ + sqlcipher_sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8); + assert( rc==SQLITE_OK ); + rc = sqlcipher_sqlite3VdbeChangeEncoding(pVal, enc); + if( rc==SQLITE_OK && sqlcipher_sqlite3VdbeMemTooBig(pVal) ){ + rc = SQLITE_TOOBIG; + pCtx->pParse->nErr++; } + } + pCtx->pParse->rc = rc; - if( pOvfl ){ - sqlcipher_sqlite3PagerUnref(pOvfl->pDbPage); + value_from_function_out: + if( rc!=SQLITE_OK ){ + pVal = 0; + } + if( apVal ){ + for(i=0; iaData -** area. pCell might point to some temporary storage. The cell will -** be constructed in this temporary area then copied into pPage->aData -** later. +** If pCtx is NULL and an error occurs after the sqlcipher_sqlite3_value object +** has been allocated, it is freed before returning. Or, if pCtx is not +** NULL, it is assumed that the caller will free any allocated object +** in all cases. */ -static int fillInCell( - MemPage *pPage, /* The page that contains the cell */ - unsigned char *pCell, /* Complete text of the cell */ - const BtreePayload *pX, /* Payload with which to construct the cell */ - int *pnSize /* Write cell size here */ +static int valueFromExpr( + sqlcipher_sqlite3 *db, /* The database connection */ + const Expr *pExpr, /* The expression to evaluate */ + u8 enc, /* Encoding to use */ + u8 affinity, /* Affinity to use */ + sqlcipher_sqlite3_value **ppVal, /* Write the new value here */ + struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */ ){ - int nPayload; - const u8 *pSrc; - int nSrc, n, rc, mn; - int spaceLeft; - MemPage *pToRelease; - unsigned char *pPrior; - unsigned char *pPayload; - BtShared *pBt; - Pgno pgnoOvfl; - int nHeader; - - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); + int op; + char *zVal = 0; + sqlcipher_sqlite3_value *pVal = 0; + int negInt = 1; + const char *zNeg = ""; + int rc = SQLITE_OK; - /* pPage is not necessarily writeable since pCell might be auxiliary - ** buffer space that is separate from the pPage buffer area */ - assert( pCellaData || pCell>=&pPage->aData[pPage->pBt->pageSize] - || sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); + assert( pExpr!=0 ); + while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft; + if( op==TK_REGISTER ) op = pExpr->op2; - /* Fill in the header. */ - nHeader = pPage->childPtrSize; - if( pPage->intKey ){ - nPayload = pX->nData + pX->nZero; - pSrc = pX->pData; - nSrc = pX->nData; - assert( pPage->intKeyLeaf ); /* fillInCell() only called for leaves */ - nHeader += putVarint32(&pCell[nHeader], nPayload); - nHeader += putVarint(&pCell[nHeader], *(u64*)&pX->nKey); - }else{ - assert( pX->nKey<=0x7fffffff && pX->pKey!=0 ); - nSrc = nPayload = (int)pX->nKey; - pSrc = pX->pKey; - nHeader += putVarint32(&pCell[nHeader], nPayload); - } + /* Compressed expressions only appear when parsing the DEFAULT clause + ** on a table column definition, and hence only when pCtx==0. This + ** check ensures that an EP_TokenOnly expression is never passed down + ** into valueFromFunction(). */ + assert( (pExpr->flags & EP_TokenOnly)==0 || pCtx==0 ); - /* Fill in the payload */ - pPayload = &pCell[nHeader]; - if( nPayload<=pPage->maxLocal ){ - /* This is the common case where everything fits on the btree page - ** and no overflow pages are required. */ - n = nHeader + nPayload; - testcase( n==3 ); - testcase( n==4 ); - if( n<4 ) n = 4; - *pnSize = n; - assert( nSrc<=nPayload ); - testcase( nSrcu.zToken,0); + rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx); + testcase( rc!=SQLITE_OK ); + if( *ppVal ){ + sqlcipher_sqlite3VdbeMemCast(*ppVal, aff, enc); + sqlcipher_sqlite3ValueApplyAffinity(*ppVal, affinity, enc); + } + return rc; } - /* If we reach this point, it means that some of the content will need - ** to spill onto overflow pages. + /* Handle negative integers in a single step. This is needed in the + ** case when the value is -9223372036854775808. */ - mn = pPage->minLocal; - n = mn + (nPayload - mn) % (pPage->pBt->usableSize - 4); - testcase( n==pPage->maxLocal ); - testcase( n==pPage->maxLocal+1 ); - if( n > pPage->maxLocal ) n = mn; - spaceLeft = n; - *pnSize = n + nHeader + 4; - pPrior = &pCell[nHeader+n]; - pToRelease = 0; - pgnoOvfl = 0; - pBt = pPage->pBt; + if( op==TK_UMINUS + && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){ + pExpr = pExpr->pLeft; + op = pExpr->op; + negInt = -1; + zNeg = "-"; + } - /* At this point variables should be set as follows: - ** - ** nPayload Total payload size in bytes - ** pPayload Begin writing payload here - ** spaceLeft Space available at pPayload. If nPayload>spaceLeft, - ** that means content must spill into overflow pages. - ** *pnSize Size of the local cell (not counting overflow pages) - ** pPrior Where to write the pgno of the first overflow page - ** - ** Use a call to btreeParseCellPtr() to verify that the values above - ** were computed correctly. - */ -#ifdef SQLITE_DEBUG - { - CellInfo info; - pPage->xParseCell(pPage, pCell, &info); - assert( nHeader==(int)(info.pPayload - pCell) ); - assert( info.nKey==pX->nKey ); - assert( *pnSize == info.nSize ); - assert( spaceLeft == info.nLocal ); + if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ + pVal = valueNew(db, pCtx); + if( pVal==0 ) goto no_mem; + if( ExprHasProperty(pExpr, EP_IntValue) ){ + sqlcipher_sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); + }else{ + zVal = sqlcipher_sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); + if( zVal==0 ) goto no_mem; + sqlcipher_sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); + } + if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){ + sqlcipher_sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); + }else{ + sqlcipher_sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); + } + assert( (pVal->flags & MEM_IntReal)==0 ); + if( pVal->flags & (MEM_Int|MEM_IntReal|MEM_Real) ){ + testcase( pVal->flags & MEM_Int ); + testcase( pVal->flags & MEM_Real ); + pVal->flags &= ~MEM_Str; + } + if( enc!=SQLITE_UTF8 ){ + rc = sqlcipher_sqlite3VdbeChangeEncoding(pVal, enc); + } + }else if( op==TK_UMINUS ) { + /* This branch happens for multiple negative signs. Ex: -(-5) */ + if( SQLITE_OK==valueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal,pCtx) + && pVal!=0 + ){ + sqlcipher_sqlite3VdbeMemNumerify(pVal); + if( pVal->flags & MEM_Real ){ + pVal->u.r = -pVal->u.r; + }else if( pVal->u.i==SMALLEST_INT64 ){ +#ifndef SQLITE_OMIT_FLOATING_POINT + pVal->u.r = -(double)SMALLEST_INT64; +#else + pVal->u.r = LARGEST_INT64; +#endif + MemSetTypeFlag(pVal, MEM_Real); + }else{ + pVal->u.i = -pVal->u.i; + } + sqlcipher_sqlite3ValueApplyAffinity(pVal, affinity, enc); + } + }else if( op==TK_NULL ){ + pVal = valueNew(db, pCtx); + if( pVal==0 ) goto no_mem; + sqlcipher_sqlite3VdbeMemSetNull(pVal); + } +#ifndef SQLITE_OMIT_BLOB_LITERAL + else if( op==TK_BLOB ){ + int nVal; + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); + assert( pExpr->u.zToken[1]=='\'' ); + pVal = valueNew(db, pCtx); + if( !pVal ) goto no_mem; + zVal = &pExpr->u.zToken[2]; + nVal = sqlcipher_sqlite3Strlen30(zVal)-1; + assert( zVal[nVal]=='\'' ); + sqlcipher_sqlite3VdbeMemSetStr(pVal, sqlcipher_sqlite3HexToBlob(db, zVal, nVal), nVal/2, + 0, SQLITE_DYNAMIC); + } +#endif +#ifdef SQLITE_ENABLE_STAT4 + else if( op==TK_FUNCTION && pCtx!=0 ){ + rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx); } #endif + else if( op==TK_TRUEFALSE ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + pVal = valueNew(db, pCtx); + if( pVal ){ + pVal->flags = MEM_Int; + pVal->u.i = pExpr->u.zToken[4]==0; + } + } - /* Write the payload into the local Cell and any extra into overflow pages */ - while( 1 ){ - n = nPayload; - if( n>spaceLeft ) n = spaceLeft; + *ppVal = pVal; + return rc; - /* If pToRelease is not zero than pPayload points into the data area - ** of pToRelease. Make sure pToRelease is still writeable. */ - assert( pToRelease==0 || sqlcipher_sqlite3PagerIswriteable(pToRelease->pDbPage) ); +no_mem: +#ifdef SQLITE_ENABLE_STAT4 + if( pCtx==0 || NEVER(pCtx->pParse->nErr==0) ) +#endif + sqlcipher_sqlite3OomFault(db); + sqlcipher_sqlite3DbFree(db, zVal); + assert( *ppVal==0 ); +#ifdef SQLITE_ENABLE_STAT4 + if( pCtx==0 ) sqlcipher_sqlite3ValueFree(pVal); +#else + assert( pCtx==0 ); sqlcipher_sqlite3ValueFree(pVal); +#endif + return SQLITE_NOMEM_BKPT; +} - /* If pPayload is part of the data area of pPage, then make sure pPage - ** is still writeable */ - assert( pPayloadaData || pPayload>=&pPage->aData[pBt->pageSize] - || sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); +/* +** Create a new sqlcipher_sqlite3_value object, containing the value of pExpr. +** +** This only works for very simple expressions that consist of one constant +** token (i.e. "5", "5.1", "'a string'"). If the expression can +** be converted directly into a value, then the value is allocated and +** a pointer written to *ppVal. The caller is responsible for deallocating +** the value by passing it to sqlcipher_sqlite3ValueFree() later on. If the expression +** cannot be converted to a value, then *ppVal is set to NULL. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3ValueFromExpr( + sqlcipher_sqlite3 *db, /* The database connection */ + const Expr *pExpr, /* The expression to evaluate */ + u8 enc, /* Encoding to use */ + u8 affinity, /* Affinity to use */ + sqlcipher_sqlite3_value **ppVal /* Write the new value here */ +){ + return pExpr ? valueFromExpr(db, pExpr, enc, affinity, ppVal, 0) : 0; +} - if( nSrc>=n ){ - memcpy(pPayload, pSrc, n); - }else if( nSrc>0 ){ - n = nSrc; - memcpy(pPayload, pSrc, n); - }else{ - memset(pPayload, 0, n); +#ifdef SQLITE_ENABLE_STAT4 +/* +** Attempt to extract a value from pExpr and use it to construct *ppVal. +** +** If pAlloc is not NULL, then an UnpackedRecord object is created for +** pAlloc if one does not exist and the new value is added to the +** UnpackedRecord object. +** +** A value is extracted in the following cases: +** +** * (pExpr==0). In this case the value is assumed to be an SQL NULL, +** +** * The expression is a bound variable, and this is a reprepare, or +** +** * The expression is a literal value. +** +** On success, *ppVal is made to point to the extracted value. The caller +** is responsible for ensuring that the value is eventually freed. +*/ +static int stat4ValueFromExpr( + Parse *pParse, /* Parse context */ + Expr *pExpr, /* The expression to extract a value from */ + u8 affinity, /* Affinity to use */ + struct ValueNewStat4Ctx *pAlloc,/* How to allocate space. Or NULL */ + sqlcipher_sqlite3_value **ppVal /* OUT: New value object (or NULL) */ +){ + int rc = SQLITE_OK; + sqlcipher_sqlite3_value *pVal = 0; + sqlcipher_sqlite3 *db = pParse->db; + + /* Skip over any TK_COLLATE nodes */ + pExpr = sqlcipher_sqlite3ExprSkipCollate(pExpr); + + assert( pExpr==0 || pExpr->op!=TK_REGISTER || pExpr->op2!=TK_VARIABLE ); + if( !pExpr ){ + pVal = valueNew(db, pAlloc); + if( pVal ){ + sqlcipher_sqlite3VdbeMemSetNull((Mem*)pVal); } - nPayload -= n; - if( nPayload<=0 ) break; - pPayload += n; - pSrc += n; - nSrc -= n; - spaceLeft -= n; - if( spaceLeft==0 ){ - MemPage *pOvfl = 0; -#ifndef SQLITE_OMIT_AUTOVACUUM - Pgno pgnoPtrmap = pgnoOvfl; /* Overflow page pointer-map entry page */ - if( pBt->autoVacuum ){ - do{ - pgnoOvfl++; - } while( - PTRMAP_ISPAGE(pBt, pgnoOvfl) || pgnoOvfl==PENDING_BYTE_PAGE(pBt) - ); - } -#endif - rc = allocateBtreePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl, 0); -#ifndef SQLITE_OMIT_AUTOVACUUM - /* If the database supports auto-vacuum, and the second or subsequent - ** overflow page is being allocated, add an entry to the pointer-map - ** for that page now. - ** - ** If this is the first overflow page, then write a partial entry - ** to the pointer-map. If we write nothing to this pointer-map slot, - ** then the optimistic overflow chain processing in clearCell() - ** may misinterpret the uninitialized values and delete the - ** wrong pages from the database. - */ - if( pBt->autoVacuum && rc==SQLITE_OK ){ - u8 eType = (pgnoPtrmap?PTRMAP_OVERFLOW2:PTRMAP_OVERFLOW1); - ptrmapPut(pBt, pgnoOvfl, eType, pgnoPtrmap, &rc); - if( rc ){ - releasePage(pOvfl); - } - } -#endif - if( rc ){ - releasePage(pToRelease); - return rc; + }else if( pExpr->op==TK_VARIABLE && (db->flags & SQLITE_EnableQPSG)==0 ){ + Vdbe *v; + int iBindVar = pExpr->iColumn; + sqlcipher_sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar); + if( (v = pParse->pReprepare)!=0 ){ + pVal = valueNew(db, pAlloc); + if( pVal ){ + rc = sqlcipher_sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]); + sqlcipher_sqlite3ValueApplyAffinity(pVal, affinity, ENC(db)); + pVal->db = pParse->db; } + } + }else{ + rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, pAlloc); + } - /* If pToRelease is not zero than pPrior points into the data area - ** of pToRelease. Make sure pToRelease is still writeable. */ - assert( pToRelease==0 || sqlcipher_sqlite3PagerIswriteable(pToRelease->pDbPage) ); + assert( pVal==0 || pVal->db==db ); + *ppVal = pVal; + return rc; +} - /* If pPrior is part of the data area of pPage, then make sure pPage - ** is still writeable */ - assert( pPrioraData || pPrior>=&pPage->aData[pBt->pageSize] - || sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); +/* +** This function is used to allocate and populate UnpackedRecord +** structures intended to be compared against sample index keys stored +** in the sqlite_stat4 table. +** +** A single call to this function populates zero or more fields of the +** record starting with field iVal (fields are numbered from left to +** right starting with 0). A single field is populated if: +** +** * (pExpr==0). In this case the value is assumed to be an SQL NULL, +** +** * The expression is a bound variable, and this is a reprepare, or +** +** * The sqlcipher_sqlite3ValueFromExpr() function is able to extract a value +** from the expression (i.e. the expression is a literal value). +** +** Or, if pExpr is a TK_VECTOR, one field is populated for each of the +** vector components that match either of the two latter criteria listed +** above. +** +** Before any value is appended to the record, the affinity of the +** corresponding column within index pIdx is applied to it. Before +** this function returns, output parameter *pnExtract is set to the +** number of values appended to the record. +** +** When this function is called, *ppRec must either point to an object +** allocated by an earlier call to this function, or must be NULL. If it +** is NULL and a value can be successfully extracted, a new UnpackedRecord +** is allocated (and *ppRec set to point to it) before returning. +** +** Unless an error is encountered, SQLITE_OK is returned. It is not an +** error if a value cannot be extracted from pExpr. If an error does +** occur, an SQLite error code is returned. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3Stat4ProbeSetValue( + Parse *pParse, /* Parse context */ + Index *pIdx, /* Index being probed */ + UnpackedRecord **ppRec, /* IN/OUT: Probe record */ + Expr *pExpr, /* The expression to extract a value from */ + int nElem, /* Maximum number of values to append */ + int iVal, /* Array element to populate */ + int *pnExtract /* OUT: Values appended to the record */ +){ + int rc = SQLITE_OK; + int nExtract = 0; - put4byte(pPrior, pgnoOvfl); - releasePage(pToRelease); - pToRelease = pOvfl; - pPrior = pOvfl->aData; - put4byte(pPrior, 0); - pPayload = &pOvfl->aData[4]; - spaceLeft = pBt->usableSize - 4; + if( pExpr==0 || pExpr->op!=TK_SELECT ){ + int i; + struct ValueNewStat4Ctx alloc; + + alloc.pParse = pParse; + alloc.pIdx = pIdx; + alloc.ppRec = ppRec; + + for(i=0; idb, pIdx, iVal+i); + alloc.iVal = iVal+i; + rc = stat4ValueFromExpr(pParse, pElem, aff, &alloc, &pVal); + if( !pVal ) break; + nExtract++; } } - releasePage(pToRelease); + + *pnExtract = nExtract; + return rc; +} + +/* +** Attempt to extract a value from expression pExpr using the methods +** as described for sqlcipher_sqlite3Stat4ProbeSetValue() above. +** +** If successful, set *ppVal to point to a new value object and return +** SQLITE_OK. If no value can be extracted, but no other error occurs +** (e.g. OOM), return SQLITE_OK and set *ppVal to NULL. Or, if an error +** does occur, return an SQLite error code. The final value of *ppVal +** is undefined in this case. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3Stat4ValueFromExpr( + Parse *pParse, /* Parse context */ + Expr *pExpr, /* The expression to extract a value from */ + u8 affinity, /* Affinity to use */ + sqlcipher_sqlite3_value **ppVal /* OUT: New value object (or NULL) */ +){ + return stat4ValueFromExpr(pParse, pExpr, affinity, 0, ppVal); +} + +/* +** Extract the iCol-th column from the nRec-byte record in pRec. Write +** the column value into *ppVal. If *ppVal is initially NULL then a new +** sqlcipher_sqlite3_value object is allocated. +** +** If *ppVal is initially NULL then the caller is responsible for +** ensuring that the value written into *ppVal is eventually freed. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3Stat4Column( + sqlcipher_sqlite3 *db, /* Database handle */ + const void *pRec, /* Pointer to buffer containing record */ + int nRec, /* Size of buffer pRec in bytes */ + int iCol, /* Column to extract */ + sqlcipher_sqlite3_value **ppVal /* OUT: Extracted value */ +){ + u32 t = 0; /* a column type code */ + int nHdr; /* Size of the header in the record */ + int iHdr; /* Next unread header byte */ + int iField; /* Next unread data byte */ + int szField = 0; /* Size of the current data field */ + int i; /* Column index */ + u8 *a = (u8*)pRec; /* Typecast byte array */ + Mem *pMem = *ppVal; /* Write result into this Mem object */ + + assert( iCol>0 ); + iHdr = getVarint32(a, nHdr); + if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; + iField = nHdr; + for(i=0; i<=iCol; i++){ + iHdr += getVarint32(&a[iHdr], t); + testcase( iHdr==nHdr ); + testcase( iHdr==nHdr+1 ); + if( iHdr>nHdr ) return SQLITE_CORRUPT_BKPT; + szField = sqlcipher_sqlite3VdbeSerialTypeLen(t); + iField += szField; + } + testcase( iField==nRec ); + testcase( iField==nRec+1 ); + if( iField>nRec ) return SQLITE_CORRUPT_BKPT; + if( pMem==0 ){ + pMem = *ppVal = sqlcipher_sqlite3ValueNew(db); + if( pMem==0 ) return SQLITE_NOMEM_BKPT; + } + sqlcipher_sqlite3VdbeSerialGet(&a[iField-szField], t, pMem); + pMem->enc = ENC(db); return SQLITE_OK; } /* -** Remove the i-th cell from pPage. This routine effects pPage only. -** The cell content is not freed or deallocated. It is assumed that -** the cell content has been copied someplace else. This routine just -** removes the reference to the cell from pPage. -** -** "sz" must be the number of bytes in the cell. +** Unless it is NULL, the argument must be an UnpackedRecord object returned +** by an earlier call to sqlcipher_sqlite3Stat4ProbeSetValue(). This call deletes +** the object. */ -static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ - u32 pc; /* Offset to cell content of cell being deleted */ - u8 *data; /* pPage->aData */ - u8 *ptr; /* Used to move bytes around within data[] */ - int rc; /* The return code */ - int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ - - if( *pRC ) return; - assert( idx>=0 && idxnCell ); - assert( CORRUPT_DB || sz==cellSize(pPage, idx) ); - assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( pPage->nFree>=0 ); - data = pPage->aData; - ptr = &pPage->aCellIdx[2*idx]; - pc = get2byte(ptr); - hdr = pPage->hdrOffset; - testcase( pc==get2byte(&data[hdr+5]) ); - testcase( pc+sz==pPage->pBt->usableSize ); - if( pc+sz > pPage->pBt->usableSize ){ - *pRC = SQLITE_CORRUPT_BKPT; - return; - } - rc = freeSpace(pPage, pc, sz); - if( rc ){ - *pRC = rc; - return; - } - pPage->nCell--; - if( pPage->nCell==0 ){ - memset(&data[hdr+1], 0, 4); - data[hdr+7] = 0; - put2byte(&data[hdr+5], pPage->pBt->usableSize); - pPage->nFree = pPage->pBt->usableSize - pPage->hdrOffset - - pPage->childPtrSize - 8; - }else{ - memmove(ptr, ptr+2, 2*(pPage->nCell - idx)); - put2byte(&data[hdr+3], pPage->nCell); - pPage->nFree += 2; +SQLITE_PRIVATE void sqlcipher_sqlite3Stat4ProbeFree(UnpackedRecord *pRec){ + if( pRec ){ + int i; + int nCol = pRec->pKeyInfo->nAllField; + Mem *aMem = pRec->aMem; + sqlcipher_sqlite3 *db = aMem[0].db; + for(i=0; ipKeyInfo); + sqlcipher_sqlite3DbFreeNN(db, pRec); } } +#endif /* ifdef SQLITE_ENABLE_STAT4 */ /* -** Insert a new cell on pPage at cell index "i". pCell points to the -** content of the cell. -** -** If the cell content will fit on the page, then put it there. If it -** will not fit, then make a copy of the cell content into pTemp if -** pTemp is not null. Regardless of pTemp, allocate a new entry -** in pPage->apOvfl[] and make it point to the cell content (either -** in pTemp or the original pCell) and also record its index. -** Allocating a new entry in pPage->aCell[] implies that -** pPage->nOverflow is incremented. -** -** *pRC must be SQLITE_OK when this routine is called. +** Change the string value of an sqlcipher_sqlite3_value object */ -static void insertCell( - MemPage *pPage, /* Page into which we are copying */ - int i, /* New cell becomes the i-th cell of the page */ - u8 *pCell, /* Content of the new cell */ - int sz, /* Bytes of content in pCell */ - u8 *pTemp, /* Temp storage space for pCell, if needed */ - Pgno iChild, /* If non-zero, replace first 4 bytes with this value */ - int *pRC /* Read and write return code from here */ +SQLITE_PRIVATE void sqlcipher_sqlite3ValueSetStr( + sqlcipher_sqlite3_value *v, /* Value to be set */ + int n, /* Length of string z */ + const void *z, /* Text of the new string */ + u8 enc, /* Encoding to use */ + void (*xDel)(void*) /* Destructor for the string */ ){ - int idx = 0; /* Where to write new cell content in data[] */ - int j; /* Loop counter */ - u8 *data; /* The content of the whole page */ - u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */ + if( v ) sqlcipher_sqlite3VdbeMemSetStr((Mem *)v, z, n, enc, xDel); +} - assert( *pRC==SQLITE_OK ); - assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); - assert( MX_CELL(pPage->pBt)<=10921 ); - assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB ); - assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) ); - assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) ); - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB ); - assert( pPage->nFree>=0 ); - if( pPage->nOverflow || sz+2>pPage->nFree ){ - if( pTemp ){ - memcpy(pTemp, pCell, sz); - pCell = pTemp; - } - if( iChild ){ - put4byte(pCell, iChild); - } - j = pPage->nOverflow++; - /* Comparison against ArraySize-1 since we hold back one extra slot - ** as a contingency. In other words, never need more than 3 overflow - ** slots but 4 are allocated, just to be safe. */ - assert( j < ArraySize(pPage->apOvfl)-1 ); - pPage->apOvfl[j] = pCell; - pPage->aiOvfl[j] = (u16)i; +/* +** Free an sqlcipher_sqlite3_value object +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3ValueFree(sqlcipher_sqlite3_value *v){ + if( !v ) return; + sqlcipher_sqlite3VdbeMemRelease((Mem *)v); + sqlcipher_sqlite3DbFreeNN(((Mem*)v)->db, v); +} - /* When multiple overflows occur, they are always sequential and in - ** sorted order. This invariants arise because multiple overflows can - ** only occur when inserting divider cells into the parent page during - ** balancing, and the dividers are adjacent and sorted. - */ - assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */ - assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */ - }else{ - int rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage); - if( rc!=SQLITE_OK ){ - *pRC = rc; - return; - } - assert( sqlcipher_sqlite3PagerIswriteable(pPage->pDbPage) ); - data = pPage->aData; - assert( &data[pPage->cellOffset]==pPage->aCellIdx ); - rc = allocateSpace(pPage, sz, &idx); - if( rc ){ *pRC = rc; return; } - /* The allocateSpace() routine guarantees the following properties - ** if it returns successfully */ - assert( idx >= 0 ); - assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB ); - assert( idx+sz <= (int)pPage->pBt->usableSize ); - pPage->nFree -= (u16)(2 + sz); - if( iChild ){ - /* In a corrupt database where an entry in the cell index section of - ** a btree page has a value of 3 or less, the pCell value might point - ** as many as 4 bytes in front of the start of the aData buffer for - ** the source page. Make sure this does not cause problems by not - ** reading the first 4 bytes */ - memcpy(&data[idx+4], pCell+4, sz-4); - put4byte(&data[idx], iChild); +/* +** The sqlcipher_sqlite3ValueBytes() routine returns the number of bytes in the +** sqlcipher_sqlite3_value object assuming that it uses the encoding "enc". +** The valueBytes() routine is a helper function. +*/ +static SQLITE_NOINLINE int valueBytes(sqlcipher_sqlite3_value *pVal, u8 enc){ + return valueToText(pVal, enc)!=0 ? pVal->n : 0; +} +SQLITE_PRIVATE int sqlcipher_sqlite3ValueBytes(sqlcipher_sqlite3_value *pVal, u8 enc){ + Mem *p = (Mem*)pVal; + assert( (p->flags & MEM_Null)==0 || (p->flags & (MEM_Str|MEM_Blob))==0 ); + if( (p->flags & MEM_Str)!=0 && pVal->enc==enc ){ + return p->n; + } + if( (p->flags & MEM_Blob)!=0 ){ + if( p->flags & MEM_Zero ){ + return p->n + p->u.nZero; }else{ - memcpy(&data[idx], pCell, sz); - } - pIns = pPage->aCellIdx + i*2; - memmove(pIns+2, pIns, 2*(pPage->nCell - i)); - put2byte(pIns, idx); - pPage->nCell++; - /* increment the cell count */ - if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++; - assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB ); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pPage->pBt->autoVacuum ){ - /* The cell may contain a pointer to an overflow page. If so, write - ** the entry for the overflow page into the pointer map. - */ - ptrmapPutOvflPtr(pPage, pPage, pCell, pRC); + return p->n; } -#endif } + if( p->flags & MEM_Null ) return 0; + return valueBytes(pVal, enc); } +/************** End of vdbemem.c *********************************************/ +/************** Begin file vdbeaux.c *****************************************/ /* -** The following parameters determine how many adjacent pages get involved -** in a balancing operation. NN is the number of neighbors on either side -** of the page that participate in the balancing operation. NB is the -** total number of pages that participate, including the target page and -** NN neighbors on either side. -** -** The minimum value of NN is 1 (of course). Increasing NN above 1 -** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance -** in exchange for a larger degradation in INSERT and UPDATE performance. -** The value of NN appears to give the best results overall. -** -** (Later:) The description above makes it seem as if these values are -** tunable - as if you could change them and recompile and it would all work. -** But that is unlikely. NB has been 3 since the inception of SQLite and -** we have never tested any other value. -*/ -#define NN 1 /* Number of neighbors on either side of pPage */ -#define NB 3 /* (NN*2+1): Total pages involved in the balance */ - -/* -** A CellArray object contains a cache of pointers and sizes for a -** consecutive sequence of cells that might be held on multiple pages. -** -** The cells in this array are the divider cell or cells from the pParent -** page plus up to three child pages. There are a total of nCell cells. -** -** pRef is a pointer to one of the pages that contributes cells. This is -** used to access information such as MemPage.intKey and MemPage.pBt->pageSize -** which should be common to all pages that contribute cells to this array. -** -** apCell[] and szCell[] hold, respectively, pointers to the start of each -** cell and the size of each cell. Some of the apCell[] pointers might refer -** to overflow cells. In other words, some apCel[] pointers might not point -** to content area of the pages. -** -** A szCell[] of zero means the size of that cell has not yet been computed. -** -** The cells come from as many as four different pages: -** -** ----------- -** | Parent | -** ----------- -** / | \ -** / | \ -** --------- --------- --------- -** |Child-1| |Child-2| |Child-3| -** --------- --------- --------- -** -** The order of cells is in the array is for an index btree is: -** -** 1. All cells from Child-1 in order -** 2. The first divider cell from Parent -** 3. All cells from Child-2 in order -** 4. The second divider cell from Parent -** 5. All cells from Child-3 in order -** -** For a table-btree (with rowids) the items 2 and 4 are empty because -** content exists only in leaves and there are no divider cells. -** -** For an index btree, the apEnd[] array holds pointer to the end of page -** for Child-1, the Parent, Child-2, the Parent (again), and Child-3, -** respectively. The ixNx[] array holds the number of cells contained in -** each of these 5 stages, and all stages to the left. Hence: -** -** ixNx[0] = Number of cells in Child-1. -** ixNx[1] = Number of cells in Child-1 plus 1 for first divider. -** ixNx[2] = Number of cells in Child-1 and Child-2 + 1 for 1st divider. -** ixNx[3] = Number of cells in Child-1 and Child-2 + both divider cells -** ixNx[4] = Total number of cells. +** 2003 September 6 ** -** For a table-btree, the concept is similar, except only apEnd[0]..apEnd[2] -** are used and they point to the leaf pages only, and the ixNx value are: +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** ixNx[0] = Number of cells in Child-1. -** ixNx[1] = Number of cells in Child-1 and Child-2. -** ixNx[2] = Total number of cells. +** 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. ** -** Sometimes when deleting, a child page can have zero cells. In those -** cases, ixNx[] entries with higher indexes, and the corresponding apEnd[] -** entries, shift down. The end result is that each ixNx[] entry should -** be larger than the previous +************************************************************************* +** This file contains code used for creating, destroying, and populating +** a VDBE (or an "sqlcipher_sqlite3_stmt" as it is known to the outside world.) */ -typedef struct CellArray CellArray; -struct CellArray { - int nCell; /* Number of cells in apCell[] */ - MemPage *pRef; /* Reference page */ - u8 **apCell; /* All cells begin balanced */ - u16 *szCell; /* Local size of all cells in apCell[] */ - u8 *apEnd[NB*2]; /* MemPage.aDataEnd values */ - int ixNx[NB*2]; /* Index of at which we move to the next apEnd[] */ -}; +/* #include "sqliteInt.h" */ +/* #include "vdbeInt.h" */ + +/* Forward references */ +static void freeEphemeralFunction(sqlcipher_sqlite3 *db, FuncDef *pDef); +static void vdbeFreeOpArray(sqlcipher_sqlite3 *, Op *, int); /* -** Make sure the cell sizes at idx, idx+1, ..., idx+N-1 have been -** computed. +** Create a new virtual database engine. */ -static void populateCellCache(CellArray *p, int idx, int N){ - assert( idx>=0 && idx+N<=p->nCell ); - while( N>0 ){ - assert( p->apCell[idx]!=0 ); - if( p->szCell[idx]==0 ){ - p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]); - }else{ - assert( CORRUPT_DB || - p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) ); - } - idx++; - N--; +SQLITE_PRIVATE Vdbe *sqlcipher_sqlite3VdbeCreate(Parse *pParse){ + sqlcipher_sqlite3 *db = pParse->db; + Vdbe *p; + p = sqlcipher_sqlite3DbMallocRawNN(db, sizeof(Vdbe) ); + if( p==0 ) return 0; + memset(&p->aOp, 0, sizeof(Vdbe)-offsetof(Vdbe,aOp)); + p->db = db; + if( db->pVdbe ){ + db->pVdbe->pPrev = p; } + p->pNext = db->pVdbe; + p->pPrev = 0; + db->pVdbe = p; + assert( p->eVdbeState==VDBE_INIT_STATE ); + p->pParse = pParse; + pParse->pVdbe = p; + assert( pParse->aLabel==0 ); + assert( pParse->nLabel==0 ); + assert( p->nOpAlloc==0 ); + assert( pParse->szOpAlloc==0 ); + sqlcipher_sqlite3VdbeAddOp2(p, OP_Init, 0, 1); + return p; } /* -** Return the size of the Nth element of the cell array +** Return the Parse object that owns a Vdbe object. */ -static SQLITE_NOINLINE u16 computeCellSize(CellArray *p, int N){ - assert( N>=0 && NnCell ); - assert( p->szCell[N]==0 ); - p->szCell[N] = p->pRef->xCellSize(p->pRef, p->apCell[N]); - return p->szCell[N]; -} -static u16 cachedCellSize(CellArray *p, int N){ - assert( N>=0 && NnCell ); - if( p->szCell[N] ) return p->szCell[N]; - return computeCellSize(p, N); +SQLITE_PRIVATE Parse *sqlcipher_sqlite3VdbeParser(Vdbe *p){ + return p->pParse; } /* -** Array apCell[] contains pointers to nCell b-tree page cells. The -** szCell[] array contains the size in bytes of each cell. This function -** replaces the current contents of page pPg with the contents of the cell -** array. -** -** Some of the cells in apCell[] may currently be stored in pPg. This -** function works around problems caused by this by making a copy of any -** such cells before overwriting the page data. -** -** The MemPage.nFree field is invalidated by this function. It is the -** responsibility of the caller to set it correctly. +** Change the error string stored in Vdbe.zErrMsg */ -static int rebuildPage( - CellArray *pCArray, /* Content to be added to page pPg */ - int iFirst, /* First cell in pCArray to use */ - int nCell, /* Final number of cells on page */ - MemPage *pPg /* The page to be reconstructed */ -){ - const int hdr = pPg->hdrOffset; /* Offset of header on pPg */ - u8 * const aData = pPg->aData; /* Pointer to data for pPg */ - const int usableSize = pPg->pBt->usableSize; - u8 * const pEnd = &aData[usableSize]; - int i = iFirst; /* Which cell to copy from pCArray*/ - u32 j; /* Start of cell content area */ - int iEnd = i+nCell; /* Loop terminator */ - u8 *pCellptr = pPg->aCellIdx; - u8 *pTmp = sqlcipher_sqlite3PagerTempSpace(pPg->pBt->pPager); - u8 *pData; - int k; /* Current slot in pCArray->apEnd[] */ - u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */ - - assert( i(u32)usableSize) ){ j = 0; } - memcpy(&pTmp[j], &aData[j], usableSize - j); - - for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kapEnd[k]; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeError(Vdbe *p, const char *zFormat, ...){ + va_list ap; + sqlcipher_sqlite3DbFree(p->db, p->zErrMsg); + va_start(ap, zFormat); + p->zErrMsg = sqlcipher_sqlite3VMPrintf(p->db, zFormat, ap); + va_end(ap); +} - pData = pEnd; - while( 1/*exit by break*/ ){ - u8 *pCell = pCArray->apCell[i]; - u16 sz = pCArray->szCell[i]; - assert( sz>0 ); - if( SQLITE_WITHIN(pCell,aData,pEnd) ){ - if( ((uptr)(pCell+sz))>(uptr)pEnd ) return SQLITE_CORRUPT_BKPT; - pCell = &pTmp[pCell - aData]; - }else if( (uptr)(pCell+sz)>(uptr)pSrcEnd - && (uptr)(pCell)<(uptr)pSrcEnd - ){ - return SQLITE_CORRUPT_BKPT; - } +/* +** Remember the SQL string for a prepared statement. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, u8 prepFlags){ + if( p==0 ) return; + p->prepFlags = prepFlags; + if( (prepFlags & SQLITE_PREPARE_SAVESQL)==0 ){ + p->expmask = 0; + } + assert( p->zSql==0 ); + p->zSql = sqlcipher_sqlite3DbStrNDup(p->db, z, n); +} - pData -= sz; - put2byte(pCellptr, (pData - aData)); - pCellptr += 2; - if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT; - memcpy(pData, pCell, sz); - assert( sz==pPg->xCellSize(pPg, pCell) || CORRUPT_DB ); - testcase( sz!=pPg->xCellSize(pPg,pCell) ) - i++; - if( i>=iEnd ) break; - if( pCArray->ixNx[k]<=i ){ - k++; - pSrcEnd = pCArray->apEnd[k]; +#ifdef SQLITE_ENABLE_NORMALIZE +/* +** Add a new element to the Vdbe->pDblStr list. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeAddDblquoteStr(sqlcipher_sqlite3 *db, Vdbe *p, const char *z){ + if( p ){ + int n = sqlcipher_sqlite3Strlen30(z); + DblquoteStr *pStr = sqlcipher_sqlite3DbMallocRawNN(db, + sizeof(*pStr)+n+1-sizeof(pStr->z)); + if( pStr ){ + pStr->pNextStr = p->pDblStr; + p->pDblStr = pStr; + memcpy(pStr->z, z, n+1); } } - - /* The pPg->nFree field is now set incorrectly. The caller will fix it. */ - pPg->nCell = nCell; - pPg->nOverflow = 0; - - put2byte(&aData[hdr+1], 0); - put2byte(&aData[hdr+3], pPg->nCell); - put2byte(&aData[hdr+5], pData - aData); - aData[hdr+7] = 0x00; - return SQLITE_OK; } +#endif +#ifdef SQLITE_ENABLE_NORMALIZE /* -** The pCArray objects contains pointers to b-tree cells and the cell sizes. -** This function attempts to add the cells stored in the array to page pPg. -** If it cannot (because the page needs to be defragmented before the cells -** will fit), non-zero is returned. Otherwise, if the cells are added -** successfully, zero is returned. -** -** Argument pCellptr points to the first entry in the cell-pointer array -** (part of page pPg) to populate. After cell apCell[0] is written to the -** page body, a 16-bit offset is written to pCellptr. And so on, for each -** cell in the array. It is the responsibility of the caller to ensure -** that it is safe to overwrite this part of the cell-pointer array. -** -** When this function is called, *ppData points to the start of the -** content area on page pPg. If the size of the content area is extended, -** *ppData is updated to point to the new start of the content area -** before returning. -** -** Finally, argument pBegin points to the byte immediately following the -** end of the space required by this page for the cell-pointer area (for -** all cells - not just those inserted by the current call). If the content -** area must be extended to before this point in order to accomodate all -** cells in apCell[], then the cells do not fit and non-zero is returned. +** zId of length nId is a double-quoted identifier. Check to see if +** that identifier is really used as a string literal. */ -static int pageInsertArray( - MemPage *pPg, /* Page to add cells to */ - u8 *pBegin, /* End of cell-pointer array */ - u8 **ppData, /* IN/OUT: Page content-area pointer */ - u8 *pCellptr, /* Pointer to cell-pointer area */ - int iFirst, /* Index of first cell to add */ - int nCell, /* Number of cells to add to pPg */ - CellArray *pCArray /* Array of cells */ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeUsesDoubleQuotedString( + Vdbe *pVdbe, /* The prepared statement */ + const char *zId /* The double-quoted identifier, already dequoted */ ){ - int i = iFirst; /* Loop counter - cell index to insert */ - u8 *aData = pPg->aData; /* Complete page */ - u8 *pData = *ppData; /* Content area. A subset of aData[] */ - int iEnd = iFirst + nCell; /* End of loop. One past last cell to ins */ - int k; /* Current slot in pCArray->apEnd[] */ - u8 *pEnd; /* Maximum extent of cell data */ - assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */ - if( iEnd<=iFirst ) return 0; - for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kapEnd[k]; - while( 1 /*Exit by break*/ ){ - int sz, rc; - u8 *pSlot; - assert( pCArray->szCell[i]!=0 ); - sz = pCArray->szCell[i]; - if( (aData[1]==0 && aData[2]==0) || (pSlot = pageFindSlot(pPg,sz,&rc))==0 ){ - if( (pData - pBegin)apCell[i] will never overlap on a well-formed - ** database. But they might for a corrupt database. Hence use memmove() - ** since memcpy() sends SIGABORT with overlapping buffers on OpenBSD */ - assert( (pSlot+sz)<=pCArray->apCell[i] - || pSlot>=(pCArray->apCell[i]+sz) - || CORRUPT_DB ); - if( (uptr)(pCArray->apCell[i]+sz)>(uptr)pEnd - && (uptr)(pCArray->apCell[i])<(uptr)pEnd - ){ - assert( CORRUPT_DB ); - (void)SQLITE_CORRUPT_BKPT; - return 1; - } - memmove(pSlot, pCArray->apCell[i], sz); - put2byte(pCellptr, (pSlot - aData)); - pCellptr += 2; - i++; - if( i>=iEnd ) break; - if( pCArray->ixNx[k]<=i ){ - k++; - pEnd = pCArray->apEnd[k]; - } + DblquoteStr *pStr; + assert( zId!=0 ); + if( pVdbe->pDblStr==0 ) return 0; + for(pStr=pVdbe->pDblStr; pStr; pStr=pStr->pNextStr){ + if( strcmp(zId, pStr->z)==0 ) return 1; } - *ppData = pData; return 0; } +#endif /* -** The pCArray object contains pointers to b-tree cells and their sizes. -** -** This function adds the space associated with each cell in the array -** that is currently stored within the body of pPg to the pPg free-list. -** The cell-pointers and other fields of the page are not updated. +** Swap all content between two VDBE structures. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ + Vdbe tmp, *pTmp; + char *zTmp; + assert( pA->db==pB->db ); + tmp = *pA; + *pA = *pB; + *pB = tmp; + pTmp = pA->pNext; + pA->pNext = pB->pNext; + pB->pNext = pTmp; + pTmp = pA->pPrev; + pA->pPrev = pB->pPrev; + pB->pPrev = pTmp; + zTmp = pA->zSql; + pA->zSql = pB->zSql; + pB->zSql = zTmp; +#ifdef SQLITE_ENABLE_NORMALIZE + zTmp = pA->zNormSql; + pA->zNormSql = pB->zNormSql; + pB->zNormSql = zTmp; +#endif + pB->expmask = pA->expmask; + pB->prepFlags = pA->prepFlags; + memcpy(pB->aCounter, pA->aCounter, sizeof(pB->aCounter)); + pB->aCounter[SQLITE_STMTSTATUS_REPREPARE]++; +} + +/* +** Resize the Vdbe.aOp array so that it is at least nOp elements larger +** than its current size. nOp is guaranteed to be less than or equal +** to 1024/sizeof(Op). ** -** This function returns the total number of cells added to the free-list. +** If an out-of-memory error occurs while resizing the array, return +** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain +** unchanged (this is so that any opcodes already allocated can be +** correctly deallocated along with the rest of the Vdbe). */ -static int pageFreeArray( - MemPage *pPg, /* Page to edit */ - int iFirst, /* First cell to delete */ - int nCell, /* Cells to delete */ - CellArray *pCArray /* Array of cells */ -){ - u8 * const aData = pPg->aData; - u8 * const pEnd = &aData[pPg->pBt->usableSize]; - u8 * const pStart = &aData[pPg->hdrOffset + 8 + pPg->childPtrSize]; - int nRet = 0; - int i; - int iEnd = iFirst + nCell; - u8 *pFree = 0; - int szFree = 0; +static int growOpArray(Vdbe *v, int nOp){ + VdbeOp *pNew; + Parse *p = v->pParse; - for(i=iFirst; iapCell[i]; - if( SQLITE_WITHIN(pCell, pStart, pEnd) ){ - int sz; - /* No need to use cachedCellSize() here. The sizes of all cells that - ** are to be freed have already been computing while deciding which - ** cells need freeing */ - sz = pCArray->szCell[i]; assert( sz>0 ); - if( pFree!=(pCell + sz) ){ - if( pFree ){ - assert( pFree>aData && (pFree - aData)<65536 ); - freeSpace(pPg, (u16)(pFree - aData), szFree); - } - pFree = pCell; - szFree = sz; - if( pFree+sz>pEnd ) return 0; - }else{ - pFree = pCell; - szFree += sz; - } - nRet++; - } + /* The SQLITE_TEST_REALLOC_STRESS compile-time option is designed to force + ** more frequent reallocs and hence provide more opportunities for + ** simulated OOM faults. SQLITE_TEST_REALLOC_STRESS is generally used + ** during testing only. With SQLITE_TEST_REALLOC_STRESS grow the op array + ** by the minimum* amount required until the size reaches 512. Normal + ** operation (without SQLITE_TEST_REALLOC_STRESS) is to double the current + ** size of the op array or add 1KB of space, whichever is smaller. */ +#ifdef SQLITE_TEST_REALLOC_STRESS + sqlcipher_sqlite3_int64 nNew = (v->nOpAlloc>=512 ? 2*(sqlcipher_sqlite3_int64)v->nOpAlloc + : (sqlcipher_sqlite3_int64)v->nOpAlloc+nOp); +#else + sqlcipher_sqlite3_int64 nNew = (v->nOpAlloc ? 2*(sqlcipher_sqlite3_int64)v->nOpAlloc + : (sqlcipher_sqlite3_int64)(1024/sizeof(Op))); + UNUSED_PARAMETER(nOp); +#endif + + /* Ensure that the size of a VDBE does not grow too large */ + if( nNew > p->db->aLimit[SQLITE_LIMIT_VDBE_OP] ){ + sqlcipher_sqlite3OomFault(p->db); + return SQLITE_NOMEM; } - if( pFree ){ - assert( pFree>aData && (pFree - aData)<65536 ); - freeSpace(pPg, (u16)(pFree - aData), szFree); + + assert( nOp<=(int)(1024/sizeof(Op)) ); + assert( nNew>=(v->nOpAlloc+nOp) ); + pNew = sqlcipher_sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op)); + if( pNew ){ + p->szOpAlloc = sqlcipher_sqlite3DbMallocSize(p->db, pNew); + v->nOpAlloc = p->szOpAlloc/sizeof(Op); + v->aOp = pNew; } - return nRet; + return (pNew ? SQLITE_OK : SQLITE_NOMEM_BKPT); +} + +#ifdef SQLITE_DEBUG +/* This routine is just a convenient place to set a breakpoint that will +** fire after each opcode is inserted and displayed using +** "PRAGMA vdbe_addoptrace=on". Parameters "pc" (program counter) and +** pOp are available to make the breakpoint conditional. +** +** Other useful labels for breakpoints include: +** test_trace_breakpoint(pc,pOp) +** sqlcipher_sqlite3CorruptError(lineno) +** sqlcipher_sqlite3MisuseError(lineno) +** sqlcipher_sqlite3CantopenError(lineno) +*/ +static void test_addop_breakpoint(int pc, Op *pOp){ + static int n = 0; + n++; } +#endif /* -** pCArray contains pointers to and sizes of all cells in the page being -** balanced. The current page, pPg, has pPg->nCell cells starting with -** pCArray->apCell[iOld]. After balancing, this page should hold nNew cells -** starting at apCell[iNew]. +** Add a new instruction to the list of instructions current in the +** VDBE. Return the address of the new instruction. ** -** This routine makes the necessary adjustments to pPg so that it contains -** the correct cells after being balanced. +** Parameters: ** -** The pPg->nFree field is invalid when this function returns. It is the -** responsibility of the caller to set it correctly. +** p Pointer to the VDBE +** +** op The opcode for this instruction +** +** p1, p2, p3 Operands +** +** Use the sqlcipher_sqlite3VdbeResolveLabel() function to fix an address and +** the sqlcipher_sqlite3VdbeChangeP4() function to change the value of the P4 +** operand. */ -static int editPage( - MemPage *pPg, /* Edit this page */ - int iOld, /* Index of first cell currently on page */ - int iNew, /* Index of new first cell on page */ - int nNew, /* Final number of cells on page */ - CellArray *pCArray /* Array of cells and sizes */ -){ - u8 * const aData = pPg->aData; - const int hdr = pPg->hdrOffset; - u8 *pBegin = &pPg->aCellIdx[nNew * 2]; - int nCell = pPg->nCell; /* Cells stored on pPg */ - u8 *pData; - u8 *pCellptr; +static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){ + assert( p->nOpAlloc<=p->nOp ); + if( growOpArray(p, 1) ) return 1; + assert( p->nOpAlloc>p->nOp ); + return sqlcipher_sqlite3VdbeAddOp3(p, op, p1, p2, p3); +} +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ int i; - int iOldEnd = iOld + pPg->nCell + pPg->nOverflow; - int iNewEnd = iNew + nNew; + VdbeOp *pOp; + i = p->nOp; + assert( p->eVdbeState==VDBE_INIT_STATE ); + assert( op>=0 && op<0xff ); + if( p->nOpAlloc<=i ){ + return growOp3(p, op, p1, p2, p3); + } + assert( p->aOp!=0 ); + p->nOp++; + pOp = &p->aOp[i]; + assert( pOp!=0 ); + pOp->opcode = (u8)op; + pOp->p5 = 0; + pOp->p1 = p1; + pOp->p2 = p2; + pOp->p3 = p3; + pOp->p4.p = 0; + pOp->p4type = P4_NOTUSED; +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + pOp->zComment = 0; +#endif #ifdef SQLITE_DEBUG - u8 *pTmp = sqlcipher_sqlite3PagerTempSpace(pPg->pBt->pPager); - memcpy(pTmp, aData, pPg->pBt->usableSize); + if( p->db->flags & SQLITE_VdbeAddopTrace ){ + sqlcipher_sqlite3VdbePrintOp(0, i, &p->aOp[i]); + test_addop_breakpoint(i, &p->aOp[i]); + } #endif +#ifdef VDBE_PROFILE + pOp->cycles = 0; + pOp->cnt = 0; +#endif +#ifdef SQLITE_VDBE_COVERAGE + pOp->iSrcLine = 0; +#endif + return i; +} +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp0(Vdbe *p, int op){ + return sqlcipher_sqlite3VdbeAddOp3(p, op, 0, 0, 0); +} +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){ + return sqlcipher_sqlite3VdbeAddOp3(p, op, p1, 0, 0); +} +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){ + return sqlcipher_sqlite3VdbeAddOp3(p, op, p1, p2, 0); +} - /* Remove cells from the start and end of the page */ - assert( nCell>=0 ); - if( iOldnCell) ) return SQLITE_CORRUPT_BKPT; - memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2); - nCell -= nShift; - } - if( iNewEnd < iOldEnd ){ - int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); - assert( nCell>=nTail ); - nCell -= nTail; - } +/* Generate code for an unconditional jump to instruction iDest +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeGoto(Vdbe *p, int iDest){ + return sqlcipher_sqlite3VdbeAddOp3(p, OP_Goto, 0, iDest, 0); +} - pData = &aData[get2byteNotZero(&aData[hdr+5])]; - if( pData=0 ); - pCellptr = pPg->aCellIdx; - memmove(&pCellptr[nAdd*2], pCellptr, nCell*2); - if( pageInsertArray( - pPg, pBegin, &pData, pCellptr, - iNew, nAdd, pCArray - ) ) goto editpage_fail; - nCell += nAdd; +/* +** Generate code that initializes multiple registers to string or integer +** constants. The registers begin with iDest and increase consecutively. +** One register is initialized for each characgter in zTypes[]. For each +** "s" character in zTypes[], the register is a string if the argument is +** not NULL, or OP_Null if the value is a null pointer. For each "i" character +** in zTypes[], the register is initialized to an integer. +** +** If the input string does not end with "X" then an OP_ResultRow instruction +** is generated for the values inserted. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMultiLoad(Vdbe *p, int iDest, const char *zTypes, ...){ + va_list ap; + int i; + char c; + va_start(ap, zTypes); + for(i=0; (c = zTypes[i])!=0; i++){ + if( c=='s' ){ + const char *z = va_arg(ap, const char*); + sqlcipher_sqlite3VdbeAddOp4(p, z==0 ? OP_Null : OP_String8, 0, iDest+i, 0, z, 0); + }else if( c=='i' ){ + sqlcipher_sqlite3VdbeAddOp2(p, OP_Integer, va_arg(ap, int), iDest+i); + }else{ + goto skip_op_resultrow; + } } + sqlcipher_sqlite3VdbeAddOp2(p, OP_ResultRow, iDest, i); +skip_op_resultrow: + va_end(ap); +} - /* Add any overflow cells */ - for(i=0; inOverflow; i++){ - int iCell = (iOld + pPg->aiOvfl[i]) - iNew; - if( iCell>=0 && iCellaCellIdx[iCell * 2]; - if( nCell>iCell ){ - memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2); - } - nCell++; - cachedCellSize(pCArray, iCell+iNew); - if( pageInsertArray( - pPg, pBegin, &pData, pCellptr, - iCell+iNew, 1, pCArray - ) ) goto editpage_fail; - } +/* +** Add an opcode that includes the p4 value as a pointer. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp4( + Vdbe *p, /* Add the opcode to this VM */ + int op, /* The new opcode */ + int p1, /* The P1 operand */ + int p2, /* The P2 operand */ + int p3, /* The P3 operand */ + const char *zP4, /* The P4 operand */ + int p4type /* P4 operand type */ +){ + int addr = sqlcipher_sqlite3VdbeAddOp3(p, op, p1, p2, p3); + sqlcipher_sqlite3VdbeChangeP4(p, addr, zP4, p4type); + return addr; +} + +/* +** Add an OP_Function or OP_PureFunc opcode. +** +** The eCallCtx argument is information (typically taken from Expr.op2) +** that describes the calling context of the function. 0 means a general +** function call. NC_IsCheck means called by a check constraint, +** NC_IdxExpr means called as part of an index expression. NC_PartIdx +** means in the WHERE clause of a partial index. NC_GenCol means called +** while computing a generated column value. 0 is the usual case. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddFunctionCall( + Parse *pParse, /* Parsing context */ + int p1, /* Constant argument mask */ + int p2, /* First argument register */ + int p3, /* Register into which results are written */ + int nArg, /* Number of argument */ + const FuncDef *pFunc, /* The function to be invoked */ + int eCallCtx /* Calling context */ +){ + Vdbe *v = pParse->pVdbe; + int nByte; + int addr; + sqlcipher_sqlite3_context *pCtx; + assert( v ); + nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlcipher_sqlite3_value*); + pCtx = sqlcipher_sqlite3DbMallocRawNN(pParse->db, nByte); + if( pCtx==0 ){ + assert( pParse->db->mallocFailed ); + freeEphemeralFunction(pParse->db, (FuncDef*)pFunc); + return 0; } + pCtx->pOut = 0; + pCtx->pFunc = (FuncDef*)pFunc; + pCtx->pVdbe = 0; + pCtx->isError = 0; + pCtx->argc = nArg; + pCtx->iOp = sqlcipher_sqlite3VdbeCurrentAddr(v); + addr = sqlcipher_sqlite3VdbeAddOp4(v, eCallCtx ? OP_PureFunc : OP_Function, + p1, p2, p3, (char*)pCtx, P4_FUNCCTX); + sqlcipher_sqlite3VdbeChangeP5(v, eCallCtx & NC_SelfRef); + sqlcipher_sqlite3MayAbort(pParse); + return addr; +} - /* Append cells to the end of the page */ - assert( nCell>=0 ); - pCellptr = &pPg->aCellIdx[nCell*2]; - if( pageInsertArray( - pPg, pBegin, &pData, pCellptr, - iNew+nCell, nNew-nCell, pCArray - ) ) goto editpage_fail; +/* +** Add an opcode that includes the p4 value with a P4_INT64 or +** P4_REAL type. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp4Dup8( + Vdbe *p, /* Add the opcode to this VM */ + int op, /* The new opcode */ + int p1, /* The P1 operand */ + int p2, /* The P2 operand */ + int p3, /* The P3 operand */ + const u8 *zP4, /* The P4 operand */ + int p4type /* P4 operand type */ +){ + char *p4copy = sqlcipher_sqlite3DbMallocRawNN(sqlcipher_sqlite3VdbeDb(p), 8); + if( p4copy ) memcpy(p4copy, zP4, 8); + return sqlcipher_sqlite3VdbeAddOp4(p, op, p1, p2, p3, p4copy, p4type); +} - pPg->nCell = nNew; - pPg->nOverflow = 0; +#ifndef SQLITE_OMIT_EXPLAIN +/* +** Return the address of the current EXPLAIN QUERY PLAN baseline. +** 0 means "none". +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeExplainParent(Parse *pParse){ + VdbeOp *pOp; + if( pParse->addrExplain==0 ) return 0; + pOp = sqlcipher_sqlite3VdbeGetOp(pParse->pVdbe, pParse->addrExplain); + return pOp->p2; +} - put2byte(&aData[hdr+3], pPg->nCell); - put2byte(&aData[hdr+5], pData - aData); +/* +** Set a debugger breakpoint on the following routine in order to +** monitor the EXPLAIN QUERY PLAN code generation. +*/ +#if defined(SQLITE_DEBUG) +SQLITE_PRIVATE void sqlcipher_sqlite3ExplainBreakpoint(const char *z1, const char *z2){ + (void)z1; + (void)z2; +} +#endif -#ifdef SQLITE_DEBUG - for(i=0; iapCell[i+iNew]; - int iOff = get2byteAligned(&pPg->aCellIdx[i*2]); - if( SQLITE_WITHIN(pCell, aData, &aData[pPg->pBt->usableSize]) ){ - pCell = &pTmp[pCell - aData]; +/* +** Add a new OP_Explain opcode. +** +** If the bPush flag is true, then make this opcode the parent for +** subsequent Explains until sqlcipher_sqlite3VdbeExplainPop() is called. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ +#ifndef SQLITE_DEBUG + /* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined. + ** But omit them (for performance) during production builds */ + if( pParse->explain==2 ) +#endif + { + char *zMsg; + Vdbe *v; + va_list ap; + int iThis; + va_start(ap, zFmt); + zMsg = sqlcipher_sqlite3VMPrintf(pParse->db, zFmt, ap); + va_end(ap); + v = pParse->pVdbe; + iThis = v->nOp; + sqlcipher_sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, + zMsg, P4_DYNAMIC); + sqlcipher_sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlcipher_sqlite3VdbeGetOp(v,-1)->p4.z); + if( bPush){ + pParse->addrExplain = iThis; } - assert( 0==memcmp(pCell, &aData[iOff], - pCArray->pRef->xCellSize(pCArray->pRef, pCArray->apCell[i+iNew])) ); } -#endif +} - return SQLITE_OK; - editpage_fail: - /* Unable to edit this page. Rebuild it from scratch instead. */ - populateCellCache(pCArray, iNew, nNew); - return rebuildPage(pCArray, iNew, nNew, pPg); +/* +** Pop the EXPLAIN QUERY PLAN stack one level. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeExplainPop(Parse *pParse){ + sqlcipher_sqlite3ExplainBreakpoint("POP", 0); + pParse->addrExplain = sqlcipher_sqlite3VdbeExplainParent(pParse); +} +#endif /* SQLITE_OMIT_EXPLAIN */ + +/* +** Add an OP_ParseSchema opcode. This routine is broken out from +** sqlcipher_sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees +** as having been used. +** +** The zWhere string must have been obtained from sqlcipher_sqlite3_malloc(). +** This routine will take ownership of the allocated memory. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere, u16 p5){ + int j; + sqlcipher_sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC); + sqlcipher_sqlite3VdbeChangeP5(p, p5); + for(j=0; jdb->nDb; j++) sqlcipher_sqlite3VdbeUsesBtree(p, j); + sqlcipher_sqlite3MayAbort(p->pParse); +} + +/* +** Add an opcode that includes the p4 value as an integer. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp4Int( + Vdbe *p, /* Add the opcode to this VM */ + int op, /* The new opcode */ + int p1, /* The P1 operand */ + int p2, /* The P2 operand */ + int p3, /* The P3 operand */ + int p4 /* The P4 operand as an integer */ +){ + int addr = sqlcipher_sqlite3VdbeAddOp3(p, op, p1, p2, p3); + if( p->db->mallocFailed==0 ){ + VdbeOp *pOp = &p->aOp[addr]; + pOp->p4type = P4_INT32; + pOp->p4.i = p4; + } + return addr; } +/* Insert the end of a co-routine +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeEndCoroutine(Vdbe *v, int regYield){ + sqlcipher_sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield); + + /* Clear the temporary register cache, thereby ensuring that each + ** co-routine has its own independent set of registers, because co-routines + ** might expect their registers to be preserved across an OP_Yield, and + ** that could cause problems if two or more co-routines are using the same + ** temporary register. + */ + v->pParse->nTempReg = 0; + v->pParse->nRangeReg = 0; +} -#ifndef SQLITE_OMIT_QUICKBALANCE /* -** This version of balance() handles the common special case where -** a new entry is being inserted on the extreme right-end of the -** tree, in other words, when the new entry will become the largest -** entry in the tree. +** Create a new symbolic label for an instruction that has yet to be +** coded. The symbolic label is really just a negative number. The +** label can be used as the P2 value of an operation. Later, when +** the label is resolved to a specific address, the VDBE will scan +** through its operation list and change all values of P2 which match +** the label into the resolved address. ** -** Instead of trying to balance the 3 right-most leaf pages, just add -** a new page to the right-hand side and put the one new entry in -** that page. This leaves the right side of the tree somewhat -** unbalanced. But odds are that we will be inserting new entries -** at the end soon afterwards so the nearly empty page will quickly -** fill up. On average. +** The VDBE knows that a P2 value is a label because labels are +** always negative and P2 values are suppose to be non-negative. +** Hence, a negative P2 value is a label that has yet to be resolved. +** (Later:) This is only true for opcodes that have the OPFLG_JUMP +** property. ** -** pPage is the leaf page which is the right-most page in the tree. -** pParent is its parent. pPage must have a single overflow entry -** which is also the right-most entry on the page. +** Variable usage notes: ** -** The pSpace buffer is used to store a temporary copy of the divider -** cell that will be inserted into pParent. Such a cell consists of a 4 -** byte page number followed by a variable length integer. In other -** words, at most 13 bytes. Hence the pSpace buffer must be at -** least 13 bytes in size. +** Parse.aLabel[x] Stores the address that the x-th label resolves +** into. For testing (SQLITE_DEBUG), unresolved +** labels stores -1, but that is not required. +** Parse.nLabelAlloc Number of slots allocated to Parse.aLabel[] +** Parse.nLabel The *negative* of the number of labels that have +** been issued. The negative is stored because +** that gives a performance improvement over storing +** the equivalent positive value. */ -static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ - BtShared *const pBt = pPage->pBt; /* B-Tree Database */ - MemPage *pNew; /* Newly allocated page */ - int rc; /* Return Code */ - Pgno pgnoNew; /* Page number of pNew */ - - assert( sqlcipher_sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( sqlcipher_sqlite3PagerIswriteable(pParent->pDbPage) ); - assert( pPage->nOverflow==1 ); - - if( pPage->nCell==0 ) return SQLITE_CORRUPT_BKPT; /* dbfuzz001.test */ - assert( pPage->nFree>=0 ); - assert( pParent->nFree>=0 ); - - /* Allocate a new page. This page will become the right-sibling of - ** pPage. Make the parent page writable, so that the new divider cell - ** may be inserted. If both these operations are successful, proceed. - */ - rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); - - if( rc==SQLITE_OK ){ - - u8 *pOut = &pSpace[4]; - u8 *pCell = pPage->apOvfl[0]; - u16 szCell = pPage->xCellSize(pPage, pCell); - u8 *pStop; - CellArray b; - - assert( sqlcipher_sqlite3PagerIswriteable(pNew->pDbPage) ); - assert( CORRUPT_DB || pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) ); - zeroPage(pNew, PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF); - b.nCell = 1; - b.pRef = pPage; - b.apCell = &pCell; - b.szCell = &szCell; - b.apEnd[0] = pPage->aDataEnd; - b.ixNx[0] = 2; - rc = rebuildPage(&b, 0, 1, pNew); - if( NEVER(rc) ){ - releasePage(pNew); - return rc; - } - pNew->nFree = pBt->usableSize - pNew->cellOffset - 2 - szCell; - - /* If this is an auto-vacuum database, update the pointer map - ** with entries for the new page, and any pointer from the - ** cell on the page to an overflow page. If either of these - ** operations fails, the return code is set, but the contents - ** of the parent page are still manipulated by thh code below. - ** That is Ok, at this point the parent page is guaranteed to - ** be marked as dirty. Returning an error code will cause a - ** rollback, undoing any changes made to the parent page. - */ - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc); - if( szCell>pNew->minLocal ){ - ptrmapPutOvflPtr(pNew, pNew, pCell, &rc); - } - } - - /* Create a divider cell to insert into pParent. The divider cell - ** consists of a 4-byte page number (the page number of pPage) and - ** a variable length key value (which must be the same value as the - ** largest key on pPage). - ** - ** To find the largest key value on pPage, first find the right-most - ** cell on pPage. The first two fields of this cell are the - ** record-length (a variable length integer at most 32-bits in size) - ** and the key value (a variable length integer, may have any value). - ** The first of the while(...) loops below skips over the record-length - ** field. The second while(...) loop copies the key value from the - ** cell on pPage into the pSpace buffer. - */ - pCell = findCell(pPage, pPage->nCell-1); - pStop = &pCell[9]; - while( (*(pCell++)&0x80) && pCellnCell, pSpace, (int)(pOut-pSpace), - 0, pPage->pgno, &rc); - } - - /* Set the right-child pointer of pParent to point to the new page. */ - put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMakeLabel(Parse *pParse){ + return --pParse->nLabel; +} - /* Release the reference to the new page. */ - releasePage(pNew); +/* +** Resolve label "x" to be the address of the next instruction to +** be inserted. The parameter "x" must have been obtained from +** a prior call to sqlcipher_sqlite3VdbeMakeLabel(). +*/ +static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){ + int nNewSize = 10 - p->nLabel; + p->aLabel = sqlcipher_sqlite3DbReallocOrFree(p->db, p->aLabel, + nNewSize*sizeof(p->aLabel[0])); + if( p->aLabel==0 ){ + p->nLabelAlloc = 0; + }else{ +#ifdef SQLITE_DEBUG + int i; + for(i=p->nLabelAlloc; iaLabel[i] = -1; +#endif + p->nLabelAlloc = nNewSize; + p->aLabel[j] = v->nOp; + } +} +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeResolveLabel(Vdbe *v, int x){ + Parse *p = v->pParse; + int j = ADDR(x); + assert( v->eVdbeState==VDBE_INIT_STATE ); + assert( j<-p->nLabel ); + assert( j>=0 ); +#ifdef SQLITE_DEBUG + if( p->db->flags & SQLITE_VdbeAddopTrace ){ + printf("RESOLVE LABEL %d to %d\n", x, v->nOp); + } +#endif + if( p->nLabelAlloc + p->nLabel < 0 ){ + resizeResolveLabel(p,v,j); + }else{ + assert( p->aLabel[j]==(-1) ); /* Labels may only be resolved once */ + p->aLabel[j] = v->nOp; } - - return rc; } -#endif /* SQLITE_OMIT_QUICKBALANCE */ -#if 0 /* -** This function does not contribute anything to the operation of SQLite. -** it is sometimes activated temporarily while debugging code responsible -** for setting pointer-map entries. +** Mark the VDBE as one that can only be run one time. */ -static int ptrmapCheckPages(MemPage **apPage, int nPage){ - int i, j; - for(i=0; ipBt; - assert( pPage->isInit ); - - for(j=0; jnCell; j++){ - CellInfo info; - u8 *z; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeRunOnlyOnce(Vdbe *p){ + sqlcipher_sqlite3VdbeAddOp2(p, OP_Expire, 1, 1); +} - z = findCell(pPage, j); - pPage->xParseCell(pPage, z, &info); - if( info.nLocalpgno && e==PTRMAP_OVERFLOW1 ); - } - if( !pPage->leaf ){ - Pgno child = get4byte(z); - ptrmapGet(pBt, child, &e, &n); - assert( n==pPage->pgno && e==PTRMAP_BTREE ); - } - } - if( !pPage->leaf ){ - Pgno child = get4byte(&pPage->aData[pPage->hdrOffset+8]); - ptrmapGet(pBt, child, &e, &n); - assert( n==pPage->pgno && e==PTRMAP_BTREE ); +/* +** Mark the VDBE as one that can be run multiple times. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeReusable(Vdbe *p){ + int i; + for(i=1; ALWAYS(inOp); i++){ + if( ALWAYS(p->aOp[i].opcode==OP_Expire) ){ + p->aOp[1].opcode = OP_Noop; + break; } } - return 1; } -#endif + +#ifdef SQLITE_DEBUG /* sqlcipher_sqlite3AssertMayAbort() logic */ /* -** This function is used to copy the contents of the b-tree node stored -** on page pFrom to page pTo. If page pFrom was not a leaf page, then -** the pointer-map entries for each child page are updated so that the -** parent page stored in the pointer map is page pTo. If pFrom contained -** any cells with overflow page pointers, then the corresponding pointer -** map entries are also updated so that the parent page is page pTo. +** The following type and function are used to iterate through all opcodes +** in a Vdbe main program and each of the sub-programs (triggers) it may +** invoke directly or indirectly. It should be used as follows: ** -** If pFrom is currently carrying any overflow cells (entries in the -** MemPage.apOvfl[] array), they are not copied to pTo. +** Op *pOp; +** VdbeOpIter sIter; ** -** Before returning, page pTo is reinitialized using btreeInitPage(). +** memset(&sIter, 0, sizeof(sIter)); +** sIter.v = v; // v is of type Vdbe* +** while( (pOp = opIterNext(&sIter)) ){ +** // Do something with pOp +** } +** sqlcipher_sqlite3DbFree(v->db, sIter.apSub); ** -** The performance of this function is not critical. It is only used by -** the balance_shallower() and balance_deeper() procedures, neither of -** which are called often under normal circumstances. */ -static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ - if( (*pRC)==SQLITE_OK ){ - BtShared * const pBt = pFrom->pBt; - u8 * const aFrom = pFrom->aData; - u8 * const aTo = pTo->aData; - int const iFromHdr = pFrom->hdrOffset; - int const iToHdr = ((pTo->pgno==1) ? 100 : 0); - int rc; - int iData; - +typedef struct VdbeOpIter VdbeOpIter; +struct VdbeOpIter { + Vdbe *v; /* Vdbe to iterate through the opcodes of */ + SubProgram **apSub; /* Array of subprograms */ + int nSub; /* Number of entries in apSub */ + int iAddr; /* Address of next instruction to return */ + int iSub; /* 0 = main program, 1 = first sub-program etc. */ +}; +static Op *opIterNext(VdbeOpIter *p){ + Vdbe *v = p->v; + Op *pRet = 0; + Op *aOp; + int nOp; - assert( pFrom->isInit ); - assert( pFrom->nFree>=iToHdr ); - assert( get2byte(&aFrom[iFromHdr+5]) <= (int)pBt->usableSize ); + if( p->iSub<=p->nSub ){ - /* Copy the b-tree node content from page pFrom to page pTo. */ - iData = get2byte(&aFrom[iFromHdr+5]); - memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData); - memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell); + if( p->iSub==0 ){ + aOp = v->aOp; + nOp = v->nOp; + }else{ + aOp = p->apSub[p->iSub-1]->aOp; + nOp = p->apSub[p->iSub-1]->nOp; + } + assert( p->iAddrisInit = 0; - rc = btreeInitPage(pTo); - if( rc==SQLITE_OK ) rc = btreeComputeFreeSpace(pTo); - if( rc!=SQLITE_OK ){ - *pRC = rc; - return; + pRet = &aOp[p->iAddr]; + p->iAddr++; + if( p->iAddr==nOp ){ + p->iSub++; + p->iAddr = 0; } - /* If this is an auto-vacuum database, update the pointer-map entries - ** for any b-tree or overflow pages that pTo now contains the pointers to. - */ - if( ISAUTOVACUUM ){ - *pRC = setChildPtrmaps(pTo); + if( pRet->p4type==P4_SUBPROGRAM ){ + int nByte = (p->nSub+1)*sizeof(SubProgram*); + int j; + for(j=0; jnSub; j++){ + if( p->apSub[j]==pRet->p4.pProgram ) break; + } + if( j==p->nSub ){ + p->apSub = sqlcipher_sqlite3DbReallocOrFree(v->db, p->apSub, nByte); + if( !p->apSub ){ + pRet = 0; + }else{ + p->apSub[p->nSub++] = pRet->p4.pProgram; + } + } } } + + return pRet; } /* -** This routine redistributes cells on the iParentIdx'th child of pParent -** (hereafter "the page") and up to 2 siblings so that all pages have about the -** same amount of free space. Usually a single sibling on either side of the -** page are used in the balancing, though both siblings might come from one -** side if the page is the first or last child of its parent. If the page -** has fewer than 2 siblings (something which can only happen if the page -** is a root page or a child of a root page) then all available siblings -** participate in the balancing. -** -** The number of siblings of the page might be increased or decreased by -** one or two in an effort to keep pages nearly full but not over full. -** -** Note that when this routine is called, some of the cells on the page -** might not actually be stored in MemPage.aData[]. This can happen -** if the page is overfull. This routine ensures that all cells allocated -** to the page and its siblings fit into MemPage.aData[] before returning. -** -** In the course of balancing the page and its siblings, cells may be -** inserted into or removed from the parent page (pParent). Doing so -** may cause the parent page to become overfull or underfull. If this -** happens, it is the responsibility of the caller to invoke the correct -** balancing routine to fix this problem (see the balance() routine). +** Check if the program stored in the VM associated with pParse may +** throw an ABORT exception (causing the statement, but not entire transaction +** to be rolled back). This condition is true if the main program or any +** sub-programs contains any of the following: ** -** If this routine fails for any reason, it might leave the database -** in a corrupted state. So if this routine fails, the database should -** be rolled back. +** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort. +** * OP_HaltIfNull with P1=SQLITE_CONSTRAINT and P2=OE_Abort. +** * OP_Destroy +** * OP_VUpdate +** * OP_VCreate +** * OP_VRename +** * OP_FkCounter with P2==0 (immediate foreign key constraint) +** * OP_CreateBtree/BTREE_INTKEY and OP_InitCoroutine +** (for CREATE TABLE AS SELECT ...) ** -** The third argument to this function, aOvflSpace, is a pointer to a -** buffer big enough to hold one page. If while inserting cells into the parent -** page (pParent) the parent page becomes overfull, this buffer is -** used to store the parent's overflow cells. Because this function inserts -** a maximum of four divider cells into the parent page, and the maximum -** size of a cell stored within an internal node is always less than 1/4 -** of the page-size, the aOvflSpace[] buffer is guaranteed to be large -** enough for all overflow cells. +** Then check that the value of Parse.mayAbort is true if an +** ABORT may be thrown, or false otherwise. Return true if it does +** match, or false otherwise. This function is intended to be used as +** part of an assert statement in the compiler. Similar to: ** -** If aOvflSpace is set to a null pointer, this function returns -** SQLITE_NOMEM. +** assert( sqlcipher_sqlite3VdbeAssertMayAbort(pParse->pVdbe, pParse->mayAbort) ); */ -static int balance_nonroot( - MemPage *pParent, /* Parent page of siblings being balanced */ - int iParentIdx, /* Index of "the page" in pParent */ - u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */ - int isRoot, /* True if pParent is a root-page */ - int bBulk /* True if this call is part of a bulk load */ -){ - BtShared *pBt; /* The whole database */ - int nMaxCells = 0; /* Allocated size of apCell, szCell, aFrom. */ - int nNew = 0; /* Number of pages in apNew[] */ - int nOld; /* Number of pages in apOld[] */ - int i, j, k; /* Loop counters */ - int nxDiv; /* Next divider slot in pParent->aCell[] */ - int rc = SQLITE_OK; /* The return code */ - u16 leafCorrection; /* 4 if pPage is a leaf. 0 if not */ - int leafData; /* True if pPage is a leaf of a LEAFDATA tree */ - int usableSpace; /* Bytes in pPage beyond the header */ - int pageFlags; /* Value of pPage->aData[0] */ - int iSpace1 = 0; /* First unused byte of aSpace1[] */ - int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */ - int szScratch; /* Size of scratch memory requested */ - MemPage *apOld[NB]; /* pPage and up to two siblings */ - MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */ - u8 *pRight; /* Location in parent of right-sibling pointer */ - u8 *apDiv[NB-1]; /* Divider cells in pParent */ - int cntNew[NB+2]; /* Index in b.paCell[] of cell after i-th page */ - int cntOld[NB+2]; /* Old index in b.apCell[] */ - int szNew[NB+2]; /* Combined size of cells placed on i-th page */ - u8 *aSpace1; /* Space for copies of dividers cells */ - Pgno pgno; /* Temp var to store a page number in */ - u8 abDone[NB+2]; /* True after i'th new page is populated */ - Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */ - Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */ - u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */ - CellArray b; /* Parsed information on cells being balanced */ - - memset(abDone, 0, sizeof(abDone)); - b.nCell = 0; - b.apCell = 0; - pBt = pParent->pBt; - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - assert( sqlcipher_sqlite3PagerIswriteable(pParent->pDbPage) ); - - /* At this point pParent may have at most one overflow cell. And if - ** this overflow cell is present, it must be the cell with - ** index iParentIdx. This scenario comes about when this function - ** is called (indirectly) from sqlcipher_sqlite3BtreeDelete(). - */ - assert( pParent->nOverflow==0 || pParent->nOverflow==1 ); - assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx ); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ + int hasAbort = 0; + int hasFkCounter = 0; + int hasCreateTable = 0; + int hasCreateIndex = 0; + int hasInitCoroutine = 0; + Op *pOp; + VdbeOpIter sIter; - if( !aOvflSpace ){ - return SQLITE_NOMEM_BKPT; - } - assert( pParent->nFree>=0 ); + if( v==0 ) return 0; + memset(&sIter, 0, sizeof(sIter)); + sIter.v = v; - /* Find the sibling pages to balance. Also locate the cells in pParent - ** that divide the siblings. An attempt is made to find NN siblings on - ** either side of pPage. More siblings are taken from one side, however, - ** if there are fewer than NN siblings on the other side. If pParent - ** has NB or fewer children then all children of pParent are taken. - ** - ** This loop also drops the divider cells from the parent page. This - ** way, the remainder of the function does not have to deal with any - ** overflow cells in the parent page, since if any existed they will - ** have already been removed. - */ - i = pParent->nOverflow + pParent->nCell; - if( i<2 ){ - nxDiv = 0; - }else{ - assert( bBulk==0 || bBulk==1 ); - if( iParentIdx==0 ){ - nxDiv = 0; - }else if( iParentIdx==i ){ - nxDiv = i-2+bBulk; - }else{ - nxDiv = iParentIdx-1; - } - i = 2-bBulk; - } - nOld = i+1; - if( (i+nxDiv-pParent->nOverflow)==pParent->nCell ){ - pRight = &pParent->aData[pParent->hdrOffset+8]; - }else{ - pRight = findCell(pParent, i+nxDiv-pParent->nOverflow); - } - pgno = get4byte(pRight); - while( 1 ){ - rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0); - if( rc ){ - memset(apOld, 0, (i+1)*sizeof(MemPage*)); - goto balance_cleanup; + while( (pOp = opIterNext(&sIter))!=0 ){ + int opcode = pOp->opcode; + if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename + || opcode==OP_VDestroy + || opcode==OP_VCreate + || opcode==OP_ParseSchema + || opcode==OP_Function || opcode==OP_PureFunc + || ((opcode==OP_Halt || opcode==OP_HaltIfNull) + && ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort)) + ){ + hasAbort = 1; + break; } - if( apOld[i]->nFree<0 ){ - rc = btreeComputeFreeSpace(apOld[i]); - if( rc ){ - memset(apOld, 0, (i)*sizeof(MemPage*)); - goto balance_cleanup; - } + if( opcode==OP_CreateBtree && pOp->p3==BTREE_INTKEY ) hasCreateTable = 1; + if( mayAbort ){ + /* hasCreateIndex may also be set for some DELETE statements that use + ** OP_Clear. So this routine may end up returning true in the case + ** where a "DELETE FROM tbl" has a statement-journal but does not + ** require one. This is not so bad - it is an inefficiency, not a bug. */ + if( opcode==OP_CreateBtree && pOp->p3==BTREE_BLOBKEY ) hasCreateIndex = 1; + if( opcode==OP_Clear ) hasCreateIndex = 1; } - if( (i--)==0 ) break; - - if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){ - apDiv[i] = pParent->apOvfl[0]; - pgno = get4byte(apDiv[i]); - szNew[i] = pParent->xCellSize(pParent, apDiv[i]); - pParent->nOverflow = 0; - }else{ - apDiv[i] = findCell(pParent, i+nxDiv-pParent->nOverflow); - pgno = get4byte(apDiv[i]); - szNew[i] = pParent->xCellSize(pParent, apDiv[i]); - - /* Drop the cell from the parent page. apDiv[i] still points to - ** the cell within the parent, even though it has been dropped. - ** This is safe because dropping a cell only overwrites the first - ** four bytes of it, and this function does not need the first - ** four bytes of the divider cell. So the pointer is safe to use - ** later on. - ** - ** But not if we are in secure-delete mode. In secure-delete mode, - ** the dropCell() routine will overwrite the entire cell with zeroes. - ** In this case, temporarily copy the cell into the aOvflSpace[] - ** buffer. It will be copied out again as soon as the aSpace[] buffer - ** is allocated. */ - if( pBt->btsFlags & BTS_FAST_SECURE ){ - int iOff; - - iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); - if( (iOff+szNew[i])>(int)pBt->usableSize ){ - rc = SQLITE_CORRUPT_BKPT; - memset(apOld, 0, (i+1)*sizeof(MemPage*)); - goto balance_cleanup; - }else{ - memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]); - apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; - } - } - dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc); + if( opcode==OP_InitCoroutine ) hasInitCoroutine = 1; +#ifndef SQLITE_OMIT_FOREIGN_KEY + if( opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1 ){ + hasFkCounter = 1; } +#endif } + sqlcipher_sqlite3DbFree(v->db, sIter.apSub); - /* Make nMaxCells a multiple of 4 in order to preserve 8-byte - ** alignment */ - nMaxCells = nOld*(MX_CELL(pBt) + ArraySize(pParent->apOvfl)); - nMaxCells = (nMaxCells + 3)&~3; - - /* - ** Allocate space for memory structures - */ - szScratch = - nMaxCells*sizeof(u8*) /* b.apCell */ - + nMaxCells*sizeof(u16) /* b.szCell */ - + pBt->pageSize; /* aSpace1 */ + /* Return true if hasAbort==mayAbort. Or if a malloc failure occurred. + ** If malloc failed, then the while() loop above may not have iterated + ** through all opcodes and hasAbort may be set incorrectly. Return + ** true for this case to prevent the assert() in the callers frame + ** from failing. */ + return ( v->db->mallocFailed || hasAbort==mayAbort || hasFkCounter + || (hasCreateTable && hasInitCoroutine) || hasCreateIndex + ); +} +#endif /* SQLITE_DEBUG - the sqlcipher_sqlite3AssertMayAbort() function */ - assert( szScratch<=7*(int)pBt->pageSize ); - b.apCell = sqlcipher_sqlite3StackAllocRaw(0, szScratch ); - if( b.apCell==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto balance_cleanup; +#ifdef SQLITE_DEBUG +/* +** Increment the nWrite counter in the VDBE if the cursor is not an +** ephemeral cursor, or if the cursor argument is NULL. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeIncrWriteCounter(Vdbe *p, VdbeCursor *pC){ + if( pC==0 + || (pC->eCurType!=CURTYPE_SORTER + && pC->eCurType!=CURTYPE_PSEUDO + && !pC->isEphemeral) + ){ + p->nWrite++; } - b.szCell = (u16*)&b.apCell[nMaxCells]; - aSpace1 = (u8*)&b.szCell[nMaxCells]; - assert( EIGHT_BYTE_ALIGNMENT(aSpace1) ); +} +#endif - /* - ** Load pointers to all cells on sibling pages and the divider cells - ** into the local b.apCell[] array. Make copies of the divider cells - ** into space obtained from aSpace1[]. The divider cells have already - ** been removed from pParent. - ** - ** If the siblings are on leaf pages, then the child pointers of the - ** divider cells are stripped from the cells before they are copied - ** into aSpace1[]. In this way, all cells in b.apCell[] are without - ** child pointers. If siblings are not leaves, then all cell in - ** b.apCell[] include child pointers. Either way, all cells in b.apCell[] - ** are alike. - ** - ** leafCorrection: 4 if pPage is a leaf. 0 if pPage is not a leaf. - ** leafData: 1 if pPage holds key+data and pParent holds only keys. - */ - b.pRef = apOld[0]; - leafCorrection = b.pRef->leaf*4; - leafData = b.pRef->intKeyLeaf; - for(i=0; inCell; - u8 *aData = pOld->aData; - u16 maskPage = pOld->maskPage; - u8 *piCell = aData + pOld->cellOffset; - u8 *piEnd; - VVA_ONLY( int nCellAtStart = b.nCell; ) +#ifdef SQLITE_DEBUG +/* +** Assert if an Abort at this point in time might result in a corrupt +** database. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeAssertAbortable(Vdbe *p){ + assert( p->nWrite==0 || p->usesStmtJournal ); +} +#endif - /* Verify that all sibling pages are of the same "type" (table-leaf, - ** table-interior, index-leaf, or index-interior). - */ - if( pOld->aData[0]!=apOld[0]->aData[0] ){ - rc = SQLITE_CORRUPT_BKPT; - goto balance_cleanup; - } +/* +** This routine is called after all opcodes have been inserted. It loops +** through all the opcodes and fixes up some details. +** +** (1) For each jump instruction with a negative P2 value (a label) +** resolve the P2 value to an actual address. +** +** (2) Compute the maximum number of arguments used by any SQL function +** and store that value in *pMaxFuncArgs. +** +** (3) Update the Vdbe.readOnly and Vdbe.bIsReader flags to accurately +** indicate what the prepared statement actually does. +** +** (4) (discontinued) +** +** (5) Reclaim the memory allocated for storing labels. +** +** This routine will only function correctly if the mkopcodeh.tcl generator +** script numbers the opcodes correctly. Changes to this routine must be +** coordinated with changes to mkopcodeh.tcl. +*/ +static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ + int nMaxArgs = *pMaxFuncArgs; + Op *pOp; + Parse *pParse = p->pParse; + int *aLabel = pParse->aLabel; + p->readOnly = 1; + p->bIsReader = 0; + pOp = &p->aOp[p->nOp-1]; + while(1){ - /* Load b.apCell[] with pointers to all cells in pOld. If pOld - ** contains overflow cells, include them in the b.apCell[] array - ** in the correct spot. - ** - ** Note that when there are multiple overflow cells, it is always the - ** case that they are sequential and adjacent. This invariant arises - ** because multiple overflows can only occurs when inserting divider - ** cells into a parent on a prior balance, and divider cells are always - ** adjacent and are inserted in order. There is an assert() tagged - ** with "NOTE 1" in the overflow cell insertion loop to prove this - ** invariant. - ** - ** This must be done in advance. Once the balance starts, the cell - ** offset section of the btree page will be overwritten and we will no - ** long be able to find the cells if a pointer to each cell is not saved - ** first. + /* Only JUMP opcodes and the short list of special opcodes in the switch + ** below need to be considered. The mkopcodeh.tcl generator script groups + ** all these opcodes together near the front of the opcode list. Skip + ** any opcode that does not need processing by virtual of the fact that + ** it is larger than SQLITE_MX_JUMP_OPCODE, as a performance optimization. */ - memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow)); - if( pOld->nOverflow>0 ){ - if( NEVER(limitaiOvfl[0]) ){ - rc = SQLITE_CORRUPT_BKPT; - goto balance_cleanup; - } - limit = pOld->aiOvfl[0]; - for(j=0; jnOverflow; k++){ - assert( k==0 || pOld->aiOvfl[k-1]+1==pOld->aiOvfl[k] );/* NOTE 1 */ - b.apCell[b.nCell] = pOld->apOvfl[k]; - b.nCell++; - } - } - piEnd = aData + pOld->cellOffset + 2*pOld->nCell; - while( piCellnCell+pOld->nOverflow) ); - - cntOld[i] = b.nCell; - if( imaxLocal+23 ); - assert( iSpace1 <= (int)pBt->pageSize ); - memcpy(pTemp, apDiv[i], sz); - b.apCell[b.nCell] = pTemp+leafCorrection; - assert( leafCorrection==0 || leafCorrection==4 ); - b.szCell[b.nCell] = b.szCell[b.nCell] - leafCorrection; - if( !pOld->leaf ){ - assert( leafCorrection==0 ); - assert( pOld->hdrOffset==0 ); - /* The right pointer of the child page pOld becomes the left - ** pointer of the divider cell */ - memcpy(b.apCell[b.nCell], &pOld->aData[8], 4); - }else{ - assert( leafCorrection==4 ); - while( b.szCell[b.nCell]<4 ){ - /* Do not allow any cells smaller than 4 bytes. If a smaller cell - ** does exist, pad it with 0x00 bytes. */ - assert( b.szCell[b.nCell]==3 || CORRUPT_DB ); - assert( b.apCell[b.nCell]==&aSpace1[iSpace1-3] || CORRUPT_DB ); - aSpace1[iSpace1++] = 0x00; - b.szCell[b.nCell]++; + if( pOp->opcode<=SQLITE_MX_JUMP_OPCODE ){ + /* NOTE: Be sure to update mkopcodeh.tcl when adding or removing + ** cases from this switch! */ + switch( pOp->opcode ){ + case OP_Transaction: { + if( pOp->p2!=0 ) p->readOnly = 0; + /* no break */ deliberate_fall_through + } + case OP_AutoCommit: + case OP_Savepoint: { + p->bIsReader = 1; + break; + } +#ifndef SQLITE_OMIT_WAL + case OP_Checkpoint: +#endif + case OP_Vacuum: + case OP_JournalMode: { + p->readOnly = 0; + p->bIsReader = 1; + break; + } +#ifndef SQLITE_OMIT_VIRTUALTABLE + case OP_VUpdate: { + if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; + break; + } + case OP_VFilter: { + int n; + assert( (pOp - p->aOp) >= 3 ); + assert( pOp[-1].opcode==OP_Integer ); + n = pOp[-1].p1; + if( n>nMaxArgs ) nMaxArgs = n; + /* Fall through into the default case */ + /* no break */ deliberate_fall_through + } +#endif + default: { + if( pOp->p2<0 ){ + /* The mkopcodeh.tcl script has so arranged things that the only + ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to + ** have non-negative values for P2. */ + assert( (sqlcipher_sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); + assert( ADDR(pOp->p2)<-pParse->nLabel ); + pOp->p2 = aLabel[ADDR(pOp->p2)]; + } + break; } } - b.nCell++; + /* The mkopcodeh.tcl script has so arranged things that the only + ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to + ** have non-negative values for P2. */ + assert( (sqlcipher_sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0); } + if( pOp==p->aOp ) break; + pOp--; } - - /* - ** Figure out the number of pages needed to hold all b.nCell cells. - ** Store this number in "k". Also compute szNew[] which is the total - ** size of all cells on the i-th page and cntNew[] which is the index - ** in b.apCell[] of the cell that divides page i from page i+1. - ** cntNew[k] should equal b.nCell. - ** - ** Values computed by this block: - ** - ** k: The total number of sibling pages - ** szNew[i]: Spaced used on the i-th sibling page. - ** cntNew[i]: Index in b.apCell[] and b.szCell[] for the first cell to - ** the right of the i-th sibling page. - ** usableSpace: Number of bytes of space available on each sibling. - ** - */ - usableSpace = pBt->usableSize - 12 + leafCorrection; - for(i=k=0; iaDataEnd; - b.ixNx[k] = cntOld[i]; - if( k && b.ixNx[k]==b.ixNx[k-1] ){ - k--; /* Omit b.ixNx[] entry for child pages with no cells */ - } - if( !leafData ){ - k++; - b.apEnd[k] = pParent->aDataEnd; - b.ixNx[k] = cntOld[i]+1; - } - assert( p->nFree>=0 ); - szNew[i] = usableSpace - p->nFree; - for(j=0; jnOverflow; j++){ - szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]); - } - cntNew[i] = cntOld[i]; + if( aLabel ){ + sqlcipher_sqlite3DbFreeNN(p->db, pParse->aLabel); + pParse->aLabel = 0; } - k = nOld; - for(i=0; iusableSpace ){ - if( i+1>=k ){ - k = i+2; - if( k>NB+2 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; } - szNew[k-1] = 0; - cntNew[k-1] = b.nCell; - } - sz = 2 + cachedCellSize(&b, cntNew[i]-1); - szNew[i] -= sz; - if( !leafData ){ - if( cntNew[i]nLabel = 0; + *pMaxFuncArgs = nMaxArgs; + assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) ); +} + +#ifdef SQLITE_DEBUG +/* +** Check to see if a subroutine contains a jump to a location outside of +** the subroutine. If a jump outside the subroutine is detected, add code +** that will cause the program to halt with an error message. +** +** The subroutine consists of opcodes between iFirst and iLast. Jumps to +** locations within the subroutine are acceptable. iRetReg is a register +** that contains the return address. Jumps to outside the range of iFirst +** through iLast are also acceptable as long as the jump destination is +** an OP_Return to iReturnAddr. +** +** A jump to an unresolved label means that the jump destination will be +** beyond the current address. That is normally a jump to an early +** termination and is consider acceptable. +** +** This routine only runs during debug builds. The purpose is (of course) +** to detect invalid escapes out of a subroutine. The OP_Halt opcode +** is generated rather than an assert() or other error, so that ".eqp full" +** will still work to show the original bytecode, to aid in debugging. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeNoJumpsOutsideSubrtn( + Vdbe *v, /* The byte-code program under construction */ + int iFirst, /* First opcode of the subroutine */ + int iLast, /* Last opcode of the subroutine */ + int iRetReg /* Subroutine return address register */ +){ + VdbeOp *pOp; + Parse *pParse; + int i; + sqlcipher_sqlite3_str *pErr = 0; + assert( v!=0 ); + pParse = v->pParse; + assert( pParse!=0 ); + if( pParse->nErr ) return; + assert( iLast>=iFirst ); + assert( iLastnOp ); + pOp = &v->aOp[iFirst]; + for(i=iFirst; i<=iLast; i++, pOp++){ + if( (sqlcipher_sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ){ + int iDest = pOp->p2; /* Jump destination */ + if( iDest==0 ) continue; + if( pOp->opcode==OP_Gosub ) continue; + if( iDest<0 ){ + int j = ADDR(iDest); + assert( j>=0 ); + if( j>=-pParse->nLabel || pParse->aLabel[j]<0 ){ + continue; } + iDest = pParse->aLabel[j]; } - szNew[i+1] += sz; - cntNew[i]--; - } - while( cntNew[i]usableSpace ) break; - szNew[i] += sz; - cntNew[i]++; - if( !leafData ){ - if( cntNew[i]iLast ){ + int j = iDest; + for(; jnOp; j++){ + VdbeOp *pX = &v->aOp[j]; + if( pX->opcode==OP_Return ){ + if( pX->p1==iRetReg ) break; + continue; + } + if( pX->opcode==OP_Noop ) continue; + if( pX->opcode==OP_Explain ) continue; + if( pErr==0 ){ + pErr = sqlcipher_sqlite3_str_new(0); + }else{ + sqlcipher_sqlite3_str_appendchar(pErr, 1, '\n'); + } + sqlcipher_sqlite3_str_appendf(pErr, + "Opcode at %d jumps to %d which is outside the " + "subroutine at %d..%d", + i, iDest, iFirst, iLast); + break; } } - szNew[i+1] -= sz; - } - if( cntNew[i]>=b.nCell ){ - k = i+1; - }else if( cntNew[i] <= (i>0 ? cntNew[i-1] : 0) ){ - rc = SQLITE_CORRUPT_BKPT; - goto balance_cleanup; } } + if( pErr ){ + char *zErr = sqlcipher_sqlite3_str_finish(pErr); + sqlcipher_sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_INTERNAL, OE_Abort, 0, zErr, 0); + sqlcipher_sqlite3_free(zErr); + sqlcipher_sqlite3MayAbort(pParse); + } +} +#endif /* SQLITE_DEBUG */ - /* - ** The packing computed by the previous block is biased toward the siblings - ** on the left side (siblings with smaller keys). The left siblings are - ** always nearly full, while the right-most sibling might be nearly empty. - ** The next block of code attempts to adjust the packing of siblings to - ** get a better balance. - ** - ** This adjustment is more than an optimization. The packing above might - ** be so out of balance as to be illegal. For example, the right-most - ** sibling might be completely empty. This adjustment is not optional. - */ - for(i=k-1; i>0; i--){ - int szRight = szNew[i]; /* Size of sibling on the right */ - int szLeft = szNew[i-1]; /* Size of sibling on the left */ - int r; /* Index of right-most cell in left sibling */ - int d; /* Index of first cell to the left of right sibling */ +/* +** Return the address of the next instruction to be inserted. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCurrentAddr(Vdbe *p){ + assert( p->eVdbeState==VDBE_INIT_STATE ); + return p->nOp; +} - r = cntNew[i-1] - 1; - d = r + 1 - leafData; - (void)cachedCellSize(&b, d); - do{ - assert( d szLeft-(b.szCell[r]+(i==k-1?0:2)))){ - break; - } - szRight += b.szCell[d] + 2; - szLeft -= b.szCell[r] + 2; - cntNew[i-1] = r; - r--; - d--; - }while( r>=0 ); - szNew[i] = szRight; - szNew[i-1] = szLeft; - if( cntNew[i-1] <= (i>1 ? cntNew[i-2] : 0) ){ - rc = SQLITE_CORRUPT_BKPT; - goto balance_cleanup; - } +/* +** Verify that at least N opcode slots are available in p without +** having to malloc for more space (except when compiled using +** SQLITE_TEST_REALLOC_STRESS). This interface is used during testing +** to verify that certain calls to sqlcipher_sqlite3VdbeAddOpList() can never +** fail due to a OOM fault and hence that the return value from +** sqlcipher_sqlite3VdbeAddOpList() will always be non-NULL. +*/ +#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS) +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){ + assert( p->nOp + N <= p->nOpAlloc ); +} +#endif + +/* +** Verify that the VM passed as the only argument does not contain +** an OP_ResultRow opcode. Fail an assert() if it does. This is used +** by code in pragma.c to ensure that the implementation of certain +** pragmas comports with the flags specified in the mkpragmatab.tcl +** script. +*/ +#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS) +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeVerifyNoResultRow(Vdbe *p){ + int i; + for(i=0; inOp; i++){ + assert( p->aOp[i].opcode!=OP_ResultRow ); } +} +#endif - /* Sanity check: For a non-corrupt database file one of the follwing - ** must be true: - ** (1) We found one or more cells (cntNew[0])>0), or - ** (2) pPage is a virtual root page. A virtual root page is when - ** the real root page is page 1 and we are the only child of - ** that page. - */ - assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) || CORRUPT_DB); - TRACE(("BALANCE: old: %d(nc=%d) %d(nc=%d) %d(nc=%d)\n", - apOld[0]->pgno, apOld[0]->nCell, - nOld>=2 ? apOld[1]->pgno : 0, nOld>=2 ? apOld[1]->nCell : 0, - nOld>=3 ? apOld[2]->pgno : 0, nOld>=3 ? apOld[2]->nCell : 0 - )); +/* +** Generate code (a single OP_Abortable opcode) that will +** verify that the VDBE program can safely call Abort in the current +** context. +*/ +#if defined(SQLITE_DEBUG) +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeVerifyAbortable(Vdbe *p, int onError){ + if( onError==OE_Abort ) sqlcipher_sqlite3VdbeAddOp0(p, OP_Abortable); +} +#endif - /* - ** Allocate k new pages. Reuse old pages where possible. - */ - pageFlags = apOld[0]->aData[0]; - for(i=0; ipDbPage); - nNew++; - if( rc ) goto balance_cleanup; - }else{ - assert( i>0 ); - rc = allocateBtreePage(pBt, &pNew, &pgno, (bBulk ? 1 : pgno), 0); - if( rc ) goto balance_cleanup; - zeroPage(pNew, pageFlags); - apNew[i] = pNew; - nNew++; - cntOld[i] = b.nCell; +/* +** This function returns a pointer to the array of opcodes associated with +** the Vdbe passed as the first argument. It is the callers responsibility +** to arrange for the returned array to be eventually freed using the +** vdbeFreeOpArray() function. +** +** Before returning, *pnOp is set to the number of entries in the returned +** array. Also, *pnMaxArg is set to the larger of its current value and +** the number of entries in the Vdbe.apArg[] array required to execute the +** returned program. +*/ +SQLITE_PRIVATE VdbeOp *sqlcipher_sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ + VdbeOp *aOp = p->aOp; + assert( aOp && !p->db->mallocFailed ); - /* Set the pointer-map entry for the new sibling page. */ - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc); - if( rc!=SQLITE_OK ){ - goto balance_cleanup; - } - } - } - } + /* Check that sqlcipher_sqlite3VdbeUsesBtree() was not called on this VM */ + assert( DbMaskAllZero(p->btreeMask) ); - /* - ** Reassign page numbers so that the new pages are in ascending order. - ** This helps to keep entries in the disk file in order so that a scan - ** of the table is closer to a linear scan through the file. That in turn - ** helps the operating system to deliver pages from the disk more rapidly. - ** - ** An O(n^2) insertion sort algorithm is used, but since n is never more - ** than (NB+2) (a small constant), that should not be a problem. - ** - ** When NB==3, this one optimization makes the database about 25% faster - ** for large insertions and deletions. - */ - for(i=0; ipgno; - aPgFlags[i] = apNew[i]->pDbPage->flags; - for(j=0; jnOp; + p->aOp = 0; + return aOp; +} + +/* +** Add a whole list of operations to the operation stack. Return a +** pointer to the first operation inserted. +** +** Non-zero P2 arguments to jump instructions are automatically adjusted +** so that the jump target is relative to the first operation inserted. +*/ +SQLITE_PRIVATE VdbeOp *sqlcipher_sqlite3VdbeAddOpList( + Vdbe *p, /* Add opcodes to the prepared statement */ + int nOp, /* Number of opcodes to add */ + VdbeOpList const *aOp, /* The opcodes to be added */ + int iLineno /* Source-file line number of first opcode */ +){ + int i; + VdbeOp *pOut, *pFirst; + assert( nOp>0 ); + assert( p->eVdbeState==VDBE_INIT_STATE ); + if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){ + return 0; } - for(i=0; iaOp[p->nOp]; + for(i=0; iopcode = aOp->opcode; + pOut->p1 = aOp->p1; + pOut->p2 = aOp->p2; + assert( aOp->p2>=0 ); + if( (sqlcipher_sqlite3OpcodeProperty[aOp->opcode] & OPFLG_JUMP)!=0 && aOp->p2>0 ){ + pOut->p2 += p->nOp; } - pgno = aPgOrder[iBest]; - aPgOrder[iBest] = 0xffffffff; - if( iBest!=i ){ - if( iBest>i ){ - sqlcipher_sqlite3PagerRekey(apNew[iBest]->pDbPage, pBt->nPage+iBest+1, 0); - } - sqlcipher_sqlite3PagerRekey(apNew[i]->pDbPage, pgno, aPgFlags[iBest]); - apNew[i]->pgno = pgno; + pOut->p3 = aOp->p3; + pOut->p4type = P4_NOTUSED; + pOut->p4.p = 0; + pOut->p5 = 0; +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + pOut->zComment = 0; +#endif +#ifdef SQLITE_VDBE_COVERAGE + pOut->iSrcLine = iLineno+i; +#else + (void)iLineno; +#endif +#ifdef SQLITE_DEBUG + if( p->db->flags & SQLITE_VdbeAddopTrace ){ + sqlcipher_sqlite3VdbePrintOp(0, i+p->nOp, &p->aOp[i+p->nOp]); } +#endif } + p->nOp += nOp; + return pFirst; +} - TRACE(("BALANCE: new: %d(%d nc=%d) %d(%d nc=%d) %d(%d nc=%d) " - "%d(%d nc=%d) %d(%d nc=%d)\n", - apNew[0]->pgno, szNew[0], cntNew[0], - nNew>=2 ? apNew[1]->pgno : 0, nNew>=2 ? szNew[1] : 0, - nNew>=2 ? cntNew[1] - cntNew[0] - !leafData : 0, - nNew>=3 ? apNew[2]->pgno : 0, nNew>=3 ? szNew[2] : 0, - nNew>=3 ? cntNew[2] - cntNew[1] - !leafData : 0, - nNew>=4 ? apNew[3]->pgno : 0, nNew>=4 ? szNew[3] : 0, - nNew>=4 ? cntNew[3] - cntNew[2] - !leafData : 0, - nNew>=5 ? apNew[4]->pgno : 0, nNew>=5 ? szNew[4] : 0, - nNew>=5 ? cntNew[4] - cntNew[3] - !leafData : 0 - )); +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) +/* +** Add an entry to the array of counters managed by sqlcipher_sqlite3_stmt_scanstatus(). +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeScanStatus( + Vdbe *p, /* VM to add scanstatus() to */ + int addrExplain, /* Address of OP_Explain (or 0) */ + int addrLoop, /* Address of loop counter */ + int addrVisit, /* Address of rows visited counter */ + LogEst nEst, /* Estimated number of output rows */ + const char *zName /* Name of table or index being scanned */ +){ + sqlcipher_sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus); + ScanStatus *aNew; + aNew = (ScanStatus*)sqlcipher_sqlite3DbRealloc(p->db, p->aScan, nByte); + if( aNew ){ + ScanStatus *pNew = &aNew[p->nScan++]; + pNew->addrExplain = addrExplain; + pNew->addrLoop = addrLoop; + pNew->addrVisit = addrVisit; + pNew->nEst = nEst; + pNew->zName = sqlcipher_sqlite3DbStrDup(p->db, zName); + p->aScan = aNew; + } +} +#endif - assert( sqlcipher_sqlite3PagerIswriteable(pParent->pDbPage) ); - assert( nNew>=1 && nNew<=ArraySize(apNew) ); - assert( apNew[nNew-1]!=0 ); - put4byte(pRight, apNew[nNew-1]->pgno); - /* If the sibling pages are not leaves, ensure that the right-child pointer - ** of the right-most new sibling page is set to the value that was - ** originally in the same field of the right-most old sibling page. */ - if( (pageFlags & PTF_LEAF)==0 && nOld!=nNew ){ - MemPage *pOld = (nNew>nOld ? apNew : apOld)[nOld-1]; - memcpy(&apNew[nNew-1]->aData[8], &pOld->aData[8], 4); +/* +** Change the value of the opcode, or P1, P2, P3, or P5 operands +** for a specific instruction. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeOpcode(Vdbe *p, int addr, u8 iNewOpcode){ + sqlcipher_sqlite3VdbeGetOp(p,addr)->opcode = iNewOpcode; +} +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){ + sqlcipher_sqlite3VdbeGetOp(p,addr)->p1 = val; +} +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){ + sqlcipher_sqlite3VdbeGetOp(p,addr)->p2 = val; +} +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){ + sqlcipher_sqlite3VdbeGetOp(p,addr)->p3 = val; +} +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP5(Vdbe *p, u16 p5){ + assert( p->nOp>0 || p->db->mallocFailed ); + if( p->nOp>0 ) p->aOp[p->nOp-1].p5 = p5; +} + +/* +** Change the P2 operand of instruction addr so that it points to +** the address of the next instruction to be coded. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeJumpHere(Vdbe *p, int addr){ + sqlcipher_sqlite3VdbeChangeP2(p, addr, p->nOp); +} + +/* +** Change the P2 operand of the jump instruction at addr so that +** the jump lands on the next opcode. Or if the jump instruction was +** the previous opcode (and is thus a no-op) then simply back up +** the next instruction counter by one slot so that the jump is +** overwritten by the next inserted opcode. +** +** This routine is an optimization of sqlcipher_sqlite3VdbeJumpHere() that +** strives to omit useless byte-code like this: +** +** 7 Once 0 8 0 +** 8 ... +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){ + if( addr==p->nOp-1 ){ + assert( p->aOp[addr].opcode==OP_Once + || p->aOp[addr].opcode==OP_If + || p->aOp[addr].opcode==OP_FkIfZero ); + assert( p->aOp[addr].p4type==0 ); +#ifdef SQLITE_VDBE_COVERAGE + sqlcipher_sqlite3VdbeGetOp(p,-1)->iSrcLine = 0; /* Erase VdbeCoverage() macros */ +#endif + p->nOp--; + }else{ + sqlcipher_sqlite3VdbeChangeP2(p, addr, p->nOp); } +} - /* Make any required updates to pointer map entries associated with - ** cells stored on sibling pages following the balance operation. Pointer - ** map entries associated with divider cells are set by the insertCell() - ** routine. The associated pointer map entries are: - ** - ** a) if the cell contains a reference to an overflow chain, the - ** entry associated with the first page in the overflow chain, and - ** - ** b) if the sibling pages are not leaves, the child page associated - ** with the cell. - ** - ** If the sibling pages are not leaves, then the pointer map entry - ** associated with the right-child of each sibling may also need to be - ** updated. This happens below, after the sibling pages have been - ** populated, not here. - */ - if( ISAUTOVACUUM ){ - MemPage *pOld; - MemPage *pNew = pOld = apNew[0]; - int cntOldNext = pNew->nCell + pNew->nOverflow; - int iNew = 0; - int iOld = 0; - for(i=0; i=0 && iOldnCell + pOld->nOverflow + !leafData; - } - if( i==cntNew[iNew] ){ - pNew = apNew[++iNew]; - if( !leafData ) continue; - } +/* +** If the input FuncDef structure is ephemeral, then free it. If +** the FuncDef is not ephermal, then do nothing. +*/ +static void freeEphemeralFunction(sqlcipher_sqlite3 *db, FuncDef *pDef){ + if( (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){ + sqlcipher_sqlite3DbFreeNN(db, pDef); + } +} - /* Cell pCell is destined for new sibling page pNew. Originally, it - ** was either part of sibling page iOld (possibly an overflow cell), - ** or else the divider cell to the left of sibling page iOld. So, - ** if sibling page iOld had the same page number as pNew, and if - ** pCell really was a part of sibling page iOld (not a divider or - ** overflow cell), we can skip updating the pointer map entries. */ - if( iOld>=nNew - || pNew->pgno!=aPgno[iOld] - || !SQLITE_WITHIN(pCell,pOld->aData,pOld->aDataEnd) - ){ - if( !leafCorrection ){ - ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc); - } - if( cachedCellSize(&b,i)>pNew->minLocal ){ - ptrmapPutOvflPtr(pNew, pOld, pCell, &rc); - } - if( rc ) goto balance_cleanup; +/* +** Delete a P4 value if necessary. +*/ +static SQLITE_NOINLINE void freeP4Mem(sqlcipher_sqlite3 *db, Mem *p){ + if( p->szMalloc ) sqlcipher_sqlite3DbFree(db, p->zMalloc); + sqlcipher_sqlite3DbFreeNN(db, p); +} +static SQLITE_NOINLINE void freeP4FuncCtx(sqlcipher_sqlite3 *db, sqlcipher_sqlite3_context *p){ + freeEphemeralFunction(db, p->pFunc); + sqlcipher_sqlite3DbFreeNN(db, p); +} +static void freeP4(sqlcipher_sqlite3 *db, int p4type, void *p4){ + assert( db ); + switch( p4type ){ + case P4_FUNCCTX: { + freeP4FuncCtx(db, (sqlcipher_sqlite3_context*)p4); + break; + } + case P4_REAL: + case P4_INT64: + case P4_DYNAMIC: + case P4_INTARRAY: { + sqlcipher_sqlite3DbFree(db, p4); + break; + } + case P4_KEYINFO: { + if( db->pnBytesFreed==0 ) sqlcipher_sqlite3KeyInfoUnref((KeyInfo*)p4); + break; + } +#ifdef SQLITE_ENABLE_CURSOR_HINTS + case P4_EXPR: { + sqlcipher_sqlite3ExprDelete(db, (Expr*)p4); + break; + } +#endif + case P4_FUNCDEF: { + freeEphemeralFunction(db, (FuncDef*)p4); + break; + } + case P4_MEM: { + if( db->pnBytesFreed==0 ){ + sqlcipher_sqlite3ValueFree((sqlcipher_sqlite3_value*)p4); + }else{ + freeP4Mem(db, (Mem*)p4); } + break; + } + case P4_VTAB : { + if( db->pnBytesFreed==0 ) sqlcipher_sqlite3VtabUnlock((VTable *)p4); + break; } } +} - /* Insert new divider cells into pParent. */ - for(i=0; ileaf ){ - memcpy(&pNew->aData[8], pCell, 4); - }else if( leafData ){ - /* If the tree is a leaf-data tree, and the siblings are leaves, - ** then there is no divider cell in b.apCell[]. Instead, the divider - ** cell consists of the integer key for the right-most cell of - ** the sibling-page assembled above only. - */ - CellInfo info; - j--; - pNew->xParseCell(pNew, b.apCell[j], &info); - pCell = pTemp; - sz = 4 + putVarint(&pCell[4], info.nKey); - pTemp = 0; - }else{ - pCell -= 4; - /* Obscure case for non-leaf-data trees: If the cell at pCell was - ** previously stored on a leaf node, and its reported size was 4 - ** bytes, then it may actually be smaller than this - ** (see btreeParseCellPtr(), 4 bytes is the minimum size of - ** any cell). But it is important to pass the correct size to - ** insertCell(), so reparse the cell now. - ** - ** This can only happen for b-trees used to evaluate "IN (SELECT ...)" - ** and WITHOUT ROWID tables with exactly one column which is the - ** primary key. - */ - if( b.szCell[j]==4 ){ - assert(leafCorrection==4); - sz = pParent->xCellSize(pParent, pCell); - } +/* +** Free the space allocated for aOp and any p4 values allocated for the +** opcodes contained within. If aOp is not NULL it is assumed to contain +** nOp entries. +*/ +static void vdbeFreeOpArray(sqlcipher_sqlite3 *db, Op *aOp, int nOp){ + assert( nOp>=0 ); + if( aOp ){ + Op *pOp = &aOp[nOp-1]; + while(1){ /* Exit via break */ + if( pOp->p4type <= P4_FREE_IF_LE ) freeP4(db, pOp->p4type, pOp->p4.p); +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + sqlcipher_sqlite3DbFree(db, pOp->zComment); +#endif + if( pOp==aOp ) break; + pOp--; } - iOvflSpace += sz; - assert( sz<=pBt->maxLocal+23 ); - assert( iOvflSpace <= (int)pBt->pageSize ); - insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno, &rc); - if( rc!=SQLITE_OK ) goto balance_cleanup; - assert( sqlcipher_sqlite3PagerIswriteable(pParent->pDbPage) ); + sqlcipher_sqlite3DbFreeNN(db, aOp); } +} - /* Now update the actual sibling pages. The order in which they are updated - ** is important, as this code needs to avoid disrupting any page from which - ** cells may still to be read. In practice, this means: - ** - ** (1) If cells are moving left (from apNew[iPg] to apNew[iPg-1]) - ** then it is not safe to update page apNew[iPg] until after - ** the left-hand sibling apNew[iPg-1] has been updated. - ** - ** (2) If cells are moving right (from apNew[iPg] to apNew[iPg+1]) - ** then it is not safe to update page apNew[iPg] until after - ** the right-hand sibling apNew[iPg+1] has been updated. - ** - ** If neither of the above apply, the page is safe to update. - ** - ** The iPg value in the following loop starts at nNew-1 goes down - ** to 0, then back up to nNew-1 again, thus making two passes over - ** the pages. On the initial downward pass, only condition (1) above - ** needs to be tested because (2) will always be true from the previous - ** step. On the upward pass, both conditions are always true, so the - ** upwards pass simply processes pages that were missed on the downward - ** pass. - */ - for(i=1-nNew; i=0 && iPg=0 /* On the upwards pass, or... */ - || cntOld[iPg-1]>=cntNew[iPg-1] /* Condition (1) is true */ - ){ - int iNew; - int iOld; - int nNewCell; +/* +** Link the SubProgram object passed as the second argument into the linked +** list at Vdbe.pSubProgram. This list is used to delete all sub-program +** objects when the VM is no longer required. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeLinkSubProgram(Vdbe *pVdbe, SubProgram *p){ + p->pNext = pVdbe->pProgram; + pVdbe->pProgram = p; +} - /* Verify condition (1): If cells are moving left, update iPg - ** only after iPg-1 has already been updated. */ - assert( iPg==0 || cntOld[iPg-1]>=cntNew[iPg-1] || abDone[iPg-1] ); +/* +** Return true if the given Vdbe has any SubPrograms. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeHasSubProgram(Vdbe *pVdbe){ + return pVdbe->pProgram!=0; +} - /* Verify condition (2): If cells are moving right, update iPg - ** only after iPg+1 has already been updated. */ - assert( cntNew[iPg]>=cntOld[iPg] || abDone[iPg+1] ); +/* +** Change the opcode at addr into OP_Noop +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeChangeToNoop(Vdbe *p, int addr){ + VdbeOp *pOp; + if( p->db->mallocFailed ) return 0; + assert( addr>=0 && addrnOp ); + pOp = &p->aOp[addr]; + freeP4(p->db, pOp->p4type, pOp->p4.p); + pOp->p4type = P4_NOTUSED; + pOp->p4.z = 0; + pOp->opcode = OP_Noop; + return 1; +} - if( iPg==0 ){ - iNew = iOld = 0; - nNewCell = cntNew[0]; - }else{ - iOld = iPgnOp>0 && p->aOp[p->nOp-1].opcode==op ){ + return sqlcipher_sqlite3VdbeChangeToNoop(p, p->nOp-1); + }else{ + return 0; + } +} - rc = editPage(apNew[iPg], iOld, iNew, nNewCell, &b); - if( rc ) goto balance_cleanup; - abDone[iPg]++; - apNew[iPg]->nFree = usableSpace-szNew[iPg]; - assert( apNew[iPg]->nOverflow==0 ); - assert( apNew[iPg]->nCell==nNewCell ); +#ifdef SQLITE_DEBUG +/* +** Generate an OP_ReleaseReg opcode to indicate that a range of +** registers, except any identified by mask, are no longer in use. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeReleaseRegisters( + Parse *pParse, /* Parsing context */ + int iFirst, /* Index of first register to be released */ + int N, /* Number of registers to release */ + u32 mask, /* Mask of registers to NOT release */ + int bUndefine /* If true, mark registers as undefined */ +){ + if( N==0 || OptimizationDisabled(pParse->db, SQLITE_ReleaseReg) ) return; + assert( pParse->pVdbe ); + assert( iFirst>=1 ); + assert( iFirst+N-1<=pParse->nMem ); + if( N<=31 && mask!=0 ){ + while( N>0 && (mask&1)!=0 ){ + mask >>= 1; + iFirst++; + N--; + } + while( N>0 && N<=32 && (mask & MASKBIT32(N-1))!=0 ){ + mask &= ~MASKBIT32(N-1); + N--; } } + if( N>0 ){ + sqlcipher_sqlite3VdbeAddOp3(pParse->pVdbe, OP_ReleaseReg, iFirst, N, *(int*)&mask); + if( bUndefine ) sqlcipher_sqlite3VdbeChangeP5(pParse->pVdbe, 1); + } +} +#endif /* SQLITE_DEBUG */ - /* All pages have been processed exactly once */ - assert( memcmp(abDone, "\01\01\01\01\01", nNew)==0 ); - assert( nOld>0 ); - assert( nNew>0 ); +/* +** Change the value of the P4 operand for a specific instruction. +** This routine is useful when a large program is loaded from a +** static array using sqlcipher_sqlite3VdbeAddOpList but we want to make a +** few minor changes to the program. +** +** If n>=0 then the P4 operand is dynamic, meaning that a copy of +** the string is made into memory obtained from sqlcipher_sqlite3_malloc(). +** A value of n==0 means copy bytes of zP4 up to and including the +** first null byte. If n>0 then copy n+1 bytes of zP4. +** +** Other values of n (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points +** to a string or structure that is guaranteed to exist for the lifetime of +** the Vdbe. In these cases we can just copy the pointer. +** +** If addr<0 then change P4 on the most recently inserted instruction. +*/ +static void SQLITE_NOINLINE vdbeChangeP4Full( + Vdbe *p, + Op *pOp, + const char *zP4, + int n +){ + if( pOp->p4type ){ + freeP4(p->db, pOp->p4type, pOp->p4.p); + pOp->p4type = 0; + pOp->p4.p = 0; + } + if( n<0 ){ + sqlcipher_sqlite3VdbeChangeP4(p, (int)(pOp - p->aOp), zP4, n); + }else{ + if( n==0 ) n = sqlcipher_sqlite3Strlen30(zP4); + pOp->p4.z = sqlcipher_sqlite3DbStrNDup(p->db, zP4, n); + pOp->p4type = P4_DYNAMIC; + } +} +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){ + Op *pOp; + sqlcipher_sqlite3 *db; + assert( p!=0 ); + db = p->db; + assert( p->eVdbeState==VDBE_INIT_STATE ); + assert( p->aOp!=0 || db->mallocFailed ); + if( db->mallocFailed ){ + if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4); + return; + } + assert( p->nOp>0 ); + assert( addrnOp ); + if( addr<0 ){ + addr = p->nOp - 1; + } + pOp = &p->aOp[addr]; + if( n>=0 || pOp->p4type ){ + vdbeChangeP4Full(p, pOp, zP4, n); + return; + } + if( n==P4_INT32 ){ + /* Note: this cast is safe, because the origin data point was an int + ** that was cast to a (const char *). */ + pOp->p4.i = SQLITE_PTR_TO_INT(zP4); + pOp->p4type = P4_INT32; + }else if( zP4!=0 ){ + assert( n<0 ); + pOp->p4.p = (void*)zP4; + pOp->p4type = (signed char)n; + if( n==P4_VTAB ) sqlcipher_sqlite3VtabLock((VTable*)zP4); + } +} - if( isRoot && pParent->nCell==0 && pParent->hdrOffset<=apNew[0]->nFree ){ - /* The root page of the b-tree now contains no cells. The only sibling - ** page is the right-child of the parent. Copy the contents of the - ** child page into the parent, decreasing the overall height of the - ** b-tree structure by one. This is described as the "balance-shallower" - ** sub-algorithm in some documentation. - ** - ** If this is an auto-vacuum database, the call to copyNodeContent() - ** sets all pointer-map entries corresponding to database image pages - ** for which the pointer is stored within the content being copied. - ** - ** It is critical that the child page be defragmented before being - ** copied into the parent, because if the parent is page 1 then it will - ** by smaller than the child due to the database header, and so all the - ** free space needs to be up front. - */ - assert( nNew==1 || CORRUPT_DB ); - rc = defragmentPage(apNew[0], -1); - testcase( rc!=SQLITE_OK ); - assert( apNew[0]->nFree == - (get2byteNotZero(&apNew[0]->aData[5]) - apNew[0]->cellOffset - - apNew[0]->nCell*2) - || rc!=SQLITE_OK - ); - copyNodeContent(apNew[0], pParent, &rc); - freePage(apNew[0], &rc); - }else if( ISAUTOVACUUM && !leafCorrection ){ - /* Fix the pointer map entries associated with the right-child of each - ** sibling page. All other pointer map entries have already been taken - ** care of. */ - for(i=0; iaData[8]); - ptrmapPut(pBt, key, PTRMAP_BTREE, apNew[i]->pgno, &rc); - } +/* +** Change the P4 operand of the most recently coded instruction +** to the value defined by the arguments. This is a high-speed +** version of sqlcipher_sqlite3VdbeChangeP4(). +** +** The P4 operand must not have been previously defined. And the new +** P4 must not be P4_INT32. Use sqlcipher_sqlite3VdbeChangeP4() in either of +** those cases. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeAppendP4(Vdbe *p, void *pP4, int n){ + VdbeOp *pOp; + assert( n!=P4_INT32 && n!=P4_VTAB ); + assert( n<=0 ); + if( p->db->mallocFailed ){ + freeP4(p->db, n, pP4); + }else{ + assert( pP4!=0 ); + assert( p->nOp>0 ); + pOp = &p->aOp[p->nOp-1]; + assert( pOp->p4type==P4_NOTUSED ); + pOp->p4type = n; + pOp->p4.p = pP4; } +} - assert( pParent->isInit ); - TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n", - nOld, nNew, b.nCell)); +/* +** Set the P4 on the most recently added opcode to the KeyInfo for the +** index given. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){ + Vdbe *v = pParse->pVdbe; + KeyInfo *pKeyInfo; + assert( v!=0 ); + assert( pIdx!=0 ); + pKeyInfo = sqlcipher_sqlite3KeyInfoOfIndex(pParse, pIdx); + if( pKeyInfo ) sqlcipher_sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO); +} - /* Free any old pages that were not reused as new pages. - */ - for(i=nNew; inOp>0 || p->aOp==0 ); + assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->pParse->nErr>0 ); + if( p->nOp ){ + assert( p->aOp ); + sqlcipher_sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment); + p->aOp[p->nOp-1].zComment = sqlcipher_sqlite3VMPrintf(p->db, zFormat, ap); } - -#if 0 - if( ISAUTOVACUUM && rc==SQLITE_OK && apNew[0]->isInit ){ - /* The ptrmapCheckPages() contains assert() statements that verify that - ** all pointer map pages are set correctly. This is helpful while - ** debugging. This is usually disabled because a corrupt database may - ** cause an assert() statement to fail. */ - ptrmapCheckPages(apNew, nNew); - ptrmapCheckPages(&pParent, 1); +} +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){ + va_list ap; + if( p ){ + va_start(ap, zFormat); + vdbeVComment(p, zFormat, ap); + va_end(ap); } -#endif +} +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){ + va_list ap; + if( p ){ + sqlcipher_sqlite3VdbeAddOp0(p, OP_Noop); + va_start(ap, zFormat); + vdbeVComment(p, zFormat, ap); + va_end(ap); + } +} +#endif /* NDEBUG */ - /* - ** Cleanup before returning. - */ -balance_cleanup: - sqlcipher_sqlite3StackFree(0, b.apCell); - for(i=0; iiSrcLine = iLine; +} +#endif /* SQLITE_VDBE_COVERAGE */ + +/* +** Return the opcode for a given address. If the address is -1, then +** return the most recently inserted opcode. +** +** If a memory allocation error has occurred prior to the calling of this +** routine, then a pointer to a dummy VdbeOp will be returned. That opcode +** is readable but not writable, though it is cast to a writable value. +** The return of a dummy opcode allows the call to continue functioning +** after an OOM fault without having to check to see if the return from +** this routine is a valid pointer. But because the dummy.opcode is 0, +** dummy will never be written to. This is verified by code inspection and +** by running with Valgrind. +*/ +SQLITE_PRIVATE VdbeOp *sqlcipher_sqlite3VdbeGetOp(Vdbe *p, int addr){ + /* C89 specifies that the constant "dummy" will be initialized to all + ** zeros, which is correct. MSVC generates a warning, nevertheless. */ + static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */ + assert( p->eVdbeState==VDBE_INIT_STATE ); + if( addr<0 ){ + addr = p->nOp - 1; } - for(i=0; i=0 && addrnOp) || p->db->mallocFailed ); + if( p->db->mallocFailed ){ + return (VdbeOp*)&dummy; + }else{ + return &p->aOp[addr]; } - - return rc; } +#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) +/* +** Return an integer value for one of the parameters to the opcode pOp +** determined by character c. +*/ +static int translateP(char c, const Op *pOp){ + if( c=='1' ) return pOp->p1; + if( c=='2' ) return pOp->p2; + if( c=='3' ) return pOp->p3; + if( c=='4' ) return pOp->p4.i; + return pOp->p5; +} /* -** This function is called when the root page of a b-tree structure is -** overfull (has one or more overflow pages). -** -** A new child page is allocated and the contents of the current root -** page, including overflow cells, are copied into the child. The root -** page is then overwritten to make it an empty page with the right-child -** pointer pointing to the new page. +** Compute a string for the "comment" field of a VDBE opcode listing. ** -** Before returning, all pointer-map entries corresponding to pages -** that the new child-page now contains pointers to are updated. The -** entry corresponding to the new right-child pointer of the root -** page is also updated. +** The Synopsis: field in comments in the vdbe.c source file gets converted +** to an extra string that is appended to the sqlcipher_sqlite3OpcodeName(). In the +** absence of other comments, this synopsis becomes the comment on the opcode. +** Some translation occurs: ** -** If successful, *ppChild is set to contain a reference to the child -** page and SQLITE_OK is returned. In this case the caller is required -** to call releasePage() on *ppChild exactly once. If an error occurs, -** an error code is returned and *ppChild is set to 0. +** "PX" -> "r[X]" +** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1 +** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0 +** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x */ -static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ - int rc; /* Return value from subprocedures */ - MemPage *pChild = 0; /* Pointer to a new child page */ - Pgno pgnoChild = 0; /* Page number of the new child page */ - BtShared *pBt = pRoot->pBt; /* The BTree */ - - assert( pRoot->nOverflow>0 ); - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); +SQLITE_PRIVATE char *sqlcipher_sqlite3VdbeDisplayComment( + sqlcipher_sqlite3 *db, /* Optional - Oom error reporting only */ + const Op *pOp, /* The opcode to be commented */ + const char *zP4 /* Previously obtained value for P4 */ +){ + const char *zOpName; + const char *zSynopsis; + int nOpName; + int ii; + char zAlt[50]; + StrAccum x; - /* Make pRoot, the root page of the b-tree, writable. Allocate a new - ** page that will become the new right-child of pPage. Copy the contents - ** of the node stored on pRoot into the new child page. - */ - rc = sqlcipher_sqlite3PagerWrite(pRoot->pDbPage); - if( rc==SQLITE_OK ){ - rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0); - copyNodeContent(pRoot, pChild, &rc); - if( ISAUTOVACUUM ){ - ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc); + sqlcipher_sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH); + zOpName = sqlcipher_sqlite3OpcodeName(pOp->opcode); + nOpName = sqlcipher_sqlite3Strlen30(zOpName); + if( zOpName[nOpName+1] ){ + int seenCom = 0; + char c; + zSynopsis = zOpName + nOpName + 1; + if( strncmp(zSynopsis,"IF ",3)==0 ){ + sqlcipher_sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3); + zSynopsis = zAlt; + } + for(ii=0; (c = zSynopsis[ii])!=0; ii++){ + if( c=='P' ){ + c = zSynopsis[++ii]; + if( c=='4' ){ + sqlcipher_sqlite3_str_appendall(&x, zP4); + }else if( c=='X' ){ + if( pOp->zComment && pOp->zComment[0] ){ + sqlcipher_sqlite3_str_appendall(&x, pOp->zComment); + seenCom = 1; + break; + } + }else{ + int v1 = translateP(c, pOp); + int v2; + if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){ + ii += 3; + v2 = translateP(zSynopsis[ii], pOp); + if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){ + ii += 2; + v2++; + } + if( v2<2 ){ + sqlcipher_sqlite3_str_appendf(&x, "%d", v1); + }else{ + sqlcipher_sqlite3_str_appendf(&x, "%d..%d", v1, v1+v2-1); + } + }else if( strncmp(zSynopsis+ii+1, "@NP", 3)==0 ){ + sqlcipher_sqlite3_context *pCtx = pOp->p4.pCtx; + if( pOp->p4type!=P4_FUNCCTX || pCtx->argc==1 ){ + sqlcipher_sqlite3_str_appendf(&x, "%d", v1); + }else if( pCtx->argc>1 ){ + sqlcipher_sqlite3_str_appendf(&x, "%d..%d", v1, v1+pCtx->argc-1); + }else if( x.accError==0 ){ + assert( x.nChar>2 ); + x.nChar -= 2; + ii++; + } + ii += 3; + }else{ + sqlcipher_sqlite3_str_appendf(&x, "%d", v1); + if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){ + ii += 4; + } + } + } + }else{ + sqlcipher_sqlite3_str_appendchar(&x, 1, c); + } } + if( !seenCom && pOp->zComment ){ + sqlcipher_sqlite3_str_appendf(&x, "; %s", pOp->zComment); + } + }else if( pOp->zComment ){ + sqlcipher_sqlite3_str_appendall(&x, pOp->zComment); } - if( rc ){ - *ppChild = 0; - releasePage(pChild); - return rc; + if( (x.accError & SQLITE_NOMEM)!=0 && db!=0 ){ + sqlcipher_sqlite3OomFault(db); } - assert( sqlcipher_sqlite3PagerIswriteable(pChild->pDbPage) ); - assert( sqlcipher_sqlite3PagerIswriteable(pRoot->pDbPage) ); - assert( pChild->nCell==pRoot->nCell || CORRUPT_DB ); + return sqlcipher_sqlite3StrAccumFinish(&x); +} +#endif /* SQLITE_ENABLE_EXPLAIN_COMMENTS */ - TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno)); +#if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) +/* +** Translate the P4.pExpr value for an OP_CursorHint opcode into text +** that can be displayed in the P4 column of EXPLAIN output. +*/ +static void displayP4Expr(StrAccum *p, Expr *pExpr){ + const char *zOp = 0; + switch( pExpr->op ){ + case TK_STRING: + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlcipher_sqlite3_str_appendf(p, "%Q", pExpr->u.zToken); + break; + case TK_INTEGER: + sqlcipher_sqlite3_str_appendf(p, "%d", pExpr->u.iValue); + break; + case TK_NULL: + sqlcipher_sqlite3_str_appendf(p, "NULL"); + break; + case TK_REGISTER: { + sqlcipher_sqlite3_str_appendf(p, "r[%d]", pExpr->iTable); + break; + } + case TK_COLUMN: { + if( pExpr->iColumn<0 ){ + sqlcipher_sqlite3_str_appendf(p, "rowid"); + }else{ + sqlcipher_sqlite3_str_appendf(p, "c%d", (int)pExpr->iColumn); + } + break; + } + case TK_LT: zOp = "LT"; break; + case TK_LE: zOp = "LE"; break; + case TK_GT: zOp = "GT"; break; + case TK_GE: zOp = "GE"; break; + case TK_NE: zOp = "NE"; break; + case TK_EQ: zOp = "EQ"; break; + case TK_IS: zOp = "IS"; break; + case TK_ISNOT: zOp = "ISNOT"; break; + case TK_AND: zOp = "AND"; break; + case TK_OR: zOp = "OR"; break; + case TK_PLUS: zOp = "ADD"; break; + case TK_STAR: zOp = "MUL"; break; + case TK_MINUS: zOp = "SUB"; break; + case TK_REM: zOp = "REM"; break; + case TK_BITAND: zOp = "BITAND"; break; + case TK_BITOR: zOp = "BITOR"; break; + case TK_SLASH: zOp = "DIV"; break; + case TK_LSHIFT: zOp = "LSHIFT"; break; + case TK_RSHIFT: zOp = "RSHIFT"; break; + case TK_CONCAT: zOp = "CONCAT"; break; + case TK_UMINUS: zOp = "MINUS"; break; + case TK_UPLUS: zOp = "PLUS"; break; + case TK_BITNOT: zOp = "BITNOT"; break; + case TK_NOT: zOp = "NOT"; break; + case TK_ISNULL: zOp = "ISNULL"; break; + case TK_NOTNULL: zOp = "NOTNULL"; break; - /* Copy the overflow cells from pRoot to pChild */ - memcpy(pChild->aiOvfl, pRoot->aiOvfl, - pRoot->nOverflow*sizeof(pRoot->aiOvfl[0])); - memcpy(pChild->apOvfl, pRoot->apOvfl, - pRoot->nOverflow*sizeof(pRoot->apOvfl[0])); - pChild->nOverflow = pRoot->nOverflow; + default: + sqlcipher_sqlite3_str_appendf(p, "%s", "expr"); + break; + } - /* Zero the contents of pRoot. Then install pChild as the right-child. */ - zeroPage(pRoot, pChild->aData[0] & ~PTF_LEAF); - put4byte(&pRoot->aData[pRoot->hdrOffset+8], pgnoChild); + if( zOp ){ + sqlcipher_sqlite3_str_appendf(p, "%s(", zOp); + displayP4Expr(p, pExpr->pLeft); + if( pExpr->pRight ){ + sqlcipher_sqlite3_str_append(p, ",", 1); + displayP4Expr(p, pExpr->pRight); + } + sqlcipher_sqlite3_str_append(p, ")", 1); + } +} +#endif /* VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) */ - *ppChild = pChild; - return SQLITE_OK; + +#if VDBE_DISPLAY_P4 +/* +** Compute a string that describes the P4 parameter for an opcode. +** Use zTemp for any required temporary buffer space. +*/ +SQLITE_PRIVATE char *sqlcipher_sqlite3VdbeDisplayP4(sqlcipher_sqlite3 *db, Op *pOp){ + char *zP4 = 0; + StrAccum x; + + sqlcipher_sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH); + switch( pOp->p4type ){ + case P4_KEYINFO: { + int j; + KeyInfo *pKeyInfo = pOp->p4.pKeyInfo; + assert( pKeyInfo->aSortFlags!=0 ); + sqlcipher_sqlite3_str_appendf(&x, "k(%d", pKeyInfo->nKeyField); + for(j=0; jnKeyField; j++){ + CollSeq *pColl = pKeyInfo->aColl[j]; + const char *zColl = pColl ? pColl->zName : ""; + if( strcmp(zColl, "BINARY")==0 ) zColl = "B"; + sqlcipher_sqlite3_str_appendf(&x, ",%s%s%s", + (pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_DESC) ? "-" : "", + (pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_BIGNULL)? "N." : "", + zColl); + } + sqlcipher_sqlite3_str_append(&x, ")", 1); + break; + } +#ifdef SQLITE_ENABLE_CURSOR_HINTS + case P4_EXPR: { + displayP4Expr(&x, pOp->p4.pExpr); + break; + } +#endif + case P4_COLLSEQ: { + static const char *const encnames[] = {"?", "8", "16LE", "16BE"}; + CollSeq *pColl = pOp->p4.pColl; + assert( pColl->enc<4 ); + sqlcipher_sqlite3_str_appendf(&x, "%.18s-%s", pColl->zName, + encnames[pColl->enc]); + break; + } + case P4_FUNCDEF: { + FuncDef *pDef = pOp->p4.pFunc; + sqlcipher_sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg); + break; + } + case P4_FUNCCTX: { + FuncDef *pDef = pOp->p4.pCtx->pFunc; + sqlcipher_sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg); + break; + } + case P4_INT64: { + sqlcipher_sqlite3_str_appendf(&x, "%lld", *pOp->p4.pI64); + break; + } + case P4_INT32: { + sqlcipher_sqlite3_str_appendf(&x, "%d", pOp->p4.i); + break; + } + case P4_REAL: { + sqlcipher_sqlite3_str_appendf(&x, "%.16g", *pOp->p4.pReal); + break; + } + case P4_MEM: { + Mem *pMem = pOp->p4.pMem; + if( pMem->flags & MEM_Str ){ + zP4 = pMem->z; + }else if( pMem->flags & (MEM_Int|MEM_IntReal) ){ + sqlcipher_sqlite3_str_appendf(&x, "%lld", pMem->u.i); + }else if( pMem->flags & MEM_Real ){ + sqlcipher_sqlite3_str_appendf(&x, "%.16g", pMem->u.r); + }else if( pMem->flags & MEM_Null ){ + zP4 = "NULL"; + }else{ + assert( pMem->flags & MEM_Blob ); + zP4 = "(blob)"; + } + break; + } +#ifndef SQLITE_OMIT_VIRTUALTABLE + case P4_VTAB: { + sqlcipher_sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab; + sqlcipher_sqlite3_str_appendf(&x, "vtab:%p", pVtab); + break; + } +#endif + case P4_INTARRAY: { + u32 i; + u32 *ai = pOp->p4.ai; + u32 n = ai[0]; /* The first element of an INTARRAY is always the + ** count of the number of elements to follow */ + for(i=1; i<=n; i++){ + sqlcipher_sqlite3_str_appendf(&x, "%c%u", (i==1 ? '[' : ','), ai[i]); + } + sqlcipher_sqlite3_str_append(&x, "]", 1); + break; + } + case P4_SUBPROGRAM: { + zP4 = "program"; + break; + } + case P4_TABLE: { + zP4 = pOp->p4.pTab->zName; + break; + } + default: { + zP4 = pOp->p4.z; + } + } + if( zP4 ) sqlcipher_sqlite3_str_appendall(&x, zP4); + if( (x.accError & SQLITE_NOMEM)!=0 ){ + sqlcipher_sqlite3OomFault(db); + } + return sqlcipher_sqlite3StrAccumFinish(&x); } +#endif /* VDBE_DISPLAY_P4 */ /* -** Return SQLITE_CORRUPT if any cursor other than pCur is currently valid -** on the same B-tree as pCur. +** Declare to the Vdbe that the BTree object at db->aDb[i] is used. ** -** This can if a database is corrupt with two or more SQL tables -** pointing to the same b-tree. If an insert occurs on one SQL table -** and causes a BEFORE TRIGGER to do a secondary insert on the other SQL -** table linked to the same b-tree. If the secondary insert causes a -** rebalance, that can change content out from under the cursor on the -** first SQL table, violating invariants on the first insert. +** The prepared statements need to know in advance the complete set of +** attached databases that will be use. A mask of these databases +** is maintained in p->btreeMask. The p->lockMask value is the subset of +** p->btreeMask of databases that will require a lock. */ -static int anotherValidCursor(BtCursor *pCur){ - BtCursor *pOther; - for(pOther=pCur->pBt->pCursor; pOther; pOther=pOther->pNext){ - if( pOther!=pCur - && pOther->eState==CURSOR_VALID - && pOther->pPage==pCur->pPage - ){ - return SQLITE_CORRUPT_BKPT; - } +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeUsesBtree(Vdbe *p, int i){ + assert( i>=0 && idb->nDb && i<(int)sizeof(yDbMask)*8 ); + assert( i<(int)sizeof(p->btreeMask)*8 ); + DbMaskSet(p->btreeMask, i); + if( i!=1 && sqlcipher_sqlite3BtreeSharable(p->db->aDb[i].pBt) ){ + DbMaskSet(p->lockMask, i); } - return SQLITE_OK; } +#if !defined(SQLITE_OMIT_SHARED_CACHE) /* -** The page that pCur currently points to has just been modified in -** some way. This function figures out if this modification means the -** tree needs to be balanced, and if so calls the appropriate balancing -** routine. Balancing routines are: +** If SQLite is compiled to support shared-cache mode and to be threadsafe, +** this routine obtains the mutex associated with each BtShared structure +** that may be accessed by the VM passed as an argument. In doing so it also +** sets the BtShared.db member of each of the BtShared structures, ensuring +** that the correct busy-handler callback is invoked if required. ** -** balance_quick() -** balance_deeper() -** balance_nonroot() +** If SQLite is not threadsafe but does support shared-cache mode, then +** sqlcipher_sqlite3BtreeEnter() is invoked to set the BtShared.db variables +** of all of BtShared structures accessible via the database handle +** associated with the VM. +** +** If SQLite is not threadsafe and does not support shared-cache mode, this +** function is a no-op. +** +** The p->btreeMask field is a bitmask of all btrees that the prepared +** statement p will ever use. Let N be the number of bits in p->btreeMask +** corresponding to btrees that use shared cache. Then the runtime of +** this routine is N*N. But as N is rarely more than 1, this should not +** be a problem. */ -static int balance(BtCursor *pCur){ - int rc = SQLITE_OK; - const int nMin = pCur->pBt->usableSize * 2 / 3; - u8 aBalanceQuickSpace[13]; - u8 *pFree = 0; - - VVA_ONLY( int balance_quick_called = 0 ); - VVA_ONLY( int balance_deeper_called = 0 ); - - do { - int iPage; - MemPage *pPage = pCur->pPage; - - if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break; - if( pPage->nOverflow==0 && pPage->nFree<=nMin ){ - break; - }else if( (iPage = pCur->iPage)==0 ){ - if( pPage->nOverflow && (rc = anotherValidCursor(pCur))==SQLITE_OK ){ - /* The root page of the b-tree is overfull. In this case call the - ** balance_deeper() function to create a new child for the root-page - ** and copy the current contents of the root-page to it. The - ** next iteration of the do-loop will balance the child page. - */ - assert( balance_deeper_called==0 ); - VVA_ONLY( balance_deeper_called++ ); - rc = balance_deeper(pPage, &pCur->apPage[1]); - if( rc==SQLITE_OK ){ - pCur->iPage = 1; - pCur->ix = 0; - pCur->aiIdx[0] = 0; - pCur->apPage[0] = pPage; - pCur->pPage = pCur->apPage[1]; - assert( pCur->pPage->nOverflow ); - } - }else{ - break; - } - }else{ - MemPage * const pParent = pCur->apPage[iPage-1]; - int const iIdx = pCur->aiIdx[iPage-1]; - - rc = sqlcipher_sqlite3PagerWrite(pParent->pDbPage); - if( rc==SQLITE_OK && pParent->nFree<0 ){ - rc = btreeComputeFreeSpace(pParent); - } - if( rc==SQLITE_OK ){ -#ifndef SQLITE_OMIT_QUICKBALANCE - if( pPage->intKeyLeaf - && pPage->nOverflow==1 - && pPage->aiOvfl[0]==pPage->nCell - && pParent->pgno!=1 - && pParent->nCell==iIdx - ){ - /* Call balance_quick() to create a new sibling of pPage on which - ** to store the overflow cell. balance_quick() inserts a new cell - ** into pParent, which may cause pParent overflow. If this - ** happens, the next iteration of the do-loop will balance pParent - ** use either balance_nonroot() or balance_deeper(). Until this - ** happens, the overflow cell is stored in the aBalanceQuickSpace[] - ** buffer. - ** - ** The purpose of the following assert() is to check that only a - ** single call to balance_quick() is made for each call to this - ** function. If this were not verified, a subtle bug involving reuse - ** of the aBalanceQuickSpace[] might sneak in. - */ - assert( balance_quick_called==0 ); - VVA_ONLY( balance_quick_called++ ); - rc = balance_quick(pParent, pPage, aBalanceQuickSpace); - }else -#endif - { - /* In this case, call balance_nonroot() to redistribute cells - ** between pPage and up to 2 of its sibling pages. This involves - ** modifying the contents of pParent, which may cause pParent to - ** become overfull or underfull. The next iteration of the do-loop - ** will balance the parent page to correct this. - ** - ** If the parent page becomes overfull, the overflow cell or cells - ** are stored in the pSpace buffer allocated immediately below. - ** A subsequent iteration of the do-loop will deal with this by - ** calling balance_nonroot() (balance_deeper() may be called first, - ** but it doesn't deal with overflow cells - just moves them to a - ** different page). Once this subsequent call to balance_nonroot() - ** has completed, it is safe to release the pSpace buffer used by - ** the previous call, as the overflow cell data will have been - ** copied either into the body of a database page or into the new - ** pSpace buffer passed to the latter call to balance_nonroot(). - */ - u8 *pSpace = sqlcipher_sqlite3PageMalloc(pCur->pBt->pageSize); - rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1, - pCur->hints&BTREE_BULKLOAD); - if( pFree ){ - /* If pFree is not NULL, it points to the pSpace buffer used - ** by a previous call to balance_nonroot(). Its contents are - ** now stored either on real database pages or within the - ** new pSpace buffer, so it may be safely freed here. */ - sqlcipher_sqlite3PageFree(pFree); - } - - /* The pSpace buffer will be freed after the next call to - ** balance_nonroot(), or just before this function returns, whichever - ** comes first. */ - pFree = pSpace; - } - } - - pPage->nOverflow = 0; - - /* The next iteration of the do-loop balances the parent page. */ - releasePage(pPage); - pCur->iPage--; - assert( pCur->iPage>=0 ); - pCur->pPage = pCur->apPage[pCur->iPage]; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeEnter(Vdbe *p){ + int i; + sqlcipher_sqlite3 *db; + Db *aDb; + int nDb; + if( DbMaskAllZero(p->lockMask) ) return; /* The common case */ + db = p->db; + aDb = db->aDb; + nDb = db->nDb; + for(i=0; ilockMask,i) && ALWAYS(aDb[i].pBt!=0) ){ + sqlcipher_sqlite3BtreeEnter(aDb[i].pBt); } - }while( rc==SQLITE_OK ); - - if( pFree ){ - sqlcipher_sqlite3PageFree(pFree); } - return rc; } +#endif -/* Overwrite content from pX into pDest. Only do the write if the -** content is different from what is already there. +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 +/* +** Unlock all of the btrees previously locked by a call to sqlcipher_sqlite3VdbeEnter(). */ -static int btreeOverwriteContent( - MemPage *pPage, /* MemPage on which writing will occur */ - u8 *pDest, /* Pointer to the place to start writing */ - const BtreePayload *pX, /* Source of data to write */ - int iOffset, /* Offset of first byte to write */ - int iAmt /* Number of bytes to be written */ -){ - int nData = pX->nData - iOffset; - if( nData<=0 ){ - /* Overwritting with zeros */ - int i; - for(i=0; ipDbPage); - if( rc ) return rc; - memset(pDest + i, 0, iAmt - i); - } - }else{ - if( nDatapData) + iOffset, iAmt)!=0 ){ - int rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage); - if( rc ) return rc; - /* In a corrupt database, it is possible for the source and destination - ** buffers to overlap. This is harmless since the database is already - ** corrupt but it does cause valgrind and ASAN warnings. So use - ** memmove(). */ - memmove(pDest, ((u8*)pX->pData) + iOffset, iAmt); +static SQLITE_NOINLINE void vdbeLeave(Vdbe *p){ + int i; + sqlcipher_sqlite3 *db; + Db *aDb; + int nDb; + db = p->db; + aDb = db->aDb; + nDb = db->nDb; + for(i=0; ilockMask,i) && ALWAYS(aDb[i].pBt!=0) ){ + sqlcipher_sqlite3BtreeLeave(aDb[i].pBt); } } - return SQLITE_OK; } +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeLeave(Vdbe *p){ + if( DbMaskAllZero(p->lockMask) ) return; /* The common case */ + vdbeLeave(p); +} +#endif +#if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) /* -** Overwrite the cell that cursor pCur is pointing to with fresh content -** contained in pX. +** Print a single opcode. This routine is used for debugging only. */ -static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ - int iOffset; /* Next byte of pX->pData to write */ - int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */ - int rc; /* Return code */ - MemPage *pPage = pCur->pPage; /* Page being written */ - BtShared *pBt; /* Btree */ - Pgno ovflPgno; /* Next overflow page to write */ - u32 ovflPageSize; /* Size to write on overflow page */ - - if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd - || pCur->info.pPayload < pPage->aData + pPage->cellOffset - ){ - return SQLITE_CORRUPT_BKPT; - } - /* Overwrite the local portion first */ - rc = btreeOverwriteContent(pPage, pCur->info.pPayload, pX, - 0, pCur->info.nLocal); - if( rc ) return rc; - if( pCur->info.nLocal==nTotal ) return SQLITE_OK; - - /* Now overwrite the overflow pages */ - iOffset = pCur->info.nLocal; - assert( nTotal>=0 ); - assert( iOffset>=0 ); - ovflPgno = get4byte(pCur->info.pPayload + iOffset); - pBt = pPage->pBt; - ovflPageSize = pBt->usableSize - 4; - do{ - rc = btreeGetPage(pBt, ovflPgno, &pPage, 0); - if( rc ) return rc; - if( sqlcipher_sqlite3PagerPageRefcount(pPage->pDbPage)!=1 ){ - rc = SQLITE_CORRUPT_BKPT; - }else{ - if( iOffset+ovflPageSize<(u32)nTotal ){ - ovflPgno = get4byte(pPage->aData); - }else{ - ovflPageSize = nTotal - iOffset; - } - rc = btreeOverwriteContent(pPage, pPage->aData+4, pX, - iOffset, ovflPageSize); - } - sqlcipher_sqlite3PagerUnref(pPage->pDbPage); - if( rc ) return rc; - iOffset += ovflPageSize; - }while( iOffsetopcode), pOp->p1, pOp->p2, pOp->p3, + zP4 ? zP4 : "", pOp->p5, + zCom ? zCom : "" + ); + fflush(pOut); + sqlcipher_sqlite3_free(zP4); + sqlcipher_sqlite3_free(zCom); + sqlcipher_sqlite3EndBenignMalloc(); } - +#endif /* -** Insert a new record into the BTree. The content of the new record -** is described by the pX object. The pCur cursor is used only to -** define what table the record should be inserted into, and is left -** pointing at a random location. -** -** For a table btree (used for rowid tables), only the pX.nKey value of -** the key is used. The pX.pKey value must be NULL. The pX.nKey is the -** rowid or INTEGER PRIMARY KEY of the row. The pX.nData,pData,nZero fields -** hold the content of the row. +** Initialize an array of N Mem element. ** -** For an index btree (used for indexes and WITHOUT ROWID tables), the -** key is an arbitrary byte sequence stored in pX.pKey,nKey. The -** pX.pData,nData,nZero fields must be zero. +** This is a high-runner, so only those fields that really do need to +** be initialized are set. The Mem structure is organized so that +** the fields that get initialized are nearby and hopefully on the same +** cache line. ** -** If the seekResult parameter is non-zero, then a successful call to -** MovetoUnpacked() to seek cursor pCur to (pKey,nKey) has already -** been performed. In other words, if seekResult!=0 then the cursor -** is currently pointing to a cell that will be adjacent to the cell -** to be inserted. If seekResult<0 then pCur points to a cell that is -** smaller then (pKey,nKey). If seekResult>0 then pCur points to a cell -** that is larger than (pKey,nKey). +** Mem.flags = flags +** Mem.db = db +** Mem.szMalloc = 0 ** -** If seekResult==0, that means pCur is pointing at some unknown location. -** In that case, this routine must seek the cursor to the correct insertion -** point for (pKey,nKey) before doing the insertion. For index btrees, -** if pX->nMem is non-zero, then pX->aMem contains pointers to the unpacked -** key values and pX->aMem can be used instead of pX->pKey to avoid having -** to decode the key. +** All other fields of Mem can safely remain uninitialized for now. They +** will be initialized before use. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeInsert( - BtCursor *pCur, /* Insert data into the table of this cursor */ - const BtreePayload *pX, /* Content of the row to be inserted */ - int flags, /* True if this is likely an append */ - int seekResult /* Result of prior MovetoUnpacked() call */ -){ - int rc; - int loc = seekResult; /* -1: before desired location +1: after */ - int szNew = 0; - int idx; - MemPage *pPage; - Btree *p = pCur->pBtree; - BtShared *pBt = p->pBt; - unsigned char *oldCell; - unsigned char *newCell = 0; - - assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND))==flags ); - - if( pCur->eState==CURSOR_FAULT ){ - assert( pCur->skipNext!=SQLITE_OK ); - return pCur->skipNext; +static void initMemArray(Mem *p, int N, sqlcipher_sqlite3 *db, u16 flags){ + if( N>0 ){ + do{ + p->flags = flags; + p->db = db; + p->szMalloc = 0; +#ifdef SQLITE_DEBUG + p->pScopyFrom = 0; +#endif + p++; + }while( (--N)>0 ); } +} - assert( cursorOwnsBtShared(pCur) ); - assert( (pCur->curFlags & BTCF_WriteFlag)!=0 - && pBt->inTransaction==TRANS_WRITE - && (pBt->btsFlags & BTS_READ_ONLY)==0 ); - assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); - - /* Assert that the caller has been consistent. If this cursor was opened - ** expecting an index b-tree, then the caller should be inserting blob - ** keys with no associated data. If the cursor was opened expecting an - ** intkey table, the caller should be inserting integer keys with a - ** blob of associated data. */ - assert( (pX->pKey==0)==(pCur->pKeyInfo==0) ); +/* +** Release auxiliary memory held in an array of N Mem elements. +** +** After this routine returns, all Mem elements in the array will still +** be valid. Those Mem elements that were not holding auxiliary resources +** will be unchanged. Mem elements which had something freed will be +** set to MEM_Undefined. +*/ +static void releaseMemArray(Mem *p, int N){ + if( p && N ){ + Mem *pEnd = &p[N]; + sqlcipher_sqlite3 *db = p->db; + if( db->pnBytesFreed ){ + do{ + if( p->szMalloc ) sqlcipher_sqlite3DbFree(db, p->zMalloc); + }while( (++p)curFlags & BTCF_Multiple ){ - rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); - if( rc ) return rc; + /* This block is really an inlined version of sqlcipher_sqlite3VdbeMemRelease() + ** that takes advantage of the fact that the memory cell value is + ** being set to NULL after releasing any dynamic resources. + ** + ** The justification for duplicating code is that according to + ** callgrind, this causes a certain test case to hit the CPU 4.7 + ** percent less (x86 linux, gcc version 4.1.2, -O6) than if + ** sqlcipher_sqlite3MemRelease() were called from here. With -O2, this jumps + ** to 6.6 percent. The test case is inserting 1000 rows into a table + ** with no indexes using a single prepared INSERT statement, bind() + ** and reset(). Inserts are grouped into a transaction. + */ + testcase( p->flags & MEM_Agg ); + testcase( p->flags & MEM_Dyn ); + if( p->flags&(MEM_Agg|MEM_Dyn) ){ + testcase( (p->flags & MEM_Dyn)!=0 && p->xDel==sqlcipher_sqlite3VdbeFrameMemDel ); + sqlcipher_sqlite3VdbeMemRelease(p); + p->flags = MEM_Undefined; + }else if( p->szMalloc ){ + sqlcipher_sqlite3DbFreeNN(db, p->zMalloc); + p->szMalloc = 0; + p->flags = MEM_Undefined; + } +#ifdef SQLITE_DEBUG + else{ + p->flags = MEM_Undefined; + } +#endif + }while( (++p)pKeyInfo==0 ){ - assert( pX->pKey==0 ); - /* If this is an insert into a table b-tree, invalidate any incrblob - ** cursors open on the row being replaced */ - invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0); - - /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing - ** to a row with the same key as the new entry being inserted. - */ #ifdef SQLITE_DEBUG - if( flags & BTREE_SAVEPOSITION ){ - assert( pCur->curFlags & BTCF_ValidNKey ); - assert( pX->nKey==pCur->info.nKey ); - assert( loc==0 ); - } +/* +** Verify that pFrame is a valid VdbeFrame pointer. Return true if it is +** and false if something is wrong. +** +** This routine is intended for use inside of assert() statements only. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeFrameIsValid(VdbeFrame *pFrame){ + if( pFrame->iFrameMagic!=SQLITE_FRAME_MAGIC ) return 0; + return 1; +} #endif - /* On the other hand, BTREE_SAVEPOSITION==0 does not imply - ** that the cursor is not pointing to a row to be overwritten. - ** So do a complete check. - */ - if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey ){ - /* The cursor is pointing to the entry that is to be - ** overwritten */ - assert( pX->nData>=0 && pX->nZero>=0 ); - if( pCur->info.nSize!=0 - && pCur->info.nPayload==(u32)pX->nData+pX->nZero - ){ - /* New entry is the same size as the old. Do an overwrite */ - return btreeOverwriteCell(pCur, pX); - } - assert( loc==0 ); - }else if( loc==0 ){ - /* The cursor is *not* pointing to the cell to be overwritten, nor - ** to an adjacent cell. Move the cursor so that it is pointing either - ** to the cell to be overwritten or an adjacent cell. - */ - rc = sqlcipher_sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc); - if( rc ) return rc; - } - }else{ - /* This is an index or a WITHOUT ROWID table */ - /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing - ** to a row with the same key as the new entry being inserted. - */ - assert( (flags & BTREE_SAVEPOSITION)==0 || loc==0 ); +/* +** This is a destructor on a Mem object (which is really an sqlcipher_sqlite3_value) +** that deletes the Frame object that is attached to it as a blob. +** +** This routine does not delete the Frame right away. It merely adds the +** frame to a list of frames to be deleted when the Vdbe halts. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeFrameMemDel(void *pArg){ + VdbeFrame *pFrame = (VdbeFrame*)pArg; + assert( sqlcipher_sqlite3VdbeFrameIsValid(pFrame) ); + pFrame->pParent = pFrame->v->pDelFrame; + pFrame->v->pDelFrame = pFrame; +} - /* If the cursor is not already pointing either to the cell to be - ** overwritten, or if a new cell is being inserted, if the cursor is - ** not pointing to an immediately adjacent cell, then move the cursor - ** so that it does. - */ - if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){ - if( pX->nMem ){ - UnpackedRecord r; - r.pKeyInfo = pCur->pKeyInfo; - r.aMem = pX->aMem; - r.nField = pX->nMem; - r.default_rc = 0; - r.errCode = 0; - r.r1 = 0; - r.r2 = 0; - r.eqSeen = 0; - rc = sqlcipher_sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc); - }else{ - rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc); - } - if( rc ) return rc; - } +#if defined(SQLITE_ENABLE_BYTECODE_VTAB) || !defined(SQLITE_OMIT_EXPLAIN) +/* +** Locate the next opcode to be displayed in EXPLAIN or EXPLAIN +** QUERY PLAN output. +** +** Return SQLITE_ROW on success. Return SQLITE_DONE if there are no +** more opcodes to be displayed. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeNextOpcode( + Vdbe *p, /* The statement being explained */ + Mem *pSub, /* Storage for keeping track of subprogram nesting */ + int eMode, /* 0: normal. 1: EQP. 2: TablesUsed */ + int *piPc, /* IN/OUT: Current rowid. Overwritten with next rowid */ + int *piAddr, /* OUT: Write index into (*paOp)[] here */ + Op **paOp /* OUT: Write the opcode array here */ +){ + int nRow; /* Stop when row count reaches this */ + int nSub = 0; /* Number of sub-vdbes seen so far */ + SubProgram **apSub = 0; /* Array of sub-vdbes */ + int i; /* Next instruction address */ + int rc = SQLITE_OK; /* Result code */ + Op *aOp = 0; /* Opcode array */ + int iPc; /* Rowid. Copy of value in *piPc */ - /* If the cursor is currently pointing to an entry to be overwritten - ** and the new content is the same as as the old, then use the - ** overwrite optimization. - */ - if( loc==0 ){ - getCellInfo(pCur); - if( pCur->info.nKey==pX->nKey ){ - BtreePayload x2; - x2.pData = pX->pKey; - x2.nData = pX->nKey; - x2.nZero = 0; - return btreeOverwriteCell(pCur, &x2); - } + /* When the number of output rows reaches nRow, that means the + ** listing has finished and sqlcipher_sqlite3_step() should return SQLITE_DONE. + ** nRow is the sum of the number of rows in the main program, plus + ** the sum of the number of rows in all trigger subprograms encountered + ** so far. The nRow value will increase as new trigger subprograms are + ** encountered, but p->pc will eventually catch up to nRow. + */ + nRow = p->nOp; + if( pSub!=0 ){ + if( pSub->flags&MEM_Blob ){ + /* pSub is initiallly NULL. It is initialized to a BLOB by + ** the P4_SUBPROGRAM processing logic below */ + nSub = pSub->n/sizeof(Vdbe*); + apSub = (SubProgram **)pSub->z; } - - } - assert( pCur->eState==CURSOR_VALID - || (pCur->eState==CURSOR_INVALID && loc) - || CORRUPT_DB ); - - pPage = pCur->pPage; - assert( pPage->intKey || pX->nKey>=0 ); - assert( pPage->leaf || !pPage->intKey ); - if( pPage->nFree<0 ){ - if( pCur->eState>CURSOR_INVALID ){ - rc = SQLITE_CORRUPT_BKPT; - }else{ - rc = btreeComputeFreeSpace(pPage); + for(i=0; inOp; } - if( rc ) return rc; } - - TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", - pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, - loc==0 ? "overwrite" : "new entry")); - assert( pPage->isInit ); - newCell = pBt->pTmpSpace; - assert( newCell!=0 ); - rc = fillInCell(pPage, newCell, pX, &szNew); - if( rc ) goto end_insert; - assert( szNew==pPage->xCellSize(pPage, newCell) ); - assert( szNew <= MX_CELL_SIZE(pBt) ); - idx = pCur->ix; - if( loc==0 ){ - CellInfo info; - assert( idxnCell ); - rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage); - if( rc ){ - goto end_insert; - } - oldCell = findCell(pPage, idx); - if( !pPage->leaf ){ - memcpy(newCell, oldCell, 4); + iPc = *piPc; + while(1){ /* Loop exits via break */ + i = iPc++; + if( i>=nRow ){ + p->rc = SQLITE_OK; + rc = SQLITE_DONE; + break; } - rc = clearCell(pPage, oldCell, &info); - testcase( pCur->curFlags & BTCF_ValidOvfl ); - invalidateOverflowCache(pCur); - if( info.nSize==szNew && info.nLocal==info.nPayload - && (!ISAUTOVACUUM || szNewminLocal) - ){ - /* Overwrite the old cell with the new if they are the same size. - ** We could also try to do this if the old cell is smaller, then add - ** the leftover space to the free list. But experiments show that - ** doing that is no faster then skipping this optimization and just - ** calling dropCell() and insertCell(). - ** - ** This optimization cannot be used on an autovacuum database if the - ** new entry uses overflow pages, as the insertCell() call below is - ** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */ - assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */ - if( oldCell < pPage->aData+pPage->hdrOffset+10 ){ - return SQLITE_CORRUPT_BKPT; - } - if( oldCell+szNew > pPage->aDataEnd ){ - return SQLITE_CORRUPT_BKPT; + if( inOp ){ + /* The rowid is small enough that we are still in the + ** main program. */ + aOp = p->aOp; + }else{ + /* We are currently listing subprograms. Figure out which one and + ** pick up the appropriate opcode. */ + int j; + i -= p->nOp; + assert( apSub!=0 ); + assert( nSub>0 ); + for(j=0; i>=apSub[j]->nOp; j++){ + i -= apSub[j]->nOp; + assert( inOp || j+1aOp; } - dropCell(pPage, idx, info.nSize, &rc); - if( rc ) goto end_insert; - }else if( loc<0 && pPage->nCell>0 ){ - assert( pPage->leaf ); - idx = ++pCur->ix; - pCur->curFlags &= ~BTCF_ValidNKey; - }else{ - assert( pPage->leaf ); - } - insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); - assert( pPage->nOverflow==0 || rc==SQLITE_OK ); - assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); - - /* If no error has occurred and pPage has an overflow cell, call balance() - ** to redistribute the cells within the tree. Since balance() may move - ** the cursor, zero the BtCursor.info.nSize and BTCF_ValidNKey - ** variables. - ** - ** Previous versions of SQLite called moveToRoot() to move the cursor - ** back to the root page as balance() used to invalidate the contents - ** of BtCursor.apPage[] and BtCursor.aiIdx[]. Instead of doing that, - ** set the cursor state to "invalid". This makes common insert operations - ** slightly faster. - ** - ** There is a subtle but important optimization here too. When inserting - ** multiple records into an intkey b-tree using a single cursor (as can - ** happen while processing an "INSERT INTO ... SELECT" statement), it - ** is advantageous to leave the cursor pointing to the last entry in - ** the b-tree if possible. If the cursor is left pointing to the last - ** entry in the table, and the next row inserted has an integer key - ** larger than the largest existing key, it is possible to insert the - ** row without seeking the cursor. This can be a big performance boost. - */ - pCur->info.nSize = 0; - if( pPage->nOverflow ){ - assert( rc==SQLITE_OK ); - pCur->curFlags &= ~(BTCF_ValidNKey); - rc = balance(pCur); - /* Must make sure nOverflow is reset to zero even if the balance() - ** fails. Internal data structure corruption will result otherwise. - ** Also, set the cursor state to invalid. This stops saveCursorPosition() - ** from trying to save the current position of the cursor. */ - pCur->pPage->nOverflow = 0; - pCur->eState = CURSOR_INVALID; - if( (flags & BTREE_SAVEPOSITION) && rc==SQLITE_OK ){ - btreeReleaseAllCursorPages(pCur); - if( pCur->pKeyInfo ){ - assert( pCur->pKey==0 ); - pCur->pKey = sqlcipher_sqlite3Malloc( pX->nKey ); - if( pCur->pKey==0 ){ - rc = SQLITE_NOMEM; - }else{ - memcpy(pCur->pKey, pX->pKey, pX->nKey); + /* When an OP_Program opcode is encounter (the only opcode that has + ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms + ** kept in p->aMem[9].z to hold the new program - assuming this subprogram + ** has not already been seen. + */ + if( pSub!=0 && aOp[i].p4type==P4_SUBPROGRAM ){ + int nByte = (nSub+1)*sizeof(SubProgram*); + int j; + for(j=0; jrc = sqlcipher_sqlite3VdbeMemGrow(pSub, nByte, nSub!=0); + if( p->rc!=SQLITE_OK ){ + rc = SQLITE_ERROR; + break; } + apSub = (SubProgram **)pSub->z; + apSub[nSub++] = aOp[i].p4.pProgram; + MemSetTypeFlag(pSub, MEM_Blob); + pSub->n = nSub*sizeof(SubProgram*); + nRow += aOp[i].p4.pProgram->nOp; } - pCur->eState = CURSOR_REQUIRESEEK; - pCur->nKey = pX->nKey; + } + if( eMode==0 ) break; +#ifdef SQLITE_ENABLE_BYTECODE_VTAB + if( eMode==2 ){ + Op *pOp = aOp + i; + if( pOp->opcode==OP_OpenRead ) break; + if( pOp->opcode==OP_OpenWrite && (pOp->p5 & OPFLAG_P2ISREG)==0 ) break; + if( pOp->opcode==OP_ReopenIdx ) break; + }else +#endif + { + assert( eMode==1 ); + if( aOp[i].opcode==OP_Explain ) break; + if( aOp[i].opcode==OP_Init && iPc>1 ) break; } } - assert( pCur->iPage<0 || pCur->pPage->nOverflow==0 ); - -end_insert: + *piPc = iPc; + *piAddr = i; + *paOp = aOp; return rc; } +#endif /* SQLITE_ENABLE_BYTECODE_VTAB || !SQLITE_OMIT_EXPLAIN */ + /* -** Delete the entry that the cursor is pointing to. -** -** If the BTREE_SAVEPOSITION bit of the flags parameter is zero, then -** the cursor is left pointing at an arbitrary location after the delete. -** But if that bit is set, then the cursor is left in a state such that -** the next call to BtreeNext() or BtreePrev() moves it to the same row -** as it would have been on if the call to BtreeDelete() had been omitted. -** -** The BTREE_AUXDELETE bit of flags indicates that is one of several deletes -** associated with a single table entry and its indexes. Only one of those -** deletes is considered the "primary" delete. The primary delete occurs -** on a cursor that is not a BTREE_FORDELETE cursor. All but one delete -** operation on non-FORDELETE cursors is tagged with the AUXDELETE flag. -** The BTREE_AUXDELETE bit is a hint that is not used by this implementation, -** but which might be used by alternative storage engines. +** Delete a VdbeFrame object and its contents. VdbeFrame objects are +** allocated by the OP_Program opcode in sqlcipher_sqlite3VdbeExec(). */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ - Btree *p = pCur->pBtree; - BtShared *pBt = p->pBt; - int rc; /* Return code */ - MemPage *pPage; /* Page to delete cell from */ - unsigned char *pCell; /* Pointer to cell to delete */ - int iCellIdx; /* Index of cell to delete */ - int iCellDepth; /* Depth of node containing pCell */ - CellInfo info; /* Size of the cell being deleted */ - int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */ - u8 bPreserve = flags & BTREE_SAVEPOSITION; /* Keep cursor valid */ - - assert( cursorOwnsBtShared(pCur) ); - assert( pBt->inTransaction==TRANS_WRITE ); - assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); - assert( pCur->curFlags & BTCF_WriteFlag ); - assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); - assert( !hasReadConflicts(p, pCur->pgnoRoot) ); - assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); - if( pCur->eState==CURSOR_REQUIRESEEK ){ - rc = btreeRestoreCursorPosition(pCur); - if( rc ) return rc; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeFrameDelete(VdbeFrame *p){ + int i; + Mem *aMem = VdbeFrameMem(p); + VdbeCursor **apCsr = (VdbeCursor **)&aMem[p->nChildMem]; + assert( sqlcipher_sqlite3VdbeFrameIsValid(p) ); + for(i=0; inChildCsr; i++){ + if( apCsr[i] ) sqlcipher_sqlite3VdbeFreeCursorNN(p->v, apCsr[i]); } - assert( pCur->eState==CURSOR_VALID ); + releaseMemArray(aMem, p->nChildMem); + sqlcipher_sqlite3VdbeDeleteAuxData(p->v->db, &p->pAuxData, -1, 0); + sqlcipher_sqlite3DbFree(p->v->db, p); +} - iCellDepth = pCur->iPage; - iCellIdx = pCur->ix; - pPage = pCur->pPage; - pCell = findCell(pPage, iCellIdx); - if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ) return SQLITE_CORRUPT; +#ifndef SQLITE_OMIT_EXPLAIN +/* +** Give a listing of the program in the virtual machine. +** +** The interface is the same as sqlcipher_sqlite3VdbeExec(). But instead of +** running the code, it invokes the callback once for each instruction. +** This feature is used to implement "EXPLAIN". +** +** When p->explain==1, each instruction is listed. When +** p->explain==2, only OP_Explain instructions are listed and these +** are shown in a different format. p->explain==2 is used to implement +** EXPLAIN QUERY PLAN. +** 2018-04-24: In p->explain==2 mode, the OP_Init opcodes of triggers +** are also shown, so that the boundaries between the main program and +** each trigger are clear. +** +** When p->explain==1, first the main program is listed, then each of +** the trigger subprograms are listed one by one. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeList( + Vdbe *p /* The VDBE */ +){ + Mem *pSub = 0; /* Memory cell hold array of subprogs */ + sqlcipher_sqlite3 *db = p->db; /* The database connection */ + int i; /* Loop counter */ + int rc = SQLITE_OK; /* Return code */ + Mem *pMem = &p->aMem[1]; /* First Mem of result set */ + int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0); + Op *aOp; /* Array of opcodes */ + Op *pOp; /* Current opcode */ - /* If the bPreserve flag is set to true, then the cursor position must - ** be preserved following this delete operation. If the current delete - ** will cause a b-tree rebalance, then this is done by saving the cursor - ** key and leaving the cursor in CURSOR_REQUIRESEEK state before - ** returning. - ** - ** Or, if the current delete will not cause a rebalance, then the cursor - ** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately - ** before or after the deleted entry. In this case set bSkipnext to true. */ - if( bPreserve ){ - if( !pPage->leaf - || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3) - || pPage->nCell==1 /* See dbfuzz001.test for a test case */ - ){ - /* A b-tree rebalance will be required after deleting this entry. - ** Save the cursor key. */ - rc = saveCursorKey(pCur); - if( rc ) return rc; - }else{ - bSkipnext = 1; - } - } + assert( p->explain ); + assert( p->eVdbeState==VDBE_RUN_STATE ); + assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); - /* If the page containing the entry to delete is not a leaf page, move - ** the cursor to the largest entry in the tree that is smaller than - ** the entry being deleted. This cell will replace the cell being deleted - ** from the internal node. The 'previous' entry is used for this instead - ** of the 'next' entry, as the previous entry is always a part of the - ** sub-tree headed by the child page of the cell being deleted. This makes - ** balancing the tree following the delete operation easier. */ - if( !pPage->leaf ){ - rc = sqlcipher_sqlite3BtreePrevious(pCur, 0); - assert( rc!=SQLITE_DONE ); - if( rc ) return rc; - } + /* Even though this opcode does not use dynamic strings for + ** the result, result columns may become dynamic if the user calls + ** sqlcipher_sqlite3_column_text16(), causing a translation to UTF-16 encoding. + */ + releaseMemArray(pMem, 8); + p->pResultSet = 0; - /* Save the positions of any other cursors open on this table before - ** making any modifications. */ - if( pCur->curFlags & BTCF_Multiple ){ - rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); - if( rc ) return rc; + if( p->rc==SQLITE_NOMEM ){ + /* This happens if a malloc() inside a call to sqlcipher_sqlite3_column_text() or + ** sqlcipher_sqlite3_column_text16() failed. */ + sqlcipher_sqlite3OomFault(db); + return SQLITE_ERROR; } - /* If this is a delete operation to remove a row from a table b-tree, - ** invalidate any incrblob cursors open on the row being deleted. */ - if( pCur->pKeyInfo==0 ){ - invalidateIncrblobCursors(p, pCur->pgnoRoot, pCur->info.nKey, 0); + if( bListSubprogs ){ + /* The first 8 memory cells are used for the result set. So we will + ** commandeer the 9th cell to use as storage for an array of pointers + ** to trigger subprograms. The VDBE is guaranteed to have at least 9 + ** cells. */ + assert( p->nMem>9 ); + pSub = &p->aMem[9]; + }else{ + pSub = 0; } - /* Make the page containing the entry to be deleted writable. Then free any - ** overflow pages associated with the entry and finally remove the cell - ** itself from within the page. */ - rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage); - if( rc ) return rc; - rc = clearCell(pPage, pCell, &info); - dropCell(pPage, iCellIdx, info.nSize, &rc); - if( rc ) return rc; - - /* If the cell deleted was not located on a leaf page, then the cursor - ** is currently pointing to the largest entry in the sub-tree headed - ** by the child-page of the cell that was just deleted from an internal - ** node. The cell from the leaf node needs to be moved to the internal - ** node to replace the deleted cell. */ - if( !pPage->leaf ){ - MemPage *pLeaf = pCur->pPage; - int nCell; - Pgno n; - unsigned char *pTmp; + /* Figure out which opcode is next to display */ + rc = sqlcipher_sqlite3VdbeNextOpcode(p, pSub, p->explain==2, &p->pc, &i, &aOp); - if( pLeaf->nFree<0 ){ - rc = btreeComputeFreeSpace(pLeaf); - if( rc ) return rc; - } - if( iCellDepthiPage-1 ){ - n = pCur->apPage[iCellDepth+1]->pgno; + if( rc==SQLITE_OK ){ + pOp = aOp + i; + if( AtomicLoad(&db->u1.isInterrupted) ){ + p->rc = SQLITE_INTERRUPT; + rc = SQLITE_ERROR; + sqlcipher_sqlite3VdbeError(p, sqlcipher_sqlite3ErrStr(p->rc)); }else{ - n = pCur->pPage->pgno; - } - pCell = findCell(pLeaf, pLeaf->nCell-1); - if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT; - nCell = pLeaf->xCellSize(pLeaf, pCell); - assert( MX_CELL_SIZE(pBt) >= nCell ); - pTmp = pBt->pTmpSpace; - assert( pTmp!=0 ); - rc = sqlcipher_sqlite3PagerWrite(pLeaf->pDbPage); - if( rc==SQLITE_OK ){ - insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); + char *zP4 = sqlcipher_sqlite3VdbeDisplayP4(db, pOp); + if( p->explain==2 ){ + sqlcipher_sqlite3VdbeMemSetInt64(pMem, pOp->p1); + sqlcipher_sqlite3VdbeMemSetInt64(pMem+1, pOp->p2); + sqlcipher_sqlite3VdbeMemSetInt64(pMem+2, pOp->p3); + sqlcipher_sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlcipher_sqlite3_free); + p->nResColumn = 4; + }else{ + sqlcipher_sqlite3VdbeMemSetInt64(pMem+0, i); + sqlcipher_sqlite3VdbeMemSetStr(pMem+1, (char*)sqlcipher_sqlite3OpcodeName(pOp->opcode), + -1, SQLITE_UTF8, SQLITE_STATIC); + sqlcipher_sqlite3VdbeMemSetInt64(pMem+2, pOp->p1); + sqlcipher_sqlite3VdbeMemSetInt64(pMem+3, pOp->p2); + sqlcipher_sqlite3VdbeMemSetInt64(pMem+4, pOp->p3); + /* pMem+5 for p4 is done last */ + sqlcipher_sqlite3VdbeMemSetInt64(pMem+6, pOp->p5); +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + { + char *zCom = sqlcipher_sqlite3VdbeDisplayComment(db, pOp, zP4); + sqlcipher_sqlite3VdbeMemSetStr(pMem+7, zCom, -1, SQLITE_UTF8, sqlcipher_sqlite3_free); + } +#else + sqlcipher_sqlite3VdbeMemSetNull(pMem+7); +#endif + sqlcipher_sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlcipher_sqlite3_free); + p->nResColumn = 8; + } + p->pResultSet = pMem; + if( db->mallocFailed ){ + p->rc = SQLITE_NOMEM; + rc = SQLITE_ERROR; + }else{ + p->rc = SQLITE_OK; + rc = SQLITE_ROW; + } } - dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc); - if( rc ) return rc; } + return rc; +} +#endif /* SQLITE_OMIT_EXPLAIN */ - /* Balance the tree. If the entry deleted was located on a leaf page, - ** then the cursor still points to that page. In this case the first - ** call to balance() repairs the tree, and the if(...) condition is - ** never true. - ** - ** Otherwise, if the entry deleted was on an internal node page, then - ** pCur is pointing to the leaf page from which a cell was removed to - ** replace the cell deleted from the internal node. This is slightly - ** tricky as the leaf node may be underfull, and the internal node may - ** be either under or overfull. In this case run the balancing algorithm - ** on the leaf node first. If the balance proceeds far enough up the - ** tree that we can be sure that any problem in the internal node has - ** been corrected, so be it. Otherwise, after balancing the leaf node, - ** walk the cursor up the tree to the internal node and balance it as - ** well. */ - rc = balance(pCur); - if( rc==SQLITE_OK && pCur->iPage>iCellDepth ){ - releasePageNotNull(pCur->pPage); - pCur->iPage--; - while( pCur->iPage>iCellDepth ){ - releasePage(pCur->apPage[pCur->iPage--]); +#ifdef SQLITE_DEBUG +/* +** Print the SQL that was used to generate a VDBE program. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbePrintSql(Vdbe *p){ + const char *z = 0; + if( p->zSql ){ + z = p->zSql; + }else if( p->nOp>=1 ){ + const VdbeOp *pOp = &p->aOp[0]; + if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){ + z = pOp->p4.z; + while( sqlcipher_sqlite3Isspace(*z) ) z++; } - pCur->pPage = pCur->apPage[pCur->iPage]; - rc = balance(pCur); } + if( z ) printf("SQL: [%s]\n", z); +} +#endif - if( rc==SQLITE_OK ){ - if( bSkipnext ){ - assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) ); - assert( pPage==pCur->pPage || CORRUPT_DB ); - assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell ); - pCur->eState = CURSOR_SKIPNEXT; - if( iCellIdx>=pPage->nCell ){ - pCur->skipNext = -1; - pCur->ix = pPage->nCell-1; +#if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE) +/* +** Print an IOTRACE message showing SQL content. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeIOTraceSql(Vdbe *p){ + int nOp = p->nOp; + VdbeOp *pOp; + if( sqlcipher_sqlite3IoTrace==0 ) return; + if( nOp<1 ) return; + pOp = &p->aOp[0]; + if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){ + int i, j; + char z[1000]; + sqlcipher_sqlite3_snprintf(sizeof(z), z, "%s", pOp->p4.z); + for(i=0; sqlcipher_sqlite3Isspace(z[i]); i++){} + for(j=0; z[i]; i++){ + if( sqlcipher_sqlite3Isspace(z[i]) ){ + if( z[i-1]!=' ' ){ + z[j++] = ' '; + } }else{ - pCur->skipNext = 1; - } - }else{ - rc = moveToRoot(pCur); - if( bPreserve ){ - btreeReleaseAllCursorPages(pCur); - pCur->eState = CURSOR_REQUIRESEEK; + z[j++] = z[i]; } - if( rc==SQLITE_EMPTY ) rc = SQLITE_OK; } + z[j] = 0; + sqlcipher_sqlite3IoTrace("SQL %s\n", z); } - return rc; } +#endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */ -/* -** Create a new BTree table. Write into *piTable the page -** number for the root page of the new table. +/* An instance of this object describes bulk memory available for use +** by subcomponents of a prepared statement. Space is allocated out +** of a ReusableSpace object by the allocSpace() routine below. +*/ +struct ReusableSpace { + u8 *pSpace; /* Available memory */ + sqlcipher_sqlite3_int64 nFree; /* Bytes of available memory */ + sqlcipher_sqlite3_int64 nNeeded; /* Total bytes that could not be allocated */ +}; + +/* Try to allocate nByte bytes of 8-byte aligned bulk memory for pBuf +** from the ReusableSpace object. Return a pointer to the allocated +** memory on success. If insufficient memory is available in the +** ReusableSpace object, increase the ReusableSpace.nNeeded +** value by the amount needed and return NULL. ** -** The type of type is determined by the flags parameter. Only the -** following values of flags are currently in use. Other values for -** flags might not work: +** If pBuf is not initially NULL, that means that the memory has already +** been allocated by a prior call to this routine, so just return a copy +** of pBuf and leave ReusableSpace unchanged. ** -** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys -** BTREE_ZERODATA Used for SQL indices +** This allocator is employed to repurpose unused slots at the end of the +** opcode array of prepared state for other memory needs of the prepared +** statement. */ -static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ - BtShared *pBt = p->pBt; - MemPage *pRoot; - Pgno pgnoRoot; - int rc; - int ptfFlags; /* Page-type flage for the root page of new table */ - - assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); - assert( pBt->inTransaction==TRANS_WRITE ); - assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); - -#ifdef SQLITE_OMIT_AUTOVACUUM - rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); - if( rc ){ - return rc; +static void *allocSpace( + struct ReusableSpace *p, /* Bulk memory available for allocation */ + void *pBuf, /* Pointer to a prior allocation */ + sqlcipher_sqlite3_int64 nByte /* Bytes of memory needed. */ +){ + assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) ); + if( pBuf==0 ){ + nByte = ROUND8P(nByte); + if( nByte <= p->nFree ){ + p->nFree -= nByte; + pBuf = &p->pSpace[p->nFree]; + }else{ + p->nNeeded += nByte; + } } -#else - if( pBt->autoVacuum ){ - Pgno pgnoMove; /* Move a page here to make room for the root-page */ - MemPage *pPageMove; /* The page to move to. */ + assert( EIGHT_BYTE_ALIGNMENT(pBuf) ); + return pBuf; +} - /* Creating a new table may probably require moving an existing database - ** to make room for the new tables root page. In case this page turns - ** out to be an overflow page, delete all overflow page-map caches - ** held by open cursors. - */ - invalidateAllOverflowCache(pBt); +/* +** Rewind the VDBE back to the beginning in preparation for +** running it. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeRewind(Vdbe *p){ +#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) + int i; +#endif + assert( p!=0 ); + assert( p->eVdbeState==VDBE_INIT_STATE + || p->eVdbeState==VDBE_READY_STATE + || p->eVdbeState==VDBE_HALT_STATE ); - /* Read the value of meta[3] from the database to determine where the - ** root page of the new table should go. meta[3] is the largest root-page - ** created so far, so the new root-page is (meta[3]+1). - */ - sqlcipher_sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); - if( pgnoRoot>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; - } - pgnoRoot++; + /* There should be at least one opcode. + */ + assert( p->nOp>0 ); - /* The new root-page may not be allocated on a pointer-map page, or the - ** PENDING_BYTE page. - */ - while( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || - pgnoRoot==PENDING_BYTE_PAGE(pBt) ){ - pgnoRoot++; - } - assert( pgnoRoot>=3 ); + p->eVdbeState = VDBE_READY_STATE; - /* Allocate a page. The page that currently resides at pgnoRoot will - ** be moved to the allocated page (unless the allocated page happens - ** to reside at pgnoRoot). - */ - rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, BTALLOC_EXACT); - if( rc!=SQLITE_OK ){ - return rc; - } +#ifdef SQLITE_DEBUG + for(i=0; inMem; i++){ + assert( p->aMem[i].db==p->db ); + } +#endif + p->pc = -1; + p->rc = SQLITE_OK; + p->errorAction = OE_Abort; + p->nChange = 0; + p->cacheCtr = 1; + p->minWriteFileFormat = 255; + p->iStatement = 0; + p->nFkConstraint = 0; +#ifdef VDBE_PROFILE + for(i=0; inOp; i++){ + p->aOp[i].cnt = 0; + p->aOp[i].cycles = 0; + } +#endif +} - if( pgnoMove!=pgnoRoot ){ - /* pgnoRoot is the page that will be used for the root-page of - ** the new table (assuming an error did not occur). But we were - ** allocated pgnoMove. If required (i.e. if it was not allocated - ** by extending the file), the current page at position pgnoMove - ** is already journaled. - */ - u8 eType = 0; - Pgno iPtrPage = 0; +/* +** Prepare a virtual machine for execution for the first time after +** creating the virtual machine. This involves things such +** as allocating registers and initializing the program counter. +** After the VDBE has be prepped, it can be executed by one or more +** calls to sqlcipher_sqlite3VdbeExec(). +** +** This function may be called exactly once on each virtual machine. +** After this routine is called the VM has been "packaged" and is ready +** to run. After this routine is called, further calls to +** sqlcipher_sqlite3VdbeAddOp() functions are prohibited. This routine disconnects +** the Vdbe from the Parse object that helped generate it so that the +** the Vdbe becomes an independent entity and the Parse object can be +** destroyed. +** +** Use the sqlcipher_sqlite3VdbeRewind() procedure to restore a virtual machine back +** to its initial state after it has been run. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMakeReady( + Vdbe *p, /* The VDBE */ + Parse *pParse /* Parsing context */ +){ + sqlcipher_sqlite3 *db; /* The database connection */ + int nVar; /* Number of parameters */ + int nMem; /* Number of VM memory registers */ + int nCursor; /* Number of cursors required */ + int nArg; /* Number of arguments in subprograms */ + int n; /* Loop counter */ + struct ReusableSpace x; /* Reusable bulk memory */ - /* Save the positions of any open cursors. This is required in - ** case they are holding a reference to an xFetch reference - ** corresponding to page pgnoRoot. */ - rc = saveAllCursors(pBt, 0, 0); - releasePage(pPageMove); - if( rc!=SQLITE_OK ){ - return rc; - } + assert( p!=0 ); + assert( p->nOp>0 ); + assert( pParse!=0 ); + assert( p->eVdbeState==VDBE_INIT_STATE ); + assert( pParse==p->pParse ); + p->pVList = pParse->pVList; + pParse->pVList = 0; + db = p->db; + assert( db->mallocFailed==0 ); + nVar = pParse->nVar; + nMem = pParse->nMem; + nCursor = pParse->nTab; + nArg = pParse->nMaxArg; - /* Move the page currently at pgnoRoot to pgnoMove. */ - rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); - if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ - rc = SQLITE_CORRUPT_BKPT; - } - if( rc!=SQLITE_OK ){ - releasePage(pRoot); - return rc; - } - assert( eType!=PTRMAP_ROOTPAGE ); - assert( eType!=PTRMAP_FREEPAGE ); - rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove, 0); - releasePage(pRoot); + /* Each cursor uses a memory cell. The first cursor (cursor 0) can + ** use aMem[0] which is not otherwise used by the VDBE program. Allocate + ** space at the end of aMem[] for cursors 1 and greater. + ** See also: allocateCursor(). + */ + nMem += nCursor; + if( nCursor==0 && nMem>0 ) nMem++; /* Space for aMem[0] even if not used */ - /* Obtain the page at pgnoRoot */ - if( rc!=SQLITE_OK ){ - return rc; - } - rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = sqlcipher_sqlite3PagerWrite(pRoot->pDbPage); - if( rc!=SQLITE_OK ){ - releasePage(pRoot); - return rc; - } + /* Figure out how much reusable memory is available at the end of the + ** opcode array. This extra memory will be reallocated for other elements + ** of the prepared statement. + */ + n = ROUND8P(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */ + x.pSpace = &((u8*)p->aOp)[n]; /* Unused opcode memory */ + assert( EIGHT_BYTE_ALIGNMENT(x.pSpace) ); + x.nFree = ROUNDDOWN8(pParse->szOpAlloc - n); /* Bytes of unused memory */ + assert( x.nFree>=0 ); + assert( EIGHT_BYTE_ALIGNMENT(&x.pSpace[x.nFree]) ); + + resolveP2Values(p, &nArg); + p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort); + if( pParse->explain ){ + static const char * const azColName[] = { + "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", + "id", "parent", "notused", "detail" + }; + int iFirst, mx, i; + if( nMem<10 ) nMem = 10; + p->explain = pParse->explain; + if( pParse->explain==2 ){ + sqlcipher_sqlite3VdbeSetNumCols(p, 4); + iFirst = 8; + mx = 12; }else{ - pRoot = pPageMove; + sqlcipher_sqlite3VdbeSetNumCols(p, 8); + iFirst = 0; + mx = 8; } - - /* Update the pointer-map and meta-data with the new root-page number. */ - ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0, &rc); - if( rc ){ - releasePage(pRoot); - return rc; + for(i=iFirst; iexpired = 0; - /* When the new root page was allocated, page 1 was made writable in - ** order either to increase the database filesize, or to decrement the - ** freelist count. Hence, the sqlcipher_sqlite3BtreeUpdateMeta() call cannot fail. - */ - assert( sqlcipher_sqlite3PagerIswriteable(pBt->pPage1->pDbPage) ); - rc = sqlcipher_sqlite3BtreeUpdateMeta(p, 4, pgnoRoot); - if( NEVER(rc) ){ - releasePage(pRoot); - return rc; + /* Memory for registers, parameters, cursor, etc, is allocated in one or two + ** passes. On the first pass, we try to reuse unused memory at the + ** end of the opcode array. If we are unable to satisfy all memory + ** requirements by reusing the opcode array tail, then the second + ** pass will fill in the remainder using a fresh memory allocation. + ** + ** This two-pass approach that reuses as much memory as possible from + ** the leftover memory at the end of the opcode array. This can significantly + ** reduce the amount of memory held by a prepared statement. + */ + x.nNeeded = 0; + p->aMem = allocSpace(&x, 0, nMem*sizeof(Mem)); + p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem)); + p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*)); + p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*)); +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + p->anExec = allocSpace(&x, 0, p->nOp*sizeof(i64)); +#endif + if( x.nNeeded ){ + x.pSpace = p->pFree = sqlcipher_sqlite3DbMallocRawNN(db, x.nNeeded); + x.nFree = x.nNeeded; + if( !db->mallocFailed ){ + p->aMem = allocSpace(&x, p->aMem, nMem*sizeof(Mem)); + p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem)); + p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*)); + p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); +#endif } + } + if( db->mallocFailed ){ + p->nVar = 0; + p->nCursor = 0; + p->nMem = 0; }else{ - rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); - if( rc ) return rc; - } + p->nCursor = nCursor; + p->nVar = (ynVar)nVar; + initMemArray(p->aVar, nVar, db, MEM_Null); + p->nMem = nMem; + initMemArray(p->aMem, nMem, db, MEM_Undefined); + memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*)); +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + memset(p->anExec, 0, p->nOp*sizeof(i64)); #endif - assert( sqlcipher_sqlite3PagerIswriteable(pRoot->pDbPage) ); - if( createTabFlags & BTREE_INTKEY ){ - ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF; - }else{ - ptfFlags = PTF_ZERODATA | PTF_LEAF; } - zeroPage(pRoot, ptfFlags); - sqlcipher_sqlite3PagerUnref(pRoot->pDbPage); - assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); - *piTable = pgnoRoot; - return SQLITE_OK; -} -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCreateTable(Btree *p, Pgno *piTable, int flags){ - int rc; - sqlcipher_sqlite3BtreeEnter(p); - rc = btreeCreateTable(p, piTable, flags); - sqlcipher_sqlite3BtreeLeave(p); - return rc; + sqlcipher_sqlite3VdbeRewind(p); } /* -** Erase the given database page and all its children. Return -** the page to the freelist. +** Close a VDBE cursor and release all the resources that cursor +** happens to hold. */ -static int clearDatabasePage( - BtShared *pBt, /* The BTree that contains the table */ - Pgno pgno, /* Page number to clear */ - int freePageFlag, /* Deallocate page if true */ - int *pnChange /* Add number of Cells freed to this counter */ -){ - MemPage *pPage; - int rc; - unsigned char *pCell; - int i; - int hdr; - CellInfo info; - - assert( sqlcipher_sqlite3_mutex_held(pBt->mutex) ); - if( pgno>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; - } - rc = getAndInitPage(pBt, pgno, &pPage, 0, 0); - if( rc ) return rc; - if( pPage->bBusy ){ - rc = SQLITE_CORRUPT_BKPT; - goto cleardatabasepage_out; - } - pPage->bBusy = 1; - hdr = pPage->hdrOffset; - for(i=0; inCell; i++){ - pCell = findCell(pPage, i); - if( !pPage->leaf ){ - rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange); - if( rc ) goto cleardatabasepage_out; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ + if( pCx ) sqlcipher_sqlite3VdbeFreeCursorNN(p,pCx); +} +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeFreeCursorNN(Vdbe *p, VdbeCursor *pCx){ + switch( pCx->eCurType ){ + case CURTYPE_SORTER: { + sqlcipher_sqlite3VdbeSorterClose(p->db, pCx); + break; } - rc = clearCell(pPage, pCell, &info); - if( rc ) goto cleardatabasepage_out; - } - if( !pPage->leaf ){ - rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange); - if( rc ) goto cleardatabasepage_out; - }else if( pnChange ){ - assert( pPage->intKey || CORRUPT_DB ); - testcase( !pPage->intKey ); - *pnChange += pPage->nCell; - } - if( freePageFlag ){ - freePage(pPage, &rc); - }else if( (rc = sqlcipher_sqlite3PagerWrite(pPage->pDbPage))==0 ){ - zeroPage(pPage, pPage->aData[hdr] | PTF_LEAF); + case CURTYPE_BTREE: { + assert( pCx->uc.pCursor!=0 ); + sqlcipher_sqlite3BtreeCloseCursor(pCx->uc.pCursor); + break; + } +#ifndef SQLITE_OMIT_VIRTUALTABLE + case CURTYPE_VTAB: { + sqlcipher_sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur; + const sqlcipher_sqlite3_module *pModule = pVCur->pVtab->pModule; + assert( pVCur->pVtab->nRef>0 ); + pVCur->pVtab->nRef--; + pModule->xClose(pVCur); + break; + } +#endif } - -cleardatabasepage_out: - pPage->bBusy = 0; - releasePage(pPage); - return rc; } /* -** Delete all information from a single table in the database. iTable is -** the page number of the root of the table. After this routine returns, -** the root page is empty, but still exists. -** -** This routine will fail with SQLITE_LOCKED if there are any open -** read cursors on the table. Open write cursors are moved to the -** root of the table. -** -** If pnChange is not NULL, then table iTable must be an intkey table. The -** integer value pointed to by pnChange is incremented by the number of -** entries in the table. +** Close all cursors in the current frame. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){ - int rc; - BtShared *pBt = p->pBt; - sqlcipher_sqlite3BtreeEnter(p); - assert( p->inTrans==TRANS_WRITE ); - - rc = saveAllCursors(pBt, (Pgno)iTable, 0); - - if( SQLITE_OK==rc ){ - /* Invalidate all incrblob cursors open on table iTable (assuming iTable - ** is the root of a table b-tree - if it is not, the following call is - ** a no-op). */ - invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1); - rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); +static void closeCursorsInFrame(Vdbe *p){ + int i; + for(i=0; inCursor; i++){ + VdbeCursor *pC = p->apCsr[i]; + if( pC ){ + sqlcipher_sqlite3VdbeFreeCursorNN(p, pC); + p->apCsr[i] = 0; + } } - sqlcipher_sqlite3BtreeLeave(p); - return rc; } /* -** Delete all information from the single table that pCur is open on. -** -** This routine only work for pCur on an ephemeral table. +** Copy the values stored in the VdbeFrame structure to its Vdbe. This +** is used, for example, when a trigger sub-program is halted to restore +** control to the main program. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeClearTableOfCursor(BtCursor *pCur){ - return sqlcipher_sqlite3BtreeClearTable(pCur->pBtree, pCur->pgnoRoot, 0); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ + Vdbe *v = pFrame->v; + closeCursorsInFrame(v); +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + v->anExec = pFrame->anExec; +#endif + v->aOp = pFrame->aOp; + v->nOp = pFrame->nOp; + v->aMem = pFrame->aMem; + v->nMem = pFrame->nMem; + v->apCsr = pFrame->apCsr; + v->nCursor = pFrame->nCursor; + v->db->lastRowid = pFrame->lastRowid; + v->nChange = pFrame->nChange; + v->db->nChange = pFrame->nDbChange; + sqlcipher_sqlite3VdbeDeleteAuxData(v->db, &v->pAuxData, -1, 0); + v->pAuxData = pFrame->pAuxData; + pFrame->pAuxData = 0; + return pFrame->pc; } /* -** Erase all information in a table and add the root of the table to -** the freelist. Except, the root of the principle table (the one on -** page 1) is never added to the freelist. -** -** This routine will fail with SQLITE_LOCKED if there are any open -** cursors on the table. +** Close all cursors. ** -** If AUTOVACUUM is enabled and the page at iTable is not the last -** root page in the database file, then the last root page -** in the database file is moved into the slot formerly occupied by -** iTable and that last slot formerly occupied by the last root page -** is added to the freelist instead of iTable. In this say, all -** root pages are kept at the beginning of the database file, which -** is necessary for AUTOVACUUM to work right. *piMoved is set to the -** page number that used to be the last root page in the file before -** the move. If no page gets moved, *piMoved is set to 0. -** The last root page is recorded in meta[3] and the value of -** meta[3] is updated by this procedure. +** Also release any dynamic memory held by the VM in the Vdbe.aMem memory +** cell array. This is necessary as the memory cell array may contain +** pointers to VdbeFrame objects, which may in turn contain pointers to +** open cursors. */ -static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ - int rc; - MemPage *pPage = 0; - BtShared *pBt = p->pBt; - - assert( sqlcipher_sqlite3BtreeHoldsMutex(p) ); - assert( p->inTrans==TRANS_WRITE ); - assert( iTable>=2 ); - if( iTable>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; +static void closeAllCursors(Vdbe *p){ + if( p->pFrame ){ + VdbeFrame *pFrame; + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); + sqlcipher_sqlite3VdbeFrameRestore(pFrame); + p->pFrame = 0; + p->nFrame = 0; } - - rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); - if( rc ) return rc; - rc = sqlcipher_sqlite3BtreeClearTable(p, iTable, 0); - if( rc ){ - releasePage(pPage); - return rc; + assert( p->nFrame==0 ); + closeCursorsInFrame(p); + releaseMemArray(p->aMem, p->nMem); + while( p->pDelFrame ){ + VdbeFrame *pDel = p->pDelFrame; + p->pDelFrame = pDel->pParent; + sqlcipher_sqlite3VdbeFrameDelete(pDel); } - *piMoved = 0; - -#ifdef SQLITE_OMIT_AUTOVACUUM - freePage(pPage, &rc); - releasePage(pPage); -#else - if( pBt->autoVacuum ){ - Pgno maxRootPgno; - sqlcipher_sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); - - if( iTable==maxRootPgno ){ - /* If the table being dropped is the table with the largest root-page - ** number in the database, put the root page on the free list. - */ - freePage(pPage, &rc); - releasePage(pPage); - if( rc!=SQLITE_OK ){ - return rc; - } - }else{ - /* The table being dropped does not have the largest root-page - ** number in the database. So move the page that does into the - ** gap left by the deleted root-page. - */ - MemPage *pMove; - releasePage(pPage); - rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0); - releasePage(pMove); - if( rc!=SQLITE_OK ){ - return rc; - } - pMove = 0; - rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); - freePage(pMove, &rc); - releasePage(pMove); - if( rc!=SQLITE_OK ){ - return rc; - } - *piMoved = maxRootPgno; - } - - /* Set the new 'max-root-page' value in the database header. This - ** is the old value less one, less one more if that happens to - ** be a root-page number, less one again if that is the - ** PENDING_BYTE_PAGE. - */ - maxRootPgno--; - while( maxRootPgno==PENDING_BYTE_PAGE(pBt) - || PTRMAP_ISPAGE(pBt, maxRootPgno) ){ - maxRootPgno--; - } - assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) ); - - rc = sqlcipher_sqlite3BtreeUpdateMeta(p, 4, maxRootPgno); - }else{ - freePage(pPage, &rc); - releasePage(pPage); - } -#endif - return rc; -} -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){ - int rc; - sqlcipher_sqlite3BtreeEnter(p); - rc = btreeDropTable(p, iTable, piMoved); - sqlcipher_sqlite3BtreeLeave(p); - return rc; + /* Delete any auxdata allocations made by the VM */ + if( p->pAuxData ) sqlcipher_sqlite3VdbeDeleteAuxData(p->db, &p->pAuxData, -1, 0); + assert( p->pAuxData==0 ); } - /* -** This function may only be called if the b-tree connection already -** has a read or write transaction open on the database. -** -** Read the meta-information out of a database file. Meta[0] -** is the number of free pages currently in the database. Meta[1] -** through meta[15] are available for use by higher layers. Meta[0] -** is read-only, the others are read/write. -** -** The schema layer numbers meta values differently. At the schema -** layer (and the SetCookie and ReadCookie opcodes) the number of -** free pages is not visible. So Cookie[0] is the same as Meta[1]. -** -** This routine treats Meta[BTREE_DATA_VERSION] as a special case. Instead -** of reading the value out of the header, it instead loads the "DataVersion" -** from the pager. The BTREE_DATA_VERSION value is not actually stored in the -** database file. It is a number computed by the pager. But its access -** pattern is the same as header meta values, and so it is convenient to -** read it from this routine. +** Set the number of result columns that will be returned by this SQL +** statement. This is now set at compile time, rather than during +** execution of the vdbe program so that sqlcipher_sqlite3_column_count() can +** be called on an SQL statement before sqlcipher_sqlite3_step(). */ -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ - BtShared *pBt = p->pBt; - - sqlcipher_sqlite3BtreeEnter(p); - assert( p->inTrans>TRANS_NONE ); - assert( SQLITE_OK==querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK) ); - assert( pBt->pPage1 ); - assert( idx>=0 && idx<=15 ); - - if( idx==BTREE_DATA_VERSION ){ - *pMeta = sqlcipher_sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion; - }else{ - *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); - } +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ + int n; + sqlcipher_sqlite3 *db = p->db; - /* If auto-vacuum is disabled in this build and this is an auto-vacuum - ** database, mark the database as read-only. */ -#ifdef SQLITE_OMIT_AUTOVACUUM - if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){ - pBt->btsFlags |= BTS_READ_ONLY; + if( p->nResColumn ){ + releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); + sqlcipher_sqlite3DbFree(db, p->aColName); } -#endif - - sqlcipher_sqlite3BtreeLeave(p); + n = nResColumn*COLNAME_N; + p->nResColumn = (u16)nResColumn; + p->aColName = (Mem*)sqlcipher_sqlite3DbMallocRawNN(db, sizeof(Mem)*n ); + if( p->aColName==0 ) return; + initMemArray(p->aColName, n, db, MEM_Null); } /* -** Write meta-information back into the database. Meta[0] is -** read-only and may not be written. +** Set the name of the idx'th column to be returned by the SQL statement. +** zName must be a pointer to a nul terminated string. +** +** This call must be made after a call to sqlcipher_sqlite3VdbeSetNumCols(). +** +** The final parameter, xDel, must be one of SQLITE_DYNAMIC, SQLITE_STATIC +** or SQLITE_TRANSIENT. If it is SQLITE_DYNAMIC, then the buffer pointed +** to by zName will be freed by sqlcipher_sqlite3DbFree() when the vdbe is destroyed. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){ - BtShared *pBt = p->pBt; - unsigned char *pP1; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSetColName( + Vdbe *p, /* Vdbe being configured */ + int idx, /* Index of column zName applies to */ + int var, /* One of the COLNAME_* constants */ + const char *zName, /* Pointer to buffer containing name */ + void (*xDel)(void*) /* Memory management strategy for zName */ +){ int rc; - assert( idx>=1 && idx<=15 ); - sqlcipher_sqlite3BtreeEnter(p); - assert( p->inTrans==TRANS_WRITE ); - assert( pBt->pPage1!=0 ); - pP1 = pBt->pPage1->aData; - rc = sqlcipher_sqlite3PagerWrite(pBt->pPage1->pDbPage); - if( rc==SQLITE_OK ){ - put4byte(&pP1[36 + idx*4], iMeta); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( idx==BTREE_INCR_VACUUM ){ - assert( pBt->autoVacuum || iMeta==0 ); - assert( iMeta==0 || iMeta==1 ); - pBt->incrVacuum = (u8)iMeta; - } -#endif + Mem *pColName; + assert( idxnResColumn ); + assert( vardb->mallocFailed ){ + assert( !zName || xDel!=SQLITE_DYNAMIC ); + return SQLITE_NOMEM_BKPT; } - sqlcipher_sqlite3BtreeLeave(p); + assert( p->aColName!=0 ); + pColName = &(p->aColName[idx+var*p->nResColumn]); + rc = sqlcipher_sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel); + assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); return rc; } /* -** The first argument, pCur, is a cursor opened on some b-tree. Count the -** number of entries in the b-tree and write the result to *pnEntry. -** -** SQLITE_OK is returned if the operation is successfully executed. -** Otherwise, if an error is encountered (i.e. an IO error or database -** corruption) an SQLite error code is returned. +** A read or write transaction may or may not be active on database handle +** db. If a transaction is active, commit it. If there is a +** write-transaction spanning more than one database file, this routine +** takes care of the super-journal trickery. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCount(sqlcipher_sqlite3 *db, BtCursor *pCur, i64 *pnEntry){ - i64 nEntry = 0; /* Value to return in *pnEntry */ - int rc; /* Return code */ +static int vdbeCommit(sqlcipher_sqlite3 *db, Vdbe *p){ + int i; + int nTrans = 0; /* Number of databases with an active write-transaction + ** that are candidates for a two-phase commit using a + ** super-journal */ + int rc = SQLITE_OK; + int needXcommit = 0; - rc = moveToRoot(pCur); - if( rc==SQLITE_EMPTY ){ - *pnEntry = 0; - return SQLITE_OK; - } +#ifdef SQLITE_OMIT_VIRTUALTABLE + /* With this option, sqlcipher_sqlite3VtabSync() is defined to be simply + ** SQLITE_OK so p is not used. + */ + UNUSED_PARAMETER(p); +#endif - /* Unless an error occurs, the following loop runs one iteration for each - ** page in the B-Tree structure (not including overflow pages). + /* Before doing anything else, call the xSync() callback for any + ** virtual module tables written in this transaction. This has to + ** be done before determining whether a super-journal file is + ** required, as an xSync() callback may add an attached database + ** to the transaction. */ - while( rc==SQLITE_OK && !AtomicLoad(&db->u1.isInterrupted) ){ - int iIdx; /* Index of child node in parent */ - MemPage *pPage; /* Current page of the b-tree */ + rc = sqlcipher_sqlite3VtabSync(db, p); - /* If this is a leaf page or the tree is not an int-key tree, then - ** this page contains countable entries. Increment the entry counter - ** accordingly. - */ - pPage = pCur->pPage; - if( pPage->leaf || !pPage->intKey ){ - nEntry += pPage->nCell; + /* This loop determines (a) if the commit hook should be invoked and + ** (b) how many database files have open write transactions, not + ** including the temp database. (b) is important because if more than + ** one database file has an open write transaction, a super-journal + ** file is required for an atomic commit. + */ + for(i=0; rc==SQLITE_OK && inDb; i++){ + Btree *pBt = db->aDb[i].pBt; + if( sqlcipher_sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){ + /* Whether or not a database might need a super-journal depends upon + ** its journal mode (among other things). This matrix determines which + ** journal modes use a super-journal and which do not */ + static const u8 aMJNeeded[] = { + /* DELETE */ 1, + /* PERSIST */ 1, + /* OFF */ 0, + /* TRUNCATE */ 1, + /* MEMORY */ 0, + /* WAL */ 0 + }; + Pager *pPager; /* Pager associated with pBt */ + needXcommit = 1; + sqlcipher_sqlite3BtreeEnter(pBt); + pPager = sqlcipher_sqlite3BtreePager(pBt); + if( db->aDb[i].safety_level!=PAGER_SYNCHRONOUS_OFF + && aMJNeeded[sqlcipher_sqlite3PagerGetJournalMode(pPager)] + && sqlcipher_sqlite3PagerIsMemdb(pPager)==0 + ){ + assert( i!=1 ); + nTrans++; + } + rc = sqlcipher_sqlite3PagerExclusiveLock(pPager); + sqlcipher_sqlite3BtreeLeave(pBt); } + } + if( rc!=SQLITE_OK ){ + return rc; + } - /* pPage is a leaf node. This loop navigates the cursor so that it - ** points to the first interior cell that it points to the parent of - ** the next page in the tree that has not yet been visited. The - ** pCur->aiIdx[pCur->iPage] value is set to the index of the parent cell - ** of the page, or to the number of cells in the page if the next page - ** to visit is the right-child of its parent. - ** - ** If all pages in the tree have been visited, return SQLITE_OK to the - ** caller. - */ - if( pPage->leaf ){ - do { - if( pCur->iPage==0 ){ - /* All pages of the b-tree have been visited. Return successfully. */ - *pnEntry = nEntry; - return moveToRoot(pCur); - } - moveToParent(pCur); - }while ( pCur->ix>=pCur->pPage->nCell ); + /* If there are any write-transactions at all, invoke the commit hook */ + if( needXcommit && db->xCommitCallback ){ + rc = db->xCommitCallback(db->pCommitArg); + if( rc ){ + return SQLITE_CONSTRAINT_COMMITHOOK; + } + } - pCur->ix++; - pPage = pCur->pPage; + /* The simple case - no more than one database file (not counting the + ** TEMP database) has a transaction active. There is no need for the + ** super-journal. + ** + ** If the return value of sqlcipher_sqlite3BtreeGetFilename() is a zero length + ** string, it means the main database is :memory: or a temp file. In + ** that case we do not support atomic multi-file commits, so use the + ** simple case then too. + */ + if( 0==sqlcipher_sqlite3Strlen30(sqlcipher_sqlite3BtreeGetFilename(db->aDb[0].pBt)) + || nTrans<=1 + ){ + for(i=0; rc==SQLITE_OK && inDb; i++){ + Btree *pBt = db->aDb[i].pBt; + if( pBt ){ + rc = sqlcipher_sqlite3BtreeCommitPhaseOne(pBt, 0); + } } - /* Descend to the child node of the cell that the cursor currently - ** points at. This is the right-child if (iIdx==pPage->nCell). + /* Do the commit only if all databases successfully complete phase 1. + ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an + ** IO error while deleting or truncating a journal file. It is unlikely, + ** but could happen. In this case abandon processing and return the error. */ - iIdx = pCur->ix; - if( iIdx==pPage->nCell ){ - rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); - }else{ - rc = moveToChild(pCur, get4byte(findCell(pPage, iIdx))); + for(i=0; rc==SQLITE_OK && inDb; i++){ + Btree *pBt = db->aDb[i].pBt; + if( pBt ){ + rc = sqlcipher_sqlite3BtreeCommitPhaseTwo(pBt, 0); + } + } + if( rc==SQLITE_OK ){ + sqlcipher_sqlite3VtabCommit(db); } } - /* An error has occurred. Return an error code. */ - return rc; -} + /* The complex case - There is a multi-file write-transaction active. + ** This requires a super-journal file to ensure the transaction is + ** committed atomically. + */ +#ifndef SQLITE_OMIT_DISKIO + else{ + sqlcipher_sqlite3_vfs *pVfs = db->pVfs; + char *zSuper = 0; /* File-name for the super-journal */ + char const *zMainFile = sqlcipher_sqlite3BtreeGetFilename(db->aDb[0].pBt); + sqlcipher_sqlite3_file *pSuperJrnl = 0; + i64 offset = 0; + int res; + int retryCount = 0; + int nMainFile; -/* -** Return the pager associated with a BTree. This routine is used for -** testing and debugging only. -*/ -SQLITE_PRIVATE Pager *sqlcipher_sqlite3BtreePager(Btree *p){ - return p->pBt->pPager; -} + /* Select a super-journal file name */ + nMainFile = sqlcipher_sqlite3Strlen30(zMainFile); + zSuper = sqlcipher_sqlite3MPrintf(db, "%.4c%s%.16c", 0,zMainFile,0); + if( zSuper==0 ) return SQLITE_NOMEM_BKPT; + zSuper += 4; + do { + u32 iRandom; + if( retryCount ){ + if( retryCount>100 ){ + sqlcipher_sqlite3_log(SQLITE_FULL, "MJ delete: %s", zSuper); + sqlcipher_sqlite3OsDelete(pVfs, zSuper, 0); + break; + }else if( retryCount==1 ){ + sqlcipher_sqlite3_log(SQLITE_FULL, "MJ collide: %s", zSuper); + } + } + retryCount++; + sqlcipher_sqlite3_randomness(sizeof(iRandom), &iRandom); + sqlcipher_sqlite3_snprintf(13, &zSuper[nMainFile], "-mj%06X9%02X", + (iRandom>>8)&0xffffff, iRandom&0xff); + /* The antipenultimate character of the super-journal name must + ** be "9" to avoid name collisions when using 8+3 filenames. */ + assert( zSuper[sqlcipher_sqlite3Strlen30(zSuper)-3]=='9' ); + sqlcipher_sqlite3FileSuffix3(zMainFile, zSuper); + rc = sqlcipher_sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res); + }while( rc==SQLITE_OK && res ); + if( rc==SQLITE_OK ){ + /* Open the super-journal. */ + rc = sqlcipher_sqlite3OsOpenMalloc(pVfs, zSuper, &pSuperJrnl, + SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| + SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_SUPER_JOURNAL, 0 + ); + } + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3DbFree(db, zSuper-4); + return rc; + } -#ifndef SQLITE_OMIT_INTEGRITY_CHECK -/* -** Append a message to the error message string. -*/ -static void checkAppendMsg( - IntegrityCk *pCheck, - const char *zFormat, - ... -){ - va_list ap; - if( !pCheck->mxErr ) return; - pCheck->mxErr--; - pCheck->nErr++; - va_start(ap, zFormat); - if( pCheck->errMsg.nChar ){ - sqlcipher_sqlite3_str_append(&pCheck->errMsg, "\n", 1); - } - if( pCheck->zPfx ){ - sqlcipher_sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2); - } - sqlcipher_sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap); - va_end(ap); - if( pCheck->errMsg.accError==SQLITE_NOMEM ){ - pCheck->bOomFault = 1; - } -} -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ + /* Write the name of each database file in the transaction into the new + ** super-journal file. If an error occurs at this point close + ** and delete the super-journal file. All the individual journal files + ** still have 'null' as the super-journal pointer, so they will roll + ** back independently if a failure occurs. + */ + for(i=0; inDb; i++){ + Btree *pBt = db->aDb[i].pBt; + if( sqlcipher_sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){ + char const *zFile = sqlcipher_sqlite3BtreeGetJournalname(pBt); + if( zFile==0 ){ + continue; /* Ignore TEMP and :memory: databases */ + } + assert( zFile[0]!=0 ); + rc = sqlcipher_sqlite3OsWrite(pSuperJrnl, zFile, sqlcipher_sqlite3Strlen30(zFile)+1,offset); + offset += sqlcipher_sqlite3Strlen30(zFile)+1; + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3OsCloseFree(pSuperJrnl); + sqlcipher_sqlite3OsDelete(pVfs, zSuper, 0); + sqlcipher_sqlite3DbFree(db, zSuper-4); + return rc; + } + } + } -#ifndef SQLITE_OMIT_INTEGRITY_CHECK + /* Sync the super-journal file. If the IOCAP_SEQUENTIAL device + ** flag is set this is not required. + */ + if( 0==(sqlcipher_sqlite3OsDeviceCharacteristics(pSuperJrnl)&SQLITE_IOCAP_SEQUENTIAL) + && SQLITE_OK!=(rc = sqlcipher_sqlite3OsSync(pSuperJrnl, SQLITE_SYNC_NORMAL)) + ){ + sqlcipher_sqlite3OsCloseFree(pSuperJrnl); + sqlcipher_sqlite3OsDelete(pVfs, zSuper, 0); + sqlcipher_sqlite3DbFree(db, zSuper-4); + return rc; + } -/* -** Return non-zero if the bit in the IntegrityCk.aPgRef[] array that -** corresponds to page iPg is already set. -*/ -static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){ - assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); - return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07))); -} + /* Sync all the db files involved in the transaction. The same call + ** sets the super-journal pointer in each individual journal. If + ** an error occurs here, do not delete the super-journal file. + ** + ** If the error occurs during the first call to + ** sqlcipher_sqlite3BtreeCommitPhaseOne(), then there is a chance that the + ** super-journal file will be orphaned. But we cannot delete it, + ** in case the super-journal file name was written into the journal + ** file before the failure occurred. + */ + for(i=0; rc==SQLITE_OK && inDb; i++){ + Btree *pBt = db->aDb[i].pBt; + if( pBt ){ + rc = sqlcipher_sqlite3BtreeCommitPhaseOne(pBt, zSuper); + } + } + sqlcipher_sqlite3OsCloseFree(pSuperJrnl); + assert( rc!=SQLITE_BUSY ); + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3DbFree(db, zSuper-4); + return rc; + } -/* -** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg. -*/ -static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){ - assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); - pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07)); -} + /* Delete the super-journal file. This commits the transaction. After + ** doing this the directory is synced again before any individual + ** transaction files are deleted. + */ + rc = sqlcipher_sqlite3OsDelete(pVfs, zSuper, 1); + sqlcipher_sqlite3DbFree(db, zSuper-4); + zSuper = 0; + if( rc ){ + return rc; + } + /* All files and directories have already been synced, so the following + ** calls to sqlcipher_sqlite3BtreeCommitPhaseTwo() are only closing files and + ** deleting or truncating journals. If something goes wrong while + ** this is happening we don't really care. The integrity of the + ** transaction is already guaranteed, but some stray 'cold' journals + ** may be lying around. Returning an error code won't help matters. + */ + disable_simulated_io_errors(); + sqlcipher_sqlite3BeginBenignMalloc(); + for(i=0; inDb; i++){ + Btree *pBt = db->aDb[i].pBt; + if( pBt ){ + sqlcipher_sqlite3BtreeCommitPhaseTwo(pBt, 1); + } + } + sqlcipher_sqlite3EndBenignMalloc(); + enable_simulated_io_errors(); -/* -** Add 1 to the reference count for page iPage. If this is the second -** reference to the page, add an error message to pCheck->zErrMsg. -** Return 1 if there are 2 or more references to the page and 0 if -** if this is the first reference to the page. -** -** Also check that the page number is in bounds. -*/ -static int checkRef(IntegrityCk *pCheck, Pgno iPage){ - if( iPage>pCheck->nPage || iPage==0 ){ - checkAppendMsg(pCheck, "invalid page number %d", iPage); - return 1; - } - if( getPageReferenced(pCheck, iPage) ){ - checkAppendMsg(pCheck, "2nd reference to page %d", iPage); - return 1; + sqlcipher_sqlite3VtabCommit(db); } - if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1; - setPageReferenced(pCheck, iPage); - return 0; +#endif + + return rc; } -#ifndef SQLITE_OMIT_AUTOVACUUM /* -** Check that the entry in the pointer-map for page iChild maps to -** page iParent, pointer type ptrType. If not, append an error message -** to pCheck. +** This routine checks that the sqlcipher_sqlite3.nVdbeActive count variable +** matches the number of vdbe's in the list sqlcipher_sqlite3.pVdbe that are +** currently active. An assertion fails if the two counts do not match. +** This is an internal self-check only - it is not an essential processing +** step. +** +** This is a no-op if NDEBUG is defined. */ -static void checkPtrmap( - IntegrityCk *pCheck, /* Integrity check context */ - Pgno iChild, /* Child page number */ - u8 eType, /* Expected pointer map type */ - Pgno iParent /* Expected pointer map parent page number */ -){ - int rc; - u8 ePtrmapType; - Pgno iPtrmapParent; - - rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->bOomFault = 1; - checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild); - return; - } - - if( ePtrmapType!=eType || iPtrmapParent!=iParent ){ - checkAppendMsg(pCheck, - "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", - iChild, eType, iParent, ePtrmapType, iPtrmapParent); +#ifndef NDEBUG +static void checkActiveVdbeCnt(sqlcipher_sqlite3 *db){ + Vdbe *p; + int cnt = 0; + int nWrite = 0; + int nRead = 0; + p = db->pVdbe; + while( p ){ + if( sqlcipher_sqlite3_stmt_busy((sqlcipher_sqlite3_stmt*)p) ){ + cnt++; + if( p->readOnly==0 ) nWrite++; + if( p->bIsReader ) nRead++; + } + p = p->pNext; } + assert( cnt==db->nVdbeActive ); + assert( nWrite==db->nVdbeWrite ); + assert( nRead==db->nVdbeRead ); } +#else +#define checkActiveVdbeCnt(x) #endif /* -** Check the integrity of the freelist or of an overflow page list. -** Verify that the number of pages on the list is N. +** If the Vdbe passed as the first argument opened a statement-transaction, +** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or +** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement +** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the +** statement transaction is committed. +** +** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned. +** Otherwise SQLITE_OK. */ -static void checkList( - IntegrityCk *pCheck, /* Integrity checking context */ - int isFreeList, /* True for a freelist. False for overflow page list */ - Pgno iPage, /* Page number for first page in the list */ - u32 N /* Expected number of pages in the list */ -){ +static SQLITE_NOINLINE int vdbeCloseStatement(Vdbe *p, int eOp){ + sqlcipher_sqlite3 *const db = p->db; + int rc = SQLITE_OK; int i; - u32 expected = N; - int nErrAtStart = pCheck->nErr; - while( iPage!=0 && pCheck->mxErr ){ - DbPage *pOvflPage; - unsigned char *pOvflData; - if( checkRef(pCheck, iPage) ) break; - N--; - if( sqlcipher_sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage, 0) ){ - checkAppendMsg(pCheck, "failed to get page %d", iPage); - break; - } - pOvflData = (unsigned char *)sqlcipher_sqlite3PagerGetData(pOvflPage); - if( isFreeList ){ - u32 n = (u32)get4byte(&pOvflData[4]); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pCheck->pBt->autoVacuum ){ - checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0); + const int iSavepoint = p->iStatement-1; + + assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE); + assert( db->nStatement>0 ); + assert( p->iStatement==(db->nStatement+db->nSavepoint) ); + + for(i=0; inDb; i++){ + int rc2 = SQLITE_OK; + Btree *pBt = db->aDb[i].pBt; + if( pBt ){ + if( eOp==SAVEPOINT_ROLLBACK ){ + rc2 = sqlcipher_sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint); } -#endif - if( n>pCheck->pBt->usableSize/4-2 ){ - checkAppendMsg(pCheck, - "freelist leaf count too big on page %d", iPage); - N--; - }else{ - for(i=0; i<(int)n; i++){ - Pgno iFreePage = get4byte(&pOvflData[8+i*4]); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pCheck->pBt->autoVacuum ){ - checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0); - } -#endif - checkRef(pCheck, iFreePage); - } - N -= n; + if( rc2==SQLITE_OK ){ + rc2 = sqlcipher_sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint); } - } -#ifndef SQLITE_OMIT_AUTOVACUUM - else{ - /* If this database supports auto-vacuum and iPage is not the last - ** page in this overflow list, check that the pointer-map entry for - ** the following page matches iPage. - */ - if( pCheck->pBt->autoVacuum && N>0 ){ - i = get4byte(pOvflData); - checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage); + if( rc==SQLITE_OK ){ + rc = rc2; } } -#endif - iPage = get4byte(pOvflData); - sqlcipher_sqlite3PagerUnref(pOvflPage); } - if( N && nErrAtStart==pCheck->nErr ){ - checkAppendMsg(pCheck, - "%s is %d but should be %d", - isFreeList ? "size" : "overflow list length", - expected-N, expected); + db->nStatement--; + p->iStatement = 0; + + if( rc==SQLITE_OK ){ + if( eOp==SAVEPOINT_ROLLBACK ){ + rc = sqlcipher_sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint); + } + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint); + } + } + + /* If the statement transaction is being rolled back, also restore the + ** database handles deferred constraint counter to the value it had when + ** the statement transaction was opened. */ + if( eOp==SAVEPOINT_ROLLBACK ){ + db->nDeferredCons = p->nStmtDefCons; + db->nDeferredImmCons = p->nStmtDefImmCons; } + return rc; } -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ + if( p->db->nStatement && p->iStatement ){ + return vdbeCloseStatement(p, eOp); + } + return SQLITE_OK; +} + /* -** An implementation of a min-heap. -** -** aHeap[0] is the number of elements on the heap. aHeap[1] is the -** root element. The daughter nodes of aHeap[N] are aHeap[N*2] -** and aHeap[N*2+1]. -** -** The heap property is this: Every node is less than or equal to both -** of its daughter nodes. A consequence of the heap property is that the -** root node aHeap[1] is always the minimum value currently in the heap. -** -** The btreeHeapInsert() routine inserts an unsigned 32-bit number onto -** the heap, preserving the heap property. The btreeHeapPull() routine -** removes the root element from the heap (the minimum value in the heap) -** and then moves other nodes around as necessary to preserve the heap -** property. +** This function is called when a transaction opened by the database +** handle associated with the VM passed as an argument is about to be +** committed. If there are outstanding deferred foreign key constraint +** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK. ** -** This heap is used for cell overlap and coverage testing. Each u32 -** entry represents the span of a cell or freeblock on a btree page. -** The upper 16 bits are the index of the first byte of a range and the -** lower 16 bits are the index of the last byte of that range. +** If there are outstanding FK violations and this function returns +** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY +** and write an error message to it. Then return SQLITE_ERROR. */ -static void btreeHeapInsert(u32 *aHeap, u32 x){ - u32 j, i = ++aHeap[0]; - aHeap[i] = x; - while( (j = i/2)>0 && aHeap[j]>aHeap[i] ){ - x = aHeap[j]; - aHeap[j] = aHeap[i]; - aHeap[i] = x; - i = j; - } -} -static int btreeHeapPull(u32 *aHeap, u32 *pOut){ - u32 j, i, x; - if( (x = aHeap[0])==0 ) return 0; - *pOut = aHeap[1]; - aHeap[1] = aHeap[x]; - aHeap[x] = 0xffffffff; - aHeap[0]--; - i = 1; - while( (j = i*2)<=aHeap[0] ){ - if( aHeap[j]>aHeap[j+1] ) j++; - if( aHeap[i]db; + if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0) + || (!deferred && p->nFkConstraint>0) + ){ + p->rc = SQLITE_CONSTRAINT_FOREIGNKEY; + p->errorAction = OE_Abort; + sqlcipher_sqlite3VdbeError(p, "FOREIGN KEY constraint failed"); + if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR; + return SQLITE_CONSTRAINT_FOREIGNKEY; } - return 1; + return SQLITE_OK; } +#endif -#ifndef SQLITE_OMIT_INTEGRITY_CHECK /* -** Do various sanity checks on a single page of a tree. Return -** the tree depth. Root pages return 0. Parents of root pages -** return 1, and so forth. +** This routine is called the when a VDBE tries to halt. If the VDBE +** has made changes and is in autocommit mode, then commit those +** changes. If a rollback is needed, then do the rollback. ** -** These checks are done: +** This routine is the only way to move the sqlcipher_sqlite3eOpenState of a VM from +** SQLITE_STATE_RUN to SQLITE_STATE_HALT. It is harmless to +** call this on a VM that is in the SQLITE_STATE_HALT state. ** -** 1. Make sure that cells and freeblocks do not overlap -** but combine to completely cover the page. -** 2. Make sure integer cell keys are in order. -** 3. Check the integrity of overflow pages. -** 4. Recursively call checkTreePage on all children. -** 5. Verify that the depth of all children is the same. +** Return an error code. If the commit could not complete because of +** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it +** means the close did not happen and needs to be repeated. */ -static int checkTreePage( - IntegrityCk *pCheck, /* Context for the sanity check */ - Pgno iPage, /* Page number of the page to check */ - i64 *piMinKey, /* Write minimum integer primary key here */ - i64 maxKey /* Error if integer primary key greater than this */ -){ - MemPage *pPage = 0; /* The page being analyzed */ - int i; /* Loop counter */ - int rc; /* Result code from subroutine call */ - int depth = -1, d2; /* Depth of a subtree */ - int pgno; /* Page number */ - int nFrag; /* Number of fragmented bytes on the page */ - int hdr; /* Offset to the page header */ - int cellStart; /* Offset to the start of the cell pointer array */ - int nCell; /* Number of cells */ - int doCoverageCheck = 1; /* True if cell coverage checking should be done */ - int keyCanBeEqual = 1; /* True if IPK can be equal to maxKey - ** False if IPK must be strictly less than maxKey */ - u8 *data; /* Page content */ - u8 *pCell; /* Cell content */ - u8 *pCellIdx; /* Next element of the cell pointer array */ - BtShared *pBt; /* The BtShared object that owns pPage */ - u32 pc; /* Address of a cell */ - u32 usableSize; /* Usable size of the page */ - u32 contentOffset; /* Offset to the start of the cell content area */ - u32 *heap = 0; /* Min-heap used for checking cell coverage */ - u32 x, prev = 0; /* Next and previous entry on the min-heap */ - const char *saved_zPfx = pCheck->zPfx; - int saved_v1 = pCheck->v1; - int saved_v2 = pCheck->v2; - u8 savedIsInit = 0; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeHalt(Vdbe *p){ + int rc; /* Used to store transient return codes */ + sqlcipher_sqlite3 *db = p->db; - /* Check that the page exists + /* This function contains the logic that determines if a statement or + ** transaction will be committed or rolled back as a result of the + ** execution of this virtual machine. + ** + ** If any of the following errors occur: + ** + ** SQLITE_NOMEM + ** SQLITE_IOERR + ** SQLITE_FULL + ** SQLITE_INTERRUPT + ** + ** Then the internal cache might have been left in an inconsistent + ** state. We need to rollback the statement transaction, if there is + ** one, or the complete transaction if there is no statement transaction. */ - pBt = pCheck->pBt; - usableSize = pBt->usableSize; - if( iPage==0 ) return 0; - if( checkRef(pCheck, iPage) ) return 0; - pCheck->zPfx = "Page %u: "; - pCheck->v1 = iPage; - if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){ - checkAppendMsg(pCheck, - "unable to get the page. error code=%d", rc); - goto end_of_check; - } - /* Clear MemPage.isInit to make sure the corruption detection code in - ** btreeInitPage() is executed. */ - savedIsInit = pPage->isInit; - pPage->isInit = 0; - if( (rc = btreeInitPage(pPage))!=0 ){ - assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */ - checkAppendMsg(pCheck, - "btreeInitPage() returns error code %d", rc); - goto end_of_check; - } - if( (rc = btreeComputeFreeSpace(pPage))!=0 ){ - assert( rc==SQLITE_CORRUPT ); - checkAppendMsg(pCheck, "free space corruption", rc); - goto end_of_check; + assert( p->eVdbeState==VDBE_RUN_STATE ); + if( db->mallocFailed ){ + p->rc = SQLITE_NOMEM_BKPT; } - data = pPage->aData; - hdr = pPage->hdrOffset; - - /* Set up for cell analysis */ - pCheck->zPfx = "On tree page %u cell %d: "; - contentOffset = get2byteNotZero(&data[hdr+5]); - assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ - - /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the - ** number of cells on the page. */ - nCell = get2byte(&data[hdr+3]); - assert( pPage->nCell==nCell ); - - /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page - ** immediately follows the b-tree page header. */ - cellStart = hdr + 12 - 4*pPage->leaf; - assert( pPage->aCellIdx==&data[cellStart] ); - pCellIdx = &data[cellStart + 2*(nCell-1)]; + closeAllCursors(p); + checkActiveVdbeCnt(db); - if( !pPage->leaf ){ - /* Analyze the right-child page of internal pages */ - pgno = get4byte(&data[hdr+8]); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - pCheck->zPfx = "On page %u at right child: "; - checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage); - } -#endif - depth = checkTreePage(pCheck, pgno, &maxKey, maxKey); - keyCanBeEqual = 0; - }else{ - /* For leaf pages, the coverage check will occur in the same loop - ** as the other cell checks, so initialize the heap. */ - heap = pCheck->heap; - heap[0] = 0; - } + /* No commit or rollback needed if the program never started or if the + ** SQL statement does not read or write a database file. */ + if( p->bIsReader ){ + int mrc; /* Primary error code from p->rc */ + int eStatementOp = 0; + int isSpecialError; /* Set to true if a 'special' error */ - /* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte - ** integer offsets to the cell contents. */ - for(i=nCell-1; i>=0 && pCheck->mxErr; i--){ - CellInfo info; + /* Lock all btrees used by the statement */ + sqlcipher_sqlite3VdbeEnter(p); - /* Check cell size */ - pCheck->v2 = i; - assert( pCellIdx==&data[cellStart + i*2] ); - pc = get2byteAligned(pCellIdx); - pCellIdx -= 2; - if( pcusableSize-4 ){ - checkAppendMsg(pCheck, "Offset %d out of range %d..%d", - pc, contentOffset, usableSize-4); - doCoverageCheck = 0; - continue; - } - pCell = &data[pc]; - pPage->xParseCell(pPage, pCell, &info); - if( pc+info.nSize>usableSize ){ - checkAppendMsg(pCheck, "Extends off end of page"); - doCoverageCheck = 0; - continue; + /* Check for one of the special errors */ + if( p->rc ){ + mrc = p->rc & 0xff; + isSpecialError = mrc==SQLITE_NOMEM + || mrc==SQLITE_IOERR + || mrc==SQLITE_INTERRUPT + || mrc==SQLITE_FULL; + }else{ + mrc = isSpecialError = 0; } - - /* Check for integer primary key out of range */ - if( pPage->intKey ){ - if( keyCanBeEqual ? (info.nKey > maxKey) : (info.nKey >= maxKey) ){ - checkAppendMsg(pCheck, "Rowid %lld out of order", info.nKey); + if( isSpecialError ){ + /* If the query was read-only and the error code is SQLITE_INTERRUPT, + ** no rollback is necessary. Otherwise, at least a savepoint + ** transaction must be rolled back to restore the database to a + ** consistent state. + ** + ** Even if the statement is read-only, it is important to perform + ** a statement or transaction rollback operation. If the error + ** occurred while writing to the journal, sub-journal or database + ** file as part of an effort to free up cache space (see function + ** pagerStress() in pager.c), the rollback is required to restore + ** the pager to a consistent state. + */ + if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){ + if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){ + eStatementOp = SAVEPOINT_ROLLBACK; + }else{ + /* We are forced to roll back the active transaction. Before doing + ** so, abort any other statements this handle currently has active. + */ + sqlcipher_sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); + sqlcipher_sqlite3CloseSavepoints(db); + db->autoCommit = 1; + p->nChange = 0; + } } - maxKey = info.nKey; - keyCanBeEqual = 0; /* Only the first key on the page may ==maxKey */ } - /* Check the content overflow list */ - if( info.nPayload>info.nLocal ){ - u32 nPage; /* Number of pages on the overflow chain */ - Pgno pgnoOvfl; /* First page of the overflow chain */ - assert( pc + info.nSize - 4 <= usableSize ); - nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4); - pgnoOvfl = get4byte(&pCell[info.nSize - 4]); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage); - } -#endif - checkList(pCheck, 0, pgnoOvfl, nPage); + /* Check for immediate foreign key violations. */ + if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ + sqlcipher_sqlite3VdbeCheckFk(p, 0); } - if( !pPage->leaf ){ - /* Check sanity of left child page for internal pages */ - pgno = get4byte(pCell); -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage); + /* If the auto-commit flag is set and this is the only active writer + ** VM, then we do either a commit or rollback of the current transaction. + ** + ** Note: This block also runs if one of the special errors handled + ** above has occurred. + */ + if( !sqlcipher_sqlite3VtabInSync(db) + && db->autoCommit + && db->nVdbeWrite==(p->readOnly==0) + ){ + if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ + rc = sqlcipher_sqlite3VdbeCheckFk(p, 1); + if( rc!=SQLITE_OK ){ + if( NEVER(p->readOnly) ){ + sqlcipher_sqlite3VdbeLeave(p); + return SQLITE_ERROR; + } + rc = SQLITE_CONSTRAINT_FOREIGNKEY; + }else if( db->flags & SQLITE_CorruptRdOnly ){ + rc = SQLITE_CORRUPT; + db->flags &= ~SQLITE_CorruptRdOnly; + }else{ + /* The auto-commit flag is true, the vdbe program was successful + ** or hit an 'OR FAIL' constraint and there are no deferred foreign + ** key constraints to hold up the transaction. This means a commit + ** is required. */ + rc = vdbeCommit(db, p); + } + if( rc==SQLITE_BUSY && p->readOnly ){ + sqlcipher_sqlite3VdbeLeave(p); + return SQLITE_BUSY; + }else if( rc!=SQLITE_OK ){ + p->rc = rc; + sqlcipher_sqlite3RollbackAll(db, SQLITE_OK); + p->nChange = 0; + }else{ + db->nDeferredCons = 0; + db->nDeferredImmCons = 0; + db->flags &= ~(u64)SQLITE_DeferFKs; + sqlcipher_sqlite3CommitInternalChanges(db); + } + }else{ + sqlcipher_sqlite3RollbackAll(db, SQLITE_OK); + p->nChange = 0; } -#endif - d2 = checkTreePage(pCheck, pgno, &maxKey, maxKey); - keyCanBeEqual = 0; - if( d2!=depth ){ - checkAppendMsg(pCheck, "Child page depth differs"); - depth = d2; + db->nStatement = 0; + }else if( eStatementOp==0 ){ + if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ + eStatementOp = SAVEPOINT_RELEASE; + }else if( p->errorAction==OE_Abort ){ + eStatementOp = SAVEPOINT_ROLLBACK; + }else{ + sqlcipher_sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); + sqlcipher_sqlite3CloseSavepoints(db); + db->autoCommit = 1; + p->nChange = 0; } - }else{ - /* Populate the coverage-checking heap for leaf pages */ - btreeHeapInsert(heap, (pc<<16)|(pc+info.nSize-1)); } - } - *piMinKey = maxKey; - /* Check for complete coverage of the page - */ - pCheck->zPfx = 0; - if( doCoverageCheck && pCheck->mxErr>0 ){ - /* For leaf pages, the min-heap has already been initialized and the - ** cells have already been inserted. But for internal pages, that has - ** not yet been done, so do it now */ - if( !pPage->leaf ){ - heap = pCheck->heap; - heap[0] = 0; - for(i=nCell-1; i>=0; i--){ - u32 size; - pc = get2byteAligned(&data[cellStart+i*2]); - size = pPage->xCellSize(pPage, &data[pc]); - btreeHeapInsert(heap, (pc<<16)|(pc+size-1)); - } - } - /* Add the freeblocks to the min-heap - ** - ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header - ** is the offset of the first freeblock, or zero if there are no - ** freeblocks on the page. + /* If eStatementOp is non-zero, then a statement transaction needs to + ** be committed or rolled back. Call sqlcipher_sqlite3VdbeCloseStatement() to + ** do so. If this operation returns an error, and the current statement + ** error code is SQLITE_OK or SQLITE_CONSTRAINT, then promote the + ** current statement error code. */ - i = get2byte(&data[hdr+1]); - while( i>0 ){ - int size, j; - assert( (u32)i<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */ - size = get2byte(&data[i+2]); - assert( (u32)(i+size)<=usableSize ); /* due to btreeComputeFreeSpace() */ - btreeHeapInsert(heap, (((u32)i)<<16)|(i+size-1)); - /* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a - ** big-endian integer which is the offset in the b-tree page of the next - ** freeblock in the chain, or zero if the freeblock is the last on the - ** chain. */ - j = get2byte(&data[i]); - /* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of - ** increasing offset. */ - assert( j==0 || j>i+size ); /* Enforced by btreeComputeFreeSpace() */ - assert( (u32)j<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */ - i = j; + if( eStatementOp ){ + rc = sqlcipher_sqlite3VdbeCloseStatement(p, eStatementOp); + if( rc ){ + if( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT ){ + p->rc = rc; + sqlcipher_sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = 0; + } + sqlcipher_sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); + sqlcipher_sqlite3CloseSavepoints(db); + db->autoCommit = 1; + p->nChange = 0; + } } - /* Analyze the min-heap looking for overlap between cells and/or - ** freeblocks, and counting the number of untracked bytes in nFrag. - ** - ** Each min-heap entry is of the form: (start_address<<16)|end_address. - ** There is an implied first entry the covers the page header, the cell - ** pointer index, and the gap between the cell pointer index and the start - ** of cell content. - ** - ** The loop below pulls entries from the min-heap in order and compares - ** the start_address against the previous end_address. If there is an - ** overlap, that means bytes are used multiple times. If there is a gap, - ** that gap is added to the fragmentation count. + + /* If this was an INSERT, UPDATE or DELETE and no statement transaction + ** has been rolled back, update the database connection change-counter. */ - nFrag = 0; - prev = contentOffset - 1; /* Implied first min-heap entry */ - while( btreeHeapPull(heap,&x) ){ - if( (prev&0xffff)>=(x>>16) ){ - checkAppendMsg(pCheck, - "Multiple uses for byte %u of page %u", x>>16, iPage); - break; + if( p->changeCntOn ){ + if( eStatementOp!=SAVEPOINT_ROLLBACK ){ + sqlcipher_sqlite3VdbeSetChanges(db, p->nChange); }else{ - nFrag += (x>>16) - (prev&0xffff) - 1; - prev = x; + sqlcipher_sqlite3VdbeSetChanges(db, 0); } + p->nChange = 0; } - nFrag += usableSize - (prev&0xffff) - 1; - /* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments - ** is stored in the fifth field of the b-tree page header. - ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the - ** number of fragmented free bytes within the cell content area. - */ - if( heap[0]==0 && nFrag!=data[hdr+7] ){ - checkAppendMsg(pCheck, - "Fragmentation of %d bytes reported as %d on page %u", - nFrag, data[hdr+7], iPage); - } + + /* Release the locks */ + sqlcipher_sqlite3VdbeLeave(p); } -end_of_check: - if( !doCoverageCheck ) pPage->isInit = savedIsInit; - releasePage(pPage); - pCheck->zPfx = saved_zPfx; - pCheck->v1 = saved_v1; - pCheck->v2 = saved_v2; - return depth+1; + /* We have successfully halted and closed the VM. Record this fact. */ + db->nVdbeActive--; + if( !p->readOnly ) db->nVdbeWrite--; + if( p->bIsReader ) db->nVdbeRead--; + assert( db->nVdbeActive>=db->nVdbeRead ); + assert( db->nVdbeRead>=db->nVdbeWrite ); + assert( db->nVdbeWrite>=0 ); + p->eVdbeState = VDBE_HALT_STATE; + checkActiveVdbeCnt(db); + if( db->mallocFailed ){ + p->rc = SQLITE_NOMEM_BKPT; + } + + /* If the auto-commit flag is set to true, then any locks that were held + ** by connection db have now been released. Call sqlcipher_sqlite3ConnectionUnlocked() + ** to invoke any required unlock-notify callbacks. + */ + if( db->autoCommit ){ + sqlcipher_sqlite3ConnectionUnlocked(db); + } + + assert( db->nVdbeActive>0 || db->autoCommit==0 || db->nStatement==0 ); + return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK); } -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ -#ifndef SQLITE_OMIT_INTEGRITY_CHECK + /* -** This routine does a complete check of the given BTree file. aRoot[] is -** an array of pages numbers were each page number is the root page of -** a table. nRoot is the number of entries in aRoot. +** Each VDBE holds the result of the most recent sqlcipher_sqlite3_step() call +** in p->rc. This routine sets that result back to SQLITE_OK. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeResetStepResult(Vdbe *p){ + p->rc = SQLITE_OK; +} + +/* +** Copy the error code and error message belonging to the VDBE passed +** as the first argument to its database handle (so that they will be +** returned by calls to sqlcipher_sqlite3_errcode() and sqlcipher_sqlite3_errmsg()). ** -** A read-only or read-write transaction must be opened before calling -** this function. +** This function does not clear the VDBE error code or message, just +** copies them to the database handle. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeTransferError(Vdbe *p){ + sqlcipher_sqlite3 *db = p->db; + int rc = p->rc; + if( p->zErrMsg ){ + db->bBenignMalloc++; + sqlcipher_sqlite3BeginBenignMalloc(); + if( db->pErr==0 ) db->pErr = sqlcipher_sqlite3ValueNew(db); + sqlcipher_sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); + sqlcipher_sqlite3EndBenignMalloc(); + db->bBenignMalloc--; + }else if( db->pErr ){ + sqlcipher_sqlite3ValueSetNull(db->pErr); + } + db->errCode = rc; + db->errByteOffset = -1; + return rc; +} + +#ifdef SQLITE_ENABLE_SQLLOG +/* +** If an SQLITE_CONFIG_SQLLOG hook is registered and the VM has been run, +** invoke it. +*/ +static void vdbeInvokeSqllog(Vdbe *v){ + if( sqlcipher_sqlite3GlobalConfig.xSqllog && v->rc==SQLITE_OK && v->zSql && v->pc>=0 ){ + char *zExpanded = sqlcipher_sqlite3VdbeExpandSql(v, v->zSql); + assert( v->db->init.busy==0 ); + if( zExpanded ){ + sqlcipher_sqlite3GlobalConfig.xSqllog( + sqlcipher_sqlite3GlobalConfig.pSqllogArg, v->db, zExpanded, 1 + ); + sqlcipher_sqlite3DbFree(v->db, zExpanded); + } + } +} +#else +# define vdbeInvokeSqllog(x) +#endif + +/* +** Clean up a VDBE after execution but do not delete the VDBE just yet. +** Write any error messages into *pzErrMsg. Return the result code. ** -** Write the number of error seen in *pnErr. Except for some memory -** allocation errors, an error message held in memory obtained from -** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is -** returned. If a memory allocation error occurs, NULL is returned. +** After this routine is run, the VDBE should be ready to be executed +** again. ** -** If the first entry in aRoot[] is 0, that indicates that the list of -** root pages is incomplete. This is a "partial integrity-check". This -** happens when performing an integrity check on a single table. The -** zero is skipped, of course. But in addition, the freelist checks -** and the checks to make sure every page is referenced are also skipped, -** since obviously it is not possible to know which pages are covered by -** the unverified btrees. Except, if aRoot[1] is 1, then the freelist -** checks are still performed. +** To look at it another way, this routine resets the state of the +** virtual machine from VDBE_RUN_STATE or VDBE_HALT_STATE back to +** VDBE_READY_STATE. */ -SQLITE_PRIVATE char *sqlcipher_sqlite3BtreeIntegrityCheck( - sqlcipher_sqlite3 *db, /* Database connection that is running the check */ - Btree *p, /* The btree to be checked */ - Pgno *aRoot, /* An array of root pages numbers for individual trees */ - int nRoot, /* Number of entries in aRoot[] */ - int mxErr, /* Stop reporting errors after this many */ - int *pnErr /* Write number of errors seen to this variable */ -){ - Pgno i; - IntegrityCk sCheck; - BtShared *pBt = p->pBt; - u64 savedDbFlags = pBt->db->flags; - char zErr[100]; - int bPartial = 0; /* True if not checking all btrees */ - int bCkFreelist = 1; /* True to scan the freelist */ - VVA_ONLY( int nRef ); - assert( nRoot>0 ); +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeReset(Vdbe *p){ +#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) + int i; +#endif - /* aRoot[0]==0 means this is a partial check */ - if( aRoot[0]==0 ){ - assert( nRoot>1 ); - bPartial = 1; - if( aRoot[1]!=1 ) bCkFreelist = 0; - } + sqlcipher_sqlite3 *db; + db = p->db; - sqlcipher_sqlite3BtreeEnter(p); - assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); - VVA_ONLY( nRef = sqlcipher_sqlite3PagerRefcount(pBt->pPager) ); - assert( nRef>=0 ); - sCheck.db = db; - sCheck.pBt = pBt; - sCheck.pPager = pBt->pPager; - sCheck.nPage = btreePagecount(sCheck.pBt); - sCheck.mxErr = mxErr; - sCheck.nErr = 0; - sCheck.bOomFault = 0; - sCheck.zPfx = 0; - sCheck.v1 = 0; - sCheck.v2 = 0; - sCheck.aPgRef = 0; - sCheck.heap = 0; - sqlcipher_sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); - sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL; - if( sCheck.nPage==0 ){ - goto integrity_ck_cleanup; - } + /* If the VM did not run to completion or if it encountered an + ** error, then it might not have been halted properly. So halt + ** it now. + */ + if( p->eVdbeState==VDBE_RUN_STATE ) sqlcipher_sqlite3VdbeHalt(p); - sCheck.aPgRef = sqlcipher_sqlite3MallocZero((sCheck.nPage / 8)+ 1); - if( !sCheck.aPgRef ){ - sCheck.bOomFault = 1; - goto integrity_ck_cleanup; - } - sCheck.heap = (u32*)sqlcipher_sqlite3PageMalloc( pBt->pageSize ); - if( sCheck.heap==0 ){ - sCheck.bOomFault = 1; - goto integrity_ck_cleanup; + /* If the VDBE has been run even partially, then transfer the error code + ** and error message from the VDBE into the main database structure. But + ** if the VDBE has just been set to run but has not actually executed any + ** instructions yet, leave the main database error information unchanged. + */ + if( p->pc>=0 ){ + vdbeInvokeSqllog(p); + if( db->pErr || p->zErrMsg ){ + sqlcipher_sqlite3VdbeTransferError(p); + }else{ + db->errCode = p->rc; + } } - i = PENDING_BYTE_PAGE(pBt); - if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); - - /* Check the integrity of the freelist + /* Reset register contents and reclaim error message memory. */ - if( bCkFreelist ){ - sCheck.zPfx = "Main freelist: "; - checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), - get4byte(&pBt->pPage1->aData[36])); - sCheck.zPfx = 0; +#ifdef SQLITE_DEBUG + /* Execute assert() statements to ensure that the Vdbe.apCsr[] and + ** Vdbe.aMem[] arrays have already been cleaned up. */ + if( p->apCsr ) for(i=0; inCursor; i++) assert( p->apCsr[i]==0 ); + if( p->aMem ){ + for(i=0; inMem; i++) assert( p->aMem[i].flags==MEM_Undefined ); + } +#endif + if( p->zErrMsg ){ + sqlcipher_sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = 0; } + p->pResultSet = 0; +#ifdef SQLITE_DEBUG + p->nWrite = 0; +#endif - /* Check all the tables. + /* Save profiling information from this VDBE run. */ -#ifndef SQLITE_OMIT_AUTOVACUUM - if( !bPartial ){ - if( pBt->autoVacuum ){ - Pgno mx = 0; - Pgno mxInHdr; - for(i=0; (int)ipPage1->aData[52]); - if( mx!=mxInHdr ){ - checkAppendMsg(&sCheck, - "max rootpage (%d) disagrees with header (%d)", - mx, mxInHdr +#ifdef VDBE_PROFILE + { + FILE *out = fopen("vdbe_profile.out", "a"); + if( out ){ + fprintf(out, "---- "); + for(i=0; inOp; i++){ + fprintf(out, "%02x", p->aOp[i].opcode); + } + fprintf(out, "\n"); + if( p->zSql ){ + char c, pc = 0; + fprintf(out, "-- "); + for(i=0; (c = p->zSql[i])!=0; i++){ + if( pc=='\n' ) fprintf(out, "-- "); + putc(c, out); + pc = c; + } + if( pc!='\n' ) fprintf(out, "\n"); + } + for(i=0; inOp; i++){ + char zHdr[100]; + sqlcipher_sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ", + p->aOp[i].cnt, + p->aOp[i].cycles, + p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0 ); + fprintf(out, "%s", zHdr); + sqlcipher_sqlite3VdbePrintOp(out, i, &p->aOp[i]); } - }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){ - checkAppendMsg(&sCheck, - "incremental_vacuum enabled with a max rootpage of zero" - ); + fclose(out); } } #endif - testcase( pBt->db->flags & SQLITE_CellSizeCk ); - pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; - for(i=0; (int)iautoVacuum && aRoot[i]>1 && !bPartial ){ - checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); - } -#endif - checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); + return p->rc & db->errMask; +} + +/* +** Clean up and delete a VDBE after execution. Return an integer which is +** the result code. Write any error message text into *pzErrMsg. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeFinalize(Vdbe *p){ + int rc = SQLITE_OK; + assert( VDBE_RUN_STATE>VDBE_READY_STATE ); + assert( VDBE_HALT_STATE>VDBE_READY_STATE ); + assert( VDBE_INIT_STATEeVdbeState>=VDBE_READY_STATE ){ + rc = sqlcipher_sqlite3VdbeReset(p); + assert( (rc & p->db->errMask)==rc ); } - pBt->db->flags = savedDbFlags; + sqlcipher_sqlite3VdbeDelete(p); + return rc; +} - /* Make sure every page in the file is referenced - */ - if( !bPartial ){ - for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ -#ifdef SQLITE_OMIT_AUTOVACUUM - if( getPageReferenced(&sCheck, i)==0 ){ - checkAppendMsg(&sCheck, "Page %d is never used", i); - } -#else - /* If the database supports auto-vacuum, make sure no tables contain - ** references to pointer-map pages. - */ - if( getPageReferenced(&sCheck, i)==0 && - (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ - checkAppendMsg(&sCheck, "Page %d is never used", i); - } - if( getPageReferenced(&sCheck, i)!=0 && - (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ - checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i); +/* +** If parameter iOp is less than zero, then invoke the destructor for +** all auxiliary data pointers currently cached by the VM passed as +** the first argument. +** +** Or, if iOp is greater than or equal to zero, then the destructor is +** only invoked for those auxiliary data pointers created by the user +** function invoked by the OP_Function opcode at instruction iOp of +** VM pVdbe, and only then if: +** +** * the associated function parameter is the 32nd or later (counting +** from left to right), or +** +** * the corresponding bit in argument mask is clear (where the first +** function parameter corresponds to bit 0 etc.). +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeDeleteAuxData(sqlcipher_sqlite3 *db, AuxData **pp, int iOp, int mask){ + while( *pp ){ + AuxData *pAux = *pp; + if( (iOp<0) + || (pAux->iAuxOp==iOp + && pAux->iAuxArg>=0 + && (pAux->iAuxArg>31 || !(mask & MASKBIT32(pAux->iAuxArg)))) + ){ + testcase( pAux->iAuxArg==31 ); + if( pAux->xDeleteAux ){ + pAux->xDeleteAux(pAux->pAux); } + *pp = pAux->pNextAux; + sqlcipher_sqlite3DbFree(db, pAux); + }else{ + pp= &pAux->pNextAux; + } + } +} + +/* +** Free all memory associated with the Vdbe passed as the second argument, +** except for object itself, which is preserved. +** +** The difference between this function and sqlcipher_sqlite3VdbeDelete() is that +** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with +** the database connection and frees the object itself. +*/ +static void sqlcipher_sqlite3VdbeClearObject(sqlcipher_sqlite3 *db, Vdbe *p){ + SubProgram *pSub, *pNext; + assert( p->db==0 || p->db==db ); + if( p->aColName ){ + releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); + sqlcipher_sqlite3DbFreeNN(db, p->aColName); + } + for(pSub=p->pProgram; pSub; pSub=pNext){ + pNext = pSub->pNext; + vdbeFreeOpArray(db, pSub->aOp, pSub->nOp); + sqlcipher_sqlite3DbFree(db, pSub); + } + if( p->eVdbeState!=VDBE_INIT_STATE ){ + releaseMemArray(p->aVar, p->nVar); + if( p->pVList ) sqlcipher_sqlite3DbFreeNN(db, p->pVList); + if( p->pFree ) sqlcipher_sqlite3DbFreeNN(db, p->pFree); + } + vdbeFreeOpArray(db, p->aOp, p->nOp); + sqlcipher_sqlite3DbFree(db, p->zSql); +#ifdef SQLITE_ENABLE_NORMALIZE + sqlcipher_sqlite3DbFree(db, p->zNormSql); + { + DblquoteStr *pThis, *pNext; + for(pThis=p->pDblStr; pThis; pThis=pNext){ + pNext = pThis->pNextStr; + sqlcipher_sqlite3DbFree(db, pThis); + } + } #endif +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + { + int i; + for(i=0; inScan; i++){ + sqlcipher_sqlite3DbFree(db, p->aScan[i].zName); } + sqlcipher_sqlite3DbFree(db, p->aScan); } +#endif +} - /* Clean up and report errors. - */ -integrity_ck_cleanup: - sqlcipher_sqlite3PageFree(sCheck.heap); - sqlcipher_sqlite3_free(sCheck.aPgRef); - if( sCheck.bOomFault ){ - sqlcipher_sqlite3_str_reset(&sCheck.errMsg); - sCheck.nErr++; +/* +** Delete an entire VDBE. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeDelete(Vdbe *p){ + sqlcipher_sqlite3 *db; + + assert( p!=0 ); + db = p->db; + assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); + sqlcipher_sqlite3VdbeClearObject(db, p); + if( db->pnBytesFreed==0 ){ + if( p->pPrev ){ + p->pPrev->pNext = p->pNext; + }else{ + assert( db->pVdbe==p ); + db->pVdbe = p->pNext; + } + if( p->pNext ){ + p->pNext->pPrev = p->pPrev; + } } - *pnErr = sCheck.nErr; - if( sCheck.nErr==0 ) sqlcipher_sqlite3_str_reset(&sCheck.errMsg); - /* Make sure this analysis did not leave any unref() pages. */ - assert( nRef==sqlcipher_sqlite3PagerRefcount(pBt->pPager) ); - sqlcipher_sqlite3BtreeLeave(p); - return sqlcipher_sqlite3StrAccumFinish(&sCheck.errMsg); + sqlcipher_sqlite3DbFreeNN(db, p); } -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ /* -** Return the full pathname of the underlying database file. Return -** an empty string if the database is in-memory or a TEMP database. -** -** The pager filename is invariant as long as the pager is -** open so it is safe to access without the BtShared mutex. +** The cursor "p" has a pending seek operation that has not yet been +** carried out. Seek the cursor now. If an error occurs, return +** the appropriate error code. */ -SQLITE_PRIVATE const char *sqlcipher_sqlite3BtreeGetFilename(Btree *p){ - assert( p->pBt->pPager!=0 ); - return sqlcipher_sqlite3PagerFilename(p->pBt->pPager, 1); +SQLITE_PRIVATE int SQLITE_NOINLINE sqlcipher_sqlite3VdbeFinishMoveto(VdbeCursor *p){ + int res, rc; +#ifdef SQLITE_TEST + extern int sqlcipher_sqlite3_search_count; +#endif + assert( p->deferredMoveto ); + assert( p->isTable ); + assert( p->eCurType==CURTYPE_BTREE ); + rc = sqlcipher_sqlite3BtreeTableMoveto(p->uc.pCursor, p->movetoTarget, 0, &res); + if( rc ) return rc; + if( res!=0 ) return SQLITE_CORRUPT_BKPT; +#ifdef SQLITE_TEST + sqlcipher_sqlite3_search_count++; +#endif + p->deferredMoveto = 0; + p->cacheStatus = CACHE_STALE; + return SQLITE_OK; } /* -** Return the pathname of the journal file for this database. The return -** value of this routine is the same regardless of whether the journal file -** has been created or not. -** -** The pager journal filename is invariant as long as the pager is -** open so it is safe to access without the BtShared mutex. +** Something has moved cursor "p" out of place. Maybe the row it was +** pointed to was deleted out from under it. Or maybe the btree was +** rebalanced. Whatever the cause, try to restore "p" to the place it +** is supposed to be pointing. If the row was deleted out from under the +** cursor, set the cursor to point to a NULL row. */ -SQLITE_PRIVATE const char *sqlcipher_sqlite3BtreeGetJournalname(Btree *p){ - assert( p->pBt->pPager!=0 ); - return sqlcipher_sqlite3PagerJournalname(p->pBt->pPager); +SQLITE_PRIVATE int SQLITE_NOINLINE sqlcipher_sqlite3VdbeHandleMovedCursor(VdbeCursor *p){ + int isDifferentRow, rc; + assert( p->eCurType==CURTYPE_BTREE ); + assert( p->uc.pCursor!=0 ); + assert( sqlcipher_sqlite3BtreeCursorHasMoved(p->uc.pCursor) ); + rc = sqlcipher_sqlite3BtreeCursorRestore(p->uc.pCursor, &isDifferentRow); + p->cacheStatus = CACHE_STALE; + if( isDifferentRow ) p->nullRow = 1; + return rc; } /* -** Return one of SQLITE_TXN_NONE, SQLITE_TXN_READ, or SQLITE_TXN_WRITE -** to describe the current transaction state of Btree p. +** Check to ensure that the cursor is valid. Restore the cursor +** if need be. Return any I/O error from the restore operation. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeTxnState(Btree *p){ - assert( p==0 || sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - return p ? p->inTrans : 0; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCursorRestore(VdbeCursor *p){ + assert( p->eCurType==CURTYPE_BTREE || IsNullCursor(p) ); + if( sqlcipher_sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ + return sqlcipher_sqlite3VdbeHandleMovedCursor(p); + } + return SQLITE_OK; } -#ifndef SQLITE_OMIT_WAL /* -** Run a checkpoint on the Btree passed as the first argument. +** The following functions: ** -** Return SQLITE_LOCKED if this or any other connection has an open -** transaction on the shared-cache the argument Btree is connected to. +** sqlcipher_sqlite3VdbeSerialType() +** sqlcipher_sqlite3VdbeSerialTypeLen() +** sqlcipher_sqlite3VdbeSerialLen() +** sqlcipher_sqlite3VdbeSerialPut() <--- in-lined into OP_MakeRecord as of 2022-04-02 +** sqlcipher_sqlite3VdbeSerialGet() ** -** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. +** encapsulate the code that serializes values for storage in SQLite +** data and index records. Each serialized value consists of a +** 'serial-type' and a blob of data. The serial type is an 8-byte unsigned +** integer, stored as a varint. +** +** In an SQLite index record, the serial type is stored directly before +** the blob of data that it corresponds to. In a table record, all serial +** types are stored at the start of the record, and the blobs of data at +** the end. Hence these functions allow the caller to handle the +** serial-type and data blob separately. +** +** The following table describes the various storage classes for data: +** +** serial type bytes of data type +** -------------- --------------- --------------- +** 0 0 NULL +** 1 1 signed integer +** 2 2 signed integer +** 3 3 signed integer +** 4 4 signed integer +** 5 6 signed integer +** 6 8 signed integer +** 7 8 IEEE float +** 8 0 Integer constant 0 +** 9 0 Integer constant 1 +** 10,11 reserved for expansion +** N>=12 and even (N-12)/2 BLOB +** N>=13 and odd (N-13)/2 text +** +** The 8 and 9 types were added in 3.3.0, file format 4. Prior versions +** of SQLite will not understand those serial types. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){ - int rc = SQLITE_OK; - if( p ){ - BtShared *pBt = p->pBt; - sqlcipher_sqlite3BtreeEnter(p); - if( pBt->inTransaction!=TRANS_NONE ){ - rc = SQLITE_LOCKED; + +#if 0 /* Inlined into the OP_MakeRecord opcode */ +/* +** Return the serial-type for the value stored in pMem. +** +** This routine might convert a large MEM_IntReal value into MEM_Real. +** +** 2019-07-11: The primary user of this subroutine was the OP_MakeRecord +** opcode in the byte-code engine. But by moving this routine in-line, we +** can omit some redundant tests and make that opcode a lot faster. So +** this routine is now only used by the STAT3 logic and STAT3 support has +** ended. The code is kept here for historical reference only. +*/ +SQLITE_PRIVATE u32 sqlcipher_sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){ + int flags = pMem->flags; + u32 n; + + assert( pLen!=0 ); + if( flags&MEM_Null ){ + *pLen = 0; + return 0; + } + if( flags&(MEM_Int|MEM_IntReal) ){ + /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */ +# define MAX_6BYTE ((((i64)0x00008000)<<32)-1) + i64 i = pMem->u.i; + u64 u; + testcase( flags & MEM_Int ); + testcase( flags & MEM_IntReal ); + if( i<0 ){ + u = ~i; }else{ - rc = sqlcipher_sqlite3PagerCheckpoint(pBt->pPager, p->db, eMode, pnLog, pnCkpt); + u = i; } - sqlcipher_sqlite3BtreeLeave(p); + if( u<=127 ){ + if( (i&1)==i && file_format>=4 ){ + *pLen = 0; + return 8+(u32)u; + }else{ + *pLen = 1; + return 1; + } + } + if( u<=32767 ){ *pLen = 2; return 2; } + if( u<=8388607 ){ *pLen = 3; return 3; } + if( u<=2147483647 ){ *pLen = 4; return 4; } + if( u<=MAX_6BYTE ){ *pLen = 6; return 5; } + *pLen = 8; + if( flags&MEM_IntReal ){ + /* If the value is IntReal and is going to take up 8 bytes to store + ** as an integer, then we might as well make it an 8-byte floating + ** point value */ + pMem->u.r = (double)pMem->u.i; + pMem->flags &= ~MEM_IntReal; + pMem->flags |= MEM_Real; + return 7; + } + return 6; } - return rc; + if( flags&MEM_Real ){ + *pLen = 8; + return 7; + } + assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) ); + assert( pMem->n>=0 ); + n = (u32)pMem->n; + if( flags & MEM_Zero ){ + n += pMem->u.nZero; + } + *pLen = n; + return ((n*2) + 12 + ((flags&MEM_Str)!=0)); } -#endif +#endif /* inlined into OP_MakeRecord */ /* -** Return true if there is currently a backup running on Btree p. +** The sizes for serial types less than 128 */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeIsInBackup(Btree *p){ - assert( p ); - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - return p->nBackup!=0; -} +SQLITE_PRIVATE const u8 sqlcipher_sqlite3SmallTypeSizes[128] = { + /* 0 1 2 3 4 5 6 7 8 9 */ +/* 0 */ 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, +/* 10 */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, +/* 20 */ 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, +/* 30 */ 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, +/* 40 */ 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, +/* 50 */ 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, +/* 60 */ 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, +/* 70 */ 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, +/* 80 */ 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, +/* 90 */ 39, 39, 40, 40, 41, 41, 42, 42, 43, 43, +/* 100 */ 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, +/* 110 */ 49, 49, 50, 50, 51, 51, 52, 52, 53, 53, +/* 120 */ 54, 54, 55, 55, 56, 56, 57, 57 +}; /* -** This function returns a pointer to a blob of memory associated with -** a single shared-btree. The memory is used by client code for its own -** purposes (for example, to store a high-level schema associated with -** the shared-btree). The btree layer manages reference counting issues. -** -** The first time this is called on a shared-btree, nBytes bytes of memory -** are allocated, zeroed, and returned to the caller. For each subsequent -** call the nBytes parameter is ignored and a pointer to the same blob -** of memory returned. -** -** If the nBytes parameter is 0 and the blob of memory has not yet been -** allocated, a null pointer is returned. If the blob has already been -** allocated, it is returned as normal. -** -** Just before the shared-btree is closed, the function passed as the -** xFree argument when the memory allocation was made is invoked on the -** blob of allocated memory. The xFree function should not call sqlcipher_sqlite3_free() -** on the memory, the btree layer does that. +** Return the length of the data corresponding to the supplied serial-type. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ - BtShared *pBt = p->pBt; - sqlcipher_sqlite3BtreeEnter(p); - if( !pBt->pSchema && nBytes ){ - pBt->pSchema = sqlcipher_sqlite3DbMallocZero(0, nBytes); - pBt->xFreeSchema = xFree; +SQLITE_PRIVATE u32 sqlcipher_sqlite3VdbeSerialTypeLen(u32 serial_type){ + if( serial_type>=128 ){ + return (serial_type-12)/2; + }else{ + assert( serial_type<12 + || sqlcipher_sqlite3SmallTypeSizes[serial_type]==(serial_type - 12)/2 ); + return sqlcipher_sqlite3SmallTypeSizes[serial_type]; } - sqlcipher_sqlite3BtreeLeave(p); - return pBt->pSchema; +} +SQLITE_PRIVATE u8 sqlcipher_sqlite3VdbeOneByteSerialTypeLen(u8 serial_type){ + assert( serial_type<128 ); + return sqlcipher_sqlite3SmallTypeSizes[serial_type]; } /* -** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared -** btree as the argument handle holds an exclusive lock on the -** sqlite_schema table. Otherwise SQLITE_OK. +** If we are on an architecture with mixed-endian floating +** points (ex: ARM7) then swap the lower 4 bytes with the +** upper 4 bytes. Return the result. +** +** For most architectures, this is a no-op. +** +** (later): It is reported to me that the mixed-endian problem +** on ARM7 is an issue with GCC, not with the ARM7 chip. It seems +** that early versions of GCC stored the two words of a 64-bit +** float in the wrong order. And that error has been propagated +** ever since. The blame is not necessarily with GCC, though. +** GCC might have just copying the problem from a prior compiler. +** I am also told that newer versions of GCC that follow a different +** ABI get the byte order right. +** +** Developers using SQLite on an ARM7 should compile and run their +** application using -DSQLITE_DEBUG=1 at least once. With DEBUG +** enabled, some asserts below will ensure that the byte order of +** floating point values is correct. +** +** (2007-08-30) Frank van Vugt has studied this problem closely +** and has send his findings to the SQLite developers. Frank +** writes that some Linux kernels offer floating point hardware +** emulation that uses only 32-bit mantissas instead of a full +** 48-bits as required by the IEEE standard. (This is the +** CONFIG_FPE_FASTFPE option.) On such systems, floating point +** byte swapping becomes very complicated. To avoid problems, +** the necessary byte swapping is carried out using a 64-bit integer +** rather than a 64-bit float. Frank assures us that the code here +** works for him. We, the developers, have no way to independently +** verify this, but Frank seems to know what he is talking about +** so we trust him. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSchemaLocked(Btree *p){ - int rc; - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - sqlcipher_sqlite3BtreeEnter(p); - rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK); - assert( rc==SQLITE_OK || rc==SQLITE_LOCKED_SHAREDCACHE ); - sqlcipher_sqlite3BtreeLeave(p); - return rc; +#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT +SQLITE_PRIVATE u64 sqlcipher_sqlite3FloatSwap(u64 in){ + union { + u64 r; + u32 i[2]; + } u; + u32 t; + + u.r = in; + t = u.i[0]; + u.i[0] = u.i[1]; + u.i[1] = t; + return u.r; } +#endif /* SQLITE_MIXED_ENDIAN_64BIT_FLOAT */ -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** Obtain a lock on the table whose root page is iTab. The -** lock is a write lock if isWritelock is true or a read lock -** if it is false. +/* Input "x" is a sequence of unsigned characters that represent a +** big-endian integer. Return the equivalent native integer */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){ - int rc = SQLITE_OK; - assert( p->inTrans!=TRANS_NONE ); - if( p->sharable ){ - u8 lockType = READ_LOCK + isWriteLock; - assert( READ_LOCK+1==WRITE_LOCK ); - assert( isWriteLock==0 || isWriteLock==1 ); +#define ONE_BYTE_INT(x) ((i8)(x)[0]) +#define TWO_BYTE_INT(x) (256*(i8)((x)[0])|(x)[1]) +#define THREE_BYTE_INT(x) (65536*(i8)((x)[0])|((x)[1]<<8)|(x)[2]) +#define FOUR_BYTE_UINT(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3]) +#define FOUR_BYTE_INT(x) (16777216*(i8)((x)[0])|((x)[1]<<16)|((x)[2]<<8)|(x)[3]) - sqlcipher_sqlite3BtreeEnter(p); - rc = querySharedCacheTableLock(p, iTab, lockType); - if( rc==SQLITE_OK ){ - rc = setSharedCacheTableLock(p, iTab, lockType); - } - sqlcipher_sqlite3BtreeLeave(p); +/* +** Deserialize the data blob pointed to by buf as serial type serial_type +** and store the result in pMem. +** +** This function is implemented as two separate routines for performance. +** The few cases that require local variables are broken out into a separate +** routine so that in most cases the overhead of moving the stack pointer +** is avoided. +*/ +static void serialGet( + const unsigned char *buf, /* Buffer to deserialize from */ + u32 serial_type, /* Serial type to deserialize */ + Mem *pMem /* Memory cell to write value into */ +){ + u64 x = FOUR_BYTE_UINT(buf); + u32 y = FOUR_BYTE_UINT(buf+4); + x = (x<<32) + y; + if( serial_type==6 ){ + /* EVIDENCE-OF: R-29851-52272 Value is a big-endian 64-bit + ** twos-complement integer. */ + pMem->u.i = *(i64*)&x; + pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); + }else{ + /* EVIDENCE-OF: R-57343-49114 Value is a big-endian IEEE 754-2008 64-bit + ** floating point number. */ +#if !defined(NDEBUG) && !defined(SQLITE_OMIT_FLOATING_POINT) + /* Verify that integers and floating point values use the same + ** byte order. Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is + ** defined that 64-bit floating point values really are mixed + ** endian. + */ + static const u64 t1 = ((u64)0x3ff00000)<<32; + static const double r1 = 1.0; + u64 t2 = t1; + swapMixedEndianFloat(t2); + assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 ); +#endif + assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 ); + swapMixedEndianFloat(x); + memcpy(&pMem->u.r, &x, sizeof(x)); + pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real; } - return rc; } +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSerialGet( + const unsigned char *buf, /* Buffer to deserialize from */ + u32 serial_type, /* Serial type to deserialize */ + Mem *pMem /* Memory cell to write value into */ +){ + switch( serial_type ){ + case 10: { /* Internal use only: NULL with virtual table + ** UPDATE no-change flag set */ + pMem->flags = MEM_Null|MEM_Zero; + pMem->n = 0; + pMem->u.nZero = 0; + return; + } + case 11: /* Reserved for future use */ + case 0: { /* Null */ + /* EVIDENCE-OF: R-24078-09375 Value is a NULL. */ + pMem->flags = MEM_Null; + return; + } + case 1: { + /* EVIDENCE-OF: R-44885-25196 Value is an 8-bit twos-complement + ** integer. */ + pMem->u.i = ONE_BYTE_INT(buf); + pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); + return; + } + case 2: { /* 2-byte signed integer */ + /* EVIDENCE-OF: R-49794-35026 Value is a big-endian 16-bit + ** twos-complement integer. */ + pMem->u.i = TWO_BYTE_INT(buf); + pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); + return; + } + case 3: { /* 3-byte signed integer */ + /* EVIDENCE-OF: R-37839-54301 Value is a big-endian 24-bit + ** twos-complement integer. */ + pMem->u.i = THREE_BYTE_INT(buf); + pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); + return; + } + case 4: { /* 4-byte signed integer */ + /* EVIDENCE-OF: R-01849-26079 Value is a big-endian 32-bit + ** twos-complement integer. */ + pMem->u.i = FOUR_BYTE_INT(buf); +#ifdef __HP_cc + /* Work around a sign-extension bug in the HP compiler for HP/UX */ + if( buf[0]&0x80 ) pMem->u.i |= 0xffffffff80000000LL; #endif - -#ifndef SQLITE_OMIT_INCRBLOB + pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); + return; + } + case 5: { /* 6-byte signed integer */ + /* EVIDENCE-OF: R-50385-09674 Value is a big-endian 48-bit + ** twos-complement integer. */ + pMem->u.i = FOUR_BYTE_UINT(buf+2) + (((i64)1)<<32)*TWO_BYTE_INT(buf); + pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); + return; + } + case 6: /* 8-byte signed integer */ + case 7: { /* IEEE floating point */ + /* These use local variables, so do them in a separate routine + ** to avoid having to move the frame pointer in the common case */ + serialGet(buf,serial_type,pMem); + return; + } + case 8: /* Integer 0 */ + case 9: { /* Integer 1 */ + /* EVIDENCE-OF: R-12976-22893 Value is the integer 0. */ + /* EVIDENCE-OF: R-18143-12121 Value is the integer 1. */ + pMem->u.i = serial_type-8; + pMem->flags = MEM_Int; + return; + } + default: { + /* EVIDENCE-OF: R-14606-31564 Value is a BLOB that is (N-12)/2 bytes in + ** length. + ** EVIDENCE-OF: R-28401-00140 Value is a string in the text encoding and + ** (N-13)/2 bytes in length. */ + static const u16 aFlag[] = { MEM_Blob|MEM_Ephem, MEM_Str|MEM_Ephem }; + pMem->z = (char *)buf; + pMem->n = (serial_type-12)/2; + pMem->flags = aFlag[serial_type&1]; + return; + } + } + return; +} /* -** Argument pCsr must be a cursor opened for writing on an -** INTKEY table currently pointing at a valid table entry. -** This function modifies the data stored as part of that entry. +** This routine is used to allocate sufficient space for an UnpackedRecord +** structure large enough to be used with sqlcipher_sqlite3VdbeRecordUnpack() if +** the first argument is a pointer to KeyInfo structure pKeyInfo. ** -** Only the data content may only be modified, it is not possible to -** change the length of the data stored. If this function is called with -** parameters that attempt to write past the end of the existing data, -** no modifications are made and SQLITE_CORRUPT is returned. +** The space is either allocated using sqlcipher_sqlite3DbMallocRaw() or from within +** the unaligned buffer passed via the second and third arguments (presumably +** stack space). If the former, then *ppFree is set to a pointer that should +** be eventually freed by the caller using sqlcipher_sqlite3DbFree(). Or, if the +** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL +** before returning. +** +** If an OOM error occurs, NULL is returned. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ - int rc; - assert( cursorOwnsBtShared(pCsr) ); - assert( sqlcipher_sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); - assert( pCsr->curFlags & BTCF_Incrblob ); - - rc = restoreCursorPosition(pCsr); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pCsr->eState!=CURSOR_REQUIRESEEK ); - if( pCsr->eState!=CURSOR_VALID ){ - return SQLITE_ABORT; - } - - /* Save the positions of all other cursors open on this table. This is - ** required in case any of them are holding references to an xFetch - ** version of the b-tree page modified by the accessPayload call below. - ** - ** Note that pCsr must be open on a INTKEY table and saveCursorPosition() - ** and hence saveAllCursors() cannot fail on a BTREE_INTKEY table, hence - ** saveAllCursors can only return SQLITE_OK. - */ - VVA_ONLY(rc =) saveAllCursors(pCsr->pBt, pCsr->pgnoRoot, pCsr); - assert( rc==SQLITE_OK ); - - /* Check some assumptions: - ** (a) the cursor is open for writing, - ** (b) there is a read/write transaction open, - ** (c) the connection holds a write-lock on the table (if required), - ** (d) there are no conflicting read-locks, and - ** (e) the cursor points at a valid row of an intKey table. - */ - if( (pCsr->curFlags & BTCF_WriteFlag)==0 ){ - return SQLITE_READONLY; - } - assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0 - && pCsr->pBt->inTransaction==TRANS_WRITE ); - assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) ); - assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) ); - assert( pCsr->pPage->intKey ); - - return accessPayload(pCsr, offset, amt, (unsigned char *)z, 1); +SQLITE_PRIVATE UnpackedRecord *sqlcipher_sqlite3VdbeAllocUnpackedRecord( + KeyInfo *pKeyInfo /* Description of the record */ +){ + UnpackedRecord *p; /* Unpacked record to return */ + int nByte; /* Number of bytes required for *p */ + nByte = ROUND8P(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1); + p = (UnpackedRecord *)sqlcipher_sqlite3DbMallocRaw(pKeyInfo->db, nByte); + if( !p ) return 0; + p->aMem = (Mem*)&((char*)p)[ROUND8P(sizeof(UnpackedRecord))]; + assert( pKeyInfo->aSortFlags!=0 ); + p->pKeyInfo = pKeyInfo; + p->nField = pKeyInfo->nKeyField + 1; + return p; } /* -** Mark this cursor as an incremental blob cursor. +** Given the nKey-byte encoding of a record in pKey[], populate the +** UnpackedRecord structure indicated by the fourth argument with the +** contents of the decoded record. */ -SQLITE_PRIVATE void sqlcipher_sqlite3BtreeIncrblobCursor(BtCursor *pCur){ - pCur->curFlags |= BTCF_Incrblob; - pCur->pBtree->hasIncrblobCur = 1; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeRecordUnpack( + KeyInfo *pKeyInfo, /* Information about the record format */ + int nKey, /* Size of the binary record */ + const void *pKey, /* The binary record */ + UnpackedRecord *p /* Populate this structure before returning. */ +){ + const unsigned char *aKey = (const unsigned char *)pKey; + u32 d; + u32 idx; /* Offset in aKey[] to read from */ + u16 u; /* Unsigned loop counter */ + u32 szHdr; + Mem *pMem = p->aMem; + + p->default_rc = 0; + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + idx = getVarint32(aKey, szHdr); + d = szHdr; + u = 0; + while( idxenc = pKeyInfo->enc; + pMem->db = pKeyInfo->db; + /* pMem->flags = 0; // sqlcipher_sqlite3VdbeSerialGet() will set this for us */ + pMem->szMalloc = 0; + pMem->z = 0; + sqlcipher_sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); + d += sqlcipher_sqlite3VdbeSerialTypeLen(serial_type); + pMem++; + if( (++u)>=p->nField ) break; + } + if( d>(u32)nKey && u ){ + assert( CORRUPT_DB ); + /* In a corrupt record entry, the last pMem might have been set up using + ** uninitialized memory. Overwrite its value with NULL, to prevent + ** warnings from MSAN. */ + sqlcipher_sqlite3VdbeMemSetNull(pMem-1); + } + assert( u<=pKeyInfo->nKeyField + 1 ); + p->nField = u; } -#endif +#ifdef SQLITE_DEBUG /* -** Set both the "read version" (single byte at byte offset 18) and -** "write version" (single byte at byte offset 19) fields in the database -** header to iVersion. +** This function compares two index or table record keys in the same way +** as the sqlcipher_sqlite3VdbeRecordCompare() routine. Unlike VdbeRecordCompare(), +** this function deserializes and compares values using the +** sqlcipher_sqlite3VdbeSerialGet() and sqlcipher_sqlite3MemCompare() functions. It is used +** in assert() statements to ensure that the optimized code in +** sqlcipher_sqlite3VdbeRecordCompare() returns results with these two primitives. +** +** Return true if the result of comparison is equivalent to desiredResult. +** Return false if there is a disagreement. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ - BtShared *pBt = pBtree->pBt; - int rc; /* Return code */ +static int vdbeRecordCompareDebug( + int nKey1, const void *pKey1, /* Left key */ + const UnpackedRecord *pPKey2, /* Right key */ + int desiredResult /* Correct answer */ +){ + u32 d1; /* Offset into aKey[] of next data element */ + u32 idx1; /* Offset into aKey[] of next header element */ + u32 szHdr1; /* Number of bytes in header */ + int i = 0; + int rc = 0; + const unsigned char *aKey1 = (const unsigned char *)pKey1; + KeyInfo *pKeyInfo; + Mem mem1; - assert( iVersion==1 || iVersion==2 ); + pKeyInfo = pPKey2->pKeyInfo; + if( pKeyInfo->db==0 ) return 1; + mem1.enc = pKeyInfo->enc; + mem1.db = pKeyInfo->db; + /* mem1.flags = 0; // Will be initialized by sqlcipher_sqlite3VdbeSerialGet() */ + VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */ - /* If setting the version fields to 1, do not automatically open the - ** WAL connection, even if the version fields are currently set to 2. + /* Compilers may complain that mem1.u.i is potentially uninitialized. + ** We could initialize it, as shown here, to silence those complaints. + ** But in fact, mem1.u.i will never actually be used uninitialized, and doing + ** the unnecessary initialization has a measurable negative performance + ** impact, since this routine is a very high runner. And so, we choose + ** to ignore the compiler warnings and leave this variable uninitialized. */ - pBt->btsFlags &= ~BTS_NO_WAL; - if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL; + /* mem1.u.i = 0; // not needed, here to silence compiler warning */ - rc = sqlcipher_sqlite3BtreeBeginTrans(pBtree, 0, 0); - if( rc==SQLITE_OK ){ - u8 *aData = pBt->pPage1->aData; - if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){ - rc = sqlcipher_sqlite3BtreeBeginTrans(pBtree, 2, 0); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3PagerWrite(pBt->pPage1->pDbPage); - if( rc==SQLITE_OK ){ - aData[18] = (u8)iVersion; - aData[19] = (u8)iVersion; - } + idx1 = getVarint32(aKey1, szHdr1); + if( szHdr1>98307 ) return SQLITE_CORRUPT; + d1 = szHdr1; + assert( pKeyInfo->nAllField>=pPKey2->nField || CORRUPT_DB ); + assert( pKeyInfo->aSortFlags!=0 ); + assert( pKeyInfo->nKeyField>0 ); + assert( idx1<=szHdr1 || CORRUPT_DB ); + do{ + u32 serial_type1; + + /* Read the serial types for the next element in each key. */ + idx1 += getVarint32( aKey1+idx1, serial_type1 ); + + /* Verify that there is enough key space remaining to avoid + ** a buffer overread. The "d1+serial_type1+2" subexpression will + ** always be greater than or equal to the amount of required key space. + ** Use that approximation to avoid the more expensive call to + ** sqlcipher_sqlite3VdbeSerialTypeLen() in the common case. + */ + if( d1+(u64)serial_type1+2>(u64)nKey1 + && d1+(u64)sqlcipher_sqlite3VdbeSerialTypeLen(serial_type1)>(u64)nKey1 + ){ + break; + } + + /* Extract the values to be compared. + */ + sqlcipher_sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); + d1 += sqlcipher_sqlite3VdbeSerialTypeLen(serial_type1); + + /* Do the comparison + */ + rc = sqlcipher_sqlite3MemCompare(&mem1, &pPKey2->aMem[i], + pKeyInfo->nAllField>i ? pKeyInfo->aColl[i] : 0); + if( rc!=0 ){ + assert( mem1.szMalloc==0 ); /* See comment below */ + if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) + && ((mem1.flags & MEM_Null) || (pPKey2->aMem[i].flags & MEM_Null)) + ){ + rc = -rc; + } + if( pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC ){ + rc = -rc; /* Invert the result for DESC sort order. */ } + goto debugCompareEnd; } - } + i++; + }while( idx1nField ); - pBt->btsFlags &= ~BTS_NO_WAL; - return rc; -} + /* No memory allocation is ever used on mem1. Prove this using + ** the following assert(). If the assert() fails, it indicates a + ** memory leak and a need to call sqlcipher_sqlite3VdbeMemRelease(&mem1). + */ + assert( mem1.szMalloc==0 ); -/* -** Return true if the cursor has a hint specified. This routine is -** only used from within assert() statements -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCursorHasHint(BtCursor *pCsr, unsigned int mask){ - return (pCsr->hints & mask)!=0; -} + /* rc==0 here means that one of the keys ran out of fields and + ** all the fields up to that point were equal. Return the default_rc + ** value. */ + rc = pPKey2->default_rc; -/* -** Return true if the given Btree is read-only. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeIsReadonly(Btree *p){ - return (p->pBt->btsFlags & BTS_READ_ONLY)!=0; +debugCompareEnd: + if( desiredResult==0 && rc==0 ) return 1; + if( desiredResult<0 && rc<0 ) return 1; + if( desiredResult>0 && rc>0 ) return 1; + if( CORRUPT_DB ) return 1; + if( pKeyInfo->db->mallocFailed ) return 1; + return 0; } +#endif +#ifdef SQLITE_DEBUG /* -** Return the size of the header added to each page by this module. +** Count the number of fields (a.k.a. columns) in the record given by +** pKey,nKey. The verify that this count is less than or equal to the +** limit given by pKeyInfo->nAllField. +** +** If this constraint is not satisfied, it means that the high-speed +** vdbeRecordCompareInt() and vdbeRecordCompareString() routines will +** not work correctly. If this assert() ever fires, it probably means +** that the KeyInfo.nKeyField or KeyInfo.nAllField values were computed +** incorrectly. */ -SQLITE_PRIVATE int sqlcipher_sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } +static void vdbeAssertFieldCountWithinLimits( + int nKey, const void *pKey, /* The record to verify */ + const KeyInfo *pKeyInfo /* Compare size with this KeyInfo */ +){ + int nField = 0; + u32 szHdr; + u32 idx; + u32 notUsed; + const unsigned char *aKey = (const unsigned char*)pKey; -#if !defined(SQLITE_OMIT_SHARED_CACHE) -/* -** Return true if the Btree passed as the only argument is sharable. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeSharable(Btree *p){ - return p->sharable; + if( CORRUPT_DB ) return; + idx = getVarint32(aKey, szHdr); + assert( nKey>=0 ); + assert( szHdr<=(u32)nKey ); + while( idxnAllField ); } +#else +# define vdbeAssertFieldCountWithinLimits(A,B,C) +#endif /* -** Return the number of connections to the BtShared object accessed by -** the Btree handle passed as the only argument. For private caches -** this is always 1. For shared caches it may be 1 or greater. +** Both *pMem1 and *pMem2 contain string values. Compare the two values +** using the collation sequence pColl. As usual, return a negative , zero +** or positive value if *pMem1 is less than, equal to or greater than +** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);". */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeConnectionCount(Btree *p){ - testcase( p->sharable ); - return p->pBt->nRef; +static int vdbeCompareMemString( + const Mem *pMem1, + const Mem *pMem2, + const CollSeq *pColl, + u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */ +){ + if( pMem1->enc==pColl->enc ){ + /* The strings are already in the correct encoding. Call the + ** comparison function directly */ + return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z); + }else{ + int rc; + const void *v1, *v2; + Mem c1; + Mem c2; + sqlcipher_sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null); + sqlcipher_sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null); + sqlcipher_sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem); + sqlcipher_sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem); + v1 = sqlcipher_sqlite3ValueText((sqlcipher_sqlite3_value*)&c1, pColl->enc); + v2 = sqlcipher_sqlite3ValueText((sqlcipher_sqlite3_value*)&c2, pColl->enc); + if( (v1==0 || v2==0) ){ + if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT; + rc = 0; + }else{ + rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2); + } + sqlcipher_sqlite3VdbeMemReleaseMalloc(&c1); + sqlcipher_sqlite3VdbeMemReleaseMalloc(&c2); + return rc; + } } -#endif -/************** End of btree.c ***********************************************/ -/************** Begin file backup.c ******************************************/ /* -** 2009 January 28 -** -** 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 the implementation of the sqlcipher_sqlite3_backup_XXX() -** API functions and the related features. +** The input pBlob is guaranteed to be a Blob that is not marked +** with MEM_Zero. Return true if it could be a zero-blob. */ -/* #include "sqliteInt.h" */ -/* #include "btreeInt.h" */ +static int isAllZero(const char *z, int n){ + int i; + for(i=0; in; + int n2 = pB2->n; - /* These two variables are set by every call to backup_step(). They are - ** read by calls to backup_remaining() and backup_pagecount(). - */ - Pgno nRemaining; /* Number of pages left to copy */ - Pgno nPagecount; /* Total number of pages to copy */ + /* It is possible to have a Blob value that has some non-zero content + ** followed by zero content. But that only comes up for Blobs formed + ** by the OP_MakeRecord opcode, and such Blobs never get passed into + ** sqlcipher_sqlite3MemCompare(). */ + assert( (pB1->flags & MEM_Zero)==0 || n1==0 ); + assert( (pB2->flags & MEM_Zero)==0 || n2==0 ); - int isAttached; /* True once backup has been registered with pager */ - sqlcipher_sqlite3_backup *pNext; /* Next backup associated with source pager */ -}; + if( (pB1->flags|pB2->flags) & MEM_Zero ){ + if( pB1->flags & pB2->flags & MEM_Zero ){ + return pB1->u.nZero - pB2->u.nZero; + }else if( pB1->flags & MEM_Zero ){ + if( !isAllZero(pB2->z, pB2->n) ) return -1; + return pB1->u.nZero - n2; + }else{ + if( !isAllZero(pB1->z, pB1->n) ) return +1; + return n1 - pB2->u.nZero; + } + } + c = memcmp(pB1->z, pB2->z, n1>n2 ? n2 : n1); + if( c ) return c; + return n1 - n2; +} /* -** THREAD SAFETY NOTES: -** -** Once it has been created using backup_init(), a single sqlcipher_sqlite3_backup -** structure may be accessed via two groups of thread-safe entry points: -** -** * Via the sqlcipher_sqlite3_backup_XXX() API function backup_step() and -** backup_finish(). Both these functions obtain the source database -** handle mutex and the mutex associated with the source BtShared -** structure, in that order. -** -** * Via the BackupUpdate() and BackupRestart() functions, which are -** invoked by the pager layer to report various state changes in -** the page cache associated with the source database. The mutex -** associated with the source database BtShared structure will always -** be held when either of these functions are invoked. -** -** The other sqlcipher_sqlite3_backup_XXX() API functions, backup_remaining() and -** backup_pagecount() are not thread-safe functions. If they are called -** while some other thread is calling backup_step() or backup_finish(), -** the values returned may be invalid. There is no way for a call to -** BackupUpdate() or BackupRestart() to interfere with backup_remaining() -** or backup_pagecount(). -** -** Depending on the SQLite configuration, the database handles and/or -** the Btree objects may have their own mutexes that require locking. -** Non-sharable Btrees (in-memory databases for example), do not have -** associated mutexes. +** Do a comparison between a 64-bit signed integer and a 64-bit floating-point +** number. Return negative, zero, or positive if the first (i64) is less than, +** equal to, or greater than the second (double). */ +SQLITE_PRIVATE int sqlcipher_sqlite3IntFloatCompare(i64 i, double r){ + if( sizeof(LONGDOUBLE_TYPE)>8 ){ + LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; + testcase( xr ); + testcase( x==r ); + if( xr ) return +1; /*NO_TEST*/ /* work around bugs in gcov */ + return 0; /*NO_TEST*/ /* work around bugs in gcov */ + }else{ + i64 y; + double s; + if( r<-9223372036854775808.0 ) return +1; + if( r>=9223372036854775808.0 ) return -1; + y = (i64)r; + if( iy ) return +1; + s = (double)i; + if( sr ) return +1; + return 0; + } +} /* -** Return a pointer corresponding to database zDb (i.e. "main", "temp") -** in connection handle pDb. If such a database cannot be found, return -** a NULL pointer and write an error message to pErrorDb. +** Compare the values contained by the two memory cells, returning +** negative, zero or positive if pMem1 is less than, equal to, or greater +** than pMem2. Sorting order is NULL's first, followed by numbers (integers +** and reals) sorted numerically, followed by text ordered by the collating +** sequence pColl and finally blob's ordered by memcmp(). ** -** If the "temp" database is requested, it may need to be opened by this -** function. If an error occurs while doing so, return 0 and write an -** error message to pErrorDb. +** Two NULL values are considered equal by this function. */ -static Btree *findBtree(sqlcipher_sqlite3 *pErrorDb, sqlcipher_sqlite3 *pDb, const char *zDb){ - int i = sqlcipher_sqlite3FindDbName(pDb, zDb); +SQLITE_PRIVATE int sqlcipher_sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){ + int f1, f2; + int combined_flags; - if( i==1 ){ - Parse sParse; - int rc = 0; - memset(&sParse, 0, sizeof(sParse)); - sParse.db = pDb; - if( sqlcipher_sqlite3OpenTempDatabase(&sParse) ){ - sqlcipher_sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg); - rc = SQLITE_ERROR; + f1 = pMem1->flags; + f2 = pMem2->flags; + combined_flags = f1|f2; + assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem1) && !sqlcipher_sqlite3VdbeMemIsRowSet(pMem2) ); + + /* If one value is NULL, it is less than the other. If both values + ** are NULL, return 0. + */ + if( combined_flags&MEM_Null ){ + return (f2&MEM_Null) - (f1&MEM_Null); + } + + /* At least one of the two values is a number + */ + if( combined_flags&(MEM_Int|MEM_Real|MEM_IntReal) ){ + testcase( combined_flags & MEM_Int ); + testcase( combined_flags & MEM_Real ); + testcase( combined_flags & MEM_IntReal ); + if( (f1 & f2 & (MEM_Int|MEM_IntReal))!=0 ){ + testcase( f1 & f2 & MEM_Int ); + testcase( f1 & f2 & MEM_IntReal ); + if( pMem1->u.i < pMem2->u.i ) return -1; + if( pMem1->u.i > pMem2->u.i ) return +1; + return 0; } - sqlcipher_sqlite3DbFree(pErrorDb, sParse.zErrMsg); - sqlcipher_sqlite3ParserReset(&sParse); - if( rc ){ + if( (f1 & f2 & MEM_Real)!=0 ){ + if( pMem1->u.r < pMem2->u.r ) return -1; + if( pMem1->u.r > pMem2->u.r ) return +1; return 0; } + if( (f1&(MEM_Int|MEM_IntReal))!=0 ){ + testcase( f1 & MEM_Int ); + testcase( f1 & MEM_IntReal ); + if( (f2&MEM_Real)!=0 ){ + return sqlcipher_sqlite3IntFloatCompare(pMem1->u.i, pMem2->u.r); + }else if( (f2&(MEM_Int|MEM_IntReal))!=0 ){ + if( pMem1->u.i < pMem2->u.i ) return -1; + if( pMem1->u.i > pMem2->u.i ) return +1; + return 0; + }else{ + return -1; + } + } + if( (f1&MEM_Real)!=0 ){ + if( (f2&(MEM_Int|MEM_IntReal))!=0 ){ + testcase( f2 & MEM_Int ); + testcase( f2 & MEM_IntReal ); + return -sqlcipher_sqlite3IntFloatCompare(pMem2->u.i, pMem1->u.r); + }else{ + return -1; + } + } + return +1; } - if( i<0 ){ - sqlcipher_sqlite3ErrorWithMsg(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb); - return 0; + /* If one value is a string and the other is a blob, the string is less. + ** If both are strings, compare using the collating functions. + */ + if( combined_flags&MEM_Str ){ + if( (f1 & MEM_Str)==0 ){ + return 1; + } + if( (f2 & MEM_Str)==0 ){ + return -1; + } + + assert( pMem1->enc==pMem2->enc || pMem1->db->mallocFailed ); + assert( pMem1->enc==SQLITE_UTF8 || + pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE ); + + /* The collation sequence must be defined at this point, even if + ** the user deletes the collation sequence after the vdbe program is + ** compiled (this was not always the case). + */ + assert( !pColl || pColl->xCmp ); + + if( pColl ){ + return vdbeCompareMemString(pMem1, pMem2, pColl, 0); + } + /* If a NULL pointer was passed as the collate function, fall through + ** to the blob case and use memcmp(). */ } - return pDb->aDb[i].pBt; + /* Both values must be blobs. Compare using memcmp(). */ + return sqlcipher_sqlite3BlobCompare(pMem1, pMem2); } -/* -** Attempt to set the page size of the destination to match the page size -** of the source. -*/ -static int setDestPgsz(sqlcipher_sqlite3_backup *p){ - int rc; - rc = sqlcipher_sqlite3BtreeSetPageSize(p->pDest,sqlcipher_sqlite3BtreeGetPageSize(p->pSrc),0,0); - return rc; -} /* -** Check that there is no open read-transaction on the b-tree passed as the -** second argument. If there is not, return SQLITE_OK. Otherwise, if there -** is an open read-transaction, return SQLITE_ERROR and leave an error -** message in database handle db. +** The first argument passed to this function is a serial-type that +** corresponds to an integer - all values between 1 and 9 inclusive +** except 7. The second points to a buffer containing an integer value +** serialized according to serial_type. This function deserializes +** and returns the value. */ -static int checkReadTransaction(sqlcipher_sqlite3 *db, Btree *p){ - if( sqlcipher_sqlite3BtreeTxnState(p)!=SQLITE_TXN_NONE ){ - sqlcipher_sqlite3ErrorWithMsg(db, SQLITE_ERROR, "destination database is in use"); - return SQLITE_ERROR; +static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){ + u32 y; + assert( CORRUPT_DB || (serial_type>=1 && serial_type<=9 && serial_type!=7) ); + switch( serial_type ){ + case 0: + case 1: + testcase( aKey[0]&0x80 ); + return ONE_BYTE_INT(aKey); + case 2: + testcase( aKey[0]&0x80 ); + return TWO_BYTE_INT(aKey); + case 3: + testcase( aKey[0]&0x80 ); + return THREE_BYTE_INT(aKey); + case 4: { + testcase( aKey[0]&0x80 ); + y = FOUR_BYTE_UINT(aKey); + return (i64)*(int*)&y; + } + case 5: { + testcase( aKey[0]&0x80 ); + return FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey); + } + case 6: { + u64 x = FOUR_BYTE_UINT(aKey); + testcase( aKey[0]&0x80 ); + x = (x<<32) | FOUR_BYTE_UINT(aKey+4); + return (i64)*(i64*)&x; + } } - return SQLITE_OK; + + return (serial_type - 8); } /* -** Create an sqlcipher_sqlite3_backup process to copy the contents of zSrcDb from -** connection handle pSrcDb to zDestDb in pDestDb. If successful, return -** a pointer to the new sqlcipher_sqlite3_backup object. +** This function compares the two table rows or index records +** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero +** or positive integer if key1 is less than, equal to or +** greater than key2. The {nKey1, pKey1} key must be a blob +** created by the OP_MakeRecord opcode of the VDBE. The pPKey2 +** key must be a parsed key such as obtained from +** sqlcipher_sqlite3VdbeParseRecord. ** -** If an error occurs, NULL is returned and an error code and error message -** stored in database handle pDestDb. +** If argument bSkip is non-zero, it is assumed that the caller has already +** determined that the first fields of the keys are equal. +** +** Key1 and Key2 do not have to contain the same number of fields. If all +** fields that appear in both keys are equal, then pPKey2->default_rc is +** returned. +** +** If database corruption is discovered, set pPKey2->errCode to +** SQLITE_CORRUPT and return 0. If an OOM error is encountered, +** pPKey2->errCode is set to SQLITE_NOMEM and, if it is not NULL, the +** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db). */ -SQLITE_API sqlcipher_sqlite3_backup *sqlcipher_sqlite3_backup_init( - sqlcipher_sqlite3* pDestDb, /* Database to write to */ - const char *zDestDb, /* Name of database within pDestDb */ - sqlcipher_sqlite3* pSrcDb, /* Database connection to read from */ - const char *zSrcDb /* Name of database within pSrcDb */ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeRecordCompareWithSkip( + int nKey1, const void *pKey1, /* Left key */ + UnpackedRecord *pPKey2, /* Right key */ + int bSkip /* If true, skip the first field */ ){ - sqlcipher_sqlite3_backup *p; /* Value to return */ + u32 d1; /* Offset into aKey[] of next data element */ + int i; /* Index of next field to compare */ + u32 szHdr1; /* Size of record header in bytes */ + u32 idx1; /* Offset of first type in header */ + int rc = 0; /* Return value */ + Mem *pRhs = pPKey2->aMem; /* Next field of pPKey2 to compare */ + KeyInfo *pKeyInfo; + const unsigned char *aKey1 = (const unsigned char *)pKey1; + Mem mem1; -#ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlcipher_sqlite3SafetyCheckOk(pSrcDb)||!sqlcipher_sqlite3SafetyCheckOk(pDestDb) ){ - (void)SQLITE_MISUSE_BKPT; - return 0; + /* If bSkip is true, then the caller has already determined that the first + ** two elements in the keys are equal. Fix the various stack variables so + ** that this routine begins comparing at the second field. */ + if( bSkip ){ + u32 s1 = aKey1[1]; + if( s1<0x80 ){ + idx1 = 2; + }else{ + idx1 = 1 + sqlcipher_sqlite3GetVarint32(&aKey1[1], &s1); + } + szHdr1 = aKey1[0]; + d1 = szHdr1 + sqlcipher_sqlite3VdbeSerialTypeLen(s1); + i = 1; + pRhs++; + }else{ + if( (szHdr1 = aKey1[0])<0x80 ){ + idx1 = 1; + }else{ + idx1 = sqlcipher_sqlite3GetVarint32(aKey1, &szHdr1); + } + d1 = szHdr1; + i = 0; + } + if( d1>(unsigned)nKey1 ){ + pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; + return 0; /* Corruption */ } -#endif -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - { - extern int sqlcipher_find_db_index(sqlcipher_sqlite3*, const char*); - extern void sqlcipher_sqlite3CodecGetKey(sqlcipher_sqlite3*, int, void**, int*); - int srcNKey, destNKey; - void *zKey; + VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */ + assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField + || CORRUPT_DB ); + assert( pPKey2->pKeyInfo->aSortFlags!=0 ); + assert( pPKey2->pKeyInfo->nKeyField>0 ); + assert( idx1<=szHdr1 || CORRUPT_DB ); + do{ + u32 serial_type; - sqlcipher_sqlite3CodecGetKey(pSrcDb, sqlcipher_find_db_index(pSrcDb, zSrcDb), &zKey, &srcNKey); - sqlcipher_sqlite3CodecGetKey(pDestDb, sqlcipher_find_db_index(pDestDb, zDestDb), &zKey, &destNKey); - zKey = NULL; + /* RHS is an integer */ + if( pRhs->flags & (MEM_Int|MEM_IntReal) ){ + testcase( pRhs->flags & MEM_Int ); + testcase( pRhs->flags & MEM_IntReal ); + serial_type = aKey1[idx1]; + testcase( serial_type==12 ); + if( serial_type>=10 ){ + rc = +1; + }else if( serial_type==0 ){ + rc = -1; + }else if( serial_type==7 ){ + sqlcipher_sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); + rc = -sqlcipher_sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r); + }else{ + i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]); + i64 rhs = pRhs->u.i; + if( lhsrhs ){ + rc = +1; + } + } + } - /* either both databases must be plaintext, or both must be encrypted */ - if((srcNKey == 0 && destNKey > 0) || (srcNKey > 0 && destNKey == 0)) { - sqlcipher_sqlite3ErrorWithMsg(pDestDb, SQLITE_ERROR, "backup is not supported with encrypted databases"); - return NULL; + /* RHS is real */ + else if( pRhs->flags & MEM_Real ){ + serial_type = aKey1[idx1]; + if( serial_type>=10 ){ + /* Serial types 12 or greater are strings and blobs (greater than + ** numbers). Types 10 and 11 are currently "reserved for future + ** use", so it doesn't really matter what the results of comparing + ** them to numberic values are. */ + rc = +1; + }else if( serial_type==0 ){ + rc = -1; + }else{ + sqlcipher_sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); + if( serial_type==7 ){ + if( mem1.u.ru.r ){ + rc = -1; + }else if( mem1.u.r>pRhs->u.r ){ + rc = +1; + } + }else{ + rc = sqlcipher_sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r); + } + } } - } -#endif -/* END SQLCIPHER */ - /* Lock the source database handle. The destination database - ** handle is not locked in this routine, but it is locked in - ** sqlcipher_sqlite3_backup_step(). The user is required to ensure that no - ** other thread accesses the destination handle for the duration - ** of the backup operation. Any attempt to use the destination - ** database connection while a backup is in progress may cause - ** a malfunction or a deadlock. - */ - sqlcipher_sqlite3_mutex_enter(pSrcDb->mutex); - sqlcipher_sqlite3_mutex_enter(pDestDb->mutex); + /* RHS is a string */ + else if( pRhs->flags & MEM_Str ){ + getVarint32NR(&aKey1[idx1], serial_type); + testcase( serial_type==12 ); + if( serial_type<12 ){ + rc = -1; + }else if( !(serial_type & 0x01) ){ + rc = +1; + }else{ + mem1.n = (serial_type - 12) / 2; + testcase( (d1+mem1.n)==(unsigned)nKey1 ); + testcase( (d1+mem1.n+1)==(unsigned)nKey1 ); + if( (d1+mem1.n) > (unsigned)nKey1 + || (pKeyInfo = pPKey2->pKeyInfo)->nAllField<=i + ){ + pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; + return 0; /* Corruption */ + }else if( pKeyInfo->aColl[i] ){ + mem1.enc = pKeyInfo->enc; + mem1.db = pKeyInfo->db; + mem1.flags = MEM_Str; + mem1.z = (char*)&aKey1[d1]; + rc = vdbeCompareMemString( + &mem1, pRhs, pKeyInfo->aColl[i], &pPKey2->errCode + ); + }else{ + int nCmp = MIN(mem1.n, pRhs->n); + rc = memcmp(&aKey1[d1], pRhs->z, nCmp); + if( rc==0 ) rc = mem1.n - pRhs->n; + } + } + } - if( pSrcDb==pDestDb ){ - sqlcipher_sqlite3ErrorWithMsg( - pDestDb, SQLITE_ERROR, "source and destination must be distinct" - ); - p = 0; - }else { - /* Allocate space for a new sqlcipher_sqlite3_backup object... - ** EVIDENCE-OF: R-64852-21591 The sqlcipher_sqlite3_backup object is created by a - ** call to sqlcipher_sqlite3_backup_init() and is destroyed by a call to - ** sqlcipher_sqlite3_backup_finish(). */ - p = (sqlcipher_sqlite3_backup *)sqlcipher_sqlite3MallocZero(sizeof(sqlcipher_sqlite3_backup)); - if( !p ){ - sqlcipher_sqlite3Error(pDestDb, SQLITE_NOMEM_BKPT); + /* RHS is a blob */ + else if( pRhs->flags & MEM_Blob ){ + assert( (pRhs->flags & MEM_Zero)==0 || pRhs->n==0 ); + getVarint32NR(&aKey1[idx1], serial_type); + testcase( serial_type==12 ); + if( serial_type<12 || (serial_type & 0x01) ){ + rc = -1; + }else{ + int nStr = (serial_type - 12) / 2; + testcase( (d1+nStr)==(unsigned)nKey1 ); + testcase( (d1+nStr+1)==(unsigned)nKey1 ); + if( (d1+nStr) > (unsigned)nKey1 ){ + pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; + return 0; /* Corruption */ + }else if( pRhs->flags & MEM_Zero ){ + if( !isAllZero((const char*)&aKey1[d1],nStr) ){ + rc = 1; + }else{ + rc = nStr - pRhs->u.nZero; + } + }else{ + int nCmp = MIN(nStr, pRhs->n); + rc = memcmp(&aKey1[d1], pRhs->z, nCmp); + if( rc==0 ) rc = nStr - pRhs->n; + } + } } - } - /* If the allocation succeeded, populate the new object. */ - if( p ){ - p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb); - p->pDest = findBtree(pDestDb, pDestDb, zDestDb); - p->pDestDb = pDestDb; - p->pSrcDb = pSrcDb; - p->iNext = 1; - p->isAttached = 0; + /* RHS is null */ + else{ + serial_type = aKey1[idx1]; + rc = (serial_type!=0); + } - if( 0==p->pSrc || 0==p->pDest - || checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK - ){ - /* One (or both) of the named databases did not exist or an OOM - ** error was hit. Or there is a transaction open on the destination - ** database. The error has already been written into the pDestDb - ** handle. All that is left to do here is free the sqlcipher_sqlite3_backup - ** structure. */ - sqlcipher_sqlite3_free(p); - p = 0; + if( rc!=0 ){ + int sortFlags = pPKey2->pKeyInfo->aSortFlags[i]; + if( sortFlags ){ + if( (sortFlags & KEYINFO_ORDER_BIGNULL)==0 + || ((sortFlags & KEYINFO_ORDER_DESC) + !=(serial_type==0 || (pRhs->flags&MEM_Null))) + ){ + rc = -rc; + } + } + assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) ); + assert( mem1.szMalloc==0 ); /* See comment below */ + return rc; } - } - if( p ){ - p->pSrc->nBackup++; - } - sqlcipher_sqlite3_mutex_leave(pDestDb->mutex); - sqlcipher_sqlite3_mutex_leave(pSrcDb->mutex); - return p; -} + i++; + if( i==pPKey2->nField ) break; + pRhs++; + d1 += sqlcipher_sqlite3VdbeSerialTypeLen(serial_type); + idx1 += sqlcipher_sqlite3VarintLen(serial_type); + }while( idx1<(unsigned)szHdr1 && d1<=(unsigned)nKey1 ); -/* -** Argument rc is an SQLite error code. Return true if this error is -** considered fatal if encountered during a backup operation. All errors -** are considered fatal except for SQLITE_BUSY and SQLITE_LOCKED. -*/ -static int isFatalError(int rc){ - return (rc!=SQLITE_OK && rc!=SQLITE_BUSY && ALWAYS(rc!=SQLITE_LOCKED)); + /* No memory allocation is ever used on mem1. Prove this using + ** the following assert(). If the assert() fails, it indicates a + ** memory leak and a need to call sqlcipher_sqlite3VdbeMemRelease(&mem1). */ + assert( mem1.szMalloc==0 ); + + /* rc==0 here means that one or both of the keys ran out of fields and + ** all the fields up to that point were equal. Return the default_rc + ** value. */ + assert( CORRUPT_DB + || vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc) + || pPKey2->pKeyInfo->db->mallocFailed + ); + pPKey2->eqSeen = 1; + return pPKey2->default_rc; +} +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeRecordCompare( + int nKey1, const void *pKey1, /* Left key */ + UnpackedRecord *pPKey2 /* Right key */ +){ + return sqlcipher_sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0); } + /* -** Parameter zSrcData points to a buffer containing the data for -** page iSrcPg from the source database. Copy this data into the -** destination database. +** This function is an optimized version of sqlcipher_sqlite3VdbeRecordCompare() +** that (a) the first field of pPKey2 is an integer, and (b) the +** size-of-header varint at the start of (pKey1/nKey1) fits in a single +** byte (i.e. is less than 128). +** +** To avoid concerns about buffer overreads, this routine is only used +** on schemas where the maximum valid header size is 63 bytes or less. */ -static int backupOnePage( - sqlcipher_sqlite3_backup *p, /* Backup handle */ - Pgno iSrcPg, /* Source database page to backup */ - const u8 *zSrcData, /* Source database page data */ - int bUpdate /* True for an update, false otherwise */ +static int vdbeRecordCompareInt( + int nKey1, const void *pKey1, /* Left key */ + UnpackedRecord *pPKey2 /* Right key */ ){ - Pager * const pDestPager = sqlcipher_sqlite3BtreePager(p->pDest); - const int nSrcPgsz = sqlcipher_sqlite3BtreeGetPageSize(p->pSrc); - int nDestPgsz = sqlcipher_sqlite3BtreeGetPageSize(p->pDest); - const int nCopy = MIN(nSrcPgsz, nDestPgsz); - const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is - ** guaranteed that the shared-mutex is held by this thread, handle - ** p->pSrc may not actually be the owner. */ - int nSrcReserve = sqlcipher_sqlite3BtreeGetReserveNoMutex(p->pSrc); - int nDestReserve = sqlcipher_sqlite3BtreeGetRequestedReserve(p->pDest); -#endif -/* END SQLCIPHER */ - int rc = SQLITE_OK; - i64 iOff; - - assert( sqlcipher_sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 ); - assert( p->bDestLocked ); - assert( !isFatalError(p->rc) ); - assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); - assert( zSrcData ); - - /* Catch the case where the destination is an in-memory database and the - ** page sizes of the source and destination differ. - */ - if( nSrcPgsz!=nDestPgsz && sqlcipher_sqlite3PagerIsMemdb(pDestPager) ){ - rc = SQLITE_READONLY; - } - -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - /* Backup is not possible if the page size of the destination is changing - ** and a codec is in use. - */ - if( nSrcPgsz!=nDestPgsz && sqlcipher_sqlite3PagerGetCodec(pDestPager)!=0 ){ - rc = SQLITE_READONLY; - } + const u8 *aKey = &((const u8*)pKey1)[*(const u8*)pKey1 & 0x3F]; + int serial_type = ((const u8*)pKey1)[1]; + int res; + u32 y; + u64 x; + i64 v; + i64 lhs; - /* Backup is not possible if the number of bytes of reserve space differ - ** between source and destination. If there is a difference, try to - ** fix the destination to agree with the source. If that is not possible, - ** then the backup cannot proceed. - */ - if( nSrcReserve!=nDestReserve ){ - u32 newPgsz = nSrcPgsz; - rc = sqlcipher_sqlite3PagerSetPagesize(pDestPager, &newPgsz, nSrcReserve); - if( rc==SQLITE_OK && newPgsz!=(u32)nSrcPgsz ) rc = SQLITE_READONLY; - } -#endif -/* END SQLCIPHER */ + vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); + assert( (*(u8*)pKey1)<=0x3F || CORRUPT_DB ); + switch( serial_type ){ + case 1: { /* 1-byte signed integer */ + lhs = ONE_BYTE_INT(aKey); + testcase( lhs<0 ); + break; + } + case 2: { /* 2-byte signed integer */ + lhs = TWO_BYTE_INT(aKey); + testcase( lhs<0 ); + break; + } + case 3: { /* 3-byte signed integer */ + lhs = THREE_BYTE_INT(aKey); + testcase( lhs<0 ); + break; + } + case 4: { /* 4-byte signed integer */ + y = FOUR_BYTE_UINT(aKey); + lhs = (i64)*(int*)&y; + testcase( lhs<0 ); + break; + } + case 5: { /* 6-byte signed integer */ + lhs = FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey); + testcase( lhs<0 ); + break; + } + case 6: { /* 8-byte signed integer */ + x = FOUR_BYTE_UINT(aKey); + x = (x<<32) | FOUR_BYTE_UINT(aKey+4); + lhs = *(i64*)&x; + testcase( lhs<0 ); + break; + } + case 8: + lhs = 0; + break; + case 9: + lhs = 1; + break; - /* This loop runs once for each destination page spanned by the source - ** page. For each iteration, variable iOff is set to the byte offset - ** of the destination page. - */ - for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOffpDest->pBt) ) continue; - if( SQLITE_OK==(rc = sqlcipher_sqlite3PagerGet(pDestPager, iDest, &pDestPg, 0)) - && SQLITE_OK==(rc = sqlcipher_sqlite3PagerWrite(pDestPg)) - ){ - const u8 *zIn = &zSrcData[iOff%nSrcPgsz]; - u8 *zDestData = sqlcipher_sqlite3PagerGetData(pDestPg); - u8 *zOut = &zDestData[iOff%nDestPgsz]; + /* This case could be removed without changing the results of running + ** this code. Including it causes gcc to generate a faster switch + ** statement (since the range of switch targets now starts at zero and + ** is contiguous) but does not cause any duplicate code to be generated + ** (as gcc is clever enough to combine the two like cases). Other + ** compilers might be similar. */ + case 0: case 7: + return sqlcipher_sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2); - /* Copy the data from the source page into the destination page. - ** Then clear the Btree layer MemPage.isInit flag. Both this module - ** and the pager code use this trick (clearing the first byte - ** of the page 'extra' space to invalidate the Btree layers - ** cached parse of the page). MemPage.isInit is marked - ** "MUST BE FIRST" for this purpose. - */ - memcpy(zOut, zIn, nCopy); - ((u8 *)sqlcipher_sqlite3PagerGetExtra(pDestPg))[0] = 0; - if( iOff==0 && bUpdate==0 ){ - sqlcipher_sqlite3Put4byte(&zOut[28], sqlcipher_sqlite3BtreeLastPage(p->pSrc)); - } - } - sqlcipher_sqlite3PagerUnref(pDestPg); + default: + return sqlcipher_sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2); } - return rc; -} - -/* -** If pFile is currently larger than iSize bytes, then truncate it to -** exactly iSize bytes. If pFile is not larger than iSize bytes, then -** this function is a no-op. -** -** Return SQLITE_OK if everything is successful, or an SQLite error -** code if an error occurs. -*/ -static int backupTruncateFile(sqlcipher_sqlite3_file *pFile, i64 iSize){ - i64 iCurrent; - int rc = sqlcipher_sqlite3OsFileSize(pFile, &iCurrent); - if( rc==SQLITE_OK && iCurrent>iSize ){ - rc = sqlcipher_sqlite3OsTruncate(pFile, iSize); + assert( pPKey2->u.i == pPKey2->aMem[0].u.i ); + v = pPKey2->u.i; + if( v>lhs ){ + res = pPKey2->r1; + }else if( vr2; + }else if( pPKey2->nField>1 ){ + /* The first fields of the two keys are equal. Compare the trailing + ** fields. */ + res = sqlcipher_sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); + }else{ + /* The first fields of the two keys are equal and there are no trailing + ** fields. Return pPKey2->default_rc in this case. */ + res = pPKey2->default_rc; + pPKey2->eqSeen = 1; } - return rc; -} -/* -** Register this backup object with the associated source pager for -** callbacks when pages are changed or the cache invalidated. -*/ -static void attachBackupObject(sqlcipher_sqlite3_backup *p){ - sqlcipher_sqlite3_backup **pp; - assert( sqlcipher_sqlite3BtreeHoldsMutex(p->pSrc) ); - pp = sqlcipher_sqlite3PagerBackupPtr(sqlcipher_sqlite3BtreePager(p->pSrc)); - p->pNext = *pp; - *pp = p; - p->isAttached = 1; + assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) ); + return res; } /* -** Copy nPage pages from the source b-tree to the destination. +** This function is an optimized version of sqlcipher_sqlite3VdbeRecordCompare() +** that (a) the first field of pPKey2 is a string, that (b) the first field +** uses the collation sequence BINARY and (c) that the size-of-header varint +** at the start of (pKey1/nKey1) fits in a single byte. */ -SQLITE_API int sqlcipher_sqlite3_backup_step(sqlcipher_sqlite3_backup *p, int nPage){ - int rc; - int destMode; /* Destination journal mode */ - int pgszSrc = 0; /* Source page size */ - int pgszDest = 0; /* Destination page size */ - -#ifdef SQLITE_ENABLE_API_ARMOR - if( p==0 ) return SQLITE_MISUSE_BKPT; -#endif - sqlcipher_sqlite3_mutex_enter(p->pSrcDb->mutex); - sqlcipher_sqlite3BtreeEnter(p->pSrc); - if( p->pDestDb ){ - sqlcipher_sqlite3_mutex_enter(p->pDestDb->mutex); - } - - rc = p->rc; - if( !isFatalError(rc) ){ - Pager * const pSrcPager = sqlcipher_sqlite3BtreePager(p->pSrc); /* Source pager */ - Pager * const pDestPager = sqlcipher_sqlite3BtreePager(p->pDest); /* Dest pager */ - int ii; /* Iterator variable */ - int nSrcPage = -1; /* Size of source db in pages */ - int bCloseTrans = 0; /* True if src db requires unlocking */ +static int vdbeRecordCompareString( + int nKey1, const void *pKey1, /* Left key */ + UnpackedRecord *pPKey2 /* Right key */ +){ + const u8 *aKey1 = (const u8*)pKey1; + int serial_type; + int res; - /* If the source pager is currently in a write-transaction, return - ** SQLITE_BUSY immediately. - */ - if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){ - rc = SQLITE_BUSY; - }else{ - rc = SQLITE_OK; - } + assert( pPKey2->aMem[0].flags & MEM_Str ); + assert( pPKey2->aMem[0].n == pPKey2->n ); + assert( pPKey2->aMem[0].z == pPKey2->u.z ); + vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); + serial_type = (signed char)(aKey1[1]); - /* If there is no open read-transaction on the source database, open - ** one now. If a transaction is opened here, then it will be closed - ** before this function exits. - */ - if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlcipher_sqlite3BtreeTxnState(p->pSrc) ){ - rc = sqlcipher_sqlite3BtreeBeginTrans(p->pSrc, 0, 0); - bCloseTrans = 1; +vrcs_restart: + if( serial_type<12 ){ + if( serial_type<0 ){ + sqlcipher_sqlite3GetVarint32(&aKey1[1], (u32*)&serial_type); + if( serial_type>=12 ) goto vrcs_restart; + assert( CORRUPT_DB ); } + res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ + }else if( !(serial_type & 0x01) ){ + res = pPKey2->r2; /* (pKey1/nKey1) is a blob */ + }else{ + int nCmp; + int nStr; + int szHdr = aKey1[0]; - /* If the destination database has not yet been locked (i.e. if this - ** is the first call to backup_step() for the current backup operation), - ** try to set its page size to the same as the source database. This - ** is especially important on ZipVFS systems, as in that case it is - ** not possible to create a database file that uses one page size by - ** writing to it with another. */ - if( p->bDestLocked==0 && rc==SQLITE_OK && setDestPgsz(p)==SQLITE_NOMEM ){ - rc = SQLITE_NOMEM; + nStr = (serial_type-12) / 2; + if( (szHdr + nStr) > nKey1 ){ + pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; + return 0; /* Corruption */ } + nCmp = MIN( pPKey2->n, nStr ); + res = memcmp(&aKey1[szHdr], pPKey2->u.z, nCmp); - /* Lock the destination database, if it is not locked already. */ - if( SQLITE_OK==rc && p->bDestLocked==0 - && SQLITE_OK==(rc = sqlcipher_sqlite3BtreeBeginTrans(p->pDest, 2, - (int*)&p->iDestSchema)) - ){ - p->bDestLocked = 1; + if( res>0 ){ + res = pPKey2->r2; + }else if( res<0 ){ + res = pPKey2->r1; + }else{ + res = nStr - pPKey2->n; + if( res==0 ){ + if( pPKey2->nField>1 ){ + res = sqlcipher_sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); + }else{ + res = pPKey2->default_rc; + pPKey2->eqSeen = 1; + } + }else if( res>0 ){ + res = pPKey2->r2; + }else{ + res = pPKey2->r1; + } } + } - /* Do not allow backup if the destination database is in WAL mode - ** and the page sizes are different between source and destination */ - pgszSrc = sqlcipher_sqlite3BtreeGetPageSize(p->pSrc); - pgszDest = sqlcipher_sqlite3BtreeGetPageSize(p->pDest); - destMode = sqlcipher_sqlite3PagerGetJournalMode(sqlcipher_sqlite3BtreePager(p->pDest)); - if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ - rc = SQLITE_READONLY; - } + assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) + || CORRUPT_DB + || pPKey2->pKeyInfo->db->mallocFailed + ); + return res; +} - /* Now that there is a read-lock on the source database, query the - ** source pager for the number of pages in the database. - */ - nSrcPage = (int)sqlcipher_sqlite3BtreeLastPage(p->pSrc); - assert( nSrcPage>=0 ); - for(ii=0; (nPage<0 || iiiNext<=(Pgno)nSrcPage && !rc; ii++){ - const Pgno iSrcPg = p->iNext; /* Source page number */ - if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){ - DbPage *pSrcPg; /* Source page object */ - rc = sqlcipher_sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg,PAGER_GET_READONLY); - if( rc==SQLITE_OK ){ - rc = backupOnePage(p, iSrcPg, sqlcipher_sqlite3PagerGetData(pSrcPg), 0); - sqlcipher_sqlite3PagerUnref(pSrcPg); - } +/* +** Return a pointer to an sqlcipher_sqlite3VdbeRecordCompare() compatible function +** suitable for comparing serialized records to the unpacked record passed +** as the only argument. +*/ +SQLITE_PRIVATE RecordCompare sqlcipher_sqlite3VdbeFindCompare(UnpackedRecord *p){ + /* varintRecordCompareInt() and varintRecordCompareString() both assume + ** that the size-of-header varint that occurs at the start of each record + ** fits in a single byte (i.e. is 127 or less). varintRecordCompareInt() + ** also assumes that it is safe to overread a buffer by at least the + ** maximum possible legal header size plus 8 bytes. Because there is + ** guaranteed to be at least 74 (but not 136) bytes of padding following each + ** buffer passed to varintRecordCompareInt() this makes it convenient to + ** limit the size of the header to 64 bytes in cases where the first field + ** is an integer. + ** + ** The easiest way to enforce this limit is to consider only records with + ** 13 fields or less. If the first field is an integer, the maximum legal + ** header size is (12*5 + 1 + 1) bytes. */ + if( p->pKeyInfo->nAllField<=13 ){ + int flags = p->aMem[0].flags; + if( p->pKeyInfo->aSortFlags[0] ){ + if( p->pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL ){ + return sqlcipher_sqlite3VdbeRecordCompare; } - p->iNext++; + p->r1 = 1; + p->r2 = -1; + }else{ + p->r1 = -1; + p->r2 = 1; } - if( rc==SQLITE_OK ){ - p->nPagecount = nSrcPage; - p->nRemaining = nSrcPage+1-p->iNext; - if( p->iNext>(Pgno)nSrcPage ){ - rc = SQLITE_DONE; - }else if( !p->isAttached ){ - attachBackupObject(p); - } + if( (flags & MEM_Int) ){ + p->u.i = p->aMem[0].u.i; + return vdbeRecordCompareInt; + } + testcase( flags & MEM_Real ); + testcase( flags & MEM_Null ); + testcase( flags & MEM_Blob ); + if( (flags & (MEM_Real|MEM_IntReal|MEM_Null|MEM_Blob))==0 + && p->pKeyInfo->aColl[0]==0 + ){ + assert( flags & MEM_Str ); + p->u.z = p->aMem[0].z; + p->n = p->aMem[0].n; + return vdbeRecordCompareString; } + } - /* Update the schema version field in the destination database. This - ** is to make sure that the schema-version really does change in - ** the case where the source and destination databases have the - ** same schema version. - */ - if( rc==SQLITE_DONE ){ - if( nSrcPage==0 ){ - rc = sqlcipher_sqlite3BtreeNewDb(p->pDest); - nSrcPage = 1; - } - if( rc==SQLITE_OK || rc==SQLITE_DONE ){ - rc = sqlcipher_sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1); - } - if( rc==SQLITE_OK ){ - if( p->pDestDb ){ - sqlcipher_sqlite3ResetAllSchemasOfConnection(p->pDestDb); - } - if( destMode==PAGER_JOURNALMODE_WAL ){ - rc = sqlcipher_sqlite3BtreeSetVersion(p->pDest, 2); - } - } - if( rc==SQLITE_OK ){ - int nDestTruncate; - /* Set nDestTruncate to the final number of pages in the destination - ** database. The complication here is that the destination page - ** size may be different to the source page size. - ** - ** If the source page size is smaller than the destination page size, - ** round up. In this case the call to sqlcipher_sqlite3OsTruncate() below will - ** fix the size of the file. However it is important to call - ** sqlcipher_sqlite3PagerTruncateImage() here so that any pages in the - ** destination file that lie beyond the nDestTruncate page mark are - ** journalled by PagerCommitPhaseOne() before they are destroyed - ** by the file truncation. - */ - assert( pgszSrc==sqlcipher_sqlite3BtreeGetPageSize(p->pSrc) ); - assert( pgszDest==sqlcipher_sqlite3BtreeGetPageSize(p->pDest) ); - if( pgszSrcpDest->pBt) ){ - nDestTruncate--; - } - }else{ - nDestTruncate = nSrcPage * (pgszSrc/pgszDest); - } - assert( nDestTruncate>0 ); + return sqlcipher_sqlite3VdbeRecordCompare; +} - if( pgszSrc= iSize || ( - nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1) - && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest - )); + /* Get the size of the index entry. Only indices entries of less + ** than 2GiB are support - anything large must be database corruption. + ** Any corruption is detected in sqlcipher_sqlite3BtreeParseCellPtr(), though, so + ** this code can safely assume that nCellKey is 32-bits + */ + assert( sqlcipher_sqlite3BtreeCursorIsValid(pCur) ); + nCellKey = sqlcipher_sqlite3BtreePayloadSize(pCur); + assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey ); - /* This block ensures that all data required to recreate the original - ** database has been stored in the journal for pDestPager and the - ** journal synced to disk. So at this point we may safely modify - ** the database file in any way, knowing that if a power failure - ** occurs, the original database will be reconstructed from the - ** journal file. */ - sqlcipher_sqlite3PagerPagecount(pDestPager, &nDstPage); - for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){ - if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){ - DbPage *pPg; - rc = sqlcipher_sqlite3PagerGet(pDestPager, iPg, &pPg, 0); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3PagerWrite(pPg); - sqlcipher_sqlite3PagerUnref(pPg); - } - } - } - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3PagerCommitPhaseOne(pDestPager, 0, 1); - } + /* Read in the complete content of the index entry */ + sqlcipher_sqlite3VdbeMemInit(&m, db, 0); + rc = sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m); + if( rc ){ + return rc; + } - /* Write the extra pages and truncate the database file as required */ - iEnd = MIN(PENDING_BYTE + pgszDest, iSize); - for( - iOff=PENDING_BYTE+pgszSrc; - rc==SQLITE_OK && iOff0x7fffffff ); + assert( m.n>=0 ); + if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){ + goto idx_rowid_corruption; + } - /* Sync the database file to disk. */ - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3PagerSync(pDestPager, 0); - } - }else{ - sqlcipher_sqlite3PagerTruncateImage(pDestPager, nDestTruncate); - rc = sqlcipher_sqlite3PagerCommitPhaseOne(pDestPager, 0, 0); - } + /* The last field of the index should be an integer - the ROWID. + ** Verify that the last entry really is an integer. */ + getVarint32NR((u8*)&m.z[szHdr-1], typeRowid); + testcase( typeRowid==1 ); + testcase( typeRowid==2 ); + testcase( typeRowid==3 ); + testcase( typeRowid==4 ); + testcase( typeRowid==5 ); + testcase( typeRowid==6 ); + testcase( typeRowid==8 ); + testcase( typeRowid==9 ); + if( unlikely(typeRowid<1 || typeRowid>9 || typeRowid==7) ){ + goto idx_rowid_corruption; + } + lenRowid = sqlcipher_sqlite3SmallTypeSizes[typeRowid]; + testcase( (u32)m.n==szHdr+lenRowid ); + if( unlikely((u32)m.npDest, 0)) - ){ - rc = SQLITE_DONE; - } - } - } + /* Fetch the integer off the end of the index record */ + sqlcipher_sqlite3VdbeSerialGet((u8*)&m.z[m.n-lenRowid], typeRowid, &v); + *rowid = v.u.i; + sqlcipher_sqlite3VdbeMemReleaseMalloc(&m); + return SQLITE_OK; - /* If bCloseTrans is true, then this function opened a read transaction - ** on the source database. Close the read transaction here. There is - ** no need to check the return values of the btree methods here, as - ** "committing" a read-only transaction cannot fail. - */ - if( bCloseTrans ){ - TESTONLY( int rc2 ); - TESTONLY( rc2 = ) sqlcipher_sqlite3BtreeCommitPhaseOne(p->pSrc, 0); - TESTONLY( rc2 |= ) sqlcipher_sqlite3BtreeCommitPhaseTwo(p->pSrc, 0); - assert( rc2==SQLITE_OK ); - } + /* Jump here if database corruption is detected after m has been + ** allocated. Free the m object and return SQLITE_CORRUPT. */ +idx_rowid_corruption: + testcase( m.szMalloc!=0 ); + sqlcipher_sqlite3VdbeMemReleaseMalloc(&m); + return SQLITE_CORRUPT_BKPT; +} - if( rc==SQLITE_IOERR_NOMEM ){ - rc = SQLITE_NOMEM_BKPT; - } - p->rc = rc; +/* +** Compare the key of the index entry that cursor pC is pointing to against +** the key string in pUnpacked. Write into *pRes a number +** that is negative, zero, or positive if pC is less than, equal to, +** or greater than pUnpacked. Return SQLITE_OK on success. +** +** pUnpacked is either created without a rowid or is truncated so that it +** omits the rowid at the end. The rowid at the end of the index entry +** is ignored as well. Hence, this routine only compares the prefixes +** of the keys prior to the final rowid, not the entire key. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeIdxKeyCompare( + sqlcipher_sqlite3 *db, /* Database connection */ + VdbeCursor *pC, /* The cursor to compare against */ + UnpackedRecord *pUnpacked, /* Unpacked version of key */ + int *res /* Write the comparison result here */ +){ + i64 nCellKey = 0; + int rc; + BtCursor *pCur; + Mem m; + + assert( pC->eCurType==CURTYPE_BTREE ); + pCur = pC->uc.pCursor; + assert( sqlcipher_sqlite3BtreeCursorIsValid(pCur) ); + nCellKey = sqlcipher_sqlite3BtreePayloadSize(pCur); + /* nCellKey will always be between 0 and 0xffffffff because of the way + ** that btreeParseCellPtr() and sqlcipher_sqlite3GetVarint32() are implemented */ + if( nCellKey<=0 || nCellKey>0x7fffffff ){ + *res = 0; + return SQLITE_CORRUPT_BKPT; } - if( p->pDestDb ){ - sqlcipher_sqlite3_mutex_leave(p->pDestDb->mutex); + sqlcipher_sqlite3VdbeMemInit(&m, db, 0); + rc = sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m); + if( rc ){ + return rc; } - sqlcipher_sqlite3BtreeLeave(p->pSrc); - sqlcipher_sqlite3_mutex_leave(p->pSrcDb->mutex); - return rc; + *res = sqlcipher_sqlite3VdbeRecordCompareWithSkip(m.n, m.z, pUnpacked, 0); + sqlcipher_sqlite3VdbeMemReleaseMalloc(&m); + return SQLITE_OK; } /* -** Release all resources associated with an sqlcipher_sqlite3_backup* handle. +** This routine sets the value to be returned by subsequent calls to +** sqlcipher_sqlite3_changes() on the database handle 'db'. */ -SQLITE_API int sqlcipher_sqlite3_backup_finish(sqlcipher_sqlite3_backup *p){ - sqlcipher_sqlite3_backup **pp; /* Ptr to head of pagers backup list */ - sqlcipher_sqlite3 *pSrcDb; /* Source database connection */ - int rc; /* Value to return */ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetChanges(sqlcipher_sqlite3 *db, i64 nChange){ + assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); + db->nChange = nChange; + db->nTotalChange += nChange; +} - /* Enter the mutexes */ - if( p==0 ) return SQLITE_OK; - pSrcDb = p->pSrcDb; - sqlcipher_sqlite3_mutex_enter(pSrcDb->mutex); - sqlcipher_sqlite3BtreeEnter(p->pSrc); - if( p->pDestDb ){ - sqlcipher_sqlite3_mutex_enter(p->pDestDb->mutex); - } +/* +** Set a flag in the vdbe to update the change counter when it is finalised +** or reset. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeCountChanges(Vdbe *v){ + v->changeCntOn = 1; +} - /* Detach this backup from the source pager. */ - if( p->pDestDb ){ - p->pSrc->nBackup--; - } - if( p->isAttached ){ - pp = sqlcipher_sqlite3PagerBackupPtr(sqlcipher_sqlite3BtreePager(p->pSrc)); - assert( pp!=0 ); - while( *pp!=p ){ - pp = &(*pp)->pNext; - assert( pp!=0 ); - } - *pp = p->pNext; +/* +** Mark every prepared statement associated with a database connection +** as expired. +** +** An expired statement means that recompilation of the statement is +** recommend. Statements expire when things happen that make their +** programs obsolete. Removing user-defined functions or collating +** sequences, or changing an authorization function are the types of +** things that make prepared statements obsolete. +** +** If iCode is 1, then expiration is advisory. The statement should +** be reprepared before being restarted, but if it is already running +** it is allowed to run to completion. +** +** Internally, this function just sets the Vdbe.expired flag on all +** prepared statements. The flag is set to 1 for an immediate expiration +** and set to 2 for an advisory expiration. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3ExpirePreparedStatements(sqlcipher_sqlite3 *db, int iCode){ + Vdbe *p; + for(p = db->pVdbe; p; p=p->pNext){ + p->expired = iCode+1; } +} - /* If a transaction is still open on the Btree, roll it back. */ - sqlcipher_sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0); - - /* Set the error code of the destination database handle. */ - rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc; - if( p->pDestDb ){ - sqlcipher_sqlite3Error(p->pDestDb, rc); +/* +** Return the database associated with the Vdbe. +*/ +SQLITE_PRIVATE sqlcipher_sqlite3 *sqlcipher_sqlite3VdbeDb(Vdbe *v){ + return v->db; +} - /* Exit the mutexes and free the backup context structure. */ - sqlcipher_sqlite3LeaveMutexAndCloseZombie(p->pDestDb); - } - sqlcipher_sqlite3BtreeLeave(p->pSrc); - if( p->pDestDb ){ - /* EVIDENCE-OF: R-64852-21591 The sqlcipher_sqlite3_backup object is created by a - ** call to sqlcipher_sqlite3_backup_init() and is destroyed by a call to - ** sqlcipher_sqlite3_backup_finish(). */ - sqlcipher_sqlite3_free(p); - } - sqlcipher_sqlite3LeaveMutexAndCloseZombie(pSrcDb); - return rc; +/* +** Return the SQLITE_PREPARE flags for a Vdbe. +*/ +SQLITE_PRIVATE u8 sqlcipher_sqlite3VdbePrepareFlags(Vdbe *v){ + return v->prepFlags; } /* -** Return the number of pages still to be backed up as of the most recent -** call to sqlcipher_sqlite3_backup_step(). +** Return a pointer to an sqlcipher_sqlite3_value structure containing the value bound +** parameter iVar of VM v. Except, if the value is an SQL NULL, return +** 0 instead. Unless it is NULL, apply affinity aff (one of the SQLITE_AFF_* +** constants) to the value before returning it. +** +** The returned value must be freed by the caller using sqlcipher_sqlite3ValueFree(). */ -SQLITE_API int sqlcipher_sqlite3_backup_remaining(sqlcipher_sqlite3_backup *p){ -#ifdef SQLITE_ENABLE_API_ARMOR - if( p==0 ){ - (void)SQLITE_MISUSE_BKPT; - return 0; +SQLITE_PRIVATE sqlcipher_sqlite3_value *sqlcipher_sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff){ + assert( iVar>0 ); + if( v ){ + Mem *pMem = &v->aVar[iVar-1]; + assert( (v->db->flags & SQLITE_EnableQPSG)==0 ); + if( 0==(pMem->flags & MEM_Null) ){ + sqlcipher_sqlite3_value *pRet = sqlcipher_sqlite3ValueNew(v->db); + if( pRet ){ + sqlcipher_sqlite3VdbeMemCopy((Mem *)pRet, pMem); + sqlcipher_sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8); + } + return pRet; + } } -#endif - return p->nRemaining; + return 0; } /* -** Return the total number of pages in the source database as of the most -** recent call to sqlcipher_sqlite3_backup_step(). +** Configure SQL variable iVar so that binding a new value to it signals +** to sqlcipher_sqlite3_reoptimize() that re-preparing the statement may result +** in a better query plan. */ -SQLITE_API int sqlcipher_sqlite3_backup_pagecount(sqlcipher_sqlite3_backup *p){ -#ifdef SQLITE_ENABLE_API_ARMOR - if( p==0 ){ - (void)SQLITE_MISUSE_BKPT; - return 0; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetVarmask(Vdbe *v, int iVar){ + assert( iVar>0 ); + assert( (v->db->flags & SQLITE_EnableQPSG)==0 ); + if( iVar>=32 ){ + v->expmask |= 0x80000000; + }else{ + v->expmask |= ((u32)1 << (iVar-1)); } -#endif - return p->nPagecount; } /* -** This function is called after the contents of page iPage of the -** source database have been modified. If page iPage has already been -** copied into the destination database, then the data written to the -** destination is now invalidated. The destination copy of iPage needs -** to be updated with the new data before the backup operation is -** complete. +** Cause a function to throw an error if it was call from OP_PureFunc +** rather than OP_Function. ** -** It is assumed that the mutex associated with the BtShared object -** corresponding to the source database is held when this function is -** called. +** OP_PureFunc means that the function must be deterministic, and should +** throw an error if it is given inputs that would make it non-deterministic. +** This routine is invoked by date/time functions that use non-deterministic +** features such as 'now'. */ -static SQLITE_NOINLINE void backupUpdate( - sqlcipher_sqlite3_backup *p, - Pgno iPage, - const u8 *aData -){ - assert( p!=0 ); - do{ - assert( sqlcipher_sqlite3_mutex_held(p->pSrc->pBt->mutex) ); - if( !isFatalError(p->rc) && iPageiNext ){ - /* The backup process p has already copied page iPage. But now it - ** has been modified by a transaction on the source pager. Copy - ** the new data into the backup. - */ - int rc; - assert( p->pDestDb ); - sqlcipher_sqlite3_mutex_enter(p->pDestDb->mutex); - rc = backupOnePage(p, iPage, aData, 1); - sqlcipher_sqlite3_mutex_leave(p->pDestDb->mutex); - assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED ); - if( rc!=SQLITE_OK ){ - p->rc = rc; - } +SQLITE_PRIVATE int sqlcipher_sqlite3NotPureFunc(sqlcipher_sqlite3_context *pCtx){ + const VdbeOp *pOp; +#ifdef SQLITE_ENABLE_STAT4 + if( pCtx->pVdbe==0 ) return 1; +#endif + pOp = pCtx->pVdbe->aOp + pCtx->iOp; + if( pOp->opcode==OP_PureFunc ){ + const char *zContext; + char *zMsg; + if( pOp->p5 & NC_IsCheck ){ + zContext = "a CHECK constraint"; + }else if( pOp->p5 & NC_GenCol ){ + zContext = "a generated column"; + }else{ + zContext = "an index"; } - }while( (p = p->pNext)!=0 ); + zMsg = sqlcipher_sqlite3_mprintf("non-deterministic use of %s() in %s", + pCtx->pFunc->zName, zContext); + sqlcipher_sqlite3_result_error(pCtx, zMsg, -1); + sqlcipher_sqlite3_free(zMsg); + return 0; + } + return 1; } -SQLITE_PRIVATE void sqlcipher_sqlite3BackupUpdate(sqlcipher_sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){ - if( pBackup ) backupUpdate(pBackup, iPage, aData); + +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Transfer error message text from an sqlcipher_sqlite3_vtab.zErrMsg (text stored +** in memory obtained from sqlcipher_sqlite3_malloc) into a Vdbe.zErrMsg (text stored +** in memory obtained from sqlcipher_sqlite3DbMalloc). +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VtabImportErrmsg(Vdbe *p, sqlcipher_sqlite3_vtab *pVtab){ + if( pVtab->zErrMsg ){ + sqlcipher_sqlite3 *db = p->db; + sqlcipher_sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = sqlcipher_sqlite3DbStrDup(db, pVtab->zErrMsg); + sqlcipher_sqlite3_free(pVtab->zErrMsg); + pVtab->zErrMsg = 0; + } } +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* -** Restart the backup process. This is called when the pager layer -** detects that the database has been modified by an external database -** connection. In this case there is no way of knowing which of the -** pages that have been copied into the destination database are still -** valid and which are not, so the entire process needs to be restarted. +** If the second argument is not NULL, release any allocations associated +** with the memory cells in the p->aMem[] array. Also free the UnpackedRecord +** structure itself, using sqlcipher_sqlite3DbFree(). ** -** It is assumed that the mutex associated with the BtShared object -** corresponding to the source database is held when this function is -** called. +** This function is used to free UnpackedRecord structures allocated by +** the vdbeUnpackRecord() function found in vdbeapi.c. */ -SQLITE_PRIVATE void sqlcipher_sqlite3BackupRestart(sqlcipher_sqlite3_backup *pBackup){ - sqlcipher_sqlite3_backup *p; /* Iterator variable */ - for(p=pBackup; p; p=p->pNext){ - assert( sqlcipher_sqlite3_mutex_held(p->pSrc->pBt->mutex) ); - p->iNext = 1; +static void vdbeFreeUnpacked(sqlcipher_sqlite3 *db, int nField, UnpackedRecord *p){ + if( p ){ + int i; + for(i=0; iaMem[i]; + if( pMem->zMalloc ) sqlcipher_sqlite3VdbeMemReleaseMalloc(pMem); + } + sqlcipher_sqlite3DbFreeNN(db, p); } } +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ -#ifndef SQLITE_OMIT_VACUUM +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* -** Copy the complete content of pBtFrom into pBtTo. A transaction -** must be active for both files. -** -** The size of file pTo may be reduced by this operation. If anything -** goes wrong, the transaction on pTo is rolled back. If successful, the -** transaction is committed before returning. +** Invoke the pre-update hook. If this is an UPDATE or DELETE pre-update call, +** then cursor passed as the second argument should point to the row about +** to be update or deleted. If the application calls sqlcipher_sqlite3_preupdate_old(), +** the required value will be read from the row the cursor points to. */ -SQLITE_PRIVATE int sqlcipher_sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ - int rc; - sqlcipher_sqlite3_file *pFd; /* File descriptor for database pTo */ - sqlcipher_sqlite3_backup b; - sqlcipher_sqlite3BtreeEnter(pTo); - sqlcipher_sqlite3BtreeEnter(pFrom); +SQLITE_PRIVATE void sqlcipher_sqlite3VdbePreUpdateHook( + Vdbe *v, /* Vdbe pre-update hook is invoked by */ + VdbeCursor *pCsr, /* Cursor to grab old.* values from */ + int op, /* SQLITE_INSERT, UPDATE or DELETE */ + const char *zDb, /* Database name */ + Table *pTab, /* Modified table */ + i64 iKey1, /* Initial key value */ + int iReg, /* Register for new.* record */ + int iBlobWrite +){ + sqlcipher_sqlite3 *db = v->db; + i64 iKey2; + PreUpdate preupdate; + const char *zTbl = pTab->zName; + static const u8 fakeSortOrder = 0; - assert( sqlcipher_sqlite3BtreeTxnState(pTo)==SQLITE_TXN_WRITE ); - pFd = sqlcipher_sqlite3PagerFile(sqlcipher_sqlite3BtreePager(pTo)); - if( pFd->pMethods ){ - i64 nByte = sqlcipher_sqlite3BtreeGetPageSize(pFrom)*(i64)sqlcipher_sqlite3BtreeLastPage(pFrom); - rc = sqlcipher_sqlite3OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte); - if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; - if( rc ) goto copy_finished; + assert( db->pPreUpdate==0 ); + memset(&preupdate, 0, sizeof(PreUpdate)); + if( HasRowid(pTab)==0 ){ + iKey1 = iKey2 = 0; + preupdate.pPk = sqlcipher_sqlite3PrimaryKeyIndex(pTab); + }else{ + if( op==SQLITE_UPDATE ){ + iKey2 = v->aMem[iReg].u.i; + }else{ + iKey2 = iKey1; + } } - /* Set up an sqlcipher_sqlite3_backup object. sqlcipher_sqlite3_backup.pDestDb must be set - ** to 0. This is used by the implementations of sqlcipher_sqlite3_backup_step() - ** and sqlcipher_sqlite3_backup_finish() to detect that they are being called - ** from this function, not directly by the user. - */ - memset(&b, 0, sizeof(b)); - b.pSrcDb = pFrom->db; - b.pSrc = pFrom; - b.pDest = pTo; - b.iNext = 1; - -/* BEGIN SQLCIPHER */ -#ifdef SQLITE_HAS_CODEC - sqlcipher_sqlite3PagerAlignReserve(sqlcipher_sqlite3BtreePager(pTo), sqlcipher_sqlite3BtreePager(pFrom)); -#endif -/* END SQLCIPHER */ + assert( pCsr!=0 ); + assert( pCsr->eCurType==CURTYPE_BTREE ); + assert( pCsr->nField==pTab->nCol + || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1) + ); - /* 0x7FFFFFFF is the hard limit for the number of pages in a database - ** file. By passing this as the number of pages to copy to - ** sqlcipher_sqlite3_backup_step(), we can guarantee that the copy finishes - ** within a single call (unless an error occurs). The assert() statement - ** checks this assumption - (p->rc) should be set to either SQLITE_DONE - ** or an error code. */ - sqlcipher_sqlite3_backup_step(&b, 0x7FFFFFFF); - assert( b.rc!=SQLITE_OK ); + preupdate.v = v; + preupdate.pCsr = pCsr; + preupdate.op = op; + preupdate.iNewReg = iReg; + preupdate.keyinfo.db = db; + preupdate.keyinfo.enc = ENC(db); + preupdate.keyinfo.nKeyField = pTab->nCol; + preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder; + preupdate.iKey1 = iKey1; + preupdate.iKey2 = iKey2; + preupdate.pTab = pTab; + preupdate.iBlobWrite = iBlobWrite; - rc = sqlcipher_sqlite3_backup_finish(&b); - if( rc==SQLITE_OK ){ - pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; - }else{ - sqlcipher_sqlite3PagerClearCache(sqlcipher_sqlite3BtreePager(b.pDest)); + db->pPreUpdate = &preupdate; + db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); + db->pPreUpdate = 0; + sqlcipher_sqlite3DbFree(db, preupdate.aRecord); + vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); + vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); + if( preupdate.aNew ){ + int i; + for(i=0; inField; i++){ + sqlcipher_sqlite3VdbeMemRelease(&preupdate.aNew[i]); + } + sqlcipher_sqlite3DbFreeNN(db, preupdate.aNew); } - - assert( sqlcipher_sqlite3BtreeTxnState(pTo)!=SQLITE_TXN_WRITE ); -copy_finished: - sqlcipher_sqlite3BtreeLeave(pFrom); - sqlcipher_sqlite3BtreeLeave(pTo); - return rc; } -#endif /* SQLITE_OMIT_VACUUM */ +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ -/************** End of backup.c **********************************************/ -/************** Begin file vdbemem.c *****************************************/ +/************** End of vdbeaux.c *********************************************/ +/************** Begin file vdbeapi.c *****************************************/ /* ** 2004 May 26 ** @@ -80800,1904 +86378,2358 @@ copy_finished: ** ************************************************************************* ** -** This file contains code use to manipulate "Mem" structure. A "Mem" -** stores a single value in the VDBE. Mem is an opaque structure visible -** only within the VDBE. Interface routines refer to a Mem using the -** name sqlite_value +** This file contains code use to implement APIs that are part of the +** VDBE. */ /* #include "sqliteInt.h" */ /* #include "vdbeInt.h" */ -/* True if X is a power of two. 0 is considered a power of two here. -** In other words, return true if X has at most one bit set. +#ifndef SQLITE_OMIT_DEPRECATED +/* +** Return TRUE (non-zero) of the statement supplied as an argument needs +** to be recompiled. A statement needs to be recompiled whenever the +** execution environment changes in a way that would alter the program +** that sqlcipher_sqlite3_prepare() generates. For example, if new functions or +** collating sequences are registered or if an authorizer function is +** added or changed. */ -#define ISPOWEROF2(X) (((X)&((X)-1))==0) +SQLITE_API int sqlcipher_sqlite3_expired(sqlcipher_sqlite3_stmt *pStmt){ + Vdbe *p = (Vdbe*)pStmt; + return p==0 || p->expired; +} +#endif -#ifdef SQLITE_DEBUG /* -** Check invariants on a Mem object. -** -** This routine is intended for use inside of assert() statements, like -** this: assert( sqlcipher_sqlite3VdbeCheckMemInvariants(pMem) ); +** Check on a Vdbe to make sure it has not been finalized. Log +** an error and return true if it has been finalized (or is otherwise +** invalid). Return false if it is ok. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCheckMemInvariants(Mem *p){ - /* If MEM_Dyn is set then Mem.xDel!=0. - ** Mem.xDel might not be initialized if MEM_Dyn is clear. - */ - assert( (p->flags & MEM_Dyn)==0 || p->xDel!=0 ); - - /* MEM_Dyn may only be set if Mem.szMalloc==0. In this way we - ** ensure that if Mem.szMalloc>0 then it is safe to do - ** Mem.z = Mem.zMalloc without having to check Mem.flags&MEM_Dyn. - ** That saves a few cycles in inner loops. */ - assert( (p->flags & MEM_Dyn)==0 || p->szMalloc==0 ); - - /* Cannot have more than one of MEM_Int, MEM_Real, or MEM_IntReal */ - assert( ISPOWEROF2(p->flags & (MEM_Int|MEM_Real|MEM_IntReal)) ); - - if( p->flags & MEM_Null ){ - /* Cannot be both MEM_Null and some other type */ - assert( (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob|MEM_Agg))==0 ); - - /* If MEM_Null is set, then either the value is a pure NULL (the usual - ** case) or it is a pointer set using sqlcipher_sqlite3_bind_pointer() or - ** sqlcipher_sqlite3_result_pointer(). If a pointer, then MEM_Term must also be - ** set. - */ - if( (p->flags & (MEM_Term|MEM_Subtype))==(MEM_Term|MEM_Subtype) ){ - /* This is a pointer type. There may be a flag to indicate what to - ** do with the pointer. */ - assert( ((p->flags&MEM_Dyn)!=0 ? 1 : 0) + - ((p->flags&MEM_Ephem)!=0 ? 1 : 0) + - ((p->flags&MEM_Static)!=0 ? 1 : 0) <= 1 ); - - /* No other bits set */ - assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype|MEM_FromBind - |MEM_Dyn|MEM_Ephem|MEM_Static))==0 ); - }else{ - /* A pure NULL might have other flags, such as MEM_Static, MEM_Dyn, - ** MEM_Ephem, MEM_Cleared, or MEM_Subtype */ - } +static int vdbeSafety(Vdbe *p){ + if( p->db==0 ){ + sqlcipher_sqlite3_log(SQLITE_MISUSE, "API called with finalized prepared statement"); + return 1; }else{ - /* The MEM_Cleared bit is only allowed on NULLs */ - assert( (p->flags & MEM_Cleared)==0 ); + return 0; } - - /* The szMalloc field holds the correct memory allocation size */ - assert( p->szMalloc==0 - || p->szMalloc==sqlcipher_sqlite3DbMallocSize(p->db,p->zMalloc) ); - - /* If p holds a string or blob, the Mem.z must point to exactly - ** one of the following: - ** - ** (1) Memory in Mem.zMalloc and managed by the Mem object - ** (2) Memory to be freed using Mem.xDel - ** (3) An ephemeral string or blob - ** (4) A static string or blob - */ - if( (p->flags & (MEM_Str|MEM_Blob)) && p->n>0 ){ - assert( - ((p->szMalloc>0 && p->z==p->zMalloc)? 1 : 0) + - ((p->flags&MEM_Dyn)!=0 ? 1 : 0) + - ((p->flags&MEM_Ephem)!=0 ? 1 : 0) + - ((p->flags&MEM_Static)!=0 ? 1 : 0) == 1 - ); +} +static int vdbeSafetyNotNull(Vdbe *p){ + if( p==0 ){ + sqlcipher_sqlite3_log(SQLITE_MISUSE, "API called with NULL prepared statement"); + return 1; + }else{ + return vdbeSafety(p); } - return 1; } -#endif +#ifndef SQLITE_OMIT_TRACE /* -** Render a Mem object which is one of MEM_Int, MEM_Real, or MEM_IntReal -** into a buffer. +** Invoke the profile callback. This routine is only called if we already +** know that the profile callback is defined and needs to be invoked. */ -static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ - StrAccum acc; - assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) ); - assert( sz>22 ); - if( p->flags & MEM_Int ){ -#if GCC_VERSION>=7000000 - /* Work-around for GCC bug - ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96270 */ - i64 x; - assert( (p->flags&MEM_Int)*2==sizeof(x) ); - memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2); - sqlcipher_sqlite3Int64ToText(x, zBuf); -#else - sqlcipher_sqlite3Int64ToText(p->u.i, zBuf); +static SQLITE_NOINLINE void invokeProfileCallback(sqlcipher_sqlite3 *db, Vdbe *p){ + sqlcipher_sqlite3_int64 iNow; + sqlcipher_sqlite3_int64 iElapse; + assert( p->startTime>0 ); + assert( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 ); + assert( db->init.busy==0 ); + assert( p->zSql!=0 ); + sqlcipher_sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); + iElapse = (iNow - p->startTime)*1000000; +#ifndef SQLITE_OMIT_DEPRECATED + if( db->xProfile ){ + db->xProfile(db->pProfileArg, p->zSql, iElapse); + } #endif - }else{ - sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); - sqlcipher_sqlite3_str_appendf(&acc, "%!.15g", - (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r); - assert( acc.zText==zBuf && acc.mxAlloc<=0 ); - zBuf[acc.nChar] = 0; /* Fast version of sqlcipher_sqlite3StrAccumFinish(&acc) */ + if( db->mTrace & SQLITE_TRACE_PROFILE ){ + db->trace.xV2(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); } + p->startTime = 0; } +/* +** The checkProfileCallback(DB,P) macro checks to see if a profile callback +** is needed, and it invokes the callback if it is needed. +*/ +# define checkProfileCallback(DB,P) \ + if( ((P)->startTime)>0 ){ invokeProfileCallback(DB,P); } +#else +# define checkProfileCallback(DB,P) /*no-op*/ +#endif -#ifdef SQLITE_DEBUG /* -** Validity checks on pMem. pMem holds a string. -** -** (1) Check that string value of pMem agrees with its integer or real value. -** (2) Check that the string is correctly zero terminated -** -** A single int or real value always converts to the same strings. But -** many different strings can be converted into the same int or real. -** If a table contains a numeric value and an index is based on the -** corresponding string value, then it is important that the string be -** derived from the numeric value, not the other way around, to ensure -** that the index and table are consistent. See ticket -** https://www.sqlite.org/src/info/343634942dd54ab (2018-01-31) for -** an example. -** -** This routine looks at pMem to verify that if it has both a numeric -** representation and a string representation then the string rep has -** been derived from the numeric and not the other way around. It returns -** true if everything is ok and false if there is a problem. +** The following routine destroys a virtual machine that is created by +** the sqlcipher_sqlite3_compile() routine. The integer returned is an SQLITE_ +** success/failure code that describes the result of executing the virtual +** machine. ** -** This routine is for use inside of assert() statements only. +** This routine sets the error code and string returned by +** sqlcipher_sqlite3_errcode(), sqlcipher_sqlite3_errmsg() and sqlcipher_sqlite3_errmsg16(). */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemValidStrRep(Mem *p){ - char zBuf[100]; - char *z; - int i, j, incr; - if( (p->flags & MEM_Str)==0 ) return 1; - if( p->flags & MEM_Term ){ - /* Insure that the string is properly zero-terminated. Pay particular - ** attention to the case where p->n is odd */ - if( p->szMalloc>0 && p->z==p->zMalloc ){ - assert( p->enc==SQLITE_UTF8 || p->szMalloc >= ((p->n+1)&~1)+2 ); - assert( p->enc!=SQLITE_UTF8 || p->szMalloc >= p->n+1 ); - } - assert( p->z[p->n]==0 ); - assert( p->enc==SQLITE_UTF8 || p->z[(p->n+1)&~1]==0 ); - assert( p->enc==SQLITE_UTF8 || p->z[((p->n+1)&~1)+1]==0 ); - } - if( (p->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ) return 1; - vdbeMemRenderNum(sizeof(zBuf), zBuf, p); - z = p->z; - i = j = 0; - incr = 1; - if( p->enc!=SQLITE_UTF8 ){ - incr = 2; - if( p->enc==SQLITE_UTF16BE ) z++; - } - while( zBuf[j] ){ - if( zBuf[j++]!=z[i] ) return 0; - i += incr; +SQLITE_API int sqlcipher_sqlite3_finalize(sqlcipher_sqlite3_stmt *pStmt){ + int rc; + if( pStmt==0 ){ + /* IMPLEMENTATION-OF: R-57228-12904 Invoking sqlcipher_sqlite3_finalize() on a NULL + ** pointer is a harmless no-op. */ + rc = SQLITE_OK; + }else{ + Vdbe *v = (Vdbe*)pStmt; + sqlcipher_sqlite3 *db = v->db; + if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT; + sqlcipher_sqlite3_mutex_enter(db->mutex); + checkProfileCallback(db, v); + rc = sqlcipher_sqlite3VdbeFinalize(v); + rc = sqlcipher_sqlite3ApiExit(db, rc); + sqlcipher_sqlite3LeaveMutexAndCloseZombie(db); } - return 1; + return rc; } -#endif /* SQLITE_DEBUG */ /* -** If pMem is an object with a valid string representation, this routine -** ensures the internal encoding for the string representation is -** 'desiredEnc', one of SQLITE_UTF8, SQLITE_UTF16LE or SQLITE_UTF16BE. -** -** If pMem is not a string object, or the encoding of the string -** representation is already stored using the requested encoding, then this -** routine is a no-op. +** Terminate the current execution of an SQL statement and reset it +** back to its starting state so that it can be reused. A success code from +** the prior execution is returned. ** -** SQLITE_OK is returned if the conversion is successful (or not required). -** SQLITE_NOMEM may be returned if a malloc() fails during conversion -** between formats. +** This routine sets the error code and string returned by +** sqlcipher_sqlite3_errcode(), sqlcipher_sqlite3_errmsg() and sqlcipher_sqlite3_errmsg16(). */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ -#ifndef SQLITE_OMIT_UTF16 +SQLITE_API int sqlcipher_sqlite3_reset(sqlcipher_sqlite3_stmt *pStmt){ int rc; -#endif - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); - assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE - || desiredEnc==SQLITE_UTF16BE ); - if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){ - return SQLITE_OK; + if( pStmt==0 ){ + rc = SQLITE_OK; + }else{ + Vdbe *v = (Vdbe*)pStmt; + sqlcipher_sqlite3 *db = v->db; + sqlcipher_sqlite3_mutex_enter(db->mutex); + checkProfileCallback(db, v); + rc = sqlcipher_sqlite3VdbeReset(v); + sqlcipher_sqlite3VdbeRewind(v); + assert( (rc & (db->errMask))==rc ); + rc = sqlcipher_sqlite3ApiExit(db, rc); + sqlcipher_sqlite3_mutex_leave(db->mutex); } - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); -#ifdef SQLITE_OMIT_UTF16 - return SQLITE_ERROR; -#else + return rc; +} - /* MemTranslate() may return SQLITE_OK or SQLITE_NOMEM. If NOMEM is returned, - ** then the encoding of the value may not have changed. - */ - rc = sqlcipher_sqlite3VdbeMemTranslate(pMem, (u8)desiredEnc); - assert(rc==SQLITE_OK || rc==SQLITE_NOMEM); - assert(rc==SQLITE_OK || pMem->enc!=desiredEnc); - assert(rc==SQLITE_NOMEM || pMem->enc==desiredEnc); +/* +** Set all the parameters in the compiled SQL statement to NULL. +*/ +SQLITE_API int sqlcipher_sqlite3_clear_bindings(sqlcipher_sqlite3_stmt *pStmt){ + int i; + int rc = SQLITE_OK; + Vdbe *p = (Vdbe*)pStmt; +#if SQLITE_THREADSAFE + sqlcipher_sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex; +#endif + sqlcipher_sqlite3_mutex_enter(mutex); + for(i=0; inVar; i++){ + sqlcipher_sqlite3VdbeMemRelease(&p->aVar[i]); + p->aVar[i].flags = MEM_Null; + } + assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || p->expmask==0 ); + if( p->expmask ){ + p->expired = 1; + } + sqlcipher_sqlite3_mutex_leave(mutex); return rc; +} + + +/**************************** sqlcipher_sqlite3_value_ ******************************* +** The following routines extract information from a Mem or sqlcipher_sqlite3_value +** structure. +*/ +SQLITE_API const void *sqlcipher_sqlite3_value_blob(sqlcipher_sqlite3_value *pVal){ + Mem *p = (Mem*)pVal; + if( p->flags & (MEM_Blob|MEM_Str) ){ + if( ExpandBlob(p)!=SQLITE_OK ){ + assert( p->flags==MEM_Null && p->z==0 ); + return 0; + } + p->flags |= MEM_Blob; + return p->n ? p->z : 0; + }else{ + return sqlcipher_sqlite3_value_text(pVal); + } +} +SQLITE_API int sqlcipher_sqlite3_value_bytes(sqlcipher_sqlite3_value *pVal){ + return sqlcipher_sqlite3ValueBytes(pVal, SQLITE_UTF8); +} +SQLITE_API int sqlcipher_sqlite3_value_bytes16(sqlcipher_sqlite3_value *pVal){ + return sqlcipher_sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE); +} +SQLITE_API double sqlcipher_sqlite3_value_double(sqlcipher_sqlite3_value *pVal){ + return sqlcipher_sqlite3VdbeRealValue((Mem*)pVal); +} +SQLITE_API int sqlcipher_sqlite3_value_int(sqlcipher_sqlite3_value *pVal){ + return (int)sqlcipher_sqlite3VdbeIntValue((Mem*)pVal); +} +SQLITE_API sqlite_int64 sqlcipher_sqlite3_value_int64(sqlcipher_sqlite3_value *pVal){ + return sqlcipher_sqlite3VdbeIntValue((Mem*)pVal); +} +SQLITE_API unsigned int sqlcipher_sqlite3_value_subtype(sqlcipher_sqlite3_value *pVal){ + Mem *pMem = (Mem*)pVal; + return ((pMem->flags & MEM_Subtype) ? pMem->eSubtype : 0); +} +SQLITE_API void *sqlcipher_sqlite3_value_pointer(sqlcipher_sqlite3_value *pVal, const char *zPType){ + Mem *p = (Mem*)pVal; + if( (p->flags&(MEM_TypeMask|MEM_Term|MEM_Subtype)) == + (MEM_Null|MEM_Term|MEM_Subtype) + && zPType!=0 + && p->eSubtype=='p' + && strcmp(p->u.zPType, zPType)==0 + ){ + return (void*)p->z; + }else{ + return 0; + } +} +SQLITE_API const unsigned char *sqlcipher_sqlite3_value_text(sqlcipher_sqlite3_value *pVal){ + return (const unsigned char *)sqlcipher_sqlite3ValueText(pVal, SQLITE_UTF8); +} +#ifndef SQLITE_OMIT_UTF16 +SQLITE_API const void *sqlcipher_sqlite3_value_text16(sqlcipher_sqlite3_value* pVal){ + return sqlcipher_sqlite3ValueText(pVal, SQLITE_UTF16NATIVE); +} +SQLITE_API const void *sqlcipher_sqlite3_value_text16be(sqlcipher_sqlite3_value *pVal){ + return sqlcipher_sqlite3ValueText(pVal, SQLITE_UTF16BE); +} +SQLITE_API const void *sqlcipher_sqlite3_value_text16le(sqlcipher_sqlite3_value *pVal){ + return sqlcipher_sqlite3ValueText(pVal, SQLITE_UTF16LE); +} +#endif /* SQLITE_OMIT_UTF16 */ +/* EVIDENCE-OF: R-12793-43283 Every value in SQLite has one of five +** fundamental datatypes: 64-bit signed integer 64-bit IEEE floating +** point number string BLOB NULL +*/ +SQLITE_API int sqlcipher_sqlite3_value_type(sqlcipher_sqlite3_value* pVal){ + static const u8 aType[] = { + SQLITE_BLOB, /* 0x00 (not possible) */ + SQLITE_NULL, /* 0x01 NULL */ + SQLITE_TEXT, /* 0x02 TEXT */ + SQLITE_NULL, /* 0x03 (not possible) */ + SQLITE_INTEGER, /* 0x04 INTEGER */ + SQLITE_NULL, /* 0x05 (not possible) */ + SQLITE_INTEGER, /* 0x06 INTEGER + TEXT */ + SQLITE_NULL, /* 0x07 (not possible) */ + SQLITE_FLOAT, /* 0x08 FLOAT */ + SQLITE_NULL, /* 0x09 (not possible) */ + SQLITE_FLOAT, /* 0x0a FLOAT + TEXT */ + SQLITE_NULL, /* 0x0b (not possible) */ + SQLITE_INTEGER, /* 0x0c (not possible) */ + SQLITE_NULL, /* 0x0d (not possible) */ + SQLITE_INTEGER, /* 0x0e (not possible) */ + SQLITE_NULL, /* 0x0f (not possible) */ + SQLITE_BLOB, /* 0x10 BLOB */ + SQLITE_NULL, /* 0x11 (not possible) */ + SQLITE_TEXT, /* 0x12 (not possible) */ + SQLITE_NULL, /* 0x13 (not possible) */ + SQLITE_INTEGER, /* 0x14 INTEGER + BLOB */ + SQLITE_NULL, /* 0x15 (not possible) */ + SQLITE_INTEGER, /* 0x16 (not possible) */ + SQLITE_NULL, /* 0x17 (not possible) */ + SQLITE_FLOAT, /* 0x18 FLOAT + BLOB */ + SQLITE_NULL, /* 0x19 (not possible) */ + SQLITE_FLOAT, /* 0x1a (not possible) */ + SQLITE_NULL, /* 0x1b (not possible) */ + SQLITE_INTEGER, /* 0x1c (not possible) */ + SQLITE_NULL, /* 0x1d (not possible) */ + SQLITE_INTEGER, /* 0x1e (not possible) */ + SQLITE_NULL, /* 0x1f (not possible) */ + SQLITE_FLOAT, /* 0x20 INTREAL */ + SQLITE_NULL, /* 0x21 (not possible) */ + SQLITE_TEXT, /* 0x22 INTREAL + TEXT */ + SQLITE_NULL, /* 0x23 (not possible) */ + SQLITE_FLOAT, /* 0x24 (not possible) */ + SQLITE_NULL, /* 0x25 (not possible) */ + SQLITE_FLOAT, /* 0x26 (not possible) */ + SQLITE_NULL, /* 0x27 (not possible) */ + SQLITE_FLOAT, /* 0x28 (not possible) */ + SQLITE_NULL, /* 0x29 (not possible) */ + SQLITE_FLOAT, /* 0x2a (not possible) */ + SQLITE_NULL, /* 0x2b (not possible) */ + SQLITE_FLOAT, /* 0x2c (not possible) */ + SQLITE_NULL, /* 0x2d (not possible) */ + SQLITE_FLOAT, /* 0x2e (not possible) */ + SQLITE_NULL, /* 0x2f (not possible) */ + SQLITE_BLOB, /* 0x30 (not possible) */ + SQLITE_NULL, /* 0x31 (not possible) */ + SQLITE_TEXT, /* 0x32 (not possible) */ + SQLITE_NULL, /* 0x33 (not possible) */ + SQLITE_FLOAT, /* 0x34 (not possible) */ + SQLITE_NULL, /* 0x35 (not possible) */ + SQLITE_FLOAT, /* 0x36 (not possible) */ + SQLITE_NULL, /* 0x37 (not possible) */ + SQLITE_FLOAT, /* 0x38 (not possible) */ + SQLITE_NULL, /* 0x39 (not possible) */ + SQLITE_FLOAT, /* 0x3a (not possible) */ + SQLITE_NULL, /* 0x3b (not possible) */ + SQLITE_FLOAT, /* 0x3c (not possible) */ + SQLITE_NULL, /* 0x3d (not possible) */ + SQLITE_FLOAT, /* 0x3e (not possible) */ + SQLITE_NULL, /* 0x3f (not possible) */ + }; +#ifdef SQLITE_DEBUG + { + int eType = SQLITE_BLOB; + if( pVal->flags & MEM_Null ){ + eType = SQLITE_NULL; + }else if( pVal->flags & (MEM_Real|MEM_IntReal) ){ + eType = SQLITE_FLOAT; + }else if( pVal->flags & MEM_Int ){ + eType = SQLITE_INTEGER; + }else if( pVal->flags & MEM_Str ){ + eType = SQLITE_TEXT; + } + assert( eType == aType[pVal->flags&MEM_AffMask] ); + } #endif + return aType[pVal->flags&MEM_AffMask]; } -/* -** Make sure pMem->z points to a writable allocation of at least n bytes. -** -** If the bPreserve argument is true, then copy of the content of -** pMem->z into the new allocation. pMem must be either a string or -** blob if bPreserve is true. If bPreserve is false, any prior content -** in pMem->z is discarded. +/* Return true if a parameter to xUpdate represents an unchanged column */ +SQLITE_API int sqlcipher_sqlite3_value_nochange(sqlcipher_sqlite3_value *pVal){ + return (pVal->flags&(MEM_Null|MEM_Zero))==(MEM_Null|MEM_Zero); +} + +/* Return true if a parameter value originated from an sqlcipher_sqlite3_bind() */ +SQLITE_API int sqlcipher_sqlite3_value_frombind(sqlcipher_sqlite3_value *pVal){ + return (pVal->flags&MEM_FromBind)!=0; +} + +/* Make a copy of an sqlcipher_sqlite3_value object */ -SQLITE_PRIVATE SQLITE_NOINLINE int sqlcipher_sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){ - assert( sqlcipher_sqlite3VdbeCheckMemInvariants(pMem) ); - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); - testcase( pMem->db==0 ); +SQLITE_API sqlcipher_sqlite3_value *sqlcipher_sqlite3_value_dup(const sqlcipher_sqlite3_value *pOrig){ + sqlcipher_sqlite3_value *pNew; + if( pOrig==0 ) return 0; + pNew = sqlcipher_sqlite3_malloc( sizeof(*pNew) ); + if( pNew==0 ) return 0; + memset(pNew, 0, sizeof(*pNew)); + memcpy(pNew, pOrig, MEMCELLSIZE); + pNew->flags &= ~MEM_Dyn; + pNew->db = 0; + if( pNew->flags&(MEM_Str|MEM_Blob) ){ + pNew->flags &= ~(MEM_Static|MEM_Dyn); + pNew->flags |= MEM_Ephem; + if( sqlcipher_sqlite3VdbeMemMakeWriteable(pNew)!=SQLITE_OK ){ + sqlcipher_sqlite3ValueFree(pNew); + pNew = 0; + } + }else if( pNew->flags & MEM_Null ){ + /* Do not duplicate pointer values */ + pNew->flags &= ~(MEM_Term|MEM_Subtype); + } + return pNew; +} - /* If the bPreserve flag is set to true, then the memory cell must already - ** contain a valid string or blob value. */ - assert( bPreserve==0 || pMem->flags&(MEM_Blob|MEM_Str) ); - testcase( bPreserve && pMem->z==0 ); +/* Destroy an sqlcipher_sqlite3_value object previously obtained from +** sqlcipher_sqlite3_value_dup(). +*/ +SQLITE_API void sqlcipher_sqlite3_value_free(sqlcipher_sqlite3_value *pOld){ + sqlcipher_sqlite3ValueFree(pOld); +} - assert( pMem->szMalloc==0 - || pMem->szMalloc==sqlcipher_sqlite3DbMallocSize(pMem->db, pMem->zMalloc) ); - if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){ - if( pMem->db ){ - pMem->z = pMem->zMalloc = sqlcipher_sqlite3DbReallocOrFree(pMem->db, pMem->z, n); + +/**************************** sqlcipher_sqlite3_result_ ******************************* +** The following routines are used by user-defined functions to specify +** the function result. +** +** The setStrOrError() function calls sqlcipher_sqlite3VdbeMemSetStr() to store the +** result as a string or blob. Appropriate errors are set if the string/blob +** is too big or if an OOM occurs. +** +** The invokeValueDestructor(P,X) routine invokes destructor function X() +** on value P is not going to be used and need to be destroyed. +*/ +static void setResultStrOrError( + sqlcipher_sqlite3_context *pCtx, /* Function context */ + const char *z, /* String pointer */ + int n, /* Bytes in string, or negative */ + u8 enc, /* Encoding of z. 0 for BLOBs */ + void (*xDel)(void*) /* Destructor function */ +){ + Mem *pOut = pCtx->pOut; + int rc = sqlcipher_sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel); + if( rc ){ + if( rc==SQLITE_TOOBIG ){ + sqlcipher_sqlite3_result_error_toobig(pCtx); }else{ - pMem->zMalloc = sqlcipher_sqlite3Realloc(pMem->z, n); - if( pMem->zMalloc==0 ) sqlcipher_sqlite3_free(pMem->z); - pMem->z = pMem->zMalloc; + /* The only errors possible from sqlcipher_sqlite3VdbeMemSetStr are + ** SQLITE_TOOBIG and SQLITE_NOMEM */ + assert( rc==SQLITE_NOMEM ); + sqlcipher_sqlite3_result_error_nomem(pCtx); } - bPreserve = 0; + return; + } + sqlcipher_sqlite3VdbeChangeEncoding(pOut, pCtx->enc); + if( sqlcipher_sqlite3VdbeMemTooBig(pOut) ){ + sqlcipher_sqlite3_result_error_toobig(pCtx); + } +} +static int invokeValueDestructor( + const void *p, /* Value to destroy */ + void (*xDel)(void*), /* The destructor */ + sqlcipher_sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */ +){ + assert( xDel!=SQLITE_DYNAMIC ); + if( xDel==0 ){ + /* noop */ + }else if( xDel==SQLITE_TRANSIENT ){ + /* noop */ }else{ - if( pMem->szMalloc>0 ) sqlcipher_sqlite3DbFreeNN(pMem->db, pMem->zMalloc); - pMem->zMalloc = sqlcipher_sqlite3DbMallocRaw(pMem->db, n); + xDel((void*)p); } - if( pMem->zMalloc==0 ){ - sqlcipher_sqlite3VdbeMemSetNull(pMem); - pMem->z = 0; - pMem->szMalloc = 0; - return SQLITE_NOMEM_BKPT; + sqlcipher_sqlite3_result_error_toobig(pCtx); + return SQLITE_TOOBIG; +} +SQLITE_API void sqlcipher_sqlite3_result_blob( + sqlcipher_sqlite3_context *pCtx, + const void *z, + int n, + void (*xDel)(void *) +){ + assert( n>=0 ); + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + setResultStrOrError(pCtx, z, n, 0, xDel); +} +SQLITE_API void sqlcipher_sqlite3_result_blob64( + sqlcipher_sqlite3_context *pCtx, + const void *z, + sqlcipher_sqlite3_uint64 n, + void (*xDel)(void *) +){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + assert( xDel!=SQLITE_DYNAMIC ); + if( n>0x7fffffff ){ + (void)invokeValueDestructor(z, xDel, pCtx); }else{ - pMem->szMalloc = sqlcipher_sqlite3DbMallocSize(pMem->db, pMem->zMalloc); + setResultStrOrError(pCtx, z, (int)n, 0, xDel); } - - if( bPreserve && pMem->z ){ - assert( pMem->z!=pMem->zMalloc ); - memcpy(pMem->zMalloc, pMem->z, pMem->n); +} +SQLITE_API void sqlcipher_sqlite3_result_double(sqlcipher_sqlite3_context *pCtx, double rVal){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + sqlcipher_sqlite3VdbeMemSetDouble(pCtx->pOut, rVal); +} +SQLITE_API void sqlcipher_sqlite3_result_error(sqlcipher_sqlite3_context *pCtx, const char *z, int n){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + pCtx->isError = SQLITE_ERROR; + sqlcipher_sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); +} +#ifndef SQLITE_OMIT_UTF16 +SQLITE_API void sqlcipher_sqlite3_result_error16(sqlcipher_sqlite3_context *pCtx, const void *z, int n){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + pCtx->isError = SQLITE_ERROR; + sqlcipher_sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); +} +#endif +SQLITE_API void sqlcipher_sqlite3_result_int(sqlcipher_sqlite3_context *pCtx, int iVal){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + sqlcipher_sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal); +} +SQLITE_API void sqlcipher_sqlite3_result_int64(sqlcipher_sqlite3_context *pCtx, i64 iVal){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + sqlcipher_sqlite3VdbeMemSetInt64(pCtx->pOut, iVal); +} +SQLITE_API void sqlcipher_sqlite3_result_null(sqlcipher_sqlite3_context *pCtx){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + sqlcipher_sqlite3VdbeMemSetNull(pCtx->pOut); +} +SQLITE_API void sqlcipher_sqlite3_result_pointer( + sqlcipher_sqlite3_context *pCtx, + void *pPtr, + const char *zPType, + void (*xDestructor)(void*) +){ + Mem *pOut = pCtx->pOut; + assert( sqlcipher_sqlite3_mutex_held(pOut->db->mutex) ); + sqlcipher_sqlite3VdbeMemRelease(pOut); + pOut->flags = MEM_Null; + sqlcipher_sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor); +} +SQLITE_API void sqlcipher_sqlite3_result_subtype(sqlcipher_sqlite3_context *pCtx, unsigned int eSubtype){ + Mem *pOut = pCtx->pOut; + assert( sqlcipher_sqlite3_mutex_held(pOut->db->mutex) ); + pOut->eSubtype = eSubtype & 0xff; + pOut->flags |= MEM_Subtype; +} +SQLITE_API void sqlcipher_sqlite3_result_text( + sqlcipher_sqlite3_context *pCtx, + const char *z, + int n, + void (*xDel)(void *) +){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel); +} +SQLITE_API void sqlcipher_sqlite3_result_text64( + sqlcipher_sqlite3_context *pCtx, + const char *z, + sqlcipher_sqlite3_uint64 n, + void (*xDel)(void *), + unsigned char enc +){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + assert( xDel!=SQLITE_DYNAMIC ); + if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; + if( n>0x7fffffff ){ + (void)invokeValueDestructor(z, xDel, pCtx); + }else{ + setResultStrOrError(pCtx, z, (int)n, enc, xDel); } - if( (pMem->flags&MEM_Dyn)!=0 ){ - assert( pMem->xDel!=0 && pMem->xDel!=SQLITE_DYNAMIC ); - pMem->xDel((void *)(pMem->z)); +} +#ifndef SQLITE_OMIT_UTF16 +SQLITE_API void sqlcipher_sqlite3_result_text16( + sqlcipher_sqlite3_context *pCtx, + const void *z, + int n, + void (*xDel)(void *) +){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel); +} +SQLITE_API void sqlcipher_sqlite3_result_text16be( + sqlcipher_sqlite3_context *pCtx, + const void *z, + int n, + void (*xDel)(void *) +){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel); +} +SQLITE_API void sqlcipher_sqlite3_result_text16le( + sqlcipher_sqlite3_context *pCtx, + const void *z, + int n, + void (*xDel)(void *) +){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel); +} +#endif /* SQLITE_OMIT_UTF16 */ +SQLITE_API void sqlcipher_sqlite3_result_value(sqlcipher_sqlite3_context *pCtx, sqlcipher_sqlite3_value *pValue){ + Mem *pOut = pCtx->pOut; + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + sqlcipher_sqlite3VdbeMemCopy(pOut, pValue); + sqlcipher_sqlite3VdbeChangeEncoding(pOut, pCtx->enc); + if( sqlcipher_sqlite3VdbeMemTooBig(pOut) ){ + sqlcipher_sqlite3_result_error_toobig(pCtx); + } +} +SQLITE_API void sqlcipher_sqlite3_result_zeroblob(sqlcipher_sqlite3_context *pCtx, int n){ + sqlcipher_sqlite3_result_zeroblob64(pCtx, n>0 ? n : 0); +} +SQLITE_API int sqlcipher_sqlite3_result_zeroblob64(sqlcipher_sqlite3_context *pCtx, u64 n){ + Mem *pOut = pCtx->pOut; + assert( sqlcipher_sqlite3_mutex_held(pOut->db->mutex) ); + if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){ + sqlcipher_sqlite3_result_error_toobig(pCtx); + return SQLITE_TOOBIG; } +#ifndef SQLITE_OMIT_INCRBLOB + sqlcipher_sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n); + return SQLITE_OK; +#else + return sqlcipher_sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n); +#endif +} +SQLITE_API void sqlcipher_sqlite3_result_error_code(sqlcipher_sqlite3_context *pCtx, int errCode){ + pCtx->isError = errCode ? errCode : -1; +#ifdef SQLITE_DEBUG + if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode; +#endif + if( pCtx->pOut->flags & MEM_Null ){ + setResultStrOrError(pCtx, sqlcipher_sqlite3ErrStr(errCode), -1, SQLITE_UTF8, + SQLITE_STATIC); + } +} + +/* Force an SQLITE_TOOBIG error. */ +SQLITE_API void sqlcipher_sqlite3_result_error_toobig(sqlcipher_sqlite3_context *pCtx){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + pCtx->isError = SQLITE_TOOBIG; + sqlcipher_sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1, + SQLITE_UTF8, SQLITE_STATIC); +} - pMem->z = pMem->zMalloc; - pMem->flags &= ~(MEM_Dyn|MEM_Ephem|MEM_Static); - return SQLITE_OK; +/* An SQLITE_NOMEM error. */ +SQLITE_API void sqlcipher_sqlite3_result_error_nomem(sqlcipher_sqlite3_context *pCtx){ + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + sqlcipher_sqlite3VdbeMemSetNull(pCtx->pOut); + pCtx->isError = SQLITE_NOMEM_BKPT; + sqlcipher_sqlite3OomFault(pCtx->pOut->db); } -/* -** Change the pMem->zMalloc allocation to be at least szNew bytes. -** If pMem->zMalloc already meets or exceeds the requested size, this -** routine is a no-op. -** -** Any prior string or blob content in the pMem object may be discarded. -** The pMem->xDel destructor is called, if it exists. Though MEM_Str -** and MEM_Blob values may be discarded, MEM_Int, MEM_Real, MEM_IntReal, -** and MEM_Null values are preserved. -** -** Return SQLITE_OK on success or an error code (probably SQLITE_NOMEM) -** if unable to complete the resizing. +#ifndef SQLITE_UNTESTABLE +/* Force the INT64 value currently stored as the result to be +** a MEM_IntReal value. See the SQLITE_TESTCTRL_RESULT_INTREAL +** test-control. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){ - assert( CORRUPT_DB || szNew>0 ); - assert( (pMem->flags & MEM_Dyn)==0 || pMem->szMalloc==0 ); - if( pMem->szMallocpOut->db->mutex) ); + if( pCtx->pOut->flags & MEM_Int ){ + pCtx->pOut->flags &= ~MEM_Int; + pCtx->pOut->flags |= MEM_IntReal; } - assert( (pMem->flags & MEM_Dyn)==0 ); - pMem->z = pMem->zMalloc; - pMem->flags &= (MEM_Null|MEM_Int|MEM_Real|MEM_IntReal); - return SQLITE_OK; } +#endif + /* -** It is already known that pMem contains an unterminated string. -** Add the zero terminator. -** -** Three bytes of zero are added. In this way, there is guaranteed -** to be a double-zero byte at an even byte boundary in order to -** terminate a UTF16 string, even if the initial size of the buffer -** is an odd number of bytes. +** This function is called after a transaction has been committed. It +** invokes callbacks registered with sqlcipher_sqlite3_wal_hook() as required. */ -static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){ - if( sqlcipher_sqlite3VdbeMemGrow(pMem, pMem->n+3, 1) ){ - return SQLITE_NOMEM_BKPT; +static int doWalCallbacks(sqlcipher_sqlite3 *db){ + int rc = SQLITE_OK; +#ifndef SQLITE_OMIT_WAL + int i; + for(i=0; inDb; i++){ + Btree *pBt = db->aDb[i].pBt; + if( pBt ){ + int nEntry; + sqlcipher_sqlite3BtreeEnter(pBt); + nEntry = sqlcipher_sqlite3PagerWalCallback(sqlcipher_sqlite3BtreePager(pBt)); + sqlcipher_sqlite3BtreeLeave(pBt); + if( nEntry>0 && db->xWalCallback && rc==SQLITE_OK ){ + rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zDbSName, nEntry); + } + } } - pMem->z[pMem->n] = 0; - pMem->z[pMem->n+1] = 0; - pMem->z[pMem->n+2] = 0; - pMem->flags |= MEM_Term; - return SQLITE_OK; +#endif + return rc; } + /* -** Change pMem so that its MEM_Str or MEM_Blob value is stored in -** MEM.zMalloc, where it can be safely written. +** Execute the statement pStmt, either until a row of data is ready, the +** statement is completely executed or an error occurs. ** -** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. +** This routine implements the bulk of the logic behind the sqlite_step() +** API. The only thing omitted is the automatic recompile if a +** schema change has occurred. That detail is handled by the +** outer sqlcipher_sqlite3_step() wrapper procedure. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemMakeWriteable(Mem *pMem){ - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); - if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){ - if( ExpandBlob(pMem) ) return SQLITE_NOMEM; - if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){ - int rc = vdbeMemAddTerminator(pMem); - if( rc ) return rc; +static int sqlcipher_sqlite3Step(Vdbe *p){ + sqlcipher_sqlite3 *db; + int rc; + + assert(p); + db = p->db; + if( p->eVdbeState!=VDBE_RUN_STATE ){ + restart_step: + if( p->eVdbeState==VDBE_READY_STATE ){ + if( p->expired ){ + p->rc = SQLITE_SCHEMA; + rc = SQLITE_ERROR; + if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ + /* If this statement was prepared using saved SQL and an + ** error has occurred, then return the error code in p->rc to the + ** caller. Set the error code in the database handle to the same + ** value. + */ + rc = sqlcipher_sqlite3VdbeTransferError(p); + } + goto end_of_step; + } + + /* If there are no other statements currently running, then + ** reset the interrupt flag. This prevents a call to sqlcipher_sqlite3_interrupt + ** from interrupting a statement that has not yet started. + */ + if( db->nVdbeActive==0 ){ + AtomicStore(&db->u1.isInterrupted, 0); + } + + assert( db->nVdbeWrite>0 || db->autoCommit==0 + || (db->nDeferredCons==0 && db->nDeferredImmCons==0) + ); + +#ifndef SQLITE_OMIT_TRACE + if( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 + && !db->init.busy && p->zSql ){ + sqlcipher_sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); + }else{ + assert( p->startTime==0 ); + } +#endif + + db->nVdbeActive++; + if( p->readOnly==0 ) db->nVdbeWrite++; + if( p->bIsReader ) db->nVdbeRead++; + p->pc = 0; + p->eVdbeState = VDBE_RUN_STATE; + }else + + if( ALWAYS(p->eVdbeState==VDBE_HALT_STATE) ){ + /* We used to require that sqlcipher_sqlite3_reset() be called before retrying + ** sqlcipher_sqlite3_step() after any error or after SQLITE_DONE. But beginning + ** with version 3.7.0, we changed this so that sqlcipher_sqlite3_reset() would + ** be called automatically instead of throwing the SQLITE_MISUSE error. + ** This "automatic-reset" change is not technically an incompatibility, + ** since any application that receives an SQLITE_MISUSE is broken by + ** definition. + ** + ** Nevertheless, some published applications that were originally written + ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE + ** returns, and those were broken by the automatic-reset change. As a + ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the + ** legacy behavior of returning SQLITE_MISUSE for cases where the + ** previous sqlcipher_sqlite3_step() returned something other than a SQLITE_LOCKED + ** or SQLITE_BUSY error. + */ +#ifdef SQLITE_OMIT_AUTORESET + if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){ + sqlcipher_sqlite3_reset((sqlcipher_sqlite3_stmt*)p); + }else{ + return SQLITE_MISUSE_BKPT; + } +#else + sqlcipher_sqlite3_reset((sqlcipher_sqlite3_stmt*)p); +#endif + assert( p->eVdbeState==VDBE_READY_STATE ); + goto restart_step; } } - pMem->flags &= ~MEM_Ephem; + #ifdef SQLITE_DEBUG - pMem->pScopyFrom = 0; + p->rcApp = SQLITE_OK; #endif +#ifndef SQLITE_OMIT_EXPLAIN + if( p->explain ){ + rc = sqlcipher_sqlite3VdbeList(p); + }else +#endif /* SQLITE_OMIT_EXPLAIN */ + { + db->nVdbeExec++; + rc = sqlcipher_sqlite3VdbeExec(p); + db->nVdbeExec--; + } - return SQLITE_OK; -} - -/* -** If the given Mem* has a zero-filled tail, turn it into an ordinary -** blob stored in dynamically allocated space. -*/ -#ifndef SQLITE_OMIT_INCRBLOB -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemExpandBlob(Mem *pMem){ - int nByte; - assert( pMem->flags & MEM_Zero ); - assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) ); - testcase( sqlcipher_sqlite3_value_nochange(pMem) ); - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); + if( rc==SQLITE_ROW ){ + assert( p->rc==SQLITE_OK ); + assert( db->mallocFailed==0 ); + db->errCode = SQLITE_ROW; + return SQLITE_ROW; + }else{ +#ifndef SQLITE_OMIT_TRACE + /* If the statement completed successfully, invoke the profile callback */ + checkProfileCallback(db, p); +#endif - /* Set nByte to the number of bytes required to store the expanded blob. */ - nByte = pMem->n + pMem->u.nZero; - if( nByte<=0 ){ - if( (pMem->flags & MEM_Blob)==0 ) return SQLITE_OK; - nByte = 1; - } - if( sqlcipher_sqlite3VdbeMemGrow(pMem, nByte, 1) ){ - return SQLITE_NOMEM_BKPT; + if( rc==SQLITE_DONE && db->autoCommit ){ + assert( p->rc==SQLITE_OK ); + p->rc = doWalCallbacks(db); + if( p->rc!=SQLITE_OK ){ + rc = SQLITE_ERROR; + } + }else if( rc!=SQLITE_DONE && (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ + /* If this statement was prepared using saved SQL and an + ** error has occurred, then return the error code in p->rc to the + ** caller. Set the error code in the database handle to the same value. + */ + rc = sqlcipher_sqlite3VdbeTransferError(p); + } } - memset(&pMem->z[pMem->n], 0, pMem->u.nZero); - pMem->n += pMem->u.nZero; - pMem->flags &= ~(MEM_Zero|MEM_Term); - return SQLITE_OK; + db->errCode = rc; + if( SQLITE_NOMEM==sqlcipher_sqlite3ApiExit(p->db, p->rc) ){ + p->rc = SQLITE_NOMEM_BKPT; + if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ) rc = p->rc; + } +end_of_step: + /* There are only a limited number of result codes allowed from the + ** statements prepared using the legacy sqlcipher_sqlite3_prepare() interface */ + assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 + || rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR + || (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE + ); + return (rc&db->errMask); } -#endif /* -** Make sure the given Mem is \u0000 terminated. +** This is the top-level implementation of sqlcipher_sqlite3_step(). Call +** sqlcipher_sqlite3Step() to do most of the work. If a schema error occurs, +** call sqlcipher_sqlite3Reprepare() and try again. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemNulTerminate(Mem *pMem){ - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - testcase( (pMem->flags & (MEM_Term|MEM_Str))==(MEM_Term|MEM_Str) ); - testcase( (pMem->flags & (MEM_Term|MEM_Str))==0 ); - if( (pMem->flags & (MEM_Term|MEM_Str))!=MEM_Str ){ - return SQLITE_OK; /* Nothing to do */ - }else{ - return vdbeMemAddTerminator(pMem); +SQLITE_API int sqlcipher_sqlite3_step(sqlcipher_sqlite3_stmt *pStmt){ + int rc = SQLITE_OK; /* Result from sqlcipher_sqlite3Step() */ + Vdbe *v = (Vdbe*)pStmt; /* the prepared statement */ + int cnt = 0; /* Counter to prevent infinite loop of reprepares */ + sqlcipher_sqlite3 *db; /* The database connection */ + + if( vdbeSafetyNotNull(v) ){ + return SQLITE_MISUSE_BKPT; + } + db = v->db; + sqlcipher_sqlite3_mutex_enter(db->mutex); + while( (rc = sqlcipher_sqlite3Step(v))==SQLITE_SCHEMA + && cnt++ < SQLITE_MAX_SCHEMA_RETRY ){ + int savedPc = v->pc; + rc = sqlcipher_sqlite3Reprepare(v); + if( rc!=SQLITE_OK ){ + /* This case occurs after failing to recompile an sql statement. + ** The error message from the SQL compiler has already been loaded + ** into the database handle. This block copies the error message + ** from the database handle into the statement and sets the statement + ** program counter to 0 to ensure that when the statement is + ** finalized or reset the parser error message is available via + ** sqlcipher_sqlite3_errmsg() and sqlcipher_sqlite3_errcode(). + */ + const char *zErr = (const char *)sqlcipher_sqlite3_value_text(db->pErr); + sqlcipher_sqlite3DbFree(db, v->zErrMsg); + if( !db->mallocFailed ){ + v->zErrMsg = sqlcipher_sqlite3DbStrDup(db, zErr); + v->rc = rc = sqlcipher_sqlite3ApiExit(db, rc); + } else { + v->zErrMsg = 0; + v->rc = rc = SQLITE_NOMEM_BKPT; + } + break; + } + sqlcipher_sqlite3_reset(pStmt); + if( savedPc>=0 ){ + /* Setting minWriteFileFormat to 254 is a signal to the OP_Init and + ** OP_Trace opcodes to *not* perform SQLITE_TRACE_STMT because it has + ** already been done once on a prior invocation that failed due to + ** SQLITE_SCHEMA. tag-20220401a */ + v->minWriteFileFormat = 254; + } + assert( v->expired==0 ); } + sqlcipher_sqlite3_mutex_leave(db->mutex); + return rc; } + /* -** Add MEM_Str to the set of representations for the given Mem. This -** routine is only called if pMem is a number of some kind, not a NULL -** or a BLOB. -** -** Existing representations MEM_Int, MEM_Real, or MEM_IntReal are invalidated -** if bForce is true but are retained if bForce is false. -** -** A MEM_Null value will never be passed to this function. This function is -** used for converting values to text for returning to the user (i.e. via -** sqlcipher_sqlite3_value_text()), or for ensuring that values to be used as btree -** keys are strings. In the former case a NULL pointer is returned the -** user and the latter is an internal programming error. +** Extract the user data from a sqlcipher_sqlite3_context structure and return a +** pointer to it. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ - const int nByte = 32; - - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - assert( !(pMem->flags&MEM_Zero) ); - assert( !(pMem->flags&(MEM_Str|MEM_Blob)) ); - assert( pMem->flags&(MEM_Int|MEM_Real|MEM_IntReal) ); - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - - - if( sqlcipher_sqlite3VdbeMemClearAndResize(pMem, nByte) ){ - pMem->enc = 0; - return SQLITE_NOMEM_BKPT; - } - - vdbeMemRenderNum(nByte, pMem->z, pMem); - assert( pMem->z!=0 ); - pMem->n = sqlcipher_sqlite3Strlen30NN(pMem->z); - pMem->enc = SQLITE_UTF8; - pMem->flags |= MEM_Str|MEM_Term; - if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); - sqlcipher_sqlite3VdbeChangeEncoding(pMem, enc); - return SQLITE_OK; +SQLITE_API void *sqlcipher_sqlite3_user_data(sqlcipher_sqlite3_context *p){ + assert( p && p->pFunc ); + return p->pFunc->pUserData; } /* -** Memory cell pMem contains the context of an aggregate function. -** This routine calls the finalize method for that function. The -** result of the aggregate is stored back into pMem. +** Extract the user data from a sqlcipher_sqlite3_context structure and return a +** pointer to it. ** -** Return SQLITE_ERROR if the finalizer reports an error. SQLITE_OK -** otherwise. +** IMPLEMENTATION-OF: R-46798-50301 The sqlcipher_sqlite3_context_db_handle() interface +** returns a copy of the pointer to the database connection (the 1st +** parameter) of the sqlcipher_sqlite3_create_function() and +** sqlcipher_sqlite3_create_function16() routines that originally registered the +** application defined function. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){ - sqlcipher_sqlite3_context ctx; - Mem t; - assert( pFunc!=0 ); - assert( pFunc->xFinalize!=0 ); - assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef ); - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - memset(&ctx, 0, sizeof(ctx)); - memset(&t, 0, sizeof(t)); - t.flags = MEM_Null; - t.db = pMem->db; - ctx.pOut = &t; - ctx.pMem = pMem; - ctx.pFunc = pFunc; - pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */ - assert( (pMem->flags & MEM_Dyn)==0 ); - if( pMem->szMalloc>0 ) sqlcipher_sqlite3DbFreeNN(pMem->db, pMem->zMalloc); - memcpy(pMem, &t, sizeof(t)); - return ctx.isError; +SQLITE_API sqlcipher_sqlite3 *sqlcipher_sqlite3_context_db_handle(sqlcipher_sqlite3_context *p){ + assert( p && p->pOut ); + return p->pOut->db; } /* -** Memory cell pAccum contains the context of an aggregate function. -** This routine calls the xValue method for that function and stores -** the results in memory cell pMem. +** If this routine is invoked from within an xColumn method of a virtual +** table, then it returns true if and only if the the call is during an +** UPDATE operation and the value of the column will not be modified +** by the UPDATE. ** -** SQLITE_ERROR is returned if xValue() reports an error. SQLITE_OK -** otherwise. +** If this routine is called from any context other than within the +** xColumn method of a virtual table, then the return value is meaningless +** and arbitrary. +** +** Virtual table implements might use this routine to optimize their +** performance by substituting a NULL result, or some other light-weight +** value, as a signal to the xUpdate routine that the column is unchanged. */ -#ifndef SQLITE_OMIT_WINDOWFUNC -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pFunc){ - sqlcipher_sqlite3_context ctx; - assert( pFunc!=0 ); - assert( pFunc->xValue!=0 ); - assert( (pAccum->flags & MEM_Null)!=0 || pFunc==pAccum->u.pDef ); - assert( pAccum->db==0 || sqlcipher_sqlite3_mutex_held(pAccum->db->mutex) ); - memset(&ctx, 0, sizeof(ctx)); - sqlcipher_sqlite3VdbeMemSetNull(pOut); - ctx.pOut = pOut; - ctx.pMem = pAccum; - ctx.pFunc = pFunc; - pFunc->xValue(&ctx); - return ctx.isError; +SQLITE_API int sqlcipher_sqlite3_vtab_nochange(sqlcipher_sqlite3_context *p){ + assert( p ); + return sqlcipher_sqlite3_value_nochange(p->pOut); } -#endif /* SQLITE_OMIT_WINDOWFUNC */ /* -** If the memory cell contains a value that must be freed by -** invoking the external callback in Mem.xDel, then this routine -** will free that value. It also sets Mem.flags to MEM_Null. -** -** This is a helper routine for sqlcipher_sqlite3VdbeMemSetNull() and -** for sqlcipher_sqlite3VdbeMemRelease(). Use those other routines as the -** entry point for releasing Mem resources. +** Implementation of sqlcipher_sqlite3_vtab_in_first() (if bNext==0) and +** sqlcipher_sqlite3_vtab_in_next() (if bNext!=0). */ -static SQLITE_NOINLINE void vdbeMemClearExternAndSetNull(Mem *p){ - assert( p->db==0 || sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - assert( VdbeMemDynamic(p) ); - if( p->flags&MEM_Agg ){ - sqlcipher_sqlite3VdbeMemFinalize(p, p->u.pDef); - assert( (p->flags & MEM_Agg)==0 ); - testcase( p->flags & MEM_Dyn ); +static int valueFromValueList( + sqlcipher_sqlite3_value *pVal, /* Pointer to the ValueList object */ + sqlcipher_sqlite3_value **ppOut, /* Store the next value from the list here */ + int bNext /* 1 for _next(). 0 for _first() */ +){ + int rc; + ValueList *pRhs; + + *ppOut = 0; + if( pVal==0 ) return SQLITE_MISUSE; + pRhs = (ValueList*)sqlcipher_sqlite3_value_pointer(pVal, "ValueList"); + if( pRhs==0 ) return SQLITE_MISUSE; + if( bNext ){ + rc = sqlcipher_sqlite3BtreeNext(pRhs->pCsr, 0); + }else{ + int dummy = 0; + rc = sqlcipher_sqlite3BtreeFirst(pRhs->pCsr, &dummy); + assert( rc==SQLITE_OK || sqlcipher_sqlite3BtreeEof(pRhs->pCsr) ); + if( sqlcipher_sqlite3BtreeEof(pRhs->pCsr) ) rc = SQLITE_DONE; } - if( p->flags&MEM_Dyn ){ - assert( p->xDel!=SQLITE_DYNAMIC && p->xDel!=0 ); - p->xDel((void *)p->z); + if( rc==SQLITE_OK ){ + u32 sz; /* Size of current row in bytes */ + Mem sMem; /* Raw content of current row */ + memset(&sMem, 0, sizeof(sMem)); + sz = sqlcipher_sqlite3BtreePayloadSize(pRhs->pCsr); + rc = sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,(int)sz,&sMem); + if( rc==SQLITE_OK ){ + u8 *zBuf = (u8*)sMem.z; + u32 iSerial; + sqlcipher_sqlite3_value *pOut = pRhs->pOut; + int iOff = 1 + getVarint32(&zBuf[1], iSerial); + sqlcipher_sqlite3VdbeSerialGet(&zBuf[iOff], iSerial, pOut); + pOut->enc = ENC(pOut->db); + if( (pOut->flags & MEM_Ephem)!=0 && sqlcipher_sqlite3VdbeMemMakeWriteable(pOut) ){ + rc = SQLITE_NOMEM; + }else{ + *ppOut = pOut; + } + } + sqlcipher_sqlite3VdbeMemRelease(&sMem); } - p->flags = MEM_Null; + return rc; } /* -** Release memory held by the Mem p, both external memory cleared -** by p->xDel and memory in p->zMalloc. -** -** This is a helper routine invoked by sqlcipher_sqlite3VdbeMemRelease() in -** the unusual case where there really is memory in p that needs -** to be freed. +** Set the iterator value pVal to point to the first value in the set. +** Set (*ppOut) to point to this value before returning. */ -static SQLITE_NOINLINE void vdbeMemClear(Mem *p){ - if( VdbeMemDynamic(p) ){ - vdbeMemClearExternAndSetNull(p); - } - if( p->szMalloc ){ - sqlcipher_sqlite3DbFreeNN(p->db, p->zMalloc); - p->szMalloc = 0; - } - p->z = 0; +SQLITE_API int sqlcipher_sqlite3_vtab_in_first(sqlcipher_sqlite3_value *pVal, sqlcipher_sqlite3_value **ppOut){ + return valueFromValueList(pVal, ppOut, 0); } /* -** Release any memory resources held by the Mem. Both the memory that is -** free by Mem.xDel and the Mem.zMalloc allocation are freed. -** -** Use this routine prior to clean up prior to abandoning a Mem, or to -** reset a Mem back to its minimum memory utilization. -** -** Use sqlcipher_sqlite3VdbeMemSetNull() to release just the Mem.xDel space -** prior to inserting new content into the Mem. +** Set the iterator value pVal to point to the next value in the set. +** Set (*ppOut) to point to this value before returning. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemRelease(Mem *p){ - assert( sqlcipher_sqlite3VdbeCheckMemInvariants(p) ); - if( VdbeMemDynamic(p) || p->szMalloc ){ - vdbeMemClear(p); - } +SQLITE_API int sqlcipher_sqlite3_vtab_in_next(sqlcipher_sqlite3_value *pVal, sqlcipher_sqlite3_value **ppOut){ + return valueFromValueList(pVal, ppOut, 1); } /* -** Convert a 64-bit IEEE double into a 64-bit signed integer. -** If the double is out of range of a 64-bit signed integer then -** return the closest available 64-bit signed integer. +** Return the current time for a statement. If the current time +** is requested more than once within the same run of a single prepared +** statement, the exact same time is returned for each invocation regardless +** of the amount of time that elapses between invocations. In other words, +** the time returned is always the time of the first call. */ -static SQLITE_NOINLINE i64 doubleToInt64(double r){ -#ifdef SQLITE_OMIT_FLOATING_POINT - /* When floating-point is omitted, double and int64 are the same thing */ - return r; +SQLITE_PRIVATE sqlcipher_sqlite3_int64 sqlcipher_sqlite3StmtCurrentTime(sqlcipher_sqlite3_context *p){ + int rc; +#ifndef SQLITE_ENABLE_STAT4 + sqlcipher_sqlite3_int64 *piTime = &p->pVdbe->iCurrentTime; + assert( p->pVdbe!=0 ); #else - /* - ** Many compilers we encounter do not define constants for the - ** minimum and maximum 64-bit integers, or they define them - ** inconsistently. And many do not understand the "LL" notation. - ** So we define our own static constants here using nothing - ** larger than a 32-bit integer constant. - */ - static const i64 maxInt = LARGEST_INT64; - static const i64 minInt = SMALLEST_INT64; - - if( r<=(double)minInt ){ - return minInt; - }else if( r>=(double)maxInt ){ - return maxInt; - }else{ - return (i64)r; - } + sqlcipher_sqlite3_int64 iTime = 0; + sqlcipher_sqlite3_int64 *piTime = p->pVdbe!=0 ? &p->pVdbe->iCurrentTime : &iTime; #endif + if( *piTime==0 ){ + rc = sqlcipher_sqlite3OsCurrentTimeInt64(p->pOut->db->pVfs, piTime); + if( rc ) *piTime = 0; + } + return *piTime; } /* -** Return some kind of integer value which is the best we can do -** at representing the value that *pMem describes as an integer. -** If pMem is an integer, then the value is exact. If pMem is -** a floating-point then the value returned is the integer part. -** If pMem is a string or blob, then we make an attempt to convert -** it into an integer and return that. If pMem represents an -** an SQL-NULL value, return 0. -** -** If pMem represents a string value, its encoding might be changed. +** Create a new aggregate context for p and return a pointer to +** its pMem->z element. */ -static SQLITE_NOINLINE i64 memIntValue(Mem *pMem){ - i64 value = 0; - sqlcipher_sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); - return value; -} -SQLITE_PRIVATE i64 sqlcipher_sqlite3VdbeIntValue(Mem *pMem){ - int flags; - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - flags = pMem->flags; - if( flags & (MEM_Int|MEM_IntReal) ){ - testcase( flags & MEM_IntReal ); - return pMem->u.i; - }else if( flags & MEM_Real ){ - return doubleToInt64(pMem->u.r); - }else if( (flags & (MEM_Str|MEM_Blob))!=0 && pMem->z!=0 ){ - return memIntValue(pMem); +static SQLITE_NOINLINE void *createAggContext(sqlcipher_sqlite3_context *p, int nByte){ + Mem *pMem = p->pMem; + assert( (pMem->flags & MEM_Agg)==0 ); + if( nByte<=0 ){ + sqlcipher_sqlite3VdbeMemSetNull(pMem); + pMem->z = 0; }else{ - return 0; + sqlcipher_sqlite3VdbeMemClearAndResize(pMem, nByte); + pMem->flags = MEM_Agg; + pMem->u.pDef = p->pFunc; + if( pMem->z ){ + memset(pMem->z, 0, nByte); + } } + return (void*)pMem->z; } /* -** Return the best representation of pMem that we can get into a -** double. If pMem is already a double or an integer, return its -** value. If it is a string or blob, try to convert it to a double. -** If it is a NULL, return 0.0. +** Allocate or return the aggregate context for a user function. A new +** context is allocated on the first call. Subsequent calls return the +** same context that was returned on prior calls. */ -static SQLITE_NOINLINE double memRealValue(Mem *pMem){ - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - double val = (double)0; - sqlcipher_sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc); - return val; -} -SQLITE_PRIVATE double sqlcipher_sqlite3VdbeRealValue(Mem *pMem){ - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - if( pMem->flags & MEM_Real ){ - return pMem->u.r; - }else if( pMem->flags & (MEM_Int|MEM_IntReal) ){ - testcase( pMem->flags & MEM_IntReal ); - return (double)pMem->u.i; - }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ - return memRealValue(pMem); +SQLITE_API void *sqlcipher_sqlite3_aggregate_context(sqlcipher_sqlite3_context *p, int nByte){ + assert( p && p->pFunc && p->pFunc->xFinalize ); + assert( sqlcipher_sqlite3_mutex_held(p->pOut->db->mutex) ); + testcase( nByte<0 ); + if( (p->pMem->flags & MEM_Agg)==0 ){ + return createAggContext(p, nByte); }else{ - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - return (double)0; + return (void*)p->pMem->z; } } /* -** Return 1 if pMem represents true, and return 0 if pMem represents false. -** Return the value ifNull if pMem is NULL. +** Return the auxiliary data pointer, if any, for the iArg'th argument to +** the user-function defined by pCtx. +** +** The left-most argument is 0. +** +** Undocumented behavior: If iArg is negative then access a cache of +** auxiliary data pointers that is available to all functions within a +** single prepared statement. The iArg values must match. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){ - testcase( pMem->flags & MEM_IntReal ); - if( pMem->flags & (MEM_Int|MEM_IntReal) ) return pMem->u.i!=0; - if( pMem->flags & MEM_Null ) return ifNull; - return sqlcipher_sqlite3VdbeRealValue(pMem)!=0.0; +SQLITE_API void *sqlcipher_sqlite3_get_auxdata(sqlcipher_sqlite3_context *pCtx, int iArg){ + AuxData *pAuxData; + + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); +#if SQLITE_ENABLE_STAT4 + if( pCtx->pVdbe==0 ) return 0; +#else + assert( pCtx->pVdbe!=0 ); +#endif + for(pAuxData=pCtx->pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNextAux){ + if( pAuxData->iAuxArg==iArg && (pAuxData->iAuxOp==pCtx->iOp || iArg<0) ){ + return pAuxData->pAux; + } + } + return 0; } /* -** The MEM structure is already a MEM_Real. Try to also make it a -** MEM_Int if we can. +** Set the auxiliary data pointer and delete function, for the iArg'th +** argument to the user-function defined by pCtx. Any previous value is +** deleted by calling the delete function specified when it was set. +** +** The left-most argument is 0. +** +** Undocumented behavior: If iArg is negative then make the data available +** to all functions within the current prepared statement using iArg as an +** access code. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeIntegerAffinity(Mem *pMem){ - i64 ix; - assert( pMem->flags & MEM_Real ); - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); +SQLITE_API void sqlcipher_sqlite3_set_auxdata( + sqlcipher_sqlite3_context *pCtx, + int iArg, + void *pAux, + void (*xDelete)(void*) +){ + AuxData *pAuxData; + Vdbe *pVdbe = pCtx->pVdbe; - ix = doubleToInt64(pMem->u.r); + assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); +#ifdef SQLITE_ENABLE_STAT4 + if( pVdbe==0 ) goto failed; +#else + assert( pVdbe!=0 ); +#endif - /* Only mark the value as an integer if - ** - ** (1) the round-trip conversion real->int->real is a no-op, and - ** (2) The integer is neither the largest nor the smallest - ** possible integer (ticket #3922) - ** - ** The second and third terms in the following conditional enforces - ** the second condition under the assumption that addition overflow causes - ** values to wrap around. - */ - if( pMem->u.r==ix && ix>SMALLEST_INT64 && ixu.i = ix; - MemSetTypeFlag(pMem, MEM_Int); + for(pAuxData=pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNextAux){ + if( pAuxData->iAuxArg==iArg && (pAuxData->iAuxOp==pCtx->iOp || iArg<0) ){ + break; + } + } + if( pAuxData==0 ){ + pAuxData = sqlcipher_sqlite3DbMallocZero(pVdbe->db, sizeof(AuxData)); + if( !pAuxData ) goto failed; + pAuxData->iAuxOp = pCtx->iOp; + pAuxData->iAuxArg = iArg; + pAuxData->pNextAux = pVdbe->pAuxData; + pVdbe->pAuxData = pAuxData; + if( pCtx->isError==0 ) pCtx->isError = -1; + }else if( pAuxData->xDeleteAux ){ + pAuxData->xDeleteAux(pAuxData->pAux); } -} -/* -** Convert pMem to type integer. Invalidate any prior representations. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemIntegerify(Mem *pMem){ - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + pAuxData->pAux = pAux; + pAuxData->xDeleteAux = xDelete; + return; - pMem->u.i = sqlcipher_sqlite3VdbeIntValue(pMem); - MemSetTypeFlag(pMem, MEM_Int); - return SQLITE_OK; +failed: + if( xDelete ){ + xDelete(pAux); + } } +#ifndef SQLITE_OMIT_DEPRECATED /* -** Convert pMem so that it is of type MEM_Real. -** Invalidate any prior representations. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemRealify(Mem *pMem){ - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - - pMem->u.r = sqlcipher_sqlite3VdbeRealValue(pMem); - MemSetTypeFlag(pMem, MEM_Real); - return SQLITE_OK; -} - -/* Compare a floating point value to an integer. Return true if the two -** values are the same within the precision of the floating point value. -** -** This function assumes that i was obtained by assignment from r1. +** Return the number of times the Step function of an aggregate has been +** called. ** -** For some versions of GCC on 32-bit machines, if you do the more obvious -** comparison of "r1==(double)i" you sometimes get an answer of false even -** though the r1 and (double)i values are bit-for-bit the same. +** This function is deprecated. Do not use it for new code. It is +** provide only to avoid breaking legacy code. New aggregate function +** implementations should keep their own counts within their aggregate +** context. */ -SQLITE_PRIVATE int sqlcipher_sqlite3RealSameAsInt(double r1, sqlcipher_sqlite3_int64 i){ - double r2 = (double)i; - return r1==0.0 - || (memcmp(&r1, &r2, sizeof(r1))==0 - && i >= -2251799813685248LL && i < 2251799813685248LL); +SQLITE_API int sqlcipher_sqlite3_aggregate_count(sqlcipher_sqlite3_context *p){ + assert( p && p->pMem && p->pFunc && p->pFunc->xFinalize ); + return p->pMem->n; } +#endif /* -** Convert pMem so that it has type MEM_Real or MEM_Int. -** Invalidate any prior representations. -** -** Every effort is made to force the conversion, even if the input -** is a string that does not look completely like a number. Convert -** as much of the string as we can and ignore the rest. +** Return the number of columns in the result set for the statement pStmt. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemNumerify(Mem *pMem){ - testcase( pMem->flags & MEM_Int ); - testcase( pMem->flags & MEM_Real ); - testcase( pMem->flags & MEM_IntReal ); - testcase( pMem->flags & MEM_Null ); - if( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null))==0 ){ - int rc; - sqlcipher_sqlite3_int64 ix; - assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - rc = sqlcipher_sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); - if( ((rc==0 || rc==1) && sqlcipher_sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1) - || sqlcipher_sqlite3RealSameAsInt(pMem->u.r, (ix = (i64)pMem->u.r)) - ){ - pMem->u.i = ix; - MemSetTypeFlag(pMem, MEM_Int); - }else{ - MemSetTypeFlag(pMem, MEM_Real); - } - } - assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null))!=0 ); - pMem->flags &= ~(MEM_Str|MEM_Blob|MEM_Zero); - return SQLITE_OK; +SQLITE_API int sqlcipher_sqlite3_column_count(sqlcipher_sqlite3_stmt *pStmt){ + Vdbe *pVm = (Vdbe *)pStmt; + return pVm ? pVm->nResColumn : 0; } /* -** Cast the datatype of the value in pMem according to the affinity -** "aff". Casting is different from applying affinity in that a cast -** is forced. In other words, the value is converted into the desired -** affinity even if that results in loss of data. This routine is -** used (for example) to implement the SQL "cast()" operator. +** Return the number of values available from the current row of the +** currently executing statement pStmt. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){ - if( pMem->flags & MEM_Null ) return SQLITE_OK; - switch( aff ){ - case SQLITE_AFF_BLOB: { /* Really a cast to BLOB */ - if( (pMem->flags & MEM_Blob)==0 ){ - sqlcipher_sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding); - assert( pMem->flags & MEM_Str || pMem->db->mallocFailed ); - if( pMem->flags & MEM_Str ) MemSetTypeFlag(pMem, MEM_Blob); - }else{ - pMem->flags &= ~(MEM_TypeMask&~MEM_Blob); - } - break; - } - case SQLITE_AFF_NUMERIC: { - sqlcipher_sqlite3VdbeMemNumerify(pMem); - break; - } - case SQLITE_AFF_INTEGER: { - sqlcipher_sqlite3VdbeMemIntegerify(pMem); - break; - } - case SQLITE_AFF_REAL: { - sqlcipher_sqlite3VdbeMemRealify(pMem); - break; - } - default: { - assert( aff==SQLITE_AFF_TEXT ); - assert( MEM_Str==(MEM_Blob>>3) ); - pMem->flags |= (pMem->flags&MEM_Blob)>>3; - sqlcipher_sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding); - assert( pMem->flags & MEM_Str || pMem->db->mallocFailed ); - pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal|MEM_Blob|MEM_Zero); - return sqlcipher_sqlite3VdbeChangeEncoding(pMem, encoding); - } - } - return SQLITE_OK; +SQLITE_API int sqlcipher_sqlite3_data_count(sqlcipher_sqlite3_stmt *pStmt){ + Vdbe *pVm = (Vdbe *)pStmt; + if( pVm==0 || pVm->pResultSet==0 ) return 0; + return pVm->nResColumn; } /* -** Initialize bulk memory to be a consistent Mem object. -** -** The minimum amount of initialization feasible is performed. +** Return a pointer to static memory containing an SQL NULL value. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemInit(Mem *pMem, sqlcipher_sqlite3 *db, u16 flags){ - assert( (flags & ~MEM_TypeMask)==0 ); - pMem->flags = flags; - pMem->db = db; - pMem->szMalloc = 0; +static const Mem *columnNullValue(void){ + /* Even though the Mem structure contains an element + ** of type i64, on certain architectures (x86) with certain compiler + ** switches (-Os), gcc may align this Mem object on a 4-byte boundary + ** instead of an 8-byte one. This all works fine, except that when + ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s + ** that a Mem structure is located on an 8-byte boundary. To prevent + ** these assert()s from failing, when building with SQLITE_DEBUG defined + ** using gcc, we force nullMem to be 8-byte aligned using the magical + ** __attribute__((aligned(8))) macro. */ + static const Mem nullMem +#if defined(SQLITE_DEBUG) && defined(__GNUC__) + __attribute__((aligned(8))) +#endif + = { + /* .u = */ {0}, + /* .z = */ (char*)0, + /* .n = */ (int)0, + /* .flags = */ (u16)MEM_Null, + /* .enc = */ (u8)0, + /* .eSubtype = */ (u8)0, + /* .db = */ (sqlcipher_sqlite3*)0, + /* .szMalloc = */ (int)0, + /* .uTemp = */ (u32)0, + /* .zMalloc = */ (char*)0, + /* .xDel = */ (void(*)(void*))0, +#ifdef SQLITE_DEBUG + /* .pScopyFrom = */ (Mem*)0, + /* .mScopyFlags= */ 0, +#endif + }; + return &nullMem; } - /* -** Delete any previous value and set the value stored in *pMem to NULL. -** -** This routine calls the Mem.xDel destructor to dispose of values that -** require the destructor. But it preserves the Mem.zMalloc memory allocation. -** To free all resources, use sqlcipher_sqlite3VdbeMemRelease(), which both calls this -** routine to invoke the destructor and deallocates Mem.zMalloc. -** -** Use this routine to reset the Mem prior to insert a new value. -** -** Use sqlcipher_sqlite3VdbeMemRelease() to complete erase the Mem prior to abandoning it. +** Check to see if column iCol of the given statement is valid. If +** it is, return a pointer to the Mem for the value of that column. +** If iCol is not valid, return a pointer to a Mem which has a value +** of NULL. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetNull(Mem *pMem){ - if( VdbeMemDynamic(pMem) ){ - vdbeMemClearExternAndSetNull(pMem); +static Mem *columnMem(sqlcipher_sqlite3_stmt *pStmt, int i){ + Vdbe *pVm; + Mem *pOut; + + pVm = (Vdbe *)pStmt; + if( pVm==0 ) return (Mem*)columnNullValue(); + assert( pVm->db ); + sqlcipher_sqlite3_mutex_enter(pVm->db->mutex); + if( pVm->pResultSet!=0 && inResColumn && i>=0 ){ + pOut = &pVm->pResultSet[i]; }else{ - pMem->flags = MEM_Null; + sqlcipher_sqlite3Error(pVm->db, SQLITE_RANGE); + pOut = (Mem*)columnNullValue(); } -} -SQLITE_PRIVATE void sqlcipher_sqlite3ValueSetNull(sqlcipher_sqlite3_value *p){ - sqlcipher_sqlite3VdbeMemSetNull((Mem*)p); + return pOut; } /* -** Delete any previous value and set the value to be a BLOB of length -** n containing all zeros. +** This function is called after invoking an sqlcipher_sqlite3_value_XXX function on a +** column value (i.e. a value returned by evaluating an SQL expression in the +** select list of a SELECT statement) that may cause a malloc() failure. If +** malloc() has failed, the threads mallocFailed flag is cleared and the result +** code of statement pStmt set to SQLITE_NOMEM. +** +** Specifically, this is called from within: +** +** sqlcipher_sqlite3_column_int() +** sqlcipher_sqlite3_column_int64() +** sqlcipher_sqlite3_column_text() +** sqlcipher_sqlite3_column_text16() +** sqlcipher_sqlite3_column_real() +** sqlcipher_sqlite3_column_bytes() +** sqlcipher_sqlite3_column_bytes16() +** sqiite3_column_blob() */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ - sqlcipher_sqlite3VdbeMemRelease(pMem); - pMem->flags = MEM_Blob|MEM_Zero; - pMem->n = 0; - if( n<0 ) n = 0; - pMem->u.nZero = n; - pMem->enc = SQLITE_UTF8; - pMem->z = 0; +static void columnMallocFailure(sqlcipher_sqlite3_stmt *pStmt) +{ + /* If malloc() failed during an encoding conversion within an + ** sqlcipher_sqlite3_column_XXX API, then set the return code of the statement to + ** SQLITE_NOMEM. The next call to _step() (if any) will return SQLITE_ERROR + ** and _finalize() will return NOMEM. + */ + Vdbe *p = (Vdbe *)pStmt; + if( p ){ + assert( p->db!=0 ); + assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); + p->rc = sqlcipher_sqlite3ApiExit(p->db, p->rc); + sqlcipher_sqlite3_mutex_leave(p->db->mutex); + } } -/* -** The pMem is known to contain content that needs to be destroyed prior -** to a value change. So invoke the destructor, then set the value to -** a 64-bit integer. +/**************************** sqlcipher_sqlite3_column_ ******************************* +** The following routines are used to access elements of the current row +** in the result set. */ -static SQLITE_NOINLINE void vdbeReleaseAndSetInt64(Mem *pMem, i64 val){ - sqlcipher_sqlite3VdbeMemSetNull(pMem); - pMem->u.i = val; - pMem->flags = MEM_Int; +SQLITE_API const void *sqlcipher_sqlite3_column_blob(sqlcipher_sqlite3_stmt *pStmt, int i){ + const void *val; + val = sqlcipher_sqlite3_value_blob( columnMem(pStmt,i) ); + /* Even though there is no encoding conversion, value_blob() might + ** need to call malloc() to expand the result of a zeroblob() + ** expression. + */ + columnMallocFailure(pStmt); + return val; } - -/* -** Delete any previous value and set the value stored in *pMem to val, -** manifest type INTEGER. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){ - if( VdbeMemDynamic(pMem) ){ - vdbeReleaseAndSetInt64(pMem, val); - }else{ - pMem->u.i = val; - pMem->flags = MEM_Int; +SQLITE_API int sqlcipher_sqlite3_column_bytes(sqlcipher_sqlite3_stmt *pStmt, int i){ + int val = sqlcipher_sqlite3_value_bytes( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; +} +SQLITE_API int sqlcipher_sqlite3_column_bytes16(sqlcipher_sqlite3_stmt *pStmt, int i){ + int val = sqlcipher_sqlite3_value_bytes16( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; +} +SQLITE_API double sqlcipher_sqlite3_column_double(sqlcipher_sqlite3_stmt *pStmt, int i){ + double val = sqlcipher_sqlite3_value_double( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; +} +SQLITE_API int sqlcipher_sqlite3_column_int(sqlcipher_sqlite3_stmt *pStmt, int i){ + int val = sqlcipher_sqlite3_value_int( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; +} +SQLITE_API sqlite_int64 sqlcipher_sqlite3_column_int64(sqlcipher_sqlite3_stmt *pStmt, int i){ + sqlite_int64 val = sqlcipher_sqlite3_value_int64( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; +} +SQLITE_API const unsigned char *sqlcipher_sqlite3_column_text(sqlcipher_sqlite3_stmt *pStmt, int i){ + const unsigned char *val = sqlcipher_sqlite3_value_text( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; +} +SQLITE_API sqlcipher_sqlite3_value *sqlcipher_sqlite3_column_value(sqlcipher_sqlite3_stmt *pStmt, int i){ + Mem *pOut = columnMem(pStmt, i); + if( pOut->flags&MEM_Static ){ + pOut->flags &= ~MEM_Static; + pOut->flags |= MEM_Ephem; } + columnMallocFailure(pStmt); + return (sqlcipher_sqlite3_value *)pOut; +} +#ifndef SQLITE_OMIT_UTF16 +SQLITE_API const void *sqlcipher_sqlite3_column_text16(sqlcipher_sqlite3_stmt *pStmt, int i){ + const void *val = sqlcipher_sqlite3_value_text16( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; +} +#endif /* SQLITE_OMIT_UTF16 */ +SQLITE_API int sqlcipher_sqlite3_column_type(sqlcipher_sqlite3_stmt *pStmt, int i){ + int iType = sqlcipher_sqlite3_value_type( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return iType; } - -/* A no-op destructor */ -SQLITE_PRIVATE void sqlcipher_sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); } /* -** Set the value stored in *pMem should already be a NULL. -** Also store a pointer to go with it. +** Convert the N-th element of pStmt->pColName[] into a string using +** xFunc() then return that string. If N is out of range, return 0. +** +** There are up to 5 names for each column. useType determines which +** name is returned. Here are the names: +** +** 0 The column name as it should be displayed for output +** 1 The datatype name for the column +** 2 The name of the database that the column derives from +** 3 The name of the table that the column derives from +** 4 The name of the table column that the result column derives from +** +** If the result is not a simple column reference (if it is an expression +** or a constant) then useTypes 2, 3, and 4 return NULL. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetPointer( - Mem *pMem, - void *pPtr, - const char *zPType, - void (*xDestructor)(void*) +static const void *columnName( + sqlcipher_sqlite3_stmt *pStmt, /* The statement */ + int N, /* Which column to get the name for */ + int useUtf16, /* True to return the name as UTF16 */ + int useType /* What type of name */ ){ - assert( pMem->flags==MEM_Null ); - pMem->u.zPType = zPType ? zPType : ""; - pMem->z = pPtr; - pMem->flags = MEM_Null|MEM_Dyn|MEM_Subtype|MEM_Term; - pMem->eSubtype = 'p'; - pMem->xDel = xDestructor ? xDestructor : sqlcipher_sqlite3NoopDestructor; + const void *ret; + Vdbe *p; + int n; + sqlcipher_sqlite3 *db; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pStmt==0 ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif + ret = 0; + p = (Vdbe *)pStmt; + db = p->db; + assert( db!=0 ); + n = sqlcipher_sqlite3_column_count(pStmt); + if( N=0 ){ + N += useType*n; + sqlcipher_sqlite3_mutex_enter(db->mutex); + assert( db->mallocFailed==0 ); +#ifndef SQLITE_OMIT_UTF16 + if( useUtf16 ){ + ret = sqlcipher_sqlite3_value_text16((sqlcipher_sqlite3_value*)&p->aColName[N]); + }else +#endif + { + ret = sqlcipher_sqlite3_value_text((sqlcipher_sqlite3_value*)&p->aColName[N]); + } + /* A malloc may have failed inside of the _text() call. If this + ** is the case, clear the mallocFailed flag and return NULL. + */ + if( db->mallocFailed ){ + sqlcipher_sqlite3OomClear(db); + ret = 0; + } + sqlcipher_sqlite3_mutex_leave(db->mutex); + } + return ret; } -#ifndef SQLITE_OMIT_FLOATING_POINT /* -** Delete any previous value and set the value stored in *pMem to val, -** manifest type REAL. +** Return the name of the Nth column of the result set returned by SQL +** statement pStmt. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemSetDouble(Mem *pMem, double val){ - sqlcipher_sqlite3VdbeMemSetNull(pMem); - if( !sqlcipher_sqlite3IsNaN(val) ){ - pMem->u.r = val; - pMem->flags = MEM_Real; - } +SQLITE_API const char *sqlcipher_sqlite3_column_name(sqlcipher_sqlite3_stmt *pStmt, int N){ + return columnName(pStmt, N, 0, COLNAME_NAME); +} +#ifndef SQLITE_OMIT_UTF16 +SQLITE_API const void *sqlcipher_sqlite3_column_name16(sqlcipher_sqlite3_stmt *pStmt, int N){ + return columnName(pStmt, N, 1, COLNAME_NAME); } #endif -#ifdef SQLITE_DEBUG /* -** Return true if the Mem holds a RowSet object. This routine is intended -** for use inside of assert() statements. +** Constraint: If you have ENABLE_COLUMN_METADATA then you must +** not define OMIT_DECLTYPE. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemIsRowSet(const Mem *pMem){ - return (pMem->flags&(MEM_Blob|MEM_Dyn))==(MEM_Blob|MEM_Dyn) - && pMem->xDel==sqlcipher_sqlite3RowSetDelete; -} +#if defined(SQLITE_OMIT_DECLTYPE) && defined(SQLITE_ENABLE_COLUMN_METADATA) +# error "Must not define both SQLITE_OMIT_DECLTYPE \ + and SQLITE_ENABLE_COLUMN_METADATA" #endif +#ifndef SQLITE_OMIT_DECLTYPE /* -** Delete any previous value and set the value of pMem to be an -** empty boolean index. -** -** Return SQLITE_OK on success and SQLITE_NOMEM if a memory allocation -** error occurs. +** Return the column declaration type (if applicable) of the 'i'th column +** of the result set of SQL statement pStmt. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemSetRowSet(Mem *pMem){ - sqlcipher_sqlite3 *db = pMem->db; - RowSet *p; - assert( db!=0 ); - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); - sqlcipher_sqlite3VdbeMemRelease(pMem); - p = sqlcipher_sqlite3RowSetInit(db); - if( p==0 ) return SQLITE_NOMEM; - pMem->z = (char*)p; - pMem->flags = MEM_Blob|MEM_Dyn; - pMem->xDel = sqlcipher_sqlite3RowSetDelete; - return SQLITE_OK; +SQLITE_API const char *sqlcipher_sqlite3_column_decltype(sqlcipher_sqlite3_stmt *pStmt, int N){ + return columnName(pStmt, N, 0, COLNAME_DECLTYPE); } - -/* -** Return true if the Mem object contains a TEXT or BLOB that is -** too large - whose size exceeds SQLITE_MAX_LENGTH. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemTooBig(Mem *p){ - assert( p->db!=0 ); - if( p->flags & (MEM_Str|MEM_Blob) ){ - int n = p->n; - if( p->flags & MEM_Zero ){ - n += p->u.nZero; - } - return n>p->db->aLimit[SQLITE_LIMIT_LENGTH]; - } - return 0; +#ifndef SQLITE_OMIT_UTF16 +SQLITE_API const void *sqlcipher_sqlite3_column_decltype16(sqlcipher_sqlite3_stmt *pStmt, int N){ + return columnName(pStmt, N, 1, COLNAME_DECLTYPE); } +#endif /* SQLITE_OMIT_UTF16 */ +#endif /* SQLITE_OMIT_DECLTYPE */ -#ifdef SQLITE_DEBUG +#ifdef SQLITE_ENABLE_COLUMN_METADATA /* -** This routine prepares a memory cell for modification by breaking -** its link to a shallow copy and by marking any current shallow -** copies of this cell as invalid. -** -** This is used for testing and debugging only - to help ensure that shallow -** copies (created by OP_SCopy) are not misused. +** Return the name of the database from which a result column derives. +** NULL is returned if the result column is an expression or constant or +** anything else which is not an unambiguous reference to a database column. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ - int i; - Mem *pX; - for(i=1, pX=pVdbe->aMem+1; inMem; i++, pX++){ - if( pX->pScopyFrom==pMem ){ - u16 mFlags; - if( pVdbe->db->flags & SQLITE_VdbeTrace ){ - sqlcipher_sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n", - (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem)); - } - /* If pX is marked as a shallow copy of pMem, then try to verify that - ** no significant changes have been made to pX since the OP_SCopy. - ** A significant change would indicated a missed call to this - ** function for pX. Minor changes, such as adding or removing a - ** dual type, are allowed, as long as the underlying value is the - ** same. */ - mFlags = pMem->flags & pX->flags & pX->mScopyFlags; - assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i ); - - /* pMem is the register that is changing. But also mark pX as - ** undefined so that we can quickly detect the shallow-copy error */ - pX->flags = MEM_Undefined; - pX->pScopyFrom = 0; - } - } - pMem->pScopyFrom = 0; +SQLITE_API const char *sqlcipher_sqlite3_column_database_name(sqlcipher_sqlite3_stmt *pStmt, int N){ + return columnName(pStmt, N, 0, COLNAME_DATABASE); } -#endif /* SQLITE_DEBUG */ +#ifndef SQLITE_OMIT_UTF16 +SQLITE_API const void *sqlcipher_sqlite3_column_database_name16(sqlcipher_sqlite3_stmt *pStmt, int N){ + return columnName(pStmt, N, 1, COLNAME_DATABASE); +} +#endif /* SQLITE_OMIT_UTF16 */ /* -** Make an shallow copy of pFrom into pTo. Prior contents of -** pTo are freed. The pFrom->z field is not duplicated. If -** pFrom->z is used, then pTo->z points to the same thing as pFrom->z -** and flags gets srcType (either MEM_Ephem or MEM_Static). +** Return the name of the table from which a result column derives. +** NULL is returned if the result column is an expression or constant or +** anything else which is not an unambiguous reference to a database column. */ -static SQLITE_NOINLINE void vdbeClrCopy(Mem *pTo, const Mem *pFrom, int eType){ - vdbeMemClearExternAndSetNull(pTo); - assert( !VdbeMemDynamic(pTo) ); - sqlcipher_sqlite3VdbeMemShallowCopy(pTo, pFrom, eType); +SQLITE_API const char *sqlcipher_sqlite3_column_table_name(sqlcipher_sqlite3_stmt *pStmt, int N){ + return columnName(pStmt, N, 0, COLNAME_TABLE); } -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){ - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pFrom) ); - assert( pTo->db==pFrom->db ); - if( VdbeMemDynamic(pTo) ){ vdbeClrCopy(pTo,pFrom,srcType); return; } - memcpy(pTo, pFrom, MEMCELLSIZE); - if( (pFrom->flags&MEM_Static)==0 ){ - pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Ephem); - assert( srcType==MEM_Ephem || srcType==MEM_Static ); - pTo->flags |= srcType; - } +#ifndef SQLITE_OMIT_UTF16 +SQLITE_API const void *sqlcipher_sqlite3_column_table_name16(sqlcipher_sqlite3_stmt *pStmt, int N){ + return columnName(pStmt, N, 1, COLNAME_TABLE); } +#endif /* SQLITE_OMIT_UTF16 */ /* -** Make a full copy of pFrom into pTo. Prior contents of pTo are -** freed before the copy is made. +** Return the name of the table column from which a result column derives. +** NULL is returned if the result column is an expression or constant or +** anything else which is not an unambiguous reference to a database column. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){ - int rc = SQLITE_OK; - - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pFrom) ); - if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo); - memcpy(pTo, pFrom, MEMCELLSIZE); - pTo->flags &= ~MEM_Dyn; - if( pTo->flags&(MEM_Str|MEM_Blob) ){ - if( 0==(pFrom->flags&MEM_Static) ){ - pTo->flags |= MEM_Ephem; - rc = sqlcipher_sqlite3VdbeMemMakeWriteable(pTo); - } - } - - return rc; +SQLITE_API const char *sqlcipher_sqlite3_column_origin_name(sqlcipher_sqlite3_stmt *pStmt, int N){ + return columnName(pStmt, N, 0, COLNAME_COLUMN); +} +#ifndef SQLITE_OMIT_UTF16 +SQLITE_API const void *sqlcipher_sqlite3_column_origin_name16(sqlcipher_sqlite3_stmt *pStmt, int N){ + return columnName(pStmt, N, 1, COLNAME_COLUMN); } +#endif /* SQLITE_OMIT_UTF16 */ +#endif /* SQLITE_ENABLE_COLUMN_METADATA */ -/* -** Transfer the contents of pFrom to pTo. Any existing value in pTo is -** freed. If pFrom contains ephemeral data, a copy is made. + +/******************************* sqlcipher_sqlite3_bind_ *************************** ** -** pFrom contains an SQL NULL when this routine returns. +** Routines used to attach values to wildcards in a compiled SQL statement. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){ - assert( pFrom->db==0 || sqlcipher_sqlite3_mutex_held(pFrom->db->mutex) ); - assert( pTo->db==0 || sqlcipher_sqlite3_mutex_held(pTo->db->mutex) ); - assert( pFrom->db==0 || pTo->db==0 || pFrom->db==pTo->db ); - - sqlcipher_sqlite3VdbeMemRelease(pTo); - memcpy(pTo, pFrom, sizeof(Mem)); - pFrom->flags = MEM_Null; - pFrom->szMalloc = 0; -} - /* -** Change the value of a Mem to be a string or a BLOB. +** Unbind the value bound to variable i in virtual machine p. This is the +** the same as binding a NULL value to the column. If the "i" parameter is +** out of range, then SQLITE_RANGE is returned. Othewise SQLITE_OK. ** -** The memory management strategy depends on the value of the xDel -** parameter. If the value passed is SQLITE_TRANSIENT, then the -** string is copied into a (possibly existing) buffer managed by the -** Mem structure. Otherwise, any existing buffer is freed and the -** pointer copied. +** A successful evaluation of this routine acquires the mutex on p. +** the mutex is released if any kind of error occurs. ** -** If the string is too large (if it exceeds the SQLITE_LIMIT_LENGTH -** size limit) then no memory allocation occurs. If the string can be -** stored without allocating memory, then it is. If a memory allocation -** is required to store the string, then value of pMem is unchanged. In -** either case, SQLITE_TOOBIG is returned. +** The error code stored in database p->db is overwritten with the return +** value in any case. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemSetStr( - Mem *pMem, /* Memory cell to set to string value */ - const char *z, /* String pointer */ - int n, /* Bytes in string, or negative */ - u8 enc, /* Encoding of z. 0 for BLOBs */ - void (*xDel)(void*) /* Destructor function */ -){ - int nByte = n; /* New value for pMem->n */ - int iLimit; /* Maximum allowed string or blob size */ - u16 flags = 0; /* New value for pMem->flags */ - - assert( pMem->db==0 || sqlcipher_sqlite3_mutex_held(pMem->db->mutex) ); - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); - - /* If z is a NULL pointer, set pMem to contain an SQL NULL. */ - if( !z ){ - sqlcipher_sqlite3VdbeMemSetNull(pMem); - return SQLITE_OK; +static int vdbeUnbind(Vdbe *p, int i){ + Mem *pVar; + if( vdbeSafetyNotNull(p) ){ + return SQLITE_MISUSE_BKPT; } - - if( pMem->db ){ - iLimit = pMem->db->aLimit[SQLITE_LIMIT_LENGTH]; - }else{ - iLimit = SQLITE_MAX_LENGTH; + sqlcipher_sqlite3_mutex_enter(p->db->mutex); + if( p->eVdbeState!=VDBE_READY_STATE ){ + sqlcipher_sqlite3Error(p->db, SQLITE_MISUSE); + sqlcipher_sqlite3_mutex_leave(p->db->mutex); + sqlcipher_sqlite3_log(SQLITE_MISUSE, + "bind on a busy prepared statement: [%s]", p->zSql); + return SQLITE_MISUSE_BKPT; } - flags = (enc==0?MEM_Blob:MEM_Str); - if( nByte<0 ){ - assert( enc!=0 ); - if( enc==SQLITE_UTF8 ){ - nByte = 0x7fffffff & (int)strlen(z); - }else{ - for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} - } - flags |= MEM_Term; + if( i<1 || i>p->nVar ){ + sqlcipher_sqlite3Error(p->db, SQLITE_RANGE); + sqlcipher_sqlite3_mutex_leave(p->db->mutex); + return SQLITE_RANGE; } + i--; + pVar = &p->aVar[i]; + sqlcipher_sqlite3VdbeMemRelease(pVar); + pVar->flags = MEM_Null; + p->db->errCode = SQLITE_OK; - /* The following block sets the new values of Mem.z and Mem.xDel. It - ** also sets a flag in local variable "flags" to indicate the memory - ** management (one of MEM_Dyn or MEM_Static). + /* If the bit corresponding to this variable in Vdbe.expmask is set, then + ** binding a new value to this variable invalidates the current query plan. + ** + ** IMPLEMENTATION-OF: R-57496-20354 If the specific value bound to a host + ** parameter in the WHERE clause might influence the choice of query plan + ** for a statement, then the statement will be automatically recompiled, + ** as if there had been a schema change, on the first sqlcipher_sqlite3_step() call + ** following any change to the bindings of that parameter. */ - if( xDel==SQLITE_TRANSIENT ){ - u32 nAlloc = nByte; - if( flags&MEM_Term ){ - nAlloc += (enc==SQLITE_UTF8?1:2); - } - if( nByte>iLimit ){ - return sqlcipher_sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); - } - testcase( nAlloc==0 ); - testcase( nAlloc==31 ); - testcase( nAlloc==32 ); - if( sqlcipher_sqlite3VdbeMemClearAndResize(pMem, (int)MAX(nAlloc,32)) ){ - return SQLITE_NOMEM_BKPT; - } - memcpy(pMem->z, z, nAlloc); - }else{ - sqlcipher_sqlite3VdbeMemRelease(pMem); - pMem->z = (char *)z; - if( xDel==SQLITE_DYNAMIC ){ - pMem->zMalloc = pMem->z; - pMem->szMalloc = sqlcipher_sqlite3DbMallocSize(pMem->db, pMem->zMalloc); - }else{ - pMem->xDel = xDel; - flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn); - } - } - - pMem->n = nByte; - pMem->flags = flags; - if( enc ){ - pMem->enc = enc; -#ifdef SQLITE_ENABLE_SESSION - }else if( pMem->db==0 ){ - pMem->enc = SQLITE_UTF8; -#endif - }else{ - assert( pMem->db!=0 ); - pMem->enc = ENC(pMem->db); + assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || p->expmask==0 ); + if( p->expmask!=0 && (p->expmask & (i>=31 ? 0x80000000 : (u32)1<expired = 1; } + return SQLITE_OK; +} -#ifndef SQLITE_OMIT_UTF16 - if( enc>SQLITE_UTF8 && sqlcipher_sqlite3VdbeMemHandleBom(pMem) ){ - return SQLITE_NOMEM_BKPT; - } -#endif +/* +** Bind a text or BLOB value. +*/ +static int bindText( + sqlcipher_sqlite3_stmt *pStmt, /* The statement to bind against */ + int i, /* Index of the parameter to bind */ + const void *zData, /* Pointer to the data to be bound */ + i64 nData, /* Number of bytes of data to be bound */ + void (*xDel)(void*), /* Destructor for the data */ + u8 encoding /* Encoding for the data */ +){ + Vdbe *p = (Vdbe *)pStmt; + Mem *pVar; + int rc; - if( nByte>iLimit ){ - return SQLITE_TOOBIG; + rc = vdbeUnbind(p, i); + if( rc==SQLITE_OK ){ + if( zData!=0 ){ + pVar = &p->aVar[i-1]; + rc = sqlcipher_sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); + if( rc==SQLITE_OK && encoding!=0 ){ + rc = sqlcipher_sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); + } + if( rc ){ + sqlcipher_sqlite3Error(p->db, rc); + rc = sqlcipher_sqlite3ApiExit(p->db, rc); + } + } + sqlcipher_sqlite3_mutex_leave(p->db->mutex); + }else if( xDel!=SQLITE_STATIC && xDel!=SQLITE_TRANSIENT ){ + xDel((void*)zData); } - - return SQLITE_OK; + return rc; } + /* -** Move data out of a btree key or data field and into a Mem structure. -** The data is payload from the entry that pCur is currently pointing -** to. offset and amt determine what portion of the data or key to retrieve. -** The result is written into the pMem element. -** -** The pMem object must have been initialized. This routine will use -** pMem->zMalloc to hold the content from the btree, if possible. New -** pMem->zMalloc space will be allocated if necessary. The calling routine -** is responsible for making sure that the pMem object is eventually -** destroyed. -** -** If this routine fails for any reason (malloc returns NULL or unable -** to read from the disk) then the pMem is left in an inconsistent state. +** Bind a blob value to an SQL statement variable. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemFromBtree( - BtCursor *pCur, /* Cursor pointing at record to retrieve. */ - u32 offset, /* Offset from the start of data to return bytes from. */ - u32 amt, /* Number of bytes to return. */ - Mem *pMem /* OUT: Return data in this Mem structure. */ +SQLITE_API int sqlcipher_sqlite3_bind_blob( + sqlcipher_sqlite3_stmt *pStmt, + int i, + const void *zData, + int nData, + void (*xDel)(void*) +){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( nData<0 ) return SQLITE_MISUSE_BKPT; +#endif + return bindText(pStmt, i, zData, nData, xDel, 0); +} +SQLITE_API int sqlcipher_sqlite3_bind_blob64( + sqlcipher_sqlite3_stmt *pStmt, + int i, + const void *zData, + sqlcipher_sqlite3_uint64 nData, + void (*xDel)(void*) ){ + assert( xDel!=SQLITE_DYNAMIC ); + return bindText(pStmt, i, zData, nData, xDel, 0); +} +SQLITE_API int sqlcipher_sqlite3_bind_double(sqlcipher_sqlite3_stmt *pStmt, int i, double rValue){ int rc; - pMem->flags = MEM_Null; - if( sqlcipher_sqlite3BtreeMaxRecordSize(pCur)aVar[i-1], rValue); + sqlcipher_sqlite3_mutex_leave(p->db->mutex); } - if( SQLITE_OK==(rc = sqlcipher_sqlite3VdbeMemClearAndResize(pMem, amt+1)) ){ - rc = sqlcipher_sqlite3BtreePayload(pCur, offset, amt, pMem->z); - if( rc==SQLITE_OK ){ - pMem->z[amt] = 0; /* Overrun area used when reading malformed records */ - pMem->flags = MEM_Blob; - pMem->n = (int)amt; - }else{ - sqlcipher_sqlite3VdbeMemRelease(pMem); - } + return rc; +} +SQLITE_API int sqlcipher_sqlite3_bind_int(sqlcipher_sqlite3_stmt *p, int i, int iValue){ + return sqlcipher_sqlite3_bind_int64(p, i, (i64)iValue); +} +SQLITE_API int sqlcipher_sqlite3_bind_int64(sqlcipher_sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ + int rc; + Vdbe *p = (Vdbe *)pStmt; + rc = vdbeUnbind(p, i); + if( rc==SQLITE_OK ){ + sqlcipher_sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue); + sqlcipher_sqlite3_mutex_leave(p->db->mutex); } return rc; } -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset( - BtCursor *pCur, /* Cursor pointing at record to retrieve. */ - u32 amt, /* Number of bytes to return. */ - Mem *pMem /* OUT: Return data in this Mem structure. */ +SQLITE_API int sqlcipher_sqlite3_bind_null(sqlcipher_sqlite3_stmt *pStmt, int i){ + int rc; + Vdbe *p = (Vdbe*)pStmt; + rc = vdbeUnbind(p, i); + if( rc==SQLITE_OK ){ + sqlcipher_sqlite3_mutex_leave(p->db->mutex); + } + return rc; +} +SQLITE_API int sqlcipher_sqlite3_bind_pointer( + sqlcipher_sqlite3_stmt *pStmt, + int i, + void *pPtr, + const char *zPTtype, + void (*xDestructor)(void*) ){ - u32 available = 0; /* Number of bytes available on the local btree page */ - int rc = SQLITE_OK; /* Return code */ - - assert( sqlcipher_sqlite3BtreeCursorIsValid(pCur) ); - assert( !VdbeMemDynamic(pMem) ); - - /* Note: the calls to BtreeKeyFetch() and DataFetch() below assert() - ** that both the BtShared and database handle mutexes are held. */ - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem) ); - pMem->z = (char *)sqlcipher_sqlite3BtreePayloadFetch(pCur, &available); - assert( pMem->z!=0 ); - - if( amt<=available ){ - pMem->flags = MEM_Blob|MEM_Ephem; - pMem->n = (int)amt; - }else{ - rc = sqlcipher_sqlite3VdbeMemFromBtree(pCur, 0, amt, pMem); + int rc; + Vdbe *p = (Vdbe*)pStmt; + rc = vdbeUnbind(p, i); + if( rc==SQLITE_OK ){ + sqlcipher_sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor); + sqlcipher_sqlite3_mutex_leave(p->db->mutex); + }else if( xDestructor ){ + xDestructor(pPtr); } - return rc; } - -/* -** The pVal argument is known to be a value other than NULL. -** Convert it into a string with encoding enc and return a pointer -** to a zero-terminated version of that string. -*/ -static SQLITE_NOINLINE const void *valueToText(sqlcipher_sqlite3_value* pVal, u8 enc){ - assert( pVal!=0 ); - assert( pVal->db==0 || sqlcipher_sqlite3_mutex_held(pVal->db->mutex) ); - assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) ); - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pVal) ); - assert( (pVal->flags & (MEM_Null))==0 ); - if( pVal->flags & (MEM_Blob|MEM_Str) ){ - if( ExpandBlob(pVal) ) return 0; - pVal->flags |= MEM_Str; - if( pVal->enc != (enc & ~SQLITE_UTF16_ALIGNED) ){ - sqlcipher_sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED); +SQLITE_API int sqlcipher_sqlite3_bind_text( + sqlcipher_sqlite3_stmt *pStmt, + int i, + const char *zData, + int nData, + void (*xDel)(void*) +){ + return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF8); +} +SQLITE_API int sqlcipher_sqlite3_bind_text64( + sqlcipher_sqlite3_stmt *pStmt, + int i, + const char *zData, + sqlcipher_sqlite3_uint64 nData, + void (*xDel)(void*), + unsigned char enc +){ + assert( xDel!=SQLITE_DYNAMIC ); + if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; + return bindText(pStmt, i, zData, nData, xDel, enc); +} +#ifndef SQLITE_OMIT_UTF16 +SQLITE_API int sqlcipher_sqlite3_bind_text16( + sqlcipher_sqlite3_stmt *pStmt, + int i, + const void *zData, + int nData, + void (*xDel)(void*) +){ + return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE); +} +#endif /* SQLITE_OMIT_UTF16 */ +SQLITE_API int sqlcipher_sqlite3_bind_value(sqlcipher_sqlite3_stmt *pStmt, int i, const sqlcipher_sqlite3_value *pValue){ + int rc; + switch( sqlcipher_sqlite3_value_type((sqlcipher_sqlite3_value*)pValue) ){ + case SQLITE_INTEGER: { + rc = sqlcipher_sqlite3_bind_int64(pStmt, i, pValue->u.i); + break; } - if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){ - assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 ); - if( sqlcipher_sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){ - return 0; + case SQLITE_FLOAT: { + assert( pValue->flags & (MEM_Real|MEM_IntReal) ); + rc = sqlcipher_sqlite3_bind_double(pStmt, i, + (pValue->flags & MEM_Real) ? pValue->u.r : (double)pValue->u.i + ); + break; + } + case SQLITE_BLOB: { + if( pValue->flags & MEM_Zero ){ + rc = sqlcipher_sqlite3_bind_zeroblob(pStmt, i, pValue->u.nZero); + }else{ + rc = sqlcipher_sqlite3_bind_blob(pStmt, i, pValue->z, pValue->n,SQLITE_TRANSIENT); } + break; + } + case SQLITE_TEXT: { + rc = bindText(pStmt,i, pValue->z, pValue->n, SQLITE_TRANSIENT, + pValue->enc); + break; + } + default: { + rc = sqlcipher_sqlite3_bind_null(pStmt, i); + break; } - sqlcipher_sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-31275-44060 */ - }else{ - sqlcipher_sqlite3VdbeMemStringify(pVal, enc, 0); - assert( 0==(1&SQLITE_PTR_TO_INT(pVal->z)) ); } - assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0 - || pVal->db->mallocFailed ); - if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){ - assert( sqlcipher_sqlite3VdbeMemValidStrRep(pVal) ); - return pVal->z; + return rc; +} +SQLITE_API int sqlcipher_sqlite3_bind_zeroblob(sqlcipher_sqlite3_stmt *pStmt, int i, int n){ + int rc; + Vdbe *p = (Vdbe *)pStmt; + rc = vdbeUnbind(p, i); + if( rc==SQLITE_OK ){ +#ifndef SQLITE_OMIT_INCRBLOB + sqlcipher_sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); +#else + rc = sqlcipher_sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); +#endif + sqlcipher_sqlite3_mutex_leave(p->db->mutex); + } + return rc; +} +SQLITE_API int sqlcipher_sqlite3_bind_zeroblob64(sqlcipher_sqlite3_stmt *pStmt, int i, sqlcipher_sqlite3_uint64 n){ + int rc; + Vdbe *p = (Vdbe *)pStmt; + sqlcipher_sqlite3_mutex_enter(p->db->mutex); + if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){ + rc = SQLITE_TOOBIG; }else{ - return 0; + assert( (n & 0x7FFFFFFF)==n ); + rc = sqlcipher_sqlite3_bind_zeroblob(pStmt, i, n); } + rc = sqlcipher_sqlite3ApiExit(p->db, rc); + sqlcipher_sqlite3_mutex_leave(p->db->mutex); + return rc; } -/* This function is only available internally, it is not part of the -** external API. It works in a similar way to sqlcipher_sqlite3_value_text(), -** except the data returned is in the encoding specified by the second -** parameter, which must be one of SQLITE_UTF16BE, SQLITE_UTF16LE or -** SQLITE_UTF8. -** -** (2006-02-16:) The enc value can be or-ed with SQLITE_UTF16_ALIGNED. -** If that is the case, then the result must be aligned on an even byte -** boundary. +/* +** Return the number of wildcards that can be potentially bound to. +** This routine is added to support DBD::SQLite. */ -SQLITE_PRIVATE const void *sqlcipher_sqlite3ValueText(sqlcipher_sqlite3_value* pVal, u8 enc){ - if( !pVal ) return 0; - assert( pVal->db==0 || sqlcipher_sqlite3_mutex_held(pVal->db->mutex) ); - assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) ); - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pVal) ); - if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){ - assert( sqlcipher_sqlite3VdbeMemValidStrRep(pVal) ); - return pVal->z; - } - if( pVal->flags&MEM_Null ){ - return 0; - } - return valueToText(pVal, enc); +SQLITE_API int sqlcipher_sqlite3_bind_parameter_count(sqlcipher_sqlite3_stmt *pStmt){ + Vdbe *p = (Vdbe*)pStmt; + return p ? p->nVar : 0; } /* -** Create a new sqlcipher_sqlite3_value object. +** Return the name of a wildcard parameter. Return NULL if the index +** is out of range or if the wildcard is unnamed. +** +** The result is always UTF-8. */ -SQLITE_PRIVATE sqlcipher_sqlite3_value *sqlcipher_sqlite3ValueNew(sqlcipher_sqlite3 *db){ - Mem *p = sqlcipher_sqlite3DbMallocZero(db, sizeof(*p)); - if( p ){ - p->flags = MEM_Null; - p->db = db; - } - return p; +SQLITE_API const char *sqlcipher_sqlite3_bind_parameter_name(sqlcipher_sqlite3_stmt *pStmt, int i){ + Vdbe *p = (Vdbe*)pStmt; + if( p==0 ) return 0; + return sqlcipher_sqlite3VListNumToName(p->pVList, i); } /* -** Context object passed by sqlcipher_sqlite3Stat4ProbeSetValue() through to -** valueNew(). See comments above valueNew() for details. +** Given a wildcard parameter name, return the index of the variable +** with that name. If there is no variable with the given name, +** return 0. */ -struct ValueNewStat4Ctx { - Parse *pParse; - Index *pIdx; - UnpackedRecord **ppRec; - int iVal; -}; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nName){ + if( p==0 || zName==0 ) return 0; + return sqlcipher_sqlite3VListNameToNum(p->pVList, zName, nName); +} +SQLITE_API int sqlcipher_sqlite3_bind_parameter_index(sqlcipher_sqlite3_stmt *pStmt, const char *zName){ + return sqlcipher_sqlite3VdbeParameterIndex((Vdbe*)pStmt, zName, sqlcipher_sqlite3Strlen30(zName)); +} /* -** Allocate and return a pointer to a new sqlcipher_sqlite3_value object. If -** the second argument to this function is NULL, the object is allocated -** by calling sqlcipher_sqlite3ValueNew(). -** -** Otherwise, if the second argument is non-zero, then this function is -** being called indirectly by sqlcipher_sqlite3Stat4ProbeSetValue(). If it has not -** already been allocated, allocate the UnpackedRecord structure that -** that function will return to its caller here. Then return a pointer to -** an sqlcipher_sqlite3_value within the UnpackedRecord.a[] array. +** Transfer all bindings from the first statement over to the second. */ -static sqlcipher_sqlite3_value *valueNew(sqlcipher_sqlite3 *db, struct ValueNewStat4Ctx *p){ -#ifdef SQLITE_ENABLE_STAT4 - if( p ){ - UnpackedRecord *pRec = p->ppRec[0]; - - if( pRec==0 ){ - Index *pIdx = p->pIdx; /* Index being probed */ - int nByte; /* Bytes of space to allocate */ - int i; /* Counter variable */ - int nCol = pIdx->nColumn; /* Number of index columns including rowid */ - - nByte = sizeof(Mem) * nCol + ROUND8(sizeof(UnpackedRecord)); - pRec = (UnpackedRecord*)sqlcipher_sqlite3DbMallocZero(db, nByte); - if( pRec ){ - pRec->pKeyInfo = sqlcipher_sqlite3KeyInfoOfIndex(p->pParse, pIdx); - if( pRec->pKeyInfo ){ - assert( pRec->pKeyInfo->nAllField==nCol ); - assert( pRec->pKeyInfo->enc==ENC(db) ); - pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord))); - for(i=0; iaMem[i].flags = MEM_Null; - pRec->aMem[i].db = db; - } - }else{ - sqlcipher_sqlite3DbFreeNN(db, pRec); - pRec = 0; - } - } - if( pRec==0 ) return 0; - p->ppRec[0] = pRec; - } - - pRec->nField = p->iVal+1; - return &pRec->aMem[p->iVal]; +SQLITE_PRIVATE int sqlcipher_sqlite3TransferBindings(sqlcipher_sqlite3_stmt *pFromStmt, sqlcipher_sqlite3_stmt *pToStmt){ + Vdbe *pFrom = (Vdbe*)pFromStmt; + Vdbe *pTo = (Vdbe*)pToStmt; + int i; + assert( pTo->db==pFrom->db ); + assert( pTo->nVar==pFrom->nVar ); + sqlcipher_sqlite3_mutex_enter(pTo->db->mutex); + for(i=0; inVar; i++){ + sqlcipher_sqlite3VdbeMemMove(&pTo->aVar[i], &pFrom->aVar[i]); } -#else - UNUSED_PARAMETER(p); -#endif /* defined(SQLITE_ENABLE_STAT4) */ - return sqlcipher_sqlite3ValueNew(db); + sqlcipher_sqlite3_mutex_leave(pTo->db->mutex); + return SQLITE_OK; } +#ifndef SQLITE_OMIT_DEPRECATED /* -** The expression object indicated by the second argument is guaranteed -** to be a scalar SQL function. If -** -** * all function arguments are SQL literals, -** * one of the SQLITE_FUNC_CONSTANT or _SLOCHNG function flags is set, and -** * the SQLITE_FUNC_NEEDCOLL function flag is not set, -** -** then this routine attempts to invoke the SQL function. Assuming no -** error occurs, output parameter (*ppVal) is set to point to a value -** object containing the result before returning SQLITE_OK. +** Deprecated external interface. Internal/core SQLite code +** should call sqlcipher_sqlite3TransferBindings. ** -** Affinity aff is applied to the result of the function before returning. -** If the result is a text value, the sqlcipher_sqlite3_value object uses encoding -** enc. +** It is misuse to call this routine with statements from different +** database connections. But as this is a deprecated interface, we +** will not bother to check for that condition. ** -** If the conditions above are not met, this function returns SQLITE_OK -** and sets (*ppVal) to NULL. Or, if an error occurs, (*ppVal) is set to -** NULL and an SQLite error code returned. +** If the two statements contain a different number of bindings, then +** an SQLITE_ERROR is returned. Nothing else can go wrong, so otherwise +** SQLITE_OK is returned. */ -#ifdef SQLITE_ENABLE_STAT4 -static int valueFromFunction( - sqlcipher_sqlite3 *db, /* The database connection */ - Expr *p, /* The expression to evaluate */ - u8 enc, /* Encoding to use */ - u8 aff, /* Affinity to use */ - sqlcipher_sqlite3_value **ppVal, /* Write the new value here */ - struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */ -){ - sqlcipher_sqlite3_context ctx; /* Context object for function invocation */ - sqlcipher_sqlite3_value **apVal = 0; /* Function arguments */ - int nVal = 0; /* Size of apVal[] array */ - FuncDef *pFunc = 0; /* Function definition */ - sqlcipher_sqlite3_value *pVal = 0; /* New value */ - int rc = SQLITE_OK; /* Return code */ - ExprList *pList = 0; /* Function arguments */ - int i; /* Iterator variable */ - - assert( pCtx!=0 ); - assert( (p->flags & EP_TokenOnly)==0 ); - pList = p->x.pList; - if( pList ) nVal = pList->nExpr; - pFunc = sqlcipher_sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0); - assert( pFunc ); - if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 - || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) - ){ - return SQLITE_OK; - } - - if( pList ){ - apVal = (sqlcipher_sqlite3_value**)sqlcipher_sqlite3DbMallocZero(db, sizeof(apVal[0]) * nVal); - if( apVal==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto value_from_function_out; - } - for(i=0; ia[i].pExpr, enc, aff, &apVal[i]); - if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out; - } - } - - pVal = valueNew(db, pCtx); - if( pVal==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto value_from_function_out; - } - - assert( pCtx->pParse->rc==SQLITE_OK ); - memset(&ctx, 0, sizeof(ctx)); - ctx.pOut = pVal; - ctx.pFunc = pFunc; - pFunc->xSFunc(&ctx, nVal, apVal); - if( ctx.isError ){ - rc = ctx.isError; - sqlcipher_sqlite3ErrorMsg(pCtx->pParse, "%s", sqlcipher_sqlite3_value_text(pVal)); - }else{ - sqlcipher_sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8); - assert( rc==SQLITE_OK ); - rc = sqlcipher_sqlite3VdbeChangeEncoding(pVal, enc); - if( rc==SQLITE_OK && sqlcipher_sqlite3VdbeMemTooBig(pVal) ){ - rc = SQLITE_TOOBIG; - pCtx->pParse->nErr++; - } +SQLITE_API int sqlcipher_sqlite3_transfer_bindings(sqlcipher_sqlite3_stmt *pFromStmt, sqlcipher_sqlite3_stmt *pToStmt){ + Vdbe *pFrom = (Vdbe*)pFromStmt; + Vdbe *pTo = (Vdbe*)pToStmt; + if( pFrom->nVar!=pTo->nVar ){ + return SQLITE_ERROR; } - pCtx->pParse->rc = rc; - - value_from_function_out: - if( rc!=SQLITE_OK ){ - pVal = 0; + assert( (pTo->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || pTo->expmask==0 ); + if( pTo->expmask ){ + pTo->expired = 1; } - if( apVal ){ - for(i=0; iprepFlags & SQLITE_PREPARE_SAVESQL)!=0 || pFrom->expmask==0 ); + if( pFrom->expmask ){ + pFrom->expired = 1; } - - *ppVal = pVal; - return rc; + return sqlcipher_sqlite3TransferBindings(pFromStmt, pToStmt); } -#else -# define valueFromFunction(a,b,c,d,e,f) SQLITE_OK -#endif /* defined(SQLITE_ENABLE_STAT4) */ +#endif /* -** Extract a value from the supplied expression in the manner described -** above sqlcipher_sqlite3ValueFromExpr(). Allocate the sqlcipher_sqlite3_value object -** using valueNew(). -** -** If pCtx is NULL and an error occurs after the sqlcipher_sqlite3_value object -** has been allocated, it is freed before returning. Or, if pCtx is not -** NULL, it is assumed that the caller will free any allocated object -** in all cases. +** Return the sqlcipher_sqlite3* database handle to which the prepared statement given +** in the argument belongs. This is the same database handle that was +** the first argument to the sqlcipher_sqlite3_prepare() that was used to create +** the statement in the first place. */ -static int valueFromExpr( - sqlcipher_sqlite3 *db, /* The database connection */ - Expr *pExpr, /* The expression to evaluate */ - u8 enc, /* Encoding to use */ - u8 affinity, /* Affinity to use */ - sqlcipher_sqlite3_value **ppVal, /* Write the new value here */ - struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */ -){ - int op; - char *zVal = 0; - sqlcipher_sqlite3_value *pVal = 0; - int negInt = 1; - const char *zNeg = ""; - int rc = SQLITE_OK; +SQLITE_API sqlcipher_sqlite3 *sqlcipher_sqlite3_db_handle(sqlcipher_sqlite3_stmt *pStmt){ + return pStmt ? ((Vdbe*)pStmt)->db : 0; +} - assert( pExpr!=0 ); - while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft; -#if defined(SQLITE_ENABLE_STAT4) - if( op==TK_REGISTER ) op = pExpr->op2; -#else - if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; -#endif +/* +** Return true if the prepared statement is guaranteed to not modify the +** database. +*/ +SQLITE_API int sqlcipher_sqlite3_stmt_readonly(sqlcipher_sqlite3_stmt *pStmt){ + return pStmt ? ((Vdbe*)pStmt)->readOnly : 1; +} - /* Compressed expressions only appear when parsing the DEFAULT clause - ** on a table column definition, and hence only when pCtx==0. This - ** check ensures that an EP_TokenOnly expression is never passed down - ** into valueFromFunction(). */ - assert( (pExpr->flags & EP_TokenOnly)==0 || pCtx==0 ); +/* +** Return 1 if the statement is an EXPLAIN and return 2 if the +** statement is an EXPLAIN QUERY PLAN +*/ +SQLITE_API int sqlcipher_sqlite3_stmt_isexplain(sqlcipher_sqlite3_stmt *pStmt){ + return pStmt ? ((Vdbe*)pStmt)->explain : 0; +} - if( op==TK_CAST ){ - u8 aff = sqlcipher_sqlite3AffinityType(pExpr->u.zToken,0); - rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx); - testcase( rc!=SQLITE_OK ); - if( *ppVal ){ - sqlcipher_sqlite3VdbeMemCast(*ppVal, aff, SQLITE_UTF8); - sqlcipher_sqlite3ValueApplyAffinity(*ppVal, affinity, SQLITE_UTF8); - } - return rc; - } +/* +** Return true if the prepared statement is in need of being reset. +*/ +SQLITE_API int sqlcipher_sqlite3_stmt_busy(sqlcipher_sqlite3_stmt *pStmt){ + Vdbe *v = (Vdbe*)pStmt; + return v!=0 && v->eVdbeState==VDBE_RUN_STATE; +} - /* Handle negative integers in a single step. This is needed in the - ** case when the value is -9223372036854775808. - */ - if( op==TK_UMINUS - && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){ - pExpr = pExpr->pLeft; - op = pExpr->op; - negInt = -1; - zNeg = "-"; +/* +** Return a pointer to the next prepared statement after pStmt associated +** with database connection pDb. If pStmt is NULL, return the first +** prepared statement for the database connection. Return NULL if there +** are no more. +*/ +SQLITE_API sqlcipher_sqlite3_stmt *sqlcipher_sqlite3_next_stmt(sqlcipher_sqlite3 *pDb, sqlcipher_sqlite3_stmt *pStmt){ + sqlcipher_sqlite3_stmt *pNext; +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlcipher_sqlite3SafetyCheckOk(pDb) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; } - - if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ - pVal = valueNew(db, pCtx); - if( pVal==0 ) goto no_mem; - if( ExprHasProperty(pExpr, EP_IntValue) ){ - sqlcipher_sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); - }else{ - zVal = sqlcipher_sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); - if( zVal==0 ) goto no_mem; - sqlcipher_sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); - } - if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){ - sqlcipher_sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); - }else{ - sqlcipher_sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); - } - assert( (pVal->flags & MEM_IntReal)==0 ); - if( pVal->flags & (MEM_Int|MEM_IntReal|MEM_Real) ){ - testcase( pVal->flags & MEM_Int ); - testcase( pVal->flags & MEM_Real ); - pVal->flags &= ~MEM_Str; - } - if( enc!=SQLITE_UTF8 ){ - rc = sqlcipher_sqlite3VdbeChangeEncoding(pVal, enc); - } - }else if( op==TK_UMINUS ) { - /* This branch happens for multiple negative signs. Ex: -(-5) */ - if( SQLITE_OK==valueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal,pCtx) - && pVal!=0 - ){ - sqlcipher_sqlite3VdbeMemNumerify(pVal); - if( pVal->flags & MEM_Real ){ - pVal->u.r = -pVal->u.r; - }else if( pVal->u.i==SMALLEST_INT64 ){ -#ifndef SQLITE_OMIT_FLOATING_POINT - pVal->u.r = -(double)SMALLEST_INT64; -#else - pVal->u.r = LARGEST_INT64; #endif - MemSetTypeFlag(pVal, MEM_Real); - }else{ - pVal->u.i = -pVal->u.i; - } - sqlcipher_sqlite3ValueApplyAffinity(pVal, affinity, enc); - } - }else if( op==TK_NULL ){ - pVal = valueNew(db, pCtx); - if( pVal==0 ) goto no_mem; - sqlcipher_sqlite3VdbeMemSetNull(pVal); - } -#ifndef SQLITE_OMIT_BLOB_LITERAL - else if( op==TK_BLOB ){ - int nVal; - assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); - assert( pExpr->u.zToken[1]=='\'' ); - pVal = valueNew(db, pCtx); - if( !pVal ) goto no_mem; - zVal = &pExpr->u.zToken[2]; - nVal = sqlcipher_sqlite3Strlen30(zVal)-1; - assert( zVal[nVal]=='\'' ); - sqlcipher_sqlite3VdbeMemSetStr(pVal, sqlcipher_sqlite3HexToBlob(db, zVal, nVal), nVal/2, - 0, SQLITE_DYNAMIC); + sqlcipher_sqlite3_mutex_enter(pDb->mutex); + if( pStmt==0 ){ + pNext = (sqlcipher_sqlite3_stmt*)pDb->pVdbe; + }else{ + pNext = (sqlcipher_sqlite3_stmt*)((Vdbe*)pStmt)->pNext; } -#endif -#ifdef SQLITE_ENABLE_STAT4 - else if( op==TK_FUNCTION && pCtx!=0 ){ - rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx); + sqlcipher_sqlite3_mutex_leave(pDb->mutex); + return pNext; +} + +/* +** Return the value of a status counter for a prepared statement +*/ +SQLITE_API int sqlcipher_sqlite3_stmt_status(sqlcipher_sqlite3_stmt *pStmt, int op, int resetFlag){ + Vdbe *pVdbe = (Vdbe*)pStmt; + u32 v; +#ifdef SQLITE_ENABLE_API_ARMOR + if( !pStmt + || (op!=SQLITE_STMTSTATUS_MEMUSED && (op<0||op>=ArraySize(pVdbe->aCounter))) + ){ + (void)SQLITE_MISUSE_BKPT; + return 0; } #endif - else if( op==TK_TRUEFALSE ){ - pVal = valueNew(db, pCtx); - if( pVal ){ - pVal->flags = MEM_Int; - pVal->u.i = pExpr->u.zToken[4]==0; - } + if( op==SQLITE_STMTSTATUS_MEMUSED ){ + sqlcipher_sqlite3 *db = pVdbe->db; + sqlcipher_sqlite3_mutex_enter(db->mutex); + v = 0; + db->pnBytesFreed = (int*)&v; + sqlcipher_sqlite3VdbeDelete(pVdbe); + db->pnBytesFreed = 0; + sqlcipher_sqlite3_mutex_leave(db->mutex); + }else{ + v = pVdbe->aCounter[op]; + if( resetFlag ) pVdbe->aCounter[op] = 0; } + return (int)v; +} - *ppVal = pVal; - return rc; +/* +** Return the SQL associated with a prepared statement +*/ +SQLITE_API const char *sqlcipher_sqlite3_sql(sqlcipher_sqlite3_stmt *pStmt){ + Vdbe *p = (Vdbe *)pStmt; + return p ? p->zSql : 0; +} -no_mem: -#ifdef SQLITE_ENABLE_STAT4 - if( pCtx==0 || pCtx->pParse->nErr==0 ) -#endif - sqlcipher_sqlite3OomFault(db); - sqlcipher_sqlite3DbFree(db, zVal); - assert( *ppVal==0 ); -#ifdef SQLITE_ENABLE_STAT4 - if( pCtx==0 ) sqlcipher_sqlite3ValueFree(pVal); +/* +** Return the SQL associated with a prepared statement with +** bound parameters expanded. Space to hold the returned string is +** obtained from sqlcipher_sqlite3_malloc(). The caller is responsible for +** freeing the returned string by passing it to sqlcipher_sqlite3_free(). +** +** The SQLITE_TRACE_SIZE_LIMIT puts an upper bound on the size of +** expanded bound parameters. +*/ +SQLITE_API char *sqlcipher_sqlite3_expanded_sql(sqlcipher_sqlite3_stmt *pStmt){ +#ifdef SQLITE_OMIT_TRACE + return 0; #else - assert( pCtx==0 ); sqlcipher_sqlite3ValueFree(pVal); + char *z = 0; + const char *zSql = sqlcipher_sqlite3_sql(pStmt); + if( zSql ){ + Vdbe *p = (Vdbe *)pStmt; + sqlcipher_sqlite3_mutex_enter(p->db->mutex); + z = sqlcipher_sqlite3VdbeExpandSql(p, zSql); + sqlcipher_sqlite3_mutex_leave(p->db->mutex); + } + return z; #endif - return SQLITE_NOMEM_BKPT; } +#ifdef SQLITE_ENABLE_NORMALIZE /* -** Create a new sqlcipher_sqlite3_value object, containing the value of pExpr. -** -** This only works for very simple expressions that consist of one constant -** token (i.e. "5", "5.1", "'a string'"). If the expression can -** be converted directly into a value, then the value is allocated and -** a pointer written to *ppVal. The caller is responsible for deallocating -** the value by passing it to sqlcipher_sqlite3ValueFree() later on. If the expression -** cannot be converted to a value, then *ppVal is set to NULL. +** Return the normalized SQL associated with a prepared statement. */ -SQLITE_PRIVATE int sqlcipher_sqlite3ValueFromExpr( - sqlcipher_sqlite3 *db, /* The database connection */ - Expr *pExpr, /* The expression to evaluate */ - u8 enc, /* Encoding to use */ - u8 affinity, /* Affinity to use */ - sqlcipher_sqlite3_value **ppVal /* Write the new value here */ -){ - return pExpr ? valueFromExpr(db, pExpr, enc, affinity, ppVal, 0) : 0; +SQLITE_API const char *sqlcipher_sqlite3_normalized_sql(sqlcipher_sqlite3_stmt *pStmt){ + Vdbe *p = (Vdbe *)pStmt; + if( p==0 ) return 0; + if( p->zNormSql==0 && ALWAYS(p->zSql!=0) ){ + sqlcipher_sqlite3_mutex_enter(p->db->mutex); + p->zNormSql = sqlcipher_sqlite3Normalize(p, p->zSql); + sqlcipher_sqlite3_mutex_leave(p->db->mutex); + } + return p->zNormSql; } +#endif /* SQLITE_ENABLE_NORMALIZE */ -#ifdef SQLITE_ENABLE_STAT4 +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* -** Attempt to extract a value from pExpr and use it to construct *ppVal. -** -** If pAlloc is not NULL, then an UnpackedRecord object is created for -** pAlloc if one does not exist and the new value is added to the -** UnpackedRecord object. -** -** A value is extracted in the following cases: -** -** * (pExpr==0). In this case the value is assumed to be an SQL NULL, -** -** * The expression is a bound variable, and this is a reprepare, or -** -** * The expression is a literal value. -** -** On success, *ppVal is made to point to the extracted value. The caller -** is responsible for ensuring that the value is eventually freed. +** Allocate and populate an UnpackedRecord structure based on the serialized +** record in nKey/pKey. Return a pointer to the new UnpackedRecord structure +** if successful, or a NULL pointer if an OOM error is encountered. */ -static int stat4ValueFromExpr( - Parse *pParse, /* Parse context */ - Expr *pExpr, /* The expression to extract a value from */ - u8 affinity, /* Affinity to use */ - struct ValueNewStat4Ctx *pAlloc,/* How to allocate space. Or NULL */ - sqlcipher_sqlite3_value **ppVal /* OUT: New value object (or NULL) */ +static UnpackedRecord *vdbeUnpackRecord( + KeyInfo *pKeyInfo, + int nKey, + const void *pKey ){ - int rc = SQLITE_OK; - sqlcipher_sqlite3_value *pVal = 0; - sqlcipher_sqlite3 *db = pParse->db; - - /* Skip over any TK_COLLATE nodes */ - pExpr = sqlcipher_sqlite3ExprSkipCollate(pExpr); + UnpackedRecord *pRet; /* Return value */ - assert( pExpr==0 || pExpr->op!=TK_REGISTER || pExpr->op2!=TK_VARIABLE ); - if( !pExpr ){ - pVal = valueNew(db, pAlloc); - if( pVal ){ - sqlcipher_sqlite3VdbeMemSetNull((Mem*)pVal); - } - }else if( pExpr->op==TK_VARIABLE && (db->flags & SQLITE_EnableQPSG)==0 ){ - Vdbe *v; - int iBindVar = pExpr->iColumn; - sqlcipher_sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar); - if( (v = pParse->pReprepare)!=0 ){ - pVal = valueNew(db, pAlloc); - if( pVal ){ - rc = sqlcipher_sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]); - sqlcipher_sqlite3ValueApplyAffinity(pVal, affinity, ENC(db)); - pVal->db = pParse->db; - } - } - }else{ - rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, pAlloc); + pRet = sqlcipher_sqlite3VdbeAllocUnpackedRecord(pKeyInfo); + if( pRet ){ + memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nKeyField+1)); + sqlcipher_sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet); } - - assert( pVal==0 || pVal->db==db ); - *ppVal = pVal; - return rc; + return pRet; } /* -** This function is used to allocate and populate UnpackedRecord -** structures intended to be compared against sample index keys stored -** in the sqlite_stat4 table. -** -** A single call to this function populates zero or more fields of the -** record starting with field iVal (fields are numbered from left to -** right starting with 0). A single field is populated if: -** -** * (pExpr==0). In this case the value is assumed to be an SQL NULL, -** -** * The expression is a bound variable, and this is a reprepare, or -** -** * The sqlcipher_sqlite3ValueFromExpr() function is able to extract a value -** from the expression (i.e. the expression is a literal value). -** -** Or, if pExpr is a TK_VECTOR, one field is populated for each of the -** vector components that match either of the two latter criteria listed -** above. -** -** Before any value is appended to the record, the affinity of the -** corresponding column within index pIdx is applied to it. Before -** this function returns, output parameter *pnExtract is set to the -** number of values appended to the record. -** -** When this function is called, *ppRec must either point to an object -** allocated by an earlier call to this function, or must be NULL. If it -** is NULL and a value can be successfully extracted, a new UnpackedRecord -** is allocated (and *ppRec set to point to it) before returning. -** -** Unless an error is encountered, SQLITE_OK is returned. It is not an -** error if a value cannot be extracted from pExpr. If an error does -** occur, an SQLite error code is returned. +** This function is called from within a pre-update callback to retrieve +** a field of the row currently being updated or deleted. */ -SQLITE_PRIVATE int sqlcipher_sqlite3Stat4ProbeSetValue( - Parse *pParse, /* Parse context */ - Index *pIdx, /* Index being probed */ - UnpackedRecord **ppRec, /* IN/OUT: Probe record */ - Expr *pExpr, /* The expression to extract a value from */ - int nElem, /* Maximum number of values to append */ - int iVal, /* Array element to populate */ - int *pnExtract /* OUT: Values appended to the record */ -){ +SQLITE_API int sqlcipher_sqlite3_preupdate_old(sqlcipher_sqlite3 *db, int iIdx, sqlcipher_sqlite3_value **ppValue){ + PreUpdate *p = db->pPreUpdate; + Mem *pMem; int rc = SQLITE_OK; - int nExtract = 0; - if( pExpr==0 || pExpr->op!=TK_SELECT ){ - int i; - struct ValueNewStat4Ctx alloc; + /* Test that this call is being made from within an SQLITE_DELETE or + ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */ + if( !p || p->op==SQLITE_INSERT ){ + rc = SQLITE_MISUSE_BKPT; + goto preupdate_old_out; + } + if( p->pPk ){ + iIdx = sqlcipher_sqlite3TableColumnToIndex(p->pPk, iIdx); + } + if( iIdx>=p->pCsr->nField || iIdx<0 ){ + rc = SQLITE_RANGE; + goto preupdate_old_out; + } - alloc.pParse = pParse; - alloc.pIdx = pIdx; - alloc.ppRec = ppRec; + /* If the old.* record has not yet been loaded into memory, do so now. */ + if( p->pUnpacked==0 ){ + u32 nRec; + u8 *aRec; - for(i=0; idb, pIdx, iVal+i); - alloc.iVal = iVal+i; - rc = stat4ValueFromExpr(pParse, pElem, aff, &alloc, &pVal); - if( !pVal ) break; - nExtract++; + assert( p->pCsr->eCurType==CURTYPE_BTREE ); + nRec = sqlcipher_sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); + aRec = sqlcipher_sqlite3DbMallocRaw(db, nRec); + if( !aRec ) goto preupdate_old_out; + rc = sqlcipher_sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); + if( rc==SQLITE_OK ){ + p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); + if( !p->pUnpacked ) rc = SQLITE_NOMEM; } + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3DbFree(db, aRec); + goto preupdate_old_out; + } + p->aRecord = aRec; } - *pnExtract = nExtract; - return rc; + pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; + if( iIdx==p->pTab->iPKey ){ + sqlcipher_sqlite3VdbeMemSetInt64(pMem, p->iKey1); + }else if( iIdx>=p->pUnpacked->nField ){ + *ppValue = (sqlcipher_sqlite3_value *)columnNullValue(); + }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ + if( pMem->flags & (MEM_Int|MEM_IntReal) ){ + testcase( pMem->flags & MEM_Int ); + testcase( pMem->flags & MEM_IntReal ); + sqlcipher_sqlite3VdbeMemRealify(pMem); + } + } + + preupdate_old_out: + sqlcipher_sqlite3Error(db, rc); + return sqlcipher_sqlite3ApiExit(db, rc); } +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* -** Attempt to extract a value from expression pExpr using the methods -** as described for sqlcipher_sqlite3Stat4ProbeSetValue() above. -** -** If successful, set *ppVal to point to a new value object and return -** SQLITE_OK. If no value can be extracted, but no other error occurs -** (e.g. OOM), return SQLITE_OK and set *ppVal to NULL. Or, if an error -** does occur, return an SQLite error code. The final value of *ppVal -** is undefined in this case. +** This function is called from within a pre-update callback to retrieve +** the number of columns in the row being updated, deleted or inserted. */ -SQLITE_PRIVATE int sqlcipher_sqlite3Stat4ValueFromExpr( - Parse *pParse, /* Parse context */ - Expr *pExpr, /* The expression to extract a value from */ - u8 affinity, /* Affinity to use */ - sqlcipher_sqlite3_value **ppVal /* OUT: New value object (or NULL) */ -){ - return stat4ValueFromExpr(pParse, pExpr, affinity, 0, ppVal); +SQLITE_API int sqlcipher_sqlite3_preupdate_count(sqlcipher_sqlite3 *db){ + PreUpdate *p = db->pPreUpdate; + return (p ? p->keyinfo.nKeyField : 0); } +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* -** Extract the iCol-th column from the nRec-byte record in pRec. Write -** the column value into *ppVal. If *ppVal is initially NULL then a new -** sqlcipher_sqlite3_value object is allocated. +** This function is designed to be called from within a pre-update callback +** only. It returns zero if the change that caused the callback was made +** immediately by a user SQL statement. Or, if the change was made by a +** trigger program, it returns the number of trigger programs currently +** on the stack (1 for a top-level trigger, 2 for a trigger fired by a +** top-level trigger etc.). ** -** If *ppVal is initially NULL then the caller is responsible for -** ensuring that the value written into *ppVal is eventually freed. +** For the purposes of the previous paragraph, a foreign key CASCADE, SET NULL +** or SET DEFAULT action is considered a trigger. */ -SQLITE_PRIVATE int sqlcipher_sqlite3Stat4Column( - sqlcipher_sqlite3 *db, /* Database handle */ - const void *pRec, /* Pointer to buffer containing record */ - int nRec, /* Size of buffer pRec in bytes */ - int iCol, /* Column to extract */ - sqlcipher_sqlite3_value **ppVal /* OUT: Extracted value */ -){ - u32 t = 0; /* a column type code */ - int nHdr; /* Size of the header in the record */ - int iHdr; /* Next unread header byte */ - int iField; /* Next unread data byte */ - int szField = 0; /* Size of the current data field */ - int i; /* Column index */ - u8 *a = (u8*)pRec; /* Typecast byte array */ - Mem *pMem = *ppVal; /* Write result into this Mem object */ +SQLITE_API int sqlcipher_sqlite3_preupdate_depth(sqlcipher_sqlite3 *db){ + PreUpdate *p = db->pPreUpdate; + return (p ? p->v->nFrame : 0); +} +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ - assert( iCol>0 ); - iHdr = getVarint32(a, nHdr); - if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; - iField = nHdr; - for(i=0; i<=iCol; i++){ - iHdr += getVarint32(&a[iHdr], t); - testcase( iHdr==nHdr ); - testcase( iHdr==nHdr+1 ); - if( iHdr>nHdr ) return SQLITE_CORRUPT_BKPT; - szField = sqlcipher_sqlite3VdbeSerialTypeLen(t); - iField += szField; - } - testcase( iField==nRec ); - testcase( iField==nRec+1 ); - if( iField>nRec ) return SQLITE_CORRUPT_BKPT; - if( pMem==0 ){ - pMem = *ppVal = sqlcipher_sqlite3ValueNew(db); - if( pMem==0 ) return SQLITE_NOMEM_BKPT; - } - sqlcipher_sqlite3VdbeSerialGet(&a[iField-szField], t, pMem); - pMem->enc = ENC(db); - return SQLITE_OK; +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +/* +** This function is designed to be called from within a pre-update callback +** only. +*/ +SQLITE_API int sqlcipher_sqlite3_preupdate_blobwrite(sqlcipher_sqlite3 *db){ + PreUpdate *p = db->pPreUpdate; + return (p ? p->iBlobWrite : -1); } +#endif +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* -** Unless it is NULL, the argument must be an UnpackedRecord object returned -** by an earlier call to sqlcipher_sqlite3Stat4ProbeSetValue(). This call deletes -** the object. +** This function is called from within a pre-update callback to retrieve +** a field of the row currently being updated or inserted. */ -SQLITE_PRIVATE void sqlcipher_sqlite3Stat4ProbeFree(UnpackedRecord *pRec){ - if( pRec ){ - int i; - int nCol = pRec->pKeyInfo->nAllField; - Mem *aMem = pRec->aMem; - sqlcipher_sqlite3 *db = aMem[0].db; - for(i=0; ipPreUpdate; + int rc = SQLITE_OK; + Mem *pMem; + + if( !p || p->op==SQLITE_DELETE ){ + rc = SQLITE_MISUSE_BKPT; + goto preupdate_new_out; + } + if( p->pPk && p->op!=SQLITE_UPDATE ){ + iIdx = sqlcipher_sqlite3TableColumnToIndex(p->pPk, iIdx); + } + if( iIdx>=p->pCsr->nField || iIdx<0 ){ + rc = SQLITE_RANGE; + goto preupdate_new_out; + } + + if( p->op==SQLITE_INSERT ){ + /* For an INSERT, memory cell p->iNewReg contains the serialized record + ** that is being inserted. Deserialize it. */ + UnpackedRecord *pUnpack = p->pNewUnpacked; + if( !pUnpack ){ + Mem *pData = &p->v->aMem[p->iNewReg]; + rc = ExpandBlob(pData); + if( rc!=SQLITE_OK ) goto preupdate_new_out; + pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z); + if( !pUnpack ){ + rc = SQLITE_NOMEM; + goto preupdate_new_out; + } + p->pNewUnpacked = pUnpack; + } + pMem = &pUnpack->aMem[iIdx]; + if( iIdx==p->pTab->iPKey ){ + sqlcipher_sqlite3VdbeMemSetInt64(pMem, p->iKey2); + }else if( iIdx>=pUnpack->nField ){ + pMem = (sqlcipher_sqlite3_value *)columnNullValue(); + } + }else{ + /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required + ** value. Make a copy of the cell contents and return a pointer to it. + ** It is not safe to return a pointer to the memory cell itself as the + ** caller may modify the value text encoding. + */ + assert( p->op==SQLITE_UPDATE ); + if( !p->aNew ){ + p->aNew = (Mem *)sqlcipher_sqlite3DbMallocZero(db, sizeof(Mem) * p->pCsr->nField); + if( !p->aNew ){ + rc = SQLITE_NOMEM; + goto preupdate_new_out; + } + } + assert( iIdx>=0 && iIdxpCsr->nField ); + pMem = &p->aNew[iIdx]; + if( pMem->flags==0 ){ + if( iIdx==p->pTab->iPKey ){ + sqlcipher_sqlite3VdbeMemSetInt64(pMem, p->iKey2); + }else{ + rc = sqlcipher_sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]); + if( rc!=SQLITE_OK ) goto preupdate_new_out; + } } - sqlcipher_sqlite3KeyInfoUnref(pRec->pKeyInfo); - sqlcipher_sqlite3DbFreeNN(db, pRec); } + *ppValue = pMem; + + preupdate_new_out: + sqlcipher_sqlite3Error(db, rc); + return sqlcipher_sqlite3ApiExit(db, rc); } -#endif /* ifdef SQLITE_ENABLE_STAT4 */ +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS /* -** Change the string value of an sqlcipher_sqlite3_value object +** Return status data for a single loop within query pStmt. */ -SQLITE_PRIVATE void sqlcipher_sqlite3ValueSetStr( - sqlcipher_sqlite3_value *v, /* Value to be set */ - int n, /* Length of string z */ - const void *z, /* Text of the new string */ - u8 enc, /* Encoding to use */ - void (*xDel)(void*) /* Destructor for the string */ +SQLITE_API int sqlcipher_sqlite3_stmt_scanstatus( + sqlcipher_sqlite3_stmt *pStmt, /* Prepared statement being queried */ + int idx, /* Index of loop to report on */ + int iScanStatusOp, /* Which metric to return */ + void *pOut /* OUT: Write the answer here */ ){ - if( v ) sqlcipher_sqlite3VdbeMemSetStr((Mem *)v, z, n, enc, xDel); + Vdbe *p = (Vdbe*)pStmt; + ScanStatus *pScan; + if( idx<0 || idx>=p->nScan ) return 1; + pScan = &p->aScan[idx]; + switch( iScanStatusOp ){ + case SQLITE_SCANSTAT_NLOOP: { + *(sqlcipher_sqlite3_int64*)pOut = p->anExec[pScan->addrLoop]; + break; + } + case SQLITE_SCANSTAT_NVISIT: { + *(sqlcipher_sqlite3_int64*)pOut = p->anExec[pScan->addrVisit]; + break; + } + case SQLITE_SCANSTAT_EST: { + double r = 1.0; + LogEst x = pScan->nEst; + while( x<100 ){ + x += 10; + r *= 0.5; + } + *(double*)pOut = r*sqlcipher_sqlite3LogEstToInt(x); + break; + } + case SQLITE_SCANSTAT_NAME: { + *(const char**)pOut = pScan->zName; + break; + } + case SQLITE_SCANSTAT_EXPLAIN: { + if( pScan->addrExplain ){ + *(const char**)pOut = p->aOp[ pScan->addrExplain ].p4.z; + }else{ + *(const char**)pOut = 0; + } + break; + } + case SQLITE_SCANSTAT_SELECTID: { + if( pScan->addrExplain ){ + *(int*)pOut = p->aOp[ pScan->addrExplain ].p1; + }else{ + *(int*)pOut = -1; + } + break; + } + default: { + return 1; + } + } + return 0; } /* -** Free an sqlcipher_sqlite3_value object +** Zero all counters associated with the sqlcipher_sqlite3_stmt_scanstatus() data. */ -SQLITE_PRIVATE void sqlcipher_sqlite3ValueFree(sqlcipher_sqlite3_value *v){ - if( !v ) return; - sqlcipher_sqlite3VdbeMemRelease((Mem *)v); - sqlcipher_sqlite3DbFreeNN(((Mem*)v)->db, v); +SQLITE_API void sqlcipher_sqlite3_stmt_scanstatus_reset(sqlcipher_sqlite3_stmt *pStmt){ + Vdbe *p = (Vdbe*)pStmt; + memset(p->anExec, 0, p->nOp * sizeof(i64)); } +#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */ +/************** End of vdbeapi.c *********************************************/ +/************** Begin file vdbetrace.c ***************************************/ /* -** The sqlcipher_sqlite3ValueBytes() routine returns the number of bytes in the -** sqlcipher_sqlite3_value object assuming that it uses the encoding "enc". -** The valueBytes() routine is a helper function. +** 2009 November 25 +** +** 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 used to insert the values of host parameters +** (aka "wildcards") into the SQL text output by sqlcipher_sqlite3_trace(). +** +** The Vdbe parse-tree explainer is also found here. */ -static SQLITE_NOINLINE int valueBytes(sqlcipher_sqlite3_value *pVal, u8 enc){ - return valueToText(pVal, enc)!=0 ? pVal->n : 0; -} -SQLITE_PRIVATE int sqlcipher_sqlite3ValueBytes(sqlcipher_sqlite3_value *pVal, u8 enc){ - Mem *p = (Mem*)pVal; - assert( (p->flags & MEM_Null)==0 || (p->flags & (MEM_Str|MEM_Blob))==0 ); - if( (p->flags & MEM_Str)!=0 && pVal->enc==enc ){ - return p->n; +/* #include "sqliteInt.h" */ +/* #include "vdbeInt.h" */ + +#ifndef SQLITE_OMIT_TRACE + +/* +** zSql is a zero-terminated string of UTF-8 SQL text. Return the number of +** bytes in this text up to but excluding the first character in +** a host parameter. If the text contains no host parameters, return +** the total number of bytes in the text. +*/ +static int findNextHostParameter(const char *zSql, int *pnToken){ + int tokenType; + int nTotal = 0; + int n; + + *pnToken = 0; + while( zSql[0] ){ + n = sqlcipher_sqlite3GetToken((u8*)zSql, &tokenType); + assert( n>0 && tokenType!=TK_ILLEGAL ); + if( tokenType==TK_VARIABLE ){ + *pnToken = n; + break; + } + nTotal += n; + zSql += n; } - if( (p->flags & MEM_Blob)!=0 ){ - if( p->flags & MEM_Zero ){ - return p->n + p->u.nZero; - }else{ - return p->n; + return nTotal; +} + +/* +** This function returns a pointer to a nul-terminated string in memory +** obtained from sqlcipher_sqlite3DbMalloc(). If sqlcipher_sqlite3.nVdbeExec is 1, then the +** string contains a copy of zRawSql but with host parameters expanded to +** their current bindings. Or, if sqlcipher_sqlite3.nVdbeExec is greater than 1, +** then the returned string holds a copy of zRawSql with "-- " prepended +** to each line of text. +** +** If the SQLITE_TRACE_SIZE_LIMIT macro is defined to an integer, then +** then long strings and blobs are truncated to that many bytes. This +** can be used to prevent unreasonably large trace strings when dealing +** with large (multi-megabyte) strings and blobs. +** +** The calling function is responsible for making sure the memory returned +** is eventually freed. +** +** ALGORITHM: Scan the input string looking for host parameters in any of +** these forms: ?, ?N, $A, @A, :A. Take care to avoid text within +** string literals, quoted identifier names, and comments. For text forms, +** the host parameter index is found by scanning the prepared +** statement for the corresponding OP_Variable opcode. Once the host +** parameter index is known, locate the value in p->aVar[]. Then render +** the value as a literal in place of the host parameter name. +*/ +SQLITE_PRIVATE char *sqlcipher_sqlite3VdbeExpandSql( + Vdbe *p, /* The prepared statement being evaluated */ + const char *zRawSql /* Raw text of the SQL statement */ +){ + sqlcipher_sqlite3 *db; /* The database connection */ + int idx = 0; /* Index of a host parameter */ + int nextIndex = 1; /* Index of next ? host parameter */ + int n; /* Length of a token prefix */ + int nToken; /* Length of the parameter token */ + int i; /* Loop counter */ + Mem *pVar; /* Value of a host parameter */ + StrAccum out; /* Accumulate the output here */ +#ifndef SQLITE_OMIT_UTF16 + Mem utf8; /* Used to convert UTF16 into UTF8 for display */ +#endif + + db = p->db; + sqlcipher_sqlite3StrAccumInit(&out, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); + if( db->nVdbeExec>1 ){ + while( *zRawSql ){ + const char *zStart = zRawSql; + while( *(zRawSql++)!='\n' && *zRawSql ); + sqlcipher_sqlite3_str_append(&out, "-- ", 3); + assert( (zRawSql - zStart) > 0 ); + sqlcipher_sqlite3_str_append(&out, zStart, (int)(zRawSql-zStart)); + } + }else if( p->nVar==0 ){ + sqlcipher_sqlite3_str_append(&out, zRawSql, sqlcipher_sqlite3Strlen30(zRawSql)); + }else{ + while( zRawSql[0] ){ + n = findNextHostParameter(zRawSql, &nToken); + assert( n>0 ); + sqlcipher_sqlite3_str_append(&out, zRawSql, n); + zRawSql += n; + assert( zRawSql[0] || nToken==0 ); + if( nToken==0 ) break; + if( zRawSql[0]=='?' ){ + if( nToken>1 ){ + assert( sqlcipher_sqlite3Isdigit(zRawSql[1]) ); + sqlcipher_sqlite3GetInt32(&zRawSql[1], &idx); + }else{ + idx = nextIndex; + } + }else{ + assert( zRawSql[0]==':' || zRawSql[0]=='$' || + zRawSql[0]=='@' || zRawSql[0]=='#' ); + testcase( zRawSql[0]==':' ); + testcase( zRawSql[0]=='$' ); + testcase( zRawSql[0]=='@' ); + testcase( zRawSql[0]=='#' ); + idx = sqlcipher_sqlite3VdbeParameterIndex(p, zRawSql, nToken); + assert( idx>0 ); + } + zRawSql += nToken; + nextIndex = MAX(idx + 1, nextIndex); + assert( idx>0 && idx<=p->nVar ); + pVar = &p->aVar[idx-1]; + if( pVar->flags & MEM_Null ){ + sqlcipher_sqlite3_str_append(&out, "NULL", 4); + }else if( pVar->flags & (MEM_Int|MEM_IntReal) ){ + sqlcipher_sqlite3_str_appendf(&out, "%lld", pVar->u.i); + }else if( pVar->flags & MEM_Real ){ + sqlcipher_sqlite3_str_appendf(&out, "%!.15g", pVar->u.r); + }else if( pVar->flags & MEM_Str ){ + int nOut; /* Number of bytes of the string text to include in output */ +#ifndef SQLITE_OMIT_UTF16 + u8 enc = ENC(db); + if( enc!=SQLITE_UTF8 ){ + memset(&utf8, 0, sizeof(utf8)); + utf8.db = db; + sqlcipher_sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC); + if( SQLITE_NOMEM==sqlcipher_sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8) ){ + out.accError = SQLITE_NOMEM; + out.nAlloc = 0; + } + pVar = &utf8; + } +#endif + nOut = pVar->n; +#ifdef SQLITE_TRACE_SIZE_LIMIT + if( nOut>SQLITE_TRACE_SIZE_LIMIT ){ + nOut = SQLITE_TRACE_SIZE_LIMIT; + while( nOutn && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; } + } +#endif + sqlcipher_sqlite3_str_appendf(&out, "'%.*q'", nOut, pVar->z); +#ifdef SQLITE_TRACE_SIZE_LIMIT + if( nOutn ){ + sqlcipher_sqlite3_str_appendf(&out, "/*+%d bytes*/", pVar->n-nOut); + } +#endif +#ifndef SQLITE_OMIT_UTF16 + if( enc!=SQLITE_UTF8 ) sqlcipher_sqlite3VdbeMemRelease(&utf8); +#endif + }else if( pVar->flags & MEM_Zero ){ + sqlcipher_sqlite3_str_appendf(&out, "zeroblob(%d)", pVar->u.nZero); + }else{ + int nOut; /* Number of bytes of the blob to include in output */ + assert( pVar->flags & MEM_Blob ); + sqlcipher_sqlite3_str_append(&out, "x'", 2); + nOut = pVar->n; +#ifdef SQLITE_TRACE_SIZE_LIMIT + if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT; +#endif + for(i=0; iz[i]&0xff); + } + sqlcipher_sqlite3_str_append(&out, "'", 1); +#ifdef SQLITE_TRACE_SIZE_LIMIT + if( nOutn ){ + sqlcipher_sqlite3_str_appendf(&out, "/*+%d bytes*/", pVar->n-nOut); + } +#endif + } } } - if( p->flags & MEM_Null ) return 0; - return valueBytes(pVal, enc); + if( out.accError ) sqlcipher_sqlite3_str_reset(&out); + return sqlcipher_sqlite3StrAccumFinish(&out); } -/************** End of vdbemem.c *********************************************/ -/************** Begin file vdbeaux.c *****************************************/ +#endif /* #ifndef SQLITE_OMIT_TRACE */ + +/************** End of vdbetrace.c *******************************************/ +/************** Begin file vdbe.c ********************************************/ /* -** 2003 September 6 +** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -82707,8108 +88739,8732 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ValueBytes(sqlcipher_sqlite3_value *pVal, u8 ** May you share freely, never taking more than you give. ** ************************************************************************* -** This file contains code used for creating, destroying, and populating -** a VDBE (or an "sqlcipher_sqlite3_stmt" as it is known to the outside world.) +** The code in this file implements the function that runs the +** bytecode of a prepared statement. +** +** Various scripts scan this source file in order to generate HTML +** documentation, headers files, or other derived files. The formatting +** of the code in this file is, therefore, important. See other comments +** in this file for details. If in doubt, do not deviate from existing +** commenting and indentation practices when changing or adding code. */ /* #include "sqliteInt.h" */ /* #include "vdbeInt.h" */ -/* Forward references */ -static void freeEphemeralFunction(sqlcipher_sqlite3 *db, FuncDef *pDef); -static void vdbeFreeOpArray(sqlcipher_sqlite3 *, Op *, int); - /* -** Create a new virtual database engine. +** Invoke this macro on memory cells just prior to changing the +** value of the cell. This macro verifies that shallow copies are +** not misused. A shallow copy of a string or blob just copies a +** pointer to the string or blob, not the content. If the original +** is changed while the copy is still in use, the string or blob might +** be changed out from under the copy. This macro verifies that nothing +** like that ever happens. */ -SQLITE_PRIVATE Vdbe *sqlcipher_sqlite3VdbeCreate(Parse *pParse){ - sqlcipher_sqlite3 *db = pParse->db; - Vdbe *p; - p = sqlcipher_sqlite3DbMallocRawNN(db, sizeof(Vdbe) ); - if( p==0 ) return 0; - memset(&p->aOp, 0, sizeof(Vdbe)-offsetof(Vdbe,aOp)); - p->db = db; - if( db->pVdbe ){ - db->pVdbe->pPrev = p; - } - p->pNext = db->pVdbe; - p->pPrev = 0; - db->pVdbe = p; - p->magic = VDBE_MAGIC_INIT; - p->pParse = pParse; - pParse->pVdbe = p; - assert( pParse->aLabel==0 ); - assert( pParse->nLabel==0 ); - assert( p->nOpAlloc==0 ); - assert( pParse->szOpAlloc==0 ); - sqlcipher_sqlite3VdbeAddOp2(p, OP_Init, 0, 1); - return p; -} +#ifdef SQLITE_DEBUG +# define memAboutToChange(P,M) sqlcipher_sqlite3VdbeMemAboutToChange(P,M) +#else +# define memAboutToChange(P,M) +#endif /* -** Return the Parse object that owns a Vdbe object. +** The following global variable is incremented every time a cursor +** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test +** procedures use this information to make sure that indices are +** working correctly. This variable has no function other than to +** help verify the correct operation of the library. */ -SQLITE_PRIVATE Parse *sqlcipher_sqlite3VdbeParser(Vdbe *p){ - return p->pParse; -} +#ifdef SQLITE_TEST +SQLITE_API int sqlcipher_sqlite3_search_count = 0; +#endif /* -** Change the error string stored in Vdbe.zErrMsg +** When this global variable is positive, it gets decremented once before +** each instruction in the VDBE. When it reaches zero, the u1.isInterrupted +** field of the sqlcipher_sqlite3 structure is set in order to simulate an interrupt. +** +** This facility is used for testing purposes only. It does not function +** in an ordinary build. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeError(Vdbe *p, const char *zFormat, ...){ - va_list ap; - sqlcipher_sqlite3DbFree(p->db, p->zErrMsg); - va_start(ap, zFormat); - p->zErrMsg = sqlcipher_sqlite3VMPrintf(p->db, zFormat, ap); - va_end(ap); -} +#ifdef SQLITE_TEST +SQLITE_API int sqlcipher_sqlite3_interrupt_count = 0; +#endif /* -** Remember the SQL string for a prepared statement. +** The next global variable is incremented each type the OP_Sort opcode +** is executed. The test procedures use this information to make sure that +** sorting is occurring or not occurring at appropriate times. This variable +** has no function other than to help verify the correct operation of the +** library. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, u8 prepFlags){ - if( p==0 ) return; - p->prepFlags = prepFlags; - if( (prepFlags & SQLITE_PREPARE_SAVESQL)==0 ){ - p->expmask = 0; - } - assert( p->zSql==0 ); - p->zSql = sqlcipher_sqlite3DbStrNDup(p->db, z, n); -} +#ifdef SQLITE_TEST +SQLITE_API int sqlcipher_sqlite3_sort_count = 0; +#endif -#ifdef SQLITE_ENABLE_NORMALIZE /* -** Add a new element to the Vdbe->pDblStr list. +** The next global variable records the size of the largest MEM_Blob +** or MEM_Str that has been used by a VDBE opcode. The test procedures +** use this information to make sure that the zero-blob functionality +** is working correctly. This variable has no function other than to +** help verify the correct operation of the library. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeAddDblquoteStr(sqlcipher_sqlite3 *db, Vdbe *p, const char *z){ - if( p ){ - int n = sqlcipher_sqlite3Strlen30(z); - DblquoteStr *pStr = sqlcipher_sqlite3DbMallocRawNN(db, - sizeof(*pStr)+n+1-sizeof(pStr->z)); - if( pStr ){ - pStr->pNextStr = p->pDblStr; - p->pDblStr = pStr; - memcpy(pStr->z, z, n+1); - } +#ifdef SQLITE_TEST +SQLITE_API int sqlcipher_sqlite3_max_blobsize = 0; +static void updateMaxBlobsize(Mem *p){ + if( (p->flags & (MEM_Str|MEM_Blob))!=0 && p->n>sqlcipher_sqlite3_max_blobsize ){ + sqlcipher_sqlite3_max_blobsize = p->n; } } #endif -#ifdef SQLITE_ENABLE_NORMALIZE /* -** zId of length nId is a double-quoted identifier. Check to see if -** that identifier is really used as a string literal. +** This macro evaluates to true if either the update hook or the preupdate +** hook are enabled for database connect DB. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeUsesDoubleQuotedString( - Vdbe *pVdbe, /* The prepared statement */ - const char *zId /* The double-quoted identifier, already dequoted */ -){ - DblquoteStr *pStr; - assert( zId!=0 ); - if( pVdbe->pDblStr==0 ) return 0; - for(pStr=pVdbe->pDblStr; pStr; pStr=pStr->pNextStr){ - if( strcmp(zId, pStr->z)==0 ) return 1; - } - return 0; -} +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +# define HAS_UPDATE_HOOK(DB) ((DB)->xPreUpdateCallback||(DB)->xUpdateCallback) +#else +# define HAS_UPDATE_HOOK(DB) ((DB)->xUpdateCallback) #endif /* -** Swap all content between two VDBE structures. +** The next global variable is incremented each time the OP_Found opcode +** is executed. This is used to test whether or not the foreign key +** operation implemented using OP_FkIsZero is working. This variable +** has no function other than to help verify the correct operation of the +** library. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ - Vdbe tmp, *pTmp; - char *zTmp; - assert( pA->db==pB->db ); - tmp = *pA; - *pA = *pB; - *pB = tmp; - pTmp = pA->pNext; - pA->pNext = pB->pNext; - pB->pNext = pTmp; - pTmp = pA->pPrev; - pA->pPrev = pB->pPrev; - pB->pPrev = pTmp; - zTmp = pA->zSql; - pA->zSql = pB->zSql; - pB->zSql = zTmp; -#ifdef SQLITE_ENABLE_NORMALIZE - zTmp = pA->zNormSql; - pA->zNormSql = pB->zNormSql; - pB->zNormSql = zTmp; +#ifdef SQLITE_TEST +SQLITE_API int sqlcipher_sqlite3_found_count = 0; #endif - pB->expmask = pA->expmask; - pB->prepFlags = pA->prepFlags; - memcpy(pB->aCounter, pA->aCounter, sizeof(pB->aCounter)); - pB->aCounter[SQLITE_STMTSTATUS_REPREPARE]++; -} /* -** Resize the Vdbe.aOp array so that it is at least nOp elements larger -** than its current size. nOp is guaranteed to be less than or equal -** to 1024/sizeof(Op). -** -** If an out-of-memory error occurs while resizing the array, return -** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain -** unchanged (this is so that any opcodes already allocated can be -** correctly deallocated along with the rest of the Vdbe). +** Test a register to see if it exceeds the current maximum blob size. +** If it does, record the new maximum blob size. */ -static int growOpArray(Vdbe *v, int nOp){ - VdbeOp *pNew; - Parse *p = v->pParse; - - /* The SQLITE_TEST_REALLOC_STRESS compile-time option is designed to force - ** more frequent reallocs and hence provide more opportunities for - ** simulated OOM faults. SQLITE_TEST_REALLOC_STRESS is generally used - ** during testing only. With SQLITE_TEST_REALLOC_STRESS grow the op array - ** by the minimum* amount required until the size reaches 512. Normal - ** operation (without SQLITE_TEST_REALLOC_STRESS) is to double the current - ** size of the op array or add 1KB of space, whichever is smaller. */ -#ifdef SQLITE_TEST_REALLOC_STRESS - sqlcipher_sqlite3_int64 nNew = (v->nOpAlloc>=512 ? 2*(sqlcipher_sqlite3_int64)v->nOpAlloc - : (sqlcipher_sqlite3_int64)v->nOpAlloc+nOp); +#if defined(SQLITE_TEST) && !defined(SQLITE_UNTESTABLE) +# define UPDATE_MAX_BLOBSIZE(P) updateMaxBlobsize(P) #else - sqlcipher_sqlite3_int64 nNew = (v->nOpAlloc ? 2*(sqlcipher_sqlite3_int64)v->nOpAlloc - : (sqlcipher_sqlite3_int64)(1024/sizeof(Op))); - UNUSED_PARAMETER(nOp); +# define UPDATE_MAX_BLOBSIZE(P) #endif - /* Ensure that the size of a VDBE does not grow too large */ - if( nNew > p->db->aLimit[SQLITE_LIMIT_VDBE_OP] ){ - sqlcipher_sqlite3OomFault(p->db); - return SQLITE_NOMEM; - } - - assert( nOp<=(1024/sizeof(Op)) ); - assert( nNew>=(v->nOpAlloc+nOp) ); - pNew = sqlcipher_sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op)); - if( pNew ){ - p->szOpAlloc = sqlcipher_sqlite3DbMallocSize(p->db, pNew); - v->nOpAlloc = p->szOpAlloc/sizeof(Op); - v->aOp = pNew; - } - return (pNew ? SQLITE_OK : SQLITE_NOMEM_BKPT); -} - #ifdef SQLITE_DEBUG -/* This routine is just a convenient place to set a breakpoint that will -** fire after each opcode is inserted and displayed using -** "PRAGMA vdbe_addoptrace=on". Parameters "pc" (program counter) and -** pOp are available to make the breakpoint conditional. +/* This routine provides a convenient place to set a breakpoint during +** tracing with PRAGMA vdbe_trace=on. The breakpoint fires right after +** each opcode is printed. Variables "pc" (program counter) and pOp are +** available to add conditionals to the breakpoint. GDB example: +** +** break test_trace_breakpoint if pc=22 ** ** Other useful labels for breakpoints include: -** test_trace_breakpoint(pc,pOp) +** test_addop_breakpoint(pc,pOp) ** sqlcipher_sqlite3CorruptError(lineno) ** sqlcipher_sqlite3MisuseError(lineno) ** sqlcipher_sqlite3CantopenError(lineno) */ -static void test_addop_breakpoint(int pc, Op *pOp){ +static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){ static int n = 0; n++; } #endif /* -** Add a new instruction to the list of instructions current in the -** VDBE. Return the address of the new instruction. +** Invoke the VDBE coverage callback, if that callback is defined. This +** feature is used for test suite validation only and does not appear an +** production builds. ** -** Parameters: +** M is the type of branch. I is the direction taken for this instance of +** the branch. ** -** p Pointer to the VDBE +** M: 2 - two-way branch (I=0: fall-thru 1: jump ) +** 3 - two-way + NULL (I=0: fall-thru 1: jump 2: NULL ) +** 4 - OP_Jump (I=0: jump p1 1: jump p2 2: jump p3) ** -** op The opcode for this instruction +** In other words, if M is 2, then I is either 0 (for fall-through) or +** 1 (for when the branch is taken). If M is 3, the I is 0 for an +** ordinary fall-through, I is 1 if the branch was taken, and I is 2 +** if the result of comparison is NULL. For M=3, I=2 the jump may or +** may not be taken, depending on the SQLITE_JUMPIFNULL flags in p5. +** When M is 4, that means that an OP_Jump is being run. I is 0, 1, or 2 +** depending on if the operands are less than, equal, or greater than. ** -** p1, p2, p3 Operands +** iSrcLine is the source code line (from the __LINE__ macro) that +** generated the VDBE instruction combined with flag bits. The source +** code line number is in the lower 24 bits of iSrcLine and the upper +** 8 bytes are flags. The lower three bits of the flags indicate +** values for I that should never occur. For example, if the branch is +** always taken, the flags should be 0x05 since the fall-through and +** alternate branch are never taken. If a branch is never taken then +** flags should be 0x06 since only the fall-through approach is allowed. ** -** Use the sqlcipher_sqlite3VdbeResolveLabel() function to fix an address and -** the sqlcipher_sqlite3VdbeChangeP4() function to change the value of the P4 -** operand. +** Bit 0x08 of the flags indicates an OP_Jump opcode that is only +** interested in equal or not-equal. In other words, I==0 and I==2 +** should be treated as equivalent +** +** Since only a line number is retained, not the filename, this macro +** only works for amalgamation builds. But that is ok, since these macros +** should be no-ops except for special builds used to measure test coverage. */ -static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){ - assert( p->nOpAlloc<=p->nOp ); - if( growOpArray(p, 1) ) return 1; - assert( p->nOpAlloc>p->nOp ); - return sqlcipher_sqlite3VdbeAddOp3(p, op, p1, p2, p3); -} -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ - int i; - VdbeOp *pOp; +#if !defined(SQLITE_VDBE_COVERAGE) +# define VdbeBranchTaken(I,M) +#else +# define VdbeBranchTaken(I,M) vdbeTakeBranch(pOp->iSrcLine,I,M) + static void vdbeTakeBranch(u32 iSrcLine, u8 I, u8 M){ + u8 mNever; + assert( I<=2 ); /* 0: fall through, 1: taken, 2: alternate taken */ + assert( M<=4 ); /* 2: two-way branch, 3: three-way branch, 4: OP_Jump */ + assert( I> 24; + assert( (I & mNever)==0 ); + if( sqlcipher_sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/ + /* Invoke the branch coverage callback with three arguments: + ** iSrcLine - the line number of the VdbeCoverage() macro, with + ** flags removed. + ** I - Mask of bits 0x07 indicating which cases are are + ** fulfilled by this instance of the jump. 0x01 means + ** fall-thru, 0x02 means taken, 0x04 means NULL. Any + ** impossible cases (ex: if the comparison is never NULL) + ** are filled in automatically so that the coverage + ** measurement logic does not flag those impossible cases + ** as missed coverage. + ** M - Type of jump. Same as M argument above + */ + I |= mNever; + if( M==2 ) I |= 0x04; + if( M==4 ){ + I |= 0x08; + if( (mNever&0x08)!=0 && (I&0x05)!=0) I |= 0x05; /*NO_TEST*/ + } + sqlcipher_sqlite3GlobalConfig.xVdbeBranch(sqlcipher_sqlite3GlobalConfig.pVdbeBranchArg, + iSrcLine&0xffffff, I, M); + } +#endif + +/* +** An ephemeral string value (signified by the MEM_Ephem flag) contains +** a pointer to a dynamically allocated string where some other entity +** is responsible for deallocating that string. Because the register +** does not control the string, it might be deleted without the register +** knowing it. +** +** This routine converts an ephemeral string into a dynamically allocated +** string that the register itself controls. In other words, it +** converts an MEM_Ephem string into a string with P.z==P.zMalloc. +*/ +#define Deephemeralize(P) \ + if( ((P)->flags&MEM_Ephem)!=0 \ + && sqlcipher_sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;} + +/* Return true if the cursor was opened using the OP_OpenSorter opcode. */ +#define isSorter(x) ((x)->eCurType==CURTYPE_SORTER) + +/* +** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL +** if we run out of memory. +*/ +static VdbeCursor *allocateCursor( + Vdbe *p, /* The virtual machine */ + int iCur, /* Index of the new VdbeCursor */ + int nField, /* Number of fields in the table or index */ + u8 eCurType /* Type of the new cursor */ +){ + /* Find the memory cell that will be used to store the blob of memory + ** required for this VdbeCursor structure. It is convenient to use a + ** vdbe memory cell to manage the memory allocation required for a + ** VdbeCursor structure for the following reasons: + ** + ** * Sometimes cursor numbers are used for a couple of different + ** purposes in a vdbe program. The different uses might require + ** different sized allocations. Memory cells provide growable + ** allocations. + ** + ** * When using ENABLE_MEMORY_MANAGEMENT, memory cell buffers can + ** be freed lazily via the sqlcipher_sqlite3_release_memory() API. This + ** minimizes the number of malloc calls made by the system. + ** + ** The memory cell for cursor 0 is aMem[0]. The rest are allocated from + ** the top of the register space. Cursor 1 is at Mem[p->nMem-1]. + ** Cursor 2 is at Mem[p->nMem-2]. And so forth. + */ + Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem; - i = p->nOp; - assert( p->magic==VDBE_MAGIC_INIT ); - assert( op>=0 && op<0xff ); - if( p->nOpAlloc<=i ){ - return growOp3(p, op, p1, p2, p3); + int nByte; + VdbeCursor *pCx = 0; + nByte = + ROUND8P(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + + (eCurType==CURTYPE_BTREE?sqlcipher_sqlite3BtreeCursorSize():0); + + assert( iCur>=0 && iCurnCursor ); + if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ + sqlcipher_sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]); + p->apCsr[iCur] = 0; } - p->nOp++; - pOp = &p->aOp[i]; - pOp->opcode = (u8)op; - pOp->p5 = 0; - pOp->p1 = p1; - pOp->p2 = p2; - pOp->p3 = p3; - pOp->p4.p = 0; - pOp->p4type = P4_NOTUSED; -#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - pOp->zComment = 0; -#endif -#ifdef SQLITE_DEBUG - if( p->db->flags & SQLITE_VdbeAddopTrace ){ - sqlcipher_sqlite3VdbePrintOp(0, i, &p->aOp[i]); - test_addop_breakpoint(i, &p->aOp[i]); + + /* There used to be a call to sqlcipher_sqlite3VdbeMemClearAndResize() to make sure + ** the pMem used to hold space for the cursor has enough storage available + ** in pMem->zMalloc. But for the special case of the aMem[] entries used + ** to hold cursors, it is faster to in-line the logic. */ + assert( pMem->flags==MEM_Undefined ); + assert( (pMem->flags & MEM_Dyn)==0 ); + assert( pMem->szMalloc==0 || pMem->z==pMem->zMalloc ); + if( pMem->szMallocszMalloc>0 ){ + sqlcipher_sqlite3DbFreeNN(pMem->db, pMem->zMalloc); + } + pMem->z = pMem->zMalloc = sqlcipher_sqlite3DbMallocRaw(pMem->db, nByte); + if( pMem->zMalloc==0 ){ + pMem->szMalloc = 0; + return 0; + } + pMem->szMalloc = nByte; } -#endif -#ifdef VDBE_PROFILE - pOp->cycles = 0; - pOp->cnt = 0; -#endif -#ifdef SQLITE_VDBE_COVERAGE - pOp->iSrcLine = 0; -#endif - return i; -} -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp0(Vdbe *p, int op){ - return sqlcipher_sqlite3VdbeAddOp3(p, op, 0, 0, 0); -} -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){ - return sqlcipher_sqlite3VdbeAddOp3(p, op, p1, 0, 0); -} -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){ - return sqlcipher_sqlite3VdbeAddOp3(p, op, p1, p2, 0); -} -/* Generate code for an unconditional jump to instruction iDest -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeGoto(Vdbe *p, int iDest){ - return sqlcipher_sqlite3VdbeAddOp3(p, OP_Goto, 0, iDest, 0); + p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->zMalloc; + memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); + pCx->eCurType = eCurType; + pCx->nField = nField; + pCx->aOffset = &pCx->aType[nField]; + if( eCurType==CURTYPE_BTREE ){ + pCx->uc.pCursor = (BtCursor*) + &pMem->z[ROUND8P(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; + sqlcipher_sqlite3BtreeCursorZero(pCx->uc.pCursor); + } + return pCx; } -/* Generate code to cause the string zStr to be loaded into -** register iDest +/* +** The string in pRec is known to look like an integer and to have a +** floating point value of rValue. Return true and set *piValue to the +** integer value if the string is in range to be an integer. Otherwise, +** return false. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeLoadString(Vdbe *p, int iDest, const char *zStr){ - return sqlcipher_sqlite3VdbeAddOp4(p, OP_String8, 0, iDest, 0, zStr, 0); +static int alsoAnInt(Mem *pRec, double rValue, i64 *piValue){ + i64 iValue = (double)rValue; + if( sqlcipher_sqlite3RealSameAsInt(rValue,iValue) ){ + *piValue = iValue; + return 1; + } + return 0==sqlcipher_sqlite3Atoi64(pRec->z, piValue, pRec->n, pRec->enc); } /* -** Generate code that initializes multiple registers to string or integer -** constants. The registers begin with iDest and increase consecutively. -** One register is initialized for each characgter in zTypes[]. For each -** "s" character in zTypes[], the register is a string if the argument is -** not NULL, or OP_Null if the value is a null pointer. For each "i" character -** in zTypes[], the register is initialized to an integer. +** Try to convert a value into a numeric representation if we can +** do so without loss of information. In other words, if the string +** looks like a number, convert it into a number. If it does not +** look like a number, leave it alone. ** -** If the input string does not end with "X" then an OP_ResultRow instruction -** is generated for the values inserted. +** If the bTryForInt flag is true, then extra effort is made to give +** an integer representation. Strings that look like floating point +** values but which have no fractional component (example: '48.00') +** will have a MEM_Int representation when bTryForInt is true. +** +** If bTryForInt is false, then if the input string contains a decimal +** point or exponential notation, the result is only MEM_Real, even +** if there is an exact integer representation of the quantity. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMultiLoad(Vdbe *p, int iDest, const char *zTypes, ...){ - va_list ap; - int i; - char c; - va_start(ap, zTypes); - for(i=0; (c = zTypes[i])!=0; i++){ - if( c=='s' ){ - const char *z = va_arg(ap, const char*); - sqlcipher_sqlite3VdbeAddOp4(p, z==0 ? OP_Null : OP_String8, 0, iDest+i, 0, z, 0); - }else if( c=='i' ){ - sqlcipher_sqlite3VdbeAddOp2(p, OP_Integer, va_arg(ap, int), iDest+i); - }else{ - goto skip_op_resultrow; - } +static void applyNumericAffinity(Mem *pRec, int bTryForInt){ + double rValue; + u8 enc = pRec->enc; + int rc; + assert( (pRec->flags & (MEM_Str|MEM_Int|MEM_Real|MEM_IntReal))==MEM_Str ); + rc = sqlcipher_sqlite3AtoF(pRec->z, &rValue, pRec->n, enc); + if( rc<=0 ) return; + if( rc==1 && alsoAnInt(pRec, rValue, &pRec->u.i) ){ + pRec->flags |= MEM_Int; + }else{ + pRec->u.r = rValue; + pRec->flags |= MEM_Real; + if( bTryForInt ) sqlcipher_sqlite3VdbeIntegerAffinity(pRec); } - sqlcipher_sqlite3VdbeAddOp2(p, OP_ResultRow, iDest, i); -skip_op_resultrow: - va_end(ap); + /* TEXT->NUMERIC is many->one. Hence, it is important to invalidate the + ** string representation after computing a numeric equivalent, because the + ** string representation might not be the canonical representation for the + ** numeric value. Ticket [343634942dd54ab57b7024] 2018-01-31. */ + pRec->flags &= ~MEM_Str; } /* -** Add an opcode that includes the p4 value as a pointer. +** Processing is determine by the affinity parameter: +** +** SQLITE_AFF_INTEGER: +** SQLITE_AFF_REAL: +** SQLITE_AFF_NUMERIC: +** Try to convert pRec to an integer representation or a +** floating-point representation if an integer representation +** is not possible. Note that the integer representation is +** always preferred, even if the affinity is REAL, because +** an integer representation is more space efficient on disk. +** +** SQLITE_AFF_TEXT: +** Convert pRec to a text representation. +** +** SQLITE_AFF_BLOB: +** SQLITE_AFF_NONE: +** No-op. pRec is unchanged. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp4( - Vdbe *p, /* Add the opcode to this VM */ - int op, /* The new opcode */ - int p1, /* The P1 operand */ - int p2, /* The P2 operand */ - int p3, /* The P3 operand */ - const char *zP4, /* The P4 operand */ - int p4type /* P4 operand type */ +static void applyAffinity( + Mem *pRec, /* The value to apply affinity to */ + char affinity, /* The affinity to be applied */ + u8 enc /* Use this text encoding */ ){ - int addr = sqlcipher_sqlite3VdbeAddOp3(p, op, p1, p2, p3); - sqlcipher_sqlite3VdbeChangeP4(p, addr, zP4, p4type); - return addr; + if( affinity>=SQLITE_AFF_NUMERIC ){ + assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL + || affinity==SQLITE_AFF_NUMERIC ); + if( (pRec->flags & MEM_Int)==0 ){ /*OPTIMIZATION-IF-FALSE*/ + if( (pRec->flags & MEM_Real)==0 ){ + if( pRec->flags & MEM_Str ) applyNumericAffinity(pRec,1); + }else{ + sqlcipher_sqlite3VdbeIntegerAffinity(pRec); + } + } + }else if( affinity==SQLITE_AFF_TEXT ){ + /* Only attempt the conversion to TEXT if there is an integer or real + ** representation (blob and NULL do not get converted) but no string + ** representation. It would be harmless to repeat the conversion if + ** there is already a string rep, but it is pointless to waste those + ** CPU cycles. */ + if( 0==(pRec->flags&MEM_Str) ){ /*OPTIMIZATION-IF-FALSE*/ + if( (pRec->flags&(MEM_Real|MEM_Int|MEM_IntReal)) ){ + testcase( pRec->flags & MEM_Int ); + testcase( pRec->flags & MEM_Real ); + testcase( pRec->flags & MEM_IntReal ); + sqlcipher_sqlite3VdbeMemStringify(pRec, enc, 1); + } + } + pRec->flags &= ~(MEM_Real|MEM_Int|MEM_IntReal); + } } /* -** Add an OP_Function or OP_PureFunc opcode. -** -** The eCallCtx argument is information (typically taken from Expr.op2) -** that describes the calling context of the function. 0 means a general -** function call. NC_IsCheck means called by a check constraint, -** NC_IdxExpr means called as part of an index expression. NC_PartIdx -** means in the WHERE clause of a partial index. NC_GenCol means called -** while computing a generated column value. 0 is the usual case. +** Try to convert the type of a function argument or a result column +** into a numeric representation. Use either INTEGER or REAL whichever +** is appropriate. But only do the conversion if it is possible without +** loss of information and return the revised type of the argument. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddFunctionCall( - Parse *pParse, /* Parsing context */ - int p1, /* Constant argument mask */ - int p2, /* First argument register */ - int p3, /* Register into which results are written */ - int nArg, /* Number of argument */ - const FuncDef *pFunc, /* The function to be invoked */ - int eCallCtx /* Calling context */ -){ - Vdbe *v = pParse->pVdbe; - int nByte; - int addr; - sqlcipher_sqlite3_context *pCtx; - assert( v ); - nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlcipher_sqlite3_value*); - pCtx = sqlcipher_sqlite3DbMallocRawNN(pParse->db, nByte); - if( pCtx==0 ){ - assert( pParse->db->mallocFailed ); - freeEphemeralFunction(pParse->db, (FuncDef*)pFunc); - return 0; +SQLITE_API int sqlcipher_sqlite3_value_numeric_type(sqlcipher_sqlite3_value *pVal){ + int eType = sqlcipher_sqlite3_value_type(pVal); + if( eType==SQLITE_TEXT ){ + Mem *pMem = (Mem*)pVal; + applyNumericAffinity(pMem, 0); + eType = sqlcipher_sqlite3_value_type(pVal); } - pCtx->pOut = 0; - pCtx->pFunc = (FuncDef*)pFunc; - pCtx->pVdbe = 0; - pCtx->isError = 0; - pCtx->argc = nArg; - pCtx->iOp = sqlcipher_sqlite3VdbeCurrentAddr(v); - addr = sqlcipher_sqlite3VdbeAddOp4(v, eCallCtx ? OP_PureFunc : OP_Function, - p1, p2, p3, (char*)pCtx, P4_FUNCCTX); - sqlcipher_sqlite3VdbeChangeP5(v, eCallCtx & NC_SelfRef); - return addr; + return eType; } /* -** Add an opcode that includes the p4 value with a P4_INT64 or -** P4_REAL type. +** Exported version of applyAffinity(). This one works on sqlcipher_sqlite3_value*, +** not the internal Mem* type. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp4Dup8( - Vdbe *p, /* Add the opcode to this VM */ - int op, /* The new opcode */ - int p1, /* The P1 operand */ - int p2, /* The P2 operand */ - int p3, /* The P3 operand */ - const u8 *zP4, /* The P4 operand */ - int p4type /* P4 operand type */ +SQLITE_PRIVATE void sqlcipher_sqlite3ValueApplyAffinity( + sqlcipher_sqlite3_value *pVal, + u8 affinity, + u8 enc ){ - char *p4copy = sqlcipher_sqlite3DbMallocRawNN(sqlcipher_sqlite3VdbeDb(p), 8); - if( p4copy ) memcpy(p4copy, zP4, 8); - return sqlcipher_sqlite3VdbeAddOp4(p, op, p1, p2, p3, p4copy, p4type); + applyAffinity((Mem *)pVal, affinity, enc); } -#ifndef SQLITE_OMIT_EXPLAIN /* -** Return the address of the current EXPLAIN QUERY PLAN baseline. -** 0 means "none". +** pMem currently only holds a string type (or maybe a BLOB that we can +** interpret as a string if we want to). Compute its corresponding +** numeric type, if has one. Set the pMem->u.r and pMem->u.i fields +** accordingly. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeExplainParent(Parse *pParse){ - VdbeOp *pOp; - if( pParse->addrExplain==0 ) return 0; - pOp = sqlcipher_sqlite3VdbeGetOp(pParse->pVdbe, pParse->addrExplain); - return pOp->p2; +static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ + int rc; + sqlcipher_sqlite3_int64 ix; + assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ); + assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ); + if( ExpandBlob(pMem) ){ + pMem->u.i = 0; + return MEM_Int; + } + rc = sqlcipher_sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); + if( rc<=0 ){ + if( rc==0 && sqlcipher_sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1 ){ + pMem->u.i = ix; + return MEM_Int; + }else{ + return MEM_Real; + } + }else if( rc==1 && sqlcipher_sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)==0 ){ + pMem->u.i = ix; + return MEM_Int; + } + return MEM_Real; } /* -** Set a debugger breakpoint on the following routine in order to -** monitor the EXPLAIN QUERY PLAN code generation. +** Return the numeric type for pMem, either MEM_Int or MEM_Real or both or +** none. +** +** Unlike applyNumericAffinity(), this routine does not modify pMem->flags. +** But it does set pMem->u.r and pMem->u.i appropriately. */ -#if defined(SQLITE_DEBUG) -SQLITE_PRIVATE void sqlcipher_sqlite3ExplainBreakpoint(const char *z1, const char *z2){ - (void)z1; - (void)z2; +static u16 numericType(Mem *pMem){ + if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal) ){ + testcase( pMem->flags & MEM_Int ); + testcase( pMem->flags & MEM_Real ); + testcase( pMem->flags & MEM_IntReal ); + return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal); + } + if( pMem->flags & (MEM_Str|MEM_Blob) ){ + testcase( pMem->flags & MEM_Str ); + testcase( pMem->flags & MEM_Blob ); + return computeNumericType(pMem); + } + return 0; } -#endif +#ifdef SQLITE_DEBUG /* -** Add a new OP_Explain opcode. -** -** If the bPush flag is true, then make this opcode the parent for -** subsequent Explains until sqlcipher_sqlite3VdbeExplainPop() is called. +** Write a nice string representation of the contents of cell pMem +** into buffer zBuf, length nBuf. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ -#ifndef SQLITE_DEBUG - /* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined. - ** But omit them (for performance) during production builds */ - if( pParse->explain==2 ) -#endif - { - char *zMsg; - Vdbe *v; - va_list ap; - int iThis; - va_start(ap, zFmt); - zMsg = sqlcipher_sqlite3VMPrintf(pParse->db, zFmt, ap); - va_end(ap); - v = pParse->pVdbe; - iThis = v->nOp; - sqlcipher_sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, - zMsg, P4_DYNAMIC); - sqlcipher_sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlcipher_sqlite3VdbeGetOp(v,-1)->p4.z); - if( bPush){ - pParse->addrExplain = iThis; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr){ + int f = pMem->flags; + static const char *const encnames[] = {"(X)", "(8)", "(16LE)", "(16BE)"}; + if( f&MEM_Blob ){ + int i; + char c; + if( f & MEM_Dyn ){ + c = 'z'; + assert( (f & (MEM_Static|MEM_Ephem))==0 ); + }else if( f & MEM_Static ){ + c = 't'; + assert( (f & (MEM_Dyn|MEM_Ephem))==0 ); + }else if( f & MEM_Ephem ){ + c = 'e'; + assert( (f & (MEM_Static|MEM_Dyn))==0 ); + }else{ + c = 's'; + } + sqlcipher_sqlite3_str_appendf(pStr, "%cx[", c); + for(i=0; i<25 && in; i++){ + sqlcipher_sqlite3_str_appendf(pStr, "%02X", ((int)pMem->z[i] & 0xFF)); + } + sqlcipher_sqlite3_str_appendf(pStr, "|"); + for(i=0; i<25 && in; i++){ + char z = pMem->z[i]; + sqlcipher_sqlite3_str_appendchar(pStr, 1, (z<32||z>126)?'.':z); + } + sqlcipher_sqlite3_str_appendf(pStr,"]"); + if( f & MEM_Zero ){ + sqlcipher_sqlite3_str_appendf(pStr, "+%dz",pMem->u.nZero); + } + }else if( f & MEM_Str ){ + int j; + u8 c; + if( f & MEM_Dyn ){ + c = 'z'; + assert( (f & (MEM_Static|MEM_Ephem))==0 ); + }else if( f & MEM_Static ){ + c = 't'; + assert( (f & (MEM_Dyn|MEM_Ephem))==0 ); + }else if( f & MEM_Ephem ){ + c = 'e'; + assert( (f & (MEM_Static|MEM_Dyn))==0 ); + }else{ + c = 's'; + } + sqlcipher_sqlite3_str_appendf(pStr, " %c%d[", c, pMem->n); + for(j=0; j<25 && jn; j++){ + c = pMem->z[j]; + sqlcipher_sqlite3_str_appendchar(pStr, 1, (c>=0x20&&c<=0x7f) ? c : '.'); } + sqlcipher_sqlite3_str_appendf(pStr, "]%s", encnames[pMem->enc]); } } +#endif +#ifdef SQLITE_DEBUG /* -** Pop the EXPLAIN QUERY PLAN stack one level. +** Print the value of a register for tracing purposes: */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeExplainPop(Parse *pParse){ - sqlcipher_sqlite3ExplainBreakpoint("POP", 0); - pParse->addrExplain = sqlcipher_sqlite3VdbeExplainParent(pParse); +static void memTracePrint(Mem *p){ + if( p->flags & MEM_Undefined ){ + printf(" undefined"); + }else if( p->flags & MEM_Null ){ + printf(p->flags & MEM_Zero ? " NULL-nochng" : " NULL"); + }else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){ + printf(" si:%lld", p->u.i); + }else if( (p->flags & (MEM_IntReal))!=0 ){ + printf(" ir:%lld", p->u.i); + }else if( p->flags & MEM_Int ){ + printf(" i:%lld", p->u.i); +#ifndef SQLITE_OMIT_FLOATING_POINT + }else if( p->flags & MEM_Real ){ + printf(" r:%.17g", p->u.r); +#endif + }else if( sqlcipher_sqlite3VdbeMemIsRowSet(p) ){ + printf(" (rowset)"); + }else{ + StrAccum acc; + char zBuf[1000]; + sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); + sqlcipher_sqlite3VdbeMemPrettyPrint(p, &acc); + printf(" %s", sqlcipher_sqlite3StrAccumFinish(&acc)); + } + if( p->flags & MEM_Subtype ) printf(" subtype=0x%02x", p->eSubtype); } -#endif /* SQLITE_OMIT_EXPLAIN */ - -/* -** Add an OP_ParseSchema opcode. This routine is broken out from -** sqlcipher_sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees -** as having been used. -** -** The zWhere string must have been obtained from sqlcipher_sqlite3_malloc(). -** This routine will take ownership of the allocated memory. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){ - int j; - sqlcipher_sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC); - for(j=0; jdb->nDb; j++) sqlcipher_sqlite3VdbeUsesBtree(p, j); - sqlcipher_sqlite3MayAbort(p->pParse); +static void registerTrace(int iReg, Mem *p){ + printf("R[%d] = ", iReg); + memTracePrint(p); + if( p->pScopyFrom ){ + printf(" <== R[%d]", (int)(p->pScopyFrom - &p[-iReg])); + } + printf("\n"); + sqlcipher_sqlite3VdbeCheckMemInvariants(p); +} +/**/ void sqlcipher_sqlite3PrintMem(Mem *pMem){ + memTracePrint(pMem); + printf("\n"); + fflush(stdout); } +#endif +#ifdef SQLITE_DEBUG /* -** Add an opcode that includes the p4 value as an integer. +** Show the values of all registers in the virtual machine. Used for +** interactive debugging. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAddOp4Int( - Vdbe *p, /* Add the opcode to this VM */ - int op, /* The new opcode */ - int p1, /* The P1 operand */ - int p2, /* The P2 operand */ - int p3, /* The P3 operand */ - int p4 /* The P4 operand as an integer */ -){ - int addr = sqlcipher_sqlite3VdbeAddOp3(p, op, p1, p2, p3); - if( p->db->mallocFailed==0 ){ - VdbeOp *pOp = &p->aOp[addr]; - pOp->p4type = P4_INT32; - pOp->p4.i = p4; - } - return addr; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeRegisterDump(Vdbe *v){ + int i; + for(i=1; inMem; i++) registerTrace(i, v->aMem+i); } +#endif /* SQLITE_DEBUG */ -/* Insert the end of a co-routine + +#ifdef SQLITE_DEBUG +# define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M) +#else +# define REGISTER_TRACE(R,M) +#endif + + +#ifdef VDBE_PROFILE + +/* +** hwtime.h contains inline assembler code for implementing +** high-performance timing routines. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeEndCoroutine(Vdbe *v, int regYield){ - sqlcipher_sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield); +/* #include "hwtime.h" */ - /* Clear the temporary register cache, thereby ensuring that each - ** co-routine has its own independent set of registers, because co-routines - ** might expect their registers to be preserved across an OP_Yield, and - ** that could cause problems if two or more co-routines are using the same - ** temporary register. - */ - v->pParse->nTempReg = 0; - v->pParse->nRangeReg = 0; -} +#endif +#ifndef NDEBUG /* -** Create a new symbolic label for an instruction that has yet to be -** coded. The symbolic label is really just a negative number. The -** label can be used as the P2 value of an operation. Later, when -** the label is resolved to a specific address, the VDBE will scan -** through its operation list and change all values of P2 which match -** the label into the resolved address. -** -** The VDBE knows that a P2 value is a label because labels are -** always negative and P2 values are suppose to be non-negative. -** Hence, a negative P2 value is a label that has yet to be resolved. -** (Later:) This is only true for opcodes that have the OPFLG_JUMP -** property. +** This function is only called from within an assert() expression. It +** checks that the sqlcipher_sqlite3.nTransaction variable is correctly set to +** the number of non-transaction savepoints currently in the +** linked list starting at sqlcipher_sqlite3.pSavepoint. ** -** Variable usage notes: +** Usage: ** -** Parse.aLabel[x] Stores the address that the x-th label resolves -** into. For testing (SQLITE_DEBUG), unresolved -** labels stores -1, but that is not required. -** Parse.nLabelAlloc Number of slots allocated to Parse.aLabel[] -** Parse.nLabel The *negative* of the number of labels that have -** been issued. The negative is stored because -** that gives a performance improvement over storing -** the equivalent positive value. +** assert( checkSavepointCount(db) ); */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeMakeLabel(Parse *pParse){ - return --pParse->nLabel; +static int checkSavepointCount(sqlcipher_sqlite3 *db){ + int n = 0; + Savepoint *p; + for(p=db->pSavepoint; p; p=p->pNext) n++; + assert( n==(db->nSavepoint + db->isTransactionSavepoint) ); + return 1; } +#endif /* -** Resolve label "x" to be the address of the next instruction to -** be inserted. The parameter "x" must have been obtained from -** a prior call to sqlcipher_sqlite3VdbeMakeLabel(). +** Return the register of pOp->p2 after first preparing it to be +** overwritten with an integer value. */ -static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){ - int nNewSize = 10 - p->nLabel; - p->aLabel = sqlcipher_sqlite3DbReallocOrFree(p->db, p->aLabel, - nNewSize*sizeof(p->aLabel[0])); - if( p->aLabel==0 ){ - p->nLabelAlloc = 0; - }else{ -#ifdef SQLITE_DEBUG - int i; - for(i=p->nLabelAlloc; iaLabel[i] = -1; -#endif - p->nLabelAlloc = nNewSize; - p->aLabel[j] = v->nOp; - } +static SQLITE_NOINLINE Mem *out2PrereleaseWithClear(Mem *pOut){ + sqlcipher_sqlite3VdbeMemSetNull(pOut); + pOut->flags = MEM_Int; + return pOut; } -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeResolveLabel(Vdbe *v, int x){ - Parse *p = v->pParse; - int j = ADDR(x); - assert( v->magic==VDBE_MAGIC_INIT ); - assert( j<-p->nLabel ); - assert( j>=0 ); -#ifdef SQLITE_DEBUG - if( p->db->flags & SQLITE_VdbeAddopTrace ){ - printf("RESOLVE LABEL %d to %d\n", x, v->nOp); - } -#endif - if( p->nLabelAlloc + p->nLabel < 0 ){ - resizeResolveLabel(p,v,j); +static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){ + Mem *pOut; + assert( pOp->p2>0 ); + assert( pOp->p2<=(p->nMem+1 - p->nCursor) ); + pOut = &p->aMem[pOp->p2]; + memAboutToChange(p, pOut); + if( VdbeMemDynamic(pOut) ){ /*OPTIMIZATION-IF-FALSE*/ + return out2PrereleaseWithClear(pOut); }else{ - assert( p->aLabel[j]==(-1) ); /* Labels may only be resolved once */ - p->aLabel[j] = v->nOp; + pOut->flags = MEM_Int; + return pOut; } } /* -** Mark the VDBE as one that can only be run one time. +** Compute a bloom filter hash using pOp->p4.i registers from aMem[] beginning +** with pOp->p3. Return the hash. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeRunOnlyOnce(Vdbe *p){ - p->runOnlyOnce = 1; +static u64 filterHash(const Mem *aMem, const Op *pOp){ + int i, mx; + u64 h = 0; + + assert( pOp->p4type==P4_INT32 ); + for(i=pOp->p3, mx=i+pOp->p4.i; iflags & (MEM_Int|MEM_IntReal) ){ + h += p->u.i; + }else if( p->flags & MEM_Real ){ + h += sqlcipher_sqlite3VdbeIntValue(p); + }else if( p->flags & (MEM_Str|MEM_Blob) ){ + h += p->n; + if( p->flags & MEM_Zero ) h += p->u.nZero; + } + } + return h; } /* -** Mark the VDBE as one that can only be run multiple times. +** Return the symbolic name for the data type of a pMem */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeReusable(Vdbe *p){ - p->runOnlyOnce = 0; +static const char *vdbeMemTypeName(Mem *pMem){ + static const char *azTypes[] = { + /* SQLITE_INTEGER */ "INT", + /* SQLITE_FLOAT */ "REAL", + /* SQLITE_TEXT */ "TEXT", + /* SQLITE_BLOB */ "BLOB", + /* SQLITE_NULL */ "NULL" + }; + return azTypes[sqlcipher_sqlite3_value_type(pMem)-1]; } -#ifdef SQLITE_DEBUG /* sqlcipher_sqlite3AssertMayAbort() logic */ - /* -** The following type and function are used to iterate through all opcodes -** in a Vdbe main program and each of the sub-programs (triggers) it may -** invoke directly or indirectly. It should be used as follows: -** -** Op *pOp; -** VdbeOpIter sIter; -** -** memset(&sIter, 0, sizeof(sIter)); -** sIter.v = v; // v is of type Vdbe* -** while( (pOp = opIterNext(&sIter)) ){ -** // Do something with pOp -** } -** sqlcipher_sqlite3DbFree(v->db, sIter.apSub); -** +** Execute as much of a VDBE program as we can. +** This is the core of sqlcipher_sqlite3_step(). */ -typedef struct VdbeOpIter VdbeOpIter; -struct VdbeOpIter { - Vdbe *v; /* Vdbe to iterate through the opcodes of */ - SubProgram **apSub; /* Array of subprograms */ - int nSub; /* Number of entries in apSub */ - int iAddr; /* Address of next instruction to return */ - int iSub; /* 0 = main program, 1 = first sub-program etc. */ -}; -static Op *opIterNext(VdbeOpIter *p){ - Vdbe *v = p->v; - Op *pRet = 0; - Op *aOp; - int nOp; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeExec( + Vdbe *p /* The VDBE */ +){ + Op *aOp = p->aOp; /* Copy of p->aOp */ + Op *pOp = aOp; /* Current operation */ +#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) + Op *pOrigOp; /* Value of pOp at the top of the loop */ +#endif +#ifdef SQLITE_DEBUG + int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */ +#endif + int rc = SQLITE_OK; /* Value to return */ + sqlcipher_sqlite3 *db = p->db; /* The database */ + u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ + u8 encoding = ENC(db); /* The database encoding */ + int iCompare = 0; /* Result of last comparison */ + u64 nVmStep = 0; /* Number of virtual machine steps */ +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + u64 nProgressLimit; /* Invoke xProgress() when nVmStep reaches this */ +#endif + Mem *aMem = p->aMem; /* Copy of p->aMem */ + Mem *pIn1 = 0; /* 1st input operand */ + Mem *pIn2 = 0; /* 2nd input operand */ + Mem *pIn3 = 0; /* 3rd input operand */ + Mem *pOut = 0; /* Output operand */ +#ifdef VDBE_PROFILE + u64 start; /* CPU clock count at start of opcode */ +#endif + /*** INSERT STACK UNION HERE ***/ - if( p->iSub<=p->nSub ){ + assert( p->eVdbeState==VDBE_RUN_STATE ); /* sqlcipher_sqlite3_step() verifies this */ + sqlcipher_sqlite3VdbeEnter(p); +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + if( db->xProgress ){ + u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP]; + assert( 0 < db->nProgressOps ); + nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps); + }else{ + nProgressLimit = LARGEST_UINT64; + } +#endif + if( p->rc==SQLITE_NOMEM ){ + /* This happens if a malloc() inside a call to sqlcipher_sqlite3_column_text() or + ** sqlcipher_sqlite3_column_text16() failed. */ + goto no_mem; + } + assert( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_BUSY ); + testcase( p->rc!=SQLITE_OK ); + p->rc = SQLITE_OK; + assert( p->bIsReader || p->readOnly!=0 ); + p->iCurrentTime = 0; + assert( p->explain==0 ); + p->pResultSet = 0; + db->busyHandler.nBusy = 0; + if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; + sqlcipher_sqlite3VdbeIOTraceSql(p); +#ifdef SQLITE_DEBUG + sqlcipher_sqlite3BeginBenignMalloc(); + if( p->pc==0 + && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0 + ){ + int i; + int once = 1; + sqlcipher_sqlite3VdbePrintSql(p); + if( p->db->flags & SQLITE_VdbeListing ){ + printf("VDBE Program Listing:\n"); + for(i=0; inOp; i++){ + sqlcipher_sqlite3VdbePrintOp(stdout, i, &aOp[i]); + } + } + if( p->db->flags & SQLITE_VdbeEQP ){ + for(i=0; inOp; i++){ + if( aOp[i].opcode==OP_Explain ){ + if( once ) printf("VDBE Query Plan:\n"); + printf("%s\n", aOp[i].p4.z); + once = 0; + } + } + } + if( p->db->flags & SQLITE_VdbeTrace ) printf("VDBE Trace:\n"); + } + sqlcipher_sqlite3EndBenignMalloc(); +#endif + for(pOp=&aOp[p->pc]; 1; pOp++){ + /* Errors are detected by individual opcodes, with an immediate + ** jumps to abort_due_to_error. */ + assert( rc==SQLITE_OK ); - if( p->iSub==0 ){ - aOp = v->aOp; - nOp = v->nOp; - }else{ - aOp = p->apSub[p->iSub-1]->aOp; - nOp = p->apSub[p->iSub-1]->nOp; + assert( pOp>=aOp && pOp<&aOp[p->nOp]); +#ifdef VDBE_PROFILE + start = sqlcipher_sqlite3NProfileCnt ? sqlcipher_sqlite3NProfileCnt : sqlcipher_sqlite3Hwtime(); +#endif + nVmStep++; +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + if( p->anExec ) p->anExec[(int)(pOp-aOp)]++; +#endif + + /* Only allow tracing if SQLITE_DEBUG is defined. + */ +#ifdef SQLITE_DEBUG + if( db->flags & SQLITE_VdbeTrace ){ + sqlcipher_sqlite3VdbePrintOp(stdout, (int)(pOp - aOp), pOp); + test_trace_breakpoint((int)(pOp - aOp),pOp,p); } - assert( p->iAddriAddr]; - p->iAddr++; - if( p->iAddr==nOp ){ - p->iSub++; - p->iAddr = 0; + + /* Check to see if we need to simulate an interrupt. This only happens + ** if we have a special test build. + */ +#ifdef SQLITE_TEST + if( sqlcipher_sqlite3_interrupt_count>0 ){ + sqlcipher_sqlite3_interrupt_count--; + if( sqlcipher_sqlite3_interrupt_count==0 ){ + sqlcipher_sqlite3_interrupt(db); + } } +#endif - if( pRet->p4type==P4_SUBPROGRAM ){ - int nByte = (p->nSub+1)*sizeof(SubProgram*); - int j; - for(j=0; jnSub; j++){ - if( p->apSub[j]==pRet->p4.pProgram ) break; + /* Sanity checking on other operands */ +#ifdef SQLITE_DEBUG + { + u8 opProperty = sqlcipher_sqlite3OpcodeProperty[pOp->opcode]; + if( (opProperty & OPFLG_IN1)!=0 ){ + assert( pOp->p1>0 ); + assert( pOp->p1<=(p->nMem+1 - p->nCursor) ); + assert( memIsValid(&aMem[pOp->p1]) ); + assert( sqlcipher_sqlite3VdbeCheckMemInvariants(&aMem[pOp->p1]) ); + REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]); } - if( j==p->nSub ){ - p->apSub = sqlcipher_sqlite3DbReallocOrFree(v->db, p->apSub, nByte); - if( !p->apSub ){ - pRet = 0; - }else{ - p->apSub[p->nSub++] = pRet->p4.pProgram; - } + if( (opProperty & OPFLG_IN2)!=0 ){ + assert( pOp->p2>0 ); + assert( pOp->p2<=(p->nMem+1 - p->nCursor) ); + assert( memIsValid(&aMem[pOp->p2]) ); + assert( sqlcipher_sqlite3VdbeCheckMemInvariants(&aMem[pOp->p2]) ); + REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]); + } + if( (opProperty & OPFLG_IN3)!=0 ){ + assert( pOp->p3>0 ); + assert( pOp->p3<=(p->nMem+1 - p->nCursor) ); + assert( memIsValid(&aMem[pOp->p3]) ); + assert( sqlcipher_sqlite3VdbeCheckMemInvariants(&aMem[pOp->p3]) ); + REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]); + } + if( (opProperty & OPFLG_OUT2)!=0 ){ + assert( pOp->p2>0 ); + assert( pOp->p2<=(p->nMem+1 - p->nCursor) ); + memAboutToChange(p, &aMem[pOp->p2]); + } + if( (opProperty & OPFLG_OUT3)!=0 ){ + assert( pOp->p3>0 ); + assert( pOp->p3<=(p->nMem+1 - p->nCursor) ); + memAboutToChange(p, &aMem[pOp->p3]); } } - } +#endif +#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) + pOrigOp = pOp; +#endif - return pRet; -} + switch( pOp->opcode ){ -/* -** Check if the program stored in the VM associated with pParse may -** throw an ABORT exception (causing the statement, but not entire transaction -** to be rolled back). This condition is true if the main program or any -** sub-programs contains any of the following: +/***************************************************************************** +** What follows is a massive switch statement where each case implements a +** separate instruction in the virtual machine. If we follow the usual +** indentation conventions, each case should be indented by 6 spaces. But +** that is a lot of wasted space on the left margin. So the code within +** the switch statement will break with convention and be flush-left. Another +** big comment (similar to this one) will mark the point in the code where +** we transition back to normal indentation. ** -** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort. -** * OP_HaltIfNull with P1=SQLITE_CONSTRAINT and P2=OE_Abort. -** * OP_Destroy -** * OP_VUpdate -** * OP_VCreate -** * OP_VRename -** * OP_FkCounter with P2==0 (immediate foreign key constraint) -** * OP_CreateBtree/BTREE_INTKEY and OP_InitCoroutine -** (for CREATE TABLE AS SELECT ...) +** The formatting of each case is important. The makefile for SQLite +** generates two C files "opcodes.h" and "opcodes.c" by scanning this +** file looking for lines that begin with "case OP_". The opcodes.h files +** will be filled with #defines that give unique integer values to each +** opcode and the opcodes.c file is filled with an array of strings where +** each string is the symbolic name for the corresponding opcode. If the +** case statement is followed by a comment of the form "/# same as ... #/" +** that comment is used to determine the particular value of the opcode. ** -** Then check that the value of Parse.mayAbort is true if an -** ABORT may be thrown, or false otherwise. Return true if it does -** match, or false otherwise. This function is intended to be used as -** part of an assert statement in the compiler. Similar to: +** Other keywords in the comment that follows each case are used to +** construct the OPFLG_INITIALIZER value that initializes opcodeProperty[]. +** Keywords include: in1, in2, in3, out2, out3. See +** the mkopcodeh.awk script for additional information. ** -** assert( sqlcipher_sqlite3VdbeAssertMayAbort(pParse->pVdbe, pParse->mayAbort) ); +** Documentation about VDBE opcodes is generated by scanning this file +** for lines of that contain "Opcode:". That line and all subsequent +** comment lines are used in the generation of the opcode.html documentation +** file. +** +** SUMMARY: +** +** Formatting is important to scripts that scan this file. +** Do not deviate from the formatting style currently in use. +** +*****************************************************************************/ + +/* Opcode: Goto * P2 * * * +** +** An unconditional jump to address P2. +** The next instruction executed will be +** the one at index P2 from the beginning of +** the program. +** +** The P1 parameter is not actually used by this opcode. However, it +** is sometimes set to 1 instead of 0 as a hint to the command-line shell +** that this Goto is the bottom of a loop and that the lines from P2 down +** to the current line should be indented for EXPLAIN output. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ - int hasAbort = 0; - int hasFkCounter = 0; - int hasCreateTable = 0; - int hasCreateIndex = 0; - int hasInitCoroutine = 0; - Op *pOp; - VdbeOpIter sIter; - memset(&sIter, 0, sizeof(sIter)); - sIter.v = v; +case OP_Goto: { /* jump */ - while( (pOp = opIterNext(&sIter))!=0 ){ - int opcode = pOp->opcode; - if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename - || opcode==OP_VDestroy - || opcode==OP_VCreate - || opcode==OP_ParseSchema - || ((opcode==OP_Halt || opcode==OP_HaltIfNull) - && ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort)) - ){ - hasAbort = 1; - break; - } - if( opcode==OP_CreateBtree && pOp->p3==BTREE_INTKEY ) hasCreateTable = 1; - if( mayAbort ){ - /* hasCreateIndex may also be set for some DELETE statements that use - ** OP_Clear. So this routine may end up returning true in the case - ** where a "DELETE FROM tbl" has a statement-journal but does not - ** require one. This is not so bad - it is an inefficiency, not a bug. */ - if( opcode==OP_CreateBtree && pOp->p3==BTREE_BLOBKEY ) hasCreateIndex = 1; - if( opcode==OP_Clear ) hasCreateIndex = 1; - } - if( opcode==OP_InitCoroutine ) hasInitCoroutine = 1; -#ifndef SQLITE_OMIT_FOREIGN_KEY - if( opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1 ){ - hasFkCounter = 1; +#ifdef SQLITE_DEBUG + /* In debuggging mode, when the p5 flags is set on an OP_Goto, that + ** means we should really jump back to the preceeding OP_ReleaseReg + ** instruction. */ + if( pOp->p5 ){ + assert( pOp->p2 < (int)(pOp - aOp) ); + assert( pOp->p2 > 1 ); + pOp = &aOp[pOp->p2 - 2]; + assert( pOp[1].opcode==OP_ReleaseReg ); + goto check_for_interrupt; + } +#endif + +jump_to_p2_and_check_for_interrupt: + pOp = &aOp[pOp->p2 - 1]; + + /* Opcodes that are used as the bottom of a loop (OP_Next, OP_Prev, + ** OP_VNext, or OP_SorterNext) all jump here upon + ** completion. Check to see if sqlcipher_sqlite3_interrupt() has been called + ** or if the progress callback needs to be invoked. + ** + ** This code uses unstructured "goto" statements and does not look clean. + ** But that is not due to sloppy coding habits. The code is written this + ** way for performance, to avoid having to run the interrupt and progress + ** checks on every opcode. This helps sqlcipher_sqlite3_step() to run about 1.5% + ** faster according to "valgrind --tool=cachegrind" */ +check_for_interrupt: + if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + /* Call the progress callback if it is configured and the required number + ** of VDBE ops have been executed (either since this invocation of + ** sqlcipher_sqlite3VdbeExec() or since last time the progress callback was called). + ** If the progress callback returns non-zero, exit the virtual machine with + ** a return code SQLITE_ABORT. + */ + while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ + assert( db->nProgressOps!=0 ); + nProgressLimit += db->nProgressOps; + if( db->xProgress(db->pProgressArg) ){ + nProgressLimit = LARGEST_UINT64; + rc = SQLITE_INTERRUPT; + goto abort_due_to_error; } + } #endif + + break; +} + +/* Opcode: Gosub P1 P2 * * * +** +** Write the current address onto register P1 +** and then jump to address P2. +*/ +case OP_Gosub: { /* jump */ + assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); + pIn1 = &aMem[pOp->p1]; + assert( VdbeMemDynamic(pIn1)==0 ); + memAboutToChange(p, pIn1); + pIn1->flags = MEM_Int; + pIn1->u.i = (int)(pOp-aOp); + REGISTER_TRACE(pOp->p1, pIn1); + goto jump_to_p2_and_check_for_interrupt; +} + +/* Opcode: Return P1 P2 P3 * * +** +** Jump to the address stored in register P1. If P1 is a return address +** register, then this accomplishes a return from a subroutine. +** +** If P3 is 1, then the jump is only taken if register P1 holds an integer +** values, otherwise execution falls through to the next opcode, and the +** OP_Return becomes a no-op. If P3 is 0, then register P1 must hold an +** integer or else an assert() is raised. P3 should be set to 1 when +** this opcode is used in combination with OP_BeginSubrtn, and set to 0 +** otherwise. +** +** The value in register P1 is unchanged by this opcode. +** +** P2 is not used by the byte-code engine. However, if P2 is positive +** and also less than the current address, then the "EXPLAIN" output +** formatter in the CLI will indent all opcodes from the P2 opcode up +** to be not including the current Return. P2 should be the first opcode +** in the subroutine from which this opcode is returning. Thus the P2 +** value is a byte-code indentation hint. See tag-20220407a in +** wherecode.c and shell.c. +*/ +case OP_Return: { /* in1 */ + pIn1 = &aMem[pOp->p1]; + if( pIn1->flags & MEM_Int ){ + if( pOp->p3 ){ VdbeBranchTaken(1, 2); } + pOp = &aOp[pIn1->u.i]; + }else if( ALWAYS(pOp->p3) ){ + VdbeBranchTaken(0, 2); } - sqlcipher_sqlite3DbFree(v->db, sIter.apSub); + break; +} - /* Return true if hasAbort==mayAbort. Or if a malloc failure occurred. - ** If malloc failed, then the while() loop above may not have iterated - ** through all opcodes and hasAbort may be set incorrectly. Return - ** true for this case to prevent the assert() in the callers frame - ** from failing. */ - return ( v->db->mallocFailed || hasAbort==mayAbort || hasFkCounter - || (hasCreateTable && hasInitCoroutine) || hasCreateIndex - ); +/* Opcode: InitCoroutine P1 P2 P3 * * +** +** Set up register P1 so that it will Yield to the coroutine +** located at address P3. +** +** If P2!=0 then the coroutine implementation immediately follows +** this opcode. So jump over the coroutine implementation to +** address P2. +** +** See also: EndCoroutine +*/ +case OP_InitCoroutine: { /* jump */ + assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); + assert( pOp->p2>=0 && pOp->p2nOp ); + assert( pOp->p3>=0 && pOp->p3nOp ); + pOut = &aMem[pOp->p1]; + assert( !VdbeMemDynamic(pOut) ); + pOut->u.i = pOp->p3 - 1; + pOut->flags = MEM_Int; + if( pOp->p2==0 ) break; + + /* Most jump operations do a goto to this spot in order to update + ** the pOp pointer. */ +jump_to_p2: + assert( pOp->p2>0 ); /* There are never any jumps to instruction 0 */ + assert( pOp->p2nOp ); /* Jumps must be in range */ + pOp = &aOp[pOp->p2 - 1]; + break; +} + +/* Opcode: EndCoroutine P1 * * * * +** +** The instruction at the address in register P1 is a Yield. +** Jump to the P2 parameter of that Yield. +** After the jump, register P1 becomes undefined. +** +** See also: InitCoroutine +*/ +case OP_EndCoroutine: { /* in1 */ + VdbeOp *pCaller; + pIn1 = &aMem[pOp->p1]; + assert( pIn1->flags==MEM_Int ); + assert( pIn1->u.i>=0 && pIn1->u.inOp ); + pCaller = &aOp[pIn1->u.i]; + assert( pCaller->opcode==OP_Yield ); + assert( pCaller->p2>=0 && pCaller->p2nOp ); + pOp = &aOp[pCaller->p2 - 1]; + pIn1->flags = MEM_Undefined; + break; } -#endif /* SQLITE_DEBUG - the sqlcipher_sqlite3AssertMayAbort() function */ -#ifdef SQLITE_DEBUG -/* -** Increment the nWrite counter in the VDBE if the cursor is not an -** ephemeral cursor, or if the cursor argument is NULL. +/* Opcode: Yield P1 P2 * * * +** +** Swap the program counter with the value in register P1. This +** has the effect of yielding to a coroutine. +** +** If the coroutine that is launched by this instruction ends with +** Yield or Return then continue to the next instruction. But if +** the coroutine launched by this instruction ends with +** EndCoroutine, then jump to P2 rather than continuing with the +** next instruction. +** +** See also: InitCoroutine */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeIncrWriteCounter(Vdbe *p, VdbeCursor *pC){ - if( pC==0 - || (pC->eCurType!=CURTYPE_SORTER - && pC->eCurType!=CURTYPE_PSEUDO - && !pC->isEphemeral) - ){ - p->nWrite++; - } +case OP_Yield: { /* in1, jump */ + int pcDest; + pIn1 = &aMem[pOp->p1]; + assert( VdbeMemDynamic(pIn1)==0 ); + pIn1->flags = MEM_Int; + pcDest = (int)pIn1->u.i; + pIn1->u.i = (int)(pOp - aOp); + REGISTER_TRACE(pOp->p1, pIn1); + pOp = &aOp[pcDest]; + break; } -#endif -#ifdef SQLITE_DEBUG -/* -** Assert if an Abort at this point in time might result in a corrupt -** database. +/* Opcode: HaltIfNull P1 P2 P3 P4 P5 +** Synopsis: if r[P3]=null halt +** +** Check the value in register P3. If it is NULL then Halt using +** parameter P1, P2, and P4 as if this were a Halt instruction. If the +** value in register P3 is not NULL, then this routine is a no-op. +** The P5 parameter should be 1. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeAssertAbortable(Vdbe *p){ - assert( p->nWrite==0 || p->usesStmtJournal ); -} +case OP_HaltIfNull: { /* in3 */ + pIn3 = &aMem[pOp->p3]; +#ifdef SQLITE_DEBUG + if( pOp->p2==OE_Abort ){ sqlcipher_sqlite3VdbeAssertAbortable(p); } #endif + if( (pIn3->flags & MEM_Null)==0 ) break; + /* Fall through into OP_Halt */ + /* no break */ deliberate_fall_through +} -/* -** This routine is called after all opcodes have been inserted. It loops -** through all the opcodes and fixes up some details. +/* Opcode: Halt P1 P2 * P4 P5 ** -** (1) For each jump instruction with a negative P2 value (a label) -** resolve the P2 value to an actual address. +** Exit immediately. All open cursors, etc are closed +** automatically. ** -** (2) Compute the maximum number of arguments used by any SQL function -** and store that value in *pMaxFuncArgs. +** P1 is the result code returned by sqlcipher_sqlite3_exec(), sqlcipher_sqlite3_reset(), +** or sqlcipher_sqlite3_finalize(). For a normal halt, this should be SQLITE_OK (0). +** For errors, it can be some other value. If P1!=0 then P2 will determine +** whether or not to rollback the current transaction. Do not rollback +** if P2==OE_Fail. Do the rollback if P2==OE_Rollback. If P2==OE_Abort, +** then back out all changes that have occurred during this execution of the +** VDBE, but do not rollback the transaction. ** -** (3) Update the Vdbe.readOnly and Vdbe.bIsReader flags to accurately -** indicate what the prepared statement actually does. +** If P4 is not null then it is an error message string. ** -** (4) Initialize the p4.xAdvance pointer on opcodes that use it. +** P5 is a value between 0 and 4, inclusive, that modifies the P4 string. ** -** (5) Reclaim the memory allocated for storing labels. +** 0: (no change) +** 1: NOT NULL contraint failed: P4 +** 2: UNIQUE constraint failed: P4 +** 3: CHECK constraint failed: P4 +** 4: FOREIGN KEY constraint failed: P4 ** -** This routine will only function correctly if the mkopcodeh.tcl generator -** script numbers the opcodes correctly. Changes to this routine must be -** coordinated with changes to mkopcodeh.tcl. +** If P5 is not zero and P4 is NULL, then everything after the ":" is +** omitted. +** +** There is an implied "Halt 0 0 0" instruction inserted at the very end of +** every program. So a jump past the last instruction of the program +** is the same as executing Halt. */ -static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ - int nMaxArgs = *pMaxFuncArgs; - Op *pOp; - Parse *pParse = p->pParse; - int *aLabel = pParse->aLabel; - p->readOnly = 1; - p->bIsReader = 0; - pOp = &p->aOp[p->nOp-1]; - while(1){ +case OP_Halt: { + VdbeFrame *pFrame; + int pcx; - /* Only JUMP opcodes and the short list of special opcodes in the switch - ** below need to be considered. The mkopcodeh.tcl generator script groups - ** all these opcodes together near the front of the opcode list. Skip - ** any opcode that does not need processing by virtual of the fact that - ** it is larger than SQLITE_MX_JUMP_OPCODE, as a performance optimization. - */ - if( pOp->opcode<=SQLITE_MX_JUMP_OPCODE ){ - /* NOTE: Be sure to update mkopcodeh.tcl when adding or removing - ** cases from this switch! */ - switch( pOp->opcode ){ - case OP_Transaction: { - if( pOp->p2!=0 ) p->readOnly = 0; - /* no break */ deliberate_fall_through - } - case OP_AutoCommit: - case OP_Savepoint: { - p->bIsReader = 1; - break; - } -#ifndef SQLITE_OMIT_WAL - case OP_Checkpoint: -#endif - case OP_Vacuum: - case OP_JournalMode: { - p->readOnly = 0; - p->bIsReader = 1; - break; - } - case OP_Next: - case OP_SorterNext: { - pOp->p4.xAdvance = sqlcipher_sqlite3BtreeNext; - pOp->p4type = P4_ADVANCE; - /* The code generator never codes any of these opcodes as a jump - ** to a label. They are always coded as a jump backwards to a - ** known address */ - assert( pOp->p2>=0 ); - break; - } - case OP_Prev: { - pOp->p4.xAdvance = sqlcipher_sqlite3BtreePrevious; - pOp->p4type = P4_ADVANCE; - /* The code generator never codes any of these opcodes as a jump - ** to a label. They are always coded as a jump backwards to a - ** known address */ - assert( pOp->p2>=0 ); - break; - } -#ifndef SQLITE_OMIT_VIRTUALTABLE - case OP_VUpdate: { - if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; - break; - } - case OP_VFilter: { - int n; - assert( (pOp - p->aOp) >= 3 ); - assert( pOp[-1].opcode==OP_Integer ); - n = pOp[-1].p1; - if( n>nMaxArgs ) nMaxArgs = n; - /* Fall through into the default case */ - /* no break */ deliberate_fall_through - } +#ifdef SQLITE_DEBUG + if( pOp->p2==OE_Abort ){ sqlcipher_sqlite3VdbeAssertAbortable(p); } #endif - default: { - if( pOp->p2<0 ){ - /* The mkopcodeh.tcl script has so arranged things that the only - ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to - ** have non-negative values for P2. */ - assert( (sqlcipher_sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); - assert( ADDR(pOp->p2)<-pParse->nLabel ); - pOp->p2 = aLabel[ADDR(pOp->p2)]; - } - break; - } + if( p->pFrame && pOp->p1==SQLITE_OK ){ + /* Halt the sub-program. Return control to the parent frame. */ + pFrame = p->pFrame; + p->pFrame = pFrame->pParent; + p->nFrame--; + sqlcipher_sqlite3VdbeSetChanges(db, p->nChange); + pcx = sqlcipher_sqlite3VdbeFrameRestore(pFrame); + if( pOp->p2==OE_Ignore ){ + /* Instruction pcx is the OP_Program that invoked the sub-program + ** currently being halted. If the p2 instruction of this OP_Halt + ** instruction is set to OE_Ignore, then the sub-program is throwing + ** an IGNORE exception. In this case jump to the address specified + ** as the p2 of the calling OP_Program. */ + pcx = p->aOp[pcx].p2-1; + } + aOp = p->aOp; + aMem = p->aMem; + pOp = &aOp[pcx]; + break; + } + p->rc = pOp->p1; + p->errorAction = (u8)pOp->p2; + assert( pOp->p5<=4 ); + if( p->rc ){ + if( pOp->p5 ){ + static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK", + "FOREIGN KEY" }; + testcase( pOp->p5==1 ); + testcase( pOp->p5==2 ); + testcase( pOp->p5==3 ); + testcase( pOp->p5==4 ); + sqlcipher_sqlite3VdbeError(p, "%s constraint failed", azType[pOp->p5-1]); + if( pOp->p4.z ){ + p->zErrMsg = sqlcipher_sqlite3MPrintf(db, "%z: %s", p->zErrMsg, pOp->p4.z); } - /* The mkopcodeh.tcl script has so arranged things that the only - ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to - ** have non-negative values for P2. */ - assert( (sqlcipher_sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0); + }else{ + sqlcipher_sqlite3VdbeError(p, "%s", pOp->p4.z); } - if( pOp==p->aOp ) break; - pOp--; + pcx = (int)(pOp - aOp); + sqlcipher_sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg); } - sqlcipher_sqlite3DbFree(p->db, pParse->aLabel); - pParse->aLabel = 0; - pParse->nLabel = 0; - *pMaxFuncArgs = nMaxArgs; - assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) ); -} - -/* -** Return the address of the next instruction to be inserted. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCurrentAddr(Vdbe *p){ - assert( p->magic==VDBE_MAGIC_INIT ); - return p->nOp; + rc = sqlcipher_sqlite3VdbeHalt(p); + assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); + if( rc==SQLITE_BUSY ){ + p->rc = SQLITE_BUSY; + }else{ + assert( rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT ); + assert( rc==SQLITE_OK || db->nDeferredCons>0 || db->nDeferredImmCons>0 ); + rc = p->rc ? SQLITE_ERROR : SQLITE_DONE; + } + goto vdbe_return; } -/* -** Verify that at least N opcode slots are available in p without -** having to malloc for more space (except when compiled using -** SQLITE_TEST_REALLOC_STRESS). This interface is used during testing -** to verify that certain calls to sqlcipher_sqlite3VdbeAddOpList() can never -** fail due to a OOM fault and hence that the return value from -** sqlcipher_sqlite3VdbeAddOpList() will always be non-NULL. +/* Opcode: Integer P1 P2 * * * +** Synopsis: r[P2]=P1 +** +** The 32-bit integer value P1 is written into register P2. */ -#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS) -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){ - assert( p->nOp + N <= p->nOpAlloc ); +case OP_Integer: { /* out2 */ + pOut = out2Prerelease(p, pOp); + pOut->u.i = pOp->p1; + break; } -#endif -/* -** Verify that the VM passed as the only argument does not contain -** an OP_ResultRow opcode. Fail an assert() if it does. This is used -** by code in pragma.c to ensure that the implementation of certain -** pragmas comports with the flags specified in the mkpragmatab.tcl -** script. +/* Opcode: Int64 * P2 * P4 * +** Synopsis: r[P2]=P4 +** +** P4 is a pointer to a 64-bit integer value. +** Write that value into register P2. */ -#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS) -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeVerifyNoResultRow(Vdbe *p){ - int i; - for(i=0; inOp; i++){ - assert( p->aOp[i].opcode!=OP_ResultRow ); - } +case OP_Int64: { /* out2 */ + pOut = out2Prerelease(p, pOp); + assert( pOp->p4.pI64!=0 ); + pOut->u.i = *pOp->p4.pI64; + break; } -#endif -/* -** Generate code (a single OP_Abortable opcode) that will -** verify that the VDBE program can safely call Abort in the current -** context. +#ifndef SQLITE_OMIT_FLOATING_POINT +/* Opcode: Real * P2 * P4 * +** Synopsis: r[P2]=P4 +** +** P4 is a pointer to a 64-bit floating point value. +** Write that value into register P2. */ -#if defined(SQLITE_DEBUG) -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeVerifyAbortable(Vdbe *p, int onError){ - if( onError==OE_Abort ) sqlcipher_sqlite3VdbeAddOp0(p, OP_Abortable); +case OP_Real: { /* same as TK_FLOAT, out2 */ + pOut = out2Prerelease(p, pOp); + pOut->flags = MEM_Real; + assert( !sqlcipher_sqlite3IsNaN(*pOp->p4.pReal) ); + pOut->u.r = *pOp->p4.pReal; + break; } #endif -/* -** This function returns a pointer to the array of opcodes associated with -** the Vdbe passed as the first argument. It is the callers responsibility -** to arrange for the returned array to be eventually freed using the -** vdbeFreeOpArray() function. +/* Opcode: String8 * P2 * P4 * +** Synopsis: r[P2]='P4' ** -** Before returning, *pnOp is set to the number of entries in the returned -** array. Also, *pnMaxArg is set to the larger of its current value and -** the number of entries in the Vdbe.apArg[] array required to execute the -** returned program. +** P4 points to a nul terminated UTF-8 string. This opcode is transformed +** into a String opcode before it is executed for the first time. During +** this transformation, the length of string P4 is computed and stored +** as the P1 parameter. */ -SQLITE_PRIVATE VdbeOp *sqlcipher_sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ - VdbeOp *aOp = p->aOp; - assert( aOp && !p->db->mallocFailed ); - - /* Check that sqlcipher_sqlite3VdbeUsesBtree() was not called on this VM */ - assert( DbMaskAllZero(p->btreeMask) ); - - resolveP2Values(p, pnMaxArg); - *pnOp = p->nOp; - p->aOp = 0; - return aOp; -} +case OP_String8: { /* same as TK_STRING, out2 */ + assert( pOp->p4.z!=0 ); + pOut = out2Prerelease(p, pOp); + pOp->p1 = sqlcipher_sqlite3Strlen30(pOp->p4.z); -/* -** Add a whole list of operations to the operation stack. Return a -** pointer to the first operation inserted. -** -** Non-zero P2 arguments to jump instructions are automatically adjusted -** so that the jump target is relative to the first operation inserted. -*/ -SQLITE_PRIVATE VdbeOp *sqlcipher_sqlite3VdbeAddOpList( - Vdbe *p, /* Add opcodes to the prepared statement */ - int nOp, /* Number of opcodes to add */ - VdbeOpList const *aOp, /* The opcodes to be added */ - int iLineno /* Source-file line number of first opcode */ -){ - int i; - VdbeOp *pOut, *pFirst; - assert( nOp>0 ); - assert( p->magic==VDBE_MAGIC_INIT ); - if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){ - return 0; - } - pFirst = pOut = &p->aOp[p->nOp]; - for(i=0; iopcode = aOp->opcode; - pOut->p1 = aOp->p1; - pOut->p2 = aOp->p2; - assert( aOp->p2>=0 ); - if( (sqlcipher_sqlite3OpcodeProperty[aOp->opcode] & OPFLG_JUMP)!=0 && aOp->p2>0 ){ - pOut->p2 += p->nOp; - } - pOut->p3 = aOp->p3; - pOut->p4type = P4_NOTUSED; - pOut->p4.p = 0; - pOut->p5 = 0; -#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - pOut->zComment = 0; -#endif -#ifdef SQLITE_VDBE_COVERAGE - pOut->iSrcLine = iLineno+i; -#else - (void)iLineno; -#endif -#ifdef SQLITE_DEBUG - if( p->db->flags & SQLITE_VdbeAddopTrace ){ - sqlcipher_sqlite3VdbePrintOp(0, i+p->nOp, &p->aOp[i+p->nOp]); +#ifndef SQLITE_OMIT_UTF16 + if( encoding!=SQLITE_UTF8 ){ + rc = sqlcipher_sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC); + assert( rc==SQLITE_OK || rc==SQLITE_TOOBIG ); + if( rc ) goto too_big; + if( SQLITE_OK!=sqlcipher_sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem; + assert( pOut->szMalloc>0 && pOut->zMalloc==pOut->z ); + assert( VdbeMemDynamic(pOut)==0 ); + pOut->szMalloc = 0; + pOut->flags |= MEM_Static; + if( pOp->p4type==P4_DYNAMIC ){ + sqlcipher_sqlite3DbFree(db, pOp->p4.z); } + pOp->p4type = P4_DYNAMIC; + pOp->p4.z = pOut->z; + pOp->p1 = pOut->n; + } #endif + if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; } - p->nOp += nOp; - return pFirst; + pOp->opcode = OP_String; + assert( rc==SQLITE_OK ); + /* Fall through to the next case, OP_String */ + /* no break */ deliberate_fall_through } -#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) -/* -** Add an entry to the array of counters managed by sqlcipher_sqlite3_stmt_scanstatus(). +/* Opcode: String P1 P2 P3 P4 P5 +** Synopsis: r[P2]='P4' (len=P1) +** +** The string value P4 of length P1 (bytes) is stored in register P2. +** +** If P3 is not zero and the content of register P3 is equal to P5, then +** the datatype of the register P2 is converted to BLOB. The content is +** the same sequence of bytes, it is merely interpreted as a BLOB instead +** of a string, as if it had been CAST. In other words: +** +** if( P3!=0 and reg[P3]==P5 ) reg[P2] := CAST(reg[P2] as BLOB) */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeScanStatus( - Vdbe *p, /* VM to add scanstatus() to */ - int addrExplain, /* Address of OP_Explain (or 0) */ - int addrLoop, /* Address of loop counter */ - int addrVisit, /* Address of rows visited counter */ - LogEst nEst, /* Estimated number of output rows */ - const char *zName /* Name of table or index being scanned */ -){ - sqlcipher_sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus); - ScanStatus *aNew; - aNew = (ScanStatus*)sqlcipher_sqlite3DbRealloc(p->db, p->aScan, nByte); - if( aNew ){ - ScanStatus *pNew = &aNew[p->nScan++]; - pNew->addrExplain = addrExplain; - pNew->addrLoop = addrLoop; - pNew->addrVisit = addrVisit; - pNew->nEst = nEst; - pNew->zName = sqlcipher_sqlite3DbStrDup(p->db, zName); - p->aScan = aNew; +case OP_String: { /* out2 */ + assert( pOp->p4.z!=0 ); + pOut = out2Prerelease(p, pOp); + pOut->flags = MEM_Str|MEM_Static|MEM_Term; + pOut->z = pOp->p4.z; + pOut->n = pOp->p1; + pOut->enc = encoding; + UPDATE_MAX_BLOBSIZE(pOut); +#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS + if( pOp->p3>0 ){ + assert( pOp->p3<=(p->nMem+1 - p->nCursor) ); + pIn3 = &aMem[pOp->p3]; + assert( pIn3->flags & MEM_Int ); + if( pIn3->u.i==pOp->p5 ) pOut->flags = MEM_Blob|MEM_Static|MEM_Term; } -} #endif + break; +} - -/* -** Change the value of the opcode, or P1, P2, P3, or P5 operands -** for a specific instruction. +/* Opcode: BeginSubrtn * P2 * * * +** Synopsis: r[P2]=NULL +** +** Mark the beginning of a subroutine that can be entered in-line +** or that can be called using OP_Gosub. The subroutine should +** be terminated by an OP_Return instruction that has a P1 operand that +** is the same as the P2 operand to this opcode and that has P3 set to 1. +** If the subroutine is entered in-line, then the OP_Return will simply +** fall through. But if the subroutine is entered using OP_Gosub, then +** the OP_Return will jump back to the first instruction after the OP_Gosub. +** +** This routine works by loading a NULL into the P2 register. When the +** return address register contains a NULL, the OP_Return instruction is +** a no-op that simply falls through to the next instruction (assuming that +** the OP_Return opcode has a P3 value of 1). Thus if the subroutine is +** entered in-line, then the OP_Return will cause in-line execution to +** continue. But if the subroutine is entered via OP_Gosub, then the +** OP_Return will cause a return to the address following the OP_Gosub. +** +** This opcode is identical to OP_Null. It has a different name +** only to make the byte code easier to read and verify. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeOpcode(Vdbe *p, int addr, u8 iNewOpcode){ - sqlcipher_sqlite3VdbeGetOp(p,addr)->opcode = iNewOpcode; -} -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){ - sqlcipher_sqlite3VdbeGetOp(p,addr)->p1 = val; -} -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){ - sqlcipher_sqlite3VdbeGetOp(p,addr)->p2 = val; -} -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){ - sqlcipher_sqlite3VdbeGetOp(p,addr)->p3 = val; -} -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP5(Vdbe *p, u16 p5){ - assert( p->nOp>0 || p->db->mallocFailed ); - if( p->nOp>0 ) p->aOp[p->nOp-1].p5 = p5; +/* Opcode: Null P1 P2 P3 * * +** Synopsis: r[P2..P3]=NULL +** +** Write a NULL into registers P2. If P3 greater than P2, then also write +** NULL into register P3 and every register in between P2 and P3. If P3 +** is less than P2 (typically P3 is zero) then only register P2 is +** set to NULL. +** +** If the P1 value is non-zero, then also set the MEM_Cleared flag so that +** NULL values will not compare equal even if SQLITE_NULLEQ is set on +** OP_Ne or OP_Eq. +*/ +case OP_BeginSubrtn: +case OP_Null: { /* out2 */ + int cnt; + u16 nullFlag; + pOut = out2Prerelease(p, pOp); + cnt = pOp->p3-pOp->p2; + assert( pOp->p3<=(p->nMem+1 - p->nCursor) ); + pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null; + pOut->n = 0; +#ifdef SQLITE_DEBUG + pOut->uTemp = 0; +#endif + while( cnt>0 ){ + pOut++; + memAboutToChange(p, pOut); + sqlcipher_sqlite3VdbeMemSetNull(pOut); + pOut->flags = nullFlag; + pOut->n = 0; + cnt--; + } + break; } -/* -** Change the P2 operand of instruction addr so that it points to -** the address of the next instruction to be coded. +/* Opcode: SoftNull P1 * * * * +** Synopsis: r[P1]=NULL +** +** Set register P1 to have the value NULL as seen by the OP_MakeRecord +** instruction, but do not free any string or blob memory associated with +** the register, so that if the value was a string or blob that was +** previously copied using OP_SCopy, the copies will continue to be valid. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeJumpHere(Vdbe *p, int addr){ - sqlcipher_sqlite3VdbeChangeP2(p, addr, p->nOp); +case OP_SoftNull: { + assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); + pOut = &aMem[pOp->p1]; + pOut->flags = (pOut->flags&~(MEM_Undefined|MEM_AffMask))|MEM_Null; + break; } -/* -** Change the P2 operand of the jump instruction at addr so that -** the jump lands on the next opcode. Or if the jump instruction was -** the previous opcode (and is thus a no-op) then simply back up -** the next instruction counter by one slot so that the jump is -** overwritten by the next inserted opcode. -** -** This routine is an optimization of sqlcipher_sqlite3VdbeJumpHere() that -** strives to omit useless byte-code like this: +/* Opcode: Blob P1 P2 * P4 * +** Synopsis: r[P2]=P4 (len=P1) ** -** 7 Once 0 8 0 -** 8 ... +** P4 points to a blob of data P1 bytes long. Store this +** blob in register P2. If P4 is a NULL pointer, then construct +** a zero-filled blob that is P1 bytes long in P2. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){ - if( addr==p->nOp-1 ){ - assert( p->aOp[addr].opcode==OP_Once - || p->aOp[addr].opcode==OP_If - || p->aOp[addr].opcode==OP_FkIfZero ); - assert( p->aOp[addr].p4type==0 ); -#ifdef SQLITE_VDBE_COVERAGE - sqlcipher_sqlite3VdbeGetOp(p,-1)->iSrcLine = 0; /* Erase VdbeCoverage() macros */ -#endif - p->nOp--; +case OP_Blob: { /* out2 */ + assert( pOp->p1 <= SQLITE_MAX_LENGTH ); + pOut = out2Prerelease(p, pOp); + if( pOp->p4.z==0 ){ + sqlcipher_sqlite3VdbeMemSetZeroBlob(pOut, pOp->p1); + if( sqlcipher_sqlite3VdbeMemExpandBlob(pOut) ) goto no_mem; }else{ - sqlcipher_sqlite3VdbeChangeP2(p, addr, p->nOp); + sqlcipher_sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); } + pOut->enc = encoding; + UPDATE_MAX_BLOBSIZE(pOut); + break; } - -/* -** If the input FuncDef structure is ephemeral, then free it. If -** the FuncDef is not ephermal, then do nothing. +/* Opcode: Variable P1 P2 * P4 * +** Synopsis: r[P2]=parameter(P1,P4) +** +** Transfer the values of bound parameter P1 into register P2 +** +** If the parameter is named, then its name appears in P4. +** The P4 value is used by sqlcipher_sqlite3_bind_parameter_name(). */ -static void freeEphemeralFunction(sqlcipher_sqlite3 *db, FuncDef *pDef){ - if( (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){ - sqlcipher_sqlite3DbFreeNN(db, pDef); +case OP_Variable: { /* out2 */ + Mem *pVar; /* Value being transferred */ + + assert( pOp->p1>0 && pOp->p1<=p->nVar ); + assert( pOp->p4.z==0 || pOp->p4.z==sqlcipher_sqlite3VListNumToName(p->pVList,pOp->p1) ); + pVar = &p->aVar[pOp->p1 - 1]; + if( sqlcipher_sqlite3VdbeMemTooBig(pVar) ){ + goto too_big; } + pOut = &aMem[pOp->p2]; + if( VdbeMemDynamic(pOut) ) sqlcipher_sqlite3VdbeMemSetNull(pOut); + memcpy(pOut, pVar, MEMCELLSIZE); + pOut->flags &= ~(MEM_Dyn|MEM_Ephem); + pOut->flags |= MEM_Static|MEM_FromBind; + UPDATE_MAX_BLOBSIZE(pOut); + break; } -/* -** Delete a P4 value if necessary. +/* Opcode: Move P1 P2 P3 * * +** Synopsis: r[P2@P3]=r[P1@P3] +** +** Move the P3 values in register P1..P1+P3-1 over into +** registers P2..P2+P3-1. Registers P1..P1+P3-1 are +** left holding a NULL. It is an error for register ranges +** P1..P1+P3-1 and P2..P2+P3-1 to overlap. It is an error +** for P3 to be less than 1. */ -static SQLITE_NOINLINE void freeP4Mem(sqlcipher_sqlite3 *db, Mem *p){ - if( p->szMalloc ) sqlcipher_sqlite3DbFree(db, p->zMalloc); - sqlcipher_sqlite3DbFreeNN(db, p); -} -static SQLITE_NOINLINE void freeP4FuncCtx(sqlcipher_sqlite3 *db, sqlcipher_sqlite3_context *p){ - freeEphemeralFunction(db, p->pFunc); - sqlcipher_sqlite3DbFreeNN(db, p); -} -static void freeP4(sqlcipher_sqlite3 *db, int p4type, void *p4){ - assert( db ); - switch( p4type ){ - case P4_FUNCCTX: { - freeP4FuncCtx(db, (sqlcipher_sqlite3_context*)p4); - break; - } - case P4_REAL: - case P4_INT64: - case P4_DYNAMIC: - case P4_DYNBLOB: - case P4_INTARRAY: { - sqlcipher_sqlite3DbFree(db, p4); - break; - } - case P4_KEYINFO: { - if( db->pnBytesFreed==0 ) sqlcipher_sqlite3KeyInfoUnref((KeyInfo*)p4); - break; - } -#ifdef SQLITE_ENABLE_CURSOR_HINTS - case P4_EXPR: { - sqlcipher_sqlite3ExprDelete(db, (Expr*)p4); - break; - } -#endif - case P4_FUNCDEF: { - freeEphemeralFunction(db, (FuncDef*)p4); - break; - } - case P4_MEM: { - if( db->pnBytesFreed==0 ){ - sqlcipher_sqlite3ValueFree((sqlcipher_sqlite3_value*)p4); - }else{ - freeP4Mem(db, (Mem*)p4); +case OP_Move: { + int n; /* Number of registers left to copy */ + int p1; /* Register to copy from */ + int p2; /* Register to copy to */ + + n = pOp->p3; + p1 = pOp->p1; + p2 = pOp->p2; + assert( n>0 && p1>0 && p2>0 ); + assert( p1+n<=p2 || p2+n<=p1 ); + + pIn1 = &aMem[p1]; + pOut = &aMem[p2]; + do{ + assert( pOut<=&aMem[(p->nMem+1 - p->nCursor)] ); + assert( pIn1<=&aMem[(p->nMem+1 - p->nCursor)] ); + assert( memIsValid(pIn1) ); + memAboutToChange(p, pOut); + sqlcipher_sqlite3VdbeMemMove(pOut, pIn1); +#ifdef SQLITE_DEBUG + pIn1->pScopyFrom = 0; + { int i; + for(i=1; inMem; i++){ + if( aMem[i].pScopyFrom==pIn1 ){ + aMem[i].pScopyFrom = pOut; + } } - break; - } - case P4_VTAB : { - if( db->pnBytesFreed==0 ) sqlcipher_sqlite3VtabUnlock((VTable *)p4); - break; } - } +#endif + Deephemeralize(pOut); + REGISTER_TRACE(p2++, pOut); + pIn1++; + pOut++; + }while( --n ); + break; } -/* -** Free the space allocated for aOp and any p4 values allocated for the -** opcodes contained within. If aOp is not NULL it is assumed to contain -** nOp entries. +/* Opcode: Copy P1 P2 P3 * P5 +** Synopsis: r[P2@P3+1]=r[P1@P3+1] +** +** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. +** +** If the 0x0002 bit of P5 is set then also clear the MEM_Subtype flag in the +** destination. The 0x0001 bit of P5 indicates that this Copy opcode cannot +** be merged. The 0x0001 bit is used by the query planner and does not +** come into play during query execution. +** +** This instruction makes a deep copy of the value. A duplicate +** is made of any string or blob constant. See also OP_SCopy. */ -static void vdbeFreeOpArray(sqlcipher_sqlite3 *db, Op *aOp, int nOp){ - if( aOp ){ - Op *pOp; - for(pOp=&aOp[nOp-1]; pOp>=aOp; pOp--){ - if( pOp->p4type <= P4_FREE_IF_LE ) freeP4(db, pOp->p4type, pOp->p4.p); -#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - sqlcipher_sqlite3DbFree(db, pOp->zComment); -#endif +case OP_Copy: { + int n; + + n = pOp->p3; + pIn1 = &aMem[pOp->p1]; + pOut = &aMem[pOp->p2]; + assert( pOut!=pIn1 ); + while( 1 ){ + memAboutToChange(p, pOut); + sqlcipher_sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); + Deephemeralize(pOut); + if( (pOut->flags & MEM_Subtype)!=0 && (pOp->p5 & 0x0002)!=0 ){ + pOut->flags &= ~MEM_Subtype; } - sqlcipher_sqlite3DbFreeNN(db, aOp); +#ifdef SQLITE_DEBUG + pOut->pScopyFrom = 0; +#endif + REGISTER_TRACE(pOp->p2+pOp->p3-n, pOut); + if( (n--)==0 ) break; + pOut++; + pIn1++; } + break; } -/* -** Link the SubProgram object passed as the second argument into the linked -** list at Vdbe.pSubProgram. This list is used to delete all sub-program -** objects when the VM is no longer required. +/* Opcode: SCopy P1 P2 * * * +** Synopsis: r[P2]=r[P1] +** +** Make a shallow copy of register P1 into register P2. +** +** This instruction makes a shallow copy of the value. If the value +** is a string or blob, then the copy is only a pointer to the +** original and hence if the original changes so will the copy. +** Worse, if the original is deallocated, the copy becomes invalid. +** Thus the program must guarantee that the original will not change +** during the lifetime of the copy. Use OP_Copy to make a complete +** copy. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeLinkSubProgram(Vdbe *pVdbe, SubProgram *p){ - p->pNext = pVdbe->pProgram; - pVdbe->pProgram = p; +case OP_SCopy: { /* out2 */ + pIn1 = &aMem[pOp->p1]; + pOut = &aMem[pOp->p2]; + assert( pOut!=pIn1 ); + sqlcipher_sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); +#ifdef SQLITE_DEBUG + pOut->pScopyFrom = pIn1; + pOut->mScopyFlags = pIn1->flags; +#endif + break; } -/* -** Return true if the given Vdbe has any SubPrograms. +/* Opcode: IntCopy P1 P2 * * * +** Synopsis: r[P2]=r[P1] +** +** Transfer the integer value held in register P1 into register P2. +** +** This is an optimized version of SCopy that works only for integer +** values. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeHasSubProgram(Vdbe *pVdbe){ - return pVdbe->pProgram!=0; +case OP_IntCopy: { /* out2 */ + pIn1 = &aMem[pOp->p1]; + assert( (pIn1->flags & MEM_Int)!=0 ); + pOut = &aMem[pOp->p2]; + sqlcipher_sqlite3VdbeMemSetInt64(pOut, pIn1->u.i); + break; } -/* -** Change the opcode at addr into OP_Noop +/* Opcode: FkCheck * * * * * +** +** Halt with an SQLITE_CONSTRAINT error if there are any unresolved +** foreign key constraint violations. If there are no foreign key +** constraint violations, this is a no-op. +** +** FK constraint violations are also checked when the prepared statement +** exits. This opcode is used to raise foreign key constraint errors prior +** to returning results such as a row change count or the result of a +** RETURNING clause. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeChangeToNoop(Vdbe *p, int addr){ - VdbeOp *pOp; - if( p->db->mallocFailed ) return 0; - assert( addr>=0 && addrnOp ); - pOp = &p->aOp[addr]; - freeP4(p->db, pOp->p4type, pOp->p4.p); - pOp->p4type = P4_NOTUSED; - pOp->p4.z = 0; - pOp->opcode = OP_Noop; - return 1; +case OP_FkCheck: { + if( (rc = sqlcipher_sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){ + goto abort_due_to_error; + } + break; } -/* -** If the last opcode is "op" and it is not a jump destination, -** then remove it. Return true if and only if an opcode was removed. +/* Opcode: ResultRow P1 P2 * * * +** Synopsis: output=r[P1@P2] +** +** The registers P1 through P1+P2-1 contain a single row of +** results. This opcode causes the sqlcipher_sqlite3_step() call to terminate +** with an SQLITE_ROW return code and it sets up the sqlcipher_sqlite3_stmt +** structure to provide access to the r(P1)..r(P1+P2-1) values as +** the result row. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){ - if( p->nOp>0 && p->aOp[p->nOp-1].opcode==op ){ - return sqlcipher_sqlite3VdbeChangeToNoop(p, p->nOp-1); - }else{ - return 0; - } -} +case OP_ResultRow: { + assert( p->nResColumn==pOp->p2 ); + assert( pOp->p1>0 || CORRUPT_DB ); + assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); + p->cacheCtr = (p->cacheCtr + 2)|1; + p->pResultSet = &aMem[pOp->p1]; #ifdef SQLITE_DEBUG -/* -** Generate an OP_ReleaseReg opcode to indicate that a range of -** registers, except any identified by mask, are no longer in use. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeReleaseRegisters( - Parse *pParse, /* Parsing context */ - int iFirst, /* Index of first register to be released */ - int N, /* Number of registers to release */ - u32 mask, /* Mask of registers to NOT release */ - int bUndefine /* If true, mark registers as undefined */ -){ - if( N==0 ) return; - assert( pParse->pVdbe ); - assert( iFirst>=1 ); - assert( iFirst+N-1<=pParse->nMem ); - if( N<=31 && mask!=0 ){ - while( N>0 && (mask&1)!=0 ){ - mask >>= 1; - iFirst++; - N--; - } - while( N>0 && N<=32 && (mask & MASKBIT32(N-1))!=0 ){ - mask &= ~MASKBIT32(N-1); - N--; + { + Mem *pMem = p->pResultSet; + int i; + for(i=0; ip2; i++){ + assert( memIsValid(&pMem[i]) ); + REGISTER_TRACE(pOp->p1+i, &pMem[i]); + /* The registers in the result will not be used again when the + ** prepared statement restarts. This is because sqlcipher_sqlite3_column() + ** APIs might have caused type conversions of made other changes to + ** the register values. Therefore, we can go ahead and break any + ** OP_SCopy dependencies. */ + pMem[i].pScopyFrom = 0; } } - if( N>0 ){ - sqlcipher_sqlite3VdbeAddOp3(pParse->pVdbe, OP_ReleaseReg, iFirst, N, *(int*)&mask); - if( bUndefine ) sqlcipher_sqlite3VdbeChangeP5(pParse->pVdbe, 1); +#endif + if( db->mallocFailed ) goto no_mem; + if( db->mTrace & SQLITE_TRACE_ROW ){ + db->trace.xV2(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); } + p->pc = (int)(pOp - aOp) + 1; + rc = SQLITE_ROW; + goto vdbe_return; } -#endif /* SQLITE_DEBUG */ - -/* -** Change the value of the P4 operand for a specific instruction. -** This routine is useful when a large program is loaded from a -** static array using sqlcipher_sqlite3VdbeAddOpList but we want to make a -** few minor changes to the program. +/* Opcode: Concat P1 P2 P3 * * +** Synopsis: r[P3]=r[P2]+r[P1] ** -** If n>=0 then the P4 operand is dynamic, meaning that a copy of -** the string is made into memory obtained from sqlcipher_sqlite3_malloc(). -** A value of n==0 means copy bytes of zP4 up to and including the -** first null byte. If n>0 then copy n+1 bytes of zP4. +** Add the text in register P1 onto the end of the text in +** register P2 and store the result in register P3. +** If either the P1 or P2 text are NULL then store NULL in P3. ** -** Other values of n (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points -** to a string or structure that is guaranteed to exist for the lifetime of -** the Vdbe. In these cases we can just copy the pointer. +** P3 = P2 || P1 ** -** If addr<0 then change P4 on the most recently inserted instruction. +** It is illegal for P1 and P3 to be the same register. Sometimes, +** if P3 is the same register as P2, the implementation is able +** to avoid a memcpy(). */ -static void SQLITE_NOINLINE vdbeChangeP4Full( - Vdbe *p, - Op *pOp, - const char *zP4, - int n -){ - if( pOp->p4type ){ - freeP4(p->db, pOp->p4type, pOp->p4.p); - pOp->p4type = 0; - pOp->p4.p = 0; +case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ + i64 nByte; /* Total size of the output string or blob */ + u16 flags1; /* Initial flags for P1 */ + u16 flags2; /* Initial flags for P2 */ + + pIn1 = &aMem[pOp->p1]; + pIn2 = &aMem[pOp->p2]; + pOut = &aMem[pOp->p3]; + testcase( pOut==pIn2 ); + assert( pIn1!=pOut ); + flags1 = pIn1->flags; + testcase( flags1 & MEM_Null ); + testcase( pIn2->flags & MEM_Null ); + if( (flags1 | pIn2->flags) & MEM_Null ){ + sqlcipher_sqlite3VdbeMemSetNull(pOut); + break; } - if( n<0 ){ - sqlcipher_sqlite3VdbeChangeP4(p, (int)(pOp - p->aOp), zP4, n); - }else{ - if( n==0 ) n = sqlcipher_sqlite3Strlen30(zP4); - pOp->p4.z = sqlcipher_sqlite3DbStrNDup(p->db, zP4, n); - pOp->p4type = P4_DYNAMIC; + if( (flags1 & (MEM_Str|MEM_Blob))==0 ){ + if( sqlcipher_sqlite3VdbeMemStringify(pIn1,encoding,0) ) goto no_mem; + flags1 = pIn1->flags & ~MEM_Str; + }else if( (flags1 & MEM_Zero)!=0 ){ + if( sqlcipher_sqlite3VdbeMemExpandBlob(pIn1) ) goto no_mem; + flags1 = pIn1->flags & ~MEM_Str; } -} -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){ - Op *pOp; - sqlcipher_sqlite3 *db; - assert( p!=0 ); - db = p->db; - assert( p->magic==VDBE_MAGIC_INIT ); - assert( p->aOp!=0 || db->mallocFailed ); - if( db->mallocFailed ){ - if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4); - return; + flags2 = pIn2->flags; + if( (flags2 & (MEM_Str|MEM_Blob))==0 ){ + if( sqlcipher_sqlite3VdbeMemStringify(pIn2,encoding,0) ) goto no_mem; + flags2 = pIn2->flags & ~MEM_Str; + }else if( (flags2 & MEM_Zero)!=0 ){ + if( sqlcipher_sqlite3VdbeMemExpandBlob(pIn2) ) goto no_mem; + flags2 = pIn2->flags & ~MEM_Str; } - assert( p->nOp>0 ); - assert( addrnOp ); - if( addr<0 ){ - addr = p->nOp - 1; + nByte = pIn1->n + pIn2->n; + if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; } - pOp = &p->aOp[addr]; - if( n>=0 || pOp->p4type ){ - vdbeChangeP4Full(p, pOp, zP4, n); - return; + if( sqlcipher_sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){ + goto no_mem; } - if( n==P4_INT32 ){ - /* Note: this cast is safe, because the origin data point was an int - ** that was cast to a (const char *). */ - pOp->p4.i = SQLITE_PTR_TO_INT(zP4); - pOp->p4type = P4_INT32; - }else if( zP4!=0 ){ - assert( n<0 ); - pOp->p4.p = (void*)zP4; - pOp->p4type = (signed char)n; - if( n==P4_VTAB ) sqlcipher_sqlite3VtabLock((VTable*)zP4); + MemSetTypeFlag(pOut, MEM_Str); + if( pOut!=pIn2 ){ + memcpy(pOut->z, pIn2->z, pIn2->n); + assert( (pIn2->flags & MEM_Dyn) == (flags2 & MEM_Dyn) ); + pIn2->flags = flags2; } + memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n); + assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); + pIn1->flags = flags1; + if( encoding>SQLITE_UTF8 ) nByte &= ~1; + pOut->z[nByte]=0; + pOut->z[nByte+1] = 0; + pOut->flags |= MEM_Term; + pOut->n = (int)nByte; + pOut->enc = encoding; + UPDATE_MAX_BLOBSIZE(pOut); + break; } -/* -** Change the P4 operand of the most recently coded instruction -** to the value defined by the arguments. This is a high-speed -** version of sqlcipher_sqlite3VdbeChangeP4(). +/* Opcode: Add P1 P2 P3 * * +** Synopsis: r[P3]=r[P1]+r[P2] ** -** The P4 operand must not have been previously defined. And the new -** P4 must not be P4_INT32. Use sqlcipher_sqlite3VdbeChangeP4() in either of -** those cases. +** Add the value in register P1 to the value in register P2 +** and store the result in register P3. +** If either input is NULL, the result is NULL. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeAppendP4(Vdbe *p, void *pP4, int n){ - VdbeOp *pOp; - assert( n!=P4_INT32 && n!=P4_VTAB ); - assert( n<=0 ); - if( p->db->mallocFailed ){ - freeP4(p->db, n, pP4); +/* Opcode: Multiply P1 P2 P3 * * +** Synopsis: r[P3]=r[P1]*r[P2] +** +** +** Multiply the value in register P1 by the value in register P2 +** and store the result in register P3. +** If either input is NULL, the result is NULL. +*/ +/* Opcode: Subtract P1 P2 P3 * * +** Synopsis: r[P3]=r[P2]-r[P1] +** +** Subtract the value in register P1 from the value in register P2 +** and store the result in register P3. +** If either input is NULL, the result is NULL. +*/ +/* Opcode: Divide P1 P2 P3 * * +** Synopsis: r[P3]=r[P2]/r[P1] +** +** Divide the value in register P1 by the value in register P2 +** and store the result in register P3 (P3=P2/P1). If the value in +** register P1 is zero, then the result is NULL. If either input is +** NULL, the result is NULL. +*/ +/* Opcode: Remainder P1 P2 P3 * * +** Synopsis: r[P3]=r[P2]%r[P1] +** +** Compute the remainder after integer register P2 is divided by +** register P1 and store the result in register P3. +** If the value in register P1 is zero the result is NULL. +** If either operand is NULL, the result is NULL. +*/ +case OP_Add: /* same as TK_PLUS, in1, in2, out3 */ +case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */ +case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */ +case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */ +case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ + u16 flags; /* Combined MEM_* flags from both inputs */ + u16 type1; /* Numeric type of left operand */ + u16 type2; /* Numeric type of right operand */ + i64 iA; /* Integer value of left operand */ + i64 iB; /* Integer value of right operand */ + double rA; /* Real value of left operand */ + double rB; /* Real value of right operand */ + + pIn1 = &aMem[pOp->p1]; + type1 = numericType(pIn1); + pIn2 = &aMem[pOp->p2]; + type2 = numericType(pIn2); + pOut = &aMem[pOp->p3]; + flags = pIn1->flags | pIn2->flags; + if( (type1 & type2 & MEM_Int)!=0 ){ + iA = pIn1->u.i; + iB = pIn2->u.i; + switch( pOp->opcode ){ + case OP_Add: if( sqlcipher_sqlite3AddInt64(&iB,iA) ) goto fp_math; break; + case OP_Subtract: if( sqlcipher_sqlite3SubInt64(&iB,iA) ) goto fp_math; break; + case OP_Multiply: if( sqlcipher_sqlite3MulInt64(&iB,iA) ) goto fp_math; break; + case OP_Divide: { + if( iA==0 ) goto arithmetic_result_is_null; + if( iA==-1 && iB==SMALLEST_INT64 ) goto fp_math; + iB /= iA; + break; + } + default: { + if( iA==0 ) goto arithmetic_result_is_null; + if( iA==-1 ) iA = 1; + iB %= iA; + break; + } + } + pOut->u.i = iB; + MemSetTypeFlag(pOut, MEM_Int); + }else if( (flags & MEM_Null)!=0 ){ + goto arithmetic_result_is_null; }else{ - assert( pP4!=0 ); - assert( p->nOp>0 ); - pOp = &p->aOp[p->nOp-1]; - assert( pOp->p4type==P4_NOTUSED ); - pOp->p4type = n; - pOp->p4.p = pP4; +fp_math: + rA = sqlcipher_sqlite3VdbeRealValue(pIn1); + rB = sqlcipher_sqlite3VdbeRealValue(pIn2); + switch( pOp->opcode ){ + case OP_Add: rB += rA; break; + case OP_Subtract: rB -= rA; break; + case OP_Multiply: rB *= rA; break; + case OP_Divide: { + /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ + if( rA==(double)0 ) goto arithmetic_result_is_null; + rB /= rA; + break; + } + default: { + iA = sqlcipher_sqlite3VdbeIntValue(pIn1); + iB = sqlcipher_sqlite3VdbeIntValue(pIn2); + if( iA==0 ) goto arithmetic_result_is_null; + if( iA==-1 ) iA = 1; + rB = (double)(iB % iA); + break; + } + } +#ifdef SQLITE_OMIT_FLOATING_POINT + pOut->u.i = rB; + MemSetTypeFlag(pOut, MEM_Int); +#else + if( sqlcipher_sqlite3IsNaN(rB) ){ + goto arithmetic_result_is_null; + } + pOut->u.r = rB; + MemSetTypeFlag(pOut, MEM_Real); +#endif } -} + break; -/* -** Set the P4 on the most recently added opcode to the KeyInfo for the -** index given. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){ - Vdbe *v = pParse->pVdbe; - KeyInfo *pKeyInfo; - assert( v!=0 ); - assert( pIdx!=0 ); - pKeyInfo = sqlcipher_sqlite3KeyInfoOfIndex(pParse, pIdx); - if( pKeyInfo ) sqlcipher_sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO); +arithmetic_result_is_null: + sqlcipher_sqlite3VdbeMemSetNull(pOut); + break; } -#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS -/* -** Change the comment on the most recently coded instruction. Or -** insert a No-op and add the comment to that new instruction. This -** makes the code easier to read during debugging. None of this happens -** in a production build. +/* Opcode: CollSeq P1 * * P4 +** +** P4 is a pointer to a CollSeq object. If the next call to a user function +** or aggregate calls sqlcipher_sqlite3GetFuncCollSeq(), this collation sequence will +** be returned. This is used by the built-in min(), max() and nullif() +** functions. +** +** If P1 is not zero, then it is a register that a subsequent min() or +** max() aggregate will set to 1 if the current row is not the minimum or +** maximum. The P1 register is initialized to 0 by this instruction. +** +** The interface used by the implementation of the aforementioned functions +** to retrieve the collation sequence set by this opcode is not available +** publicly. Only built-in functions have access to this feature. */ -static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){ - assert( p->nOp>0 || p->aOp==0 ); - assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed - || p->pParse->nErr>0 ); - if( p->nOp ){ - assert( p->aOp ); - sqlcipher_sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment); - p->aOp[p->nOp-1].zComment = sqlcipher_sqlite3VMPrintf(p->db, zFormat, ap); - } -} -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){ - va_list ap; - if( p ){ - va_start(ap, zFormat); - vdbeVComment(p, zFormat, ap); - va_end(ap); - } -} -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){ - va_list ap; - if( p ){ - sqlcipher_sqlite3VdbeAddOp0(p, OP_Noop); - va_start(ap, zFormat); - vdbeVComment(p, zFormat, ap); - va_end(ap); +case OP_CollSeq: { + assert( pOp->p4type==P4_COLLSEQ ); + if( pOp->p1 ){ + sqlcipher_sqlite3VdbeMemSetInt64(&aMem[pOp->p1], 0); } + break; } -#endif /* NDEBUG */ - -#ifdef SQLITE_VDBE_COVERAGE -/* -** Set the value if the iSrcLine field for the previously coded instruction. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetLineNumber(Vdbe *v, int iLine){ - sqlcipher_sqlite3VdbeGetOp(v,-1)->iSrcLine = iLine; -} -#endif /* SQLITE_VDBE_COVERAGE */ -/* -** Return the opcode for a given address. If the address is -1, then -** return the most recently inserted opcode. +/* Opcode: BitAnd P1 P2 P3 * * +** Synopsis: r[P3]=r[P1]&r[P2] ** -** If a memory allocation error has occurred prior to the calling of this -** routine, then a pointer to a dummy VdbeOp will be returned. That opcode -** is readable but not writable, though it is cast to a writable value. -** The return of a dummy opcode allows the call to continue functioning -** after an OOM fault without having to check to see if the return from -** this routine is a valid pointer. But because the dummy.opcode is 0, -** dummy will never be written to. This is verified by code inspection and -** by running with Valgrind. +** Take the bit-wise AND of the values in register P1 and P2 and +** store the result in register P3. +** If either input is NULL, the result is NULL. */ -SQLITE_PRIVATE VdbeOp *sqlcipher_sqlite3VdbeGetOp(Vdbe *p, int addr){ - /* C89 specifies that the constant "dummy" will be initialized to all - ** zeros, which is correct. MSVC generates a warning, nevertheless. */ - static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */ - assert( p->magic==VDBE_MAGIC_INIT ); - if( addr<0 ){ - addr = p->nOp - 1; - } - assert( (addr>=0 && addrnOp) || p->db->mallocFailed ); - if( p->db->mallocFailed ){ - return (VdbeOp*)&dummy; - }else{ - return &p->aOp[addr]; - } -} - -#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) -/* -** Return an integer value for one of the parameters to the opcode pOp -** determined by character c. +/* Opcode: BitOr P1 P2 P3 * * +** Synopsis: r[P3]=r[P1]|r[P2] +** +** Take the bit-wise OR of the values in register P1 and P2 and +** store the result in register P3. +** If either input is NULL, the result is NULL. */ -static int translateP(char c, const Op *pOp){ - if( c=='1' ) return pOp->p1; - if( c=='2' ) return pOp->p2; - if( c=='3' ) return pOp->p3; - if( c=='4' ) return pOp->p4.i; - return pOp->p5; -} - -/* -** Compute a string for the "comment" field of a VDBE opcode listing. +/* Opcode: ShiftLeft P1 P2 P3 * * +** Synopsis: r[P3]=r[P2]<>r[P1] ** -** "PX" -> "r[X]" -** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1 -** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0 -** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x +** Shift the integer value in register P2 to the right by the +** number of bits specified by the integer in register P1. +** Store the result in register P3. +** If either input is NULL, the result is NULL. */ -SQLITE_PRIVATE char *sqlcipher_sqlite3VdbeDisplayComment( - sqlcipher_sqlite3 *db, /* Optional - Oom error reporting only */ - const Op *pOp, /* The opcode to be commented */ - const char *zP4 /* Previously obtained value for P4 */ -){ - const char *zOpName; - const char *zSynopsis; - int nOpName; - int ii; - char zAlt[50]; - StrAccum x; +case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */ +case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */ +case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */ +case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ + i64 iA; + u64 uA; + i64 iB; + u8 op; - sqlcipher_sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH); - zOpName = sqlcipher_sqlite3OpcodeName(pOp->opcode); - nOpName = sqlcipher_sqlite3Strlen30(zOpName); - if( zOpName[nOpName+1] ){ - int seenCom = 0; - char c; - zSynopsis = zOpName += nOpName + 1; - if( strncmp(zSynopsis,"IF ",3)==0 ){ - if( pOp->p5 & SQLITE_STOREP2 ){ - sqlcipher_sqlite3_snprintf(sizeof(zAlt), zAlt, "r[P2] = (%s)", zSynopsis+3); - }else{ - sqlcipher_sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3); - } - zSynopsis = zAlt; + pIn1 = &aMem[pOp->p1]; + pIn2 = &aMem[pOp->p2]; + pOut = &aMem[pOp->p3]; + if( (pIn1->flags | pIn2->flags) & MEM_Null ){ + sqlcipher_sqlite3VdbeMemSetNull(pOut); + break; + } + iA = sqlcipher_sqlite3VdbeIntValue(pIn2); + iB = sqlcipher_sqlite3VdbeIntValue(pIn1); + op = pOp->opcode; + if( op==OP_BitAnd ){ + iA &= iB; + }else if( op==OP_BitOr ){ + iA |= iB; + }else if( iB!=0 ){ + assert( op==OP_ShiftRight || op==OP_ShiftLeft ); + + /* If shifting by a negative amount, shift in the other direction */ + if( iB<0 ){ + assert( OP_ShiftRight==OP_ShiftLeft+1 ); + op = 2*OP_ShiftLeft + 1 - op; + iB = iB>(-64) ? -iB : 64; } - for(ii=0; (c = zSynopsis[ii])!=0; ii++){ - if( c=='P' ){ - c = zSynopsis[++ii]; - if( c=='4' ){ - sqlcipher_sqlite3_str_appendall(&x, zP4); - }else if( c=='X' ){ - sqlcipher_sqlite3_str_appendall(&x, pOp->zComment); - seenCom = 1; - }else{ - int v1 = translateP(c, pOp); - int v2; - if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){ - ii += 3; - v2 = translateP(zSynopsis[ii], pOp); - if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){ - ii += 2; - v2++; - } - if( v2<2 ){ - sqlcipher_sqlite3_str_appendf(&x, "%d", v1); - }else{ - sqlcipher_sqlite3_str_appendf(&x, "%d..%d", v1, v1+v2-1); - } - }else if( strncmp(zSynopsis+ii+1, "@NP", 3)==0 ){ - sqlcipher_sqlite3_context *pCtx = pOp->p4.pCtx; - if( pOp->p4type!=P4_FUNCCTX || pCtx->argc==1 ){ - sqlcipher_sqlite3_str_appendf(&x, "%d", v1); - }else if( pCtx->argc>1 ){ - sqlcipher_sqlite3_str_appendf(&x, "%d..%d", v1, v1+pCtx->argc-1); - }else if( x.accError==0 ){ - assert( x.nChar>2 ); - x.nChar -= 2; - ii++; - } - ii += 3; - }else{ - sqlcipher_sqlite3_str_appendf(&x, "%d", v1); - if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){ - ii += 4; - } - } - } + + if( iB>=64 ){ + iA = (iA>=0 || op==OP_ShiftLeft) ? 0 : -1; + }else{ + memcpy(&uA, &iA, sizeof(uA)); + if( op==OP_ShiftLeft ){ + uA <<= iB; }else{ - sqlcipher_sqlite3_str_appendchar(&x, 1, c); + uA >>= iB; + /* Sign-extend on a right shift of a negative number */ + if( iA<0 ) uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-iB); } + memcpy(&iA, &uA, sizeof(iA)); } - if( !seenCom && pOp->zComment ){ - sqlcipher_sqlite3_str_appendf(&x, "; %s", pOp->zComment); - } - }else if( pOp->zComment ){ - sqlcipher_sqlite3_str_appendall(&x, pOp->zComment); - } - if( (x.accError & SQLITE_NOMEM)!=0 && db!=0 ){ - sqlcipher_sqlite3OomFault(db); } - return sqlcipher_sqlite3StrAccumFinish(&x); + pOut->u.i = iA; + MemSetTypeFlag(pOut, MEM_Int); + break; } -#endif /* SQLITE_ENABLE_EXPLAIN_COMMENTS */ -#if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) -/* -** Translate the P4.pExpr value for an OP_CursorHint opcode into text -** that can be displayed in the P4 column of EXPLAIN output. +/* Opcode: AddImm P1 P2 * * * +** Synopsis: r[P1]=r[P1]+P2 +** +** Add the constant P2 to the value in register P1. +** The result is always an integer. +** +** To force any register to be an integer, just add 0. */ -static void displayP4Expr(StrAccum *p, Expr *pExpr){ - const char *zOp = 0; - switch( pExpr->op ){ - case TK_STRING: - sqlcipher_sqlite3_str_appendf(p, "%Q", pExpr->u.zToken); - break; - case TK_INTEGER: - sqlcipher_sqlite3_str_appendf(p, "%d", pExpr->u.iValue); - break; - case TK_NULL: - sqlcipher_sqlite3_str_appendf(p, "NULL"); - break; - case TK_REGISTER: { - sqlcipher_sqlite3_str_appendf(p, "r[%d]", pExpr->iTable); - break; - } - case TK_COLUMN: { - if( pExpr->iColumn<0 ){ - sqlcipher_sqlite3_str_appendf(p, "rowid"); +case OP_AddImm: { /* in1 */ + pIn1 = &aMem[pOp->p1]; + memAboutToChange(p, pIn1); + sqlcipher_sqlite3VdbeMemIntegerify(pIn1); + pIn1->u.i += pOp->p2; + break; +} + +/* Opcode: MustBeInt P1 P2 * * * +** +** Force the value in register P1 to be an integer. If the value +** in P1 is not an integer and cannot be converted into an integer +** without data loss, then jump immediately to P2, or if P2==0 +** raise an SQLITE_MISMATCH exception. +*/ +case OP_MustBeInt: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; + if( (pIn1->flags & MEM_Int)==0 ){ + applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); + if( (pIn1->flags & MEM_Int)==0 ){ + VdbeBranchTaken(1, 2); + if( pOp->p2==0 ){ + rc = SQLITE_MISMATCH; + goto abort_due_to_error; }else{ - sqlcipher_sqlite3_str_appendf(p, "c%d", (int)pExpr->iColumn); + goto jump_to_p2; } - break; } - case TK_LT: zOp = "LT"; break; - case TK_LE: zOp = "LE"; break; - case TK_GT: zOp = "GT"; break; - case TK_GE: zOp = "GE"; break; - case TK_NE: zOp = "NE"; break; - case TK_EQ: zOp = "EQ"; break; - case TK_IS: zOp = "IS"; break; - case TK_ISNOT: zOp = "ISNOT"; break; - case TK_AND: zOp = "AND"; break; - case TK_OR: zOp = "OR"; break; - case TK_PLUS: zOp = "ADD"; break; - case TK_STAR: zOp = "MUL"; break; - case TK_MINUS: zOp = "SUB"; break; - case TK_REM: zOp = "REM"; break; - case TK_BITAND: zOp = "BITAND"; break; - case TK_BITOR: zOp = "BITOR"; break; - case TK_SLASH: zOp = "DIV"; break; - case TK_LSHIFT: zOp = "LSHIFT"; break; - case TK_RSHIFT: zOp = "RSHIFT"; break; - case TK_CONCAT: zOp = "CONCAT"; break; - case TK_UMINUS: zOp = "MINUS"; break; - case TK_UPLUS: zOp = "PLUS"; break; - case TK_BITNOT: zOp = "BITNOT"; break; - case TK_NOT: zOp = "NOT"; break; - case TK_ISNULL: zOp = "ISNULL"; break; - case TK_NOTNULL: zOp = "NOTNULL"; break; - - default: - sqlcipher_sqlite3_str_appendf(p, "%s", "expr"); - break; } + VdbeBranchTaken(0, 2); + MemSetTypeFlag(pIn1, MEM_Int); + break; +} - if( zOp ){ - sqlcipher_sqlite3_str_appendf(p, "%s(", zOp); - displayP4Expr(p, pExpr->pLeft); - if( pExpr->pRight ){ - sqlcipher_sqlite3_str_append(p, ",", 1); - displayP4Expr(p, pExpr->pRight); - } - sqlcipher_sqlite3_str_append(p, ")", 1); +#ifndef SQLITE_OMIT_FLOATING_POINT +/* Opcode: RealAffinity P1 * * * * +** +** If register P1 holds an integer convert it to a real value. +** +** This opcode is used when extracting information from a column that +** has REAL affinity. Such column values may still be stored as +** integers, for space efficiency, but after extraction we want them +** to have only a real value. +*/ +case OP_RealAffinity: { /* in1 */ + pIn1 = &aMem[pOp->p1]; + if( pIn1->flags & (MEM_Int|MEM_IntReal) ){ + testcase( pIn1->flags & MEM_Int ); + testcase( pIn1->flags & MEM_IntReal ); + sqlcipher_sqlite3VdbeMemRealify(pIn1); + REGISTER_TRACE(pOp->p1, pIn1); } + break; } -#endif /* VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) */ +#endif +#ifndef SQLITE_OMIT_CAST +/* Opcode: Cast P1 P2 * * * +** Synopsis: affinity(r[P1]) +** +** Force the value in register P1 to be the type defined by P2. +** +**
      +**
    • P2=='A' → BLOB +**
    • P2=='B' → TEXT +**
    • P2=='C' → NUMERIC +**
    • P2=='D' → INTEGER +**
    • P2=='E' → REAL +**
    +** +** A NULL value is not changed by this routine. It remains NULL. +*/ +case OP_Cast: { /* in1 */ + assert( pOp->p2>=SQLITE_AFF_BLOB && pOp->p2<=SQLITE_AFF_REAL ); + testcase( pOp->p2==SQLITE_AFF_TEXT ); + testcase( pOp->p2==SQLITE_AFF_BLOB ); + testcase( pOp->p2==SQLITE_AFF_NUMERIC ); + testcase( pOp->p2==SQLITE_AFF_INTEGER ); + testcase( pOp->p2==SQLITE_AFF_REAL ); + pIn1 = &aMem[pOp->p1]; + memAboutToChange(p, pIn1); + rc = ExpandBlob(pIn1); + if( rc ) goto abort_due_to_error; + rc = sqlcipher_sqlite3VdbeMemCast(pIn1, pOp->p2, encoding); + if( rc ) goto abort_due_to_error; + UPDATE_MAX_BLOBSIZE(pIn1); + REGISTER_TRACE(pOp->p1, pIn1); + break; +} +#endif /* SQLITE_OMIT_CAST */ -#if VDBE_DISPLAY_P4 -/* -** Compute a string that describes the P4 parameter for an opcode. -** Use zTemp for any required temporary buffer space. +/* Opcode: Eq P1 P2 P3 P4 P5 +** Synopsis: IF r[P3]==r[P1] +** +** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then +** jump to address P2. +** +** The SQLITE_AFF_MASK portion of P5 must be an affinity character - +** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made +** to coerce both inputs according to this affinity before the +** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric +** affinity is used. Note that the affinity conversions are stored +** back into the input registers P1 and P3. So this opcode can cause +** persistent changes to registers P1 and P3. +** +** Once any conversions have taken place, and neither value is NULL, +** the values are compared. If both values are blobs then memcmp() is +** used to determine the results of the comparison. If both values +** are text, then the appropriate collating function specified in +** P4 is used to do the comparison. If P4 is not specified then +** memcmp() is used to compare text string. If both values are +** numeric, then a numeric comparison is used. If the two values +** are of different types, then numbers are considered less than +** strings and strings are considered less than blobs. +** +** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either +** true or false and is never NULL. If both operands are NULL then the result +** of comparison is true. If either operand is NULL then the result is false. +** If neither operand is NULL the result is the same as it would be if +** the SQLITE_NULLEQ flag were omitted from P5. +** +** This opcode saves the result of comparison for use by the new +** OP_Jump opcode. */ -SQLITE_PRIVATE char *sqlcipher_sqlite3VdbeDisplayP4(sqlcipher_sqlite3 *db, Op *pOp){ - char *zP4 = 0; - StrAccum x; +/* Opcode: Ne P1 P2 P3 P4 P5 +** Synopsis: IF r[P3]!=r[P1] +** +** This works just like the Eq opcode except that the jump is taken if +** the operands in registers P1 and P3 are not equal. See the Eq opcode for +** additional information. +*/ +/* Opcode: Lt P1 P2 P3 P4 P5 +** Synopsis: IF r[P3]r[P1] +** +** This works just like the Lt opcode except that the jump is taken if +** the content of register P3 is greater than the content of +** register P1. See the Lt opcode for additional information. +*/ +/* Opcode: Ge P1 P2 P3 P4 P5 +** Synopsis: IF r[P3]>=r[P1] +** +** This works just like the Lt opcode except that the jump is taken if +** the content of register P3 is greater than or equal to the content of +** register P1. See the Lt opcode for additional information. +*/ +case OP_Eq: /* same as TK_EQ, jump, in1, in3 */ +case OP_Ne: /* same as TK_NE, jump, in1, in3 */ +case OP_Lt: /* same as TK_LT, jump, in1, in3 */ +case OP_Le: /* same as TK_LE, jump, in1, in3 */ +case OP_Gt: /* same as TK_GT, jump, in1, in3 */ +case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ + int res, res2; /* Result of the comparison of pIn1 against pIn3 */ + char affinity; /* Affinity to use for comparison */ + u16 flags1; /* Copy of initial value of pIn1->flags */ + u16 flags3; /* Copy of initial value of pIn3->flags */ - sqlcipher_sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH); - switch( pOp->p4type ){ - case P4_KEYINFO: { - int j; - KeyInfo *pKeyInfo = pOp->p4.pKeyInfo; - assert( pKeyInfo->aSortFlags!=0 ); - sqlcipher_sqlite3_str_appendf(&x, "k(%d", pKeyInfo->nKeyField); - for(j=0; jnKeyField; j++){ - CollSeq *pColl = pKeyInfo->aColl[j]; - const char *zColl = pColl ? pColl->zName : ""; - if( strcmp(zColl, "BINARY")==0 ) zColl = "B"; - sqlcipher_sqlite3_str_appendf(&x, ",%s%s%s", - (pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_DESC) ? "-" : "", - (pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_BIGNULL)? "N." : "", - zColl); + pIn1 = &aMem[pOp->p1]; + pIn3 = &aMem[pOp->p3]; + flags1 = pIn1->flags; + flags3 = pIn3->flags; + if( (flags1 & flags3 & MEM_Int)!=0 ){ + assert( (pOp->p5 & SQLITE_AFF_MASK)!=SQLITE_AFF_TEXT || CORRUPT_DB ); + /* Common case of comparison of two integers */ + if( pIn3->u.i > pIn1->u.i ){ + if( sqlcipher_sqlite3aGTb[pOp->opcode] ){ + VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); + goto jump_to_p2; } - sqlcipher_sqlite3_str_append(&x, ")", 1); - break; - } -#ifdef SQLITE_ENABLE_CURSOR_HINTS - case P4_EXPR: { - displayP4Expr(&x, pOp->p4.pExpr); - break; - } -#endif - case P4_COLLSEQ: { - static const char *const encnames[] = {"?", "8", "16LE", "16BE"}; - CollSeq *pColl = pOp->p4.pColl; - assert( pColl->enc>=0 && pColl->enc<4 ); - sqlcipher_sqlite3_str_appendf(&x, "%.18s-%s", pColl->zName, - encnames[pColl->enc]); - break; - } - case P4_FUNCDEF: { - FuncDef *pDef = pOp->p4.pFunc; - sqlcipher_sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg); - break; - } - case P4_FUNCCTX: { - FuncDef *pDef = pOp->p4.pCtx->pFunc; - sqlcipher_sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg); - break; - } - case P4_INT64: { - sqlcipher_sqlite3_str_appendf(&x, "%lld", *pOp->p4.pI64); - break; - } - case P4_INT32: { - sqlcipher_sqlite3_str_appendf(&x, "%d", pOp->p4.i); - break; - } - case P4_REAL: { - sqlcipher_sqlite3_str_appendf(&x, "%.16g", *pOp->p4.pReal); - break; + iCompare = +1; + }else if( pIn3->u.i < pIn1->u.i ){ + if( sqlcipher_sqlite3aLTb[pOp->opcode] ){ + VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); + goto jump_to_p2; + } + iCompare = -1; + }else{ + if( sqlcipher_sqlite3aEQb[pOp->opcode] ){ + VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); + goto jump_to_p2; + } + iCompare = 0; } - case P4_MEM: { - Mem *pMem = pOp->p4.pMem; - if( pMem->flags & MEM_Str ){ - zP4 = pMem->z; - }else if( pMem->flags & (MEM_Int|MEM_IntReal) ){ - sqlcipher_sqlite3_str_appendf(&x, "%lld", pMem->u.i); - }else if( pMem->flags & MEM_Real ){ - sqlcipher_sqlite3_str_appendf(&x, "%.16g", pMem->u.r); - }else if( pMem->flags & MEM_Null ){ - zP4 = "NULL"; + VdbeBranchTaken(0, (pOp->p5 & SQLITE_NULLEQ)?2:3); + break; + } + if( (flags1 | flags3)&MEM_Null ){ + /* One or both operands are NULL */ + if( pOp->p5 & SQLITE_NULLEQ ){ + /* If SQLITE_NULLEQ is set (which will only happen if the operator is + ** OP_Eq or OP_Ne) then take the jump or not depending on whether + ** or not both operands are null. + */ + assert( (flags1 & MEM_Cleared)==0 ); + assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 || CORRUPT_DB ); + testcase( (pOp->p5 & SQLITE_JUMPIFNULL)!=0 ); + if( (flags1&flags3&MEM_Null)!=0 + && (flags3&MEM_Cleared)==0 + ){ + res = 0; /* Operands are equal */ }else{ - assert( pMem->flags & MEM_Blob ); - zP4 = "(blob)"; + res = ((flags3 & MEM_Null) ? -1 : +1); /* Operands are not equal */ } - break; - } -#ifndef SQLITE_OMIT_VIRTUALTABLE - case P4_VTAB: { - sqlcipher_sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab; - sqlcipher_sqlite3_str_appendf(&x, "vtab:%p", pVtab); - break; - } -#endif - case P4_INTARRAY: { - u32 i; - u32 *ai = pOp->p4.ai; - u32 n = ai[0]; /* The first element of an INTARRAY is always the - ** count of the number of elements to follow */ - for(i=1; i<=n; i++){ - sqlcipher_sqlite3_str_appendf(&x, "%c%u", (i==1 ? '[' : ','), ai[i]); + }else{ + /* SQLITE_NULLEQ is clear and at least one operand is NULL, + ** then the result is always NULL. + ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. + */ + VdbeBranchTaken(2,3); + if( pOp->p5 & SQLITE_JUMPIFNULL ){ + goto jump_to_p2; } - sqlcipher_sqlite3_str_append(&x, "]", 1); - break; - } - case P4_SUBPROGRAM: { - zP4 = "program"; - break; - } - case P4_DYNBLOB: - case P4_ADVANCE: { - break; - } - case P4_TABLE: { - zP4 = pOp->p4.pTab->zName; + iCompare = 1; /* Operands are not equal */ break; } - default: { - zP4 = pOp->p4.z; + }else{ + /* Neither operand is NULL and we couldn't do the special high-speed + ** integer comparison case. So do a general-case comparison. */ + affinity = pOp->p5 & SQLITE_AFF_MASK; + if( affinity>=SQLITE_AFF_NUMERIC ){ + if( (flags1 | flags3)&MEM_Str ){ + if( (flags1 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ + applyNumericAffinity(pIn1,0); + testcase( flags3==pIn3->flags ); + flags3 = pIn3->flags; + } + if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ + applyNumericAffinity(pIn3,0); + } + } + }else if( affinity==SQLITE_AFF_TEXT ){ + if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ + testcase( pIn1->flags & MEM_Int ); + testcase( pIn1->flags & MEM_Real ); + testcase( pIn1->flags & MEM_IntReal ); + sqlcipher_sqlite3VdbeMemStringify(pIn1, encoding, 1); + testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) ); + flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); + if( pIn1==pIn3 ) flags3 = flags1 | MEM_Str; + } + if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ + testcase( pIn3->flags & MEM_Int ); + testcase( pIn3->flags & MEM_Real ); + testcase( pIn3->flags & MEM_IntReal ); + sqlcipher_sqlite3VdbeMemStringify(pIn3, encoding, 1); + testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn) ); + flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask); + } } + assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); + res = sqlcipher_sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); } - if( zP4 ) sqlcipher_sqlite3_str_appendall(&x, zP4); - if( (x.accError & SQLITE_NOMEM)!=0 ){ - sqlcipher_sqlite3OomFault(db); + + /* At this point, res is negative, zero, or positive if reg[P1] is + ** less than, equal to, or greater than reg[P3], respectively. Compute + ** the answer to this operator in res2, depending on what the comparison + ** operator actually is. The next block of code depends on the fact + ** that the 6 comparison operators are consecutive integers in this + ** order: NE, EQ, GT, LE, LT, GE */ + assert( OP_Eq==OP_Ne+1 ); assert( OP_Gt==OP_Ne+2 ); assert( OP_Le==OP_Ne+3 ); + assert( OP_Lt==OP_Ne+4 ); assert( OP_Ge==OP_Ne+5 ); + if( res<0 ){ + res2 = sqlcipher_sqlite3aLTb[pOp->opcode]; + }else if( res==0 ){ + res2 = sqlcipher_sqlite3aEQb[pOp->opcode]; + }else{ + res2 = sqlcipher_sqlite3aGTb[pOp->opcode]; } - return sqlcipher_sqlite3StrAccumFinish(&x); + iCompare = res; + + /* Undo any changes made by applyAffinity() to the input registers. */ + assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) ); + pIn3->flags = flags3; + assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); + pIn1->flags = flags1; + + VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); + if( res2 ){ + goto jump_to_p2; + } + break; } -#endif /* VDBE_DISPLAY_P4 */ -/* -** Declare to the Vdbe that the BTree object at db->aDb[i] is used. +/* Opcode: ElseEq * P2 * * * ** -** The prepared statements need to know in advance the complete set of -** attached databases that will be use. A mask of these databases -** is maintained in p->btreeMask. The p->lockMask value is the subset of -** p->btreeMask of databases that will require a lock. +** This opcode must follow an OP_Lt or OP_Gt comparison operator. There +** can be zero or more OP_ReleaseReg opcodes intervening, but no other +** opcodes are allowed to occur between this instruction and the previous +** OP_Lt or OP_Gt. +** +** If result of an OP_Eq comparison on the same two operands as the +** prior OP_Lt or OP_Gt would have been true, then jump to P2. +** If the result of an OP_Eq comparison on the two previous +** operands would have been false or NULL, then fall through. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeUsesBtree(Vdbe *p, int i){ - assert( i>=0 && idb->nDb && i<(int)sizeof(yDbMask)*8 ); - assert( i<(int)sizeof(p->btreeMask)*8 ); - DbMaskSet(p->btreeMask, i); - if( i!=1 && sqlcipher_sqlite3BtreeSharable(p->db->aDb[i].pBt) ){ - DbMaskSet(p->lockMask, i); +case OP_ElseEq: { /* same as TK_ESCAPE, jump */ + +#ifdef SQLITE_DEBUG + /* Verify the preconditions of this opcode - that it follows an OP_Lt or + ** OP_Gt with zero or more intervening OP_ReleaseReg opcodes */ + int iAddr; + for(iAddr = (int)(pOp - aOp) - 1; ALWAYS(iAddr>=0); iAddr--){ + if( aOp[iAddr].opcode==OP_ReleaseReg ) continue; + assert( aOp[iAddr].opcode==OP_Lt || aOp[iAddr].opcode==OP_Gt ); + break; } +#endif /* SQLITE_DEBUG */ + VdbeBranchTaken(iCompare==0, 2); + if( iCompare==0 ) goto jump_to_p2; + break; } -#if !defined(SQLITE_OMIT_SHARED_CACHE) -/* -** If SQLite is compiled to support shared-cache mode and to be threadsafe, -** this routine obtains the mutex associated with each BtShared structure -** that may be accessed by the VM passed as an argument. In doing so it also -** sets the BtShared.db member of each of the BtShared structures, ensuring -** that the correct busy-handler callback is invoked if required. + +/* Opcode: Permutation * * * P4 * ** -** If SQLite is not threadsafe but does support shared-cache mode, then -** sqlcipher_sqlite3BtreeEnter() is invoked to set the BtShared.db variables -** of all of BtShared structures accessible via the database handle -** associated with the VM. +** Set the permutation used by the OP_Compare operator in the next +** instruction. The permutation is stored in the P4 operand. ** -** If SQLite is not threadsafe and does not support shared-cache mode, this -** function is a no-op. +** The permutation is only valid for the next opcode which must be +** an OP_Compare that has the OPFLAG_PERMUTE bit set in P5. ** -** The p->btreeMask field is a bitmask of all btrees that the prepared -** statement p will ever use. Let N be the number of bits in p->btreeMask -** corresponding to btrees that use shared cache. Then the runtime of -** this routine is N*N. But as N is rarely more than 1, this should not -** be a problem. +** The first integer in the P4 integer array is the length of the array +** and does not become part of the permutation. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeEnter(Vdbe *p){ - int i; - sqlcipher_sqlite3 *db; - Db *aDb; - int nDb; - if( DbMaskAllZero(p->lockMask) ) return; /* The common case */ - db = p->db; - aDb = db->aDb; - nDb = db->nDb; - for(i=0; ilockMask,i) && ALWAYS(aDb[i].pBt!=0) ){ - sqlcipher_sqlite3BtreeEnter(aDb[i].pBt); - } - } +case OP_Permutation: { + assert( pOp->p4type==P4_INTARRAY ); + assert( pOp->p4.ai ); + assert( pOp[1].opcode==OP_Compare ); + assert( pOp[1].p5 & OPFLAG_PERMUTE ); + break; } -#endif -#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 -/* -** Unlock all of the btrees previously locked by a call to sqlcipher_sqlite3VdbeEnter(). +/* Opcode: Compare P1 P2 P3 P4 P5 +** Synopsis: r[P1@P3] <-> r[P2@P3] +** +** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this +** vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of +** the comparison for use by the next OP_Jump instruct. +** +** If P5 has the OPFLAG_PERMUTE bit set, then the order of comparison is +** determined by the most recent OP_Permutation operator. If the +** OPFLAG_PERMUTE bit is clear, then register are compared in sequential +** order. +** +** P4 is a KeyInfo structure that defines collating sequences and sort +** orders for the comparison. The permutation applies to registers +** only. The KeyInfo elements are used sequentially. +** +** The comparison is a sort comparison, so NULLs compare equal, +** NULLs are less than numbers, numbers are less than strings, +** and strings are less than blobs. +** +** This opcode must be immediately followed by an OP_Jump opcode. */ -static SQLITE_NOINLINE void vdbeLeave(Vdbe *p){ +case OP_Compare: { + int n; int i; - sqlcipher_sqlite3 *db; - Db *aDb; - int nDb; - db = p->db; - aDb = db->aDb; - nDb = db->nDb; - for(i=0; ilockMask,i) && ALWAYS(aDb[i].pBt!=0) ){ - sqlcipher_sqlite3BtreeLeave(aDb[i].pBt); + int p1; + int p2; + const KeyInfo *pKeyInfo; + u32 idx; + CollSeq *pColl; /* Collating sequence to use on this term */ + int bRev; /* True for DESCENDING sort order */ + u32 *aPermute; /* The permutation */ + + if( (pOp->p5 & OPFLAG_PERMUTE)==0 ){ + aPermute = 0; + }else{ + assert( pOp>aOp ); + assert( pOp[-1].opcode==OP_Permutation ); + assert( pOp[-1].p4type==P4_INTARRAY ); + aPermute = pOp[-1].p4.ai + 1; + assert( aPermute!=0 ); + } + n = pOp->p3; + pKeyInfo = pOp->p4.pKeyInfo; + assert( n>0 ); + assert( pKeyInfo!=0 ); + p1 = pOp->p1; + p2 = pOp->p2; +#ifdef SQLITE_DEBUG + if( aPermute ){ + int k, mx = 0; + for(k=0; k(u32)mx ) mx = aPermute[k]; + assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 ); + assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 ); + }else{ + assert( p1>0 && p1+n<=(p->nMem+1 - p->nCursor)+1 ); + assert( p2>0 && p2+n<=(p->nMem+1 - p->nCursor)+1 ); + } +#endif /* SQLITE_DEBUG */ + for(i=0; inKeyField ); + pColl = pKeyInfo->aColl[i]; + bRev = (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC); + iCompare = sqlcipher_sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl); + if( iCompare ){ + if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) + && ((aMem[p1+idx].flags & MEM_Null) || (aMem[p2+idx].flags & MEM_Null)) + ){ + iCompare = -iCompare; + } + if( bRev ) iCompare = -iCompare; + break; } } + assert( pOp[1].opcode==OP_Jump ); + break; } -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeLeave(Vdbe *p){ - if( DbMaskAllZero(p->lockMask) ) return; /* The common case */ - vdbeLeave(p); -} -#endif - -#if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) -/* -** Print a single opcode. This routine is used for debugging only. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){ - char *zP4; - char *zCom; - sqlcipher_sqlite3 dummyDb; - static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n"; - if( pOut==0 ) pOut = stdout; - sqlcipher_sqlite3BeginBenignMalloc(); - dummyDb.mallocFailed = 1; - zP4 = sqlcipher_sqlite3VdbeDisplayP4(&dummyDb, pOp); -#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - zCom = sqlcipher_sqlite3VdbeDisplayComment(0, pOp, zP4); -#else - zCom = 0; -#endif - /* NB: The sqlcipher_sqlite3OpcodeName() function is implemented by code created - ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the - ** information from the vdbe.c source text */ - fprintf(pOut, zFormat1, pc, - sqlcipher_sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, - zP4 ? zP4 : "", pOp->p5, - zCom ? zCom : "" - ); - fflush(pOut); - sqlcipher_sqlite3_free(zP4); - sqlcipher_sqlite3_free(zCom); - sqlcipher_sqlite3EndBenignMalloc(); -} -#endif -/* -** Initialize an array of N Mem element. +/* Opcode: Jump P1 P2 P3 * * +** +** Jump to the instruction at address P1, P2, or P3 depending on whether +** in the most recent OP_Compare instruction the P1 vector was less than +** equal to, or greater than the P2 vector, respectively. +** +** This opcode must immediately follow an OP_Compare opcode. */ -static void initMemArray(Mem *p, int N, sqlcipher_sqlite3 *db, u16 flags){ - while( (N--)>0 ){ - p->db = db; - p->flags = flags; - p->szMalloc = 0; -#ifdef SQLITE_DEBUG - p->pScopyFrom = 0; -#endif - p++; +case OP_Jump: { /* jump */ + assert( pOp>aOp && pOp[-1].opcode==OP_Compare ); + if( iCompare<0 ){ + VdbeBranchTaken(0,4); pOp = &aOp[pOp->p1 - 1]; + }else if( iCompare==0 ){ + VdbeBranchTaken(1,4); pOp = &aOp[pOp->p2 - 1]; + }else{ + VdbeBranchTaken(2,4); pOp = &aOp[pOp->p3 - 1]; } + break; } -/* -** Release an array of N Mem elements +/* Opcode: And P1 P2 P3 * * +** Synopsis: r[P3]=(r[P1] && r[P2]) +** +** Take the logical AND of the values in registers P1 and P2 and +** write the result into register P3. +** +** If either P1 or P2 is 0 (false) then the result is 0 even if +** the other input is NULL. A NULL and true or two NULLs give +** a NULL output. */ -static void releaseMemArray(Mem *p, int N){ - if( p && N ){ - Mem *pEnd = &p[N]; - sqlcipher_sqlite3 *db = p->db; - if( db->pnBytesFreed ){ - do{ - if( p->szMalloc ) sqlcipher_sqlite3DbFree(db, p->zMalloc); - }while( (++p)flags & MEM_Agg ); - testcase( p->flags & MEM_Dyn ); - testcase( p->xDel==sqlcipher_sqlite3VdbeFrameMemDel ); - if( p->flags&(MEM_Agg|MEM_Dyn) ){ - sqlcipher_sqlite3VdbeMemRelease(p); - }else if( p->szMalloc ){ - sqlcipher_sqlite3DbFreeNN(db, p->zMalloc); - p->szMalloc = 0; - } +/* Opcode: Or P1 P2 P3 * * +** Synopsis: r[P3]=(r[P1] || r[P2]) +** +** Take the logical OR of the values in register P1 and P2 and +** store the answer in register P3. +** +** If either P1 or P2 is nonzero (true) then the result is 1 (true) +** even if the other input is NULL. A NULL and false or two NULLs +** give a NULL output. +*/ +case OP_And: /* same as TK_AND, in1, in2, out3 */ +case OP_Or: { /* same as TK_OR, in1, in2, out3 */ + int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ + int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ - p->flags = MEM_Undefined; - }while( (++p)p1], 2); + v2 = sqlcipher_sqlite3VdbeBooleanValue(&aMem[pOp->p2], 2); + if( pOp->opcode==OP_And ){ + static const unsigned char and_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; + v1 = and_logic[v1*3+v2]; + }else{ + static const unsigned char or_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 }; + v1 = or_logic[v1*3+v2]; + } + pOut = &aMem[pOp->p3]; + if( v1==2 ){ + MemSetTypeFlag(pOut, MEM_Null); + }else{ + pOut->u.i = v1; + MemSetTypeFlag(pOut, MEM_Int); } + break; } -#ifdef SQLITE_DEBUG -/* -** Verify that pFrame is a valid VdbeFrame pointer. Return true if it is -** and false if something is wrong. +/* Opcode: IsTrue P1 P2 P3 P4 * +** Synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 ** -** This routine is intended for use inside of assert() statements only. +** This opcode implements the IS TRUE, IS FALSE, IS NOT TRUE, and +** IS NOT FALSE operators. +** +** Interpret the value in register P1 as a boolean value. Store that +** boolean (a 0 or 1) in register P2. Or if the value in register P1 is +** NULL, then the P3 is stored in register P2. Invert the answer if P4 +** is 1. +** +** The logic is summarized like this: +** +**
      +**
    • If P3==0 and P4==0 then r[P2] := r[P1] IS TRUE +**
    • If P3==1 and P4==1 then r[P2] := r[P1] IS FALSE +**
    • If P3==0 and P4==1 then r[P2] := r[P1] IS NOT TRUE +**
    • If P3==1 and P4==0 then r[P2] := r[P1] IS NOT FALSE +**
    */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeFrameIsValid(VdbeFrame *pFrame){ - if( pFrame->iFrameMagic!=SQLITE_FRAME_MAGIC ) return 0; - return 1; +case OP_IsTrue: { /* in1, out2 */ + assert( pOp->p4type==P4_INT32 ); + assert( pOp->p4.i==0 || pOp->p4.i==1 ); + assert( pOp->p3==0 || pOp->p3==1 ); + sqlcipher_sqlite3VdbeMemSetInt64(&aMem[pOp->p2], + sqlcipher_sqlite3VdbeBooleanValue(&aMem[pOp->p1], pOp->p3) ^ pOp->p4.i); + break; } -#endif - -/* -** This is a destructor on a Mem object (which is really an sqlcipher_sqlite3_value) -** that deletes the Frame object that is attached to it as a blob. +/* Opcode: Not P1 P2 * * * +** Synopsis: r[P2]= !r[P1] ** -** This routine does not delete the Frame right away. It merely adds the -** frame to a list of frames to be deleted when the Vdbe halts. +** Interpret the value in register P1 as a boolean value. Store the +** boolean complement in register P2. If the value in register P1 is +** NULL, then a NULL is stored in P2. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeFrameMemDel(void *pArg){ - VdbeFrame *pFrame = (VdbeFrame*)pArg; - assert( sqlcipher_sqlite3VdbeFrameIsValid(pFrame) ); - pFrame->pParent = pFrame->v->pDelFrame; - pFrame->v->pDelFrame = pFrame; +case OP_Not: { /* same as TK_NOT, in1, out2 */ + pIn1 = &aMem[pOp->p1]; + pOut = &aMem[pOp->p2]; + if( (pIn1->flags & MEM_Null)==0 ){ + sqlcipher_sqlite3VdbeMemSetInt64(pOut, !sqlcipher_sqlite3VdbeBooleanValue(pIn1,0)); + }else{ + sqlcipher_sqlite3VdbeMemSetNull(pOut); + } + break; } -#if defined(SQLITE_ENABLE_BYTECODE_VTAB) || !defined(SQLITE_OMIT_EXPLAIN) -/* -** Locate the next opcode to be displayed in EXPLAIN or EXPLAIN -** QUERY PLAN output. +/* Opcode: BitNot P1 P2 * * * +** Synopsis: r[P2]= ~r[P1] ** -** Return SQLITE_ROW on success. Return SQLITE_DONE if there are no -** more opcodes to be displayed. +** Interpret the content of register P1 as an integer. Store the +** ones-complement of the P1 value into register P2. If P1 holds +** a NULL then store a NULL in P2. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeNextOpcode( - Vdbe *p, /* The statement being explained */ - Mem *pSub, /* Storage for keeping track of subprogram nesting */ - int eMode, /* 0: normal. 1: EQP. 2: TablesUsed */ - int *piPc, /* IN/OUT: Current rowid. Overwritten with next rowid */ - int *piAddr, /* OUT: Write index into (*paOp)[] here */ - Op **paOp /* OUT: Write the opcode array here */ -){ - int nRow; /* Stop when row count reaches this */ - int nSub = 0; /* Number of sub-vdbes seen so far */ - SubProgram **apSub = 0; /* Array of sub-vdbes */ - int i; /* Next instruction address */ - int rc = SQLITE_OK; /* Result code */ - Op *aOp = 0; /* Opcode array */ - int iPc; /* Rowid. Copy of value in *piPc */ - - /* When the number of output rows reaches nRow, that means the - ** listing has finished and sqlcipher_sqlite3_step() should return SQLITE_DONE. - ** nRow is the sum of the number of rows in the main program, plus - ** the sum of the number of rows in all trigger subprograms encountered - ** so far. The nRow value will increase as new trigger subprograms are - ** encountered, but p->pc will eventually catch up to nRow. - */ - nRow = p->nOp; - if( pSub!=0 ){ - if( pSub->flags&MEM_Blob ){ - /* pSub is initiallly NULL. It is initialized to a BLOB by - ** the P4_SUBPROGRAM processing logic below */ - nSub = pSub->n/sizeof(Vdbe*); - apSub = (SubProgram **)pSub->z; - } - for(i=0; inOp; - } +case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ + pIn1 = &aMem[pOp->p1]; + pOut = &aMem[pOp->p2]; + sqlcipher_sqlite3VdbeMemSetNull(pOut); + if( (pIn1->flags & MEM_Null)==0 ){ + pOut->flags = MEM_Int; + pOut->u.i = ~sqlcipher_sqlite3VdbeIntValue(pIn1); } - iPc = *piPc; - while(1){ /* Loop exits via break */ - i = iPc++; - if( i>=nRow ){ - p->rc = SQLITE_OK; - rc = SQLITE_DONE; - break; - } - if( inOp ){ - /* The rowid is small enough that we are still in the - ** main program. */ - aOp = p->aOp; - }else{ - /* We are currently listing subprograms. Figure out which one and - ** pick up the appropriate opcode. */ - int j; - i -= p->nOp; - assert( apSub!=0 ); - assert( nSub>0 ); - for(j=0; i>=apSub[j]->nOp; j++){ - i -= apSub[j]->nOp; - assert( inOp || j+1aOp; - } + break; +} - /* When an OP_Program opcode is encounter (the only opcode that has - ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms - ** kept in p->aMem[9].z to hold the new program - assuming this subprogram - ** has not already been seen. - */ - if( pSub!=0 && aOp[i].p4type==P4_SUBPROGRAM ){ - int nByte = (nSub+1)*sizeof(SubProgram*); - int j; - for(j=0; jrc = sqlcipher_sqlite3VdbeMemGrow(pSub, nByte, nSub!=0); - if( p->rc!=SQLITE_OK ){ - rc = SQLITE_ERROR; - break; - } - apSub = (SubProgram **)pSub->z; - apSub[nSub++] = aOp[i].p4.pProgram; - MemSetTypeFlag(pSub, MEM_Blob); - pSub->n = nSub*sizeof(SubProgram*); - nRow += aOp[i].p4.pProgram->nOp; - } +/* Opcode: Once P1 P2 * * * +** +** Fall through to the next instruction the first time this opcode is +** encountered on each invocation of the byte-code program. Jump to P2 +** on the second and all subsequent encounters during the same invocation. +** +** Top-level programs determine first invocation by comparing the P1 +** operand against the P1 operand on the OP_Init opcode at the beginning +** of the program. If the P1 values differ, then fall through and make +** the P1 of this opcode equal to the P1 of OP_Init. If P1 values are +** the same then take the jump. +** +** For subprograms, there is a bitmask in the VdbeFrame that determines +** whether or not the jump should be taken. The bitmask is necessary +** because the self-altering code trick does not work for recursive +** triggers. +*/ +case OP_Once: { /* jump */ + u32 iAddr; /* Address of this instruction */ + assert( p->aOp[0].opcode==OP_Init ); + if( p->pFrame ){ + iAddr = (int)(pOp - p->aOp); + if( (p->pFrame->aOnce[iAddr/8] & (1<<(iAddr & 7)))!=0 ){ + VdbeBranchTaken(1, 2); + goto jump_to_p2; } - if( eMode==0 ) break; -#ifdef SQLITE_ENABLE_BYTECODE_VTAB - if( eMode==2 ){ - Op *pOp = aOp + i; - if( pOp->opcode==OP_OpenRead ) break; - if( pOp->opcode==OP_OpenWrite && (pOp->p5 & OPFLAG_P2ISREG)==0 ) break; - if( pOp->opcode==OP_ReopenIdx ) break; - }else -#endif - { - assert( eMode==1 ); - if( aOp[i].opcode==OP_Explain ) break; - if( aOp[i].opcode==OP_Init && iPc>1 ) break; + p->pFrame->aOnce[iAddr/8] |= 1<<(iAddr & 7); + }else{ + if( p->aOp[0].p1==pOp->p1 ){ + VdbeBranchTaken(1, 2); + goto jump_to_p2; } } - *piPc = iPc; - *piAddr = i; - *paOp = aOp; - return rc; + VdbeBranchTaken(0, 2); + pOp->p1 = p->aOp[0].p1; + break; } -#endif /* SQLITE_ENABLE_BYTECODE_VTAB || !SQLITE_OMIT_EXPLAIN */ - -/* -** Delete a VdbeFrame object and its contents. VdbeFrame objects are -** allocated by the OP_Program opcode in sqlcipher_sqlite3VdbeExec(). +/* Opcode: If P1 P2 P3 * * +** +** Jump to P2 if the value in register P1 is true. The value +** is considered true if it is numeric and non-zero. If the value +** in P1 is NULL then take the jump if and only if P3 is non-zero. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeFrameDelete(VdbeFrame *p){ - int i; - Mem *aMem = VdbeFrameMem(p); - VdbeCursor **apCsr = (VdbeCursor **)&aMem[p->nChildMem]; - assert( sqlcipher_sqlite3VdbeFrameIsValid(p) ); - for(i=0; inChildCsr; i++){ - sqlcipher_sqlite3VdbeFreeCursor(p->v, apCsr[i]); - } - releaseMemArray(aMem, p->nChildMem); - sqlcipher_sqlite3VdbeDeleteAuxData(p->v->db, &p->pAuxData, -1, 0); - sqlcipher_sqlite3DbFree(p->v->db, p); +case OP_If: { /* jump, in1 */ + int c; + c = sqlcipher_sqlite3VdbeBooleanValue(&aMem[pOp->p1], pOp->p3); + VdbeBranchTaken(c!=0, 2); + if( c ) goto jump_to_p2; + break; } -#ifndef SQLITE_OMIT_EXPLAIN -/* -** Give a listing of the program in the virtual machine. -** -** The interface is the same as sqlcipher_sqlite3VdbeExec(). But instead of -** running the code, it invokes the callback once for each instruction. -** This feature is used to implement "EXPLAIN". -** -** When p->explain==1, each instruction is listed. When -** p->explain==2, only OP_Explain instructions are listed and these -** are shown in a different format. p->explain==2 is used to implement -** EXPLAIN QUERY PLAN. -** 2018-04-24: In p->explain==2 mode, the OP_Init opcodes of triggers -** are also shown, so that the boundaries between the main program and -** each trigger are clear. +/* Opcode: IfNot P1 P2 P3 * * ** -** When p->explain==1, first the main program is listed, then each of -** the trigger subprograms are listed one by one. +** Jump to P2 if the value in register P1 is False. The value +** is considered false if it has a numeric value of zero. If the value +** in P1 is NULL then take the jump if and only if P3 is non-zero. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeList( - Vdbe *p /* The VDBE */ -){ - Mem *pSub = 0; /* Memory cell hold array of subprogs */ - sqlcipher_sqlite3 *db = p->db; /* The database connection */ - int i; /* Loop counter */ - int rc = SQLITE_OK; /* Return code */ - Mem *pMem = &p->aMem[1]; /* First Mem of result set */ - int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0); - Op *aOp; /* Array of opcodes */ - Op *pOp; /* Current opcode */ - - assert( p->explain ); - assert( p->magic==VDBE_MAGIC_RUN ); - assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); - - /* Even though this opcode does not use dynamic strings for - ** the result, result columns may become dynamic if the user calls - ** sqlcipher_sqlite3_column_text16(), causing a translation to UTF-16 encoding. - */ - releaseMemArray(pMem, 8); - p->pResultSet = 0; - - if( p->rc==SQLITE_NOMEM ){ - /* This happens if a malloc() inside a call to sqlcipher_sqlite3_column_text() or - ** sqlcipher_sqlite3_column_text16() failed. */ - sqlcipher_sqlite3OomFault(db); - return SQLITE_ERROR; - } +case OP_IfNot: { /* jump, in1 */ + int c; + c = !sqlcipher_sqlite3VdbeBooleanValue(&aMem[pOp->p1], !pOp->p3); + VdbeBranchTaken(c!=0, 2); + if( c ) goto jump_to_p2; + break; +} - if( bListSubprogs ){ - /* The first 8 memory cells are used for the result set. So we will - ** commandeer the 9th cell to use as storage for an array of pointers - ** to trigger subprograms. The VDBE is guaranteed to have at least 9 - ** cells. */ - assert( p->nMem>9 ); - pSub = &p->aMem[9]; - }else{ - pSub = 0; +/* Opcode: IsNull P1 P2 * * * +** Synopsis: if r[P1]==NULL goto P2 +** +** Jump to P2 if the value in register P1 is NULL. +*/ +case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ + pIn1 = &aMem[pOp->p1]; + VdbeBranchTaken( (pIn1->flags & MEM_Null)!=0, 2); + if( (pIn1->flags & MEM_Null)!=0 ){ + goto jump_to_p2; } + break; +} - /* Figure out which opcode is next to display */ - rc = sqlcipher_sqlite3VdbeNextOpcode(p, pSub, p->explain==2, &p->pc, &i, &aOp); - - if( rc==SQLITE_OK ){ - pOp = aOp + i; - if( AtomicLoad(&db->u1.isInterrupted) ){ - p->rc = SQLITE_INTERRUPT; - rc = SQLITE_ERROR; - sqlcipher_sqlite3VdbeError(p, sqlcipher_sqlite3ErrStr(p->rc)); - }else{ - char *zP4 = sqlcipher_sqlite3VdbeDisplayP4(db, pOp); - if( p->explain==2 ){ - sqlcipher_sqlite3VdbeMemSetInt64(pMem, pOp->p1); - sqlcipher_sqlite3VdbeMemSetInt64(pMem+1, pOp->p2); - sqlcipher_sqlite3VdbeMemSetInt64(pMem+2, pOp->p3); - sqlcipher_sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlcipher_sqlite3_free); - p->nResColumn = 4; - }else{ - sqlcipher_sqlite3VdbeMemSetInt64(pMem+0, i); - sqlcipher_sqlite3VdbeMemSetStr(pMem+1, (char*)sqlcipher_sqlite3OpcodeName(pOp->opcode), - -1, SQLITE_UTF8, SQLITE_STATIC); - sqlcipher_sqlite3VdbeMemSetInt64(pMem+2, pOp->p1); - sqlcipher_sqlite3VdbeMemSetInt64(pMem+3, pOp->p2); - sqlcipher_sqlite3VdbeMemSetInt64(pMem+4, pOp->p3); - /* pMem+5 for p4 is done last */ - sqlcipher_sqlite3VdbeMemSetInt64(pMem+6, pOp->p5); -#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - { - char *zCom = sqlcipher_sqlite3VdbeDisplayComment(db, pOp, zP4); - sqlcipher_sqlite3VdbeMemSetStr(pMem+7, zCom, -1, SQLITE_UTF8, sqlcipher_sqlite3_free); - } -#else - sqlcipher_sqlite3VdbeMemSetNull(pMem+7); -#endif - sqlcipher_sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlcipher_sqlite3_free); - p->nResColumn = 8; - } - p->pResultSet = pMem; - if( db->mallocFailed ){ - p->rc = SQLITE_NOMEM; - rc = SQLITE_ERROR; - }else{ - p->rc = SQLITE_OK; - rc = SQLITE_ROW; - } - } - } - return rc; +/* Opcode: IsNullOrType P1 P2 P3 * * +** Synopsis: if typeof(r[P1]) IN (P3,5) goto P2 +** +** Jump to P2 if the value in register P1 is NULL or has a datatype P3. +** P3 is an integer which should be one of SQLITE_INTEGER, SQLITE_FLOAT, +** SQLITE_BLOB, SQLITE_NULL, or SQLITE_TEXT. +*/ +case OP_IsNullOrType: { /* jump, in1 */ + int doTheJump; + pIn1 = &aMem[pOp->p1]; + doTheJump = (pIn1->flags & MEM_Null)!=0 || sqlcipher_sqlite3_value_type(pIn1)==pOp->p3; + VdbeBranchTaken( doTheJump, 2); + if( doTheJump ) goto jump_to_p2; + break; } -#endif /* SQLITE_OMIT_EXPLAIN */ -#ifdef SQLITE_DEBUG -/* -** Print the SQL that was used to generate a VDBE program. +/* Opcode: ZeroOrNull P1 P2 P3 * * +** Synopsis: r[P2] = 0 OR NULL +** +** If all both registers P1 and P3 are NOT NULL, then store a zero in +** register P2. If either registers P1 or P3 are NULL then put +** a NULL in register P2. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbePrintSql(Vdbe *p){ - const char *z = 0; - if( p->zSql ){ - z = p->zSql; - }else if( p->nOp>=1 ){ - const VdbeOp *pOp = &p->aOp[0]; - if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){ - z = pOp->p4.z; - while( sqlcipher_sqlite3Isspace(*z) ) z++; - } +case OP_ZeroOrNull: { /* in1, in2, out2, in3 */ + if( (aMem[pOp->p1].flags & MEM_Null)!=0 + || (aMem[pOp->p3].flags & MEM_Null)!=0 + ){ + sqlcipher_sqlite3VdbeMemSetNull(aMem + pOp->p2); + }else{ + sqlcipher_sqlite3VdbeMemSetInt64(aMem + pOp->p2, 0); } - if( z ) printf("SQL: [%s]\n", z); + break; } -#endif -#if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE) -/* -** Print an IOTRACE message showing SQL content. +/* Opcode: NotNull P1 P2 * * * +** Synopsis: if r[P1]!=NULL goto P2 +** +** Jump to P2 if the value in register P1 is not NULL. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeIOTraceSql(Vdbe *p){ - int nOp = p->nOp; - VdbeOp *pOp; - if( sqlcipher_sqlite3IoTrace==0 ) return; - if( nOp<1 ) return; - pOp = &p->aOp[0]; - if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){ - int i, j; - char z[1000]; - sqlcipher_sqlite3_snprintf(sizeof(z), z, "%s", pOp->p4.z); - for(i=0; sqlcipher_sqlite3Isspace(z[i]); i++){} - for(j=0; z[i]; i++){ - if( sqlcipher_sqlite3Isspace(z[i]) ){ - if( z[i-1]!=' ' ){ - z[j++] = ' '; - } - }else{ - z[j++] = z[i]; - } - } - z[j] = 0; - sqlcipher_sqlite3IoTrace("SQL %s\n", z); +case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ + pIn1 = &aMem[pOp->p1]; + VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2); + if( (pIn1->flags & MEM_Null)==0 ){ + goto jump_to_p2; } + break; } -#endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */ -/* An instance of this object describes bulk memory available for use -** by subcomponents of a prepared statement. Space is allocated out -** of a ReusableSpace object by the allocSpace() routine below. +/* Opcode: IfNullRow P1 P2 P3 * * +** Synopsis: if P1.nullRow then r[P3]=NULL, goto P2 +** +** Check the cursor P1 to see if it is currently pointing at a NULL row. +** If it is, then set register P3 to NULL and jump immediately to P2. +** If P1 is not on a NULL row, then fall through without making any +** changes. */ -struct ReusableSpace { - u8 *pSpace; /* Available memory */ - sqlcipher_sqlite3_int64 nFree; /* Bytes of available memory */ - sqlcipher_sqlite3_int64 nNeeded; /* Total bytes that could not be allocated */ -}; +case OP_IfNullRow: { /* jump */ + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( p->apCsr[pOp->p1]!=0 ); + if( p->apCsr[pOp->p1]->nullRow ){ + sqlcipher_sqlite3VdbeMemSetNull(aMem + pOp->p3); + goto jump_to_p2; + } + break; +} -/* Try to allocate nByte bytes of 8-byte aligned bulk memory for pBuf -** from the ReusableSpace object. Return a pointer to the allocated -** memory on success. If insufficient memory is available in the -** ReusableSpace object, increase the ReusableSpace.nNeeded -** value by the amount needed and return NULL. +#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC +/* Opcode: Offset P1 P2 P3 * * +** Synopsis: r[P3] = sqlite_offset(P1) ** -** If pBuf is not initially NULL, that means that the memory has already -** been allocated by a prior call to this routine, so just return a copy -** of pBuf and leave ReusableSpace unchanged. +** Store in register r[P3] the byte offset into the database file that is the +** start of the payload for the record at which that cursor P1 is currently +** pointing. ** -** This allocator is employed to repurpose unused slots at the end of the -** opcode array of prepared state for other memory needs of the prepared -** statement. +** P2 is the column number for the argument to the sqlite_offset() function. +** This opcode does not use P2 itself, but the P2 value is used by the +** code generator. The P1, P2, and P3 operands to this opcode are the +** same as for OP_Column. +** +** This opcode is only available if SQLite is compiled with the +** -DSQLITE_ENABLE_OFFSET_SQL_FUNC option. */ -static void *allocSpace( - struct ReusableSpace *p, /* Bulk memory available for allocation */ - void *pBuf, /* Pointer to a prior allocation */ - sqlcipher_sqlite3_int64 nByte /* Bytes of memory needed */ -){ - assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) ); - if( pBuf==0 ){ - nByte = ROUND8(nByte); - if( nByte <= p->nFree ){ - p->nFree -= nByte; - pBuf = &p->pSpace[p->nFree]; +case OP_Offset: { /* out3 */ + VdbeCursor *pC; /* The VDBE cursor */ + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + pOut = &p->aMem[pOp->p3]; + if( pC==0 || pC->eCurType!=CURTYPE_BTREE ){ + sqlcipher_sqlite3VdbeMemSetNull(pOut); + }else{ + if( pC->deferredMoveto ){ + rc = sqlcipher_sqlite3VdbeFinishMoveto(pC); + if( rc ) goto abort_due_to_error; + } + if( sqlcipher_sqlite3BtreeEof(pC->uc.pCursor) ){ + sqlcipher_sqlite3VdbeMemSetNull(pOut); }else{ - p->nNeeded += nByte; + sqlcipher_sqlite3VdbeMemSetInt64(pOut, sqlcipher_sqlite3BtreeOffset(pC->uc.pCursor)); } } - assert( EIGHT_BYTE_ALIGNMENT(pBuf) ); - return pBuf; -} - -/* -** Rewind the VDBE back to the beginning in preparation for -** running it. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeRewind(Vdbe *p){ -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) - int i; -#endif - assert( p!=0 ); - assert( p->magic==VDBE_MAGIC_INIT || p->magic==VDBE_MAGIC_RESET ); - - /* There should be at least one opcode. - */ - assert( p->nOp>0 ); - - /* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */ - p->magic = VDBE_MAGIC_RUN; - -#ifdef SQLITE_DEBUG - for(i=0; inMem; i++){ - assert( p->aMem[i].db==p->db ); - } -#endif - p->pc = -1; - p->rc = SQLITE_OK; - p->errorAction = OE_Abort; - p->nChange = 0; - p->cacheCtr = 1; - p->minWriteFileFormat = 255; - p->iStatement = 0; - p->nFkConstraint = 0; -#ifdef VDBE_PROFILE - for(i=0; inOp; i++){ - p->aOp[i].cnt = 0; - p->aOp[i].cycles = 0; - } -#endif + break; } +#endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */ -/* -** Prepare a virtual machine for execution for the first time after -** creating the virtual machine. This involves things such -** as allocating registers and initializing the program counter. -** After the VDBE has be prepped, it can be executed by one or more -** calls to sqlcipher_sqlite3VdbeExec(). +/* Opcode: Column P1 P2 P3 P4 P5 +** Synopsis: r[P3]=PX cursor P1 column P2 ** -** This function may be called exactly once on each virtual machine. -** After this routine is called the VM has been "packaged" and is ready -** to run. After this routine is called, further calls to -** sqlcipher_sqlite3VdbeAddOp() functions are prohibited. This routine disconnects -** the Vdbe from the Parse object that helped generate it so that the -** the Vdbe becomes an independent entity and the Parse object can be -** destroyed. +** Interpret the data that cursor P1 points to as a structure built using +** the MakeRecord instruction. (See the MakeRecord opcode for additional +** information about the format of the data.) Extract the P2-th column +** from this record. If there are less that (P2+1) +** values in the record, extract a NULL. ** -** Use the sqlcipher_sqlite3VdbeRewind() procedure to restore a virtual machine back -** to its initial state after it has been run. +** The value extracted is stored in register P3. +** +** If the record contains fewer than P2 fields, then extract a NULL. Or, +** if the P4 argument is a P4_MEM use the value of the P4 argument as +** the result. +** +** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then +** the result is guaranteed to only be used as the argument of a length() +** or typeof() function, respectively. The loading of large blobs can be +** skipped for length() and all content loading can be skipped for typeof(). */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMakeReady( - Vdbe *p, /* The VDBE */ - Parse *pParse /* Parsing context */ -){ - sqlcipher_sqlite3 *db; /* The database connection */ - int nVar; /* Number of parameters */ - int nMem; /* Number of VM memory registers */ - int nCursor; /* Number of cursors required */ - int nArg; /* Number of arguments in subprograms */ - int n; /* Loop counter */ - struct ReusableSpace x; /* Reusable bulk memory */ - - assert( p!=0 ); - assert( p->nOp>0 ); - assert( pParse!=0 ); - assert( p->magic==VDBE_MAGIC_INIT ); - assert( pParse==p->pParse ); - db = p->db; - assert( db->mallocFailed==0 ); - nVar = pParse->nVar; - nMem = pParse->nMem; - nCursor = pParse->nTab; - nArg = pParse->nMaxArg; +case OP_Column: { + u32 p2; /* column number to retrieve */ + VdbeCursor *pC; /* The VDBE cursor */ + BtCursor *pCrsr; /* The B-Tree cursor corresponding to pC */ + u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ + int len; /* The length of the serialized data for the column */ + int i; /* Loop counter */ + Mem *pDest; /* Where to write the extracted value */ + Mem sMem; /* For storing the record being decoded */ + const u8 *zData; /* Part of the record being decoded */ + const u8 *zHdr; /* Next unparsed byte of the header */ + const u8 *zEndHdr; /* Pointer to first byte after the header */ + u64 offset64; /* 64-bit offset */ + u32 t; /* A type code from the record header */ + Mem *pReg; /* PseudoTable input register */ - /* Each cursor uses a memory cell. The first cursor (cursor 0) can - ** use aMem[0] which is not otherwise used by the VDBE program. Allocate - ** space at the end of aMem[] for cursors 1 and greater. - ** See also: allocateCursor(). - */ - nMem += nCursor; - if( nCursor==0 && nMem>0 ) nMem++; /* Space for aMem[0] even if not used */ + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); + pC = p->apCsr[pOp->p1]; + p2 = (u32)pOp->p2; - /* Figure out how much reusable memory is available at the end of the - ** opcode array. This extra memory will be reallocated for other elements - ** of the prepared statement. - */ - n = ROUND8(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */ - x.pSpace = &((u8*)p->aOp)[n]; /* Unused opcode memory */ - assert( EIGHT_BYTE_ALIGNMENT(x.pSpace) ); - x.nFree = ROUNDDOWN8(pParse->szOpAlloc - n); /* Bytes of unused memory */ - assert( x.nFree>=0 ); - assert( EIGHT_BYTE_ALIGNMENT(&x.pSpace[x.nFree]) ); +op_column_restart: + assert( pC!=0 ); + assert( p2<(u32)pC->nField + || (pC->eCurType==CURTYPE_PSEUDO && pC->seekResult==0) ); + aOffset = pC->aOffset; + assert( aOffset==pC->aType+pC->nField ); + assert( pC->eCurType!=CURTYPE_VTAB ); + assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); + assert( pC->eCurType!=CURTYPE_SORTER ); - resolveP2Values(p, &nArg); - p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort); - if( pParse->explain ){ - static const char * const azColName[] = { - "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", - "id", "parent", "notused", "detail" - }; - int iFirst, mx, i; - if( nMem<10 ) nMem = 10; - p->explain = pParse->explain; - if( pParse->explain==2 ){ - sqlcipher_sqlite3VdbeSetNumCols(p, 4); - iFirst = 8; - mx = 12; + if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/ + if( pC->nullRow ){ + if( pC->eCurType==CURTYPE_PSEUDO && pC->seekResult>0 ){ + /* For the special case of as pseudo-cursor, the seekResult field + ** identifies the register that holds the record */ + pReg = &aMem[pC->seekResult]; + assert( pReg->flags & MEM_Blob ); + assert( memIsValid(pReg) ); + pC->payloadSize = pC->szRow = pReg->n; + pC->aRow = (u8*)pReg->z; + }else{ + pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); + sqlcipher_sqlite3VdbeMemSetNull(pDest); + goto op_column_out; + } }else{ - sqlcipher_sqlite3VdbeSetNumCols(p, 8); - iFirst = 0; - mx = 8; + pCrsr = pC->uc.pCursor; + if( pC->deferredMoveto ){ + u32 iMap; + assert( !pC->isEphemeral ); + if( pC->ub.aAltMap && (iMap = pC->ub.aAltMap[1+p2])>0 ){ + pC = pC->pAltCursor; + p2 = iMap - 1; + goto op_column_restart; + } + rc = sqlcipher_sqlite3VdbeFinishMoveto(pC); + if( rc ) goto abort_due_to_error; + }else if( sqlcipher_sqlite3BtreeCursorHasMoved(pCrsr) ){ + rc = sqlcipher_sqlite3VdbeHandleMovedCursor(pC); + if( rc ) goto abort_due_to_error; + goto op_column_restart; + } + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pCrsr ); + assert( sqlcipher_sqlite3BtreeCursorIsValid(pCrsr) ); + pC->payloadSize = sqlcipher_sqlite3BtreePayloadSize(pCrsr); + pC->aRow = sqlcipher_sqlite3BtreePayloadFetch(pCrsr, &pC->szRow); + assert( pC->szRow<=pC->payloadSize ); + assert( pC->szRow<=65536 ); /* Maximum page size is 64KiB */ } - for(i=iFirst; icacheStatus = p->cacheCtr; + if( (aOffset[0] = pC->aRow[0])<0x80 ){ + pC->iHdrOffset = 1; + }else{ + pC->iHdrOffset = sqlcipher_sqlite3GetVarint32(pC->aRow, aOffset); + } + pC->nHdrParsed = 0; + + if( pC->szRowaRow does not have to hold the entire row, but it does at least + ** need to cover the header of the record. If pC->aRow does not contain + ** the complete header, then set it to zero, forcing the header to be + ** dynamically allocated. */ + pC->aRow = 0; + pC->szRow = 0; + + /* Make sure a corrupt database has not given us an oversize header. + ** Do this now to avoid an oversize memory allocation. + ** + ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte + ** types use so much data space that there can only be 4096 and 32 of + ** them, respectively. So the maximum header length results from a + ** 3-byte type for each of the maximum of 32768 columns plus three + ** extra bytes for the header length itself. 32768*3 + 3 = 98307. + */ + if( aOffset[0] > 98307 || aOffset[0] > pC->payloadSize ){ + goto op_column_corrupt; + } + }else{ + /* This is an optimization. By skipping over the first few tests + ** (ex: pC->nHdrParsed<=p2) in the next section, we achieve a + ** measurable performance gain. + ** + ** This branch is taken even if aOffset[0]==0. Such a record is never + ** generated by SQLite, and could be considered corruption, but we + ** accept it for historical reasons. When aOffset[0]==0, the code this + ** branch jumps to reads past the end of the record, but never more + ** than a few bytes. Even if the record occurs at the end of the page + ** content area, the "page header" comes after the page content and so + ** this overread is harmless. Similar overreads can occur for a corrupt + ** database file. + */ + zData = pC->aRow; + assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */ + testcase( aOffset[0]==0 ); + goto op_column_read_header; } + }else if( sqlcipher_sqlite3BtreeCursorHasMoved(pC->uc.pCursor) ){ + rc = sqlcipher_sqlite3VdbeHandleMovedCursor(pC); + if( rc ) goto abort_due_to_error; + goto op_column_restart; } - p->expired = 0; - /* Memory for registers, parameters, cursor, etc, is allocated in one or two - ** passes. On the first pass, we try to reuse unused memory at the - ** end of the opcode array. If we are unable to satisfy all memory - ** requirements by reusing the opcode array tail, then the second - ** pass will fill in the remainder using a fresh memory allocation. - ** - ** This two-pass approach that reuses as much memory as possible from - ** the leftover memory at the end of the opcode array. This can significantly - ** reduce the amount of memory held by a prepared statement. + /* Make sure at least the first p2+1 entries of the header have been + ** parsed and valid information is in aOffset[] and pC->aType[]. */ - x.nNeeded = 0; - p->aMem = allocSpace(&x, 0, nMem*sizeof(Mem)); - p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem)); - p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*)); - p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*)); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - p->anExec = allocSpace(&x, 0, p->nOp*sizeof(i64)); -#endif - if( x.nNeeded ){ - x.pSpace = p->pFree = sqlcipher_sqlite3DbMallocRawNN(db, x.nNeeded); - x.nFree = x.nNeeded; - if( !db->mallocFailed ){ - p->aMem = allocSpace(&x, p->aMem, nMem*sizeof(Mem)); - p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem)); - p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*)); - p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); -#endif + if( pC->nHdrParsed<=p2 ){ + /* If there is more header available for parsing in the record, try + ** to extract additional fields up through the p2+1-th field + */ + if( pC->iHdrOffsetaRow==0 ){ + memset(&sMem, 0, sizeof(sMem)); + rc = sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(pC->uc.pCursor,aOffset[0],&sMem); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + zData = (u8*)sMem.z; + }else{ + zData = pC->aRow; + } + + /* Fill in pC->aType[i] and aOffset[i] values through the p2-th field. */ + op_column_read_header: + i = pC->nHdrParsed; + offset64 = aOffset[i]; + zHdr = zData + pC->iHdrOffset; + zEndHdr = zData + aOffset[0]; + testcase( zHdr>=zEndHdr ); + do{ + if( (pC->aType[i] = t = zHdr[0])<0x80 ){ + zHdr++; + offset64 += sqlcipher_sqlite3VdbeOneByteSerialTypeLen(t); + }else{ + zHdr += sqlcipher_sqlite3GetVarint32(zHdr, &t); + pC->aType[i] = t; + offset64 += sqlcipher_sqlite3VdbeSerialTypeLen(t); + } + aOffset[++i] = (u32)(offset64 & 0xffffffff); + }while( (u32)i<=p2 && zHdr=zEndHdr && (zHdr>zEndHdr || offset64!=pC->payloadSize)) + || (offset64 > pC->payloadSize) + ){ + if( aOffset[0]==0 ){ + i = 0; + zHdr = zEndHdr; + }else{ + if( pC->aRow==0 ) sqlcipher_sqlite3VdbeMemRelease(&sMem); + goto op_column_corrupt; + } + } + + pC->nHdrParsed = i; + pC->iHdrOffset = (u32)(zHdr - zData); + if( pC->aRow==0 ) sqlcipher_sqlite3VdbeMemRelease(&sMem); + }else{ + t = 0; } - } - p->pVList = pParse->pVList; - pParse->pVList = 0; - if( db->mallocFailed ){ - p->nVar = 0; - p->nCursor = 0; - p->nMem = 0; + /* If after trying to extract new entries from the header, nHdrParsed is + ** still not up to p2, that means that the record has fewer than p2 + ** columns. So the result will be either the default value or a NULL. + */ + if( pC->nHdrParsed<=p2 ){ + pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); + if( pOp->p4type==P4_MEM ){ + sqlcipher_sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static); + }else{ + sqlcipher_sqlite3VdbeMemSetNull(pDest); + } + goto op_column_out; + } }else{ - p->nCursor = nCursor; - p->nVar = (ynVar)nVar; - initMemArray(p->aVar, nVar, db, MEM_Null); - p->nMem = nMem; - initMemArray(p->aMem, nMem, db, MEM_Undefined); - memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*)); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - memset(p->anExec, 0, p->nOp*sizeof(i64)); -#endif + t = pC->aType[p2]; } - sqlcipher_sqlite3VdbeRewind(p); -} -/* -** Close a VDBE cursor and release all the resources that cursor -** happens to hold. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ - if( pCx==0 ){ - return; + /* Extract the content for the p2+1-th column. Control can only + ** reach this point if aOffset[p2], aOffset[p2+1], and pC->aType[p2] are + ** all valid. + */ + assert( p2nHdrParsed ); + assert( rc==SQLITE_OK ); + pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); + assert( sqlcipher_sqlite3VdbeCheckMemInvariants(pDest) ); + if( VdbeMemDynamic(pDest) ){ + sqlcipher_sqlite3VdbeMemSetNull(pDest); } - assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE ); - switch( pCx->eCurType ){ - case CURTYPE_SORTER: { - sqlcipher_sqlite3VdbeSorterClose(p->db, pCx); - break; - } - case CURTYPE_BTREE: { - if( pCx->isEphemeral ){ - if( pCx->pBtx ) sqlcipher_sqlite3BtreeClose(pCx->pBtx); - /* The pCx->pCursor will be close automatically, if it exists, by - ** the call above. */ + assert( t==pC->aType[p2] ); + if( pC->szRow>=aOffset[p2+1] ){ + /* This is the common case where the desired content fits on the original + ** page - where the content is not on an overflow page */ + zData = pC->aRow + aOffset[p2]; + if( t<12 ){ + sqlcipher_sqlite3VdbeSerialGet(zData, t, pDest); + }else{ + /* If the column value is a string, we need a persistent value, not + ** a MEM_Ephem value. This branch is a fast short-cut that is equivalent + ** to calling sqlcipher_sqlite3VdbeSerialGet() and sqlcipher_sqlite3VdbeDeephemeralize(). + */ + static const u16 aFlag[] = { MEM_Blob, MEM_Str|MEM_Term }; + pDest->n = len = (t-12)/2; + pDest->enc = encoding; + if( pDest->szMalloc < len+2 ){ + if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) goto too_big; + pDest->flags = MEM_Null; + if( sqlcipher_sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem; }else{ - assert( pCx->uc.pCursor!=0 ); - sqlcipher_sqlite3BtreeCloseCursor(pCx->uc.pCursor); + pDest->z = pDest->zMalloc; } - break; + memcpy(pDest->z, zData, len); + pDest->z[len] = 0; + pDest->z[len+1] = 0; + pDest->flags = aFlag[t&1]; } -#ifndef SQLITE_OMIT_VIRTUALTABLE - case CURTYPE_VTAB: { - sqlcipher_sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur; - const sqlcipher_sqlite3_module *pModule = pVCur->pVtab->pModule; - assert( pVCur->pVtab->nRef>0 ); - pVCur->pVtab->nRef--; - pModule->xClose(pVCur); - break; + }else{ + pDest->enc = encoding; + /* This branch happens only when content is on overflow pages */ + if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 + && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) + || (len = sqlcipher_sqlite3VdbeSerialTypeLen(t))==0 + ){ + /* Content is irrelevant for + ** 1. the typeof() function, + ** 2. the length(X) function if X is a blob, and + ** 3. if the content length is zero. + ** So we might as well use bogus content rather than reading + ** content from disk. + ** + ** Although sqlcipher_sqlite3VdbeSerialGet() may read at most 8 bytes from the + ** buffer passed to it, debugging function VdbeMemPrettyPrint() may + ** read more. Use the global constant sqlcipher_sqlite3CtypeMap[] as the array, + ** as that array is 256 bytes long (plenty for VdbeMemPrettyPrint()) + ** and it begins with a bunch of zeros. + */ + sqlcipher_sqlite3VdbeSerialGet((u8*)sqlcipher_sqlite3CtypeMap, t, pDest); + }else{ + if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) goto too_big; + rc = sqlcipher_sqlite3VdbeMemFromBtree(pC->uc.pCursor, aOffset[p2], len, pDest); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + sqlcipher_sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); + pDest->flags &= ~MEM_Ephem; } -#endif } -} -/* -** Close all cursors in the current frame. -*/ -static void closeCursorsInFrame(Vdbe *p){ - if( p->apCsr ){ - int i; - for(i=0; inCursor; i++){ - VdbeCursor *pC = p->apCsr[i]; - if( pC ){ - sqlcipher_sqlite3VdbeFreeCursor(p, pC); - p->apCsr[i] = 0; - } - } - } -} +op_column_out: + UPDATE_MAX_BLOBSIZE(pDest); + REGISTER_TRACE(pOp->p3, pDest); + break; -/* -** Copy the values stored in the VdbeFrame structure to its Vdbe. This -** is used, for example, when a trigger sub-program is halted to restore -** control to the main program. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ - Vdbe *v = pFrame->v; - closeCursorsInFrame(v); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - v->anExec = pFrame->anExec; -#endif - v->aOp = pFrame->aOp; - v->nOp = pFrame->nOp; - v->aMem = pFrame->aMem; - v->nMem = pFrame->nMem; - v->apCsr = pFrame->apCsr; - v->nCursor = pFrame->nCursor; - v->db->lastRowid = pFrame->lastRowid; - v->nChange = pFrame->nChange; - v->db->nChange = pFrame->nDbChange; - sqlcipher_sqlite3VdbeDeleteAuxData(v->db, &v->pAuxData, -1, 0); - v->pAuxData = pFrame->pAuxData; - pFrame->pAuxData = 0; - return pFrame->pc; +op_column_corrupt: + if( aOp[0].p3>0 ){ + pOp = &aOp[aOp[0].p3-1]; + break; + }else{ + rc = SQLITE_CORRUPT_BKPT; + goto abort_due_to_error; + } } -/* -** Close all cursors. +/* Opcode: TypeCheck P1 P2 P3 P4 * +** Synopsis: typecheck(r[P1@P2]) ** -** Also release any dynamic memory held by the VM in the Vdbe.aMem memory -** cell array. This is necessary as the memory cell array may contain -** pointers to VdbeFrame objects, which may in turn contain pointers to -** open cursors. +** Apply affinities to the range of P2 registers beginning with P1. +** Take the affinities from the Table object in P4. If any value +** cannot be coerced into the correct type, then raise an error. +** +** This opcode is similar to OP_Affinity except that this opcode +** forces the register type to the Table column type. This is used +** to implement "strict affinity". +** +** GENERATED ALWAYS AS ... STATIC columns are only checked if P3 +** is zero. When P3 is non-zero, no type checking occurs for +** static generated columns. Virtual columns are computed at query time +** and so they are never checked. +** +** Preconditions: +** +**
      +**
    • P2 should be the number of non-virtual columns in the +** table of P4. +**
    • Table P4 should be a STRICT table. +**
    +** +** If any precondition is false, an assertion fault occurs. */ -static void closeAllCursors(Vdbe *p){ - if( p->pFrame ){ - VdbeFrame *pFrame; - for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); - sqlcipher_sqlite3VdbeFrameRestore(pFrame); - p->pFrame = 0; - p->nFrame = 0; - } - assert( p->nFrame==0 ); - closeCursorsInFrame(p); - if( p->aMem ){ - releaseMemArray(p->aMem, p->nMem); - } - while( p->pDelFrame ){ - VdbeFrame *pDel = p->pDelFrame; - p->pDelFrame = pDel->pParent; - sqlcipher_sqlite3VdbeFrameDelete(pDel); +case OP_TypeCheck: { + Table *pTab; + Column *aCol; + int i; + + assert( pOp->p4type==P4_TABLE ); + pTab = pOp->p4.pTab; + assert( pTab->tabFlags & TF_Strict ); + assert( pTab->nNVCol==pOp->p2 ); + aCol = pTab->aCol; + pIn1 = &aMem[pOp->p1]; + for(i=0; inCol; i++){ + if( aCol[i].colFlags & COLFLAG_GENERATED ){ + if( aCol[i].colFlags & COLFLAG_VIRTUAL ) continue; + if( pOp->p3 ){ pIn1++; continue; } + } + assert( pIn1 < &aMem[pOp->p1+pOp->p2] ); + applyAffinity(pIn1, aCol[i].affinity, encoding); + if( (pIn1->flags & MEM_Null)==0 ){ + switch( aCol[i].eCType ){ + case COLTYPE_BLOB: { + if( (pIn1->flags & MEM_Blob)==0 ) goto vdbe_type_error; + break; + } + case COLTYPE_INTEGER: + case COLTYPE_INT: { + if( (pIn1->flags & MEM_Int)==0 ) goto vdbe_type_error; + break; + } + case COLTYPE_TEXT: { + if( (pIn1->flags & MEM_Str)==0 ) goto vdbe_type_error; + break; + } + case COLTYPE_REAL: { + testcase( (pIn1->flags & (MEM_Real|MEM_IntReal))==MEM_Real ); + testcase( (pIn1->flags & (MEM_Real|MEM_IntReal))==MEM_IntReal ); + if( pIn1->flags & MEM_Int ){ + /* When applying REAL affinity, if the result is still an MEM_Int + ** that will fit in 6 bytes, then change the type to MEM_IntReal + ** so that we keep the high-resolution integer value but know that + ** the type really wants to be REAL. */ + testcase( pIn1->u.i==140737488355328LL ); + testcase( pIn1->u.i==140737488355327LL ); + testcase( pIn1->u.i==-140737488355328LL ); + testcase( pIn1->u.i==-140737488355329LL ); + if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL){ + pIn1->flags |= MEM_IntReal; + pIn1->flags &= ~MEM_Int; + }else{ + pIn1->u.r = (double)pIn1->u.i; + pIn1->flags |= MEM_Real; + pIn1->flags &= ~MEM_Int; + } + }else if( (pIn1->flags & (MEM_Real|MEM_IntReal))==0 ){ + goto vdbe_type_error; + } + break; + } + default: { + /* COLTYPE_ANY. Accept anything. */ + break; + } + } + } + REGISTER_TRACE((int)(pIn1-aMem), pIn1); + pIn1++; } + assert( pIn1 == &aMem[pOp->p1+pOp->p2] ); + break; - /* Delete any auxdata allocations made by the VM */ - if( p->pAuxData ) sqlcipher_sqlite3VdbeDeleteAuxData(p->db, &p->pAuxData, -1, 0); - assert( p->pAuxData==0 ); +vdbe_type_error: + sqlcipher_sqlite3VdbeError(p, "cannot store %s value in %s column %s.%s", + vdbeMemTypeName(pIn1), sqlcipher_sqlite3StdType[aCol[i].eCType-1], + pTab->zName, aCol[i].zCnName); + rc = SQLITE_CONSTRAINT_DATATYPE; + goto abort_due_to_error; } -/* -** Set the number of result columns that will be returned by this SQL -** statement. This is now set at compile time, rather than during -** execution of the vdbe program so that sqlcipher_sqlite3_column_count() can -** be called on an SQL statement before sqlcipher_sqlite3_step(). +/* Opcode: Affinity P1 P2 * P4 * +** Synopsis: affinity(r[P1@P2]) +** +** Apply affinities to a range of P2 registers starting with P1. +** +** P4 is a string that is P2 characters long. The N-th character of the +** string indicates the column affinity that should be used for the N-th +** memory cell in the range. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ - int n; - sqlcipher_sqlite3 *db = p->db; +case OP_Affinity: { + const char *zAffinity; /* The affinity to be applied */ - if( p->nResColumn ){ - releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); - sqlcipher_sqlite3DbFree(db, p->aColName); + zAffinity = pOp->p4.z; + assert( zAffinity!=0 ); + assert( pOp->p2>0 ); + assert( zAffinity[pOp->p2]==0 ); + pIn1 = &aMem[pOp->p1]; + while( 1 /*exit-by-break*/ ){ + assert( pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)] ); + assert( zAffinity[0]==SQLITE_AFF_NONE || memIsValid(pIn1) ); + applyAffinity(pIn1, zAffinity[0], encoding); + if( zAffinity[0]==SQLITE_AFF_REAL && (pIn1->flags & MEM_Int)!=0 ){ + /* When applying REAL affinity, if the result is still an MEM_Int + ** that will fit in 6 bytes, then change the type to MEM_IntReal + ** so that we keep the high-resolution integer value but know that + ** the type really wants to be REAL. */ + testcase( pIn1->u.i==140737488355328LL ); + testcase( pIn1->u.i==140737488355327LL ); + testcase( pIn1->u.i==-140737488355328LL ); + testcase( pIn1->u.i==-140737488355329LL ); + if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL ){ + pIn1->flags |= MEM_IntReal; + pIn1->flags &= ~MEM_Int; + }else{ + pIn1->u.r = (double)pIn1->u.i; + pIn1->flags |= MEM_Real; + pIn1->flags &= ~MEM_Int; + } + } + REGISTER_TRACE((int)(pIn1-aMem), pIn1); + zAffinity++; + if( zAffinity[0]==0 ) break; + pIn1++; } - n = nResColumn*COLNAME_N; - p->nResColumn = (u16)nResColumn; - p->aColName = (Mem*)sqlcipher_sqlite3DbMallocRawNN(db, sizeof(Mem)*n ); - if( p->aColName==0 ) return; - initMemArray(p->aColName, n, db, MEM_Null); + break; } -/* -** Set the name of the idx'th column to be returned by the SQL statement. -** zName must be a pointer to a nul terminated string. +/* Opcode: MakeRecord P1 P2 P3 P4 * +** Synopsis: r[P3]=mkrec(r[P1@P2]) ** -** This call must be made after a call to sqlcipher_sqlite3VdbeSetNumCols(). +** Convert P2 registers beginning with P1 into the [record format] +** use as a data record in a database table or as a key +** in an index. The OP_Column opcode can decode the record later. ** -** The final parameter, xDel, must be one of SQLITE_DYNAMIC, SQLITE_STATIC -** or SQLITE_TRANSIENT. If it is SQLITE_DYNAMIC, then the buffer pointed -** to by zName will be freed by sqlcipher_sqlite3DbFree() when the vdbe is destroyed. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSetColName( - Vdbe *p, /* Vdbe being configured */ - int idx, /* Index of column zName applies to */ - int var, /* One of the COLNAME_* constants */ - const char *zName, /* Pointer to buffer containing name */ - void (*xDel)(void*) /* Memory management strategy for zName */ -){ - int rc; - Mem *pColName; - assert( idxnResColumn ); - assert( vardb->mallocFailed ){ - assert( !zName || xDel!=SQLITE_DYNAMIC ); - return SQLITE_NOMEM_BKPT; - } - assert( p->aColName!=0 ); - pColName = &(p->aColName[idx+var*p->nResColumn]); - rc = sqlcipher_sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel); - assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); - return rc; -} - -/* -** A read or write transaction may or may not be active on database handle -** db. If a transaction is active, commit it. If there is a -** write-transaction spanning more than one database file, this routine -** takes care of the super-journal trickery. +** P4 may be a string that is P2 characters long. The N-th character of the +** string indicates the column affinity that should be used for the N-th +** field of the index key. +** +** The mapping from character to affinity is given by the SQLITE_AFF_ +** macros defined in sqliteInt.h. +** +** If P4 is NULL then all index fields have the affinity BLOB. +** +** The meaning of P5 depends on whether or not the SQLITE_ENABLE_NULL_TRIM +** compile-time option is enabled: +** +** * If SQLITE_ENABLE_NULL_TRIM is enabled, then the P5 is the index +** of the right-most table that can be null-trimmed. +** +** * If SQLITE_ENABLE_NULL_TRIM is omitted, then P5 has the value +** OPFLAG_NOCHNG_MAGIC if the OP_MakeRecord opcode is allowed to +** accept no-change records with serial_type 10. This value is +** only used inside an assert() and does not affect the end result. */ -static int vdbeCommit(sqlcipher_sqlite3 *db, Vdbe *p){ - int i; - int nTrans = 0; /* Number of databases with an active write-transaction - ** that are candidates for a two-phase commit using a - ** super-journal */ - int rc = SQLITE_OK; - int needXcommit = 0; +case OP_MakeRecord: { + Mem *pRec; /* The new record */ + u64 nData; /* Number of bytes of data space */ + int nHdr; /* Number of bytes of header space */ + i64 nByte; /* Data space required for this record */ + i64 nZero; /* Number of zero bytes at the end of the record */ + int nVarint; /* Number of bytes in a varint */ + u32 serial_type; /* Type field */ + Mem *pData0; /* First field to be combined into the record */ + Mem *pLast; /* Last field of the record */ + int nField; /* Number of fields in the record */ + char *zAffinity; /* The affinity string for the record */ + u32 len; /* Length of a field */ + u8 *zHdr; /* Where to write next byte of the header */ + u8 *zPayload; /* Where to write next byte of the payload */ -#ifdef SQLITE_OMIT_VIRTUALTABLE - /* With this option, sqlcipher_sqlite3VtabSync() is defined to be simply - ** SQLITE_OK so p is not used. + /* Assuming the record contains N fields, the record format looks + ** like this: + ** + ** ------------------------------------------------------------------------ + ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 | + ** ------------------------------------------------------------------------ + ** + ** Data(0) is taken from register P1. Data(1) comes from register P1+1 + ** and so forth. + ** + ** Each type field is a varint representing the serial type of the + ** corresponding data element (see sqlcipher_sqlite3VdbeSerialType()). The + ** hdr-size field is also a varint which is the offset from the beginning + ** of the record to data0. */ - UNUSED_PARAMETER(p); -#endif + nData = 0; /* Number of bytes of data space */ + nHdr = 0; /* Number of bytes of header space */ + nZero = 0; /* Number of zero bytes at the end of the record */ + nField = pOp->p1; + zAffinity = pOp->p4.z; + assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=(p->nMem+1 - p->nCursor)+1 ); + pData0 = &aMem[nField]; + nField = pOp->p2; + pLast = &pData0[nField-1]; - /* Before doing anything else, call the xSync() callback for any - ** virtual module tables written in this transaction. This has to - ** be done before determining whether a super-journal file is - ** required, as an xSync() callback may add an attached database - ** to the transaction. - */ - rc = sqlcipher_sqlite3VtabSync(db, p); + /* Identify the output register */ + assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); + pOut = &aMem[pOp->p3]; + memAboutToChange(p, pOut); - /* This loop determines (a) if the commit hook should be invoked and - ** (b) how many database files have open write transactions, not - ** including the temp database. (b) is important because if more than - ** one database file has an open write transaction, a super-journal - ** file is required for an atomic commit. + /* Apply the requested affinity to all inputs */ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( sqlcipher_sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){ - /* Whether or not a database might need a super-journal depends upon - ** its journal mode (among other things). This matrix determines which - ** journal modes use a super-journal and which do not */ - static const u8 aMJNeeded[] = { - /* DELETE */ 1, - /* PERSIST */ 1, - /* OFF */ 0, - /* TRUNCATE */ 1, - /* MEMORY */ 0, - /* WAL */ 0 - }; - Pager *pPager; /* Pager associated with pBt */ - needXcommit = 1; - sqlcipher_sqlite3BtreeEnter(pBt); - pPager = sqlcipher_sqlite3BtreePager(pBt); - if( db->aDb[i].safety_level!=PAGER_SYNCHRONOUS_OFF - && aMJNeeded[sqlcipher_sqlite3PagerGetJournalMode(pPager)] - && sqlcipher_sqlite3PagerIsMemdb(pPager)==0 - ){ - assert( i!=1 ); - nTrans++; + assert( pData0<=pLast ); + if( zAffinity ){ + pRec = pData0; + do{ + applyAffinity(pRec, zAffinity[0], encoding); + if( zAffinity[0]==SQLITE_AFF_REAL && (pRec->flags & MEM_Int) ){ + pRec->flags |= MEM_IntReal; + pRec->flags &= ~(MEM_Int); } - rc = sqlcipher_sqlite3PagerExclusiveLock(pPager); - sqlcipher_sqlite3BtreeLeave(pBt); - } - } - if( rc!=SQLITE_OK ){ - return rc; + REGISTER_TRACE((int)(pRec-aMem), pRec); + zAffinity++; + pRec++; + assert( zAffinity[0]==0 || pRec<=pLast ); + }while( zAffinity[0] ); } - /* If there are any write-transactions at all, invoke the commit hook */ - if( needXcommit && db->xCommitCallback ){ - rc = db->xCommitCallback(db->pCommitArg); - if( rc ){ - return SQLITE_CONSTRAINT_COMMITHOOK; +#ifdef SQLITE_ENABLE_NULL_TRIM + /* NULLs can be safely trimmed from the end of the record, as long as + ** as the schema format is 2 or more and none of the omitted columns + ** have a non-NULL default value. Also, the record must be left with + ** at least one field. If P5>0 then it will be one more than the + ** index of the right-most column with a non-NULL default value */ + if( pOp->p5 ){ + while( (pLast->flags & MEM_Null)!=0 && nField>pOp->p5 ){ + pLast--; + nField--; } } +#endif - /* The simple case - no more than one database file (not counting the - ** TEMP database) has a transaction active. There is no need for the - ** super-journal. + /* Loop through the elements that will make up the record to figure + ** out how much space is required for the new record. After this loop, + ** the Mem.uTemp field of each term should hold the serial-type that will + ** be used for that term in the generated record: ** - ** If the return value of sqlcipher_sqlite3BtreeGetFilename() is a zero length - ** string, it means the main database is :memory: or a temp file. In - ** that case we do not support atomic multi-file commits, so use the - ** simple case then too. + ** Mem.uTemp value type + ** --------------- --------------- + ** 0 NULL + ** 1 1-byte signed integer + ** 2 2-byte signed integer + ** 3 3-byte signed integer + ** 4 4-byte signed integer + ** 5 6-byte signed integer + ** 6 8-byte signed integer + ** 7 IEEE float + ** 8 Integer constant 0 + ** 9 Integer constant 1 + ** 10,11 reserved for expansion + ** N>=12 and even BLOB + ** N>=13 and odd text + ** + ** The following additional values are computed: + ** nHdr Number of bytes needed for the record header + ** nData Number of bytes of data space needed for the record + ** nZero Zero bytes at the end of the record */ - if( 0==sqlcipher_sqlite3Strlen30(sqlcipher_sqlite3BtreeGetFilename(db->aDb[0].pBt)) - || nTrans<=1 - ){ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = sqlcipher_sqlite3BtreeCommitPhaseOne(pBt, 0); + pRec = pLast; + do{ + assert( memIsValid(pRec) ); + if( pRec->flags & MEM_Null ){ + if( pRec->flags & MEM_Zero ){ + /* Values with MEM_Null and MEM_Zero are created by xColumn virtual + ** table methods that never invoke sqlcipher_sqlite3_result_xxxxx() while + ** computing an unchanging column value in an UPDATE statement. + ** Give such values a special internal-use-only serial-type of 10 + ** so that they can be passed through to xUpdate and have + ** a true sqlcipher_sqlite3_value_nochange(). */ +#ifndef SQLITE_ENABLE_NULL_TRIM + assert( pOp->p5==OPFLAG_NOCHNG_MAGIC || CORRUPT_DB ); +#endif + pRec->uTemp = 10; + }else{ + pRec->uTemp = 0; } - } - - /* Do the commit only if all databases successfully complete phase 1. - ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an - ** IO error while deleting or truncating a journal file. It is unlikely, - ** but could happen. In this case abandon processing and return the error. - */ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = sqlcipher_sqlite3BtreeCommitPhaseTwo(pBt, 0); + nHdr++; + }else if( pRec->flags & (MEM_Int|MEM_IntReal) ){ + /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */ + i64 i = pRec->u.i; + u64 uu; + testcase( pRec->flags & MEM_Int ); + testcase( pRec->flags & MEM_IntReal ); + if( i<0 ){ + uu = ~i; + }else{ + uu = i; } + nHdr++; + testcase( uu==127 ); testcase( uu==128 ); + testcase( uu==32767 ); testcase( uu==32768 ); + testcase( uu==8388607 ); testcase( uu==8388608 ); + testcase( uu==2147483647 ); testcase( uu==2147483648LL ); + testcase( uu==140737488355327LL ); testcase( uu==140737488355328LL ); + if( uu<=127 ){ + if( (i&1)==i && p->minWriteFileFormat>=4 ){ + pRec->uTemp = 8+(u32)uu; + }else{ + nData++; + pRec->uTemp = 1; + } + }else if( uu<=32767 ){ + nData += 2; + pRec->uTemp = 2; + }else if( uu<=8388607 ){ + nData += 3; + pRec->uTemp = 3; + }else if( uu<=2147483647 ){ + nData += 4; + pRec->uTemp = 4; + }else if( uu<=140737488355327LL ){ + nData += 6; + pRec->uTemp = 5; + }else{ + nData += 8; + if( pRec->flags & MEM_IntReal ){ + /* If the value is IntReal and is going to take up 8 bytes to store + ** as an integer, then we might as well make it an 8-byte floating + ** point value */ + pRec->u.r = (double)pRec->u.i; + pRec->flags &= ~MEM_IntReal; + pRec->flags |= MEM_Real; + pRec->uTemp = 7; + }else{ + pRec->uTemp = 6; + } + } + }else if( pRec->flags & MEM_Real ){ + nHdr++; + nData += 8; + pRec->uTemp = 7; + }else{ + assert( db->mallocFailed || pRec->flags&(MEM_Str|MEM_Blob) ); + assert( pRec->n>=0 ); + len = (u32)pRec->n; + serial_type = (len*2) + 12 + ((pRec->flags & MEM_Str)!=0); + if( pRec->flags & MEM_Zero ){ + serial_type += pRec->u.nZero*2; + if( nData ){ + if( sqlcipher_sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem; + len += pRec->u.nZero; + }else{ + nZero += pRec->u.nZero; + } + } + nData += len; + nHdr += sqlcipher_sqlite3VarintLen(serial_type); + pRec->uTemp = serial_type; } - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3VtabCommit(db); - } + if( pRec==pData0 ) break; + pRec--; + }while(1); + + /* EVIDENCE-OF: R-22564-11647 The header begins with a single varint + ** which determines the total number of bytes in the header. The varint + ** value is the size of the header in bytes including the size varint + ** itself. */ + testcase( nHdr==126 ); + testcase( nHdr==127 ); + if( nHdr<=126 ){ + /* The common case */ + nHdr += 1; + }else{ + /* Rare case of a really large header */ + nVarint = sqlcipher_sqlite3VarintLen(nHdr); + nHdr += nVarint; + if( nVarintp3) is not allowed to + ** be one of the input registers (because the following call to + ** sqlcipher_sqlite3VdbeMemClearAndResize() could clobber the value before it is used). */ -#ifndef SQLITE_OMIT_DISKIO - else{ - sqlcipher_sqlite3_vfs *pVfs = db->pVfs; - char *zSuper = 0; /* File-name for the super-journal */ - char const *zMainFile = sqlcipher_sqlite3BtreeGetFilename(db->aDb[0].pBt); - sqlcipher_sqlite3_file *pSuperJrnl = 0; - i64 offset = 0; - int res; - int retryCount = 0; - int nMainFile; - - /* Select a super-journal file name */ - nMainFile = sqlcipher_sqlite3Strlen30(zMainFile); - zSuper = sqlcipher_sqlite3MPrintf(db, "%.4c%s%.16c", 0,zMainFile,0); - if( zSuper==0 ) return SQLITE_NOMEM_BKPT; - zSuper += 4; - do { - u32 iRandom; - if( retryCount ){ - if( retryCount>100 ){ - sqlcipher_sqlite3_log(SQLITE_FULL, "MJ delete: %s", zSuper); - sqlcipher_sqlite3OsDelete(pVfs, zSuper, 0); - break; - }else if( retryCount==1 ){ - sqlcipher_sqlite3_log(SQLITE_FULL, "MJ collide: %s", zSuper); - } - } - retryCount++; - sqlcipher_sqlite3_randomness(sizeof(iRandom), &iRandom); - sqlcipher_sqlite3_snprintf(13, &zSuper[nMainFile], "-mj%06X9%02X", - (iRandom>>8)&0xffffff, iRandom&0xff); - /* The antipenultimate character of the super-journal name must - ** be "9" to avoid name collisions when using 8+3 filenames. */ - assert( zSuper[sqlcipher_sqlite3Strlen30(zSuper)-3]=='9' ); - sqlcipher_sqlite3FileSuffix3(zMainFile, zSuper); - rc = sqlcipher_sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res); - }while( rc==SQLITE_OK && res ); - if( rc==SQLITE_OK ){ - /* Open the super-journal. */ - rc = sqlcipher_sqlite3OsOpenMalloc(pVfs, zSuper, &pSuperJrnl, - SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| - SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_SUPER_JOURNAL, 0 - ); + if( nByte+nZero<=pOut->szMalloc ){ + /* The output register is already large enough to hold the record. + ** No error checks or buffer enlargement is required */ + pOut->z = pOut->zMalloc; + }else{ + /* Need to make sure that the output is not too big and then enlarge + ** the output register to hold the full result */ + if( nByte+nZero>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; } - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3DbFree(db, zSuper-4); - return rc; + if( sqlcipher_sqlite3VdbeMemClearAndResize(pOut, (int)nByte) ){ + goto no_mem; } + } + pOut->n = (int)nByte; + pOut->flags = MEM_Blob; + if( nZero ){ + pOut->u.nZero = nZero; + pOut->flags |= MEM_Zero; + } + UPDATE_MAX_BLOBSIZE(pOut); + zHdr = (u8 *)pOut->z; + zPayload = zHdr + nHdr; - /* Write the name of each database file in the transaction into the new - ** super-journal file. If an error occurs at this point close - ** and delete the super-journal file. All the individual journal files - ** still have 'null' as the super-journal pointer, so they will roll - ** back independently if a failure occurs. - */ - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( sqlcipher_sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){ - char const *zFile = sqlcipher_sqlite3BtreeGetJournalname(pBt); - if( zFile==0 ){ - continue; /* Ignore TEMP and :memory: databases */ + /* Write the record */ + if( nHdr<0x80 ){ + *(zHdr++) = nHdr; + }else{ + zHdr += sqlcipher_sqlite3PutVarint(zHdr,nHdr); + } + assert( pData0<=pLast ); + pRec = pData0; + while( 1 /*exit-by-break*/ ){ + serial_type = pRec->uTemp; + /* EVIDENCE-OF: R-06529-47362 Following the size varint are one or more + ** additional varints, one per column. + ** EVIDENCE-OF: R-64536-51728 The values for each column in the record + ** immediately follow the header. */ + if( serial_type<=7 ){ + *(zHdr++) = serial_type; + if( serial_type==0 ){ + /* NULL value. No change in zPayload */ + }else{ + u64 v; + u32 i; + if( serial_type==7 ){ + assert( sizeof(v)==sizeof(pRec->u.r) ); + memcpy(&v, &pRec->u.r, sizeof(v)); + swapMixedEndianFloat(v); + }else{ + v = pRec->u.i; } - assert( zFile[0]!=0 ); - rc = sqlcipher_sqlite3OsWrite(pSuperJrnl, zFile, sqlcipher_sqlite3Strlen30(zFile)+1,offset); - offset += sqlcipher_sqlite3Strlen30(zFile)+1; - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3OsCloseFree(pSuperJrnl); - sqlcipher_sqlite3OsDelete(pVfs, zSuper, 0); - sqlcipher_sqlite3DbFree(db, zSuper-4); - return rc; + len = i = sqlcipher_sqlite3SmallTypeSizes[serial_type]; + assert( i>0 ); + while( 1 /*exit-by-break*/ ){ + zPayload[--i] = (u8)(v&0xFF); + if( i==0 ) break; + v >>= 8; } + zPayload += len; } - } - - /* Sync the super-journal file. If the IOCAP_SEQUENTIAL device - ** flag is set this is not required. - */ - if( 0==(sqlcipher_sqlite3OsDeviceCharacteristics(pSuperJrnl)&SQLITE_IOCAP_SEQUENTIAL) - && SQLITE_OK!=(rc = sqlcipher_sqlite3OsSync(pSuperJrnl, SQLITE_SYNC_NORMAL)) - ){ - sqlcipher_sqlite3OsCloseFree(pSuperJrnl); - sqlcipher_sqlite3OsDelete(pVfs, zSuper, 0); - sqlcipher_sqlite3DbFree(db, zSuper-4); - return rc; - } - - /* Sync all the db files involved in the transaction. The same call - ** sets the super-journal pointer in each individual journal. If - ** an error occurs here, do not delete the super-journal file. - ** - ** If the error occurs during the first call to - ** sqlcipher_sqlite3BtreeCommitPhaseOne(), then there is a chance that the - ** super-journal file will be orphaned. But we cannot delete it, - ** in case the super-journal file name was written into the journal - ** file before the failure occurred. - */ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = sqlcipher_sqlite3BtreeCommitPhaseOne(pBt, zSuper); + }else if( serial_type<0x80 ){ + *(zHdr++) = serial_type; + if( serial_type>=14 && pRec->n>0 ){ + assert( pRec->z!=0 ); + memcpy(zPayload, pRec->z, pRec->n); + zPayload += pRec->n; } - } - sqlcipher_sqlite3OsCloseFree(pSuperJrnl); - assert( rc!=SQLITE_BUSY ); - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3DbFree(db, zSuper-4); - return rc; - } - - /* Delete the super-journal file. This commits the transaction. After - ** doing this the directory is synced again before any individual - ** transaction files are deleted. - */ - rc = sqlcipher_sqlite3OsDelete(pVfs, zSuper, 1); - sqlcipher_sqlite3DbFree(db, zSuper-4); - zSuper = 0; - if( rc ){ - return rc; - } - - /* All files and directories have already been synced, so the following - ** calls to sqlcipher_sqlite3BtreeCommitPhaseTwo() are only closing files and - ** deleting or truncating journals. If something goes wrong while - ** this is happening we don't really care. The integrity of the - ** transaction is already guaranteed, but some stray 'cold' journals - ** may be lying around. Returning an error code won't help matters. - */ - disable_simulated_io_errors(); - sqlcipher_sqlite3BeginBenignMalloc(); - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - sqlcipher_sqlite3BtreeCommitPhaseTwo(pBt, 1); + }else{ + zHdr += sqlcipher_sqlite3PutVarint(zHdr, serial_type); + if( pRec->n ){ + assert( pRec->z!=0 ); + memcpy(zPayload, pRec->z, pRec->n); + zPayload += pRec->n; } } - sqlcipher_sqlite3EndBenignMalloc(); - enable_simulated_io_errors(); - - sqlcipher_sqlite3VtabCommit(db); + if( pRec==pLast ) break; + pRec++; } -#endif + assert( nHdr==(int)(zHdr - (u8*)pOut->z) ); + assert( nByte==(int)(zPayload - (u8*)pOut->z) ); - return rc; + assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); + REGISTER_TRACE(pOp->p3, pOut); + break; } -/* -** This routine checks that the sqlcipher_sqlite3.nVdbeActive count variable -** matches the number of vdbe's in the list sqlcipher_sqlite3.pVdbe that are -** currently active. An assertion fails if the two counts do not match. -** This is an internal self-check only - it is not an essential processing -** step. +/* Opcode: Count P1 P2 P3 * * +** Synopsis: r[P2]=count() ** -** This is a no-op if NDEBUG is defined. +** Store the number of entries (an integer value) in the table or index +** opened by cursor P1 in register P2. +** +** If P3==0, then an exact count is obtained, which involves visiting +** every btree page of the table. But if P3 is non-zero, an estimate +** is returned based on the current cursor position. */ -#ifndef NDEBUG -static void checkActiveVdbeCnt(sqlcipher_sqlite3 *db){ - Vdbe *p; - int cnt = 0; - int nWrite = 0; - int nRead = 0; - p = db->pVdbe; - while( p ){ - if( sqlcipher_sqlite3_stmt_busy((sqlcipher_sqlite3_stmt*)p) ){ - cnt++; - if( p->readOnly==0 ) nWrite++; - if( p->bIsReader ) nRead++; - } - p = p->pNext; +case OP_Count: { /* out2 */ + i64 nEntry; + BtCursor *pCrsr; + + assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE ); + pCrsr = p->apCsr[pOp->p1]->uc.pCursor; + assert( pCrsr ); + if( pOp->p3 ){ + nEntry = sqlcipher_sqlite3BtreeRowCountEst(pCrsr); + }else{ + nEntry = 0; /* Not needed. Only used to silence a warning. */ + rc = sqlcipher_sqlite3BtreeCount(db, pCrsr, &nEntry); + if( rc ) goto abort_due_to_error; } - assert( cnt==db->nVdbeActive ); - assert( nWrite==db->nVdbeWrite ); - assert( nRead==db->nVdbeRead ); + pOut = out2Prerelease(p, pOp); + pOut->u.i = nEntry; + goto check_for_interrupt; } -#else -#define checkActiveVdbeCnt(x) -#endif -/* -** If the Vdbe passed as the first argument opened a statement-transaction, -** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or -** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement -** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the -** statement transaction is committed. +/* Opcode: Savepoint P1 * * P4 * ** -** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned. -** Otherwise SQLITE_OK. +** Open, release or rollback the savepoint named by parameter P4, depending +** on the value of P1. To open a new savepoint set P1==0 (SAVEPOINT_BEGIN). +** To release (commit) an existing savepoint set P1==1 (SAVEPOINT_RELEASE). +** To rollback an existing savepoint set P1==2 (SAVEPOINT_ROLLBACK). */ -static SQLITE_NOINLINE int vdbeCloseStatement(Vdbe *p, int eOp){ - sqlcipher_sqlite3 *const db = p->db; - int rc = SQLITE_OK; - int i; - const int iSavepoint = p->iStatement-1; +case OP_Savepoint: { + int p1; /* Value of P1 operand */ + char *zName; /* Name of savepoint */ + int nName; + Savepoint *pNew; + Savepoint *pSavepoint; + Savepoint *pTmp; + int iSavepoint; + int ii; - assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE); - assert( db->nStatement>0 ); - assert( p->iStatement==(db->nStatement+db->nSavepoint) ); + p1 = pOp->p1; + zName = pOp->p4.z; - for(i=0; inDb; i++){ - int rc2 = SQLITE_OK; - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - if( eOp==SAVEPOINT_ROLLBACK ){ - rc2 = sqlcipher_sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint); - } - if( rc2==SQLITE_OK ){ - rc2 = sqlcipher_sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint); - } - if( rc==SQLITE_OK ){ - rc = rc2; + /* Assert that the p1 parameter is valid. Also that if there is no open + ** transaction, then there cannot be any savepoints. + */ + assert( db->pSavepoint==0 || db->autoCommit==0 ); + assert( p1==SAVEPOINT_BEGIN||p1==SAVEPOINT_RELEASE||p1==SAVEPOINT_ROLLBACK ); + assert( db->pSavepoint || db->isTransactionSavepoint==0 ); + assert( checkSavepointCount(db) ); + assert( p->bIsReader ); + + if( p1==SAVEPOINT_BEGIN ){ + if( db->nVdbeWrite>0 ){ + /* A new savepoint cannot be created if there are active write + ** statements (i.e. open read/write incremental blob handles). + */ + sqlcipher_sqlite3VdbeError(p, "cannot open savepoint - SQL statements in progress"); + rc = SQLITE_BUSY; + }else{ + nName = sqlcipher_sqlite3Strlen30(zName); + +#ifndef SQLITE_OMIT_VIRTUALTABLE + /* This call is Ok even if this savepoint is actually a transaction + ** savepoint (and therefore should not prompt xSavepoint()) callbacks. + ** If this is a transaction savepoint being opened, it is guaranteed + ** that the db->aVTrans[] array is empty. */ + assert( db->autoCommit==0 || db->nVTrans==0 ); + rc = sqlcipher_sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, + db->nStatement+db->nSavepoint); + if( rc!=SQLITE_OK ) goto abort_due_to_error; +#endif + + /* Create a new savepoint structure. */ + pNew = sqlcipher_sqlite3DbMallocRawNN(db, sizeof(Savepoint)+nName+1); + if( pNew ){ + pNew->zName = (char *)&pNew[1]; + memcpy(pNew->zName, zName, nName+1); + + /* If there is no open transaction, then mark this as a special + ** "transaction savepoint". */ + if( db->autoCommit ){ + db->autoCommit = 0; + db->isTransactionSavepoint = 1; + }else{ + db->nSavepoint++; + } + + /* Link the new savepoint into the database handle's list. */ + pNew->pNext = db->pSavepoint; + db->pSavepoint = pNew; + pNew->nDeferredCons = db->nDeferredCons; + pNew->nDeferredImmCons = db->nDeferredImmCons; } } - } - db->nStatement--; - p->iStatement = 0; + }else{ + assert( p1==SAVEPOINT_RELEASE || p1==SAVEPOINT_ROLLBACK ); + iSavepoint = 0; - if( rc==SQLITE_OK ){ - if( eOp==SAVEPOINT_ROLLBACK ){ - rc = sqlcipher_sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint); - } - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint); + /* Find the named savepoint. If there is no such savepoint, then an + ** an error is returned to the user. */ + for( + pSavepoint = db->pSavepoint; + pSavepoint && sqlcipher_sqlite3StrICmp(pSavepoint->zName, zName); + pSavepoint = pSavepoint->pNext + ){ + iSavepoint++; } - } + if( !pSavepoint ){ + sqlcipher_sqlite3VdbeError(p, "no such savepoint: %s", zName); + rc = SQLITE_ERROR; + }else if( db->nVdbeWrite>0 && p1==SAVEPOINT_RELEASE ){ + /* It is not possible to release (commit) a savepoint if there are + ** active write statements. + */ + sqlcipher_sqlite3VdbeError(p, "cannot release savepoint - " + "SQL statements in progress"); + rc = SQLITE_BUSY; + }else{ - /* If the statement transaction is being rolled back, also restore the - ** database handles deferred constraint counter to the value it had when - ** the statement transaction was opened. */ - if( eOp==SAVEPOINT_ROLLBACK ){ - db->nDeferredCons = p->nStmtDefCons; - db->nDeferredImmCons = p->nStmtDefImmCons; - } - return rc; -} -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ - if( p->db->nStatement && p->iStatement ){ - return vdbeCloseStatement(p, eOp); - } - return SQLITE_OK; -} + /* Determine whether or not this is a transaction savepoint. If so, + ** and this is a RELEASE command, then the current transaction + ** is committed. + */ + int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint; + if( isTransaction && p1==SAVEPOINT_RELEASE ){ + if( (rc = sqlcipher_sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ + goto vdbe_return; + } + db->autoCommit = 1; + if( sqlcipher_sqlite3VdbeHalt(p)==SQLITE_BUSY ){ + p->pc = (int)(pOp - aOp); + db->autoCommit = 0; + p->rc = rc = SQLITE_BUSY; + goto vdbe_return; + } + rc = p->rc; + if( rc ){ + db->autoCommit = 0; + }else{ + db->isTransactionSavepoint = 0; + } + }else{ + int isSchemaChange; + iSavepoint = db->nSavepoint - iSavepoint - 1; + if( p1==SAVEPOINT_ROLLBACK ){ + isSchemaChange = (db->mDbFlags & DBFLAG_SchemaChange)!=0; + for(ii=0; iinDb; ii++){ + rc = sqlcipher_sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, + SQLITE_ABORT_ROLLBACK, + isSchemaChange==0); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + } + }else{ + assert( p1==SAVEPOINT_RELEASE ); + isSchemaChange = 0; + } + for(ii=0; iinDb; ii++){ + rc = sqlcipher_sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } + } + if( isSchemaChange ){ + sqlcipher_sqlite3ExpirePreparedStatements(db, 0); + sqlcipher_sqlite3ResetAllSchemasOfConnection(db); + db->mDbFlags |= DBFLAG_SchemaChange; + } + } + if( rc ) goto abort_due_to_error; + /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all + ** savepoints nested inside of the savepoint being operated on. */ + while( db->pSavepoint!=pSavepoint ){ + pTmp = db->pSavepoint; + db->pSavepoint = pTmp->pNext; + sqlcipher_sqlite3DbFree(db, pTmp); + db->nSavepoint--; + } -/* -** This function is called when a transaction opened by the database -** handle associated with the VM passed as an argument is about to be -** committed. If there are outstanding deferred foreign key constraint -** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK. -** -** If there are outstanding FK violations and this function returns -** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY -** and write an error message to it. Then return SQLITE_ERROR. -*/ -#ifndef SQLITE_OMIT_FOREIGN_KEY -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCheckFk(Vdbe *p, int deferred){ - sqlcipher_sqlite3 *db = p->db; - if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0) - || (!deferred && p->nFkConstraint>0) - ){ - p->rc = SQLITE_CONSTRAINT_FOREIGNKEY; - p->errorAction = OE_Abort; - sqlcipher_sqlite3VdbeError(p, "FOREIGN KEY constraint failed"); - return SQLITE_ERROR; + /* If it is a RELEASE, then destroy the savepoint being operated on + ** too. If it is a ROLLBACK TO, then set the number of deferred + ** constraint violations present in the database to the value stored + ** when the savepoint was created. */ + if( p1==SAVEPOINT_RELEASE ){ + assert( pSavepoint==db->pSavepoint ); + db->pSavepoint = pSavepoint->pNext; + sqlcipher_sqlite3DbFree(db, pSavepoint); + if( !isTransaction ){ + db->nSavepoint--; + } + }else{ + assert( p1==SAVEPOINT_ROLLBACK ); + db->nDeferredCons = pSavepoint->nDeferredCons; + db->nDeferredImmCons = pSavepoint->nDeferredImmCons; + } + + if( !isTransaction || p1==SAVEPOINT_ROLLBACK ){ + rc = sqlcipher_sqlite3VtabSavepoint(db, p1, iSavepoint); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + } + } } - return SQLITE_OK; + if( rc ) goto abort_due_to_error; + if( p->eVdbeState==VDBE_HALT_STATE ){ + rc = SQLITE_DONE; + goto vdbe_return; + } + break; } -#endif -/* -** This routine is called the when a VDBE tries to halt. If the VDBE -** has made changes and is in autocommit mode, then commit those -** changes. If a rollback is needed, then do the rollback. +/* Opcode: AutoCommit P1 P2 * * * ** -** This routine is the only way to move the state of a VM from -** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT. It is harmless to -** call this on a VM that is in the SQLITE_MAGIC_HALT state. +** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll +** back any currently active btree transactions. If there are any active +** VMs (apart from this one), then a ROLLBACK fails. A COMMIT fails if +** there are active writing VMs or active VMs that use shared cache. ** -** Return an error code. If the commit could not complete because of -** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it -** means the close did not happen and needs to be repeated. +** This instruction causes the VM to halt. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeHalt(Vdbe *p){ - int rc; /* Used to store transient return codes */ - sqlcipher_sqlite3 *db = p->db; +case OP_AutoCommit: { + int desiredAutoCommit; + int iRollback; - /* This function contains the logic that determines if a statement or - ** transaction will be committed or rolled back as a result of the - ** execution of this virtual machine. - ** - ** If any of the following errors occur: - ** - ** SQLITE_NOMEM - ** SQLITE_IOERR - ** SQLITE_FULL - ** SQLITE_INTERRUPT - ** - ** Then the internal cache might have been left in an inconsistent - ** state. We need to rollback the statement transaction, if there is - ** one, or the complete transaction if there is no statement transaction. - */ + desiredAutoCommit = pOp->p1; + iRollback = pOp->p2; + assert( desiredAutoCommit==1 || desiredAutoCommit==0 ); + assert( desiredAutoCommit==1 || iRollback==0 ); + assert( db->nVdbeActive>0 ); /* At least this one VM is active */ + assert( p->bIsReader ); - if( p->magic!=VDBE_MAGIC_RUN ){ - return SQLITE_OK; - } - if( db->mallocFailed ){ - p->rc = SQLITE_NOMEM_BKPT; - } - closeAllCursors(p); - checkActiveVdbeCnt(db); + if( desiredAutoCommit!=db->autoCommit ){ + if( iRollback ){ + assert( desiredAutoCommit==1 ); + sqlcipher_sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); + db->autoCommit = 1; + }else if( desiredAutoCommit && db->nVdbeWrite>0 ){ + /* If this instruction implements a COMMIT and other VMs are writing + ** return an error indicating that the other VMs must complete first. + */ + sqlcipher_sqlite3VdbeError(p, "cannot commit transaction - " + "SQL statements in progress"); + rc = SQLITE_BUSY; + goto abort_due_to_error; + }else if( (rc = sqlcipher_sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ + goto vdbe_return; + }else{ + db->autoCommit = (u8)desiredAutoCommit; + } + if( sqlcipher_sqlite3VdbeHalt(p)==SQLITE_BUSY ){ + p->pc = (int)(pOp - aOp); + db->autoCommit = (u8)(1-desiredAutoCommit); + p->rc = rc = SQLITE_BUSY; + goto vdbe_return; + } + sqlcipher_sqlite3CloseSavepoints(db); + if( p->rc==SQLITE_OK ){ + rc = SQLITE_DONE; + }else{ + rc = SQLITE_ERROR; + } + goto vdbe_return; + }else{ + sqlcipher_sqlite3VdbeError(p, + (!desiredAutoCommit)?"cannot start a transaction within a transaction":( + (iRollback)?"cannot rollback - no transaction is active": + "cannot commit - no transaction is active")); - /* No commit or rollback needed if the program never started or if the - ** SQL statement does not read or write a database file. */ - if( p->pc>=0 && p->bIsReader ){ - int mrc; /* Primary error code from p->rc */ - int eStatementOp = 0; - int isSpecialError; /* Set to true if a 'special' error */ + rc = SQLITE_ERROR; + goto abort_due_to_error; + } + /*NOTREACHED*/ assert(0); +} - /* Lock all btrees used by the statement */ - sqlcipher_sqlite3VdbeEnter(p); +/* Opcode: Transaction P1 P2 P3 P4 P5 +** +** Begin a transaction on database P1 if a transaction is not already +** active. +** If P2 is non-zero, then a write-transaction is started, or if a +** read-transaction is already active, it is upgraded to a write-transaction. +** If P2 is zero, then a read-transaction is started. If P2 is 2 or more +** then an exclusive transaction is started. +** +** P1 is the index of the database file on which the transaction is +** started. Index 0 is the main database file and index 1 is the +** file used for temporary tables. Indices of 2 or more are used for +** attached databases. +** +** If a write-transaction is started and the Vdbe.usesStmtJournal flag is +** true (this flag is set if the Vdbe may modify more than one row and may +** throw an ABORT exception), a statement transaction may also be opened. +** More specifically, a statement transaction is opened iff the database +** connection is currently not in autocommit mode, or if there are other +** active statements. A statement transaction allows the changes made by this +** VDBE to be rolled back after an error without having to roll back the +** entire transaction. If no error is encountered, the statement transaction +** will automatically commit when the VDBE halts. +** +** If P5!=0 then this opcode also checks the schema cookie against P3 +** and the schema generation counter against P4. +** The cookie changes its value whenever the database schema changes. +** This operation is used to detect when that the cookie has changed +** and that the current process needs to reread the schema. If the schema +** cookie in P3 differs from the schema cookie in the database header or +** if the schema generation counter in P4 differs from the current +** generation counter, then an SQLITE_SCHEMA error is raised and execution +** halts. The sqlcipher_sqlite3_step() wrapper function might then reprepare the +** statement and rerun it from the beginning. +*/ +case OP_Transaction: { + Btree *pBt; + Db *pDb; + int iMeta = 0; - /* Check for one of the special errors */ - mrc = p->rc & 0xff; - isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR - || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; - if( isSpecialError ){ - /* If the query was read-only and the error code is SQLITE_INTERRUPT, - ** no rollback is necessary. Otherwise, at least a savepoint - ** transaction must be rolled back to restore the database to a - ** consistent state. - ** - ** Even if the statement is read-only, it is important to perform - ** a statement or transaction rollback operation. If the error - ** occurred while writing to the journal, sub-journal or database - ** file as part of an effort to free up cache space (see function - ** pagerStress() in pager.c), the rollback is required to restore - ** the pager to a consistent state. - */ - if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){ - if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){ - eStatementOp = SAVEPOINT_ROLLBACK; - }else{ - /* We are forced to roll back the active transaction. Before doing - ** so, abort any other statements this handle currently has active. - */ - sqlcipher_sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); - sqlcipher_sqlite3CloseSavepoints(db); - db->autoCommit = 1; - p->nChange = 0; - } - } + assert( p->bIsReader ); + assert( p->readOnly==0 || pOp->p2==0 ); + assert( pOp->p2>=0 && pOp->p2<=2 ); + assert( pOp->p1>=0 && pOp->p1nDb ); + assert( DbMaskTest(p->btreeMask, pOp->p1) ); + assert( rc==SQLITE_OK ); + if( pOp->p2 && (db->flags & (SQLITE_QueryOnly|SQLITE_CorruptRdOnly))!=0 ){ + if( db->flags & SQLITE_QueryOnly ){ + /* Writes prohibited by the "PRAGMA query_only=TRUE" statement */ + rc = SQLITE_READONLY; + }else{ + /* Writes prohibited due to a prior SQLITE_CORRUPT in the current + ** transaction */ + rc = SQLITE_CORRUPT; } + goto abort_due_to_error; + } + pDb = &db->aDb[pOp->p1]; + pBt = pDb->pBt; - /* Check for immediate foreign key violations. */ - if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ - sqlcipher_sqlite3VdbeCheckFk(p, 0); + if( pBt ){ + rc = sqlcipher_sqlite3BtreeBeginTrans(pBt, pOp->p2, &iMeta); + testcase( rc==SQLITE_BUSY_SNAPSHOT ); + testcase( rc==SQLITE_BUSY_RECOVERY ); + if( rc!=SQLITE_OK ){ + if( (rc&0xff)==SQLITE_BUSY ){ + p->pc = (int)(pOp - aOp); + p->rc = rc; + goto vdbe_return; + } + goto abort_due_to_error; } - /* If the auto-commit flag is set and this is the only active writer - ** VM, then we do either a commit or rollback of the current transaction. - ** - ** Note: This block also runs if one of the special errors handled - ** above has occurred. - */ - if( !sqlcipher_sqlite3VtabInSync(db) - && db->autoCommit - && db->nVdbeWrite==(p->readOnly==0) + if( p->usesStmtJournal + && pOp->p2 + && (db->autoCommit==0 || db->nVdbeRead>1) ){ - if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ - rc = sqlcipher_sqlite3VdbeCheckFk(p, 1); - if( rc!=SQLITE_OK ){ - if( NEVER(p->readOnly) ){ - sqlcipher_sqlite3VdbeLeave(p); - return SQLITE_ERROR; - } - rc = SQLITE_CONSTRAINT_FOREIGNKEY; - }else{ - /* The auto-commit flag is true, the vdbe program was successful - ** or hit an 'OR FAIL' constraint and there are no deferred foreign - ** key constraints to hold up the transaction. This means a commit - ** is required. */ - rc = vdbeCommit(db, p); - } - if( rc==SQLITE_BUSY && p->readOnly ){ - sqlcipher_sqlite3VdbeLeave(p); - return SQLITE_BUSY; - }else if( rc!=SQLITE_OK ){ - p->rc = rc; - sqlcipher_sqlite3RollbackAll(db, SQLITE_OK); - p->nChange = 0; - }else{ - db->nDeferredCons = 0; - db->nDeferredImmCons = 0; - db->flags &= ~(u64)SQLITE_DeferFKs; - sqlcipher_sqlite3CommitInternalChanges(db); - } - }else{ - sqlcipher_sqlite3RollbackAll(db, SQLITE_OK); - p->nChange = 0; - } - db->nStatement = 0; - }else if( eStatementOp==0 ){ - if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ - eStatementOp = SAVEPOINT_RELEASE; - }else if( p->errorAction==OE_Abort ){ - eStatementOp = SAVEPOINT_ROLLBACK; - }else{ - sqlcipher_sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); - sqlcipher_sqlite3CloseSavepoints(db); - db->autoCommit = 1; - p->nChange = 0; + assert( sqlcipher_sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ); + if( p->iStatement==0 ){ + assert( db->nStatement>=0 && db->nSavepoint>=0 ); + db->nStatement++; + p->iStatement = db->nSavepoint + db->nStatement; } - } - /* If eStatementOp is non-zero, then a statement transaction needs to - ** be committed or rolled back. Call sqlcipher_sqlite3VdbeCloseStatement() to - ** do so. If this operation returns an error, and the current statement - ** error code is SQLITE_OK or SQLITE_CONSTRAINT, then promote the - ** current statement error code. - */ - if( eStatementOp ){ - rc = sqlcipher_sqlite3VdbeCloseStatement(p, eStatementOp); - if( rc ){ - if( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT ){ - p->rc = rc; - sqlcipher_sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = 0; - } - sqlcipher_sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); - sqlcipher_sqlite3CloseSavepoints(db); - db->autoCommit = 1; - p->nChange = 0; + rc = sqlcipher_sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement-1); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3BtreeBeginStmt(pBt, p->iStatement); } - } - /* If this was an INSERT, UPDATE or DELETE and no statement transaction - ** has been rolled back, update the database connection change-counter. - */ - if( p->changeCntOn ){ - if( eStatementOp!=SAVEPOINT_ROLLBACK ){ - sqlcipher_sqlite3VdbeSetChanges(db, p->nChange); - }else{ - sqlcipher_sqlite3VdbeSetChanges(db, 0); - } - p->nChange = 0; + /* Store the current value of the database handles deferred constraint + ** counter. If the statement transaction needs to be rolled back, + ** the value of this counter needs to be restored too. */ + p->nStmtDefCons = db->nDeferredCons; + p->nStmtDefImmCons = db->nDeferredImmCons; } - - /* Release the locks */ - sqlcipher_sqlite3VdbeLeave(p); - } - - /* We have successfully halted and closed the VM. Record this fact. */ - if( p->pc>=0 ){ - db->nVdbeActive--; - if( !p->readOnly ) db->nVdbeWrite--; - if( p->bIsReader ) db->nVdbeRead--; - assert( db->nVdbeActive>=db->nVdbeRead ); - assert( db->nVdbeRead>=db->nVdbeWrite ); - assert( db->nVdbeWrite>=0 ); - } - p->magic = VDBE_MAGIC_HALT; - checkActiveVdbeCnt(db); - if( db->mallocFailed ){ - p->rc = SQLITE_NOMEM_BKPT; } + assert( pOp->p5==0 || pOp->p4type==P4_INT32 ); + if( rc==SQLITE_OK + && pOp->p5 + && (iMeta!=pOp->p3 || pDb->pSchema->iGeneration!=pOp->p4.i) + ){ + /* + ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema + ** version is checked to ensure that the schema has not changed since the + ** SQL statement was prepared. + */ + sqlcipher_sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = sqlcipher_sqlite3DbStrDup(db, "database schema has changed"); + /* If the schema-cookie from the database file matches the cookie + ** stored with the in-memory representation of the schema, do + ** not reload the schema from the database file. + ** + ** If virtual-tables are in use, this is not just an optimization. + ** Often, v-tables store their data in other SQLite tables, which + ** are queried from within xNext() and other v-table methods using + ** prepared queries. If such a query is out-of-date, we do not want to + ** discard the database schema, as the user code implementing the + ** v-table would have to be ready for the sqlcipher_sqlite3_vtab structure itself + ** to be invalidated whenever sqlcipher_sqlite3_step() is called from within + ** a v-table method. + */ + if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){ + sqlcipher_sqlite3ResetOneSchema(db, pOp->p1); + } + p->expired = 1; + rc = SQLITE_SCHEMA; - /* If the auto-commit flag is set to true, then any locks that were held - ** by connection db have now been released. Call sqlcipher_sqlite3ConnectionUnlocked() - ** to invoke any required unlock-notify callbacks. - */ - if( db->autoCommit ){ - sqlcipher_sqlite3ConnectionUnlocked(db); + /* Set changeCntOn to 0 to prevent the value returned by sqlcipher_sqlite3_changes() + ** from being modified in sqlcipher_sqlite3VdbeHalt(). If this statement is + ** reprepared, changeCntOn will be set again. */ + p->changeCntOn = 0; } - - assert( db->nVdbeActive>0 || db->autoCommit==0 || db->nStatement==0 ); - return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK); + if( rc ) goto abort_due_to_error; + break; } - -/* -** Each VDBE holds the result of the most recent sqlcipher_sqlite3_step() call -** in p->rc. This routine sets that result back to SQLITE_OK. +/* Opcode: ReadCookie P1 P2 P3 * * +** +** Read cookie number P3 from database P1 and write it into register P2. +** P3==1 is the schema version. P3==2 is the database format. +** P3==3 is the recommended pager cache size, and so forth. P1==0 is +** the main database file and P1==1 is the database file used to store +** temporary tables. +** +** There must be a read-lock on the database (either a transaction +** must be started or there must be an open cursor) before +** executing this instruction. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeResetStepResult(Vdbe *p){ - p->rc = SQLITE_OK; +case OP_ReadCookie: { /* out2 */ + int iMeta; + int iDb; + int iCookie; + + assert( p->bIsReader ); + iDb = pOp->p1; + iCookie = pOp->p3; + assert( pOp->p3=0 && iDbnDb ); + assert( db->aDb[iDb].pBt!=0 ); + assert( DbMaskTest(p->btreeMask, iDb) ); + + sqlcipher_sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta); + pOut = out2Prerelease(p, pOp); + pOut->u.i = iMeta; + break; } -/* -** Copy the error code and error message belonging to the VDBE passed -** as the first argument to its database handle (so that they will be -** returned by calls to sqlcipher_sqlite3_errcode() and sqlcipher_sqlite3_errmsg()). +/* Opcode: SetCookie P1 P2 P3 * P5 ** -** This function does not clear the VDBE error code or message, just -** copies them to the database handle. +** Write the integer value P3 into cookie number P2 of database P1. +** P2==1 is the schema version. P2==2 is the database format. +** P2==3 is the recommended pager cache +** size, and so forth. P1==0 is the main database file and P1==1 is the +** database file used to store temporary tables. +** +** A transaction must be started before executing this opcode. +** +** If P2 is the SCHEMA_VERSION cookie (cookie number 1) then the internal +** schema version is set to P3-P5. The "PRAGMA schema_version=N" statement +** has P5 set to 1, so that the internal schema version will be different +** from the database schema version, resulting in a schema reset. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeTransferError(Vdbe *p){ - sqlcipher_sqlite3 *db = p->db; - int rc = p->rc; - if( p->zErrMsg ){ - db->bBenignMalloc++; - sqlcipher_sqlite3BeginBenignMalloc(); - if( db->pErr==0 ) db->pErr = sqlcipher_sqlite3ValueNew(db); - sqlcipher_sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); - sqlcipher_sqlite3EndBenignMalloc(); - db->bBenignMalloc--; - }else if( db->pErr ){ - sqlcipher_sqlite3ValueSetNull(db->pErr); - } - db->errCode = rc; - return rc; -} +case OP_SetCookie: { + Db *pDb; -#ifdef SQLITE_ENABLE_SQLLOG -/* -** If an SQLITE_CONFIG_SQLLOG hook is registered and the VM has been run, -** invoke it. -*/ -static void vdbeInvokeSqllog(Vdbe *v){ - if( sqlcipher_sqlite3GlobalConfig.xSqllog && v->rc==SQLITE_OK && v->zSql && v->pc>=0 ){ - char *zExpanded = sqlcipher_sqlite3VdbeExpandSql(v, v->zSql); - assert( v->db->init.busy==0 ); - if( zExpanded ){ - sqlcipher_sqlite3GlobalConfig.xSqllog( - sqlcipher_sqlite3GlobalConfig.pSqllogArg, v->db, zExpanded, 1 - ); - sqlcipher_sqlite3DbFree(v->db, zExpanded); - } + sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); + assert( pOp->p2p1>=0 && pOp->p1nDb ); + assert( DbMaskTest(p->btreeMask, pOp->p1) ); + assert( p->readOnly==0 ); + pDb = &db->aDb[pOp->p1]; + assert( pDb->pBt!=0 ); + assert( sqlcipher_sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); + /* See note about index shifting on OP_ReadCookie */ + rc = sqlcipher_sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3); + if( pOp->p2==BTREE_SCHEMA_VERSION ){ + /* When the schema cookie changes, record the new cookie internally */ + *(u32*)&pDb->pSchema->schema_cookie = *(u32*)&pOp->p3 - pOp->p5; + db->mDbFlags |= DBFLAG_SchemaChange; + sqlcipher_sqlite3FkClearTriggerCache(db, pOp->p1); + }else if( pOp->p2==BTREE_FILE_FORMAT ){ + /* Record changes in the file format */ + pDb->pSchema->file_format = pOp->p3; + } + if( pOp->p1==1 ){ + /* Invalidate all prepared statements whenever the TEMP database + ** schema is changed. Ticket #1644 */ + sqlcipher_sqlite3ExpirePreparedStatements(db, 0); + p->expired = 0; } + if( rc ) goto abort_due_to_error; + break; } -#else -# define vdbeInvokeSqllog(x) -#endif -/* -** Clean up a VDBE after execution but do not delete the VDBE just yet. -** Write any error messages into *pzErrMsg. Return the result code. +/* Opcode: OpenRead P1 P2 P3 P4 P5 +** Synopsis: root=P2 iDb=P3 ** -** After this routine is run, the VDBE should be ready to be executed -** again. +** Open a read-only cursor for the database table whose root page is +** P2 in a database file. The database file is determined by P3. +** P3==0 means the main database, P3==1 means the database used for +** temporary tables, and P3>1 means used the corresponding attached +** database. Give the new cursor an identifier of P1. The P1 +** values need not be contiguous but all P1 values should be small integers. +** It is an error for P1 to be negative. ** -** To look at it another way, this routine resets the state of the -** virtual machine from VDBE_MAGIC_RUN or VDBE_MAGIC_HALT back to -** VDBE_MAGIC_INIT. +** Allowed P5 bits: +**
      +**
    • 0x02 OPFLAG_SEEKEQ: This cursor will only be used for +** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT +** of OP_SeekLE/OP_IdxLT) +**
    +** +** The P4 value may be either an integer (P4_INT32) or a pointer to +** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo +** object, then table being opened must be an [index b-tree] where the +** KeyInfo object defines the content and collating +** sequence of that index b-tree. Otherwise, if P4 is an integer +** value, then the table being opened must be a [table b-tree] with a +** number of columns no less than the value of P4. +** +** See also: OpenWrite, ReopenIdx */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeReset(Vdbe *p){ -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) - int i; -#endif +/* Opcode: ReopenIdx P1 P2 P3 P4 P5 +** Synopsis: root=P2 iDb=P3 +** +** The ReopenIdx opcode works like OP_OpenRead except that it first +** checks to see if the cursor on P1 is already open on the same +** b-tree and if it is this opcode becomes a no-op. In other words, +** if the cursor is already open, do not reopen it. +** +** The ReopenIdx opcode may only be used with P5==0 or P5==OPFLAG_SEEKEQ +** and with P4 being a P4_KEYINFO object. Furthermore, the P3 value must +** be the same as every other ReopenIdx or OpenRead for the same cursor +** number. +** +** Allowed P5 bits: +**
      +**
    • 0x02 OPFLAG_SEEKEQ: This cursor will only be used for +** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT +** of OP_SeekLE/OP_IdxLT) +**
    +** +** See also: OP_OpenRead, OP_OpenWrite +*/ +/* Opcode: OpenWrite P1 P2 P3 P4 P5 +** Synopsis: root=P2 iDb=P3 +** +** Open a read/write cursor named P1 on the table or index whose root +** page is P2 (or whose root page is held in register P2 if the +** OPFLAG_P2ISREG bit is set in P5 - see below). +** +** The P4 value may be either an integer (P4_INT32) or a pointer to +** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo +** object, then table being opened must be an [index b-tree] where the +** KeyInfo object defines the content and collating +** sequence of that index b-tree. Otherwise, if P4 is an integer +** value, then the table being opened must be a [table b-tree] with a +** number of columns no less than the value of P4. +** +** Allowed P5 bits: +**
      +**
    • 0x02 OPFLAG_SEEKEQ: This cursor will only be used for +** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT +** of OP_SeekLE/OP_IdxLT) +**
    • 0x08 OPFLAG_FORDELETE: This cursor is used only to seek +** and subsequently delete entries in an index btree. This is a +** hint to the storage engine that the storage engine is allowed to +** ignore. The hint is not used by the official SQLite b*tree storage +** engine, but is used by COMDB2. +**
    • 0x10 OPFLAG_P2ISREG: Use the content of register P2 +** as the root page, not the value of P2 itself. +**
    +** +** This instruction works like OpenRead except that it opens the cursor +** in read/write mode. +** +** See also: OP_OpenRead, OP_ReopenIdx +*/ +case OP_ReopenIdx: { + int nField; + KeyInfo *pKeyInfo; + u32 p2; + int iDb; + int wrFlag; + Btree *pX; + VdbeCursor *pCur; + Db *pDb; - sqlcipher_sqlite3 *db; - db = p->db; + assert( pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); + assert( pOp->p4type==P4_KEYINFO ); + pCur = p->apCsr[pOp->p1]; + if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){ + assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */ + assert( pCur->eCurType==CURTYPE_BTREE ); + sqlcipher_sqlite3BtreeClearCursor(pCur->uc.pCursor); + goto open_cursor_set_hints; + } + /* If the cursor is not currently open or is open on a different + ** index, then fall through into OP_OpenRead to force a reopen */ +case OP_OpenRead: +case OP_OpenWrite: - /* If the VM did not run to completion or if it encountered an - ** error, then it might not have been halted properly. So halt - ** it now. - */ - sqlcipher_sqlite3VdbeHalt(p); + assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); + assert( p->bIsReader ); + assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx + || p->readOnly==0 ); - /* If the VDBE has been run even partially, then transfer the error code - ** and error message from the VDBE into the main database structure. But - ** if the VDBE has just been set to run but has not actually executed any - ** instructions yet, leave the main database error information unchanged. - */ - if( p->pc>=0 ){ - vdbeInvokeSqllog(p); - if( db->pErr || p->zErrMsg ){ - sqlcipher_sqlite3VdbeTransferError(p); - }else{ - db->errCode = p->rc; - } - if( p->runOnlyOnce ) p->expired = 1; - }else if( p->rc && p->expired ){ - /* The expired flag was set on the VDBE before the first call - ** to sqlcipher_sqlite3_step(). For consistency (since sqlcipher_sqlite3_step() was - ** called), set the database error in this case as well. - */ - sqlcipher_sqlite3ErrorWithMsg(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg); + if( p->expired==1 ){ + rc = SQLITE_ABORT_ROLLBACK; + goto abort_due_to_error; } - /* Reset register contents and reclaim error message memory. - */ -#ifdef SQLITE_DEBUG - /* Execute assert() statements to ensure that the Vdbe.apCsr[] and - ** Vdbe.aMem[] arrays have already been cleaned up. */ - if( p->apCsr ) for(i=0; inCursor; i++) assert( p->apCsr[i]==0 ); - if( p->aMem ){ - for(i=0; inMem; i++) assert( p->aMem[i].flags==MEM_Undefined ); + nField = 0; + pKeyInfo = 0; + p2 = (u32)pOp->p2; + iDb = pOp->p3; + assert( iDb>=0 && iDbnDb ); + assert( DbMaskTest(p->btreeMask, iDb) ); + pDb = &db->aDb[iDb]; + pX = pDb->pBt; + assert( pX!=0 ); + if( pOp->opcode==OP_OpenWrite ){ + assert( OPFLAG_FORDELETE==BTREE_FORDELETE ); + wrFlag = BTREE_WRCSR | (pOp->p5 & OPFLAG_FORDELETE); + assert( sqlcipher_sqlite3SchemaMutexHeld(db, iDb, 0) ); + if( pDb->pSchema->file_format < p->minWriteFileFormat ){ + p->minWriteFileFormat = pDb->pSchema->file_format; + } + }else{ + wrFlag = 0; } -#endif - if( p->zErrMsg ){ - sqlcipher_sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = 0; + if( pOp->p5 & OPFLAG_P2ISREG ){ + assert( p2>0 ); + assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); + assert( pOp->opcode==OP_OpenWrite ); + pIn2 = &aMem[p2]; + assert( memIsValid(pIn2) ); + assert( (pIn2->flags & MEM_Int)!=0 ); + sqlcipher_sqlite3VdbeMemIntegerify(pIn2); + p2 = (int)pIn2->u.i; + /* The p2 value always comes from a prior OP_CreateBtree opcode and + ** that opcode will always set the p2 value to 2 or more or else fail. + ** If there were a failure, the prepared statement would have halted + ** before reaching this instruction. */ + assert( p2>=2 ); } - p->pResultSet = 0; + if( pOp->p4type==P4_KEYINFO ){ + pKeyInfo = pOp->p4.pKeyInfo; + assert( pKeyInfo->enc==ENC(db) ); + assert( pKeyInfo->db==db ); + nField = pKeyInfo->nAllField; + }else if( pOp->p4type==P4_INT32 ){ + nField = pOp->p4.i; + } + assert( pOp->p1>=0 ); + assert( nField>=0 ); + testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */ + pCur = allocateCursor(p, pOp->p1, nField, CURTYPE_BTREE); + if( pCur==0 ) goto no_mem; + pCur->iDb = iDb; + pCur->nullRow = 1; + pCur->isOrdered = 1; + pCur->pgnoRoot = p2; #ifdef SQLITE_DEBUG - p->nWrite = 0; + pCur->wrFlag = wrFlag; #endif + rc = sqlcipher_sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->uc.pCursor); + pCur->pKeyInfo = pKeyInfo; + /* Set the VdbeCursor.isTable variable. Previous versions of + ** SQLite used to check if the root-page flags were sane at this point + ** and report database corruption if they were not, but this check has + ** since moved into the btree layer. */ + pCur->isTable = pOp->p4type!=P4_KEYINFO; - /* Save profiling information from this VDBE run. - */ -#ifdef VDBE_PROFILE - { - FILE *out = fopen("vdbe_profile.out", "a"); - if( out ){ - fprintf(out, "---- "); - for(i=0; inOp; i++){ - fprintf(out, "%02x", p->aOp[i].opcode); - } - fprintf(out, "\n"); - if( p->zSql ){ - char c, pc = 0; - fprintf(out, "-- "); - for(i=0; (c = p->zSql[i])!=0; i++){ - if( pc=='\n' ) fprintf(out, "-- "); - putc(c, out); - pc = c; - } - if( pc!='\n' ) fprintf(out, "\n"); - } - for(i=0; inOp; i++){ - char zHdr[100]; - sqlcipher_sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ", - p->aOp[i].cnt, - p->aOp[i].cycles, - p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0 - ); - fprintf(out, "%s", zHdr); - sqlcipher_sqlite3VdbePrintOp(out, i, &p->aOp[i]); - } - fclose(out); - } - } -#endif - p->magic = VDBE_MAGIC_RESET; - return p->rc & db->errMask; +open_cursor_set_hints: + assert( OPFLAG_BULKCSR==BTREE_BULKLOAD ); + assert( OPFLAG_SEEKEQ==BTREE_SEEK_EQ ); + testcase( pOp->p5 & OPFLAG_BULKCSR ); + testcase( pOp->p2 & OPFLAG_SEEKEQ ); + sqlcipher_sqlite3BtreeCursorHintFlags(pCur->uc.pCursor, + (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ))); + if( rc ) goto abort_due_to_error; + break; } -/* -** Clean up and delete a VDBE after execution. Return an integer which is -** the result code. Write any error message text into *pzErrMsg. +/* Opcode: OpenDup P1 P2 * * * +** +** Open a new cursor P1 that points to the same ephemeral table as +** cursor P2. The P2 cursor must have been opened by a prior OP_OpenEphemeral +** opcode. Only ephemeral cursors may be duplicated. +** +** Duplicate ephemeral cursors are used for self-joins of materialized views. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeFinalize(Vdbe *p){ - int rc = SQLITE_OK; - if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){ - rc = sqlcipher_sqlite3VdbeReset(p); - assert( (rc & p->db->errMask)==rc ); - } - sqlcipher_sqlite3VdbeDelete(p); - return rc; +case OP_OpenDup: { + VdbeCursor *pOrig; /* The original cursor to be duplicated */ + VdbeCursor *pCx; /* The new cursor */ + + pOrig = p->apCsr[pOp->p2]; + assert( pOrig ); + assert( pOrig->isEphemeral ); /* Only ephemeral cursors can be duplicated */ + + pCx = allocateCursor(p, pOp->p1, pOrig->nField, CURTYPE_BTREE); + if( pCx==0 ) goto no_mem; + pCx->nullRow = 1; + pCx->isEphemeral = 1; + pCx->pKeyInfo = pOrig->pKeyInfo; + pCx->isTable = pOrig->isTable; + pCx->pgnoRoot = pOrig->pgnoRoot; + pCx->isOrdered = pOrig->isOrdered; + pCx->ub.pBtx = pOrig->ub.pBtx; + pCx->noReuse = 1; + pOrig->noReuse = 1; + rc = sqlcipher_sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR, + pCx->pKeyInfo, pCx->uc.pCursor); + /* The sqlcipher_sqlite3BtreeCursor() routine can only fail for the first cursor + ** opened for a database. Since there is already an open cursor when this + ** opcode is run, the sqlcipher_sqlite3BtreeCursor() cannot fail */ + assert( rc==SQLITE_OK ); + break; } -/* -** If parameter iOp is less than zero, then invoke the destructor for -** all auxiliary data pointers currently cached by the VM passed as -** the first argument. + +/* Opcode: OpenEphemeral P1 P2 P3 P4 P5 +** Synopsis: nColumn=P2 ** -** Or, if iOp is greater than or equal to zero, then the destructor is -** only invoked for those auxiliary data pointers created by the user -** function invoked by the OP_Function opcode at instruction iOp of -** VM pVdbe, and only then if: +** Open a new cursor P1 to a transient table. +** The cursor is always opened read/write even if +** the main database is read-only. The ephemeral +** table is deleted automatically when the cursor is closed. ** -** * the associated function parameter is the 32nd or later (counting -** from left to right), or +** If the cursor P1 is already opened on an ephemeral table, the table +** is cleared (all content is erased). ** -** * the corresponding bit in argument mask is clear (where the first -** function parameter corresponds to bit 0 etc.). +** P2 is the number of columns in the ephemeral table. +** The cursor points to a BTree table if P4==0 and to a BTree index +** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure +** that defines the format of keys in the index. +** +** The P5 parameter can be a mask of the BTREE_* flags defined +** in btree.h. These flags control aspects of the operation of +** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are +** added automatically. +** +** If P3 is positive, then reg[P3] is modified slightly so that it +** can be used as zero-length data for OP_Insert. This is an optimization +** that avoids an extra OP_Blob opcode to initialize that register. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeDeleteAuxData(sqlcipher_sqlite3 *db, AuxData **pp, int iOp, int mask){ - while( *pp ){ - AuxData *pAux = *pp; - if( (iOp<0) - || (pAux->iAuxOp==iOp - && pAux->iAuxArg>=0 - && (pAux->iAuxArg>31 || !(mask & MASKBIT32(pAux->iAuxArg)))) - ){ - testcase( pAux->iAuxArg==31 ); - if( pAux->xDeleteAux ){ - pAux->xDeleteAux(pAux->pAux); - } - *pp = pAux->pNextAux; - sqlcipher_sqlite3DbFree(db, pAux); - }else{ - pp= &pAux->pNextAux; - } - } -} - -/* -** Free all memory associated with the Vdbe passed as the second argument, -** except for object itself, which is preserved. +/* Opcode: OpenAutoindex P1 P2 * P4 * +** Synopsis: nColumn=P2 ** -** The difference between this function and sqlcipher_sqlite3VdbeDelete() is that -** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with -** the database connection and frees the object itself. +** This opcode works the same as OP_OpenEphemeral. It has a +** different name to distinguish its use. Tables created using +** by this opcode will be used for automatically created transient +** indices in joins. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeClearObject(sqlcipher_sqlite3 *db, Vdbe *p){ - SubProgram *pSub, *pNext; - assert( p->db==0 || p->db==db ); - releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); - for(pSub=p->pProgram; pSub; pSub=pNext){ - pNext = pSub->pNext; - vdbeFreeOpArray(db, pSub->aOp, pSub->nOp); - sqlcipher_sqlite3DbFree(db, pSub); - } - if( p->magic!=VDBE_MAGIC_INIT ){ - releaseMemArray(p->aVar, p->nVar); - sqlcipher_sqlite3DbFree(db, p->pVList); - sqlcipher_sqlite3DbFree(db, p->pFree); - } - vdbeFreeOpArray(db, p->aOp, p->nOp); - sqlcipher_sqlite3DbFree(db, p->aColName); - sqlcipher_sqlite3DbFree(db, p->zSql); -#ifdef SQLITE_ENABLE_NORMALIZE - sqlcipher_sqlite3DbFree(db, p->zNormSql); - { - DblquoteStr *pThis, *pNext; - for(pThis=p->pDblStr; pThis; pThis=pNext){ - pNext = pThis->pNextStr; - sqlcipher_sqlite3DbFree(db, pThis); - } +case OP_OpenAutoindex: +case OP_OpenEphemeral: { + VdbeCursor *pCx; + KeyInfo *pKeyInfo; + + static const int vfsFlags = + SQLITE_OPEN_READWRITE | + SQLITE_OPEN_CREATE | + SQLITE_OPEN_EXCLUSIVE | + SQLITE_OPEN_DELETEONCLOSE | + SQLITE_OPEN_TRANSIENT_DB; + assert( pOp->p1>=0 ); + assert( pOp->p2>=0 ); + if( pOp->p3>0 ){ + /* Make register reg[P3] into a value that can be used as the data + ** form sqlcipher_sqlite3BtreeInsert() where the length of the data is zero. */ + assert( pOp->p2==0 ); /* Only used when number of columns is zero */ + assert( pOp->opcode==OP_OpenEphemeral ); + assert( aMem[pOp->p3].flags & MEM_Null ); + aMem[pOp->p3].n = 0; + aMem[pOp->p3].z = ""; } -#endif -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - { - int i; - for(i=0; inScan; i++){ - sqlcipher_sqlite3DbFree(db, p->aScan[i].zName); + pCx = p->apCsr[pOp->p1]; + if( pCx && !pCx->noReuse && ALWAYS(pOp->p2<=pCx->nField) ){ + /* If the ephermeral table is already open and has no duplicates from + ** OP_OpenDup, then erase all existing content so that the table is + ** empty again, rather than creating a new table. */ + assert( pCx->isEphemeral ); + pCx->seqCount = 0; + pCx->cacheStatus = CACHE_STALE; + rc = sqlcipher_sqlite3BtreeClearTable(pCx->ub.pBtx, pCx->pgnoRoot, 0); + }else{ + pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_BTREE); + if( pCx==0 ) goto no_mem; + pCx->isEphemeral = 1; + rc = sqlcipher_sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->ub.pBtx, + BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, + vfsFlags); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3BtreeBeginTrans(pCx->ub.pBtx, 1, 0); + if( rc==SQLITE_OK ){ + /* If a transient index is required, create it by calling + ** sqlcipher_sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before + ** opening it. If a transient table is required, just use the + ** automatically created table with root-page 1 (an BLOB_INTKEY table). + */ + if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ + assert( pOp->p4type==P4_KEYINFO ); + rc = sqlcipher_sqlite3BtreeCreateTable(pCx->ub.pBtx, &pCx->pgnoRoot, + BTREE_BLOBKEY | pOp->p5); + if( rc==SQLITE_OK ){ + assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); + assert( pKeyInfo->db==db ); + assert( pKeyInfo->enc==ENC(db) ); + rc = sqlcipher_sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR, + pKeyInfo, pCx->uc.pCursor); + } + pCx->isTable = 0; + }else{ + pCx->pgnoRoot = SCHEMA_ROOT; + rc = sqlcipher_sqlite3BtreeCursor(pCx->ub.pBtx, SCHEMA_ROOT, BTREE_WRCSR, + 0, pCx->uc.pCursor); + pCx->isTable = 1; + } + } + pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); + if( rc ){ + sqlcipher_sqlite3BtreeClose(pCx->ub.pBtx); + } } - sqlcipher_sqlite3DbFree(db, p->aScan); } -#endif + if( rc ) goto abort_due_to_error; + pCx->nullRow = 1; + break; } -/* -** Delete an entire VDBE. +/* Opcode: SorterOpen P1 P2 P3 P4 * +** +** This opcode works like OP_OpenEphemeral except that it opens +** a transient index that is specifically designed to sort large +** tables using an external merge-sort algorithm. +** +** If argument P3 is non-zero, then it indicates that the sorter may +** assume that a stable sort considering the first P3 fields of each +** key is sufficient to produce the required results. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeDelete(Vdbe *p){ - sqlcipher_sqlite3 *db; +case OP_SorterOpen: { + VdbeCursor *pCx; - assert( p!=0 ); - db = p->db; - assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - sqlcipher_sqlite3VdbeClearObject(db, p); - if( p->pPrev ){ - p->pPrev->pNext = p->pNext; - }else{ - assert( db->pVdbe==p ); - db->pVdbe = p->pNext; - } - if( p->pNext ){ - p->pNext->pPrev = p->pPrev; - } - p->magic = VDBE_MAGIC_DEAD; - p->db = 0; - sqlcipher_sqlite3DbFreeNN(db, p); + assert( pOp->p1>=0 ); + assert( pOp->p2>=0 ); + pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_SORTER); + if( pCx==0 ) goto no_mem; + pCx->pKeyInfo = pOp->p4.pKeyInfo; + assert( pCx->pKeyInfo->db==db ); + assert( pCx->pKeyInfo->enc==ENC(db) ); + rc = sqlcipher_sqlite3VdbeSorterInit(db, pOp->p3, pCx); + if( rc ) goto abort_due_to_error; + break; } -/* -** The cursor "p" has a pending seek operation that has not yet been -** carried out. Seek the cursor now. If an error occurs, return -** the appropriate error code. +/* Opcode: SequenceTest P1 P2 * * * +** Synopsis: if( cursor[P1].ctr++ ) pc = P2 +** +** P1 is a sorter cursor. If the sequence counter is currently zero, jump +** to P2. Regardless of whether or not the jump is taken, increment the +** the sequence value. */ -SQLITE_PRIVATE int SQLITE_NOINLINE sqlcipher_sqlite3VdbeFinishMoveto(VdbeCursor *p){ - int res, rc; -#ifdef SQLITE_TEST - extern int sqlcipher_sqlite3_search_count; -#endif - assert( p->deferredMoveto ); - assert( p->isTable ); - assert( p->eCurType==CURTYPE_BTREE ); - rc = sqlcipher_sqlite3BtreeMovetoUnpacked(p->uc.pCursor, 0, p->movetoTarget, 0, &res); - if( rc ) return rc; - if( res!=0 ) return SQLITE_CORRUPT_BKPT; -#ifdef SQLITE_TEST - sqlcipher_sqlite3_search_count++; -#endif - p->deferredMoveto = 0; - p->cacheStatus = CACHE_STALE; - return SQLITE_OK; +case OP_SequenceTest: { + VdbeCursor *pC; + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( isSorter(pC) ); + if( (pC->seqCount++)==0 ){ + goto jump_to_p2; + } + break; } -/* -** Something has moved cursor "p" out of place. Maybe the row it was -** pointed to was deleted out from under it. Or maybe the btree was -** rebalanced. Whatever the cause, try to restore "p" to the place it -** is supposed to be pointing. If the row was deleted out from under the -** cursor, set the cursor to point to a NULL row. +/* Opcode: OpenPseudo P1 P2 P3 * * +** Synopsis: P3 columns in r[P2] +** +** Open a new cursor that points to a fake table that contains a single +** row of data. The content of that one row is the content of memory +** register P2. In other words, cursor P1 becomes an alias for the +** MEM_Blob content contained in register P2. +** +** A pseudo-table created by this opcode is used to hold a single +** row output from the sorter so that the row can be decomposed into +** individual columns using the OP_Column opcode. The OP_Column opcode +** is the only cursor opcode that works with a pseudo-table. +** +** P3 is the number of fields in the records that will be stored by +** the pseudo-table. */ -static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){ - int isDifferentRow, rc; - assert( p->eCurType==CURTYPE_BTREE ); - assert( p->uc.pCursor!=0 ); - assert( sqlcipher_sqlite3BtreeCursorHasMoved(p->uc.pCursor) ); - rc = sqlcipher_sqlite3BtreeCursorRestore(p->uc.pCursor, &isDifferentRow); - p->cacheStatus = CACHE_STALE; - if( isDifferentRow ) p->nullRow = 1; - return rc; +case OP_OpenPseudo: { + VdbeCursor *pCx; + + assert( pOp->p1>=0 ); + assert( pOp->p3>=0 ); + pCx = allocateCursor(p, pOp->p1, pOp->p3, CURTYPE_PSEUDO); + if( pCx==0 ) goto no_mem; + pCx->nullRow = 1; + pCx->seekResult = pOp->p2; + pCx->isTable = 1; + /* Give this pseudo-cursor a fake BtCursor pointer so that pCx + ** can be safely passed to sqlcipher_sqlite3VdbeCursorMoveto(). This avoids a test + ** for pCx->eCurType==CURTYPE_BTREE inside of sqlcipher_sqlite3VdbeCursorMoveto() + ** which is a performance optimization */ + pCx->uc.pCursor = sqlcipher_sqlite3BtreeFakeValidCursor(); + assert( pOp->p5==0 ); + break; } -/* -** Check to ensure that the cursor is valid. Restore the cursor -** if need be. Return any I/O error from the restore operation. +/* Opcode: Close P1 * * * * +** +** Close a cursor previously opened as P1. If P1 is not +** currently open, this instruction is a no-op. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCursorRestore(VdbeCursor *p){ - assert( p->eCurType==CURTYPE_BTREE ); - if( sqlcipher_sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ - return handleMovedCursor(p); - } - return SQLITE_OK; +case OP_Close: { + assert( pOp->p1>=0 && pOp->p1nCursor ); + sqlcipher_sqlite3VdbeFreeCursor(p, p->apCsr[pOp->p1]); + p->apCsr[pOp->p1] = 0; + break; } -/* -** Make sure the cursor p is ready to read or write the row to which it -** was last positioned. Return an error code if an OOM fault or I/O error -** prevents us from positioning the cursor to its correct position. -** -** If a MoveTo operation is pending on the given cursor, then do that -** MoveTo now. If no move is pending, check to see if the row has been -** deleted out from under the cursor and if it has, mark the row as -** a NULL row. +#ifdef SQLITE_ENABLE_COLUMN_USED_MASK +/* Opcode: ColumnsUsed P1 * * P4 * ** -** If the cursor is already pointing to the correct row and that row has -** not been deleted out from under the cursor, then this routine is a no-op. +** This opcode (which only exists if SQLite was compiled with +** SQLITE_ENABLE_COLUMN_USED_MASK) identifies which columns of the +** table or index for cursor P1 are used. P4 is a 64-bit integer +** (P4_INT64) in which the first 63 bits are one for each of the +** first 63 columns of the table or index that are actually used +** by the cursor. The high-order bit is set if any column after +** the 64th is used. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ - VdbeCursor *p = *pp; - assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); - if( p->deferredMoveto ){ - u32 iMap; - if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ - *pp = p->pAltCursor; - *piCol = iMap - 1; - return SQLITE_OK; - } - return sqlcipher_sqlite3VdbeFinishMoveto(p); - } - if( sqlcipher_sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ - return handleMovedCursor(p); - } - return SQLITE_OK; +case OP_ColumnsUsed: { + VdbeCursor *pC; + pC = p->apCsr[pOp->p1]; + assert( pC->eCurType==CURTYPE_BTREE ); + pC->maskUsed = *(u64*)pOp->p4.pI64; + break; } +#endif -/* -** The following functions: -** -** sqlcipher_sqlite3VdbeSerialType() -** sqlcipher_sqlite3VdbeSerialTypeLen() -** sqlcipher_sqlite3VdbeSerialLen() -** sqlcipher_sqlite3VdbeSerialPut() -** sqlcipher_sqlite3VdbeSerialGet() +/* Opcode: SeekGE P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] ** -** encapsulate the code that serializes values for storage in SQLite -** data and index records. Each serialized value consists of a -** 'serial-type' and a blob of data. The serial type is an 8-byte unsigned -** integer, stored as a varint. +** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), +** use the value in register P3 as the key. If cursor P1 refers +** to an SQL index, then P3 is the first in an array of P4 registers +** that are used as an unpacked index key. ** -** In an SQLite index record, the serial type is stored directly before -** the blob of data that it corresponds to. In a table record, all serial -** types are stored at the start of the record, and the blobs of data at -** the end. Hence these functions allow the caller to handle the -** serial-type and data blob separately. +** Reposition cursor P1 so that it points to the smallest entry that +** is greater than or equal to the key value. If there are no records +** greater than or equal to the key and P2 is not zero, then jump to P2. ** -** The following table describes the various storage classes for data: +** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this +** opcode will either land on a record that exactly matches the key, or +** else it will cause a jump to P2. When the cursor is OPFLAG_SEEKEQ, +** this opcode must be followed by an IdxLE opcode with the same arguments. +** The IdxGT opcode will be skipped if this opcode succeeds, but the +** IdxGT opcode will be used on subsequent loop iterations. The +** OPFLAG_SEEKEQ flags is a hint to the btree layer to say that this +** is an equality search. ** -** serial type bytes of data type -** -------------- --------------- --------------- -** 0 0 NULL -** 1 1 signed integer -** 2 2 signed integer -** 3 3 signed integer -** 4 4 signed integer -** 5 6 signed integer -** 6 8 signed integer -** 7 8 IEEE float -** 8 0 Integer constant 0 -** 9 0 Integer constant 1 -** 10,11 reserved for expansion -** N>=12 and even (N-12)/2 BLOB -** N>=13 and odd (N-13)/2 text +** This opcode leaves the cursor configured to move in forward order, +** from the beginning toward the end. In other words, the cursor is +** configured to use Next, not Prev. ** -** The 8 and 9 types were added in 3.3.0, file format 4. Prior versions -** of SQLite will not understand those serial types. +** See also: Found, NotFound, SeekLt, SeekGt, SeekLe */ - -#if 0 /* Inlined into the OP_MakeRecord opcode */ -/* -** Return the serial-type for the value stored in pMem. -** -** This routine might convert a large MEM_IntReal value into MEM_Real. +/* Opcode: SeekGT P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] ** -** 2019-07-11: The primary user of this subroutine was the OP_MakeRecord -** opcode in the byte-code engine. But by moving this routine in-line, we -** can omit some redundant tests and make that opcode a lot faster. So -** this routine is now only used by the STAT3 logic and STAT3 support has -** ended. The code is kept here for historical reference only. -*/ -SQLITE_PRIVATE u32 sqlcipher_sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){ - int flags = pMem->flags; - u32 n; - - assert( pLen!=0 ); - if( flags&MEM_Null ){ - *pLen = 0; - return 0; - } - if( flags&(MEM_Int|MEM_IntReal) ){ - /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */ -# define MAX_6BYTE ((((i64)0x00008000)<<32)-1) - i64 i = pMem->u.i; - u64 u; - testcase( flags & MEM_Int ); - testcase( flags & MEM_IntReal ); - if( i<0 ){ - u = ~i; - }else{ - u = i; - } - if( u<=127 ){ - if( (i&1)==i && file_format>=4 ){ - *pLen = 0; - return 8+(u32)u; - }else{ - *pLen = 1; - return 1; - } - } - if( u<=32767 ){ *pLen = 2; return 2; } - if( u<=8388607 ){ *pLen = 3; return 3; } - if( u<=2147483647 ){ *pLen = 4; return 4; } - if( u<=MAX_6BYTE ){ *pLen = 6; return 5; } - *pLen = 8; - if( flags&MEM_IntReal ){ - /* If the value is IntReal and is going to take up 8 bytes to store - ** as an integer, then we might as well make it an 8-byte floating - ** point value */ - pMem->u.r = (double)pMem->u.i; - pMem->flags &= ~MEM_IntReal; - pMem->flags |= MEM_Real; - return 7; - } - return 6; - } - if( flags&MEM_Real ){ - *pLen = 8; - return 7; - } - assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) ); - assert( pMem->n>=0 ); - n = (u32)pMem->n; - if( flags & MEM_Zero ){ - n += pMem->u.nZero; - } - *pLen = n; - return ((n*2) + 12 + ((flags&MEM_Str)!=0)); -} -#endif /* inlined into OP_MakeRecord */ - -/* -** The sizes for serial types less than 128 +** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), +** use the value in register P3 as a key. If cursor P1 refers +** to an SQL index, then P3 is the first in an array of P4 registers +** that are used as an unpacked index key. +** +** Reposition cursor P1 so that it points to the smallest entry that +** is greater than the key value. If there are no records greater than +** the key and P2 is not zero, then jump to P2. +** +** This opcode leaves the cursor configured to move in forward order, +** from the beginning toward the end. In other words, the cursor is +** configured to use Next, not Prev. +** +** See also: Found, NotFound, SeekLt, SeekGe, SeekLe */ -static const u8 sqlcipher_sqlite3SmallTypeSizes[] = { - /* 0 1 2 3 4 5 6 7 8 9 */ -/* 0 */ 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, -/* 10 */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, -/* 20 */ 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, -/* 30 */ 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, -/* 40 */ 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, -/* 50 */ 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, -/* 60 */ 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, -/* 70 */ 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, -/* 80 */ 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, -/* 90 */ 39, 39, 40, 40, 41, 41, 42, 42, 43, 43, -/* 100 */ 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, -/* 110 */ 49, 49, 50, 50, 51, 51, 52, 52, 53, 53, -/* 120 */ 54, 54, 55, 55, 56, 56, 57, 57 -}; - -/* -** Return the length of the data corresponding to the supplied serial-type. +/* Opcode: SeekLT P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] +** +** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), +** use the value in register P3 as a key. If cursor P1 refers +** to an SQL index, then P3 is the first in an array of P4 registers +** that are used as an unpacked index key. +** +** Reposition cursor P1 so that it points to the largest entry that +** is less than the key value. If there are no records less than +** the key and P2 is not zero, then jump to P2. +** +** This opcode leaves the cursor configured to move in reverse order, +** from the end toward the beginning. In other words, the cursor is +** configured to use Prev, not Next. +** +** See also: Found, NotFound, SeekGt, SeekGe, SeekLe */ -SQLITE_PRIVATE u32 sqlcipher_sqlite3VdbeSerialTypeLen(u32 serial_type){ - if( serial_type>=128 ){ - return (serial_type-12)/2; - }else{ - assert( serial_type<12 - || sqlcipher_sqlite3SmallTypeSizes[serial_type]==(serial_type - 12)/2 ); - return sqlcipher_sqlite3SmallTypeSizes[serial_type]; - } -} -SQLITE_PRIVATE u8 sqlcipher_sqlite3VdbeOneByteSerialTypeLen(u8 serial_type){ - assert( serial_type<128 ); - return sqlcipher_sqlite3SmallTypeSizes[serial_type]; -} - -/* -** If we are on an architecture with mixed-endian floating -** points (ex: ARM7) then swap the lower 4 bytes with the -** upper 4 bytes. Return the result. +/* Opcode: SeekLE P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] ** -** For most architectures, this is a no-op. +** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), +** use the value in register P3 as a key. If cursor P1 refers +** to an SQL index, then P3 is the first in an array of P4 registers +** that are used as an unpacked index key. ** -** (later): It is reported to me that the mixed-endian problem -** on ARM7 is an issue with GCC, not with the ARM7 chip. It seems -** that early versions of GCC stored the two words of a 64-bit -** float in the wrong order. And that error has been propagated -** ever since. The blame is not necessarily with GCC, though. -** GCC might have just copying the problem from a prior compiler. -** I am also told that newer versions of GCC that follow a different -** ABI get the byte order right. +** Reposition cursor P1 so that it points to the largest entry that +** is less than or equal to the key value. If there are no records +** less than or equal to the key and P2 is not zero, then jump to P2. ** -** Developers using SQLite on an ARM7 should compile and run their -** application using -DSQLITE_DEBUG=1 at least once. With DEBUG -** enabled, some asserts below will ensure that the byte order of -** floating point values is correct. +** This opcode leaves the cursor configured to move in reverse order, +** from the end toward the beginning. In other words, the cursor is +** configured to use Prev, not Next. ** -** (2007-08-30) Frank van Vugt has studied this problem closely -** and has send his findings to the SQLite developers. Frank -** writes that some Linux kernels offer floating point hardware -** emulation that uses only 32-bit mantissas instead of a full -** 48-bits as required by the IEEE standard. (This is the -** CONFIG_FPE_FASTFPE option.) On such systems, floating point -** byte swapping becomes very complicated. To avoid problems, -** the necessary byte swapping is carried out using a 64-bit integer -** rather than a 64-bit float. Frank assures us that the code here -** works for him. We, the developers, have no way to independently -** verify this, but Frank seems to know what he is talking about -** so we trust him. +** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this +** opcode will either land on a record that exactly matches the key, or +** else it will cause a jump to P2. When the cursor is OPFLAG_SEEKEQ, +** this opcode must be followed by an IdxLE opcode with the same arguments. +** The IdxGE opcode will be skipped if this opcode succeeds, but the +** IdxGE opcode will be used on subsequent loop iterations. The +** OPFLAG_SEEKEQ flags is a hint to the btree layer to say that this +** is an equality search. +** +** See also: Found, NotFound, SeekGt, SeekGe, SeekLt */ -#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT -static u64 floatSwap(u64 in){ - union { - u64 r; - u32 i[2]; - } u; - u32 t; +case OP_SeekLT: /* jump, in3, group */ +case OP_SeekLE: /* jump, in3, group */ +case OP_SeekGE: /* jump, in3, group */ +case OP_SeekGT: { /* jump, in3, group */ + int res; /* Comparison result */ + int oc; /* Opcode */ + VdbeCursor *pC; /* The cursor to seek */ + UnpackedRecord r; /* The key to seek for */ + int nField; /* Number of columns or fields in the key */ + i64 iKey; /* The rowid we are to seek to */ + int eqOnly; /* Only interested in == results */ - u.r = in; - t = u.i[0]; - u.i[0] = u.i[1]; - u.i[1] = t; - return u.r; -} -# define swapMixedEndianFloat(X) X = floatSwap(X) -#else -# define swapMixedEndianFloat(X) + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p2!=0 ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( OP_SeekLE == OP_SeekLT+1 ); + assert( OP_SeekGE == OP_SeekLT+2 ); + assert( OP_SeekGT == OP_SeekLT+3 ); + assert( pC->isOrdered ); + assert( pC->uc.pCursor!=0 ); + oc = pOp->opcode; + eqOnly = 0; + pC->nullRow = 0; +#ifdef SQLITE_DEBUG + pC->seekOp = pOp->opcode; #endif -/* -** Write the serialized data blob for the value stored in pMem into -** buf. It is assumed that the caller has allocated sufficient space. -** Return the number of bytes written. -** -** nBuf is the amount of space left in buf[]. The caller is responsible -** for allocating enough space to buf[] to hold the entire field, exclusive -** of the pMem->u.nZero bytes for a MEM_Zero value. -** -** Return the number of bytes actually written into buf[]. The number -** of bytes in the zero-filled tail is included in the return value only -** if those bytes were zeroed in buf[]. -*/ -SQLITE_PRIVATE u32 sqlcipher_sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){ - u32 len; + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; + if( pC->isTable ){ + u16 flags3, newType; + /* The OPFLAG_SEEKEQ/BTREE_SEEK_EQ flag is only set on index cursors */ + assert( sqlcipher_sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ)==0 + || CORRUPT_DB ); - /* Integer and Real */ - if( serial_type<=7 && serial_type>0 ){ - u64 v; - u32 i; - if( serial_type==7 ){ - assert( sizeof(v)==sizeof(pMem->u.r) ); - memcpy(&v, &pMem->u.r, sizeof(v)); - swapMixedEndianFloat(v); - }else{ - v = pMem->u.i; + /* The input value in P3 might be of any type: integer, real, string, + ** blob, or NULL. But it needs to be an integer before we can do + ** the seek, so convert it. */ + pIn3 = &aMem[pOp->p3]; + flags3 = pIn3->flags; + if( (flags3 & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Str))==MEM_Str ){ + applyNumericAffinity(pIn3, 0); } - len = i = sqlcipher_sqlite3SmallTypeSizes[serial_type]; - assert( i>0 ); - do{ - buf[--i] = (u8)(v&0xFF); - v >>= 8; - }while( i ); - return len; - } - - /* String or blob */ - if( serial_type>=12 ){ - assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0) - == (int)sqlcipher_sqlite3VdbeSerialTypeLen(serial_type) ); - len = pMem->n; - if( len>0 ) memcpy(buf, pMem->z, len); - return len; - } + iKey = sqlcipher_sqlite3VdbeIntValue(pIn3); /* Get the integer key value */ + newType = pIn3->flags; /* Record the type after applying numeric affinity */ + pIn3->flags = flags3; /* But convert the type back to its original */ - /* NULL or constants 0 or 1 */ - return 0; -} + /* If the P3 value could not be converted into an integer without + ** loss of information, then special processing is required... */ + if( (newType & (MEM_Int|MEM_IntReal))==0 ){ + int c; + if( (newType & MEM_Real)==0 ){ + if( (newType & MEM_Null) || oc>=OP_SeekGE ){ + VdbeBranchTaken(1,2); + goto jump_to_p2; + }else{ + rc = sqlcipher_sqlite3BtreeLast(pC->uc.pCursor, &res); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + goto seek_not_found; + } + } + c = sqlcipher_sqlite3IntFloatCompare(iKey, pIn3->u.r); -/* Input "x" is a sequence of unsigned characters that represent a -** big-endian integer. Return the equivalent native integer -*/ -#define ONE_BYTE_INT(x) ((i8)(x)[0]) -#define TWO_BYTE_INT(x) (256*(i8)((x)[0])|(x)[1]) -#define THREE_BYTE_INT(x) (65536*(i8)((x)[0])|((x)[1]<<8)|(x)[2]) -#define FOUR_BYTE_UINT(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3]) -#define FOUR_BYTE_INT(x) (16777216*(i8)((x)[0])|((x)[1]<<16)|((x)[2]<<8)|(x)[3]) + /* If the approximation iKey is larger than the actual real search + ** term, substitute >= for > and < for <=. e.g. if the search term + ** is 4.9 and the integer approximation 5: + ** + ** (x > 4.9) -> (x >= 5) + ** (x <= 4.9) -> (x < 5) + */ + if( c>0 ){ + assert( OP_SeekGE==(OP_SeekGT-1) ); + assert( OP_SeekLT==(OP_SeekLE-1) ); + assert( (OP_SeekLE & 0x0001)==(OP_SeekGT & 0x0001) ); + if( (oc & 0x0001)==(OP_SeekGT & 0x0001) ) oc--; + } -/* -** Deserialize the data blob pointed to by buf as serial type serial_type -** and store the result in pMem. Return the number of bytes read. -** -** This function is implemented as two separate routines for performance. -** The few cases that require local variables are broken out into a separate -** routine so that in most cases the overhead of moving the stack pointer -** is avoided. -*/ -static u32 serialGet( - const unsigned char *buf, /* Buffer to deserialize from */ - u32 serial_type, /* Serial type to deserialize */ - Mem *pMem /* Memory cell to write value into */ -){ - u64 x = FOUR_BYTE_UINT(buf); - u32 y = FOUR_BYTE_UINT(buf+4); - x = (x<<32) + y; - if( serial_type==6 ){ - /* EVIDENCE-OF: R-29851-52272 Value is a big-endian 64-bit - ** twos-complement integer. */ - pMem->u.i = *(i64*)&x; - pMem->flags = MEM_Int; - testcase( pMem->u.i<0 ); - }else{ - /* EVIDENCE-OF: R-57343-49114 Value is a big-endian IEEE 754-2008 64-bit - ** floating point number. */ -#if !defined(NDEBUG) && !defined(SQLITE_OMIT_FLOATING_POINT) - /* Verify that integers and floating point values use the same - ** byte order. Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is - ** defined that 64-bit floating point values really are mixed - ** endian. - */ - static const u64 t1 = ((u64)0x3ff00000)<<32; - static const double r1 = 1.0; - u64 t2 = t1; - swapMixedEndianFloat(t2); - assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 ); -#endif - assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 ); - swapMixedEndianFloat(x); - memcpy(&pMem->u.r, &x, sizeof(x)); - pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real; - } - return 8; -} -SQLITE_PRIVATE u32 sqlcipher_sqlite3VdbeSerialGet( - const unsigned char *buf, /* Buffer to deserialize from */ - u32 serial_type, /* Serial type to deserialize */ - Mem *pMem /* Memory cell to write value into */ -){ - switch( serial_type ){ - case 10: { /* Internal use only: NULL with virtual table - ** UPDATE no-change flag set */ - pMem->flags = MEM_Null|MEM_Zero; - pMem->n = 0; - pMem->u.nZero = 0; - break; - } - case 11: /* Reserved for future use */ - case 0: { /* Null */ - /* EVIDENCE-OF: R-24078-09375 Value is a NULL. */ - pMem->flags = MEM_Null; - break; - } - case 1: { - /* EVIDENCE-OF: R-44885-25196 Value is an 8-bit twos-complement - ** integer. */ - pMem->u.i = ONE_BYTE_INT(buf); - pMem->flags = MEM_Int; - testcase( pMem->u.i<0 ); - return 1; + /* If the approximation iKey is smaller than the actual real search + ** term, substitute <= for < and > for >=. */ + else if( c<0 ){ + assert( OP_SeekLE==(OP_SeekLT+1) ); + assert( OP_SeekGT==(OP_SeekGE+1) ); + assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) ); + if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++; + } } - case 2: { /* 2-byte signed integer */ - /* EVIDENCE-OF: R-49794-35026 Value is a big-endian 16-bit - ** twos-complement integer. */ - pMem->u.i = TWO_BYTE_INT(buf); - pMem->flags = MEM_Int; - testcase( pMem->u.i<0 ); - return 2; + rc = sqlcipher_sqlite3BtreeTableMoveto(pC->uc.pCursor, (u64)iKey, 0, &res); + pC->movetoTarget = iKey; /* Used by OP_Delete */ + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; } - case 3: { /* 3-byte signed integer */ - /* EVIDENCE-OF: R-37839-54301 Value is a big-endian 24-bit - ** twos-complement integer. */ - pMem->u.i = THREE_BYTE_INT(buf); - pMem->flags = MEM_Int; - testcase( pMem->u.i<0 ); - return 3; + }else{ + /* For a cursor with the OPFLAG_SEEKEQ/BTREE_SEEK_EQ hint, only the + ** OP_SeekGE and OP_SeekLE opcodes are allowed, and these must be + ** immediately followed by an OP_IdxGT or OP_IdxLT opcode, respectively, + ** with the same key. + */ + if( sqlcipher_sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ) ){ + eqOnly = 1; + assert( pOp->opcode==OP_SeekGE || pOp->opcode==OP_SeekLE ); + assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT ); + assert( pOp->opcode==OP_SeekGE || pOp[1].opcode==OP_IdxLT ); + assert( pOp->opcode==OP_SeekLE || pOp[1].opcode==OP_IdxGT ); + assert( pOp[1].p1==pOp[0].p1 ); + assert( pOp[1].p2==pOp[0].p2 ); + assert( pOp[1].p3==pOp[0].p3 ); + assert( pOp[1].p4.i==pOp[0].p4.i ); } - case 4: { /* 4-byte signed integer */ - /* EVIDENCE-OF: R-01849-26079 Value is a big-endian 32-bit - ** twos-complement integer. */ - pMem->u.i = FOUR_BYTE_INT(buf); -#ifdef __HP_cc - /* Work around a sign-extension bug in the HP compiler for HP/UX */ - if( buf[0]&0x80 ) pMem->u.i |= 0xffffffff80000000LL; + + nField = pOp->p4.i; + assert( pOp->p4type==P4_INT32 ); + assert( nField>0 ); + r.pKeyInfo = pC->pKeyInfo; + r.nField = (u16)nField; + + /* The next line of code computes as follows, only faster: + ** if( oc==OP_SeekGT || oc==OP_SeekLE ){ + ** r.default_rc = -1; + ** }else{ + ** r.default_rc = +1; + ** } + */ + r.default_rc = ((1 & (oc - OP_SeekLT)) ? -1 : +1); + assert( oc!=OP_SeekGT || r.default_rc==-1 ); + assert( oc!=OP_SeekLE || r.default_rc==-1 ); + assert( oc!=OP_SeekGE || r.default_rc==+1 ); + assert( oc!=OP_SeekLT || r.default_rc==+1 ); + + r.aMem = &aMem[pOp->p3]; +#ifdef SQLITE_DEBUG + { int i; for(i=0; iflags = MEM_Int; - testcase( pMem->u.i<0 ); - return 4; - } - case 5: { /* 6-byte signed integer */ - /* EVIDENCE-OF: R-50385-09674 Value is a big-endian 48-bit - ** twos-complement integer. */ - pMem->u.i = FOUR_BYTE_UINT(buf+2) + (((i64)1)<<32)*TWO_BYTE_INT(buf); - pMem->flags = MEM_Int; - testcase( pMem->u.i<0 ); - return 6; + r.eqSeen = 0; + rc = sqlcipher_sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &res); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; } - case 6: /* 8-byte signed integer */ - case 7: { /* IEEE floating point */ - /* These use local variables, so do them in a separate routine - ** to avoid having to move the frame pointer in the common case */ - return serialGet(buf,serial_type,pMem); + if( eqOnly && r.eqSeen==0 ){ + assert( res!=0 ); + goto seek_not_found; } - case 8: /* Integer 0 */ - case 9: { /* Integer 1 */ - /* EVIDENCE-OF: R-12976-22893 Value is the integer 0. */ - /* EVIDENCE-OF: R-18143-12121 Value is the integer 1. */ - pMem->u.i = serial_type-8; - pMem->flags = MEM_Int; - return 0; + } +#ifdef SQLITE_TEST + sqlcipher_sqlite3_search_count++; +#endif + if( oc>=OP_SeekGE ){ assert( oc==OP_SeekGE || oc==OP_SeekGT ); + if( res<0 || (res==0 && oc==OP_SeekGT) ){ + res = 0; + rc = sqlcipher_sqlite3BtreeNext(pC->uc.pCursor, 0); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; + res = 1; + }else{ + goto abort_due_to_error; + } + } + }else{ + res = 0; } - default: { - /* EVIDENCE-OF: R-14606-31564 Value is a BLOB that is (N-12)/2 bytes in - ** length. - ** EVIDENCE-OF: R-28401-00140 Value is a string in the text encoding and - ** (N-13)/2 bytes in length. */ - static const u16 aFlag[] = { MEM_Blob|MEM_Ephem, MEM_Str|MEM_Ephem }; - pMem->z = (char *)buf; - pMem->n = (serial_type-12)/2; - pMem->flags = aFlag[serial_type&1]; - return pMem->n; + }else{ + assert( oc==OP_SeekLT || oc==OP_SeekLE ); + if( res>0 || (res==0 && oc==OP_SeekLT) ){ + res = 0; + rc = sqlcipher_sqlite3BtreePrevious(pC->uc.pCursor, 0); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; + res = 1; + }else{ + goto abort_due_to_error; + } + } + }else{ + /* res might be negative because the table is empty. Check to + ** see if this is the case. + */ + res = sqlcipher_sqlite3BtreeEof(pC->uc.pCursor); } } - return 0; +seek_not_found: + assert( pOp->p2>0 ); + VdbeBranchTaken(res!=0,2); + if( res ){ + goto jump_to_p2; + }else if( eqOnly ){ + assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT ); + pOp++; /* Skip the OP_IdxLt or OP_IdxGT that follows */ + } + break; } -/* -** This routine is used to allocate sufficient space for an UnpackedRecord -** structure large enough to be used with sqlcipher_sqlite3VdbeRecordUnpack() if -** the first argument is a pointer to KeyInfo structure pKeyInfo. + + +/* Opcode: SeekScan P1 P2 * * * +** Synopsis: Scan-ahead up to P1 rows ** -** The space is either allocated using sqlcipher_sqlite3DbMallocRaw() or from within -** the unaligned buffer passed via the second and third arguments (presumably -** stack space). If the former, then *ppFree is set to a pointer that should -** be eventually freed by the caller using sqlcipher_sqlite3DbFree(). Or, if the -** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL -** before returning. +** This opcode is a prefix opcode to OP_SeekGE. In other words, this +** opcode must be immediately followed by OP_SeekGE. This constraint is +** checked by assert() statements. ** -** If an OOM error occurs, NULL is returned. +** This opcode uses the P1 through P4 operands of the subsequent +** OP_SeekGE. In the text that follows, the operands of the subsequent +** OP_SeekGE opcode are denoted as SeekOP.P1 through SeekOP.P4. Only +** the P1 and P2 operands of this opcode are also used, and are called +** This.P1 and This.P2. +** +** This opcode helps to optimize IN operators on a multi-column index +** where the IN operator is on the later terms of the index by avoiding +** unnecessary seeks on the btree, substituting steps to the next row +** of the b-tree instead. A correct answer is obtained if this opcode +** is omitted or is a no-op. +** +** The SeekGE.P3 and SeekGE.P4 operands identify an unpacked key which +** is the desired entry that we want the cursor SeekGE.P1 to be pointing +** to. Call this SeekGE.P4/P5 row the "target". +** +** If the SeekGE.P1 cursor is not currently pointing to a valid row, +** then this opcode is a no-op and control passes through into the OP_SeekGE. +** +** If the SeekGE.P1 cursor is pointing to a valid row, then that row +** might be the target row, or it might be near and slightly before the +** target row. This opcode attempts to position the cursor on the target +** row by, perhaps by invoking sqlcipher_sqlite3BtreeStep() on the cursor +** between 0 and This.P1 times. +** +** There are three possible outcomes from this opcode:
      +** +**
    1. If after This.P1 steps, the cursor is still pointing to a place that +** is earlier in the btree than the target row, then fall through +** into the subsquence OP_SeekGE opcode. +** +**
    2. If the cursor is successfully moved to the target row by 0 or more +** sqlcipher_sqlite3BtreeNext() calls, then jump to This.P2, which will land just +** past the OP_IdxGT or OP_IdxGE opcode that follows the OP_SeekGE. +** +**
    3. If the cursor ends up past the target row (indicating the the target +** row does not exist in the btree) then jump to SeekOP.P2. +**
    */ -SQLITE_PRIVATE UnpackedRecord *sqlcipher_sqlite3VdbeAllocUnpackedRecord( - KeyInfo *pKeyInfo /* Description of the record */ -){ - UnpackedRecord *p; /* Unpacked record to return */ - int nByte; /* Number of bytes required for *p */ - nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1); - p = (UnpackedRecord *)sqlcipher_sqlite3DbMallocRaw(pKeyInfo->db, nByte); - if( !p ) return 0; - p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))]; - assert( pKeyInfo->aSortFlags!=0 ); - p->pKeyInfo = pKeyInfo; - p->nField = pKeyInfo->nKeyField + 1; - return p; -} +case OP_SeekScan: { + VdbeCursor *pC; + int res; + int nStep; + UnpackedRecord r; -/* -** Given the nKey-byte encoding of a record in pKey[], populate the -** UnpackedRecord structure indicated by the fourth argument with the -** contents of the decoded record. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeRecordUnpack( - KeyInfo *pKeyInfo, /* Information about the record format */ - int nKey, /* Size of the binary record */ - const void *pKey, /* The binary record */ - UnpackedRecord *p /* Populate this structure before returning. */ -){ - const unsigned char *aKey = (const unsigned char *)pKey; - u32 d; - u32 idx; /* Offset in aKey[] to read from */ - u16 u; /* Unsigned loop counter */ - u32 szHdr; - Mem *pMem = p->aMem; + assert( pOp[1].opcode==OP_SeekGE ); - p->default_rc = 0; - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - idx = getVarint32(aKey, szHdr); - d = szHdr; - u = 0; - while( idxp2 points to the first instruction past the OP_IdxGT that + ** follows the OP_SeekGE. */ + assert( pOp->p2>=(int)(pOp-aOp)+2 ); + assert( aOp[pOp->p2-1].opcode==OP_IdxGT || aOp[pOp->p2-1].opcode==OP_IdxGE ); + testcase( aOp[pOp->p2-1].opcode==OP_IdxGE ); + assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); + assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); + assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); - idx += getVarint32(&aKey[idx], serial_type); - pMem->enc = pKeyInfo->enc; - pMem->db = pKeyInfo->db; - /* pMem->flags = 0; // sqlcipher_sqlite3VdbeSerialGet() will set this for us */ - pMem->szMalloc = 0; - pMem->z = 0; - d += sqlcipher_sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); - pMem++; - if( (++u)>=p->nField ) break; + assert( pOp->p1>0 ); + pC = p->apCsr[pOp[1].p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( !pC->isTable ); + if( !sqlcipher_sqlite3BtreeCursorIsValidNN(pC->uc.pCursor) ){ +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + printf("... cursor not valid - fall through\n"); + } +#endif + break; } - if( d>(u32)nKey && u ){ - assert( CORRUPT_DB ); - /* In a corrupt record entry, the last pMem might have been set up using - ** uninitialized memory. Overwrite its value with NULL, to prevent - ** warnings from MSAN. */ - sqlcipher_sqlite3VdbeMemSetNull(pMem-1); + nStep = pOp->p1; + assert( nStep>=1 ); + r.pKeyInfo = pC->pKeyInfo; + r.nField = (u16)pOp[1].p4.i; + r.default_rc = 0; + r.aMem = &aMem[pOp[1].p3]; +#ifdef SQLITE_DEBUG + { + int i; + for(i=0; inKeyField + 1 ); - p->nField = u; -} - +#endif + res = 0; /* Not needed. Only used to silence a warning. */ + while(1){ + rc = sqlcipher_sqlite3VdbeIdxKeyCompare(db, pC, &r, &res); + if( rc ) goto abort_due_to_error; + if( res>0 ){ + seekscan_search_fail: #ifdef SQLITE_DEBUG -/* -** This function compares two index or table record keys in the same way -** as the sqlcipher_sqlite3VdbeRecordCompare() routine. Unlike VdbeRecordCompare(), -** this function deserializes and compares values using the -** sqlcipher_sqlite3VdbeSerialGet() and sqlcipher_sqlite3MemCompare() functions. It is used -** in assert() statements to ensure that the optimized code in -** sqlcipher_sqlite3VdbeRecordCompare() returns results with these two primitives. -** -** Return true if the result of comparison is equivalent to desiredResult. -** Return false if there is a disagreement. -*/ -static int vdbeRecordCompareDebug( - int nKey1, const void *pKey1, /* Left key */ - const UnpackedRecord *pPKey2, /* Right key */ - int desiredResult /* Correct answer */ -){ - u32 d1; /* Offset into aKey[] of next data element */ - u32 idx1; /* Offset into aKey[] of next header element */ - u32 szHdr1; /* Number of bytes in header */ - int i = 0; - int rc = 0; - const unsigned char *aKey1 = (const unsigned char *)pKey1; - KeyInfo *pKeyInfo; - Mem mem1; - - pKeyInfo = pPKey2->pKeyInfo; - if( pKeyInfo->db==0 ) return 1; - mem1.enc = pKeyInfo->enc; - mem1.db = pKeyInfo->db; - /* mem1.flags = 0; // Will be initialized by sqlcipher_sqlite3VdbeSerialGet() */ - VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */ - - /* Compilers may complain that mem1.u.i is potentially uninitialized. - ** We could initialize it, as shown here, to silence those complaints. - ** But in fact, mem1.u.i will never actually be used uninitialized, and doing - ** the unnecessary initialization has a measurable negative performance - ** impact, since this routine is a very high runner. And so, we choose - ** to ignore the compiler warnings and leave this variable uninitialized. - */ - /* mem1.u.i = 0; // not needed, here to silence compiler warning */ - - idx1 = getVarint32(aKey1, szHdr1); - if( szHdr1>98307 ) return SQLITE_CORRUPT; - d1 = szHdr1; - assert( pKeyInfo->nAllField>=pPKey2->nField || CORRUPT_DB ); - assert( pKeyInfo->aSortFlags!=0 ); - assert( pKeyInfo->nKeyField>0 ); - assert( idx1<=szHdr1 || CORRUPT_DB ); - do{ - u32 serial_type1; - - /* Read the serial types for the next element in each key. */ - idx1 += getVarint32( aKey1+idx1, serial_type1 ); - - /* Verify that there is enough key space remaining to avoid - ** a buffer overread. The "d1+serial_type1+2" subexpression will - ** always be greater than or equal to the amount of required key space. - ** Use that approximation to avoid the more expensive call to - ** sqlcipher_sqlite3VdbeSerialTypeLen() in the common case. - */ - if( d1+(u64)serial_type1+2>(u64)nKey1 - && d1+(u64)sqlcipher_sqlite3VdbeSerialTypeLen(serial_type1)>(u64)nKey1 - ){ + if( db->flags&SQLITE_VdbeTrace ){ + printf("... %d steps and then skip\n", pOp->p1 - nStep); + } +#endif + VdbeBranchTaken(1,3); + pOp++; + goto jump_to_p2; + } + if( res==0 ){ +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + printf("... %d steps and then success\n", pOp->p1 - nStep); + } +#endif + VdbeBranchTaken(2,3); + goto jump_to_p2; break; } - - /* Extract the values to be compared. - */ - d1 += sqlcipher_sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); - - /* Do the comparison - */ - rc = sqlcipher_sqlite3MemCompare(&mem1, &pPKey2->aMem[i], - pKeyInfo->nAllField>i ? pKeyInfo->aColl[i] : 0); - if( rc!=0 ){ - assert( mem1.szMalloc==0 ); /* See comment below */ - if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) - && ((mem1.flags & MEM_Null) || (pPKey2->aMem[i].flags & MEM_Null)) - ){ - rc = -rc; + if( nStep<=0 ){ +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + printf("... fall through after %d steps\n", pOp->p1); } - if( pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC ){ - rc = -rc; /* Invert the result for DESC sort order. */ +#endif + VdbeBranchTaken(0,3); + break; + } + nStep--; + rc = sqlcipher_sqlite3BtreeNext(pC->uc.pCursor, 0); + if( rc ){ + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; + goto seekscan_search_fail; + }else{ + goto abort_due_to_error; } - goto debugCompareEnd; } - i++; - }while( idx1nField ); - - /* No memory allocation is ever used on mem1. Prove this using - ** the following assert(). If the assert() fails, it indicates a - ** memory leak and a need to call sqlcipher_sqlite3VdbeMemRelease(&mem1). - */ - assert( mem1.szMalloc==0 ); - - /* rc==0 here means that one of the keys ran out of fields and - ** all the fields up to that point were equal. Return the default_rc - ** value. */ - rc = pPKey2->default_rc; + } -debugCompareEnd: - if( desiredResult==0 && rc==0 ) return 1; - if( desiredResult<0 && rc<0 ) return 1; - if( desiredResult>0 && rc>0 ) return 1; - if( CORRUPT_DB ) return 1; - if( pKeyInfo->db->mallocFailed ) return 1; - return 0; + break; } -#endif -#ifdef SQLITE_DEBUG -/* -** Count the number of fields (a.k.a. columns) in the record given by -** pKey,nKey. The verify that this count is less than or equal to the -** limit given by pKeyInfo->nAllField. + +/* Opcode: SeekHit P1 P2 P3 * * +** Synopsis: set P2<=seekHit<=P3 ** -** If this constraint is not satisfied, it means that the high-speed -** vdbeRecordCompareInt() and vdbeRecordCompareString() routines will -** not work correctly. If this assert() ever fires, it probably means -** that the KeyInfo.nKeyField or KeyInfo.nAllField values were computed -** incorrectly. +** Increase or decrease the seekHit value for cursor P1, if necessary, +** so that it is no less than P2 and no greater than P3. +** +** The seekHit integer represents the maximum of terms in an index for which +** there is known to be at least one match. If the seekHit value is smaller +** than the total number of equality terms in an index lookup, then the +** OP_IfNoHope opcode might run to see if the IN loop can be abandoned +** early, thus saving work. This is part of the IN-early-out optimization. +** +** P1 must be a valid b-tree cursor. */ -static void vdbeAssertFieldCountWithinLimits( - int nKey, const void *pKey, /* The record to verify */ - const KeyInfo *pKeyInfo /* Compare size with this KeyInfo */ -){ - int nField = 0; - u32 szHdr; - u32 idx; - u32 notUsed; - const unsigned char *aKey = (const unsigned char*)pKey; - - if( CORRUPT_DB ) return; - idx = getVarint32(aKey, szHdr); - assert( nKey>=0 ); - assert( szHdr<=(u32)nKey ); - while( idxnAllField ); -} -#else -# define vdbeAssertFieldCountWithinLimits(A,B,C) +case OP_SeekHit: { + VdbeCursor *pC; + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pOp->p3>=pOp->p2 ); + if( pC->seekHitp2 ){ +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + printf("seekHit changes from %d to %d\n", pC->seekHit, pOp->p2); + } #endif - -/* -** Both *pMem1 and *pMem2 contain string values. Compare the two values -** using the collation sequence pColl. As usual, return a negative , zero -** or positive value if *pMem1 is less than, equal to or greater than -** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);". -*/ -static int vdbeCompareMemString( - const Mem *pMem1, - const Mem *pMem2, - const CollSeq *pColl, - u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */ -){ - if( pMem1->enc==pColl->enc ){ - /* The strings are already in the correct encoding. Call the - ** comparison function directly */ - return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z); - }else{ - int rc; - const void *v1, *v2; - Mem c1; - Mem c2; - sqlcipher_sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null); - sqlcipher_sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null); - sqlcipher_sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem); - sqlcipher_sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem); - v1 = sqlcipher_sqlite3ValueText((sqlcipher_sqlite3_value*)&c1, pColl->enc); - v2 = sqlcipher_sqlite3ValueText((sqlcipher_sqlite3_value*)&c2, pColl->enc); - if( (v1==0 || v2==0) ){ - if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT; - rc = 0; - }else{ - rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2); + pC->seekHit = pOp->p2; + }else if( pC->seekHit>pOp->p3 ){ +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + printf("seekHit changes from %d to %d\n", pC->seekHit, pOp->p3); } - sqlcipher_sqlite3VdbeMemRelease(&c1); - sqlcipher_sqlite3VdbeMemRelease(&c2); - return rc; +#endif + pC->seekHit = pOp->p3; } + break; } -/* -** The input pBlob is guaranteed to be a Blob that is not marked -** with MEM_Zero. Return true if it could be a zero-blob. +/* Opcode: IfNotOpen P1 P2 * * * +** Synopsis: if( !csr[P1] ) goto P2 +** +** If cursor P1 is not open, jump to instruction P2. Otherwise, fall through. */ -static int isAllZero(const char *z, int n){ - int i; - for(i=0; ip1>=0 && pOp->p1nCursor ); + VdbeBranchTaken(p->apCsr[pOp->p1]==0, 2); + if( !p->apCsr[pOp->p1] ){ + goto jump_to_p2_and_check_for_interrupt; } - return 1; + break; } -/* -** Compare two blobs. Return negative, zero, or positive if the first -** is less than, equal to, or greater than the second, respectively. -** If one blob is a prefix of the other, then the shorter is the lessor. +/* Opcode: Found P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] +** +** If P4==0 then register P3 holds a blob constructed by MakeRecord. If +** P4>0 then register P3 is the first of P4 registers that form an unpacked +** record. +** +** Cursor P1 is on an index btree. If the record identified by P3 and P4 +** is a prefix of any entry in P1 then a jump is made to P2 and +** P1 is left pointing at the matching entry. +** +** This operation leaves the cursor in a state where it can be +** advanced in the forward direction. The Next instruction will work, +** but not the Prev instruction. +** +** See also: NotFound, NoConflict, NotExists. SeekGe */ -SQLITE_PRIVATE SQLITE_NOINLINE int sqlcipher_sqlite3BlobCompare(const Mem *pB1, const Mem *pB2){ - int c; - int n1 = pB1->n; - int n2 = pB2->n; - - /* It is possible to have a Blob value that has some non-zero content - ** followed by zero content. But that only comes up for Blobs formed - ** by the OP_MakeRecord opcode, and such Blobs never get passed into - ** sqlcipher_sqlite3MemCompare(). */ - assert( (pB1->flags & MEM_Zero)==0 || n1==0 ); - assert( (pB2->flags & MEM_Zero)==0 || n2==0 ); - - if( (pB1->flags|pB2->flags) & MEM_Zero ){ - if( pB1->flags & pB2->flags & MEM_Zero ){ - return pB1->u.nZero - pB2->u.nZero; - }else if( pB1->flags & MEM_Zero ){ - if( !isAllZero(pB2->z, pB2->n) ) return -1; - return pB1->u.nZero - n2; - }else{ - if( !isAllZero(pB1->z, pB1->n) ) return +1; - return n1 - pB2->u.nZero; - } - } - c = memcmp(pB1->z, pB2->z, n1>n2 ? n2 : n1); - if( c ) return c; - return n1 - n2; -} - -/* -** Do a comparison between a 64-bit signed integer and a 64-bit floating-point -** number. Return negative, zero, or positive if the first (i64) is less than, -** equal to, or greater than the second (double). +/* Opcode: NotFound P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] +** +** If P4==0 then register P3 holds a blob constructed by MakeRecord. If +** P4>0 then register P3 is the first of P4 registers that form an unpacked +** record. +** +** Cursor P1 is on an index btree. If the record identified by P3 and P4 +** is not the prefix of any entry in P1 then a jump is made to P2. If P1 +** does contain an entry whose prefix matches the P3/P4 record then control +** falls through to the next instruction and P1 is left pointing at the +** matching entry. +** +** This operation leaves the cursor in a state where it cannot be +** advanced in either direction. In other words, the Next and Prev +** opcodes do not work after this operation. +** +** See also: Found, NotExists, NoConflict, IfNoHope */ -static int sqlcipher_sqlite3IntFloatCompare(i64 i, double r){ - if( sizeof(LONGDOUBLE_TYPE)>8 ){ - LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; - testcase( xr ); - testcase( x==r ); - if( xr ) return +1; /*NO_TEST*/ /* work around bugs in gcov */ - return 0; /*NO_TEST*/ /* work around bugs in gcov */ - }else{ - i64 y; - double s; - if( r<-9223372036854775808.0 ) return +1; - if( r>=9223372036854775808.0 ) return -1; - y = (i64)r; - if( iy ) return +1; - s = (double)i; - if( sr ) return +1; - return 0; - } -} - -/* -** Compare the values contained by the two memory cells, returning -** negative, zero or positive if pMem1 is less than, equal to, or greater -** than pMem2. Sorting order is NULL's first, followed by numbers (integers -** and reals) sorted numerically, followed by text ordered by the collating -** sequence pColl and finally blob's ordered by memcmp(). +/* Opcode: IfNoHope P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] ** -** Two NULL values are considered equal by this function. +** Register P3 is the first of P4 registers that form an unpacked +** record. Cursor P1 is an index btree. P2 is a jump destination. +** In other words, the operands to this opcode are the same as the +** operands to OP_NotFound and OP_IdxGT. +** +** This opcode is an optimization attempt only. If this opcode always +** falls through, the correct answer is still obtained, but extra works +** is performed. +** +** A value of N in the seekHit flag of cursor P1 means that there exists +** a key P3:N that will match some record in the index. We want to know +** if it is possible for a record P3:P4 to match some record in the +** index. If it is not possible, we can skips some work. So if seekHit +** is less than P4, attempt to find out if a match is possible by running +** OP_NotFound. +** +** This opcode is used in IN clause processing for a multi-column key. +** If an IN clause is attached to an element of the key other than the +** left-most element, and if there are no matches on the most recent +** seek over the whole key, then it might be that one of the key element +** to the left is prohibiting a match, and hence there is "no hope" of +** any match regardless of how many IN clause elements are checked. +** In such a case, we abandon the IN clause search early, using this +** opcode. The opcode name comes from the fact that the +** jump is taken if there is "no hope" of achieving a match. +** +** See also: NotFound, SeekHit */ -SQLITE_PRIVATE int sqlcipher_sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){ - int f1, f2; - int combined_flags; - - f1 = pMem1->flags; - f2 = pMem2->flags; - combined_flags = f1|f2; - assert( !sqlcipher_sqlite3VdbeMemIsRowSet(pMem1) && !sqlcipher_sqlite3VdbeMemIsRowSet(pMem2) ); - - /* If one value is NULL, it is less than the other. If both values - ** are NULL, return 0. - */ - if( combined_flags&MEM_Null ){ - return (f2&MEM_Null) - (f1&MEM_Null); +/* Opcode: NoConflict P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] +** +** If P4==0 then register P3 holds a blob constructed by MakeRecord. If +** P4>0 then register P3 is the first of P4 registers that form an unpacked +** record. +** +** Cursor P1 is on an index btree. If the record identified by P3 and P4 +** contains any NULL value, jump immediately to P2. If all terms of the +** record are not-NULL then a check is done to determine if any row in the +** P1 index btree has a matching key prefix. If there are no matches, jump +** immediately to P2. If there is a match, fall through and leave the P1 +** cursor pointing to the matching row. +** +** This opcode is similar to OP_NotFound with the exceptions that the +** branch is always taken if any part of the search key input is NULL. +** +** This operation leaves the cursor in a state where it cannot be +** advanced in either direction. In other words, the Next and Prev +** opcodes do not work after this operation. +** +** See also: NotFound, Found, NotExists +*/ +case OP_IfNoHope: { /* jump, in3 */ + VdbeCursor *pC; + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + printf("seekHit is %d\n", pC->seekHit); } - - /* At least one of the two values is a number - */ - if( combined_flags&(MEM_Int|MEM_Real|MEM_IntReal) ){ - testcase( combined_flags & MEM_Int ); - testcase( combined_flags & MEM_Real ); - testcase( combined_flags & MEM_IntReal ); - if( (f1 & f2 & (MEM_Int|MEM_IntReal))!=0 ){ - testcase( f1 & f2 & MEM_Int ); - testcase( f1 & f2 & MEM_IntReal ); - if( pMem1->u.i < pMem2->u.i ) return -1; - if( pMem1->u.i > pMem2->u.i ) return +1; - return 0; +#endif + if( pC->seekHit>=pOp->p4.i ) break; + /* Fall through into OP_NotFound */ + /* no break */ deliberate_fall_through +} +case OP_NoConflict: /* jump, in3 */ +case OP_NotFound: /* jump, in3 */ +case OP_Found: { /* jump, in3 */ + int alreadyExists; + int ii; + VdbeCursor *pC; + UnpackedRecord *pIdxKey; + UnpackedRecord r; + +#ifdef SQLITE_TEST + if( pOp->opcode!=OP_NoConflict ) sqlcipher_sqlite3_found_count++; +#endif + + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p4type==P4_INT32 ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); +#ifdef SQLITE_DEBUG + pC->seekOp = pOp->opcode; +#endif + r.aMem = &aMem[pOp->p3]; + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); + assert( pC->isTable==0 ); + r.nField = (u16)pOp->p4.i; + if( r.nField>0 ){ + /* Key values in an array of registers */ + r.pKeyInfo = pC->pKeyInfo; + r.default_rc = 0; +#ifdef SQLITE_DEBUG + for(ii=0; iip3+ii, &r.aMem[ii]); } - if( (f1 & f2 & MEM_Real)!=0 ){ - if( pMem1->u.r < pMem2->u.r ) return -1; - if( pMem1->u.r > pMem2->u.r ) return +1; - return 0; +#endif + rc = sqlcipher_sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &pC->seekResult); + }else{ + /* Composite key generated by OP_MakeRecord */ + assert( r.aMem->flags & MEM_Blob ); + assert( pOp->opcode!=OP_NoConflict ); + rc = ExpandBlob(r.aMem); + assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); + if( rc ) goto no_mem; + pIdxKey = sqlcipher_sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo); + if( pIdxKey==0 ) goto no_mem; + sqlcipher_sqlite3VdbeRecordUnpack(pC->pKeyInfo, r.aMem->n, r.aMem->z, pIdxKey); + pIdxKey->default_rc = 0; + rc = sqlcipher_sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &pC->seekResult); + sqlcipher_sqlite3DbFreeNN(db, pIdxKey); + } + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } + alreadyExists = (pC->seekResult==0); + pC->nullRow = 1-alreadyExists; + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; + if( pOp->opcode==OP_Found ){ + VdbeBranchTaken(alreadyExists!=0,2); + if( alreadyExists ) goto jump_to_p2; + }else{ + if( !alreadyExists ){ + VdbeBranchTaken(1,2); + goto jump_to_p2; } - if( (f1&(MEM_Int|MEM_IntReal))!=0 ){ - testcase( f1 & MEM_Int ); - testcase( f1 & MEM_IntReal ); - if( (f2&MEM_Real)!=0 ){ - return sqlcipher_sqlite3IntFloatCompare(pMem1->u.i, pMem2->u.r); - }else if( (f2&(MEM_Int|MEM_IntReal))!=0 ){ - if( pMem1->u.i < pMem2->u.i ) return -1; - if( pMem1->u.i > pMem2->u.i ) return +1; - return 0; - }else{ - return -1; + if( pOp->opcode==OP_NoConflict ){ + /* For the OP_NoConflict opcode, take the jump if any of the + ** input fields are NULL, since any key with a NULL will not + ** conflict */ + for(ii=0; iiu.i, pMem1->u.r); - }else{ - return -1; - } + VdbeBranchTaken(0,2); + if( pOp->opcode==OP_IfNoHope ){ + pC->seekHit = pOp->p4.i; } - return +1; } + break; +} - /* If one value is a string and the other is a blob, the string is less. - ** If both are strings, compare using the collating functions. - */ - if( combined_flags&MEM_Str ){ - if( (f1 & MEM_Str)==0 ){ - return 1; - } - if( (f2 & MEM_Str)==0 ){ - return -1; - } - - assert( pMem1->enc==pMem2->enc || pMem1->db->mallocFailed ); - assert( pMem1->enc==SQLITE_UTF8 || - pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE ); - - /* The collation sequence must be defined at this point, even if - ** the user deletes the collation sequence after the vdbe program is - ** compiled (this was not always the case). - */ - assert( !pColl || pColl->xCmp ); +/* Opcode: SeekRowid P1 P2 P3 * * +** Synopsis: intkey=r[P3] +** +** P1 is the index of a cursor open on an SQL table btree (with integer +** keys). If register P3 does not contain an integer or if P1 does not +** contain a record with rowid P3 then jump immediately to P2. +** Or, if P2 is 0, raise an SQLITE_CORRUPT error. If P1 does contain +** a record with rowid P3 then +** leave the cursor pointing at that record and fall through to the next +** instruction. +** +** The OP_NotExists opcode performs the same operation, but with OP_NotExists +** the P3 register must be guaranteed to contain an integer value. With this +** opcode, register P3 might not contain an integer. +** +** The OP_NotFound opcode performs the same operation on index btrees +** (with arbitrary multi-value keys). +** +** This opcode leaves the cursor in a state where it cannot be advanced +** in either direction. In other words, the Next and Prev opcodes will +** not work following this opcode. +** +** See also: Found, NotFound, NoConflict, SeekRowid +*/ +/* Opcode: NotExists P1 P2 P3 * * +** Synopsis: intkey=r[P3] +** +** P1 is the index of a cursor open on an SQL table btree (with integer +** keys). P3 is an integer rowid. If P1 does not contain a record with +** rowid P3 then jump immediately to P2. Or, if P2 is 0, raise an +** SQLITE_CORRUPT error. If P1 does contain a record with rowid P3 then +** leave the cursor pointing at that record and fall through to the next +** instruction. +** +** The OP_SeekRowid opcode performs the same operation but also allows the +** P3 register to contain a non-integer value, in which case the jump is +** always taken. This opcode requires that P3 always contain an integer. +** +** The OP_NotFound opcode performs the same operation on index btrees +** (with arbitrary multi-value keys). +** +** This opcode leaves the cursor in a state where it cannot be advanced +** in either direction. In other words, the Next and Prev opcodes will +** not work following this opcode. +** +** See also: Found, NotFound, NoConflict, SeekRowid +*/ +case OP_SeekRowid: { /* jump, in3 */ + VdbeCursor *pC; + BtCursor *pCrsr; + int res; + u64 iKey; - if( pColl ){ - return vdbeCompareMemString(pMem1, pMem2, pColl, 0); + pIn3 = &aMem[pOp->p3]; + testcase( pIn3->flags & MEM_Int ); + testcase( pIn3->flags & MEM_IntReal ); + testcase( pIn3->flags & MEM_Real ); + testcase( (pIn3->flags & (MEM_Str|MEM_Int))==MEM_Str ); + if( (pIn3->flags & (MEM_Int|MEM_IntReal))==0 ){ + /* If pIn3->u.i does not contain an integer, compute iKey as the + ** integer value of pIn3. Jump to P2 if pIn3 cannot be converted + ** into an integer without loss of information. Take care to avoid + ** changing the datatype of pIn3, however, as it is used by other + ** parts of the prepared statement. */ + Mem x = pIn3[0]; + applyAffinity(&x, SQLITE_AFF_NUMERIC, encoding); + if( (x.flags & MEM_Int)==0 ) goto jump_to_p2; + iKey = x.u.i; + goto notExistsWithKey; + } + /* Fall through into OP_NotExists */ + /* no break */ deliberate_fall_through +case OP_NotExists: /* jump, in3 */ + pIn3 = &aMem[pOp->p3]; + assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid ); + assert( pOp->p1>=0 && pOp->p1nCursor ); + iKey = pIn3->u.i; +notExistsWithKey: + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); +#ifdef SQLITE_DEBUG + if( pOp->opcode==OP_SeekRowid ) pC->seekOp = OP_SeekRowid; +#endif + assert( pC->isTable ); + assert( pC->eCurType==CURTYPE_BTREE ); + pCrsr = pC->uc.pCursor; + assert( pCrsr!=0 ); + res = 0; + rc = sqlcipher_sqlite3BtreeTableMoveto(pCrsr, iKey, 0, &res); + assert( rc==SQLITE_OK || res==0 ); + pC->movetoTarget = iKey; /* Used by OP_Delete */ + pC->nullRow = 0; + pC->cacheStatus = CACHE_STALE; + pC->deferredMoveto = 0; + VdbeBranchTaken(res!=0,2); + pC->seekResult = res; + if( res!=0 ){ + assert( rc==SQLITE_OK ); + if( pOp->p2==0 ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + goto jump_to_p2; } - /* If a NULL pointer was passed as the collate function, fall through - ** to the blob case and use memcmp(). */ } - - /* Both values must be blobs. Compare using memcmp(). */ - return sqlcipher_sqlite3BlobCompare(pMem1, pMem2); + if( rc ) goto abort_due_to_error; + break; } - -/* -** The first argument passed to this function is a serial-type that -** corresponds to an integer - all values between 1 and 9 inclusive -** except 7. The second points to a buffer containing an integer value -** serialized according to serial_type. This function deserializes -** and returns the value. +/* Opcode: Sequence P1 P2 * * * +** Synopsis: r[P2]=cursor[P1].ctr++ +** +** Find the next available sequence number for cursor P1. +** Write the sequence number into register P2. +** The sequence number on the cursor is incremented after this +** instruction. */ -static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){ - u32 y; - assert( CORRUPT_DB || (serial_type>=1 && serial_type<=9 && serial_type!=7) ); - switch( serial_type ){ - case 0: - case 1: - testcase( aKey[0]&0x80 ); - return ONE_BYTE_INT(aKey); - case 2: - testcase( aKey[0]&0x80 ); - return TWO_BYTE_INT(aKey); - case 3: - testcase( aKey[0]&0x80 ); - return THREE_BYTE_INT(aKey); - case 4: { - testcase( aKey[0]&0x80 ); - y = FOUR_BYTE_UINT(aKey); - return (i64)*(int*)&y; - } - case 5: { - testcase( aKey[0]&0x80 ); - return FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey); - } - case 6: { - u64 x = FOUR_BYTE_UINT(aKey); - testcase( aKey[0]&0x80 ); - x = (x<<32) | FOUR_BYTE_UINT(aKey+4); - return (i64)*(i64*)&x; - } - } - - return (serial_type - 8); +case OP_Sequence: { /* out2 */ + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( p->apCsr[pOp->p1]!=0 ); + assert( p->apCsr[pOp->p1]->eCurType!=CURTYPE_VTAB ); + pOut = out2Prerelease(p, pOp); + pOut->u.i = p->apCsr[pOp->p1]->seqCount++; + break; } -/* -** This function compares the two table rows or index records -** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero -** or positive integer if key1 is less than, equal to or -** greater than key2. The {nKey1, pKey1} key must be a blob -** created by the OP_MakeRecord opcode of the VDBE. The pPKey2 -** key must be a parsed key such as obtained from -** sqlcipher_sqlite3VdbeParseRecord. -** -** If argument bSkip is non-zero, it is assumed that the caller has already -** determined that the first fields of the keys are equal. + +/* Opcode: NewRowid P1 P2 P3 * * +** Synopsis: r[P2]=rowid ** -** Key1 and Key2 do not have to contain the same number of fields. If all -** fields that appear in both keys are equal, then pPKey2->default_rc is -** returned. +** Get a new integer record number (a.k.a "rowid") used as the key to a table. +** The record number is not previously used as a key in the database +** table that cursor P1 points to. The new record number is written +** written to register P2. ** -** If database corruption is discovered, set pPKey2->errCode to -** SQLITE_CORRUPT and return 0. If an OOM error is encountered, -** pPKey2->errCode is set to SQLITE_NOMEM and, if it is not NULL, the -** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db). +** If P3>0 then P3 is a register in the root frame of this VDBE that holds +** the largest previously generated record number. No new record numbers are +** allowed to be less than this value. When this value reaches its maximum, +** an SQLITE_FULL error is generated. The P3 register is updated with the ' +** generated record number. This P3 mechanism is used to help implement the +** AUTOINCREMENT feature. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeRecordCompareWithSkip( - int nKey1, const void *pKey1, /* Left key */ - UnpackedRecord *pPKey2, /* Right key */ - int bSkip /* If true, skip the first field */ -){ - u32 d1; /* Offset into aKey[] of next data element */ - int i; /* Index of next field to compare */ - u32 szHdr1; /* Size of record header in bytes */ - u32 idx1; /* Offset of first type in header */ - int rc = 0; /* Return value */ - Mem *pRhs = pPKey2->aMem; /* Next field of pPKey2 to compare */ - KeyInfo *pKeyInfo; - const unsigned char *aKey1 = (const unsigned char *)pKey1; - Mem mem1; +case OP_NewRowid: { /* out2 */ + i64 v; /* The new rowid */ + VdbeCursor *pC; /* Cursor of table to get the new rowid */ + int res; /* Result of an sqlcipher_sqlite3BtreeLast() */ + int cnt; /* Counter to limit the number of searches */ +#ifndef SQLITE_OMIT_AUTOINCREMENT + Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ + VdbeFrame *pFrame; /* Root frame of VDBE */ +#endif - /* If bSkip is true, then the caller has already determined that the first - ** two elements in the keys are equal. Fix the various stack variables so - ** that this routine begins comparing at the second field. */ - if( bSkip ){ - u32 s1; - idx1 = 1 + getVarint32(&aKey1[1], s1); - szHdr1 = aKey1[0]; - d1 = szHdr1 + sqlcipher_sqlite3VdbeSerialTypeLen(s1); - i = 1; - pRhs++; - }else{ - idx1 = getVarint32(aKey1, szHdr1); - d1 = szHdr1; - i = 0; - } - if( d1>(unsigned)nKey1 ){ - pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; - return 0; /* Corruption */ - } + v = 0; + res = 0; + pOut = out2Prerelease(p, pOp); + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->isTable ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); + { + /* The next rowid or record number (different terms for the same + ** thing) is obtained in a two-step algorithm. + ** + ** First we attempt to find the largest existing rowid and add one + ** to that. But if the largest existing rowid is already the maximum + ** positive integer, we have to fall through to the second + ** probabilistic algorithm + ** + ** The second algorithm is to select a rowid at random and see if + ** it already exists in the table. If it does not exist, we have + ** succeeded. If the random rowid does exist, we select a new one + ** and try again, up to 100 times. + */ + assert( pC->isTable ); - VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */ - assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField - || CORRUPT_DB ); - assert( pPKey2->pKeyInfo->aSortFlags!=0 ); - assert( pPKey2->pKeyInfo->nKeyField>0 ); - assert( idx1<=szHdr1 || CORRUPT_DB ); - do{ - u32 serial_type; +#ifdef SQLITE_32BIT_ROWID +# define MAX_ROWID 0x7fffffff +#else + /* Some compilers complain about constants of the form 0x7fffffffffffffff. + ** Others complain about 0x7ffffffffffffffffLL. The following macro seems + ** to provide the constant while making all compilers happy. + */ +# define MAX_ROWID (i64)( (((u64)0x7fffffff)<<32) | (u64)0xffffffff ) +#endif - /* RHS is an integer */ - if( pRhs->flags & (MEM_Int|MEM_IntReal) ){ - testcase( pRhs->flags & MEM_Int ); - testcase( pRhs->flags & MEM_IntReal ); - serial_type = aKey1[idx1]; - testcase( serial_type==12 ); - if( serial_type>=10 ){ - rc = +1; - }else if( serial_type==0 ){ - rc = -1; - }else if( serial_type==7 ){ - sqlcipher_sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); - rc = -sqlcipher_sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r); - }else{ - i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]); - i64 rhs = pRhs->u.i; - if( lhsrhs ){ - rc = +1; - } + if( !pC->useRandomRowid ){ + rc = sqlcipher_sqlite3BtreeLast(pC->uc.pCursor, &res); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; } - } - - /* RHS is real */ - else if( pRhs->flags & MEM_Real ){ - serial_type = aKey1[idx1]; - if( serial_type>=10 ){ - /* Serial types 12 or greater are strings and blobs (greater than - ** numbers). Types 10 and 11 are currently "reserved for future - ** use", so it doesn't really matter what the results of comparing - ** them to numberic values are. */ - rc = +1; - }else if( serial_type==0 ){ - rc = -1; + if( res ){ + v = 1; /* IMP: R-61914-48074 */ }else{ - sqlcipher_sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); - if( serial_type==7 ){ - if( mem1.u.ru.r ){ - rc = -1; - }else if( mem1.u.r>pRhs->u.r ){ - rc = +1; - } + assert( sqlcipher_sqlite3BtreeCursorIsValid(pC->uc.pCursor) ); + v = sqlcipher_sqlite3BtreeIntegerKey(pC->uc.pCursor); + if( v>=MAX_ROWID ){ + pC->useRandomRowid = 1; }else{ - rc = sqlcipher_sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r); + v++; /* IMP: R-29538-34987 */ } } } - /* RHS is a string */ - else if( pRhs->flags & MEM_Str ){ - getVarint32NR(&aKey1[idx1], serial_type); - testcase( serial_type==12 ); - if( serial_type<12 ){ - rc = -1; - }else if( !(serial_type & 0x01) ){ - rc = +1; +#ifndef SQLITE_OMIT_AUTOINCREMENT + if( pOp->p3 ){ + /* Assert that P3 is a valid memory cell. */ + assert( pOp->p3>0 ); + if( p->pFrame ){ + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); + /* Assert that P3 is a valid memory cell. */ + assert( pOp->p3<=pFrame->nMem ); + pMem = &pFrame->aMem[pOp->p3]; }else{ - mem1.n = (serial_type - 12) / 2; - testcase( (d1+mem1.n)==(unsigned)nKey1 ); - testcase( (d1+mem1.n+1)==(unsigned)nKey1 ); - if( (d1+mem1.n) > (unsigned)nKey1 - || (pKeyInfo = pPKey2->pKeyInfo)->nAllField<=i - ){ - pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; - return 0; /* Corruption */ - }else if( pKeyInfo->aColl[i] ){ - mem1.enc = pKeyInfo->enc; - mem1.db = pKeyInfo->db; - mem1.flags = MEM_Str; - mem1.z = (char*)&aKey1[d1]; - rc = vdbeCompareMemString( - &mem1, pRhs, pKeyInfo->aColl[i], &pPKey2->errCode - ); - }else{ - int nCmp = MIN(mem1.n, pRhs->n); - rc = memcmp(&aKey1[d1], pRhs->z, nCmp); - if( rc==0 ) rc = mem1.n - pRhs->n; - } + /* Assert that P3 is a valid memory cell. */ + assert( pOp->p3<=(p->nMem+1 - p->nCursor) ); + pMem = &aMem[pOp->p3]; + memAboutToChange(p, pMem); } - } + assert( memIsValid(pMem) ); - /* RHS is a blob */ - else if( pRhs->flags & MEM_Blob ){ - assert( (pRhs->flags & MEM_Zero)==0 || pRhs->n==0 ); - getVarint32NR(&aKey1[idx1], serial_type); - testcase( serial_type==12 ); - if( serial_type<12 || (serial_type & 0x01) ){ - rc = -1; - }else{ - int nStr = (serial_type - 12) / 2; - testcase( (d1+nStr)==(unsigned)nKey1 ); - testcase( (d1+nStr+1)==(unsigned)nKey1 ); - if( (d1+nStr) > (unsigned)nKey1 ){ - pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; - return 0; /* Corruption */ - }else if( pRhs->flags & MEM_Zero ){ - if( !isAllZero((const char*)&aKey1[d1],nStr) ){ - rc = 1; - }else{ - rc = nStr - pRhs->u.nZero; - } - }else{ - int nCmp = MIN(nStr, pRhs->n); - rc = memcmp(&aKey1[d1], pRhs->z, nCmp); - if( rc==0 ) rc = nStr - pRhs->n; - } + REGISTER_TRACE(pOp->p3, pMem); + sqlcipher_sqlite3VdbeMemIntegerify(pMem); + assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ + if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ + rc = SQLITE_FULL; /* IMP: R-17817-00630 */ + goto abort_due_to_error; } + if( vu.i+1 ){ + v = pMem->u.i + 1; + } + pMem->u.i = v; } - - /* RHS is null */ - else{ - serial_type = aKey1[idx1]; - rc = (serial_type!=0); - } - - if( rc!=0 ){ - int sortFlags = pPKey2->pKeyInfo->aSortFlags[i]; - if( sortFlags ){ - if( (sortFlags & KEYINFO_ORDER_BIGNULL)==0 - || ((sortFlags & KEYINFO_ORDER_DESC) - !=(serial_type==0 || (pRhs->flags&MEM_Null))) - ){ - rc = -rc; - } +#endif + if( pC->useRandomRowid ){ + /* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the + ** largest possible integer (9223372036854775807) then the database + ** engine starts picking positive candidate ROWIDs at random until + ** it finds one that is not previously used. */ + assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is + ** an AUTOINCREMENT table. */ + cnt = 0; + do{ + sqlcipher_sqlite3_randomness(sizeof(v), &v); + v &= (MAX_ROWID>>1); v++; /* Ensure that v is greater than zero */ + }while( ((rc = sqlcipher_sqlite3BtreeTableMoveto(pC->uc.pCursor, (u64)v, + 0, &res))==SQLITE_OK) + && (res==0) + && (++cnt<100)); + if( rc ) goto abort_due_to_error; + if( res==0 ){ + rc = SQLITE_FULL; /* IMP: R-38219-53002 */ + goto abort_due_to_error; } - assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) ); - assert( mem1.szMalloc==0 ); /* See comment below */ - return rc; + assert( v>0 ); /* EV: R-40812-03570 */ } - - i++; - if( i==pPKey2->nField ) break; - pRhs++; - d1 += sqlcipher_sqlite3VdbeSerialTypeLen(serial_type); - idx1 += sqlcipher_sqlite3VarintLen(serial_type); - }while( idx1<(unsigned)szHdr1 && d1<=(unsigned)nKey1 ); - - /* No memory allocation is ever used on mem1. Prove this using - ** the following assert(). If the assert() fails, it indicates a - ** memory leak and a need to call sqlcipher_sqlite3VdbeMemRelease(&mem1). */ - assert( mem1.szMalloc==0 ); - - /* rc==0 here means that one or both of the keys ran out of fields and - ** all the fields up to that point were equal. Return the default_rc - ** value. */ - assert( CORRUPT_DB - || vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc) - || pPKey2->pKeyInfo->db->mallocFailed - ); - pPKey2->eqSeen = 1; - return pPKey2->default_rc; -} -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeRecordCompare( - int nKey1, const void *pKey1, /* Left key */ - UnpackedRecord *pPKey2 /* Right key */ -){ - return sqlcipher_sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0); + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; + } + pOut->u.i = v; + break; } - -/* -** This function is an optimized version of sqlcipher_sqlite3VdbeRecordCompare() -** that (a) the first field of pPKey2 is an integer, and (b) the -** size-of-header varint at the start of (pKey1/nKey1) fits in a single -** byte (i.e. is less than 128). +/* Opcode: Insert P1 P2 P3 P4 P5 +** Synopsis: intkey=r[P3] data=r[P2] ** -** To avoid concerns about buffer overreads, this routine is only used -** on schemas where the maximum valid header size is 63 bytes or less. +** Write an entry into the table of cursor P1. A new entry is +** created if it doesn't already exist or the data for an existing +** entry is overwritten. The data is the value MEM_Blob stored in register +** number P2. The key is stored in register P3. The key must +** be a MEM_Int. +** +** If the OPFLAG_NCHANGE flag of P5 is set, then the row change count is +** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P5 is set, +** then rowid is stored for subsequent return by the +** sqlcipher_sqlite3_last_insert_rowid() function (otherwise it is unmodified). +** +** If the OPFLAG_USESEEKRESULT flag of P5 is set, the implementation might +** run faster by avoiding an unnecessary seek on cursor P1. However, +** the OPFLAG_USESEEKRESULT flag must only be set if there have been no prior +** seeks on the cursor or if the most recent seek used a key equal to P3. +** +** If the OPFLAG_ISUPDATE flag is set, then this opcode is part of an +** UPDATE operation. Otherwise (if the flag is clear) then this opcode +** is part of an INSERT operation. The difference is only important to +** the update hook. +** +** Parameter P4 may point to a Table structure, or may be NULL. If it is +** not NULL, then the update-hook (sqlcipher_sqlite3.xUpdateCallback) is invoked +** following a successful insert. +** +** (WARNING/TODO: If P1 is a pseudo-cursor and P2 is dynamically +** allocated, then ownership of P2 is transferred to the pseudo-cursor +** and register P2 becomes ephemeral. If the cursor is changed, the +** value of register P2 will then change. Make sure this does not +** cause any problems.) +** +** This instruction only works on tables. The equivalent instruction +** for indices is OP_IdxInsert. */ -static int vdbeRecordCompareInt( - int nKey1, const void *pKey1, /* Left key */ - UnpackedRecord *pPKey2 /* Right key */ -){ - const u8 *aKey = &((const u8*)pKey1)[*(const u8*)pKey1 & 0x3F]; - int serial_type = ((const u8*)pKey1)[1]; - int res; - u32 y; - u64 x; - i64 v; - i64 lhs; - - vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); - assert( (*(u8*)pKey1)<=0x3F || CORRUPT_DB ); - switch( serial_type ){ - case 1: { /* 1-byte signed integer */ - lhs = ONE_BYTE_INT(aKey); - testcase( lhs<0 ); - break; - } - case 2: { /* 2-byte signed integer */ - lhs = TWO_BYTE_INT(aKey); - testcase( lhs<0 ); - break; - } - case 3: { /* 3-byte signed integer */ - lhs = THREE_BYTE_INT(aKey); - testcase( lhs<0 ); - break; - } - case 4: { /* 4-byte signed integer */ - y = FOUR_BYTE_UINT(aKey); - lhs = (i64)*(int*)&y; - testcase( lhs<0 ); - break; - } - case 5: { /* 6-byte signed integer */ - lhs = FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey); - testcase( lhs<0 ); - break; - } - case 6: { /* 8-byte signed integer */ - x = FOUR_BYTE_UINT(aKey); - x = (x<<32) | FOUR_BYTE_UINT(aKey+4); - lhs = *(i64*)&x; - testcase( lhs<0 ); - break; - } - case 8: - lhs = 0; - break; - case 9: - lhs = 1; - break; +case OP_Insert: { + Mem *pData; /* MEM cell holding data for the record to be inserted */ + Mem *pKey; /* MEM cell holding key for the record */ + VdbeCursor *pC; /* Cursor to table into which insert is written */ + int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ + const char *zDb; /* database name - used by the update hook */ + Table *pTab; /* Table structure - used by update and pre-update hooks */ + BtreePayload x; /* Payload to be inserted */ - /* This case could be removed without changing the results of running - ** this code. Including it causes gcc to generate a faster switch - ** statement (since the range of switch targets now starts at zero and - ** is contiguous) but does not cause any duplicate code to be generated - ** (as gcc is clever enough to combine the two like cases). Other - ** compilers might be similar. */ - case 0: case 7: - return sqlcipher_sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2); + pData = &aMem[pOp->p2]; + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( memIsValid(pData) ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->deferredMoveto==0 ); + assert( pC->uc.pCursor!=0 ); + assert( (pOp->p5 & OPFLAG_ISNOOP) || pC->isTable ); + assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC ); + REGISTER_TRACE(pOp->p2, pData); + sqlcipher_sqlite3VdbeIncrWriteCounter(p, pC); - default: - return sqlcipher_sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2); - } + pKey = &aMem[pOp->p3]; + assert( pKey->flags & MEM_Int ); + assert( memIsValid(pKey) ); + REGISTER_TRACE(pOp->p3, pKey); + x.nKey = pKey->u.i; - v = pPKey2->aMem[0].u.i; - if( v>lhs ){ - res = pPKey2->r1; - }else if( vr2; - }else if( pPKey2->nField>1 ){ - /* The first fields of the two keys are equal. Compare the trailing - ** fields. */ - res = sqlcipher_sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); + if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){ + assert( pC->iDb>=0 ); + zDb = db->aDb[pC->iDb].zDbSName; + pTab = pOp->p4.pTab; + assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) ); }else{ - /* The first fields of the two keys are equal and there are no trailing - ** fields. Return pPKey2->default_rc in this case. */ - res = pPKey2->default_rc; - pPKey2->eqSeen = 1; - } - - assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) ); - return res; -} - -/* -** This function is an optimized version of sqlcipher_sqlite3VdbeRecordCompare() -** that (a) the first field of pPKey2 is a string, that (b) the first field -** uses the collation sequence BINARY and (c) that the size-of-header varint -** at the start of (pKey1/nKey1) fits in a single byte. -*/ -static int vdbeRecordCompareString( - int nKey1, const void *pKey1, /* Left key */ - UnpackedRecord *pPKey2 /* Right key */ -){ - const u8 *aKey1 = (const u8*)pKey1; - int serial_type; - int res; - - assert( pPKey2->aMem[0].flags & MEM_Str ); - vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); - serial_type = (u8)(aKey1[1]); - if( serial_type >= 0x80 ){ - sqlcipher_sqlite3GetVarint32(&aKey1[1], (u32*)&serial_type); + pTab = 0; + zDb = 0; } - if( serial_type<12 ){ - res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ - }else if( !(serial_type & 0x01) ){ - res = pPKey2->r2; /* (pKey1/nKey1) is a blob */ - }else{ - int nCmp; - int nStr; - int szHdr = aKey1[0]; - nStr = (serial_type-12) / 2; - if( (szHdr + nStr) > nKey1 ){ - pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; - return 0; /* Corruption */ +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + /* Invoke the pre-update hook, if any */ + if( pTab ){ + if( db->xPreUpdateCallback && !(pOp->p5 & OPFLAG_ISUPDATE) ){ + sqlcipher_sqlite3VdbePreUpdateHook(p,pC,SQLITE_INSERT,zDb,pTab,x.nKey,pOp->p2,-1); } - nCmp = MIN( pPKey2->aMem[0].n, nStr ); - res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp); - - if( res>0 ){ - res = pPKey2->r2; - }else if( res<0 ){ - res = pPKey2->r1; - }else{ - res = nStr - pPKey2->aMem[0].n; - if( res==0 ){ - if( pPKey2->nField>1 ){ - res = sqlcipher_sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); - }else{ - res = pPKey2->default_rc; - pPKey2->eqSeen = 1; - } - }else if( res>0 ){ - res = pPKey2->r2; - }else{ - res = pPKey2->r1; - } + if( db->xUpdateCallback==0 || pTab->aCol==0 ){ + /* Prevent post-update hook from running in cases when it should not */ + pTab = 0; } } + if( pOp->p5 & OPFLAG_ISNOOP ) break; +#endif - assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) - || CORRUPT_DB - || pPKey2->pKeyInfo->db->mallocFailed + if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; + if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; + assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 ); + x.pData = pData->z; + x.nData = pData->n; + seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); + if( pData->flags & MEM_Zero ){ + x.nZero = pData->u.nZero; + }else{ + x.nZero = 0; + } + x.pKey = 0; + rc = sqlcipher_sqlite3BtreeInsert(pC->uc.pCursor, &x, + (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)), + seekResult ); - return res; -} + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; -/* -** Return a pointer to an sqlcipher_sqlite3VdbeRecordCompare() compatible function -** suitable for comparing serialized records to the unpacked record passed -** as the only argument. -*/ -SQLITE_PRIVATE RecordCompare sqlcipher_sqlite3VdbeFindCompare(UnpackedRecord *p){ - /* varintRecordCompareInt() and varintRecordCompareString() both assume - ** that the size-of-header varint that occurs at the start of each record - ** fits in a single byte (i.e. is 127 or less). varintRecordCompareInt() - ** also assumes that it is safe to overread a buffer by at least the - ** maximum possible legal header size plus 8 bytes. Because there is - ** guaranteed to be at least 74 (but not 136) bytes of padding following each - ** buffer passed to varintRecordCompareInt() this makes it convenient to - ** limit the size of the header to 64 bytes in cases where the first field - ** is an integer. - ** - ** The easiest way to enforce this limit is to consider only records with - ** 13 fields or less. If the first field is an integer, the maximum legal - ** header size is (12*5 + 1 + 1) bytes. */ - if( p->pKeyInfo->nAllField<=13 ){ - int flags = p->aMem[0].flags; - if( p->pKeyInfo->aSortFlags[0] ){ - if( p->pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL ){ - return sqlcipher_sqlite3VdbeRecordCompare; - } - p->r1 = 1; - p->r2 = -1; - }else{ - p->r1 = -1; - p->r2 = 1; - } - if( (flags & MEM_Int) ){ - return vdbeRecordCompareInt; - } - testcase( flags & MEM_Real ); - testcase( flags & MEM_Null ); - testcase( flags & MEM_Blob ); - if( (flags & (MEM_Real|MEM_IntReal|MEM_Null|MEM_Blob))==0 - && p->pKeyInfo->aColl[0]==0 - ){ - assert( flags & MEM_Str ); - return vdbeRecordCompareString; - } + /* Invoke the update-hook if required. */ + if( rc ) goto abort_due_to_error; + if( pTab ){ + assert( db->xUpdateCallback!=0 ); + assert( pTab->aCol!=0 ); + db->xUpdateCallback(db->pUpdateArg, + (pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT, + zDb, pTab->zName, x.nKey); } - - return sqlcipher_sqlite3VdbeRecordCompare; + break; } -/* -** pCur points at an index entry created using the OP_MakeRecord opcode. -** Read the rowid (the last field in the record) and store it in *rowid. -** Return SQLITE_OK if everything works, or an error code otherwise. +/* Opcode: RowCell P1 P2 P3 * * +** +** P1 and P2 are both open cursors. Both must be opened on the same type +** of table - intkey or index. This opcode is used as part of copying +** the current row from P2 into P1. If the cursors are opened on intkey +** tables, register P3 contains the rowid to use with the new record in +** P1. If they are opened on index tables, P3 is not used. +** +** This opcode must be followed by either an Insert or InsertIdx opcode +** with the OPFLAG_PREFORMAT flag set to complete the insert operation. +*/ +case OP_RowCell: { + VdbeCursor *pDest; /* Cursor to write to */ + VdbeCursor *pSrc; /* Cursor to read from */ + i64 iKey; /* Rowid value to insert with */ + assert( pOp[1].opcode==OP_Insert || pOp[1].opcode==OP_IdxInsert ); + assert( pOp[1].opcode==OP_Insert || pOp->p3==0 ); + assert( pOp[1].opcode==OP_IdxInsert || pOp->p3>0 ); + assert( pOp[1].p5 & OPFLAG_PREFORMAT ); + pDest = p->apCsr[pOp->p1]; + pSrc = p->apCsr[pOp->p2]; + iKey = pOp->p3 ? aMem[pOp->p3].u.i : 0; + rc = sqlcipher_sqlite3BtreeTransferRow(pDest->uc.pCursor, pSrc->uc.pCursor, iKey); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + break; +}; + +/* Opcode: Delete P1 P2 P3 P4 P5 ** -** pCur might be pointing to text obtained from a corrupt database file. -** So the content cannot be trusted. Do appropriate checks on the content. +** Delete the record at which the P1 cursor is currently pointing. +** +** If the OPFLAG_SAVEPOSITION bit of the P5 parameter is set, then +** the cursor will be left pointing at either the next or the previous +** record in the table. If it is left pointing at the next record, then +** the next Next instruction will be a no-op. As a result, in this case +** it is ok to delete a record from within a Next loop. If +** OPFLAG_SAVEPOSITION bit of P5 is clear, then the cursor will be +** left in an undefined state. +** +** If the OPFLAG_AUXDELETE bit is set on P5, that indicates that this +** delete one of several associated with deleting a table row and all its +** associated index entries. Exactly one of those deletes is the "primary" +** delete. The others are all on OPFLAG_FORDELETE cursors or else are +** marked with the AUXDELETE flag. +** +** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row +** change count is incremented (otherwise not). +** +** P1 must not be pseudo-table. It has to be a real table with +** multiple rows. +** +** If P4 is not NULL then it points to a Table object. In this case either +** the update or pre-update hook, or both, may be invoked. The P1 cursor must +** have been positioned using OP_NotFound prior to invoking this opcode in +** this case. Specifically, if one is configured, the pre-update hook is +** invoked if P4 is not NULL. The update-hook is invoked if one is configured, +** P4 is not NULL, and the OPFLAG_NCHANGE flag is set in P2. +** +** If the OPFLAG_ISUPDATE flag is set in P2, then P3 contains the address +** of the memory cell that contains the value that the rowid of the row will +** be set to by the update. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeIdxRowid(sqlcipher_sqlite3 *db, BtCursor *pCur, i64 *rowid){ - i64 nCellKey = 0; - int rc; - u32 szHdr; /* Size of the header */ - u32 typeRowid; /* Serial type of the rowid */ - u32 lenRowid; /* Size of the rowid */ - Mem m, v; - - /* Get the size of the index entry. Only indices entries of less - ** than 2GiB are support - anything large must be database corruption. - ** Any corruption is detected in sqlcipher_sqlite3BtreeParseCellPtr(), though, so - ** this code can safely assume that nCellKey is 32-bits - */ - assert( sqlcipher_sqlite3BtreeCursorIsValid(pCur) ); - nCellKey = sqlcipher_sqlite3BtreePayloadSize(pCur); - assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey ); +case OP_Delete: { + VdbeCursor *pC; + const char *zDb; + Table *pTab; + int opflags; - /* Read in the complete content of the index entry */ - sqlcipher_sqlite3VdbeMemInit(&m, db, 0); - rc = sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m); - if( rc ){ - return rc; - } + opflags = pOp->p2; + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); + assert( pC->deferredMoveto==0 ); + sqlcipher_sqlite3VdbeIncrWriteCounter(p, pC); - /* The index entry must begin with a header size */ - getVarint32NR((u8*)m.z, szHdr); - testcase( szHdr==3 ); - testcase( szHdr==m.n ); - testcase( szHdr>0x7fffffff ); - assert( m.n>=0 ); - if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){ - goto idx_rowid_corruption; +#ifdef SQLITE_DEBUG + if( pOp->p4type==P4_TABLE + && HasRowid(pOp->p4.pTab) + && pOp->p5==0 + && sqlcipher_sqlite3BtreeCursorIsValidNN(pC->uc.pCursor) + ){ + /* If p5 is zero, the seek operation that positioned the cursor prior to + ** OP_Delete will have also set the pC->movetoTarget field to the rowid of + ** the row that is being deleted */ + i64 iKey = sqlcipher_sqlite3BtreeIntegerKey(pC->uc.pCursor); + assert( CORRUPT_DB || pC->movetoTarget==iKey ); } +#endif - /* The last field of the index should be an integer - the ROWID. - ** Verify that the last entry really is an integer. */ - getVarint32NR((u8*)&m.z[szHdr-1], typeRowid); - testcase( typeRowid==1 ); - testcase( typeRowid==2 ); - testcase( typeRowid==3 ); - testcase( typeRowid==4 ); - testcase( typeRowid==5 ); - testcase( typeRowid==6 ); - testcase( typeRowid==8 ); - testcase( typeRowid==9 ); - if( unlikely(typeRowid<1 || typeRowid>9 || typeRowid==7) ){ - goto idx_rowid_corruption; - } - lenRowid = sqlcipher_sqlite3SmallTypeSizes[typeRowid]; - testcase( (u32)m.n==szHdr+lenRowid ); - if( unlikely((u32)m.np4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){ + assert( pC->iDb>=0 ); + assert( pOp->p4.pTab!=0 ); + zDb = db->aDb[pC->iDb].zDbSName; + pTab = pOp->p4.pTab; + if( (pOp->p5 & OPFLAG_SAVEPOSITION)!=0 && pC->isTable ){ + pC->movetoTarget = sqlcipher_sqlite3BtreeIntegerKey(pC->uc.pCursor); + } + }else{ + zDb = 0; + pTab = 0; } - /* Fetch the integer off the end of the index record */ - sqlcipher_sqlite3VdbeSerialGet((u8*)&m.z[m.n-lenRowid], typeRowid, &v); - *rowid = v.u.i; - sqlcipher_sqlite3VdbeMemRelease(&m); - return SQLITE_OK; - - /* Jump here if database corruption is detected after m has been - ** allocated. Free the m object and return SQLITE_CORRUPT. */ -idx_rowid_corruption: - testcase( m.szMalloc!=0 ); - sqlcipher_sqlite3VdbeMemRelease(&m); - return SQLITE_CORRUPT_BKPT; -} - -/* -** Compare the key of the index entry that cursor pC is pointing to against -** the key string in pUnpacked. Write into *pRes a number -** that is negative, zero, or positive if pC is less than, equal to, -** or greater than pUnpacked. Return SQLITE_OK on success. -** -** pUnpacked is either created without a rowid or is truncated so that it -** omits the rowid at the end. The rowid at the end of the index entry -** is ignored as well. Hence, this routine only compares the prefixes -** of the keys prior to the final rowid, not the entire key. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeIdxKeyCompare( - sqlcipher_sqlite3 *db, /* Database connection */ - VdbeCursor *pC, /* The cursor to compare against */ - UnpackedRecord *pUnpacked, /* Unpacked version of key */ - int *res /* Write the comparison result here */ -){ - i64 nCellKey = 0; - int rc; - BtCursor *pCur; - Mem m; +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + /* Invoke the pre-update-hook if required. */ + assert( db->xPreUpdateCallback==0 || pTab==pOp->p4.pTab ); + if( db->xPreUpdateCallback && pTab ){ + assert( !(opflags & OPFLAG_ISUPDATE) + || HasRowid(pTab)==0 + || (aMem[pOp->p3].flags & MEM_Int) + ); + sqlcipher_sqlite3VdbePreUpdateHook(p, pC, + (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE, + zDb, pTab, pC->movetoTarget, + pOp->p3, -1 + ); + } + if( opflags & OPFLAG_ISNOOP ) break; +#endif - assert( pC->eCurType==CURTYPE_BTREE ); - pCur = pC->uc.pCursor; - assert( sqlcipher_sqlite3BtreeCursorIsValid(pCur) ); - nCellKey = sqlcipher_sqlite3BtreePayloadSize(pCur); - /* nCellKey will always be between 0 and 0xffffffff because of the way - ** that btreeParseCellPtr() and sqlcipher_sqlite3GetVarint32() are implemented */ - if( nCellKey<=0 || nCellKey>0x7fffffff ){ - *res = 0; - return SQLITE_CORRUPT_BKPT; + /* Only flags that can be set are SAVEPOISTION and AUXDELETE */ + assert( (pOp->p5 & ~(OPFLAG_SAVEPOSITION|OPFLAG_AUXDELETE))==0 ); + assert( OPFLAG_SAVEPOSITION==BTREE_SAVEPOSITION ); + assert( OPFLAG_AUXDELETE==BTREE_AUXDELETE ); + +#ifdef SQLITE_DEBUG + if( p->pFrame==0 ){ + if( pC->isEphemeral==0 + && (pOp->p5 & OPFLAG_AUXDELETE)==0 + && (pC->wrFlag & OPFLAG_FORDELETE)==0 + ){ + nExtraDelete++; + } + if( pOp->p2 & OPFLAG_NCHANGE ){ + nExtraDelete--; + } } - sqlcipher_sqlite3VdbeMemInit(&m, db, 0); - rc = sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m); - if( rc ){ - return rc; +#endif + + rc = sqlcipher_sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5); + pC->cacheStatus = CACHE_STALE; + pC->seekResult = 0; + if( rc ) goto abort_due_to_error; + + /* Invoke the update-hook if required. */ + if( opflags & OPFLAG_NCHANGE ){ + p->nChange++; + if( db->xUpdateCallback && ALWAYS(pTab!=0) && HasRowid(pTab) ){ + db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName, + pC->movetoTarget); + assert( pC->iDb>=0 ); + } } - *res = sqlcipher_sqlite3VdbeRecordCompareWithSkip(m.n, m.z, pUnpacked, 0); - sqlcipher_sqlite3VdbeMemRelease(&m); - return SQLITE_OK; -} -/* -** This routine sets the value to be returned by subsequent calls to -** sqlcipher_sqlite3_changes() on the database handle 'db'. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetChanges(sqlcipher_sqlite3 *db, int nChange){ - assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - db->nChange = nChange; - db->nTotalChange += nChange; + break; } - -/* -** Set a flag in the vdbe to update the change counter when it is finalised -** or reset. +/* Opcode: ResetCount * * * * * +** +** The value of the change counter is copied to the database handle +** change counter (returned by subsequent calls to sqlcipher_sqlite3_changes()). +** Then the VMs internal change counter resets to 0. +** This is used by trigger programs. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeCountChanges(Vdbe *v){ - v->changeCntOn = 1; +case OP_ResetCount: { + sqlcipher_sqlite3VdbeSetChanges(db, p->nChange); + p->nChange = 0; + break; } -/* -** Mark every prepared statement associated with a database connection -** as expired. +/* Opcode: SorterCompare P1 P2 P3 P4 +** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2 ** -** An expired statement means that recompilation of the statement is -** recommend. Statements expire when things happen that make their -** programs obsolete. Removing user-defined functions or collating -** sequences, or changing an authorization function are the types of -** things that make prepared statements obsolete. +** P1 is a sorter cursor. This instruction compares a prefix of the +** record blob in register P3 against a prefix of the entry that +** the sorter cursor currently points to. Only the first P4 fields +** of r[P3] and the sorter record are compared. ** -** If iCode is 1, then expiration is advisory. The statement should -** be reprepared before being restarted, but if it is already running -** it is allowed to run to completion. +** If either P3 or the sorter contains a NULL in one of their significant +** fields (not counting the P4 fields at the end which are ignored) then +** the comparison is assumed to be equal. ** -** Internally, this function just sets the Vdbe.expired flag on all -** prepared statements. The flag is set to 1 for an immediate expiration -** and set to 2 for an advisory expiration. +** Fall through to next instruction if the two records compare equal to +** each other. Jump to P2 if they are different. */ -SQLITE_PRIVATE void sqlcipher_sqlite3ExpirePreparedStatements(sqlcipher_sqlite3 *db, int iCode){ - Vdbe *p; - for(p = db->pVdbe; p; p=p->pNext){ - p->expired = iCode+1; - } -} +case OP_SorterCompare: { + VdbeCursor *pC; + int res; + int nKeyCol; -/* -** Return the database associated with the Vdbe. -*/ -SQLITE_PRIVATE sqlcipher_sqlite3 *sqlcipher_sqlite3VdbeDb(Vdbe *v){ - return v->db; -} + pC = p->apCsr[pOp->p1]; + assert( isSorter(pC) ); + assert( pOp->p4type==P4_INT32 ); + pIn3 = &aMem[pOp->p3]; + nKeyCol = pOp->p4.i; + res = 0; + rc = sqlcipher_sqlite3VdbeSorterCompare(pC, pIn3, nKeyCol, &res); + VdbeBranchTaken(res!=0,2); + if( rc ) goto abort_due_to_error; + if( res ) goto jump_to_p2; + break; +}; -/* -** Return the SQLITE_PREPARE flags for a Vdbe. +/* Opcode: SorterData P1 P2 P3 * * +** Synopsis: r[P2]=data +** +** Write into register P2 the current sorter data for sorter cursor P1. +** Then clear the column header cache on cursor P3. +** +** This opcode is normally use to move a record out of the sorter and into +** a register that is the source for a pseudo-table cursor created using +** OpenPseudo. That pseudo-table cursor is the one that is identified by +** parameter P3. Clearing the P3 column cache as part of this opcode saves +** us from having to issue a separate NullRow instruction to clear that cache. */ -SQLITE_PRIVATE u8 sqlcipher_sqlite3VdbePrepareFlags(Vdbe *v){ - return v->prepFlags; +case OP_SorterData: { + VdbeCursor *pC; + + pOut = &aMem[pOp->p2]; + pC = p->apCsr[pOp->p1]; + assert( isSorter(pC) ); + rc = sqlcipher_sqlite3VdbeSorterRowkey(pC, pOut); + assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) ); + assert( pOp->p1>=0 && pOp->p1nCursor ); + if( rc ) goto abort_due_to_error; + p->apCsr[pOp->p3]->cacheStatus = CACHE_STALE; + break; } -/* -** Return a pointer to an sqlcipher_sqlite3_value structure containing the value bound -** parameter iVar of VM v. Except, if the value is an SQL NULL, return -** 0 instead. Unless it is NULL, apply affinity aff (one of the SQLITE_AFF_* -** constants) to the value before returning it. +/* Opcode: RowData P1 P2 P3 * * +** Synopsis: r[P2]=data ** -** The returned value must be freed by the caller using sqlcipher_sqlite3ValueFree(). +** Write into register P2 the complete row content for the row at +** which cursor P1 is currently pointing. +** There is no interpretation of the data. +** It is just copied onto the P2 register exactly as +** it is found in the database file. +** +** If cursor P1 is an index, then the content is the key of the row. +** If cursor P2 is a table, then the content extracted is the data. +** +** If the P1 cursor must be pointing to a valid row (not a NULL row) +** of a real table, not a pseudo-table. +** +** If P3!=0 then this opcode is allowed to make an ephemeral pointer +** into the database page. That means that the content of the output +** register will be invalidated as soon as the cursor moves - including +** moves caused by other cursors that "save" the current cursors +** position in order that they can write to the same table. If P3==0 +** then a copy of the data is made into memory. P3!=0 is faster, but +** P3==0 is safer. +** +** If P3!=0 then the content of the P2 register is unsuitable for use +** in OP_Result and any OP_Result will invalidate the P2 register content. +** The P2 register content is invalidated by opcodes like OP_Function or +** by any use of another cursor pointing to the same table. */ -SQLITE_PRIVATE sqlcipher_sqlite3_value *sqlcipher_sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff){ - assert( iVar>0 ); - if( v ){ - Mem *pMem = &v->aVar[iVar-1]; - assert( (v->db->flags & SQLITE_EnableQPSG)==0 ); - if( 0==(pMem->flags & MEM_Null) ){ - sqlcipher_sqlite3_value *pRet = sqlcipher_sqlite3ValueNew(v->db); - if( pRet ){ - sqlcipher_sqlite3VdbeMemCopy((Mem *)pRet, pMem); - sqlcipher_sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8); - } - return pRet; - } - } - return 0; -} +case OP_RowData: { + VdbeCursor *pC; + BtCursor *pCrsr; + u32 n; -/* -** Configure SQL variable iVar so that binding a new value to it signals -** to sqlcipher_sqlite3_reoptimize() that re-preparing the statement may result -** in a better query plan. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSetVarmask(Vdbe *v, int iVar){ - assert( iVar>0 ); - assert( (v->db->flags & SQLITE_EnableQPSG)==0 ); - if( iVar>=32 ){ - v->expmask |= 0x80000000; - }else{ - v->expmask |= ((u32)1 << (iVar-1)); + pOut = out2Prerelease(p, pOp); + + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( isSorter(pC)==0 ); + assert( pC->nullRow==0 ); + assert( pC->uc.pCursor!=0 ); + pCrsr = pC->uc.pCursor; + + /* The OP_RowData opcodes always follow OP_NotExists or + ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions + ** that might invalidate the cursor. + ** If this where not the case, on of the following assert()s + ** would fail. Should this ever change (because of changes in the code + ** generator) then the fix would be to insert a call to + ** sqlcipher_sqlite3VdbeCursorMoveto(). + */ + assert( pC->deferredMoveto==0 ); + assert( sqlcipher_sqlite3BtreeCursorIsValid(pCrsr) ); + + n = sqlcipher_sqlite3BtreePayloadSize(pCrsr); + if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; } + testcase( n==0 ); + rc = sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(pCrsr, n, pOut); + if( rc ) goto abort_due_to_error; + if( !pOp->p3 ) Deephemeralize(pOut); + UPDATE_MAX_BLOBSIZE(pOut); + REGISTER_TRACE(pOp->p2, pOut); + break; } -/* -** Cause a function to throw an error if it was call from OP_PureFunc -** rather than OP_Function. +/* Opcode: Rowid P1 P2 * * * +** Synopsis: r[P2]=PX rowid of P1 ** -** OP_PureFunc means that the function must be deterministic, and should -** throw an error if it is given inputs that would make it non-deterministic. -** This routine is invoked by date/time functions that use non-deterministic -** features such as 'now'. +** Store in register P2 an integer which is the key of the table entry that +** P1 is currently point to. +** +** P1 can be either an ordinary table or a virtual table. There used to +** be a separate OP_VRowid opcode for use with virtual tables, but this +** one opcode now works for both table types. */ -SQLITE_PRIVATE int sqlcipher_sqlite3NotPureFunc(sqlcipher_sqlite3_context *pCtx){ - const VdbeOp *pOp; -#ifdef SQLITE_ENABLE_STAT4 - if( pCtx->pVdbe==0 ) return 1; -#endif - pOp = pCtx->pVdbe->aOp + pCtx->iOp; - if( pOp->opcode==OP_PureFunc ){ - const char *zContext; - char *zMsg; - if( pOp->p5 & NC_IsCheck ){ - zContext = "a CHECK constraint"; - }else if( pOp->p5 & NC_GenCol ){ - zContext = "a generated column"; - }else{ - zContext = "an index"; - } - zMsg = sqlcipher_sqlite3_mprintf("non-deterministic use of %s() in %s", - pCtx->pFunc->zName, zContext); - sqlcipher_sqlite3_result_error(pCtx, zMsg, -1); - sqlcipher_sqlite3_free(zMsg); - return 0; - } - return 1; -} +case OP_Rowid: { /* out2 */ + VdbeCursor *pC; + i64 v; + sqlcipher_sqlite3_vtab *pVtab; + const sqlcipher_sqlite3_module *pModule; + pOut = out2Prerelease(p, pOp); + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); + if( pC->nullRow ){ + pOut->flags = MEM_Null; + break; + }else if( pC->deferredMoveto ){ + v = pC->movetoTarget; #ifndef SQLITE_OMIT_VIRTUALTABLE -/* -** Transfer error message text from an sqlcipher_sqlite3_vtab.zErrMsg (text stored -** in memory obtained from sqlcipher_sqlite3_malloc) into a Vdbe.zErrMsg (text stored -** in memory obtained from sqlcipher_sqlite3DbMalloc). -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VtabImportErrmsg(Vdbe *p, sqlcipher_sqlite3_vtab *pVtab){ - if( pVtab->zErrMsg ){ - sqlcipher_sqlite3 *db = p->db; - sqlcipher_sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = sqlcipher_sqlite3DbStrDup(db, pVtab->zErrMsg); - sqlcipher_sqlite3_free(pVtab->zErrMsg); - pVtab->zErrMsg = 0; - } -} + }else if( pC->eCurType==CURTYPE_VTAB ){ + assert( pC->uc.pVCur!=0 ); + pVtab = pC->uc.pVCur->pVtab; + pModule = pVtab->pModule; + assert( pModule->xRowid ); + rc = pModule->xRowid(pC->uc.pVCur, &v); + sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); + if( rc ) goto abort_due_to_error; #endif /* SQLITE_OMIT_VIRTUALTABLE */ - -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK - -/* -** If the second argument is not NULL, release any allocations associated -** with the memory cells in the p->aMem[] array. Also free the UnpackedRecord -** structure itself, using sqlcipher_sqlite3DbFree(). -** -** This function is used to free UnpackedRecord structures allocated by -** the vdbeUnpackRecord() function found in vdbeapi.c. -*/ -static void vdbeFreeUnpacked(sqlcipher_sqlite3 *db, int nField, UnpackedRecord *p){ - if( p ){ - int i; - for(i=0; iaMem[i]; - if( pMem->zMalloc ) sqlcipher_sqlite3VdbeMemRelease(pMem); + }else{ + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); + rc = sqlcipher_sqlite3VdbeCursorRestore(pC); + if( rc ) goto abort_due_to_error; + if( pC->nullRow ){ + pOut->flags = MEM_Null; + break; } - sqlcipher_sqlite3DbFreeNN(db, p); + v = sqlcipher_sqlite3BtreeIntegerKey(pC->uc.pCursor); } + pOut->u.i = v; + break; } -#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK -/* -** Invoke the pre-update hook. If this is an UPDATE or DELETE pre-update call, -** then cursor passed as the second argument should point to the row about -** to be update or deleted. If the application calls sqlcipher_sqlite3_preupdate_old(), -** the required value will be read from the row the cursor points to. +/* Opcode: NullRow P1 * * * * +** +** Move the cursor P1 to a null row. Any OP_Column operations +** that occur while the cursor is on the null row will always +** write a NULL. +** +** If cursor P1 is not previously opened, open it now to a special +** pseudo-cursor that always returns NULL for every column. */ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbePreUpdateHook( - Vdbe *v, /* Vdbe pre-update hook is invoked by */ - VdbeCursor *pCsr, /* Cursor to grab old.* values from */ - int op, /* SQLITE_INSERT, UPDATE or DELETE */ - const char *zDb, /* Database name */ - Table *pTab, /* Modified table */ - i64 iKey1, /* Initial key value */ - int iReg /* Register for new.* record */ -){ - sqlcipher_sqlite3 *db = v->db; - i64 iKey2; - PreUpdate preupdate; - const char *zTbl = pTab->zName; - static const u8 fakeSortOrder = 0; +case OP_NullRow: { + VdbeCursor *pC; - assert( db->pPreUpdate==0 ); - memset(&preupdate, 0, sizeof(PreUpdate)); - if( HasRowid(pTab)==0 ){ - iKey1 = iKey2 = 0; - preupdate.pPk = sqlcipher_sqlite3PrimaryKeyIndex(pTab); - }else{ - if( op==SQLITE_UPDATE ){ - iKey2 = v->aMem[iReg].u.i; - }else{ - iKey2 = iKey1; - } + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + if( pC==0 ){ + /* If the cursor is not already open, create a special kind of + ** pseudo-cursor that always gives null rows. */ + pC = allocateCursor(p, pOp->p1, 1, CURTYPE_PSEUDO); + if( pC==0 ) goto no_mem; + pC->seekResult = 0; + pC->isTable = 1; + pC->noReuse = 1; + pC->uc.pCursor = sqlcipher_sqlite3BtreeFakeValidCursor(); } - - assert( pCsr->nField==pTab->nCol - || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1) - ); - - preupdate.v = v; - preupdate.pCsr = pCsr; - preupdate.op = op; - preupdate.iNewReg = iReg; - preupdate.keyinfo.db = db; - preupdate.keyinfo.enc = ENC(db); - preupdate.keyinfo.nKeyField = pTab->nCol; - preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder; - preupdate.iKey1 = iKey1; - preupdate.iKey2 = iKey2; - preupdate.pTab = pTab; - - db->pPreUpdate = &preupdate; - db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); - db->pPreUpdate = 0; - sqlcipher_sqlite3DbFree(db, preupdate.aRecord); - vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); - vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); - if( preupdate.aNew ){ - int i; - for(i=0; inField; i++){ - sqlcipher_sqlite3VdbeMemRelease(&preupdate.aNew[i]); - } - sqlcipher_sqlite3DbFreeNN(db, preupdate.aNew); + pC->nullRow = 1; + pC->cacheStatus = CACHE_STALE; + if( pC->eCurType==CURTYPE_BTREE ){ + assert( pC->uc.pCursor!=0 ); + sqlcipher_sqlite3BtreeClearCursor(pC->uc.pCursor); } +#ifdef SQLITE_DEBUG + if( pC->seekOp==0 ) pC->seekOp = OP_NullRow; +#endif + break; } -#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ -/************** End of vdbeaux.c *********************************************/ -/************** Begin file vdbeapi.c *****************************************/ -/* -** 2004 May 26 +/* Opcode: SeekEnd P1 * * * * ** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** Position cursor P1 at the end of the btree for the purpose of +** appending a new entry onto the btree. ** -** 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. +** It is assumed that the cursor is used only for appending and so +** if the cursor is valid, then the cursor must already be pointing +** at the end of the btree and so no changes are made to +** the cursor. +*/ +/* Opcode: Last P1 P2 * * * ** -************************************************************************* +** The next use of the Rowid or Column or Prev instruction for P1 +** will refer to the last entry in the database table or index. +** If the table or index is empty and P2>0, then jump immediately to P2. +** If P2 is 0 or if the table or index is not empty, fall through +** to the following instruction. ** -** This file contains code use to implement APIs that are part of the -** VDBE. +** This opcode leaves the cursor configured to move in reverse order, +** from the end toward the beginning. In other words, the cursor is +** configured to use Prev, not Next. */ -/* #include "sqliteInt.h" */ -/* #include "vdbeInt.h" */ +case OP_SeekEnd: +case OP_Last: { /* jump */ + VdbeCursor *pC; + BtCursor *pCrsr; + int res; -#ifndef SQLITE_OMIT_DEPRECATED -/* -** Return TRUE (non-zero) of the statement supplied as an argument needs -** to be recompiled. A statement needs to be recompiled whenever the -** execution environment changes in a way that would alter the program -** that sqlcipher_sqlite3_prepare() generates. For example, if new functions or -** collating sequences are registered or if an authorizer function is -** added or changed. -*/ -SQLITE_API int sqlcipher_sqlite3_expired(sqlcipher_sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe*)pStmt; - return p==0 || p->expired; -} + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + pCrsr = pC->uc.pCursor; + res = 0; + assert( pCrsr!=0 ); +#ifdef SQLITE_DEBUG + pC->seekOp = pOp->opcode; #endif - -/* -** Check on a Vdbe to make sure it has not been finalized. Log -** an error and return true if it has been finalized (or is otherwise -** invalid). Return false if it is ok. -*/ -static int vdbeSafety(Vdbe *p){ - if( p->db==0 ){ - sqlcipher_sqlite3_log(SQLITE_MISUSE, "API called with finalized prepared statement"); - return 1; - }else{ - return 0; + if( pOp->opcode==OP_SeekEnd ){ + assert( pOp->p2==0 ); + pC->seekResult = -1; + if( sqlcipher_sqlite3BtreeCursorIsValidNN(pCrsr) ){ + break; + } } -} -static int vdbeSafetyNotNull(Vdbe *p){ - if( p==0 ){ - sqlcipher_sqlite3_log(SQLITE_MISUSE, "API called with NULL prepared statement"); - return 1; - }else{ - return vdbeSafety(p); + rc = sqlcipher_sqlite3BtreeLast(pCrsr, &res); + pC->nullRow = (u8)res; + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; + if( rc ) goto abort_due_to_error; + if( pOp->p2>0 ){ + VdbeBranchTaken(res!=0,2); + if( res ) goto jump_to_p2; } + break; } -#ifndef SQLITE_OMIT_TRACE -/* -** Invoke the profile callback. This routine is only called if we already -** know that the profile callback is defined and needs to be invoked. +/* Opcode: IfSmaller P1 P2 P3 * * +** +** Estimate the number of rows in the table P1. Jump to P2 if that +** estimate is less than approximately 2**(0.1*P3). */ -static SQLITE_NOINLINE void invokeProfileCallback(sqlcipher_sqlite3 *db, Vdbe *p){ - sqlcipher_sqlite3_int64 iNow; - sqlcipher_sqlite3_int64 iElapse; - assert( p->startTime>0 ); - assert( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 ); - assert( db->init.busy==0 ); - assert( p->zSql!=0 ); - sqlcipher_sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); - iElapse = (iNow - p->startTime)*1000000; -#ifndef SQLITE_OMIT_DEPRECATED - if( db->xProfile ){ - db->xProfile(db->pProfileArg, p->zSql, iElapse); - } -#endif - if( db->mTrace & SQLITE_TRACE_PROFILE ){ - db->trace.xV2(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); +case OP_IfSmaller: { /* jump */ + VdbeCursor *pC; + BtCursor *pCrsr; + int res; + i64 sz; + + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + pCrsr = pC->uc.pCursor; + assert( pCrsr ); + rc = sqlcipher_sqlite3BtreeFirst(pCrsr, &res); + if( rc ) goto abort_due_to_error; + if( res==0 ){ + sz = sqlcipher_sqlite3BtreeRowCountEst(pCrsr); + if( ALWAYS(sz>=0) && sqlcipher_sqlite3LogEst((u64)sz)p3 ) res = 1; } - p->startTime = 0; + VdbeBranchTaken(res!=0,2); + if( res ) goto jump_to_p2; + break; } -/* -** The checkProfileCallback(DB,P) macro checks to see if a profile callback -** is needed, and it invokes the callback if it is needed. -*/ -# define checkProfileCallback(DB,P) \ - if( ((P)->startTime)>0 ){ invokeProfileCallback(DB,P); } -#else -# define checkProfileCallback(DB,P) /*no-op*/ -#endif -/* -** The following routine destroys a virtual machine that is created by -** the sqlcipher_sqlite3_compile() routine. The integer returned is an SQLITE_ -** success/failure code that describes the result of executing the virtual -** machine. + +/* Opcode: SorterSort P1 P2 * * * ** -** This routine sets the error code and string returned by -** sqlcipher_sqlite3_errcode(), sqlcipher_sqlite3_errmsg() and sqlcipher_sqlite3_errmsg16(). +** After all records have been inserted into the Sorter object +** identified by P1, invoke this opcode to actually do the sorting. +** Jump to P2 if there are no records to be sorted. +** +** This opcode is an alias for OP_Sort and OP_Rewind that is used +** for Sorter objects. */ -SQLITE_API int sqlcipher_sqlite3_finalize(sqlcipher_sqlite3_stmt *pStmt){ - int rc; - if( pStmt==0 ){ - /* IMPLEMENTATION-OF: R-57228-12904 Invoking sqlcipher_sqlite3_finalize() on a NULL - ** pointer is a harmless no-op. */ - rc = SQLITE_OK; - }else{ - Vdbe *v = (Vdbe*)pStmt; - sqlcipher_sqlite3 *db = v->db; - if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT; - sqlcipher_sqlite3_mutex_enter(db->mutex); - checkProfileCallback(db, v); - rc = sqlcipher_sqlite3VdbeFinalize(v); - rc = sqlcipher_sqlite3ApiExit(db, rc); - sqlcipher_sqlite3LeaveMutexAndCloseZombie(db); - } - return rc; +/* Opcode: Sort P1 P2 * * * +** +** This opcode does exactly the same thing as OP_Rewind except that +** it increments an undocumented global variable used for testing. +** +** Sorting is accomplished by writing records into a sorting index, +** then rewinding that index and playing it back from beginning to +** end. We use the OP_Sort opcode instead of OP_Rewind to do the +** rewinding so that the global variable will be incremented and +** regression tests can determine whether or not the optimizer is +** correctly optimizing out sorts. +*/ +case OP_SorterSort: /* jump */ +case OP_Sort: { /* jump */ +#ifdef SQLITE_TEST + sqlcipher_sqlite3_sort_count++; + sqlcipher_sqlite3_search_count--; +#endif + p->aCounter[SQLITE_STMTSTATUS_SORT]++; + /* Fall through into OP_Rewind */ + /* no break */ deliberate_fall_through } - -/* -** Terminate the current execution of an SQL statement and reset it -** back to its starting state so that it can be reused. A success code from -** the prior execution is returned. +/* Opcode: Rewind P1 P2 * * * ** -** This routine sets the error code and string returned by -** sqlcipher_sqlite3_errcode(), sqlcipher_sqlite3_errmsg() and sqlcipher_sqlite3_errmsg16(). +** The next use of the Rowid or Column or Next instruction for P1 +** will refer to the first entry in the database table or index. +** If the table or index is empty, jump immediately to P2. +** If the table or index is not empty, fall through to the following +** instruction. +** +** This opcode leaves the cursor configured to move in forward order, +** from the beginning toward the end. In other words, the cursor is +** configured to use Next, not Prev. */ -SQLITE_API int sqlcipher_sqlite3_reset(sqlcipher_sqlite3_stmt *pStmt){ - int rc; - if( pStmt==0 ){ - rc = SQLITE_OK; +case OP_Rewind: { /* jump */ + VdbeCursor *pC; + BtCursor *pCrsr; + int res; + + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p5==0 ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) ); + res = 1; +#ifdef SQLITE_DEBUG + pC->seekOp = OP_Rewind; +#endif + if( isSorter(pC) ){ + rc = sqlcipher_sqlite3VdbeSorterRewind(pC, &res); }else{ - Vdbe *v = (Vdbe*)pStmt; - sqlcipher_sqlite3 *db = v->db; - sqlcipher_sqlite3_mutex_enter(db->mutex); - checkProfileCallback(db, v); - rc = sqlcipher_sqlite3VdbeReset(v); - sqlcipher_sqlite3VdbeRewind(v); - assert( (rc & (db->errMask))==rc ); - rc = sqlcipher_sqlite3ApiExit(db, rc); - sqlcipher_sqlite3_mutex_leave(db->mutex); + assert( pC->eCurType==CURTYPE_BTREE ); + pCrsr = pC->uc.pCursor; + assert( pCrsr ); + rc = sqlcipher_sqlite3BtreeFirst(pCrsr, &res); + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; } - return rc; + if( rc ) goto abort_due_to_error; + pC->nullRow = (u8)res; + assert( pOp->p2>0 && pOp->p2nOp ); + VdbeBranchTaken(res!=0,2); + if( res ) goto jump_to_p2; + break; } -/* -** Set all the parameters in the compiled SQL statement to NULL. +/* Opcode: Next P1 P2 P3 * P5 +** +** Advance cursor P1 so that it points to the next key/data pair in its +** table or index. If there are no more key/value pairs then fall through +** to the following instruction. But if the cursor advance was successful, +** jump immediately to P2. +** +** The Next opcode is only valid following an SeekGT, SeekGE, or +** OP_Rewind opcode used to position the cursor. Next is not allowed +** to follow SeekLT, SeekLE, or OP_Last. +** +** The P1 cursor must be for a real table, not a pseudo-table. P1 must have +** been opened prior to this opcode or the program will segfault. +** +** The P3 value is a hint to the btree implementation. If P3==1, that +** means P1 is an SQL index and that this instruction could have been +** omitted if that index had been unique. P3 is usually 0. P3 is +** always either 0 or 1. +** +** If P5 is positive and the jump is taken, then event counter +** number P5-1 in the prepared statement is incremented. +** +** See also: Prev */ -SQLITE_API int sqlcipher_sqlite3_clear_bindings(sqlcipher_sqlite3_stmt *pStmt){ - int i; - int rc = SQLITE_OK; - Vdbe *p = (Vdbe*)pStmt; -#if SQLITE_THREADSAFE - sqlcipher_sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex; -#endif - sqlcipher_sqlite3_mutex_enter(mutex); - for(i=0; inVar; i++){ - sqlcipher_sqlite3VdbeMemRelease(&p->aVar[i]); - p->aVar[i].flags = MEM_Null; - } - assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || p->expmask==0 ); - if( p->expmask ){ - p->expired = 1; - } - sqlcipher_sqlite3_mutex_leave(mutex); - return rc; -} +/* Opcode: Prev P1 P2 P3 * P5 +** +** Back up cursor P1 so that it points to the previous key/data pair in its +** table or index. If there is no previous key/value pairs then fall through +** to the following instruction. But if the cursor backup was successful, +** jump immediately to P2. +** +** +** The Prev opcode is only valid following an SeekLT, SeekLE, or +** OP_Last opcode used to position the cursor. Prev is not allowed +** to follow SeekGT, SeekGE, or OP_Rewind. +** +** The P1 cursor must be for a real table, not a pseudo-table. If P1 is +** not open then the behavior is undefined. +** +** The P3 value is a hint to the btree implementation. If P3==1, that +** means P1 is an SQL index and that this instruction could have been +** omitted if that index had been unique. P3 is usually 0. P3 is +** always either 0 or 1. +** +** If P5 is positive and the jump is taken, then event counter +** number P5-1 in the prepared statement is incremented. +*/ +/* Opcode: SorterNext P1 P2 * * P5 +** +** This opcode works just like OP_Next except that P1 must be a +** sorter object for which the OP_SorterSort opcode has been +** invoked. This opcode advances the cursor to the next sorted +** record, or jumps to P2 if there are no more sorted records. +*/ +case OP_SorterNext: { /* jump */ + VdbeCursor *pC; + pC = p->apCsr[pOp->p1]; + assert( isSorter(pC) ); + rc = sqlcipher_sqlite3VdbeSorterNext(db, pC); + goto next_tail; -/**************************** sqlcipher_sqlite3_value_ ******************************* -** The following routines extract information from a Mem or sqlcipher_sqlite3_value -** structure. -*/ -SQLITE_API const void *sqlcipher_sqlite3_value_blob(sqlcipher_sqlite3_value *pVal){ - Mem *p = (Mem*)pVal; - if( p->flags & (MEM_Blob|MEM_Str) ){ - if( ExpandBlob(p)!=SQLITE_OK ){ - assert( p->flags==MEM_Null && p->z==0 ); - return 0; - } - p->flags |= MEM_Blob; - return p->n ? p->z : 0; - }else{ - return sqlcipher_sqlite3_value_text(pVal); - } -} -SQLITE_API int sqlcipher_sqlite3_value_bytes(sqlcipher_sqlite3_value *pVal){ - return sqlcipher_sqlite3ValueBytes(pVal, SQLITE_UTF8); -} -SQLITE_API int sqlcipher_sqlite3_value_bytes16(sqlcipher_sqlite3_value *pVal){ - return sqlcipher_sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE); -} -SQLITE_API double sqlcipher_sqlite3_value_double(sqlcipher_sqlite3_value *pVal){ - return sqlcipher_sqlite3VdbeRealValue((Mem*)pVal); -} -SQLITE_API int sqlcipher_sqlite3_value_int(sqlcipher_sqlite3_value *pVal){ - return (int)sqlcipher_sqlite3VdbeIntValue((Mem*)pVal); -} -SQLITE_API sqlite_int64 sqlcipher_sqlite3_value_int64(sqlcipher_sqlite3_value *pVal){ - return sqlcipher_sqlite3VdbeIntValue((Mem*)pVal); -} -SQLITE_API unsigned int sqlcipher_sqlite3_value_subtype(sqlcipher_sqlite3_value *pVal){ - Mem *pMem = (Mem*)pVal; - return ((pMem->flags & MEM_Subtype) ? pMem->eSubtype : 0); -} -SQLITE_API void *sqlcipher_sqlite3_value_pointer(sqlcipher_sqlite3_value *pVal, const char *zPType){ - Mem *p = (Mem*)pVal; - if( (p->flags&(MEM_TypeMask|MEM_Term|MEM_Subtype)) == - (MEM_Null|MEM_Term|MEM_Subtype) - && zPType!=0 - && p->eSubtype=='p' - && strcmp(p->u.zPType, zPType)==0 - ){ - return (void*)p->z; - }else{ - return 0; +case OP_Prev: /* jump */ + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p5aCounter) ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->deferredMoveto==0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE + || pC->seekOp==OP_Last || pC->seekOp==OP_IfNoHope + || pC->seekOp==OP_NullRow); + rc = sqlcipher_sqlite3BtreePrevious(pC->uc.pCursor, pOp->p3); + goto next_tail; + +case OP_Next: /* jump */ + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p5aCounter) ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->deferredMoveto==0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE + || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found + || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid + || pC->seekOp==OP_IfNoHope); + rc = sqlcipher_sqlite3BtreeNext(pC->uc.pCursor, pOp->p3); + +next_tail: + pC->cacheStatus = CACHE_STALE; + VdbeBranchTaken(rc==SQLITE_OK,2); + if( rc==SQLITE_OK ){ + pC->nullRow = 0; + p->aCounter[pOp->p5]++; +#ifdef SQLITE_TEST + sqlcipher_sqlite3_search_count++; +#endif + goto jump_to_p2_and_check_for_interrupt; } + if( rc!=SQLITE_DONE ) goto abort_due_to_error; + rc = SQLITE_OK; + pC->nullRow = 1; + goto check_for_interrupt; } -SQLITE_API const unsigned char *sqlcipher_sqlite3_value_text(sqlcipher_sqlite3_value *pVal){ - return (const unsigned char *)sqlcipher_sqlite3ValueText(pVal, SQLITE_UTF8); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlcipher_sqlite3_value_text16(sqlcipher_sqlite3_value* pVal){ - return sqlcipher_sqlite3ValueText(pVal, SQLITE_UTF16NATIVE); -} -SQLITE_API const void *sqlcipher_sqlite3_value_text16be(sqlcipher_sqlite3_value *pVal){ - return sqlcipher_sqlite3ValueText(pVal, SQLITE_UTF16BE); -} -SQLITE_API const void *sqlcipher_sqlite3_value_text16le(sqlcipher_sqlite3_value *pVal){ - return sqlcipher_sqlite3ValueText(pVal, SQLITE_UTF16LE); -} -#endif /* SQLITE_OMIT_UTF16 */ -/* EVIDENCE-OF: R-12793-43283 Every value in SQLite has one of five -** fundamental datatypes: 64-bit signed integer 64-bit IEEE floating -** point number string BLOB NULL + +/* Opcode: IdxInsert P1 P2 P3 P4 P5 +** Synopsis: key=r[P2] +** +** Register P2 holds an SQL index key made using the +** MakeRecord instructions. This opcode writes that key +** into the index P1. Data for the entry is nil. +** +** If P4 is not zero, then it is the number of values in the unpacked +** key of reg(P2). In that case, P3 is the index of the first register +** for the unpacked key. The availability of the unpacked key can sometimes +** be an optimization. +** +** If P5 has the OPFLAG_APPEND bit set, that is a hint to the b-tree layer +** that this insert is likely to be an append. +** +** If P5 has the OPFLAG_NCHANGE bit set, then the change counter is +** incremented by this instruction. If the OPFLAG_NCHANGE bit is clear, +** then the change counter is unchanged. +** +** If the OPFLAG_USESEEKRESULT flag of P5 is set, the implementation might +** run faster by avoiding an unnecessary seek on cursor P1. However, +** the OPFLAG_USESEEKRESULT flag must only be set if there have been no prior +** seeks on the cursor or if the most recent seek used a key equivalent +** to P2. +** +** This instruction only works for indices. The equivalent instruction +** for tables is OP_Insert. */ -SQLITE_API int sqlcipher_sqlite3_value_type(sqlcipher_sqlite3_value* pVal){ - static const u8 aType[] = { - SQLITE_BLOB, /* 0x00 (not possible) */ - SQLITE_NULL, /* 0x01 NULL */ - SQLITE_TEXT, /* 0x02 TEXT */ - SQLITE_NULL, /* 0x03 (not possible) */ - SQLITE_INTEGER, /* 0x04 INTEGER */ - SQLITE_NULL, /* 0x05 (not possible) */ - SQLITE_INTEGER, /* 0x06 INTEGER + TEXT */ - SQLITE_NULL, /* 0x07 (not possible) */ - SQLITE_FLOAT, /* 0x08 FLOAT */ - SQLITE_NULL, /* 0x09 (not possible) */ - SQLITE_FLOAT, /* 0x0a FLOAT + TEXT */ - SQLITE_NULL, /* 0x0b (not possible) */ - SQLITE_INTEGER, /* 0x0c (not possible) */ - SQLITE_NULL, /* 0x0d (not possible) */ - SQLITE_INTEGER, /* 0x0e (not possible) */ - SQLITE_NULL, /* 0x0f (not possible) */ - SQLITE_BLOB, /* 0x10 BLOB */ - SQLITE_NULL, /* 0x11 (not possible) */ - SQLITE_TEXT, /* 0x12 (not possible) */ - SQLITE_NULL, /* 0x13 (not possible) */ - SQLITE_INTEGER, /* 0x14 INTEGER + BLOB */ - SQLITE_NULL, /* 0x15 (not possible) */ - SQLITE_INTEGER, /* 0x16 (not possible) */ - SQLITE_NULL, /* 0x17 (not possible) */ - SQLITE_FLOAT, /* 0x18 FLOAT + BLOB */ - SQLITE_NULL, /* 0x19 (not possible) */ - SQLITE_FLOAT, /* 0x1a (not possible) */ - SQLITE_NULL, /* 0x1b (not possible) */ - SQLITE_INTEGER, /* 0x1c (not possible) */ - SQLITE_NULL, /* 0x1d (not possible) */ - SQLITE_INTEGER, /* 0x1e (not possible) */ - SQLITE_NULL, /* 0x1f (not possible) */ - SQLITE_FLOAT, /* 0x20 INTREAL */ - SQLITE_NULL, /* 0x21 (not possible) */ - SQLITE_TEXT, /* 0x22 INTREAL + TEXT */ - SQLITE_NULL, /* 0x23 (not possible) */ - SQLITE_FLOAT, /* 0x24 (not possible) */ - SQLITE_NULL, /* 0x25 (not possible) */ - SQLITE_FLOAT, /* 0x26 (not possible) */ - SQLITE_NULL, /* 0x27 (not possible) */ - SQLITE_FLOAT, /* 0x28 (not possible) */ - SQLITE_NULL, /* 0x29 (not possible) */ - SQLITE_FLOAT, /* 0x2a (not possible) */ - SQLITE_NULL, /* 0x2b (not possible) */ - SQLITE_FLOAT, /* 0x2c (not possible) */ - SQLITE_NULL, /* 0x2d (not possible) */ - SQLITE_FLOAT, /* 0x2e (not possible) */ - SQLITE_NULL, /* 0x2f (not possible) */ - SQLITE_BLOB, /* 0x30 (not possible) */ - SQLITE_NULL, /* 0x31 (not possible) */ - SQLITE_TEXT, /* 0x32 (not possible) */ - SQLITE_NULL, /* 0x33 (not possible) */ - SQLITE_FLOAT, /* 0x34 (not possible) */ - SQLITE_NULL, /* 0x35 (not possible) */ - SQLITE_FLOAT, /* 0x36 (not possible) */ - SQLITE_NULL, /* 0x37 (not possible) */ - SQLITE_FLOAT, /* 0x38 (not possible) */ - SQLITE_NULL, /* 0x39 (not possible) */ - SQLITE_FLOAT, /* 0x3a (not possible) */ - SQLITE_NULL, /* 0x3b (not possible) */ - SQLITE_FLOAT, /* 0x3c (not possible) */ - SQLITE_NULL, /* 0x3d (not possible) */ - SQLITE_FLOAT, /* 0x3e (not possible) */ - SQLITE_NULL, /* 0x3f (not possible) */ - }; -#ifdef SQLITE_DEBUG - { - int eType = SQLITE_BLOB; - if( pVal->flags & MEM_Null ){ - eType = SQLITE_NULL; - }else if( pVal->flags & (MEM_Real|MEM_IntReal) ){ - eType = SQLITE_FLOAT; - }else if( pVal->flags & MEM_Int ){ - eType = SQLITE_INTEGER; - }else if( pVal->flags & MEM_Str ){ - eType = SQLITE_TEXT; - } - assert( eType == aType[pVal->flags&MEM_AffMask] ); - } -#endif - return aType[pVal->flags&MEM_AffMask]; -} +case OP_IdxInsert: { /* in2 */ + VdbeCursor *pC; + BtreePayload x; -/* Return true if a parameter to xUpdate represents an unchanged column */ -SQLITE_API int sqlcipher_sqlite3_value_nochange(sqlcipher_sqlite3_value *pVal){ - return (pVal->flags&(MEM_Null|MEM_Zero))==(MEM_Null|MEM_Zero); + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + sqlcipher_sqlite3VdbeIncrWriteCounter(p, pC); + assert( pC!=0 ); + assert( !isSorter(pC) ); + pIn2 = &aMem[pOp->p2]; + assert( (pIn2->flags & MEM_Blob) || (pOp->p5 & OPFLAG_PREFORMAT) ); + if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->isTable==0 ); + rc = ExpandBlob(pIn2); + if( rc ) goto abort_due_to_error; + x.nKey = pIn2->n; + x.pKey = pIn2->z; + x.aMem = aMem + pOp->p3; + x.nMem = (u16)pOp->p4.i; + rc = sqlcipher_sqlite3BtreeInsert(pC->uc.pCursor, &x, + (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)), + ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) + ); + assert( pC->deferredMoveto==0 ); + pC->cacheStatus = CACHE_STALE; + if( rc) goto abort_due_to_error; + break; } -/* Return true if a parameter value originated from an sqlcipher_sqlite3_bind() */ -SQLITE_API int sqlcipher_sqlite3_value_frombind(sqlcipher_sqlite3_value *pVal){ - return (pVal->flags&MEM_FromBind)!=0; +/* Opcode: SorterInsert P1 P2 * * * +** Synopsis: key=r[P2] +** +** Register P2 holds an SQL index key made using the +** MakeRecord instructions. This opcode writes that key +** into the sorter P1. Data for the entry is nil. +*/ +case OP_SorterInsert: { /* in2 */ + VdbeCursor *pC; + + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + sqlcipher_sqlite3VdbeIncrWriteCounter(p, pC); + assert( pC!=0 ); + assert( isSorter(pC) ); + pIn2 = &aMem[pOp->p2]; + assert( pIn2->flags & MEM_Blob ); + assert( pC->isTable==0 ); + rc = ExpandBlob(pIn2); + if( rc ) goto abort_due_to_error; + rc = sqlcipher_sqlite3VdbeSorterWrite(pC, pIn2); + if( rc) goto abort_due_to_error; + break; } -/* Make a copy of an sqlcipher_sqlite3_value object +/* Opcode: IdxDelete P1 P2 P3 * P5 +** Synopsis: key=r[P2@P3] +** +** The content of P3 registers starting at register P2 form +** an unpacked index key. This opcode removes that entry from the +** index opened by cursor P1. +** +** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error +** if no matching index entry is found. This happens when running +** an UPDATE or DELETE statement and the index entry to be updated +** or deleted is not found. For some uses of IdxDelete +** (example: the EXCEPT operator) it does not matter that no matching +** entry is found. For those cases, P5 is zero. Also, do not raise +** this (self-correcting and non-critical) error if in writable_schema mode. */ -SQLITE_API sqlcipher_sqlite3_value *sqlcipher_sqlite3_value_dup(const sqlcipher_sqlite3_value *pOrig){ - sqlcipher_sqlite3_value *pNew; - if( pOrig==0 ) return 0; - pNew = sqlcipher_sqlite3_malloc( sizeof(*pNew) ); - if( pNew==0 ) return 0; - memset(pNew, 0, sizeof(*pNew)); - memcpy(pNew, pOrig, MEMCELLSIZE); - pNew->flags &= ~MEM_Dyn; - pNew->db = 0; - if( pNew->flags&(MEM_Str|MEM_Blob) ){ - pNew->flags &= ~(MEM_Static|MEM_Dyn); - pNew->flags |= MEM_Ephem; - if( sqlcipher_sqlite3VdbeMemMakeWriteable(pNew)!=SQLITE_OK ){ - sqlcipher_sqlite3ValueFree(pNew); - pNew = 0; - } +case OP_IdxDelete: { + VdbeCursor *pC; + BtCursor *pCrsr; + int res; + UnpackedRecord r; + + assert( pOp->p3>0 ); + assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem+1 - p->nCursor)+1 ); + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + sqlcipher_sqlite3VdbeIncrWriteCounter(p, pC); + pCrsr = pC->uc.pCursor; + assert( pCrsr!=0 ); + r.pKeyInfo = pC->pKeyInfo; + r.nField = (u16)pOp->p3; + r.default_rc = 0; + r.aMem = &aMem[pOp->p2]; + rc = sqlcipher_sqlite3BtreeIndexMoveto(pCrsr, &r, &res); + if( rc ) goto abort_due_to_error; + if( res==0 ){ + rc = sqlcipher_sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); + if( rc ) goto abort_due_to_error; + }else if( pOp->p5 && !sqlcipher_sqlite3WritableSchema(db) ){ + rc = sqlcipher_sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption"); + goto abort_due_to_error; } - return pNew; + assert( pC->deferredMoveto==0 ); + pC->cacheStatus = CACHE_STALE; + pC->seekResult = 0; + break; } -/* Destroy an sqlcipher_sqlite3_value object previously obtained from -** sqlcipher_sqlite3_value_dup(). +/* Opcode: DeferredSeek P1 * P3 P4 * +** Synopsis: Move P3 to P1.rowid if needed +** +** P1 is an open index cursor and P3 is a cursor on the corresponding +** table. This opcode does a deferred seek of the P3 table cursor +** to the row that corresponds to the current row of P1. +** +** This is a deferred seek. Nothing actually happens until +** the cursor is used to read a record. That way, if no reads +** occur, no unnecessary I/O happens. +** +** P4 may be an array of integers (type P4_INTARRAY) containing +** one entry for each column in the P3 table. If array entry a(i) +** is non-zero, then reading column a(i)-1 from cursor P3 is +** equivalent to performing the deferred seek and then reading column i +** from P1. This information is stored in P3 and used to redirect +** reads against P3 over to P1, thus possibly avoiding the need to +** seek and read cursor P3. */ -SQLITE_API void sqlcipher_sqlite3_value_free(sqlcipher_sqlite3_value *pOld){ - sqlcipher_sqlite3ValueFree(pOld); -} - - -/**************************** sqlcipher_sqlite3_result_ ******************************* -** The following routines are used by user-defined functions to specify -** the function result. +/* Opcode: IdxRowid P1 P2 * * * +** Synopsis: r[P2]=rowid ** -** The setStrOrError() function calls sqlcipher_sqlite3VdbeMemSetStr() to store the -** result as a string or blob but if the string or blob is too large, it -** then sets the error code to SQLITE_TOOBIG +** Write into register P2 an integer which is the last entry in the record at +** the end of the index key pointed to by cursor P1. This integer should be +** the rowid of the table entry to which this index entry points. ** -** The invokeValueDestructor(P,X) routine invokes destructor function X() -** on value P is not going to be used and need to be destroyed. +** See also: Rowid, MakeRecord. */ -static void setResultStrOrError( - sqlcipher_sqlite3_context *pCtx, /* Function context */ - const char *z, /* String pointer */ - int n, /* Bytes in string, or negative */ - u8 enc, /* Encoding of z. 0 for BLOBs */ - void (*xDel)(void*) /* Destructor function */ -){ - if( sqlcipher_sqlite3VdbeMemSetStr(pCtx->pOut, z, n, enc, xDel)==SQLITE_TOOBIG ){ - sqlcipher_sqlite3_result_error_toobig(pCtx); - } -} -static int invokeValueDestructor( - const void *p, /* Value to destroy */ - void (*xDel)(void*), /* The destructor */ - sqlcipher_sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */ -){ - assert( xDel!=SQLITE_DYNAMIC ); - if( xDel==0 ){ - /* noop */ - }else if( xDel==SQLITE_TRANSIENT ){ - /* noop */ +case OP_DeferredSeek: +case OP_IdxRowid: { /* out2 */ + VdbeCursor *pC; /* The P1 index cursor */ + VdbeCursor *pTabCur; /* The P2 table cursor (OP_DeferredSeek only) */ + i64 rowid; /* Rowid that P1 current points to */ + + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE || IsNullCursor(pC) ); + assert( pC->uc.pCursor!=0 ); + assert( pC->isTable==0 || IsNullCursor(pC) ); + assert( pC->deferredMoveto==0 ); + assert( !pC->nullRow || pOp->opcode==OP_IdxRowid ); + + /* The IdxRowid and Seek opcodes are combined because of the commonality + ** of sqlcipher_sqlite3VdbeCursorRestore() and sqlcipher_sqlite3VdbeIdxRowid(). */ + rc = sqlcipher_sqlite3VdbeCursorRestore(pC); + + /* sqlcipher_sqlite3VbeCursorRestore() can only fail if the record has been deleted + ** out from under the cursor. That will never happens for an IdxRowid + ** or Seek opcode */ + if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; + + if( !pC->nullRow ){ + rowid = 0; /* Not needed. Only used to silence a warning. */ + rc = sqlcipher_sqlite3VdbeIdxRowid(db, pC->uc.pCursor, &rowid); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } + if( pOp->opcode==OP_DeferredSeek ){ + assert( pOp->p3>=0 && pOp->p3nCursor ); + pTabCur = p->apCsr[pOp->p3]; + assert( pTabCur!=0 ); + assert( pTabCur->eCurType==CURTYPE_BTREE ); + assert( pTabCur->uc.pCursor!=0 ); + assert( pTabCur->isTable ); + pTabCur->nullRow = 0; + pTabCur->movetoTarget = rowid; + pTabCur->deferredMoveto = 1; + pTabCur->cacheStatus = CACHE_STALE; + assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 ); + assert( !pTabCur->isEphemeral ); + pTabCur->ub.aAltMap = pOp->p4.ai; + assert( !pC->isEphemeral ); + pTabCur->pAltCursor = pC; + }else{ + pOut = out2Prerelease(p, pOp); + pOut->u.i = rowid; + } }else{ - xDel((void*)p); + assert( pOp->opcode==OP_IdxRowid ); + sqlcipher_sqlite3VdbeMemSetNull(&aMem[pOp->p2]); } - if( pCtx ) sqlcipher_sqlite3_result_error_toobig(pCtx); - return SQLITE_TOOBIG; -} -SQLITE_API void sqlcipher_sqlite3_result_blob( - sqlcipher_sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - assert( n>=0 ); - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - setResultStrOrError(pCtx, z, n, 0, xDel); + break; } -SQLITE_API void sqlcipher_sqlite3_result_blob64( - sqlcipher_sqlite3_context *pCtx, - const void *z, - sqlcipher_sqlite3_uint64 n, - void (*xDel)(void *) -){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - assert( xDel!=SQLITE_DYNAMIC ); - if( n>0x7fffffff ){ - (void)invokeValueDestructor(z, xDel, pCtx); - }else{ - setResultStrOrError(pCtx, z, (int)n, 0, xDel); + +/* Opcode: FinishSeek P1 * * * * +** +** If cursor P1 was previously moved via OP_DeferredSeek, complete that +** seek operation now, without further delay. If the cursor seek has +** already occurred, this instruction is a no-op. +*/ +case OP_FinishSeek: { + VdbeCursor *pC; /* The P1 index cursor */ + + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + if( pC->deferredMoveto ){ + rc = sqlcipher_sqlite3VdbeFinishMoveto(pC); + if( rc ) goto abort_due_to_error; } + break; } -SQLITE_API void sqlcipher_sqlite3_result_double(sqlcipher_sqlite3_context *pCtx, double rVal){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlcipher_sqlite3VdbeMemSetDouble(pCtx->pOut, rVal); -} -SQLITE_API void sqlcipher_sqlite3_result_error(sqlcipher_sqlite3_context *pCtx, const char *z, int n){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - pCtx->isError = SQLITE_ERROR; - sqlcipher_sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API void sqlcipher_sqlite3_result_error16(sqlcipher_sqlite3_context *pCtx, const void *z, int n){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - pCtx->isError = SQLITE_ERROR; - sqlcipher_sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); -} -#endif -SQLITE_API void sqlcipher_sqlite3_result_int(sqlcipher_sqlite3_context *pCtx, int iVal){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlcipher_sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal); -} -SQLITE_API void sqlcipher_sqlite3_result_int64(sqlcipher_sqlite3_context *pCtx, i64 iVal){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlcipher_sqlite3VdbeMemSetInt64(pCtx->pOut, iVal); -} -SQLITE_API void sqlcipher_sqlite3_result_null(sqlcipher_sqlite3_context *pCtx){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlcipher_sqlite3VdbeMemSetNull(pCtx->pOut); -} -SQLITE_API void sqlcipher_sqlite3_result_pointer( - sqlcipher_sqlite3_context *pCtx, - void *pPtr, - const char *zPType, - void (*xDestructor)(void*) -){ - Mem *pOut = pCtx->pOut; - assert( sqlcipher_sqlite3_mutex_held(pOut->db->mutex) ); - sqlcipher_sqlite3VdbeMemRelease(pOut); - pOut->flags = MEM_Null; - sqlcipher_sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor); -} -SQLITE_API void sqlcipher_sqlite3_result_subtype(sqlcipher_sqlite3_context *pCtx, unsigned int eSubtype){ - Mem *pOut = pCtx->pOut; - assert( sqlcipher_sqlite3_mutex_held(pOut->db->mutex) ); - pOut->eSubtype = eSubtype & 0xff; - pOut->flags |= MEM_Subtype; -} -SQLITE_API void sqlcipher_sqlite3_result_text( - sqlcipher_sqlite3_context *pCtx, - const char *z, - int n, - void (*xDel)(void *) -){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel); -} -SQLITE_API void sqlcipher_sqlite3_result_text64( - sqlcipher_sqlite3_context *pCtx, - const char *z, - sqlcipher_sqlite3_uint64 n, - void (*xDel)(void *), - unsigned char enc -){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - assert( xDel!=SQLITE_DYNAMIC ); - if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; - if( n>0x7fffffff ){ - (void)invokeValueDestructor(z, xDel, pCtx); + +/* Opcode: IdxGE P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] +** +** The P4 register values beginning with P3 form an unpacked index +** key that omits the PRIMARY KEY. Compare this key value against the index +** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID +** fields at the end. +** +** If the P1 index entry is greater than or equal to the key value +** then jump to P2. Otherwise fall through to the next instruction. +*/ +/* Opcode: IdxGT P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] +** +** The P4 register values beginning with P3 form an unpacked index +** key that omits the PRIMARY KEY. Compare this key value against the index +** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID +** fields at the end. +** +** If the P1 index entry is greater than the key value +** then jump to P2. Otherwise fall through to the next instruction. +*/ +/* Opcode: IdxLT P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] +** +** The P4 register values beginning with P3 form an unpacked index +** key that omits the PRIMARY KEY or ROWID. Compare this key value against +** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or +** ROWID on the P1 index. +** +** If the P1 index entry is less than the key value then jump to P2. +** Otherwise fall through to the next instruction. +*/ +/* Opcode: IdxLE P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] +** +** The P4 register values beginning with P3 form an unpacked index +** key that omits the PRIMARY KEY or ROWID. Compare this key value against +** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or +** ROWID on the P1 index. +** +** If the P1 index entry is less than or equal to the key value then jump +** to P2. Otherwise fall through to the next instruction. +*/ +case OP_IdxLE: /* jump */ +case OP_IdxGT: /* jump */ +case OP_IdxLT: /* jump */ +case OP_IdxGE: { /* jump */ + VdbeCursor *pC; + int res; + UnpackedRecord r; + + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->isOrdered ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0); + assert( pC->deferredMoveto==0 ); + assert( pOp->p4type==P4_INT32 ); + r.pKeyInfo = pC->pKeyInfo; + r.nField = (u16)pOp->p4.i; + if( pOp->opcodeopcode==OP_IdxLE || pOp->opcode==OP_IdxGT ); + r.default_rc = -1; }else{ - setResultStrOrError(pCtx, z, (int)n, enc, xDel); - } -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API void sqlcipher_sqlite3_result_text16( - sqlcipher_sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel); -} -SQLITE_API void sqlcipher_sqlite3_result_text16be( - sqlcipher_sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel); -} -SQLITE_API void sqlcipher_sqlite3_result_text16le( - sqlcipher_sqlite3_context *pCtx, - const void *z, - int n, - void (*xDel)(void *) -){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel); -} -#endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API void sqlcipher_sqlite3_result_value(sqlcipher_sqlite3_context *pCtx, sqlcipher_sqlite3_value *pValue){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlcipher_sqlite3VdbeMemCopy(pCtx->pOut, pValue); -} -SQLITE_API void sqlcipher_sqlite3_result_zeroblob(sqlcipher_sqlite3_context *pCtx, int n){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlcipher_sqlite3VdbeMemSetZeroBlob(pCtx->pOut, n); -} -SQLITE_API int sqlcipher_sqlite3_result_zeroblob64(sqlcipher_sqlite3_context *pCtx, u64 n){ - Mem *pOut = pCtx->pOut; - assert( sqlcipher_sqlite3_mutex_held(pOut->db->mutex) ); - if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){ - return SQLITE_TOOBIG; + assert( pOp->opcode==OP_IdxGE || pOp->opcode==OP_IdxLT ); + r.default_rc = 0; } - sqlcipher_sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n); - return SQLITE_OK; -} -SQLITE_API void sqlcipher_sqlite3_result_error_code(sqlcipher_sqlite3_context *pCtx, int errCode){ - pCtx->isError = errCode ? errCode : -1; + r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG - if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode; -#endif - if( pCtx->pOut->flags & MEM_Null ){ - sqlcipher_sqlite3VdbeMemSetStr(pCtx->pOut, sqlcipher_sqlite3ErrStr(errCode), -1, - SQLITE_UTF8, SQLITE_STATIC); + { + int i; + for(i=0; ip3+i, &aMem[pOp->p3+i]); + } } -} +#endif -/* Force an SQLITE_TOOBIG error. */ -SQLITE_API void sqlcipher_sqlite3_result_error_toobig(sqlcipher_sqlite3_context *pCtx){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - pCtx->isError = SQLITE_TOOBIG; - sqlcipher_sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1, - SQLITE_UTF8, SQLITE_STATIC); -} + /* Inlined version of sqlcipher_sqlite3VdbeIdxKeyCompare() */ + { + i64 nCellKey = 0; + BtCursor *pCur; + Mem m; -/* An SQLITE_NOMEM error. */ -SQLITE_API void sqlcipher_sqlite3_result_error_nomem(sqlcipher_sqlite3_context *pCtx){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlcipher_sqlite3VdbeMemSetNull(pCtx->pOut); - pCtx->isError = SQLITE_NOMEM_BKPT; - sqlcipher_sqlite3OomFault(pCtx->pOut->db); -} + assert( pC->eCurType==CURTYPE_BTREE ); + pCur = pC->uc.pCursor; + assert( sqlcipher_sqlite3BtreeCursorIsValid(pCur) ); + nCellKey = sqlcipher_sqlite3BtreePayloadSize(pCur); + /* nCellKey will always be between 0 and 0xffffffff because of the way + ** that btreeParseCellPtr() and sqlcipher_sqlite3GetVarint32() are implemented */ + if( nCellKey<=0 || nCellKey>0x7fffffff ){ + rc = SQLITE_CORRUPT_BKPT; + goto abort_due_to_error; + } + sqlcipher_sqlite3VdbeMemInit(&m, db, 0); + rc = sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m); + if( rc ) goto abort_due_to_error; + res = sqlcipher_sqlite3VdbeRecordCompareWithSkip(m.n, m.z, &r, 0); + sqlcipher_sqlite3VdbeMemReleaseMalloc(&m); + } + /* End of inlined sqlcipher_sqlite3VdbeIdxKeyCompare() */ -#ifndef SQLITE_UNTESTABLE -/* Force the INT64 value currently stored as the result to be -** a MEM_IntReal value. See the SQLITE_TESTCTRL_RESULT_INTREAL -** test-control. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3ResultIntReal(sqlcipher_sqlite3_context *pCtx){ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - if( pCtx->pOut->flags & MEM_Int ){ - pCtx->pOut->flags &= ~MEM_Int; - pCtx->pOut->flags |= MEM_IntReal; + assert( (OP_IdxLE&1)==(OP_IdxLT&1) && (OP_IdxGE&1)==(OP_IdxGT&1) ); + if( (pOp->opcode&1)==(OP_IdxLT&1) ){ + assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxLT ); + res = -res; + }else{ + assert( pOp->opcode==OP_IdxGE || pOp->opcode==OP_IdxGT ); + res++; } + VdbeBranchTaken(res>0,2); + assert( rc==SQLITE_OK ); + if( res>0 ) goto jump_to_p2; + break; } -#endif - -/* -** This function is called after a transaction has been committed. It -** invokes callbacks registered with sqlcipher_sqlite3_wal_hook() as required. +/* Opcode: Destroy P1 P2 P3 * * +** +** Delete an entire database table or index whose root page in the database +** file is given by P1. +** +** The table being destroyed is in the main database file if P3==0. If +** P3==1 then the table to be clear is in the auxiliary database file +** that is used to store tables create using CREATE TEMPORARY TABLE. +** +** If AUTOVACUUM is enabled then it is possible that another root page +** might be moved into the newly deleted root page in order to keep all +** root pages contiguous at the beginning of the database. The former +** value of the root page that moved - its value before the move occurred - +** is stored in register P2. If no page movement was required (because the +** table being dropped was already the last one in the database) then a +** zero is stored in register P2. If AUTOVACUUM is disabled then a zero +** is stored in register P2. +** +** This opcode throws an error if there are any active reader VMs when +** it is invoked. This is done to avoid the difficulty associated with +** updating existing cursors when a root page is moved in an AUTOVACUUM +** database. This error is thrown even if the database is not an AUTOVACUUM +** db in order to avoid introducing an incompatibility between autovacuum +** and non-autovacuum modes. +** +** See also: Clear */ -static int doWalCallbacks(sqlcipher_sqlite3 *db){ - int rc = SQLITE_OK; -#ifndef SQLITE_OMIT_WAL - int i; - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - int nEntry; - sqlcipher_sqlite3BtreeEnter(pBt); - nEntry = sqlcipher_sqlite3PagerWalCallback(sqlcipher_sqlite3BtreePager(pBt)); - sqlcipher_sqlite3BtreeLeave(pBt); - if( nEntry>0 && db->xWalCallback && rc==SQLITE_OK ){ - rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zDbSName, nEntry); - } +case OP_Destroy: { /* out2 */ + int iMoved; + int iDb; + + sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); + assert( p->readOnly==0 ); + assert( pOp->p1>1 ); + pOut = out2Prerelease(p, pOp); + pOut->flags = MEM_Null; + if( db->nVdbeRead > db->nVDestroy+1 ){ + rc = SQLITE_LOCKED; + p->errorAction = OE_Abort; + goto abort_due_to_error; + }else{ + iDb = pOp->p3; + assert( DbMaskTest(p->btreeMask, iDb) ); + iMoved = 0; /* Not needed. Only to silence a warning. */ + rc = sqlcipher_sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved); + pOut->flags = MEM_Int; + pOut->u.i = iMoved; + if( rc ) goto abort_due_to_error; +#ifndef SQLITE_OMIT_AUTOVACUUM + if( iMoved!=0 ){ + sqlcipher_sqlite3RootPageMoved(db, iDb, iMoved, pOp->p1); + /* All OP_Destroy operations occur on the same btree */ + assert( resetSchemaOnFault==0 || resetSchemaOnFault==iDb+1 ); + resetSchemaOnFault = iDb+1; } - } #endif - return rc; + } + break; } - -/* -** Execute the statement pStmt, either until a row of data is ready, the -** statement is completely executed or an error occurs. +/* Opcode: Clear P1 P2 P3 ** -** This routine implements the bulk of the logic behind the sqlite_step() -** API. The only thing omitted is the automatic recompile if a -** schema change has occurred. That detail is handled by the -** outer sqlcipher_sqlite3_step() wrapper procedure. +** Delete all contents of the database table or index whose root page +** in the database file is given by P1. But, unlike Destroy, do not +** remove the table or index from the database file. +** +** The table being clear is in the main database file if P2==0. If +** P2==1 then the table to be clear is in the auxiliary database file +** that is used to store tables create using CREATE TEMPORARY TABLE. +** +** If the P3 value is non-zero, then the row change count is incremented +** by the number of rows in the table being cleared. If P3 is greater +** than zero, then the value stored in register P3 is also incremented +** by the number of rows in the table being cleared. +** +** See also: Destroy */ -static int sqlcipher_sqlite3Step(Vdbe *p){ - sqlcipher_sqlite3 *db; - int rc; +case OP_Clear: { + i64 nChange; - assert(p); - if( p->magic!=VDBE_MAGIC_RUN ){ - /* We used to require that sqlcipher_sqlite3_reset() be called before retrying - ** sqlcipher_sqlite3_step() after any error or after SQLITE_DONE. But beginning - ** with version 3.7.0, we changed this so that sqlcipher_sqlite3_reset() would - ** be called automatically instead of throwing the SQLITE_MISUSE error. - ** This "automatic-reset" change is not technically an incompatibility, - ** since any application that receives an SQLITE_MISUSE is broken by - ** definition. - ** - ** Nevertheless, some published applications that were originally written - ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE - ** returns, and those were broken by the automatic-reset change. As a - ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the - ** legacy behavior of returning SQLITE_MISUSE for cases where the - ** previous sqlcipher_sqlite3_step() returned something other than a SQLITE_LOCKED - ** or SQLITE_BUSY error. - */ -#ifdef SQLITE_OMIT_AUTORESET - if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){ - sqlcipher_sqlite3_reset((sqlcipher_sqlite3_stmt*)p); - }else{ - return SQLITE_MISUSE_BKPT; + sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); + nChange = 0; + assert( p->readOnly==0 ); + assert( DbMaskTest(p->btreeMask, pOp->p2) ); + rc = sqlcipher_sqlite3BtreeClearTable(db->aDb[pOp->p2].pBt, (u32)pOp->p1, &nChange); + if( pOp->p3 ){ + p->nChange += nChange; + if( pOp->p3>0 ){ + assert( memIsValid(&aMem[pOp->p3]) ); + memAboutToChange(p, &aMem[pOp->p3]); + aMem[pOp->p3].u.i += nChange; } -#else - sqlcipher_sqlite3_reset((sqlcipher_sqlite3_stmt*)p); -#endif } + if( rc ) goto abort_due_to_error; + break; +} - /* Check that malloc() has not failed. If it has, return early. */ - db = p->db; - if( db->mallocFailed ){ - p->rc = SQLITE_NOMEM; - return SQLITE_NOMEM_BKPT; - } +/* Opcode: ResetSorter P1 * * * * +** +** Delete all contents from the ephemeral table or sorter +** that is open on cursor P1. +** +** This opcode only works for cursors used for sorting and +** opened with OP_OpenEphemeral or OP_SorterOpen. +*/ +case OP_ResetSorter: { + VdbeCursor *pC; - if( p->pc<0 && p->expired ){ - p->rc = SQLITE_SCHEMA; - rc = SQLITE_ERROR; - if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ - /* If this statement was prepared using saved SQL and an - ** error has occurred, then return the error code in p->rc to the - ** caller. Set the error code in the database handle to the same value. - */ - rc = sqlcipher_sqlite3VdbeTransferError(p); - } - goto end_of_step; + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + if( isSorter(pC) ){ + sqlcipher_sqlite3VdbeSorterReset(db, pC->uc.pSorter); + }else{ + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->isEphemeral ); + rc = sqlcipher_sqlite3BtreeClearTableOfCursor(pC->uc.pCursor); + if( rc ) goto abort_due_to_error; } - if( p->pc<0 ){ - /* If there are no other statements currently running, then - ** reset the interrupt flag. This prevents a call to sqlcipher_sqlite3_interrupt - ** from interrupting a statement that has not yet started. - */ - if( db->nVdbeActive==0 ){ - AtomicStore(&db->u1.isInterrupted, 0); - } + break; +} - assert( db->nVdbeWrite>0 || db->autoCommit==0 - || (db->nDeferredCons==0 && db->nDeferredImmCons==0) - ); +/* Opcode: CreateBtree P1 P2 P3 * * +** Synopsis: r[P2]=root iDb=P1 flags=P3 +** +** Allocate a new b-tree in the main database file if P1==0 or in the +** TEMP database file if P1==1 or in an attached database if +** P1>1. The P3 argument must be 1 (BTREE_INTKEY) for a rowid table +** it must be 2 (BTREE_BLOBKEY) for an index or WITHOUT ROWID table. +** The root page number of the new b-tree is stored in register P2. +*/ +case OP_CreateBtree: { /* out2 */ + Pgno pgno; + Db *pDb; -#ifndef SQLITE_OMIT_TRACE - if( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 - && !db->init.busy && p->zSql ){ - sqlcipher_sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); - }else{ - assert( p->startTime==0 ); - } -#endif + sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); + pOut = out2Prerelease(p, pOp); + pgno = 0; + assert( pOp->p3==BTREE_INTKEY || pOp->p3==BTREE_BLOBKEY ); + assert( pOp->p1>=0 && pOp->p1nDb ); + assert( DbMaskTest(p->btreeMask, pOp->p1) ); + assert( p->readOnly==0 ); + pDb = &db->aDb[pOp->p1]; + assert( pDb->pBt!=0 ); + rc = sqlcipher_sqlite3BtreeCreateTable(pDb->pBt, &pgno, pOp->p3); + if( rc ) goto abort_due_to_error; + pOut->u.i = pgno; + break; +} - db->nVdbeActive++; - if( p->readOnly==0 ) db->nVdbeWrite++; - if( p->bIsReader ) db->nVdbeRead++; - p->pc = 0; - } +/* Opcode: SqlExec * * * P4 * +** +** Run the SQL statement or statements specified in the P4 string. +*/ +case OP_SqlExec: { + sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); + db->nSqlExec++; + rc = sqlcipher_sqlite3_exec(db, pOp->p4.z, 0, 0, 0); + db->nSqlExec--; + if( rc ) goto abort_due_to_error; + break; +} + +/* Opcode: ParseSchema P1 * * P4 * +** +** Read and parse all entries from the schema table of database P1 +** that match the WHERE clause P4. If P4 is a NULL pointer, then the +** entire schema for P1 is reparsed. +** +** This opcode invokes the parser to create a new virtual machine, +** then runs the new virtual machine. It is thus a re-entrant opcode. +*/ +case OP_ParseSchema: { + int iDb; + const char *zSchema; + char *zSql; + InitData initData; + + /* Any prepared statement that invokes this opcode will hold mutexes + ** on every btree. This is a prerequisite for invoking + ** sqlcipher_sqlite3InitCallback(). + */ #ifdef SQLITE_DEBUG - p->rcApp = SQLITE_OK; -#endif -#ifndef SQLITE_OMIT_EXPLAIN - if( p->explain ){ - rc = sqlcipher_sqlite3VdbeList(p); - }else -#endif /* SQLITE_OMIT_EXPLAIN */ - { - db->nVdbeExec++; - rc = sqlcipher_sqlite3VdbeExec(p); - db->nVdbeExec--; + for(iDb=0; iDbnDb; iDb++){ + assert( iDb==1 || sqlcipher_sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); } - - if( rc!=SQLITE_ROW ){ -#ifndef SQLITE_OMIT_TRACE - /* If the statement completed successfully, invoke the profile callback */ - checkProfileCallback(db, p); #endif - if( rc==SQLITE_DONE && db->autoCommit ){ - assert( p->rc==SQLITE_OK ); - p->rc = doWalCallbacks(db); - if( p->rc!=SQLITE_OK ){ - rc = SQLITE_ERROR; + iDb = pOp->p1; + assert( iDb>=0 && iDbnDb ); + assert( DbHasProperty(db, iDb, DB_SchemaLoaded) + || db->mallocFailed + || (CORRUPT_DB && (db->flags & SQLITE_NoSchemaError)!=0) ); + +#ifndef SQLITE_OMIT_ALTERTABLE + if( pOp->p4.z==0 ){ + sqlcipher_sqlite3SchemaClear(db->aDb[iDb].pSchema); + db->mDbFlags &= ~DBFLAG_SchemaKnownOk; + rc = sqlcipher_sqlite3InitOne(db, iDb, &p->zErrMsg, pOp->p5); + db->mDbFlags |= DBFLAG_SchemaChange; + p->expired = 0; + }else +#endif + { + zSchema = LEGACY_SCHEMA_TABLE; + initData.db = db; + initData.iDb = iDb; + initData.pzErrMsg = &p->zErrMsg; + initData.mInitFlags = 0; + initData.mxPage = sqlcipher_sqlite3BtreeLastPage(db->aDb[iDb].pBt); + zSql = sqlcipher_sqlite3MPrintf(db, + "SELECT*FROM\"%w\".%s WHERE %s ORDER BY rowid", + db->aDb[iDb].zDbSName, zSchema, pOp->p4.z); + if( zSql==0 ){ + rc = SQLITE_NOMEM_BKPT; + }else{ + assert( db->init.busy==0 ); + db->init.busy = 1; + initData.rc = SQLITE_OK; + initData.nInitRow = 0; + assert( !db->mallocFailed ); + rc = sqlcipher_sqlite3_exec(db, zSql, sqlcipher_sqlite3InitCallback, &initData, 0); + if( rc==SQLITE_OK ) rc = initData.rc; + if( rc==SQLITE_OK && initData.nInitRow==0 ){ + /* The OP_ParseSchema opcode with a non-NULL P4 argument should parse + ** at least one SQL statement. Any less than that indicates that + ** the sqlite_schema table is corrupt. */ + rc = SQLITE_CORRUPT_BKPT; } - }else if( rc!=SQLITE_DONE && (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ - /* If this statement was prepared using saved SQL and an - ** error has occurred, then return the error code in p->rc to the - ** caller. Set the error code in the database handle to the same value. - */ - rc = sqlcipher_sqlite3VdbeTransferError(p); + sqlcipher_sqlite3DbFreeNN(db, zSql); + db->init.busy = 0; } } - - db->errCode = rc; - if( SQLITE_NOMEM==sqlcipher_sqlite3ApiExit(p->db, p->rc) ){ - p->rc = SQLITE_NOMEM_BKPT; - if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ) rc = p->rc; + if( rc ){ + sqlcipher_sqlite3ResetAllSchemasOfConnection(db); + if( rc==SQLITE_NOMEM ){ + goto no_mem; + } + goto abort_due_to_error; } -end_of_step: - /* There are only a limited number of result codes allowed from the - ** statements prepared using the legacy sqlcipher_sqlite3_prepare() interface */ - assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 - || rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR - || (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE - ); - return (rc&db->errMask); + break; } -/* -** This is the top-level implementation of sqlcipher_sqlite3_step(). Call -** sqlcipher_sqlite3Step() to do most of the work. If a schema error occurs, -** call sqlcipher_sqlite3Reprepare() and try again. +#if !defined(SQLITE_OMIT_ANALYZE) +/* Opcode: LoadAnalysis P1 * * * * +** +** Read the sqlite_stat1 table for database P1 and load the content +** of that table into the internal index hash table. This will cause +** the analysis to be used when preparing all subsequent queries. */ -SQLITE_API int sqlcipher_sqlite3_step(sqlcipher_sqlite3_stmt *pStmt){ - int rc = SQLITE_OK; /* Result from sqlcipher_sqlite3Step() */ - Vdbe *v = (Vdbe*)pStmt; /* the prepared statement */ - int cnt = 0; /* Counter to prevent infinite loop of reprepares */ - sqlcipher_sqlite3 *db; /* The database connection */ - - if( vdbeSafetyNotNull(v) ){ - return SQLITE_MISUSE_BKPT; - } - db = v->db; - sqlcipher_sqlite3_mutex_enter(db->mutex); - v->doingRerun = 0; - while( (rc = sqlcipher_sqlite3Step(v))==SQLITE_SCHEMA - && cnt++ < SQLITE_MAX_SCHEMA_RETRY ){ - int savedPc = v->pc; - rc = sqlcipher_sqlite3Reprepare(v); - if( rc!=SQLITE_OK ){ - /* This case occurs after failing to recompile an sql statement. - ** The error message from the SQL compiler has already been loaded - ** into the database handle. This block copies the error message - ** from the database handle into the statement and sets the statement - ** program counter to 0 to ensure that when the statement is - ** finalized or reset the parser error message is available via - ** sqlcipher_sqlite3_errmsg() and sqlcipher_sqlite3_errcode(). - */ - const char *zErr = (const char *)sqlcipher_sqlite3_value_text(db->pErr); - sqlcipher_sqlite3DbFree(db, v->zErrMsg); - if( !db->mallocFailed ){ - v->zErrMsg = sqlcipher_sqlite3DbStrDup(db, zErr); - v->rc = rc = sqlcipher_sqlite3ApiExit(db, rc); - } else { - v->zErrMsg = 0; - v->rc = rc = SQLITE_NOMEM_BKPT; - } - break; - } - sqlcipher_sqlite3_reset(pStmt); - if( savedPc>=0 ) v->doingRerun = 1; - assert( v->expired==0 ); - } - sqlcipher_sqlite3_mutex_leave(db->mutex); - return rc; +case OP_LoadAnalysis: { + assert( pOp->p1>=0 && pOp->p1nDb ); + rc = sqlcipher_sqlite3AnalysisLoad(db, pOp->p1); + if( rc ) goto abort_due_to_error; + break; } +#endif /* !defined(SQLITE_OMIT_ANALYZE) */ - -/* -** Extract the user data from a sqlcipher_sqlite3_context structure and return a -** pointer to it. +/* Opcode: DropTable P1 * * P4 * +** +** Remove the internal (in-memory) data structures that describe +** the table named P4 in database P1. This is called after a table +** is dropped from disk (using the Destroy opcode) in order to keep +** the internal representation of the +** schema consistent with what is on disk. */ -SQLITE_API void *sqlcipher_sqlite3_user_data(sqlcipher_sqlite3_context *p){ - assert( p && p->pFunc ); - return p->pFunc->pUserData; +case OP_DropTable: { + sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); + sqlcipher_sqlite3UnlinkAndDeleteTable(db, pOp->p1, pOp->p4.z); + break; } -/* -** Extract the user data from a sqlcipher_sqlite3_context structure and return a -** pointer to it. +/* Opcode: DropIndex P1 * * P4 * ** -** IMPLEMENTATION-OF: R-46798-50301 The sqlcipher_sqlite3_context_db_handle() interface -** returns a copy of the pointer to the database connection (the 1st -** parameter) of the sqlcipher_sqlite3_create_function() and -** sqlcipher_sqlite3_create_function16() routines that originally registered the -** application defined function. +** Remove the internal (in-memory) data structures that describe +** the index named P4 in database P1. This is called after an index +** is dropped from disk (using the Destroy opcode) +** in order to keep the internal representation of the +** schema consistent with what is on disk. */ -SQLITE_API sqlcipher_sqlite3 *sqlcipher_sqlite3_context_db_handle(sqlcipher_sqlite3_context *p){ - assert( p && p->pOut ); - return p->pOut->db; +case OP_DropIndex: { + sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); + sqlcipher_sqlite3UnlinkAndDeleteIndex(db, pOp->p1, pOp->p4.z); + break; } -/* -** If this routine is invoked from within an xColumn method of a virtual -** table, then it returns true if and only if the the call is during an -** UPDATE operation and the value of the column will not be modified -** by the UPDATE. -** -** If this routine is called from any context other than within the -** xColumn method of a virtual table, then the return value is meaningless -** and arbitrary. +/* Opcode: DropTrigger P1 * * P4 * ** -** Virtual table implements might use this routine to optimize their -** performance by substituting a NULL result, or some other light-weight -** value, as a signal to the xUpdate routine that the column is unchanged. +** Remove the internal (in-memory) data structures that describe +** the trigger named P4 in database P1. This is called after a trigger +** is dropped from disk (using the Destroy opcode) in order to keep +** the internal representation of the +** schema consistent with what is on disk. */ -SQLITE_API int sqlcipher_sqlite3_vtab_nochange(sqlcipher_sqlite3_context *p){ - assert( p ); - return sqlcipher_sqlite3_value_nochange(p->pOut); +case OP_DropTrigger: { + sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); + sqlcipher_sqlite3UnlinkAndDeleteTrigger(db, pOp->p1, pOp->p4.z); + break; } -/* -** Return the current time for a statement. If the current time -** is requested more than once within the same run of a single prepared -** statement, the exact same time is returned for each invocation regardless -** of the amount of time that elapses between invocations. In other words, -** the time returned is always the time of the first call. + +#ifndef SQLITE_OMIT_INTEGRITY_CHECK +/* Opcode: IntegrityCk P1 P2 P3 P4 P5 +** +** Do an analysis of the currently open database. Store in +** register P1 the text of an error message describing any problems. +** If no problems are found, store a NULL in register P1. +** +** The register P3 contains one less than the maximum number of allowed errors. +** At most reg(P3) errors will be reported. +** In other words, the analysis stops as soon as reg(P1) errors are +** seen. Reg(P1) is updated with the number of errors remaining. +** +** The root page numbers of all tables in the database are integers +** stored in P4_INTARRAY argument. +** +** If P5 is not zero, the check is done on the auxiliary database +** file, not the main database file. +** +** This opcode is used to implement the integrity_check pragma. */ -SQLITE_PRIVATE sqlcipher_sqlite3_int64 sqlcipher_sqlite3StmtCurrentTime(sqlcipher_sqlite3_context *p){ - int rc; -#ifndef SQLITE_ENABLE_STAT4 - sqlcipher_sqlite3_int64 *piTime = &p->pVdbe->iCurrentTime; - assert( p->pVdbe!=0 ); -#else - sqlcipher_sqlite3_int64 iTime = 0; - sqlcipher_sqlite3_int64 *piTime = p->pVdbe!=0 ? &p->pVdbe->iCurrentTime : &iTime; -#endif - if( *piTime==0 ){ - rc = sqlcipher_sqlite3OsCurrentTimeInt64(p->pOut->db->pVfs, piTime); - if( rc ) *piTime = 0; +case OP_IntegrityCk: { + int nRoot; /* Number of tables to check. (Number of root pages.) */ + Pgno *aRoot; /* Array of rootpage numbers for tables to be checked */ + int nErr; /* Number of errors reported */ + char *z; /* Text of the error report */ + Mem *pnErr; /* Register keeping track of errors remaining */ + + assert( p->bIsReader ); + nRoot = pOp->p2; + aRoot = pOp->p4.ai; + assert( nRoot>0 ); + assert( aRoot[0]==(Pgno)nRoot ); + assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); + pnErr = &aMem[pOp->p3]; + assert( (pnErr->flags & MEM_Int)!=0 ); + assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); + pIn1 = &aMem[pOp->p1]; + assert( pOp->p5nDb ); + assert( DbMaskTest(p->btreeMask, pOp->p5) ); + z = sqlcipher_sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot, + (int)pnErr->u.i+1, &nErr); + sqlcipher_sqlite3VdbeMemSetNull(pIn1); + if( nErr==0 ){ + assert( z==0 ); + }else if( z==0 ){ + goto no_mem; + }else{ + pnErr->u.i -= nErr-1; + sqlcipher_sqlite3VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, sqlcipher_sqlite3_free); } - return *piTime; + UPDATE_MAX_BLOBSIZE(pIn1); + sqlcipher_sqlite3VdbeChangeEncoding(pIn1, encoding); + goto check_for_interrupt; } +#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ -/* -** Create a new aggregate context for p and return a pointer to -** its pMem->z element. +/* Opcode: RowSetAdd P1 P2 * * * +** Synopsis: rowset(P1)=r[P2] +** +** Insert the integer value held by register P2 into a RowSet object +** held in register P1. +** +** An assertion fails if P2 is not an integer. */ -static SQLITE_NOINLINE void *createAggContext(sqlcipher_sqlite3_context *p, int nByte){ - Mem *pMem = p->pMem; - assert( (pMem->flags & MEM_Agg)==0 ); - if( nByte<=0 ){ - sqlcipher_sqlite3VdbeMemSetNull(pMem); - pMem->z = 0; - }else{ - sqlcipher_sqlite3VdbeMemClearAndResize(pMem, nByte); - pMem->flags = MEM_Agg; - pMem->u.pDef = p->pFunc; - if( pMem->z ){ - memset(pMem->z, 0, nByte); - } +case OP_RowSetAdd: { /* in1, in2 */ + pIn1 = &aMem[pOp->p1]; + pIn2 = &aMem[pOp->p2]; + assert( (pIn2->flags & MEM_Int)!=0 ); + if( (pIn1->flags & MEM_Blob)==0 ){ + if( sqlcipher_sqlite3VdbeMemSetRowSet(pIn1) ) goto no_mem; } - return (void*)pMem->z; + assert( sqlcipher_sqlite3VdbeMemIsRowSet(pIn1) ); + sqlcipher_sqlite3RowSetInsert((RowSet*)pIn1->z, pIn2->u.i); + break; } -/* -** Allocate or return the aggregate context for a user function. A new -** context is allocated on the first call. Subsequent calls return the -** same context that was returned on prior calls. +/* Opcode: RowSetRead P1 P2 P3 * * +** Synopsis: r[P3]=rowset(P1) +** +** Extract the smallest value from the RowSet object in P1 +** and put that value into register P3. +** Or, if RowSet object P1 is initially empty, leave P3 +** unchanged and jump to instruction P2. */ -SQLITE_API void *sqlcipher_sqlite3_aggregate_context(sqlcipher_sqlite3_context *p, int nByte){ - assert( p && p->pFunc && p->pFunc->xFinalize ); - assert( sqlcipher_sqlite3_mutex_held(p->pOut->db->mutex) ); - testcase( nByte<0 ); - if( (p->pMem->flags & MEM_Agg)==0 ){ - return createAggContext(p, nByte); +case OP_RowSetRead: { /* jump, in1, out3 */ + i64 val; + + pIn1 = &aMem[pOp->p1]; + assert( (pIn1->flags & MEM_Blob)==0 || sqlcipher_sqlite3VdbeMemIsRowSet(pIn1) ); + if( (pIn1->flags & MEM_Blob)==0 + || sqlcipher_sqlite3RowSetNext((RowSet*)pIn1->z, &val)==0 + ){ + /* The boolean index is empty */ + sqlcipher_sqlite3VdbeMemSetNull(pIn1); + VdbeBranchTaken(1,2); + goto jump_to_p2_and_check_for_interrupt; }else{ - return (void*)p->pMem->z; + /* A value was pulled from the index */ + VdbeBranchTaken(0,2); + sqlcipher_sqlite3VdbeMemSetInt64(&aMem[pOp->p3], val); } + goto check_for_interrupt; } -/* -** Return the auxiliary data pointer, if any, for the iArg'th argument to -** the user-function defined by pCtx. +/* Opcode: RowSetTest P1 P2 P3 P4 +** Synopsis: if r[P3] in rowset(P1) goto P2 ** -** The left-most argument is 0. +** Register P3 is assumed to hold a 64-bit integer value. If register P1 +** contains a RowSet object and that RowSet object contains +** the value held in P3, jump to register P2. Otherwise, insert the +** integer in P3 into the RowSet and continue on to the +** next opcode. ** -** Undocumented behavior: If iArg is negative then access a cache of -** auxiliary data pointers that is available to all functions within a -** single prepared statement. The iArg values must match. +** The RowSet object is optimized for the case where sets of integers +** are inserted in distinct phases, which each set contains no duplicates. +** Each set is identified by a unique P4 value. The first set +** must have P4==0, the final set must have P4==-1, and for all other sets +** must have P4>0. +** +** This allows optimizations: (a) when P4==0 there is no need to test +** the RowSet object for P3, as it is guaranteed not to contain it, +** (b) when P4==-1 there is no need to insert the value, as it will +** never be tested for, and (c) when a value that is part of set X is +** inserted, there is no need to search to see if the same value was +** previously inserted as part of set X (only if it was previously +** inserted as part of some other set). */ -SQLITE_API void *sqlcipher_sqlite3_get_auxdata(sqlcipher_sqlite3_context *pCtx, int iArg){ - AuxData *pAuxData; +case OP_RowSetTest: { /* jump, in1, in3 */ + int iSet; + int exists; - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); -#if SQLITE_ENABLE_STAT4 - if( pCtx->pVdbe==0 ) return 0; -#else - assert( pCtx->pVdbe!=0 ); -#endif - for(pAuxData=pCtx->pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNextAux){ - if( pAuxData->iAuxArg==iArg && (pAuxData->iAuxOp==pCtx->iOp || iArg<0) ){ - return pAuxData->pAux; - } + pIn1 = &aMem[pOp->p1]; + pIn3 = &aMem[pOp->p3]; + iSet = pOp->p4.i; + assert( pIn3->flags&MEM_Int ); + + /* If there is anything other than a rowset object in memory cell P1, + ** delete it now and initialize P1 with an empty rowset + */ + if( (pIn1->flags & MEM_Blob)==0 ){ + if( sqlcipher_sqlite3VdbeMemSetRowSet(pIn1) ) goto no_mem; } - return 0; + assert( sqlcipher_sqlite3VdbeMemIsRowSet(pIn1) ); + assert( pOp->p4type==P4_INT32 ); + assert( iSet==-1 || iSet>=0 ); + if( iSet ){ + exists = sqlcipher_sqlite3RowSetTest((RowSet*)pIn1->z, iSet, pIn3->u.i); + VdbeBranchTaken(exists!=0,2); + if( exists ) goto jump_to_p2; + } + if( iSet>=0 ){ + sqlcipher_sqlite3RowSetInsert((RowSet*)pIn1->z, pIn3->u.i); + } + break; } -/* -** Set the auxiliary data pointer and delete function, for the iArg'th -** argument to the user-function defined by pCtx. Any previous value is -** deleted by calling the delete function specified when it was set. + +#ifndef SQLITE_OMIT_TRIGGER + +/* Opcode: Program P1 P2 P3 P4 P5 ** -** The left-most argument is 0. +** Execute the trigger program passed as P4 (type P4_SUBPROGRAM). ** -** Undocumented behavior: If iArg is negative then make the data available -** to all functions within the current prepared statement using iArg as an -** access code. +** P1 contains the address of the memory cell that contains the first memory +** cell in an array of values used as arguments to the sub-program. P2 +** contains the address to jump to if the sub-program throws an IGNORE +** exception using the RAISE() function. Register P3 contains the address +** of a memory cell in this (the parent) VM that is used to allocate the +** memory required by the sub-vdbe at runtime. +** +** P4 is a pointer to the VM containing the trigger program. +** +** If P5 is non-zero, then recursive program invocation is enabled. */ -SQLITE_API void sqlcipher_sqlite3_set_auxdata( - sqlcipher_sqlite3_context *pCtx, - int iArg, - void *pAux, - void (*xDelete)(void*) -){ - AuxData *pAuxData; - Vdbe *pVdbe = pCtx->pVdbe; +case OP_Program: { /* jump */ + int nMem; /* Number of memory registers for sub-program */ + int nByte; /* Bytes of runtime space required for sub-program */ + Mem *pRt; /* Register to allocate runtime space */ + Mem *pMem; /* Used to iterate through memory cells */ + Mem *pEnd; /* Last memory cell in new array */ + VdbeFrame *pFrame; /* New vdbe frame to execute in */ + SubProgram *pProgram; /* Sub-program to execute */ + void *t; /* Token identifying trigger */ - assert( sqlcipher_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); -#ifdef SQLITE_ENABLE_STAT4 - if( pVdbe==0 ) goto failed; -#else - assert( pVdbe!=0 ); -#endif + pProgram = pOp->p4.pProgram; + pRt = &aMem[pOp->p3]; + assert( pProgram->nOp>0 ); - for(pAuxData=pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNextAux){ - if( pAuxData->iAuxArg==iArg && (pAuxData->iAuxOp==pCtx->iOp || iArg<0) ){ - break; - } - } - if( pAuxData==0 ){ - pAuxData = sqlcipher_sqlite3DbMallocZero(pVdbe->db, sizeof(AuxData)); - if( !pAuxData ) goto failed; - pAuxData->iAuxOp = pCtx->iOp; - pAuxData->iAuxArg = iArg; - pAuxData->pNextAux = pVdbe->pAuxData; - pVdbe->pAuxData = pAuxData; - if( pCtx->isError==0 ) pCtx->isError = -1; - }else if( pAuxData->xDeleteAux ){ - pAuxData->xDeleteAux(pAuxData->pAux); + /* If the p5 flag is clear, then recursive invocation of triggers is + ** disabled for backwards compatibility (p5 is set if this sub-program + ** is really a trigger, not a foreign key action, and the flag set + ** and cleared by the "PRAGMA recursive_triggers" command is clear). + ** + ** It is recursive invocation of triggers, at the SQL level, that is + ** disabled. In some cases a single trigger may generate more than one + ** SubProgram (if the trigger may be executed with more than one different + ** ON CONFLICT algorithm). SubProgram structures associated with a + ** single trigger all have the same value for the SubProgram.token + ** variable. */ + if( pOp->p5 ){ + t = pProgram->token; + for(pFrame=p->pFrame; pFrame && pFrame->token!=t; pFrame=pFrame->pParent); + if( pFrame ) break; } - pAuxData->pAux = pAux; - pAuxData->xDeleteAux = xDelete; - return; - -failed: - if( xDelete ){ - xDelete(pAux); + if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){ + rc = SQLITE_ERROR; + sqlcipher_sqlite3VdbeError(p, "too many levels of trigger recursion"); + goto abort_due_to_error; } -} -#ifndef SQLITE_OMIT_DEPRECATED -/* -** Return the number of times the Step function of an aggregate has been -** called. -** -** This function is deprecated. Do not use it for new code. It is -** provide only to avoid breaking legacy code. New aggregate function -** implementations should keep their own counts within their aggregate -** context. -*/ -SQLITE_API int sqlcipher_sqlite3_aggregate_count(sqlcipher_sqlite3_context *p){ - assert( p && p->pMem && p->pFunc && p->pFunc->xFinalize ); - return p->pMem->n; -} -#endif + /* Register pRt is used to store the memory required to save the state + ** of the current program, and the memory required at runtime to execute + ** the trigger program. If this trigger has been fired before, then pRt + ** is already allocated. Otherwise, it must be initialized. */ + if( (pRt->flags&MEM_Blob)==0 ){ + /* SubProgram.nMem is set to the number of memory cells used by the + ** program stored in SubProgram.aOp. As well as these, one memory + ** cell is required for each cursor used by the program. Set local + ** variable nMem (and later, VdbeFrame.nChildMem) to this value. + */ + nMem = pProgram->nMem + pProgram->nCsr; + assert( nMem>0 ); + if( pProgram->nCsr==0 ) nMem++; + nByte = ROUND8(sizeof(VdbeFrame)) + + nMem * sizeof(Mem) + + pProgram->nCsr * sizeof(VdbeCursor*) + + (pProgram->nOp + 7)/8; + pFrame = sqlcipher_sqlite3DbMallocZero(db, nByte); + if( !pFrame ){ + goto no_mem; + } + sqlcipher_sqlite3VdbeMemRelease(pRt); + pRt->flags = MEM_Blob|MEM_Dyn; + pRt->z = (char*)pFrame; + pRt->n = nByte; + pRt->xDel = sqlcipher_sqlite3VdbeFrameMemDel; -/* -** Return the number of columns in the result set for the statement pStmt. -*/ -SQLITE_API int sqlcipher_sqlite3_column_count(sqlcipher_sqlite3_stmt *pStmt){ - Vdbe *pVm = (Vdbe *)pStmt; - return pVm ? pVm->nResColumn : 0; -} + pFrame->v = p; + pFrame->nChildMem = nMem; + pFrame->nChildCsr = pProgram->nCsr; + pFrame->pc = (int)(pOp - aOp); + pFrame->aMem = p->aMem; + pFrame->nMem = p->nMem; + pFrame->apCsr = p->apCsr; + pFrame->nCursor = p->nCursor; + pFrame->aOp = p->aOp; + pFrame->nOp = p->nOp; + pFrame->token = pProgram->token; +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + pFrame->anExec = p->anExec; +#endif +#ifdef SQLITE_DEBUG + pFrame->iFrameMagic = SQLITE_FRAME_MAGIC; +#endif -/* -** Return the number of values available from the current row of the -** currently executing statement pStmt. -*/ -SQLITE_API int sqlcipher_sqlite3_data_count(sqlcipher_sqlite3_stmt *pStmt){ - Vdbe *pVm = (Vdbe *)pStmt; - if( pVm==0 || pVm->pResultSet==0 ) return 0; - return pVm->nResColumn; -} + pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem]; + for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){ + pMem->flags = MEM_Undefined; + pMem->db = db; + } + }else{ + pFrame = (VdbeFrame*)pRt->z; + assert( pRt->xDel==sqlcipher_sqlite3VdbeFrameMemDel ); + assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem + || (pProgram->nCsr==0 && pProgram->nMem+1==pFrame->nChildMem) ); + assert( pProgram->nCsr==pFrame->nChildCsr ); + assert( (int)(pOp - aOp)==pFrame->pc ); + } -/* -** Return a pointer to static memory containing an SQL NULL value. -*/ -static const Mem *columnNullValue(void){ - /* Even though the Mem structure contains an element - ** of type i64, on certain architectures (x86) with certain compiler - ** switches (-Os), gcc may align this Mem object on a 4-byte boundary - ** instead of an 8-byte one. This all works fine, except that when - ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s - ** that a Mem structure is located on an 8-byte boundary. To prevent - ** these assert()s from failing, when building with SQLITE_DEBUG defined - ** using gcc, we force nullMem to be 8-byte aligned using the magical - ** __attribute__((aligned(8))) macro. */ - static const Mem nullMem -#if defined(SQLITE_DEBUG) && defined(__GNUC__) - __attribute__((aligned(8))) + p->nFrame++; + pFrame->pParent = p->pFrame; + pFrame->lastRowid = db->lastRowid; + pFrame->nChange = p->nChange; + pFrame->nDbChange = p->db->nChange; + assert( pFrame->pAuxData==0 ); + pFrame->pAuxData = p->pAuxData; + p->pAuxData = 0; + p->nChange = 0; + p->pFrame = pFrame; + p->aMem = aMem = VdbeFrameMem(pFrame); + p->nMem = pFrame->nChildMem; + p->nCursor = (u16)pFrame->nChildCsr; + p->apCsr = (VdbeCursor **)&aMem[p->nMem]; + pFrame->aOnce = (u8*)&p->apCsr[pProgram->nCsr]; + memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8); + p->aOp = aOp = pProgram->aOp; + p->nOp = pProgram->nOp; +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + p->anExec = 0; #endif - = { - /* .u = */ {0}, - /* .flags = */ (u16)MEM_Null, - /* .enc = */ (u8)0, - /* .eSubtype = */ (u8)0, - /* .n = */ (int)0, - /* .z = */ (char*)0, - /* .zMalloc = */ (char*)0, - /* .szMalloc = */ (int)0, - /* .uTemp = */ (u32)0, - /* .db = */ (sqlcipher_sqlite3*)0, - /* .xDel = */ (void(*)(void*))0, #ifdef SQLITE_DEBUG - /* .pScopyFrom = */ (Mem*)0, - /* .mScopyFlags= */ 0, + /* Verify that second and subsequent executions of the same trigger do not + ** try to reuse register values from the first use. */ + { + int i; + for(i=0; inMem; i++){ + aMem[i].pScopyFrom = 0; /* Prevent false-positive AboutToChange() errs */ + MemSetTypeFlag(&aMem[i], MEM_Undefined); /* Fault if this reg is reused */ + } + } #endif - }; - return &nullMem; + pOp = &aOp[-1]; + goto check_for_interrupt; } -/* -** Check to see if column iCol of the given statement is valid. If -** it is, return a pointer to the Mem for the value of that column. -** If iCol is not valid, return a pointer to a Mem which has a value -** of NULL. +/* Opcode: Param P1 P2 * * * +** +** This opcode is only ever present in sub-programs called via the +** OP_Program instruction. Copy a value currently stored in a memory +** cell of the calling (parent) frame to cell P2 in the current frames +** address space. This is used by trigger programs to access the new.* +** and old.* values. +** +** The address of the cell in the parent frame is determined by adding +** the value of the P1 argument to the value of the P1 argument to the +** calling OP_Program instruction. */ -static Mem *columnMem(sqlcipher_sqlite3_stmt *pStmt, int i){ - Vdbe *pVm; - Mem *pOut; +case OP_Param: { /* out2 */ + VdbeFrame *pFrame; + Mem *pIn; + pOut = out2Prerelease(p, pOp); + pFrame = p->pFrame; + pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1]; + sqlcipher_sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem); + break; +} - pVm = (Vdbe *)pStmt; - if( pVm==0 ) return (Mem*)columnNullValue(); - assert( pVm->db ); - sqlcipher_sqlite3_mutex_enter(pVm->db->mutex); - if( pVm->pResultSet!=0 && inResColumn && i>=0 ){ - pOut = &pVm->pResultSet[i]; +#endif /* #ifndef SQLITE_OMIT_TRIGGER */ + +#ifndef SQLITE_OMIT_FOREIGN_KEY +/* Opcode: FkCounter P1 P2 * * * +** Synopsis: fkctr[P1]+=P2 +** +** Increment a "constraint counter" by P2 (P2 may be negative or positive). +** If P1 is non-zero, the database constraint counter is incremented +** (deferred foreign key constraints). Otherwise, if P1 is zero, the +** statement counter is incremented (immediate foreign key constraints). +*/ +case OP_FkCounter: { + if( db->flags & SQLITE_DeferFKs ){ + db->nDeferredImmCons += pOp->p2; + }else if( pOp->p1 ){ + db->nDeferredCons += pOp->p2; }else{ - sqlcipher_sqlite3Error(pVm->db, SQLITE_RANGE); - pOut = (Mem*)columnNullValue(); + p->nFkConstraint += pOp->p2; } - return pOut; + break; } -/* -** This function is called after invoking an sqlcipher_sqlite3_value_XXX function on a -** column value (i.e. a value returned by evaluating an SQL expression in the -** select list of a SELECT statement) that may cause a malloc() failure. If -** malloc() has failed, the threads mallocFailed flag is cleared and the result -** code of statement pStmt set to SQLITE_NOMEM. +/* Opcode: FkIfZero P1 P2 * * * +** Synopsis: if fkctr[P1]==0 goto P2 ** -** Specifically, this is called from within: +** This opcode tests if a foreign key constraint-counter is currently zero. +** If so, jump to instruction P2. Otherwise, fall through to the next +** instruction. ** -** sqlcipher_sqlite3_column_int() -** sqlcipher_sqlite3_column_int64() -** sqlcipher_sqlite3_column_text() -** sqlcipher_sqlite3_column_text16() -** sqlcipher_sqlite3_column_real() -** sqlcipher_sqlite3_column_bytes() -** sqlcipher_sqlite3_column_bytes16() -** sqiite3_column_blob() +** If P1 is non-zero, then the jump is taken if the database constraint-counter +** is zero (the one that counts deferred constraint violations). If P1 is +** zero, the jump is taken if the statement constraint-counter is zero +** (immediate foreign key constraint violations). */ -static void columnMallocFailure(sqlcipher_sqlite3_stmt *pStmt) -{ - /* If malloc() failed during an encoding conversion within an - ** sqlcipher_sqlite3_column_XXX API, then set the return code of the statement to - ** SQLITE_NOMEM. The next call to _step() (if any) will return SQLITE_ERROR - ** and _finalize() will return NOMEM. - */ - Vdbe *p = (Vdbe *)pStmt; - if( p ){ - assert( p->db!=0 ); - assert( sqlcipher_sqlite3_mutex_held(p->db->mutex) ); - p->rc = sqlcipher_sqlite3ApiExit(p->db, p->rc); - sqlcipher_sqlite3_mutex_leave(p->db->mutex); +case OP_FkIfZero: { /* jump */ + if( pOp->p1 ){ + VdbeBranchTaken(db->nDeferredCons==0 && db->nDeferredImmCons==0, 2); + if( db->nDeferredCons==0 && db->nDeferredImmCons==0 ) goto jump_to_p2; + }else{ + VdbeBranchTaken(p->nFkConstraint==0 && db->nDeferredImmCons==0, 2); + if( p->nFkConstraint==0 && db->nDeferredImmCons==0 ) goto jump_to_p2; } + break; } +#endif /* #ifndef SQLITE_OMIT_FOREIGN_KEY */ -/**************************** sqlcipher_sqlite3_column_ ******************************* -** The following routines are used to access elements of the current row -** in the result set. +#ifndef SQLITE_OMIT_AUTOINCREMENT +/* Opcode: MemMax P1 P2 * * * +** Synopsis: r[P1]=max(r[P1],r[P2]) +** +** P1 is a register in the root frame of this VM (the root frame is +** different from the current frame if this instruction is being executed +** within a sub-program). Set the value of register P1 to the maximum of +** its current value and the value in register P2. +** +** This instruction throws an error if the memory cell is not initially +** an integer. */ -SQLITE_API const void *sqlcipher_sqlite3_column_blob(sqlcipher_sqlite3_stmt *pStmt, int i){ - const void *val; - val = sqlcipher_sqlite3_value_blob( columnMem(pStmt,i) ); - /* Even though there is no encoding conversion, value_blob() might - ** need to call malloc() to expand the result of a zeroblob() - ** expression. - */ - columnMallocFailure(pStmt); - return val; -} -SQLITE_API int sqlcipher_sqlite3_column_bytes(sqlcipher_sqlite3_stmt *pStmt, int i){ - int val = sqlcipher_sqlite3_value_bytes( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API int sqlcipher_sqlite3_column_bytes16(sqlcipher_sqlite3_stmt *pStmt, int i){ - int val = sqlcipher_sqlite3_value_bytes16( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API double sqlcipher_sqlite3_column_double(sqlcipher_sqlite3_stmt *pStmt, int i){ - double val = sqlcipher_sqlite3_value_double( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API int sqlcipher_sqlite3_column_int(sqlcipher_sqlite3_stmt *pStmt, int i){ - int val = sqlcipher_sqlite3_value_int( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API sqlite_int64 sqlcipher_sqlite3_column_int64(sqlcipher_sqlite3_stmt *pStmt, int i){ - sqlite_int64 val = sqlcipher_sqlite3_value_int64( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API const unsigned char *sqlcipher_sqlite3_column_text(sqlcipher_sqlite3_stmt *pStmt, int i){ - const unsigned char *val = sqlcipher_sqlite3_value_text( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -SQLITE_API sqlcipher_sqlite3_value *sqlcipher_sqlite3_column_value(sqlcipher_sqlite3_stmt *pStmt, int i){ - Mem *pOut = columnMem(pStmt, i); - if( pOut->flags&MEM_Static ){ - pOut->flags &= ~MEM_Static; - pOut->flags |= MEM_Ephem; +case OP_MemMax: { /* in2 */ + VdbeFrame *pFrame; + if( p->pFrame ){ + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); + pIn1 = &pFrame->aMem[pOp->p1]; + }else{ + pIn1 = &aMem[pOp->p1]; } - columnMallocFailure(pStmt); - return (sqlcipher_sqlite3_value *)pOut; -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlcipher_sqlite3_column_text16(sqlcipher_sqlite3_stmt *pStmt, int i){ - const void *val = sqlcipher_sqlite3_value_text16( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return val; -} -#endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API int sqlcipher_sqlite3_column_type(sqlcipher_sqlite3_stmt *pStmt, int i){ - int iType = sqlcipher_sqlite3_value_type( columnMem(pStmt,i) ); - columnMallocFailure(pStmt); - return iType; + assert( memIsValid(pIn1) ); + sqlcipher_sqlite3VdbeMemIntegerify(pIn1); + pIn2 = &aMem[pOp->p2]; + sqlcipher_sqlite3VdbeMemIntegerify(pIn2); + if( pIn1->u.iu.i){ + pIn1->u.i = pIn2->u.i; + } + break; } +#endif /* SQLITE_OMIT_AUTOINCREMENT */ -/* -** Convert the N-th element of pStmt->pColName[] into a string using -** xFunc() then return that string. If N is out of range, return 0. -** -** There are up to 5 names for each column. useType determines which -** name is returned. Here are the names: +/* Opcode: IfPos P1 P2 P3 * * +** Synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 ** -** 0 The column name as it should be displayed for output -** 1 The datatype name for the column -** 2 The name of the database that the column derives from -** 3 The name of the table that the column derives from -** 4 The name of the table column that the result column derives from +** Register P1 must contain an integer. +** If the value of register P1 is 1 or greater, subtract P3 from the +** value in P1 and jump to P2. ** -** If the result is not a simple column reference (if it is an expression -** or a constant) then useTypes 2, 3, and 4 return NULL. +** If the initial value of register P1 is less than 1, then the +** value is unchanged and control passes through to the next instruction. */ -static const void *columnName( - sqlcipher_sqlite3_stmt *pStmt, /* The statement */ - int N, /* Which column to get the name for */ - int useUtf16, /* True to return the name as UTF16 */ - int useType /* What type of name */ -){ - const void *ret; - Vdbe *p; - int n; - sqlcipher_sqlite3 *db; -#ifdef SQLITE_ENABLE_API_ARMOR - if( pStmt==0 ){ - (void)SQLITE_MISUSE_BKPT; - return 0; - } -#endif - ret = 0; - p = (Vdbe *)pStmt; - db = p->db; - assert( db!=0 ); - n = sqlcipher_sqlite3_column_count(pStmt); - if( N=0 ){ - N += useType*n; - sqlcipher_sqlite3_mutex_enter(db->mutex); - assert( db->mallocFailed==0 ); -#ifndef SQLITE_OMIT_UTF16 - if( useUtf16 ){ - ret = sqlcipher_sqlite3_value_text16((sqlcipher_sqlite3_value*)&p->aColName[N]); - }else -#endif - { - ret = sqlcipher_sqlite3_value_text((sqlcipher_sqlite3_value*)&p->aColName[N]); - } - /* A malloc may have failed inside of the _text() call. If this - ** is the case, clear the mallocFailed flag and return NULL. - */ - if( db->mallocFailed ){ - sqlcipher_sqlite3OomClear(db); - ret = 0; - } - sqlcipher_sqlite3_mutex_leave(db->mutex); +case OP_IfPos: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; + assert( pIn1->flags&MEM_Int ); + VdbeBranchTaken( pIn1->u.i>0, 2); + if( pIn1->u.i>0 ){ + pIn1->u.i -= pOp->p3; + goto jump_to_p2; } - return ret; + break; } -/* -** Return the name of the Nth column of the result set returned by SQL -** statement pStmt. +/* Opcode: OffsetLimit P1 P2 P3 * * +** Synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) +** +** This opcode performs a commonly used computation associated with +** LIMIT and OFFSET process. r[P1] holds the limit counter. r[P3] +** holds the offset counter. The opcode computes the combined value +** of the LIMIT and OFFSET and stores that value in r[P2]. The r[P2] +** value computed is the total number of rows that will need to be +** visited in order to complete the query. +** +** If r[P3] is zero or negative, that means there is no OFFSET +** and r[P2] is set to be the value of the LIMIT, r[P1]. +** +** if r[P1] is zero or negative, that means there is no LIMIT +** and r[P2] is set to -1. +** +** Otherwise, r[P2] is set to the sum of r[P1] and r[P3]. */ -SQLITE_API const char *sqlcipher_sqlite3_column_name(sqlcipher_sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, 0, COLNAME_NAME); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlcipher_sqlite3_column_name16(sqlcipher_sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, 1, COLNAME_NAME); +case OP_OffsetLimit: { /* in1, out2, in3 */ + i64 x; + pIn1 = &aMem[pOp->p1]; + pIn3 = &aMem[pOp->p3]; + pOut = out2Prerelease(p, pOp); + assert( pIn1->flags & MEM_Int ); + assert( pIn3->flags & MEM_Int ); + x = pIn1->u.i; + if( x<=0 || sqlcipher_sqlite3AddInt64(&x, pIn3->u.i>0?pIn3->u.i:0) ){ + /* If the LIMIT is less than or equal to zero, loop forever. This + ** is documented. But also, if the LIMIT+OFFSET exceeds 2^63 then + ** also loop forever. This is undocumented. In fact, one could argue + ** that the loop should terminate. But assuming 1 billion iterations + ** per second (far exceeding the capabilities of any current hardware) + ** it would take nearly 300 years to actually reach the limit. So + ** looping forever is a reasonable approximation. */ + pOut->u.i = -1; + }else{ + pOut->u.i = x; + } + break; } -#endif - -/* -** Constraint: If you have ENABLE_COLUMN_METADATA then you must -** not define OMIT_DECLTYPE. -*/ -#if defined(SQLITE_OMIT_DECLTYPE) && defined(SQLITE_ENABLE_COLUMN_METADATA) -# error "Must not define both SQLITE_OMIT_DECLTYPE \ - and SQLITE_ENABLE_COLUMN_METADATA" -#endif -#ifndef SQLITE_OMIT_DECLTYPE -/* -** Return the column declaration type (if applicable) of the 'i'th column -** of the result set of SQL statement pStmt. +/* Opcode: IfNotZero P1 P2 * * * +** Synopsis: if r[P1]!=0 then r[P1]--, goto P2 +** +** Register P1 must contain an integer. If the content of register P1 is +** initially greater than zero, then decrement the value in register P1. +** If it is non-zero (negative or positive) and then also jump to P2. +** If register P1 is initially zero, leave it unchanged and fall through. */ -SQLITE_API const char *sqlcipher_sqlite3_column_decltype(sqlcipher_sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, 0, COLNAME_DECLTYPE); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlcipher_sqlite3_column_decltype16(sqlcipher_sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, 1, COLNAME_DECLTYPE); +case OP_IfNotZero: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; + assert( pIn1->flags&MEM_Int ); + VdbeBranchTaken(pIn1->u.i<0, 2); + if( pIn1->u.i ){ + if( pIn1->u.i>0 ) pIn1->u.i--; + goto jump_to_p2; + } + break; } -#endif /* SQLITE_OMIT_UTF16 */ -#endif /* SQLITE_OMIT_DECLTYPE */ -#ifdef SQLITE_ENABLE_COLUMN_METADATA -/* -** Return the name of the database from which a result column derives. -** NULL is returned if the result column is an expression or constant or -** anything else which is not an unambiguous reference to a database column. +/* Opcode: DecrJumpZero P1 P2 * * * +** Synopsis: if (--r[P1])==0 goto P2 +** +** Register P1 must hold an integer. Decrement the value in P1 +** and jump to P2 if the new value is exactly zero. */ -SQLITE_API const char *sqlcipher_sqlite3_column_database_name(sqlcipher_sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, 0, COLNAME_DATABASE); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlcipher_sqlite3_column_database_name16(sqlcipher_sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, 1, COLNAME_DATABASE); +case OP_DecrJumpZero: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; + assert( pIn1->flags&MEM_Int ); + if( pIn1->u.i>SMALLEST_INT64 ) pIn1->u.i--; + VdbeBranchTaken(pIn1->u.i==0, 2); + if( pIn1->u.i==0 ) goto jump_to_p2; + break; } -#endif /* SQLITE_OMIT_UTF16 */ -/* -** Return the name of the table from which a result column derives. -** NULL is returned if the result column is an expression or constant or -** anything else which is not an unambiguous reference to a database column. -*/ -SQLITE_API const char *sqlcipher_sqlite3_column_table_name(sqlcipher_sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, 0, COLNAME_TABLE); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlcipher_sqlite3_column_table_name16(sqlcipher_sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, 1, COLNAME_TABLE); -} -#endif /* SQLITE_OMIT_UTF16 */ -/* -** Return the name of the table column from which a result column derives. -** NULL is returned if the result column is an expression or constant or -** anything else which is not an unambiguous reference to a database column. +/* Opcode: AggStep * P2 P3 P4 P5 +** Synopsis: accum=r[P3] step(r[P2@P5]) +** +** Execute the xStep function for an aggregate. +** The function has P5 arguments. P4 is a pointer to the +** FuncDef structure that specifies the function. Register P3 is the +** accumulator. +** +** The P5 arguments are taken from register P2 and its +** successors. */ -SQLITE_API const char *sqlcipher_sqlite3_column_origin_name(sqlcipher_sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, 0, COLNAME_COLUMN); -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlcipher_sqlite3_column_origin_name16(sqlcipher_sqlite3_stmt *pStmt, int N){ - return columnName(pStmt, N, 1, COLNAME_COLUMN); -} -#endif /* SQLITE_OMIT_UTF16 */ -#endif /* SQLITE_ENABLE_COLUMN_METADATA */ - - -/******************************* sqlcipher_sqlite3_bind_ *************************** +/* Opcode: AggInverse * P2 P3 P4 P5 +** Synopsis: accum=r[P3] inverse(r[P2@P5]) ** -** Routines used to attach values to wildcards in a compiled SQL statement. +** Execute the xInverse function for an aggregate. +** The function has P5 arguments. P4 is a pointer to the +** FuncDef structure that specifies the function. Register P3 is the +** accumulator. +** +** The P5 arguments are taken from register P2 and its +** successors. */ -/* -** Unbind the value bound to variable i in virtual machine p. This is the -** the same as binding a NULL value to the column. If the "i" parameter is -** out of range, then SQLITE_RANGE is returned. Othewise SQLITE_OK. +/* Opcode: AggStep1 P1 P2 P3 P4 P5 +** Synopsis: accum=r[P3] step(r[P2@P5]) ** -** A successful evaluation of this routine acquires the mutex on p. -** the mutex is released if any kind of error occurs. +** Execute the xStep (if P1==0) or xInverse (if P1!=0) function for an +** aggregate. The function has P5 arguments. P4 is a pointer to the +** FuncDef structure that specifies the function. Register P3 is the +** accumulator. ** -** The error code stored in database p->db is overwritten with the return -** value in any case. +** The P5 arguments are taken from register P2 and its +** successors. +** +** This opcode is initially coded as OP_AggStep0. On first evaluation, +** the FuncDef stored in P4 is converted into an sqlcipher_sqlite3_context and +** the opcode is changed. In this way, the initialization of the +** sqlcipher_sqlite3_context only happens once, instead of on each call to the +** step function. */ -static int vdbeUnbind(Vdbe *p, int i){ - Mem *pVar; - if( vdbeSafetyNotNull(p) ){ - return SQLITE_MISUSE_BKPT; - } - sqlcipher_sqlite3_mutex_enter(p->db->mutex); - if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){ - sqlcipher_sqlite3Error(p->db, SQLITE_MISUSE); - sqlcipher_sqlite3_mutex_leave(p->db->mutex); - sqlcipher_sqlite3_log(SQLITE_MISUSE, - "bind on a busy prepared statement: [%s]", p->zSql); - return SQLITE_MISUSE_BKPT; - } - if( i<1 || i>p->nVar ){ - sqlcipher_sqlite3Error(p->db, SQLITE_RANGE); - sqlcipher_sqlite3_mutex_leave(p->db->mutex); - return SQLITE_RANGE; - } - i--; - pVar = &p->aVar[i]; - sqlcipher_sqlite3VdbeMemRelease(pVar); - pVar->flags = MEM_Null; - p->db->errCode = SQLITE_OK; +case OP_AggInverse: +case OP_AggStep: { + int n; + sqlcipher_sqlite3_context *pCtx; - /* If the bit corresponding to this variable in Vdbe.expmask is set, then - ** binding a new value to this variable invalidates the current query plan. - ** - ** IMPLEMENTATION-OF: R-57496-20354 If the specific value bound to a host - ** parameter in the WHERE clause might influence the choice of query plan - ** for a statement, then the statement will be automatically recompiled, - ** as if there had been a schema change, on the first sqlcipher_sqlite3_step() call - ** following any change to the bindings of that parameter. - */ - assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || p->expmask==0 ); - if( p->expmask!=0 && (p->expmask & (i>=31 ? 0x80000000 : (u32)1<expired = 1; - } - return SQLITE_OK; -} + assert( pOp->p4type==P4_FUNCDEF ); + n = pOp->p5; + assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); + assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) ); + assert( pOp->p3p2 || pOp->p3>=pOp->p2+n ); + pCtx = sqlcipher_sqlite3DbMallocRawNN(db, n*sizeof(sqlcipher_sqlite3_value*) + + (sizeof(pCtx[0]) + sizeof(Mem) - sizeof(sqlcipher_sqlite3_value*))); + if( pCtx==0 ) goto no_mem; + pCtx->pMem = 0; + pCtx->pOut = (Mem*)&(pCtx->argv[n]); + sqlcipher_sqlite3VdbeMemInit(pCtx->pOut, db, MEM_Null); + pCtx->pFunc = pOp->p4.pFunc; + pCtx->iOp = (int)(pOp - aOp); + pCtx->pVdbe = p; + pCtx->skipFlag = 0; + pCtx->isError = 0; + pCtx->enc = encoding; + pCtx->argc = n; + pOp->p4type = P4_FUNCCTX; + pOp->p4.pCtx = pCtx; -/* -** Bind a text or BLOB value. -*/ -static int bindText( - sqlcipher_sqlite3_stmt *pStmt, /* The statement to bind against */ - int i, /* Index of the parameter to bind */ - const void *zData, /* Pointer to the data to be bound */ - int nData, /* Number of bytes of data to be bound */ - void (*xDel)(void*), /* Destructor for the data */ - u8 encoding /* Encoding for the data */ -){ - Vdbe *p = (Vdbe *)pStmt; - Mem *pVar; - int rc; + /* OP_AggInverse must have P1==1 and OP_AggStep must have P1==0 */ + assert( pOp->p1==(pOp->opcode==OP_AggInverse) ); - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - if( zData!=0 ){ - pVar = &p->aVar[i-1]; - rc = sqlcipher_sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); - if( rc==SQLITE_OK && encoding!=0 ){ - rc = sqlcipher_sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); - } - if( rc ){ - sqlcipher_sqlite3Error(p->db, rc); - rc = sqlcipher_sqlite3ApiExit(p->db, rc); - } - } - sqlcipher_sqlite3_mutex_leave(p->db->mutex); - }else if( xDel!=SQLITE_STATIC && xDel!=SQLITE_TRANSIENT ){ - xDel((void*)zData); - } - return rc; + pOp->opcode = OP_AggStep1; + /* Fall through into OP_AggStep */ + /* no break */ deliberate_fall_through } +case OP_AggStep1: { + int i; + sqlcipher_sqlite3_context *pCtx; + Mem *pMem; + assert( pOp->p4type==P4_FUNCCTX ); + pCtx = pOp->p4.pCtx; + pMem = &aMem[pOp->p3]; -/* -** Bind a blob value to an SQL statement variable. -*/ -SQLITE_API int sqlcipher_sqlite3_bind_blob( - sqlcipher_sqlite3_stmt *pStmt, - int i, - const void *zData, - int nData, - void (*xDel)(void*) -){ -#ifdef SQLITE_ENABLE_API_ARMOR - if( nData<0 ) return SQLITE_MISUSE_BKPT; -#endif - return bindText(pStmt, i, zData, nData, xDel, 0); -} -SQLITE_API int sqlcipher_sqlite3_bind_blob64( - sqlcipher_sqlite3_stmt *pStmt, - int i, - const void *zData, - sqlcipher_sqlite3_uint64 nData, - void (*xDel)(void*) -){ - assert( xDel!=SQLITE_DYNAMIC ); - if( nData>0x7fffffff ){ - return invokeValueDestructor(zData, xDel, 0); +#ifdef SQLITE_DEBUG + if( pOp->p1 ){ + /* This is an OP_AggInverse call. Verify that xStep has always + ** been called at least once prior to any xInverse call. */ + assert( pMem->uTemp==0x1122e0e3 ); }else{ - return bindText(pStmt, i, zData, (int)nData, xDel, 0); - } -} -SQLITE_API int sqlcipher_sqlite3_bind_double(sqlcipher_sqlite3_stmt *pStmt, int i, double rValue){ - int rc; - Vdbe *p = (Vdbe *)pStmt; - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue); - sqlcipher_sqlite3_mutex_leave(p->db->mutex); - } - return rc; -} -SQLITE_API int sqlcipher_sqlite3_bind_int(sqlcipher_sqlite3_stmt *p, int i, int iValue){ - return sqlcipher_sqlite3_bind_int64(p, i, (i64)iValue); -} -SQLITE_API int sqlcipher_sqlite3_bind_int64(sqlcipher_sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ - int rc; - Vdbe *p = (Vdbe *)pStmt; - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue); - sqlcipher_sqlite3_mutex_leave(p->db->mutex); - } - return rc; -} -SQLITE_API int sqlcipher_sqlite3_bind_null(sqlcipher_sqlite3_stmt *pStmt, int i){ - int rc; - Vdbe *p = (Vdbe*)pStmt; - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3_mutex_leave(p->db->mutex); + /* This is an OP_AggStep call. Mark it as such. */ + pMem->uTemp = 0x1122e0e3; } - return rc; -} -SQLITE_API int sqlcipher_sqlite3_bind_pointer( - sqlcipher_sqlite3_stmt *pStmt, - int i, - void *pPtr, - const char *zPTtype, - void (*xDestructor)(void*) -){ - int rc; - Vdbe *p = (Vdbe*)pStmt; - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor); - sqlcipher_sqlite3_mutex_leave(p->db->mutex); - }else if( xDestructor ){ - xDestructor(pPtr); +#endif + + /* If this function is inside of a trigger, the register array in aMem[] + ** might change from one evaluation to the next. The next block of code + ** checks to see if the register array has changed, and if so it + ** reinitializes the relavant parts of the sqlcipher_sqlite3_context object */ + if( pCtx->pMem != pMem ){ + pCtx->pMem = pMem; + for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i]; } - return rc; -} -SQLITE_API int sqlcipher_sqlite3_bind_text( - sqlcipher_sqlite3_stmt *pStmt, - int i, - const char *zData, - int nData, - void (*xDel)(void*) -){ - return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF8); -} -SQLITE_API int sqlcipher_sqlite3_bind_text64( - sqlcipher_sqlite3_stmt *pStmt, - int i, - const char *zData, - sqlcipher_sqlite3_uint64 nData, - void (*xDel)(void*), - unsigned char enc -){ - assert( xDel!=SQLITE_DYNAMIC ); - if( nData>0x7fffffff ){ - return invokeValueDestructor(zData, xDel, 0); - }else{ - if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; - return bindText(pStmt, i, zData, (int)nData, xDel, enc); + +#ifdef SQLITE_DEBUG + for(i=0; iargc; i++){ + assert( memIsValid(pCtx->argv[i]) ); + REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]); } -} -#ifndef SQLITE_OMIT_UTF16 -SQLITE_API int sqlcipher_sqlite3_bind_text16( - sqlcipher_sqlite3_stmt *pStmt, - int i, - const void *zData, - int nData, - void (*xDel)(void*) -){ - return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE); -} -#endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API int sqlcipher_sqlite3_bind_value(sqlcipher_sqlite3_stmt *pStmt, int i, const sqlcipher_sqlite3_value *pValue){ - int rc; - switch( sqlcipher_sqlite3_value_type((sqlcipher_sqlite3_value*)pValue) ){ - case SQLITE_INTEGER: { - rc = sqlcipher_sqlite3_bind_int64(pStmt, i, pValue->u.i); - break; - } - case SQLITE_FLOAT: { - rc = sqlcipher_sqlite3_bind_double(pStmt, i, pValue->u.r); - break; - } - case SQLITE_BLOB: { - if( pValue->flags & MEM_Zero ){ - rc = sqlcipher_sqlite3_bind_zeroblob(pStmt, i, pValue->u.nZero); - }else{ - rc = sqlcipher_sqlite3_bind_blob(pStmt, i, pValue->z, pValue->n,SQLITE_TRANSIENT); - } - break; - } - case SQLITE_TEXT: { - rc = bindText(pStmt,i, pValue->z, pValue->n, SQLITE_TRANSIENT, - pValue->enc); - break; +#endif + + pMem->n++; + assert( pCtx->pOut->flags==MEM_Null ); + assert( pCtx->isError==0 ); + assert( pCtx->skipFlag==0 ); +#ifndef SQLITE_OMIT_WINDOWFUNC + if( pOp->p1 ){ + (pCtx->pFunc->xInverse)(pCtx,pCtx->argc,pCtx->argv); + }else +#endif + (pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */ + + if( pCtx->isError ){ + if( pCtx->isError>0 ){ + sqlcipher_sqlite3VdbeError(p, "%s", sqlcipher_sqlite3_value_text(pCtx->pOut)); + rc = pCtx->isError; } - default: { - rc = sqlcipher_sqlite3_bind_null(pStmt, i); - break; + if( pCtx->skipFlag ){ + assert( pOp[-1].opcode==OP_CollSeq ); + i = pOp[-1].p1; + if( i ) sqlcipher_sqlite3VdbeMemSetInt64(&aMem[i], 1); + pCtx->skipFlag = 0; } + sqlcipher_sqlite3VdbeMemRelease(pCtx->pOut); + pCtx->pOut->flags = MEM_Null; + pCtx->isError = 0; + if( rc ) goto abort_due_to_error; } - return rc; -} -SQLITE_API int sqlcipher_sqlite3_bind_zeroblob(sqlcipher_sqlite3_stmt *pStmt, int i, int n){ - int rc; - Vdbe *p = (Vdbe *)pStmt; - rc = vdbeUnbind(p, i); - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); - sqlcipher_sqlite3_mutex_leave(p->db->mutex); - } - return rc; -} -SQLITE_API int sqlcipher_sqlite3_bind_zeroblob64(sqlcipher_sqlite3_stmt *pStmt, int i, sqlcipher_sqlite3_uint64 n){ - int rc; - Vdbe *p = (Vdbe *)pStmt; - sqlcipher_sqlite3_mutex_enter(p->db->mutex); - if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){ - rc = SQLITE_TOOBIG; - }else{ - assert( (n & 0x7FFFFFFF)==n ); - rc = sqlcipher_sqlite3_bind_zeroblob(pStmt, i, n); - } - rc = sqlcipher_sqlite3ApiExit(p->db, rc); - sqlcipher_sqlite3_mutex_leave(p->db->mutex); - return rc; + assert( pCtx->pOut->flags==MEM_Null ); + assert( pCtx->skipFlag==0 ); + break; } -/* -** Return the number of wildcards that can be potentially bound to. -** This routine is added to support DBD::SQLite. +/* Opcode: AggFinal P1 P2 * P4 * +** Synopsis: accum=r[P1] N=P2 +** +** P1 is the memory location that is the accumulator for an aggregate +** or window function. Execute the finalizer function +** for an aggregate and store the result in P1. +** +** P2 is the number of arguments that the step function takes and +** P4 is a pointer to the FuncDef for this function. The P2 +** argument is not used by this opcode. It is only there to disambiguate +** functions that can take varying numbers of arguments. The +** P4 argument is only needed for the case where +** the step function was not previously called. */ -SQLITE_API int sqlcipher_sqlite3_bind_parameter_count(sqlcipher_sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe*)pStmt; - return p ? p->nVar : 0; -} - -/* -** Return the name of a wildcard parameter. Return NULL if the index -** is out of range or if the wildcard is unnamed. +/* Opcode: AggValue * P2 P3 P4 * +** Synopsis: r[P3]=value N=P2 ** -** The result is always UTF-8. +** Invoke the xValue() function and store the result in register P3. +** +** P2 is the number of arguments that the step function takes and +** P4 is a pointer to the FuncDef for this function. The P2 +** argument is not used by this opcode. It is only there to disambiguate +** functions that can take varying numbers of arguments. The +** P4 argument is only needed for the case where +** the step function was not previously called. */ -SQLITE_API const char *sqlcipher_sqlite3_bind_parameter_name(sqlcipher_sqlite3_stmt *pStmt, int i){ - Vdbe *p = (Vdbe*)pStmt; - if( p==0 ) return 0; - return sqlcipher_sqlite3VListNumToName(p->pVList, i); -} +case OP_AggValue: +case OP_AggFinal: { + Mem *pMem; + assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); + assert( pOp->p3==0 || pOp->opcode==OP_AggValue ); + pMem = &aMem[pOp->p1]; + assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); +#ifndef SQLITE_OMIT_WINDOWFUNC + if( pOp->p3 ){ + memAboutToChange(p, &aMem[pOp->p3]); + rc = sqlcipher_sqlite3VdbeMemAggValue(pMem, &aMem[pOp->p3], pOp->p4.pFunc); + pMem = &aMem[pOp->p3]; + }else +#endif + { + rc = sqlcipher_sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); + } -/* -** Given a wildcard parameter name, return the index of the variable -** with that name. If there is no variable with the given name, -** return 0. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nName){ - if( p==0 || zName==0 ) return 0; - return sqlcipher_sqlite3VListNameToNum(p->pVList, zName, nName); -} -SQLITE_API int sqlcipher_sqlite3_bind_parameter_index(sqlcipher_sqlite3_stmt *pStmt, const char *zName){ - return sqlcipher_sqlite3VdbeParameterIndex((Vdbe*)pStmt, zName, sqlcipher_sqlite3Strlen30(zName)); + if( rc ){ + sqlcipher_sqlite3VdbeError(p, "%s", sqlcipher_sqlite3_value_text(pMem)); + goto abort_due_to_error; + } + sqlcipher_sqlite3VdbeChangeEncoding(pMem, encoding); + UPDATE_MAX_BLOBSIZE(pMem); + break; } -/* -** Transfer all bindings from the first statement over to the second. +#ifndef SQLITE_OMIT_WAL +/* Opcode: Checkpoint P1 P2 P3 * * +** +** Checkpoint database P1. This is a no-op if P1 is not currently in +** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL, +** RESTART, or TRUNCATE. Write 1 or 0 into mem[P3] if the checkpoint returns +** SQLITE_BUSY or not, respectively. Write the number of pages in the +** WAL after the checkpoint into mem[P3+1] and the number of pages +** in the WAL that have been checkpointed after the checkpoint +** completes into mem[P3+2]. However on an error, mem[P3+1] and +** mem[P3+2] are initialized to -1. */ -SQLITE_PRIVATE int sqlcipher_sqlite3TransferBindings(sqlcipher_sqlite3_stmt *pFromStmt, sqlcipher_sqlite3_stmt *pToStmt){ - Vdbe *pFrom = (Vdbe*)pFromStmt; - Vdbe *pTo = (Vdbe*)pToStmt; - int i; - assert( pTo->db==pFrom->db ); - assert( pTo->nVar==pFrom->nVar ); - sqlcipher_sqlite3_mutex_enter(pTo->db->mutex); - for(i=0; inVar; i++){ - sqlcipher_sqlite3VdbeMemMove(&pTo->aVar[i], &pFrom->aVar[i]); +case OP_Checkpoint: { + int i; /* Loop counter */ + int aRes[3]; /* Results */ + Mem *pMem; /* Write results here */ + + assert( p->readOnly==0 ); + aRes[0] = 0; + aRes[1] = aRes[2] = -1; + assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE + || pOp->p2==SQLITE_CHECKPOINT_FULL + || pOp->p2==SQLITE_CHECKPOINT_RESTART + || pOp->p2==SQLITE_CHECKPOINT_TRUNCATE + ); + rc = sqlcipher_sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]); + if( rc ){ + if( rc!=SQLITE_BUSY ) goto abort_due_to_error; + rc = SQLITE_OK; + aRes[0] = 1; } - sqlcipher_sqlite3_mutex_leave(pTo->db->mutex); - return SQLITE_OK; -} + for(i=0, pMem = &aMem[pOp->p3]; i<3; i++, pMem++){ + sqlcipher_sqlite3VdbeMemSetInt64(pMem, (i64)aRes[i]); + } + break; +}; +#endif -#ifndef SQLITE_OMIT_DEPRECATED -/* -** Deprecated external interface. Internal/core SQLite code -** should call sqlcipher_sqlite3TransferBindings. +#ifndef SQLITE_OMIT_PRAGMA +/* Opcode: JournalMode P1 P2 P3 * * ** -** It is misuse to call this routine with statements from different -** database connections. But as this is a deprecated interface, we -** will not bother to check for that condition. +** Change the journal mode of database P1 to P3. P3 must be one of the +** PAGER_JOURNALMODE_XXX values. If changing between the various rollback +** modes (delete, truncate, persist, off and memory), this is a simple +** operation. No IO is required. ** -** If the two statements contain a different number of bindings, then -** an SQLITE_ERROR is returned. Nothing else can go wrong, so otherwise -** SQLITE_OK is returned. +** If changing into or out of WAL mode the procedure is more complicated. +** +** Write a string containing the final journal-mode to register P2. */ -SQLITE_API int sqlcipher_sqlite3_transfer_bindings(sqlcipher_sqlite3_stmt *pFromStmt, sqlcipher_sqlite3_stmt *pToStmt){ - Vdbe *pFrom = (Vdbe*)pFromStmt; - Vdbe *pTo = (Vdbe*)pToStmt; - if( pFrom->nVar!=pTo->nVar ){ - return SQLITE_ERROR; - } - assert( (pTo->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || pTo->expmask==0 ); - if( pTo->expmask ){ - pTo->expired = 1; +case OP_JournalMode: { /* out2 */ + Btree *pBt; /* Btree to change journal mode of */ + Pager *pPager; /* Pager associated with pBt */ + int eNew; /* New journal mode */ + int eOld; /* The old journal mode */ +#ifndef SQLITE_OMIT_WAL + const char *zFilename; /* Name of database file for pPager */ +#endif + + pOut = out2Prerelease(p, pOp); + eNew = pOp->p3; + assert( eNew==PAGER_JOURNALMODE_DELETE + || eNew==PAGER_JOURNALMODE_TRUNCATE + || eNew==PAGER_JOURNALMODE_PERSIST + || eNew==PAGER_JOURNALMODE_OFF + || eNew==PAGER_JOURNALMODE_MEMORY + || eNew==PAGER_JOURNALMODE_WAL + || eNew==PAGER_JOURNALMODE_QUERY + ); + assert( pOp->p1>=0 && pOp->p1nDb ); + assert( p->readOnly==0 ); + + pBt = db->aDb[pOp->p1].pBt; + pPager = sqlcipher_sqlite3BtreePager(pBt); + eOld = sqlcipher_sqlite3PagerGetJournalMode(pPager); + if( eNew==PAGER_JOURNALMODE_QUERY ) eNew = eOld; + assert( sqlcipher_sqlite3BtreeHoldsMutex(pBt) ); + if( !sqlcipher_sqlite3PagerOkToChangeJournalMode(pPager) ) eNew = eOld; + +#ifndef SQLITE_OMIT_WAL + zFilename = sqlcipher_sqlite3PagerFilename(pPager, 1); + + /* Do not allow a transition to journal_mode=WAL for a database + ** in temporary storage or if the VFS does not support shared memory + */ + if( eNew==PAGER_JOURNALMODE_WAL + && (sqlcipher_sqlite3Strlen30(zFilename)==0 /* Temp file */ + || !sqlcipher_sqlite3PagerWalSupported(pPager)) /* No shared-memory support */ + ){ + eNew = eOld; } - assert( (pFrom->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || pFrom->expmask==0 ); - if( pFrom->expmask ){ - pFrom->expired = 1; + + if( (eNew!=eOld) + && (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL) + ){ + if( !db->autoCommit || db->nVdbeRead>1 ){ + rc = SQLITE_ERROR; + sqlcipher_sqlite3VdbeError(p, + "cannot change %s wal mode from within a transaction", + (eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") + ); + goto abort_due_to_error; + }else{ + + if( eOld==PAGER_JOURNALMODE_WAL ){ + /* If leaving WAL mode, close the log file. If successful, the call + ** to PagerCloseWal() checkpoints and deletes the write-ahead-log + ** file. An EXCLUSIVE lock may still be held on the database file + ** after a successful return. + */ + rc = sqlcipher_sqlite3PagerCloseWal(pPager, db); + if( rc==SQLITE_OK ){ + sqlcipher_sqlite3PagerSetJournalMode(pPager, eNew); + } + }else if( eOld==PAGER_JOURNALMODE_MEMORY ){ + /* Cannot transition directly from MEMORY to WAL. Use mode OFF + ** as an intermediate */ + sqlcipher_sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_OFF); + } + + /* Open a transaction on the database file. Regardless of the journal + ** mode, this transaction always uses a rollback journal. + */ + assert( sqlcipher_sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE ); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); + } + } } - return sqlcipher_sqlite3TransferBindings(pFromStmt, pToStmt); -} -#endif +#endif /* ifndef SQLITE_OMIT_WAL */ -/* -** Return the sqlcipher_sqlite3* database handle to which the prepared statement given -** in the argument belongs. This is the same database handle that was -** the first argument to the sqlcipher_sqlite3_prepare() that was used to create -** the statement in the first place. -*/ -SQLITE_API sqlcipher_sqlite3 *sqlcipher_sqlite3_db_handle(sqlcipher_sqlite3_stmt *pStmt){ - return pStmt ? ((Vdbe*)pStmt)->db : 0; -} + if( rc ) eNew = eOld; + eNew = sqlcipher_sqlite3PagerSetJournalMode(pPager, eNew); -/* -** Return true if the prepared statement is guaranteed to not modify the -** database. -*/ -SQLITE_API int sqlcipher_sqlite3_stmt_readonly(sqlcipher_sqlite3_stmt *pStmt){ - return pStmt ? ((Vdbe*)pStmt)->readOnly : 1; -} + pOut->flags = MEM_Str|MEM_Static|MEM_Term; + pOut->z = (char *)sqlcipher_sqlite3JournalModename(eNew); + pOut->n = sqlcipher_sqlite3Strlen30(pOut->z); + pOut->enc = SQLITE_UTF8; + sqlcipher_sqlite3VdbeChangeEncoding(pOut, encoding); + if( rc ) goto abort_due_to_error; + break; +}; +#endif /* SQLITE_OMIT_PRAGMA */ -/* -** Return 1 if the statement is an EXPLAIN and return 2 if the -** statement is an EXPLAIN QUERY PLAN +#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) +/* Opcode: Vacuum P1 P2 * * * +** +** Vacuum the entire database P1. P1 is 0 for "main", and 2 or more +** for an attached database. The "temp" database may not be vacuumed. +** +** If P2 is not zero, then it is a register holding a string which is +** the file into which the result of vacuum should be written. When +** P2 is zero, the vacuum overwrites the original database. */ -SQLITE_API int sqlcipher_sqlite3_stmt_isexplain(sqlcipher_sqlite3_stmt *pStmt){ - return pStmt ? ((Vdbe*)pStmt)->explain : 0; +case OP_Vacuum: { + assert( p->readOnly==0 ); + rc = sqlcipher_sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1, + pOp->p2 ? &aMem[pOp->p2] : 0); + if( rc ) goto abort_due_to_error; + break; } +#endif -/* -** Return true if the prepared statement is in need of being reset. +#if !defined(SQLITE_OMIT_AUTOVACUUM) +/* Opcode: IncrVacuum P1 P2 * * * +** +** Perform a single step of the incremental vacuum procedure on +** the P1 database. If the vacuum has finished, jump to instruction +** P2. Otherwise, fall through to the next instruction. */ -SQLITE_API int sqlcipher_sqlite3_stmt_busy(sqlcipher_sqlite3_stmt *pStmt){ - Vdbe *v = (Vdbe*)pStmt; - return v!=0 && v->magic==VDBE_MAGIC_RUN && v->pc>=0; -} +case OP_IncrVacuum: { /* jump */ + Btree *pBt; -/* -** Return a pointer to the next prepared statement after pStmt associated -** with database connection pDb. If pStmt is NULL, return the first -** prepared statement for the database connection. Return NULL if there -** are no more. -*/ -SQLITE_API sqlcipher_sqlite3_stmt *sqlcipher_sqlite3_next_stmt(sqlcipher_sqlite3 *pDb, sqlcipher_sqlite3_stmt *pStmt){ - sqlcipher_sqlite3_stmt *pNext; -#ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlcipher_sqlite3SafetyCheckOk(pDb) ){ - (void)SQLITE_MISUSE_BKPT; - return 0; + assert( pOp->p1>=0 && pOp->p1nDb ); + assert( DbMaskTest(p->btreeMask, pOp->p1) ); + assert( p->readOnly==0 ); + pBt = db->aDb[pOp->p1].pBt; + rc = sqlcipher_sqlite3BtreeIncrVacuum(pBt); + VdbeBranchTaken(rc==SQLITE_DONE,2); + if( rc ){ + if( rc!=SQLITE_DONE ) goto abort_due_to_error; + rc = SQLITE_OK; + goto jump_to_p2; } + break; +} #endif - sqlcipher_sqlite3_mutex_enter(pDb->mutex); - if( pStmt==0 ){ - pNext = (sqlcipher_sqlite3_stmt*)pDb->pVdbe; + +/* Opcode: Expire P1 P2 * * * +** +** Cause precompiled statements to expire. When an expired statement +** is executed using sqlcipher_sqlite3_step() it will either automatically +** reprepare itself (if it was originally created using sqlcipher_sqlite3_prepare_v2()) +** or it will fail with SQLITE_SCHEMA. +** +** If P1 is 0, then all SQL statements become expired. If P1 is non-zero, +** then only the currently executing statement is expired. +** +** If P2 is 0, then SQL statements are expired immediately. If P2 is 1, +** then running SQL statements are allowed to continue to run to completion. +** The P2==1 case occurs when a CREATE INDEX or similar schema change happens +** that might help the statement run faster but which does not affect the +** correctness of operation. +*/ +case OP_Expire: { + assert( pOp->p2==0 || pOp->p2==1 ); + if( !pOp->p1 ){ + sqlcipher_sqlite3ExpirePreparedStatements(db, pOp->p2); }else{ - pNext = (sqlcipher_sqlite3_stmt*)((Vdbe*)pStmt)->pNext; + p->expired = pOp->p2+1; } - sqlcipher_sqlite3_mutex_leave(pDb->mutex); - return pNext; + break; } -/* -** Return the value of a status counter for a prepared statement +/* Opcode: CursorLock P1 * * * * +** +** Lock the btree to which cursor P1 is pointing so that the btree cannot be +** written by an other cursor. */ -SQLITE_API int sqlcipher_sqlite3_stmt_status(sqlcipher_sqlite3_stmt *pStmt, int op, int resetFlag){ - Vdbe *pVdbe = (Vdbe*)pStmt; - u32 v; -#ifdef SQLITE_ENABLE_API_ARMOR - if( !pStmt - || (op!=SQLITE_STMTSTATUS_MEMUSED && (op<0||op>=ArraySize(pVdbe->aCounter))) - ){ - (void)SQLITE_MISUSE_BKPT; - return 0; - } -#endif - if( op==SQLITE_STMTSTATUS_MEMUSED ){ - sqlcipher_sqlite3 *db = pVdbe->db; - sqlcipher_sqlite3_mutex_enter(db->mutex); - v = 0; - db->pnBytesFreed = (int*)&v; - sqlcipher_sqlite3VdbeClearObject(db, pVdbe); - sqlcipher_sqlite3DbFree(db, pVdbe); - db->pnBytesFreed = 0; - sqlcipher_sqlite3_mutex_leave(db->mutex); - }else{ - v = pVdbe->aCounter[op]; - if( resetFlag ) pVdbe->aCounter[op] = 0; - } - return (int)v; +case OP_CursorLock: { + VdbeCursor *pC; + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + sqlcipher_sqlite3BtreeCursorPin(pC->uc.pCursor); + break; } -/* -** Return the SQL associated with a prepared statement +/* Opcode: CursorUnlock P1 * * * * +** +** Unlock the btree to which cursor P1 is pointing so that it can be +** written by other cursors. */ -SQLITE_API const char *sqlcipher_sqlite3_sql(sqlcipher_sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe *)pStmt; - return p ? p->zSql : 0; +case OP_CursorUnlock: { + VdbeCursor *pC; + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + sqlcipher_sqlite3BtreeCursorUnpin(pC->uc.pCursor); + break; } -/* -** Return the SQL associated with a prepared statement with -** bound parameters expanded. Space to hold the returned string is -** obtained from sqlcipher_sqlite3_malloc(). The caller is responsible for -** freeing the returned string by passing it to sqlcipher_sqlite3_free(). +#ifndef SQLITE_OMIT_SHARED_CACHE +/* Opcode: TableLock P1 P2 P3 P4 * +** Synopsis: iDb=P1 root=P2 write=P3 ** -** The SQLITE_TRACE_SIZE_LIMIT puts an upper bound on the size of -** expanded bound parameters. +** Obtain a lock on a particular table. This instruction is only used when +** the shared-cache feature is enabled. +** +** P1 is the index of the database in sqlcipher_sqlite3.aDb[] of the database +** on which the lock is acquired. A readlock is obtained if P3==0 or +** a write lock if P3==1. +** +** P2 contains the root-page of the table to lock. +** +** P4 contains a pointer to the name of the table being locked. This is only +** used to generate an error message if the lock cannot be obtained. */ -SQLITE_API char *sqlcipher_sqlite3_expanded_sql(sqlcipher_sqlite3_stmt *pStmt){ -#ifdef SQLITE_OMIT_TRACE - return 0; -#else - char *z = 0; - const char *zSql = sqlcipher_sqlite3_sql(pStmt); - if( zSql ){ - Vdbe *p = (Vdbe *)pStmt; - sqlcipher_sqlite3_mutex_enter(p->db->mutex); - z = sqlcipher_sqlite3VdbeExpandSql(p, zSql); - sqlcipher_sqlite3_mutex_leave(p->db->mutex); +case OP_TableLock: { + u8 isWriteLock = (u8)pOp->p3; + if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommit) ){ + int p1 = pOp->p1; + assert( p1>=0 && p1nDb ); + assert( DbMaskTest(p->btreeMask, p1) ); + assert( isWriteLock==0 || isWriteLock==1 ); + rc = sqlcipher_sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock); + if( rc ){ + if( (rc&0xFF)==SQLITE_LOCKED ){ + const char *z = pOp->p4.z; + sqlcipher_sqlite3VdbeError(p, "database table is locked: %s", z); + } + goto abort_due_to_error; + } } - return z; -#endif + break; } +#endif /* SQLITE_OMIT_SHARED_CACHE */ -#ifdef SQLITE_ENABLE_NORMALIZE -/* -** Return the normalized SQL associated with a prepared statement. +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VBegin * * * P4 * +** +** P4 may be a pointer to an sqlcipher_sqlite3_vtab structure. If so, call the +** xBegin method for that table. +** +** Also, whether or not P4 is set, check that this is not being called from +** within a callback to a virtual table xSync() method. If it is, the error +** code will be set to SQLITE_LOCKED. */ -SQLITE_API const char *sqlcipher_sqlite3_normalized_sql(sqlcipher_sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe *)pStmt; - if( p==0 ) return 0; - if( p->zNormSql==0 && ALWAYS(p->zSql!=0) ){ - sqlcipher_sqlite3_mutex_enter(p->db->mutex); - p->zNormSql = sqlcipher_sqlite3Normalize(p, p->zSql); - sqlcipher_sqlite3_mutex_leave(p->db->mutex); - } - return p->zNormSql; +case OP_VBegin: { + VTable *pVTab; + pVTab = pOp->p4.pVtab; + rc = sqlcipher_sqlite3VtabBegin(db, pVTab); + if( pVTab ) sqlcipher_sqlite3VtabImportErrmsg(p, pVTab->pVtab); + if( rc ) goto abort_due_to_error; + break; } -#endif /* SQLITE_ENABLE_NORMALIZE */ +#endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK -/* -** Allocate and populate an UnpackedRecord structure based on the serialized -** record in nKey/pKey. Return a pointer to the new UnpackedRecord structure -** if successful, or a NULL pointer if an OOM error is encountered. +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VCreate P1 P2 * * * +** +** P2 is a register that holds the name of a virtual table in database +** P1. Call the xCreate method for that table. */ -static UnpackedRecord *vdbeUnpackRecord( - KeyInfo *pKeyInfo, - int nKey, - const void *pKey -){ - UnpackedRecord *pRet; /* Return value */ +case OP_VCreate: { + Mem sMem; /* For storing the record being decoded */ + const char *zTab; /* Name of the virtual table */ - pRet = sqlcipher_sqlite3VdbeAllocUnpackedRecord(pKeyInfo); - if( pRet ){ - memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nKeyField+1)); - sqlcipher_sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet); + memset(&sMem, 0, sizeof(sMem)); + sMem.db = db; + /* Because P2 is always a static string, it is impossible for the + ** sqlcipher_sqlite3VdbeMemCopy() to fail */ + assert( (aMem[pOp->p2].flags & MEM_Str)!=0 ); + assert( (aMem[pOp->p2].flags & MEM_Static)!=0 ); + rc = sqlcipher_sqlite3VdbeMemCopy(&sMem, &aMem[pOp->p2]); + assert( rc==SQLITE_OK ); + zTab = (const char*)sqlcipher_sqlite3_value_text(&sMem); + assert( zTab || db->mallocFailed ); + if( zTab ){ + rc = sqlcipher_sqlite3VtabCallCreate(db, pOp->p1, zTab, &p->zErrMsg); } - return pRet; + sqlcipher_sqlite3VdbeMemRelease(&sMem); + if( rc ) goto abort_due_to_error; + break; } +#endif /* SQLITE_OMIT_VIRTUALTABLE */ -/* -** This function is called from within a pre-update callback to retrieve -** a field of the row currently being updated or deleted. +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VDestroy P1 * * P4 * +** +** P4 is the name of a virtual table in database P1. Call the xDestroy method +** of that table. */ -SQLITE_API int sqlcipher_sqlite3_preupdate_old(sqlcipher_sqlite3 *db, int iIdx, sqlcipher_sqlite3_value **ppValue){ - PreUpdate *p = db->pPreUpdate; - Mem *pMem; - int rc = SQLITE_OK; - - /* Test that this call is being made from within an SQLITE_DELETE or - ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */ - if( !p || p->op==SQLITE_INSERT ){ - rc = SQLITE_MISUSE_BKPT; - goto preupdate_old_out; - } - if( p->pPk ){ - iIdx = sqlcipher_sqlite3TableColumnToIndex(p->pPk, iIdx); - } - if( iIdx>=p->pCsr->nField || iIdx<0 ){ - rc = SQLITE_RANGE; - goto preupdate_old_out; - } +case OP_VDestroy: { + db->nVDestroy++; + rc = sqlcipher_sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z); + db->nVDestroy--; + assert( p->errorAction==OE_Abort && p->usesStmtJournal ); + if( rc ) goto abort_due_to_error; + break; +} +#endif /* SQLITE_OMIT_VIRTUALTABLE */ - /* If the old.* record has not yet been loaded into memory, do so now. */ - if( p->pUnpacked==0 ){ - u32 nRec; - u8 *aRec; +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VOpen P1 * * P4 * +** +** P4 is a pointer to a virtual table object, an sqlcipher_sqlite3_vtab structure. +** P1 is a cursor number. This opcode opens a cursor to the virtual +** table and stores that cursor in P1. +*/ +case OP_VOpen: { + VdbeCursor *pCur; + sqlcipher_sqlite3_vtab_cursor *pVCur; + sqlcipher_sqlite3_vtab *pVtab; + const sqlcipher_sqlite3_module *pModule; - nRec = sqlcipher_sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); - aRec = sqlcipher_sqlite3DbMallocRaw(db, nRec); - if( !aRec ) goto preupdate_old_out; - rc = sqlcipher_sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); - if( rc==SQLITE_OK ){ - p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); - if( !p->pUnpacked ) rc = SQLITE_NOMEM; - } - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3DbFree(db, aRec); - goto preupdate_old_out; - } - p->aRecord = aRec; + assert( p->bIsReader ); + pCur = 0; + pVCur = 0; + pVtab = pOp->p4.pVtab->pVtab; + if( pVtab==0 || NEVER(pVtab->pModule==0) ){ + rc = SQLITE_LOCKED; + goto abort_due_to_error; } + pModule = pVtab->pModule; + rc = pModule->xOpen(pVtab, &pVCur); + sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); + if( rc ) goto abort_due_to_error; - pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; - if( iIdx==p->pTab->iPKey ){ - sqlcipher_sqlite3VdbeMemSetInt64(pMem, p->iKey1); - }else if( iIdx>=p->pUnpacked->nField ){ - *ppValue = (sqlcipher_sqlite3_value *)columnNullValue(); - }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ - if( pMem->flags & (MEM_Int|MEM_IntReal) ){ - testcase( pMem->flags & MEM_Int ); - testcase( pMem->flags & MEM_IntReal ); - sqlcipher_sqlite3VdbeMemRealify(pMem); - } - } + /* Initialize sqlcipher_sqlite3_vtab_cursor base class */ + pVCur->pVtab = pVtab; - preupdate_old_out: - sqlcipher_sqlite3Error(db, rc); - return sqlcipher_sqlite3ApiExit(db, rc); + /* Initialize vdbe cursor object */ + pCur = allocateCursor(p, pOp->p1, 0, CURTYPE_VTAB); + if( pCur ){ + pCur->uc.pVCur = pVCur; + pVtab->nRef++; + }else{ + assert( db->mallocFailed ); + pModule->xClose(pVCur); + goto no_mem; + } + break; } -#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +#endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK -/* -** This function is called from within a pre-update callback to retrieve -** the number of columns in the row being updated, deleted or inserted. +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VInitIn P1 P2 P3 * * +** Synopsis: r[P2]=ValueList(P1,P3) +** +** Set register P2 to be a pointer to a ValueList object for cursor P1 +** with cache register P3 and output register P3+1. This ValueList object +** can be used as the first argument to sqlcipher_sqlite3_vtab_in_first() and +** sqlcipher_sqlite3_vtab_in_next() to extract all of the values stored in the P1 +** cursor. Register P3 is used to hold the values returned by +** sqlcipher_sqlite3_vtab_in_first() and sqlcipher_sqlite3_vtab_in_next(). */ -SQLITE_API int sqlcipher_sqlite3_preupdate_count(sqlcipher_sqlite3 *db){ - PreUpdate *p = db->pPreUpdate; - return (p ? p->keyinfo.nKeyField : 0); +case OP_VInitIn: { /* out2 */ + VdbeCursor *pC; /* The cursor containing the RHS values */ + ValueList *pRhs; /* New ValueList object to put in reg[P2] */ + + pC = p->apCsr[pOp->p1]; + pRhs = sqlcipher_sqlite3_malloc64( sizeof(*pRhs) ); + if( pRhs==0 ) goto no_mem; + pRhs->pCsr = pC->uc.pCursor; + pRhs->pOut = &aMem[pOp->p3]; + pOut = out2Prerelease(p, pOp); + pOut->flags = MEM_Null; + sqlcipher_sqlite3VdbeMemSetPointer(pOut, pRhs, "ValueList", sqlcipher_sqlite3_free); + break; } -#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +#endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK -/* -** This function is designed to be called from within a pre-update callback -** only. It returns zero if the change that caused the callback was made -** immediately by a user SQL statement. Or, if the change was made by a -** trigger program, it returns the number of trigger programs currently -** on the stack (1 for a top-level trigger, 2 for a trigger fired by a -** top-level trigger etc.). + +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VFilter P1 P2 P3 P4 * +** Synopsis: iplan=r[P3] zplan='P4' ** -** For the purposes of the previous paragraph, a foreign key CASCADE, SET NULL -** or SET DEFAULT action is considered a trigger. +** P1 is a cursor opened using VOpen. P2 is an address to jump to if +** the filtered result set is empty. +** +** P4 is either NULL or a string that was generated by the xBestIndex +** method of the module. The interpretation of the P4 string is left +** to the module implementation. +** +** This opcode invokes the xFilter method on the virtual table specified +** by P1. The integer query plan parameter to xFilter is stored in register +** P3. Register P3+1 stores the argc parameter to be passed to the +** xFilter method. Registers P3+2..P3+1+argc are the argc +** additional parameters which are passed to +** xFilter as argv. Register P3+2 becomes argv[0] when passed to xFilter. +** +** A jump is made to P2 if the result set after filtering would be empty. */ -SQLITE_API int sqlcipher_sqlite3_preupdate_depth(sqlcipher_sqlite3 *db){ - PreUpdate *p = db->pPreUpdate; - return (p ? p->v->nFrame : 0); +case OP_VFilter: { /* jump */ + int nArg; + int iQuery; + const sqlcipher_sqlite3_module *pModule; + Mem *pQuery; + Mem *pArgc; + sqlcipher_sqlite3_vtab_cursor *pVCur; + sqlcipher_sqlite3_vtab *pVtab; + VdbeCursor *pCur; + int res; + int i; + Mem **apArg; + + pQuery = &aMem[pOp->p3]; + pArgc = &pQuery[1]; + pCur = p->apCsr[pOp->p1]; + assert( memIsValid(pQuery) ); + REGISTER_TRACE(pOp->p3, pQuery); + assert( pCur!=0 ); + assert( pCur->eCurType==CURTYPE_VTAB ); + pVCur = pCur->uc.pVCur; + pVtab = pVCur->pVtab; + pModule = pVtab->pModule; + + /* Grab the index number and argc parameters */ + assert( (pQuery->flags&MEM_Int)!=0 && pArgc->flags==MEM_Int ); + nArg = (int)pArgc->u.i; + iQuery = (int)pQuery->u.i; + + /* Invoke the xFilter method */ + apArg = p->apArg; + for(i = 0; ixFilter(pVCur, iQuery, pOp->p4.z, nArg, apArg); + sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); + if( rc ) goto abort_due_to_error; + res = pModule->xEof(pVCur); + pCur->nullRow = 0; + VdbeBranchTaken(res!=0,2); + if( res ) goto jump_to_p2; + break; } -#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +#endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK -/* -** This function is called from within a pre-update callback to retrieve -** a field of the row currently being updated or inserted. +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VColumn P1 P2 P3 * P5 +** Synopsis: r[P3]=vcolumn(P2) +** +** Store in register P3 the value of the P2-th column of +** the current row of the virtual-table of cursor P1. +** +** If the VColumn opcode is being used to fetch the value of +** an unchanging column during an UPDATE operation, then the P5 +** value is OPFLAG_NOCHNG. This will cause the sqlcipher_sqlite3_vtab_nochange() +** function to return true inside the xColumn method of the virtual +** table implementation. The P5 column might also contain other +** bits (OPFLAG_LENGTHARG or OPFLAG_TYPEOFARG) but those bits are +** unused by OP_VColumn. */ -SQLITE_API int sqlcipher_sqlite3_preupdate_new(sqlcipher_sqlite3 *db, int iIdx, sqlcipher_sqlite3_value **ppValue){ - PreUpdate *p = db->pPreUpdate; - int rc = SQLITE_OK; - Mem *pMem; +case OP_VColumn: { + sqlcipher_sqlite3_vtab *pVtab; + const sqlcipher_sqlite3_module *pModule; + Mem *pDest; + sqlcipher_sqlite3_context sContext; - if( !p || p->op==SQLITE_DELETE ){ - rc = SQLITE_MISUSE_BKPT; - goto preupdate_new_out; + VdbeCursor *pCur = p->apCsr[pOp->p1]; + assert( pCur!=0 ); + assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); + pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); + if( pCur->nullRow ){ + sqlcipher_sqlite3VdbeMemSetNull(pDest); + break; } - if( p->pPk && p->op!=SQLITE_UPDATE ){ - iIdx = sqlcipher_sqlite3TableColumnToIndex(p->pPk, iIdx); + assert( pCur->eCurType==CURTYPE_VTAB ); + pVtab = pCur->uc.pVCur->pVtab; + pModule = pVtab->pModule; + assert( pModule->xColumn ); + memset(&sContext, 0, sizeof(sContext)); + sContext.pOut = pDest; + sContext.enc = encoding; + assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 ); + if( pOp->p5 & OPFLAG_NOCHNG ){ + sqlcipher_sqlite3VdbeMemSetNull(pDest); + pDest->flags = MEM_Null|MEM_Zero; + pDest->u.nZero = 0; + }else{ + MemSetTypeFlag(pDest, MEM_Null); } - if( iIdx>=p->pCsr->nField || iIdx<0 ){ - rc = SQLITE_RANGE; - goto preupdate_new_out; + rc = pModule->xColumn(pCur->uc.pVCur, &sContext, pOp->p2); + sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); + if( sContext.isError>0 ){ + sqlcipher_sqlite3VdbeError(p, "%s", sqlcipher_sqlite3_value_text(pDest)); + rc = sContext.isError; } + sqlcipher_sqlite3VdbeChangeEncoding(pDest, encoding); + REGISTER_TRACE(pOp->p3, pDest); + UPDATE_MAX_BLOBSIZE(pDest); - if( p->op==SQLITE_INSERT ){ - /* For an INSERT, memory cell p->iNewReg contains the serialized record - ** that is being inserted. Deserialize it. */ - UnpackedRecord *pUnpack = p->pNewUnpacked; - if( !pUnpack ){ - Mem *pData = &p->v->aMem[p->iNewReg]; - rc = ExpandBlob(pData); - if( rc!=SQLITE_OK ) goto preupdate_new_out; - pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z); - if( !pUnpack ){ - rc = SQLITE_NOMEM; - goto preupdate_new_out; - } - p->pNewUnpacked = pUnpack; - } - pMem = &pUnpack->aMem[iIdx]; - if( iIdx==p->pTab->iPKey ){ - sqlcipher_sqlite3VdbeMemSetInt64(pMem, p->iKey2); - }else if( iIdx>=pUnpack->nField ){ - pMem = (sqlcipher_sqlite3_value *)columnNullValue(); - } - }else{ - /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required - ** value. Make a copy of the cell contents and return a pointer to it. - ** It is not safe to return a pointer to the memory cell itself as the - ** caller may modify the value text encoding. - */ - assert( p->op==SQLITE_UPDATE ); - if( !p->aNew ){ - p->aNew = (Mem *)sqlcipher_sqlite3DbMallocZero(db, sizeof(Mem) * p->pCsr->nField); - if( !p->aNew ){ - rc = SQLITE_NOMEM; - goto preupdate_new_out; - } - } - assert( iIdx>=0 && iIdxpCsr->nField ); - pMem = &p->aNew[iIdx]; - if( pMem->flags==0 ){ - if( iIdx==p->pTab->iPKey ){ - sqlcipher_sqlite3VdbeMemSetInt64(pMem, p->iKey2); - }else{ - rc = sqlcipher_sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]); - if( rc!=SQLITE_OK ) goto preupdate_new_out; - } - } + if( rc ) goto abort_due_to_error; + break; +} +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VNext P1 P2 * * * +** +** Advance virtual table P1 to the next row in its result set and +** jump to instruction P2. Or, if the virtual table has reached +** the end of its result set, then fall through to the next instruction. +*/ +case OP_VNext: { /* jump */ + sqlcipher_sqlite3_vtab *pVtab; + const sqlcipher_sqlite3_module *pModule; + int res; + VdbeCursor *pCur; + + pCur = p->apCsr[pOp->p1]; + assert( pCur!=0 ); + assert( pCur->eCurType==CURTYPE_VTAB ); + if( pCur->nullRow ){ + break; } - *ppValue = pMem; + pVtab = pCur->uc.pVCur->pVtab; + pModule = pVtab->pModule; + assert( pModule->xNext ); - preupdate_new_out: - sqlcipher_sqlite3Error(db, rc); - return sqlcipher_sqlite3ApiExit(db, rc); + /* Invoke the xNext() method of the module. There is no way for the + ** underlying implementation to return an error if one occurs during + ** xNext(). Instead, if an error occurs, true is returned (indicating that + ** data is available) and the error code returned when xColumn or + ** some other method is next invoked on the save virtual table cursor. + */ + rc = pModule->xNext(pCur->uc.pVCur); + sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); + if( rc ) goto abort_due_to_error; + res = pModule->xEof(pCur->uc.pVCur); + VdbeBranchTaken(!res,2); + if( !res ){ + /* If there is data, jump to P2 */ + goto jump_to_p2_and_check_for_interrupt; + } + goto check_for_interrupt; } -#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +#endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -/* -** Return status data for a single loop within query pStmt. +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VRename P1 * * P4 * +** +** P4 is a pointer to a virtual table object, an sqlcipher_sqlite3_vtab structure. +** This opcode invokes the corresponding xRename method. The value +** in register P1 is passed as the zName argument to the xRename method. */ -SQLITE_API int sqlcipher_sqlite3_stmt_scanstatus( - sqlcipher_sqlite3_stmt *pStmt, /* Prepared statement being queried */ - int idx, /* Index of loop to report on */ - int iScanStatusOp, /* Which metric to return */ - void *pOut /* OUT: Write the answer here */ -){ - Vdbe *p = (Vdbe*)pStmt; - ScanStatus *pScan; - if( idx<0 || idx>=p->nScan ) return 1; - pScan = &p->aScan[idx]; - switch( iScanStatusOp ){ - case SQLITE_SCANSTAT_NLOOP: { - *(sqlcipher_sqlite3_int64*)pOut = p->anExec[pScan->addrLoop]; - break; - } - case SQLITE_SCANSTAT_NVISIT: { - *(sqlcipher_sqlite3_int64*)pOut = p->anExec[pScan->addrVisit]; - break; - } - case SQLITE_SCANSTAT_EST: { - double r = 1.0; - LogEst x = pScan->nEst; - while( x<100 ){ - x += 10; - r *= 0.5; - } - *(double*)pOut = r*sqlcipher_sqlite3LogEstToInt(x); - break; - } - case SQLITE_SCANSTAT_NAME: { - *(const char**)pOut = pScan->zName; - break; +case OP_VRename: { + sqlcipher_sqlite3_vtab *pVtab; + Mem *pName; + int isLegacy; + + isLegacy = (db->flags & SQLITE_LegacyAlter); + db->flags |= SQLITE_LegacyAlter; + pVtab = pOp->p4.pVtab->pVtab; + pName = &aMem[pOp->p1]; + assert( pVtab->pModule->xRename ); + assert( memIsValid(pName) ); + assert( p->readOnly==0 ); + REGISTER_TRACE(pOp->p1, pName); + assert( pName->flags & MEM_Str ); + testcase( pName->enc==SQLITE_UTF8 ); + testcase( pName->enc==SQLITE_UTF16BE ); + testcase( pName->enc==SQLITE_UTF16LE ); + rc = sqlcipher_sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8); + if( rc ) goto abort_due_to_error; + rc = pVtab->pModule->xRename(pVtab, pName->z); + if( isLegacy==0 ) db->flags &= ~(u64)SQLITE_LegacyAlter; + sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); + p->expired = 0; + if( rc ) goto abort_due_to_error; + break; +} +#endif + +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VUpdate P1 P2 P3 P4 P5 +** Synopsis: data=r[P3@P2] +** +** P4 is a pointer to a virtual table object, an sqlcipher_sqlite3_vtab structure. +** This opcode invokes the corresponding xUpdate method. P2 values +** are contiguous memory cells starting at P3 to pass to the xUpdate +** invocation. The value in register (P3+P2-1) corresponds to the +** p2th element of the argv array passed to xUpdate. +** +** The xUpdate method will do a DELETE or an INSERT or both. +** The argv[0] element (which corresponds to memory cell P3) +** is the rowid of a row to delete. If argv[0] is NULL then no +** deletion occurs. The argv[1] element is the rowid of the new +** row. This can be NULL to have the virtual table select the new +** rowid for itself. The subsequent elements in the array are +** the values of columns in the new row. +** +** If P2==1 then no insert is performed. argv[0] is the rowid of +** a row to delete. +** +** P1 is a boolean flag. If it is set to true and the xUpdate call +** is successful, then the value returned by sqlcipher_sqlite3_last_insert_rowid() +** is set to the value of the rowid for the row just inserted. +** +** P5 is the error actions (OE_Replace, OE_Fail, OE_Ignore, etc) to +** apply in the case of a constraint failure on an insert or update. +*/ +case OP_VUpdate: { + sqlcipher_sqlite3_vtab *pVtab; + const sqlcipher_sqlite3_module *pModule; + int nArg; + int i; + sqlite_int64 rowid = 0; + Mem **apArg; + Mem *pX; + + assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback + || pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace + ); + assert( p->readOnly==0 ); + if( db->mallocFailed ) goto no_mem; + sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); + pVtab = pOp->p4.pVtab->pVtab; + if( pVtab==0 || NEVER(pVtab->pModule==0) ){ + rc = SQLITE_LOCKED; + goto abort_due_to_error; + } + pModule = pVtab->pModule; + nArg = pOp->p2; + assert( pOp->p4type==P4_VTAB ); + if( ALWAYS(pModule->xUpdate) ){ + u8 vtabOnConflict = db->vtabOnConflict; + apArg = p->apArg; + pX = &aMem[pOp->p3]; + for(i=0; iaddrExplain ){ - *(const char**)pOut = p->aOp[ pScan->addrExplain ].p4.z; - }else{ - *(const char**)pOut = 0; - } - break; + db->vtabOnConflict = pOp->p5; + rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); + db->vtabOnConflict = vtabOnConflict; + sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); + if( rc==SQLITE_OK && pOp->p1 ){ + assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) ); + db->lastRowid = rowid; } - case SQLITE_SCANSTAT_SELECTID: { - if( pScan->addrExplain ){ - *(int*)pOut = p->aOp[ pScan->addrExplain ].p1; + if( (rc&0xff)==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){ + if( pOp->p5==OE_Ignore ){ + rc = SQLITE_OK; }else{ - *(int*)pOut = -1; + p->errorAction = ((pOp->p5==OE_Replace) ? OE_Abort : pOp->p5); } - break; - } - default: { - return 1; + }else{ + p->nChange++; } + if( rc ) goto abort_due_to_error; } - return 0; + break; } +#endif /* SQLITE_OMIT_VIRTUALTABLE */ -/* -** Zero all counters associated with the sqlcipher_sqlite3_stmt_scanstatus() data. +#ifndef SQLITE_OMIT_PAGER_PRAGMAS +/* Opcode: Pagecount P1 P2 * * * +** +** Write the current number of pages in database P1 to memory cell P2. */ -SQLITE_API void sqlcipher_sqlite3_stmt_scanstatus_reset(sqlcipher_sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe*)pStmt; - memset(p->anExec, 0, p->nOp * sizeof(i64)); +case OP_Pagecount: { /* out2 */ + pOut = out2Prerelease(p, pOp); + pOut->u.i = sqlcipher_sqlite3BtreeLastPage(db->aDb[pOp->p1].pBt); + break; } -#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */ +#endif -/************** End of vdbeapi.c *********************************************/ -/************** Begin file vdbetrace.c ***************************************/ -/* -** 2009 November 25 -** -** 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. -** -************************************************************************* + +#ifndef SQLITE_OMIT_PAGER_PRAGMAS +/* Opcode: MaxPgcnt P1 P2 P3 * * ** -** This file contains code used to insert the values of host parameters -** (aka "wildcards") into the SQL text output by sqlcipher_sqlite3_trace(). +** Try to set the maximum page count for database P1 to the value in P3. +** Do not let the maximum page count fall below the current page count and +** do not change the maximum page count value if P3==0. ** -** The Vdbe parse-tree explainer is also found here. -*/ -/* #include "sqliteInt.h" */ -/* #include "vdbeInt.h" */ - -#ifndef SQLITE_OMIT_TRACE - -/* -** zSql is a zero-terminated string of UTF-8 SQL text. Return the number of -** bytes in this text up to but excluding the first character in -** a host parameter. If the text contains no host parameters, return -** the total number of bytes in the text. +** Store the maximum page count after the change in register P2. */ -static int findNextHostParameter(const char *zSql, int *pnToken){ - int tokenType; - int nTotal = 0; - int n; +case OP_MaxPgcnt: { /* out2 */ + unsigned int newMax; + Btree *pBt; - *pnToken = 0; - while( zSql[0] ){ - n = sqlcipher_sqlite3GetToken((u8*)zSql, &tokenType); - assert( n>0 && tokenType!=TK_ILLEGAL ); - if( tokenType==TK_VARIABLE ){ - *pnToken = n; - break; - } - nTotal += n; - zSql += n; + pOut = out2Prerelease(p, pOp); + pBt = db->aDb[pOp->p1].pBt; + newMax = 0; + if( pOp->p3 ){ + newMax = sqlcipher_sqlite3BtreeLastPage(pBt); + if( newMax < (unsigned)pOp->p3 ) newMax = (unsigned)pOp->p3; } - return nTotal; + pOut->u.i = sqlcipher_sqlite3BtreeMaxPageCount(pBt, newMax); + break; } +#endif -/* -** This function returns a pointer to a nul-terminated string in memory -** obtained from sqlcipher_sqlite3DbMalloc(). If sqlcipher_sqlite3.nVdbeExec is 1, then the -** string contains a copy of zRawSql but with host parameters expanded to -** their current bindings. Or, if sqlcipher_sqlite3.nVdbeExec is greater than 1, -** then the returned string holds a copy of zRawSql with "-- " prepended -** to each line of text. +/* Opcode: Function P1 P2 P3 P4 * +** Synopsis: r[P3]=func(r[P2@NP]) ** -** If the SQLITE_TRACE_SIZE_LIMIT macro is defined to an integer, then -** then long strings and blobs are truncated to that many bytes. This -** can be used to prevent unreasonably large trace strings when dealing -** with large (multi-megabyte) strings and blobs. +** Invoke a user function (P4 is a pointer to an sqlcipher_sqlite3_context object that +** contains a pointer to the function to be run) with arguments taken +** from register P2 and successors. The number of arguments is in +** the sqlcipher_sqlite3_context object that P4 points to. +** The result of the function is stored +** in register P3. Register P3 must not be one of the function inputs. ** -** The calling function is responsible for making sure the memory returned -** is eventually freed. +** P1 is a 32-bit bitmask indicating whether or not each argument to the +** function was determined to be constant at compile time. If the first +** argument was constant then bit 0 of P1 is set. This is used to determine +** whether meta data associated with a user function argument using the +** sqlcipher_sqlite3_set_auxdata() API may be safely retained until the next +** invocation of this opcode. ** -** ALGORITHM: Scan the input string looking for host parameters in any of -** these forms: ?, ?N, $A, @A, :A. Take care to avoid text within -** string literals, quoted identifier names, and comments. For text forms, -** the host parameter index is found by scanning the prepared -** statement for the corresponding OP_Variable opcode. Once the host -** parameter index is known, locate the value in p->aVar[]. Then render -** the value as a literal in place of the host parameter name. +** See also: AggStep, AggFinal, PureFunc */ -SQLITE_PRIVATE char *sqlcipher_sqlite3VdbeExpandSql( - Vdbe *p, /* The prepared statement being evaluated */ - const char *zRawSql /* Raw text of the SQL statement */ -){ - sqlcipher_sqlite3 *db; /* The database connection */ - int idx = 0; /* Index of a host parameter */ - int nextIndex = 1; /* Index of next ? host parameter */ - int n; /* Length of a token prefix */ - int nToken; /* Length of the parameter token */ - int i; /* Loop counter */ - Mem *pVar; /* Value of a host parameter */ - StrAccum out; /* Accumulate the output here */ -#ifndef SQLITE_OMIT_UTF16 - Mem utf8; /* Used to convert UTF16 into UTF8 for display */ -#endif - char zBase[100]; /* Initial working space */ - - db = p->db; - sqlcipher_sqlite3StrAccumInit(&out, 0, zBase, sizeof(zBase), - db->aLimit[SQLITE_LIMIT_LENGTH]); - if( db->nVdbeExec>1 ){ - while( *zRawSql ){ - const char *zStart = zRawSql; - while( *(zRawSql++)!='\n' && *zRawSql ); - sqlcipher_sqlite3_str_append(&out, "-- ", 3); - assert( (zRawSql - zStart) > 0 ); - sqlcipher_sqlite3_str_append(&out, zStart, (int)(zRawSql-zStart)); - } - }else if( p->nVar==0 ){ - sqlcipher_sqlite3_str_append(&out, zRawSql, sqlcipher_sqlite3Strlen30(zRawSql)); - }else{ - while( zRawSql[0] ){ - n = findNextHostParameter(zRawSql, &nToken); - assert( n>0 ); - sqlcipher_sqlite3_str_append(&out, zRawSql, n); - zRawSql += n; - assert( zRawSql[0] || nToken==0 ); - if( nToken==0 ) break; - if( zRawSql[0]=='?' ){ - if( nToken>1 ){ - assert( sqlcipher_sqlite3Isdigit(zRawSql[1]) ); - sqlcipher_sqlite3GetInt32(&zRawSql[1], &idx); - }else{ - idx = nextIndex; - } - }else{ - assert( zRawSql[0]==':' || zRawSql[0]=='$' || - zRawSql[0]=='@' || zRawSql[0]=='#' ); - testcase( zRawSql[0]==':' ); - testcase( zRawSql[0]=='$' ); - testcase( zRawSql[0]=='@' ); - testcase( zRawSql[0]=='#' ); - idx = sqlcipher_sqlite3VdbeParameterIndex(p, zRawSql, nToken); - assert( idx>0 ); - } - zRawSql += nToken; - nextIndex = idx + 1; - assert( idx>0 && idx<=p->nVar ); - pVar = &p->aVar[idx-1]; - if( pVar->flags & MEM_Null ){ - sqlcipher_sqlite3_str_append(&out, "NULL", 4); - }else if( pVar->flags & (MEM_Int|MEM_IntReal) ){ - sqlcipher_sqlite3_str_appendf(&out, "%lld", pVar->u.i); - }else if( pVar->flags & MEM_Real ){ - sqlcipher_sqlite3_str_appendf(&out, "%!.15g", pVar->u.r); - }else if( pVar->flags & MEM_Str ){ - int nOut; /* Number of bytes of the string text to include in output */ -#ifndef SQLITE_OMIT_UTF16 - u8 enc = ENC(db); - if( enc!=SQLITE_UTF8 ){ - memset(&utf8, 0, sizeof(utf8)); - utf8.db = db; - sqlcipher_sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC); - if( SQLITE_NOMEM==sqlcipher_sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8) ){ - out.accError = SQLITE_NOMEM; - out.nAlloc = 0; - } - pVar = &utf8; - } -#endif - nOut = pVar->n; -#ifdef SQLITE_TRACE_SIZE_LIMIT - if( nOut>SQLITE_TRACE_SIZE_LIMIT ){ - nOut = SQLITE_TRACE_SIZE_LIMIT; - while( nOutn && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; } - } -#endif - sqlcipher_sqlite3_str_appendf(&out, "'%.*q'", nOut, pVar->z); -#ifdef SQLITE_TRACE_SIZE_LIMIT - if( nOutn ){ - sqlcipher_sqlite3_str_appendf(&out, "/*+%d bytes*/", pVar->n-nOut); - } -#endif -#ifndef SQLITE_OMIT_UTF16 - if( enc!=SQLITE_UTF8 ) sqlcipher_sqlite3VdbeMemRelease(&utf8); -#endif - }else if( pVar->flags & MEM_Zero ){ - sqlcipher_sqlite3_str_appendf(&out, "zeroblob(%d)", pVar->u.nZero); - }else{ - int nOut; /* Number of bytes of the blob to include in output */ - assert( pVar->flags & MEM_Blob ); - sqlcipher_sqlite3_str_append(&out, "x'", 2); - nOut = pVar->n; -#ifdef SQLITE_TRACE_SIZE_LIMIT - if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT; -#endif - for(i=0; iz[i]&0xff); - } - sqlcipher_sqlite3_str_append(&out, "'", 1); -#ifdef SQLITE_TRACE_SIZE_LIMIT - if( nOutn ){ - sqlcipher_sqlite3_str_appendf(&out, "/*+%d bytes*/", pVar->n-nOut); - } -#endif - } - } - } - if( out.accError ) sqlcipher_sqlite3_str_reset(&out); - return sqlcipher_sqlite3StrAccumFinish(&out); -} - -#endif /* #ifndef SQLITE_OMIT_TRACE */ - -/************** End of vdbetrace.c *******************************************/ -/************** Begin file vdbe.c ********************************************/ -/* -** 2001 September 15 +/* Opcode: PureFunc P1 P2 P3 P4 * +** Synopsis: r[P3]=func(r[P2@NP]) ** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** Invoke a user function (P4 is a pointer to an sqlcipher_sqlite3_context object that +** contains a pointer to the function to be run) with arguments taken +** from register P2 and successors. The number of arguments is in +** the sqlcipher_sqlite3_context object that P4 points to. +** The result of the function is stored +** in register P3. Register P3 must not be one of the function inputs. ** -** 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. +** P1 is a 32-bit bitmask indicating whether or not each argument to the +** function was determined to be constant at compile time. If the first +** argument was constant then bit 0 of P1 is set. This is used to determine +** whether meta data associated with a user function argument using the +** sqlcipher_sqlite3_set_auxdata() API may be safely retained until the next +** invocation of this opcode. ** -************************************************************************* -** The code in this file implements the function that runs the -** bytecode of a prepared statement. +** This opcode works exactly like OP_Function. The only difference is in +** its name. This opcode is used in places where the function must be +** purely non-deterministic. Some built-in date/time functions can be +** either determinitic of non-deterministic, depending on their arguments. +** When those function are used in a non-deterministic way, they will check +** to see if they were called using OP_PureFunc instead of OP_Function, and +** if they were, they throw an error. ** -** Various scripts scan this source file in order to generate HTML -** documentation, headers files, or other derived files. The formatting -** of the code in this file is, therefore, important. See other comments -** in this file for details. If in doubt, do not deviate from existing -** commenting and indentation practices when changing or adding code. -*/ -/* #include "sqliteInt.h" */ -/* #include "vdbeInt.h" */ - -/* -** Invoke this macro on memory cells just prior to changing the -** value of the cell. This macro verifies that shallow copies are -** not misused. A shallow copy of a string or blob just copies a -** pointer to the string or blob, not the content. If the original -** is changed while the copy is still in use, the string or blob might -** be changed out from under the copy. This macro verifies that nothing -** like that ever happens. +** See also: AggStep, AggFinal, Function */ -#ifdef SQLITE_DEBUG -# define memAboutToChange(P,M) sqlcipher_sqlite3VdbeMemAboutToChange(P,M) -#else -# define memAboutToChange(P,M) -#endif +case OP_PureFunc: /* group */ +case OP_Function: { /* group */ + int i; + sqlcipher_sqlite3_context *pCtx; -/* -** The following global variable is incremented every time a cursor -** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test -** procedures use this information to make sure that indices are -** working correctly. This variable has no function other than to -** help verify the correct operation of the library. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlcipher_sqlite3_search_count = 0; -#endif + assert( pOp->p4type==P4_FUNCCTX ); + pCtx = pOp->p4.pCtx; -/* -** When this global variable is positive, it gets decremented once before -** each instruction in the VDBE. When it reaches zero, the u1.isInterrupted -** field of the sqlcipher_sqlite3 structure is set in order to simulate an interrupt. -** -** This facility is used for testing purposes only. It does not function -** in an ordinary build. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlcipher_sqlite3_interrupt_count = 0; -#endif + /* If this function is inside of a trigger, the register array in aMem[] + ** might change from one evaluation to the next. The next block of code + ** checks to see if the register array has changed, and if so it + ** reinitializes the relavant parts of the sqlcipher_sqlite3_context object */ + pOut = &aMem[pOp->p3]; + if( pCtx->pOut != pOut ){ + pCtx->pVdbe = p; + pCtx->pOut = pOut; + pCtx->enc = encoding; + for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i]; + } + assert( pCtx->pVdbe==p ); -/* -** The next global variable is incremented each type the OP_Sort opcode -** is executed. The test procedures use this information to make sure that -** sorting is occurring or not occurring at appropriate times. This variable -** has no function other than to help verify the correct operation of the -** library. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlcipher_sqlite3_sort_count = 0; + memAboutToChange(p, pOut); +#ifdef SQLITE_DEBUG + for(i=0; iargc; i++){ + assert( memIsValid(pCtx->argv[i]) ); + REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]); + } #endif + MemSetTypeFlag(pOut, MEM_Null); + assert( pCtx->isError==0 ); + (*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */ -/* -** The next global variable records the size of the largest MEM_Blob -** or MEM_Str that has been used by a VDBE opcode. The test procedures -** use this information to make sure that the zero-blob functionality -** is working correctly. This variable has no function other than to -** help verify the correct operation of the library. -*/ -#ifdef SQLITE_TEST -SQLITE_API int sqlcipher_sqlite3_max_blobsize = 0; -static void updateMaxBlobsize(Mem *p){ - if( (p->flags & (MEM_Str|MEM_Blob))!=0 && p->n>sqlcipher_sqlite3_max_blobsize ){ - sqlcipher_sqlite3_max_blobsize = p->n; + /* If the function returned an error, throw an exception */ + if( pCtx->isError ){ + if( pCtx->isError>0 ){ + sqlcipher_sqlite3VdbeError(p, "%s", sqlcipher_sqlite3_value_text(pOut)); + rc = pCtx->isError; + } + sqlcipher_sqlite3VdbeDeleteAuxData(db, &p->pAuxData, pCtx->iOp, pOp->p1); + pCtx->isError = 0; + if( rc ) goto abort_due_to_error; } + + assert( (pOut->flags&MEM_Str)==0 + || pOut->enc==encoding + || db->mallocFailed ); + assert( !sqlcipher_sqlite3VdbeMemTooBig(pOut) ); + + REGISTER_TRACE(pOp->p3, pOut); + UPDATE_MAX_BLOBSIZE(pOut); + break; } -#endif -/* -** This macro evaluates to true if either the update hook or the preupdate -** hook are enabled for database connect DB. +/* Opcode: ClrSubtype P1 * * * * +** Synopsis: r[P1].subtype = 0 +** +** Clear the subtype from register P1. */ -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK -# define HAS_UPDATE_HOOK(DB) ((DB)->xPreUpdateCallback||(DB)->xUpdateCallback) -#else -# define HAS_UPDATE_HOOK(DB) ((DB)->xUpdateCallback) -#endif +case OP_ClrSubtype: { /* in1 */ + pIn1 = &aMem[pOp->p1]; + pIn1->flags &= ~MEM_Subtype; + break; +} -/* -** The next global variable is incremented each time the OP_Found opcode -** is executed. This is used to test whether or not the foreign key -** operation implemented using OP_FkIsZero is working. This variable -** has no function other than to help verify the correct operation of the -** library. +/* Opcode: FilterAdd P1 * P3 P4 * +** Synopsis: filter(P1) += key(P3@P4) +** +** Compute a hash on the P4 registers starting with r[P3] and +** add that hash to the bloom filter contained in r[P1]. */ -#ifdef SQLITE_TEST -SQLITE_API int sqlcipher_sqlite3_found_count = 0; -#endif +case OP_FilterAdd: { + u64 h; -/* -** Test a register to see if it exceeds the current maximum blob size. -** If it does, record the new maximum blob size. -*/ -#if defined(SQLITE_TEST) && !defined(SQLITE_UNTESTABLE) -# define UPDATE_MAX_BLOBSIZE(P) updateMaxBlobsize(P) -#else -# define UPDATE_MAX_BLOBSIZE(P) + assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); + pIn1 = &aMem[pOp->p1]; + assert( pIn1->flags & MEM_Blob ); + assert( pIn1->n>0 ); + h = filterHash(aMem, pOp); +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + int ii; + for(ii=pOp->p3; iip3+pOp->p4.i; ii++){ + registerTrace(ii, &aMem[ii]); + } + printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n)); + } #endif + h %= pIn1->n; + pIn1->z[h/8] |= 1<<(h&7); + break; +} -#ifdef SQLITE_DEBUG -/* This routine provides a convenient place to set a breakpoint during -** tracing with PRAGMA vdbe_trace=on. The breakpoint fires right after -** each opcode is printed. Variables "pc" (program counter) and pOp are -** available to add conditionals to the breakpoint. GDB example: +/* Opcode: Filter P1 P2 P3 P4 * +** Synopsis: if key(P3@P4) not in filter(P1) goto P2 ** -** break test_trace_breakpoint if pc=22 +** Compute a hash on the key contained in the P4 registers starting +** with r[P3]. Check to see if that hash is found in the +** bloom filter hosted by register P1. If it is not present then +** maybe jump to P2. Otherwise fall through. ** -** Other useful labels for breakpoints include: -** test_addop_breakpoint(pc,pOp) -** sqlcipher_sqlite3CorruptError(lineno) -** sqlcipher_sqlite3MisuseError(lineno) -** sqlcipher_sqlite3CantopenError(lineno) +** False negatives are harmless. It is always safe to fall through, +** even if the value is in the bloom filter. A false negative causes +** more CPU cycles to be used, but it should still yield the correct +** answer. However, an incorrect answer may well arise from a +** false positive - if the jump is taken when it should fall through. */ -static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){ - static int n = 0; - n++; -} +case OP_Filter: { /* jump */ + u64 h; + + assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); + pIn1 = &aMem[pOp->p1]; + assert( (pIn1->flags & MEM_Blob)!=0 ); + assert( pIn1->n >= 1 ); + h = filterHash(aMem, pOp); +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + int ii; + for(ii=pOp->p3; iip3+pOp->p4.i; ii++){ + registerTrace(ii, &aMem[ii]); + } + printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n)); + } #endif + h %= pIn1->n; + if( (pIn1->z[h/8] & (1<<(h&7)))==0 ){ + VdbeBranchTaken(1, 2); + p->aCounter[SQLITE_STMTSTATUS_FILTER_HIT]++; + goto jump_to_p2; + }else{ + p->aCounter[SQLITE_STMTSTATUS_FILTER_MISS]++; + VdbeBranchTaken(0, 2); + } + break; +} -/* -** Invoke the VDBE coverage callback, if that callback is defined. This -** feature is used for test suite validation only and does not appear an -** production builds. +/* Opcode: Trace P1 P2 * P4 * ** -** M is the type of branch. I is the direction taken for this instance of -** the branch. +** Write P4 on the statement trace output if statement tracing is +** enabled. ** -** M: 2 - two-way branch (I=0: fall-thru 1: jump ) -** 3 - two-way + NULL (I=0: fall-thru 1: jump 2: NULL ) -** 4 - OP_Jump (I=0: jump p1 1: jump p2 2: jump p3) +** Operand P1 must be 0x7fffffff and P2 must positive. +*/ +/* Opcode: Init P1 P2 P3 P4 * +** Synopsis: Start at P2 ** -** In other words, if M is 2, then I is either 0 (for fall-through) or -** 1 (for when the branch is taken). If M is 3, the I is 0 for an -** ordinary fall-through, I is 1 if the branch was taken, and I is 2 -** if the result of comparison is NULL. For M=3, I=2 the jump may or -** may not be taken, depending on the SQLITE_JUMPIFNULL flags in p5. -** When M is 4, that means that an OP_Jump is being run. I is 0, 1, or 2 -** depending on if the operands are less than, equal, or greater than. +** Programs contain a single instance of this opcode as the very first +** opcode. ** -** iSrcLine is the source code line (from the __LINE__ macro) that -** generated the VDBE instruction combined with flag bits. The source -** code line number is in the lower 24 bits of iSrcLine and the upper -** 8 bytes are flags. The lower three bits of the flags indicate -** values for I that should never occur. For example, if the branch is -** always taken, the flags should be 0x05 since the fall-through and -** alternate branch are never taken. If a branch is never taken then -** flags should be 0x06 since only the fall-through approach is allowed. +** If tracing is enabled (by the sqlcipher_sqlite3_trace()) interface, then +** the UTF-8 string contained in P4 is emitted on the trace callback. +** Or if P4 is blank, use the string returned by sqlcipher_sqlite3_sql(). ** -** Bit 0x08 of the flags indicates an OP_Jump opcode that is only -** interested in equal or not-equal. In other words, I==0 and I==2 -** should be treated as equivalent +** If P2 is not zero, jump to instruction P2. ** -** Since only a line number is retained, not the filename, this macro -** only works for amalgamation builds. But that is ok, since these macros -** should be no-ops except for special builds used to measure test coverage. -*/ -#if !defined(SQLITE_VDBE_COVERAGE) -# define VdbeBranchTaken(I,M) -#else -# define VdbeBranchTaken(I,M) vdbeTakeBranch(pOp->iSrcLine,I,M) - static void vdbeTakeBranch(u32 iSrcLine, u8 I, u8 M){ - u8 mNever; - assert( I<=2 ); /* 0: fall through, 1: taken, 2: alternate taken */ - assert( M<=4 ); /* 2: two-way branch, 3: three-way branch, 4: OP_Jump */ - assert( I> 24; - assert( (I & mNever)==0 ); - if( sqlcipher_sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/ - /* Invoke the branch coverage callback with three arguments: - ** iSrcLine - the line number of the VdbeCoverage() macro, with - ** flags removed. - ** I - Mask of bits 0x07 indicating which cases are are - ** fulfilled by this instance of the jump. 0x01 means - ** fall-thru, 0x02 means taken, 0x04 means NULL. Any - ** impossible cases (ex: if the comparison is never NULL) - ** are filled in automatically so that the coverage - ** measurement logic does not flag those impossible cases - ** as missed coverage. - ** M - Type of jump. Same as M argument above - */ - I |= mNever; - if( M==2 ) I |= 0x04; - if( M==4 ){ - I |= 0x08; - if( (mNever&0x08)!=0 && (I&0x05)!=0) I |= 0x05; /*NO_TEST*/ - } - sqlcipher_sqlite3GlobalConfig.xVdbeBranch(sqlcipher_sqlite3GlobalConfig.pVdbeBranchArg, - iSrcLine&0xffffff, I, M); - } -#endif - -/* -** An ephemeral string value (signified by the MEM_Ephem flag) contains -** a pointer to a dynamically allocated string where some other entity -** is responsible for deallocating that string. Because the register -** does not control the string, it might be deleted without the register -** knowing it. +** Increment the value of P1 so that OP_Once opcodes will jump the +** first time they are evaluated for this run. ** -** This routine converts an ephemeral string into a dynamically allocated -** string that the register itself controls. In other words, it -** converts an MEM_Ephem string into a string with P.z==P.zMalloc. +** If P3 is not zero, then it is an address to jump to if an SQLITE_CORRUPT +** error is encountered. */ -#define Deephemeralize(P) \ - if( ((P)->flags&MEM_Ephem)!=0 \ - && sqlcipher_sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;} - -/* Return true if the cursor was opened using the OP_OpenSorter opcode. */ -#define isSorter(x) ((x)->eCurType==CURTYPE_SORTER) +case OP_Trace: +case OP_Init: { /* jump */ + int i; +#ifndef SQLITE_OMIT_TRACE + char *zTrace; +#endif -/* -** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL -** if we run out of memory. -*/ -static VdbeCursor *allocateCursor( - Vdbe *p, /* The virtual machine */ - int iCur, /* Index of the new VdbeCursor */ - int nField, /* Number of fields in the table or index */ - int iDb, /* Database the cursor belongs to, or -1 */ - u8 eCurType /* Type of the new cursor */ -){ - /* Find the memory cell that will be used to store the blob of memory - ** required for this VdbeCursor structure. It is convenient to use a - ** vdbe memory cell to manage the memory allocation required for a - ** VdbeCursor structure for the following reasons: - ** - ** * Sometimes cursor numbers are used for a couple of different - ** purposes in a vdbe program. The different uses might require - ** different sized allocations. Memory cells provide growable - ** allocations. - ** - ** * When using ENABLE_MEMORY_MANAGEMENT, memory cell buffers can - ** be freed lazily via the sqlcipher_sqlite3_release_memory() API. This - ** minimizes the number of malloc calls made by the system. + /* If the P4 argument is not NULL, then it must be an SQL comment string. + ** The "--" string is broken up to prevent false-positives with srcck1.c. ** - ** The memory cell for cursor 0 is aMem[0]. The rest are allocated from - ** the top of the register space. Cursor 1 is at Mem[p->nMem-1]. - ** Cursor 2 is at Mem[p->nMem-2]. And so forth. + ** This assert() provides evidence for: + ** EVIDENCE-OF: R-50676-09860 The callback can compute the same text that + ** would have been returned by the legacy sqlcipher_sqlite3_trace() interface by + ** using the X argument when X begins with "--" and invoking + ** sqlcipher_sqlite3_expanded_sql(P) otherwise. */ - Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem; + assert( pOp->p4.z==0 || strncmp(pOp->p4.z, "-" "- ", 3)==0 ); - int nByte; - VdbeCursor *pCx = 0; - nByte = - ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + - (eCurType==CURTYPE_BTREE?sqlcipher_sqlite3BtreeCursorSize():0); + /* OP_Init is always instruction 0 */ + assert( pOp==p->aOp || pOp->opcode==OP_Trace ); - assert( iCur>=0 && iCurnCursor ); - if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ - /* Before calling sqlcipher_sqlite3VdbeFreeCursor(), ensure the isEphemeral flag - ** is clear. Otherwise, if this is an ephemeral cursor created by - ** OP_OpenDup, the cursor will not be closed and will still be part - ** of a BtShared.pCursor list. */ - if( p->apCsr[iCur]->pBtx==0 ) p->apCsr[iCur]->isEphemeral = 0; - sqlcipher_sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); - p->apCsr[iCur] = 0; +#ifndef SQLITE_OMIT_TRACE + if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0 + && p->minWriteFileFormat!=254 /* tag-20220401a */ + && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 + ){ +#ifndef SQLITE_OMIT_DEPRECATED + if( db->mTrace & SQLITE_TRACE_LEGACY ){ + char *z = sqlcipher_sqlite3VdbeExpandSql(p, zTrace); + db->trace.xLegacy(db->pTraceArg, z); + sqlcipher_sqlite3_free(z); + }else +#endif + if( db->nVdbeExec>1 ){ + char *z = sqlcipher_sqlite3MPrintf(db, "-- %s", zTrace); + (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, z); + sqlcipher_sqlite3DbFree(db, z); + }else{ + (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace); + } } - if( SQLITE_OK==sqlcipher_sqlite3VdbeMemClearAndResize(pMem, nByte) ){ - p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; - memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); - pCx->eCurType = eCurType; - pCx->iDb = iDb; - pCx->nField = nField; - pCx->aOffset = &pCx->aType[nField]; - if( eCurType==CURTYPE_BTREE ){ - pCx->uc.pCursor = (BtCursor*) - &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; - sqlcipher_sqlite3BtreeCursorZero(pCx->uc.pCursor); +#ifdef SQLITE_USE_FCNTL_TRACE + zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); + if( zTrace ){ + int j; + for(j=0; jnDb; j++){ + if( DbMaskTest(p->btreeMask, j)==0 ) continue; + sqlcipher_sqlite3_file_control(db, db->aDb[j].zDbSName, SQLITE_FCNTL_TRACE, zTrace); } } - return pCx; +#endif /* SQLITE_USE_FCNTL_TRACE */ +#ifdef SQLITE_DEBUG + if( (db->flags & SQLITE_SqlTrace)!=0 + && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 + ){ + sqlcipher_sqlite3DebugPrintf("SQL-trace: %s\n", zTrace); + } +#endif /* SQLITE_DEBUG */ +#endif /* SQLITE_OMIT_TRACE */ + assert( pOp->p2>0 ); + if( pOp->p1>=sqlcipher_sqlite3GlobalConfig.iOnceResetThreshold ){ + if( pOp->opcode==OP_Trace ) break; + for(i=1; inOp; i++){ + if( p->aOp[i].opcode==OP_Once ) p->aOp[i].p1 = 0; + } + pOp->p1 = 0; + } + pOp->p1++; + p->aCounter[SQLITE_STMTSTATUS_RUN]++; + goto jump_to_p2; } -/* -** The string in pRec is known to look like an integer and to have a -** floating point value of rValue. Return true and set *piValue to the -** integer value if the string is in range to be an integer. Otherwise, -** return false. +#ifdef SQLITE_ENABLE_CURSOR_HINTS +/* Opcode: CursorHint P1 * * P4 * +** +** Provide a hint to cursor P1 that it only needs to return rows that +** satisfy the Expr in P4. TK_REGISTER terms in the P4 expression refer +** to values currently held in registers. TK_COLUMN terms in the P4 +** expression refer to columns in the b-tree to which cursor P1 is pointing. */ -static int alsoAnInt(Mem *pRec, double rValue, i64 *piValue){ - i64 iValue = (double)rValue; - if( sqlcipher_sqlite3RealSameAsInt(rValue,iValue) ){ - *piValue = iValue; - return 1; +case OP_CursorHint: { + VdbeCursor *pC; + + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p4type==P4_EXPR ); + pC = p->apCsr[pOp->p1]; + if( pC ){ + assert( pC->eCurType==CURTYPE_BTREE ); + sqlcipher_sqlite3BtreeCursorHint(pC->uc.pCursor, BTREE_HINT_RANGE, + pOp->p4.pExpr, aMem); } - return 0==sqlcipher_sqlite3Atoi64(pRec->z, piValue, pRec->n, pRec->enc); + break; } +#endif /* SQLITE_ENABLE_CURSOR_HINTS */ -/* -** Try to convert a value into a numeric representation if we can -** do so without loss of information. In other words, if the string -** looks like a number, convert it into a number. If it does not -** look like a number, leave it alone. +#ifdef SQLITE_DEBUG +/* Opcode: Abortable * * * * * ** -** If the bTryForInt flag is true, then extra effort is made to give -** an integer representation. Strings that look like floating point -** values but which have no fractional component (example: '48.00') -** will have a MEM_Int representation when bTryForInt is true. +** Verify that an Abort can happen. Assert if an Abort at this point +** might cause database corruption. This opcode only appears in debugging +** builds. ** -** If bTryForInt is false, then if the input string contains a decimal -** point or exponential notation, the result is only MEM_Real, even -** if there is an exact integer representation of the quantity. +** An Abort is safe if either there have been no writes, or if there is +** an active statement journal. */ -static void applyNumericAffinity(Mem *pRec, int bTryForInt){ - double rValue; - u8 enc = pRec->enc; - int rc; - assert( (pRec->flags & (MEM_Str|MEM_Int|MEM_Real|MEM_IntReal))==MEM_Str ); - rc = sqlcipher_sqlite3AtoF(pRec->z, &rValue, pRec->n, enc); - if( rc<=0 ) return; - if( rc==1 && alsoAnInt(pRec, rValue, &pRec->u.i) ){ - pRec->flags |= MEM_Int; - }else{ - pRec->u.r = rValue; - pRec->flags |= MEM_Real; - if( bTryForInt ) sqlcipher_sqlite3VdbeIntegerAffinity(pRec); - } - /* TEXT->NUMERIC is many->one. Hence, it is important to invalidate the - ** string representation after computing a numeric equivalent, because the - ** string representation might not be the canonical representation for the - ** numeric value. Ticket [343634942dd54ab57b7024] 2018-01-31. */ - pRec->flags &= ~MEM_Str; +case OP_Abortable: { + sqlcipher_sqlite3VdbeAssertAbortable(p); + break; } +#endif -/* -** Processing is determine by the affinity parameter: +#ifdef SQLITE_DEBUG +/* Opcode: ReleaseReg P1 P2 P3 * P5 +** Synopsis: release r[P1@P2] mask P3 ** -** SQLITE_AFF_INTEGER: -** SQLITE_AFF_REAL: -** SQLITE_AFF_NUMERIC: -** Try to convert pRec to an integer representation or a -** floating-point representation if an integer representation -** is not possible. Note that the integer representation is -** always preferred, even if the affinity is REAL, because -** an integer representation is more space efficient on disk. +** Release registers from service. Any content that was in the +** the registers is unreliable after this opcode completes. ** -** SQLITE_AFF_TEXT: -** Convert pRec to a text representation. +** The registers released will be the P2 registers starting at P1, +** except if bit ii of P3 set, then do not release register P1+ii. +** In other words, P3 is a mask of registers to preserve. ** -** SQLITE_AFF_BLOB: -** SQLITE_AFF_NONE: -** No-op. pRec is unchanged. +** Releasing a register clears the Mem.pScopyFrom pointer. That means +** that if the content of the released register was set using OP_SCopy, +** a change to the value of the source register for the OP_SCopy will no longer +** generate an assertion fault in sqlcipher_sqlite3VdbeMemAboutToChange(). +** +** If P5 is set, then all released registers have their type set +** to MEM_Undefined so that any subsequent attempt to read the released +** register (before it is reinitialized) will generate an assertion fault. +** +** P5 ought to be set on every call to this opcode. +** However, there are places in the code generator will release registers +** before their are used, under the (valid) assumption that the registers +** will not be reallocated for some other purpose before they are used and +** hence are safe to release. +** +** This opcode is only available in testing and debugging builds. It is +** not generated for release builds. The purpose of this opcode is to help +** validate the generated bytecode. This opcode does not actually contribute +** to computing an answer. */ -static void applyAffinity( - Mem *pRec, /* The value to apply affinity to */ - char affinity, /* The affinity to be applied */ - u8 enc /* Use this text encoding */ -){ - if( affinity>=SQLITE_AFF_NUMERIC ){ - assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL - || affinity==SQLITE_AFF_NUMERIC ); - if( (pRec->flags & MEM_Int)==0 ){ /*OPTIMIZATION-IF-FALSE*/ - if( (pRec->flags & MEM_Real)==0 ){ - if( pRec->flags & MEM_Str ) applyNumericAffinity(pRec,1); - }else{ - sqlcipher_sqlite3VdbeIntegerAffinity(pRec); - } - } - }else if( affinity==SQLITE_AFF_TEXT ){ - /* Only attempt the conversion to TEXT if there is an integer or real - ** representation (blob and NULL do not get converted) but no string - ** representation. It would be harmless to repeat the conversion if - ** there is already a string rep, but it is pointless to waste those - ** CPU cycles. */ - if( 0==(pRec->flags&MEM_Str) ){ /*OPTIMIZATION-IF-FALSE*/ - if( (pRec->flags&(MEM_Real|MEM_Int|MEM_IntReal)) ){ - testcase( pRec->flags & MEM_Int ); - testcase( pRec->flags & MEM_Real ); - testcase( pRec->flags & MEM_IntReal ); - sqlcipher_sqlite3VdbeMemStringify(pRec, enc, 1); - } +case OP_ReleaseReg: { + Mem *pMem; + int i; + u32 constMask; + assert( pOp->p1>0 ); + assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); + pMem = &aMem[pOp->p1]; + constMask = pOp->p3; + for(i=0; ip2; i++, pMem++){ + if( i>=32 || (constMask & MASKBIT32(i))==0 ){ + pMem->pScopyFrom = 0; + if( i<32 && pOp->p5 ) MemSetTypeFlag(pMem, MEM_Undefined); } - pRec->flags &= ~(MEM_Real|MEM_Int|MEM_IntReal); } + break; } +#endif -/* -** Try to convert the type of a function argument or a result column -** into a numeric representation. Use either INTEGER or REAL whichever -** is appropriate. But only do the conversion if it is possible without -** loss of information and return the revised type of the argument. +/* Opcode: Noop * * * * * +** +** Do nothing. This instruction is often useful as a jump +** destination. */ -SQLITE_API int sqlcipher_sqlite3_value_numeric_type(sqlcipher_sqlite3_value *pVal){ - int eType = sqlcipher_sqlite3_value_type(pVal); - if( eType==SQLITE_TEXT ){ - Mem *pMem = (Mem*)pVal; - applyNumericAffinity(pMem, 0); - eType = sqlcipher_sqlite3_value_type(pVal); - } - return eType; -} - /* -** Exported version of applyAffinity(). This one works on sqlcipher_sqlite3_value*, -** not the internal Mem* type. +** The magic Explain opcode are only inserted when explain==2 (which +** is to say when the EXPLAIN QUERY PLAN syntax is used.) +** This opcode records information from the optimizer. It is the +** the same as a no-op. This opcodesnever appears in a real VM program. */ -SQLITE_PRIVATE void sqlcipher_sqlite3ValueApplyAffinity( - sqlcipher_sqlite3_value *pVal, - u8 affinity, - u8 enc -){ - applyAffinity((Mem *)pVal, affinity, enc); +default: { /* This is really OP_Noop, OP_Explain */ + assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain ); + + break; } -/* -** pMem currently only holds a string type (or maybe a BLOB that we can -** interpret as a string if we want to). Compute its corresponding -** numeric type, if has one. Set the pMem->u.r and pMem->u.i fields -** accordingly. -*/ -static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ - int rc; - sqlcipher_sqlite3_int64 ix; - assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ); - assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ); - ExpandBlob(pMem); - rc = sqlcipher_sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); - if( rc<=0 ){ - if( rc==0 && sqlcipher_sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1 ){ - pMem->u.i = ix; - return MEM_Int; - }else{ - return MEM_Real; +/***************************************************************************** +** The cases of the switch statement above this line should all be indented +** by 6 spaces. But the left-most 6 spaces have been removed to improve the +** readability. From this point on down, the normal indentation rules are +** restored. +*****************************************************************************/ } - }else if( rc==1 && sqlcipher_sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)==0 ){ - pMem->u.i = ix; - return MEM_Int; - } - return MEM_Real; -} -/* -** Return the numeric type for pMem, either MEM_Int or MEM_Real or both or -** none. -** -** Unlike applyNumericAffinity(), this routine does not modify pMem->flags. -** But it does set pMem->u.r and pMem->u.i appropriately. -*/ -static u16 numericType(Mem *pMem){ - if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal) ){ - testcase( pMem->flags & MEM_Int ); - testcase( pMem->flags & MEM_Real ); - testcase( pMem->flags & MEM_IntReal ); - return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal); - } - if( pMem->flags & (MEM_Str|MEM_Blob) ){ - testcase( pMem->flags & MEM_Str ); - testcase( pMem->flags & MEM_Blob ); - return computeNumericType(pMem); - } - return 0; -} +#ifdef VDBE_PROFILE + { + u64 endTime = sqlcipher_sqlite3NProfileCnt ? sqlcipher_sqlite3NProfileCnt : sqlcipher_sqlite3Hwtime(); + if( endTime>start ) pOrigOp->cycles += endTime - start; + pOrigOp->cnt++; + } +#endif + + /* The following code adds nothing to the actual functionality + ** of the program. It is only here for testing and debugging. + ** On the other hand, it does burn CPU cycles every time through + ** the evaluator loop. So we can leave it out when NDEBUG is defined. + */ +#ifndef NDEBUG + assert( pOp>=&aOp[-1] && pOp<&aOp[p->nOp-1] ); #ifdef SQLITE_DEBUG -/* -** Write a nice string representation of the contents of cell pMem -** into buffer zBuf, length nBuf. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr){ - int f = pMem->flags; - static const char *const encnames[] = {"(X)", "(8)", "(16LE)", "(16BE)"}; - if( f&MEM_Blob ){ - int i; - char c; - if( f & MEM_Dyn ){ - c = 'z'; - assert( (f & (MEM_Static|MEM_Ephem))==0 ); - }else if( f & MEM_Static ){ - c = 't'; - assert( (f & (MEM_Dyn|MEM_Ephem))==0 ); - }else if( f & MEM_Ephem ){ - c = 'e'; - assert( (f & (MEM_Static|MEM_Dyn))==0 ); - }else{ - c = 's'; - } - sqlcipher_sqlite3_str_appendf(pStr, "%cx[", c); - for(i=0; i<25 && in; i++){ - sqlcipher_sqlite3_str_appendf(pStr, "%02X", ((int)pMem->z[i] & 0xFF)); - } - sqlcipher_sqlite3_str_appendf(pStr, "|"); - for(i=0; i<25 && in; i++){ - char z = pMem->z[i]; - sqlcipher_sqlite3_str_appendchar(pStr, 1, (z<32||z>126)?'.':z); - } - sqlcipher_sqlite3_str_appendf(pStr,"]"); - if( f & MEM_Zero ){ - sqlcipher_sqlite3_str_appendf(pStr, "+%dz",pMem->u.nZero); - } - }else if( f & MEM_Str ){ - int j; - u8 c; - if( f & MEM_Dyn ){ - c = 'z'; - assert( (f & (MEM_Static|MEM_Ephem))==0 ); - }else if( f & MEM_Static ){ - c = 't'; - assert( (f & (MEM_Dyn|MEM_Ephem))==0 ); - }else if( f & MEM_Ephem ){ - c = 'e'; - assert( (f & (MEM_Static|MEM_Dyn))==0 ); - }else{ - c = 's'; - } - sqlcipher_sqlite3_str_appendf(pStr, " %c%d[", c, pMem->n); - for(j=0; j<25 && jn; j++){ - c = pMem->z[j]; - sqlcipher_sqlite3_str_appendchar(pStr, 1, (c>=0x20&&c<=0x7f) ? c : '.'); + if( db->flags & SQLITE_VdbeTrace ){ + u8 opProperty = sqlcipher_sqlite3OpcodeProperty[pOrigOp->opcode]; + if( rc!=0 ) printf("rc=%d\n",rc); + if( opProperty & (OPFLG_OUT2) ){ + registerTrace(pOrigOp->p2, &aMem[pOrigOp->p2]); + } + if( opProperty & OPFLG_OUT3 ){ + registerTrace(pOrigOp->p3, &aMem[pOrigOp->p3]); + } + if( opProperty==0xff ){ + /* Never happens. This code exists to avoid a harmless linkage + ** warning aboud sqlcipher_sqlite3VdbeRegisterDump() being defined but not + ** used. */ + sqlcipher_sqlite3VdbeRegisterDump(p); + } } - sqlcipher_sqlite3_str_appendf(pStr, "]%s", encnames[pMem->enc]); +#endif /* SQLITE_DEBUG */ +#endif /* NDEBUG */ + } /* The end of the for(;;) loop the loops through opcodes */ + + /* If we reach this point, it means that execution is finished with + ** an error of some kind. + */ +abort_due_to_error: + if( db->mallocFailed ){ + rc = SQLITE_NOMEM_BKPT; + }else if( rc==SQLITE_IOERR_CORRUPTFS ){ + rc = SQLITE_CORRUPT_BKPT; } -} -#endif - + assert( rc ); #ifdef SQLITE_DEBUG -/* -** Print the value of a register for tracing purposes: -*/ -static void memTracePrint(Mem *p){ - if( p->flags & MEM_Undefined ){ - printf(" undefined"); - }else if( p->flags & MEM_Null ){ - printf(p->flags & MEM_Zero ? " NULL-nochng" : " NULL"); - }else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){ - printf(" si:%lld", p->u.i); - }else if( (p->flags & (MEM_IntReal))!=0 ){ - printf(" ir:%lld", p->u.i); - }else if( p->flags & MEM_Int ){ - printf(" i:%lld", p->u.i); -#ifndef SQLITE_OMIT_FLOATING_POINT - }else if( p->flags & MEM_Real ){ - printf(" r:%.17g", p->u.r); + if( db->flags & SQLITE_VdbeTrace ){ + const char *zTrace = p->zSql; + if( zTrace==0 ){ + if( aOp[0].opcode==OP_Trace ){ + zTrace = aOp[0].p4.z; + } + if( zTrace==0 ) zTrace = "???"; + } + printf("ABORT-due-to-error (rc=%d): %s\n", rc, zTrace); + } #endif - }else if( sqlcipher_sqlite3VdbeMemIsRowSet(p) ){ - printf(" (rowset)"); - }else{ - StrAccum acc; - char zBuf[1000]; - sqlcipher_sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); - sqlcipher_sqlite3VdbeMemPrettyPrint(p, &acc); - printf(" %s", sqlcipher_sqlite3StrAccumFinish(&acc)); + if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ + sqlcipher_sqlite3VdbeError(p, "%s", sqlcipher_sqlite3ErrStr(rc)); } - if( p->flags & MEM_Subtype ) printf(" subtype=0x%02x", p->eSubtype); -} -static void registerTrace(int iReg, Mem *p){ - printf("R[%d] = ", iReg); - memTracePrint(p); - if( p->pScopyFrom ){ - printf(" <== R[%d]", (int)(p->pScopyFrom - &p[-iReg])); + p->rc = rc; + sqlcipher_sqlite3SystemError(db, rc); + testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); + sqlcipher_sqlite3_log(rc, "statement aborts at %d: [%s] %s", + (int)(pOp - aOp), p->zSql, p->zErrMsg); + if( p->eVdbeState==VDBE_RUN_STATE ) sqlcipher_sqlite3VdbeHalt(p); + if( rc==SQLITE_IOERR_NOMEM ) sqlcipher_sqlite3OomFault(db); + if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){ + db->flags |= SQLITE_CorruptRdOnly; + } + rc = SQLITE_ERROR; + if( resetSchemaOnFault>0 ){ + sqlcipher_sqlite3ResetOneSchema(db, resetSchemaOnFault-1); } - printf("\n"); - sqlcipher_sqlite3VdbeCheckMemInvariants(p); -} -#endif -#ifdef SQLITE_DEBUG -/* -** Show the values of all registers in the virtual machine. Used for -** interactive debugging. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeRegisterDump(Vdbe *v){ - int i; - for(i=1; inMem; i++) registerTrace(i, v->aMem+i); -} -#endif /* SQLITE_DEBUG */ + /* This is the only way out of this procedure. We have to + ** release the mutexes on btrees that were acquired at the + ** top. */ +vdbe_return: +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ + nProgressLimit += db->nProgressOps; + if( db->xProgress(db->pProgressArg) ){ + nProgressLimit = LARGEST_UINT64; + rc = SQLITE_INTERRUPT; + goto abort_due_to_error; + } + } +#endif + p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep; + sqlcipher_sqlite3VdbeLeave(p); + assert( rc!=SQLITE_OK || nExtraDelete==0 + || sqlcipher_sqlite3_strlike("DELETE%",p->zSql,0)!=0 + ); + return rc; + /* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH + ** is encountered. + */ +too_big: + sqlcipher_sqlite3VdbeError(p, "string or blob too big"); + rc = SQLITE_TOOBIG; + goto abort_due_to_error; -#ifdef SQLITE_DEBUG -# define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M) -#else -# define REGISTER_TRACE(R,M) -#endif + /* Jump to here if a malloc() fails. + */ +no_mem: + sqlcipher_sqlite3OomFault(db); + sqlcipher_sqlite3VdbeError(p, "out of memory"); + rc = SQLITE_NOMEM_BKPT; + goto abort_due_to_error; + /* Jump to here if the sqlcipher_sqlite3_interrupt() API sets the interrupt + ** flag. + */ +abort_due_to_interrupt: + assert( AtomicLoad(&db->u1.isInterrupted) ); + rc = SQLITE_INTERRUPT; + goto abort_due_to_error; +} -#ifdef VDBE_PROFILE +/************** End of vdbe.c ************************************************/ +/************** Begin file vdbeblob.c ****************************************/ /* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of vdbe.c *********************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 +** 2007 May 1 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -90817,11855 +97473,8983 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VdbeRegisterDump(Vdbe *v){ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** -****************************************************************************** +************************************************************************* ** -** This file contains inline asm code for retrieving "high-performance" -** counters for x86 and x86_64 class CPUs. +** This file contains code used to implement incremental BLOB I/O. */ -#ifndef SQLITE_HWTIME_H -#define SQLITE_HWTIME_H + +/* #include "sqliteInt.h" */ +/* #include "vdbeInt.h" */ + +#ifndef SQLITE_OMIT_INCRBLOB /* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. +** Valid sqlcipher_sqlite3_blob* handles point to Incrblob structures. */ -#if !defined(__STRICT_ANSI__) && \ - (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) +typedef struct Incrblob Incrblob; +struct Incrblob { + int nByte; /* Size of open blob, in bytes */ + int iOffset; /* Byte offset of blob in cursor data */ + u16 iCol; /* Table column this handle is open on */ + BtCursor *pCsr; /* Cursor pointing at blob row */ + sqlcipher_sqlite3_stmt *pStmt; /* Statement holding cursor open */ + sqlcipher_sqlite3 *db; /* The associated database */ + char *zDb; /* Database name */ + Table *pTab; /* Table object */ +}; - #if defined(__GNUC__) - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } +/* +** This function is used by both blob_open() and blob_reopen(). It seeks +** the b-tree cursor associated with blob handle p to point to row iRow. +** If successful, SQLITE_OK is returned and subsequent calls to +** sqlcipher_sqlite3_blob_read() or sqlcipher_sqlite3_blob_write() access the specified row. +** +** If an error occurs, or if the specified row does not exist or does not +** contain a value of type TEXT or BLOB in the column nominated when the +** blob handle was opened, then an error code is returned and *pzErr may +** be set to point to a buffer containing an error message. It is the +** responsibility of the caller to free the error message buffer using +** sqlcipher_sqlite3DbFree(). +** +** If an error does occur, then the b-tree cursor is closed. All subsequent +** calls to sqlcipher_sqlite3_blob_read(), blob_write() or blob_reopen() will +** immediately return SQLITE_ABORT. +*/ +static int blobSeekToRow(Incrblob *p, sqlcipher_sqlite3_int64 iRow, char **pzErr){ + int rc; /* Error code */ + char *zErr = 0; /* Error message */ + Vdbe *v = (Vdbe *)p->pStmt; - #elif defined(_MSC_VER) + /* Set the value of register r[1] in the SQL statement to integer iRow. + ** This is done directly as a performance optimization + */ + v->aMem[1].flags = MEM_Int; + v->aMem[1].u.i = iRow; - __declspec(naked) __inline sqlite_uint64 __cdecl sqlcipher_sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } + /* If the statement has been run before (and is paused at the OP_ResultRow) + ** then back it up to the point where it does the OP_NotExists. This could + ** have been down with an extra OP_Goto, but simply setting the program + ** counter is faster. */ + if( v->pc>4 ){ + v->pc = 4; + assert( v->aOp[v->pc].opcode==OP_NotExists ); + rc = sqlcipher_sqlite3VdbeExec(v); + }else{ + rc = sqlcipher_sqlite3_step(p->pStmt); + } + if( rc==SQLITE_ROW ){ + VdbeCursor *pC = v->apCsr[0]; + u32 type; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0; + testcase( pC->nHdrParsed==p->iCol ); + testcase( pC->nHdrParsed==p->iCol+1 ); + if( type<12 ){ + zErr = sqlcipher_sqlite3MPrintf(p->db, "cannot open value of type %s", + type==0?"null": type==7?"real": "integer" + ); + rc = SQLITE_ERROR; + sqlcipher_sqlite3_finalize(p->pStmt); + p->pStmt = 0; + }else{ + p->iOffset = pC->aType[p->iCol + pC->nField]; + p->nByte = sqlcipher_sqlite3VdbeSerialTypeLen(type); + p->pCsr = pC->uc.pCursor; + sqlcipher_sqlite3BtreeIncrblobCursor(p->pCsr); + } } - #endif + if( rc==SQLITE_ROW ){ + rc = SQLITE_OK; + }else if( p->pStmt ){ + rc = sqlcipher_sqlite3_finalize(p->pStmt); + p->pStmt = 0; + if( rc==SQLITE_OK ){ + zErr = sqlcipher_sqlite3MPrintf(p->db, "no such rowid: %lld", iRow); + rc = SQLITE_ERROR; + }else{ + zErr = sqlcipher_sqlite3MPrintf(p->db, "%s", sqlcipher_sqlite3_errmsg(p->db)); + } + } -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) + assert( rc!=SQLITE_OK || zErr==0 ); + assert( rc!=SQLITE_ROW && rc!=SQLITE_DONE ); - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; - } + *pzErr = zErr; + return rc; +} -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) +/* +** Open a blob handle. +*/ +SQLITE_API int sqlcipher_sqlite3_blob_open( + sqlcipher_sqlite3* db, /* The database connection */ + const char *zDb, /* The attached database containing the blob */ + const char *zTable, /* The table containing the blob */ + const char *zColumn, /* The column containing the blob */ + sqlite_int64 iRow, /* The row containing the glob */ + int wrFlag, /* True -> read/write access, false -> read-only */ + sqlcipher_sqlite3_blob **ppBlob /* Handle for accessing the blob returned here */ +){ + int nAttempt = 0; + int iCol; /* Index of zColumn in row-record */ + int rc = SQLITE_OK; + char *zErr = 0; + Table *pTab; + Incrblob *pBlob = 0; + Parse sParse; - __inline__ sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; +#ifdef SQLITE_ENABLE_API_ARMOR + if( ppBlob==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + *ppBlob = 0; +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlcipher_sqlite3SafetyCheckOk(db) || zTable==0 ){ + return SQLITE_MISUSE_BKPT; } +#endif + wrFlag = !!wrFlag; /* wrFlag = (wrFlag ? 1 : 0); */ -#else + sqlcipher_sqlite3_mutex_enter(db->mutex); - /* - ** asm() is needed for hardware timing support. Without asm(), - ** disable the sqlcipher_sqlite3Hwtime() routine. - ** - ** sqlcipher_sqlite3Hwtime() is only used for some obscure debugging - ** and analysis configurations, not in any deliverable, so this - ** should not be a great loss. - */ -SQLITE_PRIVATE sqlite_uint64 sqlcipher_sqlite3Hwtime(void){ return ((sqlite_uint64)0); } + pBlob = (Incrblob *)sqlcipher_sqlite3DbMallocZero(db, sizeof(Incrblob)); + while(1){ + sqlcipher_sqlite3ParseObjectInit(&sParse,db); + if( !pBlob ) goto blob_open_out; + sqlcipher_sqlite3DbFree(db, zErr); + zErr = 0; + sqlcipher_sqlite3BtreeEnterAll(db); + pTab = sqlcipher_sqlite3LocateTable(&sParse, 0, zTable, zDb); + if( pTab && IsVirtual(pTab) ){ + pTab = 0; + sqlcipher_sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable); + } + if( pTab && !HasRowid(pTab) ){ + pTab = 0; + sqlcipher_sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable); + } +#ifndef SQLITE_OMIT_VIEW + if( pTab && IsView(pTab) ){ + pTab = 0; + sqlcipher_sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); + } #endif + if( !pTab ){ + if( sParse.zErrMsg ){ + sqlcipher_sqlite3DbFree(db, zErr); + zErr = sParse.zErrMsg; + sParse.zErrMsg = 0; + } + rc = SQLITE_ERROR; + sqlcipher_sqlite3BtreeLeaveAll(db); + goto blob_open_out; + } + pBlob->pTab = pTab; + pBlob->zDb = db->aDb[sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; -#endif /* !defined(SQLITE_HWTIME_H) */ + /* Now search pTab for the exact column. */ + for(iCol=0; iColnCol; iCol++) { + if( sqlcipher_sqlite3StrICmp(pTab->aCol[iCol].zCnName, zColumn)==0 ){ + break; + } + } + if( iCol==pTab->nCol ){ + sqlcipher_sqlite3DbFree(db, zErr); + zErr = sqlcipher_sqlite3MPrintf(db, "no such column: \"%s\"", zColumn); + rc = SQLITE_ERROR; + sqlcipher_sqlite3BtreeLeaveAll(db); + goto blob_open_out; + } -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in vdbe.c ***********************/ + /* If the value is being opened for writing, check that the + ** column is not indexed, and that it is not part of a foreign key. + */ + if( wrFlag ){ + const char *zFault = 0; + Index *pIdx; +#ifndef SQLITE_OMIT_FOREIGN_KEY + if( db->flags&SQLITE_ForeignKeys ){ + /* Check that the column is not part of an FK child key definition. It + ** is not necessary to check if it is part of a parent key, as parent + ** key columns must be indexed. The check below will pick up this + ** case. */ + FKey *pFKey; + assert( IsOrdinaryTable(pTab) ); + for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ + int j; + for(j=0; jnCol; j++){ + if( pFKey->aCol[j].iFrom==iCol ){ + zFault = "foreign key"; + } + } + } + } +#endif + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + int j; + for(j=0; jnKeyCol; j++){ + /* FIXME: Be smarter about indexes that use expressions */ + if( pIdx->aiColumn[j]==iCol || pIdx->aiColumn[j]==XN_EXPR ){ + zFault = "indexed"; + } + } + } + if( zFault ){ + sqlcipher_sqlite3DbFree(db, zErr); + zErr = sqlcipher_sqlite3MPrintf(db, "cannot open %s column for writing", zFault); + rc = SQLITE_ERROR; + sqlcipher_sqlite3BtreeLeaveAll(db); + goto blob_open_out; + } + } + + pBlob->pStmt = (sqlcipher_sqlite3_stmt *)sqlcipher_sqlite3VdbeCreate(&sParse); + assert( pBlob->pStmt || db->mallocFailed ); + if( pBlob->pStmt ){ + + /* This VDBE program seeks a btree cursor to the identified + ** db/table/row entry. The reason for using a vdbe program instead + ** of writing code to use the b-tree layer directly is that the + ** vdbe program will take advantage of the various transaction, + ** locking and error handling infrastructure built into the vdbe. + ** + ** After seeking the cursor, the vdbe executes an OP_ResultRow. + ** Code external to the Vdbe then "borrows" the b-tree cursor and + ** uses it to implement the blob_read(), blob_write() and + ** blob_bytes() functions. + ** + ** The sqlcipher_sqlite3_blob_close() function finalizes the vdbe program, + ** which closes the b-tree cursor and (possibly) commits the + ** transaction. + */ + static const int iLn = VDBE_OFFSET_LINENO(2); + static const VdbeOpList openBlob[] = { + {OP_TableLock, 0, 0, 0}, /* 0: Acquire a read or write lock */ + {OP_OpenRead, 0, 0, 0}, /* 1: Open a cursor */ + /* blobSeekToRow() will initialize r[1] to the desired rowid */ + {OP_NotExists, 0, 5, 1}, /* 2: Seek the cursor to rowid=r[1] */ + {OP_Column, 0, 0, 1}, /* 3 */ + {OP_ResultRow, 1, 0, 0}, /* 4 */ + {OP_Halt, 0, 0, 0}, /* 5 */ + }; + Vdbe *v = (Vdbe *)pBlob->pStmt; + int iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); + VdbeOp *aOp; + + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, + pTab->pSchema->schema_cookie, + pTab->pSchema->iGeneration); + sqlcipher_sqlite3VdbeChangeP5(v, 1); + assert( sqlcipher_sqlite3VdbeCurrentAddr(v)==2 || db->mallocFailed ); + aOp = sqlcipher_sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn); + + /* Make sure a mutex is held on the table to be accessed */ + sqlcipher_sqlite3VdbeUsesBtree(v, iDb); + if( db->mallocFailed==0 ){ + assert( aOp!=0 ); + /* Configure the OP_TableLock instruction */ +#ifdef SQLITE_OMIT_SHARED_CACHE + aOp[0].opcode = OP_Noop; +#else + aOp[0].p1 = iDb; + aOp[0].p2 = pTab->tnum; + aOp[0].p3 = wrFlag; + sqlcipher_sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); + } + if( db->mallocFailed==0 ){ #endif -#ifndef NDEBUG -/* -** This function is only called from within an assert() expression. It -** checks that the sqlcipher_sqlite3.nTransaction variable is correctly set to -** the number of non-transaction savepoints currently in the -** linked list starting at sqlcipher_sqlite3.pSavepoint. -** -** Usage: -** -** assert( checkSavepointCount(db) ); -*/ -static int checkSavepointCount(sqlcipher_sqlite3 *db){ - int n = 0; - Savepoint *p; - for(p=db->pSavepoint; p; p=p->pNext) n++; - assert( n==(db->nSavepoint + db->isTransactionSavepoint) ); - return 1; + /* Remove either the OP_OpenWrite or OpenRead. Set the P2 + ** parameter of the other to pTab->tnum. */ + if( wrFlag ) aOp[1].opcode = OP_OpenWrite; + aOp[1].p2 = pTab->tnum; + aOp[1].p3 = iDb; + + /* Configure the number of columns. Configure the cursor to + ** think that the table has one more column than it really + ** does. An OP_Column to retrieve this imaginary column will + ** always return an SQL NULL. This is useful because it means + ** we can invoke OP_Column to fill in the vdbe cursors type + ** and offset cache without causing any IO. + */ + aOp[1].p4type = P4_INT32; + aOp[1].p4.i = pTab->nCol+1; + aOp[3].p2 = pTab->nCol; + + sParse.nVar = 0; + sParse.nMem = 1; + sParse.nTab = 1; + sqlcipher_sqlite3VdbeMakeReady(v, &sParse); + } + } + + pBlob->iCol = iCol; + pBlob->db = db; + sqlcipher_sqlite3BtreeLeaveAll(db); + if( db->mallocFailed ){ + goto blob_open_out; + } + rc = blobSeekToRow(pBlob, iRow, &zErr); + if( (++nAttempt)>=SQLITE_MAX_SCHEMA_RETRY || rc!=SQLITE_SCHEMA ) break; + sqlcipher_sqlite3ParseObjectReset(&sParse); + } + +blob_open_out: + if( rc==SQLITE_OK && db->mallocFailed==0 ){ + *ppBlob = (sqlcipher_sqlite3_blob *)pBlob; + }else{ + if( pBlob && pBlob->pStmt ) sqlcipher_sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt); + sqlcipher_sqlite3DbFree(db, pBlob); + } + sqlcipher_sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); + sqlcipher_sqlite3DbFree(db, zErr); + sqlcipher_sqlite3ParseObjectReset(&sParse); + rc = sqlcipher_sqlite3ApiExit(db, rc); + sqlcipher_sqlite3_mutex_leave(db->mutex); + return rc; } -#endif /* -** Return the register of pOp->p2 after first preparing it to be -** overwritten with an integer value. +** Close a blob handle that was previously created using +** sqlcipher_sqlite3_blob_open(). */ -static SQLITE_NOINLINE Mem *out2PrereleaseWithClear(Mem *pOut){ - sqlcipher_sqlite3VdbeMemSetNull(pOut); - pOut->flags = MEM_Int; - return pOut; -} -static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){ - Mem *pOut; - assert( pOp->p2>0 ); - assert( pOp->p2<=(p->nMem+1 - p->nCursor) ); - pOut = &p->aMem[pOp->p2]; - memAboutToChange(p, pOut); - if( VdbeMemDynamic(pOut) ){ /*OPTIMIZATION-IF-FALSE*/ - return out2PrereleaseWithClear(pOut); +SQLITE_API int sqlcipher_sqlite3_blob_close(sqlcipher_sqlite3_blob *pBlob){ + Incrblob *p = (Incrblob *)pBlob; + int rc; + sqlcipher_sqlite3 *db; + + if( p ){ + sqlcipher_sqlite3_stmt *pStmt = p->pStmt; + db = p->db; + sqlcipher_sqlite3_mutex_enter(db->mutex); + sqlcipher_sqlite3DbFree(db, p); + sqlcipher_sqlite3_mutex_leave(db->mutex); + rc = sqlcipher_sqlite3_finalize(pStmt); }else{ - pOut->flags = MEM_Int; - return pOut; + rc = SQLITE_OK; } + return rc; } - /* -** Execute as much of a VDBE program as we can. -** This is the core of sqlcipher_sqlite3_step(). +** Perform a read or write operation on a blob */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeExec( - Vdbe *p /* The VDBE */ +static int blobReadWrite( + sqlcipher_sqlite3_blob *pBlob, + void *z, + int n, + int iOffset, + int (*xCall)(BtCursor*, u32, u32, void*) ){ - Op *aOp = p->aOp; /* Copy of p->aOp */ - Op *pOp = aOp; /* Current operation */ -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) - Op *pOrigOp; /* Value of pOp at the top of the loop */ -#endif -#ifdef SQLITE_DEBUG - int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */ -#endif - int rc = SQLITE_OK; /* Value to return */ - sqlcipher_sqlite3 *db = p->db; /* The database */ - u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ - u8 encoding = ENC(db); /* The database encoding */ - int iCompare = 0; /* Result of last comparison */ - u64 nVmStep = 0; /* Number of virtual machine steps */ -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - u64 nProgressLimit; /* Invoke xProgress() when nVmStep reaches this */ -#endif - Mem *aMem = p->aMem; /* Copy of p->aMem */ - Mem *pIn1 = 0; /* 1st input operand */ - Mem *pIn2 = 0; /* 2nd input operand */ - Mem *pIn3 = 0; /* 3rd input operand */ - Mem *pOut = 0; /* Output operand */ -#ifdef VDBE_PROFILE - u64 start; /* CPU clock count at start of opcode */ -#endif - /*** INSERT STACK UNION HERE ***/ + int rc; + Incrblob *p = (Incrblob *)pBlob; + Vdbe *v; + sqlcipher_sqlite3 *db; - assert( p->magic==VDBE_MAGIC_RUN ); /* sqlcipher_sqlite3_step() verifies this */ - sqlcipher_sqlite3VdbeEnter(p); -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - if( db->xProgress ){ - u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP]; - assert( 0 < db->nProgressOps ); - nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps); + if( p==0 ) return SQLITE_MISUSE_BKPT; + db = p->db; + sqlcipher_sqlite3_mutex_enter(db->mutex); + v = (Vdbe*)p->pStmt; + + if( n<0 || iOffset<0 || ((sqlcipher_sqlite3_int64)iOffset+n)>p->nByte ){ + /* Request is out of range. Return a transient error. */ + rc = SQLITE_ERROR; + }else if( v==0 ){ + /* If there is no statement handle, then the blob-handle has + ** already been invalidated. Return SQLITE_ABORT in this case. + */ + rc = SQLITE_ABORT; }else{ - nProgressLimit = LARGEST_UINT64; - } -#endif - if( p->rc==SQLITE_NOMEM ){ - /* This happens if a malloc() inside a call to sqlcipher_sqlite3_column_text() or - ** sqlcipher_sqlite3_column_text16() failed. */ - goto no_mem; - } - assert( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_BUSY ); - testcase( p->rc!=SQLITE_OK ); - p->rc = SQLITE_OK; - assert( p->bIsReader || p->readOnly!=0 ); - p->iCurrentTime = 0; - assert( p->explain==0 ); - p->pResultSet = 0; - db->busyHandler.nBusy = 0; - if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; - sqlcipher_sqlite3VdbeIOTraceSql(p); -#ifdef SQLITE_DEBUG - sqlcipher_sqlite3BeginBenignMalloc(); - if( p->pc==0 - && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0 - ){ - int i; - int once = 1; - sqlcipher_sqlite3VdbePrintSql(p); - if( p->db->flags & SQLITE_VdbeListing ){ - printf("VDBE Program Listing:\n"); - for(i=0; inOp; i++){ - sqlcipher_sqlite3VdbePrintOp(stdout, i, &aOp[i]); - } + /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is + ** returned, clean-up the statement handle. + */ + assert( db == v->db ); + sqlcipher_sqlite3BtreeEnterCursor(p->pCsr); + +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + if( xCall==sqlcipher_sqlite3BtreePutData && db->xPreUpdateCallback ){ + /* If a pre-update hook is registered and this is a write cursor, + ** invoke it here. + ** + ** TODO: The preupdate-hook is passed SQLITE_DELETE, even though this + ** operation should really be an SQLITE_UPDATE. This is probably + ** incorrect, but is convenient because at this point the new.* values + ** are not easily obtainable. And for the sessions module, an + ** SQLITE_UPDATE where the PK columns do not change is handled in the + ** same way as an SQLITE_DELETE (the SQLITE_DELETE code is actually + ** slightly more efficient). Since you cannot write to a PK column + ** using the incremental-blob API, this works. For the sessions module + ** anyhow. + */ + sqlcipher_sqlite3_int64 iKey; + iKey = sqlcipher_sqlite3BtreeIntegerKey(p->pCsr); + assert( v->apCsr[0]!=0 ); + assert( v->apCsr[0]->eCurType==CURTYPE_BTREE ); + sqlcipher_sqlite3VdbePreUpdateHook( + v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol + ); } - if( p->db->flags & SQLITE_VdbeEQP ){ - for(i=0; inOp; i++){ - if( aOp[i].opcode==OP_Explain ){ - if( once ) printf("VDBE Query Plan:\n"); - printf("%s\n", aOp[i].p4.z); - once = 0; - } - } +#endif + + rc = xCall(p->pCsr, iOffset+p->iOffset, n, z); + sqlcipher_sqlite3BtreeLeaveCursor(p->pCsr); + if( rc==SQLITE_ABORT ){ + sqlcipher_sqlite3VdbeFinalize(v); + p->pStmt = 0; + }else{ + v->rc = rc; } - if( p->db->flags & SQLITE_VdbeTrace ) printf("VDBE Trace:\n"); } - sqlcipher_sqlite3EndBenignMalloc(); -#endif - for(pOp=&aOp[p->pc]; 1; pOp++){ - /* Errors are detected by individual opcodes, with an immediate - ** jumps to abort_due_to_error. */ - assert( rc==SQLITE_OK ); + sqlcipher_sqlite3Error(db, rc); + rc = sqlcipher_sqlite3ApiExit(db, rc); + sqlcipher_sqlite3_mutex_leave(db->mutex); + return rc; +} - assert( pOp>=aOp && pOp<&aOp[p->nOp]); -#ifdef VDBE_PROFILE - start = sqlcipher_sqlite3NProfileCnt ? sqlcipher_sqlite3NProfileCnt : sqlcipher_sqlite3Hwtime(); -#endif - nVmStep++; -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - if( p->anExec ) p->anExec[(int)(pOp-aOp)]++; -#endif +/* +** Read data from a blob handle. +*/ +SQLITE_API int sqlcipher_sqlite3_blob_read(sqlcipher_sqlite3_blob *pBlob, void *z, int n, int iOffset){ + return blobReadWrite(pBlob, z, n, iOffset, sqlcipher_sqlite3BtreePayloadChecked); +} - /* Only allow tracing if SQLITE_DEBUG is defined. - */ -#ifdef SQLITE_DEBUG - if( db->flags & SQLITE_VdbeTrace ){ - sqlcipher_sqlite3VdbePrintOp(stdout, (int)(pOp - aOp), pOp); - test_trace_breakpoint((int)(pOp - aOp),pOp,p); - } -#endif +/* +** Write data to a blob handle. +*/ +SQLITE_API int sqlcipher_sqlite3_blob_write(sqlcipher_sqlite3_blob *pBlob, const void *z, int n, int iOffset){ + return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlcipher_sqlite3BtreePutData); +} +/* +** Query a blob handle for the size of the data. +** +** The Incrblob.nByte field is fixed for the lifetime of the Incrblob +** so no mutex is required for access. +*/ +SQLITE_API int sqlcipher_sqlite3_blob_bytes(sqlcipher_sqlite3_blob *pBlob){ + Incrblob *p = (Incrblob *)pBlob; + return (p && p->pStmt) ? p->nByte : 0; +} - /* Check to see if we need to simulate an interrupt. This only happens - ** if we have a special test build. +/* +** Move an existing blob handle to point to a different row of the same +** database table. +** +** If an error occurs, or if the specified row does not exist or does not +** contain a blob or text value, then an error code is returned and the +** database handle error code and message set. If this happens, then all +** subsequent calls to sqlcipher_sqlite3_blob_xxx() functions (except blob_close()) +** immediately return SQLITE_ABORT. +*/ +SQLITE_API int sqlcipher_sqlite3_blob_reopen(sqlcipher_sqlite3_blob *pBlob, sqlcipher_sqlite3_int64 iRow){ + int rc; + Incrblob *p = (Incrblob *)pBlob; + sqlcipher_sqlite3 *db; + + if( p==0 ) return SQLITE_MISUSE_BKPT; + db = p->db; + sqlcipher_sqlite3_mutex_enter(db->mutex); + + if( p->pStmt==0 ){ + /* If there is no statement handle, then the blob-handle has + ** already been invalidated. Return SQLITE_ABORT in this case. */ -#ifdef SQLITE_TEST - if( sqlcipher_sqlite3_interrupt_count>0 ){ - sqlcipher_sqlite3_interrupt_count--; - if( sqlcipher_sqlite3_interrupt_count==0 ){ - sqlcipher_sqlite3_interrupt(db); - } + rc = SQLITE_ABORT; + }else{ + char *zErr; + ((Vdbe*)p->pStmt)->rc = SQLITE_OK; + rc = blobSeekToRow(p, iRow, &zErr); + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); + sqlcipher_sqlite3DbFree(db, zErr); } -#endif + assert( rc!=SQLITE_SCHEMA ); + } - /* Sanity checking on other operands */ -#ifdef SQLITE_DEBUG - { - u8 opProperty = sqlcipher_sqlite3OpcodeProperty[pOp->opcode]; - if( (opProperty & OPFLG_IN1)!=0 ){ - assert( pOp->p1>0 ); - assert( pOp->p1<=(p->nMem+1 - p->nCursor) ); - assert( memIsValid(&aMem[pOp->p1]) ); - assert( sqlcipher_sqlite3VdbeCheckMemInvariants(&aMem[pOp->p1]) ); - REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]); - } - if( (opProperty & OPFLG_IN2)!=0 ){ - assert( pOp->p2>0 ); - assert( pOp->p2<=(p->nMem+1 - p->nCursor) ); - assert( memIsValid(&aMem[pOp->p2]) ); - assert( sqlcipher_sqlite3VdbeCheckMemInvariants(&aMem[pOp->p2]) ); - REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]); - } - if( (opProperty & OPFLG_IN3)!=0 ){ - assert( pOp->p3>0 ); - assert( pOp->p3<=(p->nMem+1 - p->nCursor) ); - assert( memIsValid(&aMem[pOp->p3]) ); - assert( sqlcipher_sqlite3VdbeCheckMemInvariants(&aMem[pOp->p3]) ); - REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]); - } - if( (opProperty & OPFLG_OUT2)!=0 ){ - assert( pOp->p2>0 ); - assert( pOp->p2<=(p->nMem+1 - p->nCursor) ); - memAboutToChange(p, &aMem[pOp->p2]); - } - if( (opProperty & OPFLG_OUT3)!=0 ){ - assert( pOp->p3>0 ); - assert( pOp->p3<=(p->nMem+1 - p->nCursor) ); - memAboutToChange(p, &aMem[pOp->p3]); - } - } -#endif -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) - pOrigOp = pOp; -#endif + rc = sqlcipher_sqlite3ApiExit(db, rc); + assert( rc==SQLITE_OK || p->pStmt==0 ); + sqlcipher_sqlite3_mutex_leave(db->mutex); + return rc; +} - switch( pOp->opcode ){ +#endif /* #ifndef SQLITE_OMIT_INCRBLOB */ -/***************************************************************************** -** What follows is a massive switch statement where each case implements a -** separate instruction in the virtual machine. If we follow the usual -** indentation conventions, each case should be indented by 6 spaces. But -** that is a lot of wasted space on the left margin. So the code within -** the switch statement will break with convention and be flush-left. Another -** big comment (similar to this one) will mark the point in the code where -** we transition back to normal indentation. +/************** End of vdbeblob.c ********************************************/ +/************** Begin file vdbesort.c ****************************************/ +/* +** 2011-07-09 ** -** The formatting of each case is important. The makefile for SQLite -** generates two C files "opcodes.h" and "opcodes.c" by scanning this -** file looking for lines that begin with "case OP_". The opcodes.h files -** will be filled with #defines that give unique integer values to each -** opcode and the opcodes.c file is filled with an array of strings where -** each string is the symbolic name for the corresponding opcode. If the -** case statement is followed by a comment of the form "/# same as ... #/" -** that comment is used to determine the particular value of the opcode. +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** Other keywords in the comment that follows each case are used to -** construct the OPFLG_INITIALIZER value that initializes opcodeProperty[]. -** Keywords include: in1, in2, in3, out2, out3. See -** the mkopcodeh.awk script for additional information. +** 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. ** -** Documentation about VDBE opcodes is generated by scanning this file -** for lines of that contain "Opcode:". That line and all subsequent -** comment lines are used in the generation of the opcode.html documentation -** file. +************************************************************************* +** This file contains code for the VdbeSorter object, used in concert with +** a VdbeCursor to sort large numbers of keys for CREATE INDEX statements +** or by SELECT statements with ORDER BY clauses that cannot be satisfied +** using indexes and without LIMIT clauses. ** -** SUMMARY: +** The VdbeSorter object implements a multi-threaded external merge sort +** algorithm that is efficient even if the number of elements being sorted +** exceeds the available memory. ** -** Formatting is important to scripts that scan this file. -** Do not deviate from the formatting style currently in use. +** Here is the (internal, non-API) interface between this module and the +** rest of the SQLite system: ** -*****************************************************************************/ - -/* Opcode: Goto * P2 * * * +** sqlcipher_sqlite3VdbeSorterInit() Create a new VdbeSorter object. ** -** An unconditional jump to address P2. -** The next instruction executed will be -** the one at index P2 from the beginning of -** the program. +** sqlcipher_sqlite3VdbeSorterWrite() Add a single new row to the VdbeSorter +** object. The row is a binary blob in the +** OP_MakeRecord format that contains both +** the ORDER BY key columns and result columns +** in the case of a SELECT w/ ORDER BY, or +** the complete record for an index entry +** in the case of a CREATE INDEX. ** -** The P1 parameter is not actually used by this opcode. However, it -** is sometimes set to 1 instead of 0 as a hint to the command-line shell -** that this Goto is the bottom of a loop and that the lines from P2 down -** to the current line should be indented for EXPLAIN output. +** sqlcipher_sqlite3VdbeSorterRewind() Sort all content previously added. +** Position the read cursor on the +** first sorted element. +** +** sqlcipher_sqlite3VdbeSorterNext() Advance the read cursor to the next sorted +** element. +** +** sqlcipher_sqlite3VdbeSorterRowkey() Return the complete binary blob for the +** row currently under the read cursor. +** +** sqlcipher_sqlite3VdbeSorterCompare() Compare the binary blob for the row +** currently under the read cursor against +** another binary blob X and report if +** X is strictly less than the read cursor. +** Used to enforce uniqueness in a +** CREATE UNIQUE INDEX statement. +** +** sqlcipher_sqlite3VdbeSorterClose() Close the VdbeSorter object and reclaim +** all resources. +** +** sqlcipher_sqlite3VdbeSorterReset() Refurbish the VdbeSorter for reuse. This +** is like Close() followed by Init() only +** much faster. +** +** The interfaces above must be called in a particular order. Write() can +** only occur in between Init()/Reset() and Rewind(). Next(), Rowkey(), and +** Compare() can only occur in between Rewind() and Close()/Reset(). i.e. +** +** Init() +** for each record: Write() +** Rewind() +** Rowkey()/Compare() +** Next() +** Close() +** +** Algorithm: +** +** Records passed to the sorter via calls to Write() are initially held +** unsorted in main memory. Assuming the amount of memory used never exceeds +** a threshold, when Rewind() is called the set of records is sorted using +** an in-memory merge sort. In this case, no temporary files are required +** and subsequent calls to Rowkey(), Next() and Compare() read records +** directly from main memory. +** +** If the amount of space used to store records in main memory exceeds the +** threshold, then the set of records currently in memory are sorted and +** written to a temporary file in "Packed Memory Array" (PMA) format. +** A PMA created at this point is known as a "level-0 PMA". Higher levels +** of PMAs may be created by merging existing PMAs together - for example +** merging two or more level-0 PMAs together creates a level-1 PMA. +** +** The threshold for the amount of main memory to use before flushing +** records to a PMA is roughly the same as the limit configured for the +** page-cache of the main database. Specifically, the threshold is set to +** the value returned by "PRAGMA main.page_size" multipled by +** that returned by "PRAGMA main.cache_size", in bytes. +** +** If the sorter is running in single-threaded mode, then all PMAs generated +** are appended to a single temporary file. Or, if the sorter is running in +** multi-threaded mode then up to (N+1) temporary files may be opened, where +** N is the configured number of worker threads. In this case, instead of +** sorting the records and writing the PMA to a temporary file itself, the +** calling thread usually launches a worker thread to do so. Except, if +** there are already N worker threads running, the main thread does the work +** itself. +** +** The sorter is running in multi-threaded mode if (a) the library was built +** with pre-processor symbol SQLITE_MAX_WORKER_THREADS set to a value greater +** than zero, and (b) worker threads have been enabled at runtime by calling +** "PRAGMA threads=N" with some value of N greater than 0. +** +** When Rewind() is called, any data remaining in memory is flushed to a +** final PMA. So at this point the data is stored in some number of sorted +** PMAs within temporary files on disk. +** +** If there are fewer than SORTER_MAX_MERGE_COUNT PMAs in total and the +** sorter is running in single-threaded mode, then these PMAs are merged +** incrementally as keys are retreived from the sorter by the VDBE. The +** MergeEngine object, described in further detail below, performs this +** merge. +** +** Or, if running in multi-threaded mode, then a background thread is +** launched to merge the existing PMAs. Once the background thread has +** merged T bytes of data into a single sorted PMA, the main thread +** begins reading keys from that PMA while the background thread proceeds +** with merging the next T bytes of data. And so on. +** +** Parameter T is set to half the value of the memory threshold used +** by Write() above to determine when to create a new PMA. +** +** If there are more than SORTER_MAX_MERGE_COUNT PMAs in total when +** Rewind() is called, then a hierarchy of incremental-merges is used. +** First, T bytes of data from the first SORTER_MAX_MERGE_COUNT PMAs on +** disk are merged together. Then T bytes of data from the second set, and +** so on, such that no operation ever merges more than SORTER_MAX_MERGE_COUNT +** PMAs at a time. This done is to improve locality. +** +** If running in multi-threaded mode and there are more than +** SORTER_MAX_MERGE_COUNT PMAs on disk when Rewind() is called, then more +** than one background thread may be created. Specifically, there may be +** one background thread for each temporary file on disk, and one background +** thread to merge the output of each of the others to a single PMA for +** the main thread to read from. */ -case OP_Goto: { /* jump */ - -#ifdef SQLITE_DEBUG - /* In debuggging mode, when the p5 flags is set on an OP_Goto, that - ** means we should really jump back to the preceeding OP_ReleaseReg - ** instruction. */ - if( pOp->p5 ){ - assert( pOp->p2 < (int)(pOp - aOp) ); - assert( pOp->p2 > 1 ); - pOp = &aOp[pOp->p2 - 2]; - assert( pOp[1].opcode==OP_ReleaseReg ); - goto check_for_interrupt; - } -#endif - -jump_to_p2_and_check_for_interrupt: - pOp = &aOp[pOp->p2 - 1]; - - /* Opcodes that are used as the bottom of a loop (OP_Next, OP_Prev, - ** OP_VNext, or OP_SorterNext) all jump here upon - ** completion. Check to see if sqlcipher_sqlite3_interrupt() has been called - ** or if the progress callback needs to be invoked. - ** - ** This code uses unstructured "goto" statements and does not look clean. - ** But that is not due to sloppy coding habits. The code is written this - ** way for performance, to avoid having to run the interrupt and progress - ** checks on every opcode. This helps sqlcipher_sqlite3_step() to run about 1.5% - ** faster according to "valgrind --tool=cachegrind" */ -check_for_interrupt: - if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - /* Call the progress callback if it is configured and the required number - ** of VDBE ops have been executed (either since this invocation of - ** sqlcipher_sqlite3VdbeExec() or since last time the progress callback was called). - ** If the progress callback returns non-zero, exit the virtual machine with - ** a return code SQLITE_ABORT. - */ - while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ - assert( db->nProgressOps!=0 ); - nProgressLimit += db->nProgressOps; - if( db->xProgress(db->pProgressArg) ){ - nProgressLimit = LARGEST_UINT64; - rc = SQLITE_INTERRUPT; - goto abort_due_to_error; - } - } -#endif +/* #include "sqliteInt.h" */ +/* #include "vdbeInt.h" */ - break; -} +/* +** If SQLITE_DEBUG_SORTER_THREADS is defined, this module outputs various +** messages to stderr that may be helpful in understanding the performance +** characteristics of the sorter in multi-threaded mode. +*/ +#if 0 +# define SQLITE_DEBUG_SORTER_THREADS 1 +#endif -/* Opcode: Gosub P1 P2 * * * -** -** Write the current address onto register P1 -** and then jump to address P2. +/* +** Hard-coded maximum amount of data to accumulate in memory before flushing +** to a level 0 PMA. The purpose of this limit is to prevent various integer +** overflows. 512MiB. */ -case OP_Gosub: { /* jump */ - assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); - pIn1 = &aMem[pOp->p1]; - assert( VdbeMemDynamic(pIn1)==0 ); - memAboutToChange(p, pIn1); - pIn1->flags = MEM_Int; - pIn1->u.i = (int)(pOp-aOp); - REGISTER_TRACE(pOp->p1, pIn1); +#define SQLITE_MAX_PMASZ (1<<29) - /* Most jump operations do a goto to this spot in order to update - ** the pOp pointer. */ -jump_to_p2: - pOp = &aOp[pOp->p2 - 1]; - break; -} +/* +** Private objects used by the sorter +*/ +typedef struct MergeEngine MergeEngine; /* Merge PMAs together */ +typedef struct PmaReader PmaReader; /* Incrementally read one PMA */ +typedef struct PmaWriter PmaWriter; /* Incrementally write one PMA */ +typedef struct SorterRecord SorterRecord; /* A record being sorted */ +typedef struct SortSubtask SortSubtask; /* A sub-task in the sort process */ +typedef struct SorterFile SorterFile; /* Temporary file object wrapper */ +typedef struct SorterList SorterList; /* In-memory list of records */ +typedef struct IncrMerger IncrMerger; /* Read & merge multiple PMAs */ -/* Opcode: Return P1 * * * * -** -** Jump to the next instruction after the address in register P1. After -** the jump, register P1 becomes undefined. +/* +** A container for a temp file handle and the current amount of data +** stored in the file. */ -case OP_Return: { /* in1 */ - pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags==MEM_Int ); - pOp = &aOp[pIn1->u.i]; - pIn1->flags = MEM_Undefined; - break; -} +struct SorterFile { + sqlcipher_sqlite3_file *pFd; /* File handle */ + i64 iEof; /* Bytes of data stored in pFd */ +}; -/* Opcode: InitCoroutine P1 P2 P3 * * -** -** Set up register P1 so that it will Yield to the coroutine -** located at address P3. -** -** If P2!=0 then the coroutine implementation immediately follows -** this opcode. So jump over the coroutine implementation to -** address P2. +/* +** An in-memory list of objects to be sorted. ** -** See also: EndCoroutine +** If aMemory==0 then each object is allocated separately and the objects +** are connected using SorterRecord.u.pNext. If aMemory!=0 then all objects +** are stored in the aMemory[] bulk memory, one right after the other, and +** are connected using SorterRecord.u.iNext. */ -case OP_InitCoroutine: { /* jump */ - assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); - assert( pOp->p2>=0 && pOp->p2nOp ); - assert( pOp->p3>=0 && pOp->p3nOp ); - pOut = &aMem[pOp->p1]; - assert( !VdbeMemDynamic(pOut) ); - pOut->u.i = pOp->p3 - 1; - pOut->flags = MEM_Int; - if( pOp->p2 ) goto jump_to_p2; - break; -} +struct SorterList { + SorterRecord *pList; /* Linked list of records */ + u8 *aMemory; /* If non-NULL, bulk memory to hold pList */ + int szPMA; /* Size of pList as PMA in bytes */ +}; -/* Opcode: EndCoroutine P1 * * * * +/* +** The MergeEngine object is used to combine two or more smaller PMAs into +** one big PMA using a merge operation. Separate PMAs all need to be +** combined into one big PMA in order to be able to step through the sorted +** records in order. ** -** The instruction at the address in register P1 is a Yield. -** Jump to the P2 parameter of that Yield. -** After the jump, register P1 becomes undefined. +** The aReadr[] array contains a PmaReader object for each of the PMAs being +** merged. An aReadr[] object either points to a valid key or else is at EOF. +** ("EOF" means "End Of File". When aReadr[] is at EOF there is no more data.) +** For the purposes of the paragraphs below, we assume that the array is +** actually N elements in size, where N is the smallest power of 2 greater +** to or equal to the number of PMAs being merged. The extra aReadr[] elements +** are treated as if they are empty (always at EOF). ** -** See also: InitCoroutine -*/ -case OP_EndCoroutine: { /* in1 */ - VdbeOp *pCaller; - pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags==MEM_Int ); - assert( pIn1->u.i>=0 && pIn1->u.inOp ); - pCaller = &aOp[pIn1->u.i]; - assert( pCaller->opcode==OP_Yield ); - assert( pCaller->p2>=0 && pCaller->p2nOp ); - pOp = &aOp[pCaller->p2 - 1]; - pIn1->flags = MEM_Undefined; - break; -} - -/* Opcode: Yield P1 P2 * * * +** The aTree[] array is also N elements in size. The value of N is stored in +** the MergeEngine.nTree variable. ** -** Swap the program counter with the value in register P1. This -** has the effect of yielding to a coroutine. +** The final (N/2) elements of aTree[] contain the results of comparing +** pairs of PMA keys together. Element i contains the result of +** comparing aReadr[2*i-N] and aReadr[2*i-N+1]. Whichever key is smaller, the +** aTree element is set to the index of it. ** -** If the coroutine that is launched by this instruction ends with -** Yield or Return then continue to the next instruction. But if -** the coroutine launched by this instruction ends with -** EndCoroutine, then jump to P2 rather than continuing with the -** next instruction. +** For the purposes of this comparison, EOF is considered greater than any +** other key value. If the keys are equal (only possible with two EOF +** values), it doesn't matter which index is stored. ** -** See also: InitCoroutine -*/ -case OP_Yield: { /* in1, jump */ - int pcDest; - pIn1 = &aMem[pOp->p1]; - assert( VdbeMemDynamic(pIn1)==0 ); - pIn1->flags = MEM_Int; - pcDest = (int)pIn1->u.i; - pIn1->u.i = (int)(pOp - aOp); - REGISTER_TRACE(pOp->p1, pIn1); - pOp = &aOp[pcDest]; - break; -} - -/* Opcode: HaltIfNull P1 P2 P3 P4 P5 -** Synopsis: if r[P3]=null halt +** The (N/4) elements of aTree[] that precede the final (N/2) described +** above contains the index of the smallest of each block of 4 PmaReaders +** And so on. So that aTree[1] contains the index of the PmaReader that +** currently points to the smallest key value. aTree[0] is unused. ** -** Check the value in register P3. If it is NULL then Halt using -** parameter P1, P2, and P4 as if this were a Halt instruction. If the -** value in register P3 is not NULL, then this routine is a no-op. -** The P5 parameter should be 1. -*/ -case OP_HaltIfNull: { /* in3 */ - pIn3 = &aMem[pOp->p3]; -#ifdef SQLITE_DEBUG - if( pOp->p2==OE_Abort ){ sqlcipher_sqlite3VdbeAssertAbortable(p); } -#endif - if( (pIn3->flags & MEM_Null)==0 ) break; - /* Fall through into OP_Halt */ - /* no break */ deliberate_fall_through -} - -/* Opcode: Halt P1 P2 * P4 P5 +** Example: ** -** Exit immediately. All open cursors, etc are closed -** automatically. +** aReadr[0] -> Banana +** aReadr[1] -> Feijoa +** aReadr[2] -> Elderberry +** aReadr[3] -> Currant +** aReadr[4] -> Grapefruit +** aReadr[5] -> Apple +** aReadr[6] -> Durian +** aReadr[7] -> EOF ** -** P1 is the result code returned by sqlcipher_sqlite3_exec(), sqlcipher_sqlite3_reset(), -** or sqlcipher_sqlite3_finalize(). For a normal halt, this should be SQLITE_OK (0). -** For errors, it can be some other value. If P1!=0 then P2 will determine -** whether or not to rollback the current transaction. Do not rollback -** if P2==OE_Fail. Do the rollback if P2==OE_Rollback. If P2==OE_Abort, -** then back out all changes that have occurred during this execution of the -** VDBE, but do not rollback the transaction. +** aTree[] = { X, 5 0, 5 0, 3, 5, 6 } ** -** If P4 is not null then it is an error message string. +** The current element is "Apple" (the value of the key indicated by +** PmaReader 5). When the Next() operation is invoked, PmaReader 5 will +** be advanced to the next key in its segment. Say the next key is +** "Eggplant": ** -** P5 is a value between 0 and 4, inclusive, that modifies the P4 string. +** aReadr[5] -> Eggplant ** -** 0: (no change) -** 1: NOT NULL contraint failed: P4 -** 2: UNIQUE constraint failed: P4 -** 3: CHECK constraint failed: P4 -** 4: FOREIGN KEY constraint failed: P4 +** The contents of aTree[] are updated first by comparing the new PmaReader +** 5 key to the current key of PmaReader 4 (still "Grapefruit"). The PmaReader +** 5 value is still smaller, so aTree[6] is set to 5. And so on up the tree. +** The value of PmaReader 6 - "Durian" - is now smaller than that of PmaReader +** 5, so aTree[3] is set to 6. Key 0 is smaller than key 6 (Bananap2==OE_Abort ){ sqlcipher_sqlite3VdbeAssertAbortable(p); } -#endif - if( pOp->p1==SQLITE_OK && p->pFrame ){ - /* Halt the sub-program. Return control to the parent frame. */ - pFrame = p->pFrame; - p->pFrame = pFrame->pParent; - p->nFrame--; - sqlcipher_sqlite3VdbeSetChanges(db, p->nChange); - pcx = sqlcipher_sqlite3VdbeFrameRestore(pFrame); - if( pOp->p2==OE_Ignore ){ - /* Instruction pcx is the OP_Program that invoked the sub-program - ** currently being halted. If the p2 instruction of this OP_Halt - ** instruction is set to OE_Ignore, then the sub-program is throwing - ** an IGNORE exception. In this case jump to the address specified - ** as the p2 of the calling OP_Program. */ - pcx = p->aOp[pcx].p2-1; - } - aOp = p->aOp; - aMem = p->aMem; - pOp = &aOp[pcx]; - break; - } - p->rc = pOp->p1; - p->errorAction = (u8)pOp->p2; - p->pc = pcx; - assert( pOp->p5<=4 ); - if( p->rc ){ - if( pOp->p5 ){ - static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK", - "FOREIGN KEY" }; - testcase( pOp->p5==1 ); - testcase( pOp->p5==2 ); - testcase( pOp->p5==3 ); - testcase( pOp->p5==4 ); - sqlcipher_sqlite3VdbeError(p, "%s constraint failed", azType[pOp->p5-1]); - if( pOp->p4.z ){ - p->zErrMsg = sqlcipher_sqlite3MPrintf(db, "%z: %s", p->zErrMsg, pOp->p4.z); - } - }else{ - sqlcipher_sqlite3VdbeError(p, "%s", pOp->p4.z); - } - sqlcipher_sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg); - } - rc = sqlcipher_sqlite3VdbeHalt(p); - assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); - if( rc==SQLITE_BUSY ){ - p->rc = SQLITE_BUSY; - }else{ - assert( rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT ); - assert( rc==SQLITE_OK || db->nDeferredCons>0 || db->nDeferredImmCons>0 ); - rc = p->rc ? SQLITE_ERROR : SQLITE_DONE; - } - goto vdbe_return; -} +struct MergeEngine { + int nTree; /* Used size of aTree/aReadr (power of 2) */ + SortSubtask *pTask; /* Used by this thread only */ + int *aTree; /* Current state of incremental merge */ + PmaReader *aReadr; /* Array of PmaReaders to merge data from */ +}; -/* Opcode: Integer P1 P2 * * * -** Synopsis: r[P2]=P1 +/* +** This object represents a single thread of control in a sort operation. +** Exactly VdbeSorter.nTask instances of this object are allocated +** as part of each VdbeSorter object. Instances are never allocated any +** other way. VdbeSorter.nTask is set to the number of worker threads allowed +** (see SQLITE_CONFIG_WORKER_THREADS) plus one (the main thread). Thus for +** single-threaded operation, there is exactly one instance of this object +** and for multi-threaded operation there are two or more instances. ** -** The 32-bit integer value P1 is written into register P2. -*/ -case OP_Integer: { /* out2 */ - pOut = out2Prerelease(p, pOp); - pOut->u.i = pOp->p1; - break; -} - -/* Opcode: Int64 * P2 * P4 * -** Synopsis: r[P2]=P4 +** Essentially, this structure contains all those fields of the VdbeSorter +** structure for which each thread requires a separate instance. For example, +** each thread requries its own UnpackedRecord object to unpack records in +** as part of comparison operations. ** -** P4 is a pointer to a 64-bit integer value. -** Write that value into register P2. -*/ -case OP_Int64: { /* out2 */ - pOut = out2Prerelease(p, pOp); - assert( pOp->p4.pI64!=0 ); - pOut->u.i = *pOp->p4.pI64; - break; -} - -#ifndef SQLITE_OMIT_FLOATING_POINT -/* Opcode: Real * P2 * P4 * -** Synopsis: r[P2]=P4 +** Before a background thread is launched, variable bDone is set to 0. Then, +** right before it exits, the thread itself sets bDone to 1. This is used for +** two purposes: ** -** P4 is a pointer to a 64-bit floating point value. -** Write that value into register P2. +** 1. When flushing the contents of memory to a level-0 PMA on disk, to +** attempt to select a SortSubtask for which there is not already an +** active background thread (since doing so causes the main thread +** to block until it finishes). +** +** 2. If SQLITE_DEBUG_SORTER_THREADS is defined, to determine if a call +** to sqlcipher_sqlite3ThreadJoin() is likely to block. Cases that are likely to +** block provoke debugging output. +** +** In both cases, the effects of the main thread seeing (bDone==0) even +** after the thread has finished are not dire. So we don't worry about +** memory barriers and such here. */ -case OP_Real: { /* same as TK_FLOAT, out2 */ - pOut = out2Prerelease(p, pOp); - pOut->flags = MEM_Real; - assert( !sqlcipher_sqlite3IsNaN(*pOp->p4.pReal) ); - pOut->u.r = *pOp->p4.pReal; - break; -} -#endif +typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int); +struct SortSubtask { + SQLiteThread *pThread; /* Background thread, if any */ + int bDone; /* Set if thread is finished but not joined */ + VdbeSorter *pSorter; /* Sorter that owns this sub-task */ + UnpackedRecord *pUnpacked; /* Space to unpack a record */ + SorterList list; /* List for thread to write to a PMA */ + int nPMA; /* Number of PMAs currently in file */ + SorterCompare xCompare; /* Compare function to use */ + SorterFile file; /* Temp file for level-0 PMAs */ + SorterFile file2; /* Space for other PMAs */ +}; -/* Opcode: String8 * P2 * P4 * -** Synopsis: r[P2]='P4' + +/* +** Main sorter structure. A single instance of this is allocated for each +** sorter cursor created by the VDBE. ** -** P4 points to a nul terminated UTF-8 string. This opcode is transformed -** into a String opcode before it is executed for the first time. During -** this transformation, the length of string P4 is computed and stored -** as the P1 parameter. +** mxKeysize: +** As records are added to the sorter by calls to sqlcipher_sqlite3VdbeSorterWrite(), +** this variable is updated so as to be set to the size on disk of the +** largest record in the sorter. */ -case OP_String8: { /* same as TK_STRING, out2 */ - assert( pOp->p4.z!=0 ); - pOut = out2Prerelease(p, pOp); - pOp->p1 = sqlcipher_sqlite3Strlen30(pOp->p4.z); +struct VdbeSorter { + int mnPmaSize; /* Minimum PMA size, in bytes */ + int mxPmaSize; /* Maximum PMA size, in bytes. 0==no limit */ + int mxKeysize; /* Largest serialized key seen so far */ + int pgsz; /* Main database page size */ + PmaReader *pReader; /* Readr data from here after Rewind() */ + MergeEngine *pMerger; /* Or here, if bUseThreads==0 */ + sqlcipher_sqlite3 *db; /* Database connection */ + KeyInfo *pKeyInfo; /* How to compare records */ + UnpackedRecord *pUnpacked; /* Used by VdbeSorterCompare() */ + SorterList list; /* List of in-memory records */ + int iMemory; /* Offset of free space in list.aMemory */ + int nMemory; /* Size of list.aMemory allocation in bytes */ + u8 bUsePMA; /* True if one or more PMAs created */ + u8 bUseThreads; /* True to use background threads */ + u8 iPrev; /* Previous thread used to flush PMA */ + u8 nTask; /* Size of aTask[] array */ + u8 typeMask; + SortSubtask aTask[1]; /* One or more subtasks */ +}; -#ifndef SQLITE_OMIT_UTF16 - if( encoding!=SQLITE_UTF8 ){ - rc = sqlcipher_sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC); - assert( rc==SQLITE_OK || rc==SQLITE_TOOBIG ); - if( rc ) goto too_big; - if( SQLITE_OK!=sqlcipher_sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem; - assert( pOut->szMalloc>0 && pOut->zMalloc==pOut->z ); - assert( VdbeMemDynamic(pOut)==0 ); - pOut->szMalloc = 0; - pOut->flags |= MEM_Static; - if( pOp->p4type==P4_DYNAMIC ){ - sqlcipher_sqlite3DbFree(db, pOp->p4.z); - } - pOp->p4type = P4_DYNAMIC; - pOp->p4.z = pOut->z; - pOp->p1 = pOut->n; - } -#endif - if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - pOp->opcode = OP_String; - assert( rc==SQLITE_OK ); - /* Fall through to the next case, OP_String */ - /* no break */ deliberate_fall_through -} +#define SORTER_TYPE_INTEGER 0x01 +#define SORTER_TYPE_TEXT 0x02 -/* Opcode: String P1 P2 P3 P4 P5 -** Synopsis: r[P2]='P4' (len=P1) -** -** The string value P4 of length P1 (bytes) is stored in register P2. -** -** If P3 is not zero and the content of register P3 is equal to P5, then -** the datatype of the register P2 is converted to BLOB. The content is -** the same sequence of bytes, it is merely interpreted as a BLOB instead -** of a string, as if it had been CAST. In other words: +/* +** An instance of the following object is used to read records out of a +** PMA, in sorted order. The next key to be read is cached in nKey/aKey. +** aKey might point into aMap or into aBuffer. If neither of those locations +** contain a contiguous representation of the key, then aAlloc is allocated +** and the key is copied into aAlloc and aKey is made to poitn to aAlloc. ** -** if( P3!=0 and reg[P3]==P5 ) reg[P2] := CAST(reg[P2] as BLOB) +** pFd==0 at EOF. */ -case OP_String: { /* out2 */ - assert( pOp->p4.z!=0 ); - pOut = out2Prerelease(p, pOp); - pOut->flags = MEM_Str|MEM_Static|MEM_Term; - pOut->z = pOp->p4.z; - pOut->n = pOp->p1; - pOut->enc = encoding; - UPDATE_MAX_BLOBSIZE(pOut); -#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS - if( pOp->p3>0 ){ - assert( pOp->p3<=(p->nMem+1 - p->nCursor) ); - pIn3 = &aMem[pOp->p3]; - assert( pIn3->flags & MEM_Int ); - if( pIn3->u.i==pOp->p5 ) pOut->flags = MEM_Blob|MEM_Static|MEM_Term; - } -#endif - break; -} +struct PmaReader { + i64 iReadOff; /* Current read offset */ + i64 iEof; /* 1 byte past EOF for this PmaReader */ + int nAlloc; /* Bytes of space at aAlloc */ + int nKey; /* Number of bytes in key */ + sqlcipher_sqlite3_file *pFd; /* File handle we are reading from */ + u8 *aAlloc; /* Space for aKey if aBuffer and pMap wont work */ + u8 *aKey; /* Pointer to current key */ + u8 *aBuffer; /* Current read buffer */ + int nBuffer; /* Size of read buffer in bytes */ + u8 *aMap; /* Pointer to mapping of entire file */ + IncrMerger *pIncr; /* Incremental merger */ +}; -/* Opcode: Null P1 P2 P3 * * -** Synopsis: r[P2..P3]=NULL +/* +** Normally, a PmaReader object iterates through an existing PMA stored +** within a temp file. However, if the PmaReader.pIncr variable points to +** an object of the following type, it may be used to iterate/merge through +** multiple PMAs simultaneously. ** -** Write a NULL into registers P2. If P3 greater than P2, then also write -** NULL into register P3 and every register in between P2 and P3. If P3 -** is less than P2 (typically P3 is zero) then only register P2 is -** set to NULL. +** There are two types of IncrMerger object - single (bUseThread==0) and +** multi-threaded (bUseThread==1). ** -** If the P1 value is non-zero, then also set the MEM_Cleared flag so that -** NULL values will not compare equal even if SQLITE_NULLEQ is set on -** OP_Ne or OP_Eq. -*/ -case OP_Null: { /* out2 */ - int cnt; - u16 nullFlag; - pOut = out2Prerelease(p, pOp); - cnt = pOp->p3-pOp->p2; - assert( pOp->p3<=(p->nMem+1 - p->nCursor) ); - pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null; - pOut->n = 0; -#ifdef SQLITE_DEBUG - pOut->uTemp = 0; -#endif - while( cnt>0 ){ - pOut++; - memAboutToChange(p, pOut); - sqlcipher_sqlite3VdbeMemSetNull(pOut); - pOut->flags = nullFlag; - pOut->n = 0; - cnt--; - } - break; -} - -/* Opcode: SoftNull P1 * * * * -** Synopsis: r[P1]=NULL +** A multi-threaded IncrMerger object uses two temporary files - aFile[0] +** and aFile[1]. Neither file is allowed to grow to more than mxSz bytes in +** size. When the IncrMerger is initialized, it reads enough data from +** pMerger to populate aFile[0]. It then sets variables within the +** corresponding PmaReader object to read from that file and kicks off +** a background thread to populate aFile[1] with the next mxSz bytes of +** sorted record data from pMerger. ** -** Set register P1 to have the value NULL as seen by the OP_MakeRecord -** instruction, but do not free any string or blob memory associated with -** the register, so that if the value was a string or blob that was -** previously copied using OP_SCopy, the copies will continue to be valid. +** When the PmaReader reaches the end of aFile[0], it blocks until the +** background thread has finished populating aFile[1]. It then exchanges +** the contents of the aFile[0] and aFile[1] variables within this structure, +** sets the PmaReader fields to read from the new aFile[0] and kicks off +** another background thread to populate the new aFile[1]. And so on, until +** the contents of pMerger are exhausted. +** +** A single-threaded IncrMerger does not open any temporary files of its +** own. Instead, it has exclusive access to mxSz bytes of space beginning +** at offset iStartOff of file pTask->file2. And instead of using a +** background thread to prepare data for the PmaReader, with a single +** threaded IncrMerger the allocate part of pTask->file2 is "refilled" with +** keys from pMerger by the calling thread whenever the PmaReader runs out +** of data. */ -case OP_SoftNull: { - assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); - pOut = &aMem[pOp->p1]; - pOut->flags = (pOut->flags&~(MEM_Undefined|MEM_AffMask))|MEM_Null; - break; -} +struct IncrMerger { + SortSubtask *pTask; /* Task that owns this merger */ + MergeEngine *pMerger; /* Merge engine thread reads data from */ + i64 iStartOff; /* Offset to start writing file at */ + int mxSz; /* Maximum bytes of data to store */ + int bEof; /* Set to true when merge is finished */ + int bUseThread; /* True to use a bg thread for this object */ + SorterFile aFile[2]; /* aFile[0] for reading, [1] for writing */ +}; -/* Opcode: Blob P1 P2 * P4 * -** Synopsis: r[P2]=P4 (len=P1) +/* +** An instance of this object is used for writing a PMA. ** -** P4 points to a blob of data P1 bytes long. Store this -** blob in register P2. +** The PMA is written one record at a time. Each record is of an arbitrary +** size. But I/O is more efficient if it occurs in page-sized blocks where +** each block is aligned on a page boundary. This object caches writes to +** the PMA so that aligned, page-size blocks are written. */ -case OP_Blob: { /* out2 */ - assert( pOp->p1 <= SQLITE_MAX_LENGTH ); - pOut = out2Prerelease(p, pOp); - sqlcipher_sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); - pOut->enc = encoding; - UPDATE_MAX_BLOBSIZE(pOut); - break; -} +struct PmaWriter { + int eFWErr; /* Non-zero if in an error state */ + u8 *aBuffer; /* Pointer to write buffer */ + int nBuffer; /* Size of write buffer in bytes */ + int iBufStart; /* First byte of buffer to write */ + int iBufEnd; /* Last byte of buffer to write */ + i64 iWriteOff; /* Offset of start of buffer in file */ + sqlcipher_sqlite3_file *pFd; /* File handle to write to */ +}; -/* Opcode: Variable P1 P2 * P4 * -** Synopsis: r[P2]=parameter(P1,P4) +/* +** This object is the header on a single record while that record is being +** held in memory and prior to being written out as part of a PMA. ** -** Transfer the values of bound parameter P1 into register P2 +** How the linked list is connected depends on how memory is being managed +** by this module. If using a separate allocation for each in-memory record +** (VdbeSorter.list.aMemory==0), then the list is always connected using the +** SorterRecord.u.pNext pointers. ** -** If the parameter is named, then its name appears in P4. -** The P4 value is used by sqlcipher_sqlite3_bind_parameter_name(). +** Or, if using the single large allocation method (VdbeSorter.list.aMemory!=0), +** then while records are being accumulated the list is linked using the +** SorterRecord.u.iNext offset. This is because the aMemory[] array may +** be sqlcipher_sqlite3Realloc()ed while records are being accumulated. Once the VM +** has finished passing records to the sorter, or when the in-memory buffer +** is full, the list is sorted. As part of the sorting process, it is +** converted to use the SorterRecord.u.pNext pointers. See function +** vdbeSorterSort() for details. */ -case OP_Variable: { /* out2 */ - Mem *pVar; /* Value being transferred */ - - assert( pOp->p1>0 && pOp->p1<=p->nVar ); - assert( pOp->p4.z==0 || pOp->p4.z==sqlcipher_sqlite3VListNumToName(p->pVList,pOp->p1) ); - pVar = &p->aVar[pOp->p1 - 1]; - if( sqlcipher_sqlite3VdbeMemTooBig(pVar) ){ - goto too_big; - } - pOut = &aMem[pOp->p2]; - if( VdbeMemDynamic(pOut) ) sqlcipher_sqlite3VdbeMemSetNull(pOut); - memcpy(pOut, pVar, MEMCELLSIZE); - pOut->flags &= ~(MEM_Dyn|MEM_Ephem); - pOut->flags |= MEM_Static|MEM_FromBind; - UPDATE_MAX_BLOBSIZE(pOut); - break; -} +struct SorterRecord { + int nVal; /* Size of the record in bytes */ + union { + SorterRecord *pNext; /* Pointer to next record in list */ + int iNext; /* Offset within aMemory of next record */ + } u; + /* The data for the record immediately follows this header */ +}; -/* Opcode: Move P1 P2 P3 * * -** Synopsis: r[P2@P3]=r[P1@P3] +/* Return a pointer to the buffer containing the record data for SorterRecord +** object p. Should be used as if: ** -** Move the P3 values in register P1..P1+P3-1 over into -** registers P2..P2+P3-1. Registers P1..P1+P3-1 are -** left holding a NULL. It is an error for register ranges -** P1..P1+P3-1 and P2..P2+P3-1 to overlap. It is an error -** for P3 to be less than 1. +** void *SRVAL(SorterRecord *p) { return (void*)&p[1]; } */ -case OP_Move: { - int n; /* Number of registers left to copy */ - int p1; /* Register to copy from */ - int p2; /* Register to copy to */ - - n = pOp->p3; - p1 = pOp->p1; - p2 = pOp->p2; - assert( n>0 && p1>0 && p2>0 ); - assert( p1+n<=p2 || p2+n<=p1 ); - - pIn1 = &aMem[p1]; - pOut = &aMem[p2]; - do{ - assert( pOut<=&aMem[(p->nMem+1 - p->nCursor)] ); - assert( pIn1<=&aMem[(p->nMem+1 - p->nCursor)] ); - assert( memIsValid(pIn1) ); - memAboutToChange(p, pOut); - sqlcipher_sqlite3VdbeMemMove(pOut, pIn1); -#ifdef SQLITE_DEBUG - pIn1->pScopyFrom = 0; - { int i; - for(i=1; inMem; i++){ - if( aMem[i].pScopyFrom==pIn1 ){ - aMem[i].pScopyFrom = pOut; - } - } - } -#endif - Deephemeralize(pOut); - REGISTER_TRACE(p2++, pOut); - pIn1++; - pOut++; - }while( --n ); - break; -} +#define SRVAL(p) ((void*)((SorterRecord*)(p) + 1)) -/* Opcode: Copy P1 P2 P3 * * -** Synopsis: r[P2@P3+1]=r[P1@P3+1] -** -** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. -** -** This instruction makes a deep copy of the value. A duplicate -** is made of any string or blob constant. See also OP_SCopy. -*/ -case OP_Copy: { - int n; - n = pOp->p3; - pIn1 = &aMem[pOp->p1]; - pOut = &aMem[pOp->p2]; - assert( pOut!=pIn1 ); - while( 1 ){ - memAboutToChange(p, pOut); - sqlcipher_sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); - Deephemeralize(pOut); -#ifdef SQLITE_DEBUG - pOut->pScopyFrom = 0; -#endif - REGISTER_TRACE(pOp->p2+pOp->p3-n, pOut); - if( (n--)==0 ) break; - pOut++; - pIn1++; - } - break; -} +/* Maximum number of PMAs that a single MergeEngine can merge */ +#define SORTER_MAX_MERGE_COUNT 16 -/* Opcode: SCopy P1 P2 * * * -** Synopsis: r[P2]=r[P1] -** -** Make a shallow copy of register P1 into register P2. -** -** This instruction makes a shallow copy of the value. If the value -** is a string or blob, then the copy is only a pointer to the -** original and hence if the original changes so will the copy. -** Worse, if the original is deallocated, the copy becomes invalid. -** Thus the program must guarantee that the original will not change -** during the lifetime of the copy. Use OP_Copy to make a complete -** copy. -*/ -case OP_SCopy: { /* out2 */ - pIn1 = &aMem[pOp->p1]; - pOut = &aMem[pOp->p2]; - assert( pOut!=pIn1 ); - sqlcipher_sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); -#ifdef SQLITE_DEBUG - pOut->pScopyFrom = pIn1; - pOut->mScopyFlags = pIn1->flags; -#endif - break; -} +static int vdbeIncrSwap(IncrMerger*); +static void vdbeIncrFree(IncrMerger *); -/* Opcode: IntCopy P1 P2 * * * -** Synopsis: r[P2]=r[P1] -** -** Transfer the integer value held in register P1 into register P2. -** -** This is an optimized version of SCopy that works only for integer -** values. +/* +** Free all memory belonging to the PmaReader object passed as the +** argument. All structure fields are set to zero before returning. */ -case OP_IntCopy: { /* out2 */ - pIn1 = &aMem[pOp->p1]; - assert( (pIn1->flags & MEM_Int)!=0 ); - pOut = &aMem[pOp->p2]; - sqlcipher_sqlite3VdbeMemSetInt64(pOut, pIn1->u.i); - break; +static void vdbePmaReaderClear(PmaReader *pReadr){ + sqlcipher_sqlite3_free(pReadr->aAlloc); + sqlcipher_sqlite3_free(pReadr->aBuffer); + if( pReadr->aMap ) sqlcipher_sqlite3OsUnfetch(pReadr->pFd, 0, pReadr->aMap); + vdbeIncrFree(pReadr->pIncr); + memset(pReadr, 0, sizeof(PmaReader)); } -/* Opcode: ResultRow P1 P2 * * * -** Synopsis: output=r[P1@P2] +/* +** Read the next nByte bytes of data from the PMA p. +** If successful, set *ppOut to point to a buffer containing the data +** and return SQLITE_OK. Otherwise, if an error occurs, return an SQLite +** error code. ** -** The registers P1 through P1+P2-1 contain a single row of -** results. This opcode causes the sqlcipher_sqlite3_step() call to terminate -** with an SQLITE_ROW return code and it sets up the sqlcipher_sqlite3_stmt -** structure to provide access to the r(P1)..r(P1+P2-1) values as -** the result row. +** The buffer returned in *ppOut is only valid until the +** next call to this function. */ -case OP_ResultRow: { - Mem *pMem; - int i; - assert( p->nResColumn==pOp->p2 ); - assert( pOp->p1>0 ); - assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); +static int vdbePmaReadBlob( + PmaReader *p, /* PmaReader from which to take the blob */ + int nByte, /* Bytes of data to read */ + u8 **ppOut /* OUT: Pointer to buffer containing data */ +){ + int iBuf; /* Offset within buffer to read from */ + int nAvail; /* Bytes of data available in buffer */ - /* If this statement has violated immediate foreign key constraints, do - ** not return the number of rows modified. And do not RELEASE the statement - ** transaction. It needs to be rolled back. */ - if( SQLITE_OK!=(rc = sqlcipher_sqlite3VdbeCheckFk(p, 0)) ){ - assert( db->flags&SQLITE_CountRows ); - assert( p->usesStmtJournal ); - goto abort_due_to_error; + if( p->aMap ){ + *ppOut = &p->aMap[p->iReadOff]; + p->iReadOff += nByte; + return SQLITE_OK; } - /* If the SQLITE_CountRows flag is set in sqlcipher_sqlite3.flags mask, then - ** DML statements invoke this opcode to return the number of rows - ** modified to the user. This is the only way that a VM that - ** opens a statement transaction may invoke this opcode. - ** - ** In case this is such a statement, close any statement transaction - ** opened by this VM before returning control to the user. This is to - ** ensure that statement-transactions are always nested, not overlapping. - ** If the open statement-transaction is not closed here, then the user - ** may step another VM that opens its own statement transaction. This - ** may lead to overlapping statement transactions. - ** - ** The statement transaction is never a top-level transaction. Hence - ** the RELEASE call below can never fail. - */ - assert( p->iStatement==0 || db->flags&SQLITE_CountRows ); - rc = sqlcipher_sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE); - assert( rc==SQLITE_OK ); + assert( p->aBuffer ); - /* Invalidate all ephemeral cursor row caches */ - p->cacheCtr = (p->cacheCtr + 2)|1; + /* If there is no more data to be read from the buffer, read the next + ** p->nBuffer bytes of data from the file into it. Or, if there are less + ** than p->nBuffer bytes remaining in the PMA, read all remaining data. */ + iBuf = p->iReadOff % p->nBuffer; + if( iBuf==0 ){ + int nRead; /* Bytes to read from disk */ + int rc; /* sqlcipher_sqlite3OsRead() return code */ - /* Make sure the results of the current row are \000 terminated - ** and have an assigned type. The results are de-ephemeralized as - ** a side effect. - */ - pMem = p->pResultSet = &aMem[pOp->p1]; - for(i=0; ip2; i++){ - assert( memIsValid(&pMem[i]) ); - Deephemeralize(&pMem[i]); - assert( (pMem[i].flags & MEM_Ephem)==0 - || (pMem[i].flags & (MEM_Str|MEM_Blob))==0 ); - sqlcipher_sqlite3VdbeMemNulTerminate(&pMem[i]); - REGISTER_TRACE(pOp->p1+i, &pMem[i]); -#ifdef SQLITE_DEBUG - /* The registers in the result will not be used again when the - ** prepared statement restarts. This is because sqlcipher_sqlite3_column() - ** APIs might have caused type conversions of made other changes to - ** the register values. Therefore, we can go ahead and break any - ** OP_SCopy dependencies. */ - pMem[i].pScopyFrom = 0; -#endif - } - if( db->mallocFailed ) goto no_mem; + /* Determine how many bytes of data to read. */ + if( (p->iEof - p->iReadOff) > (i64)p->nBuffer ){ + nRead = p->nBuffer; + }else{ + nRead = (int)(p->iEof - p->iReadOff); + } + assert( nRead>0 ); - if( db->mTrace & SQLITE_TRACE_ROW ){ - db->trace.xV2(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); + /* Readr data from the file. Return early if an error occurs. */ + rc = sqlcipher_sqlite3OsRead(p->pFd, p->aBuffer, nRead, p->iReadOff); + assert( rc!=SQLITE_IOERR_SHORT_READ ); + if( rc!=SQLITE_OK ) return rc; } + nAvail = p->nBuffer - iBuf; + if( nByte<=nAvail ){ + /* The requested data is available in the in-memory buffer. In this + ** case there is no need to make a copy of the data, just return a + ** pointer into the buffer to the caller. */ + *ppOut = &p->aBuffer[iBuf]; + p->iReadOff += nByte; + }else{ + /* The requested data is not all available in the in-memory buffer. + ** In this case, allocate space at p->aAlloc[] to copy the requested + ** range into. Then return a copy of pointer p->aAlloc to the caller. */ + int nRem; /* Bytes remaining to copy */ - /* Return SQLITE_ROW - */ - p->pc = (int)(pOp - aOp) + 1; - rc = SQLITE_ROW; - goto vdbe_return; -} - -/* Opcode: Concat P1 P2 P3 * * -** Synopsis: r[P3]=r[P2]+r[P1] -** -** Add the text in register P1 onto the end of the text in -** register P2 and store the result in register P3. -** If either the P1 or P2 text are NULL then store NULL in P3. -** -** P3 = P2 || P1 -** -** It is illegal for P1 and P3 to be the same register. Sometimes, -** if P3 is the same register as P2, the implementation is able -** to avoid a memcpy(). -*/ -case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ - i64 nByte; /* Total size of the output string or blob */ - u16 flags1; /* Initial flags for P1 */ - u16 flags2; /* Initial flags for P2 */ + /* Extend the p->aAlloc[] allocation if required. */ + if( p->nAllocnAlloc); + while( nByte>nNew ) nNew = nNew*2; + aNew = sqlcipher_sqlite3Realloc(p->aAlloc, nNew); + if( !aNew ) return SQLITE_NOMEM_BKPT; + p->nAlloc = nNew; + p->aAlloc = aNew; + } - pIn1 = &aMem[pOp->p1]; - pIn2 = &aMem[pOp->p2]; - pOut = &aMem[pOp->p3]; - testcase( pOut==pIn2 ); - assert( pIn1!=pOut ); - flags1 = pIn1->flags; - testcase( flags1 & MEM_Null ); - testcase( pIn2->flags & MEM_Null ); - if( (flags1 | pIn2->flags) & MEM_Null ){ - sqlcipher_sqlite3VdbeMemSetNull(pOut); - break; - } - if( (flags1 & (MEM_Str|MEM_Blob))==0 ){ - if( sqlcipher_sqlite3VdbeMemStringify(pIn1,encoding,0) ) goto no_mem; - flags1 = pIn1->flags & ~MEM_Str; - }else if( (flags1 & MEM_Zero)!=0 ){ - if( sqlcipher_sqlite3VdbeMemExpandBlob(pIn1) ) goto no_mem; - flags1 = pIn1->flags & ~MEM_Str; - } - flags2 = pIn2->flags; - if( (flags2 & (MEM_Str|MEM_Blob))==0 ){ - if( sqlcipher_sqlite3VdbeMemStringify(pIn2,encoding,0) ) goto no_mem; - flags2 = pIn2->flags & ~MEM_Str; - }else if( (flags2 & MEM_Zero)!=0 ){ - if( sqlcipher_sqlite3VdbeMemExpandBlob(pIn2) ) goto no_mem; - flags2 = pIn2->flags & ~MEM_Str; - } - nByte = pIn1->n + pIn2->n; - if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - if( sqlcipher_sqlite3VdbeMemGrow(pOut, (int)nByte+3, pOut==pIn2) ){ - goto no_mem; - } - MemSetTypeFlag(pOut, MEM_Str); - if( pOut!=pIn2 ){ - memcpy(pOut->z, pIn2->z, pIn2->n); - assert( (pIn2->flags & MEM_Dyn) == (flags2 & MEM_Dyn) ); - pIn2->flags = flags2; - } - memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n); - assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); - pIn1->flags = flags1; - pOut->z[nByte]=0; - pOut->z[nByte+1] = 0; - pOut->z[nByte+2] = 0; - pOut->flags |= MEM_Term; - pOut->n = (int)nByte; - pOut->enc = encoding; - UPDATE_MAX_BLOBSIZE(pOut); - break; -} + /* Copy as much data as is available in the buffer into the start of + ** p->aAlloc[]. */ + memcpy(p->aAlloc, &p->aBuffer[iBuf], nAvail); + p->iReadOff += nAvail; + nRem = nByte - nAvail; -/* Opcode: Add P1 P2 P3 * * -** Synopsis: r[P3]=r[P1]+r[P2] -** -** Add the value in register P1 to the value in register P2 -** and store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -/* Opcode: Multiply P1 P2 P3 * * -** Synopsis: r[P3]=r[P1]*r[P2] -** -** -** Multiply the value in register P1 by the value in register P2 -** and store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -/* Opcode: Subtract P1 P2 P3 * * -** Synopsis: r[P3]=r[P2]-r[P1] -** -** Subtract the value in register P1 from the value in register P2 -** and store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -/* Opcode: Divide P1 P2 P3 * * -** Synopsis: r[P3]=r[P2]/r[P1] -** -** Divide the value in register P1 by the value in register P2 -** and store the result in register P3 (P3=P2/P1). If the value in -** register P1 is zero, then the result is NULL. If either input is -** NULL, the result is NULL. -*/ -/* Opcode: Remainder P1 P2 P3 * * -** Synopsis: r[P3]=r[P2]%r[P1] -** -** Compute the remainder after integer register P2 is divided by -** register P1 and store the result in register P3. -** If the value in register P1 is zero the result is NULL. -** If either operand is NULL, the result is NULL. -*/ -case OP_Add: /* same as TK_PLUS, in1, in2, out3 */ -case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */ -case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */ -case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */ -case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ - u16 flags; /* Combined MEM_* flags from both inputs */ - u16 type1; /* Numeric type of left operand */ - u16 type2; /* Numeric type of right operand */ - i64 iA; /* Integer value of left operand */ - i64 iB; /* Integer value of right operand */ - double rA; /* Real value of left operand */ - double rB; /* Real value of right operand */ + /* The following loop copies up to p->nBuffer bytes per iteration into + ** the p->aAlloc[] buffer. */ + while( nRem>0 ){ + int rc; /* vdbePmaReadBlob() return code */ + int nCopy; /* Number of bytes to copy */ + u8 *aNext; /* Pointer to buffer to copy data from */ - pIn1 = &aMem[pOp->p1]; - type1 = numericType(pIn1); - pIn2 = &aMem[pOp->p2]; - type2 = numericType(pIn2); - pOut = &aMem[pOp->p3]; - flags = pIn1->flags | pIn2->flags; - if( (type1 & type2 & MEM_Int)!=0 ){ - iA = pIn1->u.i; - iB = pIn2->u.i; - switch( pOp->opcode ){ - case OP_Add: if( sqlcipher_sqlite3AddInt64(&iB,iA) ) goto fp_math; break; - case OP_Subtract: if( sqlcipher_sqlite3SubInt64(&iB,iA) ) goto fp_math; break; - case OP_Multiply: if( sqlcipher_sqlite3MulInt64(&iB,iA) ) goto fp_math; break; - case OP_Divide: { - if( iA==0 ) goto arithmetic_result_is_null; - if( iA==-1 && iB==SMALLEST_INT64 ) goto fp_math; - iB /= iA; - break; - } - default: { - if( iA==0 ) goto arithmetic_result_is_null; - if( iA==-1 ) iA = 1; - iB %= iA; - break; - } - } - pOut->u.i = iB; - MemSetTypeFlag(pOut, MEM_Int); - }else if( (flags & MEM_Null)!=0 ){ - goto arithmetic_result_is_null; - }else{ -fp_math: - rA = sqlcipher_sqlite3VdbeRealValue(pIn1); - rB = sqlcipher_sqlite3VdbeRealValue(pIn2); - switch( pOp->opcode ){ - case OP_Add: rB += rA; break; - case OP_Subtract: rB -= rA; break; - case OP_Multiply: rB *= rA; break; - case OP_Divide: { - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - if( rA==(double)0 ) goto arithmetic_result_is_null; - rB /= rA; - break; - } - default: { - iA = sqlcipher_sqlite3VdbeIntValue(pIn1); - iB = sqlcipher_sqlite3VdbeIntValue(pIn2); - if( iA==0 ) goto arithmetic_result_is_null; - if( iA==-1 ) iA = 1; - rB = (double)(iB % iA); - break; - } - } -#ifdef SQLITE_OMIT_FLOATING_POINT - pOut->u.i = rB; - MemSetTypeFlag(pOut, MEM_Int); -#else - if( sqlcipher_sqlite3IsNaN(rB) ){ - goto arithmetic_result_is_null; + nCopy = nRem; + if( nRem>p->nBuffer ) nCopy = p->nBuffer; + rc = vdbePmaReadBlob(p, nCopy, &aNext); + if( rc!=SQLITE_OK ) return rc; + assert( aNext!=p->aAlloc ); + memcpy(&p->aAlloc[nByte - nRem], aNext, nCopy); + nRem -= nCopy; } - pOut->u.r = rB; - MemSetTypeFlag(pOut, MEM_Real); -#endif + + *ppOut = p->aAlloc; } - break; -arithmetic_result_is_null: - sqlcipher_sqlite3VdbeMemSetNull(pOut); - break; + return SQLITE_OK; } -/* Opcode: CollSeq P1 * * P4 -** -** P4 is a pointer to a CollSeq object. If the next call to a user function -** or aggregate calls sqlcipher_sqlite3GetFuncCollSeq(), this collation sequence will -** be returned. This is used by the built-in min(), max() and nullif() -** functions. -** -** If P1 is not zero, then it is a register that a subsequent min() or -** max() aggregate will set to 1 if the current row is not the minimum or -** maximum. The P1 register is initialized to 0 by this instruction. -** -** The interface used by the implementation of the aforementioned functions -** to retrieve the collation sequence set by this opcode is not available -** publicly. Only built-in functions have access to this feature. +/* +** Read a varint from the stream of data accessed by p. Set *pnOut to +** the value read. */ -case OP_CollSeq: { - assert( pOp->p4type==P4_COLLSEQ ); - if( pOp->p1 ){ - sqlcipher_sqlite3VdbeMemSetInt64(&aMem[pOp->p1], 0); +static int vdbePmaReadVarint(PmaReader *p, u64 *pnOut){ + int iBuf; + + if( p->aMap ){ + p->iReadOff += sqlcipher_sqlite3GetVarint(&p->aMap[p->iReadOff], pnOut); + }else{ + iBuf = p->iReadOff % p->nBuffer; + if( iBuf && (p->nBuffer-iBuf)>=9 ){ + p->iReadOff += sqlcipher_sqlite3GetVarint(&p->aBuffer[iBuf], pnOut); + }else{ + u8 aVarint[16], *a; + int i = 0, rc; + do{ + rc = vdbePmaReadBlob(p, 1, &a); + if( rc ) return rc; + aVarint[(i++)&0xf] = a[0]; + }while( (a[0]&0x80)!=0 ); + sqlcipher_sqlite3GetVarint(aVarint, pnOut); + } } - break; + + return SQLITE_OK; } -/* Opcode: BitAnd P1 P2 P3 * * -** Synopsis: r[P3]=r[P1]&r[P2] -** -** Take the bit-wise AND of the values in register P1 and P2 and -** store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -/* Opcode: BitOr P1 P2 P3 * * -** Synopsis: r[P3]=r[P1]|r[P2] -** -** Take the bit-wise OR of the values in register P1 and P2 and -** store the result in register P3. -** If either input is NULL, the result is NULL. -*/ -/* Opcode: ShiftLeft P1 P2 P3 * * -** Synopsis: r[P3]=r[P2]<>r[P1] -** -** Shift the integer value in register P2 to the right by the -** number of bits specified by the integer in register P1. -** Store the result in register P3. -** If either input is NULL, the result is NULL. +static int vdbeSorterMapFile(SortSubtask *pTask, SorterFile *pFile, u8 **pp){ + int rc = SQLITE_OK; + if( pFile->iEof<=(i64)(pTask->pSorter->db->nMaxSorterMmap) ){ + sqlcipher_sqlite3_file *pFd = pFile->pFd; + if( pFd->pMethods->iVersion>=3 ){ + rc = sqlcipher_sqlite3OsFetch(pFd, 0, (int)pFile->iEof, (void**)pp); + testcase( rc!=SQLITE_OK ); + } + } + return rc; +} + +/* +** Attach PmaReader pReadr to file pFile (if it is not already attached to +** that file) and seek it to offset iOff within the file. Return SQLITE_OK +** if successful, or an SQLite error code if an error occurs. */ -case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */ -case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */ -case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */ -case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ - i64 iA; - u64 uA; - i64 iB; - u8 op; +static int vdbePmaReaderSeek( + SortSubtask *pTask, /* Task context */ + PmaReader *pReadr, /* Reader whose cursor is to be moved */ + SorterFile *pFile, /* Sorter file to read from */ + i64 iOff /* Offset in pFile */ +){ + int rc = SQLITE_OK; - pIn1 = &aMem[pOp->p1]; - pIn2 = &aMem[pOp->p2]; - pOut = &aMem[pOp->p3]; - if( (pIn1->flags | pIn2->flags) & MEM_Null ){ - sqlcipher_sqlite3VdbeMemSetNull(pOut); - break; + assert( pReadr->pIncr==0 || pReadr->pIncr->bEof==0 ); + + if( sqlcipher_sqlite3FaultSim(201) ) return SQLITE_IOERR_READ; + if( pReadr->aMap ){ + sqlcipher_sqlite3OsUnfetch(pReadr->pFd, 0, pReadr->aMap); + pReadr->aMap = 0; } - iA = sqlcipher_sqlite3VdbeIntValue(pIn2); - iB = sqlcipher_sqlite3VdbeIntValue(pIn1); - op = pOp->opcode; - if( op==OP_BitAnd ){ - iA &= iB; - }else if( op==OP_BitOr ){ - iA |= iB; - }else if( iB!=0 ){ - assert( op==OP_ShiftRight || op==OP_ShiftLeft ); + pReadr->iReadOff = iOff; + pReadr->iEof = pFile->iEof; + pReadr->pFd = pFile->pFd; - /* If shifting by a negative amount, shift in the other direction */ - if( iB<0 ){ - assert( OP_ShiftRight==OP_ShiftLeft+1 ); - op = 2*OP_ShiftLeft + 1 - op; - iB = iB>(-64) ? -iB : 64; + rc = vdbeSorterMapFile(pTask, pFile, &pReadr->aMap); + if( rc==SQLITE_OK && pReadr->aMap==0 ){ + int pgsz = pTask->pSorter->pgsz; + int iBuf = pReadr->iReadOff % pgsz; + if( pReadr->aBuffer==0 ){ + pReadr->aBuffer = (u8*)sqlcipher_sqlite3Malloc(pgsz); + if( pReadr->aBuffer==0 ) rc = SQLITE_NOMEM_BKPT; + pReadr->nBuffer = pgsz; } - - if( iB>=64 ){ - iA = (iA>=0 || op==OP_ShiftLeft) ? 0 : -1; - }else{ - memcpy(&uA, &iA, sizeof(uA)); - if( op==OP_ShiftLeft ){ - uA <<= iB; - }else{ - uA >>= iB; - /* Sign-extend on a right shift of a negative number */ - if( iA<0 ) uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-iB); + if( rc==SQLITE_OK && iBuf ){ + int nRead = pgsz - iBuf; + if( (pReadr->iReadOff + nRead) > pReadr->iEof ){ + nRead = (int)(pReadr->iEof - pReadr->iReadOff); } - memcpy(&iA, &uA, sizeof(iA)); + rc = sqlcipher_sqlite3OsRead( + pReadr->pFd, &pReadr->aBuffer[iBuf], nRead, pReadr->iReadOff + ); + testcase( rc!=SQLITE_OK ); } } - pOut->u.i = iA; - MemSetTypeFlag(pOut, MEM_Int); - break; -} -/* Opcode: AddImm P1 P2 * * * -** Synopsis: r[P1]=r[P1]+P2 -** -** Add the constant P2 to the value in register P1. -** The result is always an integer. -** -** To force any register to be an integer, just add 0. -*/ -case OP_AddImm: { /* in1 */ - pIn1 = &aMem[pOp->p1]; - memAboutToChange(p, pIn1); - sqlcipher_sqlite3VdbeMemIntegerify(pIn1); - pIn1->u.i += pOp->p2; - break; + return rc; } -/* Opcode: MustBeInt P1 P2 * * * -** -** Force the value in register P1 to be an integer. If the value -** in P1 is not an integer and cannot be converted into an integer -** without data loss, then jump immediately to P2, or if P2==0 -** raise an SQLITE_MISMATCH exception. +/* +** Advance PmaReader pReadr to the next key in its PMA. Return SQLITE_OK if +** no error occurs, or an SQLite error code if one does. */ -case OP_MustBeInt: { /* jump, in1 */ - pIn1 = &aMem[pOp->p1]; - if( (pIn1->flags & MEM_Int)==0 ){ - applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); - if( (pIn1->flags & MEM_Int)==0 ){ - VdbeBranchTaken(1, 2); - if( pOp->p2==0 ){ - rc = SQLITE_MISMATCH; - goto abort_due_to_error; - }else{ - goto jump_to_p2; +static int vdbePmaReaderNext(PmaReader *pReadr){ + int rc = SQLITE_OK; /* Return Code */ + u64 nRec = 0; /* Size of record in bytes */ + + + if( pReadr->iReadOff>=pReadr->iEof ){ + IncrMerger *pIncr = pReadr->pIncr; + int bEof = 1; + if( pIncr ){ + rc = vdbeIncrSwap(pIncr); + if( rc==SQLITE_OK && pIncr->bEof==0 ){ + rc = vdbePmaReaderSeek( + pIncr->pTask, pReadr, &pIncr->aFile[0], pIncr->iStartOff + ); + bEof = 0; } } + + if( bEof ){ + /* This is an EOF condition */ + vdbePmaReaderClear(pReadr); + testcase( rc!=SQLITE_OK ); + return rc; + } } - VdbeBranchTaken(0, 2); - MemSetTypeFlag(pIn1, MEM_Int); - break; + + if( rc==SQLITE_OK ){ + rc = vdbePmaReadVarint(pReadr, &nRec); + } + if( rc==SQLITE_OK ){ + pReadr->nKey = (int)nRec; + rc = vdbePmaReadBlob(pReadr, (int)nRec, &pReadr->aKey); + testcase( rc!=SQLITE_OK ); + } + + return rc; } -#ifndef SQLITE_OMIT_FLOATING_POINT -/* Opcode: RealAffinity P1 * * * * -** -** If register P1 holds an integer convert it to a real value. +/* +** Initialize PmaReader pReadr to scan through the PMA stored in file pFile +** starting at offset iStart and ending at offset iEof-1. This function +** leaves the PmaReader pointing to the first key in the PMA (or EOF if the +** PMA is empty). ** -** This opcode is used when extracting information from a column that -** has REAL affinity. Such column values may still be stored as -** integers, for space efficiency, but after extraction we want them -** to have only a real value. +** If the pnByte parameter is NULL, then it is assumed that the file +** contains a single PMA, and that that PMA omits the initial length varint. */ -case OP_RealAffinity: { /* in1 */ - pIn1 = &aMem[pOp->p1]; - if( pIn1->flags & (MEM_Int|MEM_IntReal) ){ - testcase( pIn1->flags & MEM_Int ); - testcase( pIn1->flags & MEM_IntReal ); - sqlcipher_sqlite3VdbeMemRealify(pIn1); - REGISTER_TRACE(pOp->p1, pIn1); +static int vdbePmaReaderInit( + SortSubtask *pTask, /* Task context */ + SorterFile *pFile, /* Sorter file to read from */ + i64 iStart, /* Start offset in pFile */ + PmaReader *pReadr, /* PmaReader to populate */ + i64 *pnByte /* IN/OUT: Increment this value by PMA size */ +){ + int rc; + + assert( pFile->iEof>iStart ); + assert( pReadr->aAlloc==0 && pReadr->nAlloc==0 ); + assert( pReadr->aBuffer==0 ); + assert( pReadr->aMap==0 ); + + rc = vdbePmaReaderSeek(pTask, pReadr, pFile, iStart); + if( rc==SQLITE_OK ){ + u64 nByte = 0; /* Size of PMA in bytes */ + rc = vdbePmaReadVarint(pReadr, &nByte); + pReadr->iEof = pReadr->iReadOff + nByte; + *pnByte += nByte; } - break; + + if( rc==SQLITE_OK ){ + rc = vdbePmaReaderNext(pReadr); + } + return rc; } -#endif -#ifndef SQLITE_OMIT_CAST -/* Opcode: Cast P1 P2 * * * -** Synopsis: affinity(r[P1]) -** -** Force the value in register P1 to be the type defined by P2. -** -**
      -**
    • P2=='A' → BLOB -**
    • P2=='B' → TEXT -**
    • P2=='C' → NUMERIC -**
    • P2=='D' → INTEGER -**
    • P2=='E' → REAL -**
    -** -** A NULL value is not changed by this routine. It remains NULL. +/* +** A version of vdbeSorterCompare() that assumes that it has already been +** determined that the first field of key1 is equal to the first field of +** key2. */ -case OP_Cast: { /* in1 */ - assert( pOp->p2>=SQLITE_AFF_BLOB && pOp->p2<=SQLITE_AFF_REAL ); - testcase( pOp->p2==SQLITE_AFF_TEXT ); - testcase( pOp->p2==SQLITE_AFF_BLOB ); - testcase( pOp->p2==SQLITE_AFF_NUMERIC ); - testcase( pOp->p2==SQLITE_AFF_INTEGER ); - testcase( pOp->p2==SQLITE_AFF_REAL ); - pIn1 = &aMem[pOp->p1]; - memAboutToChange(p, pIn1); - rc = ExpandBlob(pIn1); - if( rc ) goto abort_due_to_error; - rc = sqlcipher_sqlite3VdbeMemCast(pIn1, pOp->p2, encoding); - if( rc ) goto abort_due_to_error; - UPDATE_MAX_BLOBSIZE(pIn1); - REGISTER_TRACE(pOp->p1, pIn1); - break; +static int vdbeSorterCompareTail( + SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ + const void *pKey1, int nKey1, /* Left side of comparison */ + const void *pKey2, int nKey2 /* Right side of comparison */ +){ + UnpackedRecord *r2 = pTask->pUnpacked; + if( *pbKey2Cached==0 ){ + sqlcipher_sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2); + *pbKey2Cached = 1; + } + return sqlcipher_sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, r2, 1); } -#endif /* SQLITE_OMIT_CAST */ -/* Opcode: Eq P1 P2 P3 P4 P5 -** Synopsis: IF r[P3]==r[P1] -** -** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then -** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then -** store the result of comparison in register P2. -** -** The SQLITE_AFF_MASK portion of P5 must be an affinity character - -** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made -** to coerce both inputs according to this affinity before the -** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric -** affinity is used. Note that the affinity conversions are stored -** back into the input registers P1 and P3. So this opcode can cause -** persistent changes to registers P1 and P3. -** -** Once any conversions have taken place, and neither value is NULL, -** the values are compared. If both values are blobs then memcmp() is -** used to determine the results of the comparison. If both values -** are text, then the appropriate collating function specified in -** P4 is used to do the comparison. If P4 is not specified then -** memcmp() is used to compare text string. If both values are -** numeric, then a numeric comparison is used. If the two values -** are of different types, then numbers are considered less than -** strings and strings are considered less than blobs. -** -** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either -** true or false and is never NULL. If both operands are NULL then the result -** of comparison is true. If either operand is NULL then the result is false. -** If neither operand is NULL the result is the same as it would be if -** the SQLITE_NULLEQ flag were omitted from P5. -** -** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the -** content of r[P2] is only changed if the new value is NULL or 0 (false). -** In other words, a prior r[P2] value will not be overwritten by 1 (true). -*/ -/* Opcode: Ne P1 P2 P3 P4 P5 -** Synopsis: IF r[P3]!=r[P1] -** -** This works just like the Eq opcode except that the jump is taken if -** the operands in registers P1 and P3 are not equal. See the Eq opcode for -** additional information. -** -** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the -** content of r[P2] is only changed if the new value is NULL or 1 (true). -** In other words, a prior r[P2] value will not be overwritten by 0 (false). -*/ -/* Opcode: Lt P1 P2 P3 P4 P5 -** Synopsis: IF r[P3]pKeyInfo) for the collation sequences +** used by the comparison. Return the result of the comparison. ** -** This works just like the Lt opcode except that the jump is taken if -** the content of register P3 is less than or equal to the content of -** register P1. See the Lt opcode for additional information. -*/ -/* Opcode: Gt P1 P2 P3 P4 P5 -** Synopsis: IF r[P3]>r[P1] +** If IN/OUT parameter *pbKey2Cached is true when this function is called, +** it is assumed that (pTask->pUnpacked) contains the unpacked version +** of key2. If it is false, (pTask->pUnpacked) is populated with the unpacked +** version of key2 and *pbKey2Cached set to true before returning. ** -** This works just like the Lt opcode except that the jump is taken if -** the content of register P3 is greater than the content of -** register P1. See the Lt opcode for additional information. +** If an OOM error is encountered, (pTask->pUnpacked->error_rc) is set +** to SQLITE_NOMEM. */ -/* Opcode: Ge P1 P2 P3 P4 P5 -** Synopsis: IF r[P3]>=r[P1] -** -** This works just like the Lt opcode except that the jump is taken if -** the content of register P3 is greater than or equal to the content of -** register P1. See the Lt opcode for additional information. +static int vdbeSorterCompare( + SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ + const void *pKey1, int nKey1, /* Left side of comparison */ + const void *pKey2, int nKey2 /* Right side of comparison */ +){ + UnpackedRecord *r2 = pTask->pUnpacked; + if( !*pbKey2Cached ){ + sqlcipher_sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2); + *pbKey2Cached = 1; + } + return sqlcipher_sqlite3VdbeRecordCompare(nKey1, pKey1, r2); +} + +/* +** A specially optimized version of vdbeSorterCompare() that assumes that +** the first field of each key is a TEXT value and that the collation +** sequence to compare them with is BINARY. */ -case OP_Eq: /* same as TK_EQ, jump, in1, in3 */ -case OP_Ne: /* same as TK_NE, jump, in1, in3 */ -case OP_Lt: /* same as TK_LT, jump, in1, in3 */ -case OP_Le: /* same as TK_LE, jump, in1, in3 */ -case OP_Gt: /* same as TK_GT, jump, in1, in3 */ -case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ - int res, res2; /* Result of the comparison of pIn1 against pIn3 */ - char affinity; /* Affinity to use for comparison */ - u16 flags1; /* Copy of initial value of pIn1->flags */ - u16 flags3; /* Copy of initial value of pIn3->flags */ +static int vdbeSorterCompareText( + SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ + const void *pKey1, int nKey1, /* Left side of comparison */ + const void *pKey2, int nKey2 /* Right side of comparison */ +){ + const u8 * const p1 = (const u8 * const)pKey1; + const u8 * const p2 = (const u8 * const)pKey2; + const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ + const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ - pIn1 = &aMem[pOp->p1]; - pIn3 = &aMem[pOp->p3]; - flags1 = pIn1->flags; - flags3 = pIn3->flags; - if( (flags1 | flags3)&MEM_Null ){ - /* One or both operands are NULL */ - if( pOp->p5 & SQLITE_NULLEQ ){ - /* If SQLITE_NULLEQ is set (which will only happen if the operator is - ** OP_Eq or OP_Ne) then take the jump or not depending on whether - ** or not both operands are null. - */ - assert( (flags1 & MEM_Cleared)==0 ); - assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 || CORRUPT_DB ); - testcase( (pOp->p5 & SQLITE_JUMPIFNULL)!=0 ); - if( (flags1&flags3&MEM_Null)!=0 - && (flags3&MEM_Cleared)==0 - ){ - res = 0; /* Operands are equal */ - }else{ - res = ((flags3 & MEM_Null) ? -1 : +1); /* Operands are not equal */ - } - }else{ - /* SQLITE_NULLEQ is clear and at least one operand is NULL, - ** then the result is always NULL. - ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. - */ - if( pOp->p5 & SQLITE_STOREP2 ){ - pOut = &aMem[pOp->p2]; - iCompare = 1; /* Operands are not equal */ - memAboutToChange(p, pOut); - MemSetTypeFlag(pOut, MEM_Null); - REGISTER_TRACE(pOp->p2, pOut); - }else{ - VdbeBranchTaken(2,3); - if( pOp->p5 & SQLITE_JUMPIFNULL ){ - goto jump_to_p2; - } - } - break; - } - }else{ - /* Neither operand is NULL. Do a comparison. */ - affinity = pOp->p5 & SQLITE_AFF_MASK; - if( affinity>=SQLITE_AFF_NUMERIC ){ - if( (flags1 | flags3)&MEM_Str ){ - if( (flags1 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ - applyNumericAffinity(pIn1,0); - testcase( flags3==pIn3->flags ); - flags3 = pIn3->flags; - } - if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ - applyNumericAffinity(pIn3,0); - } - } - /* Handle the common case of integer comparison here, as an - ** optimization, to avoid a call to sqlcipher_sqlite3MemCompare() */ - if( (pIn1->flags & pIn3->flags & MEM_Int)!=0 ){ - if( pIn3->u.i > pIn1->u.i ){ res = +1; goto compare_op; } - if( pIn3->u.i < pIn1->u.i ){ res = -1; goto compare_op; } - res = 0; - goto compare_op; - } - }else if( affinity==SQLITE_AFF_TEXT ){ - if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ - testcase( pIn1->flags & MEM_Int ); - testcase( pIn1->flags & MEM_Real ); - testcase( pIn1->flags & MEM_IntReal ); - sqlcipher_sqlite3VdbeMemStringify(pIn1, encoding, 1); - testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) ); - flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); - if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str; - } - if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ - testcase( pIn3->flags & MEM_Int ); - testcase( pIn3->flags & MEM_Real ); - testcase( pIn3->flags & MEM_IntReal ); - sqlcipher_sqlite3VdbeMemStringify(pIn3, encoding, 1); - testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn) ); - flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask); - } - } - assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); - res = sqlcipher_sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); - } -compare_op: - /* At this point, res is negative, zero, or positive if reg[P1] is - ** less than, equal to, or greater than reg[P3], respectively. Compute - ** the answer to this operator in res2, depending on what the comparison - ** operator actually is. The next block of code depends on the fact - ** that the 6 comparison operators are consecutive integers in this - ** order: NE, EQ, GT, LE, LT, GE */ - assert( OP_Eq==OP_Ne+1 ); assert( OP_Gt==OP_Ne+2 ); assert( OP_Le==OP_Ne+3 ); - assert( OP_Lt==OP_Ne+4 ); assert( OP_Ge==OP_Ne+5 ); - if( res<0 ){ /* ne, eq, gt, le, lt, ge */ - static const unsigned char aLTb[] = { 1, 0, 0, 1, 1, 0 }; - res2 = aLTb[pOp->opcode - OP_Ne]; - }else if( res==0 ){ - static const unsigned char aEQb[] = { 0, 1, 0, 1, 0, 1 }; - res2 = aEQb[pOp->opcode - OP_Ne]; - }else{ - static const unsigned char aGTb[] = { 1, 0, 1, 0, 0, 1 }; - res2 = aGTb[pOp->opcode - OP_Ne]; - } + int n1; + int n2; + int res; - /* Undo any changes made by applyAffinity() to the input registers. */ - assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) ); - pIn3->flags = flags3; - assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); - pIn1->flags = flags1; + getVarint32NR(&p1[1], n1); + getVarint32NR(&p2[1], n2); + res = memcmp(v1, v2, (MIN(n1, n2) - 13)/2); + if( res==0 ){ + res = n1 - n2; + } - if( pOp->p5 & SQLITE_STOREP2 ){ - pOut = &aMem[pOp->p2]; - iCompare = res; - if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){ - /* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1 - ** and prevents OP_Ne from overwriting NULL with 0. This flag - ** is only used in contexts where either: - ** (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0) - ** (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1) - ** Therefore it is not necessary to check the content of r[P2] for - ** NULL. */ - assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq ); - assert( res2==0 || res2==1 ); - testcase( res2==0 && pOp->opcode==OP_Eq ); - testcase( res2==1 && pOp->opcode==OP_Eq ); - testcase( res2==0 && pOp->opcode==OP_Ne ); - testcase( res2==1 && pOp->opcode==OP_Ne ); - if( (pOp->opcode==OP_Eq)==res2 ) break; + if( res==0 ){ + if( pTask->pSorter->pKeyInfo->nKeyField>1 ){ + res = vdbeSorterCompareTail( + pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2 + ); } - memAboutToChange(p, pOut); - MemSetTypeFlag(pOut, MEM_Int); - pOut->u.i = res2; - REGISTER_TRACE(pOp->p2, pOut); }else{ - VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); - if( res2 ){ - goto jump_to_p2; + assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) ); + if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){ + res = res * -1; } } - break; + + return res; } -/* Opcode: ElseNotEq * P2 * * * -** -** This opcode must follow an OP_Lt or OP_Gt comparison operator. There -** can be zero or more OP_ReleaseReg opcodes intervening, but no other -** opcodes are allowed to occur between this instruction and the previous -** OP_Lt or OP_Gt. Furthermore, the prior OP_Lt or OP_Gt must have the -** SQLITE_STOREP2 bit set in the P5 field. -** -** If result of an OP_Eq comparison on the same two operands as the -** prior OP_Lt or OP_Gt would have been NULL or false (0), then then -** jump to P2. If the result of an OP_Eq comparison on the two previous -** operands would have been true (1), then fall through. +/* +** A specially optimized version of vdbeSorterCompare() that assumes that +** the first field of each key is an INTEGER value. */ -case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */ +static int vdbeSorterCompareInt( + SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ + const void *pKey1, int nKey1, /* Left side of comparison */ + const void *pKey2, int nKey2 /* Right side of comparison */ +){ + const u8 * const p1 = (const u8 * const)pKey1; + const u8 * const p2 = (const u8 * const)pKey2; + const int s1 = p1[1]; /* Left hand serial type */ + const int s2 = p2[1]; /* Right hand serial type */ + const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ + const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ + int res; /* Return value */ -#ifdef SQLITE_DEBUG - /* Verify the preconditions of this opcode - that it follows an OP_Lt or - ** OP_Gt with the SQLITE_STOREP2 flag set, with zero or more intervening - ** OP_ReleaseReg opcodes */ - int iAddr; - for(iAddr = (int)(pOp - aOp) - 1; ALWAYS(iAddr>=0); iAddr--){ - if( aOp[iAddr].opcode==OP_ReleaseReg ) continue; - assert( aOp[iAddr].opcode==OP_Lt || aOp[iAddr].opcode==OP_Gt ); - assert( aOp[iAddr].p5 & SQLITE_STOREP2 ); - break; + assert( (s1>0 && s1<7) || s1==8 || s1==9 ); + assert( (s2>0 && s2<7) || s2==8 || s2==9 ); + + if( s1==s2 ){ + /* The two values have the same sign. Compare using memcmp(). */ + static const u8 aLen[] = {0, 1, 2, 3, 4, 6, 8, 0, 0, 0 }; + const u8 n = aLen[s1]; + int i; + res = 0; + for(i=0; i7 && s2>7 ){ + res = s1 - s2; + }else{ + if( s2>7 ){ + res = +1; + }else if( s1>7 ){ + res = -1; + }else{ + res = s1 - s2; + } + assert( res!=0 ); + + if( res>0 ){ + if( *v1 & 0x80 ) res = -1; + }else{ + if( *v2 & 0x80 ) res = +1; + } } -#endif /* SQLITE_DEBUG */ - VdbeBranchTaken(iCompare!=0, 2); - if( iCompare!=0 ) goto jump_to_p2; - break; -} + if( res==0 ){ + if( pTask->pSorter->pKeyInfo->nKeyField>1 ){ + res = vdbeSorterCompareTail( + pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2 + ); + } + }else if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){ + assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) ); + res = res * -1; + } -/* Opcode: Permutation * * * P4 * -** -** Set the permutation used by the OP_Compare operator in the next -** instruction. The permutation is stored in the P4 operand. -** -** The permutation is only valid until the next OP_Compare that has -** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should -** occur immediately prior to the OP_Compare. -** -** The first integer in the P4 integer array is the length of the array -** and does not become part of the permutation. -*/ -case OP_Permutation: { - assert( pOp->p4type==P4_INTARRAY ); - assert( pOp->p4.ai ); - assert( pOp[1].opcode==OP_Compare ); - assert( pOp[1].p5 & OPFLAG_PERMUTE ); - break; + return res; } -/* Opcode: Compare P1 P2 P3 P4 P5 -** Synopsis: r[P1@P3] <-> r[P2@P3] -** -** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this -** vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of -** the comparison for use by the next OP_Jump instruct. +/* +** Initialize the temporary index cursor just opened as a sorter cursor. ** -** If P5 has the OPFLAG_PERMUTE bit set, then the order of comparison is -** determined by the most recent OP_Permutation operator. If the -** OPFLAG_PERMUTE bit is clear, then register are compared in sequential -** order. +** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nKeyField) +** to determine the number of fields that should be compared from the +** records being sorted. However, if the value passed as argument nField +** is non-zero and the sorter is able to guarantee a stable sort, nField +** is used instead. This is used when sorting records for a CREATE INDEX +** statement. In this case, keys are always delivered to the sorter in +** order of the primary key, which happens to be make up the final part +** of the records being sorted. So if the sort is stable, there is never +** any reason to compare PK fields and they can be ignored for a small +** performance boost. ** -** P4 is a KeyInfo structure that defines collating sequences and sort -** orders for the comparison. The permutation applies to registers -** only. The KeyInfo elements are used sequentially. +** The sorter can guarantee a stable sort when running in single-threaded +** mode, but not in multi-threaded mode. ** -** The comparison is a sort comparison, so NULLs compare equal, -** NULLs are less than numbers, numbers are less than strings, -** and strings are less than blobs. +** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ -case OP_Compare: { - int n; - int i; - int p1; - int p2; - const KeyInfo *pKeyInfo; - u32 idx; - CollSeq *pColl; /* Collating sequence to use on this term */ - int bRev; /* True for DESCENDING sort order */ - u32 *aPermute; /* The permutation */ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterInit( + sqlcipher_sqlite3 *db, /* Database connection (for malloc()) */ + int nField, /* Number of key fields in each record */ + VdbeCursor *pCsr /* Cursor that holds the new sorter */ +){ + int pgsz; /* Page size of main database */ + int i; /* Used to iterate through aTask[] */ + VdbeSorter *pSorter; /* The new sorter */ + KeyInfo *pKeyInfo; /* Copy of pCsr->pKeyInfo with db==0 */ + int szKeyInfo; /* Size of pCsr->pKeyInfo in bytes */ + int sz; /* Size of pSorter in bytes */ + int rc = SQLITE_OK; +#if SQLITE_MAX_WORKER_THREADS==0 +# define nWorker 0 +#else + int nWorker; +#endif - if( (pOp->p5 & OPFLAG_PERMUTE)==0 ){ - aPermute = 0; + /* Initialize the upper limit on the number of worker threads */ +#if SQLITE_MAX_WORKER_THREADS>0 + if( sqlcipher_sqlite3TempInMemory(db) || sqlcipher_sqlite3GlobalConfig.bCoreMutex==0 ){ + nWorker = 0; }else{ - assert( pOp>aOp ); - assert( pOp[-1].opcode==OP_Permutation ); - assert( pOp[-1].p4type==P4_INTARRAY ); - aPermute = pOp[-1].p4.ai + 1; - assert( aPermute!=0 ); + nWorker = db->aLimit[SQLITE_LIMIT_WORKER_THREADS]; } - n = pOp->p3; - pKeyInfo = pOp->p4.pKeyInfo; - assert( n>0 ); - assert( pKeyInfo!=0 ); - p1 = pOp->p1; - p2 = pOp->p2; -#ifdef SQLITE_DEBUG - if( aPermute ){ - int k, mx = 0; - for(k=0; k(u32)mx ) mx = aPermute[k]; - assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 ); - assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 ); - }else{ - assert( p1>0 && p1+n<=(p->nMem+1 - p->nCursor)+1 ); - assert( p2>0 && p2+n<=(p->nMem+1 - p->nCursor)+1 ); +#endif + + /* Do not allow the total number of threads (main thread + all workers) + ** to exceed the maximum merge count */ +#if SQLITE_MAX_WORKER_THREADS>=SORTER_MAX_MERGE_COUNT + if( nWorker>=SORTER_MAX_MERGE_COUNT ){ + nWorker = SORTER_MAX_MERGE_COUNT-1; } -#endif /* SQLITE_DEBUG */ - for(i=0; inKeyField ); - pColl = pKeyInfo->aColl[i]; - bRev = (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC); - iCompare = sqlcipher_sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl); - if( iCompare ){ - if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) - && ((aMem[p1+idx].flags & MEM_Null) || (aMem[p2+idx].flags & MEM_Null)) - ){ - iCompare = -iCompare; +#endif + + assert( pCsr->pKeyInfo ); + assert( !pCsr->isEphemeral ); + assert( pCsr->eCurType==CURTYPE_SORTER ); + szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*); + sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); + + pSorter = (VdbeSorter*)sqlcipher_sqlite3DbMallocZero(db, sz + szKeyInfo); + pCsr->uc.pSorter = pSorter; + if( pSorter==0 ){ + rc = SQLITE_NOMEM_BKPT; + }else{ + Btree *pBt = db->aDb[0].pBt; + pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz); + memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo); + pKeyInfo->db = 0; + if( nField && nWorker==0 ){ + pKeyInfo->nKeyField = nField; + } + sqlcipher_sqlite3BtreeEnter(pBt); + pSorter->pgsz = pgsz = sqlcipher_sqlite3BtreeGetPageSize(pBt); + sqlcipher_sqlite3BtreeLeave(pBt); + pSorter->nTask = nWorker + 1; + pSorter->iPrev = (u8)(nWorker - 1); + pSorter->bUseThreads = (pSorter->nTask>1); + pSorter->db = db; + for(i=0; inTask; i++){ + SortSubtask *pTask = &pSorter->aTask[i]; + pTask->pSorter = pSorter; + } + + if( !sqlcipher_sqlite3TempInMemory(db) ){ + i64 mxCache; /* Cache size in bytes*/ + u32 szPma = sqlcipher_sqlite3GlobalConfig.szPma; + pSorter->mnPmaSize = szPma * pgsz; + + mxCache = db->aDb[0].pSchema->cache_size; + if( mxCache<0 ){ + /* A negative cache-size value C indicates that the cache is abs(C) + ** KiB in size. */ + mxCache = mxCache * -1024; + }else{ + mxCache = mxCache * pgsz; } - if( bRev ) iCompare = -iCompare; - break; + mxCache = MIN(mxCache, SQLITE_MAX_PMASZ); + pSorter->mxPmaSize = MAX(pSorter->mnPmaSize, (int)mxCache); + + /* Avoid large memory allocations if the application has requested + ** SQLITE_CONFIG_SMALL_MALLOC. */ + if( sqlcipher_sqlite3GlobalConfig.bSmallMalloc==0 ){ + assert( pSorter->iMemory==0 ); + pSorter->nMemory = pgsz; + pSorter->list.aMemory = (u8*)sqlcipher_sqlite3Malloc(pgsz); + if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM_BKPT; + } + } + + if( pKeyInfo->nAllField<13 + && (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl) + && (pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL)==0 + ){ + pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT; } } - break; + + return rc; } +#undef nWorker /* Defined at the top of this function */ -/* Opcode: Jump P1 P2 P3 * * -** -** Jump to the instruction at address P1, P2, or P3 depending on whether -** in the most recent OP_Compare instruction the P1 vector was less than -** equal to, or greater than the P2 vector, respectively. +/* +** Free the list of sorted records starting at pRecord. */ -case OP_Jump: { /* jump */ - if( iCompare<0 ){ - VdbeBranchTaken(0,4); pOp = &aOp[pOp->p1 - 1]; - }else if( iCompare==0 ){ - VdbeBranchTaken(1,4); pOp = &aOp[pOp->p2 - 1]; - }else{ - VdbeBranchTaken(2,4); pOp = &aOp[pOp->p3 - 1]; +static void vdbeSorterRecordFree(sqlcipher_sqlite3 *db, SorterRecord *pRecord){ + SorterRecord *p; + SorterRecord *pNext; + for(p=pRecord; p; p=pNext){ + pNext = p->u.pNext; + sqlcipher_sqlite3DbFree(db, p); } - break; } -/* Opcode: And P1 P2 P3 * * -** Synopsis: r[P3]=(r[P1] && r[P2]) -** -** Take the logical AND of the values in registers P1 and P2 and -** write the result into register P3. -** -** If either P1 or P2 is 0 (false) then the result is 0 even if -** the other input is NULL. A NULL and true or two NULLs give -** a NULL output. -*/ -/* Opcode: Or P1 P2 P3 * * -** Synopsis: r[P3]=(r[P1] || r[P2]) -** -** Take the logical OR of the values in register P1 and P2 and -** store the answer in register P3. -** -** If either P1 or P2 is nonzero (true) then the result is 1 (true) -** even if the other input is NULL. A NULL and false or two NULLs -** give a NULL output. +/* +** Free all resources owned by the object indicated by argument pTask. All +** fields of *pTask are zeroed before returning. */ -case OP_And: /* same as TK_AND, in1, in2, out3 */ -case OP_Or: { /* same as TK_OR, in1, in2, out3 */ - int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ - int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ +static void vdbeSortSubtaskCleanup(sqlcipher_sqlite3 *db, SortSubtask *pTask){ + sqlcipher_sqlite3DbFree(db, pTask->pUnpacked); +#if SQLITE_MAX_WORKER_THREADS>0 + /* pTask->list.aMemory can only be non-zero if it was handed memory + ** from the main thread. That only occurs SQLITE_MAX_WORKER_THREADS>0 */ + if( pTask->list.aMemory ){ + sqlcipher_sqlite3_free(pTask->list.aMemory); + }else +#endif + { + assert( pTask->list.aMemory==0 ); + vdbeSorterRecordFree(0, pTask->list.pList); + } + if( pTask->file.pFd ){ + sqlcipher_sqlite3OsCloseFree(pTask->file.pFd); + } + if( pTask->file2.pFd ){ + sqlcipher_sqlite3OsCloseFree(pTask->file2.pFd); + } + memset(pTask, 0, sizeof(SortSubtask)); +} - v1 = sqlcipher_sqlite3VdbeBooleanValue(&aMem[pOp->p1], 2); - v2 = sqlcipher_sqlite3VdbeBooleanValue(&aMem[pOp->p2], 2); - if( pOp->opcode==OP_And ){ - static const unsigned char and_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; - v1 = and_logic[v1*3+v2]; - }else{ - static const unsigned char or_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 }; - v1 = or_logic[v1*3+v2]; +#ifdef SQLITE_DEBUG_SORTER_THREADS +static void vdbeSorterWorkDebug(SortSubtask *pTask, const char *zEvent){ + i64 t; + int iTask = (pTask - pTask->pSorter->aTask); + sqlcipher_sqlite3OsCurrentTimeInt64(pTask->pSorter->db->pVfs, &t); + fprintf(stderr, "%lld:%d %s\n", t, iTask, zEvent); +} +static void vdbeSorterRewindDebug(const char *zEvent){ + i64 t = 0; + sqlcipher_sqlite3_vfs *pVfs = sqlcipher_sqlite3_vfs_find(0); + if( ALWAYS(pVfs) ) sqlcipher_sqlite3OsCurrentTimeInt64(pVfs, &t); + fprintf(stderr, "%lld:X %s\n", t, zEvent); +} +static void vdbeSorterPopulateDebug( + SortSubtask *pTask, + const char *zEvent +){ + i64 t; + int iTask = (pTask - pTask->pSorter->aTask); + sqlcipher_sqlite3OsCurrentTimeInt64(pTask->pSorter->db->pVfs, &t); + fprintf(stderr, "%lld:bg%d %s\n", t, iTask, zEvent); +} +static void vdbeSorterBlockDebug( + SortSubtask *pTask, + int bBlocked, + const char *zEvent +){ + if( bBlocked ){ + i64 t; + sqlcipher_sqlite3OsCurrentTimeInt64(pTask->pSorter->db->pVfs, &t); + fprintf(stderr, "%lld:main %s\n", t, zEvent); } - pOut = &aMem[pOp->p3]; - if( v1==2 ){ - MemSetTypeFlag(pOut, MEM_Null); - }else{ - pOut->u.i = v1; - MemSetTypeFlag(pOut, MEM_Int); +} +#else +# define vdbeSorterWorkDebug(x,y) +# define vdbeSorterRewindDebug(y) +# define vdbeSorterPopulateDebug(x,y) +# define vdbeSorterBlockDebug(x,y,z) +#endif + +#if SQLITE_MAX_WORKER_THREADS>0 +/* +** Join thread pTask->thread. +*/ +static int vdbeSorterJoinThread(SortSubtask *pTask){ + int rc = SQLITE_OK; + if( pTask->pThread ){ +#ifdef SQLITE_DEBUG_SORTER_THREADS + int bDone = pTask->bDone; +#endif + void *pRet = SQLITE_INT_TO_PTR(SQLITE_ERROR); + vdbeSorterBlockDebug(pTask, !bDone, "enter"); + (void)sqlcipher_sqlite3ThreadJoin(pTask->pThread, &pRet); + vdbeSorterBlockDebug(pTask, !bDone, "exit"); + rc = SQLITE_PTR_TO_INT(pRet); + assert( pTask->bDone==1 ); + pTask->bDone = 0; + pTask->pThread = 0; } - break; + return rc; } -/* Opcode: IsTrue P1 P2 P3 P4 * -** Synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 -** -** This opcode implements the IS TRUE, IS FALSE, IS NOT TRUE, and -** IS NOT FALSE operators. -** -** Interpret the value in register P1 as a boolean value. Store that -** boolean (a 0 or 1) in register P2. Or if the value in register P1 is -** NULL, then the P3 is stored in register P2. Invert the answer if P4 -** is 1. -** -** The logic is summarized like this: -** -**
      -**
    • If P3==0 and P4==0 then r[P2] := r[P1] IS TRUE -**
    • If P3==1 and P4==1 then r[P2] := r[P1] IS FALSE -**
    • If P3==0 and P4==1 then r[P2] := r[P1] IS NOT TRUE -**
    • If P3==1 and P4==0 then r[P2] := r[P1] IS NOT FALSE -**
    +/* +** Launch a background thread to run xTask(pIn). */ -case OP_IsTrue: { /* in1, out2 */ - assert( pOp->p4type==P4_INT32 ); - assert( pOp->p4.i==0 || pOp->p4.i==1 ); - assert( pOp->p3==0 || pOp->p3==1 ); - sqlcipher_sqlite3VdbeMemSetInt64(&aMem[pOp->p2], - sqlcipher_sqlite3VdbeBooleanValue(&aMem[pOp->p1], pOp->p3) ^ pOp->p4.i); - break; +static int vdbeSorterCreateThread( + SortSubtask *pTask, /* Thread will use this task object */ + void *(*xTask)(void*), /* Routine to run in a separate thread */ + void *pIn /* Argument passed into xTask() */ +){ + assert( pTask->pThread==0 && pTask->bDone==0 ); + return sqlcipher_sqlite3ThreadCreate(&pTask->pThread, xTask, pIn); } -/* Opcode: Not P1 P2 * * * -** Synopsis: r[P2]= !r[P1] -** -** Interpret the value in register P1 as a boolean value. Store the -** boolean complement in register P2. If the value in register P1 is -** NULL, then a NULL is stored in P2. +/* +** Join all outstanding threads launched by SorterWrite() to create +** level-0 PMAs. */ -case OP_Not: { /* same as TK_NOT, in1, out2 */ - pIn1 = &aMem[pOp->p1]; - pOut = &aMem[pOp->p2]; - if( (pIn1->flags & MEM_Null)==0 ){ - sqlcipher_sqlite3VdbeMemSetInt64(pOut, !sqlcipher_sqlite3VdbeBooleanValue(pIn1,0)); - }else{ - sqlcipher_sqlite3VdbeMemSetNull(pOut); +static int vdbeSorterJoinAll(VdbeSorter *pSorter, int rcin){ + int rc = rcin; + int i; + + /* This function is always called by the main user thread. + ** + ** If this function is being called after SorterRewind() has been called, + ** it is possible that thread pSorter->aTask[pSorter->nTask-1].pThread + ** is currently attempt to join one of the other threads. To avoid a race + ** condition where this thread also attempts to join the same object, join + ** thread pSorter->aTask[pSorter->nTask-1].pThread first. */ + for(i=pSorter->nTask-1; i>=0; i--){ + SortSubtask *pTask = &pSorter->aTask[i]; + int rc2 = vdbeSorterJoinThread(pTask); + if( rc==SQLITE_OK ) rc = rc2; } - break; + return rc; } +#else +# define vdbeSorterJoinAll(x,rcin) (rcin) +# define vdbeSorterJoinThread(pTask) SQLITE_OK +#endif -/* Opcode: BitNot P1 P2 * * * -** Synopsis: r[P2]= ~r[P1] +/* +** Allocate a new MergeEngine object capable of handling up to +** nReader PmaReader inputs. ** -** Interpret the content of register P1 as an integer. Store the -** ones-complement of the P1 value into register P2. If P1 holds -** a NULL then store a NULL in P2. +** nReader is automatically rounded up to the next power of two. +** nReader may not exceed SORTER_MAX_MERGE_COUNT even after rounding up. */ -case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ - pIn1 = &aMem[pOp->p1]; - pOut = &aMem[pOp->p2]; - sqlcipher_sqlite3VdbeMemSetNull(pOut); - if( (pIn1->flags & MEM_Null)==0 ){ - pOut->flags = MEM_Int; - pOut->u.i = ~sqlcipher_sqlite3VdbeIntValue(pIn1); +static MergeEngine *vdbeMergeEngineNew(int nReader){ + int N = 2; /* Smallest power of two >= nReader */ + int nByte; /* Total bytes of space to allocate */ + MergeEngine *pNew; /* Pointer to allocated object to return */ + + assert( nReader<=SORTER_MAX_MERGE_COUNT ); + + while( NnTree = N; + pNew->pTask = 0; + pNew->aReadr = (PmaReader*)&pNew[1]; + pNew->aTree = (int*)&pNew->aReadr[N]; } - break; + return pNew; } -/* Opcode: Once P1 P2 * * * -** -** Fall through to the next instruction the first time this opcode is -** encountered on each invocation of the byte-code program. Jump to P2 -** on the second and all subsequent encounters during the same invocation. -** -** Top-level programs determine first invocation by comparing the P1 -** operand against the P1 operand on the OP_Init opcode at the beginning -** of the program. If the P1 values differ, then fall through and make -** the P1 of this opcode equal to the P1 of OP_Init. If P1 values are -** the same then take the jump. -** -** For subprograms, there is a bitmask in the VdbeFrame that determines -** whether or not the jump should be taken. The bitmask is necessary -** because the self-altering code trick does not work for recursive -** triggers. +/* +** Free the MergeEngine object passed as the only argument. */ -case OP_Once: { /* jump */ - u32 iAddr; /* Address of this instruction */ - assert( p->aOp[0].opcode==OP_Init ); - if( p->pFrame ){ - iAddr = (int)(pOp - p->aOp); - if( (p->pFrame->aOnce[iAddr/8] & (1<<(iAddr & 7)))!=0 ){ - VdbeBranchTaken(1, 2); - goto jump_to_p2; - } - p->pFrame->aOnce[iAddr/8] |= 1<<(iAddr & 7); - }else{ - if( p->aOp[0].p1==pOp->p1 ){ - VdbeBranchTaken(1, 2); - goto jump_to_p2; +static void vdbeMergeEngineFree(MergeEngine *pMerger){ + int i; + if( pMerger ){ + for(i=0; inTree; i++){ + vdbePmaReaderClear(&pMerger->aReadr[i]); } } - VdbeBranchTaken(0, 2); - pOp->p1 = p->aOp[0].p1; - break; + sqlcipher_sqlite3_free(pMerger); } -/* Opcode: If P1 P2 P3 * * -** -** Jump to P2 if the value in register P1 is true. The value -** is considered true if it is numeric and non-zero. If the value -** in P1 is NULL then take the jump if and only if P3 is non-zero. +/* +** Free all resources associated with the IncrMerger object indicated by +** the first argument. */ -case OP_If: { /* jump, in1 */ - int c; - c = sqlcipher_sqlite3VdbeBooleanValue(&aMem[pOp->p1], pOp->p3); - VdbeBranchTaken(c!=0, 2); - if( c ) goto jump_to_p2; - break; +static void vdbeIncrFree(IncrMerger *pIncr){ + if( pIncr ){ +#if SQLITE_MAX_WORKER_THREADS>0 + if( pIncr->bUseThread ){ + vdbeSorterJoinThread(pIncr->pTask); + if( pIncr->aFile[0].pFd ) sqlcipher_sqlite3OsCloseFree(pIncr->aFile[0].pFd); + if( pIncr->aFile[1].pFd ) sqlcipher_sqlite3OsCloseFree(pIncr->aFile[1].pFd); + } +#endif + vdbeMergeEngineFree(pIncr->pMerger); + sqlcipher_sqlite3_free(pIncr); + } } -/* Opcode: IfNot P1 P2 P3 * * -** -** Jump to P2 if the value in register P1 is False. The value -** is considered false if it has a numeric value of zero. If the value -** in P1 is NULL then take the jump if and only if P3 is non-zero. +/* +** Reset a sorting cursor back to its original empty state. */ -case OP_IfNot: { /* jump, in1 */ - int c; - c = !sqlcipher_sqlite3VdbeBooleanValue(&aMem[pOp->p1], !pOp->p3); - VdbeBranchTaken(c!=0, 2); - if( c ) goto jump_to_p2; - break; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSorterReset(sqlcipher_sqlite3 *db, VdbeSorter *pSorter){ + int i; + (void)vdbeSorterJoinAll(pSorter, SQLITE_OK); + assert( pSorter->bUseThreads || pSorter->pReader==0 ); +#if SQLITE_MAX_WORKER_THREADS>0 + if( pSorter->pReader ){ + vdbePmaReaderClear(pSorter->pReader); + sqlcipher_sqlite3DbFree(db, pSorter->pReader); + pSorter->pReader = 0; + } +#endif + vdbeMergeEngineFree(pSorter->pMerger); + pSorter->pMerger = 0; + for(i=0; inTask; i++){ + SortSubtask *pTask = &pSorter->aTask[i]; + vdbeSortSubtaskCleanup(db, pTask); + pTask->pSorter = pSorter; + } + if( pSorter->list.aMemory==0 ){ + vdbeSorterRecordFree(0, pSorter->list.pList); + } + pSorter->list.pList = 0; + pSorter->list.szPMA = 0; + pSorter->bUsePMA = 0; + pSorter->iMemory = 0; + pSorter->mxKeysize = 0; + sqlcipher_sqlite3DbFree(db, pSorter->pUnpacked); + pSorter->pUnpacked = 0; } -/* Opcode: IsNull P1 P2 * * * -** Synopsis: if r[P1]==NULL goto P2 -** -** Jump to P2 if the value in register P1 is NULL. +/* +** Free any cursor components allocated by sqlcipher_sqlite3VdbeSorterXXX routines. */ -case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ - pIn1 = &aMem[pOp->p1]; - VdbeBranchTaken( (pIn1->flags & MEM_Null)!=0, 2); - if( (pIn1->flags & MEM_Null)!=0 ){ - goto jump_to_p2; +SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSorterClose(sqlcipher_sqlite3 *db, VdbeCursor *pCsr){ + VdbeSorter *pSorter; + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; + if( pSorter ){ + sqlcipher_sqlite3VdbeSorterReset(db, pSorter); + sqlcipher_sqlite3_free(pSorter->list.aMemory); + sqlcipher_sqlite3DbFree(db, pSorter); + pCsr->uc.pSorter = 0; } - break; } -/* Opcode: NotNull P1 P2 * * * -** Synopsis: if r[P1]!=NULL goto P2 +#if SQLITE_MAX_MMAP_SIZE>0 +/* +** The first argument is a file-handle open on a temporary file. The file +** is guaranteed to be nByte bytes or smaller in size. This function +** attempts to extend the file to nByte bytes in size and to ensure that +** the VFS has memory mapped it. ** -** Jump to P2 if the value in register P1 is not NULL. +** Whether or not the file does end up memory mapped of course depends on +** the specific VFS implementation. */ -case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ - pIn1 = &aMem[pOp->p1]; - VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2); - if( (pIn1->flags & MEM_Null)==0 ){ - goto jump_to_p2; +static void vdbeSorterExtendFile(sqlcipher_sqlite3 *db, sqlcipher_sqlite3_file *pFd, i64 nByte){ + if( nByte<=(i64)(db->nMaxSorterMmap) && pFd->pMethods->iVersion>=3 ){ + void *p = 0; + int chunksize = 4*1024; + sqlcipher_sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_CHUNK_SIZE, &chunksize); + sqlcipher_sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_SIZE_HINT, &nByte); + sqlcipher_sqlite3OsFetch(pFd, 0, (int)nByte, &p); + if( p ) sqlcipher_sqlite3OsUnfetch(pFd, 0, p); } - break; } +#else +# define vdbeSorterExtendFile(x,y,z) +#endif -/* Opcode: IfNullRow P1 P2 P3 * * -** Synopsis: if P1.nullRow then r[P3]=NULL, goto P2 -** -** Check the cursor P1 to see if it is currently pointing at a NULL row. -** If it is, then set register P3 to NULL and jump immediately to P2. -** If P1 is not on a NULL row, then fall through without making any -** changes. +/* +** Allocate space for a file-handle and open a temporary file. If successful, +** set *ppFd to point to the malloc'd file-handle and return SQLITE_OK. +** Otherwise, set *ppFd to 0 and return an SQLite error code. */ -case OP_IfNullRow: { /* jump */ - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( p->apCsr[pOp->p1]!=0 ); - if( p->apCsr[pOp->p1]->nullRow ){ - sqlcipher_sqlite3VdbeMemSetNull(aMem + pOp->p3); - goto jump_to_p2; +static int vdbeSorterOpenTempFile( + sqlcipher_sqlite3 *db, /* Database handle doing sort */ + i64 nExtend, /* Attempt to extend file to this size */ + sqlcipher_sqlite3_file **ppFd +){ + int rc; + if( sqlcipher_sqlite3FaultSim(202) ) return SQLITE_IOERR_ACCESS; + rc = sqlcipher_sqlite3OsOpenMalloc(db->pVfs, 0, ppFd, + SQLITE_OPEN_TEMP_JOURNAL | + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | + SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE, &rc + ); + if( rc==SQLITE_OK ){ + i64 max = SQLITE_MAX_MMAP_SIZE; + sqlcipher_sqlite3OsFileControlHint(*ppFd, SQLITE_FCNTL_MMAP_SIZE, (void*)&max); + if( nExtend>0 ){ + vdbeSorterExtendFile(db, *ppFd, nExtend); + } } - break; + return rc; } -#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC -/* Opcode: Offset P1 P2 P3 * * -** Synopsis: r[P3] = sqlite_offset(P1) -** -** Store in register r[P3] the byte offset into the database file that is the -** start of the payload for the record at which that cursor P1 is currently -** pointing. -** -** P2 is the column number for the argument to the sqlite_offset() function. -** This opcode does not use P2 itself, but the P2 value is used by the -** code generator. The P1, P2, and P3 operands to this opcode are the -** same as for OP_Column. -** -** This opcode is only available if SQLite is compiled with the -** -DSQLITE_ENABLE_OFFSET_SQL_FUNC option. +/* +** If it has not already been allocated, allocate the UnpackedRecord +** structure at pTask->pUnpacked. Return SQLITE_OK if successful (or +** if no allocation was required), or SQLITE_NOMEM otherwise. */ -case OP_Offset: { /* out3 */ - VdbeCursor *pC; /* The VDBE cursor */ - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - pOut = &p->aMem[pOp->p3]; - if( NEVER(pC==0) || pC->eCurType!=CURTYPE_BTREE ){ - sqlcipher_sqlite3VdbeMemSetNull(pOut); - }else{ - sqlcipher_sqlite3VdbeMemSetInt64(pOut, sqlcipher_sqlite3BtreeOffset(pC->uc.pCursor)); +static int vdbeSortAllocUnpacked(SortSubtask *pTask){ + if( pTask->pUnpacked==0 ){ + pTask->pUnpacked = sqlcipher_sqlite3VdbeAllocUnpackedRecord(pTask->pSorter->pKeyInfo); + if( pTask->pUnpacked==0 ) return SQLITE_NOMEM_BKPT; + pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nKeyField; + pTask->pUnpacked->errCode = 0; } - break; + return SQLITE_OK; } -#endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */ - -/* Opcode: Column P1 P2 P3 P4 P5 -** Synopsis: r[P3]=PX -** -** Interpret the data that cursor P1 points to as a structure built using -** the MakeRecord instruction. (See the MakeRecord opcode for additional -** information about the format of the data.) Extract the P2-th column -** from this record. If there are less that (P2+1) -** values in the record, extract a NULL. -** -** The value extracted is stored in register P3. -** -** If the record contains fewer than P2 fields, then extract a NULL. Or, -** if the P4 argument is a P4_MEM use the value of the P4 argument as -** the result. -** -** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then -** the result is guaranteed to only be used as the argument of a length() -** or typeof() function, respectively. The loading of large blobs can be -** skipped for length() and all content loading can be skipped for typeof(). -*/ -case OP_Column: { - u32 p2; /* column number to retrieve */ - VdbeCursor *pC; /* The VDBE cursor */ - BtCursor *pCrsr; /* The BTree cursor */ - u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ - int len; /* The length of the serialized data for the column */ - int i; /* Loop counter */ - Mem *pDest; /* Where to write the extracted value */ - Mem sMem; /* For storing the record being decoded */ - const u8 *zData; /* Part of the record being decoded */ - const u8 *zHdr; /* Next unparsed byte of the header */ - const u8 *zEndHdr; /* Pointer to first byte after the header */ - u64 offset64; /* 64-bit offset */ - u32 t; /* A type code from the record header */ - Mem *pReg; /* PseudoTable input register */ - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - p2 = (u32)pOp->p2; - /* If the cursor cache is stale (meaning it is not currently point at - ** the correct row) then bring it up-to-date by doing the necessary - ** B-Tree seek. */ - rc = sqlcipher_sqlite3VdbeCursorMoveto(&pC, &p2); - if( rc ) goto abort_due_to_error; +/* +** Merge the two sorted lists p1 and p2 into a single list. +*/ +static SorterRecord *vdbeSorterMerge( + SortSubtask *pTask, /* Calling thread context */ + SorterRecord *p1, /* First list to merge */ + SorterRecord *p2 /* Second list to merge */ +){ + SorterRecord *pFinal = 0; + SorterRecord **pp = &pFinal; + int bCached = 0; - assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); - pDest = &aMem[pOp->p3]; - memAboutToChange(p, pDest); - assert( pC!=0 ); - assert( p2<(u32)pC->nField ); - aOffset = pC->aOffset; - assert( pC->eCurType!=CURTYPE_VTAB ); - assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); - assert( pC->eCurType!=CURTYPE_SORTER ); + assert( p1!=0 && p2!=0 ); + for(;;){ + int res; + res = pTask->xCompare( + pTask, &bCached, SRVAL(p1), p1->nVal, SRVAL(p2), p2->nVal + ); - if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/ - if( pC->nullRow ){ - if( pC->eCurType==CURTYPE_PSEUDO ){ - /* For the special case of as pseudo-cursor, the seekResult field - ** identifies the register that holds the record */ - assert( pC->seekResult>0 ); - pReg = &aMem[pC->seekResult]; - assert( pReg->flags & MEM_Blob ); - assert( memIsValid(pReg) ); - pC->payloadSize = pC->szRow = pReg->n; - pC->aRow = (u8*)pReg->z; - }else{ - sqlcipher_sqlite3VdbeMemSetNull(pDest); - goto op_column_out; + if( res<=0 ){ + *pp = p1; + pp = &p1->u.pNext; + p1 = p1->u.pNext; + if( p1==0 ){ + *pp = p2; + break; } }else{ - pCrsr = pC->uc.pCursor; - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pCrsr ); - assert( sqlcipher_sqlite3BtreeCursorIsValid(pCrsr) ); - pC->payloadSize = sqlcipher_sqlite3BtreePayloadSize(pCrsr); - pC->aRow = sqlcipher_sqlite3BtreePayloadFetch(pCrsr, &pC->szRow); - assert( pC->szRow<=pC->payloadSize ); - assert( pC->szRow<=65536 ); /* Maximum page size is 64KiB */ - if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - } - pC->cacheStatus = p->cacheCtr; - pC->iHdrOffset = getVarint32(pC->aRow, aOffset[0]); - pC->nHdrParsed = 0; - - - if( pC->szRowaRow does not have to hold the entire row, but it does at least - ** need to cover the header of the record. If pC->aRow does not contain - ** the complete header, then set it to zero, forcing the header to be - ** dynamically allocated. */ - pC->aRow = 0; - pC->szRow = 0; - - /* Make sure a corrupt database has not given us an oversize header. - ** Do this now to avoid an oversize memory allocation. - ** - ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte - ** types use so much data space that there can only be 4096 and 32 of - ** them, respectively. So the maximum header length results from a - ** 3-byte type for each of the maximum of 32768 columns plus three - ** extra bytes for the header length itself. 32768*3 + 3 = 98307. - */ - if( aOffset[0] > 98307 || aOffset[0] > pC->payloadSize ){ - goto op_column_corrupt; + *pp = p2; + pp = &p2->u.pNext; + p2 = p2->u.pNext; + bCached = 0; + if( p2==0 ){ + *pp = p1; + break; } - }else{ - /* This is an optimization. By skipping over the first few tests - ** (ex: pC->nHdrParsed<=p2) in the next section, we achieve a - ** measurable performance gain. - ** - ** This branch is taken even if aOffset[0]==0. Such a record is never - ** generated by SQLite, and could be considered corruption, but we - ** accept it for historical reasons. When aOffset[0]==0, the code this - ** branch jumps to reads past the end of the record, but never more - ** than a few bytes. Even if the record occurs at the end of the page - ** content area, the "page header" comes after the page content and so - ** this overread is harmless. Similar overreads can occur for a corrupt - ** database file. - */ - zData = pC->aRow; - assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */ - testcase( aOffset[0]==0 ); - goto op_column_read_header; } } + return pFinal; +} - /* Make sure at least the first p2+1 entries of the header have been - ** parsed and valid information is in aOffset[] and pC->aType[]. - */ - if( pC->nHdrParsed<=p2 ){ - /* If there is more header available for parsing in the record, try - ** to extract additional fields up through the p2+1-th field - */ - if( pC->iHdrOffsetaRow==0 ){ - memset(&sMem, 0, sizeof(sMem)); - rc = sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(pC->uc.pCursor,aOffset[0],&sMem); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - zData = (u8*)sMem.z; - }else{ - zData = pC->aRow; - } +/* +** Return the SorterCompare function to compare values collected by the +** sorter object passed as the only argument. +*/ +static SorterCompare vdbeSorterGetCompare(VdbeSorter *p){ + if( p->typeMask==SORTER_TYPE_INTEGER ){ + return vdbeSorterCompareInt; + }else if( p->typeMask==SORTER_TYPE_TEXT ){ + return vdbeSorterCompareText; + } + return vdbeSorterCompare; +} - /* Fill in pC->aType[i] and aOffset[i] values through the p2-th field. */ - op_column_read_header: - i = pC->nHdrParsed; - offset64 = aOffset[i]; - zHdr = zData + pC->iHdrOffset; - zEndHdr = zData + aOffset[0]; - testcase( zHdr>=zEndHdr ); - do{ - if( (pC->aType[i] = t = zHdr[0])<0x80 ){ - zHdr++; - offset64 += sqlcipher_sqlite3VdbeOneByteSerialTypeLen(t); - }else{ - zHdr += sqlcipher_sqlite3GetVarint32(zHdr, &t); - pC->aType[i] = t; - offset64 += sqlcipher_sqlite3VdbeSerialTypeLen(t); - } - aOffset[++i] = (u32)(offset64 & 0xffffffff); - }while( (u32)i<=p2 && zHdrpList. Return +** SQLITE_OK if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if +** an error occurs. +*/ +static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){ + int i; + SorterRecord *p; + int rc; + SorterRecord *aSlot[64]; - /* The record is corrupt if any of the following are true: - ** (1) the bytes of the header extend past the declared header size - ** (2) the entire header was used but not all data was used - ** (3) the end of the data extends beyond the end of the record. - */ - if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset64!=pC->payloadSize)) - || (offset64 > pC->payloadSize) - ){ - if( aOffset[0]==0 ){ - i = 0; - zHdr = zEndHdr; - }else{ - if( pC->aRow==0 ) sqlcipher_sqlite3VdbeMemRelease(&sMem); - goto op_column_corrupt; - } - } + rc = vdbeSortAllocUnpacked(pTask); + if( rc!=SQLITE_OK ) return rc; - pC->nHdrParsed = i; - pC->iHdrOffset = (u32)(zHdr - zData); - if( pC->aRow==0 ) sqlcipher_sqlite3VdbeMemRelease(&sMem); - }else{ - t = 0; - } + p = pList->pList; + pTask->xCompare = vdbeSorterGetCompare(pTask->pSorter); + memset(aSlot, 0, sizeof(aSlot)); - /* If after trying to extract new entries from the header, nHdrParsed is - ** still not up to p2, that means that the record has fewer than p2 - ** columns. So the result will be either the default value or a NULL. - */ - if( pC->nHdrParsed<=p2 ){ - if( pOp->p4type==P4_MEM ){ - sqlcipher_sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static); + while( p ){ + SorterRecord *pNext; + if( pList->aMemory ){ + if( (u8*)p==pList->aMemory ){ + pNext = 0; }else{ - sqlcipher_sqlite3VdbeMemSetNull(pDest); + assert( p->u.iNextaMemory) ); + pNext = (SorterRecord*)&pList->aMemory[p->u.iNext]; } - goto op_column_out; - } - }else{ - t = pC->aType[p2]; - } - - /* Extract the content for the p2+1-th column. Control can only - ** reach this point if aOffset[p2], aOffset[p2+1], and pC->aType[p2] are - ** all valid. - */ - assert( p2nHdrParsed ); - assert( rc==SQLITE_OK ); - assert( sqlcipher_sqlite3VdbeCheckMemInvariants(pDest) ); - if( VdbeMemDynamic(pDest) ){ - sqlcipher_sqlite3VdbeMemSetNull(pDest); - } - assert( t==pC->aType[p2] ); - if( pC->szRow>=aOffset[p2+1] ){ - /* This is the common case where the desired content fits on the original - ** page - where the content is not on an overflow page */ - zData = pC->aRow + aOffset[p2]; - if( t<12 ){ - sqlcipher_sqlite3VdbeSerialGet(zData, t, pDest); }else{ - /* If the column value is a string, we need a persistent value, not - ** a MEM_Ephem value. This branch is a fast short-cut that is equivalent - ** to calling sqlcipher_sqlite3VdbeSerialGet() and sqlcipher_sqlite3VdbeDeephemeralize(). - */ - static const u16 aFlag[] = { MEM_Blob, MEM_Str|MEM_Term }; - pDest->n = len = (t-12)/2; - pDest->enc = encoding; - if( pDest->szMalloc < len+2 ){ - pDest->flags = MEM_Null; - if( sqlcipher_sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem; - }else{ - pDest->z = pDest->zMalloc; - } - memcpy(pDest->z, zData, len); - pDest->z[len] = 0; - pDest->z[len+1] = 0; - pDest->flags = aFlag[t&1]; + pNext = p->u.pNext; } - }else{ - pDest->enc = encoding; - /* This branch happens only when content is on overflow pages */ - if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 - && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) - || (len = sqlcipher_sqlite3VdbeSerialTypeLen(t))==0 - ){ - /* Content is irrelevant for - ** 1. the typeof() function, - ** 2. the length(X) function if X is a blob, and - ** 3. if the content length is zero. - ** So we might as well use bogus content rather than reading - ** content from disk. - ** - ** Although sqlcipher_sqlite3VdbeSerialGet() may read at most 8 bytes from the - ** buffer passed to it, debugging function VdbeMemPrettyPrint() may - ** read more. Use the global constant sqlcipher_sqlite3CtypeMap[] as the array, - ** as that array is 256 bytes long (plenty for VdbeMemPrettyPrint()) - ** and it begins with a bunch of zeros. - */ - sqlcipher_sqlite3VdbeSerialGet((u8*)sqlcipher_sqlite3CtypeMap, t, pDest); - }else{ - rc = sqlcipher_sqlite3VdbeMemFromBtree(pC->uc.pCursor, aOffset[p2], len, pDest); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - sqlcipher_sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); - pDest->flags &= ~MEM_Ephem; + + p->u.pNext = 0; + for(i=0; aSlot[i]; i++){ + p = vdbeSorterMerge(pTask, p, aSlot[i]); + aSlot[i] = 0; } + aSlot[i] = p; + p = pNext; } -op_column_out: - UPDATE_MAX_BLOBSIZE(pDest); - REGISTER_TRACE(pOp->p3, pDest); - break; - -op_column_corrupt: - if( aOp[0].p3>0 ){ - pOp = &aOp[aOp[0].p3-1]; - break; - }else{ - rc = SQLITE_CORRUPT_BKPT; - goto abort_due_to_error; + p = 0; + for(i=0; ipList = p; + + assert( pTask->pUnpacked->errCode==SQLITE_OK + || pTask->pUnpacked->errCode==SQLITE_NOMEM + ); + return pTask->pUnpacked->errCode; } -/* Opcode: Affinity P1 P2 * P4 * -** Synopsis: affinity(r[P1@P2]) -** -** Apply affinities to a range of P2 registers starting with P1. -** -** P4 is a string that is P2 characters long. The N-th character of the -** string indicates the column affinity that should be used for the N-th -** memory cell in the range. +/* +** Initialize a PMA-writer object. */ -case OP_Affinity: { - const char *zAffinity; /* The affinity to be applied */ - - zAffinity = pOp->p4.z; - assert( zAffinity!=0 ); - assert( pOp->p2>0 ); - assert( zAffinity[pOp->p2]==0 ); - pIn1 = &aMem[pOp->p1]; - while( 1 /*exit-by-break*/ ){ - assert( pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)] ); - assert( zAffinity[0]==SQLITE_AFF_NONE || memIsValid(pIn1) ); - applyAffinity(pIn1, zAffinity[0], encoding); - if( zAffinity[0]==SQLITE_AFF_REAL && (pIn1->flags & MEM_Int)!=0 ){ - /* When applying REAL affinity, if the result is still an MEM_Int - ** that will fit in 6 bytes, then change the type to MEM_IntReal - ** so that we keep the high-resolution integer value but know that - ** the type really wants to be REAL. */ - testcase( pIn1->u.i==140737488355328LL ); - testcase( pIn1->u.i==140737488355327LL ); - testcase( pIn1->u.i==-140737488355328LL ); - testcase( pIn1->u.i==-140737488355329LL ); - if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL ){ - pIn1->flags |= MEM_IntReal; - pIn1->flags &= ~MEM_Int; - }else{ - pIn1->u.r = (double)pIn1->u.i; - pIn1->flags |= MEM_Real; - pIn1->flags &= ~MEM_Int; - } - } - REGISTER_TRACE((int)(pIn1-aMem), pIn1); - zAffinity++; - if( zAffinity[0]==0 ) break; - pIn1++; +static void vdbePmaWriterInit( + sqlcipher_sqlite3_file *pFd, /* File handle to write to */ + PmaWriter *p, /* Object to populate */ + int nBuf, /* Buffer size */ + i64 iStart /* Offset of pFd to begin writing at */ +){ + memset(p, 0, sizeof(PmaWriter)); + p->aBuffer = (u8*)sqlcipher_sqlite3Malloc(nBuf); + if( !p->aBuffer ){ + p->eFWErr = SQLITE_NOMEM_BKPT; + }else{ + p->iBufEnd = p->iBufStart = (iStart % nBuf); + p->iWriteOff = iStart - p->iBufStart; + p->nBuffer = nBuf; + p->pFd = pFd; } - break; } -/* Opcode: MakeRecord P1 P2 P3 P4 * -** Synopsis: r[P3]=mkrec(r[P1@P2]) -** -** Convert P2 registers beginning with P1 into the [record format] -** use as a data record in a database table or as a key -** in an index. The OP_Column opcode can decode the record later. -** -** P4 may be a string that is P2 characters long. The N-th character of the -** string indicates the column affinity that should be used for the N-th -** field of the index key. -** -** The mapping from character to affinity is given by the SQLITE_AFF_ -** macros defined in sqliteInt.h. -** -** If P4 is NULL then all index fields have the affinity BLOB. -** -** The meaning of P5 depends on whether or not the SQLITE_ENABLE_NULL_TRIM -** compile-time option is enabled: -** -** * If SQLITE_ENABLE_NULL_TRIM is enabled, then the P5 is the index -** of the right-most table that can be null-trimmed. -** -** * If SQLITE_ENABLE_NULL_TRIM is omitted, then P5 has the value -** OPFLAG_NOCHNG_MAGIC if the OP_MakeRecord opcode is allowed to -** accept no-change records with serial_type 10. This value is -** only used inside an assert() and does not affect the end result. +/* +** Write nData bytes of data to the PMA. Return SQLITE_OK +** if successful, or an SQLite error code if an error occurs. */ -case OP_MakeRecord: { - Mem *pRec; /* The new record */ - u64 nData; /* Number of bytes of data space */ - int nHdr; /* Number of bytes of header space */ - i64 nByte; /* Data space required for this record */ - i64 nZero; /* Number of zero bytes at the end of the record */ - int nVarint; /* Number of bytes in a varint */ - u32 serial_type; /* Type field */ - Mem *pData0; /* First field to be combined into the record */ - Mem *pLast; /* Last field of the record */ - int nField; /* Number of fields in the record */ - char *zAffinity; /* The affinity string for the record */ - int file_format; /* File format to use for encoding */ - u32 len; /* Length of a field */ - u8 *zHdr; /* Where to write next byte of the header */ - u8 *zPayload; /* Where to write next byte of the payload */ - - /* Assuming the record contains N fields, the record format looks - ** like this: - ** - ** ------------------------------------------------------------------------ - ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 | - ** ------------------------------------------------------------------------ - ** - ** Data(0) is taken from register P1. Data(1) comes from register P1+1 - ** and so forth. - ** - ** Each type field is a varint representing the serial type of the - ** corresponding data element (see sqlcipher_sqlite3VdbeSerialType()). The - ** hdr-size field is also a varint which is the offset from the beginning - ** of the record to data0. - */ - nData = 0; /* Number of bytes of data space */ - nHdr = 0; /* Number of bytes of header space */ - nZero = 0; /* Number of zero bytes at the end of the record */ - nField = pOp->p1; - zAffinity = pOp->p4.z; - assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=(p->nMem+1 - p->nCursor)+1 ); - pData0 = &aMem[nField]; - nField = pOp->p2; - pLast = &pData0[nField-1]; - file_format = p->minWriteFileFormat; - - /* Identify the output register */ - assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); - pOut = &aMem[pOp->p3]; - memAboutToChange(p, pOut); - - /* Apply the requested affinity to all inputs - */ - assert( pData0<=pLast ); - if( zAffinity ){ - pRec = pData0; - do{ - applyAffinity(pRec, zAffinity[0], encoding); - if( zAffinity[0]==SQLITE_AFF_REAL && (pRec->flags & MEM_Int) ){ - pRec->flags |= MEM_IntReal; - pRec->flags &= ~(MEM_Int); - } - REGISTER_TRACE((int)(pRec-aMem), pRec); - zAffinity++; - pRec++; - assert( zAffinity[0]==0 || pRec<=pLast ); - }while( zAffinity[0] ); - } - -#ifdef SQLITE_ENABLE_NULL_TRIM - /* NULLs can be safely trimmed from the end of the record, as long as - ** as the schema format is 2 or more and none of the omitted columns - ** have a non-NULL default value. Also, the record must be left with - ** at least one field. If P5>0 then it will be one more than the - ** index of the right-most column with a non-NULL default value */ - if( pOp->p5 ){ - while( (pLast->flags & MEM_Null)!=0 && nField>pOp->p5 ){ - pLast--; - nField--; +static void vdbePmaWriteBlob(PmaWriter *p, u8 *pData, int nData){ + int nRem = nData; + while( nRem>0 && p->eFWErr==0 ){ + int nCopy = nRem; + if( nCopy>(p->nBuffer - p->iBufEnd) ){ + nCopy = p->nBuffer - p->iBufEnd; } - } -#endif - /* Loop through the elements that will make up the record to figure - ** out how much space is required for the new record. After this loop, - ** the Mem.uTemp field of each term should hold the serial-type that will - ** be used for that term in the generated record: - ** - ** Mem.uTemp value type - ** --------------- --------------- - ** 0 NULL - ** 1 1-byte signed integer - ** 2 2-byte signed integer - ** 3 3-byte signed integer - ** 4 4-byte signed integer - ** 5 6-byte signed integer - ** 6 8-byte signed integer - ** 7 IEEE float - ** 8 Integer constant 0 - ** 9 Integer constant 1 - ** 10,11 reserved for expansion - ** N>=12 and even BLOB - ** N>=13 and odd text - ** - ** The following additional values are computed: - ** nHdr Number of bytes needed for the record header - ** nData Number of bytes of data space needed for the record - ** nZero Zero bytes at the end of the record - */ - pRec = pLast; - do{ - assert( memIsValid(pRec) ); - if( pRec->flags & MEM_Null ){ - if( pRec->flags & MEM_Zero ){ - /* Values with MEM_Null and MEM_Zero are created by xColumn virtual - ** table methods that never invoke sqlcipher_sqlite3_result_xxxxx() while - ** computing an unchanging column value in an UPDATE statement. - ** Give such values a special internal-use-only serial-type of 10 - ** so that they can be passed through to xUpdate and have - ** a true sqlcipher_sqlite3_value_nochange(). */ -#ifndef SQLITE_ENABLE_NULL_TRIM - assert( pOp->p5==OPFLAG_NOCHNG_MAGIC || CORRUPT_DB ); -#endif - pRec->uTemp = 10; - }else{ - pRec->uTemp = 0; - } - nHdr++; - }else if( pRec->flags & (MEM_Int|MEM_IntReal) ){ - /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */ - i64 i = pRec->u.i; - u64 uu; - testcase( pRec->flags & MEM_Int ); - testcase( pRec->flags & MEM_IntReal ); - if( i<0 ){ - uu = ~i; - }else{ - uu = i; - } - nHdr++; - testcase( uu==127 ); testcase( uu==128 ); - testcase( uu==32767 ); testcase( uu==32768 ); - testcase( uu==8388607 ); testcase( uu==8388608 ); - testcase( uu==2147483647 ); testcase( uu==2147483648 ); - testcase( uu==140737488355327LL ); testcase( uu==140737488355328LL ); - if( uu<=127 ){ - if( (i&1)==i && file_format>=4 ){ - pRec->uTemp = 8+(u32)uu; - }else{ - nData++; - pRec->uTemp = 1; - } - }else if( uu<=32767 ){ - nData += 2; - pRec->uTemp = 2; - }else if( uu<=8388607 ){ - nData += 3; - pRec->uTemp = 3; - }else if( uu<=2147483647 ){ - nData += 4; - pRec->uTemp = 4; - }else if( uu<=140737488355327LL ){ - nData += 6; - pRec->uTemp = 5; - }else{ - nData += 8; - if( pRec->flags & MEM_IntReal ){ - /* If the value is IntReal and is going to take up 8 bytes to store - ** as an integer, then we might as well make it an 8-byte floating - ** point value */ - pRec->u.r = (double)pRec->u.i; - pRec->flags &= ~MEM_IntReal; - pRec->flags |= MEM_Real; - pRec->uTemp = 7; - }else{ - pRec->uTemp = 6; - } - } - }else if( pRec->flags & MEM_Real ){ - nHdr++; - nData += 8; - pRec->uTemp = 7; - }else{ - assert( db->mallocFailed || pRec->flags&(MEM_Str|MEM_Blob) ); - assert( pRec->n>=0 ); - len = (u32)pRec->n; - serial_type = (len*2) + 12 + ((pRec->flags & MEM_Str)!=0); - if( pRec->flags & MEM_Zero ){ - serial_type += pRec->u.nZero*2; - if( nData ){ - if( sqlcipher_sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem; - len += pRec->u.nZero; - }else{ - nZero += pRec->u.nZero; - } - } - nData += len; - nHdr += sqlcipher_sqlite3VarintLen(serial_type); - pRec->uTemp = serial_type; + memcpy(&p->aBuffer[p->iBufEnd], &pData[nData-nRem], nCopy); + p->iBufEnd += nCopy; + if( p->iBufEnd==p->nBuffer ){ + p->eFWErr = sqlcipher_sqlite3OsWrite(p->pFd, + &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart, + p->iWriteOff + p->iBufStart + ); + p->iBufStart = p->iBufEnd = 0; + p->iWriteOff += p->nBuffer; } - if( pRec==pData0 ) break; - pRec--; - }while(1); + assert( p->iBufEndnBuffer ); - /* EVIDENCE-OF: R-22564-11647 The header begins with a single varint - ** which determines the total number of bytes in the header. The varint - ** value is the size of the header in bytes including the size varint - ** itself. */ - testcase( nHdr==126 ); - testcase( nHdr==127 ); - if( nHdr<=126 ){ - /* The common case */ - nHdr += 1; - }else{ - /* Rare case of a really large header */ - nVarint = sqlcipher_sqlite3VarintLen(nHdr); - nHdr += nVarint; - if( nVarintp3) is not allowed to - ** be one of the input registers (because the following call to - ** sqlcipher_sqlite3VdbeMemClearAndResize() could clobber the value before it is used). - */ - if( nByte+nZero<=pOut->szMalloc ){ - /* The output register is already large enough to hold the record. - ** No error checks or buffer enlargement is required */ - pOut->z = pOut->zMalloc; - }else{ - /* Need to make sure that the output is not too big and then enlarge - ** the output register to hold the full result */ - if( nByte+nZero>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - if( sqlcipher_sqlite3VdbeMemClearAndResize(pOut, (int)nByte) ){ - goto no_mem; - } - } - pOut->n = (int)nByte; - pOut->flags = MEM_Blob; - if( nZero ){ - pOut->u.nZero = nZero; - pOut->flags |= MEM_Zero; +/* +** Flush any buffered data to disk and clean up the PMA-writer object. +** The results of using the PMA-writer after this call are undefined. +** Return SQLITE_OK if flushing the buffered data succeeds or is not +** required. Otherwise, return an SQLite error code. +** +** Before returning, set *piEof to the offset immediately following the +** last byte written to the file. +*/ +static int vdbePmaWriterFinish(PmaWriter *p, i64 *piEof){ + int rc; + if( p->eFWErr==0 && ALWAYS(p->aBuffer) && p->iBufEnd>p->iBufStart ){ + p->eFWErr = sqlcipher_sqlite3OsWrite(p->pFd, + &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart, + p->iWriteOff + p->iBufStart + ); } - UPDATE_MAX_BLOBSIZE(pOut); - zHdr = (u8 *)pOut->z; - zPayload = zHdr + nHdr; - - /* Write the record */ - zHdr += putVarint32(zHdr, nHdr); - assert( pData0<=pLast ); - pRec = pData0; - do{ - serial_type = pRec->uTemp; - /* EVIDENCE-OF: R-06529-47362 Following the size varint are one or more - ** additional varints, one per column. */ - zHdr += putVarint32(zHdr, serial_type); /* serial type */ - /* EVIDENCE-OF: R-64536-51728 The values for each column in the record - ** immediately follow the header. */ - zPayload += sqlcipher_sqlite3VdbeSerialPut(zPayload, pRec, serial_type); /* content */ - }while( (++pRec)<=pLast ); - assert( nHdr==(int)(zHdr - (u8*)pOut->z) ); - assert( nByte==(int)(zPayload - (u8*)pOut->z) ); + *piEof = (p->iWriteOff + p->iBufEnd); + sqlcipher_sqlite3_free(p->aBuffer); + rc = p->eFWErr; + memset(p, 0, sizeof(PmaWriter)); + return rc; +} - assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); - REGISTER_TRACE(pOp->p3, pOut); - break; +/* +** Write value iVal encoded as a varint to the PMA. Return +** SQLITE_OK if successful, or an SQLite error code if an error occurs. +*/ +static void vdbePmaWriteVarint(PmaWriter *p, u64 iVal){ + int nByte; + u8 aByte[10]; + nByte = sqlcipher_sqlite3PutVarint(aByte, iVal); + vdbePmaWriteBlob(p, aByte, nByte); } -/* Opcode: Count P1 P2 p3 * * -** Synopsis: r[P2]=count() +/* +** Write the current contents of in-memory linked-list pList to a level-0 +** PMA in the temp file belonging to sub-task pTask. Return SQLITE_OK if +** successful, or an SQLite error code otherwise. ** -** Store the number of entries (an integer value) in the table or index -** opened by cursor P1 in register P2. +** The format of a PMA is: ** -** If P3==0, then an exact count is obtained, which involves visiting -** every btree page of the table. But if P3 is non-zero, an estimate -** is returned based on the current cursor position. -*/ -case OP_Count: { /* out2 */ - i64 nEntry; - BtCursor *pCrsr; - - assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE ); - pCrsr = p->apCsr[pOp->p1]->uc.pCursor; - assert( pCrsr ); - if( pOp->p3 ){ - nEntry = sqlcipher_sqlite3BtreeRowCountEst(pCrsr); - }else{ - nEntry = 0; /* Not needed. Only used to silence a warning. */ - rc = sqlcipher_sqlite3BtreeCount(db, pCrsr, &nEntry); - if( rc ) goto abort_due_to_error; - } - pOut = out2Prerelease(p, pOp); - pOut->u.i = nEntry; - goto check_for_interrupt; -} - -/* Opcode: Savepoint P1 * * P4 * +** * A varint. This varint contains the total number of bytes of content +** in the PMA (not including the varint itself). ** -** Open, release or rollback the savepoint named by parameter P4, depending -** on the value of P1. To open a new savepoint set P1==0 (SAVEPOINT_BEGIN). -** To release (commit) an existing savepoint set P1==1 (SAVEPOINT_RELEASE). -** To rollback an existing savepoint set P1==2 (SAVEPOINT_ROLLBACK). +** * One or more records packed end-to-end in order of ascending keys. +** Each record consists of a varint followed by a blob of data (the +** key). The varint is the number of bytes in the blob of data. */ -case OP_Savepoint: { - int p1; /* Value of P1 operand */ - char *zName; /* Name of savepoint */ - int nName; - Savepoint *pNew; - Savepoint *pSavepoint; - Savepoint *pTmp; - int iSavepoint; - int ii; +static int vdbeSorterListToPMA(SortSubtask *pTask, SorterList *pList){ + sqlcipher_sqlite3 *db = pTask->pSorter->db; + int rc = SQLITE_OK; /* Return code */ + PmaWriter writer; /* Object used to write to the file */ - p1 = pOp->p1; - zName = pOp->p4.z; +#ifdef SQLITE_DEBUG + /* Set iSz to the expected size of file pTask->file after writing the PMA. + ** This is used by an assert() statement at the end of this function. */ + i64 iSz = pList->szPMA + sqlcipher_sqlite3VarintLen(pList->szPMA) + pTask->file.iEof; +#endif - /* Assert that the p1 parameter is valid. Also that if there is no open - ** transaction, then there cannot be any savepoints. - */ - assert( db->pSavepoint==0 || db->autoCommit==0 ); - assert( p1==SAVEPOINT_BEGIN||p1==SAVEPOINT_RELEASE||p1==SAVEPOINT_ROLLBACK ); - assert( db->pSavepoint || db->isTransactionSavepoint==0 ); - assert( checkSavepointCount(db) ); - assert( p->bIsReader ); + vdbeSorterWorkDebug(pTask, "enter"); + memset(&writer, 0, sizeof(PmaWriter)); + assert( pList->szPMA>0 ); - if( p1==SAVEPOINT_BEGIN ){ - if( db->nVdbeWrite>0 ){ - /* A new savepoint cannot be created if there are active write - ** statements (i.e. open read/write incremental blob handles). - */ - sqlcipher_sqlite3VdbeError(p, "cannot open savepoint - SQL statements in progress"); - rc = SQLITE_BUSY; - }else{ - nName = sqlcipher_sqlite3Strlen30(zName); + /* If the first temporary PMA file has not been opened, open it now. */ + if( pTask->file.pFd==0 ){ + rc = vdbeSorterOpenTempFile(db, 0, &pTask->file.pFd); + assert( rc!=SQLITE_OK || pTask->file.pFd ); + assert( pTask->file.iEof==0 ); + assert( pTask->nPMA==0 ); + } -#ifndef SQLITE_OMIT_VIRTUALTABLE - /* This call is Ok even if this savepoint is actually a transaction - ** savepoint (and therefore should not prompt xSavepoint()) callbacks. - ** If this is a transaction savepoint being opened, it is guaranteed - ** that the db->aVTrans[] array is empty. */ - assert( db->autoCommit==0 || db->nVTrans==0 ); - rc = sqlcipher_sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, - db->nStatement+db->nSavepoint); - if( rc!=SQLITE_OK ) goto abort_due_to_error; -#endif + /* Try to get the file to memory map */ + if( rc==SQLITE_OK ){ + vdbeSorterExtendFile(db, pTask->file.pFd, pTask->file.iEof+pList->szPMA+9); + } - /* Create a new savepoint structure. */ - pNew = sqlcipher_sqlite3DbMallocRawNN(db, sizeof(Savepoint)+nName+1); - if( pNew ){ - pNew->zName = (char *)&pNew[1]; - memcpy(pNew->zName, zName, nName+1); + /* Sort the list */ + if( rc==SQLITE_OK ){ + rc = vdbeSorterSort(pTask, pList); + } - /* If there is no open transaction, then mark this as a special - ** "transaction savepoint". */ - if( db->autoCommit ){ - db->autoCommit = 0; - db->isTransactionSavepoint = 1; - }else{ - db->nSavepoint++; - } + if( rc==SQLITE_OK ){ + SorterRecord *p; + SorterRecord *pNext = 0; - /* Link the new savepoint into the database handle's list. */ - pNew->pNext = db->pSavepoint; - db->pSavepoint = pNew; - pNew->nDeferredCons = db->nDeferredCons; - pNew->nDeferredImmCons = db->nDeferredImmCons; - } + vdbePmaWriterInit(pTask->file.pFd, &writer, pTask->pSorter->pgsz, + pTask->file.iEof); + pTask->nPMA++; + vdbePmaWriteVarint(&writer, pList->szPMA); + for(p=pList->pList; p; p=pNext){ + pNext = p->u.pNext; + vdbePmaWriteVarint(&writer, p->nVal); + vdbePmaWriteBlob(&writer, SRVAL(p), p->nVal); + if( pList->aMemory==0 ) sqlcipher_sqlite3_free(p); } - }else{ - assert( p1==SAVEPOINT_RELEASE || p1==SAVEPOINT_ROLLBACK ); - iSavepoint = 0; + pList->pList = p; + rc = vdbePmaWriterFinish(&writer, &pTask->file.iEof); + } - /* Find the named savepoint. If there is no such savepoint, then an - ** an error is returned to the user. */ - for( - pSavepoint = db->pSavepoint; - pSavepoint && sqlcipher_sqlite3StrICmp(pSavepoint->zName, zName); - pSavepoint = pSavepoint->pNext - ){ - iSavepoint++; - } - if( !pSavepoint ){ - sqlcipher_sqlite3VdbeError(p, "no such savepoint: %s", zName); - rc = SQLITE_ERROR; - }else if( db->nVdbeWrite>0 && p1==SAVEPOINT_RELEASE ){ - /* It is not possible to release (commit) a savepoint if there are - ** active write statements. - */ - sqlcipher_sqlite3VdbeError(p, "cannot release savepoint - " - "SQL statements in progress"); - rc = SQLITE_BUSY; - }else{ + vdbeSorterWorkDebug(pTask, "exit"); + assert( rc!=SQLITE_OK || pList->pList==0 ); + assert( rc!=SQLITE_OK || pTask->file.iEof==iSz ); + return rc; +} - /* Determine whether or not this is a transaction savepoint. If so, - ** and this is a RELEASE command, then the current transaction - ** is committed. - */ - int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint; - if( isTransaction && p1==SAVEPOINT_RELEASE ){ - if( (rc = sqlcipher_sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ - goto vdbe_return; - } - db->autoCommit = 1; - if( sqlcipher_sqlite3VdbeHalt(p)==SQLITE_BUSY ){ - p->pc = (int)(pOp - aOp); - db->autoCommit = 0; - p->rc = rc = SQLITE_BUSY; - goto vdbe_return; - } - rc = p->rc; - if( rc ){ - db->autoCommit = 0; - }else{ - db->isTransactionSavepoint = 0; - } - }else{ - int isSchemaChange; - iSavepoint = db->nSavepoint - iSavepoint - 1; - if( p1==SAVEPOINT_ROLLBACK ){ - isSchemaChange = (db->mDbFlags & DBFLAG_SchemaChange)!=0; - for(ii=0; iinDb; ii++){ - rc = sqlcipher_sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, - SQLITE_ABORT_ROLLBACK, - isSchemaChange==0); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - }else{ - assert( p1==SAVEPOINT_RELEASE ); - isSchemaChange = 0; - } - for(ii=0; iinDb; ii++){ - rc = sqlcipher_sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - } - if( isSchemaChange ){ - sqlcipher_sqlite3ExpirePreparedStatements(db, 0); - sqlcipher_sqlite3ResetAllSchemasOfConnection(db); - db->mDbFlags |= DBFLAG_SchemaChange; - } - } - if( rc ) goto abort_due_to_error; +/* +** Advance the MergeEngine to its next entry. +** Set *pbEof to true there is no next entry because +** the MergeEngine has reached the end of all its inputs. +** +** Return SQLITE_OK if successful or an error code if an error occurs. +*/ +static int vdbeMergeEngineStep( + MergeEngine *pMerger, /* The merge engine to advance to the next row */ + int *pbEof /* Set TRUE at EOF. Set false for more content */ +){ + int rc; + int iPrev = pMerger->aTree[1];/* Index of PmaReader to advance */ + SortSubtask *pTask = pMerger->pTask; - /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all - ** savepoints nested inside of the savepoint being operated on. */ - while( db->pSavepoint!=pSavepoint ){ - pTmp = db->pSavepoint; - db->pSavepoint = pTmp->pNext; - sqlcipher_sqlite3DbFree(db, pTmp); - db->nSavepoint--; - } + /* Advance the current PmaReader */ + rc = vdbePmaReaderNext(&pMerger->aReadr[iPrev]); - /* If it is a RELEASE, then destroy the savepoint being operated on - ** too. If it is a ROLLBACK TO, then set the number of deferred - ** constraint violations present in the database to the value stored - ** when the savepoint was created. */ - if( p1==SAVEPOINT_RELEASE ){ - assert( pSavepoint==db->pSavepoint ); - db->pSavepoint = pSavepoint->pNext; - sqlcipher_sqlite3DbFree(db, pSavepoint); - if( !isTransaction ){ - db->nSavepoint--; - } + /* Update contents of aTree[] */ + if( rc==SQLITE_OK ){ + int i; /* Index of aTree[] to recalculate */ + PmaReader *pReadr1; /* First PmaReader to compare */ + PmaReader *pReadr2; /* Second PmaReader to compare */ + int bCached = 0; + + /* Find the first two PmaReaders to compare. The one that was just + ** advanced (iPrev) and the one next to it in the array. */ + pReadr1 = &pMerger->aReadr[(iPrev & 0xFFFE)]; + pReadr2 = &pMerger->aReadr[(iPrev | 0x0001)]; + + for(i=(pMerger->nTree+iPrev)/2; i>0; i=i/2){ + /* Compare pReadr1 and pReadr2. Store the result in variable iRes. */ + int iRes; + if( pReadr1->pFd==0 ){ + iRes = +1; + }else if( pReadr2->pFd==0 ){ + iRes = -1; }else{ - assert( p1==SAVEPOINT_ROLLBACK ); - db->nDeferredCons = pSavepoint->nDeferredCons; - db->nDeferredImmCons = pSavepoint->nDeferredImmCons; + iRes = pTask->xCompare(pTask, &bCached, + pReadr1->aKey, pReadr1->nKey, pReadr2->aKey, pReadr2->nKey + ); } - if( !isTransaction || p1==SAVEPOINT_ROLLBACK ){ - rc = sqlcipher_sqlite3VtabSavepoint(db, p1, iSavepoint); - if( rc!=SQLITE_OK ) goto abort_due_to_error; + /* If pReadr1 contained the smaller value, set aTree[i] to its index. + ** Then set pReadr2 to the next PmaReader to compare to pReadr1. In this + ** case there is no cache of pReadr2 in pTask->pUnpacked, so set + ** pKey2 to point to the record belonging to pReadr2. + ** + ** Alternatively, if pReadr2 contains the smaller of the two values, + ** set aTree[i] to its index and update pReadr1. If vdbeSorterCompare() + ** was actually called above, then pTask->pUnpacked now contains + ** a value equivalent to pReadr2. So set pKey2 to NULL to prevent + ** vdbeSorterCompare() from decoding pReadr2 again. + ** + ** If the two values were equal, then the value from the oldest + ** PMA should be considered smaller. The VdbeSorter.aReadr[] array + ** is sorted from oldest to newest, so pReadr1 contains older values + ** than pReadr2 iff (pReadr1aTree[i] = (int)(pReadr1 - pMerger->aReadr); + pReadr2 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ]; + bCached = 0; + }else{ + if( pReadr1->pFd ) bCached = 0; + pMerger->aTree[i] = (int)(pReadr2 - pMerger->aReadr); + pReadr1 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ]; } } + *pbEof = (pMerger->aReadr[pMerger->aTree[1]].pFd==0); } - if( rc ) goto abort_due_to_error; - break; + return (rc==SQLITE_OK ? pTask->pUnpacked->errCode : rc); } -/* Opcode: AutoCommit P1 P2 * * * -** -** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll -** back any currently active btree transactions. If there are any active -** VMs (apart from this one), then a ROLLBACK fails. A COMMIT fails if -** there are active writing VMs or active VMs that use shared cache. -** -** This instruction causes the VM to halt. +#if SQLITE_MAX_WORKER_THREADS>0 +/* +** The main routine for background threads that write level-0 PMAs. */ -case OP_AutoCommit: { - int desiredAutoCommit; - int iRollback; +static void *vdbeSorterFlushThread(void *pCtx){ + SortSubtask *pTask = (SortSubtask*)pCtx; + int rc; /* Return code */ + assert( pTask->bDone==0 ); + rc = vdbeSorterListToPMA(pTask, &pTask->list); + pTask->bDone = 1; + return SQLITE_INT_TO_PTR(rc); +} +#endif /* SQLITE_MAX_WORKER_THREADS>0 */ - desiredAutoCommit = pOp->p1; - iRollback = pOp->p2; - assert( desiredAutoCommit==1 || desiredAutoCommit==0 ); - assert( desiredAutoCommit==1 || iRollback==0 ); - assert( db->nVdbeActive>0 ); /* At least this one VM is active */ - assert( p->bIsReader ); +/* +** Flush the current contents of VdbeSorter.list to a new PMA, possibly +** using a background thread. +*/ +static int vdbeSorterFlushPMA(VdbeSorter *pSorter){ +#if SQLITE_MAX_WORKER_THREADS==0 + pSorter->bUsePMA = 1; + return vdbeSorterListToPMA(&pSorter->aTask[0], &pSorter->list); +#else + int rc = SQLITE_OK; + int i; + SortSubtask *pTask = 0; /* Thread context used to create new PMA */ + int nWorker = (pSorter->nTask-1); - if( desiredAutoCommit!=db->autoCommit ){ - if( iRollback ){ - assert( desiredAutoCommit==1 ); - sqlcipher_sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); - db->autoCommit = 1; - }else if( desiredAutoCommit && db->nVdbeWrite>0 ){ - /* If this instruction implements a COMMIT and other VMs are writing - ** return an error indicating that the other VMs must complete first. - */ - sqlcipher_sqlite3VdbeError(p, "cannot commit transaction - " - "SQL statements in progress"); - rc = SQLITE_BUSY; - goto abort_due_to_error; - }else if( (rc = sqlcipher_sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ - goto vdbe_return; - }else{ - db->autoCommit = (u8)desiredAutoCommit; - } - if( sqlcipher_sqlite3VdbeHalt(p)==SQLITE_BUSY ){ - p->pc = (int)(pOp - aOp); - db->autoCommit = (u8)(1-desiredAutoCommit); - p->rc = rc = SQLITE_BUSY; - goto vdbe_return; + /* Set the flag to indicate that at least one PMA has been written. + ** Or will be, anyhow. */ + pSorter->bUsePMA = 1; + + /* Select a sub-task to sort and flush the current list of in-memory + ** records to disk. If the sorter is running in multi-threaded mode, + ** round-robin between the first (pSorter->nTask-1) tasks. Except, if + ** the background thread from a sub-tasks previous turn is still running, + ** skip it. If the first (pSorter->nTask-1) sub-tasks are all still busy, + ** fall back to using the final sub-task. The first (pSorter->nTask-1) + ** sub-tasks are prefered as they use background threads - the final + ** sub-task uses the main thread. */ + for(i=0; iiPrev + i + 1) % nWorker; + pTask = &pSorter->aTask[iTest]; + if( pTask->bDone ){ + rc = vdbeSorterJoinThread(pTask); } - sqlcipher_sqlite3CloseSavepoints(db); - if( p->rc==SQLITE_OK ){ - rc = SQLITE_DONE; + if( rc!=SQLITE_OK || pTask->pThread==0 ) break; + } + + if( rc==SQLITE_OK ){ + if( i==nWorker ){ + /* Use the foreground thread for this operation */ + rc = vdbeSorterListToPMA(&pSorter->aTask[nWorker], &pSorter->list); }else{ - rc = SQLITE_ERROR; - } - goto vdbe_return; - }else{ - sqlcipher_sqlite3VdbeError(p, - (!desiredAutoCommit)?"cannot start a transaction within a transaction":( - (iRollback)?"cannot rollback - no transaction is active": - "cannot commit - no transaction is active")); + /* Launch a background thread for this operation */ + u8 *aMem; + void *pCtx; - rc = SQLITE_ERROR; - goto abort_due_to_error; + assert( pTask!=0 ); + assert( pTask->pThread==0 && pTask->bDone==0 ); + assert( pTask->list.pList==0 ); + assert( pTask->list.aMemory==0 || pSorter->list.aMemory!=0 ); + + aMem = pTask->list.aMemory; + pCtx = (void*)pTask; + pSorter->iPrev = (u8)(pTask - pSorter->aTask); + pTask->list = pSorter->list; + pSorter->list.pList = 0; + pSorter->list.szPMA = 0; + if( aMem ){ + pSorter->list.aMemory = aMem; + pSorter->nMemory = sqlcipher_sqlite3MallocSize(aMem); + }else if( pSorter->list.aMemory ){ + pSorter->list.aMemory = sqlcipher_sqlite3Malloc(pSorter->nMemory); + if( !pSorter->list.aMemory ) return SQLITE_NOMEM_BKPT; + } + + rc = vdbeSorterCreateThread(pTask, vdbeSorterFlushThread, pCtx); + } } - /*NOTREACHED*/ assert(0); + + return rc; +#endif /* SQLITE_MAX_WORKER_THREADS!=0 */ } -/* Opcode: Transaction P1 P2 P3 P4 P5 -** -** Begin a transaction on database P1 if a transaction is not already -** active. -** If P2 is non-zero, then a write-transaction is started, or if a -** read-transaction is already active, it is upgraded to a write-transaction. -** If P2 is zero, then a read-transaction is started. If P2 is 2 or more -** then an exclusive transaction is started. -** -** P1 is the index of the database file on which the transaction is -** started. Index 0 is the main database file and index 1 is the -** file used for temporary tables. Indices of 2 or more are used for -** attached databases. -** -** If a write-transaction is started and the Vdbe.usesStmtJournal flag is -** true (this flag is set if the Vdbe may modify more than one row and may -** throw an ABORT exception), a statement transaction may also be opened. -** More specifically, a statement transaction is opened iff the database -** connection is currently not in autocommit mode, or if there are other -** active statements. A statement transaction allows the changes made by this -** VDBE to be rolled back after an error without having to roll back the -** entire transaction. If no error is encountered, the statement transaction -** will automatically commit when the VDBE halts. -** -** If P5!=0 then this opcode also checks the schema cookie against P3 -** and the schema generation counter against P4. -** The cookie changes its value whenever the database schema changes. -** This operation is used to detect when that the cookie has changed -** and that the current process needs to reread the schema. If the schema -** cookie in P3 differs from the schema cookie in the database header or -** if the schema generation counter in P4 differs from the current -** generation counter, then an SQLITE_SCHEMA error is raised and execution -** halts. The sqlcipher_sqlite3_step() wrapper function might then reprepare the -** statement and rerun it from the beginning. +/* +** Add a record to the sorter. */ -case OP_Transaction: { - Btree *pBt; - int iMeta = 0; +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterWrite( + const VdbeCursor *pCsr, /* Sorter cursor */ + Mem *pVal /* Memory cell containing record */ +){ + VdbeSorter *pSorter; + int rc = SQLITE_OK; /* Return Code */ + SorterRecord *pNew; /* New list element */ + int bFlush; /* True to flush contents of memory to PMA */ + int nReq; /* Bytes of memory required */ + int nPMA; /* Bytes of PMA space required */ + int t; /* serial type of first record field */ - assert( p->bIsReader ); - assert( p->readOnly==0 || pOp->p2==0 ); - assert( pOp->p2>=0 && pOp->p2<=2 ); - assert( pOp->p1>=0 && pOp->p1nDb ); - assert( DbMaskTest(p->btreeMask, pOp->p1) ); - if( pOp->p2 && (db->flags & SQLITE_QueryOnly)!=0 ){ - rc = SQLITE_READONLY; - goto abort_due_to_error; + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; + getVarint32NR((const u8*)&pVal->z[1], t); + if( t>0 && t<10 && t!=7 ){ + pSorter->typeMask &= SORTER_TYPE_INTEGER; + }else if( t>10 && (t & 0x01) ){ + pSorter->typeMask &= SORTER_TYPE_TEXT; + }else{ + pSorter->typeMask = 0; } - pBt = db->aDb[pOp->p1].pBt; - if( pBt ){ - rc = sqlcipher_sqlite3BtreeBeginTrans(pBt, pOp->p2, &iMeta); - testcase( rc==SQLITE_BUSY_SNAPSHOT ); - testcase( rc==SQLITE_BUSY_RECOVERY ); - if( rc!=SQLITE_OK ){ - if( (rc&0xff)==SQLITE_BUSY ){ - p->pc = (int)(pOp - aOp); - p->rc = rc; - goto vdbe_return; - } - goto abort_due_to_error; + assert( pSorter ); + + /* Figure out whether or not the current contents of memory should be + ** flushed to a PMA before continuing. If so, do so. + ** + ** If using the single large allocation mode (pSorter->aMemory!=0), then + ** flush the contents of memory to a new PMA if (a) at least one value is + ** already in memory and (b) the new value will not fit in memory. + ** + ** Or, if using separate allocations for each record, flush the contents + ** of memory to a PMA if either of the following are true: + ** + ** * The total memory allocated for the in-memory list is greater + ** than (page-size * cache-size), or + ** + ** * The total memory allocated for the in-memory list is greater + ** than (page-size * 10) and sqlcipher_sqlite3HeapNearlyFull() returns true. + */ + nReq = pVal->n + sizeof(SorterRecord); + nPMA = pVal->n + sqlcipher_sqlite3VarintLen(pVal->n); + if( pSorter->mxPmaSize ){ + if( pSorter->list.aMemory ){ + bFlush = pSorter->iMemory && (pSorter->iMemory+nReq) > pSorter->mxPmaSize; + }else{ + bFlush = ( + (pSorter->list.szPMA > pSorter->mxPmaSize) + || (pSorter->list.szPMA > pSorter->mnPmaSize && sqlcipher_sqlite3HeapNearlyFull()) + ); + } + if( bFlush ){ + rc = vdbeSorterFlushPMA(pSorter); + pSorter->list.szPMA = 0; + pSorter->iMemory = 0; + assert( rc!=SQLITE_OK || pSorter->list.pList==0 ); } + } - if( p->usesStmtJournal - && pOp->p2 - && (db->autoCommit==0 || db->nVdbeRead>1) - ){ - assert( sqlcipher_sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ); - if( p->iStatement==0 ){ - assert( db->nStatement>=0 && db->nSavepoint>=0 ); - db->nStatement++; - p->iStatement = db->nSavepoint + db->nStatement; - } + pSorter->list.szPMA += nPMA; + if( nPMA>pSorter->mxKeysize ){ + pSorter->mxKeysize = nPMA; + } - rc = sqlcipher_sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement-1); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3BtreeBeginStmt(pBt, p->iStatement); + if( pSorter->list.aMemory ){ + int nMin = pSorter->iMemory + nReq; + + if( nMin>pSorter->nMemory ){ + u8 *aNew; + sqlcipher_sqlite3_int64 nNew = 2 * (sqlcipher_sqlite3_int64)pSorter->nMemory; + int iListOff = -1; + if( pSorter->list.pList ){ + iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory; + } + while( nNew < nMin ) nNew = nNew*2; + if( nNew > pSorter->mxPmaSize ) nNew = pSorter->mxPmaSize; + if( nNew < nMin ) nNew = nMin; + aNew = sqlcipher_sqlite3Realloc(pSorter->list.aMemory, nNew); + if( !aNew ) return SQLITE_NOMEM_BKPT; + if( iListOff>=0 ){ + pSorter->list.pList = (SorterRecord*)&aNew[iListOff]; } + pSorter->list.aMemory = aNew; + pSorter->nMemory = nNew; + } - /* Store the current value of the database handles deferred constraint - ** counter. If the statement transaction needs to be rolled back, - ** the value of this counter needs to be restored too. */ - p->nStmtDefCons = db->nDeferredCons; - p->nStmtDefImmCons = db->nDeferredImmCons; + pNew = (SorterRecord*)&pSorter->list.aMemory[pSorter->iMemory]; + pSorter->iMemory += ROUND8(nReq); + if( pSorter->list.pList ){ + pNew->u.iNext = (int)((u8*)(pSorter->list.pList) - pSorter->list.aMemory); } - } - assert( pOp->p5==0 || pOp->p4type==P4_INT32 ); - if( pOp->p5 - && (iMeta!=pOp->p3 - || db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i) - ){ - /* - ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema - ** version is checked to ensure that the schema has not changed since the - ** SQL statement was prepared. - */ - sqlcipher_sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = sqlcipher_sqlite3DbStrDup(db, "database schema has changed"); - /* If the schema-cookie from the database file matches the cookie - ** stored with the in-memory representation of the schema, do - ** not reload the schema from the database file. - ** - ** If virtual-tables are in use, this is not just an optimization. - ** Often, v-tables store their data in other SQLite tables, which - ** are queried from within xNext() and other v-table methods using - ** prepared queries. If such a query is out-of-date, we do not want to - ** discard the database schema, as the user code implementing the - ** v-table would have to be ready for the sqlcipher_sqlite3_vtab structure itself - ** to be invalidated whenever sqlcipher_sqlite3_step() is called from within - ** a v-table method. - */ - if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){ - sqlcipher_sqlite3ResetOneSchema(db, pOp->p1); + }else{ + pNew = (SorterRecord *)sqlcipher_sqlite3Malloc(nReq); + if( pNew==0 ){ + return SQLITE_NOMEM_BKPT; } - p->expired = 1; - rc = SQLITE_SCHEMA; + pNew->u.pNext = pSorter->list.pList; } - if( rc ) goto abort_due_to_error; - break; + + memcpy(SRVAL(pNew), pVal->z, pVal->n); + pNew->nVal = pVal->n; + pSorter->list.pList = pNew; + + return rc; } -/* Opcode: ReadCookie P1 P2 P3 * * -** -** Read cookie number P3 from database P1 and write it into register P2. -** P3==1 is the schema version. P3==2 is the database format. -** P3==3 is the recommended pager cache size, and so forth. P1==0 is -** the main database file and P1==1 is the database file used to store -** temporary tables. -** -** There must be a read-lock on the database (either a transaction -** must be started or there must be an open cursor) before -** executing this instruction. +/* +** Read keys from pIncr->pMerger and populate pIncr->aFile[1]. The format +** of the data stored in aFile[1] is the same as that used by regular PMAs, +** except that the number-of-bytes varint is omitted from the start. */ -case OP_ReadCookie: { /* out2 */ - int iMeta; - int iDb; - int iCookie; +static int vdbeIncrPopulate(IncrMerger *pIncr){ + int rc = SQLITE_OK; + int rc2; + i64 iStart = pIncr->iStartOff; + SorterFile *pOut = &pIncr->aFile[1]; + SortSubtask *pTask = pIncr->pTask; + MergeEngine *pMerger = pIncr->pMerger; + PmaWriter writer; + assert( pIncr->bEof==0 ); - assert( p->bIsReader ); - iDb = pOp->p1; - iCookie = pOp->p3; - assert( pOp->p3=0 && iDbnDb ); - assert( db->aDb[iDb].pBt!=0 ); - assert( DbMaskTest(p->btreeMask, iDb) ); + vdbeSorterPopulateDebug(pTask, "enter"); - sqlcipher_sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta); - pOut = out2Prerelease(p, pOp); - pOut->u.i = iMeta; - break; -} + vdbePmaWriterInit(pOut->pFd, &writer, pTask->pSorter->pgsz, iStart); + while( rc==SQLITE_OK ){ + int dummy; + PmaReader *pReader = &pMerger->aReadr[ pMerger->aTree[1] ]; + int nKey = pReader->nKey; + i64 iEof = writer.iWriteOff + writer.iBufEnd; -/* Opcode: SetCookie P1 P2 P3 * P5 -** -** Write the integer value P3 into cookie number P2 of database P1. -** P2==1 is the schema version. P2==2 is the database format. -** P2==3 is the recommended pager cache -** size, and so forth. P1==0 is the main database file and P1==1 is the -** database file used to store temporary tables. -** -** A transaction must be started before executing this opcode. -** -** If P2 is the SCHEMA_VERSION cookie (cookie number 1) then the internal -** schema version is set to P3-P5. The "PRAGMA schema_version=N" statement -** has P5 set to 1, so that the internal schema version will be different -** from the database schema version, resulting in a schema reset. -*/ -case OP_SetCookie: { - Db *pDb; + /* Check if the output file is full or if the input has been exhausted. + ** In either case exit the loop. */ + if( pReader->pFd==0 ) break; + if( (iEof + nKey + sqlcipher_sqlite3VarintLen(nKey))>(iStart + pIncr->mxSz) ) break; - sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); - assert( pOp->p2p1>=0 && pOp->p1nDb ); - assert( DbMaskTest(p->btreeMask, pOp->p1) ); - assert( p->readOnly==0 ); - pDb = &db->aDb[pOp->p1]; - assert( pDb->pBt!=0 ); - assert( sqlcipher_sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); - /* See note about index shifting on OP_ReadCookie */ - rc = sqlcipher_sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3); - if( pOp->p2==BTREE_SCHEMA_VERSION ){ - /* When the schema cookie changes, record the new cookie internally */ - pDb->pSchema->schema_cookie = pOp->p3 - pOp->p5; - db->mDbFlags |= DBFLAG_SchemaChange; - }else if( pOp->p2==BTREE_FILE_FORMAT ){ - /* Record changes in the file format */ - pDb->pSchema->file_format = pOp->p3; - } - if( pOp->p1==1 ){ - /* Invalidate all prepared statements whenever the TEMP database - ** schema is changed. Ticket #1644 */ - sqlcipher_sqlite3ExpirePreparedStatements(db, 0); - p->expired = 0; + /* Write the next key to the output. */ + vdbePmaWriteVarint(&writer, nKey); + vdbePmaWriteBlob(&writer, pReader->aKey, nKey); + assert( pIncr->pMerger->pTask==pTask ); + rc = vdbeMergeEngineStep(pIncr->pMerger, &dummy); } - if( rc ) goto abort_due_to_error; - break; + + rc2 = vdbePmaWriterFinish(&writer, &pOut->iEof); + if( rc==SQLITE_OK ) rc = rc2; + vdbeSorterPopulateDebug(pTask, "exit"); + return rc; } -/* Opcode: OpenRead P1 P2 P3 P4 P5 -** Synopsis: root=P2 iDb=P3 -** -** Open a read-only cursor for the database table whose root page is -** P2 in a database file. The database file is determined by P3. -** P3==0 means the main database, P3==1 means the database used for -** temporary tables, and P3>1 means used the corresponding attached -** database. Give the new cursor an identifier of P1. The P1 -** values need not be contiguous but all P1 values should be small integers. -** It is an error for P1 to be negative. -** -** Allowed P5 bits: -**
      -**
    • 0x02 OPFLAG_SEEKEQ: This cursor will only be used for -** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT -** of OP_SeekLE/OP_IdxLT) -**
    -** -** The P4 value may be either an integer (P4_INT32) or a pointer to -** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo -** object, then table being opened must be an [index b-tree] where the -** KeyInfo object defines the content and collating -** sequence of that index b-tree. Otherwise, if P4 is an integer -** value, then the table being opened must be a [table b-tree] with a -** number of columns no less than the value of P4. -** -** See also: OpenWrite, ReopenIdx +#if SQLITE_MAX_WORKER_THREADS>0 +/* +** The main routine for background threads that populate aFile[1] of +** multi-threaded IncrMerger objects. */ -/* Opcode: ReopenIdx P1 P2 P3 P4 P5 -** Synopsis: root=P2 iDb=P3 -** -** The ReopenIdx opcode works like OP_OpenRead except that it first -** checks to see if the cursor on P1 is already open on the same -** b-tree and if it is this opcode becomes a no-op. In other words, -** if the cursor is already open, do not reopen it. -** -** The ReopenIdx opcode may only be used with P5==0 or P5==OPFLAG_SEEKEQ -** and with P4 being a P4_KEYINFO object. Furthermore, the P3 value must -** be the same as every other ReopenIdx or OpenRead for the same cursor -** number. -** -** Allowed P5 bits: -**
      -**
    • 0x02 OPFLAG_SEEKEQ: This cursor will only be used for -** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT -** of OP_SeekLE/OP_IdxLT) -**
    -** -** See also: OP_OpenRead, OP_OpenWrite +static void *vdbeIncrPopulateThread(void *pCtx){ + IncrMerger *pIncr = (IncrMerger*)pCtx; + void *pRet = SQLITE_INT_TO_PTR( vdbeIncrPopulate(pIncr) ); + pIncr->pTask->bDone = 1; + return pRet; +} + +/* +** Launch a background thread to populate aFile[1] of pIncr. */ -/* Opcode: OpenWrite P1 P2 P3 P4 P5 -** Synopsis: root=P2 iDb=P3 -** -** Open a read/write cursor named P1 on the table or index whose root -** page is P2 (or whose root page is held in register P2 if the -** OPFLAG_P2ISREG bit is set in P5 - see below). -** -** The P4 value may be either an integer (P4_INT32) or a pointer to -** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo -** object, then table being opened must be an [index b-tree] where the -** KeyInfo object defines the content and collating -** sequence of that index b-tree. Otherwise, if P4 is an integer -** value, then the table being opened must be a [table b-tree] with a -** number of columns no less than the value of P4. +static int vdbeIncrBgPopulate(IncrMerger *pIncr){ + void *p = (void*)pIncr; + assert( pIncr->bUseThread ); + return vdbeSorterCreateThread(pIncr->pTask, vdbeIncrPopulateThread, p); +} +#endif + +/* +** This function is called when the PmaReader corresponding to pIncr has +** finished reading the contents of aFile[0]. Its purpose is to "refill" +** aFile[0] such that the PmaReader should start rereading it from the +** beginning. ** -** Allowed P5 bits: -**
      -**
    • 0x02 OPFLAG_SEEKEQ: This cursor will only be used for -** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT -** of OP_SeekLE/OP_IdxLT) -**
    • 0x08 OPFLAG_FORDELETE: This cursor is used only to seek -** and subsequently delete entries in an index btree. This is a -** hint to the storage engine that the storage engine is allowed to -** ignore. The hint is not used by the official SQLite b*tree storage -** engine, but is used by COMDB2. -**
    • 0x10 OPFLAG_P2ISREG: Use the content of register P2 -** as the root page, not the value of P2 itself. -**
    +** For single-threaded objects, this is accomplished by literally reading +** keys from pIncr->pMerger and repopulating aFile[0]. ** -** This instruction works like OpenRead except that it opens the cursor -** in read/write mode. +** For multi-threaded objects, all that is required is to wait until the +** background thread is finished (if it is not already) and then swap +** aFile[0] and aFile[1] in place. If the contents of pMerger have not +** been exhausted, this function also launches a new background thread +** to populate the new aFile[1]. ** -** See also: OP_OpenRead, OP_ReopenIdx +** SQLITE_OK is returned on success, or an SQLite error code otherwise. */ -case OP_ReopenIdx: { - int nField; - KeyInfo *pKeyInfo; - u32 p2; - int iDb; - int wrFlag; - Btree *pX; - VdbeCursor *pCur; - Db *pDb; +static int vdbeIncrSwap(IncrMerger *pIncr){ + int rc = SQLITE_OK; - assert( pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); - assert( pOp->p4type==P4_KEYINFO ); - pCur = p->apCsr[pOp->p1]; - if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){ - assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */ - goto open_cursor_set_hints; - } - /* If the cursor is not currently open or is open on a different - ** index, then fall through into OP_OpenRead to force a reopen */ -case OP_OpenRead: -case OP_OpenWrite: +#if SQLITE_MAX_WORKER_THREADS>0 + if( pIncr->bUseThread ){ + rc = vdbeSorterJoinThread(pIncr->pTask); - assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); - assert( p->bIsReader ); - assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx - || p->readOnly==0 ); + if( rc==SQLITE_OK ){ + SorterFile f0 = pIncr->aFile[0]; + pIncr->aFile[0] = pIncr->aFile[1]; + pIncr->aFile[1] = f0; + } - if( p->expired==1 ){ - rc = SQLITE_ABORT_ROLLBACK; - goto abort_due_to_error; + if( rc==SQLITE_OK ){ + if( pIncr->aFile[0].iEof==pIncr->iStartOff ){ + pIncr->bEof = 1; + }else{ + rc = vdbeIncrBgPopulate(pIncr); + } + } + }else +#endif + { + rc = vdbeIncrPopulate(pIncr); + pIncr->aFile[0] = pIncr->aFile[1]; + if( pIncr->aFile[0].iEof==pIncr->iStartOff ){ + pIncr->bEof = 1; + } } - nField = 0; - pKeyInfo = 0; - p2 = (u32)pOp->p2; - iDb = pOp->p3; - assert( iDb>=0 && iDbnDb ); - assert( DbMaskTest(p->btreeMask, iDb) ); - pDb = &db->aDb[iDb]; - pX = pDb->pBt; - assert( pX!=0 ); - if( pOp->opcode==OP_OpenWrite ){ - assert( OPFLAG_FORDELETE==BTREE_FORDELETE ); - wrFlag = BTREE_WRCSR | (pOp->p5 & OPFLAG_FORDELETE); - assert( sqlcipher_sqlite3SchemaMutexHeld(db, iDb, 0) ); - if( pDb->pSchema->file_format < p->minWriteFileFormat ){ - p->minWriteFileFormat = pDb->pSchema->file_format; - } + return rc; +} + +/* +** Allocate and return a new IncrMerger object to read data from pMerger. +** +** If an OOM condition is encountered, return NULL. In this case free the +** pMerger argument before returning. +*/ +static int vdbeIncrMergerNew( + SortSubtask *pTask, /* The thread that will be using the new IncrMerger */ + MergeEngine *pMerger, /* The MergeEngine that the IncrMerger will control */ + IncrMerger **ppOut /* Write the new IncrMerger here */ +){ + int rc = SQLITE_OK; + IncrMerger *pIncr = *ppOut = (IncrMerger*) + (sqlcipher_sqlite3FaultSim(100) ? 0 : sqlcipher_sqlite3MallocZero(sizeof(*pIncr))); + if( pIncr ){ + pIncr->pMerger = pMerger; + pIncr->pTask = pTask; + pIncr->mxSz = MAX(pTask->pSorter->mxKeysize+9,pTask->pSorter->mxPmaSize/2); + pTask->file2.iEof += pIncr->mxSz; }else{ - wrFlag = 0; + vdbeMergeEngineFree(pMerger); + rc = SQLITE_NOMEM_BKPT; } - if( pOp->p5 & OPFLAG_P2ISREG ){ - assert( p2>0 ); - assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); - assert( pOp->opcode==OP_OpenWrite ); - pIn2 = &aMem[p2]; - assert( memIsValid(pIn2) ); - assert( (pIn2->flags & MEM_Int)!=0 ); - sqlcipher_sqlite3VdbeMemIntegerify(pIn2); - p2 = (int)pIn2->u.i; - /* The p2 value always comes from a prior OP_CreateBtree opcode and - ** that opcode will always set the p2 value to 2 or more or else fail. - ** If there were a failure, the prepared statement would have halted - ** before reaching this instruction. */ - assert( p2>=2 ); + assert( *ppOut!=0 || rc!=SQLITE_OK ); + return rc; +} + +#if SQLITE_MAX_WORKER_THREADS>0 +/* +** Set the "use-threads" flag on object pIncr. +*/ +static void vdbeIncrMergerSetThreads(IncrMerger *pIncr){ + pIncr->bUseThread = 1; + pIncr->pTask->file2.iEof -= pIncr->mxSz; +} +#endif /* SQLITE_MAX_WORKER_THREADS>0 */ + + + +/* +** Recompute pMerger->aTree[iOut] by comparing the next keys on the +** two PmaReaders that feed that entry. Neither of the PmaReaders +** are advanced. This routine merely does the comparison. +*/ +static void vdbeMergeEngineCompare( + MergeEngine *pMerger, /* Merge engine containing PmaReaders to compare */ + int iOut /* Store the result in pMerger->aTree[iOut] */ +){ + int i1; + int i2; + int iRes; + PmaReader *p1; + PmaReader *p2; + + assert( iOutnTree && iOut>0 ); + + if( iOut>=(pMerger->nTree/2) ){ + i1 = (iOut - pMerger->nTree/2) * 2; + i2 = i1 + 1; + }else{ + i1 = pMerger->aTree[iOut*2]; + i2 = pMerger->aTree[iOut*2+1]; } - if( pOp->p4type==P4_KEYINFO ){ - pKeyInfo = pOp->p4.pKeyInfo; - assert( pKeyInfo->enc==ENC(db) ); - assert( pKeyInfo->db==db ); - nField = pKeyInfo->nAllField; - }else if( pOp->p4type==P4_INT32 ){ - nField = pOp->p4.i; + + p1 = &pMerger->aReadr[i1]; + p2 = &pMerger->aReadr[i2]; + + if( p1->pFd==0 ){ + iRes = i2; + }else if( p2->pFd==0 ){ + iRes = i1; + }else{ + SortSubtask *pTask = pMerger->pTask; + int bCached = 0; + int res; + assert( pTask->pUnpacked!=0 ); /* from vdbeSortSubtaskMain() */ + res = pTask->xCompare( + pTask, &bCached, p1->aKey, p1->nKey, p2->aKey, p2->nKey + ); + if( res<=0 ){ + iRes = i1; + }else{ + iRes = i2; + } } - assert( pOp->p1>=0 ); - assert( nField>=0 ); - testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */ - pCur = allocateCursor(p, pOp->p1, nField, iDb, CURTYPE_BTREE); - if( pCur==0 ) goto no_mem; - pCur->nullRow = 1; - pCur->isOrdered = 1; - pCur->pgnoRoot = p2; -#ifdef SQLITE_DEBUG - pCur->wrFlag = wrFlag; -#endif - rc = sqlcipher_sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->uc.pCursor); - pCur->pKeyInfo = pKeyInfo; - /* Set the VdbeCursor.isTable variable. Previous versions of - ** SQLite used to check if the root-page flags were sane at this point - ** and report database corruption if they were not, but this check has - ** since moved into the btree layer. */ - pCur->isTable = pOp->p4type!=P4_KEYINFO; -open_cursor_set_hints: - assert( OPFLAG_BULKCSR==BTREE_BULKLOAD ); - assert( OPFLAG_SEEKEQ==BTREE_SEEK_EQ ); - testcase( pOp->p5 & OPFLAG_BULKCSR ); - testcase( pOp->p2 & OPFLAG_SEEKEQ ); - sqlcipher_sqlite3BtreeCursorHintFlags(pCur->uc.pCursor, - (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ))); - if( rc ) goto abort_due_to_error; - break; + pMerger->aTree[iOut] = iRes; } -/* Opcode: OpenDup P1 P2 * * * +/* +** Allowed values for the eMode parameter to vdbeMergeEngineInit() +** and vdbePmaReaderIncrMergeInit(). ** -** Open a new cursor P1 that points to the same ephemeral table as -** cursor P2. The P2 cursor must have been opened by a prior OP_OpenEphemeral -** opcode. Only ephemeral cursors may be duplicated. +** Only INCRINIT_NORMAL is valid in single-threaded builds (when +** SQLITE_MAX_WORKER_THREADS==0). The other values are only used +** when there exists one or more separate worker threads. +*/ +#define INCRINIT_NORMAL 0 +#define INCRINIT_TASK 1 +#define INCRINIT_ROOT 2 + +/* +** Forward reference required as the vdbeIncrMergeInit() and +** vdbePmaReaderIncrInit() routines are called mutually recursively when +** building a merge tree. +*/ +static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode); + +/* +** Initialize the MergeEngine object passed as the second argument. Once this +** function returns, the first key of merged data may be read from the +** MergeEngine object in the usual fashion. ** -** Duplicate ephemeral cursors are used for self-joins of materialized views. +** If argument eMode is INCRINIT_ROOT, then it is assumed that any IncrMerge +** objects attached to the PmaReader objects that the merger reads from have +** already been populated, but that they have not yet populated aFile[0] and +** set the PmaReader objects up to read from it. In this case all that is +** required is to call vdbePmaReaderNext() on each PmaReader to point it at +** its first key. +** +** Otherwise, if eMode is any value other than INCRINIT_ROOT, then use +** vdbePmaReaderIncrMergeInit() to initialize each PmaReader that feeds data +** to pMerger. +** +** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ -case OP_OpenDup: { - VdbeCursor *pOrig; /* The original cursor to be duplicated */ - VdbeCursor *pCx; /* The new cursor */ +static int vdbeMergeEngineInit( + SortSubtask *pTask, /* Thread that will run pMerger */ + MergeEngine *pMerger, /* MergeEngine to initialize */ + int eMode /* One of the INCRINIT_XXX constants */ +){ + int rc = SQLITE_OK; /* Return code */ + int i; /* For looping over PmaReader objects */ + int nTree; /* Number of subtrees to merge */ - pOrig = p->apCsr[pOp->p2]; - assert( pOrig ); - assert( pOrig->pBtx!=0 ); /* Only ephemeral cursors can be duplicated */ + /* Failure to allocate the merge would have been detected prior to + ** invoking this routine */ + assert( pMerger!=0 ); - pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE); - if( pCx==0 ) goto no_mem; - pCx->nullRow = 1; - pCx->isEphemeral = 1; - pCx->pKeyInfo = pOrig->pKeyInfo; - pCx->isTable = pOrig->isTable; - pCx->pgnoRoot = pOrig->pgnoRoot; - pCx->isOrdered = pOrig->isOrdered; - rc = sqlcipher_sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR, - pCx->pKeyInfo, pCx->uc.pCursor); - /* The sqlcipher_sqlite3BtreeCursor() routine can only fail for the first cursor - ** opened for a database. Since there is already an open cursor when this - ** opcode is run, the sqlcipher_sqlite3BtreeCursor() cannot fail */ - assert( rc==SQLITE_OK ); - break; -} + /* eMode is always INCRINIT_NORMAL in single-threaded mode */ + assert( SQLITE_MAX_WORKER_THREADS>0 || eMode==INCRINIT_NORMAL ); + /* Verify that the MergeEngine is assigned to a single thread */ + assert( pMerger->pTask==0 ); + pMerger->pTask = pTask; -/* Opcode: OpenEphemeral P1 P2 P3 P4 P5 -** Synopsis: nColumn=P2 -** -** Open a new cursor P1 to a transient table. -** The cursor is always opened read/write even if -** the main database is read-only. The ephemeral -** table is deleted automatically when the cursor is closed. + nTree = pMerger->nTree; + for(i=0; i0 && eMode==INCRINIT_ROOT ){ + /* PmaReaders should be normally initialized in order, as if they are + ** reading from the same temp file this makes for more linear file IO. + ** However, in the INCRINIT_ROOT case, if PmaReader aReadr[nTask-1] is + ** in use it will block the vdbePmaReaderNext() call while it uses + ** the main thread to fill its buffer. So calling PmaReaderNext() + ** on this PmaReader before any of the multi-threaded PmaReaders takes + ** better advantage of multi-processor hardware. */ + rc = vdbePmaReaderNext(&pMerger->aReadr[nTree-i-1]); + }else{ + rc = vdbePmaReaderIncrInit(&pMerger->aReadr[i], INCRINIT_NORMAL); + } + if( rc!=SQLITE_OK ) return rc; + } + + for(i=pMerger->nTree-1; i>0; i--){ + vdbeMergeEngineCompare(pMerger, i); + } + return pTask->pUnpacked->errCode; +} + +/* +** The PmaReader passed as the first argument is guaranteed to be an +** incremental-reader (pReadr->pIncr!=0). This function serves to open +** and/or initialize the temp file related fields of the IncrMerge +** object at (pReadr->pIncr). ** -** If the cursor P1 is already opened on an ephemeral table, the table -** is cleared (all content is erased). +** If argument eMode is set to INCRINIT_NORMAL, then all PmaReaders +** in the sub-tree headed by pReadr are also initialized. Data is then +** loaded into the buffers belonging to pReadr and it is set to point to +** the first key in its range. ** -** P2 is the number of columns in the ephemeral table. -** The cursor points to a BTree table if P4==0 and to a BTree index -** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure -** that defines the format of keys in the index. +** If argument eMode is set to INCRINIT_TASK, then pReadr is guaranteed +** to be a multi-threaded PmaReader and this function is being called in a +** background thread. In this case all PmaReaders in the sub-tree are +** initialized as for INCRINIT_NORMAL and the aFile[1] buffer belonging to +** pReadr is populated. However, pReadr itself is not set up to point +** to its first key. A call to vdbePmaReaderNext() is still required to do +** that. ** -** The P5 parameter can be a mask of the BTREE_* flags defined -** in btree.h. These flags control aspects of the operation of -** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are -** added automatically. +** The reason this function does not call vdbePmaReaderNext() immediately +** in the INCRINIT_TASK case is that vdbePmaReaderNext() assumes that it has +** to block on thread (pTask->thread) before accessing aFile[1]. But, since +** this entire function is being run by thread (pTask->thread), that will +** lead to the current background thread attempting to join itself. ** -** If P3 is positive, then reg[P3] is modified slightly so that it -** can be used as zero-length data for OP_Insert. This is an optimization -** that avoids an extra OP_Blob opcode to initialize that register. -*/ -/* Opcode: OpenAutoindex P1 P2 * P4 * -** Synopsis: nColumn=P2 +** Finally, if argument eMode is set to INCRINIT_ROOT, it may be assumed +** that pReadr->pIncr is a multi-threaded IncrMerge objects, and that all +** child-trees have already been initialized using IncrInit(INCRINIT_TASK). +** In this case vdbePmaReaderNext() is called on all child PmaReaders and +** the current PmaReader set to point to the first key in its range. ** -** This opcode works the same as OP_OpenEphemeral. It has a -** different name to distinguish its use. Tables created using -** by this opcode will be used for automatically created transient -** indices in joins. +** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ -case OP_OpenAutoindex: -case OP_OpenEphemeral: { - VdbeCursor *pCx; - KeyInfo *pKeyInfo; +static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){ + int rc = SQLITE_OK; + IncrMerger *pIncr = pReadr->pIncr; + SortSubtask *pTask = pIncr->pTask; + sqlcipher_sqlite3 *db = pTask->pSorter->db; - static const int vfsFlags = - SQLITE_OPEN_READWRITE | - SQLITE_OPEN_CREATE | - SQLITE_OPEN_EXCLUSIVE | - SQLITE_OPEN_DELETEONCLOSE | - SQLITE_OPEN_TRANSIENT_DB; - assert( pOp->p1>=0 ); - assert( pOp->p2>=0 ); - if( pOp->p3>0 ){ - /* Make register reg[P3] into a value that can be used as the data - ** form sqlcipher_sqlite3BtreeInsert() where the length of the data is zero. */ - assert( pOp->p2==0 ); /* Only used when number of columns is zero */ - assert( pOp->opcode==OP_OpenEphemeral ); - assert( aMem[pOp->p3].flags & MEM_Null ); - aMem[pOp->p3].n = 0; - aMem[pOp->p3].z = ""; - } - pCx = p->apCsr[pOp->p1]; - if( pCx && pCx->pBtx ){ - /* If the ephermeral table is already open, erase all existing content - ** so that the table is empty again, rather than creating a new table. */ - assert( pCx->isEphemeral ); - pCx->seqCount = 0; - pCx->cacheStatus = CACHE_STALE; - rc = sqlcipher_sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0); - }else{ - pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); - if( pCx==0 ) goto no_mem; - pCx->isEphemeral = 1; - rc = sqlcipher_sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, - BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, - vfsFlags); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); - } - if( rc==SQLITE_OK ){ - /* If a transient index is required, create it by calling - ** sqlcipher_sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before - ** opening it. If a transient table is required, just use the - ** automatically created table with root-page 1 (an BLOB_INTKEY table). - */ - if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ - assert( pOp->p4type==P4_KEYINFO ); - rc = sqlcipher_sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, - BTREE_BLOBKEY | pOp->p5); - if( rc==SQLITE_OK ){ - assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); - assert( pKeyInfo->db==db ); - assert( pKeyInfo->enc==ENC(db) ); - rc = sqlcipher_sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, - pKeyInfo, pCx->uc.pCursor); - } - pCx->isTable = 0; - }else{ - pCx->pgnoRoot = SCHEMA_ROOT; - rc = sqlcipher_sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR, - 0, pCx->uc.pCursor); - pCx->isTable = 1; + /* eMode is always INCRINIT_NORMAL in single-threaded mode */ + assert( SQLITE_MAX_WORKER_THREADS>0 || eMode==INCRINIT_NORMAL ); + + rc = vdbeMergeEngineInit(pTask, pIncr->pMerger, eMode); + + /* Set up the required files for pIncr. A multi-theaded IncrMerge object + ** requires two temp files to itself, whereas a single-threaded object + ** only requires a region of pTask->file2. */ + if( rc==SQLITE_OK ){ + int mxSz = pIncr->mxSz; +#if SQLITE_MAX_WORKER_THREADS>0 + if( pIncr->bUseThread ){ + rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[0].pFd); + if( rc==SQLITE_OK ){ + rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[1].pFd); + } + }else +#endif + /*if( !pIncr->bUseThread )*/{ + if( pTask->file2.pFd==0 ){ + assert( pTask->file2.iEof>0 ); + rc = vdbeSorterOpenTempFile(db, pTask->file2.iEof, &pTask->file2.pFd); + pTask->file2.iEof = 0; + } + if( rc==SQLITE_OK ){ + pIncr->aFile[1].pFd = pTask->file2.pFd; + pIncr->iStartOff = pTask->file2.iEof; + pTask->file2.iEof += mxSz; } } - pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); } - if( rc ) goto abort_due_to_error; - pCx->nullRow = 1; - break; + +#if SQLITE_MAX_WORKER_THREADS>0 + if( rc==SQLITE_OK && pIncr->bUseThread ){ + /* Use the current thread to populate aFile[1], even though this + ** PmaReader is multi-threaded. If this is an INCRINIT_TASK object, + ** then this function is already running in background thread + ** pIncr->pTask->thread. + ** + ** If this is the INCRINIT_ROOT object, then it is running in the + ** main VDBE thread. But that is Ok, as that thread cannot return + ** control to the VDBE or proceed with anything useful until the + ** first results are ready from this merger object anyway. + */ + assert( eMode==INCRINIT_ROOT || eMode==INCRINIT_TASK ); + rc = vdbeIncrPopulate(pIncr); + } +#endif + + if( rc==SQLITE_OK && (SQLITE_MAX_WORKER_THREADS==0 || eMode!=INCRINIT_TASK) ){ + rc = vdbePmaReaderNext(pReadr); + } + + return rc; } -/* Opcode: SorterOpen P1 P2 P3 P4 * -** -** This opcode works like OP_OpenEphemeral except that it opens -** a transient index that is specifically designed to sort large -** tables using an external merge-sort algorithm. -** -** If argument P3 is non-zero, then it indicates that the sorter may -** assume that a stable sort considering the first P3 fields of each -** key is sufficient to produce the required results. +#if SQLITE_MAX_WORKER_THREADS>0 +/* +** The main routine for vdbePmaReaderIncrMergeInit() operations run in +** background threads. */ -case OP_SorterOpen: { - VdbeCursor *pCx; - - assert( pOp->p1>=0 ); - assert( pOp->p2>=0 ); - pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_SORTER); - if( pCx==0 ) goto no_mem; - pCx->pKeyInfo = pOp->p4.pKeyInfo; - assert( pCx->pKeyInfo->db==db ); - assert( pCx->pKeyInfo->enc==ENC(db) ); - rc = sqlcipher_sqlite3VdbeSorterInit(db, pOp->p3, pCx); - if( rc ) goto abort_due_to_error; - break; +static void *vdbePmaReaderBgIncrInit(void *pCtx){ + PmaReader *pReader = (PmaReader*)pCtx; + void *pRet = SQLITE_INT_TO_PTR( + vdbePmaReaderIncrMergeInit(pReader,INCRINIT_TASK) + ); + pReader->pIncr->pTask->bDone = 1; + return pRet; } +#endif -/* Opcode: SequenceTest P1 P2 * * * -** Synopsis: if( cursor[P1].ctr++ ) pc = P2 +/* +** If the PmaReader passed as the first argument is not an incremental-reader +** (if pReadr->pIncr==0), then this function is a no-op. Otherwise, it invokes +** the vdbePmaReaderIncrMergeInit() function with the parameters passed to +** this routine to initialize the incremental merge. ** -** P1 is a sorter cursor. If the sequence counter is currently zero, jump -** to P2. Regardless of whether or not the jump is taken, increment the -** the sequence value. +** If the IncrMerger object is multi-threaded (IncrMerger.bUseThread==1), +** then a background thread is launched to call vdbePmaReaderIncrMergeInit(). +** Or, if the IncrMerger is single threaded, the same function is called +** using the current thread. */ -case OP_SequenceTest: { - VdbeCursor *pC; - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( isSorter(pC) ); - if( (pC->seqCount++)==0 ){ - goto jump_to_p2; +static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode){ + IncrMerger *pIncr = pReadr->pIncr; /* Incremental merger */ + int rc = SQLITE_OK; /* Return code */ + if( pIncr ){ +#if SQLITE_MAX_WORKER_THREADS>0 + assert( pIncr->bUseThread==0 || eMode==INCRINIT_TASK ); + if( pIncr->bUseThread ){ + void *pCtx = (void*)pReadr; + rc = vdbeSorterCreateThread(pIncr->pTask, vdbePmaReaderBgIncrInit, pCtx); + }else +#endif + { + rc = vdbePmaReaderIncrMergeInit(pReadr, eMode); + } } - break; + return rc; } -/* Opcode: OpenPseudo P1 P2 P3 * * -** Synopsis: P3 columns in r[P2] -** -** Open a new cursor that points to a fake table that contains a single -** row of data. The content of that one row is the content of memory -** register P2. In other words, cursor P1 becomes an alias for the -** MEM_Blob content contained in register P2. -** -** A pseudo-table created by this opcode is used to hold a single -** row output from the sorter so that the row can be decomposed into -** individual columns using the OP_Column opcode. The OP_Column opcode -** is the only cursor opcode that works with a pseudo-table. +/* +** Allocate a new MergeEngine object to merge the contents of nPMA level-0 +** PMAs from pTask->file. If no error occurs, set *ppOut to point to +** the new object and return SQLITE_OK. Or, if an error does occur, set *ppOut +** to NULL and return an SQLite error code. ** -** P3 is the number of fields in the records that will be stored by -** the pseudo-table. +** When this function is called, *piOffset is set to the offset of the +** first PMA to read from pTask->file. Assuming no error occurs, it is +** set to the offset immediately following the last byte of the last +** PMA before returning. If an error does occur, then the final value of +** *piOffset is undefined. */ -case OP_OpenPseudo: { - VdbeCursor *pCx; +static int vdbeMergeEngineLevel0( + SortSubtask *pTask, /* Sorter task to read from */ + int nPMA, /* Number of PMAs to read */ + i64 *piOffset, /* IN/OUT: Readr offset in pTask->file */ + MergeEngine **ppOut /* OUT: New merge-engine */ +){ + MergeEngine *pNew; /* Merge engine to return */ + i64 iOff = *piOffset; + int i; + int rc = SQLITE_OK; - assert( pOp->p1>=0 ); - assert( pOp->p3>=0 ); - pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, CURTYPE_PSEUDO); - if( pCx==0 ) goto no_mem; - pCx->nullRow = 1; - pCx->seekResult = pOp->p2; - pCx->isTable = 1; - /* Give this pseudo-cursor a fake BtCursor pointer so that pCx - ** can be safely passed to sqlcipher_sqlite3VdbeCursorMoveto(). This avoids a test - ** for pCx->eCurType==CURTYPE_BTREE inside of sqlcipher_sqlite3VdbeCursorMoveto() - ** which is a performance optimization */ - pCx->uc.pCursor = sqlcipher_sqlite3BtreeFakeValidCursor(); - assert( pOp->p5==0 ); - break; + *ppOut = pNew = vdbeMergeEngineNew(nPMA); + if( pNew==0 ) rc = SQLITE_NOMEM_BKPT; + + for(i=0; iaReadr[i]; + rc = vdbePmaReaderInit(pTask, &pTask->file, iOff, pReadr, &nDummy); + iOff = pReadr->iEof; + } + + if( rc!=SQLITE_OK ){ + vdbeMergeEngineFree(pNew); + *ppOut = 0; + } + *piOffset = iOff; + return rc; } -/* Opcode: Close P1 * * * * +/* +** Return the depth of a tree comprising nPMA PMAs, assuming a fanout of +** SORTER_MAX_MERGE_COUNT. The returned value does not include leaf nodes. ** -** Close a cursor previously opened as P1. If P1 is not -** currently open, this instruction is a no-op. +** i.e. +** +** nPMA<=16 -> TreeDepth() == 0 +** nPMA<=256 -> TreeDepth() == 1 +** nPMA<=65536 -> TreeDepth() == 2 */ -case OP_Close: { - assert( pOp->p1>=0 && pOp->p1nCursor ); - sqlcipher_sqlite3VdbeFreeCursor(p, p->apCsr[pOp->p1]); - p->apCsr[pOp->p1] = 0; - break; +static int vdbeSorterTreeDepth(int nPMA){ + int nDepth = 0; + i64 nDiv = SORTER_MAX_MERGE_COUNT; + while( nDiv < (i64)nPMA ){ + nDiv = nDiv * SORTER_MAX_MERGE_COUNT; + nDepth++; + } + return nDepth; } -#ifdef SQLITE_ENABLE_COLUMN_USED_MASK -/* Opcode: ColumnsUsed P1 * * P4 * +/* +** pRoot is the root of an incremental merge-tree with depth nDepth (according +** to vdbeSorterTreeDepth()). pLeaf is the iSeq'th leaf to be added to the +** tree, counting from zero. This function adds pLeaf to the tree. ** -** This opcode (which only exists if SQLite was compiled with -** SQLITE_ENABLE_COLUMN_USED_MASK) identifies which columns of the -** table or index for cursor P1 are used. P4 is a 64-bit integer -** (P4_INT64) in which the first 63 bits are one for each of the -** first 63 columns of the table or index that are actually used -** by the cursor. The high-order bit is set if any column after -** the 64th is used. +** If successful, SQLITE_OK is returned. If an error occurs, an SQLite error +** code is returned and pLeaf is freed. */ -case OP_ColumnsUsed: { - VdbeCursor *pC; - pC = p->apCsr[pOp->p1]; - assert( pC->eCurType==CURTYPE_BTREE ); - pC->maskUsed = *(u64*)pOp->p4.pI64; - break; +static int vdbeSorterAddToTree( + SortSubtask *pTask, /* Task context */ + int nDepth, /* Depth of tree according to TreeDepth() */ + int iSeq, /* Sequence number of leaf within tree */ + MergeEngine *pRoot, /* Root of tree */ + MergeEngine *pLeaf /* Leaf to add to tree */ +){ + int rc = SQLITE_OK; + int nDiv = 1; + int i; + MergeEngine *p = pRoot; + IncrMerger *pIncr; + + rc = vdbeIncrMergerNew(pTask, pLeaf, &pIncr); + + for(i=1; iaReadr[iIter]; + + if( pReadr->pIncr==0 ){ + MergeEngine *pNew = vdbeMergeEngineNew(SORTER_MAX_MERGE_COUNT); + if( pNew==0 ){ + rc = SQLITE_NOMEM_BKPT; + }else{ + rc = vdbeIncrMergerNew(pTask, pNew, &pReadr->pIncr); + } + } + if( rc==SQLITE_OK ){ + p = pReadr->pIncr->pMerger; + nDiv = nDiv / SORTER_MAX_MERGE_COUNT; + } + } + + if( rc==SQLITE_OK ){ + p->aReadr[iSeq % SORTER_MAX_MERGE_COUNT].pIncr = pIncr; + }else{ + vdbeIncrFree(pIncr); + } + return rc; } -#endif -/* Opcode: SeekGE P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] -** -** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), -** use the value in register P3 as the key. If cursor P1 refers -** to an SQL index, then P3 is the first in an array of P4 registers -** that are used as an unpacked index key. -** -** Reposition cursor P1 so that it points to the smallest entry that -** is greater than or equal to the key value. If there are no records -** greater than or equal to the key and P2 is not zero, then jump to P2. -** -** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this -** opcode will either land on a record that exactly matches the key, or -** else it will cause a jump to P2. When the cursor is OPFLAG_SEEKEQ, -** this opcode must be followed by an IdxLE opcode with the same arguments. -** The IdxGT opcode will be skipped if this opcode succeeds, but the -** IdxGT opcode will be used on subsequent loop iterations. The -** OPFLAG_SEEKEQ flags is a hint to the btree layer to say that this -** is an equality search. -** -** This opcode leaves the cursor configured to move in forward order, -** from the beginning toward the end. In other words, the cursor is -** configured to use Next, not Prev. -** -** See also: Found, NotFound, SeekLt, SeekGt, SeekLe -*/ -/* Opcode: SeekGT P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] -** -** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), -** use the value in register P3 as a key. If cursor P1 refers -** to an SQL index, then P3 is the first in an array of P4 registers -** that are used as an unpacked index key. -** -** Reposition cursor P1 so that it points to the smallest entry that -** is greater than the key value. If there are no records greater than -** the key and P2 is not zero, then jump to P2. -** -** This opcode leaves the cursor configured to move in forward order, -** from the beginning toward the end. In other words, the cursor is -** configured to use Next, not Prev. -** -** See also: Found, NotFound, SeekLt, SeekGe, SeekLe -*/ -/* Opcode: SeekLT P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] -** -** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), -** use the value in register P3 as a key. If cursor P1 refers -** to an SQL index, then P3 is the first in an array of P4 registers -** that are used as an unpacked index key. -** -** Reposition cursor P1 so that it points to the largest entry that -** is less than the key value. If there are no records less than -** the key and P2 is not zero, then jump to P2. -** -** This opcode leaves the cursor configured to move in reverse order, -** from the end toward the beginning. In other words, the cursor is -** configured to use Prev, not Next. -** -** See also: Found, NotFound, SeekGt, SeekGe, SeekLe -*/ -/* Opcode: SeekLE P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] -** -** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), -** use the value in register P3 as a key. If cursor P1 refers -** to an SQL index, then P3 is the first in an array of P4 registers -** that are used as an unpacked index key. -** -** Reposition cursor P1 so that it points to the largest entry that -** is less than or equal to the key value. If there are no records -** less than or equal to the key and P2 is not zero, then jump to P2. -** -** This opcode leaves the cursor configured to move in reverse order, -** from the end toward the beginning. In other words, the cursor is -** configured to use Prev, not Next. -** -** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this -** opcode will either land on a record that exactly matches the key, or -** else it will cause a jump to P2. When the cursor is OPFLAG_SEEKEQ, -** this opcode must be followed by an IdxLE opcode with the same arguments. -** The IdxGE opcode will be skipped if this opcode succeeds, but the -** IdxGE opcode will be used on subsequent loop iterations. The -** OPFLAG_SEEKEQ flags is a hint to the btree layer to say that this -** is an equality search. +/* +** This function is called as part of a SorterRewind() operation on a sorter +** that has already written two or more level-0 PMAs to one or more temp +** files. It builds a tree of MergeEngine/IncrMerger/PmaReader objects that +** can be used to incrementally merge all PMAs on disk. ** -** See also: Found, NotFound, SeekGt, SeekGe, SeekLt +** If successful, SQLITE_OK is returned and *ppOut set to point to the +** MergeEngine object at the root of the tree before returning. Or, if an +** error occurs, an SQLite error code is returned and the final value +** of *ppOut is undefined. */ -case OP_SeekLT: /* jump, in3, group */ -case OP_SeekLE: /* jump, in3, group */ -case OP_SeekGE: /* jump, in3, group */ -case OP_SeekGT: { /* jump, in3, group */ - int res; /* Comparison result */ - int oc; /* Opcode */ - VdbeCursor *pC; /* The cursor to seek */ - UnpackedRecord r; /* The key to seek for */ - int nField; /* Number of columns or fields in the key */ - i64 iKey; /* The rowid we are to seek to */ - int eqOnly; /* Only interested in == results */ +static int vdbeSorterMergeTreeBuild( + VdbeSorter *pSorter, /* The VDBE cursor that implements the sort */ + MergeEngine **ppOut /* Write the MergeEngine here */ +){ + MergeEngine *pMain = 0; + int rc = SQLITE_OK; + int iTask; - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p2!=0 ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->eCurType==CURTYPE_BTREE ); - assert( OP_SeekLE == OP_SeekLT+1 ); - assert( OP_SeekGE == OP_SeekLT+2 ); - assert( OP_SeekGT == OP_SeekLT+3 ); - assert( pC->isOrdered ); - assert( pC->uc.pCursor!=0 ); - oc = pOp->opcode; - eqOnly = 0; - pC->nullRow = 0; -#ifdef SQLITE_DEBUG - pC->seekOp = pOp->opcode; +#if SQLITE_MAX_WORKER_THREADS>0 + /* If the sorter uses more than one task, then create the top-level + ** MergeEngine here. This MergeEngine will read data from exactly + ** one PmaReader per sub-task. */ + assert( pSorter->bUseThreads || pSorter->nTask==1 ); + if( pSorter->nTask>1 ){ + pMain = vdbeMergeEngineNew(pSorter->nTask); + if( pMain==0 ) rc = SQLITE_NOMEM_BKPT; + } #endif - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; - if( pC->isTable ){ - u16 flags3, newType; - /* The OPFLAG_SEEKEQ/BTREE_SEEK_EQ flag is only set on index cursors */ - assert( sqlcipher_sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ)==0 - || CORRUPT_DB ); + for(iTask=0; rc==SQLITE_OK && iTasknTask; iTask++){ + SortSubtask *pTask = &pSorter->aTask[iTask]; + assert( pTask->nPMA>0 || SQLITE_MAX_WORKER_THREADS>0 ); + if( SQLITE_MAX_WORKER_THREADS==0 || pTask->nPMA ){ + MergeEngine *pRoot = 0; /* Root node of tree for this task */ + int nDepth = vdbeSorterTreeDepth(pTask->nPMA); + i64 iReadOff = 0; - /* The input value in P3 might be of any type: integer, real, string, - ** blob, or NULL. But it needs to be an integer before we can do - ** the seek, so convert it. */ - pIn3 = &aMem[pOp->p3]; - flags3 = pIn3->flags; - if( (flags3 & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Str))==MEM_Str ){ - applyNumericAffinity(pIn3, 0); - } - iKey = sqlcipher_sqlite3VdbeIntValue(pIn3); /* Get the integer key value */ - newType = pIn3->flags; /* Record the type after applying numeric affinity */ - pIn3->flags = flags3; /* But convert the type back to its original */ + if( pTask->nPMA<=SORTER_MAX_MERGE_COUNT ){ + rc = vdbeMergeEngineLevel0(pTask, pTask->nPMA, &iReadOff, &pRoot); + }else{ + int i; + int iSeq = 0; + pRoot = vdbeMergeEngineNew(SORTER_MAX_MERGE_COUNT); + if( pRoot==0 ) rc = SQLITE_NOMEM_BKPT; + for(i=0; inPMA && rc==SQLITE_OK; i += SORTER_MAX_MERGE_COUNT){ + MergeEngine *pMerger = 0; /* New level-0 PMA merger */ + int nReader; /* Number of level-0 PMAs to merge */ - /* If the P3 value could not be converted into an integer without - ** loss of information, then special processing is required... */ - if( (newType & (MEM_Int|MEM_IntReal))==0 ){ - if( (newType & MEM_Real)==0 ){ - if( (newType & MEM_Null) || oc>=OP_SeekGE ){ - VdbeBranchTaken(1,2); - goto jump_to_p2; - }else{ - rc = sqlcipher_sqlite3BtreeLast(pC->uc.pCursor, &res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - goto seek_not_found; + nReader = MIN(pTask->nPMA - i, SORTER_MAX_MERGE_COUNT); + rc = vdbeMergeEngineLevel0(pTask, nReader, &iReadOff, &pMerger); + if( rc==SQLITE_OK ){ + rc = vdbeSorterAddToTree(pTask, nDepth, iSeq++, pRoot, pMerger); + } } - }else - - /* If the approximation iKey is larger than the actual real search - ** term, substitute >= for > and < for <=. e.g. if the search term - ** is 4.9 and the integer approximation 5: - ** - ** (x > 4.9) -> (x >= 5) - ** (x <= 4.9) -> (x < 5) - */ - if( pIn3->u.r<(double)iKey ){ - assert( OP_SeekGE==(OP_SeekGT-1) ); - assert( OP_SeekLT==(OP_SeekLE-1) ); - assert( (OP_SeekLE & 0x0001)==(OP_SeekGT & 0x0001) ); - if( (oc & 0x0001)==(OP_SeekGT & 0x0001) ) oc--; } - /* If the approximation iKey is smaller than the actual real search - ** term, substitute <= for < and > for >=. */ - else if( pIn3->u.r>(double)iKey ){ - assert( OP_SeekLE==(OP_SeekLT+1) ); - assert( OP_SeekGT==(OP_SeekGE+1) ); - assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) ); - if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++; + if( rc==SQLITE_OK ){ +#if SQLITE_MAX_WORKER_THREADS>0 + if( pMain!=0 ){ + rc = vdbeIncrMergerNew(pTask, pRoot, &pMain->aReadr[iTask].pIncr); + }else +#endif + { + assert( pMain==0 ); + pMain = pRoot; + } + }else{ + vdbeMergeEngineFree(pRoot); } } - rc = sqlcipher_sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)iKey, 0, &res); - pC->movetoTarget = iKey; /* Used by OP_Delete */ - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - }else{ - /* For a cursor with the OPFLAG_SEEKEQ/BTREE_SEEK_EQ hint, only the - ** OP_SeekGE and OP_SeekLE opcodes are allowed, and these must be - ** immediately followed by an OP_IdxGT or OP_IdxLT opcode, respectively, - ** with the same key. - */ - if( sqlcipher_sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ) ){ - eqOnly = 1; - assert( pOp->opcode==OP_SeekGE || pOp->opcode==OP_SeekLE ); - assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT ); - assert( pOp->opcode==OP_SeekGE || pOp[1].opcode==OP_IdxLT ); - assert( pOp->opcode==OP_SeekLE || pOp[1].opcode==OP_IdxGT ); - assert( pOp[1].p1==pOp[0].p1 ); - assert( pOp[1].p2==pOp[0].p2 ); - assert( pOp[1].p3==pOp[0].p3 ); - assert( pOp[1].p4.i==pOp[0].p4.i ); - } + } - nField = pOp->p4.i; - assert( pOp->p4type==P4_INT32 ); - assert( nField>0 ); - r.pKeyInfo = pC->pKeyInfo; - r.nField = (u16)nField; + if( rc!=SQLITE_OK ){ + vdbeMergeEngineFree(pMain); + pMain = 0; + } + *ppOut = pMain; + return rc; +} - /* The next line of code computes as follows, only faster: - ** if( oc==OP_SeekGT || oc==OP_SeekLE ){ - ** r.default_rc = -1; - ** }else{ - ** r.default_rc = +1; - ** } - */ - r.default_rc = ((1 & (oc - OP_SeekLT)) ? -1 : +1); - assert( oc!=OP_SeekGT || r.default_rc==-1 ); - assert( oc!=OP_SeekLE || r.default_rc==-1 ); - assert( oc!=OP_SeekGE || r.default_rc==+1 ); - assert( oc!=OP_SeekLT || r.default_rc==+1 ); +/* +** This function is called as part of an sqlcipher_sqlite3VdbeSorterRewind() operation +** on a sorter that has written two or more PMAs to temporary files. It sets +** up either VdbeSorter.pMerger (for single threaded sorters) or pReader +** (for multi-threaded sorters) so that it can be used to iterate through +** all records stored in the sorter. +** +** SQLITE_OK is returned if successful, or an SQLite error code otherwise. +*/ +static int vdbeSorterSetupMerge(VdbeSorter *pSorter){ + int rc; /* Return code */ + SortSubtask *pTask0 = &pSorter->aTask[0]; + MergeEngine *pMain = 0; +#if SQLITE_MAX_WORKER_THREADS + sqlcipher_sqlite3 *db = pTask0->pSorter->db; + int i; + SorterCompare xCompare = vdbeSorterGetCompare(pSorter); + for(i=0; inTask; i++){ + pSorter->aTask[i].xCompare = xCompare; + } +#endif - r.aMem = &aMem[pOp->p3]; -#ifdef SQLITE_DEBUG - { int i; for(i=0; ibUseThreads==0 || pSorter->nTask>1 ); + if( pSorter->bUseThreads ){ + int iTask; + PmaReader *pReadr = 0; + SortSubtask *pLast = &pSorter->aTask[pSorter->nTask-1]; + rc = vdbeSortAllocUnpacked(pLast); + if( rc==SQLITE_OK ){ + pReadr = (PmaReader*)sqlcipher_sqlite3DbMallocZero(db, sizeof(PmaReader)); + pSorter->pReader = pReadr; + if( pReadr==0 ) rc = SQLITE_NOMEM_BKPT; + } + if( rc==SQLITE_OK ){ + rc = vdbeIncrMergerNew(pLast, pMain, &pReadr->pIncr); + if( rc==SQLITE_OK ){ + vdbeIncrMergerSetThreads(pReadr->pIncr); + for(iTask=0; iTask<(pSorter->nTask-1); iTask++){ + IncrMerger *pIncr; + if( (pIncr = pMain->aReadr[iTask].pIncr) ){ + vdbeIncrMergerSetThreads(pIncr); + assert( pIncr->pTask!=pLast ); + } + } + for(iTask=0; rc==SQLITE_OK && iTasknTask; iTask++){ + /* Check that: + ** + ** a) The incremental merge object is configured to use the + ** right task, and + ** b) If it is using task (nTask-1), it is configured to run + ** in single-threaded mode. This is important, as the + ** root merge (INCRINIT_ROOT) will be using the same task + ** object. + */ + PmaReader *p = &pMain->aReadr[iTask]; + assert( p->pIncr==0 || ( + (p->pIncr->pTask==&pSorter->aTask[iTask]) /* a */ + && (iTask!=pSorter->nTask-1 || p->pIncr->bUseThread==0) /* b */ + )); + rc = vdbePmaReaderIncrInit(p, INCRINIT_TASK); + } + } + pMain = 0; + } + if( rc==SQLITE_OK ){ + rc = vdbePmaReaderIncrMergeInit(pReadr, INCRINIT_ROOT); + } + }else #endif - r.eqSeen = 0; - rc = sqlcipher_sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, &r, 0, 0, &res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; + { + rc = vdbeMergeEngineInit(pTask0, pMain, INCRINIT_NORMAL); + pSorter->pMerger = pMain; + pMain = 0; } - if( eqOnly && r.eqSeen==0 ){ - assert( res!=0 ); - goto seek_not_found; + } + + if( rc!=SQLITE_OK ){ + vdbeMergeEngineFree(pMain); + } + return rc; +} + + +/* +** Once the sorter has been populated by calls to sqlcipher_sqlite3VdbeSorterWrite, +** this function is called to prepare for iterating through the records +** in sorted order. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterRewind(const VdbeCursor *pCsr, int *pbEof){ + VdbeSorter *pSorter; + int rc = SQLITE_OK; /* Return code */ + + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; + assert( pSorter ); + + /* If no data has been written to disk, then do not do so now. Instead, + ** sort the VdbeSorter.pRecord list. The vdbe layer will read data directly + ** from the in-memory list. */ + if( pSorter->bUsePMA==0 ){ + if( pSorter->list.pList ){ + *pbEof = 0; + rc = vdbeSorterSort(&pSorter->aTask[0], &pSorter->list); + }else{ + *pbEof = 1; } + return rc; } -#ifdef SQLITE_TEST - sqlcipher_sqlite3_search_count++; + + /* Write the current in-memory list to a PMA. When the VdbeSorterWrite() + ** function flushes the contents of memory to disk, it immediately always + ** creates a new list consisting of a single key immediately afterwards. + ** So the list is never empty at this point. */ + assert( pSorter->list.pList ); + rc = vdbeSorterFlushPMA(pSorter); + + /* Join all threads */ + rc = vdbeSorterJoinAll(pSorter, rc); + + vdbeSorterRewindDebug("rewind"); + + /* Assuming no errors have occurred, set up a merger structure to + ** incrementally read and merge all remaining PMAs. */ + assert( pSorter->pReader==0 ); + if( rc==SQLITE_OK ){ + rc = vdbeSorterSetupMerge(pSorter); + *pbEof = 0; + } + + vdbeSorterRewindDebug("rewinddone"); + return rc; +} + +/* +** Advance to the next element in the sorter. Return value: +** +** SQLITE_OK success +** SQLITE_DONE end of data +** otherwise some kind of error. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterNext(sqlcipher_sqlite3 *db, const VdbeCursor *pCsr){ + VdbeSorter *pSorter; + int rc; /* Return code */ + + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; + assert( pSorter->bUsePMA || (pSorter->pReader==0 && pSorter->pMerger==0) ); + if( pSorter->bUsePMA ){ + assert( pSorter->pReader==0 || pSorter->pMerger==0 ); + assert( pSorter->bUseThreads==0 || pSorter->pReader ); + assert( pSorter->bUseThreads==1 || pSorter->pMerger ); +#if SQLITE_MAX_WORKER_THREADS>0 + if( pSorter->bUseThreads ){ + rc = vdbePmaReaderNext(pSorter->pReader); + if( rc==SQLITE_OK && pSorter->pReader->pFd==0 ) rc = SQLITE_DONE; + }else #endif - if( oc>=OP_SeekGE ){ assert( oc==OP_SeekGE || oc==OP_SeekGT ); - if( res<0 || (res==0 && oc==OP_SeekGT) ){ - res = 0; - rc = sqlcipher_sqlite3BtreeNext(pC->uc.pCursor, 0); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - res = 1; - }else{ - goto abort_due_to_error; - } - } - }else{ - res = 0; + /*if( !pSorter->bUseThreads )*/ { + int res = 0; + assert( pSorter->pMerger!=0 ); + assert( pSorter->pMerger->pTask==(&pSorter->aTask[0]) ); + rc = vdbeMergeEngineStep(pSorter->pMerger, &res); + if( rc==SQLITE_OK && res ) rc = SQLITE_DONE; } }else{ - assert( oc==OP_SeekLT || oc==OP_SeekLE ); - if( res>0 || (res==0 && oc==OP_SeekLT) ){ - res = 0; - rc = sqlcipher_sqlite3BtreePrevious(pC->uc.pCursor, 0); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - res = 1; - }else{ - goto abort_due_to_error; - } - } - }else{ - /* res might be negative because the table is empty. Check to - ** see if this is the case. - */ - res = sqlcipher_sqlite3BtreeEof(pC->uc.pCursor); + SorterRecord *pFree = pSorter->list.pList; + pSorter->list.pList = pFree->u.pNext; + pFree->u.pNext = 0; + if( pSorter->list.aMemory==0 ) vdbeSorterRecordFree(db, pFree); + rc = pSorter->list.pList ? SQLITE_OK : SQLITE_DONE; + } + return rc; +} + +/* +** Return a pointer to a buffer owned by the sorter that contains the +** current key. +*/ +static void *vdbeSorterRowkey( + const VdbeSorter *pSorter, /* Sorter object */ + int *pnKey /* OUT: Size of current key in bytes */ +){ + void *pKey; + if( pSorter->bUsePMA ){ + PmaReader *pReader; +#if SQLITE_MAX_WORKER_THREADS>0 + if( pSorter->bUseThreads ){ + pReader = pSorter->pReader; + }else +#endif + /*if( !pSorter->bUseThreads )*/{ + pReader = &pSorter->pMerger->aReadr[pSorter->pMerger->aTree[1]]; } + *pnKey = pReader->nKey; + pKey = pReader->aKey; + }else{ + *pnKey = pSorter->list.pList->nVal; + pKey = SRVAL(pSorter->list.pList); } -seek_not_found: - assert( pOp->p2>0 ); - VdbeBranchTaken(res!=0,2); - if( res ){ - goto jump_to_p2; - }else if( eqOnly ){ - assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT ); - pOp++; /* Skip the OP_IdxLt or OP_IdxGT that follows */ + return pKey; +} + +/* +** Copy the current sorter key into the memory cell pOut. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterRowkey(const VdbeCursor *pCsr, Mem *pOut){ + VdbeSorter *pSorter; + void *pKey; int nKey; /* Sorter key to copy into pOut */ + + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; + pKey = vdbeSorterRowkey(pSorter, &nKey); + if( sqlcipher_sqlite3VdbeMemClearAndResize(pOut, nKey) ){ + return SQLITE_NOMEM_BKPT; } - break; -} + pOut->n = nKey; + MemSetTypeFlag(pOut, MEM_Blob); + memcpy(pOut->z, pKey, nKey); + return SQLITE_OK; +} -/* Opcode: SeekScan P1 P2 * * * -** Synopsis: Scan-ahead up to P1 rows -** -** This opcode is a prefix opcode to OP_SeekGE. In other words, this -** opcode must be immediately followed by OP_SeekGE. This constraint is -** checked by assert() statements. -** -** This opcode uses the P1 through P4 operands of the subsequent -** OP_SeekGE. In the text that follows, the operands of the subsequent -** OP_SeekGE opcode are denoted as SeekOP.P1 through SeekOP.P4. Only -** the P1 and P2 operands of this opcode are also used, and are called -** This.P1 and This.P2. -** -** This opcode helps to optimize IN operators on a multi-column index -** where the IN operator is on the later terms of the index by avoiding -** unnecessary seeks on the btree, substituting steps to the next row -** of the b-tree instead. A correct answer is obtained if this opcode -** is omitted or is a no-op. +/* +** Compare the key in memory cell pVal with the key that the sorter cursor +** passed as the first argument currently points to. For the purposes of +** the comparison, ignore the rowid field at the end of each record. ** -** The SeekGE.P3 and SeekGE.P4 operands identify an unpacked key which -** is the desired entry that we want the cursor SeekGE.P1 to be pointing -** to. Call this SeekGE.P4/P5 row the "target". +** If the sorter cursor key contains any NULL values, consider it to be +** less than pVal. Even if pVal also contains NULL values. ** -** If the SeekGE.P1 cursor is not currently pointing to a valid row, -** then this opcode is a no-op and control passes through into the OP_SeekGE. +** If an error occurs, return an SQLite error code (i.e. SQLITE_NOMEM). +** Otherwise, set *pRes to a negative, zero or positive value if the +** key in pVal is smaller than, equal to or larger than the current sorter +** key. ** -** If the SeekGE.P1 cursor is pointing to a valid row, then that row -** might be the target row, or it might be near and slightly before the -** target row. This opcode attempts to position the cursor on the target -** row by, perhaps by invoking sqlcipher_sqlite3BtreeStep() on the cursor -** between 0 and This.P1 times. +** This routine forms the core of the OP_SorterCompare opcode, which in +** turn is used to verify uniqueness when constructing a UNIQUE INDEX. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterCompare( + const VdbeCursor *pCsr, /* Sorter cursor */ + Mem *pVal, /* Value to compare to current sorter key */ + int nKeyCol, /* Compare this many columns */ + int *pRes /* OUT: Result of comparison */ +){ + VdbeSorter *pSorter; + UnpackedRecord *r2; + KeyInfo *pKeyInfo; + int i; + void *pKey; int nKey; /* Sorter key to compare pVal with */ + + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; + r2 = pSorter->pUnpacked; + pKeyInfo = pCsr->pKeyInfo; + if( r2==0 ){ + r2 = pSorter->pUnpacked = sqlcipher_sqlite3VdbeAllocUnpackedRecord(pKeyInfo); + if( r2==0 ) return SQLITE_NOMEM_BKPT; + r2->nField = nKeyCol; + } + assert( r2->nField==nKeyCol ); + + pKey = vdbeSorterRowkey(pSorter, &nKey); + sqlcipher_sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, r2); + for(i=0; iaMem[i].flags & MEM_Null ){ + *pRes = -1; + return SQLITE_OK; + } + } + + *pRes = sqlcipher_sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2); + return SQLITE_OK; +} + +/************** End of vdbesort.c ********************************************/ +/************** Begin file vdbevtab.c ****************************************/ +/* +** 2020-03-23 ** -** There are three possible outcomes from this opcode:
      +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -**
    1. If after This.P1 steps, the cursor is still point to a place that -** is earlier in the btree than the target row, -** then fall through into the subsquence OP_SeekGE opcode. +** 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. ** -**
    2. If the cursor is successfully moved to the target row by 0 or more -** sqlcipher_sqlite3BtreeNext() calls, then jump to This.P2, which will land just -** past the OP_IdxGT opcode that follows the OP_SeekGE. +************************************************************************* ** -**
    3. If the cursor ends up past the target row (indicating the the target -** row does not exist in the btree) then jump to SeekOP.P2. -**
    +** This file implements virtual-tables for examining the bytecode content +** of a prepared statement. */ -case OP_SeekScan: { - VdbeCursor *pC; - int res; - int nStep; - UnpackedRecord r; +/* #include "sqliteInt.h" */ +#if defined(SQLITE_ENABLE_BYTECODE_VTAB) && !defined(SQLITE_OMIT_VIRTUALTABLE) +/* #include "vdbeInt.h" */ - assert( pOp[1].opcode==OP_SeekGE ); +/* An instance of the bytecode() table-valued function. +*/ +typedef struct bytecodevtab bytecodevtab; +struct bytecodevtab { + sqlcipher_sqlite3_vtab base; /* Base class - must be first */ + sqlcipher_sqlite3 *db; /* Database connection */ + int bTablesUsed; /* 2 for tables_used(). 0 for bytecode(). */ +}; - /* pOp->p2 points to the first instruction past the OP_IdxGT that - ** follows the OP_SeekGE. */ - assert( pOp->p2>=(int)(pOp-aOp)+2 ); - assert( aOp[pOp->p2-1].opcode==OP_IdxGT ); - assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); - assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); - assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); +/* A cursor for scanning through the bytecode +*/ +typedef struct bytecodevtab_cursor bytecodevtab_cursor; +struct bytecodevtab_cursor { + sqlcipher_sqlite3_vtab_cursor base; /* Base class - must be first */ + sqlcipher_sqlite3_stmt *pStmt; /* The statement whose bytecode is displayed */ + int iRowid; /* The rowid of the output table */ + int iAddr; /* Address */ + int needFinalize; /* Cursors owns pStmt and must finalize it */ + int showSubprograms; /* Provide a listing of subprograms */ + Op *aOp; /* Operand array */ + char *zP4; /* Rendered P4 value */ + const char *zType; /* tables_used.type */ + const char *zSchema; /* tables_used.schema */ + const char *zName; /* tables_used.name */ + Mem sub; /* Subprograms */ +}; - assert( pOp->p1>0 ); - pC = p->apCsr[pOp[1].p1]; - assert( pC!=0 ); - assert( pC->eCurType==CURTYPE_BTREE ); - assert( !pC->isTable ); - if( !sqlcipher_sqlite3BtreeCursorIsValidNN(pC->uc.pCursor) ){ -#ifdef SQLITE_DEBUG - if( db->flags&SQLITE_VdbeTrace ){ - printf("... cursor not valid - fall through\n"); - } -#endif - break; +/* +** Create a new bytecode() table-valued function. +*/ +static int bytecodevtabConnect( + sqlcipher_sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlcipher_sqlite3_vtab **ppVtab, + char **pzErr +){ + bytecodevtab *pNew; + int rc; + int isTabUsed = pAux!=0; + const char *azSchema[2] = { + /* bytecode() schema */ + "CREATE TABLE x(" + "addr INT," + "opcode TEXT," + "p1 INT," + "p2 INT," + "p3 INT," + "p4 TEXT," + "p5 INT," + "comment TEXT," + "subprog TEXT," + "stmt HIDDEN" + ");", + + /* Tables_used() schema */ + "CREATE TABLE x(" + "type TEXT," + "schema TEXT," + "name TEXT," + "wr INT," + "subprog TEXT," + "stmt HIDDEN" + ");" + }; + + rc = sqlcipher_sqlite3_declare_vtab(db, azSchema[isTabUsed]); + if( rc==SQLITE_OK ){ + pNew = sqlcipher_sqlite3_malloc( sizeof(*pNew) ); + *ppVtab = (sqlcipher_sqlite3_vtab*)pNew; + if( pNew==0 ) return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + pNew->db = db; + pNew->bTablesUsed = isTabUsed*2; } - nStep = pOp->p1; - assert( nStep>=1 ); - r.pKeyInfo = pC->pKeyInfo; - r.nField = (u16)pOp[1].p4.i; - r.default_rc = 0; - r.aMem = &aMem[pOp[1].p3]; -#ifdef SQLITE_DEBUG - { - int i; - for(i=0; isub, pVTab->db, 1); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +/* +** Clear all internal content from a bytecodevtab cursor. +*/ +static void bytecodevtabCursorClear(bytecodevtab_cursor *pCur){ + sqlcipher_sqlite3_free(pCur->zP4); + pCur->zP4 = 0; + sqlcipher_sqlite3VdbeMemRelease(&pCur->sub); + sqlcipher_sqlite3VdbeMemSetNull(&pCur->sub); + if( pCur->needFinalize ){ + sqlcipher_sqlite3_finalize(pCur->pStmt); } -#endif - res = 0; /* Not needed. Only used to silence a warning. */ - while(1){ - rc = sqlcipher_sqlite3VdbeIdxKeyCompare(db, pC, &r, &res); - if( rc ) goto abort_due_to_error; - if( res>0 ){ - seekscan_search_fail: -#ifdef SQLITE_DEBUG - if( db->flags&SQLITE_VdbeTrace ){ - printf("... %d steps and then skip\n", pOp->p1 - nStep); + pCur->pStmt = 0; + pCur->needFinalize = 0; + pCur->zType = 0; + pCur->zSchema = 0; + pCur->zName = 0; +} + +/* +** Destructor for a bytecodevtab_cursor. +*/ +static int bytecodevtabClose(sqlcipher_sqlite3_vtab_cursor *cur){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; + bytecodevtabCursorClear(pCur); + sqlcipher_sqlite3_free(pCur); + return SQLITE_OK; +} + + +/* +** Advance a bytecodevtab_cursor to its next row of output. +*/ +static int bytecodevtabNext(sqlcipher_sqlite3_vtab_cursor *cur){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; + bytecodevtab *pTab = (bytecodevtab*)cur->pVtab; + int rc; + if( pCur->zP4 ){ + sqlcipher_sqlite3_free(pCur->zP4); + pCur->zP4 = 0; + } + if( pCur->zName ){ + pCur->zName = 0; + pCur->zType = 0; + pCur->zSchema = 0; + } + rc = sqlcipher_sqlite3VdbeNextOpcode( + (Vdbe*)pCur->pStmt, + pCur->showSubprograms ? &pCur->sub : 0, + pTab->bTablesUsed, + &pCur->iRowid, + &pCur->iAddr, + &pCur->aOp); + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3VdbeMemSetNull(&pCur->sub); + pCur->aOp = 0; + } + return SQLITE_OK; +} + +/* +** Return TRUE if the cursor has been moved off of the last +** row of output. +*/ +static int bytecodevtabEof(sqlcipher_sqlite3_vtab_cursor *cur){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; + return pCur->aOp==0; +} + +/* +** Return values of columns for the row at which the bytecodevtab_cursor +** is currently pointing. +*/ +static int bytecodevtabColumn( + sqlcipher_sqlite3_vtab_cursor *cur, /* The cursor */ + sqlcipher_sqlite3_context *ctx, /* First argument to sqlcipher_sqlite3_result_...() */ + int i /* Which column to return */ +){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; + bytecodevtab *pVTab = (bytecodevtab*)cur->pVtab; + Op *pOp = pCur->aOp + pCur->iAddr; + if( pVTab->bTablesUsed ){ + if( i==4 ){ + i = 8; + }else{ + if( i<=2 && pCur->zType==0 ){ + Schema *pSchema; + HashElem *k; + int iDb = pOp->p3; + Pgno iRoot = (Pgno)pOp->p2; + sqlcipher_sqlite3 *db = pVTab->db; + pSchema = db->aDb[iDb].pSchema; + pCur->zSchema = db->aDb[iDb].zDbSName; + for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ + Table *pTab = (Table*)sqliteHashData(k); + if( !IsVirtual(pTab) && pTab->tnum==iRoot ){ + pCur->zName = pTab->zName; + pCur->zType = "table"; + break; + } + } + if( pCur->zName==0 ){ + for(k=sqliteHashFirst(&pSchema->idxHash); k; k=sqliteHashNext(k)){ + Index *pIdx = (Index*)sqliteHashData(k); + if( pIdx->tnum==iRoot ){ + pCur->zName = pIdx->zName; + pCur->zType = "index"; + } + } + } } -#endif - VdbeBranchTaken(1,3); - pOp++; - goto jump_to_p2; + i += 10; } - if( res==0 ){ -#ifdef SQLITE_DEBUG - if( db->flags&SQLITE_VdbeTrace ){ - printf("... %d steps and then success\n", pOp->p1 - nStep); - } -#endif - VdbeBranchTaken(2,3); - goto jump_to_p2; + } + switch( i ){ + case 0: /* addr */ + sqlcipher_sqlite3_result_int(ctx, pCur->iAddr); break; - } - if( nStep<=0 ){ -#ifdef SQLITE_DEBUG - if( db->flags&SQLITE_VdbeTrace ){ - printf("... fall through after %d steps\n", pOp->p1); + case 1: /* opcode */ + sqlcipher_sqlite3_result_text(ctx, (char*)sqlcipher_sqlite3OpcodeName(pOp->opcode), + -1, SQLITE_STATIC); + break; + case 2: /* p1 */ + sqlcipher_sqlite3_result_int(ctx, pOp->p1); + break; + case 3: /* p2 */ + sqlcipher_sqlite3_result_int(ctx, pOp->p2); + break; + case 4: /* p3 */ + sqlcipher_sqlite3_result_int(ctx, pOp->p3); + break; + case 5: /* p4 */ + case 7: /* comment */ + if( pCur->zP4==0 ){ + pCur->zP4 = sqlcipher_sqlite3VdbeDisplayP4(pVTab->db, pOp); } + if( i==5 ){ + sqlcipher_sqlite3_result_text(ctx, pCur->zP4, -1, SQLITE_STATIC); + }else{ +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + char *zCom = sqlcipher_sqlite3VdbeDisplayComment(pVTab->db, pOp, pCur->zP4); + sqlcipher_sqlite3_result_text(ctx, zCom, -1, sqlcipher_sqlite3_free); #endif - VdbeBranchTaken(0,3); + } break; - } - nStep--; - rc = sqlcipher_sqlite3BtreeNext(pC->uc.pCursor, 0); - if( rc ){ - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - goto seekscan_search_fail; + case 6: /* p5 */ + sqlcipher_sqlite3_result_int(ctx, pOp->p5); + break; + case 8: { /* subprog */ + Op *aOp = pCur->aOp; + assert( aOp[0].opcode==OP_Init ); + assert( aOp[0].p4.z==0 || strncmp(aOp[0].p4.z,"-" "- ",3)==0 ); + if( pCur->iRowid==pCur->iAddr+1 ){ + break; /* Result is NULL for the main program */ + }else if( aOp[0].p4.z!=0 ){ + sqlcipher_sqlite3_result_text(ctx, aOp[0].p4.z+3, -1, SQLITE_STATIC); }else{ - goto abort_due_to_error; + sqlcipher_sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC); } + break; } + case 10: /* tables_used.type */ + sqlcipher_sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC); + break; + case 11: /* tables_used.schema */ + sqlcipher_sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC); + break; + case 12: /* tables_used.name */ + sqlcipher_sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC); + break; + case 13: /* tables_used.wr */ + sqlcipher_sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite); + break; } - - 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 bytecodevtabRowid(sqlcipher_sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} -/* Opcode: SeekHit P1 P2 P3 * * -** Synopsis: set P2<=seekHit<=P3 -** -** Increase or decrease the seekHit value for cursor P1, if necessary, -** so that it is no less than P2 and no greater than P3. -** -** The seekHit integer represents the maximum of terms in an index for which -** there is known to be at least one match. If the seekHit value is smaller -** than the total number of equality terms in an index lookup, then the -** OP_IfNoHope opcode might run to see if the IN loop can be abandoned -** early, thus saving work. This is part of the IN-early-out optimization. +/* +** Initialize a cursor. ** -** P1 must be a valid b-tree cursor. +** idxNum==0 means show all subprograms +** idxNum==1 means show only the main bytecode and omit subprograms. */ -case OP_SeekHit: { - VdbeCursor *pC; - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pOp->p3>=pOp->p2 ); - if( pC->seekHitp2 ){ - pC->seekHit = pOp->p2; - }else if( pC->seekHit>pOp->p3 ){ - pC->seekHit = pOp->p3; +static int bytecodevtabFilter( + sqlcipher_sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlcipher_sqlite3_value **argv +){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor; + bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab; + int rc = SQLITE_OK; + + bytecodevtabCursorClear(pCur); + pCur->iRowid = 0; + pCur->iAddr = 0; + pCur->showSubprograms = idxNum==0; + assert( argc==1 ); + if( sqlcipher_sqlite3_value_type(argv[0])==SQLITE_TEXT ){ + const char *zSql = (const char*)sqlcipher_sqlite3_value_text(argv[0]); + if( zSql==0 ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlcipher_sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pStmt, 0); + pCur->needFinalize = 1; + } + }else{ + pCur->pStmt = (sqlcipher_sqlite3_stmt*)sqlcipher_sqlite3_value_pointer(argv[0],"stmt-pointer"); } - break; + if( pCur->pStmt==0 ){ + pVTab->base.zErrMsg = sqlcipher_sqlite3_mprintf( + "argument to %s() is not a valid SQL statement", + pVTab->bTablesUsed ? "tables_used" : "bytecode" + ); + rc = SQLITE_ERROR; + }else{ + bytecodevtabNext(pVtabCursor); + } + return rc; } -/* Opcode: IfNotOpen P1 P2 * * * -** Synopsis: if( !csr[P1] ) goto P2 -** -** If cursor P1 is not open, jump to instruction P2. Otherwise, fall through. +/* +** We must have a single stmt=? constraint that will be passed through +** into the xFilter method. If there is no valid stmt=? constraint, +** then return an SQLITE_CONSTRAINT error. */ -case OP_IfNotOpen: { /* jump */ - assert( pOp->p1>=0 && pOp->p1nCursor ); - VdbeBranchTaken(p->apCsr[pOp->p1]==0, 2); - if( !p->apCsr[pOp->p1] ){ - goto jump_to_p2_and_check_for_interrupt; +static int bytecodevtabBestIndex( + sqlcipher_sqlite3_vtab *tab, + sqlcipher_sqlite3_index_info *pIdxInfo +){ + int i; + int rc = SQLITE_CONSTRAINT; + struct sqlcipher_sqlite3_index_constraint *p; + bytecodevtab *pVTab = (bytecodevtab*)tab; + int iBaseCol = pVTab->bTablesUsed ? 4 : 8; + pIdxInfo->estimatedCost = (double)100; + pIdxInfo->estimatedRows = 100; + pIdxInfo->idxNum = 0; + for(i=0, p=pIdxInfo->aConstraint; inConstraint; i++, p++){ + if( p->usable==0 ) continue; + if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==iBaseCol+1 ){ + rc = SQLITE_OK; + pIdxInfo->aConstraintUsage[i].omit = 1; + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + } + if( p->op==SQLITE_INDEX_CONSTRAINT_ISNULL && p->iColumn==iBaseCol ){ + pIdxInfo->aConstraintUsage[i].omit = 1; + pIdxInfo->idxNum = 1; + } } - break; + return rc; } -/* Opcode: Found P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] -** -** If P4==0 then register P3 holds a blob constructed by MakeRecord. If -** P4>0 then register P3 is the first of P4 registers that form an unpacked -** record. -** -** Cursor P1 is on an index btree. If the record identified by P3 and P4 -** is a prefix of any entry in P1 then a jump is made to P2 and -** P1 is left pointing at the matching entry. -** -** This operation leaves the cursor in a state where it can be -** advanced in the forward direction. The Next instruction will work, -** but not the Prev instruction. -** -** See also: NotFound, NoConflict, NotExists. SeekGe -*/ -/* Opcode: NotFound P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] -** -** If P4==0 then register P3 holds a blob constructed by MakeRecord. If -** P4>0 then register P3 is the first of P4 registers that form an unpacked -** record. -** -** Cursor P1 is on an index btree. If the record identified by P3 and P4 -** is not the prefix of any entry in P1 then a jump is made to P2. If P1 -** does contain an entry whose prefix matches the P3/P4 record then control -** falls through to the next instruction and P1 is left pointing at the -** matching entry. -** -** This operation leaves the cursor in a state where it cannot be -** advanced in either direction. In other words, the Next and Prev -** opcodes do not work after this operation. -** -** See also: Found, NotExists, NoConflict, IfNoHope +/* +** This following structure defines all the methods for the +** virtual table. */ -/* Opcode: IfNoHope P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] +static sqlcipher_sqlite3_module bytecodevtabModule = { + /* iVersion */ 0, + /* xCreate */ 0, + /* xConnect */ bytecodevtabConnect, + /* xBestIndex */ bytecodevtabBestIndex, + /* xDisconnect */ bytecodevtabDisconnect, + /* xDestroy */ 0, + /* xOpen */ bytecodevtabOpen, + /* xClose */ bytecodevtabClose, + /* xFilter */ bytecodevtabFilter, + /* xNext */ bytecodevtabNext, + /* xEof */ bytecodevtabEof, + /* xColumn */ bytecodevtabColumn, + /* xRowid */ bytecodevtabRowid, + /* xUpdate */ 0, + /* xBegin */ 0, + /* xSync */ 0, + /* xCommit */ 0, + /* xRollback */ 0, + /* xFindMethod */ 0, + /* xRename */ 0, + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, + /* xShadowName */ 0 +}; + + +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeBytecodeVtabInit(sqlcipher_sqlite3 *db){ + int rc; + rc = sqlcipher_sqlite3_create_module(db, "bytecode", &bytecodevtabModule, 0); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db); + } + return rc; +} +#elif defined(SQLITE_ENABLE_BYTECODE_VTAB) +SQLITE_PRIVATE int sqlcipher_sqlite3VdbeBytecodeVtabInit(sqlcipher_sqlite3 *db){ return SQLITE_OK; } +#endif /* SQLITE_ENABLE_BYTECODE_VTAB */ + +/************** End of vdbevtab.c ********************************************/ +/************** Begin file memjournal.c **************************************/ +/* +** 2008 October 7 ** -** Register P3 is the first of P4 registers that form an unpacked -** record. Cursor P1 is an index btree. P2 is a jump destination. -** In other words, the operands to this opcode are the same as the -** operands to OP_NotFound and OP_IdxGT. +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** This opcode is an optimization attempt only. If this opcode always -** falls through, the correct answer is still obtained, but extra works -** is performed. +** 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. ** -** A value of N in the seekHit flag of cursor P1 means that there exists -** a key P3:N that will match some record in the index. We want to know -** if it is possible for a record P3:P4 to match some record in the -** index. If it is not possible, we can skips some work. So if seekHit -** is less than P4, attempt to find out if a match is possible by running -** OP_NotFound. +************************************************************************* ** -** This opcode is used in IN clause processing for a multi-column key. -** If an IN clause is attached to an element of the key other than the -** left-most element, and if there are no matches on the most recent -** seek over the whole key, then it might be that one of the key element -** to the left is prohibiting a match, and hence there is "no hope" of -** any match regardless of how many IN clause elements are checked. -** In such a case, we abandon the IN clause search early, using this -** opcode. The opcode name comes from the fact that the -** jump is taken if there is "no hope" of achieving a match. +** This file contains code use to implement an in-memory rollback journal. +** The in-memory rollback journal is used to journal transactions for +** ":memory:" databases and when the journal_mode=MEMORY pragma is used. ** -** See also: NotFound, SeekHit +** Update: The in-memory journal is also used to temporarily cache +** smaller journals that are not critical for power-loss recovery. +** For example, statement journals that are not too big will be held +** entirely in memory, thus reducing the number of file I/O calls, and +** more importantly, reducing temporary file creation events. If these +** journals become too large for memory, they are spilled to disk. But +** in the common case, they are usually small and no file I/O needs to +** occur. */ -/* Opcode: NoConflict P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] -** -** If P4==0 then register P3 holds a blob constructed by MakeRecord. If -** P4>0 then register P3 is the first of P4 registers that form an unpacked -** record. -** -** Cursor P1 is on an index btree. If the record identified by P3 and P4 -** contains any NULL value, jump immediately to P2. If all terms of the -** record are not-NULL then a check is done to determine if any row in the -** P1 index btree has a matching key prefix. If there are no matches, jump -** immediately to P2. If there is a match, fall through and leave the P1 -** cursor pointing to the matching row. -** -** This opcode is similar to OP_NotFound with the exceptions that the -** branch is always taken if any part of the search key input is NULL. -** -** This operation leaves the cursor in a state where it cannot be -** advanced in either direction. In other words, the Next and Prev -** opcodes do not work after this operation. +/* #include "sqliteInt.h" */ + +/* Forward references to internal structures */ +typedef struct MemJournal MemJournal; +typedef struct FilePoint FilePoint; +typedef struct FileChunk FileChunk; + +/* +** The rollback journal is composed of a linked list of these structures. ** -** See also: NotFound, Found, NotExists +** The zChunk array is always at least 8 bytes in size - usually much more. +** Its actual size is stored in the MemJournal.nChunkSize variable. */ -case OP_IfNoHope: { /* jump, in3 */ - VdbeCursor *pC; - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - if( pC->seekHit>=pOp->p4.i ) break; - /* Fall through into OP_NotFound */ - /* no break */ deliberate_fall_through -} -case OP_NoConflict: /* jump, in3 */ -case OP_NotFound: /* jump, in3 */ -case OP_Found: { /* jump, in3 */ - int alreadyExists; - int takeJump; - int ii; - VdbeCursor *pC; - int res; - UnpackedRecord *pFree; - UnpackedRecord *pIdxKey; - UnpackedRecord r; +struct FileChunk { + FileChunk *pNext; /* Next chunk in the journal */ + u8 zChunk[8]; /* Content of this chunk */ +}; -#ifdef SQLITE_TEST - if( pOp->opcode!=OP_NoConflict ) sqlcipher_sqlite3_found_count++; -#endif +/* +** By default, allocate this many bytes of memory for each FileChunk object. +*/ +#define MEMJOURNAL_DFLT_FILECHUNKSIZE 1024 - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p4type==P4_INT32 ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); -#ifdef SQLITE_DEBUG - pC->seekOp = pOp->opcode; -#endif - pIn3 = &aMem[pOp->p3]; - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->uc.pCursor!=0 ); - assert( pC->isTable==0 ); - if( pOp->p4.i>0 ){ - r.pKeyInfo = pC->pKeyInfo; - r.nField = (u16)pOp->p4.i; - r.aMem = pIn3; -#ifdef SQLITE_DEBUG - for(ii=0; iip3+ii, &r.aMem[ii]); +/* +** For chunk size nChunkSize, return the number of bytes that should +** be allocated for each FileChunk structure. +*/ +#define fileChunkSize(nChunkSize) (sizeof(FileChunk) + ((nChunkSize)-8)) + +/* +** An instance of this object serves as a cursor into the rollback journal. +** The cursor can be either for reading or writing. +*/ +struct FilePoint { + sqlcipher_sqlite3_int64 iOffset; /* Offset from the beginning of the file */ + FileChunk *pChunk; /* Specific chunk into which cursor points */ +}; + +/* +** This structure is a subclass of sqlcipher_sqlite3_file. Each open memory-journal +** is an instance of this class. +*/ +struct MemJournal { + const sqlcipher_sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */ + int nChunkSize; /* In-memory chunk-size */ + + int nSpill; /* Bytes of data before flushing */ + FileChunk *pFirst; /* Head of in-memory chunk-list */ + FilePoint endpoint; /* Pointer to the end of the file */ + FilePoint readpoint; /* Pointer to the end of the last xRead() */ + + int flags; /* xOpen flags */ + sqlcipher_sqlite3_vfs *pVfs; /* The "real" underlying VFS */ + const char *zJournal; /* Name of the journal file */ +}; + +/* +** Read data from the in-memory journal file. This is the implementation +** of the sqlcipher_sqlite3_vfs.xRead method. +*/ +static int memjrnlRead( + sqlcipher_sqlite3_file *pJfd, /* The journal file from which to read */ + void *zBuf, /* Put the results here */ + int iAmt, /* Number of bytes to read */ + sqlite_int64 iOfst /* Begin reading at this offset */ +){ + MemJournal *p = (MemJournal *)pJfd; + u8 *zOut = zBuf; + int nRead = iAmt; + int iChunkOffset; + FileChunk *pChunk; + + if( (iAmt+iOfst)>p->endpoint.iOffset ){ + return SQLITE_IOERR_SHORT_READ; + } + assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 ); + if( p->readpoint.iOffset!=iOfst || iOfst==0 ){ + sqlcipher_sqlite3_int64 iOff = 0; + for(pChunk=p->pFirst; + ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst; + pChunk=pChunk->pNext + ){ + iOff += p->nChunkSize; } -#endif - pIdxKey = &r; - pFree = 0; }else{ - assert( pIn3->flags & MEM_Blob ); - rc = ExpandBlob(pIn3); - assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); - if( rc ) goto no_mem; - pFree = pIdxKey = sqlcipher_sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo); - if( pIdxKey==0 ) goto no_mem; - sqlcipher_sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); + pChunk = p->readpoint.pChunk; + assert( pChunk!=0 ); } - pIdxKey->default_rc = 0; - takeJump = 0; - if( pOp->opcode==OP_NoConflict ){ - /* For the OP_NoConflict opcode, take the jump if any of the - ** input fields are NULL, since any key with a NULL will not - ** conflict */ - for(ii=0; iinField; ii++){ - if( pIdxKey->aMem[ii].flags & MEM_Null ){ - takeJump = 1; - break; + + iChunkOffset = (int)(iOfst%p->nChunkSize); + do { + int iSpace = p->nChunkSize - iChunkOffset; + int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset)); + memcpy(zOut, (u8*)pChunk->zChunk + iChunkOffset, nCopy); + zOut += nCopy; + nRead -= iSpace; + iChunkOffset = 0; + } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 ); + p->readpoint.iOffset = pChunk ? iOfst+iAmt : 0; + p->readpoint.pChunk = pChunk; + + return SQLITE_OK; +} + +/* +** Free the list of FileChunk structures headed at MemJournal.pFirst. +*/ +static void memjrnlFreeChunks(FileChunk *pFirst){ + FileChunk *pIter; + FileChunk *pNext; + for(pIter=pFirst; pIter; pIter=pNext){ + pNext = pIter->pNext; + sqlcipher_sqlite3_free(pIter); + } +} + +/* +** Flush the contents of memory to a real file on disk. +*/ +static int memjrnlCreateFile(MemJournal *p){ + int rc; + sqlcipher_sqlite3_file *pReal = (sqlcipher_sqlite3_file*)p; + MemJournal copy = *p; + + memset(p, 0, sizeof(MemJournal)); + rc = sqlcipher_sqlite3OsOpen(copy.pVfs, copy.zJournal, pReal, copy.flags, 0); + if( rc==SQLITE_OK ){ + int nChunk = copy.nChunkSize; + i64 iOff = 0; + FileChunk *pIter; + for(pIter=copy.pFirst; pIter; pIter=pIter->pNext){ + if( iOff + nChunk > copy.endpoint.iOffset ){ + nChunk = copy.endpoint.iOffset - iOff; } + rc = sqlcipher_sqlite3OsWrite(pReal, (u8*)pIter->zChunk, nChunk, iOff); + if( rc ) break; + iOff += nChunk; + } + if( rc==SQLITE_OK ){ + /* No error has occurred. Free the in-memory buffers. */ + memjrnlFreeChunks(copy.pFirst); } } - rc = sqlcipher_sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, pIdxKey, 0, 0, &res); - if( pFree ) sqlcipher_sqlite3DbFreeNN(db, pFree); if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - pC->seekResult = res; - alreadyExists = (res==0); - pC->nullRow = 1-alreadyExists; - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; - if( pOp->opcode==OP_Found ){ - VdbeBranchTaken(alreadyExists!=0,2); - if( alreadyExists ) goto jump_to_p2; - }else{ - VdbeBranchTaken(takeJump||alreadyExists==0,2); - if( takeJump || !alreadyExists ) goto jump_to_p2; - if( pOp->opcode==OP_IfNoHope ) pC->seekHit = pOp->p4.i; + /* If an error occurred while creating or writing to the file, restore + ** the original before returning. This way, SQLite uses the in-memory + ** journal data to roll back changes made to the internal page-cache + ** before this function was called. */ + sqlcipher_sqlite3OsClose(pReal); + *p = copy; } - break; + return rc; } -/* Opcode: SeekRowid P1 P2 P3 * * -** Synopsis: intkey=r[P3] -** -** P1 is the index of a cursor open on an SQL table btree (with integer -** keys). If register P3 does not contain an integer or if P1 does not -** contain a record with rowid P3 then jump immediately to P2. -** Or, if P2 is 0, raise an SQLITE_CORRUPT error. If P1 does contain -** a record with rowid P3 then -** leave the cursor pointing at that record and fall through to the next -** instruction. -** -** The OP_NotExists opcode performs the same operation, but with OP_NotExists -** the P3 register must be guaranteed to contain an integer value. With this -** opcode, register P3 might not contain an integer. -** -** The OP_NotFound opcode performs the same operation on index btrees -** (with arbitrary multi-value keys). -** -** This opcode leaves the cursor in a state where it cannot be advanced -** in either direction. In other words, the Next and Prev opcodes will -** not work following this opcode. -** -** See also: Found, NotFound, NoConflict, SeekRowid -*/ -/* Opcode: NotExists P1 P2 P3 * * -** Synopsis: intkey=r[P3] -** -** P1 is the index of a cursor open on an SQL table btree (with integer -** keys). P3 is an integer rowid. If P1 does not contain a record with -** rowid P3 then jump immediately to P2. Or, if P2 is 0, raise an -** SQLITE_CORRUPT error. If P1 does contain a record with rowid P3 then -** leave the cursor pointing at that record and fall through to the next -** instruction. -** -** The OP_SeekRowid opcode performs the same operation but also allows the -** P3 register to contain a non-integer value, in which case the jump is -** always taken. This opcode requires that P3 always contain an integer. -** -** The OP_NotFound opcode performs the same operation on index btrees -** (with arbitrary multi-value keys). -** -** This opcode leaves the cursor in a state where it cannot be advanced -** in either direction. In other words, the Next and Prev opcodes will -** not work following this opcode. -** -** See also: Found, NotFound, NoConflict, SeekRowid + +/* Forward reference */ +static int memjrnlTruncate(sqlcipher_sqlite3_file *pJfd, sqlite_int64 size); + +/* +** Write data to the file. */ -case OP_SeekRowid: { /* jump, in3 */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - u64 iKey; +static int memjrnlWrite( + sqlcipher_sqlite3_file *pJfd, /* The journal file into which to write */ + const void *zBuf, /* Take data to be written from here */ + int iAmt, /* Number of bytes to write */ + sqlite_int64 iOfst /* Begin writing at this offset into the file */ +){ + MemJournal *p = (MemJournal *)pJfd; + int nWrite = iAmt; + u8 *zWrite = (u8 *)zBuf; - pIn3 = &aMem[pOp->p3]; - testcase( pIn3->flags & MEM_Int ); - testcase( pIn3->flags & MEM_IntReal ); - testcase( pIn3->flags & MEM_Real ); - testcase( (pIn3->flags & (MEM_Str|MEM_Int))==MEM_Str ); - if( (pIn3->flags & (MEM_Int|MEM_IntReal))==0 ){ - /* If pIn3->u.i does not contain an integer, compute iKey as the - ** integer value of pIn3. Jump to P2 if pIn3 cannot be converted - ** into an integer without loss of information. Take care to avoid - ** changing the datatype of pIn3, however, as it is used by other - ** parts of the prepared statement. */ - Mem x = pIn3[0]; - applyAffinity(&x, SQLITE_AFF_NUMERIC, encoding); - if( (x.flags & MEM_Int)==0 ) goto jump_to_p2; - iKey = x.u.i; - goto notExistsWithKey; + /* If the file should be created now, create it and write the new data + ** into the file on disk. */ + if( p->nSpill>0 && (iAmt+iOfst)>p->nSpill ){ + int rc = memjrnlCreateFile(p); + if( rc==SQLITE_OK ){ + rc = sqlcipher_sqlite3OsWrite(pJfd, zBuf, iAmt, iOfst); + } + return rc; } - /* Fall through into OP_NotExists */ - /* no break */ deliberate_fall_through -case OP_NotExists: /* jump, in3 */ - pIn3 = &aMem[pOp->p3]; - assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid ); - assert( pOp->p1>=0 && pOp->p1nCursor ); - iKey = pIn3->u.i; -notExistsWithKey: - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); -#ifdef SQLITE_DEBUG - if( pOp->opcode==OP_SeekRowid ) pC->seekOp = OP_SeekRowid; -#endif - assert( pC->isTable ); - assert( pC->eCurType==CURTYPE_BTREE ); - pCrsr = pC->uc.pCursor; - assert( pCrsr!=0 ); - res = 0; - rc = sqlcipher_sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res); - assert( rc==SQLITE_OK || res==0 ); - pC->movetoTarget = iKey; /* Used by OP_Delete */ - pC->nullRow = 0; - pC->cacheStatus = CACHE_STALE; - pC->deferredMoveto = 0; - VdbeBranchTaken(res!=0,2); - pC->seekResult = res; - if( res!=0 ){ - assert( rc==SQLITE_OK ); - if( pOp->p2==0 ){ - rc = SQLITE_CORRUPT_BKPT; + + /* If the contents of this write should be stored in memory */ + else{ + /* An in-memory journal file should only ever be appended to. Random + ** access writes are not required. The only exception to this is when + ** the in-memory journal is being used by a connection using the + ** atomic-write optimization. In this case the first 28 bytes of the + ** journal file may be written as part of committing the transaction. */ + assert( iOfst<=p->endpoint.iOffset ); + if( iOfst>0 && iOfst!=p->endpoint.iOffset ){ + memjrnlTruncate(pJfd, iOfst); + } + if( iOfst==0 && p->pFirst ){ + assert( p->nChunkSize>iAmt ); + memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt); + }else{ + while( nWrite>0 ){ + FileChunk *pChunk = p->endpoint.pChunk; + int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize); + int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset); + + assert( pChunk!=0 || iChunkOffset==0 ); + if( iChunkOffset==0 ){ + /* New chunk is required to extend the file. */ + FileChunk *pNew = sqlcipher_sqlite3_malloc(fileChunkSize(p->nChunkSize)); + if( !pNew ){ + return SQLITE_IOERR_NOMEM_BKPT; + } + pNew->pNext = 0; + if( pChunk ){ + assert( p->pFirst ); + pChunk->pNext = pNew; + }else{ + assert( !p->pFirst ); + p->pFirst = pNew; + } + pChunk = p->endpoint.pChunk = pNew; + } + + assert( pChunk!=0 ); + memcpy((u8*)pChunk->zChunk + iChunkOffset, zWrite, iSpace); + zWrite += iSpace; + nWrite -= iSpace; + p->endpoint.iOffset += iSpace; + } + } + } + + return SQLITE_OK; +} + +/* +** Truncate the in-memory file. +*/ +static int memjrnlTruncate(sqlcipher_sqlite3_file *pJfd, sqlite_int64 size){ + MemJournal *p = (MemJournal *)pJfd; + assert( p->endpoint.pChunk==0 || p->endpoint.pChunk->pNext==0 ); + if( sizeendpoint.iOffset ){ + FileChunk *pIter = 0; + if( size==0 ){ + memjrnlFreeChunks(p->pFirst); + p->pFirst = 0; }else{ - goto jump_to_p2; + i64 iOff = p->nChunkSize; + for(pIter=p->pFirst; ALWAYS(pIter) && iOffpNext){ + iOff += p->nChunkSize; + } + if( ALWAYS(pIter) ){ + memjrnlFreeChunks(pIter->pNext); + pIter->pNext = 0; + } } + + p->endpoint.pChunk = pIter; + p->endpoint.iOffset = size; + p->readpoint.pChunk = 0; + p->readpoint.iOffset = 0; } - if( rc ) goto abort_due_to_error; - break; + return SQLITE_OK; } -/* Opcode: Sequence P1 P2 * * * -** Synopsis: r[P2]=cursor[P1].ctr++ +/* +** Close the file. +*/ +static int memjrnlClose(sqlcipher_sqlite3_file *pJfd){ + MemJournal *p = (MemJournal *)pJfd; + memjrnlFreeChunks(p->pFirst); + return SQLITE_OK; +} + +/* +** Sync the file. ** -** Find the next available sequence number for cursor P1. -** Write the sequence number into register P2. -** The sequence number on the cursor is incremented after this -** instruction. +** If the real file has been created, call its xSync method. Otherwise, +** syncing an in-memory journal is a no-op. */ -case OP_Sequence: { /* out2 */ - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( p->apCsr[pOp->p1]!=0 ); - assert( p->apCsr[pOp->p1]->eCurType!=CURTYPE_VTAB ); - pOut = out2Prerelease(p, pOp); - pOut->u.i = p->apCsr[pOp->p1]->seqCount++; - break; +static int memjrnlSync(sqlcipher_sqlite3_file *pJfd, int flags){ + UNUSED_PARAMETER2(pJfd, flags); + return SQLITE_OK; } +/* +** Query the size of the file in bytes. +*/ +static int memjrnlFileSize(sqlcipher_sqlite3_file *pJfd, sqlite_int64 *pSize){ + MemJournal *p = (MemJournal *)pJfd; + *pSize = (sqlite_int64) p->endpoint.iOffset; + return SQLITE_OK; +} -/* Opcode: NewRowid P1 P2 P3 * * -** Synopsis: r[P2]=rowid -** -** Get a new integer record number (a.k.a "rowid") used as the key to a table. -** The record number is not previously used as a key in the database -** table that cursor P1 points to. The new record number is written -** written to register P2. +/* +** Table of methods for MemJournal sqlcipher_sqlite3_file object. +*/ +static const struct sqlcipher_sqlite3_io_methods MemJournalMethods = { + 1, /* iVersion */ + memjrnlClose, /* xClose */ + memjrnlRead, /* xRead */ + memjrnlWrite, /* xWrite */ + memjrnlTruncate, /* xTruncate */ + memjrnlSync, /* xSync */ + memjrnlFileSize, /* xFileSize */ + 0, /* xLock */ + 0, /* xUnlock */ + 0, /* xCheckReservedLock */ + 0, /* xFileControl */ + 0, /* xSectorSize */ + 0, /* xDeviceCharacteristics */ + 0, /* xShmMap */ + 0, /* xShmLock */ + 0, /* xShmBarrier */ + 0, /* xShmUnmap */ + 0, /* xFetch */ + 0 /* xUnfetch */ +}; + +/* +** Open a journal file. ** -** If P3>0 then P3 is a register in the root frame of this VDBE that holds -** the largest previously generated record number. No new record numbers are -** allowed to be less than this value. When this value reaches its maximum, -** an SQLITE_FULL error is generated. The P3 register is updated with the ' -** generated record number. This P3 mechanism is used to help implement the -** AUTOINCREMENT feature. +** The behaviour of the journal file depends on the value of parameter +** nSpill. If nSpill is 0, then the journal file is always create and +** accessed using the underlying VFS. If nSpill is less than zero, then +** all content is always stored in main-memory. Finally, if nSpill is a +** positive value, then the journal file is initially created in-memory +** but may be flushed to disk later on. In this case the journal file is +** flushed to disk either when it grows larger than nSpill bytes in size, +** or when sqlcipher_sqlite3JournalCreate() is called. */ -case OP_NewRowid: { /* out2 */ - i64 v; /* The new rowid */ - VdbeCursor *pC; /* Cursor of table to get the new rowid */ - int res; /* Result of an sqlcipher_sqlite3BtreeLast() */ - int cnt; /* Counter to limit the number of searches */ - Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ - VdbeFrame *pFrame; /* Root frame of VDBE */ +SQLITE_PRIVATE int sqlcipher_sqlite3JournalOpen( + sqlcipher_sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */ + const char *zName, /* Name of the journal file */ + sqlcipher_sqlite3_file *pJfd, /* Preallocated, blank file handle */ + int flags, /* Opening flags */ + int nSpill /* Bytes buffered before opening the file */ +){ + MemJournal *p = (MemJournal*)pJfd; - v = 0; - res = 0; - pOut = out2Prerelease(p, pOp); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->isTable ); - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->uc.pCursor!=0 ); - { - /* The next rowid or record number (different terms for the same - ** thing) is obtained in a two-step algorithm. - ** - ** First we attempt to find the largest existing rowid and add one - ** to that. But if the largest existing rowid is already the maximum - ** positive integer, we have to fall through to the second - ** probabilistic algorithm - ** - ** The second algorithm is to select a rowid at random and see if - ** it already exists in the table. If it does not exist, we have - ** succeeded. If the random rowid does exist, we select a new one - ** and try again, up to 100 times. - */ - assert( pC->isTable ); + /* Zero the file-handle object. If nSpill was passed zero, initialize + ** it using the sqlcipher_sqlite3OsOpen() function of the underlying VFS. In this + ** case none of the code in this module is executed as a result of calls + ** made on the journal file-handle. */ + memset(p, 0, sizeof(MemJournal)); + if( nSpill==0 ){ + return sqlcipher_sqlite3OsOpen(pVfs, zName, pJfd, flags, 0); + } -#ifdef SQLITE_32BIT_ROWID -# define MAX_ROWID 0x7fffffff -#else - /* Some compilers complain about constants of the form 0x7fffffffffffffff. - ** Others complain about 0x7ffffffffffffffffLL. The following macro seems - ** to provide the constant while making all compilers happy. - */ -# define MAX_ROWID (i64)( (((u64)0x7fffffff)<<32) | (u64)0xffffffff ) -#endif + if( nSpill>0 ){ + p->nChunkSize = nSpill; + }else{ + p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk); + assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) ); + } - if( !pC->useRandomRowid ){ - rc = sqlcipher_sqlite3BtreeLast(pC->uc.pCursor, &res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( res ){ - v = 1; /* IMP: R-61914-48074 */ - }else{ - assert( sqlcipher_sqlite3BtreeCursorIsValid(pC->uc.pCursor) ); - v = sqlcipher_sqlite3BtreeIntegerKey(pC->uc.pCursor); - if( v>=MAX_ROWID ){ - pC->useRandomRowid = 1; - }else{ - v++; /* IMP: R-29538-34987 */ - } - } - } + pJfd->pMethods = (const sqlcipher_sqlite3_io_methods*)&MemJournalMethods; + p->nSpill = nSpill; + p->flags = flags; + p->zJournal = zName; + p->pVfs = pVfs; + return SQLITE_OK; +} -#ifndef SQLITE_OMIT_AUTOINCREMENT - if( pOp->p3 ){ - /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3>0 ); - if( p->pFrame ){ - for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); - /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3<=pFrame->nMem ); - pMem = &pFrame->aMem[pOp->p3]; - }else{ - /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3<=(p->nMem+1 - p->nCursor) ); - pMem = &aMem[pOp->p3]; - memAboutToChange(p, pMem); - } - assert( memIsValid(pMem) ); +/* +** Open an in-memory journal file. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3MemJournalOpen(sqlcipher_sqlite3_file *pJfd){ + sqlcipher_sqlite3JournalOpen(0, 0, pJfd, 0, -1); +} - REGISTER_TRACE(pOp->p3, pMem); - sqlcipher_sqlite3VdbeMemIntegerify(pMem); - assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ - if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ - rc = SQLITE_FULL; /* IMP: R-17817-00630 */ - goto abort_due_to_error; - } - if( vu.i+1 ){ - v = pMem->u.i + 1; - } - pMem->u.i = v; - } +#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ + || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) +/* +** If the argument p points to a MemJournal structure that is not an +** in-memory-only journal file (i.e. is one that was opened with a +ve +** nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying +** file has not yet been created, create it now. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3JournalCreate(sqlcipher_sqlite3_file *pJfd){ + int rc = SQLITE_OK; + MemJournal *p = (MemJournal*)pJfd; + if( pJfd->pMethods==&MemJournalMethods && ( +#ifdef SQLITE_ENABLE_ATOMIC_WRITE + p->nSpill>0 +#else + /* While this appears to not be possible without ATOMIC_WRITE, the + ** paths are complex, so it seems prudent to leave the test in as + ** a NEVER(), in case our analysis is subtly flawed. */ + NEVER(p->nSpill>0) #endif - if( pC->useRandomRowid ){ - /* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the - ** largest possible integer (9223372036854775807) then the database - ** engine starts picking positive candidate ROWIDs at random until - ** it finds one that is not previously used. */ - assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is - ** an AUTOINCREMENT table. */ - cnt = 0; - do{ - sqlcipher_sqlite3_randomness(sizeof(v), &v); - v &= (MAX_ROWID>>1); v++; /* Ensure that v is greater than zero */ - }while( ((rc = sqlcipher_sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)v, - 0, &res))==SQLITE_OK) - && (res==0) - && (++cnt<100)); - if( rc ) goto abort_due_to_error; - if( res==0 ){ - rc = SQLITE_FULL; /* IMP: R-38219-53002 */ - goto abort_due_to_error; - } - assert( v>0 ); /* EV: R-40812-03570 */ - } - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; +#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE + || (p->flags & SQLITE_OPEN_MAIN_JOURNAL) +#endif + )){ + rc = memjrnlCreateFile(p); } - pOut->u.i = v; - break; + return rc; } +#endif -/* Opcode: Insert P1 P2 P3 P4 P5 -** Synopsis: intkey=r[P3] data=r[P2] +/* +** The file-handle passed as the only argument is open on a journal file. +** Return true if this "journal file" is currently stored in heap memory, +** or false otherwise. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3JournalIsInMemory(sqlcipher_sqlite3_file *p){ + return p->pMethods==&MemJournalMethods; +} + +/* +** Return the number of bytes required to store a JournalFile that uses vfs +** pVfs to create the underlying on-disk files. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3JournalSize(sqlcipher_sqlite3_vfs *pVfs){ + return MAX(pVfs->szOsFile, (int)sizeof(MemJournal)); +} + +/************** End of memjournal.c ******************************************/ +/************** Begin file crypto.c ******************************************/ +/* +** SQLCipher +** http://sqlcipher.net ** -** Write an entry into the table of cursor P1. A new entry is -** created if it doesn't already exist or the data for an existing -** entry is overwritten. The data is the value MEM_Blob stored in register -** number P2. The key is stored in register P3. The key must -** be a MEM_Int. +** Copyright (c) 2008 - 2013, ZETETIC LLC +** All rights reserved. ** -** If the OPFLAG_NCHANGE flag of P5 is set, then the row change count is -** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P5 is set, -** then rowid is stored for subsequent return by the -** sqlcipher_sqlite3_last_insert_rowid() function (otherwise it is unmodified). +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the ZETETIC LLC nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. ** -** If the OPFLAG_USESEEKRESULT flag of P5 is set, the implementation might -** run faster by avoiding an unnecessary seek on cursor P1. However, -** the OPFLAG_USESEEKRESULT flag must only be set if there have been no prior -** seeks on the cursor or if the most recent seek used a key equal to P3. +** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** -** If the OPFLAG_ISUPDATE flag is set, then this opcode is part of an -** UPDATE operation. Otherwise (if the flag is clear) then this opcode -** is part of an INSERT operation. The difference is only important to -** the update hook. +*/ +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC + +/* #include */ +/************** Include sqlcipher.h in the middle of crypto.c ****************/ +/************** Begin file sqlcipher.h ***************************************/ +/* +** SQLCipher +** sqlcipher.h developed by Stephen Lombardo (Zetetic LLC) +** sjlombardo at zetetic dot net +** http://zetetic.net ** -** Parameter P4 may point to a Table structure, or may be NULL. If it is -** not NULL, then the update-hook (sqlcipher_sqlite3.xUpdateCallback) is invoked -** following a successful insert. +** Copyright (c) 2008, ZETETIC LLC +** All rights reserved. ** -** (WARNING/TODO: If P1 is a pseudo-cursor and P2 is dynamically -** allocated, then ownership of P2 is transferred to the pseudo-cursor -** and register P2 becomes ephemeral. If the cursor is changed, the -** value of register P2 will then change. Make sure this does not -** cause any problems.) +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the ZETETIC LLC nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** -** This instruction only works on tables. The equivalent instruction -** for indices is OP_IdxInsert. */ -case OP_Insert: { - Mem *pData; /* MEM cell holding data for the record to be inserted */ - Mem *pKey; /* MEM cell holding key for the record */ - VdbeCursor *pC; /* Cursor to table into which insert is written */ - int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ - const char *zDb; /* database name - used by the update hook */ - Table *pTab; /* Table structure - used by update and pre-update hooks */ - BtreePayload x; /* Payload to be inserted */ +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC +#ifndef SQLCIPHER_H +#define SQLCIPHER_H - pData = &aMem[pOp->p2]; - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( memIsValid(pData) ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->deferredMoveto==0 ); - assert( pC->uc.pCursor!=0 ); - assert( (pOp->p5 & OPFLAG_ISNOOP) || pC->isTable ); - assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC ); - REGISTER_TRACE(pOp->p2, pData); - sqlcipher_sqlite3VdbeIncrWriteCounter(p, pC); +/* #include "sqlcipher_sqlite3.h" */ - pKey = &aMem[pOp->p3]; - assert( pKey->flags & MEM_Int ); - assert( memIsValid(pKey) ); - REGISTER_TRACE(pOp->p3, pKey); - x.nKey = pKey->u.i; +#define SQLCIPHER_HMAC_SHA1 0 +#define SQLCIPHER_HMAC_SHA1_LABEL "HMAC_SHA1" +#define SQLCIPHER_HMAC_SHA256 1 +#define SQLCIPHER_HMAC_SHA256_LABEL "HMAC_SHA256" +#define SQLCIPHER_HMAC_SHA512 2 +#define SQLCIPHER_HMAC_SHA512_LABEL "HMAC_SHA512" - if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){ - assert( pC->iDb>=0 ); - zDb = db->aDb[pC->iDb].zDbSName; - pTab = pOp->p4.pTab; - assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) ); - }else{ - pTab = 0; - zDb = 0; /* Not needed. Silence a compiler warning. */ - } -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK - /* Invoke the pre-update hook, if any */ - if( pTab ){ - if( db->xPreUpdateCallback && !(pOp->p5 & OPFLAG_ISUPDATE) ){ - sqlcipher_sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey,pOp->p2); - } - if( db->xUpdateCallback==0 || pTab->aCol==0 ){ - /* Prevent post-update hook from running in cases when it should not */ - pTab = 0; - } - } - if( pOp->p5 & OPFLAG_ISNOOP ) break; -#endif +#define SQLCIPHER_PBKDF2_HMAC_SHA1 0 +#define SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL "PBKDF2_HMAC_SHA1" +#define SQLCIPHER_PBKDF2_HMAC_SHA256 1 +#define SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL "PBKDF2_HMAC_SHA256" +#define SQLCIPHER_PBKDF2_HMAC_SHA512 2 +#define SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL "PBKDF2_HMAC_SHA512" - if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; - assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 ); - x.pData = pData->z; - x.nData = pData->n; - seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); - if( pData->flags & MEM_Zero ){ - x.nZero = pData->u.nZero; - }else{ - x.nZero = 0; - } - x.pKey = 0; - rc = sqlcipher_sqlite3BtreeInsert(pC->uc.pCursor, &x, - (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), seekResult - ); - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; - /* Invoke the update-hook if required. */ - if( rc ) goto abort_due_to_error; - if( pTab ){ - assert( db->xUpdateCallback!=0 ); - assert( pTab->aCol!=0 ); - db->xUpdateCallback(db->pUpdateArg, - (pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT, - zDb, pTab->zName, x.nKey); - } - break; -} +typedef struct { + int (*activate)(void *ctx); + int (*deactivate)(void *ctx); + const char* (*get_provider_name)(void *ctx); + int (*add_random)(void *ctx, void *buffer, int length); + int (*random)(void *ctx, void *buffer, int length); + int (*hmac)(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out); + int (*kdf)(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key); + int (*cipher)(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out); + const char* (*get_cipher)(void *ctx); + int (*get_key_sz)(void *ctx); + int (*get_iv_sz)(void *ctx); + int (*get_block_sz)(void *ctx); + int (*get_hmac_sz)(void *ctx, int algorithm); + int (*ctx_init)(void **ctx); + int (*ctx_free)(void **ctx); + int (*fips_status)(void *ctx); + const char* (*get_provider_version)(void *ctx); +} sqlcipher_provider; -/* Opcode: Delete P1 P2 P3 P4 P5 -** -** Delete the record at which the P1 cursor is currently pointing. -** -** If the OPFLAG_SAVEPOSITION bit of the P5 parameter is set, then -** the cursor will be left pointing at either the next or the previous -** record in the table. If it is left pointing at the next record, then -** the next Next instruction will be a no-op. As a result, in this case -** it is ok to delete a record from within a Next loop. If -** OPFLAG_SAVEPOSITION bit of P5 is clear, then the cursor will be -** left in an undefined state. -** -** If the OPFLAG_AUXDELETE bit is set on P5, that indicates that this -** delete one of several associated with deleting a table row and all its -** associated index entries. Exactly one of those deletes is the "primary" -** delete. The others are all on OPFLAG_FORDELETE cursors or else are -** marked with the AUXDELETE flag. +/* utility functions */ +void* sqlcipher_malloc(sqlite_uint64); +void sqlcipher_mlock(void *, sqlite_uint64); +void sqlcipher_munlock(void *, sqlite_uint64); +void* sqlcipher_memset(void *, unsigned char, sqlite_uint64); +int sqlcipher_ismemset(const void *, unsigned char, sqlite_uint64); +int sqlcipher_memcmp(const void *, const void *, int); +void sqlcipher_free(void *, sqlite_uint64); +char* sqlcipher_version(); + +/* provider interfaces */ +int sqlcipher_register_provider(sqlcipher_provider *); +sqlcipher_provider* sqlcipher_get_provider(void); + +#define SQLCIPHER_MUTEX_PROVIDER 0 +#define SQLCIPHER_MUTEX_PROVIDER_ACTIVATE 1 +#define SQLCIPHER_MUTEX_PROVIDER_RAND 2 +#define SQLCIPHER_MUTEX_RESERVED1 3 +#define SQLCIPHER_MUTEX_RESERVED2 4 +#define SQLCIPHER_MUTEX_RESERVED3 5 +#define SQLCIPHER_MUTEX_COUNT 6 + +sqlcipher_sqlite3_mutex* sqlcipher_mutex(int); + +#endif +#endif +/* END SQLCIPHER */ + + +/************** End of sqlcipher.h *******************************************/ +/************** Continuing where we left off in crypto.c *********************/ +/************** Include crypto.h in the middle of crypto.c *******************/ +/************** Begin file crypto.h ******************************************/ +/* +** SQLCipher +** crypto.h developed by Stephen Lombardo (Zetetic LLC) +** sjlombardo at zetetic dot net +** http://zetetic.net ** -** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row -** change count is incremented (otherwise not). +** Copyright (c) 2008, ZETETIC LLC +** All rights reserved. ** -** P1 must not be pseudo-table. It has to be a real table with -** multiple rows. +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the ZETETIC LLC nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. ** -** If P4 is not NULL then it points to a Table object. In this case either -** the update or pre-update hook, or both, may be invoked. The P1 cursor must -** have been positioned using OP_NotFound prior to invoking this opcode in -** this case. Specifically, if one is configured, the pre-update hook is -** invoked if P4 is not NULL. The update-hook is invoked if one is configured, -** P4 is not NULL, and the OPFLAG_NCHANGE flag is set in P2. +** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** -** If the OPFLAG_ISUPDATE flag is set in P2, then P3 contains the address -** of the memory cell that contains the value that the rowid of the row will -** be set to by the update. */ -case OP_Delete: { - VdbeCursor *pC; - const char *zDb; - Table *pTab; - int opflags; +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC +#ifndef CRYPTO_H +#define CRYPTO_H - opflags = pOp->p2; - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->uc.pCursor!=0 ); - assert( pC->deferredMoveto==0 ); - sqlcipher_sqlite3VdbeIncrWriteCounter(p, pC); +/* #include "sqliteInt.h" */ +/* #include "btreeInt.h" */ +/* #include "pager.h" */ +/* #include "vdbeInt.h" */ -#ifdef SQLITE_DEBUG - if( pOp->p4type==P4_TABLE - && HasRowid(pOp->p4.pTab) - && pOp->p5==0 - && sqlcipher_sqlite3BtreeCursorIsValidNN(pC->uc.pCursor) - ){ - /* If p5 is zero, the seek operation that positioned the cursor prior to - ** OP_Delete will have also set the pC->movetoTarget field to the rowid of - ** the row that is being deleted */ - i64 iKey = sqlcipher_sqlite3BtreeIntegerKey(pC->uc.pCursor); - assert( CORRUPT_DB || pC->movetoTarget==iKey ); - } +#ifdef __ANDROID__ +#include #endif - /* If the update-hook or pre-update-hook will be invoked, set zDb to - ** the name of the db to pass as to it. Also set local pTab to a copy - ** of p4.pTab. Finally, if p5 is true, indicating that this cursor was - ** last moved with OP_Next or OP_Prev, not Seek or NotFound, set - ** VdbeCursor.movetoTarget to the current rowid. */ - if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){ - assert( pC->iDb>=0 ); - assert( pOp->p4.pTab!=0 ); - zDb = db->aDb[pC->iDb].zDbSName; - pTab = pOp->p4.pTab; - if( (pOp->p5 & OPFLAG_SAVEPOSITION)!=0 && pC->isTable ){ - pC->movetoTarget = sqlcipher_sqlite3BtreeIntegerKey(pC->uc.pCursor); - } - }else{ - zDb = 0; /* Not needed. Silence a compiler warning. */ - pTab = 0; /* Not needed. Silence a compiler warning. */ - } +/* #include */ -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK - /* Invoke the pre-update-hook if required. */ - if( db->xPreUpdateCallback && pOp->p4.pTab ){ - assert( !(opflags & OPFLAG_ISUPDATE) - || HasRowid(pTab)==0 - || (aMem[pOp->p3].flags & MEM_Int) - ); - sqlcipher_sqlite3VdbePreUpdateHook(p, pC, - (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE, - zDb, pTab, pC->movetoTarget, - pOp->p3 - ); - } - if( opflags & OPFLAG_ISNOOP ) break; +#if defined(_WIN32) || defined(SQLITE_OS_WINRT) +/* #include ** amalgamator: dontcache ** */ +#else +/* #include ** amalgamator: dontcache ** */ #endif - /* Only flags that can be set are SAVEPOISTION and AUXDELETE */ - assert( (pOp->p5 & ~(OPFLAG_SAVEPOSITION|OPFLAG_AUXDELETE))==0 ); - assert( OPFLAG_SAVEPOSITION==BTREE_SAVEPOSITION ); - assert( OPFLAG_AUXDELETE==BTREE_AUXDELETE ); +#ifndef OMIT_MEMLOCK +#if defined(__unix__) || defined(__APPLE__) || defined(_AIX) +/* #include ** amalgamator: dontcache ** */ +/* #include ** amalgamator: dontcache ** */ +#include /* amalgamator: dontcache */ +/* #include ** amalgamator: dontcache ** */ +#endif +#endif -#ifdef SQLITE_DEBUG - if( p->pFrame==0 ){ - if( pC->isEphemeral==0 - && (pOp->p5 & OPFLAG_AUXDELETE)==0 - && (pC->wrFlag & OPFLAG_FORDELETE)==0 - ){ - nExtraDelete++; - } - if( pOp->p2 & OPFLAG_NCHANGE ){ - nExtraDelete--; - } - } +/* #include "sqlcipher.h" */ + +/* extensions defined in pager.c */ +void *sqlcipherPagerGetCodec(Pager*); +void sqlcipherPagerSetCodec(Pager*, void *(*)(void*,void*,Pgno,int), void (*)(void*,int,int), void (*)(void*), void *); +SQLITE_API int sqlcipher_sqlite3pager_is_sj_pgno(Pager*, Pgno); +SQLITE_API void sqlcipher_sqlite3pager_error(Pager*, int); +SQLITE_API void sqlcipher_sqlite3pager_reset(Pager *pPager); +/* end extensions defined in pager.c */ + +#if !defined (SQLCIPHER_CRYPTO_CC) \ + && !defined (SQLCIPHER_CRYPTO_LIBTOMCRYPT) \ + && !defined (SQLCIPHER_CRYPTO_NSS) \ + && !defined (SQLCIPHER_CRYPTO_OPENSSL) +#define SQLCIPHER_CRYPTO_OPENSSL #endif - rc = sqlcipher_sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5); - pC->cacheStatus = CACHE_STALE; - pC->seekResult = 0; - if( rc ) goto abort_due_to_error; +#define FILE_HEADER_SZ 16 - /* Invoke the update-hook if required. */ - if( opflags & OPFLAG_NCHANGE ){ - p->nChange++; - if( db->xUpdateCallback && HasRowid(pTab) ){ - db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName, - pC->movetoTarget); - assert( pC->iDb>=0 ); - } - } +#define CIPHER_XSTR(s) CIPHER_STR(s) +#define CIPHER_STR(s) #s - break; -} -/* Opcode: ResetCount * * * * * -** -** The value of the change counter is copied to the database handle -** change counter (returned by subsequent calls to sqlcipher_sqlite3_changes()). -** Then the VMs internal change counter resets to 0. -** This is used by trigger programs. -*/ -case OP_ResetCount: { - sqlcipher_sqlite3VdbeSetChanges(db, p->nChange); - p->nChange = 0; - break; -} +#ifndef CIPHER_VERSION_NUMBER +#define CIPHER_VERSION_NUMBER 4.5.3 +#endif -/* Opcode: SorterCompare P1 P2 P3 P4 -** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2 -** -** P1 is a sorter cursor. This instruction compares a prefix of the -** record blob in register P3 against a prefix of the entry that -** the sorter cursor currently points to. Only the first P4 fields -** of r[P3] and the sorter record are compared. -** -** If either P3 or the sorter contains a NULL in one of their significant -** fields (not counting the P4 fields at the end which are ignored) then -** the comparison is assumed to be equal. -** -** Fall through to next instruction if the two records compare equal to -** each other. Jump to P2 if they are different. -*/ -case OP_SorterCompare: { - VdbeCursor *pC; - int res; - int nKeyCol; +#ifndef CIPHER_VERSION_BUILD +#define CIPHER_VERSION_BUILD community +#endif - pC = p->apCsr[pOp->p1]; - assert( isSorter(pC) ); - assert( pOp->p4type==P4_INT32 ); - pIn3 = &aMem[pOp->p3]; - nKeyCol = pOp->p4.i; - res = 0; - rc = sqlcipher_sqlite3VdbeSorterCompare(pC, pIn3, nKeyCol, &res); - VdbeBranchTaken(res!=0,2); - if( rc ) goto abort_due_to_error; - if( res ) goto jump_to_p2; - break; -}; +#define CIPHER_DECRYPT 0 +#define CIPHER_ENCRYPT 1 -/* Opcode: SorterData P1 P2 P3 * * -** Synopsis: r[P2]=data -** -** Write into register P2 the current sorter data for sorter cursor P1. -** Then clear the column header cache on cursor P3. -** -** This opcode is normally use to move a record out of the sorter and into -** a register that is the source for a pseudo-table cursor created using -** OpenPseudo. That pseudo-table cursor is the one that is identified by -** parameter P3. Clearing the P3 column cache as part of this opcode saves -** us from having to issue a separate NullRow instruction to clear that cache. -*/ -case OP_SorterData: { - VdbeCursor *pC; +#define CIPHER_READ_CTX 0 +#define CIPHER_WRITE_CTX 1 +#define CIPHER_READWRITE_CTX 2 - pOut = &aMem[pOp->p2]; - pC = p->apCsr[pOp->p1]; - assert( isSorter(pC) ); - rc = sqlcipher_sqlite3VdbeSorterRowkey(pC, pOut); - assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) ); - assert( pOp->p1>=0 && pOp->p1nCursor ); - if( rc ) goto abort_due_to_error; - p->apCsr[pOp->p3]->cacheStatus = CACHE_STALE; - break; -} +#ifndef PBKDF2_ITER +#define PBKDF2_ITER 256000 +#endif -/* Opcode: RowData P1 P2 P3 * * -** Synopsis: r[P2]=data -** -** Write into register P2 the complete row content for the row at -** which cursor P1 is currently pointing. -** There is no interpretation of the data. -** It is just copied onto the P2 register exactly as -** it is found in the database file. -** -** If cursor P1 is an index, then the content is the key of the row. -** If cursor P2 is a table, then the content extracted is the data. -** -** If the P1 cursor must be pointing to a valid row (not a NULL row) -** of a real table, not a pseudo-table. -** -** If P3!=0 then this opcode is allowed to make an ephemeral pointer -** into the database page. That means that the content of the output -** register will be invalidated as soon as the cursor moves - including -** moves caused by other cursors that "save" the current cursors -** position in order that they can write to the same table. If P3==0 -** then a copy of the data is made into memory. P3!=0 is faster, but -** P3==0 is safer. -** -** If P3!=0 then the content of the P2 register is unsuitable for use -** in OP_Result and any OP_Result will invalidate the P2 register content. -** The P2 register content is invalidated by opcodes like OP_Function or -** by any use of another cursor pointing to the same table. -*/ -case OP_RowData: { - VdbeCursor *pC; - BtCursor *pCrsr; - u32 n; +/* possible flags for cipher_ctx->flags */ +#define CIPHER_FLAG_HMAC 0x01 +#define CIPHER_FLAG_LE_PGNO 0x02 +#define CIPHER_FLAG_BE_PGNO 0x04 - pOut = out2Prerelease(p, pOp); +#ifndef DEFAULT_CIPHER_FLAGS +#define DEFAULT_CIPHER_FLAGS CIPHER_FLAG_HMAC | CIPHER_FLAG_LE_PGNO +#endif - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->eCurType==CURTYPE_BTREE ); - assert( isSorter(pC)==0 ); - assert( pC->nullRow==0 ); - assert( pC->uc.pCursor!=0 ); - pCrsr = pC->uc.pCursor; - /* The OP_RowData opcodes always follow OP_NotExists or - ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions - ** that might invalidate the cursor. - ** If this where not the case, on of the following assert()s - ** would fail. Should this ever change (because of changes in the code - ** generator) then the fix would be to insert a call to - ** sqlcipher_sqlite3VdbeCursorMoveto(). - */ - assert( pC->deferredMoveto==0 ); - assert( sqlcipher_sqlite3BtreeCursorIsValid(pCrsr) ); +/* by default, sqlcipher will use a reduced number of iterations to generate + the HMAC key / or transform a raw cipher key + */ +#ifndef FAST_PBKDF2_ITER +#define FAST_PBKDF2_ITER 2 +#endif - n = sqlcipher_sqlite3BtreePayloadSize(pCrsr); - if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; +/* this if a fixed random array that will be xor'd with the database salt to ensure that the + salt passed to the HMAC key derivation function is not the same as that used to derive + the encryption key. This can be overridden at compile time but it will make the resulting + binary incompatible with the default builds when using HMAC. A future version of SQLcipher + will likely allow this to be defined at runtime via pragma */ +#ifndef HMAC_SALT_MASK +#define HMAC_SALT_MASK 0x3a +#endif + +#ifndef CIPHER_MAX_IV_SZ +#define CIPHER_MAX_IV_SZ 16 +#endif + +#ifndef CIPHER_MAX_KEY_SZ +#define CIPHER_MAX_KEY_SZ 64 +#endif + + +/* +** Simple shared routines for converting hex char strings to binary data + */ +static int cipher_hex2int(char c) { + return (c>='0' && c<='9') ? (c)-'0' : + (c>='A' && c<='F') ? (c)-'A'+10 : + (c>='a' && c<='f') ? (c)-'a'+10 : 0; +} + +static void cipher_hex2bin(const unsigned char *hex, int sz, unsigned char *out){ + int i; + for(i = 0; i < sz; i += 2){ + out[i/2] = (cipher_hex2int(hex[i])<<4) | cipher_hex2int(hex[i+1]); } - testcase( n==0 ); - rc = sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(pCrsr, n, pOut); - if( rc ) goto abort_due_to_error; - if( !pOp->p3 ) Deephemeralize(pOut); - UPDATE_MAX_BLOBSIZE(pOut); - REGISTER_TRACE(pOp->p2, pOut); - break; } -/* Opcode: Rowid P1 P2 * * * -** Synopsis: r[P2]=rowid -** -** Store in register P2 an integer which is the key of the table entry that -** P1 is currently point to. -** -** P1 can be either an ordinary table or a virtual table. There used to -** be a separate OP_VRowid opcode for use with virtual tables, but this -** one opcode now works for both table types. -*/ -case OP_Rowid: { /* out2 */ - VdbeCursor *pC; - i64 v; - sqlcipher_sqlite3_vtab *pVtab; - const sqlcipher_sqlite3_module *pModule; +static void cipher_bin2hex(const unsigned char* in, int sz, char *out) { + int i; + for(i=0; i < sz; i++) { + sqlcipher_sqlite3_snprintf(3, out + (i*2), "%02x ", in[i]); + } +} - pOut = out2Prerelease(p, pOp); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); - if( pC->nullRow ){ - pOut->flags = MEM_Null; - break; - }else if( pC->deferredMoveto ){ - v = pC->movetoTarget; -#ifndef SQLITE_OMIT_VIRTUALTABLE - }else if( pC->eCurType==CURTYPE_VTAB ){ - assert( pC->uc.pVCur!=0 ); - pVtab = pC->uc.pVCur->pVtab; - pModule = pVtab->pModule; - assert( pModule->xRowid ); - rc = pModule->xRowid(pC->uc.pVCur, &v); - sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); - if( rc ) goto abort_due_to_error; -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - }else{ - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->uc.pCursor!=0 ); - rc = sqlcipher_sqlite3VdbeCursorRestore(pC); - if( rc ) goto abort_due_to_error; - if( pC->nullRow ){ - pOut->flags = MEM_Null; - break; +static int cipher_isHex(const unsigned char *hex, int sz){ + int i; + for(i = 0; i < sz; i++) { + unsigned char c = hex[i]; + if ((c < '0' || c > '9') && + (c < 'A' || c > 'F') && + (c < 'a' || c > 'f')) { + return 0; } - v = sqlcipher_sqlite3BtreeIntegerKey(pC->uc.pCursor); } - pOut->u.i = v; - break; + return 1; } -/* Opcode: NullRow P1 * * * * -** -** Move the cursor P1 to a null row. Any OP_Column operations -** that occur while the cursor is on the null row will always -** write a NULL. -*/ -case OP_NullRow: { - VdbeCursor *pC; +/* possible flags for simulating specific test conditions */ +#ifdef SQLCIPHER_TEST +#define TEST_FAIL_ENCRYPT 0x01 +#define TEST_FAIL_DECRYPT 0x02 +#define TEST_FAIL_MIGRATE 0x04 +unsigned int sqlcipher_get_test_flags(void); +void sqlcipher_set_test_flags(unsigned int); +int sqlcipher_get_test_rand(void); +void sqlcipher_set_test_rand(int); +int sqlcipher_get_test_fail(void); +#endif + +/* extensions defined in crypto_impl.c */ +/* the default implementation of SQLCipher uses a cipher_ctx + to keep track of read / write state separately. The following + struct and associated functions are defined here */ +typedef struct { + int derive_key; + int pass_sz; + unsigned char *key; + unsigned char *hmac_key; + unsigned char *pass; + char *keyspec; +} cipher_ctx; + + +typedef struct { + int store_pass; + int kdf_iter; + int fast_kdf_iter; + int kdf_salt_sz; + int key_sz; + int iv_sz; + int block_sz; + int page_sz; + int keyspec_sz; + int reserve_sz; + int hmac_sz; + int plaintext_header_sz; + int hmac_algorithm; + int kdf_algorithm; + unsigned int skip_read_hmac; + unsigned int need_kdf_salt; + unsigned int flags; + unsigned char *kdf_salt; + unsigned char *hmac_kdf_salt; + unsigned char *buffer; + Btree *pBt; + cipher_ctx *read_ctx; + cipher_ctx *write_ctx; + sqlcipher_provider *provider; + void *provider_ctx; +} codec_ctx ; + +/* crypto.c functions */ +int sqlcipher_codec_pragma(sqlcipher_sqlite3*, int, Parse*, const char *, const char*); +int sqlcipherCodecAttach(sqlcipher_sqlite3*, int, const void *, int); +void sqlcipherCodecGetKey(sqlcipher_sqlite3*, int, void**, int*); +void sqlcipher_exportFunc(sqlcipher_sqlite3_context *, int, sqlcipher_sqlite3_value **); + +/* crypto_impl.c functions */ + +void sqlcipher_init_memmethods(void); + +/* activation and initialization */ +void sqlcipher_activate(void); +void sqlcipher_deactivate(void); + +int sqlcipher_codec_ctx_init(codec_ctx **, Db *, Pager *, const void *, int); +void sqlcipher_codec_ctx_free(codec_ctx **); +int sqlcipher_codec_key_derive(codec_ctx *); +int sqlcipher_codec_key_copy(codec_ctx *, int); + +/* page cipher implementation */ +int sqlcipher_page_cipher(codec_ctx *, int, Pgno, int, int, unsigned char *, unsigned char *); + +/* context setters & getters */ +void sqlcipher_codec_ctx_set_error(codec_ctx *, int); + +void sqlcipher_codec_get_pass(codec_ctx *, void **, int *); +int sqlcipher_codec_ctx_set_pass(codec_ctx *, const void *, int, int); +void sqlcipher_codec_get_keyspec(codec_ctx *, void **zKey, int *nKey); + +int sqlcipher_codec_ctx_set_pagesize(codec_ctx *, int); +int sqlcipher_codec_ctx_get_pagesize(codec_ctx *); +int sqlcipher_codec_ctx_get_reservesize(codec_ctx *); + +void sqlcipher_set_default_pagesize(int page_size); +int sqlcipher_get_default_pagesize(void); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - pC->nullRow = 1; - pC->cacheStatus = CACHE_STALE; - if( pC->eCurType==CURTYPE_BTREE ){ - assert( pC->uc.pCursor!=0 ); - sqlcipher_sqlite3BtreeClearCursor(pC->uc.pCursor); - } -#ifdef SQLITE_DEBUG - if( pC->seekOp==0 ) pC->seekOp = OP_NullRow; -#endif - break; -} +void sqlcipher_set_default_kdf_iter(int iter); +int sqlcipher_get_default_kdf_iter(void); +int sqlcipher_codec_ctx_set_kdf_iter(codec_ctx *, int); +int sqlcipher_codec_ctx_get_kdf_iter(codec_ctx *ctx); -/* Opcode: SeekEnd P1 * * * * -** -** Position cursor P1 at the end of the btree for the purpose of -** appending a new entry onto the btree. -** -** It is assumed that the cursor is used only for appending and so -** if the cursor is valid, then the cursor must already be pointing -** at the end of the btree and so no changes are made to -** the cursor. -*/ -/* Opcode: Last P1 P2 * * * -** -** The next use of the Rowid or Column or Prev instruction for P1 -** will refer to the last entry in the database table or index. -** If the table or index is empty and P2>0, then jump immediately to P2. -** If P2 is 0 or if the table or index is not empty, fall through -** to the following instruction. -** -** This opcode leaves the cursor configured to move in reverse order, -** from the end toward the beginning. In other words, the cursor is -** configured to use Prev, not Next. -*/ -case OP_SeekEnd: -case OP_Last: { /* jump */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; +int sqlcipher_codec_ctx_set_kdf_salt(codec_ctx *ctx, unsigned char *salt, int sz); +int sqlcipher_codec_ctx_get_kdf_salt(codec_ctx *ctx, void **salt); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->eCurType==CURTYPE_BTREE ); - pCrsr = pC->uc.pCursor; - res = 0; - assert( pCrsr!=0 ); -#ifdef SQLITE_DEBUG - pC->seekOp = pOp->opcode; -#endif - if( pOp->opcode==OP_SeekEnd ){ - assert( pOp->p2==0 ); - pC->seekResult = -1; - if( sqlcipher_sqlite3BtreeCursorIsValidNN(pCrsr) ){ - break; - } - } - rc = sqlcipher_sqlite3BtreeLast(pCrsr, &res); - pC->nullRow = (u8)res; - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; - if( rc ) goto abort_due_to_error; - if( pOp->p2>0 ){ - VdbeBranchTaken(res!=0,2); - if( res ) goto jump_to_p2; - } - break; -} +int sqlcipher_codec_ctx_set_fast_kdf_iter(codec_ctx *, int); +int sqlcipher_codec_ctx_get_fast_kdf_iter(codec_ctx *); -/* Opcode: IfSmaller P1 P2 P3 * * -** -** Estimate the number of rows in the table P1. Jump to P2 if that -** estimate is less than approximately 2**(0.1*P3). -*/ -case OP_IfSmaller: { /* jump */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - i64 sz; +const char* sqlcipher_codec_ctx_get_cipher(codec_ctx *ctx); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - pCrsr = pC->uc.pCursor; - assert( pCrsr ); - rc = sqlcipher_sqlite3BtreeFirst(pCrsr, &res); - if( rc ) goto abort_due_to_error; - if( res==0 ){ - sz = sqlcipher_sqlite3BtreeRowCountEst(pCrsr); - if( ALWAYS(sz>=0) && sqlcipher_sqlite3LogEst((u64)sz)p3 ) res = 1; - } - VdbeBranchTaken(res!=0,2); - if( res ) goto jump_to_p2; - break; -} +void* sqlcipher_codec_ctx_get_data(codec_ctx *); +void sqlcipher_set_default_use_hmac(int use); +int sqlcipher_get_default_use_hmac(void); -/* Opcode: SorterSort P1 P2 * * * -** -** After all records have been inserted into the Sorter object -** identified by P1, invoke this opcode to actually do the sorting. -** Jump to P2 if there are no records to be sorted. -** -** This opcode is an alias for OP_Sort and OP_Rewind that is used -** for Sorter objects. -*/ -/* Opcode: Sort P1 P2 * * * -** -** This opcode does exactly the same thing as OP_Rewind except that -** it increments an undocumented global variable used for testing. -** -** Sorting is accomplished by writing records into a sorting index, -** then rewinding that index and playing it back from beginning to -** end. We use the OP_Sort opcode instead of OP_Rewind to do the -** rewinding so that the global variable will be incremented and -** regression tests can determine whether or not the optimizer is -** correctly optimizing out sorts. -*/ -case OP_SorterSort: /* jump */ -case OP_Sort: { /* jump */ -#ifdef SQLITE_TEST - sqlcipher_sqlite3_sort_count++; - sqlcipher_sqlite3_search_count--; -#endif - p->aCounter[SQLITE_STMTSTATUS_SORT]++; - /* Fall through into OP_Rewind */ - /* no break */ deliberate_fall_through -} -/* Opcode: Rewind P1 P2 * * * -** -** The next use of the Rowid or Column or Next instruction for P1 -** will refer to the first entry in the database table or index. -** If the table or index is empty, jump immediately to P2. -** If the table or index is not empty, fall through to the following -** instruction. -** -** This opcode leaves the cursor configured to move in forward order, -** from the beginning toward the end. In other words, the cursor is -** configured to use Next, not Prev. -*/ -case OP_Rewind: { /* jump */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; +void sqlcipher_set_hmac_salt_mask(unsigned char mask); +unsigned char sqlcipher_get_hmac_salt_mask(void); - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p5==0 ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) ); - res = 1; -#ifdef SQLITE_DEBUG - pC->seekOp = OP_Rewind; -#endif - if( isSorter(pC) ){ - rc = sqlcipher_sqlite3VdbeSorterRewind(pC, &res); - }else{ - assert( pC->eCurType==CURTYPE_BTREE ); - pCrsr = pC->uc.pCursor; - assert( pCrsr ); - rc = sqlcipher_sqlite3BtreeFirst(pCrsr, &res); - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; - } - if( rc ) goto abort_due_to_error; - pC->nullRow = (u8)res; - assert( pOp->p2>0 && pOp->p2nOp ); - VdbeBranchTaken(res!=0,2); - if( res ) goto jump_to_p2; - break; -} +int sqlcipher_codec_ctx_set_use_hmac(codec_ctx *ctx, int use); +int sqlcipher_codec_ctx_get_use_hmac(codec_ctx *ctx); -/* Opcode: Next P1 P2 P3 P4 P5 -** -** Advance cursor P1 so that it points to the next key/data pair in its -** table or index. If there are no more key/value pairs then fall through -** to the following instruction. But if the cursor advance was successful, -** jump immediately to P2. -** -** The Next opcode is only valid following an SeekGT, SeekGE, or -** OP_Rewind opcode used to position the cursor. Next is not allowed -** to follow SeekLT, SeekLE, or OP_Last. -** -** The P1 cursor must be for a real table, not a pseudo-table. P1 must have -** been opened prior to this opcode or the program will segfault. -** -** The P3 value is a hint to the btree implementation. If P3==1, that -** means P1 is an SQL index and that this instruction could have been -** omitted if that index had been unique. P3 is usually 0. P3 is -** always either 0 or 1. -** -** P4 is always of type P4_ADVANCE. The function pointer points to -** sqlcipher_sqlite3BtreeNext(). -** -** If P5 is positive and the jump is taken, then event counter -** number P5-1 in the prepared statement is incremented. -** -** See also: Prev -*/ -/* Opcode: Prev P1 P2 P3 P4 P5 -** -** Back up cursor P1 so that it points to the previous key/data pair in its -** table or index. If there is no previous key/value pairs then fall through -** to the following instruction. But if the cursor backup was successful, -** jump immediately to P2. -** -** -** The Prev opcode is only valid following an SeekLT, SeekLE, or -** OP_Last opcode used to position the cursor. Prev is not allowed -** to follow SeekGT, SeekGE, or OP_Rewind. -** -** The P1 cursor must be for a real table, not a pseudo-table. If P1 is -** not open then the behavior is undefined. -** -** The P3 value is a hint to the btree implementation. If P3==1, that -** means P1 is an SQL index and that this instruction could have been -** omitted if that index had been unique. P3 is usually 0. P3 is -** always either 0 or 1. -** -** P4 is always of type P4_ADVANCE. The function pointer points to -** sqlcipher_sqlite3BtreePrevious(). -** -** If P5 is positive and the jump is taken, then event counter -** number P5-1 in the prepared statement is incremented. -*/ -/* Opcode: SorterNext P1 P2 * * P5 -** -** This opcode works just like OP_Next except that P1 must be a -** sorter object for which the OP_SorterSort opcode has been -** invoked. This opcode advances the cursor to the next sorted -** record, or jumps to P2 if there are no more sorted records. -*/ -case OP_SorterNext: { /* jump */ - VdbeCursor *pC; +int sqlcipher_codec_ctx_set_flag(codec_ctx *ctx, unsigned int flag); +int sqlcipher_codec_ctx_unset_flag(codec_ctx *ctx, unsigned int flag); +int sqlcipher_codec_ctx_get_flag(codec_ctx *ctx, unsigned int flag); - pC = p->apCsr[pOp->p1]; - assert( isSorter(pC) ); - rc = sqlcipher_sqlite3VdbeSorterNext(db, pC); - goto next_tail; -case OP_Prev: /* jump */ -case OP_Next: /* jump */ - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p5aCounter) ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->deferredMoveto==0 ); - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlcipher_sqlite3BtreeNext ); - assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlcipher_sqlite3BtreePrevious ); +const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx); +int sqlcipher_codec_ctx_migrate(codec_ctx *ctx); +int sqlcipher_codec_add_random(codec_ctx *ctx, const char *data, int random_sz); +int sqlcipher_cipher_profile(sqlcipher_sqlite3 *db, const char *destination); +int sqlcipher_codec_get_store_pass(codec_ctx *ctx); +void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey); +void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value); +int sqlcipher_codec_fips_status(codec_ctx *ctx); +const char* sqlcipher_codec_get_provider_version(codec_ctx *ctx); - /* The Next opcode is only used after SeekGT, SeekGE, Rewind, and Found. - ** The Prev opcode is only used after SeekLT, SeekLE, and Last. */ - assert( pOp->opcode!=OP_Next - || pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE - || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found - || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid - || pC->seekOp==OP_IfNoHope); - assert( pOp->opcode!=OP_Prev - || pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE - || pC->seekOp==OP_Last || pC->seekOp==OP_IfNoHope - || pC->seekOp==OP_NullRow); +int sqlcipher_set_default_plaintext_header_size(int size); +int sqlcipher_get_default_plaintext_header_size(void); +int sqlcipher_codec_ctx_set_plaintext_header_size(codec_ctx *ctx, int size); +int sqlcipher_codec_ctx_get_plaintext_header_size(codec_ctx *ctx); - rc = pOp->p4.xAdvance(pC->uc.pCursor, pOp->p3); -next_tail: - pC->cacheStatus = CACHE_STALE; - VdbeBranchTaken(rc==SQLITE_OK,2); - if( rc==SQLITE_OK ){ - pC->nullRow = 0; - p->aCounter[pOp->p5]++; -#ifdef SQLITE_TEST - sqlcipher_sqlite3_search_count++; -#endif - goto jump_to_p2_and_check_for_interrupt; - } - if( rc!=SQLITE_DONE ) goto abort_due_to_error; - rc = SQLITE_OK; - pC->nullRow = 1; - goto check_for_interrupt; -} +int sqlcipher_set_default_hmac_algorithm(int algorithm); +int sqlcipher_get_default_hmac_algorithm(void); +int sqlcipher_codec_ctx_set_hmac_algorithm(codec_ctx *ctx, int algorithm); +int sqlcipher_codec_ctx_get_hmac_algorithm(codec_ctx *ctx); -/* Opcode: IdxInsert P1 P2 P3 P4 P5 -** Synopsis: key=r[P2] -** -** Register P2 holds an SQL index key made using the -** MakeRecord instructions. This opcode writes that key -** into the index P1. Data for the entry is nil. -** -** If P4 is not zero, then it is the number of values in the unpacked -** key of reg(P2). In that case, P3 is the index of the first register -** for the unpacked key. The availability of the unpacked key can sometimes -** be an optimization. -** -** If P5 has the OPFLAG_APPEND bit set, that is a hint to the b-tree layer -** that this insert is likely to be an append. -** -** If P5 has the OPFLAG_NCHANGE bit set, then the change counter is -** incremented by this instruction. If the OPFLAG_NCHANGE bit is clear, -** then the change counter is unchanged. -** -** If the OPFLAG_USESEEKRESULT flag of P5 is set, the implementation might -** run faster by avoiding an unnecessary seek on cursor P1. However, -** the OPFLAG_USESEEKRESULT flag must only be set if there have been no prior -** seeks on the cursor or if the most recent seek used a key equivalent -** to P2. -** -** This instruction only works for indices. The equivalent instruction -** for tables is OP_Insert. -*/ -case OP_IdxInsert: { /* in2 */ - VdbeCursor *pC; - BtreePayload x; +int sqlcipher_set_default_kdf_algorithm(int algorithm); +int sqlcipher_get_default_kdf_algorithm(void); +int sqlcipher_codec_ctx_set_kdf_algorithm(codec_ctx *ctx, int algorithm); +int sqlcipher_codec_ctx_get_kdf_algorithm(codec_ctx *ctx); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - sqlcipher_sqlite3VdbeIncrWriteCounter(p, pC); - assert( pC!=0 ); - assert( !isSorter(pC) ); - pIn2 = &aMem[pOp->p2]; - assert( pIn2->flags & MEM_Blob ); - if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->isTable==0 ); - rc = ExpandBlob(pIn2); - if( rc ) goto abort_due_to_error; - x.nKey = pIn2->n; - x.pKey = pIn2->z; - x.aMem = aMem + pOp->p3; - x.nMem = (u16)pOp->p4.i; - rc = sqlcipher_sqlite3BtreeInsert(pC->uc.pCursor, &x, - (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), - ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) - ); - assert( pC->deferredMoveto==0 ); - pC->cacheStatus = CACHE_STALE; - if( rc) goto abort_due_to_error; - break; -} +void sqlcipher_set_mem_security(int); +int sqlcipher_get_mem_security(void); -/* Opcode: SorterInsert P1 P2 * * * -** Synopsis: key=r[P2] -** -** Register P2 holds an SQL index key made using the -** MakeRecord instructions. This opcode writes that key -** into the sorter P1. Data for the entry is nil. -*/ -case OP_SorterInsert: { /* in2 */ - VdbeCursor *pC; +int sqlcipher_find_db_index(sqlcipher_sqlite3 *db, const char *zDb); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - sqlcipher_sqlite3VdbeIncrWriteCounter(p, pC); - assert( pC!=0 ); - assert( isSorter(pC) ); - pIn2 = &aMem[pOp->p2]; - assert( pIn2->flags & MEM_Blob ); - assert( pC->isTable==0 ); - rc = ExpandBlob(pIn2); - if( rc ) goto abort_due_to_error; - rc = sqlcipher_sqlite3VdbeSorterWrite(pC, pIn2); - if( rc) goto abort_due_to_error; - break; -} +int sqlcipher_codec_ctx_integrity_check(codec_ctx *, Parse *, char *); -/* Opcode: IdxDelete P1 P2 P3 * P5 -** Synopsis: key=r[P2@P3] -** -** The content of P3 registers starting at register P2 form -** an unpacked index key. This opcode removes that entry from the -** index opened by cursor P1. -** -** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error -** if no matching index entry is found. This happens when running -** an UPDATE or DELETE statement and the index entry to be updated -** or deleted is not found. For some uses of IdxDelete -** (example: the EXCEPT operator) it does not matter that no matching -** entry is found. For those cases, P5 is zero. -*/ -case OP_IdxDelete: { - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - UnpackedRecord r; +int sqlcipher_set_log(const char *destination); +void sqlcipher_set_log_level(unsigned int level); +void sqlcipher_log(unsigned int tag, const char *message, ...); - assert( pOp->p3>0 ); - assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem+1 - p->nCursor)+1 ); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->eCurType==CURTYPE_BTREE ); - sqlcipher_sqlite3VdbeIncrWriteCounter(p, pC); - pCrsr = pC->uc.pCursor; - assert( pCrsr!=0 ); - r.pKeyInfo = pC->pKeyInfo; - r.nField = (u16)pOp->p3; - r.default_rc = 0; - r.aMem = &aMem[pOp->p2]; - rc = sqlcipher_sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); - if( rc ) goto abort_due_to_error; - if( res==0 ){ - rc = sqlcipher_sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); - if( rc ) goto abort_due_to_error; - }else if( pOp->p5 ){ - rc = SQLITE_CORRUPT_INDEX; - goto abort_due_to_error; +#define SQLCIPHER_LOG_NONE 0x00 +#define SQLCIPHER_LOG_ERROR 0x01 +#define SQLCIPHER_LOG_WARN 0x02 +#define SQLCIPHER_LOG_INFO 0x04 +#define SQLCIPHER_LOG_DEBUG 0x08 +#define SQLCIPHER_LOG_TRACE 0x10 +#define SQLCIPHER_LOG_ALL 0xffffffff + +void sqlcipher_vdbe_return_string(Parse*, const char*, const char*, int); + +#ifdef CODEC_DEBUG_PAGEDATA +#define CODEC_HEXDUMP(DESC,BUFFER,LEN) \ + { \ + int __pctr; \ + printf(DESC); \ + for(__pctr=0; __pctr < LEN; __pctr++) { \ + if(__pctr % 16 == 0) printf("\n%05x: ",__pctr); \ + printf("%02x ",((unsigned char*) BUFFER)[__pctr]); \ + } \ + printf("\n"); \ + fflush(stdout); \ } - assert( pC->deferredMoveto==0 ); - pC->cacheStatus = CACHE_STALE; - pC->seekResult = 0; - break; +#else +#define CODEC_HEXDUMP(DESC,BUFFER,LEN) +#endif + +#endif +#endif +/* END SQLCIPHER */ + +/************** End of crypto.h **********************************************/ +/************** Continuing where we left off in crypto.c *********************/ + +#ifdef SQLCIPHER_EXT +#include "sqlcipher_ext.h" +#endif + +void sqlcipher_vdbe_return_string(Parse *pParse, const char *zLabel, const char *value, int value_type){ + Vdbe *v = sqlcipher_sqlite3GetVdbe(pParse); + sqlcipher_sqlite3VdbeSetNumCols(v, 1); + sqlcipher_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC); + sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, value, value_type); + sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); } -/* Opcode: DeferredSeek P1 * P3 P4 * -** Synopsis: Move P3 to P1.rowid if needed -** -** P1 is an open index cursor and P3 is a cursor on the corresponding -** table. This opcode does a deferred seek of the P3 table cursor -** to the row that corresponds to the current row of P1. -** -** This is a deferred seek. Nothing actually happens until -** the cursor is used to read a record. That way, if no reads -** occur, no unnecessary I/O happens. -** -** P4 may be an array of integers (type P4_INTARRAY) containing -** one entry for each column in the P3 table. If array entry a(i) -** is non-zero, then reading column a(i)-1 from cursor P3 is -** equivalent to performing the deferred seek and then reading column i -** from P1. This information is stored in P3 and used to redirect -** reads against P3 over to P1, thus possibly avoiding the need to -** seek and read cursor P3. -*/ -/* Opcode: IdxRowid P1 P2 * * * -** Synopsis: r[P2]=rowid -** -** Write into register P2 an integer which is the last entry in the record at -** the end of the index key pointed to by cursor P1. This integer should be -** the rowid of the table entry to which this index entry points. -** -** See also: Rowid, MakeRecord. -*/ -case OP_DeferredSeek: -case OP_IdxRowid: { /* out2 */ - VdbeCursor *pC; /* The P1 index cursor */ - VdbeCursor *pTabCur; /* The P2 table cursor (OP_DeferredSeek only) */ - i64 rowid; /* Rowid that P1 current points to */ +static int codec_set_btree_to_codec_pagesize(sqlcipher_sqlite3 *db, Db *pDb, codec_ctx *ctx) { + int rc, page_sz, reserve_sz; - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->uc.pCursor!=0 ); - assert( pC->isTable==0 ); - assert( pC->deferredMoveto==0 ); - assert( !pC->nullRow || pOp->opcode==OP_IdxRowid ); + page_sz = sqlcipher_codec_ctx_get_pagesize(ctx); + reserve_sz = sqlcipher_codec_ctx_get_reservesize(ctx); - /* The IdxRowid and Seek opcodes are combined because of the commonality - ** of sqlcipher_sqlite3VdbeCursorRestore() and sqlcipher_sqlite3VdbeIdxRowid(). */ - rc = sqlcipher_sqlite3VdbeCursorRestore(pC); + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "codec_set_btree_to_codec_pagesize: sqlcipher_sqlite3BtreeSetPageSize() size=%d reserve=%d", page_sz, reserve_sz); - /* sqlcipher_sqlite3VbeCursorRestore() can only fail if the record has been deleted - ** out from under the cursor. That will never happens for an IdxRowid - ** or Seek opcode */ - if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "codec_set_btree_to_codec_pagesize: entering database mutex %p", db->mutex); + sqlcipher_sqlite3_mutex_enter(db->mutex); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "codec_set_btree_to_codec_pagesize: entered database mutex %p", db->mutex); + db->nextPagesize = page_sz; - if( !pC->nullRow ){ - rowid = 0; /* Not needed. Only used to silence a warning. */ - rc = sqlcipher_sqlite3VdbeIdxRowid(db, pC->uc.pCursor, &rowid); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( pOp->opcode==OP_DeferredSeek ){ - assert( pOp->p3>=0 && pOp->p3nCursor ); - pTabCur = p->apCsr[pOp->p3]; - assert( pTabCur!=0 ); - assert( pTabCur->eCurType==CURTYPE_BTREE ); - assert( pTabCur->uc.pCursor!=0 ); - assert( pTabCur->isTable ); - pTabCur->nullRow = 0; - pTabCur->movetoTarget = rowid; - pTabCur->deferredMoveto = 1; - assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 ); - pTabCur->aAltMap = pOp->p4.ai; - pTabCur->pAltCursor = pC; - }else{ - pOut = out2Prerelease(p, pOp); - pOut->u.i = rowid; - } - }else{ - assert( pOp->opcode==OP_IdxRowid ); - sqlcipher_sqlite3VdbeMemSetNull(&aMem[pOp->p2]); - } - break; -} + /* before forcing the page size we need to unset the BTS_PAGESIZE_FIXED flag, else + sqliteBtreeSetPageSize will block the change */ + pDb->pBt->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; + rc = sqlcipher_sqlite3BtreeSetPageSize(pDb->pBt, page_sz, reserve_sz, 0); -/* Opcode: FinishSeek P1 * * * * -** -** If cursor P1 was previously moved via OP_DeferredSeek, complete that -** seek operation now, without further delay. If the cursor seek has -** already occurred, this instruction is a no-op. -*/ -case OP_FinishSeek: { - VdbeCursor *pC; /* The P1 index cursor */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "codec_set_btree_to_codec_pagesize: sqlcipher_sqlite3BtreeSetPageSize returned %d", rc); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - if( pC->deferredMoveto ){ - rc = sqlcipher_sqlite3VdbeFinishMoveto(pC); - if( rc ) goto abort_due_to_error; - } - break; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "codec_set_btree_to_codec_pagesize: leaving database mutex %p", db->mutex); + sqlcipher_sqlite3_mutex_leave(db->mutex); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "codec_set_btree_to_codec_pagesize: left database mutex %p", db->mutex); + + return rc; } -/* Opcode: IdxGE P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] -** -** The P4 register values beginning with P3 form an unpacked index -** key that omits the PRIMARY KEY. Compare this key value against the index -** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID -** fields at the end. -** -** If the P1 index entry is greater than or equal to the key value -** then jump to P2. Otherwise fall through to the next instruction. -*/ -/* Opcode: IdxGT P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] -** -** The P4 register values beginning with P3 form an unpacked index -** key that omits the PRIMARY KEY. Compare this key value against the index -** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID -** fields at the end. -** -** If the P1 index entry is greater than the key value -** then jump to P2. Otherwise fall through to the next instruction. -*/ -/* Opcode: IdxLT P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] -** -** The P4 register values beginning with P3 form an unpacked index -** key that omits the PRIMARY KEY or ROWID. Compare this key value against -** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or -** ROWID on the P1 index. -** -** If the P1 index entry is less than the key value then jump to P2. -** Otherwise fall through to the next instruction. -*/ -/* Opcode: IdxLE P1 P2 P3 P4 * -** Synopsis: key=r[P3@P4] -** -** The P4 register values beginning with P3 form an unpacked index -** key that omits the PRIMARY KEY or ROWID. Compare this key value against -** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or -** ROWID on the P1 index. -** -** If the P1 index entry is less than or equal to the key value then jump -** to P2. Otherwise fall through to the next instruction. -*/ -case OP_IdxLE: /* jump */ -case OP_IdxGT: /* jump */ -case OP_IdxLT: /* jump */ -case OP_IdxGE: { /* jump */ - VdbeCursor *pC; - int res; - UnpackedRecord r; +static int codec_set_pass_key(sqlcipher_sqlite3* db, int nDb, const void *zKey, int nKey, int for_ctx) { + struct Db *pDb = &db->aDb[nDb]; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "codec_set_pass_key: db=%p nDb=%d for_ctx=%d", db, nDb, for_ctx); + if(pDb->pBt) { + codec_ctx *ctx = (codec_ctx*) sqlcipherPagerGetCodec(pDb->pBt->pBt->pPager); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->isOrdered ); - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->uc.pCursor!=0); - assert( pC->deferredMoveto==0 ); - assert( pOp->p4type==P4_INT32 ); - r.pKeyInfo = pC->pKeyInfo; - r.nField = (u16)pOp->p4.i; - if( pOp->opcodeopcode==OP_IdxLE || pOp->opcode==OP_IdxGT ); - r.default_rc = -1; - }else{ - assert( pOp->opcode==OP_IdxGE || pOp->opcode==OP_IdxLT ); - r.default_rc = 0; - } - r.aMem = &aMem[pOp->p3]; -#ifdef SQLITE_DEBUG - { - int i; - for(i=0; ip3+i, &aMem[pOp->p3+i]); + if(ctx) { + return sqlcipher_codec_ctx_set_pass(ctx, zKey, nKey, for_ctx); + } else { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "codec_set_pass_key: error ocurred fetching codec from pager on db %d", nDb); + return SQLITE_ERROR; } } -#endif + sqlcipher_log(SQLCIPHER_LOG_ERROR, "codec_set_pass_key: no btree present on db %d", nDb); + return SQLITE_ERROR; +} - /* Inlined version of sqlcipher_sqlite3VdbeIdxKeyCompare() */ - { - i64 nCellKey = 0; - BtCursor *pCur; - Mem m; +int sqlcipher_codec_pragma(sqlcipher_sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const char *zRight) { + struct Db *pDb = &db->aDb[iDb]; + codec_ctx *ctx = NULL; + int rc; - assert( pC->eCurType==CURTYPE_BTREE ); - pCur = pC->uc.pCursor; - assert( sqlcipher_sqlite3BtreeCursorIsValid(pCur) ); - nCellKey = sqlcipher_sqlite3BtreePayloadSize(pCur); - /* nCellKey will always be between 0 and 0xffffffff because of the way - ** that btreeParseCellPtr() and sqlcipher_sqlite3GetVarint32() are implemented */ - if( nCellKey<=0 || nCellKey>0x7fffffff ){ - rc = SQLITE_CORRUPT_BKPT; - goto abort_due_to_error; - } - sqlcipher_sqlite3VdbeMemInit(&m, db, 0); - rc = sqlcipher_sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m); - if( rc ) goto abort_due_to_error; - res = sqlcipher_sqlite3VdbeRecordCompareWithSkip(m.n, m.z, &r, 0); - sqlcipher_sqlite3VdbeMemRelease(&m); + if(pDb->pBt) { + ctx = (codec_ctx*) sqlcipherPagerGetCodec(pDb->pBt->pBt->pPager); } - /* End of inlined sqlcipher_sqlite3VdbeIdxKeyCompare() */ - assert( (OP_IdxLE&1)==(OP_IdxLT&1) && (OP_IdxGE&1)==(OP_IdxGT&1) ); - if( (pOp->opcode&1)==(OP_IdxLT&1) ){ - assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxLT ); - res = -res; - }else{ - assert( pOp->opcode==OP_IdxGE || pOp->opcode==OP_IdxGT ); - res++; + if(sqlcipher_sqlite3_stricmp(zLeft, "key") !=0 && sqlcipher_sqlite3_stricmp(zLeft, "rekey") != 0) { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_codec_pragma: db=%p iDb=%d pParse=%p zLeft=%s zRight=%s ctx=%p", db, iDb, pParse, zLeft, zRight, ctx); } - VdbeBranchTaken(res>0,2); - assert( rc==SQLITE_OK ); - if( res>0 ) goto jump_to_p2; - break; -} - -/* Opcode: Destroy P1 P2 P3 * * -** -** Delete an entire database table or index whose root page in the database -** file is given by P1. -** -** The table being destroyed is in the main database file if P3==0. If -** P3==1 then the table to be clear is in the auxiliary database file -** that is used to store tables create using CREATE TEMPORARY TABLE. -** -** If AUTOVACUUM is enabled then it is possible that another root page -** might be moved into the newly deleted root page in order to keep all -** root pages contiguous at the beginning of the database. The former -** value of the root page that moved - its value before the move occurred - -** is stored in register P2. If no page movement was required (because the -** table being dropped was already the last one in the database) then a -** zero is stored in register P2. If AUTOVACUUM is disabled then a zero -** is stored in register P2. -** -** This opcode throws an error if there are any active reader VMs when -** it is invoked. This is done to avoid the difficulty associated with -** updating existing cursors when a root page is moved in an AUTOVACUUM -** database. This error is thrown even if the database is not an AUTOVACUUM -** db in order to avoid introducing an incompatibility between autovacuum -** and non-autovacuum modes. -** -** See also: Clear -*/ -case OP_Destroy: { /* out2 */ - int iMoved; - int iDb; - sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); - assert( p->readOnly==0 ); - assert( pOp->p1>1 ); - pOut = out2Prerelease(p, pOp); - pOut->flags = MEM_Null; - if( db->nVdbeRead > db->nVDestroy+1 ){ - rc = SQLITE_LOCKED; - p->errorAction = OE_Abort; - goto abort_due_to_error; - }else{ - iDb = pOp->p3; - assert( DbMaskTest(p->btreeMask, iDb) ); - iMoved = 0; /* Not needed. Only to silence a warning. */ - rc = sqlcipher_sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved); - pOut->flags = MEM_Int; - pOut->u.i = iMoved; - if( rc ) goto abort_due_to_error; -#ifndef SQLITE_OMIT_AUTOVACUUM - if( iMoved!=0 ){ - sqlcipher_sqlite3RootPageMoved(db, iDb, iMoved, pOp->p1); - /* All OP_Destroy operations occur on the same btree */ - assert( resetSchemaOnFault==0 || resetSchemaOnFault==iDb+1 ); - resetSchemaOnFault = iDb+1; +#ifdef SQLCIPHER_EXT + if(sqlcipher_ext_pragma(db, iDb, pParse, zLeft, zRight)) { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_codec_pragma: PRAGMA handled by sqlcipher_ext_pragma"); + } else +#endif +#ifdef SQLCIPHER_TEST + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_test_on")==0 ){ + if( zRight ) { + unsigned int flags = sqlcipher_get_test_flags(); + if(sqlcipher_sqlite3_stricmp(zRight, "fail_encrypt")==0) { + flags |= TEST_FAIL_ENCRYPT; + } else + if(sqlcipher_sqlite3_stricmp(zRight, "fail_decrypt")==0) { + flags |= TEST_FAIL_DECRYPT; + } else + if(sqlcipher_sqlite3_stricmp(zRight, "fail_migrate")==0) { + flags |= TEST_FAIL_MIGRATE; + } + sqlcipher_set_test_flags(flags); + } + } else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_test_off")==0 ){ + if( zRight ) { + unsigned int flags = sqlcipher_get_test_flags(); + if(sqlcipher_sqlite3_stricmp(zRight, "fail_encrypt")==0) { + flags &= ~TEST_FAIL_ENCRYPT; + } else + if(sqlcipher_sqlite3_stricmp(zRight, "fail_decrypt")==0) { + flags &= ~TEST_FAIL_DECRYPT; + } else + if(sqlcipher_sqlite3_stricmp(zRight, "fail_migrate")==0) { + flags &= ~TEST_FAIL_MIGRATE; + } + sqlcipher_set_test_flags(flags); } + } else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_test")==0 ){ + char *flags = sqlcipher_sqlite3_mprintf("%u", sqlcipher_get_test_flags()); + sqlcipher_vdbe_return_string(pParse, "cipher_test", flags, P4_DYNAMIC); + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_test_rand")==0 ){ + if( zRight ) { + int rand = atoi(zRight); + sqlcipher_set_test_rand(rand); + } else { + char *rand = sqlcipher_sqlite3_mprintf("%d", sqlcipher_get_test_rand()); + sqlcipher_vdbe_return_string(pParse, "cipher_test_rand", rand, P4_DYNAMIC); + } + } else #endif + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher_fips_status")== 0 && !zRight ){ + if(ctx) { + char *fips_mode_status = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_fips_status(ctx)); + sqlcipher_vdbe_return_string(pParse, "cipher_fips_status", fips_mode_status, P4_DYNAMIC); + } + } else + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher_store_pass")==0 && zRight ) { + if(ctx) { + char *deprecation = "PRAGMA cipher_store_pass is deprecated, please remove from use"; + sqlcipher_codec_set_store_pass(ctx, sqlcipher_sqlite3GetBoolean(zRight, 1)); + sqlcipher_vdbe_return_string(pParse, "cipher_store_pass", deprecation, P4_TRANSIENT); + sqlcipher_sqlite3_log(SQLITE_WARNING, deprecation); + } + } else + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher_store_pass")==0 && !zRight ) { + if(ctx){ + char *store_pass_value = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_get_store_pass(ctx)); + sqlcipher_vdbe_return_string(pParse, "cipher_store_pass", store_pass_value, P4_DYNAMIC); + } } - break; -} - -/* Opcode: Clear P1 P2 P3 -** -** Delete all contents of the database table or index whose root page -** in the database file is given by P1. But, unlike Destroy, do not -** remove the table or index from the database file. -** -** The table being clear is in the main database file if P2==0. If -** P2==1 then the table to be clear is in the auxiliary database file -** that is used to store tables create using CREATE TEMPORARY TABLE. -** -** If the P3 value is non-zero, then the table referred to must be an -** intkey table (an SQL table, not an index). In this case the row change -** count is incremented by the number of rows in the table being cleared. -** If P3 is greater than zero, then the value stored in register P3 is -** also incremented by the number of rows in the table being cleared. -** -** See also: Destroy -*/ -case OP_Clear: { - int nChange; + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher_profile")== 0 && zRight ){ + char *profile_status = sqlcipher_sqlite3_mprintf("%d", sqlcipher_cipher_profile(db, zRight)); + sqlcipher_vdbe_return_string(pParse, "cipher_profile", profile_status, P4_DYNAMIC); + } else + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher_add_random")==0 && zRight ){ + if(ctx) { + char *add_random_status = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_add_random(ctx, zRight, sqlcipher_sqlite3Strlen30(zRight))); + sqlcipher_vdbe_return_string(pParse, "cipher_add_random", add_random_status, P4_DYNAMIC); + } + } else + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher_migrate")==0 && !zRight ){ + if(ctx){ + int status = sqlcipher_codec_ctx_migrate(ctx); + char *migrate_status = sqlcipher_sqlite3_mprintf("%d", status); + sqlcipher_vdbe_return_string(pParse, "cipher_migrate", migrate_status, P4_DYNAMIC); + if(status != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_pragma: error occurred during cipher_migrate: %d", status); + sqlcipher_codec_ctx_set_error(ctx, status); + } + } + } else + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher_provider")==0 && !zRight ){ + if(ctx) { sqlcipher_vdbe_return_string(pParse, "cipher_provider", + sqlcipher_codec_get_cipher_provider(ctx), P4_TRANSIENT); + } + } else + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher_provider_version")==0 && !zRight){ + if(ctx) { sqlcipher_vdbe_return_string(pParse, "cipher_provider_version", + sqlcipher_codec_get_provider_version(ctx), P4_TRANSIENT); + } + } else + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher_version")==0 && !zRight ){ + sqlcipher_vdbe_return_string(pParse, "cipher_version", sqlcipher_version(), P4_DYNAMIC); + }else + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher")==0 ){ + if(ctx) { + if( zRight ) { + const char* message = "PRAGMA cipher is no longer supported."; + sqlcipher_vdbe_return_string(pParse, "cipher", message, P4_TRANSIENT); + sqlcipher_sqlite3_log(SQLITE_WARNING, message); + }else { + sqlcipher_vdbe_return_string(pParse, "cipher", sqlcipher_codec_ctx_get_cipher(ctx), P4_TRANSIENT); + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft, "rekey_cipher")==0 && zRight ){ + const char* message = "PRAGMA rekey_cipher is no longer supported."; + sqlcipher_vdbe_return_string(pParse, "rekey_cipher", message, P4_TRANSIENT); + sqlcipher_sqlite3_log(SQLITE_WARNING, message); + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_default_kdf_iter")==0 ){ + if( zRight ) { + sqlcipher_set_default_kdf_iter(atoi(zRight)); /* change default KDF iterations */ + } else { + char *kdf_iter = sqlcipher_sqlite3_mprintf("%d", sqlcipher_get_default_kdf_iter()); + sqlcipher_vdbe_return_string(pParse, "cipher_default_kdf_iter", kdf_iter, P4_DYNAMIC); + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft, "kdf_iter")==0 ){ + if(ctx) { + if( zRight ) { + sqlcipher_codec_ctx_set_kdf_iter(ctx, atoi(zRight)); /* change of RW PBKDF2 iteration */ + } else { + char *kdf_iter = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_kdf_iter(ctx)); + sqlcipher_vdbe_return_string(pParse, "kdf_iter", kdf_iter, P4_DYNAMIC); + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft, "fast_kdf_iter")==0){ + if(ctx) { + if( zRight ) { + char *deprecation = "PRAGMA fast_kdf_iter is deprecated, please remove from use"; + sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, atoi(zRight)); /* change of RW PBKDF2 iteration */ + sqlcipher_vdbe_return_string(pParse, "fast_kdf_iter", deprecation, P4_TRANSIENT); + sqlcipher_sqlite3_log(SQLITE_WARNING, deprecation); + } else { + char *fast_kdf_iter = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_fast_kdf_iter(ctx)); + sqlcipher_vdbe_return_string(pParse, "fast_kdf_iter", fast_kdf_iter, P4_DYNAMIC); + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft, "rekey_kdf_iter")==0 && zRight ){ + const char* message = "PRAGMA rekey_kdf_iter is no longer supported."; + sqlcipher_vdbe_return_string(pParse, "rekey_kdf_iter", message, P4_TRANSIENT); + sqlcipher_sqlite3_log(SQLITE_WARNING, message); + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_page_size")==0 ){ + if(ctx) { + if( zRight ) { + int size = atoi(zRight); + rc = sqlcipher_codec_ctx_set_pagesize(ctx, size); + if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); + rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); + if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); + } else { + char * page_size = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_pagesize(ctx)); + sqlcipher_vdbe_return_string(pParse, "cipher_page_size", page_size, P4_DYNAMIC); + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_default_page_size")==0 ){ + if( zRight ) { + sqlcipher_set_default_pagesize(atoi(zRight)); + } else { + char *default_page_size = sqlcipher_sqlite3_mprintf("%d", sqlcipher_get_default_pagesize()); + sqlcipher_vdbe_return_string(pParse, "cipher_default_page_size", default_page_size, P4_DYNAMIC); + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_default_use_hmac")==0 ){ + if( zRight ) { + sqlcipher_set_default_use_hmac(sqlcipher_sqlite3GetBoolean(zRight,1)); + } else { + char *default_use_hmac = sqlcipher_sqlite3_mprintf("%d", sqlcipher_get_default_use_hmac()); + sqlcipher_vdbe_return_string(pParse, "cipher_default_use_hmac", default_use_hmac, P4_DYNAMIC); + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_use_hmac")==0 ){ + if(ctx) { + if( zRight ) { + rc = sqlcipher_codec_ctx_set_use_hmac(ctx, sqlcipher_sqlite3GetBoolean(zRight,1)); + if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); + /* since the use of hmac has changed, the page size may also change */ + rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); + if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); + } else { + char *hmac_flag = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_use_hmac(ctx)); + sqlcipher_vdbe_return_string(pParse, "cipher_use_hmac", hmac_flag, P4_DYNAMIC); + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_hmac_pgno")==0 ){ + if(ctx) { + if(zRight) { + char *deprecation = "PRAGMA cipher_hmac_pgno is deprecated, please remove from use"; + /* clear both pgno endian flags */ + if(sqlcipher_sqlite3_stricmp(zRight, "le") == 0) { + sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO); + sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_LE_PGNO); + } else if(sqlcipher_sqlite3_stricmp(zRight, "be") == 0) { + sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO); + sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_BE_PGNO); + } else if(sqlcipher_sqlite3_stricmp(zRight, "native") == 0) { + sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO); + sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO); + } + sqlcipher_vdbe_return_string(pParse, "cipher_hmac_pgno", deprecation, P4_TRANSIENT); + sqlcipher_sqlite3_log(SQLITE_WARNING, deprecation); - sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); - nChange = 0; - assert( p->readOnly==0 ); - assert( DbMaskTest(p->btreeMask, pOp->p2) ); - rc = sqlcipher_sqlite3BtreeClearTable( - db->aDb[pOp->p2].pBt, (u32)pOp->p1, (pOp->p3 ? &nChange : 0) - ); - if( pOp->p3 ){ - p->nChange += nChange; - if( pOp->p3>0 ){ - assert( memIsValid(&aMem[pOp->p3]) ); - memAboutToChange(p, &aMem[pOp->p3]); - aMem[pOp->p3].u.i += nChange; + } else { + if(sqlcipher_codec_ctx_get_flag(ctx, CIPHER_FLAG_LE_PGNO)) { + sqlcipher_vdbe_return_string(pParse, "cipher_hmac_pgno", "le", P4_TRANSIENT); + } else if(sqlcipher_codec_ctx_get_flag(ctx, CIPHER_FLAG_BE_PGNO)) { + sqlcipher_vdbe_return_string(pParse, "cipher_hmac_pgno", "be", P4_TRANSIENT); + } else { + sqlcipher_vdbe_return_string(pParse, "cipher_hmac_pgno", "native", P4_TRANSIENT); + } + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_hmac_salt_mask")==0 ){ + if(ctx) { + if(zRight) { + char *deprecation = "PRAGMA cipher_hmac_salt_mask is deprecated, please remove from use"; + if (sqlcipher_sqlite3StrNICmp(zRight ,"x'", 2) == 0 && sqlcipher_sqlite3Strlen30(zRight) == 5) { + unsigned char mask = 0; + const unsigned char *hex = (const unsigned char *)zRight+2; + cipher_hex2bin(hex,2,&mask); + sqlcipher_set_hmac_salt_mask(mask); + } + sqlcipher_vdbe_return_string(pParse, "cipher_hmac_salt_mask", deprecation, P4_TRANSIENT); + sqlcipher_sqlite3_log(SQLITE_WARNING, deprecation); + } else { + char *hmac_salt_mask = sqlcipher_sqlite3_mprintf("%02x", sqlcipher_get_hmac_salt_mask()); + sqlcipher_vdbe_return_string(pParse, "cipher_hmac_salt_mask", hmac_salt_mask, P4_DYNAMIC); + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_plaintext_header_size")==0 ){ + if(ctx) { + if( zRight ) { + int size = atoi(zRight); + /* deliberately ignore result code, if size is invalid it will be set to -1 + and trip the error later in the codec */ + sqlcipher_codec_ctx_set_plaintext_header_size(ctx, size); + } else { + char *size = sqlcipher_sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_plaintext_header_size(ctx)); + sqlcipher_vdbe_return_string(pParse, "cipher_plaintext_header_size", size, P4_DYNAMIC); + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_default_plaintext_header_size")==0 ){ + if( zRight ) { + sqlcipher_set_default_plaintext_header_size(atoi(zRight)); + } else { + char *size = sqlcipher_sqlite3_mprintf("%d", sqlcipher_get_default_plaintext_header_size()); + sqlcipher_vdbe_return_string(pParse, "cipher_default_plaintext_header_size", size, P4_DYNAMIC); + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_salt")==0 ){ + if(ctx) { + if(zRight) { + if (sqlcipher_sqlite3StrNICmp(zRight ,"x'", 2) == 0 && sqlcipher_sqlite3Strlen30(zRight) == (FILE_HEADER_SZ*2)+3) { + unsigned char *salt = (unsigned char*) sqlcipher_sqlite3_malloc(FILE_HEADER_SZ); + const unsigned char *hex = (const unsigned char *)zRight+2; + cipher_hex2bin(hex,FILE_HEADER_SZ*2,salt); + sqlcipher_codec_ctx_set_kdf_salt(ctx, salt, FILE_HEADER_SZ); + sqlcipher_sqlite3_free(salt); + } + } else { + void *salt; + char *hexsalt = (char*) sqlcipher_sqlite3_malloc((FILE_HEADER_SZ*2)+1); + if((rc = sqlcipher_codec_ctx_get_kdf_salt(ctx, &salt)) == SQLITE_OK) { + cipher_bin2hex(salt, FILE_HEADER_SZ, hexsalt); + sqlcipher_vdbe_return_string(pParse, "cipher_salt", hexsalt, P4_DYNAMIC); + } else { + sqlcipher_sqlite3_free(hexsalt); + sqlcipher_codec_ctx_set_error(ctx, rc); + } + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_hmac_algorithm")==0 ){ + if(ctx) { + if(zRight) { + rc = SQLITE_ERROR; + if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_HMAC_SHA1_LABEL) == 0) { + rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA1); + } else if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_HMAC_SHA256_LABEL) == 0) { + rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA256); + } else if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_HMAC_SHA512_LABEL) == 0) { + rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA512); + } + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + } else { + int algorithm = sqlcipher_codec_ctx_get_hmac_algorithm(ctx); + if(algorithm == SQLCIPHER_HMAC_SHA1) { + sqlcipher_vdbe_return_string(pParse, "cipher_hmac_algorithm", SQLCIPHER_HMAC_SHA1_LABEL, P4_TRANSIENT); + } else if(algorithm == SQLCIPHER_HMAC_SHA256) { + sqlcipher_vdbe_return_string(pParse, "cipher_hmac_algorithm", SQLCIPHER_HMAC_SHA256_LABEL, P4_TRANSIENT); + } else if(algorithm == SQLCIPHER_HMAC_SHA512) { + sqlcipher_vdbe_return_string(pParse, "cipher_hmac_algorithm", SQLCIPHER_HMAC_SHA512_LABEL, P4_TRANSIENT); + } + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_default_hmac_algorithm")==0 ){ + if(zRight) { + rc = SQLITE_ERROR; + if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_HMAC_SHA1_LABEL) == 0) { + rc = sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA1); + } else if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_HMAC_SHA256_LABEL) == 0) { + rc = sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA256); + } else if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_HMAC_SHA512_LABEL) == 0) { + rc = sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA512); + } + } else { + int algorithm = sqlcipher_get_default_hmac_algorithm(); + if(algorithm == SQLCIPHER_HMAC_SHA1) { + sqlcipher_vdbe_return_string(pParse, "cipher_default_hmac_algorithm", SQLCIPHER_HMAC_SHA1_LABEL, P4_TRANSIENT); + } else if(algorithm == SQLCIPHER_HMAC_SHA256) { + sqlcipher_vdbe_return_string(pParse, "cipher_default_hmac_algorithm", SQLCIPHER_HMAC_SHA256_LABEL, P4_TRANSIENT); + } else if(algorithm == SQLCIPHER_HMAC_SHA512) { + sqlcipher_vdbe_return_string(pParse, "cipher_default_hmac_algorithm", SQLCIPHER_HMAC_SHA512_LABEL, P4_TRANSIENT); + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_kdf_algorithm")==0 ){ + if(ctx) { + if(zRight) { + rc = SQLITE_ERROR; + if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL) == 0) { + rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA1); + } else if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL) == 0) { + rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA256); + } else if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL) == 0) { + rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA512); + } + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + } else { + int algorithm = sqlcipher_codec_ctx_get_kdf_algorithm(ctx); + if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA1) { + sqlcipher_vdbe_return_string(pParse, "cipher_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL, P4_TRANSIENT); + } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA256) { + sqlcipher_vdbe_return_string(pParse, "cipher_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL, P4_TRANSIENT); + } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA512) { + sqlcipher_vdbe_return_string(pParse, "cipher_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL, P4_TRANSIENT); + } + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_default_kdf_algorithm")==0 ){ + if(zRight) { + rc = SQLITE_ERROR; + if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL) == 0) { + rc = sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA1); + } else if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL) == 0) { + rc = sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA256); + } else if(sqlcipher_sqlite3_stricmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL) == 0) { + rc = sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA512); + } + } else { + int algorithm = sqlcipher_get_default_kdf_algorithm(); + if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA1) { + sqlcipher_vdbe_return_string(pParse, "cipher_default_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL, P4_TRANSIENT); + } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA256) { + sqlcipher_vdbe_return_string(pParse, "cipher_default_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL, P4_TRANSIENT); + } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA512) { + sqlcipher_vdbe_return_string(pParse, "cipher_default_kdf_algorithm", SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL, P4_TRANSIENT); + } } - } - if( rc ) goto abort_due_to_error; - break; -} - -/* Opcode: ResetSorter P1 * * * * -** -** Delete all contents from the ephemeral table or sorter -** that is open on cursor P1. -** -** This opcode only works for cursors used for sorting and -** opened with OP_OpenEphemeral or OP_SorterOpen. -*/ -case OP_ResetSorter: { - VdbeCursor *pC; + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_compatibility")==0 ){ + if(ctx) { + if(zRight) { + int version = atoi(zRight); - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - if( isSorter(pC) ){ - sqlcipher_sqlite3VdbeSorterReset(db, pC->uc.pSorter); - }else{ - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->isEphemeral ); - rc = sqlcipher_sqlite3BtreeClearTableOfCursor(pC->uc.pCursor); - if( rc ) goto abort_due_to_error; - } - break; -} + switch(version) { + case 1: + rc = sqlcipher_codec_ctx_set_pagesize(ctx, 1024); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 4000); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 0); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + break; -/* Opcode: CreateBtree P1 P2 P3 * * -** Synopsis: r[P2]=root iDb=P1 flags=P3 -** -** Allocate a new b-tree in the main database file if P1==0 or in the -** TEMP database file if P1==1 or in an attached database if -** P1>1. The P3 argument must be 1 (BTREE_INTKEY) for a rowid table -** it must be 2 (BTREE_BLOBKEY) for an index or WITHOUT ROWID table. -** The root page number of the new b-tree is stored in register P2. -*/ -case OP_CreateBtree: { /* out2 */ - Pgno pgno; - Db *pDb; + case 2: + rc = sqlcipher_codec_ctx_set_pagesize(ctx, 1024); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 4000); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + break; - sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); - pOut = out2Prerelease(p, pOp); - pgno = 0; - assert( pOp->p3==BTREE_INTKEY || pOp->p3==BTREE_BLOBKEY ); - assert( pOp->p1>=0 && pOp->p1nDb ); - assert( DbMaskTest(p->btreeMask, pOp->p1) ); - assert( p->readOnly==0 ); - pDb = &db->aDb[pOp->p1]; - assert( pDb->pBt!=0 ); - rc = sqlcipher_sqlite3BtreeCreateTable(pDb->pBt, &pgno, pOp->p3); - if( rc ) goto abort_due_to_error; - pOut->u.i = pgno; - break; -} + case 3: + rc = sqlcipher_codec_ctx_set_pagesize(ctx, 1024); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 64000); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + break; -/* Opcode: SqlExec * * * P4 * -** -** Run the SQL statement or statements specified in the P4 string. -*/ -case OP_SqlExec: { - sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); - db->nSqlExec++; - rc = sqlcipher_sqlite3_exec(db, pOp->p4.z, 0, 0, 0); - db->nSqlExec--; - if( rc ) goto abort_due_to_error; - break; -} + default: + rc = sqlcipher_codec_ctx_set_pagesize(ctx, 4096); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA512); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA512); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 256000); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + break; + } -/* Opcode: ParseSchema P1 * * P4 * -** -** Read and parse all entries from the schema table of database P1 -** that match the WHERE clause P4. If P4 is a NULL pointer, then the -** entire schema for P1 is reparsed. -** -** This opcode invokes the parser to create a new virtual machine, -** then runs the new virtual machine. It is thus a re-entrant opcode. -*/ -case OP_ParseSchema: { - int iDb; - const char *zSchema; - char *zSql; - InitData initData; + rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + } + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_default_compatibility")==0 ){ + if(zRight) { + int version = atoi(zRight); + switch(version) { + case 1: + sqlcipher_set_default_pagesize(1024); + sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA1); + sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA1); + sqlcipher_set_default_kdf_iter(4000); + sqlcipher_set_default_use_hmac(0); + break; - /* Any prepared statement that invokes this opcode will hold mutexes - ** on every btree. This is a prerequisite for invoking - ** sqlcipher_sqlite3InitCallback(). - */ -#ifdef SQLITE_DEBUG - for(iDb=0; iDbnDb; iDb++){ - assert( iDb==1 || sqlcipher_sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); - } -#endif + case 2: + sqlcipher_set_default_pagesize(1024); + sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA1); + sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA1); + sqlcipher_set_default_kdf_iter(4000); + sqlcipher_set_default_use_hmac(1); + break; - iDb = pOp->p1; - assert( iDb>=0 && iDbnDb ); - assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); + case 3: + sqlcipher_set_default_pagesize(1024); + sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA1); + sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA1); + sqlcipher_set_default_kdf_iter(64000); + sqlcipher_set_default_use_hmac(1); + break; -#ifndef SQLITE_OMIT_ALTERTABLE - if( pOp->p4.z==0 ){ - sqlcipher_sqlite3SchemaClear(db->aDb[iDb].pSchema); - db->mDbFlags &= ~DBFLAG_SchemaKnownOk; - rc = sqlcipher_sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable); - db->mDbFlags |= DBFLAG_SchemaChange; - p->expired = 0; - }else -#endif - { - zSchema = DFLT_SCHEMA_TABLE; - initData.db = db; - initData.iDb = iDb; - initData.pzErrMsg = &p->zErrMsg; - initData.mInitFlags = 0; - initData.mxPage = sqlcipher_sqlite3BtreeLastPage(db->aDb[iDb].pBt); - zSql = sqlcipher_sqlite3MPrintf(db, - "SELECT*FROM\"%w\".%s WHERE %s ORDER BY rowid", - db->aDb[iDb].zDbSName, zSchema, pOp->p4.z); - if( zSql==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - assert( db->init.busy==0 ); - db->init.busy = 1; - initData.rc = SQLITE_OK; - initData.nInitRow = 0; - assert( !db->mallocFailed ); - rc = sqlcipher_sqlite3_exec(db, zSql, sqlcipher_sqlite3InitCallback, &initData, 0); - if( rc==SQLITE_OK ) rc = initData.rc; - if( rc==SQLITE_OK && initData.nInitRow==0 ){ - /* The OP_ParseSchema opcode with a non-NULL P4 argument should parse - ** at least one SQL statement. Any less than that indicates that - ** the sqlite_schema table is corrupt. */ - rc = SQLITE_CORRUPT_BKPT; + default: + sqlcipher_set_default_pagesize(4096); + sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA512); + sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA512); + sqlcipher_set_default_kdf_iter(256000); + sqlcipher_set_default_use_hmac(1); + break; } - sqlcipher_sqlite3DbFreeNN(db, zSql); - db->init.busy = 0; } - } - if( rc ){ - sqlcipher_sqlite3ResetAllSchemasOfConnection(db); - if( rc==SQLITE_NOMEM ){ - goto no_mem; + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_memory_security")==0 ){ + if( zRight ) { + sqlcipher_set_mem_security(sqlcipher_sqlite3GetBoolean(zRight,1)); + } else { + char *on = sqlcipher_sqlite3_mprintf("%d", sqlcipher_get_mem_security()); + sqlcipher_vdbe_return_string(pParse, "cipher_memory_security", on, P4_DYNAMIC); } - goto abort_due_to_error; - } - break; -} + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_settings")==0 ){ + if(ctx) { + int algorithm; + char *pragma; -#if !defined(SQLITE_OMIT_ANALYZE) -/* Opcode: LoadAnalysis P1 * * * * -** -** Read the sqlite_stat1 table for database P1 and load the content -** of that table into the internal index hash table. This will cause -** the analysis to be used when preparing all subsequent queries. -*/ -case OP_LoadAnalysis: { - assert( pOp->p1>=0 && pOp->p1nDb ); - rc = sqlcipher_sqlite3AnalysisLoad(db, pOp->p1); - if( rc ) goto abort_due_to_error; - break; -} -#endif /* !defined(SQLITE_OMIT_ANALYZE) */ + pragma = sqlcipher_sqlite3_mprintf("PRAGMA kdf_iter = %d;", sqlcipher_codec_ctx_get_kdf_iter(ctx)); + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); -/* Opcode: DropTable P1 * * P4 * -** -** Remove the internal (in-memory) data structures that describe -** the table named P4 in database P1. This is called after a table -** is dropped from disk (using the Destroy opcode) in order to keep -** the internal representation of the -** schema consistent with what is on disk. -*/ -case OP_DropTable: { - sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); - sqlcipher_sqlite3UnlinkAndDeleteTable(db, pOp->p1, pOp->p4.z); - break; -} + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_page_size = %d;", sqlcipher_codec_ctx_get_pagesize(ctx)); + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); -/* Opcode: DropIndex P1 * * P4 * -** -** Remove the internal (in-memory) data structures that describe -** the index named P4 in database P1. This is called after an index -** is dropped from disk (using the Destroy opcode) -** in order to keep the internal representation of the -** schema consistent with what is on disk. -*/ -case OP_DropIndex: { - sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); - sqlcipher_sqlite3UnlinkAndDeleteIndex(db, pOp->p1, pOp->p4.z); - break; -} + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_use_hmac = %d;", sqlcipher_codec_ctx_get_use_hmac(ctx)); + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); -/* Opcode: DropTrigger P1 * * P4 * -** -** Remove the internal (in-memory) data structures that describe -** the trigger named P4 in database P1. This is called after a trigger -** is dropped from disk (using the Destroy opcode) in order to keep -** the internal representation of the -** schema consistent with what is on disk. -*/ -case OP_DropTrigger: { - sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); - sqlcipher_sqlite3UnlinkAndDeleteTrigger(db, pOp->p1, pOp->p4.z); - break; -} + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_plaintext_header_size = %d;", sqlcipher_codec_ctx_get_plaintext_header_size(ctx)); + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); + algorithm = sqlcipher_codec_ctx_get_hmac_algorithm(ctx); + pragma = NULL; + if(algorithm == SQLCIPHER_HMAC_SHA1) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA1_LABEL); + } else if(algorithm == SQLCIPHER_HMAC_SHA256) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA256_LABEL); + } else if(algorithm == SQLCIPHER_HMAC_SHA512) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA512_LABEL); + } + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); -#ifndef SQLITE_OMIT_INTEGRITY_CHECK -/* Opcode: IntegrityCk P1 P2 P3 P4 P5 -** -** Do an analysis of the currently open database. Store in -** register P1 the text of an error message describing any problems. -** If no problems are found, store a NULL in register P1. -** -** The register P3 contains one less than the maximum number of allowed errors. -** At most reg(P3) errors will be reported. -** In other words, the analysis stops as soon as reg(P1) errors are -** seen. Reg(P1) is updated with the number of errors remaining. -** -** The root page numbers of all tables in the database are integers -** stored in P4_INTARRAY argument. -** -** If P5 is not zero, the check is done on the auxiliary database -** file, not the main database file. -** -** This opcode is used to implement the integrity_check pragma. -*/ -case OP_IntegrityCk: { - int nRoot; /* Number of tables to check. (Number of root pages.) */ - Pgno *aRoot; /* Array of rootpage numbers for tables to be checked */ - int nErr; /* Number of errors reported */ - char *z; /* Text of the error report */ - Mem *pnErr; /* Register keeping track of errors remaining */ + algorithm = sqlcipher_codec_ctx_get_kdf_algorithm(ctx); + pragma = NULL; + if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA1) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL); + } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA256) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL); + } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA512) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL); + } + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - assert( p->bIsReader ); - nRoot = pOp->p2; - aRoot = pOp->p4.ai; - assert( nRoot>0 ); - assert( aRoot[0]==(Pgno)nRoot ); - assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); - pnErr = &aMem[pOp->p3]; - assert( (pnErr->flags & MEM_Int)!=0 ); - assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); - pIn1 = &aMem[pOp->p1]; - assert( pOp->p5nDb ); - assert( DbMaskTest(p->btreeMask, pOp->p5) ); - z = sqlcipher_sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot, - (int)pnErr->u.i+1, &nErr); - sqlcipher_sqlite3VdbeMemSetNull(pIn1); - if( nErr==0 ){ - assert( z==0 ); - }else if( z==0 ){ - goto no_mem; - }else{ - pnErr->u.i -= nErr-1; - sqlcipher_sqlite3VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, sqlcipher_sqlite3_free); - } - UPDATE_MAX_BLOBSIZE(pIn1); - sqlcipher_sqlite3VdbeChangeEncoding(pIn1, encoding); - goto check_for_interrupt; -} -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ + } + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_default_settings")==0 ){ + int algorithm; + char *pragma; -/* Opcode: RowSetAdd P1 P2 * * * -** Synopsis: rowset(P1)=r[P2] -** -** Insert the integer value held by register P2 into a RowSet object -** held in register P1. -** -** An assertion fails if P2 is not an integer. -*/ -case OP_RowSetAdd: { /* in1, in2 */ - pIn1 = &aMem[pOp->p1]; - pIn2 = &aMem[pOp->p2]; - assert( (pIn2->flags & MEM_Int)!=0 ); - if( (pIn1->flags & MEM_Blob)==0 ){ - if( sqlcipher_sqlite3VdbeMemSetRowSet(pIn1) ) goto no_mem; - } - assert( sqlcipher_sqlite3VdbeMemIsRowSet(pIn1) ); - sqlcipher_sqlite3RowSetInsert((RowSet*)pIn1->z, pIn2->u.i); - break; -} + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_kdf_iter = %d;", sqlcipher_get_default_kdf_iter()); + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); -/* Opcode: RowSetRead P1 P2 P3 * * -** Synopsis: r[P3]=rowset(P1) -** -** Extract the smallest value from the RowSet object in P1 -** and put that value into register P3. -** Or, if RowSet object P1 is initially empty, leave P3 -** unchanged and jump to instruction P2. -*/ -case OP_RowSetRead: { /* jump, in1, out3 */ - i64 val; + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_page_size = %d;", sqlcipher_get_default_pagesize()); + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - pIn1 = &aMem[pOp->p1]; - assert( (pIn1->flags & MEM_Blob)==0 || sqlcipher_sqlite3VdbeMemIsRowSet(pIn1) ); - if( (pIn1->flags & MEM_Blob)==0 - || sqlcipher_sqlite3RowSetNext((RowSet*)pIn1->z, &val)==0 - ){ - /* The boolean index is empty */ - sqlcipher_sqlite3VdbeMemSetNull(pIn1); - VdbeBranchTaken(1,2); - goto jump_to_p2_and_check_for_interrupt; - }else{ - /* A value was pulled from the index */ - VdbeBranchTaken(0,2); - sqlcipher_sqlite3VdbeMemSetInt64(&aMem[pOp->p3], val); - } - goto check_for_interrupt; -} + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_use_hmac = %d;", sqlcipher_get_default_use_hmac()); + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); -/* Opcode: RowSetTest P1 P2 P3 P4 -** Synopsis: if r[P3] in rowset(P1) goto P2 -** -** Register P3 is assumed to hold a 64-bit integer value. If register P1 -** contains a RowSet object and that RowSet object contains -** the value held in P3, jump to register P2. Otherwise, insert the -** integer in P3 into the RowSet and continue on to the -** next opcode. -** -** The RowSet object is optimized for the case where sets of integers -** are inserted in distinct phases, which each set contains no duplicates. -** Each set is identified by a unique P4 value. The first set -** must have P4==0, the final set must have P4==-1, and for all other sets -** must have P4>0. -** -** This allows optimizations: (a) when P4==0 there is no need to test -** the RowSet object for P3, as it is guaranteed not to contain it, -** (b) when P4==-1 there is no need to insert the value, as it will -** never be tested for, and (c) when a value that is part of set X is -** inserted, there is no need to search to see if the same value was -** previously inserted as part of set X (only if it was previously -** inserted as part of some other set). -*/ -case OP_RowSetTest: { /* jump, in1, in3 */ - int iSet; - int exists; + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_plaintext_header_size = %d;", sqlcipher_get_default_plaintext_header_size()); + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - pIn1 = &aMem[pOp->p1]; - pIn3 = &aMem[pOp->p3]; - iSet = pOp->p4.i; - assert( pIn3->flags&MEM_Int ); + algorithm = sqlcipher_get_default_hmac_algorithm(); + pragma = NULL; + if(algorithm == SQLCIPHER_HMAC_SHA1) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA1_LABEL); + } else if(algorithm == SQLCIPHER_HMAC_SHA256) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA256_LABEL); + } else if(algorithm == SQLCIPHER_HMAC_SHA512) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_hmac_algorithm = %s;", SQLCIPHER_HMAC_SHA512_LABEL); + } + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); - /* If there is anything other than a rowset object in memory cell P1, - ** delete it now and initialize P1 with an empty rowset - */ - if( (pIn1->flags & MEM_Blob)==0 ){ - if( sqlcipher_sqlite3VdbeMemSetRowSet(pIn1) ) goto no_mem; - } - assert( sqlcipher_sqlite3VdbeMemIsRowSet(pIn1) ); - assert( pOp->p4type==P4_INT32 ); - assert( iSet==-1 || iSet>=0 ); - if( iSet ){ - exists = sqlcipher_sqlite3RowSetTest((RowSet*)pIn1->z, iSet, pIn3->u.i); - VdbeBranchTaken(exists!=0,2); - if( exists ) goto jump_to_p2; - } - if( iSet>=0 ){ - sqlcipher_sqlite3RowSetInsert((RowSet*)pIn1->z, pIn3->u.i); + algorithm = sqlcipher_get_default_kdf_algorithm(); + pragma = NULL; + if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA1) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA1_LABEL); + } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA256) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA256_LABEL); + } else if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA512) { + pragma = sqlcipher_sqlite3_mprintf("PRAGMA cipher_default_kdf_algorithm = %s;", SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL); + } + sqlcipher_vdbe_return_string(pParse, "pragma", pragma, P4_DYNAMIC); + }else + if( sqlcipher_sqlite3_stricmp(zLeft,"cipher_integrity_check")==0 ){ + if(ctx) { + sqlcipher_codec_ctx_integrity_check(ctx, pParse, "cipher_integrity_check"); + } + } else + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher_log_level")==0 && zRight){ + unsigned int level = SQLCIPHER_LOG_NONE; + if(sqlcipher_sqlite3_stricmp(zRight, "ERROR")==0) level = SQLCIPHER_LOG_ERROR; + else if(sqlcipher_sqlite3_stricmp(zRight, "WARN" )==0) level = SQLCIPHER_LOG_WARN; + else if(sqlcipher_sqlite3_stricmp(zRight, "INFO" )==0) level = SQLCIPHER_LOG_INFO; + else if(sqlcipher_sqlite3_stricmp(zRight, "DEBUG")==0) level = SQLCIPHER_LOG_DEBUG; + else if(sqlcipher_sqlite3_stricmp(zRight, "TRACE")==0) level = SQLCIPHER_LOG_TRACE; + sqlcipher_set_log_level(level); + sqlcipher_vdbe_return_string(pParse, "cipher_log_level", sqlcipher_sqlite3_mprintf("%u", level), P4_DYNAMIC); + } else + if( sqlcipher_sqlite3_stricmp(zLeft, "cipher_log")== 0 && zRight ){ + char *status = sqlcipher_sqlite3_mprintf("%d", sqlcipher_set_log(zRight)); + sqlcipher_vdbe_return_string(pParse, "cipher_log", status, P4_DYNAMIC); + }else { + return 0; } - break; + return 1; } +/* these constants are used internally within SQLite's pager.c to differentiate between + operations on the main database or journal pages. This is important in the context + of a rekey operations, where the journal must be written using the original key + material (to allow a transactional rollback), while the new database pages are being + written with the new key material*/ +#define CODEC_READ_OP 3 +#define CODEC_WRITE_OP 6 +#define CODEC_JOURNAL_OP 7 -#ifndef SQLITE_OMIT_TRIGGER +/* + * sqlcipher_sqlite3Codec can be called in multiple modes. + * encrypt mode - expected to return a pointer to the + * encrypted data without altering pData. + * decrypt mode - expected to return a pointer to pData, with + * the data decrypted in the input buffer + */ +static void* sqlcipher_sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) { + codec_ctx *ctx = (codec_ctx *) iCtx; + int offset = 0, rc = 0; + int page_sz = sqlcipher_codec_ctx_get_pagesize(ctx); + unsigned char *pData = (unsigned char *) data; + void *buffer = sqlcipher_codec_ctx_get_data(ctx); + int plaintext_header_sz = sqlcipher_codec_ctx_get_plaintext_header_size(ctx); + int cctx = CIPHER_READ_CTX; -/* Opcode: Program P1 P2 P3 P4 P5 -** -** Execute the trigger program passed as P4 (type P4_SUBPROGRAM). -** -** P1 contains the address of the memory cell that contains the first memory -** cell in an array of values used as arguments to the sub-program. P2 -** contains the address to jump to if the sub-program throws an IGNORE -** exception using the RAISE() function. Register P3 contains the address -** of a memory cell in this (the parent) VM that is used to allocate the -** memory required by the sub-vdbe at runtime. -** -** P4 is a pointer to the VM containing the trigger program. -** -** If P5 is non-zero, then recursive program invocation is enabled. -*/ -case OP_Program: { /* jump */ - int nMem; /* Number of memory registers for sub-program */ - int nByte; /* Bytes of runtime space required for sub-program */ - Mem *pRt; /* Register to allocate runtime space */ - Mem *pMem; /* Used to iterate through memory cells */ - Mem *pEnd; /* Last memory cell in new array */ - VdbeFrame *pFrame; /* New vdbe frame to execute in */ - SubProgram *pProgram; /* Sub-program to execute */ - void *t; /* Token identifying trigger */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_sqlite3Codec: pgno=%d, mode=%d, page_sz=%d", pgno, mode, page_sz); - pProgram = pOp->p4.pProgram; - pRt = &aMem[pOp->p3]; - assert( pProgram->nOp>0 ); +#ifdef SQLCIPHER_EXT + if(sqlcipher_license_check(ctx) != SQLITE_OK) return NULL; +#endif - /* If the p5 flag is clear, then recursive invocation of triggers is - ** disabled for backwards compatibility (p5 is set if this sub-program - ** is really a trigger, not a foreign key action, and the flag set - ** and cleared by the "PRAGMA recursive_triggers" command is clear). - ** - ** It is recursive invocation of triggers, at the SQL level, that is - ** disabled. In some cases a single trigger may generate more than one - ** SubProgram (if the trigger may be executed with more than one different - ** ON CONFLICT algorithm). SubProgram structures associated with a - ** single trigger all have the same value for the SubProgram.token - ** variable. */ - if( pOp->p5 ){ - t = pProgram->token; - for(pFrame=p->pFrame; pFrame && pFrame->token!=t; pFrame=pFrame->pParent); - if( pFrame ) break; + /* call to derive keys if not present yet */ + if((rc = sqlcipher_codec_key_derive(ctx)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_sqlite3Codec: error occurred during key derivation: %d", rc); + sqlcipher_codec_ctx_set_error(ctx, rc); + return NULL; } - if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){ - rc = SQLITE_ERROR; - sqlcipher_sqlite3VdbeError(p, "too many levels of trigger recursion"); - goto abort_due_to_error; + /* if the plaintext_header_size is negative that means an invalid size was set via + PRAGMA. We can't set the error state on the pager at that point because the pager + may not be open yet. However, this is a fatal error state, so abort the codec */ + if(plaintext_header_sz < 0) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_sqlite3Codec: error invalid plaintext_header_sz: %d", plaintext_header_sz); + sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + return NULL; } - /* Register pRt is used to store the memory required to save the state - ** of the current program, and the memory required at runtime to execute - ** the trigger program. If this trigger has been fired before, then pRt - ** is already allocated. Otherwise, it must be initialized. */ - if( (pRt->flags&MEM_Blob)==0 ){ - /* SubProgram.nMem is set to the number of memory cells used by the - ** program stored in SubProgram.aOp. As well as these, one memory - ** cell is required for each cursor used by the program. Set local - ** variable nMem (and later, VdbeFrame.nChildMem) to this value. - */ - nMem = pProgram->nMem + pProgram->nCsr; - assert( nMem>0 ); - if( pProgram->nCsr==0 ) nMem++; - nByte = ROUND8(sizeof(VdbeFrame)) - + nMem * sizeof(Mem) - + pProgram->nCsr * sizeof(VdbeCursor*) - + (pProgram->nOp + 7)/8; - pFrame = sqlcipher_sqlite3DbMallocZero(db, nByte); - if( !pFrame ){ - goto no_mem; - } - sqlcipher_sqlite3VdbeMemRelease(pRt); - pRt->flags = MEM_Blob|MEM_Dyn; - pRt->z = (char*)pFrame; - pRt->n = nByte; - pRt->xDel = sqlcipher_sqlite3VdbeFrameMemDel; + if(pgno == 1) /* adjust starting pointers in data page for header offset on first page*/ + offset = plaintext_header_sz ? plaintext_header_sz : FILE_HEADER_SZ; - pFrame->v = p; - pFrame->nChildMem = nMem; - pFrame->nChildCsr = pProgram->nCsr; - pFrame->pc = (int)(pOp - aOp); - pFrame->aMem = p->aMem; - pFrame->nMem = p->nMem; - pFrame->apCsr = p->apCsr; - pFrame->nCursor = p->nCursor; - pFrame->aOp = p->aOp; - pFrame->nOp = p->nOp; - pFrame->token = pProgram->token; -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - pFrame->anExec = p->anExec; -#endif -#ifdef SQLITE_DEBUG - pFrame->iFrameMagic = SQLITE_FRAME_MAGIC; -#endif - pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem]; - for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){ - pMem->flags = MEM_Undefined; - pMem->db = db; - } - }else{ - pFrame = (VdbeFrame*)pRt->z; - assert( pRt->xDel==sqlcipher_sqlite3VdbeFrameMemDel ); - assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem - || (pProgram->nCsr==0 && pProgram->nMem+1==pFrame->nChildMem) ); - assert( pProgram->nCsr==pFrame->nChildCsr ); - assert( (int)(pOp - aOp)==pFrame->pc ); - } + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_sqlite3Codec: switch mode=%d offset=%d", mode, offset); + switch(mode) { + case CODEC_READ_OP: /* decrypt */ + if(pgno == 1) /* copy initial part of file header or SQLite magic to buffer */ + memcpy(buffer, plaintext_header_sz ? pData : (void *) SQLITE_FILE_HEADER, offset); - p->nFrame++; - pFrame->pParent = p->pFrame; - pFrame->lastRowid = db->lastRowid; - pFrame->nChange = p->nChange; - pFrame->nDbChange = p->db->nChange; - assert( pFrame->pAuxData==0 ); - pFrame->pAuxData = p->pAuxData; - p->pAuxData = 0; - p->nChange = 0; - p->pFrame = pFrame; - p->aMem = aMem = VdbeFrameMem(pFrame); - p->nMem = pFrame->nChildMem; - p->nCursor = (u16)pFrame->nChildCsr; - p->apCsr = (VdbeCursor **)&aMem[p->nMem]; - pFrame->aOnce = (u8*)&p->apCsr[pProgram->nCsr]; - memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8); - p->aOp = aOp = pProgram->aOp; - p->nOp = pProgram->nOp; -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - p->anExec = 0; -#endif -#ifdef SQLITE_DEBUG - /* Verify that second and subsequent executions of the same trigger do not - ** try to reuse register values from the first use. */ - { - int i; - for(i=0; inMem; i++){ - aMem[i].pScopyFrom = 0; /* Prevent false-positive AboutToChange() errs */ - MemSetTypeFlag(&aMem[i], MEM_Undefined); /* Fault if this reg is reused */ - } - } + rc = sqlcipher_page_cipher(ctx, cctx, pgno, CIPHER_DECRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset); +#ifdef SQLCIPHER_TEST + if((sqlcipher_get_test_flags() & TEST_FAIL_DECRYPT) > 0 && sqlcipher_get_test_fail()) { + rc = SQLITE_ERROR; + sqlcipher_log(SQLCIPHER_LOG_ERROR, "simulating decryption failure for pgno=%d, mode=%d, page_sz=%d\n", pgno, mode, page_sz); + } #endif - pOp = &aOp[-1]; - goto check_for_interrupt; -} - -/* Opcode: Param P1 P2 * * * -** -** This opcode is only ever present in sub-programs called via the -** OP_Program instruction. Copy a value currently stored in a memory -** cell of the calling (parent) frame to cell P2 in the current frames -** address space. This is used by trigger programs to access the new.* -** and old.* values. -** -** The address of the cell in the parent frame is determined by adding -** the value of the P1 argument to the value of the P1 argument to the -** calling OP_Program instruction. -*/ -case OP_Param: { /* out2 */ - VdbeFrame *pFrame; - Mem *pIn; - pOut = out2Prerelease(p, pOp); - pFrame = p->pFrame; - pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1]; - sqlcipher_sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem); - break; -} - -#endif /* #ifndef SQLITE_OMIT_TRIGGER */ - -#ifndef SQLITE_OMIT_FOREIGN_KEY -/* Opcode: FkCounter P1 P2 * * * -** Synopsis: fkctr[P1]+=P2 -** -** Increment a "constraint counter" by P2 (P2 may be negative or positive). -** If P1 is non-zero, the database constraint counter is incremented -** (deferred foreign key constraints). Otherwise, if P1 is zero, the -** statement counter is incremented (immediate foreign key constraints). -*/ -case OP_FkCounter: { - if( db->flags & SQLITE_DeferFKs ){ - db->nDeferredImmCons += pOp->p2; - }else if( pOp->p1 ){ - db->nDeferredCons += pOp->p2; - }else{ - p->nFkConstraint += pOp->p2; - } - break; -} + if(rc != SQLITE_OK) { + /* failure to decrypt a page is considered a permanent error and will render the pager unusable + in order to prevent inconsistent data being loaded into page cache */ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_sqlite3Codec: error decrypting page %d data: %d", pgno, rc); + sqlcipher_memset((unsigned char*) buffer+offset, 0, page_sz-offset); + sqlcipher_codec_ctx_set_error(ctx, rc); + } + memcpy(pData, buffer, page_sz); /* copy buffer data back to pData and return */ + return pData; + break; -/* Opcode: FkIfZero P1 P2 * * * -** Synopsis: if fkctr[P1]==0 goto P2 -** -** This opcode tests if a foreign key constraint-counter is currently zero. -** If so, jump to instruction P2. Otherwise, fall through to the next -** instruction. -** -** If P1 is non-zero, then the jump is taken if the database constraint-counter -** is zero (the one that counts deferred constraint violations). If P1 is -** zero, the jump is taken if the statement constraint-counter is zero -** (immediate foreign key constraint violations). -*/ -case OP_FkIfZero: { /* jump */ - if( pOp->p1 ){ - VdbeBranchTaken(db->nDeferredCons==0 && db->nDeferredImmCons==0, 2); - if( db->nDeferredCons==0 && db->nDeferredImmCons==0 ) goto jump_to_p2; - }else{ - VdbeBranchTaken(p->nFkConstraint==0 && db->nDeferredImmCons==0, 2); - if( p->nFkConstraint==0 && db->nDeferredImmCons==0 ) goto jump_to_p2; - } - break; -} -#endif /* #ifndef SQLITE_OMIT_FOREIGN_KEY */ + case CODEC_WRITE_OP: /* encrypt database page, operate on write context and fall through to case 7, so the write context is used*/ + cctx = CIPHER_WRITE_CTX; -#ifndef SQLITE_OMIT_AUTOINCREMENT -/* Opcode: MemMax P1 P2 * * * -** Synopsis: r[P1]=max(r[P1],r[P2]) -** -** P1 is a register in the root frame of this VM (the root frame is -** different from the current frame if this instruction is being executed -** within a sub-program). Set the value of register P1 to the maximum of -** its current value and the value in register P2. -** -** This instruction throws an error if the memory cell is not initially -** an integer. -*/ -case OP_MemMax: { /* in2 */ - VdbeFrame *pFrame; - if( p->pFrame ){ - for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); - pIn1 = &pFrame->aMem[pOp->p1]; - }else{ - pIn1 = &aMem[pOp->p1]; - } - assert( memIsValid(pIn1) ); - sqlcipher_sqlite3VdbeMemIntegerify(pIn1); - pIn2 = &aMem[pOp->p2]; - sqlcipher_sqlite3VdbeMemIntegerify(pIn2); - if( pIn1->u.iu.i){ - pIn1->u.i = pIn2->u.i; - } - break; -} -#endif /* SQLITE_OMIT_AUTOINCREMENT */ + case CODEC_JOURNAL_OP: /* encrypt journal page, operate on read context use to get the original page data from the database */ + if(pgno == 1) { /* copy initial part of file header or salt to buffer */ + void *kdf_salt = NULL; + /* retrieve the kdf salt */ + if((rc = sqlcipher_codec_ctx_get_kdf_salt(ctx, &kdf_salt)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_sqlite3Codec: error retrieving salt: %d", rc); + sqlcipher_codec_ctx_set_error(ctx, rc); + return NULL; + } + memcpy(buffer, plaintext_header_sz ? pData : kdf_salt, offset); + } + rc = sqlcipher_page_cipher(ctx, cctx, pgno, CIPHER_ENCRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset); +#ifdef SQLCIPHER_TEST + if((sqlcipher_get_test_flags() & TEST_FAIL_ENCRYPT) > 0 && sqlcipher_get_test_fail()) { + rc = SQLITE_ERROR; + sqlcipher_log(SQLCIPHER_LOG_ERROR, "simulating encryption failure for pgno=%d, mode=%d, page_sz=%d\n", pgno, mode, page_sz); + } +#endif + if(rc != SQLITE_OK) { + /* failure to encrypt a page is considered a permanent error and will render the pager unusable + in order to prevent corrupted pages from being written to the main databased when using WAL */ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_sqlite3Codec: error encrypting page %d data: %d", pgno, rc); + sqlcipher_memset((unsigned char*)buffer+offset, 0, page_sz-offset); + sqlcipher_codec_ctx_set_error(ctx, rc); + return NULL; + } + return buffer; /* return persistent buffer data, pData remains intact */ + break; -/* Opcode: IfPos P1 P2 P3 * * -** Synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 -** -** Register P1 must contain an integer. -** If the value of register P1 is 1 or greater, subtract P3 from the -** value in P1 and jump to P2. -** -** If the initial value of register P1 is less than 1, then the -** value is unchanged and control passes through to the next instruction. -*/ -case OP_IfPos: { /* jump, in1 */ - pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags&MEM_Int ); - VdbeBranchTaken( pIn1->u.i>0, 2); - if( pIn1->u.i>0 ){ - pIn1->u.i -= pOp->p3; - goto jump_to_p2; + default: + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_sqlite3Codec: error unsupported codec mode %d", mode); + sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); /* unsupported mode, set error */ + return pData; + break; } - break; } -/* Opcode: OffsetLimit P1 P2 P3 * * -** Synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) -** -** This opcode performs a commonly used computation associated with -** LIMIT and OFFSET process. r[P1] holds the limit counter. r[P3] -** holds the offset counter. The opcode computes the combined value -** of the LIMIT and OFFSET and stores that value in r[P2]. The r[P2] -** value computed is the total number of rows that will need to be -** visited in order to complete the query. -** -** If r[P3] is zero or negative, that means there is no OFFSET -** and r[P2] is set to be the value of the LIMIT, r[P1]. -** -** if r[P1] is zero or negative, that means there is no LIMIT -** and r[P2] is set to -1. -** -** Otherwise, r[P2] is set to the sum of r[P1] and r[P3]. -*/ -case OP_OffsetLimit: { /* in1, out2, in3 */ - i64 x; - pIn1 = &aMem[pOp->p1]; - pIn3 = &aMem[pOp->p3]; - pOut = out2Prerelease(p, pOp); - assert( pIn1->flags & MEM_Int ); - assert( pIn3->flags & MEM_Int ); - x = pIn1->u.i; - if( x<=0 || sqlcipher_sqlite3AddInt64(&x, pIn3->u.i>0?pIn3->u.i:0) ){ - /* If the LIMIT is less than or equal to zero, loop forever. This - ** is documented. But also, if the LIMIT+OFFSET exceeds 2^63 then - ** also loop forever. This is undocumented. In fact, one could argue - ** that the loop should terminate. But assuming 1 billion iterations - ** per second (far exceeding the capabilities of any current hardware) - ** it would take nearly 300 years to actually reach the limit. So - ** looping forever is a reasonable approximation. */ - pOut->u.i = -1; - }else{ - pOut->u.i = x; - } - break; +static void sqlcipher_sqlite3FreeCodecArg(void *pCodecArg) { + codec_ctx *ctx = (codec_ctx *) pCodecArg; + if(pCodecArg == NULL) return; + sqlcipher_codec_ctx_free(&ctx); /* wipe and free allocated memory for the context */ + sqlcipher_deactivate(); /* cleanup related structures, OpenSSL etc, when codec is detatched */ } -/* Opcode: IfNotZero P1 P2 * * * -** Synopsis: if r[P1]!=0 then r[P1]--, goto P2 -** -** Register P1 must contain an integer. If the content of register P1 is -** initially greater than zero, then decrement the value in register P1. -** If it is non-zero (negative or positive) and then also jump to P2. -** If register P1 is initially zero, leave it unchanged and fall through. -*/ -case OP_IfNotZero: { /* jump, in1 */ - pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags&MEM_Int ); - VdbeBranchTaken(pIn1->u.i<0, 2); - if( pIn1->u.i ){ - if( pIn1->u.i>0 ) pIn1->u.i--; - goto jump_to_p2; - } - break; -} +int sqlcipherCodecAttach(sqlcipher_sqlite3* db, int nDb, const void *zKey, int nKey) { + struct Db *pDb = &db->aDb[nDb]; -/* Opcode: DecrJumpZero P1 P2 * * * -** Synopsis: if (--r[P1])==0 goto P2 -** -** Register P1 must hold an integer. Decrement the value in P1 -** and jump to P2 if the new value is exactly zero. -*/ -case OP_DecrJumpZero: { /* jump, in1 */ - pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags&MEM_Int ); - if( pIn1->u.i>SMALLEST_INT64 ) pIn1->u.i--; - VdbeBranchTaken(pIn1->u.i==0, 2); - if( pIn1->u.i==0 ) goto jump_to_p2; - break; -} + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipherCodecAttach: db=%p, nDb=%d", db, nDb); + if(nKey && zKey && pDb->pBt) { + int rc; + Pager *pPager = pDb->pBt->pBt->pPager; + sqlcipher_sqlite3_file *fd; + codec_ctx *ctx; -/* Opcode: AggStep * P2 P3 P4 P5 -** Synopsis: accum=r[P3] step(r[P2@P5]) -** -** Execute the xStep function for an aggregate. -** The function has P5 arguments. P4 is a pointer to the -** FuncDef structure that specifies the function. Register P3 is the -** accumulator. -** -** The P5 arguments are taken from register P2 and its -** successors. -*/ -/* Opcode: AggInverse * P2 P3 P4 P5 -** Synopsis: accum=r[P3] inverse(r[P2@P5]) -** -** Execute the xInverse function for an aggregate. -** The function has P5 arguments. P4 is a pointer to the -** FuncDef structure that specifies the function. Register P3 is the -** accumulator. -** -** The P5 arguments are taken from register P2 and its -** successors. -*/ -/* Opcode: AggStep1 P1 P2 P3 P4 P5 -** Synopsis: accum=r[P3] step(r[P2@P5]) -** -** Execute the xStep (if P1==0) or xInverse (if P1!=0) function for an -** aggregate. The function has P5 arguments. P4 is a pointer to the -** FuncDef structure that specifies the function. Register P3 is the -** accumulator. -** -** The P5 arguments are taken from register P2 and its -** successors. -** -** This opcode is initially coded as OP_AggStep0. On first evaluation, -** the FuncDef stored in P4 is converted into an sqlcipher_sqlite3_context and -** the opcode is changed. In this way, the initialization of the -** sqlcipher_sqlite3_context only happens once, instead of on each call to the -** step function. -*/ -case OP_AggInverse: -case OP_AggStep: { - int n; - sqlcipher_sqlite3_context *pCtx; + /* check if the sqlcipher_sqlite3_file is open, and if not force handle to NULL */ + if((fd = sqlcipher_sqlite3PagerFile(pPager))->pMethods == 0) fd = NULL; - assert( pOp->p4type==P4_FUNCDEF ); - n = pOp->p5; - assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); - assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) ); - assert( pOp->p3p2 || pOp->p3>=pOp->p2+n ); - pCtx = sqlcipher_sqlite3DbMallocRawNN(db, n*sizeof(sqlcipher_sqlite3_value*) + - (sizeof(pCtx[0]) + sizeof(Mem) - sizeof(sqlcipher_sqlite3_value*))); - if( pCtx==0 ) goto no_mem; - pCtx->pMem = 0; - pCtx->pOut = (Mem*)&(pCtx->argv[n]); - sqlcipher_sqlite3VdbeMemInit(pCtx->pOut, db, MEM_Null); - pCtx->pFunc = pOp->p4.pFunc; - pCtx->iOp = (int)(pOp - aOp); - pCtx->pVdbe = p; - pCtx->skipFlag = 0; - pCtx->isError = 0; - pCtx->argc = n; - pOp->p4type = P4_FUNCCTX; - pOp->p4.pCtx = pCtx; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipherCodecAttach: calling sqlcipher_activate()"); + sqlcipher_activate(); /* perform internal initialization for sqlcipher */ - /* OP_AggInverse must have P1==1 and OP_AggStep must have P1==0 */ - assert( pOp->p1==(pOp->opcode==OP_AggInverse) ); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipherCodecAttach: entering database mutex %p", db->mutex); + sqlcipher_sqlite3_mutex_enter(db->mutex); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipherCodecAttach: entered database mutex %p", db->mutex); - pOp->opcode = OP_AggStep1; - /* Fall through into OP_AggStep */ - /* no break */ deliberate_fall_through -} -case OP_AggStep1: { - int i; - sqlcipher_sqlite3_context *pCtx; - Mem *pMem; +#ifdef SQLCIPHER_EXT + if((rc = sqlcipher_sqlite3_set_authorizer(db, sqlcipher_license_authorizer, db)) != SQLITE_OK) { + sqlcipher_sqlite3_mutex_leave(db->mutex); + return rc; + } +#endif - assert( pOp->p4type==P4_FUNCCTX ); - pCtx = pOp->p4.pCtx; - pMem = &aMem[pOp->p3]; + /* point the internal codec argument against the contet to be prepared */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipherCodecAttach: calling sqlcipher_codec_ctx_init()"); + rc = sqlcipher_codec_ctx_init(&ctx, pDb, pDb->pBt->pBt->pPager, zKey, nKey); -#ifdef SQLITE_DEBUG - if( pOp->p1 ){ - /* This is an OP_AggInverse call. Verify that xStep has always - ** been called at least once prior to any xInverse call. */ - assert( pMem->uTemp==0x1122e0e3 ); - }else{ - /* This is an OP_AggStep call. Mark it as such. */ - pMem->uTemp = 0x1122e0e3; - } -#endif + if(rc != SQLITE_OK) { + /* initialization failed, do not attach potentially corrupted context */ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipherCodecAttach: context initialization failed forcing error state with rc=%d", rc); + /* force an error at the pager level, such that even the upstream caller ignores the return code + the pager will be in an error state and will process no further operations */ + sqlcipher_sqlite3pager_error(pPager, rc); + pDb->pBt->pBt->db->errCode = rc; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipherCodecAttach: leaving database mutex %p (early return on rc=%d)", db->mutex, rc); + sqlcipher_sqlite3_mutex_leave(db->mutex); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipherCodecAttach: left database mutex %p (early return on rc=%d)", db->mutex, rc); + return rc; + } - /* If this function is inside of a trigger, the register array in aMem[] - ** might change from one evaluation to the next. The next block of code - ** checks to see if the register array has changed, and if so it - ** reinitializes the relavant parts of the sqlcipher_sqlite3_context object */ - if( pCtx->pMem != pMem ){ - pCtx->pMem = pMem; - for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i]; - } + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipherCodecAttach: calling sqlcipherPagerSetCodec()"); + sqlcipherPagerSetCodec(sqlcipher_sqlite3BtreePager(pDb->pBt), sqlcipher_sqlite3Codec, NULL, sqlcipher_sqlite3FreeCodecArg, (void *) ctx); -#ifdef SQLITE_DEBUG - for(i=0; iargc; i++){ - assert( memIsValid(pCtx->argv[i]) ); - REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]); - } -#endif + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipherCodecAttach: calling codec_set_btree_to_codec_pagesize()"); + codec_set_btree_to_codec_pagesize(db, pDb, ctx); - pMem->n++; - assert( pCtx->pOut->flags==MEM_Null ); - assert( pCtx->isError==0 ); - assert( pCtx->skipFlag==0 ); -#ifndef SQLITE_OMIT_WINDOWFUNC - if( pOp->p1 ){ - (pCtx->pFunc->xInverse)(pCtx,pCtx->argc,pCtx->argv); - }else -#endif - (pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */ + /* force secure delete. This has the benefit of wiping internal data when deleted + and also ensures that all pages are written to disk (i.e. not skipped by + sqlcipher_sqlite3PagerDontWrite optimizations) */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipherCodecAttach: calling sqlcipher_sqlite3BtreeSecureDelete()"); + sqlcipher_sqlite3BtreeSecureDelete(pDb->pBt, 1); - if( pCtx->isError ){ - if( pCtx->isError>0 ){ - sqlcipher_sqlite3VdbeError(p, "%s", sqlcipher_sqlite3_value_text(pCtx->pOut)); - rc = pCtx->isError; - } - if( pCtx->skipFlag ){ - assert( pOp[-1].opcode==OP_CollSeq ); - i = pOp[-1].p1; - if( i ) sqlcipher_sqlite3VdbeMemSetInt64(&aMem[i], 1); - pCtx->skipFlag = 0; + /* if fd is null, then this is an in-memory database and + we dont' want to overwrite the AutoVacuum settings + if not null, then set to the default */ + if(fd != NULL) { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipherCodecAttach: calling sqlcipher_sqlite3BtreeSetAutoVacuum()"); + sqlcipher_sqlite3BtreeSetAutoVacuum(pDb->pBt, SQLITE_DEFAULT_AUTOVACUUM); } - sqlcipher_sqlite3VdbeMemRelease(pCtx->pOut); - pCtx->pOut->flags = MEM_Null; - pCtx->isError = 0; - if( rc ) goto abort_due_to_error; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipherCodecAttach: leaving database mutex %p", db->mutex); + sqlcipher_sqlite3_mutex_leave(db->mutex); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipherCodecAttach: left database mutex %p", db->mutex); } - assert( pCtx->pOut->flags==MEM_Null ); - assert( pCtx->skipFlag==0 ); - break; + return SQLITE_OK; } -/* Opcode: AggFinal P1 P2 * P4 * -** Synopsis: accum=r[P1] N=P2 -** -** P1 is the memory location that is the accumulator for an aggregate -** or window function. Execute the finalizer function -** for an aggregate and store the result in P1. -** -** P2 is the number of arguments that the step function takes and -** P4 is a pointer to the FuncDef for this function. The P2 -** argument is not used by this opcode. It is only there to disambiguate -** functions that can take varying numbers of arguments. The -** P4 argument is only needed for the case where -** the step function was not previously called. -*/ -/* Opcode: AggValue * P2 P3 P4 * -** Synopsis: r[P3]=value N=P2 -** -** Invoke the xValue() function and store the result in register P3. -** -** P2 is the number of arguments that the step function takes and -** P4 is a pointer to the FuncDef for this function. The P2 -** argument is not used by this opcode. It is only there to disambiguate -** functions that can take varying numbers of arguments. The -** P4 argument is only needed for the case where -** the step function was not previously called. -*/ -case OP_AggValue: -case OP_AggFinal: { - Mem *pMem; - assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); - assert( pOp->p3==0 || pOp->opcode==OP_AggValue ); - pMem = &aMem[pOp->p1]; - assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); -#ifndef SQLITE_OMIT_WINDOWFUNC - if( pOp->p3 ){ - memAboutToChange(p, &aMem[pOp->p3]); - rc = sqlcipher_sqlite3VdbeMemAggValue(pMem, &aMem[pOp->p3], pOp->p4.pFunc); - pMem = &aMem[pOp->p3]; - }else -#endif - { - rc = sqlcipher_sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); - } - - if( rc ){ - sqlcipher_sqlite3VdbeError(p, "%s", sqlcipher_sqlite3_value_text(pMem)); - goto abort_due_to_error; +int sqlcipher_find_db_index(sqlcipher_sqlite3 *db, const char *zDb) { + int db_index; + if(zDb == NULL){ + return 0; } - sqlcipher_sqlite3VdbeChangeEncoding(pMem, encoding); - UPDATE_MAX_BLOBSIZE(pMem); - if( sqlcipher_sqlite3VdbeMemTooBig(pMem) ){ - goto too_big; + for(db_index = 0; db_index < db->nDb; db_index++) { + struct Db *pDb = &db->aDb[db_index]; + if(strcmp(pDb->zDbSName, zDb) == 0) { + return db_index; + } } - break; + return 0; } -#ifndef SQLITE_OMIT_WAL -/* Opcode: Checkpoint P1 P2 P3 * * -** -** Checkpoint database P1. This is a no-op if P1 is not currently in -** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL, -** RESTART, or TRUNCATE. Write 1 or 0 into mem[P3] if the checkpoint returns -** SQLITE_BUSY or not, respectively. Write the number of pages in the -** WAL after the checkpoint into mem[P3+1] and the number of pages -** in the WAL that have been checkpointed after the checkpoint -** completes into mem[P3+2]. However on an error, mem[P3+1] and -** mem[P3+2] are initialized to -1. -*/ -case OP_Checkpoint: { - int i; /* Loop counter */ - int aRes[3]; /* Results */ - Mem *pMem; /* Write results here */ +SQLITE_API void sqlcipher_sqlite3_activate_see(const char* in) { + /* do nothing, security enhancements are always active */ +} - assert( p->readOnly==0 ); - aRes[0] = 0; - aRes[1] = aRes[2] = -1; - assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE - || pOp->p2==SQLITE_CHECKPOINT_FULL - || pOp->p2==SQLITE_CHECKPOINT_RESTART - || pOp->p2==SQLITE_CHECKPOINT_TRUNCATE - ); - rc = sqlcipher_sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]); - if( rc ){ - if( rc!=SQLITE_BUSY ) goto abort_due_to_error; - rc = SQLITE_OK; - aRes[0] = 1; - } - for(i=0, pMem = &aMem[pOp->p3]; i<3; i++, pMem++){ - sqlcipher_sqlite3VdbeMemSetInt64(pMem, (i64)aRes[i]); +SQLITE_API int sqlcipher_sqlite3_key(sqlcipher_sqlite3 *db, const void *pKey, int nKey) { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_sqlite3_key: db=%p", db); + return sqlcipher_sqlite3_key_v2(db, "main", pKey, nKey); +} + +SQLITE_API int sqlcipher_sqlite3_key_v2(sqlcipher_sqlite3 *db, const char *zDb, const void *pKey, int nKey) { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_sqlite3_key_v2: db=%p zDb=%s", db, zDb); + /* attach key if db and pKey are not null and nKey is > 0 */ + if(db && pKey && nKey) { + int db_index = sqlcipher_find_db_index(db, zDb); + return sqlcipherCodecAttach(db, db_index, pKey, nKey); } - break; -}; -#endif + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_sqlite3_key_v2: no key provided"); + return SQLITE_ERROR; +} -#ifndef SQLITE_OMIT_PRAGMA -/* Opcode: JournalMode P1 P2 P3 * * -** -** Change the journal mode of database P1 to P3. P3 must be one of the -** PAGER_JOURNALMODE_XXX values. If changing between the various rollback -** modes (delete, truncate, persist, off and memory), this is a simple -** operation. No IO is required. -** -** If changing into or out of WAL mode the procedure is more complicated. -** -** Write a string containing the final journal-mode to register P2. -*/ -case OP_JournalMode: { /* out2 */ - Btree *pBt; /* Btree to change journal mode of */ - Pager *pPager; /* Pager associated with pBt */ - int eNew; /* New journal mode */ - int eOld; /* The old journal mode */ -#ifndef SQLITE_OMIT_WAL - const char *zFilename; /* Name of database file for pPager */ -#endif +SQLITE_API int sqlcipher_sqlite3_rekey(sqlcipher_sqlite3 *db, const void *pKey, int nKey) { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_sqlite3_rekey: db=%p", db); + return sqlcipher_sqlite3_rekey_v2(db, "main", pKey, nKey); +} - pOut = out2Prerelease(p, pOp); - eNew = pOp->p3; - assert( eNew==PAGER_JOURNALMODE_DELETE - || eNew==PAGER_JOURNALMODE_TRUNCATE - || eNew==PAGER_JOURNALMODE_PERSIST - || eNew==PAGER_JOURNALMODE_OFF - || eNew==PAGER_JOURNALMODE_MEMORY - || eNew==PAGER_JOURNALMODE_WAL - || eNew==PAGER_JOURNALMODE_QUERY - ); - assert( pOp->p1>=0 && pOp->p1nDb ); - assert( p->readOnly==0 ); +/* sqlcipher_sqlite3_rekey_v2 +** Given a database, this will reencrypt the database using a new key. +** There is only one possible modes of operation - to encrypt a database +** that is already encrpyted. If the database is not already encrypted +** this should do nothing +** The proposed logic for this function follows: +** 1. Determine if the database is already encryptped +** 2. If there is NOT already a key present do nothing +** 3. If there is a key present, re-encrypt the database with the new key +*/ +SQLITE_API int sqlcipher_sqlite3_rekey_v2(sqlcipher_sqlite3 *db, const char *zDb, const void *pKey, int nKey) { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_sqlite3_rekey_v2: db=%p zDb=%s", db, zDb); + if(db && pKey && nKey) { + int db_index = sqlcipher_find_db_index(db, zDb); + struct Db *pDb = &db->aDb[db_index]; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_sqlite3_rekey_v2: database zDb=%p db_index:%d", zDb, db_index); + if(pDb->pBt) { + codec_ctx *ctx; + int rc, page_count; + Pgno pgno; + PgHdr *page; + Pager *pPager = pDb->pBt->pBt->pPager; - pBt = db->aDb[pOp->p1].pBt; - pPager = sqlcipher_sqlite3BtreePager(pBt); - eOld = sqlcipher_sqlite3PagerGetJournalMode(pPager); - if( eNew==PAGER_JOURNALMODE_QUERY ) eNew = eOld; - if( !sqlcipher_sqlite3PagerOkToChangeJournalMode(pPager) ) eNew = eOld; + ctx = (codec_ctx*) sqlcipherPagerGetCodec(pDb->pBt->pBt->pPager); -#ifndef SQLITE_OMIT_WAL - zFilename = sqlcipher_sqlite3PagerFilename(pPager, 1); + if(ctx == NULL) { + /* there was no codec attached to this database, so this should do nothing! */ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_sqlite3_rekey_v2: no codec attached to db, exiting"); + return SQLITE_OK; + } - /* Do not allow a transition to journal_mode=WAL for a database - ** in temporary storage or if the VFS does not support shared memory - */ - if( eNew==PAGER_JOURNALMODE_WAL - && (sqlcipher_sqlite3Strlen30(zFilename)==0 /* Temp file */ - || !sqlcipher_sqlite3PagerWalSupported(pPager)) /* No shared-memory support */ - ){ - eNew = eOld; - } + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_sqlite3_rekey_v2: entering database mutex %p", db->mutex); + sqlcipher_sqlite3_mutex_enter(db->mutex); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_sqlite3_rekey_v2: entered database mutex %p", db->mutex); - if( (eNew!=eOld) - && (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL) - ){ - if( !db->autoCommit || db->nVdbeRead>1 ){ - rc = SQLITE_ERROR; - sqlcipher_sqlite3VdbeError(p, - "cannot change %s wal mode from within a transaction", - (eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") - ); - goto abort_due_to_error; - }else{ + codec_set_pass_key(db, db_index, pKey, nKey, CIPHER_WRITE_CTX); - if( eOld==PAGER_JOURNALMODE_WAL ){ - /* If leaving WAL mode, close the log file. If successful, the call - ** to PagerCloseWal() checkpoints and deletes the write-ahead-log - ** file. An EXCLUSIVE lock may still be held on the database file - ** after a successful return. - */ - rc = sqlcipher_sqlite3PagerCloseWal(pPager, db); - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3PagerSetJournalMode(pPager, eNew); + /* do stuff here to rewrite the database + ** 1. Create a transaction on the database + ** 2. Iterate through each page, reading it and then writing it. + ** 3. If that goes ok then commit and put ctx->rekey into ctx->key + ** note: don't deallocate rekey since it may be used in a subsequent iteration + */ + rc = sqlcipher_sqlite3BtreeBeginTrans(pDb->pBt, 1, 0); /* begin write transaction */ + sqlcipher_sqlite3PagerPagecount(pPager, &page_count); + for(pgno = 1; rc == SQLITE_OK && pgno <= (unsigned int)page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */ + if(!sqlcipher_sqlite3pager_is_sj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */ + rc = sqlcipher_sqlite3PagerGet(pPager, pgno, &page, 0); + if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */ + rc = sqlcipher_sqlite3PagerWrite(page); + if(rc == SQLITE_OK) { + sqlcipher_sqlite3PagerUnref(page); + } else { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_sqlite3_rekey_v2: error %d occurred writing page %d", rc, pgno); + } + } else { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_sqlite3_rekey_v2: error %d occurred getting page %d", rc, pgno); + } } - }else if( eOld==PAGER_JOURNALMODE_MEMORY ){ - /* Cannot transition directly from MEMORY to WAL. Use mode OFF - ** as an intermediate */ - sqlcipher_sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_OFF); } - /* Open a transaction on the database file. Regardless of the journal - ** mode, this transaction always uses a rollback journal. - */ - assert( sqlcipher_sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE ); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); + /* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */ + if(rc == SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_sqlite3_rekey_v2: committing"); + rc = sqlcipher_sqlite3BtreeCommit(pDb->pBt); + sqlcipher_codec_key_copy(ctx, CIPHER_WRITE_CTX); + } else { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_sqlite3_rekey_v2: rollback"); + sqlcipher_sqlite3BtreeRollback(pDb->pBt, SQLITE_ABORT_ROLLBACK, 0); } + + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_sqlite3_rekey_v2: leaving database mutex %p", db->mutex); + sqlcipher_sqlite3_mutex_leave(db->mutex); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_sqlite3_rekey_v2: left database mutex %p", db->mutex); } + return SQLITE_OK; } -#endif /* ifndef SQLITE_OMIT_WAL */ - - if( rc ) eNew = eOld; - eNew = sqlcipher_sqlite3PagerSetJournalMode(pPager, eNew); + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_sqlite3_rekey_v2: no key provided"); + return SQLITE_ERROR; +} - pOut->flags = MEM_Str|MEM_Static|MEM_Term; - pOut->z = (char *)sqlcipher_sqlite3JournalModename(eNew); - pOut->n = sqlcipher_sqlite3Strlen30(pOut->z); - pOut->enc = SQLITE_UTF8; - sqlcipher_sqlite3VdbeChangeEncoding(pOut, encoding); - if( rc ) goto abort_due_to_error; - break; -}; -#endif /* SQLITE_OMIT_PRAGMA */ +void sqlcipherCodecGetKey(sqlcipher_sqlite3* db, int nDb, void **zKey, int *nKey) { + struct Db *pDb = &db->aDb[nDb]; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipherCodecGetKey:db=%p, nDb=%d", db, nDb); + if( pDb->pBt ) { + codec_ctx *ctx = (codec_ctx*) sqlcipherPagerGetCodec(pDb->pBt->pBt->pPager); -#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) -/* Opcode: Vacuum P1 P2 * * * -** -** Vacuum the entire database P1. P1 is 0 for "main", and 2 or more -** for an attached database. The "temp" database may not be vacuumed. -** -** If P2 is not zero, then it is a register holding a string which is -** the file into which the result of vacuum should be written. When -** P2 is zero, the vacuum overwrites the original database. -*/ -case OP_Vacuum: { - assert( p->readOnly==0 ); - rc = sqlcipher_sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1, - pOp->p2 ? &aMem[pOp->p2] : 0); - if( rc ) goto abort_due_to_error; - break; + if(ctx) { + /* pass back the keyspec from the codec, unless PRAGMA cipher_store_pass + is set or keyspec has not yet been derived, in which case pass + back the password key material */ + sqlcipher_codec_get_keyspec(ctx, zKey, nKey); + if(sqlcipher_codec_get_store_pass(ctx) == 1 || *zKey == NULL) { + sqlcipher_codec_get_pass(ctx, zKey, nKey); + } + } else { + *zKey = NULL; + *nKey = 0; + } + } } -#endif -#if !defined(SQLITE_OMIT_AUTOVACUUM) -/* Opcode: IncrVacuum P1 P2 * * * +/* + * Implementation of an "export" function that allows a caller + * to duplicate the main database to an attached database. This is intended + * as a conveneince for users who need to: + * + * 1. migrate from an non-encrypted database to an encrypted database + * 2. move from an encrypted database to a non-encrypted database + * 3. convert beween the various flavors of encrypted databases. + * + * This implementation is based heavily on the procedure and code used + * in vacuum.c, but is exposed as a function that allows export to any + * named attached database. + */ + +/* +** Finalize a prepared statement. If there was an error, store the +** text of the error message in *pzErrMsg. Return the result code. ** -** Perform a single step of the incremental vacuum procedure on -** the P1 database. If the vacuum has finished, jump to instruction -** P2. Otherwise, fall through to the next instruction. +** Based on vacuumFinalize from vacuum.c */ -case OP_IncrVacuum: { /* jump */ - Btree *pBt; - - assert( pOp->p1>=0 && pOp->p1nDb ); - assert( DbMaskTest(p->btreeMask, pOp->p1) ); - assert( p->readOnly==0 ); - pBt = db->aDb[pOp->p1].pBt; - rc = sqlcipher_sqlite3BtreeIncrVacuum(pBt); - VdbeBranchTaken(rc==SQLITE_DONE,2); +static int sqlcipher_finalize(sqlcipher_sqlite3 *db, sqlcipher_sqlite3_stmt *pStmt, char **pzErrMsg){ + int rc; + rc = sqlcipher_sqlite3VdbeFinalize((Vdbe*)pStmt); if( rc ){ - if( rc!=SQLITE_DONE ) goto abort_due_to_error; - rc = SQLITE_OK; - goto jump_to_p2; + sqlcipher_sqlite3SetString(pzErrMsg, db, sqlcipher_sqlite3_errmsg(db)); } - break; + return rc; } -#endif -/* Opcode: Expire P1 P2 * * * -** -** Cause precompiled statements to expire. When an expired statement -** is executed using sqlcipher_sqlite3_step() it will either automatically -** reprepare itself (if it was originally created using sqlcipher_sqlite3_prepare_v2()) -** or it will fail with SQLITE_SCHEMA. -** -** If P1 is 0, then all SQL statements become expired. If P1 is non-zero, -** then only the currently executing statement is expired. +/* +** Execute zSql on database db. Return an error code. ** -** If P2 is 0, then SQL statements are expired immediately. If P2 is 1, -** then running SQL statements are allowed to continue to run to completion. -** The P2==1 case occurs when a CREATE INDEX or similar schema change happens -** that might help the statement run faster but which does not affect the -** correctness of operation. +** Based on execSql from vacuum.c */ -case OP_Expire: { - assert( pOp->p2==0 || pOp->p2==1 ); - if( !pOp->p1 ){ - sqlcipher_sqlite3ExpirePreparedStatements(db, pOp->p2); - }else{ - p->expired = pOp->p2+1; +static int sqlcipher_execSql(sqlcipher_sqlite3 *db, char **pzErrMsg, const char *zSql){ + sqlcipher_sqlite3_stmt *pStmt; + VVA_ONLY( int rc; ) + if( !zSql ){ + return SQLITE_NOMEM; } - break; + if( SQLITE_OK!=sqlcipher_sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){ + sqlcipher_sqlite3SetString(pzErrMsg, db, sqlcipher_sqlite3_errmsg(db)); + return sqlcipher_sqlite3_errcode(db); + } + VVA_ONLY( rc = ) sqlcipher_sqlite3_step(pStmt); + assert( rc!=SQLITE_ROW ); + return sqlcipher_finalize(db, pStmt, pzErrMsg); } -/* Opcode: CursorLock P1 * * * * +/* +** Execute zSql on database db. The statement returns exactly +** one column. Execute this as SQL on the same database. ** -** Lock the btree to which cursor P1 is pointing so that the btree cannot be -** written by an other cursor. +** Based on execExecSql from vacuum.c */ -case OP_CursorLock: { - VdbeCursor *pC; - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->eCurType==CURTYPE_BTREE ); - sqlcipher_sqlite3BtreeCursorPin(pC->uc.pCursor); - break; -} +static int sqlcipher_execExecSql(sqlcipher_sqlite3 *db, char **pzErrMsg, const char *zSql){ + sqlcipher_sqlite3_stmt *pStmt; + int rc; -/* Opcode: CursorUnlock P1 * * * * -** -** Unlock the btree to which cursor P1 is pointing so that it can be -** written by other cursors. -*/ -case OP_CursorUnlock: { - VdbeCursor *pC; - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->eCurType==CURTYPE_BTREE ); - sqlcipher_sqlite3BtreeCursorUnpin(pC->uc.pCursor); - break; -} + rc = sqlcipher_sqlite3_prepare(db, zSql, -1, &pStmt, 0); + if( rc!=SQLITE_OK ) return rc; -#ifndef SQLITE_OMIT_SHARED_CACHE -/* Opcode: TableLock P1 P2 P3 P4 * -** Synopsis: iDb=P1 root=P2 write=P3 -** -** Obtain a lock on a particular table. This instruction is only used when -** the shared-cache feature is enabled. -** -** P1 is the index of the database in sqlcipher_sqlite3.aDb[] of the database -** on which the lock is acquired. A readlock is obtained if P3==0 or -** a write lock if P3==1. -** -** P2 contains the root-page of the table to lock. -** -** P4 contains a pointer to the name of the table being locked. This is only -** used to generate an error message if the lock cannot be obtained. -*/ -case OP_TableLock: { - u8 isWriteLock = (u8)pOp->p3; - if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommit) ){ - int p1 = pOp->p1; - assert( p1>=0 && p1nDb ); - assert( DbMaskTest(p->btreeMask, p1) ); - assert( isWriteLock==0 || isWriteLock==1 ); - rc = sqlcipher_sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock); - if( rc ){ - if( (rc&0xFF)==SQLITE_LOCKED ){ - const char *z = pOp->p4.z; - sqlcipher_sqlite3VdbeError(p, "database table is locked: %s", z); - } - goto abort_due_to_error; + while( SQLITE_ROW==sqlcipher_sqlite3_step(pStmt) ){ + rc = sqlcipher_execSql(db, pzErrMsg, (char*)sqlcipher_sqlite3_column_text(pStmt, 0)); + if( rc!=SQLITE_OK ){ + sqlcipher_finalize(db, pStmt, pzErrMsg); + return rc; } } - break; -} -#endif /* SQLITE_OMIT_SHARED_CACHE */ -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VBegin * * * P4 * -** -** P4 may be a pointer to an sqlcipher_sqlite3_vtab structure. If so, call the -** xBegin method for that table. -** -** Also, whether or not P4 is set, check that this is not being called from -** within a callback to a virtual table xSync() method. If it is, the error -** code will be set to SQLITE_LOCKED. -*/ -case OP_VBegin: { - VTable *pVTab; - pVTab = pOp->p4.pVtab; - rc = sqlcipher_sqlite3VtabBegin(db, pVTab); - if( pVTab ) sqlcipher_sqlite3VtabImportErrmsg(p, pVTab->pVtab); - if( rc ) goto abort_due_to_error; - break; + return sqlcipher_finalize(db, pStmt, pzErrMsg); } -#endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VCreate P1 P2 * * * -** -** P2 is a register that holds the name of a virtual table in database -** P1. Call the xCreate method for that table. +/* + * copy database and schema from the main database to an attached database + * + * Based on sqlcipher_sqlite3RunVacuum from vacuum.c */ -case OP_VCreate: { - Mem sMem; /* For storing the record being decoded */ - const char *zTab; /* Name of the virtual table */ +void sqlcipher_exportFunc(sqlcipher_sqlite3_context *context, int argc, sqlcipher_sqlite3_value **argv) { + sqlcipher_sqlite3 *db = sqlcipher_sqlite3_context_db_handle(context); + const char* targetDb, *sourceDb; + int targetDb_idx = 0; + u64 saved_flags = db->flags; /* Saved value of the db->flags */ + u32 saved_mDbFlags = db->mDbFlags; /* Saved value of the db->mDbFlags */ + int saved_nChange = db->nChange; /* Saved value of db->nChange */ + int saved_nTotalChange = db->nTotalChange; /* Saved value of db->nTotalChange */ + u8 saved_mTrace = db->mTrace; /* Saved value of db->mTrace */ + int rc = SQLITE_OK; /* Return code from service routines */ + char *zSql = NULL; /* SQL statements */ + char *pzErrMsg = NULL; - memset(&sMem, 0, sizeof(sMem)); - sMem.db = db; - /* Because P2 is always a static string, it is impossible for the - ** sqlcipher_sqlite3VdbeMemCopy() to fail */ - assert( (aMem[pOp->p2].flags & MEM_Str)!=0 ); - assert( (aMem[pOp->p2].flags & MEM_Static)!=0 ); - rc = sqlcipher_sqlite3VdbeMemCopy(&sMem, &aMem[pOp->p2]); - assert( rc==SQLITE_OK ); - zTab = (const char*)sqlcipher_sqlite3_value_text(&sMem); - assert( zTab || db->mallocFailed ); - if( zTab ){ - rc = sqlcipher_sqlite3VtabCallCreate(db, pOp->p1, zTab, &p->zErrMsg); + if(argc != 1 && argc != 2) { + rc = SQLITE_ERROR; + pzErrMsg = sqlcipher_sqlite3_mprintf("invalid number of arguments (%d) passed to sqlcipher_export", argc); + goto end_of_export; } - sqlcipher_sqlite3VdbeMemRelease(&sMem); - if( rc ) goto abort_due_to_error; - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VDestroy P1 * * P4 * -** -** P4 is the name of a virtual table in database P1. Call the xDestroy method -** of that table. -*/ -case OP_VDestroy: { - db->nVDestroy++; - rc = sqlcipher_sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z); - db->nVDestroy--; - assert( p->errorAction==OE_Abort && p->usesStmtJournal ); - if( rc ) goto abort_due_to_error; - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ + if(sqlcipher_sqlite3_value_type(argv[0]) == SQLITE_NULL) { + rc = SQLITE_ERROR; + pzErrMsg = sqlcipher_sqlite3_mprintf("target database can't be NULL"); + goto end_of_export; + } -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VOpen P1 * * P4 * -** -** P4 is a pointer to a virtual table object, an sqlcipher_sqlite3_vtab structure. -** P1 is a cursor number. This opcode opens a cursor to the virtual -** table and stores that cursor in P1. -*/ -case OP_VOpen: { - VdbeCursor *pCur; - sqlcipher_sqlite3_vtab_cursor *pVCur; - sqlcipher_sqlite3_vtab *pVtab; - const sqlcipher_sqlite3_module *pModule; + targetDb = (const char*) sqlcipher_sqlite3_value_text(argv[0]); + sourceDb = "main"; - assert( p->bIsReader ); - pCur = 0; - pVCur = 0; - pVtab = pOp->p4.pVtab->pVtab; - if( pVtab==0 || NEVER(pVtab->pModule==0) ){ - rc = SQLITE_LOCKED; - goto abort_due_to_error; + if(argc == 2) { + if(sqlcipher_sqlite3_value_type(argv[1]) == SQLITE_NULL) { + rc = SQLITE_ERROR; + pzErrMsg = sqlcipher_sqlite3_mprintf("target database can't be NULL"); + goto end_of_export; + } + sourceDb = (char *) sqlcipher_sqlite3_value_text(argv[1]); } - pModule = pVtab->pModule; - rc = pModule->xOpen(pVtab, &pVCur); - sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); - if( rc ) goto abort_due_to_error; - /* Initialize sqlcipher_sqlite3_vtab_cursor base class */ - pVCur->pVtab = pVtab; - /* Initialize vdbe cursor object */ - pCur = allocateCursor(p, pOp->p1, 0, -1, CURTYPE_VTAB); - if( pCur ){ - pCur->uc.pVCur = pVCur; - pVtab->nRef++; - }else{ - assert( db->mallocFailed ); - pModule->xClose(pVCur); - goto no_mem; + /* if the name of the target is not main, but the index returned is zero + there is a mismatch and we should not proceed */ + targetDb_idx = sqlcipher_find_db_index(db, targetDb); + if(targetDb_idx == 0 && targetDb != NULL && sqlcipher_sqlite3_stricmp("main", targetDb) != 0) { + rc = SQLITE_ERROR; + pzErrMsg = sqlcipher_sqlite3_mprintf("unknown database %s", targetDb); + goto end_of_export; } - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ + db->init.iDb = targetDb_idx; -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VFilter P1 P2 P3 P4 * -** Synopsis: iplan=r[P3] zplan='P4' -** -** P1 is a cursor opened using VOpen. P2 is an address to jump to if -** the filtered result set is empty. -** -** P4 is either NULL or a string that was generated by the xBestIndex -** method of the module. The interpretation of the P4 string is left -** to the module implementation. -** -** This opcode invokes the xFilter method on the virtual table specified -** by P1. The integer query plan parameter to xFilter is stored in register -** P3. Register P3+1 stores the argc parameter to be passed to the -** xFilter method. Registers P3+2..P3+1+argc are the argc -** additional parameters which are passed to -** xFilter as argv. Register P3+2 becomes argv[0] when passed to xFilter. -** -** A jump is made to P2 if the result set after filtering would be empty. -*/ -case OP_VFilter: { /* jump */ - int nArg; - int iQuery; - const sqlcipher_sqlite3_module *pModule; - Mem *pQuery; - Mem *pArgc; - sqlcipher_sqlite3_vtab_cursor *pVCur; - sqlcipher_sqlite3_vtab *pVtab; - VdbeCursor *pCur; - int res; - int i; - Mem **apArg; + db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; + db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; + db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_Defensive | SQLITE_CountRows); + db->mTrace = 0; - pQuery = &aMem[pOp->p3]; - pArgc = &pQuery[1]; - pCur = p->apCsr[pOp->p1]; - assert( memIsValid(pQuery) ); - REGISTER_TRACE(pOp->p3, pQuery); - assert( pCur->eCurType==CURTYPE_VTAB ); - pVCur = pCur->uc.pVCur; - pVtab = pVCur->pVtab; - pModule = pVtab->pModule; + /* Query the schema of the main database. Create a mirror schema + ** in the temporary database. + */ + zSql = sqlcipher_sqlite3_mprintf( + "SELECT sql " + " FROM %s.sqlite_schema WHERE type='table' AND name!='sqlite_sequence'" + " AND rootpage>0" + , sourceDb); + rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql); + if( rc!=SQLITE_OK ) goto end_of_export; + sqlcipher_sqlite3_free(zSql); - /* Grab the index number and argc parameters */ - assert( (pQuery->flags&MEM_Int)!=0 && pArgc->flags==MEM_Int ); - nArg = (int)pArgc->u.i; - iQuery = (int)pQuery->u.i; + zSql = sqlcipher_sqlite3_mprintf( + "SELECT sql " + " FROM %s.sqlite_schema WHERE sql LIKE 'CREATE INDEX %%' " + , sourceDb); + rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql); + if( rc!=SQLITE_OK ) goto end_of_export; + sqlcipher_sqlite3_free(zSql); - /* Invoke the xFilter method */ - res = 0; - apArg = p->apArg; - for(i = 0; ixFilter(pVCur, iQuery, pOp->p4.z, nArg, apArg); - sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); - if( rc ) goto abort_due_to_error; - res = pModule->xEof(pVCur); - pCur->nullRow = 0; - VdbeBranchTaken(res!=0,2); - if( res ) goto jump_to_p2; - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ + zSql = sqlcipher_sqlite3_mprintf( + "SELECT sql " + " FROM %s.sqlite_schema WHERE sql LIKE 'CREATE UNIQUE INDEX %%'" + , sourceDb); + rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql); + if( rc!=SQLITE_OK ) goto end_of_export; + sqlcipher_sqlite3_free(zSql); -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VColumn P1 P2 P3 * P5 -** Synopsis: r[P3]=vcolumn(P2) -** -** Store in register P3 the value of the P2-th column of -** the current row of the virtual-table of cursor P1. -** -** If the VColumn opcode is being used to fetch the value of -** an unchanging column during an UPDATE operation, then the P5 -** value is OPFLAG_NOCHNG. This will cause the sqlcipher_sqlite3_vtab_nochange() -** function to return true inside the xColumn method of the virtual -** table implementation. The P5 column might also contain other -** bits (OPFLAG_LENGTHARG or OPFLAG_TYPEOFARG) but those bits are -** unused by OP_VColumn. -*/ -case OP_VColumn: { - sqlcipher_sqlite3_vtab *pVtab; - const sqlcipher_sqlite3_module *pModule; - Mem *pDest; - sqlcipher_sqlite3_context sContext; + /* Loop through the tables in the main database. For each, do + ** an "INSERT INTO rekey_db.xxx SELECT * FROM main.xxx;" to copy + ** the contents to the temporary database. + */ + zSql = sqlcipher_sqlite3_mprintf( + "SELECT 'INSERT INTO %s.' || quote(name) " + "|| ' SELECT * FROM %s.' || quote(name) || ';'" + "FROM %s.sqlite_schema " + "WHERE type = 'table' AND name!='sqlite_sequence' " + " AND rootpage>0" + , targetDb, sourceDb, sourceDb); + rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql); + if( rc!=SQLITE_OK ) goto end_of_export; + sqlcipher_sqlite3_free(zSql); - VdbeCursor *pCur = p->apCsr[pOp->p1]; - assert( pCur->eCurType==CURTYPE_VTAB ); - assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); - pDest = &aMem[pOp->p3]; - memAboutToChange(p, pDest); - if( pCur->nullRow ){ - sqlcipher_sqlite3VdbeMemSetNull(pDest); - break; - } - pVtab = pCur->uc.pVCur->pVtab; - pModule = pVtab->pModule; - assert( pModule->xColumn ); - memset(&sContext, 0, sizeof(sContext)); - sContext.pOut = pDest; - assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 ); - if( pOp->p5 & OPFLAG_NOCHNG ){ - sqlcipher_sqlite3VdbeMemSetNull(pDest); - pDest->flags = MEM_Null|MEM_Zero; - pDest->u.nZero = 0; - }else{ - MemSetTypeFlag(pDest, MEM_Null); - } - rc = pModule->xColumn(pCur->uc.pVCur, &sContext, pOp->p2); - sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); - if( sContext.isError>0 ){ - sqlcipher_sqlite3VdbeError(p, "%s", sqlcipher_sqlite3_value_text(pDest)); - rc = sContext.isError; - } - sqlcipher_sqlite3VdbeChangeEncoding(pDest, encoding); - REGISTER_TRACE(pOp->p3, pDest); - UPDATE_MAX_BLOBSIZE(pDest); + /* Copy over the contents of the sequence table + */ + zSql = sqlcipher_sqlite3_mprintf( + "SELECT 'INSERT INTO %s.' || quote(name) " + "|| ' SELECT * FROM %s.' || quote(name) || ';' " + "FROM %s.sqlite_schema WHERE name=='sqlite_sequence';" + , targetDb, sourceDb, targetDb); + rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql); + if( rc!=SQLITE_OK ) goto end_of_export; + sqlcipher_sqlite3_free(zSql); - if( sqlcipher_sqlite3VdbeMemTooBig(pDest) ){ - goto too_big; + /* Copy the triggers, views, and virtual tables from the main database + ** over to the temporary database. None of these objects has any + ** associated storage, so all we have to do is copy their entries + ** from the SQLITE_MASTER table. + */ + zSql = sqlcipher_sqlite3_mprintf( + "INSERT INTO %s.sqlite_schema " + " SELECT type, name, tbl_name, rootpage, sql" + " FROM %s.sqlite_schema" + " WHERE type='view' OR type='trigger'" + " OR (type='table' AND rootpage=0)" + , targetDb, sourceDb); + rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execSql(db, &pzErrMsg, zSql); + if( rc!=SQLITE_OK ) goto end_of_export; + sqlcipher_sqlite3_free(zSql); + + zSql = NULL; +end_of_export: + db->init.iDb = 0; + db->flags = saved_flags; + db->mDbFlags = saved_mDbFlags; + db->nChange = saved_nChange; + db->nTotalChange = saved_nTotalChange; + db->mTrace = saved_mTrace; + + if(zSql) sqlcipher_sqlite3_free(zSql); + + if(rc) { + if(pzErrMsg != NULL) { + sqlcipher_sqlite3_result_error(context, pzErrMsg, -1); + sqlcipher_sqlite3DbFree(db, pzErrMsg); + } else { + sqlcipher_sqlite3_result_error(context, sqlcipher_sqlite3ErrStr(rc), -1); + } } - if( rc ) goto abort_due_to_error; - break; } -#endif /* SQLITE_OMIT_VIRTUALTABLE */ +#endif +/* END SQLCIPHER */ -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VNext P1 P2 * * * +/************** End of crypto.c **********************************************/ +/************** Begin file crypto_impl.c *************************************/ +/* +** SQLCipher +** http://sqlcipher.net +** +** Copyright (c) 2008 - 2013, ZETETIC LLC +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the ZETETIC LLC nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** -** Advance virtual table P1 to the next row in its result set and -** jump to instruction P2. Or, if the virtual table has reached -** the end of its result set, then fall through to the next instruction. */ -case OP_VNext: { /* jump */ - sqlcipher_sqlite3_vtab *pVtab; - const sqlcipher_sqlite3_module *pModule; - int res; - VdbeCursor *pCur; +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC - res = 0; - pCur = p->apCsr[pOp->p1]; - assert( pCur->eCurType==CURTYPE_VTAB ); - if( pCur->nullRow ){ - break; - } - pVtab = pCur->uc.pVCur->pVtab; - pModule = pVtab->pModule; - assert( pModule->xNext ); +/* #include "sqlcipher.h" */ +/* #include "crypto.h" */ - /* Invoke the xNext() method of the module. There is no way for the - ** underlying implementation to return an error if one occurs during - ** xNext(). Instead, if an error occurs, true is returned (indicating that - ** data is available) and the error code returned when xColumn or - ** some other method is next invoked on the save virtual table cursor. - */ - rc = pModule->xNext(pCur->uc.pVCur); - sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); - if( rc ) goto abort_due_to_error; - res = pModule->xEof(pCur->uc.pVCur); - VdbeBranchTaken(!res,2); - if( !res ){ - /* If there is data, jump to P2 */ - goto jump_to_p2_and_check_for_interrupt; - } - goto check_for_interrupt; +#ifdef SQLCIPHER_TEST +static volatile unsigned int cipher_test_flags = 0; +unsigned int sqlcipher_get_test_flags() { + return cipher_test_flags; +} +void sqlcipher_set_test_flags(unsigned int flags) { + cipher_test_flags = flags; } -#endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VRename P1 * * P4 * -** -** P4 is a pointer to a virtual table object, an sqlcipher_sqlite3_vtab structure. -** This opcode invokes the corresponding xRename method. The value -** in register P1 is passed as the zName argument to the xRename method. -*/ -case OP_VRename: { - sqlcipher_sqlite3_vtab *pVtab; - Mem *pName; - int isLegacy; +static volatile int cipher_test_rand = 0; +int sqlcipher_get_test_rand() { + return cipher_test_rand; +} +void sqlcipher_set_test_rand(int rand) { + cipher_test_rand = rand; +} +int sqlcipher_get_test_fail() { + int x; - isLegacy = (db->flags & SQLITE_LegacyAlter); - db->flags |= SQLITE_LegacyAlter; - pVtab = pOp->p4.pVtab->pVtab; - pName = &aMem[pOp->p1]; - assert( pVtab->pModule->xRename ); - assert( memIsValid(pName) ); - assert( p->readOnly==0 ); - REGISTER_TRACE(pOp->p1, pName); - assert( pName->flags & MEM_Str ); - testcase( pName->enc==SQLITE_UTF8 ); - testcase( pName->enc==SQLITE_UTF16BE ); - testcase( pName->enc==SQLITE_UTF16LE ); - rc = sqlcipher_sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8); - if( rc ) goto abort_due_to_error; - rc = pVtab->pModule->xRename(pVtab, pName->z); - if( isLegacy==0 ) db->flags &= ~(u64)SQLITE_LegacyAlter; - sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); - p->expired = 0; - if( rc ) goto abort_due_to_error; - break; + /* if cipher_test_rand is not set to a non-zero value always fail (return true) */ + if (cipher_test_rand == 0) return 1; + + sqlcipher_sqlite3_randomness(sizeof(x), &x); + return ((x % cipher_test_rand) == 0); } #endif -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VUpdate P1 P2 P3 P4 P5 -** Synopsis: data=r[P3@P2] -** -** P4 is a pointer to a virtual table object, an sqlcipher_sqlite3_vtab structure. -** This opcode invokes the corresponding xUpdate method. P2 values -** are contiguous memory cells starting at P3 to pass to the xUpdate -** invocation. The value in register (P3+P2-1) corresponds to the -** p2th element of the argv array passed to xUpdate. -** -** The xUpdate method will do a DELETE or an INSERT or both. -** The argv[0] element (which corresponds to memory cell P3) -** is the rowid of a row to delete. If argv[0] is NULL then no -** deletion occurs. The argv[1] element is the rowid of the new -** row. This can be NULL to have the virtual table select the new -** rowid for itself. The subsequent elements in the array are -** the values of columns in the new row. -** -** If P2==1 then no insert is performed. argv[0] is the rowid of -** a row to delete. -** -** P1 is a boolean flag. If it is set to true and the xUpdate call -** is successful, then the value returned by sqlcipher_sqlite3_last_insert_rowid() -** is set to the value of the rowid for the row just inserted. -** -** P5 is the error actions (OE_Replace, OE_Fail, OE_Ignore, etc) to -** apply in the case of a constraint failure on an insert or update. -*/ -case OP_VUpdate: { - sqlcipher_sqlite3_vtab *pVtab; - const sqlcipher_sqlite3_module *pModule; - int nArg; - int i; - sqlite_int64 rowid; - Mem **apArg; - Mem *pX; +/* Generate code to return a string value */ - assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback - || pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace - ); - assert( p->readOnly==0 ); - if( db->mallocFailed ) goto no_mem; - sqlcipher_sqlite3VdbeIncrWriteCounter(p, 0); - pVtab = pOp->p4.pVtab->pVtab; - if( pVtab==0 || NEVER(pVtab->pModule==0) ){ - rc = SQLITE_LOCKED; - goto abort_due_to_error; - } - pModule = pVtab->pModule; - nArg = pOp->p2; - assert( pOp->p4type==P4_VTAB ); - if( ALWAYS(pModule->xUpdate) ){ - u8 vtabOnConflict = db->vtabOnConflict; - apArg = p->apArg; - pX = &aMem[pOp->p3]; - for(i=0; ivtabOnConflict = pOp->p5; - rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); - db->vtabOnConflict = vtabOnConflict; - sqlcipher_sqlite3VtabImportErrmsg(p, pVtab); - if( rc==SQLITE_OK && pOp->p1 ){ - assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) ); - db->lastRowid = rowid; - } - if( (rc&0xff)==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){ - if( pOp->p5==OE_Ignore ){ - rc = SQLITE_OK; - }else{ - p->errorAction = ((pOp->p5==OE_Replace) ? OE_Abort : pOp->p5); +static volatile unsigned int default_flags = DEFAULT_CIPHER_FLAGS; +static volatile unsigned char hmac_salt_mask = HMAC_SALT_MASK; +static volatile int default_kdf_iter = PBKDF2_ITER; +static volatile int default_page_size = 4096; +static volatile int default_plaintext_header_sz = 0; +static volatile int default_hmac_algorithm = SQLCIPHER_HMAC_SHA512; +static volatile int default_kdf_algorithm = SQLCIPHER_PBKDF2_HMAC_SHA512; +static volatile int sqlcipher_mem_security_on = 0; +static volatile int sqlcipher_mem_executed = 0; +static volatile int sqlcipher_mem_initialized = 0; +static volatile unsigned int sqlcipher_activate_count = 0; +static volatile sqlcipher_sqlite3_mem_methods default_mem_methods; +static sqlcipher_provider *default_provider = NULL; + +static sqlcipher_sqlite3_mutex* sqlcipher_static_mutex[SQLCIPHER_MUTEX_COUNT]; +static FILE* sqlcipher_log_file = NULL; +static volatile int sqlcipher_log_logcat = 0; +static volatile unsigned int sqlcipher_log_level = SQLCIPHER_LOG_NONE; + +sqlcipher_sqlite3_mutex* sqlcipher_mutex(int mutex) { + if(mutex < 0 || mutex >= SQLCIPHER_MUTEX_COUNT) return NULL; + return sqlcipher_static_mutex[mutex]; +} + +static int sqlcipher_mem_init(void *pAppData) { + return default_mem_methods.xInit(pAppData); +} +static void sqlcipher_mem_shutdown(void *pAppData) { + default_mem_methods.xShutdown(pAppData); +} +static void *sqlcipher_mem_malloc(int n) { + void *ptr = default_mem_methods.xMalloc(n); + if(!sqlcipher_mem_executed) sqlcipher_mem_executed = 1; + if(sqlcipher_mem_security_on) { + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_mem_malloc: calling sqlcipher_mlock(%p,%d)", ptr, n); + sqlcipher_mlock(ptr, n); + } + return ptr; +} +static int sqlcipher_mem_size(void *p) { + return default_mem_methods.xSize(p); +} +static void sqlcipher_mem_free(void *p) { + int sz; + if(!sqlcipher_mem_executed) sqlcipher_mem_executed = 1; + if(sqlcipher_mem_security_on) { + sz = sqlcipher_mem_size(p); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_mem_free: calling sqlcipher_memset(%p,0,%d) and sqlcipher_munlock(%p, %d)", p, sz, p, sz); + sqlcipher_memset(p, 0, sz); + sqlcipher_munlock(p, sz); + } + default_mem_methods.xFree(p); +} +static void *sqlcipher_mem_realloc(void *p, int n) { + void *new = NULL; + int orig_sz = 0; + if(sqlcipher_mem_security_on) { + orig_sz = sqlcipher_mem_size(p); + if (n==0) { + sqlcipher_mem_free(p); + return NULL; + } else if (!p) { + return sqlcipher_mem_malloc(n); + } else if(n <= orig_sz) { + return p; + } else { + new = sqlcipher_mem_malloc(n); + if(new) { + memcpy(new, p, orig_sz); + sqlcipher_mem_free(p); } - }else{ - p->nChange++; + return new; } - if( rc ) goto abort_due_to_error; + } else { + return default_mem_methods.xRealloc(p, n); } - break; } -#endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -/* Opcode: Pagecount P1 P2 * * * -** -** Write the current number of pages in database P1 to memory cell P2. -*/ -case OP_Pagecount: { /* out2 */ - pOut = out2Prerelease(p, pOp); - pOut->u.i = sqlcipher_sqlite3BtreeLastPage(db->aDb[pOp->p1].pBt); - break; +static int sqlcipher_mem_roundup(int n) { + return default_mem_methods.xRoundup(n); } -#endif - -#ifndef SQLITE_OMIT_PAGER_PRAGMAS -/* Opcode: MaxPgcnt P1 P2 P3 * * -** -** Try to set the maximum page count for database P1 to the value in P3. -** Do not let the maximum page count fall below the current page count and -** do not change the maximum page count value if P3==0. -** -** Store the maximum page count after the change in register P2. -*/ -case OP_MaxPgcnt: { /* out2 */ - unsigned int newMax; - Btree *pBt; +static sqlcipher_sqlite3_mem_methods sqlcipher_mem_methods = { + sqlcipher_mem_malloc, + sqlcipher_mem_free, + sqlcipher_mem_realloc, + sqlcipher_mem_size, + sqlcipher_mem_roundup, + sqlcipher_mem_init, + sqlcipher_mem_shutdown, + 0 +}; - pOut = out2Prerelease(p, pOp); - pBt = db->aDb[pOp->p1].pBt; - newMax = 0; - if( pOp->p3 ){ - newMax = sqlcipher_sqlite3BtreeLastPage(pBt); - if( newMax < (unsigned)pOp->p3 ) newMax = (unsigned)pOp->p3; +void sqlcipher_init_memmethods() { + if(sqlcipher_mem_initialized) return; + if(sqlcipher_sqlite3_config(SQLITE_CONFIG_GETMALLOC, &default_mem_methods) != SQLITE_OK || + sqlcipher_sqlite3_config(SQLITE_CONFIG_MALLOC, &sqlcipher_mem_methods) != SQLITE_OK) { + sqlcipher_mem_security_on = sqlcipher_mem_executed = sqlcipher_mem_initialized = 0; + } else { + sqlcipher_mem_initialized = 1; } - pOut->u.i = sqlcipher_sqlite3BtreeMaxPageCount(pBt, newMax); - break; } -#endif - -/* Opcode: Function P1 P2 P3 P4 * -** Synopsis: r[P3]=func(r[P2@NP]) -** -** Invoke a user function (P4 is a pointer to an sqlcipher_sqlite3_context object that -** contains a pointer to the function to be run) with arguments taken -** from register P2 and successors. The number of arguments is in -** the sqlcipher_sqlite3_context object that P4 points to. -** The result of the function is stored -** in register P3. Register P3 must not be one of the function inputs. -** -** P1 is a 32-bit bitmask indicating whether or not each argument to the -** function was determined to be constant at compile time. If the first -** argument was constant then bit 0 of P1 is set. This is used to determine -** whether meta data associated with a user function argument using the -** sqlcipher_sqlite3_set_auxdata() API may be safely retained until the next -** invocation of this opcode. -** -** See also: AggStep, AggFinal, PureFunc -*/ -/* Opcode: PureFunc P1 P2 P3 P4 * -** Synopsis: r[P3]=func(r[P2@NP]) -** -** Invoke a user function (P4 is a pointer to an sqlcipher_sqlite3_context object that -** contains a pointer to the function to be run) with arguments taken -** from register P2 and successors. The number of arguments is in -** the sqlcipher_sqlite3_context object that P4 points to. -** The result of the function is stored -** in register P3. Register P3 must not be one of the function inputs. -** -** P1 is a 32-bit bitmask indicating whether or not each argument to the -** function was determined to be constant at compile time. If the first -** argument was constant then bit 0 of P1 is set. This is used to determine -** whether meta data associated with a user function argument using the -** sqlcipher_sqlite3_set_auxdata() API may be safely retained until the next -** invocation of this opcode. -** -** This opcode works exactly like OP_Function. The only difference is in -** its name. This opcode is used in places where the function must be -** purely non-deterministic. Some built-in date/time functions can be -** either determinitic of non-deterministic, depending on their arguments. -** When those function are used in a non-deterministic way, they will check -** to see if they were called using OP_PureFunc instead of OP_Function, and -** if they were, they throw an error. -** -** See also: AggStep, AggFinal, Function -*/ -case OP_PureFunc: /* group */ -case OP_Function: { /* group */ - int i; - sqlcipher_sqlite3_context *pCtx; - assert( pOp->p4type==P4_FUNCCTX ); - pCtx = pOp->p4.pCtx; +int sqlcipher_register_provider(sqlcipher_provider *p) { + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_register_provider: entering SQLCIPHER_MUTEX_PROVIDER"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_register_provider: entered SQLCIPHER_MUTEX_PROVIDER"); - /* If this function is inside of a trigger, the register array in aMem[] - ** might change from one evaluation to the next. The next block of code - ** checks to see if the register array has changed, and if so it - ** reinitializes the relavant parts of the sqlcipher_sqlite3_context object */ - pOut = &aMem[pOp->p3]; - if( pCtx->pOut != pOut ){ - pCtx->pVdbe = p; - pCtx->pOut = pOut; - for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i]; + if(default_provider != NULL && default_provider != p) { + /* only free the current registerd provider if it has been initialized + and it isn't a pointer to the same provider passed to the function + (i.e. protect against a caller calling register twice for the same provider) */ + sqlcipher_free(default_provider, sizeof(sqlcipher_provider)); } - assert( pCtx->pVdbe==p ); + default_provider = p; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_register_provider: leaving SQLCIPHER_MUTEX_PROVIDER"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_register_provider: left SQLCIPHER_MUTEX_PROVIDER"); - memAboutToChange(p, pOut); -#ifdef SQLITE_DEBUG - for(i=0; iargc; i++){ - assert( memIsValid(pCtx->argv[i]) ); - REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]); - } -#endif - MemSetTypeFlag(pOut, MEM_Null); - assert( pCtx->isError==0 ); - (*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */ + return SQLITE_OK; +} - /* If the function returned an error, throw an exception */ - if( pCtx->isError ){ - if( pCtx->isError>0 ){ - sqlcipher_sqlite3VdbeError(p, "%s", sqlcipher_sqlite3_value_text(pOut)); - rc = pCtx->isError; +/* return a pointer to the currently registered provider. This will + allow an application to fetch the current registered provider and + make minor changes to it */ +sqlcipher_provider* sqlcipher_get_provider() { + return default_provider; +} + +void sqlcipher_activate() { + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_activate: entering static master mutex"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_activate: entered static master mutex"); + + /* allocate new mutexes */ + if(sqlcipher_activate_count == 0) { + int i; + for(i = 0; i < SQLCIPHER_MUTEX_COUNT; i++) { + sqlcipher_static_mutex[i] = sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); } - sqlcipher_sqlite3VdbeDeleteAuxData(db, &p->pAuxData, pCtx->iOp, pOp->p1); - pCtx->isError = 0; - if( rc ) goto abort_due_to_error; } - /* Copy the result of the function into register P3 */ - if( pOut->flags & (MEM_Str|MEM_Blob) ){ - sqlcipher_sqlite3VdbeChangeEncoding(pOut, encoding); - if( sqlcipher_sqlite3VdbeMemTooBig(pOut) ) goto too_big; + /* check to see if there is a provider registered at this point + if there no provider registered at this point, register the + default provider */ + if(sqlcipher_get_provider() == NULL) { + sqlcipher_provider *p = sqlcipher_malloc(sizeof(sqlcipher_provider)); +#if defined (SQLCIPHER_CRYPTO_CC) + extern int sqlcipher_cc_setup(sqlcipher_provider *p); + sqlcipher_cc_setup(p); +#elif defined (SQLCIPHER_CRYPTO_LIBTOMCRYPT) + extern int sqlcipher_ltc_setup(sqlcipher_provider *p); + sqlcipher_ltc_setup(p); +#elif defined (SQLCIPHER_CRYPTO_NSS) + extern int sqlcipher_nss_setup(sqlcipher_provider *p); + sqlcipher_nss_setup(p); +#elif defined (SQLCIPHER_CRYPTO_OPENSSL) + extern int sqlcipher_openssl_setup(sqlcipher_provider *p); + sqlcipher_openssl_setup(p); +#else +#error "NO DEFAULT SQLCIPHER CRYPTO PROVIDER DEFINED" +#endif + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_activate: calling sqlcipher_register_provider(%p)", p); +#ifdef SQLCIPHER_EXT + sqlcipher_ext_provider_setup(p); +#endif + sqlcipher_register_provider(p); + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_activate: called sqlcipher_register_provider(%p)",p); } - REGISTER_TRACE(pOp->p3, pOut); - UPDATE_MAX_BLOBSIZE(pOut); - break; + sqlcipher_activate_count++; /* increment activation count */ + + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_activate: leaving static master mutex"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_activate: left static master mutex"); } -/* Opcode: Trace P1 P2 * P4 * -** -** Write P4 on the statement trace output if statement tracing is -** enabled. -** -** Operand P1 must be 0x7fffffff and P2 must positive. -*/ -/* Opcode: Init P1 P2 P3 P4 * -** Synopsis: Start at P2 -** -** Programs contain a single instance of this opcode as the very first -** opcode. -** -** If tracing is enabled (by the sqlcipher_sqlite3_trace()) interface, then -** the UTF-8 string contained in P4 is emitted on the trace callback. -** Or if P4 is blank, use the string returned by sqlcipher_sqlite3_sql(). -** -** If P2 is not zero, jump to instruction P2. -** -** Increment the value of P1 so that OP_Once opcodes will jump the -** first time they are evaluated for this run. -** -** If P3 is not zero, then it is an address to jump to if an SQLITE_CORRUPT -** error is encountered. -*/ -case OP_Trace: -case OP_Init: { /* jump */ - int i; -#ifndef SQLITE_OMIT_TRACE - char *zTrace; -#endif +void sqlcipher_deactivate() { + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_deactivate: entering static master mutex"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_deactivate: entered static master mutex"); - /* If the P4 argument is not NULL, then it must be an SQL comment string. - ** The "--" string is broken up to prevent false-positives with srcck1.c. - ** - ** This assert() provides evidence for: - ** EVIDENCE-OF: R-50676-09860 The callback can compute the same text that - ** would have been returned by the legacy sqlcipher_sqlite3_trace() interface by - ** using the X argument when X begins with "--" and invoking - ** sqlcipher_sqlite3_expanded_sql(P) otherwise. - */ - assert( pOp->p4.z==0 || strncmp(pOp->p4.z, "-" "- ", 3)==0 ); + sqlcipher_activate_count--; + /* if no connections are using sqlcipher, cleanup globals */ + if(sqlcipher_activate_count < 1) { + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_deactivate: entering SQLCIPHER_MUTEX_PROVIDER"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_deactivate: entered SQLCIPHER_MUTEX_PROVIDER"); - /* OP_Init is always instruction 0 */ - assert( pOp==p->aOp || pOp->opcode==OP_Trace ); + if(default_provider != NULL) { + sqlcipher_free(default_provider, sizeof(sqlcipher_provider)); + default_provider = NULL; + } -#ifndef SQLITE_OMIT_TRACE - if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0 - && !p->doingRerun - && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 - ){ -#ifndef SQLITE_OMIT_DEPRECATED - if( db->mTrace & SQLITE_TRACE_LEGACY ){ - char *z = sqlcipher_sqlite3VdbeExpandSql(p, zTrace); - db->trace.xLegacy(db->pTraceArg, z); - sqlcipher_sqlite3_free(z); - }else + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_deactivate: leaving SQLCIPHER_MUTEX_PROVIDER"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_deactivate: left SQLCIPHER_MUTEX_PROVIDER"); + +#ifdef SQLCIPHER_EXT + sqlcipher_ext_provider_destroy(); #endif - if( db->nVdbeExec>1 ){ - char *z = sqlcipher_sqlite3MPrintf(db, "-- %s", zTrace); - (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, z); - sqlcipher_sqlite3DbFree(db, z); - }else{ - (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace); - } - } -#ifdef SQLITE_USE_FCNTL_TRACE - zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); - if( zTrace ){ - int j; - for(j=0; jnDb; j++){ - if( DbMaskTest(p->btreeMask, j)==0 ) continue; - sqlcipher_sqlite3_file_control(db, db->aDb[j].zDbSName, SQLITE_FCNTL_TRACE, zTrace); - } - } -#endif /* SQLITE_USE_FCNTL_TRACE */ -#ifdef SQLITE_DEBUG - if( (db->flags & SQLITE_SqlTrace)!=0 - && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 - ){ - sqlcipher_sqlite3DebugPrintf("SQL-trace: %s\n", zTrace); - } -#endif /* SQLITE_DEBUG */ -#endif /* SQLITE_OMIT_TRACE */ - assert( pOp->p2>0 ); - if( pOp->p1>=sqlcipher_sqlite3GlobalConfig.iOnceResetThreshold ){ - if( pOp->opcode==OP_Trace ) break; - for(i=1; inOp; i++){ - if( p->aOp[i].opcode==OP_Once ) p->aOp[i].p1 = 0; + + /* last connection closed, free mutexes */ + if(sqlcipher_activate_count == 0) { + int i; + for(i = 0; i < SQLCIPHER_MUTEX_COUNT; i++) { + sqlcipher_sqlite3_mutex_free(sqlcipher_static_mutex[i]); + } } - pOp->p1 = 0; + sqlcipher_activate_count = 0; /* reset activation count */ } - pOp->p1++; - p->aCounter[SQLITE_STMTSTATUS_RUN]++; - goto jump_to_p2; + + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_deactivate: leaving static master mutex"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_deactivate: left static master mutex"); } -#ifdef SQLITE_ENABLE_CURSOR_HINTS -/* Opcode: CursorHint P1 * * P4 * -** -** Provide a hint to cursor P1 that it only needs to return rows that -** satisfy the Expr in P4. TK_REGISTER terms in the P4 expression refer -** to values currently held in registers. TK_COLUMN terms in the P4 -** expression refer to columns in the b-tree to which cursor P1 is pointing. +/* constant time memset using volitile to avoid having the memset + optimized out by the compiler. + Note: As suggested by Joachim Schipper (joachim.schipper@fox-it.com) */ -case OP_CursorHint: { - VdbeCursor *pC; +void* sqlcipher_memset(void *v, unsigned char value, sqlite_uint64 len) { + sqlite_uint64 i = 0; + volatile unsigned char *a = v; - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p4type==P4_EXPR ); - pC = p->apCsr[pOp->p1]; - if( pC ){ - assert( pC->eCurType==CURTYPE_BTREE ); - sqlcipher_sqlite3BtreeCursorHint(pC->uc.pCursor, BTREE_HINT_RANGE, - pOp->p4.pExpr, aMem); + if (v == NULL) return v; + + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_memset: setting %p[0-%llu]=%d)", a, len, value); + for(i = 0; i < len; i++) { + a[i] = value; } - break; -} -#endif /* SQLITE_ENABLE_CURSOR_HINTS */ -#ifdef SQLITE_DEBUG -/* Opcode: Abortable * * * * * -** -** Verify that an Abort can happen. Assert if an Abort at this point -** might cause database corruption. This opcode only appears in debugging -** builds. -** -** An Abort is safe if either there have been no writes, or if there is -** an active statement journal. -*/ -case OP_Abortable: { - sqlcipher_sqlite3VdbeAssertAbortable(p); - break; + return v; } -#endif -#ifdef SQLITE_DEBUG -/* Opcode: ReleaseReg P1 P2 P3 * P5 -** Synopsis: release r[P1@P2] mask P3 -** -** Release registers from service. Any content that was in the -** the registers is unreliable after this opcode completes. -** -** The registers released will be the P2 registers starting at P1, -** except if bit ii of P3 set, then do not release register P1+ii. -** In other words, P3 is a mask of registers to preserve. -** -** Releasing a register clears the Mem.pScopyFrom pointer. That means -** that if the content of the released register was set using OP_SCopy, -** a change to the value of the source register for the OP_SCopy will no longer -** generate an assertion fault in sqlcipher_sqlite3VdbeMemAboutToChange(). -** -** If P5 is set, then all released registers have their type set -** to MEM_Undefined so that any subsequent attempt to read the released -** register (before it is reinitialized) will generate an assertion fault. -** -** P5 ought to be set on every call to this opcode. -** However, there are places in the code generator will release registers -** before their are used, under the (valid) assumption that the registers -** will not be reallocated for some other purpose before they are used and -** hence are safe to release. -** -** This opcode is only available in testing and debugging builds. It is -** not generated for release builds. The purpose of this opcode is to help -** validate the generated bytecode. This opcode does not actually contribute -** to computing an answer. -*/ -case OP_ReleaseReg: { - Mem *pMem; - int i; - u32 constMask; - assert( pOp->p1>0 ); - assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); - pMem = &aMem[pOp->p1]; - constMask = pOp->p3; - for(i=0; ip2; i++, pMem++){ - if( i>=32 || (constMask & MASKBIT32(i))==0 ){ - pMem->pScopyFrom = 0; - if( i<32 && pOp->p5 ) MemSetTypeFlag(pMem, MEM_Undefined); - } +/* constant time memory check tests every position of a memory segement + matches a single value (i.e. the memory is all zeros) + returns 0 if match, 1 of no match */ +int sqlcipher_ismemset(const void *v, unsigned char value, sqlite_uint64 len) { + const unsigned char *a = v; + sqlite_uint64 i = 0, result = 0; + + for(i = 0; i < len; i++) { + result |= a[i] ^ value; } - break; + + return (result != 0); } -#endif -/* Opcode: Noop * * * * * -** -** Do nothing. This instruction is often useful as a jump -** destination. -*/ -/* -** The magic Explain opcode are only inserted when explain==2 (which -** is to say when the EXPLAIN QUERY PLAN syntax is used.) -** This opcode records information from the optimizer. It is the -** the same as a no-op. This opcodesnever appears in a real VM program. -*/ -default: { /* This is really OP_Noop, OP_Explain */ - assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain ); +/* constant time memory comparison routine. + returns 0 if match, 1 if no match */ +int sqlcipher_memcmp(const void *v0, const void *v1, int len) { + const unsigned char *a0 = v0, *a1 = v1; + int i = 0, result = 0; - break; + for(i = 0; i < len; i++) { + result |= a0[i] ^ a1[i]; + } + + return (result != 0); } -/***************************************************************************** -** The cases of the switch statement above this line should all be indented -** by 6 spaces. But the left-most 6 spaces have been removed to improve the -** readability. From this point on down, the normal indentation rules are -** restored. -*****************************************************************************/ - } +void sqlcipher_mlock(void *ptr, sqlite_uint64 sz) { +#ifndef OMIT_MEMLOCK +#if defined(__unix__) || defined(__APPLE__) + int rc; + unsigned long pagesize = sysconf(_SC_PAGESIZE); + unsigned long offset = (unsigned long) ptr % pagesize; -#ifdef VDBE_PROFILE - { - u64 endTime = sqlcipher_sqlite3NProfileCnt ? sqlcipher_sqlite3NProfileCnt : sqlcipher_sqlite3Hwtime(); - if( endTime>start ) pOrigOp->cycles += endTime - start; - pOrigOp->cnt++; - } + if(ptr == NULL || sz == 0) return; + + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_mem_lock: calling mlock(%p,%lu); _SC_PAGESIZE=%lu", ptr - offset, sz + offset, pagesize); + rc = mlock(ptr - offset, sz + offset); + if(rc!=0) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_mem_lock: mlock(%p,%lu) returned %d errno=%d", ptr - offset, sz + offset, rc, errno); + } +#elif defined(_WIN32) +#if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP)) + int rc; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_mem_lock: calling VirtualLock(%p,%d)", ptr, sz); + rc = VirtualLock(ptr, sz); + if(rc==0) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_mem_lock: VirtualLock(%p,%d) returned %d LastError=%d", ptr, sz, rc, GetLastError()); + } +#endif +#endif #endif +} - /* The following code adds nothing to the actual functionality - ** of the program. It is only here for testing and debugging. - ** On the other hand, it does burn CPU cycles every time through - ** the evaluator loop. So we can leave it out when NDEBUG is defined. - */ -#ifndef NDEBUG - assert( pOp>=&aOp[-1] && pOp<&aOp[p->nOp-1] ); +void sqlcipher_munlock(void *ptr, sqlite_uint64 sz) { +#ifndef OMIT_MEMLOCK +#if defined(__unix__) || defined(__APPLE__) + int rc; + unsigned long pagesize = sysconf(_SC_PAGESIZE); + unsigned long offset = (unsigned long) ptr % pagesize; -#ifdef SQLITE_DEBUG - if( db->flags & SQLITE_VdbeTrace ){ - u8 opProperty = sqlcipher_sqlite3OpcodeProperty[pOrigOp->opcode]; - if( rc!=0 ) printf("rc=%d\n",rc); - if( opProperty & (OPFLG_OUT2) ){ - registerTrace(pOrigOp->p2, &aMem[pOrigOp->p2]); - } - if( opProperty & OPFLG_OUT3 ){ - registerTrace(pOrigOp->p3, &aMem[pOrigOp->p3]); - } - if( opProperty==0xff ){ - /* Never happens. This code exists to avoid a harmless linkage - ** warning aboud sqlcipher_sqlite3VdbeRegisterDump() being defined but not - ** used. */ - sqlcipher_sqlite3VdbeRegisterDump(p); - } - } -#endif /* SQLITE_DEBUG */ -#endif /* NDEBUG */ - } /* The end of the for(;;) loop the loops through opcodes */ + if(ptr == NULL || sz == 0) return; - /* If we reach this point, it means that execution is finished with - ** an error of some kind. - */ -abort_due_to_error: - if( db->mallocFailed ){ - rc = SQLITE_NOMEM_BKPT; - }else if( rc==SQLITE_IOERR_CORRUPTFS ){ - rc = SQLITE_CORRUPT_BKPT; - } - assert( rc ); - if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ - sqlcipher_sqlite3VdbeError(p, "%s", sqlcipher_sqlite3ErrStr(rc)); - } - p->rc = rc; - sqlcipher_sqlite3SystemError(db, rc); - testcase( sqlcipher_sqlite3GlobalConfig.xLog!=0 ); - sqlcipher_sqlite3_log(rc, "statement aborts at %d: [%s] %s", - (int)(pOp - aOp), p->zSql, p->zErrMsg); - sqlcipher_sqlite3VdbeHalt(p); - if( rc==SQLITE_IOERR_NOMEM ) sqlcipher_sqlite3OomFault(db); - rc = SQLITE_ERROR; - if( resetSchemaOnFault>0 ){ - sqlcipher_sqlite3ResetOneSchema(db, resetSchemaOnFault-1); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_mem_unlock: calling munlock(%p,%lu)", ptr - offset, sz + offset); + rc = munlock(ptr - offset, sz + offset); + if(rc!=0) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_mem_unlock: munlock(%p,%lu) returned %d errno=%d", ptr - offset, sz + offset, rc, errno); } - - /* This is the only way out of this procedure. We have to - ** release the mutexes on btrees that were acquired at the - ** top. */ -vdbe_return: -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ - nProgressLimit += db->nProgressOps; - if( db->xProgress(db->pProgressArg) ){ - nProgressLimit = LARGEST_UINT64; - rc = SQLITE_INTERRUPT; - goto abort_due_to_error; - } +#elif defined(_WIN32) +#if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP)) + int rc; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_mem_lock: calling VirtualUnlock(%p,%d)", ptr, sz); + rc = VirtualUnlock(ptr, sz); + if(!rc) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_mem_unlock: VirtualUnlock(%p,%d) returned %d LastError=%d", ptr, sz, rc, GetLastError()); } #endif - p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep; - sqlcipher_sqlite3VdbeLeave(p); - assert( rc!=SQLITE_OK || nExtraDelete==0 - || sqlcipher_sqlite3_strlike("DELETE%",p->zSql,0)!=0 - ); - return rc; - - /* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH - ** is encountered. - */ -too_big: - sqlcipher_sqlite3VdbeError(p, "string or blob too big"); - rc = SQLITE_TOOBIG; - goto abort_due_to_error; +#endif +#endif +} - /* Jump to here if a malloc() fails. +/** + * Free and wipe memory. Uses SQLites internal sqlcipher_sqlite3_free so that memory + * can be countend and memory leak detection works in the test suite. + * If ptr is not null memory will be freed. + * If sz is greater than zero, the memory will be overwritten with zero before it is freed + * If sz is > 0, and not compiled with OMIT_MEMLOCK, system will attempt to unlock the + * memory segment so it can be paged */ -no_mem: - sqlcipher_sqlite3OomFault(db); - sqlcipher_sqlite3VdbeError(p, "out of memory"); - rc = SQLITE_NOMEM_BKPT; - goto abort_due_to_error; +void sqlcipher_free(void *ptr, sqlite_uint64 sz) { + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_free: calling sqlcipher_memset(%p,0,%llu)", ptr, sz); + sqlcipher_memset(ptr, 0, sz); + sqlcipher_munlock(ptr, sz); + sqlcipher_sqlite3_free(ptr); +} - /* Jump to here if the sqlcipher_sqlite3_interrupt() API sets the interrupt - ** flag. +/** + * allocate memory. Uses sqlite's internall malloc wrapper so memory can be + * reference counted and leak detection works. Unless compiled with OMIT_MEMLOCK + * attempts to lock the memory pages so sensitive information won't be swapped */ -abort_due_to_interrupt: - assert( AtomicLoad(&db->u1.isInterrupted) ); - rc = SQLITE_INTERRUPT; - goto abort_due_to_error; +void* sqlcipher_malloc(sqlite_uint64 sz) { + void *ptr; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_malloc: calling sqlcipher_sqlite3Malloc(%llu)", sz); + ptr = sqlcipher_sqlite3Malloc(sz); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_malloc: calling sqlcipher_memset(%p,0,%llu)", ptr, sz); + sqlcipher_memset(ptr, 0, sz); + sqlcipher_mlock(ptr, sz); + return ptr; } +char* sqlcipher_version() { +#ifdef CIPHER_VERSION_QUALIFIER + char *version = sqlcipher_sqlite3_mprintf("%s %s %s", CIPHER_XSTR(CIPHER_VERSION_NUMBER), CIPHER_XSTR(CIPHER_VERSION_QUALIFIER), CIPHER_XSTR(CIPHER_VERSION_BUILD)); +#else + char *version = sqlcipher_sqlite3_mprintf("%s %s", CIPHER_XSTR(CIPHER_VERSION_NUMBER), CIPHER_XSTR(CIPHER_VERSION_BUILD)); +#endif + return version; +} -/************** End of vdbe.c ************************************************/ -/************** Begin file vdbeblob.c ****************************************/ -/* -** 2007 May 1 -** -** 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 used to implement incremental BLOB I/O. -*/ - -/* #include "sqliteInt.h" */ -/* #include "vdbeInt.h" */ +/** + * Initialize new cipher_ctx struct. This function will allocate memory + * for the cipher context and for the key + * + * returns SQLITE_OK if initialization was successful + * returns SQLITE_NOMEM if an error occured allocating memory + */ +static int sqlcipher_cipher_ctx_init(codec_ctx *ctx, cipher_ctx **iCtx) { + cipher_ctx *c_ctx; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_cipher_ctx_init: allocating context"); + *iCtx = (cipher_ctx *) sqlcipher_malloc(sizeof(cipher_ctx)); + c_ctx = *iCtx; + if(c_ctx == NULL) return SQLITE_NOMEM; -#ifndef SQLITE_OMIT_INCRBLOB + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_cipher_ctx_init: allocating key"); + c_ctx->key = (unsigned char *) sqlcipher_malloc(ctx->key_sz); -/* -** Valid sqlcipher_sqlite3_blob* handles point to Incrblob structures. -*/ -typedef struct Incrblob Incrblob; -struct Incrblob { - int nByte; /* Size of open blob, in bytes */ - int iOffset; /* Byte offset of blob in cursor data */ - u16 iCol; /* Table column this handle is open on */ - BtCursor *pCsr; /* Cursor pointing at blob row */ - sqlcipher_sqlite3_stmt *pStmt; /* Statement holding cursor open */ - sqlcipher_sqlite3 *db; /* The associated database */ - char *zDb; /* Database name */ - Table *pTab; /* Table object */ -}; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_cipher_ctx_init: allocating hmac_key"); + c_ctx->hmac_key = (unsigned char *) sqlcipher_malloc(ctx->key_sz); + if(c_ctx->key == NULL) return SQLITE_NOMEM; + if(c_ctx->hmac_key == NULL) return SQLITE_NOMEM; -/* -** This function is used by both blob_open() and blob_reopen(). It seeks -** the b-tree cursor associated with blob handle p to point to row iRow. -** If successful, SQLITE_OK is returned and subsequent calls to -** sqlcipher_sqlite3_blob_read() or sqlcipher_sqlite3_blob_write() access the specified row. -** -** If an error occurs, or if the specified row does not exist or does not -** contain a value of type TEXT or BLOB in the column nominated when the -** blob handle was opened, then an error code is returned and *pzErr may -** be set to point to a buffer containing an error message. It is the -** responsibility of the caller to free the error message buffer using -** sqlcipher_sqlite3DbFree(). -** -** If an error does occur, then the b-tree cursor is closed. All subsequent -** calls to sqlcipher_sqlite3_blob_read(), blob_write() or blob_reopen() will -** immediately return SQLITE_ABORT. -*/ -static int blobSeekToRow(Incrblob *p, sqlcipher_sqlite3_int64 iRow, char **pzErr){ - int rc; /* Error code */ - char *zErr = 0; /* Error message */ - Vdbe *v = (Vdbe *)p->pStmt; + return SQLITE_OK; +} - /* Set the value of register r[1] in the SQL statement to integer iRow. - ** This is done directly as a performance optimization +/** + * Free and wipe memory associated with a cipher_ctx */ - v->aMem[1].flags = MEM_Int; - v->aMem[1].u.i = iRow; +static void sqlcipher_cipher_ctx_free(codec_ctx* ctx, cipher_ctx **iCtx) { + cipher_ctx *c_ctx = *iCtx; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "cipher_ctx_free: iCtx=%p", iCtx); + sqlcipher_free(c_ctx->key, ctx->key_sz); + sqlcipher_free(c_ctx->hmac_key, ctx->key_sz); + sqlcipher_free(c_ctx->pass, c_ctx->pass_sz); + sqlcipher_free(c_ctx->keyspec, ctx->keyspec_sz); + sqlcipher_free(c_ctx, sizeof(cipher_ctx)); +} - /* If the statement has been run before (and is paused at the OP_ResultRow) - ** then back it up to the point where it does the OP_NotExists. This could - ** have been down with an extra OP_Goto, but simply setting the program - ** counter is faster. */ - if( v->pc>4 ){ - v->pc = 4; - assert( v->aOp[v->pc].opcode==OP_NotExists ); - rc = sqlcipher_sqlite3VdbeExec(v); - }else{ - rc = sqlcipher_sqlite3_step(p->pStmt); - } - if( rc==SQLITE_ROW ){ - VdbeCursor *pC = v->apCsr[0]; - u32 type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0; - testcase( pC->nHdrParsed==p->iCol ); - testcase( pC->nHdrParsed==p->iCol+1 ); - if( type<12 ){ - zErr = sqlcipher_sqlite3MPrintf(p->db, "cannot open value of type %s", - type==0?"null": type==7?"real": "integer" - ); - rc = SQLITE_ERROR; - sqlcipher_sqlite3_finalize(p->pStmt); - p->pStmt = 0; - }else{ - p->iOffset = pC->aType[p->iCol + pC->nField]; - p->nByte = sqlcipher_sqlite3VdbeSerialTypeLen(type); - p->pCsr = pC->uc.pCursor; - sqlcipher_sqlite3BtreeIncrblobCursor(p->pCsr); - } - } +static int sqlcipher_codec_ctx_reserve_setup(codec_ctx *ctx) { + int base_reserve = ctx->iv_sz; /* base reserve size will be IV only */ + int reserve = base_reserve; - if( rc==SQLITE_ROW ){ - rc = SQLITE_OK; - }else if( p->pStmt ){ - rc = sqlcipher_sqlite3_finalize(p->pStmt); - p->pStmt = 0; - if( rc==SQLITE_OK ){ - zErr = sqlcipher_sqlite3MPrintf(p->db, "no such rowid: %lld", iRow); - rc = SQLITE_ERROR; - }else{ - zErr = sqlcipher_sqlite3MPrintf(p->db, "%s", sqlcipher_sqlite3_errmsg(p->db)); - } - } + ctx->hmac_sz = ctx->provider->get_hmac_sz(ctx->provider_ctx, ctx->hmac_algorithm); - assert( rc!=SQLITE_OK || zErr==0 ); - assert( rc!=SQLITE_ROW && rc!=SQLITE_DONE ); + if(sqlcipher_codec_ctx_get_use_hmac(ctx)) + reserve += ctx->hmac_sz; /* if reserve will include hmac, update that size */ - *pzErr = zErr; - return rc; -} + /* calculate the amount of reserve needed in even increments of the cipher block size */ + reserve = ((reserve % ctx->block_sz) == 0) ? reserve : + ((reserve / ctx->block_sz) + 1) * ctx->block_sz; -/* -** Open a blob handle. -*/ -SQLITE_API int sqlcipher_sqlite3_blob_open( - sqlcipher_sqlite3* db, /* The database connection */ - const char *zDb, /* The attached database containing the blob */ - const char *zTable, /* The table containing the blob */ - const char *zColumn, /* The column containing the blob */ - sqlite_int64 iRow, /* The row containing the glob */ - int wrFlag, /* True -> read/write access, false -> read-only */ - sqlcipher_sqlite3_blob **ppBlob /* Handle for accessing the blob returned here */ -){ - int nAttempt = 0; - int iCol; /* Index of zColumn in row-record */ - int rc = SQLITE_OK; - char *zErr = 0; - Table *pTab; - Incrblob *pBlob = 0; - Parse sParse; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_codec_ctx_reserve_setup: base_reserve=%d block_sz=%d md_size=%d reserve=%d", + base_reserve, ctx->block_sz, ctx->hmac_sz, reserve); -#ifdef SQLITE_ENABLE_API_ARMOR - if( ppBlob==0 ){ - return SQLITE_MISUSE_BKPT; - } -#endif - *ppBlob = 0; -#ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlcipher_sqlite3SafetyCheckOk(db) || zTable==0 ){ - return SQLITE_MISUSE_BKPT; - } -#endif - wrFlag = !!wrFlag; /* wrFlag = (wrFlag ? 1 : 0); */ + ctx->reserve_sz = reserve; - sqlcipher_sqlite3_mutex_enter(db->mutex); + return SQLITE_OK; +} - pBlob = (Incrblob *)sqlcipher_sqlite3DbMallocZero(db, sizeof(Incrblob)); - do { - memset(&sParse, 0, sizeof(Parse)); - if( !pBlob ) goto blob_open_out; - sParse.db = db; - sqlcipher_sqlite3DbFree(db, zErr); - zErr = 0; +/** + * Compare one cipher_ctx to another. + * + * returns 0 if all the parameters (except the derived key data) are the same + * returns 1 otherwise + */ +static int sqlcipher_cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) { + int are_equal = ( + c1->pass_sz == c2->pass_sz + && ( + c1->pass == c2->pass + || !sqlcipher_memcmp((const unsigned char*)c1->pass, + (const unsigned char*)c2->pass, + c1->pass_sz) + )); - sqlcipher_sqlite3BtreeEnterAll(db); - pTab = sqlcipher_sqlite3LocateTable(&sParse, 0, zTable, zDb); - if( pTab && IsVirtual(pTab) ){ - pTab = 0; - sqlcipher_sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable); - } - if( pTab && !HasRowid(pTab) ){ - pTab = 0; - sqlcipher_sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable); - } -#ifndef SQLITE_OMIT_VIEW - if( pTab && pTab->pSelect ){ - pTab = 0; - sqlcipher_sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); - } -#endif - if( !pTab ){ - if( sParse.zErrMsg ){ - sqlcipher_sqlite3DbFree(db, zErr); - zErr = sParse.zErrMsg; - sParse.zErrMsg = 0; - } - rc = SQLITE_ERROR; - sqlcipher_sqlite3BtreeLeaveAll(db); - goto blob_open_out; - } - pBlob->pTab = pTab; - pBlob->zDb = db->aDb[sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_cipher_ctx_cmp: c1=%p c2=%p sqlcipher_memcmp(c1->pass, c2_pass)=%d are_equal=%d", + c1, c2, + (c1->pass == NULL || c2->pass == NULL) ? + -1 : + sqlcipher_memcmp( + (const unsigned char*)c1->pass, + (const unsigned char*)c2->pass, + c1->pass_sz + ), + are_equal + ); - /* Now search pTab for the exact column. */ - for(iCol=0; iColnCol; iCol++) { - if( sqlcipher_sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ - break; - } - } - if( iCol==pTab->nCol ){ - sqlcipher_sqlite3DbFree(db, zErr); - zErr = sqlcipher_sqlite3MPrintf(db, "no such column: \"%s\"", zColumn); - rc = SQLITE_ERROR; - sqlcipher_sqlite3BtreeLeaveAll(db); - goto blob_open_out; - } + return !are_equal; /* return 0 if they are the same, 1 otherwise */ +} - /* If the value is being opened for writing, check that the - ** column is not indexed, and that it is not part of a foreign key. - */ - if( wrFlag ){ - const char *zFault = 0; - Index *pIdx; -#ifndef SQLITE_OMIT_FOREIGN_KEY - if( db->flags&SQLITE_ForeignKeys ){ - /* Check that the column is not part of an FK child key definition. It - ** is not necessary to check if it is part of a parent key, as parent - ** key columns must be indexed. The check below will pick up this - ** case. */ - FKey *pFKey; - for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ - int j; - for(j=0; jnCol; j++){ - if( pFKey->aCol[j].iFrom==iCol ){ - zFault = "foreign key"; - } - } - } - } -#endif - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int j; - for(j=0; jnKeyCol; j++){ - /* FIXME: Be smarter about indexes that use expressions */ - if( pIdx->aiColumn[j]==iCol || pIdx->aiColumn[j]==XN_EXPR ){ - zFault = "indexed"; - } - } - } - if( zFault ){ - sqlcipher_sqlite3DbFree(db, zErr); - zErr = sqlcipher_sqlite3MPrintf(db, "cannot open %s column for writing", zFault); - rc = SQLITE_ERROR; - sqlcipher_sqlite3BtreeLeaveAll(db); - goto blob_open_out; - } - } +/** + * Copy one cipher_ctx to another. For instance, assuming that read_ctx is a + * fully initialized context, you could copy it to write_ctx and all yet data + * and pass information across + * + * returns SQLITE_OK if initialization was successful + * returns SQLITE_NOMEM if an error occured allocating memory + */ +static int sqlcipher_cipher_ctx_copy(codec_ctx *ctx, cipher_ctx *target, cipher_ctx *source) { + void *key = target->key; + void *hmac_key = target->hmac_key; - pBlob->pStmt = (sqlcipher_sqlite3_stmt *)sqlcipher_sqlite3VdbeCreate(&sParse); - assert( pBlob->pStmt || db->mallocFailed ); - if( pBlob->pStmt ){ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_cipher_ctx_copy: target=%p, source=%p", target, source); + sqlcipher_free(target->pass, target->pass_sz); + sqlcipher_free(target->keyspec, ctx->keyspec_sz); + memcpy(target, source, sizeof(cipher_ctx)); - /* This VDBE program seeks a btree cursor to the identified - ** db/table/row entry. The reason for using a vdbe program instead - ** of writing code to use the b-tree layer directly is that the - ** vdbe program will take advantage of the various transaction, - ** locking and error handling infrastructure built into the vdbe. - ** - ** After seeking the cursor, the vdbe executes an OP_ResultRow. - ** Code external to the Vdbe then "borrows" the b-tree cursor and - ** uses it to implement the blob_read(), blob_write() and - ** blob_bytes() functions. - ** - ** The sqlcipher_sqlite3_blob_close() function finalizes the vdbe program, - ** which closes the b-tree cursor and (possibly) commits the - ** transaction. - */ - static const int iLn = VDBE_OFFSET_LINENO(2); - static const VdbeOpList openBlob[] = { - {OP_TableLock, 0, 0, 0}, /* 0: Acquire a read or write lock */ - {OP_OpenRead, 0, 0, 0}, /* 1: Open a cursor */ - /* blobSeekToRow() will initialize r[1] to the desired rowid */ - {OP_NotExists, 0, 5, 1}, /* 2: Seek the cursor to rowid=r[1] */ - {OP_Column, 0, 0, 1}, /* 3 */ - {OP_ResultRow, 1, 0, 0}, /* 4 */ - {OP_Halt, 0, 0, 0}, /* 5 */ - }; - Vdbe *v = (Vdbe *)pBlob->pStmt; - int iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); - VdbeOp *aOp; + target->key = key; /* restore pointer to previously allocated key data */ + memcpy(target->key, source->key, ctx->key_sz); - sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, - pTab->pSchema->schema_cookie, - pTab->pSchema->iGeneration); - sqlcipher_sqlite3VdbeChangeP5(v, 1); - assert( sqlcipher_sqlite3VdbeCurrentAddr(v)==2 || db->mallocFailed ); - aOp = sqlcipher_sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn); + target->hmac_key = hmac_key; /* restore pointer to previously allocated hmac key data */ + memcpy(target->hmac_key, source->hmac_key, ctx->key_sz); - /* Make sure a mutex is held on the table to be accessed */ - sqlcipher_sqlite3VdbeUsesBtree(v, iDb); + if(source->pass && source->pass_sz) { + target->pass = sqlcipher_malloc(source->pass_sz); + if(target->pass == NULL) return SQLITE_NOMEM; + memcpy(target->pass, source->pass, source->pass_sz); + } + if(source->keyspec) { + target->keyspec = sqlcipher_malloc(ctx->keyspec_sz); + if(target->keyspec == NULL) return SQLITE_NOMEM; + memcpy(target->keyspec, source->keyspec, ctx->keyspec_sz); + } + return SQLITE_OK; +} - if( db->mallocFailed==0 ){ - assert( aOp!=0 ); - /* Configure the OP_TableLock instruction */ -#ifdef SQLITE_OMIT_SHARED_CACHE - aOp[0].opcode = OP_Noop; -#else - aOp[0].p1 = iDb; - aOp[0].p2 = pTab->tnum; - aOp[0].p3 = wrFlag; - sqlcipher_sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); - } - if( db->mallocFailed==0 ){ -#endif +/** + * Set the keyspec for the cipher_ctx + * + * returns SQLITE_OK if assignment was successfull + * returns SQLITE_NOMEM if an error occured allocating memory + */ +static int sqlcipher_cipher_ctx_set_keyspec(codec_ctx *ctx, cipher_ctx *c_ctx, const unsigned char *key) { + /* free, zero existing pointers and size */ + sqlcipher_free(c_ctx->keyspec, ctx->keyspec_sz); + c_ctx->keyspec = NULL; - /* Remove either the OP_OpenWrite or OpenRead. Set the P2 - ** parameter of the other to pTab->tnum. */ - if( wrFlag ) aOp[1].opcode = OP_OpenWrite; - aOp[1].p2 = pTab->tnum; - aOp[1].p3 = iDb; + c_ctx->keyspec = sqlcipher_malloc(ctx->keyspec_sz); + if(c_ctx->keyspec == NULL) return SQLITE_NOMEM; - /* Configure the number of columns. Configure the cursor to - ** think that the table has one more column than it really - ** does. An OP_Column to retrieve this imaginary column will - ** always return an SQL NULL. This is useful because it means - ** we can invoke OP_Column to fill in the vdbe cursors type - ** and offset cache without causing any IO. - */ - aOp[1].p4type = P4_INT32; - aOp[1].p4.i = pTab->nCol+1; - aOp[3].p2 = pTab->nCol; + c_ctx->keyspec[0] = 'x'; + c_ctx->keyspec[1] = '\''; + cipher_bin2hex(key, ctx->key_sz, c_ctx->keyspec + 2); + cipher_bin2hex(ctx->kdf_salt, ctx->kdf_salt_sz, c_ctx->keyspec + (ctx->key_sz * 2) + 2); + c_ctx->keyspec[ctx->keyspec_sz - 1] = '\''; + return SQLITE_OK; +} - sParse.nVar = 0; - sParse.nMem = 1; - sParse.nTab = 1; - sqlcipher_sqlite3VdbeMakeReady(v, &sParse); - } - } +int sqlcipher_codec_get_store_pass(codec_ctx *ctx) { + return ctx->store_pass; +} - pBlob->iCol = iCol; - pBlob->db = db; - sqlcipher_sqlite3BtreeLeaveAll(db); - if( db->mallocFailed ){ - goto blob_open_out; - } - rc = blobSeekToRow(pBlob, iRow, &zErr); - } while( (++nAttempt)store_pass = value; +} + +void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey) { + *zKey = ctx->read_ctx->pass; + *nKey = ctx->read_ctx->pass_sz; +} -blob_open_out: - if( rc==SQLITE_OK && db->mallocFailed==0 ){ - *ppBlob = (sqlcipher_sqlite3_blob *)pBlob; - }else{ - if( pBlob && pBlob->pStmt ) sqlcipher_sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt); - sqlcipher_sqlite3DbFree(db, pBlob); - } - sqlcipher_sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); - sqlcipher_sqlite3DbFree(db, zErr); - sqlcipher_sqlite3ParserReset(&sParse); - rc = sqlcipher_sqlite3ApiExit(db, rc); - sqlcipher_sqlite3_mutex_leave(db->mutex); - return rc; +static void sqlcipher_set_derive_key(codec_ctx *ctx, int derive) { + if(ctx->read_ctx != NULL) ctx->read_ctx->derive_key = 1; + if(ctx->write_ctx != NULL) ctx->write_ctx->derive_key = 1; } -/* -** Close a blob handle that was previously created using -** sqlcipher_sqlite3_blob_open(). -*/ -SQLITE_API int sqlcipher_sqlite3_blob_close(sqlcipher_sqlite3_blob *pBlob){ - Incrblob *p = (Incrblob *)pBlob; - int rc; - sqlcipher_sqlite3 *db; +/** + * Set the passphrase for the cipher_ctx + * + * returns SQLITE_OK if assignment was successfull + * returns SQLITE_NOMEM if an error occured allocating memory + */ +static int sqlcipher_cipher_ctx_set_pass(cipher_ctx *ctx, const void *zKey, int nKey) { + /* free, zero existing pointers and size */ + sqlcipher_free(ctx->pass, ctx->pass_sz); + ctx->pass = NULL; + ctx->pass_sz = 0; - if( p ){ - sqlcipher_sqlite3_stmt *pStmt = p->pStmt; - db = p->db; - sqlcipher_sqlite3_mutex_enter(db->mutex); - sqlcipher_sqlite3DbFree(db, p); - sqlcipher_sqlite3_mutex_leave(db->mutex); - rc = sqlcipher_sqlite3_finalize(pStmt); - }else{ - rc = SQLITE_OK; + if(zKey && nKey) { /* if new password is provided, copy it */ + ctx->pass_sz = nKey; + ctx->pass = sqlcipher_malloc(nKey); + if(ctx->pass == NULL) return SQLITE_NOMEM; + memcpy(ctx->pass, zKey, nKey); } - return rc; + return SQLITE_OK; } -/* -** Perform a read or write operation on a blob -*/ -static int blobReadWrite( - sqlcipher_sqlite3_blob *pBlob, - void *z, - int n, - int iOffset, - int (*xCall)(BtCursor*, u32, u32, void*) -){ +int sqlcipher_codec_ctx_set_pass(codec_ctx *ctx, const void *zKey, int nKey, int for_ctx) { + cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx; int rc; - Incrblob *p = (Incrblob *)pBlob; - Vdbe *v; - sqlcipher_sqlite3 *db; - - if( p==0 ) return SQLITE_MISUSE_BKPT; - db = p->db; - sqlcipher_sqlite3_mutex_enter(db->mutex); - v = (Vdbe*)p->pStmt; - if( n<0 || iOffset<0 || ((sqlcipher_sqlite3_int64)iOffset+n)>p->nByte ){ - /* Request is out of range. Return a transient error. */ - rc = SQLITE_ERROR; - }else if( v==0 ){ - /* If there is no statement handle, then the blob-handle has - ** already been invalidated. Return SQLITE_ABORT in this case. - */ - rc = SQLITE_ABORT; - }else{ - /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is - ** returned, clean-up the statement handle. - */ - assert( db == v->db ); - sqlcipher_sqlite3BtreeEnterCursor(p->pCsr); + if((rc = sqlcipher_cipher_ctx_set_pass(c_ctx, zKey, nKey)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_set_pass: error %d from sqlcipher_cipher_ctx_set_pass", rc); + return rc; + } -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK - if( xCall==sqlcipher_sqlite3BtreePutData && db->xPreUpdateCallback ){ - /* If a pre-update hook is registered and this is a write cursor, - ** invoke it here. - ** - ** TODO: The preupdate-hook is passed SQLITE_DELETE, even though this - ** operation should really be an SQLITE_UPDATE. This is probably - ** incorrect, but is convenient because at this point the new.* values - ** are not easily obtainable. And for the sessions module, an - ** SQLITE_UPDATE where the PK columns do not change is handled in the - ** same way as an SQLITE_DELETE (the SQLITE_DELETE code is actually - ** slightly more efficient). Since you cannot write to a PK column - ** using the incremental-blob API, this works. For the sessions module - ** anyhow. - */ - sqlcipher_sqlite3_int64 iKey; - iKey = sqlcipher_sqlite3BtreeIntegerKey(p->pCsr); - sqlcipher_sqlite3VdbePreUpdateHook( - v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1 - ); - } -#endif + c_ctx->derive_key = 1; - rc = xCall(p->pCsr, iOffset+p->iOffset, n, z); - sqlcipher_sqlite3BtreeLeaveCursor(p->pCsr); - if( rc==SQLITE_ABORT ){ - sqlcipher_sqlite3VdbeFinalize(v); - p->pStmt = 0; - }else{ - v->rc = rc; + if(for_ctx == 2) { + if((rc = sqlcipher_cipher_ctx_copy(ctx, for_ctx ? ctx->read_ctx : ctx->write_ctx, c_ctx)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_set_pass: error %d from sqlcipher_cipher_ctx_copy", rc); + return rc; } } - sqlcipher_sqlite3Error(db, rc); - rc = sqlcipher_sqlite3ApiExit(db, rc); - sqlcipher_sqlite3_mutex_leave(db->mutex); - return rc; + + return SQLITE_OK; } -/* -** Read data from a blob handle. -*/ -SQLITE_API int sqlcipher_sqlite3_blob_read(sqlcipher_sqlite3_blob *pBlob, void *z, int n, int iOffset){ - return blobReadWrite(pBlob, z, n, iOffset, sqlcipher_sqlite3BtreePayloadChecked); +const char* sqlcipher_codec_ctx_get_cipher(codec_ctx *ctx) { + return ctx->provider->get_cipher(ctx->provider_ctx); } -/* -** Write data to a blob handle. -*/ -SQLITE_API int sqlcipher_sqlite3_blob_write(sqlcipher_sqlite3_blob *pBlob, const void *z, int n, int iOffset){ - return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlcipher_sqlite3BtreePutData); +/* set the global default KDF iteration */ +void sqlcipher_set_default_kdf_iter(int iter) { + default_kdf_iter = iter; } -/* -** Query a blob handle for the size of the data. -** -** The Incrblob.nByte field is fixed for the lifetime of the Incrblob -** so no mutex is required for access. -*/ -SQLITE_API int sqlcipher_sqlite3_blob_bytes(sqlcipher_sqlite3_blob *pBlob){ - Incrblob *p = (Incrblob *)pBlob; - return (p && p->pStmt) ? p->nByte : 0; +int sqlcipher_get_default_kdf_iter() { + return default_kdf_iter; } -/* -** Move an existing blob handle to point to a different row of the same -** database table. -** -** If an error occurs, or if the specified row does not exist or does not -** contain a blob or text value, then an error code is returned and the -** database handle error code and message set. If this happens, then all -** subsequent calls to sqlcipher_sqlite3_blob_xxx() functions (except blob_close()) -** immediately return SQLITE_ABORT. -*/ -SQLITE_API int sqlcipher_sqlite3_blob_reopen(sqlcipher_sqlite3_blob *pBlob, sqlcipher_sqlite3_int64 iRow){ - int rc; - Incrblob *p = (Incrblob *)pBlob; - sqlcipher_sqlite3 *db; +int sqlcipher_codec_ctx_set_kdf_iter(codec_ctx *ctx, int kdf_iter) { + ctx->kdf_iter = kdf_iter; + sqlcipher_set_derive_key(ctx, 1); + return SQLITE_OK; +} - if( p==0 ) return SQLITE_MISUSE_BKPT; - db = p->db; - sqlcipher_sqlite3_mutex_enter(db->mutex); +int sqlcipher_codec_ctx_get_kdf_iter(codec_ctx *ctx) { + return ctx->kdf_iter; +} - if( p->pStmt==0 ){ - /* If there is no statement handle, then the blob-handle has - ** already been invalidated. Return SQLITE_ABORT in this case. - */ - rc = SQLITE_ABORT; - }else{ - char *zErr; - rc = blobSeekToRow(p, iRow, &zErr); - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); - sqlcipher_sqlite3DbFree(db, zErr); - } - assert( rc!=SQLITE_SCHEMA ); - } +int sqlcipher_codec_ctx_set_fast_kdf_iter(codec_ctx *ctx, int fast_kdf_iter) { + ctx->fast_kdf_iter = fast_kdf_iter; + sqlcipher_set_derive_key(ctx, 1); + return SQLITE_OK; +} - rc = sqlcipher_sqlite3ApiExit(db, rc); - assert( rc==SQLITE_OK || p->pStmt==0 ); - sqlcipher_sqlite3_mutex_leave(db->mutex); - return rc; +int sqlcipher_codec_ctx_get_fast_kdf_iter(codec_ctx *ctx) { + return ctx->fast_kdf_iter; } -#endif /* #ifndef SQLITE_OMIT_INCRBLOB */ +/* set the global default flag for HMAC */ +void sqlcipher_set_default_use_hmac(int use) { + if(use) default_flags |= CIPHER_FLAG_HMAC; + else default_flags &= ~CIPHER_FLAG_HMAC; +} -/************** End of vdbeblob.c ********************************************/ -/************** Begin file vdbesort.c ****************************************/ -/* -** 2011-07-09 -** -** 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 for the VdbeSorter object, used in concert with -** a VdbeCursor to sort large numbers of keys for CREATE INDEX statements -** or by SELECT statements with ORDER BY clauses that cannot be satisfied -** using indexes and without LIMIT clauses. -** -** The VdbeSorter object implements a multi-threaded external merge sort -** algorithm that is efficient even if the number of elements being sorted -** exceeds the available memory. -** -** Here is the (internal, non-API) interface between this module and the -** rest of the SQLite system: -** -** sqlcipher_sqlite3VdbeSorterInit() Create a new VdbeSorter object. -** -** sqlcipher_sqlite3VdbeSorterWrite() Add a single new row to the VdbeSorter -** object. The row is a binary blob in the -** OP_MakeRecord format that contains both -** the ORDER BY key columns and result columns -** in the case of a SELECT w/ ORDER BY, or -** the complete record for an index entry -** in the case of a CREATE INDEX. -** -** sqlcipher_sqlite3VdbeSorterRewind() Sort all content previously added. -** Position the read cursor on the -** first sorted element. -** -** sqlcipher_sqlite3VdbeSorterNext() Advance the read cursor to the next sorted -** element. -** -** sqlcipher_sqlite3VdbeSorterRowkey() Return the complete binary blob for the -** row currently under the read cursor. -** -** sqlcipher_sqlite3VdbeSorterCompare() Compare the binary blob for the row -** currently under the read cursor against -** another binary blob X and report if -** X is strictly less than the read cursor. -** Used to enforce uniqueness in a -** CREATE UNIQUE INDEX statement. -** -** sqlcipher_sqlite3VdbeSorterClose() Close the VdbeSorter object and reclaim -** all resources. -** -** sqlcipher_sqlite3VdbeSorterReset() Refurbish the VdbeSorter for reuse. This -** is like Close() followed by Init() only -** much faster. -** -** The interfaces above must be called in a particular order. Write() can -** only occur in between Init()/Reset() and Rewind(). Next(), Rowkey(), and -** Compare() can only occur in between Rewind() and Close()/Reset(). i.e. -** -** Init() -** for each record: Write() -** Rewind() -** Rowkey()/Compare() -** Next() -** Close() -** -** Algorithm: -** -** Records passed to the sorter via calls to Write() are initially held -** unsorted in main memory. Assuming the amount of memory used never exceeds -** a threshold, when Rewind() is called the set of records is sorted using -** an in-memory merge sort. In this case, no temporary files are required -** and subsequent calls to Rowkey(), Next() and Compare() read records -** directly from main memory. -** -** If the amount of space used to store records in main memory exceeds the -** threshold, then the set of records currently in memory are sorted and -** written to a temporary file in "Packed Memory Array" (PMA) format. -** A PMA created at this point is known as a "level-0 PMA". Higher levels -** of PMAs may be created by merging existing PMAs together - for example -** merging two or more level-0 PMAs together creates a level-1 PMA. -** -** The threshold for the amount of main memory to use before flushing -** records to a PMA is roughly the same as the limit configured for the -** page-cache of the main database. Specifically, the threshold is set to -** the value returned by "PRAGMA main.page_size" multipled by -** that returned by "PRAGMA main.cache_size", in bytes. -** -** If the sorter is running in single-threaded mode, then all PMAs generated -** are appended to a single temporary file. Or, if the sorter is running in -** multi-threaded mode then up to (N+1) temporary files may be opened, where -** N is the configured number of worker threads. In this case, instead of -** sorting the records and writing the PMA to a temporary file itself, the -** calling thread usually launches a worker thread to do so. Except, if -** there are already N worker threads running, the main thread does the work -** itself. -** -** The sorter is running in multi-threaded mode if (a) the library was built -** with pre-processor symbol SQLITE_MAX_WORKER_THREADS set to a value greater -** than zero, and (b) worker threads have been enabled at runtime by calling -** "PRAGMA threads=N" with some value of N greater than 0. -** -** When Rewind() is called, any data remaining in memory is flushed to a -** final PMA. So at this point the data is stored in some number of sorted -** PMAs within temporary files on disk. -** -** If there are fewer than SORTER_MAX_MERGE_COUNT PMAs in total and the -** sorter is running in single-threaded mode, then these PMAs are merged -** incrementally as keys are retreived from the sorter by the VDBE. The -** MergeEngine object, described in further detail below, performs this -** merge. -** -** Or, if running in multi-threaded mode, then a background thread is -** launched to merge the existing PMAs. Once the background thread has -** merged T bytes of data into a single sorted PMA, the main thread -** begins reading keys from that PMA while the background thread proceeds -** with merging the next T bytes of data. And so on. -** -** Parameter T is set to half the value of the memory threshold used -** by Write() above to determine when to create a new PMA. -** -** If there are more than SORTER_MAX_MERGE_COUNT PMAs in total when -** Rewind() is called, then a hierarchy of incremental-merges is used. -** First, T bytes of data from the first SORTER_MAX_MERGE_COUNT PMAs on -** disk are merged together. Then T bytes of data from the second set, and -** so on, such that no operation ever merges more than SORTER_MAX_MERGE_COUNT -** PMAs at a time. This done is to improve locality. -** -** If running in multi-threaded mode and there are more than -** SORTER_MAX_MERGE_COUNT PMAs on disk when Rewind() is called, then more -** than one background thread may be created. Specifically, there may be -** one background thread for each temporary file on disk, and one background -** thread to merge the output of each of the others to a single PMA for -** the main thread to read from. -*/ -/* #include "sqliteInt.h" */ -/* #include "vdbeInt.h" */ +int sqlcipher_get_default_use_hmac() { + return (default_flags & CIPHER_FLAG_HMAC) != 0; +} -/* -** If SQLITE_DEBUG_SORTER_THREADS is defined, this module outputs various -** messages to stderr that may be helpful in understanding the performance -** characteristics of the sorter in multi-threaded mode. -*/ -#if 0 -# define SQLITE_DEBUG_SORTER_THREADS 1 -#endif +void sqlcipher_set_hmac_salt_mask(unsigned char mask) { + hmac_salt_mask = mask; +} -/* -** Hard-coded maximum amount of data to accumulate in memory before flushing -** to a level 0 PMA. The purpose of this limit is to prevent various integer -** overflows. 512MiB. -*/ -#define SQLITE_MAX_PMASZ (1<<29) +unsigned char sqlcipher_get_hmac_salt_mask() { + return hmac_salt_mask; +} -/* -** Private objects used by the sorter -*/ -typedef struct MergeEngine MergeEngine; /* Merge PMAs together */ -typedef struct PmaReader PmaReader; /* Incrementally read one PMA */ -typedef struct PmaWriter PmaWriter; /* Incrementally write one PMA */ -typedef struct SorterRecord SorterRecord; /* A record being sorted */ -typedef struct SortSubtask SortSubtask; /* A sub-task in the sort process */ -typedef struct SorterFile SorterFile; /* Temporary file object wrapper */ -typedef struct SorterList SorterList; /* In-memory list of records */ -typedef struct IncrMerger IncrMerger; /* Read & merge multiple PMAs */ +/* set the codec flag for whether this individual database should be using hmac */ +int sqlcipher_codec_ctx_set_use_hmac(codec_ctx *ctx, int use) { + if(use) { + sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_HMAC); + } else { + sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_HMAC); + } -/* -** A container for a temp file handle and the current amount of data -** stored in the file. -*/ -struct SorterFile { - sqlcipher_sqlite3_file *pFd; /* File handle */ - i64 iEof; /* Bytes of data stored in pFd */ -}; + return sqlcipher_codec_ctx_reserve_setup(ctx); +} -/* -** An in-memory list of objects to be sorted. -** -** If aMemory==0 then each object is allocated separately and the objects -** are connected using SorterRecord.u.pNext. If aMemory!=0 then all objects -** are stored in the aMemory[] bulk memory, one right after the other, and -** are connected using SorterRecord.u.iNext. -*/ -struct SorterList { - SorterRecord *pList; /* Linked list of records */ - u8 *aMemory; /* If non-NULL, bulk memory to hold pList */ - int szPMA; /* Size of pList as PMA in bytes */ -}; +int sqlcipher_codec_ctx_get_use_hmac(codec_ctx *ctx) { + return (ctx->flags & CIPHER_FLAG_HMAC) != 0; +} -/* -** The MergeEngine object is used to combine two or more smaller PMAs into -** one big PMA using a merge operation. Separate PMAs all need to be -** combined into one big PMA in order to be able to step through the sorted -** records in order. -** -** The aReadr[] array contains a PmaReader object for each of the PMAs being -** merged. An aReadr[] object either points to a valid key or else is at EOF. -** ("EOF" means "End Of File". When aReadr[] is at EOF there is no more data.) -** For the purposes of the paragraphs below, we assume that the array is -** actually N elements in size, where N is the smallest power of 2 greater -** to or equal to the number of PMAs being merged. The extra aReadr[] elements -** are treated as if they are empty (always at EOF). -** -** The aTree[] array is also N elements in size. The value of N is stored in -** the MergeEngine.nTree variable. -** -** The final (N/2) elements of aTree[] contain the results of comparing -** pairs of PMA keys together. Element i contains the result of -** comparing aReadr[2*i-N] and aReadr[2*i-N+1]. Whichever key is smaller, the -** aTree element is set to the index of it. -** -** For the purposes of this comparison, EOF is considered greater than any -** other key value. If the keys are equal (only possible with two EOF -** values), it doesn't matter which index is stored. -** -** The (N/4) elements of aTree[] that precede the final (N/2) described -** above contains the index of the smallest of each block of 4 PmaReaders -** And so on. So that aTree[1] contains the index of the PmaReader that -** currently points to the smallest key value. aTree[0] is unused. -** -** Example: -** -** aReadr[0] -> Banana -** aReadr[1] -> Feijoa -** aReadr[2] -> Elderberry -** aReadr[3] -> Currant -** aReadr[4] -> Grapefruit -** aReadr[5] -> Apple -** aReadr[6] -> Durian -** aReadr[7] -> EOF -** -** aTree[] = { X, 5 0, 5 0, 3, 5, 6 } -** -** The current element is "Apple" (the value of the key indicated by -** PmaReader 5). When the Next() operation is invoked, PmaReader 5 will -** be advanced to the next key in its segment. Say the next key is -** "Eggplant": -** -** aReadr[5] -> Eggplant -** -** The contents of aTree[] are updated first by comparing the new PmaReader -** 5 key to the current key of PmaReader 4 (still "Grapefruit"). The PmaReader -** 5 value is still smaller, so aTree[6] is set to 5. And so on up the tree. -** The value of PmaReader 6 - "Durian" - is now smaller than that of PmaReader -** 5, so aTree[3] is set to 6. Key 0 is smaller than key 6 (Banana= 0 && (size % ctx->block_sz) == 0 && size < (ctx->page_sz - ctx->reserve_sz)) { + ctx->plaintext_header_sz = size; + return SQLITE_OK; + } + ctx->plaintext_header_sz = -1; + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_set_plaintext_header_size: attempt to set invalid plantext_header_size %d", size); + return SQLITE_ERROR; +} +int sqlcipher_get_default_plaintext_header_size() { + return default_plaintext_header_sz; +} -/* -** Main sorter structure. A single instance of this is allocated for each -** sorter cursor created by the VDBE. -** -** mxKeysize: -** As records are added to the sorter by calls to sqlcipher_sqlite3VdbeSorterWrite(), -** this variable is updated so as to be set to the size on disk of the -** largest record in the sorter. -*/ -struct VdbeSorter { - int mnPmaSize; /* Minimum PMA size, in bytes */ - int mxPmaSize; /* Maximum PMA size, in bytes. 0==no limit */ - int mxKeysize; /* Largest serialized key seen so far */ - int pgsz; /* Main database page size */ - PmaReader *pReader; /* Readr data from here after Rewind() */ - MergeEngine *pMerger; /* Or here, if bUseThreads==0 */ - sqlcipher_sqlite3 *db; /* Database connection */ - KeyInfo *pKeyInfo; /* How to compare records */ - UnpackedRecord *pUnpacked; /* Used by VdbeSorterCompare() */ - SorterList list; /* List of in-memory records */ - int iMemory; /* Offset of free space in list.aMemory */ - int nMemory; /* Size of list.aMemory allocation in bytes */ - u8 bUsePMA; /* True if one or more PMAs created */ - u8 bUseThreads; /* True to use background threads */ - u8 iPrev; /* Previous thread used to flush PMA */ - u8 nTask; /* Size of aTask[] array */ - u8 typeMask; - SortSubtask aTask[1]; /* One or more subtasks */ -}; +int sqlcipher_codec_ctx_get_plaintext_header_size(codec_ctx *ctx) { + return ctx->plaintext_header_sz; +} -#define SORTER_TYPE_INTEGER 0x01 -#define SORTER_TYPE_TEXT 0x02 +/* manipulate HMAC algorithm */ +int sqlcipher_set_default_hmac_algorithm(int algorithm) { + default_hmac_algorithm = algorithm; + return SQLITE_OK; +} -/* -** An instance of the following object is used to read records out of a -** PMA, in sorted order. The next key to be read is cached in nKey/aKey. -** aKey might point into aMap or into aBuffer. If neither of those locations -** contain a contiguous representation of the key, then aAlloc is allocated -** and the key is copied into aAlloc and aKey is made to poitn to aAlloc. -** -** pFd==0 at EOF. -*/ -struct PmaReader { - i64 iReadOff; /* Current read offset */ - i64 iEof; /* 1 byte past EOF for this PmaReader */ - int nAlloc; /* Bytes of space at aAlloc */ - int nKey; /* Number of bytes in key */ - sqlcipher_sqlite3_file *pFd; /* File handle we are reading from */ - u8 *aAlloc; /* Space for aKey if aBuffer and pMap wont work */ - u8 *aKey; /* Pointer to current key */ - u8 *aBuffer; /* Current read buffer */ - int nBuffer; /* Size of read buffer in bytes */ - u8 *aMap; /* Pointer to mapping of entire file */ - IncrMerger *pIncr; /* Incremental merger */ -}; +int sqlcipher_codec_ctx_set_hmac_algorithm(codec_ctx *ctx, int algorithm) { + ctx->hmac_algorithm = algorithm; + return sqlcipher_codec_ctx_reserve_setup(ctx); +} -/* -** Normally, a PmaReader object iterates through an existing PMA stored -** within a temp file. However, if the PmaReader.pIncr variable points to -** an object of the following type, it may be used to iterate/merge through -** multiple PMAs simultaneously. -** -** There are two types of IncrMerger object - single (bUseThread==0) and -** multi-threaded (bUseThread==1). -** -** A multi-threaded IncrMerger object uses two temporary files - aFile[0] -** and aFile[1]. Neither file is allowed to grow to more than mxSz bytes in -** size. When the IncrMerger is initialized, it reads enough data from -** pMerger to populate aFile[0]. It then sets variables within the -** corresponding PmaReader object to read from that file and kicks off -** a background thread to populate aFile[1] with the next mxSz bytes of -** sorted record data from pMerger. -** -** When the PmaReader reaches the end of aFile[0], it blocks until the -** background thread has finished populating aFile[1]. It then exchanges -** the contents of the aFile[0] and aFile[1] variables within this structure, -** sets the PmaReader fields to read from the new aFile[0] and kicks off -** another background thread to populate the new aFile[1]. And so on, until -** the contents of pMerger are exhausted. -** -** A single-threaded IncrMerger does not open any temporary files of its -** own. Instead, it has exclusive access to mxSz bytes of space beginning -** at offset iStartOff of file pTask->file2. And instead of using a -** background thread to prepare data for the PmaReader, with a single -** threaded IncrMerger the allocate part of pTask->file2 is "refilled" with -** keys from pMerger by the calling thread whenever the PmaReader runs out -** of data. -*/ -struct IncrMerger { - SortSubtask *pTask; /* Task that owns this merger */ - MergeEngine *pMerger; /* Merge engine thread reads data from */ - i64 iStartOff; /* Offset to start writing file at */ - int mxSz; /* Maximum bytes of data to store */ - int bEof; /* Set to true when merge is finished */ - int bUseThread; /* True to use a bg thread for this object */ - SorterFile aFile[2]; /* aFile[0] for reading, [1] for writing */ -}; +int sqlcipher_get_default_hmac_algorithm() { + return default_hmac_algorithm; +} -/* -** An instance of this object is used for writing a PMA. -** -** The PMA is written one record at a time. Each record is of an arbitrary -** size. But I/O is more efficient if it occurs in page-sized blocks where -** each block is aligned on a page boundary. This object caches writes to -** the PMA so that aligned, page-size blocks are written. -*/ -struct PmaWriter { - int eFWErr; /* Non-zero if in an error state */ - u8 *aBuffer; /* Pointer to write buffer */ - int nBuffer; /* Size of write buffer in bytes */ - int iBufStart; /* First byte of buffer to write */ - int iBufEnd; /* Last byte of buffer to write */ - i64 iWriteOff; /* Offset of start of buffer in file */ - sqlcipher_sqlite3_file *pFd; /* File handle to write to */ -}; +int sqlcipher_codec_ctx_get_hmac_algorithm(codec_ctx *ctx) { + return ctx->hmac_algorithm; +} -/* -** This object is the header on a single record while that record is being -** held in memory and prior to being written out as part of a PMA. -** -** How the linked list is connected depends on how memory is being managed -** by this module. If using a separate allocation for each in-memory record -** (VdbeSorter.list.aMemory==0), then the list is always connected using the -** SorterRecord.u.pNext pointers. -** -** Or, if using the single large allocation method (VdbeSorter.list.aMemory!=0), -** then while records are being accumulated the list is linked using the -** SorterRecord.u.iNext offset. This is because the aMemory[] array may -** be sqlcipher_sqlite3Realloc()ed while records are being accumulated. Once the VM -** has finished passing records to the sorter, or when the in-memory buffer -** is full, the list is sorted. As part of the sorting process, it is -** converted to use the SorterRecord.u.pNext pointers. See function -** vdbeSorterSort() for details. -*/ -struct SorterRecord { - int nVal; /* Size of the record in bytes */ - union { - SorterRecord *pNext; /* Pointer to next record in list */ - int iNext; /* Offset within aMemory of next record */ - } u; - /* The data for the record immediately follows this header */ -}; +/* manipulate KDF algorithm */ +int sqlcipher_set_default_kdf_algorithm(int algorithm) { + default_kdf_algorithm = algorithm; + return SQLITE_OK; +} -/* Return a pointer to the buffer containing the record data for SorterRecord -** object p. Should be used as if: -** -** void *SRVAL(SorterRecord *p) { return (void*)&p[1]; } -*/ -#define SRVAL(p) ((void*)((SorterRecord*)(p) + 1)) +int sqlcipher_codec_ctx_set_kdf_algorithm(codec_ctx *ctx, int algorithm) { + ctx->kdf_algorithm = algorithm; + return SQLITE_OK; +} +int sqlcipher_get_default_kdf_algorithm() { + return default_kdf_algorithm; +} -/* Maximum number of PMAs that a single MergeEngine can merge */ -#define SORTER_MAX_MERGE_COUNT 16 +int sqlcipher_codec_ctx_get_kdf_algorithm(codec_ctx *ctx) { + return ctx->kdf_algorithm; +} -static int vdbeIncrSwap(IncrMerger*); -static void vdbeIncrFree(IncrMerger *); +int sqlcipher_codec_ctx_set_flag(codec_ctx *ctx, unsigned int flag) { + ctx->flags |= flag; + return SQLITE_OK; +} -/* -** Free all memory belonging to the PmaReader object passed as the -** argument. All structure fields are set to zero before returning. -*/ -static void vdbePmaReaderClear(PmaReader *pReadr){ - sqlcipher_sqlite3_free(pReadr->aAlloc); - sqlcipher_sqlite3_free(pReadr->aBuffer); - if( pReadr->aMap ) sqlcipher_sqlite3OsUnfetch(pReadr->pFd, 0, pReadr->aMap); - vdbeIncrFree(pReadr->pIncr); - memset(pReadr, 0, sizeof(PmaReader)); +int sqlcipher_codec_ctx_unset_flag(codec_ctx *ctx, unsigned int flag) { + ctx->flags &= ~flag; + return SQLITE_OK; } -/* -** Read the next nByte bytes of data from the PMA p. -** If successful, set *ppOut to point to a buffer containing the data -** and return SQLITE_OK. Otherwise, if an error occurs, return an SQLite -** error code. -** -** The buffer returned in *ppOut is only valid until the -** next call to this function. -*/ -static int vdbePmaReadBlob( - PmaReader *p, /* PmaReader from which to take the blob */ - int nByte, /* Bytes of data to read */ - u8 **ppOut /* OUT: Pointer to buffer containing data */ -){ - int iBuf; /* Offset within buffer to read from */ - int nAvail; /* Bytes of data available in buffer */ +int sqlcipher_codec_ctx_get_flag(codec_ctx *ctx, unsigned int flag) { + return (ctx->flags & flag) != 0; +} - if( p->aMap ){ - *ppOut = &p->aMap[p->iReadOff]; - p->iReadOff += nByte; - return SQLITE_OK; - } +void sqlcipher_codec_ctx_set_error(codec_ctx *ctx, int error) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_set_error: ctx=%p, error=%d", ctx, error); + sqlcipher_sqlite3pager_error(ctx->pBt->pBt->pPager, error); + ctx->pBt->pBt->db->errCode = error; +} - assert( p->aBuffer ); +int sqlcipher_codec_ctx_get_reservesize(codec_ctx *ctx) { + return ctx->reserve_sz; +} - /* If there is no more data to be read from the buffer, read the next - ** p->nBuffer bytes of data from the file into it. Or, if there are less - ** than p->nBuffer bytes remaining in the PMA, read all remaining data. */ - iBuf = p->iReadOff % p->nBuffer; - if( iBuf==0 ){ - int nRead; /* Bytes to read from disk */ - int rc; /* sqlcipher_sqlite3OsRead() return code */ +void* sqlcipher_codec_ctx_get_data(codec_ctx *ctx) { + return ctx->buffer; +} - /* Determine how many bytes of data to read. */ - if( (p->iEof - p->iReadOff) > (i64)p->nBuffer ){ - nRead = p->nBuffer; - }else{ - nRead = (int)(p->iEof - p->iReadOff); - } - assert( nRead>0 ); +static int sqlcipher_codec_ctx_init_kdf_salt(codec_ctx *ctx) { + sqlcipher_sqlite3_file *fd = sqlcipher_sqlite3PagerFile(ctx->pBt->pBt->pPager); - /* Readr data from the file. Return early if an error occurs. */ - rc = sqlcipher_sqlite3OsRead(p->pFd, p->aBuffer, nRead, p->iReadOff); - assert( rc!=SQLITE_IOERR_SHORT_READ ); - if( rc!=SQLITE_OK ) return rc; + if(!ctx->need_kdf_salt) { + return SQLITE_OK; /* don't reload salt when not needed */ } - nAvail = p->nBuffer - iBuf; - - if( nByte<=nAvail ){ - /* The requested data is available in the in-memory buffer. In this - ** case there is no need to make a copy of the data, just return a - ** pointer into the buffer to the caller. */ - *ppOut = &p->aBuffer[iBuf]; - p->iReadOff += nByte; - }else{ - /* The requested data is not all available in the in-memory buffer. - ** In this case, allocate space at p->aAlloc[] to copy the requested - ** range into. Then return a copy of pointer p->aAlloc to the caller. */ - int nRem; /* Bytes remaining to copy */ - /* Extend the p->aAlloc[] allocation if required. */ - if( p->nAllocnAlloc); - while( nByte>nNew ) nNew = nNew*2; - aNew = sqlcipher_sqlite3Realloc(p->aAlloc, nNew); - if( !aNew ) return SQLITE_NOMEM_BKPT; - p->nAlloc = nNew; - p->aAlloc = aNew; + /* read salt from header, if present, otherwise generate a new random salt */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_codec_ctx_init_kdf_salt: obtaining salt"); + if(fd == NULL || fd->pMethods == 0 || sqlcipher_sqlite3OsRead(fd, ctx->kdf_salt, ctx->kdf_salt_sz, 0) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_codec_ctx_init_kdf_salt: unable to read salt from file header, generating random"); + if(ctx->provider->random(ctx->provider_ctx, ctx->kdf_salt, ctx->kdf_salt_sz) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init_kdf_salt: error retrieving random bytes from provider"); + return SQLITE_ERROR; } + } + ctx->need_kdf_salt = 0; + return SQLITE_OK; +} - /* Copy as much data as is available in the buffer into the start of - ** p->aAlloc[]. */ - memcpy(p->aAlloc, &p->aBuffer[iBuf], nAvail); - p->iReadOff += nAvail; - nRem = nByte - nAvail; - - /* The following loop copies up to p->nBuffer bytes per iteration into - ** the p->aAlloc[] buffer. */ - while( nRem>0 ){ - int rc; /* vdbePmaReadBlob() return code */ - int nCopy; /* Number of bytes to copy */ - u8 *aNext; /* Pointer to buffer to copy data from */ +int sqlcipher_codec_ctx_set_kdf_salt(codec_ctx *ctx, unsigned char *salt, int size) { + if(size >= ctx->kdf_salt_sz) { + memcpy(ctx->kdf_salt, salt, ctx->kdf_salt_sz); + ctx->need_kdf_salt = 0; + return SQLITE_OK; + } + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_set_kdf_salt: attempt to set salt of incorrect size %d", size); + return SQLITE_ERROR; +} - nCopy = nRem; - if( nRem>p->nBuffer ) nCopy = p->nBuffer; - rc = vdbePmaReadBlob(p, nCopy, &aNext); - if( rc!=SQLITE_OK ) return rc; - assert( aNext!=p->aAlloc ); - memcpy(&p->aAlloc[nByte - nRem], aNext, nCopy); - nRem -= nCopy; +int sqlcipher_codec_ctx_get_kdf_salt(codec_ctx *ctx, void** salt) { + int rc = SQLITE_OK; + if(ctx->need_kdf_salt) { + if((rc = sqlcipher_codec_ctx_init_kdf_salt(ctx)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_get_kdf_salt: error %d from sqlcipher_codec_ctx_init_kdf_salt", rc); } - - *ppOut = p->aAlloc; } + *salt = ctx->kdf_salt; - return SQLITE_OK; + return rc; } -/* -** Read a varint from the stream of data accessed by p. Set *pnOut to -** the value read. -*/ -static int vdbePmaReadVarint(PmaReader *p, u64 *pnOut){ - int iBuf; +void sqlcipher_codec_get_keyspec(codec_ctx *ctx, void **zKey, int *nKey) { + *zKey = ctx->read_ctx->keyspec; + *nKey = ctx->keyspec_sz; +} - if( p->aMap ){ - p->iReadOff += sqlcipher_sqlite3GetVarint(&p->aMap[p->iReadOff], pnOut); - }else{ - iBuf = p->iReadOff % p->nBuffer; - if( iBuf && (p->nBuffer-iBuf)>=9 ){ - p->iReadOff += sqlcipher_sqlite3GetVarint(&p->aBuffer[iBuf], pnOut); - }else{ - u8 aVarint[16], *a; - int i = 0, rc; - do{ - rc = vdbePmaReadBlob(p, 1, &a); - if( rc ) return rc; - aVarint[(i++)&0xf] = a[0]; - }while( (a[0]&0x80)!=0 ); - sqlcipher_sqlite3GetVarint(aVarint, pnOut); - } +int sqlcipher_codec_ctx_set_pagesize(codec_ctx *ctx, int size) { + if(!((size != 0) && ((size & (size - 1)) == 0)) || size < 512 || size > 65536) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "cipher_page_size not a power of 2 and between 512 and 65536 inclusive"); + return SQLITE_ERROR; } + /* attempt to free the existing page buffer */ + sqlcipher_free(ctx->buffer,ctx->page_sz); + ctx->page_sz = size; + + /* pre-allocate a page buffer of PageSize bytes. This will + be used as a persistent buffer for encryption and decryption + operations to avoid overhead of multiple memory allocations*/ + ctx->buffer = sqlcipher_malloc(size); + if(ctx->buffer == NULL) return SQLITE_NOMEM; return SQLITE_OK; } -/* -** Attempt to memory map file pFile. If successful, set *pp to point to the -** new mapping and return SQLITE_OK. If the mapping is not attempted -** (because the file is too large or the VFS layer is configured not to use -** mmap), return SQLITE_OK and set *pp to NULL. -** -** Or, if an error occurs, return an SQLite error code. The final value of -** *pp is undefined in this case. -*/ -static int vdbeSorterMapFile(SortSubtask *pTask, SorterFile *pFile, u8 **pp){ - int rc = SQLITE_OK; - if( pFile->iEof<=(i64)(pTask->pSorter->db->nMaxSorterMmap) ){ - sqlcipher_sqlite3_file *pFd = pFile->pFd; - if( pFd->pMethods->iVersion>=3 ){ - rc = sqlcipher_sqlite3OsFetch(pFd, 0, (int)pFile->iEof, (void**)pp); - testcase( rc!=SQLITE_OK ); - } - } - return rc; +int sqlcipher_codec_ctx_get_pagesize(codec_ctx *ctx) { + return ctx->page_sz; } -/* -** Attach PmaReader pReadr to file pFile (if it is not already attached to -** that file) and seek it to offset iOff within the file. Return SQLITE_OK -** if successful, or an SQLite error code if an error occurs. -*/ -static int vdbePmaReaderSeek( - SortSubtask *pTask, /* Task context */ - PmaReader *pReadr, /* Reader whose cursor is to be moved */ - SorterFile *pFile, /* Sorter file to read from */ - i64 iOff /* Offset in pFile */ -){ - int rc = SQLITE_OK; - - assert( pReadr->pIncr==0 || pReadr->pIncr->bEof==0 ); +void sqlcipher_set_default_pagesize(int page_size) { + default_page_size = page_size; +} - if( sqlcipher_sqlite3FaultSim(201) ) return SQLITE_IOERR_READ; - if( pReadr->aMap ){ - sqlcipher_sqlite3OsUnfetch(pReadr->pFd, 0, pReadr->aMap); - pReadr->aMap = 0; - } - pReadr->iReadOff = iOff; - pReadr->iEof = pFile->iEof; - pReadr->pFd = pFile->pFd; +int sqlcipher_get_default_pagesize() { + return default_page_size; +} - rc = vdbeSorterMapFile(pTask, pFile, &pReadr->aMap); - if( rc==SQLITE_OK && pReadr->aMap==0 ){ - int pgsz = pTask->pSorter->pgsz; - int iBuf = pReadr->iReadOff % pgsz; - if( pReadr->aBuffer==0 ){ - pReadr->aBuffer = (u8*)sqlcipher_sqlite3Malloc(pgsz); - if( pReadr->aBuffer==0 ) rc = SQLITE_NOMEM_BKPT; - pReadr->nBuffer = pgsz; - } - if( rc==SQLITE_OK && iBuf ){ - int nRead = pgsz - iBuf; - if( (pReadr->iReadOff + nRead) > pReadr->iEof ){ - nRead = (int)(pReadr->iEof - pReadr->iReadOff); - } - rc = sqlcipher_sqlite3OsRead( - pReadr->pFd, &pReadr->aBuffer[iBuf], nRead, pReadr->iReadOff - ); - testcase( rc!=SQLITE_OK ); - } +void sqlcipher_set_mem_security(int on) { + /* memory security can only be enabled, not disabled */ + if(on) { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_set_mem_security: on"); + sqlcipher_mem_security_on = on; } +} - return rc; +int sqlcipher_get_mem_security() { + /* only report that memory security is enabled if pragma cipher_memory_security is ON and + SQLCipher's allocator/deallocator was run at least one timecurrently used */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_get_mem_security: sqlcipher_mem_security_on = %d, sqlcipher_mem_executed = %d", sqlcipher_mem_security_on, sqlcipher_mem_executed); + return sqlcipher_mem_security_on && sqlcipher_mem_executed; } -/* -** Advance PmaReader pReadr to the next key in its PMA. Return SQLITE_OK if -** no error occurs, or an SQLite error code if one does. -*/ -static int vdbePmaReaderNext(PmaReader *pReadr){ - int rc = SQLITE_OK; /* Return Code */ - u64 nRec = 0; /* Size of record in bytes */ +int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, const void *zKey, int nKey) { + int rc; + codec_ctx *ctx; + + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_codec_ctx_init: allocating context"); + + *iCtx = sqlcipher_malloc(sizeof(codec_ctx)); + ctx = *iCtx; + + if(ctx == NULL) return SQLITE_NOMEM; + + ctx->pBt = pDb->pBt; /* assign pointer to database btree structure */ + + /* allocate space for salt data. Then read the first 16 bytes + directly off the database file. This is the salt for the + key derivation function. If we get a short read allocate + a new random salt value */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_codec_ctx_init: allocating kdf_salt"); + ctx->kdf_salt_sz = FILE_HEADER_SZ; + ctx->kdf_salt = sqlcipher_malloc(ctx->kdf_salt_sz); + if(ctx->kdf_salt == NULL) return SQLITE_NOMEM; + + /* allocate space for separate hmac salt data. We want the + HMAC derivation salt to be different than the encryption + key derivation salt */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_codec_ctx_init: allocating hmac_kdf_salt"); + ctx->hmac_kdf_salt = sqlcipher_malloc(ctx->kdf_salt_sz); + if(ctx->hmac_kdf_salt == NULL) return SQLITE_NOMEM; + + /* setup default flags */ + ctx->flags = default_flags; + + /* defer attempt to read KDF salt until first use */ + ctx->need_kdf_salt = 1; + + /* setup the crypto provider */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_codec_ctx_init: allocating provider"); + ctx->provider = (sqlcipher_provider *) sqlcipher_malloc(sizeof(sqlcipher_provider)); + if(ctx->provider == NULL) return SQLITE_NOMEM; + + /* make a copy of the provider to be used for the duration of the context */ + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_codec_ctx_init: entering SQLCIPHER_MUTEX_PROVIDER"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_codec_ctx_init: entered SQLCIPHER_MUTEX_PROVIDER"); + + memcpy(ctx->provider, default_provider, sizeof(sqlcipher_provider)); - if( pReadr->iReadOff>=pReadr->iEof ){ - IncrMerger *pIncr = pReadr->pIncr; - int bEof = 1; - if( pIncr ){ - rc = vdbeIncrSwap(pIncr); - if( rc==SQLITE_OK && pIncr->bEof==0 ){ - rc = vdbePmaReaderSeek( - pIncr->pTask, pReadr, &pIncr->aFile[0], pIncr->iStartOff - ); - bEof = 0; - } - } + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_codec_ctx_init: leaving SQLCIPHER_MUTEX_PROVIDER"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_codec_ctx_init: left SQLCIPHER_MUTEX_PROVIDER"); - if( bEof ){ - /* This is an EOF condition */ - vdbePmaReaderClear(pReadr); - testcase( rc!=SQLITE_OK ); - return rc; - } + if((rc = ctx->provider->ctx_init(&ctx->provider_ctx)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d returned from ctx_init", rc); + return rc; } - if( rc==SQLITE_OK ){ - rc = vdbePmaReadVarint(pReadr, &nRec); - } - if( rc==SQLITE_OK ){ - pReadr->nKey = (int)nRec; - rc = vdbePmaReadBlob(pReadr, (int)nRec, &pReadr->aKey); - testcase( rc!=SQLITE_OK ); - } + ctx->key_sz = ctx->provider->get_key_sz(ctx->provider_ctx); + ctx->iv_sz = ctx->provider->get_iv_sz(ctx->provider_ctx); + ctx->block_sz = ctx->provider->get_block_sz(ctx->provider_ctx); - return rc; -} + /* establic the size for a hex-formated key specification, containing the + raw encryption key and the salt used to generate it format. will be x'hexkey...hexsalt' + so oversize by 3 bytes */ + ctx->keyspec_sz = ((ctx->key_sz + ctx->kdf_salt_sz) * 2) + 3; -/* -** Initialize PmaReader pReadr to scan through the PMA stored in file pFile -** starting at offset iStart and ending at offset iEof-1. This function -** leaves the PmaReader pointing to the first key in the PMA (or EOF if the -** PMA is empty). -** -** If the pnByte parameter is NULL, then it is assumed that the file -** contains a single PMA, and that that PMA omits the initial length varint. -*/ -static int vdbePmaReaderInit( - SortSubtask *pTask, /* Task context */ - SorterFile *pFile, /* Sorter file to read from */ - i64 iStart, /* Start offset in pFile */ - PmaReader *pReadr, /* PmaReader to populate */ - i64 *pnByte /* IN/OUT: Increment this value by PMA size */ -){ - int rc; + /* + Always overwrite page size and set to the default because the first page of the database + in encrypted and thus sqlite can't effectively determine the pagesize. this causes an issue in + cases where bytes 16 & 17 of the page header are a power of 2 as reported by John Lehman + */ + if((rc = sqlcipher_codec_ctx_set_pagesize(ctx, default_page_size)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d returned from sqlcipher_codec_ctx_set_pagesize with %d", rc, default_page_size); + return rc; + } - assert( pFile->iEof>iStart ); - assert( pReadr->aAlloc==0 && pReadr->nAlloc==0 ); - assert( pReadr->aBuffer==0 ); - assert( pReadr->aMap==0 ); + /* establish settings for the KDF iterations and fast (HMAC) KDF iterations */ + if((rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, default_kdf_iter)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d setting default_kdf_iter %d", rc, default_kdf_iter); + return rc; + } - rc = vdbePmaReaderSeek(pTask, pReadr, pFile, iStart); - if( rc==SQLITE_OK ){ - u64 nByte = 0; /* Size of PMA in bytes */ - rc = vdbePmaReadVarint(pReadr, &nByte); - pReadr->iEof = pReadr->iReadOff + nByte; - *pnByte += nByte; + if((rc = sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, FAST_PBKDF2_ITER)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d setting fast_kdf_iter to %d", rc, FAST_PBKDF2_ITER); + return rc; } - if( rc==SQLITE_OK ){ - rc = vdbePmaReaderNext(pReadr); + /* set the default HMAC and KDF algorithms which will determine the reserve size */ + if((rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, default_hmac_algorithm)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d setting sqlcipher_codec_ctx_set_hmac_algorithm with %d", rc, default_hmac_algorithm); + return rc; } - return rc; -} -/* -** A version of vdbeSorterCompare() that assumes that it has already been -** determined that the first field of key1 is equal to the first field of -** key2. -*/ -static int vdbeSorterCompareTail( - SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ - int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ - const void *pKey1, int nKey1, /* Left side of comparison */ - const void *pKey2, int nKey2 /* Right side of comparison */ -){ - UnpackedRecord *r2 = pTask->pUnpacked; - if( *pbKey2Cached==0 ){ - sqlcipher_sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2); - *pbKey2Cached = 1; + /* Note that use_hmac is a special case that requires recalculation of page size + so we call set_use_hmac to perform setup */ + if((rc = sqlcipher_codec_ctx_set_use_hmac(ctx, default_flags & CIPHER_FLAG_HMAC)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d setting use_hmac %d", rc, default_flags & CIPHER_FLAG_HMAC); + return rc; } - return sqlcipher_sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, r2, 1); -} -/* -** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2, -** size nKey2 bytes). Use (pTask->pKeyInfo) for the collation sequences -** used by the comparison. Return the result of the comparison. -** -** If IN/OUT parameter *pbKey2Cached is true when this function is called, -** it is assumed that (pTask->pUnpacked) contains the unpacked version -** of key2. If it is false, (pTask->pUnpacked) is populated with the unpacked -** version of key2 and *pbKey2Cached set to true before returning. -** -** If an OOM error is encountered, (pTask->pUnpacked->error_rc) is set -** to SQLITE_NOMEM. -*/ -static int vdbeSorterCompare( - SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ - int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ - const void *pKey1, int nKey1, /* Left side of comparison */ - const void *pKey2, int nKey2 /* Right side of comparison */ -){ - UnpackedRecord *r2 = pTask->pUnpacked; - if( !*pbKey2Cached ){ - sqlcipher_sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2); - *pbKey2Cached = 1; + if((rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, default_kdf_algorithm)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d setting sqlcipher_codec_ctx_set_kdf_algorithm with %d", rc, default_kdf_algorithm); + return rc; } - return sqlcipher_sqlite3VdbeRecordCompare(nKey1, pKey1, r2); -} -/* -** A specially optimized version of vdbeSorterCompare() that assumes that -** the first field of each key is a TEXT value and that the collation -** sequence to compare them with is BINARY. -*/ -static int vdbeSorterCompareText( - SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ - int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ - const void *pKey1, int nKey1, /* Left side of comparison */ - const void *pKey2, int nKey2 /* Right side of comparison */ -){ - const u8 * const p1 = (const u8 * const)pKey1; - const u8 * const p2 = (const u8 * const)pKey2; - const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ - const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ + /* setup the default plaintext header size */ + if((rc = sqlcipher_codec_ctx_set_plaintext_header_size(ctx, default_plaintext_header_sz)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d setting sqlcipher_codec_ctx_set_plaintext_header_size with %d", rc, default_plaintext_header_sz); + return rc; + } - int n1; - int n2; - int res; + /* initialize the read and write sub-contexts. this must happen after key_sz is established */ + if((rc = sqlcipher_cipher_ctx_init(ctx, &ctx->read_ctx)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d initializing read_ctx", rc); + return rc; + } - getVarint32NR(&p1[1], n1); - getVarint32NR(&p2[1], n2); - res = memcmp(v1, v2, (MIN(n1, n2) - 13)/2); - if( res==0 ){ - res = n1 - n2; + if((rc = sqlcipher_cipher_ctx_init(ctx, &ctx->write_ctx)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d initializing write_ctx", rc); + return rc; } - if( res==0 ){ - if( pTask->pSorter->pKeyInfo->nKeyField>1 ){ - res = vdbeSorterCompareTail( - pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2 - ); - } - }else{ - assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) ); - if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){ - res = res * -1; - } + /* set the key material on one of the sub cipher contexts and sync them up */ + if((rc = sqlcipher_codec_ctx_set_pass(ctx, zKey, nKey, 0)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d setting pass key", rc); + return rc; } - return res; + if((rc = sqlcipher_cipher_ctx_copy(ctx, ctx->write_ctx, ctx->read_ctx)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_ctx_init: error %d copying write_ctx to read_ctx", rc); + return rc; + } + + return SQLITE_OK; } -/* -** A specially optimized version of vdbeSorterCompare() that assumes that -** the first field of each key is an INTEGER value. -*/ -static int vdbeSorterCompareInt( - SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ - int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ - const void *pKey1, int nKey1, /* Left side of comparison */ - const void *pKey2, int nKey2 /* Right side of comparison */ -){ - const u8 * const p1 = (const u8 * const)pKey1; - const u8 * const p2 = (const u8 * const)pKey2; - const int s1 = p1[1]; /* Left hand serial type */ - const int s2 = p2[1]; /* Right hand serial type */ - const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ - const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ - int res; /* Return value */ +/** + * Free and wipe memory associated with a cipher_ctx, including the allocated + * read_ctx and write_ctx. + */ +void sqlcipher_codec_ctx_free(codec_ctx **iCtx) { + codec_ctx *ctx = *iCtx; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "codec_ctx_free: iCtx=%p", iCtx); + sqlcipher_free(ctx->kdf_salt, ctx->kdf_salt_sz); + sqlcipher_free(ctx->hmac_kdf_salt, ctx->kdf_salt_sz); + sqlcipher_free(ctx->buffer, ctx->page_sz); - assert( (s1>0 && s1<7) || s1==8 || s1==9 ); - assert( (s2>0 && s2<7) || s2==8 || s2==9 ); + ctx->provider->ctx_free(&ctx->provider_ctx); + sqlcipher_free(ctx->provider, sizeof(sqlcipher_provider)); - if( s1==s2 ){ - /* The two values have the same sign. Compare using memcmp(). */ - static const u8 aLen[] = {0, 1, 2, 3, 4, 6, 8, 0, 0, 0 }; - const u8 n = aLen[s1]; - int i; - res = 0; - for(i=0; i7 && s2>7 ){ - res = s1 - s2; - }else{ - if( s2>7 ){ - res = +1; - }else if( s1>7 ){ - res = -1; - }else{ - res = s1 - s2; - } - assert( res!=0 ); + sqlcipher_cipher_ctx_free(ctx, &ctx->read_ctx); + sqlcipher_cipher_ctx_free(ctx, &ctx->write_ctx); + sqlcipher_free(ctx, sizeof(codec_ctx)); +} - if( res>0 ){ - if( *v1 & 0x80 ) res = -1; - }else{ - if( *v2 & 0x80 ) res = +1; - } - } +/** convert a 32bit unsigned integer to little endian byte ordering */ +static void sqlcipher_put4byte_le(unsigned char *p, u32 v) { + p[0] = (u8)v; + p[1] = (u8)(v>>8); + p[2] = (u8)(v>>16); + p[3] = (u8)(v>>24); +} - if( res==0 ){ - if( pTask->pSorter->pKeyInfo->nKeyField>1 ){ - res = vdbeSorterCompareTail( - pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2 - ); - } - }else if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){ - assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) ); - res = res * -1; +static int sqlcipher_page_hmac(codec_ctx *ctx, cipher_ctx *c_ctx, Pgno pgno, unsigned char *in, int in_sz, unsigned char *out) { + unsigned char pgno_raw[sizeof(pgno)]; + /* we may convert page number to consistent representation before calculating MAC for + compatibility across big-endian and little-endian platforms. + + Note: The public release of sqlcipher 2.0.0 to 2.0.6 had a bug where the bytes of pgno + were used directly in the MAC. SQLCipher convert's to little endian by default to preserve + backwards compatibility on the most popular platforms, but can optionally be configured + to use either big endian or native byte ordering via pragma. */ + + if(ctx->flags & CIPHER_FLAG_LE_PGNO) { /* compute hmac using little endian pgno*/ + sqlcipher_put4byte_le(pgno_raw, pgno); + } else if(ctx->flags & CIPHER_FLAG_BE_PGNO) { /* compute hmac using big endian pgno */ + sqlcipher_sqlite3Put4byte(pgno_raw, pgno); /* sqlcipher_sqlite3Put4byte converts 32bit uint to big endian */ + } else { /* use native byte ordering */ + memcpy(pgno_raw, &pgno, sizeof(pgno)); } - return res; + /* include the encrypted page data, initialization vector, and page number in HMAC. This will + prevent both tampering with the ciphertext, manipulation of the IV, or resequencing otherwise + valid pages out of order in a database */ + return ctx->provider->hmac( + ctx->provider_ctx, ctx->hmac_algorithm, c_ctx->hmac_key, + ctx->key_sz, in, + in_sz, (unsigned char*) &pgno_raw, + sizeof(pgno), out); } /* -** Initialize the temporary index cursor just opened as a sorter cursor. -** -** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nKeyField) -** to determine the number of fields that should be compared from the -** records being sorted. However, if the value passed as argument nField -** is non-zero and the sorter is able to guarantee a stable sort, nField -** is used instead. This is used when sorting records for a CREATE INDEX -** statement. In this case, keys are always delivered to the sorter in -** order of the primary key, which happens to be make up the final part -** of the records being sorted. So if the sort is stable, there is never -** any reason to compare PK fields and they can be ignored for a small -** performance boost. -** -** The sorter can guarantee a stable sort when running in single-threaded -** mode, but not in multi-threaded mode. -** -** SQLITE_OK is returned if successful, or an SQLite error code otherwise. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterInit( - sqlcipher_sqlite3 *db, /* Database connection (for malloc()) */ - int nField, /* Number of key fields in each record */ - VdbeCursor *pCsr /* Cursor that holds the new sorter */ -){ - int pgsz; /* Page size of main database */ - int i; /* Used to iterate through aTask[] */ - VdbeSorter *pSorter; /* The new sorter */ - KeyInfo *pKeyInfo; /* Copy of pCsr->pKeyInfo with db==0 */ - int szKeyInfo; /* Size of pCsr->pKeyInfo in bytes */ - int sz; /* Size of pSorter in bytes */ - int rc = SQLITE_OK; -#if SQLITE_MAX_WORKER_THREADS==0 -# define nWorker 0 -#else - int nWorker; -#endif - - /* Initialize the upper limit on the number of worker threads */ -#if SQLITE_MAX_WORKER_THREADS>0 - if( sqlcipher_sqlite3TempInMemory(db) || sqlcipher_sqlite3GlobalConfig.bCoreMutex==0 ){ - nWorker = 0; - }else{ - nWorker = db->aLimit[SQLITE_LIMIT_WORKER_THREADS]; - } -#endif + * ctx - codec context + * pgno - page number in database + * size - size in bytes of input and output buffers + * mode - 1 to encrypt, 0 to decrypt + * in - pointer to input bytes + * out - pouter to output bytes + */ +int sqlcipher_page_cipher(codec_ctx *ctx, int for_ctx, Pgno pgno, int mode, int page_sz, unsigned char *in, unsigned char *out) { + cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx; + unsigned char *iv_in, *iv_out, *hmac_in, *hmac_out, *out_start; + int size; - /* Do not allow the total number of threads (main thread + all workers) - ** to exceed the maximum merge count */ -#if SQLITE_MAX_WORKER_THREADS>=SORTER_MAX_MERGE_COUNT - if( nWorker>=SORTER_MAX_MERGE_COUNT ){ - nWorker = SORTER_MAX_MERGE_COUNT-1; - } -#endif + /* calculate some required positions into various buffers */ + size = page_sz - ctx->reserve_sz; /* adjust size to useable size and memset reserve at end of page */ + iv_out = out + size; + iv_in = in + size; - assert( pCsr->pKeyInfo && pCsr->pBtx==0 ); - assert( pCsr->eCurType==CURTYPE_SORTER ); - szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*); - sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); + /* hmac will be written immediately after the initialization vector. the remainder of the page reserve will contain + random bytes. note, these pointers are only valid when using hmac */ + hmac_in = in + size + ctx->iv_sz; + hmac_out = out + size + ctx->iv_sz; + out_start = out; /* note the original position of the output buffer pointer, as out will be rewritten during encryption */ - pSorter = (VdbeSorter*)sqlcipher_sqlite3DbMallocZero(db, sz + szKeyInfo); - pCsr->uc.pSorter = pSorter; - if( pSorter==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - Btree *pBt = db->aDb[0].pBt; - pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz); - memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo); - pKeyInfo->db = 0; - if( nField && nWorker==0 ){ - pKeyInfo->nKeyField = nField; - } - sqlcipher_sqlite3BtreeEnter(pBt); - pSorter->pgsz = pgsz = sqlcipher_sqlite3BtreeGetPageSize(pBt); - sqlcipher_sqlite3BtreeLeave(pBt); - pSorter->nTask = nWorker + 1; - pSorter->iPrev = (u8)(nWorker - 1); - pSorter->bUseThreads = (pSorter->nTask>1); - pSorter->db = db; - for(i=0; inTask; i++){ - SortSubtask *pTask = &pSorter->aTask[i]; - pTask->pSorter = pSorter; - } + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_page_cipher: pgno=%d, mode=%d, size=%d", pgno, mode, size); + CODEC_HEXDUMP("sqlcipher_page_cipher: input page data", in, page_sz); - if( !sqlcipher_sqlite3TempInMemory(db) ){ - i64 mxCache; /* Cache size in bytes*/ - u32 szPma = sqlcipher_sqlite3GlobalConfig.szPma; - pSorter->mnPmaSize = szPma * pgsz; + /* the key size should never be zero. If it is, error out. */ + if(ctx->key_sz == 0) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_page_cipher: error possible context corruption, key_sz is zero for pgno=%d", pgno); + goto error; + } - mxCache = db->aDb[0].pSchema->cache_size; - if( mxCache<0 ){ - /* A negative cache-size value C indicates that the cache is abs(C) - ** KiB in size. */ - mxCache = mxCache * -1024; - }else{ - mxCache = mxCache * pgsz; - } - mxCache = MIN(mxCache, SQLITE_MAX_PMASZ); - pSorter->mxPmaSize = MAX(pSorter->mnPmaSize, (int)mxCache); + if(mode == CIPHER_ENCRYPT) { + /* start at front of the reserve block, write random data to the end */ + if(ctx->provider->random(ctx->provider_ctx, iv_out, ctx->reserve_sz) != SQLITE_OK) goto error; + } else { /* CIPHER_DECRYPT */ + memcpy(iv_out, iv_in, ctx->iv_sz); /* copy the iv from the input to output buffer */ + } - /* Avoid large memory allocations if the application has requested - ** SQLITE_CONFIG_SMALL_MALLOC. */ - if( sqlcipher_sqlite3GlobalConfig.bSmallMalloc==0 ){ - assert( pSorter->iMemory==0 ); - pSorter->nMemory = pgsz; - pSorter->list.aMemory = (u8*)sqlcipher_sqlite3Malloc(pgsz); - if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM_BKPT; - } + if((ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_DECRYPT) && !ctx->skip_read_hmac) { + if(sqlcipher_page_hmac(ctx, c_ctx, pgno, in, size + ctx->iv_sz, hmac_out) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_page_cipher: hmac operation on decrypt failed for pgno=%d", pgno); + goto error; } - if( pKeyInfo->nAllField<13 - && (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl) - && (pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL)==0 - ){ - pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_page_cipher: comparing hmac on in=%p out=%p hmac_sz=%d", hmac_in, hmac_out, ctx->hmac_sz); + if(sqlcipher_memcmp(hmac_in, hmac_out, ctx->hmac_sz) != 0) { /* the hmac check failed */ + if(sqlcipher_sqlite3BtreeGetAutoVacuum(ctx->pBt) != BTREE_AUTOVACUUM_NONE && sqlcipher_ismemset(in, 0, page_sz) == 0) { + /* first check if the entire contents of the page is zeros. If so, this page + resulted from a short read (i.e. sqlite attempted to pull a page after the end of the file. these + short read failures must be ignored for autovaccum mode to work so wipe the output buffer + and return SQLITE_OK to skip the decryption step. */ + sqlcipher_log(SQLCIPHER_LOG_WARN, "sqlcipher_page_cipher: zeroed page (short read) for pgno %d, encryption but returning SQLITE_OK", pgno); + sqlcipher_memset(out, 0, page_sz); + return SQLITE_OK; + } else { + /* if the page memory is not all zeros, it means the there was data and a hmac on the page. + since the check failed, the page was either tampered with or corrupted. wipe the output buffer, + and return SQLITE_ERROR to the caller */ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_page_cipher: hmac check failed for pgno=%d returning SQLITE_ERROR", pgno); + goto error; + } } } - return rc; -} -#undef nWorker /* Defined at the top of this function */ + if(ctx->provider->cipher(ctx->provider_ctx, mode, c_ctx->key, ctx->key_sz, iv_out, in, size, out) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_page_cipher: cipher operation mode=%d failed for pgno=%d returning SQLITE_ERROR", mode, pgno); + goto error; + }; -/* -** Free the list of sorted records starting at pRecord. -*/ -static void vdbeSorterRecordFree(sqlcipher_sqlite3 *db, SorterRecord *pRecord){ - SorterRecord *p; - SorterRecord *pNext; - for(p=pRecord; p; p=pNext){ - pNext = p->u.pNext; - sqlcipher_sqlite3DbFree(db, p); + if((ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_ENCRYPT)) { + if(sqlcipher_page_hmac(ctx, c_ctx, pgno, out_start, size + ctx->iv_sz, hmac_out) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_page_cipher: hmac operation on encrypt failed for pgno=%d", pgno); + goto error; + }; } -} -/* -** Free all resources owned by the object indicated by argument pTask. All -** fields of *pTask are zeroed before returning. -*/ -static void vdbeSortSubtaskCleanup(sqlcipher_sqlite3 *db, SortSubtask *pTask){ - sqlcipher_sqlite3DbFree(db, pTask->pUnpacked); -#if SQLITE_MAX_WORKER_THREADS>0 - /* pTask->list.aMemory can only be non-zero if it was handed memory - ** from the main thread. That only occurs SQLITE_MAX_WORKER_THREADS>0 */ - if( pTask->list.aMemory ){ - sqlcipher_sqlite3_free(pTask->list.aMemory); - }else -#endif - { - assert( pTask->list.aMemory==0 ); - vdbeSorterRecordFree(0, pTask->list.pList); - } - if( pTask->file.pFd ){ - sqlcipher_sqlite3OsCloseFree(pTask->file.pFd); - } - if( pTask->file2.pFd ){ - sqlcipher_sqlite3OsCloseFree(pTask->file2.pFd); - } - memset(pTask, 0, sizeof(SortSubtask)); -} + CODEC_HEXDUMP("sqlcipher_page_cipher: output page data", out_start, page_sz); -#ifdef SQLITE_DEBUG_SORTER_THREADS -static void vdbeSorterWorkDebug(SortSubtask *pTask, const char *zEvent){ - i64 t; - int iTask = (pTask - pTask->pSorter->aTask); - sqlcipher_sqlite3OsCurrentTimeInt64(pTask->pSorter->db->pVfs, &t); - fprintf(stderr, "%lld:%d %s\n", t, iTask, zEvent); -} -static void vdbeSorterRewindDebug(const char *zEvent){ - i64 t; - sqlcipher_sqlite3OsCurrentTimeInt64(sqlcipher_sqlite3_vfs_find(0), &t); - fprintf(stderr, "%lld:X %s\n", t, zEvent); -} -static void vdbeSorterPopulateDebug( - SortSubtask *pTask, - const char *zEvent -){ - i64 t; - int iTask = (pTask - pTask->pSorter->aTask); - sqlcipher_sqlite3OsCurrentTimeInt64(pTask->pSorter->db->pVfs, &t); - fprintf(stderr, "%lld:bg%d %s\n", t, iTask, zEvent); -} -static void vdbeSorterBlockDebug( - SortSubtask *pTask, - int bBlocked, - const char *zEvent -){ - if( bBlocked ){ - i64 t; - sqlcipher_sqlite3OsCurrentTimeInt64(pTask->pSorter->db->pVfs, &t); - fprintf(stderr, "%lld:main %s\n", t, zEvent); - } + return SQLITE_OK; +error: + sqlcipher_memset(out, 0, page_sz); + return SQLITE_ERROR; } -#else -# define vdbeSorterWorkDebug(x,y) -# define vdbeSorterRewindDebug(y) -# define vdbeSorterPopulateDebug(x,y) -# define vdbeSorterBlockDebug(x,y,z) -#endif -#if SQLITE_MAX_WORKER_THREADS>0 -/* -** Join thread pTask->thread. -*/ -static int vdbeSorterJoinThread(SortSubtask *pTask){ - int rc = SQLITE_OK; - if( pTask->pThread ){ -#ifdef SQLITE_DEBUG_SORTER_THREADS - int bDone = pTask->bDone; -#endif - void *pRet = SQLITE_INT_TO_PTR(SQLITE_ERROR); - vdbeSorterBlockDebug(pTask, !bDone, "enter"); - (void)sqlcipher_sqlite3ThreadJoin(pTask->pThread, &pRet); - vdbeSorterBlockDebug(pTask, !bDone, "exit"); - rc = SQLITE_PTR_TO_INT(pRet); - assert( pTask->bDone==1 ); - pTask->bDone = 0; - pTask->pThread = 0; - } - return rc; -} +/** + * Derive an encryption key for a cipher contex key based on the raw password. + * + * If the raw key data is formated as x'hex' and there are exactly enough hex chars to fill + * the key (i.e 64 hex chars for a 256 bit key) then the key data will be used directly. -/* -** Launch a background thread to run xTask(pIn). -*/ -static int vdbeSorterCreateThread( - SortSubtask *pTask, /* Thread will use this task object */ - void *(*xTask)(void*), /* Routine to run in a separate thread */ - void *pIn /* Argument passed into xTask() */ -){ - assert( pTask->pThread==0 && pTask->bDone==0 ); - return sqlcipher_sqlite3ThreadCreate(&pTask->pThread, xTask, pIn); -} + * Else, if the raw key data is formated as x'hex' and there are exactly enough hex chars to fill + * the key and the salt (i.e 92 hex chars for a 256 bit key and 16 byte salt) then it will be unpacked + * as the key followed by the salt. + * + * Otherwise, a key data will be derived using PBKDF2 + * + * returns SQLITE_OK if initialization was successful + * returns SQLITE_ERROR if the key could't be derived (for instance if pass is NULL or pass_sz is 0) + */ +static int sqlcipher_cipher_ctx_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) { + int rc; + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_cipher_ctx_key_derive: ctx->kdf_salt_sz=%d ctx->kdf_iter=%d ctx->fast_kdf_iter=%d ctx->key_sz=%d", + ctx->kdf_salt_sz, ctx->kdf_iter, ctx->fast_kdf_iter, ctx->key_sz); -/* -** Join all outstanding threads launched by SorterWrite() to create -** level-0 PMAs. -*/ -static int vdbeSorterJoinAll(VdbeSorter *pSorter, int rcin){ - int rc = rcin; - int i; + if(c_ctx->pass && c_ctx->pass_sz) { /* if key material is present on the context for derivation */ - /* This function is always called by the main user thread. - ** - ** If this function is being called after SorterRewind() has been called, - ** it is possible that thread pSorter->aTask[pSorter->nTask-1].pThread - ** is currently attempt to join one of the other threads. To avoid a race - ** condition where this thread also attempts to join the same object, join - ** thread pSorter->aTask[pSorter->nTask-1].pThread first. */ - for(i=pSorter->nTask-1; i>=0; i--){ - SortSubtask *pTask = &pSorter->aTask[i]; - int rc2 = vdbeSorterJoinThread(pTask); - if( rc==SQLITE_OK ) rc = rc2; - } - return rc; -} -#else -# define vdbeSorterJoinAll(x,rcin) (rcin) -# define vdbeSorterJoinThread(pTask) SQLITE_OK -#endif + /* if necessary, initialize the salt from the header or random source */ + if(ctx->need_kdf_salt) { + if((rc = sqlcipher_codec_ctx_init_kdf_salt(ctx)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_cipher_ctx_key_derive: error %d from sqlcipher_codec_ctx_init_kdf_salt", rc); + return rc; + } + } -/* -** Allocate a new MergeEngine object capable of handling up to -** nReader PmaReader inputs. -** -** nReader is automatically rounded up to the next power of two. -** nReader may not exceed SORTER_MAX_MERGE_COUNT even after rounding up. -*/ -static MergeEngine *vdbeMergeEngineNew(int nReader){ - int N = 2; /* Smallest power of two >= nReader */ - int nByte; /* Total bytes of space to allocate */ - MergeEngine *pNew; /* Pointer to allocated object to return */ + if (c_ctx->pass_sz == ((ctx->key_sz * 2) + 3) && sqlcipher_sqlite3StrNICmp((const char *)c_ctx->pass ,"x'", 2) == 0 && cipher_isHex(c_ctx->pass + 2, ctx->key_sz * 2)) { + int n = c_ctx->pass_sz - 3; /* adjust for leading x' and tailing ' */ + const unsigned char *z = c_ctx->pass + 2; /* adjust lead offset of x' */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "cipher_ctx_key_derive: using raw key from hex"); + cipher_hex2bin(z, n, c_ctx->key); + } else if (c_ctx->pass_sz == (((ctx->key_sz + ctx->kdf_salt_sz) * 2) + 3) && sqlcipher_sqlite3StrNICmp((const char *)c_ctx->pass ,"x'", 2) == 0 && cipher_isHex(c_ctx->pass + 2, (ctx->key_sz + ctx->kdf_salt_sz) * 2)) { + const unsigned char *z = c_ctx->pass + 2; /* adjust lead offset of x' */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "cipher_ctx_key_derive: using raw key from hex"); + cipher_hex2bin(z, (ctx->key_sz * 2), c_ctx->key); + cipher_hex2bin(z + (ctx->key_sz * 2), (ctx->kdf_salt_sz * 2), ctx->kdf_salt); + } else { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "cipher_ctx_key_derive: deriving key using full PBKDF2 with %d iterations", ctx->kdf_iter); + if(ctx->provider->kdf(ctx->provider_ctx, ctx->kdf_algorithm, c_ctx->pass, c_ctx->pass_sz, + ctx->kdf_salt, ctx->kdf_salt_sz, ctx->kdf_iter, + ctx->key_sz, c_ctx->key) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "cipher_ctx_key_derive: error occurred from provider kdf generating encryption key"); + return SQLITE_ERROR; + } + } - assert( nReader<=SORTER_MAX_MERGE_COUNT ); + /* set the context "keyspec" containing the hex-formatted key and salt to be used when attaching databases */ + if((rc = sqlcipher_cipher_ctx_set_keyspec(ctx, c_ctx, c_ctx->key)) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_cipher_ctx_key_derive: error %d from sqlcipher_cipher_ctx_set_keyspec", rc); + return rc; + } - while( Nflags & CIPHER_FLAG_HMAC) { + int i; - pNew = sqlcipher_sqlite3FaultSim(100) ? 0 : (MergeEngine*)sqlcipher_sqlite3MallocZero(nByte); - if( pNew ){ - pNew->nTree = N; - pNew->pTask = 0; - pNew->aReadr = (PmaReader*)&pNew[1]; - pNew->aTree = (int*)&pNew->aReadr[N]; - } - return pNew; -} + /* start by copying the kdf key into the hmac salt slot + then XOR it with the fixed hmac salt defined at compile time + this ensures that the salt passed in to derive the hmac key, while + easy to derive and publically known, is not the same as the salt used + to generate the encryption key */ + memcpy(ctx->hmac_kdf_salt, ctx->kdf_salt, ctx->kdf_salt_sz); + for(i = 0; i < ctx->kdf_salt_sz; i++) { + ctx->hmac_kdf_salt[i] ^= hmac_salt_mask; + } -/* -** Free the MergeEngine object passed as the only argument. -*/ -static void vdbeMergeEngineFree(MergeEngine *pMerger){ - int i; - if( pMerger ){ - for(i=0; inTree; i++){ - vdbePmaReaderClear(&pMerger->aReadr[i]); + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "cipher_ctx_key_derive: deriving hmac key from encryption key using PBKDF2 with %d iterations", + ctx->fast_kdf_iter); + + + if(ctx->provider->kdf(ctx->provider_ctx, ctx->kdf_algorithm, c_ctx->key, ctx->key_sz, + ctx->hmac_kdf_salt, ctx->kdf_salt_sz, ctx->fast_kdf_iter, + ctx->key_sz, c_ctx->hmac_key) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "cipher_ctx_key_derive: error occurred from provider kdf generating HMAC key"); + return SQLITE_ERROR; + } } + + c_ctx->derive_key = 0; + return SQLITE_OK; } - sqlcipher_sqlite3_free(pMerger); + sqlcipher_log(SQLCIPHER_LOG_ERROR, "cipher_ctx_key_derive: key material is not present on the context for key derivation"); + return SQLITE_ERROR; } -/* -** Free all resources associated with the IncrMerger object indicated by -** the first argument. -*/ -static void vdbeIncrFree(IncrMerger *pIncr){ - if( pIncr ){ -#if SQLITE_MAX_WORKER_THREADS>0 - if( pIncr->bUseThread ){ - vdbeSorterJoinThread(pIncr->pTask); - if( pIncr->aFile[0].pFd ) sqlcipher_sqlite3OsCloseFree(pIncr->aFile[0].pFd); - if( pIncr->aFile[1].pFd ) sqlcipher_sqlite3OsCloseFree(pIncr->aFile[1].pFd); +int sqlcipher_codec_key_derive(codec_ctx *ctx) { + /* derive key on first use if necessary */ + if(ctx->read_ctx->derive_key) { + if(sqlcipher_cipher_ctx_key_derive(ctx, ctx->read_ctx) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_key_derive: error occurred deriving read_ctx key"); + return SQLITE_ERROR; } -#endif - vdbeMergeEngineFree(pIncr->pMerger); - sqlcipher_sqlite3_free(pIncr); } -} -/* -** Reset a sorting cursor back to its original empty state. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSorterReset(sqlcipher_sqlite3 *db, VdbeSorter *pSorter){ - int i; - (void)vdbeSorterJoinAll(pSorter, SQLITE_OK); - assert( pSorter->bUseThreads || pSorter->pReader==0 ); -#if SQLITE_MAX_WORKER_THREADS>0 - if( pSorter->pReader ){ - vdbePmaReaderClear(pSorter->pReader); - sqlcipher_sqlite3DbFree(db, pSorter->pReader); - pSorter->pReader = 0; - } -#endif - vdbeMergeEngineFree(pSorter->pMerger); - pSorter->pMerger = 0; - for(i=0; inTask; i++){ - SortSubtask *pTask = &pSorter->aTask[i]; - vdbeSortSubtaskCleanup(db, pTask); - pTask->pSorter = pSorter; + if(ctx->write_ctx->derive_key) { + if(sqlcipher_cipher_ctx_cmp(ctx->write_ctx, ctx->read_ctx) == 0) { + /* the relevant parameters are the same, just copy read key */ + if(sqlcipher_cipher_ctx_copy(ctx, ctx->write_ctx, ctx->read_ctx) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_key_derive: error occurred copying read_ctx to write_ctx"); + return SQLITE_ERROR; + } + } else { + if(sqlcipher_cipher_ctx_key_derive(ctx, ctx->write_ctx) != SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_key_derive: error occurred deriving write_ctx key"); + return SQLITE_ERROR; + } + } } - if( pSorter->list.aMemory==0 ){ - vdbeSorterRecordFree(0, pSorter->list.pList); + + /* TODO: wipe and free passphrase after key derivation */ + if(ctx->store_pass != 1) { + sqlcipher_cipher_ctx_set_pass(ctx->read_ctx, NULL, 0); + sqlcipher_cipher_ctx_set_pass(ctx->write_ctx, NULL, 0); } - pSorter->list.pList = 0; - pSorter->list.szPMA = 0; - pSorter->bUsePMA = 0; - pSorter->iMemory = 0; - pSorter->mxKeysize = 0; - sqlcipher_sqlite3DbFree(db, pSorter->pUnpacked); - pSorter->pUnpacked = 0; + + return SQLITE_OK; } -/* -** Free any cursor components allocated by sqlcipher_sqlite3VdbeSorterXXX routines. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3VdbeSorterClose(sqlcipher_sqlite3 *db, VdbeCursor *pCsr){ - VdbeSorter *pSorter; - assert( pCsr->eCurType==CURTYPE_SORTER ); - pSorter = pCsr->uc.pSorter; - if( pSorter ){ - sqlcipher_sqlite3VdbeSorterReset(db, pSorter); - sqlcipher_sqlite3_free(pSorter->list.aMemory); - sqlcipher_sqlite3DbFree(db, pSorter); - pCsr->uc.pSorter = 0; +int sqlcipher_codec_key_copy(codec_ctx *ctx, int source) { + if(source == CIPHER_READ_CTX) { + return sqlcipher_cipher_ctx_copy(ctx, ctx->write_ctx, ctx->read_ctx); + } else { + return sqlcipher_cipher_ctx_copy(ctx, ctx->read_ctx, ctx->write_ctx); } } -#if SQLITE_MAX_MMAP_SIZE>0 -/* -** The first argument is a file-handle open on a temporary file. The file -** is guaranteed to be nByte bytes or smaller in size. This function -** attempts to extend the file to nByte bytes in size and to ensure that -** the VFS has memory mapped it. -** -** Whether or not the file does end up memory mapped of course depends on -** the specific VFS implementation. -*/ -static void vdbeSorterExtendFile(sqlcipher_sqlite3 *db, sqlcipher_sqlite3_file *pFd, i64 nByte){ - if( nByte<=(i64)(db->nMaxSorterMmap) && pFd->pMethods->iVersion>=3 ){ - void *p = 0; - int chunksize = 4*1024; - sqlcipher_sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_CHUNK_SIZE, &chunksize); - sqlcipher_sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_SIZE_HINT, &nByte); - sqlcipher_sqlite3OsFetch(pFd, 0, (int)nByte, &p); - sqlcipher_sqlite3OsUnfetch(pFd, 0, p); - } +const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx) { + return ctx->provider->get_provider_name(ctx->provider_ctx); } -#else -# define vdbeSorterExtendFile(x,y,z) -#endif -/* -** Allocate space for a file-handle and open a temporary file. If successful, -** set *ppFd to point to the malloc'd file-handle and return SQLITE_OK. -** Otherwise, set *ppFd to 0 and return an SQLite error code. -*/ -static int vdbeSorterOpenTempFile( - sqlcipher_sqlite3 *db, /* Database handle doing sort */ - i64 nExtend, /* Attempt to extend file to this size */ - sqlcipher_sqlite3_file **ppFd -){ + +static int sqlcipher_check_connection(const char *filename, char *key, int key_sz, char *sql, int *user_version, char** journal_mode) { int rc; - if( sqlcipher_sqlite3FaultSim(202) ) return SQLITE_IOERR_ACCESS; - rc = sqlcipher_sqlite3OsOpenMalloc(db->pVfs, 0, ppFd, - SQLITE_OPEN_TEMP_JOURNAL | - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | - SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE, &rc - ); - if( rc==SQLITE_OK ){ - i64 max = SQLITE_MAX_MMAP_SIZE; - sqlcipher_sqlite3OsFileControlHint(*ppFd, SQLITE_FCNTL_MMAP_SIZE, (void*)&max); - if( nExtend>0 ){ - vdbeSorterExtendFile(db, *ppFd, nExtend); - } - } - return rc; -} + sqlcipher_sqlite3 *db = NULL; + sqlcipher_sqlite3_stmt *statement = NULL; + char *query_journal_mode = "PRAGMA journal_mode;"; + char *query_user_version = "PRAGMA user_version;"; -/* -** If it has not already been allocated, allocate the UnpackedRecord -** structure at pTask->pUnpacked. Return SQLITE_OK if successful (or -** if no allocation was required), or SQLITE_NOMEM otherwise. -*/ -static int vdbeSortAllocUnpacked(SortSubtask *pTask){ - if( pTask->pUnpacked==0 ){ - pTask->pUnpacked = sqlcipher_sqlite3VdbeAllocUnpackedRecord(pTask->pSorter->pKeyInfo); - if( pTask->pUnpacked==0 ) return SQLITE_NOMEM_BKPT; - pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nKeyField; - pTask->pUnpacked->errCode = 0; - } - return SQLITE_OK; -} + rc = sqlcipher_sqlite3_open(filename, &db); + if(rc != SQLITE_OK) goto cleanup; + rc = sqlcipher_sqlite3_key(db, key, key_sz); + if(rc != SQLITE_OK) goto cleanup; -/* -** Merge the two sorted lists p1 and p2 into a single list. -*/ -static SorterRecord *vdbeSorterMerge( - SortSubtask *pTask, /* Calling thread context */ - SorterRecord *p1, /* First list to merge */ - SorterRecord *p2 /* Second list to merge */ -){ - SorterRecord *pFinal = 0; - SorterRecord **pp = &pFinal; - int bCached = 0; + rc = sqlcipher_sqlite3_exec(db, sql, NULL, NULL, NULL); + if(rc != SQLITE_OK) goto cleanup; - assert( p1!=0 && p2!=0 ); - for(;;){ - int res; - res = pTask->xCompare( - pTask, &bCached, SRVAL(p1), p1->nVal, SRVAL(p2), p2->nVal - ); + /* start by querying the user version. + this will fail if the key is incorrect */ + rc = sqlcipher_sqlite3_prepare(db, query_user_version, -1, &statement, NULL); + if(rc != SQLITE_OK) goto cleanup; - if( res<=0 ){ - *pp = p1; - pp = &p1->u.pNext; - p1 = p1->u.pNext; - if( p1==0 ){ - *pp = p2; - break; - } - }else{ - *pp = p2; - pp = &p2->u.pNext; - p2 = p2->u.pNext; - bCached = 0; - if( p2==0 ){ - *pp = p1; - break; - } - } + rc = sqlcipher_sqlite3_step(statement); + if(rc == SQLITE_ROW) { + *user_version = sqlcipher_sqlite3_column_int(statement, 0); + } else { + goto cleanup; } - return pFinal; -} + sqlcipher_sqlite3_finalize(statement); -/* -** Return the SorterCompare function to compare values collected by the -** sorter object passed as the only argument. -*/ -static SorterCompare vdbeSorterGetCompare(VdbeSorter *p){ - if( p->typeMask==SORTER_TYPE_INTEGER ){ - return vdbeSorterCompareInt; - }else if( p->typeMask==SORTER_TYPE_TEXT ){ - return vdbeSorterCompareText; + rc = sqlcipher_sqlite3_prepare(db, query_journal_mode, -1, &statement, NULL); + if(rc != SQLITE_OK) goto cleanup; + + rc = sqlcipher_sqlite3_step(statement); + if(rc == SQLITE_ROW) { + *journal_mode = sqlcipher_sqlite3_mprintf("%s", sqlcipher_sqlite3_column_text(statement, 0)); + } else { + goto cleanup; } - return vdbeSorterCompare; -} + rc = SQLITE_OK; + /* cleanup will finalize open statement */ -/* -** Sort the linked list of records headed at pTask->pList. Return -** SQLITE_OK if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if -** an error occurs. -*/ -static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){ - int i; - SorterRecord *p; - int rc; - SorterRecord *aSlot[64]; +cleanup: + if(statement) sqlcipher_sqlite3_finalize(statement); + if(db) sqlcipher_sqlite3_close(db); + return rc; +} - rc = vdbeSortAllocUnpacked(pTask); - if( rc!=SQLITE_OK ) return rc; +int sqlcipher_codec_ctx_integrity_check(codec_ctx *ctx, Parse *pParse, char *column) { + Pgno page = 1; + int rc = 0; + char *result; + unsigned char *hmac_out = NULL; + sqlcipher_sqlite3_file *fd = sqlcipher_sqlite3PagerFile(ctx->pBt->pBt->pPager); + i64 file_sz; - p = pList->pList; - pTask->xCompare = vdbeSorterGetCompare(pTask->pSorter); - memset(aSlot, 0, sizeof(aSlot)); + Vdbe *v = sqlcipher_sqlite3GetVdbe(pParse); + sqlcipher_sqlite3VdbeSetNumCols(v, 1); + sqlcipher_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, column, SQLITE_STATIC); - while( p ){ - SorterRecord *pNext; - if( pList->aMemory ){ - if( (u8*)p==pList->aMemory ){ - pNext = 0; - }else{ - assert( p->u.iNextaMemory) ); - pNext = (SorterRecord*)&pList->aMemory[p->u.iNext]; - } - }else{ - pNext = p->u.pNext; - } + if(fd == NULL || fd->pMethods == 0) { + sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, "database file is undefined", P4_TRANSIENT); + sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); + goto cleanup; + } - p->u.pNext = 0; - for(i=0; aSlot[i]; i++){ - p = vdbeSorterMerge(pTask, p, aSlot[i]); - aSlot[i] = 0; - } - aSlot[i] = p; - p = pNext; + if(!(ctx->flags & CIPHER_FLAG_HMAC)) { + sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, "HMAC is not enabled, unable to integrity check", P4_TRANSIENT); + sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); + goto cleanup; } - p = 0; - for(i=0; ipList = p; - assert( pTask->pUnpacked->errCode==SQLITE_OK - || pTask->pUnpacked->errCode==SQLITE_NOMEM - ); - return pTask->pUnpacked->errCode; -} + sqlcipher_sqlite3OsFileSize(fd, &file_sz); + hmac_out = sqlcipher_malloc(ctx->hmac_sz); -/* -** Initialize a PMA-writer object. -*/ -static void vdbePmaWriterInit( - sqlcipher_sqlite3_file *pFd, /* File handle to write to */ - PmaWriter *p, /* Object to populate */ - int nBuf, /* Buffer size */ - i64 iStart /* Offset of pFd to begin writing at */ -){ - memset(p, 0, sizeof(PmaWriter)); - p->aBuffer = (u8*)sqlcipher_sqlite3Malloc(nBuf); - if( !p->aBuffer ){ - p->eFWErr = SQLITE_NOMEM_BKPT; - }else{ - p->iBufEnd = p->iBufStart = (iStart % nBuf); - p->iWriteOff = iStart - p->iBufStart; - p->nBuffer = nBuf; - p->pFd = pFd; - } -} + for(page = 1; page <= file_sz / ctx->page_sz; page++) { + i64 offset = (page - 1) * ctx->page_sz; + int payload_sz = ctx->page_sz - ctx->reserve_sz + ctx->iv_sz; + int read_sz = ctx->page_sz; -/* -** Write nData bytes of data to the PMA. Return SQLITE_OK -** if successful, or an SQLite error code if an error occurs. -*/ -static void vdbePmaWriteBlob(PmaWriter *p, u8 *pData, int nData){ - int nRem = nData; - while( nRem>0 && p->eFWErr==0 ){ - int nCopy = nRem; - if( nCopy>(p->nBuffer - p->iBufEnd) ){ - nCopy = p->nBuffer - p->iBufEnd; + /* skip integrity check on PAGER_SJ_PGNO since it will have no valid content */ + if(sqlcipher_sqlite3pager_is_sj_pgno(ctx->pBt->pBt->pPager, page)) continue; + + if(page==1) { + int page1_offset = ctx->plaintext_header_sz ? ctx->plaintext_header_sz : FILE_HEADER_SZ; + read_sz = read_sz - page1_offset; + payload_sz = payload_sz - page1_offset; + offset += page1_offset; } - memcpy(&p->aBuffer[p->iBufEnd], &pData[nData-nRem], nCopy); - p->iBufEnd += nCopy; - if( p->iBufEnd==p->nBuffer ){ - p->eFWErr = sqlcipher_sqlite3OsWrite(p->pFd, - &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart, - p->iWriteOff + p->iBufStart - ); - p->iBufStart = p->iBufEnd = 0; - p->iWriteOff += p->nBuffer; + sqlcipher_memset(ctx->buffer, 0, ctx->page_sz); + sqlcipher_memset(hmac_out, 0, ctx->hmac_sz); + if(sqlcipher_sqlite3OsRead(fd, ctx->buffer, read_sz, offset) != SQLITE_OK) { + result = sqlcipher_sqlite3_mprintf("error reading %d bytes from file page %d at offset %d", read_sz, page, offset); + sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, result, P4_DYNAMIC); + sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); + } else if(sqlcipher_page_hmac(ctx, ctx->read_ctx, page, ctx->buffer, payload_sz, hmac_out) != SQLITE_OK) { + result = sqlcipher_sqlite3_mprintf("HMAC operation failed for page %d", page); + sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, result, P4_DYNAMIC); + sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); + } else if(sqlcipher_memcmp(ctx->buffer + payload_sz, hmac_out, ctx->hmac_sz) != 0) { + result = sqlcipher_sqlite3_mprintf("HMAC verification failed for page %d", page); + sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, result, P4_DYNAMIC); + sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); } - assert( p->iBufEndnBuffer ); + } - nRem -= nCopy; + if(file_sz % ctx->page_sz != 0) { + result = sqlcipher_sqlite3_mprintf("page %d has an invalid size of %lld bytes", page, file_sz - ((file_sz / ctx->page_sz) * ctx->page_sz)); + sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, result, P4_DYNAMIC); + sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); } + +cleanup: + if(hmac_out != NULL) sqlcipher_free(hmac_out, ctx->hmac_sz); + return SQLITE_OK; } -/* -** Flush any buffered data to disk and clean up the PMA-writer object. -** The results of using the PMA-writer after this call are undefined. -** Return SQLITE_OK if flushing the buffered data succeeds or is not -** required. Otherwise, return an SQLite error code. -** -** Before returning, set *piEof to the offset immediately following the -** last byte written to the file. -*/ -static int vdbePmaWriterFinish(PmaWriter *p, i64 *piEof){ - int rc; - if( p->eFWErr==0 && ALWAYS(p->aBuffer) && p->iBufEnd>p->iBufStart ){ - p->eFWErr = sqlcipher_sqlite3OsWrite(p->pFd, - &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart, - p->iWriteOff + p->iBufStart - ); +int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) { + int i, pass_sz, keyspec_sz, nRes, user_version, rc, oflags; + Db *pDb = 0; + sqlcipher_sqlite3 *db = ctx->pBt->db; + const char *db_filename = sqlcipher_sqlite3_db_filename(db, "main"); + char *set_user_version = NULL, *pass = NULL, *attach_command = NULL, *migrated_db_filename = NULL, *keyspec = NULL, *temp = NULL, *journal_mode = NULL, *set_journal_mode = NULL, *pragma_compat = NULL; + Btree *pDest = NULL, *pSrc = NULL; + sqlcipher_sqlite3_file *srcfile, *destfile; +#if defined(_WIN32) || defined(SQLITE_OS_WINRT) + LPWSTR w_db_filename = NULL, w_migrated_db_filename = NULL; + int w_db_filename_sz = 0, w_migrated_db_filename_sz = 0; +#endif + pass_sz = keyspec_sz = rc = user_version = 0; + + if(!db_filename || sqlcipher_sqlite3Strlen30(db_filename) < 1) + goto cleanup; /* exit immediately if this is an in memory database */ + + /* pull the provided password / key material off the current codec context */ + pass_sz = ctx->read_ctx->pass_sz; + pass = sqlcipher_malloc(pass_sz+1); + memset(pass, 0, pass_sz+1); + memcpy(pass, ctx->read_ctx->pass, pass_sz); + + /* Version 4 - current, no upgrade required, so exit immediately */ + rc = sqlcipher_check_connection(db_filename, pass, pass_sz, "", &user_version, &journal_mode); + if(rc == SQLITE_OK){ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "No upgrade required - exiting"); + goto cleanup; } - *piEof = (p->iWriteOff + p->iBufEnd); - sqlcipher_sqlite3_free(p->aBuffer); - rc = p->eFWErr; - memset(p, 0, sizeof(PmaWriter)); - return rc; -} -/* -** Write value iVal encoded as a varint to the PMA. Return -** SQLITE_OK if successful, or an SQLite error code if an error occurs. -*/ -static void vdbePmaWriteVarint(PmaWriter *p, u64 iVal){ - int nByte; - u8 aByte[10]; - nByte = sqlcipher_sqlite3PutVarint(aByte, iVal); - vdbePmaWriteBlob(p, aByte, nByte); -} + for(i = 3; i > 0; i--) { + pragma_compat = sqlcipher_sqlite3_mprintf("PRAGMA cipher_compatibility = %d;", i); + rc = sqlcipher_check_connection(db_filename, pass, pass_sz, pragma_compat, &user_version, &journal_mode); + if(rc == SQLITE_OK) { + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "Version %d format found", i); + goto migrate; + } + if(pragma_compat) sqlcipher_free(pragma_compat, sqlcipher_sqlite3Strlen30(pragma_compat)); + pragma_compat = NULL; + } -/* -** Write the current contents of in-memory linked-list pList to a level-0 -** PMA in the temp file belonging to sub-task pTask. Return SQLITE_OK if -** successful, or an SQLite error code otherwise. -** -** The format of a PMA is: -** -** * A varint. This varint contains the total number of bytes of content -** in the PMA (not including the varint itself). -** -** * One or more records packed end-to-end in order of ascending keys. -** Each record consists of a varint followed by a blob of data (the -** key). The varint is the number of bytes in the blob of data. -*/ -static int vdbeSorterListToPMA(SortSubtask *pTask, SorterList *pList){ - sqlcipher_sqlite3 *db = pTask->pSorter->db; - int rc = SQLITE_OK; /* Return code */ - PmaWriter writer; /* Object used to write to the file */ + /* if we exit the loop normally we failed to determine the version, this is an error */ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "Upgrade format not determined"); + goto handle_error; -#ifdef SQLITE_DEBUG - /* Set iSz to the expected size of file pTask->file after writing the PMA. - ** This is used by an assert() statement at the end of this function. */ - i64 iSz = pList->szPMA + sqlcipher_sqlite3VarintLen(pList->szPMA) + pTask->file.iEof; -#endif +migrate: - vdbeSorterWorkDebug(pTask, "enter"); - memset(&writer, 0, sizeof(PmaWriter)); - assert( pList->szPMA>0 ); + temp = sqlcipher_sqlite3_mprintf("%s-migrated", db_filename); + /* overallocate migrated_db_filename, because sqlcipher_sqlite3OsOpen will read past the null terminator + * to determine whether the filename was URI formatted */ + migrated_db_filename = sqlcipher_malloc(sqlcipher_sqlite3Strlen30(temp)+2); + memcpy(migrated_db_filename, temp, sqlcipher_sqlite3Strlen30(temp)); + sqlcipher_free(temp, sqlcipher_sqlite3Strlen30(temp)); - /* If the first temporary PMA file has not been opened, open it now. */ - if( pTask->file.pFd==0 ){ - rc = vdbeSorterOpenTempFile(db, 0, &pTask->file.pFd); - assert( rc!=SQLITE_OK || pTask->file.pFd ); - assert( pTask->file.iEof==0 ); - assert( pTask->nPMA==0 ); + attach_command = sqlcipher_sqlite3_mprintf("ATTACH DATABASE '%s' as migrate;", migrated_db_filename, pass); + set_user_version = sqlcipher_sqlite3_mprintf("PRAGMA migrate.user_version = %d;", user_version); + + rc = sqlcipher_sqlite3_exec(db, pragma_compat, NULL, NULL, NULL); + if(rc != SQLITE_OK){ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "set compatibility mode failed, error code %d", rc); + goto handle_error; } - /* Try to get the file to memory map */ - if( rc==SQLITE_OK ){ - vdbeSorterExtendFile(db, pTask->file.pFd, pTask->file.iEof+pList->szPMA+9); + /* force journal mode to DELETE, we will set it back later if different */ + rc = sqlcipher_sqlite3_exec(db, "PRAGMA journal_mode = delete;", NULL, NULL, NULL); + if(rc != SQLITE_OK){ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "force journal mode DELETE failed, error code %d", rc); + goto handle_error; } - /* Sort the list */ - if( rc==SQLITE_OK ){ - rc = vdbeSorterSort(pTask, pList); + rc = sqlcipher_sqlite3_exec(db, attach_command, NULL, NULL, NULL); + if(rc != SQLITE_OK){ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "attach failed, error code %d", rc); + goto handle_error; } - if( rc==SQLITE_OK ){ - SorterRecord *p; - SorterRecord *pNext = 0; + rc = sqlcipher_sqlite3_key_v2(db, "migrate", pass, pass_sz); + if(rc != SQLITE_OK){ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "keying attached database failed, error code %d", rc); + goto handle_error; + } - vdbePmaWriterInit(pTask->file.pFd, &writer, pTask->pSorter->pgsz, - pTask->file.iEof); - pTask->nPMA++; - vdbePmaWriteVarint(&writer, pList->szPMA); - for(p=pList->pList; p; p=pNext){ - pNext = p->u.pNext; - vdbePmaWriteVarint(&writer, p->nVal); - vdbePmaWriteBlob(&writer, SRVAL(p), p->nVal); - if( pList->aMemory==0 ) sqlcipher_sqlite3_free(p); - } - pList->pList = p; - rc = vdbePmaWriterFinish(&writer, &pTask->file.iEof); + rc = sqlcipher_sqlite3_exec(db, "SELECT sqlcipher_export('migrate');", NULL, NULL, NULL); + if(rc != SQLITE_OK){ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_export failed, error code %d", rc); + goto handle_error; } - vdbeSorterWorkDebug(pTask, "exit"); - assert( rc!=SQLITE_OK || pList->pList==0 ); - assert( rc!=SQLITE_OK || pTask->file.iEof==iSz ); - return rc; -} +#ifdef SQLCIPHER_TEST + if((sqlcipher_get_test_flags() & TEST_FAIL_MIGRATE) > 0) { + rc = SQLITE_ERROR; + sqlcipher_log(SQLCIPHER_LOG_ERROR, "simulated migrate failure, error code %d", rc); + goto handle_error; + } +#endif -/* -** Advance the MergeEngine to its next entry. -** Set *pbEof to true there is no next entry because -** the MergeEngine has reached the end of all its inputs. -** -** Return SQLITE_OK if successful or an error code if an error occurs. -*/ -static int vdbeMergeEngineStep( - MergeEngine *pMerger, /* The merge engine to advance to the next row */ - int *pbEof /* Set TRUE at EOF. Set false for more content */ -){ - int rc; - int iPrev = pMerger->aTree[1];/* Index of PmaReader to advance */ - SortSubtask *pTask = pMerger->pTask; + rc = sqlcipher_sqlite3_exec(db, set_user_version, NULL, NULL, NULL); + if(rc != SQLITE_OK){ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "set user version failed, error code %d", rc); + goto handle_error; + } - /* Advance the current PmaReader */ - rc = vdbePmaReaderNext(&pMerger->aReadr[iPrev]); + if( !db->autoCommit ){ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "cannot migrate from within a transaction"); + goto handle_error; + } + if( db->nVdbeActive>1 ){ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "cannot migrate - SQL statements in progress"); + goto handle_error; + } - /* Update contents of aTree[] */ - if( rc==SQLITE_OK ){ - int i; /* Index of aTree[] to recalculate */ - PmaReader *pReadr1; /* First PmaReader to compare */ - PmaReader *pReadr2; /* Second PmaReader to compare */ - int bCached = 0; + pDest = db->aDb[0].pBt; + pDb = &(db->aDb[db->nDb-1]); + pSrc = pDb->pBt; - /* Find the first two PmaReaders to compare. The one that was just - ** advanced (iPrev) and the one next to it in the array. */ - pReadr1 = &pMerger->aReadr[(iPrev & 0xFFFE)]; - pReadr2 = &pMerger->aReadr[(iPrev | 0x0001)]; + nRes = sqlcipher_sqlite3BtreeGetRequestedReserve(pSrc); + /* unset the BTS_PAGESIZE_FIXED flag to avoid SQLITE_READONLY */ + pDest->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; + rc = sqlcipher_sqlite3BtreeSetPageSize(pDest, default_page_size, nRes, 0); + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "set btree page size to %d res %d rc %d", default_page_size, nRes, rc); + if( rc!=SQLITE_OK ) goto handle_error; - for(i=(pMerger->nTree+iPrev)/2; i>0; i=i/2){ - /* Compare pReadr1 and pReadr2. Store the result in variable iRes. */ - int iRes; - if( pReadr1->pFd==0 ){ - iRes = +1; - }else if( pReadr2->pFd==0 ){ - iRes = -1; - }else{ - iRes = pTask->xCompare(pTask, &bCached, - pReadr1->aKey, pReadr1->nKey, pReadr2->aKey, pReadr2->nKey - ); - } + sqlcipherCodecGetKey(db, db->nDb - 1, (void**)&keyspec, &keyspec_sz); + sqlcipherCodecAttach(db, 0, keyspec, keyspec_sz); - /* If pReadr1 contained the smaller value, set aTree[i] to its index. - ** Then set pReadr2 to the next PmaReader to compare to pReadr1. In this - ** case there is no cache of pReadr2 in pTask->pUnpacked, so set - ** pKey2 to point to the record belonging to pReadr2. - ** - ** Alternatively, if pReadr2 contains the smaller of the two values, - ** set aTree[i] to its index and update pReadr1. If vdbeSorterCompare() - ** was actually called above, then pTask->pUnpacked now contains - ** a value equivalent to pReadr2. So set pKey2 to NULL to prevent - ** vdbeSorterCompare() from decoding pReadr2 again. - ** - ** If the two values were equal, then the value from the oldest - ** PMA should be considered smaller. The VdbeSorter.aReadr[] array - ** is sorted from oldest to newest, so pReadr1 contains older values - ** than pReadr2 iff (pReadr1aTree[i] = (int)(pReadr1 - pMerger->aReadr); - pReadr2 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ]; - bCached = 0; - }else{ - if( pReadr1->pFd ) bCached = 0; - pMerger->aTree[i] = (int)(pReadr2 - pMerger->aReadr); - pReadr1 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ]; - } - } - *pbEof = (pMerger->aReadr[pMerger->aTree[1]].pFd==0); - } + srcfile = sqlcipher_sqlite3PagerFile(pSrc->pBt->pPager); + destfile = sqlcipher_sqlite3PagerFile(pDest->pBt->pPager); - return (rc==SQLITE_OK ? pTask->pUnpacked->errCode : rc); -} + sqlcipher_sqlite3OsClose(srcfile); + sqlcipher_sqlite3OsClose(destfile); -#if SQLITE_MAX_WORKER_THREADS>0 -/* -** The main routine for background threads that write level-0 PMAs. -*/ -static void *vdbeSorterFlushThread(void *pCtx){ - SortSubtask *pTask = (SortSubtask*)pCtx; - int rc; /* Return code */ - assert( pTask->bDone==0 ); - rc = vdbeSorterListToPMA(pTask, &pTask->list); - pTask->bDone = 1; - return SQLITE_INT_TO_PTR(rc); -} -#endif /* SQLITE_MAX_WORKER_THREADS>0 */ +#if defined(_WIN32) || defined(SQLITE_OS_WINRT) + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "performing windows MoveFileExA"); -/* -** Flush the current contents of VdbeSorter.list to a new PMA, possibly -** using a background thread. -*/ -static int vdbeSorterFlushPMA(VdbeSorter *pSorter){ -#if SQLITE_MAX_WORKER_THREADS==0 - pSorter->bUsePMA = 1; - return vdbeSorterListToPMA(&pSorter->aTask[0], &pSorter->list); -#else - int rc = SQLITE_OK; - int i; - SortSubtask *pTask = 0; /* Thread context used to create new PMA */ - int nWorker = (pSorter->nTask-1); + w_db_filename_sz = MultiByteToWideChar(CP_UTF8, 0, (LPCCH) db_filename, -1, NULL, 0); + w_db_filename = sqlcipher_malloc(w_db_filename_sz * sizeof(wchar_t)); + w_db_filename_sz = MultiByteToWideChar(CP_UTF8, 0, (LPCCH) db_filename, -1, (const LPWSTR) w_db_filename, w_db_filename_sz); - /* Set the flag to indicate that at least one PMA has been written. - ** Or will be, anyhow. */ - pSorter->bUsePMA = 1; + w_migrated_db_filename_sz = MultiByteToWideChar(CP_UTF8, 0, (LPCCH) migrated_db_filename, -1, NULL, 0); + w_migrated_db_filename = sqlcipher_malloc(w_migrated_db_filename_sz * sizeof(wchar_t)); + w_migrated_db_filename_sz = MultiByteToWideChar(CP_UTF8, 0, (LPCCH) migrated_db_filename, -1, (const LPWSTR) w_migrated_db_filename, w_migrated_db_filename_sz); - /* Select a sub-task to sort and flush the current list of in-memory - ** records to disk. If the sorter is running in multi-threaded mode, - ** round-robin between the first (pSorter->nTask-1) tasks. Except, if - ** the background thread from a sub-tasks previous turn is still running, - ** skip it. If the first (pSorter->nTask-1) sub-tasks are all still busy, - ** fall back to using the final sub-task. The first (pSorter->nTask-1) - ** sub-tasks are prefered as they use background threads - the final - ** sub-task uses the main thread. */ - for(i=0; iiPrev + i + 1) % nWorker; - pTask = &pSorter->aTask[iTest]; - if( pTask->bDone ){ - rc = vdbeSorterJoinThread(pTask); - } - if( rc!=SQLITE_OK || pTask->pThread==0 ) break; + if(!MoveFileExW(w_migrated_db_filename, w_db_filename, MOVEFILE_REPLACE_EXISTING)) { + rc = SQLITE_ERROR; + sqlcipher_log(SQLCIPHER_LOG_ERROR, "error occurred while renaming %d", rc); + goto handle_error; + } +#else + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "performing POSIX rename"); + if ((rc = rename(migrated_db_filename, db_filename)) != 0) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "error occurred while renaming %d", rc); + goto handle_error; } +#endif + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "renamed migration database %s to main database %s: %d", migrated_db_filename, db_filename, rc); - if( rc==SQLITE_OK ){ - if( i==nWorker ){ - /* Use the foreground thread for this operation */ - rc = vdbeSorterListToPMA(&pSorter->aTask[nWorker], &pSorter->list); - }else{ - /* Launch a background thread for this operation */ - u8 *aMem; - void *pCtx; + rc = sqlcipher_sqlite3OsOpen(db->pVfs, migrated_db_filename, srcfile, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MAIN_DB, &oflags); + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "reopened migration database: %d", rc); + if( rc!=SQLITE_OK ) goto handle_error; - assert( pTask!=0 ); - assert( pTask->pThread==0 && pTask->bDone==0 ); - assert( pTask->list.pList==0 ); - assert( pTask->list.aMemory==0 || pSorter->list.aMemory!=0 ); + rc = sqlcipher_sqlite3OsOpen(db->pVfs, db_filename, destfile, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MAIN_DB, &oflags); + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "reopened main database: %d", rc); + if( rc!=SQLITE_OK ) goto handle_error; - aMem = pTask->list.aMemory; - pCtx = (void*)pTask; - pSorter->iPrev = (u8)(pTask - pSorter->aTask); - pTask->list = pSorter->list; - pSorter->list.pList = 0; - pSorter->list.szPMA = 0; - if( aMem ){ - pSorter->list.aMemory = aMem; - pSorter->nMemory = sqlcipher_sqlite3MallocSize(aMem); - }else if( pSorter->list.aMemory ){ - pSorter->list.aMemory = sqlcipher_sqlite3Malloc(pSorter->nMemory); - if( !pSorter->list.aMemory ) return SQLITE_NOMEM_BKPT; - } + sqlcipher_sqlite3pager_reset(pDest->pBt->pPager); + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "reset pager"); - rc = vdbeSorterCreateThread(pTask, vdbeSorterFlushThread, pCtx); - } - } + rc = sqlcipher_sqlite3_exec(db, "DETACH DATABASE migrate;", NULL, NULL, NULL); + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "DETACH DATABASE called %d", rc); + if(rc != SQLITE_OK) goto cleanup; - return rc; -#endif /* SQLITE_MAX_WORKER_THREADS!=0 */ -} + sqlcipher_sqlite3ResetAllSchemasOfConnection(db); + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "reset all schemas"); -/* -** Add a record to the sorter. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterWrite( - const VdbeCursor *pCsr, /* Sorter cursor */ - Mem *pVal /* Memory cell containing record */ -){ - VdbeSorter *pSorter; - int rc = SQLITE_OK; /* Return Code */ - SorterRecord *pNew; /* New list element */ - int bFlush; /* True to flush contents of memory to PMA */ - int nReq; /* Bytes of memory required */ - int nPMA; /* Bytes of PMA space required */ - int t; /* serial type of first record field */ + set_journal_mode = sqlcipher_sqlite3_mprintf("PRAGMA journal_mode = %s;", journal_mode); + rc = sqlcipher_sqlite3_exec(db, set_journal_mode, NULL, NULL, NULL); + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "%s: %d", set_journal_mode, rc); + if( rc!=SQLITE_OK ) goto handle_error; - assert( pCsr->eCurType==CURTYPE_SORTER ); - pSorter = pCsr->uc.pSorter; - getVarint32NR((const u8*)&pVal->z[1], t); - if( t>0 && t<10 && t!=7 ){ - pSorter->typeMask &= SORTER_TYPE_INTEGER; - }else if( t>10 && (t & 0x01) ){ - pSorter->typeMask &= SORTER_TYPE_TEXT; - }else{ - pSorter->typeMask = 0; - } + goto cleanup; - assert( pSorter ); +handle_error: + sqlcipher_log(SQLCIPHER_LOG_ERROR, "An error occurred attempting to migrate the database - last error %d", rc); - /* Figure out whether or not the current contents of memory should be - ** flushed to a PMA before continuing. If so, do so. - ** - ** If using the single large allocation mode (pSorter->aMemory!=0), then - ** flush the contents of memory to a new PMA if (a) at least one value is - ** already in memory and (b) the new value will not fit in memory. - ** - ** Or, if using separate allocations for each record, flush the contents - ** of memory to a PMA if either of the following are true: - ** - ** * The total memory allocated for the in-memory list is greater - ** than (page-size * cache-size), or - ** - ** * The total memory allocated for the in-memory list is greater - ** than (page-size * 10) and sqlcipher_sqlite3HeapNearlyFull() returns true. - */ - nReq = pVal->n + sizeof(SorterRecord); - nPMA = pVal->n + sqlcipher_sqlite3VarintLen(pVal->n); - if( pSorter->mxPmaSize ){ - if( pSorter->list.aMemory ){ - bFlush = pSorter->iMemory && (pSorter->iMemory+nReq) > pSorter->mxPmaSize; - }else{ - bFlush = ( - (pSorter->list.szPMA > pSorter->mxPmaSize) - || (pSorter->list.szPMA > pSorter->mnPmaSize && sqlcipher_sqlite3HeapNearlyFull()) - ); - } - if( bFlush ){ - rc = vdbeSorterFlushPMA(pSorter); - pSorter->list.szPMA = 0; - pSorter->iMemory = 0; - assert( rc!=SQLITE_OK || pSorter->list.pList==0 ); - } +cleanup: + if(migrated_db_filename) { + int del_rc = sqlcipher_sqlite3OsDelete(db->pVfs, migrated_db_filename, 0); + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "deleted migration database: %d", del_rc); } - pSorter->list.szPMA += nPMA; - if( nPMA>pSorter->mxKeysize ){ - pSorter->mxKeysize = nPMA; - } + if(pass) sqlcipher_free(pass, pass_sz); + if(attach_command) sqlcipher_free(attach_command, sqlcipher_sqlite3Strlen30(attach_command)); + if(migrated_db_filename) sqlcipher_free(migrated_db_filename, sqlcipher_sqlite3Strlen30(migrated_db_filename)); + if(set_user_version) sqlcipher_free(set_user_version, sqlcipher_sqlite3Strlen30(set_user_version)); + if(set_journal_mode) sqlcipher_free(set_journal_mode, sqlcipher_sqlite3Strlen30(set_journal_mode)); + if(journal_mode) sqlcipher_free(journal_mode, sqlcipher_sqlite3Strlen30(journal_mode)); + if(pragma_compat) sqlcipher_free(pragma_compat, sqlcipher_sqlite3Strlen30(pragma_compat)); +#if defined(_WIN32) || defined(SQLITE_OS_WINRT) + if(w_db_filename) sqlcipher_free(w_db_filename, w_db_filename_sz); + if(w_migrated_db_filename) sqlcipher_free(w_migrated_db_filename, w_migrated_db_filename_sz); +#endif + return rc; +} - if( pSorter->list.aMemory ){ - int nMin = pSorter->iMemory + nReq; +int sqlcipher_codec_add_random(codec_ctx *ctx, const char *zRight, int random_sz){ + const char *suffix = &zRight[random_sz-1]; + int n = random_sz - 3; /* adjust for leading x' and tailing ' */ + if (n > 0 && + sqlcipher_sqlite3StrNICmp((const char *)zRight ,"x'", 2) == 0 && + sqlcipher_sqlite3StrNICmp(suffix, "'", 1) == 0 && + n % 2 == 0) { + int rc = 0; + int buffer_sz = n / 2; + unsigned char *random; + const unsigned char *z = (const unsigned char *)zRight + 2; /* adjust lead offset of x' */ + sqlcipher_log(SQLCIPHER_LOG_DEBUG, "sqlcipher_codec_add_random: using raw random blob from hex"); + random = sqlcipher_malloc(buffer_sz); + memset(random, 0, buffer_sz); + cipher_hex2bin(z, n, random); + rc = ctx->provider->add_random(ctx->provider_ctx, random, buffer_sz); + sqlcipher_free(random, buffer_sz); + return rc; + } + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_codec_add_random: attemt to add random with invalid format"); + return SQLITE_ERROR; +} - if( nMin>pSorter->nMemory ){ - u8 *aNew; - sqlcipher_sqlite3_int64 nNew = 2 * (sqlcipher_sqlite3_int64)pSorter->nMemory; - int iListOff = -1; - if( pSorter->list.pList ){ - iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory; - } - while( nNew < nMin ) nNew = nNew*2; - if( nNew > pSorter->mxPmaSize ) nNew = pSorter->mxPmaSize; - if( nNew < nMin ) nNew = nMin; - aNew = sqlcipher_sqlite3Realloc(pSorter->list.aMemory, nNew); - if( !aNew ) return SQLITE_NOMEM_BKPT; - if( iListOff>=0 ){ - pSorter->list.pList = (SorterRecord*)&aNew[iListOff]; - } - pSorter->list.aMemory = aNew; - pSorter->nMemory = nNew; - } +#if !defined(SQLITE_OMIT_TRACE) +static int sqlcipher_profile_callback(unsigned int trace, void *file, void *stmt, void *run_time){ + FILE *f = (FILE*) file; + char *fmt = "Elapsed time:%.3f ms - %s\n"; + double elapsed = (*((sqlcipher_sqlite3_uint64*)run_time))/1000000.0; +#ifdef __ANDROID__ + if(f == NULL) { + __android_log_print(ANDROID_LOG_DEBUG, "sqlcipher", fmt, elapsed, sqlcipher_sqlite3_sql((sqlcipher_sqlite3_stmt*)stmt)); + } +#endif + if(f) fprintf(f, fmt, elapsed, sqlcipher_sqlite3_sql((sqlcipher_sqlite3_stmt*)stmt)); + return SQLITE_OK; +} +#endif - pNew = (SorterRecord*)&pSorter->list.aMemory[pSorter->iMemory]; - pSorter->iMemory += ROUND8(nReq); - if( pSorter->list.pList ){ - pNew->u.iNext = (int)((u8*)(pSorter->list.pList) - pSorter->list.aMemory); - } - }else{ - pNew = (SorterRecord *)sqlcipher_sqlite3Malloc(nReq); - if( pNew==0 ){ - return SQLITE_NOMEM_BKPT; +int sqlcipher_cipher_profile(sqlcipher_sqlite3 *db, const char *destination){ +#if defined(SQLITE_OMIT_TRACE) + return SQLITE_ERROR; +#else + FILE *f = NULL; + if(sqlcipher_sqlite3_stricmp(destination, "off") == 0){ + sqlcipher_sqlite3_trace_v2(db, 0, NULL, NULL); /* disable tracing */ + } else { + if(sqlcipher_sqlite3_stricmp(destination, "stdout") == 0){ + f = stdout; + }else if(sqlcipher_sqlite3_stricmp(destination, "stderr") == 0){ + f = stderr; + }else if(sqlcipher_sqlite3_stricmp(destination, "logcat") == 0){ + f = NULL; /* file pointer will be NULL indicating logcat on android */ + }else{ +#if !defined(SQLCIPHER_PROFILE_USE_FOPEN) && (defined(_WIN32) && (__STDC_VERSION__ > 199901L) || defined(SQLITE_OS_WINRT)) + if(fopen_s(&f, destination, "a") != 0) return SQLITE_ERROR; +#else + if((f = fopen(destination, "a")) == 0) return SQLITE_ERROR; +#endif } - pNew->u.pNext = pSorter->list.pList; + sqlcipher_sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, sqlcipher_profile_callback, f); } - - memcpy(SRVAL(pNew), pVal->z, pVal->n); - pNew->nVal = pVal->n; - pSorter->list.pList = pNew; - - return rc; + return SQLITE_OK; +#endif } -/* -** Read keys from pIncr->pMerger and populate pIncr->aFile[1]. The format -** of the data stored in aFile[1] is the same as that used by regular PMAs, -** except that the number-of-bytes varint is omitted from the start. -*/ -static int vdbeIncrPopulate(IncrMerger *pIncr){ - int rc = SQLITE_OK; - int rc2; - i64 iStart = pIncr->iStartOff; - SorterFile *pOut = &pIncr->aFile[1]; - SortSubtask *pTask = pIncr->pTask; - MergeEngine *pMerger = pIncr->pMerger; - PmaWriter writer; - assert( pIncr->bEof==0 ); +int sqlcipher_codec_fips_status(codec_ctx *ctx) { + return ctx->provider->fips_status(ctx->provider_ctx); +} - vdbeSorterPopulateDebug(pTask, "enter"); +const char* sqlcipher_codec_get_provider_version(codec_ctx *ctx) { + return ctx->provider->get_provider_version(ctx->provider_ctx); +} - vdbePmaWriterInit(pOut->pFd, &writer, pTask->pSorter->pgsz, iStart); - while( rc==SQLITE_OK ){ - int dummy; - PmaReader *pReader = &pMerger->aReadr[ pMerger->aTree[1] ]; - int nKey = pReader->nKey; - i64 iEof = writer.iWriteOff + writer.iBufEnd; +#ifndef SQLCIPHER_OMIT_LOG +/* constants from https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-crt/misc/gettimeofday.c */ +#define FILETIME_1970 116444736000000000ull /* seconds between 1/1/1601 and 1/1/1970 */ +#define HECTONANOSEC_PER_SEC 10000000ull +void sqlcipher_log(unsigned int level, const char *message, ...) { + va_list params; + va_start(params, message); - /* Check if the output file is full or if the input has been exhausted. - ** In either case exit the loop. */ - if( pReader->pFd==0 ) break; - if( (iEof + nKey + sqlcipher_sqlite3VarintLen(nKey))>(iStart + pIncr->mxSz) ) break; +#ifdef CODEC_DEBUG +#ifdef __ANDROID__ + __android_log_vprint(ANDROID_LOG_DEBUG, "sqlcipher", message, params); +#else + vfprintf(stderr, message, params); + fprintf(stderr, "\n"); +#endif +#endif - /* Write the next key to the output. */ - vdbePmaWriteVarint(&writer, nKey); - vdbePmaWriteBlob(&writer, pReader->aKey, nKey); - assert( pIncr->pMerger->pTask==pTask ); - rc = vdbeMergeEngineStep(pIncr->pMerger, &dummy); + if(level > sqlcipher_log_level || (sqlcipher_log_logcat == 0 && sqlcipher_log_file == NULL)) { + /* no log target or tag not in included filters */ + goto end; } - - rc2 = vdbePmaWriterFinish(&writer, &pOut->iEof); - if( rc==SQLITE_OK ) rc = rc2; - vdbeSorterPopulateDebug(pTask, "exit"); - return rc; + if(sqlcipher_log_file != NULL){ + char buffer[24]; + struct tm tt; + int ms; + time_t sec; +#ifdef _WIN32 + SYSTEMTIME st; + FILETIME ft; + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + sec = (time_t) ((*((sqlite_int64*)&ft) - FILETIME_1970) / HECTONANOSEC_PER_SEC); + ms = st.wMilliseconds; + localtime_s(&tt, &sec); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + sec = tv.tv_sec; + ms = tv.tv_usec/1000.0; + localtime_r(&sec, &tt); +#endif + if(strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tt)) { + fprintf((FILE*)sqlcipher_log_file, "%s.%03d: ", buffer, ms); + vfprintf((FILE*)sqlcipher_log_file, message, params); + fprintf((FILE*)sqlcipher_log_file, "\n"); + } + } +#ifdef __ANDROID__ + if(sqlcipher_log_logcat) { + __android_log_vprint(ANDROID_LOG_DEBUG, "sqlcipher", message, params); + } +#endif +end: + va_end(params); } +#endif -#if SQLITE_MAX_WORKER_THREADS>0 -/* -** The main routine for background threads that populate aFile[1] of -** multi-threaded IncrMerger objects. -*/ -static void *vdbeIncrPopulateThread(void *pCtx){ - IncrMerger *pIncr = (IncrMerger*)pCtx; - void *pRet = SQLITE_INT_TO_PTR( vdbeIncrPopulate(pIncr) ); - pIncr->pTask->bDone = 1; - return pRet; +void sqlcipher_set_log_level(unsigned int level) { + sqlcipher_log_level = level; } -/* -** Launch a background thread to populate aFile[1] of pIncr. -*/ -static int vdbeIncrBgPopulate(IncrMerger *pIncr){ - void *p = (void*)pIncr; - assert( pIncr->bUseThread ); - return vdbeSorterCreateThread(pIncr->pTask, vdbeIncrPopulateThread, p); +int sqlcipher_set_log(const char *destination){ +#ifdef SQLCIPHER_OMIT_LOG + return SQLITE_ERROR; +#else + /* close open trace file if it is not stdout or stderr, then + reset trace settings */ + if(sqlcipher_log_file != NULL && sqlcipher_log_file != stdout && sqlcipher_log_file != stderr) { + fclose((FILE*)sqlcipher_log_file); + } + sqlcipher_log_file = NULL; + sqlcipher_log_logcat = 0; + + if(sqlcipher_sqlite3_stricmp(destination, "logcat") == 0){ + sqlcipher_log_logcat = 1; + } else if(sqlcipher_sqlite3_stricmp(destination, "stdout") == 0){ + sqlcipher_log_file = stdout; + }else if(sqlcipher_sqlite3_stricmp(destination, "stderr") == 0){ + sqlcipher_log_file = stderr; + }else if(sqlcipher_sqlite3_stricmp(destination, "off") != 0){ +#if !defined(SQLCIPHER_PROFILE_USE_FOPEN) && (defined(_WIN32) && (__STDC_VERSION__ > 199901L) || defined(SQLITE_OS_WINRT)) + if(fopen_s(&sqlcipher_log_file, destination, "a") != 0) return SQLITE_ERROR; +#else + if((sqlcipher_log_file = fopen(destination, "a")) == 0) return SQLITE_ERROR; +#endif + } + sqlcipher_log(SQLCIPHER_LOG_INFO, "sqlcipher_set_log: set log to %s", destination); + return SQLITE_OK; +#endif } + #endif +/* END SQLCIPHER */ +/************** End of crypto_impl.c *****************************************/ +/************** Begin file crypto_libtomcrypt.c ******************************/ /* -** This function is called when the PmaReader corresponding to pIncr has -** finished reading the contents of aFile[0]. Its purpose is to "refill" -** aFile[0] such that the PmaReader should start rereading it from the -** beginning. +** SQLCipher +** http://sqlcipher.net ** -** For single-threaded objects, this is accomplished by literally reading -** keys from pIncr->pMerger and repopulating aFile[0]. +** Copyright (c) 2008 - 2013, ZETETIC LLC +** All rights reserved. ** -** For multi-threaded objects, all that is required is to wait until the -** background thread is finished (if it is not already) and then swap -** aFile[0] and aFile[1] in place. If the contents of pMerger have not -** been exhausted, this function also launches a new background thread -** to populate the new aFile[1]. +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the ZETETIC LLC nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** -** SQLITE_OK is returned on success, or an SQLite error code otherwise. */ -static int vdbeIncrSwap(IncrMerger *pIncr){ - int rc = SQLITE_OK; +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC +#ifdef SQLCIPHER_CRYPTO_LIBTOMCRYPT +/* #include "sqliteInt.h" */ +/* #include "sqlcipher.h" */ +#include -#if SQLITE_MAX_WORKER_THREADS>0 - if( pIncr->bUseThread ){ - rc = vdbeSorterJoinThread(pIncr->pTask); +#define FORTUNA_MAX_SZ 32 +static prng_state prng; +static volatile unsigned int ltc_init = 0; +static volatile unsigned int ltc_ref_count = 0; - if( rc==SQLITE_OK ){ - SorterFile f0 = pIncr->aFile[0]; - pIncr->aFile[0] = pIncr->aFile[1]; - pIncr->aFile[1] = f0; - } +#define LTC_CIPHER "rijndael" - if( rc==SQLITE_OK ){ - if( pIncr->aFile[0].iEof==pIncr->iStartOff ){ - pIncr->bEof = 1; - }else{ - rc = vdbeIncrBgPopulate(pIncr); - } - } - }else -#endif - { - rc = vdbeIncrPopulate(pIncr); - pIncr->aFile[0] = pIncr->aFile[1]; - if( pIncr->aFile[0].iEof==pIncr->iStartOff ){ - pIncr->bEof = 1; +static int sqlcipher_ltc_add_random(void *ctx, void *buffer, int length) { + int rc = 0; + int data_to_read = length; + int block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ; + const unsigned char * data = (const unsigned char *)buffer; + + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_add_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_add_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND"); + + while(data_to_read > 0){ + rc = fortuna_add_entropy(data, block_sz, &prng); + rc = rc != CRYPT_OK ? SQLITE_ERROR : SQLITE_OK; + if(rc != SQLITE_OK){ + break; } + data_to_read -= block_sz; + data += block_sz; + block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ; } + fortuna_ready(&prng); - return rc; -} + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_add_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_add_random: left SQLCIPHER_MUTEX_PROVIDER_RAND"); -/* -** Allocate and return a new IncrMerger object to read data from pMerger. -** -** If an OOM condition is encountered, return NULL. In this case free the -** pMerger argument before returning. -*/ -static int vdbeIncrMergerNew( - SortSubtask *pTask, /* The thread that will be using the new IncrMerger */ - MergeEngine *pMerger, /* The MergeEngine that the IncrMerger will control */ - IncrMerger **ppOut /* Write the new IncrMerger here */ -){ - int rc = SQLITE_OK; - IncrMerger *pIncr = *ppOut = (IncrMerger*) - (sqlcipher_sqlite3FaultSim(100) ? 0 : sqlcipher_sqlite3MallocZero(sizeof(*pIncr))); - if( pIncr ){ - pIncr->pMerger = pMerger; - pIncr->pTask = pTask; - pIncr->mxSz = MAX(pTask->pSorter->mxKeysize+9,pTask->pSorter->mxPmaSize/2); - pTask->file2.iEof += pIncr->mxSz; - }else{ - vdbeMergeEngineFree(pMerger); - rc = SQLITE_NOMEM_BKPT; - } return rc; } -#if SQLITE_MAX_WORKER_THREADS>0 -/* -** Set the "use-threads" flag on object pIncr. -*/ -static void vdbeIncrMergerSetThreads(IncrMerger *pIncr){ - pIncr->bUseThread = 1; - pIncr->pTask->file2.iEof -= pIncr->mxSz; -} -#endif /* SQLITE_MAX_WORKER_THREADS>0 */ +static int sqlcipher_ltc_activate(void *ctx) { + unsigned char random_buffer[FORTUNA_MAX_SZ]; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ); + if(ltc_init == 0) { + if(register_prng(&fortuna_desc) < 0) return SQLITE_ERROR; + if(register_cipher(&rijndael_desc) < 0) return SQLITE_ERROR; + if(register_hash(&sha512_desc) < 0) return SQLITE_ERROR; + if(register_hash(&sha256_desc) < 0) return SQLITE_ERROR; + if(register_hash(&sha1_desc) < 0) return SQLITE_ERROR; + if(fortuna_start(&prng) != CRYPT_OK) { + return SQLITE_ERROR; + } -/* -** Recompute pMerger->aTree[iOut] by comparing the next keys on the -** two PmaReaders that feed that entry. Neither of the PmaReaders -** are advanced. This routine merely does the comparison. -*/ -static void vdbeMergeEngineCompare( - MergeEngine *pMerger, /* Merge engine containing PmaReaders to compare */ - int iOut /* Store the result in pMerger->aTree[iOut] */ -){ - int i1; - int i2; - int iRes; - PmaReader *p1; - PmaReader *p2; + ltc_init = 1; + } + ltc_ref_count++; - assert( iOutnTree && iOut>0 ); +#ifndef SQLCIPHER_TEST + sqlcipher_sqlite3_randomness(FORTUNA_MAX_SZ, random_buffer); +#endif - if( iOut>=(pMerger->nTree/2) ){ - i1 = (iOut - pMerger->nTree/2) * 2; - i2 = i1 + 1; - }else{ - i1 = pMerger->aTree[iOut*2]; - i2 = pMerger->aTree[iOut*2+1]; + if(sqlcipher_ltc_add_random(ctx, random_buffer, FORTUNA_MAX_SZ) != SQLITE_OK) { + return SQLITE_ERROR; } + sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ); - p1 = &pMerger->aReadr[i1]; - p2 = &pMerger->aReadr[i2]; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); - if( p1->pFd==0 ){ - iRes = i2; - }else if( p2->pFd==0 ){ - iRes = i1; - }else{ - SortSubtask *pTask = pMerger->pTask; - int bCached = 0; - int res; - assert( pTask->pUnpacked!=0 ); /* from vdbeSortSubtaskMain() */ - res = pTask->xCompare( - pTask, &bCached, p1->aKey, p1->nKey, p2->aKey, p2->nKey - ); - if( res<=0 ){ - iRes = i1; - }else{ - iRes = i2; - } + return SQLITE_OK; +} + +static int sqlcipher_ltc_deactivate(void *ctx) { + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_deactivate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_deactivate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + + ltc_ref_count--; + if(ltc_ref_count == 0){ + fortuna_done(&prng); + sqlcipher_memset((void *)&prng, 0, sizeof(prng)); } - pMerger->aTree[iOut] = iRes; -} + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_deactivate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_deactivate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); -/* -** Allowed values for the eMode parameter to vdbeMergeEngineInit() -** and vdbePmaReaderIncrMergeInit(). -** -** Only INCRINIT_NORMAL is valid in single-threaded builds (when -** SQLITE_MAX_WORKER_THREADS==0). The other values are only used -** when there exists one or more separate worker threads. -*/ -#define INCRINIT_NORMAL 0 -#define INCRINIT_TASK 1 -#define INCRINIT_ROOT 2 + return SQLITE_OK; +} -/* -** Forward reference required as the vdbeIncrMergeInit() and -** vdbePmaReaderIncrInit() routines are called mutually recursively when -** building a merge tree. -*/ -static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode); +static const char* sqlcipher_ltc_get_provider_name(void *ctx) { + return "libtomcrypt"; +} -/* -** Initialize the MergeEngine object passed as the second argument. Once this -** function returns, the first key of merged data may be read from the -** MergeEngine object in the usual fashion. -** -** If argument eMode is INCRINIT_ROOT, then it is assumed that any IncrMerge -** objects attached to the PmaReader objects that the merger reads from have -** already been populated, but that they have not yet populated aFile[0] and -** set the PmaReader objects up to read from it. In this case all that is -** required is to call vdbePmaReaderNext() on each PmaReader to point it at -** its first key. -** -** Otherwise, if eMode is any value other than INCRINIT_ROOT, then use -** vdbePmaReaderIncrMergeInit() to initialize each PmaReader that feeds data -** to pMerger. -** -** SQLITE_OK is returned if successful, or an SQLite error code otherwise. -*/ -static int vdbeMergeEngineInit( - SortSubtask *pTask, /* Thread that will run pMerger */ - MergeEngine *pMerger, /* MergeEngine to initialize */ - int eMode /* One of the INCRINIT_XXX constants */ -){ - int rc = SQLITE_OK; /* Return code */ - int i; /* For looping over PmaReader objects */ - int nTree; /* Number of subtrees to merge */ +static const char* sqlcipher_ltc_get_provider_version(void *ctx) { + return SCRYPT; +} - /* Failure to allocate the merge would have been detected prior to - ** invoking this routine */ - assert( pMerger!=0 ); +static int sqlcipher_ltc_random(void *ctx, void *buffer, int length) { + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND"); - /* eMode is always INCRINIT_NORMAL in single-threaded mode */ - assert( SQLITE_MAX_WORKER_THREADS>0 || eMode==INCRINIT_NORMAL ); + fortuna_read(buffer, length, &prng); - /* Verify that the MergeEngine is assigned to a single thread */ - assert( pMerger->pTask==0 ); - pMerger->pTask = pTask; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_random: left SQLCIPHER_MUTEX_PROVIDER_RAND"); - nTree = pMerger->nTree; - for(i=0; i0 && eMode==INCRINIT_ROOT ){ - /* PmaReaders should be normally initialized in order, as if they are - ** reading from the same temp file this makes for more linear file IO. - ** However, in the INCRINIT_ROOT case, if PmaReader aReadr[nTask-1] is - ** in use it will block the vdbePmaReaderNext() call while it uses - ** the main thread to fill its buffer. So calling PmaReaderNext() - ** on this PmaReader before any of the multi-threaded PmaReaders takes - ** better advantage of multi-processor hardware. */ - rc = vdbePmaReaderNext(&pMerger->aReadr[nTree-i-1]); - }else{ - rc = vdbePmaReaderIncrInit(&pMerger->aReadr[i], INCRINIT_NORMAL); - } - if( rc!=SQLITE_OK ) return rc; - } + return SQLITE_OK; +} - for(i=pMerger->nTree-1; i>0; i--){ - vdbeMergeEngineCompare(pMerger, i); +static int sqlcipher_ltc_hmac(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out) { + int rc, hash_idx; + hmac_state hmac; + unsigned long outlen; + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + hash_idx = find_hash("sha1"); + break; + case SQLCIPHER_HMAC_SHA256: + hash_idx = find_hash("sha256"); + break; + case SQLCIPHER_HMAC_SHA512: + hash_idx = find_hash("sha512"); + break; + default: + return SQLITE_ERROR; } - return pTask->pUnpacked->errCode; -} -/* -** The PmaReader passed as the first argument is guaranteed to be an -** incremental-reader (pReadr->pIncr!=0). This function serves to open -** and/or initialize the temp file related fields of the IncrMerge -** object at (pReadr->pIncr). -** -** If argument eMode is set to INCRINIT_NORMAL, then all PmaReaders -** in the sub-tree headed by pReadr are also initialized. Data is then -** loaded into the buffers belonging to pReadr and it is set to point to -** the first key in its range. -** -** If argument eMode is set to INCRINIT_TASK, then pReadr is guaranteed -** to be a multi-threaded PmaReader and this function is being called in a -** background thread. In this case all PmaReaders in the sub-tree are -** initialized as for INCRINIT_NORMAL and the aFile[1] buffer belonging to -** pReadr is populated. However, pReadr itself is not set up to point -** to its first key. A call to vdbePmaReaderNext() is still required to do -** that. -** -** The reason this function does not call vdbePmaReaderNext() immediately -** in the INCRINIT_TASK case is that vdbePmaReaderNext() assumes that it has -** to block on thread (pTask->thread) before accessing aFile[1]. But, since -** this entire function is being run by thread (pTask->thread), that will -** lead to the current background thread attempting to join itself. -** -** Finally, if argument eMode is set to INCRINIT_ROOT, it may be assumed -** that pReadr->pIncr is a multi-threaded IncrMerge objects, and that all -** child-trees have already been initialized using IncrInit(INCRINIT_TASK). -** In this case vdbePmaReaderNext() is called on all child PmaReaders and -** the current PmaReader set to point to the first key in its range. -** -** SQLITE_OK is returned if successful, or an SQLite error code otherwise. -*/ -static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){ - int rc = SQLITE_OK; - IncrMerger *pIncr = pReadr->pIncr; - SortSubtask *pTask = pIncr->pTask; - sqlcipher_sqlite3 *db = pTask->pSorter->db; + if(hash_idx < 0) return SQLITE_ERROR; + outlen = hash_descriptor[hash_idx].hashsize; - /* eMode is always INCRINIT_NORMAL in single-threaded mode */ - assert( SQLITE_MAX_WORKER_THREADS>0 || eMode==INCRINIT_NORMAL ); + if(in == NULL) return SQLITE_ERROR; + if((rc = hmac_init(&hmac, hash_idx, hmac_key, key_sz)) != CRYPT_OK) return SQLITE_ERROR; + if((rc = hmac_process(&hmac, in, in_sz)) != CRYPT_OK) return SQLITE_ERROR; + if(in2 != NULL && (rc = hmac_process(&hmac, in2, in2_sz)) != CRYPT_OK) return SQLITE_ERROR; + if((rc = hmac_done(&hmac, out, &outlen)) != CRYPT_OK) return SQLITE_ERROR; + return SQLITE_OK; +} - rc = vdbeMergeEngineInit(pTask, pIncr->pMerger, eMode); +static int sqlcipher_ltc_kdf(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) { + int rc, hash_idx; + unsigned long outlen = key_sz; - /* Set up the required files for pIncr. A multi-theaded IncrMerge object - ** requires two temp files to itself, whereas a single-threaded object - ** only requires a region of pTask->file2. */ - if( rc==SQLITE_OK ){ - int mxSz = pIncr->mxSz; -#if SQLITE_MAX_WORKER_THREADS>0 - if( pIncr->bUseThread ){ - rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[0].pFd); - if( rc==SQLITE_OK ){ - rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[1].pFd); - } - }else -#endif - /*if( !pIncr->bUseThread )*/{ - if( pTask->file2.pFd==0 ){ - assert( pTask->file2.iEof>0 ); - rc = vdbeSorterOpenTempFile(db, pTask->file2.iEof, &pTask->file2.pFd); - pTask->file2.iEof = 0; - } - if( rc==SQLITE_OK ){ - pIncr->aFile[1].pFd = pTask->file2.pFd; - pIncr->iStartOff = pTask->file2.iEof; - pTask->file2.iEof += mxSz; - } - } + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + hash_idx = find_hash("sha1"); + break; + case SQLCIPHER_HMAC_SHA256: + hash_idx = find_hash("sha256"); + break; + case SQLCIPHER_HMAC_SHA512: + hash_idx = find_hash("sha512"); + break; + default: + return SQLITE_ERROR; } + if(hash_idx < 0) return SQLITE_ERROR; -#if SQLITE_MAX_WORKER_THREADS>0 - if( rc==SQLITE_OK && pIncr->bUseThread ){ - /* Use the current thread to populate aFile[1], even though this - ** PmaReader is multi-threaded. If this is an INCRINIT_TASK object, - ** then this function is already running in background thread - ** pIncr->pTask->thread. - ** - ** If this is the INCRINIT_ROOT object, then it is running in the - ** main VDBE thread. But that is Ok, as that thread cannot return - ** control to the VDBE or proceed with anything useful until the - ** first results are ready from this merger object anyway. - */ - assert( eMode==INCRINIT_ROOT || eMode==INCRINIT_TASK ); - rc = vdbeIncrPopulate(pIncr); + if((rc = pkcs_5_alg2(pass, pass_sz, salt, salt_sz, + workfactor, hash_idx, key, &outlen)) != CRYPT_OK) { + return SQLITE_ERROR; } -#endif + return SQLITE_OK; +} + +static const char* sqlcipher_ltc_get_cipher(void *ctx) { + return "aes-256-cbc"; +} + +static int sqlcipher_ltc_cipher(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out) { + int rc, cipher_idx; + symmetric_CBC cbc; + + if((cipher_idx = find_cipher(LTC_CIPHER)) == -1) return SQLITE_ERROR; + if((rc = cbc_start(cipher_idx, iv, key, key_sz, 0, &cbc)) != CRYPT_OK) return SQLITE_ERROR; + rc = mode == 1 ? cbc_encrypt(in, out, in_sz, &cbc) : cbc_decrypt(in, out, in_sz, &cbc); + if(rc != CRYPT_OK) return SQLITE_ERROR; + cbc_done(&cbc); + return SQLITE_OK; +} - if( rc==SQLITE_OK && (SQLITE_MAX_WORKER_THREADS==0 || eMode!=INCRINIT_TASK) ){ - rc = vdbePmaReaderNext(pReadr); - } +static int sqlcipher_ltc_get_key_sz(void *ctx) { + int cipher_idx = find_cipher(LTC_CIPHER); + return cipher_descriptor[cipher_idx].max_key_length; +} - return rc; +static int sqlcipher_ltc_get_iv_sz(void *ctx) { + int cipher_idx = find_cipher(LTC_CIPHER); + return cipher_descriptor[cipher_idx].block_length; } -#if SQLITE_MAX_WORKER_THREADS>0 -/* -** The main routine for vdbePmaReaderIncrMergeInit() operations run in -** background threads. -*/ -static void *vdbePmaReaderBgIncrInit(void *pCtx){ - PmaReader *pReader = (PmaReader*)pCtx; - void *pRet = SQLITE_INT_TO_PTR( - vdbePmaReaderIncrMergeInit(pReader,INCRINIT_TASK) - ); - pReader->pIncr->pTask->bDone = 1; - return pRet; +static int sqlcipher_ltc_get_block_sz(void *ctx) { + int cipher_idx = find_cipher(LTC_CIPHER); + return cipher_descriptor[cipher_idx].block_length; } -#endif -/* -** If the PmaReader passed as the first argument is not an incremental-reader -** (if pReadr->pIncr==0), then this function is a no-op. Otherwise, it invokes -** the vdbePmaReaderIncrMergeInit() function with the parameters passed to -** this routine to initialize the incremental merge. -** -** If the IncrMerger object is multi-threaded (IncrMerger.bUseThread==1), -** then a background thread is launched to call vdbePmaReaderIncrMergeInit(). -** Or, if the IncrMerger is single threaded, the same function is called -** using the current thread. -*/ -static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode){ - IncrMerger *pIncr = pReadr->pIncr; /* Incremental merger */ - int rc = SQLITE_OK; /* Return code */ - if( pIncr ){ -#if SQLITE_MAX_WORKER_THREADS>0 - assert( pIncr->bUseThread==0 || eMode==INCRINIT_TASK ); - if( pIncr->bUseThread ){ - void *pCtx = (void*)pReadr; - rc = vdbeSorterCreateThread(pIncr->pTask, vdbePmaReaderBgIncrInit, pCtx); - }else -#endif - { - rc = vdbePmaReaderIncrMergeInit(pReadr, eMode); - } +static int sqlcipher_ltc_get_hmac_sz(void *ctx, int algorithm) { + int hash_idx; + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + hash_idx = find_hash("sha1"); + break; + case SQLCIPHER_HMAC_SHA256: + hash_idx = find_hash("sha256"); + break; + case SQLCIPHER_HMAC_SHA512: + hash_idx = find_hash("sha512"); + break; + default: + return 0; } - return rc; + + if(hash_idx < 0) return 0; + + return hash_descriptor[hash_idx].hashsize; } -/* -** Allocate a new MergeEngine object to merge the contents of nPMA level-0 -** PMAs from pTask->file. If no error occurs, set *ppOut to point to -** the new object and return SQLITE_OK. Or, if an error does occur, set *ppOut -** to NULL and return an SQLite error code. -** -** When this function is called, *piOffset is set to the offset of the -** first PMA to read from pTask->file. Assuming no error occurs, it is -** set to the offset immediately following the last byte of the last -** PMA before returning. If an error does occur, then the final value of -** *piOffset is undefined. -*/ -static int vdbeMergeEngineLevel0( - SortSubtask *pTask, /* Sorter task to read from */ - int nPMA, /* Number of PMAs to read */ - i64 *piOffset, /* IN/OUT: Readr offset in pTask->file */ - MergeEngine **ppOut /* OUT: New merge-engine */ -){ - MergeEngine *pNew; /* Merge engine to return */ - i64 iOff = *piOffset; - int i; - int rc = SQLITE_OK; +static int sqlcipher_ltc_ctx_init(void **ctx) { + sqlcipher_ltc_activate(NULL); + return SQLITE_OK; +} - *ppOut = pNew = vdbeMergeEngineNew(nPMA); - if( pNew==0 ) rc = SQLITE_NOMEM_BKPT; +static int sqlcipher_ltc_ctx_free(void **ctx) { + sqlcipher_ltc_deactivate(&ctx); + return SQLITE_OK; +} - for(i=0; iaReadr[i]; - rc = vdbePmaReaderInit(pTask, &pTask->file, iOff, pReadr, &nDummy); - iOff = pReadr->iEof; - } +static int sqlcipher_ltc_fips_status(void *ctx) { + return 0; +} - if( rc!=SQLITE_OK ){ - vdbeMergeEngineFree(pNew); - *ppOut = 0; - } - *piOffset = iOff; - return rc; +int sqlcipher_ltc_setup(sqlcipher_provider *p) { + p->activate = sqlcipher_ltc_activate; + p->deactivate = sqlcipher_ltc_deactivate; + p->get_provider_name = sqlcipher_ltc_get_provider_name; + p->random = sqlcipher_ltc_random; + p->hmac = sqlcipher_ltc_hmac; + p->kdf = sqlcipher_ltc_kdf; + p->cipher = sqlcipher_ltc_cipher; + p->get_cipher = sqlcipher_ltc_get_cipher; + p->get_key_sz = sqlcipher_ltc_get_key_sz; + p->get_iv_sz = sqlcipher_ltc_get_iv_sz; + p->get_block_sz = sqlcipher_ltc_get_block_sz; + p->get_hmac_sz = sqlcipher_ltc_get_hmac_sz; + p->ctx_init = sqlcipher_ltc_ctx_init; + p->ctx_free = sqlcipher_ltc_ctx_free; + p->add_random = sqlcipher_ltc_add_random; + p->fips_status = sqlcipher_ltc_fips_status; + p->get_provider_version = sqlcipher_ltc_get_provider_version; + return SQLITE_OK; } +#endif +#endif +/* END SQLCIPHER */ + +/************** End of crypto_libtomcrypt.c **********************************/ +/************** Begin file crypto_nss.c **************************************/ /* -** Return the depth of a tree comprising nPMA PMAs, assuming a fanout of -** SORTER_MAX_MERGE_COUNT. The returned value does not include leaf nodes. +** SQLCipher +** http://sqlcipher.net ** -** i.e. +** Copyright (c) 2008 - 2013, ZETETIC LLC +** All rights reserved. ** -** nPMA<=16 -> TreeDepth() == 0 -** nPMA<=256 -> TreeDepth() == 1 -** nPMA<=65536 -> TreeDepth() == 2 -*/ -static int vdbeSorterTreeDepth(int nPMA){ - int nDepth = 0; - i64 nDiv = SORTER_MAX_MERGE_COUNT; - while( nDiv < (i64)nPMA ){ - nDiv = nDiv * SORTER_MAX_MERGE_COUNT; - nDepth++; - } - return nDepth; -} - -/* -** pRoot is the root of an incremental merge-tree with depth nDepth (according -** to vdbeSorterTreeDepth()). pLeaf is the iSeq'th leaf to be added to the -** tree, counting from zero. This function adds pLeaf to the tree. +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the ZETETIC LLC nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** -** If successful, SQLITE_OK is returned. If an error occurs, an SQLite error -** code is returned and pLeaf is freed. */ -static int vdbeSorterAddToTree( - SortSubtask *pTask, /* Task context */ - int nDepth, /* Depth of tree according to TreeDepth() */ - int iSeq, /* Sequence number of leaf within tree */ - MergeEngine *pRoot, /* Root of tree */ - MergeEngine *pLeaf /* Leaf to add to tree */ -){ - int rc = SQLITE_OK; - int nDiv = 1; - int i; - MergeEngine *p = pRoot; - IncrMerger *pIncr; - - rc = vdbeIncrMergerNew(pTask, pLeaf, &pIncr); +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC +#ifdef SQLCIPHER_CRYPTO_NSS +/* #include "crypto.h" */ +/* #include "sqlcipher.h" */ +#include +#include +#include - for(i=1; iaReadr[iIter]; +int sqlcipher_nss_setup(sqlcipher_provider *p); - if( pReadr->pIncr==0 ){ - MergeEngine *pNew = vdbeMergeEngineNew(SORTER_MAX_MERGE_COUNT); - if( pNew==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - rc = vdbeIncrMergerNew(pTask, pNew, &pReadr->pIncr); - } - } - if( rc==SQLITE_OK ){ - p = pReadr->pIncr->pMerger; - nDiv = nDiv / SORTER_MAX_MERGE_COUNT; - } - } +static int sqlcipher_nss_activate(void *ctx) { - if( rc==SQLITE_OK ){ - p->aReadr[iSeq % SORTER_MAX_MERGE_COUNT].pIncr = pIncr; - }else{ - vdbeIncrFree(pIncr); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + if (nss_init_context == NULL) { + nss_init_context = NSS_InitContext("", "", "", "", NULL, + NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | + NSS_INIT_FORCEOPEN | NSS_INIT_OPTIMIZESPACE | NSS_INIT_NOROOTINIT); } - return rc; + nss_init_count++; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + return SQLITE_OK; } -/* -** This function is called as part of a SorterRewind() operation on a sorter -** that has already written two or more level-0 PMAs to one or more temp -** files. It builds a tree of MergeEngine/IncrMerger/PmaReader objects that -** can be used to incrementally merge all PMAs on disk. -** -** If successful, SQLITE_OK is returned and *ppOut set to point to the -** MergeEngine object at the root of the tree before returning. Or, if an -** error occurs, an SQLite error code is returned and the final value -** of *ppOut is undefined. -*/ -static int vdbeSorterMergeTreeBuild( - VdbeSorter *pSorter, /* The VDBE cursor that implements the sort */ - MergeEngine **ppOut /* Write the MergeEngine here */ -){ - MergeEngine *pMain = 0; - int rc = SQLITE_OK; - int iTask; +static int sqlcipher_nss_deactivate(void *ctx) { + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); -#if SQLITE_MAX_WORKER_THREADS>0 - /* If the sorter uses more than one task, then create the top-level - ** MergeEngine here. This MergeEngine will read data from exactly - ** one PmaReader per sub-task. */ - assert( pSorter->bUseThreads || pSorter->nTask==1 ); - if( pSorter->nTask>1 ){ - pMain = vdbeMergeEngineNew(pSorter->nTask); - if( pMain==0 ) rc = SQLITE_NOMEM_BKPT; + nss_init_count--; + if (nss_init_count == 0 && nss_init_context != NULL) { + NSS_ShutdownContext(nss_init_context); + nss_init_context = NULL; } -#endif - - for(iTask=0; rc==SQLITE_OK && iTasknTask; iTask++){ - SortSubtask *pTask = &pSorter->aTask[iTask]; - assert( pTask->nPMA>0 || SQLITE_MAX_WORKER_THREADS>0 ); - if( SQLITE_MAX_WORKER_THREADS==0 || pTask->nPMA ){ - MergeEngine *pRoot = 0; /* Root node of tree for this task */ - int nDepth = vdbeSorterTreeDepth(pTask->nPMA); - i64 iReadOff = 0; - - if( pTask->nPMA<=SORTER_MAX_MERGE_COUNT ){ - rc = vdbeMergeEngineLevel0(pTask, pTask->nPMA, &iReadOff, &pRoot); - }else{ - int i; - int iSeq = 0; - pRoot = vdbeMergeEngineNew(SORTER_MAX_MERGE_COUNT); - if( pRoot==0 ) rc = SQLITE_NOMEM_BKPT; - for(i=0; inPMA && rc==SQLITE_OK; i += SORTER_MAX_MERGE_COUNT){ - MergeEngine *pMerger = 0; /* New level-0 PMA merger */ - int nReader; /* Number of level-0 PMAs to merge */ - nReader = MIN(pTask->nPMA - i, SORTER_MAX_MERGE_COUNT); - rc = vdbeMergeEngineLevel0(pTask, nReader, &iReadOff, &pMerger); - if( rc==SQLITE_OK ){ - rc = vdbeSorterAddToTree(pTask, nDepth, iSeq++, pRoot, pMerger); - } - } - } + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + return SQLITE_OK; +} - if( rc==SQLITE_OK ){ -#if SQLITE_MAX_WORKER_THREADS>0 - if( pMain!=0 ){ - rc = vdbeIncrMergerNew(pTask, pRoot, &pMain->aReadr[iTask].pIncr); - }else -#endif - { - assert( pMain==0 ); - pMain = pRoot; - } - }else{ - vdbeMergeEngineFree(pRoot); - } - } - } +static int sqlcipher_nss_add_random(void *ctx, void *buffer, int length) { + return SQLITE_OK; +} - if( rc!=SQLITE_OK ){ - vdbeMergeEngineFree(pMain); - pMain = 0; - } - *ppOut = pMain; - return rc; +/* generate a defined number of random bytes */ +static int sqlcipher_nss_random (void *ctx, void *buffer, int length) { + // PK11_GenerateRandom should be thread-safe. + return (PK11_GenerateRandom((unsigned char *)buffer, length) == SECSuccess) ? SQLITE_OK : SQLITE_ERROR; } -/* -** This function is called as part of an sqlcipher_sqlite3VdbeSorterRewind() operation -** on a sorter that has written two or more PMAs to temporary files. It sets -** up either VdbeSorter.pMerger (for single threaded sorters) or pReader -** (for multi-threaded sorters) so that it can be used to iterate through -** all records stored in the sorter. -** -** SQLITE_OK is returned if successful, or an SQLite error code otherwise. -*/ -static int vdbeSorterSetupMerge(VdbeSorter *pSorter){ - int rc; /* Return code */ - SortSubtask *pTask0 = &pSorter->aTask[0]; - MergeEngine *pMain = 0; -#if SQLITE_MAX_WORKER_THREADS - sqlcipher_sqlite3 *db = pTask0->pSorter->db; - int i; - SorterCompare xCompare = vdbeSorterGetCompare(pSorter); - for(i=0; inTask; i++){ - pSorter->aTask[i].xCompare = xCompare; - } -#endif +static const char* sqlcipher_nss_get_provider_name(void *ctx) { + return "nss"; +} - rc = vdbeSorterMergeTreeBuild(pSorter, &pMain); - if( rc==SQLITE_OK ){ -#if SQLITE_MAX_WORKER_THREADS - assert( pSorter->bUseThreads==0 || pSorter->nTask>1 ); - if( pSorter->bUseThreads ){ - int iTask; - PmaReader *pReadr = 0; - SortSubtask *pLast = &pSorter->aTask[pSorter->nTask-1]; - rc = vdbeSortAllocUnpacked(pLast); - if( rc==SQLITE_OK ){ - pReadr = (PmaReader*)sqlcipher_sqlite3DbMallocZero(db, sizeof(PmaReader)); - pSorter->pReader = pReadr; - if( pReadr==0 ) rc = SQLITE_NOMEM_BKPT; - } - if( rc==SQLITE_OK ){ - rc = vdbeIncrMergerNew(pLast, pMain, &pReadr->pIncr); - if( rc==SQLITE_OK ){ - vdbeIncrMergerSetThreads(pReadr->pIncr); - for(iTask=0; iTask<(pSorter->nTask-1); iTask++){ - IncrMerger *pIncr; - if( (pIncr = pMain->aReadr[iTask].pIncr) ){ - vdbeIncrMergerSetThreads(pIncr); - assert( pIncr->pTask!=pLast ); - } - } - for(iTask=0; rc==SQLITE_OK && iTasknTask; iTask++){ - /* Check that: - ** - ** a) The incremental merge object is configured to use the - ** right task, and - ** b) If it is using task (nTask-1), it is configured to run - ** in single-threaded mode. This is important, as the - ** root merge (INCRINIT_ROOT) will be using the same task - ** object. - */ - PmaReader *p = &pMain->aReadr[iTask]; - assert( p->pIncr==0 || ( - (p->pIncr->pTask==&pSorter->aTask[iTask]) /* a */ - && (iTask!=pSorter->nTask-1 || p->pIncr->bUseThread==0) /* b */ - )); - rc = vdbePmaReaderIncrInit(p, INCRINIT_TASK); - } - } - pMain = 0; - } - if( rc==SQLITE_OK ){ - rc = vdbePmaReaderIncrMergeInit(pReadr, INCRINIT_ROOT); - } - }else -#endif - { - rc = vdbeMergeEngineInit(pTask0, pMain, INCRINIT_NORMAL); - pSorter->pMerger = pMain; - pMain = 0; - } - } +static const char* sqlcipher_nss_get_provider_version(void *ctx) { + return NSS_GetVersion(); +} - if( rc!=SQLITE_OK ){ - vdbeMergeEngineFree(pMain); - } - return rc; +static const char* sqlcipher_nss_get_cipher(void *ctx) { + return "aes-256-cbc"; } +static int sqlcipher_nss_get_key_sz(void *ctx) { + return AES_256_KEY_LENGTH; +} -/* -** Once the sorter has been populated by calls to sqlcipher_sqlite3VdbeSorterWrite, -** this function is called to prepare for iterating through the records -** in sorted order. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterRewind(const VdbeCursor *pCsr, int *pbEof){ - VdbeSorter *pSorter; - int rc = SQLITE_OK; /* Return code */ +static int sqlcipher_nss_get_iv_sz(void *ctx) { + return AES_BLOCK_SIZE; +} - assert( pCsr->eCurType==CURTYPE_SORTER ); - pSorter = pCsr->uc.pSorter; - assert( pSorter ); +static int sqlcipher_nss_get_block_sz(void *ctx) { + return AES_BLOCK_SIZE; +} - /* If no data has been written to disk, then do not do so now. Instead, - ** sort the VdbeSorter.pRecord list. The vdbe layer will read data directly - ** from the in-memory list. */ - if( pSorter->bUsePMA==0 ){ - if( pSorter->list.pList ){ - *pbEof = 0; - rc = vdbeSorterSort(&pSorter->aTask[0], &pSorter->list); - }else{ - *pbEof = 1; - } - return rc; +static int sqlcipher_nss_get_hmac_sz(void *ctx, int algorithm) { + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + return SHA1_LENGTH; + break; + case SQLCIPHER_HMAC_SHA256: + return SHA256_LENGTH; + break; + case SQLCIPHER_HMAC_SHA512: + return SHA512_LENGTH; + break; + default: + return 0; } +} - /* Write the current in-memory list to a PMA. When the VdbeSorterWrite() - ** function flushes the contents of memory to disk, it immediately always - ** creates a new list consisting of a single key immediately afterwards. - ** So the list is never empty at this point. */ - assert( pSorter->list.pList ); - rc = vdbeSorterFlushPMA(pSorter); - - /* Join all threads */ - rc = vdbeSorterJoinAll(pSorter, rc); +static int sqlcipher_nss_hmac(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out) { + int rc = SQLITE_OK; + unsigned int length; + unsigned int outLen; + PK11Context* context = NULL; + PK11SlotInfo * slot = NULL; + PK11SymKey* symKey = NULL; + if(in == NULL) goto error; + CK_MECHANISM_TYPE mech; + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + mech = CKM_SHA_1_HMAC; + break; + case SQLCIPHER_HMAC_SHA256: + mech = CKM_SHA256_HMAC; + break; + case SQLCIPHER_HMAC_SHA512: + mech = CKM_SHA512_HMAC; + break; + default: + goto error; + } + length = sqlcipher_nss_get_hmac_sz(ctx, algorithm); + slot = PK11_GetInternalSlot(); + if (slot == NULL) goto error; + SECItem keyItem; + keyItem.data = hmac_key; + keyItem.len = key_sz; + symKey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, + CKA_SIGN, &keyItem, NULL); + if (symKey == NULL) goto error; + SECItem noParams; + noParams.data = 0; + noParams.len = 0; + context = PK11_CreateContextBySymKey(mech, CKA_SIGN, symKey, &noParams); + if (context == NULL) goto error; + if (PK11_DigestBegin(context) != SECSuccess) goto error; + if (PK11_DigestOp(context, in, in_sz) != SECSuccess) goto error; + if (in2 != NULL) { + if (PK11_DigestOp(context, in2, in2_sz) != SECSuccess) goto error; + } + if (PK11_DigestFinal(context, out, &outLen, length) != SECSuccess) goto error; - vdbeSorterRewindDebug("rewind"); + goto cleanup; + error: + rc = SQLITE_ERROR; + cleanup: + if (context) PK11_DestroyContext(context, PR_TRUE); + if (symKey) PK11_FreeSymKey(symKey); + if (slot) PK11_FreeSlot(slot); + return rc; +} - /* Assuming no errors have occurred, set up a merger structure to - ** incrementally read and merge all remaining PMAs. */ - assert( pSorter->pReader==0 ); - if( rc==SQLITE_OK ){ - rc = vdbeSorterSetupMerge(pSorter); - *pbEof = 0; +static int sqlcipher_nss_kdf(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) { + int rc = SQLITE_OK; + PK11SlotInfo * slot = NULL; + SECAlgorithmID * algid = NULL; + PK11SymKey* symKey = NULL; + SECOidTag oidtag; + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + oidtag = SEC_OID_HMAC_SHA1; + break; + case SQLCIPHER_HMAC_SHA256: + oidtag = SEC_OID_HMAC_SHA256; + break; + case SQLCIPHER_HMAC_SHA512: + oidtag = SEC_OID_HMAC_SHA512; + break; + default: + goto error; } + SECItem secSalt; + secSalt.data = salt; + secSalt.len = salt_sz; + // Always pass SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this parameter + // is unused for key generation. It is currently only used + // for PBKDF2 authentication or key (un)wrapping when specifying an + // encryption algorithm (PBES2). + algid = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1, + oidtag, key_sz, workfactor, &secSalt); + if (algid == NULL) goto error; + slot = PK11_GetInternalSlot(); + if (slot == NULL) goto error; + SECItem pwItem; + pwItem.data = (unsigned char *) pass; // PK11_PBEKeyGen doesn't modify the key. + pwItem.len = pass_sz; + symKey = PK11_PBEKeyGen(slot, algid, &pwItem, PR_FALSE, NULL); + if (symKey == NULL) goto error; + if (PK11_ExtractKeyValue(symKey) != SECSuccess) goto error; + // No need to free keyData as it is a buffer managed by symKey. + SECItem* keyData = PK11_GetKeyData(symKey); + if (keyData == NULL) goto error; + memcpy(key, keyData->data, key_sz); - vdbeSorterRewindDebug("rewinddone"); - return rc; + goto cleanup; + error: + rc = SQLITE_ERROR; + cleanup: + if (slot) PK11_FreeSlot(slot); + if (algid) SECOID_DestroyAlgorithmID(algid, PR_TRUE); + if (symKey) PK11_FreeSymKey(symKey); + return rc; } -/* -** Advance to the next element in the sorter. Return value: -** -** SQLITE_OK success -** SQLITE_DONE end of data -** otherwise some kind of error. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterNext(sqlcipher_sqlite3 *db, const VdbeCursor *pCsr){ - VdbeSorter *pSorter; - int rc; /* Return code */ - - assert( pCsr->eCurType==CURTYPE_SORTER ); - pSorter = pCsr->uc.pSorter; - assert( pSorter->bUsePMA || (pSorter->pReader==0 && pSorter->pMerger==0) ); - if( pSorter->bUsePMA ){ - assert( pSorter->pReader==0 || pSorter->pMerger==0 ); - assert( pSorter->bUseThreads==0 || pSorter->pReader ); - assert( pSorter->bUseThreads==1 || pSorter->pMerger ); -#if SQLITE_MAX_WORKER_THREADS>0 - if( pSorter->bUseThreads ){ - rc = vdbePmaReaderNext(pSorter->pReader); - if( rc==SQLITE_OK && pSorter->pReader->pFd==0 ) rc = SQLITE_DONE; - }else -#endif - /*if( !pSorter->bUseThreads )*/ { - int res = 0; - assert( pSorter->pMerger!=0 ); - assert( pSorter->pMerger->pTask==(&pSorter->aTask[0]) ); - rc = vdbeMergeEngineStep(pSorter->pMerger, &res); - if( rc==SQLITE_OK && res ) rc = SQLITE_DONE; - } - }else{ - SorterRecord *pFree = pSorter->list.pList; - pSorter->list.pList = pFree->u.pNext; - pFree->u.pNext = 0; - if( pSorter->list.aMemory==0 ) vdbeSorterRecordFree(db, pFree); - rc = pSorter->list.pList ? SQLITE_OK : SQLITE_DONE; +static int sqlcipher_nss_cipher(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out) { + int rc = SQLITE_OK; + PK11SlotInfo * slot = NULL; + PK11SymKey* symKey = NULL; + unsigned int outLen; + SECItem params; + params.data = iv; + params.len = sqlcipher_nss_get_iv_sz(ctx); + slot = PK11_GetInternalSlot(); + if (slot == NULL) goto error; + SECItem keyItem; + keyItem.data = key; + keyItem.len = key_sz; + symKey = PK11_ImportSymKey(slot, CKM_AES_CBC, PK11_OriginUnwrap, + CKA_ENCRYPT, &keyItem, NULL); + if (symKey == NULL) goto error; + SECStatus rv; + if (mode == CIPHER_ENCRYPT) { + rv = PK11_Encrypt(symKey, CKM_AES_CBC, ¶ms, out, &outLen, + in_sz + 16, in, in_sz); + } else { + rv = PK11_Decrypt(symKey, CKM_AES_CBC, ¶ms, out, &outLen, + in_sz + 16, in, in_sz); } - return rc; + if (rv != SECSuccess) goto error; + + goto cleanup; + error: + rc = SQLITE_ERROR; + cleanup: + if (slot) PK11_FreeSlot(slot); + if (symKey) PK11_FreeSymKey(symKey); + return rc; } -/* -** Return a pointer to a buffer owned by the sorter that contains the -** current key. -*/ -static void *vdbeSorterRowkey( - const VdbeSorter *pSorter, /* Sorter object */ - int *pnKey /* OUT: Size of current key in bytes */ -){ - void *pKey; - if( pSorter->bUsePMA ){ - PmaReader *pReader; -#if SQLITE_MAX_WORKER_THREADS>0 - if( pSorter->bUseThreads ){ - pReader = pSorter->pReader; - }else -#endif - /*if( !pSorter->bUseThreads )*/{ - pReader = &pSorter->pMerger->aReadr[pSorter->pMerger->aTree[1]]; - } - *pnKey = pReader->nKey; - pKey = pReader->aKey; - }else{ - *pnKey = pSorter->list.pList->nVal; - pKey = SRVAL(pSorter->list.pList); - } - return pKey; +static int sqlcipher_nss_ctx_init(void **ctx) { + sqlcipher_nss_activate(NULL); + return SQLITE_OK; } -/* -** Copy the current sorter key into the memory cell pOut. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterRowkey(const VdbeCursor *pCsr, Mem *pOut){ - VdbeSorter *pSorter; - void *pKey; int nKey; /* Sorter key to copy into pOut */ +static int sqlcipher_nss_ctx_free(void **ctx) { + sqlcipher_nss_deactivate(NULL); + return SQLITE_OK; +} - assert( pCsr->eCurType==CURTYPE_SORTER ); - pSorter = pCsr->uc.pSorter; - pKey = vdbeSorterRowkey(pSorter, &nKey); - if( sqlcipher_sqlite3VdbeMemClearAndResize(pOut, nKey) ){ - return SQLITE_NOMEM_BKPT; - } - pOut->n = nKey; - MemSetTypeFlag(pOut, MEM_Blob); - memcpy(pOut->z, pKey, nKey); +static int sqlcipher_nss_fips_status(void *ctx) { + return 0; +} +int sqlcipher_nss_setup(sqlcipher_provider *p) { + p->activate = sqlcipher_nss_activate; + p->deactivate = sqlcipher_nss_deactivate; + p->random = sqlcipher_nss_random; + p->get_provider_name = sqlcipher_nss_get_provider_name; + p->hmac = sqlcipher_nss_hmac; + p->kdf = sqlcipher_nss_kdf; + p->cipher = sqlcipher_nss_cipher; + p->get_cipher = sqlcipher_nss_get_cipher; + p->get_key_sz = sqlcipher_nss_get_key_sz; + p->get_iv_sz = sqlcipher_nss_get_iv_sz; + p->get_block_sz = sqlcipher_nss_get_block_sz; + p->get_hmac_sz = sqlcipher_nss_get_hmac_sz; + p->ctx_init = sqlcipher_nss_ctx_init; + p->ctx_free = sqlcipher_nss_ctx_free; + p->add_random = sqlcipher_nss_add_random; + p->fips_status = sqlcipher_nss_fips_status; + p->get_provider_version = sqlcipher_nss_get_provider_version; return SQLITE_OK; } +#endif +#endif +/* END SQLCIPHER */ + +/************** End of crypto_nss.c ******************************************/ +/************** Begin file crypto_openssl.c **********************************/ /* -** Compare the key in memory cell pVal with the key that the sorter cursor -** passed as the first argument currently points to. For the purposes of -** the comparison, ignore the rowid field at the end of each record. +** SQLCipher +** http://sqlcipher.net ** -** If the sorter cursor key contains any NULL values, consider it to be -** less than pVal. Even if pVal also contains NULL values. +** Copyright (c) 2008 - 2013, ZETETIC LLC +** All rights reserved. ** -** If an error occurs, return an SQLite error code (i.e. SQLITE_NOMEM). -** Otherwise, set *pRes to a negative, zero or positive value if the -** key in pVal is smaller than, equal to or larger than the current sorter -** key. +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the ZETETIC LLC nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** -** This routine forms the core of the OP_SorterCompare opcode, which in -** turn is used to verify uniqueness when constructing a UNIQUE INDEX. */ -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeSorterCompare( - const VdbeCursor *pCsr, /* Sorter cursor */ - Mem *pVal, /* Value to compare to current sorter key */ - int nKeyCol, /* Compare this many columns */ - int *pRes /* OUT: Result of comparison */ -){ - VdbeSorter *pSorter; - UnpackedRecord *r2; - KeyInfo *pKeyInfo; - int i; - void *pKey; int nKey; /* Sorter key to compare pVal with */ +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC +#ifdef SQLCIPHER_CRYPTO_OPENSSL +/* #include "sqliteInt.h" */ +/* #include "crypto.h" */ +/* #include "sqlcipher.h" */ +#include +#include +#include +#include +#include +#include - assert( pCsr->eCurType==CURTYPE_SORTER ); - pSorter = pCsr->uc.pSorter; - r2 = pSorter->pUnpacked; - pKeyInfo = pCsr->pKeyInfo; - if( r2==0 ){ - r2 = pSorter->pUnpacked = sqlcipher_sqlite3VdbeAllocUnpackedRecord(pKeyInfo); - if( r2==0 ) return SQLITE_NOMEM_BKPT; - r2->nField = nKeyCol; - } - assert( r2->nField==nKeyCol ); +static unsigned int openssl_init_count = 0; - pKey = vdbeSorterRowkey(pSorter, &nKey); - sqlcipher_sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, r2); - for(i=0; iaMem[i].flags & MEM_Null ){ - *pRes = -1; - return SQLITE_OK; +static void sqlcipher_openssl_log_errors() { + unsigned long err = 0; + while((err = ERR_get_error()) != 0) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_log_errors: ERR_get_error() returned %lx: %s", err, ERR_error_string(err, NULL)); } +} + +#if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) +static HMAC_CTX *HMAC_CTX_new(void) +{ + HMAC_CTX *ctx = OPENSSL_malloc(sizeof(*ctx)); + if (ctx != NULL) { + HMAC_CTX_init(ctx); } + return ctx; +} - *pRes = sqlcipher_sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2); - return SQLITE_OK; +/* Per 1.1.0 (https://wiki.openssl.org/index.php/1.1_API_Changes) + HMAC_CTX_free should call HMAC_CTX_cleanup, then EVP_MD_CTX_Cleanup. + HMAC_CTX_cleanup internally calls EVP_MD_CTX_cleanup so these + calls are not needed. */ +static void HMAC_CTX_free(HMAC_CTX *ctx) +{ + if (ctx != NULL) { + HMAC_CTX_cleanup(ctx); + OPENSSL_free(ctx); + } } +#endif -/************** End of vdbesort.c ********************************************/ -/************** Begin file vdbevtab.c ****************************************/ -/* -** 2020-03-23 -** -** 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 virtual-tables for examining the bytecode content -** of a prepared statement. -*/ -/* #include "sqliteInt.h" */ -#if defined(SQLITE_ENABLE_BYTECODE_VTAB) && !defined(SQLITE_OMIT_VIRTUALTABLE) -/* #include "vdbeInt.h" */ +static int sqlcipher_openssl_add_random(void *ctx, void *buffer, int length) { +#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_add_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_add_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND"); +#endif + RAND_add(buffer, length, 0); +#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_add_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_add_random: left SQLCIPHER_MUTEX_PROVIDER_RAND"); +#endif + return SQLITE_OK; +} -/* An instance of the bytecode() table-valued function. -*/ -typedef struct bytecodevtab bytecodevtab; -struct bytecodevtab { - sqlcipher_sqlite3_vtab base; /* Base class - must be first */ - sqlcipher_sqlite3 *db; /* Database connection */ - int bTablesUsed; /* 2 for tables_used(). 0 for bytecode(). */ -}; +#define OPENSSL_CIPHER EVP_aes_256_cbc() -/* A cursor for scanning through the bytecode +/* activate and initialize sqlcipher. Most importantly, this will automatically + intialize OpenSSL's EVP system if it hasn't already be externally. Note that + this function may be called multiple times as new codecs are intiialized. + Thus it performs some basic counting to ensure that only the last and final + sqlcipher_openssl_deactivate() will free the EVP structures. */ -typedef struct bytecodevtab_cursor bytecodevtab_cursor; -struct bytecodevtab_cursor { - sqlcipher_sqlite3_vtab_cursor base; /* Base class - must be first */ - sqlcipher_sqlite3_stmt *pStmt; /* The statement whose bytecode is displayed */ - int iRowid; /* The rowid of the output table */ - int iAddr; /* Address */ - int needFinalize; /* Cursors owns pStmt and must finalize it */ - int showSubprograms; /* Provide a listing of subprograms */ - Op *aOp; /* Operand array */ - char *zP4; /* Rendered P4 value */ - const char *zType; /* tables_used.type */ - const char *zSchema; /* tables_used.schema */ - const char *zName; /* tables_used.name */ - Mem sub; /* Subprograms */ -}; +static int sqlcipher_openssl_activate(void *ctx) { + /* initialize openssl and increment the internal init counter + but only if it hasn't been initalized outside of SQLCipher by this program + e.g. on startup */ + int rc = 0; -/* -** Create a new bytecode() table-valued function. -*/ -static int bytecodevtabConnect( - sqlcipher_sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlcipher_sqlite3_vtab **ppVtab, - char **pzErr -){ - bytecodevtab *pNew; - int rc; - int isTabUsed = pAux!=0; - const char *azSchema[2] = { - /* bytecode() schema */ - "CREATE TABLE x(" - "addr INT," - "opcode TEXT," - "p1 INT," - "p2 INT," - "p3 INT," - "p4 TEXT," - "p5 INT," - "comment TEXT," - "subprog TEXT," - "stmt HIDDEN" - ");", + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); - /* Tables_used() schema */ - "CREATE TABLE x(" - "type TEXT," - "schema TEXT," - "name TEXT," - "wr INT," - "subprog TEXT," - "stmt HIDDEN" - ");" - }; +#if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) + ERR_load_crypto_strings(); +#endif - rc = sqlcipher_sqlite3_declare_vtab(db, azSchema[isTabUsed]); - if( rc==SQLITE_OK ){ - pNew = sqlcipher_sqlite3_malloc( sizeof(*pNew) ); - *ppVtab = (sqlcipher_sqlite3_vtab*)pNew; - if( pNew==0 ) return SQLITE_NOMEM; - memset(pNew, 0, sizeof(*pNew)); - pNew->db = db; - pNew->bTablesUsed = isTabUsed*2; +#ifdef SQLCIPHER_FIPS + if(!FIPS_mode()){ + if(!(rc = FIPS_mode_set(1))){ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_activate: FIPS_mode_set() returned %d", rc); + sqlcipher_openssl_log_errors(); + } } - return rc; -} +#endif -/* -** This method is the destructor for bytecodevtab objects. -*/ -static int bytecodevtabDisconnect(sqlcipher_sqlite3_vtab *pVtab){ - bytecodevtab *p = (bytecodevtab*)pVtab; - sqlcipher_sqlite3_free(p); + openssl_init_count++; + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); return SQLITE_OK; } -/* -** Constructor for a new bytecodevtab_cursor object. -*/ -static int bytecodevtabOpen(sqlcipher_sqlite3_vtab *p, sqlcipher_sqlite3_vtab_cursor **ppCursor){ - bytecodevtab *pVTab = (bytecodevtab*)p; - bytecodevtab_cursor *pCur; - pCur = sqlcipher_sqlite3_malloc( sizeof(*pCur) ); - if( pCur==0 ) return SQLITE_NOMEM; - memset(pCur, 0, sizeof(*pCur)); - sqlcipher_sqlite3VdbeMemInit(&pCur->sub, pVTab->db, 1); - *ppCursor = &pCur->base; +/* deactivate SQLCipher, most imporantly decremeting the activation count and + freeing the EVP structures on the final deactivation to ensure that + OpenSSL memory is cleaned up */ +static int sqlcipher_openssl_deactivate(void *ctx) { + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_deactivate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_deactivate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + + openssl_init_count--; + + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_deactivate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_deactivate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE"); return SQLITE_OK; } -/* -** Clear all internal content from a bytecodevtab cursor. -*/ -static void bytecodevtabCursorClear(bytecodevtab_cursor *pCur){ - sqlcipher_sqlite3_free(pCur->zP4); - pCur->zP4 = 0; - sqlcipher_sqlite3VdbeMemRelease(&pCur->sub); - sqlcipher_sqlite3VdbeMemSetNull(&pCur->sub); - if( pCur->needFinalize ){ - sqlcipher_sqlite3_finalize(pCur->pStmt); - } - pCur->pStmt = 0; - pCur->needFinalize = 0; - pCur->zType = 0; - pCur->zSchema = 0; - pCur->zName = 0; +static const char* sqlcipher_openssl_get_provider_name(void *ctx) { + return "openssl"; } -/* -** Destructor for a bytecodevtab_cursor. -*/ -static int bytecodevtabClose(sqlcipher_sqlite3_vtab_cursor *cur){ - bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; - bytecodevtabCursorClear(pCur); - sqlcipher_sqlite3_free(pCur); +static const char* sqlcipher_openssl_get_provider_version(void *ctx) { + return OPENSSL_VERSION_TEXT; +} + +/* generate a defined number of random bytes */ +static int sqlcipher_openssl_random (void *ctx, void *buffer, int length) { + int rc = 0; + /* concurrent calls to RAND_bytes can cause a crash under some openssl versions when a + naive application doesn't use CRYPTO_set_locking_callback and + CRYPTO_THREADID_set_callback to ensure openssl thread safety. + This is simple workaround to prevent this common crash + but a more proper solution is that applications setup platform-appropriate + thread saftey in openssl externally */ +#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND"); + sqlcipher_sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND"); +#endif + rc = RAND_bytes((unsigned char *)buffer, length); +#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND)); + sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_openssl_random: left SQLCIPHER_MUTEX_PROVIDER_RAND"); +#endif + if(!rc) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_random: RAND_bytes() returned %d", rc); + sqlcipher_openssl_log_errors(); + return SQLITE_ERROR; + } return SQLITE_OK; } +static int sqlcipher_openssl_hmac(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out) { + int rc = 0; -/* -** Advance a bytecodevtab_cursor to its next row of output. -*/ -static int bytecodevtabNext(sqlcipher_sqlite3_vtab_cursor *cur){ - bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; - bytecodevtab *pTab = (bytecodevtab*)cur->pVtab; - int rc; - if( pCur->zP4 ){ - sqlcipher_sqlite3_free(pCur->zP4); - pCur->zP4 = 0; - } - if( pCur->zName ){ - pCur->zName = 0; - pCur->zType = 0; - pCur->zSchema = 0; - } - rc = sqlcipher_sqlite3VdbeNextOpcode( - (Vdbe*)pCur->pStmt, - pCur->showSubprograms ? &pCur->sub : 0, - pTab->bTablesUsed, - &pCur->iRowid, - &pCur->iAddr, - &pCur->aOp); - if( rc!=SQLITE_OK ){ - sqlcipher_sqlite3VdbeMemSetNull(&pCur->sub); - pCur->aOp = 0; - } - return SQLITE_OK; -} +#if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x30000000L) + unsigned int outlen; + HMAC_CTX* hctx = NULL; -/* -** Return TRUE if the cursor has been moved off of the last -** row of output. -*/ -static int bytecodevtabEof(sqlcipher_sqlite3_vtab_cursor *cur){ - bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; - return pCur->aOp==0; -} + if(in == NULL) goto error; -/* -** Return values of columns for the row at which the bytecodevtab_cursor -** is currently pointing. -*/ -static int bytecodevtabColumn( - sqlcipher_sqlite3_vtab_cursor *cur, /* The cursor */ - sqlcipher_sqlite3_context *ctx, /* First argument to sqlcipher_sqlite3_result_...() */ - int i /* Which column to return */ -){ - bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; - bytecodevtab *pVTab = (bytecodevtab*)cur->pVtab; - Op *pOp = pCur->aOp + pCur->iAddr; - if( pVTab->bTablesUsed ){ - if( i==4 ){ - i = 8; - }else{ - if( i<=2 && pCur->zType==0 ){ - Schema *pSchema; - HashElem *k; - int iDb = pOp->p3; - Pgno iRoot = (Pgno)pOp->p2; - sqlcipher_sqlite3 *db = pVTab->db; - pSchema = db->aDb[iDb].pSchema; - pCur->zSchema = db->aDb[iDb].zDbSName; - for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ - Table *pTab = (Table*)sqliteHashData(k); - if( !IsVirtual(pTab) && pTab->tnum==iRoot ){ - pCur->zName = pTab->zName; - pCur->zType = "table"; - break; - } - } - if( pCur->zName==0 ){ - for(k=sqliteHashFirst(&pSchema->idxHash); k; k=sqliteHashNext(k)){ - Index *pIdx = (Index*)sqliteHashData(k); - if( pIdx->tnum==iRoot ){ - pCur->zName = pIdx->zName; - pCur->zType = "index"; - } - } - } - } - i += 10; - } + hctx = HMAC_CTX_new(); + if(hctx == NULL) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: HMAC_CTX_new() failed"); + sqlcipher_openssl_log_errors(); + goto error; } - switch( i ){ - case 0: /* addr */ - sqlcipher_sqlite3_result_int(ctx, pCur->iAddr); - break; - case 1: /* opcode */ - sqlcipher_sqlite3_result_text(ctx, (char*)sqlcipher_sqlite3OpcodeName(pOp->opcode), - -1, SQLITE_STATIC); - break; - case 2: /* p1 */ - sqlcipher_sqlite3_result_int(ctx, pOp->p1); - break; - case 3: /* p2 */ - sqlcipher_sqlite3_result_int(ctx, pOp->p2); - break; - case 4: /* p3 */ - sqlcipher_sqlite3_result_int(ctx, pOp->p3); - break; - case 5: /* p4 */ - case 7: /* comment */ - if( pCur->zP4==0 ){ - pCur->zP4 = sqlcipher_sqlite3VdbeDisplayP4(pVTab->db, pOp); - } - if( i==5 ){ - sqlcipher_sqlite3_result_text(ctx, pCur->zP4, -1, SQLITE_STATIC); - }else{ -#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - char *zCom = sqlcipher_sqlite3VdbeDisplayComment(pVTab->db, pOp, pCur->zP4); - sqlcipher_sqlite3_result_text(ctx, zCom, -1, sqlcipher_sqlite3_free); -#endif + + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + if(!(rc = HMAC_Init_ex(hctx, hmac_key, key_sz, EVP_sha1(), NULL))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: HMAC_Init_ex() with key size %d and EVP_sha1() returned %d", key_sz, rc); + sqlcipher_openssl_log_errors(); + goto error; } break; - case 6: /* p5 */ - sqlcipher_sqlite3_result_int(ctx, pOp->p5); + case SQLCIPHER_HMAC_SHA256: + if(!(rc = HMAC_Init_ex(hctx, hmac_key, key_sz, EVP_sha256(), NULL))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: HMAC_Init_ex() with key size %d and EVP_sha256() returned %d", key_sz, rc); + sqlcipher_openssl_log_errors(); + goto error; + } break; - case 8: { /* subprog */ - Op *aOp = pCur->aOp; - assert( aOp[0].opcode==OP_Init ); - assert( aOp[0].p4.z==0 || strncmp(aOp[0].p4.z,"-" "- ",3)==0 ); - if( pCur->iRowid==pCur->iAddr+1 ){ - break; /* Result is NULL for the main program */ - }else if( aOp[0].p4.z!=0 ){ - sqlcipher_sqlite3_result_text(ctx, aOp[0].p4.z+3, -1, SQLITE_STATIC); - }else{ - sqlcipher_sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC); + case SQLCIPHER_HMAC_SHA512: + if(!(rc = HMAC_Init_ex(hctx, hmac_key, key_sz, EVP_sha512(), NULL))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: HMAC_Init_ex() with key size %d and EVP_sha512() returned %d", key_sz, rc); + sqlcipher_openssl_log_errors(); + goto error; } break; + default: + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: invalid algorithm %d", algorithm); + goto error; + } + + if(!(rc = HMAC_Update(hctx, in, in_sz))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: HMAC_Update() on 1st input buffer of %d bytes using algorithm %d returned %d", in_sz, algorithm, rc); + sqlcipher_openssl_log_errors(); + goto error; + } + + if(in2 != NULL) { + if(!(rc = HMAC_Update(hctx, in2, in2_sz))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: HMAC_Update() on 2nd input buffer of %d bytes using algorithm %d returned %d", in2_sz, algorithm, rc); + sqlcipher_openssl_log_errors(); + goto error; } - case 10: /* tables_used.type */ - sqlcipher_sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC); - break; - case 11: /* tables_used.schema */ - sqlcipher_sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC); + } + + if(!(rc = HMAC_Final(hctx, out, &outlen))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: HMAC_Final() using algorithm %d returned %d", algorithm, rc); + sqlcipher_openssl_log_errors(); + goto error; + } + + rc = SQLITE_OK; + goto cleanup; + +error: + rc = SQLITE_ERROR; + +cleanup: + if(hctx) HMAC_CTX_free(hctx); + +#else + size_t outlen; + EVP_MAC *mac = NULL; + EVP_MAC_CTX *hctx = NULL; + OSSL_PARAM sha1[] = { { "digest", OSSL_PARAM_UTF8_STRING, "sha1", 4, 0 }, OSSL_PARAM_END }; + OSSL_PARAM sha256[] = { { "digest", OSSL_PARAM_UTF8_STRING, "sha256", 6, 0 }, OSSL_PARAM_END }; + OSSL_PARAM sha512[] = { { "digest", OSSL_PARAM_UTF8_STRING, "sha512", 6, 0 }, OSSL_PARAM_END }; + + if(in == NULL) goto error; + + mac = EVP_MAC_fetch(NULL, "HMAC", NULL); + if(mac == NULL) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: EVP_MAC_fetch for HMAC failed"); + sqlcipher_openssl_log_errors(); + goto error; + } + + hctx = EVP_MAC_CTX_new(mac); + if(hctx == NULL) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: EVP_MAC_CTX_new() failed"); + sqlcipher_openssl_log_errors(); + goto error; + } + + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + if(!(rc = EVP_MAC_init(hctx, hmac_key, key_sz, sha1))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: EVP_MAC_init() with key size %d and sha1 returned %d", key_sz, rc); + sqlcipher_openssl_log_errors(); + goto error; + } break; - case 12: /* tables_used.name */ - sqlcipher_sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC); + case SQLCIPHER_HMAC_SHA256: + if(!(rc = EVP_MAC_init(hctx, hmac_key, key_sz, sha256))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: EVP_MAC_init() with key size %d and sha256 returned %d", key_sz, rc); + sqlcipher_openssl_log_errors(); + goto error; + } break; - case 13: /* tables_used.wr */ - sqlcipher_sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite); + case SQLCIPHER_HMAC_SHA512: + if(!(rc = EVP_MAC_init(hctx, hmac_key, key_sz, sha512))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: EVP_MAC_init() with key size %d and sha512 returned %d", key_sz, rc); + sqlcipher_openssl_log_errors(); + goto error; + } break; + default: + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: invalid algorithm %d", algorithm); + goto error; } - return SQLITE_OK; -} - -/* -** Return the rowid for the current row. In this implementation, the -** rowid is the same as the output value. -*/ -static int bytecodevtabRowid(sqlcipher_sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; - *pRowid = pCur->iRowid; - return SQLITE_OK; -} -/* -** Initialize a cursor. -** -** idxNum==0 means show all subprograms -** idxNum==1 means show only the main bytecode and omit subprograms. -*/ -static int bytecodevtabFilter( - sqlcipher_sqlite3_vtab_cursor *pVtabCursor, - int idxNum, const char *idxStr, - int argc, sqlcipher_sqlite3_value **argv -){ - bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor; - bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab; - int rc = SQLITE_OK; + if(!(rc = EVP_MAC_update(hctx, in, in_sz))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: EVP_MAC_update() on 1st input buffer of %d bytes using algorithm %d returned %d", in_sz, algorithm, rc); + sqlcipher_openssl_log_errors(); + goto error; + } - bytecodevtabCursorClear(pCur); - pCur->iRowid = 0; - pCur->iAddr = 0; - pCur->showSubprograms = idxNum==0; - assert( argc==1 ); - if( sqlcipher_sqlite3_value_type(argv[0])==SQLITE_TEXT ){ - const char *zSql = (const char*)sqlcipher_sqlite3_value_text(argv[0]); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlcipher_sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pStmt, 0); - pCur->needFinalize = 1; + if(in2 != NULL) { + if(!(rc = EVP_MAC_update(hctx, in2, in2_sz))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: EVP_MAC_update() on 2nd input buffer of %d bytes using algorithm %d returned %d", in_sz, algorithm, rc); + sqlcipher_openssl_log_errors(); + goto error; } - }else{ - pCur->pStmt = (sqlcipher_sqlite3_stmt*)sqlcipher_sqlite3_value_pointer(argv[0],"stmt-pointer"); } - if( pCur->pStmt==0 ){ - pVTab->base.zErrMsg = sqlcipher_sqlite3_mprintf( - "argument to %s() is not a valid SQL statement", - pVTab->bTablesUsed ? "tables_used" : "bytecode" - ); - rc = SQLITE_ERROR; - }else{ - bytecodevtabNext(pVtabCursor); + + if(!(rc = EVP_MAC_final(hctx, NULL, &outlen, 0))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: 1st EVP_MAC_final() for output length calculation using algorithm %d returned %d", algorithm, rc); + sqlcipher_openssl_log_errors(); + goto error; } - return rc; -} -/* -** We must have a single stmt=? constraint that will be passed through -** into the xFilter method. If there is no valid stmt=? constraint, -** then return an SQLITE_CONSTRAINT error. -*/ -static int bytecodevtabBestIndex( - sqlcipher_sqlite3_vtab *tab, - sqlcipher_sqlite3_index_info *pIdxInfo -){ - int i; - int rc = SQLITE_CONSTRAINT; - struct sqlcipher_sqlite3_index_constraint *p; - bytecodevtab *pVTab = (bytecodevtab*)tab; - int iBaseCol = pVTab->bTablesUsed ? 4 : 8; - pIdxInfo->estimatedCost = (double)100; - pIdxInfo->estimatedRows = 100; - pIdxInfo->idxNum = 0; - for(i=0, p=pIdxInfo->aConstraint; inConstraint; i++, p++){ - if( p->usable==0 ) continue; - if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==iBaseCol+1 ){ - rc = SQLITE_OK; - pIdxInfo->aConstraintUsage[i].omit = 1; - pIdxInfo->aConstraintUsage[i].argvIndex = 1; - } - if( p->op==SQLITE_INDEX_CONSTRAINT_ISNULL && p->iColumn==iBaseCol ){ - pIdxInfo->aConstraintUsage[i].omit = 1; - pIdxInfo->idxNum = 1; - } + if(!(rc = EVP_MAC_final(hctx, out, &outlen, outlen))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_hmac: 2nd EVP_MAC_final() using algorithm %d returned %d", algorithm, rc); + sqlcipher_openssl_log_errors(); + goto error; } + + rc = SQLITE_OK; + goto cleanup; + +error: + rc = SQLITE_ERROR; + +cleanup: + if(hctx) EVP_MAC_CTX_free(hctx); + if(mac) EVP_MAC_free(mac); + +#endif + return rc; } -/* -** This following structure defines all the methods for the -** virtual table. -*/ -static sqlcipher_sqlite3_module bytecodevtabModule = { - /* iVersion */ 0, - /* xCreate */ 0, - /* xConnect */ bytecodevtabConnect, - /* xBestIndex */ bytecodevtabBestIndex, - /* xDisconnect */ bytecodevtabDisconnect, - /* xDestroy */ 0, - /* xOpen */ bytecodevtabOpen, - /* xClose */ bytecodevtabClose, - /* xFilter */ bytecodevtabFilter, - /* xNext */ bytecodevtabNext, - /* xEof */ bytecodevtabEof, - /* xColumn */ bytecodevtabColumn, - /* xRowid */ bytecodevtabRowid, - /* xUpdate */ 0, - /* xBegin */ 0, - /* xSync */ 0, - /* xCommit */ 0, - /* xRollback */ 0, - /* xFindMethod */ 0, - /* xRename */ 0, - /* xSavepoint */ 0, - /* xRelease */ 0, - /* xRollbackTo */ 0, - /* xShadowName */ 0 -}; - +static int sqlcipher_openssl_kdf(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) { + int rc = 0; -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeBytecodeVtabInit(sqlcipher_sqlite3 *db){ - int rc; - rc = sqlcipher_sqlite3_create_module(db, "bytecode", &bytecodevtabModule, 0); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db); + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + if(!(rc = PKCS5_PBKDF2_HMAC((const char *)pass, pass_sz, salt, salt_sz, workfactor, EVP_sha1(), key_sz, key))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_kdf: PKCS5_PBKDF2_HMAC() for EVP_sha1() workfactor %d and key size %d returned %d", workfactor, key_sz, rc); + sqlcipher_openssl_log_errors(); + goto error; + } + break; + case SQLCIPHER_HMAC_SHA256: + if(!(rc = PKCS5_PBKDF2_HMAC((const char *)pass, pass_sz, salt, salt_sz, workfactor, EVP_sha256(), key_sz, key))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_kdf: PKCS5_PBKDF2_HMAC() for EVP_sha256() workfactor %d and key size %d returned %d", workfactor, key_sz, rc); + sqlcipher_openssl_log_errors(); + goto error; + } + break; + case SQLCIPHER_HMAC_SHA512: + if(!(rc = PKCS5_PBKDF2_HMAC((const char *)pass, pass_sz, salt, salt_sz, workfactor, EVP_sha512(), key_sz, key))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_kdf: PKCS5_PBKDF2_HMAC() for EVP_sha512() workfactor %d and key size %d returned %d", workfactor, key_sz, rc); + sqlcipher_openssl_log_errors(); + goto error; + } + break; + default: + return SQLITE_ERROR; } + + rc = SQLITE_OK; + goto cleanup; +error: + rc = SQLITE_ERROR; +cleanup: return rc; } -#elif defined(SQLITE_ENABLE_BYTECODE_VTAB) -SQLITE_PRIVATE int sqlcipher_sqlite3VdbeBytecodeVtabInit(sqlcipher_sqlite3 *db){ return SQLITE_OK; } -#endif /* SQLITE_ENABLE_BYTECODE_VTAB */ -/************** End of vdbevtab.c ********************************************/ -/************** Begin file memjournal.c **************************************/ -/* -** 2008 October 7 -** -** 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 use to implement an in-memory rollback journal. -** The in-memory rollback journal is used to journal transactions for -** ":memory:" databases and when the journal_mode=MEMORY pragma is used. -** -** Update: The in-memory journal is also used to temporarily cache -** smaller journals that are not critical for power-loss recovery. -** For example, statement journals that are not too big will be held -** entirely in memory, thus reducing the number of file I/O calls, and -** more importantly, reducing temporary file creation events. If these -** journals become too large for memory, they are spilled to disk. But -** in the common case, they are usually small and no file I/O needs to -** occur. -*/ -/* #include "sqliteInt.h" */ +static int sqlcipher_openssl_cipher(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out) { + int tmp_csz, csz, rc = 0; + EVP_CIPHER_CTX* ectx = EVP_CIPHER_CTX_new(); + if(ectx == NULL) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_cipher: EVP_CIPHER_CTX_new failed"); + sqlcipher_openssl_log_errors(); + goto error; + } -/* Forward references to internal structures */ -typedef struct MemJournal MemJournal; -typedef struct FilePoint FilePoint; -typedef struct FileChunk FileChunk; + if(!(rc = EVP_CipherInit_ex(ectx, OPENSSL_CIPHER, NULL, NULL, NULL, mode))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_cipher: EVP_CipherInit_ex for mode %d returned %d", mode, rc); + sqlcipher_openssl_log_errors(); + goto error; + } -/* -** The rollback journal is composed of a linked list of these structures. -** -** The zChunk array is always at least 8 bytes in size - usually much more. -** Its actual size is stored in the MemJournal.nChunkSize variable. -*/ -struct FileChunk { - FileChunk *pNext; /* Next chunk in the journal */ - u8 zChunk[8]; /* Content of this chunk */ -}; + if(!(rc = EVP_CIPHER_CTX_set_padding(ectx, 0))) { /* no padding */ + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_cipher: EVP_CIPHER_CTX_set_padding 0 returned %d", rc); + sqlcipher_openssl_log_errors(); + goto error; + } -/* -** By default, allocate this many bytes of memory for each FileChunk object. -*/ -#define MEMJOURNAL_DFLT_FILECHUNKSIZE 1024 + if(!(rc = EVP_CipherInit_ex(ectx, NULL, NULL, key, iv, mode))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_cipher: EVP_CipherInit_ex for mode %d returned %d", mode, rc); + sqlcipher_openssl_log_errors(); + goto error; + } -/* -** For chunk size nChunkSize, return the number of bytes that should -** be allocated for each FileChunk structure. -*/ -#define fileChunkSize(nChunkSize) (sizeof(FileChunk) + ((nChunkSize)-8)) + if(!(rc = EVP_CipherUpdate(ectx, out, &tmp_csz, in, in_sz))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_cipher: EVP_CipherUpdate returned %d", rc); + sqlcipher_openssl_log_errors(); + goto error; + } -/* -** An instance of this object serves as a cursor into the rollback journal. -** The cursor can be either for reading or writing. -*/ -struct FilePoint { - sqlcipher_sqlite3_int64 iOffset; /* Offset from the beginning of the file */ - FileChunk *pChunk; /* Specific chunk into which cursor points */ -}; + csz = tmp_csz; + out += tmp_csz; + if(!(rc = EVP_CipherFinal_ex(ectx, out, &tmp_csz))) { + sqlcipher_log(SQLCIPHER_LOG_ERROR, "sqlcipher_openssl_cipher: EVP_CipherFinal_ex returned %d", rc); + sqlcipher_openssl_log_errors(); + goto error; + } -/* -** This structure is a subclass of sqlcipher_sqlite3_file. Each open memory-journal -** is an instance of this class. -*/ -struct MemJournal { - const sqlcipher_sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */ - int nChunkSize; /* In-memory chunk-size */ + csz += tmp_csz; + assert(in_sz == csz); - int nSpill; /* Bytes of data before flushing */ - int nSize; /* Bytes of data currently in memory */ - FileChunk *pFirst; /* Head of in-memory chunk-list */ - FilePoint endpoint; /* Pointer to the end of the file */ - FilePoint readpoint; /* Pointer to the end of the last xRead() */ + rc = SQLITE_OK; + goto cleanup; +error: + rc = SQLITE_ERROR; +cleanup: + if(ectx) EVP_CIPHER_CTX_free(ectx); + return rc; +} - int flags; /* xOpen flags */ - sqlcipher_sqlite3_vfs *pVfs; /* The "real" underlying VFS */ - const char *zJournal; /* Name of the journal file */ -}; +static const char* sqlcipher_openssl_get_cipher(void *ctx) { + return OBJ_nid2sn(EVP_CIPHER_nid(OPENSSL_CIPHER)); +} -/* -** Read data from the in-memory journal file. This is the implementation -** of the sqlcipher_sqlite3_vfs.xRead method. -*/ -static int memjrnlRead( - sqlcipher_sqlite3_file *pJfd, /* The journal file from which to read */ - void *zBuf, /* Put the results here */ - int iAmt, /* Number of bytes to read */ - sqlite_int64 iOfst /* Begin reading at this offset */ -){ - MemJournal *p = (MemJournal *)pJfd; - u8 *zOut = zBuf; - int nRead = iAmt; - int iChunkOffset; - FileChunk *pChunk; +static int sqlcipher_openssl_get_key_sz(void *ctx) { + return EVP_CIPHER_key_length(OPENSSL_CIPHER); +} - if( (iAmt+iOfst)>p->endpoint.iOffset ){ - return SQLITE_IOERR_SHORT_READ; - } - assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 ); - if( p->readpoint.iOffset!=iOfst || iOfst==0 ){ - sqlcipher_sqlite3_int64 iOff = 0; - for(pChunk=p->pFirst; - ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst; - pChunk=pChunk->pNext - ){ - iOff += p->nChunkSize; - } - }else{ - pChunk = p->readpoint.pChunk; - assert( pChunk!=0 ); +static int sqlcipher_openssl_get_iv_sz(void *ctx) { + return EVP_CIPHER_iv_length(OPENSSL_CIPHER); +} + +static int sqlcipher_openssl_get_block_sz(void *ctx) { + return EVP_CIPHER_block_size(OPENSSL_CIPHER); +} + +static int sqlcipher_openssl_get_hmac_sz(void *ctx, int algorithm) { + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + return EVP_MD_size(EVP_sha1()); + break; + case SQLCIPHER_HMAC_SHA256: + return EVP_MD_size(EVP_sha256()); + break; + case SQLCIPHER_HMAC_SHA512: + return EVP_MD_size(EVP_sha512()); + break; + default: + return 0; } +} - iChunkOffset = (int)(iOfst%p->nChunkSize); - do { - int iSpace = p->nChunkSize - iChunkOffset; - int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset)); - memcpy(zOut, (u8*)pChunk->zChunk + iChunkOffset, nCopy); - zOut += nCopy; - nRead -= iSpace; - iChunkOffset = 0; - } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 ); - p->readpoint.iOffset = pChunk ? iOfst+iAmt : 0; - p->readpoint.pChunk = pChunk; +static int sqlcipher_openssl_ctx_init(void **ctx) { + return sqlcipher_openssl_activate(*ctx); +} - return SQLITE_OK; +static int sqlcipher_openssl_ctx_free(void **ctx) { + return sqlcipher_openssl_deactivate(NULL); } -/* -** Free the list of FileChunk structures headed at MemJournal.pFirst. -*/ -static void memjrnlFreeChunks(MemJournal *p){ - FileChunk *pIter; - FileChunk *pNext; - for(pIter=p->pFirst; pIter; pIter=pNext){ - pNext = pIter->pNext; - sqlcipher_sqlite3_free(pIter); - } - p->pFirst = 0; +static int sqlcipher_openssl_fips_status(void *ctx) { +#ifdef SQLCIPHER_FIPS + return FIPS_mode(); +#else + return 0; +#endif +} + +int sqlcipher_openssl_setup(sqlcipher_provider *p) { + p->activate = sqlcipher_openssl_activate; + p->deactivate = sqlcipher_openssl_deactivate; + p->get_provider_name = sqlcipher_openssl_get_provider_name; + p->random = sqlcipher_openssl_random; + p->hmac = sqlcipher_openssl_hmac; + p->kdf = sqlcipher_openssl_kdf; + p->cipher = sqlcipher_openssl_cipher; + p->get_cipher = sqlcipher_openssl_get_cipher; + p->get_key_sz = sqlcipher_openssl_get_key_sz; + p->get_iv_sz = sqlcipher_openssl_get_iv_sz; + p->get_block_sz = sqlcipher_openssl_get_block_sz; + p->get_hmac_sz = sqlcipher_openssl_get_hmac_sz; + p->ctx_init = sqlcipher_openssl_ctx_init; + p->ctx_free = sqlcipher_openssl_ctx_free; + p->add_random = sqlcipher_openssl_add_random; + p->fips_status = sqlcipher_openssl_fips_status; + p->get_provider_version = sqlcipher_openssl_get_provider_version; + return SQLITE_OK; } +#endif +#endif +/* END SQLCIPHER */ + +/************** End of crypto_openssl.c **************************************/ +/************** Begin file crypto_cc.c ***************************************/ /* -** Flush the contents of memory to a real file on disk. +** SQLCipher +** http://sqlcipher.net +** +** Copyright (c) 2008 - 2013, ZETETIC LLC +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the ZETETIC LLC nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** */ -static int memjrnlCreateFile(MemJournal *p){ - int rc; - sqlcipher_sqlite3_file *pReal = (sqlcipher_sqlite3_file*)p; - MemJournal copy = *p; +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC +#ifdef SQLCIPHER_CRYPTO_CC +/* #include "crypto.h" */ +/* #include "sqlcipher.h" */ +#include +#include +#include - memset(p, 0, sizeof(MemJournal)); - rc = sqlcipher_sqlite3OsOpen(copy.pVfs, copy.zJournal, pReal, copy.flags, 0); - if( rc==SQLITE_OK ){ - int nChunk = copy.nChunkSize; - i64 iOff = 0; - FileChunk *pIter; - for(pIter=copy.pFirst; pIter; pIter=pIter->pNext){ - if( iOff + nChunk > copy.endpoint.iOffset ){ - nChunk = copy.endpoint.iOffset - iOff; - } - rc = sqlcipher_sqlite3OsWrite(pReal, (u8*)pIter->zChunk, nChunk, iOff); - if( rc ) break; - iOff += nChunk; - } - if( rc==SQLITE_OK ){ - /* No error has occurred. Free the in-memory buffers. */ - memjrnlFreeChunks(©); - } - } - if( rc!=SQLITE_OK ){ - /* If an error occurred while creating or writing to the file, restore - ** the original before returning. This way, SQLite uses the in-memory - ** journal data to roll back changes made to the internal page-cache - ** before this function was called. */ - sqlcipher_sqlite3OsClose(pReal); - *p = copy; - } - return rc; +int sqlcipher_cc_setup(sqlcipher_provider *p); + +static int sqlcipher_cc_add_random(void *ctx, void *buffer, int length) { + return SQLITE_OK; } +/* generate a defined number of random bytes */ +static int sqlcipher_cc_random (void *ctx, void *buffer, int length) { + return (SecRandomCopyBytes(kSecRandomDefault, length, (uint8_t *)buffer) == kCCSuccess) ? SQLITE_OK : SQLITE_ERROR; +} -/* -** Write data to the file. -*/ -static int memjrnlWrite( - sqlcipher_sqlite3_file *pJfd, /* The journal file into which to write */ - const void *zBuf, /* Take data to be written from here */ - int iAmt, /* Number of bytes to write */ - sqlite_int64 iOfst /* Begin writing at this offset into the file */ -){ - MemJournal *p = (MemJournal *)pJfd; - int nWrite = iAmt; - u8 *zWrite = (u8 *)zBuf; +static const char* sqlcipher_cc_get_provider_name(void *ctx) { + return "commoncrypto"; +} - /* If the file should be created now, create it and write the new data - ** into the file on disk. */ - if( p->nSpill>0 && (iAmt+iOfst)>p->nSpill ){ - int rc = memjrnlCreateFile(p); - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3OsWrite(pJfd, zBuf, iAmt, iOfst); - } - return rc; +static const char* sqlcipher_cc_get_provider_version(void *ctx) { +#if TARGET_OS_MAC + CFTypeRef version; + CFBundleRef bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")); + if(bundle == NULL) { + return "unknown"; } - - /* If the contents of this write should be stored in memory */ - else{ - /* An in-memory journal file should only ever be appended to. Random - ** access writes are not required. The only exception to this is when - ** the in-memory journal is being used by a connection using the - ** atomic-write optimization. In this case the first 28 bytes of the - ** journal file may be written as part of committing the transaction. */ - assert( iOfst==p->endpoint.iOffset || iOfst==0 ); -#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ - || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) - if( iOfst==0 && p->pFirst ){ - assert( p->nChunkSize>iAmt ); - memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt); - }else + version = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString")); + return CFStringGetCStringPtr(version, kCFStringEncodingUTF8); #else - assert( iOfst>0 || p->pFirst==0 ); + return "unknown"; #endif - { - while( nWrite>0 ){ - FileChunk *pChunk = p->endpoint.pChunk; - int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize); - int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset); - - if( iChunkOffset==0 ){ - /* New chunk is required to extend the file. */ - FileChunk *pNew = sqlcipher_sqlite3_malloc(fileChunkSize(p->nChunkSize)); - if( !pNew ){ - return SQLITE_IOERR_NOMEM_BKPT; - } - pNew->pNext = 0; - if( pChunk ){ - assert( p->pFirst ); - pChunk->pNext = pNew; - }else{ - assert( !p->pFirst ); - p->pFirst = pNew; - } - p->endpoint.pChunk = pNew; - } +} - memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace); - zWrite += iSpace; - nWrite -= iSpace; - p->endpoint.iOffset += iSpace; - } - p->nSize = iAmt + iOfst; - } +static int sqlcipher_cc_hmac(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out) { + CCHmacContext hmac_context; + if(in == NULL) return SQLITE_ERROR; + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + CCHmacInit(&hmac_context, kCCHmacAlgSHA1, hmac_key, key_sz); + break; + case SQLCIPHER_HMAC_SHA256: + CCHmacInit(&hmac_context, kCCHmacAlgSHA256, hmac_key, key_sz); + break; + case SQLCIPHER_HMAC_SHA512: + CCHmacInit(&hmac_context, kCCHmacAlgSHA512, hmac_key, key_sz); + break; + default: + return SQLITE_ERROR; } - + CCHmacUpdate(&hmac_context, in, in_sz); + if(in2 != NULL) CCHmacUpdate(&hmac_context, in2, in2_sz); + CCHmacFinal(&hmac_context, out); return SQLITE_OK; } -/* -** Truncate the file. -** -** If the journal file is already on disk, truncate it there. Or, if it -** is still in main memory but is being truncated to zero bytes in size, -** ignore -*/ -static int memjrnlTruncate(sqlcipher_sqlite3_file *pJfd, sqlite_int64 size){ - MemJournal *p = (MemJournal *)pJfd; - if( ALWAYS(size==0) ){ - memjrnlFreeChunks(p); - p->nSize = 0; - p->endpoint.pChunk = 0; - p->endpoint.iOffset = 0; - p->readpoint.pChunk = 0; - p->readpoint.iOffset = 0; +static int sqlcipher_cc_kdf(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) { + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + if(CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pass, pass_sz, salt, salt_sz, kCCPRFHmacAlgSHA1, workfactor, key, key_sz) != kCCSuccess) return SQLITE_ERROR; + break; + case SQLCIPHER_HMAC_SHA256: + if(CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pass, pass_sz, salt, salt_sz, kCCPRFHmacAlgSHA256, workfactor, key, key_sz) != kCCSuccess) return SQLITE_ERROR; + break; + case SQLCIPHER_HMAC_SHA512: + if(CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pass, pass_sz, salt, salt_sz, kCCPRFHmacAlgSHA512, workfactor, key, key_sz) != kCCSuccess) return SQLITE_ERROR; + break; + default: + return SQLITE_ERROR; } return SQLITE_OK; } -/* -** Close the file. -*/ -static int memjrnlClose(sqlcipher_sqlite3_file *pJfd){ - MemJournal *p = (MemJournal *)pJfd; - memjrnlFreeChunks(p); - return SQLITE_OK; -} +static int sqlcipher_cc_cipher(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out) { + CCCryptorRef cryptor; + size_t tmp_csz, csz; + CCOperation op = mode == CIPHER_ENCRYPT ? kCCEncrypt : kCCDecrypt; + + if(CCCryptorCreate(op, kCCAlgorithmAES128, 0, key, kCCKeySizeAES256, iv, &cryptor) != kCCSuccess) return SQLITE_ERROR; + if(CCCryptorUpdate(cryptor, in, in_sz, out, in_sz, &tmp_csz) != kCCSuccess) return SQLITE_ERROR; + csz = tmp_csz; + out += tmp_csz; + if(CCCryptorFinal(cryptor, out, in_sz - csz, &tmp_csz) != kCCSuccess) return SQLITE_ERROR; + csz += tmp_csz; + if(CCCryptorRelease(cryptor) != kCCSuccess) return SQLITE_ERROR; + assert(in_sz == csz); -/* -** Sync the file. -** -** If the real file has been created, call its xSync method. Otherwise, -** syncing an in-memory journal is a no-op. -*/ -static int memjrnlSync(sqlcipher_sqlite3_file *pJfd, int flags){ - UNUSED_PARAMETER2(pJfd, flags); return SQLITE_OK; } -/* -** Query the size of the file in bytes. -*/ -static int memjrnlFileSize(sqlcipher_sqlite3_file *pJfd, sqlite_int64 *pSize){ - MemJournal *p = (MemJournal *)pJfd; - *pSize = (sqlite_int64) p->endpoint.iOffset; - return SQLITE_OK; +static const char* sqlcipher_cc_get_cipher(void *ctx) { + return "aes-256-cbc"; } -/* -** Table of methods for MemJournal sqlcipher_sqlite3_file object. -*/ -static const struct sqlcipher_sqlite3_io_methods MemJournalMethods = { - 1, /* iVersion */ - memjrnlClose, /* xClose */ - memjrnlRead, /* xRead */ - memjrnlWrite, /* xWrite */ - memjrnlTruncate, /* xTruncate */ - memjrnlSync, /* xSync */ - memjrnlFileSize, /* xFileSize */ - 0, /* xLock */ - 0, /* xUnlock */ - 0, /* xCheckReservedLock */ - 0, /* xFileControl */ - 0, /* xSectorSize */ - 0, /* xDeviceCharacteristics */ - 0, /* xShmMap */ - 0, /* xShmLock */ - 0, /* xShmBarrier */ - 0, /* xShmUnmap */ - 0, /* xFetch */ - 0 /* xUnfetch */ -}; +static int sqlcipher_cc_get_key_sz(void *ctx) { + return kCCKeySizeAES256; +} -/* -** Open a journal file. -** -** The behaviour of the journal file depends on the value of parameter -** nSpill. If nSpill is 0, then the journal file is always create and -** accessed using the underlying VFS. If nSpill is less than zero, then -** all content is always stored in main-memory. Finally, if nSpill is a -** positive value, then the journal file is initially created in-memory -** but may be flushed to disk later on. In this case the journal file is -** flushed to disk either when it grows larger than nSpill bytes in size, -** or when sqlcipher_sqlite3JournalCreate() is called. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3JournalOpen( - sqlcipher_sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */ - const char *zName, /* Name of the journal file */ - sqlcipher_sqlite3_file *pJfd, /* Preallocated, blank file handle */ - int flags, /* Opening flags */ - int nSpill /* Bytes buffered before opening the file */ -){ - MemJournal *p = (MemJournal*)pJfd; +static int sqlcipher_cc_get_iv_sz(void *ctx) { + return kCCBlockSizeAES128; +} - /* Zero the file-handle object. If nSpill was passed zero, initialize - ** it using the sqlcipher_sqlite3OsOpen() function of the underlying VFS. In this - ** case none of the code in this module is executed as a result of calls - ** made on the journal file-handle. */ - memset(p, 0, sizeof(MemJournal)); - if( nSpill==0 ){ - return sqlcipher_sqlite3OsOpen(pVfs, zName, pJfd, flags, 0); - } +static int sqlcipher_cc_get_block_sz(void *ctx) { + return kCCBlockSizeAES128; +} - if( nSpill>0 ){ - p->nChunkSize = nSpill; - }else{ - p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk); - assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) ); +static int sqlcipher_cc_get_hmac_sz(void *ctx, int algorithm) { + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + return CC_SHA1_DIGEST_LENGTH; + break; + case SQLCIPHER_HMAC_SHA256: + return CC_SHA256_DIGEST_LENGTH; + break; + case SQLCIPHER_HMAC_SHA512: + return CC_SHA512_DIGEST_LENGTH; + break; + default: + return 0; } +} - pJfd->pMethods = (const sqlcipher_sqlite3_io_methods*)&MemJournalMethods; - p->nSpill = nSpill; - p->flags = flags; - p->zJournal = zName; - p->pVfs = pVfs; +static int sqlcipher_cc_ctx_init(void **ctx) { return SQLITE_OK; } -/* -** Open an in-memory journal file. -*/ -SQLITE_PRIVATE void sqlcipher_sqlite3MemJournalOpen(sqlcipher_sqlite3_file *pJfd){ - sqlcipher_sqlite3JournalOpen(0, 0, pJfd, 0, -1); +static int sqlcipher_cc_ctx_free(void **ctx) { + return SQLITE_OK; } -#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ - || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) -/* -** If the argument p points to a MemJournal structure that is not an -** in-memory-only journal file (i.e. is one that was opened with a +ve -** nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying -** file has not yet been created, create it now. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3JournalCreate(sqlcipher_sqlite3_file *pJfd){ - int rc = SQLITE_OK; - MemJournal *p = (MemJournal*)pJfd; - if( pJfd->pMethods==&MemJournalMethods && ( -#ifdef SQLITE_ENABLE_ATOMIC_WRITE - p->nSpill>0 -#else - /* While this appears to not be possible without ATOMIC_WRITE, the - ** paths are complex, so it seems prudent to leave the test in as - ** a NEVER(), in case our analysis is subtly flawed. */ - NEVER(p->nSpill>0) -#endif -#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE - || (p->flags & SQLITE_OPEN_MAIN_JOURNAL) -#endif - )){ - rc = memjrnlCreateFile(p); - } - return rc; +static int sqlcipher_cc_fips_status(void *ctx) { + return 0; } -#endif -/* -** The file-handle passed as the only argument is open on a journal file. -** Return true if this "journal file" is currently stored in heap memory, -** or false otherwise. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3JournalIsInMemory(sqlcipher_sqlite3_file *p){ - return p->pMethods==&MemJournalMethods; +int sqlcipher_cc_setup(sqlcipher_provider *p) { + p->random = sqlcipher_cc_random; + p->get_provider_name = sqlcipher_cc_get_provider_name; + p->hmac = sqlcipher_cc_hmac; + p->kdf = sqlcipher_cc_kdf; + p->cipher = sqlcipher_cc_cipher; + p->get_cipher = sqlcipher_cc_get_cipher; + p->get_key_sz = sqlcipher_cc_get_key_sz; + p->get_iv_sz = sqlcipher_cc_get_iv_sz; + p->get_block_sz = sqlcipher_cc_get_block_sz; + p->get_hmac_sz = sqlcipher_cc_get_hmac_sz; + p->ctx_init = sqlcipher_cc_ctx_init; + p->ctx_free = sqlcipher_cc_ctx_free; + p->add_random = sqlcipher_cc_add_random; + p->fips_status = sqlcipher_cc_fips_status; + p->get_provider_version = sqlcipher_cc_get_provider_version; + return SQLITE_OK; } -/* -** Return the number of bytes required to store a JournalFile that uses vfs -** pVfs to create the underlying on-disk files. -*/ -SQLITE_PRIVATE int sqlcipher_sqlite3JournalSize(sqlcipher_sqlite3_vfs *pVfs){ - return MAX(pVfs->szOsFile, (int)sizeof(MemJournal)); -} +#endif +#endif +/* END SQLCIPHER */ -/************** End of memjournal.c ******************************************/ +/************** End of crypto_cc.c *******************************************/ /************** Begin file walker.c ******************************************/ /* ** 2008 August 16 @@ -102691,7 +106475,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3JournalSize(sqlcipher_sqlite3_vfs *pVfs){ ** Walk all expressions linked into the list of Window objects passed ** as the second argument. */ -static int walkWindowList(Walker *pWalker, Window *pList){ +static int walkWindowList(Walker *pWalker, Window *pList, int bOneOnly){ Window *pWin; for(pWin=pList; pWin; pWin=pWin->pNextWin){ int rc; @@ -102701,15 +106485,11 @@ static int walkWindowList(Walker *pWalker, Window *pList){ if( rc ) return WRC_Abort; rc = sqlcipher_sqlite3WalkExpr(pWalker, pWin->pFilter); if( rc ) return WRC_Abort; - - /* The next two are purely for calls to sqlcipher_sqlite3RenameExprUnmap() - ** within sqlcipher_sqlite3WindowOffsetExpr(). Because of constraints imposed - ** by sqlcipher_sqlite3WindowOffsetExpr(), they can never fail. The results do - ** not matter anyhow. */ rc = sqlcipher_sqlite3WalkExpr(pWalker, pWin->pStart); - if( NEVER(rc) ) return WRC_Abort; + if( rc ) return WRC_Abort; rc = sqlcipher_sqlite3WalkExpr(pWalker, pWin->pEnd); - if( NEVER(rc) ) return WRC_Abort; + if( rc ) return WRC_Abort; + if( bOneOnly ) break; } return WRC_Continue; } @@ -102748,7 +106528,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ assert( !ExprHasProperty(pExpr, EP_WinFunc) ); pExpr = pExpr->pRight; continue; - }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + }else if( ExprUseXSelect(pExpr) ){ assert( !ExprHasProperty(pExpr, EP_WinFunc) ); if( sqlcipher_sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort; }else{ @@ -102757,7 +106537,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ } #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(pExpr, EP_WinFunc) ){ - if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort; + if( walkWindowList(pWalker, pExpr->y.pWin, 1) ) return WRC_Abort; } #endif } @@ -102785,6 +106565,16 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WalkExprList(Walker *pWalker, ExprList *p){ return WRC_Continue; } +/* +** This is a no-op callback for Walker->xSelectCallback2. If this +** callback is set, then the Select->pWinDefn list is traversed. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3WalkWinDefnDummyCallback(Walker *pWalker, Select *p){ + UNUSED_PARAMETER(pWalker); + UNUSED_PARAMETER(p); + /* No-op */ +} + /* ** Walk all expressions associated with SELECT statement p. Do ** not invoke the SELECT callback on p, but do (of course) invoke @@ -102798,13 +106588,18 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WalkSelectExpr(Walker *pWalker, Select *p){ if( sqlcipher_sqlite3WalkExpr(pWalker, p->pHaving) ) return WRC_Abort; if( sqlcipher_sqlite3WalkExprList(pWalker, p->pOrderBy) ) return WRC_Abort; if( sqlcipher_sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort; -#if !defined(SQLITE_OMIT_WINDOWFUNC) && !defined(SQLITE_OMIT_ALTERTABLE) - { - Parse *pParse = pWalker->pParse; - if( pParse && IN_RENAME_OBJECT ){ +#if !defined(SQLITE_OMIT_WINDOWFUNC) + if( p->pWinDefn ){ + Parse *pParse; + if( pWalker->xSelectCallback2==sqlcipher_sqlite3WalkWinDefnDummyCallback + || ((pParse = pWalker->pParse)!=0 && IN_RENAME_OBJECT) +#ifndef SQLITE_OMIT_CTE + || pWalker->xSelectCallback2==sqlcipher_sqlite3SelectPopWith +#endif + ){ /* The following may return WRC_Abort if there are unresolvable ** symbols (e.g. a table that does not exist) in a window definition. */ - int rc = walkWindowList(pWalker, p->pWinDefn); + int rc = walkWindowList(pWalker, p->pWinDefn, 0); return rc; } } @@ -102822,10 +106617,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WalkSelectExpr(Walker *pWalker, Select *p){ SQLITE_PRIVATE int sqlcipher_sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ SrcList *pSrc; int i; - struct SrcList_item *pItem; + SrcItem *pItem; pSrc = p->pSrc; - if( pSrc ){ + if( ALWAYS(pSrc) ){ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ if( pItem->pSelect && sqlcipher_sqlite3WalkSelect(pWalker, pItem->pSelect) ){ return WRC_Abort; @@ -102988,7 +106783,6 @@ static void resolveAlias( ExprList *pEList, /* A result set */ int iCol, /* A column in the result set. 0..pEList->nExpr-1 */ Expr *pExpr, /* Transform this into an alias to the result set */ - const char *zType, /* "GROUP" or "ORDER" or "" */ int nSubquery /* Number of subqueries that the label is moving */ ){ Expr *pOrig; /* The iCol-th column of the result set */ @@ -103000,54 +106794,28 @@ static void resolveAlias( assert( pOrig!=0 ); db = pParse->db; pDup = sqlcipher_sqlite3ExprDup(db, pOrig, 0); - if( pDup!=0 ){ - if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery); + if( db->mallocFailed ){ + sqlcipher_sqlite3ExprDelete(db, pDup); + pDup = 0; + }else{ + Expr temp; + incrAggFunctionDepth(pDup, nSubquery); if( pExpr->op==TK_COLLATE ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); pDup = sqlcipher_sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken); } - - /* Before calling sqlcipher_sqlite3ExprDelete(), set the EP_Static flag. This - ** prevents ExprDelete() from deleting the Expr structure itself, - ** allowing it to be repopulated by the memcpy() on the following line. - ** The pExpr->u.zToken might point into memory that will be freed by the - ** sqlcipher_sqlite3DbFree(db, pDup) on the last line of this block, so be sure to - ** make a copy of the token before doing the sqlcipher_sqlite3DbFree(). - */ - ExprSetProperty(pExpr, EP_Static); - sqlcipher_sqlite3ExprDelete(db, pExpr); - memcpy(pExpr, pDup, sizeof(*pExpr)); - if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){ - assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 ); - pExpr->u.zToken = sqlcipher_sqlite3DbStrDup(db, pExpr->u.zToken); - pExpr->flags |= EP_MemToken; - } + memcpy(&temp, pDup, sizeof(Expr)); + memcpy(pDup, pExpr, sizeof(Expr)); + memcpy(pExpr, &temp, sizeof(Expr)); if( ExprHasProperty(pExpr, EP_WinFunc) ){ - if( pExpr->y.pWin!=0 ){ + if( ALWAYS(pExpr->y.pWin!=0) ){ pExpr->y.pWin->pOwner = pExpr; - }else{ - assert( db->mallocFailed ); } } - sqlcipher_sqlite3DbFree(db, pDup); - } - ExprSetProperty(pExpr, EP_Alias); -} - - -/* -** Return TRUE if the name zCol occurs anywhere in the USING clause. -** -** Return FALSE if the USING clause is NULL or if it does not contain -** zCol. -*/ -static int nameInUsingClause(IdList *pUsing, const char *zCol){ - if( pUsing ){ - int k; - for(k=0; knId; k++){ - if( sqlcipher_sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1; - } + sqlcipher_sqlite3ParserAddCleanup(pParse, + (void(*)(sqlcipher_sqlite3*,void*))sqlcipher_sqlite3ExprDelete, + pDup); } - return 0; } /* @@ -103065,7 +106833,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3MatchEName( ){ int n; const char *zSpan; - if( pItem->eEName!=ENAME_TAB ) return 0; + if( pItem->fg.eEName!=ENAME_TAB ) return 0; zSpan = pItem->zEName; for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} if( zDb && (sqlcipher_sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){ @@ -103109,6 +106877,7 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3ExprColUsed(Expr *pExpr){ Table *pExTab; n = pExpr->iColumn; + assert( ExprUseYTab(pExpr) ); pExTab = pExpr->y.pTab; assert( pExTab!=0 ); if( (pExTab->tabFlags & TF_HasGenerated)!=0 @@ -103125,6 +106894,29 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3ExprColUsed(Expr *pExpr){ } } +/* +** Create a new expression term for the column specified by pMatch and +** iColumn. Append this new expression term to the FULL JOIN Match set +** in *ppList. Create a new *ppList if this is the first term in the +** set. +*/ +static void extendFJMatch( + Parse *pParse, /* Parsing context */ + ExprList **ppList, /* ExprList to extend */ + SrcItem *pMatch, /* Source table containing the column */ + i16 iColumn /* The column number */ +){ + Expr *pNew = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0); + if( pNew ){ + pNew->iTable = pMatch->iCursor; + pNew->iColumn = iColumn; + pNew->y.pTab = pMatch->pTab; + assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ); + ExprSetProperty(pNew, EP_CanBeNull); + *ppList = sqlcipher_sqlite3ExprListAppend(pParse, *ppList, pNew); + } +} + /* ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up ** that name in the set of source tables in pSrcList and make the pExpr @@ -103165,16 +106957,18 @@ static int lookupName( int cntTab = 0; /* Number of matching table names */ int nSubquery = 0; /* How many levels of subquery */ sqlcipher_sqlite3 *db = pParse->db; /* The database connection */ - struct SrcList_item *pItem; /* Use for looping over pSrcList items */ - struct SrcList_item *pMatch = 0; /* The matching pSrcList item */ + SrcItem *pItem; /* Use for looping over pSrcList items */ + SrcItem *pMatch = 0; /* The matching pSrcList item */ NameContext *pTopNC = pNC; /* First namecontext in the list */ Schema *pSchema = 0; /* Schema of the expression */ int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */ - Table *pTab = 0; /* Table hold the row */ + Table *pTab = 0; /* Table holding the row */ Column *pCol; /* A column of pTab */ + ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */ assert( pNC ); /* the name context cannot be NULL. */ assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ + assert( zDb==0 || zTab!=0 ); assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); /* Initialize the node to no-match */ @@ -103222,62 +107016,124 @@ static int lookupName( u8 hCol; pTab = pItem->pTab; assert( pTab!=0 && pTab->zName!=0 ); - assert( pTab->nCol>0 ); - if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ + assert( pTab->nCol>0 || pParse->nErr ); + assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); + if( pItem->fg.isNestedFrom ){ + /* In this case, pItem is a subquery that has been formed from a + ** parenthesized subset of the FROM clause terms. Example: + ** .... FROM t1 LEFT JOIN (t2 RIGHT JOIN t3 USING(x)) USING(y) ... + ** \_________________________/ + ** This pItem -------------^ + */ int hit = 0; + assert( pItem->pSelect!=0 ); pEList = pItem->pSelect->pEList; + assert( pEList!=0 ); + assert( pEList->nExpr==pTab->nCol ); for(j=0; jnExpr; j++){ - if( sqlcipher_sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){ - cnt++; - cntTab = 2; - pMatch = pItem; - pExpr->iColumn = j; - hit = 1; + if( !sqlcipher_sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){ + continue; + } + if( cnt>0 ){ + if( pItem->fg.isUsing==0 + || sqlcipher_sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 + ){ + /* Two or more tables have the same column name which is + ** not joined by USING. This is an error. Signal as much + ** by clearing pFJMatch and letting cnt go above 1. */ + sqlcipher_sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else + if( (pItem->fg.jointype & JT_RIGHT)==0 ){ + /* An INNER or LEFT JOIN. Use the left-most table */ + continue; + }else + if( (pItem->fg.jointype & JT_LEFT)==0 ){ + /* A RIGHT JOIN. Use the right-most table */ + cnt = 0; + sqlcipher_sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else{ + /* For a FULL JOIN, we must construct a coalesce() func */ + extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); + } } + cnt++; + cntTab = 2; + pMatch = pItem; + pExpr->iColumn = j; + pEList->a[j].fg.bUsed = 1; + hit = 1; + if( pEList->a[j].fg.bUsingTerm ) break; } if( hit || zTab==0 ) continue; } - if( zDb && pTab->pSchema!=pSchema ){ - continue; - } + assert( zDb==0 || zTab!=0 ); if( zTab ){ - const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; + const char *zTabName; + if( zDb ){ + if( pTab->pSchema!=pSchema ) continue; + if( pSchema==0 && strcmp(zDb,"*")!=0 ) continue; + } + zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; assert( zTabName!=0 ); if( sqlcipher_sqlite3StrICmp(zTabName, zTab)!=0 ){ continue; } + assert( ExprUseYTab(pExpr) ); if( IN_RENAME_OBJECT && pItem->zAlias ){ sqlcipher_sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab); } } - if( 0==(cntTab++) ){ - pMatch = pItem; - } hCol = sqlcipher_sqlite3StrIHash(zCol); for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ - if( pCol->hName==hCol && sqlcipher_sqlite3StrICmp(pCol->zName, zCol)==0 ){ - /* If there has been exactly one prior match and this match - ** is for the right-hand table of a NATURAL JOIN or is in a - ** USING clause, then skip this match. - */ - if( cnt==1 ){ - if( pItem->fg.jointype & JT_NATURAL ) continue; - if( nameInUsingClause(pItem->pUsing, zCol) ) continue; + if( pCol->hName==hCol + && sqlcipher_sqlite3StrICmp(pCol->zCnName, zCol)==0 + ){ + if( cnt>0 ){ + if( pItem->fg.isUsing==0 + || sqlcipher_sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 + ){ + /* Two or more tables have the same column name which is + ** not joined by USING. This is an error. Signal as much + ** by clearing pFJMatch and letting cnt go above 1. */ + sqlcipher_sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else + if( (pItem->fg.jointype & JT_RIGHT)==0 ){ + /* An INNER or LEFT JOIN. Use the left-most table */ + continue; + }else + if( (pItem->fg.jointype & JT_LEFT)==0 ){ + /* A RIGHT JOIN. Use the right-most table */ + cnt = 0; + sqlcipher_sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else{ + /* For a FULL JOIN, we must construct a coalesce() func */ + extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); + } } cnt++; pMatch = pItem; /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; + if( pItem->fg.isNestedFrom ){ + sqlcipher_sqlite3SrcItemColumnUsed(pItem, j); + } break; } } + if( 0==cnt && VisibleRowid(pTab) ){ + cntTab++; + pMatch = pItem; + } } if( pMatch ){ pExpr->iTable = pMatch->iCursor; + assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pMatch->pTab; - /* RIGHT JOIN not (yet) supported */ - assert( (pMatch->fg.jointype & JT_RIGHT)==0 ); - if( (pMatch->fg.jointype & JT_LEFT)!=0 ){ + if( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ){ ExprSetProperty(pExpr, EP_CanBeNull); } pSchema = pExpr->y.pTab->pSchema; @@ -103287,25 +107143,33 @@ static int lookupName( #if !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT) /* If we have not already resolved the name, then maybe ** it is a new.* or old.* trigger argument reference. Or - ** maybe it is an excluded.* from an upsert. + ** maybe it is an excluded.* from an upsert. Or maybe it is + ** a reference in the RETURNING clause to a table being modified. */ - if( zDb==0 && zTab!=0 && cntTab==0 ){ + if( cnt==0 && zDb==0 ){ pTab = 0; #ifndef SQLITE_OMIT_TRIGGER if( pParse->pTriggerTab!=0 ){ int op = pParse->eTriggerOp; assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); - if( op!=TK_DELETE && sqlcipher_sqlite3StrICmp("new",zTab) == 0 ){ + if( pParse->bReturning ){ + if( (pNC->ncFlags & NC_UBaseReg)!=0 + && (zTab==0 || sqlcipher_sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0) + ){ + pExpr->iTable = op!=TK_DELETE; + pTab = pParse->pTriggerTab; + } + }else if( op!=TK_DELETE && zTab && sqlcipher_sqlite3StrICmp("new",zTab) == 0 ){ pExpr->iTable = 1; pTab = pParse->pTriggerTab; - }else if( op!=TK_INSERT && sqlcipher_sqlite3StrICmp("old",zTab)==0 ){ + }else if( op!=TK_INSERT && zTab && sqlcipher_sqlite3StrICmp("old",zTab)==0 ){ pExpr->iTable = 0; pTab = pParse->pTriggerTab; } } #endif /* SQLITE_OMIT_TRIGGER */ #ifndef SQLITE_OMIT_UPSERT - if( (pNC->ncFlags & NC_UUpsert)!=0 ){ + if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab!=0 ){ Upsert *pUpsert = pNC->uNC.pUpsert; if( pUpsert && sqlcipher_sqlite3StrICmp("excluded",zTab)==0 ){ pTab = pUpsert->pUpsertSrc->a[0].pTab; @@ -103320,7 +107184,9 @@ static int lookupName( pSchema = pTab->pSchema; cntTab++; for(iCol=0, pCol=pTab->aCol; iColnCol; iCol++, pCol++){ - if( pCol->hName==hCol && sqlcipher_sqlite3StrICmp(pCol->zName, zCol)==0 ){ + if( pCol->hName==hCol + && sqlcipher_sqlite3StrICmp(pCol->zCnName, zCol)==0 + ){ if( iCol==pTab->iPKey ){ iCol = -1; } @@ -103333,9 +107199,11 @@ static int lookupName( } if( iColnCol ){ cnt++; + pMatch = 0; #ifndef SQLITE_OMIT_UPSERT if( pExpr->iTable==EXCLUDED_TABLE_NUMBER ){ testcase( iCol==(-1) ); + assert( ExprUseYTab(pExpr) ); if( IN_RENAME_OBJECT ){ pExpr->iColumn = iCol; pExpr->y.pTab = pTab; @@ -103344,27 +107212,34 @@ static int lookupName( pExpr->iTable = pNC->uNC.pUpsert->regData + sqlcipher_sqlite3TableColumnToStorage(pTab, iCol); eNewExprOp = TK_REGISTER; - ExprSetProperty(pExpr, EP_Alias); } }else #endif /* SQLITE_OMIT_UPSERT */ { -#ifndef SQLITE_OMIT_TRIGGER - if( iCol<0 ){ - pExpr->affExpr = SQLITE_AFF_INTEGER; - }else if( pExpr->iTable==0 ){ - testcase( iCol==31 ); - testcase( iCol==32 ); - pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<y.pTab = pTab; - pExpr->iColumn = (i16)iCol; - eNewExprOp = TK_TRIGGER; + if( pParse->bReturning ){ + eNewExprOp = TK_REGISTER; + pExpr->op2 = TK_COLUMN; + pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable + + sqlcipher_sqlite3TableColumnToStorage(pTab, iCol) + 1; + }else{ + pExpr->iColumn = (i16)iCol; + eNewExprOp = TK_TRIGGER; +#ifndef SQLITE_OMIT_TRIGGER + if( iCol<0 ){ + pExpr->affExpr = SQLITE_AFF_INTEGER; + }else if( pExpr->iTable==0 ){ + testcase( iCol==31 ); + testcase( iCol==32 ); + pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<ncFlags & (NC_IdxExpr|NC_GenCol))==0 && sqlcipher_sqlite3IsRowid(zCol) - && VisibleRowid(pMatch->pTab) + && ALWAYS(VisibleRowid(pMatch->pTab)) ){ cnt = 1; pExpr->iColumn = -1; @@ -103404,21 +107279,21 @@ static int lookupName( ** is supported for backwards compatibility only. Hence, we issue a warning ** on sqlcipher_sqlite3_log() whenever the capability is used. */ - if( (pNC->ncFlags & NC_UEList)!=0 - && cnt==0 + if( cnt==0 + && (pNC->ncFlags & NC_UEList)!=0 && zTab==0 ){ pEList = pNC->uNC.pEList; assert( pEList!=0 ); for(j=0; jnExpr; j++){ char *zAs = pEList->a[j].zEName; - if( pEList->a[j].eEName==ENAME_NAME + if( pEList->a[j].fg.eEName==ENAME_NAME && sqlcipher_sqlite3_stricmp(zAs, zCol)==0 ){ Expr *pOrig; assert( pExpr->pLeft==0 && pExpr->pRight==0 ); - assert( pExpr->x.pList==0 ); - assert( pExpr->x.pSelect==0 ); + assert( ExprUseXList(pExpr)==0 || pExpr->x.pList==0 ); + assert( ExprUseXSelect(pExpr)==0 || pExpr->x.pSelect==0 ); pOrig = pEList->a[j].pExpr; if( (pNC->ncFlags&NC_AllowAgg)==0 && ExprHasProperty(pOrig, EP_Agg) ){ sqlcipher_sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); @@ -103434,7 +107309,7 @@ static int lookupName( sqlcipher_sqlite3ErrorMsg(pParse, "row value misused"); return WRC_Abort; } - resolveAlias(pParse, pEList, j, pExpr, "", nSubquery); + resolveAlias(pParse, pEList, j, pExpr, nSubquery); cnt = 1; pMatch = 0; assert( zTab==0 && zDb==0 ); @@ -103490,7 +107365,7 @@ static int lookupName( sqlcipher_sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol); #endif pExpr->op = TK_STRING; - pExpr->y.pTab = 0; + memset(&pExpr->y, 0, sizeof(pExpr->y)); return WRC_Prune; } if( sqlcipher_sqlite3ExprIdToTrueFalse(pExpr) ){ @@ -103499,11 +107374,37 @@ static int lookupName( } /* - ** cnt==0 means there was not match. cnt>1 means there were two or - ** more matches. Either way, we have an error. + ** cnt==0 means there was not match. + ** cnt>1 means there were two or more matches. + ** + ** cnt==0 is always an error. cnt>1 is often an error, but might + ** be multiple matches for a NATURAL LEFT JOIN or a LEFT JOIN USING. */ + assert( pFJMatch==0 || cnt>0 ); + assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); if( cnt!=1 ){ const char *zErr; + if( pFJMatch ){ + if( pFJMatch->nExpr==cnt-1 ){ + if( ExprHasProperty(pExpr,EP_Leaf) ){ + ExprClearProperty(pExpr,EP_Leaf); + }else{ + sqlcipher_sqlite3ExprDelete(db, pExpr->pLeft); + pExpr->pLeft = 0; + sqlcipher_sqlite3ExprDelete(db, pExpr->pRight); + pExpr->pRight = 0; + } + extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); + pExpr->op = TK_FUNCTION; + pExpr->u.zToken = "coalesce"; + pExpr->x.pList = pFJMatch; + cnt = 1; + goto lookupname_end; + }else{ + sqlcipher_sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + } + } zErr = cnt==0 ? "no such column" : "ambiguous column name"; if( zDb ){ sqlcipher_sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); @@ -103512,8 +107413,19 @@ static int lookupName( }else{ sqlcipher_sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); } + sqlcipher_sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); pParse->checkSchema = 1; - pTopNC->nErr++; + pTopNC->nNcErr++; + } + assert( pFJMatch==0 ); + + /* Remove all substructure from pExpr */ + if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ + sqlcipher_sqlite3ExprDelete(db, pExpr->pLeft); + pExpr->pLeft = 0; + sqlcipher_sqlite3ExprDelete(db, pExpr->pRight); + pExpr->pRight = 0; + ExprSetProperty(pExpr, EP_Leaf); } /* If a column from a table in pSrcList is referenced, then record @@ -103534,20 +107446,17 @@ static int lookupName( pMatch->colUsed |= sqlcipher_sqlite3ExprColUsed(pExpr); } - /* Clean up and return - */ - sqlcipher_sqlite3ExprDelete(db, pExpr->pLeft); - pExpr->pLeft = 0; - sqlcipher_sqlite3ExprDelete(db, pExpr->pRight); - pExpr->pRight = 0; pExpr->op = eNewExprOp; - ExprSetProperty(pExpr, EP_Leaf); lookupname_end: if( cnt==1 ){ assert( pNC!=0 ); - if( !ExprHasProperty(pExpr, EP_Alias) ){ +#ifndef SQLITE_OMIT_AUTHORIZATION + if( pParse->db->xAuth + && (pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER) + ){ sqlcipher_sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList); } +#endif /* Increment the nRef value on all name contexts from TopNC up to ** the point where the name matched. */ for(;;){ @@ -103569,8 +107478,10 @@ lookupname_end: SQLITE_PRIVATE Expr *sqlcipher_sqlite3CreateColumnExpr(sqlcipher_sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){ Expr *p = sqlcipher_sqlite3ExprAlloc(db, TK_COLUMN, 0, 0); if( p ){ - struct SrcList_item *pItem = &pSrc->a[iSrc]; - Table *pTab = p->y.pTab = pItem->pTab; + SrcItem *pItem = &pSrc->a[iSrc]; + Table *pTab; + assert( ExprUseYTab(p) ); + pTab = p->y.pTab = pItem->pTab; p->iTable = pItem->iCursor; if( p->y.pTab->iPKey==iCol ){ p->iColumn = -1; @@ -103612,7 +107523,8 @@ static void notValidImpl( Parse *pParse, /* Leave error message here */ NameContext *pNC, /* The name context */ const char *zMsg, /* Type of error */ - Expr *pExpr /* Invalidate this expression on error */ + Expr *pExpr, /* Invalidate this expression on error */ + Expr *pError /* Associate error with this expression */ ){ const char *zIn = "partial index WHERE clauses"; if( pNC->ncFlags & NC_IdxExpr ) zIn = "index expressions"; @@ -103624,10 +107536,11 @@ static void notValidImpl( #endif sqlcipher_sqlite3ErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn); if( pExpr ) pExpr->op = TK_NULL; + sqlcipher_sqlite3RecordErrorOffsetOfExpr(pParse->db, pError); } -#define sqlcipher_sqlite3ResolveNotValid(P,N,M,X,E) \ +#define sqlcipher_sqlite3ResolveNotValid(P,N,M,X,E,R) \ assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \ - if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E); + if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R); /* ** Expression p should encode a floating point value between 1.0 and 0.0. @@ -103637,6 +107550,7 @@ static void notValidImpl( static int exprProbability(Expr *p){ double r = -1.0; if( p->op!=TK_FLOAT ) return -1; + assert( !ExprHasProperty(p, EP_IntValue) ); sqlcipher_sqlite3AtoF(p->u.zToken, &r, sqlcipher_sqlite3Strlen30(p->u.zToken), SQLITE_UTF8); assert( r>=0.0 ); if( r>1.0 ) return -1; @@ -103681,10 +107595,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ */ case TK_ROW: { SrcList *pSrcList = pNC->pSrcList; - struct SrcList_item *pItem; + SrcItem *pItem; assert( pSrcList && pSrcList->nSrc>=1 ); pItem = pSrcList->a; pExpr->op = TK_COLUMN; + assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pItem->pTab; pExpr->iTable = pItem->iCursor; pExpr->iColumn--; @@ -103692,6 +107607,49 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ break; } + /* An optimization: Attempt to convert + ** + ** "expr IS NOT NULL" --> "TRUE" + ** "expr IS NULL" --> "FALSE" + ** + ** if we can prove that "expr" is never NULL. Call this the + ** "NOT NULL strength reduction optimization". + ** + ** If this optimization occurs, also restore the NameContext ref-counts + ** to the state they where in before the "column" LHS expression was + ** resolved. This prevents "column" from being counted as having been + ** referenced, which might prevent a SELECT from being erroneously + ** marked as correlated. + */ + case TK_NOTNULL: + case TK_ISNULL: { + int anRef[8]; + NameContext *p; + int i; + for(i=0, p=pNC; p && ipNext, i++){ + anRef[i] = p->nRef; + } + sqlcipher_sqlite3WalkExpr(pWalker, pExpr->pLeft); + if( 0==sqlcipher_sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){ + testcase( ExprHasProperty(pExpr, EP_OuterON) ); + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + if( pExpr->op==TK_NOTNULL ){ + pExpr->u.zToken = "true"; + ExprSetProperty(pExpr, EP_IsTrue); + }else{ + pExpr->u.zToken = "false"; + ExprSetProperty(pExpr, EP_IsFalse); + } + pExpr->op = TK_TRUEFALSE; + for(i=0, p=pNC; p && ipNext, i++){ + p->nRef = anRef[i]; + } + sqlcipher_sqlite3ExprDelete(pParse->db, pExpr->pLeft); + pExpr->pLeft = 0; + } + return WRC_Prune; + } + /* A column name: ID ** Or table name and column name: ID.ID ** Or a database, table and column: ID.ID.ID @@ -103710,24 +107668,28 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ if( pExpr->op==TK_ID ){ zDb = 0; zTable = 0; + assert( !ExprHasProperty(pExpr, EP_IntValue) ); zColumn = pExpr->u.zToken; }else{ Expr *pLeft = pExpr->pLeft; testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); sqlcipher_sqlite3ResolveNotValid(pParse, pNC, "the \".\" operator", - NC_IdxExpr|NC_GenCol, 0); + NC_IdxExpr|NC_GenCol, 0, pExpr); pRight = pExpr->pRight; if( pRight->op==TK_ID ){ zDb = 0; }else{ assert( pRight->op==TK_DOT ); + assert( !ExprHasProperty(pRight, EP_IntValue) ); zDb = pLeft->u.zToken; pLeft = pRight->pLeft; pRight = pRight->pRight; } + assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) ); zTable = pLeft->u.zToken; zColumn = pRight->u.zToken; + assert( ExprUseYTab(pExpr) ); if( IN_RENAME_OBJECT ){ sqlcipher_sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight); sqlcipher_sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft); @@ -103744,7 +107706,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ int no_such_func = 0; /* True if no such function exists */ int wrong_num_args = 0; /* True if wrong number of arguments */ int is_agg = 0; /* True if is an aggregate function */ - int nId; /* Number of characters in function name */ const char *zId; /* The function name. */ FuncDef *pDef; /* Information about the function */ u8 enc = ENC(pParse->db); /* The database encoding */ @@ -103752,9 +107713,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #ifndef SQLITE_OMIT_WINDOWFUNC Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0); #endif - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); zId = pExpr->u.zToken; - nId = sqlcipher_sqlite3Strlen30(zId); pDef = sqlcipher_sqlite3FindFunction(pParse->db, zId, n, enc, 0); if( pDef==0 ){ pDef = sqlcipher_sqlite3FindFunction(pParse->db, zId, -2, enc, 0); @@ -103771,9 +107731,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pExpr->iTable = exprProbability(pList->a[1].pExpr); if( pExpr->iTable<0 ){ sqlcipher_sqlite3ErrorMsg(pParse, - "second argument to likelihood() must be a " - "constant between 0.0 and 1.0"); - pNC->nErr++; + "second argument to %#T() must be a " + "constant between 0.0 and 1.0", pExpr); + pNC->nNcErr++; } }else{ /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is @@ -103793,9 +107753,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ int auth = sqlcipher_sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0,pDef->zName,0); if( auth!=SQLITE_OK ){ if( auth==SQLITE_DENY ){ - sqlcipher_sqlite3ErrorMsg(pParse, "not authorized to use function: %s", - pDef->zName); - pNC->nErr++; + sqlcipher_sqlite3ErrorMsg(pParse, "not authorized to use function: %#T", + pExpr); + pNC->nNcErr++; } pExpr->op = TK_NULL; return WRC_Prune; @@ -103817,7 +107777,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ ** in a CHECK constraint. SQLServer, MySQL, and PostgreSQL all ** all this. */ sqlcipher_sqlite3ResolveNotValid(pParse, pNC, "non-deterministic functions", - NC_IdxExpr|NC_PartIdx|NC_GenCol, 0); + NC_IdxExpr|NC_PartIdx|NC_GenCol, 0, pExpr); }else{ assert( (NC_SelfRef & 0xff)==NC_SelfRef ); /* Must fit in 8 bits */ pExpr->op2 = pNC->ncFlags & NC_SelfRef; @@ -103830,7 +107790,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ /* Internal-use-only functions are disallowed unless the ** SQL is being compiled using sqlcipher_sqlite3NestedParse() or ** the SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test-control has be - ** used to activate internal functionsn for testing purposes */ + ** used to activate internal functions for testing purposes */ no_such_func = 1; pDef = 0; }else @@ -103849,9 +107809,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ ); if( pDef && pDef->xValue==0 && pWin ){ sqlcipher_sqlite3ErrorMsg(pParse, - "%.*s() may not be used as a window function", nId, zId + "%#T() may not be used as a window function", pExpr ); - pNC->nErr++; + pNC->nNcErr++; }else if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pWin) @@ -103863,14 +107823,14 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ }else{ zType = "aggregate"; } - sqlcipher_sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId); - pNC->nErr++; + sqlcipher_sqlite3ErrorMsg(pParse, "misuse of %s function %#T()",zType,pExpr); + pNC->nNcErr++; is_agg = 0; } #else if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){ - sqlcipher_sqlite3ErrorMsg(pParse,"misuse of aggregate function %.*s()",nId,zId); - pNC->nErr++; + sqlcipher_sqlite3ErrorMsg(pParse,"misuse of aggregate function %#T()",pExpr); + pNC->nNcErr++; is_agg = 0; } #endif @@ -103879,20 +107839,20 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ && pParse->explain==0 #endif ){ - sqlcipher_sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); - pNC->nErr++; + sqlcipher_sqlite3ErrorMsg(pParse, "no such function: %#T", pExpr); + pNC->nNcErr++; }else if( wrong_num_args ){ - sqlcipher_sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", - nId, zId); - pNC->nErr++; + sqlcipher_sqlite3ErrorMsg(pParse,"wrong number of arguments to function %#T()", + pExpr); + pNC->nNcErr++; } #ifndef SQLITE_OMIT_WINDOWFUNC else if( is_agg==0 && ExprHasProperty(pExpr, EP_WinFunc) ){ sqlcipher_sqlite3ErrorMsg(pParse, - "FILTER may not be used with non-aggregate %.*s()", - nId, zId + "FILTER may not be used with non-aggregate %#T()", + pExpr ); - pNC->nErr++; + pNC->nNcErr++; } #endif if( is_agg ){ @@ -103916,9 +107876,10 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #ifndef SQLITE_OMIT_WINDOWFUNC if( pWin ){ Select *pSel = pNC->pWinSelect; - assert( pWin==pExpr->y.pWin ); + assert( pWin==0 || (ExprUseYWin(pExpr) && pWin==pExpr->y.pWin) ); if( IN_RENAME_OBJECT==0 ){ sqlcipher_sqlite3WindowUpdate(pParse, pSel ? pSel->pWinDefn : 0, pWin, pDef); + if( pParse->db->mallocFailed ) break; } sqlcipher_sqlite3WalkExprList(pWalker, pWin->pPartition); sqlcipher_sqlite3WalkExprList(pWalker, pWin->pOrderBy); @@ -103928,7 +107889,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ }else #endif /* SQLITE_OMIT_WINDOWFUNC */ { - NameContext *pNC2 = pNC; + NameContext *pNC2; /* For looping up thru outer contexts */ pExpr->op = TK_AGG_FUNCTION; pExpr->op2 = 0; #ifndef SQLITE_OMIT_WINDOWFUNC @@ -103936,16 +107897,22 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ sqlcipher_sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter); } #endif - while( pNC2 && !sqlcipher_sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){ + pNC2 = pNC; + while( pNC2 + && sqlcipher_sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0 + ){ pExpr->op2++; pNC2 = pNC2->pNext; } assert( pDef!=0 || IN_RENAME_OBJECT ); if( pNC2 && pDef ){ assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg ); + assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg ); testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 ); - pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX); - + testcase( (pDef->funcFlags & SQLITE_FUNC_ANYORDER)!=0 ); + pNC2->ncFlags |= NC_HasAgg + | ((pDef->funcFlags^SQLITE_FUNC_ANYORDER) + & (SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER)); } } pNC->ncFlags |= savedAllowFlags; @@ -103961,15 +107928,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #endif case TK_IN: { testcase( pExpr->op==TK_IN ); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ int nRef = pNC->nRef; testcase( pNC->ncFlags & NC_IsCheck ); testcase( pNC->ncFlags & NC_PartIdx ); testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); - sqlcipher_sqlite3ResolveNotValid(pParse, pNC, "subqueries", - NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr); - sqlcipher_sqlite3WalkSelect(pWalker, pExpr->x.pSelect); + if( pNC->ncFlags & NC_SelfRef ){ + notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr); + }else{ + sqlcipher_sqlite3WalkSelect(pWalker, pExpr->x.pSelect); + } assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); @@ -103984,7 +107953,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); sqlcipher_sqlite3ResolveNotValid(pParse, pNC, "parameters", - NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr); + NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr, pExpr); break; } case TK_IS: @@ -103993,7 +107962,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ assert( !ExprHasProperty(pExpr, EP_Reduced) ); /* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE", ** and "x IS NOT FALSE". */ - if( pRight && (pRight->op==TK_ID || pRight->op==TK_TRUEFALSE) ){ + if( ALWAYS(pRight) && (pRight->op==TK_ID || pRight->op==TK_TRUEFALSE) ){ int rc = resolveExprStep(pWalker, pRight); if( rc==WRC_Abort ) return WRC_Abort; if( pRight->op==TK_TRUEFALSE ){ @@ -104016,6 +107985,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ assert( pExpr->pLeft!=0 ); nLeft = sqlcipher_sqlite3ExprVectorSize(pExpr->pLeft); if( pExpr->op==TK_BETWEEN ){ + assert( ExprUseXList(pExpr) ); nRight = sqlcipher_sqlite3ExprVectorSize(pExpr->x.pList->a[0].pExpr); if( nRight==nLeft ){ nRight = sqlcipher_sqlite3ExprVectorSize(pExpr->x.pList->a[1].pExpr); @@ -104035,11 +108005,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_ISNOT ); testcase( pExpr->op==TK_BETWEEN ); sqlcipher_sqlite3ErrorMsg(pParse, "row value misused"); + sqlcipher_sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); } break; } } - return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; + assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); + return pParse->nErr ? WRC_Abort : WRC_Continue; } /* @@ -104064,9 +108036,11 @@ static int resolveAsName( UNUSED_PARAMETER(pParse); if( pE->op==TK_ID ){ - char *zCol = pE->u.zToken; + const char *zCol; + assert( !ExprHasProperty(pE, EP_IntValue) ); + zCol = pE->u.zToken; for(i=0; inExpr; i++){ - if( pEList->a[i].eEName==ENAME_NAME + if( pEList->a[i].fg.eEName==ENAME_NAME && sqlcipher_sqlite3_stricmp(pEList->a[i].zEName, zCol)==0 ){ return i+1; @@ -104115,11 +108089,11 @@ static int resolveOrderByTermToExprList( nc.pParse = pParse; nc.pSrcList = pSelect->pSrc; nc.uNC.pEList = pEList; - nc.ncFlags = NC_AllowAgg|NC_UEList; - nc.nErr = 0; + nc.ncFlags = NC_AllowAgg|NC_UEList|NC_NoSelect; + nc.nNcErr = 0; db = pParse->db; savedSuppErr = db->suppressErr; - if( IN_RENAME_OBJECT==0 ) db->suppressErr = 1; + db->suppressErr = 1; rc = sqlcipher_sqlite3ResolveExprNames(&nc, pE); db->suppressErr = savedSuppErr; if( rc ) return 0; @@ -104145,11 +108119,13 @@ static void resolveOutOfRangeError( Parse *pParse, /* The error context into which to write the error */ const char *zType, /* "ORDER" or "GROUP" */ int i, /* The index (1-based) of the term out of range */ - int mx /* Largest permissible value of i */ + int mx, /* Largest permissible value of i */ + Expr *pError /* Associate the error with the expression */ ){ sqlcipher_sqlite3ErrorMsg(pParse, "%r %s BY term out of range - should be " "between 1 and %d", i, zType, mx); + sqlcipher_sqlite3RecordErrorOffsetOfExpr(pParse->db, pError); } /* @@ -104185,7 +108161,7 @@ static int resolveCompoundOrderBy( return 1; } for(i=0; inExpr; i++){ - pOrderBy->a[i].done = 0; + pOrderBy->a[i].fg.done = 0; } pSelect->pNext = 0; while( pSelect->pPrior ){ @@ -104200,12 +108176,12 @@ static int resolveCompoundOrderBy( for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ int iCol = -1; Expr *pE, *pDup; - if( pItem->done ) continue; + if( pItem->fg.done ) continue; pE = sqlcipher_sqlite3ExprSkipCollateAndLikely(pItem->pExpr); if( NEVER(pE==0) ) continue; if( sqlcipher_sqlite3ExprIsInteger(pE, &iCol) ){ if( iCol<=0 || iCol>pEList->nExpr ){ - resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr); + resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr, pE); return 1; } }else{ @@ -104218,29 +108194,24 @@ static int resolveCompoundOrderBy( ** Once the comparisons are finished, the duplicate expression ** is deleted. ** - ** Or, if this is running as part of an ALTER TABLE operation, - ** resolve the symbols in the actual expression, not a duplicate. - ** And, if one of the comparisons is successful, leave the expression - ** as is instead of transforming it to an integer as in the usual - ** case. This allows the code in alter.c to modify column - ** refererences within the ORDER BY expression as required. */ - if( IN_RENAME_OBJECT ){ - pDup = pE; - }else{ - pDup = sqlcipher_sqlite3ExprDup(db, pE, 0); - } + ** If this is running as part of an ALTER TABLE operation and + ** the symbols resolve successfully, also resolve the symbols in the + ** actual expression. This allows the code in alter.c to modify + ** column references within the ORDER BY expression as required. */ + pDup = sqlcipher_sqlite3ExprDup(db, pE, 0); if( !db->mallocFailed ){ assert(pDup); iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup); + if( IN_RENAME_OBJECT && iCol>0 ){ + resolveOrderByTermToExprList(pParse, pSelect, pE); + } } - if( !IN_RENAME_OBJECT ){ - sqlcipher_sqlite3ExprDelete(db, pDup); - } + sqlcipher_sqlite3ExprDelete(db, pDup); } } if( iCol>0 ){ /* Convert the ORDER BY term into an integer column number iCol, - ** taking care to preserve the COLLATE clause if it exists */ + ** taking care to preserve the COLLATE clause if it exists. */ if( !IN_RENAME_OBJECT ){ Expr *pNew = sqlcipher_sqlite3Expr(db, TK_INTEGER, 0); if( pNew==0 ) return 1; @@ -104258,7 +108229,7 @@ static int resolveCompoundOrderBy( sqlcipher_sqlite3ExprDelete(db, pE); pItem->u.x.iOrderByCol = (u16)iCol; } - pItem->done = 1; + pItem->fg.done = 1; }else{ moreToDo = 1; } @@ -104266,7 +108237,7 @@ static int resolveCompoundOrderBy( pSelect = pSelect->pNext; } for(i=0; inExpr; i++){ - if( pOrderBy->a[i].done==0 ){ + if( pOrderBy->a[i].fg.done==0 ){ sqlcipher_sqlite3ErrorMsg(pParse, "%r ORDER BY term does not match any " "column in the result set", i+1); return 1; @@ -104306,11 +108277,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ResolveOrderGroupBy( for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ if( pItem->u.x.iOrderByCol ){ if( pItem->u.x.iOrderByCol>pEList->nExpr ){ - resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); + resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr, 0); return 1; } - resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, - zType,0); + resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,0); } } return 0; @@ -104376,7 +108346,7 @@ static int resolveOrderGroupBy( Parse *pParse; /* Parsing context */ int nResult; /* Number of terms in the result set */ - if( pOrderBy==0 ) return 0; + assert( pOrderBy!=0 ); nResult = pSelect->pEList->nExpr; pParse = pNC->pParse; for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ @@ -104399,7 +108369,7 @@ static int resolveOrderGroupBy( ** number so that sqlcipher_sqlite3ResolveOrderGroupBy() will convert the ** order-by term to a copy of the result-set expression */ if( iCol<1 || iCol>0xffff ){ - resolveOutOfRangeError(pParse, zType, i+1, nResult); + resolveOutOfRangeError(pParse, zType, i+1, nResult, pE2); return 1; } pItem->u.x.iOrderByCol = (u16)iCol; @@ -104457,7 +108427,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ */ if( (p->selFlags & SF_Expanded)==0 ){ sqlcipher_sqlite3SelectPrep(pParse, p, pOuterNC); - return (pParse->nErr || db->mallocFailed) ? WRC_Abort : WRC_Prune; + return pParse->nErr ? WRC_Abort : WRC_Prune; } isCompound = p->pPrior!=0; @@ -104466,8 +108436,10 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ while( p ){ assert( (p->selFlags & SF_Expanded)!=0 ); assert( (p->selFlags & SF_Resolved)==0 ); + assert( db->suppressErr==0 ); /* SF_Resolved not set if errors suppressed */ p->selFlags |= SF_Resolved; + /* Resolve the expressions in the LIMIT and OFFSET clauses. These ** are not allowed to refer to any names, so pass an empty NameContext. */ @@ -104492,30 +108464,30 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ p->pOrderBy = 0; } - /* Recursively resolve names in all subqueries + /* Recursively resolve names in all subqueries in the FROM clause */ for(i=0; ipSrc->nSrc; i++){ - struct SrcList_item *pItem = &p->pSrc->a[i]; + SrcItem *pItem = &p->pSrc->a[i]; if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ - NameContext *pNC; /* Used to iterate name contexts */ - int nRef = 0; /* Refcount for pOuterNC and outer contexts */ + int nRef = pOuterNC ? pOuterNC->nRef : 0; const char *zSavedContext = pParse->zAuthContext; - /* Count the total number of references to pOuterNC and all of its - ** parent contexts. After resolving references to expressions in - ** pItem->pSelect, check if this value has changed. If so, then - ** SELECT statement pItem->pSelect must be correlated. Set the - ** pItem->fg.isCorrelated flag if this is the case. */ - for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef; - if( pItem->zName ) pParse->zAuthContext = pItem->zName; sqlcipher_sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); pParse->zAuthContext = zSavedContext; - if( pParse->nErr || db->mallocFailed ) return WRC_Abort; + if( pParse->nErr ) return WRC_Abort; + assert( db->mallocFailed==0 ); - for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef; - assert( pItem->fg.isCorrelated==0 && nRef<=0 ); - pItem->fg.isCorrelated = (nRef!=0); + /* If the number of references to the outer context changed when + ** expressions in the sub-select were resolved, the sub-select + ** is correlated. It is not required to check the refcount on any + ** but the innermost outer context object, as lookupName() increments + ** the refcount on all contexts between the current one and the + ** context containing the column when it resolves a name. */ + if( pOuterNC ){ + assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef ); + pItem->fg.isCorrelated = (pOuterNC->nRef>nRef); + } } } @@ -104537,18 +108509,12 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ pGroupBy = p->pGroupBy; if( pGroupBy || (sNC.ncFlags & NC_HasAgg)!=0 ){ assert( NC_MinMaxAgg==SF_MinMaxAgg ); - p->selFlags |= SF_Aggregate | (sNC.ncFlags&NC_MinMaxAgg); + assert( NC_OrderAgg==SF_OrderByReqd ); + p->selFlags |= SF_Aggregate | (sNC.ncFlags&(NC_MinMaxAgg|NC_OrderAgg)); }else{ sNC.ncFlags &= ~NC_AllowAgg; } - /* If a HAVING clause is present, then there must be a GROUP BY clause. - */ - if( p->pHaving && !pGroupBy ){ - sqlcipher_sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); - return WRC_Abort; - } - /* Add the output column list to the name-context before parsing the ** other expressions in the SELECT statement. This is so that ** expressions in the WHERE clause (etc.) can refer to expressions by @@ -104557,15 +108523,21 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ ** Minor point: If this is the case, then the expression will be ** re-evaluated for each reference to it. */ - assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert))==0 ); + assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert|NC_UBaseReg))==0 ); sNC.uNC.pEList = p->pEList; sNC.ncFlags |= NC_UEList; - if( sqlcipher_sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; + if( p->pHaving ){ + if( (p->selFlags & SF_Aggregate)==0 ){ + sqlcipher_sqlite3ErrorMsg(pParse, "HAVING clause on a non-aggregate query"); + return WRC_Abort; + } + if( sqlcipher_sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; + } if( sqlcipher_sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; /* Resolve names in table-valued-function arguments */ for(i=0; ipSrc->nSrc; i++){ - struct SrcList_item *pItem = &p->pSrc->a[i]; + SrcItem *pItem = &p->pSrc->a[i]; if( pItem->fg.isTabFunc && sqlcipher_sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg) ){ @@ -104573,6 +108545,19 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } +#ifndef SQLITE_OMIT_WINDOWFUNC + if( IN_RENAME_OBJECT ){ + Window *pWin; + for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){ + if( sqlcipher_sqlite3ResolveExprListNames(&sNC, pWin->pOrderBy) + || sqlcipher_sqlite3ResolveExprListNames(&sNC, pWin->pPartition) + ){ + return WRC_Abort; + } + } + } +#endif + /* The ORDER BY and GROUP BY clauses may not refer to terms in ** outer queries */ @@ -104600,7 +108585,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ ** is not detected until much later, and so we need to go ahead and ** resolve those symbols on the incorrect ORDER BY for consistency. */ - if( isCompound<=nCompound /* Defer right-most ORDER BY of a compound */ + if( p->pOrderBy!=0 + && isCompound<=nCompound /* Defer right-most ORDER BY of a compound */ && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") ){ return WRC_Abort; @@ -104628,19 +108614,6 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } -#ifndef SQLITE_OMIT_WINDOWFUNC - if( IN_RENAME_OBJECT ){ - Window *pWin; - for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){ - if( sqlcipher_sqlite3ResolveExprListNames(&sNC, pWin->pOrderBy) - || sqlcipher_sqlite3ResolveExprListNames(&sNC, pWin->pPartition) - ){ - return WRC_Abort; - } - } - } -#endif - /* If this is part of a compound SELECT, check that it has the right ** number of expressions in the select list. */ if( p->pNext && p->pEList->nExpr!=p->pNext->pEList->nExpr ){ @@ -104720,11 +108693,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ResolveExprNames( Walker w; if( pExpr==0 ) return SQLITE_OK; - savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); - pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); w.pParse = pNC->pParse; w.xExprCallback = resolveExprStep; - w.xSelectCallback = resolveSelectStep; + w.xSelectCallback = (pNC->ncFlags & NC_NoSelect) ? 0 : resolveSelectStep; w.xSelectCallback2 = 0; w.u.pNC = pNC; #if SQLITE_MAX_EXPR_DEPTH>0 @@ -104743,7 +108716,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ResolveExprNames( testcase( pNC->ncFlags & NC_HasWin ); ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); pNC->ncFlags |= savedHasAgg; - return pNC->nErr>0 || w.pParse->nErr>0; + return pNC->nNcErr>0 || w.pParse->nErr>0; } /* @@ -104764,8 +108737,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ResolveExprListNames( w.xSelectCallback = resolveSelectStep; w.xSelectCallback2 = 0; w.u.pNC = pNC; - savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); - pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); for(i=0; inExpr; i++){ Expr *pExpr = pList->a[i].pExpr; if( pExpr==0 ) continue; @@ -104783,12 +108756,13 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ResolveExprListNames( assert( EP_Win==NC_HasWin ); testcase( pNC->ncFlags & NC_HasAgg ); testcase( pNC->ncFlags & NC_HasWin ); - if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin) ){ + if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg) ){ ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); - savedHasAgg |= pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); - pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + savedHasAgg |= pNC->ncFlags & + (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); } - if( pNC->nErr>0 || w.pParse->nErr>0 ) return WRC_Abort; + if( w.pParse->nErr>0 ) return WRC_Abort; } pNC->ncFlags |= savedHasAgg; return WRC_Continue; @@ -104900,9 +108874,9 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree); /* ** Return the affinity character for a single column of a table. */ -SQLITE_PRIVATE char sqlcipher_sqlite3TableColumnAffinity(Table *pTab, int iCol){ - assert( iColnCol ); - return iCol>=0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER; +SQLITE_PRIVATE char sqlcipher_sqlite3TableColumnAffinity(const Table *pTab, int iCol){ + if( iCol<0 || NEVER(iCol>=pTab->nCol) ) return SQLITE_AFF_INTEGER; + return pTab->aCol[iCol].affinity; } /* @@ -104931,30 +108905,36 @@ SQLITE_PRIVATE char sqlcipher_sqlite3ExprAffinity(const Expr *pExpr){ assert( pExpr!=0 ); } op = pExpr->op; + if( op==TK_REGISTER ) op = pExpr->op2; + if( op==TK_COLUMN || op==TK_AGG_COLUMN ){ + assert( ExprUseYTab(pExpr) ); + if( pExpr->y.pTab ){ + return sqlcipher_sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); + } + } if( op==TK_SELECT ){ - assert( pExpr->flags&EP_xIsSelect ); + assert( ExprUseXSelect(pExpr) ); assert( pExpr->x.pSelect!=0 ); assert( pExpr->x.pSelect->pEList!=0 ); assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 ); return sqlcipher_sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); } - if( op==TK_REGISTER ) op = pExpr->op2; #ifndef SQLITE_OMIT_CAST if( op==TK_CAST ){ assert( !ExprHasProperty(pExpr, EP_IntValue) ); return sqlcipher_sqlite3AffinityType(pExpr->u.zToken, 0); } #endif - if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->y.pTab ){ - return sqlcipher_sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); - } if( op==TK_SELECT_COLUMN ){ - assert( pExpr->pLeft->flags&EP_xIsSelect ); + assert( pExpr->pLeft!=0 && ExprUseXSelect(pExpr->pLeft) ); + assert( pExpr->iColumn < pExpr->iTable ); + assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr ); return sqlcipher_sqlite3ExprAffinity( pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr ); } if( op==TK_VECTOR ){ + assert( ExprUseXList(pExpr) ); return sqlcipher_sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr); } return pExpr->affExpr; @@ -104969,7 +108949,7 @@ SQLITE_PRIVATE char sqlcipher_sqlite3ExprAffinity(const Expr *pExpr){ ** and the pExpr parameter is returned unchanged. */ SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprAddCollateToken( - Parse *pParse, /* Parsing context */ + const Parse *pParse, /* Parsing context */ Expr *pExpr, /* Add the "COLLATE" clause to this expression */ const Token *pCollName, /* Name of collating sequence */ int dequote /* True to dequote pCollName */ @@ -104984,7 +108964,11 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprAddCollateToken( } return pExpr; } -SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){ +SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprAddCollateString( + const Parse *pParse, /* Parsing context */ + Expr *pExpr, /* Add the "COLLATE" clause to this expression */ + const char *zC /* The collating sequence name */ +){ Token s; assert( zC!=0 ); sqlcipher_sqlite3TokenInit(&s, (char*)zC); @@ -105010,7 +108994,7 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprSkipCollate(Expr *pExpr){ SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprSkipCollateAndLikely(Expr *pExpr){ while( pExpr && ExprHasProperty(pExpr, EP_Skip|EP_Unlikely) ){ if( ExprHasProperty(pExpr, EP_Unlikely) ){ - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( ExprUseXList(pExpr) ); assert( pExpr->x.pList->nExpr>0 ); assert( pExpr->op==TK_FUNCTION ); pExpr = pExpr->x.pList->a[0].pExpr; @@ -105043,27 +109027,30 @@ SQLITE_PRIVATE CollSeq *sqlcipher_sqlite3ExprCollSeq(Parse *pParse, const Expr * while( p ){ int op = p->op; if( op==TK_REGISTER ) op = p->op2; - if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER) - && p->y.pTab!=0 - ){ - /* op==TK_REGISTER && p->y.pTab!=0 happens when pExpr was originally - ** a TK_COLUMN but was previously evaluated and cached in a register */ - int j = p->iColumn; - if( j>=0 ){ - const char *zColl = p->y.pTab->aCol[j].zColl; - pColl = sqlcipher_sqlite3FindCollSeq(db, ENC(db), zColl, 0); + if( op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER ){ + assert( ExprUseYTab(p) ); + if( p->y.pTab!=0 ){ + /* op==TK_REGISTER && p->y.pTab!=0 happens when pExpr was originally + ** a TK_COLUMN but was previously evaluated and cached in a register */ + int j = p->iColumn; + if( j>=0 ){ + const char *zColl = sqlcipher_sqlite3ColumnColl(&p->y.pTab->aCol[j]); + pColl = sqlcipher_sqlite3FindCollSeq(db, ENC(db), zColl, 0); + } + break; } - break; } if( op==TK_CAST || op==TK_UPLUS ){ p = p->pLeft; continue; } if( op==TK_VECTOR ){ + assert( ExprUseXList(p) ); p = p->x.pList->a[0].pExpr; continue; } if( op==TK_COLLATE ){ + assert( !ExprHasProperty(p, EP_IntValue) ); pColl = sqlcipher_sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); break; } @@ -105073,11 +109060,9 @@ SQLITE_PRIVATE CollSeq *sqlcipher_sqlite3ExprCollSeq(Parse *pParse, const Expr * }else{ Expr *pNext = p->pRight; /* The Expr.x union is never used at the same time as Expr.pRight */ + assert( ExprUseXList(p) ); assert( p->x.pList==0 || p->pRight==0 ); - if( p->x.pList!=0 - && !db->mallocFailed - && ALWAYS(!ExprHasProperty(p, EP_xIsSelect)) - ){ + if( p->x.pList!=0 && !db->mallocFailed ){ int i; for(i=0; ALWAYS(ix.pList->nExpr); i++){ if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){ @@ -105160,7 +109145,7 @@ static char comparisonAffinity(const Expr *pExpr){ aff = sqlcipher_sqlite3ExprAffinity(pExpr->pLeft); if( pExpr->pRight ){ aff = sqlcipher_sqlite3CompareAffinity(pExpr->pRight, aff); - }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + }else if( ExprUseXSelect(pExpr) ){ aff = sqlcipher_sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff); }else if( aff==0 ){ aff = SQLITE_AFF_BLOB; @@ -105286,7 +109271,7 @@ static int codeCompare( ** But a TK_SELECT might be either a vector or a scalar. It is only ** considered a vector if it has two or more result columns. */ -SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsVector(Expr *pExpr){ +SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsVector(const Expr *pExpr){ return sqlcipher_sqlite3ExprVectorSize(pExpr)>1; } @@ -105296,12 +109281,14 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsVector(Expr *pExpr){ ** is a sub-select, return the number of columns in the sub-select. For ** any other type of expression, return 1. */ -SQLITE_PRIVATE int sqlcipher_sqlite3ExprVectorSize(Expr *pExpr){ +SQLITE_PRIVATE int sqlcipher_sqlite3ExprVectorSize(const Expr *pExpr){ u8 op = pExpr->op; if( op==TK_REGISTER ) op = pExpr->op2; if( op==TK_VECTOR ){ + assert( ExprUseXList(pExpr) ); return pExpr->x.pList->nExpr; }else if( op==TK_SELECT ){ + assert( ExprUseXSelect(pExpr) ); return pExpr->x.pSelect->pEList->nExpr; }else{ return 1; @@ -105324,12 +109311,14 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprVectorSize(Expr *pExpr){ ** been positioned. */ SQLITE_PRIVATE Expr *sqlcipher_sqlite3VectorFieldSubexpr(Expr *pVector, int i){ - assert( iop==TK_ERROR ); if( sqlcipher_sqlite3ExprIsVector(pVector) ){ assert( pVector->op2==0 || pVector->op==TK_REGISTER ); if( pVector->op==TK_SELECT || pVector->op2==TK_SELECT ){ + assert( ExprUseXSelect(pVector) ); return pVector->x.pSelect->pEList->a[i].pExpr; }else{ + assert( ExprUseXList(pVector) ); return pVector->x.pList->a[i].pExpr; } } @@ -105360,11 +109349,12 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3VectorFieldSubexpr(Expr *pVector, int i){ SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprForVectorField( Parse *pParse, /* Parsing context */ Expr *pVector, /* The vector. List of expressions or a sub-SELECT */ - int iField /* Which column of the vector to return */ + int iField, /* Which column of the vector to return */ + int nField /* Total number of columns in the vector */ ){ Expr *pRet; if( pVector->op==TK_SELECT ){ - assert( pVector->flags & EP_xIsSelect ); + assert( ExprUseXSelect(pVector) ); /* The TK_SELECT_COLUMN Expr node: ** ** pLeft: pVector containing TK_SELECT. Not deleted. @@ -105383,14 +109373,23 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprForVectorField( */ pRet = sqlcipher_sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0); if( pRet ){ + pRet->iTable = nField; pRet->iColumn = iField; pRet->pLeft = pVector; } - assert( pRet==0 || pRet->iTable==0 ); }else{ - if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr; + if( pVector->op==TK_VECTOR ){ + Expr **ppVector; + assert( ExprUseXList(pVector) ); + ppVector = &pVector->x.pList->a[iField].pExpr; + pVector = *ppVector; + if( IN_RENAME_OBJECT ){ + /* This must be a vector UPDATE inside a trigger */ + *ppVector = 0; + return pVector; + } + } pRet = sqlcipher_sqlite3ExprDup(pParse->db, pVector, 0); - sqlcipher_sqlite3RenameTokenRemap(pParse, pRet, pVector); } return pRet; } @@ -105440,17 +109439,22 @@ static int exprVectorRegister( int *pRegFree /* OUT: Temp register to free */ ){ u8 op = pVector->op; - assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT ); + assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT || op==TK_ERROR ); if( op==TK_REGISTER ){ *ppExpr = sqlcipher_sqlite3VectorFieldSubexpr(pVector, iField); return pVector->iTable+iField; } if( op==TK_SELECT ){ + assert( ExprUseXSelect(pVector) ); *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr; return regSelect+iField; } - *ppExpr = pVector->x.pList->a[iField].pExpr; - return sqlcipher_sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree); + if( op==TK_VECTOR ){ + assert( ExprUseXList(pVector) ); + *ppExpr = pVector->x.pList->a[iField].pExpr; + return sqlcipher_sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree); + } + return 0; } /* @@ -105479,6 +109483,7 @@ static void codeVectorCompare( int regLeft = 0; int regRight = 0; u8 opx = op; + int addrCmp = 0; int addrDone = sqlcipher_sqlite3VdbeMakeLabel(pParse); int isCommuted = ExprHasProperty(pExpr,EP_Commuted); @@ -105498,21 +109503,24 @@ static void codeVectorCompare( assert( p5==0 || pExpr->op!=op ); assert( p5==SQLITE_NULLEQ || pExpr->op==op ); - p5 |= SQLITE_STOREP2; - if( opx==TK_LE ) opx = TK_LT; - if( opx==TK_GE ) opx = TK_GT; + if( op==TK_LE ) opx = TK_LT; + if( op==TK_GE ) opx = TK_GT; + if( op==TK_NE ) opx = TK_EQ; regLeft = exprCodeSubselect(pParse, pLeft); regRight = exprCodeSubselect(pParse, pRight); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 1, dest); for(i=0; 1 /*Loop exits by "break"*/; i++){ int regFree1 = 0, regFree2 = 0; - Expr *pL, *pR; + Expr *pL = 0, *pR = 0; int r1, r2; assert( i>=0 && i0 @@ -105570,14 +109584,14 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCheckHeight(Parse *pParse, int nHeight){ ** to by pnHeight, the second parameter, then set *pnHeight to that ** value. */ -static void heightOfExpr(Expr *p, int *pnHeight){ +static void heightOfExpr(const Expr *p, int *pnHeight){ if( p ){ if( p->nHeight>*pnHeight ){ *pnHeight = p->nHeight; } } } -static void heightOfExprList(ExprList *p, int *pnHeight){ +static void heightOfExprList(const ExprList *p, int *pnHeight){ if( p ){ int i; for(i=0; inExpr; i++){ @@ -105585,8 +109599,8 @@ static void heightOfExprList(ExprList *p, int *pnHeight){ } } } -static void heightOfSelect(Select *pSelect, int *pnHeight){ - Select *p; +static void heightOfSelect(const Select *pSelect, int *pnHeight){ + const Select *p; for(p=pSelect; p; p=p->pPrior){ heightOfExpr(p->pWhere, pnHeight); heightOfExpr(p->pHaving, pnHeight); @@ -105608,10 +109622,9 @@ static void heightOfSelect(Select *pSelect, int *pnHeight){ ** if appropriate. */ static void exprSetHeight(Expr *p){ - int nHeight = 0; - heightOfExpr(p->pLeft, &nHeight); - heightOfExpr(p->pRight, &nHeight); - if( ExprHasProperty(p, EP_xIsSelect) ){ + int nHeight = p->pLeft ? p->pLeft->nHeight : 0; + if( p->pRight && p->pRight->nHeight>nHeight ) nHeight = p->pRight->nHeight; + if( ExprUseXSelect(p) ){ heightOfSelect(p->x.pSelect, &nHeight); }else if( p->x.pList ){ heightOfExprList(p->x.pList, &nHeight); @@ -105638,7 +109651,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr * ** Return the maximum height of any expression tree referenced ** by the select statement passed as an argument. */ -SQLITE_PRIVATE int sqlcipher_sqlite3SelectExprHeight(Select *p){ +SQLITE_PRIVATE int sqlcipher_sqlite3SelectExprHeight(const Select *p){ int nHeight = 0; heightOfSelect(p, &nHeight); return nHeight; @@ -105650,7 +109663,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3SelectExprHeight(Select *p){ */ SQLITE_PRIVATE void sqlcipher_sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ if( pParse->nErr ) return; - if( p && p->x.pList && !ExprHasProperty(p, EP_xIsSelect) ){ + if( p && ExprUseXList(p) && p->x.pList ){ p->flags |= EP_Propagate & sqlcipher_sqlite3ExprListFlags(p->x.pList); } } @@ -105808,6 +109821,63 @@ SQLITE_PRIVATE void sqlcipher_sqlite3PExprAddSelect(Parse *pParse, Expr *pExpr, } } +/* +** Expression list pEList is a list of vector values. This function +** converts the contents of pEList to a VALUES(...) Select statement +** returning 1 row for each element of the list. For example, the +** expression list: +** +** ( (1,2), (3,4) (5,6) ) +** +** is translated to the equivalent of: +** +** VALUES(1,2), (3,4), (5,6) +** +** Each of the vector values in pEList must contain exactly nElem terms. +** If a list element that is not a vector or does not contain nElem terms, +** an error message is left in pParse. +** +** This is used as part of processing IN(...) expressions with a list +** of vectors on the RHS. e.g. "... IN ((1,2), (3,4), (5,6))". +*/ +SQLITE_PRIVATE Select *sqlcipher_sqlite3ExprListToValues(Parse *pParse, int nElem, ExprList *pEList){ + int ii; + Select *pRet = 0; + assert( nElem>1 ); + for(ii=0; iinExpr; ii++){ + Select *pSel; + Expr *pExpr = pEList->a[ii].pExpr; + int nExprElem; + if( pExpr->op==TK_VECTOR ){ + assert( ExprUseXList(pExpr) ); + nExprElem = pExpr->x.pList->nExpr; + }else{ + nExprElem = 1; + } + if( nExprElem!=nElem ){ + sqlcipher_sqlite3ErrorMsg(pParse, "IN(...) element has %d term%s - expected %d", + nExprElem, nExprElem>1?"s":"", nElem + ); + break; + } + assert( ExprUseXList(pExpr) ); + pSel = sqlcipher_sqlite3SelectNew(pParse, pExpr->x.pList, 0, 0, 0, 0, 0, SF_Values,0); + pExpr->x.pList = 0; + if( pSel ){ + if( pRet ){ + pSel->op = TK_ALL; + pSel->pPrior = pRet; + } + pRet = pSel; + } + } + + if( pRet && pRet->pPrior ){ + pRet->selFlags |= SF_MultiValue; + } + sqlcipher_sqlite3ExprListDelete(pParse->db, pEList); + return pRet; +} /* ** Join two expressions using an AND operator. If either expression is @@ -105826,8 +109896,8 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr * }else if( (ExprAlwaysFalse(pLeft) || ExprAlwaysFalse(pRight)) && !IN_RENAME_OBJECT ){ - sqlcipher_sqlite3ExprDelete(db, pLeft); - sqlcipher_sqlite3ExprDelete(db, pRight); + sqlcipher_sqlite3ExprDeferredDelete(pParse, pLeft); + sqlcipher_sqlite3ExprDeferredDelete(pParse, pRight); return sqlcipher_sqlite3Expr(db, TK_INTEGER, "0"); }else{ return sqlcipher_sqlite3PExpr(pParse, TK_AND, pLeft, pRight); @@ -105841,7 +109911,7 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr * SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprFunction( Parse *pParse, /* Parsing context */ ExprList *pList, /* Argument list */ - Token *pToken, /* Name of the function */ + const Token *pToken, /* Name of the function */ int eDistinct /* SF_Distinct or SF_ALL or 0 */ ){ Expr *pNew; @@ -105852,12 +109922,17 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprFunction( sqlcipher_sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */ return 0; } - if( pList && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ + assert( !ExprHasProperty(pNew, EP_InnerON|EP_OuterON) ); + pNew->w.iOfst = (int)(pToken->z - pParse->zTail); + if( pList + && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] + && !pParse->nested + ){ sqlcipher_sqlite3ErrorMsg(pParse, "too many arguments on function %T", pToken); } pNew->x.pList = pList; ExprSetProperty(pNew, EP_HasFunc); - assert( !ExprHasProperty(pNew, EP_xIsSelect) ); + assert( ExprUseXList(pNew) ); sqlcipher_sqlite3ExprSetHeightAndFlags(pParse, pNew); if( eDistinct==SF_Distinct ) ExprSetProperty(pNew, EP_Distinct); return pNew; @@ -105876,8 +109951,8 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprFunction( */ SQLITE_PRIVATE void sqlcipher_sqlite3ExprFunctionUsable( Parse *pParse, /* Parsing and code generating context */ - Expr *pExpr, /* The function invocation */ - FuncDef *pDef /* The function being invoked */ + const Expr *pExpr, /* The function invocation */ + const FuncDef *pDef /* The function being invoked */ ){ assert( !IN_RENAME_OBJECT ); assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 ); @@ -105892,7 +109967,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprFunctionUsable( ** SQLITE_DBCONFIG_TRUSTED_SCHEMA is off (meaning ** that the schema is possibly tainted). */ - sqlcipher_sqlite3ErrorMsg(pParse, "unsafe use of %s()", pDef->zName); + sqlcipher_sqlite3ErrorMsg(pParse, "unsafe use of %#T()", pExpr); } } } @@ -105948,6 +110023,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pE if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ sqlcipher_sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]); + sqlcipher_sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); return; } x = (ynVar)i; @@ -105975,6 +110051,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pE pExpr->iColumn = x; if( x>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ sqlcipher_sqlite3ErrorMsg(pParse, "too many SQL variables"); + sqlcipher_sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); } } @@ -105983,27 +110060,26 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pE */ static SQLITE_NOINLINE void sqlcipher_sqlite3ExprDeleteNN(sqlcipher_sqlite3 *db, Expr *p){ assert( p!=0 ); - /* Sanity check: Assert that the IntValue is non-negative if it exists */ - assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 ); - - assert( !ExprHasProperty(p, EP_WinFunc) || p->y.pWin!=0 || db->mallocFailed ); - assert( p->op!=TK_FUNCTION || ExprHasProperty(p, EP_TokenOnly|EP_Reduced) - || p->y.pWin==0 || ExprHasProperty(p, EP_WinFunc) ); + assert( !ExprUseUValue(p) || p->u.iValue>=0 ); + assert( !ExprUseYWin(p) || !ExprUseYSub(p) ); + assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed ); + assert( p->op!=TK_FUNCTION || !ExprUseYSub(p) ); #ifdef SQLITE_DEBUG if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){ assert( p->pLeft==0 ); assert( p->pRight==0 ); - assert( p->x.pSelect==0 ); + assert( !ExprUseXSelect(p) || p->x.pSelect==0 ); + assert( !ExprUseXList(p) || p->x.pList==0 ); } #endif if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ - assert( p->x.pList==0 || p->pRight==0 ); + assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlcipher_sqlite3ExprDeleteNN(db, p->pLeft); if( p->pRight ){ assert( !ExprHasProperty(p, EP_WinFunc) ); sqlcipher_sqlite3ExprDeleteNN(db, p->pRight); - }else if( ExprHasProperty(p, EP_xIsSelect) ){ + }else if( ExprUseXSelect(p) ){ assert( !ExprHasProperty(p, EP_WinFunc) ); sqlcipher_sqlite3SelectDelete(db, p->x.pSelect); }else{ @@ -106015,7 +110091,10 @@ static SQLITE_NOINLINE void sqlcipher_sqlite3ExprDeleteNN(sqlcipher_sqlite3 *db, #endif } } - if( ExprHasProperty(p, EP_MemToken) ) sqlcipher_sqlite3DbFree(db, p->u.zToken); + if( ExprHasProperty(p, EP_MemToken) ){ + assert( !ExprHasProperty(p, EP_IntValue) ); + sqlcipher_sqlite3DbFree(db, p->u.zToken); + } if( !ExprHasProperty(p, EP_Static) ){ sqlcipher_sqlite3DbFreeNN(db, p); } @@ -106024,6 +110103,34 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprDelete(sqlcipher_sqlite3 *db, Expr *p){ if( p ) sqlcipher_sqlite3ExprDeleteNN(db, p); } +/* +** Clear both elements of an OnOrUsing object +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3ClearOnOrUsing(sqlcipher_sqlite3 *db, OnOrUsing *p){ + if( p==0 ){ + /* Nothing to clear */ + }else if( p->pOn ){ + sqlcipher_sqlite3ExprDeleteNN(db, p->pOn); + }else if( p->pUsing ){ + sqlcipher_sqlite3IdListDelete(db, p->pUsing); + } +} + +/* +** Arrange to cause pExpr to be deleted when the pParse is deleted. +** This is similar to sqlcipher_sqlite3ExprDelete() except that the delete is +** deferred untilthe pParse is deleted. +** +** The pExpr might be deleted immediately on an OOM error. +** +** The deferred delete is (currently) implemented by adding the +** pExpr to the pParse->pConstExpr list with a register number of 0. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ + pParse->pConstExpr = + sqlcipher_sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); +} + /* Invoke sqlcipher_sqlite3RenameExprUnmap() and sqlcipher_sqlite3ExprDelete() on the ** expression. */ @@ -106041,7 +110148,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){ ** passed as the first argument. This is always one of EXPR_FULLSIZE, ** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE. */ -static int exprStructSize(Expr *p){ +static int exprStructSize(const Expr *p){ if( ExprHasProperty(p, EP_TokenOnly) ) return EXPR_TOKENONLYSIZE; if( ExprHasProperty(p, EP_Reduced) ) return EXPR_REDUCEDSIZE; return EXPR_FULLSIZE; @@ -106081,7 +110188,7 @@ static int exprStructSize(Expr *p){ ** of dupedExprStructSize() contain multiple assert() statements that attempt ** to enforce this constraint. */ -static int dupedExprStructSize(Expr *p, int flags){ +static int dupedExprStructSize(const Expr *p, int flags){ int nSize; assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */ assert( EXPR_FULLSIZE<=0xfff ); @@ -106094,7 +110201,7 @@ static int dupedExprStructSize(Expr *p, int flags){ nSize = EXPR_FULLSIZE; }else{ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); - assert( !ExprHasProperty(p, EP_FromJoin) ); + assert( !ExprHasProperty(p, EP_OuterON) ); assert( !ExprHasProperty(p, EP_MemToken) ); assert( !ExprHasVVAProperty(p, EP_NoReduce) ); if( p->pLeft || p->x.pList ){ @@ -106112,7 +110219,7 @@ static int dupedExprStructSize(Expr *p, int flags){ ** of the Expr structure and a copy of the Expr.u.zToken string (if that ** string is defined.) */ -static int dupedExprNodeSize(Expr *p, int flags){ +static int dupedExprNodeSize(const Expr *p, int flags){ int nByte = dupedExprStructSize(p, flags) & 0xfff; if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ nByte += sqlcipher_sqlite3Strlen30NN(p->u.zToken)+1; @@ -106133,7 +110240,7 @@ static int dupedExprNodeSize(Expr *p, int flags){ ** and Expr.pRight variables (but not for any structures pointed to or ** descended from the Expr.x.pList or Expr.x.pSelect variables). */ -static int dupedExprSize(Expr *p, int flags){ +static int dupedExprSize(const Expr *p, int flags){ int nByte = 0; if( p ){ nByte = dupedExprNodeSize(p, flags); @@ -106152,7 +110259,7 @@ static int dupedExprSize(Expr *p, int flags){ ** if any. Before returning, *pzBuffer is set to the first byte past the ** portion of the buffer copied into by this function. */ -static Expr *exprDup(sqlcipher_sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ +static Expr *exprDup(sqlcipher_sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){ Expr *pNew; /* Value to return */ u8 *zAlloc; /* Memory space from which to build Expr object */ u32 staticFlag; /* EP_Static if space not obtained from malloc */ @@ -106166,6 +110273,7 @@ static Expr *exprDup(sqlcipher_sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer if( pzBuffer ){ zAlloc = *pzBuffer; staticFlag = EP_Static; + assert( zAlloc!=0 ); }else{ zAlloc = sqlcipher_sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags)); staticFlag = 0; @@ -106214,7 +110322,7 @@ static Expr *exprDup(sqlcipher_sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_Leaf)) ){ /* Fill in the pNew->x.pSelect or pNew->x.pList member. */ - if( ExprHasProperty(p, EP_xIsSelect) ){ + if( ExprUseXSelect(p) ){ pNew->x.pSelect = sqlcipher_sqlite3SelectDup(db, p->x.pSelect, dupFlags); }else{ pNew->x.pList = sqlcipher_sqlite3ExprListDup(db, p->x.pList, dupFlags); @@ -106243,8 +110351,8 @@ static Expr *exprDup(sqlcipher_sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ if( pNew->op==TK_SELECT_COLUMN ){ pNew->pLeft = p->pLeft; - assert( p->iColumn==0 || p->pRight==0 ); - assert( p->pRight==0 || p->pRight==p->pLeft ); + assert( p->pRight==0 || p->pRight==p->pLeft + || ExprHasProperty(p->pLeft, EP_Subquery) ); }else{ pNew->pLeft = sqlcipher_sqlite3ExprDup(db, p->pLeft, 0); } @@ -106261,7 +110369,7 @@ static Expr *exprDup(sqlcipher_sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer ** and the db->mallocFailed flag set. */ #ifndef SQLITE_OMIT_CTE -static With *withDup(sqlcipher_sqlite3 *db, With *p){ +SQLITE_PRIVATE With *sqlcipher_sqlite3WithDup(sqlcipher_sqlite3 *db, With *p){ With *pRet = 0; if( p ){ sqlcipher_sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); @@ -106273,13 +110381,14 @@ static With *withDup(sqlcipher_sqlite3 *db, With *p){ pRet->a[i].pSelect = sqlcipher_sqlite3SelectDup(db, p->a[i].pSelect, 0); pRet->a[i].pCols = sqlcipher_sqlite3ExprListDup(db, p->a[i].pCols, 0); pRet->a[i].zName = sqlcipher_sqlite3DbStrDup(db, p->a[i].zName); + pRet->a[i].eM10d = p->a[i].eM10d; } } } return pRet; } #else -# define withDup(x,y) 0 +# define sqlcipher_sqlite3WithDup(x,y) 0 #endif #ifndef SQLITE_OMIT_WINDOWFUNC @@ -106332,20 +110441,23 @@ static void gatherSelectWindows(Select *p){ ** truncated version of the usual Expr structure that will be stored as ** part of the in-memory representation of the database schema. */ -SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprDup(sqlcipher_sqlite3 *db, Expr *p, int flags){ +SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprDup(sqlcipher_sqlite3 *db, const Expr *p, int flags){ assert( flags==0 || flags==EXPRDUP_REDUCE ); return p ? exprDup(db, p, flags, 0) : 0; } -SQLITE_PRIVATE ExprList *sqlcipher_sqlite3ExprListDup(sqlcipher_sqlite3 *db, ExprList *p, int flags){ +SQLITE_PRIVATE ExprList *sqlcipher_sqlite3ExprListDup(sqlcipher_sqlite3 *db, const ExprList *p, int flags){ ExprList *pNew; - struct ExprList_item *pItem, *pOldItem; + struct ExprList_item *pItem; + const struct ExprList_item *pOldItem; int i; - Expr *pPriorSelectCol = 0; + Expr *pPriorSelectColOld = 0; + Expr *pPriorSelectColNew = 0; assert( db!=0 ); if( p==0 ) return 0; pNew = sqlcipher_sqlite3DbMallocRawNN(db, sqlcipher_sqlite3DbMallocSize(db, p)); if( pNew==0 ) return 0; pNew->nExpr = p->nExpr; + pNew->nAlloc = p->nAlloc; pItem = pNew->a; pOldItem = p->a; for(i=0; inExpr; i++, pItem++, pOldItem++){ @@ -106356,24 +110468,22 @@ SQLITE_PRIVATE ExprList *sqlcipher_sqlite3ExprListDup(sqlcipher_sqlite3 *db, Exp && pOldExpr->op==TK_SELECT_COLUMN && (pNewExpr = pItem->pExpr)!=0 ){ - assert( pNewExpr->iColumn==0 || i>0 ); - if( pNewExpr->iColumn==0 ){ - assert( pOldExpr->pLeft==pOldExpr->pRight ); - pPriorSelectCol = pNewExpr->pLeft = pNewExpr->pRight; + if( pNewExpr->pRight ){ + pPriorSelectColOld = pOldExpr->pRight; + pPriorSelectColNew = pNewExpr->pRight; + pNewExpr->pLeft = pNewExpr->pRight; }else{ - assert( i>0 ); - assert( pItem[-1].pExpr!=0 ); - assert( pNewExpr->iColumn==pItem[-1].pExpr->iColumn+1 ); - assert( pPriorSelectCol==pItem[-1].pExpr->pLeft ); - pNewExpr->pLeft = pPriorSelectCol; + if( pOldExpr->pLeft!=pPriorSelectColOld ){ + pPriorSelectColOld = pOldExpr->pLeft; + pPriorSelectColNew = sqlcipher_sqlite3ExprDup(db, pPriorSelectColOld, flags); + pNewExpr->pRight = pPriorSelectColNew; + } + pNewExpr->pLeft = pPriorSelectColNew; } } pItem->zEName = sqlcipher_sqlite3DbStrDup(db, pOldItem->zEName); - pItem->sortFlags = pOldItem->sortFlags; - pItem->eEName = pOldItem->eEName; - pItem->done = 0; - pItem->bNulls = pOldItem->bNulls; - pItem->bSorterRef = pOldItem->bSorterRef; + pItem->fg = pOldItem->fg; + pItem->fg.done = 0; pItem->u = pOldItem->u; } return pNew; @@ -106387,7 +110497,7 @@ SQLITE_PRIVATE ExprList *sqlcipher_sqlite3ExprListDup(sqlcipher_sqlite3 *db, Exp */ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ || !defined(SQLITE_OMIT_SUBQUERY) -SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListDup(sqlcipher_sqlite3 *db, SrcList *p, int flags){ +SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListDup(sqlcipher_sqlite3 *db, const SrcList *p, int flags){ SrcList *pNew; int i; int nByte; @@ -106398,8 +110508,8 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListDup(sqlcipher_sqlite3 *db, SrcLi if( pNew==0 ) return 0; pNew->nSrc = pNew->nAlloc = p->nSrc; for(i=0; inSrc; i++){ - struct SrcList_item *pNewItem = &pNew->a[i]; - struct SrcList_item *pOldItem = &p->a[i]; + SrcItem *pNewItem = &pNew->a[i]; + const SrcItem *pOldItem = &p->a[i]; Table *pTab; pNewItem->pSchema = pOldItem->pSchema; pNewItem->zDatabase = sqlcipher_sqlite3DbStrDup(db, pOldItem->zDatabase); @@ -106412,7 +110522,10 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListDup(sqlcipher_sqlite3 *db, SrcLi if( pNewItem->fg.isIndexedBy ){ pNewItem->u1.zIndexedBy = sqlcipher_sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy); } - pNewItem->pIBIndex = pOldItem->pIBIndex; + pNewItem->u2 = pOldItem->u2; + if( pNewItem->fg.isCte ){ + pNewItem->u2.pCteUse->nUse++; + } if( pNewItem->fg.isTabFunc ){ pNewItem->u1.pFuncArg = sqlcipher_sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); @@ -106422,41 +110535,39 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListDup(sqlcipher_sqlite3 *db, SrcLi pTab->nTabRef++; } pNewItem->pSelect = sqlcipher_sqlite3SelectDup(db, pOldItem->pSelect, flags); - pNewItem->pOn = sqlcipher_sqlite3ExprDup(db, pOldItem->pOn, flags); - pNewItem->pUsing = sqlcipher_sqlite3IdListDup(db, pOldItem->pUsing); + if( pOldItem->fg.isUsing ){ + assert( pNewItem->fg.isUsing ); + pNewItem->u3.pUsing = sqlcipher_sqlite3IdListDup(db, pOldItem->u3.pUsing); + }else{ + pNewItem->u3.pOn = sqlcipher_sqlite3ExprDup(db, pOldItem->u3.pOn, flags); + } pNewItem->colUsed = pOldItem->colUsed; } return pNew; } -SQLITE_PRIVATE IdList *sqlcipher_sqlite3IdListDup(sqlcipher_sqlite3 *db, IdList *p){ +SQLITE_PRIVATE IdList *sqlcipher_sqlite3IdListDup(sqlcipher_sqlite3 *db, const IdList *p){ IdList *pNew; int i; assert( db!=0 ); if( p==0 ) return 0; - pNew = sqlcipher_sqlite3DbMallocRawNN(db, sizeof(*pNew) ); + assert( p->eU4!=EU4_EXPR ); + pNew = sqlcipher_sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) ); if( pNew==0 ) return 0; pNew->nId = p->nId; - pNew->a = sqlcipher_sqlite3DbMallocRawNN(db, p->nId*sizeof(p->a[0]) ); - if( pNew->a==0 ){ - sqlcipher_sqlite3DbFreeNN(db, pNew); - return 0; - } - /* Note that because the size of the allocation for p->a[] is not - ** necessarily a power of two, sqlcipher_sqlite3IdListAppend() may not be called - ** on the duplicate created by this function. */ + pNew->eU4 = p->eU4; for(i=0; inId; i++){ struct IdList_item *pNewItem = &pNew->a[i]; - struct IdList_item *pOldItem = &p->a[i]; + const struct IdList_item *pOldItem = &p->a[i]; pNewItem->zName = sqlcipher_sqlite3DbStrDup(db, pOldItem->zName); - pNewItem->idx = pOldItem->idx; + pNewItem->u4 = pOldItem->u4; } return pNew; } -SQLITE_PRIVATE Select *sqlcipher_sqlite3SelectDup(sqlcipher_sqlite3 *db, Select *pDup, int flags){ +SQLITE_PRIVATE Select *sqlcipher_sqlite3SelectDup(sqlcipher_sqlite3 *db, const Select *pDup, int flags){ Select *pRet = 0; Select *pNext = 0; Select **pp = &pRet; - Select *p; + const Select *p; assert( db!=0 ); for(p=pDup; p; p=p->pPrior){ @@ -106478,13 +110589,21 @@ SQLITE_PRIVATE Select *sqlcipher_sqlite3SelectDup(sqlcipher_sqlite3 *db, Select pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; pNew->nSelectRow = p->nSelectRow; - pNew->pWith = withDup(db, p->pWith); + pNew->pWith = sqlcipher_sqlite3WithDup(db, p->pWith); #ifndef SQLITE_OMIT_WINDOWFUNC pNew->pWin = 0; pNew->pWinDefn = sqlcipher_sqlite3WindowListDup(db, p->pWinDefn); if( p->pWin && db->mallocFailed==0 ) gatherSelectWindows(pNew); #endif pNew->selId = p->selId; + if( db->mallocFailed ){ + /* Any prior OOM might have left the Select object incomplete. + ** Delete the whole thing rather than allow an incomplete Select + ** to be used by the code generator. */ + pNew->pNext = 0; + sqlcipher_sqlite3SelectDelete(db, pNew); + break; + } *pp = pNew; pp = &pNew->pPrior; pNext = pNew; @@ -106493,7 +110612,7 @@ SQLITE_PRIVATE Select *sqlcipher_sqlite3SelectDup(sqlcipher_sqlite3 *db, Select return pRet; } #else -SQLITE_PRIVATE Select *sqlcipher_sqlite3SelectDup(sqlcipher_sqlite3 *db, Select *p, int flags){ +SQLITE_PRIVATE Select *sqlcipher_sqlite3SelectDup(sqlcipher_sqlite3 *db, const Select *p, int flags){ assert( p==0 ); return 0; } @@ -106515,41 +110634,64 @@ SQLITE_PRIVATE Select *sqlcipher_sqlite3SelectDup(sqlcipher_sqlite3 *db, Select ** NULL is returned. If non-NULL is returned, then it is guaranteed ** that the new entry was successfully appended. */ +static const struct ExprList_item zeroItem = {0}; +SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlcipher_sqlite3ExprListAppendNew( + sqlcipher_sqlite3 *db, /* Database handle. Used for memory allocation */ + Expr *pExpr /* Expression to be appended. Might be NULL */ +){ + struct ExprList_item *pItem; + ExprList *pList; + + pList = sqlcipher_sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 ); + if( pList==0 ){ + sqlcipher_sqlite3ExprDelete(db, pExpr); + return 0; + } + pList->nAlloc = 4; + pList->nExpr = 1; + pItem = &pList->a[0]; + *pItem = zeroItem; + pItem->pExpr = pExpr; + return pList; +} +SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlcipher_sqlite3ExprListAppendGrow( + sqlcipher_sqlite3 *db, /* Database handle. Used for memory allocation */ + ExprList *pList, /* List to which to append. Might be NULL */ + Expr *pExpr /* Expression to be appended. Might be NULL */ +){ + struct ExprList_item *pItem; + ExprList *pNew; + pList->nAlloc *= 2; + pNew = sqlcipher_sqlite3DbRealloc(db, pList, + sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0])); + if( pNew==0 ){ + sqlcipher_sqlite3ExprListDelete(db, pList); + sqlcipher_sqlite3ExprDelete(db, pExpr); + return 0; + }else{ + pList = pNew; + } + pItem = &pList->a[pList->nExpr++]; + *pItem = zeroItem; + pItem->pExpr = pExpr; + return pList; +} SQLITE_PRIVATE ExprList *sqlcipher_sqlite3ExprListAppend( Parse *pParse, /* Parsing context */ ExprList *pList, /* List to which to append. Might be NULL */ Expr *pExpr /* Expression to be appended. Might be NULL */ ){ struct ExprList_item *pItem; - sqlcipher_sqlite3 *db = pParse->db; - assert( db!=0 ); if( pList==0 ){ - pList = sqlcipher_sqlite3DbMallocRawNN(db, sizeof(ExprList) ); - if( pList==0 ){ - goto no_mem; - } - pList->nExpr = 0; - }else if( (pList->nExpr & (pList->nExpr-1))==0 ){ - ExprList *pNew; - pNew = sqlcipher_sqlite3DbRealloc(db, pList, - sizeof(*pList)+(2*(sqlcipher_sqlite3_int64)pList->nExpr-1)*sizeof(pList->a[0])); - if( pNew==0 ){ - goto no_mem; - } - pList = pNew; + return sqlcipher_sqlite3ExprListAppendNew(pParse->db,pExpr); + } + if( pList->nAllocnExpr+1 ){ + return sqlcipher_sqlite3ExprListAppendGrow(pParse->db,pList,pExpr); } pItem = &pList->a[pList->nExpr++]; - assert( offsetof(struct ExprList_item,zEName)==sizeof(pItem->pExpr) ); - assert( offsetof(struct ExprList_item,pExpr)==0 ); - memset(&pItem->zEName,0,sizeof(*pItem)-offsetof(struct ExprList_item,zEName)); + *pItem = zeroItem; pItem->pExpr = pExpr; return pList; - -no_mem: - /* Avoid leaking memory if malloc has failed. */ - sqlcipher_sqlite3ExprDelete(db, pExpr); - sqlcipher_sqlite3ExprListDelete(db, pList); - return 0; } /* @@ -106590,11 +110732,9 @@ SQLITE_PRIVATE ExprList *sqlcipher_sqlite3ExprListAppendVector( } for(i=0; inId; i++){ - Expr *pSubExpr = sqlcipher_sqlite3ExprForVectorField(pParse, pExpr, i); + Expr *pSubExpr = sqlcipher_sqlite3ExprForVectorField(pParse, pExpr, i, pColumns->nId); assert( pSubExpr!=0 || db->mallocFailed ); - assert( pSubExpr==0 || pSubExpr->iTable==0 ); if( pSubExpr==0 ) continue; - pSubExpr->iTable = pColumns->nId; pList = sqlcipher_sqlite3ExprListAppend(pParse, pList, pSubExpr); if( pList ){ assert( pList->nExpr==iFirst+i+1 ); @@ -106643,16 +110783,16 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprListSetSortOrder(ExprList *p, int iSort ); pItem = &p->a[p->nExpr-1]; - assert( pItem->bNulls==0 ); + assert( pItem->fg.bNulls==0 ); if( iSortOrder==SQLITE_SO_UNDEFINED ){ iSortOrder = SQLITE_SO_ASC; } - pItem->sortFlags = (u8)iSortOrder; + pItem->fg.sortFlags = (u8)iSortOrder; if( eNulls!=SQLITE_SO_UNDEFINED ){ - pItem->bNulls = 1; + pItem->fg.bNulls = 1; if( iSortOrder!=eNulls ){ - pItem->sortFlags |= KEYINFO_ORDER_BIGNULL; + pItem->fg.sortFlags |= KEYINFO_ORDER_BIGNULL; } } } @@ -106668,7 +110808,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprListSetSortOrder(ExprList *p, int iSort SQLITE_PRIVATE void sqlcipher_sqlite3ExprListSetName( Parse *pParse, /* Parsing context */ ExprList *pList, /* List to which to add the span. */ - Token *pName, /* Name to be added */ + const Token *pName, /* Name to be added */ int dequote /* True to cause the name to be dequoted */ ){ assert( pList!=0 || pParse->db->mallocFailed!=0 ); @@ -106678,7 +110818,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprListSetName( assert( pList->nExpr>0 ); pItem = &pList->a[pList->nExpr-1]; assert( pItem->zEName==0 ); - assert( pItem->eEName==ENAME_NAME ); + assert( pItem->fg.eEName==ENAME_NAME ); pItem->zEName = sqlcipher_sqlite3DbStrNDup(pParse->db, pName->z, pName->n); if( dequote ){ /* If dequote==0, then pName->z does not point to part of a DDL @@ -106686,7 +110826,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprListSetName( ** to the token-map. */ sqlcipher_sqlite3Dequote(pItem->zEName); if( IN_RENAME_OBJECT ){ - sqlcipher_sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName); + sqlcipher_sqlite3RenameTokenMap(pParse, (const void*)pItem->zEName, pName); } } } @@ -106713,7 +110853,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprListSetSpan( assert( pList->nExpr>0 ); if( pItem->zEName==0 ){ pItem->zEName = sqlcipher_sqlite3DbSpanDup(db, zStart, zEnd); - pItem->eEName = ENAME_SPAN; + pItem->fg.eEName = ENAME_SPAN; } } } @@ -106805,7 +110945,7 @@ SQLITE_PRIVATE u32 sqlcipher_sqlite3IsTrueOrFalse(const char *zIn){ SQLITE_PRIVATE int sqlcipher_sqlite3ExprIdToTrueFalse(Expr *pExpr){ u32 v; assert( pExpr->op==TK_ID || pExpr->op==TK_STRING ); - if( !ExprHasProperty(pExpr, EP_Quoted) + if( !ExprHasProperty(pExpr, EP_Quoted|EP_IntValue) && (v = sqlcipher_sqlite3IsTrueOrFalse(pExpr->u.zToken))!=0 ){ pExpr->op = TK_TRUEFALSE; @@ -106822,6 +110962,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprIdToTrueFalse(Expr *pExpr){ SQLITE_PRIVATE int sqlcipher_sqlite3ExprTruthValue(const Expr *pExpr){ pExpr = sqlcipher_sqlite3ExprSkipCollate((Expr*)pExpr); assert( pExpr->op==TK_TRUEFALSE ); + assert( !ExprHasProperty(pExpr, EP_IntValue) ); assert( sqlcipher_sqlite3StrICmp(pExpr->u.zToken,"true")==0 || sqlcipher_sqlite3StrICmp(pExpr->u.zToken,"false")==0 ); return pExpr->u.zToken[4]==0; @@ -106884,9 +111025,9 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3ExprSimplifiedAndOr(Expr *pExpr){ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ /* If pWalker->eCode is 2 then any term of the expression that comes from - ** the ON or USING clauses of a left join disqualifies the expression + ** the ON or USING clauses of an outer join disqualifies the expression ** from being considered constant. */ - if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_FromJoin) ){ + if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_OuterON) ){ pWalker->eCode = 0; return WRC_Abort; } @@ -107005,6 +111146,42 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsTableConstant(Expr *p, int iCur){ return exprIsConst(p, 3, iCur); } +/* +** Check pExpr to see if it is an invariant constraint on data source pSrc. +** This is an optimization. False negatives will perhaps cause slower +** queries, but false positives will yield incorrect answers. So when in +** doubt, return 0. +** +** To be an invariant constraint, the following must be true: +** +** (1) pExpr cannot refer to any table other than pSrc->iCursor. +** +** (2) pExpr cannot use subqueries or non-deterministic functions. +** +** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. +** (Is there some way to relax this constraint?) +** +** (4) If pSrc is the right operand of a LEFT JOIN, then... +** (4a) pExpr must come from an ON clause.. + (4b) and specifically the ON clause associated with the LEFT JOIN. +** +** (5) If pSrc is not the right operand of a LEFT JOIN or the left +** operand of a RIGHT JOIN, then pExpr must be from the WHERE +** clause, not an ON clause. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsTableConstraint(Expr *pExpr, const SrcItem *pSrc){ + if( pSrc->fg.jointype & JT_LTORJ ){ + return 0; /* rule (3) */ + } + if( pSrc->fg.jointype & JT_LEFT ){ + if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */ + if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */ + }else{ + if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */ + } + return sqlcipher_sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */ +} + /* ** sqlcipher_sqlite3WalkExpr() callback used by sqlcipher_sqlite3ExprIsConstantOrGroupBy(). @@ -107026,7 +111203,7 @@ static int exprNodeIsConstantOrGroupBy(Walker *pWalker, Expr *pExpr){ } /* Check if pExpr is a sub-select. If so, consider it variable. */ - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ pWalker->eCode = 0; return WRC_Abort; } @@ -107114,7 +111291,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprContainsSubquery(Expr *p){ ** in *pValue. If the expression is not an integer or if it is too big ** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. */ -SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsInteger(Expr *p, int *pValue){ +SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsInteger(const Expr *p, int *pValue){ int rc = 0; if( NEVER(p==0) ) return 0; /* Used to only happen following on OOM */ @@ -107133,9 +111310,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsInteger(Expr *p, int *pValue){ break; } case TK_UMINUS: { - int v; + int v = 0; if( sqlcipher_sqlite3ExprIsInteger(p->pLeft, &v) ){ - assert( v!=(-2147483647-1) ); + assert( ((unsigned int)v)!=0x80000000 ); *pValue = -v; rc = 1; } @@ -107162,8 +111339,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprIsInteger(Expr *p, int *pValue){ */ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCanBeNull(const Expr *p){ u8 op; + assert( p!=0 ); while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; + assert( p!=0 ); } op = p->op; if( op==TK_REGISTER ) op = p->op2; @@ -107174,10 +111353,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCanBeNull(const Expr *p){ case TK_BLOB: return 0; case TK_COLUMN: + assert( ExprUseYTab(p) ); return ExprHasProperty(p, EP_CanBeNull) || p->y.pTab==0 || /* Reference to column of index on expression */ (p->iColumn>=0 - && ALWAYS(p->y.pTab->aCol!=0) /* Defense against OOM problems */ + && p->y.pTab->aCol!=0 /* Possible due to prior error */ && p->y.pTab->aCol[p->iColumn].notNull==0); default: return 1; @@ -107245,13 +111425,13 @@ SQLITE_PRIVATE int sqlcipher_sqlite3IsRowid(const char *z){ ** table, then return NULL. */ #ifndef SQLITE_OMIT_SUBQUERY -static Select *isCandidateForInOpt(Expr *pX){ +static Select *isCandidateForInOpt(const Expr *pX){ Select *p; SrcList *pSrc; ExprList *pEList; Table *pTab; int i; - if( !ExprHasProperty(pX, EP_xIsSelect) ) return 0; /* Not a subquery */ + if( !ExprUseXSelect(pX) ) return 0; /* Not a subquery */ if( ExprHasProperty(pX, EP_VarSelect) ) return 0; /* Correlated subq */ p = pX->x.pSelect; if( p->pPrior ) return 0; /* Not a compound SELECT */ @@ -107269,7 +111449,7 @@ static Select *isCandidateForInOpt(Expr *pX){ if( pSrc->a[0].pSelect ) return 0; /* FROM is not a subquery or view */ pTab = pSrc->a[0].pTab; assert( pTab!=0 ); - assert( pTab->pSelect==0 ); /* FROM clause is not a view */ + assert( !IsView(pTab) ); /* FROM clause is not a view */ if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */ pEList = p->pEList; assert( pEList!=0 ); @@ -107329,7 +111509,7 @@ static int sqlcipher_sqlite3InRhsIsConstant(Expr *pIn){ ** all members of the RHS set, skipping duplicates. ** ** A cursor is opened on the b-tree object that is the RHS of the IN operator -** and pX->iTable is set to the index of that cursor. +** and the *piTab parameter is set to the index of that cursor. ** ** The returned value of this function indicates the b-tree type, as follows: ** @@ -107349,7 +111529,10 @@ static int sqlcipher_sqlite3InRhsIsConstant(Expr *pIn){ ** If the RHS of the IN operator is a list or a more complex subquery, then ** an ephemeral table might need to be generated from the RHS and then ** pX->iTable made to point to the ephemeral table instead of an -** existing table. +** existing table. In this case, the creation and initialization of the +** ephmeral table might be put inside of a subroutine, the EP_Subrtn flag +** will be set on pX and the pX->y.sub fields will be set to show where +** the subroutine is coded. ** ** The inFlags parameter must contain, at a minimum, one of the bits ** IN_INDEX_MEMBERSHIP or IN_INDEX_LOOP but not both. If inFlags contains @@ -107410,19 +111593,20 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FindInIndex( ){ Select *p; /* SELECT to the right of IN operator */ int eType = 0; /* Type of RHS table. IN_INDEX_* */ - int iTab = pParse->nTab++; /* Cursor of the RHS table */ + int iTab; /* Cursor of the RHS table */ int mustBeUnique; /* True if RHS must be unique */ Vdbe *v = sqlcipher_sqlite3GetVdbe(pParse); /* Virtual machine being coded */ assert( pX->op==TK_IN ); mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0; + iTab = pParse->nTab++; /* If the RHS of this IN(...) operator is a SELECT, and if it matters ** whether or not the SELECT result contains NULL values, check whether ** or not NULL is actually possible (it may not be, for example, due ** to NOT NULL constraints in the schema). If no NULL values are possible, ** set prRhsHasNull to 0 before continuing. */ - if( prRhsHasNull && (pX->flags & EP_xIsSelect) ){ + if( prRhsHasNull && ExprUseXSelect(pX) ){ int i; ExprList *pEList = pX->x.pSelect->pEList; for(i=0; inExpr; i++){ @@ -107450,7 +111634,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FindInIndex( /* Code an OP_Transaction and OP_TableLock for . */ iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); - assert( iDb>=0 && iDb=0 && iDbtnum, 0, pTab->zName); @@ -107578,9 +111762,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FindInIndex( */ if( eType==0 && (inFlags & IN_INDEX_NOOP_OK) - && !ExprHasProperty(pX, EP_xIsSelect) + && ExprUseXList(pX) && (!sqlcipher_sqlite3InRhsIsConstant(pX) || pX->x.pList->nExpr<=2) ){ + pParse->nTab--; /* Back out the allocation of the unused cursor */ + iTab = -1; /* Cursor is not allocated */ eType = IN_INDEX_NOOP; } @@ -107623,10 +111809,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FindInIndex( ** It is the responsibility of the caller to ensure that the returned ** string is eventually freed using sqlcipher_sqlite3DbFree(). */ -static char *exprINAffinity(Parse *pParse, Expr *pExpr){ +static char *exprINAffinity(Parse *pParse, const Expr *pExpr){ Expr *pLeft = pExpr->pLeft; int nVal = sqlcipher_sqlite3ExprVectorSize(pLeft); - Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0; + Select *pSelect = ExprUseXSelect(pExpr) ? pExpr->x.pSelect : 0; char *zRet; assert( pExpr->op==TK_IN ); @@ -107676,7 +111862,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3SubselectError(Parse *pParse, int nActual, */ SQLITE_PRIVATE void sqlcipher_sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ #ifndef SQLITE_OMIT_SUBQUERY - if( pExpr->flags & EP_xIsSelect ){ + if( ExprUseXSelect(pExpr) ){ sqlcipher_sqlite3SubselectError(pParse, pExpr->x.pSelect->pEList->nExpr, 1); }else #endif @@ -107740,24 +111926,26 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CodeRhsOfIN( */ if( ExprHasProperty(pExpr, EP_Subrtn) ){ addrOnce = sqlcipher_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ ExplainQueryPlan((pParse, 0, "REUSE LIST SUBQUERY %d", pExpr->x.pSelect->selId)); } + assert( ExprUseYSub(pExpr) ); sqlcipher_sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, pExpr->y.sub.iAddr); + assert( iTab!=pExpr->iTable ); sqlcipher_sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable); sqlcipher_sqlite3VdbeJumpHere(v, addrOnce); return; } /* Begin coding the subroutine */ + assert( !ExprUseYWin(pExpr) ); ExprSetProperty(pExpr, EP_Subrtn); assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); pExpr->y.sub.regReturn = ++pParse->nMem; pExpr->y.sub.iAddr = - sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; - VdbeComment((v, "return address")); + sqlcipher_sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1; addrOnce = sqlcipher_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); } @@ -107772,7 +111960,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CodeRhsOfIN( pExpr->iTable = iTab; addr = sqlcipher_sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, nVal); #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ VdbeComment((v, "Result of SELECT %u", pExpr->x.pSelect->selId)); }else{ VdbeComment((v, "RHS of IN operator")); @@ -107780,7 +111968,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CodeRhsOfIN( #endif pKeyInfo = sqlcipher_sqlite3KeyInfoAlloc(pParse->db, nVal, 1); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ /* Case 1: expr IN (SELECT ...) ** ** Generate code to write the results of the select into the temporary @@ -107795,19 +111983,23 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CodeRhsOfIN( /* If the LHS and RHS of the IN operator do not match, that ** error will have been caught long before we reach this point. */ if( ALWAYS(pEList->nExpr==nVal) ){ + Select *pCopy; SelectDest dest; int i; + int rc; sqlcipher_sqlite3SelectDestInit(&dest, SRT_Set, iTab); dest.zAffSdst = exprINAffinity(pParse, pExpr); pSelect->iLimit = 0; testcase( pSelect->selFlags & SF_Distinct ); testcase( pKeyInfo==0 ); /* Caused by OOM in sqlcipher_sqlite3KeyInfoAlloc() */ - if( sqlcipher_sqlite3Select(pParse, pSelect, &dest) ){ - sqlcipher_sqlite3DbFree(pParse->db, dest.zAffSdst); + pCopy = sqlcipher_sqlite3SelectDup(pParse->db, pSelect, 0); + rc = pParse->db->mallocFailed ? 1 :sqlcipher_sqlite3Select(pParse, pCopy, &dest); + sqlcipher_sqlite3SelectDelete(pParse->db, pCopy); + sqlcipher_sqlite3DbFree(pParse->db, dest.zAffSdst); + if( rc ){ sqlcipher_sqlite3KeyInfoUnref(pKeyInfo); return; } - sqlcipher_sqlite3DbFree(pParse->db, dest.zAffSdst); assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlcipher_sqlite3Select() */ assert( pEList!=0 ); assert( pEList->nExpr>0 ); @@ -107855,6 +112047,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CodeRhsOfIN( ** expression we need to rerun this code each time. */ if( addrOnce && !sqlcipher_sqlite3ExprIsConstant(pE2) ){ + sqlcipher_sqlite3VdbeChangeToNoop(v, addrOnce-1); sqlcipher_sqlite3VdbeChangeToNoop(v, addrOnce); ExprClearProperty(pExpr, EP_Subrtn); addrOnce = 0; @@ -107874,8 +112067,12 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CodeRhsOfIN( if( addrOnce ){ sqlcipher_sqlite3VdbeJumpHere(v, addrOnce); /* Subroutine return */ - sqlcipher_sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); - sqlcipher_sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlcipher_sqlite3VdbeCurrentAddr(v)-1); + assert( ExprUseYSub(pExpr) ); + assert( sqlcipher_sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn + || pParse->nErr ); + sqlcipher_sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn, + pExpr->y.sub.iAddr, 1); + VdbeCoverage(v); sqlcipher_sqlite3ClearTempRegCache(pParse); } } @@ -107906,12 +112103,31 @@ SQLITE_PRIVATE int sqlcipher_sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ Vdbe *v = pParse->pVdbe; assert( v!=0 ); + if( pParse->nErr ) return 0; testcase( pExpr->op==TK_EXISTS ); testcase( pExpr->op==TK_SELECT ); assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); - assert( ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( ExprUseXSelect(pExpr) ); pSel = pExpr->x.pSelect; + /* If this routine has already been coded, then invoke it as a + ** subroutine. */ + if( ExprHasProperty(pExpr, EP_Subrtn) ){ + ExplainQueryPlan((pParse, 0, "REUSE SUBQUERY %d", pSel->selId)); + assert( ExprUseYSub(pExpr) ); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, + pExpr->y.sub.iAddr); + return pExpr->iTable; + } + + /* Begin coding the subroutine */ + assert( !ExprUseYWin(pExpr) ); + assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) ); + ExprSetProperty(pExpr, EP_Subrtn); + pExpr->y.sub.regReturn = ++pParse->nMem; + pExpr->y.sub.iAddr = + sqlcipher_sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1; + /* The evaluation of the EXISTS/SELECT must be repeated every time it ** is encountered if any of the following is true: ** @@ -107923,22 +112139,6 @@ SQLITE_PRIVATE int sqlcipher_sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ ** save the results, and reuse the same result on subsequent invocations. */ if( !ExprHasProperty(pExpr, EP_VarSelect) ){ - /* If this routine has already been coded, then invoke it as a - ** subroutine. */ - if( ExprHasProperty(pExpr, EP_Subrtn) ){ - ExplainQueryPlan((pParse, 0, "REUSE SUBQUERY %d", pSel->selId)); - sqlcipher_sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, - pExpr->y.sub.iAddr); - return pExpr->iTable; - } - - /* Begin coding the subroutine */ - ExprSetProperty(pExpr, EP_Subrtn); - pExpr->y.sub.regReturn = ++pParse->nMem; - pExpr->y.sub.iAddr = - sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; - VdbeComment((v, "return address")); - addrOnce = sqlcipher_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); } @@ -107987,19 +112187,24 @@ SQLITE_PRIVATE int sqlcipher_sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ } pSel->iLimit = 0; if( sqlcipher_sqlite3Select(pParse, pSel, &dest) ){ + pExpr->op2 = pExpr->op; + pExpr->op = TK_ERROR; return 0; } pExpr->iTable = rReg = dest.iSDParm; ExprSetVVAProperty(pExpr, EP_NoReduce); if( addrOnce ){ sqlcipher_sqlite3VdbeJumpHere(v, addrOnce); - - /* Subroutine return */ - sqlcipher_sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); - sqlcipher_sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlcipher_sqlite3VdbeCurrentAddr(v)-1); - sqlcipher_sqlite3ClearTempRegCache(pParse); } + /* Subroutine return */ + assert( ExprUseYSub(pExpr) ); + assert( sqlcipher_sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn + || pParse->nErr ); + sqlcipher_sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn, + pExpr->y.sub.iAddr, 1); + VdbeCoverage(v); + sqlcipher_sqlite3ClearTempRegCache(pParse); return rReg; } #endif /* SQLITE_OMIT_SUBQUERY */ @@ -108013,7 +112218,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ */ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){ int nVector = sqlcipher_sqlite3ExprVectorSize(pIn->pLeft); - if( (pIn->flags & EP_xIsSelect) ){ + if( ExprUseXSelect(pIn) && !pParse->db->mallocFailed ){ if( nVector!=pIn->x.pSelect->pEList->nExpr ){ sqlcipher_sqlite3SubselectError(pParse, pIn->x.pSelect->pEList->nExpr, nVector); return 1; @@ -108147,13 +112352,15 @@ static void sqlcipher_sqlite3ExprCodeIN( ** This is step (1) in the in-operator.md optimized algorithm. */ if( eType==IN_INDEX_NOOP ){ - ExprList *pList = pExpr->x.pList; - CollSeq *pColl = sqlcipher_sqlite3ExprCollSeq(pParse, pExpr->pLeft); + ExprList *pList; + CollSeq *pColl; int labelOk = sqlcipher_sqlite3VdbeMakeLabel(pParse); int r2, regToFree; int regCkNull = 0; int ii; - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( ExprUseXList(pExpr) ); + pList = pExpr->x.pList; + pColl = sqlcipher_sqlite3ExprCollSeq(pParse, pExpr->pLeft); if( destIfNull!=destIfFalse ){ regCkNull = sqlcipher_sqlite3GetTempReg(pParse); sqlcipher_sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull); @@ -108201,9 +112408,9 @@ static void sqlcipher_sqlite3ExprCodeIN( }else{ destStep2 = destStep6 = sqlcipher_sqlite3VdbeMakeLabel(pParse); } - if( pParse->nErr ) goto sqlcipher_sqlite3ExprCodeIN_finished; for(i=0; ipLeft, i); + if( pParse->nErr ) goto sqlcipher_sqlite3ExprCodeIN_oom_error; if( sqlcipher_sqlite3ExprCanBeNull(p) ){ sqlcipher_sqlite3VdbeAddOp2(v, OP_IsNull, rLhs+i, destStep2); VdbeCoverage(v); @@ -108341,11 +112548,12 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ c = sqlcipher_sqlite3DecOrHexToI64(z, &value); if( (c==3 && !negFlag) || (c==2) || (negFlag && value==SMALLEST_INT64)){ #ifdef SQLITE_OMIT_FLOATING_POINT - sqlcipher_sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); + sqlcipher_sqlite3ErrorMsg(pParse, "oversized integer: %s%#T", negFlag?"-":"",pExpr); #else #ifndef SQLITE_OMIT_HEX_INTEGER if( sqlcipher_sqlite3_strnicmp(z,"0x",2)==0 ){ - sqlcipher_sqlite3ErrorMsg(pParse, "hex literal too big: %s%s", negFlag?"-":"",z); + sqlcipher_sqlite3ErrorMsg(pParse, "hex literal too big: %s%#T", + negFlag?"-":"",pExpr); }else #endif { @@ -108389,9 +112597,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprCodeLoadIndexColumn( ** and store the result in register regOut */ SQLITE_PRIVATE void sqlcipher_sqlite3ExprCodeGeneratedColumn( - Parse *pParse, - Column *pCol, - int regOut + Parse *pParse, /* Parsing context */ + Table *pTab, /* Table containing the generated column */ + Column *pCol, /* The generated column */ + int regOut /* Put the result in this register */ ){ int iAddr; Vdbe *v = pParse->pVdbe; @@ -108402,7 +112611,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprCodeGeneratedColumn( }else{ iAddr = 0; } - sqlcipher_sqlite3ExprCodeCopy(pParse, pCol->pDflt, regOut); + sqlcipher_sqlite3ExprCodeCopy(pParse, sqlcipher_sqlite3ColumnExpr(pTab,pCol), regOut); if( pCol->affinity>=SQLITE_AFF_TEXT ){ sqlcipher_sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1); } @@ -108428,6 +112637,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprCodeGetColumnOfTable( } if( iCol<0 || iCol==pTab->iPKey ){ sqlcipher_sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut); + VdbeComment((v, "%s.rowid", pTab->zName)); }else{ int op; int x; @@ -108438,12 +112648,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprCodeGetColumnOfTable( }else if( (pCol = &pTab->aCol[iCol])->colFlags & COLFLAG_VIRTUAL ){ Parse *pParse = sqlcipher_sqlite3VdbeParser(v); if( pCol->colFlags & COLFLAG_BUSY ){ - sqlcipher_sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pCol->zName); + sqlcipher_sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", + pCol->zCnName); }else{ int savedSelfTab = pParse->iSelfTab; pCol->colFlags |= COLFLAG_BUSY; pParse->iSelfTab = iTabCur+1; - sqlcipher_sqlite3ExprCodeGeneratedColumn(pParse, pCol, regOut); + sqlcipher_sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, regOut); pParse->iSelfTab = savedSelfTab; pCol->colFlags &= ~COLFLAG_BUSY; } @@ -108536,6 +112747,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ int i; iResult = pParse->nMem+1; pParse->nMem += nResult; + assert( ExprUseXList(p) ); for(i=0; ix.pList->a[i].pExpr, i+iResult); } @@ -108596,7 +112808,17 @@ static int exprCodeInlineFunction( caseExpr.x.pList = pFarg; return sqlcipher_sqlite3ExprCodeTarget(pParse, &caseExpr, target); } - +#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC + case INLINEFUNC_sqlite_offset: { + Expr *pArg = pFarg->a[0].pExpr; + if( pArg->op==TK_COLUMN && pArg->iTable>=0 ){ + sqlcipher_sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target); + }else{ + sqlcipher_sqlite3VdbeAddOp2(v, OP_Null, 0, target); + } + break; + } +#endif default: { /* The UNLIKELY() function is a no-op. The result is the value ** of the first argument. @@ -108610,6 +112832,7 @@ static int exprCodeInlineFunction( ** Test-only SQL functions that are only usable if enabled ** via SQLITE_TESTCTRL_INTERNAL_FUNCTIONS */ +#if !defined(SQLITE_UNTESTABLE) case INLINEFUNC_expr_compare: { /* Compare two expressions using sqlcipher_sqlite3ExprCompare() */ assert( nFarg==2 ); @@ -108643,7 +112866,6 @@ static int exprCodeInlineFunction( break; } -#ifdef SQLITE_DEBUG case INLINEFUNC_affinity: { /* The AFFINITY() function evaluates to a string that describes ** the type affinity of the argument. This is used for testing of @@ -108657,7 +112879,7 @@ static int exprCodeInlineFunction( (aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]); break; } -#endif +#endif /* !defined(SQLITE_UNTESTABLE) */ } return target; } @@ -108711,7 +112933,8 @@ expr_code_doover: if( pCol->iColumn<0 ){ VdbeComment((v,"%s.rowid",pTab->zName)); }else{ - VdbeComment((v,"%s.%s",pTab->zName,pTab->aCol[pCol->iColumn].zName)); + VdbeComment((v,"%s.%s", + pTab->zName, pTab->aCol[pCol->iColumn].zCnName)); if( pTab->aCol[pCol->iColumn].affinity==SQLITE_AFF_REAL ){ sqlcipher_sqlite3VdbeAddOp1(v, OP_RealAffinity, target); } @@ -108733,6 +112956,7 @@ expr_code_doover: */ int aff; iReg = sqlcipher_sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target); + assert( ExprUseYTab(pExpr) ); if( pExpr->y.pTab ){ aff = sqlcipher_sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); }else{ @@ -108756,9 +112980,11 @@ expr_code_doover: ** immediately prior to the first column. */ Column *pCol; - Table *pTab = pExpr->y.pTab; + Table *pTab; int iSrc; int iCol = pExpr->iColumn; + assert( ExprUseYTab(pExpr) ); + pTab = pExpr->y.pTab; assert( pTab!=0 ); assert( iCol>=XN_ROWID ); assert( iColnCol ); @@ -108772,12 +112998,12 @@ expr_code_doover: if( pCol->colFlags & COLFLAG_GENERATED ){ if( pCol->colFlags & COLFLAG_BUSY ){ sqlcipher_sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", - pCol->zName); + pCol->zCnName); return 0; } pCol->colFlags |= COLFLAG_BUSY; if( pCol->colFlags & COLFLAG_NOTAVAIL ){ - sqlcipher_sqlite3ExprCodeGeneratedColumn(pParse, pCol, iSrc); + sqlcipher_sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, iSrc); } pCol->colFlags &= ~(COLFLAG_BUSY|COLFLAG_NOTAVAIL); return iSrc; @@ -108796,6 +113022,7 @@ expr_code_doover: iTab = pParse->iSelfTab - 1; } } + assert( ExprUseYTab(pExpr) ); iReg = sqlcipher_sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab, pExpr->iColumn, iTab, target, pExpr->op2); @@ -108829,7 +113056,7 @@ expr_code_doover: ** Expr node to be passed into this function, it will be handled ** sanely and not crash. But keep the assert() to bring the problem ** to the attention of the developers. */ - assert( op==TK_NULL ); + assert( op==TK_NULL || op==TK_ERROR || pParse->db->mallocFailed ); sqlcipher_sqlite3VdbeAddOp2(v, OP_Null, 0, target); return target; } @@ -108873,6 +113100,7 @@ expr_code_doover: sqlcipher_sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); inReg = target; } + assert( !ExprHasProperty(pExpr, EP_IntValue) ); sqlcipher_sqlite3VdbeAddOp2(v, OP_Cast, target, sqlcipher_sqlite3AffinityType(pExpr->u.zToken, 0)); return inReg; @@ -108895,8 +113123,9 @@ expr_code_doover: }else{ r1 = sqlcipher_sqlite3ExprCodeTemp(pParse, pLeft, ®Free1); r2 = sqlcipher_sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); - codeCompare(pParse, pLeft, pExpr->pRight, op, - r1, r2, inReg, SQLITE_STOREP2 | p5, + sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 1, inReg); + codeCompare(pParse, pLeft, pExpr->pRight, op, r1, r2, + sqlcipher_sqlite3VdbeCurrentAddr(v)+2, p5, ExprHasProperty(pExpr,EP_Commuted)); assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); @@ -108904,6 +113133,11 @@ expr_code_doover: assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); + if( p5==SQLITE_NULLEQ ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 0, inReg); + }else{ + sqlcipher_sqlite3VdbeAddOp3(v, OP_ZeroOrNull, r1, inReg, r2); + } testcase( regFree1==0 ); testcase( regFree2==0 ); } @@ -109006,7 +113240,7 @@ expr_code_doover: || NEVER(pExpr->iAgg>=pInfo->nFunc) ){ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlcipher_sqlite3ErrorMsg(pParse, "misuse of aggregate: %s()", pExpr->u.zToken); + sqlcipher_sqlite3ErrorMsg(pParse, "misuse of aggregate: %#T()", pExpr); }else{ return pInfo->aFunc[pExpr->iAgg].iMem; } @@ -109034,8 +113268,8 @@ expr_code_doover: ** multiple times if we know they always give the same result */ return sqlcipher_sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); } - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); assert( !ExprHasProperty(pExpr, EP_TokenOnly) ); + assert( ExprUseXList(pExpr) ); pFarg = pExpr->x.pList; nFarg = pFarg ? pFarg->nExpr : 0; assert( !ExprHasProperty(pExpr, EP_IntValue) ); @@ -109047,7 +113281,7 @@ expr_code_doover: } #endif if( pDef==0 || pDef->xFinalize!=0 ){ - sqlcipher_sqlite3ErrorMsg(pParse, "unknown function: %s()", zId); + sqlcipher_sqlite3ErrorMsg(pParse, "unknown function: %#T()", pExpr); break; } if( pDef->funcFlags & SQLITE_FUNC_INLINE ){ @@ -109123,20 +113357,8 @@ expr_code_doover: if( !pColl ) pColl = db->pDfltColl; sqlcipher_sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ); } -#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC - if( pDef->funcFlags & SQLITE_FUNC_OFFSET ){ - Expr *pArg = pFarg->a[0].pExpr; - if( pArg->op==TK_COLUMN ){ - sqlcipher_sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target); - }else{ - sqlcipher_sqlite3VdbeAddOp2(v, OP_Null, 0, target); - } - }else -#endif - { - sqlcipher_sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg, - pDef, pExpr->op2); - } + sqlcipher_sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg, + pDef, pExpr->op2); if( nFarg ){ if( constMask==0 ){ sqlcipher_sqlite3ReleaseTempRange(pParse, r1, nFarg); @@ -109154,7 +113376,10 @@ expr_code_doover: testcase( op==TK_SELECT ); if( pParse->db->mallocFailed ){ return 0; - }else if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){ + }else if( op==TK_SELECT + && ALWAYS( ExprUseXSelect(pExpr) ) + && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 + ){ sqlcipher_sqlite3SubselectError(pParse, nCol, 1); }else{ return sqlcipher_sqlite3CodeSubselect(pParse, pExpr); @@ -109163,17 +113388,18 @@ expr_code_doover: } case TK_SELECT_COLUMN: { int n; - if( pExpr->pLeft->iTable==0 ){ - pExpr->pLeft->iTable = sqlcipher_sqlite3CodeSubselect(pParse, pExpr->pLeft); + Expr *pLeft = pExpr->pLeft; + if( pLeft->iTable==0 || pParse->withinRJSubrtn > pLeft->op2 ){ + pLeft->iTable = sqlcipher_sqlite3CodeSubselect(pParse, pLeft); + pLeft->op2 = pParse->withinRJSubrtn; } - assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT ); - if( pExpr->iTable!=0 - && pExpr->iTable!=(n = sqlcipher_sqlite3ExprVectorSize(pExpr->pLeft)) - ){ + assert( pLeft->op==TK_SELECT || pLeft->op==TK_ERROR ); + n = sqlcipher_sqlite3ExprVectorSize(pLeft); + if( pExpr->iTable!=n ){ sqlcipher_sqlite3ErrorMsg(pParse, "%d columns assigned %d values", pExpr->iTable, n); } - return pExpr->pLeft->iTable + pExpr->iColumn; + return pLeft->iTable + pExpr->iColumn; } case TK_IN: { int destIfFalse = sqlcipher_sqlite3VdbeMakeLabel(pParse); @@ -109204,8 +113430,24 @@ expr_code_doover: exprCodeBetween(pParse, pExpr, target, 0, 0); return target; } + case TK_COLLATE: { + if( !ExprHasProperty(pExpr, EP_Collate) + && ALWAYS(pExpr->pLeft) + && pExpr->pLeft->op==TK_FUNCTION + ){ + inReg = sqlcipher_sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); + if( inReg!=target ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); + inReg = target; + } + sqlcipher_sqlite3VdbeAddOp1(v, OP_ClrSubtype, inReg); + return inReg; + }else{ + pExpr = pExpr->pLeft; + goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. */ + } + } case TK_SPAN: - case TK_COLLATE: case TK_UPLUS: { pExpr = pExpr->pLeft; goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. OSSFuzz. */ @@ -109237,9 +113479,14 @@ expr_code_doover: ** p1==1 -> old.a p1==4 -> new.a ** p1==2 -> old.b p1==5 -> new.b */ - Table *pTab = pExpr->y.pTab; - int iCol = pExpr->iColumn; - int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + Table *pTab; + int iCol; + int p1; + + assert( ExprUseYTab(pExpr) ); + pTab = pExpr->y.pTab; + iCol = pExpr->iColumn; + p1 = pExpr->iTable * (pTab->nCol+1) + 1 + sqlcipher_sqlite3TableColumnToStorage(pTab, iCol); assert( pExpr->iTable==0 || pExpr->iTable==1 ); @@ -109250,7 +113497,7 @@ expr_code_doover: sqlcipher_sqlite3VdbeAddOp2(v, OP_Param, p1, target); VdbeComment((v, "r[%d]=%s.%s", target, (pExpr->iTable ? "new" : "old"), - (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[iCol].zName) + (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[iCol].zCnName) )); #ifndef SQLITE_OMIT_FLOATING_POINT @@ -109327,7 +113574,7 @@ expr_code_doover: Expr *pDel = 0; sqlcipher_sqlite3 *db = pParse->db; - assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList ); + assert( ExprUseXList(pExpr) && pExpr->x.pList!=0 ); assert(pExpr->x.pList->nExpr > 0); pEList = pExpr->x.pList; aListelem = pEList->a; @@ -109441,7 +113688,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCodeRunJustOnce( struct ExprList_item *pItem; int i; for(pItem=p->a, i=p->nExpr; i>0; pItem++, i--){ - if( pItem->reusable && sqlcipher_sqlite3ExprCompare(0,pItem->pExpr,pExpr,-1)==0 ){ + if( pItem->fg.reusable + && sqlcipher_sqlite3ExprCompare(0,pItem->pExpr,pExpr,-1)==0 + ){ return pItem->u.iConstExprReg; } } @@ -109464,7 +113713,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCodeRunJustOnce( p = sqlcipher_sqlite3ExprListAppend(pParse, p, pExpr); if( p ){ struct ExprList_item *pItem = &p->a[p->nExpr-1]; - pItem->reusable = regDest<0; + pItem->fg.reusable = regDest<0; if( regDest<0 ) regDest = ++pParse->nMem; pItem->u.iConstExprReg = regDest; } @@ -109524,7 +113773,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprCode(Parse *pParse, Expr *pExpr, int ta inReg = sqlcipher_sqlite3ExprCodeTarget(pParse, pExpr, target); if( inReg!=target ){ u8 op; - if( ExprHasProperty(pExpr,EP_Subquery) ){ + if( ALWAYS(pExpr) && ExprHasProperty(pExpr,EP_Subquery) ){ op = OP_Copy; }else{ op = OP_SCopy; @@ -109598,7 +113847,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCodeExprList( for(pItem=pList->a, i=0; ipExpr; #ifdef SQLITE_ENABLE_SORTER_REFERENCES - if( pItem->bSorterRef ){ + if( pItem->fg.bSorterRef ){ i--; n--; }else @@ -109672,7 +113921,7 @@ static void exprCodeBetween( memset(&compRight, 0, sizeof(Expr)); memset(&exprAnd, 0, sizeof(Expr)); - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( ExprUseXList(pExpr) ); pDel = sqlcipher_sqlite3ExprDup(db, pExpr->pLeft, 0); if( db->mallocFailed==0 ){ exprAnd.op = TK_AND; @@ -109692,8 +113941,8 @@ static void exprCodeBetween( ** so that the sqlcipher_sqlite3ExprCodeTarget() routine will not attempt to move ** it into the Parse.pConstExpr list. We should use a new bit for this, ** for clarity, but we are out of bits in the Expr.flags field so we - ** have to reuse the EP_FromJoin bit. Bummer. */ - pDel->flags |= EP_FromJoin; + ** have to reuse the EP_OuterON bit. Bummer. */ + pDel->flags |= EP_OuterON; sqlcipher_sqlite3ExprCodeTarget(pParse, &exprAnd, dest); } sqlcipher_sqlite3ReleaseTempReg(pParse, regFree1); @@ -110062,7 +114311,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ExprIfFalseDup(Parse *pParse, Expr *pExpr, ** Otherwise, if the values are not the same or if pExpr is not a simple ** SQL value, zero is returned. */ -static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ +static int exprCompareVariable( + const Parse *pParse, + const Expr *pVar, + const Expr *pExpr +){ int res = 0; int iVar; sqlcipher_sqlite3_value *pL, *pR = 0; @@ -110114,7 +114367,12 @@ static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ ** Argument pParse should normally be NULL. If it is not NULL and pA or ** pB causes a return value of 2. */ -SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){ +SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompare( + const Parse *pParse, + const Expr *pA, + const Expr *pB, + int iTab +){ u32 combinedFlags; if( pA==0 || pB==0 ){ return pB==pA ? 0 : 2; @@ -110138,7 +114396,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *p } return 2; } - if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){ + assert( !ExprHasProperty(pA, EP_IntValue) ); + assert( !ExprHasProperty(pB, EP_IntValue) ); + if( pA->u.zToken ){ if( pA->op==TK_FUNCTION || pA->op==TK_AGG_FUNCTION ){ if( sqlcipher_sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; #ifndef SQLITE_OMIT_WINDOWFUNC @@ -110156,7 +114416,12 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *p return 0; }else if( pA->op==TK_COLLATE ){ if( sqlcipher_sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; - }else if( ALWAYS(pB->u.zToken!=0) && strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ + }else + if( pB->u.zToken!=0 + && pA->op!=TK_COLUMN + && pA->op!=TK_AGG_COLUMN + && strcmp(pA->u.zToken,pB->u.zToken)!=0 + ){ return 2; } } @@ -110198,7 +114463,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *p ** Two NULL pointers are considered to be the same. But a NULL pointer ** always differs from a non-NULL pointer. */ -SQLITE_PRIVATE int sqlcipher_sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){ +SQLITE_PRIVATE int sqlcipher_sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB, int iTab){ int i; if( pA==0 && pB==0 ) return 0; if( pA==0 || pB==0 ) return 1; @@ -110207,7 +114472,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int res; Expr *pExprA = pA->a[i].pExpr; Expr *pExprB = pB->a[i].pExpr; - if( pA->a[i].sortFlags!=pB->a[i].sortFlags ) return 1; + if( pA->a[i].fg.sortFlags!=pB->a[i].fg.sortFlags ) return 1; if( (res = sqlcipher_sqlite3ExprCompare(0, pExprA, pExprB, iTab)) ) return res; } return 0; @@ -110217,7 +114482,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprListCompare(ExprList *pA, ExprList *pB, ** Like sqlcipher_sqlite3ExprCompare() except COLLATE operators at the top-level ** are ignored. */ -SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){ +SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){ return sqlcipher_sqlite3ExprCompare(0, sqlcipher_sqlite3ExprSkipCollateAndLikely(pA), sqlcipher_sqlite3ExprSkipCollateAndLikely(pB), @@ -110231,9 +114496,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab ** non-NULL if pNN is not NULL */ static int exprImpliesNotNull( - Parse *pParse, /* Parsing context */ - Expr *p, /* The expression to be checked */ - Expr *pNN, /* The expression that is NOT NULL */ + const Parse *pParse,/* Parsing context */ + const Expr *p, /* The expression to be checked */ + const Expr *pNN, /* The expression that is NOT NULL */ int iTab, /* Table being evaluated */ int seenNot /* Return true only if p can be any non-NULL value */ ){ @@ -110245,12 +114510,13 @@ static int exprImpliesNotNull( switch( p->op ){ case TK_IN: { if( seenNot && ExprHasProperty(p, EP_xIsSelect) ) return 0; - assert( ExprHasProperty(p,EP_xIsSelect) - || (p->x.pList!=0 && p->x.pList->nExpr>0) ); + assert( ExprUseXSelect(p) || (p->x.pList!=0 && p->x.pList->nExpr>0) ); return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1); } case TK_BETWEEN: { - ExprList *pList = p->x.pList; + ExprList *pList; + assert( ExprUseXList(p) ); + pList = p->x.pList; assert( pList!=0 ); assert( pList->nExpr==2 ); if( seenNot ) return 0; @@ -110326,7 +114592,12 @@ static int exprImpliesNotNull( ** improvement. Returning false might cause a performance reduction, but ** it will always give the correct answer and is hence always safe. */ -SQLITE_PRIVATE int sqlcipher_sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, int iTab){ +SQLITE_PRIVATE int sqlcipher_sqlite3ExprImpliesExpr( + const Parse *pParse, + const Expr *pE1, + const Expr *pE2, + int iTab +){ if( sqlcipher_sqlite3ExprCompare(pParse, pE1, pE2, iTab)==0 ){ return 1; } @@ -110356,7 +114627,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Ex static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_AGG_COLUMN ); testcase( pExpr->op==TK_AGG_FUNCTION ); - if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune; + if( ExprHasProperty(pExpr, EP_OuterON) ) return WRC_Prune; switch( pExpr->op ){ case TK_ISNOT: case TK_ISNULL: @@ -110422,10 +114693,14 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_GE ); /* The y.pTab=0 assignment in wherecode.c always happens after the ** impliesNotNullRow() test */ - if( (pLeft->op==TK_COLUMN && ALWAYS(pLeft->y.pTab!=0) - && IsVirtual(pLeft->y.pTab)) - || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) - && IsVirtual(pRight->y.pTab)) + assert( pLeft->op!=TK_COLUMN || ExprUseYTab(pLeft) ); + assert( pRight->op!=TK_COLUMN || ExprUseYTab(pRight) ); + if( (pLeft->op==TK_COLUMN + && pLeft->y.pTab!=0 + && IsVirtual(pLeft->y.pTab)) + || (pRight->op==TK_COLUMN + && pRight->y.pTab!=0 + && IsVirtual(pRight->y.pTab)) ){ return WRC_Prune; } @@ -110449,8 +114724,8 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ ** False positives are not allowed, however. A false positive may result ** in an incorrect answer. ** -** Terms of p that are marked with EP_FromJoin (and hence that come from -** the ON or USING clauses of LEFT JOINS) are excluded from the analysis. +** Terms of p that are marked with EP_OuterON (and hence that come from +** the ON or USING clauses of OUTER JOINS) are excluded from the analysis. ** ** This routine is used to check if a LEFT JOIN can be converted into ** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE @@ -110534,88 +114809,125 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprCoveredByIndex( } -/* -** An instance of the following structure is used by the tree walker -** to count references to table columns in the arguments of an -** aggregate function, in order to implement the -** sqlcipher_sqlite3FunctionThisSrc() routine. -*/ -struct SrcCount { - SrcList *pSrc; /* One particular FROM clause in a nested query */ - int iSrcInner; /* Smallest cursor number in this context */ - int nThis; /* Number of references to columns in pSrcList */ - int nOther; /* Number of references to columns in other FROM clauses */ +/* Structure used to pass information throught the Walker in order to +** implement sqlcipher_sqlite3ReferencesSrcList(). +*/ +struct RefSrcList { + sqlcipher_sqlite3 *db; /* Database connection used for sqlcipher_sqlite3DbRealloc() */ + SrcList *pRef; /* Looking for references to these tables */ + i64 nExclude; /* Number of tables to exclude from the search */ + int *aiExclude; /* Cursor IDs for tables to exclude from the search */ }; /* -** xSelect callback for sqlcipher_sqlite3FunctionUsesThisSrc(). If this is the first -** SELECT with a FROM clause encountered during this iteration, set -** SrcCount.iSrcInner to the cursor number of the leftmost object in -** the FROM cause. +** Walker SELECT callbacks for sqlcipher_sqlite3ReferencesSrcList(). +** +** When entering a new subquery on the pExpr argument, add all FROM clause +** entries for that subquery to the exclude list. +** +** When leaving the subquery, remove those entries from the exclude list. */ -static int selectSrcCount(Walker *pWalker, Select *pSel){ - struct SrcCount *p = pWalker->u.pSrcCount; - if( p->iSrcInner==0x7FFFFFFF && ALWAYS(pSel->pSrc) && pSel->pSrc->nSrc ){ - pWalker->u.pSrcCount->iSrcInner = pSel->pSrc->a[0].iCursor; +static int selectRefEnter(Walker *pWalker, Select *pSelect){ + struct RefSrcList *p = pWalker->u.pRefSrcList; + SrcList *pSrc = pSelect->pSrc; + i64 i, j; + int *piNew; + if( pSrc->nSrc==0 ) return WRC_Continue; + j = p->nExclude; + p->nExclude += pSrc->nSrc; + piNew = sqlcipher_sqlite3DbRealloc(p->db, p->aiExclude, p->nExclude*sizeof(int)); + if( piNew==0 ){ + p->nExclude = 0; + return WRC_Abort; + }else{ + p->aiExclude = piNew; + } + for(i=0; inSrc; i++, j++){ + p->aiExclude[j] = pSrc->a[i].iCursor; } return WRC_Continue; } +static void selectRefLeave(Walker *pWalker, Select *pSelect){ + struct RefSrcList *p = pWalker->u.pRefSrcList; + SrcList *pSrc = pSelect->pSrc; + if( p->nExclude ){ + assert( p->nExclude>=pSrc->nSrc ); + p->nExclude -= pSrc->nSrc; + } +} -/* -** Count the number of references to columns. +/* This is the Walker EXPR callback for sqlcipher_sqlite3ReferencesSrcList(). +** +** Set the 0x01 bit of pWalker->eCode if there is a reference to any +** of the tables shown in RefSrcList.pRef. +** +** Set the 0x02 bit of pWalker->eCode if there is a reference to a +** table is in neither RefSrcList.pRef nor RefSrcList.aiExclude. */ -static int exprSrcCount(Walker *pWalker, Expr *pExpr){ - /* There was once a NEVER() on the second term on the grounds that - ** sqlcipher_sqlite3FunctionUsesThisSrc() was always called before - ** sqlcipher_sqlite3ExprAnalyzeAggregates() and so the TK_COLUMNs have not yet - ** been converted into TK_AGG_COLUMN. But this is no longer true due - ** to window functions - sqlcipher_sqlite3WindowRewrite() may now indirectly call - ** FunctionUsesThisSrc() when creating a new sub-select. */ - if( pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN ){ +static int exprRefToSrcList(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_COLUMN + || pExpr->op==TK_AGG_COLUMN + ){ int i; - struct SrcCount *p = pWalker->u.pSrcCount; - SrcList *pSrc = p->pSrc; + struct RefSrcList *p = pWalker->u.pRefSrcList; + SrcList *pSrc = p->pRef; int nSrc = pSrc ? pSrc->nSrc : 0; for(i=0; iiTable==pSrc->a[i].iCursor ) break; + if( pExpr->iTable==pSrc->a[i].iCursor ){ + pWalker->eCode |= 1; + return WRC_Continue; + } } - if( inThis++; - }else if( pExpr->iTableiSrcInner ){ - /* In a well-formed parse tree (no name resolution errors), - ** TK_COLUMN nodes with smaller Expr.iTable values are in an - ** outer context. Those are the only ones to count as "other" */ - p->nOther++; + for(i=0; inExclude && p->aiExclude[i]!=pExpr->iTable; i++){} + if( i>=p->nExclude ){ + pWalker->eCode |= 2; } } return WRC_Continue; } /* -** Determine if any of the arguments to the pExpr Function reference -** pSrcList. Return true if they do. Also return true if the function -** has no arguments or has only constant arguments. Return false if pExpr -** references columns but not columns of tables found in pSrcList. +** Check to see if pExpr references any tables in pSrcList. +** Possible return values: +** +** 1 pExpr does references a table in pSrcList. +** +** 0 pExpr references some table that is not defined in either +** pSrcList or in subqueries of pExpr itself. +** +** -1 pExpr only references no tables at all, or it only +** references tables defined in subqueries of pExpr itself. +** +** As currently used, pExpr is always an aggregate function call. That +** fact is exploited for efficiency. */ -SQLITE_PRIVATE int sqlcipher_sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ +SQLITE_PRIVATE int sqlcipher_sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList *pSrcList){ Walker w; - struct SrcCount cnt; - assert( pExpr->op==TK_AGG_FUNCTION ); + struct RefSrcList x; memset(&w, 0, sizeof(w)); - w.xExprCallback = exprSrcCount; - w.xSelectCallback = selectSrcCount; - w.u.pSrcCount = &cnt; - cnt.pSrc = pSrcList; - cnt.iSrcInner = (pSrcList&&pSrcList->nSrc)?pSrcList->a[0].iCursor:0x7FFFFFFF; - cnt.nThis = 0; - cnt.nOther = 0; + memset(&x, 0, sizeof(x)); + w.xExprCallback = exprRefToSrcList; + w.xSelectCallback = selectRefEnter; + w.xSelectCallback2 = selectRefLeave; + w.u.pRefSrcList = &x; + x.db = pParse->db; + x.pRef = pSrcList; + assert( pExpr->op==TK_AGG_FUNCTION ); + assert( ExprUseXList(pExpr) ); sqlcipher_sqlite3WalkExprList(&w, pExpr->x.pList); #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(pExpr, EP_WinFunc) ){ sqlcipher_sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter); } #endif - return cnt.nThis>0 || cnt.nOther==0; + sqlcipher_sqlite3DbFree(pParse->db, x.aiExclude); + if( w.eCode & 0x01 ){ + return 1; + }else if( w.eCode ){ + return 0; + }else{ + return -1; + } } /* @@ -110646,8 +114958,7 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ pExpr = sqlcipher_sqlite3ExprDup(db, pExpr, 0); if( pExpr ){ pAggInfo->aCol[iAgg].pCExpr = pExpr; - pParse->pConstExpr = - sqlcipher_sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); + sqlcipher_sqlite3ExprDeferredDelete(pParse, pExpr); } } }else{ @@ -110656,8 +114967,7 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ pExpr = sqlcipher_sqlite3ExprDup(db, pExpr, 0); if( pExpr ){ pAggInfo->aFunc[iAgg].pFExpr = pExpr; - pParse->pConstExpr = - sqlcipher_sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); + sqlcipher_sqlite3ExprDeferredDelete(pParse, pExpr); } } } @@ -110729,7 +115039,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ /* Check to see if the column is in one of the tables in the FROM ** clause of the aggregate query */ if( ALWAYS(pSrcList!=0) ){ - struct SrcList_item *pItem = pSrcList->a; + SrcItem *pItem = pSrcList->a; for(i=0; inSrc; i++, pItem++){ struct AggInfo_col *pCol; assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); @@ -110752,6 +115062,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ && (k = addAggInfoColumn(pParse->db, pAggInfo))>=0 ){ pCol = &pAggInfo->aCol[k]; + assert( ExprUseYTab(pExpr) ); pCol->pTab = pExpr->y.pTab; pCol->iTable = pExpr->iTable; pCol->iColumn = pExpr->iColumn; @@ -110800,6 +115111,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ */ struct AggInfo_func *pItem = pAggInfo->aFunc; for(i=0; inFunc; i++, pItem++){ + if( pItem->pFExpr==pExpr ) break; if( sqlcipher_sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){ break; } @@ -110814,7 +115126,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ pItem = &pAggInfo->aFunc[i]; pItem->pFExpr = pExpr; pItem->iMem = ++pParse->nMem; - assert( !ExprHasProperty(pExpr, EP_IntValue) ); + assert( ExprUseUToken(pExpr) ); pItem->pFunc = sqlcipher_sqlite3FindFunction(pParse->db, pExpr->u.zToken, pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0); @@ -111000,6 +115312,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3NoTempsInRange(Parse *pParse, int iFirst, in static int isAlterableTable(Parse *pParse, Table *pTab){ if( 0==sqlcipher_sqlite3StrNICmp(pTab->zName, "sqlite_", 7) #ifndef SQLITE_OMIT_VIRTUALTABLE + || (pTab->tabFlags & TF_Eponymous)!=0 || ( (pTab->tabFlags & TF_Shadow)!=0 && sqlcipher_sqlite3ReadOnlyShadowTables(pParse->db) ) @@ -111018,25 +115331,56 @@ static int isAlterableTable(Parse *pParse, Table *pTab){ ** statement to ensure that the operation has not rendered any schema ** objects unusable. */ -static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){ +static void renameTestSchema( + Parse *pParse, /* Parse context */ + const char *zDb, /* Name of db to verify schema of */ + int bTemp, /* True if this is the temp db */ + const char *zWhen, /* "when" part of error message */ + int bNoDQS /* Do not allow DQS in the schema */ +){ + pParse->colNamesSet = 1; sqlcipher_sqlite3NestedParse(pParse, "SELECT 1 " - "FROM \"%w\"." DFLT_SCHEMA_TABLE " " + "FROM \"%w\"." LEGACY_SCHEMA_TABLE " " "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" " AND sql NOT LIKE 'create virtual%%'" - " AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ", + " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %d)=NULL ", zDb, - zDb, bTemp + zDb, bTemp, zWhen, bNoDQS ); if( bTemp==0 ){ sqlcipher_sqlite3NestedParse(pParse, "SELECT 1 " - "FROM temp." DFLT_SCHEMA_TABLE " " + "FROM temp." LEGACY_SCHEMA_TABLE " " "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" " AND sql NOT LIKE 'create virtual%%'" - " AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ", - zDb + " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %d)=NULL ", + zDb, zWhen, bNoDQS + ); + } +} + +/* +** Generate VM code to replace any double-quoted strings (but not double-quoted +** identifiers) within the "sql" column of the sqlite_schema table in +** database zDb with their single-quoted equivalents. If argument bTemp is +** not true, similarly update all SQL statements in the sqlite_schema table +** of the temp db. +*/ +static void renameFixQuotes(Parse *pParse, const char *zDb, int bTemp){ + sqlcipher_sqlite3NestedParse(pParse, + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE + " SET sql = sqlite_rename_quotefix(%Q, sql)" + "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" + " AND sql NOT LIKE 'create virtual%%'" , zDb, zDb + ); + if( bTemp==0 ){ + sqlcipher_sqlite3NestedParse(pParse, + "UPDATE temp." LEGACY_SCHEMA_TABLE + " SET sql = sqlite_rename_quotefix('temp', sql)" + "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" + " AND sql NOT LIKE 'create virtual%%'" ); } } @@ -111045,12 +115389,12 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){ ** Generate code to reload the schema for database iDb. And, if iDb!=1, for ** the temp database as well. */ -static void renameReloadSchema(Parse *pParse, int iDb){ +static void renameReloadSchema(Parse *pParse, int iDb, u16 p5){ Vdbe *v = pParse->pVdbe; if( v ){ sqlcipher_sqlite3ChangeCookie(pParse, iDb); - sqlcipher_sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0); - if( iDb!=1 ) sqlcipher_sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0); + sqlcipher_sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0, p5); + if( iDb!=1 ) sqlcipher_sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0, p5); } } @@ -111072,9 +115416,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterRenameTable( const char *zTabName; /* Original name of the table */ Vdbe *v; VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ - u32 savedDbFlags; /* Saved value of db->mDbFlags */ - savedDbFlags = db->mDbFlags; if( NEVER(db->mallocFailed) ) goto exit_rename_table; assert( pSrc->nSrc==1 ); assert( sqlcipher_sqlite3BtreeHoldsAllMutexes(pParse->db) ); @@ -111083,7 +115425,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterRenameTable( if( !pTab ) goto exit_rename_table; iDb = sqlcipher_sqlite3SchemaToIndex(pParse->db, pTab->pSchema); zDb = db->aDb[iDb].zDbSName; - db->mDbFlags |= DBFLAG_PreferBuiltin; /* Get a NULL terminated version of the new table name. */ zName = sqlcipher_sqlite3NameFromToken(db, pName); @@ -111112,7 +115453,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterRenameTable( } #ifndef SQLITE_OMIT_VIEW - if( pTab->pSelect ){ + if( IsView(pTab) ){ sqlcipher_sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName); goto exit_rename_table; } @@ -111154,7 +115495,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterRenameTable( /* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in ** the schema to use the new table name. */ sqlcipher_sqlite3NestedParse(pParse, - "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) " "WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)" "AND name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" @@ -111164,7 +115505,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterRenameTable( /* Update the tbl_name and name columns of the sqlite_schema table ** as required. */ sqlcipher_sqlite3NestedParse(pParse, - "UPDATE %Q." DFLT_SCHEMA_TABLE " SET " + "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET " "tbl_name = %Q, " "name = CASE " "WHEN type='table' THEN %Q " @@ -111199,7 +115540,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterRenameTable( "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), " "tbl_name = " "CASE WHEN tbl_name=%Q COLLATE nocase AND " - " sqlite_rename_test(%Q, sql, type, name, 1) " + " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename', 0) " "THEN %Q ELSE tbl_name END " "WHERE type IN ('view', 'trigger')" , zDb, zTabName, zName, zTabName, zDb, zName); @@ -111218,13 +115559,12 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterRenameTable( } #endif - renameReloadSchema(pParse, iDb); - renameTestSchema(pParse, zDb, iDb==1); + renameReloadSchema(pParse, iDb, INITFLAG_AlterRename); + renameTestSchema(pParse, zDb, iDb==1, "after rename", 0); exit_rename_table: sqlcipher_sqlite3SrcListDelete(db, pSrc); sqlcipher_sqlite3DbFree(db, zName); - db->mDbFlags = savedDbFlags; } /* @@ -111265,7 +115605,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterFinishAddColumn(Parse *pParse, Token * int r1; /* Temporary registers */ db = pParse->db; - if( pParse->nErr || db->mallocFailed ) return; + assert( db->pParse==pParse ); + if( pParse->nErr ) return; + assert( db->mallocFailed==0 ); pNew = pParse->pNewTable; assert( pNew ); @@ -111274,7 +115616,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterFinishAddColumn(Parse *pParse, Token * zDb = db->aDb[iDb].zDbSName; zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */ pCol = &pNew->aCol[pNew->nCol-1]; - pDflt = pCol->pDflt; + pDflt = sqlcipher_sqlite3ColumnExpr(pNew, pCol); pTab = sqlcipher_sqlite3FindTable(db, zTab, zDb); assert( pTab ); @@ -111308,7 +115650,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterFinishAddColumn(Parse *pParse, Token * if( pDflt && pDflt->pLeft->op==TK_NULL ){ pDflt = 0; } - if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ + assert( IsOrdinaryTable(pNew) ); + if( (db->flags&SQLITE_ForeignKeys) && pNew->u.tab.pFKey && pDflt ){ sqlcipher_sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "Cannot add a REFERENCES column with non-NULL default value"); } @@ -111345,28 +115688,30 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterFinishAddColumn(Parse *pParse, Token * zCol = sqlcipher_sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); if( zCol ){ char *zEnd = &zCol[pColDef->n-1]; - u32 savedDbFlags = db->mDbFlags; while( zEnd>zCol && (*zEnd==';' || sqlcipher_sqlite3Isspace(*zEnd)) ){ *zEnd-- = '\0'; } - db->mDbFlags |= DBFLAG_PreferBuiltin; + /* substr() operations on characters, but addColOffset is in bytes. So we + ** have to use printf() to translate between these units: */ + assert( IsOrdinaryTable(pTab) ); + assert( IsOrdinaryTable(pNew) ); sqlcipher_sqlite3NestedParse(pParse, - "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " - "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " + "sql = printf('%%.%ds, ',sql) || %Q" + " || substr(sql,1+length(printf('%%.%ds',sql))) " "WHERE type = 'table' AND name = %Q", - zDb, pNew->addColOffset, zCol, pNew->addColOffset+1, + zDb, pNew->u.tab.addColOffset, zCol, pNew->u.tab.addColOffset, zTab ); sqlcipher_sqlite3DbFree(db, zCol); - db->mDbFlags = savedDbFlags; } - /* Make sure the schema version is at least 3. But do not upgrade - ** from less than 3 to 4, as that will corrupt any preexisting DESC - ** index. - */ v = sqlcipher_sqlite3GetVdbe(pParse); if( v ){ + /* Make sure the schema version is at least 3. But do not upgrade + ** from less than 3 to 4, as that will corrupt any preexisting DESC + ** index. + */ r1 = sqlcipher_sqlite3GetTempReg(pParse); sqlcipher_sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT); sqlcipher_sqlite3VdbeUsesBtree(v, iDb); @@ -111375,10 +115720,25 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterFinishAddColumn(Parse *pParse, Token * VdbeCoverage(v); sqlcipher_sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3); sqlcipher_sqlite3ReleaseTempReg(pParse, r1); - } - /* Reload the table definition */ - renameReloadSchema(pParse, iDb); + /* Reload the table definition */ + renameReloadSchema(pParse, iDb, INITFLAG_AlterAdd); + + /* Verify that constraints are still satisfied */ + if( pNew->pCheck!=0 + || (pCol->notNull && (pCol->colFlags & COLFLAG_GENERATED)!=0) + ){ + sqlcipher_sqlite3NestedParse(pParse, + "SELECT CASE WHEN quick_check GLOB 'CHECK*'" + " THEN raise(ABORT,'CHECK constraint failed')" + " ELSE raise(ABORT,'NOT NULL constraint failed')" + " END" + " FROM pragma_quick_check(%Q,%Q)" + " WHERE quick_check GLOB 'CHECK*' OR quick_check GLOB 'NULL*'", + zTab, zDb + ); + } + } } /* @@ -111419,7 +115779,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterBeginAddColumn(Parse *pParse, SrcList #endif /* Make sure this is not an attempt to ALTER a view. */ - if( pTab->pSelect ){ + if( IsView(pTab) ){ sqlcipher_sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); goto exit_begin_add_column; } @@ -111428,7 +115788,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterBeginAddColumn(Parse *pParse, SrcList } sqlcipher_sqlite3MayAbort(pParse); - assert( pTab->addColOffset>0 ); + assert( IsOrdinaryTable(pTab) ); + assert( pTab->u.tab.addColOffset>0 ); iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); /* Put a copy of the Table struct in Parse.pNewTable for the @@ -111455,13 +115816,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterBeginAddColumn(Parse *pParse, SrcList memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); for(i=0; inCol; i++){ Column *pCol = &pNew->aCol[i]; - pCol->zName = sqlcipher_sqlite3DbStrDup(db, pCol->zName); - pCol->hName = sqlcipher_sqlite3StrIHash(pCol->zName); - pCol->zColl = 0; - pCol->pDflt = 0; + pCol->zCnName = sqlcipher_sqlite3DbStrDup(db, pCol->zCnName); + pCol->hName = sqlcipher_sqlite3StrIHash(pCol->zCnName); } + assert( IsOrdinaryTable(pNew) ); + pNew->u.tab.pDfltList = sqlcipher_sqlite3ExprListDup(db, pTab->u.tab.pDfltList, 0); pNew->pSchema = db->aDb[iDb].pSchema; - pNew->addColOffset = pTab->addColOffset; + pNew->u.tab.addColOffset = pTab->u.tab.addColOffset; pNew->nTabRef = 1; exit_begin_add_column: @@ -111478,10 +115839,10 @@ exit_begin_add_column: ** Or, if pTab is not a view or virtual table, zero is returned. */ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) -static int isRealTable(Parse *pParse, Table *pTab){ +static int isRealTable(Parse *pParse, Table *pTab, int bDrop){ const char *zType = 0; #ifndef SQLITE_OMIT_VIEW - if( pTab->pSelect ){ + if( IsView(pTab) ){ zType = "view"; } #endif @@ -111491,15 +115852,16 @@ static int isRealTable(Parse *pParse, Table *pTab){ } #endif if( zType ){ - sqlcipher_sqlite3ErrorMsg( - pParse, "cannot rename columns of %s \"%s\"", zType, pTab->zName + sqlcipher_sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"", + (bDrop ? "drop column from" : "rename columns of"), + zType, pTab->zName ); return 1; } return 0; } #else /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */ -# define isRealTable(x,y) (0) +# define isRealTable(x,y,z) (0) #endif /* @@ -111528,7 +115890,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterRenameColumn( /* Cannot alter a system table */ if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column; - if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column; + if( SQLITE_OK!=isRealTable(pParse, pTab, 0) ) goto exit_rename_column; /* Which schema holds the table to be altered */ iSchema = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); @@ -111547,13 +115909,17 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterRenameColumn( zOld = sqlcipher_sqlite3NameFromToken(db, pOld); if( !zOld ) goto exit_rename_column; for(iCol=0; iColnCol; iCol++){ - if( 0==sqlcipher_sqlite3StrICmp(pTab->aCol[iCol].zName, zOld) ) break; + if( 0==sqlcipher_sqlite3StrICmp(pTab->aCol[iCol].zCnName, zOld) ) break; } if( iCol==pTab->nCol ){ - sqlcipher_sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zOld); + sqlcipher_sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pOld); goto exit_rename_column; } + /* Ensure the schema contains no double-quoted strings */ + renameTestSchema(pParse, zDb, iSchema==1, "", 0); + renameFixQuotes(pParse, zDb, iSchema==1); + /* Do the rename operation using a recursive UPDATE statement that ** uses the sqlite_rename_column() SQL function to compute the new ** CREATE statement text for the sqlite_schema table. @@ -111564,26 +115930,25 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterRenameColumn( assert( pNew->n>0 ); bQuote = sqlcipher_sqlite3Isquote(pNew->z[0]); sqlcipher_sqlite3NestedParse(pParse, - "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) " "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' " - " AND (type != 'index' OR tbl_name = %Q)" - " AND sql NOT LIKE 'create virtual%%'", + " AND (type != 'index' OR tbl_name = %Q)", zDb, zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1, pTab->zName ); sqlcipher_sqlite3NestedParse(pParse, - "UPDATE temp." DFLT_SCHEMA_TABLE " SET " + "UPDATE temp." LEGACY_SCHEMA_TABLE " SET " "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, 1) " "WHERE type IN ('trigger', 'view')", zDb, pTab->zName, iCol, zNew, bQuote ); /* Drop and reload the database schema. */ - renameReloadSchema(pParse, iSchema); - renameTestSchema(pParse, zDb, iSchema==1); + renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename); + renameTestSchema(pParse, zDb, iSchema==1, "after rename", 1); exit_rename_column: sqlcipher_sqlite3SrcListDelete(db, pSrc); @@ -111610,7 +115975,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AlterRenameColumn( ** the parse tree. */ struct RenameToken { - void *p; /* Parse tree element created by token t */ + const void *p; /* Parse tree element created by token t */ Token t; /* The token that created parse tree element p */ RenameToken *pNext; /* Next is a list of all RenameToken objects */ }; @@ -111652,9 +116017,11 @@ struct RenameCtx { ** Technically, as x no longer points into a valid object or to the byte ** following a valid object, it may not be used in comparison operations. */ -static void renameTokenCheckAll(Parse *pParse, void *pPtr){ - if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){ - RenameToken *p; +static void renameTokenCheckAll(Parse *pParse, const void *pPtr){ + assert( pParse==pParse->db->pParse ); + assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); + if( pParse->nErr==0 ){ + const RenameToken *p; u8 i = 0; for(p=pParse->pRename; p; p=p->pNext){ if( p->p ){ @@ -111680,7 +116047,11 @@ static void renameTokenCheckAll(Parse *pParse, void *pPtr){ ** with tail recursion in tokenExpr() routine, for a small performance ** improvement. */ -SQLITE_PRIVATE void *sqlcipher_sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ +SQLITE_PRIVATE const void *sqlcipher_sqlite3RenameTokenMap( + Parse *pParse, + const void *pPtr, + const Token *pToken +){ RenameToken *pNew; assert( pPtr || pParse->db->mallocFailed ); renameTokenCheckAll(pParse, pPtr); @@ -111702,7 +116073,7 @@ SQLITE_PRIVATE void *sqlcipher_sqlite3RenameTokenMap(Parse *pParse, void *pPtr, ** with parse tree element pFrom. This function remaps the associated token ** to parse tree element pTo. */ -SQLITE_PRIVATE void sqlcipher_sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){ +SQLITE_PRIVATE void sqlcipher_sqlite3RenameTokenRemap(Parse *pParse, const void *pTo, const void *pFrom){ RenameToken *p; renameTokenCheckAll(pParse, pTo); for(p=pParse->pRename; p; p=p->pNext){ @@ -111718,7 +116089,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RenameTokenRemap(Parse *pParse, void *pTo, */ static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){ Parse *pParse = pWalker->pParse; - sqlcipher_sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr); + sqlcipher_sqlite3RenameTokenRemap(pParse, 0, (const void*)pExpr); + if( ExprUseYTab(pExpr) ){ + sqlcipher_sqlite3RenameTokenRemap(pParse, 0, (const void*)&pExpr->y.pTab); + } return WRC_Continue; } @@ -111729,15 +116103,31 @@ static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){ static void renameWalkWith(Walker *pWalker, Select *pSelect){ With *pWith = pSelect->pWith; if( pWith ){ + Parse *pParse = pWalker->pParse; int i; + With *pCopy = 0; + assert( pWith->nCte>0 ); + if( (pWith->a[0].pSelect->selFlags & SF_Expanded)==0 ){ + /* Push a copy of the With object onto the with-stack. We use a copy + ** here as the original will be expanded and resolved (flags SF_Expanded + ** and SF_Resolved) below. And the parser code that uses the with-stack + ** fails if the Select objects on it have already been expanded and + ** resolved. */ + pCopy = sqlcipher_sqlite3WithDup(pParse->db, pWith); + pCopy = sqlcipher_sqlite3WithPush(pParse, pCopy, 1); + } for(i=0; inCte; i++){ Select *p = pWith->a[i].pSelect; NameContext sNC; memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pWalker->pParse; - sqlcipher_sqlite3SelectPrep(sNC.pParse, p, &sNC); + sNC.pParse = pParse; + if( pCopy ) sqlcipher_sqlite3SelectPrep(sNC.pParse, p, &sNC); + if( sNC.pParse->db->mallocFailed ) return; sqlcipher_sqlite3WalkSelect(pWalker, p); - sqlcipher_sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols); + sqlcipher_sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols); + } + if( pCopy && pParse->pWith==pCopy ){ + pParse->pWith = pCopy->pOuter; } } } @@ -111747,13 +116137,12 @@ static void renameWalkWith(Walker *pWalker, Select *pSelect){ */ static void unmapColumnIdlistNames( Parse *pParse, - IdList *pIdList + const IdList *pIdList ){ - if( pIdList ){ - int ii; - for(ii=0; iinId; ii++){ - sqlcipher_sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName); - } + int ii; + assert( pIdList!=0 ); + for(ii=0; iinId; ii++){ + sqlcipher_sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName); } } @@ -111764,11 +116153,15 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){ Parse *pParse = pWalker->pParse; int i; if( pParse->nErr ) return WRC_Abort; - if( NEVER(p->selFlags & SF_View) ) return WRC_Prune; + testcase( p->selFlags & SF_View ); + testcase( p->selFlags & SF_CopyCte ); + if( p->selFlags & (SF_View|SF_CopyCte) ){ + return WRC_Prune; + } if( ALWAYS(p->pEList) ){ ExprList *pList = p->pEList; for(i=0; inExpr; i++){ - if( pList->a[i].zEName && pList->a[i].eEName==ENAME_NAME ){ + if( pList->a[i].zEName && pList->a[i].fg.eEName==ENAME_NAME ){ sqlcipher_sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName); } } @@ -111777,8 +116170,11 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){ SrcList *pSrc = p->pSrc; for(i=0; inSrc; i++){ sqlcipher_sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName); - if( sqlcipher_sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort; - unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing); + if( pSrc->a[i].fg.isUsing==0 ){ + sqlcipher_sqlite3WalkExpr(pWalker, pSrc->a[i].u3.pOn); + }else{ + unmapColumnIdlistNames(pParse, pSrc->a[i].u3.pUsing); + } } } @@ -111814,7 +116210,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RenameExprlistUnmap(Parse *pParse, ExprList sWalker.xExprCallback = renameUnmapExprCb; sqlcipher_sqlite3WalkExprList(&sWalker, pEList); for(i=0; inExpr; i++){ - if( ALWAYS(pEList->a[i].eEName==ENAME_NAME) ){ + if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) ){ sqlcipher_sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zEName); } } @@ -111835,23 +116231,35 @@ static void renameTokenFree(sqlcipher_sqlite3 *db, RenameToken *pToken){ /* ** Search the Parse object passed as the first argument for a RenameToken -** object associated with parse tree element pPtr. If found, remove it -** from the Parse object and add it to the list maintained by the -** RenameCtx object passed as the second argument. +** object associated with parse tree element pPtr. If found, return a pointer +** to it. Otherwise, return NULL. +** +** If the second argument passed to this function is not NULL and a matching +** RenameToken object is found, remove it from the Parse object and add it to +** the list maintained by the RenameCtx object. */ -static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ +static RenameToken *renameTokenFind( + Parse *pParse, + struct RenameCtx *pCtx, + const void *pPtr +){ RenameToken **pp; - assert( pPtr!=0 ); + if( NEVER(pPtr==0) ){ + return 0; + } for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){ if( (*pp)->p==pPtr ){ RenameToken *pToken = *pp; - *pp = pToken->pNext; - pToken->pNext = pCtx->pList; - pCtx->pList = pToken; - pCtx->nList++; - break; + if( pCtx ){ + *pp = pToken->pNext; + pToken->pNext = pCtx->pList; + pCtx->pList = pToken; + pCtx->nList++; + } + return pToken; } } + return 0; } /* @@ -111860,7 +116268,11 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ ** descend into sub-select statements. */ static int renameColumnSelectCb(Walker *pWalker, Select *p){ - if( p->selFlags & SF_View ) return WRC_Prune; + if( p->selFlags & (SF_View|SF_CopyCte) ){ + testcase( p->selFlags & SF_View ); + testcase( p->selFlags & SF_CopyCte ); + return WRC_Prune; + } renameWalkWith(pWalker, p); return WRC_Continue; } @@ -111883,6 +116295,7 @@ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ renameTokenFind(pWalker->pParse, p, (void*)pExpr); }else if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol + && ALWAYS(ExprUseYTab(pExpr)) && p->pTab==pExpr->y.pTab ){ renameTokenFind(pWalker->pParse, p, (void*)pExpr); @@ -111922,7 +116335,7 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ */ static void renameColumnParseError( sqlcipher_sqlite3_context *pCtx, - int bPost, + const char *zWhen, sqlcipher_sqlite3_value *pType, sqlcipher_sqlite3_value *pObject, Parse *pParse @@ -111931,12 +116344,12 @@ static void renameColumnParseError( const char *zN = (const char*)sqlcipher_sqlite3_value_text(pObject); char *zErr; - zErr = sqlcipher_sqlite3_mprintf("error in %s %s%s: %s", - zT, zN, (bPost ? " after rename" : ""), + zErr = sqlcipher_sqlite3MPrintf(pParse->db, "error in %s %s%s%s: %s", + zT, zN, (zWhen[0] ? " " : ""), zWhen, pParse->zErrMsg ); sqlcipher_sqlite3_result_error(pCtx, zErr, -1); - sqlcipher_sqlite3_free(zErr); + sqlcipher_sqlite3DbFree(pParse->db, zErr); } /* @@ -111948,18 +116361,18 @@ static void renameColumnParseError( static void renameColumnElistNames( Parse *pParse, RenameCtx *pCtx, - ExprList *pEList, + const ExprList *pEList, const char *zOld ){ if( pEList ){ int i; for(i=0; inExpr; i++){ - char *zName = pEList->a[i].zEName; - if( ALWAYS(pEList->a[i].eEName==ENAME_NAME) + const char *zName = pEList->a[i].zEName; + if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) && ALWAYS(zName!=0) && 0==sqlcipher_sqlite3_stricmp(zName, zOld) ){ - renameTokenFind(pParse, pCtx, (void*)zName); + renameTokenFind(pParse, pCtx, (const void*)zName); } } } @@ -111973,15 +116386,15 @@ static void renameColumnElistNames( static void renameColumnIdlistNames( Parse *pParse, RenameCtx *pCtx, - IdList *pIdList, + const IdList *pIdList, const char *zOld ){ if( pIdList ){ int i; for(i=0; inId; i++){ - char *zName = pIdList->a[i].zName; + const char *zName = pIdList->a[i].zName; if( 0==sqlcipher_sqlite3_stricmp(zName, zOld) ){ - renameTokenFind(pParse, pCtx, (void*)zName); + renameTokenFind(pParse, pCtx, (const void*)zName); } } } @@ -112000,24 +116413,22 @@ static int renameParseSql( int bTemp /* True if SQL is from temp schema */ ){ int rc; - char *zErr = 0; + sqlcipher_sqlite3ParseObjectInit(p, db); + if( zSql==0 ){ + return SQLITE_NOMEM; + } + if( sqlcipher_sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){ + return SQLITE_CORRUPT_BKPT; + } db->init.iDb = bTemp ? 1 : sqlcipher_sqlite3FindDbName(db, zDb); - - /* Parse the SQL statement passed as the first argument. If no error - ** occurs and the parse does not result in a new table, index or - ** trigger object, the database must be corrupt. */ - memset(p, 0, sizeof(Parse)); p->eParseMode = PARSE_MODE_RENAME; p->db = db; p->nQueryLoop = 1; - rc = sqlcipher_sqlite3RunParser(p, zSql, &zErr); - assert( p->zErrMsg==0 ); - assert( rc!=SQLITE_OK || zErr==0 ); - p->zErrMsg = zErr; + rc = sqlcipher_sqlite3RunParser(p, zSql); if( db->mallocFailed ) rc = SQLITE_NOMEM; if( rc==SQLITE_OK - && p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0 + && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0) ){ rc = SQLITE_CORRUPT_BKPT; } @@ -112054,51 +116465,76 @@ static int renameEditSql( const char *zNew, /* New token text */ int bQuote /* True to always quote token */ ){ - int nNew = sqlcipher_sqlite3Strlen30(zNew); - int nSql = sqlcipher_sqlite3Strlen30(zSql); + i64 nNew = sqlcipher_sqlite3Strlen30(zNew); + i64 nSql = sqlcipher_sqlite3Strlen30(zSql); sqlcipher_sqlite3 *db = sqlcipher_sqlite3_context_db_handle(pCtx); int rc = SQLITE_OK; - char *zQuot; + char *zQuot = 0; char *zOut; - int nQuot; - - /* Set zQuot to point to a buffer containing a quoted copy of the - ** identifier zNew. If the corresponding identifier in the original - ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to - ** point to zQuot so that all substitutions are made using the - ** quoted version of the new column name. */ - zQuot = sqlcipher_sqlite3MPrintf(db, "\"%w\"", zNew); - if( zQuot==0 ){ - return SQLITE_NOMEM; + i64 nQuot = 0; + char *zBuf1 = 0; + char *zBuf2 = 0; + + if( zNew ){ + /* Set zQuot to point to a buffer containing a quoted copy of the + ** identifier zNew. If the corresponding identifier in the original + ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to + ** point to zQuot so that all substitutions are made using the + ** quoted version of the new column name. */ + zQuot = sqlcipher_sqlite3MPrintf(db, "\"%w\" ", zNew); + if( zQuot==0 ){ + return SQLITE_NOMEM; + }else{ + nQuot = sqlcipher_sqlite3Strlen30(zQuot)-1; + } + + assert( nQuot>=nNew ); + zOut = sqlcipher_sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); }else{ - nQuot = sqlcipher_sqlite3Strlen30(zQuot); - } - if( bQuote ){ - zNew = zQuot; - nNew = nQuot; + zOut = (char*)sqlcipher_sqlite3DbMallocZero(db, (nSql*2+1) * 3); + if( zOut ){ + zBuf1 = &zOut[nSql*2+1]; + zBuf2 = &zOut[nSql*4+2]; + } } /* At this point pRename->pList contains a list of RenameToken objects ** corresponding to all tokens in the input SQL that must be replaced - ** with the new column name. All that remains is to construct and - ** return the edited SQL string. */ - assert( nQuot>=nNew ); - zOut = sqlcipher_sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); + ** with the new column name, or with single-quoted versions of themselves. + ** All that remains is to construct and return the edited SQL string. */ if( zOut ){ int nOut = nSql; memcpy(zOut, zSql, nSql); while( pRename->pList ){ int iOff; /* Offset of token to replace in zOut */ - RenameToken *pBest = renameColumnTokenNext(pRename); - u32 nReplace; const char *zReplace; - if( sqlcipher_sqlite3IsIdChar(*pBest->t.z) ){ - nReplace = nNew; - zReplace = zNew; + RenameToken *pBest = renameColumnTokenNext(pRename); + + if( zNew ){ + if( bQuote==0 && sqlcipher_sqlite3IsIdChar(*pBest->t.z) ){ + nReplace = nNew; + zReplace = zNew; + }else{ + nReplace = nQuot; + zReplace = zQuot; + if( pBest->t.z[pBest->t.n]=='"' ) nReplace++; + } }else{ - nReplace = nQuot; - zReplace = zQuot; + /* Dequote the double-quoted token. Then requote it again, this time + ** using single quotes. If the character immediately following the + ** original token within the input SQL was a single quote ('), then + ** add another space after the new, single-quoted version of the + ** token. This is so that (SELECT "string"'alias') maps to + ** (SELECT 'string' 'alias'), and not (SELECT 'string''alias'). */ + memcpy(zBuf1, pBest->t.z, pBest->t.n); + zBuf1[pBest->t.n] = 0; + sqlcipher_sqlite3Dequote(zBuf1); + sqlcipher_sqlite3_snprintf(nSql*2, zBuf2, "%Q%s", zBuf1, + pBest->t.z[pBest->t.n]=='\'' ? " " : "" + ); + zReplace = zBuf2; + nReplace = sqlcipher_sqlite3Strlen30(zReplace); } iOff = pBest->t.z - zSql; @@ -112162,26 +116598,35 @@ static int renameResolveTrigger(Parse *pParse){ if( rc==SQLITE_OK && pStep->zTarget ){ SrcList *pSrc = sqlcipher_sqlite3TriggerStepSrc(pParse, pStep); if( pSrc ){ - int i; - for(i=0; inSrc && rc==SQLITE_OK; i++){ - struct SrcList_item *p = &pSrc->a[i]; - p->iCursor = pParse->nTab++; - if( p->pSelect ){ - sqlcipher_sqlite3SelectPrep(pParse, p->pSelect, 0); - sqlcipher_sqlite3ExpandSubquery(pParse, p); - assert( i>0 ); - assert( pStep->pFrom->a[i-1].pSelect ); - sqlcipher_sqlite3SelectPrep(pParse, pStep->pFrom->a[i-1].pSelect, 0); - }else{ - p->pTab = sqlcipher_sqlite3LocateTableItem(pParse, 0, p); - if( p->pTab==0 ){ - rc = SQLITE_ERROR; - }else{ - p->pTab->nTabRef++; - rc = sqlcipher_sqlite3ViewGetColumnNames(pParse, p->pTab); + Select *pSel = sqlcipher_sqlite3SelectNew( + pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0 + ); + if( pSel==0 ){ + pStep->pExprList = 0; + pSrc = 0; + rc = SQLITE_NOMEM; + }else{ + sqlcipher_sqlite3SelectPrep(pParse, pSel, 0); + rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK; + assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList ); + assert( pSrc==pSel->pSrc ); + if( pStep->pExprList ) pSel->pEList = 0; + pSel->pSrc = 0; + sqlcipher_sqlite3SelectDelete(db, pSel); + } + if( pStep->pFrom ){ + int i; + for(i=0; ipFrom->nSrc && rc==SQLITE_OK; i++){ + SrcItem *p = &pStep->pFrom->a[i]; + if( p->pSelect ){ + sqlcipher_sqlite3SelectPrep(pParse, p->pSelect, 0); } } } + + if( db->mallocFailed ){ + rc = SQLITE_NOMEM; + } sNC.pSrcList = pSrc; if( rc==SQLITE_OK && pStep->pWhere ){ rc = sqlcipher_sqlite3ResolveExprNames(&sNC, pStep->pWhere); @@ -112190,9 +116635,8 @@ static int renameResolveTrigger(Parse *pParse){ rc = sqlcipher_sqlite3ResolveExprListNames(&sNC, pStep->pExprList); } assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) ); - if( pStep->pUpsert ){ + if( pStep->pUpsert && rc==SQLITE_OK ){ Upsert *pUpsert = pStep->pUpsert; - assert( rc==SQLITE_OK ); pUpsert->pUpsertSrc = pSrc; sNC.uNC.pUpsert = pUpsert; sNC.ncFlags = NC_UUpsert; @@ -112268,13 +116712,13 @@ static void renameParseCleanup(Parse *pParse){ sqlcipher_sqlite3DeleteTrigger(db, pParse->pNewTrigger); sqlcipher_sqlite3DbFree(db, pParse->zErrMsg); renameTokenFree(db, pParse->pRename); - sqlcipher_sqlite3ParserReset(pParse); + sqlcipher_sqlite3ParseObjectReset(pParse); } /* ** SQL function: ** -** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld) +** sqlite_rename_column(SQL,TYPE,OBJ,DB,TABLE,COL,NEWNAME,QUOTE,TEMP) ** ** 0. zSql: SQL statement to rewrite ** 1. type: Type of object ("table", "view" etc.) @@ -112292,7 +116736,8 @@ static void renameParseCleanup(Parse *pParse){ ** ** This function is used internally by the ALTER TABLE RENAME COLUMN command. ** It is only accessible to SQL created using sqlcipher_sqlite3NestedParse(). It is -** not reachable from ordinary SQL passed into sqlcipher_sqlite3_prepare(). +** not reachable from ordinary SQL passed into sqlcipher_sqlite3_prepare() unless the +** SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test setting is enabled. */ static void renameColumnFunc( sqlcipher_sqlite3_context *context, @@ -112330,7 +116775,7 @@ static void renameColumnFunc( sqlcipher_sqlite3BtreeLeaveAll(db); return; } - zOld = pTab->aCol[iCol].zName; + zOld = pTab->aCol[iCol].zCnName; memset(&sCtx, 0, sizeof(sCtx)); sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); @@ -112349,8 +116794,8 @@ static void renameColumnFunc( sCtx.pTab = pTab; if( rc!=SQLITE_OK ) goto renameColumnFunc_done; if( sParse.pNewTable ){ - Select *pSelect = sParse.pNewTable->pSelect; - if( pSelect ){ + if( IsView(sParse.pNewTable) ){ + Select *pSelect = sParse.pNewTable->u.view.pSelect; pSelect->selFlags &= ~SF_View; sParse.rc = SQLITE_OK; sqlcipher_sqlite3SelectPrep(&sParse, pSelect, 0); @@ -112359,16 +116804,17 @@ static void renameColumnFunc( sqlcipher_sqlite3WalkSelect(&sWalker, pSelect); } if( rc!=SQLITE_OK ) goto renameColumnFunc_done; - }else{ + }else if( IsOrdinaryTable(sParse.pNewTable) ){ /* A regular table */ int bFKOnly = sqlcipher_sqlite3_stricmp(zTable, sParse.pNewTable->zName); FKey *pFKey; - assert( sParse.pNewTable->pSelect==0 ); sCtx.pTab = sParse.pNewTable; if( bFKOnly==0 ){ - renameTokenFind( - &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zName - ); + if( iColnCol ){ + renameTokenFind( + &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zCnName + ); + } if( sCtx.iCol<0 ){ renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey); } @@ -112379,14 +116825,17 @@ static void renameColumnFunc( for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){ sqlcipher_sqlite3WalkExprList(&sWalker, pIdx->aColExpr); } - } #ifndef SQLITE_OMIT_GENERATED_COLUMNS - for(i=0; inCol; i++){ - sqlcipher_sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt); - } + for(i=0; inCol; i++){ + Expr *pExpr = sqlcipher_sqlite3ColumnExpr(sParse.pNewTable, + &sParse.pNewTable->aCol[i]); + sqlcipher_sqlite3WalkExpr(&sWalker, pExpr); + } #endif + } - for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + assert( IsOrdinaryTable(sParse.pNewTable) ); + for(pFKey=sParse.pNewTable->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ for(i=0; inCol; i++){ if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){ renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]); @@ -112437,8 +116886,10 @@ static void renameColumnFunc( renameColumnFunc_done: if( rc!=SQLITE_OK ){ - if( sParse.zErrMsg ){ - renameColumnParseError(context, 0, argv[1], argv[2], &sParse); + if( rc==SQLITE_ERROR && sqlcipher_sqlite3WritableSchema(db) ){ + sqlcipher_sqlite3_result_value(context, argv[0]); + }else if( sParse.zErrMsg ){ + renameColumnParseError(context, "", argv[1], argv[2], &sParse); }else{ sqlcipher_sqlite3_result_error_code(context, rc); } @@ -112457,7 +116908,10 @@ renameColumnFunc_done: */ static int renameTableExprCb(Walker *pWalker, Expr *pExpr){ RenameCtx *p = pWalker->u.pRename; - if( pExpr->op==TK_COLUMN && p->pTab==pExpr->y.pTab ){ + if( pExpr->op==TK_COLUMN + && ALWAYS(ExprUseYTab(pExpr)) + && p->pTab==pExpr->y.pTab + ){ renameTokenFind(pWalker->pParse, p, (void*)&pExpr->y.pTab); } return WRC_Continue; @@ -112470,13 +116924,17 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ int i; RenameCtx *p = pWalker->u.pRename; SrcList *pSrc = pSelect->pSrc; - if( pSelect->selFlags & SF_View ) return WRC_Prune; - if( pSrc==0 ){ + if( pSelect->selFlags & (SF_View|SF_CopyCte) ){ + testcase( pSelect->selFlags & SF_View ); + testcase( pSelect->selFlags & SF_CopyCte ); + return WRC_Prune; + } + if( NEVER(pSrc==0) ){ assert( pWalker->pParse->db->mallocFailed ); return WRC_Abort; } for(i=0; inSrc; i++){ - struct SrcList_item *pItem = &pSrc->a[i]; + SrcItem *pItem = &pSrc->a[i]; if( pItem->pTab==p->pTab ){ renameTokenFind(pWalker->pParse, p, pItem->zName); } @@ -112548,28 +117006,31 @@ static void renameTableFunc( if( sParse.pNewTable ){ Table *pTab = sParse.pNewTable; - if( pTab->pSelect ){ + if( IsView(pTab) ){ if( isLegacy==0 ){ - Select *pSelect = pTab->pSelect; + Select *pSelect = pTab->u.view.pSelect; NameContext sNC; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = &sParse; assert( pSelect->selFlags & SF_View ); pSelect->selFlags &= ~SF_View; - sqlcipher_sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC); + sqlcipher_sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC); if( sParse.nErr ){ rc = sParse.rc; }else{ - sqlcipher_sqlite3WalkSelect(&sWalker, pTab->pSelect); + sqlcipher_sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect); } } }else{ /* Modify any FK definitions to point to the new table. */ #ifndef SQLITE_OMIT_FOREIGN_KEY - if( isLegacy==0 || (db->flags & SQLITE_ForeignKeys) ){ + if( (isLegacy==0 || (db->flags & SQLITE_ForeignKeys)) + && !IsVirtual(pTab) + ){ FKey *pFKey; - for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + assert( IsOrdinaryTable(pTab) ); + for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ if( sqlcipher_sqlite3_stricmp(pFKey->zTo, zOld)==0 ){ renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo); } @@ -112615,6 +117076,15 @@ static void renameTableFunc( if( pStep->zTarget && 0==sqlcipher_sqlite3_stricmp(pStep->zTarget, zOld) ){ renameTokenFind(&sParse, &sCtx, pStep->zTarget); } + if( pStep->pFrom ){ + int i; + for(i=0; ipFrom->nSrc; i++){ + SrcItem *pItem = &pStep->pFrom->a[i]; + if( 0==sqlcipher_sqlite3_stricmp(pItem->zName, zOld) ){ + renameTokenFind(&sParse, &sCtx, pItem->zName); + } + } + } } } } @@ -112626,8 +117096,10 @@ static void renameTableFunc( rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote); } if( rc!=SQLITE_OK ){ - if( sParse.zErrMsg ){ - renameColumnParseError(context, 0, argv[1], argv[2], &sParse); + if( rc==SQLITE_ERROR && sqlcipher_sqlite3WritableSchema(db) ){ + sqlcipher_sqlite3_result_value(context, argv[3]); + }else if( sParse.zErrMsg ){ + renameColumnParseError(context, "", argv[1], argv[2], &sParse); }else{ sqlcipher_sqlite3_result_error_code(context, rc); } @@ -112644,7 +117116,131 @@ static void renameTableFunc( return; } -/* +static int renameQuotefixExprCb(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_STRING && (pExpr->flags & EP_DblQuoted) ){ + renameTokenFind(pWalker->pParse, pWalker->u.pRename, (const void*)pExpr); + } + return WRC_Continue; +} + +/* SQL function: sqlite_rename_quotefix(DB,SQL) +** +** Rewrite the DDL statement "SQL" so that any string literals that use +** double-quotes use single quotes instead. +** +** Two arguments must be passed: +** +** 0: Database name ("main", "temp" etc.). +** 1: SQL statement to edit. +** +** The returned value is the modified SQL statement. For example, given +** the database schema: +** +** CREATE TABLE t1(a, b, c); +** +** SELECT sqlite_rename_quotefix('main', +** 'CREATE VIEW v1 AS SELECT "a", "string" FROM t1' +** ); +** +** returns the string: +** +** CREATE VIEW v1 AS SELECT "a", 'string' FROM t1 +** +** If there is a error in the input SQL, then raise an error, except +** if PRAGMA writable_schema=ON, then just return the input string +** unmodified following an error. +*/ +static void renameQuotefixFunc( + sqlcipher_sqlite3_context *context, + int NotUsed, + sqlcipher_sqlite3_value **argv +){ + sqlcipher_sqlite3 *db = sqlcipher_sqlite3_context_db_handle(context); + char const *zDb = (const char*)sqlcipher_sqlite3_value_text(argv[0]); + char const *zInput = (const char*)sqlcipher_sqlite3_value_text(argv[1]); + +#ifndef SQLITE_OMIT_AUTHORIZATION + sqlcipher_sqlite3_xauth xAuth = db->xAuth; + db->xAuth = 0; +#endif + + sqlcipher_sqlite3BtreeEnterAll(db); + + UNUSED_PARAMETER(NotUsed); + if( zDb && zInput ){ + int rc; + Parse sParse; + rc = renameParseSql(&sParse, zDb, db, zInput, 0); + + if( rc==SQLITE_OK ){ + RenameCtx sCtx; + Walker sWalker; + + /* Walker to find tokens that need to be replaced. */ + memset(&sCtx, 0, sizeof(RenameCtx)); + memset(&sWalker, 0, sizeof(Walker)); + sWalker.pParse = &sParse; + sWalker.xExprCallback = renameQuotefixExprCb; + sWalker.xSelectCallback = renameColumnSelectCb; + sWalker.u.pRename = &sCtx; + + if( sParse.pNewTable ){ + if( IsView(sParse.pNewTable) ){ + Select *pSelect = sParse.pNewTable->u.view.pSelect; + pSelect->selFlags &= ~SF_View; + sParse.rc = SQLITE_OK; + sqlcipher_sqlite3SelectPrep(&sParse, pSelect, 0); + rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); + if( rc==SQLITE_OK ){ + sqlcipher_sqlite3WalkSelect(&sWalker, pSelect); + } + }else{ + int i; + sqlcipher_sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck); +#ifndef SQLITE_OMIT_GENERATED_COLUMNS + for(i=0; inCol; i++){ + sqlcipher_sqlite3WalkExpr(&sWalker, + sqlcipher_sqlite3ColumnExpr(sParse.pNewTable, + &sParse.pNewTable->aCol[i])); + } +#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ + } + }else if( sParse.pNewIndex ){ + sqlcipher_sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); + sqlcipher_sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); + }else{ +#ifndef SQLITE_OMIT_TRIGGER + rc = renameResolveTrigger(&sParse); + if( rc==SQLITE_OK ){ + renameWalkTrigger(&sWalker, sParse.pNewTrigger); + } +#endif /* SQLITE_OMIT_TRIGGER */ + } + + if( rc==SQLITE_OK ){ + rc = renameEditSql(context, &sCtx, zInput, 0, 0); + } + renameTokenFree(db, sCtx.pList); + } + if( rc!=SQLITE_OK ){ + if( sqlcipher_sqlite3WritableSchema(db) && rc==SQLITE_ERROR ){ + sqlcipher_sqlite3_result_value(context, argv[1]); + }else{ + sqlcipher_sqlite3_result_error_code(context, rc); + } + } + renameParseCleanup(&sParse); + } + +#ifndef SQLITE_OMIT_AUTHORIZATION + db->xAuth = xAuth; +#endif + + sqlcipher_sqlite3BtreeLeaveAll(db); +} + +/* Function: sqlite_rename_test(DB,SQL,TYPE,NAME,ISTEMP,WHEN,DQS) +** ** An SQL user function that checks that there are no parse or symbol ** resolution problems in a CREATE TRIGGER|TABLE|VIEW|INDEX statement. ** After an ALTER TABLE .. RENAME operation is performed and the schema @@ -112656,12 +117252,16 @@ static void renameTableFunc( ** 2: Object type ("view", "table", "trigger" or "index"). ** 3: Object name. ** 4: True if object is from temp schema. +** 5: "when" part of error message. +** 6: True to disable the DQS quirk when parsing SQL. ** -** Unless it finds an error, this function normally returns NULL. However, it -** returns integer value 1 if: +** The return value is computed as follows: ** -** * the SQL argument creates a trigger, and -** * the table that the trigger is attached to is in database zDb. +** A. If an error is seen and not in PRAGMA writable_schema=ON mode, +** then raise the error. +** B. Else if a trigger is created and the the table that the trigger is +** attached to is in database zDb, then return 1. +** C. Otherwise return NULL. */ static void renameTableTest( sqlcipher_sqlite3_context *context, @@ -112673,6 +117273,8 @@ static void renameTableTest( char const *zInput = (const char*)sqlcipher_sqlite3_value_text(argv[1]); int bTemp = sqlcipher_sqlite3_value_int(argv[4]); int isLegacy = (db->flags & SQLITE_LegacyAlter); + char const *zWhen = (const char*)sqlcipher_sqlite3_value_text(argv[5]); + int bNoDQS = sqlcipher_sqlite3_value_int(argv[6]); #ifndef SQLITE_OMIT_AUTHORIZATION sqlcipher_sqlite3_xauth xAuth = db->xAuth; @@ -112680,16 +117282,20 @@ static void renameTableTest( #endif UNUSED_PARAMETER(NotUsed); + if( zDb && zInput ){ int rc; Parse sParse; + int flags = db->flags; + if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); + db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL)); if( rc==SQLITE_OK ){ - if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){ + if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){ NameContext sNC; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = &sParse; - sqlcipher_sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC); + sqlcipher_sqlite3SelectPrep(&sParse, sParse.pNewTable->u.view.pSelect, &sNC); if( sParse.nErr ) rc = sParse.rc; } @@ -112700,13 +117306,17 @@ static void renameTableTest( if( rc==SQLITE_OK ){ int i1 = sqlcipher_sqlite3SchemaToIndex(db, sParse.pNewTrigger->pTabSchema); int i2 = sqlcipher_sqlite3FindDbName(db, zDb); - if( i1==i2 ) sqlcipher_sqlite3_result_int(context, 1); + if( i1==i2 ){ + /* Handle output case B */ + sqlcipher_sqlite3_result_int(context, 1); + } } } } - if( rc!=SQLITE_OK ){ - renameColumnParseError(context, 1, argv[2], argv[3], &sParse); + if( rc!=SQLITE_OK && zWhen && !sqlcipher_sqlite3WritableSchema(db) ){ + /* Output case A */ + renameColumnParseError(context, zWhen, argv[2], argv[3],&sParse); } renameParseCleanup(&sParse); } @@ -112716,14 +117326,232 @@ static void renameTableTest( #endif } +/* +** The implementation of internal UDF sqlite_drop_column(). +** +** Arguments: +** +** argv[0]: An integer - the index of the schema containing the table +** argv[1]: CREATE TABLE statement to modify. +** argv[2]: An integer - the index of the column to remove. +** +** The value returned is a string containing the CREATE TABLE statement +** with column argv[2] removed. +*/ +static void dropColumnFunc( + sqlcipher_sqlite3_context *context, + int NotUsed, + sqlcipher_sqlite3_value **argv +){ + sqlcipher_sqlite3 *db = sqlcipher_sqlite3_context_db_handle(context); + int iSchema = sqlcipher_sqlite3_value_int(argv[0]); + const char *zSql = (const char*)sqlcipher_sqlite3_value_text(argv[1]); + int iCol = sqlcipher_sqlite3_value_int(argv[2]); + const char *zDb = db->aDb[iSchema].zDbSName; + int rc; + Parse sParse; + RenameToken *pCol; + Table *pTab; + const char *zEnd; + char *zNew = 0; + +#ifndef SQLITE_OMIT_AUTHORIZATION + sqlcipher_sqlite3_xauth xAuth = db->xAuth; + db->xAuth = 0; +#endif + + UNUSED_PARAMETER(NotUsed); + rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1); + if( rc!=SQLITE_OK ) goto drop_column_done; + pTab = sParse.pNewTable; + if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){ + /* This can happen if the sqlite_schema table is corrupt */ + rc = SQLITE_CORRUPT_BKPT; + goto drop_column_done; + } + + pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName); + if( iColnCol-1 ){ + RenameToken *pEnd; + pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zCnName); + zEnd = (const char*)pEnd->t.z; + }else{ + assert( IsOrdinaryTable(pTab) ); + zEnd = (const char*)&zSql[pTab->u.tab.addColOffset]; + while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--; + } + + zNew = sqlcipher_sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd); + sqlcipher_sqlite3_result_text(context, zNew, -1, SQLITE_TRANSIENT); + sqlcipher_sqlite3_free(zNew); + +drop_column_done: + renameParseCleanup(&sParse); +#ifndef SQLITE_OMIT_AUTHORIZATION + db->xAuth = xAuth; +#endif + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3_result_error_code(context, rc); + } +} + +/* +** This function is called by the parser upon parsing an +** +** ALTER TABLE pSrc DROP COLUMN pName +** +** statement. Argument pSrc contains the possibly qualified name of the +** table being edited, and token pName the name of the column to drop. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const Token *pName){ + sqlcipher_sqlite3 *db = pParse->db; /* Database handle */ + Table *pTab; /* Table to modify */ + int iDb; /* Index of db containing pTab in aDb[] */ + const char *zDb; /* Database containing pTab ("main" etc.) */ + char *zCol = 0; /* Name of column to drop */ + int iCol; /* Index of column zCol in pTab->aCol[] */ + + /* Look up the table being altered. */ + assert( pParse->pNewTable==0 ); + assert( sqlcipher_sqlite3BtreeHoldsAllMutexes(db) ); + if( NEVER(db->mallocFailed) ) goto exit_drop_column; + pTab = sqlcipher_sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); + if( !pTab ) goto exit_drop_column; + + /* Make sure this is not an attempt to ALTER a view, virtual table or + ** system table. */ + if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_drop_column; + if( SQLITE_OK!=isRealTable(pParse, pTab, 1) ) goto exit_drop_column; + + /* Find the index of the column being dropped. */ + zCol = sqlcipher_sqlite3NameFromToken(db, pName); + if( zCol==0 ){ + assert( db->mallocFailed ); + goto exit_drop_column; + } + iCol = sqlcipher_sqlite3ColumnIndex(pTab, zCol); + if( iCol<0 ){ + sqlcipher_sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pName); + goto exit_drop_column; + } + + /* Do not allow the user to drop a PRIMARY KEY column or a column + ** constrained by a UNIQUE constraint. */ + if( pTab->aCol[iCol].colFlags & (COLFLAG_PRIMKEY|COLFLAG_UNIQUE) ){ + sqlcipher_sqlite3ErrorMsg(pParse, "cannot drop %s column: \"%s\"", + (pTab->aCol[iCol].colFlags&COLFLAG_PRIMKEY) ? "PRIMARY KEY" : "UNIQUE", + zCol + ); + goto exit_drop_column; + } + + /* Do not allow the number of columns to go to zero */ + if( pTab->nCol<=1 ){ + sqlcipher_sqlite3ErrorMsg(pParse, "cannot drop column \"%s\": no other columns exist",zCol); + goto exit_drop_column; + } + + /* Edit the sqlite_schema table */ + iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); + assert( iDb>=0 ); + zDb = db->aDb[iDb].zDbSName; +#ifndef SQLITE_OMIT_AUTHORIZATION + /* Invoke the authorization callback. */ + if( sqlcipher_sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){ + goto exit_drop_column; + } +#endif + renameTestSchema(pParse, zDb, iDb==1, "", 0); + renameFixQuotes(pParse, zDb, iDb==1); + sqlcipher_sqlite3NestedParse(pParse, + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " + "sql = sqlite_drop_column(%d, sql, %d) " + "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)" + , zDb, iDb, iCol, pTab->zName + ); + + /* Drop and reload the database schema. */ + renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop); + renameTestSchema(pParse, zDb, iDb==1, "after drop column", 1); + + /* Edit rows of table on disk */ + if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){ + int i; + int addr; + int reg; + int regRec; + Index *pPk = 0; + int nField = 0; /* Number of non-virtual columns after drop */ + int iCur; + Vdbe *v = sqlcipher_sqlite3GetVdbe(pParse); + iCur = pParse->nTab++; + sqlcipher_sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); + addr = sqlcipher_sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); + reg = ++pParse->nMem; + if( HasRowid(pTab) ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_Rowid, iCur, reg); + pParse->nMem += pTab->nCol; + }else{ + pPk = sqlcipher_sqlite3PrimaryKeyIndex(pTab); + pParse->nMem += pPk->nColumn; + for(i=0; inKeyCol; i++){ + sqlcipher_sqlite3VdbeAddOp3(v, OP_Column, iCur, i, reg+i+1); + } + nField = pPk->nKeyCol; + } + regRec = ++pParse->nMem; + for(i=0; inCol; i++){ + if( i!=iCol && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ + int regOut; + if( pPk ){ + int iPos = sqlcipher_sqlite3TableColumnToIndex(pPk, i); + int iColPos = sqlcipher_sqlite3TableColumnToIndex(pPk, iCol); + if( iPosnKeyCol ) continue; + regOut = reg+1+iPos-(iPos>iColPos); + }else{ + regOut = reg+1+nField; + } + if( i==pTab->iPKey ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_Null, 0, regOut); + }else{ + sqlcipher_sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut); + } + nField++; + } + } + if( nField==0 ){ + /* dbsqlfuzz 5f09e7bcc78b4954d06bf9f2400d7715f48d1fef */ + pParse->nMem++; + sqlcipher_sqlite3VdbeAddOp2(v, OP_Null, 0, reg+1); + nField = 1; + } + sqlcipher_sqlite3VdbeAddOp3(v, OP_MakeRecord, reg+1, nField, regRec); + if( pPk ){ + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iCur, regRec, reg+1, pPk->nKeyCol); + }else{ + sqlcipher_sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, reg); + } + sqlcipher_sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION); + + sqlcipher_sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v); + sqlcipher_sqlite3VdbeJumpHere(v, addr); + } + +exit_drop_column: + sqlcipher_sqlite3DbFree(db, zCol); + sqlcipher_sqlite3SrcListDelete(db, pSrc); +} + /* ** Register built-in functions used to help implement ALTER TABLE */ SQLITE_PRIVATE void sqlcipher_sqlite3AlterFunctions(void){ static FuncDef aAlterTableFuncs[] = { - INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), - INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), - INTERNAL_FUNCTION(sqlite_rename_test, 5, renameTableTest), + INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), + INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), + INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest), + INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc), + INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc), }; sqlcipher_sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); } @@ -113166,7 +117994,6 @@ static void statInit( + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample); } #endif - db = sqlcipher_sqlite3_context_db_handle(context); p = sqlcipher_sqlite3DbMallocZero(db, n); if( p==0 ){ sqlcipher_sqlite3_result_error_nomem(context); @@ -113581,32 +118408,29 @@ static void statGet( ** * "WHERE a=? AND b=?" matches 2 rows. ** ** If D is the count of distinct values and K is the total number of - ** rows, then each estimate is computed as: + ** rows, then each estimate is usually computed as: ** ** I = (K+D-1)/D + ** + ** In other words, I is K/D rounded up to the next whole integer. + ** However, if I is between 1.0 and 1.1 (in other words if I is + ** close to 1.0 but just a little larger) then do not round up but + ** instead keep the I value at 1.0. */ - char *z; - int i; - - char *zRet = sqlcipher_sqlite3MallocZero( (p->nKeyCol+1)*25 ); - if( zRet==0 ){ - sqlcipher_sqlite3_result_error_nomem(context); - return; - } + sqlcipher_sqlite3_str sStat; /* Text of the constructed "stat" line */ + int i; /* Loop counter */ - sqlcipher_sqlite3_snprintf(24, zRet, "%llu", + sqlcipher_sqlite3StrAccumInit(&sStat, 0, 0, 0, (p->nKeyCol+1)*100); + sqlcipher_sqlite3_str_appendf(&sStat, "%llu", p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow); - z = zRet + sqlcipher_sqlite3Strlen30(zRet); for(i=0; inKeyCol; i++){ u64 nDistinct = p->current.anDLt[i] + 1; u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; - sqlcipher_sqlite3_snprintf(24, z, " %llu", iVal); - z += sqlcipher_sqlite3Strlen30(z); + if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1; + sqlcipher_sqlite3_str_appendf(&sStat, " %llu", iVal); assert( p->current.anEq[i] ); } - assert( z[0]=='\0' && z>zRet ); - - sqlcipher_sqlite3_result_text(context, zRet, -1, sqlcipher_sqlite3_free); + sqlcipher_sqlite3ResultStrAccum(context, &sStat); } #ifdef SQLITE_ENABLE_STAT4 else if( eCall==STAT_GET_ROWID ){ @@ -113625,6 +118449,8 @@ static void statGet( } }else{ tRowcnt *aCnt = 0; + sqlcipher_sqlite3_str sStat; + int i; assert( p->iGetnSample ); switch( eCall ){ @@ -113636,23 +118462,12 @@ static void statGet( break; } } - - { - char *zRet = sqlcipher_sqlite3MallocZero(p->nCol * 25); - if( zRet==0 ){ - sqlcipher_sqlite3_result_error_nomem(context); - }else{ - int i; - char *z = zRet; - for(i=0; inCol; i++){ - sqlcipher_sqlite3_snprintf(24, z, "%llu ", (u64)aCnt[i]); - z += sqlcipher_sqlite3Strlen30(z); - } - assert( z[0]=='\0' && z>zRet ); - z[-1] = '\0'; - sqlcipher_sqlite3_result_text(context, zRet, -1, sqlcipher_sqlite3_free); - } + sqlcipher_sqlite3StrAccumInit(&sStat, 0, 0, 0, p->nCol*100); + for(i=0; inCol; i++){ + sqlcipher_sqlite3_str_appendf(&sStat, "%llu ", (u64)aCnt[i]); } + if( sStat.nChar ) sStat.nChar--; + sqlcipher_sqlite3ResultStrAccum(context, &sStat); } #endif /* SQLITE_ENABLE_STAT4 */ #ifndef SQLITE_DEBUG @@ -113701,7 +118516,7 @@ static void analyzeVdbeCommentIndexWithColumnName( }else if( i==XN_EXPR ){ VdbeComment((v,"%s.expr(%d)",pIdx->zName, k)); }else{ - VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zName)); + VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zCnName)); } } #else @@ -113748,7 +118563,7 @@ static void analyzeOneTable( if( v==0 || NEVER(pTab==0) ){ return; } - if( pTab->tnum==0 ){ + if( !IsOrdinaryTable(pTab) ){ /* Do not gather statistics on views or virtual tables */ return; } @@ -113775,7 +118590,7 @@ static void analyzeOneTable( memcpy(pStat1->zName, "sqlite_stat1", 13); pStat1->nCol = 3; pStat1->iPKey = -1; - sqlcipher_sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNBLOB); + sqlcipher_sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNAMIC); } #endif @@ -114573,9 +119388,12 @@ static int loadStatTbl( */ static int loadStat4(sqlcipher_sqlite3 *db, const char *zDb){ int rc = SQLITE_OK; /* Result codes from subroutines */ + const Table *pStat4; assert( db->lookaside.bDisable ); - if( sqlcipher_sqlite3FindTable(db, "sqlite_stat4", zDb) ){ + if( (pStat4 = sqlcipher_sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 + && IsOrdinaryTable(pStat4) + ){ rc = loadStatTbl(db, "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx", "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", @@ -114612,6 +119430,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3AnalysisLoad(sqlcipher_sqlite3 *db, int iDb) char *zSql; int rc = SQLITE_OK; Schema *pSchema = db->aDb[iDb].pSchema; + const Table *pStat1; assert( iDb>=0 && iDbnDb ); assert( db->aDb[iDb].pBt!=0 ); @@ -114634,7 +119453,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3AnalysisLoad(sqlcipher_sqlite3 *db, int iDb) /* Load new statistics out of the sqlite_stat1 table */ sInfo.db = db; sInfo.zDatabase = db->aDb[iDb].zDbSName; - if( sqlcipher_sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){ + if( (pStat1 = sqlcipher_sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)) + && IsOrdinaryTable(pStat1) + ){ zSql = sqlcipher_sqlite3MPrintf(db, "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase); if( zSql==0 ){ @@ -114774,7 +119595,7 @@ static void attachFunc( if( zFile==0 ) zFile = ""; if( zName==0 ) zName = ""; -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE # define REOPEN_AS_MEMDB(db) (db->init.reopenMemdb) #else # define REOPEN_AS_MEMDB(db) (0) @@ -114880,8 +119701,8 @@ static void attachFunc( /* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC if( rc==SQLITE_OK ){ - extern int sqlcipher_sqlite3CodecAttach(sqlcipher_sqlite3*, int, const void*, int); - extern void sqlcipher_sqlite3CodecGetKey(sqlcipher_sqlite3*, int, void**, int*); + extern int sqlcipherCodecAttach(sqlcipher_sqlite3*, int, const void*, int); + extern void sqlcipherCodecGetKey(sqlcipher_sqlite3*, int, void**, int*); int nKey; char *zKey; int t = sqlcipher_sqlite3_value_type(argv[2]); @@ -114896,16 +119717,16 @@ static void attachFunc( case SQLITE_BLOB: nKey = sqlcipher_sqlite3_value_bytes(argv[2]); zKey = (char *)sqlcipher_sqlite3_value_blob(argv[2]); - rc = sqlcipher_sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + rc = sqlcipherCodecAttach(db, db->nDb-1, zKey, nKey); break; case SQLITE_NULL: /* No key specified. Use the key from URI filename, or if none, ** use the key from the main database. */ if( sqlcipher_sqlite3CodecQueryParameters(db, zName, zPath)==0 ){ - sqlcipher_sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); + sqlcipherCodecGetKey(db, 0, (void**)&zKey, &nKey); if( nKey || sqlcipher_sqlite3BtreeGetRequestedReserve(db->aDb[0].pBt)>0 ){ - rc = sqlcipher_sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + rc = sqlcipherCodecAttach(db, db->nDb-1, zKey, nKey); } } break; @@ -115062,17 +119883,18 @@ static void codeAttach( sName.pParse = pParse; if( - SQLITE_OK!=(rc = resolveAttachExpr(&sName, pFilename)) || - SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) || - SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey)) + SQLITE_OK!=resolveAttachExpr(&sName, pFilename) || + SQLITE_OK!=resolveAttachExpr(&sName, pDbname) || + SQLITE_OK!=resolveAttachExpr(&sName, pKey) ){ goto attach_end; } #ifndef SQLITE_OMIT_AUTHORIZATION - if( pAuthArg ){ + if( ALWAYS(pAuthArg) ){ char *zAuthArg; if( pAuthArg->op==TK_STRING ){ + assert( !ExprHasProperty(pAuthArg, EP_IntValue) ); zAuthArg = pAuthArg->u.zToken; }else{ zAuthArg = 0; @@ -115149,6 +119971,69 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbnam } #endif /* SQLITE_OMIT_ATTACH */ +/* +** Expression callback used by sqlcipher_sqlite3FixAAAA() routines. +*/ +static int fixExprCb(Walker *p, Expr *pExpr){ + DbFixer *pFix = p->u.pFix; + if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL); + if( pExpr->op==TK_VARIABLE ){ + if( pFix->pParse->db->init.busy ){ + pExpr->op = TK_NULL; + }else{ + sqlcipher_sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType); + return WRC_Abort; + } + } + return WRC_Continue; +} + +/* +** Select callback used by sqlcipher_sqlite3FixAAAA() routines. +*/ +static int fixSelectCb(Walker *p, Select *pSelect){ + DbFixer *pFix = p->u.pFix; + int i; + SrcItem *pItem; + sqlcipher_sqlite3 *db = pFix->pParse->db; + int iDb = sqlcipher_sqlite3FindDbName(db, pFix->zDb); + SrcList *pList = pSelect->pSrc; + + if( NEVER(pList==0) ) return WRC_Continue; + for(i=0, pItem=pList->a; inSrc; i++, pItem++){ + if( pFix->bTemp==0 ){ + if( pItem->zDatabase ){ + if( iDb!=sqlcipher_sqlite3FindDbName(db, pItem->zDatabase) ){ + sqlcipher_sqlite3ErrorMsg(pFix->pParse, + "%s %T cannot reference objects in database %s", + pFix->zType, pFix->pName, pItem->zDatabase); + return WRC_Abort; + } + sqlcipher_sqlite3DbFree(db, pItem->zDatabase); + pItem->zDatabase = 0; + pItem->fg.notCte = 1; + } + pItem->pSchema = pFix->pSchema; + pItem->fg.fromDDL = 1; + } +#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) + if( pList->a[i].fg.isUsing==0 + && sqlcipher_sqlite3WalkExpr(&pFix->w, pList->a[i].u3.pOn) + ){ + return WRC_Abort; + } +#endif + } + if( pSelect->pWith ){ + for(i=0; ipWith->nCte; i++){ + if( sqlcipher_sqlite3WalkSelect(p, pSelect->pWith->a[i].pSelect) ){ + return WRC_Abort; + } + } + } + return WRC_Continue; +} + /* ** Initialize a DbFixer structure. This routine must be called prior ** to passing the structure to one of the sqliteFixAAAA() routines below. @@ -115160,9 +120045,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FixInit( const char *zType, /* "view", "trigger", or "index" */ const Token *pName /* Name of the view, trigger, or index */ ){ - sqlcipher_sqlite3 *db; - - db = pParse->db; + sqlcipher_sqlite3 *db = pParse->db; assert( db->nDb>iDb ); pFix->pParse = pParse; pFix->zDb = db->aDb[iDb].zDbSName; @@ -115170,6 +120053,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FixInit( pFix->zType = zType; pFix->pName = pName; pFix->bTemp = (iDb==1); + pFix->w.pParse = pParse; + pFix->w.xExprCallback = fixExprCb; + pFix->w.xSelectCallback = fixSelectCb; + pFix->w.xSelectCallback2 = sqlcipher_sqlite3WalkWinDefnDummyCallback; + pFix->w.walkerDepth = 0; + pFix->w.eCode = 0; + pFix->w.u.pFix = pFix; } /* @@ -115190,115 +120080,27 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FixSrcList( DbFixer *pFix, /* Context of the fixation */ SrcList *pList /* The Source list to check and modify */ ){ - int i; - struct SrcList_item *pItem; - sqlcipher_sqlite3 *db = pFix->pParse->db; - int iDb = sqlcipher_sqlite3FindDbName(db, pFix->zDb); - - if( NEVER(pList==0) ) return 0; - - for(i=0, pItem=pList->a; inSrc; i++, pItem++){ - if( pFix->bTemp==0 ){ - if( pItem->zDatabase && iDb!=sqlcipher_sqlite3FindDbName(db, pItem->zDatabase) ){ - sqlcipher_sqlite3ErrorMsg(pFix->pParse, - "%s %T cannot reference objects in database %s", - pFix->zType, pFix->pName, pItem->zDatabase); - return 1; - } - sqlcipher_sqlite3DbFree(db, pItem->zDatabase); - pItem->zDatabase = 0; - pItem->pSchema = pFix->pSchema; - pItem->fg.fromDDL = 1; - } -#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) - if( sqlcipher_sqlite3FixSelect(pFix, pItem->pSelect) ) return 1; - if( sqlcipher_sqlite3FixExpr(pFix, pItem->pOn) ) return 1; -#endif - if( pItem->fg.isTabFunc && sqlcipher_sqlite3FixExprList(pFix, pItem->u1.pFuncArg) ){ - return 1; - } + int res = 0; + if( pList ){ + Select s; + memset(&s, 0, sizeof(s)); + s.pSrc = pList; + res = sqlcipher_sqlite3WalkSelect(&pFix->w, &s); } - return 0; + return res; } #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) SQLITE_PRIVATE int sqlcipher_sqlite3FixSelect( DbFixer *pFix, /* Context of the fixation */ Select *pSelect /* The SELECT statement to be fixed to one database */ ){ - while( pSelect ){ - if( sqlcipher_sqlite3FixExprList(pFix, pSelect->pEList) ){ - return 1; - } - if( sqlcipher_sqlite3FixSrcList(pFix, pSelect->pSrc) ){ - return 1; - } - if( sqlcipher_sqlite3FixExpr(pFix, pSelect->pWhere) ){ - return 1; - } - if( sqlcipher_sqlite3FixExprList(pFix, pSelect->pGroupBy) ){ - return 1; - } - if( sqlcipher_sqlite3FixExpr(pFix, pSelect->pHaving) ){ - return 1; - } - if( sqlcipher_sqlite3FixExprList(pFix, pSelect->pOrderBy) ){ - return 1; - } - if( sqlcipher_sqlite3FixExpr(pFix, pSelect->pLimit) ){ - return 1; - } - if( pSelect->pWith ){ - int i; - for(i=0; ipWith->nCte; i++){ - if( sqlcipher_sqlite3FixSelect(pFix, pSelect->pWith->a[i].pSelect) ){ - return 1; - } - } - } - pSelect = pSelect->pPrior; - } - return 0; + return sqlcipher_sqlite3WalkSelect(&pFix->w, pSelect); } SQLITE_PRIVATE int sqlcipher_sqlite3FixExpr( DbFixer *pFix, /* Context of the fixation */ Expr *pExpr /* The expression to be fixed to one database */ ){ - while( pExpr ){ - if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL); - if( pExpr->op==TK_VARIABLE ){ - if( pFix->pParse->db->init.busy ){ - pExpr->op = TK_NULL; - }else{ - sqlcipher_sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType); - return 1; - } - } - if( ExprHasProperty(pExpr, EP_TokenOnly|EP_Leaf) ) break; - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - if( sqlcipher_sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1; - }else{ - if( sqlcipher_sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1; - } - if( sqlcipher_sqlite3FixExpr(pFix, pExpr->pRight) ){ - return 1; - } - pExpr = pExpr->pLeft; - } - return 0; -} -SQLITE_PRIVATE int sqlcipher_sqlite3FixExprList( - DbFixer *pFix, /* Context of the fixation */ - ExprList *pList /* The expression to be fixed to one database */ -){ - int i; - struct ExprList_item *pItem; - if( pList==0 ) return 0; - for(i=0, pItem=pList->a; inExpr; i++, pItem++){ - if( sqlcipher_sqlite3FixExpr(pFix, pItem->pExpr) ){ - return 1; - } - } - return 0; + return sqlcipher_sqlite3WalkExpr(&pFix->w, pExpr); } #endif @@ -115308,32 +120110,30 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FixTriggerStep( TriggerStep *pStep /* The trigger step be fixed to one database */ ){ while( pStep ){ - if( sqlcipher_sqlite3FixSelect(pFix, pStep->pSelect) ){ - return 1; - } - if( sqlcipher_sqlite3FixExpr(pFix, pStep->pWhere) ){ - return 1; - } - if( sqlcipher_sqlite3FixExprList(pFix, pStep->pExprList) ){ - return 1; - } - if( pStep->pFrom && sqlcipher_sqlite3FixSrcList(pFix, pStep->pFrom) ){ + if( sqlcipher_sqlite3WalkSelect(&pFix->w, pStep->pSelect) + || sqlcipher_sqlite3WalkExpr(&pFix->w, pStep->pWhere) + || sqlcipher_sqlite3WalkExprList(&pFix->w, pStep->pExprList) + || sqlcipher_sqlite3FixSrcList(pFix, pStep->pFrom) + ){ return 1; } #ifndef SQLITE_OMIT_UPSERT - if( pStep->pUpsert ){ - Upsert *pUp = pStep->pUpsert; - if( sqlcipher_sqlite3FixExprList(pFix, pUp->pUpsertTarget) - || sqlcipher_sqlite3FixExpr(pFix, pUp->pUpsertTargetWhere) - || sqlcipher_sqlite3FixExprList(pFix, pUp->pUpsertSet) - || sqlcipher_sqlite3FixExpr(pFix, pUp->pUpsertWhere) - ){ - return 1; + { + Upsert *pUp; + for(pUp=pStep->pUpsert; pUp; pUp=pUp->pNextUpsert){ + if( sqlcipher_sqlite3WalkExprList(&pFix->w, pUp->pUpsertTarget) + || sqlcipher_sqlite3WalkExpr(&pFix->w, pUp->pUpsertTargetWhere) + || sqlcipher_sqlite3WalkExprList(&pFix->w, pUp->pUpsertSet) + || sqlcipher_sqlite3WalkExpr(&pFix->w, pUp->pUpsertWhere) + ){ + return 1; + } } } #endif pStep = pStep->pNext; } + return 0; } #endif @@ -115485,7 +120285,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AuthRead( Schema *pSchema, /* The schema of the expression */ SrcList *pTabList /* All table that pExpr might refer to */ ){ - sqlcipher_sqlite3 *db = pParse->db; Table *pTab = 0; /* The table being read */ const char *zCol; /* Name of the column of the table */ int iSrc; /* Index in pTabList->a[] of table being read */ @@ -115493,8 +120292,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AuthRead( int iCol; /* Index of column in table */ assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER ); - assert( !IN_RENAME_OBJECT || db->xAuth==0 ); - if( db->xAuth==0 ) return; + assert( !IN_RENAME_OBJECT ); + assert( pParse->db->xAuth!=0 ); iDb = sqlcipher_sqlite3SchemaToIndex(pParse->db, pSchema); if( iDb<0 ){ /* An attempt to read a column out of a subquery or other @@ -115506,7 +120305,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AuthRead( pTab = pParse->pTriggerTab; }else{ assert( pTabList ); - for(iSrc=0; ALWAYS(iSrcnSrc); iSrc++){ + for(iSrc=0; iSrcnSrc; iSrc++){ if( pExpr->iTable==pTabList->a[iSrc].iCursor ){ pTab = pTabList->a[iSrc].pTab; break; @@ -115514,18 +120313,18 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AuthRead( } } iCol = pExpr->iColumn; - if( NEVER(pTab==0) ) return; + if( pTab==0 ) return; if( iCol>=0 ){ assert( iColnCol ); - zCol = pTab->aCol[iCol].zName; + zCol = pTab->aCol[iCol].zCnName; }else if( pTab->iPKey>=0 ){ assert( pTab->iPKeynCol ); - zCol = pTab->aCol[pTab->iPKey].zName; + zCol = pTab->aCol[pTab->iPKey].zCnName; }else{ zCol = "ROWID"; } - assert( iDb>=0 && iDbnDb ); + assert( iDb>=0 && iDbdb->nDb ); if( SQLITE_IGNORE==sqlcipher_sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){ pExpr->op = TK_NULL; } @@ -115551,11 +120350,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3AuthCheck( ** or if the parser is being invoked from within sqlcipher_sqlite3_declare_vtab. */ assert( !IN_RENAME_OBJECT || db->xAuth==0 ); - if( db->init.busy || IN_SPECIAL_PARSE ){ - return SQLITE_OK; - } - - if( db->xAuth==0 ){ + if( db->xAuth==0 || db->init.busy || IN_SPECIAL_PARSE ){ return SQLITE_OK; } @@ -115664,7 +120459,7 @@ struct TableLock { ** code to make the lock occur is generated by a later call to ** codeTableLocks() which occurs during sqlcipher_sqlite3FinishCoding(). */ -SQLITE_PRIVATE void sqlcipher_sqlite3TableLock( +static SQLITE_NOINLINE void lockTable( Parse *pParse, /* Parsing context */ int iDb, /* Index of the database containing the table to lock */ Pgno iTab, /* Root page number of the table to be locked */ @@ -115677,8 +120472,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3TableLock( TableLock *p; assert( iDb>=0 ); - if( iDb==1 ) return; - if( !sqlcipher_sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return; pToplevel = sqlcipher_sqlite3ParseToplevel(pParse); for(i=0; inTableLock; i++){ p = &pToplevel->aTableLock[i]; @@ -115702,6 +120495,17 @@ SQLITE_PRIVATE void sqlcipher_sqlite3TableLock( sqlcipher_sqlite3OomFault(pToplevel->db); } } +SQLITE_PRIVATE void sqlcipher_sqlite3TableLock( + Parse *pParse, /* Parsing context */ + int iDb, /* Index of the database containing the table to lock */ + Pgno iTab, /* Root page number of the table to be locked */ + u8 isWriteLock, /* True for a write lock */ + const char *zName /* Name of the table to be locked */ +){ + if( iDb==1 ) return; + if( !sqlcipher_sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return; + lockTable(pParse, iDb, iTab, isWriteLock, zName); +} /* ** Code an OP_TableLock instruction for each table locked by the @@ -115752,19 +120556,50 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FinishCoding(Parse *pParse){ assert( pParse->pToplevel==0 ); db = pParse->db; + assert( db->pParse==pParse ); if( pParse->nested ) return; - if( db->mallocFailed || pParse->nErr ){ - if( pParse->rc==SQLITE_OK ) pParse->rc = SQLITE_ERROR; + if( pParse->nErr ){ + if( db->mallocFailed ) pParse->rc = SQLITE_NOMEM; return; } + assert( db->mallocFailed==0 ); /* Begin by generating some termination code at the end of the ** vdbe program */ - v = sqlcipher_sqlite3GetVdbe(pParse); + v = pParse->pVdbe; + if( v==0 ){ + if( db->init.busy ){ + pParse->rc = SQLITE_DONE; + return; + } + v = sqlcipher_sqlite3GetVdbe(pParse); + if( v==0 ) pParse->rc = SQLITE_ERROR; + } assert( !pParse->isMultiWrite || sqlcipher_sqlite3VdbeAssertMayAbort(v, pParse->mayAbort)); if( v ){ + if( pParse->bReturning ){ + Returning *pReturning = pParse->u1.pReturning; + int addrRewind; + int i; + int reg; + + if( pReturning->nRetCol ){ + sqlcipher_sqlite3VdbeAddOp0(v, OP_FkCheck); + addrRewind = + sqlcipher_sqlite3VdbeAddOp1(v, OP_Rewind, pReturning->iRetCur); + VdbeCoverage(v); + reg = pReturning->iRetReg; + for(i=0; inRetCol; i++){ + sqlcipher_sqlite3VdbeAddOp3(v, OP_Column, pReturning->iRetCur, i, reg+i); + } + sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, reg, i); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1); + VdbeCoverage(v); + sqlcipher_sqlite3VdbeJumpHere(v, addrRewind); + } + } sqlcipher_sqlite3VdbeAddOp0(v, OP_Halt); #if SQLITE_USER_AUTHENTICATION @@ -115790,7 +120625,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FinishCoding(Parse *pParse){ int iDb, i; assert( sqlcipher_sqlite3VdbeGetOp(v, 0)->opcode==OP_Init ); sqlcipher_sqlite3VdbeJumpHere(v, 0); - for(iDb=0; iDbnDb; iDb++){ + assert( db->nDb>0 ); + iDb = 0; + do{ Schema *pSchema; if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue; sqlcipher_sqlite3VdbeUsesBtree(v, iDb); @@ -115805,7 +120642,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FinishCoding(Parse *pParse){ if( db->init.busy==0 ) sqlcipher_sqlite3VdbeChangeP5(v, 1); VdbeComment((v, "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite)); - } + }while( ++iDbnDb ); #ifndef SQLITE_OMIT_VIRTUALTABLE for(i=0; inVtabLock; i++){ char *vtab = (char *)sqlcipher_sqlite3GetVTable(db, pParse->apVtabLock[i]); @@ -115842,15 +120679,23 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FinishCoding(Parse *pParse){ } } + if( pParse->bReturning ){ + Returning *pRet = pParse->u1.pReturning; + if( pRet->nRetCol ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol); + } + } + /* Finally, jump back to the beginning of the executable code. */ sqlcipher_sqlite3VdbeGoto(v, 1); } } - /* Get the VDBE program ready for execution */ - if( v && pParse->nErr==0 && !db->mallocFailed ){ + assert( v!=0 || pParse->nErr ); + assert( db->mallocFailed==0 || pParse->nErr ); + if( pParse->nErr==0 ){ /* A minimum of one cursor is required if autoincrement is used * See ticket [a696379c1f08866] */ assert( pParse->pAinc==0 || pParse->nTab>0 ); @@ -115864,20 +120709,21 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FinishCoding(Parse *pParse){ /* ** Run the parser and code generator recursively in order to generate ** code for the SQL statement given onto the end of the pParse context -** currently under construction. When the parser is run recursively -** this way, the final OP_Halt is not appended and other initialization -** and finalization steps are omitted because those are handling by the -** outermost parser. +** currently under construction. Notes: +** +** * The final OP_Halt is not appended and other initialization +** and finalization steps are omitted because those are handling by the +** outermost parser. ** -** Not everything is nestable. This facility is designed to permit -** INSERT, UPDATE, and DELETE operations against the schema table. Use -** care if you decide to try to use this routine for some other purposes. +** * Built-in SQL functions always take precedence over application-defined +** SQL functions. In other words, it is not possible to override a +** built-in function. */ SQLITE_PRIVATE void sqlcipher_sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){ va_list ap; char *zSql; - char *zErrMsg = 0; sqlcipher_sqlite3 *db = pParse->db; + u32 savedDbFlags = db->mDbFlags; char saveBuf[PARSE_TAIL_SZ]; if( pParse->nErr ) return; @@ -115896,8 +120742,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3NestedParse(Parse *pParse, const char *zFor pParse->nested++; memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ); memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ); - sqlcipher_sqlite3RunParser(pParse, zSql, &zErrMsg); - sqlcipher_sqlite3DbFree(db, zErrMsg); + db->mDbFlags |= DBFLAG_PreferBuiltin; + sqlcipher_sqlite3RunParser(pParse, zSql); + db->mDbFlags = savedDbFlags; sqlcipher_sqlite3DbFree(db, zSql); memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ); pParse->nested--; @@ -115954,17 +120801,17 @@ SQLITE_PRIVATE Table *sqlcipher_sqlite3FindTable(sqlcipher_sqlite3 *db, const ch p = sqlcipher_sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName); if( p==0 && sqlcipher_sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ if( i==1 ){ - if( sqlcipher_sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0 - || sqlcipher_sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 - || sqlcipher_sqlite3StrICmp(zName+7, &DFLT_SCHEMA_TABLE[7])==0 + if( sqlcipher_sqlite3StrICmp(zName+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 + || sqlcipher_sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 + || sqlcipher_sqlite3StrICmp(zName+7, &LEGACY_SCHEMA_TABLE[7])==0 ){ p = sqlcipher_sqlite3HashFind(&db->aDb[1].pSchema->tblHash, - DFLT_TEMP_SCHEMA_TABLE); + LEGACY_TEMP_SCHEMA_TABLE); } }else{ - if( sqlcipher_sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){ + if( sqlcipher_sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 ){ p = sqlcipher_sqlite3HashFind(&db->aDb[i].pSchema->tblHash, - DFLT_SCHEMA_TABLE); + LEGACY_SCHEMA_TABLE); } } } @@ -115982,11 +120829,11 @@ SQLITE_PRIVATE Table *sqlcipher_sqlite3FindTable(sqlcipher_sqlite3 *db, const ch if( p ) break; } if( p==0 && sqlcipher_sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ - if( sqlcipher_sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){ - p = sqlcipher_sqlite3HashFind(&db->aDb[0].pSchema->tblHash, DFLT_SCHEMA_TABLE); - }else if( sqlcipher_sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0 ){ + if( sqlcipher_sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 ){ + p = sqlcipher_sqlite3HashFind(&db->aDb[0].pSchema->tblHash, LEGACY_SCHEMA_TABLE); + }else if( sqlcipher_sqlite3StrICmp(zName+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){ p = sqlcipher_sqlite3HashFind(&db->aDb[1].pSchema->tblHash, - DFLT_TEMP_SCHEMA_TABLE); + LEGACY_TEMP_SCHEMA_TABLE); } } } @@ -116026,12 +120873,13 @@ SQLITE_PRIVATE Table *sqlcipher_sqlite3LocateTable( /* If zName is the not the name of a table in the schema created using ** CREATE, then check to see if it is the name of an virtual table that ** can be an eponymous virtual table. */ - if( pParse->disableVtab==0 ){ + if( pParse->disableVtab==0 && db->init.busy==0 ){ Module *pMod = (Module*)sqlcipher_sqlite3HashFind(&db->aModule, zName); if( pMod==0 && sqlcipher_sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ pMod = sqlcipher_sqlite3PragmaVtabRegister(db, zName); } if( pMod && sqlcipher_sqlite3VtabEponymousTableInit(pParse, pMod) ){ + testcase( pMod->pEpoTab==0 ); return pMod->pEpoTab; } } @@ -116049,6 +120897,8 @@ SQLITE_PRIVATE Table *sqlcipher_sqlite3LocateTable( }else{ sqlcipher_sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); } + }else{ + assert( HasRowid(p) || p->iPKey<0 ); } return p; @@ -116066,7 +120916,7 @@ SQLITE_PRIVATE Table *sqlcipher_sqlite3LocateTable( SQLITE_PRIVATE Table *sqlcipher_sqlite3LocateTableItem( Parse *pParse, u32 flags, - struct SrcList_item *p + SrcItem *p ){ const char *zDb; assert( p->pSchema==0 || p->zDatabase==0 ); @@ -116079,6 +120929,22 @@ SQLITE_PRIVATE Table *sqlcipher_sqlite3LocateTableItem( return sqlcipher_sqlite3LocateTable(pParse, flags, p->zName, zDb); } +/* +** Return the preferred table name for system tables. Translate legacy +** names into the new preferred names, as appropriate. +*/ +SQLITE_PRIVATE const char *sqlcipher_sqlite3PreferredTableName(const char *zName){ + if( sqlcipher_sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ + if( sqlcipher_sqlite3StrICmp(zName+7, &LEGACY_SCHEMA_TABLE[7])==0 ){ + return PREFERRED_SCHEMA_TABLE; + } + if( sqlcipher_sqlite3StrICmp(zName+7, &LEGACY_TEMP_SCHEMA_TABLE[7])==0 ){ + return PREFERRED_TEMP_SCHEMA_TABLE; + } + } + return zName; +} + /* ** Locate the in-memory structure that describes ** a particular index given the name of that index @@ -116243,6 +121109,84 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CommitInternalChanges(sqlcipher_sqlite3 *db db->mDbFlags &= ~DBFLAG_SchemaChange; } +/* +** Set the expression associated with a column. This is usually +** the DEFAULT value, but might also be the expression that computes +** the value for a generated column. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3ColumnSetExpr( + Parse *pParse, /* Parsing context */ + Table *pTab, /* The table containing the column */ + Column *pCol, /* The column to receive the new DEFAULT expression */ + Expr *pExpr /* The new default expression */ +){ + ExprList *pList; + assert( IsOrdinaryTable(pTab) ); + pList = pTab->u.tab.pDfltList; + if( pCol->iDflt==0 + || NEVER(pList==0) + || NEVER(pList->nExpriDflt) + ){ + pCol->iDflt = pList==0 ? 1 : pList->nExpr+1; + pTab->u.tab.pDfltList = sqlcipher_sqlite3ExprListAppend(pParse, pList, pExpr); + }else{ + sqlcipher_sqlite3ExprDelete(pParse->db, pList->a[pCol->iDflt-1].pExpr); + pList->a[pCol->iDflt-1].pExpr = pExpr; + } +} + +/* +** Return the expression associated with a column. The expression might be +** the DEFAULT clause or the AS clause of a generated column. +** Return NULL if the column has no associated expression. +*/ +SQLITE_PRIVATE Expr *sqlcipher_sqlite3ColumnExpr(Table *pTab, Column *pCol){ + if( pCol->iDflt==0 ) return 0; + if( NEVER(!IsOrdinaryTable(pTab)) ) return 0; + if( NEVER(pTab->u.tab.pDfltList==0) ) return 0; + if( NEVER(pTab->u.tab.pDfltList->nExpriDflt) ) return 0; + return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; +} + +/* +** Set the collating sequence name for a column. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3ColumnSetColl( + sqlcipher_sqlite3 *db, + Column *pCol, + const char *zColl +){ + i64 nColl; + i64 n; + char *zNew; + assert( zColl!=0 ); + n = sqlcipher_sqlite3Strlen30(pCol->zCnName) + 1; + if( pCol->colFlags & COLFLAG_HASTYPE ){ + n += sqlcipher_sqlite3Strlen30(pCol->zCnName+n) + 1; + } + nColl = sqlcipher_sqlite3Strlen30(zColl) + 1; + zNew = sqlcipher_sqlite3DbRealloc(db, pCol->zCnName, nColl+n); + if( zNew ){ + pCol->zCnName = zNew; + memcpy(pCol->zCnName + n, zColl, nColl); + pCol->colFlags |= COLFLAG_HASCOLL; + } +} + +/* +** Return the collating squence name for a column +*/ +SQLITE_PRIVATE const char *sqlcipher_sqlite3ColumnColl(Column *pCol){ + const char *z; + if( (pCol->colFlags & COLFLAG_HASCOLL)==0 ) return 0; + z = pCol->zCnName; + while( *z ){ z++; } + if( pCol->colFlags & COLFLAG_HASTYPE ){ + do{ z++; }while( *z ); + } + return z+1; +} + /* ** Delete memory allocated for the column names of a table or view (the ** Table.aCol[] array). @@ -116253,12 +121197,20 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DeleteColumnNames(sqlcipher_sqlite3 *db, Ta assert( pTable!=0 ); if( (pCol = pTable->aCol)!=0 ){ for(i=0; inCol; i++, pCol++){ - assert( pCol->zName==0 || pCol->hName==sqlcipher_sqlite3StrIHash(pCol->zName) ); - sqlcipher_sqlite3DbFree(db, pCol->zName); - sqlcipher_sqlite3ExprDelete(db, pCol->pDflt); - sqlcipher_sqlite3DbFree(db, pCol->zColl); + assert( pCol->zCnName==0 || pCol->hName==sqlcipher_sqlite3StrIHash(pCol->zCnName) ); + sqlcipher_sqlite3DbFree(db, pCol->zCnName); } sqlcipher_sqlite3DbFree(db, pTable->aCol); + if( IsOrdinaryTable(pTable) ){ + sqlcipher_sqlite3ExprListDelete(db, pTable->u.tab.pDfltList); + } + if( db==0 || db->pnBytesFreed==0 ){ + pTable->aCol = 0; + pTable->nCol = 0; + if( IsOrdinaryTable(pTable) ){ + pTable->u.tab.pDfltList = 0; + } + } } } @@ -116310,19 +121262,25 @@ static void SQLITE_NOINLINE deleteTable(sqlcipher_sqlite3 *db, Table *pTable){ sqlcipher_sqlite3FreeIndex(db, pIndex); } - /* Delete any foreign keys attached to this table. */ - sqlcipher_sqlite3FkDelete(db, pTable); + if( IsOrdinaryTable(pTable) ){ + sqlcipher_sqlite3FkDelete(db, pTable); + } +#ifndef SQLITE_OMIT_VIRTUAL_TABLE + else if( IsVirtual(pTable) ){ + sqlcipher_sqlite3VtabClear(db, pTable); + } +#endif + else{ + assert( IsView(pTable) ); + sqlcipher_sqlite3SelectDelete(db, pTable->u.view.pSelect); + } /* Delete the Table structure itself. */ sqlcipher_sqlite3DeleteColumnNames(db, pTable); sqlcipher_sqlite3DbFree(db, pTable->zName); sqlcipher_sqlite3DbFree(db, pTable->zColAff); - sqlcipher_sqlite3SelectDelete(db, pTable->pSelect); sqlcipher_sqlite3ExprListDelete(db, pTable->pCheck); -#ifndef SQLITE_OMIT_VIRTUALTABLE - sqlcipher_sqlite3VtabClear(db, pTable); -#endif sqlcipher_sqlite3DbFree(db, pTable); /* Verify that no lookaside memory was used by schema tables */ @@ -116368,10 +121326,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3UnlinkAndDeleteTable(sqlcipher_sqlite3 *db, ** are not \000 terminated and are not persistent. The returned string ** is \000 terminated and is persistent. */ -SQLITE_PRIVATE char *sqlcipher_sqlite3NameFromToken(sqlcipher_sqlite3 *db, Token *pName){ +SQLITE_PRIVATE char *sqlcipher_sqlite3NameFromToken(sqlcipher_sqlite3 *db, const Token *pName){ char *zName; if( pName ){ - zName = sqlcipher_sqlite3DbStrNDup(db, (char*)pName->z, pName->n); + zName = sqlcipher_sqlite3DbStrNDup(db, (const char*)pName->z, pName->n); sqlcipher_sqlite3Dequote(zName); }else{ zName = 0; @@ -116385,7 +121343,7 @@ SQLITE_PRIVATE char *sqlcipher_sqlite3NameFromToken(sqlcipher_sqlite3 *db, Token */ SQLITE_PRIVATE void sqlcipher_sqlite3OpenSchemaTable(Parse *p, int iDb){ Vdbe *v = sqlcipher_sqlite3GetVdbe(p); - sqlcipher_sqlite3TableLock(p, iDb, SCHEMA_ROOT, 1, DFLT_SCHEMA_TABLE); + sqlcipher_sqlite3TableLock(p, iDb, SCHEMA_ROOT, 1, LEGACY_SCHEMA_TABLE); sqlcipher_sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, SCHEMA_ROOT, iDb, 5); if( p->nTab==0 ){ p->nTab = 1; @@ -116465,7 +121423,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3TwoPartName( return -1; } }else{ - assert( db->init.iDb==0 || db->init.busy || IN_RENAME_OBJECT + assert( db->init.iDb==0 || db->init.busy || IN_SPECIAL_PARSE || (db->mDbFlags & DBFLAG_Vacuum)!=0); iDb = db->init.iDb; *pUnqual = pName1; @@ -116634,6 +121592,23 @@ SQLITE_PRIVATE i16 sqlcipher_sqlite3TableColumnToStorage(Table *pTab, i16 iCol){ } #endif +/* +** Insert a single OP_JournalMode query opcode in order to force the +** prepared statement to return false for sqlcipher_sqlite3_stmt_readonly(). This +** is used by CREATE TABLE IF NOT EXISTS and similar if the table already +** exists, so that the prepared statement for CREATE TABLE IF NOT EXISTS +** will return false for sqlcipher_sqlite3_stmt_readonly() even if that statement +** is a read-only no-op. +*/ +static void sqlcipher_sqlite3ForceNotReadOnly(Parse *pParse){ + int iReg = ++pParse->nMem; + Vdbe *v = sqlcipher_sqlite3GetVdbe(pParse); + if( v ){ + sqlcipher_sqlite3VdbeAddOp3(v, OP_JournalMode, 0, iReg, PAGER_JOURNALMODE_QUERY); + sqlcipher_sqlite3VdbeUsesBtree(v, 0); + } +} + /* ** Begin constructing a new table representation in memory. This is ** the first of several action routines that get called in response @@ -116729,10 +121704,12 @@ SQLITE_PRIVATE void sqlcipher_sqlite3StartTable( pTable = sqlcipher_sqlite3FindTable(db, zName, zDb); if( pTable ){ if( !noErr ){ - sqlcipher_sqlite3ErrorMsg(pParse, "table %T already exists", pName); + sqlcipher_sqlite3ErrorMsg(pParse, "%s %T already exists", + (IsView(pTable)? "view" : "table"), pName); }else{ assert( !db->init.busy || CORRUPT_DB ); sqlcipher_sqlite3CodeVerifySchema(pParse, iDb); + sqlcipher_sqlite3ForceNotReadOnly(pParse); } goto begin_table_error; } @@ -116761,17 +121738,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3StartTable( assert( pParse->pNewTable==0 ); pParse->pNewTable = pTable; - /* If this is the magic sqlite_sequence table used by autoincrement, - ** then record a pointer to this table in the main database structure - ** so that INSERT can find the table easily. - */ -#ifndef SQLITE_OMIT_AUTOINCREMENT - if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ - assert( sqlcipher_sqlite3SchemaMutexHeld(db, iDb, 0) ); - pTable->pSchema->pSeqTab = pTable; - } -#endif - /* Begin generating the code that will insert the table record into ** the schema table. Note in particular that we must go ahead ** and allocate the record number for the table entry now. Before any @@ -116824,7 +121790,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3StartTable( }else #endif { - pParse->addrCrTab = + assert( !pParse->bReturning ); + pParse->u1.addrCrTab = sqlcipher_sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY); } sqlcipher_sqlite3OpenSchemaTable(pParse, iDb); @@ -116840,6 +121807,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3StartTable( /* If an error occurs, we jump here */ begin_table_error: + pParse->checkSchema = 1; sqlcipher_sqlite3DbFree(db, zName); return; } @@ -116849,14 +121817,88 @@ begin_table_error: */ #if SQLITE_ENABLE_HIDDEN_COLUMNS SQLITE_PRIVATE void sqlcipher_sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){ - if( sqlcipher_sqlite3_strnicmp(pCol->zName, "__hidden__", 10)==0 ){ + if( sqlcipher_sqlite3_strnicmp(pCol->zCnName, "__hidden__", 10)==0 ){ pCol->colFlags |= COLFLAG_HIDDEN; + if( pTab ) pTab->tabFlags |= TF_HasHidden; }else if( pTab && pCol!=pTab->aCol && (pCol[-1].colFlags & COLFLAG_HIDDEN) ){ pTab->tabFlags |= TF_OOOHidden; } } #endif +/* +** Name of the special TEMP trigger used to implement RETURNING. The +** name begins with "sqlite_" so that it is guaranteed not to collide +** with any application-generated triggers. +*/ +#define RETURNING_TRIGGER_NAME "sqlite_returning" + +/* +** Clean up the data structures associated with the RETURNING clause. +*/ +static void sqlcipher_sqlite3DeleteReturning(sqlcipher_sqlite3 *db, Returning *pRet){ + Hash *pHash; + pHash = &(db->aDb[1].pSchema->trigHash); + sqlcipher_sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, 0); + sqlcipher_sqlite3ExprListDelete(db, pRet->pReturnEL); + sqlcipher_sqlite3DbFree(db, pRet); +} + +/* +** Add the RETURNING clause to the parse currently underway. +** +** This routine creates a special TEMP trigger that will fire for each row +** of the DML statement. That TEMP trigger contains a single SELECT +** statement with a result set that is the argument of the RETURNING clause. +** The trigger has the Trigger.bReturning flag and an opcode of +** TK_RETURNING instead of TK_SELECT, so that the trigger code generator +** knows to handle it specially. The TEMP trigger is automatically +** removed at the end of the parse. +** +** When this routine is called, we do not yet know if the RETURNING clause +** is attached to a DELETE, INSERT, or UPDATE, so construct it as a +** RETURNING trigger instead. It will then be converted into the appropriate +** type on the first call to sqlcipher_sqlite3TriggersExist(). +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3AddReturning(Parse *pParse, ExprList *pList){ + Returning *pRet; + Hash *pHash; + sqlcipher_sqlite3 *db = pParse->db; + if( pParse->pNewTrigger ){ + sqlcipher_sqlite3ErrorMsg(pParse, "cannot use RETURNING in a trigger"); + }else{ + assert( pParse->bReturning==0 ); + } + pParse->bReturning = 1; + pRet = sqlcipher_sqlite3DbMallocZero(db, sizeof(*pRet)); + if( pRet==0 ){ + sqlcipher_sqlite3ExprListDelete(db, pList); + return; + } + pParse->u1.pReturning = pRet; + pRet->pParse = pParse; + pRet->pReturnEL = pList; + sqlcipher_sqlite3ParserAddCleanup(pParse, + (void(*)(sqlcipher_sqlite3*,void*))sqlcipher_sqlite3DeleteReturning, pRet); + testcase( pParse->earlyCleanup ); + if( db->mallocFailed ) return; + pRet->retTrig.zName = RETURNING_TRIGGER_NAME; + pRet->retTrig.op = TK_RETURNING; + pRet->retTrig.tr_tm = TRIGGER_AFTER; + pRet->retTrig.bReturning = 1; + pRet->retTrig.pSchema = db->aDb[1].pSchema; + pRet->retTrig.pTabSchema = db->aDb[1].pSchema; + pRet->retTrig.step_list = &pRet->retTStep; + pRet->retTStep.op = TK_RETURNING; + pRet->retTStep.pTrig = &pRet->retTrig; + pRet->retTStep.pExprList = pList; + pHash = &(db->aDb[1].pSchema->trigHash); + assert( sqlcipher_sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0 || pParse->nErr ); + if( sqlcipher_sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig) + ==&pRet->retTrig ){ + sqlcipher_sqlite3OomFault(db); + } +} /* ** Add a new column to the table currently being constructed. @@ -116866,60 +121908,104 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ColumnPropertiesFromName(Table *pTab, Colum ** first to get things going. Then this routine is called for each ** column. */ -SQLITE_PRIVATE void sqlcipher_sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){ +SQLITE_PRIVATE void sqlcipher_sqlite3AddColumn(Parse *pParse, Token sName, Token sType){ Table *p; int i; char *z; char *zType; Column *pCol; sqlcipher_sqlite3 *db = pParse->db; + u8 hName; + Column *aNew; + u8 eType = COLTYPE_CUSTOM; + u8 szEst = 1; + char affinity = SQLITE_AFF_BLOB; + if( (p = pParse->pNewTable)==0 ) return; if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){ sqlcipher_sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName); return; } - z = sqlcipher_sqlite3DbMallocRaw(db, pName->n + pType->n + 2); + if( !IN_RENAME_OBJECT ) sqlcipher_sqlite3DequoteToken(&sName); + + /* Because keywords GENERATE ALWAYS can be converted into indentifiers + ** by the parser, we can sometimes end up with a typename that ends + ** with "generated always". Check for this case and omit the surplus + ** text. */ + if( sType.n>=16 + && sqlcipher_sqlite3_strnicmp(sType.z+(sType.n-6),"always",6)==0 + ){ + sType.n -= 6; + while( ALWAYS(sType.n>0) && sqlcipher_sqlite3Isspace(sType.z[sType.n-1]) ) sType.n--; + if( sType.n>=9 + && sqlcipher_sqlite3_strnicmp(sType.z+(sType.n-9),"generated",9)==0 + ){ + sType.n -= 9; + while( sType.n>0 && sqlcipher_sqlite3Isspace(sType.z[sType.n-1]) ) sType.n--; + } + } + + /* Check for standard typenames. For standard typenames we will + ** set the Column.eType field rather than storing the typename after + ** the column name, in order to save space. */ + if( sType.n>=3 ){ + sqlcipher_sqlite3DequoteToken(&sType); + for(i=0; i0) ); if( z==0 ) return; - if( IN_RENAME_OBJECT ) sqlcipher_sqlite3RenameTokenMap(pParse, (void*)z, pName); - memcpy(z, pName->z, pName->n); - z[pName->n] = 0; + if( IN_RENAME_OBJECT ) sqlcipher_sqlite3RenameTokenMap(pParse, (void*)z, &sName); + memcpy(z, sName.z, sName.n); + z[sName.n] = 0; sqlcipher_sqlite3Dequote(z); + hName = sqlcipher_sqlite3StrIHash(z); for(i=0; inCol; i++){ - if( sqlcipher_sqlite3_stricmp(z, p->aCol[i].zName)==0 ){ + if( p->aCol[i].hName==hName && sqlcipher_sqlite3StrICmp(z, p->aCol[i].zCnName)==0 ){ sqlcipher_sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); sqlcipher_sqlite3DbFree(db, z); return; } } - if( (p->nCol & 0x7)==0 ){ - Column *aNew; - aNew = sqlcipher_sqlite3DbRealloc(db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0])); - if( aNew==0 ){ - sqlcipher_sqlite3DbFree(db, z); - return; - } - p->aCol = aNew; + aNew = sqlcipher_sqlite3DbRealloc(db,p->aCol,((i64)p->nCol+1)*sizeof(p->aCol[0])); + if( aNew==0 ){ + sqlcipher_sqlite3DbFree(db, z); + return; } + p->aCol = aNew; pCol = &p->aCol[p->nCol]; memset(pCol, 0, sizeof(p->aCol[0])); - pCol->zName = z; - pCol->hName = sqlcipher_sqlite3StrIHash(z); + pCol->zCnName = z; + pCol->hName = hName; sqlcipher_sqlite3ColumnPropertiesFromName(p, pCol); - if( pType->n==0 ){ + if( sType.n==0 ){ /* If there is no type specified, columns have the default affinity ** 'BLOB' with a default size of 4 bytes. */ - pCol->affinity = SQLITE_AFF_BLOB; - pCol->szEst = 1; + pCol->affinity = affinity; + pCol->eCType = eType; + pCol->szEst = szEst; #ifdef SQLITE_ENABLE_SORTER_REFERENCES - if( 4>=sqlcipher_sqlite3GlobalConfig.szSorterRef ){ - pCol->colFlags |= COLFLAG_SORTERREF; + if( affinity==SQLITE_AFF_BLOB ){ + if( 4>=sqlcipher_sqlite3GlobalConfig.szSorterRef ){ + pCol->colFlags |= COLFLAG_SORTERREF; + } } #endif }else{ zType = z + sqlcipher_sqlite3Strlen30(z) + 1; - memcpy(zType, pType->z, pType->n); - zType[pType->n] = 0; + memcpy(zType, sType.z, sType.n); + zType[sType.n] = 0; sqlcipher_sqlite3Dequote(zType); pCol->affinity = sqlcipher_sqlite3AffinityType(zType, pCol); pCol->colFlags |= COLFLAG_HASTYPE; @@ -117074,7 +122160,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AddDefaultValue( pCol = &(p->aCol[p->nCol-1]); if( !sqlcipher_sqlite3ExprIsConstantOrFunction(pExpr, isInit) ){ sqlcipher_sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant", - pCol->zName); + pCol->zCnName); #ifndef SQLITE_OMIT_GENERATED_COLUMNS }else if( pCol->colFlags & COLFLAG_GENERATED ){ testcase( pCol->colFlags & COLFLAG_VIRTUAL ); @@ -117085,15 +122171,15 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AddDefaultValue( /* A copy of pExpr is used instead of the original, as pExpr contains ** tokens that point to volatile memory. */ - Expr x; - sqlcipher_sqlite3ExprDelete(db, pCol->pDflt); + Expr x, *pDfltExpr; memset(&x, 0, sizeof(x)); x.op = TK_SPAN; x.u.zToken = sqlcipher_sqlite3DbSpanDup(db, zStart, zEnd); x.pLeft = pExpr; x.flags = EP_Skip; - pCol->pDflt = sqlcipher_sqlite3ExprDup(db, &x, EXPRDUP_REDUCE); + pDfltExpr = sqlcipher_sqlite3ExprDup(db, &x, EXPRDUP_REDUCE); sqlcipher_sqlite3DbFree(db, x.u.zToken); + sqlcipher_sqlite3ColumnSetExpr(pParse, p, pCol, pDfltExpr); } } if( IN_RENAME_OBJECT ){ @@ -117189,9 +122275,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AddPrimaryKey( assert( pCExpr!=0 ); sqlcipher_sqlite3StringToId(pCExpr); if( pCExpr->op==TK_ID ){ - const char *zCName = pCExpr->u.zToken; + const char *zCName; + assert( !ExprHasProperty(pCExpr, EP_IntValue) ); + zCName = pCExpr->u.zToken; for(iCol=0; iColnCol; iCol++){ - if( sqlcipher_sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){ + if( sqlcipher_sqlite3StrICmp(zCName, pTab->aCol[iCol].zCnName)==0 ){ pCol = &pTab->aCol[iCol]; makeColumnPartOfPrimaryKey(pParse, pCol); break; @@ -117202,7 +122290,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AddPrimaryKey( } if( nTerm==1 && pCol - && sqlcipher_sqlite3StrICmp(sqlcipher_sqlite3ColumnType(pCol,""), "INTEGER")==0 + && pCol->eCType==COLTYPE_INTEGER && sortOrder!=SQLITE_SO_DESC ){ if( IN_RENAME_OBJECT && pList ){ @@ -117213,7 +122301,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AddPrimaryKey( pTab->keyConf = (u8)onError; assert( autoInc==0 || autoInc==1 ); pTab->tabFlags |= autoInc*TF_Autoincrement; - if( pList ) pParse->iPkSortOrder = pList->a[0].sortFlags; + if( pList ) pParse->iPkSortOrder = pList->a[0].fg.sortFlags; (void)sqlcipher_sqlite3HasExplicitNulls(pParse, pList); }else if( autoInc ){ #ifndef SQLITE_OMIT_AUTOINCREMENT @@ -117282,8 +122370,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AddCollateType(Parse *pParse, Token *pToken if( sqlcipher_sqlite3LocateCollSeq(pParse, zColl) ){ Index *pIdx; - sqlcipher_sqlite3DbFree(db, p->aCol[i].zColl); - p->aCol[i].zColl = zColl; + sqlcipher_sqlite3ColumnSetColl(db, &p->aCol[i], zColl); /* If the column is declared as " PRIMARY KEY COLLATE ", ** then an index may have been created on this column before the @@ -117292,12 +122379,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AddCollateType(Parse *pParse, Token *pToken for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){ assert( pIdx->nKeyCol==1 ); if( pIdx->aiColumn[0]==i ){ - pIdx->azColl[0] = p->aCol[i].zColl; + pIdx->azColl[0] = sqlcipher_sqlite3ColumnColl(&p->aCol[i]); } } - }else{ - sqlcipher_sqlite3DbFree(db, zColl); } + sqlcipher_sqlite3DbFree(db, zColl); } /* Change the most recently parsed column to be a GENERATED ALWAYS AS @@ -117317,7 +122403,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AddGenerated(Parse *pParse, Expr *pExpr, To sqlcipher_sqlite3ErrorMsg(pParse, "virtual tables cannot use computed columns"); goto generated_done; } - if( pCol->pDflt ) goto generated_error; + if( pCol->iDflt>0 ) goto generated_error; if( pType ){ if( pType->n==7 && sqlcipher_sqlite3StrNICmp("virtual",pType->z,7)==0 ){ /* no-op */ @@ -117335,13 +122421,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AddGenerated(Parse *pParse, Expr *pExpr, To if( pCol->colFlags & COLFLAG_PRIMKEY ){ makeColumnPartOfPrimaryKey(pParse, pCol); /* For the error message */ } - pCol->pDflt = pExpr; + sqlcipher_sqlite3ColumnSetExpr(pParse, pTab, pCol, pExpr); pExpr = 0; goto generated_done; generated_error: sqlcipher_sqlite3ErrorMsg(pParse, "error in generated column \"%s\"", - pCol->zName); + pCol->zCnName); generated_done: sqlcipher_sqlite3ExprDelete(pParse->db, pExpr); #else @@ -117443,7 +122529,7 @@ static char *createTableStmt(sqlcipher_sqlite3 *db, Table *p){ Column *pCol; n = 0; for(pCol = p->aCol, i=0; inCol; i++, pCol++){ - n += identLength(pCol->zName) + 5; + n += identLength(pCol->zCnName) + 5; } n += identLength(p->zName); if( n<50 ){ @@ -117479,7 +122565,7 @@ static char *createTableStmt(sqlcipher_sqlite3 *db, Table *p){ sqlcipher_sqlite3_snprintf(n-k, &zStmt[k], zSep); k += sqlcipher_sqlite3Strlen30(&zStmt[k]); zSep = zSep2; - identPut(zStmt, &k, pCol->zName); + identPut(zStmt, &k, pCol->zCnName); assert( pCol->affinity-SQLITE_AFF_BLOB >= 0 ); assert( pCol->affinity-SQLITE_AFF_BLOB < ArraySize(azType) ); testcase( pCol->affinity==SQLITE_AFF_BLOB ); @@ -117563,7 +122649,6 @@ static void estimateIndexWidth(Index *pIdx){ */ static int hasColumn(const i16 *aiCol, int nCol, int x){ while( nCol-- > 0 ){ - assert( aiCol[0]>=0 ); if( x==*(aiCol++) ){ return 1; } @@ -117676,7 +122761,9 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ */ if( !db->init.imposterTable ){ for(i=0; inCol; i++){ - if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 ){ + if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 + && (pTab->aCol[i].notNull==OE_None) + ){ pTab->aCol[i].notNull = OE_Abort; } } @@ -117686,9 +122773,10 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ /* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY ** into BTREE_BLOBKEY. */ - if( pParse->addrCrTab ){ + assert( !pParse->bReturning ); + if( pParse->u1.addrCrTab ){ assert( v ); - sqlcipher_sqlite3VdbeChangeP3(v, pParse->addrCrTab, BTREE_BLOBKEY); + sqlcipher_sqlite3VdbeChangeP3(v, pParse->u1.addrCrTab, BTREE_BLOBKEY); } /* Locate the PRIMARY KEY index. Or, if this table was originally @@ -117697,19 +122785,26 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ if( pTab->iPKey>=0 ){ ExprList *pList; Token ipkToken; - sqlcipher_sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zName); + sqlcipher_sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zCnName); pList = sqlcipher_sqlite3ExprListAppend(pParse, 0, sqlcipher_sqlite3ExprAlloc(db, TK_ID, &ipkToken, 0)); - if( pList==0 ) return; + if( pList==0 ){ + pTab->tabFlags &= ~TF_WithoutRowid; + return; + } if( IN_RENAME_OBJECT ){ sqlcipher_sqlite3RenameTokenRemap(pParse, pList->a[0].pExpr, &pTab->iPKey); } - pList->a[0].sortFlags = pParse->iPkSortOrder; + pList->a[0].fg.sortFlags = pParse->iPkSortOrder; assert( pParse->pNewTable==pTab ); pTab->iPKey = -1; sqlcipher_sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0, SQLITE_IDXTYPE_PRIMARYKEY); - if( db->mallocFailed || pParse->nErr ) return; + if( pParse->nErr ){ + pTab->tabFlags &= ~TF_WithoutRowid; + return; + } + assert( db->mallocFailed==0 ); pPk = sqlcipher_sqlite3PrimaryKeyIndex(pTab); assert( pPk->nKeyCol==1 ); }else{ @@ -117821,7 +122916,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3IsShadowTableOf(sqlcipher_sqlite3 *db, Table nName = sqlcipher_sqlite3Strlen30(pTab->zName); if( sqlcipher_sqlite3_strnicmp(zName, pTab->zName, nName)!=0 ) return 0; if( zName[nName]!='_' ) return 0; - pMod = (Module*)sqlcipher_sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]); + pMod = (Module*)sqlcipher_sqlite3HashFind(&db->aModule, pTab->u.vtab.azArg[0]); if( pMod==0 ) return 0; if( pMod->pModule->iVersion<3 ) return 0; if( pMod->pModule->xShadowName==0 ) return 0; @@ -117829,6 +122924,41 @@ SQLITE_PRIVATE int sqlcipher_sqlite3IsShadowTableOf(sqlcipher_sqlite3 *db, Table } #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Table pTab is a virtual table. If it the virtual table implementation +** exists and has an xShadowName method, then loop over all other ordinary +** tables within the same schema looking for shadow tables of pTab, and mark +** any shadow tables seen using the TF_Shadow flag. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3MarkAllShadowTablesOf(sqlcipher_sqlite3 *db, Table *pTab){ + int nName; /* Length of pTab->zName */ + Module *pMod; /* Module for the virtual table */ + HashElem *k; /* For looping through the symbol table */ + + assert( IsVirtual(pTab) ); + pMod = (Module*)sqlcipher_sqlite3HashFind(&db->aModule, pTab->u.vtab.azArg[0]); + if( pMod==0 ) return; + if( NEVER(pMod->pModule==0) ) return; + if( pMod->pModule->iVersion<3 ) return; + if( pMod->pModule->xShadowName==0 ) return; + assert( pTab->zName!=0 ); + nName = sqlcipher_sqlite3Strlen30(pTab->zName); + for(k=sqliteHashFirst(&pTab->pSchema->tblHash); k; k=sqliteHashNext(k)){ + Table *pOther = sqliteHashData(k); + assert( pOther->zName!=0 ); + if( !IsOrdinaryTable(pOther) ) continue; + if( pOther->tabFlags & TF_Shadow ) continue; + if( sqlcipher_sqlite3StrNICmp(pOther->zName, pTab->zName, nName)==0 + && pOther->zName[nName]=='_' + && pMod->pModule->xShadowName(pOther->zName+nName+1) + ){ + pOther->tabFlags |= TF_Shadow; + } + } +} +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ + #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Return true if zName is a shadow table name in the current database @@ -117902,7 +123032,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( Parse *pParse, /* Parse context */ Token *pCons, /* The ',' token after the last column defn. */ Token *pEnd, /* The ')' before options in the CREATE TABLE */ - u8 tabOpts, /* Extra table options. Usually 0. */ + u32 tabOpts, /* Extra table options. Usually 0. */ Select *pSelect /* Select from a "CREATE ... AS SELECT" */ ){ Table *p; /* The new table */ @@ -117913,7 +123043,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( if( pEnd==0 && pSelect==0 ){ return; } - assert( !db->mallocFailed ); p = pParse->pNewTable; if( p==0 ) return; @@ -117931,7 +123060,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( ** table itself. So mark it read-only. */ if( db->init.busy ){ - if( pSelect ){ + if( pSelect || (!IsOrdinaryTable(p) && db->init.newTnum) ){ sqlcipher_sqlite3ErrorMsg(pParse, ""); return; } @@ -117939,6 +123068,44 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( if( p->tnum==1 ) p->tabFlags |= TF_Readonly; } + /* Special processing for tables that include the STRICT keyword: + ** + ** * Do not allow custom column datatypes. Every column must have + ** a datatype that is one of INT, INTEGER, REAL, TEXT, or BLOB. + ** + ** * If a PRIMARY KEY is defined, other than the INTEGER PRIMARY KEY, + ** then all columns of the PRIMARY KEY must have a NOT NULL + ** constraint. + */ + if( tabOpts & TF_Strict ){ + int ii; + p->tabFlags |= TF_Strict; + for(ii=0; iinCol; ii++){ + Column *pCol = &p->aCol[ii]; + if( pCol->eCType==COLTYPE_CUSTOM ){ + if( pCol->colFlags & COLFLAG_HASTYPE ){ + sqlcipher_sqlite3ErrorMsg(pParse, + "unknown datatype for %s.%s: \"%s\"", + p->zName, pCol->zCnName, sqlcipher_sqlite3ColumnType(pCol, "") + ); + }else{ + sqlcipher_sqlite3ErrorMsg(pParse, "missing datatype for %s.%s", + p->zName, pCol->zCnName); + } + return; + }else if( pCol->eCType==COLTYPE_ANY ){ + pCol->affinity = SQLITE_AFF_BLOB; + } + if( (pCol->colFlags & COLFLAG_PRIMKEY)!=0 + && p->iPKey!=ii + && pCol->notNull == OE_None + ){ + pCol->notNull = OE_Abort; + p->tabFlags |= TF_HasNotNull; + } + } + } + assert( (p->tabFlags & TF_HasPrimaryKey)==0 || p->iPKey>=0 || sqlcipher_sqlite3PrimaryKeyIndex(p)!=0 ); assert( (p->tabFlags & TF_HasPrimaryKey)!=0 @@ -117983,7 +123150,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( for(ii=0; iinCol; ii++){ u32 colFlags = p->aCol[ii].colFlags; if( (colFlags & COLFLAG_GENERATED)!=0 ){ - Expr *pX = p->aCol[ii].pDflt; + Expr *pX = sqlcipher_sqlite3ColumnExpr(p, &p->aCol[ii]); testcase( colFlags & COLFLAG_VIRTUAL ); testcase( colFlags & COLFLAG_STORED ); if( sqlcipher_sqlite3ResolveSelfReference(pParse, p, NC_GenCol, pX, 0) ){ @@ -117993,8 +123160,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( ** tree that have been allocated from lookaside memory, which is ** illegal in a schema and will lead to errors or heap corruption ** when the database connection closes. */ - sqlcipher_sqlite3ExprDelete(db, pX); - p->aCol[ii].pDflt = sqlcipher_sqlite3ExprAlloc(db, TK_NULL, 0, 0); + sqlcipher_sqlite3ColumnSetExpr(pParse, p, &p->aCol[ii], + sqlcipher_sqlite3ExprAlloc(db, TK_NULL, 0, 0)); } }else{ nNG++; @@ -118034,7 +123201,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( /* ** Initialize zType for the new view or table. */ - if( p->pSelect==0 ){ + if( IsOrdinaryTable(p) ){ /* A regular table */ zType = "table"; zType2 = "TABLE"; @@ -118068,6 +123235,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( int addrInsLoop; /* Top of the loop for inserting rows */ Table *pSelTab; /* A table that describes the SELECT results */ + if( IN_SPECIAL_PARSE ){ + pParse->rc = SQLITE_ERROR; + pParse->nErr++; + return; + } regYield = ++pParse->nMem; regRec = ++pParse->nMem; regRowid = ++pParse->nMem; @@ -118120,7 +123292,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( ** the information we've collected. */ sqlcipher_sqlite3NestedParse(pParse, - "UPDATE %Q." DFLT_SCHEMA_TABLE + "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q" " WHERE rowid=#%d", db->aDb[iDb].zDbSName, @@ -118138,7 +123310,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( /* Check to see if we need to create an sqlite_sequence table for ** keeping track of autoincrement keys. */ - if( (p->tabFlags & TF_Autoincrement)!=0 ){ + if( (p->tabFlags & TF_Autoincrement)!=0 && !IN_SPECIAL_PARSE ){ Db *pDb = &db->aDb[iDb]; assert( sqlcipher_sqlite3SchemaMutexHeld(db, iDb, 0) ); if( pDb->pSchema->pSeqTab==0 ){ @@ -118152,7 +123324,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( /* Reparse everything to update our internal data structures */ sqlcipher_sqlite3VdbeAddParseSchemaOp(v, iDb, - sqlcipher_sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName)); + sqlcipher_sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0); } /* Add the table to the in-memory representation of the database. @@ -118161,6 +123333,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( Table *pOld; Schema *pSchema = p->pSchema; assert( sqlcipher_sqlite3SchemaMutexHeld(db, iDb, 0) ); + assert( HasRowid(p) || p->iPKey<0 ); pOld = sqlcipher_sqlite3HashInsert(&pSchema->tblHash, p->zName, p); if( pOld ){ assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ @@ -118170,19 +123343,27 @@ SQLITE_PRIVATE void sqlcipher_sqlite3EndTable( pParse->pNewTable = 0; db->mDbFlags |= DBFLAG_SchemaChange; -#ifndef SQLITE_OMIT_ALTERTABLE - if( !p->pSelect ){ - const char *zName = (const char *)pParse->sNameToken.z; - int nName; - assert( !pSelect && pCons && pEnd ); - if( pCons->z==0 ){ - pCons = pEnd; - } - nName = (int)((const char *)pCons->z - zName); - p->addColOffset = 13 + sqlcipher_sqlite3Utf8CharLen(zName, nName); + /* If this is the magic sqlite_sequence table used by autoincrement, + ** then record a pointer to this table in the main database structure + ** so that INSERT can find the table easily. */ + assert( !pParse->nested ); +#ifndef SQLITE_OMIT_AUTOINCREMENT + if( strcmp(p->zName, "sqlite_sequence")==0 ){ + assert( sqlcipher_sqlite3SchemaMutexHeld(db, iDb, 0) ); + p->pSchema->pSeqTab = p; } #endif } + +#ifndef SQLITE_OMIT_ALTERTABLE + if( !pSelect && IsOrdinaryTable(p) ){ + assert( pCons && pEnd ); + if( pCons->z==0 ){ + pCons = pEnd; + } + p->u.tab.addColOffset = 13 + (int)(pCons->z - pParse->sNameToken.z); + } +#endif } #ifndef SQLITE_OMIT_VIEW @@ -118215,6 +123396,16 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateView( sqlcipher_sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); p = pParse->pNewTable; if( p==0 || pParse->nErr ) goto create_view_fail; + + /* Legacy versions of SQLite allowed the use of the magic "rowid" column + ** on a view, even though views do not have rowids. The following flag + ** setting fixes this problem. But the fix can be disabled by compiling + ** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that + ** depend upon the old buggy behavior. */ +#ifndef SQLITE_ALLOW_ROWID_IN_VIEW + p->tabFlags |= TF_NoVisibleRowid; +#endif + sqlcipher_sqlite3TwoPartName(pParse, pName1, pName2, &pName); iDb = sqlcipher_sqlite3SchemaToIndex(db, p->pSchema); sqlcipher_sqlite3FixInit(&sFix, pParse, iDb, "view", pName); @@ -118227,12 +123418,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateView( */ pSelect->selFlags |= SF_View; if( IN_RENAME_OBJECT ){ - p->pSelect = pSelect; + p->u.view.pSelect = pSelect; pSelect = 0; }else{ - p->pSelect = sqlcipher_sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); + p->u.view.pSelect = sqlcipher_sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); } p->pCheck = sqlcipher_sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE); + p->eTabType = TABTYP_VIEW; if( db->mallocFailed ) goto create_view_fail; /* Locate the end of the CREATE VIEW statement. Make sEnd point to @@ -118274,7 +123466,6 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTa Table *pSelTab; /* A fake table from which we get the result set */ Select *pSel; /* Copy of the SELECT that implements the view */ int nErr = 0; /* Number of errors encountered */ - int n; /* Temporarily holds the number of cursors assigned */ sqlcipher_sqlite3 *db = pParse->db; /* Database connection for malloc errors */ #ifndef SQLITE_OMIT_VIRTUALTABLE int rc; @@ -118286,13 +123477,12 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTa assert( pTable ); #ifndef SQLITE_OMIT_VIRTUALTABLE - db->nSchemaLock++; - rc = sqlcipher_sqlite3VtabCallConnect(pParse, pTable); - db->nSchemaLock--; - if( rc ){ - return 1; + if( IsVirtual(pTable) ){ + db->nSchemaLock++; + rc = sqlcipher_sqlite3VtabCallConnect(pParse, pTable); + db->nSchemaLock--; + return rc; } - if( IsVirtual(pTable) ) return 0; #endif #ifndef SQLITE_OMIT_VIEW @@ -118329,12 +123519,13 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTa ** to be permanent. So the computation is done on a copy of the SELECT ** statement that defines the view. */ - assert( pTable->pSelect ); - pSel = sqlcipher_sqlite3SelectDup(db, pTable->pSelect, 0); + assert( IsView(pTable) ); + pSel = sqlcipher_sqlite3SelectDup(db, pTable->u.view.pSelect, 0); if( pSel ){ u8 eParseMode = pParse->eParseMode; + int nTab = pParse->nTab; + int nSelect = pParse->nSelect; pParse->eParseMode = PARSE_MODE_NORMAL; - n = pParse->nTab; sqlcipher_sqlite3SrcListAssignCursors(pParse, pSel->pSrc); pTable->nCol = -1; DisableLookaside; @@ -118346,7 +123537,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTa #else pSelTab = sqlcipher_sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE); #endif - pParse->nTab = n; + pParse->nTab = nTab; + pParse->nSelect = nSelect; if( pSelTab==0 ){ pTable->nCol = 0; nErr++; @@ -118359,10 +123551,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTa */ sqlcipher_sqlite3ColumnsFromExprList(pParse, pTable->pCheck, &pTable->nCol, &pTable->aCol); - if( db->mallocFailed==0 - && pParse->nErr==0 + if( pParse->nErr==0 && pTable->nCol==pSel->pEList->nExpr ){ + assert( db->mallocFailed==0 ); sqlcipher_sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel, SQLITE_AFF_NONE); } @@ -118373,6 +123565,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTa assert( pTable->aCol==0 ); pTable->nCol = pSelTab->nCol; pTable->aCol = pSelTab->aCol; + pTable->tabFlags |= (pSelTab->tabFlags & COLFLAG_NOINSERT); pSelTab->nCol = 0; pSelTab->aCol = 0; assert( sqlcipher_sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); @@ -118388,8 +123581,6 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTa pTable->pSchema->schemaFlags |= DB_UnresetViews; if( db->mallocFailed ){ sqlcipher_sqlite3DeleteColumnNames(db, pTable); - pTable->aCol = 0; - pTable->nCol = 0; } #endif /* SQLITE_OMIT_VIEW */ return nErr; @@ -118406,10 +123597,8 @@ static void sqliteViewResetAll(sqlcipher_sqlite3 *db, int idx){ if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); - if( pTab->pSelect ){ + if( IsView(pTab) ){ sqlcipher_sqlite3DeleteColumnNames(db, pTab); - pTab->aCol = 0; - pTab->nCol = 0; } } DbClearProperty(db, idx, DB_UnresetViews); @@ -118483,7 +123672,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){ ** token for additional information. */ sqlcipher_sqlite3NestedParse(pParse, - "UPDATE %Q." DFLT_SCHEMA_TABLE + "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET rootpage=%d WHERE #%d AND rootpage=#%d", pParse->db->aDb[iDb].zDbSName, iTable, r1, r1); #endif @@ -118618,7 +123807,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CodeDropTable(Parse *pParse, Table *pTab, i ** database. */ sqlcipher_sqlite3NestedParse(pParse, - "DELETE FROM %Q." DFLT_SCHEMA_TABLE + "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE tbl_name=%Q and type!='trigger'", pDb->zDbSName, pTab->zName); if( !isView && !IsVirtual(pTab) ){ @@ -118646,6 +123835,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ReadOnlyShadowTables(sqlcipher_sqlite3 *db){ if( (db->flags & SQLITE_Defensive)!=0 && db->pVtabCtx==0 && db->nVdbeExec==0 + && !sqlcipher_sqlite3VtabInSync(db) ){ return 1; } @@ -118665,6 +123855,9 @@ static int tableMayNotBeDropped(sqlcipher_sqlite3 *db, Table *pTab){ if( (pTab->tabFlags & TF_Shadow)!=0 && sqlcipher_sqlite3ReadOnlyShadowTables(db) ){ return 1; } + if( pTab->tabFlags & TF_Eponymous ){ + return 1; + } return 0; } @@ -118690,7 +123883,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DropTable(Parse *pParse, SrcList *pName, in if( noErr ) db->suppressErr--; if( pTab==0 ){ - if( noErr ) sqlcipher_sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + if( noErr ){ + sqlcipher_sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + sqlcipher_sqlite3ForceNotReadOnly(pParse); + } goto exit_drop_table; } iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); @@ -118746,11 +123942,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DropTable(Parse *pParse, SrcList *pName, in /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used ** on a table. */ - if( isView && pTab->pSelect==0 ){ + if( isView && !IsView(pTab) ){ sqlcipher_sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName); goto exit_drop_table; } - if( !isView && pTab->pSelect ){ + if( !isView && IsView(pTab) ){ sqlcipher_sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName); goto exit_drop_table; } @@ -118801,7 +123997,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateForeignKey( FKey *pFKey = 0; FKey *pNextTo; Table *p = pParse->pNewTable; - int nByte; + i64 nByte; int i; int nCol; char *z; @@ -118814,7 +124010,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateForeignKey( if( pToCol && pToCol->nExpr!=1 ){ sqlcipher_sqlite3ErrorMsg(pParse, "foreign key on %s" " should reference only one column of table %T", - p->aCol[iCol].zName, pTo); + p->aCol[iCol].zCnName, pTo); goto fk_end; } nCol = 1; @@ -118837,7 +124033,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateForeignKey( goto fk_end; } pFKey->pFrom = p; - pFKey->pNextFrom = p->pFKey; + assert( IsOrdinaryTable(p) ); + pFKey->pNextFrom = p->u.tab.pFKey; z = (char*)&pFKey->aCol[nCol]; pFKey->zTo = z; if( IN_RENAME_OBJECT ){ @@ -118854,7 +124051,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateForeignKey( for(i=0; inCol; j++){ - if( sqlcipher_sqlite3StrICmp(p->aCol[j].zName, pFromCol->a[i].zEName)==0 ){ + if( sqlcipher_sqlite3StrICmp(p->aCol[j].zCnName, pFromCol->a[i].zEName)==0 ){ pFKey->aCol[i].iFrom = j; break; } @@ -118902,7 +124099,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateForeignKey( /* Link the foreign key to the table as the last step. */ - p->pFKey = pFKey; + assert( IsOrdinaryTable(p) ); + p->u.tab.pFKey = pFKey; pFKey = 0; fk_end: @@ -118923,7 +124121,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DeferForeignKey(Parse *pParse, int isDeferr #ifndef SQLITE_OMIT_FOREIGN_KEY Table *pTab; FKey *pFKey; - if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return; + if( (pTab = pParse->pNewTable)==0 ) return; + if( NEVER(!IsOrdinaryTable(pTab)) ) return; + if( (pFKey = pTab->u.tab.pFKey)==0 ) return; assert( isDeferred==0 || isDeferred==1 ); /* EV: R-30323-21917 */ pFKey->isDeferred = (u8)isDeferred; #endif @@ -118973,7 +124173,7 @@ static void sqlcipher_sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRo tnum = pIndex->tnum; } pKey = sqlcipher_sqlite3KeyInfoOfIndex(pParse, pIndex); - assert( pKey!=0 || db->mallocFailed || pParse->nErr ); + assert( pKey!=0 || pParse->nErr ); /* Open the sorter cursor if we are to use one. */ iSorter = pParse->nTab++; @@ -119083,8 +124283,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3HasExplicitNulls(Parse *pParse, ExprList *pL if( pList ){ int i; for(i=0; inExpr; i++){ - if( pList->a[i].bNulls ){ - u8 sf = pList->a[i].sortFlags; + if( pList->a[i].fg.bNulls ){ + u8 sf = pList->a[i].fg.sortFlags; sqlcipher_sqlite3ErrorMsg(pParse, "unsupported use of NULLS %s", (sf==0 || sf==3) ? "FIRST" : "LAST" ); @@ -119137,9 +124337,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex( char *zExtra = 0; /* Extra space after the Index object */ Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */ - if( db->mallocFailed || pParse->nErr>0 ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto exit_create_index; } + assert( db->mallocFailed==0 ); if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){ goto exit_create_index; } @@ -119203,7 +124405,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex( pDb = &db->aDb[iDb]; assert( pTab!=0 ); - assert( pParse->nErr==0 ); if( sqlcipher_sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 && db->init.busy==0 && pTblName!=0 @@ -119215,7 +124416,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex( goto exit_create_index; } #ifndef SQLITE_OMIT_VIEW - if( pTab->pSelect ){ + if( IsView(pTab) ){ sqlcipher_sqlite3ErrorMsg(pParse, "views may not be indexed"); goto exit_create_index; } @@ -119260,6 +124461,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex( }else{ assert( !db->init.busy ); sqlcipher_sqlite3CodeVerifySchema(pParse, iDb); + sqlcipher_sqlite3ForceNotReadOnly(pParse); } goto exit_create_index; } @@ -119305,7 +124507,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex( Token prevCol; Column *pCol = &pTab->aCol[pTab->nCol-1]; pCol->colFlags |= COLFLAG_UNIQUE; - sqlcipher_sqlite3TokenInit(&prevCol, pCol->zName); + sqlcipher_sqlite3TokenInit(&prevCol, pCol->zCnName); pList = sqlcipher_sqlite3ExprListAppend(pParse, 0, sqlcipher_sqlite3ExprAlloc(db, TK_ID, &prevCol, 0)); if( pList==0 ) goto exit_create_index; @@ -119323,6 +124525,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex( Expr *pExpr = pList->a[i].pExpr; assert( pExpr!=0 ); if( pExpr->op==TK_COLLATE ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); nExtra += (1 + sqlcipher_sqlite3Strlen30(pExpr->u.zToken)); } } @@ -119418,6 +124621,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex( zColl = 0; if( pListItem->pExpr->op==TK_COLLATE ){ int nColl; + assert( !ExprHasProperty(pListItem->pExpr, EP_IntValue) ); zColl = pListItem->pExpr->u.zToken; nColl = sqlcipher_sqlite3Strlen30(zColl) + 1; assert( nExtra>=nColl ); @@ -119426,14 +124630,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex( zExtra += nColl; nExtra -= nColl; }else if( j>=0 ){ - zColl = pTab->aCol[j].zColl; + zColl = sqlcipher_sqlite3ColumnColl(&pTab->aCol[j]); } if( !zColl ) zColl = sqlcipher_sqlite3StrBINARY; if( !db->init.busy && !sqlcipher_sqlite3LocateCollSeq(pParse, zColl) ){ goto exit_create_index; } pIndex->azColl[i] = zColl; - requestedSortOrder = pListItem->sortFlags & sortOrderMask; + requestedSortOrder = pListItem->fg.sortFlags & sortOrderMask; pIndex->aSortOrder[i] = (u8)requestedSortOrder; } @@ -119624,13 +124828,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex( /* Add an entry in sqlite_schema for this index */ sqlcipher_sqlite3NestedParse(pParse, - "INSERT INTO %Q." DFLT_SCHEMA_TABLE " VALUES('index',%Q,%Q,#%d,%Q);", - db->aDb[iDb].zDbSName, - pIndex->zName, - pTab->zName, - iMem, - zStmt - ); + "INSERT INTO %Q." LEGACY_SCHEMA_TABLE " VALUES('index',%Q,%Q,#%d,%Q);", + db->aDb[iDb].zDbSName, + pIndex->zName, + pTab->zName, + iMem, + zStmt + ); sqlcipher_sqlite3DbFree(db, zStmt); /* Fill the index with data and reparse the schema. Code an OP_Expire @@ -119640,7 +124844,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex( sqlcipher_sqlite3RefillIndex(pParse, pIndex, iMem); sqlcipher_sqlite3ChangeCookie(pParse, iDb); sqlcipher_sqlite3VdbeAddParseSchemaOp(v, iDb, - sqlcipher_sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); + sqlcipher_sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName), 0); sqlcipher_sqlite3VdbeAddOp2(v, OP_Expire, 0, 1); } @@ -119661,8 +124865,12 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CreateIndex( /* Clean up before exiting */ exit_create_index: if( pIndex ) sqlcipher_sqlite3FreeIndex(db, pIndex); - if( pTab ){ /* Ensure all REPLACE indexes are at the end of the list */ - Index **ppFrom = &pTab->pIndex; + if( pTab ){ + /* Ensure all REPLACE indexes on pTab are at the end of the pIndex list. + ** The list was already ordered when this routine was entered, so at this + ** point at most a single index (the newly added index) will be out of + ** order. So we have to reorder at most one index. */ + Index **ppFrom; Index *pThis; for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){ Index *pNext; @@ -119675,6 +124883,16 @@ exit_create_index: } break; } +#ifdef SQLITE_DEBUG + /* Verify that all REPLACE indexes really are now at the end + ** of the index list. In other words, no other index type ever + ** comes after a REPLACE index on the list. */ + for(pThis = pTab->pIndex; pThis; pThis=pThis->pNext){ + assert( pThis->onError!=OE_Replace + || pThis->pNext==0 + || pThis->pNext->onError==OE_Replace ); + } +#endif } sqlcipher_sqlite3ExprDelete(db, pPIWhere); sqlcipher_sqlite3ExprListDelete(db, pList); @@ -119726,7 +124944,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DefaultRowEst(Index *pIdx){ if( x<99 ){ pIdx->pTable->nRowLogEst = x = 99; } - if( pIdx->pPartIdxWhere!=0 ) x -= 10; assert( 10==sqlcipher_sqlite3LogEst(2) ); + if( pIdx->pPartIdxWhere!=0 ){ x -= 10; assert( 10==sqlcipher_sqlite3LogEst(2) ); } a[0] = x; /* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is @@ -119750,10 +124968,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DropIndex(Parse *pParse, SrcList *pName, in sqlcipher_sqlite3 *db = pParse->db; int iDb; - assert( pParse->nErr==0 ); /* Never called with prior errors */ if( db->mallocFailed ){ goto exit_drop_index; } + assert( pParse->nErr==0 ); /* Never called with prior non-OOM errors */ assert( pName->nSrc==1 ); if( SQLITE_OK!=sqlcipher_sqlite3ReadSchema(pParse) ){ goto exit_drop_index; @@ -119761,9 +124979,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DropIndex(Parse *pParse, SrcList *pName, in pIndex = sqlcipher_sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase); if( pIndex==0 ){ if( !ifExists ){ - sqlcipher_sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0); + sqlcipher_sqlite3ErrorMsg(pParse, "no such index: %S", pName->a); }else{ sqlcipher_sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + sqlcipher_sqlite3ForceNotReadOnly(pParse); } pParse->checkSchema = 1; goto exit_drop_index; @@ -119783,7 +125002,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DropIndex(Parse *pParse, SrcList *pName, in if( sqlcipher_sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ goto exit_drop_index; } - if( !OMIT_TEMPDB && iDb ) code = SQLITE_DROP_TEMP_INDEX; + if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX; if( sqlcipher_sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ goto exit_drop_index; } @@ -119795,7 +125014,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DropIndex(Parse *pParse, SrcList *pName, in if( v ){ sqlcipher_sqlite3BeginWriteOperation(pParse, 1, iDb); sqlcipher_sqlite3NestedParse(pParse, - "DELETE FROM %Q." DFLT_SCHEMA_TABLE " WHERE name=%Q AND type='index'", + "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='index'", db->aDb[iDb].zDbSName, pIndex->zName ); sqlcipher_sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName); @@ -119861,18 +125080,17 @@ SQLITE_PRIVATE IdList *sqlcipher_sqlite3IdListAppend(Parse *pParse, IdList *pLis if( pList==0 ){ pList = sqlcipher_sqlite3DbMallocZero(db, sizeof(IdList) ); if( pList==0 ) return 0; + }else{ + IdList *pNew; + pNew = sqlcipher_sqlite3DbRealloc(db, pList, + sizeof(IdList) + pList->nId*sizeof(pList->a)); + if( pNew==0 ){ + sqlcipher_sqlite3IdListDelete(db, pList); + return 0; + } + pList = pNew; } - pList->a = sqlcipher_sqlite3ArrayAllocate( - db, - pList->a, - sizeof(pList->a[0]), - &pList->nId, - &i - ); - if( i<0 ){ - sqlcipher_sqlite3IdListDelete(db, pList); - return 0; - } + i = pList->nId++; pList->a[i].zName = sqlcipher_sqlite3NameFromToken(db, pToken); if( IN_RENAME_OBJECT && pList->a[i].zName ){ sqlcipher_sqlite3RenameTokenMap(pParse, (void*)pList->a[i].zName, pToken); @@ -119886,10 +125104,10 @@ SQLITE_PRIVATE IdList *sqlcipher_sqlite3IdListAppend(Parse *pParse, IdList *pLis SQLITE_PRIVATE void sqlcipher_sqlite3IdListDelete(sqlcipher_sqlite3 *db, IdList *pList){ int i; if( pList==0 ) return; + assert( pList->eU4!=EU4_EXPR ); /* EU4_EXPR mode is not currently used */ for(i=0; inId; i++){ sqlcipher_sqlite3DbFree(db, pList->a[i].zName); } - sqlcipher_sqlite3DbFree(db, pList->a); sqlcipher_sqlite3DbFreeNN(db, pList); } @@ -119899,7 +125117,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3IdListDelete(sqlcipher_sqlite3 *db, IdList */ SQLITE_PRIVATE int sqlcipher_sqlite3IdListIndex(IdList *pList, const char *zName){ int i; - if( pList==0 ) return -1; + assert( pList!=0 ); for(i=0; inId; i++){ if( sqlcipher_sqlite3StrICmp(pList->a[i].zName, zName)==0 ) return i; } @@ -120033,7 +125251,7 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListAppend( Token *pTable, /* Table to append */ Token *pDatabase /* Database of the table */ ){ - struct SrcList_item *pItem; + SrcItem *pItem; sqlcipher_sqlite3 *db; assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ assert( pParse!=0 ); @@ -120074,9 +125292,9 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListAppend( */ SQLITE_PRIVATE void sqlcipher_sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){ int i; - struct SrcList_item *pItem; - assert(pList || pParse->db->mallocFailed ); - if( pList ){ + SrcItem *pItem; + assert( pList || pParse->db->mallocFailed ); + if( ALWAYS(pList) ){ for(i=0, pItem=pList->a; inSrc; i++, pItem++){ if( pItem->iCursor>=0 ) continue; pItem->iCursor = pParse->nTab++; @@ -120092,7 +125310,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3SrcListAssignCursors(Parse *pParse, SrcList */ SQLITE_PRIVATE void sqlcipher_sqlite3SrcListDelete(sqlcipher_sqlite3 *db, SrcList *pList){ int i; - struct SrcList_item *pItem; + SrcItem *pItem; if( pList==0 ) return; for(pItem=pList->a, i=0; inSrc; i++, pItem++){ if( pItem->zDatabase ) sqlcipher_sqlite3DbFreeNN(db, pItem->zDatabase); @@ -120102,8 +125320,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3SrcListDelete(sqlcipher_sqlite3 *db, SrcLis if( pItem->fg.isTabFunc ) sqlcipher_sqlite3ExprListDelete(db, pItem->u1.pFuncArg); sqlcipher_sqlite3DeleteTable(db, pItem->pTab); if( pItem->pSelect ) sqlcipher_sqlite3SelectDelete(db, pItem->pSelect); - if( pItem->pOn ) sqlcipher_sqlite3ExprDelete(db, pItem->pOn); - if( pItem->pUsing ) sqlcipher_sqlite3IdListDelete(db, pItem->pUsing); + if( pItem->fg.isUsing ){ + sqlcipher_sqlite3IdListDelete(db, pItem->u3.pUsing); + }else if( pItem->u3.pOn ){ + sqlcipher_sqlite3ExprDelete(db, pItem->u3.pOn); + } } sqlcipher_sqlite3DbFreeNN(db, pList); } @@ -120131,14 +125352,13 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListAppendFromTerm( Token *pDatabase, /* Name of the database containing pTable */ Token *pAlias, /* The right-hand side of the AS subexpression */ Select *pSubquery, /* A subquery used in place of a table name */ - Expr *pOn, /* The ON clause of a join */ - IdList *pUsing /* The USING clause of a join */ + OnOrUsing *pOnUsing /* Either the ON clause or the USING clause */ ){ - struct SrcList_item *pItem; + SrcItem *pItem; sqlcipher_sqlite3 *db = pParse->db; - if( !p && (pOn || pUsing) ){ + if( !p && pOnUsing!=0 && (pOnUsing->pOn || pOnUsing->pUsing) ){ sqlcipher_sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s", - (pOn ? "ON" : "USING") + (pOnUsing->pOn ? "ON" : "USING") ); goto append_from_error; } @@ -120158,15 +125378,27 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListAppendFromTerm( if( pAlias->n ){ pItem->zAlias = sqlcipher_sqlite3NameFromToken(db, pAlias); } - pItem->pSelect = pSubquery; - pItem->pOn = pOn; - pItem->pUsing = pUsing; + if( pSubquery ){ + pItem->pSelect = pSubquery; + if( pSubquery->selFlags & SF_NestedFrom ){ + pItem->fg.isNestedFrom = 1; + } + } + assert( pOnUsing==0 || pOnUsing->pOn==0 || pOnUsing->pUsing==0 ); + assert( pItem->fg.isUsing==0 ); + if( pOnUsing==0 ){ + pItem->u3.pOn = 0; + }else if( pOnUsing->pUsing ){ + pItem->fg.isUsing = 1; + pItem->u3.pUsing = pOnUsing->pUsing; + }else{ + pItem->u3.pOn = pOnUsing->pOn; + } return p; - append_from_error: +append_from_error: assert( p==0 ); - sqlcipher_sqlite3ExprDelete(db, pOn); - sqlcipher_sqlite3IdListDelete(db, pUsing); + sqlcipher_sqlite3ClearOnOrUsing(db, pOnUsing); sqlcipher_sqlite3SelectDelete(db, pSubquery); return 0; } @@ -120178,7 +125410,7 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListAppendFromTerm( SQLITE_PRIVATE void sqlcipher_sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){ assert( pIndexedBy!=0 ); if( p && pIndexedBy->n>0 ){ - struct SrcList_item *pItem; + SrcItem *pItem; assert( p->nSrc>0 ); pItem = &p->a[p->nSrc-1]; assert( pItem->fg.notIndexed==0 ); @@ -120191,6 +125423,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, }else{ pItem->u1.zIndexedBy = sqlcipher_sqlite3NameFromToken(pParse->db, pIndexedBy); pItem->fg.isIndexedBy = 1; + assert( pItem->fg.isCte==0 ); /* No collision on union u2 */ } } } @@ -120208,8 +125441,9 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListAppendList(Parse *pParse, SrcLis sqlcipher_sqlite3SrcListDelete(pParse->db, p2); }else{ p1 = pNew; - memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(struct SrcList_item)); + memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem)); sqlcipher_sqlite3DbFree(pParse->db, p2); + p1->a[0].fg.jointype |= (JT_LTORJ & p1->a[1].fg.jointype); } } return p1; @@ -120221,7 +125455,7 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3SrcListAppendList(Parse *pParse, SrcLis */ SQLITE_PRIVATE void sqlcipher_sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){ if( p ){ - struct SrcList_item *pItem = &p->a[p->nSrc-1]; + SrcItem *pItem = &p->a[p->nSrc-1]; assert( pItem->fg.notIndexed==0 ); assert( pItem->fg.isIndexedBy==0 ); assert( pItem->fg.isTabFunc==0 ); @@ -120246,14 +125480,34 @@ SQLITE_PRIVATE void sqlcipher_sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ** The operator is "natural cross join". The A and B operands are stored ** in p->a[0] and p->a[1], respectively. The parser initially stores the ** operator with A. This routine shifts that operator over to B. +** +** Additional changes: +** +** * All tables to the left of the right-most RIGHT JOIN are tagged with +** JT_LTORJ (mnemonic: Left Table Of Right Join) so that the +** code generator can easily tell that the table is part of +** the left operand of at least one RIGHT JOIN. */ -SQLITE_PRIVATE void sqlcipher_sqlite3SrcListShiftJoinType(SrcList *p){ - if( p ){ - int i; - for(i=p->nSrc-1; i>0; i--){ - p->a[i].fg.jointype = p->a[i-1].fg.jointype; - } +SQLITE_PRIVATE void sqlcipher_sqlite3SrcListShiftJoinType(Parse *pParse, SrcList *p){ + (void)pParse; + if( p && p->nSrc>1 ){ + int i = p->nSrc-1; + u8 allFlags = 0; + do{ + allFlags |= p->a[i].fg.jointype = p->a[i-1].fg.jointype; + }while( (--i)>0 ); p->a[0].fg.jointype = 0; + + /* All terms to the left of a RIGHT JOIN should be tagged with the + ** JT_LTORJ flags */ + if( allFlags & JT_RIGHT ){ + for(i=p->nSrc-1; ALWAYS(i>0) && (p->a[i].fg.jointype&JT_RIGHT)==0; i--){} + i--; + assert( i>=0 ); + do{ + p->a[i].fg.jointype |= JT_LTORJ; + }while( (--i)>=0 ); + } } } @@ -120376,7 +125630,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3OpenTempDatabase(Parse *pParse){ static void sqlcipher_sqlite3CodeVerifySchemaAtToplevel(Parse *pToplevel, int iDb){ assert( iDb>=0 && iDbdb->nDb ); assert( pToplevel->db->aDb[iDb].pBt!=0 || iDb==1 ); - assert( iDbdb, iDb, 0) ); if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){ DbMaskSet(pToplevel->cookieMask, iDb); @@ -120503,7 +125757,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3UniqueConstraint( for(j=0; jnKeyCol; j++){ char *zCol; assert( pIdx->aiColumn[j]>=0 ); - zCol = pTab->aCol[pIdx->aiColumn[j]].zName; + zCol = pTab->aCol[pIdx->aiColumn[j]].zCnName; if( j ) sqlcipher_sqlite3_str_append(&errMsg, ", ", 2); sqlcipher_sqlite3_str_appendall(&errMsg, pTab->zName); sqlcipher_sqlite3_str_append(&errMsg, ".", 1); @@ -120530,7 +125784,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RowidConstraint( int rc; if( pTab->iPKey>=0 ){ zMsg = sqlcipher_sqlite3MPrintf(pParse->db, "%s.%s", pTab->zName, - pTab->aCol[pTab->iPKey].zName); + pTab->aCol[pTab->iPKey].zCnName); rc = SQLITE_CONSTRAINT_PRIMARYKEY; }else{ zMsg = sqlcipher_sqlite3MPrintf(pParse->db, "%s.rowid", pTab->zName); @@ -120718,24 +125972,76 @@ SQLITE_PRIVATE KeyInfo *sqlcipher_sqlite3KeyInfoOfIndex(Parse *pParse, Index *pI } #ifndef SQLITE_OMIT_CTE +/* +** Create a new CTE object +*/ +SQLITE_PRIVATE Cte *sqlcipher_sqlite3CteNew( + Parse *pParse, /* Parsing context */ + Token *pName, /* Name of the common-table */ + ExprList *pArglist, /* Optional column name list for the table */ + Select *pQuery, /* Query used to initialize the table */ + u8 eM10d /* The MATERIALIZED flag */ +){ + Cte *pNew; + sqlcipher_sqlite3 *db = pParse->db; + + pNew = sqlcipher_sqlite3DbMallocZero(db, sizeof(*pNew)); + assert( pNew!=0 || db->mallocFailed ); + + if( db->mallocFailed ){ + sqlcipher_sqlite3ExprListDelete(db, pArglist); + sqlcipher_sqlite3SelectDelete(db, pQuery); + }else{ + pNew->pSelect = pQuery; + pNew->pCols = pArglist; + pNew->zName = sqlcipher_sqlite3NameFromToken(pParse->db, pName); + pNew->eM10d = eM10d; + } + return pNew; +} + +/* +** Clear information from a Cte object, but do not deallocate storage +** for the object itself. +*/ +static void cteClear(sqlcipher_sqlite3 *db, Cte *pCte){ + assert( pCte!=0 ); + sqlcipher_sqlite3ExprListDelete(db, pCte->pCols); + sqlcipher_sqlite3SelectDelete(db, pCte->pSelect); + sqlcipher_sqlite3DbFree(db, pCte->zName); +} + +/* +** Free the contents of the CTE object passed as the second argument. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3CteDelete(sqlcipher_sqlite3 *db, Cte *pCte){ + assert( pCte!=0 ); + cteClear(db, pCte); + sqlcipher_sqlite3DbFree(db, pCte); +} + /* ** This routine is invoked once per CTE by the parser while parsing a -** WITH clause. +** WITH clause. The CTE described by teh third argument is added to +** the WITH clause of the second argument. If the second argument is +** NULL, then a new WITH argument is created. */ SQLITE_PRIVATE With *sqlcipher_sqlite3WithAdd( Parse *pParse, /* Parsing context */ With *pWith, /* Existing WITH clause, or NULL */ - Token *pName, /* Name of the common-table */ - ExprList *pArglist, /* Optional column name list for the table */ - Select *pQuery /* Query used to initialize the table */ + Cte *pCte /* CTE to add to the WITH clause */ ){ sqlcipher_sqlite3 *db = pParse->db; With *pNew; char *zName; + if( pCte==0 ){ + return pWith; + } + /* Check that the CTE name is unique within this WITH clause. If ** not, store an error in the Parse structure. */ - zName = sqlcipher_sqlite3NameFromToken(pParse->db, pName); + zName = pCte->zName; if( zName && pWith ){ int i; for(i=0; inCte; i++){ @@ -120754,16 +126060,11 @@ SQLITE_PRIVATE With *sqlcipher_sqlite3WithAdd( assert( (pNew!=0 && zName!=0) || db->mallocFailed ); if( db->mallocFailed ){ - sqlcipher_sqlite3ExprListDelete(db, pArglist); - sqlcipher_sqlite3SelectDelete(db, pQuery); - sqlcipher_sqlite3DbFree(db, zName); + sqlcipher_sqlite3CteDelete(db, pCte); pNew = pWith; }else{ - pNew->a[pNew->nCte].pSelect = pQuery; - pNew->a[pNew->nCte].pCols = pArglist; - pNew->a[pNew->nCte].zName = zName; - pNew->a[pNew->nCte].zCteErr = 0; - pNew->nCte++; + pNew->a[pNew->nCte++] = *pCte; + sqlcipher_sqlite3DbFree(db, pCte); } return pNew; @@ -120776,10 +126077,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WithDelete(sqlcipher_sqlite3 *db, With *pWi if( pWith ){ int i; for(i=0; inCte; i++){ - struct Cte *pCte = &pWith->a[i]; - sqlcipher_sqlite3ExprListDelete(db, pCte->pCols); - sqlcipher_sqlite3SelectDelete(db, pCte->pSelect); - sqlcipher_sqlite3DbFree(db, pCte->zName); + cteClear(db, &pWith->a[i]); } sqlcipher_sqlite3DbFree(db, pWith); } @@ -121127,6 +126425,7 @@ SQLITE_PRIVATE FuncDef *sqlcipher_sqlite3FunctionSearch( ){ FuncDef *p; for(p=sqlcipher_sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){ + assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); if( sqlcipher_sqlite3StrICmp(p->zName, zFunc)==0 ){ return p; } @@ -121147,7 +126446,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3InsertBuiltinFuncs( const char *zName = aDef[i].zName; int nName = sqlcipher_sqlite3Strlen30(zName); int h = SQLITE_FUNC_HASH(zName[0], nName); - assert( zName[0]>='a' && zName[0]<='z' ); + assert( aDef[i].funcFlags & SQLITE_FUNC_BUILTIN ); pOther = sqlcipher_sqlite3FunctionSearch(h, zName); if( pOther ){ assert( pOther!=&aDef[i] && pOther->pNext!=&aDef[i] ); @@ -121358,7 +126657,7 @@ SQLITE_PRIVATE Schema *sqlcipher_sqlite3SchemaGet(sqlcipher_sqlite3 *db, Btree * ** */ SQLITE_PRIVATE Table *sqlcipher_sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ - struct SrcList_item *pItem = pSrc->a; + SrcItem *pItem = pSrc->a; Table *pTab; assert( pItem && pSrc->nSrc>=1 ); pTab = sqlcipher_sqlite3LocateTableItem(pParse, 0, pItem); @@ -121366,13 +126665,23 @@ SQLITE_PRIVATE Table *sqlcipher_sqlite3SrcListLookup(Parse *pParse, SrcList *pSr pItem->pTab = pTab; if( pTab ){ pTab->nTabRef++; - } - if( sqlcipher_sqlite3IndexedByLookup(pParse, pItem) ){ - pTab = 0; + if( pItem->fg.isIndexedBy && sqlcipher_sqlite3IndexedByLookup(pParse, pItem) ){ + pTab = 0; + } } return pTab; } +/* Generate byte-code that will report the number of rows modified +** by a DELETE, INSERT, or UPDATE statement. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3CodeChangeCount(Vdbe *v, int regCounter, const char *zColName){ + sqlcipher_sqlite3VdbeAddOp0(v, OP_FkCheck); + sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, regCounter, 1); + sqlcipher_sqlite3VdbeSetNumCols(v, 1); + sqlcipher_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zColName, SQLITE_STATIC); +} + /* Return true if table pTab is read-only. ** ** A table is read-only if any of the following are true: @@ -121413,7 +126722,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3IsReadOnly(Parse *pParse, Table *pTab, int v return 1; } #ifndef SQLITE_OMIT_VIEW - if( !viewOk && pTab->pSelect ){ + if( !viewOk && IsView(pTab) ){ sqlcipher_sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); return 1; } @@ -121447,8 +126756,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3MaterializeView( assert( pFrom->nSrc==1 ); pFrom->a[0].zName = sqlcipher_sqlite3DbStrDup(db, pView->zName); pFrom->a[0].zDatabase = sqlcipher_sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); - assert( pFrom->a[0].pOn==0 ); - assert( pFrom->a[0].pUsing==0 ); + assert( pFrom->a[0].fg.isUsing==0 ); + assert( pFrom->a[0].u3.pOn==0 ); } pSel = sqlcipher_sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy, SF_IncludeHidden, pLimit); @@ -121517,13 +126826,13 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3LimitWhere( }else{ Index *pPk = sqlcipher_sqlite3PrimaryKeyIndex(pTab); if( pPk->nKeyCol==1 ){ - const char *zName = pTab->aCol[pPk->aiColumn[0]].zName; + const char *zName = pTab->aCol[pPk->aiColumn[0]].zCnName; pLhs = sqlcipher_sqlite3Expr(db, TK_ID, zName); pEList = sqlcipher_sqlite3ExprListAppend(pParse, 0, sqlcipher_sqlite3Expr(db, TK_ID, zName)); }else{ int i; for(i=0; inKeyCol; i++){ - Expr *p = sqlcipher_sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName); + Expr *p = sqlcipher_sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zCnName); pEList = sqlcipher_sqlite3ExprListAppend(pParse, pEList, p); } pLhs = sqlcipher_sqlite3PExpr(pParse, TK_VECTOR, 0, 0); @@ -121536,9 +126845,16 @@ SQLITE_PRIVATE Expr *sqlcipher_sqlite3LimitWhere( /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree ** and the SELECT subtree. */ pSrc->a[0].pTab = 0; - pSelectSrc = sqlcipher_sqlite3SrcListDup(pParse->db, pSrc, 0); + pSelectSrc = sqlcipher_sqlite3SrcListDup(db, pSrc, 0); pSrc->a[0].pTab = pTab; - pSrc->a[0].pIBIndex = 0; + if( pSrc->a[0].fg.isIndexedBy ){ + assert( pSrc->a[0].fg.isCte==0 ); + pSrc->a[0].u2.pIBIndex = 0; + pSrc->a[0].fg.isIndexedBy = 0; + sqlcipher_sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy); + }else if( pSrc->a[0].fg.isCte ){ + pSrc->a[0].u2.pCteUse->nUse++; + } /* generate the SELECT expression tree. */ pSelect = sqlcipher_sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0, @@ -121605,12 +126921,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DeleteFrom( memset(&sContext, 0, sizeof(sContext)); db = pParse->db; - if( pParse->nErr || db->mallocFailed ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto delete_from_cleanup; } + assert( db->mallocFailed==0 ); assert( pTabList->nSrc==1 ); - /* Locate the table which we want to delete. This table has to be ** put in an SrcList structure because some of the subroutines we ** will be calling are designed to work with multiple tables and expect @@ -121624,7 +126941,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DeleteFrom( */ #ifndef SQLITE_OMIT_TRIGGER pTrigger = sqlcipher_sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); - isView = pTab->pSelect!=0; + isView = IsView(pTab); #else # define pTrigger 0 # define isView 0 @@ -121635,6 +126952,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DeleteFrom( # define isView 0 #endif +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x10000 ){ + sqlcipher_sqlite3TreeViewLine(0, "In sqlcipher_sqlite3Delete() at %s:%d", __FILE__, __LINE__); + sqlcipher_sqlite3TreeViewDelete(pParse->pWith, pTabList, pWhere, + pOrderBy, pLimit, pTrigger); + } +#endif + #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT if( !isView ){ pWhere = sqlcipher_sqlite3LimitWhere( @@ -121716,6 +127041,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DeleteFrom( if( (db->flags & SQLITE_CountRows)!=0 && !pParse->nested && !pParse->pTriggerTab + && !pParse->bReturning ){ memCnt = ++pParse->nMem; sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt); @@ -121750,6 +127076,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DeleteFrom( for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ assert( pIdx->pSchema==pTab->pSchema ); sqlcipher_sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); + if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ + sqlcipher_sqlite3VdbeChangeP3(v, -1, memCnt ? memCnt : -1); + } } }else #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ @@ -121784,7 +127113,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DeleteFrom( ** ONEPASS_SINGLE: One-pass approach - at most one row deleted. ** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted. */ - pWInfo = sqlcipher_sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1); + pWInfo = sqlcipher_sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,0,wcf,iTabCur+1); if( pWInfo==0 ) goto delete_from_cleanup; eOnePass = sqlcipher_sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI ); @@ -121870,7 +127199,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DeleteFrom( if( eOnePass!=ONEPASS_OFF ){ assert( nKey==nPk ); /* OP_Found will use an unpacked key */ if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){ - assert( pPk!=0 || pTab->pSelect!=0 ); + assert( pPk!=0 || IsView(pTab) ); sqlcipher_sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); VdbeCoverage(v); } @@ -121937,9 +127266,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DeleteFrom( ** invoke the callback function. */ if( memCnt ){ - sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1); - sqlcipher_sqlite3VdbeSetNumCols(v, 1); - sqlcipher_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC); + sqlcipher_sqlite3CodeChangeCount(v, memCnt, "rows deleted"); } delete_from_cleanup: @@ -122104,7 +127431,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateRowDelete( ** the update-hook is not invoked for rows removed by REPLACE, but the ** pre-update-hook is. */ - if( pTab->pSelect==0 ){ + if( !IsView(pTab) ){ u8 p5 = 0; sqlcipher_sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek); sqlcipher_sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0)); @@ -122261,13 +127588,15 @@ SQLITE_PRIVATE int sqlcipher_sqlite3GenerateIndexKey( continue; } sqlcipher_sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j); - /* If the column affinity is REAL but the number is an integer, then it - ** might be stored in the table as an integer (using a compact - ** representation) then converted to REAL by an OP_RealAffinity opcode. - ** But we are getting ready to store this value back into an index, where - ** it should be converted by to INTEGER again. So omit the OP_RealAffinity - ** opcode if it is present */ - sqlcipher_sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); + if( pIdx->aiColumn[j]>=0 ){ + /* If the column affinity is REAL but the number is an integer, then it + ** might be stored in the table as an integer (using a compact + ** representation) then converted to REAL by an OP_RealAffinity opcode. + ** But we are getting ready to store this value back into an index, where + ** it should be converted by to INTEGER again. So omit the + ** OP_RealAffinity opcode if it is present */ + sqlcipher_sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); + } } if( regOut ){ sqlcipher_sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); @@ -122388,6 +127717,18 @@ static void typeofFunc( sqlcipher_sqlite3_result_text(context, azType[i], -1, SQLITE_STATIC); } +/* subtype(X) +** +** Return the subtype of X +*/ +static void subtypeFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + UNUSED_PARAMETER(argc); + sqlcipher_sqlite3_result_int(context, sqlcipher_sqlite3_value_subtype(argv[0])); +} /* ** Implementation of the length() function @@ -122549,7 +127890,7 @@ endInstrOOM: } /* -** Implementation of the printf() function. +** Implementation of the printf() (a.k.a. format()) SQL function. */ static void printfFunc( sqlcipher_sqlite3_context *context, @@ -122862,9 +128203,9 @@ static void last_insert_rowid( /* ** Implementation of the changes() SQL function. ** -** IMP: R-62073-11209 The changes() SQL function is a wrapper -** around the sqlcipher_sqlite3_changes() C/C++ function and hence follows the same -** rules for counting changes. +** IMP: R-32760-32347 The changes() SQL function is a wrapper +** around the sqlcipher_sqlite3_changes64() C/C++ function and hence follows the +** same rules for counting changes. */ static void changes( sqlcipher_sqlite3_context *context, @@ -122873,12 +128214,12 @@ static void changes( ){ sqlcipher_sqlite3 *db = sqlcipher_sqlite3_context_db_handle(context); UNUSED_PARAMETER2(NotUsed, NotUsed2); - sqlcipher_sqlite3_result_int(context, sqlcipher_sqlite3_changes(db)); + sqlcipher_sqlite3_result_int64(context, sqlcipher_sqlite3_changes64(db)); } /* ** Implementation of the total_changes() SQL function. The return value is -** the same as the sqlcipher_sqlite3_total_changes() API function. +** the same as the sqlcipher_sqlite3_total_changes64() API function. */ static void total_changes( sqlcipher_sqlite3_context *context, @@ -122887,9 +128228,9 @@ static void total_changes( ){ sqlcipher_sqlite3 *db = sqlcipher_sqlite3_context_db_handle(context); UNUSED_PARAMETER2(NotUsed, NotUsed2); - /* IMP: R-52756-41993 This function is a wrapper around the - ** sqlcipher_sqlite3_total_changes() C/C++ interface. */ - sqlcipher_sqlite3_result_int(context, sqlcipher_sqlite3_total_changes(db)); + /* IMP: R-11217-42568 This function is a wrapper around the + ** sqlcipher_sqlite3_total_changes64() C/C++ interface. */ + sqlcipher_sqlite3_result_int64(context, sqlcipher_sqlite3_total_changes64(db)); } /* @@ -122985,7 +128326,8 @@ static int patternCompare( /* Skip over multiple "*" characters in the pattern. If there ** are also "?" characters, skip those as well, but consume a ** single character of the input string for each "?" skipped */ - while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){ + while( (c=Utf8Read(zPattern)) == matchAll + || (c == matchOne && matchOne!=0) ){ if( c==matchOne && sqlcipher_sqlite3Utf8Read(&zString)==0 ){ return SQLITE_NOWILDCARDMATCH; } @@ -123317,39 +128659,42 @@ static const char hexdigits[] = { }; /* -** Implementation of the QUOTE() function. This function takes a single -** argument. If the argument is numeric, the return value is the same as -** the argument. If the argument is NULL, the return value is the string -** "NULL". Otherwise, the argument is enclosed in single quotes with -** single-quote escapes. +** Append to pStr text that is the SQL literal representation of the +** value contained in pValue. */ -static void quoteFunc(sqlcipher_sqlite3_context *context, int argc, sqlcipher_sqlite3_value **argv){ - assert( argc==1 ); - UNUSED_PARAMETER(argc); - switch( sqlcipher_sqlite3_value_type(argv[0]) ){ +SQLITE_PRIVATE void sqlcipher_sqlite3QuoteValue(StrAccum *pStr, sqlcipher_sqlite3_value *pValue){ + /* As currently implemented, the string must be initially empty. + ** we might relax this requirement in the future, but that will + ** require enhancements to the implementation. */ + assert( pStr!=0 && pStr->nChar==0 ); + + switch( sqlcipher_sqlite3_value_type(pValue) ){ case SQLITE_FLOAT: { double r1, r2; - char zBuf[50]; - r1 = sqlcipher_sqlite3_value_double(argv[0]); - sqlcipher_sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1); - sqlcipher_sqlite3AtoF(zBuf, &r2, 20, SQLITE_UTF8); - if( r1!=r2 ){ - sqlcipher_sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.20e", r1); + const char *zVal; + r1 = sqlcipher_sqlite3_value_double(pValue); + sqlcipher_sqlite3_str_appendf(pStr, "%!.15g", r1); + zVal = sqlcipher_sqlite3_str_value(pStr); + if( zVal ){ + sqlcipher_sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8); + if( r1!=r2 ){ + sqlcipher_sqlite3_str_reset(pStr); + sqlcipher_sqlite3_str_appendf(pStr, "%!.20e", r1); + } } - sqlcipher_sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); break; } case SQLITE_INTEGER: { - sqlcipher_sqlite3_result_value(context, argv[0]); + sqlcipher_sqlite3_str_appendf(pStr, "%lld", sqlcipher_sqlite3_value_int64(pValue)); break; } case SQLITE_BLOB: { - char *zText = 0; - char const *zBlob = sqlcipher_sqlite3_value_blob(argv[0]); - int nBlob = sqlcipher_sqlite3_value_bytes(argv[0]); - assert( zBlob==sqlcipher_sqlite3_value_blob(argv[0]) ); /* No encoding change */ - zText = (char *)contextMalloc(context, (2*(i64)nBlob)+4); - if( zText ){ + char const *zBlob = sqlcipher_sqlite3_value_blob(pValue); + int nBlob = sqlcipher_sqlite3_value_bytes(pValue); + assert( zBlob==sqlcipher_sqlite3_value_blob(pValue) ); /* No encoding change */ + sqlcipher_sqlite3StrAccumEnlarge(pStr, nBlob*2 + 4); + if( pStr->accError==0 ){ + char *zText = pStr->zText; int i; for(i=0; i>4)&0x0F]; @@ -123359,42 +128704,48 @@ static void quoteFunc(sqlcipher_sqlite3_context *context, int argc, sqlcipher_sq zText[(nBlob*2)+3] = '\0'; zText[0] = 'X'; zText[1] = '\''; - sqlcipher_sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); - sqlcipher_sqlite3_free(zText); + pStr->nChar = nBlob*2 + 3; } break; } case SQLITE_TEXT: { - int i,j; - u64 n; - const unsigned char *zArg = sqlcipher_sqlite3_value_text(argv[0]); - char *z; - - if( zArg==0 ) return; - for(i=0, n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; } - z = contextMalloc(context, ((i64)i)+((i64)n)+3); - if( z ){ - z[0] = '\''; - for(i=0, j=1; zArg[i]; i++){ - z[j++] = zArg[i]; - if( zArg[i]=='\'' ){ - z[j++] = '\''; - } - } - z[j++] = '\''; - z[j] = 0; - sqlcipher_sqlite3_result_text(context, z, j, sqlcipher_sqlite3_free); - } + const unsigned char *zArg = sqlcipher_sqlite3_value_text(pValue); + sqlcipher_sqlite3_str_appendf(pStr, "%Q", zArg); break; } default: { - assert( sqlcipher_sqlite3_value_type(argv[0])==SQLITE_NULL ); - sqlcipher_sqlite3_result_text(context, "NULL", 4, SQLITE_STATIC); + assert( sqlcipher_sqlite3_value_type(pValue)==SQLITE_NULL ); + sqlcipher_sqlite3_str_append(pStr, "NULL", 4); break; } } } +/* +** Implementation of the QUOTE() function. +** +** The quote(X) function returns the text of an SQL literal which is the +** value of its argument suitable for inclusion into an SQL statement. +** Strings are surrounded by single-quotes with escapes on interior quotes +** as needed. BLOBs are encoded as hexadecimal literals. Strings with +** embedded NUL characters cannot be represented as string literals in SQL +** and hence the returned string literal is truncated prior to the first NUL. +*/ +static void quoteFunc(sqlcipher_sqlite3_context *context, int argc, sqlcipher_sqlite3_value **argv){ + sqlcipher_sqlite3_str str; + sqlcipher_sqlite3 *db = sqlcipher_sqlite3_context_db_handle(context); + assert( argc==1 ); + UNUSED_PARAMETER(argc); + sqlcipher_sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); + sqlcipher_sqlite3QuoteValue(&str,argv[0]); + sqlcipher_sqlite3_result_text(context, sqlcipher_sqlite3StrAccumFinish(&str), str.nChar, + SQLITE_DYNAMIC); + if( str.accError!=SQLITE_OK ){ + sqlcipher_sqlite3_result_null(context); + sqlcipher_sqlite3_result_error_code(context, str.accError); + } +} + /* ** The unicode() function. Return the integer unicode code-point value ** for the first character of the input string. @@ -123606,10 +128957,10 @@ static void trimFunc( ){ const unsigned char *zIn; /* Input string */ const unsigned char *zCharSet; /* Set of characters to trim */ - int nIn; /* Number of bytes in input */ + unsigned int nIn; /* Number of bytes in input */ int flags; /* 1: trimleft 2: trimright 3: trim */ int i; /* Loop counter */ - unsigned char *aLen = 0; /* Length of each character in zCharSet */ + unsigned int *aLen = 0; /* Length of each character in zCharSet */ unsigned char **azChar = 0; /* Individual characters in zCharSet */ int nChar; /* Number of characters in zCharSet */ @@ -123618,13 +128969,13 @@ static void trimFunc( } zIn = sqlcipher_sqlite3_value_text(argv[0]); if( zIn==0 ) return; - nIn = sqlcipher_sqlite3_value_bytes(argv[0]); + nIn = (unsigned)sqlcipher_sqlite3_value_bytes(argv[0]); assert( zIn==sqlcipher_sqlite3_value_text(argv[0]) ); if( argc==1 ){ - static const unsigned char lenOne[] = { 1 }; + static const unsigned lenOne[] = { 1 }; static unsigned char * const azOne[] = { (u8*)" " }; nChar = 1; - aLen = (u8*)lenOne; + aLen = (unsigned*)lenOne; azChar = (unsigned char **)azOne; zCharSet = 0; }else if( (zCharSet = sqlcipher_sqlite3_value_text(argv[1]))==0 ){ @@ -123635,15 +128986,16 @@ static void trimFunc( SQLITE_SKIP_UTF8(z); } if( nChar>0 ){ - azChar = contextMalloc(context, ((i64)nChar)*(sizeof(char*)+1)); + azChar = contextMalloc(context, + ((i64)nChar)*(sizeof(char*)+sizeof(unsigned))); if( azChar==0 ){ return; } - aLen = (unsigned char*)&azChar[nChar]; + aLen = (unsigned*)&azChar[nChar]; for(z=zCharSet, nChar=0; *z; nChar++){ azChar[nChar] = (unsigned char *)z; SQLITE_SKIP_UTF8(z); - aLen[nChar] = (u8)(z - azChar[nChar]); + aLen[nChar] = (unsigned)(z - azChar[nChar]); } } } @@ -123651,7 +129003,7 @@ static void trimFunc( flags = SQLITE_PTR_TO_INT(sqlcipher_sqlite3_user_data(context)); if( flags & 1 ){ while( nIn>0 ){ - int len = 0; + unsigned int len = 0; for(i=0; i0 ){ - int len = 0; + unsigned int len = 0; for(i=0; imxAlloc==0; - pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; - if( !firstTerm ){ - if( argc==2 ){ - zSep = (char*)sqlcipher_sqlite3_value_text(argv[1]); - nSep = sqlcipher_sqlite3_value_bytes(argv[1]); - }else{ - zSep = ","; - nSep = 1; + int firstTerm = pGCC->str.mxAlloc==0; + pGCC->str.mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; + if( argc==1 ){ + if( !firstTerm ){ + sqlcipher_sqlite3_str_appendchar(&pGCC->str, 1, ','); } - if( zSep ) sqlcipher_sqlite3_str_append(pAccum, zSep, nSep); +#ifndef SQLITE_OMIT_WINDOWFUNC + else{ + pGCC->nFirstSepLength = 1; + } +#endif + }else if( !firstTerm ){ + zSep = (char*)sqlcipher_sqlite3_value_text(argv[1]); + nSep = sqlcipher_sqlite3_value_bytes(argv[1]); + if( zSep ){ + sqlcipher_sqlite3_str_append(&pGCC->str, zSep, nSep); + } +#ifndef SQLITE_OMIT_WINDOWFUNC + else{ + nSep = 0; + } + if( nSep != pGCC->nFirstSepLength || pGCC->pnSepLengths != 0 ){ + int *pnsl = pGCC->pnSepLengths; + if( pnsl == 0 ){ + /* First separator length variation seen, start tracking them. */ + pnsl = (int*)sqlcipher_sqlite3_malloc64((pGCC->nAccum+1) * sizeof(int)); + if( pnsl!=0 ){ + int i = 0, nA = pGCC->nAccum-1; + while( inFirstSepLength; + } + }else{ + pnsl = (int*)sqlcipher_sqlite3_realloc64(pnsl, pGCC->nAccum * sizeof(int)); + } + if( pnsl!=0 ){ + if( ALWAYS(pGCC->nAccum>0) ){ + pnsl[pGCC->nAccum-1] = nSep; + } + pGCC->pnSepLengths = pnsl; + }else{ + sqlcipher_sqlite3StrAccumSetError(&pGCC->str, SQLITE_NOMEM); + } + } +#endif + } +#ifndef SQLITE_OMIT_WINDOWFUNC + else{ + pGCC->nFirstSepLength = sqlcipher_sqlite3_value_bytes(argv[1]); } + pGCC->nAccum += 1; +#endif zVal = (char*)sqlcipher_sqlite3_value_text(argv[0]); nVal = sqlcipher_sqlite3_value_bytes(argv[0]); - if( zVal ) sqlcipher_sqlite3_str_append(pAccum, zVal, nVal); + if( zVal ) sqlcipher_sqlite3_str_append(&pGCC->str, zVal, nVal); } } + #ifndef SQLITE_OMIT_WINDOWFUNC static void groupConcatInverse( sqlcipher_sqlite3_context *context, int argc, sqlcipher_sqlite3_value **argv ){ - int n; - StrAccum *pAccum; + GroupConcatCtx *pGCC; assert( argc==1 || argc==2 ); + (void)argc; /* Suppress unused parameter warning */ if( sqlcipher_sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - pAccum = (StrAccum*)sqlcipher_sqlite3_aggregate_context(context, sizeof(*pAccum)); - /* pAccum is always non-NULL since groupConcatStep() will have always + pGCC = (GroupConcatCtx*)sqlcipher_sqlite3_aggregate_context(context, sizeof(*pGCC)); + /* pGCC is always non-NULL since groupConcatStep() will have always ** run frist to initialize it */ - if( ALWAYS(pAccum) ){ - n = sqlcipher_sqlite3_value_bytes(argv[0]); - if( argc==2 ){ - n += sqlcipher_sqlite3_value_bytes(argv[1]); + if( ALWAYS(pGCC) ){ + int nVS; + /* Must call sqlcipher_sqlite3_value_text() to convert the argument into text prior + ** to invoking sqlcipher_sqlite3_value_bytes(), in case the text encoding is UTF16 */ + (void)sqlcipher_sqlite3_value_text(argv[0]); + nVS = sqlcipher_sqlite3_value_bytes(argv[0]); + pGCC->nAccum -= 1; + if( pGCC->pnSepLengths!=0 ){ + assert(pGCC->nAccum >= 0); + if( pGCC->nAccum>0 ){ + nVS += *pGCC->pnSepLengths; + memmove(pGCC->pnSepLengths, pGCC->pnSepLengths+1, + (pGCC->nAccum-1)*sizeof(int)); + } }else{ - n++; + /* If removing single accumulated string, harmlessly over-do. */ + nVS += pGCC->nFirstSepLength; } - if( n>=(int)pAccum->nChar ){ - pAccum->nChar = 0; + if( nVS>=(int)pGCC->str.nChar ){ + pGCC->str.nChar = 0; }else{ - pAccum->nChar -= n; - memmove(pAccum->zText, &pAccum->zText[n], pAccum->nChar); + pGCC->str.nChar -= nVS; + memmove(pGCC->str.zText, &pGCC->str.zText[nVS], pGCC->str.nChar); + } + if( pGCC->str.nChar==0 ){ + pGCC->str.mxAlloc = 0; + sqlcipher_sqlite3_free(pGCC->pnSepLengths); + pGCC->pnSepLengths = 0; } - if( pAccum->nChar==0 ) pAccum->mxAlloc = 0; } } #else # define groupConcatInverse 0 #endif /* SQLITE_OMIT_WINDOWFUNC */ static void groupConcatFinalize(sqlcipher_sqlite3_context *context){ - StrAccum *pAccum; - pAccum = sqlcipher_sqlite3_aggregate_context(context, 0); - if( pAccum ){ - if( pAccum->accError==SQLITE_TOOBIG ){ - sqlcipher_sqlite3_result_error_toobig(context); - }else if( pAccum->accError==SQLITE_NOMEM ){ - sqlcipher_sqlite3_result_error_nomem(context); - }else{ - sqlcipher_sqlite3_result_text(context, sqlcipher_sqlite3StrAccumFinish(pAccum), -1, - sqlcipher_sqlite3_free); - } + GroupConcatCtx *pGCC + = (GroupConcatCtx*)sqlcipher_sqlite3_aggregate_context(context, 0); + if( pGCC ){ + sqlcipher_sqlite3ResultStrAccum(context, &pGCC->str); +#ifndef SQLITE_OMIT_WINDOWFUNC + sqlcipher_sqlite3_free(pGCC->pnSepLengths); +#endif } } #ifndef SQLITE_OMIT_WINDOWFUNC static void groupConcatValue(sqlcipher_sqlite3_context *context){ - sqlcipher_sqlite3_str *pAccum; - pAccum = (sqlcipher_sqlite3_str*)sqlcipher_sqlite3_aggregate_context(context, 0); - if( pAccum ){ + GroupConcatCtx *pGCC + = (GroupConcatCtx*)sqlcipher_sqlite3_aggregate_context(context, 0); + if( pGCC ){ + StrAccum *pAccum = &pGCC->str; if( pAccum->accError==SQLITE_TOOBIG ){ sqlcipher_sqlite3_result_error_toobig(context); }else if( pAccum->accError==SQLITE_NOMEM ){ sqlcipher_sqlite3_result_error_nomem(context); }else{ const char *zText = sqlcipher_sqlite3_str_value(pAccum); - sqlcipher_sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); + sqlcipher_sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT); } } } @@ -124167,11 +129589,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RegisterLikeFunctions(sqlcipher_sqlite3 *db SQLITE_PRIVATE int sqlcipher_sqlite3IsLikeFunction(sqlcipher_sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ FuncDef *pDef; int nExpr; - if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){ + assert( pExpr!=0 ); + assert( pExpr->op==TK_FUNCTION ); + assert( ExprUseXList(pExpr) ); + if( !pExpr->x.pList ){ return 0; } - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); nExpr = pExpr->x.pList->nExpr; + assert( !ExprHasProperty(pExpr, EP_IntValue) ); pDef = sqlcipher_sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0); #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION if( pDef==0 ) return 0; @@ -124195,6 +129620,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3IsLikeFunction(sqlcipher_sqlite3 *db, Expr * Expr *pEscape = pExpr->x.pList->a[2].pExpr; char *zEscape; if( pEscape->op!=TK_STRING ) return 0; + assert( !ExprHasProperty(pEscape, EP_IntValue) ); zEscape = pEscape->u.zToken; if( zEscape[0]==0 || zEscape[1]!=0 ) return 0; if( zEscape[0]==aWc[0] ) return 0; @@ -124206,6 +129632,201 @@ SQLITE_PRIVATE int sqlcipher_sqlite3IsLikeFunction(sqlcipher_sqlite3 *db, Expr * return 1; } +/* Mathematical Constants */ +#ifndef M_PI +# define M_PI 3.141592653589793238462643383279502884 +#endif +#ifndef M_LN10 +# define M_LN10 2.302585092994045684017991454684364208 +#endif +#ifndef M_LN2 +# define M_LN2 0.693147180559945309417232121458176568 +#endif + + +/* Extra math functions that require linking with -lm +*/ +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS +/* +** Implementation SQL functions: +** +** ceil(X) +** ceiling(X) +** floor(X) +** +** The sqlcipher_sqlite3_user_data() pointer is a pointer to the libm implementation +** of the underlying C function. +*/ +static void ceilingFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + assert( argc==1 ); + switch( sqlcipher_sqlite3_value_numeric_type(argv[0]) ){ + case SQLITE_INTEGER: { + sqlcipher_sqlite3_result_int64(context, sqlcipher_sqlite3_value_int64(argv[0])); + break; + } + case SQLITE_FLOAT: { + double (*x)(double) = (double(*)(double))sqlcipher_sqlite3_user_data(context); + sqlcipher_sqlite3_result_double(context, x(sqlcipher_sqlite3_value_double(argv[0]))); + break; + } + default: { + break; + } + } +} + +/* +** On some systems, ceil() and floor() are intrinsic function. You are +** unable to take a pointer to these functions. Hence, we here wrap them +** in our own actual functions. +*/ +static double xCeil(double x){ return ceil(x); } +static double xFloor(double x){ return floor(x); } + +/* +** Implementation of SQL functions: +** +** ln(X) - natural logarithm +** log(X) - log X base 10 +** log10(X) - log X base 10 +** log(B,X) - log X base B +*/ +static void logFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + double x, b, ans; + assert( argc==1 || argc==2 ); + switch( sqlcipher_sqlite3_value_numeric_type(argv[0]) ){ + case SQLITE_INTEGER: + case SQLITE_FLOAT: + x = sqlcipher_sqlite3_value_double(argv[0]); + if( x<=0.0 ) return; + break; + default: + return; + } + if( argc==2 ){ + switch( sqlcipher_sqlite3_value_numeric_type(argv[0]) ){ + case SQLITE_INTEGER: + case SQLITE_FLOAT: + b = log(x); + if( b<=0.0 ) return; + x = sqlcipher_sqlite3_value_double(argv[1]); + if( x<=0.0 ) return; + break; + default: + return; + } + ans = log(x)/b; + }else{ + ans = log(x); + switch( SQLITE_PTR_TO_INT(sqlcipher_sqlite3_user_data(context)) ){ + case 1: + /* Convert from natural logarithm to log base 10 */ + ans /= M_LN10; + break; + case 2: + /* Convert from natural logarithm to log base 2 */ + ans /= M_LN2; + break; + default: + break; + } + } + sqlcipher_sqlite3_result_double(context, ans); +} + +/* +** Functions to converts degrees to radians and radians to degrees. +*/ +static double degToRad(double x){ return x*(M_PI/180.0); } +static double radToDeg(double x){ return x*(180.0/M_PI); } + +/* +** Implementation of 1-argument SQL math functions: +** +** exp(X) - Compute e to the X-th power +*/ +static void math1Func( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + int type0; + double v0, ans; + double (*x)(double); + assert( argc==1 ); + type0 = sqlcipher_sqlite3_value_numeric_type(argv[0]); + if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return; + v0 = sqlcipher_sqlite3_value_double(argv[0]); + x = (double(*)(double))sqlcipher_sqlite3_user_data(context); + ans = x(v0); + sqlcipher_sqlite3_result_double(context, ans); +} + +/* +** Implementation of 2-argument SQL math functions: +** +** power(X,Y) - Compute X to the Y-th power +*/ +static void math2Func( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + int type0, type1; + double v0, v1, ans; + double (*x)(double,double); + assert( argc==2 ); + type0 = sqlcipher_sqlite3_value_numeric_type(argv[0]); + if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return; + type1 = sqlcipher_sqlite3_value_numeric_type(argv[1]); + if( type1!=SQLITE_INTEGER && type1!=SQLITE_FLOAT ) return; + v0 = sqlcipher_sqlite3_value_double(argv[0]); + v1 = sqlcipher_sqlite3_value_double(argv[1]); + x = (double(*)(double,double))sqlcipher_sqlite3_user_data(context); + ans = x(v0, v1); + sqlcipher_sqlite3_result_double(context, ans); +} + +/* +** Implementation of 0-argument pi() function. +*/ +static void piFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + assert( argc==0 ); + sqlcipher_sqlite3_result_double(context, M_PI); +} + +#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */ + +/* +** Implementation of sign(X) function. +*/ +static void signFunc( + sqlcipher_sqlite3_context *context, + int argc, + sqlcipher_sqlite3_value **argv +){ + int type0; + double x; + UNUSED_PARAMETER(argc); + assert( argc==1 ); + type0 = sqlcipher_sqlite3_value_numeric_type(argv[0]); + if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return; + x = sqlcipher_sqlite3_value_double(argv[0]); + sqlcipher_sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0); +} + /* ** All of the FuncDef structures in the aBuiltinFunc[] array above ** to the global function hash table. This occurs at start-time (as @@ -124226,12 +129847,12 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RegisterBuiltinFunctions(void){ */ static FuncDef aBuiltinFunc[] = { /***** Functions only available with SQLITE_TESTCTRL_INTERNAL_FUNCTIONS *****/ +#if !defined(SQLITE_UNTESTABLE) TEST_FUNC(implies_nonnull_row, 2, INLINEFUNC_implies_nonnull_row, 0), TEST_FUNC(expr_compare, 2, INLINEFUNC_expr_compare, 0), TEST_FUNC(expr_implies_expr, 2, INLINEFUNC_expr_implies_expr, 0), -#ifdef SQLITE_DEBUG - TEST_FUNC(affinity, 1, INLINEFUNC_affinity, 0), -#endif + TEST_FUNC(affinity, 1, INLINEFUNC_affinity, 0), +#endif /* !defined(SQLITE_UNTESTABLE) */ /***** Regular functions *****/ #ifdef SQLITE_SOUNDEX FUNCTION(soundex, 1, 0, 0, soundexFunc ), @@ -124251,8 +129872,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RegisterBuiltinFunctions(void){ INLINE_FUNC(likelihood, 2, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY), INLINE_FUNC(likely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY), #ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC - FUNCTION2(sqlite_offset, 1, 0, 0, noopFunc, SQLITE_FUNC_OFFSET| - SQLITE_FUNC_TYPEOF), + INLINE_FUNC(sqlite_offset, 1, INLINEFUNC_sqlite_offset, 0 ), #endif FUNCTION(ltrim, 1, 1, 0, trimFunc ), FUNCTION(ltrim, 2, 1, 0, trimFunc ), @@ -124263,15 +129883,17 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RegisterBuiltinFunctions(void){ FUNCTION(min, -1, 0, 1, minmaxFunc ), FUNCTION(min, 0, 0, 1, 0 ), WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, - SQLITE_FUNC_MINMAX ), + SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), FUNCTION(max, -1, 1, 1, minmaxFunc ), FUNCTION(max, 0, 1, 1, 0 ), WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, - SQLITE_FUNC_MINMAX ), + SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), + FUNCTION2(subtype, 1, 0, 0, subtypeFunc, SQLITE_FUNC_TYPEOF), FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), FUNCTION(instr, 2, 0, 0, instrFunc ), FUNCTION(printf, -1, 0, 0, printfFunc ), + FUNCTION(format, -1, 0, 0, printfFunc ), FUNCTION(unicode, 1, 0, 0, unicodeFunc ), FUNCTION(char, -1, 0, 0, charFunc ), FUNCTION(abs, 1, 0, 0, absFunc ), @@ -124303,9 +129925,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RegisterBuiltinFunctions(void){ WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0), WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0), WAGGREGATE(count, 0,0,0, countStep, - countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ), + countFinalize, countFinalize, countInverse, + SQLITE_FUNC_COUNT|SQLITE_FUNC_ANYORDER ), WAGGREGATE(count, 1,0,0, countStep, - countFinalize, countFinalize, countInverse, 0 ), + countFinalize, countFinalize, countInverse, SQLITE_FUNC_ANYORDER ), WAGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize, groupConcatValue, groupConcatInverse, 0), WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep, @@ -124324,6 +129947,43 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RegisterBuiltinFunctions(void){ #endif FUNCTION(coalesce, 1, 0, 0, 0 ), FUNCTION(coalesce, 0, 0, 0, 0 ), +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + MFUNCTION(ceil, 1, xCeil, ceilingFunc ), + MFUNCTION(ceiling, 1, xCeil, ceilingFunc ), + MFUNCTION(floor, 1, xFloor, ceilingFunc ), +#if SQLITE_HAVE_C99_MATH_FUNCS + MFUNCTION(trunc, 1, trunc, ceilingFunc ), +#endif + FUNCTION(ln, 1, 0, 0, logFunc ), + FUNCTION(log, 1, 1, 0, logFunc ), + FUNCTION(log10, 1, 1, 0, logFunc ), + FUNCTION(log2, 1, 2, 0, logFunc ), + FUNCTION(log, 2, 0, 0, logFunc ), + MFUNCTION(exp, 1, exp, math1Func ), + MFUNCTION(pow, 2, pow, math2Func ), + MFUNCTION(power, 2, pow, math2Func ), + MFUNCTION(mod, 2, fmod, math2Func ), + MFUNCTION(acos, 1, acos, math1Func ), + MFUNCTION(asin, 1, asin, math1Func ), + MFUNCTION(atan, 1, atan, math1Func ), + MFUNCTION(atan2, 2, atan2, math2Func ), + MFUNCTION(cos, 1, cos, math1Func ), + MFUNCTION(sin, 1, sin, math1Func ), + MFUNCTION(tan, 1, tan, math1Func ), + MFUNCTION(cosh, 1, cosh, math1Func ), + MFUNCTION(sinh, 1, sinh, math1Func ), + MFUNCTION(tanh, 1, tanh, math1Func ), +#if SQLITE_HAVE_C99_MATH_FUNCS + MFUNCTION(acosh, 1, acosh, math1Func ), + MFUNCTION(asinh, 1, asinh, math1Func ), + MFUNCTION(atanh, 1, atanh, math1Func ), +#endif + MFUNCTION(sqrt, 1, sqrt, math1Func ), + MFUNCTION(radians, 1, degToRad, math1Func ), + MFUNCTION(degrees, 1, radToDeg, math1Func ), + FUNCTION(pi, 0, 0, 0, piFunc ), +#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */ + FUNCTION(sign, 1, 0, 0, signFunc ), INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ), INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ), }; @@ -124332,6 +129992,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RegisterBuiltinFunctions(void){ #endif sqlcipher_sqlite3WindowFunctions(); sqlcipher_sqlite3RegisterDateTimeFunctions(); + sqlcipher_sqlite3RegisterJsonFunctions(); sqlcipher_sqlite3InsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc)); #if 0 /* Enable to print out how the built-in functions are hashed */ @@ -124343,6 +130004,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RegisterBuiltinFunctions(void){ for(p=sqlcipher_sqlite3BuiltinFunctions.a[i]; p; p=p->u.pHash){ int n = sqlcipher_sqlite3Strlen30(p->zName); int h = p->zName[0] + n; + assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); printf(" %s(%d)", p->zName, h); } printf("\n"); @@ -124570,7 +130232,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FkLocateIndex( */ if( pParent->iPKey>=0 ){ if( !zKey ) return 0; - if( !sqlcipher_sqlite3StrICmp(pParent->aCol[pParent->iPKey].zName, zKey) ) return 0; + if( !sqlcipher_sqlite3StrICmp(pParent->aCol[pParent->iPKey].zCnName, zKey) ){ + return 0; + } } }else if( paiCol ){ assert( nCol>1 ); @@ -124612,11 +130276,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FkLocateIndex( /* If the index uses a collation sequence that is different from ** the default collation sequence for the column, this index is ** unusable. Bail out early in this case. */ - zDfltColl = pParent->aCol[iCol].zColl; + zDfltColl = sqlcipher_sqlite3ColumnColl(&pParent->aCol[iCol]); if( !zDfltColl ) zDfltColl = sqlcipher_sqlite3StrBINARY; if( sqlcipher_sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break; - zIdxCol = pParent->aCol[iCol].zName; + zIdxCol = pParent->aCol[iCol].zCnName; for(j=0; jaCol[j].zCol, zIdxCol)==0 ){ if( aiCol ) aiCol[i] = pFKey->aCol[j].iFrom; @@ -124743,7 +130407,6 @@ static void fkLookupParent( }else{ int nCol = pFKey->nCol; int regTemp = sqlcipher_sqlite3GetTempRange(pParse, nCol); - int regRec = sqlcipher_sqlite3GetTempReg(pParse); sqlcipher_sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); sqlcipher_sqlite3VdbeSetP4KeyInfo(pParse, pIdx); @@ -124783,11 +130446,10 @@ static void fkLookupParent( sqlcipher_sqlite3VdbeGoto(v, iOk); } - sqlcipher_sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec, + sqlcipher_sqlite3VdbeAddOp4(v, OP_Affinity, regTemp, nCol, 0, sqlcipher_sqlite3IndexAffinityStr(pParse->db,pIdx), nCol); - sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v); - - sqlcipher_sqlite3ReleaseTempReg(pParse, regRec); + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regTemp, nCol); + VdbeCoverage(v); sqlcipher_sqlite3ReleaseTempRange(pParse, regTemp, nCol); } } @@ -124840,7 +130502,7 @@ static Expr *exprTableRegister( pCol = &pTab->aCol[iCol]; pExpr->iTable = regBase + sqlcipher_sqlite3TableColumnToStorage(pTab,iCol) + 1; pExpr->affExpr = pCol->affinity; - zColl = pCol->zColl; + zColl = sqlcipher_sqlite3ColumnColl(pCol); if( zColl==0 ) zColl = db->pDfltColl->zName; pExpr = sqlcipher_sqlite3ExprAddCollateString(pParse, pExpr, zColl); }else{ @@ -124863,6 +130525,7 @@ static Expr *exprTableColumn( ){ Expr *pExpr = sqlcipher_sqlite3Expr(db, TK_COLUMN, 0); if( pExpr ){ + assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pTab; pExpr->iTable = iCursor; pExpr->iColumn = iCol; @@ -124888,14 +130551,10 @@ static Expr *exprTableColumn( ** Operation | FK type | Action taken ** -------------------------------------------------------------------------- ** DELETE immediate Increment the "immediate constraint counter". -** Or, if the ON (UPDATE|DELETE) action is RESTRICT, -** throw a "FOREIGN KEY constraint failed" exception. ** ** INSERT immediate Decrement the "immediate constraint counter". ** ** DELETE deferred Increment the "deferred constraint counter". -** Or, if the ON (UPDATE|DELETE) action is RESTRICT, -** throw a "FOREIGN KEY constraint failed" exception. ** ** INSERT deferred Decrement the "deferred constraint counter". ** @@ -124949,7 +130608,7 @@ static void fkScanChildren( pLeft = exprTableRegister(pParse, pTab, regData, iCol); iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; assert( iCol>=0 ); - zCol = pFKey->pFrom->aCol[iCol].zName; + zCol = pFKey->pFrom->aCol[iCol].zCnName; pRight = sqlcipher_sqlite3Expr(db, TK_ID, zCol); pEq = sqlcipher_sqlite3PExpr(pParse, TK_EQ, pLeft, pRight); pWhere = sqlcipher_sqlite3ExprAnd(pParse, pWhere, pEq); @@ -124984,7 +130643,7 @@ static void fkScanChildren( i16 iCol = pIdx->aiColumn[i]; assert( iCol>=0 ); pLeft = exprTableRegister(pParse, pTab, regData, iCol); - pRight = sqlcipher_sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zName); + pRight = sqlcipher_sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zCnName); pEq = sqlcipher_sqlite3PExpr(pParse, TK_IS, pLeft, pRight); pAll = sqlcipher_sqlite3ExprAnd(pParse, pAll, pEq); } @@ -125003,7 +130662,7 @@ static void fkScanChildren( ** clause. For each row found, increment either the deferred or immediate ** foreign key constraint counter. */ if( pParse->nErr==0 ){ - pWInfo = sqlcipher_sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); + pWInfo = sqlcipher_sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0, 0); sqlcipher_sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); if( pWInfo ){ sqlcipher_sqlite3WhereEnd(pWInfo); @@ -125054,6 +130713,25 @@ static void fkTriggerDelete(sqlcipher_sqlite3 *dbMem, Trigger *p){ } } +/* +** Clear the apTrigger[] cache of CASCADE triggers for all foreign keys +** in a particular database. This needs to happen when the schema +** changes. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3FkClearTriggerCache(sqlcipher_sqlite3 *db, int iDb){ + HashElem *k; + Hash *pHash = &db->aDb[iDb].pSchema->tblHash; + for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k)){ + Table *pTab = sqliteHashData(k); + FKey *pFKey; + if( !IsOrdinaryTable(pTab) ) continue; + for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ + fkTriggerDelete(db, pFKey->apTrigger[0]); pFKey->apTrigger[0] = 0; + fkTriggerDelete(db, pFKey->apTrigger[1]); pFKey->apTrigger[1] = 0; + } + } +} + /* ** This function is called to generate code that runs when table pTab is ** being dropped from the database. The SrcList passed as the second argument @@ -125073,12 +130751,12 @@ static void fkTriggerDelete(sqlcipher_sqlite3 *dbMem, Trigger *p){ */ SQLITE_PRIVATE void sqlcipher_sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){ sqlcipher_sqlite3 *db = pParse->db; - if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) ){ + if( (db->flags&SQLITE_ForeignKeys) && IsOrdinaryTable(pTab) ){ int iSkip = 0; Vdbe *v = sqlcipher_sqlite3GetVdbe(pParse); assert( v ); /* VDBE has already been allocated */ - assert( pTab->pSelect==0 ); /* Not a view */ + assert( IsOrdinaryTable(pTab) ); if( sqlcipher_sqlite3FkReferences(pTab)==0 ){ /* Search for a deferred foreign key constraint for which this table ** is the child table. If one cannot be found, return without @@ -125086,7 +130764,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FkDropTable(Parse *pParse, SrcList *pName, ** the entire DELETE if there are no outstanding deferred constraints ** when this statement is run. */ FKey *p; - for(p=pTab->pFKey; p; p=p->pNextFrom){ + for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break; } if( !p ) return; @@ -125175,7 +130853,7 @@ static int fkParentIsModified( if( aChange[iKey]>=0 || (iKey==pTab->iPKey && bChngRowid) ){ Column *pCol = &pTab->aCol[iKey]; if( zKey ){ - if( 0==sqlcipher_sqlite3StrICmp(pCol->zName, zKey) ) return 1; + if( 0==sqlcipher_sqlite3StrICmp(pCol->zCnName, zKey) ) return 1; }else if( pCol->colFlags & COLFLAG_PRIMKEY ){ return 1; } @@ -125242,13 +130920,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FkCheck( /* If foreign-keys are disabled, this function is a no-op. */ if( (db->flags&SQLITE_ForeignKeys)==0 ) return; + if( !IsOrdinaryTable(pTab) ) return; iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); zDb = db->aDb[iDb].zDbSName; /* Loop through all the foreign key constraints for which pTab is the ** child table (the table that the foreign key definition is part of). */ - for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ Table *pTo; /* Parent table of foreign key pFKey */ Index *pIdx = 0; /* Index on key columns in pTo */ int *aiFree = 0; @@ -125315,7 +130994,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FkCheck( ** values read from the parent table are NULL. */ if( db->xAuth ){ int rcauth; - char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; + char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zCnName; rcauth = sqlcipher_sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); bIgnore = (rcauth==SQLITE_IGNORE); } @@ -125379,7 +131058,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FkCheck( ** child table as a SrcList for sqlcipher_sqlite3WhereBegin() */ pSrc = sqlcipher_sqlite3SrcListAppend(pParse, 0, 0, 0); if( pSrc ){ - struct SrcList_item *pItem = pSrc->a; + SrcItem *pItem = pSrc->a; pItem->pTab = pFKey->pFrom; pItem->zName = pFKey->pFrom->zName; pItem->pTab->nTabRef++; @@ -125430,10 +131109,10 @@ SQLITE_PRIVATE u32 sqlcipher_sqlite3FkOldmask( Table *pTab /* Table being modified */ ){ u32 mask = 0; - if( pParse->db->flags&SQLITE_ForeignKeys ){ + if( pParse->db->flags&SQLITE_ForeignKeys && IsOrdinaryTable(pTab) ){ FKey *p; int i; - for(p=pTab->pFKey; p; p=p->pNextFrom){ + for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ for(i=0; inCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom); } for(p=sqlcipher_sqlite3FkReferences(pTab); p; p=p->pNextTo){ @@ -125467,7 +131146,9 @@ SQLITE_PRIVATE u32 sqlcipher_sqlite3FkOldmask( ** ** For an UPDATE, this function returns 2 if: ** -** * There are any FKs for which pTab is the child and the parent table, or +** * There are any FKs for which pTab is the child and the parent table +** and any FK processing at all is required (even of a different FK), or +** ** * the UPDATE modifies one or more parent keys for which the action is ** not "NO ACTION" (i.e. is CASCADE, SET DEFAULT or SET NULL). ** @@ -125479,23 +131160,24 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FkRequired( int *aChange, /* Non-NULL for UPDATE operations */ int chngRowid /* True for UPDATE that affects rowid */ ){ - int eRet = 0; - if( pParse->db->flags&SQLITE_ForeignKeys ){ + int eRet = 1; /* Value to return if bHaveFK is true */ + int bHaveFK = 0; /* If FK processing is required */ + if( pParse->db->flags&SQLITE_ForeignKeys && IsOrdinaryTable(pTab) ){ if( !aChange ){ /* A DELETE operation. Foreign key processing is required if the ** table in question is either the child or parent table for any ** foreign key constraint. */ - eRet = (sqlcipher_sqlite3FkReferences(pTab) || pTab->pFKey); + bHaveFK = (sqlcipher_sqlite3FkReferences(pTab) || pTab->u.tab.pFKey); }else{ /* This is an UPDATE. Foreign key processing is only required if the ** operation modifies one or more child or parent key columns. */ FKey *p; /* Check if any child key columns are being modified. */ - for(p=pTab->pFKey; p; p=p->pNextFrom){ - if( 0==sqlcipher_sqlite3_stricmp(pTab->zName, p->zTo) ) return 2; + for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ if( fkChildIsModified(pTab, p, aChange, chngRowid) ){ - eRet = 1; + if( 0==sqlcipher_sqlite3_stricmp(pTab->zName, p->zTo) ) eRet = 2; + bHaveFK = 1; } } @@ -125503,12 +131185,12 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FkRequired( for(p=sqlcipher_sqlite3FkReferences(pTab); p; p=p->pNextTo){ if( fkParentIsModified(pTab, p, aChange, chngRowid) ){ if( p->aAction[1]!=OE_None ) return 2; - eRet = 1; + bHaveFK = 1; } } } } - return eRet; + return bHaveFK ? eRet : 0; } /* @@ -125520,9 +131202,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FkRequired( ** ** It returns a pointer to a Trigger structure containing a trigger ** equivalent to the ON UPDATE or ON DELETE action specified by pFKey. -** If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is -** returned (these actions require no special handling by the triggers -** sub-system, code for them is created by fkScanChildren()). +** If the action is "NO ACTION" then a NULL pointer is returned (these actions +** require no special handling by the triggers sub-system, code for them is +** created by fkScanChildren()). ** ** For example, if pFKey is the foreign key and pTab is table "p" in ** the following schema: @@ -125585,8 +131267,8 @@ static Trigger *fkActionTrigger( assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKeynCol) ); assert( pIdx==0 || pIdx->aiColumn[i]>=0 ); sqlcipher_sqlite3TokenInit(&tToCol, - pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName); - sqlcipher_sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zName); + pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zCnName); + sqlcipher_sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zCnName); /* Create the expression "OLD.zToCol = zFromCol". It is important ** that the "OLD.zToCol" term is on the LHS of the = operator, so @@ -125631,7 +131313,7 @@ static Trigger *fkActionTrigger( testcase( pCol->colFlags & COLFLAG_STORED ); pDflt = 0; }else{ - pDflt = pCol->pDflt; + pDflt = sqlcipher_sqlite3ColumnExpr(pFKey->pFrom, pCol); } if( pDflt ){ pNew = sqlcipher_sqlite3ExprDup(db, pDflt, 0); @@ -125651,18 +131333,23 @@ static Trigger *fkActionTrigger( nFrom = sqlcipher_sqlite3Strlen30(zFrom); if( action==OE_Restrict ){ + int iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); Token tFrom; + Token tDb; Expr *pRaise; tFrom.z = zFrom; tFrom.n = nFrom; + tDb.z = db->aDb[iDb].zDbSName; + tDb.n = sqlcipher_sqlite3Strlen30(tDb.z); + pRaise = sqlcipher_sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed"); if( pRaise ){ pRaise->affExpr = OE_Abort; } pSelect = sqlcipher_sqlite3SelectNew(pParse, sqlcipher_sqlite3ExprListAppend(pParse, 0, pRaise), - sqlcipher_sqlite3SrcListAppend(pParse, 0, &tFrom, 0), + sqlcipher_sqlite3SrcListAppend(pParse, 0, &tDb, &tFrom), pWhere, 0, 0, 0, 0, 0 ); @@ -125768,9 +131455,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FkDelete(sqlcipher_sqlite3 *db, Table *pTab FKey *pFKey; /* Iterator variable */ FKey *pNext; /* Copy of pFKey->pNextFrom */ - assert( db==0 || IsVirtual(pTab) - || sqlcipher_sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); - for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ + assert( IsOrdinaryTable(pTab) ); + for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pNext){ + assert( db==0 || sqlcipher_sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); /* Remove the FK from the fkeyHash hash table. */ if( !db || db->pnBytesFreed==0 ){ @@ -125850,7 +131537,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3OpenTable( }else{ Index *pPk = sqlcipher_sqlite3PrimaryKeyIndex(pTab); assert( pPk!=0 ); - assert( pPk->tnum==pTab->tnum ); + assert( pPk->tnum==pTab->tnum || CORRUPT_DB ); sqlcipher_sqlite3VdbeAddOp3(v, opcode, iCur, pPk->tnum, iDb); sqlcipher_sqlite3VdbeSetP4KeyInfo(pParse, pPk); VdbeComment((v, "%s", pTab->zName)); @@ -125917,28 +131604,68 @@ SQLITE_PRIVATE const char *sqlcipher_sqlite3IndexAffinityStr(sqlcipher_sqlite3 * } /* +** Make changes to the evolving bytecode to do affinity transformations +** of values that are about to be gathered into a row for table pTab. +** +** For ordinary (legacy, non-strict) tables: +** ----------------------------------------- +** ** Compute the affinity string for table pTab, if it has not already been ** computed. As an optimization, omit trailing SQLITE_AFF_BLOB affinities. ** -** If the affinity exists (if it is no entirely SQLITE_AFF_BLOB values) and -** if iReg>0 then code an OP_Affinity opcode that will set the affinities -** for register iReg and following. Or if affinities exists and iReg==0, +** If the affinity string is empty (because it was all SQLITE_AFF_BLOB entries +** which were then optimized out) then this routine becomes a no-op. +** +** Otherwise if iReg>0 then code an OP_Affinity opcode that will set the +** affinities for register iReg and following. Or if iReg==0, ** then just set the P4 operand of the previous opcode (which should be ** an OP_MakeRecord) to the affinity string. ** ** A column affinity string has one character per column: ** -** Character Column affinity -** ------------------------------ -** 'A' BLOB -** 'B' TEXT -** 'C' NUMERIC -** 'D' INTEGER -** 'E' REAL +** Character Column affinity +** --------- --------------- +** 'A' BLOB +** 'B' TEXT +** 'C' NUMERIC +** 'D' INTEGER +** 'E' REAL +** +** For STRICT tables: +** ------------------ +** +** Generate an appropropriate OP_TypeCheck opcode that will verify the +** datatypes against the column definitions in pTab. If iReg==0, that +** means an OP_MakeRecord opcode has already been generated and should be +** the last opcode generated. The new OP_TypeCheck needs to be inserted +** before the OP_MakeRecord. The new OP_TypeCheck should use the same +** register set as the OP_MakeRecord. If iReg>0 then register iReg is +** the first of a series of registers that will form the new record. +** Apply the type checking to that array of registers. */ SQLITE_PRIVATE void sqlcipher_sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){ int i, j; - char *zColAff = pTab->zColAff; + char *zColAff; + if( pTab->tabFlags & TF_Strict ){ + if( iReg==0 ){ + /* Move the previous opcode (which should be OP_MakeRecord) forward + ** by one slot and insert a new OP_TypeCheck where the current + ** OP_MakeRecord is found */ + VdbeOp *pPrev; + sqlcipher_sqlite3VdbeAppendP4(v, pTab, P4_TABLE); + pPrev = sqlcipher_sqlite3VdbeGetOp(v, -1); + assert( pPrev!=0 ); + assert( pPrev->opcode==OP_MakeRecord || sqlcipher_sqlite3VdbeDb(v)->mallocFailed ); + pPrev->opcode = OP_TypeCheck; + sqlcipher_sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, pPrev->p3); + }else{ + /* Insert an isolated OP_Typecheck */ + sqlcipher_sqlite3VdbeAddOp2(v, OP_TypeCheck, iReg, pTab->nNVCol); + sqlcipher_sqlite3VdbeAppendP4(v, pTab, P4_TABLE); + } + return; + } + zColAff = pTab->zColAff; if( zColAff==0 ){ sqlcipher_sqlite3 *db = sqlcipher_sqlite3VdbeDb(v); zColAff = (char *)sqlcipher_sqlite3DbMallocRaw(0, pTab->nCol+1); @@ -125948,7 +131675,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3TableAffinity(Vdbe *v, Table *pTab, int iRe } for(i=j=0; inCol; i++){ - assert( pTab->aCol[i].affinity!=0 ); + assert( pTab->aCol[i].affinity!=0 || sqlcipher_sqlite3VdbeParser(v)->nErr>0 ); if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ zColAff[j++] = pTab->aCol[i].affinity; } @@ -125964,6 +131691,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3TableAffinity(Vdbe *v, Table *pTab, int iRe if( iReg ){ sqlcipher_sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i); }else{ + assert( sqlcipher_sqlite3VdbeGetOp(v, -1)->opcode==OP_MakeRecord + || sqlcipher_sqlite3VdbeDb(v)->mallocFailed ); sqlcipher_sqlite3VdbeChangeP4(v, -1, zColAff, i); } } @@ -126047,24 +131776,30 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ComputeGeneratedColumns( ** that appropriate affinity has been applied to the regular columns */ sqlcipher_sqlite3TableAffinity(pParse->pVdbe, pTab, iRegStore); - if( (pTab->tabFlags & TF_HasStored)!=0 - && (pOp = sqlcipher_sqlite3VdbeGetOp(pParse->pVdbe,-1))->opcode==OP_Affinity - ){ - /* Change the OP_Affinity argument to '@' (NONE) for all stored - ** columns. '@' is the no-op affinity and those columns have not - ** yet been computed. */ - int ii, jj; - char *zP4 = pOp->p4.z; - assert( zP4!=0 ); - assert( pOp->p4type==P4_DYNAMIC ); - for(ii=jj=0; zP4[jj]; ii++){ - if( pTab->aCol[ii].colFlags & COLFLAG_VIRTUAL ){ - continue; - } - if( pTab->aCol[ii].colFlags & COLFLAG_STORED ){ - zP4[jj] = SQLITE_AFF_NONE; + if( (pTab->tabFlags & TF_HasStored)!=0 ){ + pOp = sqlcipher_sqlite3VdbeGetOp(pParse->pVdbe,-1); + if( pOp->opcode==OP_Affinity ){ + /* Change the OP_Affinity argument to '@' (NONE) for all stored + ** columns. '@' is the no-op affinity and those columns have not + ** yet been computed. */ + int ii, jj; + char *zP4 = pOp->p4.z; + assert( zP4!=0 ); + assert( pOp->p4type==P4_DYNAMIC ); + for(ii=jj=0; zP4[jj]; ii++){ + if( pTab->aCol[ii].colFlags & COLFLAG_VIRTUAL ){ + continue; + } + if( pTab->aCol[ii].colFlags & COLFLAG_STORED ){ + zP4[jj] = SQLITE_AFF_NONE; + } + jj++; } - jj++; + }else if( pOp->opcode==OP_TypeCheck ){ + /* If an OP_TypeCheck was generated because the table is STRICT, + ** then set the P3 operand to indicate that generated columns should + ** not be checked */ + pOp->p3 = 1; } } @@ -126100,7 +131835,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ComputeGeneratedColumns( int x; pCol->colFlags |= COLFLAG_BUSY; w.eCode = 0; - sqlcipher_sqlite3WalkExpr(&w, pCol->pDflt); + sqlcipher_sqlite3WalkExpr(&w, sqlcipher_sqlite3ColumnExpr(pTab, pCol)); pCol->colFlags &= ~COLFLAG_BUSY; if( w.eCode & COLFLAG_NOTAVAIL ){ pRedo = pCol; @@ -126109,13 +131844,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3ComputeGeneratedColumns( eProgress = 1; assert( pCol->colFlags & COLFLAG_GENERATED ); x = sqlcipher_sqlite3TableColumnToStorage(pTab, i) + iRegStore; - sqlcipher_sqlite3ExprCodeGeneratedColumn(pParse, pCol, x); + sqlcipher_sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, x); pCol->colFlags &= ~COLFLAG_NOTAVAIL; } } }while( pRedo && eProgress ); if( pRedo ){ - sqlcipher_sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pRedo->zName); + sqlcipher_sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pRedo->zCnName); } pParse->iSelfTab = 0; } @@ -126165,7 +131900,7 @@ static int autoIncBegin( ** Ticket d8dc2b3a58cd5dc2918a1d4acb 2018-05-23 */ if( pSeqTab==0 || !HasRowid(pSeqTab) - || IsVirtual(pSeqTab) + || NEVER(IsVirtual(pSeqTab)) || pSeqTab->nCol!=2 ){ pParse->nErr++; @@ -126177,7 +131912,9 @@ static int autoIncBegin( while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } if( pInfo==0 ){ pInfo = sqlcipher_sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo)); - if( pInfo==0 ) return 0; + sqlcipher_sqlite3ParserAddCleanup(pToplevel, sqlcipher_sqlite3DbFree, pInfo); + testcase( pParse->earlyCleanup ); + if( pParse->db->mallocFailed ) return 0; pInfo->pNext = pToplevel->pAinc; pToplevel->pAinc = pInfo; pInfo->pTab = pTab; @@ -126472,9 +132209,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( #endif db = pParse->db; - if( pParse->nErr || db->mallocFailed ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto insert_cleanup; } + assert( db->mallocFailed==0 ); dest.iSDParm = 0; /* Suppress a harmless compiler warning */ /* If the Select object is really just a simple VALUES() list with a @@ -126508,7 +132247,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( */ #ifndef SQLITE_OMIT_TRIGGER pTrigger = sqlcipher_sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask); - isView = pTab->pSelect!=0; + isView = IsView(pTab); #else # define pTrigger 0 # define tmask 0 @@ -126520,6 +132259,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( #endif assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x10000 ){ + sqlcipher_sqlite3TreeViewLine(0, "In sqlcipher_sqlite3Insert() at %s:%d", __FILE__, __LINE__); + sqlcipher_sqlite3TreeViewInsert(pParse->pWith, pTabList, pColumn, pSelect, pList, + onError, pUpsert, pTrigger); + } +#endif + /* If pTab is really a view, make sure it has been initialized. ** ViewGetColumnNames() is a no-op if pTab is not a view. */ @@ -126550,7 +132297,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( ** ** This is the 2nd template. */ - if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){ + if( pColumn==0 + && pSelect!=0 + && pTrigger==0 + && xferOptimization(pParse, pTab, pSelect, onError, iDb) + ){ assert( !pTrigger ); assert( pList==0 ); goto insert_end; @@ -126594,13 +132345,15 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( */ bIdListInOrder = (pTab->tabFlags & (TF_OOOHidden|TF_HasStored))==0; if( pColumn ){ + assert( pColumn->eU4!=EU4_EXPR ); + pColumn->eU4 = EU4_IDX; for(i=0; inId; i++){ - pColumn->a[i].idx = -1; + pColumn->a[i].u4.idx = -1; } for(i=0; inId; i++){ for(j=0; jnCol; j++){ - if( sqlcipher_sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ - pColumn->a[i].idx = j; + if( sqlcipher_sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zCnName)==0 ){ + pColumn->a[i].u4.idx = j; if( i!=j ) bIdListInOrder = 0; if( j==pTab->iPKey ){ ipkColumn = i; assert( !withoutRowid ); @@ -126609,7 +132362,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){ sqlcipher_sqlite3ErrorMsg(pParse, "cannot INSERT into generated column \"%s\"", - pTab->aCol[j].zName); + pTab->aCol[j].zCnName); goto insert_cleanup; } #endif @@ -126622,7 +132375,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( bIdListInOrder = 0; }else{ sqlcipher_sqlite3ErrorMsg(pParse, "table %S has no column named %s", - pTabList, 0, pColumn->a[i].zName); + pTabList->a, pColumn->a[i].zName); pParse->checkSchema = 1; goto insert_cleanup; } @@ -126650,7 +132403,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( dest.nSdst = pTab->nCol; rc = sqlcipher_sqlite3Select(pParse, pSelect, &dest); regFromSelect = dest.iSdst; - if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup; + assert( db->pParse==pParse ); + if( rc || pParse->nErr ) goto insert_cleanup; + assert( db->mallocFailed==0 ); sqlcipher_sqlite3VdbeEndCoroutine(v, regYield); sqlcipher_sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ assert( pSelect->pEList ); @@ -126735,19 +132490,24 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( } } #endif - } - /* Make sure the number of columns in the source data matches the number - ** of columns to be inserted into the table. - */ - for(i=0; inCol; i++){ - if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++; - } - if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){ - sqlcipher_sqlite3ErrorMsg(pParse, - "table %S has %d columns but %d values were supplied", - pTabList, 0, pTab->nCol-nHidden, nColumn); - goto insert_cleanup; + /* Make sure the number of columns in the source data matches the number + ** of columns to be inserted into the table. + */ + assert( TF_HasHidden==COLFLAG_HIDDEN ); + assert( TF_HasGenerated==COLFLAG_GENERATED ); + assert( COLFLAG_NOINSERT==(COLFLAG_GENERATED|COLFLAG_HIDDEN) ); + if( (pTab->tabFlags & (TF_HasGenerated|TF_HasHidden))!=0 ){ + for(i=0; inCol; i++){ + if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++; + } + } + if( nColumn!=(pTab->nCol-nHidden) ){ + sqlcipher_sqlite3ErrorMsg(pParse, + "table %S has %d columns but %d values were supplied", + pTabList->a, pTab->nCol-nHidden, nColumn); + goto insert_cleanup; + } } if( pColumn!=0 && nColumn!=pColumn->nId ){ sqlcipher_sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId); @@ -126759,6 +132519,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( if( (db->flags & SQLITE_CountRows)!=0 && !pParse->nested && !pParse->pTriggerTab + && !pParse->bReturning ){ regRowCount = ++pParse->nMem; sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); @@ -126782,12 +132543,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( } #ifndef SQLITE_OMIT_UPSERT if( pUpsert ){ + Upsert *pNx; if( IsVirtual(pTab) ){ sqlcipher_sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"", pTab->zName); goto insert_cleanup; } - if( pTab->pSelect ){ + if( IsView(pTab) ){ sqlcipher_sqlite3ErrorMsg(pParse, "cannot UPSERT a view"); goto insert_cleanup; } @@ -126795,13 +132557,19 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( goto insert_cleanup; } pTabList->a[0].iCursor = iDataCur; - pUpsert->pUpsertSrc = pTabList; - pUpsert->regData = regData; - pUpsert->iDataCur = iDataCur; - pUpsert->iIdxCur = iIdxCur; - if( pUpsert->pUpsertTarget ){ - sqlcipher_sqlite3UpsertAnalyzeTarget(pParse, pTabList, pUpsert); - } + pNx = pUpsert; + do{ + pNx->pUpsertSrc = pTabList; + pNx->regData = regData; + pNx->iDataCur = iDataCur; + pNx->iIdxCur = iIdxCur; + if( pNx->pUpsertTarget ){ + if( sqlcipher_sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx) ){ + goto insert_cleanup; + } + } + pNx = pNx->pNextUpsert; + }while( pNx!=0 ); } #endif @@ -126880,22 +132648,29 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( }else if( pColumn==0 ){ /* Hidden columns that are not explicitly named in the INSERT ** get there default value */ - sqlcipher_sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); + sqlcipher_sqlite3ExprCodeFactorable(pParse, + sqlcipher_sqlite3ColumnExpr(pTab, &pTab->aCol[i]), + iRegStore); continue; } } if( pColumn ){ - for(j=0; jnId && pColumn->a[j].idx!=i; j++){} + assert( pColumn->eU4==EU4_IDX ); + for(j=0; jnId && pColumn->a[j].u4.idx!=i; j++){} if( j>=pColumn->nId ){ /* A column not named in the insert column list gets its ** default value */ - sqlcipher_sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); + sqlcipher_sqlite3ExprCodeFactorable(pParse, + sqlcipher_sqlite3ColumnExpr(pTab, &pTab->aCol[i]), + iRegStore); continue; } k = j; }else if( nColumn==0 ){ /* This is INSERT INTO ... DEFAULT VALUES. Load the default value. */ - sqlcipher_sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); + sqlcipher_sqlite3ExprCodeFactorable(pParse, + sqlcipher_sqlite3ColumnExpr(pTab, &pTab->aCol[i]), + iRegStore); continue; }else{ k = i - nHidden; @@ -126942,11 +132717,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( sqlcipher_sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v); } - /* Cannot have triggers on a virtual table. If it were possible, - ** this block would have to account for hidden column. - */ - assert( !IsVirtual(pTab) ); - /* Copy the new data already generated. */ assert( pTab->nNVCol>0 ); sqlcipher_sqlite3VdbeAddOp3(v, OP_Copy, regRowid+1, regCols+1, pTab->nNVCol-1); @@ -127045,7 +132815,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( }else #endif { - int isReplace; /* Set to true if constraints may cause a replace */ + int isReplace = 0;/* Set to true if constraints may cause a replace */ int bUseSeek; /* True to use OPFLAG_SEEKRESULT */ sqlcipher_sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0, pUpsert @@ -127065,6 +132835,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( regIns, aRegIdx, 0, appendFlag, bUseSeek ); } +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + }else if( pParse->bReturning ){ + /* If there is a RETURNING clause, populate the rowid register with + ** constant value -1, in case one or more of the returned expressions + ** refer to the "rowid" of the view. */ + sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, -1, regRowid); +#endif } /* Update the count of rows that are inserted @@ -127101,7 +132878,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Insert( sqlcipher_sqlite3VdbeJumpHere(v, addrInsTop); } +#ifndef SQLITE_OMIT_XFER_OPT insert_end: +#endif /* SQLITE_OMIT_XFER_OPT */ /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. @@ -127116,9 +132895,7 @@ insert_end: ** invoke the callback function. */ if( regRowCount ){ - sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); - sqlcipher_sqlite3VdbeSetNumCols(v, 1); - sqlcipher_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC); + sqlcipher_sqlite3CodeChangeCount(v, regRowCount, "rows inserted"); } insert_cleanup: @@ -127206,6 +132983,70 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExprReferencesUpdatedColumn( return w.eCode!=0; } +/* +** The sqlcipher_sqlite3GenerateConstraintChecks() routine usually wants to visit +** the indexes of a table in the order provided in the Table->pIndex list. +** However, sometimes (rarely - when there is an upsert) it wants to visit +** the indexes in a different order. The following data structures accomplish +** this. +** +** The IndexIterator object is used to walk through all of the indexes +** of a table in either Index.pNext order, or in some other order established +** by an array of IndexListTerm objects. +*/ +typedef struct IndexListTerm IndexListTerm; +typedef struct IndexIterator IndexIterator; +struct IndexIterator { + int eType; /* 0 for Index.pNext list. 1 for an array of IndexListTerm */ + int i; /* Index of the current item from the list */ + union { + struct { /* Use this object for eType==0: A Index.pNext list */ + Index *pIdx; /* The current Index */ + } lx; + struct { /* Use this object for eType==1; Array of IndexListTerm */ + int nIdx; /* Size of the array */ + IndexListTerm *aIdx; /* Array of IndexListTerms */ + } ax; + } u; +}; + +/* When IndexIterator.eType==1, then each index is an array of instances +** of the following object +*/ +struct IndexListTerm { + Index *p; /* The index */ + int ix; /* Which entry in the original Table.pIndex list is this index*/ +}; + +/* Return the first index on the list */ +static Index *indexIteratorFirst(IndexIterator *pIter, int *pIx){ + assert( pIter->i==0 ); + if( pIter->eType ){ + *pIx = pIter->u.ax.aIdx[0].ix; + return pIter->u.ax.aIdx[0].p; + }else{ + *pIx = 0; + return pIter->u.lx.pIdx; + } +} + +/* Return the next index from the list. Return NULL when out of indexes */ +static Index *indexIteratorNext(IndexIterator *pIter, int *pIx){ + if( pIter->eType ){ + int i = ++pIter->i; + if( i>=pIter->u.ax.nIdx ){ + *pIx = i; + return 0; + } + *pIx = pIter->u.ax.aIdx[i].ix; + return pIter->u.ax.aIdx[i].p; + }else{ + ++(*pIx); + pIter->u.lx.pIdx = pIter->u.lx.pIdx->pNext; + return pIter->u.lx.pIdx; + } +} + /* ** Generate code to do constraint checks prior to an INSERT or an UPDATE ** on table pTab. @@ -127314,7 +133155,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( ){ Vdbe *v; /* VDBE under constrution */ Index *pIdx; /* Pointer to one of the indices */ - Index *pPk = 0; /* The PRIMARY KEY index */ + Index *pPk = 0; /* The PRIMARY KEY index for WITHOUT ROWID tables */ sqlcipher_sqlite3 *db; /* Database connection */ int i; /* loop counter */ int ix; /* Index loop counter */ @@ -127322,11 +133163,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( int onError; /* Conflict resolution strategy */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */ - Index *pUpIdx = 0; /* Index to which to apply the upsert */ - u8 isUpdate; /* True if this is an UPDATE operation */ + Upsert *pUpsertClause = 0; /* The specific ON CONFLICT clause for pIdx */ + u8 isUpdate; /* True if this is an UPDATE operation */ u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */ - int upsertBypass = 0; /* Address of Goto to bypass upsert subroutine */ - int upsertJump = 0; /* Address of Goto that jumps into upsert subroutine */ + int upsertIpkReturn = 0; /* Address of Goto at end of IPK uniqueness check */ + int upsertIpkDelay = 0; /* Address of Goto to bypass initial IPK check */ int ipkTop = 0; /* Top of the IPK uniqueness check */ int ipkBottom = 0; /* OP_Goto at the end of the IPK uniqueness check */ /* Variables associated with retesting uniqueness constraints after @@ -127336,12 +133177,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */ Trigger *pTrigger; /* List of DELETE triggers on the table pTab */ int nReplaceTrig = 0; /* Number of replace triggers coded */ + IndexIterator sIdxIter; /* Index iterator */ isUpdate = regOldData!=0; db = pParse->db; v = pParse->pVdbe; assert( v!=0 ); - assert( pTab->pSelect==0 ); /* This table is not a VIEW */ + assert( !IsView(pTab) ); /* This table is not a VIEW */ nCol = pTab->nCol; /* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for @@ -127392,7 +133234,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( } if( onError==OE_Replace ){ if( b2ndPass /* REPLACE becomes ABORT on the 2nd pass */ - || pCol->pDflt==0 /* REPLACE is ABORT if no DEFAULT value */ + || pCol->iDflt==0 /* REPLACE is ABORT if no DEFAULT value */ ){ testcase( pCol->colFlags & COLFLAG_VIRTUAL ); testcase( pCol->colFlags & COLFLAG_STORED ); @@ -127414,7 +133256,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( VdbeCoverage(v); assert( (pCol->colFlags & COLFLAG_GENERATED)==0 ); nSeenReplace++; - sqlcipher_sqlite3ExprCodeCopy(pParse, pCol->pDflt, iReg); + sqlcipher_sqlite3ExprCodeCopy(pParse, + sqlcipher_sqlite3ColumnExpr(pTab, pCol), iReg); sqlcipher_sqlite3VdbeJumpHere(v, addr1); break; } @@ -127424,7 +133267,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( case OE_Rollback: case OE_Fail: { char *zMsg = sqlcipher_sqlite3MPrintf(db, "%s.%s", pTab->zName, - pCol->zName); + pCol->zCnName); sqlcipher_sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError, iReg); sqlcipher_sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC); @@ -127533,19 +133376,63 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( ** list of indexes attached to a table puts all OE_Replace indexes last ** in the list. See sqlcipher_sqlite3CreateIndex() for where that happens. */ - + sIdxIter.eType = 0; + sIdxIter.i = 0; + sIdxIter.u.ax.aIdx = 0; /* Silence harmless compiler warning */ + sIdxIter.u.lx.pIdx = pTab->pIndex; if( pUpsert ){ if( pUpsert->pUpsertTarget==0 ){ - /* An ON CONFLICT DO NOTHING clause, without a constraint-target. - ** Make all unique constraint resolution be OE_Ignore */ - assert( pUpsert->pUpsertSet==0 ); - overrideError = OE_Ignore; - pUpsert = 0; - }else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){ - /* If the constraint-target uniqueness check must be run first. - ** Jump to that uniqueness check now */ - upsertJump = sqlcipher_sqlite3VdbeAddOp0(v, OP_Goto); - VdbeComment((v, "UPSERT constraint goes first")); + /* There is just on ON CONFLICT clause and it has no constraint-target */ + assert( pUpsert->pNextUpsert==0 ); + if( pUpsert->isDoUpdate==0 ){ + /* A single ON CONFLICT DO NOTHING clause, without a constraint-target. + ** Make all unique constraint resolution be OE_Ignore */ + overrideError = OE_Ignore; + pUpsert = 0; + }else{ + /* A single ON CONFLICT DO UPDATE. Make all resolutions OE_Update */ + overrideError = OE_Update; + } + }else if( pTab->pIndex!=0 ){ + /* Otherwise, we'll need to run the IndexListTerm array version of the + ** iterator to ensure that all of the ON CONFLICT conditions are + ** checked first and in order. */ + int nIdx, jj; + u64 nByte; + Upsert *pTerm; + u8 *bUsed; + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ + assert( aRegIdx[nIdx]>0 ); + } + sIdxIter.eType = 1; + sIdxIter.u.ax.nIdx = nIdx; + nByte = (sizeof(IndexListTerm)+1)*nIdx + nIdx; + sIdxIter.u.ax.aIdx = sqlcipher_sqlite3DbMallocZero(db, nByte); + if( sIdxIter.u.ax.aIdx==0 ) return; /* OOM */ + bUsed = (u8*)&sIdxIter.u.ax.aIdx[nIdx]; + pUpsert->pToFree = sIdxIter.u.ax.aIdx; + for(i=0, pTerm=pUpsert; pTerm; pTerm=pTerm->pNextUpsert){ + if( pTerm->pUpsertTarget==0 ) break; + if( pTerm->pUpsertIdx==0 ) continue; /* Skip ON CONFLICT for the IPK */ + jj = 0; + pIdx = pTab->pIndex; + while( ALWAYS(pIdx!=0) && pIdx!=pTerm->pUpsertIdx ){ + pIdx = pIdx->pNext; + jj++; + } + if( bUsed[jj] ) continue; /* Duplicate ON CONFLICT clause ignored */ + bUsed[jj] = 1; + sIdxIter.u.ax.aIdx[i].p = pIdx; + sIdxIter.u.ax.aIdx[i].ix = jj; + i++; + } + for(jj=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, jj++){ + if( bUsed[jj] ) continue; + sIdxIter.u.ax.aIdx[i].p = pIdx; + sIdxIter.u.ax.aIdx[i].ix = jj; + i++; + } + assert( i==nIdx ); } } @@ -127608,11 +133495,20 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( } /* figure out whether or not upsert applies in this case */ - if( pUpsert && pUpsert->pUpsertIdx==0 ){ - if( pUpsert->pUpsertSet==0 ){ - onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ - }else{ - onError = OE_Update; /* DO UPDATE */ + if( pUpsert ){ + pUpsertClause = sqlcipher_sqlite3UpsertOfIndex(pUpsert,0); + if( pUpsertClause!=0 ){ + if( pUpsertClause->isDoUpdate==0 ){ + onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ + }else{ + onError = OE_Update; /* DO UPDATE */ + } + } + if( pUpsertClause!=pUpsert ){ + /* The first ON CONFLICT clause has a conflict target other than + ** the IPK. We have to jump ahead to that first ON CONFLICT clause + ** and then come back here and deal with the IPK afterwards */ + upsertIpkDelay = sqlcipher_sqlite3VdbeAddOp0(v, OP_Goto); } } @@ -127622,8 +133518,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( ** the UNIQUE constraints have run. */ if( onError==OE_Replace /* IPK rule is REPLACE */ - && onError!=overrideError /* Rules for other contraints are different */ + && onError!=overrideError /* Rules for other constraints are different */ && pTab->pIndex /* There exist other constraints */ + && !upsertIpkDelay /* IPK check already deferred by UPSERT */ ){ ipkTop = sqlcipher_sqlite3VdbeAddOp0(v, OP_Goto)+1; VdbeComment((v, "defer IPK REPLACE until last")); @@ -127719,7 +133616,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( } } sqlcipher_sqlite3VdbeResolveLabel(v, addrRowidOk); - if( ipkTop ){ + if( pUpsert && pUpsertClause!=pUpsert ){ + upsertIpkReturn = sqlcipher_sqlite3VdbeAddOp0(v, OP_Goto); + }else if( ipkTop ){ ipkBottom = sqlcipher_sqlite3VdbeAddOp0(v, OP_Goto); sqlcipher_sqlite3VdbeJumpHere(v, ipkTop-1); } @@ -127732,7 +133631,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( ** This loop also handles the case of the PRIMARY KEY index for a ** WITHOUT ROWID table. */ - for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){ + for(pIdx = indexIteratorFirst(&sIdxIter, &ix); + pIdx; + pIdx = indexIteratorNext(&sIdxIter, &ix) + ){ int regIdx; /* Range of registers hold conent for pIdx */ int regR; /* Range of registers holding conflicting PK */ int iThisCur; /* Cursor for this UNIQUE index */ @@ -127740,15 +133642,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( int addrConflictCk; /* First opcode in the conflict check logic */ if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */ - if( pUpIdx==pIdx ){ - addrUniqueOk = upsertJump+1; - upsertBypass = sqlcipher_sqlite3VdbeGoto(v, 0); - VdbeComment((v, "Skip upsert subroutine")); - sqlcipher_sqlite3VdbeJumpHere(v, upsertJump); - }else{ - addrUniqueOk = sqlcipher_sqlite3VdbeMakeLabel(pParse); + if( pUpsert ){ + pUpsertClause = sqlcipher_sqlite3UpsertOfIndex(pUpsert, pIdx); + if( upsertIpkDelay && pUpsertClause==pUpsert ){ + sqlcipher_sqlite3VdbeJumpHere(v, upsertIpkDelay); + } } - if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){ + addrUniqueOk = sqlcipher_sqlite3VdbeMakeLabel(pParse); + if( bAffinityDone==0 ){ sqlcipher_sqlite3TableAffinity(v, pTab, regNewData+1); bAffinityDone = 1; } @@ -127785,7 +133686,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( testcase( sqlcipher_sqlite3TableColumnToStorage(pTab, iField)!=iField ); x = sqlcipher_sqlite3TableColumnToStorage(pTab, iField) + regNewData + 1; sqlcipher_sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i); - VdbeComment((v, "%s", pTab->aCol[iField].zName)); + VdbeComment((v, "%s", pTab->aCol[iField].zCnName)); } } sqlcipher_sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]); @@ -127819,8 +133720,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( } /* Figure out if the upsert clause applies to this index */ - if( pUpIdx==pIdx ){ - if( pUpsert->pUpsertSet==0 ){ + if( pUpsertClause ){ + if( pUpsertClause->isDoUpdate==0 ){ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ }else{ onError = OE_Update; /* DO UPDATE */ @@ -127837,6 +133738,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( ** This is not possible for ENABLE_PREUPDATE_HOOK builds, as the row ** must be explicitly deleted in order to ensure any pre-update hook ** is invoked. */ + assert( IsOrdinaryTable(pTab) ); #ifndef SQLITE_ENABLE_PREUPDATE_HOOK if( (ix==0 && pIdx->pNext==0) /* Condition 3 */ && pPk==pIdx /* Condition 2 */ @@ -127844,7 +133746,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( && ( 0==(db->flags&SQLITE_RecTriggers) || /* Condition 4 */ 0==sqlcipher_sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0)) && ( 0==(db->flags&SQLITE_ForeignKeys) || /* Condition 5 */ - (0==pTab->pFKey && 0==sqlcipher_sqlite3FkReferences(pTab))) + (0==pTab->u.tab.pFKey && 0==sqlcipher_sqlite3FkReferences(pTab))) ){ sqlcipher_sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; @@ -127858,7 +133760,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( regIdx, pIdx->nKeyCol); VdbeCoverage(v); /* Generate code to handle collisions */ - regR = (pIdx==pPk) ? regIdx : sqlcipher_sqlite3GetTempRange(pParse, nPkField); + regR = pIdx==pPk ? regIdx : sqlcipher_sqlite3GetTempRange(pParse, nPkField); if( isUpdate || onError==OE_Replace ){ if( HasRowid(pTab) ){ sqlcipher_sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); @@ -127879,13 +133781,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( x = sqlcipher_sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]); sqlcipher_sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); VdbeComment((v, "%s.%s", pTab->zName, - pTab->aCol[pPk->aiColumn[i]].zName)); + pTab->aCol[pPk->aiColumn[i]].zCnName)); } } if( isUpdate ){ /* If currently processing the PRIMARY KEY of a WITHOUT ROWID ** table, only conflict if the new PRIMARY KEY values are actually - ** different from the old. + ** different from the old. See TH3 withoutrowid04.test. ** ** For a UNIQUE index, only conflict if the PRIMARY KEY values ** of the matched index row are different from the original PRIMARY @@ -127943,7 +133845,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( assert( onError==OE_Replace ); nConflictCk = sqlcipher_sqlite3VdbeCurrentAddr(v) - addrConflictCk; - assert( nConflictCk>0 ); + assert( nConflictCk>0 || db->mallocFailed ); + testcase( nConflictCk<=0 ); testcase( nConflictCk>1 ); if( regTrigCnt ){ sqlcipher_sqlite3MultiWrite(pParse); @@ -128010,19 +133913,23 @@ SQLITE_PRIVATE void sqlcipher_sqlite3GenerateConstraintChecks( break; } } - if( pUpIdx==pIdx ){ - sqlcipher_sqlite3VdbeGoto(v, upsertJump+1); - sqlcipher_sqlite3VdbeJumpHere(v, upsertBypass); - }else{ - sqlcipher_sqlite3VdbeResolveLabel(v, addrUniqueOk); - } + sqlcipher_sqlite3VdbeResolveLabel(v, addrUniqueOk); if( regR!=regIdx ) sqlcipher_sqlite3ReleaseTempRange(pParse, regR, nPkField); + if( pUpsertClause + && upsertIpkReturn + && sqlcipher_sqlite3UpsertNextIsIPK(pUpsertClause) + ){ + sqlcipher_sqlite3VdbeGoto(v, upsertIpkDelay+1); + sqlcipher_sqlite3VdbeJumpHere(v, upsertIpkReturn); + upsertIpkReturn = 0; + } } /* If the IPK constraint is a REPLACE, run it last */ if( ipkTop ){ sqlcipher_sqlite3VdbeGoto(v, ipkTop); VdbeComment((v, "Do IPK REPLACE")); + assert( ipkBottom>0 ); sqlcipher_sqlite3VdbeJumpHere(v, ipkBottom); } @@ -128075,13 +133982,39 @@ SQLITE_PRIVATE void sqlcipher_sqlite3SetMakeRecordP5(Vdbe *v, Table *pTab){ if( pTab->pSchema->file_format<2 ) return; for(i=pTab->nCol-1; i>0; i--){ - if( pTab->aCol[i].pDflt!=0 ) break; + if( pTab->aCol[i].iDflt!=0 ) break; if( pTab->aCol[i].colFlags & COLFLAG_PRIMKEY ) break; } sqlcipher_sqlite3VdbeChangeP5(v, i+1); } #endif +/* +** Table pTab is a WITHOUT ROWID table that is being written to. The cursor +** number is iCur, and register regData contains the new record for the +** PK index. This function adds code to invoke the pre-update hook, +** if one is registered. +*/ +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +static void codeWithoutRowidPreupdate( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table being updated */ + int iCur, /* Cursor number for table */ + int regData /* Data containing new record */ +){ + Vdbe *v = pParse->pVdbe; + int r = sqlcipher_sqlite3GetTempReg(pParse); + assert( !HasRowid(pTab) ); + assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) || CORRUPT_DB ); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 0, r); + sqlcipher_sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE); + sqlcipher_sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); + sqlcipher_sqlite3ReleaseTempReg(pParse, r); +} +#else +# define codeWithoutRowidPreupdate(a,b,c,d) +#endif + /* ** This routine generates code to finish the INSERT or UPDATE operation ** that was started by a prior call to sqlcipher_sqlite3GenerateConstraintChecks. @@ -128114,7 +134047,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CompleteInsertion( v = pParse->pVdbe; assert( v!=0 ); - assert( pTab->pSelect==0 ); /* This table is not a VIEW */ + assert( !IsView(pTab) ); /* This table is not a VIEW */ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ /* All REPLACE indexes are at the end of the list */ assert( pIdx->onError!=OE_Replace @@ -128127,20 +134060,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CompleteInsertion( } pik_flags = (useSeekResult ? OPFLAG_USESEEKRESULT : 0); if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ - assert( pParse->nested==0 ); pik_flags |= OPFLAG_NCHANGE; pik_flags |= (update_flags & OPFLAG_SAVEPOSITION); -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK if( update_flags==0 ){ - int r = sqlcipher_sqlite3GetTempReg(pParse); - sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 0, r); - sqlcipher_sqlite3VdbeAddOp4(v, OP_Insert, - iIdxCur+i, aRegIdx[i], r, (char*)pTab, P4_TABLE - ); - sqlcipher_sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); - sqlcipher_sqlite3ReleaseTempReg(pParse, r); + codeWithoutRowidPreupdate(pParse, pTab, iIdxCur+i, aRegIdx[i]); } -#endif } sqlcipher_sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i], aRegIdx[i]+1, @@ -128208,8 +134132,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3OpenTableAndIndices( assert( op==OP_OpenWrite || p5==0 ); if( IsVirtual(pTab) ){ /* This routine is a no-op for virtual tables. Leave the output - ** variables *piDataCur and *piIdxCur uninitialized so that valgrind - ** can detect if they are used by mistake in the caller. */ + ** variables *piDataCur and *piIdxCur set to illegal cursor numbers + ** for improved error detection. */ + *piDataCur = *piIdxCur = -999; return 0; } iDb = sqlcipher_sqlite3SchemaToIndex(pParse->db, pTab->pSchema); @@ -128338,7 +134263,7 @@ static int xferOptimization( ExprList *pEList; /* The result set of the SELECT */ Table *pSrc; /* The table in the FROM clause of SELECT */ Index *pSrcIdx, *pDestIdx; /* Source and destination indices */ - struct SrcList_item *pItem; /* An element of pSelect->pSrc */ + SrcItem *pItem; /* An element of pSelect->pSrc */ int i; /* Loop counter */ int iDbSrc; /* The database of pSrc */ int iSrc, iDest; /* Cursors from source and destination */ @@ -128350,18 +134275,13 @@ static int xferOptimization( int destHasUniqueIdx = 0; /* True if pDest has a UNIQUE index */ int regData, regRowid; /* Registers holding data and rowid */ - if( pSelect==0 ){ - return 0; /* Must be of the form INSERT INTO ... SELECT ... */ - } + assert( pSelect!=0 ); if( pParse->pWith || pSelect->pWith ){ /* Do not attempt to process this query if there are an WITH clauses ** attached to it. Proceeding may generate a false "no such table: xxx" ** error if pSelect reads from a CTE named "xxx". */ return 0; } - if( sqlcipher_sqlite3TriggerList(pParse, pDest) ){ - return 0; /* tab1 must not have triggers */ - } #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pDest) ){ return 0; /* tab1 must not be a virtual table */ @@ -128424,13 +134344,8 @@ static int xferOptimization( if( HasRowid(pDest)!=HasRowid(pSrc) ){ return 0; /* source and destination must both be WITHOUT ROWID or not */ } -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pSrc) ){ - return 0; /* tab2 must not be a virtual table */ - } -#endif - if( pSrc->pSelect ){ - return 0; /* tab2 may not be a view */ + if( !IsOrdinaryTable(pSrc) ){ + return 0; /* tab2 may not be a view or virtual table */ } if( pDest->nCol!=pSrc->nCol ){ return 0; /* Number of columns must be the same in tab1 and tab2 */ @@ -128438,6 +134353,9 @@ static int xferOptimization( if( pDest->iPKey!=pSrc->iPKey ){ return 0; /* Both tables must have the same INTEGER PRIMARY KEY */ } + if( (pDest->tabFlags & TF_Strict)!=0 && (pSrc->tabFlags & TF_Strict)==0 ){ + return 0; /* Cannot feed from a non-strict into a strict table */ + } for(i=0; inCol; i++){ Column *pDestCol = &pDest->aCol[i]; Column *pSrcCol = &pSrc->aCol[i]; @@ -128474,7 +134392,9 @@ static int xferOptimization( ** This requirement could be relaxed for VIRTUAL columns, I suppose. */ if( (pDestCol->colFlags & COLFLAG_GENERATED)!=0 ){ - if( sqlcipher_sqlite3ExprCompare(0, pSrcCol->pDflt, pDestCol->pDflt, -1)!=0 ){ + if( sqlcipher_sqlite3ExprCompare(0, + sqlcipher_sqlite3ColumnExpr(pSrc, pSrcCol), + sqlcipher_sqlite3ColumnExpr(pDest, pDestCol), -1)!=0 ){ testcase( pDestCol->colFlags & COLFLAG_VIRTUAL ); testcase( pDestCol->colFlags & COLFLAG_STORED ); return 0; /* Different generator expressions */ @@ -128484,7 +134404,8 @@ static int xferOptimization( if( pDestCol->affinity!=pSrcCol->affinity ){ return 0; /* Affinity must be the same on all columns */ } - if( sqlcipher_sqlite3_stricmp(pDestCol->zColl, pSrcCol->zColl)!=0 ){ + if( sqlcipher_sqlite3_stricmp(sqlcipher_sqlite3ColumnColl(pDestCol), + sqlcipher_sqlite3ColumnColl(pSrcCol))!=0 ){ return 0; /* Collating sequence must be the same on all columns */ } if( pDestCol->notNull && !pSrcCol->notNull ){ @@ -128492,11 +134413,15 @@ static int xferOptimization( } /* Default values for second and subsequent columns need to match. */ if( (pDestCol->colFlags & COLFLAG_GENERATED)==0 && i>0 ){ - assert( pDestCol->pDflt==0 || pDestCol->pDflt->op==TK_SPAN ); - assert( pSrcCol->pDflt==0 || pSrcCol->pDflt->op==TK_SPAN ); - if( (pDestCol->pDflt==0)!=(pSrcCol->pDflt==0) - || (pDestCol->pDflt && strcmp(pDestCol->pDflt->u.zToken, - pSrcCol->pDflt->u.zToken)!=0) + Expr *pDestExpr = sqlcipher_sqlite3ColumnExpr(pDest, pDestCol); + Expr *pSrcExpr = sqlcipher_sqlite3ColumnExpr(pSrc, pSrcCol); + assert( pDestExpr==0 || pDestExpr->op==TK_SPAN ); + assert( pDestExpr==0 || !ExprHasProperty(pDestExpr, EP_IntValue) ); + assert( pSrcExpr==0 || pSrcExpr->op==TK_SPAN ); + assert( pSrcExpr==0 || !ExprHasProperty(pSrcExpr, EP_IntValue) ); + if( (pDestExpr==0)!=(pSrcExpr==0) + || (pDestExpr!=0 && strcmp(pDestExpr->u.zToken, + pSrcExpr->u.zToken)!=0) ){ return 0; /* Default values must be the same for all columns */ } @@ -128533,7 +134458,8 @@ static int xferOptimization( ** the extra complication to make this rule less restrictive is probably ** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e] */ - if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ + assert( IsOrdinaryTable(pDest) ); + if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->u.tab.pFKey!=0 ){ return 0; } #endif @@ -128555,6 +134481,7 @@ static int xferOptimization( iDest = pParse->nTab++; regAutoinc = autoIncBegin(pParse, iDbDest, pDest); regData = sqlcipher_sqlite3GetTempReg(pParse); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Null, 0, regData); regRowid = sqlcipher_sqlite3GetTempReg(pParse); sqlcipher_sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite); assert( HasRowid(pDest) || destHasUniqueIdx ); @@ -128590,11 +134517,13 @@ static int xferOptimization( emptySrcTest = sqlcipher_sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v); if( pDest->iPKey>=0 ){ addr1 = sqlcipher_sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); - sqlcipher_sqlite3VdbeVerifyAbortable(v, onError); - addr2 = sqlcipher_sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); - VdbeCoverage(v); - sqlcipher_sqlite3RowidConstraint(pParse, onError, pDest); - sqlcipher_sqlite3VdbeJumpHere(v, addr2); + if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){ + sqlcipher_sqlite3VdbeVerifyAbortable(v, onError); + addr2 = sqlcipher_sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); + VdbeCoverage(v); + sqlcipher_sqlite3RowidConstraint(pParse, onError, pDest); + sqlcipher_sqlite3VdbeJumpHere(v, addr2); + } autoIncStep(pParse, regAutoinc, regRowid); }else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){ addr1 = sqlcipher_sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); @@ -128602,16 +134531,28 @@ static int xferOptimization( addr1 = sqlcipher_sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); assert( (pDest->tabFlags & TF_Autoincrement)==0 ); } + if( db->mDbFlags & DBFLAG_Vacuum ){ sqlcipher_sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); - insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT; + insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT; }else{ - insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND; + insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND|OPFLAG_PREFORMAT; + } +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){ + sqlcipher_sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); + insFlags &= ~OPFLAG_PREFORMAT; + }else +#endif + { + sqlcipher_sqlite3VdbeAddOp3(v, OP_RowCell, iDest, iSrc, regRowid); + } + sqlcipher_sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid); + if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){ + sqlcipher_sqlite3VdbeChangeP4(v, -1, (char*)pDest, P4_TABLE); } - sqlcipher_sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); - sqlcipher_sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid, - (char*)pDest, P4_TABLE); sqlcipher_sqlite3VdbeChangeP5(v, insFlags); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v); sqlcipher_sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); sqlcipher_sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); @@ -128653,13 +134594,22 @@ static int xferOptimization( if( sqlcipher_sqlite3_stricmp(sqlcipher_sqlite3StrBINARY, zColl) ) break; } if( i==pSrcIdx->nColumn ){ - idxInsFlags = OPFLAG_USESEEKRESULT; + idxInsFlags = OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT; sqlcipher_sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); + sqlcipher_sqlite3VdbeAddOp2(v, OP_RowCell, iDest, iSrc); } }else if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){ idxInsFlags |= OPFLAG_NCHANGE; } - sqlcipher_sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); + if( idxInsFlags!=(OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT) ){ + sqlcipher_sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); + if( (db->mDbFlags & DBFLAG_Vacuum)==0 + && !HasRowid(pDest) + && IsPrimaryKeyIndex(pDestIdx) + ){ + codeWithoutRowidPreupdate(pParse, pDest, iDest, regData); + } + } sqlcipher_sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData); sqlcipher_sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND); sqlcipher_sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v); @@ -129187,6 +135137,26 @@ struct sqlcipher_sqlite3_api_routines { sqlcipher_sqlite3_file *(*database_file_object)(const char*); /* Version 3.34.0 and later */ int (*txn_state)(sqlcipher_sqlite3*,const char*); + /* Version 3.36.1 and later */ + sqlcipher_sqlite3_int64 (*changes64)(sqlcipher_sqlite3*); + sqlcipher_sqlite3_int64 (*total_changes64)(sqlcipher_sqlite3*); + /* Version 3.37.0 and later */ + int (*autovacuum_pages)(sqlcipher_sqlite3*, + unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), + void*, void(*)(void*)); + /* Version 3.38.0 and later */ + int (*error_offset)(sqlcipher_sqlite3*); + int (*vtab_rhs_value)(sqlcipher_sqlite3_index_info*,int,sqlcipher_sqlite3_value**); + int (*vtab_distinct)(sqlcipher_sqlite3_index_info*); + int (*vtab_in)(sqlcipher_sqlite3_index_info*,int,int); + int (*vtab_in_first)(sqlcipher_sqlite3_value*,sqlcipher_sqlite3_value**); + int (*vtab_in_next)(sqlcipher_sqlite3_value*,sqlcipher_sqlite3_value**); + /* Version 3.39.0 and later */ + int (*deserialize)(sqlcipher_sqlite3*,const char*,unsigned char*, + sqlcipher_sqlite3_int64,sqlcipher_sqlite3_int64,unsigned); + unsigned char *(*serialize)(sqlcipher_sqlite3*,const char *,sqlcipher_sqlite3_int64*, + unsigned int); + const char *(*db_name)(sqlcipher_sqlite3*,int); }; /* @@ -129493,6 +135463,24 @@ typedef int (*sqlcipher_sqlite3_loadext_entry)( #define sqlcipher_sqlite3_database_file_object sqlcipher_sqlite3_api->database_file_object /* Version 3.34.0 and later */ #define sqlcipher_sqlite3_txn_state sqlcipher_sqlite3_api->txn_state +/* Version 3.36.1 and later */ +#define sqlcipher_sqlite3_changes64 sqlcipher_sqlite3_api->changes64 +#define sqlcipher_sqlite3_total_changes64 sqlcipher_sqlite3_api->total_changes64 +/* Version 3.37.0 and later */ +#define sqlcipher_sqlite3_autovacuum_pages sqlcipher_sqlite3_api->autovacuum_pages +/* Version 3.38.0 and later */ +#define sqlcipher_sqlite3_error_offset sqlcipher_sqlite3_api->error_offset +#define sqlcipher_sqlite3_vtab_rhs_value sqlcipher_sqlite3_api->vtab_rhs_value +#define sqlcipher_sqlite3_vtab_distinct sqlcipher_sqlite3_api->vtab_distinct +#define sqlcipher_sqlite3_vtab_in sqlcipher_sqlite3_api->vtab_in +#define sqlcipher_sqlite3_vtab_in_first sqlcipher_sqlite3_api->vtab_in_first +#define sqlcipher_sqlite3_vtab_in_next sqlcipher_sqlite3_api->vtab_in_next +/* Version 3.39.0 and later */ +#ifndef SQLITE_OMIT_DESERIALIZE +#define sqlcipher_sqlite3_deserialize sqlcipher_sqlite3_api->deserialize +#define sqlcipher_sqlite3_serialize sqlcipher_sqlite3_api->serialize +#endif +#define sqlcipher_sqlite3_db_name sqlcipher_sqlite3_api->db_name #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -129977,6 +135965,35 @@ static const sqlcipher_sqlite3_api_routines sqlcipher_sqlite3Apis = { sqlcipher_sqlite3_database_file_object, /* Version 3.34.0 and later */ sqlcipher_sqlite3_txn_state, + /* Version 3.36.1 and later */ + sqlcipher_sqlite3_changes64, + sqlcipher_sqlite3_total_changes64, + /* Version 3.37.0 and later */ + sqlcipher_sqlite3_autovacuum_pages, + /* Version 3.38.0 and later */ + sqlcipher_sqlite3_error_offset, +#ifndef SQLITE_OMIT_VIRTUALTABLE + sqlcipher_sqlite3_vtab_rhs_value, + sqlcipher_sqlite3_vtab_distinct, + sqlcipher_sqlite3_vtab_in, + sqlcipher_sqlite3_vtab_in_first, + sqlcipher_sqlite3_vtab_in_next, +#else + 0, + 0, + 0, + 0, + 0, +#endif + /* Version 3.39.0 and later */ +#ifndef SQLITE_OMIT_DESERIALIZE + sqlcipher_sqlite3_deserialize, + sqlcipher_sqlite3_serialize, +#else + 0, + 0, +#endif + sqlcipher_sqlite3_db_name }; /* True if x is the directory separator character @@ -130012,7 +136029,7 @@ static int sqlcipher_sqlite3LoadExtension( const char *zEntry; char *zAltEntry = 0; void **aHandle; - u64 nMsg = 300 + sqlcipher_sqlite3Strlen30(zFile); + u64 nMsg = strlen(zFile); int ii; int rc; @@ -130046,6 +136063,12 @@ static int sqlcipher_sqlite3LoadExtension( zEntry = zProc ? zProc : "sqlcipher_sqlite3_extension_init"; + /* tag-20210611-1. Some dlopen() implementations will segfault if given + ** an oversize filename. Most filesystems have a pathname limit of 4K, + ** so limit the extension filename length to about twice that. + ** https://sqlite.org/forum/forumpost/08a0d6d9bf */ + if( nMsg>SQLITE_MAX_PATHLEN ) goto extension_not_found; + handle = sqlcipher_sqlite3OsDlOpen(pVfs, zFile); #if SQLITE_OS_UNIX || SQLITE_OS_WIN for(ii=0; iiaExtension[db->nExtension++] = handle; return SQLITE_OK; + +extension_not_found: + if( pzErrMsg ){ + nMsg += 300; + *pzErrMsg = zErrmsg = sqlcipher_sqlite3_malloc64(nMsg); + if( zErrmsg ){ + assert( nMsg<0x7fffffff ); /* zErrmsg would be NULL if not so */ + sqlcipher_sqlite3_snprintf((int)nMsg, zErrmsg, + "unable to open shared library [%.*s]", SQLITE_MAX_PATHLEN, zFile); + sqlcipher_sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); + } + } + return SQLITE_ERROR; } SQLITE_API int sqlcipher_sqlite3_load_extension( sqlcipher_sqlite3 *db, /* Load the extension into this database connection */ @@ -130430,13 +136457,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3AutoLoadExtensions(sqlcipher_sqlite3 *db){ #define PragTyp_SOFT_HEAP_LIMIT 35 #define PragTyp_SYNCHRONOUS 36 #define PragTyp_TABLE_INFO 37 -#define PragTyp_TEMP_STORE 38 -#define PragTyp_TEMP_STORE_DIRECTORY 39 -#define PragTyp_THREADS 40 -#define PragTyp_WAL_AUTOCHECKPOINT 41 -#define PragTyp_WAL_CHECKPOINT 42 -#define PragTyp_LOCK_STATUS 43 -#define PragTyp_STATS 44 +#define PragTyp_TABLE_LIST 38 +#define PragTyp_TEMP_STORE 39 +#define PragTyp_TEMP_STORE_DIRECTORY 40 +#define PragTyp_THREADS 41 +#define PragTyp_WAL_AUTOCHECKPOINT 42 +#define PragTyp_WAL_CHECKPOINT 43 +#define PragTyp_LOCK_STATUS 44 +#define PragTyp_STATS 45 /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ @@ -130469,45 +136497,51 @@ static const char *const pragCName[] = { /* 13 */ "pk", /* 14 */ "hidden", /* table_info reuses 8 */ - /* 15 */ "seqno", /* Used by: index_xinfo */ - /* 16 */ "cid", - /* 17 */ "name", - /* 18 */ "desc", - /* 19 */ "coll", - /* 20 */ "key", - /* 21 */ "name", /* Used by: function_list */ - /* 22 */ "builtin", - /* 23 */ "type", - /* 24 */ "enc", - /* 25 */ "narg", - /* 26 */ "flags", - /* 27 */ "tbl", /* Used by: stats */ - /* 28 */ "idx", - /* 29 */ "wdth", - /* 30 */ "hght", - /* 31 */ "flgs", - /* 32 */ "seq", /* Used by: index_list */ - /* 33 */ "name", - /* 34 */ "unique", - /* 35 */ "origin", - /* 36 */ "partial", - /* 37 */ "table", /* Used by: foreign_key_check */ - /* 38 */ "rowid", - /* 39 */ "parent", - /* 40 */ "fkid", - /* index_info reuses 15 */ - /* 41 */ "seq", /* Used by: database_list */ - /* 42 */ "name", - /* 43 */ "file", - /* 44 */ "busy", /* Used by: wal_checkpoint */ - /* 45 */ "log", - /* 46 */ "checkpointed", - /* collation_list reuses 32 */ - /* 47 */ "database", /* Used by: lock_status */ - /* 48 */ "status", - /* 49 */ "cache_size", /* Used by: default_cache_size */ + /* 15 */ "schema", /* Used by: table_list */ + /* 16 */ "name", + /* 17 */ "type", + /* 18 */ "ncol", + /* 19 */ "wr", + /* 20 */ "strict", + /* 21 */ "seqno", /* Used by: index_xinfo */ + /* 22 */ "cid", + /* 23 */ "name", + /* 24 */ "desc", + /* 25 */ "coll", + /* 26 */ "key", + /* 27 */ "name", /* Used by: function_list */ + /* 28 */ "builtin", + /* 29 */ "type", + /* 30 */ "enc", + /* 31 */ "narg", + /* 32 */ "flags", + /* 33 */ "tbl", /* Used by: stats */ + /* 34 */ "idx", + /* 35 */ "wdth", + /* 36 */ "hght", + /* 37 */ "flgs", + /* 38 */ "seq", /* Used by: index_list */ + /* 39 */ "name", + /* 40 */ "unique", + /* 41 */ "origin", + /* 42 */ "partial", + /* 43 */ "table", /* Used by: foreign_key_check */ + /* 44 */ "rowid", + /* 45 */ "parent", + /* 46 */ "fkid", + /* index_info reuses 21 */ + /* 47 */ "seq", /* Used by: database_list */ + /* 48 */ "name", + /* 49 */ "file", + /* 50 */ "busy", /* Used by: wal_checkpoint */ + /* 51 */ "log", + /* 52 */ "checkpointed", + /* collation_list reuses 38 */ + /* 53 */ "database", /* Used by: lock_status */ + /* 54 */ "status", + /* 55 */ "cache_size", /* Used by: default_cache_size */ /* module_list pragma_list reuses 9 */ - /* 50 */ "timeout", /* Used by: busy_timeout */ + /* 56 */ "timeout", /* Used by: busy_timeout */ }; /* Definitions of all built-in pragmas */ @@ -130558,7 +136592,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "busy_timeout", /* ePragTyp: */ PragTyp_BUSY_TIMEOUT, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 50, 1, + /* ColNames: */ 56, 1, /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "cache_size", @@ -130597,7 +136631,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "collation_list", /* ePragTyp: */ PragTyp_COLLATION_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 32, 2, + /* ColNames: */ 38, 2, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) @@ -130631,15 +136665,15 @@ static const PragmaName aPragmaName[] = { #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) {/* zName: */ "database_list", /* ePragTyp: */ PragTyp_DATABASE_LIST, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, - /* ColNames: */ 41, 3, + /* ePragFlg: */ PragFlg_Result0, + /* ColNames: */ 47, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) {/* zName: */ "default_cache_size", /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, - /* ColNames: */ 49, 1, + /* ColNames: */ 55, 1, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) @@ -130669,7 +136703,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "foreign_key_check", /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 37, 4, + /* ColNames: */ 43, 4, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FOREIGN_KEY) @@ -130712,7 +136746,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "function_list", /* ePragTyp: */ PragTyp_FUNCTION_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 21, 6, + /* ColNames: */ 27, 6, /* iArg: */ 0 }, #endif #endif @@ -130755,23 +136789,23 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "index_info", /* ePragTyp: */ PragTyp_INDEX_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 15, 3, + /* ColNames: */ 21, 3, /* iArg: */ 0 }, {/* zName: */ "index_list", /* ePragTyp: */ PragTyp_INDEX_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 32, 5, + /* ColNames: */ 38, 5, /* iArg: */ 0 }, {/* zName: */ "index_xinfo", /* ePragTyp: */ PragTyp_INDEX_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 15, 6, + /* ColNames: */ 21, 6, /* iArg: */ 1 }, #endif #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) {/* zName: */ "integrity_check", /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif @@ -130814,7 +136848,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "lock_status", /* ePragTyp: */ PragTyp_LOCK_STATUS, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 47, 2, + /* ColNames: */ 53, 2, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) @@ -130888,7 +136922,7 @@ static const PragmaName aPragmaName[] = { #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) {/* zName: */ "quick_check", /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif @@ -130964,7 +136998,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "stats", /* ePragTyp: */ PragTyp_STATS, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, - /* ColNames: */ 27, 5, + /* ColNames: */ 33, 5, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) @@ -130980,6 +137014,11 @@ static const PragmaName aPragmaName[] = { /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 8, 6, /* iArg: */ 0 }, + {/* zName: */ "table_list", + /* ePragTyp: */ PragTyp_TABLE_LIST, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1, + /* ColNames: */ 15, 6, + /* iArg: */ 0 }, {/* zName: */ "table_xinfo", /* ePragTyp: */ PragTyp_TABLE_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, @@ -131069,7 +137108,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "wal_checkpoint", /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, /* ePragFlg: */ PragFlg_NeedSchema, - /* ColNames: */ 44, 3, + /* ColNames: */ 50, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) @@ -131080,7 +137119,7 @@ static const PragmaName aPragmaName[] = { /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, #endif }; -/* Number of pragmas: 67 on by default, 77 total. */ +/* Number of pragmas: 68 on by default, 78 total. */ /************** End of pragma.h **********************************************/ /************** Continuing where we left off in pragma.c *********************/ @@ -131362,15 +137401,16 @@ static void pragmaFunclistLine( int isBuiltin, /* True if this is a built-in function */ int showInternFuncs /* True if showing internal functions */ ){ + u32 mask = + SQLITE_DETERMINISTIC | + SQLITE_DIRECTONLY | + SQLITE_SUBTYPE | + SQLITE_INNOCUOUS | + SQLITE_FUNC_INTERNAL + ; + if( showInternFuncs ) mask = 0xffffffff; for(; p; p=p->pNext){ const char *zType; - static const u32 mask = - SQLITE_DETERMINISTIC | - SQLITE_DIRECTONLY | - SQLITE_SUBTYPE | - SQLITE_INNOCUOUS | - SQLITE_FUNC_INTERNAL - ; static const char *azEnc[] = { 0, "utf8", "utf16le", "utf16be" }; assert( SQLITE_FUNC_ENCMASK==0x3 ); @@ -131537,7 +137577,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( /* Locate the pragma in the lookup table */ pPragma = pragmaLocate(zLeft); - if( pPragma==0 ) goto pragma_out; + if( pPragma==0 ){ + /* IMP: R-43042-22504 No error messages are generated if an + ** unknown pragma is issued. */ + goto pragma_out; + } /* Make sure the database schema is loaded if the pragma requires that */ if( (pPragma->mPragFlg & PragFlg_NeedSchema)!=0 ){ @@ -131873,7 +137917,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( */ #ifndef SQLITE_OMIT_AUTOVACUUM case PragTyp_INCREMENTAL_VACUUM: { - int iLimit, addr; + int iLimit = 0, addr; if( zRight==0 || !sqlcipher_sqlite3GetInt32(zRight, &iLimit) || iLimit<=0 ){ iLimit = 0x7fffffff; } @@ -132030,6 +138074,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( ** */ case PragTyp_TEMP_STORE_DIRECTORY: { + sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); if( !zRight ){ returnSingleText(v, sqlcipher_sqlite3_temp_directory); }else{ @@ -132039,6 +138084,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( rc = sqlcipher_sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); if( rc!=SQLITE_OK || res==0 ){ sqlcipher_sqlite3ErrorMsg(pParse, "not a writable directory"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); goto pragma_out; } } @@ -132056,6 +138102,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( } #endif /* SQLITE_OMIT_WSD */ } + sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); break; } @@ -132074,6 +138121,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( ** */ case PragTyp_DATA_STORE_DIRECTORY: { + sqlcipher_sqlite3_mutex_enter(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); if( !zRight ){ returnSingleText(v, sqlcipher_sqlite3_data_directory); }else{ @@ -132083,6 +138131,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( rc = sqlcipher_sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); if( rc!=SQLITE_OK || res==0 ){ sqlcipher_sqlite3ErrorMsg(pParse, "not a writable directory"); + sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); goto pragma_out; } } @@ -132094,6 +138143,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( } #endif /* SQLITE_OMIT_WSD */ } + sqlcipher_sqlite3_mutex_leave(sqlcipher_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); break; } #endif @@ -132187,6 +138237,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( }else{ db->flags &= ~mask; if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0; + if( (mask & SQLITE_WriteSchema)!=0 + && sqlcipher_sqlite3_stricmp(zRight, "reset")==0 + ){ + /* IMP: R-60817-01178 If the argument is "RESET" then schema + ** writing is disabled (as with "PRAGMA writable_schema=OFF") and, + ** in addition, the schema is reloaded. */ + sqlcipher_sqlite3ResetAllSchemasOfConnection(db); + } } /* Many of the flag-pragmas modify the code generated by the SQL @@ -132227,6 +138285,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( sqlcipher_sqlite3ViewGetColumnNames(pParse, pTab); for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ int isHidden = 0; + const Expr *pColExpr; if( pCol->colFlags & COLFLAG_NOINSERT ){ if( pPragma->iArg==0 ){ nHidden++; @@ -132247,13 +138306,16 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( }else{ for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){} } - assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN || isHidden>=2 ); + pColExpr = sqlcipher_sqlite3ColumnExpr(pTab,pCol); + assert( pColExpr==0 || pColExpr->op==TK_SPAN || isHidden>=2 ); + assert( pColExpr==0 || !ExprHasProperty(pColExpr, EP_IntValue) + || isHidden>=2 ); sqlcipher_sqlite3VdbeMultiLoad(v, 1, pPragma->iArg ? "issisii" : "issisi", i-nHidden, - pCol->zName, + pCol->zCnName, sqlcipher_sqlite3ColumnType(pCol,""), pCol->notNull ? 1 : 0, - pCol->pDflt && isHidden<2 ? pCol->pDflt->u.zToken : 0, + (isHidden>=2 || pColExpr==0) ? 0 : pColExpr->u.zToken, k, isHidden); } @@ -132261,6 +138323,85 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( } break; + /* + ** PRAGMA table_list + ** + ** Return a single row for each table, virtual table, or view in the + ** entire schema. + ** + ** schema: Name of attached database hold this table + ** name: Name of the table itself + ** type: "table", "view", "virtual", "shadow" + ** ncol: Number of columns + ** wr: True for a WITHOUT ROWID table + ** strict: True for a STRICT table + */ + case PragTyp_TABLE_LIST: { + int ii; + pParse->nMem = 6; + sqlcipher_sqlite3CodeVerifyNamedSchema(pParse, zDb); + for(ii=0; iinDb; ii++){ + HashElem *k; + Hash *pHash; + int initNCol; + if( zDb && sqlcipher_sqlite3_stricmp(zDb, db->aDb[ii].zDbSName)!=0 ) continue; + + /* Ensure that the Table.nCol field is initialized for all views + ** and virtual tables. Each time we initialize a Table.nCol value + ** for a table, that can potentially disrupt the hash table, so restart + ** the initialization scan. + */ + pHash = &db->aDb[ii].pSchema->tblHash; + initNCol = sqliteHashCount(pHash); + while( initNCol-- ){ + for(k=sqliteHashFirst(pHash); 1; k=sqliteHashNext(k) ){ + Table *pTab; + if( k==0 ){ initNCol = 0; break; } + pTab = sqliteHashData(k); + if( pTab->nCol==0 ){ + char *zSql = sqlcipher_sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName); + if( zSql ){ + sqlcipher_sqlite3_stmt *pDummy = 0; + (void)sqlcipher_sqlite3_prepare(db, zSql, -1, &pDummy, 0); + (void)sqlcipher_sqlite3_finalize(pDummy); + sqlcipher_sqlite3DbFree(db, zSql); + } + if( db->mallocFailed ){ + sqlcipher_sqlite3ErrorMsg(db->pParse, "out of memory"); + db->pParse->rc = SQLITE_NOMEM_BKPT; + } + pHash = &db->aDb[ii].pSchema->tblHash; + break; + } + } + } + + for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k) ){ + Table *pTab = sqliteHashData(k); + const char *zType; + if( zRight && sqlcipher_sqlite3_stricmp(zRight, pTab->zName)!=0 ) continue; + if( IsView(pTab) ){ + zType = "view"; + }else if( IsVirtual(pTab) ){ + zType = "virtual"; + }else if( pTab->tabFlags & TF_Shadow ){ + zType = "shadow"; + }else{ + zType = "table"; + } + sqlcipher_sqlite3VdbeMultiLoad(v, 1, "sssiii", + db->aDb[ii].zDbSName, + sqlcipher_sqlite3PreferredTableName(pTab->zName), + zType, + pTab->nCol, + (pTab->tabFlags & TF_WithoutRowid)!=0, + (pTab->tabFlags & TF_Strict)!=0 + ); + } + } + } + break; + #ifdef SQLITE_DEBUG case PragTyp_STATS: { Index *pIdx; @@ -132270,7 +138411,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); sqlcipher_sqlite3VdbeMultiLoad(v, 1, "ssiii", - pTab->zName, + sqlcipher_sqlite3PreferredTableName(pTab->zName), 0, pTab->szTabRow, pTab->nRowLogEst, @@ -132320,7 +138461,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( for(i=0; iaiColumn[i]; sqlcipher_sqlite3VdbeMultiLoad(v, 1, "iisX", i, cnum, - cnum<0 ? 0 : pTab->aCol[cnum].zName); + cnum<0 ? 0 : pTab->aCol[cnum].zCnName); if( pPragma->iArg ){ sqlcipher_sqlite3VdbeMultiLoad(v, 4, "isiX", pIdx->aSortOrder[i], @@ -132389,11 +138530,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( pParse->nMem = 6; for(i=0; iu.pHash ){ + assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); pragmaFunclistLine(v, p, 1, showInternFunc); } } for(j=sqliteHashFirst(&db->aFunc); j; j=sqliteHashNext(j)){ p = (FuncDef*)sqliteHashData(j); + assert( (p->funcFlags & SQLITE_FUNC_BUILTIN)==0 ); pragmaFunclistLine(v, p, 0, showInternFunc); } } @@ -132427,8 +138570,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( FKey *pFK; Table *pTab; pTab = sqlcipher_sqlite3FindTable(db, zRight, zDb); - if( pTab ){ - pFK = pTab->pFKey; + if( pTab && IsOrdinaryTable(pTab) ){ + pFK = pTab->u.tab.pFKey; if( pFK ){ int iTabDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); int i = 0; @@ -132441,7 +138584,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( i, j, pFK->zTo, - pTab->aCol[pFK->aCol[j].iFrom].zName, + pTab->aCol[pFK->aCol[j].iFrom].zCnName, pFK->aCol[j].zCol, actionName(pFK->aAction[1]), /* ON UPDATE */ actionName(pFK->aAction[0]), /* ON DELETE */ @@ -132468,7 +138611,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( HashElem *k; /* Loop counter: Next table in schema */ int x; /* result variable */ int regResult; /* 3 registers to hold a result row */ - int regKey; /* Register to hold key for checking the FK */ int regRow; /* Registers to hold a row from pTab */ int addrTop; /* Top of a loop checking foreign keys */ int addrOk; /* Jump here if the key is OK */ @@ -132476,7 +138618,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( regResult = pParse->nMem+1; pParse->nMem += 4; - regKey = ++pParse->nMem; regRow = ++pParse->nMem; k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); while( k ){ @@ -132487,7 +138628,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( pTab = (Table*)sqliteHashData(k); k = sqliteHashNext(k); } - if( pTab==0 || pTab->pFKey==0 ) continue; + if( pTab==0 || !IsOrdinaryTable(pTab) || pTab->u.tab.pFKey==0 ) continue; iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); zDb = db->aDb[iDb].zDbSName; sqlcipher_sqlite3CodeVerifySchema(pParse, iDb); @@ -132495,7 +138636,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; sqlcipher_sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); sqlcipher_sqlite3VdbeLoadString(v, regResult, pTab->zName); - for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ + assert( IsOrdinaryTable(pTab) ); + for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){ pParent = sqlcipher_sqlite3FindTable(db, pFK->zTo, zDb); if( pParent==0 ) continue; pIdx = 0; @@ -132517,7 +138659,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( if( pFK ) break; if( pParse->nTabnTab = i; addrTop = sqlcipher_sqlite3VdbeAddOp1(v, OP_Rewind, 0); VdbeCoverage(v); - for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ + assert( IsOrdinaryTable(pTab) ); + for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){ pParent = sqlcipher_sqlite3FindTable(db, pFK->zTo, zDb); pIdx = 0; aiCols = 0; @@ -132531,6 +138674,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( ** regRow..regRow+n. If any of the child key values are NULL, this ** row cannot cause an FK violation. Jump directly to addrOk in ** this case. */ + if( regRow+pFK->nCol>pParse->nMem ) pParse->nMem = regRow+pFK->nCol; for(j=0; jnCol; j++){ int iCol = aiCols ? aiCols[j] : pFK->aCol[j].iFrom; sqlcipher_sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, iCol, regRow+j); @@ -132540,9 +138684,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( /* Generate code to query the parent index for a matching parent ** key. If a match is found, jump to addrOk. */ if( pIdx ){ - sqlcipher_sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey, + sqlcipher_sqlite3VdbeAddOp4(v, OP_Affinity, regRow, pFK->nCol, 0, sqlcipher_sqlite3IndexAffinityStr(db,pIdx), pFK->nCol); - sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regRow, pFK->nCol); VdbeCoverage(v); }else if( pParent ){ int jmp = sqlcipher_sqlite3VdbeCurrentAddr(v)+2; @@ -132717,8 +138861,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( int loopTop; int iDataCur, iIdxCur; int r1 = -1; + int bStrict; - if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ + if( !IsOrdinaryTable(pTab) ) continue; if( pObjTab && pObjTab!=pTab ) continue; pPk = HasRowid(pTab) ? 0 : sqlcipher_sqlite3PrimaryKeyIndex(pTab); sqlcipher_sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, @@ -132738,23 +138883,48 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( /* Sanity check on record header decoding */ sqlcipher_sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nNVCol-1,3); sqlcipher_sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); + VdbeComment((v, "(right-most column)")); } - /* Verify that all NOT NULL columns really are NOT NULL */ + /* Verify that all NOT NULL columns really are NOT NULL. At the + ** same time verify the type of the content of STRICT tables */ + bStrict = (pTab->tabFlags & TF_Strict)!=0; for(j=0; jnCol; j++){ char *zErr; - int jmp2; + Column *pCol = pTab->aCol + j; + int doError, jmp2; if( j==pTab->iPKey ) continue; - if( pTab->aCol[j].notNull==0 ) continue; + if( pCol->notNull==0 && !bStrict ) continue; + doError = bStrict ? sqlcipher_sqlite3VdbeMakeLabel(pParse) : 0; sqlcipher_sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); if( sqlcipher_sqlite3VdbeGetOp(v,-1)->opcode==OP_Column ){ sqlcipher_sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); } - jmp2 = sqlcipher_sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v); - zErr = sqlcipher_sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, - pTab->aCol[j].zName); - sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); - integrityCheckResultRow(v); - sqlcipher_sqlite3VdbeJumpHere(v, jmp2); + if( pCol->notNull ){ + jmp2 = sqlcipher_sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v); + zErr = sqlcipher_sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, + pCol->zCnName); + sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); + if( bStrict && pCol->eCType!=COLTYPE_ANY ){ + sqlcipher_sqlite3VdbeGoto(v, doError); + }else{ + integrityCheckResultRow(v); + } + sqlcipher_sqlite3VdbeJumpHere(v, jmp2); + } + if( (pTab->tabFlags & TF_Strict)!=0 + && pCol->eCType!=COLTYPE_ANY + ){ + jmp2 = sqlcipher_sqlite3VdbeAddOp3(v, OP_IsNullOrType, 3, 0, + sqlcipher_sqlite3StdTypeMap[pCol->eCType-1]); + VdbeCoverage(v); + zErr = sqlcipher_sqlite3MPrintf(db, "non-%s value in %s.%s", + sqlcipher_sqlite3StdType[pCol->eCType-1], + pTab->zName, pTab->aCol[j].zCnName); + sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); + sqlcipher_sqlite3VdbeResolveLabel(v, doError); + integrityCheckResultRow(v); + sqlcipher_sqlite3VdbeJumpHere(v, jmp2); + } } /* Verify CHECK constraints */ if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ @@ -133039,7 +139209,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( ** Checkpoint the database. */ case PragTyp_WAL_CHECKPOINT: { - int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED); + int iBt = (pId2->z?iDb:SQLITE_MAX_DB); int eMode = SQLITE_CHECKPOINT_PASSIVE; if( zRight ){ if( sqlcipher_sqlite3StrICmp(zRight, "full")==0 ){ @@ -133288,12 +139458,12 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Pragma( case PragTyp_ANALYSIS_LIMIT: { sqlcipher_sqlite3_int64 N; if( zRight - && sqlcipher_sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK + && sqlcipher_sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK /* IMP: R-40975-20399 */ && N>=0 ){ db->nAnalysisLimit = (int)(N&0x7fffffff); } - returnSingleInt(v, db->nAnalysisLimit); + returnSingleInt(v, db->nAnalysisLimit); /* IMP: R-57594-65522 */ break; } @@ -133730,7 +139900,7 @@ SQLITE_PRIVATE Module *sqlcipher_sqlite3PragmaVtabRegister(sqlcipher_sqlite3 *db */ static void corruptSchema( InitData *pData, /* Initialization context */ - const char *zObj, /* Object being parsed at the point of error */ + char **azObj, /* Type and name of object being parsed */ const char *zExtra /* Error information */ ){ sqlcipher_sqlite3 *db = pData->db; @@ -133738,14 +139908,23 @@ static void corruptSchema( pData->rc = SQLITE_NOMEM_BKPT; }else if( pData->pzErrMsg[0]!=0 ){ /* A error message has already been generated. Do not overwrite it */ - }else if( pData->mInitFlags & INITFLAG_AlterTable ){ - *pData->pzErrMsg = sqlcipher_sqlite3DbStrDup(db, zExtra); + }else if( pData->mInitFlags & (INITFLAG_AlterMask) ){ + static const char *azAlterType[] = { + "rename", + "drop column", + "add column" + }; + *pData->pzErrMsg = sqlcipher_sqlite3MPrintf(db, + "error in %s %s after %s: %s", azObj[0], azObj[1], + azAlterType[(pData->mInitFlags&INITFLAG_AlterMask)-1], + zExtra + ); pData->rc = SQLITE_ERROR; }else if( db->flags & SQLITE_WriteSchema ){ pData->rc = SQLITE_CORRUPT_BKPT; }else{ char *z; - if( zObj==0 ) zObj = "?"; + const char *zObj = azObj[1] ? azObj[1] : "?"; z = sqlcipher_sqlite3MPrintf(db, "malformed database schema (%s)", zObj); if( zExtra && zExtra[0] ) z = sqlcipher_sqlite3MPrintf(db, "%z - %s", z, zExtra); *pData->pzErrMsg = z; @@ -133801,21 +139980,28 @@ SQLITE_PRIVATE int sqlcipher_sqlite3InitCallback(void *pInit, int argc, char **a UNUSED_PARAMETER2(NotUsed, argc); assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); db->mDbFlags |= DBFLAG_EncodingFixed; + if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ pData->nInitRow++; if( db->mallocFailed ){ - corruptSchema(pData, argv[1], 0); + corruptSchema(pData, argv, 0); return 1; } assert( iDb>=0 && iDbnDb ); - if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ if( argv[3]==0 ){ - corruptSchema(pData, argv[1], 0); - }else if( sqlcipher_sqlite3_strnicmp(argv[4],"create ",7)==0 ){ + corruptSchema(pData, argv, 0); + }else if( argv[4] + && 'c'==sqlcipher_sqlite3UpperToLower[(unsigned char)argv[4][0]] + && 'r'==sqlcipher_sqlite3UpperToLower[(unsigned char)argv[4][1]] ){ /* Call the parser to process a CREATE TABLE, INDEX or VIEW. ** But because db->init.busy is set to 1, no VDBE code is generated ** or executed. All the parser does is build the internal data ** structures that describe the table, index, or view. + ** + ** No other valid SQL statement, other than the variable CREATE statements, + ** can begin with the letters "C" and "R". Thus, it is not possible run + ** any other kind of statement while parsing the schema, even a corrupt + ** schema. */ int rc; u8 saved_iDb = db->init.iDb; @@ -133828,11 +140014,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3InitCallback(void *pInit, int argc, char **a || (db->init.newTnum>pData->mxPage && pData->mxPage>0) ){ if( sqlcipher_sqlite3Config.bExtraSchemaChecks ){ - corruptSchema(pData, argv[1], "invalid rootpage"); + corruptSchema(pData, argv, "invalid rootpage"); } } db->init.orphanTrigger = 0; - db->init.azInit = argv; + db->init.azInit = (const char**)argv; pStmt = 0; TESTONLY(rcp = ) sqlcipher_sqlite3Prepare(db, argv[4], -1, 0, 0, &pStmt, 0); rc = db->errCode; @@ -133847,13 +140033,14 @@ SQLITE_PRIVATE int sqlcipher_sqlite3InitCallback(void *pInit, int argc, char **a if( rc==SQLITE_NOMEM ){ sqlcipher_sqlite3OomFault(db); }else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){ - corruptSchema(pData, argv[1], sqlcipher_sqlite3_errmsg(db)); + corruptSchema(pData, argv, sqlcipher_sqlite3_errmsg(db)); } } } + db->init.azInit = sqlcipher_sqlite3StdType; /* Any array of string ptrs will do */ sqlcipher_sqlite3_finalize(pStmt); }else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){ - corruptSchema(pData, argv[1], 0); + corruptSchema(pData, argv, 0); }else{ /* If the SQL column is blank it means this is an index that ** was created to be the PRIMARY KEY or to fulfill a UNIQUE @@ -133864,7 +140051,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3InitCallback(void *pInit, int argc, char **a Index *pIndex; pIndex = sqlcipher_sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName); if( pIndex==0 ){ - corruptSchema(pData, argv[1], "orphan index"); + corruptSchema(pData, argv, "orphan index"); }else if( sqlcipher_sqlite3GetUInt32(argv[3],&pIndex->tnum)==0 || pIndex->tnum<2 @@ -133872,7 +140059,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3InitCallback(void *pInit, int argc, char **a || sqlcipher_sqlite3IndexHasDuplicateRootPage(pIndex) ){ if( sqlcipher_sqlite3Config.bExtraSchemaChecks ){ - corruptSchema(pData, argv[1], "invalid rootpage"); + corruptSchema(pData, argv, "invalid rootpage"); } } } @@ -134075,18 +140262,22 @@ SQLITE_PRIVATE int sqlcipher_sqlite3InitOne(sqlcipher_sqlite3 *db, int iDb, char } #endif } + assert( pDb == &(db->aDb[iDb]) ); if( db->mallocFailed ){ rc = SQLITE_NOMEM_BKPT; sqlcipher_sqlite3ResetAllSchemasOfConnection(db); - } - if( rc==SQLITE_OK || (db->flags&SQLITE_NoSchemaError)){ - /* Black magic: If the SQLITE_NoSchemaError flag is set, then consider - ** the schema loaded, even if errors occurred. In this situation the - ** current sqlcipher_sqlite3_prepare() operation will fail, but the following one - ** will attempt to compile the supplied statement against whatever subset - ** of the schema was loaded before the error occurred. The primary - ** purpose of this is to allow access to the sqlite_schema table - ** even when its contents have been corrupted. + pDb = &db->aDb[iDb]; + }else + if( rc==SQLITE_OK || ((db->flags&SQLITE_NoSchemaError) && rc!=SQLITE_NOMEM)){ + /* Hack: If the SQLITE_NoSchemaError flag is set, then consider + ** the schema loaded, even if errors (other than OOM) occurred. In + ** this situation the current sqlcipher_sqlite3_prepare() operation will fail, + ** but the following one will attempt to compile the supplied statement + ** against whatever subset of the schema was loaded before the error + ** occurred. + ** + ** The primary purpose of this is to allow access to the sqlite_schema + ** table even when its contents have been corrupted. */ DbSetProperty(db, iDb, DB_SchemaLoaded); rc = SQLITE_OK; @@ -134196,6 +140387,7 @@ static void schemaIsValid(Parse *pParse){ rc = sqlcipher_sqlite3BtreeBeginTrans(pBt, 0, 0); if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ sqlcipher_sqlite3OomFault(db); + pParse->rc = SQLITE_NOMEM; } if( rc!=SQLITE_OK ) return; openedTransaction = 1; @@ -134252,36 +140444,112 @@ SQLITE_PRIVATE int sqlcipher_sqlite3SchemaToIndex(sqlcipher_sqlite3 *db, Schema return i; } -/* -** Deallocate a single AggInfo object -*/ -static void agginfoFree(sqlcipher_sqlite3 *db, AggInfo *p){ - sqlcipher_sqlite3DbFree(db, p->aCol); - sqlcipher_sqlite3DbFree(db, p->aFunc); - sqlcipher_sqlite3DbFree(db, p); -} - /* ** Free all memory allocations in the pParse object */ -SQLITE_PRIVATE void sqlcipher_sqlite3ParserReset(Parse *pParse){ +SQLITE_PRIVATE void sqlcipher_sqlite3ParseObjectReset(Parse *pParse){ sqlcipher_sqlite3 *db = pParse->db; - AggInfo *pThis = pParse->pAggList; - while( pThis ){ - AggInfo *pNext = pThis->pNext; - agginfoFree(db, pThis); - pThis = pNext; + assert( db!=0 ); + assert( db->pParse==pParse ); + assert( pParse->nested==0 ); +#ifndef SQLITE_OMIT_SHARED_CACHE + sqlcipher_sqlite3DbFree(db, pParse->aTableLock); +#endif + while( pParse->pCleanup ){ + ParseCleanup *pCleanup = pParse->pCleanup; + pParse->pCleanup = pCleanup->pNext; + pCleanup->xCleanup(db, pCleanup->pPtr); + sqlcipher_sqlite3DbFreeNN(db, pCleanup); } sqlcipher_sqlite3DbFree(db, pParse->aLabel); - sqlcipher_sqlite3ExprListDelete(db, pParse->pConstExpr); - if( db ){ - assert( db->lookaside.bDisable >= pParse->disableLookaside ); - db->lookaside.bDisable -= pParse->disableLookaside; - db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue; - } + if( pParse->pConstExpr ){ + sqlcipher_sqlite3ExprListDelete(db, pParse->pConstExpr); + } + assert( db->lookaside.bDisable >= pParse->disableLookaside ); + db->lookaside.bDisable -= pParse->disableLookaside; + db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue; + assert( pParse->db->pParse==pParse ); + db->pParse = pParse->pOuterParse; + pParse->db = 0; pParse->disableLookaside = 0; } +/* +** Add a new cleanup operation to a Parser. The cleanup should happen when +** the parser object is destroyed. But, beware: the cleanup might happen +** immediately. +** +** Use this mechanism for uncommon cleanups. There is a higher setup +** cost for this mechansim (an extra malloc), so it should not be used +** for common cleanups that happen on most calls. But for less +** common cleanups, we save a single NULL-pointer comparison in +** sqlcipher_sqlite3ParseObjectReset(), which reduces the total CPU cycle count. +** +** If a memory allocation error occurs, then the cleanup happens immediately. +** When either SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the +** pParse->earlyCleanup flag is set in that case. Calling code show verify +** that test cases exist for which this happens, to guard against possible +** use-after-free errors following an OOM. The preferred way to do this is +** to immediately follow the call to this routine with: +** +** testcase( pParse->earlyCleanup ); +** +** This routine returns a copy of its pPtr input (the third parameter) +** except if an early cleanup occurs, in which case it returns NULL. So +** another way to check for early cleanup is to check the return value. +** Or, stop using the pPtr parameter with this call and use only its +** return value thereafter. Something like this: +** +** pObj = sqlcipher_sqlite3ParserAddCleanup(pParse, destructor, pObj); +*/ +SQLITE_PRIVATE void *sqlcipher_sqlite3ParserAddCleanup( + Parse *pParse, /* Destroy when this Parser finishes */ + void (*xCleanup)(sqlcipher_sqlite3*,void*), /* The cleanup routine */ + void *pPtr /* Pointer to object to be cleaned up */ +){ + ParseCleanup *pCleanup = sqlcipher_sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup)); + if( pCleanup ){ + pCleanup->pNext = pParse->pCleanup; + pParse->pCleanup = pCleanup; + pCleanup->pPtr = pPtr; + pCleanup->xCleanup = xCleanup; + }else{ + xCleanup(pParse->db, pPtr); + pPtr = 0; +#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) + pParse->earlyCleanup = 1; +#endif + } + return pPtr; +} + +/* +** Turn bulk memory into a valid Parse object and link that Parse object +** into database connection db. +** +** Call sqlcipher_sqlite3ParseObjectReset() to undo this operation. +** +** Caution: Do not confuse this routine with sqlcipher_sqlite3ParseObjectInit() which +** is generated by Lemon. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3ParseObjectInit(Parse *pParse, sqlcipher_sqlite3 *db){ + memset(PARSE_HDR(pParse), 0, PARSE_HDR_SZ); + memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ); + assert( db->pParse!=pParse ); + pParse->pOuterParse = db->pParse; + db->pParse = pParse; + pParse->db = db; + if( db->mallocFailed ) sqlcipher_sqlite3ErrorMsg(pParse, "out of memory"); +} + +/* +** Maximum number of times that we will try again to prepare a statement +** that returns SQLITE_ERROR_RETRY. +*/ +#ifndef SQLITE_MAX_PREPARE_RETRY +# define SQLITE_MAX_PREPARE_RETRY 25 +#endif + /* ** Compile the UTF-8 encoded SQL statement zSql into a statement handle. */ @@ -134294,16 +140562,19 @@ static int sqlcipher_sqlite3Prepare( sqlcipher_sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ - char *zErrMsg = 0; /* Error message */ int rc = SQLITE_OK; /* Result code */ int i; /* Loop counter */ Parse sParse; /* Parsing context */ - memset(&sParse, 0, PARSE_HDR_SZ); + /* sqlcipher_sqlite3ParseObjectInit(&sParse, db); // inlined for performance */ + memset(PARSE_HDR(&sParse), 0, PARSE_HDR_SZ); memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ); + sParse.pOuterParse = db->pParse; + db->pParse = &sParse; + sParse.db = db; sParse.pReprepare = pReprepare; assert( ppStmt && *ppStmt==0 ); - /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */ + if( db->mallocFailed ) sqlcipher_sqlite3ErrorMsg(&sParse, "out of memory"); assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); /* For a long-term use prepared statement avoid the use of @@ -134356,7 +140627,6 @@ static int sqlcipher_sqlite3Prepare( sqlcipher_sqlite3VtabUnlockList(db); - sParse.db = db; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ char *zSqlCopy; int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; @@ -134369,23 +140639,17 @@ static int sqlcipher_sqlite3Prepare( } zSqlCopy = sqlcipher_sqlite3DbStrNDup(db, zSql, nBytes); if( zSqlCopy ){ - sqlcipher_sqlite3RunParser(&sParse, zSqlCopy, &zErrMsg); + sqlcipher_sqlite3RunParser(&sParse, zSqlCopy); sParse.zTail = &zSql[sParse.zTail-zSqlCopy]; sqlcipher_sqlite3DbFree(db, zSqlCopy); }else{ sParse.zTail = &zSql[nBytes]; } }else{ - sqlcipher_sqlite3RunParser(&sParse, zSql, &zErrMsg); + sqlcipher_sqlite3RunParser(&sParse, zSql); } assert( 0==sParse.nQueryLoop ); - if( sParse.rc==SQLITE_DONE ){ - sParse.rc = SQLITE_OK; - } - if( sParse.checkSchema ){ - schemaIsValid(&sParse); - } if( pzTail ){ *pzTail = sParse.zTail; } @@ -134395,21 +140659,30 @@ static int sqlcipher_sqlite3Prepare( } if( db->mallocFailed ){ sParse.rc = SQLITE_NOMEM_BKPT; + sParse.checkSchema = 0; } - rc = sParse.rc; - if( rc!=SQLITE_OK ){ - if( sParse.pVdbe ) sqlcipher_sqlite3VdbeFinalize(sParse.pVdbe); - assert(!(*ppStmt)); + if( sParse.rc!=SQLITE_OK && sParse.rc!=SQLITE_DONE ){ + if( sParse.checkSchema && db->init.busy==0 ){ + schemaIsValid(&sParse); + } + if( sParse.pVdbe ){ + sqlcipher_sqlite3VdbeFinalize(sParse.pVdbe); + } + assert( 0==(*ppStmt) ); + rc = sParse.rc; + if( sParse.zErrMsg ){ + sqlcipher_sqlite3ErrorWithMsg(db, rc, "%s", sParse.zErrMsg); + sqlcipher_sqlite3DbFree(db, sParse.zErrMsg); + }else{ + sqlcipher_sqlite3Error(db, rc); + } }else{ + assert( sParse.zErrMsg==0 ); *ppStmt = (sqlcipher_sqlite3_stmt*)sParse.pVdbe; + rc = SQLITE_OK; + sqlcipher_sqlite3ErrorClear(db); } - if( zErrMsg ){ - sqlcipher_sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg); - sqlcipher_sqlite3DbFree(db, zErrMsg); - }else{ - sqlcipher_sqlite3Error(db, rc); - } /* Delete any TriggerPrg structures allocated while parsing this statement. */ while( sParse.pTriggerPrg ){ @@ -134420,7 +140693,7 @@ static int sqlcipher_sqlite3Prepare( end_prepare: - sqlcipher_sqlite3ParserReset(&sParse); + sqlcipher_sqlite3ParseObjectReset(&sParse); return rc; } static int sqlcipher_sqlite3LockAndPrepare( @@ -134450,7 +140723,8 @@ static int sqlcipher_sqlite3LockAndPrepare( ** reset is considered a permanent error. */ rc = sqlcipher_sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail); assert( rc==SQLITE_OK || *ppStmt==0 ); - }while( rc==SQLITE_ERROR_RETRY + if( rc==SQLITE_OK || db->mallocFailed ) break; + }while( (rc==SQLITE_ERROR_RETRY && (cnt++)pHaving); sqlcipher_sqlite3ExprListDelete(db, p->pOrderBy); sqlcipher_sqlite3ExprDelete(db, p->pLimit); + if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlcipher_sqlite3WithDelete(db, p->pWith); #ifndef SQLITE_OMIT_WINDOWFUNC if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){ sqlcipher_sqlite3WindowListDelete(db, p->pWinDefn); } + while( p->pWin ){ + assert( p->pWin->ppThis==&p->pWin ); + sqlcipher_sqlite3WindowUnlinkFromSelect(p->pWin); + } #endif - if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlcipher_sqlite3WithDelete(db, p->pWith); if( bFree ) sqlcipher_sqlite3DbFreeNN(db, p); p = pPrior; bFree = 1; @@ -134870,6 +141148,52 @@ static Select *findRightmost(Select *p){ ** ** If an illegal or unsupported join type is seen, then still return ** a join type, but put an error in the pParse structure. +** +** These are the valid join types: +** +** +** pA pB pC Return Value +** ------- ----- ----- ------------ +** CROSS - - JT_CROSS +** INNER - - JT_INNER +** LEFT - - JT_LEFT|JT_OUTER +** LEFT OUTER - JT_LEFT|JT_OUTER +** RIGHT - - JT_RIGHT|JT_OUTER +** RIGHT OUTER - JT_RIGHT|JT_OUTER +** FULL - - JT_LEFT|JT_RIGHT|JT_OUTER +** FULL OUTER - JT_LEFT|JT_RIGHT|JT_OUTER +** NATURAL INNER - JT_NATURAL|JT_INNER +** NATURAL LEFT - JT_NATURAL|JT_LEFT|JT_OUTER +** NATURAL LEFT OUTER JT_NATURAL|JT_LEFT|JT_OUTER +** NATURAL RIGHT - JT_NATURAL|JT_RIGHT|JT_OUTER +** NATURAL RIGHT OUTER JT_NATURAL|JT_RIGHT|JT_OUTER +** NATURAL FULL - JT_NATURAL|JT_LEFT|JT_RIGHT +** NATURAL FULL OUTER JT_NATRUAL|JT_LEFT|JT_RIGHT +** +** To preserve historical compatibly, SQLite also accepts a variety +** of other non-standard and in many cases non-sensical join types. +** This routine makes as much sense at it can from the nonsense join +** type and returns a result. Examples of accepted nonsense join types +** include but are not limited to: +** +** INNER CROSS JOIN -> same as JOIN +** NATURAL CROSS JOIN -> same as NATURAL JOIN +** OUTER LEFT JOIN -> same as LEFT JOIN +** LEFT NATURAL JOIN -> same as NATURAL LEFT JOIN +** LEFT RIGHT JOIN -> same as FULL JOIN +** RIGHT OUTER FULL JOIN -> same as FULL JOIN +** CROSS CROSS CROSS JOIN -> same as JOIN +** +** The only restrictions on the join type name are: +** +** * "INNER" cannot appear together with "OUTER", "LEFT", "RIGHT", +** or "FULL". +** +** * "CROSS" cannot appear together with "OUTER", "LEFT", "RIGHT, +** or "FULL". +** +** * If "OUTER" is present then there must also be one of +** "LEFT", "RIGHT", or "FULL" */ SQLITE_PRIVATE int sqlcipher_sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){ int jointype = 0; @@ -134882,13 +141206,13 @@ SQLITE_PRIVATE int sqlcipher_sqlite3JoinType(Parse *pParse, Token *pA, Token *pB u8 nChar; /* Length of the keyword in characters */ u8 code; /* Join type mask */ } aKeyword[] = { - /* natural */ { 0, 7, JT_NATURAL }, - /* left */ { 6, 4, JT_LEFT|JT_OUTER }, - /* outer */ { 10, 5, JT_OUTER }, - /* right */ { 14, 5, JT_RIGHT|JT_OUTER }, - /* full */ { 19, 4, JT_LEFT|JT_RIGHT|JT_OUTER }, - /* inner */ { 23, 5, JT_INNER }, - /* cross */ { 28, 5, JT_INNER|JT_CROSS }, + /* (0) natural */ { 0, 7, JT_NATURAL }, + /* (1) left */ { 6, 4, JT_LEFT|JT_OUTER }, + /* (2) outer */ { 10, 5, JT_OUTER }, + /* (3) right */ { 14, 5, JT_RIGHT|JT_OUTER }, + /* (4) full */ { 19, 4, JT_LEFT|JT_RIGHT|JT_OUTER }, + /* (5) inner */ { 23, 5, JT_INNER }, + /* (6) cross */ { 28, 5, JT_INNER|JT_CROSS }, }; int i, j; apAll[0] = pA; @@ -134911,18 +141235,15 @@ SQLITE_PRIVATE int sqlcipher_sqlite3JoinType(Parse *pParse, Token *pA, Token *pB } if( (jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) || - (jointype & JT_ERROR)!=0 + (jointype & JT_ERROR)!=0 || + (jointype & (JT_OUTER|JT_LEFT|JT_RIGHT))==JT_OUTER ){ - const char *zSp = " "; - assert( pB!=0 ); - if( pC==0 ){ zSp++; } - sqlcipher_sqlite3ErrorMsg(pParse, "unknown or unsupported join type: " - "%T %T%s%T", pA, pB, zSp, pC); - jointype = JT_INNER; - }else if( (jointype & JT_OUTER)!=0 - && (jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ){ - sqlcipher_sqlite3ErrorMsg(pParse, - "RIGHT and FULL OUTER JOINs are not currently supported"); + const char *zSp1 = " "; + const char *zSp2 = " "; + if( pB==0 ){ zSp1++; } + if( pC==0 ){ zSp2++; } + sqlcipher_sqlite3ErrorMsg(pParse, "unknown join type: " + "%T%s%T%s%T", pA, zSp1, pB, zSp2, pC); jointype = JT_INNER; } return jointype; @@ -134932,19 +141253,36 @@ SQLITE_PRIVATE int sqlcipher_sqlite3JoinType(Parse *pParse, Token *pA, Token *pB ** Return the index of a column in a table. Return -1 if the column ** is not contained in the table. */ -static int columnIndex(Table *pTab, const char *zCol){ +SQLITE_PRIVATE int sqlcipher_sqlite3ColumnIndex(Table *pTab, const char *zCol){ int i; u8 h = sqlcipher_sqlite3StrIHash(zCol); Column *pCol; for(pCol=pTab->aCol, i=0; inCol; pCol++, i++){ - if( pCol->hName==h && sqlcipher_sqlite3StrICmp(pCol->zName, zCol)==0 ) return i; + if( pCol->hName==h && sqlcipher_sqlite3StrICmp(pCol->zCnName, zCol)==0 ) return i; } return -1; } /* -** Search the first N tables in pSrc, from left to right, looking for a -** table that has a column named zCol. +** Mark a subquery result column as having been used. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3SrcItemColumnUsed(SrcItem *pItem, int iCol){ + assert( pItem!=0 ); + assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); + if( pItem->fg.isNestedFrom ){ + ExprList *pResults; + assert( pItem->pSelect!=0 ); + pResults = pItem->pSelect->pEList; + assert( pResults!=0 ); + assert( iCol>=0 && iColnExpr ); + pResults->a[iCol].fg.bUsed = 1; + } +} + +/* +** Search the tables iStart..iEnd (inclusive) in pSrc, looking for a +** table that has a column named zCol. The search is left-to-right. +** The first match found is returned. ** ** When found, set *piTab and *piCol to the table index and column index ** of the matching column and return TRUE. @@ -134953,22 +141291,27 @@ static int columnIndex(Table *pTab, const char *zCol){ */ static int tableAndColumnIndex( SrcList *pSrc, /* Array of tables to search */ - int N, /* Number of tables in pSrc->a[] to search */ + int iStart, /* First member of pSrc->a[] to check */ + int iEnd, /* Last member of pSrc->a[] to check */ const char *zCol, /* Name of the column we are looking for */ int *piTab, /* Write index of pSrc->a[] here */ int *piCol, /* Write index of pSrc->a[*piTab].pTab->aCol[] here */ - int bIgnoreHidden /* True to ignore hidden columns */ + int bIgnoreHidden /* Ignore hidden columns */ ){ int i; /* For looping over tables in pSrc */ int iCol; /* Index of column matching zCol */ + assert( iEndnSrc ); + assert( iStart>=0 ); assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */ - for(i=0; ia[i].pTab, zCol); + + for(i=iStart; i<=iEnd; i++){ + iCol = sqlcipher_sqlite3ColumnIndex(pSrc->a[i].pTab, zCol); if( iCol>=0 && (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0) ){ if( piTab ){ + sqlcipher_sqlite3SrcItemColumnUsed(&pSrc->a[i], iCol); *piTab = i; *piCol = iCol; } @@ -134979,63 +141322,19 @@ static int tableAndColumnIndex( } /* -** This function is used to add terms implied by JOIN syntax to the -** WHERE clause expression of a SELECT statement. The new term, which -** is ANDed with the existing WHERE clause, is of the form: -** -** (tab1.col1 = tab2.col2) -** -** where tab1 is the iSrc'th table in SrcList pSrc and tab2 is the -** (iSrc+1)'th. Column col1 is column iColLeft of tab1, and col2 is -** column iColRight of tab2. -*/ -static void addWhereTerm( - Parse *pParse, /* Parsing context */ - SrcList *pSrc, /* List of tables in FROM clause */ - int iLeft, /* Index of first table to join in pSrc */ - int iColLeft, /* Index of column in first table */ - int iRight, /* Index of second table in pSrc */ - int iColRight, /* Index of column in second table */ - int isOuterJoin, /* True if this is an OUTER join */ - Expr **ppWhere /* IN/OUT: The WHERE clause to add to */ -){ - sqlcipher_sqlite3 *db = pParse->db; - Expr *pE1; - Expr *pE2; - Expr *pEq; - - assert( iLeftnSrc>iRight ); - assert( pSrc->a[iLeft].pTab ); - assert( pSrc->a[iRight].pTab ); - - pE1 = sqlcipher_sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft); - pE2 = sqlcipher_sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight); - - pEq = sqlcipher_sqlite3PExpr(pParse, TK_EQ, pE1, pE2); - if( pEq && isOuterJoin ){ - ExprSetProperty(pEq, EP_FromJoin); - assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) ); - ExprSetVVAProperty(pEq, EP_NoReduce); - pEq->iRightJoinTable = (i16)pE2->iTable; - } - *ppWhere = sqlcipher_sqlite3ExprAnd(pParse, *ppWhere, pEq); -} - -/* -** Set the EP_FromJoin property on all terms of the given expression. -** And set the Expr.iRightJoinTable to iTable for every term in the +** Set the EP_OuterON property on all terms of the given expression. +** And set the Expr.w.iJoin to iTable for every term in the ** expression. ** -** The EP_FromJoin property is used on terms of an expression to tell -** the LEFT OUTER JOIN processing logic that this term is part of the +** The EP_OuterON property is used on terms of an expression to tell +** the OUTER JOIN processing logic that this term is part of the ** join restriction specified in the ON or USING clause and not a part ** of the more general WHERE clause. These terms are moved over to the ** WHERE clause during join processing but we need to remember that they ** originated in the ON or USING clause. ** -** The Expr.iRightJoinTable tells the WHERE clause processing that the -** expression depends on table iRightJoinTable even if that table is not +** The Expr.w.iJoin tells the WHERE clause processing that the +** expression depends on table w.iJoin even if that table is not ** explicitly mentioned in the expression. That information is needed ** for cases like this: ** @@ -135048,114 +141347,131 @@ static void addWhereTerm( ** after the t1 loop and rows with t1.x!=5 will never appear in ** the output, which is incorrect. */ -SQLITE_PRIVATE void sqlcipher_sqlite3SetJoinExpr(Expr *p, int iTable){ +SQLITE_PRIVATE void sqlcipher_sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){ + assert( joinFlag==EP_OuterON || joinFlag==EP_InnerON ); while( p ){ - ExprSetProperty(p, EP_FromJoin); + ExprSetProperty(p, joinFlag); assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); ExprSetVVAProperty(p, EP_NoReduce); - p->iRightJoinTable = (i16)iTable; - if( p->op==TK_FUNCTION && p->x.pList ){ - int i; - for(i=0; ix.pList->nExpr; i++){ - sqlcipher_sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable); + p->w.iJoin = iTable; + if( p->op==TK_FUNCTION ){ + assert( ExprUseXList(p) ); + if( p->x.pList ){ + int i; + for(i=0; ix.pList->nExpr; i++){ + sqlcipher_sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag); + } } } - sqlcipher_sqlite3SetJoinExpr(p->pLeft, iTable); + sqlcipher_sqlite3SetJoinExpr(p->pLeft, iTable, joinFlag); p = p->pRight; } } -/* Undo the work of sqlcipher_sqlite3SetJoinExpr(). In the expression p, convert every -** term that is marked with EP_FromJoin and iRightJoinTable==iTable into -** an ordinary term that omits the EP_FromJoin mark. +/* Undo the work of sqlcipher_sqlite3SetJoinExpr(). This is used when a LEFT JOIN +** is simplified into an ordinary JOIN, and when an ON expression is +** "pushed down" into the WHERE clause of a subquery. ** -** This happens when a LEFT JOIN is simplified into an ordinary JOIN. +** Convert every term that is marked with EP_OuterON and w.iJoin==iTable into +** an ordinary term that omits the EP_OuterON mark. Or if iTable<0, then +** just clear every EP_OuterON and EP_InnerON mark from the expression tree. +** +** If nullable is true, that means that Expr p might evaluate to NULL even +** if it is a reference to a NOT NULL column. This can happen, for example, +** if the table that p references is on the left side of a RIGHT JOIN. +** If nullable is true, then take care to not remove the EP_CanBeNull bit. +** See forum thread https://sqlite.org/forum/forumpost/b40696f50145d21c */ -static void unsetJoinExpr(Expr *p, int iTable){ +static void unsetJoinExpr(Expr *p, int iTable, int nullable){ while( p ){ - if( ExprHasProperty(p, EP_FromJoin) - && (iTable<0 || p->iRightJoinTable==iTable) ){ - ExprClearProperty(p, EP_FromJoin); + if( iTable<0 || (ExprHasProperty(p, EP_OuterON) && p->w.iJoin==iTable) ){ + ExprClearProperty(p, EP_OuterON|EP_InnerON); + if( iTable>=0 ) ExprSetProperty(p, EP_InnerON); } - if( p->op==TK_FUNCTION && p->x.pList ){ - int i; - for(i=0; ix.pList->nExpr; i++){ - unsetJoinExpr(p->x.pList->a[i].pExpr, iTable); + if( p->op==TK_COLUMN && p->iTable==iTable && !nullable ){ + ExprClearProperty(p, EP_CanBeNull); + } + if( p->op==TK_FUNCTION ){ + assert( ExprUseXList(p) ); + if( p->x.pList ){ + int i; + for(i=0; ix.pList->nExpr; i++){ + unsetJoinExpr(p->x.pList->a[i].pExpr, iTable, nullable); + } } } - unsetJoinExpr(p->pLeft, iTable); + unsetJoinExpr(p->pLeft, iTable, nullable); p = p->pRight; } } /* ** This routine processes the join information for a SELECT statement. -** ON and USING clauses are converted into extra terms of the WHERE clause. -** NATURAL joins also create extra WHERE clause terms. +** +** * A NATURAL join is converted into a USING join. After that, we +** do not need to be concerned with NATURAL joins and we only have +** think about USING joins. +** +** * ON and USING clauses result in extra terms being added to the +** WHERE clause to enforce the specified constraints. The extra +** WHERE clause terms will be tagged with EP_OuterON or +** EP_InnerON so that we know that they originated in ON/USING. ** ** The terms of a FROM clause are contained in the Select.pSrc structure. ** The left most table is the first entry in Select.pSrc. The right-most ** table is the last entry. The join operator is held in the entry to -** the left. Thus entry 0 contains the join operator for the join between +** the right. Thus entry 1 contains the join operator for the join between ** entries 0 and 1. Any ON or USING clauses associated with the join are -** also attached to the left entry. +** also attached to the right entry. ** ** This routine returns the number of errors encountered. */ -static int sqliteProcessJoin(Parse *pParse, Select *p){ +static int sqlcipher_sqlite3ProcessJoin(Parse *pParse, Select *p){ SrcList *pSrc; /* All tables in the FROM clause */ int i, j; /* Loop counters */ - struct SrcList_item *pLeft; /* Left table being joined */ - struct SrcList_item *pRight; /* Right table being joined */ + SrcItem *pLeft; /* Left table being joined */ + SrcItem *pRight; /* Right table being joined */ pSrc = p->pSrc; pLeft = &pSrc->a[0]; pRight = &pLeft[1]; for(i=0; inSrc-1; i++, pRight++, pLeft++){ Table *pRightTab = pRight->pTab; - int isOuter; + u32 joinType; if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue; - isOuter = (pRight->fg.jointype & JT_OUTER)!=0; + joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_OuterON : EP_InnerON; - /* When the NATURAL keyword is present, add WHERE clause terms for - ** every column that the two tables have in common. + /* If this is a NATURAL join, synthesize an approprate USING clause + ** to specify which columns should be joined. */ if( pRight->fg.jointype & JT_NATURAL ){ - if( pRight->pOn || pRight->pUsing ){ + IdList *pUsing = 0; + if( pRight->fg.isUsing || pRight->u3.pOn ){ sqlcipher_sqlite3ErrorMsg(pParse, "a NATURAL join may not have " "an ON or USING clause", 0); return 1; } for(j=0; jnCol; j++){ char *zName; /* Name of column in the right table */ - int iLeft; /* Matching left table */ - int iLeftCol; /* Matching column in the left table */ if( IsHiddenColumn(&pRightTab->aCol[j]) ) continue; - zName = pRightTab->aCol[j].zName; - if( tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 1) ){ - addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, j, - isOuter, &p->pWhere); + zName = pRightTab->aCol[j].zCnName; + if( tableAndColumnIndex(pSrc, 0, i, zName, 0, 0, 1) ){ + pUsing = sqlcipher_sqlite3IdListAppend(pParse, pUsing, 0); + if( pUsing ){ + assert( pUsing->nId>0 ); + assert( pUsing->a[pUsing->nId-1].zName==0 ); + pUsing->a[pUsing->nId-1].zName = sqlcipher_sqlite3DbStrDup(pParse->db, zName); + } } } - } - - /* Disallow both ON and USING clauses in the same join - */ - if( pRight->pOn && pRight->pUsing ){ - sqlcipher_sqlite3ErrorMsg(pParse, "cannot have both ON and USING " - "clauses in the same join"); - return 1; - } - - /* Add the ON clause to the end of the WHERE clause, connected by - ** an AND operator. - */ - if( pRight->pOn ){ - if( isOuter ) sqlcipher_sqlite3SetJoinExpr(pRight->pOn, pRight->iCursor); - p->pWhere = sqlcipher_sqlite3ExprAnd(pParse, p->pWhere, pRight->pOn); - pRight->pOn = 0; + if( pUsing ){ + pRight->fg.isUsing = 1; + pRight->fg.isSynthUsing = 1; + pRight->u3.pUsing = pUsing; + } + if( pParse->nErr ) return 1; } /* Create extra terms on the WHERE clause for each column named @@ -135165,27 +141481,88 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ ** Report an error if any column mentioned in the USING clause is ** not contained in both tables to be joined. */ - if( pRight->pUsing ){ - IdList *pList = pRight->pUsing; + if( pRight->fg.isUsing ){ + IdList *pList = pRight->u3.pUsing; + sqlcipher_sqlite3 *db = pParse->db; + assert( pList!=0 ); for(j=0; jnId; j++){ char *zName; /* Name of the term in the USING clause */ int iLeft; /* Table on the left with matching column name */ int iLeftCol; /* Column number of matching column on the left */ int iRightCol; /* Column number of matching column on the right */ + Expr *pE1; /* Reference to the column on the LEFT of the join */ + Expr *pE2; /* Reference to the column on the RIGHT of the join */ + Expr *pEq; /* Equality constraint. pE1 == pE2 */ zName = pList->a[j].zName; - iRightCol = columnIndex(pRightTab, zName); + iRightCol = sqlcipher_sqlite3ColumnIndex(pRightTab, zName); if( iRightCol<0 - || !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 0) + || tableAndColumnIndex(pSrc, 0, i, zName, &iLeft, &iLeftCol, + pRight->fg.isSynthUsing)==0 ){ sqlcipher_sqlite3ErrorMsg(pParse, "cannot join using column %s - column " "not present in both tables", zName); return 1; } - addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, iRightCol, - isOuter, &p->pWhere); + pE1 = sqlcipher_sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol); + sqlcipher_sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol); + if( (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ + /* This branch runs if the query contains one or more RIGHT or FULL + ** JOINs. If only a single table on the left side of this join + ** contains the zName column, then this branch is a no-op. + ** But if there are two or more tables on the left side + ** of the join, construct a coalesce() function that gathers all + ** such tables. Raise an error if more than one of those references + ** to zName is not also within a prior USING clause. + ** + ** We really ought to raise an error if there are two or more + ** non-USING references to zName on the left of an INNER or LEFT + ** JOIN. But older versions of SQLite do not do that, so we avoid + ** adding a new error so as to not break legacy applications. + */ + ExprList *pFuncArgs = 0; /* Arguments to the coalesce() */ + static const Token tkCoalesce = { "coalesce", 8 }; + while( tableAndColumnIndex(pSrc, iLeft+1, i, zName, &iLeft, &iLeftCol, + pRight->fg.isSynthUsing)!=0 ){ + if( pSrc->a[iLeft].fg.isUsing==0 + || sqlcipher_sqlite3IdListIndex(pSrc->a[iLeft].u3.pUsing, zName)<0 + ){ + sqlcipher_sqlite3ErrorMsg(pParse, "ambiguous reference to %s in USING()", + zName); + break; + } + pFuncArgs = sqlcipher_sqlite3ExprListAppend(pParse, pFuncArgs, pE1); + pE1 = sqlcipher_sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol); + sqlcipher_sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol); + } + if( pFuncArgs ){ + pFuncArgs = sqlcipher_sqlite3ExprListAppend(pParse, pFuncArgs, pE1); + pE1 = sqlcipher_sqlite3ExprFunction(pParse, pFuncArgs, &tkCoalesce, 0); + } + } + pE2 = sqlcipher_sqlite3CreateColumnExpr(db, pSrc, i+1, iRightCol); + sqlcipher_sqlite3SrcItemColumnUsed(pRight, iRightCol); + pEq = sqlcipher_sqlite3PExpr(pParse, TK_EQ, pE1, pE2); + assert( pE2!=0 || pEq==0 ); + if( pEq ){ + ExprSetProperty(pEq, joinType); + assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) ); + ExprSetVVAProperty(pEq, EP_NoReduce); + pEq->w.iJoin = pE2->iTable; + } + p->pWhere = sqlcipher_sqlite3ExprAnd(pParse, p->pWhere, pEq); } } + + /* Add the ON clause to the end of the WHERE clause, connected by + ** an AND operator. + */ + else if( pRight->u3.pOn ){ + sqlcipher_sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType); + p->pWhere = sqlcipher_sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn); + pRight->u3.pOn = 0; + pRight->fg.isOn = 1; + } } return 0; } @@ -135404,31 +141781,157 @@ static void codeOffset( } /* -** Add code that will check to make sure the N registers starting at iMem -** form a distinct entry. iTab is a sorting index that holds previously -** seen combinations of the N values. A new entry is made in iTab -** if the current N values are new. +** Add code that will check to make sure the array of registers starting at +** iMem form a distinct entry. This is used by both "SELECT DISTINCT ..." and +** distinct aggregates ("SELECT count(DISTINCT ) ..."). Three strategies +** are available. Which is used depends on the value of parameter eTnctType, +** as follows: ** -** A jump to addrRepeat is made and the N+1 values are popped from the -** stack if the top N elements are not distinct. -*/ -static void codeDistinct( +** WHERE_DISTINCT_UNORDERED/WHERE_DISTINCT_NOOP: +** Build an ephemeral table that contains all entries seen before and +** skip entries which have been seen before. +** +** Parameter iTab is the cursor number of an ephemeral table that must +** be opened before the VM code generated by this routine is executed. +** The ephemeral cursor table is queried for a record identical to the +** record formed by the current array of registers. If one is found, +** jump to VM address addrRepeat. Otherwise, insert a new record into +** the ephemeral cursor and proceed. +** +** The returned value in this case is a copy of parameter iTab. +** +** WHERE_DISTINCT_ORDERED: +** In this case rows are being delivered sorted order. The ephermal +** table is not required. Instead, the current set of values +** is compared against previous row. If they match, the new row +** is not distinct and control jumps to VM address addrRepeat. Otherwise, +** the VM program proceeds with processing the new row. +** +** The returned value in this case is the register number of the first +** in an array of registers used to store the previous result row so that +** it can be compared to the next. The caller must ensure that this +** register is initialized to NULL. (The fixDistinctOpenEph() routine +** will take care of this initialization.) +** +** WHERE_DISTINCT_UNIQUE: +** In this case it has already been determined that the rows are distinct. +** No special action is required. The return value is zero. +** +** Parameter pEList is the list of expressions used to generated the +** contents of each row. It is used by this routine to determine (a) +** how many elements there are in the array of registers and (b) the +** collation sequences that should be used for the comparisons if +** eTnctType is WHERE_DISTINCT_ORDERED. +*/ +static int codeDistinct( Parse *pParse, /* Parsing and code generating context */ + int eTnctType, /* WHERE_DISTINCT_* value */ int iTab, /* A sorting index used to test for distinctness */ int addrRepeat, /* Jump to here if not distinct */ - int N, /* Number of elements */ - int iMem /* First element */ + ExprList *pEList, /* Expression for each element */ + int regElem /* First element */ ){ - Vdbe *v; - int r1; + int iRet = 0; + int nResultCol = pEList->nExpr; + Vdbe *v = pParse->pVdbe; - v = pParse->pVdbe; - r1 = sqlcipher_sqlite3GetTempReg(pParse); - sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); VdbeCoverage(v); - sqlcipher_sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); - sqlcipher_sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r1, iMem, N); - sqlcipher_sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); - sqlcipher_sqlite3ReleaseTempReg(pParse, r1); + switch( eTnctType ){ + case WHERE_DISTINCT_ORDERED: { + int i; + int iJump; /* Jump destination */ + int regPrev; /* Previous row content */ + + /* Allocate space for the previous row */ + iRet = regPrev = pParse->nMem+1; + pParse->nMem += nResultCol; + + iJump = sqlcipher_sqlite3VdbeCurrentAddr(v) + nResultCol; + for(i=0; ia[i].pExpr); + if( idb->mallocFailed ); + sqlcipher_sqlite3VdbeAddOp3(v, OP_Copy, regElem, regPrev, nResultCol-1); + break; + } + + case WHERE_DISTINCT_UNIQUE: { + /* nothing to do */ + break; + } + + default: { + int r1 = sqlcipher_sqlite3GetTempReg(pParse); + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, regElem, nResultCol); + VdbeCoverage(v); + sqlcipher_sqlite3VdbeAddOp3(v, OP_MakeRecord, regElem, nResultCol, r1); + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r1, regElem, nResultCol); + sqlcipher_sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); + sqlcipher_sqlite3ReleaseTempReg(pParse, r1); + iRet = iTab; + break; + } + } + + return iRet; +} + +/* +** This routine runs after codeDistinct(). It makes necessary +** adjustments to the OP_OpenEphemeral opcode that the codeDistinct() +** routine made use of. This processing must be done separately since +** sometimes codeDistinct is called before the OP_OpenEphemeral is actually +** laid down. +** +** WHERE_DISTINCT_NOOP: +** WHERE_DISTINCT_UNORDERED: +** +** No adjustments necessary. This function is a no-op. +** +** WHERE_DISTINCT_UNIQUE: +** +** The ephemeral table is not needed. So change the +** OP_OpenEphemeral opcode into an OP_Noop. +** +** WHERE_DISTINCT_ORDERED: +** +** The ephemeral table is not needed. But we do need register +** iVal to be initialized to NULL. So change the OP_OpenEphemeral +** into an OP_Null on the iVal register. +*/ +static void fixDistinctOpenEph( + Parse *pParse, /* Parsing and code generating context */ + int eTnctType, /* WHERE_DISTINCT_* value */ + int iVal, /* Value returned by codeDistinct() */ + int iOpenEphAddr /* Address of OP_OpenEphemeral instruction for iTab */ +){ + if( pParse->nErr==0 + && (eTnctType==WHERE_DISTINCT_UNIQUE || eTnctType==WHERE_DISTINCT_ORDERED) + ){ + Vdbe *v = pParse->pVdbe; + sqlcipher_sqlite3VdbeChangeToNoop(v, iOpenEphAddr); + if( sqlcipher_sqlite3VdbeGetOp(v, iOpenEphAddr+1)->opcode==OP_Explain ){ + sqlcipher_sqlite3VdbeChangeToNoop(v, iOpenEphAddr+1); + } + if( eTnctType==WHERE_DISTINCT_ORDERED ){ + /* Change the OP_OpenEphemeral to an OP_Null that sets the MEM_Cleared + ** bit on the first register of the previous value. This will cause the + ** OP_Ne added in codeDistinct() to always fail on the first iteration of + ** the loop even if the first row is all NULLs. */ + VdbeOp *pOp = sqlcipher_sqlite3VdbeGetOp(v, iOpenEphAddr); + pOp->opcode = OP_Null; + pOp->p1 = 1; + pOp->p2 = iVal; + } + } } #ifdef SQLITE_ENABLE_SORTER_REFERENCES @@ -135448,7 +141951,7 @@ static void codeDistinct( ** retrieved directly from table t1. If the values are very large, this ** can be more efficient than storing them directly in the sorter records. ** -** The ExprList_item.bSorterRef flag is set for each expression in pEList +** The ExprList_item.fg.bSorterRef flag is set for each expression in pEList ** for which the sorter-reference optimization should be enabled. ** Additionally, the pSort->aDefer[] array is populated with entries ** for all cursors required to evaluate all selected expressions. Finally. @@ -135469,9 +141972,13 @@ static void selectExprDefer( struct ExprList_item *pItem = &pEList->a[i]; if( pItem->u.x.iOrderByCol==0 ){ Expr *pExpr = pItem->pExpr; - Table *pTab = pExpr->y.pTab; - if( pExpr->op==TK_COLUMN && pExpr->iColumn>=0 && pTab && !IsVirtual(pTab) - && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF) + Table *pTab; + if( pExpr->op==TK_COLUMN + && pExpr->iColumn>=0 + && ALWAYS( ExprUseYTab(pExpr) ) + && (pTab = pExpr->y.pTab)!=0 + && IsOrdinaryTable(pTab) + && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF)!=0 ){ int j; for(j=0; jiTable = pExpr->iTable; + assert( ExprUseYTab(pNew) ); pNew->y.pTab = pExpr->y.pTab; pNew->iColumn = pPk ? pPk->aiColumn[k] : -1; pExtra = sqlcipher_sqlite3ExprListAppend(pParse, pExtra, pNew); @@ -135503,7 +142011,7 @@ static void selectExprDefer( nDefer++; } } - pItem->bSorterRef = 1; + pItem->fg.bSorterRef = 1; } } } @@ -135634,7 +142142,7 @@ static void selectInnerLoop( for(i=0; inExpr; i++){ if( pEList->a[i].u.x.iOrderByCol>0 #ifdef SQLITE_ENABLE_SORTER_REFERENCES - || pEList->a[i].bSorterRef + || pEList->a[i].fg.bSorterRef #endif ){ nResultCol--; @@ -135676,59 +142184,11 @@ static void selectInnerLoop( ** part of the result. */ if( hasDistinct ){ - switch( pDistinct->eTnctType ){ - case WHERE_DISTINCT_ORDERED: { - VdbeOp *pOp; /* No longer required OpenEphemeral instr. */ - int iJump; /* Jump destination */ - int regPrev; /* Previous row content */ - - /* Allocate space for the previous row */ - regPrev = pParse->nMem+1; - pParse->nMem += nResultCol; - - /* Change the OP_OpenEphemeral coded earlier to an OP_Null - ** sets the MEM_Cleared bit on the first register of the - ** previous value. This will cause the OP_Ne below to always - ** fail on the first iteration of the loop even if the first - ** row is all NULLs. - */ - sqlcipher_sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct); - pOp = sqlcipher_sqlite3VdbeGetOp(v, pDistinct->addrTnct); - pOp->opcode = OP_Null; - pOp->p1 = 1; - pOp->p2 = regPrev; - pOp = 0; /* Ensure pOp is not used after sqlcipher_sqlite3VdbeAddOp() */ - - iJump = sqlcipher_sqlite3VdbeCurrentAddr(v) + nResultCol; - for(i=0; ipEList->a[i].pExpr); - if( idb->mallocFailed ); - sqlcipher_sqlite3VdbeAddOp3(v, OP_Copy, regResult, regPrev, nResultCol-1); - break; - } - - case WHERE_DISTINCT_UNIQUE: { - sqlcipher_sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct); - break; - } - - default: { - assert( pDistinct->eTnctType==WHERE_DISTINCT_UNORDERED ); - codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol, - regResult); - break; - } - } + int eType = pDistinct->eTnctType; + int iTab = pDistinct->tabTnct; + assert( nResultCol==p->pEList->nExpr ); + iTab = codeDistinct(pParse, eType, iTab, iContinue, p->pEList, regResult); + fixDistinctOpenEph(pParse, eType, iTab, pDistinct->addrTnct); if( pSort==0 ){ codeOffset(v, p->iOffset, iContinue); } @@ -135975,7 +142435,7 @@ SQLITE_PRIVATE KeyInfo *sqlcipher_sqlite3KeyInfoAlloc(sqlcipher_sqlite3 *db, int p->nRef = 1; memset(&p[1], 0, nExtra); }else{ - sqlcipher_sqlite3OomFault(db); + return (KeyInfo*)sqlcipher_sqlite3OomFault(db); } return p; } @@ -136044,7 +142504,7 @@ SQLITE_PRIVATE KeyInfo *sqlcipher_sqlite3KeyInfoFromExprList( assert( sqlcipher_sqlite3KeyInfoIsWriteable(pInfo) ); for(i=iStart, pItem=pList->a+iStart; iaColl[i-iStart] = sqlcipher_sqlite3ExprNNCollSeq(pParse, pItem->pExpr); - pInfo->aSortFlags[i-iStart] = pItem->sortFlags; + pInfo->aSortFlags[i-iStart] = pItem->fg.sortFlags; } } return pInfo; @@ -136053,7 +142513,7 @@ SQLITE_PRIVATE KeyInfo *sqlcipher_sqlite3KeyInfoFromExprList( /* ** Name of the connection operator, used for error messages. */ -static const char *selectOpName(int id){ +SQLITE_PRIVATE const char *sqlcipher_sqlite3SelectOpName(int id){ char *z; switch( id ){ case TK_ALL: z = "UNION ALL"; break; @@ -136146,6 +142606,9 @@ static void generateSortTail( iTab = pSort->iECursor; if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){ + if( eDest==SRT_Mem && p->iOffset ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_Null, 0, pDest->iSdst); + } regRowid = 0; regRow = pDest->iSdst; }else{ @@ -136169,7 +142632,7 @@ static void generateSortTail( if( addrOnce ) sqlcipher_sqlite3VdbeJumpHere(v, addrOnce); addr = 1 + sqlcipher_sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); VdbeCoverage(v); - codeOffset(v, p->iOffset, addrContinue); + assert( p->iLimit==0 && p->iOffset==0 ); sqlcipher_sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab); bSeq = 0; }else{ @@ -136177,10 +142640,13 @@ static void generateSortTail( codeOffset(v, p->iOffset, addrContinue); iSortTab = iTab; bSeq = 1; + if( p->iOffset>0 ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1); + } } for(i=0, iCol=nKey+bSeq-1; i=0; i--){ #ifdef SQLITE_ENABLE_SORTER_REFERENCES - if( aOutEx[i].bSorterRef ){ + if( aOutEx[i].fg.bSorterRef ){ sqlcipher_sqlite3ExprCode(pParse, aOutEx[i].pExpr, regRow+i); }else #endif @@ -136388,13 +142854,19 @@ static const char *columnTypeImpl( break; } - assert( pTab && pExpr->y.pTab==pTab ); + assert( pTab && ExprUseYTab(pExpr) && pExpr->y.pTab==pTab ); if( pS ){ /* The "table" is actually a sub-select or a view in the FROM clause ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ - if( iCol>=0 && iColpEList->nExpr ){ + if( iColpEList->nExpr +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + && iCol>=0 +#else + && ALWAYS(iCol>=0) +#endif + ){ /* If iCol is less than zero, then the expression requests the ** rowid of the sub-select or view. This expression is legal (see ** test case misc2.2.2) - it always evaluates to NULL. @@ -136416,7 +142888,7 @@ static const char *columnTypeImpl( zType = "INTEGER"; zOrigCol = "rowid"; }else{ - zOrigCol = pTab->aCol[iCol].zName; + zOrigCol = pTab->aCol[iCol].zCnName; zType = sqlcipher_sqlite3ColumnType(&pTab->aCol[iCol],0); } zOrigTab = pTab->zName; @@ -136442,9 +142914,11 @@ static const char *columnTypeImpl( ** statement. */ NameContext sNC; - Select *pS = pExpr->x.pSelect; - Expr *p = pS->pEList->a[0].pExpr; - assert( ExprHasProperty(pExpr, EP_xIsSelect) ); + Select *pS; + Expr *p; + assert( ExprUseXSelect(pExpr) ); + pS = pExpr->x.pSelect; + p = pS->pEList->a[0].pExpr; sNC.pSrcList = pS->pSrc; sNC.pNext = pNC; sNC.pParse = pNC->pParse; @@ -136536,7 +143010,7 @@ static void generateColumnTypes( ** then the result column name with the table name ** prefix, ex: TABLE.COLUMN. Otherwise use zSpan. */ -static void generateColumnNames( +SQLITE_PRIVATE void sqlcipher_sqlite3GenerateColumnNames( Parse *pParse, /* Parser context */ Select *pSelect /* Generate column names for this SELECT statement */ ){ @@ -136573,8 +143047,9 @@ static void generateColumnNames( assert( p!=0 ); assert( p->op!=TK_AGG_COLUMN ); /* Agg processing has not run yet */ - assert( p->op!=TK_COLUMN || p->y.pTab!=0 ); /* Covering idx not yet coded */ - if( pEList->a[i].zEName && pEList->a[i].eEName==ENAME_NAME ){ + assert( p->op!=TK_COLUMN + || (ExprUseYTab(p) && p->y.pTab!=0) ); /* Covering idx not yet coded */ + if( pEList->a[i].zEName && pEList->a[i].fg.eEName==ENAME_NAME ){ /* An AS clause always takes first priority */ char *zName = pEList->a[i].zEName; sqlcipher_sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT); @@ -136588,7 +143063,7 @@ static void generateColumnNames( if( iCol<0 ){ zCol = "rowid"; }else{ - zCol = pTab->aCol[iCol].zName; + zCol = pTab->aCol[iCol].zCnName; } if( fullName ){ char *zName = 0; @@ -136626,7 +143101,7 @@ static void generateColumnNames( ** and will break if those assumptions changes. Hence, use extreme caution ** when modifying this routine to avoid breaking legacy. ** -** See Also: generateColumnNames() +** See Also: sqlcipher_sqlite3GenerateColumnNames() */ SQLITE_PRIVATE int sqlcipher_sqlite3ColumnsFromExprList( Parse *pParse, /* Parsing context */ @@ -136649,7 +143124,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ColumnsFromExprList( nCol = pEList->nExpr; aCol = sqlcipher_sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); testcase( aCol==0 ); - if( nCol>32767 ) nCol = 32767; + if( NEVER(nCol>32767) ) nCol = 32767; }else{ nCol = 0; aCol = 0; @@ -136659,27 +143134,33 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ColumnsFromExprList( *paCol = aCol; for(i=0, pCol=aCol; imallocFailed; i++, pCol++){ + struct ExprList_item *pX = &pEList->a[i]; + struct ExprList_item *pCollide; /* Get an appropriate name for the column */ - if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){ + if( (zName = pX->zEName)!=0 && pX->fg.eEName==ENAME_NAME ){ /* If the column contains an "AS " phrase, use as the name */ }else{ - Expr *pColExpr = sqlcipher_sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr); + Expr *pColExpr = sqlcipher_sqlite3ExprSkipCollateAndLikely(pX->pExpr); while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){ pColExpr = pColExpr->pRight; assert( pColExpr!=0 ); } - if( pColExpr->op==TK_COLUMN && (pTab = pColExpr->y.pTab)!=0 ){ + if( pColExpr->op==TK_COLUMN + && ALWAYS( ExprUseYTab(pColExpr) ) + && ALWAYS( pColExpr->y.pTab!=0 ) + ){ /* For columns use the column name name */ int iCol = pColExpr->iColumn; + pTab = pColExpr->y.pTab; if( iCol<0 ) iCol = pTab->iPKey; - zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; + zName = iCol>=0 ? pTab->aCol[iCol].zCnName : "rowid"; }else if( pColExpr->op==TK_ID ){ assert( !ExprHasProperty(pColExpr, EP_IntValue) ); zName = pColExpr->u.zToken; }else{ /* Use the original text of the column expression as its name */ - zName = pEList->a[i].zEName; + assert( zName==pX->zEName ); /* pointer comparison intended */ } } if( zName && !sqlcipher_sqlite3IsTrueOrFalse(zName) ){ @@ -136692,7 +143173,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ColumnsFromExprList( ** append an integer to the name so that it becomes unique. */ cnt = 0; - while( zName && sqlcipher_sqlite3HashFind(&ht, zName)!=0 ){ + while( zName && (pCollide = sqlcipher_sqlite3HashFind(&ht, zName))!=0 ){ + if( pCollide->fg.bUsingTerm ){ + pCol->colFlags |= COLFLAG_NOEXPAND; + } nName = sqlcipher_sqlite3Strlen30(zName); if( nName>0 ){ for(j=nName-1; j>0 && sqlcipher_sqlite3Isdigit(zName[j]); j--){} @@ -136701,17 +143185,20 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ColumnsFromExprList( zName = sqlcipher_sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt); if( cnt>3 ) sqlcipher_sqlite3_randomness(sizeof(cnt), &cnt); } - pCol->zName = zName; + pCol->zCnName = zName; pCol->hName = sqlcipher_sqlite3StrIHash(zName); + if( pX->fg.bNoExpand ){ + pCol->colFlags |= COLFLAG_NOEXPAND; + } sqlcipher_sqlite3ColumnPropertiesFromName(0, pCol); - if( zName && sqlcipher_sqlite3HashInsert(&ht, zName, pCol)==pCol ){ + if( zName && sqlcipher_sqlite3HashInsert(&ht, zName, pX)==pX ){ sqlcipher_sqlite3OomFault(db); } } sqlcipher_sqlite3HashClear(&ht); if( db->mallocFailed ){ for(j=0; jpEList->a; for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ const char *zType; - int n, m; + i64 n, m; + pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT); p = a[i].pExpr; zType = columnType(&sNC, p, 0, 0, 0); /* pCol->szEst = ... // Column size est for SELECT tables never used */ pCol->affinity = sqlcipher_sqlite3ExprAffinity(p); if( zType ){ m = sqlcipher_sqlite3Strlen30(zType); - n = sqlcipher_sqlite3Strlen30(pCol->zName); - pCol->zName = sqlcipher_sqlite3DbReallocOrFree(db, pCol->zName, n+m+2); - if( pCol->zName ){ - memcpy(&pCol->zName[n+1], zType, m+1); + n = sqlcipher_sqlite3Strlen30(pCol->zCnName); + pCol->zCnName = sqlcipher_sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2); + if( pCol->zCnName ){ + memcpy(&pCol->zCnName[n+1], zType, m+1); pCol->colFlags |= COLFLAG_HASTYPE; + }else{ + testcase( pCol->colFlags & COLFLAG_HASTYPE ); + pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); } } if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff; pColl = sqlcipher_sqlite3ExprCollSeq(pParse, p); - if( pColl && pCol->zColl==0 ){ - pCol->zColl = sqlcipher_sqlite3DbStrDup(db, pColl->zName); + if( pColl ){ + assert( pTab->pIndex==0 ); + sqlcipher_sqlite3ColumnSetColl(db, pCol, pColl->zName); } } pTab->szTabRow = 1; /* Any non-zero value works */ @@ -136936,7 +143428,7 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){ */ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ ExprList *pOrderBy = p->pOrderBy; - int nOrderBy = p->pOrderBy->nExpr; + int nOrderBy = ALWAYS(pOrderBy!=0) ? pOrderBy->nExpr : 0; sqlcipher_sqlite3 *db = pParse->db; KeyInfo *pRet = sqlcipher_sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1); if( pRet ){ @@ -136956,7 +143448,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ } assert( sqlcipher_sqlite3KeyInfoIsWriteable(pRet) ); pRet->aColl[i] = pColl; - pRet->aSortFlags[i] = pOrderBy->a[i].sortFlags; + pRet->aSortFlags[i] = pOrderBy->a[i].fg.sortFlags; } } @@ -137008,7 +143500,7 @@ static void generateWithRecursiveQuery( SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */ int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */ Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */ - Select *pSetup = p->pPrior; /* The setup query */ + Select *pSetup; /* The setup query */ Select *pFirstRec; /* Left-most recursive term */ int addrTop; /* Top of the loop */ int addrCont, addrBreak; /* CONTINUE and BREAK addresses */ @@ -137092,7 +143584,6 @@ static void generateWithRecursiveQuery( ** iDistinct table. pFirstRec is left pointing to the left-most ** recursive term of the CTE. */ - pFirstRec = p; for(pFirstRec=p; ALWAYS(pFirstRec!=0); pFirstRec=pFirstRec->pPrior){ if( pFirstRec->selFlags & SF_Aggregate ){ sqlcipher_sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported"); @@ -137175,7 +143666,7 @@ static int multiSelectOrderBy( ** The "LIMIT of exactly 1" case of condition (1) comes about when a VALUES ** clause occurs within scalar expression (ex: "SELECT (VALUES(1),(2),(3))"). ** The sqlcipher_sqlite3CodeSubselect will have added the LIMIT 1 clause in tht case. -** Since the limit is exactly 1, we only need to evalutes the left-most VALUES. +** Since the limit is exactly 1, we only need to evaluate the left-most VALUES. */ static int multiSelectValues( Parse *pParse, /* Parsing context */ @@ -137271,12 +143762,8 @@ static int multiSelect( db = pParse->db; pPrior = p->pPrior; dest = *pDest; - if( pPrior->pOrderBy || pPrior->pLimit ){ - sqlcipher_sqlite3ErrorMsg(pParse,"%s clause should come after %s not before", - pPrior->pOrderBy!=0 ? "ORDER BY" : "LIMIT", selectOpName(p->op)); - rc = 1; - goto multi_select_end; - } + assert( pPrior->pOrderBy==0 ); + assert( pPrior->pLimit==0 ); v = sqlcipher_sqlite3GetVdbe(pParse); assert( v!=0 ); /* The VDBE already created by calling function */ @@ -137327,13 +143814,14 @@ static int multiSelect( switch( p->op ){ case TK_ALL: { int addr = 0; - int nLimit; + int nLimit = 0; /* Initialize to suppress harmless compiler warning */ assert( !pPrior->pLimit ); pPrior->iLimit = p->iLimit; pPrior->iOffset = p->iOffset; pPrior->pLimit = p->pLimit; + SELECTTRACE(1, pParse, p, ("multiSelect UNION ALL left...\n")); rc = sqlcipher_sqlite3Select(pParse, pPrior, &dest); - p->pLimit = 0; + pPrior->pLimit = 0; if( rc ){ goto multi_select_end; } @@ -137349,13 +143837,14 @@ static int multiSelect( } } ExplainQueryPlan((pParse, 1, "UNION ALL")); + SELECTTRACE(1, pParse, p, ("multiSelect UNION ALL right...\n")); rc = sqlcipher_sqlite3Select(pParse, p, &dest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; p->pPrior = pPrior; p->nSelectRow = sqlcipher_sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); - if( pPrior->pLimit - && sqlcipher_sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit) + if( p->pLimit + && sqlcipher_sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit) && nLimit>0 && p->nSelectRow > sqlcipher_sqlite3LogEst((u64)nLimit) ){ p->nSelectRow = sqlcipher_sqlite3LogEst((u64)nLimit); @@ -137401,6 +143890,7 @@ static int multiSelect( */ assert( !pPrior->pOrderBy ); sqlcipher_sqlite3SelectDestInit(&uniondest, priorOp, unionTab); + SELECTTRACE(1, pParse, p, ("multiSelect EXCEPT/UNION left...\n")); rc = sqlcipher_sqlite3Select(pParse, pPrior, &uniondest); if( rc ){ goto multi_select_end; @@ -137419,7 +143909,8 @@ static int multiSelect( p->pLimit = 0; uniondest.eDest = op; ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", - selectOpName(p->op))); + sqlcipher_sqlite3SelectOpName(p->op))); + SELECTTRACE(1, pParse, p, ("multiSelect EXCEPT/UNION right...\n")); rc = sqlcipher_sqlite3Select(pParse, p, &uniondest); testcase( rc!=SQLITE_OK ); assert( p->pOrderBy==0 ); @@ -137480,6 +143971,7 @@ static int multiSelect( /* Code the SELECTs to our left into temporary table "tab1". */ sqlcipher_sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); + SELECTTRACE(1, pParse, p, ("multiSelect INTERSECT left...\n")); rc = sqlcipher_sqlite3Select(pParse, pPrior, &intersectdest); if( rc ){ goto multi_select_end; @@ -137495,7 +143987,8 @@ static int multiSelect( p->pLimit = 0; intersectdest.iSDParm = tab2; ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", - selectOpName(p->op))); + sqlcipher_sqlite3SelectOpName(p->op))); + SELECTTRACE(1, pParse, p, ("multiSelect INTERSECT right...\n")); rc = sqlcipher_sqlite3Select(pParse, p, &intersectdest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; @@ -137556,6 +144049,7 @@ static int multiSelect( int nCol; /* Number of columns in result set */ assert( p->pNext==0 ); + assert( p->pEList!=0 ); nCol = p->pEList->nExpr; pKeyInfo = sqlcipher_sqlite3KeyInfoAlloc(db, nCol, 1); if( !pKeyInfo ){ @@ -137590,7 +144084,11 @@ static int multiSelect( multi_select_end: pDest->iSdst = dest.iSdst; pDest->nSdst = dest.nSdst; - sqlcipher_sqlite3SelectDelete(db, pDelete); + if( pDelete ){ + sqlcipher_sqlite3ParserAddCleanup(pParse, + (void(*)(sqlcipher_sqlite3*,void*))sqlcipher_sqlite3SelectDelete, + pDelete); + } return rc; } #endif /* SQLITE_OMIT_COMPOUND_SELECT */ @@ -137604,7 +144102,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3SelectWrongNumTermsError(Parse *pParse, Sel sqlcipher_sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); }else{ sqlcipher_sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" - " do not have the same number of result columns", selectOpName(p->op)); + " do not have the same number of result columns", + sqlcipher_sqlite3SelectOpName(p->op)); } } @@ -137701,10 +144200,8 @@ static int generateOutputSubroutine( ** if it is the RHS of a row-value IN operator. */ case SRT_Mem: { - if( pParse->nErr==0 ){ - testcase( pIn->nSdst>1 ); - sqlcipher_sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst); - } + testcase( pIn->nSdst>1 ); + sqlcipher_sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst); /* The LIMIT clause will jump out of the loop for us */ break; } @@ -137845,6 +144342,8 @@ static int multiSelectOrderBy( ){ int i, j; /* Loop counters */ Select *pPrior; /* Another SELECT immediately to our left */ + Select *pSplit; /* Left-most SELECT in the right-hand group */ + int nSelect; /* Number of SELECT statements in the compound */ Vdbe *v; /* Generate code to this VDBE */ SelectDest destA; /* Destination for coroutine A */ SelectDest destB; /* Destination for coroutine B */ @@ -137890,8 +144389,7 @@ static int multiSelectOrderBy( /* Patch up the ORDER BY clause */ op = p->op; - pPrior = p->pPrior; - assert( pPrior->pOrderBy==0 ); + assert( p->pPrior->pOrderBy==0 ); pOrderBy = p->pOrderBy; assert( pOrderBy ); nOrderBy = pOrderBy->nExpr; @@ -137904,6 +144402,7 @@ static int multiSelectOrderBy( for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){ struct ExprList_item *pItem; for(j=0, pItem=pOrderBy->a; ju.x.iOrderByCol>0 ); if( pItem->u.x.iOrderByCol==i ) break; } @@ -137930,6 +144429,7 @@ static int multiSelectOrderBy( struct ExprList_item *pItem; aPermute[0] = nOrderBy; for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ + assert( pItem!=0 ); assert( pItem->u.x.iOrderByCol>0 ); assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ); aPermute[i] = pItem->u.x.iOrderByCol - 1; @@ -137939,11 +144439,6 @@ static int multiSelectOrderBy( pKeyMerge = 0; } - /* Reattach the ORDER BY clause to the query. - */ - p->pOrderBy = pOrderBy; - pPrior->pOrderBy = sqlcipher_sqlite3ExprListDup(pParse->db, pOrderBy, 0); - /* Allocate a range of temporary registers and the KeyInfo needed ** for the logic that removes duplicate result rows when the ** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL). @@ -137968,12 +144463,30 @@ static int multiSelectOrderBy( /* Separate the left and the right query from one another */ - p->pPrior = 0; + nSelect = 1; + if( (op==TK_ALL || op==TK_UNION) + && OptimizationEnabled(db, SQLITE_BalancedMerge) + ){ + for(pSplit=p; pSplit->pPrior!=0 && pSplit->op==op; pSplit=pSplit->pPrior){ + nSelect++; + assert( pSplit->pPrior->pNext==pSplit ); + } + } + if( nSelect<=3 ){ + pSplit = p; + }else{ + pSplit = p; + for(i=2; ipPrior; } + } + pPrior = pSplit->pPrior; + assert( pPrior!=0 ); + pSplit->pPrior = 0; pPrior->pNext = 0; + assert( p->pOrderBy == pOrderBy ); + assert( pOrderBy!=0 || db->mallocFailed ); + pPrior->pOrderBy = sqlcipher_sqlite3ExprListDup(pParse->db, pOrderBy, 0); sqlcipher_sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER"); - if( pPrior->pPrior==0 ){ - sqlcipher_sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); - } + sqlcipher_sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); /* Compute the limit registers */ computeLimitRegisters(pParse, p, labelEnd); @@ -137996,7 +144509,7 @@ static int multiSelectOrderBy( sqlcipher_sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA); sqlcipher_sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB); - ExplainQueryPlan((pParse, 1, "MERGE (%s)", selectOpName(p->op))); + ExplainQueryPlan((pParse, 1, "MERGE (%s)", sqlcipher_sqlite3SelectOpName(p->op))); /* Generate a coroutine to evaluate the SELECT statement to the ** left of the compound operator - the "A" select. @@ -138122,13 +144635,16 @@ static int multiSelectOrderBy( */ sqlcipher_sqlite3VdbeResolveLabel(v, labelEnd); - /* Reassembly the compound query so that it will be freed correctly + /* Reassemble the compound query so that it will be freed correctly ** by the calling function */ - if( p->pPrior ){ - sqlcipher_sqlite3SelectDelete(db, p->pPrior); + if( pSplit->pPrior ){ + sqlcipher_sqlite3ParserAddCleanup(pParse, + (void(*)(sqlcipher_sqlite3*,void*))sqlcipher_sqlite3SelectDelete, pSplit->pPrior); } - p->pPrior = pPrior; - pPrior->pNext = p; + pSplit->pPrior = pPrior; + pPrior->pNext = pSplit; + sqlcipher_sqlite3ExprListDelete(db, pPrior->pOrderBy); + pPrior->pOrderBy = 0; /*** TBD: Insert subroutine calls to close cursors on incomplete **** subqueries ****/ @@ -138144,12 +144660,40 @@ static int multiSelectOrderBy( ** ** All references to columns in table iTable are to be replaced by corresponding ** expressions in pEList. +** +** ## About "isOuterJoin": +** +** The isOuterJoin column indicates that the replacement will occur into a +** position in the parent that NULL-able due to an OUTER JOIN. Either the +** target slot in the parent is the right operand of a LEFT JOIN, or one of +** the left operands of a RIGHT JOIN. In either case, we need to potentially +** bypass the substituted expression with OP_IfNullRow. +** +** Suppose the original expression integer constant. Even though the table +** has the nullRow flag set, because the expression is an integer constant, +** it will not be NULLed out. So instead, we insert an OP_IfNullRow opcode +** that checks to see if the nullRow flag is set on the table. If the nullRow +** flag is set, then the value in the register is set to NULL and the original +** expression is bypassed. If the nullRow flag is not set, then the original +** expression runs to populate the register. +** +** Example where this is needed: +** +** CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT); +** CREATE TABLE t2(x INT UNIQUE); +** +** SELECT a,b,m,x FROM t1 LEFT JOIN (SELECT 59 AS m,x FROM t2) ON b=x; +** +** When the subquery on the right side of the LEFT JOIN is flattened, we +** have to add OP_IfNullRow in front of the OP_Integer that implements the +** "m" value of the subquery so that a NULL will be loaded instead of 59 +** when processing a non-matched row of the left. */ typedef struct SubstContext { Parse *pParse; /* The parsing context */ int iTable; /* Replace references to this table */ int iNewTable; /* New table number */ - int isLeftJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */ + int isOuterJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */ ExprList *pEList; /* Replacement expressions */ } SubstContext; @@ -138175,18 +144719,22 @@ static Expr *substExpr( Expr *pExpr /* Expr in which substitution occurs */ ){ if( pExpr==0 ) return 0; - if( ExprHasProperty(pExpr, EP_FromJoin) - && pExpr->iRightJoinTable==pSubst->iTable + if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) + && pExpr->w.iJoin==pSubst->iTable ){ - pExpr->iRightJoinTable = pSubst->iNewTable; + testcase( ExprHasProperty(pExpr, EP_InnerON) ); + pExpr->w.iJoin = pSubst->iNewTable; } if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable && !ExprHasProperty(pExpr, EP_FixedCol) ){ +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW if( pExpr->iColumn<0 ){ pExpr->op = TK_NULL; - }else{ + }else +#endif + { Expr *pNew; Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr; Expr ifNullRow; @@ -138196,7 +144744,7 @@ static Expr *substExpr( sqlcipher_sqlite3VectorErrorMsg(pSubst->pParse, pCopy); }else{ sqlcipher_sqlite3 *db = pSubst->pParse->db; - if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){ + if( pSubst->isOuterJoin && pCopy->op!=TK_COLUMN ){ memset(&ifNullRow, 0, sizeof(ifNullRow)); ifNullRow.op = TK_IF_NULL_ROW; ifNullRow.pLeft = pCopy; @@ -138206,26 +144754,34 @@ static Expr *substExpr( } testcase( ExprHasProperty(pCopy, EP_Subquery) ); pNew = sqlcipher_sqlite3ExprDup(db, pCopy, 0); - if( pNew && pSubst->isLeftJoin ){ + if( db->mallocFailed ){ + sqlcipher_sqlite3ExprDelete(db, pNew); + return pExpr; + } + if( pSubst->isOuterJoin ){ ExprSetProperty(pNew, EP_CanBeNull); } - if( pNew && ExprHasProperty(pExpr,EP_FromJoin) ){ - sqlcipher_sqlite3SetJoinExpr(pNew, pExpr->iRightJoinTable); + if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){ + sqlcipher_sqlite3SetJoinExpr(pNew, pExpr->w.iJoin, + pExpr->flags & (EP_OuterON|EP_InnerON)); } sqlcipher_sqlite3ExprDelete(db, pExpr); pExpr = pNew; + if( pExpr->op==TK_TRUEFALSE ){ + pExpr->u.iValue = sqlcipher_sqlite3ExprTruthValue(pExpr); + pExpr->op = TK_INTEGER; + ExprSetProperty(pExpr, EP_IntValue); + } /* Ensure that the expression now has an implicit collation sequence, ** just as it did when it was a column of a view or sub-query. */ - if( pExpr ){ - if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){ - CollSeq *pColl = sqlcipher_sqlite3ExprCollSeq(pSubst->pParse, pExpr); - pExpr = sqlcipher_sqlite3ExprAddCollateString(pSubst->pParse, pExpr, - (pColl ? pColl->zName : "BINARY") - ); - } - ExprClearProperty(pExpr, EP_Collate); + if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){ + CollSeq *pColl = sqlcipher_sqlite3ExprCollSeq(pSubst->pParse, pExpr); + pExpr = sqlcipher_sqlite3ExprAddCollateString(pSubst->pParse, pExpr, + (pColl ? pColl->zName : "BINARY") + ); } + ExprClearProperty(pExpr, EP_Collate); } } }else{ @@ -138234,7 +144790,7 @@ static Expr *substExpr( } pExpr->pLeft = substExpr(pSubst, pExpr->pLeft); pExpr->pRight = substExpr(pSubst, pExpr->pRight); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ substSelect(pSubst, pExpr->x.pSelect, 1); }else{ substExprList(pSubst, pExpr->x.pList); @@ -138266,7 +144822,7 @@ static void substSelect( int doPrior /* Do substitutes on p->pPrior too */ ){ SrcList *pSrc; - struct SrcList_item *pItem; + SrcItem *pItem; int i; if( !p ) return; do{ @@ -138296,7 +144852,7 @@ static void substSelect( ** pSrcItem->colUsed mask. */ static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){ - struct SrcList_item *pItem; + SrcItem *pItem; if( pExpr->op!=TK_COLUMN ) return WRC_Continue; pItem = pWalker->u.pSrcItem; if( pItem->iCursor!=pExpr->iTable ) return WRC_Continue; @@ -138306,7 +144862,7 @@ static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){ } static void recomputeColumnsUsed( Select *pSelect, /* The complete SELECT statement */ - struct SrcList_item *pSrcItem /* Which FROM clause item to recompute */ + SrcItem *pSrcItem /* Which FROM clause item to recompute */ ){ Walker w; if( NEVER(pSrcItem->pTab==0) ) return; @@ -138319,6 +144875,103 @@ static void recomputeColumnsUsed( } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ +#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) +/* +** Assign new cursor numbers to each of the items in pSrc. For each +** new cursor number assigned, set an entry in the aCsrMap[] array +** to map the old cursor number to the new: +** +** aCsrMap[iOld+1] = iNew; +** +** The array is guaranteed by the caller to be large enough for all +** existing cursor numbers in pSrc. aCsrMap[0] is the array size. +** +** If pSrc contains any sub-selects, call this routine recursively +** on the FROM clause of each such sub-select, with iExcept set to -1. +*/ +static void srclistRenumberCursors( + Parse *pParse, /* Parse context */ + int *aCsrMap, /* Array to store cursor mappings in */ + SrcList *pSrc, /* FROM clause to renumber */ + int iExcept /* FROM clause item to skip */ +){ + int i; + SrcItem *pItem; + for(i=0, pItem=pSrc->a; inSrc; i++, pItem++){ + if( i!=iExcept ){ + Select *p; + assert( pItem->iCursor < aCsrMap[0] ); + if( !pItem->fg.isRecursive || aCsrMap[pItem->iCursor+1]==0 ){ + aCsrMap[pItem->iCursor+1] = pParse->nTab++; + } + pItem->iCursor = aCsrMap[pItem->iCursor+1]; + for(p=pItem->pSelect; p; p=p->pPrior){ + srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1); + } + } + } +} + +/* +** *piCursor is a cursor number. Change it if it needs to be mapped. +*/ +static void renumberCursorDoMapping(Walker *pWalker, int *piCursor){ + int *aCsrMap = pWalker->u.aiCol; + int iCsr = *piCursor; + if( iCsr < aCsrMap[0] && aCsrMap[iCsr+1]>0 ){ + *piCursor = aCsrMap[iCsr+1]; + } +} + +/* +** Expression walker callback used by renumberCursors() to update +** Expr objects to match newly assigned cursor numbers. +*/ +static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){ + int op = pExpr->op; + if( op==TK_COLUMN || op==TK_IF_NULL_ROW ){ + renumberCursorDoMapping(pWalker, &pExpr->iTable); + } + if( ExprHasProperty(pExpr, EP_OuterON) ){ + renumberCursorDoMapping(pWalker, &pExpr->w.iJoin); + } + return WRC_Continue; +} + +/* +** Assign a new cursor number to each cursor in the FROM clause (Select.pSrc) +** of the SELECT statement passed as the second argument, and to each +** cursor in the FROM clause of any FROM clause sub-selects, recursively. +** Except, do not assign a new cursor number to the iExcept'th element in +** the FROM clause of (*p). Update all expressions and other references +** to refer to the new cursor numbers. +** +** Argument aCsrMap is an array that may be used for temporary working +** space. Two guarantees are made by the caller: +** +** * the array is larger than the largest cursor number used within the +** select statement passed as an argument, and +** +** * the array entries for all cursor numbers that do *not* appear in +** FROM clauses of the select statement as described above are +** initialized to zero. +*/ +static void renumberCursors( + Parse *pParse, /* Parse context */ + Select *p, /* Select to renumber cursors within */ + int iExcept, /* FROM clause item to skip */ + int *aCsrMap /* Working space */ +){ + Walker w; + srclistRenumberCursors(pParse, aCsrMap, p->pSrc, iExcept); + memset(&w, 0, sizeof(w)); + w.u.aiCol = aCsrMap; + w.xExprCallback = renumberCursorsCb; + w.xSelectCallback = sqlcipher_sqlite3SelectWalkNoop; + sqlcipher_sqlite3WalkSelect(&w, p); +} +#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ + #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* ** This routine attempts to flatten subqueries as a performance optimization. @@ -138365,6 +145018,7 @@ static void recomputeColumnsUsed( ** table and ** (3c) the outer query may not be an aggregate. ** (3d) the outer query may not be DISTINCT. +** See also (26) for restrictions on RIGHT JOIN. ** ** (4) The subquery can not be DISTINCT. ** @@ -138413,9 +145067,12 @@ static void recomputeColumnsUsed( ** (17c) every term within the subquery compound must have a FROM clause ** (17d) the outer query may not be ** (17d1) aggregate, or -** (17d2) DISTINCT, or -** (17d3) a join. -** (17e) the subquery may not contain window functions +** (17d2) DISTINCT +** (17e) the subquery may not contain window functions, and +** (17f) the subquery must not be the RHS of a LEFT JOIN. +** (17g) either the subquery is the first element of the outer +** query or there are no RIGHT or FULL JOINs in any arm +** of the subquery. (This is a duplicate of condition (27b).) ** ** The parent and sub-query may contain WHERE clauses. Subject to ** rules (11), (13) and (14), they may also contain ORDER BY, @@ -138431,8 +145088,8 @@ static void recomputeColumnsUsed( ** syntax error and return a detailed message. ** ** (18) If the sub-query is a compound select, then all terms of the -** ORDER BY clause of the parent must be simple references to -** columns of the sub-query. +** ORDER BY clause of the parent must be copies of a term returned +** by the parent query. ** ** (19) If the subquery uses LIMIT then the outer query may not ** have a WHERE clause. @@ -138448,9 +145105,8 @@ static void recomputeColumnsUsed( ** ** (22) The subquery may not be a recursive CTE. ** -** (**) Subsumed into restriction (17d3). Was: If the outer query is -** a recursive CTE, then the sub-query may not be a compound query. -** This restriction is because transforming the +** (23) If the outer query is a recursive CTE, then the sub-query may not be +** a compound query. This restriction is because transforming the ** parent to a compound query confuses the code that handles ** recursive queries in multiSelect(). ** @@ -138464,6 +145120,23 @@ static void recomputeColumnsUsed( ** function in the select list or ORDER BY clause, flattening ** is not attempted. ** +** (26) The subquery may not be the right operand of a RIGHT JOIN. +** See also (3) for restrictions on LEFT JOIN. +** +** (27) The subquery may not contain a FULL or RIGHT JOIN unless it +** is the first element of the parent query. This must be the +** the case if: +** (27a) the subquery is not compound query, and +** (27b) the subquery is a compound query and the RIGHT JOIN occurs +** in any arm of the compound query. (See also (17g).) +** +** (28) The subquery is not a MATERIALIZED CTE. +** +** (29) Either the subquery is not the right-hand operand of a join with an +** ON or USING clause nor the right-hand operand of a NATURAL JOIN, or +** the right-most table within the FROM clause of the subquery +** is not part of an outer join. +** ** ** In this routine, the "p" parameter is a pointer to the outer query. ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query @@ -138489,12 +145162,13 @@ static int flattenSubquery( SrcList *pSubSrc; /* The FROM clause of the subquery */ int iParent; /* VDBE cursor number of the pSub result set temp table */ int iNewParent = -1;/* Replacement table for iParent */ - int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */ + int isOuterJoin = 0; /* True if pSub is the right side of a LEFT JOIN */ int i; /* Loop counter */ Expr *pWhere; /* The WHERE clause */ - struct SrcList_item *pSubitem; /* The subquery */ + SrcItem *pSubitem; /* The subquery */ sqlcipher_sqlite3 *db = pParse->db; Walker w; /* Walker to persist agginfo data */ + int *aCsrMap = 0; /* Check to see if flattening is permitted. Return 0 if not. */ @@ -138561,26 +145235,64 @@ static int flattenSubquery( ** ** See also tickets #306, #350, and #3300. */ - if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){ - isLeftJoin = 1; - if( pSubSrc->nSrc>1 /* (3a) */ - || isAgg /* (3b) */ - || IsVirtual(pSubSrc->a[0].pTab) /* (3c) */ - || (p->selFlags & SF_Distinct)!=0 /* (3d) */ + if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){ + if( pSubSrc->nSrc>1 /* (3a) */ + || isAgg /* (3c) */ + || IsVirtual(pSubSrc->a[0].pTab) /* (3b) */ + || (p->selFlags & SF_Distinct)!=0 /* (3d) */ + || (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */ ){ return 0; } + isOuterJoin = 1; } #ifdef SQLITE_EXTRA_IFNULLROW else if( iFrom>0 && !isAgg ){ - /* Setting isLeftJoin to -1 causes OP_IfNullRow opcodes to be generated for + /* Setting isOuterJoin to -1 causes OP_IfNullRow opcodes to be generated for ** every reference to any result column from subquery in a join, even ** though they are not necessary. This will stress-test the OP_IfNullRow ** opcode. */ - isLeftJoin = -1; + isOuterJoin = -1; } #endif + assert( pSubSrc->nSrc>0 ); /* True by restriction (7) */ + if( iFrom>0 && (pSubSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ + return 0; /* Restriction (27a) */ + } + if( pSubitem->fg.isCte && pSubitem->u2.pCteUse->eM10d==M10d_Yes ){ + return 0; /* (28) */ + } + + /* Restriction (29): + ** + ** We do not want two constraints on the same term of the flattened + ** query where one constraint has EP_InnerON and the other is EP_OuterON. + ** To prevent this, one or the other of the following conditions must be + ** false: + ** + ** (29a) The right-most entry in the FROM clause of the subquery + ** must not be part of an outer join. + ** + ** (29b) The subquery itself must not be the right operand of a + ** NATURAL join or a join that as an ON or USING clause. + ** + ** These conditions are sufficient to keep an EP_OuterON from being + ** flattened into an EP_InnerON. Restrictions (3a) and (27a) prevent + ** an EP_InnerON from being flattened into an EP_OuterON. + */ + if( pSubSrc->nSrc>=2 + && (pSubSrc->a[pSubSrc->nSrc-1].fg.jointype & JT_OUTER)!=0 + ){ + if( (pSubitem->fg.jointype & JT_NATURAL)!=0 + || pSubitem->fg.isUsing + || NEVER(pSubitem->u3.pOn!=0) /* ON clause already shifted into WHERE */ + || pSubitem->fg.isOn + ){ + return 0; + } + } + /* Restriction (17): If the sub-query is a compound SELECT, then it must ** use only the UNION ALL operator. And none of the simple select queries ** that make up the compound SELECT are allowed to be aggregate or distinct @@ -138590,13 +145302,14 @@ static int flattenSubquery( if( pSub->pOrderBy ){ return 0; /* Restriction (20) */ } - if( isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){ - return 0; /* (17d1), (17d2), or (17d3) */ + if( isAgg || (p->selFlags & SF_Distinct)!=0 || isOuterJoin>0 ){ + return 0; /* (17d1), (17d2), or (17f) */ } for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){ testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ); testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate ); assert( pSub->pSrc!=0 ); + assert( (pSub->selFlags & SF_Recursive)==0 ); assert( pSub->pEList->nExpr==pSub1->pEList->nExpr ); if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0 /* (17b) */ || (pSub1->pPrior && pSub1->op!=TK_ALL) /* (17a) */ @@ -138607,6 +145320,12 @@ static int flattenSubquery( ){ return 0; } + if( iFrom>0 && (pSub1->pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ + /* Without this restriction, the JT_LTORJ flag would end up being + ** omitted on left-hand tables of the right join that is being + ** flattened. */ + return 0; /* Restrictions (17g), (27b) */ + } testcase( pSub1->pSrc->nSrc>1 ); } @@ -138617,15 +145336,17 @@ static int flattenSubquery( if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0; } } - } - /* Ex-restriction (23): - ** The only way that the recursive part of a CTE can contain a compound - ** subquery is for the subquery to be one term of a join. But if the - ** subquery is a join, then the flattening has already been stopped by - ** restriction (17d3) - */ - assert( (p->selFlags & SF_Recursive)==0 || pSub->pPrior==0 ); + /* Restriction (23) */ + if( (p->selFlags & SF_Recursive) ) return 0; + + if( pSrc->nSrc>1 ){ + if( pParse->nSelect>500 ) return 0; + if( OptimizationDisabled(db, SQLITE_FlttnUnionAll) ) return 0; + aCsrMap = sqlcipher_sqlite3DbMallocZero(db, ((i64)pParse->nTab+1)*sizeof(int)); + if( aCsrMap ) aCsrMap[0] = pParse->nTab; + } + } /***** If we reach this point, flattening is permitted. *****/ SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n", @@ -138637,6 +145358,17 @@ static int flattenSubquery( testcase( i==SQLITE_DENY ); pParse->zAuthContext = zSavedAuthContext; + /* Delete the transient structures associated with thesubquery */ + pSub1 = pSubitem->pSelect; + sqlcipher_sqlite3DbFree(db, pSubitem->zDatabase); + sqlcipher_sqlite3DbFree(db, pSubitem->zName); + sqlcipher_sqlite3DbFree(db, pSubitem->zAlias); + pSubitem->zDatabase = 0; + pSubitem->zName = 0; + pSubitem->zAlias = 0; + pSubitem->pSelect = 0; + assert( pSubitem->fg.isUsing!=0 || pSubitem->u3.pOn==0 ); + /* If the sub-query is a compound SELECT statement, then (by restrictions ** 17 and 18 above) it must be a UNION ALL and the parent query must ** be of the form: @@ -138675,18 +145407,23 @@ static int flattenSubquery( ExprList *pOrderBy = p->pOrderBy; Expr *pLimit = p->pLimit; Select *pPrior = p->pPrior; + Table *pItemTab = pSubitem->pTab; + pSubitem->pTab = 0; p->pOrderBy = 0; - p->pSrc = 0; p->pPrior = 0; p->pLimit = 0; pNew = sqlcipher_sqlite3SelectDup(db, p, 0); p->pLimit = pLimit; p->pOrderBy = pOrderBy; - p->pSrc = pSrc; p->op = TK_ALL; + pSubitem->pTab = pItemTab; if( pNew==0 ){ p->pPrior = pPrior; }else{ + pNew->selId = ++pParse->nSelect; + if( aCsrMap && ALWAYS(db->mallocFailed==0) ){ + renumberCursors(pParse, pNew, iFrom, aCsrMap); + } pNew->pPrior = pPrior; if( pPrior ) pPrior->pNext = pNew; pNew->pNext = p; @@ -138694,24 +145431,13 @@ static int flattenSubquery( SELECTTRACE(2,pParse,p,("compound-subquery flattener" " creates %u as peer\n",pNew->selId)); } - if( db->mallocFailed ) return 1; + assert( pSubitem->pSelect==0 ); + } + sqlcipher_sqlite3DbFree(db, aCsrMap); + if( db->mallocFailed ){ + pSubitem->pSelect = pSub1; + return 1; } - - /* Begin flattening the iFrom-th entry of the FROM clause - ** in the outer query. - */ - pSub = pSub1 = pSubitem->pSelect; - - /* Delete the transient table structure associated with the - ** subquery - */ - sqlcipher_sqlite3DbFree(db, pSubitem->zDatabase); - sqlcipher_sqlite3DbFree(db, pSubitem->zName); - sqlcipher_sqlite3DbFree(db, pSubitem->zAlias); - pSubitem->zDatabase = 0; - pSubitem->zName = 0; - pSubitem->zAlias = 0; - pSubitem->pSelect = 0; /* Defer deleting the Table object associated with the ** subquery until code generation is @@ -138724,8 +145450,10 @@ static int flattenSubquery( Table *pTabToDel = pSubitem->pTab; if( pTabToDel->nTabRef==1 ){ Parse *pToplevel = sqlcipher_sqlite3ParseToplevel(pParse); - pTabToDel->pNextZombie = pToplevel->pZombieTab; - pToplevel->pZombieTab = pTabToDel; + sqlcipher_sqlite3ParserAddCleanup(pToplevel, + (void(*)(sqlcipher_sqlite3*,void*))sqlcipher_sqlite3DeleteTable, + pTabToDel); + testcase( pToplevel->earlyCleanup ); }else{ pTabToDel->nTabRef--; } @@ -138745,22 +145473,18 @@ static int flattenSubquery( ** those references with expressions that resolve to the subquery FROM ** elements we are now copying in. */ + pSub = pSub1; for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){ int nSubSrc; u8 jointype = 0; + u8 ltorj = pSrc->a[iFrom].fg.jointype & JT_LTORJ; assert( pSub!=0 ); pSubSrc = pSub->pSrc; /* FROM clause of subquery */ nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */ pSrc = pParent->pSrc; /* FROM clause of the outer query */ - if( pSrc ){ - assert( pParent==p ); /* First time through the loop */ - jointype = pSubitem->fg.jointype; - }else{ - assert( pParent!=p ); /* 2nd and subsequent times through the loop */ - pSrc = sqlcipher_sqlite3SrcListAppend(pParse, 0, 0, 0); - if( pSrc==0 ) break; - pParent->pSrc = pSrc; + if( pParent==p ){ + jointype = pSubitem->fg.jointype; /* First time through the loop */ } /* The subquery uses a single slot of the FROM clause of the outer @@ -138788,13 +145512,16 @@ static int flattenSubquery( ** outer query. */ for(i=0; ia[i+iFrom].pUsing); - assert( pSrc->a[i+iFrom].fg.isTabFunc==0 ); - pSrc->a[i+iFrom] = pSubSrc->a[i]; + SrcItem *pItem = &pSrc->a[i+iFrom]; + if( pItem->fg.isUsing ) sqlcipher_sqlite3IdListDelete(db, pItem->u3.pUsing); + assert( pItem->fg.isTabFunc==0 ); + *pItem = pSubSrc->a[i]; + pItem->fg.jointype |= ltorj; iNewParent = pSubSrc->a[i].iCursor; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } - pSrc->a[iFrom].fg.jointype = jointype; + pSrc->a[iFrom].fg.jointype &= JT_LTORJ; + pSrc->a[iFrom].fg.jointype |= jointype | ltorj; /* Now begin substituting subquery result set expressions for ** references to the iParent in the outer query. @@ -138829,8 +145556,8 @@ static int flattenSubquery( } pWhere = pSub->pWhere; pSub->pWhere = 0; - if( isLeftJoin>0 ){ - sqlcipher_sqlite3SetJoinExpr(pWhere, iNewParent); + if( isOuterJoin>0 ){ + sqlcipher_sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON); } if( pWhere ){ if( pParent->pWhere ){ @@ -138844,7 +145571,7 @@ static int flattenSubquery( x.pParse = pParse; x.iTable = iParent; x.iNewTable = iNewParent; - x.isLeftJoin = isLeftJoin; + x.isOuterJoin = isOuterJoin; x.pEList = pSub->pEList; substSelect(&x, pParent, 0); } @@ -138879,8 +145606,8 @@ static int flattenSubquery( sqlcipher_sqlite3WalkSelect(&w,pSub1); sqlcipher_sqlite3SelectDelete(db, pSub1); -#if SELECTTRACE_ENABLED - if( sqlcipher_sqlite3_unsupported_selecttrace & 0x100 ){ +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x100 ){ SELECTTRACE(0x100,pParse,p,("After flattening:\n")); sqlcipher_sqlite3TreeViewSelect(0, p, 0); } @@ -138897,8 +145624,12 @@ static int flattenSubquery( typedef struct WhereConst WhereConst; struct WhereConst { Parse *pParse; /* Parsing context */ + u8 *pOomFault; /* Pointer to pParse->db->mallocFailed */ int nConst; /* Number for COLUMN=CONSTANT terms */ int nChng; /* Number of times a constant is propagated */ + int bHasAffBlob; /* At least one column in apExpr[] as affinity BLOB */ + u32 mExcludeOn; /* Which ON expressions to exclude from considertion. + ** Either EP_OuterON or EP_InnerON|EP_OuterON */ Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */ }; @@ -138937,6 +145668,9 @@ static void constInsert( return; /* Already present. Return without doing anything. */ } } + if( sqlcipher_sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){ + pConst->bHasAffBlob = 1; + } pConst->nConst++; pConst->apExpr = sqlcipher_sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr, @@ -138957,8 +145691,12 @@ static void constInsert( */ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ Expr *pRight, *pLeft; - if( pExpr==0 ) return; - if( ExprHasProperty(pExpr, EP_FromJoin) ) return; + if( NEVER(pExpr==0) ) return; + if( ExprHasProperty(pExpr, pConst->mExcludeOn) ){ + testcase( ExprHasProperty(pExpr, EP_OuterON) ); + testcase( ExprHasProperty(pExpr, EP_InnerON) ); + return; + } if( pExpr->op==TK_AND ){ findConstInWhere(pConst, pExpr->pRight); findConstInWhere(pConst, pExpr->pLeft); @@ -138978,37 +145716,84 @@ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ } /* -** This is a Walker expression callback. pExpr is a candidate expression -** to be replaced by a value. If pExpr is equivalent to one of the -** columns named in pWalker->u.pConst, then overwrite it with its -** corresponding value. +** This is a helper function for Walker callback propagateConstantExprRewrite(). +** +** Argument pExpr is a candidate expression to be replaced by a value. If +** pExpr is equivalent to one of the columns named in pWalker->u.pConst, +** then overwrite it with the corresponding value. Except, do not do so +** if argument bIgnoreAffBlob is non-zero and the affinity of pExpr +** is SQLITE_AFF_BLOB. */ -static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ +static int propagateConstantExprRewriteOne( + WhereConst *pConst, + Expr *pExpr, + int bIgnoreAffBlob +){ int i; - WhereConst *pConst; + if( pConst->pOomFault[0] ) return WRC_Prune; if( pExpr->op!=TK_COLUMN ) return WRC_Continue; - if( ExprHasProperty(pExpr, EP_FixedCol|EP_FromJoin) ){ + if( ExprHasProperty(pExpr, EP_FixedCol|pConst->mExcludeOn) ){ testcase( ExprHasProperty(pExpr, EP_FixedCol) ); - testcase( ExprHasProperty(pExpr, EP_FromJoin) ); + testcase( ExprHasProperty(pExpr, EP_OuterON) ); + testcase( ExprHasProperty(pExpr, EP_InnerON) ); return WRC_Continue; } - pConst = pWalker->u.pConst; for(i=0; inConst; i++){ Expr *pColumn = pConst->apExpr[i*2]; if( pColumn==pExpr ) continue; if( pColumn->iTable!=pExpr->iTable ) continue; if( pColumn->iColumn!=pExpr->iColumn ) continue; + if( bIgnoreAffBlob && sqlcipher_sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){ + break; + } /* A match is found. Add the EP_FixedCol property */ pConst->nChng++; ExprClearProperty(pExpr, EP_Leaf); ExprSetProperty(pExpr, EP_FixedCol); assert( pExpr->pLeft==0 ); pExpr->pLeft = sqlcipher_sqlite3ExprDup(pConst->pParse->db, pConst->apExpr[i*2+1], 0); + if( pConst->pParse->db->mallocFailed ) return WRC_Prune; break; } return WRC_Prune; } +/* +** This is a Walker expression callback. pExpr is a node from the WHERE +** clause of a SELECT statement. This function examines pExpr to see if +** any substitutions based on the contents of pWalker->u.pConst should +** be made to pExpr or its immediate children. +** +** A substitution is made if: +** +** + pExpr is a column with an affinity other than BLOB that matches +** one of the columns in pWalker->u.pConst, or +** +** + pExpr is a binary comparison operator (=, <=, >=, <, >) that +** uses an affinity other than TEXT and one of its immediate +** children is a column that matches one of the columns in +** pWalker->u.pConst. +*/ +static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ + WhereConst *pConst = pWalker->u.pConst; + assert( TK_GT==TK_EQ+1 ); + assert( TK_LE==TK_EQ+2 ); + assert( TK_LT==TK_EQ+3 ); + assert( TK_GE==TK_EQ+4 ); + if( pConst->bHasAffBlob ){ + if( (pExpr->op>=TK_EQ && pExpr->op<=TK_GE) + || pExpr->op==TK_IS + ){ + propagateConstantExprRewriteOne(pConst, pExpr->pLeft, 0); + if( pConst->pOomFault[0] ) return WRC_Prune; + if( sqlcipher_sqlite3ExprAffinity(pExpr->pLeft)!=SQLITE_AFF_TEXT ){ + propagateConstantExprRewriteOne(pConst, pExpr->pRight, 0); + } + } + } + return propagateConstantExprRewriteOne(pConst, pExpr, pConst->bHasAffBlob); +} + /* ** The WHERE-clause constant propagation optimization. ** @@ -139044,6 +145829,21 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ ** routines know to generate the constant "123" instead of looking up the ** column value. Also, to avoid collation problems, this optimization is ** only attempted if the "a=123" term uses the default BINARY collation. +** +** 2021-05-25 forum post 6a06202608: Another troublesome case is... +** +** CREATE TABLE t1(x); +** INSERT INTO t1 VALUES(10.0); +** SELECT 1 FROM t1 WHERE x=10 AND x LIKE 10; +** +** The query should return no rows, because the t1.x value is '10.0' not '10' +** and '10.0' is not LIKE '10'. But if we are not careful, the first WHERE +** term "x=10" will cause the second WHERE term to become "10 LIKE 10", +** resulting in a false positive. To avoid this, constant propagation for +** columns with BLOB affinity is only allowed if the constant is used with +** operators ==, <=, <, >=, >, or IS in a way that will cause the correct +** type conversions to occur. See logic associated with the bHasAffBlob flag +** for details. */ static int propagateConstants( Parse *pParse, /* The parsing context */ @@ -139053,10 +145853,23 @@ static int propagateConstants( Walker w; int nChng = 0; x.pParse = pParse; + x.pOomFault = &pParse->db->mallocFailed; do{ x.nConst = 0; x.nChng = 0; x.apExpr = 0; + x.bHasAffBlob = 0; + if( ALWAYS(p->pSrc!=0) + && p->pSrc->nSrc>0 + && (p->pSrc->a[0].fg.jointype & JT_LTORJ)!=0 + ){ + /* Do not propagate constants on any ON clause if there is a + ** RIGHT JOIN anywhere in the query */ + x.mExcludeOn = EP_InnerON | EP_OuterON; + }else{ + /* Do not propagate constants through the ON clause of a LEFT JOIN */ + x.mExcludeOn = EP_OuterON; + } findConstInWhere(&x, p->pWhere); if( x.nConst ){ memset(&w, 0, sizeof(w)); @@ -139074,6 +145887,35 @@ static int propagateConstants( return nChng; } +#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) +# if !defined(SQLITE_OMIT_WINDOWFUNC) +/* +** This function is called to determine whether or not it is safe to +** push WHERE clause expression pExpr down to FROM clause sub-query +** pSubq, which contains at least one window function. Return 1 +** if it is safe and the expression should be pushed down, or 0 +** otherwise. +** +** It is only safe to push the expression down if it consists only +** of constants and copies of expressions that appear in the PARTITION +** BY clause of all window function used by the sub-query. It is safe +** to filter out entire partitions, but not rows within partitions, as +** this may change the results of the window functions. +** +** At the time this function is called it is guaranteed that +** +** * the sub-query uses only one distinct window frame, and +** * that the window frame has a PARTITION BY clase. +*/ +static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ + assert( pSubq->pWin->pPartition ); + assert( (pSubq->selFlags & SF_MultiPart)==0 ); + assert( pSubq->pPrior==0 ); + return sqlcipher_sqlite3ExprIsConstantOrGroupBy(pParse, pExpr, pSubq->pWin->pPartition); +} +# endif /* SQLITE_OMIT_WINDOWFUNC */ +#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ + #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* ** Make copies of relevant WHERE clause terms of the outer query into @@ -139121,9 +145963,24 @@ static int propagateConstants( ** But if the (b2=2) term were to be pushed down into the bb subquery, ** then the (1,1,NULL) row would be suppressed. ** -** (6) The inner query features one or more window-functions (since -** changes to the WHERE clause of the inner query could change the -** window over which window functions are calculated). +** (6) Window functions make things tricky as changes to the WHERE clause +** of the inner query could change the window over which window +** functions are calculated. Therefore, do not attempt the optimization +** if: +** +** (6a) The inner query uses multiple incompatible window partitions. +** +** (6b) The inner query is a compound and uses window-functions. +** +** (6c) The WHERE clause does not consist entirely of constants and +** copies of expressions found in the PARTITION BY clause of +** all window-functions used by the sub-query. It is safe to +** filter out entire partitions, as this does not change the +** window over which any window-function is calculated. +** +** (7) The inner query is a Common Table Expression (CTE) that should +** be materialized. (This restriction is implemented in the calling +** routine.) ** ** Return 0 if no changes are made and non-zero if one or more WHERE clause ** terms are duplicated into the subquery. @@ -139132,18 +145989,22 @@ static int pushDownWhereTerms( Parse *pParse, /* Parse context (for malloc() and error reporting) */ Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ Expr *pWhere, /* The WHERE clause of the outer query */ - int iCursor, /* Cursor number of the subquery */ - int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */ + SrcItem *pSrc /* The subquery term of the outer FROM clause */ ){ Expr *pNew; int nChng = 0; - Select *pSel; if( pWhere==0 ) return 0; - if( pSubq->selFlags & SF_Recursive ) return 0; /* restriction (2) */ + if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0; + if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ) return 0; #ifndef SQLITE_OMIT_WINDOWFUNC - for(pSel=pSubq; pSel; pSel=pSel->pPrior){ - if( pSel->pWin ) return 0; /* restriction (6) */ + if( pSubq->pPrior ){ + Select *pSel; + for(pSel=pSubq; pSel; pSel=pSel->pPrior){ + if( pSel->pWin ) return 0; /* restriction (6b) */ + } + }else{ + if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0; } #endif @@ -139164,31 +146025,45 @@ static int pushDownWhereTerms( return 0; /* restriction (3) */ } while( pWhere->op==TK_AND ){ - nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, - iCursor, isLeftJoin); + nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrc); pWhere = pWhere->pLeft; } + +#if 0 /* Legacy code. Checks now done by sqlcipher_sqlite3ExprIsTableConstraint() */ if( isLeftJoin - && (ExprHasProperty(pWhere,EP_FromJoin)==0 - || pWhere->iRightJoinTable!=iCursor) + && (ExprHasProperty(pWhere,EP_OuterON)==0 + || pWhere->w.iJoin!=iCursor) ){ return 0; /* restriction (4) */ } - if( ExprHasProperty(pWhere,EP_FromJoin) && pWhere->iRightJoinTable!=iCursor ){ + if( ExprHasProperty(pWhere,EP_OuterON) + && pWhere->w.iJoin!=iCursor + ){ return 0; /* restriction (5) */ } - if( sqlcipher_sqlite3ExprIsTableConstant(pWhere, iCursor) ){ +#endif + + if( sqlcipher_sqlite3ExprIsTableConstraint(pWhere, pSrc) ){ nChng++; + pSubq->selFlags |= SF_PushDown; while( pSubq ){ SubstContext x; pNew = sqlcipher_sqlite3ExprDup(pParse->db, pWhere, 0); - unsetJoinExpr(pNew, -1); + unsetJoinExpr(pNew, -1, 1); x.pParse = pParse; - x.iTable = iCursor; - x.iNewTable = iCursor; - x.isLeftJoin = 0; + x.iTable = pSrc->iCursor; + x.iNewTable = pSrc->iCursor; + x.isOuterJoin = 0; x.pEList = pSubq->pEList; pNew = substExpr(&x, pNew); +#ifndef SQLITE_OMIT_WINDOWFUNC + if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){ + /* Restriction 6c has prevented push-down in this case */ + sqlcipher_sqlite3ExprDelete(pParse->db, pNew); + nChng--; + break; + } +#endif if( pSubq->selFlags & SF_Aggregate ){ pSubq->pHaving = sqlcipher_sqlite3ExprAnd(pParse, pSubq->pHaving, pNew); }else{ @@ -139219,7 +146094,7 @@ static int pushDownWhereTerms( */ static u8 minMaxQuery(sqlcipher_sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ int eRet = WHERE_ORDERBY_NORMAL; /* Return value */ - ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */ + ExprList *pEList; /* Arguments to agg function */ const char *zFunc; /* Name of aggregate function pFunc */ ExprList *pOrderBy; u8 sortFlags = 0; @@ -139227,9 +146102,16 @@ static u8 minMaxQuery(sqlcipher_sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ assert( *ppMinMax==0 ); assert( pFunc->op==TK_AGG_FUNCTION ); assert( !IsWindowFunc(pFunc) ); - if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_WinFunc) ){ + assert( ExprUseXList(pFunc) ); + pEList = pFunc->x.pList; + if( pEList==0 + || pEList->nExpr!=1 + || ExprHasProperty(pFunc, EP_WinFunc) + || OptimizationDisabled(db, SQLITE_MinMaxOpt) + ){ return eRet; } + assert( !ExprHasProperty(pFunc, EP_IntValue) ); zFunc = pFunc->u.zToken; if( sqlcipher_sqlite3StrICmp(zFunc, "min")==0 ){ eRet = WHERE_ORDERBY_MIN; @@ -139244,7 +146126,7 @@ static u8 minMaxQuery(sqlcipher_sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ } *ppMinMax = pOrderBy = sqlcipher_sqlite3ExprListDup(db, pEList, 0); assert( pOrderBy!=0 || db->mallocFailed ); - if( pOrderBy ) pOrderBy->a[0].sortFlags = sortFlags; + if( pOrderBy ) pOrderBy->a[0].fg.sortFlags = sortFlags; return eRet; } @@ -139257,7 +146139,13 @@ static u8 minMaxQuery(sqlcipher_sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ ** ** where table is a database table, not a sub-select or view. If the query ** does match this pattern, then a pointer to the Table object representing -** is returned. Otherwise, 0 is returned. +** is returned. Otherwise, NULL is returned. +** +** This routine checks to see if it is safe to use the count optimization. +** A correct answer is still obtained (though perhaps more slowly) if +** this routine returns NULL when it could have returned a table pointer. +** But returning the pointer when NULL should have been returned can +** result in incorrect answers and/or crashes. So, when in doubt, return NULL. */ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ Table *pTab; @@ -139265,19 +146153,27 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ assert( !p->pGroupBy ); - if( p->pWhere || p->pEList->nExpr!=1 - || p->pSrc->nSrc!=1 || p->pSrc->a[0].pSelect + if( p->pWhere + || p->pEList->nExpr!=1 + || p->pSrc->nSrc!=1 + || p->pSrc->a[0].pSelect + || pAggInfo->nFunc!=1 + || p->pHaving ){ return 0; } pTab = p->pSrc->a[0].pTab; + assert( pTab!=0 ); + assert( !IsView(pTab) ); + if( !IsOrdinaryTable(pTab) ) return 0; pExpr = p->pEList->a[0].pExpr; - assert( pTab && !pTab->pSelect && pExpr ); - - if( IsVirtual(pTab) ) return 0; + assert( pExpr!=0 ); if( pExpr->op!=TK_AGG_FUNCTION ) return 0; - if( NEVER(pAggInfo->nFunc==0) ) return 0; + if( pExpr->pAggInfo!=pAggInfo ) return 0; if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0; + assert( pAggInfo->aFunc[0].pFExpr==pExpr ); + testcase( ExprHasProperty(pExpr, EP_Distinct) ); + testcase( ExprHasProperty(pExpr, EP_WinFunc) ); if( ExprHasProperty(pExpr, EP_Distinct|EP_WinFunc) ) return 0; return pTab; @@ -139290,24 +146186,27 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ ** SQLITE_ERROR and leave an error in pParse. Otherwise, populate ** pFrom->pIndex and return SQLITE_OK. */ -SQLITE_PRIVATE int sqlcipher_sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){ - if( pFrom->pTab && pFrom->fg.isIndexedBy ){ - Table *pTab = pFrom->pTab; - char *zIndexedBy = pFrom->u1.zIndexedBy; - Index *pIdx; - for(pIdx=pTab->pIndex; - pIdx && sqlcipher_sqlite3StrICmp(pIdx->zName, zIndexedBy); - pIdx=pIdx->pNext - ); - if( !pIdx ){ - sqlcipher_sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0); - pParse->checkSchema = 1; - return SQLITE_ERROR; - } - pFrom->pIBIndex = pIdx; +SQLITE_PRIVATE int sqlcipher_sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){ + Table *pTab = pFrom->pTab; + char *zIndexedBy = pFrom->u1.zIndexedBy; + Index *pIdx; + assert( pTab!=0 ); + assert( pFrom->fg.isIndexedBy!=0 ); + + for(pIdx=pTab->pIndex; + pIdx && sqlcipher_sqlite3StrICmp(pIdx->zName, zIndexedBy); + pIdx=pIdx->pNext + ); + if( !pIdx ){ + sqlcipher_sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0); + pParse->checkSchema = 1; + return SQLITE_ERROR; } + assert( pFrom->fg.isCte==0 ); + pFrom->u2.pIBIndex = pIdx; return SQLITE_OK; } + /* ** Detect compound SELECT statements that use an ORDER BY clause with ** an alternative collating sequence. @@ -139364,7 +146263,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ pNew = sqlcipher_sqlite3DbMallocZero(db, sizeof(*pNew) ); if( pNew==0 ) return WRC_Abort; memset(&dummy, 0, sizeof(dummy)); - pNewSrc = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0,0); + pNewSrc = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0); if( pNewSrc==0 ) return WRC_Abort; *pNew = *p; p->pSrc = pNewSrc; @@ -139394,7 +146293,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ ** arguments. If it does, leave an error message in pParse and return ** non-zero, since pFrom is not allowed to be a table-valued function. */ -static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){ +static int cannotBeFunction(Parse *pParse, SrcItem *pFrom){ if( pFrom->fg.isTabFunc ){ sqlcipher_sqlite3ErrorMsg(pParse, "'%s' is not a function", pFrom->zName); return 1; @@ -139415,21 +146314,22 @@ static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){ */ static struct Cte *searchWith( With *pWith, /* Current innermost WITH clause */ - struct SrcList_item *pItem, /* FROM clause element to resolve */ + SrcItem *pItem, /* FROM clause element to resolve */ With **ppContext /* OUT: WITH clause return value belongs to */ ){ - const char *zName; - if( pItem->zDatabase==0 && (zName = pItem->zName)!=0 ){ - With *p; - for(p=pWith; p; p=p->pOuter){ - int i; - for(i=0; inCte; i++){ - if( sqlcipher_sqlite3StrICmp(zName, p->a[i].zName)==0 ){ - *ppContext = p; - return &p->a[i]; - } + const char *zName = pItem->zName; + With *p; + assert( pItem->zDatabase==0 ); + assert( zName!=0 ); + for(p=pWith; p; p=p->pOuter){ + int i; + for(i=0; inCte; i++){ + if( sqlcipher_sqlite3StrICmp(zName, p->a[i].zName)==0 ){ + *ppContext = p; + return &p->a[i]; } } + if( p->bView ) break; } return 0; } @@ -139439,52 +146339,83 @@ static struct Cte *searchWith( ** ** This routine pushes the WITH clause passed as the second argument ** onto the top of the stack. If argument bFree is true, then this -** WITH clause will never be popped from the stack. In this case it -** should be freed along with the Parse object. In other cases, when +** WITH clause will never be popped from the stack but should instead +** be freed along with the Parse object. In other cases, when ** bFree==0, the With object will be freed along with the SELECT ** statement with which it is associated. +** +** This routine returns a copy of pWith. Or, if bFree is true and +** the pWith object is destroyed immediately due to an OOM condition, +** then this routine return NULL. +** +** If bFree is true, do not continue to use the pWith pointer after +** calling this routine, Instead, use only the return value. */ -SQLITE_PRIVATE void sqlcipher_sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ - assert( bFree==0 || (pParse->pWith==0 && pParse->pWithToFree==0) ); +SQLITE_PRIVATE With *sqlcipher_sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ if( pWith ){ - assert( pParse->pWith!=pWith ); - pWith->pOuter = pParse->pWith; - pParse->pWith = pWith; - if( bFree ) pParse->pWithToFree = pWith; + if( bFree ){ + pWith = (With*)sqlcipher_sqlite3ParserAddCleanup(pParse, + (void(*)(sqlcipher_sqlite3*,void*))sqlcipher_sqlite3WithDelete, + pWith); + if( pWith==0 ) return 0; + } + if( pParse->nErr==0 ){ + assert( pParse->pWith!=pWith ); + pWith->pOuter = pParse->pWith; + pParse->pWith = pWith; + } } + return pWith; } /* ** This function checks if argument pFrom refers to a CTE declared by -** a WITH clause on the stack currently maintained by the parser. And, -** if currently processing a CTE expression, if it is a recursive -** reference to the current CTE. +** a WITH clause on the stack currently maintained by the parser (on the +** pParse->pWith linked list). And if currently processing a CTE +** CTE expression, through routine checks to see if the reference is +** a recursive reference to the CTE. ** -** If pFrom falls into either of the two categories above, pFrom->pTab -** and other fields are populated accordingly. The caller should check -** (pFrom->pTab!=0) to determine whether or not a successful match -** was found. +** If pFrom matches a CTE according to either of these two above, pFrom->pTab +** and other fields are populated accordingly. ** -** Whether or not a match is found, SQLITE_OK is returned if no error -** occurs. If an error does occur, an error message is stored in the -** parser and some error code other than SQLITE_OK returned. +** Return 0 if no match is found. +** Return 1 if a match is found. +** Return 2 if an error condition is detected. */ -static int withExpand( - Walker *pWalker, - struct SrcList_item *pFrom +static int resolveFromTermToCte( + Parse *pParse, /* The parsing context */ + Walker *pWalker, /* Current tree walker */ + SrcItem *pFrom /* The FROM clause term to check */ ){ - Parse *pParse = pWalker->pParse; - sqlcipher_sqlite3 *db = pParse->db; - struct Cte *pCte; /* Matched CTE (or NULL if no match) */ - With *pWith; /* WITH clause that pCte belongs to */ + Cte *pCte; /* Matched CTE (or NULL if no match) */ + With *pWith; /* The matching WITH */ assert( pFrom->pTab==0 ); + if( pParse->pWith==0 ){ + /* There are no WITH clauses in the stack. No match is possible */ + return 0; + } if( pParse->nErr ){ - return SQLITE_ERROR; + /* Prior errors might have left pParse->pWith in a goofy state, so + ** go no further. */ + return 0; + } + if( pFrom->zDatabase!=0 ){ + /* The FROM term contains a schema qualifier (ex: main.t1) and so + ** it cannot possibly be a CTE reference. */ + return 0; + } + if( pFrom->fg.notCte ){ + /* The FROM term is specifically excluded from matching a CTE. + ** (1) It is part of a trigger that used to have zDatabase but had + ** zDatabase removed by sqlcipher_sqlite3FixTriggerStep(). + ** (2) This is the first term in the FROM clause of an UPDATE. + */ + return 0; } - pCte = searchWith(pParse->pWith, pFrom, &pWith); if( pCte ){ + sqlcipher_sqlite3 *db = pParse->db; Table *pTab; ExprList *pEList; Select *pSel; @@ -139493,6 +146424,7 @@ static int withExpand( int bMayRecursive; /* True if compound joined by UNION [ALL] */ With *pSavedWith; /* Initial value of pParse->pWith */ int iRecTab = -1; /* Cursor for recursive table */ + CteUse *pCteUse; /* If pCte->zCteErr is non-NULL at this point, then this is an illegal ** recursive reference to CTE pCte. Leave an error in pParse and return @@ -139500,21 +146432,44 @@ static int withExpand( ** In this case, proceed. */ if( pCte->zCteErr ){ sqlcipher_sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName); - return SQLITE_ERROR; + return 2; } - if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR; + if( cannotBeFunction(pParse, pFrom) ) return 2; assert( pFrom->pTab==0 ); - pFrom->pTab = pTab = sqlcipher_sqlite3DbMallocZero(db, sizeof(Table)); - if( pTab==0 ) return WRC_Abort; + pTab = sqlcipher_sqlite3DbMallocZero(db, sizeof(Table)); + if( pTab==0 ) return 2; + pCteUse = pCte->pUse; + if( pCteUse==0 ){ + pCte->pUse = pCteUse = sqlcipher_sqlite3DbMallocZero(db, sizeof(pCteUse[0])); + if( pCteUse==0 + || sqlcipher_sqlite3ParserAddCleanup(pParse,sqlcipher_sqlite3DbFree,pCteUse)==0 + ){ + sqlcipher_sqlite3DbFree(db, pTab); + return 2; + } + pCteUse->eM10d = pCte->eM10d; + } + pFrom->pTab = pTab; pTab->nTabRef = 1; pTab->zName = sqlcipher_sqlite3DbStrDup(db, pCte->zName); pTab->iPKey = -1; pTab->nRowLogEst = 200; assert( 200==sqlcipher_sqlite3LogEst(1048576) ); pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; pFrom->pSelect = sqlcipher_sqlite3SelectDup(db, pCte->pSelect, 0); - if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; + if( db->mallocFailed ) return 2; + pFrom->pSelect->selFlags |= SF_CopyCte; assert( pFrom->pSelect ); + if( pFrom->fg.isIndexedBy ){ + sqlcipher_sqlite3ErrorMsg(pParse, "no such index: \"%s\"", pFrom->u1.zIndexedBy); + return 2; + } + pFrom->fg.isCte = 1; + pFrom->u2.pCteUse = pCteUse; + pCteUse->nUse++; + if( pCteUse->nUse>=2 && pCteUse->eM10d==M10d_Any ){ + pCteUse->eM10d = M10d_Yes; + } /* Check if this is a recursive CTE. */ pRecTerm = pSel = pFrom->pSelect; @@ -139524,7 +146479,7 @@ static int withExpand( SrcList *pSrc = pRecTerm->pSrc; assert( pRecTerm->pPrior!=0 ); for(i=0; inSrc; i++){ - struct SrcList_item *pItem = &pSrc->a[i]; + SrcItem *pItem = &pSrc->a[i]; if( pItem->zDatabase==0 && pItem->zName!=0 && 0==sqlcipher_sqlite3StrICmp(pItem->zName, pCte->zName) @@ -139536,7 +146491,7 @@ static int withExpand( sqlcipher_sqlite3ErrorMsg(pParse, "multiple references to recursive table: %s", pCte->zName ); - return SQLITE_ERROR; + return 2; } pRecTerm->selFlags |= SF_Recursive; if( iRecTab<0 ) iRecTab = pParse->nTab++; @@ -139551,16 +146506,24 @@ static int withExpand( pSavedWith = pParse->pWith; pParse->pWith = pWith; if( pSel->selFlags & SF_Recursive ){ + int rc; assert( pRecTerm!=0 ); assert( (pRecTerm->selFlags & SF_Recursive)==0 ); assert( pRecTerm->pNext!=0 ); assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 ); assert( pRecTerm->pWith==0 ); pRecTerm->pWith = pSel->pWith; - sqlcipher_sqlite3WalkSelect(pWalker, pRecTerm); + rc = sqlcipher_sqlite3WalkSelect(pWalker, pRecTerm); pRecTerm->pWith = 0; + if( rc ){ + pParse->pWith = pSavedWith; + return 2; + } }else{ - sqlcipher_sqlite3WalkSelect(pWalker, pSel); + if( sqlcipher_sqlite3WalkSelect(pWalker, pSel) ){ + pParse->pWith = pSavedWith; + return 2; + } } pParse->pWith = pWith; @@ -139572,7 +146535,7 @@ static int withExpand( pCte->zName, pEList->nExpr, pCte->pCols->nExpr ); pParse->pWith = pSavedWith; - return SQLITE_ERROR; + return 2; } pEList = pCte->pCols; } @@ -139588,9 +146551,9 @@ static int withExpand( } pCte->zCteErr = 0; pParse->pWith = pSavedWith; + return 1; /* Success */ } - - return SQLITE_OK; + return 0; /* No match */ } #endif @@ -139603,7 +146566,7 @@ static int withExpand( ** sqlcipher_sqlite3SelectExpand() when walking a SELECT tree to resolve table ** names and other FROM clause elements. */ -static void selectPopWith(Walker *pWalker, Select *p){ +SQLITE_PRIVATE void sqlcipher_sqlite3SelectPopWith(Walker *pWalker, Select *p){ Parse *pParse = pWalker->pParse; if( OK_IF_ALWAYS_TRUE(pParse->pWith) && p->pPrior==0 ){ With *pWith = findRightmost(p)->pWith; @@ -139613,8 +146576,6 @@ static void selectPopWith(Walker *pWalker, Select *p){ } } } -#else -#define selectPopWith 0 #endif /* @@ -139624,7 +146585,7 @@ static void selectPopWith(Walker *pWalker, Select *p){ ** SQLITE_OK is returned. Otherwise, if an OOM error is encountered, ** SQLITE_NOMEM. */ -SQLITE_PRIVATE int sqlcipher_sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){ +SQLITE_PRIVATE int sqlcipher_sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){ Select *pSel = pFrom->pSelect; Table *pTab; @@ -139635,17 +146596,47 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExpandSubquery(Parse *pParse, struct SrcList if( pFrom->zAlias ){ pTab->zName = sqlcipher_sqlite3DbStrDup(pParse->db, pFrom->zAlias); }else{ - pTab->zName = sqlcipher_sqlite3MPrintf(pParse->db, "subquery_%u", pSel->selId); + pTab->zName = sqlcipher_sqlite3MPrintf(pParse->db, "%!S", pFrom); } while( pSel->pPrior ){ pSel = pSel->pPrior; } sqlcipher_sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); pTab->iPKey = -1; pTab->nRowLogEst = 200; assert( 200==sqlcipher_sqlite3LogEst(1048576) ); - pTab->tabFlags |= TF_Ephemeral; - +#ifndef SQLITE_ALLOW_ROWID_IN_VIEW + /* The usual case - do not allow ROWID on a subquery */ + pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; +#else + pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */ +#endif return pParse->nErr ? SQLITE_ERROR : SQLITE_OK; } + +/* +** Check the N SrcItem objects to the right of pBase. (N might be zero!) +** If any of those SrcItem objects have a USING clause containing zName +** then return true. +** +** If N is zero, or none of the N SrcItem objects to the right of pBase +** contains a USING clause, or if none of the USING clauses contain zName, +** then return false. +*/ +static int inAnyUsingClause( + const char *zName, /* Name we are looking for */ + SrcItem *pBase, /* The base SrcItem. Looking at pBase[1] and following */ + int N /* How many SrcItems to check */ +){ + while( N>0 ){ + N--; + pBase++; + if( pBase->fg.isUsing==0 ) continue; + if( NEVER(pBase->u3.pUsing==0) ) continue; + if( sqlcipher_sqlite3IdListIndex(pBase->u3.pUsing, zName)>=0 ) return 1; + } + return 0; +} + + /* ** This routine is a Walker callback for "expanding" a SELECT statement. ** "Expanding" means to do the following: @@ -139672,10 +146663,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ExpandSubquery(Parse *pParse, struct SrcList */ static int selectExpander(Walker *pWalker, Select *p){ Parse *pParse = pWalker->pParse; - int i, j, k; + int i, j, k, rc; SrcList *pTabList; ExprList *pEList; - struct SrcList_item *pFrom; + SrcItem *pFrom; sqlcipher_sqlite3 *db = pParse->db; Expr *pE, *pRight, *pExpr; u16 selFlags = p->selFlags; @@ -139695,6 +146686,15 @@ static int selectExpander(Walker *pWalker, Select *p){ } pTabList = p->pSrc; pEList = p->pEList; + if( pParse->pWith && (p->selFlags & SF_View) ){ + if( p->pWith==0 ){ + p->pWith = (With*)sqlcipher_sqlite3DbMallocZero(db, sizeof(With)); + if( p->pWith==0 ){ + return WRC_Abort; + } + } + p->pWith->bView = 1; + } sqlcipher_sqlite3WithPush(pParse, p->pWith, 0); /* Make sure cursor numbers have been assigned to all entries in @@ -139711,10 +146711,6 @@ static int selectExpander(Walker *pWalker, Select *p){ assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 ); if( pFrom->pTab ) continue; assert( pFrom->fg.isRecursive==0 ); -#ifndef SQLITE_OMIT_CTE - if( withExpand(pWalker, pFrom) ) return WRC_Abort; - if( pFrom->pTab ) {} else -#endif if( pFrom->zName==0 ){ #ifndef SQLITE_OMIT_SUBQUERY Select *pSel = pFrom->pSelect; @@ -139723,6 +146719,12 @@ static int selectExpander(Walker *pWalker, Select *p){ assert( pFrom->pTab==0 ); if( sqlcipher_sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort; if( sqlcipher_sqlite3ExpandSubquery(pParse, pFrom) ) return WRC_Abort; +#endif +#ifndef SQLITE_OMIT_CTE + }else if( (rc = resolveFromTermToCte(pParse, pWalker, pFrom))!=0 ){ + if( rc>1 ) return WRC_Abort; + pTab = pFrom->pTab; + assert( pTab!=0 ); #endif }else{ /* An ordinary table or view name in the FROM clause */ @@ -139740,26 +146742,31 @@ static int selectExpander(Walker *pWalker, Select *p){ return WRC_Abort; } #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) - if( IsVirtual(pTab) || pTab->pSelect ){ + if( !IsOrdinaryTable(pTab) ){ i16 nCol; u8 eCodeOrig = pWalker->eCode; if( sqlcipher_sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort; assert( pFrom->pSelect==0 ); - if( pTab->pSelect && (db->flags & SQLITE_EnableView)==0 ){ - sqlcipher_sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", - pTab->zName); + if( IsView(pTab) ){ + if( (db->flags & SQLITE_EnableView)==0 + && pTab->pSchema!=db->aDb[1].pSchema + ){ + sqlcipher_sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", + pTab->zName); + } + pFrom->pSelect = sqlcipher_sqlite3SelectDup(db, pTab->u.view.pSelect, 0); } #ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTab) + else if( ALWAYS(IsVirtual(pTab)) && pFrom->fg.fromDDL - && ALWAYS(pTab->pVTable!=0) - && pTab->pVTable->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) + && ALWAYS(pTab->u.vtab.p!=0) + && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) ){ sqlcipher_sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", pTab->zName); } + assert( SQLITE_VTABRISK_Normal==1 && SQLITE_VTABRISK_High==2 ); #endif - pFrom->pSelect = sqlcipher_sqlite3SelectDup(db, pTab->pSelect, 0); nCol = pTab->nCol; pTab->nCol = -1; pWalker->eCode = 1; /* Turn on Select.selId renumbering */ @@ -139771,14 +146778,15 @@ static int selectExpander(Walker *pWalker, Select *p){ } /* Locate the index named by the INDEXED BY clause, if any. */ - if( sqlcipher_sqlite3IndexedByLookup(pParse, pFrom) ){ + if( pFrom->fg.isIndexedBy && sqlcipher_sqlite3IndexedByLookup(pParse, pFrom) ){ return WRC_Abort; } } /* Process NATURAL keywords, and ON and USING clauses of joins. */ - if( pParse->nErr || db->mallocFailed || sqliteProcessJoin(pParse, p) ){ + assert( db->mallocFailed==0 || pParse->nErr!=0 ); + if( pParse->nErr || sqlcipher_sqlite3ProcessJoin(pParse, p) ){ return WRC_Abort; } @@ -139826,7 +146834,7 @@ static int selectExpander(Walker *pWalker, Select *p){ pNew = sqlcipher_sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); if( pNew ){ pNew->a[pNew->nExpr-1].zEName = a[k].zEName; - pNew->a[pNew->nExpr-1].eEName = a[k].eEName; + pNew->a[pNew->nExpr-1].fg.eEName = a[k].fg.eEName; a[k].zEName = 0; } a[k].pExpr = 0; @@ -139841,32 +146849,60 @@ static int selectExpander(Walker *pWalker, Select *p){ zTName = pE->pLeft->u.zToken; } for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ - Table *pTab = pFrom->pTab; - Select *pSub = pFrom->pSelect; - char *zTabName = pFrom->zAlias; - const char *zSchemaName = 0; - int iDb; - if( zTabName==0 ){ + Table *pTab = pFrom->pTab; /* Table for this data source */ + ExprList *pNestedFrom; /* Result-set of a nested FROM clause */ + char *zTabName; /* AS name for this data source */ + const char *zSchemaName = 0; /* Schema name for this data source */ + int iDb; /* Schema index for this data src */ + IdList *pUsing; /* USING clause for pFrom[1] */ + + if( (zTabName = pFrom->zAlias)==0 ){ zTabName = pTab->zName; } if( db->mallocFailed ) break; - if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ - pSub = 0; + assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom->pSelect) ); + if( pFrom->fg.isNestedFrom ){ + assert( pFrom->pSelect!=0 ); + pNestedFrom = pFrom->pSelect->pEList; + assert( pNestedFrom!=0 ); + assert( pNestedFrom->nExpr==pTab->nCol ); + }else{ if( zTName && sqlcipher_sqlite3StrICmp(zTName, zTabName)!=0 ){ continue; } + pNestedFrom = 0; iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); zSchemaName = iDb>=0 ? db->aDb[iDb].zDbSName : "*"; } + if( i+1nSrc + && pFrom[1].fg.isUsing + && (selFlags & SF_NestedFrom)!=0 + ){ + int ii; + pUsing = pFrom[1].u3.pUsing; + for(ii=0; iinId; ii++){ + const char *zUName = pUsing->a[ii].zName; + pRight = sqlcipher_sqlite3Expr(db, TK_ID, zUName); + pNew = sqlcipher_sqlite3ExprListAppend(pParse, pNew, pRight); + if( pNew ){ + struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; + assert( pX->zEName==0 ); + pX->zEName = sqlcipher_sqlite3MPrintf(db,"..%s", zUName); + pX->fg.eEName = ENAME_TAB; + pX->fg.bUsingTerm = 1; + } + } + }else{ + pUsing = 0; + } for(j=0; jnCol; j++){ - char *zName = pTab->aCol[j].zName; - char *zColname; /* The computed column name */ - char *zToFree; /* Malloced string that needs to be freed */ - Token sColname; /* Computed column name as a token */ + char *zName = pTab->aCol[j].zCnName; + struct ExprList_item *pX; /* Newly added ExprList term */ assert( zName ); - if( zTName && pSub - && sqlcipher_sqlite3MatchEName(&pSub->pEList->a[j], 0, zTName, 0)==0 + if( zTName + && pNestedFrom + && sqlcipher_sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0)==0 ){ continue; } @@ -139880,57 +146916,75 @@ static int selectExpander(Walker *pWalker, Select *p){ ){ continue; } + if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0 + && zTName==0 + && (selFlags & (SF_NestedFrom))==0 + ){ + continue; + } tableSeen = 1; - if( i>0 && zTName==0 ){ - if( (pFrom->fg.jointype & JT_NATURAL)!=0 - && tableAndColumnIndex(pTabList, i, zName, 0, 0, 1) + if( i>0 && zTName==0 && (selFlags & SF_NestedFrom)==0 ){ + if( pFrom->fg.isUsing + && sqlcipher_sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0 ){ - /* In a NATURAL join, omit the join columns from the - ** table to the right of the join */ - continue; - } - if( sqlcipher_sqlite3IdListIndex(pFrom->pUsing, zName)>=0 ){ /* In a join with a USING clause, omit columns in the ** using clause from the table on the right. */ continue; } } pRight = sqlcipher_sqlite3Expr(db, TK_ID, zName); - zColname = zName; - zToFree = 0; - if( longNames || pTabList->nSrc>1 ){ + if( (pTabList->nSrc>1 + && ( (pFrom->fg.jointype & JT_LTORJ)==0 + || (selFlags & SF_NestedFrom)!=0 + || !inAnyUsingClause(zName,pFrom,pTabList->nSrc-i-1) + ) + ) + || IN_RENAME_OBJECT + ){ Expr *pLeft; pLeft = sqlcipher_sqlite3Expr(db, TK_ID, zTabName); pExpr = sqlcipher_sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); + if( IN_RENAME_OBJECT && pE->pLeft ){ + sqlcipher_sqlite3RenameTokenRemap(pParse, pLeft, pE->pLeft); + } if( zSchemaName ){ pLeft = sqlcipher_sqlite3Expr(db, TK_ID, zSchemaName); pExpr = sqlcipher_sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr); } - if( longNames ){ - zColname = sqlcipher_sqlite3MPrintf(db, "%s.%s", zTabName, zName); - zToFree = zColname; - } }else{ pExpr = pRight; } pNew = sqlcipher_sqlite3ExprListAppend(pParse, pNew, pExpr); - sqlcipher_sqlite3TokenInit(&sColname, zColname); - sqlcipher_sqlite3ExprListSetName(pParse, pNew, &sColname, 0); - if( pNew && (p->selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ - struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; - sqlcipher_sqlite3DbFree(db, pX->zEName); - if( pSub ){ - pX->zEName = sqlcipher_sqlite3DbStrDup(db, pSub->pEList->a[j].zEName); + if( pNew==0 ){ + break; /* OOM */ + } + pX = &pNew->a[pNew->nExpr-1]; + assert( pX->zEName==0 ); + if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ + if( pNestedFrom ){ + pX->zEName = sqlcipher_sqlite3DbStrDup(db, pNestedFrom->a[j].zEName); testcase( pX->zEName==0 ); }else{ pX->zEName = sqlcipher_sqlite3MPrintf(db, "%s.%s.%s", - zSchemaName, zTabName, zColname); + zSchemaName, zTabName, zName); testcase( pX->zEName==0 ); } - pX->eEName = ENAME_TAB; + pX->fg.eEName = ENAME_TAB; + if( (pFrom->fg.isUsing + && sqlcipher_sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0) + || (pUsing && sqlcipher_sqlite3IdListIndex(pUsing, zName)>=0) + || (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0 + ){ + pX->fg.bNoExpand = 1; + } + }else if( longNames ){ + pX->zEName = sqlcipher_sqlite3MPrintf(db, "%s.%s", zTabName, zName); + pX->fg.eEName = ENAME_NAME; + }else{ + pX->zEName = sqlcipher_sqlite3DbStrDup(db, zName); + pX->fg.eEName = ENAME_NAME; } - sqlcipher_sqlite3DbFree(db, zToFree); } } if( !tableSeen ){ @@ -139954,6 +147008,12 @@ static int selectExpander(Walker *pWalker, Select *p){ p->selFlags |= SF_ComplexResult; } } +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x100 ){ + SELECTTRACE(0x100,pParse,p,("After result-set wildcard expansion:\n")); + sqlcipher_sqlite3TreeViewSelect(0, p, 0); + } +#endif return WRC_Continue; } @@ -139990,7 +147050,7 @@ static void sqlcipher_sqlite3SelectExpand(Parse *pParse, Select *pSelect){ sqlcipher_sqlite3WalkSelect(&w, pSelect); } w.xSelectCallback = selectExpander; - w.xSelectCallback2 = selectPopWith; + w.xSelectCallback2 = sqlcipher_sqlite3SelectPopWith; w.eCode = 0; sqlcipher_sqlite3WalkSelect(&w, pSelect); } @@ -140014,7 +147074,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ Parse *pParse; int i; SrcList *pTabList; - struct SrcList_item *pFrom; + SrcItem *pFrom; assert( p->selFlags & SF_Resolved ); if( p->selFlags & SF_HasTypeInfo ) return; @@ -140075,12 +147135,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3SelectPrep( NameContext *pOuterNC /* Name context for container */ ){ assert( p!=0 || pParse->db->mallocFailed ); + assert( pParse->db->pParse==pParse ); if( pParse->db->mallocFailed ) return; if( p->selFlags & SF_HasTypeInfo ) return; sqlcipher_sqlite3SelectExpand(pParse, p); - if( pParse->nErr || pParse->db->mallocFailed ) return; + if( pParse->nErr ) return; sqlcipher_sqlite3ResolveSelectNames(pParse, p, pOuterNC); - if( pParse->nErr || pParse->db->mallocFailed ) return; + if( pParse->nErr ) return; sqlcipher_sqlite3SelectAddTypeInfo(pParse, p); } @@ -140097,8 +147158,10 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ int i; struct AggInfo_func *pFunc; int nReg = pAggInfo->nFunc + pAggInfo->nColumn; + assert( pParse->db->pParse==pParse ); + assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); if( nReg==0 ) return; - if( pParse->nErr || pParse->db->mallocFailed ) return; + if( pParse->nErr ) return; #ifdef SQLITE_DEBUG /* Verify that all AggInfo registers are within the range specified by ** AggInfo.mnReg..AggInfo.mxReg */ @@ -140116,15 +147179,17 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ for(pFunc=pAggInfo->aFunc, i=0; inFunc; i++, pFunc++){ if( pFunc->iDistinct>=0 ){ Expr *pE = pFunc->pFExpr; - assert( !ExprHasProperty(pE, EP_xIsSelect) ); + assert( ExprUseXList(pE) ); if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ sqlcipher_sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " "argument"); pFunc->iDistinct = -1; }else{ KeyInfo *pKeyInfo = sqlcipher_sqlite3KeyInfoFromExprList(pParse, pE->x.pList,0,0); - sqlcipher_sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, - (char*)pKeyInfo, P4_KEYINFO); + pFunc->iDistAddr = sqlcipher_sqlite3VdbeAddOp4(v, OP_OpenEphemeral, + pFunc->iDistinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO); + ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(DISTINCT)", + pFunc->pFunc->zName)); } } } @@ -140139,8 +147204,9 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ int i; struct AggInfo_func *pF; for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ - ExprList *pList = pF->pFExpr->x.pList; - assert( !ExprHasProperty(pF->pFExpr, EP_xIsSelect) ); + ExprList *pList; + assert( ExprUseXList(pF->pFExpr) ); + pList = pF->pFExpr->x.pList; sqlcipher_sqlite3VdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0); sqlcipher_sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); } @@ -140156,7 +147222,12 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ ** registers if register regAcc contains 0. The caller will take care ** of setting and clearing regAcc. */ -static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ +static void updateAccumulator( + Parse *pParse, + int regAcc, + AggInfo *pAggInfo, + int eDistinctType +){ Vdbe *v = pParse->pVdbe; int i; int regHit = 0; @@ -140169,9 +147240,10 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ int nArg; int addrNext = 0; int regAgg; - ExprList *pList = pF->pFExpr->x.pList; - assert( !ExprHasProperty(pF->pFExpr, EP_xIsSelect) ); + ExprList *pList; + assert( ExprUseXList(pF->pFExpr) ); assert( !IsWindowFunc(pF->pFExpr) ); + pList = pF->pFExpr->x.pList; if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){ Expr *pFilter = pF->pFExpr->y.pWin->pFilter; if( pAggInfo->nAccumulator @@ -140202,13 +147274,12 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ nArg = 0; regAgg = 0; } - if( pF->iDistinct>=0 ){ + if( pF->iDistinct>=0 && pList ){ if( addrNext==0 ){ addrNext = sqlcipher_sqlite3VdbeMakeLabel(pParse); } - testcase( nArg==0 ); /* Error condition */ - testcase( nArg>1 ); /* Also an error */ - codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg); + pF->iDistinct = codeDistinct(pParse, eDistinctType, + pF->iDistinct, addrNext, pList, regAgg); } if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ CollSeq *pColl = 0; @@ -140260,7 +147331,7 @@ static void explainSimpleCount( ){ if( pParse->explain==2 ){ int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx))); - sqlcipher_sqlite3VdbeExplain(pParse, 0, "SCAN TABLE %s%s%s", + sqlcipher_sqlite3VdbeExplain(pParse, 0, "SCAN %s%s%s", pTab->zName, bCover ? " USING COVERING INDEX " : "", bCover ? pIdx->zName : "" @@ -140285,8 +147356,16 @@ static void explainSimpleCount( static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){ if( pExpr->op!=TK_AND ){ Select *pS = pWalker->u.pSelect; + /* This routine is called before the HAVING clause of the current + ** SELECT is analyzed for aggregates. So if pExpr->pAggInfo is set + ** here, it indicates that the expression is a correlated reference to a + ** column from an outer aggregate query, or an aggregate function that + ** belongs to an outer query. Do not move the expression to the WHERE + ** clause in this obscure case, as doing so may corrupt the outer Select + ** statements AggInfo structure. */ if( sqlcipher_sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) && ExprAlwaysFalse(pExpr)==0 + && pExpr->pAggInfo==0 ){ sqlcipher_sqlite3 *db = pWalker->pParse->db; Expr *pNew = sqlcipher_sqlite3Expr(db, TK_INTEGER, "1"); @@ -140325,8 +147404,8 @@ static void havingToWhere(Parse *pParse, Select *p){ sWalker.xExprCallback = havingToWhereExprCb; sWalker.u.pSelect = p; sqlcipher_sqlite3WalkExpr(&sWalker, p->pHaving); -#if SELECTTRACE_ENABLED - if( sWalker.eCode && (sqlcipher_sqlite3_unsupported_selecttrace & 0x100)!=0 ){ +#if TREETRACE_ENABLED + if( sWalker.eCode && (sqlcipher_sqlite3TreeTrace & 0x100)!=0 ){ SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); sqlcipher_sqlite3TreeViewSelect(0, p, 0); } @@ -140338,11 +147417,13 @@ static void havingToWhere(Parse *pParse, Select *p){ ** If it is, then return the SrcList_item for the prior view. If it is not, ** then return 0. */ -static struct SrcList_item *isSelfJoinView( +static SrcItem *isSelfJoinView( SrcList *pTabList, /* Search for self-joins in this FROM clause */ - struct SrcList_item *pThis /* Search for prior reference to this subquery */ + SrcItem *pThis /* Search for prior reference to this subquery */ ){ - struct SrcList_item *pItem; + SrcItem *pItem; + assert( pThis->pSelect!=0 ); + if( pThis->pSelect->selFlags & SF_PushDown ) return 0; for(pItem = pTabList->a; pItempSelect==0 ) continue; @@ -140358,9 +147439,7 @@ static struct SrcList_item *isSelfJoinView( ** names in the same FROM clause. */ continue; } - if( sqlcipher_sqlite3ExprCompare(0, pThis->pSelect->pWhere, pS1->pWhere, -1) - || sqlcipher_sqlite3ExprCompare(0, pThis->pSelect->pHaving, pS1->pHaving, -1) - ){ + if( pItem->pSelect->selFlags & SF_PushDown ){ /* The view was modified by some other optimization such as ** pushDownWhereTerms() */ continue; @@ -140370,6 +147449,15 @@ static struct SrcList_item *isSelfJoinView( return 0; } +/* +** Deallocate a single AggInfo object +*/ +static void agginfoFree(sqlcipher_sqlite3 *db, AggInfo *p){ + sqlcipher_sqlite3DbFree(db, p->aCol); + sqlcipher_sqlite3DbFree(db, p->aFunc); + sqlcipher_sqlite3DbFreeNN(db, p); +} + #ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION /* ** Attempt to transform a query of the form @@ -140401,7 +147489,9 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ if( p->pGroupBy ) return 0; pExpr = p->pEList->a[0].pExpr; if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */ + assert( ExprUseUToken(pExpr) ); if( sqlcipher_sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Is count() */ + assert( ExprUseXList(pExpr) ); if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */ if( p->pSrc->nSrc!=1 ) return 0; /* One table in FROM */ pSub = p->pSrc->a[0].pSelect; @@ -140447,8 +147537,8 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ p->pEList->a[0].pExpr = pExpr; p->selFlags &= ~SF_Aggregate; -#if SELECTTRACE_ENABLED - if( sqlcipher_sqlite3_unsupported_selecttrace & 0x400 ){ +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); sqlcipher_sqlite3TreeViewSelect(0, p, 0); } @@ -140457,6 +147547,29 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ } #endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */ +/* +** If any term of pSrc, or any SF_NestedFrom sub-query, is not the same +** as pSrcItem but has the same alias as p0, then return true. +** Otherwise return false. +*/ +static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){ + int i; + for(i=0; inSrc; i++){ + SrcItem *p1 = &pSrc->a[i]; + if( p1==p0 ) continue; + if( p0->pTab==p1->pTab && 0==sqlcipher_sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ + return 1; + } + if( p1->pSelect + && (p1->pSelect->selFlags & SF_NestedFrom)!=0 + && sameSrcAlias(p0, p1->pSelect->pSrc) + ){ + return 1; + } + } + return 0; +} + /* ** Generate code for the SELECT statement given in the p argument. ** @@ -140494,15 +147607,21 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( u8 minMaxFlag; /* Flag for min/max queries */ db = pParse->db; + assert( pParse==db->pParse ); v = sqlcipher_sqlite3GetVdbe(pParse); - if( p==0 || db->mallocFailed || pParse->nErr ){ + if( p==0 || pParse->nErr ){ return 1; } + assert( db->mallocFailed==0 ); if( sqlcipher_sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; -#if SELECTTRACE_ENABLED +#if TREETRACE_ENABLED SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); - if( sqlcipher_sqlite3_unsupported_selecttrace & 0x100 ){ - sqlcipher_sqlite3TreeViewSelect(0, p, 0); + if( sqlcipher_sqlite3TreeTrace & 0x10100 ){ + if( (sqlcipher_sqlite3TreeTrace & 0x10001)==0x10000 ){ + sqlcipher_sqlite3TreeViewLine(0, "In sqlcipher_sqlite3Select() at %s:%d", + __FILE__, __LINE__); + } + sqlcipher_sqlite3ShowSelect(p); } #endif @@ -140515,53 +147634,72 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard || pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo ); /* All of these destinations are also able to ignore the ORDER BY clause */ - sqlcipher_sqlite3ExprListDelete(db, p->pOrderBy); - p->pOrderBy = 0; + if( p->pOrderBy ){ +#if TREETRACE_ENABLED + SELECTTRACE(1,pParse,p, ("dropping superfluous ORDER BY:\n")); + if( sqlcipher_sqlite3TreeTrace & 0x100 ){ + sqlcipher_sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY"); + } +#endif + sqlcipher_sqlite3ParserAddCleanup(pParse, + (void(*)(sqlcipher_sqlite3*,void*))sqlcipher_sqlite3ExprListDelete, + p->pOrderBy); + testcase( pParse->earlyCleanup ); + p->pOrderBy = 0; + } p->selFlags &= ~SF_Distinct; p->selFlags |= SF_NoopOrderBy; } sqlcipher_sqlite3SelectPrep(pParse, p, 0); - if( pParse->nErr || db->mallocFailed ){ + if( pParse->nErr ){ goto select_end; } + assert( db->mallocFailed==0 ); assert( p->pEList!=0 ); -#if SELECTTRACE_ENABLED - if( sqlcipher_sqlite3_unsupported_selecttrace & 0x104 ){ +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x104 ){ SELECTTRACE(0x104,pParse,p, ("after name resolution:\n")); sqlcipher_sqlite3TreeViewSelect(0, p, 0); } #endif - /* If the SF_UpdateFrom flag is set, then this function is being called + /* If the SF_UFSrcCheck flag is set, then this function is being called ** as part of populating the temp table for an UPDATE...FROM statement. ** In this case, it is an error if the target object (pSrc->a[0]) name - ** or alias is duplicated within FROM clause (pSrc->a[1..n]). */ - if( p->selFlags & SF_UpdateFrom ){ - struct SrcList_item *p0 = &p->pSrc->a[0]; - for(i=1; ipSrc->nSrc; i++){ - struct SrcList_item *p1 = &p->pSrc->a[i]; - if( p0->pTab==p1->pTab && 0==sqlcipher_sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ - sqlcipher_sqlite3ErrorMsg(pParse, - "target object/alias may not appear in FROM clause: %s", - p0->zAlias ? p0->zAlias : p0->pTab->zName - ); - goto select_end; - } + ** or alias is duplicated within FROM clause (pSrc->a[1..n]). + ** + ** Postgres disallows this case too. The reason is that some other + ** systems handle this case differently, and not all the same way, + ** which is just confusing. To avoid this, we follow PG's lead and + ** disallow it altogether. */ + if( p->selFlags & SF_UFSrcCheck ){ + SrcItem *p0 = &p->pSrc->a[0]; + if( sameSrcAlias(p0, p->pSrc) ){ + sqlcipher_sqlite3ErrorMsg(pParse, + "target object/alias may not appear in FROM clause: %s", + p0->zAlias ? p0->zAlias : p0->pTab->zName + ); + goto select_end; } + + /* Clear the SF_UFSrcCheck flag. The check has already been performed, + ** and leaving this flag set can cause errors if a compound sub-query + ** in p->pSrc is flattened into this query and this function called + ** again as part of compound SELECT processing. */ + p->selFlags &= ~SF_UFSrcCheck; } if( pDest->eDest==SRT_Output ){ - generateColumnNames(pParse, p); + sqlcipher_sqlite3GenerateColumnNames(pParse, p); } #ifndef SQLITE_OMIT_WINDOWFUNC - rc = sqlcipher_sqlite3WindowRewrite(pParse, p); - if( rc ){ - assert( db->mallocFailed || pParse->nErr>0 ); + if( sqlcipher_sqlite3WindowRewrite(pParse, p) ){ + assert( pParse->nErr ); goto select_end; } -#if SELECTTRACE_ENABLED - if( p->pWin && (sqlcipher_sqlite3_unsupported_selecttrace & 0x108)!=0 ){ +#if TREETRACE_ENABLED + if( p->pWin && (sqlcipher_sqlite3TreeTrace & 0x108)!=0 ){ SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n")); sqlcipher_sqlite3TreeViewSelect(0, p, 0); } @@ -140577,7 +147715,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && inSrc; i++){ - struct SrcList_item *pItem = &pTabList->a[i]; + SrcItem *pItem = &pTabList->a[i]; Select *pSub = pItem->pSelect; Table *pTab = pItem->pTab; @@ -140589,14 +147727,16 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( /* Convert LEFT JOIN into JOIN if there are terms of the right table ** of the LEFT JOIN used in the WHERE clause. */ - if( (pItem->fg.jointype & JT_LEFT)!=0 + if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==JT_LEFT && sqlcipher_sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor) && OptimizationEnabled(db, SQLITE_SimplifyJoin) ){ SELECTTRACE(0x100,pParse,p, ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); - unsetJoinExpr(p->pWhere, pItem->iCursor); + assert( pItem->iCursor>=0 ); + unsetJoinExpr(p->pWhere, pItem->iCursor, + pTabList->a[0].fg.jointype & JT_LTORJ); } /* No futher action if this term of the FROM clause is no a subquery */ @@ -140620,6 +147760,41 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( if( (pSub->selFlags & SF_Aggregate)!=0 ) continue; assert( pSub->pGroupBy==0 ); + /* If a FROM-clause subquery has an ORDER BY clause that is not + ** really doing anything, then delete it now so that it does not + ** interfere with query flattening. See the discussion at + ** https://sqlite.org/forum/forumpost/2d76f2bcf65d256a + ** + ** Beware of these cases where the ORDER BY clause may not be safely + ** omitted: + ** + ** (1) There is also a LIMIT clause + ** (2) The subquery was added to help with window-function + ** processing + ** (3) The subquery is in the FROM clause of an UPDATE + ** (4) The outer query uses an aggregate function other than + ** the built-in count(), min(), or max(). + ** (5) The ORDER BY isn't going to accomplish anything because + ** one of: + ** (a) The outer query has a different ORDER BY clause + ** (b) The subquery is part of a join + ** See forum post 062d576715d277c8 + */ + if( pSub->pOrderBy!=0 + && (p->pOrderBy!=0 || pTabList->nSrc>1) /* Condition (5) */ + && pSub->pLimit==0 /* Condition (1) */ + && (pSub->selFlags & SF_OrderByReqd)==0 /* Condition (2) */ + && (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */ + && OptimizationEnabled(db, SQLITE_OmitOrderBy) + ){ + SELECTTRACE(0x100,pParse,p, + ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1)); + sqlcipher_sqlite3ParserAddCleanup(pParse, + (void(*)(sqlcipher_sqlite3*,void*))sqlcipher_sqlite3ExprListDelete, + pSub->pOrderBy); + pSub->pOrderBy = 0; + } + /* If the outer query contains a "complex" result set (that is, ** if the result set of the outer query uses functions or subqueries) ** and if the subquery contains an ORDER BY clause and if @@ -140642,7 +147817,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( && i==0 && (p->selFlags & SF_ComplexResult)!=0 && (pTabList->nSrc==1 - || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) + || (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0) ){ continue; } @@ -140666,9 +147841,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( */ if( p->pPrior ){ rc = multiSelect(pParse, p, pDest); -#if SELECTTRACE_ENABLED +#if TREETRACE_ENABLED SELECTTRACE(0x1,pParse,p,("end compound-select processing\n")); - if( (sqlcipher_sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ + if( (sqlcipher_sqlite3TreeTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ sqlcipher_sqlite3TreeViewSelect(0, p, 0); } #endif @@ -140682,12 +147857,13 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( ** as the equivalent optimization will be handled by query planner in ** sqlcipher_sqlite3WhereBegin(). */ - if( pTabList->nSrc>1 + if( p->pWhere!=0 + && p->pWhere->op==TK_AND && OptimizationEnabled(db, SQLITE_PropagateConst) && propagateConstants(pParse, p) ){ -#if SELECTTRACE_ENABLED - if( sqlcipher_sqlite3_unsupported_selecttrace & 0x100 ){ +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x100 ){ SELECTTRACE(0x100,pParse,p,("After constant propagation:\n")); sqlcipher_sqlite3TreeViewSelect(0, p, 0); } @@ -140711,7 +147887,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( ** (2) Generate code for all sub-queries */ for(i=0; inSrc; i++){ - struct SrcList_item *pItem = &pTabList->a[i]; + SrcItem *pItem = &pTabList->a[i]; + SrcItem *pPrior; SelectDest dest; Select *pSub; #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) @@ -140744,19 +147921,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( pSub = pItem->pSelect; if( pSub==0 ) continue; - /* The code for a subquery should only be generated once, though it is - ** technically harmless for it to be generated multiple times. The - ** following assert() will detect if something changes to cause - ** the same subquery to be coded multiple times, as a signal to the - ** developers to try to optimize the situation. - ** - ** Update 2019-07-24: - ** See ticket https://sqlite.org/src/tktview/c52b09c7f38903b1311cec40. - ** The dbsqlfuzz fuzzer found a case where the same subquery gets - ** coded twice. So this assert() now becomes a testcase(). It should - ** be very rare, though. - */ - testcase( pItem->addrFillSub!=0 ); + /* The code for a subquery should only be generated once. */ + assert( pItem->addrFillSub==0 ); /* Increment Parse.nHeight by the height of the largest expression ** tree referred to by this, the parent select. The child select @@ -140771,16 +147937,18 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( ** inside the subquery. This can help the subquery to run more efficiently. */ if( OptimizationEnabled(db, SQLITE_PushDown) - && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, - (pItem->fg.jointype & JT_OUTER)!=0) + && (pItem->fg.isCte==0 + || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2)) + && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem) ){ -#if SELECTTRACE_ENABLED - if( sqlcipher_sqlite3_unsupported_selecttrace & 0x100 ){ +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x100 ){ SELECTTRACE(0x100,pParse,p, ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); sqlcipher_sqlite3TreeViewSelect(0, p, 0); } #endif + assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 ); }else{ SELECTTRACE(0x100,pParse,p,("Push-down not possible\n")); } @@ -140790,16 +147958,19 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( /* Generate code to implement the subquery ** - ** The subquery is implemented as a co-routine if the subquery is - ** guaranteed to be the outer loop (so that it does not need to be - ** computed more than once) + ** The subquery is implemented as a co-routine if all of the following are + ** true: ** - ** TODO: Are there other reasons beside (1) to use a co-routine - ** implementation? + ** (1) the subquery is guaranteed to be the outer loop (so that + ** it does not need to be computed more than once), and + ** (2) the subquery is not a CTE that should be materialized + ** (3) the subquery is not part of a left operand for a RIGHT JOIN */ if( i==0 && (pTabList->nSrc==1 - || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */ + || (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0) /* (1) */ + && (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) /* (2) */ + && (pTabList->a[0].fg.jointype & JT_LTORJ)==0 /* (3) */ ){ /* Implement a co-routine that will return a single row of the result ** set on each invocation. @@ -140808,10 +147979,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( pItem->regReturn = ++pParse->nMem; sqlcipher_sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop); - VdbeComment((v, "%s", pItem->pTab->zName)); + VdbeComment((v, "%!S", pItem)); pItem->addrFillSub = addrTop; sqlcipher_sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); - ExplainQueryPlan((pParse, 1, "CO-ROUTINE %u", pSub->selId)); + ExplainQueryPlan((pParse, 1, "CO-ROUTINE %!S", pItem)); sqlcipher_sqlite3Select(pParse, pSub, &dest); pItem->pTab->nRowLogEst = pSub->nSelectRow; pItem->fg.viaCoroutine = 1; @@ -140819,46 +147990,62 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( sqlcipher_sqlite3VdbeEndCoroutine(v, pItem->regReturn); sqlcipher_sqlite3VdbeJumpHere(v, addrTop-1); sqlcipher_sqlite3ClearTempRegCache(pParse); - }else{ - /* Generate a subroutine that will fill an ephemeral table with - ** the content of this subquery. pItem->addrFillSub will point - ** to the address of the generated subroutine. pItem->regReturn - ** is a register allocated to hold the subroutine return address - */ + }else if( pItem->fg.isCte && pItem->u2.pCteUse->addrM9e>0 ){ + /* This is a CTE for which materialization code has already been + ** generated. Invoke the subroutine to compute the materialization, + ** the make the pItem->iCursor be a copy of the ephemerial table that + ** holds the result of the materialization. */ + CteUse *pCteUse = pItem->u2.pCteUse; + sqlcipher_sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e); + if( pItem->iCursor!=pCteUse->iCur ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pCteUse->iCur); + VdbeComment((v, "%!S", pItem)); + } + pSub->nSelectRow = pCteUse->nRowEst; + }else if( (pPrior = isSelfJoinView(pTabList, pItem))!=0 ){ + /* This view has already been materialized by a prior entry in + ** this same FROM clause. Reuse it. */ + if( pPrior->addrFillSub ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub); + } + sqlcipher_sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); + pSub->nSelectRow = pPrior->pSelect->nSelectRow; + }else{ + /* Materialize the view. If the view is not correlated, generate a + ** subroutine to do the materialization so that subsequent uses of + ** the same view can reuse the materialization. */ int topAddr; int onceAddr = 0; - int retAddr; - struct SrcList_item *pPrior; - testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */ pItem->regReturn = ++pParse->nMem; - topAddr = sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn); + topAddr = sqlcipher_sqlite3VdbeAddOp0(v, OP_Goto); pItem->addrFillSub = topAddr+1; + pItem->fg.isMaterialized = 1; if( pItem->fg.isCorrelated==0 ){ /* If the subquery is not correlated and if we are not inside of ** a trigger, then we only need to compute the value of the subquery ** once. */ onceAddr = sqlcipher_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); - VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName)); - }else{ - VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName)); - } - pPrior = isSelfJoinView(pTabList, pItem); - if( pPrior ){ - sqlcipher_sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); - assert( pPrior->pSelect!=0 ); - pSub->nSelectRow = pPrior->pSelect->nSelectRow; + VdbeComment((v, "materialize %!S", pItem)); }else{ - sqlcipher_sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); - ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId)); - sqlcipher_sqlite3Select(pParse, pSub, &dest); + VdbeNoopComment((v, "materialize %!S", pItem)); } + sqlcipher_sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); + ExplainQueryPlan((pParse, 1, "MATERIALIZE %!S", pItem)); + sqlcipher_sqlite3Select(pParse, pSub, &dest); pItem->pTab->nRowLogEst = pSub->nSelectRow; if( onceAddr ) sqlcipher_sqlite3VdbeJumpHere(v, onceAddr); - retAddr = sqlcipher_sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); - VdbeComment((v, "end %s", pItem->pTab->zName)); - sqlcipher_sqlite3VdbeChangeP1(v, topAddr, retAddr); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Return, pItem->regReturn, topAddr+1); + VdbeComment((v, "end %!S", pItem)); + sqlcipher_sqlite3VdbeJumpHere(v, topAddr); sqlcipher_sqlite3ClearTempRegCache(pParse); + if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ + CteUse *pCteUse = pItem->u2.pCteUse; + pCteUse->addrM9e = pItem->addrFillSub; + pCteUse->regRtn = pItem->regReturn; + pCteUse->iCur = pItem->iCursor; + pCteUse->nRowEst = pSub->nSelectRow; + } } if( db->mallocFailed ) goto select_end; pParse->nHeight -= sqlcipher_sqlite3SelectExprHeight(p); @@ -140874,8 +148061,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( pHaving = p->pHaving; sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; -#if SELECTTRACE_ENABLED - if( sqlcipher_sqlite3_unsupported_selecttrace & 0x400 ){ +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n")); sqlcipher_sqlite3TreeViewSelect(0, p, 0); } @@ -140909,9 +148096,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( ** the sDistinct.isTnct is still set. Hence, isTnct represents the ** original setting of the SF_Distinct flag, not the current setting */ assert( sDistinct.isTnct ); + sDistinct.isTnct = 2; -#if SELECTTRACE_ENABLED - if( sqlcipher_sqlite3_unsupported_selecttrace & 0x400 ){ +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n")); sqlcipher_sqlite3TreeViewSelect(0, p, 0); } @@ -140944,6 +148132,18 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( */ if( pDest->eDest==SRT_EphemTab ){ sqlcipher_sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr); + if( p->selFlags & SF_NestedFrom ){ + /* Delete or NULL-out result columns that will never be used */ + int ii; + for(ii=pEList->nExpr-1; ii>0 && pEList->a[ii].fg.bUsed==0; ii--){ + sqlcipher_sqlite3ExprDelete(db, pEList->a[ii].pExpr); + sqlcipher_sqlite3DbFree(db, pEList->a[ii].zEName); + pEList->nExpr--; + } + for(ii=0; iinExpr; ii++){ + if( pEList->a[ii].fg.bUsed==0 ) pEList->a[ii].pExpr->op = TK_NULL; + } + } } /* Set the limiter. @@ -140988,7 +148188,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( /* Begin the database scan. */ SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlcipher_sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy, - p->pEList, wctrlFlags, p->nSelectRow); + p->pEList, p, wctrlFlags, p->nSelectRow); if( pWInfo==0 ) goto select_end; if( sqlcipher_sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){ p->nSelectRow = sqlcipher_sqlite3WhereOutputRowCount(pWInfo); @@ -141003,6 +148203,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( sSort.pOrderBy = 0; } } + SELECTTRACE(1,pParse,p,("WhereBegin returns\n")); /* If sorting index that was created by a prior OP_OpenEphemeral ** instruction ended up not being needed, then change the OP_OpenEphemeral @@ -141041,6 +148242,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( /* End the database scan loop. */ + SELECTTRACE(1,pParse,p,("WhereEnd\n")); sqlcipher_sqlite3WhereEnd(pWInfo); } }else{ @@ -141091,8 +148293,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( ** ORDER BY to maximize the chances of rows being delivered in an ** order that makes the ORDER BY redundant. */ for(ii=0; iinExpr; ii++){ - u8 sortFlags = sSort.pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_DESC; - pGroupBy->a[ii].sortFlags = sortFlags; + u8 sortFlags; + sortFlags = sSort.pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC; + pGroupBy->a[ii].fg.sortFlags = sortFlags; } if( sqlcipher_sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){ orderByGrp = 1; @@ -141111,11 +148314,14 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( ** SELECT statement. */ pAggInfo = sqlcipher_sqlite3DbMallocZero(db, sizeof(*pAggInfo) ); - if( pAggInfo==0 ){ + if( pAggInfo ){ + sqlcipher_sqlite3ParserAddCleanup(pParse, + (void(*)(sqlcipher_sqlite3*,void*))agginfoFree, pAggInfo); + testcase( pParse->earlyCleanup ); + } + if( db->mallocFailed ){ goto select_end; } - pAggInfo->pNext = pParse->pAggList; - pParse->pAggList = pAggInfo; pAggInfo->selId = p->selId; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; @@ -141145,7 +148351,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( } for(i=0; inFunc; i++){ Expr *pExpr = pAggInfo->aFunc[i].pFExpr; - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( ExprUseXList(pExpr) ); sNC.ncFlags |= NC_InAggFunc; sqlcipher_sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList); #ifndef SQLITE_OMIT_WINDOWFUNC @@ -141158,11 +148364,15 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( } pAggInfo->mxReg = pParse->nMem; if( db->mallocFailed ) goto select_end; -#if SELECTTRACE_ENABLED - if( sqlcipher_sqlite3_unsupported_selecttrace & 0x400 ){ +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x400 ){ int ii; SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); sqlcipher_sqlite3TreeViewSelect(0, p, 0); + if( minMaxFlag ){ + sqlcipher_sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag); + sqlcipher_sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY"); + } for(ii=0; iinColumn; ii++){ sqlcipher_sqlite3DebugPrintf("agg-column[%d] iMem=%d\n", ii, pAggInfo->aCol[ii].iMem); @@ -141190,6 +148400,22 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( int addrSortingIdx; /* The OP_OpenEphemeral for the sorting index */ int addrReset; /* Subroutine for resetting the accumulator */ int regReset; /* Return address register for reset subroutine */ + ExprList *pDistinct = 0; + u16 distFlag = 0; + int eDist = WHERE_DISTINCT_NOOP; + + if( pAggInfo->nFunc==1 + && pAggInfo->aFunc[0].iDistinct>=0 + && ALWAYS(pAggInfo->aFunc[0].pFExpr!=0) + && ALWAYS(ExprUseXList(pAggInfo->aFunc[0].pFExpr)) + && pAggInfo->aFunc[0].pFExpr->x.pList!=0 + ){ + Expr *pExpr = pAggInfo->aFunc[0].pFExpr->x.pList->a[0].pExpr; + pExpr = sqlcipher_sqlite3ExprDup(db, pExpr, 0); + pDistinct = sqlcipher_sqlite3ExprListDup(db, pGroupBy, 0); + pDistinct = sqlcipher_sqlite3ExprListAppend(pParse, pDistinct, pExpr); + distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0; + } /* If there is a GROUP BY clause we might need a sorting index to ** implement it. Allocate that sorting index now. If it turns out @@ -141226,10 +148452,16 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( */ sqlcipher_sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); SELECTTRACE(1,pParse,p,("WhereBegin\n")); - pWInfo = sqlcipher_sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, - WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0 + pWInfo = sqlcipher_sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct, + 0, (sDistinct.isTnct==2 ? WHERE_DISTINCTBY : WHERE_GROUPBY) + | (orderByGrp ? WHERE_SORTBYGROUP : 0) | distFlag, 0 ); - if( pWInfo==0 ) goto select_end; + if( pWInfo==0 ){ + sqlcipher_sqlite3ExprListDelete(db, pDistinct); + goto select_end; + } + eDist = sqlcipher_sqlite3WhereIsDistinct(pWInfo); + SELECTTRACE(1,pParse,p,("WhereBegin returns\n")); if( sqlcipher_sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){ /* The optimizer is able to deliver rows in group by order so ** we do not have to sort. The OP_OpenEphemeral table will be @@ -141278,6 +148510,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( sqlcipher_sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord); sqlcipher_sqlite3ReleaseTempReg(pParse, regRecord); sqlcipher_sqlite3ReleaseTempRange(pParse, regBase, nCol); + SELECTTRACE(1,pParse,p,("WhereEnd\n")); sqlcipher_sqlite3WhereEnd(pWInfo); pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++; sortOut = sqlcipher_sqlite3GetTempReg(pParse); @@ -141345,19 +148578,21 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( ** the current row */ sqlcipher_sqlite3VdbeJumpHere(v, addr1); - updateAccumulator(pParse, iUseFlag, pAggInfo); + updateAccumulator(pParse, iUseFlag, pAggInfo, eDist); sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag); VdbeComment((v, "indicate data in accumulator")); /* End of the loop */ if( groupBySort ){ - sqlcipher_sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx, addrTopOfLoop); + sqlcipher_sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop); VdbeCoverage(v); }else{ + SELECTTRACE(1,pParse,p,("WhereEnd\n")); sqlcipher_sqlite3WhereEnd(pWInfo); sqlcipher_sqlite3VdbeChangeToNoop(v, addrSortingIdx); } + sqlcipher_sqlite3ExprListDelete(db, pDistinct); /* Output the final row of result */ @@ -141401,6 +148636,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( VdbeComment((v, "indicate accumulator empty")); sqlcipher_sqlite3VdbeAddOp1(v, OP_Return, regReset); + if( distFlag!=0 && eDist!=WHERE_DISTINCT_NOOP ){ + struct AggInfo_func *pF = &pAggInfo->aFunc[0]; + fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); + } } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ else { Table *pTab; @@ -141464,7 +148703,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( explainSimpleCount(pParse, pTab, pBest); }else{ int regAcc = 0; /* "populate accumulators" flag */ - int addrSkip; + ExprList *pDistinct = 0; + u16 distFlag = 0; + int eDist; /* If there are accumulator registers but no min() or max() functions ** without FILTER clauses, allocate register regAcc. Register regAcc @@ -141488,6 +148729,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( regAcc = ++pParse->nMem; sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 0, regAcc); } + }else if( pAggInfo->nFunc==1 && pAggInfo->aFunc[0].iDistinct>=0 ){ + assert( ExprUseXList(pAggInfo->aFunc[0].pFExpr) ); + pDistinct = pAggInfo->aFunc[0].pFExpr->x.pList; + distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0; } /* This case runs if the aggregate has no GROUP BY clause. The @@ -141507,16 +148752,25 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlcipher_sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy, - 0, minMaxFlag, 0); + pDistinct, 0, minMaxFlag|distFlag, 0); if( pWInfo==0 ){ goto select_end; } - updateAccumulator(pParse, regAcc, pAggInfo); + SELECTTRACE(1,pParse,p,("WhereBegin returns\n")); + eDist = sqlcipher_sqlite3WhereIsDistinct(pWInfo); + updateAccumulator(pParse, regAcc, pAggInfo, eDist); + if( eDist!=WHERE_DISTINCT_NOOP ){ + struct AggInfo_func *pF = pAggInfo->aFunc; + if( pF ){ + fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); + } + } + if( regAcc ) sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc); - addrSkip = sqlcipher_sqlite3WhereOrderByLimitOptLabel(pWInfo); - if( addrSkip!=sqlcipher_sqlite3WhereContinueLabel(pWInfo) ){ - sqlcipher_sqlite3VdbeGoto(v, addrSkip); + if( minMaxFlag ){ + sqlcipher_sqlite3WhereMinMaxOptEarlyOut(v, pWInfo); } + SELECTTRACE(1,pParse,p,("WhereEnd\n")); sqlcipher_sqlite3WhereEnd(pWInfo); finalizeAggFunctions(pParse, pAggInfo); } @@ -141556,29 +148810,29 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Select( ** successful coding of the SELECT. */ select_end: + assert( db->mallocFailed==0 || db->mallocFailed==1 ); + assert( db->mallocFailed==0 || pParse->nErr!=0 ); sqlcipher_sqlite3ExprListDelete(db, pMinMaxOrderBy); #ifdef SQLITE_DEBUG if( pAggInfo && !db->mallocFailed ){ for(i=0; inColumn; i++){ Expr *pExpr = pAggInfo->aCol[i].pCExpr; - assert( pExpr!=0 || db->mallocFailed ); - if( pExpr==0 ) continue; + assert( pExpr!=0 ); assert( pExpr->pAggInfo==pAggInfo ); assert( pExpr->iAgg==i ); } for(i=0; inFunc; i++){ Expr *pExpr = pAggInfo->aFunc[i].pFExpr; - assert( pExpr!=0 || db->mallocFailed ); - if( pExpr==0 ) continue; + assert( pExpr!=0 ); assert( pExpr->pAggInfo==pAggInfo ); assert( pExpr->iAgg==i ); } } #endif -#if SELECTTRACE_ENABLED +#if TREETRACE_ENABLED SELECTTRACE(0x1,pParse,p,("end processing\n")); - if( (sqlcipher_sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ + if( (sqlcipher_sqlite3TreeTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ sqlcipher_sqlite3TreeViewSelect(0, p, 0); } #endif @@ -141839,28 +149093,48 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DeleteTriggerStep(sqlcipher_sqlite3 *db, Tr ** pTab as well as the triggers lised in pTab->pTrigger. */ SQLITE_PRIVATE Trigger *sqlcipher_sqlite3TriggerList(Parse *pParse, Table *pTab){ - Schema * const pTmpSchema = pParse->db->aDb[1].pSchema; - Trigger *pList = 0; /* List of triggers to return */ - - if( pParse->disableTriggers ){ - return 0; + Schema *pTmpSchema; /* Schema of the pTab table */ + Trigger *pList; /* List of triggers to return */ + HashElem *p; /* Loop variable for TEMP triggers */ + + assert( pParse->disableTriggers==0 ); + pTmpSchema = pParse->db->aDb[1].pSchema; + p = sqliteHashFirst(&pTmpSchema->trigHash); + pList = pTab->pTrigger; + while( p ){ + Trigger *pTrig = (Trigger *)sqliteHashData(p); + if( pTrig->pTabSchema==pTab->pSchema + && pTrig->table + && 0==sqlcipher_sqlite3StrICmp(pTrig->table, pTab->zName) + && pTrig->pTabSchema!=pTmpSchema + ){ + pTrig->pNext = pList; + pList = pTrig; + }else if( pTrig->op==TK_RETURNING ){ +#ifndef SQLITE_OMIT_VIRTUALTABLE + assert( pParse->db->pVtabCtx==0 ); +#endif + assert( pParse->bReturning ); + assert( &(pParse->u1.pReturning->retTrig) == pTrig ); + pTrig->table = pTab->zName; + pTrig->pTabSchema = pTab->pSchema; + pTrig->pNext = pList; + pList = pTrig; + } + p = sqliteHashNext(p); } - - if( pTmpSchema!=pTab->pSchema ){ - HashElem *p; - assert( sqlcipher_sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) ); - for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ - Trigger *pTrig = (Trigger *)sqliteHashData(p); - if( pTrig->pTabSchema==pTab->pSchema - && 0==sqlcipher_sqlite3StrICmp(pTrig->table, pTab->zName) - ){ - pTrig->pNext = (pList ? pList : pTab->pTrigger); - pList = pTrig; - } +#if 0 + if( pList ){ + Trigger *pX; + printf("Triggers for %s:", pTab->zName); + for(pX=pList; pX; pX=pX->pNext){ + printf(" %s", pX->zName); } + printf("\n"); + fflush(stdout); } - - return (pList ? pList : pTab->pTrigger); +#endif + return pList; } /* @@ -141987,14 +149261,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3BeginTrigger( /* INSTEAD of triggers are only for views and views only support INSTEAD ** of triggers. */ - if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ + if( IsView(pTab) && tr_tm!=TK_INSTEAD ){ sqlcipher_sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", - (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); + (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName->a); goto trigger_orphan_error; } - if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ + if( !IsView(pTab) && tr_tm==TK_INSTEAD ){ sqlcipher_sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" - " trigger on table: %S", pTableName, 0); + " trigger on table: %S", pTableName->a); goto trigger_orphan_error; } @@ -142122,6 +149396,23 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FinishTrigger( Vdbe *v; char *z; + /* If this is a new CREATE TABLE statement, and if shadow tables + ** are read-only, and the trigger makes a change to a shadow table, + ** then raise an error - do not allow the trigger to be created. */ + if( sqlcipher_sqlite3ReadOnlyShadowTables(db) ){ + TriggerStep *pStep; + for(pStep=pTrig->step_list; pStep; pStep=pStep->pNext){ + if( pStep->zTarget!=0 + && sqlcipher_sqlite3ShadowTableName(db, pStep->zTarget) + ){ + sqlcipher_sqlite3ErrorMsg(pParse, + "trigger \"%s\" may not write to shadow table \"%s\"", + pTrig->zName, pStep->zTarget); + goto triggerfinish_cleanup; + } + } + } + /* Make an entry in the sqlite_schema table */ v = sqlcipher_sqlite3GetVdbe(pParse); if( v==0 ) goto triggerfinish_cleanup; @@ -142129,14 +149420,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3FinishTrigger( z = sqlcipher_sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); testcase( z==0 ); sqlcipher_sqlite3NestedParse(pParse, - "INSERT INTO %Q." DFLT_SCHEMA_TABLE + "INSERT INTO %Q." LEGACY_SCHEMA_TABLE " VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", db->aDb[iDb].zDbSName, zName, pTrig->table, z); sqlcipher_sqlite3DbFree(db, z); sqlcipher_sqlite3ChangeCookie(pParse, iDb); sqlcipher_sqlite3VdbeAddParseSchemaOp(v, iDb, - sqlcipher_sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName)); + sqlcipher_sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName), 0); } if( db->init.busy ){ @@ -142214,6 +149505,7 @@ static TriggerStep *triggerStepAllocate( sqlcipher_sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; + if( pParse->nErr ) return 0; pTriggerStep = sqlcipher_sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1); if( pTriggerStep ){ char *z = (char*)&pTriggerStep[1]; @@ -142284,7 +149576,7 @@ SQLITE_PRIVATE TriggerStep *sqlcipher_sqlite3TriggerInsertStep( SQLITE_PRIVATE TriggerStep *sqlcipher_sqlite3TriggerUpdateStep( Parse *pParse, /* Parser */ Token *pTableName, /* Name of the table to be updated */ - SrcList *pFrom, + SrcList *pFrom, /* FROM clause for an UPDATE-FROM, or NULL */ ExprList *pEList, /* The SET clause: list of column and new values */ Expr *pWhere, /* The WHERE clause */ u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ @@ -142349,7 +149641,7 @@ SQLITE_PRIVATE TriggerStep *sqlcipher_sqlite3TriggerDeleteStep( ** Recursively delete a Trigger structure */ SQLITE_PRIVATE void sqlcipher_sqlite3DeleteTrigger(sqlcipher_sqlite3 *db, Trigger *pTrigger){ - if( pTrigger==0 ) return; + if( pTrigger==0 || pTrigger->bReturning ) return; sqlcipher_sqlite3DeleteTriggerStep(db, pTrigger->step_list); sqlcipher_sqlite3DbFree(db, pTrigger->zName); sqlcipher_sqlite3DbFree(db, pTrigger->table); @@ -142391,7 +149683,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DropTrigger(Parse *pParse, SrcList *pName, } if( !pTrigger ){ if( !noErr ){ - sqlcipher_sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0); + sqlcipher_sqlite3ErrorMsg(pParse, "no such trigger: %S", pName->a); }else{ sqlcipher_sqlite3CodeVerifyNamedSchema(pParse, zDb); } @@ -142443,7 +149735,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTri */ if( (v = sqlcipher_sqlite3GetVdbe(pParse))!=0 ){ sqlcipher_sqlite3NestedParse(pParse, - "DELETE FROM %Q." DFLT_SCHEMA_TABLE " WHERE name=%Q AND type='trigger'", + "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='trigger'", db->aDb[iDb].zDbSName, pTrigger->zName ); sqlcipher_sqlite3ChangeCookie(pParse, iDb); @@ -142497,13 +149789,22 @@ static int checkColumnOverlap(IdList *pIdList, ExprList *pEList){ return 0; } +/* +** Return true if any TEMP triggers exist +*/ +static int tempTriggersExist(sqlcipher_sqlite3 *db){ + if( NEVER(db->aDb[1].pSchema==0) ) return 0; + if( sqliteHashFirst(&db->aDb[1].pSchema->trigHash)==0 ) return 0; + return 1; +} + /* ** Return a list of all triggers on table pTab if there exists at least ** one trigger that must be fired when an operation of type 'op' is ** performed on the table, and, if that operation is an UPDATE, if at ** least one of the columns in pChanges is being modified. */ -SQLITE_PRIVATE Trigger *sqlcipher_sqlite3TriggersExist( +static SQLITE_NOINLINE Trigger *triggersReallyExist( Parse *pParse, /* Parse context */ Table *pTab, /* The table the contains the triggers */ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ @@ -142514,20 +149815,74 @@ SQLITE_PRIVATE Trigger *sqlcipher_sqlite3TriggersExist( Trigger *pList = 0; Trigger *p; - if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){ - pList = sqlcipher_sqlite3TriggerList(pParse, pTab); - } - assert( pList==0 || IsVirtual(pTab)==0 ); - for(p=pList; p; p=p->pNext){ - if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){ - mask |= p->tr_tm; + pList = sqlcipher_sqlite3TriggerList(pParse, pTab); + assert( pList==0 || IsVirtual(pTab)==0 + || (pList->bReturning && pList->pNext==0) ); + if( pList!=0 ){ + p = pList; + if( (pParse->db->flags & SQLITE_EnableTrigger)==0 + && pTab->pTrigger!=0 + ){ + /* The SQLITE_DBCONFIG_ENABLE_TRIGGER setting is off. That means that + ** only TEMP triggers are allowed. Truncate the pList so that it + ** includes only TEMP triggers */ + if( pList==pTab->pTrigger ){ + pList = 0; + goto exit_triggers_exist; + } + while( ALWAYS(p->pNext) && p->pNext!=pTab->pTrigger ) p = p->pNext; + p->pNext = 0; + p = pList; } + do{ + if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){ + mask |= p->tr_tm; + }else if( p->op==TK_RETURNING ){ + /* The first time a RETURNING trigger is seen, the "op" value tells + ** us what time of trigger it should be. */ + assert( sqlcipher_sqlite3IsToplevel(pParse) ); + p->op = op; + if( IsVirtual(pTab) ){ + if( op!=TK_INSERT ){ + sqlcipher_sqlite3ErrorMsg(pParse, + "%s RETURNING is not available on virtual tables", + op==TK_DELETE ? "DELETE" : "UPDATE"); + } + p->tr_tm = TRIGGER_BEFORE; + }else{ + p->tr_tm = TRIGGER_AFTER; + } + mask |= p->tr_tm; + }else if( p->bReturning && p->op==TK_INSERT && op==TK_UPDATE + && sqlcipher_sqlite3IsToplevel(pParse) ){ + /* Also fire a RETURNING trigger for an UPSERT */ + mask |= p->tr_tm; + } + p = p->pNext; + }while( p ); } +exit_triggers_exist: if( pMask ){ *pMask = mask; } return (mask ? pList : 0); } +SQLITE_PRIVATE Trigger *sqlcipher_sqlite3TriggersExist( + Parse *pParse, /* Parse context */ + Table *pTab, /* The table the contains the triggers */ + int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ + ExprList *pChanges, /* Columns that change in an UPDATE statement */ + int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ +){ + assert( pTab!=0 ); + if( (pTab->pTrigger==0 && !tempTriggersExist(pParse->db)) + || pParse->disableTriggers + ){ + if( pMask ) *pMask = 0; + return 0; + } + return triggersReallyExist(pParse,pTab,op,pChanges,pMask); +} /* ** Convert the pStep->zTarget string into a SrcList and return a pointer @@ -142557,6 +149912,14 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3TriggerStepSrc( } if( pStep->pFrom ){ SrcList *pDup = sqlcipher_sqlite3SrcListDup(db, pStep->pFrom, 0); + if( pDup && pDup->nSrc>1 && !IN_RENAME_OBJECT ){ + Select *pSubquery; + Token as; + pSubquery = sqlcipher_sqlite3SelectNew(pParse,0,pDup,0,0,0,0,SF_NestedFrom,0); + as.n = 0; + as.z = 0; + pDup = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); + } pSrc = sqlcipher_sqlite3SrcListAppendList(pParse, pSrc, pDup); } }else{ @@ -142565,6 +149928,146 @@ SQLITE_PRIVATE SrcList *sqlcipher_sqlite3TriggerStepSrc( return pSrc; } +/* +** Return true if the pExpr term from the RETURNING clause argument +** list is of the form "*". Raise an error if the terms if of the +** form "table.*". +*/ +static int isAsteriskTerm( + Parse *pParse, /* Parsing context */ + Expr *pTerm /* A term in the RETURNING clause */ +){ + assert( pTerm!=0 ); + if( pTerm->op==TK_ASTERISK ) return 1; + if( pTerm->op!=TK_DOT ) return 0; + assert( pTerm->pRight!=0 ); + assert( pTerm->pLeft!=0 ); + if( pTerm->pRight->op!=TK_ASTERISK ) return 0; + sqlcipher_sqlite3ErrorMsg(pParse, "RETURNING may not use \"TABLE.*\" wildcards"); + return 1; +} + +/* The input list pList is the list of result set terms from a RETURNING +** clause. The table that we are returning from is pTab. +** +** This routine makes a copy of the pList, and at the same time expands +** any "*" wildcards to be the complete set of columns from pTab. +*/ +static ExprList *sqlcipher_sqlite3ExpandReturning( + Parse *pParse, /* Parsing context */ + ExprList *pList, /* The arguments to RETURNING */ + Table *pTab /* The table being updated */ +){ + ExprList *pNew = 0; + sqlcipher_sqlite3 *db = pParse->db; + int i; + + for(i=0; inExpr; i++){ + Expr *pOldExpr = pList->a[i].pExpr; + if( NEVER(pOldExpr==0) ) continue; + if( isAsteriskTerm(pParse, pOldExpr) ){ + int jj; + for(jj=0; jjnCol; jj++){ + Expr *pNewExpr; + if( IsHiddenColumn(pTab->aCol+jj) ) continue; + pNewExpr = sqlcipher_sqlite3Expr(db, TK_ID, pTab->aCol[jj].zCnName); + pNew = sqlcipher_sqlite3ExprListAppend(pParse, pNew, pNewExpr); + if( !db->mallocFailed ){ + struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1]; + pItem->zEName = sqlcipher_sqlite3DbStrDup(db, pTab->aCol[jj].zCnName); + pItem->fg.eEName = ENAME_NAME; + } + } + }else{ + Expr *pNewExpr = sqlcipher_sqlite3ExprDup(db, pOldExpr, 0); + pNew = sqlcipher_sqlite3ExprListAppend(pParse, pNew, pNewExpr); + if( !db->mallocFailed && ALWAYS(pList->a[i].zEName!=0) ){ + struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1]; + pItem->zEName = sqlcipher_sqlite3DbStrDup(db, pList->a[i].zEName); + pItem->fg.eEName = pList->a[i].fg.eEName; + } + } + } + return pNew; +} + +/* +** Generate code for the RETURNING trigger. Unlike other triggers +** that invoke a subprogram in the bytecode, the code for RETURNING +** is generated in-line. +*/ +static void codeReturningTrigger( + Parse *pParse, /* Parse context */ + Trigger *pTrigger, /* The trigger step that defines the RETURNING */ + Table *pTab, /* The table to code triggers from */ + int regIn /* The first in an array of registers */ +){ + Vdbe *v = pParse->pVdbe; + sqlcipher_sqlite3 *db = pParse->db; + ExprList *pNew; + Returning *pReturning; + Select sSelect; + SrcList sFrom; + + assert( v!=0 ); + assert( pParse->bReturning ); + assert( db->pParse==pParse ); + pReturning = pParse->u1.pReturning; + assert( pTrigger == &(pReturning->retTrig) ); + memset(&sSelect, 0, sizeof(sSelect)); + memset(&sFrom, 0, sizeof(sFrom)); + sSelect.pEList = sqlcipher_sqlite3ExprListDup(db, pReturning->pReturnEL, 0); + sSelect.pSrc = &sFrom; + sFrom.nSrc = 1; + sFrom.a[0].pTab = pTab; + sFrom.a[0].iCursor = -1; + sqlcipher_sqlite3SelectPrep(pParse, &sSelect, 0); + if( pParse->nErr==0 ){ + assert( db->mallocFailed==0 ); + sqlcipher_sqlite3GenerateColumnNames(pParse, &sSelect); + } + sqlcipher_sqlite3ExprListDelete(db, sSelect.pEList); + pNew = sqlcipher_sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab); + if( !db->mallocFailed ){ + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + if( pReturning->nRetCol==0 ){ + pReturning->nRetCol = pNew->nExpr; + pReturning->iRetCur = pParse->nTab++; + } + sNC.pParse = pParse; + sNC.uNC.iBaseReg = regIn; + sNC.ncFlags = NC_UBaseReg; + pParse->eTriggerOp = pTrigger->op; + pParse->pTriggerTab = pTab; + if( sqlcipher_sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK + && ALWAYS(!db->mallocFailed) + ){ + int i; + int nCol = pNew->nExpr; + int reg = pParse->nMem+1; + pParse->nMem += nCol+2; + pReturning->iRetReg = reg; + for(i=0; ia[i].pExpr; + assert( pCol!=0 ); /* Due to !db->mallocFailed ~9 lines above */ + sqlcipher_sqlite3ExprCodeFactorable(pParse, pCol, reg+i); + if( sqlcipher_sqlite3ExprAffinity(pCol)==SQLITE_AFF_REAL ){ + sqlcipher_sqlite3VdbeAddOp1(v, OP_RealAffinity, reg+i); + } + } + sqlcipher_sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, i, reg+i); + sqlcipher_sqlite3VdbeAddOp2(v, OP_NewRowid, pReturning->iRetCur, reg+i+1); + sqlcipher_sqlite3VdbeAddOp3(v, OP_Insert, pReturning->iRetCur, reg+i, reg+i+1); + } + } + sqlcipher_sqlite3ExprListDelete(db, pNew); + pParse->eTriggerOp = 0; + pParse->pTriggerTab = 0; +} + + + /* ** Generate VDBE code for the statements inside the body of a single ** trigger. @@ -142614,6 +150117,7 @@ static int codeTriggerProgram( sqlcipher_sqlite3ExprDup(db, pStep->pWhere, 0), pParse->eOrconf, 0, 0, 0 ); + sqlcipher_sqlite3VdbeAddOp0(v, OP_ResetCount); break; } case TK_INSERT: { @@ -142624,6 +150128,7 @@ static int codeTriggerProgram( pParse->eOrconf, sqlcipher_sqlite3UpsertDup(db, pStep->pUpsert) ); + sqlcipher_sqlite3VdbeAddOp0(v, OP_ResetCount); break; } case TK_DELETE: { @@ -142631,6 +150136,7 @@ static int codeTriggerProgram( sqlcipher_sqlite3TriggerStepSrc(pParse, pStep), sqlcipher_sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0 ); + sqlcipher_sqlite3VdbeAddOp0(v, OP_ResetCount); break; } default: assert( pStep->op==TK_SELECT ); { @@ -142642,9 +150148,6 @@ static int codeTriggerProgram( break; } } - if( pStep->op!=TK_SELECT ){ - sqlcipher_sqlite3VdbeAddOp0(v, OP_ResetCount); - } } return 0; @@ -142702,8 +150205,8 @@ static TriggerPrg *codeRowTrigger( Vdbe *v; /* Temporary VM */ NameContext sNC; /* Name context for sub-vdbe */ SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */ - Parse *pSubParse; /* Parse context for sub-vdbe */ int iEndTrigger = 0; /* Label to jump to if WHEN is false */ + Parse sSubParse; /* Parse context for sub-vdbe */ assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); assert( pTop->pVdbe ); @@ -142725,19 +150228,17 @@ static TriggerPrg *codeRowTrigger( /* Allocate and populate a new Parse context to use for coding the ** trigger sub-program. */ - pSubParse = sqlcipher_sqlite3StackAllocZero(db, sizeof(Parse)); - if( !pSubParse ) return 0; + sqlcipher_sqlite3ParseObjectInit(&sSubParse, db); memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pSubParse; - pSubParse->db = db; - pSubParse->pTriggerTab = pTab; - pSubParse->pToplevel = pTop; - pSubParse->zAuthContext = pTrigger->zName; - pSubParse->eTriggerOp = pTrigger->op; - pSubParse->nQueryLoop = pParse->nQueryLoop; - pSubParse->disableVtab = pParse->disableVtab; - - v = sqlcipher_sqlite3GetVdbe(pSubParse); + sNC.pParse = &sSubParse; + sSubParse.pTriggerTab = pTab; + sSubParse.pToplevel = pTop; + sSubParse.zAuthContext = pTrigger->zName; + sSubParse.eTriggerOp = pTrigger->op; + sSubParse.nQueryLoop = pParse->nQueryLoop; + sSubParse.disableVtab = pParse->disableVtab; + + v = sqlcipher_sqlite3GetVdbe(&sSubParse); if( v ){ VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", pTrigger->zName, onErrorText(orconf), @@ -142760,17 +150261,17 @@ static TriggerPrg *codeRowTrigger( ** OP_Halt inserted at the end of the program. */ if( pTrigger->pWhen ){ pWhen = sqlcipher_sqlite3ExprDup(db, pTrigger->pWhen, 0); - if( SQLITE_OK==sqlcipher_sqlite3ResolveExprNames(&sNC, pWhen) - && db->mallocFailed==0 + if( db->mallocFailed==0 + && SQLITE_OK==sqlcipher_sqlite3ResolveExprNames(&sNC, pWhen) ){ - iEndTrigger = sqlcipher_sqlite3VdbeMakeLabel(pSubParse); - sqlcipher_sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); + iEndTrigger = sqlcipher_sqlite3VdbeMakeLabel(&sSubParse); + sqlcipher_sqlite3ExprIfFalse(&sSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); } sqlcipher_sqlite3ExprDelete(db, pWhen); } /* Code the trigger program into the sub-vdbe. */ - codeTriggerProgram(pSubParse, pTrigger->step_list, orconf); + codeTriggerProgram(&sSubParse, pTrigger->step_list, orconf); /* Insert an OP_Halt at the end of the sub-program. */ if( iEndTrigger ){ @@ -142778,24 +150279,24 @@ static TriggerPrg *codeRowTrigger( } sqlcipher_sqlite3VdbeAddOp0(v, OP_Halt); VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf))); + transferParseError(pParse, &sSubParse); - transferParseError(pParse, pSubParse); - if( db->mallocFailed==0 && pParse->nErr==0 ){ + if( pParse->nErr==0 ){ + assert( db->mallocFailed==0 ); pProgram->aOp = sqlcipher_sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); } - pProgram->nMem = pSubParse->nMem; - pProgram->nCsr = pSubParse->nTab; + pProgram->nMem = sSubParse.nMem; + pProgram->nCsr = sSubParse.nTab; pProgram->token = (void *)pTrigger; - pPrg->aColmask[0] = pSubParse->oldmask; - pPrg->aColmask[1] = pSubParse->newmask; + pPrg->aColmask[0] = sSubParse.oldmask; + pPrg->aColmask[1] = sSubParse.newmask; sqlcipher_sqlite3VdbeDelete(v); + }else{ + transferParseError(pParse, &sSubParse); } - assert( !pSubParse->pAinc && !pSubParse->pZombieTab ); - assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); - sqlcipher_sqlite3ParserReset(pSubParse); - sqlcipher_sqlite3StackFree(db, pSubParse); - + assert( !sSubParse.pTriggerPrg && !sSubParse.nMaxArg ); + sqlcipher_sqlite3ParseObjectReset(&sSubParse); return pPrg; } @@ -142828,6 +150329,7 @@ static TriggerPrg *getRowTrigger( /* If an existing TriggerPrg could not be located, create a new one. */ if( !pPrg ){ pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf); + pParse->db->errByteOffset = -1; } return pPrg; @@ -142850,7 +150352,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CodeRowTriggerDirect( Vdbe *v = sqlcipher_sqlite3GetVdbe(pParse); /* Main VM */ TriggerPrg *pPrg; pPrg = getRowTrigger(pParse, p, pTab, orconf); - assert( pPrg || pParse->nErr || pParse->db->mallocFailed ); + assert( pPrg || pParse->nErr ); /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program ** is a pointer to the sub-vdbe containing the trigger program. */ @@ -142893,7 +150395,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CodeRowTriggerDirect( ** ... ... ** reg+N OLD.* value of right-most column of pTab ** reg+N+1 NEW.rowid -** reg+N+2 OLD.* value of left-most column of pTab +** reg+N+2 NEW.* value of left-most column of pTab ** ... ... ** reg+N+N+1 NEW.* value of right-most column of pTab ** @@ -142938,12 +150440,20 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CodeRowTrigger( assert( p->pSchema==p->pTabSchema || p->pSchema==pParse->db->aDb[1].pSchema ); - /* Determine whether we should code this trigger */ - if( p->op==op + /* Determine whether we should code this trigger. One of two choices: + ** 1. The trigger is an exact match to the current DML statement + ** 2. This is a RETURNING trigger for INSERT but we are currently + ** doing the UPDATE part of an UPSERT. + */ + if( (p->op==op || (p->bReturning && p->op==TK_INSERT && op==TK_UPDATE)) && p->tr_tm==tr_tm && checkColumnOverlap(p->pColumns, pChanges) ){ - sqlcipher_sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump); + if( !p->bReturning ){ + sqlcipher_sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump); + }else if( sqlcipher_sqlite3IsToplevel(pParse) ){ + codeReturningTrigger(pParse, p, pTab, reg); + } } } } @@ -142988,13 +150498,18 @@ SQLITE_PRIVATE u32 sqlcipher_sqlite3TriggerColmask( assert( isNew==1 || isNew==0 ); for(p=pTrigger; p; p=p->pNext){ - if( p->op==op && (tr_tm&p->tr_tm) + if( p->op==op + && (tr_tm&p->tr_tm) && checkColumnOverlap(p->pColumns,pChanges) ){ - TriggerPrg *pPrg; - pPrg = getRowTrigger(pParse, p, pTab, orconf); - if( pPrg ){ - mask |= pPrg->aColmask[isNew]; + if( p->bReturning ){ + mask = 0xffffffff; + }else{ + TriggerPrg *pPrg; + pPrg = getRowTrigger(pParse, p, pTab, orconf); + if( pPrg ){ + mask |= pPrg->aColmask[isNew]; + } } } } @@ -143068,13 +150583,14 @@ static void updateVirtualTable( */ SQLITE_PRIVATE void sqlcipher_sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ assert( pTab!=0 ); - if( !pTab->pSelect ){ + if( !IsView(pTab) ){ sqlcipher_sqlite3_value *pValue = 0; u8 enc = ENC(sqlcipher_sqlite3VdbeDb(v)); Column *pCol = &pTab->aCol[i]; - VdbeComment((v, "%s.%s", pTab->zName, pCol->zName)); + VdbeComment((v, "%s.%s", pTab->zName, pCol->zCnName)); assert( inCol ); - sqlcipher_sqlite3ValueFromExpr(sqlcipher_sqlite3VdbeDb(v), pCol->pDflt, enc, + sqlcipher_sqlite3ValueFromExpr(sqlcipher_sqlite3VdbeDb(v), + sqlcipher_sqlite3ColumnExpr(pTab,pCol), enc, pCol->affinity, &pValue); if( pValue ){ sqlcipher_sqlite3VdbeAppendP4(v, pValue, P4_MEM); @@ -143228,6 +150744,7 @@ static void updateFromSelect( assert( pTabList->nSrc>1 ); if( pSrc ){ + pSrc->a[0].fg.notCte = 1; pSrc->a[0].iCursor = -1; pSrc->a[0].pTab->nTabRef--; pSrc->a[0].pTab = 0; @@ -143243,7 +150760,7 @@ static void updateFromSelect( pList = sqlcipher_sqlite3ExprListAppend(pParse, pList, pNew); } eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom; - }else if( pTab->pSelect ){ + }else if( IsView(pTab) ){ for(i=0; inCol; i++){ pList = sqlcipher_sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i)); } @@ -143257,7 +150774,8 @@ static void updateFromSelect( } #endif } - if( ALWAYS(pChanges) ){ + assert( pChanges!=0 || pParse->db->mallocFailed ); + if( pChanges ){ for(i=0; inExpr; i++){ pList = sqlcipher_sqlite3ExprListAppend(pParse, pList, sqlcipher_sqlite3ExprDup(db, pChanges->a[i].pExpr, 0) @@ -143265,8 +150783,9 @@ static void updateFromSelect( } } pSelect = sqlcipher_sqlite3SelectNew(pParse, pList, - pSrc, pWhere2, pGrp, 0, pOrderBy2, SF_UpdateFrom|SF_IncludeHidden, pLimit2 + pSrc, pWhere2, pGrp, 0, pOrderBy2, SF_UFSrcCheck|SF_IncludeHidden, pLimit2 ); + if( pSelect ) pSelect->selFlags |= SF_OrderByReqd; sqlcipher_sqlite3SelectDestInit(&dest, eDest, iEph); dest.iSDParm2 = (pPk ? pPk->nKeyCol : -1); sqlcipher_sqlite3Select(pParse, pSelect, &dest); @@ -143351,9 +150870,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( memset(&sContext, 0, sizeof(sContext)); db = pParse->db; - if( pParse->nErr || db->mallocFailed ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto update_cleanup; } + assert( db->mallocFailed==0 ); /* Locate the table which we want to update. */ @@ -143366,7 +150887,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( */ #ifndef SQLITE_OMIT_TRIGGER pTrigger = sqlcipher_sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, &tmask); - isView = pTab->pSelect!=0; + isView = IsView(pTab); assert( pTrigger || tmask==0 ); #else # define pTrigger 0 @@ -143378,6 +150899,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( # define isView 0 #endif +#if TREETRACE_ENABLED + if( sqlcipher_sqlite3TreeTrace & 0x10000 ){ + sqlcipher_sqlite3TreeViewLine(0, "In sqlcipher_sqlite3Update() at %s:%d", __FILE__, __LINE__); + sqlcipher_sqlite3TreeViewUpdate(pParse->pWith, pTabList, pChanges, pWhere, + onError, pOrderBy, pLimit, pUpsert, pTrigger); + } +#endif + /* If there was a FROM clause, set nChangeFrom to the number of expressions ** in the change-list. Otherwise, set it to 0. There cannot be a FROM ** clause if this function is being called to generate code for part of @@ -143455,13 +150984,16 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( */ chngRowid = chngPk = 0; for(i=0; inExpr; i++){ + u8 hCol = sqlcipher_sqlite3StrIHash(pChanges->a[i].zEName); /* If this is an UPDATE with a FROM clause, do not resolve expressions ** here. The call to sqlcipher_sqlite3Select() below will do that. */ if( nChangeFrom==0 && sqlcipher_sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){ goto update_cleanup; } for(j=0; jnCol; j++){ - if( sqlcipher_sqlite3StrICmp(pTab->aCol[j].zName, pChanges->a[i].zEName)==0 ){ + if( pTab->aCol[j].hName==hCol + && sqlcipher_sqlite3StrICmp(pTab->aCol[j].zCnName, pChanges->a[i].zEName)==0 + ){ if( j==pTab->iPKey ){ chngRowid = 1; pRowidExpr = pChanges->a[i].pExpr; @@ -143475,7 +151007,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( testcase( pTab->aCol[j].colFlags & COLFLAG_STORED ); sqlcipher_sqlite3ErrorMsg(pParse, "cannot UPDATE generated column \"%s\"", - pTab->aCol[j].zName); + pTab->aCol[j].zCnName); goto update_cleanup; } #endif @@ -143499,7 +151031,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( { int rc; rc = sqlcipher_sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, - j<0 ? "ROWID" : pTab->aCol[j].zName, + j<0 ? "ROWID" : pTab->aCol[j].zCnName, db->aDb[iDb].zDbSName); if( rc==SQLITE_DENY ){ goto update_cleanup; @@ -143531,8 +151063,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( for(i=0; inCol; i++){ if( aXRef[i]>=0 ) continue; if( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)==0 ) continue; - if( sqlcipher_sqlite3ExprReferencesUpdatedColumn(pTab->aCol[i].pDflt, - aXRef, chngRowid) ){ + if( sqlcipher_sqlite3ExprReferencesUpdatedColumn( + sqlcipher_sqlite3ColumnExpr(pTab, &pTab->aCol[i]), + aXRef, chngRowid) + ){ aXRef[i] = 99999; bProgress = 1; } @@ -143651,6 +151185,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( if( (db->flags&SQLITE_CountRows)!=0 && !pParse->pTriggerTab && !pParse->nested + && !pParse->bReturning && pUpsert==0 ){ regRowCount = ++pParse->nMem; @@ -143719,7 +151254,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ flags |= WHERE_ONEPASS_MULTIROW; } - pWInfo = sqlcipher_sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags,iIdxCur); + pWInfo = sqlcipher_sqlite3WhereBegin(pParse, pTabList, pWhere,0,0,0,flags,iIdxCur); if( pWInfo==0 ) goto update_cleanup; /* A one-pass strategy that might update more than one row may not @@ -143806,7 +151341,12 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( /* Top of the update loop */ if( eOnePass!=ONEPASS_OFF ){ - if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){ + if( aiCurOnePass[0]!=iDataCur + && aiCurOnePass[1]!=iDataCur +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + && !isView +#endif + ){ assert( pPk ); sqlcipher_sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey,nKey); VdbeCoverage(v); @@ -144011,7 +151551,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( }else{ sqlcipher_sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid); } - VdbeCoverageNeverTaken(v); + VdbeCoverage(v); } /* Do FK constraint checks. */ @@ -144114,9 +151654,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Update( ** that information. */ if( regRowCount ){ - sqlcipher_sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); - sqlcipher_sqlite3VdbeSetNumCols(v, 1); - sqlcipher_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC); + sqlcipher_sqlite3CodeChangeCount(v, regRowCount, "rows updated"); } update_cleanup: @@ -144238,7 +151776,9 @@ static void updateVirtualTable( regRowid = ++pParse->nMem; /* Start scanning the virtual table */ - pWInfo = sqlcipher_sqlite3WhereBegin(pParse, pSrc,pWhere,0,0,WHERE_ONEPASS_DESIRED,0); + pWInfo = sqlcipher_sqlite3WhereBegin( + pParse, pSrc, pWhere, 0, 0, 0, WHERE_ONEPASS_DESIRED, 0 + ); if( pWInfo==0 ) return; /* Populate the argument registers. */ @@ -144349,16 +151889,23 @@ static void updateVirtualTable( /* ** Free a list of Upsert objects */ -SQLITE_PRIVATE void sqlcipher_sqlite3UpsertDelete(sqlcipher_sqlite3 *db, Upsert *p){ - if( p ){ +static void SQLITE_NOINLINE upsertDelete(sqlcipher_sqlite3 *db, Upsert *p){ + do{ + Upsert *pNext = p->pNextUpsert; sqlcipher_sqlite3ExprListDelete(db, p->pUpsertTarget); sqlcipher_sqlite3ExprDelete(db, p->pUpsertTargetWhere); sqlcipher_sqlite3ExprListDelete(db, p->pUpsertSet); sqlcipher_sqlite3ExprDelete(db, p->pUpsertWhere); + sqlcipher_sqlite3DbFree(db, p->pToFree); sqlcipher_sqlite3DbFree(db, p); - } + p = pNext; + }while( p ); +} +SQLITE_PRIVATE void sqlcipher_sqlite3UpsertDelete(sqlcipher_sqlite3 *db, Upsert *p){ + if( p ) upsertDelete(db, p); } + /* ** Duplicate an Upsert object. */ @@ -144368,7 +151915,8 @@ SQLITE_PRIVATE Upsert *sqlcipher_sqlite3UpsertDup(sqlcipher_sqlite3 *db, Upsert sqlcipher_sqlite3ExprListDup(db, p->pUpsertTarget, 0), sqlcipher_sqlite3ExprDup(db, p->pUpsertTargetWhere, 0), sqlcipher_sqlite3ExprListDup(db, p->pUpsertSet, 0), - sqlcipher_sqlite3ExprDup(db, p->pUpsertWhere, 0) + sqlcipher_sqlite3ExprDup(db, p->pUpsertWhere, 0), + sqlcipher_sqlite3UpsertDup(db, p->pNextUpsert) ); } @@ -144380,22 +151928,25 @@ SQLITE_PRIVATE Upsert *sqlcipher_sqlite3UpsertNew( ExprList *pTarget, /* Target argument to ON CONFLICT, or NULL */ Expr *pTargetWhere, /* Optional WHERE clause on the target */ ExprList *pSet, /* UPDATE columns, or NULL for a DO NOTHING */ - Expr *pWhere /* WHERE clause for the ON CONFLICT UPDATE */ + Expr *pWhere, /* WHERE clause for the ON CONFLICT UPDATE */ + Upsert *pNext /* Next ON CONFLICT clause in the list */ ){ Upsert *pNew; - pNew = sqlcipher_sqlite3DbMallocRaw(db, sizeof(Upsert)); + pNew = sqlcipher_sqlite3DbMallocZero(db, sizeof(Upsert)); if( pNew==0 ){ sqlcipher_sqlite3ExprListDelete(db, pTarget); sqlcipher_sqlite3ExprDelete(db, pTargetWhere); sqlcipher_sqlite3ExprListDelete(db, pSet); sqlcipher_sqlite3ExprDelete(db, pWhere); + sqlcipher_sqlite3UpsertDelete(db, pNext); return 0; }else{ pNew->pUpsertTarget = pTarget; pNew->pUpsertTargetWhere = pTargetWhere; pNew->pUpsertSet = pSet; pNew->pUpsertWhere = pWhere; - pNew->pUpsertIdx = 0; + pNew->isDoUpdate = pSet!=0; + pNew->pNextUpsert = pNext; } return pNew; } @@ -144420,6 +151971,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3UpsertAnalyzeTarget( Expr *pTerm; /* One term of the conflict-target clause */ NameContext sNC; /* Context for resolving symbolic names */ Expr sCol[2]; /* Index column converted into an Expr */ + int nClause = 0; /* Counter of ON CONFLICT clauses */ assert( pTabList->nSrc==1 ); assert( pTabList->a[0].pTab!=0 ); @@ -144433,87 +151985,131 @@ SQLITE_PRIVATE int sqlcipher_sqlite3UpsertAnalyzeTarget( memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; sNC.pSrcList = pTabList; - rc = sqlcipher_sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); - if( rc ) return rc; - rc = sqlcipher_sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); - if( rc ) return rc; + for(; pUpsert && pUpsert->pUpsertTarget; + pUpsert=pUpsert->pNextUpsert, nClause++){ + rc = sqlcipher_sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); + if( rc ) return rc; + rc = sqlcipher_sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); + if( rc ) return rc; - /* Check to see if the conflict target matches the rowid. */ - pTab = pTabList->a[0].pTab; - pTarget = pUpsert->pUpsertTarget; - iCursor = pTabList->a[0].iCursor; - if( HasRowid(pTab) - && pTarget->nExpr==1 - && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN - && pTerm->iColumn==XN_ROWID - ){ - /* The conflict-target is the rowid of the primary table */ - assert( pUpsert->pUpsertIdx==0 ); - return SQLITE_OK; - } + /* Check to see if the conflict target matches the rowid. */ + pTab = pTabList->a[0].pTab; + pTarget = pUpsert->pUpsertTarget; + iCursor = pTabList->a[0].iCursor; + if( HasRowid(pTab) + && pTarget->nExpr==1 + && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN + && pTerm->iColumn==XN_ROWID + ){ + /* The conflict-target is the rowid of the primary table */ + assert( pUpsert->pUpsertIdx==0 ); + continue; + } - /* Initialize sCol[0..1] to be an expression parse tree for a - ** single column of an index. The sCol[0] node will be the TK_COLLATE - ** operator and sCol[1] will be the TK_COLUMN operator. Code below - ** will populate the specific collation and column number values - ** prior to comparing against the conflict-target expression. - */ - memset(sCol, 0, sizeof(sCol)); - sCol[0].op = TK_COLLATE; - sCol[0].pLeft = &sCol[1]; - sCol[1].op = TK_COLUMN; - sCol[1].iTable = pTabList->a[0].iCursor; + /* Initialize sCol[0..1] to be an expression parse tree for a + ** single column of an index. The sCol[0] node will be the TK_COLLATE + ** operator and sCol[1] will be the TK_COLUMN operator. Code below + ** will populate the specific collation and column number values + ** prior to comparing against the conflict-target expression. + */ + memset(sCol, 0, sizeof(sCol)); + sCol[0].op = TK_COLLATE; + sCol[0].pLeft = &sCol[1]; + sCol[1].op = TK_COLUMN; + sCol[1].iTable = pTabList->a[0].iCursor; - /* Check for matches against other indexes */ - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int ii, jj, nn; - if( !IsUniqueIndex(pIdx) ) continue; - if( pTarget->nExpr!=pIdx->nKeyCol ) continue; - if( pIdx->pPartIdxWhere ){ - if( pUpsert->pUpsertTargetWhere==0 ) continue; - if( sqlcipher_sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere, - pIdx->pPartIdxWhere, iCursor)!=0 ){ - continue; + /* Check for matches against other indexes */ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + int ii, jj, nn; + if( !IsUniqueIndex(pIdx) ) continue; + if( pTarget->nExpr!=pIdx->nKeyCol ) continue; + if( pIdx->pPartIdxWhere ){ + if( pUpsert->pUpsertTargetWhere==0 ) continue; + if( sqlcipher_sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere, + pIdx->pPartIdxWhere, iCursor)!=0 ){ + continue; + } } - } - nn = pIdx->nKeyCol; - for(ii=0; iiazColl[ii]; - if( pIdx->aiColumn[ii]==XN_EXPR ){ - assert( pIdx->aColExpr!=0 ); - assert( pIdx->aColExpr->nExpr>ii ); - pExpr = pIdx->aColExpr->a[ii].pExpr; - if( pExpr->op!=TK_COLLATE ){ - sCol[0].pLeft = pExpr; + nn = pIdx->nKeyCol; + for(ii=0; iiazColl[ii]; + if( pIdx->aiColumn[ii]==XN_EXPR ){ + assert( pIdx->aColExpr!=0 ); + assert( pIdx->aColExpr->nExpr>ii ); + pExpr = pIdx->aColExpr->a[ii].pExpr; + if( pExpr->op!=TK_COLLATE ){ + sCol[0].pLeft = pExpr; + pExpr = &sCol[0]; + } + }else{ + sCol[0].pLeft = &sCol[1]; + sCol[1].iColumn = pIdx->aiColumn[ii]; pExpr = &sCol[0]; } - }else{ - sCol[0].pLeft = &sCol[1]; - sCol[1].iColumn = pIdx->aiColumn[ii]; - pExpr = &sCol[0]; - } - for(jj=0; jja[jj].pExpr, pExpr,iCursor)<2 ){ - break; /* Column ii of the index matches column jj of target */ + for(jj=0; jja[jj].pExpr,pExpr,iCursor)<2 ){ + break; /* Column ii of the index matches column jj of target */ + } + } + if( jj>=nn ){ + /* The target contains no match for column jj of the index */ + break; } } - if( jj>=nn ){ - /* The target contains no match for column jj of the index */ - break; + if( iipUpsertIdx = pIdx; + break; } - if( iipUpsertIdx==0 ){ + char zWhich[16]; + if( nClause==0 && pUpsert->pNextUpsert==0 ){ + zWhich[0] = 0; + }else{ + sqlcipher_sqlite3_snprintf(sizeof(zWhich),zWhich,"%r ", nClause+1); + } + sqlcipher_sqlite3ErrorMsg(pParse, "%sON CONFLICT clause does not match any " + "PRIMARY KEY or UNIQUE constraint", zWhich); + return SQLITE_ERROR; } - pUpsert->pUpsertIdx = pIdx; - return SQLITE_OK; } - sqlcipher_sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any " - "PRIMARY KEY or UNIQUE constraint"); - return SQLITE_ERROR; + return SQLITE_OK; +} + +/* +** Return true if pUpsert is the last ON CONFLICT clause with a +** conflict target, or if pUpsert is followed by another ON CONFLICT +** clause that targets the INTEGER PRIMARY KEY. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3UpsertNextIsIPK(Upsert *pUpsert){ + Upsert *pNext; + if( NEVER(pUpsert==0) ) return 0; + pNext = pUpsert->pNextUpsert; + if( pNext==0 ) return 1; + if( pNext->pUpsertTarget==0 ) return 1; + if( pNext->pUpsertIdx==0 ) return 1; + return 0; +} + +/* +** Given the list of ON CONFLICT clauses described by pUpsert, and +** a particular index pIdx, return a pointer to the particular ON CONFLICT +** clause that applies to the index. Or, if the index is not subject to +** any ON CONFLICT clause, return NULL. +*/ +SQLITE_PRIVATE Upsert *sqlcipher_sqlite3UpsertOfIndex(Upsert *pUpsert, Index *pIdx){ + while( + pUpsert + && pUpsert->pUpsertTarget!=0 + && pUpsert->pUpsertIdx!=pIdx + ){ + pUpsert = pUpsert->pNextUpsert; + } + return pUpsert; } /* @@ -144537,11 +152133,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3UpsertDoUpdate( SrcList *pSrc; /* FROM clause for the UPDATE */ int iDataCur; int i; + Upsert *pTop = pUpsert; assert( v!=0 ); assert( pUpsert!=0 ); - VdbeNoopComment((v, "Begin DO UPDATE of UPSERT")); iDataCur = pUpsert->iDataCur; + pUpsert = sqlcipher_sqlite3UpsertOfIndex(pTop, pIdx); + VdbeNoopComment((v, "Begin DO UPDATE of UPSERT")); if( pIdx && iCur!=iDataCur ){ if( HasRowid(pTab) ){ int regRowid = sqlcipher_sqlite3GetTempReg(pParse); @@ -144560,7 +152158,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3UpsertDoUpdate( k = sqlcipher_sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]); sqlcipher_sqlite3VdbeAddOp3(v, OP_Column, iCur, k, iPk+i); VdbeComment((v, "%s.%s", pIdx->zName, - pTab->aCol[pPk->aiColumn[i]].zName)); + pTab->aCol[pPk->aiColumn[i]].zCnName)); } sqlcipher_sqlite3VdbeVerifyAbortable(v, OE_Abort); i = sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Found, iDataCur, 0, iPk, nPk); @@ -144571,19 +152169,17 @@ SQLITE_PRIVATE void sqlcipher_sqlite3UpsertDoUpdate( sqlcipher_sqlite3VdbeJumpHere(v, i); } } - /* pUpsert does not own pUpsertSrc - the outer INSERT statement does. So - ** we have to make a copy before passing it down into sqlcipher_sqlite3Update() */ - pSrc = sqlcipher_sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0); + /* pUpsert does not own pTop->pUpsertSrc - the outer INSERT statement does. + ** So we have to make a copy before passing it down into sqlcipher_sqlite3Update() */ + pSrc = sqlcipher_sqlite3SrcListDup(db, pTop->pUpsertSrc, 0); /* excluded.* columns of type REAL need to be converted to a hard real */ for(i=0; inCol; i++){ if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ - sqlcipher_sqlite3VdbeAddOp1(v, OP_RealAffinity, pUpsert->regData+i); + sqlcipher_sqlite3VdbeAddOp1(v, OP_RealAffinity, pTop->regData+i); } } - sqlcipher_sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet, - pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert); - pUpsert->pUpsertSet = 0; /* Will have been deleted by sqlcipher_sqlite3Update() */ - pUpsert->pUpsertWhere = 0; /* Will have been deleted by sqlcipher_sqlite3Update() */ + sqlcipher_sqlite3Update(pParse, pSrc, sqlcipher_sqlite3ExprListDup(db,pUpsert->pUpsertSet,0), + sqlcipher_sqlite3ExprDup(db,pUpsert->pUpsertWhere,0), OE_Abort, 0, 0, pUpsert); VdbeNoopComment((v, "End DO UPDATE of UPSERT")); } @@ -144744,8 +152340,8 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlcipher_sqlite3RunVacuum( Btree *pTemp; /* The temporary database we vacuum into */ u32 saved_mDbFlags; /* Saved value of db->mDbFlags */ u64 saved_flags; /* Saved value of db->flags */ - int saved_nChange; /* Saved value of db->nChange */ - int saved_nTotalChange; /* Saved value of db->nTotalChange */ + i64 saved_nChange; /* Saved value of db->nChange */ + i64 saved_nTotalChange; /* Saved value of db->nTotalChange */ u32 saved_openFlags; /* Saved value of db->openFlags */ u8 saved_mTrace; /* Saved trace settings */ Db *pDb = 0; /* Database to detach at end of vacuum */ @@ -144832,10 +152428,10 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlcipher_sqlite3RunVacuum( /* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC if( db->nextPagesize ){ - extern void sqlcipher_sqlite3CodecGetKey(sqlcipher_sqlite3*, int, void**, int*); + extern void sqlcipherCodecGetKey(sqlcipher_sqlite3*, int, void**, int*); int nKey; char *zKey; - sqlcipher_sqlite3CodecGetKey(db, iDb, (void**)&zKey, &nKey); + sqlcipherCodecGetKey(db, iDb, (void**)&zKey, &nKey); if( nKey ) db->nextPagesize = 0; } #endif @@ -144856,7 +152452,9 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlcipher_sqlite3RunVacuum( /* Do not attempt to change the page size for a WAL database */ if( sqlcipher_sqlite3PagerGetJournalMode(sqlcipher_sqlite3BtreePager(pMain)) - ==PAGER_JOURNALMODE_WAL ){ + ==PAGER_JOURNALMODE_WAL + && pOut==0 + ){ db->nextPagesize = 0; } @@ -144972,6 +152570,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlcipher_sqlite3RunVacuum( assert( rc==SQLITE_OK ); if( pOut==0 ){ + nRes = sqlcipher_sqlite3BtreeGetRequestedReserve(pTemp); rc = sqlcipher_sqlite3BtreeSetPageSize(pMain, sqlcipher_sqlite3BtreeGetPageSize(pTemp), nRes,1); } @@ -145205,7 +152804,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VtabLock(VTable *pVTab){ SQLITE_PRIVATE VTable *sqlcipher_sqlite3GetVTable(sqlcipher_sqlite3 *db, Table *pTab){ VTable *pVtab; assert( IsVirtual(pTab) ); - for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); + for(pVtab=pTab->u.vtab.p; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); return pVtab; } @@ -145218,7 +152817,8 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VtabUnlock(VTable *pVTab){ assert( db ); assert( pVTab->nRef>0 ); - assert( db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ZOMBIE ); + assert( db->eOpenState==SQLITE_STATE_OPEN + || db->eOpenState==SQLITE_STATE_ZOMBIE ); pVTab->nRef--; if( pVTab->nRef==0 ){ @@ -145233,21 +152833,24 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VtabUnlock(VTable *pVTab){ /* ** Table p is a virtual table. This function moves all elements in the -** p->pVTable list to the sqlcipher_sqlite3.pDisconnect lists of their associated +** p->u.vtab.p list to the sqlcipher_sqlite3.pDisconnect lists of their associated ** database connections to be disconnected at the next opportunity. ** Except, if argument db is not NULL, then the entry associated with -** connection db is left in the p->pVTable list. +** connection db is left in the p->u.vtab.p list. */ static VTable *vtabDisconnectAll(sqlcipher_sqlite3 *db, Table *p){ VTable *pRet = 0; - VTable *pVTable = p->pVTable; - p->pVTable = 0; + VTable *pVTable; + + assert( IsVirtual(p) ); + pVTable = p->u.vtab.p; + p->u.vtab.p = 0; /* Assert that the mutex (if any) associated with the BtShared database ** that contains table p is held by the caller. See header comments ** above function sqlcipher_sqlite3VtabUnlockList() for an explanation of why ** this makes it safe to access the sqlcipher_sqlite3.pDisconnect list of any - ** database connection that may have an entry in the p->pVTable list. + ** database connection that may have an entry in the p->u.vtab.p list. */ assert( db==0 || sqlcipher_sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); @@ -145257,7 +152860,7 @@ static VTable *vtabDisconnectAll(sqlcipher_sqlite3 *db, Table *p){ assert( db2 ); if( db2==db ){ pRet = pVTable; - p->pVTable = pRet; + p->u.vtab.p = pRet; pRet->pNext = 0; }else{ pVTable->pNext = db2->pDisconnect; @@ -145285,7 +152888,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VtabDisconnect(sqlcipher_sqlite3 *db, Table assert( sqlcipher_sqlite3BtreeHoldsAllMutexes(db) ); assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); - for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){ + for(ppVTab=&p->u.vtab.p; *ppVTab; ppVTab=&(*ppVTab)->pNext){ if( (*ppVTab)->db==db ){ VTable *pVTab = *ppVTab; *ppVTab = pVTab->pNext; @@ -145348,37 +152951,41 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VtabUnlockList(sqlcipher_sqlite3 *db){ ** database connection. */ SQLITE_PRIVATE void sqlcipher_sqlite3VtabClear(sqlcipher_sqlite3 *db, Table *p){ + assert( IsVirtual(p) ); if( !db || db->pnBytesFreed==0 ) vtabDisconnectAll(0, p); - if( p->azModuleArg ){ + if( p->u.vtab.azArg ){ int i; - for(i=0; inModuleArg; i++){ - if( i!=1 ) sqlcipher_sqlite3DbFree(db, p->azModuleArg[i]); + for(i=0; iu.vtab.nArg; i++){ + if( i!=1 ) sqlcipher_sqlite3DbFree(db, p->u.vtab.azArg[i]); } - sqlcipher_sqlite3DbFree(db, p->azModuleArg); + sqlcipher_sqlite3DbFree(db, p->u.vtab.azArg); } } /* -** Add a new module argument to pTable->azModuleArg[]. +** Add a new module argument to pTable->u.vtab.azArg[]. ** The string is not copied - the pointer is stored. The ** string will be freed automatically when the table is ** deleted. */ static void addModuleArgument(Parse *pParse, Table *pTable, char *zArg){ - sqlcipher_sqlite3_int64 nBytes = sizeof(char *)*(2+pTable->nModuleArg); + sqlcipher_sqlite3_int64 nBytes; char **azModuleArg; sqlcipher_sqlite3 *db = pParse->db; - if( pTable->nModuleArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){ + + assert( IsVirtual(pTable) ); + nBytes = sizeof(char *)*(2+pTable->u.vtab.nArg); + if( pTable->u.vtab.nArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){ sqlcipher_sqlite3ErrorMsg(pParse, "too many columns on %s", pTable->zName); } - azModuleArg = sqlcipher_sqlite3DbRealloc(db, pTable->azModuleArg, nBytes); + azModuleArg = sqlcipher_sqlite3DbRealloc(db, pTable->u.vtab.azArg, nBytes); if( azModuleArg==0 ){ sqlcipher_sqlite3DbFree(db, zArg); }else{ - int i = pTable->nModuleArg++; + int i = pTable->u.vtab.nArg++; azModuleArg[i] = zArg; azModuleArg[i+1] = 0; - pTable->azModuleArg = azModuleArg; + pTable->u.vtab.azArg = azModuleArg; } } @@ -145401,10 +153008,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VtabBeginParse( pTable = pParse->pNewTable; if( pTable==0 ) return; assert( 0==pTable->pIndex ); + pTable->eTabType = TABTYP_VTAB; db = pParse->db; - assert( pTable->nModuleArg==0 ); + assert( pTable->u.vtab.nArg==0 ); addModuleArgument(pParse, pTable, sqlcipher_sqlite3NameFromToken(db, pModuleName)); addModuleArgument(pParse, pTable, 0); addModuleArgument(pParse, pTable, sqlcipher_sqlite3DbStrDup(db, pTable->zName)); @@ -145421,11 +153029,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VtabBeginParse( ** sqlite_schema table, has already been made by sqlcipher_sqlite3StartTable(). ** The second call, to obtain permission to create the table, is made now. */ - if( pTable->azModuleArg ){ + if( pTable->u.vtab.azArg ){ int iDb = sqlcipher_sqlite3SchemaToIndex(db, pTable->pSchema); assert( iDb>=0 ); /* The database the table is being created in */ sqlcipher_sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName, - pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName); + pTable->u.vtab.azArg[0], pParse->db->aDb[iDb].zDbSName); } #endif } @@ -145453,9 +153061,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VtabFinishParse(Parse *pParse, Token *pEnd) sqlcipher_sqlite3 *db = pParse->db; /* The database connection */ if( pTab==0 ) return; + assert( IsVirtual(pTab) ); addArgumentToVtab(pParse); pParse->sArg.z = 0; - if( pTab->nModuleArg<1 ) return; + if( pTab->u.vtab.nArg<1 ) return; /* If the CREATE VIRTUAL TABLE statement is being entered for the ** first time (in other words if the virtual table is actually being @@ -145488,7 +153097,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VtabFinishParse(Parse *pParse, Token *pEnd) */ iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); sqlcipher_sqlite3NestedParse(pParse, - "UPDATE %Q." DFLT_SCHEMA_TABLE " " + "UPDATE %Q." LEGACY_SCHEMA_TABLE " " "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " "WHERE rowid=#%d", db->aDb[iDb].zDbSName, @@ -145502,24 +153111,20 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VtabFinishParse(Parse *pParse, Token *pEnd) sqlcipher_sqlite3VdbeAddOp0(v, OP_Expire); zWhere = sqlcipher_sqlite3MPrintf(db, "name=%Q AND sql=%Q", pTab->zName, zStmt); - sqlcipher_sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); + sqlcipher_sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere, 0); sqlcipher_sqlite3DbFree(db, zStmt); iReg = ++pParse->nMem; sqlcipher_sqlite3VdbeLoadString(v, iReg, pTab->zName); sqlcipher_sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg); - } - - /* If we are rereading the sqlite_schema table create the in-memory - ** record of the table. The xConnect() method is not called until - ** the first time the virtual table is used in an SQL statement. This - ** allows a schema that contains virtual tables to be loaded before - ** the required virtual table implementations are registered. */ - else { + }else{ + /* If we are rereading the sqlite_schema table create the in-memory + ** record of the table. */ Table *pOld; Schema *pSchema = pTab->pSchema; const char *zName = pTab->zName; - assert( sqlcipher_sqlite3SchemaMutexHeld(db, 0, pSchema) ); + assert( zName!=0 ); + sqlcipher_sqlite3MarkAllShadowTablesOf(db, pTab); pOld = sqlcipher_sqlite3HashInsert(&pSchema->tblHash, zName, pTab); if( pOld ){ sqlcipher_sqlite3OomFault(db); @@ -145570,13 +153175,16 @@ static int vtabCallConstructor( VtabCtx sCtx; VTable *pVTable; int rc; - const char *const*azArg = (const char *const*)pTab->azModuleArg; - int nArg = pTab->nModuleArg; + const char *const*azArg; + int nArg = pTab->u.vtab.nArg; char *zErr = 0; char *zModuleName; int iDb; VtabCtx *pCtx; + assert( IsVirtual(pTab) ); + azArg = (const char *const*)pTab->u.vtab.azArg; + /* Check that the virtual-table is not already being initialized */ for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){ if( pCtx->pTab==pTab ){ @@ -145603,7 +153211,7 @@ static int vtabCallConstructor( pVTable->eVtabRisk = SQLITE_VTABRISK_Normal; iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); - pTab->azModuleArg[1] = db->aDb[iDb].zDbSName; + pTab->u.vtab.azArg[1] = db->aDb[iDb].zDbSName; /* Invoke the virtual table constructor */ assert( &db->pVtabCtx ); @@ -145642,12 +153250,12 @@ static int vtabCallConstructor( int iCol; u16 oooHidden = 0; /* If everything went according to plan, link the new VTable structure - ** into the linked list headed by pTab->pVTable. Then loop through the + ** into the linked list headed by pTab->u.vtab.p. Then loop through the ** columns of the table to see if any of them contain the token "hidden". ** If so, set the Column COLFLAG_HIDDEN flag and remove the token from ** the type string. */ - pVTable->pNext = pTab->pVTable; - pTab->pVTable = pVTable; + pVTable->pNext = pTab->u.vtab.p; + pTab->u.vtab.p = pVTable; for(iCol=0; iColnCol; iCol++){ char *zType = sqlcipher_sqlite3ColumnType(&pTab->aCol[iCol], ""); @@ -145673,6 +153281,7 @@ static int vtabCallConstructor( zType[i-1] = '\0'; } pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN; + pTab->tabFlags |= TF_HasHidden; oooHidden = TF_OOOHidden; }else{ pTab->tabFlags |= oooHidden; @@ -145699,16 +153308,17 @@ SQLITE_PRIVATE int sqlcipher_sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ int rc; assert( pTab ); - if( !IsVirtual(pTab) || sqlcipher_sqlite3GetVTable(db, pTab) ){ + assert( IsVirtual(pTab) ); + if( sqlcipher_sqlite3GetVTable(db, pTab) ){ return SQLITE_OK; } /* Locate the required virtual table module */ - zMod = pTab->azModuleArg[0]; + zMod = pTab->u.vtab.azArg[0]; pMod = (Module*)sqlcipher_sqlite3HashFind(&db->aModule, zMod); if( !pMod ){ - const char *zModule = pTab->azModuleArg[0]; + const char *zModule = pTab->u.vtab.azArg[0]; sqlcipher_sqlite3ErrorMsg(pParse, "no such module: %s", zModule); rc = SQLITE_ERROR; }else{ @@ -145771,10 +153381,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3VtabCallCreate(sqlcipher_sqlite3 *db, int iD const char *zMod; pTab = sqlcipher_sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName); - assert( pTab && IsVirtual(pTab) && !pTab->pVTable ); + assert( pTab && IsVirtual(pTab) && !pTab->u.vtab.p ); /* Locate the required virtual table module */ - zMod = pTab->azModuleArg[0]; + zMod = pTab->u.vtab.azArg[0]; pMod = (Module*)sqlcipher_sqlite3HashFind(&db->aModule, zMod); /* If the module has been registered and includes a Create method, @@ -145809,8 +153419,8 @@ SQLITE_API int sqlcipher_sqlite3_declare_vtab(sqlcipher_sqlite3 *db, const char VtabCtx *pCtx; int rc = SQLITE_OK; Table *pTab; - char *zErr = 0; Parse sParse; + int initBusy; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlcipher_sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ @@ -145827,21 +153437,28 @@ SQLITE_API int sqlcipher_sqlite3_declare_vtab(sqlcipher_sqlite3 *db, const char pTab = pCtx->pTab; assert( IsVirtual(pTab) ); - memset(&sParse, 0, sizeof(sParse)); + sqlcipher_sqlite3ParseObjectInit(&sParse, db); sParse.eParseMode = PARSE_MODE_DECLARE_VTAB; - sParse.db = db; + sParse.disableTriggers = 1; + /* We should never be able to reach this point while loading the + ** schema. Nevertheless, defend against that (turn off db->init.busy) + ** in case a bug arises. */ + assert( db->init.busy==0 ); + initBusy = db->init.busy; + db->init.busy = 0; sParse.nQueryLoop = 1; - if( SQLITE_OK==sqlcipher_sqlite3RunParser(&sParse, zCreateTable, &zErr) - && sParse.pNewTable - && !db->mallocFailed - && !sParse.pNewTable->pSelect - && !IsVirtual(sParse.pNewTable) + if( SQLITE_OK==sqlcipher_sqlite3RunParser(&sParse, zCreateTable) + && ALWAYS(sParse.pNewTable!=0) + && ALWAYS(!db->mallocFailed) + && IsOrdinaryTable(sParse.pNewTable) ){ + assert( sParse.zErrMsg==0 ); if( !pTab->aCol ){ Table *pNew = sParse.pNewTable; Index *pIdx; pTab->aCol = pNew->aCol; - pTab->nCol = pNew->nCol; + sqlcipher_sqlite3ExprListDelete(db, pNew->u.tab.pDfltList); + pTab->nNVCol = pTab->nCol = pNew->nCol; pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid); pNew->nCol = 0; pNew->aCol = 0; @@ -145865,8 +153482,9 @@ SQLITE_API int sqlcipher_sqlite3_declare_vtab(sqlcipher_sqlite3 *db, const char } pCtx->bDeclared = 1; }else{ - sqlcipher_sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); - sqlcipher_sqlite3DbFree(db, zErr); + sqlcipher_sqlite3ErrorWithMsg(db, SQLITE_ERROR, + (sParse.zErrMsg ? "%s" : 0), sParse.zErrMsg); + sqlcipher_sqlite3DbFree(db, sParse.zErrMsg); rc = SQLITE_ERROR; } sParse.eParseMode = PARSE_MODE_NORMAL; @@ -145875,7 +153493,8 @@ SQLITE_API int sqlcipher_sqlite3_declare_vtab(sqlcipher_sqlite3 *db, const char sqlcipher_sqlite3VdbeFinalize(sParse.pVdbe); } sqlcipher_sqlite3DeleteTable(db, sParse.pNewTable); - sqlcipher_sqlite3ParserReset(&sParse); + sqlcipher_sqlite3ParseObjectReset(&sParse); + db->init.busy = initBusy; assert( (rc&0xff)==rc ); rc = sqlcipher_sqlite3ApiExit(db, rc); @@ -145895,10 +153514,13 @@ SQLITE_PRIVATE int sqlcipher_sqlite3VtabCallDestroy(sqlcipher_sqlite3 *db, int i Table *pTab; pTab = sqlcipher_sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName); - if( pTab!=0 && ALWAYS(pTab->pVTable!=0) ){ + if( ALWAYS(pTab!=0) + && ALWAYS(IsVirtual(pTab)) + && ALWAYS(pTab->u.vtab.p!=0) + ){ VTable *p; int (*xDestroy)(sqlcipher_sqlite3_vtab *); - for(p=pTab->pVTable; p; p=p->pNext){ + for(p=pTab->u.vtab.p; p; p=p->pNext){ assert( p->pVtab ); if( p->pVtab->nRef>0 ){ return SQLITE_LOCKED; @@ -145912,9 +153534,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3VtabCallDestroy(sqlcipher_sqlite3 *db, int i rc = xDestroy(p->pVtab); /* Remove the sqlcipher_sqlite3_vtab* from the aVTrans[] array, if applicable */ if( rc==SQLITE_OK ){ - assert( pTab->pVTable==p && p->pNext==0 ); + assert( pTab->u.vtab.p==p && p->pNext==0 ); p->pVtab = 0; - pTab->pVTable = 0; + pTab->u.vtab.p = 0; sqlcipher_sqlite3VtabUnlock(p); } sqlcipher_sqlite3DeleteTable(db, pTab); @@ -146128,6 +153750,7 @@ SQLITE_PRIVATE FuncDef *sqlcipher_sqlite3VtabOverloadFunction( /* Check to see the left operand is a column in a virtual table */ if( NEVER(pExpr==0) ) return pDef; if( pExpr->op!=TK_COLUMN ) return pDef; + assert( ExprUseYTab(pExpr) ); pTab = pExpr->y.pTab; if( pTab==0 ) return pDef; if( !IsVirtual(pTab) ) return pDef; @@ -146202,8 +153825,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3VtabMakeWritable(Parse *pParse, Table *pTab /* ** Check to see if virtual table module pMod can be have an eponymous ** virtual table instance. If it can, create one if one does not already -** exist. Return non-zero if the eponymous virtual table instance exists -** when this routine returns, and return zero if it does not exist. +** exist. Return non-zero if either the eponymous virtual table instance +** exists when this routine returns or if an attempt to create it failed +** and an error message was left in pParse. ** ** An eponymous virtual table instance is one that is named after its ** module, and more importantly, does not require a CREATE VIRTUAL TABLE @@ -146230,9 +153854,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3VtabEponymousTableInit(Parse *pParse, Module } pMod->pEpoTab = pTab; pTab->nTabRef = 1; + pTab->eTabType = TABTYP_VTAB; pTab->pSchema = db->aDb[0].pSchema; - assert( pTab->nModuleArg==0 ); + assert( pTab->u.vtab.nArg==0 ); pTab->iPKey = -1; + pTab->tabFlags |= TF_Eponymous; addModuleArgument(pParse, pTab, sqlcipher_sqlite3DbStrDup(db, pTab->zName)); addModuleArgument(pParse, pTab, 0); addModuleArgument(pParse, pTab, sqlcipher_sqlite3DbStrDup(db, pTab->zName)); @@ -146241,7 +153867,6 @@ SQLITE_PRIVATE int sqlcipher_sqlite3VtabEponymousTableInit(Parse *pParse, Module sqlcipher_sqlite3ErrorMsg(pParse, "%s", zErr); sqlcipher_sqlite3DbFree(db, zErr); sqlcipher_sqlite3VtabEponymousTableClear(db, pMod); - return 0; } return 1; } @@ -146373,19 +153998,6 @@ SQLITE_API int sqlcipher_sqlite3_vtab_config(sqlcipher_sqlite3 *db, int op, ...) #ifndef SQLITE_WHEREINT_H #define SQLITE_WHEREINT_H -/* -** Trace output macros -*/ -#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) -/***/ extern int sqlcipher_sqlite3WhereTrace; -#endif -#if defined(SQLITE_DEBUG) \ - && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE)) -# define WHERETRACE(K,X) if(sqlcipher_sqlite3WhereTrace&(K)) sqlcipher_sqlite3DebugPrintf X -# define WHERETRACE_ENABLED 1 -#else -# define WHERETRACE(K,X) -#endif /* Forward references */ @@ -146401,6 +154013,28 @@ typedef struct WhereLoopBuilder WhereLoopBuilder; typedef struct WhereScan WhereScan; typedef struct WhereOrCost WhereOrCost; typedef struct WhereOrSet WhereOrSet; +typedef struct WhereMemBlock WhereMemBlock; +typedef struct WhereRightJoin WhereRightJoin; + +/* +** This object is a header on a block of allocated memory that will be +** automatically freed when its WInfo oject is destructed. +*/ +struct WhereMemBlock { + WhereMemBlock *pNext; /* Next block in the chain */ + u64 sz; /* Bytes of space */ +}; + +/* +** Extra information attached to a WhereLevel that is a RIGHT JOIN. +*/ +struct WhereRightJoin { + int iMatch; /* Cursor used to determine prior matched rows */ + int regBloom; /* Bloom filter for iRJMatch */ + int regReturn; /* Return register for the interior subroutine */ + int addrSubrtn; /* Starting address for the interior subroutine */ + int endSubrtn; /* The last opcode in the interior subroutine */ +}; /* ** This object contains information needed to implement a single nested @@ -146433,6 +154067,8 @@ struct WhereLevel { u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */ int addrLikeRep; /* LIKE range processing address */ #endif + int regFilter; /* Bloom filter */ + WhereRightJoin *pRJ; /* Extra information for RIGHT JOIN */ u8 iFrom; /* Which entry in the FROM clause */ u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */ int p1, p2; /* Operands of the opcode used to end the loop */ @@ -146447,7 +154083,7 @@ struct WhereLevel { u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */ } *aInLoop; /* Information about each nested IN operator */ } in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */ - Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */ + Index *pCoveringIdx; /* Possible covering index for WHERE_MULTI_OR */ } u; struct WhereLoop *pWLoop; /* The selected WhereLoop object */ Bitmask notReady; /* FROM entries not usable at this level */ @@ -146491,10 +154127,12 @@ struct WhereLoop { } btree; struct { /* Information for virtual tables */ int idxNum; /* Index number */ - u8 needFree; /* True if sqlcipher_sqlite3_free(idxStr) is needed */ + u32 needFree : 1; /* True if sqlcipher_sqlite3_free(idxStr) is needed */ + u32 bOmitOffset : 1; /* True to let virtual table handle offset */ i8 isOrdered; /* True if satisfies ORDER BY */ u16 omitMask; /* Terms that may be omitted */ char *idxStr; /* Index identifier string */ + u32 mHandleIn; /* Terms to handle as IN(...) instead of == */ } vtab; } u; u32 wsFlags; /* WHERE_* flags describing the plan */ @@ -146638,12 +154276,8 @@ struct WhereTerm { #define TERM_COPIED 0x0008 /* Has a child */ #define TERM_ORINFO 0x0010 /* Need to free the WhereTerm.u.pOrInfo object */ #define TERM_ANDINFO 0x0020 /* Need to free the WhereTerm.u.pAndInfo obj */ -#define TERM_OR_OK 0x0040 /* Used during OR-clause processing */ -#ifdef SQLITE_ENABLE_STAT4 -# define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */ -#else -# define TERM_VNULL 0x0000 /* Disabled if not using stat4 */ -#endif +#define TERM_OK 0x0040 /* Used during OR-clause processing */ +#define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */ #define TERM_LIKEOPT 0x0100 /* Virtual terms from the LIKE optimization */ #define TERM_LIKECOND 0x0200 /* Conditionally this LIKE operator term */ #define TERM_LIKE 0x0400 /* The original LIKE operator */ @@ -146655,6 +154289,7 @@ struct WhereTerm { #else # define TERM_HIGHTRUTH 0 /* Only used with STAT4 */ #endif +#define TERM_SLICE 0x8000 /* One slice of a row-value/vector comparison */ /* ** An instance of the WhereScan object is used as an iterator for locating @@ -146665,11 +154300,11 @@ struct WhereScan { WhereClause *pWC; /* WhereClause currently being scanned */ const char *zCollName; /* Required collating sequence, if not NULL */ Expr *pIdxExpr; /* Search for this index expression */ - char idxaff; /* Must match this affinity, if zCollName!=NULL */ - unsigned char nEquiv; /* Number of entries in aEquiv[] */ - unsigned char iEquiv; /* Next unused slot in aEquiv[] */ - u32 opMask; /* Acceptable operators */ int k; /* Resume scanning at this->pWC->a[this->k] */ + u32 opMask; /* Acceptable operators */ + char idxaff; /* Must match this affinity, if zCollName!=NULL */ + unsigned char iEquiv; /* Current slot in aiCur[] and aiColumn[] */ + unsigned char nEquiv; /* Number of entries in aiCur[] and aiColumn[] */ int aiCur[11]; /* Cursors in the equivalence class */ i16 aiColumn[11]; /* Corresponding column number in the eq-class */ }; @@ -146693,6 +154328,7 @@ struct WhereClause { u8 hasOr; /* True if any a[].eOperator is WO_OR */ int nTerm; /* Number of terms */ int nSlot; /* Number of entries in a[] */ + int nBase; /* Number of terms through the last non-Virtual */ WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */ #if defined(SQLITE_SMALL_STACK) WhereTerm aStatic[1]; /* Initial static space for a[] */ @@ -146750,11 +154386,6 @@ struct WhereMaskSet { int ix[BMS]; /* Cursor assigned to each bit */ }; -/* -** Initialize a WhereMaskSet object -*/ -#define initMaskSet(P) (P)->n=0 - /* ** This object is a convenience wrapper holding all information needed ** to construct WhereLoop objects for a particular query. @@ -146762,7 +154393,6 @@ struct WhereMaskSet { struct WhereLoopBuilder { WhereInfo *pWInfo; /* Information about this WHERE */ WhereClause *pWC; /* WHERE clause terms */ - ExprList *pOrderBy; /* ORDER BY clause */ WhereLoop *pNew; /* Template WhereLoop */ WhereOrSet *pOrSet; /* Record best loops here, if not NULL */ #ifdef SQLITE_ENABLE_STAT4 @@ -146830,6 +154460,9 @@ struct WhereInfo { ExprList *pOrderBy; /* The ORDER BY clause or NULL */ ExprList *pResultSet; /* Result set of the query */ Expr *pWhere; /* The complete WHERE clause */ +#ifndef SQLITE_OMIT_VIRTUALTABLE + Select *pLimit; /* Used to access LIMIT expr/registers for vtabs */ +#endif int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ @@ -146849,6 +154482,7 @@ struct WhereInfo { int iEndWhere; /* End of the WHERE clause itself */ WhereLoop *pLoops; /* List of all WhereLoop objects */ WhereExprMod *pExprMods; /* Expression modifications */ + WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */ Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ WhereClause sWC; /* Decomposition of the WHERE clause */ WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ @@ -146874,6 +154508,8 @@ SQLITE_PRIVATE WhereTerm *sqlcipher_sqlite3WhereFindTerm( u32 op, /* Mask of WO_xx values describing operator */ Index *pIdx /* Must be compatible with this index, if not NULL */ ); +SQLITE_PRIVATE void *sqlcipher_sqlite3WhereMalloc(WhereInfo *pWInfo, u64 nByte); +SQLITE_PRIVATE void *sqlcipher_sqlite3WhereRealloc(WhereInfo *pWInfo, void *pOld, u64 nByte); /* wherecode.c: */ #ifndef SQLITE_OMIT_EXPLAIN @@ -146883,8 +154519,14 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WhereExplainOneScan( WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ u16 wctrlFlags /* Flags passed to sqlcipher_sqlite3WhereBegin() */ ); +SQLITE_PRIVATE int sqlcipher_sqlite3WhereExplainBloomFilter( + const Parse *pParse, /* Parse context */ + const WhereInfo *pWInfo, /* WHERE clause */ + const WhereLevel *pLevel /* Bloom filter on this level */ +); #else # define sqlcipher_sqlite3WhereExplainOneScan(u,v,w,x) 0 +# define sqlcipher_sqlite3WhereExplainBloomFilter(u,v,w) 0 #endif /* SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS SQLITE_PRIVATE void sqlcipher_sqlite3WhereAddScanStatus( @@ -146904,16 +154546,22 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( WhereLevel *pLevel, /* The current level pointer */ Bitmask notReady /* Which tables are currently available */ ); +SQLITE_PRIVATE SQLITE_NOINLINE void sqlcipher_sqlite3WhereRightJoinLoop( + WhereInfo *pWInfo, + int iLevel, + WhereLevel *pLevel +); /* whereexpr.c: */ SQLITE_PRIVATE void sqlcipher_sqlite3WhereClauseInit(WhereClause*,WhereInfo*); SQLITE_PRIVATE void sqlcipher_sqlite3WhereClauseClear(WhereClause*); SQLITE_PRIVATE void sqlcipher_sqlite3WhereSplit(WhereClause*,Expr*,u8); +SQLITE_PRIVATE void sqlcipher_sqlite3WhereAddLimit(WhereClause*, Select*); SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereExprUsage(WhereMaskSet*, Expr*); SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*); SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*); SQLITE_PRIVATE void sqlcipher_sqlite3WhereExprAnalyze(SrcList*, WhereClause*); -SQLITE_PRIVATE void sqlcipher_sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*); +SQLITE_PRIVATE void sqlcipher_sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*); @@ -146945,8 +154593,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereTabFuncArgs(Parse*, struct SrcList_ite #define WO_AND 0x0400 /* Two or more AND-connected terms */ #define WO_EQUIV 0x0800 /* Of the form A==B, both columns */ #define WO_NOOP 0x1000 /* This term does not restrict search space */ +#define WO_ROWVAL 0x2000 /* A row-value term */ -#define WO_ALL 0x1fff /* Mask of all possible WO_* values */ +#define WO_ALL 0x3fff /* Mask of all possible WO_* values */ #define WO_SINGLE 0x01ff /* Mask of all non-compound WO_* values */ /* @@ -146976,6 +154625,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereTabFuncArgs(Parse*, struct SrcList_ite #define WHERE_IN_EARLYOUT 0x00040000 /* Perhaps quit IN loops early */ #define WHERE_BIGNULL_SORT 0x00080000 /* Column nEq of index is BIGNULL */ #define WHERE_IN_SEEKSCAN 0x00100000 /* Seek-scan optimization for IN */ +#define WHERE_TRANSCONS 0x00200000 /* Uses a transitive constraint */ +#define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */ +#define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */ +#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */ #endif /* !defined(SQLITE_WHEREINT_H) */ @@ -146991,7 +154644,7 @@ static const char *explainIndexColumnName(Index *pIdx, int i){ i = pIdx->aiColumn[i]; if( i==XN_EXPR ) return ""; if( i==XN_ROWID ) return "rowid"; - return pIdx->pTable->aCol[i].zName; + return pIdx->pTable->aCol[i].zCnName; } /* @@ -147091,7 +154744,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WhereExplainOneScan( if( sqlcipher_sqlite3ParseToplevel(pParse)->explain==2 ) #endif { - struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; + SrcItem *pItem = &pTabList->a[pLevel->iFrom]; Vdbe *v = pParse->pVdbe; /* VM being constructed */ sqlcipher_sqlite3 *db = pParse->db; /* Database handle */ int isSearch; /* True for a SEARCH. False for SCAN. */ @@ -147110,16 +154763,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WhereExplainOneScan( || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); sqlcipher_sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); - sqlcipher_sqlite3_str_appendall(&str, isSearch ? "SEARCH" : "SCAN"); - if( pItem->pSelect ){ - sqlcipher_sqlite3_str_appendf(&str, " SUBQUERY %u", pItem->pSelect->selId); - }else{ - sqlcipher_sqlite3_str_appendf(&str, " TABLE %s", pItem->zName); - } - - if( pItem->zAlias ){ - sqlcipher_sqlite3_str_appendf(&str, " AS %s", pItem->zAlias); - } + str.printfFlags = SQLITE_PRINTF_INTERNAL; + sqlcipher_sqlite3_str_appendf(&str, "%s %S", isSearch ? "SEARCH" : "SCAN", pItem); if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){ const char *zFmt = 0; Index *pIdx; @@ -147146,19 +154791,27 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WhereExplainOneScan( explainIndexRange(&str, pLoop); } }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){ - const char *zRangeOp; + char cRangeOp; +#if 0 /* Better output, but breaks many tests */ + const Table *pTab = pItem->pTab; + const char *zRowid = pTab->iPKey>=0 ? pTab->aCol[pTab->iPKey].zCnName: + "rowid"; +#else + const char *zRowid = "rowid"; +#endif + sqlcipher_sqlite3_str_appendf(&str, " USING INTEGER PRIMARY KEY (%s", zRowid); if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){ - zRangeOp = "="; + cRangeOp = '='; }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){ - zRangeOp = ">? AND rowid<"; + sqlcipher_sqlite3_str_appendf(&str, ">? AND %s", zRowid); + cRangeOp = '<'; }else if( flags&WHERE_BTM_LIMIT ){ - zRangeOp = ">"; + cRangeOp = '>'; }else{ assert( flags&WHERE_TOP_LIMIT); - zRangeOp = "<"; + cRangeOp = '<'; } - sqlcipher_sqlite3_str_appendf(&str, - " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp); + sqlcipher_sqlite3_str_appendf(&str, "%c?)", cRangeOp); } #ifndef SQLITE_OMIT_VIRTUALTABLE else if( (flags & WHERE_VIRTUALTABLE)!=0 ){ @@ -147166,6 +154819,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WhereExplainOneScan( pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr); } #endif + if( pItem->fg.jointype & JT_LEFT ){ + sqlcipher_sqlite3_str_appendf(&str, " LEFT-JOIN"); + } #ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS if( pLoop->nOut>=10 ){ sqlcipher_sqlite3_str_appendf(&str, " (~%llu rows)", @@ -147181,6 +154837,56 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WhereExplainOneScan( } return ret; } + +/* +** Add a single OP_Explain opcode that describes a Bloom filter. +** +** Or if not processing EXPLAIN QUERY PLAN and not in a SQLITE_DEBUG and/or +** SQLITE_ENABLE_STMT_SCANSTATUS build, then OP_Explain opcodes are not +** required and this routine is a no-op. +** +** If an OP_Explain opcode is added to the VM, its address is returned. +** Otherwise, if no OP_Explain is coded, zero is returned. +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3WhereExplainBloomFilter( + const Parse *pParse, /* Parse context */ + const WhereInfo *pWInfo, /* WHERE clause */ + const WhereLevel *pLevel /* Bloom filter on this level */ +){ + int ret = 0; + SrcItem *pItem = &pWInfo->pTabList->a[pLevel->iFrom]; + Vdbe *v = pParse->pVdbe; /* VM being constructed */ + sqlcipher_sqlite3 *db = pParse->db; /* Database handle */ + char *zMsg; /* Text to add to EQP output */ + int i; /* Loop counter */ + WhereLoop *pLoop; /* The where loop */ + StrAccum str; /* EQP output string */ + char zBuf[100]; /* Initial space for EQP output string */ + + sqlcipher_sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); + str.printfFlags = SQLITE_PRINTF_INTERNAL; + sqlcipher_sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem); + pLoop = pLevel->pWLoop; + if( pLoop->wsFlags & WHERE_IPK ){ + const Table *pTab = pItem->pTab; + if( pTab->iPKey>=0 ){ + sqlcipher_sqlite3_str_appendf(&str, "%s=?", pTab->aCol[pTab->iPKey].zCnName); + }else{ + sqlcipher_sqlite3_str_appendf(&str, "rowid=?"); + } + }else{ + for(i=pLoop->nSkip; iu.btree.nEq; i++){ + const char *z = explainIndexColumnName(pLoop->u.btree.pIndex, i); + if( i>pLoop->nSkip ) sqlcipher_sqlite3_str_append(&str, " AND ", 5); + sqlcipher_sqlite3_str_appendf(&str, "%s=?", z); + } + } + sqlcipher_sqlite3_str_append(&str, ")", 1); + zMsg = sqlcipher_sqlite3StrAccumFinish(&str); + ret = sqlcipher_sqlite3VdbeAddOp4(v, OP_Explain, sqlcipher_sqlite3VdbeCurrentAddr(v), + pParse->addrExplain, 0, zMsg,P4_DYNAMIC); + return ret; +} #endif /* SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS @@ -147259,7 +154965,7 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ int nLoop = 0; assert( pTerm!=0 ); while( (pTerm->wtFlags & TERM_CODED)==0 - && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin)) + && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_OuterON)) && (pLevel->notReady & pTerm->prereqAll)==0 ){ if( nLoop && (pTerm->wtFlags & TERM_LIKE)!=0 ){ @@ -147267,6 +154973,12 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ }else{ pTerm->wtFlags |= TERM_CODED; } +#ifdef WHERETRACE_ENABLED + if( sqlcipher_sqlite3WhereTrace & 0x20000 ){ + sqlcipher_sqlite3DebugPrintf("DISABLE-"); + sqlcipher_sqlite3WhereTermPrint(pTerm, (int)(pTerm - (pTerm->pWC->a))); + } +#endif if( pTerm->iParent<0 ) break; pTerm = &pTerm->pWC->a[pTerm->iParent]; assert( pTerm!=0 ); @@ -147380,16 +155092,23 @@ static Expr *removeUnindexableInClauseTerms( Expr *pNew; pNew = sqlcipher_sqlite3ExprDup(db, pX, 0); if( db->mallocFailed==0 ){ - ExprList *pOrigRhs = pNew->x.pSelect->pEList; /* Original unmodified RHS */ - ExprList *pOrigLhs = pNew->pLeft->x.pList; /* Original unmodified LHS */ + ExprList *pOrigRhs; /* Original unmodified RHS */ + ExprList *pOrigLhs; /* Original unmodified LHS */ ExprList *pRhs = 0; /* New RHS after modifications */ ExprList *pLhs = 0; /* New LHS after mods */ int i; /* Loop counter */ Select *pSelect; /* Pointer to the SELECT on the RHS */ + assert( ExprUseXSelect(pNew) ); + pOrigRhs = pNew->x.pSelect->pEList; + assert( pNew->pLeft!=0 ); + assert( ExprUseXList(pNew->pLeft) ); + pOrigLhs = pNew->pLeft->x.pList; for(i=iEq; inLTerm; i++){ if( pLoop->aLTerm[i]->pExpr==pX ){ - int iField = pLoop->aLTerm[i]->u.x.iField - 1; + int iField; + assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 ); + iField = pLoop->aLTerm[i]->u.x.iField - 1; if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */ pRhs = sqlcipher_sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr); pOrigRhs->a[iField].pExpr = 0; @@ -147504,19 +155223,25 @@ static int codeEqualityTerm( } iTab = 0; - if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){ + if( !ExprUseXSelect(pX) || pX->x.pSelect->pEList->nExpr==1 ){ eType = sqlcipher_sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab); }else{ - sqlcipher_sqlite3 *db = pParse->db; - pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); - - if( !db->mallocFailed ){ - aiMap = (int*)sqlcipher_sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); + Expr *pExpr = pTerm->pExpr; + if( pExpr->iTable==0 || !ExprHasProperty(pExpr, EP_Subrtn) ){ + sqlcipher_sqlite3 *db = pParse->db; + pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); + if( !db->mallocFailed ){ + aiMap = (int*)sqlcipher_sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); + eType = sqlcipher_sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap,&iTab); + pExpr->iTable = iTab; + } + sqlcipher_sqlite3ExprDelete(db, pX); + }else{ + int n = sqlcipher_sqlite3ExprVectorSize(pX->pLeft); + aiMap = (int*)sqlcipher_sqlite3DbMallocZero(pParse->db, sizeof(int)*MAX(nEq,n)); eType = sqlcipher_sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab); - pTerm->pExpr->iTable = iTab; } - sqlcipher_sqlite3ExprDelete(db, pX); - pX = pTerm->pExpr; + pX = pExpr; } if( eType==IN_INDEX_INDEX_DESC ){ @@ -147526,8 +155251,8 @@ static int codeEqualityTerm( sqlcipher_sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); VdbeCoverageIf(v, bRev); VdbeCoverageIf(v, !bRev); - assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); + assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); pLoop->wsFlags |= WHERE_IN_ABLE; if( pLevel->u.in.nIn==0 ){ pLevel->addrNxt = sqlcipher_sqlite3VdbeMakeLabel(pParse); @@ -147539,8 +155264,9 @@ static int codeEqualityTerm( i = pLevel->u.in.nIn; pLevel->u.in.nIn += nEq; pLevel->u.in.aInLoop = - sqlcipher_sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop, - sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn); + sqlcipher_sqlite3WhereRealloc(pTerm->pWC->pWInfo, + pLevel->u.in.aInLoop, + sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn); pIn = pLevel->u.in.aInLoop; if( pIn ){ int iMap = 0; /* Index in aiMap[] */ @@ -147584,7 +155310,22 @@ static int codeEqualityTerm( sqlcipher_sqlite3DbFree(pParse->db, aiMap); #endif } - disableTerm(pLevel, pTerm); + + /* As an optimization, try to disable the WHERE clause term that is + ** driving the index as it will always be true. The correct answer is + ** obtained regardless, but we might get the answer with fewer CPU cycles + ** by omitting the term. + ** + ** But do not disable the term unless we are certain that the term is + ** not a transitive constraint. For an example of where that does not + ** work, see https://sqlite.org/forum/forumpost/eb8613976a (2021-05-04) + */ + if( (pLevel->pWLoop->wsFlags & WHERE_TRANSCONS)==0 + || (pTerm->eOperator & WO_EQUIV)==0 + ){ + disableTerm(pLevel, pTerm); + } + return iReg; } @@ -147670,11 +155411,13 @@ static int codeAllEqualityTerms( if( nSkip ){ int iIdxCur = pLevel->iIdxCur; + sqlcipher_sqlite3VdbeAddOp3(v, OP_Null, 0, regBase, regBase+nSkip-1); sqlcipher_sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur); VdbeCoverageIf(v, bRev==0); VdbeCoverageIf(v, bRev!=0); VdbeComment((v, "begin skip-scan on %s", pIdx->zName)); j = sqlcipher_sqlite3VdbeAddOp0(v, OP_Goto); + assert( pLevel->addrSkip==0 ); pLevel->addrSkip = sqlcipher_sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLT:OP_SeekGT), iIdxCur, 0, regBase, nSkip); VdbeCoverageIf(v, bRev==0); @@ -147704,9 +155447,12 @@ static int codeAllEqualityTerms( sqlcipher_sqlite3ReleaseTempReg(pParse, regBase); regBase = r1; }else{ - sqlcipher_sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Copy, r1, regBase+j); } } + } + for(j=nSkip; jaLTerm[j]; if( pTerm->eOperator & WO_IN ){ if( pTerm->pExpr->flags & EP_xIsSelect ){ /* No affinity ever needs to be (or should be) applied to a value @@ -147721,7 +155467,8 @@ static int codeAllEqualityTerms( sqlcipher_sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); VdbeCoverage(v); } - if( zAff ){ + if( pParse->nErr==0 ){ + assert( pParse->db->mallocFailed==0 ); if( sqlcipher_sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){ zAff[j] = SQLITE_AFF_BLOB; } @@ -147884,7 +155631,7 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){ ** Insert an OP_CursorHint instruction if it is appropriate to do so. */ static void codeCursorHint( - struct SrcList_item *pTabItem, /* FROM clause item */ + SrcItem *pTabItem, /* FROM clause item */ WhereInfo *pWInfo, /* The where clause */ WhereLevel *pLevel, /* Which loop to provide hints for */ WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */ @@ -147911,7 +155658,7 @@ static void codeCursorHint( sWalker.pParse = pParse; sWalker.u.pCCurHint = &sHint; pWC = &pWInfo->sWC; - for(i=0; inTerm; i++){ + for(i=0; inBase; i++){ pTerm = &pWC->a[i]; if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( pTerm->prereqAll & pLevel->notReady ) continue; @@ -147940,8 +155687,8 @@ static void codeCursorHint( */ if( pTabItem->fg.jointype & JT_LEFT ){ Expr *pExpr = pTerm->pExpr; - if( !ExprHasProperty(pExpr, EP_FromJoin) - || pExpr->iRightJoinTable!=pTabItem->iCursor + if( !ExprHasProperty(pExpr, EP_OuterON) + || pExpr->w.iJoin!=pTabItem->iCursor ){ sWalker.eCode = 0; sWalker.xExprCallback = codeCursorHintIsOrFunction; @@ -147949,7 +155696,7 @@ static void codeCursorHint( if( sWalker.eCode ) continue; } }else{ - if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue; + if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) continue; } /* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize @@ -147997,13 +155744,21 @@ static void codeCursorHint( ** ** OP_DeferredSeek $iCur $iRowid ** +** Which causes a seek on $iCur to the row with rowid $iRowid. +** ** However, if the scan currently being coded is a branch of an OR-loop and -** the statement currently being coded is a SELECT, then P3 of OP_DeferredSeek -** is set to iIdxCur and P4 is set to point to an array of integers -** containing one entry for each column of the table cursor iCur is open -** on. For each table column, if the column is the i'th column of the -** index, then the corresponding array entry is set to (i+1). If the column -** does not appear in the index at all, the array entry is set to 0. +** the statement currently being coded is a SELECT, then additional information +** is added that might allow OP_Column to omit the seek and instead do its +** lookup on the index, thus avoiding an expensive seek operation. To +** enable this optimization, the P3 of OP_DeferredSeek is set to iIdxCur +** and P4 is set to an array of integers containing one entry for each column +** in the table. For each table column, if the column is the i'th +** column of the index, then the corresponding array entry is set to (i+1). +** If the column does not appear in the index at all, the array entry is set +** to 0. The OP_Column opcode can check this array to see if the column it +** wants is in the index and if it is, it will substitute the index cursor +** and column number and continue with those new values, rather than seeking +** the table cursor. */ static void codeDeferredSeek( WhereInfo *pWInfo, /* Where clause context */ @@ -148019,7 +155774,7 @@ static void codeDeferredSeek( pWInfo->bDeferredSeek = 1; sqlcipher_sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur); - if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) + if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) && DbMaskAllZero(sqlcipher_sqlite3ParseToplevel(pParse)->writeMask) ){ int i; @@ -148053,7 +155808,7 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ assert( nReg>0 ); if( p && sqlcipher_sqlite3ExprIsVector(p) ){ #ifndef SQLITE_OMIT_SUBQUERY - if( (p->flags & EP_xIsSelect) ){ + if( ExprUseXSelect(p) ){ Vdbe *v = pParse->pVdbe; int iSelect; assert( p->op==TK_SELECT ); @@ -148063,14 +155818,16 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ #endif { int i; - ExprList *pList = p->x.pList; + const ExprList *pList; + assert( ExprUseXList(p) ); + pList = p->x.pList; assert( nReg<=pList->nExpr ); for(i=0; ia[i].pExpr, iReg+i); } } }else{ - assert( nReg==1 ); + assert( nReg==1 || pParse->nErr ); sqlcipher_sqlite3ExprCode(pParse, p, iReg); } } @@ -148111,15 +155868,15 @@ static void preserveExpr(IdxExprTrans *pTrans, Expr *pExpr){ static int whereIndexExprTransNode(Walker *p, Expr *pExpr){ IdxExprTrans *pX = p->u.pIdxTrans; if( sqlcipher_sqlite3ExprCompare(0, pExpr, pX->pIdxExpr, pX->iTabCur)==0 ){ + pExpr = sqlcipher_sqlite3ExprSkipCollate(pExpr); preserveExpr(pX, pExpr); pExpr->affExpr = sqlcipher_sqlite3ExprAffinity(pExpr); pExpr->op = TK_COLUMN; pExpr->iTable = pX->iIdxCur; pExpr->iColumn = pX->iIdxCol; - pExpr->y.pTab = 0; - testcase( ExprHasProperty(pExpr, EP_Skip) ); testcase( ExprHasProperty(pExpr, EP_Unlikely) ); - ExprClearProperty(pExpr, EP_Skip|EP_Unlikely); + ExprClearProperty(pExpr, EP_Skip|EP_Unlikely|EP_WinFunc|EP_Subrtn); + pExpr->y.pTab = 0; return WRC_Prune; }else{ return WRC_Continue; @@ -148134,7 +155891,7 @@ static int whereIndexExprTransColumn(Walker *p, Expr *pExpr){ if( pExpr->op==TK_COLUMN ){ IdxExprTrans *pX = p->u.pIdxTrans; if( pExpr->iTable==pX->iTabCur && pExpr->iColumn==pX->iTabCol ){ - assert( pExpr->y.pTab!=0 ); + assert( ExprUseYTab(pExpr) && pExpr->y.pTab!=0 ); preserveExpr(pX, pExpr); pExpr->affExpr = sqlcipher_sqlite3TableColumnAffinity(pExpr->y.pTab,pExpr->iColumn); pExpr->iTable = pX->iIdxCur; @@ -148182,15 +155939,16 @@ static void whereIndexExprTrans( for(iIdxCol=0; iIdxColnColumn; iIdxCol++){ i16 iRef = pIdx->aiColumn[iIdxCol]; if( iRef==XN_EXPR ){ - assert( aColExpr->a[iIdxCol].pExpr!=0 ); + assert( aColExpr!=0 && aColExpr->a[iIdxCol].pExpr!=0 ); x.pIdxExpr = aColExpr->a[iIdxCol].pExpr; if( sqlcipher_sqlite3ExprIsConstant(x.pIdxExpr) ) continue; w.xExprCallback = whereIndexExprTransNode; #ifndef SQLITE_OMIT_GENERATED_COLUMNS }else if( iRef>=0 && (pTab->aCol[iRef].colFlags & COLFLAG_VIRTUAL)!=0 - && (pTab->aCol[iRef].zColl==0 - || sqlcipher_sqlite3StrICmp(pTab->aCol[iRef].zColl, sqlcipher_sqlite3StrBINARY)==0) + && ((pTab->aCol[iRef].colFlags & COLFLAG_HASCOLL)==0 + || sqlcipher_sqlite3StrICmp(sqlcipher_sqlite3ColumnColl(&pTab->aCol[iRef]), + sqlcipher_sqlite3StrBINARY)==0) ){ /* Check to see if there are direct references to generated columns ** that are contained in the index. Pulling the generated column @@ -148239,6 +155997,68 @@ static void whereApplyPartialIndexConstraints( } } +/* +** This routine is called right after An OP_Filter has been generated and +** before the corresponding index search has been performed. This routine +** checks to see if there are additional Bloom filters in inner loops that +** can be checked prior to doing the index lookup. If there are available +** inner-loop Bloom filters, then evaluate those filters now, before the +** index lookup. The idea is that a Bloom filter check is way faster than +** an index lookup, and the Bloom filter might return false, meaning that +** the index lookup can be skipped. +** +** We know that an inner loop uses a Bloom filter because it has the +** WhereLevel.regFilter set. If an inner-loop Bloom filter is checked, +** then clear the WhereLevel.regFilter value to prevent the Bloom filter +** from being checked a second time when the inner loop is evaluated. +*/ +static SQLITE_NOINLINE void filterPullDown( + Parse *pParse, /* Parsing context */ + WhereInfo *pWInfo, /* Complete information about the WHERE clause */ + int iLevel, /* Which level of pWInfo->a[] should be coded */ + int addrNxt, /* Jump here to bypass inner loops */ + Bitmask notReady /* Loops that are not ready */ +){ + while( ++iLevel < pWInfo->nLevel ){ + WhereLevel *pLevel = &pWInfo->a[iLevel]; + WhereLoop *pLoop = pLevel->pWLoop; + if( pLevel->regFilter==0 ) continue; + if( pLevel->pWLoop->nSkip ) continue; + /* ,--- Because sqlcipher_sqlite3ConstructBloomFilter() has will not have set + ** vvvvv--' pLevel->regFilter if this were true. */ + if( NEVER(pLoop->prereq & notReady) ) continue; + assert( pLevel->addrBrk==0 ); + pLevel->addrBrk = addrNxt; + if( pLoop->wsFlags & WHERE_IPK ){ + WhereTerm *pTerm = pLoop->aLTerm[0]; + int regRowid; + assert( pTerm!=0 ); + assert( pTerm->pExpr!=0 ); + testcase( pTerm->wtFlags & TERM_VIRTUAL ); + regRowid = sqlcipher_sqlite3GetTempReg(pParse); + regRowid = codeEqualityTerm(pParse, pTerm, pLevel, 0, 0, regRowid); + sqlcipher_sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter, + addrNxt, regRowid, 1); + VdbeCoverage(pParse->pVdbe); + }else{ + u16 nEq = pLoop->u.btree.nEq; + int r1; + char *zStartAff; + + assert( pLoop->wsFlags & WHERE_INDEXED ); + assert( (pLoop->wsFlags & WHERE_COLUMN_IN)==0 ); + r1 = codeAllEqualityTerms(pParse,pLevel,0,0,&zStartAff); + codeApplyAffinity(pParse, r1, nEq, zStartAff); + sqlcipher_sqlite3DbFree(pParse->db, zStartAff); + sqlcipher_sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter, + addrNxt, r1, nEq); + VdbeCoverage(pParse->pVdbe); + } + pLevel->regFilter = 0; + pLevel->addrBrk = 0; + } +} + /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. @@ -148259,7 +156079,7 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( WhereClause *pWC; /* Decomposition of the entire WHERE clause */ WhereTerm *pTerm; /* A WHERE clause term */ sqlcipher_sqlite3 *db; /* Database connection */ - struct SrcList_item *pTabItem; /* FROM clause term being coded */ + SrcItem *pTabItem; /* FROM clause term being coded */ int addrBrk; /* Jump here to break out of the loop */ int addrHalt; /* addrBrk for the outermost loop */ int addrCont; /* Jump here to continue with next cycle */ @@ -148309,7 +156129,7 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( ** initialize a memory cell that records if this table matches any ** row of the left table of the join. */ - assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) + assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) || pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0 ); if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ @@ -148320,7 +156140,10 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( /* Compute a safe address to jump to if we discover that the table for ** this loop is empty and can never contribute content. */ - for(j=iLevel; j>0 && pWInfo->a[j].iLeftJoin==0; j--){} + for(j=iLevel; j>0; j--){ + if( pWInfo->a[j].iLeftJoin ) break; + if( pWInfo->a[j].pRJ ) break; + } addrHalt = pWInfo->a[j].addrBrk; /* Special case of a FROM clause subquery implemented as a co-routine */ @@ -148341,7 +156164,6 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( int iReg; /* P3 Value for OP_VFilter */ int addrNotFound; int nConstraint = pLoop->nLTerm; - int iIn; /* Counter for IN constraints */ iReg = sqlcipher_sqlite3GetTempRange(pParse, nConstraint+2); addrNotFound = pLevel->addrBrk; @@ -148350,11 +156172,27 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( pTerm = pLoop->aLTerm[j]; if( NEVER(pTerm==0) ) continue; if( pTerm->eOperator & WO_IN ){ - codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); - addrNotFound = pLevel->addrNxt; + if( SMASKBIT32(j) & pLoop->u.vtab.mHandleIn ){ + int iTab = pParse->nTab++; + int iCache = ++pParse->nMem; + sqlcipher_sqlite3CodeRhsOfIN(pParse, pTerm->pExpr, iTab); + sqlcipher_sqlite3VdbeAddOp3(v, OP_VInitIn, iTab, iTarget, iCache); + }else{ + codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); + addrNotFound = pLevel->addrNxt; + } }else{ Expr *pRight = pTerm->pExpr->pRight; codeExprOrVector(pParse, pRight, iTarget, 1); + if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET + && pLoop->u.vtab.bOmitOffset + ){ + assert( pTerm->eOperator==WO_AUX ); + assert( pWInfo->pLimit!=0 ); + assert( pWInfo->pLimit->iOffset>0 ); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 0, pWInfo->pLimit->iOffset); + VdbeComment((v,"Zero OFFSET counter")); + } } } sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); @@ -148370,40 +156208,55 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( pLevel->p1 = iCur; pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext; pLevel->p2 = sqlcipher_sqlite3VdbeCurrentAddr(v); - iIn = pLevel->u.in.nIn; - for(j=nConstraint-1; j>=0; j--){ + assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); + + for(j=0; jaLTerm[j]; - if( (pTerm->eOperator & WO_IN)!=0 ) iIn--; if( j<16 && (pLoop->u.vtab.omitMask>>j)&1 ){ disableTerm(pLevel, pTerm); - }else if( (pTerm->eOperator & WO_IN)!=0 - && sqlcipher_sqlite3ExprVectorSize(pTerm->pExpr->pLeft)==1 + continue; + } + if( (pTerm->eOperator & WO_IN)!=0 + && (SMASKBIT32(j) & pLoop->u.vtab.mHandleIn)==0 + && !db->mallocFailed ){ Expr *pCompare; /* The comparison operator */ Expr *pRight; /* RHS of the comparison */ VdbeOp *pOp; /* Opcode to access the value of the IN constraint */ + int iIn; /* IN loop corresponding to the j-th constraint */ /* Reload the constraint value into reg[iReg+j+2]. The same value ** was loaded into the same register prior to the OP_VFilter, but ** the xFilter implementation might have changed the datatype or - ** encoding of the value in the register, so it *must* be reloaded. */ - assert( pLevel->u.in.aInLoop!=0 || db->mallocFailed ); - if( !db->mallocFailed ){ - assert( iIn>=0 && iInu.in.nIn ); + ** encoding of the value in the register, so it *must* be reloaded. + */ + for(iIn=0; ALWAYS(iInu.in.nIn); iIn++){ pOp = sqlcipher_sqlite3VdbeGetOp(v, pLevel->u.in.aInLoop[iIn].addrInTop); - assert( pOp->opcode==OP_Column || pOp->opcode==OP_Rowid ); - assert( pOp->opcode!=OP_Column || pOp->p3==iReg+j+2 ); - assert( pOp->opcode!=OP_Rowid || pOp->p2==iReg+j+2 ); - testcase( pOp->opcode==OP_Rowid ); - sqlcipher_sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3); + if( (pOp->opcode==OP_Column && pOp->p3==iReg+j+2) + || (pOp->opcode==OP_Rowid && pOp->p2==iReg+j+2) + ){ + testcase( pOp->opcode==OP_Rowid ); + sqlcipher_sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3); + break; + } } /* Generate code that will continue to the next row if - ** the IN constraint is not satisfied */ + ** the IN constraint is not satisfied + */ pCompare = sqlcipher_sqlite3PExpr(pParse, TK_EQ, 0, 0); - assert( pCompare!=0 || db->mallocFailed ); - if( pCompare ){ - pCompare->pLeft = pTerm->pExpr->pLeft; + if( !db->mallocFailed ){ + int iFld = pTerm->u.x.iField; + Expr *pLeft = pTerm->pExpr->pLeft; + assert( pLeft!=0 ); + if( iFld>0 ){ + assert( pLeft->op==TK_VECTOR ); + assert( ExprUseXList(pLeft) ); + assert( iFld<=pLeft->x.pList->nExpr ); + pCompare->pLeft = pLeft->x.pList->a[iFld-1].pExpr; + }else{ + pCompare->pLeft = pLeft; + } pCompare->pRight = pRight = sqlcipher_sqlite3Expr(db, TK_REGISTER, 0); if( pRight ){ pRight->iTable = iReg+j+2; @@ -148412,11 +156265,11 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( ); } pCompare->pLeft = 0; - sqlcipher_sqlite3ExprDelete(db, pCompare); } + sqlcipher_sqlite3ExprDelete(db, pCompare); } } - assert( iIn==0 || db->mallocFailed ); + /* These registers need to be preserved in case there is an IN operator ** loop. So we could deallocate the registers here (and potentially ** reuse them later) if (pLoop->wsFlags & WHERE_IN_ABLE)==0. But it seems @@ -148444,12 +156297,15 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg); if( iRowidReg!=iReleaseReg ) sqlcipher_sqlite3ReleaseTempReg(pParse, iReleaseReg); addrNxt = pLevel->addrNxt; + if( pLevel->regFilter ){ + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt, + iRowidReg, 1); + VdbeCoverage(v); + filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady); + } sqlcipher_sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg); VdbeCoverage(v); pLevel->op = OP_Noop; - if( (pTerm->prereqAll & pLevel->notReady)==0 ){ - pTerm->wtFlags |= TERM_CODED; - } }else if( (pLoop->wsFlags & WHERE_IPK)!=0 && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0 ){ @@ -148697,14 +156553,18 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( ** a forward order scan on a descending index, interchange the ** start and end terms (pRangeStart and pRangeEnd). */ - if( (nEqnKeyCol && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) - || (bRev && pIdx->nKeyCol==nEq) - ){ + if( (nEqnColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) ){ SWAP(WhereTerm *, pRangeEnd, pRangeStart); SWAP(u8, bSeekPastNull, bStopAtNull); SWAP(u8, nBtm, nTop); } + if( iLevel>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 ){ + /* In case OP_SeekScan is used, ensure that the index cursor does not + ** point to a valid row for the first iteration of this loop. */ + sqlcipher_sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); + } + /* Generate code to evaluate all constraint terms using == or IN ** and store the values of those terms in an array of registers ** starting at regBase. @@ -148768,6 +156628,12 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 1, regBignull); VdbeComment((v, "NULL-scan pass ctr")); } + if( pLevel->regFilter ){ + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt, + regBase, nEq); + VdbeCoverage(v); + filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady); + } op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; assert( op!=0 ); @@ -148816,8 +156682,19 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( ** range (if any). */ nConstraint = nEq; + assert( pLevel->p2==0 ); if( pRangeEnd ){ Expr *pRight = pRangeEnd->pExpr->pRight; + if( addrSeekScan ){ + /* For a seek-scan that has a range on the lowest term of the index, + ** we have to make the top of the loop be code that sets the end + ** condition of the range. Otherwise, the OP_SeekScan might jump + ** over that initialization, leaving the range-end value set to the + ** range-start value, resulting in a wrong answer. + ** See ticket 5981a8c041a3c2f3 (2021-11-02). + */ + pLevel->p2 = sqlcipher_sqlite3VdbeCurrentAddr(v); + } codeExprOrVector(pParse, pRight, regBase+nEq, nTop); whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); if( (pRangeEnd->wtFlags & TERM_VNULL)==0 @@ -148851,7 +156728,7 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( sqlcipher_sqlite3DbFree(db, zEndAff); /* Top of the loop body */ - pLevel->p2 = sqlcipher_sqlite3VdbeCurrentAddr(v); + if( pLevel->p2==0 ) pLevel->p2 = sqlcipher_sqlite3VdbeCurrentAddr(v); /* Check if the index cursor is past the end of the range. */ if( nConstraint ){ @@ -148893,7 +156770,7 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( /* Seek the table cursor, if required */ omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0; + && (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0; if( omitTable ){ /* pIdx is a covering index. No need to access the main table. */ }else if( HasRowid(pIdx->pTable) ){ @@ -148927,7 +156804,7 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( ** move forward to the next index. ** https://sqlite.org/src/info/4e8e4857d32d401f */ - if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ + if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ){ whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo); } @@ -148946,7 +156823,7 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( /* The following assert() is not a requirement, merely an observation: ** The OR-optimization doesn't work for the right hand table of ** a LEFT JOIN: */ - assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ); + assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ); } /* Record the instruction used to terminate the loop. */ @@ -149041,7 +156918,7 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( */ if( pWInfo->nLevel>1 ){ int nNotReady; /* The number of notReady tables */ - struct SrcList_item *origSrc; /* Original list of tables */ + SrcItem *origSrc; /* Original list of tables */ nNotReady = pWInfo->nLevel - iLevel - 1; pOrTab = sqlcipher_sqlite3StackAllocRaw(db, sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); @@ -149084,7 +156961,7 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( iRetInit = sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn); /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y - ** Then for every term xN, evaluate as the subexpression: xN AND z + ** Then for every term xN, evaluate as the subexpression: xN AND y ** That way, terms in y that are factored into the disjunction will ** be picked up by the recursive calls to sqlcipher_sqlite3WhereBegin() below. ** @@ -149096,6 +156973,20 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( ** This optimization also only applies if the (x1 OR x2 OR ...) term ** is not contained in the ON clause of a LEFT JOIN. ** See ticket http://www.sqlite.org/src/info/f2369304e4 + ** + ** 2022-02-04: Do not push down slices of a row-value comparison. + ** In other words, "w" or "y" may not be a slice of a vector. Otherwise, + ** the initialization of the right-hand operand of the vector comparison + ** might not occur, or might occur only in an OR branch that is not + ** taken. dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1. + ** + ** 2022-03-03: Do not push down expressions that involve subqueries. + ** The subquery might get coded as a subroutine. Any table-references + ** in the subquery might be resolved to index-references for the index on + ** the OR branch in which the subroutine is coded. But if the subroutine + ** is invoked from a different OR branch that uses a different index, such + ** index-references will not work. tag-20220303a + ** https://sqlite.org/forum/forumpost/36937b197273d403 */ if( pWC->nTerm>1 ){ int iTerm; @@ -149104,9 +156995,12 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( if( &pWC->a[iTerm] == pTerm ) continue; testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL ); testcase( pWC->a[iTerm].wtFlags & TERM_CODED ); - if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue; + testcase( pWC->a[iTerm].wtFlags & TERM_SLICE ); + if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED|TERM_SLICE))!=0 ){ + continue; + } if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue; - testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO ); + if( ExprHasProperty(pExpr, EP_Subquery) ) continue; /* tag-20220303a */ pExpr = sqlcipher_sqlite3ExprDup(db, pExpr, 0); pAndExpr = sqlcipher_sqlite3ExprAnd(pParse, pAndExpr, pExpr); } @@ -149114,7 +157008,7 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( /* The extra 0x10000 bit on the opcode is masked off and does not ** become part of the new Expr.op. However, it does make the ** op==TK_AND comparison inside of sqlcipher_sqlite3PExpr() false, and this - ** prevents sqlcipher_sqlite3PExpr() from implementing AND short-circuit + ** prevents sqlcipher_sqlite3PExpr() from applying the AND short-circuit ** optimization, which we do not want here. */ pAndExpr = sqlcipher_sqlite3PExpr(pParse, TK_AND|0x10000, 0, pAndExpr); } @@ -149130,10 +157024,16 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */ + Expr *pDelete; /* Local copy of OR clause term */ int jmp1 = 0; /* Address of jump operation */ testcase( (pTabItem[0].fg.jointype & JT_LEFT)!=0 - && !ExprHasProperty(pOrExpr, EP_FromJoin) + && !ExprHasProperty(pOrExpr, EP_OuterON) ); /* See TH3 vtab25.400 and ticket 614b25314c766238 */ + pDelete = pOrExpr = sqlcipher_sqlite3ExprDup(db, pOrExpr, 0); + if( db->mallocFailed ){ + sqlcipher_sqlite3ExprDelete(db, pDelete); + continue; + } if( pAndExpr ){ pAndExpr->pLeft = pOrExpr; pOrExpr = pAndExpr; @@ -149141,9 +157041,9 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( /* Loop through table entries that match term pOrTerm. */ ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1)); WHERETRACE(0xffff, ("Subplan for OR-clause:\n")); - pSubWInfo = sqlcipher_sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, + pSubWInfo = sqlcipher_sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, 0, WHERE_OR_SUBCLAUSE, iCovCur); - assert( pSubWInfo || pParse->nErr || db->mallocFailed ); + assert( pSubWInfo || pParse->nErr ); if( pSubWInfo ){ WhereLoop *pSubLoop; int addrExplain = sqlcipher_sqlite3WhereExplainOneScan( @@ -149248,10 +157148,14 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( sqlcipher_sqlite3WhereEnd(pSubWInfo); ExplainQueryPlanPop(pParse); } + sqlcipher_sqlite3ExprDelete(db, pDelete); } } ExplainQueryPlanPop(pParse); - pLevel->u.pCovidx = pCov; + assert( pLevel->pWLoop==pLoop ); + assert( (pLoop->wsFlags & WHERE_MULTI_OR)!=0 ); + assert( (pLoop->wsFlags & WHERE_IN_ABLE)==0 ); + pLevel->u.pCoveringIdx = pCov; if( pCov ) pLevel->iIdxCur = iCovCur; if( pAndExpr ){ pAndExpr->pLeft = 0; @@ -149261,6 +157165,14 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( sqlcipher_sqlite3VdbeGoto(v, pLevel->addrBrk); sqlcipher_sqlite3VdbeResolveLabel(v, iLoopBody); + /* Set the P2 operand of the OP_Return opcode that will end the current + ** loop to point to this spot, which is the top of the next containing + ** loop. The byte-code formatter will use that P2 value as a hint to + ** indent everything in between the this point and the final OP_Return. + ** See tag-20220407a in vdbe.c and shell.c */ + assert( pLevel->op==OP_Return ); + pLevel->p2 = sqlcipher_sqlite3VdbeCurrentAddr(v); + if( pWInfo->nLevel>1 ){ sqlcipher_sqlite3StackFree(db, pOrTab); } if( !untestedTerms ) disableTerm(pLevel, pTerm); }else @@ -149323,10 +157235,22 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( } pE = pTerm->pExpr; assert( pE!=0 ); - if( (pTabItem->fg.jointype&JT_LEFT) && !ExprHasProperty(pE,EP_FromJoin) ){ - continue; + if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ){ + if( !ExprHasProperty(pE,EP_OuterON|EP_InnerON) ){ + /* Defer processing WHERE clause constraints until after outer + ** join processing. tag-20220513a */ + continue; + }else if( (pTabItem->fg.jointype & JT_LEFT)==JT_LEFT + && !ExprHasProperty(pE,EP_OuterON) ){ + continue; + }else{ + Bitmask m = sqlcipher_sqlite3WhereGetMask(&pWInfo->sMaskSet, pE->w.iJoin); + if( m & pLevel->notReady ){ + /* An ON clause that is not ripe */ + continue; + } + } } - if( iLoop==1 && !sqlcipher_sqlite3ExprCoveredByIndex(pE, pLevel->iTabCur, pIdx) ){ iNext = 2; continue; @@ -149378,14 +157302,14 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( ** then we cannot use the "t1.a=t2.b" constraint, but we can code ** the implied "t1.a=123" constraint. */ - for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ + for(pTerm=pWC->a, j=pWC->nBase; j>0; j--, pTerm++){ Expr *pE, sEAlt; WhereTerm *pAlt; if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue; if( (pTerm->eOperator & WO_EQUIV)==0 ) continue; if( pTerm->leftCursor!=iCur ) continue; - if( pTabItem->fg.jointype & JT_LEFT ) continue; + if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ) continue; pE = pTerm->pExpr; #ifdef WHERETRACE_ENABLED /* 0x800 */ if( sqlcipher_sqlite3WhereTrace & 0x800 ){ @@ -149393,14 +157317,15 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( sqlcipher_sqlite3WhereTermPrint(pTerm, pWC->nTerm-j); } #endif - assert( !ExprHasProperty(pE, EP_FromJoin) ); + assert( !ExprHasProperty(pE, EP_OuterON) ); assert( (pTerm->prereqRight & pLevel->notReady)!=0 ); + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); pAlt = sqlcipher_sqlite3WhereFindTerm(pWC, iCur, pTerm->u.x.leftColumn, notReady, WO_EQ|WO_IN|WO_IS, 0); if( pAlt==0 ) continue; if( pAlt->wtFlags & (TERM_CODED) ) continue; if( (pAlt->eOperator & WO_IN) - && (pAlt->pExpr->flags & EP_xIsSelect) + && ExprUseXSelect(pAlt->pExpr) && (pAlt->pExpr->x.pSelect->pEList->nExpr>1) ){ continue; @@ -149412,6 +157337,48 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( sEAlt = *pAlt->pExpr; sEAlt.pLeft = pE->pLeft; sqlcipher_sqlite3ExprIfFalse(pParse, &sEAlt, addrCont, SQLITE_JUMPIFNULL); + pAlt->wtFlags |= TERM_CODED; + } + + /* For a RIGHT OUTER JOIN, record the fact that the current row has + ** been matched at least once. + */ + if( pLevel->pRJ ){ + Table *pTab; + int nPk; + int r; + int jmp1 = 0; + WhereRightJoin *pRJ = pLevel->pRJ; + + /* pTab is the right-hand table of the RIGHT JOIN. Generate code that + ** will record that the current row of that table has been matched at + ** least once. This is accomplished by storing the PK for the row in + ** both the iMatch index and the regBloom Bloom filter. + */ + pTab = pWInfo->pTabList->a[pLevel->iFrom].pTab; + if( HasRowid(pTab) ){ + r = sqlcipher_sqlite3GetTempRange(pParse, 2); + sqlcipher_sqlite3ExprCodeGetColumnOfTable(v, pTab, pLevel->iTabCur, -1, r+1); + nPk = 1; + }else{ + int iPk; + Index *pPk = sqlcipher_sqlite3PrimaryKeyIndex(pTab); + nPk = pPk->nKeyCol; + r = sqlcipher_sqlite3GetTempRange(pParse, nPk+1); + for(iPk=0; iPkaiColumn[iPk]; + sqlcipher_sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+1+iPk); + } + } + jmp1 = sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, 0, r+1, nPk); + VdbeCoverage(v); + VdbeComment((v, "match against %s", pTab->zName)); + sqlcipher_sqlite3VdbeAddOp3(v, OP_MakeRecord, r+1, nPk, r); + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pRJ->iMatch, r, r+1, nPk); + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pRJ->regBloom, 0, r+1, nPk); + sqlcipher_sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); + sqlcipher_sqlite3VdbeJumpHere(v, jmp1); + sqlcipher_sqlite3ReleaseTempRange(pParse, r, nPk+1); } /* For a LEFT OUTER JOIN, generate code that will record the fact that @@ -149421,7 +157388,31 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( pLevel->addrFirst = sqlcipher_sqlite3VdbeCurrentAddr(v); sqlcipher_sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin); VdbeComment((v, "record LEFT JOIN hit")); - for(pTerm=pWC->a, j=0; jnTerm; j++, pTerm++){ + if( pLevel->pRJ==0 ){ + goto code_outer_join_constraints; /* WHERE clause constraints */ + } + } + + if( pLevel->pRJ ){ + /* Create a subroutine used to process all interior loops and code + ** of the RIGHT JOIN. During normal operation, the subroutine will + ** be in-line with the rest of the code. But at the end, a separate + ** loop will run that invokes this subroutine for unmatched rows + ** of pTab, with all tables to left begin set to NULL. + */ + WhereRightJoin *pRJ = pLevel->pRJ; + sqlcipher_sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pRJ->regReturn); + pRJ->addrSubrtn = sqlcipher_sqlite3VdbeCurrentAddr(v); + assert( pParse->withinRJSubrtn < 255 ); + pParse->withinRJSubrtn++; + + /* WHERE clause constraints must be deferred until after outer join + ** row elimination has completed, since WHERE clause constraints apply + ** to the results of the OUTER JOIN. The following loop generates the + ** appropriate WHERE clause constraint checks. tag-20220513a. + */ + code_outer_join_constraints: + for(pTerm=pWC->a, j=0; jnBase; j++, pTerm++){ testcase( pTerm->wtFlags & TERM_VIRTUAL ); testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; @@ -149429,6 +157420,7 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( assert( pWInfo->untestedTerms ); continue; } + if( pTabItem->fg.jointype & JT_LTORJ ) continue; assert( pTerm->pExpr ); sqlcipher_sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); pTerm->wtFlags |= TERM_CODED; @@ -149449,6 +157441,96 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereCodeOneLoopStart( return pLevel->notReady; } +/* +** Generate the code for the loop that finds all non-matched terms +** for a RIGHT JOIN. +*/ +SQLITE_PRIVATE SQLITE_NOINLINE void sqlcipher_sqlite3WhereRightJoinLoop( + WhereInfo *pWInfo, + int iLevel, + WhereLevel *pLevel +){ + Parse *pParse = pWInfo->pParse; + Vdbe *v = pParse->pVdbe; + WhereRightJoin *pRJ = pLevel->pRJ; + Expr *pSubWhere = 0; + WhereClause *pWC = &pWInfo->sWC; + WhereInfo *pSubWInfo; + WhereLoop *pLoop = pLevel->pWLoop; + SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; + SrcList sFrom; + Bitmask mAll = 0; + int k; + + ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pTab->zName)); + sqlcipher_sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, + pRJ->regReturn); + for(k=0; ka[k].pWLoop->maskSelf; + sqlcipher_sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); + iIdxCur = pWInfo->a[k].iIdxCur; + if( iIdxCur ){ + sqlcipher_sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); + } + } + if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){ + mAll |= pLoop->maskSelf; + for(k=0; knTerm; k++){ + WhereTerm *pTerm = &pWC->a[k]; + if( (pTerm->wtFlags & (TERM_VIRTUAL|TERM_SLICE))!=0 + && pTerm->eOperator!=WO_ROWVAL + ){ + break; + } + if( pTerm->prereqAll & ~mAll ) continue; + if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue; + pSubWhere = sqlcipher_sqlite3ExprAnd(pParse, pSubWhere, + sqlcipher_sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); + } + } + sFrom.nSrc = 1; + sFrom.nAlloc = 1; + memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem)); + sFrom.a[0].fg.jointype = 0; + assert( pParse->withinRJSubrtn < 100 ); + pParse->withinRJSubrtn++; + pSubWInfo = sqlcipher_sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0, + WHERE_RIGHT_JOIN, 0); + if( pSubWInfo ){ + int iCur = pLevel->iTabCur; + int r = ++pParse->nMem; + int nPk; + int jmp; + int addrCont = sqlcipher_sqlite3WhereContinueLabel(pSubWInfo); + Table *pTab = pTabItem->pTab; + if( HasRowid(pTab) ){ + sqlcipher_sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, r); + nPk = 1; + }else{ + int iPk; + Index *pPk = sqlcipher_sqlite3PrimaryKeyIndex(pTab); + nPk = pPk->nKeyCol; + pParse->nMem += nPk - 1; + for(iPk=0; iPkaiColumn[iPk]; + sqlcipher_sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+iPk); + } + } + jmp = sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Filter, pRJ->regBloom, 0, r, nPk); + VdbeCoverage(v); + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, addrCont, r, nPk); + VdbeCoverage(v); + sqlcipher_sqlite3VdbeJumpHere(v, jmp); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Gosub, pRJ->regReturn, pRJ->addrSubrtn); + sqlcipher_sqlite3WhereEnd(pSubWInfo); + } + sqlcipher_sqlite3ExprDelete(pParse->db, pSubWhere); + ExplainQueryPlanPop(pParse); + assert( pParse->withinRJSubrtn>0 ); + pParse->withinRJSubrtn--; +} + /************** End of wherecode.c *******************************************/ /************** Begin file whereexpr.c ***************************************/ /* @@ -149517,7 +157599,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){ if( pWC->nTerm>=pWC->nSlot ){ WhereTerm *pOld = pWC->a; sqlcipher_sqlite3 *db = pWC->pWInfo->pParse->db; - pWC->a = sqlcipher_sqlite3DbMallocRawNN(db, sizeof(pWC->a[0])*pWC->nSlot*2 ); + pWC->a = sqlcipher_sqlite3WhereMalloc(pWC->pWInfo, sizeof(pWC->a[0])*pWC->nSlot*2 ); if( pWC->a==0 ){ if( wtFlags & TERM_DYNAMIC ){ sqlcipher_sqlite3ExprDelete(db, p); @@ -149526,12 +157608,10 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){ return 0; } memcpy(pWC->a, pOld, sizeof(pWC->a[0])*pWC->nTerm); - if( pOld!=pWC->aStatic ){ - sqlcipher_sqlite3DbFree(db, pOld); - } - pWC->nSlot = sqlcipher_sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]); + pWC->nSlot = pWC->nSlot*2; } pTerm = &pWC->a[idx = pWC->nTerm++]; + if( (wtFlags & TERM_VIRTUAL)==0 ) pWC->nBase = pWC->nTerm; if( p && ExprHasProperty(p, EP_Unlikely) ){ pTerm->truthProb = sqlcipher_sqlite3LogEst(p->iTable) - 270; }else{ @@ -149648,6 +157728,7 @@ static int isLikeOrGlob( #ifdef SQLITE_EBCDIC if( *pnoCase ) return 0; #endif + assert( ExprUseXList(pExpr) ); pList = pExpr->x.pList; pLeft = pList->a[1].pExpr; @@ -149663,7 +157744,8 @@ static int isLikeOrGlob( sqlcipher_sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); }else if( op==TK_STRING ){ - z = (u8*)pRight->u.zToken; + assert( !ExprHasProperty(pRight, EP_IntValue) ); + z = (u8*)pRight->u.zToken; } if( z ){ @@ -149692,7 +157774,9 @@ static int isLikeOrGlob( pPrefix = sqlcipher_sqlite3Expr(db, TK_STRING, (char*)z); if( pPrefix ){ int iFrom, iTo; - char *zNew = pPrefix->u.zToken; + char *zNew; + assert( !ExprHasProperty(pPrefix, EP_IntValue) ); + zNew = pPrefix->u.zToken; zNew[cnt] = 0; for(iFrom=iTo=0; iFromop!=TK_COLUMN || sqlcipher_sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT - || IsVirtual(pLeft->y.pTab) /* Value might be numeric */ + || (ALWAYS( ExprUseYTab(pLeft) ) + && pLeft->y.pTab + && IsVirtual(pLeft->y.pTab)) /* Might be numeric */ ){ int isNum; double rDummy; @@ -149744,6 +157830,7 @@ static int isLikeOrGlob( if( op==TK_VARIABLE ){ Vdbe *v = pParse->pVdbe; sqlcipher_sqlite3VdbeSetVarmask(v, pRight->iColumn); + assert( !ExprHasProperty(pRight, EP_IntValue) ); if( *pisComplete && pRight->u.zToken[1] ){ /* If the rhs of the LIKE expression is a variable, and the current ** value of the variable means there is no need to invoke the LIKE @@ -149817,6 +157904,7 @@ static int isAuxiliaryVtabOperator( Expr *pCol; /* Column reference */ int i; + assert( ExprUseXList(pExpr) ); pList = pExpr->x.pList; if( pList==0 || pList->nExpr!=2 ){ return 0; @@ -149830,9 +157918,11 @@ static int isAuxiliaryVtabOperator( ** MATCH(expression,vtab_column) */ pCol = pList->a[1].pExpr; + assert( pCol->op!=TK_COLUMN || ExprUseYTab(pCol) ); testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); if( ExprIsVtab(pCol) ){ for(i=0; iu.zToken, aOp[i].zOp)==0 ){ *peOp2 = aOp[i].eOp2; *ppRight = pList->a[0].pExpr; @@ -149853,6 +157943,7 @@ static int isAuxiliaryVtabOperator( ** with function names in an arbitrary case. */ pCol = pList->a[0].pExpr; + assert( pCol->op!=TK_COLUMN || ExprUseYTab(pCol) ); testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); if( ExprIsVtab(pCol) ){ sqlcipher_sqlite3_vtab *pVtab; @@ -149862,6 +157953,7 @@ static int isAuxiliaryVtabOperator( pVtab = sqlcipher_sqlite3GetVTable(db, pCol->y.pTab)->pVtab; assert( pVtab!=0 ); assert( pVtab->pModule!=0 ); + assert( !ExprHasProperty(pExpr, EP_IntValue) ); pMod = (sqlcipher_sqlite3_module *)pVtab->pModule; if( pMod->xFindFunction!=0 ){ i = pMod->xFindFunction(pVtab,2, pExpr->u.zToken, &xNotUsed, &pNotUsed); @@ -149877,10 +157969,12 @@ static int isAuxiliaryVtabOperator( int res = 0; Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; + assert( pLeft->op!=TK_COLUMN || ExprUseYTab(pLeft) ); testcase( pLeft->op==TK_COLUMN && pLeft->y.pTab==0 ); if( ExprIsVtab(pLeft) ){ res++; } + assert( pRight==0 || pRight->op!=TK_COLUMN || ExprUseYTab(pRight) ); testcase( pRight && pRight->op==TK_COLUMN && pRight->y.pTab==0 ); if( pRight && ExprIsVtab(pRight) ){ res++; @@ -149902,9 +157996,9 @@ static int isAuxiliaryVtabOperator( ** a join, then transfer the appropriate markings over to derived. */ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){ - if( pDerived ){ - pDerived->flags |= pBase->flags & EP_FromJoin; - pDerived->iRightJoinTable = pBase->iRightJoinTable; + if( pDerived && ExprHasProperty(pBase, EP_OuterON|EP_InnerON) ){ + pDerived->flags |= pBase->flags & (EP_OuterON|EP_InnerON); + pDerived->w.iJoin = pBase->w.iJoin; } } @@ -149964,6 +158058,7 @@ static void whereCombineDisjuncts( int op; /* Operator for the combined expression */ int idxNew; /* Index in pWC of the next virtual term */ + if( (pOne->wtFlags | pTwo->wtFlags) & TERM_VNULL ) return; if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp @@ -150132,6 +158227,7 @@ static void exprAnalyzeOrTerm( pOrTerm->u.pAndInfo = pAndInfo; pOrTerm->wtFlags |= TERM_ANDINFO; pOrTerm->eOperator = WO_AND; + pOrTerm->leftCursor = -1; pAndWC = &pAndInfo->wc; memset(pAndWC->aStatic, 0, sizeof(pAndWC->aStatic)); sqlcipher_sqlite3WhereClauseInit(pAndWC, pWC->pWInfo); @@ -150174,11 +158270,10 @@ static void exprAnalyzeOrTerm( ** empty. */ pOrInfo->indexable = indexable; + pTerm->eOperator = WO_OR; + pTerm->leftCursor = -1; if( indexable ){ - pTerm->eOperator = WO_OR; pWC->hasOr = 1; - }else{ - pTerm->eOperator = WO_OR; } /* For a two-way OR, attempt to implementation case 2. @@ -150233,7 +158328,7 @@ static void exprAnalyzeOrTerm( pOrTerm = pOrWc->a; for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ assert( pOrTerm->eOperator & WO_EQ ); - pOrTerm->wtFlags &= ~TERM_OR_OK; + pOrTerm->wtFlags &= ~TERM_OK; if( pOrTerm->leftCursor==iCursor ){ /* This is the 2-bit case and we are on the second iteration and ** current term is from the first iteration. So skip this term. */ @@ -150251,6 +158346,7 @@ static void exprAnalyzeOrTerm( assert( pOrTerm->wtFlags & (TERM_COPIED|TERM_VIRTUAL) ); continue; } + assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); iColumn = pOrTerm->u.x.leftColumn; iCursor = pOrTerm->leftCursor; pLeft = pOrTerm->pExpr->pLeft; @@ -150271,8 +158367,9 @@ static void exprAnalyzeOrTerm( okToChngToIN = 1; for(; i>=0 && okToChngToIN; i--, pOrTerm++){ assert( pOrTerm->eOperator & WO_EQ ); + assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); if( pOrTerm->leftCursor!=iCursor ){ - pOrTerm->wtFlags &= ~TERM_OR_OK; + pOrTerm->wtFlags &= ~TERM_OK; }else if( pOrTerm->u.x.leftColumn!=iColumn || (iColumn==XN_EXPR && sqlcipher_sqlite3ExprCompare(pParse, pOrTerm->pExpr->pLeft, pLeft, -1) )){ @@ -150288,7 +158385,7 @@ static void exprAnalyzeOrTerm( if( affRight!=0 && affRight!=affLeft ){ okToChngToIN = 0; }else{ - pOrTerm->wtFlags |= TERM_OR_OK; + pOrTerm->wtFlags |= TERM_OK; } } } @@ -150305,8 +158402,9 @@ static void exprAnalyzeOrTerm( Expr *pNew; /* The complete IN operator */ for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ - if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; + if( (pOrTerm->wtFlags & TERM_OK)==0 ) continue; assert( pOrTerm->eOperator & WO_EQ ); + assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); assert( pOrTerm->leftCursor==iCursor ); assert( pOrTerm->u.x.leftColumn==iColumn ); pDup = sqlcipher_sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); @@ -150319,12 +158417,12 @@ static void exprAnalyzeOrTerm( if( pNew ){ int idxNew; transferJoinMarkings(pNew, pExpr); - assert( !ExprHasProperty(pNew, EP_xIsSelect) ); + assert( ExprUseXList(pNew) ); pNew->x.pList = pList; idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew==0 ); exprAnalyze(pSrc, pWC, idxNew); - /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where used again */ + /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where reused */ markTermAsChild(pWC, idxNew, idxTerm); }else{ sqlcipher_sqlite3ExprListDelete(db, pList); @@ -150354,7 +158452,7 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr){ CollSeq *pColl; if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0; if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0; - if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0; + if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; aff1 = sqlcipher_sqlite3ExprAffinity(pExpr->pLeft); aff2 = sqlcipher_sqlite3ExprAffinity(pExpr->pRight); if( aff1!=aff2 @@ -150385,7 +158483,9 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){ int i; for(i=0; inSrc; i++){ mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect); - mask |= sqlcipher_sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn); + if( pSrc->a[i].fg.isUsing==0 ){ + mask |= sqlcipher_sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].u3.pOn); + } if( pSrc->a[i].fg.isTabFunc ){ mask |= sqlcipher_sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg); } @@ -150447,7 +158547,9 @@ static int exprMightBeIndexed( assert( TK_ISop==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){ + assert( ExprUseXList(pExpr) ); pExpr = pExpr->x.pList->a[0].pExpr; + } if( pExpr->op==TK_COLUMN ){ @@ -150460,6 +158562,7 @@ static int exprMightBeIndexed( return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr); } + /* ** The input to this routine is an WhereTerm structure with only the ** "pExpr" field filled in. The job of this routine is to analyze the @@ -150502,36 +158605,67 @@ static void exprAnalyze( if( db->mallocFailed ){ return; } + assert( pWC->nTerm > idxTerm ); pTerm = &pWC->a[idxTerm]; pMaskSet = &pWInfo->sMaskSet; pExpr = pTerm->pExpr; + assert( pExpr!=0 ); /* Because malloc() has not failed */ assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE ); + pMaskSet->bVarSelect = 0; prereqLeft = sqlcipher_sqlite3WhereExprUsage(pMaskSet, pExpr->pLeft); op = pExpr->op; if( op==TK_IN ){ assert( pExpr->pRight==0 ); if( sqlcipher_sqlite3ExprCheckIN(pParse, pExpr) ) return; - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ pTerm->prereqRight = exprSelectUsage(pMaskSet, pExpr->x.pSelect); }else{ pTerm->prereqRight = sqlcipher_sqlite3WhereExprListUsage(pMaskSet, pExpr->x.pList); } - }else if( op==TK_ISNULL ){ - pTerm->prereqRight = 0; + prereqAll = prereqLeft | pTerm->prereqRight; }else{ pTerm->prereqRight = sqlcipher_sqlite3WhereExprUsage(pMaskSet, pExpr->pRight); + if( pExpr->pLeft==0 + || ExprHasProperty(pExpr, EP_xIsSelect|EP_IfNullRow) + || pExpr->x.pList!=0 + ){ + prereqAll = sqlcipher_sqlite3WhereExprUsageNN(pMaskSet, pExpr); + }else{ + prereqAll = prereqLeft | pTerm->prereqRight; + } } - pMaskSet->bVarSelect = 0; - prereqAll = sqlcipher_sqlite3WhereExprUsageNN(pMaskSet, pExpr); if( pMaskSet->bVarSelect ) pTerm->wtFlags |= TERM_VARSELECT; - if( ExprHasProperty(pExpr, EP_FromJoin) ){ - Bitmask x = sqlcipher_sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable); - prereqAll |= x; - extraRight = x-1; /* ON clause terms may not be used with an index - ** on left table of a LEFT JOIN. Ticket #3015 */ - if( (prereqAll>>1)>=x ){ - sqlcipher_sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); - return; + +#ifdef SQLITE_DEBUG + if( prereqAll!=sqlcipher_sqlite3WhereExprUsageNN(pMaskSet, pExpr) ){ + printf("\n*** Incorrect prereqAll computed for:\n"); + sqlcipher_sqlite3TreeViewExpr(0,pExpr,0); + assert( 0 ); + } +#endif + + if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ){ + Bitmask x = sqlcipher_sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin); + if( ExprHasProperty(pExpr, EP_OuterON) ){ + prereqAll |= x; + extraRight = x-1; /* ON clause terms may not be used with an index + ** on left table of a LEFT JOIN. Ticket #3015 */ + if( (prereqAll>>1)>=x ){ + sqlcipher_sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); + return; + } + }else if( (prereqAll>>1)>=x ){ + /* The ON clause of an INNER JOIN references a table to its right. + ** Most other SQL database engines raise an error. But SQLite versions + ** 3.0 through 3.38 just put the ON clause constraint into the WHERE + ** clause and carried on. Beginning with 3.39, raise an error only + ** if there is a RIGHT or FULL JOIN in the query. This makes SQLite + ** more like other systems, and also preserves legacy. */ + if( ALWAYS(pSrc->nSrc>0) && (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ + sqlcipher_sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); + return; + } + ExprClearProperty(pExpr, EP_InnerON); } } pTerm->prereqAll = prereqAll; @@ -150547,17 +158681,20 @@ static void exprAnalyze( if( pTerm->u.x.iField>0 ){ assert( op==TK_IN ); assert( pLeft->op==TK_VECTOR ); + assert( ExprUseXList(pLeft) ); pLeft = pLeft->x.pList->a[pTerm->u.x.iField-1].pExpr; } if( exprMightBeIndexed(pSrc, prereqLeft, aiCurCol, pLeft, op) ){ pTerm->leftCursor = aiCurCol[0]; + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); pTerm->u.x.leftColumn = aiCurCol[1]; pTerm->eOperator = operatorMask(op) & opMask; } if( op==TK_IS ) pTerm->wtFlags |= TERM_IS; if( pRight && exprMightBeIndexed(pSrc, pTerm->prereqRight, aiCurCol, pRight, op) + && !ExprHasProperty(pRight, EP_FixedCol) ){ WhereTerm *pNew; Expr *pDup; @@ -150588,11 +158725,23 @@ static void exprAnalyze( } pNew->wtFlags |= exprCommute(pParse, pDup); pNew->leftCursor = aiCurCol[0]; + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); pNew->u.x.leftColumn = aiCurCol[1]; testcase( (prereqLeft | extraRight) != prereqLeft ); pNew->prereqRight = prereqLeft | extraRight; pNew->prereqAll = prereqAll; pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; + }else + if( op==TK_ISNULL + && !ExprHasProperty(pExpr,EP_OuterON) + && 0==sqlcipher_sqlite3ExprCanBeNull(pLeft) + ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + pExpr->op = TK_TRUEFALSE; + pExpr->u.zToken = "false"; + ExprSetProperty(pExpr, EP_IsFalse); + pTerm->prereqAll = 0; + pTerm->eOperator = 0; } } @@ -150613,9 +158762,11 @@ static void exprAnalyze( ** BETWEEN term is skipped. */ else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){ - ExprList *pList = pExpr->x.pList; + ExprList *pList; int i; static const u8 ops[] = {TK_GE, TK_LE}; + assert( ExprUseXList(pExpr) ); + pList = pExpr->x.pList; assert( pList!=0 ); assert( pList->nExpr==2 ); for(i=0; i<2; i++){ @@ -150644,6 +158795,42 @@ static void exprAnalyze( pTerm = &pWC->a[idxTerm]; } #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ + /* The form "x IS NOT NULL" can sometimes be evaluated more efficiently + ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a + ** virtual term of that form. + ** + ** The virtual term must be tagged with TERM_VNULL. + */ + else if( pExpr->op==TK_NOTNULL ){ + if( pExpr->pLeft->op==TK_COLUMN + && pExpr->pLeft->iColumn>=0 + && !ExprHasProperty(pExpr, EP_OuterON) + ){ + Expr *pNewExpr; + Expr *pLeft = pExpr->pLeft; + int idxNew; + WhereTerm *pNewTerm; + + pNewExpr = sqlcipher_sqlite3PExpr(pParse, TK_GT, + sqlcipher_sqlite3ExprDup(db, pLeft, 0), + sqlcipher_sqlite3ExprAlloc(db, TK_NULL, 0, 0)); + + idxNew = whereClauseInsert(pWC, pNewExpr, + TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); + if( idxNew ){ + pNewTerm = &pWC->a[idxNew]; + pNewTerm->prereqRight = 0; + pNewTerm->leftCursor = pLeft->iTable; + pNewTerm->u.x.leftColumn = pLeft->iColumn; + pNewTerm->eOperator = WO_GT; + markTermAsChild(pWC, idxNew, idxTerm); + pTerm = &pWC->a[idxTerm]; + pTerm->wtFlags |= TERM_COPIED; + pNewTerm->prereqAll = pTerm->prereqAll; + } + } + } + #ifndef SQLITE_OMIT_LIKE_OPTIMIZATION /* Add constraints to reduce the search space on a LIKE or GLOB @@ -150659,7 +158846,8 @@ static void exprAnalyze( ** bound is made all lowercase so that the bounds also work when comparing ** BLOBs. */ - if( pWC->op==TK_AND + else if( pExpr->op==TK_FUNCTION + && pWC->op==TK_AND && isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase) ){ Expr *pLeft; /* LHS of LIKE/GLOB operator */ @@ -150671,8 +158859,12 @@ static void exprAnalyze( const char *zCollSeqName; /* Name of collating sequence */ const u16 wtFlags = TERM_LIKEOPT | TERM_VIRTUAL | TERM_DYNAMIC; + assert( ExprUseXList(pExpr) ); pLeft = pExpr->x.pList->a[1].pExpr; pStr2 = sqlcipher_sqlite3ExprDup(db, pStr1, 0); + assert( pStr1==0 || !ExprHasProperty(pStr1, EP_IntValue) ); + assert( pStr2==0 || !ExprHasProperty(pStr2, EP_IntValue) ); + /* Convert the lower bound to upper-case and the upper bound to ** lower-case (upper-case is less than lower-case in ASCII) so that @@ -150729,81 +158921,38 @@ static void exprAnalyze( } #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ -#ifndef SQLITE_OMIT_VIRTUALTABLE - /* Add a WO_AUX auxiliary term to the constraint set if the - ** current expression is of the form "column OP expr" where OP - ** is an operator that gets passed into virtual tables but which is - ** not normally optimized for ordinary tables. In other words, OP - ** is one of MATCH, LIKE, GLOB, REGEXP, !=, IS, IS NOT, or NOT NULL. - ** This information is used by the xBestIndex methods of - ** virtual tables. The native query optimizer does not attempt - ** to do anything with MATCH functions. - */ - if( pWC->op==TK_AND ){ - Expr *pRight = 0, *pLeft = 0; - int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight); - while( res-- > 0 ){ - int idxNew; - WhereTerm *pNewTerm; - Bitmask prereqColumn, prereqExpr; - - prereqExpr = sqlcipher_sqlite3WhereExprUsage(pMaskSet, pRight); - prereqColumn = sqlcipher_sqlite3WhereExprUsage(pMaskSet, pLeft); - if( (prereqExpr & prereqColumn)==0 ){ - Expr *pNewExpr; - pNewExpr = sqlcipher_sqlite3PExpr(pParse, TK_MATCH, - 0, sqlcipher_sqlite3ExprDup(db, pRight, 0)); - if( ExprHasProperty(pExpr, EP_FromJoin) && pNewExpr ){ - ExprSetProperty(pNewExpr, EP_FromJoin); - pNewExpr->iRightJoinTable = pExpr->iRightJoinTable; - } - idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); - testcase( idxNew==0 ); - pNewTerm = &pWC->a[idxNew]; - pNewTerm->prereqRight = prereqExpr; - pNewTerm->leftCursor = pLeft->iTable; - pNewTerm->u.x.leftColumn = pLeft->iColumn; - pNewTerm->eOperator = WO_AUX; - pNewTerm->eMatchOp = eOp2; - markTermAsChild(pWC, idxNew, idxTerm); - pTerm = &pWC->a[idxTerm]; - pTerm->wtFlags |= TERM_COPIED; - pNewTerm->prereqAll = pTerm->prereqAll; - } - SWAP(Expr*, pLeft, pRight); - } - } -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create ** new terms for each component comparison - "a = ?" and "b = ?". The ** new terms completely replace the original vector comparison, which is ** no longer used. ** ** This is only required if at least one side of the comparison operation - ** is not a sub-select. */ - if( pWC->op==TK_AND - && (pExpr->op==TK_EQ || pExpr->op==TK_IS) - && (nLeft = sqlcipher_sqlite3ExprVectorSize(pExpr->pLeft))>1 - && sqlcipher_sqlite3ExprVectorSize(pExpr->pRight)==nLeft - && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 - || (pExpr->pRight->flags & EP_xIsSelect)==0) + ** is not a sub-select. + ** + ** tag-20220128a + */ + if( (pExpr->op==TK_EQ || pExpr->op==TK_IS) + && (nLeft = sqlcipher_sqlite3ExprVectorSize(pExpr->pLeft))>1 + && sqlcipher_sqlite3ExprVectorSize(pExpr->pRight)==nLeft + && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 + || (pExpr->pRight->flags & EP_xIsSelect)==0) + && pWC->op==TK_AND ){ int i; for(i=0; ipLeft, i); - Expr *pRight = sqlcipher_sqlite3ExprForVectorField(pParse, pExpr->pRight, i); + Expr *pLeft = sqlcipher_sqlite3ExprForVectorField(pParse, pExpr->pLeft, i, nLeft); + Expr *pRight = sqlcipher_sqlite3ExprForVectorField(pParse, pExpr->pRight, i, nLeft); pNew = sqlcipher_sqlite3PExpr(pParse, pExpr->op, pLeft, pRight); transferJoinMarkings(pNew, pExpr); - idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); + idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_SLICE); exprAnalyze(pSrc, pWC, idxNew); } pTerm = &pWC->a[idxTerm]; pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */ - pTerm->eOperator = 0; + pTerm->eOperator = WO_ROWVAL; } /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create @@ -150815,61 +158964,71 @@ static void exprAnalyze( ** This only works if the RHS is a simple SELECT (not a compound) that does ** not use window functions. */ - if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->u.x.iField==0 + else if( pExpr->op==TK_IN + && pTerm->u.x.iField==0 && pExpr->pLeft->op==TK_VECTOR + && ALWAYS( ExprUseXSelect(pExpr) ) && pExpr->x.pSelect->pPrior==0 #ifndef SQLITE_OMIT_WINDOWFUNC && pExpr->x.pSelect->pWin==0 #endif + && pWC->op==TK_AND ){ int i; for(i=0; ipLeft); i++){ int idxNew; - idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL); + idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL|TERM_SLICE); pWC->a[idxNew].u.x.iField = i+1; exprAnalyze(pSrc, pWC, idxNew); markTermAsChild(pWC, idxNew, idxTerm); } } -#ifdef SQLITE_ENABLE_STAT4 - /* When sqlite_stat4 histogram data is available an operator of the - ** form "x IS NOT NULL" can sometimes be evaluated more efficiently - ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a - ** virtual term of that form. - ** - ** Note that the virtual term must be tagged with TERM_VNULL. +#ifndef SQLITE_OMIT_VIRTUALTABLE + /* Add a WO_AUX auxiliary term to the constraint set if the + ** current expression is of the form "column OP expr" where OP + ** is an operator that gets passed into virtual tables but which is + ** not normally optimized for ordinary tables. In other words, OP + ** is one of MATCH, LIKE, GLOB, REGEXP, !=, IS, IS NOT, or NOT NULL. + ** This information is used by the xBestIndex methods of + ** virtual tables. The native query optimizer does not attempt + ** to do anything with MATCH functions. */ - if( pExpr->op==TK_NOTNULL - && pExpr->pLeft->op==TK_COLUMN - && pExpr->pLeft->iColumn>=0 - && !ExprHasProperty(pExpr, EP_FromJoin) - && OptimizationEnabled(db, SQLITE_Stat4) - ){ - Expr *pNewExpr; - Expr *pLeft = pExpr->pLeft; - int idxNew; - WhereTerm *pNewTerm; - - pNewExpr = sqlcipher_sqlite3PExpr(pParse, TK_GT, - sqlcipher_sqlite3ExprDup(db, pLeft, 0), - sqlcipher_sqlite3ExprAlloc(db, TK_NULL, 0, 0)); - - idxNew = whereClauseInsert(pWC, pNewExpr, - TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); - if( idxNew ){ - pNewTerm = &pWC->a[idxNew]; - pNewTerm->prereqRight = 0; - pNewTerm->leftCursor = pLeft->iTable; - pNewTerm->u.x.leftColumn = pLeft->iColumn; - pNewTerm->eOperator = WO_GT; - markTermAsChild(pWC, idxNew, idxTerm); - pTerm = &pWC->a[idxTerm]; - pTerm->wtFlags |= TERM_COPIED; - pNewTerm->prereqAll = pTerm->prereqAll; + else if( pWC->op==TK_AND ){ + Expr *pRight = 0, *pLeft = 0; + int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight); + while( res-- > 0 ){ + int idxNew; + WhereTerm *pNewTerm; + Bitmask prereqColumn, prereqExpr; + + prereqExpr = sqlcipher_sqlite3WhereExprUsage(pMaskSet, pRight); + prereqColumn = sqlcipher_sqlite3WhereExprUsage(pMaskSet, pLeft); + if( (prereqExpr & prereqColumn)==0 ){ + Expr *pNewExpr; + pNewExpr = sqlcipher_sqlite3PExpr(pParse, TK_MATCH, + 0, sqlcipher_sqlite3ExprDup(db, pRight, 0)); + if( ExprHasProperty(pExpr, EP_OuterON) && pNewExpr ){ + ExprSetProperty(pNewExpr, EP_OuterON); + pNewExpr->w.iJoin = pExpr->w.iJoin; + } + idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); + testcase( idxNew==0 ); + pNewTerm = &pWC->a[idxNew]; + pNewTerm->prereqRight = prereqExpr; + pNewTerm->leftCursor = pLeft->iTable; + pNewTerm->u.x.leftColumn = pLeft->iColumn; + pNewTerm->eOperator = WO_AUX; + pNewTerm->eMatchOp = eOp2; + markTermAsChild(pWC, idxNew, idxTerm); + pTerm = &pWC->a[idxTerm]; + pTerm->wtFlags |= TERM_COPIED; + pNewTerm->prereqAll = pTerm->prereqAll; + } + SWAP(Expr*, pLeft, pRight); } } -#endif /* SQLITE_ENABLE_STAT4 */ +#endif /* SQLITE_OMIT_VIRTUALTABLE */ /* Prevent ON clause terms of a LEFT JOIN from being used to drive ** an index for tables to the left of the join. @@ -150914,6 +159073,113 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u } } +/* +** Add either a LIMIT (if eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT) or +** OFFSET (if eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET) term to the +** where-clause passed as the first argument. The value for the term +** is found in register iReg. +** +** In the common case where the value is a simple integer +** (example: "LIMIT 5 OFFSET 10") then the expression codes as a +** TK_INTEGER so that it will be available to sqlcipher_sqlite3_vtab_rhs_value(). +** If not, then it codes as a TK_REGISTER expression. +*/ +static void whereAddLimitExpr( + WhereClause *pWC, /* Add the constraint to this WHERE clause */ + int iReg, /* Register that will hold value of the limit/offset */ + Expr *pExpr, /* Expression that defines the limit/offset */ + int iCsr, /* Cursor to which the constraint applies */ + int eMatchOp /* SQLITE_INDEX_CONSTRAINT_LIMIT or _OFFSET */ +){ + Parse *pParse = pWC->pWInfo->pParse; + sqlcipher_sqlite3 *db = pParse->db; + Expr *pNew; + int iVal = 0; + + if( sqlcipher_sqlite3ExprIsInteger(pExpr, &iVal) && iVal>=0 ){ + Expr *pVal = sqlcipher_sqlite3Expr(db, TK_INTEGER, 0); + if( pVal==0 ) return; + ExprSetProperty(pVal, EP_IntValue); + pVal->u.iValue = iVal; + pNew = sqlcipher_sqlite3PExpr(pParse, TK_MATCH, 0, pVal); + }else{ + Expr *pVal = sqlcipher_sqlite3Expr(db, TK_REGISTER, 0); + if( pVal==0 ) return; + pVal->iTable = iReg; + pNew = sqlcipher_sqlite3PExpr(pParse, TK_MATCH, 0, pVal); + } + if( pNew ){ + WhereTerm *pTerm; + int idx; + idx = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_VIRTUAL); + pTerm = &pWC->a[idx]; + pTerm->leftCursor = iCsr; + pTerm->eOperator = WO_AUX; + pTerm->eMatchOp = eMatchOp; + } +} + +/* +** Possibly add terms corresponding to the LIMIT and OFFSET clauses of the +** SELECT statement passed as the second argument. These terms are only +** added if: +** +** 1. The SELECT statement has a LIMIT clause, and +** 2. The SELECT statement is not an aggregate or DISTINCT query, and +** 3. The SELECT statement has exactly one object in its from clause, and +** that object is a virtual table, and +** 4. There are no terms in the WHERE clause that will not be passed +** to the virtual table xBestIndex method. +** 5. The ORDER BY clause, if any, will be made available to the xBestIndex +** method. +** +** LIMIT and OFFSET terms are ignored by most of the planner code. They +** exist only so that they may be passed to the xBestIndex method of the +** single virtual table in the FROM clause of the SELECT. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ + assert( p==0 || (p->pGroupBy==0 && (p->selFlags & SF_Aggregate)==0) ); + if( (p && p->pLimit) /* 1 */ + && (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */ + && (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pTab)) /* 3 */ + ){ + ExprList *pOrderBy = p->pOrderBy; + int iCsr = p->pSrc->a[0].iCursor; + int ii; + + /* Check condition (4). Return early if it is not met. */ + for(ii=0; iinTerm; ii++){ + if( pWC->a[ii].wtFlags & TERM_CODED ){ + /* This term is a vector operation that has been decomposed into + ** other, subsequent terms. It can be ignored. See tag-20220128a */ + assert( pWC->a[ii].wtFlags & TERM_VIRTUAL ); + assert( pWC->a[ii].eOperator==WO_ROWVAL ); + continue; + } + if( pWC->a[ii].leftCursor!=iCsr ) return; + } + + /* Check condition (5). Return early if it is not met. */ + if( pOrderBy ){ + for(ii=0; iinExpr; ii++){ + Expr *pExpr = pOrderBy->a[ii].pExpr; + if( pExpr->op!=TK_COLUMN ) return; + if( pExpr->iTable!=iCsr ) return; + if( pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) return; + } + } + + /* All conditions are met. Add the terms to the where-clause object. */ + assert( p->pLimit->op==TK_LIMIT ); + whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, + iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); + if( p->iOffset>0 ){ + whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, + iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); + } + } +} + /* ** Initialize a preallocated WhereClause structure. */ @@ -150925,6 +159191,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereClauseInit( pWC->hasOr = 0; pWC->pOuter = 0; pWC->nTerm = 0; + pWC->nBase = 0; pWC->nSlot = ArraySize(pWC->aStatic); pWC->a = pWC->aStatic; } @@ -150935,22 +159202,36 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereClauseInit( ** sqlcipher_sqlite3WhereClauseInit(). */ SQLITE_PRIVATE void sqlcipher_sqlite3WhereClauseClear(WhereClause *pWC){ - int i; - WhereTerm *a; sqlcipher_sqlite3 *db = pWC->pWInfo->pParse->db; - for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){ - if( a->wtFlags & TERM_DYNAMIC ){ - sqlcipher_sqlite3ExprDelete(db, a->pExpr); + assert( pWC->nTerm>=pWC->nBase ); + if( pWC->nTerm>0 ){ + WhereTerm *a = pWC->a; + WhereTerm *aLast = &pWC->a[pWC->nTerm-1]; +#ifdef SQLITE_DEBUG + int i; + /* Verify that every term past pWC->nBase is virtual */ + for(i=pWC->nBase; inTerm; i++){ + assert( (pWC->a[i].wtFlags & TERM_VIRTUAL)!=0 ); } - if( a->wtFlags & TERM_ORINFO ){ - whereOrInfoDelete(db, a->u.pOrInfo); - }else if( a->wtFlags & TERM_ANDINFO ){ - whereAndInfoDelete(db, a->u.pAndInfo); +#endif + while(1){ + assert( a->eMatchOp==0 || a->eOperator==WO_AUX ); + if( a->wtFlags & TERM_DYNAMIC ){ + sqlcipher_sqlite3ExprDelete(db, a->pExpr); + } + if( a->wtFlags & (TERM_ORINFO|TERM_ANDINFO) ){ + if( a->wtFlags & TERM_ORINFO ){ + assert( (a->wtFlags & TERM_ANDINFO)==0 ); + whereOrInfoDelete(db, a->u.pOrInfo); + }else{ + assert( (a->wtFlags & TERM_ANDINFO)!=0 ); + whereAndInfoDelete(db, a->u.pAndInfo); + } + } + if( a==aLast ) break; + a++; } } - if( pWC->a!=pWC->aStatic ){ - sqlcipher_sqlite3DbFree(db, pWC->a); - } } @@ -150958,28 +159239,52 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereClauseClear(WhereClause *pWC){ ** These routines walk (recursively) an expression tree and generate ** a bitmask indicating which tables are used in that expression ** tree. +** +** sqlcipher_sqlite3WhereExprUsage(MaskSet, Expr) -> +** +** Return a Bitmask of all tables referenced by Expr. Expr can be +** be NULL, in which case 0 is returned. +** +** sqlcipher_sqlite3WhereExprUsageNN(MaskSet, Expr) -> +** +** Same as sqlcipher_sqlite3WhereExprUsage() except that Expr must not be +** NULL. The "NN" suffix on the name stands for "Not Null". +** +** sqlcipher_sqlite3WhereExprListUsage(MaskSet, ExprList) -> +** +** Return a Bitmask of all tables referenced by every expression +** in the expression list ExprList. ExprList can be NULL, in which +** case 0 is returned. +** +** sqlcipher_sqlite3WhereExprUsageFull(MaskSet, ExprList) -> +** +** Internal use only. Called only by sqlcipher_sqlite3WhereExprUsageNN() for +** complex expressions that require pushing register values onto +** the stack. Many calls to sqlcipher_sqlite3WhereExprUsageNN() do not need +** the more complex analysis done by this routine. Hence, the +** computations done by this routine are broken out into a separate +** "no-inline" function to avoid the stack push overhead in the +** common case where it is not needed. */ -SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ +static SQLITE_NOINLINE Bitmask sqlcipher_sqlite3WhereExprUsageFull( + WhereMaskSet *pMaskSet, + Expr *p +){ Bitmask mask; - if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ - return sqlcipher_sqlite3WhereGetMask(pMaskSet, p->iTable); - }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ - assert( p->op!=TK_IF_NULL_ROW ); - return 0; - } mask = (p->op==TK_IF_NULL_ROW) ? sqlcipher_sqlite3WhereGetMask(pMaskSet, p->iTable) : 0; if( p->pLeft ) mask |= sqlcipher_sqlite3WhereExprUsageNN(pMaskSet, p->pLeft); if( p->pRight ){ mask |= sqlcipher_sqlite3WhereExprUsageNN(pMaskSet, p->pRight); assert( p->x.pList==0 ); - }else if( ExprHasProperty(p, EP_xIsSelect) ){ + }else if( ExprUseXSelect(p) ){ if( ExprHasProperty(p, EP_VarSelect) ) pMaskSet->bVarSelect = 1; mask |= exprSelectUsage(pMaskSet, p->x.pSelect); }else if( p->x.pList ){ mask |= sqlcipher_sqlite3WhereExprListUsage(pMaskSet, p->x.pList); } #ifndef SQLITE_OMIT_WINDOWFUNC - if( (p->op==TK_FUNCTION || p->op==TK_AGG_FUNCTION) && p->y.pWin ){ + if( (p->op==TK_FUNCTION || p->op==TK_AGG_FUNCTION) && ExprUseYWin(p) ){ + assert( p->y.pWin!=0 ); mask |= sqlcipher_sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pPartition); mask |= sqlcipher_sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pOrderBy); mask |= sqlcipher_sqlite3WhereExprUsage(pMaskSet, p->y.pWin->pFilter); @@ -150987,6 +159292,15 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, #endif return mask; } +SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ + if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ + return sqlcipher_sqlite3WhereGetMask(pMaskSet, p->iTable); + }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ + assert( p->op!=TK_IF_NULL_ROW ); + return 0; + } + return sqlcipher_sqlite3WhereExprUsageFull(pMaskSet, p); +} SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){ return p ? sqlcipher_sqlite3WhereExprUsageNN(pMaskSet,p) : 0; } @@ -151029,7 +159343,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereExprAnalyze( */ SQLITE_PRIVATE void sqlcipher_sqlite3WhereTabFuncArgs( Parse *pParse, /* Parsing context */ - struct SrcList_item *pItem, /* The FROM clause term to process */ + SrcItem *pItem, /* The FROM clause term to process */ WhereClause *pWC /* Xfer function arguments to here */ ){ Table *pTab; @@ -151044,6 +159358,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereTabFuncArgs( if( pArgs==0 ) return; for(j=k=0; jnExpr; j++){ Expr *pRhs; + u32 joinType; while( knCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;} if( k>=pTab->nCol ){ sqlcipher_sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d", @@ -151054,13 +159369,18 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereTabFuncArgs( if( pColRef==0 ) return; pColRef->iTable = pItem->iCursor; pColRef->iColumn = k++; + assert( ExprUseYTab(pColRef) ); pColRef->y.pTab = pTab; + pItem->colUsed |= sqlcipher_sqlite3ExprColUsed(pColRef); pRhs = sqlcipher_sqlite3PExpr(pParse, TK_UPLUS, sqlcipher_sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); pTerm = sqlcipher_sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs); - if( pItem->fg.jointype & JT_LEFT ){ - sqlcipher_sqlite3SetJoinExpr(pTerm, pItem->iCursor); + if( pItem->fg.jointype & (JT_LEFT|JT_LTORJ) ){ + joinType = EP_OuterON; + }else{ + joinType = EP_InnerON; } + sqlcipher_sqlite3SetJoinExpr(pTerm, pItem->iCursor, joinType); whereClauseInsert(pWC, pTerm, TERM_DYNAMIC); } } @@ -151099,19 +159419,19 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereTabFuncArgs( */ typedef struct HiddenIndexInfo HiddenIndexInfo; struct HiddenIndexInfo { - WhereClause *pWC; /* The Where clause being analyzed */ - Parse *pParse; /* The parsing context */ + WhereClause *pWC; /* The Where clause being analyzed */ + Parse *pParse; /* The parsing context */ + int eDistinct; /* Value to return from sqlcipher_sqlite3_vtab_distinct() */ + u32 mIn; /* Mask of terms that are IN (...) */ + u32 mHandleIn; /* Terms that vtab will handle as IN (...) */ + sqlcipher_sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST + ** because extra space is allocated to hold up + ** to nTerm such values */ }; /* Forward declaration of methods */ static int whereLoopResize(sqlcipher_sqlite3*, WhereLoop*, int); -/* Test variable that can be set to enable WHERE tracing */ -#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) -/***/ int sqlcipher_sqlite3WhereTrace = 0; -#endif - - /* ** Return the estimated number of output rows from a WHERE clause */ @@ -151171,7 +159491,33 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WhereOrderByLimitOptLabel(WhereInfo *pWInfo) } pInner = &pWInfo->a[pWInfo->nLevel-1]; assert( pInner->addrNxt!=0 ); - return pInner->addrNxt; + return pInner->pRJ ? pWInfo->iContinue : pInner->addrNxt; +} + +/* +** While generating code for the min/max optimization, after handling +** the aggregate-step call to min() or max(), check to see if any +** additional looping is required. If the output order is such that +** we are certain that the correct answer has already been found, then +** code an OP_Goto to by pass subsequent processing. +** +** Any extra OP_Goto that is coded here is an optimization. The +** correct answer should be obtained regardless. This OP_Goto just +** makes the answer appear faster. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3WhereMinMaxOptEarlyOut(Vdbe *v, WhereInfo *pWInfo){ + WhereLevel *pInner; + int i; + if( !pWInfo->bOrderedInnerLoop ) return; + if( pWInfo->nOBSat==0 ) return; + for(i=pWInfo->nLevel-1; i>=0; i--){ + pInner = &pWInfo->a[i]; + if( (pInner->pWLoop->wsFlags & WHERE_COLUMN_IN)!=0 ){ + sqlcipher_sqlite3VdbeGoto(v, pInner->addrNxt); + return; + } + } + sqlcipher_sqlite3VdbeGoto(v, pWInfo->iBreak); } /* @@ -151283,7 +159629,12 @@ whereOrInsert_done: SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){ int i; assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 ); - for(i=0; in; i++){ + assert( pMaskSet->n>0 || pMaskSet->ix[0]<0 ); + assert( iCursor>=-1 ); + if( pMaskSet->ix[0]==iCursor ){ + return 1; + } + for(i=1; in; i++){ if( pMaskSet->ix[i]==iCursor ){ return MASKBIT(i); } @@ -151291,6 +159642,30 @@ SQLITE_PRIVATE Bitmask sqlcipher_sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int return 0; } +/* Allocate memory that is automatically freed when pWInfo is freed. +*/ +SQLITE_PRIVATE void *sqlcipher_sqlite3WhereMalloc(WhereInfo *pWInfo, u64 nByte){ + WhereMemBlock *pBlock; + pBlock = sqlcipher_sqlite3DbMallocRawNN(pWInfo->pParse->db, nByte+sizeof(*pBlock)); + if( pBlock ){ + pBlock->pNext = pWInfo->pMemToFree; + pBlock->sz = nByte; + pWInfo->pMemToFree = pBlock; + pBlock++; + } + return (void*)pBlock; +} +SQLITE_PRIVATE void *sqlcipher_sqlite3WhereRealloc(WhereInfo *pWInfo, void *pOld, u64 nByte){ + void *pNew = sqlcipher_sqlite3WhereMalloc(pWInfo, nByte); + if( pNew && pOld ){ + WhereMemBlock *pOldBlk = (WhereMemBlock*)pOld; + pOldBlk--; + assert( pOldBlk->szsz); + } + return pNew; +} + /* ** Create a new mask for cursor iCursor. ** @@ -151310,7 +159685,9 @@ static void createMask(WhereMaskSet *pMaskSet, int iCursor){ */ static Expr *whereRightSubexprIsColumn(Expr *p){ p = sqlcipher_sqlite3ExprSkipCollateAndLikely(p->pRight); - if( ALWAYS(p!=0) && p->op==TK_COLUMN ) return p; + if( ALWAYS(p!=0) && p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ + return p; + } return 0; } @@ -151333,14 +159710,16 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ iColumn = pScan->aiColumn[pScan->iEquiv-1]; iCur = pScan->aiCur[pScan->iEquiv-1]; assert( pWC!=0 ); + assert( iCur>=0 ); do{ for(pTerm=pWC->a+k; knTerm; k++, pTerm++){ + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 || pTerm->leftCursor<0 ); if( pTerm->leftCursor==iCur && pTerm->u.x.leftColumn==iColumn && (iColumn!=XN_EXPR || sqlcipher_sqlite3ExprCompareSkip(pTerm->pExpr->pLeft, pScan->pIdxExpr,iCur)==0) - && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) + && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_OuterON)) ){ if( (pTerm->eOperator & WO_EQUIV)!=0 && pScan->nEquivaiCur) @@ -151376,7 +159755,8 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ } } if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0 - && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN + && (pX = pTerm->pExpr->pRight, ALWAYS(pX!=0)) + && pX->op==TK_COLUMN && pX->iTable==pScan->aiCur[0] && pX->iColumn==pScan->aiColumn[0] ){ @@ -151385,6 +159765,18 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ } pScan->pWC = pWC; pScan->k = k+1; +#ifdef WHERETRACE_ENABLED + if( sqlcipher_sqlite3WhereTrace & 0x20000 ){ + int ii; + sqlcipher_sqlite3DebugPrintf("SCAN-TERM %p: nEquiv=%d", + pTerm, pScan->nEquiv); + for(ii=0; iinEquiv; ii++){ + sqlcipher_sqlite3DebugPrintf(" {%d:%d}", + pScan->aiCur[ii], pScan->aiColumn[ii]); + } + sqlcipher_sqlite3DebugPrintf("\n"); + } +#endif return pTerm; } } @@ -151451,16 +159843,16 @@ static WhereTerm *whereScanInit( if( pIdx ){ int j = iColumn; iColumn = pIdx->aiColumn[j]; - if( iColumn==XN_EXPR ){ - pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; - pScan->zCollName = pIdx->azColl[j]; - pScan->aiColumn[0] = XN_EXPR; - return whereScanInitIndexExpr(pScan); - }else if( iColumn==pIdx->pTable->iPKey ){ + if( iColumn==pIdx->pTable->iPKey ){ iColumn = XN_ROWID; }else if( iColumn>=0 ){ pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity; pScan->zCollName = pIdx->azColl[j]; + }else if( iColumn==XN_EXPR ){ + pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; + pScan->zCollName = pIdx->azColl[j]; + pScan->aiColumn[0] = XN_EXPR; + return whereScanInitIndexExpr(pScan); } }else if( iColumn==XN_EXPR ){ return 0; @@ -151541,7 +159933,7 @@ static int findIndexCol( for(i=0; inExpr; i++){ Expr *p = sqlcipher_sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr); if( ALWAYS(p!=0) - && p->op==TK_COLUMN + && (p->op==TK_COLUMN || p->op==TK_AGG_COLUMN) && p->iColumn==pIdx->aiColumn[iCol] && p->iTable==iBase ){ @@ -151606,7 +159998,8 @@ static int isDistinctRedundant( for(i=0; inExpr; i++){ Expr *p = sqlcipher_sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr); if( NEVER(p==0) ) continue; - if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; + if( p->op!=TK_COLUMN && p->op!=TK_AGG_COLUMN ) continue; + if( p->iTable==iBase && p->iColumn<0 ) return 1; } /* Loop through all indices on the table, checking each to see if it makes @@ -151624,6 +160017,7 @@ static int isDistinctRedundant( */ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( !IsUniqueIndex(pIdx) ) continue; + if( pIdx->pPartIdxWhere ) continue; for(i=0; inKeyCol; i++){ if( 0==sqlcipher_sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask)0, WO_EQ, pIdx) ){ if( findIndexCol(pParse, pDistinct, iBase, pIdx, i)<0 ) break; @@ -151677,15 +160071,16 @@ static void translateColumnToCopy( pOp->p1 = pOp->p2 + iRegister; pOp->p2 = pOp->p3; pOp->p3 = 0; + pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */ }else if( pOp->opcode==OP_Rowid ){ - if( iAutoidxCur ){ - pOp->opcode = OP_Sequence; - pOp->p1 = iAutoidxCur; - }else{ + pOp->opcode = OP_Sequence; + pOp->p1 = iAutoidxCur; +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + if( iAutoidxCur==0 ){ pOp->opcode = OP_Null; - pOp->p1 = 0; pOp->p3 = 0; } +#endif } } } @@ -151701,12 +160096,14 @@ static void whereTraceIndexInfoInputs(sqlcipher_sqlite3_index_info *p){ int i; if( !sqlcipher_sqlite3WhereTrace ) return; for(i=0; inConstraint; i++){ - sqlcipher_sqlite3DebugPrintf(" constraint[%d]: col=%d termid=%d op=%d usabled=%d\n", + sqlcipher_sqlite3DebugPrintf( + " constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n", i, p->aConstraint[i].iColumn, p->aConstraint[i].iTermOffset, p->aConstraint[i].op, - p->aConstraint[i].usable); + p->aConstraint[i].usable, + sqlcipher_sqlite3_vtab_collation(p,i)); } for(i=0; inOrderBy; i++){ sqlcipher_sqlite3DebugPrintf(" orderby[%d]: col=%d desc=%d\n", @@ -151742,23 +160139,27 @@ static void whereTraceIndexInfoOutputs(sqlcipher_sqlite3_index_info *p){ ** index existed. */ static int termCanDriveIndex( - WhereTerm *pTerm, /* WHERE clause term to check */ - struct SrcList_item *pSrc, /* Table we are trying to access */ - Bitmask notReady /* Tables in outer loops of the join */ + const WhereTerm *pTerm, /* WHERE clause term to check */ + const SrcItem *pSrc, /* Table we are trying to access */ + const Bitmask notReady /* Tables in outer loops of the join */ ){ char aff; if( pTerm->leftCursor!=pSrc->iCursor ) return 0; if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0; - if( (pSrc->fg.jointype & JT_LEFT) - && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) - && (pTerm->eOperator & WO_IS) - ){ - /* Cannot use an IS term from the WHERE clause as an index driver for - ** the RHS of a LEFT JOIN. Such a term can only be used if it is from - ** the ON clause. */ - return 0; + assert( (pSrc->fg.jointype & JT_RIGHT)==0 ); + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){ + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); + testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) + testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); + if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) + || pTerm->pExpr->w.iJoin != pSrc->iCursor + ){ + return 0; /* See tag-20191211-001 */ + } } if( (pTerm->prereqRight & notReady)!=0 ) return 0; + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); if( pTerm->u.x.leftColumn<0 ) return 0; aff = pSrc->pTab->aCol[pTerm->u.x.leftColumn].affinity; if( !sqlcipher_sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; @@ -151774,11 +160175,11 @@ static int termCanDriveIndex( ** and to set up the WhereLevel object pLevel so that the code generator ** makes use of the automatic index. */ -static void constructAutomaticIndex( +static SQLITE_NOINLINE void constructAutomaticIndex( Parse *pParse, /* The parsing context */ - WhereClause *pWC, /* The WHERE clause */ - struct SrcList_item *pSrc, /* The FROM clause term to get the next index */ - Bitmask notReady, /* Mask of cursors that are not available */ + const WhereClause *pWC, /* The WHERE clause */ + const SrcItem *pSrc, /* The FROM clause term to get the next index */ + const Bitmask notReady, /* Mask of cursors that are not available */ WhereLevel *pLevel /* Write new index here */ ){ int nKeyCol; /* Number of columns in the constructed index */ @@ -151801,7 +160202,7 @@ static void constructAutomaticIndex( u8 sentWarning = 0; /* True if a warnning has been issued */ Expr *pPartial = 0; /* Partial Index Expression */ int iContinue = 0; /* Jump here to skip excluded rows */ - struct SrcList_item *pTabItem; /* FROM clause term being indexed */ + SrcItem *pTabItem; /* FROM clause term being indexed */ int addrCounter = 0; /* Address where integer counter is initialized */ int regBase; /* Array of registers where record is assembled */ @@ -151820,25 +160221,27 @@ static void constructAutomaticIndex( idxCols = 0; for(pTerm=pWC->a; pTermpExpr; - assert( !ExprHasProperty(pExpr, EP_FromJoin) /* prereq always non-zero */ - || pExpr->iRightJoinTable!=pSrc->iCursor /* for the right-hand */ - || pLoop->prereq!=0 ); /* table of a LEFT JOIN */ - if( pLoop->prereq==0 - && (pTerm->wtFlags & TERM_VIRTUAL)==0 - && !ExprHasProperty(pExpr, EP_FromJoin) - && sqlcipher_sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor) ){ + /* Make the automatic index a partial index if there are terms in the + ** WHERE clause (or the ON clause of a LEFT join) that constrain which + ** rows of the target table (pSrc) that can be used. */ + if( (pTerm->wtFlags & TERM_VIRTUAL)==0 + && sqlcipher_sqlite3ExprIsTableConstraint(pExpr, pSrc) + ){ pPartial = sqlcipher_sqlite3ExprAnd(pParse, pPartial, sqlcipher_sqlite3ExprDup(pParse->db, pExpr, 0)); } if( termCanDriveIndex(pTerm, pSrc, notReady) ){ - int iCol = pTerm->u.x.leftColumn; - Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); + int iCol; + Bitmask cMask; + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); + iCol = pTerm->u.x.leftColumn; + cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); testcase( iCol==BMS ); testcase( iCol==BMS-1 ); if( !sentWarning ){ sqlcipher_sqlite3_log(SQLITE_WARNING_AUTOINDEX, "automatic index on %s(%s)", pTable->zName, - pTable->aCol[iCol].zName); + pTable->aCol[iCol].zCnName); sentWarning = 1; } if( (idxCols & cMask)==0 ){ @@ -151850,7 +160253,7 @@ static void constructAutomaticIndex( } } } - assert( nKeyCol>0 ); + assert( nKeyCol>0 || pParse->db->mallocFailed ); pLoop->u.btree.nEq = pLoop->nLTerm = nKeyCol; pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED | WHERE_AUTO_INDEX; @@ -151884,8 +160287,11 @@ static void constructAutomaticIndex( idxCols = 0; for(pTerm=pWC->a; pTermu.x.leftColumn; - Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); + int iCol; + Bitmask cMask; + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); + iCol = pTerm->u.x.leftColumn; + cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); testcase( iCol==BMS-1 ); testcase( iCol==BMS ); if( (idxCols & cMask)==0 ){ @@ -151927,6 +160333,10 @@ static void constructAutomaticIndex( sqlcipher_sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); sqlcipher_sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "for %s", pTable->zName)); + if( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ){ + pLevel->regFilter = ++pParse->nMem; + sqlcipher_sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter); + } /* Fill the automatic index with content */ pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; @@ -151949,6 +160359,10 @@ static void constructAutomaticIndex( regBase = sqlcipher_sqlite3GenerateIndexKey( pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0 ); + if( pLevel->regFilter ){ + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, + regBase, pLoop->u.btree.nEq); + } sqlcipher_sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); sqlcipher_sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); if( pPartial ) sqlcipher_sqlite3VdbeResolveLabel(v, iContinue); @@ -151975,22 +160389,149 @@ end_auto_index_create: } #endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ +/* +** Generate bytecode that will initialize a Bloom filter that is appropriate +** for pLevel. +** +** If there are inner loops within pLevel that have the WHERE_BLOOMFILTER +** flag set, initialize a Bloomfilter for them as well. Except don't do +** this recursive initialization if the SQLITE_BloomPulldown optimization has +** been turned off. +** +** When the Bloom filter is initialized, the WHERE_BLOOMFILTER flag is cleared +** from the loop, but the regFilter value is set to a register that implements +** the Bloom filter. When regFilter is positive, the +** sqlcipher_sqlite3WhereCodeOneLoopStart() will generate code to test the Bloom filter +** and skip the subsequence B-Tree seek if the Bloom filter indicates that +** no matching rows exist. +** +** This routine may only be called if it has previously been determined that +** the loop would benefit from a Bloom filter, and the WHERE_BLOOMFILTER bit +** is set. +*/ +static SQLITE_NOINLINE void sqlcipher_sqlite3ConstructBloomFilter( + WhereInfo *pWInfo, /* The WHERE clause */ + int iLevel, /* Index in pWInfo->a[] that is pLevel */ + WhereLevel *pLevel, /* Make a Bloom filter for this FROM term */ + Bitmask notReady /* Loops that are not ready */ +){ + int addrOnce; /* Address of opening OP_Once */ + int addrTop; /* Address of OP_Rewind */ + int addrCont; /* Jump here to skip a row */ + const WhereTerm *pTerm; /* For looping over WHERE clause terms */ + const WhereTerm *pWCEnd; /* Last WHERE clause term */ + Parse *pParse = pWInfo->pParse; /* Parsing context */ + Vdbe *v = pParse->pVdbe; /* VDBE under construction */ + WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */ + int iCur; /* Cursor for table getting the filter */ + + assert( pLoop!=0 ); + assert( v!=0 ); + assert( pLoop->wsFlags & WHERE_BLOOMFILTER ); + + addrOnce = sqlcipher_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + do{ + const SrcItem *pItem; + const Table *pTab; + u64 sz; + sqlcipher_sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel); + addrCont = sqlcipher_sqlite3VdbeMakeLabel(pParse); + iCur = pLevel->iTabCur; + pLevel->regFilter = ++pParse->nMem; + + /* The Bloom filter is a Blob held in a register. Initialize it + ** to zero-filled blob of at least 80K bits, but maybe more if the + ** estimated size of the table is larger. We could actually + ** measure the size of the table at run-time using OP_Count with + ** P3==1 and use that value to initialize the blob. But that makes + ** testing complicated. By basing the blob size on the value in the + ** sqlite_stat1 table, testing is much easier. + */ + pItem = &pWInfo->pTabList->a[pLevel->iFrom]; + assert( pItem!=0 ); + pTab = pItem->pTab; + assert( pTab!=0 ); + sz = sqlcipher_sqlite3LogEstToInt(pTab->nRowLogEst); + if( sz<10000 ){ + sz = 10000; + }else if( sz>10000000 ){ + sz = 10000000; + } + sqlcipher_sqlite3VdbeAddOp2(v, OP_Blob, (int)sz, pLevel->regFilter); + + addrTop = sqlcipher_sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); + pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm]; + for(pTerm=pWInfo->sWC.a; pTermpExpr; + if( (pTerm->wtFlags & TERM_VIRTUAL)==0 + && sqlcipher_sqlite3ExprIsTableConstraint(pExpr, pItem) + ){ + sqlcipher_sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); + } + } + if( pLoop->wsFlags & WHERE_IPK ){ + int r1 = sqlcipher_sqlite3GetTempReg(pParse); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1); + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, 1); + sqlcipher_sqlite3ReleaseTempReg(pParse, r1); + }else{ + Index *pIdx = pLoop->u.btree.pIndex; + int n = pLoop->u.btree.nEq; + int r1 = sqlcipher_sqlite3GetTempRange(pParse, n); + int jj; + for(jj=0; jjaiColumn[jj]; + assert( pIdx->pTable==pItem->pTab ); + sqlcipher_sqlite3ExprCodeGetColumnOfTable(v, pIdx->pTable, iCur, iCol,r1+jj); + } + sqlcipher_sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n); + sqlcipher_sqlite3ReleaseTempRange(pParse, r1, n); + } + sqlcipher_sqlite3VdbeResolveLabel(v, addrCont); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); + VdbeCoverage(v); + sqlcipher_sqlite3VdbeJumpHere(v, addrTop); + pLoop->wsFlags &= ~WHERE_BLOOMFILTER; + if( OptimizationDisabled(pParse->db, SQLITE_BloomPulldown) ) break; + while( ++iLevel < pWInfo->nLevel ){ + const SrcItem *pTabItem; + pLevel = &pWInfo->a[iLevel]; + pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; + if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ) ) continue; + pLoop = pLevel->pWLoop; + if( NEVER(pLoop==0) ) continue; + if( pLoop->prereq & notReady ) continue; + if( (pLoop->wsFlags & (WHERE_BLOOMFILTER|WHERE_COLUMN_IN)) + ==WHERE_BLOOMFILTER + ){ + /* This is a candidate for bloom-filter pull-down (early evaluation). + ** The test that WHERE_COLUMN_IN is omitted is important, as we are + ** not able to do early evaluation of bloom filters that make use of + ** the IN operator */ + break; + } + } + }while( iLevel < pWInfo->nLevel ); + sqlcipher_sqlite3VdbeJumpHere(v, addrOnce); +} + + #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Allocate and populate an sqlcipher_sqlite3_index_info structure. It is the ** responsibility of the caller to eventually release the structure -** by passing the pointer returned by this function to sqlcipher_sqlite3_free(). +** by passing the pointer returned by this function to freeIndexInfo(). */ static sqlcipher_sqlite3_index_info *allocateIndexInfo( - Parse *pParse, /* The parsing context */ + WhereInfo *pWInfo, /* The WHERE clause */ WhereClause *pWC, /* The WHERE clause being analyzed */ Bitmask mUnusable, /* Ignore terms with these prereqs */ - struct SrcList_item *pSrc, /* The FROM clause term that is the vtab */ - ExprList *pOrderBy, /* The ORDER BY clause */ + SrcItem *pSrc, /* The FROM clause term that is the vtab */ u16 *pmNoOmit /* Mask of terms not to omit */ ){ int i, j; int nTerm; + Parse *pParse = pWInfo->pParse; struct sqlcipher_sqlite3_index_constraint *pIdxCons; struct sqlcipher_sqlite3_index_orderby *pIdxOrderBy; struct sqlcipher_sqlite3_index_constraint_usage *pUsage; @@ -151999,10 +160540,21 @@ static sqlcipher_sqlite3_index_info *allocateIndexInfo( int nOrderBy; sqlcipher_sqlite3_index_info *pIdxInfo; u16 mNoOmit = 0; + const Table *pTab; + int eDistinct = 0; + ExprList *pOrderBy = pWInfo->pOrderBy; + + assert( pSrc!=0 ); + pTab = pSrc->pTab; + assert( pTab!=0 ); + assert( IsVirtual(pTab) ); - /* Count the number of possible WHERE clause constraints referring - ** to this virtual table */ + /* Find all WHERE clause constraints referring to this virtual table. + ** Mark each term with the TERM_OK flag. Set nTerm to the number of + ** terms found. + */ for(i=nTerm=0, pTerm=pWC->a; inTerm; i++, pTerm++){ + pTerm->wtFlags &= ~TERM_OK; if( pTerm->leftCursor != pSrc->iCursor ) continue; if( pTerm->prereqRight & mUnusable ) continue; assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); @@ -152012,8 +160564,29 @@ static sqlcipher_sqlite3_index_info *allocateIndexInfo( testcase( pTerm->eOperator & WO_ALL ); if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; - assert( pTerm->u.x.leftColumn>=(-1) ); + + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); + assert( pTerm->u.x.leftColumn>=XN_ROWID ); + assert( pTerm->u.x.leftColumnnCol ); + + /* tag-20191211-002: WHERE-clause constraints are not useful to the + ** right-hand table of a LEFT JOIN nor to the either table of a + ** RIGHT JOIN. See tag-20191211-001 for the + ** equivalent restriction for ordinary tables. */ + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){ + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT ); + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); + testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ); + testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); + if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) + || pTerm->pExpr->w.iJoin != pSrc->iCursor + ){ + continue; + } + } nTerm++; + pTerm->wtFlags |= TERM_OK; } /* If the ORDER BY clause contains only columns in the current @@ -152025,11 +160598,49 @@ static sqlcipher_sqlite3_index_info *allocateIndexInfo( int n = pOrderBy->nExpr; for(i=0; ia[i].pExpr; - if( pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor ) break; - if( pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL ) break; + Expr *pE2; + + /* Skip over constant terms in the ORDER BY clause */ + if( sqlcipher_sqlite3ExprIsConstant(pExpr) ){ + continue; + } + + /* Virtual tables are unable to deal with NULLS FIRST */ + if( pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) break; + + /* First case - a direct column references without a COLLATE operator */ + if( pExpr->op==TK_COLUMN && pExpr->iTable==pSrc->iCursor ){ + assert( pExpr->iColumn>=XN_ROWID && pExpr->iColumnnCol ); + continue; + } + + /* 2nd case - a column reference with a COLLATE operator. Only match + ** of the COLLATE operator matches the collation of the column. */ + if( pExpr->op==TK_COLLATE + && (pE2 = pExpr->pLeft)->op==TK_COLUMN + && pE2->iTable==pSrc->iCursor + ){ + const char *zColl; /* The collating sequence name */ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + assert( pExpr->u.zToken!=0 ); + assert( pE2->iColumn>=XN_ROWID && pE2->iColumnnCol ); + pExpr->iColumn = pE2->iColumn; + if( pE2->iColumn<0 ) continue; /* Collseq does not matter for rowid */ + zColl = sqlcipher_sqlite3ColumnColl(&pTab->aCol[pE2->iColumn]); + if( zColl==0 ) zColl = sqlcipher_sqlite3StrBINARY; + if( sqlcipher_sqlite3_stricmp(pExpr->u.zToken, zColl)==0 ) continue; + } + + /* No matches cause a break out of the loop */ + break; } - if( i==n){ + if( i==n ){ nOrderBy = n; + if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) ){ + eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0); + }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ + eDistinct = 1; + } } } @@ -152037,46 +160648,35 @@ static sqlcipher_sqlite3_index_info *allocateIndexInfo( */ pIdxInfo = sqlcipher_sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm - + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) ); + + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) + + sizeof(sqlcipher_sqlite3_value*)*nTerm ); if( pIdxInfo==0 ){ sqlcipher_sqlite3ErrorMsg(pParse, "out of memory"); return 0; } pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; - pIdxCons = (struct sqlcipher_sqlite3_index_constraint*)&pHidden[1]; + pIdxCons = (struct sqlcipher_sqlite3_index_constraint*)&pHidden->aRhs[nTerm]; pIdxOrderBy = (struct sqlcipher_sqlite3_index_orderby*)&pIdxCons[nTerm]; pUsage = (struct sqlcipher_sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy]; - pIdxInfo->nOrderBy = nOrderBy; pIdxInfo->aConstraint = pIdxCons; pIdxInfo->aOrderBy = pIdxOrderBy; pIdxInfo->aConstraintUsage = pUsage; pHidden->pWC = pWC; pHidden->pParse = pParse; + pHidden->eDistinct = eDistinct; + pHidden->mIn = 0; for(i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++){ u16 op; - if( pTerm->leftCursor != pSrc->iCursor ) continue; - if( pTerm->prereqRight & mUnusable ) continue; - assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); - testcase( pTerm->eOperator & WO_IN ); - testcase( pTerm->eOperator & WO_IS ); - testcase( pTerm->eOperator & WO_ISNULL ); - testcase( pTerm->eOperator & WO_ALL ); - if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; - if( pTerm->wtFlags & TERM_VNULL ) continue; - - /* tag-20191211-002: WHERE-clause constraints are not useful to the - ** right-hand table of a LEFT JOIN. See tag-20191211-001 for the - ** equivalent restriction for ordinary tables. */ - if( (pSrc->fg.jointype & JT_LEFT)!=0 - && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) - ){ - continue; - } - assert( pTerm->u.x.leftColumn>=(-1) ); + if( (pTerm->wtFlags & TERM_OK)==0 ) continue; pIdxCons[j].iColumn = pTerm->u.x.leftColumn; pIdxCons[j].iTermOffset = i; op = pTerm->eOperator & WO_ALL; - if( op==WO_IN ) op = WO_EQ; + if( op==WO_IN ){ + if( (pTerm->wtFlags & TERM_SLICE)==0 ){ + pHidden->mIn |= SMASKBIT32(j); + } + op = WO_EQ; + } if( op==WO_AUX ){ pIdxCons[j].op = pTerm->eMatchOp; }else if( op & (WO_ISNULL|WO_IS) ){ @@ -152109,17 +160709,42 @@ static sqlcipher_sqlite3_index_info *allocateIndexInfo( j++; } + assert( j==nTerm ); pIdxInfo->nConstraint = j; - for(i=0; ia[i].pExpr; - pIdxOrderBy[i].iColumn = pExpr->iColumn; - pIdxOrderBy[i].desc = pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC; + if( sqlcipher_sqlite3ExprIsConstant(pExpr) ) continue; + assert( pExpr->op==TK_COLUMN + || (pExpr->op==TK_COLLATE && pExpr->pLeft->op==TK_COLUMN + && pExpr->iColumn==pExpr->pLeft->iColumn) ); + pIdxOrderBy[j].iColumn = pExpr->iColumn; + pIdxOrderBy[j].desc = pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC; + j++; } + pIdxInfo->nOrderBy = j; *pmNoOmit = mNoOmit; return pIdxInfo; } +/* +** Free an sqlcipher_sqlite3_index_info structure allocated by allocateIndexInfo() +** and possibly modified by xBestIndex methods. +*/ +static void freeIndexInfo(sqlcipher_sqlite3 *db, sqlcipher_sqlite3_index_info *pIdxInfo){ + HiddenIndexInfo *pHidden; + int i; + assert( pIdxInfo!=0 ); + pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + assert( pHidden->pParse!=0 ); + assert( pHidden->pParse->db==db ); + for(i=0; inConstraint; i++){ + sqlcipher_sqlite3ValueFree(pHidden->aRhs[i]); /* IMP: R-14553-25174 */ + pHidden->aRhs[i] = 0; + } + sqlcipher_sqlite3DbFree(db, pIdxInfo); +} + /* ** The table object reference passed as the second argument to this function ** must represent a virtual table. This function invokes the xBestIndex() @@ -152141,7 +160766,9 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlcipher_sqlite3_index_inf int rc; whereTraceIndexInfoInputs(p); + pParse->db->nSchemaLock++; rc = pVtab->pModule->xBestIndex(pVtab, p); + pParse->db->nSchemaLock--; whereTraceIndexInfoOutputs(p); if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){ @@ -152195,7 +160822,7 @@ static int whereKeyStats( #endif assert( pRec!=0 ); assert( pIdx->nSample>0 ); - assert( pRec->nField>0 && pRec->nField<=pIdx->nSampleCol ); + assert( pRec->nField>0 ); /* Do a binary search to find the first sample greater than or equal ** to pRec. If pRec contains a single field, the set of samples to search @@ -152241,7 +160868,7 @@ static int whereKeyStats( ** it is extended to two fields. The duplicates that this creates do not ** cause any problems. */ - nField = pRec->nField; + nField = MIN(pRec->nField, pIdx->nSample); iCol = 0; iSample = pIdx->nSample * nField; do{ @@ -152832,9 +161459,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm) memcpy(zType, "....", 5); if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V'; if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E'; - if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L'; + if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) zType[2] = 'L'; if( pTerm->wtFlags & TERM_CODED ) zType[3] = 'C'; if( pTerm->eOperator & WO_SINGLE ){ + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); sqlcipher_sqlite3_snprintf(sizeof(zLeft),zLeft,"left={%d:%d}", pTerm->leftCursor, pTerm->u.x.leftColumn); }else if( (pTerm->eOperator & WO_OR)!=0 && pTerm->u.pOrInfo!=0 ){ @@ -152852,7 +161480,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm) sqlcipher_sqlite3DebugPrintf(" prob=%-3d prereq=%llx,%llx", pTerm->truthProb, (u64)pTerm->prereqAll, (u64)pTerm->prereqRight); } - if( pTerm->u.x.iField ){ + if( (pTerm->eOperator & (WO_OR|WO_AND))==0 && pTerm->u.x.iField ){ sqlcipher_sqlite3DebugPrintf(" iField=%d", pTerm->u.x.iField); } if( pTerm->iParent>=0 ){ @@ -152883,7 +161511,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereClausePrint(WhereClause *pWC){ SQLITE_PRIVATE void sqlcipher_sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){ WhereInfo *pWInfo = pWC->pWInfo; int nb = 1+(pWInfo->pTabList->nSrc+3)/4; - struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab; + SrcItem *pItem = pWInfo->pTabList->a + p->iTab; Table *pTab = pItem->pTab; Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; sqlcipher_sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, @@ -152914,9 +161542,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *p sqlcipher_sqlite3_free(z); } if( p->wsFlags & WHERE_SKIPSCAN ){ - sqlcipher_sqlite3DebugPrintf(" f %05x %d-%d", p->wsFlags, p->nLTerm,p->nSkip); + sqlcipher_sqlite3DebugPrintf(" f %06x %d-%d", p->wsFlags, p->nLTerm,p->nSkip); }else{ - sqlcipher_sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm); + sqlcipher_sqlite3DebugPrintf(" f %06x N %d", p->wsFlags, p->nLTerm); } sqlcipher_sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); if( p->nLTerm && (sqlcipher_sqlite3WhereTrace & 0x100)!=0 ){ @@ -152987,7 +161615,7 @@ static int whereLoopResize(sqlcipher_sqlite3 *db, WhereLoop *p, int n){ static int whereLoopXfer(sqlcipher_sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){ whereLoopClearUnion(db, pTo); if( whereLoopResize(db, pTo, pFrom->nLTerm) ){ - memset(&pTo->u, 0, sizeof(pTo->u)); + memset(pTo, 0, WHERE_LOOP_XFER_SZ); return SQLITE_NOMEM_BKPT; } memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ); @@ -153012,14 +161640,7 @@ static void whereLoopDelete(sqlcipher_sqlite3 *db, WhereLoop *p){ ** Free a WhereInfo structure */ static void whereInfoFree(sqlcipher_sqlite3 *db, WhereInfo *pWInfo){ - int i; assert( pWInfo!=0 ); - for(i=0; inLevel; i++){ - WhereLevel *pLevel = &pWInfo->a[i]; - if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){ - sqlcipher_sqlite3DbFree(db, pLevel->u.in.aInLoop); - } - } sqlcipher_sqlite3WhereClauseClear(&pWInfo->sWC); while( pWInfo->pLoops ){ WhereLoop *p = pWInfo->pLoops; @@ -153027,13 +161648,30 @@ static void whereInfoFree(sqlcipher_sqlite3 *db, WhereInfo *pWInfo){ whereLoopDelete(db, p); } assert( pWInfo->pExprMods==0 ); + while( pWInfo->pMemToFree ){ + WhereMemBlock *pNext = pWInfo->pMemToFree->pNext; + sqlcipher_sqlite3DbFreeNN(db, pWInfo->pMemToFree); + pWInfo->pMemToFree = pNext; + } sqlcipher_sqlite3DbFreeNN(db, pWInfo); } +/* Undo all Expr node modifications +*/ +static void whereUndoExprMods(WhereInfo *pWInfo){ + while( pWInfo->pExprMods ){ + WhereExprMod *p = pWInfo->pExprMods; + pWInfo->pExprMods = p->pNext; + memcpy(p->pExpr, &p->orig, sizeof(p->orig)); + sqlcipher_sqlite3DbFree(pWInfo->pParse->db, p); + } +} + /* ** Return TRUE if all of the following are true: ** -** (1) X has the same or lower cost that Y +** (1) X has the same or lower cost, or returns the same or fewer rows, +** than Y. ** (2) X uses fewer WHERE clause terms than Y ** (3) Every WHERE clause term used by X is also used by Y ** (4) X skips at least as many columns as Y @@ -153056,11 +161694,8 @@ static int whereLoopCheaperProperSubset( if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){ return 0; /* X is not a subset of Y */ } + if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; if( pY->nSkip > pX->nSkip ) return 0; - if( pX->rRun >= pY->rRun ){ - if( pX->rRun > pY->rRun ) return 0; /* X costs more than Y */ - if( pX->nOut > pY->nOut ) return 0; /* X costs more than Y */ - } for(i=pX->nLTerm-1; i>=0; i--){ if( pX->aLTerm[i]==0 ) continue; for(j=pY->nLTerm-1; j>=0; j--){ @@ -153076,8 +161711,8 @@ static int whereLoopCheaperProperSubset( } /* -** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so -** that: +** Try to adjust the cost and number of output rows of WhereLoop pTemplate +** upwards or downwards so that: ** ** (1) pTemplate costs less than any other WhereLoops that are a proper ** subset of pTemplate @@ -153098,16 +161733,20 @@ static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){ /* Adjust pTemplate cost downward so that it is cheaper than its ** subset p. */ WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", - pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut-1)); - pTemplate->rRun = p->rRun; - pTemplate->nOut = p->nOut - 1; + pTemplate->rRun, pTemplate->nOut, + MIN(p->rRun, pTemplate->rRun), + MIN(p->nOut - 1, pTemplate->nOut))); + pTemplate->rRun = MIN(p->rRun, pTemplate->rRun); + pTemplate->nOut = MIN(p->nOut - 1, pTemplate->nOut); }else if( whereLoopCheaperProperSubset(pTemplate, p) ){ /* Adjust pTemplate cost upward so that it is costlier than p since ** pTemplate is a proper subset of p */ WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", - pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut+1)); - pTemplate->rRun = p->rRun; - pTemplate->nOut = p->nOut + 1; + pTemplate->rRun, pTemplate->nOut, + MAX(p->rRun, pTemplate->rRun), + MAX(p->nOut + 1, pTemplate->nOut))); + pTemplate->rRun = MAX(p->rRun, pTemplate->rRun); + pTemplate->nOut = MAX(p->nOut + 1, pTemplate->nOut); } } } @@ -153362,11 +162001,11 @@ static void whereLoopOutputAdjust( LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */ assert( (pLoop->wsFlags & WHERE_AUTO_INDEX)==0 ); - for(i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++){ + for(i=pWC->nBase, pTerm=pWC->a; i>0; i--, pTerm++){ assert( pTerm!=0 ); - if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) break; - if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue; if( (pTerm->prereqAll & notAllowed)!=0 ) continue; + if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue; + if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) continue; for(j=pLoop->nLTerm-1; j>=0; j--){ pX = pLoop->aLTerm[j]; if( pX==0 ) continue; @@ -153374,6 +162013,23 @@ static void whereLoopOutputAdjust( if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break; } if( j<0 ){ + if( pLoop->maskSelf==pTerm->prereqAll ){ + /* If there are extra terms in the WHERE clause not used by an index + ** that depend only on the table being scanned, and that will tend to + ** cause many rows to be omitted, then mark that table as + ** "self-culling". + ** + ** 2022-03-24: Self-culling only applies if either the extra terms + ** are straight comparison operators that are non-true with NULL + ** operand, or if the loop is not an OUTER JOIN. + */ + if( (pTerm->eOperator & 0x3f)!=0 + || (pWC->pWInfo->pTabList->a[pLoop->iTab].fg.jointype + & (JT_LEFT|JT_LTORJ))==0 + ){ + pLoop->wsFlags |= WHERE_SELFCULL; + } + } if( pTerm->truthProb<=0 ){ /* If a truth probability is specified using the likelihood() hints, ** then use the probability provided by the application. */ @@ -153401,7 +162057,9 @@ static void whereLoopOutputAdjust( } } } - if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce; + if( pLoop->nOut > nRow-iReduce ){ + pLoop->nOut = nRow - iReduce; + } } /* @@ -153438,9 +162096,12 @@ static int whereRangeVectorLen( char aff; /* Comparison affinity */ char idxaff = 0; /* Indexed columns affinity */ CollSeq *pColl; /* Comparison collation sequence */ - Expr *pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr; - Expr *pRhs = pTerm->pExpr->pRight; - if( pRhs->flags & EP_xIsSelect ){ + Expr *pLhs, *pRhs; + + assert( ExprUseXList(pTerm->pExpr->pLeft) ); + pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr; + pRhs = pTerm->pExpr->pRight; + if( ExprUseXSelect(pRhs) ){ pRhs = pRhs->x.pSelect->pEList->a[i].pExpr; }else{ pRhs = pRhs->x.pList->a[i].pExpr; @@ -153494,7 +162155,7 @@ static int whereRangeVectorLen( */ static int whereLoopAddBtreeIndex( WhereLoopBuilder *pBuilder, /* The WhereLoop factory */ - struct SrcList_item *pSrc, /* FROM clause term being analyzed */ + SrcItem *pSrc, /* FROM clause term being analyzed */ Index *pProbe, /* An index on pSrc */ LogEst nInMul /* log(Number of iterations due to IN) */ ){ @@ -153535,6 +162196,8 @@ static int whereLoopAddBtreeIndex( if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); assert( pNew->u.btree.nEqnColumn ); + assert( pNew->u.btree.nEqnKeyCol + || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY ); saved_nEq = pNew->u.btree.nEq; saved_nBtm = pNew->u.btree.nBtm; @@ -153569,12 +162232,29 @@ static int whereLoopAddBtreeIndex( if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue; /* tag-20191211-001: Do not allow constraints from the WHERE clause to - ** be used by the right table of a LEFT JOIN. Only constraints in the - ** ON clause are allowed. See tag-20191211-002 for the vtab equivalent. */ - if( (pSrc->fg.jointype & JT_LEFT)!=0 - && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) - ){ - continue; + ** be used by the right table of a LEFT JOIN nor by the left table of a + ** RIGHT JOIN. Only constraints in the ON clause are allowed. + ** See tag-20191211-002 for the vtab equivalent. + ** + ** 2022-06-06: See https://sqlite.org/forum/forumpost/206d99a16dd9212f + ** for an example of a WHERE clause constraints that may not be used on + ** the right table of a RIGHT JOIN because the constraint implies a + ** not-NULL condition on the left table of the RIGHT JOIN. + ** + ** 2022-06-10: The same condition applies to termCanDriveIndex() above. + ** https://sqlite.org/forum/forumpost/51e6959f61 + */ + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){ + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT ); + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); + testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) + testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); + if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) + || pTerm->pExpr->w.iJoin != pSrc->iCursor + ){ + continue; + } } if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){ @@ -153599,7 +162279,7 @@ static int whereLoopAddBtreeIndex( if( eOp & WO_IN ){ Expr *pExpr = pTerm->pExpr; - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ /* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */ int i; nIn = 46; assert( 46==sqlcipher_sqlite3LogEst(25) ); @@ -153617,7 +162297,7 @@ static int whereLoopAddBtreeIndex( nIn = sqlcipher_sqlite3LogEst(pExpr->x.pList->nExpr); } if( pProbe->hasStat1 && rLogSize>=10 ){ - LogEst M, logK, safetyMargin; + LogEst M, logK, x; /* Let: ** N = the total number of rows in the table ** K = the number of entries on the RHS of the IN operator @@ -153640,16 +162320,25 @@ static int whereLoopAddBtreeIndex( */ M = pProbe->aiRowLogEst[saved_nEq]; logK = estLog(nIn); - safetyMargin = 10; /* TUNING: extra weight for indexed IN */ - if( M + logK + safetyMargin < nIn + rLogSize ){ + /* TUNING v----- 10 to bias toward indexed IN */ + x = M + logK + 10 - (nIn + rLogSize); + if( x>=0 ){ + WHERETRACE(0x40, + ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d) " + "prefers indexed lookup\n", + saved_nEq, M, logK, nIn, rLogSize, x)); + }else if( nInMul<2 && OptimizationEnabled(db, SQLITE_SeekScan) ){ WHERETRACE(0x40, - ("Scan preferred over IN operator on column %d of \"%s\" (%d<%d)\n", - saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); + ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d" + " nInMul=%d) prefers skip-scan\n", + saved_nEq, M, logK, nIn, rLogSize, x, nInMul)); pNew->wsFlags |= WHERE_IN_SEEKSCAN; }else{ WHERETRACE(0x40, - ("IN operator preferred on column %d of \"%s\" (%d>=%d)\n", - saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); + ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d" + " nInMul=%d) prefers normal scan\n", + saved_nEq, M, logK, nIn, rLogSize, x, nInMul)); + continue; } } pNew->wsFlags |= WHERE_COLUMN_IN; @@ -153668,6 +162357,7 @@ static int whereLoopAddBtreeIndex( pNew->wsFlags |= WHERE_UNQ_WANTED; } } + if( scan.iEquiv>1 ) pNew->wsFlags |= WHERE_TRANSCONS; }else if( eOp & WO_ISNULL ){ pNew->wsFlags |= WHERE_COLUMN_NULL; }else if( eOp & (WO_GT|WO_GE) ){ @@ -153680,7 +162370,7 @@ static int whereLoopAddBtreeIndex( pBtm = pTerm; pTop = 0; if( pTerm->wtFlags & TERM_LIKEOPT ){ - /* Range contraints that come from the LIKE optimization are + /* Range constraints that come from the LIKE optimization are ** always used in pairs. */ pTop = &pTerm[1]; assert( (pTop-(pTerm->pWC->a))pWC->nTerm ); @@ -153729,8 +162419,8 @@ static int whereLoopAddBtreeIndex( tRowcnt nOut = 0; if( nInMul==0 && pProbe->nSample - && pNew->u.btree.nEq<=pProbe->nSampleCol - && ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect)) + && ALWAYS(pNew->u.btree.nEq<=pProbe->nSampleCol) + && ((eOp & WO_IN)==0 || ExprUseXList(pTerm->pExpr)) && OptimizationEnabled(db, SQLITE_Stat4) ){ Expr *pExpr = pTerm->pExpr; @@ -153811,6 +162501,8 @@ static int whereLoopAddBtreeIndex( if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 && pNew->u.btree.nEqnColumn + && (pNew->u.btree.nEqnKeyCol || + pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY) ){ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn); } @@ -153914,24 +162606,28 @@ static int indexMightHelpWithOrderBy( */ static int whereUsablePartialIndex( int iTab, /* The table for which we want an index */ - int isLeft, /* True if iTab is the right table of a LEFT JOIN */ + u8 jointype, /* The JT_* flags on the join */ WhereClause *pWC, /* The WHERE clause of the query */ Expr *pWhere /* The WHERE clause from the partial index */ ){ int i; WhereTerm *pTerm; - Parse *pParse = pWC->pWInfo->pParse; + Parse *pParse; + + if( jointype & JT_LTORJ ) return 0; + pParse = pWC->pWInfo->pParse; while( pWhere->op==TK_AND ){ - if( !whereUsablePartialIndex(iTab,isLeft,pWC,pWhere->pLeft) ) return 0; + if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0; pWhere = pWhere->pRight; } if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0; for(i=0, pTerm=pWC->a; inTerm; i++, pTerm++){ Expr *pExpr; pExpr = pTerm->pExpr; - if( (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) - && (isLeft==0 || ExprHasProperty(pExpr, EP_FromJoin)) + if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab) + && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON)) && sqlcipher_sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab) + && (pTerm->wtFlags & TERM_VNULL)==0 ){ return 1; } @@ -153985,13 +162681,12 @@ static int whereLoopAddBtree( LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */ i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */ SrcList *pTabList; /* The FROM clause */ - struct SrcList_item *pSrc; /* The FROM clause btree term to add */ + SrcItem *pSrc; /* The FROM clause btree term to add */ WhereLoop *pNew; /* Template WhereLoop object */ int rc = SQLITE_OK; /* Return code */ int iSortIdx = 1; /* Index number */ int b; /* A boolean value */ LogEst rSize; /* number of rows in the table */ - LogEst rLogSize; /* Logarithm of the number of rows in the table */ WhereClause *pWC; /* The parsed WHERE clause */ Table *pTab; /* Table being queried */ @@ -154003,9 +162698,10 @@ static int whereLoopAddBtree( pWC = pBuilder->pWC; assert( !IsVirtual(pSrc->pTab) ); - if( pSrc->pIBIndex ){ + if( pSrc->fg.isIndexedBy ){ + assert( pSrc->fg.isCte==0 ); /* An INDEXED BY clause specifies a particular index to use */ - pProbe = pSrc->pIBIndex; + pProbe = pSrc->u2.pIBIndex; }else if( !HasRowid(pTab) ){ pProbe = pTab->pIndex; }else{ @@ -154034,22 +162730,24 @@ static int whereLoopAddBtree( pProbe = &sPk; } rSize = pTab->nRowLogEst; - rLogSize = estLog(rSize); #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* Automatic indexes */ if( !pBuilder->pOrSet /* Not part of an OR optimization */ - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 + && (pWInfo->wctrlFlags & (WHERE_RIGHT_JOIN|WHERE_OR_SUBCLAUSE))==0 && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 - && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */ + && !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */ && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */ && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */ && !pSrc->fg.isCorrelated /* Not a correlated subquery */ && !pSrc->fg.isRecursive /* Not a recursive common table expression. */ + && (pSrc->fg.jointype & JT_RIGHT)==0 /* Not the right tab of a RIGHT JOIN */ ){ /* Generate auto-index WhereLoops */ + LogEst rLogSize; /* Logarithm of the number of rows in the table */ WhereTerm *pTerm; WhereTerm *pWCEnd = pWC->a + pWC->nTerm; + rLogSize = estLog(rSize); for(pTerm=pWC->a; rc==SQLITE_OK && pTermprereqRight & pNew->maskSelf ) continue; if( termCanDriveIndex(pTerm, pSrc, 0) ){ @@ -154067,7 +162765,7 @@ static int whereLoopAddBtree( ** those objects, since there is no opportunity to add schema ** indexes on subqueries and views. */ pNew->rSetup = rLogSize + rSize; - if( pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0 ){ + if( !IsView(pTab) && (pTab->tabFlags & TF_Ephemeral)==0 ){ pNew->rSetup += 28; }else{ pNew->rSetup -= 10; @@ -154091,11 +162789,10 @@ static int whereLoopAddBtree( /* Loop over all indices. If there was an INDEXED BY clause, then only ** consider index pProbe. */ for(; rc==SQLITE_OK && pProbe; - pProbe=(pSrc->pIBIndex ? 0 : pProbe->pNext), iSortIdx++ + pProbe=(pSrc->fg.isIndexedBy ? 0 : pProbe->pNext), iSortIdx++ ){ - int isLeft = (pSrc->fg.jointype & JT_OUTER)!=0; if( pProbe->pPartIdxWhere!=0 - && !whereUsablePartialIndex(pSrc->iCursor, isLeft, pWC, + && !whereUsablePartialIndex(pSrc->iCursor, pSrc->fg.jointype, pWC, pProbe->pPartIdxWhere) ){ testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */ @@ -154203,7 +162900,14 @@ static int whereLoopAddBtree( } ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); - rc = whereLoopInsert(pBuilder, pNew); + if( (pSrc->fg.jointype & JT_RIGHT)!=0 && pProbe->aColExpr ){ + /* Do not do an SCAN of a index-on-expression in a RIGHT JOIN + ** because the cursor used to access the index might not be + ** positioned to the correct row during the right-join no-match + ** loop. */ + }else{ + rc = whereLoopInsert(pBuilder, pNew); + } pNew->nOut = rSize; if( rc ) break; } @@ -154229,6 +162933,15 @@ static int whereLoopAddBtree( #ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Return true if pTerm is a virtual table LIMIT or OFFSET term. +*/ +static int isLimitTerm(WhereTerm *pTerm){ + assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 ); + return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT + && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; +} + /* ** Argument pIdxInfo is already populated with all constraints that may ** be used by the virtual table identified by pBuilder->pNew->iTab. This @@ -154256,9 +162969,11 @@ static int whereLoopAddVirtualOne( u16 mExclude, /* Exclude terms using these operators */ sqlcipher_sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */ u16 mNoOmit, /* Do not omit these constraints */ - int *pbIn /* OUT: True if plan uses an IN(...) op */ + int *pbIn, /* OUT: True if plan uses an IN(...) op */ + int *pbRetryLimit /* OUT: Retry without LIMIT/OFFSET */ ){ WhereClause *pWC = pBuilder->pWC; + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; struct sqlcipher_sqlite3_index_constraint *pIdxCons; struct sqlcipher_sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage; int i; @@ -154266,7 +162981,7 @@ static int whereLoopAddVirtualOne( int rc = SQLITE_OK; WhereLoop *pNew = pBuilder->pNew; Parse *pParse = pBuilder->pWInfo->pParse; - struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab]; + SrcItem *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab]; int nConstraint = pIdxInfo->nConstraint; assert( (mUsable & mPrereq)==mPrereq ); @@ -154281,6 +162996,7 @@ static int whereLoopAddVirtualOne( pIdxCons->usable = 0; if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight && (pTerm->eOperator & mExclude)==0 + && (pbRetryLimit || !isLimitTerm(pTerm)) ){ pIdxCons->usable = 1; } @@ -154296,6 +163012,7 @@ static int whereLoopAddVirtualOne( pIdxInfo->estimatedRows = 25; pIdxInfo->idxFlags = 0; pIdxInfo->colUsed = (sqlcipher_sqlite3_int64)pSrc->colUsed; + pHidden->mHandleIn = 0; /* Invoke the virtual table xBestIndex() method */ rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo); @@ -154313,8 +163030,8 @@ static int whereLoopAddVirtualOne( mxTerm = -1; assert( pNew->nLSlot>=nConstraint ); - for(i=0; iaLTerm[i] = 0; - pNew->u.vtab.omitMask = 0; + memset(pNew->aLTerm, 0, sizeof(pNew->aLTerm[0])*nConstraint ); + memset(&pNew->u.vtab, 0, sizeof(pNew->u.vtab)); pIdxCons = *(struct sqlcipher_sqlite3_index_constraint**)&pIdxInfo->aConstraint; for(i=0; ieMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET ){ + pNew->u.vtab.bOmitOffset = 1; + } } - if( (pTerm->eOperator & WO_IN)!=0 ){ + if( SMASKBIT32(i) & pHidden->mHandleIn ){ + pNew->u.vtab.mHandleIn |= MASKBIT32(iTerm); + }else if( (pTerm->eOperator & WO_IN)!=0 ){ /* A virtual table that is constrained by an IN clause may not ** consume the ORDER BY clause because (1) the order of IN terms ** is not necessarily related to the order of output terms and @@ -154359,6 +163081,22 @@ static int whereLoopAddVirtualOne( pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } + + assert( pbRetryLimit || !isLimitTerm(pTerm) ); + if( isLimitTerm(pTerm) && *pbIn ){ + /* If there is an IN(...) term handled as an == (separate call to + ** xFilter for each value on the RHS of the IN) and a LIMIT or + ** OFFSET term handled as well, the plan is unusable. Set output + ** variable *pbRetryLimit to true to tell the caller to retry with + ** LIMIT and OFFSET disabled. */ + if( pIdxInfo->needToFreeIdxStr ){ + sqlcipher_sqlite3_free(pIdxInfo->idxStr); + pIdxInfo->idxStr = 0; + pIdxInfo->needToFreeIdxStr = 0; + } + *pbRetryLimit = 1; + return SQLITE_OK; + } } } @@ -154403,11 +163141,19 @@ static int whereLoopAddVirtualOne( } /* -** If this function is invoked from within an xBestIndex() callback, it -** returns a pointer to a buffer containing the name of the collation -** sequence associated with element iCons of the sqlcipher_sqlite3_index_info.aConstraint -** array. Or, if iCons is out of range or there is no active xBestIndex -** call, return NULL. +** Return the collating sequence for a constraint passed into xBestIndex. +** +** pIdxInfo must be an sqlcipher_sqlite3_index_info structure passed into xBestIndex. +** This routine depends on there being a HiddenIndexInfo structure immediately +** following the sqlcipher_sqlite3_index_info structure. +** +** Return a pointer to the collation name: +** +** 1. If there is an explicit COLLATE operator on the constaint, return it. +** +** 2. Else, if the column has an alternative collation, return that. +** +** 3. Otherwise, return "BINARY". */ SQLITE_API const char *sqlcipher_sqlite3_vtab_collation(sqlcipher_sqlite3_index_info *pIdxInfo, int iCons){ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; @@ -154424,6 +163170,97 @@ SQLITE_API const char *sqlcipher_sqlite3_vtab_collation(sqlcipher_sqlite3_index_ return zRet; } +/* +** Return true if constraint iCons is really an IN(...) constraint, or +** false otherwise. If iCons is an IN(...) constraint, set (if bHandle!=0) +** or clear (if bHandle==0) the flag to handle it using an iterator. +*/ +SQLITE_API int sqlcipher_sqlite3_vtab_in(sqlcipher_sqlite3_index_info *pIdxInfo, int iCons, int bHandle){ + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + u32 m = SMASKBIT32(iCons); + if( m & pHidden->mIn ){ + if( bHandle==0 ){ + pHidden->mHandleIn &= ~m; + }else if( bHandle>0 ){ + pHidden->mHandleIn |= m; + } + return 1; + } + return 0; +} + +/* +** This interface is callable from within the xBestIndex callback only. +** +** If possible, set (*ppVal) to point to an object containing the value +** on the right-hand-side of constraint iCons. +*/ +SQLITE_API int sqlcipher_sqlite3_vtab_rhs_value( + sqlcipher_sqlite3_index_info *pIdxInfo, /* Copy of first argument to xBestIndex */ + int iCons, /* Constraint for which RHS is wanted */ + sqlcipher_sqlite3_value **ppVal /* Write value extracted here */ +){ + HiddenIndexInfo *pH = (HiddenIndexInfo*)&pIdxInfo[1]; + sqlcipher_sqlite3_value *pVal = 0; + int rc = SQLITE_OK; + if( iCons<0 || iCons>=pIdxInfo->nConstraint ){ + rc = SQLITE_MISUSE; /* EV: R-30545-25046 */ + }else{ + if( pH->aRhs[iCons]==0 ){ + WhereTerm *pTerm = &pH->pWC->a[pIdxInfo->aConstraint[iCons].iTermOffset]; + rc = sqlcipher_sqlite3ValueFromExpr( + pH->pParse->db, pTerm->pExpr->pRight, ENC(pH->pParse->db), + SQLITE_AFF_BLOB, &pH->aRhs[iCons] + ); + testcase( rc!=SQLITE_OK ); + } + pVal = pH->aRhs[iCons]; + } + *ppVal = pVal; + + if( rc==SQLITE_OK && pVal==0 ){ /* IMP: R-19933-32160 */ + rc = SQLITE_NOTFOUND; /* IMP: R-36424-56542 */ + } + + return rc; +} + +/* +** Return true if ORDER BY clause may be handled as DISTINCT. +*/ +SQLITE_API int sqlcipher_sqlite3_vtab_distinct(sqlcipher_sqlite3_index_info *pIdxInfo){ + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + assert( pHidden->eDistinct>=0 && pHidden->eDistinct<=3 ); + return pHidden->eDistinct; +} + +#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \ + && !defined(SQLITE_OMIT_VIRTUALTABLE) +/* +** Cause the prepared statement that is associated with a call to +** xBestIndex to potentiall use all schemas. If the statement being +** prepared is read-only, then just start read transactions on all +** schemas. But if this is a write operation, start writes on all +** schemas. +** +** This is used by the (built-in) sqlite_dbpage virtual table. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3VtabUsesAllSchemas(sqlcipher_sqlite3_index_info *pIdxInfo){ + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + Parse *pParse = pHidden->pParse; + int nDb = pParse->db->nDb; + int i; + for(i=0; iwriteMask ){ + for(i=0; ipNew->iTab. That table is guaranteed to be a virtual table. @@ -154458,13 +163295,14 @@ static int whereLoopAddVirtual( WhereInfo *pWInfo; /* WHERE analysis context */ Parse *pParse; /* The parsing context */ WhereClause *pWC; /* The WHERE clause */ - struct SrcList_item *pSrc; /* The FROM clause term to search */ + SrcItem *pSrc; /* The FROM clause term to search */ sqlcipher_sqlite3_index_info *p; /* Object to pass to xBestIndex() */ int nConstraint; /* Number of constraints in p */ int bIn; /* True if plan uses IN(...) operator */ WhereLoop *pNew; Bitmask mBest; /* Tables used by best possible plan */ u16 mNoOmit; + int bRetry = 0; /* True to retry with LIMIT/OFFSET disabled */ assert( (mPrereq & mUnusable)==0 ); pWInfo = pBuilder->pWInfo; @@ -154473,8 +163311,7 @@ static int whereLoopAddVirtual( pNew = pBuilder->pNew; pSrc = &pWInfo->pTabList->a[pNew->iTab]; assert( IsVirtual(pSrc->pTab) ); - p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy, - &mNoOmit); + p = allocateIndexInfo(pWInfo, pWC, mUnusable, pSrc, &mNoOmit); if( p==0 ) return SQLITE_NOMEM_BKPT; pNew->rSetup = 0; pNew->wsFlags = WHERE_VIRTUALTABLE; @@ -154482,14 +163319,22 @@ static int whereLoopAddVirtual( pNew->u.vtab.needFree = 0; nConstraint = p->nConstraint; if( whereLoopResize(pParse->db, pNew, nConstraint) ){ - sqlcipher_sqlite3DbFree(pParse->db, p); + freeIndexInfo(pParse->db, p); return SQLITE_NOMEM_BKPT; } /* First call xBestIndex() with all constraints usable. */ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); WHERETRACE(0x40, (" VirtualOne: all usable\n")); - rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry + ); + if( bRetry ){ + assert( rc==SQLITE_OK ); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, 0 + ); + } /* If the call to xBestIndex() with all terms enabled produced a plan ** that does not require any source tables (IOW: a plan with mBest==0) @@ -154507,7 +163352,7 @@ static int whereLoopAddVirtual( if( bIn ){ WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn); + pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn, 0); assert( bIn==0 ); mBestNoIn = pNew->prereq & ~mPrereq; if( mBestNoIn==0 ){ @@ -154534,7 +163379,7 @@ static int whereLoopAddVirtual( WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n", (sqlcipher_sqlite3_uint64)mPrev, (sqlcipher_sqlite3_uint64)mNext)); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn); + pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn, 0); if( pNew->prereq==mPrereq ){ seenZero = 1; if( bIn==0 ) seenZeroNoIN = 1; @@ -154547,7 +163392,7 @@ static int whereLoopAddVirtual( if( rc==SQLITE_OK && seenZero==0 ){ WHERETRACE(0x40, (" VirtualOne: all disabled\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn); + pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn, 0); if( bIn==0 ) seenZeroNoIN = 1; } @@ -154557,12 +163402,12 @@ static int whereLoopAddVirtual( if( rc==SQLITE_OK && seenZeroNoIN==0 ){ WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn); + pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn, 0); } } if( p->needToFreeIdxStr ) sqlcipher_sqlite3_free(p->idxStr); - sqlcipher_sqlite3DbFreeNN(pParse->db, p); + freeIndexInfo(pParse->db, p); WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc)); return rc; } @@ -154586,7 +163431,7 @@ static int whereLoopAddOr( WhereClause tempWC; WhereLoopBuilder sSubBuild; WhereOrSet sSum, sCur; - struct SrcList_item *pItem; + SrcItem *pItem; pWC = pBuilder->pWC; pWCEnd = pWC->a + pWC->nTerm; @@ -154595,6 +163440,9 @@ static int whereLoopAddOr( pItem = pWInfo->pTabList->a + pNew->iTab; iCur = pItem->iCursor; + /* The multi-index OR optimization does not work for RIGHT and FULL JOIN */ + if( pItem->fg.jointype & JT_RIGHT ) return SQLITE_OK; + for(pTerm=pWC->a; pTermeOperator & WO_OR)!=0 && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0 @@ -154606,7 +163454,6 @@ static int whereLoopAddOr( int i, j; sSubBuild = *pBuilder; - sSubBuild.pOrderBy = 0; sSubBuild.pOrSet = &sCur; WHERETRACE(0x200, ("Begin processing OR-clause %p\n", pTerm)); @@ -154618,6 +163465,7 @@ static int whereLoopAddOr( tempWC.pOuter = pWC; tempWC.op = TK_AND; tempWC.nTerm = 1; + tempWC.nBase = 1; tempWC.a = pOrTerm; sSubBuild.pWC = &tempWC; }else{ @@ -154642,7 +163490,9 @@ static int whereLoopAddOr( if( rc==SQLITE_OK ){ rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable); } - assert( rc==SQLITE_OK || rc==SQLITE_DONE || sCur.n==0 ); + assert( rc==SQLITE_OK || rc==SQLITE_DONE || sCur.n==0 + || rc==SQLITE_NOMEM ); + testcase( rc==SQLITE_NOMEM && sCur.n>0 ); testcase( rc==SQLITE_DONE ); if( sCur.n==0 ){ sSum.n = 0; @@ -154702,12 +163552,15 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ Bitmask mPrior = 0; int iTab; SrcList *pTabList = pWInfo->pTabList; - struct SrcList_item *pItem; - struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel]; + SrcItem *pItem; + SrcItem *pEnd = &pTabList->a[pWInfo->nLevel]; sqlcipher_sqlite3 *db = pWInfo->pParse->db; int rc = SQLITE_OK; + int bFirstPastRJ = 0; + int hasRightJoin = 0; WhereLoop *pNew; + /* Loop over the tables in the join, from left to right */ pNew = pBuilder->pNew; whereLoopInit(pNew); @@ -154717,18 +163570,30 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ pNew->iTab = iTab; pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; pNew->maskSelf = sqlcipher_sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); - if( (pItem->fg.jointype & (JT_LEFT|JT_CROSS))!=0 ){ - /* This condition is true when pItem is the FROM clause term on the - ** right-hand-side of a LEFT or CROSS JOIN. */ - mPrereq = mPrior; - }else{ + if( bFirstPastRJ + || (pItem->fg.jointype & (JT_OUTER|JT_CROSS|JT_LTORJ))!=0 + ){ + /* Add prerequisites to prevent reordering of FROM clause terms + ** across CROSS joins and outer joins. The bFirstPastRJ boolean + ** prevents the right operand of a RIGHT JOIN from being swapped with + ** other elements even further to the right. + ** + ** The JT_LTORJ case and the hasRightJoin flag work together to + ** prevent FROM-clause terms from moving from the right side of + ** a LEFT JOIN over to the left side of that join if the LEFT JOIN + ** is itself on the left side of a RIGHT JOIN. + */ + if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1; + mPrereq |= mPrior; + bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; + }else if( !hasRightJoin ){ mPrereq = 0; } #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pItem->pTab) ){ - struct SrcList_item *p; + SrcItem *p; for(p=&pItem[1]; pfg.jointype & (JT_LEFT|JT_CROSS)) ){ + if( mUnusable || (p->fg.jointype & (JT_OUTER|JT_CROSS)) ){ mUnusable |= sqlcipher_sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); } } @@ -154853,7 +163718,9 @@ static i8 wherePathSatisfiesOrderBy( pLoop = pLast; } if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ - if( pLoop->u.vtab.isOrdered && (wctrlFlags & WHERE_DISTINCTBY)==0 ){ + if( pLoop->u.vtab.isOrdered + && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY) + ){ obSat = obDone; } break; @@ -154871,7 +163738,7 @@ static i8 wherePathSatisfiesOrderBy( if( MASKBIT(i) & obSat ) continue; pOBExpr = sqlcipher_sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); if( NEVER(pOBExpr==0) ) continue; - if( pOBExpr->op!=TK_COLUMN ) continue; + if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) continue; if( pOBExpr->iTable!=iCur ) continue; pTerm = sqlcipher_sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, ~ready, eqOpMask, 0); @@ -154911,6 +163778,10 @@ static i8 wherePathSatisfiesOrderBy( assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) ); assert( pIndex->aiColumn[nColumn-1]==XN_ROWID || !HasRowid(pIndex->pTable)); + /* All relevant terms of the index must also be non-NULL in order + ** for isOrderDistinct to be true. So the isOrderDistint value + ** computed here might be a false positive. Corrections will be + ** made at tag-20210426-1 below */ isOrderDistinct = IsUniqueIndex(pIndex) && (pLoop->wsFlags & WHERE_SKIPSCAN)==0; } @@ -154978,14 +163849,18 @@ static i8 wherePathSatisfiesOrderBy( } /* An unconstrained column that might be NULL means that this - ** WhereLoop is not well-ordered + ** WhereLoop is not well-ordered. tag-20210426-1 */ - if( isOrderDistinct - && iColumn>=0 - && j>=pLoop->u.btree.nEq - && pIndex->pTable->aCol[iColumn].notNull==0 - ){ - isOrderDistinct = 0; + if( isOrderDistinct ){ + if( iColumn>=0 + && j>=pLoop->u.btree.nEq + && pIndex->pTable->aCol[iColumn].notNull==0 + ){ + isOrderDistinct = 0; + } + if( iColumn==XN_EXPR ){ + isOrderDistinct = 0; + } } /* Find the ORDER BY term that corresponds to the j-th column @@ -155000,7 +163875,7 @@ static i8 wherePathSatisfiesOrderBy( if( NEVER(pOBExpr==0) ) continue; if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; if( iColumn>=XN_ROWID ){ - if( pOBExpr->op!=TK_COLUMN ) continue; + if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) continue; if( pOBExpr->iTable!=iCur ) continue; if( pOBExpr->iColumn!=iColumn ) continue; }else{ @@ -155023,16 +163898,18 @@ static i8 wherePathSatisfiesOrderBy( /* Make sure the sort order is compatible in an ORDER BY clause. ** Sort order is irrelevant for a GROUP BY clause. */ if( revSet ){ - if( (rev ^ revIdx)!=(pOrderBy->a[i].sortFlags&KEYINFO_ORDER_DESC) ){ + if( (rev ^ revIdx) + != (pOrderBy->a[i].fg.sortFlags&KEYINFO_ORDER_DESC) + ){ isMatch = 0; } }else{ - rev = revIdx ^ (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC); + rev = revIdx ^ (pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC); if( rev ) *pRevMask |= MASKBIT(iLoop); revSet = 1; } } - if( isMatch && (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL) ){ + if( isMatch && (pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL) ){ if( j==pLoop->u.btree.nEq ){ pLoop->wsFlags |= WHERE_BIGNULL_SORT; }else{ @@ -155079,7 +163956,7 @@ static i8 wherePathSatisfiesOrderBy( if( obSat==obDone ) return (i8)nOrderBy; if( !isOrderDistinct ){ for(i=nOrderBy-1; i>0; i--){ - Bitmask m = MASKBIT(i) - 1; + Bitmask m = ALWAYS(iwctrlFlags & WHERE_GROUPBY ); + assert( pWInfo->wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY) ); assert( pWInfo->wctrlFlags & WHERE_SORTBYGROUP ); return pWInfo->sorted; } @@ -155169,7 +164046,7 @@ static LogEst whereSortingCost( }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){ /* TUNING: In the sort for a DISTINCT operator, assume that the DISTINCT ** reduces the number of output rows by a factor of 2 */ - if( nRow>10 ) nRow -= 10; assert( 10==sqlcipher_sqlite3LogEst(2) ); + if( nRow>10 ){ nRow -= 10; assert( 10==sqlcipher_sqlite3LogEst(2) ); } } rSortCost += estLog(nRow); return rSortCost; @@ -155513,12 +164390,12 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ } pWInfo->bOrderedInnerLoop = 0; if( pWInfo->pOrderBy ){ + pWInfo->nOBSat = pFrom->isOrdered; if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){ if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } }else{ - pWInfo->nOBSat = pFrom->isOrdered; pWInfo->revMask = pFrom->revLoop; if( pWInfo->nOBSat<=0 ){ pWInfo->nOBSat = 0; @@ -155581,7 +164458,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ */ static int whereShortCut(WhereLoopBuilder *pBuilder){ WhereInfo *pWInfo; - struct SrcList_item *pItem; + SrcItem *pItem; WhereClause *pWC; WhereTerm *pTerm; WhereLoop *pLoop; @@ -155589,6 +164466,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ int j; Table *pTab; Index *pIdx; + WhereScan scan; pWInfo = pBuilder->pWInfo; if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0; @@ -155596,13 +164474,18 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ pItem = pWInfo->pTabList->a; pTab = pItem->pTab; if( IsVirtual(pTab) ) return 0; - if( pItem->fg.isIndexedBy ) return 0; + if( pItem->fg.isIndexedBy || pItem->fg.notIndexed ){ + testcase( pItem->fg.isIndexedBy ); + testcase( pItem->fg.notIndexed ); + return 0; + } iCur = pItem->iCursor; pWC = &pWInfo->sWC; pLoop = pBuilder->pNew; pLoop->wsFlags = 0; pLoop->nSkip = 0; - pTerm = sqlcipher_sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0); + pTerm = whereScanInit(&scan, pWC, iCur, -1, WO_EQ|WO_IS, 0); + while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan); if( pTerm ){ testcase( pTerm->eOperator & WO_IS ); pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW; @@ -155621,7 +164504,8 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ ) continue; opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ; for(j=0; jnKeyCol; j++){ - pTerm = sqlcipher_sqlite3WhereFindTerm(pWC, iCur, j, 0, opMask, pIdx); + pTerm = whereScanInit(&scan, pWC, iCur, j, opMask, pIdx); + while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan); if( pTerm==0 ) break; testcase( pTerm->eOperator & WO_IS ); pLoop->aLTerm[j] = pTerm; @@ -155650,8 +164534,14 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } + if( scan.iEquiv>1 ) pLoop->wsFlags |= WHERE_TRANSCONS; #ifdef SQLITE_DEBUG pLoop->cId = '0'; +#endif +#ifdef WHERETRACE_ENABLED + if( sqlcipher_sqlite3WhereTrace ){ + sqlcipher_sqlite3DebugPrintf("whereShortCut() used to compute solution\n"); + } #endif return 1; } @@ -155706,6 +164596,150 @@ static void showAllWhereLoops(WhereInfo *pWInfo, WhereClause *pWC){ # define WHERETRACE_ALL_LOOPS(W,C) #endif +/* Attempt to omit tables from a join that do not affect the result. +** For a table to not affect the result, the following must be true: +** +** 1) The query must not be an aggregate. +** 2) The table must be the RHS of a LEFT JOIN. +** 3) Either the query must be DISTINCT, or else the ON or USING clause +** must contain a constraint that limits the scan of the table to +** at most a single row. +** 4) The table must not be referenced by any part of the query apart +** from its own USING or ON clause. +** +** For example, given: +** +** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); +** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); +** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); +** +** then table t2 can be omitted from the following: +** +** SELECT v1, v3 FROM t1 +** LEFT JOIN t2 ON (t1.ipk=t2.ipk) +** LEFT JOIN t3 ON (t1.ipk=t3.ipk) +** +** or from: +** +** SELECT DISTINCT v1, v3 FROM t1 +** LEFT JOIN t2 +** LEFT JOIN t3 ON (t1.ipk=t3.ipk) +*/ +static SQLITE_NOINLINE Bitmask whereOmitNoopJoin( + WhereInfo *pWInfo, + Bitmask notReady +){ + int i; + Bitmask tabUsed; + + /* Preconditions checked by the caller */ + assert( pWInfo->nLevel>=2 ); + assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_OmitNoopJoin) ); + + /* These two preconditions checked by the caller combine to guarantee + ** condition (1) of the header comment */ + assert( pWInfo->pResultSet!=0 ); + assert( 0==(pWInfo->wctrlFlags & WHERE_AGG_DISTINCT) ); + + tabUsed = sqlcipher_sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pResultSet); + if( pWInfo->pOrderBy ){ + tabUsed |= sqlcipher_sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy); + } + for(i=pWInfo->nLevel-1; i>=1; i--){ + WhereTerm *pTerm, *pEnd; + SrcItem *pItem; + WhereLoop *pLoop; + pLoop = pWInfo->a[i].pWLoop; + pItem = &pWInfo->pTabList->a[pLoop->iTab]; + if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue; + if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)==0 + && (pLoop->wsFlags & WHERE_ONEROW)==0 + ){ + continue; + } + if( (tabUsed & pLoop->maskSelf)!=0 ) continue; + pEnd = pWInfo->sWC.a + pWInfo->sWC.nTerm; + for(pTerm=pWInfo->sWC.a; pTermprereqAll & pLoop->maskSelf)!=0 ){ + if( !ExprHasProperty(pTerm->pExpr, EP_OuterON) + || pTerm->pExpr->w.iJoin!=pItem->iCursor + ){ + break; + } + } + } + if( pTerm drop loop %c not used\n", pLoop->cId)); + notReady &= ~pLoop->maskSelf; + for(pTerm=pWInfo->sWC.a; pTermprereqAll & pLoop->maskSelf)!=0 ){ + pTerm->wtFlags |= TERM_CODED; + } + } + if( i!=pWInfo->nLevel-1 ){ + int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); + memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); + } + pWInfo->nLevel--; + assert( pWInfo->nLevel>0 ); + } + return notReady; +} + +/* +** Check to see if there are any SEARCH loops that might benefit from +** using a Bloom filter. Consider a Bloom filter if: +** +** (1) The SEARCH happens more than N times where N is the number +** of rows in the table that is being considered for the Bloom +** filter. +** (2) Some searches are expected to find zero rows. (This is determined +** by the WHERE_SELFCULL flag on the term.) +** (3) Bloom-filter processing is not disabled. (Checked by the +** caller.) +** (4) The size of the table being searched is known by ANALYZE. +** +** This block of code merely checks to see if a Bloom filter would be +** appropriate, and if so sets the WHERE_BLOOMFILTER flag on the +** WhereLoop. The implementation of the Bloom filter comes further +** down where the code for each WhereLoop is generated. +*/ +static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( + const WhereInfo *pWInfo +){ + int i; + LogEst nSearch; + + assert( pWInfo->nLevel>=2 ); + assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) ); + nSearch = pWInfo->a[0].pWLoop->nOut; + for(i=1; inLevel; i++){ + WhereLoop *pLoop = pWInfo->a[i].pWLoop; + const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ); + if( (pLoop->wsFlags & reqFlags)==reqFlags + /* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */ + && ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0) + ){ + SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab]; + Table *pTab = pItem->pTab; + pTab->tabFlags |= TF_StatsUsed; + if( nSearch > pTab->nRowLogEst + && (pTab->tabFlags & TF_HasStat1)!=0 + ){ + testcase( pItem->fg.jointype & JT_LEFT ); + pLoop->wsFlags |= WHERE_BLOOMFILTER; + pLoop->wsFlags &= ~WHERE_IDX_ONLY; + WHERETRACE(0xffff, ( + "-> use Bloom-filter on loop %c because there are ~%.1e " + "lookups into %s which has only ~%.1e rows\n", + pLoop->cId, (double)sqlcipher_sqlite3LogEstToInt(nSearch), pTab->zName, + (double)sqlcipher_sqlite3LogEstToInt(pTab->nRowLogEst))); + } + } + nSearch += pLoop->nOut; + } +} + /* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains @@ -155800,6 +164834,7 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( Expr *pWhere, /* The WHERE clause */ ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */ + Select *pLimit, /* Use this LIMIT/OFFSET clause, if any */ u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */ int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number ** If WHERE_USE_LIMIT, then the limit amount */ @@ -155834,13 +164869,6 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0; - sWLB.pOrderBy = pOrderBy; - - /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via - ** sqlcipher_sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ - if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){ - wctrlFlags &= ~WHERE_WANT_DISTINCT; - } /* The number of tables in the FROM clause is limited by the number of ** bits in a Bitmask @@ -155865,7 +164893,7 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( ** field (type Bitmask) it must be aligned on an 8-byte boundary on ** some architectures. Hence the ROUND8() below. */ - nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); + nByteWInfo = ROUND8P(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); pWInfo = sqlcipher_sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); if( db->mallocFailed ){ sqlcipher_sqlite3DbFree(db, pWInfo); @@ -155883,11 +164911,18 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( pWInfo->wctrlFlags = wctrlFlags; pWInfo->iLimit = iAuxArg; pWInfo->savedNQueryLoop = pParse->nQueryLoop; +#ifndef SQLITE_OMIT_VIRTUALTABLE + pWInfo->pLimit = pLimit; +#endif memset(&pWInfo->nOBSat, 0, offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat)); memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel)); assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */ pMaskSet = &pWInfo->sMaskSet; + pMaskSet->n = 0; + pMaskSet->ix[0] = -99; /* Initialize ix[0] to a value that can never be + ** a valid cursor number, to avoid an initial + ** test for pMaskSet->n==0 in sqlcipher_sqlite3WhereGetMask() */ sWLB.pWInfo = pWInfo; sWLB.pWC = &pWInfo->sWC; sWLB.pNew = (WhereLoop*)(((char*)pWInfo)+nByteWInfo); @@ -155900,7 +164935,6 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( /* Split the WHERE clause into separate subexpressions where each ** subexpression is separated by an AND operator. */ - initMaskSet(pMaskSet); sqlcipher_sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo); sqlcipher_sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND); @@ -155908,7 +164942,9 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( */ if( nTabList==0 ){ if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr; - if( wctrlFlags & WHERE_WANT_DISTINCT ){ + if( (wctrlFlags & WHERE_WANT_DISTINCT)!=0 + && OptimizationEnabled(db, SQLITE_DistinctOpt) + ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); @@ -155946,7 +164982,8 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( /* Analyze all of the subexpressions. */ sqlcipher_sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC); - if( db->mallocFailed ) goto whereBeginError; + sqlcipher_sqlite3WhereAddLimit(&pWInfo->sWC, pLimit); + if( pParse->nErr ) goto whereBeginError; /* Special case: WHERE terms that do not refer to any tables in the join ** (constant expressions). Evaluate each such term, and jump over all the @@ -155959,7 +164996,7 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( ** FROM ... WHERE random()>0; -- eval random() once per row ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall */ - for(ii=0; iinTerm; ii++){ + for(ii=0; iinBase; ii++){ WhereTerm *pT = &sWLB.pWC->a[ii]; if( pT->wtFlags & TERM_VIRTUAL ) continue; if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){ @@ -155969,7 +165006,12 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( } if( wctrlFlags & WHERE_WANT_DISTINCT ){ - if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){ + if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){ + /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via + ** sqlcipher_sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ + wctrlFlags &= ~WHERE_WANT_DISTINCT; + pWInfo->wctrlFlags &= ~WHERE_WANT_DISTINCT; + }else if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){ /* The DISTINCT marking is pointless. Ignore it. */ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; }else if( pOrderBy==0 ){ @@ -156040,9 +165082,10 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){ pWInfo->revMask = ALLBITS; } - if( pParse->nErr || NEVER(db->mallocFailed) ){ + if( pParse->nErr ){ goto whereBeginError; } + assert( db->mallocFailed==0 ); #ifdef WHERETRACE_ENABLED if( sqlcipher_sqlite3WhereTrace ){ sqlcipher_sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); @@ -156070,83 +165113,36 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( } #endif - /* Attempt to omit tables from the join that do not affect the result. - ** For a table to not affect the result, the following must be true: - ** - ** 1) The query must not be an aggregate. - ** 2) The table must be the RHS of a LEFT JOIN. - ** 3) Either the query must be DISTINCT, or else the ON or USING clause - ** must contain a constraint that limits the scan of the table to - ** at most a single row. - ** 4) The table must not be referenced by any part of the query apart - ** from its own USING or ON clause. - ** - ** For example, given: + /* Attempt to omit tables from a join that do not affect the result. + ** See the comment on whereOmitNoopJoin() for further information. ** - ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); - ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); - ** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); - ** - ** then table t2 can be omitted from the following: - ** - ** SELECT v1, v3 FROM t1 - ** LEFT JOIN t2 ON (t1.ipk=t2.ipk) - ** LEFT JOIN t3 ON (t1.ipk=t3.ipk) - ** - ** or from: - ** - ** SELECT DISTINCT v1, v3 FROM t1 - ** LEFT JOIN t2 - ** LEFT JOIN t3 ON (t1.ipk=t3.ipk) + ** This query optimization is factored out into a separate "no-inline" + ** procedure to keep the sqlcipher_sqlite3WhereBegin() procedure from becoming + ** too large. If sqlcipher_sqlite3WhereBegin() becomes too large, that prevents + ** some C-compiler optimizers from in-lining the + ** sqlcipher_sqlite3WhereCodeOneLoopStart() procedure, and it is important to + ** in-line sqlcipher_sqlite3WhereCodeOneLoopStart() for performance reasons. */ notReady = ~(Bitmask)0; if( pWInfo->nLevel>=2 - && pResultSet!=0 /* guarantees condition (1) above */ + && pResultSet!=0 /* these two combine to guarantee */ + && 0==(wctrlFlags & WHERE_AGG_DISTINCT) /* condition (1) above */ && OptimizationEnabled(db, SQLITE_OmitNoopJoin) ){ - int i; - Bitmask tabUsed = sqlcipher_sqlite3WhereExprListUsage(pMaskSet, pResultSet); - if( sWLB.pOrderBy ){ - tabUsed |= sqlcipher_sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy); - } - for(i=pWInfo->nLevel-1; i>=1; i--){ - WhereTerm *pTerm, *pEnd; - struct SrcList_item *pItem; - pLoop = pWInfo->a[i].pWLoop; - pItem = &pWInfo->pTabList->a[pLoop->iTab]; - if( (pItem->fg.jointype & JT_LEFT)==0 ) continue; - if( (wctrlFlags & WHERE_WANT_DISTINCT)==0 - && (pLoop->wsFlags & WHERE_ONEROW)==0 - ){ - continue; - } - if( (tabUsed & pLoop->maskSelf)!=0 ) continue; - pEnd = sWLB.pWC->a + sWLB.pWC->nTerm; - for(pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0 ){ - if( !ExprHasProperty(pTerm->pExpr, EP_FromJoin) - || pTerm->pExpr->iRightJoinTable!=pItem->iCursor - ){ - break; - } - } - } - if( pTerm drop loop %c not used\n", pLoop->cId)); - notReady &= ~pLoop->maskSelf; - for(pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0 ){ - pTerm->wtFlags |= TERM_CODED; - } - } - if( i!=pWInfo->nLevel-1 ){ - int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); - memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); - } - pWInfo->nLevel--; - nTabList--; - } + notReady = whereOmitNoopJoin(pWInfo, notReady); + nTabList = pWInfo->nLevel; + assert( nTabList>0 ); + } + + /* Check to see if there are any SEARCH loops that might benefit from + ** using a Bloom filter. + */ + if( pWInfo->nLevel>=2 + && OptimizationEnabled(db, SQLITE_BloomFilter) + ){ + whereCheckIfBloomFilterIsUseful(pWInfo); } + #if defined(WHERETRACE_ENABLED) if( sqlcipher_sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */ sqlcipher_sqlite3DebugPrintf("---- WHERE clause at end of analysis:\n"); @@ -156201,13 +165197,13 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( for(ii=0, pLevel=pWInfo->a; iia[pLevel->iFrom]; pTab = pTabItem->pTab; iDb = sqlcipher_sqlite3SchemaToIndex(db, pTab->pSchema); pLoop = pLevel->pWLoop; - if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){ + if( (pTab->tabFlags & TF_Ephemeral)!=0 || IsView(pTab) ){ /* Do nothing */ }else #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -156219,8 +165215,10 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( /* noop */ }else #endif - if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 - && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ + if( ((pLoop->wsFlags & WHERE_IDX_ONLY)==0 + && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0) + || (pTabItem->fg.jointype & (JT_LTORJ|JT_RIGHT))!=0 + ){ int op = OP_OpenRead; if( pWInfo->eOnePass!=ONEPASS_OFF ){ op = OP_OpenWrite; @@ -156233,6 +165231,7 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( if( pWInfo->eOnePass==ONEPASS_OFF && pTab->nColtabFlags & (TF_HasGenerated|TF_WithoutRowid))==0 + && (pLoop->wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))==0 ){ /* If we know that only a prefix of the record will be used, ** it is advantageous to reduce the "column count" field in @@ -156288,6 +165287,7 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( iIndexCur = pParse->nTab++; } pLevel->iIdxCur = iIndexCur; + assert( pIx!=0 ); assert( pIx->pSchema==pTab->pSchema ); assert( iIndexCur>=0 ); if( op ){ @@ -156321,6 +165321,37 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( } } if( iDb>=0 ) sqlcipher_sqlite3CodeVerifySchema(pParse, iDb); + if( (pTabItem->fg.jointype & JT_RIGHT)!=0 + && (pLevel->pRJ = sqlcipher_sqlite3WhereMalloc(pWInfo, sizeof(WhereRightJoin)))!=0 + ){ + WhereRightJoin *pRJ = pLevel->pRJ; + pRJ->iMatch = pParse->nTab++; + pRJ->regBloom = ++pParse->nMem; + sqlcipher_sqlite3VdbeAddOp2(v, OP_Blob, 65536, pRJ->regBloom); + pRJ->regReturn = ++pParse->nMem; + sqlcipher_sqlite3VdbeAddOp2(v, OP_Null, 0, pRJ->regReturn); + assert( pTab==pTabItem->pTab ); + if( HasRowid(pTab) ){ + KeyInfo *pInfo; + sqlcipher_sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, 1); + pInfo = sqlcipher_sqlite3KeyInfoAlloc(pParse->db, 1, 0); + if( pInfo ){ + pInfo->aColl[0] = 0; + pInfo->aSortFlags[0] = 0; + sqlcipher_sqlite3VdbeAppendP4(v, pInfo, P4_KEYINFO); + } + }else{ + Index *pPk = sqlcipher_sqlite3PrimaryKeyIndex(pTab); + sqlcipher_sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, pPk->nKeyCol); + sqlcipher_sqlite3VdbeSetP4KeyInfo(pParse, pPk); + } + pLoop->wsFlags &= ~WHERE_IDX_ONLY; + /* The nature of RIGHT JOIN processing is such that it messes up + ** the output order. So omit any ORDER BY/GROUP BY elimination + ** optimizations. We need to do an actual sort for RIGHT JOIN. */ + pWInfo->nOBSat = 0; + pWInfo->eDistinct = WHERE_DISTINCT_UNORDERED; + } } pWInfo->iTop = sqlcipher_sqlite3VdbeCurrentAddr(v); if( db->mallocFailed ) goto whereBeginError; @@ -156332,15 +165363,31 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( for(ii=0; iinErr ) goto whereBeginError; pLevel = &pWInfo->a[ii]; wsFlags = pLevel->pWLoop->wsFlags; + pSrc = &pTabList->a[pLevel->iFrom]; + if( pSrc->fg.isMaterialized ){ + if( pSrc->fg.isCorrelated ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub); + }else{ + int iOnce = sqlcipher_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub); + sqlcipher_sqlite3VdbeJumpHere(v, iOnce); + } + } + if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ + if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){ #ifndef SQLITE_OMIT_AUTOMATIC_INDEX - if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){ - constructAutomaticIndex(pParse, &pWInfo->sWC, - &pTabList->a[pLevel->iFrom], notReady, pLevel); + constructAutomaticIndex(pParse, &pWInfo->sWC, + &pTabList->a[pLevel->iFrom], notReady, pLevel); +#endif + }else{ + sqlcipher_sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady); + } if( db->mallocFailed ) goto whereBeginError; } -#endif addrExplain = sqlcipher_sqlite3WhereExplainOneScan( pParse, pTabList, pLevel, wctrlFlags ); @@ -156360,6 +165407,8 @@ SQLITE_PRIVATE WhereInfo *sqlcipher_sqlite3WhereBegin( /* Jump here if malloc fails */ whereBeginError: if( pWInfo ){ + testcase( pWInfo->pExprMods!=0 ); + whereUndoExprMods(pWInfo); pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); } @@ -156386,6 +165435,26 @@ whereBeginError: } #endif +#ifdef SQLITE_DEBUG +/* +** Return true if cursor iCur is opened by instruction k of the +** bytecode. Used inside of assert() only. +*/ +static int cursorIsOpen(Vdbe *v, int iCur, int k){ + while( k>=0 ){ + VdbeOp *pOp = sqlcipher_sqlite3VdbeGetOp(v,k--); + if( pOp->p1!=iCur ) continue; + if( pOp->opcode==OP_Close ) return 0; + if( pOp->opcode==OP_OpenRead ) return 1; + if( pOp->opcode==OP_OpenWrite ) return 1; + if( pOp->opcode==OP_OpenDup ) return 1; + if( pOp->opcode==OP_OpenAutoindex ) return 1; + if( pOp->opcode==OP_OpenEphemeral ) return 1; + } + return 0; +} +#endif /* SQLITE_DEBUG */ + /* ** Generate the end of the WHERE loop. See comments on ** sqlcipher_sqlite3WhereBegin() for additional information. @@ -156399,6 +165468,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ SrcList *pTabList = pWInfo->pTabList; sqlcipher_sqlite3 *db = pParse->db; int iEnd = sqlcipher_sqlite3VdbeCurrentAddr(v); + int nRJ = 0; /* Generate loop termination code. */ @@ -156406,6 +165476,17 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ for(i=pWInfo->nLevel-1; i>=0; i--){ int addr; pLevel = &pWInfo->a[i]; + if( pLevel->pRJ ){ + /* Terminate the subroutine that forms the interior of the loop of + ** the RIGHT JOIN table */ + WhereRightJoin *pRJ = pLevel->pRJ; + sqlcipher_sqlite3VdbeResolveLabel(v, pLevel->addrCont); + pLevel->addrCont = 0; + pRJ->endSubrtn = sqlcipher_sqlite3VdbeCurrentAddr(v); + sqlcipher_sqlite3VdbeAddOp3(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn, 1); + VdbeCoverage(v); + nRJ++; + } pLoop = pLevel->pWLoop; if( pLevel->op!=OP_Noop ){ #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT @@ -156433,7 +165514,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ } #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ /* The common case: Advance to the next row */ - sqlcipher_sqlite3VdbeResolveLabel(v, pLevel->addrCont); + if( pLevel->addrCont ) sqlcipher_sqlite3VdbeResolveLabel(v, pLevel->addrCont); sqlcipher_sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); sqlcipher_sqlite3VdbeChangeP5(v, pLevel->p5); VdbeCoverage(v); @@ -156448,14 +165529,16 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT if( addrSeek ) sqlcipher_sqlite3VdbeJumpHere(v, addrSeek); #endif - }else{ + }else if( pLevel->addrCont ){ sqlcipher_sqlite3VdbeResolveLabel(v, pLevel->addrCont); } - if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){ + if( (pLoop->wsFlags & WHERE_IN_ABLE)!=0 && pLevel->u.in.nIn>0 ){ struct InLoop *pIn; int j; sqlcipher_sqlite3VdbeResolveLabel(v, pLevel->addrNxt); for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){ + assert( sqlcipher_sqlite3VdbeGetOp(v, pIn->addrInTop+1)->opcode==OP_IsNull + || pParse->db->mallocFailed ); sqlcipher_sqlite3VdbeJumpHere(v, pIn->addrInTop+1); if( pIn->eEndLoopOp!=OP_Noop ){ if( pIn->nPrefix ){ @@ -156480,6 +165563,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ sqlcipher_sqlite3VdbeCurrentAddr(v)+2, pIn->iBase, pIn->nPrefix); VdbeCoverage(v); + /* Retarget the OP_IsNull against the left operand of IN so + ** it jumps past the OP_IfNoHope. This is because the + ** OP_IsNull also bypasses the OP_Affinity opcode that is + ** required by OP_IfNoHope. */ + sqlcipher_sqlite3VdbeJumpHere(v, pIn->addrInTop+1); } } sqlcipher_sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop); @@ -156491,6 +165579,10 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ } } sqlcipher_sqlite3VdbeResolveLabel(v, pLevel->addrBrk); + if( pLevel->pRJ ){ + sqlcipher_sqlite3VdbeAddOp3(v, OP_Return, pLevel->pRJ->regReturn, 0, 1); + VdbeCoverage(v); + } if( pLevel->addrSkip ){ sqlcipher_sqlite3VdbeGoto(v, pLevel->addrSkip); VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName)); @@ -156513,8 +165605,14 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ sqlcipher_sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); } if( (ws & WHERE_INDEXED) - || ((ws & WHERE_MULTI_OR) && pLevel->u.pCovidx) + || ((ws & WHERE_MULTI_OR) && pLevel->u.pCoveringIdx) ){ + if( ws & WHERE_MULTI_OR ){ + Index *pIx = pLevel->u.pCoveringIdx; + int iDb = sqlcipher_sqlite3SchemaToIndex(db, pIx->pSchema); + sqlcipher_sqlite3VdbeAddOp3(v, OP_ReopenIdx, pLevel->iIdxCur, pIx->tnum, iDb); + sqlcipher_sqlite3VdbeSetP4KeyInfo(pParse, pIx); + } sqlcipher_sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur); } if( pLevel->op==OP_Return ){ @@ -156528,21 +165626,26 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); } - /* The "break" point is here, just past the end of the outer loop. - ** Set it. - */ - sqlcipher_sqlite3VdbeResolveLabel(v, pWInfo->iBreak); - assert( pWInfo->nLevel<=pTabList->nSrc ); + if( pWInfo->pExprMods ) whereUndoExprMods(pWInfo); for(i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++){ int k, last; VdbeOp *pOp, *pLastOp; Index *pIdx = 0; - struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; + SrcItem *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); pLoop = pLevel->pWLoop; + /* Do RIGHT JOIN processing. Generate code that will output the + ** unmatched rows of the right operand of the RIGHT JOIN with + ** all of the columns of the left operand set to NULL. + */ + if( pLevel->pRJ ){ + sqlcipher_sqlite3WhereRightJoinLoop(pWInfo, i, pLevel); + continue; + } + /* For a co-routine, change all OP_Column references to the table of ** the co-routine into OP_Copy of result contained in a register. ** OP_Rowid becomes OP_Null. @@ -156554,29 +165657,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ continue; } -#ifdef SQLITE_ENABLE_EARLY_CURSOR_CLOSE - /* Close all of the cursors that were opened by sqlcipher_sqlite3WhereBegin. - ** Except, do not close cursors that will be reused by the OR optimization - ** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors - ** created for the ONEPASS optimization. - */ - if( (pTab->tabFlags & TF_Ephemeral)==0 - && pTab->pSelect==0 - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 - ){ - int ws = pLoop->wsFlags; - if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){ - sqlcipher_sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); - } - if( (ws & WHERE_INDEXED)!=0 - && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 - && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1] - ){ - sqlcipher_sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); - } - } -#endif - /* If this scan uses an index, make VDBE code substitutions to read data ** from the index instead of from the table where possible. In some cases ** this optimization prevents the table from ever being read, which can @@ -156591,7 +165671,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ if( pLoop->wsFlags & (WHERE_INDEXED|WHERE_IDX_ONLY) ){ pIdx = pLoop->u.btree.pIndex; }else if( pLoop->wsFlags & WHERE_MULTI_OR ){ - pIdx = pLevel->u.pCovidx; + pIdx = pLevel->u.pCoveringIdx; } if( pIdx && !db->mallocFailed @@ -156614,7 +165694,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ #endif pOp = sqlcipher_sqlite3VdbeGetOp(v, k); pLastOp = pOp + (last - k); - assert( pOpp1!=pLevel->iTabCur ){ /* no-op */ @@ -156625,6 +165705,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ ){ int x = pOp->p2; assert( pIdx->pTable==pTab ); +#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC + if( pOp->opcode==OP_Offset ){ + /* Do not need to translate the column number */ + }else +#endif if( !HasRowid(pTab) ){ Index *pPk = sqlcipher_sqlite3PrimaryKeyIndex(pTab); x = pPk->aiColumn[x]; @@ -156638,9 +165723,22 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ pOp->p2 = x; pOp->p1 = pLevel->iIdxCur; OpcodeRewriteTrace(db, k, pOp); + }else{ + /* Unable to translate the table reference into an index + ** reference. Verify that this is harmless - that the + ** table being referenced really is open. + */ +#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC + assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 + || cursorIsOpen(v,pOp->p1,k) + || pOp->opcode==OP_Offset + ); +#else + assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 + || cursorIsOpen(v,pOp->p1,k) + ); +#endif } - assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 - || pWInfo->eOnePass ); }else if( pOp->opcode==OP_Rowid ){ pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; @@ -156659,18 +165757,16 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WhereEnd(WhereInfo *pWInfo){ } } - /* Undo all Expr node modifications */ - while( pWInfo->pExprMods ){ - WhereExprMod *p = pWInfo->pExprMods; - pWInfo->pExprMods = p->pNext; - memcpy(p->pExpr, &p->orig, sizeof(p->orig)); - sqlcipher_sqlite3DbFree(db, p); - } + /* The "break" point is here, just past the end of the outer loop. + ** Set it. + */ + sqlcipher_sqlite3VdbeResolveLabel(v, pWInfo->iBreak); /* Final cleanup */ pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); + pParse->withinRJSubrtn -= nRJ; return; } @@ -157259,7 +166355,7 @@ static void noopValueFunc(sqlcipher_sqlite3_context *p){ UNUSED_PARAMETER(p); /* /* Window functions that use all window interfaces: xStep, xFinal, ** xValue, and xInverse */ #define WINDOWFUNCALL(name,nArg,extra) { \ - nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ + nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ name ## StepFunc, name ## FinalizeFunc, name ## ValueFunc, \ name ## InvFunc, name ## Name, {0} \ } @@ -157267,7 +166363,7 @@ static void noopValueFunc(sqlcipher_sqlite3_context *p){ UNUSED_PARAMETER(p); /* /* Window functions that are implemented using bytecode and thus have ** no-op routines for their methods */ #define WINDOWFUNCNOOP(name,nArg,extra) { \ - nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ + nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ noopStepFunc, noopValueFunc, noopValueFunc, \ noopStepFunc, name ## Name, {0} \ } @@ -157276,7 +166372,7 @@ static void noopValueFunc(sqlcipher_sqlite3_context *p){ UNUSED_PARAMETER(p); /* ** same routine for xFinalize and xValue and which never call ** xInverse. */ #define WINDOWFUNCX(name,nArg,extra) { \ - nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ + nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ name ## StepFunc, name ## ValueFunc, name ## ValueFunc, \ noopStepFunc, name ## Name, {0} \ } @@ -157402,7 +166498,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WindowUpdate( } } } - pWin->pFunc = pFunc; + pWin->pWFunc = pFunc; } /* @@ -157466,6 +166562,7 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){ case TK_AGG_FUNCTION: case TK_COLUMN: { int iCol = -1; + if( pParse->db->mallocFailed ) return WRC_Abort; if( p->pSub ){ int i; for(i=0; ipSub->nExpr; i++){ @@ -157575,14 +166672,17 @@ static ExprList *exprListAppendList( int i; int nInit = pList ? pList->nExpr : 0; for(i=0; inExpr; i++){ - Expr *pDup = sqlcipher_sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); + sqlcipher_sqlite3 *db = pParse->db; + Expr *pDup = sqlcipher_sqlite3ExprDup(db, pAppend->a[i].pExpr, 0); assert( pDup==0 || !ExprHasProperty(pDup, EP_MemToken) ); - if( bIntToNull && pDup ){ + if( db->mallocFailed ){ + sqlcipher_sqlite3ExprDelete(db, pDup); + break; + } + if( bIntToNull ){ int iDummy; Expr *pSub; - for(pSub=pDup; ExprHasProperty(pSub, EP_Skip); pSub=pSub->pLeft){ - assert( pSub ); - } + pSub = sqlcipher_sqlite3ExprSkipCollateAndLikely(pDup); if( sqlcipher_sqlite3ExprIsInteger(pSub, &iDummy) ){ pSub->op = TK_NULL; pSub->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); @@ -157590,7 +166690,7 @@ static ExprList *exprListAppendList( } } pList = sqlcipher_sqlite3ExprListAppend(pParse, pList, pDup); - if( pList ) pList->a[nInit+i].sortFlags = pAppend->a[i].sortFlags; + if( pList ) pList->a[nInit+i].fg.sortFlags = pAppend->a[i].fg.sortFlags; } } return pList; @@ -157613,6 +166713,15 @@ static int sqlcipher_sqlite3WindowExtraAggFuncDepth(Walker *pWalker, Expr *pExpr return WRC_Continue; } +static int disallowAggregatesInOrderByCb(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_AGG_FUNCTION && pExpr->pAggInfo==0 ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlcipher_sqlite3ErrorMsg(pWalker->pParse, + "misuse of aggregate: %s()", pExpr->u.zToken); + } + return WRC_Continue; +} + /* ** If the SELECT statement passed as the second argument does not invoke ** any SQL window functions, this function is a no-op. Otherwise, it @@ -157622,7 +166731,11 @@ static int sqlcipher_sqlite3WindowExtraAggFuncDepth(Walker *pWalker, Expr *pExpr */ SQLITE_PRIVATE int sqlcipher_sqlite3WindowRewrite(Parse *pParse, Select *p){ int rc = SQLITE_OK; - if( p->pWin && p->pPrior==0 && (p->selFlags & SF_WinRewrite)==0 ){ + if( p->pWin + && p->pPrior==0 + && ALWAYS((p->selFlags & SF_WinRewrite)==0) + && ALWAYS(!IN_RENAME_OBJECT) + ){ Vdbe *v = sqlcipher_sqlite3GetVdbe(pParse); sqlcipher_sqlite3 *db = pParse->db; Select *pSub = 0; /* The subquery */ @@ -157646,6 +166759,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WindowRewrite(Parse *pParse, Select *p){ } sqlcipher_sqlite3AggInfoPersistWalkerInit(&w, pParse); sqlcipher_sqlite3WalkSelect(&w, p); + if( (p->selFlags & SF_Aggregate)==0 ){ + w.xExprCallback = disallowAggregatesInOrderByCb; + w.xSelectCallback = 0; + sqlcipher_sqlite3WalkExprList(&w, p->pOrderBy); + } p->pSrc = 0; p->pWhere = 0; @@ -157690,8 +166808,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WindowRewrite(Parse *pParse, Select *p){ ** window function - one for the accumulator, another for interim ** results. */ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - ExprList *pArgs = pWin->pOwner->x.pList; - if( pWin->pFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){ + ExprList *pArgs; + assert( ExprUseXList(pWin->pOwner) ); + assert( pWin->pWFunc!=0 ); + pArgs = pWin->pOwner->x.pList; + if( pWin->pWFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){ selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist); pWin->iArgCol = (pSublist ? pSublist->nExpr : 0); pWin->bExprArgs = 1; @@ -157727,11 +166848,14 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WindowRewrite(Parse *pParse, Select *p){ ("New window-function subquery in FROM clause of (%u/%p)\n", p->selId, p)); p->pSrc = sqlcipher_sqlite3SrcListAppend(pParse, 0, 0, 0); + assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside + ** of sqlcipher_sqlite3DbMallocRawNN() called from + ** sqlcipher_sqlite3SrcListAppend() */ if( p->pSrc ){ Table *pTab2; p->pSrc->a[0].pSelect = pSub; sqlcipher_sqlite3SrcListAssignCursors(pParse, p->pSrc); - pSub->selFlags |= SF_Expanded; + pSub->selFlags |= SF_Expanded|SF_OrderByReqd; pTab2 = sqlcipher_sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE); pSub->selFlags |= (selFlags & SF_Aggregate); if( pTab2==0 ){ @@ -157754,15 +166878,14 @@ SQLITE_PRIVATE int sqlcipher_sqlite3WindowRewrite(Parse *pParse, Select *p){ sqlcipher_sqlite3SelectDelete(db, pSub); } if( db->mallocFailed ) rc = SQLITE_NOMEM; - sqlcipher_sqlite3DbFree(db, pTab); - } - if( rc ){ - if( pParse->nErr==0 ){ - assert( pParse->db->mallocFailed ); - sqlcipher_sqlite3ErrorToParser(pParse->db, SQLITE_NOMEM); - } + /* Defer deleting the temporary table pTab because if an error occurred, + ** there could still be references to that table embedded in the + ** result-set or ORDER BY clause of the SELECT statement p. */ + sqlcipher_sqlite3ParserAddCleanup(pParse, sqlcipher_sqlite3DbFree, pTab); } + + assert( rc==SQLITE_OK || pParse->nErr!=0 ); return rc; } @@ -157982,15 +167105,19 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WindowAttach(Parse *pParse, Expr *p, Window ** SELECT, or (b) the windows already linked use a compatible window frame. */ SQLITE_PRIVATE void sqlcipher_sqlite3WindowLink(Select *pSel, Window *pWin){ - if( pSel!=0 - && (0==pSel->pWin || 0==sqlcipher_sqlite3WindowCompare(0, pSel->pWin, pWin, 0)) - ){ - pWin->pNextWin = pSel->pWin; - if( pSel->pWin ){ - pSel->pWin->ppThis = &pWin->pNextWin; + if( pSel ){ + if( 0==pSel->pWin || 0==sqlcipher_sqlite3WindowCompare(0, pSel->pWin, pWin, 0) ){ + pWin->pNextWin = pSel->pWin; + if( pSel->pWin ){ + pSel->pWin->ppThis = &pWin->pNextWin; + } + pSel->pWin = pWin; + pWin->ppThis = &pSel->pWin; + }else{ + if( sqlcipher_sqlite3ExprListCompare(pWin->pPartition, pSel->pWin->pPartition,-1) ){ + pSel->selFlags |= SF_MultiPart; + } } - pSel->pWin = pWin; - pWin->ppThis = &pSel->pWin; } } @@ -157999,7 +167126,12 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WindowLink(Select *pSel, Window *pWin){ ** different, or 2 if it cannot be determined if the objects are identical ** or not. Identical window objects can be processed in a single scan. */ -SQLITE_PRIVATE int sqlcipher_sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2, int bFilter){ +SQLITE_PRIVATE int sqlcipher_sqlite3WindowCompare( + const Parse *pParse, + const Window *p1, + const Window *p2, + int bFilter +){ int res; if( NEVER(p1==0) || NEVER(p2==0) ) return 1; if( p1->eFrmType!=p2->eFrmType ) return 1; @@ -158062,7 +167194,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WindowCodeInit(Parse *pParse, Select *pSele } for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *p = pWin->pFunc; + FuncDef *p = pWin->pWFunc; if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){ /* The inline versions of min() and max() require a single ephemeral ** table and 3 registers. The registers are used as follows: @@ -158071,12 +167203,15 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WindowCodeInit(Parse *pParse, Select *pSele ** regApp+1: integer value used to ensure keys are unique ** regApp+2: output of MakeRecord */ - ExprList *pList = pWin->pOwner->x.pList; - KeyInfo *pKeyInfo = sqlcipher_sqlite3KeyInfoFromExprList(pParse, pList, 0, 0); + ExprList *pList; + KeyInfo *pKeyInfo; + assert( ExprUseXList(pWin->pOwner) ); + pList = pWin->pOwner->x.pList; + pKeyInfo = sqlcipher_sqlite3KeyInfoFromExprList(pParse, pList, 0, 0); pWin->csrApp = pParse->nTab++; pWin->regApp = pParse->nMem+1; pParse->nMem += 3; - if( pKeyInfo && pWin->pFunc->zName[1]=='i' ){ + if( pKeyInfo && pWin->pWFunc->zName[1]=='i' ){ assert( pKeyInfo->aSortFlags[0]==0 ); pKeyInfo->aSortFlags[0] = KEYINFO_ORDER_DESC; } @@ -158143,6 +167278,7 @@ static void windowCheckValue(Parse *pParse, int reg, int eCond){ VdbeCoverageIf(v, eCond==2); } sqlcipher_sqlite3VdbeAddOp3(v, aOp[eCond], regZero, sqlcipher_sqlite3VdbeCurrentAddr(v)+2, reg); + sqlcipher_sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC); VdbeCoverageNeverNullIf(v, eCond==0); /* NULL case captured by */ VdbeCoverageNeverNullIf(v, eCond==1); /* the OP_MustBeInt */ VdbeCoverageNeverNullIf(v, eCond==2); @@ -158159,7 +167295,9 @@ static void windowCheckValue(Parse *pParse, int reg, int eCond){ ** with the object passed as the only argument to this function. */ static int windowArgCount(Window *pWin){ - ExprList *pList = pWin->pOwner->x.pList; + const ExprList *pList; + assert( ExprUseXList(pWin->pOwner) ); + pList = pWin->pOwner->x.pList; return (pList ? pList->nExpr : 0); } @@ -158237,6 +167375,7 @@ struct WindowCodeArg { int regGosub; /* Register used with OP_Gosub(addrGosub) */ int regArg; /* First in array of accumulator registers */ int eDelete; /* See above */ + int regRowid; WindowCsrAndReg start; WindowCsrAndReg current; @@ -158295,7 +167434,7 @@ static void windowAggStep( Vdbe *v = sqlcipher_sqlite3GetVdbe(pParse); Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; + FuncDef *pFunc = pWin->pWFunc; int regArg; int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin); int i; @@ -158343,6 +167482,7 @@ static void windowAggStep( int addrIf = 0; if( pWin->pFilter ){ int regTmp; + assert( ExprUseXList(pWin->pOwner) ); assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); regTmp = sqlcipher_sqlite3GetTempReg(pParse); @@ -158353,16 +167493,17 @@ static void windowAggStep( } if( pWin->bExprArgs ){ - int iStart = sqlcipher_sqlite3VdbeCurrentAddr(v); - VdbeOp *pOp, *pEnd; + int iOp = sqlcipher_sqlite3VdbeCurrentAddr(v); + int iEnd; + assert( ExprUseXList(pWin->pOwner) ); nArg = pWin->pOwner->x.pList->nExpr; regArg = sqlcipher_sqlite3GetTempRange(pParse, nArg); sqlcipher_sqlite3ExprCodeExprList(pParse, pWin->pOwner->x.pList, regArg, 0, 0); - pEnd = sqlcipher_sqlite3VdbeGetOp(v, -1); - for(pOp=sqlcipher_sqlite3VdbeGetOp(v, iStart); pOp<=pEnd; pOp++){ - if( pOp->opcode==OP_Column && pOp->p1==pWin->iEphCsr ){ + for(iEnd=sqlcipher_sqlite3VdbeCurrentAddr(v); iOpopcode==OP_Column && pOp->p1==pMWin->iEphCsr ){ pOp->p1 = csr; } } @@ -158370,6 +167511,7 @@ static void windowAggStep( if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ CollSeq *pColl; assert( nArg>0 ); + assert( ExprUseXList(pWin->pOwner) ); pColl = sqlcipher_sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr); sqlcipher_sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ); } @@ -158406,7 +167548,7 @@ static void windowAggFinal(WindowCodeArg *p, int bFin){ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ if( pMWin->regStartRowid==0 - && (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) + && (pWin->pWFunc->funcFlags & SQLITE_FUNC_MINMAX) && (pWin->eStart!=TK_UNBOUNDED) ){ sqlcipher_sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); @@ -158420,12 +167562,12 @@ static void windowAggFinal(WindowCodeArg *p, int bFin){ int nArg = windowArgCount(pWin); if( bFin ){ sqlcipher_sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, nArg); - sqlcipher_sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + sqlcipher_sqlite3VdbeAppendP4(v, pWin->pWFunc, P4_FUNCDEF); sqlcipher_sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult); sqlcipher_sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); }else{ sqlcipher_sqlite3VdbeAddOp3(v, OP_AggValue,pWin->regAccum,nArg,pWin->regResult); - sqlcipher_sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + sqlcipher_sqlite3VdbeAppendP4(v, pWin->pWFunc, P4_FUNCDEF); } } } @@ -158554,7 +167696,8 @@ static void windowReturnOneRow(WindowCodeArg *p){ Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; + FuncDef *pFunc = pWin->pWFunc; + assert( ExprUseXList(pWin->pOwner) ); if( pFunc->zName==nth_valueName || pFunc->zName==first_valueName ){ @@ -158625,7 +167768,7 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){ int nArg = 0; Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; + FuncDef *pFunc = pWin->pWFunc; assert( pWin->regAccum ); sqlcipher_sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); nArg = MAX(nArg, windowArgCount(pWin)); @@ -158655,7 +167798,7 @@ static int windowCacheFrame(Window *pMWin){ Window *pWin; if( pMWin->regStartRowid ) return 1; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; + FuncDef *pFunc = pWin->pWFunc; if( (pFunc->zName==nth_valueName) || (pFunc->zName==first_valueName) || (pFunc->zName==leadName) @@ -158720,7 +167863,7 @@ static void windowIfNewPeer( ** if( csr1.peerVal - regVal <= csr2.peerVal ) goto lbl; ** ** A special type of arithmetic is used such that if csr1.peerVal is not -** a numeric type (real or integer), then the result of the addition addition +** a numeric type (real or integer), then the result of the addition ** or subtraction is a a copy of csr1.peerVal. */ static void windowCodeRangeTest( @@ -158739,10 +167882,16 @@ static void windowCodeRangeTest( int regString = ++pParse->nMem; /* Reg. for constant value '' */ int arith = OP_Add; /* OP_Add or OP_Subtract */ int addrGe; /* Jump destination */ + int addrDone = sqlcipher_sqlite3VdbeMakeLabel(pParse); /* Address past OP_Ge */ + CollSeq *pColl; + + /* Read the peer-value from each cursor into a register */ + windowReadPeerValues(p, csr1, reg1); + windowReadPeerValues(p, csr2, reg2); assert( op==OP_Ge || op==OP_Gt || op==OP_Le ); assert( pOrderBy && pOrderBy->nExpr==1 ); - if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){ + if( pOrderBy->a[0].fg.sortFlags & KEYINFO_ORDER_DESC ){ switch( op ){ case OP_Ge: op = OP_Le; break; case OP_Gt: op = OP_Lt; break; @@ -158751,34 +167900,11 @@ static void windowCodeRangeTest( arith = OP_Subtract; } - /* Read the peer-value from each cursor into a register */ - windowReadPeerValues(p, csr1, reg1); - windowReadPeerValues(p, csr2, reg2); - VdbeModuleComment((v, "CodeRangeTest: if( R%d %s R%d %s R%d ) goto lbl", reg1, (arith==OP_Add ? "+" : "-"), regVal, ((op==OP_Ge) ? ">=" : (op==OP_Le) ? "<=" : (op==OP_Gt) ? ">" : "<"), reg2 )); - /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1). - ** This block adds (or subtracts for DESC) the numeric value in regVal - ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob), - ** then leave reg1 as it is. In pseudo-code, this is implemented as: - ** - ** if( reg1>='' ) goto addrGe; - ** reg1 = reg1 +/- regVal - ** addrGe: - ** - ** Since all strings and blobs are greater-than-or-equal-to an empty string, - ** the add/subtract is skipped for these, as required. If reg1 is a NULL, - ** then the arithmetic is performed, but since adding or subtracting from - ** NULL is always NULL anyway, this case is handled as required too. */ - sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); - addrGe = sqlcipher_sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1); - VdbeCoverage(v); - sqlcipher_sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1); - sqlcipher_sqlite3VdbeJumpHere(v, addrGe); - /* If the BIGNULL flag is set for the ORDER BY, then it is required to ** consider NULL values to be larger than all other values, instead of ** the usual smaller. The VDBE opcodes OP_Ge and so on do not handle this @@ -158798,7 +167924,7 @@ static void windowCodeRangeTest( ** Additionally, if either reg1 or reg2 are NULL but the jump to lbl is ** not taken, control jumps over the comparison operator coded below this ** block. */ - if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_BIGNULL ){ + if( pOrderBy->a[0].fg.sortFlags & KEYINFO_ORDER_BIGNULL ){ /* This block runs if reg1 contains a NULL. */ int addr = sqlcipher_sqlite3VdbeAddOp1(v, OP_NotNull, reg1); VdbeCoverage(v); switch( op ){ @@ -158815,21 +167941,46 @@ static void windowCodeRangeTest( break; default: assert( op==OP_Lt ); /* no-op */ break; } - sqlcipher_sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlcipher_sqlite3VdbeCurrentAddr(v)+3); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone); /* This block runs if reg1 is not NULL, but reg2 is. */ sqlcipher_sqlite3VdbeJumpHere(v, addr); sqlcipher_sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl); VdbeCoverage(v); if( op==OP_Gt || op==OP_Ge ){ - sqlcipher_sqlite3VdbeChangeP2(v, -1, sqlcipher_sqlite3VdbeCurrentAddr(v)+1); + sqlcipher_sqlite3VdbeChangeP2(v, -1, addrDone); } } + /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1). + ** This block adds (or subtracts for DESC) the numeric value in regVal + ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob), + ** then leave reg1 as it is. In pseudo-code, this is implemented as: + ** + ** if( reg1>='' ) goto addrGe; + ** reg1 = reg1 +/- regVal + ** addrGe: + ** + ** Since all strings and blobs are greater-than-or-equal-to an empty string, + ** the add/subtract is skipped for these, as required. If reg1 is a NULL, + ** then the arithmetic is performed, but since adding or subtracting from + ** NULL is always NULL anyway, this case is handled as required too. */ + sqlcipher_sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); + addrGe = sqlcipher_sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1); + VdbeCoverage(v); + if( (op==OP_Ge && arith==OP_Add) || (op==OP_Le && arith==OP_Subtract) ){ + sqlcipher_sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v); + } + sqlcipher_sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1); + sqlcipher_sqlite3VdbeJumpHere(v, addrGe); + /* Compare registers reg2 and reg1, taking the jump if required. Note that ** control skips over this test if the BIGNULL flag is set and either ** reg1 or reg2 contain a NULL value. */ sqlcipher_sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v); + pColl = sqlcipher_sqlite3ExprNNCollSeq(pParse, pOrderBy->a[0].pExpr); + sqlcipher_sqlite3VdbeAppendP4(v, (void*)pColl, P4_COLLSEQ); sqlcipher_sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); + sqlcipher_sqlite3VdbeResolveLabel(v, addrDone); assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le ); testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge); @@ -158905,16 +168056,24 @@ static int windowCodeOp( /* If this is a (RANGE BETWEEN a FOLLOWING AND b FOLLOWING) or ** (RANGE BETWEEN b PRECEDING AND a PRECEDING) frame, ensure the ** start cursor does not advance past the end cursor within the - ** temporary table. It otherwise might, if (a>b). */ + ** temporary table. It otherwise might, if (a>b). Also ensure that, + ** if the input cursor is still finding new rows, that the end + ** cursor does not go past it to EOF. */ if( pMWin->eStart==pMWin->eEnd && regCountdown - && pMWin->eFrmType==TK_RANGE && op==WINDOW_AGGINVERSE + && pMWin->eFrmType==TK_RANGE ){ int regRowid1 = sqlcipher_sqlite3GetTempReg(pParse); int regRowid2 = sqlcipher_sqlite3GetTempReg(pParse); - sqlcipher_sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1); - sqlcipher_sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2); - sqlcipher_sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1); - VdbeCoverage(v); + if( op==WINDOW_AGGINVERSE ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1); + sqlcipher_sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2); + sqlcipher_sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1); + VdbeCoverage(v); + }else if( p->regRowid ){ + sqlcipher_sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid1); + sqlcipher_sqlite3VdbeAddOp3(v, OP_Ge, p->regRowid, lblDone, regRowid1); + VdbeCoverageNeverNull(v); + } sqlcipher_sqlite3ReleaseTempReg(pParse, regRowid1); sqlcipher_sqlite3ReleaseTempReg(pParse, regRowid2); assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ); @@ -158997,7 +168156,7 @@ SQLITE_PRIVATE Window *sqlcipher_sqlite3WindowDup(sqlcipher_sqlite3 *db, Expr *p pNew->zName = sqlcipher_sqlite3DbStrDup(db, p->zName); pNew->zBase = sqlcipher_sqlite3DbStrDup(db, p->zBase); pNew->pFilter = sqlcipher_sqlite3ExprDup(db, p->pFilter, 0); - pNew->pFunc = p->pFunc; + pNew->pWFunc = p->pWFunc; pNew->pPartition = sqlcipher_sqlite3ExprListDup(db, p->pPartition, 0); pNew->pOrderBy = sqlcipher_sqlite3ExprListDup(db, p->pOrderBy, 0); pNew->eFrmType = p->eFrmType; @@ -159411,7 +168570,6 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WindowCodeStep( int addrEmpty; /* Address of OP_Rewind in flush: */ int regNew; /* Array of registers holding new input row */ int regRecord; /* regNew array in record form */ - int regRowid; /* Rowid for regRecord in eph table */ int regNewPeer = 0; /* Peer values for new row (part of regNew) */ int regPeer = 0; /* Peer values for current row */ int regFlushPart = 0; /* Register for "Gosub flush_partition" */ @@ -159483,7 +168641,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WindowCodeStep( regNew = pParse->nMem+1; pParse->nMem += nInput; regRecord = ++pParse->nMem; - regRowid = ++pParse->nMem; + s.regRowid = ++pParse->nMem; /* If the window frame contains an " PRECEDING" or " FOLLOWING" ** clause, allocate registers to store the results of evaluating each @@ -159539,9 +168697,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WindowCodeStep( } /* Insert the new row into the ephemeral table */ - sqlcipher_sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, regRowid); - sqlcipher_sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, regRowid); - addrNe = sqlcipher_sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, regRowid); + sqlcipher_sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, s.regRowid); + sqlcipher_sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, s.regRowid); + addrNe = sqlcipher_sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, s.regRowid); VdbeCoverageNeverNull(v); /* This block is run for the first row of each partition */ @@ -159659,6 +168817,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3WindowCodeStep( sqlcipher_sqlite3VdbeJumpHere(v, addrGosubFlush); } + s.regRowid = 0; addrEmpty = sqlcipher_sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite); VdbeCoverage(v); if( pMWin->eEnd==TK_PRECEDING ){ @@ -159835,11 +168994,21 @@ static void updateDeleteLimitError( static void parserDoubleLinkSelect(Parse *pParse, Select *p){ assert( p!=0 ); if( p->pPrior ){ - Select *pNext = 0, *pLoop; - int mxSelect, cnt = 0; - for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ + Select *pNext = 0, *pLoop = p; + int mxSelect, cnt = 1; + while(1){ pLoop->pNext = pNext; pLoop->selFlags |= SF_Compound; + pNext = pLoop; + pLoop = pLoop->pPrior; + if( pLoop==0 ) break; + cnt++; + if( pLoop->pOrderBy || pLoop->pLimit ){ + sqlcipher_sqlite3ErrorMsg(pParse,"%s clause should come after %s not before", + pLoop->pOrderBy!=0 ? "ORDER BY" : "LIMIT", + sqlcipher_sqlite3SelectOpName(pNext->op)); + break; + } } if( (p->selFlags & SF_MultiValue)==0 && (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 && @@ -159850,11 +169019,21 @@ static void updateDeleteLimitError( } } - - /* Construct a new Expr object from a single identifier. Use the - ** new Expr to populate pOut. Set the span of pOut to be the identifier - ** that created the expression. + /* Attach a With object describing the WITH clause to a Select + ** object describing the query for which the WITH clause is a prefix. */ + static Select *attachWithToSelect(Parse *pParse, Select *pSelect, With *pWith){ + if( pSelect ){ + pSelect->pWith = pWith; + parserDoubleLinkSelect(pParse, pSelect); + }else{ + sqlcipher_sqlite3WithDelete(pParse->db, pWith); + } + return pSelect; + } + + + /* Construct a new Expr object from a single token */ static Expr *tokenExpr(Parse *pParse, int op, Token t){ Expr *p = sqlcipher_sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1); if( p ){ @@ -159863,17 +169042,18 @@ static void updateDeleteLimitError( p->affExpr = 0; p->flags = EP_Leaf; ExprClearVVAProperties(p); - p->iAgg = -1; + /* p->iAgg = -1; // Not required */ p->pLeft = p->pRight = 0; - p->x.pList = 0; p->pAggInfo = 0; - p->y.pTab = 0; + memset(&p->x, 0, sizeof(p->x)); + memset(&p->y, 0, sizeof(p->y)); p->op2 = 0; p->iTable = 0; p->iColumn = 0; p->u.zToken = (char*)&p[1]; memcpy(p->u.zToken, t.z, t.n); p->u.zToken[t.n] = 0; + p->w.iOfst = (int)(t.z - pParse->zTail); if( sqlcipher_sqlite3Isquote(p->u.zToken[0]) ){ sqlcipher_sqlite3DequoteExpr(p); } @@ -159953,8 +169133,8 @@ static void updateDeleteLimitError( #define TK_LP 22 #define TK_RP 23 #define TK_AS 24 -#define TK_WITHOUT 25 -#define TK_COMMA 26 +#define TK_COMMA 25 +#define TK_WITHOUT 26 #define TK_ABORT 27 #define TK_ACTION 28 #define TK_AFTER 29 @@ -160025,90 +169205,94 @@ static void updateDeleteLimitError( #define TK_TIES 94 #define TK_GENERATED 95 #define TK_ALWAYS 96 -#define TK_REINDEX 97 -#define TK_RENAME 98 -#define TK_CTIME_KW 99 -#define TK_ANY 100 -#define TK_BITAND 101 -#define TK_BITOR 102 -#define TK_LSHIFT 103 -#define TK_RSHIFT 104 -#define TK_PLUS 105 -#define TK_MINUS 106 -#define TK_STAR 107 -#define TK_SLASH 108 -#define TK_REM 109 -#define TK_CONCAT 110 -#define TK_COLLATE 111 -#define TK_BITNOT 112 -#define TK_ON 113 -#define TK_INDEXED 114 -#define TK_STRING 115 -#define TK_JOIN_KW 116 -#define TK_CONSTRAINT 117 -#define TK_DEFAULT 118 -#define TK_NULL 119 -#define TK_PRIMARY 120 -#define TK_UNIQUE 121 -#define TK_CHECK 122 -#define TK_REFERENCES 123 -#define TK_AUTOINCR 124 -#define TK_INSERT 125 -#define TK_DELETE 126 -#define TK_UPDATE 127 -#define TK_SET 128 -#define TK_DEFERRABLE 129 -#define TK_FOREIGN 130 -#define TK_DROP 131 -#define TK_UNION 132 -#define TK_ALL 133 -#define TK_EXCEPT 134 -#define TK_INTERSECT 135 -#define TK_SELECT 136 -#define TK_VALUES 137 -#define TK_DISTINCT 138 -#define TK_DOT 139 -#define TK_FROM 140 -#define TK_JOIN 141 -#define TK_USING 142 -#define TK_ORDER 143 -#define TK_GROUP 144 -#define TK_HAVING 145 -#define TK_LIMIT 146 -#define TK_WHERE 147 -#define TK_INTO 148 -#define TK_NOTHING 149 -#define TK_FLOAT 150 -#define TK_BLOB 151 -#define TK_INTEGER 152 -#define TK_VARIABLE 153 -#define TK_CASE 154 -#define TK_WHEN 155 -#define TK_THEN 156 -#define TK_ELSE 157 -#define TK_INDEX 158 -#define TK_ALTER 159 -#define TK_ADD 160 -#define TK_WINDOW 161 -#define TK_OVER 162 -#define TK_FILTER 163 -#define TK_COLUMN 164 -#define TK_AGG_FUNCTION 165 -#define TK_AGG_COLUMN 166 -#define TK_TRUEFALSE 167 -#define TK_ISNOT 168 -#define TK_FUNCTION 169 -#define TK_UMINUS 170 -#define TK_UPLUS 171 -#define TK_TRUTH 172 -#define TK_REGISTER 173 -#define TK_VECTOR 174 -#define TK_SELECT_COLUMN 175 -#define TK_IF_NULL_ROW 176 -#define TK_ASTERISK 177 -#define TK_SPAN 178 -#define TK_SPACE 179 -#define TK_ILLEGAL 180 +#define TK_MATERIALIZED 97 +#define TK_REINDEX 98 +#define TK_RENAME 99 +#define TK_CTIME_KW 100 +#define TK_ANY 101 +#define TK_BITAND 102 +#define TK_BITOR 103 +#define TK_LSHIFT 104 +#define TK_RSHIFT 105 +#define TK_PLUS 106 +#define TK_MINUS 107 +#define TK_STAR 108 +#define TK_SLASH 109 +#define TK_REM 110 +#define TK_CONCAT 111 +#define TK_PTR 112 +#define TK_COLLATE 113 +#define TK_BITNOT 114 +#define TK_ON 115 +#define TK_INDEXED 116 +#define TK_STRING 117 +#define TK_JOIN_KW 118 +#define TK_CONSTRAINT 119 +#define TK_DEFAULT 120 +#define TK_NULL 121 +#define TK_PRIMARY 122 +#define TK_UNIQUE 123 +#define TK_CHECK 124 +#define TK_REFERENCES 125 +#define TK_AUTOINCR 126 +#define TK_INSERT 127 +#define TK_DELETE 128 +#define TK_UPDATE 129 +#define TK_SET 130 +#define TK_DEFERRABLE 131 +#define TK_FOREIGN 132 +#define TK_DROP 133 +#define TK_UNION 134 +#define TK_ALL 135 +#define TK_EXCEPT 136 +#define TK_INTERSECT 137 +#define TK_SELECT 138 +#define TK_VALUES 139 +#define TK_DISTINCT 140 +#define TK_DOT 141 +#define TK_FROM 142 +#define TK_JOIN 143 +#define TK_USING 144 +#define TK_ORDER 145 +#define TK_GROUP 146 +#define TK_HAVING 147 +#define TK_LIMIT 148 +#define TK_WHERE 149 +#define TK_RETURNING 150 +#define TK_INTO 151 +#define TK_NOTHING 152 +#define TK_FLOAT 153 +#define TK_BLOB 154 +#define TK_INTEGER 155 +#define TK_VARIABLE 156 +#define TK_CASE 157 +#define TK_WHEN 158 +#define TK_THEN 159 +#define TK_ELSE 160 +#define TK_INDEX 161 +#define TK_ALTER 162 +#define TK_ADD 163 +#define TK_WINDOW 164 +#define TK_OVER 165 +#define TK_FILTER 166 +#define TK_COLUMN 167 +#define TK_AGG_FUNCTION 168 +#define TK_AGG_COLUMN 169 +#define TK_TRUEFALSE 170 +#define TK_ISNOT 171 +#define TK_FUNCTION 172 +#define TK_UMINUS 173 +#define TK_UPLUS 174 +#define TK_TRUTH 175 +#define TK_REGISTER 176 +#define TK_VECTOR 177 +#define TK_SELECT_COLUMN 178 +#define TK_IF_NULL_ROW 179 +#define TK_ASTERISK 180 +#define TK_SPAN 181 +#define TK_ERROR 182 +#define TK_SPACE 183 +#define TK_ILLEGAL 184 #endif /**************** End token definitions ***************************************/ @@ -160168,28 +169352,31 @@ static void updateDeleteLimitError( #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned short int -#define YYNOCODE 310 +#define YYNOCODE 319 #define YYACTIONTYPE unsigned short int -#define YYWILDCARD 100 +#define YYWILDCARD 101 #define sqlcipher_sqlite3ParserTOKENTYPE Token typedef union { int yyinit; sqlcipher_sqlite3ParserTOKENTYPE yy0; - SrcList* yy47; - u8 yy58; - struct FrameBound yy77; - With* yy131; - int yy192; - Expr* yy202; - struct {int value; int mask;} yy207; - struct TrigEvent yy230; - ExprList* yy242; - Window* yy303; - Upsert* yy318; - const char* yy436; - TriggerStep* yy447; - Select* yy539; - IdList* yy600; + TriggerStep* yy33; + Window* yy41; + Select* yy47; + SrcList* yy131; + struct TrigEvent yy180; + struct {int value; int mask;} yy231; + IdList* yy254; + u32 yy285; + ExprList* yy322; + Cte* yy385; + int yy394; + Upsert* yy444; + u8 yy516; + With* yy521; + const char* yy522; + Expr* yy528; + OnOrUsing yy561; + struct FrameBound yy595; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -160205,18 +169392,18 @@ typedef union { #define sqlcipher_sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; #define sqlcipher_sqlite3ParserCTX_STORE yypParser->pParse=pParse; #define YYFALLBACK 1 -#define YYNSTATE 557 -#define YYNRULE 385 -#define YYNRULE_WITH_ACTION 325 -#define YYNTOKEN 181 -#define YY_MAX_SHIFT 556 -#define YY_MIN_SHIFTREDUCE 807 -#define YY_MAX_SHIFTREDUCE 1191 -#define YY_ERROR_ACTION 1192 -#define YY_ACCEPT_ACTION 1193 -#define YY_NO_ACTION 1194 -#define YY_MIN_REDUCE 1195 -#define YY_MAX_REDUCE 1579 +#define YYNSTATE 580 +#define YYNRULE 405 +#define YYNRULE_WITH_ACTION 342 +#define YYNTOKEN 185 +#define YY_MAX_SHIFT 579 +#define YY_MIN_SHIFTREDUCE 839 +#define YY_MAX_SHIFTREDUCE 1243 +#define YY_ERROR_ACTION 1244 +#define YY_ACCEPT_ACTION 1245 +#define YY_NO_ACTION 1246 +#define YY_MIN_REDUCE 1247 +#define YY_MAX_REDUCE 1651 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -160283,588 +169470,620 @@ typedef union { ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (1974) +#define YY_ACTTAB_COUNT (2101) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 550, 1226, 550, 455, 1264, 550, 1243, 550, 114, 111, - /* 10 */ 211, 550, 1541, 550, 1264, 527, 114, 111, 211, 396, - /* 20 */ 1236, 348, 42, 42, 42, 42, 1229, 42, 42, 71, - /* 30 */ 71, 941, 1228, 71, 71, 71, 71, 1466, 1497, 942, - /* 40 */ 824, 457, 6, 121, 122, 112, 1169, 1169, 1010, 1013, - /* 50 */ 1003, 1003, 119, 119, 120, 120, 120, 120, 1547, 396, - /* 60 */ 1362, 1521, 556, 2, 1197, 194, 532, 440, 143, 291, - /* 70 */ 532, 136, 532, 375, 261, 508, 272, 389, 1277, 531, - /* 80 */ 507, 497, 164, 121, 122, 112, 1169, 1169, 1010, 1013, - /* 90 */ 1003, 1003, 119, 119, 120, 120, 120, 120, 1362, 446, - /* 100 */ 1518, 118, 118, 118, 118, 117, 117, 116, 116, 116, - /* 110 */ 115, 428, 266, 266, 266, 266, 1502, 362, 1504, 439, - /* 120 */ 361, 1502, 521, 528, 1489, 547, 1118, 547, 1118, 396, - /* 130 */ 409, 241, 208, 114, 111, 211, 98, 290, 541, 221, - /* 140 */ 1033, 118, 118, 118, 118, 117, 117, 116, 116, 116, - /* 150 */ 115, 428, 1146, 121, 122, 112, 1169, 1169, 1010, 1013, - /* 160 */ 1003, 1003, 119, 119, 120, 120, 120, 120, 410, 432, - /* 170 */ 117, 117, 116, 116, 116, 115, 428, 1422, 472, 123, - /* 180 */ 118, 118, 118, 118, 117, 117, 116, 116, 116, 115, - /* 190 */ 428, 116, 116, 116, 115, 428, 544, 544, 544, 396, - /* 200 */ 509, 120, 120, 120, 120, 113, 1055, 1146, 1147, 1148, - /* 210 */ 1055, 118, 118, 118, 118, 117, 117, 116, 116, 116, - /* 220 */ 115, 428, 1465, 121, 122, 112, 1169, 1169, 1010, 1013, - /* 230 */ 1003, 1003, 119, 119, 120, 120, 120, 120, 396, 448, - /* 240 */ 320, 83, 467, 81, 363, 386, 1146, 80, 118, 118, - /* 250 */ 118, 118, 117, 117, 116, 116, 116, 115, 428, 179, - /* 260 */ 438, 428, 121, 122, 112, 1169, 1169, 1010, 1013, 1003, - /* 270 */ 1003, 119, 119, 120, 120, 120, 120, 438, 437, 266, - /* 280 */ 266, 118, 118, 118, 118, 117, 117, 116, 116, 116, - /* 290 */ 115, 428, 547, 1113, 907, 510, 1146, 114, 111, 211, - /* 300 */ 1435, 1146, 1147, 1148, 206, 495, 1113, 396, 453, 1113, - /* 310 */ 549, 334, 120, 120, 120, 120, 298, 1435, 1437, 17, - /* 320 */ 118, 118, 118, 118, 117, 117, 116, 116, 116, 115, - /* 330 */ 428, 121, 122, 112, 1169, 1169, 1010, 1013, 1003, 1003, - /* 340 */ 119, 119, 120, 120, 120, 120, 396, 1362, 438, 1146, - /* 350 */ 486, 1146, 1147, 1148, 1000, 1000, 1011, 1014, 449, 118, - /* 360 */ 118, 118, 118, 117, 117, 116, 116, 116, 115, 428, - /* 370 */ 121, 122, 112, 1169, 1169, 1010, 1013, 1003, 1003, 119, - /* 380 */ 119, 120, 120, 120, 120, 1058, 1058, 469, 1435, 118, - /* 390 */ 118, 118, 118, 117, 117, 116, 116, 116, 115, 428, - /* 400 */ 1146, 455, 550, 1430, 1146, 1147, 1148, 233, 970, 1146, - /* 410 */ 485, 482, 481, 171, 364, 396, 164, 411, 418, 846, - /* 420 */ 480, 164, 185, 338, 71, 71, 1247, 1004, 118, 118, - /* 430 */ 118, 118, 117, 117, 116, 116, 116, 115, 428, 121, - /* 440 */ 122, 112, 1169, 1169, 1010, 1013, 1003, 1003, 119, 119, - /* 450 */ 120, 120, 120, 120, 396, 1146, 1147, 1148, 839, 12, - /* 460 */ 318, 513, 163, 360, 1146, 1147, 1148, 114, 111, 211, - /* 470 */ 512, 290, 541, 550, 276, 180, 290, 541, 121, 122, - /* 480 */ 112, 1169, 1169, 1010, 1013, 1003, 1003, 119, 119, 120, - /* 490 */ 120, 120, 120, 349, 488, 71, 71, 118, 118, 118, - /* 500 */ 118, 117, 117, 116, 116, 116, 115, 428, 1146, 209, - /* 510 */ 415, 527, 1146, 1113, 1575, 382, 252, 269, 346, 491, - /* 520 */ 341, 490, 238, 396, 517, 368, 1113, 1131, 337, 1113, - /* 530 */ 191, 413, 286, 32, 461, 447, 118, 118, 118, 118, - /* 540 */ 117, 117, 116, 116, 116, 115, 428, 121, 122, 112, - /* 550 */ 1169, 1169, 1010, 1013, 1003, 1003, 119, 119, 120, 120, - /* 560 */ 120, 120, 396, 1146, 1147, 1148, 991, 1146, 1147, 1148, - /* 570 */ 1146, 233, 496, 1496, 485, 482, 481, 6, 163, 550, - /* 580 */ 516, 550, 115, 428, 480, 5, 121, 122, 112, 1169, - /* 590 */ 1169, 1010, 1013, 1003, 1003, 119, 119, 120, 120, 120, - /* 600 */ 120, 13, 13, 13, 13, 118, 118, 118, 118, 117, - /* 610 */ 117, 116, 116, 116, 115, 428, 407, 506, 412, 550, - /* 620 */ 1490, 548, 1146, 896, 896, 1146, 1147, 1148, 1477, 1146, - /* 630 */ 275, 396, 812, 813, 814, 975, 426, 426, 426, 16, - /* 640 */ 16, 55, 55, 1246, 118, 118, 118, 118, 117, 117, - /* 650 */ 116, 116, 116, 115, 428, 121, 122, 112, 1169, 1169, - /* 660 */ 1010, 1013, 1003, 1003, 119, 119, 120, 120, 120, 120, - /* 670 */ 396, 1193, 1, 1, 556, 2, 1197, 1146, 1147, 1148, - /* 680 */ 194, 291, 902, 136, 1146, 1147, 1148, 901, 525, 1496, - /* 690 */ 1277, 3, 384, 6, 121, 122, 112, 1169, 1169, 1010, - /* 700 */ 1013, 1003, 1003, 119, 119, 120, 120, 120, 120, 862, - /* 710 */ 550, 928, 550, 118, 118, 118, 118, 117, 117, 116, - /* 720 */ 116, 116, 115, 428, 266, 266, 1096, 1573, 1146, 555, - /* 730 */ 1573, 1197, 13, 13, 13, 13, 291, 547, 136, 396, - /* 740 */ 489, 425, 424, 970, 348, 1277, 472, 414, 863, 279, - /* 750 */ 140, 221, 118, 118, 118, 118, 117, 117, 116, 116, - /* 760 */ 116, 115, 428, 121, 122, 112, 1169, 1169, 1010, 1013, - /* 770 */ 1003, 1003, 119, 119, 120, 120, 120, 120, 550, 266, - /* 780 */ 266, 432, 396, 1146, 1147, 1148, 1176, 834, 1176, 472, - /* 790 */ 435, 145, 547, 1150, 405, 318, 443, 304, 842, 1494, - /* 800 */ 71, 71, 416, 6, 1094, 477, 221, 100, 112, 1169, - /* 810 */ 1169, 1010, 1013, 1003, 1003, 119, 119, 120, 120, 120, - /* 820 */ 120, 118, 118, 118, 118, 117, 117, 116, 116, 116, - /* 830 */ 115, 428, 237, 1429, 550, 455, 432, 287, 990, 550, - /* 840 */ 236, 235, 234, 834, 97, 533, 433, 1269, 1269, 1150, - /* 850 */ 498, 311, 434, 842, 981, 550, 71, 71, 980, 1245, - /* 860 */ 550, 51, 51, 300, 118, 118, 118, 118, 117, 117, - /* 870 */ 116, 116, 116, 115, 428, 194, 103, 70, 70, 266, - /* 880 */ 266, 550, 71, 71, 266, 266, 30, 395, 348, 980, - /* 890 */ 980, 982, 547, 532, 1113, 332, 396, 547, 499, 401, - /* 900 */ 1474, 195, 534, 13, 13, 1362, 240, 1113, 277, 280, - /* 910 */ 1113, 280, 308, 461, 310, 337, 396, 31, 188, 423, - /* 920 */ 121, 122, 112, 1169, 1169, 1010, 1013, 1003, 1003, 119, - /* 930 */ 119, 120, 120, 120, 120, 142, 396, 369, 461, 990, - /* 940 */ 121, 122, 112, 1169, 1169, 1010, 1013, 1003, 1003, 119, - /* 950 */ 119, 120, 120, 120, 120, 981, 327, 1146, 330, 980, - /* 960 */ 121, 110, 112, 1169, 1169, 1010, 1013, 1003, 1003, 119, - /* 970 */ 119, 120, 120, 120, 120, 468, 381, 1189, 118, 118, - /* 980 */ 118, 118, 117, 117, 116, 116, 116, 115, 428, 1146, - /* 990 */ 980, 980, 982, 309, 9, 370, 244, 366, 118, 118, - /* 1000 */ 118, 118, 117, 117, 116, 116, 116, 115, 428, 317, - /* 1010 */ 550, 348, 1146, 1147, 1148, 299, 290, 541, 118, 118, - /* 1020 */ 118, 118, 117, 117, 116, 116, 116, 115, 428, 1267, - /* 1030 */ 1267, 1167, 13, 13, 278, 425, 424, 472, 396, 927, - /* 1040 */ 260, 260, 289, 1173, 1146, 1147, 1148, 189, 1175, 266, - /* 1050 */ 266, 472, 394, 547, 1190, 550, 1174, 263, 144, 493, - /* 1060 */ 926, 550, 547, 122, 112, 1169, 1169, 1010, 1013, 1003, - /* 1070 */ 1003, 119, 119, 120, 120, 120, 120, 71, 71, 1146, - /* 1080 */ 1176, 1276, 1176, 13, 13, 902, 1074, 1167, 550, 472, - /* 1090 */ 901, 107, 542, 1495, 4, 1272, 1113, 6, 529, 1053, - /* 1100 */ 12, 1075, 1096, 1574, 316, 459, 1574, 524, 545, 1113, - /* 1110 */ 56, 56, 1113, 1493, 427, 1362, 1076, 6, 349, 285, - /* 1120 */ 118, 118, 118, 118, 117, 117, 116, 116, 116, 115, - /* 1130 */ 428, 429, 1275, 325, 1146, 1147, 1148, 882, 266, 266, - /* 1140 */ 1281, 107, 542, 539, 4, 1492, 293, 883, 1215, 6, - /* 1150 */ 210, 547, 547, 164, 294, 500, 420, 204, 545, 267, - /* 1160 */ 267, 1218, 402, 515, 503, 204, 266, 266, 400, 535, - /* 1170 */ 8, 990, 547, 523, 550, 926, 462, 105, 105, 547, - /* 1180 */ 1094, 429, 266, 266, 106, 421, 429, 552, 551, 266, - /* 1190 */ 266, 980, 522, 539, 1377, 547, 15, 15, 266, 266, - /* 1200 */ 460, 1124, 547, 266, 266, 1074, 1376, 519, 290, 541, - /* 1210 */ 550, 547, 518, 97, 448, 320, 547, 550, 926, 125, - /* 1220 */ 1075, 990, 980, 980, 982, 983, 27, 105, 105, 405, - /* 1230 */ 347, 1515, 44, 44, 106, 1076, 429, 552, 551, 57, - /* 1240 */ 57, 980, 347, 1515, 107, 542, 550, 4, 466, 405, - /* 1250 */ 214, 1124, 463, 297, 381, 1095, 538, 1313, 550, 543, - /* 1260 */ 402, 545, 290, 541, 104, 244, 102, 530, 58, 58, - /* 1270 */ 550, 199, 980, 980, 982, 983, 27, 1520, 1135, 431, - /* 1280 */ 59, 59, 270, 237, 429, 138, 95, 379, 379, 378, - /* 1290 */ 255, 376, 60, 60, 821, 1184, 539, 550, 273, 550, - /* 1300 */ 1167, 1312, 393, 392, 550, 442, 550, 215, 210, 296, - /* 1310 */ 519, 853, 550, 265, 208, 520, 1480, 295, 274, 61, - /* 1320 */ 61, 62, 62, 312, 990, 109, 45, 45, 46, 46, - /* 1330 */ 105, 105, 1190, 926, 47, 47, 345, 106, 550, 429, - /* 1340 */ 552, 551, 1546, 550, 980, 871, 344, 217, 550, 941, - /* 1350 */ 401, 107, 542, 218, 4, 156, 1167, 942, 158, 550, - /* 1360 */ 49, 49, 1166, 550, 268, 50, 50, 550, 545, 1454, - /* 1370 */ 63, 63, 550, 1453, 216, 980, 980, 982, 983, 27, - /* 1380 */ 450, 64, 64, 550, 464, 65, 65, 550, 322, 14, - /* 1390 */ 14, 429, 1309, 550, 66, 66, 1091, 550, 141, 383, - /* 1400 */ 38, 550, 967, 539, 326, 127, 127, 550, 397, 67, - /* 1410 */ 67, 550, 329, 290, 541, 52, 52, 519, 550, 68, - /* 1420 */ 68, 849, 518, 69, 69, 403, 165, 861, 860, 53, - /* 1430 */ 53, 990, 315, 151, 151, 97, 436, 105, 105, 331, - /* 1440 */ 152, 152, 530, 1052, 106, 1052, 429, 552, 551, 1135, - /* 1450 */ 431, 980, 1036, 270, 972, 239, 333, 243, 379, 379, - /* 1460 */ 378, 255, 376, 944, 945, 821, 1300, 550, 220, 550, - /* 1470 */ 107, 542, 550, 4, 550, 1260, 199, 849, 215, 1040, - /* 1480 */ 296, 1534, 980, 980, 982, 983, 27, 545, 295, 76, - /* 1490 */ 76, 54, 54, 984, 72, 72, 128, 128, 868, 869, - /* 1500 */ 107, 542, 550, 4, 1051, 550, 1051, 537, 473, 550, - /* 1510 */ 429, 550, 454, 1244, 550, 243, 550, 545, 217, 550, - /* 1520 */ 456, 197, 539, 243, 73, 73, 156, 129, 129, 158, - /* 1530 */ 340, 130, 130, 126, 126, 1040, 150, 150, 149, 149, - /* 1540 */ 429, 134, 134, 321, 478, 216, 97, 239, 335, 984, - /* 1550 */ 990, 97, 539, 350, 351, 550, 105, 105, 906, 935, - /* 1560 */ 550, 899, 243, 106, 109, 429, 552, 551, 550, 1509, - /* 1570 */ 980, 832, 99, 542, 139, 4, 550, 133, 133, 397, - /* 1580 */ 990, 1321, 131, 131, 290, 541, 105, 105, 1361, 545, - /* 1590 */ 132, 132, 1296, 106, 1307, 429, 552, 551, 75, 75, - /* 1600 */ 980, 980, 980, 982, 983, 27, 550, 436, 900, 1293, - /* 1610 */ 536, 109, 429, 1367, 550, 1225, 1217, 1206, 258, 550, - /* 1620 */ 353, 550, 1205, 11, 539, 1207, 1528, 355, 77, 77, - /* 1630 */ 380, 980, 980, 982, 983, 27, 74, 74, 357, 213, - /* 1640 */ 303, 43, 43, 48, 48, 441, 314, 201, 307, 1354, - /* 1650 */ 319, 359, 990, 458, 483, 1243, 343, 192, 105, 105, - /* 1660 */ 1426, 1425, 193, 540, 205, 106, 1531, 429, 552, 551, - /* 1670 */ 1184, 167, 980, 270, 247, 1473, 1471, 1181, 379, 379, - /* 1680 */ 378, 255, 376, 200, 373, 821, 404, 83, 79, 82, - /* 1690 */ 1431, 452, 177, 124, 530, 1346, 95, 301, 215, 302, - /* 1700 */ 296, 161, 169, 980, 980, 982, 983, 27, 295, 1343, - /* 1710 */ 305, 306, 444, 445, 1351, 172, 35, 173, 174, 175, - /* 1720 */ 476, 223, 387, 385, 36, 451, 465, 1357, 181, 388, - /* 1730 */ 88, 471, 227, 1420, 186, 474, 259, 1442, 217, 229, - /* 1740 */ 230, 324, 328, 390, 492, 231, 156, 1263, 1208, 158, - /* 1750 */ 417, 1254, 90, 853, 1262, 1261, 206, 419, 1514, 511, - /* 1760 */ 1304, 94, 1545, 352, 354, 216, 1305, 1303, 283, 1233, - /* 1770 */ 284, 391, 1232, 1544, 342, 1231, 1543, 356, 245, 1302, - /* 1780 */ 1253, 502, 505, 358, 246, 1500, 1499, 422, 10, 367, - /* 1790 */ 101, 1328, 1327, 514, 1406, 96, 253, 1214, 34, 397, - /* 1800 */ 553, 1141, 254, 365, 290, 541, 256, 257, 554, 1286, - /* 1810 */ 372, 196, 1285, 371, 1203, 1198, 153, 1458, 137, 1459, - /* 1820 */ 154, 1457, 281, 1456, 155, 808, 430, 436, 202, 398, - /* 1830 */ 203, 78, 288, 198, 292, 212, 271, 1050, 135, 1048, - /* 1840 */ 964, 157, 219, 168, 170, 885, 313, 1064, 222, 176, - /* 1850 */ 968, 159, 406, 84, 408, 178, 85, 86, 87, 160, - /* 1860 */ 1067, 224, 1063, 225, 146, 166, 399, 18, 226, 323, - /* 1870 */ 1056, 1178, 470, 243, 182, 228, 183, 37, 823, 475, - /* 1880 */ 344, 232, 479, 487, 184, 89, 19, 851, 336, 20, - /* 1890 */ 339, 484, 91, 282, 162, 147, 864, 92, 494, 93, - /* 1900 */ 1129, 148, 1016, 1099, 39, 501, 1100, 40, 504, 207, - /* 1910 */ 262, 264, 934, 187, 929, 109, 1119, 1115, 1117, 7, - /* 1920 */ 242, 1103, 33, 1123, 21, 526, 22, 23, 24, 1122, - /* 1930 */ 25, 190, 97, 26, 1031, 1017, 1015, 1019, 1073, 1020, - /* 1940 */ 1072, 249, 248, 28, 41, 895, 985, 833, 108, 29, - /* 1950 */ 377, 546, 250, 374, 1137, 1136, 1194, 1194, 251, 1194, - /* 1960 */ 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, - /* 1970 */ 1194, 1194, 1536, 1535, + /* 0 */ 572, 208, 572, 118, 115, 229, 572, 118, 115, 229, + /* 10 */ 572, 1318, 381, 1297, 412, 566, 566, 566, 572, 413, + /* 20 */ 382, 1318, 1280, 41, 41, 41, 41, 208, 1530, 71, + /* 30 */ 71, 975, 423, 41, 41, 495, 303, 279, 303, 976, + /* 40 */ 401, 71, 71, 125, 126, 80, 1221, 1221, 1054, 1057, + /* 50 */ 1044, 1044, 123, 123, 124, 124, 124, 124, 480, 413, + /* 60 */ 1245, 1, 1, 579, 2, 1249, 554, 118, 115, 229, + /* 70 */ 317, 484, 146, 484, 528, 118, 115, 229, 533, 1331, + /* 80 */ 421, 527, 142, 125, 126, 80, 1221, 1221, 1054, 1057, + /* 90 */ 1044, 1044, 123, 123, 124, 124, 124, 124, 118, 115, + /* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120, + /* 110 */ 120, 119, 116, 448, 284, 284, 284, 284, 446, 446, + /* 120 */ 446, 1571, 380, 1573, 1196, 379, 1167, 569, 1167, 569, + /* 130 */ 413, 1571, 541, 259, 226, 448, 101, 145, 453, 316, + /* 140 */ 563, 240, 122, 122, 122, 122, 121, 121, 120, 120, + /* 150 */ 120, 119, 116, 448, 125, 126, 80, 1221, 1221, 1054, + /* 160 */ 1057, 1044, 1044, 123, 123, 124, 124, 124, 124, 142, + /* 170 */ 294, 1196, 343, 452, 120, 120, 120, 119, 116, 448, + /* 180 */ 127, 1196, 1197, 1198, 148, 445, 444, 572, 119, 116, + /* 190 */ 448, 124, 124, 124, 124, 117, 122, 122, 122, 122, + /* 200 */ 121, 121, 120, 120, 120, 119, 116, 448, 458, 113, + /* 210 */ 13, 13, 550, 122, 122, 122, 122, 121, 121, 120, + /* 220 */ 120, 120, 119, 116, 448, 426, 316, 563, 1196, 1197, + /* 230 */ 1198, 149, 1228, 413, 1228, 124, 124, 124, 124, 122, + /* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, + /* 250 */ 448, 469, 346, 1041, 1041, 1055, 1058, 125, 126, 80, + /* 260 */ 1221, 1221, 1054, 1057, 1044, 1044, 123, 123, 124, 124, + /* 270 */ 124, 124, 1283, 526, 222, 1196, 572, 413, 224, 518, + /* 280 */ 175, 82, 83, 122, 122, 122, 122, 121, 121, 120, + /* 290 */ 120, 120, 119, 116, 448, 1011, 16, 16, 1196, 133, + /* 300 */ 133, 125, 126, 80, 1221, 1221, 1054, 1057, 1044, 1044, + /* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122, + /* 320 */ 121, 121, 120, 120, 120, 119, 116, 448, 1045, 550, + /* 330 */ 1196, 377, 1196, 1197, 1198, 252, 1438, 403, 508, 505, + /* 340 */ 504, 111, 564, 570, 4, 930, 930, 437, 503, 344, + /* 350 */ 464, 330, 364, 398, 1241, 1196, 1197, 1198, 567, 572, + /* 360 */ 122, 122, 122, 122, 121, 121, 120, 120, 120, 119, + /* 370 */ 116, 448, 284, 284, 373, 1584, 1611, 445, 444, 154, + /* 380 */ 413, 449, 71, 71, 1290, 569, 1225, 1196, 1197, 1198, + /* 390 */ 85, 1227, 271, 561, 547, 519, 1565, 572, 98, 1226, + /* 400 */ 6, 1282, 476, 142, 125, 126, 80, 1221, 1221, 1054, + /* 410 */ 1057, 1044, 1044, 123, 123, 124, 124, 124, 124, 554, + /* 420 */ 13, 13, 1031, 511, 1228, 1196, 1228, 553, 109, 109, + /* 430 */ 222, 572, 1242, 175, 572, 431, 110, 197, 449, 574, + /* 440 */ 573, 434, 1556, 1021, 325, 555, 1196, 270, 287, 372, + /* 450 */ 514, 367, 513, 257, 71, 71, 547, 71, 71, 363, + /* 460 */ 316, 563, 1617, 122, 122, 122, 122, 121, 121, 120, + /* 470 */ 120, 120, 119, 116, 448, 1021, 1021, 1023, 1024, 27, + /* 480 */ 284, 284, 1196, 1197, 1198, 1162, 572, 1616, 413, 905, + /* 490 */ 190, 554, 360, 569, 554, 941, 537, 521, 1162, 520, + /* 500 */ 417, 1162, 556, 1196, 1197, 1198, 572, 548, 1558, 51, + /* 510 */ 51, 214, 125, 126, 80, 1221, 1221, 1054, 1057, 1044, + /* 520 */ 1044, 123, 123, 124, 124, 124, 124, 1196, 478, 135, + /* 530 */ 135, 413, 284, 284, 1494, 509, 121, 121, 120, 120, + /* 540 */ 120, 119, 116, 448, 1011, 569, 522, 217, 545, 1565, + /* 550 */ 316, 563, 142, 6, 536, 125, 126, 80, 1221, 1221, + /* 560 */ 1054, 1057, 1044, 1044, 123, 123, 124, 124, 124, 124, + /* 570 */ 1559, 122, 122, 122, 122, 121, 121, 120, 120, 120, + /* 580 */ 119, 116, 448, 489, 1196, 1197, 1198, 486, 281, 1271, + /* 590 */ 961, 252, 1196, 377, 508, 505, 504, 1196, 344, 575, + /* 600 */ 1196, 575, 413, 292, 503, 961, 880, 191, 484, 316, + /* 610 */ 563, 388, 290, 384, 122, 122, 122, 122, 121, 121, + /* 620 */ 120, 120, 120, 119, 116, 448, 125, 126, 80, 1221, + /* 630 */ 1221, 1054, 1057, 1044, 1044, 123, 123, 124, 124, 124, + /* 640 */ 124, 413, 398, 1140, 1196, 873, 100, 284, 284, 1196, + /* 650 */ 1197, 1198, 377, 1097, 1196, 1197, 1198, 1196, 1197, 1198, + /* 660 */ 569, 459, 32, 377, 233, 125, 126, 80, 1221, 1221, + /* 670 */ 1054, 1057, 1044, 1044, 123, 123, 124, 124, 124, 124, + /* 680 */ 1437, 963, 572, 228, 962, 122, 122, 122, 122, 121, + /* 690 */ 121, 120, 120, 120, 119, 116, 448, 1162, 228, 1196, + /* 700 */ 157, 1196, 1197, 1198, 1557, 13, 13, 301, 961, 1236, + /* 710 */ 1162, 153, 413, 1162, 377, 1587, 1180, 5, 373, 1584, + /* 720 */ 433, 1242, 3, 961, 122, 122, 122, 122, 121, 121, + /* 730 */ 120, 120, 120, 119, 116, 448, 125, 126, 80, 1221, + /* 740 */ 1221, 1054, 1057, 1044, 1044, 123, 123, 124, 124, 124, + /* 750 */ 124, 413, 208, 571, 1196, 1032, 1196, 1197, 1198, 1196, + /* 760 */ 392, 856, 155, 1556, 286, 406, 1102, 1102, 492, 572, + /* 770 */ 469, 346, 1323, 1323, 1556, 125, 126, 80, 1221, 1221, + /* 780 */ 1054, 1057, 1044, 1044, 123, 123, 124, 124, 124, 124, + /* 790 */ 129, 572, 13, 13, 378, 122, 122, 122, 122, 121, + /* 800 */ 121, 120, 120, 120, 119, 116, 448, 302, 572, 457, + /* 810 */ 532, 1196, 1197, 1198, 13, 13, 1196, 1197, 1198, 1301, + /* 820 */ 467, 1271, 413, 1321, 1321, 1556, 1016, 457, 456, 200, + /* 830 */ 299, 71, 71, 1269, 122, 122, 122, 122, 121, 121, + /* 840 */ 120, 120, 120, 119, 116, 448, 125, 126, 80, 1221, + /* 850 */ 1221, 1054, 1057, 1044, 1044, 123, 123, 124, 124, 124, + /* 860 */ 124, 413, 227, 1077, 1162, 284, 284, 423, 312, 278, + /* 870 */ 278, 285, 285, 1423, 410, 409, 386, 1162, 569, 572, + /* 880 */ 1162, 1200, 569, 1604, 569, 125, 126, 80, 1221, 1221, + /* 890 */ 1054, 1057, 1044, 1044, 123, 123, 124, 124, 124, 124, + /* 900 */ 457, 1486, 13, 13, 1540, 122, 122, 122, 122, 121, + /* 910 */ 121, 120, 120, 120, 119, 116, 448, 201, 572, 358, + /* 920 */ 1590, 579, 2, 1249, 844, 845, 846, 1566, 317, 1216, + /* 930 */ 146, 6, 413, 255, 254, 253, 206, 1331, 9, 1200, + /* 940 */ 262, 71, 71, 428, 122, 122, 122, 122, 121, 121, + /* 950 */ 120, 120, 120, 119, 116, 448, 125, 126, 80, 1221, + /* 960 */ 1221, 1054, 1057, 1044, 1044, 123, 123, 124, 124, 124, + /* 970 */ 124, 572, 284, 284, 572, 1217, 413, 578, 313, 1249, + /* 980 */ 353, 1300, 356, 423, 317, 569, 146, 495, 529, 1647, + /* 990 */ 399, 375, 495, 1331, 70, 70, 1299, 71, 71, 240, + /* 1000 */ 1329, 104, 80, 1221, 1221, 1054, 1057, 1044, 1044, 123, + /* 1010 */ 123, 124, 124, 124, 124, 122, 122, 122, 122, 121, + /* 1020 */ 121, 120, 120, 120, 119, 116, 448, 1118, 284, 284, + /* 1030 */ 432, 452, 1529, 1217, 443, 284, 284, 1493, 1356, 311, + /* 1040 */ 478, 569, 1119, 975, 495, 495, 217, 1267, 569, 1542, + /* 1050 */ 572, 976, 207, 572, 1031, 240, 387, 1120, 523, 122, + /* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, + /* 1070 */ 448, 1022, 107, 71, 71, 1021, 13, 13, 916, 572, + /* 1080 */ 1499, 572, 284, 284, 97, 530, 495, 452, 917, 1330, + /* 1090 */ 1326, 549, 413, 284, 284, 569, 151, 209, 1499, 1501, + /* 1100 */ 262, 454, 55, 55, 56, 56, 569, 1021, 1021, 1023, + /* 1110 */ 447, 336, 413, 531, 12, 295, 125, 126, 80, 1221, + /* 1120 */ 1221, 1054, 1057, 1044, 1044, 123, 123, 124, 124, 124, + /* 1130 */ 124, 351, 413, 868, 1538, 1217, 125, 126, 80, 1221, + /* 1140 */ 1221, 1054, 1057, 1044, 1044, 123, 123, 124, 124, 124, + /* 1150 */ 124, 1141, 1645, 478, 1645, 375, 125, 114, 80, 1221, + /* 1160 */ 1221, 1054, 1057, 1044, 1044, 123, 123, 124, 124, 124, + /* 1170 */ 124, 1499, 333, 478, 335, 122, 122, 122, 122, 121, + /* 1180 */ 121, 120, 120, 120, 119, 116, 448, 203, 1423, 572, + /* 1190 */ 1298, 868, 468, 1217, 440, 122, 122, 122, 122, 121, + /* 1200 */ 121, 120, 120, 120, 119, 116, 448, 557, 1141, 1646, + /* 1210 */ 543, 1646, 15, 15, 896, 122, 122, 122, 122, 121, + /* 1220 */ 121, 120, 120, 120, 119, 116, 448, 572, 298, 542, + /* 1230 */ 1139, 1423, 1563, 1564, 1335, 413, 6, 6, 1173, 1272, + /* 1240 */ 419, 320, 284, 284, 1423, 512, 569, 529, 300, 461, + /* 1250 */ 43, 43, 572, 897, 12, 569, 334, 482, 429, 411, + /* 1260 */ 126, 80, 1221, 1221, 1054, 1057, 1044, 1044, 123, 123, + /* 1270 */ 124, 124, 124, 124, 572, 57, 57, 288, 1196, 1423, + /* 1280 */ 500, 462, 396, 396, 395, 273, 393, 1139, 1562, 853, + /* 1290 */ 1173, 411, 6, 572, 321, 1162, 474, 44, 44, 1561, + /* 1300 */ 1118, 430, 234, 6, 323, 256, 544, 256, 1162, 435, + /* 1310 */ 572, 1162, 322, 17, 491, 1119, 58, 58, 122, 122, + /* 1320 */ 122, 122, 121, 121, 120, 120, 120, 119, 116, 448, + /* 1330 */ 1120, 216, 485, 59, 59, 1196, 1197, 1198, 111, 564, + /* 1340 */ 324, 4, 236, 460, 530, 572, 237, 460, 572, 441, + /* 1350 */ 168, 560, 424, 141, 483, 567, 572, 293, 572, 1099, + /* 1360 */ 572, 293, 572, 1099, 535, 572, 876, 8, 60, 60, + /* 1370 */ 235, 61, 61, 572, 418, 572, 418, 572, 449, 62, + /* 1380 */ 62, 45, 45, 46, 46, 47, 47, 199, 49, 49, + /* 1390 */ 561, 572, 363, 572, 100, 490, 50, 50, 63, 63, + /* 1400 */ 64, 64, 565, 419, 539, 414, 572, 1031, 572, 538, + /* 1410 */ 316, 563, 316, 563, 65, 65, 14, 14, 572, 1031, + /* 1420 */ 572, 516, 936, 876, 1022, 109, 109, 935, 1021, 66, + /* 1430 */ 66, 131, 131, 110, 455, 449, 574, 573, 420, 177, + /* 1440 */ 1021, 132, 132, 67, 67, 572, 471, 572, 936, 475, + /* 1450 */ 1368, 283, 226, 935, 315, 1367, 411, 572, 463, 411, + /* 1460 */ 1021, 1021, 1023, 239, 411, 86, 213, 1354, 52, 52, + /* 1470 */ 68, 68, 1021, 1021, 1023, 1024, 27, 1589, 1184, 451, + /* 1480 */ 69, 69, 288, 97, 108, 1545, 106, 396, 396, 395, + /* 1490 */ 273, 393, 572, 883, 853, 887, 572, 111, 564, 470, + /* 1500 */ 4, 572, 152, 30, 38, 572, 1136, 234, 400, 323, + /* 1510 */ 111, 564, 531, 4, 567, 53, 53, 322, 572, 163, + /* 1520 */ 163, 572, 341, 472, 164, 164, 337, 567, 76, 76, + /* 1530 */ 572, 289, 1518, 572, 31, 1517, 572, 449, 342, 487, + /* 1540 */ 100, 54, 54, 348, 72, 72, 296, 236, 1084, 561, + /* 1550 */ 449, 883, 1364, 134, 134, 168, 73, 73, 141, 161, + /* 1560 */ 161, 1578, 561, 539, 572, 319, 572, 352, 540, 1013, + /* 1570 */ 477, 261, 261, 895, 894, 235, 539, 572, 1031, 572, + /* 1580 */ 479, 538, 261, 371, 109, 109, 525, 136, 136, 130, + /* 1590 */ 130, 1031, 110, 370, 449, 574, 573, 109, 109, 1021, + /* 1600 */ 162, 162, 156, 156, 572, 110, 1084, 449, 574, 573, + /* 1610 */ 414, 355, 1021, 572, 357, 316, 563, 572, 347, 572, + /* 1620 */ 100, 501, 361, 258, 100, 902, 903, 140, 140, 359, + /* 1630 */ 1314, 1021, 1021, 1023, 1024, 27, 139, 139, 366, 455, + /* 1640 */ 137, 137, 138, 138, 1021, 1021, 1023, 1024, 27, 1184, + /* 1650 */ 451, 572, 376, 288, 111, 564, 1025, 4, 396, 396, + /* 1660 */ 395, 273, 393, 572, 1145, 853, 572, 1080, 572, 258, + /* 1670 */ 496, 567, 572, 211, 75, 75, 559, 966, 234, 261, + /* 1680 */ 323, 111, 564, 933, 4, 113, 77, 77, 322, 74, + /* 1690 */ 74, 42, 42, 1377, 449, 48, 48, 1422, 567, 978, + /* 1700 */ 979, 1096, 1095, 1096, 1095, 866, 561, 150, 934, 1350, + /* 1710 */ 113, 1362, 558, 1428, 1025, 1279, 1270, 1258, 236, 1257, + /* 1720 */ 1259, 449, 1597, 1347, 308, 276, 168, 309, 11, 141, + /* 1730 */ 397, 310, 232, 561, 1409, 1031, 339, 291, 329, 219, + /* 1740 */ 340, 109, 109, 940, 297, 1414, 235, 345, 481, 110, + /* 1750 */ 506, 449, 574, 573, 332, 1413, 1021, 404, 1297, 369, + /* 1760 */ 223, 1490, 1031, 1489, 1359, 1360, 1358, 1357, 109, 109, + /* 1770 */ 204, 1600, 1236, 562, 265, 218, 110, 205, 449, 574, + /* 1780 */ 573, 414, 391, 1021, 1537, 179, 316, 563, 1021, 1021, + /* 1790 */ 1023, 1024, 27, 230, 1535, 1233, 79, 564, 85, 4, + /* 1800 */ 422, 215, 552, 81, 84, 188, 1410, 128, 1404, 550, + /* 1810 */ 455, 35, 328, 567, 173, 1021, 1021, 1023, 1024, 27, + /* 1820 */ 181, 1495, 1397, 331, 465, 183, 184, 185, 186, 466, + /* 1830 */ 499, 242, 98, 402, 1416, 1418, 449, 1415, 473, 36, + /* 1840 */ 192, 488, 405, 1506, 246, 91, 494, 196, 561, 1484, + /* 1850 */ 350, 497, 277, 354, 248, 249, 111, 564, 1260, 4, + /* 1860 */ 250, 407, 515, 436, 1317, 1308, 93, 1316, 1315, 887, + /* 1870 */ 1307, 224, 1583, 567, 438, 524, 439, 1031, 263, 264, + /* 1880 */ 442, 1615, 10, 109, 109, 1287, 408, 1614, 1286, 368, + /* 1890 */ 1285, 110, 1613, 449, 574, 573, 449, 306, 1021, 307, + /* 1900 */ 374, 1382, 1569, 1470, 1381, 385, 105, 314, 561, 99, + /* 1910 */ 1568, 534, 34, 576, 1190, 272, 1340, 551, 383, 274, + /* 1920 */ 1339, 210, 389, 390, 275, 577, 1255, 1250, 415, 165, + /* 1930 */ 1021, 1021, 1023, 1024, 27, 147, 1522, 1031, 166, 1523, + /* 1940 */ 416, 1521, 178, 109, 109, 1520, 304, 167, 840, 450, + /* 1950 */ 220, 110, 221, 449, 574, 573, 212, 78, 1021, 318, + /* 1960 */ 231, 1094, 1092, 144, 180, 326, 169, 1216, 241, 182, + /* 1970 */ 919, 338, 238, 1108, 187, 170, 171, 425, 427, 189, + /* 1980 */ 87, 88, 89, 90, 172, 1111, 243, 1107, 244, 158, + /* 1990 */ 1021, 1021, 1023, 1024, 27, 18, 245, 1230, 493, 349, + /* 2000 */ 1100, 261, 247, 193, 194, 37, 370, 855, 498, 251, + /* 2010 */ 195, 510, 92, 19, 174, 362, 502, 20, 507, 885, + /* 2020 */ 365, 898, 94, 305, 159, 95, 517, 96, 1178, 160, + /* 2030 */ 1060, 1147, 39, 1146, 225, 280, 282, 970, 198, 964, + /* 2040 */ 113, 1164, 1168, 260, 1166, 21, 1172, 7, 22, 1152, + /* 2050 */ 33, 23, 24, 25, 1171, 546, 26, 202, 100, 102, + /* 2060 */ 1075, 103, 1061, 1059, 1063, 1117, 1064, 1116, 266, 267, + /* 2070 */ 28, 40, 929, 1026, 867, 112, 29, 568, 394, 143, + /* 2080 */ 1186, 268, 176, 1185, 269, 1246, 1246, 1246, 1246, 1246, + /* 2090 */ 1246, 1246, 1246, 1246, 1246, 1606, 1246, 1246, 1246, 1246, + /* 2100 */ 1605, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 189, 211, 189, 189, 218, 189, 220, 189, 267, 268, - /* 10 */ 269, 189, 210, 189, 228, 189, 267, 268, 269, 19, - /* 20 */ 218, 189, 211, 212, 211, 212, 211, 211, 212, 211, - /* 30 */ 212, 31, 211, 211, 212, 211, 212, 288, 300, 39, - /* 40 */ 21, 189, 304, 43, 44, 45, 46, 47, 48, 49, - /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 225, 19, - /* 60 */ 189, 183, 184, 185, 186, 189, 248, 263, 236, 191, - /* 70 */ 248, 193, 248, 197, 208, 257, 262, 201, 200, 257, - /* 80 */ 200, 257, 81, 43, 44, 45, 46, 47, 48, 49, - /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 189, 80, - /* 100 */ 189, 101, 102, 103, 104, 105, 106, 107, 108, 109, - /* 110 */ 110, 111, 234, 235, 234, 235, 305, 306, 305, 118, - /* 120 */ 307, 305, 306, 297, 298, 247, 86, 247, 88, 19, - /* 130 */ 259, 251, 252, 267, 268, 269, 26, 136, 137, 261, - /* 140 */ 121, 101, 102, 103, 104, 105, 106, 107, 108, 109, - /* 150 */ 110, 111, 59, 43, 44, 45, 46, 47, 48, 49, - /* 160 */ 50, 51, 52, 53, 54, 55, 56, 57, 259, 291, - /* 170 */ 105, 106, 107, 108, 109, 110, 111, 158, 189, 69, - /* 180 */ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, - /* 190 */ 111, 107, 108, 109, 110, 111, 205, 206, 207, 19, - /* 200 */ 19, 54, 55, 56, 57, 58, 29, 114, 115, 116, - /* 210 */ 33, 101, 102, 103, 104, 105, 106, 107, 108, 109, - /* 220 */ 110, 111, 233, 43, 44, 45, 46, 47, 48, 49, - /* 230 */ 50, 51, 52, 53, 54, 55, 56, 57, 19, 126, - /* 240 */ 127, 148, 65, 24, 214, 200, 59, 67, 101, 102, - /* 250 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 22, - /* 260 */ 189, 111, 43, 44, 45, 46, 47, 48, 49, 50, - /* 270 */ 51, 52, 53, 54, 55, 56, 57, 206, 207, 234, - /* 280 */ 235, 101, 102, 103, 104, 105, 106, 107, 108, 109, - /* 290 */ 110, 111, 247, 76, 107, 114, 59, 267, 268, 269, - /* 300 */ 189, 114, 115, 116, 162, 163, 89, 19, 263, 92, - /* 310 */ 189, 23, 54, 55, 56, 57, 189, 206, 207, 22, - /* 320 */ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, - /* 330 */ 111, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 340 */ 52, 53, 54, 55, 56, 57, 19, 189, 277, 59, - /* 350 */ 23, 114, 115, 116, 46, 47, 48, 49, 61, 101, + /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276, + /* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19, + /* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216, + /* 30 */ 217, 31, 193, 216, 217, 193, 228, 213, 230, 39, + /* 40 */ 206, 216, 217, 43, 44, 45, 46, 47, 48, 49, + /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 193, 19, + /* 60 */ 185, 186, 187, 188, 189, 190, 253, 274, 275, 276, + /* 70 */ 195, 193, 197, 193, 261, 274, 275, 276, 253, 204, + /* 80 */ 238, 204, 81, 43, 44, 45, 46, 47, 48, 49, + /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 274, 275, + /* 100 */ 276, 262, 102, 103, 104, 105, 106, 107, 108, 109, + /* 110 */ 110, 111, 112, 113, 239, 240, 239, 240, 210, 211, + /* 120 */ 212, 314, 315, 314, 59, 316, 86, 252, 88, 252, + /* 130 */ 19, 314, 315, 256, 257, 113, 25, 72, 296, 138, + /* 140 */ 139, 266, 102, 103, 104, 105, 106, 107, 108, 109, + /* 150 */ 110, 111, 112, 113, 43, 44, 45, 46, 47, 48, + /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 81, + /* 170 */ 292, 59, 292, 298, 108, 109, 110, 111, 112, 113, + /* 180 */ 69, 116, 117, 118, 72, 106, 107, 193, 111, 112, + /* 190 */ 113, 54, 55, 56, 57, 58, 102, 103, 104, 105, + /* 200 */ 106, 107, 108, 109, 110, 111, 112, 113, 120, 25, + /* 210 */ 216, 217, 145, 102, 103, 104, 105, 106, 107, 108, + /* 220 */ 109, 110, 111, 112, 113, 231, 138, 139, 116, 117, + /* 230 */ 118, 164, 153, 19, 155, 54, 55, 56, 57, 102, + /* 240 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 250 */ 113, 128, 129, 46, 47, 48, 49, 43, 44, 45, + /* 260 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 270 */ 56, 57, 216, 193, 25, 59, 193, 19, 165, 166, + /* 280 */ 193, 67, 24, 102, 103, 104, 105, 106, 107, 108, + /* 290 */ 109, 110, 111, 112, 113, 73, 216, 217, 59, 216, + /* 300 */ 217, 43, 44, 45, 46, 47, 48, 49, 50, 51, + /* 310 */ 52, 53, 54, 55, 56, 57, 102, 103, 104, 105, + /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 121, 145, + /* 330 */ 59, 193, 116, 117, 118, 119, 273, 204, 122, 123, + /* 340 */ 124, 19, 20, 134, 22, 136, 137, 19, 132, 127, + /* 350 */ 128, 129, 24, 22, 23, 116, 117, 118, 36, 193, /* 360 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 370 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - /* 380 */ 53, 54, 55, 56, 57, 125, 126, 127, 277, 101, - /* 390 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 400 */ 59, 189, 189, 276, 114, 115, 116, 117, 73, 59, - /* 410 */ 120, 121, 122, 72, 214, 19, 81, 259, 19, 23, - /* 420 */ 130, 81, 72, 24, 211, 212, 221, 119, 101, 102, - /* 430 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 43, - /* 440 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 450 */ 54, 55, 56, 57, 19, 114, 115, 116, 23, 208, - /* 460 */ 125, 248, 189, 189, 114, 115, 116, 267, 268, 269, - /* 470 */ 189, 136, 137, 189, 262, 22, 136, 137, 43, 44, - /* 480 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - /* 490 */ 55, 56, 57, 189, 95, 211, 212, 101, 102, 103, - /* 500 */ 104, 105, 106, 107, 108, 109, 110, 111, 59, 189, - /* 510 */ 111, 189, 59, 76, 294, 295, 117, 118, 119, 120, - /* 520 */ 121, 122, 123, 19, 87, 189, 89, 23, 129, 92, - /* 530 */ 279, 227, 248, 22, 189, 284, 101, 102, 103, 104, - /* 540 */ 105, 106, 107, 108, 109, 110, 111, 43, 44, 45, - /* 550 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 560 */ 56, 57, 19, 114, 115, 116, 23, 114, 115, 116, - /* 570 */ 59, 117, 299, 300, 120, 121, 122, 304, 189, 189, - /* 580 */ 143, 189, 110, 111, 130, 22, 43, 44, 45, 46, - /* 590 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 600 */ 57, 211, 212, 211, 212, 101, 102, 103, 104, 105, - /* 610 */ 106, 107, 108, 109, 110, 111, 226, 189, 226, 189, - /* 620 */ 298, 132, 59, 134, 135, 114, 115, 116, 189, 59, - /* 630 */ 285, 19, 7, 8, 9, 23, 205, 206, 207, 211, - /* 640 */ 212, 211, 212, 221, 101, 102, 103, 104, 105, 106, - /* 650 */ 107, 108, 109, 110, 111, 43, 44, 45, 46, 47, - /* 660 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 670 */ 19, 181, 182, 183, 184, 185, 186, 114, 115, 116, - /* 680 */ 189, 191, 133, 193, 114, 115, 116, 138, 299, 300, - /* 690 */ 200, 22, 201, 304, 43, 44, 45, 46, 47, 48, - /* 700 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 35, - /* 710 */ 189, 141, 189, 101, 102, 103, 104, 105, 106, 107, - /* 720 */ 108, 109, 110, 111, 234, 235, 22, 23, 59, 184, - /* 730 */ 26, 186, 211, 212, 211, 212, 191, 247, 193, 19, - /* 740 */ 66, 105, 106, 73, 189, 200, 189, 226, 74, 226, - /* 750 */ 22, 261, 101, 102, 103, 104, 105, 106, 107, 108, - /* 760 */ 109, 110, 111, 43, 44, 45, 46, 47, 48, 49, - /* 770 */ 50, 51, 52, 53, 54, 55, 56, 57, 189, 234, - /* 780 */ 235, 291, 19, 114, 115, 116, 150, 59, 152, 189, - /* 790 */ 233, 236, 247, 59, 189, 125, 126, 127, 59, 300, - /* 800 */ 211, 212, 128, 304, 100, 19, 261, 156, 45, 46, - /* 810 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 820 */ 57, 101, 102, 103, 104, 105, 106, 107, 108, 109, - /* 830 */ 110, 111, 46, 233, 189, 189, 291, 248, 99, 189, - /* 840 */ 125, 126, 127, 115, 26, 200, 289, 230, 231, 115, - /* 850 */ 200, 16, 189, 114, 115, 189, 211, 212, 119, 221, - /* 860 */ 189, 211, 212, 258, 101, 102, 103, 104, 105, 106, - /* 870 */ 107, 108, 109, 110, 111, 189, 156, 211, 212, 234, - /* 880 */ 235, 189, 211, 212, 234, 235, 22, 201, 189, 150, - /* 890 */ 151, 152, 247, 248, 76, 16, 19, 247, 248, 113, - /* 900 */ 189, 24, 257, 211, 212, 189, 26, 89, 262, 223, - /* 910 */ 92, 225, 77, 189, 79, 129, 19, 53, 226, 248, - /* 920 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - /* 930 */ 53, 54, 55, 56, 57, 236, 19, 271, 189, 99, - /* 940 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - /* 950 */ 53, 54, 55, 56, 57, 115, 77, 59, 79, 119, - /* 960 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - /* 970 */ 53, 54, 55, 56, 57, 259, 22, 23, 101, 102, - /* 980 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 59, - /* 990 */ 150, 151, 152, 158, 22, 244, 24, 246, 101, 102, - /* 1000 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 285, - /* 1010 */ 189, 189, 114, 115, 116, 200, 136, 137, 101, 102, - /* 1020 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 230, - /* 1030 */ 231, 59, 211, 212, 285, 105, 106, 189, 19, 141, - /* 1040 */ 234, 235, 239, 113, 114, 115, 116, 226, 118, 234, - /* 1050 */ 235, 189, 249, 247, 100, 189, 126, 23, 236, 107, - /* 1060 */ 26, 189, 247, 44, 45, 46, 47, 48, 49, 50, - /* 1070 */ 51, 52, 53, 54, 55, 56, 57, 211, 212, 59, - /* 1080 */ 150, 233, 152, 211, 212, 133, 12, 115, 189, 189, - /* 1090 */ 138, 19, 20, 300, 22, 233, 76, 304, 226, 11, - /* 1100 */ 208, 27, 22, 23, 200, 19, 26, 87, 36, 89, - /* 1110 */ 211, 212, 92, 300, 248, 189, 42, 304, 189, 250, - /* 1120 */ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, - /* 1130 */ 111, 59, 200, 233, 114, 115, 116, 63, 234, 235, - /* 1140 */ 235, 19, 20, 71, 22, 300, 189, 73, 200, 304, - /* 1150 */ 116, 247, 247, 81, 189, 200, 227, 26, 36, 234, - /* 1160 */ 235, 203, 204, 143, 200, 26, 234, 235, 194, 200, - /* 1170 */ 48, 99, 247, 66, 189, 141, 284, 105, 106, 247, - /* 1180 */ 100, 59, 234, 235, 112, 259, 114, 115, 116, 234, - /* 1190 */ 235, 119, 85, 71, 266, 247, 211, 212, 234, 235, - /* 1200 */ 114, 94, 247, 234, 235, 12, 266, 85, 136, 137, - /* 1210 */ 189, 247, 90, 26, 126, 127, 247, 189, 26, 22, - /* 1220 */ 27, 99, 150, 151, 152, 153, 154, 105, 106, 189, - /* 1230 */ 302, 303, 211, 212, 112, 42, 114, 115, 116, 211, - /* 1240 */ 212, 119, 302, 303, 19, 20, 189, 22, 274, 189, - /* 1250 */ 15, 144, 278, 189, 22, 23, 63, 189, 189, 203, - /* 1260 */ 204, 36, 136, 137, 155, 24, 157, 143, 211, 212, - /* 1270 */ 189, 140, 150, 151, 152, 153, 154, 0, 1, 2, - /* 1280 */ 211, 212, 5, 46, 59, 161, 147, 10, 11, 12, - /* 1290 */ 13, 14, 211, 212, 17, 60, 71, 189, 258, 189, - /* 1300 */ 59, 189, 105, 106, 189, 189, 189, 30, 116, 32, - /* 1310 */ 85, 124, 189, 251, 252, 90, 189, 40, 258, 211, - /* 1320 */ 212, 211, 212, 189, 99, 26, 211, 212, 211, 212, - /* 1330 */ 105, 106, 100, 141, 211, 212, 119, 112, 189, 114, - /* 1340 */ 115, 116, 23, 189, 119, 26, 129, 70, 189, 31, - /* 1350 */ 113, 19, 20, 24, 22, 78, 115, 39, 81, 189, - /* 1360 */ 211, 212, 26, 189, 22, 211, 212, 189, 36, 189, - /* 1370 */ 211, 212, 189, 189, 97, 150, 151, 152, 153, 154, - /* 1380 */ 127, 211, 212, 189, 189, 211, 212, 189, 189, 211, - /* 1390 */ 212, 59, 189, 189, 211, 212, 23, 189, 22, 26, - /* 1400 */ 24, 189, 149, 71, 189, 211, 212, 189, 131, 211, - /* 1410 */ 212, 189, 189, 136, 137, 211, 212, 85, 189, 211, - /* 1420 */ 212, 59, 90, 211, 212, 292, 293, 118, 119, 211, - /* 1430 */ 212, 99, 23, 211, 212, 26, 159, 105, 106, 189, - /* 1440 */ 211, 212, 143, 150, 112, 152, 114, 115, 116, 1, - /* 1450 */ 2, 119, 23, 5, 23, 26, 189, 26, 10, 11, - /* 1460 */ 12, 13, 14, 83, 84, 17, 253, 189, 139, 189, - /* 1470 */ 19, 20, 189, 22, 189, 189, 140, 115, 30, 59, - /* 1480 */ 32, 139, 150, 151, 152, 153, 154, 36, 40, 211, - /* 1490 */ 212, 211, 212, 59, 211, 212, 211, 212, 7, 8, - /* 1500 */ 19, 20, 189, 22, 150, 189, 152, 231, 281, 189, - /* 1510 */ 59, 189, 23, 189, 189, 26, 189, 36, 70, 189, - /* 1520 */ 23, 237, 71, 26, 211, 212, 78, 211, 212, 81, - /* 1530 */ 189, 211, 212, 211, 212, 115, 211, 212, 211, 212, - /* 1540 */ 59, 211, 212, 23, 23, 97, 26, 26, 23, 115, - /* 1550 */ 99, 26, 71, 189, 189, 189, 105, 106, 107, 23, - /* 1560 */ 189, 23, 26, 112, 26, 114, 115, 116, 189, 309, - /* 1570 */ 119, 23, 19, 20, 26, 22, 189, 211, 212, 131, - /* 1580 */ 99, 189, 211, 212, 136, 137, 105, 106, 189, 36, - /* 1590 */ 211, 212, 189, 112, 189, 114, 115, 116, 211, 212, - /* 1600 */ 119, 150, 151, 152, 153, 154, 189, 159, 23, 250, - /* 1610 */ 189, 26, 59, 189, 189, 189, 189, 189, 280, 189, - /* 1620 */ 250, 189, 189, 238, 71, 189, 189, 250, 211, 212, - /* 1630 */ 187, 150, 151, 152, 153, 154, 211, 212, 250, 290, - /* 1640 */ 240, 211, 212, 211, 212, 254, 286, 209, 254, 241, - /* 1650 */ 240, 254, 99, 286, 215, 220, 214, 244, 105, 106, - /* 1660 */ 214, 214, 244, 273, 224, 112, 192, 114, 115, 116, - /* 1670 */ 60, 290, 119, 5, 139, 196, 196, 38, 10, 11, - /* 1680 */ 12, 13, 14, 238, 240, 17, 196, 148, 287, 287, - /* 1690 */ 276, 113, 22, 146, 143, 245, 147, 244, 30, 241, - /* 1700 */ 32, 43, 229, 150, 151, 152, 153, 154, 40, 245, - /* 1710 */ 244, 241, 18, 196, 265, 232, 264, 232, 232, 232, - /* 1720 */ 18, 195, 265, 241, 264, 241, 196, 229, 229, 241, - /* 1730 */ 155, 62, 195, 241, 22, 216, 196, 283, 70, 195, - /* 1740 */ 195, 282, 196, 216, 113, 195, 78, 213, 196, 81, - /* 1750 */ 64, 222, 22, 124, 213, 213, 162, 111, 303, 142, - /* 1760 */ 256, 113, 219, 255, 255, 97, 256, 256, 275, 213, - /* 1770 */ 275, 216, 215, 219, 213, 213, 213, 255, 196, 256, - /* 1780 */ 222, 216, 216, 255, 91, 308, 308, 82, 22, 196, - /* 1790 */ 155, 260, 260, 144, 270, 145, 25, 199, 26, 131, - /* 1800 */ 198, 13, 190, 244, 136, 137, 190, 6, 188, 245, - /* 1810 */ 241, 243, 245, 242, 188, 188, 202, 208, 217, 208, - /* 1820 */ 202, 208, 217, 208, 202, 4, 3, 159, 209, 296, - /* 1830 */ 209, 208, 272, 22, 160, 15, 98, 23, 16, 23, - /* 1840 */ 137, 128, 24, 148, 140, 20, 16, 1, 142, 140, - /* 1850 */ 149, 128, 61, 53, 37, 148, 53, 53, 53, 128, - /* 1860 */ 114, 34, 1, 139, 5, 293, 296, 22, 113, 158, - /* 1870 */ 68, 75, 41, 26, 68, 139, 113, 24, 20, 19, - /* 1880 */ 129, 123, 67, 96, 22, 22, 22, 59, 23, 22, - /* 1890 */ 24, 67, 22, 67, 37, 23, 28, 147, 22, 26, - /* 1900 */ 23, 23, 23, 23, 22, 24, 23, 22, 24, 139, - /* 1910 */ 23, 23, 114, 22, 141, 26, 75, 88, 86, 44, - /* 1920 */ 34, 23, 22, 75, 34, 24, 34, 34, 34, 93, - /* 1930 */ 34, 26, 26, 34, 23, 23, 23, 23, 23, 11, - /* 1940 */ 23, 22, 26, 22, 22, 133, 23, 23, 22, 22, - /* 1950 */ 15, 26, 139, 23, 1, 1, 310, 310, 139, 310, - /* 1960 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 1970 */ 310, 310, 139, 139, 310, 310, 310, 310, 310, 310, - /* 1980 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 1990 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2000 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2010 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2020 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2030 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2040 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2050 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2060 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2070 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2080 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2090 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2100 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2110 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2120 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2130 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2140 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - /* 2150 */ 310, 310, 310, 310, 310, + /* 370 */ 112, 113, 239, 240, 311, 312, 215, 106, 107, 241, + /* 380 */ 19, 59, 216, 217, 223, 252, 115, 116, 117, 118, + /* 390 */ 151, 120, 26, 71, 193, 308, 309, 193, 149, 128, + /* 400 */ 313, 216, 269, 81, 43, 44, 45, 46, 47, 48, + /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 253, + /* 420 */ 216, 217, 100, 95, 153, 59, 155, 261, 106, 107, + /* 430 */ 25, 193, 101, 193, 193, 231, 114, 25, 116, 117, + /* 440 */ 118, 113, 304, 121, 193, 204, 59, 119, 120, 121, + /* 450 */ 122, 123, 124, 125, 216, 217, 193, 216, 217, 131, + /* 460 */ 138, 139, 230, 102, 103, 104, 105, 106, 107, 108, + /* 470 */ 109, 110, 111, 112, 113, 153, 154, 155, 156, 157, + /* 480 */ 239, 240, 116, 117, 118, 76, 193, 23, 19, 25, + /* 490 */ 22, 253, 23, 252, 253, 108, 87, 204, 89, 261, + /* 500 */ 198, 92, 261, 116, 117, 118, 193, 306, 307, 216, + /* 510 */ 217, 150, 43, 44, 45, 46, 47, 48, 49, 50, + /* 520 */ 51, 52, 53, 54, 55, 56, 57, 59, 193, 216, + /* 530 */ 217, 19, 239, 240, 283, 23, 106, 107, 108, 109, + /* 540 */ 110, 111, 112, 113, 73, 252, 253, 142, 308, 309, + /* 550 */ 138, 139, 81, 313, 145, 43, 44, 45, 46, 47, + /* 560 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 570 */ 307, 102, 103, 104, 105, 106, 107, 108, 109, 110, + /* 580 */ 111, 112, 113, 281, 116, 117, 118, 285, 23, 193, + /* 590 */ 25, 119, 59, 193, 122, 123, 124, 59, 127, 203, + /* 600 */ 59, 205, 19, 268, 132, 25, 23, 22, 193, 138, + /* 610 */ 139, 249, 204, 251, 102, 103, 104, 105, 106, 107, + /* 620 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 630 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 640 */ 57, 19, 22, 23, 59, 23, 25, 239, 240, 116, + /* 650 */ 117, 118, 193, 11, 116, 117, 118, 116, 117, 118, + /* 660 */ 252, 269, 22, 193, 15, 43, 44, 45, 46, 47, + /* 670 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 680 */ 273, 143, 193, 118, 143, 102, 103, 104, 105, 106, + /* 690 */ 107, 108, 109, 110, 111, 112, 113, 76, 118, 59, + /* 700 */ 241, 116, 117, 118, 304, 216, 217, 292, 143, 60, + /* 710 */ 89, 241, 19, 92, 193, 193, 23, 22, 311, 312, + /* 720 */ 231, 101, 22, 143, 102, 103, 104, 105, 106, 107, + /* 730 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 740 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 750 */ 57, 19, 193, 193, 59, 23, 116, 117, 118, 59, + /* 760 */ 201, 21, 241, 304, 22, 206, 127, 128, 129, 193, + /* 770 */ 128, 129, 235, 236, 304, 43, 44, 45, 46, 47, + /* 780 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 790 */ 22, 193, 216, 217, 193, 102, 103, 104, 105, 106, + /* 800 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 193, + /* 810 */ 193, 116, 117, 118, 216, 217, 116, 117, 118, 226, + /* 820 */ 80, 193, 19, 235, 236, 304, 23, 211, 212, 231, + /* 830 */ 204, 216, 217, 205, 102, 103, 104, 105, 106, 107, + /* 840 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 850 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 860 */ 57, 19, 193, 123, 76, 239, 240, 193, 253, 239, + /* 870 */ 240, 239, 240, 193, 106, 107, 193, 89, 252, 193, + /* 880 */ 92, 59, 252, 141, 252, 43, 44, 45, 46, 47, + /* 890 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 900 */ 284, 161, 216, 217, 193, 102, 103, 104, 105, 106, + /* 910 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 16, + /* 920 */ 187, 188, 189, 190, 7, 8, 9, 309, 195, 25, + /* 930 */ 197, 313, 19, 127, 128, 129, 262, 204, 22, 117, + /* 940 */ 24, 216, 217, 263, 102, 103, 104, 105, 106, 107, + /* 950 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 960 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 970 */ 57, 193, 239, 240, 193, 59, 19, 188, 253, 190, + /* 980 */ 77, 226, 79, 193, 195, 252, 197, 193, 19, 301, + /* 990 */ 302, 193, 193, 204, 216, 217, 226, 216, 217, 266, + /* 1000 */ 204, 159, 45, 46, 47, 48, 49, 50, 51, 52, + /* 1010 */ 53, 54, 55, 56, 57, 102, 103, 104, 105, 106, + /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 12, 239, 240, + /* 1030 */ 232, 298, 238, 117, 253, 239, 240, 238, 259, 260, + /* 1040 */ 193, 252, 27, 31, 193, 193, 142, 204, 252, 193, + /* 1050 */ 193, 39, 262, 193, 100, 266, 278, 42, 204, 102, + /* 1060 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 1070 */ 113, 117, 159, 216, 217, 121, 216, 217, 63, 193, + /* 1080 */ 193, 193, 239, 240, 115, 116, 193, 298, 73, 238, + /* 1090 */ 238, 231, 19, 239, 240, 252, 22, 24, 211, 212, + /* 1100 */ 24, 193, 216, 217, 216, 217, 252, 153, 154, 155, + /* 1110 */ 253, 16, 19, 144, 213, 268, 43, 44, 45, 46, + /* 1120 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1130 */ 57, 238, 19, 59, 193, 59, 43, 44, 45, 46, + /* 1140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1150 */ 57, 22, 23, 193, 25, 193, 43, 44, 45, 46, + /* 1160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1170 */ 57, 284, 77, 193, 79, 102, 103, 104, 105, 106, + /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 286, 193, 193, + /* 1190 */ 193, 117, 291, 117, 232, 102, 103, 104, 105, 106, + /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 204, 22, 23, + /* 1210 */ 66, 25, 216, 217, 35, 102, 103, 104, 105, 106, + /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 193, 268, 85, + /* 1230 */ 101, 193, 309, 309, 240, 19, 313, 313, 94, 208, + /* 1240 */ 209, 193, 239, 240, 193, 66, 252, 19, 268, 244, + /* 1250 */ 216, 217, 193, 74, 213, 252, 161, 19, 263, 254, + /* 1260 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + /* 1270 */ 54, 55, 56, 57, 193, 216, 217, 5, 59, 193, + /* 1280 */ 19, 244, 10, 11, 12, 13, 14, 101, 309, 17, + /* 1290 */ 146, 254, 313, 193, 193, 76, 115, 216, 217, 309, + /* 1300 */ 12, 263, 30, 313, 32, 46, 87, 46, 89, 130, + /* 1310 */ 193, 92, 40, 22, 263, 27, 216, 217, 102, 103, + /* 1320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + /* 1330 */ 42, 150, 291, 216, 217, 116, 117, 118, 19, 20, + /* 1340 */ 193, 22, 70, 260, 116, 193, 24, 264, 193, 263, + /* 1350 */ 78, 63, 61, 81, 116, 36, 193, 260, 193, 29, + /* 1360 */ 193, 264, 193, 33, 145, 193, 59, 48, 216, 217, + /* 1370 */ 98, 216, 217, 193, 115, 193, 115, 193, 59, 216, + /* 1380 */ 217, 216, 217, 216, 217, 216, 217, 255, 216, 217, + /* 1390 */ 71, 193, 131, 193, 25, 65, 216, 217, 216, 217, + /* 1400 */ 216, 217, 208, 209, 85, 133, 193, 100, 193, 90, + /* 1410 */ 138, 139, 138, 139, 216, 217, 216, 217, 193, 100, + /* 1420 */ 193, 108, 135, 116, 117, 106, 107, 140, 121, 216, + /* 1430 */ 217, 216, 217, 114, 162, 116, 117, 118, 299, 300, + /* 1440 */ 121, 216, 217, 216, 217, 193, 244, 193, 135, 244, + /* 1450 */ 193, 256, 257, 140, 244, 193, 254, 193, 193, 254, + /* 1460 */ 153, 154, 155, 141, 254, 149, 150, 258, 216, 217, + /* 1470 */ 216, 217, 153, 154, 155, 156, 157, 0, 1, 2, + /* 1480 */ 216, 217, 5, 115, 158, 193, 160, 10, 11, 12, + /* 1490 */ 13, 14, 193, 59, 17, 126, 193, 19, 20, 129, + /* 1500 */ 22, 193, 22, 22, 24, 193, 23, 30, 25, 32, + /* 1510 */ 19, 20, 144, 22, 36, 216, 217, 40, 193, 216, + /* 1520 */ 217, 193, 152, 129, 216, 217, 193, 36, 216, 217, + /* 1530 */ 193, 99, 193, 193, 53, 193, 193, 59, 23, 193, + /* 1540 */ 25, 216, 217, 193, 216, 217, 152, 70, 59, 71, + /* 1550 */ 59, 117, 193, 216, 217, 78, 216, 217, 81, 216, + /* 1560 */ 217, 318, 71, 85, 193, 133, 193, 193, 90, 23, + /* 1570 */ 23, 25, 25, 120, 121, 98, 85, 193, 100, 193, + /* 1580 */ 23, 90, 25, 121, 106, 107, 19, 216, 217, 216, + /* 1590 */ 217, 100, 114, 131, 116, 117, 118, 106, 107, 121, + /* 1600 */ 216, 217, 216, 217, 193, 114, 117, 116, 117, 118, + /* 1610 */ 133, 193, 121, 193, 193, 138, 139, 193, 23, 193, + /* 1620 */ 25, 23, 23, 25, 25, 7, 8, 216, 217, 193, + /* 1630 */ 193, 153, 154, 155, 156, 157, 216, 217, 193, 162, + /* 1640 */ 216, 217, 216, 217, 153, 154, 155, 156, 157, 1, + /* 1650 */ 2, 193, 193, 5, 19, 20, 59, 22, 10, 11, + /* 1660 */ 12, 13, 14, 193, 97, 17, 193, 23, 193, 25, + /* 1670 */ 288, 36, 193, 242, 216, 217, 236, 23, 30, 25, + /* 1680 */ 32, 19, 20, 23, 22, 25, 216, 217, 40, 216, + /* 1690 */ 217, 216, 217, 193, 59, 216, 217, 193, 36, 83, + /* 1700 */ 84, 153, 153, 155, 155, 23, 71, 25, 23, 193, + /* 1710 */ 25, 193, 193, 193, 117, 193, 193, 193, 70, 193, + /* 1720 */ 193, 59, 193, 255, 255, 287, 78, 255, 243, 81, + /* 1730 */ 191, 255, 297, 71, 271, 100, 293, 245, 267, 214, + /* 1740 */ 246, 106, 107, 108, 246, 271, 98, 245, 293, 114, + /* 1750 */ 220, 116, 117, 118, 267, 271, 121, 271, 225, 219, + /* 1760 */ 229, 219, 100, 219, 259, 259, 259, 259, 106, 107, + /* 1770 */ 249, 196, 60, 280, 141, 243, 114, 249, 116, 117, + /* 1780 */ 118, 133, 245, 121, 200, 297, 138, 139, 153, 154, + /* 1790 */ 155, 156, 157, 297, 200, 38, 19, 20, 151, 22, + /* 1800 */ 200, 150, 140, 294, 294, 22, 272, 148, 250, 145, + /* 1810 */ 162, 270, 249, 36, 43, 153, 154, 155, 156, 157, + /* 1820 */ 234, 283, 250, 249, 18, 237, 237, 237, 237, 200, + /* 1830 */ 18, 199, 149, 246, 272, 234, 59, 272, 246, 270, + /* 1840 */ 234, 200, 246, 290, 199, 158, 62, 22, 71, 246, + /* 1850 */ 289, 221, 200, 200, 199, 199, 19, 20, 200, 22, + /* 1860 */ 199, 221, 115, 64, 218, 227, 22, 218, 218, 126, + /* 1870 */ 227, 165, 312, 36, 24, 305, 113, 100, 200, 91, + /* 1880 */ 82, 224, 22, 106, 107, 218, 221, 224, 220, 218, + /* 1890 */ 218, 114, 218, 116, 117, 118, 59, 282, 121, 282, + /* 1900 */ 221, 265, 317, 277, 265, 200, 158, 279, 71, 147, + /* 1910 */ 317, 146, 25, 202, 13, 194, 250, 140, 249, 194, + /* 1920 */ 250, 248, 247, 246, 6, 192, 192, 192, 303, 207, + /* 1930 */ 153, 154, 155, 156, 157, 222, 213, 100, 207, 213, + /* 1940 */ 303, 213, 300, 106, 107, 213, 222, 207, 4, 3, + /* 1950 */ 214, 114, 214, 116, 117, 118, 22, 213, 121, 163, + /* 1960 */ 15, 23, 23, 16, 151, 139, 130, 25, 144, 142, + /* 1970 */ 20, 16, 24, 1, 142, 130, 130, 61, 37, 151, + /* 1980 */ 53, 53, 53, 53, 130, 116, 34, 1, 141, 5, + /* 1990 */ 153, 154, 155, 156, 157, 22, 115, 75, 41, 161, + /* 2000 */ 68, 25, 141, 68, 115, 24, 131, 20, 19, 125, + /* 2010 */ 22, 96, 22, 22, 37, 23, 67, 22, 67, 59, + /* 2020 */ 24, 28, 22, 67, 23, 149, 22, 25, 23, 23, + /* 2030 */ 23, 23, 22, 97, 141, 23, 23, 116, 22, 143, + /* 2040 */ 25, 88, 75, 34, 86, 34, 75, 44, 34, 23, + /* 2050 */ 22, 34, 34, 34, 93, 24, 34, 25, 25, 142, + /* 2060 */ 23, 142, 23, 23, 23, 23, 11, 23, 25, 22, + /* 2070 */ 22, 22, 135, 23, 23, 22, 22, 25, 15, 23, + /* 2080 */ 1, 141, 25, 1, 141, 319, 319, 319, 319, 319, + /* 2090 */ 319, 319, 319, 319, 319, 141, 319, 319, 319, 319, + /* 2100 */ 141, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2110 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2120 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2130 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2140 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2150 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2160 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2170 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2180 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2190 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2200 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2210 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2220 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2230 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2240 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2250 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2260 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2270 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2280 */ 319, 319, 319, 319, 319, 319, }; -#define YY_SHIFT_COUNT (556) +#define YY_SHIFT_COUNT (579) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (1954) +#define YY_SHIFT_MAX (2082) static const unsigned short int yy_shift_ofst[] = { - /* 0 */ 1448, 1277, 1668, 1072, 1072, 340, 1122, 1225, 1332, 1481, - /* 10 */ 1481, 1481, 335, 0, 0, 180, 897, 1481, 1481, 1481, - /* 20 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, - /* 30 */ 930, 930, 1020, 1020, 290, 1, 340, 340, 340, 340, - /* 40 */ 340, 340, 40, 110, 219, 288, 327, 396, 435, 504, - /* 50 */ 543, 612, 651, 720, 877, 897, 897, 897, 897, 897, - /* 60 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, - /* 70 */ 897, 897, 897, 917, 897, 1019, 763, 763, 1451, 1481, - /* 80 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, - /* 90 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, - /* 100 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, - /* 110 */ 1481, 1481, 1553, 1481, 1481, 1481, 1481, 1481, 1481, 1481, - /* 120 */ 1481, 1481, 1481, 1481, 1481, 1481, 147, 258, 258, 258, - /* 130 */ 258, 258, 79, 65, 84, 449, 19, 786, 449, 636, - /* 140 */ 636, 449, 880, 880, 880, 880, 113, 142, 142, 472, - /* 150 */ 150, 1974, 1974, 399, 399, 399, 93, 237, 341, 237, - /* 160 */ 237, 1074, 1074, 437, 350, 704, 1080, 449, 449, 449, - /* 170 */ 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, - /* 180 */ 449, 449, 449, 449, 449, 449, 449, 449, 818, 818, - /* 190 */ 449, 1088, 217, 217, 734, 734, 1124, 1126, 1974, 1974, - /* 200 */ 1974, 739, 840, 840, 453, 454, 511, 187, 563, 570, - /* 210 */ 898, 669, 449, 449, 449, 449, 449, 449, 449, 449, - /* 220 */ 449, 670, 449, 449, 449, 449, 449, 449, 449, 449, - /* 230 */ 449, 449, 449, 449, 674, 674, 674, 449, 449, 449, - /* 240 */ 449, 1034, 449, 449, 449, 972, 1107, 449, 449, 1193, - /* 250 */ 449, 449, 449, 449, 449, 449, 449, 449, 260, 177, - /* 260 */ 489, 1241, 1241, 1241, 1241, 1192, 489, 489, 952, 1197, - /* 270 */ 625, 1235, 1131, 181, 181, 1086, 1139, 1131, 1086, 1187, - /* 280 */ 1319, 1237, 1318, 1318, 1318, 181, 1299, 1299, 1109, 1336, - /* 290 */ 549, 1376, 1610, 1535, 1535, 1639, 1639, 1535, 1539, 1578, - /* 300 */ 1670, 1547, 1551, 1549, 1658, 1547, 1551, 1549, 1694, 1694, - /* 310 */ 1694, 1694, 1535, 1702, 1549, 1549, 1578, 1670, 1658, 1549, - /* 320 */ 1658, 1549, 1535, 1702, 1575, 1669, 1535, 1702, 1712, 1535, - /* 330 */ 1702, 1535, 1702, 1712, 1631, 1631, 1631, 1686, 1730, 1730, - /* 340 */ 1712, 1631, 1629, 1631, 1686, 1631, 1631, 1594, 1712, 1646, - /* 350 */ 1646, 1712, 1617, 1648, 1617, 1648, 1617, 1648, 1617, 1648, - /* 360 */ 1535, 1693, 1693, 1705, 1705, 1547, 1551, 1766, 1535, 1635, - /* 370 */ 1547, 1650, 1649, 1549, 1771, 1772, 1788, 1788, 1801, 1801, - /* 380 */ 1801, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, - /* 390 */ 1974, 1974, 1974, 1974, 1974, 1974, 308, 835, 954, 1232, - /* 400 */ 879, 715, 728, 1373, 864, 1329, 1253, 1409, 297, 1431, - /* 410 */ 1489, 1497, 1520, 1521, 1525, 1362, 1309, 1491, 1217, 1420, - /* 420 */ 1429, 1536, 1380, 1538, 1293, 1354, 1548, 1585, 1434, 1342, - /* 430 */ 1821, 1823, 1811, 1674, 1820, 1738, 1822, 1814, 1816, 1703, - /* 440 */ 1695, 1713, 1818, 1704, 1825, 1706, 1830, 1846, 1709, 1701, - /* 450 */ 1723, 1791, 1817, 1707, 1800, 1803, 1804, 1805, 1731, 1746, - /* 460 */ 1827, 1724, 1861, 1859, 1845, 1755, 1711, 1802, 1847, 1806, - /* 470 */ 1796, 1831, 1736, 1763, 1853, 1858, 1860, 1751, 1758, 1862, - /* 480 */ 1815, 1863, 1864, 1865, 1867, 1824, 1828, 1866, 1787, 1868, - /* 490 */ 1870, 1826, 1857, 1872, 1750, 1876, 1877, 1878, 1879, 1873, - /* 500 */ 1880, 1882, 1881, 1883, 1885, 1884, 1770, 1887, 1888, 1798, - /* 510 */ 1886, 1891, 1773, 1889, 1890, 1892, 1893, 1894, 1829, 1841, - /* 520 */ 1832, 1875, 1848, 1836, 1896, 1898, 1900, 1901, 1905, 1906, - /* 530 */ 1899, 1911, 1889, 1912, 1913, 1914, 1915, 1916, 1917, 1919, - /* 540 */ 1928, 1921, 1922, 1923, 1924, 1926, 1927, 1925, 1812, 1813, - /* 550 */ 1819, 1833, 1834, 1930, 1935, 1953, 1954, + /* 0 */ 1648, 1477, 1272, 322, 322, 1, 1319, 1478, 1491, 1837, + /* 10 */ 1837, 1837, 471, 0, 0, 214, 1093, 1837, 1837, 1837, + /* 20 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 30 */ 271, 271, 1219, 1219, 216, 88, 1, 1, 1, 1, + /* 40 */ 1, 40, 111, 258, 361, 469, 512, 583, 622, 693, + /* 50 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093, + /* 60 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, + /* 70 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635, 1662, + /* 80 */ 1777, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 90 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 100 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 110 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 120 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 130 */ 137, 181, 181, 181, 181, 181, 181, 181, 94, 430, + /* 140 */ 66, 65, 112, 366, 533, 533, 740, 1261, 533, 533, + /* 150 */ 79, 79, 533, 412, 412, 412, 77, 412, 123, 113, + /* 160 */ 113, 22, 22, 2101, 2101, 328, 328, 328, 239, 468, + /* 170 */ 468, 468, 468, 1015, 1015, 409, 366, 1129, 1186, 533, + /* 180 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, + /* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 969, + /* 200 */ 621, 621, 533, 642, 788, 788, 1228, 1228, 822, 822, + /* 210 */ 67, 1274, 2101, 2101, 2101, 2101, 2101, 2101, 2101, 1307, + /* 220 */ 954, 954, 585, 472, 640, 387, 695, 538, 541, 700, + /* 230 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, + /* 240 */ 222, 533, 533, 533, 533, 533, 533, 533, 533, 533, + /* 250 */ 533, 533, 533, 1179, 1179, 1179, 533, 533, 533, 565, + /* 260 */ 533, 533, 533, 916, 1144, 533, 533, 1288, 533, 533, + /* 270 */ 533, 533, 533, 533, 533, 533, 639, 1330, 209, 1076, + /* 280 */ 1076, 1076, 1076, 580, 209, 209, 1313, 768, 917, 649, + /* 290 */ 1181, 1316, 405, 1316, 1238, 249, 1181, 1181, 249, 1181, + /* 300 */ 405, 1238, 1369, 464, 1259, 1012, 1012, 1012, 1368, 1368, + /* 310 */ 1368, 1368, 184, 184, 1326, 904, 1287, 1480, 1712, 1712, + /* 320 */ 1633, 1633, 1757, 1757, 1633, 1647, 1651, 1783, 1659, 1664, + /* 330 */ 1771, 1659, 1664, 1806, 1806, 1806, 1806, 1633, 1812, 1683, + /* 340 */ 1651, 1651, 1683, 1783, 1771, 1683, 1771, 1683, 1633, 1812, + /* 350 */ 1687, 1784, 1633, 1812, 1825, 1633, 1812, 1633, 1812, 1825, + /* 360 */ 1747, 1747, 1747, 1799, 1844, 1844, 1825, 1747, 1743, 1747, + /* 370 */ 1799, 1747, 1747, 1706, 1850, 1763, 1763, 1825, 1633, 1788, + /* 380 */ 1788, 1798, 1798, 1659, 1664, 1860, 1633, 1748, 1659, 1762, + /* 390 */ 1765, 1683, 1887, 1901, 1901, 1918, 1918, 1918, 2101, 2101, + /* 400 */ 2101, 2101, 2101, 2101, 2101, 2101, 2101, 2101, 2101, 2101, + /* 410 */ 2101, 2101, 2101, 207, 1095, 331, 620, 903, 806, 1074, + /* 420 */ 1483, 1432, 1481, 1322, 1370, 1394, 1515, 1291, 1546, 1547, + /* 430 */ 1557, 1595, 1598, 1599, 1434, 1453, 1618, 1462, 1567, 1489, + /* 440 */ 1644, 1654, 1616, 1660, 1548, 1549, 1682, 1685, 1597, 742, + /* 450 */ 1944, 1946, 1934, 1796, 1945, 1947, 1938, 1939, 1826, 1813, + /* 460 */ 1836, 1942, 1942, 1948, 1827, 1950, 1824, 1955, 1972, 1832, + /* 470 */ 1845, 1942, 1846, 1916, 1941, 1942, 1828, 1927, 1928, 1929, + /* 480 */ 1930, 1854, 1869, 1952, 1847, 1986, 1984, 1973, 1881, 1838, + /* 490 */ 1932, 1976, 1935, 1922, 1957, 1861, 1889, 1981, 1987, 1989, + /* 500 */ 1875, 1884, 1988, 1949, 1990, 1991, 1992, 1995, 1951, 1960, + /* 510 */ 1996, 1915, 1993, 2000, 1956, 1977, 2001, 1876, 2004, 2005, + /* 520 */ 2006, 2007, 2002, 2008, 2010, 1936, 1893, 2012, 2013, 1921, + /* 530 */ 2009, 2016, 1896, 2015, 2011, 2014, 2017, 2018, 1953, 1967, + /* 540 */ 1958, 2003, 1971, 1961, 2019, 2026, 2028, 2031, 2032, 2033, + /* 550 */ 2022, 1917, 1919, 2037, 2015, 2039, 2040, 2041, 2042, 2043, + /* 560 */ 2044, 2047, 2055, 2048, 2049, 2050, 2051, 2053, 2054, 2052, + /* 570 */ 1937, 1940, 1943, 1954, 1959, 2057, 2056, 2063, 2079, 2082, }; -#define YY_REDUCE_COUNT (395) -#define YY_REDUCE_MIN (-262) -#define YY_REDUCE_MAX (1627) +#define YY_REDUCE_COUNT (412) +#define YY_REDUCE_MIN (-271) +#define YY_REDUCE_MAX (1744) static const short yy_reduce_ofst[] = { - /* 0 */ 490, -122, 545, 645, 650, -120, -189, -187, -184, -182, - /* 10 */ -178, -176, 45, 30, 200, -251, -134, 390, 392, 521, - /* 20 */ 523, 213, 692, 821, 284, 589, 872, 666, 671, 866, - /* 30 */ 71, 111, 273, 389, 686, 815, 904, 932, 948, 955, - /* 40 */ 964, 969, -259, -259, -259, -259, -259, -259, -259, -259, - /* 50 */ -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, - /* 60 */ -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, - /* 70 */ -259, -259, -259, -259, -259, -259, -259, -259, 428, 430, - /* 80 */ 899, 985, 1021, 1028, 1057, 1069, 1081, 1108, 1110, 1115, - /* 90 */ 1117, 1123, 1149, 1154, 1159, 1170, 1174, 1178, 1183, 1194, - /* 100 */ 1198, 1204, 1208, 1212, 1218, 1222, 1229, 1278, 1280, 1283, - /* 110 */ 1285, 1313, 1316, 1320, 1322, 1325, 1327, 1330, 1366, 1371, - /* 120 */ 1379, 1387, 1417, 1425, 1430, 1432, -259, -259, -259, -259, - /* 130 */ -259, -259, -259, -259, -259, 557, 974, -214, -174, -9, - /* 140 */ 431, -124, 806, 925, 806, 925, 251, 928, 940, -259, - /* 150 */ -259, -259, -259, -198, -198, -198, 127, -186, -168, 212, - /* 160 */ 646, 617, 799, -262, 555, 220, 220, 491, 605, 1040, - /* 170 */ 1060, 699, -11, 600, 848, 862, 345, -129, 724, -91, - /* 180 */ 158, 749, 716, 900, 304, 822, 929, 926, 499, 793, - /* 190 */ 322, 892, 813, 845, 958, 1056, 751, 905, 1133, 1062, - /* 200 */ 803, -210, -185, -179, -148, -167, -89, 121, 274, 281, - /* 210 */ 320, 336, 439, 663, 711, 957, 965, 1064, 1068, 1112, - /* 220 */ 1116, -196, 1127, 1134, 1180, 1184, 1195, 1199, 1203, 1215, - /* 230 */ 1223, 1250, 1267, 1286, 205, 422, 638, 1324, 1341, 1364, - /* 240 */ 1365, 1213, 1392, 1399, 1403, 869, 1260, 1405, 1421, 1276, - /* 250 */ 1424, 121, 1426, 1427, 1428, 1433, 1436, 1437, 1227, 1338, - /* 260 */ 1284, 1359, 1370, 1377, 1388, 1213, 1284, 1284, 1385, 1438, - /* 270 */ 1443, 1349, 1400, 1391, 1394, 1360, 1408, 1410, 1367, 1439, - /* 280 */ 1440, 1435, 1442, 1446, 1447, 1397, 1413, 1418, 1390, 1444, - /* 290 */ 1445, 1474, 1381, 1479, 1480, 1401, 1402, 1490, 1414, 1449, - /* 300 */ 1452, 1450, 1453, 1458, 1473, 1464, 1466, 1470, 1483, 1485, - /* 310 */ 1486, 1487, 1517, 1526, 1482, 1484, 1457, 1460, 1498, 1488, - /* 320 */ 1499, 1492, 1530, 1537, 1454, 1459, 1540, 1544, 1519, 1546, - /* 330 */ 1545, 1552, 1550, 1527, 1534, 1541, 1542, 1529, 1543, 1554, - /* 340 */ 1555, 1556, 1557, 1561, 1558, 1562, 1563, 1455, 1565, 1493, - /* 350 */ 1495, 1566, 1504, 1508, 1510, 1509, 1511, 1522, 1523, 1528, - /* 360 */ 1582, 1477, 1478, 1531, 1532, 1564, 1559, 1524, 1593, 1560, - /* 370 */ 1567, 1568, 1571, 1569, 1598, 1602, 1612, 1616, 1620, 1626, - /* 380 */ 1627, 1533, 1570, 1572, 1614, 1609, 1611, 1613, 1615, 1618, - /* 390 */ 1601, 1605, 1619, 1621, 1623, 1622, + /* 0 */ -125, 733, 789, 241, 293, -123, -193, -191, -183, -187, + /* 10 */ 166, 238, 133, -207, -199, -267, -176, -6, 204, 489, + /* 20 */ 576, -175, 598, 686, 615, 725, 860, 778, 781, 857, + /* 30 */ 616, 887, 87, 240, -192, 408, 626, 796, 843, 854, + /* 40 */ 1003, -271, -271, -271, -271, -271, -271, -271, -271, -271, + /* 50 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + /* 60 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + /* 70 */ -271, -271, -271, -271, -271, -271, -271, -271, 80, 83, + /* 80 */ 313, 886, 888, 996, 1034, 1059, 1081, 1100, 1117, 1152, + /* 90 */ 1155, 1163, 1165, 1167, 1169, 1172, 1180, 1182, 1184, 1198, + /* 100 */ 1200, 1213, 1215, 1225, 1227, 1252, 1254, 1264, 1299, 1303, + /* 110 */ 1308, 1312, 1325, 1328, 1337, 1340, 1343, 1371, 1373, 1384, + /* 120 */ 1386, 1411, 1420, 1424, 1426, 1458, 1470, 1473, 1475, 1479, + /* 130 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + /* 140 */ -271, 138, 459, 396, -158, 470, 302, -212, 521, 201, + /* 150 */ -195, -92, 559, 630, 632, 630, -271, 632, 901, 63, + /* 160 */ 407, -271, -271, -271, -271, 161, 161, 161, 251, 335, + /* 170 */ 847, 960, 980, 537, 588, 618, 628, 688, 688, -166, + /* 180 */ -161, 674, 790, 794, 799, 851, 852, -122, 680, -120, + /* 190 */ 995, 1038, 415, 1051, 893, 798, 962, 400, 1086, 779, + /* 200 */ 923, 924, 263, 1041, 979, 990, 1083, 1097, 1031, 1194, + /* 210 */ 362, 994, 1139, 1005, 1037, 1202, 1205, 1195, 1210, -194, + /* 220 */ 56, 185, -135, 232, 522, 560, 601, 617, 669, 683, + /* 230 */ 711, 856, 908, 941, 1048, 1101, 1147, 1257, 1262, 1265, + /* 240 */ 392, 1292, 1333, 1339, 1342, 1346, 1350, 1359, 1374, 1418, + /* 250 */ 1421, 1436, 1437, 593, 755, 770, 997, 1445, 1459, 1209, + /* 260 */ 1500, 1504, 1516, 1132, 1243, 1518, 1519, 1440, 1520, 560, + /* 270 */ 1522, 1523, 1524, 1526, 1527, 1529, 1382, 1438, 1431, 1468, + /* 280 */ 1469, 1472, 1476, 1209, 1431, 1431, 1485, 1525, 1539, 1435, + /* 290 */ 1463, 1471, 1492, 1487, 1443, 1494, 1474, 1484, 1498, 1486, + /* 300 */ 1502, 1455, 1530, 1531, 1533, 1540, 1542, 1544, 1505, 1506, + /* 310 */ 1507, 1508, 1521, 1528, 1493, 1537, 1532, 1575, 1488, 1496, + /* 320 */ 1584, 1594, 1509, 1510, 1600, 1538, 1534, 1541, 1558, 1563, + /* 330 */ 1586, 1572, 1574, 1588, 1589, 1590, 1591, 1629, 1632, 1587, + /* 340 */ 1562, 1565, 1592, 1569, 1601, 1596, 1606, 1603, 1641, 1645, + /* 350 */ 1553, 1561, 1652, 1655, 1630, 1653, 1656, 1658, 1661, 1640, + /* 360 */ 1646, 1649, 1650, 1638, 1657, 1663, 1665, 1667, 1668, 1671, + /* 370 */ 1643, 1672, 1674, 1560, 1570, 1615, 1617, 1679, 1678, 1585, + /* 380 */ 1593, 1636, 1639, 1666, 1669, 1626, 1705, 1628, 1670, 1673, + /* 390 */ 1675, 1677, 1711, 1721, 1725, 1733, 1734, 1735, 1625, 1637, + /* 400 */ 1642, 1722, 1723, 1726, 1728, 1732, 1731, 1713, 1724, 1736, + /* 410 */ 1738, 1744, 1740, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 1579, 1579, 1579, 1415, 1192, 1301, 1192, 1192, 1192, 1415, - /* 10 */ 1415, 1415, 1192, 1331, 1331, 1468, 1223, 1192, 1192, 1192, - /* 20 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1414, 1192, 1192, - /* 30 */ 1192, 1192, 1498, 1498, 1192, 1192, 1192, 1192, 1192, 1192, - /* 40 */ 1192, 1192, 1192, 1340, 1192, 1192, 1192, 1192, 1192, 1192, - /* 50 */ 1416, 1417, 1192, 1192, 1192, 1467, 1469, 1432, 1350, 1349, - /* 60 */ 1348, 1347, 1450, 1318, 1345, 1338, 1342, 1410, 1411, 1409, - /* 70 */ 1413, 1417, 1416, 1192, 1341, 1381, 1395, 1380, 1192, 1192, - /* 80 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 90 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 100 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 110 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 120 */ 1192, 1192, 1192, 1192, 1192, 1192, 1389, 1394, 1400, 1393, - /* 130 */ 1390, 1383, 1382, 1384, 1385, 1192, 1213, 1265, 1192, 1192, - /* 140 */ 1192, 1192, 1486, 1485, 1192, 1192, 1223, 1375, 1374, 1386, - /* 150 */ 1387, 1397, 1396, 1475, 1533, 1532, 1433, 1192, 1192, 1192, - /* 160 */ 1192, 1192, 1192, 1498, 1192, 1192, 1192, 1192, 1192, 1192, - /* 170 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 180 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1498, 1498, - /* 190 */ 1192, 1223, 1498, 1498, 1219, 1219, 1325, 1192, 1481, 1301, - /* 200 */ 1292, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 210 */ 1192, 1192, 1192, 1192, 1192, 1472, 1470, 1192, 1192, 1192, - /* 220 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 230 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 240 */ 1192, 1192, 1192, 1192, 1192, 1297, 1192, 1192, 1192, 1192, - /* 250 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1527, 1192, 1445, - /* 260 */ 1279, 1297, 1297, 1297, 1297, 1299, 1280, 1278, 1291, 1224, - /* 270 */ 1199, 1571, 1298, 1320, 1320, 1568, 1344, 1298, 1568, 1240, - /* 280 */ 1549, 1235, 1331, 1331, 1331, 1320, 1325, 1325, 1412, 1298, - /* 290 */ 1291, 1192, 1571, 1306, 1306, 1570, 1570, 1306, 1433, 1353, - /* 300 */ 1359, 1339, 1325, 1344, 1268, 1339, 1325, 1344, 1274, 1274, - /* 310 */ 1274, 1274, 1306, 1210, 1344, 1344, 1353, 1359, 1268, 1344, - /* 320 */ 1268, 1344, 1306, 1210, 1449, 1565, 1306, 1210, 1423, 1306, - /* 330 */ 1210, 1306, 1210, 1423, 1266, 1266, 1266, 1255, 1192, 1192, - /* 340 */ 1423, 1266, 1240, 1266, 1255, 1266, 1266, 1516, 1423, 1427, - /* 350 */ 1427, 1423, 1324, 1319, 1324, 1319, 1324, 1319, 1324, 1319, - /* 360 */ 1306, 1508, 1508, 1334, 1334, 1339, 1325, 1418, 1306, 1192, - /* 370 */ 1339, 1337, 1335, 1344, 1216, 1258, 1530, 1530, 1526, 1526, - /* 380 */ 1526, 1576, 1576, 1481, 1542, 1223, 1223, 1223, 1223, 1542, - /* 390 */ 1242, 1242, 1224, 1224, 1223, 1542, 1192, 1192, 1192, 1192, - /* 400 */ 1192, 1192, 1537, 1192, 1434, 1310, 1192, 1192, 1192, 1192, - /* 410 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 420 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1364, - /* 430 */ 1192, 1195, 1478, 1192, 1192, 1476, 1192, 1192, 1192, 1192, - /* 440 */ 1192, 1192, 1311, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 450 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 460 */ 1192, 1567, 1192, 1192, 1192, 1192, 1192, 1192, 1448, 1447, - /* 470 */ 1192, 1192, 1308, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 480 */ 1192, 1192, 1192, 1192, 1192, 1192, 1238, 1192, 1192, 1192, - /* 490 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 500 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 510 */ 1192, 1192, 1192, 1336, 1192, 1192, 1192, 1192, 1192, 1192, - /* 520 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1513, 1326, - /* 530 */ 1192, 1192, 1558, 1192, 1192, 1192, 1192, 1192, 1192, 1192, - /* 540 */ 1192, 1192, 1192, 1192, 1192, 1192, 1192, 1553, 1282, 1366, - /* 550 */ 1192, 1365, 1369, 1192, 1204, 1192, 1192, + /* 0 */ 1651, 1651, 1651, 1479, 1244, 1355, 1244, 1244, 1244, 1479, + /* 10 */ 1479, 1479, 1244, 1385, 1385, 1532, 1277, 1244, 1244, 1244, + /* 20 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1478, 1244, 1244, + /* 30 */ 1244, 1244, 1567, 1567, 1244, 1244, 1244, 1244, 1244, 1244, + /* 40 */ 1244, 1244, 1394, 1244, 1401, 1244, 1244, 1244, 1244, 1244, + /* 50 */ 1480, 1481, 1244, 1244, 1244, 1531, 1533, 1496, 1408, 1407, + /* 60 */ 1406, 1405, 1514, 1373, 1399, 1392, 1396, 1474, 1475, 1473, + /* 70 */ 1477, 1481, 1480, 1244, 1395, 1442, 1458, 1441, 1244, 1244, + /* 80 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 90 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 100 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 110 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 120 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 130 */ 1450, 1457, 1456, 1455, 1464, 1454, 1451, 1444, 1443, 1445, + /* 140 */ 1446, 1244, 1244, 1268, 1244, 1244, 1265, 1319, 1244, 1244, + /* 150 */ 1244, 1244, 1244, 1551, 1550, 1244, 1447, 1244, 1277, 1436, + /* 160 */ 1435, 1461, 1448, 1460, 1459, 1539, 1603, 1602, 1497, 1244, + /* 170 */ 1244, 1244, 1244, 1244, 1244, 1567, 1244, 1244, 1244, 1244, + /* 180 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 190 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1375, + /* 200 */ 1567, 1567, 1244, 1277, 1567, 1567, 1376, 1376, 1273, 1273, + /* 210 */ 1379, 1244, 1546, 1346, 1346, 1346, 1346, 1355, 1346, 1244, + /* 220 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 230 */ 1244, 1244, 1244, 1244, 1536, 1534, 1244, 1244, 1244, 1244, + /* 240 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 250 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 260 */ 1244, 1244, 1244, 1351, 1244, 1244, 1244, 1244, 1244, 1244, + /* 270 */ 1244, 1244, 1244, 1244, 1244, 1596, 1244, 1509, 1333, 1351, + /* 280 */ 1351, 1351, 1351, 1353, 1334, 1332, 1345, 1278, 1251, 1643, + /* 290 */ 1411, 1400, 1352, 1400, 1640, 1398, 1411, 1411, 1398, 1411, + /* 300 */ 1352, 1640, 1294, 1619, 1289, 1385, 1385, 1385, 1375, 1375, + /* 310 */ 1375, 1375, 1379, 1379, 1476, 1352, 1345, 1244, 1643, 1643, + /* 320 */ 1361, 1361, 1642, 1642, 1361, 1497, 1627, 1420, 1393, 1379, + /* 330 */ 1322, 1393, 1379, 1328, 1328, 1328, 1328, 1361, 1262, 1398, + /* 340 */ 1627, 1627, 1398, 1420, 1322, 1398, 1322, 1398, 1361, 1262, + /* 350 */ 1513, 1637, 1361, 1262, 1487, 1361, 1262, 1361, 1262, 1487, + /* 360 */ 1320, 1320, 1320, 1309, 1244, 1244, 1487, 1320, 1294, 1320, + /* 370 */ 1309, 1320, 1320, 1585, 1244, 1491, 1491, 1487, 1361, 1577, + /* 380 */ 1577, 1388, 1388, 1393, 1379, 1482, 1361, 1244, 1393, 1391, + /* 390 */ 1389, 1398, 1312, 1599, 1599, 1595, 1595, 1595, 1648, 1648, + /* 400 */ 1546, 1612, 1277, 1277, 1277, 1277, 1612, 1296, 1296, 1278, + /* 410 */ 1278, 1277, 1612, 1244, 1244, 1244, 1244, 1244, 1244, 1607, + /* 420 */ 1244, 1541, 1498, 1365, 1244, 1244, 1244, 1244, 1244, 1244, + /* 430 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1552, 1244, + /* 440 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1425, + /* 450 */ 1244, 1247, 1543, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 460 */ 1244, 1402, 1403, 1366, 1244, 1244, 1244, 1244, 1244, 1244, + /* 470 */ 1244, 1417, 1244, 1244, 1244, 1412, 1244, 1244, 1244, 1244, + /* 480 */ 1244, 1244, 1244, 1244, 1639, 1244, 1244, 1244, 1244, 1244, + /* 490 */ 1244, 1512, 1511, 1244, 1244, 1363, 1244, 1244, 1244, 1244, + /* 500 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1292, + /* 510 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 520 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, + /* 530 */ 1244, 1244, 1244, 1390, 1244, 1244, 1244, 1244, 1244, 1244, + /* 540 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1582, 1380, + /* 550 */ 1244, 1244, 1244, 1244, 1630, 1244, 1244, 1244, 1244, 1244, + /* 560 */ 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1623, + /* 570 */ 1336, 1427, 1244, 1426, 1430, 1266, 1244, 1256, 1244, 1244, }; /********** End of lemon-generated parsing tables *****************************/ @@ -160909,8 +170128,8 @@ static const YYCODETYPE yyFallback[] = { 0, /* LP => nothing */ 0, /* RP => nothing */ 0, /* AS => nothing */ - 59, /* WITHOUT => ID */ 0, /* COMMA => nothing */ + 59, /* WITHOUT => ID */ 59, /* ABORT => ID */ 59, /* ACTION => ID */ 59, /* AFTER => ID */ @@ -160981,6 +170200,7 @@ static const YYCODETYPE yyFallback[] = { 59, /* TIES => ID */ 59, /* GENERATED => ID */ 59, /* ALWAYS => ID */ + 59, /* MATERIALIZED => ID */ 59, /* REINDEX => ID */ 59, /* RENAME => ID */ 59, /* CTIME_KW => ID */ @@ -160995,6 +170215,7 @@ static const YYCODETYPE yyFallback[] = { 0, /* SLASH => nothing */ 0, /* REM => nothing */ 0, /* CONCAT => nothing */ + 0, /* PTR => nothing */ 0, /* COLLATE => nothing */ 0, /* BITNOT => nothing */ 0, /* ON => nothing */ @@ -161032,6 +170253,7 @@ static const YYCODETYPE yyFallback[] = { 0, /* HAVING => nothing */ 0, /* LIMIT => nothing */ 0, /* WHERE => nothing */ + 0, /* RETURNING => nothing */ 0, /* INTO => nothing */ 0, /* NOTHING => nothing */ 0, /* FLOAT => nothing */ @@ -161063,6 +170285,7 @@ static const YYCODETYPE yyFallback[] = { 0, /* IF_NULL_ROW => nothing */ 0, /* ASTERISK => nothing */ 0, /* SPAN => nothing */ + 0, /* ERROR => nothing */ 0, /* SPACE => nothing */ 0, /* ILLEGAL => nothing */ }; @@ -161116,9 +170339,9 @@ struct yyParser { }; typedef struct yyParser yyParser; +/* #include */ #ifndef NDEBUG /* #include */ -/* #include */ static FILE *yyTraceFILE = 0; static char *yyTracePrompt = 0; #endif /* NDEBUG */ @@ -161178,8 +170401,8 @@ static const char *const yyTokenName[] = { /* 22 */ "LP", /* 23 */ "RP", /* 24 */ "AS", - /* 25 */ "WITHOUT", - /* 26 */ "COMMA", + /* 25 */ "COMMA", + /* 26 */ "WITHOUT", /* 27 */ "ABORT", /* 28 */ "ACTION", /* 29 */ "AFTER", @@ -161250,219 +170473,228 @@ static const char *const yyTokenName[] = { /* 94 */ "TIES", /* 95 */ "GENERATED", /* 96 */ "ALWAYS", - /* 97 */ "REINDEX", - /* 98 */ "RENAME", - /* 99 */ "CTIME_KW", - /* 100 */ "ANY", - /* 101 */ "BITAND", - /* 102 */ "BITOR", - /* 103 */ "LSHIFT", - /* 104 */ "RSHIFT", - /* 105 */ "PLUS", - /* 106 */ "MINUS", - /* 107 */ "STAR", - /* 108 */ "SLASH", - /* 109 */ "REM", - /* 110 */ "CONCAT", - /* 111 */ "COLLATE", - /* 112 */ "BITNOT", - /* 113 */ "ON", - /* 114 */ "INDEXED", - /* 115 */ "STRING", - /* 116 */ "JOIN_KW", - /* 117 */ "CONSTRAINT", - /* 118 */ "DEFAULT", - /* 119 */ "NULL", - /* 120 */ "PRIMARY", - /* 121 */ "UNIQUE", - /* 122 */ "CHECK", - /* 123 */ "REFERENCES", - /* 124 */ "AUTOINCR", - /* 125 */ "INSERT", - /* 126 */ "DELETE", - /* 127 */ "UPDATE", - /* 128 */ "SET", - /* 129 */ "DEFERRABLE", - /* 130 */ "FOREIGN", - /* 131 */ "DROP", - /* 132 */ "UNION", - /* 133 */ "ALL", - /* 134 */ "EXCEPT", - /* 135 */ "INTERSECT", - /* 136 */ "SELECT", - /* 137 */ "VALUES", - /* 138 */ "DISTINCT", - /* 139 */ "DOT", - /* 140 */ "FROM", - /* 141 */ "JOIN", - /* 142 */ "USING", - /* 143 */ "ORDER", - /* 144 */ "GROUP", - /* 145 */ "HAVING", - /* 146 */ "LIMIT", - /* 147 */ "WHERE", - /* 148 */ "INTO", - /* 149 */ "NOTHING", - /* 150 */ "FLOAT", - /* 151 */ "BLOB", - /* 152 */ "INTEGER", - /* 153 */ "VARIABLE", - /* 154 */ "CASE", - /* 155 */ "WHEN", - /* 156 */ "THEN", - /* 157 */ "ELSE", - /* 158 */ "INDEX", - /* 159 */ "ALTER", - /* 160 */ "ADD", - /* 161 */ "WINDOW", - /* 162 */ "OVER", - /* 163 */ "FILTER", - /* 164 */ "COLUMN", - /* 165 */ "AGG_FUNCTION", - /* 166 */ "AGG_COLUMN", - /* 167 */ "TRUEFALSE", - /* 168 */ "ISNOT", - /* 169 */ "FUNCTION", - /* 170 */ "UMINUS", - /* 171 */ "UPLUS", - /* 172 */ "TRUTH", - /* 173 */ "REGISTER", - /* 174 */ "VECTOR", - /* 175 */ "SELECT_COLUMN", - /* 176 */ "IF_NULL_ROW", - /* 177 */ "ASTERISK", - /* 178 */ "SPAN", - /* 179 */ "SPACE", - /* 180 */ "ILLEGAL", - /* 181 */ "input", - /* 182 */ "cmdlist", - /* 183 */ "ecmd", - /* 184 */ "cmdx", - /* 185 */ "explain", - /* 186 */ "cmd", - /* 187 */ "transtype", - /* 188 */ "trans_opt", - /* 189 */ "nm", - /* 190 */ "savepoint_opt", - /* 191 */ "create_table", - /* 192 */ "create_table_args", - /* 193 */ "createkw", - /* 194 */ "temp", - /* 195 */ "ifnotexists", - /* 196 */ "dbnm", - /* 197 */ "columnlist", - /* 198 */ "conslist_opt", - /* 199 */ "table_options", - /* 200 */ "select", - /* 201 */ "columnname", - /* 202 */ "carglist", - /* 203 */ "typetoken", - /* 204 */ "typename", - /* 205 */ "signed", - /* 206 */ "plus_num", - /* 207 */ "minus_num", - /* 208 */ "scanpt", - /* 209 */ "scantok", - /* 210 */ "ccons", - /* 211 */ "term", - /* 212 */ "expr", - /* 213 */ "onconf", - /* 214 */ "sortorder", - /* 215 */ "autoinc", - /* 216 */ "eidlist_opt", - /* 217 */ "refargs", - /* 218 */ "defer_subclause", - /* 219 */ "generated", - /* 220 */ "refarg", - /* 221 */ "refact", - /* 222 */ "init_deferred_pred_opt", - /* 223 */ "conslist", - /* 224 */ "tconscomma", - /* 225 */ "tcons", - /* 226 */ "sortlist", - /* 227 */ "eidlist", - /* 228 */ "defer_subclause_opt", - /* 229 */ "orconf", - /* 230 */ "resolvetype", - /* 231 */ "raisetype", - /* 232 */ "ifexists", - /* 233 */ "fullname", - /* 234 */ "selectnowith", - /* 235 */ "oneselect", - /* 236 */ "wqlist", - /* 237 */ "multiselect_op", - /* 238 */ "distinct", - /* 239 */ "selcollist", - /* 240 */ "from", - /* 241 */ "where_opt", - /* 242 */ "groupby_opt", - /* 243 */ "having_opt", - /* 244 */ "orderby_opt", - /* 245 */ "limit_opt", - /* 246 */ "window_clause", - /* 247 */ "values", - /* 248 */ "nexprlist", - /* 249 */ "sclp", - /* 250 */ "as", - /* 251 */ "seltablist", - /* 252 */ "stl_prefix", - /* 253 */ "joinop", - /* 254 */ "indexed_opt", - /* 255 */ "on_opt", - /* 256 */ "using_opt", - /* 257 */ "exprlist", - /* 258 */ "xfullname", - /* 259 */ "idlist", - /* 260 */ "nulls", - /* 261 */ "with", - /* 262 */ "setlist", - /* 263 */ "insert_cmd", - /* 264 */ "idlist_opt", - /* 265 */ "upsert", - /* 266 */ "filter_over", - /* 267 */ "likeop", - /* 268 */ "between_op", - /* 269 */ "in_op", - /* 270 */ "paren_exprlist", - /* 271 */ "case_operand", - /* 272 */ "case_exprlist", - /* 273 */ "case_else", - /* 274 */ "uniqueflag", - /* 275 */ "collate", - /* 276 */ "vinto", - /* 277 */ "nmnum", - /* 278 */ "trigger_decl", - /* 279 */ "trigger_cmd_list", - /* 280 */ "trigger_time", - /* 281 */ "trigger_event", - /* 282 */ "foreach_clause", - /* 283 */ "when_clause", - /* 284 */ "trigger_cmd", - /* 285 */ "trnm", - /* 286 */ "tridxby", - /* 287 */ "database_kw_opt", - /* 288 */ "key_opt", - /* 289 */ "add_column_fullname", - /* 290 */ "kwcolumn_opt", - /* 291 */ "create_vtab", - /* 292 */ "vtabarglist", - /* 293 */ "vtabarg", - /* 294 */ "vtabargtoken", - /* 295 */ "lp", - /* 296 */ "anylist", - /* 297 */ "windowdefn_list", - /* 298 */ "windowdefn", - /* 299 */ "window", - /* 300 */ "frame_opt", - /* 301 */ "part_opt", - /* 302 */ "filter_clause", - /* 303 */ "over_clause", - /* 304 */ "range_or_rows", - /* 305 */ "frame_bound", - /* 306 */ "frame_bound_s", - /* 307 */ "frame_bound_e", - /* 308 */ "frame_exclude_opt", - /* 309 */ "frame_exclude", + /* 97 */ "MATERIALIZED", + /* 98 */ "REINDEX", + /* 99 */ "RENAME", + /* 100 */ "CTIME_KW", + /* 101 */ "ANY", + /* 102 */ "BITAND", + /* 103 */ "BITOR", + /* 104 */ "LSHIFT", + /* 105 */ "RSHIFT", + /* 106 */ "PLUS", + /* 107 */ "MINUS", + /* 108 */ "STAR", + /* 109 */ "SLASH", + /* 110 */ "REM", + /* 111 */ "CONCAT", + /* 112 */ "PTR", + /* 113 */ "COLLATE", + /* 114 */ "BITNOT", + /* 115 */ "ON", + /* 116 */ "INDEXED", + /* 117 */ "STRING", + /* 118 */ "JOIN_KW", + /* 119 */ "CONSTRAINT", + /* 120 */ "DEFAULT", + /* 121 */ "NULL", + /* 122 */ "PRIMARY", + /* 123 */ "UNIQUE", + /* 124 */ "CHECK", + /* 125 */ "REFERENCES", + /* 126 */ "AUTOINCR", + /* 127 */ "INSERT", + /* 128 */ "DELETE", + /* 129 */ "UPDATE", + /* 130 */ "SET", + /* 131 */ "DEFERRABLE", + /* 132 */ "FOREIGN", + /* 133 */ "DROP", + /* 134 */ "UNION", + /* 135 */ "ALL", + /* 136 */ "EXCEPT", + /* 137 */ "INTERSECT", + /* 138 */ "SELECT", + /* 139 */ "VALUES", + /* 140 */ "DISTINCT", + /* 141 */ "DOT", + /* 142 */ "FROM", + /* 143 */ "JOIN", + /* 144 */ "USING", + /* 145 */ "ORDER", + /* 146 */ "GROUP", + /* 147 */ "HAVING", + /* 148 */ "LIMIT", + /* 149 */ "WHERE", + /* 150 */ "RETURNING", + /* 151 */ "INTO", + /* 152 */ "NOTHING", + /* 153 */ "FLOAT", + /* 154 */ "BLOB", + /* 155 */ "INTEGER", + /* 156 */ "VARIABLE", + /* 157 */ "CASE", + /* 158 */ "WHEN", + /* 159 */ "THEN", + /* 160 */ "ELSE", + /* 161 */ "INDEX", + /* 162 */ "ALTER", + /* 163 */ "ADD", + /* 164 */ "WINDOW", + /* 165 */ "OVER", + /* 166 */ "FILTER", + /* 167 */ "COLUMN", + /* 168 */ "AGG_FUNCTION", + /* 169 */ "AGG_COLUMN", + /* 170 */ "TRUEFALSE", + /* 171 */ "ISNOT", + /* 172 */ "FUNCTION", + /* 173 */ "UMINUS", + /* 174 */ "UPLUS", + /* 175 */ "TRUTH", + /* 176 */ "REGISTER", + /* 177 */ "VECTOR", + /* 178 */ "SELECT_COLUMN", + /* 179 */ "IF_NULL_ROW", + /* 180 */ "ASTERISK", + /* 181 */ "SPAN", + /* 182 */ "ERROR", + /* 183 */ "SPACE", + /* 184 */ "ILLEGAL", + /* 185 */ "input", + /* 186 */ "cmdlist", + /* 187 */ "ecmd", + /* 188 */ "cmdx", + /* 189 */ "explain", + /* 190 */ "cmd", + /* 191 */ "transtype", + /* 192 */ "trans_opt", + /* 193 */ "nm", + /* 194 */ "savepoint_opt", + /* 195 */ "create_table", + /* 196 */ "create_table_args", + /* 197 */ "createkw", + /* 198 */ "temp", + /* 199 */ "ifnotexists", + /* 200 */ "dbnm", + /* 201 */ "columnlist", + /* 202 */ "conslist_opt", + /* 203 */ "table_option_set", + /* 204 */ "select", + /* 205 */ "table_option", + /* 206 */ "columnname", + /* 207 */ "carglist", + /* 208 */ "typetoken", + /* 209 */ "typename", + /* 210 */ "signed", + /* 211 */ "plus_num", + /* 212 */ "minus_num", + /* 213 */ "scanpt", + /* 214 */ "scantok", + /* 215 */ "ccons", + /* 216 */ "term", + /* 217 */ "expr", + /* 218 */ "onconf", + /* 219 */ "sortorder", + /* 220 */ "autoinc", + /* 221 */ "eidlist_opt", + /* 222 */ "refargs", + /* 223 */ "defer_subclause", + /* 224 */ "generated", + /* 225 */ "refarg", + /* 226 */ "refact", + /* 227 */ "init_deferred_pred_opt", + /* 228 */ "conslist", + /* 229 */ "tconscomma", + /* 230 */ "tcons", + /* 231 */ "sortlist", + /* 232 */ "eidlist", + /* 233 */ "defer_subclause_opt", + /* 234 */ "orconf", + /* 235 */ "resolvetype", + /* 236 */ "raisetype", + /* 237 */ "ifexists", + /* 238 */ "fullname", + /* 239 */ "selectnowith", + /* 240 */ "oneselect", + /* 241 */ "wqlist", + /* 242 */ "multiselect_op", + /* 243 */ "distinct", + /* 244 */ "selcollist", + /* 245 */ "from", + /* 246 */ "where_opt", + /* 247 */ "groupby_opt", + /* 248 */ "having_opt", + /* 249 */ "orderby_opt", + /* 250 */ "limit_opt", + /* 251 */ "window_clause", + /* 252 */ "values", + /* 253 */ "nexprlist", + /* 254 */ "sclp", + /* 255 */ "as", + /* 256 */ "seltablist", + /* 257 */ "stl_prefix", + /* 258 */ "joinop", + /* 259 */ "on_using", + /* 260 */ "indexed_by", + /* 261 */ "exprlist", + /* 262 */ "xfullname", + /* 263 */ "idlist", + /* 264 */ "indexed_opt", + /* 265 */ "nulls", + /* 266 */ "with", + /* 267 */ "where_opt_ret", + /* 268 */ "setlist", + /* 269 */ "insert_cmd", + /* 270 */ "idlist_opt", + /* 271 */ "upsert", + /* 272 */ "returning", + /* 273 */ "filter_over", + /* 274 */ "likeop", + /* 275 */ "between_op", + /* 276 */ "in_op", + /* 277 */ "paren_exprlist", + /* 278 */ "case_operand", + /* 279 */ "case_exprlist", + /* 280 */ "case_else", + /* 281 */ "uniqueflag", + /* 282 */ "collate", + /* 283 */ "vinto", + /* 284 */ "nmnum", + /* 285 */ "trigger_decl", + /* 286 */ "trigger_cmd_list", + /* 287 */ "trigger_time", + /* 288 */ "trigger_event", + /* 289 */ "foreach_clause", + /* 290 */ "when_clause", + /* 291 */ "trigger_cmd", + /* 292 */ "trnm", + /* 293 */ "tridxby", + /* 294 */ "database_kw_opt", + /* 295 */ "key_opt", + /* 296 */ "add_column_fullname", + /* 297 */ "kwcolumn_opt", + /* 298 */ "create_vtab", + /* 299 */ "vtabarglist", + /* 300 */ "vtabarg", + /* 301 */ "vtabargtoken", + /* 302 */ "lp", + /* 303 */ "anylist", + /* 304 */ "wqitem", + /* 305 */ "wqas", + /* 306 */ "windowdefn_list", + /* 307 */ "windowdefn", + /* 308 */ "window", + /* 309 */ "frame_opt", + /* 310 */ "part_opt", + /* 311 */ "filter_clause", + /* 312 */ "over_clause", + /* 313 */ "range_or_rows", + /* 314 */ "frame_bound", + /* 315 */ "frame_bound_s", + /* 316 */ "frame_bound_e", + /* 317 */ "frame_exclude_opt", + /* 318 */ "frame_exclude", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ @@ -161489,372 +170721,392 @@ static const char *const yyRuleName[] = { /* 16 */ "ifnotexists ::= IF NOT EXISTS", /* 17 */ "temp ::= TEMP", /* 18 */ "temp ::=", - /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP table_options", + /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP table_option_set", /* 20 */ "create_table_args ::= AS select", - /* 21 */ "table_options ::=", - /* 22 */ "table_options ::= WITHOUT nm", - /* 23 */ "columnname ::= nm typetoken", - /* 24 */ "typetoken ::=", - /* 25 */ "typetoken ::= typename LP signed RP", - /* 26 */ "typetoken ::= typename LP signed COMMA signed RP", - /* 27 */ "typename ::= typename ID|STRING", - /* 28 */ "scanpt ::=", - /* 29 */ "scantok ::=", - /* 30 */ "ccons ::= CONSTRAINT nm", - /* 31 */ "ccons ::= DEFAULT scantok term", - /* 32 */ "ccons ::= DEFAULT LP expr RP", - /* 33 */ "ccons ::= DEFAULT PLUS scantok term", - /* 34 */ "ccons ::= DEFAULT MINUS scantok term", - /* 35 */ "ccons ::= DEFAULT scantok ID|INDEXED", - /* 36 */ "ccons ::= NOT NULL onconf", - /* 37 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", - /* 38 */ "ccons ::= UNIQUE onconf", - /* 39 */ "ccons ::= CHECK LP expr RP", - /* 40 */ "ccons ::= REFERENCES nm eidlist_opt refargs", - /* 41 */ "ccons ::= defer_subclause", - /* 42 */ "ccons ::= COLLATE ID|STRING", - /* 43 */ "generated ::= LP expr RP", - /* 44 */ "generated ::= LP expr RP ID", - /* 45 */ "autoinc ::=", - /* 46 */ "autoinc ::= AUTOINCR", - /* 47 */ "refargs ::=", - /* 48 */ "refargs ::= refargs refarg", - /* 49 */ "refarg ::= MATCH nm", - /* 50 */ "refarg ::= ON INSERT refact", - /* 51 */ "refarg ::= ON DELETE refact", - /* 52 */ "refarg ::= ON UPDATE refact", - /* 53 */ "refact ::= SET NULL", - /* 54 */ "refact ::= SET DEFAULT", - /* 55 */ "refact ::= CASCADE", - /* 56 */ "refact ::= RESTRICT", - /* 57 */ "refact ::= NO ACTION", - /* 58 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", - /* 59 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", - /* 60 */ "init_deferred_pred_opt ::=", - /* 61 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", - /* 62 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", - /* 63 */ "conslist_opt ::=", - /* 64 */ "tconscomma ::= COMMA", - /* 65 */ "tcons ::= CONSTRAINT nm", - /* 66 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf", - /* 67 */ "tcons ::= UNIQUE LP sortlist RP onconf", - /* 68 */ "tcons ::= CHECK LP expr RP onconf", - /* 69 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt", - /* 70 */ "defer_subclause_opt ::=", - /* 71 */ "onconf ::=", - /* 72 */ "onconf ::= ON CONFLICT resolvetype", - /* 73 */ "orconf ::=", - /* 74 */ "orconf ::= OR resolvetype", - /* 75 */ "resolvetype ::= IGNORE", - /* 76 */ "resolvetype ::= REPLACE", - /* 77 */ "cmd ::= DROP TABLE ifexists fullname", - /* 78 */ "ifexists ::= IF EXISTS", - /* 79 */ "ifexists ::=", - /* 80 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select", - /* 81 */ "cmd ::= DROP VIEW ifexists fullname", - /* 82 */ "cmd ::= select", - /* 83 */ "select ::= WITH wqlist selectnowith", - /* 84 */ "select ::= WITH RECURSIVE wqlist selectnowith", - /* 85 */ "select ::= selectnowith", - /* 86 */ "selectnowith ::= selectnowith multiselect_op oneselect", - /* 87 */ "multiselect_op ::= UNION", - /* 88 */ "multiselect_op ::= UNION ALL", - /* 89 */ "multiselect_op ::= EXCEPT|INTERSECT", - /* 90 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", - /* 91 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", - /* 92 */ "values ::= VALUES LP nexprlist RP", - /* 93 */ "values ::= values COMMA LP nexprlist RP", - /* 94 */ "distinct ::= DISTINCT", - /* 95 */ "distinct ::= ALL", - /* 96 */ "distinct ::=", - /* 97 */ "sclp ::=", - /* 98 */ "selcollist ::= sclp scanpt expr scanpt as", - /* 99 */ "selcollist ::= sclp scanpt STAR", - /* 100 */ "selcollist ::= sclp scanpt nm DOT STAR", - /* 101 */ "as ::= AS nm", - /* 102 */ "as ::=", - /* 103 */ "from ::=", - /* 104 */ "from ::= FROM seltablist", - /* 105 */ "stl_prefix ::= seltablist joinop", - /* 106 */ "stl_prefix ::=", - /* 107 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", - /* 108 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt", - /* 109 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", - /* 110 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", - /* 111 */ "dbnm ::=", - /* 112 */ "dbnm ::= DOT nm", - /* 113 */ "fullname ::= nm", - /* 114 */ "fullname ::= nm DOT nm", - /* 115 */ "xfullname ::= nm", - /* 116 */ "xfullname ::= nm DOT nm", - /* 117 */ "xfullname ::= nm DOT nm AS nm", - /* 118 */ "xfullname ::= nm AS nm", - /* 119 */ "joinop ::= COMMA|JOIN", - /* 120 */ "joinop ::= JOIN_KW JOIN", - /* 121 */ "joinop ::= JOIN_KW nm JOIN", - /* 122 */ "joinop ::= JOIN_KW nm nm JOIN", - /* 123 */ "on_opt ::= ON expr", - /* 124 */ "on_opt ::=", - /* 125 */ "indexed_opt ::=", - /* 126 */ "indexed_opt ::= INDEXED BY nm", - /* 127 */ "indexed_opt ::= NOT INDEXED", - /* 128 */ "using_opt ::= USING LP idlist RP", - /* 129 */ "using_opt ::=", - /* 130 */ "orderby_opt ::=", - /* 131 */ "orderby_opt ::= ORDER BY sortlist", - /* 132 */ "sortlist ::= sortlist COMMA expr sortorder nulls", - /* 133 */ "sortlist ::= expr sortorder nulls", - /* 134 */ "sortorder ::= ASC", - /* 135 */ "sortorder ::= DESC", - /* 136 */ "sortorder ::=", - /* 137 */ "nulls ::= NULLS FIRST", - /* 138 */ "nulls ::= NULLS LAST", - /* 139 */ "nulls ::=", - /* 140 */ "groupby_opt ::=", - /* 141 */ "groupby_opt ::= GROUP BY nexprlist", - /* 142 */ "having_opt ::=", - /* 143 */ "having_opt ::= HAVING expr", - /* 144 */ "limit_opt ::=", - /* 145 */ "limit_opt ::= LIMIT expr", - /* 146 */ "limit_opt ::= LIMIT expr OFFSET expr", - /* 147 */ "limit_opt ::= LIMIT expr COMMA expr", - /* 148 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt orderby_opt limit_opt", - /* 149 */ "where_opt ::=", - /* 150 */ "where_opt ::= WHERE expr", - /* 151 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt orderby_opt limit_opt", - /* 152 */ "setlist ::= setlist COMMA nm EQ expr", - /* 153 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", - /* 154 */ "setlist ::= nm EQ expr", - /* 155 */ "setlist ::= LP idlist RP EQ expr", - /* 156 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", - /* 157 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES", - /* 158 */ "upsert ::=", - /* 159 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt", - /* 160 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING", - /* 161 */ "upsert ::= ON CONFLICT DO NOTHING", - /* 162 */ "insert_cmd ::= INSERT orconf", - /* 163 */ "insert_cmd ::= REPLACE", - /* 164 */ "idlist_opt ::=", - /* 165 */ "idlist_opt ::= LP idlist RP", - /* 166 */ "idlist ::= idlist COMMA nm", - /* 167 */ "idlist ::= nm", - /* 168 */ "expr ::= LP expr RP", - /* 169 */ "expr ::= ID|INDEXED", - /* 170 */ "expr ::= JOIN_KW", - /* 171 */ "expr ::= nm DOT nm", - /* 172 */ "expr ::= nm DOT nm DOT nm", - /* 173 */ "term ::= NULL|FLOAT|BLOB", - /* 174 */ "term ::= STRING", - /* 175 */ "term ::= INTEGER", - /* 176 */ "expr ::= VARIABLE", - /* 177 */ "expr ::= expr COLLATE ID|STRING", - /* 178 */ "expr ::= CAST LP expr AS typetoken RP", - /* 179 */ "expr ::= ID|INDEXED LP distinct exprlist RP", - /* 180 */ "expr ::= ID|INDEXED LP STAR RP", - /* 181 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over", - /* 182 */ "expr ::= ID|INDEXED LP STAR RP filter_over", - /* 183 */ "term ::= CTIME_KW", - /* 184 */ "expr ::= LP nexprlist COMMA expr RP", - /* 185 */ "expr ::= expr AND expr", - /* 186 */ "expr ::= expr OR expr", - /* 187 */ "expr ::= expr LT|GT|GE|LE expr", - /* 188 */ "expr ::= expr EQ|NE expr", - /* 189 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", - /* 190 */ "expr ::= expr PLUS|MINUS expr", - /* 191 */ "expr ::= expr STAR|SLASH|REM expr", - /* 192 */ "expr ::= expr CONCAT expr", - /* 193 */ "likeop ::= NOT LIKE_KW|MATCH", - /* 194 */ "expr ::= expr likeop expr", - /* 195 */ "expr ::= expr likeop expr ESCAPE expr", - /* 196 */ "expr ::= expr ISNULL|NOTNULL", - /* 197 */ "expr ::= expr NOT NULL", - /* 198 */ "expr ::= expr IS expr", - /* 199 */ "expr ::= expr IS NOT expr", - /* 200 */ "expr ::= NOT expr", - /* 201 */ "expr ::= BITNOT expr", - /* 202 */ "expr ::= PLUS|MINUS expr", - /* 203 */ "between_op ::= BETWEEN", - /* 204 */ "between_op ::= NOT BETWEEN", - /* 205 */ "expr ::= expr between_op expr AND expr", - /* 206 */ "in_op ::= IN", - /* 207 */ "in_op ::= NOT IN", - /* 208 */ "expr ::= expr in_op LP exprlist RP", - /* 209 */ "expr ::= LP select RP", - /* 210 */ "expr ::= expr in_op LP select RP", - /* 211 */ "expr ::= expr in_op nm dbnm paren_exprlist", - /* 212 */ "expr ::= EXISTS LP select RP", - /* 213 */ "expr ::= CASE case_operand case_exprlist case_else END", - /* 214 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", - /* 215 */ "case_exprlist ::= WHEN expr THEN expr", - /* 216 */ "case_else ::= ELSE expr", - /* 217 */ "case_else ::=", - /* 218 */ "case_operand ::= expr", - /* 219 */ "case_operand ::=", - /* 220 */ "exprlist ::=", - /* 221 */ "nexprlist ::= nexprlist COMMA expr", - /* 222 */ "nexprlist ::= expr", - /* 223 */ "paren_exprlist ::=", - /* 224 */ "paren_exprlist ::= LP exprlist RP", - /* 225 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", - /* 226 */ "uniqueflag ::= UNIQUE", - /* 227 */ "uniqueflag ::=", - /* 228 */ "eidlist_opt ::=", - /* 229 */ "eidlist_opt ::= LP eidlist RP", - /* 230 */ "eidlist ::= eidlist COMMA nm collate sortorder", - /* 231 */ "eidlist ::= nm collate sortorder", - /* 232 */ "collate ::=", - /* 233 */ "collate ::= COLLATE ID|STRING", - /* 234 */ "cmd ::= DROP INDEX ifexists fullname", - /* 235 */ "cmd ::= VACUUM vinto", - /* 236 */ "cmd ::= VACUUM nm vinto", - /* 237 */ "vinto ::= INTO expr", - /* 238 */ "vinto ::=", - /* 239 */ "cmd ::= PRAGMA nm dbnm", - /* 240 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", - /* 241 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", - /* 242 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 243 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", - /* 244 */ "plus_num ::= PLUS INTEGER|FLOAT", - /* 245 */ "minus_num ::= MINUS INTEGER|FLOAT", - /* 246 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", - /* 247 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", - /* 248 */ "trigger_time ::= BEFORE|AFTER", - /* 249 */ "trigger_time ::= INSTEAD OF", - /* 250 */ "trigger_time ::=", - /* 251 */ "trigger_event ::= DELETE|INSERT", - /* 252 */ "trigger_event ::= UPDATE", - /* 253 */ "trigger_event ::= UPDATE OF idlist", - /* 254 */ "when_clause ::=", - /* 255 */ "when_clause ::= WHEN expr", - /* 256 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", - /* 257 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 258 */ "trnm ::= nm DOT nm", - /* 259 */ "tridxby ::= INDEXED BY nm", - /* 260 */ "tridxby ::= NOT INDEXED", - /* 261 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", - /* 262 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", - /* 263 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", - /* 264 */ "trigger_cmd ::= scanpt select scanpt", - /* 265 */ "expr ::= RAISE LP IGNORE RP", - /* 266 */ "expr ::= RAISE LP raisetype COMMA nm RP", - /* 267 */ "raisetype ::= ROLLBACK", - /* 268 */ "raisetype ::= ABORT", - /* 269 */ "raisetype ::= FAIL", - /* 270 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 271 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 272 */ "cmd ::= DETACH database_kw_opt expr", - /* 273 */ "key_opt ::=", - /* 274 */ "key_opt ::= KEY expr", - /* 275 */ "cmd ::= REINDEX", - /* 276 */ "cmd ::= REINDEX nm dbnm", - /* 277 */ "cmd ::= ANALYZE", - /* 278 */ "cmd ::= ANALYZE nm dbnm", - /* 279 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 280 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", - /* 281 */ "add_column_fullname ::= fullname", - /* 282 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", - /* 283 */ "cmd ::= create_vtab", - /* 284 */ "cmd ::= create_vtab LP vtabarglist RP", - /* 285 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", - /* 286 */ "vtabarg ::=", - /* 287 */ "vtabargtoken ::= ANY", - /* 288 */ "vtabargtoken ::= lp anylist RP", - /* 289 */ "lp ::= LP", - /* 290 */ "with ::= WITH wqlist", - /* 291 */ "with ::= WITH RECURSIVE wqlist", - /* 292 */ "wqlist ::= nm eidlist_opt AS LP select RP", - /* 293 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP", - /* 294 */ "windowdefn_list ::= windowdefn", - /* 295 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", - /* 296 */ "windowdefn ::= nm AS LP window RP", - /* 297 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", - /* 298 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", - /* 299 */ "window ::= ORDER BY sortlist frame_opt", - /* 300 */ "window ::= nm ORDER BY sortlist frame_opt", - /* 301 */ "window ::= frame_opt", - /* 302 */ "window ::= nm frame_opt", - /* 303 */ "frame_opt ::=", - /* 304 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", - /* 305 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", - /* 306 */ "range_or_rows ::= RANGE|ROWS|GROUPS", - /* 307 */ "frame_bound_s ::= frame_bound", - /* 308 */ "frame_bound_s ::= UNBOUNDED PRECEDING", - /* 309 */ "frame_bound_e ::= frame_bound", - /* 310 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", - /* 311 */ "frame_bound ::= expr PRECEDING|FOLLOWING", - /* 312 */ "frame_bound ::= CURRENT ROW", - /* 313 */ "frame_exclude_opt ::=", - /* 314 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", - /* 315 */ "frame_exclude ::= NO OTHERS", - /* 316 */ "frame_exclude ::= CURRENT ROW", - /* 317 */ "frame_exclude ::= GROUP|TIES", - /* 318 */ "window_clause ::= WINDOW windowdefn_list", - /* 319 */ "filter_over ::= filter_clause over_clause", - /* 320 */ "filter_over ::= over_clause", - /* 321 */ "filter_over ::= filter_clause", - /* 322 */ "over_clause ::= OVER LP window RP", - /* 323 */ "over_clause ::= OVER nm", - /* 324 */ "filter_clause ::= FILTER LP WHERE expr RP", - /* 325 */ "input ::= cmdlist", - /* 326 */ "cmdlist ::= cmdlist ecmd", - /* 327 */ "cmdlist ::= ecmd", - /* 328 */ "ecmd ::= SEMI", - /* 329 */ "ecmd ::= cmdx SEMI", - /* 330 */ "ecmd ::= explain cmdx SEMI", - /* 331 */ "trans_opt ::=", - /* 332 */ "trans_opt ::= TRANSACTION", - /* 333 */ "trans_opt ::= TRANSACTION nm", - /* 334 */ "savepoint_opt ::= SAVEPOINT", - /* 335 */ "savepoint_opt ::=", - /* 336 */ "cmd ::= create_table create_table_args", - /* 337 */ "columnlist ::= columnlist COMMA columnname carglist", - /* 338 */ "columnlist ::= columnname carglist", - /* 339 */ "nm ::= ID|INDEXED", - /* 340 */ "nm ::= STRING", - /* 341 */ "nm ::= JOIN_KW", - /* 342 */ "typetoken ::= typename", - /* 343 */ "typename ::= ID|STRING", - /* 344 */ "signed ::= plus_num", - /* 345 */ "signed ::= minus_num", - /* 346 */ "carglist ::= carglist ccons", - /* 347 */ "carglist ::=", - /* 348 */ "ccons ::= NULL onconf", - /* 349 */ "ccons ::= GENERATED ALWAYS AS generated", - /* 350 */ "ccons ::= AS generated", - /* 351 */ "conslist_opt ::= COMMA conslist", - /* 352 */ "conslist ::= conslist tconscomma tcons", - /* 353 */ "conslist ::= tcons", - /* 354 */ "tconscomma ::=", - /* 355 */ "defer_subclause_opt ::= defer_subclause", - /* 356 */ "resolvetype ::= raisetype", - /* 357 */ "selectnowith ::= oneselect", - /* 358 */ "oneselect ::= values", - /* 359 */ "sclp ::= selcollist COMMA", - /* 360 */ "as ::= ID|STRING", - /* 361 */ "expr ::= term", - /* 362 */ "likeop ::= LIKE_KW|MATCH", - /* 363 */ "exprlist ::= nexprlist", - /* 364 */ "nmnum ::= plus_num", - /* 365 */ "nmnum ::= nm", - /* 366 */ "nmnum ::= ON", - /* 367 */ "nmnum ::= DELETE", - /* 368 */ "nmnum ::= DEFAULT", - /* 369 */ "plus_num ::= INTEGER|FLOAT", - /* 370 */ "foreach_clause ::=", - /* 371 */ "foreach_clause ::= FOR EACH ROW", - /* 372 */ "trnm ::= nm", - /* 373 */ "tridxby ::=", - /* 374 */ "database_kw_opt ::= DATABASE", - /* 375 */ "database_kw_opt ::=", - /* 376 */ "kwcolumn_opt ::=", - /* 377 */ "kwcolumn_opt ::= COLUMNKW", - /* 378 */ "vtabarglist ::= vtabarg", - /* 379 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 380 */ "vtabarg ::= vtabarg vtabargtoken", - /* 381 */ "anylist ::=", - /* 382 */ "anylist ::= anylist LP anylist RP", - /* 383 */ "anylist ::= anylist ANY", - /* 384 */ "with ::=", + /* 21 */ "table_option_set ::=", + /* 22 */ "table_option_set ::= table_option_set COMMA table_option", + /* 23 */ "table_option ::= WITHOUT nm", + /* 24 */ "table_option ::= nm", + /* 25 */ "columnname ::= nm typetoken", + /* 26 */ "typetoken ::=", + /* 27 */ "typetoken ::= typename LP signed RP", + /* 28 */ "typetoken ::= typename LP signed COMMA signed RP", + /* 29 */ "typename ::= typename ID|STRING", + /* 30 */ "scanpt ::=", + /* 31 */ "scantok ::=", + /* 32 */ "ccons ::= CONSTRAINT nm", + /* 33 */ "ccons ::= DEFAULT scantok term", + /* 34 */ "ccons ::= DEFAULT LP expr RP", + /* 35 */ "ccons ::= DEFAULT PLUS scantok term", + /* 36 */ "ccons ::= DEFAULT MINUS scantok term", + /* 37 */ "ccons ::= DEFAULT scantok ID|INDEXED", + /* 38 */ "ccons ::= NOT NULL onconf", + /* 39 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", + /* 40 */ "ccons ::= UNIQUE onconf", + /* 41 */ "ccons ::= CHECK LP expr RP", + /* 42 */ "ccons ::= REFERENCES nm eidlist_opt refargs", + /* 43 */ "ccons ::= defer_subclause", + /* 44 */ "ccons ::= COLLATE ID|STRING", + /* 45 */ "generated ::= LP expr RP", + /* 46 */ "generated ::= LP expr RP ID", + /* 47 */ "autoinc ::=", + /* 48 */ "autoinc ::= AUTOINCR", + /* 49 */ "refargs ::=", + /* 50 */ "refargs ::= refargs refarg", + /* 51 */ "refarg ::= MATCH nm", + /* 52 */ "refarg ::= ON INSERT refact", + /* 53 */ "refarg ::= ON DELETE refact", + /* 54 */ "refarg ::= ON UPDATE refact", + /* 55 */ "refact ::= SET NULL", + /* 56 */ "refact ::= SET DEFAULT", + /* 57 */ "refact ::= CASCADE", + /* 58 */ "refact ::= RESTRICT", + /* 59 */ "refact ::= NO ACTION", + /* 60 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", + /* 61 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", + /* 62 */ "init_deferred_pred_opt ::=", + /* 63 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", + /* 64 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", + /* 65 */ "conslist_opt ::=", + /* 66 */ "tconscomma ::= COMMA", + /* 67 */ "tcons ::= CONSTRAINT nm", + /* 68 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf", + /* 69 */ "tcons ::= UNIQUE LP sortlist RP onconf", + /* 70 */ "tcons ::= CHECK LP expr RP onconf", + /* 71 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt", + /* 72 */ "defer_subclause_opt ::=", + /* 73 */ "onconf ::=", + /* 74 */ "onconf ::= ON CONFLICT resolvetype", + /* 75 */ "orconf ::=", + /* 76 */ "orconf ::= OR resolvetype", + /* 77 */ "resolvetype ::= IGNORE", + /* 78 */ "resolvetype ::= REPLACE", + /* 79 */ "cmd ::= DROP TABLE ifexists fullname", + /* 80 */ "ifexists ::= IF EXISTS", + /* 81 */ "ifexists ::=", + /* 82 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select", + /* 83 */ "cmd ::= DROP VIEW ifexists fullname", + /* 84 */ "cmd ::= select", + /* 85 */ "select ::= WITH wqlist selectnowith", + /* 86 */ "select ::= WITH RECURSIVE wqlist selectnowith", + /* 87 */ "select ::= selectnowith", + /* 88 */ "selectnowith ::= selectnowith multiselect_op oneselect", + /* 89 */ "multiselect_op ::= UNION", + /* 90 */ "multiselect_op ::= UNION ALL", + /* 91 */ "multiselect_op ::= EXCEPT|INTERSECT", + /* 92 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", + /* 93 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", + /* 94 */ "values ::= VALUES LP nexprlist RP", + /* 95 */ "values ::= values COMMA LP nexprlist RP", + /* 96 */ "distinct ::= DISTINCT", + /* 97 */ "distinct ::= ALL", + /* 98 */ "distinct ::=", + /* 99 */ "sclp ::=", + /* 100 */ "selcollist ::= sclp scanpt expr scanpt as", + /* 101 */ "selcollist ::= sclp scanpt STAR", + /* 102 */ "selcollist ::= sclp scanpt nm DOT STAR", + /* 103 */ "as ::= AS nm", + /* 104 */ "as ::=", + /* 105 */ "from ::=", + /* 106 */ "from ::= FROM seltablist", + /* 107 */ "stl_prefix ::= seltablist joinop", + /* 108 */ "stl_prefix ::=", + /* 109 */ "seltablist ::= stl_prefix nm dbnm as on_using", + /* 110 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using", + /* 111 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using", + /* 112 */ "seltablist ::= stl_prefix LP select RP as on_using", + /* 113 */ "seltablist ::= stl_prefix LP seltablist RP as on_using", + /* 114 */ "dbnm ::=", + /* 115 */ "dbnm ::= DOT nm", + /* 116 */ "fullname ::= nm", + /* 117 */ "fullname ::= nm DOT nm", + /* 118 */ "xfullname ::= nm", + /* 119 */ "xfullname ::= nm DOT nm", + /* 120 */ "xfullname ::= nm DOT nm AS nm", + /* 121 */ "xfullname ::= nm AS nm", + /* 122 */ "joinop ::= COMMA|JOIN", + /* 123 */ "joinop ::= JOIN_KW JOIN", + /* 124 */ "joinop ::= JOIN_KW nm JOIN", + /* 125 */ "joinop ::= JOIN_KW nm nm JOIN", + /* 126 */ "on_using ::= ON expr", + /* 127 */ "on_using ::= USING LP idlist RP", + /* 128 */ "on_using ::=", + /* 129 */ "indexed_opt ::=", + /* 130 */ "indexed_by ::= INDEXED BY nm", + /* 131 */ "indexed_by ::= NOT INDEXED", + /* 132 */ "orderby_opt ::=", + /* 133 */ "orderby_opt ::= ORDER BY sortlist", + /* 134 */ "sortlist ::= sortlist COMMA expr sortorder nulls", + /* 135 */ "sortlist ::= expr sortorder nulls", + /* 136 */ "sortorder ::= ASC", + /* 137 */ "sortorder ::= DESC", + /* 138 */ "sortorder ::=", + /* 139 */ "nulls ::= NULLS FIRST", + /* 140 */ "nulls ::= NULLS LAST", + /* 141 */ "nulls ::=", + /* 142 */ "groupby_opt ::=", + /* 143 */ "groupby_opt ::= GROUP BY nexprlist", + /* 144 */ "having_opt ::=", + /* 145 */ "having_opt ::= HAVING expr", + /* 146 */ "limit_opt ::=", + /* 147 */ "limit_opt ::= LIMIT expr", + /* 148 */ "limit_opt ::= LIMIT expr OFFSET expr", + /* 149 */ "limit_opt ::= LIMIT expr COMMA expr", + /* 150 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret orderby_opt limit_opt", + /* 151 */ "where_opt ::=", + /* 152 */ "where_opt ::= WHERE expr", + /* 153 */ "where_opt_ret ::=", + /* 154 */ "where_opt_ret ::= WHERE expr", + /* 155 */ "where_opt_ret ::= RETURNING selcollist", + /* 156 */ "where_opt_ret ::= WHERE expr RETURNING selcollist", + /* 157 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret orderby_opt limit_opt", + /* 158 */ "setlist ::= setlist COMMA nm EQ expr", + /* 159 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", + /* 160 */ "setlist ::= nm EQ expr", + /* 161 */ "setlist ::= LP idlist RP EQ expr", + /* 162 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", + /* 163 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning", + /* 164 */ "upsert ::=", + /* 165 */ "upsert ::= RETURNING selcollist", + /* 166 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert", + /* 167 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert", + /* 168 */ "upsert ::= ON CONFLICT DO NOTHING returning", + /* 169 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning", + /* 170 */ "returning ::= RETURNING selcollist", + /* 171 */ "insert_cmd ::= INSERT orconf", + /* 172 */ "insert_cmd ::= REPLACE", + /* 173 */ "idlist_opt ::=", + /* 174 */ "idlist_opt ::= LP idlist RP", + /* 175 */ "idlist ::= idlist COMMA nm", + /* 176 */ "idlist ::= nm", + /* 177 */ "expr ::= LP expr RP", + /* 178 */ "expr ::= ID|INDEXED", + /* 179 */ "expr ::= JOIN_KW", + /* 180 */ "expr ::= nm DOT nm", + /* 181 */ "expr ::= nm DOT nm DOT nm", + /* 182 */ "term ::= NULL|FLOAT|BLOB", + /* 183 */ "term ::= STRING", + /* 184 */ "term ::= INTEGER", + /* 185 */ "expr ::= VARIABLE", + /* 186 */ "expr ::= expr COLLATE ID|STRING", + /* 187 */ "expr ::= CAST LP expr AS typetoken RP", + /* 188 */ "expr ::= ID|INDEXED LP distinct exprlist RP", + /* 189 */ "expr ::= ID|INDEXED LP STAR RP", + /* 190 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over", + /* 191 */ "expr ::= ID|INDEXED LP STAR RP filter_over", + /* 192 */ "term ::= CTIME_KW", + /* 193 */ "expr ::= LP nexprlist COMMA expr RP", + /* 194 */ "expr ::= expr AND expr", + /* 195 */ "expr ::= expr OR expr", + /* 196 */ "expr ::= expr LT|GT|GE|LE expr", + /* 197 */ "expr ::= expr EQ|NE expr", + /* 198 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", + /* 199 */ "expr ::= expr PLUS|MINUS expr", + /* 200 */ "expr ::= expr STAR|SLASH|REM expr", + /* 201 */ "expr ::= expr CONCAT expr", + /* 202 */ "likeop ::= NOT LIKE_KW|MATCH", + /* 203 */ "expr ::= expr likeop expr", + /* 204 */ "expr ::= expr likeop expr ESCAPE expr", + /* 205 */ "expr ::= expr ISNULL|NOTNULL", + /* 206 */ "expr ::= expr NOT NULL", + /* 207 */ "expr ::= expr IS expr", + /* 208 */ "expr ::= expr IS NOT expr", + /* 209 */ "expr ::= expr IS NOT DISTINCT FROM expr", + /* 210 */ "expr ::= expr IS DISTINCT FROM expr", + /* 211 */ "expr ::= NOT expr", + /* 212 */ "expr ::= BITNOT expr", + /* 213 */ "expr ::= PLUS|MINUS expr", + /* 214 */ "expr ::= expr PTR expr", + /* 215 */ "between_op ::= BETWEEN", + /* 216 */ "between_op ::= NOT BETWEEN", + /* 217 */ "expr ::= expr between_op expr AND expr", + /* 218 */ "in_op ::= IN", + /* 219 */ "in_op ::= NOT IN", + /* 220 */ "expr ::= expr in_op LP exprlist RP", + /* 221 */ "expr ::= LP select RP", + /* 222 */ "expr ::= expr in_op LP select RP", + /* 223 */ "expr ::= expr in_op nm dbnm paren_exprlist", + /* 224 */ "expr ::= EXISTS LP select RP", + /* 225 */ "expr ::= CASE case_operand case_exprlist case_else END", + /* 226 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 227 */ "case_exprlist ::= WHEN expr THEN expr", + /* 228 */ "case_else ::= ELSE expr", + /* 229 */ "case_else ::=", + /* 230 */ "case_operand ::= expr", + /* 231 */ "case_operand ::=", + /* 232 */ "exprlist ::=", + /* 233 */ "nexprlist ::= nexprlist COMMA expr", + /* 234 */ "nexprlist ::= expr", + /* 235 */ "paren_exprlist ::=", + /* 236 */ "paren_exprlist ::= LP exprlist RP", + /* 237 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", + /* 238 */ "uniqueflag ::= UNIQUE", + /* 239 */ "uniqueflag ::=", + /* 240 */ "eidlist_opt ::=", + /* 241 */ "eidlist_opt ::= LP eidlist RP", + /* 242 */ "eidlist ::= eidlist COMMA nm collate sortorder", + /* 243 */ "eidlist ::= nm collate sortorder", + /* 244 */ "collate ::=", + /* 245 */ "collate ::= COLLATE ID|STRING", + /* 246 */ "cmd ::= DROP INDEX ifexists fullname", + /* 247 */ "cmd ::= VACUUM vinto", + /* 248 */ "cmd ::= VACUUM nm vinto", + /* 249 */ "vinto ::= INTO expr", + /* 250 */ "vinto ::=", + /* 251 */ "cmd ::= PRAGMA nm dbnm", + /* 252 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 253 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 254 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 255 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 256 */ "plus_num ::= PLUS INTEGER|FLOAT", + /* 257 */ "minus_num ::= MINUS INTEGER|FLOAT", + /* 258 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 259 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 260 */ "trigger_time ::= BEFORE|AFTER", + /* 261 */ "trigger_time ::= INSTEAD OF", + /* 262 */ "trigger_time ::=", + /* 263 */ "trigger_event ::= DELETE|INSERT", + /* 264 */ "trigger_event ::= UPDATE", + /* 265 */ "trigger_event ::= UPDATE OF idlist", + /* 266 */ "when_clause ::=", + /* 267 */ "when_clause ::= WHEN expr", + /* 268 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 269 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 270 */ "trnm ::= nm DOT nm", + /* 271 */ "tridxby ::= INDEXED BY nm", + /* 272 */ "tridxby ::= NOT INDEXED", + /* 273 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", + /* 274 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", + /* 275 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", + /* 276 */ "trigger_cmd ::= scanpt select scanpt", + /* 277 */ "expr ::= RAISE LP IGNORE RP", + /* 278 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 279 */ "raisetype ::= ROLLBACK", + /* 280 */ "raisetype ::= ABORT", + /* 281 */ "raisetype ::= FAIL", + /* 282 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 283 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 284 */ "cmd ::= DETACH database_kw_opt expr", + /* 285 */ "key_opt ::=", + /* 286 */ "key_opt ::= KEY expr", + /* 287 */ "cmd ::= REINDEX", + /* 288 */ "cmd ::= REINDEX nm dbnm", + /* 289 */ "cmd ::= ANALYZE", + /* 290 */ "cmd ::= ANALYZE nm dbnm", + /* 291 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 292 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", + /* 293 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", + /* 294 */ "add_column_fullname ::= fullname", + /* 295 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", + /* 296 */ "cmd ::= create_vtab", + /* 297 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 298 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 299 */ "vtabarg ::=", + /* 300 */ "vtabargtoken ::= ANY", + /* 301 */ "vtabargtoken ::= lp anylist RP", + /* 302 */ "lp ::= LP", + /* 303 */ "with ::= WITH wqlist", + /* 304 */ "with ::= WITH RECURSIVE wqlist", + /* 305 */ "wqas ::= AS", + /* 306 */ "wqas ::= AS MATERIALIZED", + /* 307 */ "wqas ::= AS NOT MATERIALIZED", + /* 308 */ "wqitem ::= nm eidlist_opt wqas LP select RP", + /* 309 */ "wqlist ::= wqitem", + /* 310 */ "wqlist ::= wqlist COMMA wqitem", + /* 311 */ "windowdefn_list ::= windowdefn", + /* 312 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 313 */ "windowdefn ::= nm AS LP window RP", + /* 314 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 315 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 316 */ "window ::= ORDER BY sortlist frame_opt", + /* 317 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 318 */ "window ::= frame_opt", + /* 319 */ "window ::= nm frame_opt", + /* 320 */ "frame_opt ::=", + /* 321 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", + /* 322 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", + /* 323 */ "range_or_rows ::= RANGE|ROWS|GROUPS", + /* 324 */ "frame_bound_s ::= frame_bound", + /* 325 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 326 */ "frame_bound_e ::= frame_bound", + /* 327 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 328 */ "frame_bound ::= expr PRECEDING|FOLLOWING", + /* 329 */ "frame_bound ::= CURRENT ROW", + /* 330 */ "frame_exclude_opt ::=", + /* 331 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", + /* 332 */ "frame_exclude ::= NO OTHERS", + /* 333 */ "frame_exclude ::= CURRENT ROW", + /* 334 */ "frame_exclude ::= GROUP|TIES", + /* 335 */ "window_clause ::= WINDOW windowdefn_list", + /* 336 */ "filter_over ::= filter_clause over_clause", + /* 337 */ "filter_over ::= over_clause", + /* 338 */ "filter_over ::= filter_clause", + /* 339 */ "over_clause ::= OVER LP window RP", + /* 340 */ "over_clause ::= OVER nm", + /* 341 */ "filter_clause ::= FILTER LP WHERE expr RP", + /* 342 */ "input ::= cmdlist", + /* 343 */ "cmdlist ::= cmdlist ecmd", + /* 344 */ "cmdlist ::= ecmd", + /* 345 */ "ecmd ::= SEMI", + /* 346 */ "ecmd ::= cmdx SEMI", + /* 347 */ "ecmd ::= explain cmdx SEMI", + /* 348 */ "trans_opt ::=", + /* 349 */ "trans_opt ::= TRANSACTION", + /* 350 */ "trans_opt ::= TRANSACTION nm", + /* 351 */ "savepoint_opt ::= SAVEPOINT", + /* 352 */ "savepoint_opt ::=", + /* 353 */ "cmd ::= create_table create_table_args", + /* 354 */ "table_option_set ::= table_option", + /* 355 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 356 */ "columnlist ::= columnname carglist", + /* 357 */ "nm ::= ID|INDEXED", + /* 358 */ "nm ::= STRING", + /* 359 */ "nm ::= JOIN_KW", + /* 360 */ "typetoken ::= typename", + /* 361 */ "typename ::= ID|STRING", + /* 362 */ "signed ::= plus_num", + /* 363 */ "signed ::= minus_num", + /* 364 */ "carglist ::= carglist ccons", + /* 365 */ "carglist ::=", + /* 366 */ "ccons ::= NULL onconf", + /* 367 */ "ccons ::= GENERATED ALWAYS AS generated", + /* 368 */ "ccons ::= AS generated", + /* 369 */ "conslist_opt ::= COMMA conslist", + /* 370 */ "conslist ::= conslist tconscomma tcons", + /* 371 */ "conslist ::= tcons", + /* 372 */ "tconscomma ::=", + /* 373 */ "defer_subclause_opt ::= defer_subclause", + /* 374 */ "resolvetype ::= raisetype", + /* 375 */ "selectnowith ::= oneselect", + /* 376 */ "oneselect ::= values", + /* 377 */ "sclp ::= selcollist COMMA", + /* 378 */ "as ::= ID|STRING", + /* 379 */ "indexed_opt ::= indexed_by", + /* 380 */ "returning ::=", + /* 381 */ "expr ::= term", + /* 382 */ "likeop ::= LIKE_KW|MATCH", + /* 383 */ "exprlist ::= nexprlist", + /* 384 */ "nmnum ::= plus_num", + /* 385 */ "nmnum ::= nm", + /* 386 */ "nmnum ::= ON", + /* 387 */ "nmnum ::= DELETE", + /* 388 */ "nmnum ::= DEFAULT", + /* 389 */ "plus_num ::= INTEGER|FLOAT", + /* 390 */ "foreach_clause ::=", + /* 391 */ "foreach_clause ::= FOR EACH ROW", + /* 392 */ "trnm ::= nm", + /* 393 */ "tridxby ::=", + /* 394 */ "database_kw_opt ::= DATABASE", + /* 395 */ "database_kw_opt ::=", + /* 396 */ "kwcolumn_opt ::=", + /* 397 */ "kwcolumn_opt ::= COLUMNKW", + /* 398 */ "vtabarglist ::= vtabarg", + /* 399 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 400 */ "vtabarg ::= vtabarg vtabargtoken", + /* 401 */ "anylist ::=", + /* 402 */ "anylist ::= anylist LP anylist RP", + /* 403 */ "anylist ::= anylist ANY", + /* 404 */ "with ::=", }; #endif /* NDEBUG */ @@ -161980,98 +171232,97 @@ static void yy_destructor( ** inside the C code. */ /********* Begin destructor definitions ***************************************/ - case 200: /* select */ - case 234: /* selectnowith */ - case 235: /* oneselect */ - case 247: /* values */ + case 204: /* select */ + case 239: /* selectnowith */ + case 240: /* oneselect */ + case 252: /* values */ { -sqlcipher_sqlite3SelectDelete(pParse->db, (yypminor->yy539)); -} - break; - case 211: /* term */ - case 212: /* expr */ - case 241: /* where_opt */ - case 243: /* having_opt */ - case 255: /* on_opt */ - case 271: /* case_operand */ - case 273: /* case_else */ - case 276: /* vinto */ - case 283: /* when_clause */ - case 288: /* key_opt */ - case 302: /* filter_clause */ +sqlcipher_sqlite3SelectDelete(pParse->db, (yypminor->yy47)); +} + break; + case 216: /* term */ + case 217: /* expr */ + case 246: /* where_opt */ + case 248: /* having_opt */ + case 267: /* where_opt_ret */ + case 278: /* case_operand */ + case 280: /* case_else */ + case 283: /* vinto */ + case 290: /* when_clause */ + case 295: /* key_opt */ + case 311: /* filter_clause */ { -sqlcipher_sqlite3ExprDelete(pParse->db, (yypminor->yy202)); -} - break; - case 216: /* eidlist_opt */ - case 226: /* sortlist */ - case 227: /* eidlist */ - case 239: /* selcollist */ - case 242: /* groupby_opt */ - case 244: /* orderby_opt */ - case 248: /* nexprlist */ - case 249: /* sclp */ - case 257: /* exprlist */ - case 262: /* setlist */ - case 270: /* paren_exprlist */ - case 272: /* case_exprlist */ - case 301: /* part_opt */ +sqlcipher_sqlite3ExprDelete(pParse->db, (yypminor->yy528)); +} + break; + case 221: /* eidlist_opt */ + case 231: /* sortlist */ + case 232: /* eidlist */ + case 244: /* selcollist */ + case 247: /* groupby_opt */ + case 249: /* orderby_opt */ + case 253: /* nexprlist */ + case 254: /* sclp */ + case 261: /* exprlist */ + case 268: /* setlist */ + case 277: /* paren_exprlist */ + case 279: /* case_exprlist */ + case 310: /* part_opt */ { -sqlcipher_sqlite3ExprListDelete(pParse->db, (yypminor->yy242)); +sqlcipher_sqlite3ExprListDelete(pParse->db, (yypminor->yy322)); } break; - case 233: /* fullname */ - case 240: /* from */ - case 251: /* seltablist */ - case 252: /* stl_prefix */ - case 258: /* xfullname */ + case 238: /* fullname */ + case 245: /* from */ + case 256: /* seltablist */ + case 257: /* stl_prefix */ + case 262: /* xfullname */ { -sqlcipher_sqlite3SrcListDelete(pParse->db, (yypminor->yy47)); +sqlcipher_sqlite3SrcListDelete(pParse->db, (yypminor->yy131)); } break; - case 236: /* wqlist */ + case 241: /* wqlist */ { -sqlcipher_sqlite3WithDelete(pParse->db, (yypminor->yy131)); +sqlcipher_sqlite3WithDelete(pParse->db, (yypminor->yy521)); } break; - case 246: /* window_clause */ - case 297: /* windowdefn_list */ + case 251: /* window_clause */ + case 306: /* windowdefn_list */ { -sqlcipher_sqlite3WindowListDelete(pParse->db, (yypminor->yy303)); +sqlcipher_sqlite3WindowListDelete(pParse->db, (yypminor->yy41)); } break; - case 256: /* using_opt */ - case 259: /* idlist */ - case 264: /* idlist_opt */ + case 263: /* idlist */ + case 270: /* idlist_opt */ { -sqlcipher_sqlite3IdListDelete(pParse->db, (yypminor->yy600)); +sqlcipher_sqlite3IdListDelete(pParse->db, (yypminor->yy254)); } break; - case 266: /* filter_over */ - case 298: /* windowdefn */ - case 299: /* window */ - case 300: /* frame_opt */ - case 303: /* over_clause */ + case 273: /* filter_over */ + case 307: /* windowdefn */ + case 308: /* window */ + case 309: /* frame_opt */ + case 312: /* over_clause */ { -sqlcipher_sqlite3WindowDelete(pParse->db, (yypminor->yy303)); +sqlcipher_sqlite3WindowDelete(pParse->db, (yypminor->yy41)); } break; - case 279: /* trigger_cmd_list */ - case 284: /* trigger_cmd */ + case 286: /* trigger_cmd_list */ + case 291: /* trigger_cmd */ { -sqlcipher_sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy447)); +sqlcipher_sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy33)); } break; - case 281: /* trigger_event */ + case 288: /* trigger_event */ { -sqlcipher_sqlite3IdListDelete(pParse->db, (yypminor->yy230).b); +sqlcipher_sqlite3IdListDelete(pParse->db, (yypminor->yy180).b); } break; - case 305: /* frame_bound */ - case 306: /* frame_bound_s */ - case 307: /* frame_bound_e */ + case 314: /* frame_bound */ + case 315: /* frame_bound_s */ + case 316: /* frame_bound_e */ { -sqlcipher_sqlite3ExprDelete(pParse->db, (yypminor->yy77).pExpr); +sqlcipher_sqlite3ExprDelete(pParse->db, (yypminor->yy595).pExpr); } break; /********* End destructor definitions *****************************************/ @@ -162362,391 +171613,411 @@ static void yy_shift( /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side ** of that rule */ static const YYCODETYPE yyRuleInfoLhs[] = { - 185, /* (0) explain ::= EXPLAIN */ - 185, /* (1) explain ::= EXPLAIN QUERY PLAN */ - 184, /* (2) cmdx ::= cmd */ - 186, /* (3) cmd ::= BEGIN transtype trans_opt */ - 187, /* (4) transtype ::= */ - 187, /* (5) transtype ::= DEFERRED */ - 187, /* (6) transtype ::= IMMEDIATE */ - 187, /* (7) transtype ::= EXCLUSIVE */ - 186, /* (8) cmd ::= COMMIT|END trans_opt */ - 186, /* (9) cmd ::= ROLLBACK trans_opt */ - 186, /* (10) cmd ::= SAVEPOINT nm */ - 186, /* (11) cmd ::= RELEASE savepoint_opt nm */ - 186, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ - 191, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ - 193, /* (14) createkw ::= CREATE */ - 195, /* (15) ifnotexists ::= */ - 195, /* (16) ifnotexists ::= IF NOT EXISTS */ - 194, /* (17) temp ::= TEMP */ - 194, /* (18) temp ::= */ - 192, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ - 192, /* (20) create_table_args ::= AS select */ - 199, /* (21) table_options ::= */ - 199, /* (22) table_options ::= WITHOUT nm */ - 201, /* (23) columnname ::= nm typetoken */ - 203, /* (24) typetoken ::= */ - 203, /* (25) typetoken ::= typename LP signed RP */ - 203, /* (26) typetoken ::= typename LP signed COMMA signed RP */ - 204, /* (27) typename ::= typename ID|STRING */ - 208, /* (28) scanpt ::= */ - 209, /* (29) scantok ::= */ - 210, /* (30) ccons ::= CONSTRAINT nm */ - 210, /* (31) ccons ::= DEFAULT scantok term */ - 210, /* (32) ccons ::= DEFAULT LP expr RP */ - 210, /* (33) ccons ::= DEFAULT PLUS scantok term */ - 210, /* (34) ccons ::= DEFAULT MINUS scantok term */ - 210, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */ - 210, /* (36) ccons ::= NOT NULL onconf */ - 210, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */ - 210, /* (38) ccons ::= UNIQUE onconf */ - 210, /* (39) ccons ::= CHECK LP expr RP */ - 210, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */ - 210, /* (41) ccons ::= defer_subclause */ - 210, /* (42) ccons ::= COLLATE ID|STRING */ - 219, /* (43) generated ::= LP expr RP */ - 219, /* (44) generated ::= LP expr RP ID */ - 215, /* (45) autoinc ::= */ - 215, /* (46) autoinc ::= AUTOINCR */ - 217, /* (47) refargs ::= */ - 217, /* (48) refargs ::= refargs refarg */ - 220, /* (49) refarg ::= MATCH nm */ - 220, /* (50) refarg ::= ON INSERT refact */ - 220, /* (51) refarg ::= ON DELETE refact */ - 220, /* (52) refarg ::= ON UPDATE refact */ - 221, /* (53) refact ::= SET NULL */ - 221, /* (54) refact ::= SET DEFAULT */ - 221, /* (55) refact ::= CASCADE */ - 221, /* (56) refact ::= RESTRICT */ - 221, /* (57) refact ::= NO ACTION */ - 218, /* (58) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ - 218, /* (59) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - 222, /* (60) init_deferred_pred_opt ::= */ - 222, /* (61) init_deferred_pred_opt ::= INITIALLY DEFERRED */ - 222, /* (62) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ - 198, /* (63) conslist_opt ::= */ - 224, /* (64) tconscomma ::= COMMA */ - 225, /* (65) tcons ::= CONSTRAINT nm */ - 225, /* (66) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ - 225, /* (67) tcons ::= UNIQUE LP sortlist RP onconf */ - 225, /* (68) tcons ::= CHECK LP expr RP onconf */ - 225, /* (69) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ - 228, /* (70) defer_subclause_opt ::= */ - 213, /* (71) onconf ::= */ - 213, /* (72) onconf ::= ON CONFLICT resolvetype */ - 229, /* (73) orconf ::= */ - 229, /* (74) orconf ::= OR resolvetype */ - 230, /* (75) resolvetype ::= IGNORE */ - 230, /* (76) resolvetype ::= REPLACE */ - 186, /* (77) cmd ::= DROP TABLE ifexists fullname */ - 232, /* (78) ifexists ::= IF EXISTS */ - 232, /* (79) ifexists ::= */ - 186, /* (80) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ - 186, /* (81) cmd ::= DROP VIEW ifexists fullname */ - 186, /* (82) cmd ::= select */ - 200, /* (83) select ::= WITH wqlist selectnowith */ - 200, /* (84) select ::= WITH RECURSIVE wqlist selectnowith */ - 200, /* (85) select ::= selectnowith */ - 234, /* (86) selectnowith ::= selectnowith multiselect_op oneselect */ - 237, /* (87) multiselect_op ::= UNION */ - 237, /* (88) multiselect_op ::= UNION ALL */ - 237, /* (89) multiselect_op ::= EXCEPT|INTERSECT */ - 235, /* (90) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - 235, /* (91) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ - 247, /* (92) values ::= VALUES LP nexprlist RP */ - 247, /* (93) values ::= values COMMA LP nexprlist RP */ - 238, /* (94) distinct ::= DISTINCT */ - 238, /* (95) distinct ::= ALL */ - 238, /* (96) distinct ::= */ - 249, /* (97) sclp ::= */ - 239, /* (98) selcollist ::= sclp scanpt expr scanpt as */ - 239, /* (99) selcollist ::= sclp scanpt STAR */ - 239, /* (100) selcollist ::= sclp scanpt nm DOT STAR */ - 250, /* (101) as ::= AS nm */ - 250, /* (102) as ::= */ - 240, /* (103) from ::= */ - 240, /* (104) from ::= FROM seltablist */ - 252, /* (105) stl_prefix ::= seltablist joinop */ - 252, /* (106) stl_prefix ::= */ - 251, /* (107) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ - 251, /* (108) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ - 251, /* (109) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ - 251, /* (110) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ - 196, /* (111) dbnm ::= */ - 196, /* (112) dbnm ::= DOT nm */ - 233, /* (113) fullname ::= nm */ - 233, /* (114) fullname ::= nm DOT nm */ - 258, /* (115) xfullname ::= nm */ - 258, /* (116) xfullname ::= nm DOT nm */ - 258, /* (117) xfullname ::= nm DOT nm AS nm */ - 258, /* (118) xfullname ::= nm AS nm */ - 253, /* (119) joinop ::= COMMA|JOIN */ - 253, /* (120) joinop ::= JOIN_KW JOIN */ - 253, /* (121) joinop ::= JOIN_KW nm JOIN */ - 253, /* (122) joinop ::= JOIN_KW nm nm JOIN */ - 255, /* (123) on_opt ::= ON expr */ - 255, /* (124) on_opt ::= */ - 254, /* (125) indexed_opt ::= */ - 254, /* (126) indexed_opt ::= INDEXED BY nm */ - 254, /* (127) indexed_opt ::= NOT INDEXED */ - 256, /* (128) using_opt ::= USING LP idlist RP */ - 256, /* (129) using_opt ::= */ - 244, /* (130) orderby_opt ::= */ - 244, /* (131) orderby_opt ::= ORDER BY sortlist */ - 226, /* (132) sortlist ::= sortlist COMMA expr sortorder nulls */ - 226, /* (133) sortlist ::= expr sortorder nulls */ - 214, /* (134) sortorder ::= ASC */ - 214, /* (135) sortorder ::= DESC */ - 214, /* (136) sortorder ::= */ - 260, /* (137) nulls ::= NULLS FIRST */ - 260, /* (138) nulls ::= NULLS LAST */ - 260, /* (139) nulls ::= */ - 242, /* (140) groupby_opt ::= */ - 242, /* (141) groupby_opt ::= GROUP BY nexprlist */ - 243, /* (142) having_opt ::= */ - 243, /* (143) having_opt ::= HAVING expr */ - 245, /* (144) limit_opt ::= */ - 245, /* (145) limit_opt ::= LIMIT expr */ - 245, /* (146) limit_opt ::= LIMIT expr OFFSET expr */ - 245, /* (147) limit_opt ::= LIMIT expr COMMA expr */ - 186, /* (148) cmd ::= with DELETE FROM xfullname indexed_opt where_opt orderby_opt limit_opt */ - 241, /* (149) where_opt ::= */ - 241, /* (150) where_opt ::= WHERE expr */ - 186, /* (151) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt orderby_opt limit_opt */ - 262, /* (152) setlist ::= setlist COMMA nm EQ expr */ - 262, /* (153) setlist ::= setlist COMMA LP idlist RP EQ expr */ - 262, /* (154) setlist ::= nm EQ expr */ - 262, /* (155) setlist ::= LP idlist RP EQ expr */ - 186, /* (156) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - 186, /* (157) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ - 265, /* (158) upsert ::= */ - 265, /* (159) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ - 265, /* (160) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ - 265, /* (161) upsert ::= ON CONFLICT DO NOTHING */ - 263, /* (162) insert_cmd ::= INSERT orconf */ - 263, /* (163) insert_cmd ::= REPLACE */ - 264, /* (164) idlist_opt ::= */ - 264, /* (165) idlist_opt ::= LP idlist RP */ - 259, /* (166) idlist ::= idlist COMMA nm */ - 259, /* (167) idlist ::= nm */ - 212, /* (168) expr ::= LP expr RP */ - 212, /* (169) expr ::= ID|INDEXED */ - 212, /* (170) expr ::= JOIN_KW */ - 212, /* (171) expr ::= nm DOT nm */ - 212, /* (172) expr ::= nm DOT nm DOT nm */ - 211, /* (173) term ::= NULL|FLOAT|BLOB */ - 211, /* (174) term ::= STRING */ - 211, /* (175) term ::= INTEGER */ - 212, /* (176) expr ::= VARIABLE */ - 212, /* (177) expr ::= expr COLLATE ID|STRING */ - 212, /* (178) expr ::= CAST LP expr AS typetoken RP */ - 212, /* (179) expr ::= ID|INDEXED LP distinct exprlist RP */ - 212, /* (180) expr ::= ID|INDEXED LP STAR RP */ - 212, /* (181) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ - 212, /* (182) expr ::= ID|INDEXED LP STAR RP filter_over */ - 211, /* (183) term ::= CTIME_KW */ - 212, /* (184) expr ::= LP nexprlist COMMA expr RP */ - 212, /* (185) expr ::= expr AND expr */ - 212, /* (186) expr ::= expr OR expr */ - 212, /* (187) expr ::= expr LT|GT|GE|LE expr */ - 212, /* (188) expr ::= expr EQ|NE expr */ - 212, /* (189) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - 212, /* (190) expr ::= expr PLUS|MINUS expr */ - 212, /* (191) expr ::= expr STAR|SLASH|REM expr */ - 212, /* (192) expr ::= expr CONCAT expr */ - 267, /* (193) likeop ::= NOT LIKE_KW|MATCH */ - 212, /* (194) expr ::= expr likeop expr */ - 212, /* (195) expr ::= expr likeop expr ESCAPE expr */ - 212, /* (196) expr ::= expr ISNULL|NOTNULL */ - 212, /* (197) expr ::= expr NOT NULL */ - 212, /* (198) expr ::= expr IS expr */ - 212, /* (199) expr ::= expr IS NOT expr */ - 212, /* (200) expr ::= NOT expr */ - 212, /* (201) expr ::= BITNOT expr */ - 212, /* (202) expr ::= PLUS|MINUS expr */ - 268, /* (203) between_op ::= BETWEEN */ - 268, /* (204) between_op ::= NOT BETWEEN */ - 212, /* (205) expr ::= expr between_op expr AND expr */ - 269, /* (206) in_op ::= IN */ - 269, /* (207) in_op ::= NOT IN */ - 212, /* (208) expr ::= expr in_op LP exprlist RP */ - 212, /* (209) expr ::= LP select RP */ - 212, /* (210) expr ::= expr in_op LP select RP */ - 212, /* (211) expr ::= expr in_op nm dbnm paren_exprlist */ - 212, /* (212) expr ::= EXISTS LP select RP */ - 212, /* (213) expr ::= CASE case_operand case_exprlist case_else END */ - 272, /* (214) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - 272, /* (215) case_exprlist ::= WHEN expr THEN expr */ - 273, /* (216) case_else ::= ELSE expr */ - 273, /* (217) case_else ::= */ - 271, /* (218) case_operand ::= expr */ - 271, /* (219) case_operand ::= */ - 257, /* (220) exprlist ::= */ - 248, /* (221) nexprlist ::= nexprlist COMMA expr */ - 248, /* (222) nexprlist ::= expr */ - 270, /* (223) paren_exprlist ::= */ - 270, /* (224) paren_exprlist ::= LP exprlist RP */ - 186, /* (225) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - 274, /* (226) uniqueflag ::= UNIQUE */ - 274, /* (227) uniqueflag ::= */ - 216, /* (228) eidlist_opt ::= */ - 216, /* (229) eidlist_opt ::= LP eidlist RP */ - 227, /* (230) eidlist ::= eidlist COMMA nm collate sortorder */ - 227, /* (231) eidlist ::= nm collate sortorder */ - 275, /* (232) collate ::= */ - 275, /* (233) collate ::= COLLATE ID|STRING */ - 186, /* (234) cmd ::= DROP INDEX ifexists fullname */ - 186, /* (235) cmd ::= VACUUM vinto */ - 186, /* (236) cmd ::= VACUUM nm vinto */ - 276, /* (237) vinto ::= INTO expr */ - 276, /* (238) vinto ::= */ - 186, /* (239) cmd ::= PRAGMA nm dbnm */ - 186, /* (240) cmd ::= PRAGMA nm dbnm EQ nmnum */ - 186, /* (241) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - 186, /* (242) cmd ::= PRAGMA nm dbnm EQ minus_num */ - 186, /* (243) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - 206, /* (244) plus_num ::= PLUS INTEGER|FLOAT */ - 207, /* (245) minus_num ::= MINUS INTEGER|FLOAT */ - 186, /* (246) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - 278, /* (247) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - 280, /* (248) trigger_time ::= BEFORE|AFTER */ - 280, /* (249) trigger_time ::= INSTEAD OF */ - 280, /* (250) trigger_time ::= */ - 281, /* (251) trigger_event ::= DELETE|INSERT */ - 281, /* (252) trigger_event ::= UPDATE */ - 281, /* (253) trigger_event ::= UPDATE OF idlist */ - 283, /* (254) when_clause ::= */ - 283, /* (255) when_clause ::= WHEN expr */ - 279, /* (256) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - 279, /* (257) trigger_cmd_list ::= trigger_cmd SEMI */ - 285, /* (258) trnm ::= nm DOT nm */ - 286, /* (259) tridxby ::= INDEXED BY nm */ - 286, /* (260) tridxby ::= NOT INDEXED */ - 284, /* (261) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ - 284, /* (262) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - 284, /* (263) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - 284, /* (264) trigger_cmd ::= scanpt select scanpt */ - 212, /* (265) expr ::= RAISE LP IGNORE RP */ - 212, /* (266) expr ::= RAISE LP raisetype COMMA nm RP */ - 231, /* (267) raisetype ::= ROLLBACK */ - 231, /* (268) raisetype ::= ABORT */ - 231, /* (269) raisetype ::= FAIL */ - 186, /* (270) cmd ::= DROP TRIGGER ifexists fullname */ - 186, /* (271) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - 186, /* (272) cmd ::= DETACH database_kw_opt expr */ - 288, /* (273) key_opt ::= */ - 288, /* (274) key_opt ::= KEY expr */ - 186, /* (275) cmd ::= REINDEX */ - 186, /* (276) cmd ::= REINDEX nm dbnm */ - 186, /* (277) cmd ::= ANALYZE */ - 186, /* (278) cmd ::= ANALYZE nm dbnm */ - 186, /* (279) cmd ::= ALTER TABLE fullname RENAME TO nm */ - 186, /* (280) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - 289, /* (281) add_column_fullname ::= fullname */ - 186, /* (282) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - 186, /* (283) cmd ::= create_vtab */ - 186, /* (284) cmd ::= create_vtab LP vtabarglist RP */ - 291, /* (285) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 293, /* (286) vtabarg ::= */ - 294, /* (287) vtabargtoken ::= ANY */ - 294, /* (288) vtabargtoken ::= lp anylist RP */ - 295, /* (289) lp ::= LP */ - 261, /* (290) with ::= WITH wqlist */ - 261, /* (291) with ::= WITH RECURSIVE wqlist */ - 236, /* (292) wqlist ::= nm eidlist_opt AS LP select RP */ - 236, /* (293) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ - 297, /* (294) windowdefn_list ::= windowdefn */ - 297, /* (295) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - 298, /* (296) windowdefn ::= nm AS LP window RP */ - 299, /* (297) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - 299, /* (298) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - 299, /* (299) window ::= ORDER BY sortlist frame_opt */ - 299, /* (300) window ::= nm ORDER BY sortlist frame_opt */ - 299, /* (301) window ::= frame_opt */ - 299, /* (302) window ::= nm frame_opt */ - 300, /* (303) frame_opt ::= */ - 300, /* (304) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - 300, /* (305) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - 304, /* (306) range_or_rows ::= RANGE|ROWS|GROUPS */ - 306, /* (307) frame_bound_s ::= frame_bound */ - 306, /* (308) frame_bound_s ::= UNBOUNDED PRECEDING */ - 307, /* (309) frame_bound_e ::= frame_bound */ - 307, /* (310) frame_bound_e ::= UNBOUNDED FOLLOWING */ - 305, /* (311) frame_bound ::= expr PRECEDING|FOLLOWING */ - 305, /* (312) frame_bound ::= CURRENT ROW */ - 308, /* (313) frame_exclude_opt ::= */ - 308, /* (314) frame_exclude_opt ::= EXCLUDE frame_exclude */ - 309, /* (315) frame_exclude ::= NO OTHERS */ - 309, /* (316) frame_exclude ::= CURRENT ROW */ - 309, /* (317) frame_exclude ::= GROUP|TIES */ - 246, /* (318) window_clause ::= WINDOW windowdefn_list */ - 266, /* (319) filter_over ::= filter_clause over_clause */ - 266, /* (320) filter_over ::= over_clause */ - 266, /* (321) filter_over ::= filter_clause */ - 303, /* (322) over_clause ::= OVER LP window RP */ - 303, /* (323) over_clause ::= OVER nm */ - 302, /* (324) filter_clause ::= FILTER LP WHERE expr RP */ - 181, /* (325) input ::= cmdlist */ - 182, /* (326) cmdlist ::= cmdlist ecmd */ - 182, /* (327) cmdlist ::= ecmd */ - 183, /* (328) ecmd ::= SEMI */ - 183, /* (329) ecmd ::= cmdx SEMI */ - 183, /* (330) ecmd ::= explain cmdx SEMI */ - 188, /* (331) trans_opt ::= */ - 188, /* (332) trans_opt ::= TRANSACTION */ - 188, /* (333) trans_opt ::= TRANSACTION nm */ - 190, /* (334) savepoint_opt ::= SAVEPOINT */ - 190, /* (335) savepoint_opt ::= */ - 186, /* (336) cmd ::= create_table create_table_args */ - 197, /* (337) columnlist ::= columnlist COMMA columnname carglist */ - 197, /* (338) columnlist ::= columnname carglist */ - 189, /* (339) nm ::= ID|INDEXED */ - 189, /* (340) nm ::= STRING */ - 189, /* (341) nm ::= JOIN_KW */ - 203, /* (342) typetoken ::= typename */ - 204, /* (343) typename ::= ID|STRING */ - 205, /* (344) signed ::= plus_num */ - 205, /* (345) signed ::= minus_num */ - 202, /* (346) carglist ::= carglist ccons */ - 202, /* (347) carglist ::= */ - 210, /* (348) ccons ::= NULL onconf */ - 210, /* (349) ccons ::= GENERATED ALWAYS AS generated */ - 210, /* (350) ccons ::= AS generated */ - 198, /* (351) conslist_opt ::= COMMA conslist */ - 223, /* (352) conslist ::= conslist tconscomma tcons */ - 223, /* (353) conslist ::= tcons */ - 224, /* (354) tconscomma ::= */ - 228, /* (355) defer_subclause_opt ::= defer_subclause */ - 230, /* (356) resolvetype ::= raisetype */ - 234, /* (357) selectnowith ::= oneselect */ - 235, /* (358) oneselect ::= values */ - 249, /* (359) sclp ::= selcollist COMMA */ - 250, /* (360) as ::= ID|STRING */ - 212, /* (361) expr ::= term */ - 267, /* (362) likeop ::= LIKE_KW|MATCH */ - 257, /* (363) exprlist ::= nexprlist */ - 277, /* (364) nmnum ::= plus_num */ - 277, /* (365) nmnum ::= nm */ - 277, /* (366) nmnum ::= ON */ - 277, /* (367) nmnum ::= DELETE */ - 277, /* (368) nmnum ::= DEFAULT */ - 206, /* (369) plus_num ::= INTEGER|FLOAT */ - 282, /* (370) foreach_clause ::= */ - 282, /* (371) foreach_clause ::= FOR EACH ROW */ - 285, /* (372) trnm ::= nm */ - 286, /* (373) tridxby ::= */ - 287, /* (374) database_kw_opt ::= DATABASE */ - 287, /* (375) database_kw_opt ::= */ - 290, /* (376) kwcolumn_opt ::= */ - 290, /* (377) kwcolumn_opt ::= COLUMNKW */ - 292, /* (378) vtabarglist ::= vtabarg */ - 292, /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */ - 293, /* (380) vtabarg ::= vtabarg vtabargtoken */ - 296, /* (381) anylist ::= */ - 296, /* (382) anylist ::= anylist LP anylist RP */ - 296, /* (383) anylist ::= anylist ANY */ - 261, /* (384) with ::= */ + 189, /* (0) explain ::= EXPLAIN */ + 189, /* (1) explain ::= EXPLAIN QUERY PLAN */ + 188, /* (2) cmdx ::= cmd */ + 190, /* (3) cmd ::= BEGIN transtype trans_opt */ + 191, /* (4) transtype ::= */ + 191, /* (5) transtype ::= DEFERRED */ + 191, /* (6) transtype ::= IMMEDIATE */ + 191, /* (7) transtype ::= EXCLUSIVE */ + 190, /* (8) cmd ::= COMMIT|END trans_opt */ + 190, /* (9) cmd ::= ROLLBACK trans_opt */ + 190, /* (10) cmd ::= SAVEPOINT nm */ + 190, /* (11) cmd ::= RELEASE savepoint_opt nm */ + 190, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ + 195, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + 197, /* (14) createkw ::= CREATE */ + 199, /* (15) ifnotexists ::= */ + 199, /* (16) ifnotexists ::= IF NOT EXISTS */ + 198, /* (17) temp ::= TEMP */ + 198, /* (18) temp ::= */ + 196, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ + 196, /* (20) create_table_args ::= AS select */ + 203, /* (21) table_option_set ::= */ + 203, /* (22) table_option_set ::= table_option_set COMMA table_option */ + 205, /* (23) table_option ::= WITHOUT nm */ + 205, /* (24) table_option ::= nm */ + 206, /* (25) columnname ::= nm typetoken */ + 208, /* (26) typetoken ::= */ + 208, /* (27) typetoken ::= typename LP signed RP */ + 208, /* (28) typetoken ::= typename LP signed COMMA signed RP */ + 209, /* (29) typename ::= typename ID|STRING */ + 213, /* (30) scanpt ::= */ + 214, /* (31) scantok ::= */ + 215, /* (32) ccons ::= CONSTRAINT nm */ + 215, /* (33) ccons ::= DEFAULT scantok term */ + 215, /* (34) ccons ::= DEFAULT LP expr RP */ + 215, /* (35) ccons ::= DEFAULT PLUS scantok term */ + 215, /* (36) ccons ::= DEFAULT MINUS scantok term */ + 215, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ + 215, /* (38) ccons ::= NOT NULL onconf */ + 215, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + 215, /* (40) ccons ::= UNIQUE onconf */ + 215, /* (41) ccons ::= CHECK LP expr RP */ + 215, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ + 215, /* (43) ccons ::= defer_subclause */ + 215, /* (44) ccons ::= COLLATE ID|STRING */ + 224, /* (45) generated ::= LP expr RP */ + 224, /* (46) generated ::= LP expr RP ID */ + 220, /* (47) autoinc ::= */ + 220, /* (48) autoinc ::= AUTOINCR */ + 222, /* (49) refargs ::= */ + 222, /* (50) refargs ::= refargs refarg */ + 225, /* (51) refarg ::= MATCH nm */ + 225, /* (52) refarg ::= ON INSERT refact */ + 225, /* (53) refarg ::= ON DELETE refact */ + 225, /* (54) refarg ::= ON UPDATE refact */ + 226, /* (55) refact ::= SET NULL */ + 226, /* (56) refact ::= SET DEFAULT */ + 226, /* (57) refact ::= CASCADE */ + 226, /* (58) refact ::= RESTRICT */ + 226, /* (59) refact ::= NO ACTION */ + 223, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + 223, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 227, /* (62) init_deferred_pred_opt ::= */ + 227, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + 227, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 202, /* (65) conslist_opt ::= */ + 229, /* (66) tconscomma ::= COMMA */ + 230, /* (67) tcons ::= CONSTRAINT nm */ + 230, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + 230, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ + 230, /* (70) tcons ::= CHECK LP expr RP onconf */ + 230, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 233, /* (72) defer_subclause_opt ::= */ + 218, /* (73) onconf ::= */ + 218, /* (74) onconf ::= ON CONFLICT resolvetype */ + 234, /* (75) orconf ::= */ + 234, /* (76) orconf ::= OR resolvetype */ + 235, /* (77) resolvetype ::= IGNORE */ + 235, /* (78) resolvetype ::= REPLACE */ + 190, /* (79) cmd ::= DROP TABLE ifexists fullname */ + 237, /* (80) ifexists ::= IF EXISTS */ + 237, /* (81) ifexists ::= */ + 190, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + 190, /* (83) cmd ::= DROP VIEW ifexists fullname */ + 190, /* (84) cmd ::= select */ + 204, /* (85) select ::= WITH wqlist selectnowith */ + 204, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ + 204, /* (87) select ::= selectnowith */ + 239, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ + 242, /* (89) multiselect_op ::= UNION */ + 242, /* (90) multiselect_op ::= UNION ALL */ + 242, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ + 240, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + 240, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + 252, /* (94) values ::= VALUES LP nexprlist RP */ + 252, /* (95) values ::= values COMMA LP nexprlist RP */ + 243, /* (96) distinct ::= DISTINCT */ + 243, /* (97) distinct ::= ALL */ + 243, /* (98) distinct ::= */ + 254, /* (99) sclp ::= */ + 244, /* (100) selcollist ::= sclp scanpt expr scanpt as */ + 244, /* (101) selcollist ::= sclp scanpt STAR */ + 244, /* (102) selcollist ::= sclp scanpt nm DOT STAR */ + 255, /* (103) as ::= AS nm */ + 255, /* (104) as ::= */ + 245, /* (105) from ::= */ + 245, /* (106) from ::= FROM seltablist */ + 257, /* (107) stl_prefix ::= seltablist joinop */ + 257, /* (108) stl_prefix ::= */ + 256, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */ + 256, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + 256, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + 256, /* (112) seltablist ::= stl_prefix LP select RP as on_using */ + 256, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */ + 200, /* (114) dbnm ::= */ + 200, /* (115) dbnm ::= DOT nm */ + 238, /* (116) fullname ::= nm */ + 238, /* (117) fullname ::= nm DOT nm */ + 262, /* (118) xfullname ::= nm */ + 262, /* (119) xfullname ::= nm DOT nm */ + 262, /* (120) xfullname ::= nm DOT nm AS nm */ + 262, /* (121) xfullname ::= nm AS nm */ + 258, /* (122) joinop ::= COMMA|JOIN */ + 258, /* (123) joinop ::= JOIN_KW JOIN */ + 258, /* (124) joinop ::= JOIN_KW nm JOIN */ + 258, /* (125) joinop ::= JOIN_KW nm nm JOIN */ + 259, /* (126) on_using ::= ON expr */ + 259, /* (127) on_using ::= USING LP idlist RP */ + 259, /* (128) on_using ::= */ + 264, /* (129) indexed_opt ::= */ + 260, /* (130) indexed_by ::= INDEXED BY nm */ + 260, /* (131) indexed_by ::= NOT INDEXED */ + 249, /* (132) orderby_opt ::= */ + 249, /* (133) orderby_opt ::= ORDER BY sortlist */ + 231, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */ + 231, /* (135) sortlist ::= expr sortorder nulls */ + 219, /* (136) sortorder ::= ASC */ + 219, /* (137) sortorder ::= DESC */ + 219, /* (138) sortorder ::= */ + 265, /* (139) nulls ::= NULLS FIRST */ + 265, /* (140) nulls ::= NULLS LAST */ + 265, /* (141) nulls ::= */ + 247, /* (142) groupby_opt ::= */ + 247, /* (143) groupby_opt ::= GROUP BY nexprlist */ + 248, /* (144) having_opt ::= */ + 248, /* (145) having_opt ::= HAVING expr */ + 250, /* (146) limit_opt ::= */ + 250, /* (147) limit_opt ::= LIMIT expr */ + 250, /* (148) limit_opt ::= LIMIT expr OFFSET expr */ + 250, /* (149) limit_opt ::= LIMIT expr COMMA expr */ + 190, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret orderby_opt limit_opt */ + 246, /* (151) where_opt ::= */ + 246, /* (152) where_opt ::= WHERE expr */ + 267, /* (153) where_opt_ret ::= */ + 267, /* (154) where_opt_ret ::= WHERE expr */ + 267, /* (155) where_opt_ret ::= RETURNING selcollist */ + 267, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */ + 190, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret orderby_opt limit_opt */ + 268, /* (158) setlist ::= setlist COMMA nm EQ expr */ + 268, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */ + 268, /* (160) setlist ::= nm EQ expr */ + 268, /* (161) setlist ::= LP idlist RP EQ expr */ + 190, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + 190, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + 271, /* (164) upsert ::= */ + 271, /* (165) upsert ::= RETURNING selcollist */ + 271, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ + 271, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ + 271, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */ + 271, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ + 272, /* (170) returning ::= RETURNING selcollist */ + 269, /* (171) insert_cmd ::= INSERT orconf */ + 269, /* (172) insert_cmd ::= REPLACE */ + 270, /* (173) idlist_opt ::= */ + 270, /* (174) idlist_opt ::= LP idlist RP */ + 263, /* (175) idlist ::= idlist COMMA nm */ + 263, /* (176) idlist ::= nm */ + 217, /* (177) expr ::= LP expr RP */ + 217, /* (178) expr ::= ID|INDEXED */ + 217, /* (179) expr ::= JOIN_KW */ + 217, /* (180) expr ::= nm DOT nm */ + 217, /* (181) expr ::= nm DOT nm DOT nm */ + 216, /* (182) term ::= NULL|FLOAT|BLOB */ + 216, /* (183) term ::= STRING */ + 216, /* (184) term ::= INTEGER */ + 217, /* (185) expr ::= VARIABLE */ + 217, /* (186) expr ::= expr COLLATE ID|STRING */ + 217, /* (187) expr ::= CAST LP expr AS typetoken RP */ + 217, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP */ + 217, /* (189) expr ::= ID|INDEXED LP STAR RP */ + 217, /* (190) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ + 217, /* (191) expr ::= ID|INDEXED LP STAR RP filter_over */ + 216, /* (192) term ::= CTIME_KW */ + 217, /* (193) expr ::= LP nexprlist COMMA expr RP */ + 217, /* (194) expr ::= expr AND expr */ + 217, /* (195) expr ::= expr OR expr */ + 217, /* (196) expr ::= expr LT|GT|GE|LE expr */ + 217, /* (197) expr ::= expr EQ|NE expr */ + 217, /* (198) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + 217, /* (199) expr ::= expr PLUS|MINUS expr */ + 217, /* (200) expr ::= expr STAR|SLASH|REM expr */ + 217, /* (201) expr ::= expr CONCAT expr */ + 274, /* (202) likeop ::= NOT LIKE_KW|MATCH */ + 217, /* (203) expr ::= expr likeop expr */ + 217, /* (204) expr ::= expr likeop expr ESCAPE expr */ + 217, /* (205) expr ::= expr ISNULL|NOTNULL */ + 217, /* (206) expr ::= expr NOT NULL */ + 217, /* (207) expr ::= expr IS expr */ + 217, /* (208) expr ::= expr IS NOT expr */ + 217, /* (209) expr ::= expr IS NOT DISTINCT FROM expr */ + 217, /* (210) expr ::= expr IS DISTINCT FROM expr */ + 217, /* (211) expr ::= NOT expr */ + 217, /* (212) expr ::= BITNOT expr */ + 217, /* (213) expr ::= PLUS|MINUS expr */ + 217, /* (214) expr ::= expr PTR expr */ + 275, /* (215) between_op ::= BETWEEN */ + 275, /* (216) between_op ::= NOT BETWEEN */ + 217, /* (217) expr ::= expr between_op expr AND expr */ + 276, /* (218) in_op ::= IN */ + 276, /* (219) in_op ::= NOT IN */ + 217, /* (220) expr ::= expr in_op LP exprlist RP */ + 217, /* (221) expr ::= LP select RP */ + 217, /* (222) expr ::= expr in_op LP select RP */ + 217, /* (223) expr ::= expr in_op nm dbnm paren_exprlist */ + 217, /* (224) expr ::= EXISTS LP select RP */ + 217, /* (225) expr ::= CASE case_operand case_exprlist case_else END */ + 279, /* (226) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + 279, /* (227) case_exprlist ::= WHEN expr THEN expr */ + 280, /* (228) case_else ::= ELSE expr */ + 280, /* (229) case_else ::= */ + 278, /* (230) case_operand ::= expr */ + 278, /* (231) case_operand ::= */ + 261, /* (232) exprlist ::= */ + 253, /* (233) nexprlist ::= nexprlist COMMA expr */ + 253, /* (234) nexprlist ::= expr */ + 277, /* (235) paren_exprlist ::= */ + 277, /* (236) paren_exprlist ::= LP exprlist RP */ + 190, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + 281, /* (238) uniqueflag ::= UNIQUE */ + 281, /* (239) uniqueflag ::= */ + 221, /* (240) eidlist_opt ::= */ + 221, /* (241) eidlist_opt ::= LP eidlist RP */ + 232, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */ + 232, /* (243) eidlist ::= nm collate sortorder */ + 282, /* (244) collate ::= */ + 282, /* (245) collate ::= COLLATE ID|STRING */ + 190, /* (246) cmd ::= DROP INDEX ifexists fullname */ + 190, /* (247) cmd ::= VACUUM vinto */ + 190, /* (248) cmd ::= VACUUM nm vinto */ + 283, /* (249) vinto ::= INTO expr */ + 283, /* (250) vinto ::= */ + 190, /* (251) cmd ::= PRAGMA nm dbnm */ + 190, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */ + 190, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + 190, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */ + 190, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + 211, /* (256) plus_num ::= PLUS INTEGER|FLOAT */ + 212, /* (257) minus_num ::= MINUS INTEGER|FLOAT */ + 190, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + 285, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + 287, /* (260) trigger_time ::= BEFORE|AFTER */ + 287, /* (261) trigger_time ::= INSTEAD OF */ + 287, /* (262) trigger_time ::= */ + 288, /* (263) trigger_event ::= DELETE|INSERT */ + 288, /* (264) trigger_event ::= UPDATE */ + 288, /* (265) trigger_event ::= UPDATE OF idlist */ + 290, /* (266) when_clause ::= */ + 290, /* (267) when_clause ::= WHEN expr */ + 286, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + 286, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */ + 292, /* (270) trnm ::= nm DOT nm */ + 293, /* (271) tridxby ::= INDEXED BY nm */ + 293, /* (272) tridxby ::= NOT INDEXED */ + 291, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + 291, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + 291, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + 291, /* (276) trigger_cmd ::= scanpt select scanpt */ + 217, /* (277) expr ::= RAISE LP IGNORE RP */ + 217, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */ + 236, /* (279) raisetype ::= ROLLBACK */ + 236, /* (280) raisetype ::= ABORT */ + 236, /* (281) raisetype ::= FAIL */ + 190, /* (282) cmd ::= DROP TRIGGER ifexists fullname */ + 190, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 190, /* (284) cmd ::= DETACH database_kw_opt expr */ + 295, /* (285) key_opt ::= */ + 295, /* (286) key_opt ::= KEY expr */ + 190, /* (287) cmd ::= REINDEX */ + 190, /* (288) cmd ::= REINDEX nm dbnm */ + 190, /* (289) cmd ::= ANALYZE */ + 190, /* (290) cmd ::= ANALYZE nm dbnm */ + 190, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 190, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 190, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + 296, /* (294) add_column_fullname ::= fullname */ + 190, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 190, /* (296) cmd ::= create_vtab */ + 190, /* (297) cmd ::= create_vtab LP vtabarglist RP */ + 298, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 300, /* (299) vtabarg ::= */ + 301, /* (300) vtabargtoken ::= ANY */ + 301, /* (301) vtabargtoken ::= lp anylist RP */ + 302, /* (302) lp ::= LP */ + 266, /* (303) with ::= WITH wqlist */ + 266, /* (304) with ::= WITH RECURSIVE wqlist */ + 305, /* (305) wqas ::= AS */ + 305, /* (306) wqas ::= AS MATERIALIZED */ + 305, /* (307) wqas ::= AS NOT MATERIALIZED */ + 304, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */ + 241, /* (309) wqlist ::= wqitem */ + 241, /* (310) wqlist ::= wqlist COMMA wqitem */ + 306, /* (311) windowdefn_list ::= windowdefn */ + 306, /* (312) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 307, /* (313) windowdefn ::= nm AS LP window RP */ + 308, /* (314) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (315) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (316) window ::= ORDER BY sortlist frame_opt */ + 308, /* (317) window ::= nm ORDER BY sortlist frame_opt */ + 308, /* (318) window ::= frame_opt */ + 308, /* (319) window ::= nm frame_opt */ + 309, /* (320) frame_opt ::= */ + 309, /* (321) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + 309, /* (322) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + 313, /* (323) range_or_rows ::= RANGE|ROWS|GROUPS */ + 315, /* (324) frame_bound_s ::= frame_bound */ + 315, /* (325) frame_bound_s ::= UNBOUNDED PRECEDING */ + 316, /* (326) frame_bound_e ::= frame_bound */ + 316, /* (327) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 314, /* (328) frame_bound ::= expr PRECEDING|FOLLOWING */ + 314, /* (329) frame_bound ::= CURRENT ROW */ + 317, /* (330) frame_exclude_opt ::= */ + 317, /* (331) frame_exclude_opt ::= EXCLUDE frame_exclude */ + 318, /* (332) frame_exclude ::= NO OTHERS */ + 318, /* (333) frame_exclude ::= CURRENT ROW */ + 318, /* (334) frame_exclude ::= GROUP|TIES */ + 251, /* (335) window_clause ::= WINDOW windowdefn_list */ + 273, /* (336) filter_over ::= filter_clause over_clause */ + 273, /* (337) filter_over ::= over_clause */ + 273, /* (338) filter_over ::= filter_clause */ + 312, /* (339) over_clause ::= OVER LP window RP */ + 312, /* (340) over_clause ::= OVER nm */ + 311, /* (341) filter_clause ::= FILTER LP WHERE expr RP */ + 185, /* (342) input ::= cmdlist */ + 186, /* (343) cmdlist ::= cmdlist ecmd */ + 186, /* (344) cmdlist ::= ecmd */ + 187, /* (345) ecmd ::= SEMI */ + 187, /* (346) ecmd ::= cmdx SEMI */ + 187, /* (347) ecmd ::= explain cmdx SEMI */ + 192, /* (348) trans_opt ::= */ + 192, /* (349) trans_opt ::= TRANSACTION */ + 192, /* (350) trans_opt ::= TRANSACTION nm */ + 194, /* (351) savepoint_opt ::= SAVEPOINT */ + 194, /* (352) savepoint_opt ::= */ + 190, /* (353) cmd ::= create_table create_table_args */ + 203, /* (354) table_option_set ::= table_option */ + 201, /* (355) columnlist ::= columnlist COMMA columnname carglist */ + 201, /* (356) columnlist ::= columnname carglist */ + 193, /* (357) nm ::= ID|INDEXED */ + 193, /* (358) nm ::= STRING */ + 193, /* (359) nm ::= JOIN_KW */ + 208, /* (360) typetoken ::= typename */ + 209, /* (361) typename ::= ID|STRING */ + 210, /* (362) signed ::= plus_num */ + 210, /* (363) signed ::= minus_num */ + 207, /* (364) carglist ::= carglist ccons */ + 207, /* (365) carglist ::= */ + 215, /* (366) ccons ::= NULL onconf */ + 215, /* (367) ccons ::= GENERATED ALWAYS AS generated */ + 215, /* (368) ccons ::= AS generated */ + 202, /* (369) conslist_opt ::= COMMA conslist */ + 228, /* (370) conslist ::= conslist tconscomma tcons */ + 228, /* (371) conslist ::= tcons */ + 229, /* (372) tconscomma ::= */ + 233, /* (373) defer_subclause_opt ::= defer_subclause */ + 235, /* (374) resolvetype ::= raisetype */ + 239, /* (375) selectnowith ::= oneselect */ + 240, /* (376) oneselect ::= values */ + 254, /* (377) sclp ::= selcollist COMMA */ + 255, /* (378) as ::= ID|STRING */ + 264, /* (379) indexed_opt ::= indexed_by */ + 272, /* (380) returning ::= */ + 217, /* (381) expr ::= term */ + 274, /* (382) likeop ::= LIKE_KW|MATCH */ + 261, /* (383) exprlist ::= nexprlist */ + 284, /* (384) nmnum ::= plus_num */ + 284, /* (385) nmnum ::= nm */ + 284, /* (386) nmnum ::= ON */ + 284, /* (387) nmnum ::= DELETE */ + 284, /* (388) nmnum ::= DEFAULT */ + 211, /* (389) plus_num ::= INTEGER|FLOAT */ + 289, /* (390) foreach_clause ::= */ + 289, /* (391) foreach_clause ::= FOR EACH ROW */ + 292, /* (392) trnm ::= nm */ + 293, /* (393) tridxby ::= */ + 294, /* (394) database_kw_opt ::= DATABASE */ + 294, /* (395) database_kw_opt ::= */ + 297, /* (396) kwcolumn_opt ::= */ + 297, /* (397) kwcolumn_opt ::= COLUMNKW */ + 299, /* (398) vtabarglist ::= vtabarg */ + 299, /* (399) vtabarglist ::= vtabarglist COMMA vtabarg */ + 300, /* (400) vtabarg ::= vtabarg vtabargtoken */ + 303, /* (401) anylist ::= */ + 303, /* (402) anylist ::= anylist LP anylist RP */ + 303, /* (403) anylist ::= anylist ANY */ + 266, /* (404) with ::= */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number @@ -162771,372 +172042,392 @@ static const signed char yyRuleInfoNRhs[] = { -3, /* (16) ifnotexists ::= IF NOT EXISTS */ -1, /* (17) temp ::= TEMP */ 0, /* (18) temp ::= */ - -5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ + -5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ -2, /* (20) create_table_args ::= AS select */ - 0, /* (21) table_options ::= */ - -2, /* (22) table_options ::= WITHOUT nm */ - -2, /* (23) columnname ::= nm typetoken */ - 0, /* (24) typetoken ::= */ - -4, /* (25) typetoken ::= typename LP signed RP */ - -6, /* (26) typetoken ::= typename LP signed COMMA signed RP */ - -2, /* (27) typename ::= typename ID|STRING */ - 0, /* (28) scanpt ::= */ - 0, /* (29) scantok ::= */ - -2, /* (30) ccons ::= CONSTRAINT nm */ - -3, /* (31) ccons ::= DEFAULT scantok term */ - -4, /* (32) ccons ::= DEFAULT LP expr RP */ - -4, /* (33) ccons ::= DEFAULT PLUS scantok term */ - -4, /* (34) ccons ::= DEFAULT MINUS scantok term */ - -3, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */ - -3, /* (36) ccons ::= NOT NULL onconf */ - -5, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */ - -2, /* (38) ccons ::= UNIQUE onconf */ - -4, /* (39) ccons ::= CHECK LP expr RP */ - -4, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */ - -1, /* (41) ccons ::= defer_subclause */ - -2, /* (42) ccons ::= COLLATE ID|STRING */ - -3, /* (43) generated ::= LP expr RP */ - -4, /* (44) generated ::= LP expr RP ID */ - 0, /* (45) autoinc ::= */ - -1, /* (46) autoinc ::= AUTOINCR */ - 0, /* (47) refargs ::= */ - -2, /* (48) refargs ::= refargs refarg */ - -2, /* (49) refarg ::= MATCH nm */ - -3, /* (50) refarg ::= ON INSERT refact */ - -3, /* (51) refarg ::= ON DELETE refact */ - -3, /* (52) refarg ::= ON UPDATE refact */ - -2, /* (53) refact ::= SET NULL */ - -2, /* (54) refact ::= SET DEFAULT */ - -1, /* (55) refact ::= CASCADE */ - -1, /* (56) refact ::= RESTRICT */ - -2, /* (57) refact ::= NO ACTION */ - -3, /* (58) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ - -2, /* (59) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - 0, /* (60) init_deferred_pred_opt ::= */ - -2, /* (61) init_deferred_pred_opt ::= INITIALLY DEFERRED */ - -2, /* (62) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ - 0, /* (63) conslist_opt ::= */ - -1, /* (64) tconscomma ::= COMMA */ - -2, /* (65) tcons ::= CONSTRAINT nm */ - -7, /* (66) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ - -5, /* (67) tcons ::= UNIQUE LP sortlist RP onconf */ - -5, /* (68) tcons ::= CHECK LP expr RP onconf */ - -10, /* (69) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ - 0, /* (70) defer_subclause_opt ::= */ - 0, /* (71) onconf ::= */ - -3, /* (72) onconf ::= ON CONFLICT resolvetype */ - 0, /* (73) orconf ::= */ - -2, /* (74) orconf ::= OR resolvetype */ - -1, /* (75) resolvetype ::= IGNORE */ - -1, /* (76) resolvetype ::= REPLACE */ - -4, /* (77) cmd ::= DROP TABLE ifexists fullname */ - -2, /* (78) ifexists ::= IF EXISTS */ - 0, /* (79) ifexists ::= */ - -9, /* (80) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ - -4, /* (81) cmd ::= DROP VIEW ifexists fullname */ - -1, /* (82) cmd ::= select */ - -3, /* (83) select ::= WITH wqlist selectnowith */ - -4, /* (84) select ::= WITH RECURSIVE wqlist selectnowith */ - -1, /* (85) select ::= selectnowith */ - -3, /* (86) selectnowith ::= selectnowith multiselect_op oneselect */ - -1, /* (87) multiselect_op ::= UNION */ - -2, /* (88) multiselect_op ::= UNION ALL */ - -1, /* (89) multiselect_op ::= EXCEPT|INTERSECT */ - -9, /* (90) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - -10, /* (91) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ - -4, /* (92) values ::= VALUES LP nexprlist RP */ - -5, /* (93) values ::= values COMMA LP nexprlist RP */ - -1, /* (94) distinct ::= DISTINCT */ - -1, /* (95) distinct ::= ALL */ - 0, /* (96) distinct ::= */ - 0, /* (97) sclp ::= */ - -5, /* (98) selcollist ::= sclp scanpt expr scanpt as */ - -3, /* (99) selcollist ::= sclp scanpt STAR */ - -5, /* (100) selcollist ::= sclp scanpt nm DOT STAR */ - -2, /* (101) as ::= AS nm */ - 0, /* (102) as ::= */ - 0, /* (103) from ::= */ - -2, /* (104) from ::= FROM seltablist */ - -2, /* (105) stl_prefix ::= seltablist joinop */ - 0, /* (106) stl_prefix ::= */ - -7, /* (107) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ - -9, /* (108) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ - -7, /* (109) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ - -7, /* (110) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ - 0, /* (111) dbnm ::= */ - -2, /* (112) dbnm ::= DOT nm */ - -1, /* (113) fullname ::= nm */ - -3, /* (114) fullname ::= nm DOT nm */ - -1, /* (115) xfullname ::= nm */ - -3, /* (116) xfullname ::= nm DOT nm */ - -5, /* (117) xfullname ::= nm DOT nm AS nm */ - -3, /* (118) xfullname ::= nm AS nm */ - -1, /* (119) joinop ::= COMMA|JOIN */ - -2, /* (120) joinop ::= JOIN_KW JOIN */ - -3, /* (121) joinop ::= JOIN_KW nm JOIN */ - -4, /* (122) joinop ::= JOIN_KW nm nm JOIN */ - -2, /* (123) on_opt ::= ON expr */ - 0, /* (124) on_opt ::= */ - 0, /* (125) indexed_opt ::= */ - -3, /* (126) indexed_opt ::= INDEXED BY nm */ - -2, /* (127) indexed_opt ::= NOT INDEXED */ - -4, /* (128) using_opt ::= USING LP idlist RP */ - 0, /* (129) using_opt ::= */ - 0, /* (130) orderby_opt ::= */ - -3, /* (131) orderby_opt ::= ORDER BY sortlist */ - -5, /* (132) sortlist ::= sortlist COMMA expr sortorder nulls */ - -3, /* (133) sortlist ::= expr sortorder nulls */ - -1, /* (134) sortorder ::= ASC */ - -1, /* (135) sortorder ::= DESC */ - 0, /* (136) sortorder ::= */ - -2, /* (137) nulls ::= NULLS FIRST */ - -2, /* (138) nulls ::= NULLS LAST */ - 0, /* (139) nulls ::= */ - 0, /* (140) groupby_opt ::= */ - -3, /* (141) groupby_opt ::= GROUP BY nexprlist */ - 0, /* (142) having_opt ::= */ - -2, /* (143) having_opt ::= HAVING expr */ - 0, /* (144) limit_opt ::= */ - -2, /* (145) limit_opt ::= LIMIT expr */ - -4, /* (146) limit_opt ::= LIMIT expr OFFSET expr */ - -4, /* (147) limit_opt ::= LIMIT expr COMMA expr */ - -8, /* (148) cmd ::= with DELETE FROM xfullname indexed_opt where_opt orderby_opt limit_opt */ - 0, /* (149) where_opt ::= */ - -2, /* (150) where_opt ::= WHERE expr */ - -11, /* (151) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt orderby_opt limit_opt */ - -5, /* (152) setlist ::= setlist COMMA nm EQ expr */ - -7, /* (153) setlist ::= setlist COMMA LP idlist RP EQ expr */ - -3, /* (154) setlist ::= nm EQ expr */ - -5, /* (155) setlist ::= LP idlist RP EQ expr */ - -7, /* (156) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - -7, /* (157) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ - 0, /* (158) upsert ::= */ - -11, /* (159) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ - -8, /* (160) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ - -4, /* (161) upsert ::= ON CONFLICT DO NOTHING */ - -2, /* (162) insert_cmd ::= INSERT orconf */ - -1, /* (163) insert_cmd ::= REPLACE */ - 0, /* (164) idlist_opt ::= */ - -3, /* (165) idlist_opt ::= LP idlist RP */ - -3, /* (166) idlist ::= idlist COMMA nm */ - -1, /* (167) idlist ::= nm */ - -3, /* (168) expr ::= LP expr RP */ - -1, /* (169) expr ::= ID|INDEXED */ - -1, /* (170) expr ::= JOIN_KW */ - -3, /* (171) expr ::= nm DOT nm */ - -5, /* (172) expr ::= nm DOT nm DOT nm */ - -1, /* (173) term ::= NULL|FLOAT|BLOB */ - -1, /* (174) term ::= STRING */ - -1, /* (175) term ::= INTEGER */ - -1, /* (176) expr ::= VARIABLE */ - -3, /* (177) expr ::= expr COLLATE ID|STRING */ - -6, /* (178) expr ::= CAST LP expr AS typetoken RP */ - -5, /* (179) expr ::= ID|INDEXED LP distinct exprlist RP */ - -4, /* (180) expr ::= ID|INDEXED LP STAR RP */ - -6, /* (181) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ - -5, /* (182) expr ::= ID|INDEXED LP STAR RP filter_over */ - -1, /* (183) term ::= CTIME_KW */ - -5, /* (184) expr ::= LP nexprlist COMMA expr RP */ - -3, /* (185) expr ::= expr AND expr */ - -3, /* (186) expr ::= expr OR expr */ - -3, /* (187) expr ::= expr LT|GT|GE|LE expr */ - -3, /* (188) expr ::= expr EQ|NE expr */ - -3, /* (189) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - -3, /* (190) expr ::= expr PLUS|MINUS expr */ - -3, /* (191) expr ::= expr STAR|SLASH|REM expr */ - -3, /* (192) expr ::= expr CONCAT expr */ - -2, /* (193) likeop ::= NOT LIKE_KW|MATCH */ - -3, /* (194) expr ::= expr likeop expr */ - -5, /* (195) expr ::= expr likeop expr ESCAPE expr */ - -2, /* (196) expr ::= expr ISNULL|NOTNULL */ - -3, /* (197) expr ::= expr NOT NULL */ - -3, /* (198) expr ::= expr IS expr */ - -4, /* (199) expr ::= expr IS NOT expr */ - -2, /* (200) expr ::= NOT expr */ - -2, /* (201) expr ::= BITNOT expr */ - -2, /* (202) expr ::= PLUS|MINUS expr */ - -1, /* (203) between_op ::= BETWEEN */ - -2, /* (204) between_op ::= NOT BETWEEN */ - -5, /* (205) expr ::= expr between_op expr AND expr */ - -1, /* (206) in_op ::= IN */ - -2, /* (207) in_op ::= NOT IN */ - -5, /* (208) expr ::= expr in_op LP exprlist RP */ - -3, /* (209) expr ::= LP select RP */ - -5, /* (210) expr ::= expr in_op LP select RP */ - -5, /* (211) expr ::= expr in_op nm dbnm paren_exprlist */ - -4, /* (212) expr ::= EXISTS LP select RP */ - -5, /* (213) expr ::= CASE case_operand case_exprlist case_else END */ - -5, /* (214) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - -4, /* (215) case_exprlist ::= WHEN expr THEN expr */ - -2, /* (216) case_else ::= ELSE expr */ - 0, /* (217) case_else ::= */ - -1, /* (218) case_operand ::= expr */ - 0, /* (219) case_operand ::= */ - 0, /* (220) exprlist ::= */ - -3, /* (221) nexprlist ::= nexprlist COMMA expr */ - -1, /* (222) nexprlist ::= expr */ - 0, /* (223) paren_exprlist ::= */ - -3, /* (224) paren_exprlist ::= LP exprlist RP */ - -12, /* (225) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - -1, /* (226) uniqueflag ::= UNIQUE */ - 0, /* (227) uniqueflag ::= */ - 0, /* (228) eidlist_opt ::= */ - -3, /* (229) eidlist_opt ::= LP eidlist RP */ - -5, /* (230) eidlist ::= eidlist COMMA nm collate sortorder */ - -3, /* (231) eidlist ::= nm collate sortorder */ - 0, /* (232) collate ::= */ - -2, /* (233) collate ::= COLLATE ID|STRING */ - -4, /* (234) cmd ::= DROP INDEX ifexists fullname */ - -2, /* (235) cmd ::= VACUUM vinto */ - -3, /* (236) cmd ::= VACUUM nm vinto */ - -2, /* (237) vinto ::= INTO expr */ - 0, /* (238) vinto ::= */ - -3, /* (239) cmd ::= PRAGMA nm dbnm */ - -5, /* (240) cmd ::= PRAGMA nm dbnm EQ nmnum */ - -6, /* (241) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - -5, /* (242) cmd ::= PRAGMA nm dbnm EQ minus_num */ - -6, /* (243) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - -2, /* (244) plus_num ::= PLUS INTEGER|FLOAT */ - -2, /* (245) minus_num ::= MINUS INTEGER|FLOAT */ - -5, /* (246) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - -11, /* (247) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - -1, /* (248) trigger_time ::= BEFORE|AFTER */ - -2, /* (249) trigger_time ::= INSTEAD OF */ - 0, /* (250) trigger_time ::= */ - -1, /* (251) trigger_event ::= DELETE|INSERT */ - -1, /* (252) trigger_event ::= UPDATE */ - -3, /* (253) trigger_event ::= UPDATE OF idlist */ - 0, /* (254) when_clause ::= */ - -2, /* (255) when_clause ::= WHEN expr */ - -3, /* (256) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - -2, /* (257) trigger_cmd_list ::= trigger_cmd SEMI */ - -3, /* (258) trnm ::= nm DOT nm */ - -3, /* (259) tridxby ::= INDEXED BY nm */ - -2, /* (260) tridxby ::= NOT INDEXED */ - -9, /* (261) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ - -8, /* (262) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - -6, /* (263) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - -3, /* (264) trigger_cmd ::= scanpt select scanpt */ - -4, /* (265) expr ::= RAISE LP IGNORE RP */ - -6, /* (266) expr ::= RAISE LP raisetype COMMA nm RP */ - -1, /* (267) raisetype ::= ROLLBACK */ - -1, /* (268) raisetype ::= ABORT */ - -1, /* (269) raisetype ::= FAIL */ - -4, /* (270) cmd ::= DROP TRIGGER ifexists fullname */ - -6, /* (271) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - -3, /* (272) cmd ::= DETACH database_kw_opt expr */ - 0, /* (273) key_opt ::= */ - -2, /* (274) key_opt ::= KEY expr */ - -1, /* (275) cmd ::= REINDEX */ - -3, /* (276) cmd ::= REINDEX nm dbnm */ - -1, /* (277) cmd ::= ANALYZE */ - -3, /* (278) cmd ::= ANALYZE nm dbnm */ - -6, /* (279) cmd ::= ALTER TABLE fullname RENAME TO nm */ - -7, /* (280) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - -1, /* (281) add_column_fullname ::= fullname */ - -8, /* (282) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - -1, /* (283) cmd ::= create_vtab */ - -4, /* (284) cmd ::= create_vtab LP vtabarglist RP */ - -8, /* (285) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 0, /* (286) vtabarg ::= */ - -1, /* (287) vtabargtoken ::= ANY */ - -3, /* (288) vtabargtoken ::= lp anylist RP */ - -1, /* (289) lp ::= LP */ - -2, /* (290) with ::= WITH wqlist */ - -3, /* (291) with ::= WITH RECURSIVE wqlist */ - -6, /* (292) wqlist ::= nm eidlist_opt AS LP select RP */ - -8, /* (293) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ - -1, /* (294) windowdefn_list ::= windowdefn */ - -3, /* (295) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - -5, /* (296) windowdefn ::= nm AS LP window RP */ - -5, /* (297) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - -6, /* (298) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - -4, /* (299) window ::= ORDER BY sortlist frame_opt */ - -5, /* (300) window ::= nm ORDER BY sortlist frame_opt */ - -1, /* (301) window ::= frame_opt */ - -2, /* (302) window ::= nm frame_opt */ - 0, /* (303) frame_opt ::= */ - -3, /* (304) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - -6, /* (305) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - -1, /* (306) range_or_rows ::= RANGE|ROWS|GROUPS */ - -1, /* (307) frame_bound_s ::= frame_bound */ - -2, /* (308) frame_bound_s ::= UNBOUNDED PRECEDING */ - -1, /* (309) frame_bound_e ::= frame_bound */ - -2, /* (310) frame_bound_e ::= UNBOUNDED FOLLOWING */ - -2, /* (311) frame_bound ::= expr PRECEDING|FOLLOWING */ - -2, /* (312) frame_bound ::= CURRENT ROW */ - 0, /* (313) frame_exclude_opt ::= */ - -2, /* (314) frame_exclude_opt ::= EXCLUDE frame_exclude */ - -2, /* (315) frame_exclude ::= NO OTHERS */ - -2, /* (316) frame_exclude ::= CURRENT ROW */ - -1, /* (317) frame_exclude ::= GROUP|TIES */ - -2, /* (318) window_clause ::= WINDOW windowdefn_list */ - -2, /* (319) filter_over ::= filter_clause over_clause */ - -1, /* (320) filter_over ::= over_clause */ - -1, /* (321) filter_over ::= filter_clause */ - -4, /* (322) over_clause ::= OVER LP window RP */ - -2, /* (323) over_clause ::= OVER nm */ - -5, /* (324) filter_clause ::= FILTER LP WHERE expr RP */ - -1, /* (325) input ::= cmdlist */ - -2, /* (326) cmdlist ::= cmdlist ecmd */ - -1, /* (327) cmdlist ::= ecmd */ - -1, /* (328) ecmd ::= SEMI */ - -2, /* (329) ecmd ::= cmdx SEMI */ - -3, /* (330) ecmd ::= explain cmdx SEMI */ - 0, /* (331) trans_opt ::= */ - -1, /* (332) trans_opt ::= TRANSACTION */ - -2, /* (333) trans_opt ::= TRANSACTION nm */ - -1, /* (334) savepoint_opt ::= SAVEPOINT */ - 0, /* (335) savepoint_opt ::= */ - -2, /* (336) cmd ::= create_table create_table_args */ - -4, /* (337) columnlist ::= columnlist COMMA columnname carglist */ - -2, /* (338) columnlist ::= columnname carglist */ - -1, /* (339) nm ::= ID|INDEXED */ - -1, /* (340) nm ::= STRING */ - -1, /* (341) nm ::= JOIN_KW */ - -1, /* (342) typetoken ::= typename */ - -1, /* (343) typename ::= ID|STRING */ - -1, /* (344) signed ::= plus_num */ - -1, /* (345) signed ::= minus_num */ - -2, /* (346) carglist ::= carglist ccons */ - 0, /* (347) carglist ::= */ - -2, /* (348) ccons ::= NULL onconf */ - -4, /* (349) ccons ::= GENERATED ALWAYS AS generated */ - -2, /* (350) ccons ::= AS generated */ - -2, /* (351) conslist_opt ::= COMMA conslist */ - -3, /* (352) conslist ::= conslist tconscomma tcons */ - -1, /* (353) conslist ::= tcons */ - 0, /* (354) tconscomma ::= */ - -1, /* (355) defer_subclause_opt ::= defer_subclause */ - -1, /* (356) resolvetype ::= raisetype */ - -1, /* (357) selectnowith ::= oneselect */ - -1, /* (358) oneselect ::= values */ - -2, /* (359) sclp ::= selcollist COMMA */ - -1, /* (360) as ::= ID|STRING */ - -1, /* (361) expr ::= term */ - -1, /* (362) likeop ::= LIKE_KW|MATCH */ - -1, /* (363) exprlist ::= nexprlist */ - -1, /* (364) nmnum ::= plus_num */ - -1, /* (365) nmnum ::= nm */ - -1, /* (366) nmnum ::= ON */ - -1, /* (367) nmnum ::= DELETE */ - -1, /* (368) nmnum ::= DEFAULT */ - -1, /* (369) plus_num ::= INTEGER|FLOAT */ - 0, /* (370) foreach_clause ::= */ - -3, /* (371) foreach_clause ::= FOR EACH ROW */ - -1, /* (372) trnm ::= nm */ - 0, /* (373) tridxby ::= */ - -1, /* (374) database_kw_opt ::= DATABASE */ - 0, /* (375) database_kw_opt ::= */ - 0, /* (376) kwcolumn_opt ::= */ - -1, /* (377) kwcolumn_opt ::= COLUMNKW */ - -1, /* (378) vtabarglist ::= vtabarg */ - -3, /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */ - -2, /* (380) vtabarg ::= vtabarg vtabargtoken */ - 0, /* (381) anylist ::= */ - -4, /* (382) anylist ::= anylist LP anylist RP */ - -2, /* (383) anylist ::= anylist ANY */ - 0, /* (384) with ::= */ + 0, /* (21) table_option_set ::= */ + -3, /* (22) table_option_set ::= table_option_set COMMA table_option */ + -2, /* (23) table_option ::= WITHOUT nm */ + -1, /* (24) table_option ::= nm */ + -2, /* (25) columnname ::= nm typetoken */ + 0, /* (26) typetoken ::= */ + -4, /* (27) typetoken ::= typename LP signed RP */ + -6, /* (28) typetoken ::= typename LP signed COMMA signed RP */ + -2, /* (29) typename ::= typename ID|STRING */ + 0, /* (30) scanpt ::= */ + 0, /* (31) scantok ::= */ + -2, /* (32) ccons ::= CONSTRAINT nm */ + -3, /* (33) ccons ::= DEFAULT scantok term */ + -4, /* (34) ccons ::= DEFAULT LP expr RP */ + -4, /* (35) ccons ::= DEFAULT PLUS scantok term */ + -4, /* (36) ccons ::= DEFAULT MINUS scantok term */ + -3, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ + -3, /* (38) ccons ::= NOT NULL onconf */ + -5, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + -2, /* (40) ccons ::= UNIQUE onconf */ + -4, /* (41) ccons ::= CHECK LP expr RP */ + -4, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ + -1, /* (43) ccons ::= defer_subclause */ + -2, /* (44) ccons ::= COLLATE ID|STRING */ + -3, /* (45) generated ::= LP expr RP */ + -4, /* (46) generated ::= LP expr RP ID */ + 0, /* (47) autoinc ::= */ + -1, /* (48) autoinc ::= AUTOINCR */ + 0, /* (49) refargs ::= */ + -2, /* (50) refargs ::= refargs refarg */ + -2, /* (51) refarg ::= MATCH nm */ + -3, /* (52) refarg ::= ON INSERT refact */ + -3, /* (53) refarg ::= ON DELETE refact */ + -3, /* (54) refarg ::= ON UPDATE refact */ + -2, /* (55) refact ::= SET NULL */ + -2, /* (56) refact ::= SET DEFAULT */ + -1, /* (57) refact ::= CASCADE */ + -1, /* (58) refact ::= RESTRICT */ + -2, /* (59) refact ::= NO ACTION */ + -3, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + -2, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 0, /* (62) init_deferred_pred_opt ::= */ + -2, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + -2, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 0, /* (65) conslist_opt ::= */ + -1, /* (66) tconscomma ::= COMMA */ + -2, /* (67) tcons ::= CONSTRAINT nm */ + -7, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + -5, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ + -5, /* (70) tcons ::= CHECK LP expr RP onconf */ + -10, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 0, /* (72) defer_subclause_opt ::= */ + 0, /* (73) onconf ::= */ + -3, /* (74) onconf ::= ON CONFLICT resolvetype */ + 0, /* (75) orconf ::= */ + -2, /* (76) orconf ::= OR resolvetype */ + -1, /* (77) resolvetype ::= IGNORE */ + -1, /* (78) resolvetype ::= REPLACE */ + -4, /* (79) cmd ::= DROP TABLE ifexists fullname */ + -2, /* (80) ifexists ::= IF EXISTS */ + 0, /* (81) ifexists ::= */ + -9, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + -4, /* (83) cmd ::= DROP VIEW ifexists fullname */ + -1, /* (84) cmd ::= select */ + -3, /* (85) select ::= WITH wqlist selectnowith */ + -4, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ + -1, /* (87) select ::= selectnowith */ + -3, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ + -1, /* (89) multiselect_op ::= UNION */ + -2, /* (90) multiselect_op ::= UNION ALL */ + -1, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ + -9, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + -10, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + -4, /* (94) values ::= VALUES LP nexprlist RP */ + -5, /* (95) values ::= values COMMA LP nexprlist RP */ + -1, /* (96) distinct ::= DISTINCT */ + -1, /* (97) distinct ::= ALL */ + 0, /* (98) distinct ::= */ + 0, /* (99) sclp ::= */ + -5, /* (100) selcollist ::= sclp scanpt expr scanpt as */ + -3, /* (101) selcollist ::= sclp scanpt STAR */ + -5, /* (102) selcollist ::= sclp scanpt nm DOT STAR */ + -2, /* (103) as ::= AS nm */ + 0, /* (104) as ::= */ + 0, /* (105) from ::= */ + -2, /* (106) from ::= FROM seltablist */ + -2, /* (107) stl_prefix ::= seltablist joinop */ + 0, /* (108) stl_prefix ::= */ + -5, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */ + -6, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + -8, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + -6, /* (112) seltablist ::= stl_prefix LP select RP as on_using */ + -6, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */ + 0, /* (114) dbnm ::= */ + -2, /* (115) dbnm ::= DOT nm */ + -1, /* (116) fullname ::= nm */ + -3, /* (117) fullname ::= nm DOT nm */ + -1, /* (118) xfullname ::= nm */ + -3, /* (119) xfullname ::= nm DOT nm */ + -5, /* (120) xfullname ::= nm DOT nm AS nm */ + -3, /* (121) xfullname ::= nm AS nm */ + -1, /* (122) joinop ::= COMMA|JOIN */ + -2, /* (123) joinop ::= JOIN_KW JOIN */ + -3, /* (124) joinop ::= JOIN_KW nm JOIN */ + -4, /* (125) joinop ::= JOIN_KW nm nm JOIN */ + -2, /* (126) on_using ::= ON expr */ + -4, /* (127) on_using ::= USING LP idlist RP */ + 0, /* (128) on_using ::= */ + 0, /* (129) indexed_opt ::= */ + -3, /* (130) indexed_by ::= INDEXED BY nm */ + -2, /* (131) indexed_by ::= NOT INDEXED */ + 0, /* (132) orderby_opt ::= */ + -3, /* (133) orderby_opt ::= ORDER BY sortlist */ + -5, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */ + -3, /* (135) sortlist ::= expr sortorder nulls */ + -1, /* (136) sortorder ::= ASC */ + -1, /* (137) sortorder ::= DESC */ + 0, /* (138) sortorder ::= */ + -2, /* (139) nulls ::= NULLS FIRST */ + -2, /* (140) nulls ::= NULLS LAST */ + 0, /* (141) nulls ::= */ + 0, /* (142) groupby_opt ::= */ + -3, /* (143) groupby_opt ::= GROUP BY nexprlist */ + 0, /* (144) having_opt ::= */ + -2, /* (145) having_opt ::= HAVING expr */ + 0, /* (146) limit_opt ::= */ + -2, /* (147) limit_opt ::= LIMIT expr */ + -4, /* (148) limit_opt ::= LIMIT expr OFFSET expr */ + -4, /* (149) limit_opt ::= LIMIT expr COMMA expr */ + -8, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret orderby_opt limit_opt */ + 0, /* (151) where_opt ::= */ + -2, /* (152) where_opt ::= WHERE expr */ + 0, /* (153) where_opt_ret ::= */ + -2, /* (154) where_opt_ret ::= WHERE expr */ + -2, /* (155) where_opt_ret ::= RETURNING selcollist */ + -4, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */ + -11, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret orderby_opt limit_opt */ + -5, /* (158) setlist ::= setlist COMMA nm EQ expr */ + -7, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */ + -3, /* (160) setlist ::= nm EQ expr */ + -5, /* (161) setlist ::= LP idlist RP EQ expr */ + -7, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + -8, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + 0, /* (164) upsert ::= */ + -2, /* (165) upsert ::= RETURNING selcollist */ + -12, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ + -9, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ + -5, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */ + -8, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ + -2, /* (170) returning ::= RETURNING selcollist */ + -2, /* (171) insert_cmd ::= INSERT orconf */ + -1, /* (172) insert_cmd ::= REPLACE */ + 0, /* (173) idlist_opt ::= */ + -3, /* (174) idlist_opt ::= LP idlist RP */ + -3, /* (175) idlist ::= idlist COMMA nm */ + -1, /* (176) idlist ::= nm */ + -3, /* (177) expr ::= LP expr RP */ + -1, /* (178) expr ::= ID|INDEXED */ + -1, /* (179) expr ::= JOIN_KW */ + -3, /* (180) expr ::= nm DOT nm */ + -5, /* (181) expr ::= nm DOT nm DOT nm */ + -1, /* (182) term ::= NULL|FLOAT|BLOB */ + -1, /* (183) term ::= STRING */ + -1, /* (184) term ::= INTEGER */ + -1, /* (185) expr ::= VARIABLE */ + -3, /* (186) expr ::= expr COLLATE ID|STRING */ + -6, /* (187) expr ::= CAST LP expr AS typetoken RP */ + -5, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP */ + -4, /* (189) expr ::= ID|INDEXED LP STAR RP */ + -6, /* (190) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ + -5, /* (191) expr ::= ID|INDEXED LP STAR RP filter_over */ + -1, /* (192) term ::= CTIME_KW */ + -5, /* (193) expr ::= LP nexprlist COMMA expr RP */ + -3, /* (194) expr ::= expr AND expr */ + -3, /* (195) expr ::= expr OR expr */ + -3, /* (196) expr ::= expr LT|GT|GE|LE expr */ + -3, /* (197) expr ::= expr EQ|NE expr */ + -3, /* (198) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + -3, /* (199) expr ::= expr PLUS|MINUS expr */ + -3, /* (200) expr ::= expr STAR|SLASH|REM expr */ + -3, /* (201) expr ::= expr CONCAT expr */ + -2, /* (202) likeop ::= NOT LIKE_KW|MATCH */ + -3, /* (203) expr ::= expr likeop expr */ + -5, /* (204) expr ::= expr likeop expr ESCAPE expr */ + -2, /* (205) expr ::= expr ISNULL|NOTNULL */ + -3, /* (206) expr ::= expr NOT NULL */ + -3, /* (207) expr ::= expr IS expr */ + -4, /* (208) expr ::= expr IS NOT expr */ + -6, /* (209) expr ::= expr IS NOT DISTINCT FROM expr */ + -5, /* (210) expr ::= expr IS DISTINCT FROM expr */ + -2, /* (211) expr ::= NOT expr */ + -2, /* (212) expr ::= BITNOT expr */ + -2, /* (213) expr ::= PLUS|MINUS expr */ + -3, /* (214) expr ::= expr PTR expr */ + -1, /* (215) between_op ::= BETWEEN */ + -2, /* (216) between_op ::= NOT BETWEEN */ + -5, /* (217) expr ::= expr between_op expr AND expr */ + -1, /* (218) in_op ::= IN */ + -2, /* (219) in_op ::= NOT IN */ + -5, /* (220) expr ::= expr in_op LP exprlist RP */ + -3, /* (221) expr ::= LP select RP */ + -5, /* (222) expr ::= expr in_op LP select RP */ + -5, /* (223) expr ::= expr in_op nm dbnm paren_exprlist */ + -4, /* (224) expr ::= EXISTS LP select RP */ + -5, /* (225) expr ::= CASE case_operand case_exprlist case_else END */ + -5, /* (226) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + -4, /* (227) case_exprlist ::= WHEN expr THEN expr */ + -2, /* (228) case_else ::= ELSE expr */ + 0, /* (229) case_else ::= */ + -1, /* (230) case_operand ::= expr */ + 0, /* (231) case_operand ::= */ + 0, /* (232) exprlist ::= */ + -3, /* (233) nexprlist ::= nexprlist COMMA expr */ + -1, /* (234) nexprlist ::= expr */ + 0, /* (235) paren_exprlist ::= */ + -3, /* (236) paren_exprlist ::= LP exprlist RP */ + -12, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + -1, /* (238) uniqueflag ::= UNIQUE */ + 0, /* (239) uniqueflag ::= */ + 0, /* (240) eidlist_opt ::= */ + -3, /* (241) eidlist_opt ::= LP eidlist RP */ + -5, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */ + -3, /* (243) eidlist ::= nm collate sortorder */ + 0, /* (244) collate ::= */ + -2, /* (245) collate ::= COLLATE ID|STRING */ + -4, /* (246) cmd ::= DROP INDEX ifexists fullname */ + -2, /* (247) cmd ::= VACUUM vinto */ + -3, /* (248) cmd ::= VACUUM nm vinto */ + -2, /* (249) vinto ::= INTO expr */ + 0, /* (250) vinto ::= */ + -3, /* (251) cmd ::= PRAGMA nm dbnm */ + -5, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */ + -6, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + -5, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */ + -6, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + -2, /* (256) plus_num ::= PLUS INTEGER|FLOAT */ + -2, /* (257) minus_num ::= MINUS INTEGER|FLOAT */ + -5, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + -11, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + -1, /* (260) trigger_time ::= BEFORE|AFTER */ + -2, /* (261) trigger_time ::= INSTEAD OF */ + 0, /* (262) trigger_time ::= */ + -1, /* (263) trigger_event ::= DELETE|INSERT */ + -1, /* (264) trigger_event ::= UPDATE */ + -3, /* (265) trigger_event ::= UPDATE OF idlist */ + 0, /* (266) when_clause ::= */ + -2, /* (267) when_clause ::= WHEN expr */ + -3, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + -2, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */ + -3, /* (270) trnm ::= nm DOT nm */ + -3, /* (271) tridxby ::= INDEXED BY nm */ + -2, /* (272) tridxby ::= NOT INDEXED */ + -9, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + -8, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + -6, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + -3, /* (276) trigger_cmd ::= scanpt select scanpt */ + -4, /* (277) expr ::= RAISE LP IGNORE RP */ + -6, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */ + -1, /* (279) raisetype ::= ROLLBACK */ + -1, /* (280) raisetype ::= ABORT */ + -1, /* (281) raisetype ::= FAIL */ + -4, /* (282) cmd ::= DROP TRIGGER ifexists fullname */ + -6, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + -3, /* (284) cmd ::= DETACH database_kw_opt expr */ + 0, /* (285) key_opt ::= */ + -2, /* (286) key_opt ::= KEY expr */ + -1, /* (287) cmd ::= REINDEX */ + -3, /* (288) cmd ::= REINDEX nm dbnm */ + -1, /* (289) cmd ::= ANALYZE */ + -3, /* (290) cmd ::= ANALYZE nm dbnm */ + -6, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */ + -7, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + -6, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + -1, /* (294) add_column_fullname ::= fullname */ + -8, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + -1, /* (296) cmd ::= create_vtab */ + -4, /* (297) cmd ::= create_vtab LP vtabarglist RP */ + -8, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 0, /* (299) vtabarg ::= */ + -1, /* (300) vtabargtoken ::= ANY */ + -3, /* (301) vtabargtoken ::= lp anylist RP */ + -1, /* (302) lp ::= LP */ + -2, /* (303) with ::= WITH wqlist */ + -3, /* (304) with ::= WITH RECURSIVE wqlist */ + -1, /* (305) wqas ::= AS */ + -2, /* (306) wqas ::= AS MATERIALIZED */ + -3, /* (307) wqas ::= AS NOT MATERIALIZED */ + -6, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */ + -1, /* (309) wqlist ::= wqitem */ + -3, /* (310) wqlist ::= wqlist COMMA wqitem */ + -1, /* (311) windowdefn_list ::= windowdefn */ + -3, /* (312) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + -5, /* (313) windowdefn ::= nm AS LP window RP */ + -5, /* (314) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + -6, /* (315) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + -4, /* (316) window ::= ORDER BY sortlist frame_opt */ + -5, /* (317) window ::= nm ORDER BY sortlist frame_opt */ + -1, /* (318) window ::= frame_opt */ + -2, /* (319) window ::= nm frame_opt */ + 0, /* (320) frame_opt ::= */ + -3, /* (321) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + -6, /* (322) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + -1, /* (323) range_or_rows ::= RANGE|ROWS|GROUPS */ + -1, /* (324) frame_bound_s ::= frame_bound */ + -2, /* (325) frame_bound_s ::= UNBOUNDED PRECEDING */ + -1, /* (326) frame_bound_e ::= frame_bound */ + -2, /* (327) frame_bound_e ::= UNBOUNDED FOLLOWING */ + -2, /* (328) frame_bound ::= expr PRECEDING|FOLLOWING */ + -2, /* (329) frame_bound ::= CURRENT ROW */ + 0, /* (330) frame_exclude_opt ::= */ + -2, /* (331) frame_exclude_opt ::= EXCLUDE frame_exclude */ + -2, /* (332) frame_exclude ::= NO OTHERS */ + -2, /* (333) frame_exclude ::= CURRENT ROW */ + -1, /* (334) frame_exclude ::= GROUP|TIES */ + -2, /* (335) window_clause ::= WINDOW windowdefn_list */ + -2, /* (336) filter_over ::= filter_clause over_clause */ + -1, /* (337) filter_over ::= over_clause */ + -1, /* (338) filter_over ::= filter_clause */ + -4, /* (339) over_clause ::= OVER LP window RP */ + -2, /* (340) over_clause ::= OVER nm */ + -5, /* (341) filter_clause ::= FILTER LP WHERE expr RP */ + -1, /* (342) input ::= cmdlist */ + -2, /* (343) cmdlist ::= cmdlist ecmd */ + -1, /* (344) cmdlist ::= ecmd */ + -1, /* (345) ecmd ::= SEMI */ + -2, /* (346) ecmd ::= cmdx SEMI */ + -3, /* (347) ecmd ::= explain cmdx SEMI */ + 0, /* (348) trans_opt ::= */ + -1, /* (349) trans_opt ::= TRANSACTION */ + -2, /* (350) trans_opt ::= TRANSACTION nm */ + -1, /* (351) savepoint_opt ::= SAVEPOINT */ + 0, /* (352) savepoint_opt ::= */ + -2, /* (353) cmd ::= create_table create_table_args */ + -1, /* (354) table_option_set ::= table_option */ + -4, /* (355) columnlist ::= columnlist COMMA columnname carglist */ + -2, /* (356) columnlist ::= columnname carglist */ + -1, /* (357) nm ::= ID|INDEXED */ + -1, /* (358) nm ::= STRING */ + -1, /* (359) nm ::= JOIN_KW */ + -1, /* (360) typetoken ::= typename */ + -1, /* (361) typename ::= ID|STRING */ + -1, /* (362) signed ::= plus_num */ + -1, /* (363) signed ::= minus_num */ + -2, /* (364) carglist ::= carglist ccons */ + 0, /* (365) carglist ::= */ + -2, /* (366) ccons ::= NULL onconf */ + -4, /* (367) ccons ::= GENERATED ALWAYS AS generated */ + -2, /* (368) ccons ::= AS generated */ + -2, /* (369) conslist_opt ::= COMMA conslist */ + -3, /* (370) conslist ::= conslist tconscomma tcons */ + -1, /* (371) conslist ::= tcons */ + 0, /* (372) tconscomma ::= */ + -1, /* (373) defer_subclause_opt ::= defer_subclause */ + -1, /* (374) resolvetype ::= raisetype */ + -1, /* (375) selectnowith ::= oneselect */ + -1, /* (376) oneselect ::= values */ + -2, /* (377) sclp ::= selcollist COMMA */ + -1, /* (378) as ::= ID|STRING */ + -1, /* (379) indexed_opt ::= indexed_by */ + 0, /* (380) returning ::= */ + -1, /* (381) expr ::= term */ + -1, /* (382) likeop ::= LIKE_KW|MATCH */ + -1, /* (383) exprlist ::= nexprlist */ + -1, /* (384) nmnum ::= plus_num */ + -1, /* (385) nmnum ::= nm */ + -1, /* (386) nmnum ::= ON */ + -1, /* (387) nmnum ::= DELETE */ + -1, /* (388) nmnum ::= DEFAULT */ + -1, /* (389) plus_num ::= INTEGER|FLOAT */ + 0, /* (390) foreach_clause ::= */ + -3, /* (391) foreach_clause ::= FOR EACH ROW */ + -1, /* (392) trnm ::= nm */ + 0, /* (393) tridxby ::= */ + -1, /* (394) database_kw_opt ::= DATABASE */ + 0, /* (395) database_kw_opt ::= */ + 0, /* (396) kwcolumn_opt ::= */ + -1, /* (397) kwcolumn_opt ::= COLUMNKW */ + -1, /* (398) vtabarglist ::= vtabarg */ + -3, /* (399) vtabarglist ::= vtabarglist COMMA vtabarg */ + -2, /* (400) vtabarg ::= vtabarg vtabargtoken */ + 0, /* (401) anylist ::= */ + -4, /* (402) anylist ::= anylist LP anylist RP */ + -2, /* (403) anylist ::= anylist ANY */ + 0, /* (404) with ::= */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -163166,55 +172457,6 @@ static YYACTIONTYPE yy_reduce( (void)yyLookahead; (void)yyLookaheadToken; yymsp = yypParser->yytos; - assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); -#ifndef NDEBUG - if( yyTraceFILE ){ - yysize = yyRuleInfoNRhs[yyruleno]; - if( yysize ){ - fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", - yyTracePrompt, - yyruleno, yyRuleName[yyruleno], - yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){ - yypParser->yyhwm++; - assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); - } -#endif -#if YYSTACKDEPTH>0 - if( yypParser->yytos>=yypParser->yystackEnd ){ - yyStackOverflow(yypParser); - /* The call to yyStackOverflow() above pops the stack until it is - ** empty, causing the main parser loop to exit. So the return value - ** is never used and does not matter. */ - return 0; - } -#else - if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ - if( yyGrowStack(yypParser) ){ - yyStackOverflow(yypParser); - /* The call to yyStackOverflow() above pops the stack until it is - ** empty, causing the main parser loop to exit. So the return value - ** is never used and does not matter. */ - return 0; - } - yymsp = yypParser->yytos; - } -#endif - } switch( yyruleno ){ /* Beginning here are the reduction cases. A typical example @@ -163237,16 +172479,16 @@ static YYACTIONTYPE yy_reduce( { sqlcipher_sqlite3FinishCoding(pParse); } break; case 3: /* cmd ::= BEGIN transtype trans_opt */ -{sqlcipher_sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy192);} +{sqlcipher_sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy394);} break; case 4: /* transtype ::= */ -{yymsp[1].minor.yy192 = TK_DEFERRED;} +{yymsp[1].minor.yy394 = TK_DEFERRED;} break; case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); - case 306: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==306); -{yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-X*/} + case 323: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==323); +{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); @@ -163269,7 +172511,7 @@ static YYACTIONTYPE yy_reduce( break; case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ { - sqlcipher_sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy192,0,0,yymsp[-2].minor.yy192); + sqlcipher_sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy394,0,0,yymsp[-2].minor.yy394); } break; case 14: /* createkw ::= CREATE */ @@ -163277,96 +172519,112 @@ static YYACTIONTYPE yy_reduce( break; case 15: /* ifnotexists ::= */ case 18: /* temp ::= */ yytestcase(yyruleno==18); - case 21: /* table_options ::= */ yytestcase(yyruleno==21); - case 45: /* autoinc ::= */ yytestcase(yyruleno==45); - case 60: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==60); - case 70: /* defer_subclause_opt ::= */ yytestcase(yyruleno==70); - case 79: /* ifexists ::= */ yytestcase(yyruleno==79); - case 96: /* distinct ::= */ yytestcase(yyruleno==96); - case 232: /* collate ::= */ yytestcase(yyruleno==232); -{yymsp[1].minor.yy192 = 0;} + case 47: /* autoinc ::= */ yytestcase(yyruleno==47); + case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62); + case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72); + case 81: /* ifexists ::= */ yytestcase(yyruleno==81); + case 98: /* distinct ::= */ yytestcase(yyruleno==98); + case 244: /* collate ::= */ yytestcase(yyruleno==244); +{yymsp[1].minor.yy394 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ -{yymsp[-2].minor.yy192 = 1;} +{yymsp[-2].minor.yy394 = 1;} break; case 17: /* temp ::= TEMP */ - case 46: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==46); -{yymsp[0].minor.yy192 = 1;} +{yymsp[0].minor.yy394 = pParse->db->init.busy==0;} break; - case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */ + case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */ { - sqlcipher_sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy192,0); + sqlcipher_sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy285,0); } break; case 20: /* create_table_args ::= AS select */ { - sqlcipher_sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy539); - sqlcipher_sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy539); + sqlcipher_sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy47); + sqlcipher_sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47); } break; - case 22: /* table_options ::= WITHOUT nm */ + case 21: /* table_option_set ::= */ +{yymsp[1].minor.yy285 = 0;} + break; + case 22: /* table_option_set ::= table_option_set COMMA table_option */ +{yylhsminor.yy285 = yymsp[-2].minor.yy285|yymsp[0].minor.yy285;} + yymsp[-2].minor.yy285 = yylhsminor.yy285; + break; + case 23: /* table_option ::= WITHOUT nm */ { if( yymsp[0].minor.yy0.n==5 && sqlcipher_sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ - yymsp[-1].minor.yy192 = TF_WithoutRowid | TF_NoVisibleRowid; + yymsp[-1].minor.yy285 = TF_WithoutRowid | TF_NoVisibleRowid; + }else{ + yymsp[-1].minor.yy285 = 0; + sqlcipher_sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); + } +} + break; + case 24: /* table_option ::= nm */ +{ + if( yymsp[0].minor.yy0.n==6 && sqlcipher_sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){ + yylhsminor.yy285 = TF_Strict; }else{ - yymsp[-1].minor.yy192 = 0; + yylhsminor.yy285 = 0; sqlcipher_sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } + yymsp[0].minor.yy285 = yylhsminor.yy285; break; - case 23: /* columnname ::= nm typetoken */ -{sqlcipher_sqlite3AddColumn(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} + case 25: /* columnname ::= nm typetoken */ +{sqlcipher_sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);} break; - case 24: /* typetoken ::= */ - case 63: /* conslist_opt ::= */ yytestcase(yyruleno==63); - case 102: /* as ::= */ yytestcase(yyruleno==102); + case 26: /* typetoken ::= */ + case 65: /* conslist_opt ::= */ yytestcase(yyruleno==65); + case 104: /* as ::= */ yytestcase(yyruleno==104); {yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;} break; - case 25: /* typetoken ::= typename LP signed RP */ + case 27: /* typetoken ::= typename LP signed RP */ { yymsp[-3].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z); } break; - case 26: /* typetoken ::= typename LP signed COMMA signed RP */ + case 28: /* typetoken ::= typename LP signed COMMA signed RP */ { yymsp[-5].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z); } break; - case 27: /* typename ::= typename ID|STRING */ + case 29: /* typename ::= typename ID|STRING */ {yymsp[-1].minor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);} break; - case 28: /* scanpt ::= */ + case 30: /* scanpt ::= */ { assert( yyLookahead!=YYNOCODE ); - yymsp[1].minor.yy436 = yyLookaheadToken.z; + yymsp[1].minor.yy522 = yyLookaheadToken.z; } break; - case 29: /* scantok ::= */ + case 31: /* scantok ::= */ { assert( yyLookahead!=YYNOCODE ); yymsp[1].minor.yy0 = yyLookaheadToken; } break; - case 30: /* ccons ::= CONSTRAINT nm */ - case 65: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==65); + case 32: /* ccons ::= CONSTRAINT nm */ + case 67: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==67); {pParse->constraintName = yymsp[0].minor.yy0;} break; - case 31: /* ccons ::= DEFAULT scantok term */ -{sqlcipher_sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy202,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} + case 33: /* ccons ::= DEFAULT scantok term */ +{sqlcipher_sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; - case 32: /* ccons ::= DEFAULT LP expr RP */ -{sqlcipher_sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy202,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} + case 34: /* ccons ::= DEFAULT LP expr RP */ +{sqlcipher_sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} break; - case 33: /* ccons ::= DEFAULT PLUS scantok term */ -{sqlcipher_sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy202,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} + case 35: /* ccons ::= DEFAULT PLUS scantok term */ +{sqlcipher_sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; - case 34: /* ccons ::= DEFAULT MINUS scantok term */ + case 36: /* ccons ::= DEFAULT MINUS scantok term */ { - Expr *p = sqlcipher_sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy202, 0); + Expr *p = sqlcipher_sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy528, 0); sqlcipher_sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]); } break; - case 35: /* ccons ::= DEFAULT scantok ID|INDEXED */ + case 37: /* ccons ::= DEFAULT scantok ID|INDEXED */ { Expr *p = tokenExpr(pParse, TK_STRING, yymsp[0].minor.yy0); if( p ){ @@ -163376,323 +172634,316 @@ static YYACTIONTYPE yy_reduce( sqlcipher_sqlite3AddDefaultValue(pParse,p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.z+yymsp[0].minor.yy0.n); } break; - case 36: /* ccons ::= NOT NULL onconf */ -{sqlcipher_sqlite3AddNotNull(pParse, yymsp[0].minor.yy192);} + case 38: /* ccons ::= NOT NULL onconf */ +{sqlcipher_sqlite3AddNotNull(pParse, yymsp[0].minor.yy394);} break; - case 37: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{sqlcipher_sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy192,yymsp[0].minor.yy192,yymsp[-2].minor.yy192);} + case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ +{sqlcipher_sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy394,yymsp[0].minor.yy394,yymsp[-2].minor.yy394);} break; - case 38: /* ccons ::= UNIQUE onconf */ -{sqlcipher_sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy192,0,0,0,0, + case 40: /* ccons ::= UNIQUE onconf */ +{sqlcipher_sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy394,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; - case 39: /* ccons ::= CHECK LP expr RP */ -{sqlcipher_sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy202,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} + case 41: /* ccons ::= CHECK LP expr RP */ +{sqlcipher_sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} break; - case 40: /* ccons ::= REFERENCES nm eidlist_opt refargs */ -{sqlcipher_sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy242,yymsp[0].minor.yy192);} + case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */ +{sqlcipher_sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy322,yymsp[0].minor.yy394);} break; - case 41: /* ccons ::= defer_subclause */ -{sqlcipher_sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy192);} + case 43: /* ccons ::= defer_subclause */ +{sqlcipher_sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy394);} break; - case 42: /* ccons ::= COLLATE ID|STRING */ + case 44: /* ccons ::= COLLATE ID|STRING */ {sqlcipher_sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} break; - case 43: /* generated ::= LP expr RP */ -{sqlcipher_sqlite3AddGenerated(pParse,yymsp[-1].minor.yy202,0);} + case 45: /* generated ::= LP expr RP */ +{sqlcipher_sqlite3AddGenerated(pParse,yymsp[-1].minor.yy528,0);} break; - case 44: /* generated ::= LP expr RP ID */ -{sqlcipher_sqlite3AddGenerated(pParse,yymsp[-2].minor.yy202,&yymsp[0].minor.yy0);} + case 46: /* generated ::= LP expr RP ID */ +{sqlcipher_sqlite3AddGenerated(pParse,yymsp[-2].minor.yy528,&yymsp[0].minor.yy0);} break; - case 47: /* refargs ::= */ -{ yymsp[1].minor.yy192 = OE_None*0x0101; /* EV: R-19803-45884 */} + case 48: /* autoinc ::= AUTOINCR */ +{yymsp[0].minor.yy394 = 1;} break; - case 48: /* refargs ::= refargs refarg */ -{ yymsp[-1].minor.yy192 = (yymsp[-1].minor.yy192 & ~yymsp[0].minor.yy207.mask) | yymsp[0].minor.yy207.value; } + case 49: /* refargs ::= */ +{ yymsp[1].minor.yy394 = OE_None*0x0101; /* EV: R-19803-45884 */} break; - case 49: /* refarg ::= MATCH nm */ -{ yymsp[-1].minor.yy207.value = 0; yymsp[-1].minor.yy207.mask = 0x000000; } + case 50: /* refargs ::= refargs refarg */ +{ yymsp[-1].minor.yy394 = (yymsp[-1].minor.yy394 & ~yymsp[0].minor.yy231.mask) | yymsp[0].minor.yy231.value; } break; - case 50: /* refarg ::= ON INSERT refact */ -{ yymsp[-2].minor.yy207.value = 0; yymsp[-2].minor.yy207.mask = 0x000000; } + case 51: /* refarg ::= MATCH nm */ +{ yymsp[-1].minor.yy231.value = 0; yymsp[-1].minor.yy231.mask = 0x000000; } break; - case 51: /* refarg ::= ON DELETE refact */ -{ yymsp[-2].minor.yy207.value = yymsp[0].minor.yy192; yymsp[-2].minor.yy207.mask = 0x0000ff; } + case 52: /* refarg ::= ON INSERT refact */ +{ yymsp[-2].minor.yy231.value = 0; yymsp[-2].minor.yy231.mask = 0x000000; } break; - case 52: /* refarg ::= ON UPDATE refact */ -{ yymsp[-2].minor.yy207.value = yymsp[0].minor.yy192<<8; yymsp[-2].minor.yy207.mask = 0x00ff00; } + case 53: /* refarg ::= ON DELETE refact */ +{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394; yymsp[-2].minor.yy231.mask = 0x0000ff; } break; - case 53: /* refact ::= SET NULL */ -{ yymsp[-1].minor.yy192 = OE_SetNull; /* EV: R-33326-45252 */} + case 54: /* refarg ::= ON UPDATE refact */ +{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394<<8; yymsp[-2].minor.yy231.mask = 0x00ff00; } break; - case 54: /* refact ::= SET DEFAULT */ -{ yymsp[-1].minor.yy192 = OE_SetDflt; /* EV: R-33326-45252 */} + case 55: /* refact ::= SET NULL */ +{ yymsp[-1].minor.yy394 = OE_SetNull; /* EV: R-33326-45252 */} break; - case 55: /* refact ::= CASCADE */ -{ yymsp[0].minor.yy192 = OE_Cascade; /* EV: R-33326-45252 */} + case 56: /* refact ::= SET DEFAULT */ +{ yymsp[-1].minor.yy394 = OE_SetDflt; /* EV: R-33326-45252 */} break; - case 56: /* refact ::= RESTRICT */ -{ yymsp[0].minor.yy192 = OE_Restrict; /* EV: R-33326-45252 */} + case 57: /* refact ::= CASCADE */ +{ yymsp[0].minor.yy394 = OE_Cascade; /* EV: R-33326-45252 */} break; - case 57: /* refact ::= NO ACTION */ -{ yymsp[-1].minor.yy192 = OE_None; /* EV: R-33326-45252 */} + case 58: /* refact ::= RESTRICT */ +{ yymsp[0].minor.yy394 = OE_Restrict; /* EV: R-33326-45252 */} break; - case 58: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -{yymsp[-2].minor.yy192 = 0;} + case 59: /* refact ::= NO ACTION */ +{ yymsp[-1].minor.yy394 = OE_None; /* EV: R-33326-45252 */} break; - case 59: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - case 74: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==74); - case 162: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==162); -{yymsp[-1].minor.yy192 = yymsp[0].minor.yy192;} + case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ +{yymsp[-2].minor.yy394 = 0;} break; - case 61: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ - case 78: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==78); - case 204: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==204); - case 207: /* in_op ::= NOT IN */ yytestcase(yyruleno==207); - case 233: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==233); -{yymsp[-1].minor.yy192 = 1;} + case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76); + case 171: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==171); +{yymsp[-1].minor.yy394 = yymsp[0].minor.yy394;} break; - case 62: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -{yymsp[-1].minor.yy192 = 0;} + case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ + case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); + case 216: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==216); + case 219: /* in_op ::= NOT IN */ yytestcase(yyruleno==219); + case 245: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==245); +{yymsp[-1].minor.yy394 = 1;} break; - case 64: /* tconscomma ::= COMMA */ + case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ +{yymsp[-1].minor.yy394 = 0;} + break; + case 66: /* tconscomma ::= COMMA */ {pParse->constraintName.n = 0;} break; - case 66: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -{sqlcipher_sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy242,yymsp[0].minor.yy192,yymsp[-2].minor.yy192,0);} + case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ +{sqlcipher_sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy322,yymsp[0].minor.yy394,yymsp[-2].minor.yy394,0);} break; - case 67: /* tcons ::= UNIQUE LP sortlist RP onconf */ -{sqlcipher_sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy242,yymsp[0].minor.yy192,0,0,0,0, + case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */ +{sqlcipher_sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy322,yymsp[0].minor.yy394,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; - case 68: /* tcons ::= CHECK LP expr RP onconf */ -{sqlcipher_sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy202,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} + case 70: /* tcons ::= CHECK LP expr RP onconf */ +{sqlcipher_sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy528,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} break; - case 69: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ { - sqlcipher_sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy242, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy242, yymsp[-1].minor.yy192); - sqlcipher_sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy192); + sqlcipher_sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy322, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[-1].minor.yy394); + sqlcipher_sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy394); } break; - case 71: /* onconf ::= */ - case 73: /* orconf ::= */ yytestcase(yyruleno==73); -{yymsp[1].minor.yy192 = OE_Default;} + case 73: /* onconf ::= */ + case 75: /* orconf ::= */ yytestcase(yyruleno==75); +{yymsp[1].minor.yy394 = OE_Default;} break; - case 72: /* onconf ::= ON CONFLICT resolvetype */ -{yymsp[-2].minor.yy192 = yymsp[0].minor.yy192;} + case 74: /* onconf ::= ON CONFLICT resolvetype */ +{yymsp[-2].minor.yy394 = yymsp[0].minor.yy394;} break; - case 75: /* resolvetype ::= IGNORE */ -{yymsp[0].minor.yy192 = OE_Ignore;} + case 77: /* resolvetype ::= IGNORE */ +{yymsp[0].minor.yy394 = OE_Ignore;} break; - case 76: /* resolvetype ::= REPLACE */ - case 163: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==163); -{yymsp[0].minor.yy192 = OE_Replace;} + case 78: /* resolvetype ::= REPLACE */ + case 172: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==172); +{yymsp[0].minor.yy394 = OE_Replace;} break; - case 77: /* cmd ::= DROP TABLE ifexists fullname */ + case 79: /* cmd ::= DROP TABLE ifexists fullname */ { - sqlcipher_sqlite3DropTable(pParse, yymsp[0].minor.yy47, 0, yymsp[-1].minor.yy192); + sqlcipher_sqlite3DropTable(pParse, yymsp[0].minor.yy131, 0, yymsp[-1].minor.yy394); } break; - case 80: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ { - sqlcipher_sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy242, yymsp[0].minor.yy539, yymsp[-7].minor.yy192, yymsp[-5].minor.yy192); + sqlcipher_sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[0].minor.yy47, yymsp[-7].minor.yy394, yymsp[-5].minor.yy394); } break; - case 81: /* cmd ::= DROP VIEW ifexists fullname */ + case 83: /* cmd ::= DROP VIEW ifexists fullname */ { - sqlcipher_sqlite3DropTable(pParse, yymsp[0].minor.yy47, 1, yymsp[-1].minor.yy192); + sqlcipher_sqlite3DropTable(pParse, yymsp[0].minor.yy131, 1, yymsp[-1].minor.yy394); } break; - case 82: /* cmd ::= select */ + case 84: /* cmd ::= select */ { SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0}; - sqlcipher_sqlite3Select(pParse, yymsp[0].minor.yy539, &dest); - sqlcipher_sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy539); + sqlcipher_sqlite3Select(pParse, yymsp[0].minor.yy47, &dest); + sqlcipher_sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47); } break; - case 83: /* select ::= WITH wqlist selectnowith */ -{ - Select *p = yymsp[0].minor.yy539; - if( p ){ - p->pWith = yymsp[-1].minor.yy131; - parserDoubleLinkSelect(pParse, p); - }else{ - sqlcipher_sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy131); - } - yymsp[-2].minor.yy539 = p; -} + case 85: /* select ::= WITH wqlist selectnowith */ +{yymsp[-2].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);} break; - case 84: /* select ::= WITH RECURSIVE wqlist selectnowith */ -{ - Select *p = yymsp[0].minor.yy539; - if( p ){ - p->pWith = yymsp[-1].minor.yy131; - parserDoubleLinkSelect(pParse, p); - }else{ - sqlcipher_sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy131); - } - yymsp[-3].minor.yy539 = p; -} + case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */ +{yymsp[-3].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);} break; - case 85: /* select ::= selectnowith */ + case 87: /* select ::= selectnowith */ { - Select *p = yymsp[0].minor.yy539; + Select *p = yymsp[0].minor.yy47; if( p ){ parserDoubleLinkSelect(pParse, p); } - yymsp[0].minor.yy539 = p; /*A-overwrites-X*/ + yymsp[0].minor.yy47 = p; /*A-overwrites-X*/ } break; - case 86: /* selectnowith ::= selectnowith multiselect_op oneselect */ + case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */ { - Select *pRhs = yymsp[0].minor.yy539; - Select *pLhs = yymsp[-2].minor.yy539; + Select *pRhs = yymsp[0].minor.yy47; + Select *pLhs = yymsp[-2].minor.yy47; if( pRhs && pRhs->pPrior ){ SrcList *pFrom; Token x; x.n = 0; parserDoubleLinkSelect(pParse, pRhs); - pFrom = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0); + pFrom = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0); pRhs = sqlcipher_sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); } if( pRhs ){ - pRhs->op = (u8)yymsp[-1].minor.yy192; + pRhs->op = (u8)yymsp[-1].minor.yy394; pRhs->pPrior = pLhs; if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; pRhs->selFlags &= ~SF_MultiValue; - if( yymsp[-1].minor.yy192!=TK_ALL ) pParse->hasCompound = 1; + if( yymsp[-1].minor.yy394!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlcipher_sqlite3SelectDelete(pParse->db, pLhs); } - yymsp[-2].minor.yy539 = pRhs; + yymsp[-2].minor.yy47 = pRhs; } break; - case 87: /* multiselect_op ::= UNION */ - case 89: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==89); -{yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-OP*/} + case 89: /* multiselect_op ::= UNION */ + case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91); +{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-OP*/} break; - case 88: /* multiselect_op ::= UNION ALL */ -{yymsp[-1].minor.yy192 = TK_ALL;} + case 90: /* multiselect_op ::= UNION ALL */ +{yymsp[-1].minor.yy394 = TK_ALL;} break; - case 90: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yymsp[-8].minor.yy539 = sqlcipher_sqlite3SelectNew(pParse,yymsp[-6].minor.yy242,yymsp[-5].minor.yy47,yymsp[-4].minor.yy202,yymsp[-3].minor.yy242,yymsp[-2].minor.yy202,yymsp[-1].minor.yy242,yymsp[-7].minor.yy192,yymsp[0].minor.yy202); + yymsp[-8].minor.yy47 = sqlcipher_sqlite3SelectNew(pParse,yymsp[-6].minor.yy322,yymsp[-5].minor.yy131,yymsp[-4].minor.yy528,yymsp[-3].minor.yy322,yymsp[-2].minor.yy528,yymsp[-1].minor.yy322,yymsp[-7].minor.yy394,yymsp[0].minor.yy528); } break; - case 91: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ { - yymsp[-9].minor.yy539 = sqlcipher_sqlite3SelectNew(pParse,yymsp[-7].minor.yy242,yymsp[-6].minor.yy47,yymsp[-5].minor.yy202,yymsp[-4].minor.yy242,yymsp[-3].minor.yy202,yymsp[-1].minor.yy242,yymsp[-8].minor.yy192,yymsp[0].minor.yy202); - if( yymsp[-9].minor.yy539 ){ - yymsp[-9].minor.yy539->pWinDefn = yymsp[-2].minor.yy303; + yymsp[-9].minor.yy47 = sqlcipher_sqlite3SelectNew(pParse,yymsp[-7].minor.yy322,yymsp[-6].minor.yy131,yymsp[-5].minor.yy528,yymsp[-4].minor.yy322,yymsp[-3].minor.yy528,yymsp[-1].minor.yy322,yymsp[-8].minor.yy394,yymsp[0].minor.yy528); + if( yymsp[-9].minor.yy47 ){ + yymsp[-9].minor.yy47->pWinDefn = yymsp[-2].minor.yy41; }else{ - sqlcipher_sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy303); + sqlcipher_sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy41); } } break; - case 92: /* values ::= VALUES LP nexprlist RP */ + case 94: /* values ::= VALUES LP nexprlist RP */ { - yymsp[-3].minor.yy539 = sqlcipher_sqlite3SelectNew(pParse,yymsp[-1].minor.yy242,0,0,0,0,0,SF_Values,0); + yymsp[-3].minor.yy47 = sqlcipher_sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values,0); } break; - case 93: /* values ::= values COMMA LP nexprlist RP */ + case 95: /* values ::= values COMMA LP nexprlist RP */ { - Select *pRight, *pLeft = yymsp[-4].minor.yy539; - pRight = sqlcipher_sqlite3SelectNew(pParse,yymsp[-1].minor.yy242,0,0,0,0,0,SF_Values|SF_MultiValue,0); + Select *pRight, *pLeft = yymsp[-4].minor.yy47; + pRight = sqlcipher_sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values|SF_MultiValue,0); if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; if( pRight ){ pRight->op = TK_ALL; pRight->pPrior = pLeft; - yymsp[-4].minor.yy539 = pRight; + yymsp[-4].minor.yy47 = pRight; }else{ - yymsp[-4].minor.yy539 = pLeft; + yymsp[-4].minor.yy47 = pLeft; } } break; - case 94: /* distinct ::= DISTINCT */ -{yymsp[0].minor.yy192 = SF_Distinct;} + case 96: /* distinct ::= DISTINCT */ +{yymsp[0].minor.yy394 = SF_Distinct;} break; - case 95: /* distinct ::= ALL */ -{yymsp[0].minor.yy192 = SF_All;} + case 97: /* distinct ::= ALL */ +{yymsp[0].minor.yy394 = SF_All;} break; - case 97: /* sclp ::= */ - case 130: /* orderby_opt ::= */ yytestcase(yyruleno==130); - case 140: /* groupby_opt ::= */ yytestcase(yyruleno==140); - case 220: /* exprlist ::= */ yytestcase(yyruleno==220); - case 223: /* paren_exprlist ::= */ yytestcase(yyruleno==223); - case 228: /* eidlist_opt ::= */ yytestcase(yyruleno==228); -{yymsp[1].minor.yy242 = 0;} + case 99: /* sclp ::= */ + case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132); + case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142); + case 232: /* exprlist ::= */ yytestcase(yyruleno==232); + case 235: /* paren_exprlist ::= */ yytestcase(yyruleno==235); + case 240: /* eidlist_opt ::= */ yytestcase(yyruleno==240); +{yymsp[1].minor.yy322 = 0;} break; - case 98: /* selcollist ::= sclp scanpt expr scanpt as */ + case 100: /* selcollist ::= sclp scanpt expr scanpt as */ { - yymsp[-4].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy242, yymsp[-2].minor.yy202); - if( yymsp[0].minor.yy0.n>0 ) sqlcipher_sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy242, &yymsp[0].minor.yy0, 1); - sqlcipher_sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy242,yymsp[-3].minor.yy436,yymsp[-1].minor.yy436); + yymsp[-4].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); + if( yymsp[0].minor.yy0.n>0 ) sqlcipher_sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[0].minor.yy0, 1); + sqlcipher_sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy322,yymsp[-3].minor.yy522,yymsp[-1].minor.yy522); } break; - case 99: /* selcollist ::= sclp scanpt STAR */ + case 101: /* selcollist ::= sclp scanpt STAR */ { Expr *p = sqlcipher_sqlite3Expr(pParse->db, TK_ASTERISK, 0); - yymsp[-2].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy242, p); + yymsp[-2].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy322, p); } break; - case 100: /* selcollist ::= sclp scanpt nm DOT STAR */ + case 102: /* selcollist ::= sclp scanpt nm DOT STAR */ { Expr *pRight = sqlcipher_sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); - Expr *pLeft = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); + Expr *pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0); Expr *pDot = sqlcipher_sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); - yymsp[-4].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, pDot); + yymsp[-4].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot); } break; - case 101: /* as ::= AS nm */ - case 112: /* dbnm ::= DOT nm */ yytestcase(yyruleno==112); - case 244: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==244); - case 245: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==245); + case 103: /* as ::= AS nm */ + case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115); + case 256: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==256); + case 257: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==257); {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} break; - case 103: /* from ::= */ - case 106: /* stl_prefix ::= */ yytestcase(yyruleno==106); -{yymsp[1].minor.yy47 = 0;} + case 105: /* from ::= */ + case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108); +{yymsp[1].minor.yy131 = 0;} break; - case 104: /* from ::= FROM seltablist */ + case 106: /* from ::= FROM seltablist */ { - yymsp[-1].minor.yy47 = yymsp[0].minor.yy47; - sqlcipher_sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy47); + yymsp[-1].minor.yy131 = yymsp[0].minor.yy131; + sqlcipher_sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy131); } break; - case 105: /* stl_prefix ::= seltablist joinop */ + case 107: /* stl_prefix ::= seltablist joinop */ { - if( ALWAYS(yymsp[-1].minor.yy47 && yymsp[-1].minor.yy47->nSrc>0) ) yymsp[-1].minor.yy47->a[yymsp[-1].minor.yy47->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy192; + if( ALWAYS(yymsp[-1].minor.yy131 && yymsp[-1].minor.yy131->nSrc>0) ) yymsp[-1].minor.yy131->a[yymsp[-1].minor.yy131->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy394; } break; - case 107: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + case 109: /* seltablist ::= stl_prefix nm dbnm as on_using */ { - yymsp[-6].minor.yy47 = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); - sqlcipher_sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy47, &yymsp[-2].minor.yy0); + yymsp[-4].minor.yy131 = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy131,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); } break; - case 108: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + case 110: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ { - yymsp[-8].minor.yy47 = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy47,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); - sqlcipher_sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy47, yymsp[-4].minor.yy242); + yymsp[-5].minor.yy131 = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy561); + sqlcipher_sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-1].minor.yy0); } break; - case 109: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + case 111: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ { - yymsp[-6].minor.yy47 = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy539,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); + yymsp[-7].minor.yy131 = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy131,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); + sqlcipher_sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy131, yymsp[-3].minor.yy322); +} + break; + case 112: /* seltablist ::= stl_prefix LP select RP as on_using */ +{ + yymsp[-5].minor.yy131 = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy47,&yymsp[0].minor.yy561); } break; - case 110: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ { - if( yymsp[-6].minor.yy47==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy202==0 && yymsp[0].minor.yy600==0 ){ - yymsp[-6].minor.yy47 = yymsp[-4].minor.yy47; - }else if( yymsp[-4].minor.yy47->nSrc==1 ){ - yymsp[-6].minor.yy47 = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); - if( yymsp[-6].minor.yy47 ){ - struct SrcList_item *pNew = &yymsp[-6].minor.yy47->a[yymsp[-6].minor.yy47->nSrc-1]; - struct SrcList_item *pOld = yymsp[-4].minor.yy47->a; + if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){ + yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131; + }else if( yymsp[-3].minor.yy131->nSrc==1 ){ + yymsp[-5].minor.yy131 = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); + if( yymsp[-5].minor.yy131 ){ + SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1]; + SrcItem *pOld = yymsp[-3].minor.yy131->a; pNew->zName = pOld->zName; pNew->zDatabase = pOld->zDatabase; pNew->pSelect = pOld->pSelect; + if( pNew->pSelect && (pNew->pSelect->selFlags & SF_NestedFrom)!=0 ){ + pNew->fg.isNestedFrom = 1; + } if( pOld->fg.isTabFunc ){ pNew->u1.pFuncArg = pOld->u1.pFuncArg; pOld->u1.pFuncArg = 0; @@ -163702,264 +172953,291 @@ static YYACTIONTYPE yy_reduce( pOld->zName = pOld->zDatabase = 0; pOld->pSelect = 0; } - sqlcipher_sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy47); + sqlcipher_sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy131); }else{ Select *pSubquery; - sqlcipher_sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy47); - pSubquery = sqlcipher_sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy47,0,0,0,0,SF_NestedFrom,0); - yymsp[-6].minor.yy47 = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); + sqlcipher_sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy131); + pSubquery = sqlcipher_sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy131,0,0,0,0,SF_NestedFrom,0); + yymsp[-5].minor.yy131 = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy561); } } break; - case 111: /* dbnm ::= */ - case 125: /* indexed_opt ::= */ yytestcase(yyruleno==125); + case 114: /* dbnm ::= */ + case 129: /* indexed_opt ::= */ yytestcase(yyruleno==129); {yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;} break; - case 113: /* fullname ::= nm */ + case 116: /* fullname ::= nm */ { - yylhsminor.yy47 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); - if( IN_RENAME_OBJECT && yylhsminor.yy47 ) sqlcipher_sqlite3RenameTokenMap(pParse, yylhsminor.yy47->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy131 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); + if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlcipher_sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[0].minor.yy47 = yylhsminor.yy47; + yymsp[0].minor.yy131 = yylhsminor.yy131; break; - case 114: /* fullname ::= nm DOT nm */ + case 117: /* fullname ::= nm DOT nm */ { - yylhsminor.yy47 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); - if( IN_RENAME_OBJECT && yylhsminor.yy47 ) sqlcipher_sqlite3RenameTokenMap(pParse, yylhsminor.yy47->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy131 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlcipher_sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[-2].minor.yy47 = yylhsminor.yy47; + yymsp[-2].minor.yy131 = yylhsminor.yy131; break; - case 115: /* xfullname ::= nm */ -{yymsp[0].minor.yy47 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} + case 118: /* xfullname ::= nm */ +{yymsp[0].minor.yy131 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} break; - case 116: /* xfullname ::= nm DOT nm */ -{yymsp[-2].minor.yy47 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 119: /* xfullname ::= nm DOT nm */ +{yymsp[-2].minor.yy131 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 117: /* xfullname ::= nm DOT nm AS nm */ + case 120: /* xfullname ::= nm DOT nm AS nm */ { - yymsp[-4].minor.yy47 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ - if( yymsp[-4].minor.yy47 ) yymsp[-4].minor.yy47->a[0].zAlias = sqlcipher_sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-4].minor.yy131 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ + if( yymsp[-4].minor.yy131 ) yymsp[-4].minor.yy131->a[0].zAlias = sqlcipher_sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; - case 118: /* xfullname ::= nm AS nm */ + case 121: /* xfullname ::= nm AS nm */ { - yymsp[-2].minor.yy47 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ - if( yymsp[-2].minor.yy47 ) yymsp[-2].minor.yy47->a[0].zAlias = sqlcipher_sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-2].minor.yy131 = sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ + if( yymsp[-2].minor.yy131 ) yymsp[-2].minor.yy131->a[0].zAlias = sqlcipher_sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; - case 119: /* joinop ::= COMMA|JOIN */ -{ yymsp[0].minor.yy192 = JT_INNER; } + case 122: /* joinop ::= COMMA|JOIN */ +{ yymsp[0].minor.yy394 = JT_INNER; } break; - case 120: /* joinop ::= JOIN_KW JOIN */ -{yymsp[-1].minor.yy192 = sqlcipher_sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} + case 123: /* joinop ::= JOIN_KW JOIN */ +{yymsp[-1].minor.yy394 = sqlcipher_sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} break; - case 121: /* joinop ::= JOIN_KW nm JOIN */ -{yymsp[-2].minor.yy192 = sqlcipher_sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} + case 124: /* joinop ::= JOIN_KW nm JOIN */ +{yymsp[-2].minor.yy394 = sqlcipher_sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} break; - case 122: /* joinop ::= JOIN_KW nm nm JOIN */ -{yymsp[-3].minor.yy192 = sqlcipher_sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} + case 125: /* joinop ::= JOIN_KW nm nm JOIN */ +{yymsp[-3].minor.yy394 = sqlcipher_sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} break; - case 123: /* on_opt ::= ON expr */ - case 143: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==143); - case 150: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==150); - case 216: /* case_else ::= ELSE expr */ yytestcase(yyruleno==216); - case 237: /* vinto ::= INTO expr */ yytestcase(yyruleno==237); -{yymsp[-1].minor.yy202 = yymsp[0].minor.yy202;} + case 126: /* on_using ::= ON expr */ +{yymsp[-1].minor.yy561.pOn = yymsp[0].minor.yy528; yymsp[-1].minor.yy561.pUsing = 0;} break; - case 124: /* on_opt ::= */ - case 142: /* having_opt ::= */ yytestcase(yyruleno==142); - case 144: /* limit_opt ::= */ yytestcase(yyruleno==144); - case 149: /* where_opt ::= */ yytestcase(yyruleno==149); - case 217: /* case_else ::= */ yytestcase(yyruleno==217); - case 219: /* case_operand ::= */ yytestcase(yyruleno==219); - case 238: /* vinto ::= */ yytestcase(yyruleno==238); -{yymsp[1].minor.yy202 = 0;} + case 127: /* on_using ::= USING LP idlist RP */ +{yymsp[-3].minor.yy561.pOn = 0; yymsp[-3].minor.yy561.pUsing = yymsp[-1].minor.yy254;} break; - case 126: /* indexed_opt ::= INDEXED BY nm */ + case 128: /* on_using ::= */ +{yymsp[1].minor.yy561.pOn = 0; yymsp[1].minor.yy561.pUsing = 0;} + break; + case 130: /* indexed_by ::= INDEXED BY nm */ {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} break; - case 127: /* indexed_opt ::= NOT INDEXED */ + case 131: /* indexed_by ::= NOT INDEXED */ {yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;} break; - case 128: /* using_opt ::= USING LP idlist RP */ -{yymsp[-3].minor.yy600 = yymsp[-1].minor.yy600;} - break; - case 129: /* using_opt ::= */ - case 164: /* idlist_opt ::= */ yytestcase(yyruleno==164); -{yymsp[1].minor.yy600 = 0;} + case 133: /* orderby_opt ::= ORDER BY sortlist */ + case 143: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==143); +{yymsp[-2].minor.yy322 = yymsp[0].minor.yy322;} break; - case 131: /* orderby_opt ::= ORDER BY sortlist */ - case 141: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==141); -{yymsp[-2].minor.yy242 = yymsp[0].minor.yy242;} - break; - case 132: /* sortlist ::= sortlist COMMA expr sortorder nulls */ + case 134: /* sortlist ::= sortlist COMMA expr sortorder nulls */ { - yymsp[-4].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242,yymsp[-2].minor.yy202); - sqlcipher_sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy242,yymsp[-1].minor.yy192,yymsp[0].minor.yy192); + yymsp[-4].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322,yymsp[-2].minor.yy528); + sqlcipher_sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394); } break; - case 133: /* sortlist ::= expr sortorder nulls */ + case 135: /* sortlist ::= expr sortorder nulls */ { - yymsp[-2].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy202); /*A-overwrites-Y*/ - sqlcipher_sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy242,yymsp[-1].minor.yy192,yymsp[0].minor.yy192); + yymsp[-2].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy528); /*A-overwrites-Y*/ + sqlcipher_sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394); } break; - case 134: /* sortorder ::= ASC */ -{yymsp[0].minor.yy192 = SQLITE_SO_ASC;} + case 136: /* sortorder ::= ASC */ +{yymsp[0].minor.yy394 = SQLITE_SO_ASC;} + break; + case 137: /* sortorder ::= DESC */ +{yymsp[0].minor.yy394 = SQLITE_SO_DESC;} break; - case 135: /* sortorder ::= DESC */ -{yymsp[0].minor.yy192 = SQLITE_SO_DESC;} + case 138: /* sortorder ::= */ + case 141: /* nulls ::= */ yytestcase(yyruleno==141); +{yymsp[1].minor.yy394 = SQLITE_SO_UNDEFINED;} break; - case 136: /* sortorder ::= */ - case 139: /* nulls ::= */ yytestcase(yyruleno==139); -{yymsp[1].minor.yy192 = SQLITE_SO_UNDEFINED;} + case 139: /* nulls ::= NULLS FIRST */ +{yymsp[-1].minor.yy394 = SQLITE_SO_ASC;} break; - case 137: /* nulls ::= NULLS FIRST */ -{yymsp[-1].minor.yy192 = SQLITE_SO_ASC;} + case 140: /* nulls ::= NULLS LAST */ +{yymsp[-1].minor.yy394 = SQLITE_SO_DESC;} break; - case 138: /* nulls ::= NULLS LAST */ -{yymsp[-1].minor.yy192 = SQLITE_SO_DESC;} + case 144: /* having_opt ::= */ + case 146: /* limit_opt ::= */ yytestcase(yyruleno==146); + case 151: /* where_opt ::= */ yytestcase(yyruleno==151); + case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153); + case 229: /* case_else ::= */ yytestcase(yyruleno==229); + case 231: /* case_operand ::= */ yytestcase(yyruleno==231); + case 250: /* vinto ::= */ yytestcase(yyruleno==250); +{yymsp[1].minor.yy528 = 0;} break; - case 145: /* limit_opt ::= LIMIT expr */ -{yymsp[-1].minor.yy202 = sqlcipher_sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy202,0);} + case 145: /* having_opt ::= HAVING expr */ + case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152); + case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154); + case 228: /* case_else ::= ELSE expr */ yytestcase(yyruleno==228); + case 249: /* vinto ::= INTO expr */ yytestcase(yyruleno==249); +{yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;} break; - case 146: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yymsp[-3].minor.yy202 = sqlcipher_sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);} + case 147: /* limit_opt ::= LIMIT expr */ +{yymsp[-1].minor.yy528 = sqlcipher_sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);} break; - case 147: /* limit_opt ::= LIMIT expr COMMA expr */ -{yymsp[-3].minor.yy202 = sqlcipher_sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy202,yymsp[-2].minor.yy202);} + case 148: /* limit_opt ::= LIMIT expr OFFSET expr */ +{yymsp[-3].minor.yy528 = sqlcipher_sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} break; - case 148: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt orderby_opt limit_opt */ + case 149: /* limit_opt ::= LIMIT expr COMMA expr */ +{yymsp[-3].minor.yy528 = sqlcipher_sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,yymsp[-2].minor.yy528);} + break; + case 150: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret orderby_opt limit_opt */ { - sqlcipher_sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy47, &yymsp[-3].minor.yy0); + sqlcipher_sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy131, &yymsp[-3].minor.yy0); #ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT - if( yymsp[-1].minor.yy242 || yymsp[0].minor.yy202 ){ - updateDeleteLimitError(pParse,yymsp[-1].minor.yy242,yymsp[0].minor.yy202); - yymsp[-1].minor.yy242 = 0; - yymsp[0].minor.yy202 = 0; + if( yymsp[-1].minor.yy322 || yymsp[0].minor.yy528 ){ + updateDeleteLimitError(pParse,yymsp[-1].minor.yy322,yymsp[0].minor.yy528); + yymsp[-1].minor.yy322 = 0; + yymsp[0].minor.yy528 = 0; } #endif - sqlcipher_sqlite3DeleteFrom(pParse,yymsp[-4].minor.yy47,yymsp[-2].minor.yy202,yymsp[-1].minor.yy242,yymsp[0].minor.yy202); + sqlcipher_sqlite3DeleteFrom(pParse,yymsp[-4].minor.yy131,yymsp[-2].minor.yy528,yymsp[-1].minor.yy322,yymsp[0].minor.yy528); } break; - case 151: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt orderby_opt limit_opt */ + case 155: /* where_opt_ret ::= RETURNING selcollist */ +{sqlcipher_sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-1].minor.yy528 = 0;} + break; + case 156: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ +{sqlcipher_sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-3].minor.yy528 = yymsp[-2].minor.yy528;} + break; + case 157: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret orderby_opt limit_opt */ { - sqlcipher_sqlite3SrcListIndexedBy(pParse, yymsp[-7].minor.yy47, &yymsp[-6].minor.yy0); - yymsp[-7].minor.yy47 = sqlcipher_sqlite3SrcListAppendList(pParse, yymsp[-7].minor.yy47, yymsp[-3].minor.yy47); - sqlcipher_sqlite3ExprListCheckLength(pParse,yymsp[-4].minor.yy242,"set list"); + sqlcipher_sqlite3SrcListIndexedBy(pParse, yymsp[-7].minor.yy131, &yymsp[-6].minor.yy0); + if( yymsp[-3].minor.yy131 ){ + SrcList *pFromClause = yymsp[-3].minor.yy131; + if( pFromClause->nSrc>1 ){ + Select *pSubquery; + Token as; + pSubquery = sqlcipher_sqlite3SelectNew(pParse,0,pFromClause,0,0,0,0,SF_NestedFrom,0); + as.n = 0; + as.z = 0; + pFromClause = sqlcipher_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); + } + yymsp[-7].minor.yy131 = sqlcipher_sqlite3SrcListAppendList(pParse, yymsp[-7].minor.yy131, pFromClause); + } + sqlcipher_sqlite3ExprListCheckLength(pParse,yymsp[-4].minor.yy322,"set list"); #ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT - if( yymsp[-1].minor.yy242 || yymsp[0].minor.yy202 ){ - updateDeleteLimitError(pParse,yymsp[-1].minor.yy242,yymsp[0].minor.yy202); - yymsp[-1].minor.yy242 = 0; - yymsp[0].minor.yy202 = 0; + if( yymsp[-1].minor.yy322 || yymsp[0].minor.yy528 ){ + updateDeleteLimitError(pParse,yymsp[-1].minor.yy322,yymsp[0].minor.yy528); + yymsp[-1].minor.yy322 = 0; + yymsp[0].minor.yy528 = 0; } #endif - sqlcipher_sqlite3Update(pParse,yymsp[-7].minor.yy47,yymsp[-4].minor.yy242,yymsp[-2].minor.yy202,yymsp[-8].minor.yy192,yymsp[-1].minor.yy242,yymsp[0].minor.yy202,0); + sqlcipher_sqlite3Update(pParse,yymsp[-7].minor.yy131,yymsp[-4].minor.yy322,yymsp[-2].minor.yy528,yymsp[-8].minor.yy394,yymsp[-1].minor.yy322,yymsp[0].minor.yy528,0); } break; - case 152: /* setlist ::= setlist COMMA nm EQ expr */ + case 158: /* setlist ::= setlist COMMA nm EQ expr */ { - yymsp[-4].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy242, yymsp[0].minor.yy202); - sqlcipher_sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy242, &yymsp[-2].minor.yy0, 1); + yymsp[-4].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[0].minor.yy528); + sqlcipher_sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, 1); } break; - case 153: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ + case 159: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ { - yymsp[-6].minor.yy242 = sqlcipher_sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy242, yymsp[-3].minor.yy600, yymsp[0].minor.yy202); + yymsp[-6].minor.yy322 = sqlcipher_sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy322, yymsp[-3].minor.yy254, yymsp[0].minor.yy528); } break; - case 154: /* setlist ::= nm EQ expr */ + case 160: /* setlist ::= nm EQ expr */ { - yylhsminor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy202); - sqlcipher_sqlite3ExprListSetName(pParse, yylhsminor.yy242, &yymsp[-2].minor.yy0, 1); + yylhsminor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy528); + sqlcipher_sqlite3ExprListSetName(pParse, yylhsminor.yy322, &yymsp[-2].minor.yy0, 1); } - yymsp[-2].minor.yy242 = yylhsminor.yy242; + yymsp[-2].minor.yy322 = yylhsminor.yy322; break; - case 155: /* setlist ::= LP idlist RP EQ expr */ + case 161: /* setlist ::= LP idlist RP EQ expr */ { - yymsp[-4].minor.yy242 = sqlcipher_sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy600, yymsp[0].minor.yy202); + yymsp[-4].minor.yy322 = sqlcipher_sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy254, yymsp[0].minor.yy528); } break; - case 156: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + case 162: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ { - sqlcipher_sqlite3Insert(pParse, yymsp[-3].minor.yy47, yymsp[-1].minor.yy539, yymsp[-2].minor.yy600, yymsp[-5].minor.yy192, yymsp[0].minor.yy318); + sqlcipher_sqlite3Insert(pParse, yymsp[-3].minor.yy131, yymsp[-1].minor.yy47, yymsp[-2].minor.yy254, yymsp[-5].minor.yy394, yymsp[0].minor.yy444); } break; - case 157: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ + case 163: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ { - sqlcipher_sqlite3Insert(pParse, yymsp[-3].minor.yy47, 0, yymsp[-2].minor.yy600, yymsp[-5].minor.yy192, 0); + sqlcipher_sqlite3Insert(pParse, yymsp[-4].minor.yy131, 0, yymsp[-3].minor.yy254, yymsp[-6].minor.yy394, 0); } break; - case 158: /* upsert ::= */ -{ yymsp[1].minor.yy318 = 0; } + case 164: /* upsert ::= */ +{ yymsp[1].minor.yy444 = 0; } + break; + case 165: /* upsert ::= RETURNING selcollist */ +{ yymsp[-1].minor.yy444 = 0; sqlcipher_sqlite3AddReturning(pParse,yymsp[0].minor.yy322); } + break; + case 166: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ +{ yymsp[-11].minor.yy444 = sqlcipher_sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy322,yymsp[-6].minor.yy528,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,yymsp[0].minor.yy444);} + break; + case 167: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ +{ yymsp[-8].minor.yy444 = sqlcipher_sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy322,yymsp[-3].minor.yy528,0,0,yymsp[0].minor.yy444); } + break; + case 168: /* upsert ::= ON CONFLICT DO NOTHING returning */ +{ yymsp[-4].minor.yy444 = sqlcipher_sqlite3UpsertNew(pParse->db,0,0,0,0,0); } break; - case 159: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ -{ yymsp[-10].minor.yy318 = sqlcipher_sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy242,yymsp[-5].minor.yy202,yymsp[-1].minor.yy242,yymsp[0].minor.yy202);} + case 169: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ +{ yymsp[-7].minor.yy444 = sqlcipher_sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,0);} break; - case 160: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ -{ yymsp[-7].minor.yy318 = sqlcipher_sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy242,yymsp[-2].minor.yy202,0,0); } + case 170: /* returning ::= RETURNING selcollist */ +{sqlcipher_sqlite3AddReturning(pParse,yymsp[0].minor.yy322);} break; - case 161: /* upsert ::= ON CONFLICT DO NOTHING */ -{ yymsp[-3].minor.yy318 = sqlcipher_sqlite3UpsertNew(pParse->db,0,0,0,0); } + case 173: /* idlist_opt ::= */ +{yymsp[1].minor.yy254 = 0;} break; - case 165: /* idlist_opt ::= LP idlist RP */ -{yymsp[-2].minor.yy600 = yymsp[-1].minor.yy600;} + case 174: /* idlist_opt ::= LP idlist RP */ +{yymsp[-2].minor.yy254 = yymsp[-1].minor.yy254;} break; - case 166: /* idlist ::= idlist COMMA nm */ -{yymsp[-2].minor.yy600 = sqlcipher_sqlite3IdListAppend(pParse,yymsp[-2].minor.yy600,&yymsp[0].minor.yy0);} + case 175: /* idlist ::= idlist COMMA nm */ +{yymsp[-2].minor.yy254 = sqlcipher_sqlite3IdListAppend(pParse,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);} break; - case 167: /* idlist ::= nm */ -{yymsp[0].minor.yy600 = sqlcipher_sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} + case 176: /* idlist ::= nm */ +{yymsp[0].minor.yy254 = sqlcipher_sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} break; - case 168: /* expr ::= LP expr RP */ -{yymsp[-2].minor.yy202 = yymsp[-1].minor.yy202;} + case 177: /* expr ::= LP expr RP */ +{yymsp[-2].minor.yy528 = yymsp[-1].minor.yy528;} break; - case 169: /* expr ::= ID|INDEXED */ - case 170: /* expr ::= JOIN_KW */ yytestcase(yyruleno==170); -{yymsp[0].minor.yy202=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 178: /* expr ::= ID|INDEXED */ + case 179: /* expr ::= JOIN_KW */ yytestcase(yyruleno==179); +{yymsp[0].minor.yy528=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 171: /* expr ::= nm DOT nm */ + case 180: /* expr ::= nm DOT nm */ { - Expr *temp1 = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); - Expr *temp2 = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1); - if( IN_RENAME_OBJECT ){ - sqlcipher_sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0); - sqlcipher_sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0); - } - yylhsminor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_DOT, temp1, temp2); + Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); + Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); + yylhsminor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } - yymsp[-2].minor.yy202 = yylhsminor.yy202; + yymsp[-2].minor.yy528 = yylhsminor.yy528; break; - case 172: /* expr ::= nm DOT nm DOT nm */ + case 181: /* expr ::= nm DOT nm DOT nm */ { - Expr *temp1 = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1); - Expr *temp2 = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); - Expr *temp3 = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1); + Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-4].minor.yy0); + Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); + Expr *temp3 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); Expr *temp4 = sqlcipher_sqlite3PExpr(pParse, TK_DOT, temp2, temp3); if( IN_RENAME_OBJECT ){ - sqlcipher_sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0); - sqlcipher_sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0); + sqlcipher_sqlite3RenameTokenRemap(pParse, 0, temp1); } - yylhsminor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_DOT, temp1, temp4); + yylhsminor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } - yymsp[-4].minor.yy202 = yylhsminor.yy202; + yymsp[-4].minor.yy528 = yylhsminor.yy528; break; - case 173: /* term ::= NULL|FLOAT|BLOB */ - case 174: /* term ::= STRING */ yytestcase(yyruleno==174); -{yymsp[0].minor.yy202=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 182: /* term ::= NULL|FLOAT|BLOB */ + case 183: /* term ::= STRING */ yytestcase(yyruleno==183); +{yymsp[0].minor.yy528=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 175: /* term ::= INTEGER */ + case 184: /* term ::= INTEGER */ { - yylhsminor.yy202 = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); + yylhsminor.yy528 = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); + if( yylhsminor.yy528 ) yylhsminor.yy528->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); } - yymsp[0].minor.yy202 = yylhsminor.yy202; + yymsp[0].minor.yy528 = yylhsminor.yy528; break; - case 176: /* expr ::= VARIABLE */ + case 185: /* expr ::= VARIABLE */ { if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlcipher_sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ u32 n = yymsp[0].minor.yy0.n; - yymsp[0].minor.yy202 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); - sqlcipher_sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy202, n); + yymsp[0].minor.yy528 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); + sqlcipher_sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy528, n); }else{ /* When doing a nested parse, one can include terms in an expression ** that look like this: #1 #2 ... These terms refer to registers @@ -163968,159 +173246,179 @@ static YYACTIONTYPE yy_reduce( assert( t.n>=2 ); if( pParse->nested==0 ){ sqlcipher_sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); - yymsp[0].minor.yy202 = 0; + yymsp[0].minor.yy528 = 0; }else{ - yymsp[0].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_REGISTER, 0, 0); - if( yymsp[0].minor.yy202 ) sqlcipher_sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy202->iTable); + yymsp[0].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_REGISTER, 0, 0); + if( yymsp[0].minor.yy528 ) sqlcipher_sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy528->iTable); } } } break; - case 177: /* expr ::= expr COLLATE ID|STRING */ + case 186: /* expr ::= expr COLLATE ID|STRING */ { - yymsp[-2].minor.yy202 = sqlcipher_sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy202, &yymsp[0].minor.yy0, 1); + yymsp[-2].minor.yy528 = sqlcipher_sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy528, &yymsp[0].minor.yy0, 1); } break; - case 178: /* expr ::= CAST LP expr AS typetoken RP */ + case 187: /* expr ::= CAST LP expr AS typetoken RP */ { - yymsp[-5].minor.yy202 = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); - sqlcipher_sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy202, yymsp[-3].minor.yy202, 0); + yymsp[-5].minor.yy528 = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); + sqlcipher_sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy528, yymsp[-3].minor.yy528, 0); } break; - case 179: /* expr ::= ID|INDEXED LP distinct exprlist RP */ + case 188: /* expr ::= ID|INDEXED LP distinct exprlist RP */ { - yylhsminor.yy202 = sqlcipher_sqlite3ExprFunction(pParse, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy192); + yylhsminor.yy528 = sqlcipher_sqlite3ExprFunction(pParse, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy394); } - yymsp[-4].minor.yy202 = yylhsminor.yy202; + yymsp[-4].minor.yy528 = yylhsminor.yy528; break; - case 180: /* expr ::= ID|INDEXED LP STAR RP */ + case 189: /* expr ::= ID|INDEXED LP STAR RP */ { - yylhsminor.yy202 = sqlcipher_sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); + yylhsminor.yy528 = sqlcipher_sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); } - yymsp[-3].minor.yy202 = yylhsminor.yy202; + yymsp[-3].minor.yy528 = yylhsminor.yy528; break; - case 181: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ + case 190: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ { - yylhsminor.yy202 = sqlcipher_sqlite3ExprFunction(pParse, yymsp[-2].minor.yy242, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy192); - sqlcipher_sqlite3WindowAttach(pParse, yylhsminor.yy202, yymsp[0].minor.yy303); + yylhsminor.yy528 = sqlcipher_sqlite3ExprFunction(pParse, yymsp[-2].minor.yy322, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy394); + sqlcipher_sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); } - yymsp[-5].minor.yy202 = yylhsminor.yy202; + yymsp[-5].minor.yy528 = yylhsminor.yy528; break; - case 182: /* expr ::= ID|INDEXED LP STAR RP filter_over */ + case 191: /* expr ::= ID|INDEXED LP STAR RP filter_over */ { - yylhsminor.yy202 = sqlcipher_sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); - sqlcipher_sqlite3WindowAttach(pParse, yylhsminor.yy202, yymsp[0].minor.yy303); + yylhsminor.yy528 = sqlcipher_sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); + sqlcipher_sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); } - yymsp[-4].minor.yy202 = yylhsminor.yy202; + yymsp[-4].minor.yy528 = yylhsminor.yy528; break; - case 183: /* term ::= CTIME_KW */ + case 192: /* term ::= CTIME_KW */ { - yylhsminor.yy202 = sqlcipher_sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); + yylhsminor.yy528 = sqlcipher_sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); } - yymsp[0].minor.yy202 = yylhsminor.yy202; + yymsp[0].minor.yy528 = yylhsminor.yy528; break; - case 184: /* expr ::= LP nexprlist COMMA expr RP */ + case 193: /* expr ::= LP nexprlist COMMA expr RP */ { - ExprList *pList = sqlcipher_sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy242, yymsp[-1].minor.yy202); - yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_VECTOR, 0, 0); - if( yymsp[-4].minor.yy202 ){ - yymsp[-4].minor.yy202->x.pList = pList; + ExprList *pList = sqlcipher_sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528); + yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_VECTOR, 0, 0); + if( yymsp[-4].minor.yy528 ){ + yymsp[-4].minor.yy528->x.pList = pList; if( ALWAYS(pList->nExpr) ){ - yymsp[-4].minor.yy202->flags |= pList->a[0].pExpr->flags & EP_Propagate; + yymsp[-4].minor.yy528->flags |= pList->a[0].pExpr->flags & EP_Propagate; } }else{ sqlcipher_sqlite3ExprListDelete(pParse->db, pList); } } break; - case 185: /* expr ::= expr AND expr */ -{yymsp[-2].minor.yy202=sqlcipher_sqlite3ExprAnd(pParse,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);} + case 194: /* expr ::= expr AND expr */ +{yymsp[-2].minor.yy528=sqlcipher_sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} break; - case 186: /* expr ::= expr OR expr */ - case 187: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==187); - case 188: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==188); - case 189: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==189); - case 190: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==190); - case 191: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==191); - case 192: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==192); -{yymsp[-2].minor.yy202=sqlcipher_sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);} + case 195: /* expr ::= expr OR expr */ + case 196: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==196); + case 197: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==197); + case 198: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==198); + case 199: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==199); + case 200: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==200); + case 201: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==201); +{yymsp[-2].minor.yy528=sqlcipher_sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} break; - case 193: /* likeop ::= NOT LIKE_KW|MATCH */ + case 202: /* likeop ::= NOT LIKE_KW|MATCH */ {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} break; - case 194: /* expr ::= expr likeop expr */ + case 203: /* expr ::= expr likeop expr */ { ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; yymsp[-1].minor.yy0.n &= 0x7fffffff; - pList = sqlcipher_sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy202); - pList = sqlcipher_sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy202); - yymsp[-2].minor.yy202 = sqlcipher_sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); - if( bNot ) yymsp[-2].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy202, 0); - if( yymsp[-2].minor.yy202 ) yymsp[-2].minor.yy202->flags |= EP_InfixFunc; + pList = sqlcipher_sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy528); + pList = sqlcipher_sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy528); + yymsp[-2].minor.yy528 = sqlcipher_sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + if( bNot ) yymsp[-2].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy528, 0); + if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc; } break; - case 195: /* expr ::= expr likeop expr ESCAPE expr */ + case 204: /* expr ::= expr likeop expr ESCAPE expr */ { ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; yymsp[-3].minor.yy0.n &= 0x7fffffff; - pList = sqlcipher_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202); - pList = sqlcipher_sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy202); - pList = sqlcipher_sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy202); - yymsp[-4].minor.yy202 = sqlcipher_sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); - if( bNot ) yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); - if( yymsp[-4].minor.yy202 ) yymsp[-4].minor.yy202->flags |= EP_InfixFunc; + pList = sqlcipher_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); + pList = sqlcipher_sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy528); + pList = sqlcipher_sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); + yymsp[-4].minor.yy528 = sqlcipher_sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); + if( bNot ) yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); + if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->flags |= EP_InfixFunc; } break; - case 196: /* expr ::= expr ISNULL|NOTNULL */ -{yymsp[-1].minor.yy202 = sqlcipher_sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy202,0);} + case 205: /* expr ::= expr ISNULL|NOTNULL */ +{yymsp[-1].minor.yy528 = sqlcipher_sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);} + break; + case 206: /* expr ::= expr NOT NULL */ +{yymsp[-2].minor.yy528 = sqlcipher_sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);} + break; + case 207: /* expr ::= expr IS expr */ +{ + yymsp[-2].minor.yy528 = sqlcipher_sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy528,yymsp[0].minor.yy528); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-2].minor.yy528, TK_ISNULL); +} break; - case 197: /* expr ::= expr NOT NULL */ -{yymsp[-2].minor.yy202 = sqlcipher_sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy202,0);} + case 208: /* expr ::= expr IS NOT expr */ +{ + yymsp[-3].minor.yy528 = sqlcipher_sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy528,yymsp[0].minor.yy528); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-3].minor.yy528, TK_NOTNULL); +} break; - case 198: /* expr ::= expr IS expr */ + case 209: /* expr ::= expr IS NOT DISTINCT FROM expr */ { - yymsp[-2].minor.yy202 = sqlcipher_sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy202,yymsp[0].minor.yy202); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy202, yymsp[-2].minor.yy202, TK_ISNULL); + yymsp[-5].minor.yy528 = sqlcipher_sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy528,yymsp[0].minor.yy528); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-5].minor.yy528, TK_ISNULL); } break; - case 199: /* expr ::= expr IS NOT expr */ + case 210: /* expr ::= expr IS DISTINCT FROM expr */ { - yymsp[-3].minor.yy202 = sqlcipher_sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy202,yymsp[0].minor.yy202); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy202, yymsp[-3].minor.yy202, TK_NOTNULL); + yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy528,yymsp[0].minor.yy528); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-4].minor.yy528, TK_NOTNULL); } break; - case 200: /* expr ::= NOT expr */ - case 201: /* expr ::= BITNOT expr */ yytestcase(yyruleno==201); -{yymsp[-1].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy202, 0);/*A-overwrites-B*/} + case 211: /* expr ::= NOT expr */ + case 212: /* expr ::= BITNOT expr */ yytestcase(yyruleno==212); +{yymsp[-1].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy528, 0);/*A-overwrites-B*/} break; - case 202: /* expr ::= PLUS|MINUS expr */ + case 213: /* expr ::= PLUS|MINUS expr */ { - yymsp[-1].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy202, 0); + yymsp[-1].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy528, 0); /*A-overwrites-B*/ } break; - case 203: /* between_op ::= BETWEEN */ - case 206: /* in_op ::= IN */ yytestcase(yyruleno==206); -{yymsp[0].minor.yy192 = 0;} + case 214: /* expr ::= expr PTR expr */ +{ + ExprList *pList = sqlcipher_sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528); + pList = sqlcipher_sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528); + yylhsminor.yy528 = sqlcipher_sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); +} + yymsp[-2].minor.yy528 = yylhsminor.yy528; + break; + case 215: /* between_op ::= BETWEEN */ + case 218: /* in_op ::= IN */ yytestcase(yyruleno==218); +{yymsp[0].minor.yy394 = 0;} break; - case 205: /* expr ::= expr between_op expr AND expr */ + case 217: /* expr ::= expr between_op expr AND expr */ { - ExprList *pList = sqlcipher_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202); - pList = sqlcipher_sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy202); - yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy202, 0); - if( yymsp[-4].minor.yy202 ){ - yymsp[-4].minor.yy202->x.pList = pList; + ExprList *pList = sqlcipher_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); + pList = sqlcipher_sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); + yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy528, 0); + if( yymsp[-4].minor.yy528 ){ + yymsp[-4].minor.yy528->x.pList = pList; }else{ sqlcipher_sqlite3ExprListDelete(pParse->db, pList); } - if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); + if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 208: /* expr ::= expr in_op LP exprlist RP */ + case 220: /* expr ::= expr in_op LP exprlist RP */ { - if( yymsp[-1].minor.yy242==0 ){ + if( yymsp[-1].minor.yy322==0 ){ /* Expressions of the form ** ** expr1 IN () @@ -164129,197 +173427,206 @@ static YYACTIONTYPE yy_reduce( ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ - sqlcipher_sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy202); - yymsp[-4].minor.yy202 = sqlcipher_sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy192 ? "1" : "0"); - }else if( yymsp[-1].minor.yy242->nExpr==1 && sqlcipher_sqlite3ExprIsConstant(yymsp[-1].minor.yy242->a[0].pExpr) ){ - Expr *pRHS = yymsp[-1].minor.yy242->a[0].pExpr; - yymsp[-1].minor.yy242->a[0].pExpr = 0; - sqlcipher_sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242); - pRHS = sqlcipher_sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); - yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy202, pRHS); - if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); - }else{ - yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); - if( yymsp[-4].minor.yy202 ){ - yymsp[-4].minor.yy202->x.pList = yymsp[-1].minor.yy242; - sqlcipher_sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy202); + sqlcipher_sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy528); + yymsp[-4].minor.yy528 = sqlcipher_sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy394 ? "true" : "false"); + if( yymsp[-4].minor.yy528 ) sqlcipher_sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy528); + }else{ + Expr *pRHS = yymsp[-1].minor.yy322->a[0].pExpr; + if( yymsp[-1].minor.yy322->nExpr==1 && sqlcipher_sqlite3ExprIsConstant(pRHS) && yymsp[-4].minor.yy528->op!=TK_VECTOR ){ + yymsp[-1].minor.yy322->a[0].pExpr = 0; + sqlcipher_sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); + pRHS = sqlcipher_sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); + yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy528, pRHS); }else{ - sqlcipher_sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242); + yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); + if( yymsp[-4].minor.yy528==0 ){ + sqlcipher_sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); + }else if( yymsp[-4].minor.yy528->pLeft->op==TK_VECTOR ){ + int nExpr = yymsp[-4].minor.yy528->pLeft->x.pList->nExpr; + Select *pSelectRHS = sqlcipher_sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy322); + if( pSelectRHS ){ + parserDoubleLinkSelect(pParse, pSelectRHS); + sqlcipher_sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelectRHS); + } + }else{ + yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy322; + sqlcipher_sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528); + } } - if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); + if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } } break; - case 209: /* expr ::= LP select RP */ + case 221: /* expr ::= LP select RP */ { - yymsp[-2].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_SELECT, 0, 0); - sqlcipher_sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy202, yymsp[-1].minor.yy539); + yymsp[-2].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_SELECT, 0, 0); + sqlcipher_sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47); } break; - case 210: /* expr ::= expr in_op LP select RP */ + case 222: /* expr ::= expr in_op LP select RP */ { - yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); - sqlcipher_sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy202, yymsp[-1].minor.yy539); - if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); + yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); + sqlcipher_sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, yymsp[-1].minor.yy47); + if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 211: /* expr ::= expr in_op nm dbnm paren_exprlist */ + case 223: /* expr ::= expr in_op nm dbnm paren_exprlist */ { SrcList *pSrc = sqlcipher_sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); Select *pSelect = sqlcipher_sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); - if( yymsp[0].minor.yy242 ) sqlcipher_sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy242); - yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); - sqlcipher_sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy202, pSelect); - if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); + if( yymsp[0].minor.yy322 ) sqlcipher_sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy322); + yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); + sqlcipher_sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelect); + if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 212: /* expr ::= EXISTS LP select RP */ + case 224: /* expr ::= EXISTS LP select RP */ { Expr *p; - p = yymsp[-3].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_EXISTS, 0, 0); - sqlcipher_sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy539); + p = yymsp[-3].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_EXISTS, 0, 0); + sqlcipher_sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy47); } break; - case 213: /* expr ::= CASE case_operand case_exprlist case_else END */ + case 225: /* expr ::= CASE case_operand case_exprlist case_else END */ { - yymsp[-4].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy202, 0); - if( yymsp[-4].minor.yy202 ){ - yymsp[-4].minor.yy202->x.pList = yymsp[-1].minor.yy202 ? sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy242,yymsp[-1].minor.yy202) : yymsp[-2].minor.yy242; - sqlcipher_sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy202); + yymsp[-4].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy528, 0); + if( yymsp[-4].minor.yy528 ){ + yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy528 ? sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528) : yymsp[-2].minor.yy322; + sqlcipher_sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528); }else{ - sqlcipher_sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy242); - sqlcipher_sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy202); + sqlcipher_sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy322); + sqlcipher_sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); } } break; - case 214: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + case 226: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { - yymsp[-4].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, yymsp[-2].minor.yy202); - yymsp[-4].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, yymsp[0].minor.yy202); + yymsp[-4].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); + yymsp[-4].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[0].minor.yy528); } break; - case 215: /* case_exprlist ::= WHEN expr THEN expr */ + case 227: /* case_exprlist ::= WHEN expr THEN expr */ { - yymsp[-3].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202); - yymsp[-3].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy242, yymsp[0].minor.yy202); + yymsp[-3].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); + yymsp[-3].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528); } break; - case 218: /* case_operand ::= expr */ -{yymsp[0].minor.yy202 = yymsp[0].minor.yy202; /*A-overwrites-X*/} + case 230: /* case_operand ::= expr */ +{yymsp[0].minor.yy528 = yymsp[0].minor.yy528; /*A-overwrites-X*/} break; - case 221: /* nexprlist ::= nexprlist COMMA expr */ -{yymsp[-2].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy242,yymsp[0].minor.yy202);} + case 233: /* nexprlist ::= nexprlist COMMA expr */ +{yymsp[-2].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);} break; - case 222: /* nexprlist ::= expr */ -{yymsp[0].minor.yy242 = sqlcipher_sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy202); /*A-overwrites-Y*/} + case 234: /* nexprlist ::= expr */ +{yymsp[0].minor.yy322 = sqlcipher_sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/} break; - case 224: /* paren_exprlist ::= LP exprlist RP */ - case 229: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==229); -{yymsp[-2].minor.yy242 = yymsp[-1].minor.yy242;} + case 236: /* paren_exprlist ::= LP exprlist RP */ + case 241: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==241); +{yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;} break; - case 225: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + case 237: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ { sqlcipher_sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, - sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy242, yymsp[-10].minor.yy192, - &yymsp[-11].minor.yy0, yymsp[0].minor.yy202, SQLITE_SO_ASC, yymsp[-8].minor.yy192, SQLITE_IDXTYPE_APPDEF); + sqlcipher_sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394, + &yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF); if( IN_RENAME_OBJECT && pParse->pNewIndex ){ sqlcipher_sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); } } break; - case 226: /* uniqueflag ::= UNIQUE */ - case 268: /* raisetype ::= ABORT */ yytestcase(yyruleno==268); -{yymsp[0].minor.yy192 = OE_Abort;} + case 238: /* uniqueflag ::= UNIQUE */ + case 280: /* raisetype ::= ABORT */ yytestcase(yyruleno==280); +{yymsp[0].minor.yy394 = OE_Abort;} break; - case 227: /* uniqueflag ::= */ -{yymsp[1].minor.yy192 = OE_None;} + case 239: /* uniqueflag ::= */ +{yymsp[1].minor.yy394 = OE_None;} break; - case 230: /* eidlist ::= eidlist COMMA nm collate sortorder */ + case 242: /* eidlist ::= eidlist COMMA nm collate sortorder */ { - yymsp[-4].minor.yy242 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy242, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy192, yymsp[0].minor.yy192); + yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); } break; - case 231: /* eidlist ::= nm collate sortorder */ + case 243: /* eidlist ::= nm collate sortorder */ { - yymsp[-2].minor.yy242 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy192, yymsp[0].minor.yy192); /*A-overwrites-Y*/ + yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/ } break; - case 234: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlcipher_sqlite3DropIndex(pParse, yymsp[0].minor.yy47, yymsp[-1].minor.yy192);} + case 246: /* cmd ::= DROP INDEX ifexists fullname */ +{sqlcipher_sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);} break; - case 235: /* cmd ::= VACUUM vinto */ -{sqlcipher_sqlite3Vacuum(pParse,0,yymsp[0].minor.yy202);} + case 247: /* cmd ::= VACUUM vinto */ +{sqlcipher_sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);} break; - case 236: /* cmd ::= VACUUM nm vinto */ -{sqlcipher_sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy202);} + case 248: /* cmd ::= VACUUM nm vinto */ +{sqlcipher_sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);} break; - case 239: /* cmd ::= PRAGMA nm dbnm */ + case 251: /* cmd ::= PRAGMA nm dbnm */ {sqlcipher_sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} break; - case 240: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + case 252: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ {sqlcipher_sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} break; - case 241: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + case 253: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ {sqlcipher_sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} break; - case 242: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ + case 254: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ {sqlcipher_sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} break; - case 243: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ + case 255: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ {sqlcipher_sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} break; - case 246: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + case 258: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ { Token all; all.z = yymsp[-3].minor.yy0.z; all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - sqlcipher_sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy447, &all); + sqlcipher_sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all); } break; - case 247: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 259: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { - sqlcipher_sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy192, yymsp[-4].minor.yy230.a, yymsp[-4].minor.yy230.b, yymsp[-2].minor.yy47, yymsp[0].minor.yy202, yymsp[-10].minor.yy192, yymsp[-8].minor.yy192); + sqlcipher_sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ } break; - case 248: /* trigger_time ::= BEFORE|AFTER */ -{ yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-X*/ } + case 260: /* trigger_time ::= BEFORE|AFTER */ +{ yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ } break; - case 249: /* trigger_time ::= INSTEAD OF */ -{ yymsp[-1].minor.yy192 = TK_INSTEAD;} + case 261: /* trigger_time ::= INSTEAD OF */ +{ yymsp[-1].minor.yy394 = TK_INSTEAD;} break; - case 250: /* trigger_time ::= */ -{ yymsp[1].minor.yy192 = TK_BEFORE; } + case 262: /* trigger_time ::= */ +{ yymsp[1].minor.yy394 = TK_BEFORE; } break; - case 251: /* trigger_event ::= DELETE|INSERT */ - case 252: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==252); -{yymsp[0].minor.yy230.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy230.b = 0;} + case 263: /* trigger_event ::= DELETE|INSERT */ + case 264: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==264); +{yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;} break; - case 253: /* trigger_event ::= UPDATE OF idlist */ -{yymsp[-2].minor.yy230.a = TK_UPDATE; yymsp[-2].minor.yy230.b = yymsp[0].minor.yy600;} + case 265: /* trigger_event ::= UPDATE OF idlist */ +{yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;} break; - case 254: /* when_clause ::= */ - case 273: /* key_opt ::= */ yytestcase(yyruleno==273); -{ yymsp[1].minor.yy202 = 0; } + case 266: /* when_clause ::= */ + case 285: /* key_opt ::= */ yytestcase(yyruleno==285); +{ yymsp[1].minor.yy528 = 0; } break; - case 255: /* when_clause ::= WHEN expr */ - case 274: /* key_opt ::= KEY expr */ yytestcase(yyruleno==274); -{ yymsp[-1].minor.yy202 = yymsp[0].minor.yy202; } + case 267: /* when_clause ::= WHEN expr */ + case 286: /* key_opt ::= KEY expr */ yytestcase(yyruleno==286); +{ yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; } break; - case 256: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 268: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { - assert( yymsp[-2].minor.yy447!=0 ); - yymsp[-2].minor.yy447->pLast->pNext = yymsp[-1].minor.yy447; - yymsp[-2].minor.yy447->pLast = yymsp[-1].minor.yy447; + assert( yymsp[-2].minor.yy33!=0 ); + yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33; + yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33; } break; - case 257: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 269: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - assert( yymsp[-1].minor.yy447!=0 ); - yymsp[-1].minor.yy447->pLast = yymsp[-1].minor.yy447; + assert( yymsp[-1].minor.yy33!=0 ); + yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33; } break; - case 258: /* trnm ::= nm DOT nm */ + case 270: /* trnm ::= nm DOT nm */ { yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; sqlcipher_sqlite3ErrorMsg(pParse, @@ -164327,344 +173634,370 @@ static YYACTIONTYPE yy_reduce( "statements within triggers"); } break; - case 259: /* tridxby ::= INDEXED BY nm */ + case 271: /* tridxby ::= INDEXED BY nm */ { sqlcipher_sqlite3ErrorMsg(pParse, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 260: /* tridxby ::= NOT INDEXED */ + case 272: /* tridxby ::= NOT INDEXED */ { sqlcipher_sqlite3ErrorMsg(pParse, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 261: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ -{yylhsminor.yy447 = sqlcipher_sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy47, yymsp[-3].minor.yy242, yymsp[-1].minor.yy202, yymsp[-7].minor.yy192, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy436);} - yymsp[-8].minor.yy447 = yylhsminor.yy447; + case 273: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ +{yylhsminor.yy33 = sqlcipher_sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);} + yymsp[-8].minor.yy33 = yylhsminor.yy33; break; - case 262: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + case 274: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ { - yylhsminor.yy447 = sqlcipher_sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy600,yymsp[-2].minor.yy539,yymsp[-6].minor.yy192,yymsp[-1].minor.yy318,yymsp[-7].minor.yy436,yymsp[0].minor.yy436);/*yylhsminor.yy447-overwrites-yymsp[-6].minor.yy192*/ + yylhsminor.yy33 = sqlcipher_sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/ } - yymsp[-7].minor.yy447 = yylhsminor.yy447; + yymsp[-7].minor.yy33 = yylhsminor.yy33; break; - case 263: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -{yylhsminor.yy447 = sqlcipher_sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy202, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy436);} - yymsp[-5].minor.yy447 = yylhsminor.yy447; + case 275: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ +{yylhsminor.yy33 = sqlcipher_sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);} + yymsp[-5].minor.yy33 = yylhsminor.yy33; break; - case 264: /* trigger_cmd ::= scanpt select scanpt */ -{yylhsminor.yy447 = sqlcipher_sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy539, yymsp[-2].minor.yy436, yymsp[0].minor.yy436); /*yylhsminor.yy447-overwrites-yymsp[-1].minor.yy539*/} - yymsp[-2].minor.yy447 = yylhsminor.yy447; + case 276: /* trigger_cmd ::= scanpt select scanpt */ +{yylhsminor.yy33 = sqlcipher_sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/} + yymsp[-2].minor.yy33 = yylhsminor.yy33; break; - case 265: /* expr ::= RAISE LP IGNORE RP */ + case 277: /* expr ::= RAISE LP IGNORE RP */ { - yymsp[-3].minor.yy202 = sqlcipher_sqlite3PExpr(pParse, TK_RAISE, 0, 0); - if( yymsp[-3].minor.yy202 ){ - yymsp[-3].minor.yy202->affExpr = OE_Ignore; + yymsp[-3].minor.yy528 = sqlcipher_sqlite3PExpr(pParse, TK_RAISE, 0, 0); + if( yymsp[-3].minor.yy528 ){ + yymsp[-3].minor.yy528->affExpr = OE_Ignore; } } break; - case 266: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 278: /* expr ::= RAISE LP raisetype COMMA nm RP */ { - yymsp[-5].minor.yy202 = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); - if( yymsp[-5].minor.yy202 ) { - yymsp[-5].minor.yy202->affExpr = (char)yymsp[-3].minor.yy192; + yymsp[-5].minor.yy528 = sqlcipher_sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); + if( yymsp[-5].minor.yy528 ) { + yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394; } } break; - case 267: /* raisetype ::= ROLLBACK */ -{yymsp[0].minor.yy192 = OE_Rollback;} + case 279: /* raisetype ::= ROLLBACK */ +{yymsp[0].minor.yy394 = OE_Rollback;} break; - case 269: /* raisetype ::= FAIL */ -{yymsp[0].minor.yy192 = OE_Fail;} + case 281: /* raisetype ::= FAIL */ +{yymsp[0].minor.yy394 = OE_Fail;} break; - case 270: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 282: /* cmd ::= DROP TRIGGER ifexists fullname */ { - sqlcipher_sqlite3DropTrigger(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy192); + sqlcipher_sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394); } break; - case 271: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 283: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - sqlcipher_sqlite3Attach(pParse, yymsp[-3].minor.yy202, yymsp[-1].minor.yy202, yymsp[0].minor.yy202); + sqlcipher_sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528); } break; - case 272: /* cmd ::= DETACH database_kw_opt expr */ + case 284: /* cmd ::= DETACH database_kw_opt expr */ { - sqlcipher_sqlite3Detach(pParse, yymsp[0].minor.yy202); + sqlcipher_sqlite3Detach(pParse, yymsp[0].minor.yy528); } break; - case 275: /* cmd ::= REINDEX */ + case 287: /* cmd ::= REINDEX */ {sqlcipher_sqlite3Reindex(pParse, 0, 0);} break; - case 276: /* cmd ::= REINDEX nm dbnm */ + case 288: /* cmd ::= REINDEX nm dbnm */ {sqlcipher_sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 277: /* cmd ::= ANALYZE */ + case 289: /* cmd ::= ANALYZE */ {sqlcipher_sqlite3Analyze(pParse, 0, 0);} break; - case 278: /* cmd ::= ANALYZE nm dbnm */ + case 290: /* cmd ::= ANALYZE nm dbnm */ {sqlcipher_sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 279: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 291: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - sqlcipher_sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy47,&yymsp[0].minor.yy0); + sqlcipher_sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0); } break; - case 280: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + case 292: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ { yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; sqlcipher_sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); } break; - case 281: /* add_column_fullname ::= fullname */ + case 293: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ +{ + sqlcipher_sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0); +} + break; + case 294: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); - sqlcipher_sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy47); + sqlcipher_sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131); } break; - case 282: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + case 295: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ { - sqlcipher_sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy47, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); + sqlcipher_sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; - case 283: /* cmd ::= create_vtab */ + case 296: /* cmd ::= create_vtab */ {sqlcipher_sqlite3VtabFinishParse(pParse,0);} break; - case 284: /* cmd ::= create_vtab LP vtabarglist RP */ + case 297: /* cmd ::= create_vtab LP vtabarglist RP */ {sqlcipher_sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} break; - case 285: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + case 298: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { - sqlcipher_sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy192); + sqlcipher_sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394); } break; - case 286: /* vtabarg ::= */ + case 299: /* vtabarg ::= */ {sqlcipher_sqlite3VtabArgInit(pParse);} break; - case 287: /* vtabargtoken ::= ANY */ - case 288: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==288); - case 289: /* lp ::= LP */ yytestcase(yyruleno==289); + case 300: /* vtabargtoken ::= ANY */ + case 301: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==301); + case 302: /* lp ::= LP */ yytestcase(yyruleno==302); {sqlcipher_sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; - case 290: /* with ::= WITH wqlist */ - case 291: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==291); -{ sqlcipher_sqlite3WithPush(pParse, yymsp[0].minor.yy131, 1); } + case 303: /* with ::= WITH wqlist */ + case 304: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==304); +{ sqlcipher_sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); } break; - case 292: /* wqlist ::= nm eidlist_opt AS LP select RP */ + case 305: /* wqas ::= AS */ +{yymsp[0].minor.yy516 = M10d_Any;} + break; + case 306: /* wqas ::= AS MATERIALIZED */ +{yymsp[-1].minor.yy516 = M10d_Yes;} + break; + case 307: /* wqas ::= AS NOT MATERIALIZED */ +{yymsp[-2].minor.yy516 = M10d_No;} + break; + case 308: /* wqitem ::= nm eidlist_opt wqas LP select RP */ +{ + yymsp[-5].minor.yy385 = sqlcipher_sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/ +} + break; + case 309: /* wqlist ::= wqitem */ { - yymsp[-5].minor.yy131 = sqlcipher_sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy242, yymsp[-1].minor.yy539); /*A-overwrites-X*/ + yymsp[0].minor.yy521 = sqlcipher_sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/ } break; - case 293: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ + case 310: /* wqlist ::= wqlist COMMA wqitem */ { - yymsp[-7].minor.yy131 = sqlcipher_sqlite3WithAdd(pParse, yymsp[-7].minor.yy131, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy242, yymsp[-1].minor.yy539); + yymsp[-2].minor.yy521 = sqlcipher_sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385); } break; - case 294: /* windowdefn_list ::= windowdefn */ -{ yylhsminor.yy303 = yymsp[0].minor.yy303; } - yymsp[0].minor.yy303 = yylhsminor.yy303; + case 311: /* windowdefn_list ::= windowdefn */ +{ yylhsminor.yy41 = yymsp[0].minor.yy41; } + yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 295: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 312: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { - assert( yymsp[0].minor.yy303!=0 ); - sqlcipher_sqlite3WindowChain(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy303); - yymsp[0].minor.yy303->pNextWin = yymsp[-2].minor.yy303; - yylhsminor.yy303 = yymsp[0].minor.yy303; + assert( yymsp[0].minor.yy41!=0 ); + sqlcipher_sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41); + yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41; + yylhsminor.yy41 = yymsp[0].minor.yy41; } - yymsp[-2].minor.yy303 = yylhsminor.yy303; + yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 296: /* windowdefn ::= nm AS LP window RP */ + case 313: /* windowdefn ::= nm AS LP window RP */ { - if( ALWAYS(yymsp[-1].minor.yy303) ){ - yymsp[-1].minor.yy303->zName = sqlcipher_sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); + if( ALWAYS(yymsp[-1].minor.yy41) ){ + yymsp[-1].minor.yy41->zName = sqlcipher_sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); } - yylhsminor.yy303 = yymsp[-1].minor.yy303; + yylhsminor.yy41 = yymsp[-1].minor.yy41; } - yymsp[-4].minor.yy303 = yylhsminor.yy303; + yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 297: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + case 314: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ { - yymsp[-4].minor.yy303 = sqlcipher_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy242, yymsp[-1].minor.yy242, 0); + yymsp[-4].minor.yy41 = sqlcipher_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0); } break; - case 298: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + case 315: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ { - yylhsminor.yy303 = sqlcipher_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy242, yymsp[-1].minor.yy242, &yymsp[-5].minor.yy0); + yylhsminor.yy41 = sqlcipher_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0); } - yymsp[-5].minor.yy303 = yylhsminor.yy303; + yymsp[-5].minor.yy41 = yylhsminor.yy41; break; - case 299: /* window ::= ORDER BY sortlist frame_opt */ + case 316: /* window ::= ORDER BY sortlist frame_opt */ { - yymsp[-3].minor.yy303 = sqlcipher_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, 0); + yymsp[-3].minor.yy41 = sqlcipher_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0); } break; - case 300: /* window ::= nm ORDER BY sortlist frame_opt */ + case 317: /* window ::= nm ORDER BY sortlist frame_opt */ { - yylhsminor.yy303 = sqlcipher_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0); + yylhsminor.yy41 = sqlcipher_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0); } - yymsp[-4].minor.yy303 = yylhsminor.yy303; + yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 301: /* window ::= frame_opt */ - case 320: /* filter_over ::= over_clause */ yytestcase(yyruleno==320); + case 318: /* window ::= frame_opt */ + case 337: /* filter_over ::= over_clause */ yytestcase(yyruleno==337); { - yylhsminor.yy303 = yymsp[0].minor.yy303; + yylhsminor.yy41 = yymsp[0].minor.yy41; } - yymsp[0].minor.yy303 = yylhsminor.yy303; + yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 302: /* window ::= nm frame_opt */ + case 319: /* window ::= nm frame_opt */ { - yylhsminor.yy303 = sqlcipher_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, 0, &yymsp[-1].minor.yy0); + yylhsminor.yy41 = sqlcipher_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0); } - yymsp[-1].minor.yy303 = yylhsminor.yy303; + yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 303: /* frame_opt ::= */ + case 320: /* frame_opt ::= */ { - yymsp[1].minor.yy303 = sqlcipher_sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); + yymsp[1].minor.yy41 = sqlcipher_sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); } break; - case 304: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + case 321: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ { - yylhsminor.yy303 = sqlcipher_sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy192, yymsp[-1].minor.yy77.eType, yymsp[-1].minor.yy77.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy58); + yylhsminor.yy41 = sqlcipher_sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516); } - yymsp[-2].minor.yy303 = yylhsminor.yy303; + yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 305: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + case 322: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ { - yylhsminor.yy303 = sqlcipher_sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy192, yymsp[-3].minor.yy77.eType, yymsp[-3].minor.yy77.pExpr, yymsp[-1].minor.yy77.eType, yymsp[-1].minor.yy77.pExpr, yymsp[0].minor.yy58); + yylhsminor.yy41 = sqlcipher_sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516); } - yymsp[-5].minor.yy303 = yylhsminor.yy303; + yymsp[-5].minor.yy41 = yylhsminor.yy41; break; - case 307: /* frame_bound_s ::= frame_bound */ - case 309: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==309); -{yylhsminor.yy77 = yymsp[0].minor.yy77;} - yymsp[0].minor.yy77 = yylhsminor.yy77; + case 324: /* frame_bound_s ::= frame_bound */ + case 326: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==326); +{yylhsminor.yy595 = yymsp[0].minor.yy595;} + yymsp[0].minor.yy595 = yylhsminor.yy595; break; - case 308: /* frame_bound_s ::= UNBOUNDED PRECEDING */ - case 310: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==310); - case 312: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==312); -{yylhsminor.yy77.eType = yymsp[-1].major; yylhsminor.yy77.pExpr = 0;} - yymsp[-1].minor.yy77 = yylhsminor.yy77; + case 325: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 327: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==327); + case 329: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==329); +{yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;} + yymsp[-1].minor.yy595 = yylhsminor.yy595; break; - case 311: /* frame_bound ::= expr PRECEDING|FOLLOWING */ -{yylhsminor.yy77.eType = yymsp[0].major; yylhsminor.yy77.pExpr = yymsp[-1].minor.yy202;} - yymsp[-1].minor.yy77 = yylhsminor.yy77; + case 328: /* frame_bound ::= expr PRECEDING|FOLLOWING */ +{yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;} + yymsp[-1].minor.yy595 = yylhsminor.yy595; break; - case 313: /* frame_exclude_opt ::= */ -{yymsp[1].minor.yy58 = 0;} + case 330: /* frame_exclude_opt ::= */ +{yymsp[1].minor.yy516 = 0;} break; - case 314: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ -{yymsp[-1].minor.yy58 = yymsp[0].minor.yy58;} + case 331: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ +{yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;} break; - case 315: /* frame_exclude ::= NO OTHERS */ - case 316: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==316); -{yymsp[-1].minor.yy58 = yymsp[-1].major; /*A-overwrites-X*/} + case 332: /* frame_exclude ::= NO OTHERS */ + case 333: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==333); +{yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/} break; - case 317: /* frame_exclude ::= GROUP|TIES */ -{yymsp[0].minor.yy58 = yymsp[0].major; /*A-overwrites-X*/} + case 334: /* frame_exclude ::= GROUP|TIES */ +{yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/} break; - case 318: /* window_clause ::= WINDOW windowdefn_list */ -{ yymsp[-1].minor.yy303 = yymsp[0].minor.yy303; } + case 335: /* window_clause ::= WINDOW windowdefn_list */ +{ yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; } break; - case 319: /* filter_over ::= filter_clause over_clause */ + case 336: /* filter_over ::= filter_clause over_clause */ { - yymsp[0].minor.yy303->pFilter = yymsp[-1].minor.yy202; - yylhsminor.yy303 = yymsp[0].minor.yy303; + if( yymsp[0].minor.yy41 ){ + yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528; + }else{ + sqlcipher_sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); + } + yylhsminor.yy41 = yymsp[0].minor.yy41; } - yymsp[-1].minor.yy303 = yylhsminor.yy303; + yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 321: /* filter_over ::= filter_clause */ + case 338: /* filter_over ::= filter_clause */ { - yylhsminor.yy303 = (Window*)sqlcipher_sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yylhsminor.yy303 ){ - yylhsminor.yy303->eFrmType = TK_FILTER; - yylhsminor.yy303->pFilter = yymsp[0].minor.yy202; + yylhsminor.yy41 = (Window*)sqlcipher_sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yylhsminor.yy41 ){ + yylhsminor.yy41->eFrmType = TK_FILTER; + yylhsminor.yy41->pFilter = yymsp[0].minor.yy528; }else{ - sqlcipher_sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy202); + sqlcipher_sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528); } } - yymsp[0].minor.yy303 = yylhsminor.yy303; + yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 322: /* over_clause ::= OVER LP window RP */ + case 339: /* over_clause ::= OVER LP window RP */ { - yymsp[-3].minor.yy303 = yymsp[-1].minor.yy303; - assert( yymsp[-3].minor.yy303!=0 ); + yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41; + assert( yymsp[-3].minor.yy41!=0 ); } break; - case 323: /* over_clause ::= OVER nm */ + case 340: /* over_clause ::= OVER nm */ { - yymsp[-1].minor.yy303 = (Window*)sqlcipher_sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yymsp[-1].minor.yy303 ){ - yymsp[-1].minor.yy303->zName = sqlcipher_sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); + yymsp[-1].minor.yy41 = (Window*)sqlcipher_sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yymsp[-1].minor.yy41 ){ + yymsp[-1].minor.yy41->zName = sqlcipher_sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); } } break; - case 324: /* filter_clause ::= FILTER LP WHERE expr RP */ -{ yymsp[-4].minor.yy202 = yymsp[-1].minor.yy202; } + case 341: /* filter_clause ::= FILTER LP WHERE expr RP */ +{ yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; } break; default: - /* (325) input ::= cmdlist */ yytestcase(yyruleno==325); - /* (326) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==326); - /* (327) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=327); - /* (328) ecmd ::= SEMI */ yytestcase(yyruleno==328); - /* (329) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==329); - /* (330) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=330); - /* (331) trans_opt ::= */ yytestcase(yyruleno==331); - /* (332) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==332); - /* (333) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==333); - /* (334) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==334); - /* (335) savepoint_opt ::= */ yytestcase(yyruleno==335); - /* (336) cmd ::= create_table create_table_args */ yytestcase(yyruleno==336); - /* (337) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==337); - /* (338) columnlist ::= columnname carglist */ yytestcase(yyruleno==338); - /* (339) nm ::= ID|INDEXED */ yytestcase(yyruleno==339); - /* (340) nm ::= STRING */ yytestcase(yyruleno==340); - /* (341) nm ::= JOIN_KW */ yytestcase(yyruleno==341); - /* (342) typetoken ::= typename */ yytestcase(yyruleno==342); - /* (343) typename ::= ID|STRING */ yytestcase(yyruleno==343); - /* (344) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=344); - /* (345) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=345); - /* (346) carglist ::= carglist ccons */ yytestcase(yyruleno==346); - /* (347) carglist ::= */ yytestcase(yyruleno==347); - /* (348) ccons ::= NULL onconf */ yytestcase(yyruleno==348); - /* (349) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==349); - /* (350) ccons ::= AS generated */ yytestcase(yyruleno==350); - /* (351) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==351); - /* (352) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==352); - /* (353) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=353); - /* (354) tconscomma ::= */ yytestcase(yyruleno==354); - /* (355) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=355); - /* (356) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=356); - /* (357) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=357); - /* (358) oneselect ::= values */ yytestcase(yyruleno==358); - /* (359) sclp ::= selcollist COMMA */ yytestcase(yyruleno==359); - /* (360) as ::= ID|STRING */ yytestcase(yyruleno==360); - /* (361) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=361); - /* (362) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==362); - /* (363) exprlist ::= nexprlist */ yytestcase(yyruleno==363); - /* (364) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=364); - /* (365) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=365); - /* (366) nmnum ::= ON */ yytestcase(yyruleno==366); - /* (367) nmnum ::= DELETE */ yytestcase(yyruleno==367); - /* (368) nmnum ::= DEFAULT */ yytestcase(yyruleno==368); - /* (369) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==369); - /* (370) foreach_clause ::= */ yytestcase(yyruleno==370); - /* (371) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==371); - /* (372) trnm ::= nm */ yytestcase(yyruleno==372); - /* (373) tridxby ::= */ yytestcase(yyruleno==373); - /* (374) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==374); - /* (375) database_kw_opt ::= */ yytestcase(yyruleno==375); - /* (376) kwcolumn_opt ::= */ yytestcase(yyruleno==376); - /* (377) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==377); - /* (378) vtabarglist ::= vtabarg */ yytestcase(yyruleno==378); - /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==379); - /* (380) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==380); - /* (381) anylist ::= */ yytestcase(yyruleno==381); - /* (382) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==382); - /* (383) anylist ::= anylist ANY */ yytestcase(yyruleno==383); - /* (384) with ::= */ yytestcase(yyruleno==384); + /* (342) input ::= cmdlist */ yytestcase(yyruleno==342); + /* (343) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==343); + /* (344) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=344); + /* (345) ecmd ::= SEMI */ yytestcase(yyruleno==345); + /* (346) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==346); + /* (347) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=347); + /* (348) trans_opt ::= */ yytestcase(yyruleno==348); + /* (349) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==349); + /* (350) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==350); + /* (351) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==351); + /* (352) savepoint_opt ::= */ yytestcase(yyruleno==352); + /* (353) cmd ::= create_table create_table_args */ yytestcase(yyruleno==353); + /* (354) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=354); + /* (355) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==355); + /* (356) columnlist ::= columnname carglist */ yytestcase(yyruleno==356); + /* (357) nm ::= ID|INDEXED */ yytestcase(yyruleno==357); + /* (358) nm ::= STRING */ yytestcase(yyruleno==358); + /* (359) nm ::= JOIN_KW */ yytestcase(yyruleno==359); + /* (360) typetoken ::= typename */ yytestcase(yyruleno==360); + /* (361) typename ::= ID|STRING */ yytestcase(yyruleno==361); + /* (362) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=362); + /* (363) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=363); + /* (364) carglist ::= carglist ccons */ yytestcase(yyruleno==364); + /* (365) carglist ::= */ yytestcase(yyruleno==365); + /* (366) ccons ::= NULL onconf */ yytestcase(yyruleno==366); + /* (367) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==367); + /* (368) ccons ::= AS generated */ yytestcase(yyruleno==368); + /* (369) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==369); + /* (370) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==370); + /* (371) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=371); + /* (372) tconscomma ::= */ yytestcase(yyruleno==372); + /* (373) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=373); + /* (374) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=374); + /* (375) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=375); + /* (376) oneselect ::= values */ yytestcase(yyruleno==376); + /* (377) sclp ::= selcollist COMMA */ yytestcase(yyruleno==377); + /* (378) as ::= ID|STRING */ yytestcase(yyruleno==378); + /* (379) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=379); + /* (380) returning ::= */ yytestcase(yyruleno==380); + /* (381) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=381); + /* (382) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==382); + /* (383) exprlist ::= nexprlist */ yytestcase(yyruleno==383); + /* (384) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=384); + /* (385) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=385); + /* (386) nmnum ::= ON */ yytestcase(yyruleno==386); + /* (387) nmnum ::= DELETE */ yytestcase(yyruleno==387); + /* (388) nmnum ::= DEFAULT */ yytestcase(yyruleno==388); + /* (389) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==389); + /* (390) foreach_clause ::= */ yytestcase(yyruleno==390); + /* (391) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==391); + /* (392) trnm ::= nm */ yytestcase(yyruleno==392); + /* (393) tridxby ::= */ yytestcase(yyruleno==393); + /* (394) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==394); + /* (395) database_kw_opt ::= */ yytestcase(yyruleno==395); + /* (396) kwcolumn_opt ::= */ yytestcase(yyruleno==396); + /* (397) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==397); + /* (398) vtabarglist ::= vtabarg */ yytestcase(yyruleno==398); + /* (399) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==399); + /* (400) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==400); + /* (401) anylist ::= */ yytestcase(yyruleno==401); + /* (402) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==402); + /* (403) anylist ::= anylist ANY */ yytestcase(yyruleno==403); + /* (404) with ::= */ yytestcase(yyruleno==404); break; /********** End reduce actions ************************************************/ }; @@ -164816,12 +174149,56 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Parser( } #endif - do{ + while(1){ /* Exit by "break" */ + assert( yypParser->yytos>=yypParser->yystack ); assert( yyact==yypParser->yytos->stateno ); yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact); if( yyact >= YY_MIN_REDUCE ){ - yyact = yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor, - yyminor sqlcipher_sqlite3ParserCTX_PARAM); + unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */ +#ifndef NDEBUG + assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); + if( yyTraceFILE ){ + int yysize = yyRuleInfoNRhs[yyruleno]; + if( yysize ){ + fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", + yyTracePrompt, + yyruleno, yyRuleName[yyruleno], + yyrulenoyytos[yysize].stateno); + }else{ + fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n", + yyTracePrompt, yyruleno, yyRuleName[yyruleno], + yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){ + yypParser->yyhwm++; + assert( yypParser->yyhwm == + (int)(yypParser->yytos - yypParser->yystack)); + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yytos>=yypParser->yystackEnd ){ + yyStackOverflow(yypParser); + break; + } +#else + if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ + if( yyGrowStack(yypParser) ){ + yyStackOverflow(yypParser); + break; + } + } +#endif + } + yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor sqlcipher_sqlite3ParserCTX_PARAM); }else if( yyact <= YY_MAX_SHIFTREDUCE ){ yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor); #ifndef YYNOERRORRECOVERY @@ -164877,14 +174254,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Parser( yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); yymajor = YYNOCODE; }else{ - while( yypParser->yytos >= yypParser->yystack - && (yyact = yy_find_reduce_action( - yypParser->yytos->stateno, - YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE - ){ + while( yypParser->yytos > yypParser->yystack ){ + yyact = yy_find_reduce_action(yypParser->yytos->stateno, + YYERRORSYMBOL); + if( yyact<=YY_MAX_SHIFTREDUCE ) break; yy_pop_parser_stack(yypParser); } - if( yypParser->yytos < yypParser->yystack || yymajor==0 ){ + if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); yy_parse_failed(yypParser); #ifndef YYNOERRORRECOVERY @@ -164934,7 +174310,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Parser( break; #endif } - }while( yypParser->yytos>yypParser->yystack ); + } #ifndef NDEBUG if( yyTraceFILE ){ yyStackEntry *i; @@ -164995,8 +174371,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ParserFallback(int iToken){ ** all of them need to be used within the switch. */ #define CC_X 0 /* The letter 'x', or start of BLOB literal */ -#define CC_KYWD 1 /* Alphabetics or '_'. Usable in a keyword */ -#define CC_ID 2 /* unicode characters usable in IDs */ +#define CC_KYWD0 1 /* First letter of a keyword */ +#define CC_KYWD 2 /* Alphabetics or '_'. Usable in a keyword */ #define CC_DIGIT 3 /* Digits */ #define CC_DOLLAR 4 /* '$' */ #define CC_VARALPHA 5 /* '@', '#', ':'. Alphabetic SQL variables */ @@ -165021,47 +174397,49 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ParserFallback(int iToken){ #define CC_AND 24 /* '&' */ #define CC_TILDA 25 /* '~' */ #define CC_DOT 26 /* '.' */ -#define CC_ILLEGAL 27 /* Illegal character */ -#define CC_NUL 28 /* 0x00 */ +#define CC_ID 27 /* unicode characters usable in IDs */ +#define CC_ILLEGAL 28 /* Illegal character */ +#define CC_NUL 29 /* 0x00 */ +#define CC_BOM 30 /* First byte of UTF8 BOM: 0xEF 0xBB 0xBF */ static const unsigned char aiClass[] = { #ifdef SQLITE_ASCII /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ -/* 0x */ 28, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27, -/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* 0x */ 29, 28, 28, 28, 28, 28, 28, 28, 28, 7, 7, 28, 7, 7, 28, 28, +/* 1x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, /* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16, /* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6, /* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 9, 27, 27, 27, 1, +/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 9, 28, 28, 28, 2, /* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 10, 27, 25, 27, -/* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* 9x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Ax */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Bx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Cx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Dx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Ex */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 +/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 28, 10, 28, 25, 28, +/* 8x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* 9x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* Ax */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* Cx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* Dx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* Ex */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 30, +/* Fx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27 #endif #ifdef SQLITE_EBCDIC /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ -/* 0x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 7, 7, 27, 27, -/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -/* 2x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -/* 3x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -/* 4x */ 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 12, 17, 20, 10, -/* 5x */ 24, 27, 27, 27, 27, 27, 27, 27, 27, 27, 15, 4, 21, 18, 19, 27, -/* 6x */ 11, 16, 27, 27, 27, 27, 27, 27, 27, 27, 27, 23, 22, 1, 13, 6, -/* 7x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 5, 5, 5, 8, 14, 8, -/* 8x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, -/* 9x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, -/* Ax */ 27, 25, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27, -/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 9, 27, 27, 27, 27, 27, -/* Cx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, -/* Dx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, -/* Ex */ 27, 27, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27, -/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 27, 27, 27, 27, 27, 27, +/* 0x */ 29, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 7, 7, 28, 28, +/* 1x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +/* 2x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +/* 3x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +/* 4x */ 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 12, 17, 20, 10, +/* 5x */ 24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 15, 4, 21, 18, 19, 28, +/* 6x */ 11, 16, 28, 28, 28, 28, 28, 28, 28, 28, 28, 23, 22, 2, 13, 6, +/* 7x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 8, 5, 5, 5, 8, 14, 8, +/* 8x */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, +/* 9x */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, +/* Ax */ 28, 25, 1, 1, 1, 1, 1, 0, 2, 2, 28, 28, 28, 28, 28, 28, +/* Bx */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 9, 28, 28, 28, 28, 28, +/* Cx */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, +/* Dx */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, +/* Ex */ 28, 28, 1, 1, 1, 1, 1, 0, 2, 2, 28, 28, 28, 28, 28, 28, +/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 28, 28, 28, 28, 28, 28, #endif }; @@ -165126,20 +174504,21 @@ const unsigned char ebcdicToAscii[] = { ** is substantially reduced. This is important for embedded applications ** on platforms with limited memory. */ -/* Hash score: 227 */ -/* zKWText[] encodes 984 bytes of keyword text in 648 bytes */ +/* Hash score: 231 */ +/* zKWText[] encodes 1007 bytes of keyword text in 667 bytes */ /* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */ /* ABLEFTHENDEFERRABLELSEXCLUDELETEMPORARYISNULLSAVEPOINTERSECT */ /* IESNOTNULLIKEXCEPTRANSACTIONATURALTERAISEXCLUSIVEXISTS */ /* CONSTRAINTOFFSETRIGGERANGENERATEDETACHAVINGLOBEGINNEREFERENCES */ /* UNIQUERYWITHOUTERELEASEATTACHBETWEENOTHINGROUPSCASCADEFAULT */ /* CASECOLLATECREATECURRENT_DATEIMMEDIATEJOINSERTMATCHPLANALYZE */ -/* PRAGMABORTUPDATEVALUESVIRTUALWAYSWHENWHERECURSIVEAFTERENAMEAND */ -/* EFERREDISTINCTAUTOINCREMENTCASTCOLUMNCOMMITCONFLICTCROSS */ -/* CURRENT_TIMESTAMPARTITIONDROPRECEDINGFAILASTFILTEREPLACEFIRST */ -/* FOLLOWINGFROMFULLIMITIFORDERESTRICTOTHERSOVERIGHTROLLBACKROWS */ -/* UNBOUNDEDUNIONUSINGVACUUMVIEWINDOWBYINITIALLYPRIMARY */ -static const char zKWText[647] = { +/* PRAGMATERIALIZEDEFERREDISTINCTUPDATEVALUESVIRTUALWAYSWHENWHERE */ +/* CURSIVEABORTAFTERENAMEANDROPARTITIONAUTOINCREMENTCASTCOLUMN */ +/* COMMITCONFLICTCROSSCURRENT_TIMESTAMPRECEDINGFAILASTFILTER */ +/* EPLACEFIRSTFOLLOWINGFROMFULLIMITIFORDERESTRICTOTHERSOVER */ +/* ETURNINGRIGHTROLLBACKROWSUNBOUNDEDUNIONUSINGVACUUMVIEWINDOWBY */ +/* INITIALLYPRIMARY */ +static const char zKWText[666] = { 'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H', 'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G', 'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A', @@ -165160,86 +174539,87 @@ static const char zKWText[647] = { 'C','R','E','A','T','E','C','U','R','R','E','N','T','_','D','A','T','E', 'I','M','M','E','D','I','A','T','E','J','O','I','N','S','E','R','T','M', 'A','T','C','H','P','L','A','N','A','L','Y','Z','E','P','R','A','G','M', - 'A','B','O','R','T','U','P','D','A','T','E','V','A','L','U','E','S','V', - 'I','R','T','U','A','L','W','A','Y','S','W','H','E','N','W','H','E','R', - 'E','C','U','R','S','I','V','E','A','F','T','E','R','E','N','A','M','E', - 'A','N','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T','A', - 'U','T','O','I','N','C','R','E','M','E','N','T','C','A','S','T','C','O', - 'L','U','M','N','C','O','M','M','I','T','C','O','N','F','L','I','C','T', - 'C','R','O','S','S','C','U','R','R','E','N','T','_','T','I','M','E','S', - 'T','A','M','P','A','R','T','I','T','I','O','N','D','R','O','P','R','E', - 'C','E','D','I','N','G','F','A','I','L','A','S','T','F','I','L','T','E', - 'R','E','P','L','A','C','E','F','I','R','S','T','F','O','L','L','O','W', - 'I','N','G','F','R','O','M','F','U','L','L','I','M','I','T','I','F','O', - 'R','D','E','R','E','S','T','R','I','C','T','O','T','H','E','R','S','O', - 'V','E','R','I','G','H','T','R','O','L','L','B','A','C','K','R','O','W', - 'S','U','N','B','O','U','N','D','E','D','U','N','I','O','N','U','S','I', - 'N','G','V','A','C','U','U','M','V','I','E','W','I','N','D','O','W','B', - 'Y','I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R','Y', + 'A','T','E','R','I','A','L','I','Z','E','D','E','F','E','R','R','E','D', + 'I','S','T','I','N','C','T','U','P','D','A','T','E','V','A','L','U','E', + 'S','V','I','R','T','U','A','L','W','A','Y','S','W','H','E','N','W','H', + 'E','R','E','C','U','R','S','I','V','E','A','B','O','R','T','A','F','T', + 'E','R','E','N','A','M','E','A','N','D','R','O','P','A','R','T','I','T', + 'I','O','N','A','U','T','O','I','N','C','R','E','M','E','N','T','C','A', + 'S','T','C','O','L','U','M','N','C','O','M','M','I','T','C','O','N','F', + 'L','I','C','T','C','R','O','S','S','C','U','R','R','E','N','T','_','T', + 'I','M','E','S','T','A','M','P','R','E','C','E','D','I','N','G','F','A', + 'I','L','A','S','T','F','I','L','T','E','R','E','P','L','A','C','E','F', + 'I','R','S','T','F','O','L','L','O','W','I','N','G','F','R','O','M','F', + 'U','L','L','I','M','I','T','I','F','O','R','D','E','R','E','S','T','R', + 'I','C','T','O','T','H','E','R','S','O','V','E','R','E','T','U','R','N', + 'I','N','G','R','I','G','H','T','R','O','L','L','B','A','C','K','R','O', + 'W','S','U','N','B','O','U','N','D','E','D','U','N','I','O','N','U','S', + 'I','N','G','V','A','C','U','U','M','V','I','E','W','I','N','D','O','W', + 'B','Y','I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R','Y', }; /* aKWHash[i] is the hash value for the i-th keyword */ static const unsigned char aKWHash[127] = { - 84, 102, 132, 82, 114, 29, 0, 0, 91, 0, 85, 72, 0, - 53, 35, 86, 15, 0, 42, 94, 54, 126, 133, 19, 0, 0, - 138, 0, 40, 128, 0, 22, 104, 0, 9, 0, 0, 122, 80, - 0, 78, 6, 0, 65, 99, 145, 0, 134, 112, 0, 0, 48, - 0, 100, 24, 0, 17, 0, 27, 70, 23, 26, 5, 60, 140, - 107, 121, 0, 73, 101, 71, 143, 61, 119, 74, 0, 49, 0, - 11, 41, 0, 110, 0, 0, 0, 106, 10, 108, 113, 124, 14, - 50, 123, 0, 89, 0, 18, 120, 142, 56, 129, 137, 88, 83, - 37, 30, 125, 0, 0, 105, 51, 130, 127, 0, 34, 0, 0, - 44, 0, 95, 38, 39, 0, 20, 45, 116, 90, + 84, 92, 134, 82, 105, 29, 0, 0, 94, 0, 85, 72, 0, + 53, 35, 86, 15, 0, 42, 97, 54, 89, 135, 19, 0, 0, + 140, 0, 40, 129, 0, 22, 107, 0, 9, 0, 0, 123, 80, + 0, 78, 6, 0, 65, 103, 147, 0, 136, 115, 0, 0, 48, + 0, 90, 24, 0, 17, 0, 27, 70, 23, 26, 5, 60, 142, + 110, 122, 0, 73, 91, 71, 145, 61, 120, 74, 0, 49, 0, + 11, 41, 0, 113, 0, 0, 0, 109, 10, 111, 116, 125, 14, + 50, 124, 0, 100, 0, 18, 121, 144, 56, 130, 139, 88, 83, + 37, 30, 126, 0, 0, 108, 51, 131, 128, 0, 34, 0, 0, + 132, 0, 98, 38, 39, 0, 20, 45, 117, 93, }; /* aKWNext[] forms the hash collision chain. If aKWHash[i]==0 ** then the i-th keyword has no more hash collisions. Otherwise, ** the next keyword with the same hash is aKWHash[i]-1. */ -static const unsigned char aKWNext[145] = { - 0, 0, 0, 0, 4, 0, 43, 0, 0, 103, 111, 0, 0, - 0, 2, 0, 0, 141, 0, 0, 0, 13, 0, 0, 0, 0, - 139, 0, 0, 118, 52, 0, 0, 135, 12, 0, 0, 62, 0, - 136, 0, 131, 0, 0, 36, 0, 0, 28, 77, 0, 0, 0, +static const unsigned char aKWNext[147] = { + 0, 0, 0, 0, 4, 0, 43, 0, 0, 106, 114, 0, 0, + 0, 2, 0, 0, 143, 0, 0, 0, 13, 0, 0, 0, 0, + 141, 0, 0, 119, 52, 0, 0, 137, 12, 0, 0, 62, 0, + 138, 0, 133, 0, 0, 36, 0, 0, 28, 77, 0, 0, 0, 0, 59, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 69, 0, 0, 0, 0, 0, 144, 3, 0, 58, 0, 1, - 75, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 64, 66, - 63, 0, 0, 0, 0, 46, 0, 16, 0, 115, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 81, 97, 0, 8, 0, 109, - 21, 7, 67, 0, 79, 93, 117, 0, 0, 68, 0, 0, 96, - 0, 55, 0, 76, 0, 92, 32, 33, 57, 25, 0, 98, 0, - 0, 87, + 0, 69, 0, 0, 0, 0, 0, 146, 3, 0, 58, 0, 1, + 75, 0, 0, 0, 31, 0, 0, 0, 0, 0, 127, 0, 104, + 0, 64, 66, 63, 0, 0, 0, 0, 0, 46, 0, 16, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 101, 0, + 112, 21, 7, 67, 0, 79, 96, 118, 0, 0, 68, 0, 0, + 99, 44, 0, 55, 0, 76, 0, 95, 32, 33, 57, 25, 0, + 102, 0, 0, 87, }; /* aKWLen[i] is the length (in bytes) of the i-th keyword */ -static const unsigned char aKWLen[145] = { +static const unsigned char aKWLen[147] = { 7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6, 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 7, 6, 9, 4, 2, 6, 5, 9, 9, 4, 7, 3, 2, 4, 4, 6, 11, 6, 2, 7, 5, 5, 9, 6, 10, 4, 6, 2, 3, 7, 5, 9, 6, 6, 4, 5, 5, 10, 6, 5, 7, 4, 5, 7, 6, 7, 7, 6, 5, 7, 3, 7, 4, - 7, 6, 12, 9, 4, 6, 5, 4, 7, 6, 5, 6, 6, - 7, 6, 4, 5, 9, 5, 6, 3, 8, 8, 2, 13, 2, - 2, 4, 6, 6, 8, 5, 17, 12, 7, 9, 4, 9, 4, - 4, 6, 7, 5, 9, 4, 4, 5, 2, 5, 8, 6, 4, - 5, 8, 4, 3, 9, 5, 5, 6, 4, 6, 2, 2, 9, - 3, 7, + 7, 6, 12, 9, 4, 6, 5, 4, 7, 6, 12, 8, 8, + 2, 6, 6, 7, 6, 4, 5, 9, 5, 5, 6, 3, 4, + 9, 13, 2, 2, 4, 6, 6, 8, 5, 17, 12, 7, 9, + 4, 4, 6, 7, 5, 9, 4, 4, 5, 2, 5, 8, 6, + 4, 9, 5, 8, 4, 3, 9, 5, 5, 6, 4, 6, 2, + 2, 9, 3, 7, }; /* aKWOffset[i] is the index into zKWText[] of the start of ** the text for the i-th keyword. */ -static const unsigned short int aKWOffset[145] = { +static const unsigned short int aKWOffset[147] = { 0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33, 36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81, 86, 90, 90, 94, 99, 101, 105, 111, 119, 123, 123, 123, 126, 129, 132, 137, 142, 146, 147, 152, 156, 160, 168, 174, 181, 184, 184, 187, 189, 195, 198, 206, 211, 216, 219, 222, 226, 236, 239, 244, 244, 248, 252, 259, 265, 271, 277, 277, 283, 284, 288, 295, - 299, 306, 312, 324, 333, 335, 341, 346, 348, 355, 360, 365, 371, - 377, 382, 388, 392, 395, 404, 408, 414, 416, 423, 424, 431, 433, - 435, 444, 448, 454, 460, 468, 473, 473, 473, 489, 498, 501, 510, - 513, 517, 522, 529, 534, 543, 547, 550, 555, 557, 561, 569, 575, - 578, 583, 591, 591, 595, 604, 609, 614, 620, 623, 626, 629, 631, - 636, 640, + 299, 306, 312, 324, 333, 335, 341, 346, 348, 355, 359, 370, 377, + 378, 385, 391, 397, 402, 408, 412, 415, 424, 429, 433, 439, 441, + 444, 453, 455, 457, 466, 470, 476, 482, 490, 495, 495, 495, 511, + 520, 523, 527, 532, 539, 544, 553, 557, 560, 565, 567, 571, 579, + 585, 588, 597, 602, 610, 610, 614, 623, 628, 633, 639, 642, 645, + 648, 650, 655, 659, }; /* aKWCode[i] is the parser symbol code for the i-th keyword */ -static const unsigned char aKWCode[145] = { +static const unsigned char aKWCode[147] = { TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE, TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN, TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD, @@ -165257,18 +174637,19 @@ static const unsigned char aKWCode[145] = { TK_BETWEEN, TK_NOTHING, TK_GROUPS, TK_GROUP, TK_CASCADE, TK_ASC, TK_DEFAULT, TK_CASE, TK_COLLATE, TK_CREATE, TK_CTIME_KW, TK_IMMEDIATE, TK_JOIN, TK_INSERT, TK_MATCH, - TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_ABORT, TK_UPDATE, - TK_VALUES, TK_VIRTUAL, TK_ALWAYS, TK_WHEN, TK_WHERE, - TK_RECURSIVE, TK_AFTER, TK_RENAME, TK_AND, TK_DEFERRED, - TK_DISTINCT, TK_IS, TK_AUTOINCR, TK_TO, TK_IN, - TK_CAST, TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, - TK_CTIME_KW, TK_CTIME_KW, TK_CURRENT, TK_PARTITION, TK_DROP, - TK_PRECEDING, TK_FAIL, TK_LAST, TK_FILTER, TK_REPLACE, - TK_FIRST, TK_FOLLOWING, TK_FROM, TK_JOIN_KW, TK_LIMIT, - TK_IF, TK_ORDER, TK_RESTRICT, TK_OTHERS, TK_OVER, - TK_JOIN_KW, TK_ROLLBACK, TK_ROWS, TK_ROW, TK_UNBOUNDED, - TK_UNION, TK_USING, TK_VACUUM, TK_VIEW, TK_WINDOW, - TK_DO, TK_BY, TK_INITIALLY, TK_ALL, TK_PRIMARY, + TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_MATERIALIZED, TK_DEFERRED, + TK_DISTINCT, TK_IS, TK_UPDATE, TK_VALUES, TK_VIRTUAL, + TK_ALWAYS, TK_WHEN, TK_WHERE, TK_RECURSIVE, TK_ABORT, + TK_AFTER, TK_RENAME, TK_AND, TK_DROP, TK_PARTITION, + TK_AUTOINCR, TK_TO, TK_IN, TK_CAST, TK_COLUMNKW, + TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW, + TK_CURRENT, TK_PRECEDING, TK_FAIL, TK_LAST, TK_FILTER, + TK_REPLACE, TK_FIRST, TK_FOLLOWING, TK_FROM, TK_JOIN_KW, + TK_LIMIT, TK_IF, TK_ORDER, TK_RESTRICT, TK_OTHERS, + TK_OVER, TK_RETURNING, TK_JOIN_KW, TK_ROLLBACK, TK_ROWS, + TK_ROW, TK_UNBOUNDED, TK_UNION, TK_USING, TK_VACUUM, + TK_VIEW, TK_WINDOW, TK_DO, TK_BY, TK_INITIALLY, + TK_ALL, TK_PRIMARY, }; /* Hash table decoded: ** 0: INSERT @@ -165292,7 +174673,7 @@ static const unsigned char aKWCode[145] = { ** 18: TRANSACTION RIGHT ** 19: WHEN ** 20: SET HAVING -** 21: IF +** 21: MATERIALIZED IF ** 22: ROWS ** 23: SELECT ** 24: @@ -165388,7 +174769,7 @@ static const unsigned char aKWCode[145] = { ** 114: INTERSECT UNBOUNDED ** 115: ** 116: -** 117: ON +** 117: RETURNING ON ** 118: ** 119: WHERE ** 120: NO INNER @@ -165406,7 +174787,7 @@ static int keywordCode(const char *z, int n, int *pType){ int i, j; const char *zKW; if( n>=2 ){ - i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n) % 127; + i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127; for(i=((int)aKWHash[i])-1; i>=0; i=((int)aKWNext[i])-1){ if( aKWLen[i]!=n ) continue; zKW = &zKWText[aKWOffset[i]]; @@ -165511,63 +174892,65 @@ static int keywordCode(const char *z, int n, int *pType){ testcase( i==85 ); /* PLAN */ testcase( i==86 ); /* ANALYZE */ testcase( i==87 ); /* PRAGMA */ - testcase( i==88 ); /* ABORT */ - testcase( i==89 ); /* UPDATE */ - testcase( i==90 ); /* VALUES */ - testcase( i==91 ); /* VIRTUAL */ - testcase( i==92 ); /* ALWAYS */ - testcase( i==93 ); /* WHEN */ - testcase( i==94 ); /* WHERE */ - testcase( i==95 ); /* RECURSIVE */ - testcase( i==96 ); /* AFTER */ - testcase( i==97 ); /* RENAME */ - testcase( i==98 ); /* AND */ - testcase( i==99 ); /* DEFERRED */ - testcase( i==100 ); /* DISTINCT */ - testcase( i==101 ); /* IS */ - testcase( i==102 ); /* AUTOINCREMENT */ - testcase( i==103 ); /* TO */ - testcase( i==104 ); /* IN */ - testcase( i==105 ); /* CAST */ - testcase( i==106 ); /* COLUMN */ - testcase( i==107 ); /* COMMIT */ - testcase( i==108 ); /* CONFLICT */ - testcase( i==109 ); /* CROSS */ - testcase( i==110 ); /* CURRENT_TIMESTAMP */ - testcase( i==111 ); /* CURRENT_TIME */ - testcase( i==112 ); /* CURRENT */ - testcase( i==113 ); /* PARTITION */ - testcase( i==114 ); /* DROP */ - testcase( i==115 ); /* PRECEDING */ - testcase( i==116 ); /* FAIL */ - testcase( i==117 ); /* LAST */ - testcase( i==118 ); /* FILTER */ - testcase( i==119 ); /* REPLACE */ - testcase( i==120 ); /* FIRST */ - testcase( i==121 ); /* FOLLOWING */ - testcase( i==122 ); /* FROM */ - testcase( i==123 ); /* FULL */ - testcase( i==124 ); /* LIMIT */ - testcase( i==125 ); /* IF */ - testcase( i==126 ); /* ORDER */ - testcase( i==127 ); /* RESTRICT */ - testcase( i==128 ); /* OTHERS */ - testcase( i==129 ); /* OVER */ - testcase( i==130 ); /* RIGHT */ - testcase( i==131 ); /* ROLLBACK */ - testcase( i==132 ); /* ROWS */ - testcase( i==133 ); /* ROW */ - testcase( i==134 ); /* UNBOUNDED */ - testcase( i==135 ); /* UNION */ - testcase( i==136 ); /* USING */ - testcase( i==137 ); /* VACUUM */ - testcase( i==138 ); /* VIEW */ - testcase( i==139 ); /* WINDOW */ - testcase( i==140 ); /* DO */ - testcase( i==141 ); /* BY */ - testcase( i==142 ); /* INITIALLY */ - testcase( i==143 ); /* ALL */ - testcase( i==144 ); /* PRIMARY */ + testcase( i==88 ); /* MATERIALIZED */ + testcase( i==89 ); /* DEFERRED */ + testcase( i==90 ); /* DISTINCT */ + testcase( i==91 ); /* IS */ + testcase( i==92 ); /* UPDATE */ + testcase( i==93 ); /* VALUES */ + testcase( i==94 ); /* VIRTUAL */ + testcase( i==95 ); /* ALWAYS */ + testcase( i==96 ); /* WHEN */ + testcase( i==97 ); /* WHERE */ + testcase( i==98 ); /* RECURSIVE */ + testcase( i==99 ); /* ABORT */ + testcase( i==100 ); /* AFTER */ + testcase( i==101 ); /* RENAME */ + testcase( i==102 ); /* AND */ + testcase( i==103 ); /* DROP */ + testcase( i==104 ); /* PARTITION */ + testcase( i==105 ); /* AUTOINCREMENT */ + testcase( i==106 ); /* TO */ + testcase( i==107 ); /* IN */ + testcase( i==108 ); /* CAST */ + testcase( i==109 ); /* COLUMN */ + testcase( i==110 ); /* COMMIT */ + testcase( i==111 ); /* CONFLICT */ + testcase( i==112 ); /* CROSS */ + testcase( i==113 ); /* CURRENT_TIMESTAMP */ + testcase( i==114 ); /* CURRENT_TIME */ + testcase( i==115 ); /* CURRENT */ + testcase( i==116 ); /* PRECEDING */ + testcase( i==117 ); /* FAIL */ + testcase( i==118 ); /* LAST */ + testcase( i==119 ); /* FILTER */ + testcase( i==120 ); /* REPLACE */ + testcase( i==121 ); /* FIRST */ + testcase( i==122 ); /* FOLLOWING */ + testcase( i==123 ); /* FROM */ + testcase( i==124 ); /* FULL */ + testcase( i==125 ); /* LIMIT */ + testcase( i==126 ); /* IF */ + testcase( i==127 ); /* ORDER */ + testcase( i==128 ); /* RESTRICT */ + testcase( i==129 ); /* OTHERS */ + testcase( i==130 ); /* OVER */ + testcase( i==131 ); /* RETURNING */ + testcase( i==132 ); /* RIGHT */ + testcase( i==133 ); /* ROLLBACK */ + testcase( i==134 ); /* ROWS */ + testcase( i==135 ); /* ROW */ + testcase( i==136 ); /* UNBOUNDED */ + testcase( i==137 ); /* UNION */ + testcase( i==138 ); /* USING */ + testcase( i==139 ); /* VACUUM */ + testcase( i==140 ); /* VIEW */ + testcase( i==141 ); /* WINDOW */ + testcase( i==142 ); /* DO */ + testcase( i==143 ); /* BY */ + testcase( i==144 ); /* INITIALLY */ + testcase( i==145 ); /* ALL */ + testcase( i==146 ); /* PRIMARY */ *pType = aKWCode[i]; break; } @@ -165579,7 +174962,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3KeywordCode(const unsigned char *z, int n){ keywordCode((char*)z, n, &id); return id; } -#define SQLITE_N_KEYWORD 145 +#define SQLITE_N_KEYWORD 147 SQLITE_API int sqlcipher_sqlite3_keyword_name(int i,const char **pzName,int *pnName){ if( i<0 || i>=SQLITE_N_KEYWORD ) return SQLITE_ERROR; *pzName = zKWText + aKWOffset[i]; @@ -165737,6 +175120,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3GetToken(const unsigned char *z, int *tokenT for(i=2; (c=z[i])!=0 && c!='\n'; i++){} *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; + }else if( z[1]=='>' ){ + *tokenType = TK_PTR; + return 2 + (z[2]=='>'); } *tokenType = TK_MINUS; return 1; @@ -165948,7 +175334,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3GetToken(const unsigned char *z, int *tokenT if( n==0 ) *tokenType = TK_ILLEGAL; return i; } - case CC_KYWD: { + case CC_KYWD0: { for(i=1; aiClass[z[i]]<=CC_KYWD; i++){} if( IdChar(z[i]) ){ /* This token started out using characters that can appear in keywords, @@ -165978,10 +175364,19 @@ SQLITE_PRIVATE int sqlcipher_sqlite3GetToken(const unsigned char *z, int *tokenT ** SQL keywords start with the letter 'x'. Fall through */ /* no break */ deliberate_fall_through } + case CC_KYWD: case CC_ID: { i = 1; break; } + case CC_BOM: { + if( z[1]==0xbb && z[2]==0xbf ){ + *tokenType = TK_SPACE; + return 3; + } + i = 1; + break; + } case CC_NUL: { *tokenType = TK_ILLEGAL; return 0; @@ -165997,13 +175392,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3GetToken(const unsigned char *z, int *tokenT } /* -** Run the parser on the given SQL string. The parser structure is -** passed in. An SQLITE_ status code is returned. If an error occurs -** then an and attempt is made to write an error message into -** memory obtained from sqlcipher_sqlite3_malloc() and to make *pzErrMsg point to that -** error message. +** Run the parser on the given SQL string. */ -SQLITE_PRIVATE int sqlcipher_sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ +SQLITE_PRIVATE int sqlcipher_sqlite3RunParser(Parse *pParse, const char *zSql){ int nErr = 0; /* Number of errors encountered */ void *pEngine; /* The LEMON-generated LALR(1) parser */ int n = 0; /* Length of the next token token */ @@ -166011,6 +175402,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3RunParser(Parse *pParse, const char *zSql, c int lastTokenParsed = -1; /* type of the previous token */ sqlcipher_sqlite3 *db = pParse->db; /* The database connection */ int mxSqlLen; /* Max length of an SQL string */ + Parse *pParentParse = 0; /* Outer parse context, if any */ #ifdef sqlcipher_sqlite3Parser_ENGINEALWAYSONSTACK yyParser sEngine; /* Space to hold the Lemon-generated Parser object */ #endif @@ -166023,7 +175415,6 @@ SQLITE_PRIVATE int sqlcipher_sqlite3RunParser(Parse *pParse, const char *zSql, c } pParse->rc = SQLITE_OK; pParse->zTail = zSql; - assert( pzErrMsg!=0 ); #ifdef SQLITE_DEBUG if( db->flags & SQLITE_ParserTrace ){ printf("parser: [[[%s]]]\n", zSql); @@ -166046,13 +175437,14 @@ SQLITE_PRIVATE int sqlcipher_sqlite3RunParser(Parse *pParse, const char *zSql, c assert( pParse->pNewTrigger==0 ); assert( pParse->nVar==0 ); assert( pParse->pVList==0 ); - pParse->pParentParse = db->pParse; + pParentParse = db->pParse; db->pParse = pParse; while( 1 ){ n = sqlcipher_sqlite3GetToken((u8*)zSql, &tokenType); mxSqlLen -= n; if( mxSqlLen<0 ){ pParse->rc = SQLITE_TOOBIG; + pParse->nErr++; break; } #ifndef SQLITE_OMIT_WINDOWFUNC @@ -166066,6 +175458,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3RunParser(Parse *pParse, const char *zSql, c #endif /* SQLITE_OMIT_WINDOWFUNC */ if( AtomicLoad(&db->u1.isInterrupted) ){ pParse->rc = SQLITE_INTERRUPT; + pParse->nErr++; break; } if( tokenType==TK_SPACE ){ @@ -166095,7 +175488,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3RunParser(Parse *pParse, const char *zSql, c tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); #endif /* SQLITE_OMIT_WINDOWFUNC */ }else{ - sqlcipher_sqlite3ErrorMsg(pParse, "unrecognized token: \"%.*s\"", n, zSql); + Token x; + x.z = zSql; + x.n = n; + sqlcipher_sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"", &x); break; } } @@ -166123,58 +175519,30 @@ SQLITE_PRIVATE int sqlcipher_sqlite3RunParser(Parse *pParse, const char *zSql, c if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM_BKPT; } - if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){ - pParse->zErrMsg = sqlcipher_sqlite3MPrintf(db, "%s", sqlcipher_sqlite3ErrStr(pParse->rc)); - } - assert( pzErrMsg!=0 ); - if( pParse->zErrMsg ){ - *pzErrMsg = pParse->zErrMsg; - sqlcipher_sqlite3_log(pParse->rc, "%s in \"%s\"", - *pzErrMsg, pParse->zTail); - pParse->zErrMsg = 0; + if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){ + if( pParse->zErrMsg==0 ){ + pParse->zErrMsg = sqlcipher_sqlite3MPrintf(db, "%s", sqlcipher_sqlite3ErrStr(pParse->rc)); + } + sqlcipher_sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail); nErr++; } pParse->zTail = zSql; - if( pParse->pVdbe && pParse->nErr>0 && pParse->nested==0 ){ - sqlcipher_sqlite3VdbeDelete(pParse->pVdbe); - pParse->pVdbe = 0; - } -#ifndef SQLITE_OMIT_SHARED_CACHE - if( pParse->nested==0 ){ - sqlcipher_sqlite3DbFree(db, pParse->aTableLock); - pParse->aTableLock = 0; - pParse->nTableLock = 0; - } -#endif #ifndef SQLITE_OMIT_VIRTUALTABLE sqlcipher_sqlite3_free(pParse->apVtabLock); #endif - if( !IN_SPECIAL_PARSE ){ + if( pParse->pNewTable && !IN_SPECIAL_PARSE ){ /* If the pParse->declareVtab flag is set, do not delete any table ** structure built up in pParse->pNewTable. The calling code (see vtab.c) ** will take responsibility for freeing the Table structure. */ sqlcipher_sqlite3DeleteTable(db, pParse->pNewTable); } - if( !IN_RENAME_OBJECT ){ + if( pParse->pNewTrigger && !IN_RENAME_OBJECT ){ sqlcipher_sqlite3DeleteTrigger(db, pParse->pNewTrigger); } - - if( pParse->pWithToFree ) sqlcipher_sqlite3WithDelete(db, pParse->pWithToFree); - sqlcipher_sqlite3DbFree(db, pParse->pVList); - while( pParse->pAinc ){ - AutoincInfo *p = pParse->pAinc; - pParse->pAinc = p->pNext; - sqlcipher_sqlite3DbFreeNN(db, p); - } - while( pParse->pZombieTab ){ - Table *p = pParse->pZombieTab; - pParse->pZombieTab = p->pNextZombie; - sqlcipher_sqlite3DeleteTable(db, p); - } - db->pParse = pParse->pParentParse; - pParse->pParentParse = 0; + if( pParse->pVList ) sqlcipher_sqlite3DbFreeNN(db, pParse->pVList); + db->pParse = pParentParse; assert( nErr==0 || pParse->rc!=SQLITE_OK ); return nErr; } @@ -166755,9 +176123,6 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts2Init(sqlcipher_sqlite3*); #ifdef SQLITE_ENABLE_FTS5 SQLITE_PRIVATE int sqlcipher_sqlite3Fts5Init(sqlcipher_sqlite3*); #endif -#ifdef SQLITE_ENABLE_JSON1 -SQLITE_PRIVATE int sqlcipher_sqlite3Json1Init(sqlcipher_sqlite3*); -#endif #ifdef SQLITE_ENABLE_STMTVTAB SQLITE_PRIVATE int sqlcipher_sqlite3StmtVtabInit(sqlcipher_sqlite3*); #endif @@ -166792,8 +176157,8 @@ static int (*const sqlcipher_sqlite3BuiltinExtensions[])(sqlcipher_sqlite3*) = { sqlcipher_sqlite3DbstatRegister, #endif sqlcipher_sqlite3TestExtInit, -#ifdef SQLITE_ENABLE_JSON1 - sqlcipher_sqlite3Json1Init, +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) + sqlcipher_sqlite3JsonTableFunctions, #endif #ifdef SQLITE_ENABLE_STMTVTAB sqlcipher_sqlite3StmtVtabInit, @@ -167010,7 +176375,7 @@ SQLITE_API int sqlcipher_sqlite3_initialize(void){ sqlcipher_sqlite3GlobalConfig.isPCacheInit = 1; rc = sqlcipher_sqlite3OsInit(); } -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE if( rc==SQLITE_OK ){ rc = sqlcipher_sqlite3MemdbInit(); } @@ -167425,12 +176790,12 @@ SQLITE_API int sqlcipher_sqlite3_config(int op, ...){ } #endif /* SQLITE_ENABLE_SORTER_REFERENCES */ -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE case SQLITE_CONFIG_MEMDB_MAXSIZE: { sqlcipher_sqlite3GlobalConfig.mxMemdbSize = va_arg(ap, sqlcipher_sqlite3_int64); break; } -#endif /* SQLITE_ENABLE_DESERIALIZE */ +#endif /* SQLITE_OMIT_DESERIALIZE */ default: { rc = SQLITE_ERROR; @@ -167791,7 +177156,7 @@ SQLITE_API void sqlcipher_sqlite3_set_last_insert_rowid(sqlcipher_sqlite3 *db, s /* ** Return the number of changes in the most recent call to sqlcipher_sqlite3_exec(). */ -SQLITE_API int sqlcipher_sqlite3_changes(sqlcipher_sqlite3 *db){ +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_changes64(sqlcipher_sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlcipher_sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -167800,11 +177165,14 @@ SQLITE_API int sqlcipher_sqlite3_changes(sqlcipher_sqlite3 *db){ #endif return db->nChange; } +SQLITE_API int sqlcipher_sqlite3_changes(sqlcipher_sqlite3 *db){ + return (int)sqlcipher_sqlite3_changes64(db); +} /* ** Return the number of changes since the database handle was opened. */ -SQLITE_API int sqlcipher_sqlite3_total_changes(sqlcipher_sqlite3 *db){ +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_total_changes64(sqlcipher_sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlcipher_sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -167813,6 +177181,9 @@ SQLITE_API int sqlcipher_sqlite3_total_changes(sqlcipher_sqlite3 *db){ #endif return db->nTotalChange; } +SQLITE_API int sqlcipher_sqlite3_total_changes(sqlcipher_sqlite3 *db){ + return (int)sqlcipher_sqlite3_total_changes64(db); +} /* ** Close all open savepoints. This function only manipulates fields of the @@ -167837,7 +177208,9 @@ SQLITE_PRIVATE void sqlcipher_sqlite3CloseSavepoints(sqlcipher_sqlite3 *db){ ** with SQLITE_ANY as the encoding. */ static void functionDestroy(sqlcipher_sqlite3 *db, FuncDef *p){ - FuncDestructor *pDestructor = p->u.pDestructor; + FuncDestructor *pDestructor; + assert( (p->funcFlags & SQLITE_FUNC_BUILTIN)==0 ); + pDestructor = p->u.pDestructor; if( pDestructor ){ pDestructor->nRef--; if( pDestructor->nRef==0 ){ @@ -167941,7 +177314,7 @@ static int sqlcipher_sqlite3Close(sqlcipher_sqlite3 *db, int forceZombie){ /* Convert the connection into a zombie and then close it. */ - db->magic = SQLITE_MAGIC_ZOMBIE; + db->eOpenState = SQLITE_STATE_ZOMBIE; sqlcipher_sqlite3LeaveMutexAndCloseZombie(db); return SQLITE_OK; } @@ -167979,7 +177352,7 @@ SQLITE_API int sqlcipher_sqlite3_txn_state(sqlcipher_sqlite3 *db, const char *zS /* ** Two variations on the public interface for closing a database ** connection. The sqlcipher_sqlite3_close() version returns SQLITE_BUSY and -** leaves the connection option if there are unfinalized prepared +** leaves the connection open if there are unfinalized prepared ** statements or unfinished sqlcipher_sqlite3_backups. The sqlcipher_sqlite3_close_v2() ** version forces the connection to become a zombie if there are ** unclosed resources, and arranges for deallocation when the last @@ -168005,7 +177378,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3LeaveMutexAndCloseZombie(sqlcipher_sqlite3 ** or if the connection has not yet been closed by sqlcipher_sqlite3_close_v2(), ** then just leave the mutex and return. */ - if( db->magic!=SQLITE_MAGIC_ZOMBIE || connectionIsBusy(db) ){ + if( db->eOpenState!=SQLITE_STATE_ZOMBIE || connectionIsBusy(db) ){ sqlcipher_sqlite3_mutex_leave(db->mutex); return; } @@ -168091,7 +177464,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3LeaveMutexAndCloseZombie(sqlcipher_sqlite3 sqlcipher_sqlite3_free(db->auth.zAuthPW); #endif - db->magic = SQLITE_MAGIC_ERROR; + db->eOpenState = SQLITE_STATE_ERROR; /* The temp-database schema is allocated differently from the other schema ** objects (using sqliteMalloc() directly, instead of sqlcipher_sqlite3BtreeSchema()). @@ -168100,8 +177473,11 @@ SQLITE_PRIVATE void sqlcipher_sqlite3LeaveMutexAndCloseZombie(sqlcipher_sqlite3 ** structure? */ sqlcipher_sqlite3DbFree(db, db->aDb[1].pSchema); + if( db->xAutovacDestr ){ + db->xAutovacDestr(db->pAutovacPagesArg); + } sqlcipher_sqlite3_mutex_leave(db->mutex); - db->magic = SQLITE_MAGIC_CLOSED; + db->eOpenState = SQLITE_STATE_CLOSED; sqlcipher_sqlite3_mutex_free(db->mutex); assert( sqlcipher_sqlite3LookasideUsed(db,0)==0 ); if( db->lookaside.bMalloced ){ @@ -168154,7 +177530,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3RollbackAll(sqlcipher_sqlite3 *db, int trip /* Any deferred constraint violations have now been resolved. */ db->nDeferredCons = 0; db->nDeferredImmCons = 0; - db->flags &= ~(u64)SQLITE_DeferFKs; + db->flags &= ~(u64)(SQLITE_DeferFKs|SQLITE_CorruptRdOnly); /* If one has been configured, invoke the rollback-hook callback */ if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){ @@ -168489,7 +177865,7 @@ SQLITE_API int sqlcipher_sqlite3_busy_timeout(sqlcipher_sqlite3 *db, int ms){ */ SQLITE_API void sqlcipher_sqlite3_interrupt(sqlcipher_sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlcipher_sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){ + if( !sqlcipher_sqlite3SafetyCheckOk(db) && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE) ){ (void)SQLITE_MISUSE_BKPT; return; } @@ -168518,7 +177894,6 @@ SQLITE_PRIVATE int sqlcipher_sqlite3CreateFunc( FuncDestructor *pDestructor ){ FuncDef *p; - int nName; int extraFlags; assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); @@ -168528,7 +177903,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3CreateFunc( || ((xFinal==0)!=(xStep==0)) /* Both or neither of xFinal and xStep */ || ((xValue==0)!=(xInverse==0)) /* Both or neither of xValue, xInverse */ || (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) - || (255<(nName = sqlcipher_sqlite3Strlen30( zFunctionName))) + || (255nRef==0 ){ - assert( rc!=SQLITE_OK ); + assert( rc!=SQLITE_OK || (xStep==0 && xFinal==0) ); xDestroy(p); sqlcipher_sqlite3_free(pArg); } @@ -168987,6 +178377,34 @@ SQLITE_API void *sqlcipher_sqlite3_preupdate_hook( } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +/* +** Register a function to be invoked prior to each autovacuum that +** determines the number of pages to vacuum. +*/ +SQLITE_API int sqlcipher_sqlite3_autovacuum_pages( + sqlcipher_sqlite3 *db, /* Attach the hook to this database */ + unsigned int (*xCallback)(void*,const char*,u32,u32,u32), + void *pArg, /* Argument to the function */ + void (*xDestructor)(void*) /* Destructor for pArg */ +){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlcipher_sqlite3SafetyCheckOk(db) ){ + if( xDestructor ) xDestructor(pArg); + return SQLITE_MISUSE_BKPT; + } +#endif + sqlcipher_sqlite3_mutex_enter(db->mutex); + if( db->xAutovacDestr ){ + db->xAutovacDestr(db->pAutovacPagesArg); + } + db->xAutovacPages = xCallback; + db->pAutovacPagesArg = pArg; + db->xAutovacDestr = xDestructor; + sqlcipher_sqlite3_mutex_leave(db->mutex); + return SQLITE_OK; +} + + #ifndef SQLITE_OMIT_WAL /* ** The sqlcipher_sqlite3_wal_hook() callback registered by sqlcipher_sqlite3_wal_autocheckpoint(). @@ -169079,7 +178497,7 @@ SQLITE_API int sqlcipher_sqlite3_wal_checkpoint_v2( return SQLITE_OK; #else int rc; /* Return code */ - int iDb = SQLITE_MAX_ATTACHED; /* sqlcipher_sqlite3.aDb[] index of db to checkpoint */ + int iDb; /* Schema to checkpoint */ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlcipher_sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; @@ -169102,6 +178520,8 @@ SQLITE_API int sqlcipher_sqlite3_wal_checkpoint_v2( sqlcipher_sqlite3_mutex_enter(db->mutex); if( zDb && zDb[0] ){ iDb = sqlcipher_sqlite3FindDbName(db, zDb); + }else{ + iDb = SQLITE_MAX_DB; /* This means process all schemas */ } if( iDb<0 ){ rc = SQLITE_ERROR; @@ -169150,7 +178570,7 @@ SQLITE_API int sqlcipher_sqlite3_wal_checkpoint(sqlcipher_sqlite3 *db, const cha ** associated with the specific b-tree being checkpointed is taken by ** this function while the checkpoint is running. ** -** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are +** If iDb is passed SQLITE_MAX_DB then all attached databases are ** checkpointed. If an error is encountered it is returned immediately - ** no attempt is made to checkpoint any remaining databases. ** @@ -169165,9 +178585,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Checkpoint(sqlcipher_sqlite3 *db, int iDb, i assert( sqlcipher_sqlite3_mutex_held(db->mutex) ); assert( !pnLog || *pnLog==-1 ); assert( !pnCkpt || *pnCkpt==-1 ); + testcase( iDb==SQLITE_MAX_ATTACHED ); /* See forum post a006d86f72 */ + testcase( iDb==SQLITE_MAX_DB ); for(i=0; inDb && rc==SQLITE_OK; i++){ - if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ + if( i==iDb || iDb==SQLITE_MAX_DB ){ rc = sqlcipher_sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt); pnLog = 0; pnCkpt = 0; @@ -169245,6 +178667,19 @@ SQLITE_API const char *sqlcipher_sqlite3_errmsg(sqlcipher_sqlite3 *db){ return z; } +/* +** Return the byte offset of the most recent error +*/ +SQLITE_API int sqlcipher_sqlite3_error_offset(sqlcipher_sqlite3 *db){ + int iOffset = -1; + if( db && sqlcipher_sqlite3SafetyCheckSickOrOk(db) && db->errCode ){ + sqlcipher_sqlite3_mutex_enter(db->mutex); + iOffset = db->errByteOffset; + sqlcipher_sqlite3_mutex_leave(db->mutex); + } + return iOffset; +} + #ifndef SQLITE_OMIT_UTF16 /* ** Return UTF-16 encoded English language explanation of the most recent @@ -169505,6 +178940,8 @@ SQLITE_API int sqlcipher_sqlite3_limit(sqlcipher_sqlite3 *db, int limitId, int n if( newLimit>=0 ){ /* IMP: R-52476-28732 */ if( newLimit>aHardLimit[limitId] ){ newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ + }else if( newLimit<1 && limitId==SQLITE_LIMIT_LENGTH ){ + newLimit = 1; } db->aLimit[limitId] = newLimit; } @@ -169776,7 +179213,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3ParseUri( */ static const char *uriParameter(const char *zFilename, const char *zParam){ zFilename += sqlcipher_sqlite3Strlen30(zFilename) + 1; - while( zFilename[0] ){ + while( ALWAYS(zFilename!=0) && zFilename[0] ){ int x = strcmp(zFilename, zParam); zFilename += sqlcipher_sqlite3Strlen30(zFilename) + 1; if( x==0 ) return zFilename; @@ -169873,8 +179310,8 @@ static int openDatabase( ** dealt with in the previous code block. Besides these, the only ** valid input flags for sqlcipher_sqlite3_open_v2() are SQLITE_OPEN_READONLY, ** SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, SQLITE_OPEN_SHAREDCACHE, - ** SQLITE_OPEN_PRIVATECACHE, and some reserved bits. Silently mask - ** off all other flags. + ** SQLITE_OPEN_PRIVATECACHE, SQLITE_OPEN_EXRESCODE, and some reserved + ** bits. Silently mask off all other flags. */ flags &= ~( SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_EXCLUSIVE | @@ -169909,9 +179346,9 @@ static int openDatabase( } } sqlcipher_sqlite3_mutex_enter(db->mutex); - db->errMask = 0xff; + db->errMask = (flags & SQLITE_OPEN_EXRESCODE)!=0 ? 0xffffffff : 0xff; db->nDb = 2; - db->magic = SQLITE_MAGIC_BUSY; + db->eOpenState = SQLITE_STATE_BUSY; db->aDb = db->aDbStatic; db->lookaside.bDisable = 1; db->lookaside.sz = 0; @@ -169923,7 +179360,15 @@ static int openDatabase( db->nextAutovac = -1; db->szMmap = sqlcipher_sqlite3GlobalConfig.szMmap; db->nextPagesize = 0; + db->init.azInit = sqlcipher_sqlite3StdType; /* Any array of string ptrs will do */ +#ifdef SQLITE_ENABLE_SORTER_MMAP + /* Beginning with version 3.37.0, using the VFS xFetch() API to memory-map + ** the temporary files used to do external sorts (see code in vdbesort.c) + ** is disabled. It can still be used either by defining + ** SQLITE_ENABLE_SORTER_MMAP at compile time or by using the + ** SQLITE_TESTCTRL_SORTER_MMAP test-control at runtime. */ db->nMaxSorterMmap = 0x7FFFFFFF; +#endif db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_EnableView @@ -170071,7 +179516,7 @@ static int openDatabase( db->aDb[1].zDbSName = "temp"; db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF; - db->magic = SQLITE_MAGIC_OPEN; + db->eOpenState = SQLITE_STATE_OPEN; if( db->mallocFailed ){ goto opendb_out; } @@ -170140,12 +179585,12 @@ opendb_out: sqlcipher_sqlite3_mutex_leave(db->mutex); } rc = sqlcipher_sqlite3_errcode(db); - assert( db!=0 || rc==SQLITE_NOMEM ); - if( rc==SQLITE_NOMEM ){ + assert( db!=0 || (rc&0xff)==SQLITE_NOMEM ); + if( (rc&0xff)==SQLITE_NOMEM ){ sqlcipher_sqlite3_close(db); db = 0; }else if( rc!=SQLITE_OK ){ - db->magic = SQLITE_MAGIC_SICK; + db->eOpenState = SQLITE_STATE_SICK; } *ppDb = db; #ifdef SQLITE_ENABLE_SQLLOG @@ -170161,7 +179606,7 @@ opendb_out: #endif /* END SQLCIPHER */ sqlcipher_sqlite3_free_filename(zOpen); - return rc & 0xff; + return rc; } @@ -170461,7 +179906,7 @@ SQLITE_API int sqlcipher_sqlite3_table_column_metadata( /* Locate the table in question */ pTab = sqlcipher_sqlite3FindTable(db, zTableName, zDbName); - if( !pTab || pTab->pSelect ){ + if( !pTab || IsView(pTab) ){ pTab = 0; goto error_out; } @@ -170472,7 +179917,7 @@ SQLITE_API int sqlcipher_sqlite3_table_column_metadata( }else{ for(iCol=0; iColnCol; iCol++){ pCol = &pTab->aCol[iCol]; - if( 0==sqlcipher_sqlite3StrICmp(pCol->zName, zColumnName) ){ + if( 0==sqlcipher_sqlite3StrICmp(pCol->zCnName, zColumnName) ){ break; } } @@ -170499,7 +179944,7 @@ SQLITE_API int sqlcipher_sqlite3_table_column_metadata( */ if( pCol ){ zDataType = sqlcipher_sqlite3ColumnType(pCol,0); - zCollSeq = pCol->zColl; + zCollSeq = sqlcipher_sqlite3ColumnColl(pCol); notnull = pCol->notNull!=0; primarykey = (pCol->colFlags & COLFLAG_PRIMKEY)!=0; autoinc = pTab->iPKey==iCol && (pTab->tabFlags & TF_Autoincrement)!=0; @@ -170706,12 +180151,16 @@ SQLITE_API int sqlcipher_sqlite3_test_control(int op, ...){ ** sqlcipher_sqlite3_test_control(). */ case SQLITE_TESTCTRL_FAULT_INSTALL: { - /* MSVC is picky about pulling func ptrs from va lists. - ** http://support.microsoft.com/kb/47961 + /* A bug in MSVC prevents it from understanding pointers to functions + ** types in the second argument to va_arg(). Work around the problem + ** using a typedef. + ** http://support.microsoft.com/kb/47961 <-- dead hyperlink + ** Search at http://web.archive.org/ to find the 2015-03-16 archive + ** of the link above to see the original text. ** sqlcipher_sqlite3GlobalConfig.xTestCallback = va_arg(ap, int(*)(int)); */ - typedef int(*TESTCALLBACKFUNC_t)(int); - sqlcipher_sqlite3GlobalConfig.xTestCallback = va_arg(ap, TESTCALLBACKFUNC_t); + typedef int(*sqlcipher_sqlite3FaultFuncType)(int); + sqlcipher_sqlite3GlobalConfig.xTestCallback = va_arg(ap, sqlcipher_sqlite3FaultFuncType); rc = sqlcipher_sqlite3FaultSim(0); break; } @@ -170770,6 +180219,28 @@ SQLITE_API int sqlcipher_sqlite3_test_control(int op, ...){ volatile int x = 0; assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 ); rc = x; +#if defined(SQLITE_DEBUG) + /* Invoke these debugging routines so that the compiler does not + ** issue "defined but not used" warnings. */ + if( x==9999 ){ + sqlcipher_sqlite3ShowExpr(0); + sqlcipher_sqlite3ShowExpr(0); + sqlcipher_sqlite3ShowExprList(0); + sqlcipher_sqlite3ShowIdList(0); + sqlcipher_sqlite3ShowSrcList(0); + sqlcipher_sqlite3ShowWith(0); + sqlcipher_sqlite3ShowUpsert(0); + sqlcipher_sqlite3ShowTriggerStep(0); + sqlcipher_sqlite3ShowTriggerStepList(0); + sqlcipher_sqlite3ShowTrigger(0); + sqlcipher_sqlite3ShowTriggerList(0); +#ifndef SQLITE_OMIT_WINDOWFUNC + sqlcipher_sqlite3ShowWindow(0); + sqlcipher_sqlite3ShowWinFunc(0); +#endif + sqlcipher_sqlite3ShowSelect(0); + } +#endif break; } @@ -170834,17 +180305,31 @@ SQLITE_API int sqlcipher_sqlite3_test_control(int op, ...){ */ case SQLITE_TESTCTRL_OPTIMIZATIONS: { sqlcipher_sqlite3 *db = va_arg(ap, sqlcipher_sqlite3*); - db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff); + db->dbOptFlags = va_arg(ap, u32); break; } - /* sqlcipher_sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff); + /* sqlcipher_sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, onoff, xAlt); + ** + ** If parameter onoff is 1, subsequent calls to localtime() fail. + ** If 2, then invoke xAlt() instead of localtime(). If 0, normal + ** processing. ** - ** If parameter onoff is non-zero, subsequent calls to localtime() - ** and its variants fail. If onoff is zero, undo this setting. + ** xAlt arguments are void pointers, but they really want to be: + ** + ** int xAlt(const time_t*, struct tm*); + ** + ** xAlt should write results in to struct tm object of its 2nd argument + ** and return zero on success, or return non-zero on failure. */ case SQLITE_TESTCTRL_LOCALTIME_FAULT: { sqlcipher_sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int); + if( sqlcipher_sqlite3GlobalConfig.bLocaltimeFault==2 ){ + typedef int(*sqlcipher_sqlite3LocaltimeType)(const void*,void*); + sqlcipher_sqlite3GlobalConfig.xAltLocaltime = va_arg(ap, sqlcipher_sqlite3LocaltimeType); + }else{ + sqlcipher_sqlite3GlobalConfig.xAltLocaltime = 0; + } break; } @@ -170949,12 +180434,16 @@ SQLITE_API int sqlcipher_sqlite3_test_control(int op, ...){ */ case SQLITE_TESTCTRL_IMPOSTER: { sqlcipher_sqlite3 *db = va_arg(ap, sqlcipher_sqlite3*); + int iDb; sqlcipher_sqlite3_mutex_enter(db->mutex); - db->init.iDb = sqlcipher_sqlite3FindDbName(db, va_arg(ap,const char*)); - db->init.busy = db->init.imposterTable = va_arg(ap,int); - db->init.newTnum = va_arg(ap,int); - if( db->init.busy==0 && db->init.newTnum>0 ){ - sqlcipher_sqlite3ResetAllSchemasOfConnection(db); + iDb = sqlcipher_sqlite3FindDbName(db, va_arg(ap,const char*)); + if( iDb>=0 ){ + db->init.iDb = iDb; + db->init.busy = db->init.imposterTable = va_arg(ap,int); + db->init.newTnum = va_arg(ap,int); + if( db->init.busy==0 && db->init.newTnum>0 ){ + sqlcipher_sqlite3ResetAllSchemasOfConnection(db); + } } sqlcipher_sqlite3_mutex_leave(db->mutex); break; @@ -171009,7 +180498,78 @@ SQLITE_API int sqlcipher_sqlite3_test_control(int op, ...){ break; } + /* sqlcipher_sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, op, ptr) + ** + ** "ptr" is a pointer to a u32. + ** + ** op==0 Store the current sqlcipher_sqlite3TreeTrace in *ptr + ** op==1 Set sqlcipher_sqlite3TreeTrace to the value *ptr + ** op==3 Store the current sqlcipher_sqlite3WhereTrace in *ptr + ** op==3 Set sqlcipher_sqlite3WhereTrace to the value *ptr + */ + case SQLITE_TESTCTRL_TRACEFLAGS: { + int opTrace = va_arg(ap, int); + u32 *ptr = va_arg(ap, u32*); + switch( opTrace ){ + case 0: *ptr = sqlcipher_sqlite3TreeTrace; break; + case 1: sqlcipher_sqlite3TreeTrace = *ptr; break; + case 2: *ptr = sqlcipher_sqlite3WhereTrace; break; + case 3: sqlcipher_sqlite3WhereTrace = *ptr; break; + } + break; + } + /* sqlcipher_sqlite3_test_control(SQLITE_TESTCTRL_LOGEST, + ** double fIn, // Input value + ** int *pLogEst, // sqlcipher_sqlite3LogEstFromDouble(fIn) + ** u64 *pInt, // sqlcipher_sqlite3LogEstToInt(*pLogEst) + ** int *pLogEst2 // sqlcipher_sqlite3LogEst(*pInt) + ** ); + ** + ** Test access for the LogEst conversion routines. + */ + case SQLITE_TESTCTRL_LOGEST: { + double rIn = va_arg(ap, double); + LogEst rLogEst = sqlcipher_sqlite3LogEstFromDouble(rIn); + int *pI1 = va_arg(ap,int*); + u64 *pU64 = va_arg(ap,u64*); + int *pI2 = va_arg(ap,int*); + *pI1 = rLogEst; + *pU64 = sqlcipher_sqlite3LogEstToInt(rLogEst); + *pI2 = sqlcipher_sqlite3LogEst(*pU64); + break; + } + + +#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) + /* sqlcipher_sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue) + ** + ** If "id" is an integer between 1 and SQLITE_NTUNE then set the value + ** of the id-th tuning parameter to *piValue. If "id" is between -1 + ** and -SQLITE_NTUNE, then write the current value of the (-id)-th + ** tuning parameter into *piValue. + ** + ** Tuning parameters are for use during transient development builds, + ** to help find the best values for constants in the query planner. + ** Access tuning parameters using the Tuning(ID) macro. Set the + ** parameters in the CLI using ".testctrl tune ID VALUE". + ** + ** Transient use only. Tuning parameters should not be used in + ** checked-in code. + */ + case SQLITE_TESTCTRL_TUNE: { + int id = va_arg(ap, int); + int *piValue = va_arg(ap, int*); + if( id>0 && id<=SQLITE_NTUNE ){ + Tuning(id) = *piValue; + }else if( id<0 && id>=-SQLITE_NTUNE ){ + *piValue = Tuning(-id); + }else{ + rc = SQLITE_NOTFOUND; + } + break; + } +#endif } va_end(ap); #endif /* SQLITE_UNTESTABLE */ @@ -171117,7 +180677,7 @@ SQLITE_API const char *sqlcipher_sqlite3_uri_key(const char *zFilename, int N){ if( zFilename==0 || N<0 ) return 0; zFilename = databaseName(zFilename); zFilename += sqlcipher_sqlite3Strlen30(zFilename) + 1; - while( zFilename[0] && (N--)>0 ){ + while( ALWAYS(zFilename) && zFilename[0] && (N--)>0 ){ zFilename += sqlcipher_sqlite3Strlen30(zFilename) + 1; zFilename += sqlcipher_sqlite3Strlen30(zFilename) + 1; } @@ -171160,12 +180720,14 @@ SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_uri_int64( ** corruption. */ SQLITE_API const char *sqlcipher_sqlite3_filename_database(const char *zFilename){ + if( zFilename==0 ) return 0; return databaseName(zFilename); } SQLITE_API const char *sqlcipher_sqlite3_filename_journal(const char *zFilename){ + if( zFilename==0 ) return 0; zFilename = databaseName(zFilename); zFilename += sqlcipher_sqlite3Strlen30(zFilename) + 1; - while( zFilename[0] ){ + while( ALWAYS(zFilename) && zFilename[0] ){ zFilename += sqlcipher_sqlite3Strlen30(zFilename) + 1; zFilename += sqlcipher_sqlite3Strlen30(zFilename) + 1; } @@ -171176,7 +180738,7 @@ SQLITE_API const char *sqlcipher_sqlite3_filename_wal(const char *zFilename){ return 0; #else zFilename = sqlcipher_sqlite3_filename_journal(zFilename); - zFilename += sqlcipher_sqlite3Strlen30(zFilename) + 1; + if( zFilename ) zFilename += sqlcipher_sqlite3Strlen30(zFilename) + 1; return zFilename; #endif } @@ -171189,6 +180751,24 @@ SQLITE_PRIVATE Btree *sqlcipher_sqlite3DbNameToBtree(sqlcipher_sqlite3 *db, cons return iDb<0 ? 0 : db->aDb[iDb].pBt; } +/* +** Return the name of the N-th database schema. Return NULL if N is out +** of range. +*/ +SQLITE_API const char *sqlcipher_sqlite3_db_name(sqlcipher_sqlite3 *db, int N){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlcipher_sqlite3SafetyCheckOk(db) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif + if( N<0 || N>=db->nDb ){ + return 0; + }else{ + return db->aDb[N].zDbSName; + } +} + /* ** Return the filename of the database associated with a database ** connection. @@ -172452,7 +182032,7 @@ SQLITE_PRIVATE Fts3HashElem *sqlcipher_sqlite3Fts3HashFindElem(const Fts3Hash *, ** is used for assert() conditions that are true only if it can be ** guranteed that the database is not corrupt. */ -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) +#ifdef SQLITE_DEBUG SQLITE_API extern int sqlcipher_sqlite3_fts3_may_be_corrupt; # define assert_fts3_nc(x) assert(sqlcipher_sqlite3_fts3_may_be_corrupt || (x)) #else @@ -172469,17 +182049,18 @@ SQLITE_API extern int sqlcipher_sqlite3_fts3_may_be_corrupt; ** Macros indicating that conditional expressions are always true or ** false. */ -#ifdef SQLITE_COVERAGE_TEST -# define ALWAYS(x) (1) -# define NEVER(X) (0) -#elif defined(SQLITE_DEBUG) -# define ALWAYS(x) sqlcipher_sqlite3Fts3Always((x)!=0) -# define NEVER(x) sqlcipher_sqlite3Fts3Never((x)!=0) -SQLITE_PRIVATE int sqlcipher_sqlite3Fts3Always(int b); -SQLITE_PRIVATE int sqlcipher_sqlite3Fts3Never(int b); +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 +#endif +#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) +# 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) +# define ALWAYS(X) (X) +# define NEVER(X) (X) #endif /* @@ -172875,7 +182456,7 @@ struct Fts3MultiSegReader { int nAdvance; /* How many seg-readers to advance */ Fts3SegFilter *pFilter; /* Pointer to filter object */ char *aBuffer; /* Buffer to merge doclists in */ - int nBuffer; /* Allocated size of aBuffer[] in bytes */ + i64 nBuffer; /* Allocated size of aBuffer[] in bytes */ int iColFilter; /* If >=0, filter for this column */ int bRestart; @@ -172938,6 +182519,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Fts3ExprFree(Fts3Expr *); SQLITE_PRIVATE int sqlcipher_sqlite3Fts3ExprInitTestInterface(sqlcipher_sqlite3 *db, Fts3Hash*); SQLITE_PRIVATE int sqlcipher_sqlite3Fts3InitTerm(sqlcipher_sqlite3 *db); #endif +SQLITE_PRIVATE void *sqlcipher_sqlite3Fts3MallocZero(i64 nByte); SQLITE_PRIVATE int sqlcipher_sqlite3Fts3OpenTokenizer(sqlcipher_sqlite3_tokenizer *, int, const char *, int, sqlcipher_sqlite3_tokenizer_cursor ** @@ -172957,7 +182539,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader SQLITE_PRIVATE int sqlcipher_sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr); /* fts3_tokenize_vtab.c */ -SQLITE_PRIVATE int sqlcipher_sqlite3Fts3InitTok(sqlcipher_sqlite3*, Fts3Hash *); +SQLITE_PRIVATE int sqlcipher_sqlite3Fts3InitTok(sqlcipher_sqlite3*, Fts3Hash *, void(*xDestroy)(void*)); /* fts3_unicode2.c (functions generated by parsing unicode text files) */ #ifndef SQLITE_DISABLE_FTS3_UNICODE @@ -172990,25 +182572,26 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FtsUnicodeIsdiacritic(int); SQLITE_EXTENSION_INIT1 #endif +typedef struct Fts3HashWrapper Fts3HashWrapper; +struct Fts3HashWrapper { + Fts3Hash hash; /* Hash table */ + int nRef; /* Number of pointers to this object */ +}; + static int fts3EvalNext(Fts3Cursor *pCsr); static int fts3EvalStart(Fts3Cursor *pCsr); static int fts3TermSegReaderCursor( Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **); -#ifndef SQLITE_AMALGAMATION -# if defined(SQLITE_DEBUG) -SQLITE_PRIVATE int sqlcipher_sqlite3Fts3Always(int b) { assert( b ); return b; } -SQLITE_PRIVATE int sqlcipher_sqlite3Fts3Never(int b) { assert( !b ); return b; } -# endif -#endif - /* ** This variable is set to false when running tests for which the on disk ** structures should not be corrupt. Otherwise, true. If it is false, extra ** assert() conditions in the fts3 code are activated - conditions that are ** only true if it is guaranteed that the fts3 database is not corrupt. */ +#ifdef SQLITE_DEBUG SQLITE_API int sqlcipher_sqlite3_fts3_may_be_corrupt = 1; +#endif /* ** Write a 64-bit variable-length integer to memory starting at p[0]. @@ -173859,7 +183442,7 @@ static int fts3InitVtab( sqlcipher_sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ char **pzErr /* Write any error message here */ ){ - Fts3Hash *pHash = (Fts3Hash *)pAux; + Fts3Hash *pHash = &((Fts3HashWrapper*)pAux)->hash; Fts3Table *p = 0; /* Pointer to allocated vtab */ int rc = SQLITE_OK; /* Return code */ int i; /* Iterator variable */ @@ -174579,7 +184162,7 @@ static int fts3ScanInteriorNode( char *zBuffer = 0; /* Buffer to load terms into */ i64 nAlloc = 0; /* Size of allocated buffer */ int isFirstTerm = 1; /* True when processing first term on page */ - sqlcipher_sqlite3_int64 iChild; /* Block id of child node to descend to */ + u64 iChild; /* Block id of child node to descend to */ int nBuffer = 0; /* Total term size */ /* Skip over the 'height' varint that occurs at the start of every @@ -174595,8 +184178,8 @@ static int fts3ScanInteriorNode( ** table, then there are always 20 bytes of zeroed padding following the ** nNode bytes of content (see sqlcipher_sqlite3Fts3ReadBlock() for details). */ - zCsr += sqlcipher_sqlite3Fts3GetVarint(zCsr, &iChild); - zCsr += sqlcipher_sqlite3Fts3GetVarint(zCsr, &iChild); + zCsr += sqlcipher_sqlite3Fts3GetVarintU(zCsr, &iChild); + zCsr += sqlcipher_sqlite3Fts3GetVarintU(zCsr, &iChild); if( zCsr>zEnd ){ return FTS_CORRUPT_VTAB; } @@ -174649,20 +184232,20 @@ static int fts3ScanInteriorNode( */ cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer)); if( piFirst && (cmp<0 || (cmp==0 && nBuffer>nTerm)) ){ - *piFirst = iChild; + *piFirst = (i64)iChild; piFirst = 0; } if( piLast && cmp<0 ){ - *piLast = iChild; + *piLast = (i64)iChild; piLast = 0; } iChild++; }; - if( piFirst ) *piFirst = iChild; - if( piLast ) *piLast = iChild; + if( piFirst ) *piFirst = (i64)iChild; + if( piLast ) *piLast = (i64)iChild; finish_scan: sqlcipher_sqlite3_free(zBuffer); @@ -175569,7 +185152,7 @@ static int fts3TermSelectMerge( ** ** Similar padding is added in the fts3DoclistOrMerge() function. */ - pTS->aaOutput[0] = sqlcipher_sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1); + pTS->aaOutput[0] = sqlcipher_sqlite3_malloc64((i64)nDoclist + FTS3_VARINT_MAX + 1); pTS->anOutput[0] = nDoclist; if( pTS->aaOutput[0] ){ memcpy(pTS->aaOutput[0], aDoclist, nDoclist); @@ -176268,14 +185851,20 @@ static int fts3SetHasStat(Fts3Table *p){ */ static int fts3BeginMethod(sqlcipher_sqlite3_vtab *pVtab){ Fts3Table *p = (Fts3Table*)pVtab; + int rc; UNUSED_PARAMETER(pVtab); assert( p->pSegments==0 ); assert( p->nPendingData==0 ); assert( p->inTransaction!=1 ); - TESTONLY( p->inTransaction = 1 ); - TESTONLY( p->mxSavepoint = -1; ); p->nLeafAdd = 0; - return fts3SetHasStat(p); + rc = fts3SetHasStat(p); +#ifdef SQLITE_DEBUG + if( rc==SQLITE_OK ){ + p->inTransaction = 1; + p->mxSavepoint = -1; + } +#endif + return rc; } /* @@ -176688,9 +186277,12 @@ static const sqlcipher_sqlite3_module fts3Module = { ** allocated for the tokenizer hash table. */ static void hashDestroy(void *p){ - Fts3Hash *pHash = (Fts3Hash *)p; - sqlcipher_sqlite3Fts3HashClear(pHash); - sqlcipher_sqlite3_free(pHash); + Fts3HashWrapper *pHash = (Fts3HashWrapper *)p; + pHash->nRef--; + if( pHash->nRef<=0 ){ + sqlcipher_sqlite3Fts3HashClear(&pHash->hash); + sqlcipher_sqlite3_free(pHash); + } } /* @@ -176720,7 +186312,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Fts3IcuTokenizerModule(sqlcipher_sqlite3_to */ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3Init(sqlcipher_sqlite3 *db){ int rc = SQLITE_OK; - Fts3Hash *pHash = 0; + Fts3HashWrapper *pHash = 0; const sqlcipher_sqlite3_tokenizer_module *pSimple = 0; const sqlcipher_sqlite3_tokenizer_module *pPorter = 0; #ifndef SQLITE_DISABLE_FTS3_UNICODE @@ -176748,23 +186340,24 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3Init(sqlcipher_sqlite3 *db){ sqlcipher_sqlite3Fts3PorterTokenizerModule(&pPorter); /* Allocate and initialize the hash-table used to store tokenizers. */ - pHash = sqlcipher_sqlite3_malloc(sizeof(Fts3Hash)); + pHash = sqlcipher_sqlite3_malloc(sizeof(Fts3HashWrapper)); if( !pHash ){ rc = SQLITE_NOMEM; }else{ - sqlcipher_sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); + sqlcipher_sqlite3Fts3HashInit(&pHash->hash, FTS3_HASH_STRING, 1); + pHash->nRef = 0; } /* Load the built-in tokenizers into the hash table */ if( rc==SQLITE_OK ){ - if( sqlcipher_sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple) - || sqlcipher_sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter) + if( sqlcipher_sqlite3Fts3HashInsert(&pHash->hash, "simple", 7, (void *)pSimple) + || sqlcipher_sqlite3Fts3HashInsert(&pHash->hash, "porter", 7, (void *)pPorter) #ifndef SQLITE_DISABLE_FTS3_UNICODE - || sqlcipher_sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode) + || sqlcipher_sqlite3Fts3HashInsert(&pHash->hash, "unicode61", 10, (void *)pUnicode) #endif #ifdef SQLITE_ENABLE_ICU - || (pIcu && sqlcipher_sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu)) + || (pIcu && sqlcipher_sqlite3Fts3HashInsert(&pHash->hash, "icu", 4, (void *)pIcu)) #endif ){ rc = SQLITE_NOMEM; @@ -176773,7 +186366,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3Init(sqlcipher_sqlite3 *db){ #ifdef SQLITE_TEST if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3Fts3ExprInitTestInterface(db, pHash); + rc = sqlcipher_sqlite3Fts3ExprInitTestInterface(db, &pHash->hash); } #endif @@ -176782,23 +186375,26 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3Init(sqlcipher_sqlite3 *db){ ** module with sqlite. */ if( SQLITE_OK==rc - && SQLITE_OK==(rc = sqlcipher_sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) + && SQLITE_OK==(rc=sqlcipher_sqlite3Fts3InitHashTable(db,&pHash->hash,"fts3_tokenizer")) && SQLITE_OK==(rc = sqlcipher_sqlite3_overload_function(db, "snippet", -1)) && SQLITE_OK==(rc = sqlcipher_sqlite3_overload_function(db, "offsets", 1)) && SQLITE_OK==(rc = sqlcipher_sqlite3_overload_function(db, "matchinfo", 1)) && SQLITE_OK==(rc = sqlcipher_sqlite3_overload_function(db, "matchinfo", 2)) && SQLITE_OK==(rc = sqlcipher_sqlite3_overload_function(db, "optimize", 1)) ){ + pHash->nRef++; rc = sqlcipher_sqlite3_create_module_v2( db, "fts3", &fts3Module, (void *)pHash, hashDestroy ); if( rc==SQLITE_OK ){ + pHash->nRef++; rc = sqlcipher_sqlite3_create_module_v2( - db, "fts4", &fts3Module, (void *)pHash, 0 + db, "fts4", &fts3Module, (void *)pHash, hashDestroy ); } if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3Fts3InitTok(db, (void *)pHash); + pHash->nRef++; + rc = sqlcipher_sqlite3Fts3InitTok(db, (void *)pHash, hashDestroy); } return rc; } @@ -176807,7 +186403,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3Init(sqlcipher_sqlite3 *db){ /* An error has occurred. Delete the hash table and return the error code. */ assert( rc!=SQLITE_OK ); if( pHash ){ - sqlcipher_sqlite3Fts3HashClear(pHash); + sqlcipher_sqlite3Fts3HashClear(&pHash->hash); sqlcipher_sqlite3_free(pHash); } return rc; @@ -176976,8 +186572,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ char *aPoslist = 0; /* Position list for deferred tokens */ int nPoslist = 0; /* Number of bytes in aPoslist */ int iPrev = -1; /* Token number of previous deferred token */ - - assert( pPhrase->doclist.bFreeList==0 ); + char *aFree = (pPhrase->doclist.bFreeList ? pPhrase->doclist.pList : 0); for(iToken=0; iTokennToken; iToken++){ Fts3PhraseToken *pToken = &pPhrase->aToken[iToken]; @@ -176991,6 +186586,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ if( pList==0 ){ sqlcipher_sqlite3_free(aPoslist); + sqlcipher_sqlite3_free(aFree); pPhrase->doclist.pList = 0; pPhrase->doclist.nList = 0; return SQLITE_OK; @@ -177011,6 +186607,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ nPoslist = (int)(aOut - aPoslist); if( nPoslist==0 ){ sqlcipher_sqlite3_free(aPoslist); + sqlcipher_sqlite3_free(aFree); pPhrase->doclist.pList = 0; pPhrase->doclist.nList = 0; return SQLITE_OK; @@ -177043,13 +186640,14 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ nDistance = iPrev - nMaxUndeferred; } - aOut = (char *)sqlcipher_sqlite3_malloc(nPoslist+8); + aOut = (char *)sqlcipher_sqlite3Fts3MallocZero(nPoslist+FTS3_BUFFER_PADDING); if( !aOut ){ sqlcipher_sqlite3_free(aPoslist); return SQLITE_NOMEM; } pPhrase->doclist.pList = aOut; + assert( p1 && p2 ); if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){ pPhrase->doclist.bFreeList = 1; pPhrase->doclist.nList = (int)(aOut - pPhrase->doclist.pList); @@ -177062,6 +186660,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ } } + if( pPhrase->doclist.pList!=aFree ) sqlcipher_sqlite3_free(aFree); return SQLITE_OK; } #endif /* SQLITE_DISABLE_FTS4_DEFERRED */ @@ -177154,7 +186753,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Fts3DoclistPrev( assert( nDoclist>0 ); assert( *pbEof==0 ); - assert( p || *piDocid==0 ); + assert_fts3_nc( p || *piDocid==0 ); assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) ); if( p==0 ){ @@ -177410,7 +187009,7 @@ static int fts3EvalIncrPhraseNext( if( bEof==0 ){ int nList = 0; int nByte = a[p->nToken-1].nList; - char *aDoclist = sqlcipher_sqlite3_malloc(nByte+FTS3_BUFFER_PADDING); + char *aDoclist = sqlcipher_sqlite3_malloc64((i64)nByte+FTS3_BUFFER_PADDING); if( !aDoclist ) return SQLITE_NOMEM; memcpy(aDoclist, a[p->nToken-1].pList, nByte+1); memset(&aDoclist[nByte], 0, FTS3_BUFFER_PADDING); @@ -177804,16 +187403,15 @@ static int fts3EvalStart(Fts3Cursor *pCsr){ #ifndef SQLITE_DISABLE_FTS4_DEFERRED if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){ Fts3TokenAndCost *aTC; - Fts3Expr **apOr; aTC = (Fts3TokenAndCost *)sqlcipher_sqlite3_malloc64( sizeof(Fts3TokenAndCost) * nToken + sizeof(Fts3Expr *) * nOr * 2 ); - apOr = (Fts3Expr **)&aTC[nToken]; if( !aTC ){ rc = SQLITE_NOMEM; }else{ + Fts3Expr **apOr = (Fts3Expr **)&aTC[nToken]; int ii; Fts3TokenAndCost *pTC = aTC; Fts3Expr **ppOr = apOr; @@ -177894,9 +187492,9 @@ static int fts3EvalNearTrim( ); if( res ){ nNew = (int)(pOut - pPhrase->doclist.pList) - 1; - if( nNew>=0 ){ + assert_fts3_nc( nNew<=pPhrase->doclist.nList && nNew>0 ); + if( nNew>=0 && nNew<=pPhrase->doclist.nList ){ assert( pPhrase->doclist.pList[nNew]=='\0' ); - assert( nNew<=pPhrase->doclist.nList && nNew>0 ); memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew); pPhrase->doclist.nList = nNew; } @@ -178019,8 +187617,8 @@ static void fts3EvalNextRow( Fts3Expr *pRight = pExpr->pRight; sqlcipher_sqlite3_int64 iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid); - assert( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); - assert( pRight->bStart || pLeft->iDocid==pRight->iDocid ); + assert_fts3_nc( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); + assert_fts3_nc( pRight->bStart || pLeft->iDocid==pRight->iDocid ); if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){ fts3EvalNextRow(pCsr, pLeft, pRc); @@ -178237,11 +187835,10 @@ static int fts3EvalTestExpr( default: { #ifndef SQLITE_DISABLE_FTS4_DEFERRED - if( pCsr->pDeferred - && (pExpr->iDocid==pCsr->iPrevId || pExpr->bDeferred) - ){ + if( pCsr->pDeferred && (pExpr->bDeferred || ( + pExpr->iDocid==pCsr->iPrevId && pExpr->pPhrase->doclist.pList + ))){ Fts3Phrase *pPhrase = pExpr->pPhrase; - assert( pExpr->bDeferred || pPhrase->doclist.bFreeList==0 ); if( pExpr->bDeferred ){ fts3EvalInvalidatePoslist(pPhrase); } @@ -178658,6 +188255,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3EvalPhrasePoslist( if( bEofSave==0 && pNear->iDocid==iDocid ) break; } assert( rc!=SQLITE_OK || pPhrase->bIncr==0 ); + if( rc==SQLITE_OK && pNear->bEof!=bEofSave ){ + rc = FTS_CORRUPT_VTAB; + } } if( bTreeEof ){ while( rc==SQLITE_OK && !pNear->bEof ){ @@ -179080,6 +188680,7 @@ static int fts3auxNextMethod(sqlcipher_sqlite3_vtab_cursor *pCursor){ if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM; memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat); iCol = 0; + rc = SQLITE_OK; while( iaStat[iCol+1].nDoc++; eState = 2; @@ -179131,7 +188736,6 @@ static int fts3auxNextMethod(sqlcipher_sqlite3_vtab_cursor *pCursor){ } pCsr->iCol = 0; - rc = SQLITE_OK; }else{ pCsr->isEof = 1; } @@ -179189,6 +188793,7 @@ static int fts3auxFilterMethod( sqlcipher_sqlite3Fts3SegReaderFinish(&pCsr->csr); sqlcipher_sqlite3_free((void *)pCsr->filter.zTerm); sqlcipher_sqlite3_free(pCsr->aStat); + sqlcipher_sqlite3_free(pCsr->zStop); memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr); pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; @@ -179459,7 +189064,7 @@ static int fts3isspace(char c){ ** zero the memory before returning a pointer to it. If unsuccessful, ** return NULL. */ -static void *fts3MallocZero(sqlcipher_sqlite3_int64 nByte){ +SQLITE_PRIVATE void *sqlcipher_sqlite3Fts3MallocZero(sqlcipher_sqlite3_int64 nByte){ void *pRet = sqlcipher_sqlite3_malloc64(nByte); if( pRet ) memset(pRet, 0, nByte); return pRet; @@ -179540,7 +189145,7 @@ static int getNextToken( rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); if( rc==SQLITE_OK ){ nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; - pRet = (Fts3Expr *)fts3MallocZero(nByte); + pRet = (Fts3Expr *)sqlcipher_sqlite3Fts3MallocZero(nByte); if( !pRet ){ rc = SQLITE_NOMEM; }else{ @@ -179795,7 +189400,7 @@ static int getNextNode( if( fts3isspace(cNext) || cNext=='"' || cNext=='(' || cNext==')' || cNext==0 ){ - pRet = (Fts3Expr *)fts3MallocZero(sizeof(Fts3Expr)); + pRet = (Fts3Expr *)sqlcipher_sqlite3Fts3MallocZero(sizeof(Fts3Expr)); if( !pRet ){ return SQLITE_NOMEM; } @@ -179830,6 +189435,11 @@ static int getNextNode( if( *zInput=='(' ){ int nConsumed = 0; pParse->nNest++; +#if !defined(SQLITE_MAX_EXPR_DEPTH) + if( pParse->nNest>1000 ) return SQLITE_ERROR; +#elif SQLITE_MAX_EXPR_DEPTH>0 + if( pParse->nNest>SQLITE_MAX_EXPR_DEPTH ) return SQLITE_ERROR; +#endif rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed); *pnConsumed = (int)(zInput - z) + 1 + nConsumed; return rc; @@ -179969,7 +189579,7 @@ static int fts3ExprParse( && p->eType==FTSQUERY_PHRASE && pParse->isNot ){ /* Create an implicit NOT operator. */ - Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr)); + Fts3Expr *pNot = sqlcipher_sqlite3Fts3MallocZero(sizeof(Fts3Expr)); if( !pNot ){ sqlcipher_sqlite3Fts3ExprFree(p); rc = SQLITE_NOMEM; @@ -180003,7 +189613,7 @@ static int fts3ExprParse( /* Insert an implicit AND operator. */ Fts3Expr *pAnd; assert( pRet && pPrev ); - pAnd = fts3MallocZero(sizeof(Fts3Expr)); + pAnd = sqlcipher_sqlite3Fts3MallocZero(sizeof(Fts3Expr)); if( !pAnd ){ sqlcipher_sqlite3Fts3ExprFree(p); rc = SQLITE_NOMEM; @@ -181635,7 +191245,7 @@ static int porterNext( if( n>c->nAllocated ){ char *pNew; c->nAllocated = n+20; - pNew = sqlcipher_sqlite3_realloc(c->zToken, c->nAllocated); + pNew = sqlcipher_sqlite3_realloc64(c->zToken, c->nAllocated); if( !pNew ) return SQLITE_NOMEM; c->zToken = pNew; } @@ -182387,7 +191997,7 @@ static int simpleNext( if( n>c->nTokenAllocated ){ char *pNew; c->nTokenAllocated = n+20; - pNew = sqlcipher_sqlite3_realloc(c->pToken, c->nTokenAllocated); + pNew = sqlcipher_sqlite3_realloc64(c->pToken, c->nTokenAllocated); if( !pNew ) return SQLITE_NOMEM; c->pToken = pNew; } @@ -182859,7 +192469,7 @@ static int fts3tokRowidMethod( ** Register the fts3tok module with database connection db. Return SQLITE_OK ** if successful or an error code if sqlcipher_sqlite3_create_module() fails. */ -SQLITE_PRIVATE int sqlcipher_sqlite3Fts3InitTok(sqlcipher_sqlite3 *db, Fts3Hash *pHash){ +SQLITE_PRIVATE int sqlcipher_sqlite3Fts3InitTok(sqlcipher_sqlite3 *db, Fts3Hash *pHash, void(*xDestroy)(void*)){ static const sqlcipher_sqlite3_module fts3tok_module = { 0, /* iVersion */ fts3tokConnectMethod, /* xCreate */ @@ -182888,7 +192498,9 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3InitTok(sqlcipher_sqlite3 *db, Fts3Hash }; int rc; /* Return code */ - rc = sqlcipher_sqlite3_create_module(db, "fts3tokenize", &fts3tok_module, (void*)pHash); + rc = sqlcipher_sqlite3_create_module_v2( + db, "fts3tokenize", &fts3tok_module, (void*)pHash, xDestroy + ); return rc; } @@ -183547,7 +193159,7 @@ static int fts3PendingListAppendVarint( /* Allocate or grow the PendingList as required. */ if( !p ){ - p = sqlcipher_sqlite3_malloc(sizeof(*p) + 100); + p = sqlcipher_sqlite3_malloc64(sizeof(*p) + 100); if( !p ){ return SQLITE_NOMEM; } @@ -183556,14 +193168,14 @@ static int fts3PendingListAppendVarint( p->nData = 0; } else if( p->nData+FTS3_VARINT_MAX+1>p->nSpace ){ - int nNew = p->nSpace * 2; - p = sqlcipher_sqlite3_realloc(p, sizeof(*p) + nNew); + i64 nNew = p->nSpace * 2; + p = sqlcipher_sqlite3_realloc64(p, sizeof(*p) + nNew); if( !p ){ sqlcipher_sqlite3_free(*pp); *pp = 0; return SQLITE_NOMEM; } - p->nSpace = nNew; + p->nSpace = (int)nNew; p->aData = (char *)&p[1]; } @@ -184120,7 +193732,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3ReadBlock( int nByte = sqlcipher_sqlite3_blob_bytes(p->pSegments); *pnBlob = nByte; if( paBlob ){ - char *aByte = sqlcipher_sqlite3_malloc(nByte + FTS3_NODE_PADDING); + char *aByte = sqlcipher_sqlite3_malloc64((i64)nByte + FTS3_NODE_PADDING); if( !aByte ){ rc = SQLITE_NOMEM; }else{ @@ -184233,9 +193845,19 @@ static int fts3SegReaderNext( char *aCopy; PendingList *pList = (PendingList *)fts3HashData(pElem); int nCopy = pList->nData+1; - pReader->zTerm = (char *)fts3HashKey(pElem); - pReader->nTerm = fts3HashKeysize(pElem); - aCopy = (char*)sqlcipher_sqlite3_malloc(nCopy); + + int nTerm = fts3HashKeysize(pElem); + if( (nTerm+1)>pReader->nTermAlloc ){ + sqlcipher_sqlite3_free(pReader->zTerm); + pReader->zTerm = (char*)sqlcipher_sqlite3_malloc64(((i64)nTerm+1)*2); + if( !pReader->zTerm ) return SQLITE_NOMEM; + pReader->nTermAlloc = (nTerm+1)*2; + } + memcpy(pReader->zTerm, fts3HashKey(pElem), nTerm); + pReader->zTerm[nTerm] = '\0'; + pReader->nTerm = nTerm; + + aCopy = (char*)sqlcipher_sqlite3_malloc64(nCopy); if( !aCopy ) return SQLITE_NOMEM; memcpy(aCopy, pList->aData, nCopy); pReader->nNode = pReader->nDoclist = nCopy; @@ -184487,9 +194109,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3MsrOvfl( */ SQLITE_PRIVATE void sqlcipher_sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){ if( pReader ){ - if( !fts3SegReaderIsPending(pReader) ){ - sqlcipher_sqlite3_free(pReader->zTerm); - } + sqlcipher_sqlite3_free(pReader->zTerm); if( !fts3SegReaderIsRootOnly(pReader) ){ sqlcipher_sqlite3_free(pReader->aNode); } @@ -184524,7 +194144,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3SegReaderNew( nExtra = nRoot + FTS3_NODE_PADDING; } - pReader = (Fts3SegReader *)sqlcipher_sqlite3_malloc(sizeof(Fts3SegReader) + nExtra); + pReader = (Fts3SegReader *)sqlcipher_sqlite3_malloc64(sizeof(Fts3SegReader) + nExtra); if( !pReader ){ return SQLITE_NOMEM; } @@ -184616,7 +194236,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3SegReaderPending( if( nElem==nAlloc ){ Fts3HashElem **aElem2; nAlloc += 16; - aElem2 = (Fts3HashElem **)sqlcipher_sqlite3_realloc( + aElem2 = (Fts3HashElem **)sqlcipher_sqlite3_realloc64( aElem, nAlloc*sizeof(Fts3HashElem *) ); if( !aElem2 ){ @@ -184705,7 +194325,7 @@ static int fts3SegReaderCmp(Fts3SegReader *pLhs, Fts3SegReader *pRhs){ if( rc==0 ){ rc = pRhs->iIdx - pLhs->iIdx; } - assert( rc!=0 ); + assert_fts3_nc( rc!=0 ); return rc; } @@ -184901,8 +194521,8 @@ static int fts3PrefixCompress( int nNext /* Size of buffer zNext in bytes */ ){ int n; - UNUSED_PARAMETER(nNext); - for(n=0; naData==(char *)&pTree[1] ); - pTree->aData = (char *)sqlcipher_sqlite3_malloc(nReq); + pTree->aData = (char *)sqlcipher_sqlite3_malloc64(nReq); if( !pTree->aData ){ return SQLITE_NOMEM; } @@ -184968,7 +194588,7 @@ static int fts3NodeAddTerm( if( isCopyTerm ){ if( pTree->nMalloczMalloc, nTerm*2); + char *zNew = sqlcipher_sqlite3_realloc64(pTree->zMalloc, (i64)nTerm*2); if( !zNew ){ return SQLITE_NOMEM; } @@ -184994,7 +194614,7 @@ static int fts3NodeAddTerm( ** now. Instead, the term is inserted into the parent of pTree. If pTree ** has no parent, one is created here. */ - pNew = (SegmentNode *)sqlcipher_sqlite3_malloc(sizeof(SegmentNode) + p->nNodeSize); + pNew = (SegmentNode *)sqlcipher_sqlite3_malloc64(sizeof(SegmentNode) + p->nNodeSize); if( !pNew ){ return SQLITE_NOMEM; } @@ -185132,7 +194752,7 @@ static int fts3SegWriterAdd( ){ int nPrefix; /* Size of term prefix in bytes */ int nSuffix; /* Size of term suffix in bytes */ - int nReq; /* Number of bytes required on leaf page */ + i64 nReq; /* Number of bytes required on leaf page */ int nData; SegmentWriter *pWriter = *ppWriter; @@ -185141,13 +194761,13 @@ static int fts3SegWriterAdd( sqlcipher_sqlite3_stmt *pStmt; /* Allocate the SegmentWriter structure */ - pWriter = (SegmentWriter *)sqlcipher_sqlite3_malloc(sizeof(SegmentWriter)); + pWriter = (SegmentWriter *)sqlcipher_sqlite3_malloc64(sizeof(SegmentWriter)); if( !pWriter ) return SQLITE_NOMEM; memset(pWriter, 0, sizeof(SegmentWriter)); *ppWriter = pWriter; /* Allocate a buffer in which to accumulate data */ - pWriter->aData = (char *)sqlcipher_sqlite3_malloc(p->nNodeSize); + pWriter->aData = (char *)sqlcipher_sqlite3_malloc64(p->nNodeSize); if( !pWriter->aData ) return SQLITE_NOMEM; pWriter->nSize = p->nNodeSize; @@ -185222,7 +194842,7 @@ static int fts3SegWriterAdd( ** the buffer to make it large enough. */ if( nReq>pWriter->nSize ){ - char *aNew = sqlcipher_sqlite3_realloc(pWriter->aData, nReq); + char *aNew = sqlcipher_sqlite3_realloc64(pWriter->aData, nReq); if( !aNew ) return SQLITE_NOMEM; pWriter->aData = aNew; pWriter->nSize = nReq; @@ -185247,7 +194867,7 @@ static int fts3SegWriterAdd( */ if( isCopyTerm ){ if( nTerm>pWriter->nMalloc ){ - char *zNew = sqlcipher_sqlite3_realloc(pWriter->zMalloc, nTerm*2); + char *zNew = sqlcipher_sqlite3_realloc64(pWriter->zMalloc, (i64)nTerm*2); if( !zNew ){ return SQLITE_NOMEM; } @@ -185555,12 +195175,12 @@ static void fts3ColumnFilter( static int fts3MsrBufferData( Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */ char *pList, - int nList + i64 nList ){ if( nList>pMsr->nBuffer ){ char *pNew; pMsr->nBuffer = nList*2; - pNew = (char *)sqlcipher_sqlite3_realloc(pMsr->aBuffer, pMsr->nBuffer); + pNew = (char *)sqlcipher_sqlite3_realloc64(pMsr->aBuffer, pMsr->nBuffer); if( !pNew ) return SQLITE_NOMEM; pMsr->aBuffer = pNew; } @@ -185616,7 +195236,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3MsrIncrNext( fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp); if( nList>0 && fts3SegReaderIsPending(apSegment[0]) ){ - rc = fts3MsrBufferData(pMsr, pList, nList+1); + rc = fts3MsrBufferData(pMsr, pList, (i64)nList+1); if( rc!=SQLITE_OK ) return rc; assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 ); pList = pMsr->aBuffer; @@ -185753,11 +195373,11 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr) return SQLITE_OK; } -static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, int nReq){ +static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, i64 nReq){ if( nReq>pCsr->nBuffer ){ char *aNew; pCsr->nBuffer = nReq*2; - aNew = sqlcipher_sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer); + aNew = sqlcipher_sqlite3_realloc64(pCsr->aBuffer, pCsr->nBuffer); if( !aNew ){ return SQLITE_NOMEM; } @@ -185848,7 +195468,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3SegReaderStep( ){ pCsr->nDoclist = apSegment[0]->nDoclist; if( fts3SegReaderIsPending(apSegment[0]) ){ - rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist); + rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, + (i64)pCsr->nDoclist); pCsr->aDoclist = pCsr->aBuffer; }else{ pCsr->aDoclist = apSegment[0]->aDoclist; @@ -185901,7 +195522,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3SegReaderStep( nByte = sqlcipher_sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0); - rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist); + rc = fts3GrowSegReaderBuffer(pCsr, + (i64)nByte+nDoclist+FTS3_NODE_PADDING); if( rc ) return rc; if( isFirst ){ @@ -185927,7 +195549,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3SegReaderStep( fts3SegReaderSort(apSegment, nMerge, j, xCmp); } if( nDoclist>0 ){ - rc = fts3GrowSegReaderBuffer(pCsr, nDoclist+FTS3_NODE_PADDING); + rc = fts3GrowSegReaderBuffer(pCsr, (i64)nDoclist+FTS3_NODE_PADDING); if( rc ) return rc; memset(&pCsr->aBuffer[nDoclist], 0, FTS3_NODE_PADDING); pCsr->aDoclist = pCsr->aBuffer; @@ -186640,7 +196262,7 @@ struct NodeReader { static void blobGrowBuffer(Blob *pBlob, int nMin, int *pRc){ if( *pRc==SQLITE_OK && nMin>pBlob->nAlloc ){ int nAlloc = nMin; - char *a = (char *)sqlcipher_sqlite3_realloc(pBlob->a, nAlloc); + char *a = (char *)sqlcipher_sqlite3_realloc64(pBlob->a, nAlloc); if( a ){ pBlob->nAlloc = nAlloc; pBlob->a = a; @@ -186681,7 +196303,7 @@ static int nodeReaderNext(NodeReader *p){ return FTS_CORRUPT_VTAB; } blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc); - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && ALWAYS(p->term.a!=0) ){ memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix); p->term.n = nPrefix+nSuffix; p->iOff += nSuffix; @@ -186789,6 +196411,8 @@ static int fts3IncrmergePush( pBlk->n += sqlcipher_sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nPrefix); } pBlk->n += sqlcipher_sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nSuffix); + assert( nPrefix+nSuffix<=nTerm ); + assert( nPrefix>=0 ); memcpy(&pBlk->a[pBlk->n], &zTerm[nPrefix], nSuffix); pBlk->n += nSuffix; @@ -186911,6 +196535,7 @@ static int fts3IncrmergeAppend( pLeaf = &pWriter->aNodeWriter[0]; nPrefix = fts3PrefixCompress(pLeaf->key.a, pLeaf->key.n, zTerm, nTerm); nSuffix = nTerm - nPrefix; + if(nSuffix<=0 ) return FTS_CORRUPT_VTAB; nSpace = sqlcipher_sqlite3Fts3VarintLen(nPrefix); nSpace += sqlcipher_sqlite3Fts3VarintLen(nSuffix) + nSuffix; @@ -187075,7 +196700,11 @@ static int fts3TermCmp( int nCmp = MIN(nLhs, nRhs); int res; - res = (nCmp ? memcmp(zLhs, zRhs, nCmp) : 0); + if( nCmp && ALWAYS(zLhs) && ALWAYS(zRhs) ){ + res = memcmp(zLhs, zRhs, nCmp); + }else{ + res = 0; + } if( res==0 ) res = nLhs - nRhs; return res; @@ -187233,17 +196862,20 @@ static int fts3IncrmergeLoad( while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader); blobGrowBuffer(&pNode->key, reader.term.n, &rc); if( rc==SQLITE_OK ){ - memcpy(pNode->key.a, reader.term.a, reader.term.n); + assert_fts3_nc( reader.term.n>0 || reader.aNode==0 ); + if( reader.term.n>0 ){ + memcpy(pNode->key.a, reader.term.a, reader.term.n); + } pNode->key.n = reader.term.n; if( i>0 ){ char *aBlock = 0; int nBlock = 0; pNode = &pWriter->aNodeWriter[i-1]; pNode->iBlock = reader.iChild; - rc = sqlcipher_sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock, 0); + rc = sqlcipher_sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0); blobGrowBuffer(&pNode->block, MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc - ); + ); if( rc==SQLITE_OK ){ memcpy(pNode->block.a, aBlock, nBlock); pNode->block.n = nBlock; @@ -187427,7 +197059,7 @@ static int fts3RepackSegdirLevel( if( nIdx>=nAlloc ){ int *aNew; nAlloc += 16; - aNew = sqlcipher_sqlite3_realloc(aIdx, nAlloc*sizeof(int)); + aNew = sqlcipher_sqlite3_realloc64(aIdx, nAlloc*sizeof(int)); if( !aNew ){ rc = SQLITE_NOMEM; break; @@ -187716,7 +197348,7 @@ static int fts3IncrmergeHintLoad(Fts3Table *p, Blob *pHint){ if( aHint ){ blobGrowBuffer(pHint, nHint, &rc); if( rc==SQLITE_OK ){ - memcpy(pHint->a, aHint, nHint); + if( ALWAYS(pHint->a!=0) ) memcpy(pHint->a, aHint, nHint); pHint->n = nHint; } } @@ -187801,7 +197433,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int /* Allocate space for the cursor, filter and writer objects */ const int nAlloc = sizeof(*pCsr) + sizeof(*pFilter) + sizeof(*pWriter); - pWriter = (IncrmergeWriter *)sqlcipher_sqlite3_malloc(nAlloc); + pWriter = (IncrmergeWriter *)sqlcipher_sqlite3_malloc64(nAlloc); if( !pWriter ) return SQLITE_NOMEM; pFilter = (Fts3SegFilter *)&pWriter[1]; pCsr = (Fts3MultiSegReader *)&pFilter[1]; @@ -188437,7 +198069,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3DeferredTokenList( return SQLITE_OK; } - pRet = (char *)sqlcipher_sqlite3_malloc(p->pList->nData); + pRet = (char *)sqlcipher_sqlite3_malloc64(p->pList->nData); if( !pRet ) return SQLITE_NOMEM; nSkip = sqlcipher_sqlite3Fts3GetVarint(p->pList->aData, &dummy); @@ -188457,7 +198089,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3DeferToken( int iCol /* Column that token must appear in (or -1) */ ){ Fts3DeferredToken *pDeferred; - pDeferred = sqlcipher_sqlite3_malloc(sizeof(*pDeferred)); + pDeferred = sqlcipher_sqlite3_malloc64(sizeof(*pDeferred)); if( !pDeferred ){ return SQLITE_NOMEM; } @@ -188712,6 +198344,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3Fts3Optimize(Fts3Table *p){ /* #include */ /* #include */ +#ifndef SQLITE_AMALGAMATION +typedef sqlcipher_sqlite3_int64 i64; +#endif + /* ** Characters that may appear in the second argument to matchinfo(). */ @@ -188762,9 +198398,9 @@ struct SnippetIter { struct SnippetPhrase { int nToken; /* Number of tokens in phrase */ char *pList; /* Pointer to start of phrase position list */ - int iHead; /* Next value in position list */ + i64 iHead; /* Next value in position list */ char *pHead; /* Position list data following iHead */ - int iTail; /* Next value in trailing position list */ + i64 iTail; /* Next value in trailing position list */ char *pTail; /* Position list data following iTail */ }; @@ -188829,9 +198465,8 @@ static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ + sizeof(MatchinfoBuffer); sqlcipher_sqlite3_int64 nStr = strlen(zMatchinfo); - pRet = sqlcipher_sqlite3_malloc64(nByte + nStr+1); + pRet = sqlcipher_sqlite3Fts3MallocZero(nByte + nStr+1); if( pRet ){ - memset(pRet, 0, nByte); pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet; pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + sizeof(u32)*((int)nElem+1); @@ -188929,7 +198564,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Fts3MIBufferFree(MatchinfoBuffer *p){ ** After it returns, *piPos contains the value of the next element of the ** list and *pp is advanced to the following varint. */ -static void fts3GetDeltaPosition(char **pp, int *piPos){ +static void fts3GetDeltaPosition(char **pp, i64 *piPos){ int iVal; *pp += fts3GetVarint32(*pp, &iVal); *piPos += (iVal-2); @@ -189038,10 +198673,10 @@ static int fts3ExprPhraseCount(Fts3Expr *pExpr){ ** arguments so that it points to the first element with a value greater ** than or equal to parameter iNext. */ -static void fts3SnippetAdvance(char **ppIter, int *piIter, int iNext){ +static void fts3SnippetAdvance(char **ppIter, i64 *piIter, int iNext){ char *pIter = *ppIter; if( pIter ){ - int iIter = *piIter; + i64 iIter = *piIter; while( iIteraPhrase[i]; if( pPhrase->pTail ){ char *pCsr = pPhrase->pTail; - int iCsr = pPhrase->iTail; + i64 iCsr = pPhrase->iTail; while( iCsr<(iStart+pIter->nSnippet) && iCsr>=iStart ){ int j; @@ -189170,7 +198805,7 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ rc = sqlcipher_sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pCsr); assert( rc==SQLITE_OK || pCsr==0 ); if( pCsr ){ - int iFirst = 0; + i64 iFirst = 0; pPhrase->pList = pCsr; fts3GetDeltaPosition(&pCsr, &iFirst); if( iFirst<0 ){ @@ -189235,11 +198870,10 @@ static int fts3BestSnippet( ** the required space using malloc(). */ nByte = sizeof(SnippetPhrase) * nList; - sIter.aPhrase = (SnippetPhrase *)sqlcipher_sqlite3_malloc64(nByte); + sIter.aPhrase = (SnippetPhrase *)sqlcipher_sqlite3Fts3MallocZero(nByte); if( !sIter.aPhrase ){ return SQLITE_NOMEM; } - memset(sIter.aPhrase, 0, nByte); /* Initialize the contents of the SnippetIter object. Then iterate through ** the set of phrases in the expression to populate the aPhrase[] array. @@ -189803,10 +199437,12 @@ static int fts3MatchinfoLcsCb( ** position list for the next column. */ static int fts3LcsIteratorAdvance(LcsIterator *pIter){ - char *pRead = pIter->pRead; + char *pRead; sqlcipher_sqlite3_int64 iRead; int rc = 0; + if( NEVER(pIter==0) ) return 1; + pRead = pIter->pRead; pRead += sqlcipher_sqlite3Fts3GetVarint(pRead, &iRead); if( iRead==0 || iRead==1 ){ pRead = 0; @@ -189840,9 +199476,8 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ /* Allocate and populate the array of LcsIterator objects. The array ** contains one element for each matchable phrase in the query. **/ - aIter = sqlcipher_sqlite3_malloc64(sizeof(LcsIterator) * pCsr->nPhrase); + aIter = sqlcipher_sqlite3Fts3MallocZero(sizeof(LcsIterator) * pCsr->nPhrase); if( !aIter ) return SQLITE_NOMEM; - memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase); (void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter); for(i=0; inPhrase; i++){ @@ -190234,8 +199869,8 @@ typedef struct TermOffsetCtx TermOffsetCtx; struct TermOffset { char *pList; /* Position-list */ - int iPos; /* Position just read from pList */ - int iOff; /* Offset of this term from read positions */ + i64 iPos; /* Position just read from pList */ + i64 iOff; /* Offset of this term from read positions */ }; struct TermOffsetCtx { @@ -190254,7 +199889,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ int nTerm; /* Number of tokens in phrase */ int iTerm; /* For looping through nTerm phrase terms */ char *pList; /* Pointer to position list for phrase */ - int iPos = 0; /* First position in position-list */ + i64 iPos = 0; /* First position in position-list */ int rc; UNUSED_PARAMETER(iPhrase); @@ -190303,7 +199938,7 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Fts3Offsets( if( rc!=SQLITE_OK ) goto offsets_out; /* Allocate the array of TermOffset iterators. */ - sCtx.aTerm = (TermOffset *)sqlcipher_sqlite3_malloc64(sizeof(TermOffset)*nToken); + sCtx.aTerm = (TermOffset *)sqlcipher_sqlite3Fts3MallocZero(sizeof(TermOffset)*nToken); if( 0==sCtx.aTerm ){ rc = SQLITE_NOMEM; goto offsets_out; @@ -190324,13 +199959,13 @@ SQLITE_PRIVATE void sqlcipher_sqlite3Fts3Offsets( const char *zDoc; int nDoc; - /* Initialize the contents of sCtx.aTerm[] for column iCol. There is - ** no way that this operation can fail, so the return code from - ** fts3ExprIterate() can be discarded. + /* Initialize the contents of sCtx.aTerm[] for column iCol. This + ** operation may fail if the database contains corrupt records. */ sCtx.iCol = iCol; sCtx.iTerm = 0; - (void)fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx); + rc = fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx); + if( rc!=SQLITE_OK ) goto offsets_out; /* Retreive the text stored in column iCol. If an SQL NULL is stored ** in column iCol, jump immediately to the next iteration of the loop. @@ -190731,6 +200366,7 @@ static int unicodeOpen( pCsr->aInput = (const unsigned char *)aInput; if( aInput==0 ){ pCsr->nInput = 0; + pCsr->aInput = (const unsigned char*)""; }else if( nInput<0 ){ pCsr->nInput = (int)strlen(aInput); }else{ @@ -191228,7 +200864,7 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ #endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */ /************** End of fts3_unicode2.c ***************************************/ -/************** Begin file json1.c *******************************************/ +/************** Begin file json.c ********************************************/ /* ** 2015-08-12 ** @@ -191241,10 +200877,10 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ ** ****************************************************************************** ** -** This SQLite extension implements JSON functions. The interface is -** modeled after MySQL JSON functions: +** This SQLite JSON functions. ** -** https://dev.mysql.com/doc/refman/5.7/en/json.html +** This file began as an extension in ext/misc/json1.c in 2015. That +** extension proved so useful that it has now been moved into the core. ** ** For the time being, all JSON is stored as pure text. (We might add ** a JSONB type in the future which stores a binary encoding of JSON in @@ -191252,48 +200888,8 @@ SQLITE_PRIVATE int sqlcipher_sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ ** This implementation parses JSON text at 250 MB/s, so it is hard to see ** how JSONB might improve on that.) */ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) -#if !defined(SQLITEINT_H) -/* #include "sqlcipher_sqlite3ext.h" */ -#endif -SQLITE_EXTENSION_INIT1 -/* #include */ -/* #include */ -/* #include */ -/* #include */ - -/* Mark a function parameter as unused, to suppress nuisance compiler -** warnings. */ -#ifndef UNUSED_PARAM -# define UNUSED_PARAM(X) (void)(X) -#endif - -#ifndef LARGEST_INT64 -# define LARGEST_INT64 (0xffffffff|(((sqlcipher_sqlite3_int64)0x7fffffff)<<32)) -# define SMALLEST_INT64 (((sqlcipher_sqlite3_int64)-1) - LARGEST_INT64) -#endif - -#ifndef deliberate_fall_through -# define deliberate_fall_through -#endif - -/* -** Versions of isspace(), isalnum() and isdigit() to which it is safe -** to pass signed char values. -*/ -#ifdef sqlcipher_sqlite3Isdigit - /* Use the SQLite core versions if this routine is part of the - ** SQLite amalgamation */ -# define safe_isdigit(x) sqlcipher_sqlite3Isdigit(x) -# define safe_isalnum(x) sqlcipher_sqlite3Isalnum(x) -# define safe_isxdigit(x) sqlcipher_sqlite3Isxdigit(x) -#else - /* Use the standard library for separate compilation */ -#include /* amalgamator: keep */ -# define safe_isdigit(x) isdigit((unsigned char)(x)) -# define safe_isalnum(x) isalnum((unsigned char)(x)) -# define safe_isxdigit(x) isxdigit((unsigned char)(x)) -#endif +#ifndef SQLITE_OMIT_JSON +/* #include "sqliteInt.h" */ /* ** Growing our own isspace() routine this way is twice as fast as @@ -191318,15 +200914,12 @@ static const char jsonIsSpace[] = { 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, }; -#define safe_isspace(x) (jsonIsSpace[(unsigned char)x]) +#define fast_isspace(x) (jsonIsSpace[(unsigned char)x]) -#ifndef SQLITE_AMALGAMATION - /* Unsigned integer types. These are already defined in the sqliteInt.h, - ** but the definitions need to be repeated for separate compilation. */ - typedef sqlcipher_sqlite3_uint64 u64; - typedef unsigned int u32; - typedef unsigned short int u16; - typedef unsigned char u8; +#if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST) +# define VVA(X) +#else +# define VVA(X) X #endif /* Objects */ @@ -191385,13 +200978,14 @@ static const char * const jsonType[] = { struct JsonNode { u8 eType; /* One of the JSON_ type values */ u8 jnFlags; /* JNODE flags */ + u8 eU; /* Which union element to use */ u32 n; /* Bytes of content, or number of sub-nodes */ union { - const char *zJContent; /* Content for INT, REAL, and STRING */ - u32 iAppend; /* More terms for ARRAY and OBJECT */ - u32 iKey; /* Key for ARRAY objects in json_tree() */ - u32 iReplace; /* Replacement content for JNODE_REPLACE */ - JsonNode *pPatch; /* Node chain of patch for JNODE_PATCH */ + const char *zJContent; /* 1: Content for INT, REAL, and STRING */ + u32 iAppend; /* 2: More terms for ARRAY and OBJECT */ + u32 iKey; /* 3: Key for ARRAY objects in json_tree() */ + u32 iReplace; /* 4: Replacement content for JNODE_REPLACE */ + JsonNode *pPatch; /* 5: Node chain of patch for JNODE_PATCH */ } u; }; @@ -191530,7 +201124,7 @@ static void jsonAppendSeparator(JsonString *p){ */ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ u32 i; - if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return; + if( zIn==0 || ((N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0) ) return; p->zBuf[p->nUsed++] = '"'; for(i=0; ijnFlags & (JNODE_REPLACE|JNODE_PATCH) ){ - if( pNode->jnFlags & JNODE_REPLACE ){ + if( (pNode->jnFlags & JNODE_REPLACE)!=0 && ALWAYS(aReplace!=0) ){ + assert( pNode->eU==4 ); jsonAppendValue(pOut, aReplace[pNode->u.iReplace]); return; } + assert( pNode->eU==5 ); pNode = pNode->u.pPatch; } switch( pNode->eType ){ @@ -191692,6 +201289,7 @@ static void jsonRenderNode( } case JSON_STRING: { if( pNode->jnFlags & JNODE_RAW ){ + assert( pNode->eU==1 ); jsonAppendString(pOut, pNode->u.zJContent, pNode->n); break; } @@ -191699,6 +201297,7 @@ static void jsonRenderNode( } case JSON_REAL: case JSON_INT: { + assert( pNode->eU==1 ); jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); break; } @@ -191714,6 +201313,7 @@ static void jsonRenderNode( j += jsonNodeSize(&pNode[j]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + assert( pNode->eU==2 ); pNode = &pNode[pNode->u.iAppend]; j = 1; } @@ -191734,6 +201334,7 @@ static void jsonRenderNode( j += 1 + jsonNodeSize(&pNode[j+1]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + assert( pNode->eU==2 ); pNode = &pNode[pNode->u.iAppend]; j = 1; } @@ -191778,10 +201379,10 @@ static u8 jsonHexToInt(int h){ */ static u32 jsonHexToInt4(const char *z){ u32 v; - assert( safe_isxdigit(z[0]) ); - assert( safe_isxdigit(z[1]) ); - assert( safe_isxdigit(z[2]) ); - assert( safe_isxdigit(z[3]) ); + assert( sqlcipher_sqlite3Isxdigit(z[0]) ); + assert( sqlcipher_sqlite3Isxdigit(z[1]) ); + assert( sqlcipher_sqlite3Isxdigit(z[2]) ); + assert( sqlcipher_sqlite3Isxdigit(z[3]) ); v = (jsonHexToInt(z[0])<<12) + (jsonHexToInt(z[1])<<8) + (jsonHexToInt(z[2])<<4) @@ -191813,7 +201414,9 @@ static void jsonReturn( } case JSON_INT: { sqlcipher_sqlite3_int64 i = 0; - const char *z = pNode->u.zJContent; + const char *z; + assert( pNode->eU==1 ); + z = pNode->u.zJContent; if( z[0]=='-' ){ z++; } while( z[0]>='0' && z[0]<='9' ){ unsigned v = *(z++) - '0'; @@ -191836,14 +201439,17 @@ static void jsonReturn( sqlcipher_sqlite3_result_int64(pCtx, i); int_done: break; - int_as_real: i=0; /* no break */ deliberate_fall_through + int_as_real: ; /* no break */ deliberate_fall_through } case JSON_REAL: { double r; #ifdef SQLITE_AMALGAMATION - const char *z = pNode->u.zJContent; + const char *z; + assert( pNode->eU==1 ); + z = pNode->u.zJContent; sqlcipher_sqlite3AtoF(z, &r, sqlcipher_sqlite3Strlen30(z), SQLITE_UTF8); #else + assert( pNode->eU==1 ); r = strtod(pNode->u.zJContent, 0); #endif sqlcipher_sqlite3_result_double(pCtx, r); @@ -191854,6 +201460,7 @@ static void jsonReturn( ** json_insert() and json_replace() and those routines do not ** call jsonReturn() */ if( pNode->jnFlags & JNODE_RAW ){ + assert( pNode->eU==1 ); sqlcipher_sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, SQLITE_TRANSIENT); }else @@ -191861,15 +201468,18 @@ static void jsonReturn( assert( (pNode->jnFlags & JNODE_RAW)==0 ); if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ /* JSON formatted without any backslash-escapes */ + assert( pNode->eU==1 ); sqlcipher_sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, SQLITE_TRANSIENT); }else{ /* Translate JSON formatted string into raw text */ u32 i; u32 n = pNode->n; - const char *z = pNode->u.zJContent; + const char *z; char *zOut; u32 j; + assert( pNode->eU==1 ); + z = pNode->u.zJContent; zOut = sqlcipher_sqlite3_malloc( n+1 ); if( zOut==0 ){ sqlcipher_sqlite3_result_error_nomem(pCtx); @@ -191990,12 +201600,13 @@ static int jsonParseAddNode( const char *zContent /* Content */ ){ JsonNode *p; - if( pParse->nNode>=pParse->nAlloc ){ + if( pParse->aNode==0 || pParse->nNode>=pParse->nAlloc ){ return jsonParseAddNodeExpand(pParse, eType, n, zContent); } p = &pParse->aNode[pParse->nNode]; p->eType = (u8)eType; p->jnFlags = 0; + VVA( p->eU = zContent ? 1 : 0 ); p->n = n; p->u.zJContent = zContent; return pParse->nNode++; @@ -192006,7 +201617,7 @@ static int jsonParseAddNode( */ static int jsonIs4Hex(const char *z){ int i; - for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0; + for(i=0; i<4; i++) if( !sqlcipher_sqlite3Isxdigit(z[i]) ) return 0; return 1; } @@ -192025,13 +201636,13 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ int x; JsonNode *pNode; const char *z = pParse->zJson; - while( safe_isspace(z[i]) ){ i++; } + while( fast_isspace(z[i]) ){ i++; } if( (c = z[i])=='{' ){ /* Parse object */ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); if( iThis<0 ) return -1; for(j=i+1;;j++){ - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); if( x<0 ){ @@ -192044,14 +201655,14 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ if( pNode->eType!=JSON_STRING ) return -1; pNode->jnFlags |= JNODE_LABEL; j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( z[j]!=':' ) return -1; j++; x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<0 ) return -1; j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } c = z[j]; if( c==',' ) continue; if( c!='}' ) return -1; @@ -192063,8 +201674,9 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ /* Parse array */ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); if( iThis<0 ) return -1; + memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); for(j=i+1;;j++){ - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); pParse->iDepth--; @@ -192073,7 +201685,7 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ return -1; } j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } c = z[j]; if( c==',' ) continue; if( c!=']' ) return -1; @@ -192110,17 +201722,17 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ return j+1; }else if( c=='n' && strncmp(z+i,"null",4)==0 - && !safe_isalnum(z[i+4]) ){ + && !sqlcipher_sqlite3Isalnum(z[i+4]) ){ jsonParseAddNode(pParse, JSON_NULL, 0, 0); return i+4; }else if( c=='t' && strncmp(z+i,"true",4)==0 - && !safe_isalnum(z[i+4]) ){ + && !sqlcipher_sqlite3Isalnum(z[i+4]) ){ jsonParseAddNode(pParse, JSON_TRUE, 0, 0); return i+4; }else if( c=='f' && strncmp(z+i,"false",5)==0 - && !safe_isalnum(z[i+5]) ){ + && !sqlcipher_sqlite3Isalnum(z[i+5]) ){ jsonParseAddNode(pParse, JSON_FALSE, 0, 0); return i+5; }else if( c=='-' || (c>='0' && c<='9') ){ @@ -192191,7 +201803,7 @@ static int jsonParse( if( pParse->oom ) i = -1; if( i>0 ){ assert( pParse->iDepth==0 ); - while( safe_isspace(zJson[i]) ) i++; + while( fast_isspace(zJson[i]) ) i++; if( zJson[i] ) i = -1; } if( i<=0 ){ @@ -192327,6 +201939,7 @@ static JsonParse *jsonParseCached( ** a match. */ static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ + assert( pNode->eU==1 ); if( pNode->jnFlags & JNODE_RAW ){ if( pNode->n!=nKey ) return 0; return strncmp(pNode->u.zJContent, zKey, nKey)==0; @@ -192373,14 +201986,15 @@ static JsonNode *jsonLookupStep( *pzErr = zPath; return 0; } + testcase( nKey==0 ); }else{ zKey = zPath; for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} nKey = i; - } - if( nKey==0 ){ - *pzErr = zPath; - return 0; + if( nKey==0 ){ + *pzErr = zPath; + return 0; + } } j = 1; for(;;){ @@ -192392,6 +202006,7 @@ static JsonNode *jsonLookupStep( j += jsonNodeSize(&pRoot[j]); } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + assert( pRoot->eU==2 ); iRoot += pRoot->u.iAppend; pRoot = &pParse->aNode[iRoot]; j = 1; @@ -192406,8 +202021,10 @@ static JsonNode *jsonLookupStep( if( pParse->oom ) return 0; if( pNode ){ pRoot = &pParse->aNode[iRoot]; + assert( pRoot->eU==0 ); pRoot->u.iAppend = iStart - iRoot; pRoot->jnFlags |= JNODE_APPEND; + VVA( pRoot->eU = 2 ); pParse->aNode[iLabel].jnFlags |= JNODE_RAW; } return pNode; @@ -192415,7 +202032,7 @@ static JsonNode *jsonLookupStep( }else if( zPath[0]=='[' ){ i = 0; j = 1; - while( safe_isdigit(zPath[j]) ){ + while( sqlcipher_sqlite3Isdigit(zPath[j]) ){ i = i*10 + zPath[j] - '0'; j++; } @@ -192430,18 +202047,19 @@ static JsonNode *jsonLookupStep( j += jsonNodeSize(&pBase[j]); } if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; + assert( pBase->eU==2 ); iBase += pBase->u.iAppend; pBase = &pParse->aNode[iBase]; j = 1; } j = 2; - if( zPath[2]=='-' && safe_isdigit(zPath[3]) ){ + if( zPath[2]=='-' && sqlcipher_sqlite3Isdigit(zPath[3]) ){ unsigned int x = 0; j = 3; do{ x = x*10 + zPath[j] - '0'; j++; - }while( safe_isdigit(zPath[j]) ); + }while( sqlcipher_sqlite3Isdigit(zPath[j]) ); if( x>i ) return 0; i -= x; } @@ -192463,6 +202081,7 @@ static JsonNode *jsonLookupStep( j += jsonNodeSize(&pRoot[j]); } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + assert( pRoot->eU==2 ); iRoot += pRoot->u.iAppend; pRoot = &pParse->aNode[iRoot]; j = 1; @@ -192478,8 +202097,10 @@ static JsonNode *jsonLookupStep( if( pParse->oom ) return 0; if( pNode ){ pRoot = &pParse->aNode[iRoot]; + assert( pRoot->eU==0 ); pRoot->u.iAppend = iStart - iRoot; pRoot->jnFlags |= JNODE_APPEND; + VVA( pRoot->eU = 2 ); } return pNode; } @@ -192633,9 +202254,13 @@ static void jsonParseFunc( } jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d", i, zType, x.aNode[i].n, x.aUp[i]); + assert( x.aNode[i].eU==0 || x.aNode[i].eU==1 ); if( x.aNode[i].u.zJContent!=0 ){ + assert( x.aNode[i].eU==1 ); jsonAppendRaw(&s, " ", 1); jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n); + }else{ + assert( x.aNode[i].eU==0 ); } jsonAppendRaw(&s, "\n", 1); } @@ -192653,7 +202278,7 @@ static void jsonTest1Func( int argc, sqlcipher_sqlite3_value **argv ){ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); sqlcipher_sqlite3_result_int(ctx, sqlcipher_sqlite3_value_subtype(argv[0])==JSON_SUBTYPE); } #endif /* SQLITE_DEBUG */ @@ -192674,7 +202299,7 @@ static void jsonQuoteFunc( sqlcipher_sqlite3_value **argv ){ JsonString jx; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); jsonInit(&jx, ctx); jsonAppendValue(&jx, argv[0]); @@ -192745,13 +202370,34 @@ static void jsonArrayLengthFunc( sqlcipher_sqlite3_result_int64(ctx, n); } +/* +** Bit values for the flags passed into jsonExtractFunc() or +** jsonSetFunc() via the user-data value. +*/ +#define JSON_JSON 0x01 /* Result is always JSON */ +#define JSON_SQL 0x02 /* Result is always SQL */ +#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ +#define JSON_ISSET 0x04 /* json_set(), not json_insert() */ + /* ** json_extract(JSON, PATH, ...) +** "->"(JSON,PATH) +** "->>"(JSON,PATH) +** +** Return the element described by PATH. Return NULL if that PATH element +** is not found. +** +** If JSON_JSON is set or if more that one PATH argument is supplied then +** always return a JSON representation of the result. If JSON_SQL is set, +** then always return an SQL representation of the result. If neither flag +** is present and argc==2, then return JSON for objects and arrays and SQL +** for all other values. ** -** Return the element described by PATH. Return NULL if there is no -** PATH element. If there are multiple PATHs, then return a JSON array -** with the result from each path. Throw an error if the JSON or any PATH -** is malformed. +** When multiple PATH arguments are supplied, the result is a JSON array +** containing the result of each PATH. +** +** Abbreviated JSON path expressions are allows if JSON_ABPATH, for +** compatibility with PG. */ static void jsonExtractFunc( sqlcipher_sqlite3_context *ctx, @@ -192761,35 +202407,77 @@ static void jsonExtractFunc( JsonParse *p; /* The parse */ JsonNode *pNode; const char *zPath; + int flags = SQLITE_PTR_TO_INT(sqlcipher_sqlite3_user_data(ctx)); JsonString jx; - int i; if( argc<2 ) return; p = jsonParseCached(ctx, argv, ctx); if( p==0 ) return; - jsonInit(&jx, ctx); - jsonAppendChar(&jx, '['); - for(i=1; inErr ) break; - if( argc>2 ){ + if( argc==2 ){ + /* With a single PATH argument */ + zPath = (const char*)sqlcipher_sqlite3_value_text(argv[1]); + if( zPath==0 ) return; + if( flags & JSON_ABPATH ){ + if( zPath[0]!='$' ){ + /* The -> and ->> operators accept abbreviated PATH arguments. This + ** is mostly for compatibility with PostgreSQL, but also for + ** convenience. + ** + ** NUMBER ==> $[NUMBER] // PG compatible + ** LABEL ==> $.LABEL // PG compatible + ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience + */ + jsonInit(&jx, ctx); + if( sqlcipher_sqlite3Isdigit(zPath[0]) ){ + jsonAppendRaw(&jx, "$[", 2); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendRaw(&jx, "]", 2); + }else{ + jsonAppendRaw(&jx, "$.", 1 + (zPath[0]!='[')); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendChar(&jx, 0); + } + pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx); + jsonReset(&jx); + }else{ + pNode = jsonLookup(p, zPath, 0, ctx); + } + if( pNode ){ + if( flags & JSON_JSON ){ + jsonReturnJson(pNode, ctx, 0); + }else{ + jsonReturn(pNode, ctx, 0); + sqlcipher_sqlite3_result_subtype(ctx, 0); + } + } + }else{ + pNode = jsonLookup(p, zPath, 0, ctx); + if( p->nErr==0 && pNode ) jsonReturn(pNode, ctx, 0); + } + }else{ + /* Two or more PATH arguments results in a JSON array with each + ** element of the array being the value selected by one of the PATHs */ + int i; + jsonInit(&jx, ctx); + jsonAppendChar(&jx, '['); + for(i=1; inErr ) break; jsonAppendSeparator(&jx); if( pNode ){ jsonRenderNode(pNode, &jx, 0); }else{ jsonAppendRaw(&jx, "null", 4); } - }else if( pNode ){ - jsonReturn(pNode, ctx, 0); } + if( i==argc ){ + jsonAppendChar(&jx, ']'); + jsonResult(&jx); + sqlcipher_sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } + jsonReset(&jx); } - if( argc>2 && i==argc ){ - jsonAppendChar(&jx, ']'); - jsonResult(&jx); - sqlcipher_sqlite3_result_subtype(ctx, JSON_SUBTYPE); - } - jsonReset(&jx); } /* This is the RFC 7396 MergePatch algorithm. @@ -192805,7 +202493,7 @@ static JsonNode *jsonMergePatch( if( pPatch->eType!=JSON_OBJECT ){ return pPatch; } - assert( iTarget>=0 && iTargetnNode ); + assert( iTargetnNode ); pTarget = &pParse->aNode[iTarget]; assert( (pPatch->jnFlags & JNODE_APPEND)==0 ); if( pTarget->eType!=JSON_OBJECT ){ @@ -192818,6 +202506,7 @@ static JsonNode *jsonMergePatch( const char *zKey; assert( pPatch[i].eType==JSON_STRING ); assert( pPatch[i].jnFlags & JNODE_LABEL ); + assert( pPatch[i].eU==1 ); nKey = pPatch[i].n; zKey = pPatch[i].u.zJContent; assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); @@ -192834,6 +202523,12 @@ static JsonNode *jsonMergePatch( if( pNew==0 ) return 0; pTarget = &pParse->aNode[iTarget]; if( pNew!=&pTarget[j+1] ){ + assert( pTarget[j+1].eU==0 + || pTarget[j+1].eU==1 + || pTarget[j+1].eU==2 ); + testcase( pTarget[j+1].eU==1 ); + testcase( pTarget[j+1].eU==2 ); + VVA( pTarget[j+1].eU = 5 ); pTarget[j+1].u.pPatch = pNew; pTarget[j+1].jnFlags |= JNODE_PATCH; } @@ -192849,9 +202544,14 @@ static JsonNode *jsonMergePatch( if( pParse->oom ) return 0; jsonRemoveAllNulls(pPatch); pTarget = &pParse->aNode[iTarget]; + assert( pParse->aNode[iRoot].eU==0 || pParse->aNode[iRoot].eU==2 ); + testcase( pParse->aNode[iRoot].eU==2 ); pParse->aNode[iRoot].jnFlags |= JNODE_APPEND; + VVA( pParse->aNode[iRoot].eU = 2 ); pParse->aNode[iRoot].u.iAppend = iStart - iRoot; iRoot = iStart; + assert( pParse->aNode[iPatch].eU==0 ); + VVA( pParse->aNode[iPatch].eU = 5 ); pParse->aNode[iPatch].jnFlags |= JNODE_PATCH; pParse->aNode[iPatch].u.pPatch = &pPatch[i+1]; } @@ -192873,7 +202573,7 @@ static void jsonPatchFunc( JsonParse y; /* The patch */ JsonNode *pResult; /* The result of the merge */ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); if( jsonParse(&x, ctx, (const char*)sqlcipher_sqlite3_value_text(argv[0])) ) return; if( jsonParse(&y, ctx, (const char*)sqlcipher_sqlite3_value_text(argv[1])) ){ jsonParseReset(&x); @@ -192993,11 +202693,15 @@ static void jsonReplaceFunc( pNode = jsonLookup(&x, zPath, 0, ctx); if( x.nErr ) goto replace_err; if( pNode ){ + assert( pNode->eU==0 || pNode->eU==1 || pNode->eU==4 ); + testcase( pNode->eU!=0 && pNode->eU!=1 ); pNode->jnFlags |= (u8)JNODE_REPLACE; + VVA( pNode->eU = 4 ); pNode->u.iReplace = i + 1; } } if( x.aNode[0].jnFlags & JNODE_REPLACE ){ + assert( x.aNode[0].eU==4 ); sqlcipher_sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); }else{ jsonReturnJson(x.aNode, ctx, argv); @@ -193006,6 +202710,7 @@ replace_err: jsonParseReset(&x); } + /* ** json_set(JSON, PATH, VALUE, ...) ** @@ -193028,7 +202733,7 @@ static void jsonSetFunc( const char *zPath; u32 i; int bApnd; - int bIsSet = *(int*)sqlcipher_sqlite3_user_data(ctx); + int bIsSet = sqlcipher_sqlite3_user_data(ctx)!=0; if( argc<1 ) return; if( (argc&1)==0 ) { @@ -193047,11 +202752,15 @@ static void jsonSetFunc( }else if( x.nErr ){ goto jsonSetDone; }else if( pNode && (bApnd || bIsSet) ){ + testcase( pNode->eU!=0 && pNode->eU!=1 ); + assert( pNode->eU!=3 && pNode->eU!=5 ); + VVA( pNode->eU = 4 ); pNode->jnFlags |= (u8)JNODE_REPLACE; pNode->u.iReplace = i + 1; } } if( x.aNode[0].jnFlags & JNODE_REPLACE ){ + assert( x.aNode[0].eU==4 ); sqlcipher_sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); }else{ jsonReturnJson(x.aNode, ctx, argv); @@ -193064,8 +202773,8 @@ jsonSetDone: ** json_type(JSON) ** json_type(JSON, PATH) ** -** Return the top-level "type" of a JSON string. Throw an error if -** either the JSON or PATH inputs are not well-formed. +** Return the top-level "type" of a JSON string. json_type() raises an +** error if either the JSON or PATH inputs are not well-formed. */ static void jsonTypeFunc( sqlcipher_sqlite3_context *ctx, @@ -193101,7 +202810,7 @@ static void jsonValidFunc( sqlcipher_sqlite3_value **argv ){ JsonParse *p; /* The parse */ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); p = jsonParseCached(ctx, argv, 0); sqlcipher_sqlite3_result_int(ctx, p!=0); } @@ -193121,7 +202830,7 @@ static void jsonArrayStep( sqlcipher_sqlite3_value **argv ){ JsonString *pStr; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); pStr = (JsonString*)sqlcipher_sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ @@ -193129,8 +202838,8 @@ static void jsonArrayStep( jsonAppendChar(pStr, '['); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); - pStr->pCtx = ctx; } + pStr->pCtx = ctx; jsonAppendValue(pStr, argv[0]); } } @@ -193181,8 +202890,8 @@ static void jsonGroupInverse( char *z; char c; JsonString *pStr; - UNUSED_PARAM(argc); - UNUSED_PARAM(argv); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); pStr = (JsonString*)sqlcipher_sqlite3_aggregate_context(ctx, 0); #ifdef NEVER /* pStr is always non-NULL since jsonArrayStep() or jsonObjectStep() will @@ -193190,11 +202899,7 @@ static void jsonGroupInverse( if( NEVER(!pStr) ) return; #endif z = pStr->zBuf; - for(i=1; (c = z[i])!=',' || inStr || nNest; i++){ - if( i>=pStr->nUsed ){ - pStr->nUsed = 1; - return; - } + for(i=1; inUsed && ((c = z[i])!=',' || inStr || nNest); i++){ if( c=='"' ){ inStr = !inStr; }else if( c=='\\' ){ @@ -193204,8 +202909,13 @@ static void jsonGroupInverse( if( c=='}' || c==']' ) nNest--; } } - pStr->nUsed -= i; - memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1); + if( inUsed ){ + pStr->nUsed -= i; + memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1); + z[pStr->nUsed] = 0; + }else{ + pStr->nUsed = 1; + } } #else # define jsonGroupInverse 0 @@ -193225,7 +202935,7 @@ static void jsonObjectStep( JsonString *pStr; const char *z; u32 n; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); pStr = (JsonString*)sqlcipher_sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ @@ -193233,8 +202943,8 @@ static void jsonObjectStep( jsonAppendChar(pStr, '{'); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); - pStr->pCtx = ctx; } + pStr->pCtx = ctx; z = (const char*)sqlcipher_sqlite3_value_text(argv[0]); n = (u32)sqlcipher_sqlite3_value_bytes(argv[0]); jsonAppendString(pStr, z, n); @@ -193316,10 +203026,10 @@ static int jsonEachConnect( #define JEACH_JSON 8 #define JEACH_ROOT 9 - UNUSED_PARAM(pzErr); - UNUSED_PARAM(argv); - UNUSED_PARAM(argc); - UNUSED_PARAM(pAux); + UNUSED_PARAMETER(pzErr); + UNUSED_PARAMETER(argv); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(pAux); rc = sqlcipher_sqlite3_declare_vtab(db, "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path," "json HIDDEN,root HIDDEN)"); @@ -193342,7 +203052,7 @@ static int jsonEachDisconnect(sqlcipher_sqlite3_vtab *pVtab){ static int jsonEachOpenEach(sqlcipher_sqlite3_vtab *p, sqlcipher_sqlite3_vtab_cursor **ppCursor){ JsonEachCursor *pCur; - UNUSED_PARAM(p); + UNUSED_PARAMETER(p); pCur = sqlcipher_sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); @@ -193401,6 +203111,9 @@ static int jsonEachNext(sqlcipher_sqlite3_vtab_cursor *cur){ JsonNode *pUp = &p->sParse.aNode[iUp]; p->eType = pUp->eType; if( pUp->eType==JSON_ARRAY ){ + assert( pUp->eU==0 || pUp->eU==3 ); + testcase( pUp->eU==3 ); + VVA( pUp->eU = 3 ); if( iUp==p->i-1 ){ pUp->u.iKey = 0; }else{ @@ -193429,6 +203142,33 @@ static int jsonEachNext(sqlcipher_sqlite3_vtab_cursor *cur){ return SQLITE_OK; } +/* Append an object label to the JSON Path being constructed +** in pStr. +*/ +static void jsonAppendObjectPathElement( + JsonString *pStr, + JsonNode *pNode +){ + int jj, nn; + const char *z; + assert( pNode->eType==JSON_STRING ); + assert( pNode->jnFlags & JNODE_LABEL ); + assert( pNode->eU==1 ); + z = pNode->u.zJContent; + nn = pNode->n; + assert( nn>=2 ); + assert( z[0]=='"' ); + assert( z[nn-1]=='"' ); + if( nn>2 && sqlcipher_sqlite3Isalpha(z[1]) ){ + for(jj=2; jjsParse.aNode[i]; pUp = &p->sParse.aNode[iUp]; if( pUp->eType==JSON_ARRAY ){ + assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) ); + testcase( pUp->eU==0 ); jsonPrintf(30, pStr, "[%d]", pUp->u.iKey); }else{ assert( pUp->eType==JSON_OBJECT ); if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--; - assert( pNode->eType==JSON_STRING ); - assert( pNode->jnFlags & JNODE_LABEL ); - jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1); + jsonAppendObjectPathElement(pStr, pNode); } } @@ -193474,6 +203214,7 @@ static int jsonEachColumn( u32 iKey; if( p->bRecursive ){ if( p->iRowid==0 ) break; + assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 ); iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey; }else{ iKey = p->iRowid; @@ -193523,7 +203264,7 @@ static int jsonEachColumn( if( p->eType==JSON_ARRAY ){ jsonPrintf(30, &x, "[%d]", p->iRowid); }else if( p->eType==JSON_OBJECT ){ - jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1); + jsonAppendObjectPathElement(&x, pThis); } } jsonResult(&x); @@ -193581,7 +203322,7 @@ static int jsonEachBestIndex( /* This implementation assumes that JSON and ROOT are the last two ** columns in the table */ assert( JEACH_ROOT == JEACH_JSON+1 ); - UNUSED_PARAM(tab); + UNUSED_PARAMETER(tab); aIdx[0] = aIdx[1] = -1; pConstraint = pIdxInfo->aConstraint; for(i=0; inConstraint; i++, pConstraint++){ @@ -193590,6 +203331,7 @@ static int jsonEachBestIndex( if( pConstraint->iColumn < JEACH_JSON ) continue; iCol = pConstraint->iColumn - JEACH_JSON; assert( iCol==0 || iCol==1 ); + testcase( iCol==0 ); iMask = 1 << iCol; if( pConstraint->usable==0 ){ unusableMask |= iMask; @@ -193636,8 +203378,8 @@ static int jsonEachFilter( const char *zRoot = 0; sqlcipher_sqlite3_int64 n; - UNUSED_PARAM(idxStr); - UNUSED_PARAM(argc); + UNUSED_PARAMETER(idxStr); + UNUSED_PARAMETER(argc); jsonEachCursorReset(p); if( idxNum==0 ) return SQLITE_OK; z = (const char*)sqlcipher_sqlite3_value_text(argv[0]); @@ -193687,6 +203429,8 @@ static int jsonEachFilter( p->iBegin = p->i = (int)(pNode - p->sParse.aNode); p->eType = pNode->eType; if( p->eType>=JSON_ARRAY ){ + assert( pNode->eU==0 ); + VVA( pNode->eU = 3 ); pNode->u.iKey = 0; p->iEnd = p->i + pNode->n + 1; if( p->bRecursive ){ @@ -193760,108 +203504,68 @@ static sqlcipher_sqlite3_module jsonTreeModule = { 0 /* xShadowName */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ - -/**************************************************************************** -** The following routines are the only publically visible identifiers in this -** file. Call the following routines in order to register the various SQL -** functions and the virtual table implemented by this file. -****************************************************************************/ - -SQLITE_PRIVATE int sqlcipher_sqlite3Json1Init(sqlcipher_sqlite3 *db){ - int rc = SQLITE_OK; - unsigned int i; - static const struct { - const char *zName; - int nArg; - int flag; - void (*xFunc)(sqlcipher_sqlite3_context*,int,sqlcipher_sqlite3_value**); - } aFunc[] = { - { "json", 1, 0, jsonRemoveFunc }, - { "json_array", -1, 0, jsonArrayFunc }, - { "json_array_length", 1, 0, jsonArrayLengthFunc }, - { "json_array_length", 2, 0, jsonArrayLengthFunc }, - { "json_extract", -1, 0, jsonExtractFunc }, - { "json_insert", -1, 0, jsonSetFunc }, - { "json_object", -1, 0, jsonObjectFunc }, - { "json_patch", 2, 0, jsonPatchFunc }, - { "json_quote", 1, 0, jsonQuoteFunc }, - { "json_remove", -1, 0, jsonRemoveFunc }, - { "json_replace", -1, 0, jsonReplaceFunc }, - { "json_set", -1, 1, jsonSetFunc }, - { "json_type", 1, 0, jsonTypeFunc }, - { "json_type", 2, 0, jsonTypeFunc }, - { "json_valid", 1, 0, jsonValidFunc }, - +#endif /* !defined(SQLITE_OMIT_JSON) */ + +/* +** Register JSON functions. +*/ +SQLITE_PRIVATE void sqlcipher_sqlite3RegisterJsonFunctions(void){ +#ifndef SQLITE_OMIT_JSON + static FuncDef aJsonFunc[] = { + JFUNCTION(json, 1, 0, jsonRemoveFunc), + JFUNCTION(json_array, -1, 0, jsonArrayFunc), + JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), + JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), + JFUNCTION(json_extract, -1, 0, jsonExtractFunc), + JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), + JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), + JFUNCTION(json_insert, -1, 0, jsonSetFunc), + JFUNCTION(json_object, -1, 0, jsonObjectFunc), + JFUNCTION(json_patch, 2, 0, jsonPatchFunc), + JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), + JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), + JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), + JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc), + JFUNCTION(json_type, 1, 0, jsonTypeFunc), + JFUNCTION(json_type, 2, 0, jsonTypeFunc), + JFUNCTION(json_valid, 1, 0, jsonValidFunc), #if SQLITE_DEBUG - /* DEBUG and TESTING functions */ - { "json_parse", 1, 0, jsonParseFunc }, - { "json_test1", 1, 0, jsonTest1Func }, -#endif + JFUNCTION(json_parse, 1, 0, jsonParseFunc), + JFUNCTION(json_test1, 1, 0, jsonTest1Func), +#endif + WAGGREGATE(json_group_array, 1, 0, 0, + jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS), + WAGGREGATE(json_group_object, 2, 0, 0, + jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS) }; + sqlcipher_sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc)); +#endif +} + +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) +/* +** Register the JSON table-valued functions +*/ +SQLITE_PRIVATE int sqlcipher_sqlite3JsonTableFunctions(sqlcipher_sqlite3 *db){ + int rc = SQLITE_OK; static const struct { - const char *zName; - int nArg; - void (*xStep)(sqlcipher_sqlite3_context*,int,sqlcipher_sqlite3_value**); - void (*xFinal)(sqlcipher_sqlite3_context*); - void (*xValue)(sqlcipher_sqlite3_context*); - } aAgg[] = { - { "json_group_array", 1, - jsonArrayStep, jsonArrayFinal, jsonArrayValue }, - { "json_group_object", 2, - jsonObjectStep, jsonObjectFinal, jsonObjectValue }, - }; -#ifndef SQLITE_OMIT_VIRTUALTABLE - static const struct { - const char *zName; - sqlcipher_sqlite3_module *pModule; + const char *zName; + sqlcipher_sqlite3_module *pModule; } aMod[] = { { "json_each", &jsonEachModule }, { "json_tree", &jsonTreeModule }, }; -#endif - static const int enc = - SQLITE_UTF8 | - SQLITE_DETERMINISTIC | - SQLITE_INNOCUOUS; - for(i=0; i */ /* #include */ @@ -194000,7 +203721,9 @@ struct Rtree { u8 nBytesPerCell; /* Bytes consumed per cell */ u8 inWrTrans; /* True if inside write transaction */ u8 nAux; /* # of auxiliary columns in %_rowid */ +#ifdef SQLITE_ENABLE_GEOPOLY u8 nAuxNotNull; /* Number of initial not-null aux columns */ +#endif #ifdef SQLITE_DEBUG u8 bCorrupt; /* Shadow table corruption detected */ #endif @@ -194282,7 +204005,12 @@ struct RtreeMatchArg { ** it is not, make it a no-op. */ #ifndef SQLITE_AMALGAMATION -# define testcase(X) +# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) + unsigned int sqlcipher_sqlite3RtreeTestcase = 0; +# define testcase(X) if( X ){ sqlcipher_sqlite3RtreeTestcase += __LINE__; } +# else +# define testcase(X) +# endif #endif /* @@ -194531,18 +204259,6 @@ static void nodeBlobReset(Rtree *pRtree){ } } -/* -** Check to see if pNode is the same as pParent or any of the parents -** of pParent. -*/ -static int nodeInParentChain(const RtreeNode *pNode, const RtreeNode *pParent){ - do{ - if( pNode==pParent ) return 1; - pParent = pParent->pParent; - }while( pParent ); - return 0; -} - /* ** Obtain a reference to an r-tree node. */ @@ -194559,14 +204275,7 @@ static int nodeAcquire( ** increase its reference count and return it. */ if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){ - if( pParent && !pNode->pParent ){ - if( nodeInParentChain(pNode, pParent) ){ - RTREE_IS_CORRUPT(pRtree); - return SQLITE_CORRUPT_VTAB; - } - pParent->nRef++; - pNode->pParent = pParent; - }else if( pParent && pNode->pParent && pParent!=pNode->pParent ){ + if( pParent && pParent!=pNode->pParent ){ RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -194624,7 +204333,7 @@ static int nodeAcquire( ** are the leaves, and so on. If the depth as specified on the root node ** is greater than RTREE_MAX_DEPTH, the r-tree structure must be corrupt. */ - if( pNode && rc==SQLITE_OK && iNode==1 ){ + if( rc==SQLITE_OK && pNode && iNode==1 ){ pRtree->iDepth = readInt16(pNode->zData); if( pRtree->iDepth>RTREE_MAX_DEPTH ){ rc = SQLITE_CORRUPT_VTAB; @@ -195147,20 +204856,29 @@ static void rtreeNonleafConstraint( switch( p->op ){ case RTREE_TRUE: return; /* Always satisfied */ case RTREE_FALSE: break; /* Never satisfied */ + case RTREE_EQ: + RTREE_DECODE_COORD(eInt, pCellData, val); + /* val now holds the lower bound of the coordinate pair */ + if( p->u.rValue>=val ){ + pCellData += 4; + RTREE_DECODE_COORD(eInt, pCellData, val); + /* val now holds the upper bound of the coordinate pair */ + if( p->u.rValue<=val ) return; + } + break; case RTREE_LE: case RTREE_LT: - case RTREE_EQ: RTREE_DECODE_COORD(eInt, pCellData, val); /* val now holds the lower bound of the coordinate pair */ if( p->u.rValue>=val ) return; - if( p->op!=RTREE_EQ ) break; /* RTREE_LE and RTREE_LT end here */ - /* Fall through for the RTREE_EQ case */ + break; - default: /* RTREE_GT or RTREE_GE, or fallthrough of RTREE_EQ */ + default: pCellData += 4; RTREE_DECODE_COORD(eInt, pCellData, val); /* val now holds the upper bound of the coordinate pair */ if( p->u.rValue<=val ) return; + break; } *peWithin = NOT_WITHIN; } @@ -195230,11 +204948,12 @@ static int nodeRowidIndex( */ static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){ RtreeNode *pParent = pNode->pParent; - if( pParent ){ + if( ALWAYS(pParent) ){ return nodeRowidIndex(pRtree, pParent, pNode->iNode, piIndex); + }else{ + *piIndex = -1; + return SQLITE_OK; } - *piIndex = -1; - return SQLITE_OK; } /* @@ -195357,7 +205076,8 @@ static RtreeSearchPoint *rtreeSearchPointNew( pNew = rtreeEnqueue(pCur, rScore, iLevel); if( pNew==0 ) return 0; ii = (int)(pNew - pCur->aPoint) + 1; - if( iiaNode[ii]==0 ); pCur->aNode[ii] = pCur->aNode[0]; }else{ @@ -195418,7 +205138,7 @@ static void rtreeSearchPointPop(RtreeCursor *p){ if( p->bPoint ){ p->anQueue[p->sPoint.iLevel]--; p->bPoint = 0; - }else if( p->nPoint ){ + }else if( ALWAYS(p->nPoint) ){ p->anQueue[p->aPoint[0].iLevel]--; n = --p->nPoint; p->aPoint[0] = p->aPoint[n]; @@ -195559,7 +205279,7 @@ static int rtreeRowid(sqlcipher_sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 * RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); - if( rc==SQLITE_OK && p ){ + if( rc==SQLITE_OK && ALWAYS(p) ){ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); } return rc; @@ -195577,7 +205297,7 @@ static int rtreeColumn(sqlcipher_sqlite3_vtab_cursor *cur, sqlcipher_sqlite3_con RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc ) return rc; - if( p==0 ) return SQLITE_OK; + if( NEVER(p==0) ) return SQLITE_OK; if( i==0 ){ sqlcipher_sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); }else if( i<=pRtree->nDim2 ){ @@ -195776,8 +205496,11 @@ static int rtreeFilter( } if( rc==SQLITE_OK ){ RtreeSearchPoint *pNew; + assert( pCsr->bPoint==0 ); /* Due to the resetCursor() call above */ pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1)); - if( pNew==0 ) return SQLITE_NOMEM; + if( NEVER(pNew==0) ){ /* Because pCsr->bPoint was FALSE */ + return SQLITE_NOMEM; + } pNew->id = 1; pNew->iCell = 0; pNew->eWithin = PARTLY_WITHIN; @@ -195854,7 +205577,7 @@ static int rtreeBestIndex(sqlcipher_sqlite3_vtab *tab, sqlcipher_sqlite3_index_i struct sqlcipher_sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; if( bMatch==0 && p->usable - && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ + && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){ /* We have an equality constraint on the rowid. Use strategy 1. */ int jj; @@ -196060,7 +205783,7 @@ static int ChooseLeaf( int nCell = NCELL(pNode); RtreeCell cell; - RtreeNode *pChild; + RtreeNode *pChild = 0; RtreeCell *aCell = 0; @@ -196107,12 +205830,19 @@ static int AdjustTree( ){ RtreeNode *p = pNode; int cnt = 0; + int rc; while( p->pParent ){ RtreeNode *pParent = p->pParent; RtreeCell cell; int iCell; - if( (++cnt)>1000 || nodeParentIndex(pRtree, p, &iCell) ){ + cnt++; + if( NEVER(cnt>100) ){ + RTREE_IS_CORRUPT(pRtree); + return SQLITE_CORRUPT_VTAB; + } + rc = nodeParentIndex(pRtree, p, &iCell); + if( NEVER(rc!=SQLITE_OK) ){ RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -196401,12 +206131,17 @@ static int updateMapping( xSetMapping = ((iHeight==0)?rowidWrite:parentWrite); if( iHeight>0 ){ RtreeNode *pChild = nodeHashLookup(pRtree, iRowid); + RtreeNode *p; + for(p=pNode; p; p=p->pParent){ + if( p==pChild ) return SQLITE_CORRUPT_VTAB; + } if( pChild ){ nodeRelease(pRtree, pChild->pParent); nodeReference(pNode); pChild->pParent = pNode; } } + if( NEVER(pNode==0) ) return SQLITE_ERROR; return xSetMapping(pRtree, iRowid, pNode->iNode); } @@ -196496,11 +206231,12 @@ static int SplitNode( RtreeNode *pParent = pLeft->pParent; int iCell; rc = nodeParentIndex(pRtree, pLeft, &iCell); - if( rc==SQLITE_OK ){ + if( ALWAYS(rc==SQLITE_OK) ){ nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell); rc = AdjustTree(pRtree, pParent, &leftbbox); + assert( rc==SQLITE_OK ); } - if( rc!=SQLITE_OK ){ + if( NEVER(rc!=SQLITE_OK) ){ goto splitnode_out; } } @@ -196575,7 +206311,7 @@ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ */ iNode = sqlcipher_sqlite3_column_int64(pRtree->pReadParent, 0); for(pTest=pLeaf; pTest && pTest->iNode!=iNode; pTest=pTest->pParent); - if( !pTest ){ + if( pTest==0 ){ rc2 = nodeAcquire(pRtree, iNode, 0, &pChild->pParent); } } @@ -196606,6 +206342,7 @@ static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){ pParent = pNode->pParent; pNode->pParent = 0; rc = deleteCell(pRtree, pParent, iCell, iHeight+1); + testcase( rc!=SQLITE_OK ); } rc2 = nodeRelease(pRtree, pParent); if( rc==SQLITE_OK ){ @@ -196828,7 +206565,7 @@ static int rtreeInsertCell( } }else{ rc = AdjustTree(pRtree, pNode, pCell); - if( rc==SQLITE_OK ){ + if( ALWAYS(rc==SQLITE_OK) ){ if( iHeight==0 ){ rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode); }else{ @@ -196934,7 +206671,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlcipher_sqlite3_int64 iDelete){ int rc2; RtreeNode *pChild = 0; i64 iChild = nodeGetRowid(pRtree, pRoot, 0); - rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); + rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); /* tag-20210916a */ if( rc==SQLITE_OK ){ rc = removeNode(pRtree, pChild, pRtree->iDepth-1); } @@ -197269,7 +207006,7 @@ static int rtreeQueryStat1(sqlcipher_sqlite3 *db, Rtree *pRtree){ char *zSql; sqlcipher_sqlite3_stmt *p; int rc; - i64 nRow = 0; + i64 nRow = RTREE_MIN_ROWEST; rc = sqlcipher_sqlite3_table_column_metadata( db, pRtree->zDb, "sqlite_stat1",0,0,0,0,0,0 @@ -197286,20 +207023,10 @@ static int rtreeQueryStat1(sqlcipher_sqlite3 *db, Rtree *pRtree){ if( rc==SQLITE_OK ){ if( sqlcipher_sqlite3_step(p)==SQLITE_ROW ) nRow = sqlcipher_sqlite3_column_int64(p, 0); rc = sqlcipher_sqlite3_finalize(p); - }else if( rc!=SQLITE_NOMEM ){ - rc = SQLITE_OK; - } - - if( rc==SQLITE_OK ){ - if( nRow==0 ){ - pRtree->nRowEst = RTREE_DEFAULT_ROWEST; - }else{ - pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); - } } sqlcipher_sqlite3_free(zSql); } - + pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); return rc; } @@ -197449,9 +207176,12 @@ static int rtreeSqlInit( sqlcipher_sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix); for(ii=0; iinAux; ii++){ if( ii ) sqlcipher_sqlite3_str_append(p, ",", 1); +#ifdef SQLITE_ENABLE_GEOPOLY if( iinAuxNotNull ){ sqlcipher_sqlite3_str_appendf(p,"a%d=coalesce(?%d,a%d)",ii,ii+2,ii); - }else{ + }else +#endif + { sqlcipher_sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2); } } @@ -197716,6 +207446,7 @@ static void rtreenode(sqlcipher_sqlite3_context *ctx, int nArg, sqlcipher_sqlite tree.nDim2 = tree.nDim*2; tree.nBytesPerCell = 8 + 8 * tree.nDim; node.zData = (u8 *)sqlcipher_sqlite3_value_blob(apArg[1]); + if( node.zData==0 ) return; nData = sqlcipher_sqlite3_value_bytes(apArg[1]); if( nData<4 ) return; if( nDataz[0]) ) p->z++; + while( fast_isspace(p->z[0]) ) p->z++; return p->z[0]; } @@ -198540,11 +208275,16 @@ static GeoPoly *geopolyFuncParam( ){ GeoPoly *p = 0; int nByte; + testcase( pCtx==0 ); if( sqlcipher_sqlite3_value_type(pVal)==SQLITE_BLOB && (nByte = sqlcipher_sqlite3_value_bytes(pVal))>=(4+6*sizeof(GeoCoord)) ){ const unsigned char *a = sqlcipher_sqlite3_value_blob(pVal); int nVertex; + if( a==0 ){ + if( pCtx ) sqlcipher_sqlite3_result_error_nomem(pCtx); + return 0; + } nVertex = (a[1]<<16) + (a[2]<<8) + a[3]; if( (a[0]==0 || a[0]==1) && (nVertex*2*sizeof(GeoCoord) + 4)==(unsigned int)nByte @@ -198918,7 +208658,7 @@ static GeoPoly *geopolyBBox( aCoord[2].f = mnY; aCoord[3].f = mxY; } - }else{ + }else if( aCoord ){ memset(aCoord, 0, sizeof(RtreeCoord)*4); } return pOut; @@ -199369,11 +209109,11 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){ }else{ /* Remove a segment */ if( pActive==pThisEvent->pSeg ){ - pActive = pActive->pNext; + pActive = ALWAYS(pActive) ? pActive->pNext : 0; }else{ for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){ if( pSeg->pNext==pThisEvent->pSeg ){ - pSeg->pNext = pSeg->pNext->pNext; + pSeg->pNext = ALWAYS(pSeg->pNext) ? pSeg->pNext->pNext : 0; break; } } @@ -199617,6 +209357,7 @@ static int geopolyFilter( RtreeCoord bbox[4]; RtreeConstraint *p; assert( argc==1 ); + assert( argv[0]!=0 ); geopolyBBox(0, argv[0], bbox, &rc); if( rc ){ goto geopoly_filter_end; @@ -199844,6 +209585,7 @@ static int geopolyUpdate( || !sqlcipher_sqlite3_value_nochange(aData[2]) /* UPDATE _shape */ || oldRowid!=newRowid) /* Rowid change */ ){ + assert( aData[2]!=0 ); geopolyBBox(0, aData[2], cell.aCoord, &rc); if( rc ){ if( rc==SQLITE_ERROR ){ @@ -199926,7 +209668,7 @@ static int geopolyUpdate( sqlcipher_sqlite3_free(p); nChange = 1; } - for(jj=1; jjnAux; jj++){ + for(jj=1; jjxGeom = 0; pGeomCtx->xQueryFunc = xQueryFunc; pGeomCtx->xDestructor = xDestructor; @@ -200526,8 +210271,9 @@ static void icuRegexpFunc(sqlcipher_sqlite3_context *p, int nArg, sqlcipher_sqli if( U_SUCCESS(status) ){ sqlcipher_sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete); - }else{ - assert(!pExpr); + pExpr = sqlcipher_sqlite3_get_auxdata(p, 0); + } + if( !pExpr ){ icuFunctionError(p, "uregex_open", status); return; } @@ -201768,6 +211514,13 @@ SQLITE_API void sqlcipher_sqlite3rbu_destroy_vfs(const char *zName); # define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;} #endif +/* +** Name of the URI option that causes RBU to take an exclusive lock as +** part of the incremental checkpoint operation. +*/ +#define RBU_EXCLUSIVE_CHECKPOINT "rbu_exclusive_checkpoint" + + /* ** The rbu_state table is used to save the state of a partially applied ** update so that it can be resumed later. The table consists of integer @@ -202852,7 +212605,9 @@ static void rbuTableType( assert( p->rc==SQLITE_OK ); p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[0], &p->zErrmsg, sqlcipher_sqlite3_mprintf( - "SELECT (sql LIKE 'create virtual%%'), rootpage" + "SELECT " + " (sql COLLATE nocase BETWEEN 'CREATE VIRTUAL' AND 'CREATE VIRTUAM')," + " rootpage" " FROM sqlite_schema" " WHERE name=%Q", zTab )); @@ -203212,7 +212967,7 @@ static char *rbuVacuumTableStart( ** the caller has to use an OFFSET clause to extract only the required ** rows from the sourct table, just as it does for an RBU update operation. */ -char *rbuVacuumIndexStart( +static char *rbuVacuumIndexStart( sqlcipher_sqlite3rbu *p, /* RBU handle */ RbuObjIter *pIter /* RBU iterator object */ ){ @@ -203278,7 +213033,9 @@ char *rbuVacuumIndexStart( zSep = ""; for(iCol=0; iColnCol; iCol++){ const char *zQuoted = (const char*)sqlcipher_sqlite3_column_text(pSel, iCol); - if( zQuoted[0]=='N' ){ + if( zQuoted==0 ){ + p->rc = SQLITE_NOMEM; + }else if( zQuoted[0]=='N' ){ bFailed = 1; break; } @@ -204383,7 +214140,7 @@ static RbuState *rbuLoadState(sqlcipher_sqlite3rbu *p){ break; case RBU_STATE_OALSZ: - pRet->iOalSz = (u32)sqlcipher_sqlite3_column_int64(pStmt, 1); + pRet->iOalSz = sqlcipher_sqlite3_column_int64(pStmt, 1); break; case RBU_STATE_PHASEONESTEP: @@ -204410,13 +214167,19 @@ static RbuState *rbuLoadState(sqlcipher_sqlite3rbu *p){ /* ** Open the database handle and attach the RBU database as "rbu". If an ** error occurs, leave an error code and message in the RBU handle. +** +** If argument dbMain is not NULL, then it is a database handle already +** open on the target database. Use this handle instead of opening a new +** one. */ -static void rbuOpenDatabase(sqlcipher_sqlite3rbu *p, int *pbRetry){ +static void rbuOpenDatabase(sqlcipher_sqlite3rbu *p, sqlcipher_sqlite3 *dbMain, int *pbRetry){ assert( p->rc || (p->dbMain==0 && p->dbRbu==0) ); assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 ); + assert( dbMain==0 || rbuIsVacuum(p)==0 ); /* Open the RBU database */ p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1); + p->dbMain = dbMain; if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){ sqlcipher_sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p); @@ -204782,15 +214545,31 @@ static void rbuCheckpointFrame(sqlcipher_sqlite3rbu *p, RbuFrame *pFrame){ /* -** Take an EXCLUSIVE lock on the database file. +** Take an EXCLUSIVE lock on the database file. Return SQLITE_OK if +** successful, or an SQLite error code otherwise. */ -static void rbuLockDatabase(sqlcipher_sqlite3rbu *p){ - sqlcipher_sqlite3_file *pReal = p->pTargetFd->pReal; - assert( p->rc==SQLITE_OK ); - p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_SHARED); - if( p->rc==SQLITE_OK ){ - p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_EXCLUSIVE); +static int rbuLockDatabase(sqlcipher_sqlite3 *db){ + int rc = SQLITE_OK; + sqlcipher_sqlite3_file *fd = 0; + sqlcipher_sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); + + if( fd->pMethods ){ + rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); + if( rc==SQLITE_OK ){ + rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE); + } } + return rc; +} + +/* +** Return true if the database handle passed as the only argument +** was opened with the rbu_exclusive_checkpoint=1 URI parameter +** specified. Or false otherwise. +*/ +static int rbuExclusiveCheckpoint(sqlcipher_sqlite3 *db){ + const char *zUri = sqlcipher_sqlite3_db_filename(db, 0); + return sqlcipher_sqlite3_uri_boolean(zUri, RBU_EXCLUSIVE_CHECKPOINT, 0); } #if defined(_WIN32_WCE) @@ -204848,18 +214627,24 @@ static void rbuMoveOalFile(sqlcipher_sqlite3rbu *p){ ** In order to ensure that there are no database readers, an EXCLUSIVE ** lock is obtained here before the *-oal is moved to *-wal. */ - rbuLockDatabase(p); - if( p->rc==SQLITE_OK ){ - rbuFileSuffix3(zBase, zWal); - rbuFileSuffix3(zBase, zOal); + sqlcipher_sqlite3 *dbMain = 0; + rbuFileSuffix3(zBase, zWal); + rbuFileSuffix3(zBase, zOal); + + /* Re-open the databases. */ + rbuObjIterFinalize(&p->objiter); + sqlcipher_sqlite3_close(p->dbRbu); + sqlcipher_sqlite3_close(p->dbMain); + p->dbMain = 0; + p->dbRbu = 0; - /* Re-open the databases. */ - rbuObjIterFinalize(&p->objiter); - sqlcipher_sqlite3_close(p->dbRbu); - sqlcipher_sqlite3_close(p->dbMain); - p->dbMain = 0; - p->dbRbu = 0; + dbMain = rbuOpenDbhandle(p, p->zTarget, 1); + if( dbMain ){ + assert( p->rc==SQLITE_OK ); + p->rc = rbuLockDatabase(dbMain); + } + if( p->rc==SQLITE_OK ){ #if defined(_WIN32_WCE) { LPWSTR zWideOal; @@ -204886,11 +214671,19 @@ static void rbuMoveOalFile(sqlcipher_sqlite3rbu *p){ #else p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK; #endif + } - if( p->rc==SQLITE_OK ){ - rbuOpenDatabase(p, 0); - rbuSetupCheckpoint(p, 0); - } + if( p->rc!=SQLITE_OK + || rbuIsVacuum(p) + || rbuExclusiveCheckpoint(dbMain)==0 + ){ + sqlcipher_sqlite3_close(dbMain); + dbMain = 0; + } + + if( p->rc==SQLITE_OK ){ + rbuOpenDatabase(p, dbMain, 0); + rbuSetupCheckpoint(p, 0); } } @@ -205641,9 +215434,9 @@ static sqlcipher_sqlite3rbu *openRbuHandle( ** If this is the case, it will have been checkpointed and deleted ** when the handle was closed and a second attempt to open the ** database may succeed. */ - rbuOpenDatabase(p, &bRetry); + rbuOpenDatabase(p, 0, &bRetry); if( bRetry ){ - rbuOpenDatabase(p, 0); + rbuOpenDatabase(p, 0, 0); } } @@ -205738,6 +215531,14 @@ static sqlcipher_sqlite3rbu *openRbuHandle( }else if( p->eStage==RBU_STAGE_MOVE ){ /* no-op */ }else if( p->eStage==RBU_STAGE_CKPT ){ + if( !rbuIsVacuum(p) && rbuExclusiveCheckpoint(p->dbMain) ){ + /* If the rbu_exclusive_checkpoint=1 URI parameter was specified + ** and an incremental checkpoint is being resumed, attempt an + ** exclusive lock on the db file. If this fails, so be it. */ + p->eStage = RBU_STAGE_DONE; + rbuLockDatabase(p->dbMain); + p->eStage = RBU_STAGE_CKPT; + } rbuSetupCheckpoint(p, pState); }else if( p->eStage==RBU_STAGE_DONE ){ p->rc = SQLITE_DONE; @@ -205775,7 +215576,6 @@ SQLITE_API sqlcipher_sqlite3rbu *sqlcipher_sqlite3rbu_open( const char *zState ){ if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); } - /* TODO: Check that zTarget and zRbu are non-NULL */ return openRbuHandle(zTarget, zRbu, zState); } @@ -206486,22 +216286,24 @@ static int rbuVfsShmLock(sqlcipher_sqlite3_file *pFile, int ofst, int n, int fla #endif assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) ); - if( pRbu && (pRbu->eStage==RBU_STAGE_OAL || pRbu->eStage==RBU_STAGE_MOVE) ){ - /* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from - ** taking this lock also prevents any checkpoints from occurring. - ** todo: really, it's not clear why this might occur, as - ** wal_autocheckpoint ought to be turned off. */ + if( pRbu && ( + pRbu->eStage==RBU_STAGE_OAL + || pRbu->eStage==RBU_STAGE_MOVE + || pRbu->eStage==RBU_STAGE_DONE + )){ + /* Prevent SQLite from taking a shm-lock on the target file when it + ** is supplying heap memory to the upper layer in place of *-shm + ** segments. */ if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY; }else{ int bCapture = 0; if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){ bCapture = 1; } - if( bCapture==0 || 0==(flags & SQLITE_SHM_UNLOCK) ){ rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); if( bCapture && rc==SQLITE_OK ){ - pRbu->mLock |= (1 << ofst); + pRbu->mLock |= ((1<pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){ - /* This call is to open a *-wal file. Intead, open the *-oal. This - ** code ensures that the string passed to xOpen() is terminated by a - ** pair of '\0' bytes in case the VFS attempts to extract a URI - ** parameter from it. */ - const char *zBase = zName; - size_t nCopy; - char *zCopy; + /* This call is to open a *-wal file. Intead, open the *-oal. */ + size_t nOpen; if( rbuIsVacuum(pDb->pRbu) ){ - zBase = sqlcipher_sqlite3_db_filename(pDb->pRbu->dbRbu, "main"); - zBase = sqlcipher_sqlite3_filename_wal(zBase); - } - nCopy = strlen(zBase); - zCopy = sqlcipher_sqlite3_malloc64(nCopy+2); - if( zCopy ){ - memcpy(zCopy, zBase, nCopy); - zCopy[nCopy-3] = 'o'; - zCopy[nCopy] = '\0'; - zCopy[nCopy+1] = '\0'; - zOpen = (const char*)(pFd->zDel = zCopy); - }else{ - rc = SQLITE_NOMEM; + zOpen = sqlcipher_sqlite3_db_filename(pDb->pRbu->dbRbu, "main"); + zOpen = sqlcipher_sqlite3_filename_wal(zOpen); } + nOpen = strlen(zOpen); + ((char*)zOpen)[nOpen-3] = 'o'; pFd->pRbu = pDb->pRbu; } pDb->pWalFd = pFd; @@ -206990,6 +216778,15 @@ SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3rbu_temp_size(sqlcipher_sqli #if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \ && !defined(SQLITE_OMIT_VIRTUALTABLE) +/* +** The pager and btree modules arrange objects in memory so that there are +** always approximately 200 bytes of addressable memory following each page +** buffer. This way small buffer overreads caused by corrupt database pages +** do not cause undefined behaviour. This module pads each page buffer +** by the following number of bytes for the same purpose. +*/ +#define DBSTAT_PAGE_PADDING_BYTES 256 + /* ** Page paths: ** @@ -207057,9 +216854,8 @@ struct StatCell { /* Size information for a single btree page */ struct StatPage { u32 iPgno; /* Page number */ - DbPage *pPg; /* Page content */ + u8 *aPg; /* Page buffer from sqlcipher_sqlite3_malloc() */ int iCell; /* Current cell */ - char *zPath; /* Path to this page */ /* Variables populated by statDecodePage(): */ @@ -207271,18 +217067,25 @@ static void statClearCells(StatPage *p){ } static void statClearPage(StatPage *p){ + u8 *aPg = p->aPg; statClearCells(p); - sqlcipher_sqlite3PagerUnref(p->pPg); sqlcipher_sqlite3_free(p->zPath); memset(p, 0, sizeof(StatPage)); + p->aPg = aPg; } static void statResetCsr(StatCursor *pCsr){ int i; - sqlcipher_sqlite3_reset(pCsr->pStmt); + /* In some circumstances, specifically if an OOM has occurred, the call + ** to sqlcipher_sqlite3_reset() may cause the pager to be reset (emptied). It is + ** important that statClearPage() is called to free any page refs before + ** this happens. dbsqlfuzz 9ed3e4e3816219d3509d711636c38542bf3f40b1. */ for(i=0; iaPage); i++){ statClearPage(&pCsr->aPage[i]); + sqlcipher_sqlite3_free(pCsr->aPage[i].aPg); + pCsr->aPage[i].aPg = 0; } + sqlcipher_sqlite3_reset(pCsr->pStmt); pCsr->iPage = 0; sqlcipher_sqlite3_free(pCsr->zPath); pCsr->zPath = 0; @@ -207347,7 +217150,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ int isLeaf; int szPage; - u8 *aData = sqlcipher_sqlite3PagerGetData(p->pPg); + u8 *aData = p->aPg; u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0]; p->flags = aHdr[0]; @@ -207418,7 +217221,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ if( nPayload>(u32)nLocal ){ int j; int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); - if( iOff+nLocal>nUsable || nPayload>0x7fffffff ){ + if( iOff+nLocal+4>nUsable || nPayload>0x7fffffff ){ goto statPageIsCorrupt; } pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); @@ -207477,6 +217280,38 @@ static void statSizeAndOffset(StatCursor *pCsr){ } } +/* +** Load a copy of the page data for page iPg into the buffer belonging +** to page object pPg. Allocate the buffer if necessary. Return SQLITE_OK +** if successful, or an SQLite error code otherwise. +*/ +static int statGetPage( + Btree *pBt, /* Load page from this b-tree */ + u32 iPg, /* Page number to load */ + StatPage *pPg /* Load page into this object */ +){ + int pgsz = sqlcipher_sqlite3BtreeGetPageSize(pBt); + DbPage *pDbPage = 0; + int rc; + + if( pPg->aPg==0 ){ + pPg->aPg = (u8*)sqlcipher_sqlite3_malloc(pgsz + DBSTAT_PAGE_PADDING_BYTES); + if( pPg->aPg==0 ){ + return SQLITE_NOMEM_BKPT; + } + memset(&pPg->aPg[pgsz], 0, DBSTAT_PAGE_PADDING_BYTES); + } + + rc = sqlcipher_sqlite3PagerGet(sqlcipher_sqlite3BtreePager(pBt), iPg, &pDbPage, 0); + if( rc==SQLITE_OK ){ + const u8 *a = sqlcipher_sqlite3PagerGetData(pDbPage); + memcpy(pPg->aPg, a, pgsz); + sqlcipher_sqlite3PagerUnref(pDbPage); + } + + return rc; +} + /* ** Move a DBSTAT cursor to the next entry. Normally, the next ** entry will be the next page, but in aggregated mode (pCsr->isAgg!=0), @@ -207495,7 +217330,7 @@ static int statNext(sqlcipher_sqlite3_vtab_cursor *pCursor){ pCsr->zPath = 0; statNextRestart: - if( pCsr->aPage[0].pPg==0 ){ + if( pCsr->iPage<0 ){ /* Start measuring space on the next btree */ statResetCounts(pCsr); rc = sqlcipher_sqlite3_step(pCsr->pStmt); @@ -207507,7 +217342,7 @@ statNextRestart: pCsr->isEof = 1; return sqlcipher_sqlite3_reset(pCsr->pStmt); } - rc = sqlcipher_sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0); + rc = statGetPage(pBt, iRoot, &pCsr->aPage[0]); pCsr->aPage[0].iPgno = iRoot; pCsr->aPage[0].iCell = 0; if( !pCsr->isAgg ){ @@ -207558,9 +217393,8 @@ statNextRestart: if( !p->iRightChildPg || p->iCell>p->nCell ){ statClearPage(p); - if( pCsr->iPage>0 ){ - pCsr->iPage--; - }else if( pCsr->isAgg ){ + pCsr->iPage--; + if( pCsr->isAgg && pCsr->iPage<0 ){ /* label-statNext-done: When computing aggregate space usage over ** an entire btree, this is the exit point from this function */ return SQLITE_OK; @@ -207579,7 +217413,7 @@ statNextRestart: }else{ p[1].iPgno = p->aCell[p->iCell].iChildPg; } - rc = sqlcipher_sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0); + rc = statGetPage(pBt, p[1].iPgno, &p[1]); pCsr->nPage++; p[1].iCell = 0; if( !pCsr->isAgg ){ @@ -207709,6 +217543,7 @@ static int statFilter( } if( rc==SQLITE_OK ){ + pCsr->iPage = -1; rc = statNext(pCursor); } return rc; @@ -207977,6 +217812,7 @@ static int dbpageBestIndex(sqlcipher_sqlite3_vtab *tab, sqlcipher_sqlite3_index_ ){ pIdxInfo->orderByConsumed = 1; } + sqlcipher_sqlite3VtabUsesAllSchemas(pIdxInfo); return SQLITE_OK; } @@ -208154,7 +217990,7 @@ static int dbpageUpdate( goto update_fail; } pBt = pTab->db->aDb[iDb].pBt; - if( pgno<1 || pBt==0 || pgno>(int)sqlcipher_sqlite3BtreeLastPage(pBt) ){ + if( pgno<1 || pBt==0 || pgno>sqlcipher_sqlite3BtreeLastPage(pBt) ){ zErr = "bad page number"; goto update_fail; } @@ -208282,12 +218118,15 @@ struct SessionHook { struct sqlcipher_sqlite3_session { sqlcipher_sqlite3 *db; /* Database handle session is attached to */ char *zDb; /* Name of database session is attached to */ + int bEnableSize; /* True if changeset_size() enabled */ int bEnable; /* True if currently recording */ int bIndirect; /* True if all changes are indirect */ int bAutoAttach; /* True to auto-attach tables */ int rc; /* Non-zero if an error has occurred */ void *pFilterCtx; /* First argument to pass to xTableFilter */ int (*xTableFilter)(void *pCtx, const char *zTab); + i64 nMalloc; /* Number of bytes of data allocated */ + i64 nMaxChangesetSize; sqlcipher_sqlite3_value *pZeroBlob; /* Value containing X'' */ sqlcipher_sqlite3_session *pNext; /* Next session object on same db. */ SessionTable *pTable; /* List of attached tables */ @@ -208330,6 +218169,7 @@ struct sqlcipher_sqlite3_changeset_iter { SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */ int bPatchset; /* True if this is a patchset */ int bInvert; /* True to invert changeset */ + int bSkipEmpty; /* Skip noop UPDATE changes */ int rc; /* Iterator error code */ sqlcipher_sqlite3_stmt *pConflict; /* Points to conflicting row, if any */ char *zTab; /* Current table */ @@ -208529,8 +218369,9 @@ struct SessionTable { ** this structure stored in a SessionTable.aChange[] hash table. */ struct SessionChange { - int op; /* One of UPDATE, DELETE, INSERT */ - int bIndirect; /* True if this change is "indirect" */ + u8 op; /* One of UPDATE, DELETE, INSERT */ + u8 bIndirect; /* True if this change is "indirect" */ + int nMaxSize; /* Max size of eventual changeset record */ int nRecord; /* Number of bytes in buffer aRecord[] */ u8 *aRecord; /* Buffer containing old.* record */ SessionChange *pNext; /* For hash-table collisions */ @@ -208655,7 +218496,7 @@ static int sessionSerializeValue( if( aBuf ){ sessionVarintPut(&aBuf[1], n); - if( n ) memcpy(&aBuf[nVarint + 1], z, n); + if( n>0 ) memcpy(&aBuf[nVarint + 1], z, n); } nByte = 1 + nVarint + n; @@ -208671,6 +218512,26 @@ static int sessionSerializeValue( return SQLITE_OK; } +/* +** Allocate and return a pointer to a buffer nByte bytes in size. If +** pSession is not NULL, increase the sqlcipher_sqlite3_session.nMalloc variable +** by the number of bytes allocated. +*/ +static void *sessionMalloc64(sqlcipher_sqlite3_session *pSession, i64 nByte){ + void *pRet = sqlcipher_sqlite3_malloc64(nByte); + if( pSession ) pSession->nMalloc += sqlcipher_sqlite3_msize(pRet); + return pRet; +} + +/* +** Free buffer pFree, which must have been allocated by an earlier +** call to sessionMalloc64(). If pSession is not NULL, decrease the +** sqlcipher_sqlite3_session.nMalloc counter by the number of bytes freed. +*/ +static void sessionFree(sqlcipher_sqlite3_session *pSession, void *pFree){ + if( pSession ) pSession->nMalloc -= sqlcipher_sqlite3_msize(pFree); + sqlcipher_sqlite3_free(pFree); +} /* ** This macro is used to calculate hash key values for data structures. In @@ -209138,13 +218999,19 @@ static int sessionPreupdateEqual( ** Growing the hash table in this case is a performance optimization only, ** it is not required for correct operation. */ -static int sessionGrowHash(int bPatchset, SessionTable *pTab){ +static int sessionGrowHash( + sqlcipher_sqlite3_session *pSession, /* For memory accounting. May be NULL */ + int bPatchset, + SessionTable *pTab +){ if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){ int i; SessionChange **apNew; sqlcipher_sqlite3_int64 nNew = 2*(sqlcipher_sqlite3_int64)(pTab->nChange ? pTab->nChange : 128); - apNew = (SessionChange **)sqlcipher_sqlite3_malloc64(sizeof(SessionChange *) * nNew); + apNew = (SessionChange**)sessionMalloc64( + pSession, sizeof(SessionChange*) * nNew + ); if( apNew==0 ){ if( pTab->nChange==0 ){ return SQLITE_ERROR; @@ -209165,7 +219032,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){ } } - sqlcipher_sqlite3_free(pTab->apChange); + sessionFree(pSession, pTab->apChange); pTab->nChange = nNew; pTab->apChange = apNew; } @@ -209199,6 +219066,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){ ** be freed using sqlcipher_sqlite3_free() by the caller */ static int sessionTableInfo( + sqlcipher_sqlite3_session *pSession, /* For memory accounting. May be NULL */ sqlcipher_sqlite3 *db, /* Database connection */ const char *zDb, /* Name of attached database (e.g. "main") */ const char *zThis, /* Table name */ @@ -209233,16 +219101,32 @@ static int sessionTableInfo( }else if( rc==SQLITE_ERROR ){ zPragma = sqlcipher_sqlite3_mprintf(""); }else{ + *pazCol = 0; + *pabPK = 0; + *pnCol = 0; + if( pzTab ) *pzTab = 0; return rc; } }else{ zPragma = sqlcipher_sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis); } - if( !zPragma ) return SQLITE_NOMEM; + if( !zPragma ){ + *pazCol = 0; + *pabPK = 0; + *pnCol = 0; + if( pzTab ) *pzTab = 0; + return SQLITE_NOMEM; + } rc = sqlcipher_sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0); sqlcipher_sqlite3_free(zPragma); - if( rc!=SQLITE_OK ) return rc; + if( rc!=SQLITE_OK ){ + *pazCol = 0; + *pabPK = 0; + *pnCol = 0; + if( pzTab ) *pzTab = 0; + return rc; + } nByte = nThis + 1; while( SQLITE_ROW==sqlcipher_sqlite3_step(pStmt) ){ @@ -209253,7 +219137,7 @@ static int sessionTableInfo( if( rc==SQLITE_OK ){ nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); - pAlloc = sqlcipher_sqlite3_malloc64(nByte); + pAlloc = sessionMalloc64(pSession, nByte); if( pAlloc==0 ){ rc = SQLITE_NOMEM; } @@ -209296,7 +219180,7 @@ static int sessionTableInfo( *pabPK = 0; *pnCol = 0; if( pzTab ) *pzTab = 0; - sqlcipher_sqlite3_free(azCol); + sessionFree(pSession, azCol); } sqlcipher_sqlite3_finalize(pStmt); return rc; @@ -209318,7 +219202,7 @@ static int sessionInitTable(sqlcipher_sqlite3_session *pSession, SessionTable *p if( pTab->nCol==0 ){ u8 *abPK; assert( pTab->azCol==0 || pTab->abPK==0 ); - pSession->rc = sessionTableInfo(pSession->db, pSession->zDb, + pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK ); if( pSession->rc==SQLITE_OK ){ @@ -209332,6 +219216,12 @@ static int sessionInitTable(sqlcipher_sqlite3_session *pSession, SessionTable *p if( 0==sqlcipher_sqlite3_stricmp("sqlite_stat1", pTab->zName) ){ pTab->bStat1 = 1; } + + if( pSession->bEnableSize ){ + pSession->nMaxChangesetSize += ( + 1 + sessionVarintLen(pTab->nCol) + pTab->nCol + strlen(pTab->zName)+1 + ); + } } } return (pSession->rc || pTab->abPK==0); @@ -209377,6 +219267,103 @@ static int sessionStat1Depth(void *pCtx){ return p->hook.xDepth(p->hook.pCtx); } +static int sessionUpdateMaxSize( + int op, + sqlcipher_sqlite3_session *pSession, /* Session object pTab is attached to */ + SessionTable *pTab, /* Table that change applies to */ + SessionChange *pC /* Update pC->nMaxSize */ +){ + i64 nNew = 2; + if( pC->op==SQLITE_INSERT ){ + if( op!=SQLITE_DELETE ){ + int ii; + for(ii=0; iinCol; ii++){ + sqlcipher_sqlite3_value *p = 0; + pSession->hook.xNew(pSession->hook.pCtx, ii, &p); + sessionSerializeValue(0, p, &nNew); + } + } + }else if( op==SQLITE_DELETE ){ + nNew += pC->nRecord; + if( sqlcipher_sqlite3_preupdate_blobwrite(pSession->db)>=0 ){ + nNew += pC->nRecord; + } + }else{ + int ii; + u8 *pCsr = pC->aRecord; + for(ii=0; iinCol; ii++){ + int bChanged = 1; + int nOld = 0; + int eType; + sqlcipher_sqlite3_value *p = 0; + pSession->hook.xNew(pSession->hook.pCtx, ii, &p); + if( p==0 ){ + return SQLITE_NOMEM; + } + + eType = *pCsr++; + switch( eType ){ + case SQLITE_NULL: + bChanged = sqlcipher_sqlite3_value_type(p)!=SQLITE_NULL; + break; + + case SQLITE_FLOAT: + case SQLITE_INTEGER: { + if( eType==sqlcipher_sqlite3_value_type(p) ){ + sqlcipher_sqlite3_int64 iVal = sessionGetI64(pCsr); + if( eType==SQLITE_INTEGER ){ + bChanged = (iVal!=sqlcipher_sqlite3_value_int64(p)); + }else{ + double dVal; + memcpy(&dVal, &iVal, 8); + bChanged = (dVal!=sqlcipher_sqlite3_value_double(p)); + } + } + nOld = 8; + pCsr += 8; + break; + } + + default: { + int nByte; + nOld = sessionVarintGet(pCsr, &nByte); + pCsr += nOld; + nOld += nByte; + assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); + if( eType==sqlcipher_sqlite3_value_type(p) + && nByte==sqlcipher_sqlite3_value_bytes(p) + && (nByte==0 || 0==memcmp(pCsr, sqlcipher_sqlite3_value_blob(p), nByte)) + ){ + bChanged = 0; + } + pCsr += nByte; + break; + } + } + + if( bChanged && pTab->abPK[ii] ){ + nNew = pC->nRecord + 2; + break; + } + + if( bChanged ){ + nNew += 1 + nOld; + sessionSerializeValue(0, p, &nNew); + }else if( pTab->abPK[ii] ){ + nNew += 2 + nOld; + }else{ + nNew += 2; + } + } + } + + if( nNew>pC->nMaxSize ){ + int nIncr = nNew - pC->nMaxSize; + pC->nMaxSize = nNew; + pSession->nMaxChangesetSize += nIncr; + } + return SQLITE_OK; +} /* ** This function is only called from with a pre-update-hook reporting a @@ -209409,7 +219396,7 @@ static void sessionPreupdateOneChange( } /* Grow the hash table if required */ - if( sessionGrowHash(0, pTab) ){ + if( sessionGrowHash(pSession, 0, pTab) ){ pSession->rc = SQLITE_NOMEM; return; } @@ -209450,7 +219437,6 @@ static void sessionPreupdateOneChange( /* Create a new change object containing all the old values (if ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK ** values (if this is an INSERT). */ - SessionChange *pChange; /* New change object */ sqlcipher_sqlite3_int64 nByte; /* Number of bytes to allocate */ int i; /* Used to iterate through columns */ @@ -209476,13 +219462,13 @@ static void sessionPreupdateOneChange( } /* Allocate the change object */ - pChange = (SessionChange *)sqlcipher_sqlite3_malloc64(nByte); - if( !pChange ){ + pC = (SessionChange *)sessionMalloc64(pSession, nByte); + if( !pC ){ rc = SQLITE_NOMEM; goto error_out; }else{ - memset(pChange, 0, sizeof(SessionChange)); - pChange->aRecord = (u8 *)&pChange[1]; + memset(pC, 0, sizeof(SessionChange)); + pC->aRecord = (u8 *)&pC[1]; } /* Populate the change object. None of the preupdate_old(), @@ -209497,17 +219483,17 @@ static void sessionPreupdateOneChange( }else if( pTab->abPK[i] ){ pSession->hook.xNew(pSession->hook.pCtx, i, &p); } - sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte); + sessionSerializeValue(&pC->aRecord[nByte], p, &nByte); } /* Add the change to the hash-table */ if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){ - pChange->bIndirect = 1; + pC->bIndirect = 1; } - pChange->nRecord = nByte; - pChange->op = op; - pChange->pNext = pTab->apChange[iHash]; - pTab->apChange[iHash] = pChange; + pC->nRecord = nByte; + pC->op = op; + pC->pNext = pTab->apChange[iHash]; + pTab->apChange[iHash] = pC; }else if( pC->bIndirect ){ /* If the existing change is considered "indirect", but this current @@ -209518,8 +219504,14 @@ static void sessionPreupdateOneChange( pC->bIndirect = 0; } } + + assert( rc==SQLITE_OK ); + if( pSession->bEnableSize ){ + rc = sessionUpdateMaxSize(op, pSession, pTab, pC); + } } + /* If an error has occurred, mark the session object as failed. */ error_out: if( pTab->bStat1 ){ @@ -209552,7 +219544,11 @@ static int sessionFindTable( ){ rc = sqlcipher_sqlite3session_attach(pSession, zName); if( rc==SQLITE_OK ){ - for(pRet=pSession->pTable; pRet->pNext; pRet=pRet->pNext); + pRet = pSession->pTable; + while( ALWAYS(pRet) && pRet->pNext ){ + pRet = pRet->pNext; + } + assert( pRet!=0 ); assert( 0==sqlcipher_sqlite3_strnicmp(pRet->zName, zName, nName+1) ); } } @@ -209849,7 +219845,7 @@ SQLITE_API int sqlcipher_sqlite3session_diff( int nCol; /* Columns in zFrom.zTbl */ u8 *abPK; const char **azCol = 0; - rc = sessionTableInfo(db, zFrom, zTbl, &nCol, 0, &azCol, &abPK); + rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK); if( rc==SQLITE_OK ){ if( pTo->nCol!=nCol ){ bMismatch = 1; @@ -209947,7 +219943,7 @@ SQLITE_API int sqlcipher_sqlite3session_create( ** Free the list of table objects passed as the first argument. The contents ** of the changed-rows hash tables are also deleted. */ -static void sessionDeleteTable(SessionTable *pList){ +static void sessionDeleteTable(sqlcipher_sqlite3_session *pSession, SessionTable *pList){ SessionTable *pNext; SessionTable *pTab; @@ -209959,12 +219955,12 @@ static void sessionDeleteTable(SessionTable *pList){ SessionChange *pNextChange; for(p=pTab->apChange[i]; p; p=pNextChange){ pNextChange = p->pNext; - sqlcipher_sqlite3_free(p); + sessionFree(pSession, p); } } - sqlcipher_sqlite3_free((char*)pTab->azCol); /* cast works around VC++ bug */ - sqlcipher_sqlite3_free(pTab->apChange); - sqlcipher_sqlite3_free(pTab); + sessionFree(pSession, (char*)pTab->azCol); /* cast works around VC++ bug */ + sessionFree(pSession, pTab->apChange); + sessionFree(pSession, pTab); } } @@ -209992,9 +219988,11 @@ SQLITE_API void sqlcipher_sqlite3session_delete(sqlcipher_sqlite3_session *pSess /* Delete all attached table objects. And the contents of their ** associated hash-tables. */ - sessionDeleteTable(pSession->pTable); + sessionDeleteTable(pSession, pSession->pTable); - /* Free the session object itself. */ + /* Assert that all allocations have been freed and then free the + ** session object itself. */ + assert( pSession->nMalloc==0 ); sqlcipher_sqlite3_free(pSession); } @@ -210041,7 +220039,8 @@ SQLITE_API int sqlcipher_sqlite3session_attach( if( !pTab ){ /* Allocate new SessionTable object. */ - pTab = (SessionTable *)sqlcipher_sqlite3_malloc64(sizeof(SessionTable) + nName + 1); + int nByte = sizeof(SessionTable) + nName + 1; + pTab = (SessionTable*)sessionMalloc64(pSession, nByte); if( !pTab ){ rc = SQLITE_NOMEM; }else{ @@ -210071,13 +220070,29 @@ SQLITE_API int sqlcipher_sqlite3session_attach( ** If successful, return zero. Otherwise, if an OOM condition is encountered, ** set *pRc to SQLITE_NOMEM and return non-zero. */ -static int sessionBufferGrow(SessionBuffer *p, size_t nByte, int *pRc){ - if( *pRc==SQLITE_OK && (size_t)(p->nAlloc-p->nBuf)nBuf + nByte; + if( *pRc==SQLITE_OK && nReq>p->nAlloc ){ u8 *aNew; i64 nNew = p->nAlloc ? p->nAlloc : 128; + do { nNew = nNew*2; - }while( (size_t)(nNew-p->nBuf)SESSION_MAX_BUFFER_SZ ){ + nNew = SESSION_MAX_BUFFER_SZ; + if( nNewaBuf, nNew); if( 0==aNew ){ @@ -210306,6 +220321,7 @@ static int sessionAppendUpdate( int i; /* Used to iterate through columns */ u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */ + assert( abPK!=0 ); sessionAppendByte(pBuf, SQLITE_UPDATE, &rc); sessionAppendByte(pBuf, p->bIndirect, &rc); for(i=0; ipTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){ if( pTab->nEntry ){ const char *zName = pTab->zName; - int nCol; /* Number of columns in table */ - u8 *abPK; /* Primary key array */ + int nCol = 0; /* Number of columns in table */ + u8 *abPK = 0; /* Primary key array */ const char **azCol = 0; /* Table columns */ int i; /* Used to iterate through hash buckets */ sqlcipher_sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */ @@ -210638,7 +220656,7 @@ static int sessionGenerateChangeset( int nNoop; /* Size of buffer after writing tbl header */ /* Check the table schema is still Ok. */ - rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK); + rc = sessionTableInfo(0, db, pSession->zDb, zName, &nCol, 0,&azCol,&abPK); if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){ rc = SQLITE_SCHEMA; } @@ -210668,6 +220686,7 @@ static int sessionGenerateChangeset( sessionAppendCol(&buf, pSel, iCol, &rc); } }else{ + assert( abPK!=0 ); /* Because sessionSelectStmt() returned ok */ rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK); } }else if( p->op!=SQLITE_INSERT ){ @@ -210728,7 +220747,14 @@ SQLITE_API int sqlcipher_sqlite3session_changeset( int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ void **ppChangeset /* OUT: Buffer containing changeset */ ){ - return sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset); + int rc; + + if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE; + rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset,ppChangeset); + assert( rc || pnChangeset==0 + || pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize + ); + return rc; } /* @@ -210739,6 +220765,7 @@ SQLITE_API int sqlcipher_sqlite3session_changeset_strm( int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ){ + if( xOutput==0 ) return SQLITE_MISUSE; return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0); } @@ -210750,6 +220777,7 @@ SQLITE_API int sqlcipher_sqlite3session_patchset_strm( int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ){ + if( xOutput==0 ) return SQLITE_MISUSE; return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0); } @@ -210765,6 +220793,7 @@ SQLITE_API int sqlcipher_sqlite3session_patchset( int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */ void **ppPatchset /* OUT: Buffer containing changeset */ ){ + if( pnPatchset==0 || ppPatchset==0 ) return SQLITE_MISUSE; return sessionGenerateChangeset(pSession, 1, 0, 0, pnPatchset, ppPatchset); } @@ -210813,6 +220842,46 @@ SQLITE_API int sqlcipher_sqlite3session_isempty(sqlcipher_sqlite3_session *pSess return (ret==0); } +/* +** Return the amount of heap memory in use. +*/ +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3session_memory_used(sqlcipher_sqlite3_session *pSession){ + return pSession->nMalloc; +} + +/* +** Configure the session object passed as the first argument. +*/ +SQLITE_API int sqlcipher_sqlite3session_object_config(sqlcipher_sqlite3_session *pSession, int op, void *pArg){ + int rc = SQLITE_OK; + switch( op ){ + case SQLITE_SESSION_OBJCONFIG_SIZE: { + int iArg = *(int*)pArg; + if( iArg>=0 ){ + if( pSession->pTable ){ + rc = SQLITE_MISUSE; + }else{ + pSession->bEnableSize = (iArg!=0); + } + } + *(int*)pArg = pSession->bEnableSize; + break; + } + + default: + rc = SQLITE_MISUSE; + } + + return rc; +} + +/* +** Return the maximum size of sqlcipher_sqlite3session_changeset() output. +*/ +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3session_changeset_size(sqlcipher_sqlite3_session *pSession){ + return pSession->nMaxChangesetSize; +} + /* ** Do the work for either sqlcipher_sqlite3changeset_start() or start_strm(). */ @@ -210822,7 +220891,8 @@ static int sessionChangesetStart( void *pIn, int nChangeset, /* Size of buffer pChangeset in bytes */ void *pChangeset, /* Pointer to buffer containing changeset */ - int bInvert /* True to invert changeset */ + int bInvert, /* True to invert changeset */ + int bSkipEmpty /* True to skip empty UPDATE changes */ ){ sqlcipher_sqlite3_changeset_iter *pRet; /* Iterator to return */ int nByte; /* Number of bytes to allocate for iterator */ @@ -210843,6 +220913,7 @@ static int sessionChangesetStart( pRet->in.pIn = pIn; pRet->in.bEof = (xInput ? 0 : 1); pRet->bInvert = bInvert; + pRet->bSkipEmpty = bSkipEmpty; /* Populate the output variable and return success. */ *pp = pRet; @@ -210857,7 +220928,7 @@ SQLITE_API int sqlcipher_sqlite3changeset_start( int nChangeset, /* Size of buffer pChangeset in bytes */ void *pChangeset /* Pointer to buffer containing changeset */ ){ - return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0); + return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0, 0); } SQLITE_API int sqlcipher_sqlite3changeset_start_v2( sqlcipher_sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */ @@ -210866,7 +220937,7 @@ SQLITE_API int sqlcipher_sqlite3changeset_start_v2( int flags ){ int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT); - return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert); + return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert, 0); } /* @@ -210877,7 +220948,7 @@ SQLITE_API int sqlcipher_sqlite3changeset_start_strm( int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn ){ - return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0); + return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0, 0); } SQLITE_API int sqlcipher_sqlite3changeset_start_v2_strm( sqlcipher_sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */ @@ -210886,7 +220957,7 @@ SQLITE_API int sqlcipher_sqlite3changeset_start_v2_strm( int flags ){ int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT); - return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert); + return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert, 0); } /* @@ -211012,11 +221083,14 @@ static int sessionReadRecord( SessionInput *pIn, /* Input data */ int nCol, /* Number of values in record */ u8 *abPK, /* Array of primary key flags, or NULL */ - sqlcipher_sqlite3_value **apOut /* Write values to this array */ + sqlcipher_sqlite3_value **apOut, /* Write values to this array */ + int *pbEmpty ){ int i; /* Used to iterate through columns */ int rc = SQLITE_OK; + assert( pbEmpty==0 || *pbEmpty==0 ); + if( pbEmpty ) *pbEmpty = 1; for(i=0; iaData[pIn->iNext++]; assert( apOut[i]==0 ); if( eType ){ + if( pbEmpty ) *pbEmpty = 0; apOut[i] = sqlcipher_sqlite3ValueNew(0); if( !apOut[i] ) rc = SQLITE_NOMEM; } @@ -211207,31 +221282,27 @@ static int sessionChangesetReadTblhdr(sqlcipher_sqlite3_changeset_iter *p){ } /* -** Advance the changeset iterator to the next change. -** -** If both paRec and pnRec are NULL, then this function works like the public -** API sqlcipher_sqlite3changeset_next(). If SQLITE_ROW is returned, then the -** sqlcipher_sqlite3changeset_new() and old() APIs may be used to query for values. +** Advance the changeset iterator to the next change. The differences between +** this function and sessionChangesetNext() are that ** -** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change -** record is written to *paRec before returning and the number of bytes in -** the record to *pnRec. +** * If pbEmpty is not NULL and the change is a no-op UPDATE (an UPDATE +** that modifies no columns), this function sets (*pbEmpty) to 1. ** -** Either way, this function returns SQLITE_ROW if the iterator is -** successfully advanced to the next change in the changeset, an SQLite -** error code if an error occurs, or SQLITE_DONE if there are no further -** changes in the changeset. +** * If the iterator is configured to skip no-op UPDATEs, +** sessionChangesetNext() does that. This function does not. */ -static int sessionChangesetNext( +static int sessionChangesetNextOne( sqlcipher_sqlite3_changeset_iter *p, /* Changeset iterator */ u8 **paRec, /* If non-NULL, store record pointer here */ int *pnRec, /* If non-NULL, store size of record here */ - int *pbNew /* If non-NULL, true if new table */ + int *pbNew, /* If non-NULL, true if new table */ + int *pbEmpty ){ int i; u8 op; assert( (paRec==0 && pnRec==0) || (paRec && pnRec) ); + assert( pbEmpty==0 || *pbEmpty==0 ); /* If the iterator is in the error-state, return immediately. */ if( p->rc!=SQLITE_OK ) return p->rc; @@ -211304,13 +221375,13 @@ static int sessionChangesetNext( /* If this is an UPDATE or DELETE, read the old.* record. */ if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){ u8 *abPK = p->bPatchset ? p->abPK : 0; - p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld); + p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld, 0); if( p->rc!=SQLITE_OK ) return p->rc; } /* If this is an INSERT or UPDATE, read the new.* record. */ if( p->op!=SQLITE_DELETE ){ - p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew); + p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew, pbEmpty); if( p->rc!=SQLITE_OK ) return p->rc; } @@ -211337,6 +221408,37 @@ static int sessionChangesetNext( return SQLITE_ROW; } +/* +** Advance the changeset iterator to the next change. +** +** If both paRec and pnRec are NULL, then this function works like the public +** API sqlcipher_sqlite3changeset_next(). If SQLITE_ROW is returned, then the +** sqlcipher_sqlite3changeset_new() and old() APIs may be used to query for values. +** +** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change +** record is written to *paRec before returning and the number of bytes in +** the record to *pnRec. +** +** Either way, this function returns SQLITE_ROW if the iterator is +** successfully advanced to the next change in the changeset, an SQLite +** error code if an error occurs, or SQLITE_DONE if there are no further +** changes in the changeset. +*/ +static int sessionChangesetNext( + sqlcipher_sqlite3_changeset_iter *p, /* Changeset iterator */ + u8 **paRec, /* If non-NULL, store record pointer here */ + int *pnRec, /* If non-NULL, store size of record here */ + int *pbNew /* If non-NULL, true if new table */ +){ + int bEmpty; + int rc; + do { + bEmpty = 0; + rc = sessionChangesetNextOne(p, paRec, pnRec, pbNew, &bEmpty); + }while( rc==SQLITE_ROW && p->bSkipEmpty && bEmpty); + return rc; +} + /* ** Advance an iterator created by sqlcipher_sqlite3changeset_start() to the next ** change in the changeset. This function may return SQLITE_ROW, SQLITE_DONE @@ -211609,9 +221711,9 @@ static int sessionChangesetInvert( /* Read the old.* and new.* records for the update change. */ pInput->iNext += 2; - rc = sessionReadRecord(pInput, nCol, 0, &apVal[0]); + rc = sessionReadRecord(pInput, nCol, 0, &apVal[0], 0); if( rc==SQLITE_OK ){ - rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol]); + rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol], 0); } /* Write the new old.* record. Consists of the PK columns from the @@ -211655,11 +221757,11 @@ static int sessionChangesetInvert( } assert( rc==SQLITE_OK ); - if( pnInverted ){ + if( pnInverted && ALWAYS(ppInverted) ){ *pnInverted = sOut.nBuf; *ppInverted = sOut.aBuf; sOut.aBuf = 0; - }else if( sOut.nBuf>0 ){ + }else if( sOut.nBuf>0 && ALWAYS(xOutput!=0) ){ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); } @@ -211712,16 +221814,25 @@ SQLITE_API int sqlcipher_sqlite3changeset_invert_strm( return rc; } + +typedef struct SessionUpdate SessionUpdate; +struct SessionUpdate { + sqlcipher_sqlite3_stmt *pStmt; + u32 *aMask; + SessionUpdate *pNext; +}; + typedef struct SessionApplyCtx SessionApplyCtx; struct SessionApplyCtx { sqlcipher_sqlite3 *db; sqlcipher_sqlite3_stmt *pDelete; /* DELETE statement */ - sqlcipher_sqlite3_stmt *pUpdate; /* UPDATE statement */ sqlcipher_sqlite3_stmt *pInsert; /* INSERT statement */ sqlcipher_sqlite3_stmt *pSelect; /* SELECT statement */ int nCol; /* Size of azCol[] and abPK[] arrays */ const char **azCol; /* Array of column names */ u8 *abPK; /* Boolean array - true if column is in PK */ + u32 *aUpdateMask; /* Used by sessionUpdateFind */ + SessionUpdate *pUp; int bStat1; /* True if table is sqlite_stat1 */ int bDeferConstraints; /* True to defer constraints */ int bInvertConstraints; /* Invert when iterating constraints buffer */ @@ -211731,6 +221842,167 @@ struct SessionApplyCtx { u8 bRebase; /* True to collect rebase information */ }; +/* Number of prepared UPDATE statements to cache. */ +#define SESSION_UPDATE_CACHE_SZ 12 + +/* +** Find a prepared UPDATE statement suitable for the UPDATE step currently +** being visited by the iterator. The UPDATE is of the form: +** +** UPDATE tbl SET col = ?, col2 = ? WHERE pk1 IS ? AND pk2 IS ? +*/ +static int sessionUpdateFind( + sqlcipher_sqlite3_changeset_iter *pIter, + SessionApplyCtx *p, + int bPatchset, + sqlcipher_sqlite3_stmt **ppStmt +){ + int rc = SQLITE_OK; + SessionUpdate *pUp = 0; + int nCol = pIter->nCol; + int nU32 = (pIter->nCol+33)/32; + int ii; + + if( p->aUpdateMask==0 ){ + p->aUpdateMask = sqlcipher_sqlite3_malloc(nU32*sizeof(u32)); + if( p->aUpdateMask==0 ){ + rc = SQLITE_NOMEM; + } + } + + if( rc==SQLITE_OK ){ + memset(p->aUpdateMask, 0, nU32*sizeof(u32)); + rc = SQLITE_CORRUPT; + for(ii=0; iinCol; ii++){ + if( sessionChangesetNew(pIter, ii) ){ + p->aUpdateMask[ii/32] |= (1<<(ii%32)); + rc = SQLITE_OK; + } + } + } + + if( rc==SQLITE_OK ){ + if( bPatchset ) p->aUpdateMask[nCol/32] |= (1<<(nCol%32)); + + if( p->pUp ){ + int nUp = 0; + SessionUpdate **pp = &p->pUp; + while( 1 ){ + nUp++; + if( 0==memcmp(p->aUpdateMask, (*pp)->aMask, nU32*sizeof(u32)) ){ + pUp = *pp; + *pp = pUp->pNext; + pUp->pNext = p->pUp; + p->pUp = pUp; + break; + } + + if( (*pp)->pNext ){ + pp = &(*pp)->pNext; + }else{ + if( nUp>=SESSION_UPDATE_CACHE_SZ ){ + sqlcipher_sqlite3_finalize((*pp)->pStmt); + sqlcipher_sqlite3_free(*pp); + *pp = 0; + } + break; + } + } + } + + if( pUp==0 ){ + int nByte = sizeof(SessionUpdate) * nU32*sizeof(u32); + int bStat1 = (sqlcipher_sqlite3_stricmp(pIter->zTab, "sqlite_stat1")==0); + pUp = (SessionUpdate*)sqlcipher_sqlite3_malloc(nByte); + if( pUp==0 ){ + rc = SQLITE_NOMEM; + }else{ + const char *zSep = ""; + SessionBuffer buf; + + memset(&buf, 0, sizeof(buf)); + pUp->aMask = (u32*)&pUp[1]; + memcpy(pUp->aMask, p->aUpdateMask, nU32*sizeof(u32)); + + sessionAppendStr(&buf, "UPDATE main.", &rc); + sessionAppendIdent(&buf, pIter->zTab, &rc); + sessionAppendStr(&buf, " SET ", &rc); + + /* Create the assignments part of the UPDATE */ + for(ii=0; iinCol; ii++){ + if( p->abPK[ii]==0 && sessionChangesetNew(pIter, ii) ){ + sessionAppendStr(&buf, zSep, &rc); + sessionAppendIdent(&buf, p->azCol[ii], &rc); + sessionAppendStr(&buf, " = ?", &rc); + sessionAppendInteger(&buf, ii*2+1, &rc); + zSep = ", "; + } + } + + /* Create the WHERE clause part of the UPDATE */ + zSep = ""; + sessionAppendStr(&buf, " WHERE ", &rc); + for(ii=0; iinCol; ii++){ + if( p->abPK[ii] || (bPatchset==0 && sessionChangesetOld(pIter, ii)) ){ + sessionAppendStr(&buf, zSep, &rc); + if( bStat1 && ii==1 ){ + assert( sqlcipher_sqlite3_stricmp(p->azCol[ii], "idx")==0 ); + sessionAppendStr(&buf, + "idx IS CASE " + "WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL " + "ELSE ?4 END ", &rc + ); + }else{ + sessionAppendIdent(&buf, p->azCol[ii], &rc); + sessionAppendStr(&buf, " IS ?", &rc); + sessionAppendInteger(&buf, ii*2+2, &rc); + } + zSep = " AND "; + } + } + + if( rc==SQLITE_OK ){ + char *zSql = (char*)buf.aBuf; + rc = sqlcipher_sqlite3_prepare_v2(p->db, zSql, buf.nBuf, &pUp->pStmt, 0); + } + + if( rc!=SQLITE_OK ){ + sqlcipher_sqlite3_free(pUp); + pUp = 0; + }else{ + pUp->pNext = p->pUp; + p->pUp = pUp; + } + sqlcipher_sqlite3_free(buf.aBuf); + } + } + } + + assert( (rc==SQLITE_OK)==(pUp!=0) ); + if( pUp ){ + *ppStmt = pUp->pStmt; + }else{ + *ppStmt = 0; + } + return rc; +} + +/* +** Free all cached UPDATE statements. +*/ +static void sessionUpdateFree(SessionApplyCtx *p){ + SessionUpdate *pUp; + SessionUpdate *pNext; + for(pUp=p->pUp; pUp; pUp=pNext){ + pNext = pUp->pNext; + sqlcipher_sqlite3_finalize(pUp->pStmt); + sqlcipher_sqlite3_free(pUp); + } + p->pUp = 0; + sqlcipher_sqlite3_free(p->aUpdateMask); + p->aUpdateMask = 0; +} + /* ** Formulate a statement to DELETE a row from database db. Assuming a table ** structure like this: @@ -211800,103 +222072,6 @@ static int sessionDeleteRow( return rc; } -/* -** Formulate and prepare a statement to UPDATE a row from database db. -** Assuming a table structure like this: -** -** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c)); -** -** The UPDATE statement looks like this: -** -** UPDATE x SET -** a = CASE WHEN ?2 THEN ?3 ELSE a END, -** b = CASE WHEN ?5 THEN ?6 ELSE b END, -** c = CASE WHEN ?8 THEN ?9 ELSE c END, -** d = CASE WHEN ?11 THEN ?12 ELSE d END -** WHERE a = ?1 AND c = ?7 AND (?13 OR -** (?5==0 OR b IS ?4) AND (?11==0 OR d IS ?10) AND -** ) -** -** For each column in the table, there are three variables to bind: -** -** ?(i*3+1) The old.* value of the column, if any. -** ?(i*3+2) A boolean flag indicating that the value is being modified. -** ?(i*3+3) The new.* value of the column, if any. -** -** Also, a boolean flag that, if set to true, causes the statement to update -** a row even if the non-PK values do not match. This is required if the -** conflict-handler is invoked with CHANGESET_DATA and returns -** CHANGESET_REPLACE. This is variable "?(nCol*3+1)". -** -** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left -** pointing to the prepared version of the SQL statement. -*/ -static int sessionUpdateRow( - sqlcipher_sqlite3 *db, /* Database handle */ - const char *zTab, /* Table name */ - SessionApplyCtx *p /* Session changeset-apply context */ -){ - int rc = SQLITE_OK; - int i; - const char *zSep = ""; - SessionBuffer buf = {0, 0, 0}; - - /* Append "UPDATE tbl SET " */ - sessionAppendStr(&buf, "UPDATE main.", &rc); - sessionAppendIdent(&buf, zTab, &rc); - sessionAppendStr(&buf, " SET ", &rc); - - /* Append the assignments */ - for(i=0; inCol; i++){ - sessionAppendStr(&buf, zSep, &rc); - sessionAppendIdent(&buf, p->azCol[i], &rc); - sessionAppendStr(&buf, " = CASE WHEN ?", &rc); - sessionAppendInteger(&buf, i*3+2, &rc); - sessionAppendStr(&buf, " THEN ?", &rc); - sessionAppendInteger(&buf, i*3+3, &rc); - sessionAppendStr(&buf, " ELSE ", &rc); - sessionAppendIdent(&buf, p->azCol[i], &rc); - sessionAppendStr(&buf, " END", &rc); - zSep = ", "; - } - - /* Append the PK part of the WHERE clause */ - sessionAppendStr(&buf, " WHERE ", &rc); - for(i=0; inCol; i++){ - if( p->abPK[i] ){ - sessionAppendIdent(&buf, p->azCol[i], &rc); - sessionAppendStr(&buf, " = ?", &rc); - sessionAppendInteger(&buf, i*3+1, &rc); - sessionAppendStr(&buf, " AND ", &rc); - } - } - - /* Append the non-PK part of the WHERE clause */ - sessionAppendStr(&buf, " (?", &rc); - sessionAppendInteger(&buf, p->nCol*3+1, &rc); - sessionAppendStr(&buf, " OR 1", &rc); - for(i=0; inCol; i++){ - if( !p->abPK[i] ){ - sessionAppendStr(&buf, " AND (?", &rc); - sessionAppendInteger(&buf, i*3+2, &rc); - sessionAppendStr(&buf, "=0 OR ", &rc); - sessionAppendIdent(&buf, p->azCol[i], &rc); - sessionAppendStr(&buf, " IS ?", &rc); - sessionAppendInteger(&buf, i*3+1, &rc); - sessionAppendStr(&buf, ")", &rc); - } - } - sessionAppendStr(&buf, ")", &rc); - - if( rc==SQLITE_OK ){ - rc = sqlcipher_sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0); - } - sqlcipher_sqlite3_free(buf.aBuf); - - return rc; -} - - /* ** Formulate and prepare an SQL statement to query table zTab by primary ** key. Assuming the following table structure: @@ -211977,17 +222152,6 @@ static int sessionStat1Sql(sqlcipher_sqlite3 *db, SessionApplyCtx *p){ "?3)" ); } - if( rc==SQLITE_OK ){ - rc = sessionPrepare(db, &p->pUpdate, - "UPDATE main.sqlite_stat1 SET " - "tbl = CASE WHEN ?2 THEN ?3 ELSE tbl END, " - "idx = CASE WHEN ?5 THEN ?6 ELSE idx END, " - "stat = CASE WHEN ?8 THEN ?9 ELSE stat END " - "WHERE tbl=?1 AND idx IS " - "CASE WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL ELSE ?4 END " - "AND (?10 OR ?8=0 OR stat IS ?7)" - ); - } if( rc==SQLITE_OK ){ rc = sessionPrepare(db, &p->pDelete, "DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS " @@ -212053,7 +222217,7 @@ static int sessionBindRow( for(i=0; rc==SQLITE_OK && ipDelete && p->pUpdate && p->pInsert && p->pSelect ); + assert( p->pDelete && p->pInsert && p->pSelect ); assert( p->azCol && p->abPK ); assert( !pbReplace || *pbReplace==0 ); @@ -212344,29 +222508,28 @@ static int sessionApplyOneOp( }else if( op==SQLITE_UPDATE ){ int i; + sqlcipher_sqlite3_stmt *pUp = 0; + int bPatchset = (pbRetry==0 || pIter->bPatchset); + + rc = sessionUpdateFind(pIter, p, bPatchset, &pUp); /* Bind values to the UPDATE statement. */ for(i=0; rc==SQLITE_OK && ipUpdate, i*3+2, !!pNew); - if( pOld ){ - rc = sessionBindValue(p->pUpdate, i*3+1, pOld); + if( p->abPK[i] || (bPatchset==0 && pOld) ){ + rc = sessionBindValue(pUp, i*2+2, pOld); } if( rc==SQLITE_OK && pNew ){ - rc = sessionBindValue(p->pUpdate, i*3+3, pNew); + rc = sessionBindValue(pUp, i*2+1, pNew); } } - if( rc==SQLITE_OK ){ - sqlcipher_sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0 || pIter->bPatchset); - } if( rc!=SQLITE_OK ) return rc; /* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict, ** the result will be SQLITE_OK with 0 rows modified. */ - sqlcipher_sqlite3_step(p->pUpdate); - rc = sqlcipher_sqlite3_reset(p->pUpdate); + sqlcipher_sqlite3_step(pUp); + rc = sqlcipher_sqlite3_reset(pUp); if( rc==SQLITE_OK && sqlcipher_sqlite3_changes(p->db)==0 ){ /* A NOTFOUND or DATA error. Search the table to see if it contains @@ -212498,7 +222661,7 @@ static int sessionRetryConstraints( memset(&pApply->constraints, 0, sizeof(SessionBuffer)); rc = sessionChangesetStart( - &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints + &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints, 1 ); if( rc==SQLITE_OK ){ size_t nByte = 2*pApply->nCol*sizeof(sqlcipher_sqlite3_value*); @@ -212589,14 +222752,13 @@ static int sessionChangesetApply( ); if( rc!=SQLITE_OK ) break; + sessionUpdateFree(&sApply); sqlcipher_sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ sqlcipher_sqlite3_finalize(sApply.pDelete); - sqlcipher_sqlite3_finalize(sApply.pUpdate); sqlcipher_sqlite3_finalize(sApply.pInsert); sqlcipher_sqlite3_finalize(sApply.pSelect); sApply.db = db; sApply.pDelete = 0; - sApply.pUpdate = 0; sApply.pInsert = 0; sApply.pSelect = 0; sApply.nCol = 0; @@ -212624,7 +222786,7 @@ static int sessionChangesetApply( int i; sqlcipher_sqlite3changeset_pk(pIter, &abPK, 0); - rc = sessionTableInfo( + rc = sessionTableInfo(0, db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK ); if( rc!=SQLITE_OK ) break; @@ -212660,11 +222822,10 @@ static int sessionChangesetApply( } sApply.bStat1 = 1; }else{ - if((rc = sessionSelectRow(db, zTab, &sApply)) - || (rc = sessionUpdateRow(db, zTab, &sApply)) - || (rc = sessionDeleteRow(db, zTab, &sApply)) - || (rc = sessionInsertRow(db, zTab, &sApply)) - ){ + if( (rc = sessionSelectRow(db, zTab, &sApply)) + || (rc = sessionDeleteRow(db, zTab, &sApply)) + || (rc = sessionInsertRow(db, zTab, &sApply)) + ){ break; } sApply.bStat1 = 0; @@ -212723,9 +222884,9 @@ static int sessionChangesetApply( *pnRebase = sApply.rebase.nBuf; sApply.rebase.aBuf = 0; } + sessionUpdateFree(&sApply); sqlcipher_sqlite3_finalize(sApply.pInsert); sqlcipher_sqlite3_finalize(sApply.pDelete); - sqlcipher_sqlite3_finalize(sApply.pUpdate); sqlcipher_sqlite3_finalize(sApply.pSelect); sqlcipher_sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ sqlcipher_sqlite3_free((char*)sApply.constraints.aBuf); @@ -212756,8 +222917,8 @@ SQLITE_API int sqlcipher_sqlite3changeset_apply_v2( int flags ){ sqlcipher_sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ - int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); - int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset,bInverse); + int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); + int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1); if( rc==SQLITE_OK ){ rc = sessionChangesetApply( db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags @@ -212815,7 +222976,7 @@ SQLITE_API int sqlcipher_sqlite3changeset_apply_v2_strm( ){ sqlcipher_sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); - int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse); + int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse, 1); if( rc==SQLITE_OK ){ rc = sessionChangesetApply( db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags @@ -213103,7 +223264,7 @@ static int sessionChangesetToHash( } } - if( sessionGrowHash(pIter->bPatchset, pTab) ){ + if( sessionGrowHash(0, pIter->bPatchset, pTab) ){ rc = SQLITE_NOMEM; break; } @@ -213199,9 +223360,9 @@ static int sessionChangegroupOutput( if( rc==SQLITE_OK ){ if( xOutput ){ if( buf.nBuf>0 ) rc = xOutput(pOut, buf.aBuf, buf.nBuf); - }else{ + }else if( ppOut ){ *ppOut = buf.aBuf; - *pnOut = buf.nBuf; + if( pnOut ) *pnOut = buf.nBuf; buf.aBuf = 0; } } @@ -213289,7 +223450,7 @@ SQLITE_API int sqlcipher_sqlite3changegroup_output_strm( */ SQLITE_API void sqlcipher_sqlite3changegroup_delete(sqlcipher_sqlite3_changegroup *pGrp){ if( pGrp ){ - sessionDeleteTable(pGrp->pList); + sessionDeleteTable(0, pGrp->pList); sqlcipher_sqlite3_free(pGrp); } } @@ -213435,7 +223596,7 @@ static void sessionAppendPartialUpdate( int n1 = sessionSerialLen(a1); int n2 = sessionSerialLen(a2); if( pIter->abPK[i] || a2[0]==0 ){ - if( !pIter->abPK[i] ) bData = 1; + if( !pIter->abPK[i] && a1[0] ) bData = 1; memcpy(pOut, a1, n1); pOut += n1; }else if( a2[0]!=0xFF ){ @@ -213601,7 +223762,7 @@ static int sessionRebase( if( sOut.nBuf>0 ){ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); } - }else{ + }else if( ppOut ){ *ppOut = (void*)sOut.aBuf; *pnOut = sOut.nBuf; sOut.aBuf = 0; @@ -213690,7 +223851,7 @@ SQLITE_API int sqlcipher_sqlite3rebaser_rebase_strm( */ SQLITE_API void sqlcipher_sqlite3rebaser_delete(sqlcipher_sqlite3_rebaser *p){ if( p ){ - sessionDeleteTable(p->grp.pList); + sessionDeleteTable(0, p->grp.pList); sqlcipher_sqlite3_free(p); } } @@ -214344,8 +224505,20 @@ typedef sqlcipher_sqlite3_uint64 u64; #endif #define testcase(x) -#define ALWAYS(x) 1 -#define NEVER(x) 0 + +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 +#endif +#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) +# 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 #define MIN(x,y) (((x) < (y)) ? (x) : (y)) #define MAX(x,y) (((x) > (y)) ? (x) : (y)) @@ -214405,7 +224578,7 @@ SQLITE_API extern int sqlcipher_sqlite3_fts5_may_be_corrupt; ** A version of memcmp() that does not cause asan errors if one of the pointer ** parameters is NULL and the number of bytes to compare is zero. */ -#define fts5Memcmp(s1, s2, n) ((n)==0 ? 0 : memcmp((s1), (s2), (n))) +#define fts5Memcmp(s1, s2, n) ((n)<=0 ? 0 : memcmp((s1), (s2), (n))) /* Mark a function parameter as unused, to suppress nuisance compiler ** warnings. */ @@ -214744,6 +224917,9 @@ static void sqlcipher_sqlite3Fts5IndexCloseReader(Fts5Index*); */ static const char *sqlcipher_sqlite3Fts5IterTerm(Fts5IndexIter*, int*); static int sqlcipher_sqlite3Fts5IterNextScan(Fts5IndexIter*); +static void *sqlcipher_sqlite3Fts5StructureRef(Fts5Index*); +static void sqlcipher_sqlite3Fts5StructureRelease(void*); +static int sqlcipher_sqlite3Fts5StructureTest(Fts5Index*, void*); /* @@ -215521,9 +225697,9 @@ struct fts5yyParser { }; typedef struct fts5yyParser fts5yyParser; +/* #include */ #ifndef NDEBUG /* #include */ -/* #include */ static FILE *fts5yyTraceFILE = 0; static char *fts5yyTracePrompt = 0; #endif /* NDEBUG */ @@ -216152,55 +226328,6 @@ static fts5YYACTIONTYPE fts5yy_reduce( (void)fts5yyLookahead; (void)fts5yyLookaheadToken; fts5yymsp = fts5yypParser->fts5yytos; - assert( fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ); -#ifndef NDEBUG - if( fts5yyTraceFILE ){ - fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno]; - if( fts5yysize ){ - fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", - fts5yyTracePrompt, - fts5yyruleno, fts5yyRuleName[fts5yyruleno], - fts5yyrulenofts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){ - fts5yypParser->fts5yyhwm++; - assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)); - } -#endif -#if fts5YYSTACKDEPTH>0 - if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){ - fts5yyStackOverflow(fts5yypParser); - /* The call to fts5yyStackOverflow() above pops the stack until it is - ** empty, causing the main parser loop to exit. So the return value - ** is never used and does not matter. */ - return 0; - } -#else - if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){ - if( fts5yyGrowStack(fts5yypParser) ){ - fts5yyStackOverflow(fts5yypParser); - /* The call to fts5yyStackOverflow() above pops the stack until it is - ** empty, causing the main parser loop to exit. So the return value - ** is never used and does not matter. */ - return 0; - } - fts5yymsp = fts5yypParser->fts5yytos; - } -#endif - } switch( fts5yyruleno ){ /* Beginning here are the reduction cases. A typical example @@ -216503,12 +226630,56 @@ static void sqlcipher_sqlite3Fts5Parser( } #endif - do{ + while(1){ /* Exit by "break" */ + assert( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystack ); assert( fts5yyact==fts5yypParser->fts5yytos->stateno ); fts5yyact = fts5yy_find_shift_action((fts5YYCODETYPE)fts5yymajor,fts5yyact); if( fts5yyact >= fts5YY_MIN_REDUCE ){ - fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyact-fts5YY_MIN_REDUCE,fts5yymajor, - fts5yyminor sqlcipher_sqlite3Fts5ParserCTX_PARAM); + unsigned int fts5yyruleno = fts5yyact - fts5YY_MIN_REDUCE; /* Reduce by this rule */ +#ifndef NDEBUG + assert( fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ); + if( fts5yyTraceFILE ){ + int fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno]; + if( fts5yysize ){ + fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", + fts5yyTracePrompt, + fts5yyruleno, fts5yyRuleName[fts5yyruleno], + fts5yyrulenofts5yytos[fts5yysize].stateno); + }else{ + fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s.\n", + fts5yyTracePrompt, fts5yyruleno, fts5yyRuleName[fts5yyruleno], + fts5yyrulenofts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){ + fts5yypParser->fts5yyhwm++; + assert( fts5yypParser->fts5yyhwm == + (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)); + } +#endif +#if fts5YYSTACKDEPTH>0 + if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){ + fts5yyStackOverflow(fts5yypParser); + break; + } +#else + if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){ + if( fts5yyGrowStack(fts5yypParser) ){ + fts5yyStackOverflow(fts5yypParser); + break; + } + } +#endif + } + fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyruleno,fts5yymajor,fts5yyminor sqlcipher_sqlite3Fts5ParserCTX_PARAM); }else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){ fts5yy_shift(fts5yypParser,fts5yyact,(fts5YYCODETYPE)fts5yymajor,fts5yyminor); #ifndef fts5YYNOERRORRECOVERY @@ -216564,14 +226735,13 @@ static void sqlcipher_sqlite3Fts5Parser( fts5yy_destructor(fts5yypParser, (fts5YYCODETYPE)fts5yymajor, &fts5yyminorunion); fts5yymajor = fts5YYNOCODE; }else{ - while( fts5yypParser->fts5yytos >= fts5yypParser->fts5yystack - && (fts5yyact = fts5yy_find_reduce_action( - fts5yypParser->fts5yytos->stateno, - fts5YYERRORSYMBOL)) > fts5YY_MAX_SHIFTREDUCE - ){ + while( fts5yypParser->fts5yytos > fts5yypParser->fts5yystack ){ + fts5yyact = fts5yy_find_reduce_action(fts5yypParser->fts5yytos->stateno, + fts5YYERRORSYMBOL); + if( fts5yyact<=fts5YY_MAX_SHIFTREDUCE ) break; fts5yy_pop_parser_stack(fts5yypParser); } - if( fts5yypParser->fts5yytos < fts5yypParser->fts5yystack || fts5yymajor==0 ){ + if( fts5yypParser->fts5yytos <= fts5yypParser->fts5yystack || fts5yymajor==0 ){ fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion); fts5yy_parse_failed(fts5yypParser); #ifndef fts5YYNOERRORRECOVERY @@ -216621,7 +226791,7 @@ static void sqlcipher_sqlite3Fts5Parser( break; #endif } - }while( fts5yypParser->fts5yytos>fts5yypParser->fts5yystack ); + } #ifndef NDEBUG if( fts5yyTraceFILE ){ fts5yyStackEntry *i; @@ -217434,7 +227604,6 @@ static void sqlcipher_sqlite3Fts5BufferAppendBlob( u32 nData, const u8 *pData ){ - assert_nc( *pRc || nData>=0 ); if( nData ){ if( fts5BufferGrow(pRc, pBuf, nData) ) return; memcpy(&pBuf->p[pBuf->n], pData, nData); @@ -217544,7 +227713,7 @@ static int sqlcipher_sqlite3Fts5PoslistNext64( return 1; }else{ i64 iOff = *piOff; - int iVal; + u32 iVal; fts5FastGetVarint32(a, i, iVal); if( iVal<=1 ){ if( iVal==0 ){ @@ -217553,15 +227722,19 @@ static int sqlcipher_sqlite3Fts5PoslistNext64( } fts5FastGetVarint32(a, i, iVal); iOff = ((i64)iVal) << 32; + assert( iOff>=0 ); fts5FastGetVarint32(a, i, iVal); if( iVal<2 ){ /* This is a corrupt record. So stop parsing it here. */ *piOff = -1; return 1; } + *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); + }else{ + *piOff = (iOff & (i64)0x7FFFFFFF<<32)+((iOff + (iVal-2)) & 0x7FFFFFFF); } - *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); *pi = i; + assert_nc( *piOff>=iOff ); return 0; } } @@ -217600,14 +227773,16 @@ static void sqlcipher_sqlite3Fts5PoslistSafeAppend( i64 *piPrev, i64 iPos ){ - static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; - if( (iPos & colmask) != (*piPrev & colmask) ){ - pBuf->p[pBuf->n++] = 1; - pBuf->n += sqlcipher_sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); - *piPrev = (iPos & colmask); + if( iPos>=*piPrev ){ + static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; + if( (iPos & colmask) != (*piPrev & colmask) ){ + pBuf->p[pBuf->n++] = 1; + pBuf->n += sqlcipher_sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); + *piPrev = (iPos & colmask); + } + pBuf->n += sqlcipher_sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); + *piPrev = iPos; } - pBuf->n += sqlcipher_sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); - *piPrev = iPos; } static int sqlcipher_sqlite3Fts5PoslistWriterAppend( @@ -218309,7 +228484,7 @@ static int sqlcipher_sqlite3Fts5ConfigParse( nByte = nArg * (sizeof(char*) + sizeof(u8)); pRet->azCol = (char**)sqlcipher_sqlite3Fts5MallocZero(&rc, nByte); - pRet->abUnindexed = (u8*)&pRet->azCol[nArg]; + pRet->abUnindexed = pRet->azCol ? (u8*)&pRet->azCol[nArg] : 0; pRet->zDb = sqlcipher_sqlite3Fts5Strndup(&rc, azArg[1], -1); pRet->zName = sqlcipher_sqlite3Fts5Strndup(&rc, azArg[2], -1); pRet->bColumnsize = 1; @@ -218334,6 +228509,7 @@ static int sqlcipher_sqlite3Fts5ConfigParse( z = fts5ConfigSkipWhitespace(z); if( z && *z=='=' ){ bOption = 1; + assert( zOne!=0 ); z++; if( bMustBeCol ) z = 0; } @@ -218350,7 +228526,11 @@ static int sqlcipher_sqlite3Fts5ConfigParse( rc = SQLITE_ERROR; }else{ if( bOption ){ - rc = fts5ConfigParseSpecial(pGlobal, pRet, zOne, zTwo?zTwo:"", pzErr); + rc = fts5ConfigParseSpecial(pGlobal, pRet, + ALWAYS(zOne)?zOne:"", + zTwo?zTwo:"", + pzErr + ); }else{ rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr); zOne = 0; @@ -218868,6 +229048,7 @@ static void sqlcipher_sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, va_list ap; va_start(ap, zFmt); if( pParse->rc==SQLITE_OK ){ + assert( pParse->zErr==0 ); pParse->zErr = sqlcipher_sqlite3_vmprintf(zFmt, ap); pParse->rc = SQLITE_ERROR; } @@ -219166,6 +229347,7 @@ static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){ int bRetValid = 0; Fts5ExprTerm *p; + assert( pTerm ); assert( pTerm->pSynonym ); assert( bDesc==0 || bDesc==1 ); for(p=pTerm; p; p=p->pSynonym){ @@ -220233,8 +230415,8 @@ static int sqlcipher_sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFir } /* If the iterator is not at a real match, skip forward until it is. */ - while( pRoot->bNomatch ){ - assert( pRoot->bEof==0 && rc==SQLITE_OK ); + while( pRoot->bNomatch && rc==SQLITE_OK ){ + assert( pRoot->bEof==0 ); rc = fts5ExprNodeNext(p, pRoot, 0, 0); } return rc; @@ -220358,6 +230540,9 @@ static Fts5ExprNearset *sqlcipher_sqlite3Fts5ParseNearset( }else{ if( pRet->nPhrase>0 ){ Fts5ExprPhrase *pLast = pRet->apPhrase[pRet->nPhrase-1]; + assert( pParse!=0 ); + assert( pParse->apPhrase!=0 ); + assert( pParse->nPhrase>=2 ); assert( pLast==pParse->apPhrase[pParse->nPhrase-2] ); if( pPhrase->nTerm==0 ){ fts5ExprPhraseFree(pPhrase); @@ -220606,7 +230791,7 @@ static int sqlcipher_sqlite3Fts5ExprClonePhrase( sCtx.pPhrase = sqlcipher_sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase)); } - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ /* All the allocations succeeded. Put the expression object together. */ pNew->pIndex = pExpr->pIndex; pNew->pConfig = pExpr->pConfig; @@ -220877,9 +231062,8 @@ static void sqlcipher_sqlite3Fts5ParseSetColset( ){ Fts5Colset *pFree = pColset; if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){ - pParse->rc = SQLITE_ERROR; - pParse->zErr = sqlcipher_sqlite3_mprintf( - "fts5: column queries are not supported (detail=none)" + sqlcipher_sqlite3Fts5ParseError(pParse, + "fts5: column queries are not supported (detail=none)" ); }else{ fts5ParseSetColset(pParse, pExpr, pColset, &pFree); @@ -221053,13 +231237,10 @@ static Fts5ExprNode *sqlcipher_sqlite3Fts5ParseNode( || pPhrase->nTerm>1 || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst) ){ - assert( pParse->rc==SQLITE_OK ); - pParse->rc = SQLITE_ERROR; - assert( pParse->zErr==0 ); - pParse->zErr = sqlcipher_sqlite3_mprintf( + sqlcipher_sqlite3Fts5ParseError(pParse, "fts5: %s queries are not supported (detail!=full)", pNear->nPhrase==1 ? "phrase": "NEAR" - ); + ); sqlcipher_sqlite3_free(pRet); pRet = 0; } @@ -221145,6 +231326,7 @@ static Fts5ExprNode *sqlcipher_sqlite3Fts5ParseImplicitAnd( return pRet; } +#ifdef SQLITE_TEST static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ sqlcipher_sqlite3_int64 nByte = 0; Fts5ExprTerm *p; @@ -221511,12 +231693,14 @@ static void fts5ExprFold( sqlcipher_sqlite3_result_int(pCtx, sqlcipher_sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics)); } } +#endif /* ifdef SQLITE_TEST */ /* ** This is called during initialization to register the fts5_expr() scalar ** UDF with the SQLite handle passed as the only argument. */ static int sqlcipher_sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlcipher_sqlite3 *db){ +#ifdef SQLITE_TEST struct Fts5ExprFunc { const char *z; void (*x)(sqlcipher_sqlite3_context*,int,sqlcipher_sqlite3_value**); @@ -221534,6 +231718,10 @@ static int sqlcipher_sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlcipher_sqlite3 struct Fts5ExprFunc *p = &aFunc[i]; rc = sqlcipher_sqlite3_create_function(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0); } +#else + int rc = SQLITE_OK; + UNUSED_PARAM2(pGlobal,db); +#endif /* Avoid warnings indicating that sqlcipher_sqlite3Fts5ParserTrace() and ** sqlcipher_sqlite3Fts5ParserFallback() are unused */ @@ -221584,6 +231772,15 @@ struct Fts5PoslistPopulator { int bMiss; }; +/* +** Clear the position lists associated with all phrases in the expression +** passed as the first argument. Argument bLive is true if the expression +** might be pointing to a real entry, otherwise it has just been reset. +** +** At present this function is only used for detail=col and detail=none +** fts5 tables. This implies that all phrases must be at most 1 token +** in size, as phrase matches are not supported without detail=full. +*/ static Fts5PoslistPopulator *sqlcipher_sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){ Fts5PoslistPopulator *pRet; pRet = sqlcipher_sqlite3_malloc64(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); @@ -221593,7 +231790,7 @@ static Fts5PoslistPopulator *sqlcipher_sqlite3Fts5ExprClearPoslists(Fts5Expr *pE for(i=0; inPhrase; i++){ Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist; Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; - assert( pExpr->apExprPhrase[i]->nTerm==1 ); + assert( pExpr->apExprPhrase[i]->nTerm<=1 ); if( bLive && (pBuf->n==0 || pNode->iRowid!=pExpr->pRoot->iRowid || pNode->bEof) ){ @@ -222144,7 +232341,7 @@ static int sqlcipher_sqlite3Fts5HashWrite( p->bContent = 1; }else{ /* Append a new column value, if necessary */ - assert( iCol>=p->iCol ); + assert_nc( iCol>=p->iCol ); if( iCol!=p->iCol ){ if( pHash->eDetail==FTS5_DETAIL_FULL ){ pPtr[p->nData++] = 0x01; @@ -222645,7 +232842,7 @@ struct Fts5Index { sqlcipher_sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */ sqlcipher_sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */ sqlcipher_sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ - sqlcipher_sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=? */ + sqlcipher_sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ sqlcipher_sqlite3_stmt *pIdxSelect; int nRead; /* Total number of blocks read */ @@ -222780,7 +232977,7 @@ struct Fts5SegIter { int iLeafPgno; /* Current leaf page number */ Fts5Data *pLeaf; /* Current leaf data */ Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ - int iLeafOffset; /* Byte offset within current leaf */ + i64 iLeafOffset; /* Byte offset within current leaf */ /* Next method */ void (*xNext)(Fts5Index*, Fts5SegIter*, int*); @@ -222949,8 +233146,11 @@ static int fts5BufferCompareBlob( ** res = *pLeft - *pRight */ static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){ - int nCmp = MIN(pLeft->n, pRight->n); - int res = fts5Memcmp(pLeft->p, pRight->p, nCmp); + int nCmp, res; + nCmp = MIN(pLeft->n, pRight->n); + assert( nCmp<=0 || pLeft->p!=0 ); + assert( nCmp<=0 || pRight->p!=0 ); + res = fts5Memcmp(pLeft->p, pRight->p, nCmp); return (res==0 ? (pLeft->n - pRight->n) : res); } @@ -223046,6 +233246,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ return pRet; } + /* ** Release a reference to data record returned by an earlier call to ** fts5DataRead(). @@ -223170,6 +233371,58 @@ static void fts5StructureRef(Fts5Structure *pStruct){ pStruct->nRef++; } +static void *sqlcipher_sqlite3Fts5StructureRef(Fts5Index *p){ + fts5StructureRef(p->pStruct); + return (void*)p->pStruct; +} +static void sqlcipher_sqlite3Fts5StructureRelease(void *p){ + if( p ){ + fts5StructureRelease((Fts5Structure*)p); + } +} +static int sqlcipher_sqlite3Fts5StructureTest(Fts5Index *p, void *pStruct){ + if( p->pStruct!=(Fts5Structure*)pStruct ){ + return SQLITE_ABORT; + } + return SQLITE_OK; +} + +/* +** Ensure that structure object (*pp) is writable. +** +** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. If +** an error occurs, (*pRc) is set to an SQLite error code before returning. +*/ +static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ + Fts5Structure *p = *pp; + if( *pRc==SQLITE_OK && p->nRef>1 ){ + i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel); + Fts5Structure *pNew; + pNew = (Fts5Structure*)sqlcipher_sqlite3Fts5MallocZero(pRc, nByte); + if( pNew ){ + int i; + memcpy(pNew, p, nByte); + for(i=0; inLevel; i++) pNew->aLevel[i].aSeg = 0; + for(i=0; inLevel; i++){ + Fts5StructureLevel *pLvl = &pNew->aLevel[i]; + nByte = sizeof(Fts5StructureSegment) * pNew->aLevel[i].nSeg; + pLvl->aSeg = (Fts5StructureSegment*)sqlcipher_sqlite3Fts5MallocZero(pRc, nByte); + if( pLvl->aSeg==0 ){ + for(i=0; inLevel; i++){ + sqlcipher_sqlite3_free(pNew->aLevel[i].aSeg); + } + sqlcipher_sqlite3_free(pNew); + return; + } + memcpy(pLvl->aSeg, p->aLevel[i].aSeg, nByte); + } + p->nRef--; + pNew->nRef = 1; + } + *pp = pNew; + } +} + /* ** Deserialize and return the structure record currently stored in serialized ** form within buffer pData/nData. @@ -223271,9 +233524,11 @@ static int fts5StructureDecode( } /* -** +** Add a level to the Fts5Structure.aLevel[] array of structure object +** (*ppStruct). */ static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ + fts5StructureMakeWritable(pRc, ppStruct); if( *pRc==SQLITE_OK ){ Fts5Structure *pStruct = *ppStruct; int nLevel = pStruct->nLevel; @@ -223960,7 +234215,7 @@ static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ - int iOff = pIter->iLeafOffset; + i64 iOff = pIter->iLeafOffset; ASSERT_SZLEAF_OK(pIter->pLeaf); if( iOff>=pIter->pLeaf->szLeaf ){ @@ -223993,7 +234248,7 @@ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ */ static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){ u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ - int iOff = pIter->iLeafOffset; /* Offset to read at */ + i64 iOff = pIter->iLeafOffset; /* Offset to read at */ int nNew; /* Bytes of new data */ iOff += fts5GetVarint32(&a[iOff], nNew); @@ -224067,6 +234322,7 @@ static void fts5SegIterInit( if( p->rc==SQLITE_OK ){ pIter->iLeafOffset = 4; + assert( pIter->pLeaf!=0 ); assert_nc( pIter->pLeaf->nn>4 ); assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 ); pIter->iPgidxOff = pIter->pLeaf->szLeaf+1; @@ -224169,8 +234425,12 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){ int iRowidOff; iRowidOff = fts5LeafFirstRowidOff(pNew); if( iRowidOff ){ - pIter->pLeaf = pNew; - pIter->iLeafOffset = iRowidOff; + if( iRowidOff>=pNew->szLeaf ){ + p->rc = FTS5_CORRUPT; + }else{ + pIter->pLeaf = pNew; + pIter->iLeafOffset = iRowidOff; + } } } @@ -224419,14 +234679,9 @@ static void fts5SegIterNext( }else{ /* The following could be done by calling fts5SegIterLoadNPos(). But ** this block is particularly performance critical, so equivalent - ** code is inlined. - ** - ** Later: Switched back to fts5SegIterLoadNPos() because it supports - ** detail=none mode. Not ideal. - */ + ** code is inlined. */ int nSz; - assert( p->rc==SQLITE_OK ); - assert( pIter->iLeafOffset<=pIter->pLeaf->nn ); + assert_nc( pIter->iLeafOffset<=pIter->pLeaf->nn ); fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz); pIter->bDel = (nSz & 0x0001); pIter->nPos = nSz>>1; @@ -224455,7 +234710,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ if( pDlidx ){ int iSegid = pIter->pSeg->iSegid; pgnoLast = fts5DlidxIterPgno(pDlidx); - pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); + pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); }else{ Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ @@ -224482,7 +234737,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ ** forward to find the page containing the last rowid. */ for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){ i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno); - Fts5Data *pNew = fts5DataRead(p, iAbs); + Fts5Data *pNew = fts5LeafRead(p, iAbs); if( pNew ){ int iRowid, bTermless; iRowid = fts5LeafFirstRowidOff(pNew); @@ -224513,6 +234768,10 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ pIter->pLeaf = pLast; pIter->iLeafPgno = pgnoLast; iOff = fts5LeafFirstRowidOff(pLast); + if( iOff>pLast->szLeaf ){ + p->rc = FTS5_CORRUPT; + return; + } iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid); pIter->iLeafOffset = iOff; @@ -224521,7 +234780,6 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ }else{ pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast); } - } fts5SegIterReverseInitPage(p, pIter); @@ -224573,21 +234831,20 @@ static void fts5LeafSeek( Fts5SegIter *pIter, /* Iterator to seek */ const u8 *pTerm, int nTerm /* Term to search for */ ){ - int iOff; + u32 iOff; const u8 *a = pIter->pLeaf->p; - int szLeaf = pIter->pLeaf->szLeaf; - int n = pIter->pLeaf->nn; + u32 n = (u32)pIter->pLeaf->nn; u32 nMatch = 0; u32 nKeep = 0; u32 nNew = 0; u32 iTermOff; - int iPgidx; /* Current offset in pgidx */ + u32 iPgidx; /* Current offset in pgidx */ int bEndOfPage = 0; assert( p->rc==SQLITE_OK ); - iPgidx = szLeaf; + iPgidx = (u32)pIter->pLeaf->szLeaf; iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff); iOff = iTermOff; if( iOff>n ){ @@ -224653,15 +234910,15 @@ static void fts5LeafSeek( if( pIter->pLeaf==0 ) return; a = pIter->pLeaf->p; if( fts5LeafIsTermless(pIter->pLeaf)==0 ){ - iPgidx = pIter->pLeaf->szLeaf; + iPgidx = (u32)pIter->pLeaf->szLeaf; iPgidx += fts5GetVarint32(&pIter->pLeaf->p[iPgidx], iOff); - if( iOff<4 || iOff>=pIter->pLeaf->szLeaf ){ + if( iOff<4 || (i64)iOff>=pIter->pLeaf->szLeaf ){ p->rc = FTS5_CORRUPT; return; }else{ nKeep = 0; iTermOff = iOff; - n = pIter->pLeaf->nn; + n = (u32)pIter->pLeaf->nn; iOff += fts5GetVarint32(&a[iOff], nNew); break; } @@ -225029,7 +235286,7 @@ static void fts5SegIterGotoPage( fts5SegIterNextPage(p, pIter); assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno ); - if( p->rc==SQLITE_OK ){ + if( p->rc==SQLITE_OK && ALWAYS(pIter->pLeaf!=0) ){ int iOff; u8 *a = pIter->pLeaf->p; int n = pIter->pLeaf->szLeaf; @@ -225418,7 +235675,7 @@ static void fts5ChunkIterate( int pgno = pSeg->iLeafPgno; int pgnoSave = 0; - /* This function does notmwork with detail=none databases. */ + /* This function does not work with detail=none databases. */ assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE ); if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){ @@ -225431,6 +235688,9 @@ static void fts5ChunkIterate( fts5DataRelease(pData); if( nRem<=0 ){ break; + }else if( pSeg->pSeg==0 ){ + p->rc = FTS5_CORRUPT; + return; }else{ pgno++; pData = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno)); @@ -225458,7 +235718,11 @@ static void fts5SegiterPoslist( Fts5Colset *pColset, Fts5Buffer *pBuf ){ + assert( pBuf!=0 ); + assert( pSeg!=0 ); if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+FTS5_DATA_ZERO_PADDING) ){ + assert( pBuf->p!=0 ); + assert( pBuf->nSpace >= pBuf->n+pSeg->nPos+FTS5_DATA_ZERO_PADDING ); memset(&pBuf->p[pBuf->n+pSeg->nPos], 0, FTS5_DATA_ZERO_PADDING); if( pColset==0 ){ fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback); @@ -225482,66 +235746,72 @@ static void fts5SegiterPoslist( } /* -** IN/OUT parameter (*pa) points to a position list n bytes in size. If -** the position list contains entries for column iCol, then (*pa) is set -** to point to the sub-position-list for that column and the number of -** bytes in it returned. Or, if the argument position list does not -** contain any entries for column iCol, return 0. +** Parameter pPos points to a buffer containing a position list, size nPos. +** This function filters it according to pColset (which must be non-NULL) +** and sets pIter->base.pData/nData to point to the new position list. +** If memory is required for the new position list, use buffer pIter->poslist. +** Or, if the new position list is a contiguous subset of the input, set +** pIter->base.pData/nData to point directly to it. +** +** This function is a no-op if *pRc is other than SQLITE_OK when it is +** called. If an OOM error is encountered, *pRc is set to SQLITE_NOMEM +** before returning. */ -static int fts5IndexExtractCol( - const u8 **pa, /* IN/OUT: Pointer to poslist */ - int n, /* IN: Size of poslist in bytes */ - int iCol /* Column to extract from poslist */ -){ - int iCurrent = 0; /* Anything before the first 0x01 is col 0 */ - const u8 *p = *pa; - const u8 *pEnd = &p[n]; /* One byte past end of position list */ - - while( iCol>iCurrent ){ - /* Advance pointer p until it points to pEnd or an 0x01 byte that is - ** not part of a varint. Note that it is not possible for a negative - ** or extremely large varint to occur within an uncorrupted position - ** list. So the last byte of each varint may be assumed to have a clear - ** 0x80 bit. */ - while( *p!=0x01 ){ - while( *p++ & 0x80 ); - if( p>=pEnd ) return 0; - } - *pa = p++; - iCurrent = *p++; - if( iCurrent & 0x80 ){ - p--; - p += fts5GetVarint32(p, iCurrent); - } - } - if( iCol!=iCurrent ) return 0; - - /* Advance pointer p until it points to pEnd or an 0x01 byte that is - ** not part of a varint */ - while( pnCol; i++){ - const u8 *pSub = pPos; - int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]); - if( nSub ){ - fts5BufferAppendBlob(pRc, pBuf, nSub, pSub); + const u8 *p = pPos; + const u8 *aCopy = p; + const u8 *pEnd = &p[nPos]; /* One byte past end of position list */ + int i = 0; + int iCurrent = 0; + + if( pColset->nCol>1 && sqlcipher_sqlite3Fts5BufferSize(pRc, &pIter->poslist, nPos) ){ + return; + } + + while( 1 ){ + while( pColset->aiCol[i]nCol ){ + pIter->base.pData = pIter->poslist.p; + pIter->base.nData = pIter->poslist.n; + return; + } + } + + /* Advance pointer p until it points to pEnd or an 0x01 byte that is + ** not part of a varint */ + while( paiCol[i]==iCurrent ){ + if( pColset->nCol==1 ){ + pIter->base.pData = aCopy; + pIter->base.nData = p-aCopy; + return; + } + fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy); + } + if( p>=pEnd ){ + pIter->base.pData = pIter->poslist.p; + pIter->base.nData = pIter->poslist.n; + return; + } + aCopy = p++; + iCurrent = *p++; + if( iCurrent & 0x80 ){ + p--; + p += fts5GetVarint32(p, iCurrent); } } } + } /* @@ -225661,16 +235931,9 @@ static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){ /* All data is stored on the current page. Populate the output ** variables to point into the body of the page object. */ const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset]; - if( pColset->nCol==1 ){ - pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]); - pIter->base.pData = a; - }else{ - int *pRc = &pIter->pIndex->rc; - fts5BufferZero(&pIter->poslist); - fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, &pIter->poslist); - pIter->base.pData = pIter->poslist.p; - pIter->base.nData = pIter->poslist.n; - } + int *pRc = &pIter->pIndex->rc; + fts5BufferZero(&pIter->poslist); + fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, pIter); }else{ /* The data is distributed over two or more pages. Copy it into the ** Fts5Iter.poslist buffer and then set the output pointer to point @@ -225683,6 +235946,7 @@ static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){ } static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){ + assert( pIter!=0 || (*pRc)!=SQLITE_OK ); if( *pRc==SQLITE_OK ){ Fts5Config *pConfig = pIter->pIndex->pConfig; if( pConfig->eDetail==FTS5_DETAIL_NONE ){ @@ -225754,7 +236018,10 @@ static void fts5MultiIterNew( } } *ppOut = pNew = fts5MultiIterAlloc(p, nSeg); - if( pNew==0 ) return; + if( pNew==0 ){ + assert( p->rc!=SQLITE_OK ); + goto fts5MultiIterNew_post_check; + } pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC)); pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY)); pNew->pColset = pColset; @@ -225818,6 +236085,10 @@ static void fts5MultiIterNew( fts5MultiIterFree(pNew); *ppOut = 0; } + +fts5MultiIterNew_post_check: + assert( (*ppOut)!=0 || p->rc!=SQLITE_OK ); + return; } /* @@ -225865,7 +236136,8 @@ static void fts5MultiIterNew2( ** False otherwise. */ static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){ - assert( p->rc + assert( pIter!=0 || p->rc!=SQLITE_OK ); + assert( p->rc!=SQLITE_OK || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof ); return (p->rc || pIter->base.bEof); @@ -226669,6 +236941,7 @@ static void fts5IndexMergeLevel( ** and last leaf page number at the same time. */ fts5WriteFinish(p, &writer, &pSeg->pgnoLast); + assert( pIter!=0 || p->rc!=SQLITE_OK ); if( fts5MultiIterEof(p, pIter) ){ int i; @@ -226769,7 +237042,7 @@ static void fts5IndexAutomerge( Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */ int nLeaf /* Number of output leaves just written */ ){ - if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 ){ + if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 && ALWAYS((*ppStruct)!=0) ){ Fts5Structure *pStruct = *ppStruct; u64 nWrite; /* Initial value of write-counter */ int nWork; /* Number of work-quanta to perform */ @@ -226892,14 +237165,14 @@ static void fts5FlushOneHash(Fts5Index *p){ fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); }else{ i64 iRowid = 0; - i64 iDelta = 0; + u64 iDelta = 0; int iOff = 0; /* The entire doclist will not fit on this leaf. The following ** loop iterates through the poslists that make up the current ** doclist. */ while( p->rc==SQLITE_OK && iOffaPoslist + pIter->nSize + pIter->nPoslist; - assert( pIter->aPoslist ); + assert( pIter->aPoslist || (p==0 && pIter->aPoslist==0) ); if( p>=pIter->aEof ){ pIter->aPoslist = 0; }else{ @@ -227173,6 +237446,9 @@ static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ } pIter->aPoslist = p; + if( &pIter->aPoslist[pIter->nPoslist]>pIter->aEof ){ + pIter->aPoslist = 0; + } } } @@ -227181,9 +237457,11 @@ static void fts5DoclistIterInit( Fts5DoclistIter *pIter ){ memset(pIter, 0, sizeof(*pIter)); - pIter->aPoslist = pBuf->p; - pIter->aEof = &pBuf->p[pBuf->n]; - fts5DoclistIterNext(pIter); + if( pBuf->n>0 ){ + pIter->aPoslist = pBuf->p; + pIter->aEof = &pBuf->p[pBuf->n]; + fts5DoclistIterNext(pIter); + } } #if 0 @@ -227237,16 +237515,20 @@ static void fts5NextRowid(Fts5Buffer *pBuf, int *piOff, i64 *piRowid){ static void fts5MergeRowidLists( Fts5Index *p, /* FTS5 backend object */ Fts5Buffer *p1, /* First list to merge */ - Fts5Buffer *p2 /* Second list to merge */ + int nBuf, /* Number of entries in apBuf[] */ + Fts5Buffer *aBuf /* Array of other lists to merge into p1 */ ){ int i1 = 0; int i2 = 0; i64 iRowid1 = 0; i64 iRowid2 = 0; i64 iOut = 0; - + Fts5Buffer *p2 = &aBuf[0]; Fts5Buffer out; + + (void)nBuf; memset(&out, 0, sizeof(out)); + assert( nBuf==1 ); sqlcipher_sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n); if( p->rc ) return; @@ -227273,180 +237555,214 @@ static void fts5MergeRowidLists( fts5BufferFree(&out); } +typedef struct PrefixMerger PrefixMerger; +struct PrefixMerger { + Fts5DoclistIter iter; /* Doclist iterator */ + i64 iPos; /* For iterating through a position list */ + int iOff; + u8 *aPos; + PrefixMerger *pNext; /* Next in docid/poslist order */ +}; + +static void fts5PrefixMergerInsertByRowid( + PrefixMerger **ppHead, + PrefixMerger *p +){ + if( p->iter.aPoslist ){ + PrefixMerger **pp = ppHead; + while( *pp && p->iter.iRowid>(*pp)->iter.iRowid ){ + pp = &(*pp)->pNext; + } + p->pNext = *pp; + *pp = p; + } +} + +static void fts5PrefixMergerInsertByPosition( + PrefixMerger **ppHead, + PrefixMerger *p +){ + if( p->iPos>=0 ){ + PrefixMerger **pp = ppHead; + while( *pp && p->iPos>(*pp)->iPos ){ + pp = &(*pp)->pNext; + } + p->pNext = *pp; + *pp = p; + } +} + + /* -** Buffers p1 and p2 contain doclists. This function merges the content -** of the two doclists together and sets buffer p1 to the result before -** returning. -** -** If an error occurs, an error code is left in p->rc. If an error has -** already occurred, this function is a no-op. +** Array aBuf[] contains nBuf doclists. These are all merged in with the +** doclist in buffer p1. */ static void fts5MergePrefixLists( Fts5Index *p, /* FTS5 backend object */ Fts5Buffer *p1, /* First list to merge */ - Fts5Buffer *p2 /* Second list to merge */ -){ - if( p2->n ){ - i64 iLastRowid = 0; - Fts5DoclistIter i1; - Fts5DoclistIter i2; - Fts5Buffer out = {0, 0, 0}; - Fts5Buffer tmp = {0, 0, 0}; - - /* The maximum size of the output is equal to the sum of the two - ** input sizes + 1 varint (9 bytes). The extra varint is because if the - ** first rowid in one input is a large negative number, and the first in - ** the other a non-negative number, the delta for the non-negative - ** number will be larger on disk than the literal integer value - ** was. - ** - ** Or, if the input position-lists are corrupt, then the output might - ** include up to 2 extra 10-byte positions created by interpreting -1 - ** (the value PoslistNext64() uses for EOF) as a position and appending - ** it to the output. This can happen at most once for each input - ** position-list, hence two 10 byte paddings. */ - if( sqlcipher_sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n + 9+10+10) ) return; - fts5DoclistIterInit(p1, &i1); - fts5DoclistIterInit(p2, &i2); + int nBuf, /* Number of buffers in array aBuf[] */ + Fts5Buffer *aBuf /* Other lists to merge in */ +){ +#define fts5PrefixMergerNextPosition(p) \ + sqlcipher_sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos) +#define FTS5_MERGE_NLIST 16 + PrefixMerger aMerger[FTS5_MERGE_NLIST]; + PrefixMerger *pHead = 0; + int i; + int nOut = 0; + Fts5Buffer out = {0, 0, 0}; + Fts5Buffer tmp = {0, 0, 0}; + i64 iLastRowid = 0; + + /* Initialize a doclist-iterator for each input buffer. Arrange them in + ** a linked-list starting at pHead in ascending order of rowid. Avoid + ** linking any iterators already at EOF into the linked list at all. */ + assert( nBuf+1<=sizeof(aMerger)/sizeof(aMerger[0]) ); + memset(aMerger, 0, sizeof(PrefixMerger)*(nBuf+1)); + pHead = &aMerger[nBuf]; + fts5DoclistIterInit(p1, &pHead->iter); + for(i=0; in + 9 + 10*nBuf; + + /* The maximum size of the output is equal to the sum of the + ** input sizes + 1 varint (9 bytes). The extra varint is because if the + ** first rowid in one input is a large negative number, and the first in + ** the other a non-negative number, the delta for the non-negative + ** number will be larger on disk than the literal integer value + ** was. + ** + ** Or, if the input position-lists are corrupt, then the output might + ** include up to (nBuf+1) extra 10-byte positions created by interpreting -1 + ** (the value PoslistNext64() uses for EOF) as a position and appending + ** it to the output. This can happen at most once for each input + ** position-list, hence (nBuf+1) 10 byte paddings. */ + if( sqlcipher_sqlite3Fts5BufferSize(&p->rc, &out, nOut) ) return; + + while( pHead ){ + fts5MergeAppendDocid(&out, iLastRowid, pHead->iter.iRowid); + + if( pHead->pNext && iLastRowid==pHead->pNext->iter.iRowid ){ + /* Merge data from two or more poslists */ + i64 iPrev = 0; + int nTmp = FTS5_DATA_ZERO_PADDING; + int nMerge = 0; + PrefixMerger *pSave = pHead; + PrefixMerger *pThis = 0; + int nTail = 0; + + pHead = 0; + while( pSave && pSave->iter.iRowid==iLastRowid ){ + PrefixMerger *pNext = pSave->pNext; + pSave->iOff = 0; + pSave->iPos = 0; + pSave->aPos = &pSave->iter.aPoslist[pSave->iter.nSize]; + fts5PrefixMergerNextPosition(pSave); + nTmp += pSave->iter.nPoslist + 10; + nMerge++; + fts5PrefixMergerInsertByPosition(&pHead, pSave); + pSave = pNext; + } + + if( pHead==0 || pHead->pNext==0 ){ + p->rc = FTS5_CORRUPT; + break; + } - while( 1 ){ - if( i1.iRowidp) + (i2.aPoslist-p2->p)+9+10+10) ); - } - else if( i2.iRowid!=i1.iRowid ){ - /* Copy entry from i2 */ - fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid); - fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize); - fts5DoclistIterNext(&i2); - if( i2.aPoslist==0 ) break; - assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) ); + /* See the earlier comment in this function for an explanation of why + ** corrupt input position lists might cause the output to consume + ** at most nMerge*10 bytes of unexpected space. */ + if( sqlcipher_sqlite3Fts5BufferSize(&p->rc, &tmp, nTmp+nMerge*10) ){ + break; } - else{ - /* Merge the two position lists. */ - i64 iPos1 = 0; - i64 iPos2 = 0; - int iOff1 = 0; - int iOff2 = 0; - u8 *a1 = &i1.aPoslist[i1.nSize]; - u8 *a2 = &i2.aPoslist[i2.nSize]; - int nCopy; - u8 *aCopy; - - i64 iPrev = 0; - Fts5PoslistWriter writer; - memset(&writer, 0, sizeof(writer)); - - /* See the earlier comment in this function for an explanation of why - ** corrupt input position lists might cause the output to consume - ** at most 20 bytes of unexpected space. */ - fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid); - fts5BufferZero(&tmp); - sqlcipher_sqlite3Fts5BufferSize(&p->rc, &tmp, - i1.nPoslist + i2.nPoslist + 10 + 10 + FTS5_DATA_ZERO_PADDING - ); - if( p->rc ) break; + fts5BufferZero(&tmp); - sqlcipher_sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1); - sqlcipher_sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2); - assert_nc( iPos1>=0 && iPos2>=0 ); + pThis = pHead; + pHead = pThis->pNext; + sqlcipher_sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos); + fts5PrefixMergerNextPosition(pThis); + fts5PrefixMergerInsertByPosition(&pHead, pThis); - if( iPos1=0 && iPos2>=0 ){ - while( 1 ){ - if( iPos1pNext ){ + pThis = pHead; + if( pThis->iPos!=iPrev ){ + sqlcipher_sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos); } + fts5PrefixMergerNextPosition(pThis); + pHead = pThis->pNext; + fts5PrefixMergerInsertByPosition(&pHead, pThis); + } - if( iPos1>=0 ){ - if( iPos1!=iPrev ){ - sqlcipher_sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1); - } - aCopy = &a1[iOff1]; - nCopy = i1.nPoslist - iOff1; - }else{ - assert_nc( iPos2>=0 && iPos2!=iPrev ); - sqlcipher_sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2); - aCopy = &a2[iOff2]; - nCopy = i2.nPoslist - iOff2; - } - if( nCopy>0 ){ - fts5BufferSafeAppendBlob(&tmp, aCopy, nCopy); - } + if( pHead->iPos!=iPrev ){ + sqlcipher_sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pHead->iPos); + } + nTail = pHead->iter.nPoslist - pHead->iOff; - /* WRITEPOSLISTSIZE */ - assert_nc( tmp.n<=i1.nPoslist+i2.nPoslist ); - assert( tmp.n<=i1.nPoslist+i2.nPoslist+10+10 ); - if( tmp.n>i1.nPoslist+i2.nPoslist ){ - if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; - break; + /* WRITEPOSLISTSIZE */ + assert_nc( tmp.n+nTail<=nTmp ); + assert( tmp.n+nTail<=nTmp+nMerge*10 ); + if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING ){ + if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; + break; + } + fts5BufferSafeAppendVarint(&out, (tmp.n+nTail) * 2); + fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); + if( nTail>0 ){ + fts5BufferSafeAppendBlob(&out, &pHead->aPos[pHead->iOff], nTail); + } + + pHead = pSave; + for(i=0; iiter.aPoslist && pX->iter.iRowid==iLastRowid ){ + fts5DoclistIterNext(&pX->iter); + fts5PrefixMergerInsertByRowid(&pHead, pX); } - fts5BufferSafeAppendVarint(&out, tmp.n * 2); - fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); - fts5DoclistIterNext(&i1); - fts5DoclistIterNext(&i2); - assert_nc( out.n<=(p1->n+p2->n+9) ); - if( i1.aPoslist==0 || i2.aPoslist==0 ) break; - assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) ); } - } - if( i1.aPoslist ){ - fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid); - fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.aEof - i1.aPoslist); - } - else if( i2.aPoslist ){ - fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid); - fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.aEof - i2.aPoslist); + }else{ + /* Copy poslist from pHead to output */ + PrefixMerger *pThis = pHead; + Fts5DoclistIter *pI = &pThis->iter; + fts5BufferSafeAppendBlob(&out, pI->aPoslist, pI->nPoslist+pI->nSize); + fts5DoclistIterNext(pI); + pHead = pThis->pNext; + fts5PrefixMergerInsertByRowid(&pHead, pThis); } - assert_nc( out.n<=(p1->n+p2->n+9) ); - - fts5BufferFree(p1); - fts5BufferFree(&tmp); - memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING); - *p1 = out; } + + fts5BufferFree(p1); + fts5BufferFree(&tmp); + memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING); + *p1 = out; } static void fts5SetupPrefixIter( Fts5Index *p, /* Index to read from */ int bDesc, /* True for "ORDER BY rowid DESC" */ - const u8 *pToken, /* Buffer containing prefix to match */ + int iIdx, /* Index to scan for data */ + u8 *pToken, /* Buffer containing prefix to match */ int nToken, /* Size of buffer pToken in bytes */ Fts5Colset *pColset, /* Restrict matches to these columns */ Fts5Iter **ppIter /* OUT: New iterator */ ){ Fts5Structure *pStruct; Fts5Buffer *aBuf; - const int nBuf = 32; + int nBuf = 32; + int nMerge = 1; - void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*); + void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*); if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ xMerge = fts5MergeRowidLists; xAppend = fts5AppendRowid; }else{ + nMerge = FTS5_MERGE_NLIST-1; + nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ xMerge = fts5MergePrefixLists; xAppend = fts5AppendPoslist; } @@ -227466,6 +237782,27 @@ static void fts5SetupPrefixIter( int bNewTerm = 1; memset(&doclist, 0, sizeof(doclist)); + if( iIdx!=0 ){ + int dummy = 0; + const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT; + pToken[0] = FTS5_MAIN_PREFIX; + fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1); + fts5IterSetOutputCb(&p->rc, p1); + for(; + fts5MultiIterEof(p, p1)==0; + fts5MultiIterNext2(p, p1, &dummy) + ){ + Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; + p1->xSetOutputs(p1, pSeg); + if( p1->base.nData ){ + xAppend(p, p1->base.iRowid-iLastRowid, p1, &doclist); + iLastRowid = p1->base.iRowid; + } + } + fts5MultiIterFree(p1); + } + + pToken[0] = FTS5_MAIN_PREFIX + iIdx; fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); fts5IterSetOutputCb(&p->rc, p1); for( /* no-op */ ; @@ -227486,13 +237823,21 @@ static void fts5SetupPrefixIter( if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ - assert( ibase.iRowid; } - for(i=0; irc==SQLITE_OK ){ - xMerge(p, &doclist, &aBuf[i]); + xMerge(p, &doclist, nMerge, &aBuf[i]); + } + for(iFree=i; iFreerc, &buf, nToken+1)==0 ){ int iIdx = 0; /* Index to search */ - if( nToken ) memcpy(&buf.p[1], pToken, nToken); + int iPrefixIdx = 0; /* +1 prefix index */ + if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); /* Figure out which index to search and set iIdx accordingly. If this ** is a prefix query for which there is no prefix index, set iIdx to @@ -227782,7 +238132,9 @@ static int sqlcipher_sqlite3Fts5IndexQuery( if( flags & FTS5INDEX_QUERY_PREFIX ){ int nChar = fts5IndexCharlen(pToken, nToken); for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){ - if( pConfig->aPrefix[iIdx-1]==nChar ) break; + int nIdxChar = pConfig->aPrefix[iIdx-1]; + if( nIdxChar==nChar ) break; + if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx; } } @@ -227799,13 +238151,16 @@ static int sqlcipher_sqlite3Fts5IndexQuery( }else{ /* Scan multiple terms in the main index */ int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; - buf.p[0] = FTS5_MAIN_PREFIX; - fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet); - assert( p->rc!=SQLITE_OK || pRet->pColset==0 ); - fts5IterSetOutputCb(&p->rc, pRet); - if( p->rc==SQLITE_OK ){ - Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; - if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg); + fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet); + if( pRet==0 ){ + assert( p->rc!=SQLITE_OK ); + }else{ + assert( pRet->pColset==0 ); + fts5IterSetOutputCb(&p->rc, pRet); + if( p->rc==SQLITE_OK ){ + Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; + if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg); + } } } @@ -227873,8 +238228,9 @@ static int sqlcipher_sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMat static const char *sqlcipher_sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ int n; const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n); + assert_nc( z || n<=1 ); *pn = n-1; - return &z[1]; + return (z ? &z[1] : 0); } /* @@ -228052,7 +238408,7 @@ static int fts5QueryCksum( Fts5IndexIter *pIter = 0; int rc = sqlcipher_sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter); - while( rc==SQLITE_OK && 0==sqlcipher_sqlite3Fts5IterEof(pIter) ){ + while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlcipher_sqlite3Fts5IterEof(pIter) ){ i64 rowid = pIter->iRowid; if( eDetail==FTS5_DETAIL_NONE ){ @@ -228417,6 +238773,7 @@ static int sqlcipher_sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */ Fts5Iter *pIter; /* Used to iterate through entire index */ Fts5Structure *pStruct; /* Index structure */ + int iLvl, iSeg; #ifdef SQLITE_DEBUG /* Used by extra internal tests only run if NDEBUG is not defined */ @@ -228427,15 +238784,16 @@ static int sqlcipher_sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int /* Load the FTS index structure */ pStruct = fts5StructureRead(p); + if( pStruct==0 ){ + assert( p->rc!=SQLITE_OK ); + return fts5IndexReturn(p); + } /* Check that the internal nodes of each segment match the leaves */ - if( pStruct ){ - int iLvl, iSeg; - for(iLvl=0; iLvlnLevel; iLvl++){ - for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ - Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; - fts5IndexIntegrityCheckSegment(p, pSeg); - } + for(iLvl=0; iLvlnLevel; iLvl++){ + for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ + Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; + fts5IndexIntegrityCheckSegment(p, pSeg); } } @@ -228499,6 +238857,7 @@ static int sqlcipher_sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int ** function only. */ +#ifdef SQLITE_TEST /* ** Decode a segment-data rowid from the %_data table. This function is ** the opposite of macro FTS5_SEGMENT_ROWID(). @@ -228521,7 +238880,9 @@ static void fts5DecodeRowid( *piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B) - 1)); } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ int iSegid, iHeight, iPgno, bDlidx; /* Rowid compenents */ fts5DecodeRowid(iKey, &iSegid, &bDlidx, &iHeight, &iPgno); @@ -228539,7 +238900,9 @@ static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ ); } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST static void fts5DebugStructure( int *pRc, /* IN/OUT: error code */ Fts5Buffer *pBuf, @@ -228561,7 +238924,9 @@ static void fts5DebugStructure( sqlcipher_sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** This is part of the fts5_decode() debugging aid. ** @@ -228586,7 +238951,9 @@ static void fts5DecodeStructure( fts5DebugStructure(pRc, pBuf, p); fts5StructureRelease(p); } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** This is part of the fts5_decode() debugging aid. ** @@ -228609,7 +238976,9 @@ static void fts5DecodeAverages( zSpace = " "; } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** Buffer (a/n) is assumed to contain a list of serialized varints. Read ** each varint and append its string representation to buffer pBuf. Return @@ -228626,7 +238995,9 @@ static int fts5DecodePoslist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ } return iOff; } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** The start of buffer (a/n) contains the start of a doclist. The doclist ** may or may not finish within the buffer. This function appends a text @@ -228659,7 +239030,9 @@ static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ return iOff; } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** This function is part of the fts5_decode() debugging function. It is ** only ever used with detail=none tables. @@ -228700,7 +239073,9 @@ static void fts5DecodeRowidList( sqlcipher_sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp); } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** The implementation of user-defined scalar function fts5_decode(). */ @@ -228909,7 +239284,9 @@ static void fts5DecodeFunction( } fts5BufferFree(&s); } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** The implementation of user-defined scalar function fts5_rowid(). */ @@ -228943,6 +239320,7 @@ static void fts5RowidFunction( } } } +#endif /* SQLITE_TEST */ /* ** This is called as part of registering the FTS5 module with database @@ -228953,6 +239331,7 @@ static void fts5RowidFunction( ** SQLite error code is returned instead. */ static int sqlcipher_sqlite3Fts5IndexInit(sqlcipher_sqlite3 *db){ +#ifdef SQLITE_TEST int rc = sqlcipher_sqlite3_create_function( db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0 ); @@ -228970,6 +239349,10 @@ static int sqlcipher_sqlite3Fts5IndexInit(sqlcipher_sqlite3 *db){ ); } return rc; +#else + return SQLITE_OK; + UNUSED_PARAM(db); +#endif } @@ -229005,7 +239388,9 @@ static int sqlcipher_sqlite3Fts5IndexReset(Fts5Index *p){ ** assert() conditions in the fts5 code are activated - conditions that are ** only true if it is guaranteed that the fts5 database is not corrupt. */ +#ifdef SQLITE_DEBUG SQLITE_API int sqlcipher_sqlite3_fts5_may_be_corrupt = 1; +#endif typedef struct Fts5Auxdata Fts5Auxdata; @@ -229785,7 +240170,7 @@ static int fts5SorterNext(Fts5Cursor *pCsr){ rc = sqlcipher_sqlite3_step(pSorter->pStmt); if( rc==SQLITE_DONE ){ rc = SQLITE_OK; - CsrFlagSet(pCsr, FTS5CSR_EOF); + CsrFlagSet(pCsr, FTS5CSR_EOF|FTS5CSR_REQUIRE_CONTENT); }else if( rc==SQLITE_ROW ){ const u8 *a; const u8 *aBlob; @@ -230355,7 +240740,8 @@ static int fts5FilterMethod( pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg ); if( rc==SQLITE_OK ){ - if( pCsr->ePlan==FTS5_PLAN_ROWID ){ + if( pRowidEq!=0 ){ + assert( pCsr->ePlan==FTS5_PLAN_ROWID ); sqlcipher_sqlite3_bind_value(pCsr->pStmt, 1, pRowidEq); }else{ sqlcipher_sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid); @@ -230930,13 +241316,15 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ nInst++; if( nInst>=pCsr->nInstAlloc ){ - pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; + int nNewSize = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; aInst = (int*)sqlcipher_sqlite3_realloc64( - pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 + pCsr->aInst, nNewSize*sizeof(int)*3 ); if( aInst ){ pCsr->aInst = aInst; + pCsr->nInstAlloc = nNewSize; }else{ + nInst--; rc = SQLITE_NOMEM; break; } @@ -231160,7 +241548,8 @@ static int fts5ApiPhraseFirst( int n; int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); if( rc==SQLITE_OK ){ - pIter->b = &pIter->a[n]; + assert( pIter->a || n==0 ); + pIter->b = (pIter->a ? &pIter->a[n] : 0); *piCol = 0; *piOff = 0; fts5ApiPhraseNext(pCtx, pIter, piCol, piOff); @@ -231219,7 +241608,8 @@ static int fts5ApiPhraseFirstColumn( rc = sqlcipher_sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n); } if( rc==SQLITE_OK ){ - pIter->b = &pIter->a[n]; + assert( pIter->a || n==0 ); + pIter->b = (pIter->a ? &pIter->a[n] : 0); *piCol = 0; fts5ApiPhraseNextColumn(pCtx, pIter, piCol); } @@ -231227,7 +241617,8 @@ static int fts5ApiPhraseFirstColumn( int n; rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); if( rc==SQLITE_OK ){ - pIter->b = &pIter->a[n]; + assert( pIter->a || n==0 ); + pIter->b = (pIter->a ? &pIter->a[n] : 0); if( n<=0 ){ *piCol = -1; }else if( pIter->a[0]==0x01 ){ @@ -231705,7 +242096,7 @@ static int sqlcipher_sqlite3Fts5GetTokenizer( *pzErr = sqlcipher_sqlite3_mprintf("no such tokenizer: %s", azArg[0]); }else{ rc = pMod->x.xCreate( - pMod->pUserData, &azArg[1], (nArg?nArg-1:0), &pConfig->pTok + pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->pTok ); pConfig->pTokApi = &pMod->x; if( rc!=SQLITE_OK ){ @@ -231768,7 +242159,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlcipher_sqlite3_result_text(pCtx, "fts5: 2021-01-20 14:10:07 10e20c0b43500cfb9bbc0eaa061c57514f715d87238f4d835880cd846b9ebd1f", -1, SQLITE_TRANSIENT); + sqlcipher_sqlite3_result_text(pCtx, "fts5: 2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26b309", -1, SQLITE_TRANSIENT); } /* @@ -232319,12 +242710,16 @@ static int fts5StorageDeleteFromIndex( if( pConfig->abUnindexed[iCol-1]==0 ){ const char *zText; int nText; + assert( pSeek==0 || apVal==0 ); + assert( pSeek!=0 || apVal!=0 ); if( pSeek ){ zText = (const char*)sqlcipher_sqlite3_column_text(pSeek, iCol); nText = sqlcipher_sqlite3_column_bytes(pSeek, iCol); - }else{ + }else if( ALWAYS(apVal) ){ zText = (const char*)sqlcipher_sqlite3_value_text(apVal[iCol-1]); nText = sqlcipher_sqlite3_value_bytes(apVal[iCol-1]); + }else{ + continue; } ctx.szCol = 0; rc = sqlcipher_sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, @@ -232960,8 +243355,9 @@ static int sqlcipher_sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int * assert( p->pConfig->bColumnsize ); rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); - if( rc==SQLITE_OK ){ + if( pLookup ){ int bCorrupt = 1; + assert( rc==SQLITE_OK ); sqlcipher_sqlite3_bind_int64(pLookup, 1, iRowid); if( SQLITE_ROW==sqlcipher_sqlite3_step(pLookup) ){ const u8 *aBlob = sqlcipher_sqlite3_column_blob(pLookup, 0); @@ -232974,6 +243370,8 @@ static int sqlcipher_sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int * if( bCorrupt && rc==SQLITE_OK ){ rc = FTS5_CORRUPT; } + }else{ + assert( rc!=SQLITE_OK ); } return rc; @@ -235664,6 +246062,7 @@ struct Fts5VocabCursor { int bEof; /* True if this cursor is at EOF */ Fts5IndexIter *pIter; /* Term/rowid iterator object */ + void *pStruct; /* From sqlcipher_sqlite3Fts5StructureRef() */ int nLeTerm; /* Size of zLeTerm in bytes */ char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */ @@ -235977,7 +246376,7 @@ static int fts5VocabOpenMethod( } if( rc==SQLITE_OK ){ - int nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); + i64 nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); pCsr = (Fts5VocabCursor*)sqlcipher_sqlite3Fts5MallocZero(&rc, nByte); } @@ -235997,6 +246396,8 @@ static int fts5VocabOpenMethod( static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){ pCsr->rowid = 0; sqlcipher_sqlite3Fts5IterClose(pCsr->pIter); + sqlcipher_sqlite3Fts5StructureRelease(pCsr->pStruct); + pCsr->pStruct = 0; pCsr->pIter = 0; sqlcipher_sqlite3_free(pCsr->zLeTerm); pCsr->nLeTerm = -1; @@ -236074,9 +246475,11 @@ static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){ static int fts5VocabNextMethod(sqlcipher_sqlite3_vtab_cursor *pCursor){ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; - int rc = SQLITE_OK; int nCol = pCsr->pFts5->pConfig->nCol; + int rc; + rc = sqlcipher_sqlite3Fts5StructureTest(pCsr->pFts5->pIndex, pCsr->pStruct); + if( rc!=SQLITE_OK ) return rc; pCsr->rowid++; if( pTab->eType==FTS5_VOCAB_INSTANCE ){ @@ -236250,6 +246653,9 @@ static int fts5VocabFilterMethod( if( rc==SQLITE_OK ){ Fts5Index *pIndex = pCsr->pFts5->pIndex; rc = sqlcipher_sqlite3Fts5IndexQuery(pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); + if( rc==SQLITE_OK ){ + pCsr->pStruct = sqlcipher_sqlite3Fts5StructureRef(pIndex); + } } if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){ rc = fts5VocabInstanceNewTerm(pCsr); @@ -236424,6 +246830,16 @@ SQLITE_EXTENSION_INIT1 #ifndef SQLITE_OMIT_VIRTUALTABLE + +#define STMT_NUM_INTEGER_COLUMN 10 +typedef struct StmtRow StmtRow; +struct StmtRow { + sqlcipher_sqlite3_int64 iRowid; /* Rowid value */ + char *zSql; /* column "sql" */ + int aCol[STMT_NUM_INTEGER_COLUMN+1]; /* all other column values */ + StmtRow *pNext; /* Next row to return */ +}; + /* stmt_vtab is a subclass of sqlcipher_sqlite3_vtab which will ** serve as the underlying representation of a stmt virtual table */ @@ -236441,8 +246857,7 @@ typedef struct stmt_cursor stmt_cursor; struct stmt_cursor { sqlcipher_sqlite3_vtab_cursor base; /* Base class - must be first */ sqlcipher_sqlite3 *db; /* Database connection for this cursor */ - sqlcipher_sqlite3_stmt *pStmt; /* Statement cursor is currently pointing at */ - sqlcipher_sqlite3_int64 iRowid; /* The rowid */ + StmtRow *pRow; /* Current row */ }; /* @@ -236486,7 +246901,7 @@ static int stmtConnect( "CREATE TABLE x(sql,ncol,ro,busy,nscan,nsort,naidx,nstep," "reprep,run,mem)"); if( rc==SQLITE_OK ){ - pNew = sqlcipher_sqlite3_malloc( sizeof(*pNew) ); + pNew = sqlcipher_sqlite3_malloc64( sizeof(*pNew) ); *ppVtab = (sqlcipher_sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); @@ -236508,7 +246923,7 @@ static int stmtDisconnect(sqlcipher_sqlite3_vtab *pVtab){ */ static int stmtOpen(sqlcipher_sqlite3_vtab *p, sqlcipher_sqlite3_vtab_cursor **ppCursor){ stmt_cursor *pCur; - pCur = sqlcipher_sqlite3_malloc( sizeof(*pCur) ); + pCur = sqlcipher_sqlite3_malloc64( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->db = ((stmt_vtab*)p)->db; @@ -236516,10 +246931,21 @@ static int stmtOpen(sqlcipher_sqlite3_vtab *p, sqlcipher_sqlite3_vtab_cursor **p return SQLITE_OK; } +static void stmtCsrReset(stmt_cursor *pCur){ + StmtRow *pRow = 0; + StmtRow *pNext = 0; + for(pRow=pCur->pRow; pRow; pRow=pNext){ + pNext = pRow->pNext; + sqlcipher_sqlite3_free(pRow); + } + pCur->pRow = 0; +} + /* ** Destructor for a stmt_cursor. */ static int stmtClose(sqlcipher_sqlite3_vtab_cursor *cur){ + stmtCsrReset((stmt_cursor*)cur); sqlcipher_sqlite3_free(cur); return SQLITE_OK; } @@ -236530,8 +246956,9 @@ static int stmtClose(sqlcipher_sqlite3_vtab_cursor *cur){ */ static int stmtNext(sqlcipher_sqlite3_vtab_cursor *cur){ stmt_cursor *pCur = (stmt_cursor*)cur; - pCur->iRowid++; - pCur->pStmt = sqlcipher_sqlite3_next_stmt(pCur->db, pCur->pStmt); + StmtRow *pNext = pCur->pRow->pNext; + sqlcipher_sqlite3_free(pCur->pRow); + pCur->pRow = pNext; return SQLITE_OK; } @@ -236545,39 +246972,11 @@ static int stmtColumn( int i /* Which column to return */ ){ stmt_cursor *pCur = (stmt_cursor*)cur; - switch( i ){ - case STMT_COLUMN_SQL: { - sqlcipher_sqlite3_result_text(ctx, sqlcipher_sqlite3_sql(pCur->pStmt), -1, SQLITE_TRANSIENT); - break; - } - case STMT_COLUMN_NCOL: { - sqlcipher_sqlite3_result_int(ctx, sqlcipher_sqlite3_column_count(pCur->pStmt)); - break; - } - case STMT_COLUMN_RO: { - sqlcipher_sqlite3_result_int(ctx, sqlcipher_sqlite3_stmt_readonly(pCur->pStmt)); - break; - } - case STMT_COLUMN_BUSY: { - sqlcipher_sqlite3_result_int(ctx, sqlcipher_sqlite3_stmt_busy(pCur->pStmt)); - break; - } - default: { - assert( i==STMT_COLUMN_MEM ); - i = SQLITE_STMTSTATUS_MEMUSED + - STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP; - /* Fall thru */ - } - case STMT_COLUMN_NSCAN: - case STMT_COLUMN_NSORT: - case STMT_COLUMN_NAIDX: - case STMT_COLUMN_NSTEP: - case STMT_COLUMN_REPREP: - case STMT_COLUMN_RUN: { - sqlcipher_sqlite3_result_int(ctx, sqlcipher_sqlite3_stmt_status(pCur->pStmt, - i-STMT_COLUMN_NSCAN+SQLITE_STMTSTATUS_FULLSCAN_STEP, 0)); - break; - } + StmtRow *pRow = pCur->pRow; + if( i==STMT_COLUMN_SQL ){ + sqlcipher_sqlite3_result_text(ctx, pRow->zSql, -1, SQLITE_TRANSIENT); + }else{ + sqlcipher_sqlite3_result_int(ctx, pRow->aCol[i]); } return SQLITE_OK; } @@ -236588,7 +246987,7 @@ static int stmtColumn( */ static int stmtRowid(sqlcipher_sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ stmt_cursor *pCur = (stmt_cursor*)cur; - *pRowid = pCur->iRowid; + *pRowid = pCur->pRow->iRowid; return SQLITE_OK; } @@ -236598,7 +246997,7 @@ static int stmtRowid(sqlcipher_sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ */ static int stmtEof(sqlcipher_sqlite3_vtab_cursor *cur){ stmt_cursor *pCur = (stmt_cursor*)cur; - return pCur->pStmt==0; + return pCur->pRow==0; } /* @@ -236613,9 +247012,53 @@ static int stmtFilter( int argc, sqlcipher_sqlite3_value **argv ){ stmt_cursor *pCur = (stmt_cursor *)pVtabCursor; - pCur->pStmt = 0; - pCur->iRowid = 0; - return stmtNext(pVtabCursor); + sqlcipher_sqlite3_stmt *p = 0; + sqlcipher_sqlite3_int64 iRowid = 1; + StmtRow **ppRow = 0; + + stmtCsrReset(pCur); + ppRow = &pCur->pRow; + for(p=sqlcipher_sqlite3_next_stmt(pCur->db, 0); p; p=sqlcipher_sqlite3_next_stmt(pCur->db, p)){ + const char *zSql = sqlcipher_sqlite3_sql(p); + sqlcipher_sqlite3_int64 nSql = zSql ? strlen(zSql)+1 : 0; + StmtRow *pNew = (StmtRow*)sqlcipher_sqlite3_malloc64(sizeof(StmtRow) + nSql); + + if( pNew==0 ) return SQLITE_NOMEM; + memset(pNew, 0, sizeof(StmtRow)); + if( zSql ){ + pNew->zSql = (char*)&pNew[1]; + memcpy(pNew->zSql, zSql, nSql); + } + pNew->aCol[STMT_COLUMN_NCOL] = sqlcipher_sqlite3_column_count(p); + pNew->aCol[STMT_COLUMN_RO] = sqlcipher_sqlite3_stmt_readonly(p); + pNew->aCol[STMT_COLUMN_BUSY] = sqlcipher_sqlite3_stmt_busy(p); + pNew->aCol[STMT_COLUMN_NSCAN] = sqlcipher_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_FULLSCAN_STEP, 0 + ); + pNew->aCol[STMT_COLUMN_NSORT] = sqlcipher_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_SORT, 0 + ); + pNew->aCol[STMT_COLUMN_NAIDX] = sqlcipher_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_AUTOINDEX, 0 + ); + pNew->aCol[STMT_COLUMN_NSTEP] = sqlcipher_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_VM_STEP, 0 + ); + pNew->aCol[STMT_COLUMN_REPREP] = sqlcipher_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_REPREPARE, 0 + ); + pNew->aCol[STMT_COLUMN_RUN] = sqlcipher_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_RUN, 0 + ); + pNew->aCol[STMT_COLUMN_MEM] = sqlcipher_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_MEMUSED, 0 + ); + pNew->iRowid = iRowid++; + *ppRow = pNew; + ppRow = &pNew->pNext; + } + + return SQLITE_OK; } /* @@ -236694,10 +247137,6 @@ SQLITE_API int sqlcipher_sqlite3_stmt_init( #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ /************** End of stmt.c ************************************************/ -#if __LINE__!=236697 -#undef SQLITE_SOURCE_ID -#define SQLITE_SOURCE_ID "2021-01-20 14:10:07 10e20c0b43500cfb9bbc0eaa061c57514f715d87238f4d835880cd846b9ealt2" -#endif /* Return the source-id for this library */ SQLITE_API const char *sqlcipher_sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } /************************** End of sqlcipher_sqlite3.c ******************************/ diff --git a/Plugins/DbSqliteCipher/sqlcipher.h b/Plugins/DbSqliteCipher/sqlcipher.h index 08260ac..0cda434 100644 --- a/Plugins/DbSqliteCipher/sqlcipher.h +++ b/Plugins/DbSqliteCipher/sqlcipher.h @@ -43,7 +43,30 @@ extern "C" { /* -** Provide the ability to override linkage features of the interface. +** Facilitate override of interface linkage and calling conventions. +** Be aware that these macros may not be used within this particular +** translation of the amalgamation and its associated header file. +** +** The SQLITE_EXTERN and SQLITE_API macros are used to instruct the +** compiler that the target identifier should have external linkage. +** +** The SQLITE_CDECL macro is used to set the calling convention for +** public functions that accept a variable number of arguments. +** +** The SQLITE_APICALL macro is used to set the calling convention for +** public functions that accept a fixed number of arguments. +** +** The SQLITE_STDCALL macro is no longer used and is now deprecated. +** +** The SQLITE_CALLBACK macro is used to set the calling convention for +** function pointers. +** +** The SQLITE_SYSAPI macro is used to set the calling convention for +** functions provided by the operating system. +** +** Currently, the SQLITE_CDECL, SQLITE_APICALL, SQLITE_CALLBACK, and +** SQLITE_SYSAPI macros are used only when building for environments +** that require non-default calling conventions. */ #ifndef SQLITE_EXTERN # define SQLITE_EXTERN extern @@ -123,9 +146,9 @@ extern "C" { ** [sqlcipher_sqlite3_libversion_number()], [sqlcipher_sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.34.1" -#define SQLITE_VERSION_NUMBER 3034001 -#define SQLITE_SOURCE_ID "2021-01-20 14:10:07 10e20c0b43500cfb9bbc0eaa061c57514f715d87238f4d835880cd846b9ealt1" +#define SQLITE_VERSION "3.39.4" +#define SQLITE_VERSION_NUMBER 3039004 +#define SQLITE_SOURCE_ID "2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26alt1" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -537,12 +560,13 @@ SQLITE_API int sqlcipher_sqlite3_exec( #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) #define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8)) +#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) -#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) +#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ /* ** CAPI3REF: Flags For File Open Operations @@ -550,6 +574,19 @@ SQLITE_API int sqlcipher_sqlite3_exec( ** These bit values are intended for use in the ** 3rd parameter to the [sqlcipher_sqlite3_open_v2()] interface and ** in the 4th parameter to the [sqlcipher_sqlite3_vfs.xOpen] method. +** +** Only those flags marked as "Ok for sqlcipher_sqlite3_open_v2()" may be +** used as the third argument to the [sqlcipher_sqlite3_open_v2()] interface. +** The other flags have historically been ignored by sqlcipher_sqlite3_open_v2(), +** though future versions of SQLite might change so that an error is +** raised if any of the disallowed bits are passed into sqlcipher_sqlite3_open_v2(). +** Applications should not depend on the historical behavior. +** +** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into +** [sqlcipher_sqlite3_open_v2()] does *not* cause the underlying database file +** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into +** [sqlcipher_sqlite3_open_v2()] has historically be a no-op and might become an +** error in future versions of SQLite. */ #define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlcipher_sqlite3_open_v2() */ #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlcipher_sqlite3_open_v2() */ @@ -572,6 +609,7 @@ SQLITE_API int sqlcipher_sqlite3_exec( #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlcipher_sqlite3_open_v2() */ #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ #define SQLITE_OPEN_NOFOLLOW 0x01000000 /* Ok for sqlcipher_sqlite3_open_v2() */ +#define SQLITE_OPEN_EXRESCODE 0x02000000 /* Extended result codes */ /* Reserved: 0x00F00000 */ /* Legacy compatibility: */ @@ -1128,6 +1166,23 @@ struct sqlcipher_sqlite3_io_methods { ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. ** +** +**
  • [[SQLITE_FCNTL_EXTERNAL_READER]] +** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect +** whether or not there is a database client in another process with a wal-mode +** transaction open on the database or not. It is only available on unix.The +** (void*) argument passed with this file-control should be a pointer to a +** value of type (int). The integer value is set to 1 if the database is a wal +** mode database and there exists at least one client in another process that +** currently has an SQL transaction open on the database. It is set to 0 if +** the database is not a wal-mode db, or if there is no such connection in any +** other process. This opcode cannot be used to detect transactions opened +** by clients within the current process, only within other processes. +** +** +**
  • [[SQLITE_FCNTL_CKSM_FILE]] +** Used by the cksmvfs VFS module only. +** */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 @@ -1167,6 +1222,8 @@ struct sqlcipher_sqlite3_io_methods { #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 +#define SQLITE_FCNTL_EXTERNAL_READER 40 +#define SQLITE_FCNTL_CKSM_FILE 41 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -2115,7 +2172,13 @@ struct sqlcipher_sqlite3_mem_methods { ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether triggers are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in -** which case the trigger setting is not reported back. +** which case the trigger setting is not reported back. +** +**

    Originally this option disabled all triggers. ^(However, since +** SQLite version 3.35.0, TEMP triggers are still allowed even if +** this option is off. So, in other words, this option now only disables +** triggers in the main database schema or in the schemas of ATTACH-ed +** databases.)^ ** ** [[SQLITE_DBCONFIG_ENABLE_VIEW]] **

    SQLITE_DBCONFIG_ENABLE_VIEW
    @@ -2126,7 +2189,13 @@ struct sqlcipher_sqlite3_mem_methods { ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether views are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in -** which case the view setting is not reported back. +** which case the view setting is not reported back. +** +**

    Originally this option disabled all views. ^(However, since +** SQLite version 3.35.0, TEMP views are still allowed even if +** this option is off. So, in other words, this option now only disables +** views in the main database schema or in the schemas of ATTACH-ed +** databases.)^ ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] **

    SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
    @@ -2433,11 +2502,14 @@ SQLITE_API void sqlcipher_sqlite3_set_last_insert_rowid(sqlcipher_sqlite3*,sqlci ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlcipher_sqlite3 ** -** ^This function returns the number of rows modified, inserted or +** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. -** ^Executing any other type of SQL statement does not modify the value -** returned by this function. +** The two functions are identical except for the type of the return value +** and that if the number of rows modified by the most recent INSERT, UPDATE +** or DELETE is greater than the maximum value supported by type "int", then +** the return value of sqlcipher_sqlite3_changes() is undefined. ^Executing any other +** type of SQL statement does not modify the value returned by these functions. ** ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], @@ -2486,16 +2558,21 @@ SQLITE_API void sqlcipher_sqlite3_set_last_insert_rowid(sqlcipher_sqlite3*,sqlci ** */ SQLITE_API int sqlcipher_sqlite3_changes(sqlcipher_sqlite3*); +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_changes64(sqlcipher_sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified ** METHOD: sqlcipher_sqlite3 ** -** ^This function returns the total number of rows inserted, modified or +** ^These functions return the total number of rows inserted, modified or ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed ** since the database connection was opened, including those executed as -** part of trigger programs. ^Executing any other type of SQL statement -** does not affect the value returned by sqlcipher_sqlite3_total_changes(). +** part of trigger programs. The two functions are identical except for the +** type of the return value and that if the number of rows modified by the +** connection exceeds the maximum value supported by type "int", then +** the return value of sqlcipher_sqlite3_total_changes() is undefined. ^Executing +** any other type of SQL statement does not affect the value returned by +** sqlcipher_sqlite3_total_changes(). ** ** ^Changes made as part of [foreign key actions] are included in the ** count, but those made as part of REPLACE constraint resolution are @@ -2523,6 +2600,7 @@ SQLITE_API int sqlcipher_sqlite3_changes(sqlcipher_sqlite3*); ** */ SQLITE_API int sqlcipher_sqlite3_total_changes(sqlcipher_sqlite3*); +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3_total_changes64(sqlcipher_sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query @@ -3352,6 +3430,14 @@ SQLITE_API void sqlcipher_sqlite3_progress_handler(sqlcipher_sqlite3*, int, int( ** the default shared cache setting provided by ** [sqlcipher_sqlite3_enable_shared_cache()].)^ ** +** [[OPEN_EXRESCODE]] ^(
    [SQLITE_OPEN_EXRESCODE]
    +**
    The database connection comes up in "extended result code mode". +** In other words, the database behaves has if +** [sqlcipher_sqlite3_extended_result_codes(db,1)] where called on the database +** connection as soon as the connection is created. In addition to setting +** the extended result code mode, this flag also causes [sqlcipher_sqlite3_open_v2()] +** to return an extended result code.
    +** ** [[OPEN_NOFOLLOW]] ^(
    [SQLITE_OPEN_NOFOLLOW]
    **
    The database filename is not allowed to be a symbolic link
    ** )^ @@ -3359,7 +3445,15 @@ SQLITE_API void sqlcipher_sqlite3_progress_handler(sqlcipher_sqlite3*, int, int( ** If the 3rd parameter to sqlcipher_sqlite3_open_v2() is not one of the ** required combinations shown above optionally combined with other ** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] -** then the behavior is undefined. +** then the behavior is undefined. Historic versions of SQLite +** have silently ignored surplus bits in the flags parameter to +** sqlcipher_sqlite3_open_v2(), however that behavior might not be carried through +** into future versions of SQLite and so applications should not rely +** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op +** for sqlcipher_sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause +** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE +** flag is intended for use by the [sqlcipher_sqlite3_vfs|VFS interface] only, and not +** by sqlcipher_sqlite3_open_v2(). ** ** ^The fourth parameter to sqlcipher_sqlite3_open_v2() is the name of the ** [sqlcipher_sqlite3_vfs] object that defines the operating system interface that @@ -3499,6 +3593,7 @@ SQLITE_API void sqlcipher_sqlite3_progress_handler(sqlcipher_sqlite3*, int, int( ** that uses dot-files in place of posix advisory locking. **
  • file:data.db?mode=readonly ** An error. "readonly" is not a valid option for the "mode" parameter. +** Use "ro" instead: "file:data.db?mode=ro". **
    ** ** ^URI hexadecimal escape sequences (%HH) are supported within the path and @@ -3729,13 +3824,14 @@ SQLITE_API void sqlcipher_sqlite3_free_filename(char*); ** sqlcipher_sqlite3_extended_errcode() might change with each API call. ** Except, there are some interfaces that are guaranteed to never ** change the value of the error code. The error-code preserving -** interfaces are: +** interfaces include the following: ** **
      **
    • sqlcipher_sqlite3_errcode() **
    • sqlcipher_sqlite3_extended_errcode() **
    • sqlcipher_sqlite3_errmsg() **
    • sqlcipher_sqlite3_errmsg16() +**
    • sqlcipher_sqlite3_error_offset() **
    ** ** ^The sqlcipher_sqlite3_errmsg() and sqlcipher_sqlite3_errmsg16() return English-language @@ -3750,6 +3846,13 @@ SQLITE_API void sqlcipher_sqlite3_free_filename(char*); ** ^(Memory to hold the error message string is managed internally ** and must not be freed by the application)^. ** +** ^If the most recent error references a specific token in the input +** SQL, the sqlcipher_sqlite3_error_offset() interface returns the byte offset +** of the start of that token. ^The byte offset returned by +** sqlcipher_sqlite3_error_offset() assumes that the input SQL is UTF8. +** ^If the most recent error does not reference a specific token in the input +** SQL, then the sqlcipher_sqlite3_error_offset() function returns -1. +** ** When the serialized [threading mode] is in use, it might be the ** case that a second error occurs on a separate thread in between ** the time of the first error and the call to these interfaces. @@ -3769,6 +3872,7 @@ SQLITE_API int sqlcipher_sqlite3_extended_errcode(sqlcipher_sqlite3 *db); SQLITE_API const char *sqlcipher_sqlite3_errmsg(sqlcipher_sqlite3*); SQLITE_API const void *sqlcipher_sqlite3_errmsg16(sqlcipher_sqlite3*); SQLITE_API const char *sqlcipher_sqlite3_errstr(int); +SQLITE_API int sqlcipher_sqlite3_error_offset(sqlcipher_sqlite3 *db); /* ** CAPI3REF: Prepared Statement Object @@ -4126,12 +4230,17 @@ SQLITE_API int sqlcipher_sqlite3_prepare16_v3( ** are managed by SQLite and are automatically freed when the prepared ** statement is finalized. ** ^The string returned by sqlcipher_sqlite3_expanded_sql(P), on the other hand, -** is obtained from [sqlcipher_sqlite3_malloc()] and must be free by the application +** is obtained from [sqlcipher_sqlite3_malloc()] and must be freed by the application ** by passing it to [sqlcipher_sqlite3_free()]. +** +** ^The sqlcipher_sqlite3_normalized_sql() interface is only available if +** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined. */ SQLITE_API const char *sqlcipher_sqlite3_sql(sqlcipher_sqlite3_stmt *pStmt); SQLITE_API char *sqlcipher_sqlite3_expanded_sql(sqlcipher_sqlite3_stmt *pStmt); +#ifdef SQLITE_ENABLE_NORMALIZE SQLITE_API const char *sqlcipher_sqlite3_normalized_sql(sqlcipher_sqlite3_stmt *pStmt); +#endif /* ** CAPI3REF: Determine If An SQL Statement Writes The Database @@ -4166,6 +4275,19 @@ SQLITE_API const char *sqlcipher_sqlite3_normalized_sql(sqlcipher_sqlite3_stmt * ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so ** sqlcipher_sqlite3_stmt_readonly() returns false for those commands. +** +** ^This routine returns false if there is any possibility that the +** statement might change the database file. ^A false return does +** not guarantee that the statement will change the database file. +** ^For example, an UPDATE statement might have a WHERE clause that +** makes it a no-op, but the sqlcipher_sqlite3_stmt_readonly() result would still +** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a +** read-only no-op if the table already exists, but +** sqlcipher_sqlite3_stmt_readonly() still returns false for such a statement. +** +** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN] +** statement, then sqlcipher_sqlite3_stmt_readonly(X) returns the same value as +** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted. */ SQLITE_API int sqlcipher_sqlite3_stmt_readonly(sqlcipher_sqlite3_stmt *pStmt); @@ -4234,6 +4356,8 @@ SQLITE_API int sqlcipher_sqlite3_stmt_busy(sqlcipher_sqlite3_stmt*); ** ** ^The sqlcipher_sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. +** ^The sqlcipher_sqlite3_value objects returned by [sqlcipher_sqlite3_vtab_rhs_value()] +** are protected. ** ^The sqlcipher_sqlite3_value object returned by ** [sqlcipher_sqlite3_column_value()] is unprotected. ** Unprotected sqlcipher_sqlite3_value objects may only be used as arguments @@ -4335,18 +4459,22 @@ typedef struct sqlcipher_sqlite3_context sqlcipher_sqlite3_context; ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. ** -** ^The fifth argument to the BLOB and string binding interfaces -** is a destructor used to dispose of the BLOB or -** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to the bind API fails, -** except the destructor is not called if the third parameter is a NULL -** pointer or the fourth parameter is negative. -** ^If the fifth argument is -** the special value [SQLITE_STATIC], then SQLite assumes that the -** information is in static, unmanaged space and does not need to be freed. -** ^If the fifth argument has the value [SQLITE_TRANSIENT], then -** SQLite makes its own private copy of the data immediately, before -** the sqlcipher_sqlite3_bind_*() routine returns. +** ^The fifth argument to the BLOB and string binding interfaces controls +** or indicates the lifetime of the object referenced by the third parameter. +** These three options exist: +** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished +** with it may be passed. ^It is called to dispose of the BLOB or string even +** if the call to the bind API fails, except the destructor is not called if +** the third parameter is a NULL pointer or the fourth parameter is negative. +** ^ (2) The special constant, [SQLITE_STATIC], may be passsed to indicate that +** the application remains responsible for disposing of the object. ^In this +** case, the object and the provided pointer to it must remain valid until +** either the prepared statement is finalized or the same SQL parameter is +** bound to something else, whichever occurs sooner. +** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the +** object is to be copied prior to the return from sqlcipher_sqlite3_bind_*(). ^The +** object and pointer to it must remain valid until then. ^SQLite will then +** manage the lifetime of its private copy. ** ** ^The sixth argument to sqlcipher_sqlite3_bind_text64() must be one of ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] @@ -4851,6 +4979,10 @@ SQLITE_API int sqlcipher_sqlite3_data_count(sqlcipher_sqlite3_stmt *pStmt); ** even empty strings, are always zero-terminated. ^The return ** value from sqlcipher_sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. ** +** ^Strings returned by sqlcipher_sqlite3_column_text16() always have the endianness +** which is native to the platform, regardless of the text encoding set +** for the database. +** ** Warning: ^The object returned by [sqlcipher_sqlite3_column_value()] is an ** [unprotected sqlcipher_sqlite3_value] object. In a multithreaded environment, ** an unprotected sqlcipher_sqlite3_value object may only be used safely with @@ -4864,7 +4996,7 @@ SQLITE_API int sqlcipher_sqlite3_data_count(sqlcipher_sqlite3_stmt *pStmt); ** [application-defined SQL functions] or [virtual tables], not within ** top-level application code. ** -** The these routines may attempt to convert the datatype of the result. +** These routines may attempt to convert the datatype of the result. ** ^For example, if the internal representation is FLOAT and a text result ** is requested, [sqlcipher_sqlite3_snprintf()] is used internally to perform the ** conversion automatically. ^(The following table details the conversions @@ -4889,7 +5021,7 @@ SQLITE_API int sqlcipher_sqlite3_data_count(sqlcipher_sqlite3_stmt *pStmt); ** TEXT BLOB No change ** BLOB INTEGER [CAST] to INTEGER ** BLOB FLOAT [CAST] to REAL -** BLOB TEXT Add a zero terminator if needed +** BLOB TEXT [CAST] to TEXT, ensure zero terminator ** ** )^ ** @@ -5088,7 +5220,6 @@ SQLITE_API int sqlcipher_sqlite3_reset(sqlcipher_sqlite3_stmt *pStmt); ** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions, ** index expressions, or the WHERE clause of partial indexes. ** -** ** For best security, the [SQLITE_DIRECTONLY] flag is recommended for ** all application-defined SQL functions that do not need to be ** used inside of triggers, view, CHECK constraints, or other elements of @@ -5098,7 +5229,6 @@ SQLITE_API int sqlcipher_sqlite3_reset(sqlcipher_sqlite3_stmt *pStmt); ** a database file to include invocations of the function with parameters ** chosen by the attacker, which the application will then execute when ** the database file is opened and read. -** ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlcipher_sqlite3_user_data()].)^ @@ -5463,7 +5593,8 @@ SQLITE_API unsigned int sqlcipher_sqlite3_value_subtype(sqlcipher_sqlite3_value* ** object D and returns a pointer to that copy. ^The [sqlcipher_sqlite3_value] returned ** is a [protected sqlcipher_sqlite3_value] object even if the input is not. ** ^The sqlcipher_sqlite3_value_dup(V) interface returns NULL if V is NULL or if a -** memory allocation fails. +** memory allocation fails. ^If V is a [pointer value], then the result +** of sqlcipher_sqlite3_value_dup(V) is a NULL value. ** ** ^The sqlcipher_sqlite3_value_free(V) interface frees an [sqlcipher_sqlite3_value] object ** previously obtained from [sqlcipher_sqlite3_value_dup()]. ^If V is a NULL pointer @@ -5976,6 +6107,19 @@ SQLITE_API int sqlcipher_sqlite3_key_v2( ** The code to implement this API is not available in the public release ** of SQLite. */ +/* SQLCipher usage note: + + If the current database is plaintext SQLCipher will NOT encrypt it. + If the current database is encrypted and pNew==0 or nNew==0, SQLCipher + will NOT decrypt it. + + This routine will ONLY work on an already encrypted database in order + to change the key. + + Conversion from plaintext-to-encrypted or encrypted-to-plaintext should + use an ATTACHed database and the sqlcipher_export() convenience function + as per the SQLCipher Documentation. +*/ SQLITE_API int sqlcipher_sqlite3_rekey( sqlcipher_sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The new key */ @@ -6192,6 +6336,28 @@ SQLITE_API int sqlcipher_sqlite3_get_autocommit(sqlcipher_sqlite3*); */ SQLITE_API sqlcipher_sqlite3 *sqlcipher_sqlite3_db_handle(sqlcipher_sqlite3_stmt*); +/* +** CAPI3REF: Return The Schema Name For A Database Connection +** METHOD: sqlcipher_sqlite3 +** +** ^The sqlcipher_sqlite3_db_name(D,N) interface returns a pointer to the schema name +** for the N-th database on database connection D, or a NULL pointer of N is +** out of range. An N value of 0 means the main database file. An N of 1 is +** the "temp" schema. Larger values of N correspond to various ATTACH-ed +** databases. +** +** Space to hold the string that is returned by sqlcipher_sqlite3_db_name() is managed +** by SQLite itself. The string might be deallocated by any operation that +** changes the schema, including [ATTACH] or [DETACH] or calls to +** [sqlcipher_sqlite3_serialize()] or [sqlcipher_sqlite3_deserialize()], even operations that +** occur on a different thread. Applications that need to +** remember the string long-term should make their own copy. Applications that +** are accessing the same database connection simultaneously on multiple +** threads should mutex-protect calls to this API and should make their own +** private copy of the result prior to releasing the mutex. +*/ +SQLITE_API const char *sqlcipher_sqlite3_db_name(sqlcipher_sqlite3 *db, int N); + /* ** CAPI3REF: Return The Filename For A Database Connection ** METHOD: sqlcipher_sqlite3 @@ -6351,6 +6517,72 @@ SQLITE_API sqlcipher_sqlite3_stmt *sqlcipher_sqlite3_next_stmt(sqlcipher_sqlite3 SQLITE_API void *sqlcipher_sqlite3_commit_hook(sqlcipher_sqlite3*, int(*)(void*), void*); SQLITE_API void *sqlcipher_sqlite3_rollback_hook(sqlcipher_sqlite3*, void(*)(void *), void*); +/* +** CAPI3REF: Autovacuum Compaction Amount Callback +** METHOD: sqlcipher_sqlite3 +** +** ^The sqlcipher_sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback +** function C that is invoked prior to each autovacuum of the database +** file. ^The callback is passed a copy of the generic data pointer (P), +** the schema-name of the attached database that is being autovacuumed, +** the the size of the database file in pages, the number of free pages, +** and the number of bytes per page, respectively. The callback should +** return the number of free pages that should be removed by the +** autovacuum. ^If the callback returns zero, then no autovacuum happens. +** ^If the value returned is greater than or equal to the number of +** free pages, then a complete autovacuum happens. +** +**

    ^If there are multiple ATTACH-ed database files that are being +** modified as part of a transaction commit, then the autovacuum pages +** callback is invoked separately for each file. +** +**

    The callback is not reentrant. The callback function should +** not attempt to invoke any other SQLite interface. If it does, bad +** things may happen, including segmentation faults and corrupt database +** files. The callback function should be a simple function that +** does some arithmetic on its input parameters and returns a result. +** +** ^The X parameter to sqlcipher_sqlite3_autovacuum_pages(D,C,P,X) is an optional +** destructor for the P parameter. ^If X is not NULL, then X(P) is +** invoked whenever the database connection closes or when the callback +** is overwritten by another invocation of sqlcipher_sqlite3_autovacuum_pages(). +** +**

    ^There is only one autovacuum pages callback per database connection. +** ^Each call to the sqlcipher_sqlite3_autovacuum_pages() interface overrides all +** previous invocations for that database connection. ^If the callback +** argument (C) to sqlcipher_sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, +** then the autovacuum steps callback is cancelled. The return value +** from sqlcipher_sqlite3_autovacuum_pages() is normally SQLITE_OK, but might +** be some other error code if something goes wrong. The current +** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other +** return codes might be added in future releases. +** +**

    If no autovacuum pages callback is specified (the usual case) or +** a NULL pointer is provided for the callback, +** then the default behavior is to vacuum all free pages. So, in other +** words, the default behavior is the same as if the callback function +** were something like this: +** +**

    +**     unsigned int demonstration_autovac_pages_callback(
    +**       void *pClientData,
    +**       const char *zSchema,
    +**       unsigned int nDbPage,
    +**       unsigned int nFreePage,
    +**       unsigned int nBytePerPage
    +**     ){
    +**       return nFreePage;
    +**     }
    +** 
    +*/ +SQLITE_API int sqlcipher_sqlite3_autovacuum_pages( + sqlcipher_sqlite3 *db, + unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), + void*, + void(*)(void*) +); + + /* ** CAPI3REF: Data Change Notification Callbacks ** METHOD: sqlcipher_sqlite3 @@ -6992,24 +7224,56 @@ struct sqlcipher_sqlite3_index_info { ** ** These macros define the allowed values for the ** [sqlcipher_sqlite3_index_info].aConstraint[].op field. Each value represents -** an operator that is part of a constraint term in the wHERE clause of +** an operator that is part of a constraint term in the WHERE clause of ** a query that uses a [virtual table]. -*/ -#define SQLITE_INDEX_CONSTRAINT_EQ 2 -#define SQLITE_INDEX_CONSTRAINT_GT 4 -#define SQLITE_INDEX_CONSTRAINT_LE 8 -#define SQLITE_INDEX_CONSTRAINT_LT 16 -#define SQLITE_INDEX_CONSTRAINT_GE 32 -#define SQLITE_INDEX_CONSTRAINT_MATCH 64 -#define SQLITE_INDEX_CONSTRAINT_LIKE 65 -#define SQLITE_INDEX_CONSTRAINT_GLOB 66 -#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 -#define SQLITE_INDEX_CONSTRAINT_NE 68 -#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 -#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 -#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 -#define SQLITE_INDEX_CONSTRAINT_IS 72 -#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 +** +** ^The left-hand operand of the operator is given by the corresponding +** aConstraint[].iColumn field. ^An iColumn of -1 indicates the left-hand +** operand is the rowid. +** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET +** operators have no left-hand operand, and so for those operators the +** corresponding aConstraint[].iColumn is meaningless and should not be +** used. +** +** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through +** value 255 are reserved to represent functions that are overloaded +** by the [xFindFunction|xFindFunction method] of the virtual table +** implementation. +** +** The right-hand operands for each constraint might be accessible using +** the [sqlcipher_sqlite3_vtab_rhs_value()] interface. Usually the right-hand +** operand is only available if it appears as a single constant literal +** in the input SQL. If the right-hand operand is another column or an +** expression (even a constant expression) or a parameter, then the +** sqlcipher_sqlite3_vtab_rhs_value() probably will not be able to extract it. +** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and +** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand +** and hence calls to sqlcipher_sqlite3_vtab_rhs_value() for those operators will +** always return SQLITE_NOTFOUND. +** +** The collating sequence to be used for comparison can be found using +** the [sqlcipher_sqlite3_vtab_collation()] interface. For most real-world virtual +** tables, the collating sequence of constraints does not matter (for example +** because the constraints are numeric) and so the sqlcipher_sqlite3_vtab_collation() +** interface is no commonly needed. +*/ +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 +#define SQLITE_INDEX_CONSTRAINT_LIKE 65 +#define SQLITE_INDEX_CONSTRAINT_GLOB 66 +#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 +#define SQLITE_INDEX_CONSTRAINT_NE 68 +#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 +#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 +#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 +#define SQLITE_INDEX_CONSTRAINT_IS 72 +#define SQLITE_INDEX_CONSTRAINT_LIMIT 73 +#define SQLITE_INDEX_CONSTRAINT_OFFSET 74 +#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* ** CAPI3REF: Register A Virtual Table Implementation @@ -7038,7 +7302,7 @@ struct sqlcipher_sqlite3_index_info { ** destructor. ** ** ^If the third parameter (the pointer to the sqlcipher_sqlite3_module object) is -** NULL then no new module is create and any existing modules with the +** NULL then no new module is created and any existing modules with the ** same name are dropped. ** ** See also: [sqlcipher_sqlite3_drop_modules()] @@ -7812,7 +8076,10 @@ SQLITE_API int sqlcipher_sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_PRNG_SEED 28 #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 #define SQLITE_TESTCTRL_SEEK_COUNT 30 -#define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */ +#define SQLITE_TESTCTRL_TRACEFLAGS 31 +#define SQLITE_TESTCTRL_TUNE 32 +#define SQLITE_TESTCTRL_LOGEST 33 +#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking @@ -8335,6 +8602,16 @@ SQLITE_API int sqlcipher_sqlite3_stmt_status(sqlcipher_sqlite3_stmt*, int op,int ** The counter is incremented on the first [sqlcipher_sqlite3_step()] call of each ** cycle. ** +** [[SQLITE_STMTSTATUS_FILTER_MISS]] +** [[SQLITE_STMTSTATUS_FILTER HIT]] +**
    SQLITE_STMTSTATUS_FILTER_HIT
    +** SQLITE_STMTSTATUS_FILTER_MISS
    +**
    ^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join +** step was bypassed because a Bloom filter returned not-found. The +** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of +** times that the Bloom filter returned a find, and thus the join step +** had to be processed as normal. +** ** [[SQLITE_STMTSTATUS_MEMUSED]]
    SQLITE_STMTSTATUS_MEMUSED
    **
    ^This is the approximate number of bytes of heap memory ** used to store the prepared statement. ^This value is not actually @@ -8349,6 +8626,8 @@ SQLITE_API int sqlcipher_sqlite3_stmt_status(sqlcipher_sqlite3_stmt*, int op,int #define SQLITE_STMTSTATUS_VM_STEP 4 #define SQLITE_STMTSTATUS_REPREPARE 5 #define SQLITE_STMTSTATUS_RUN 6 +#define SQLITE_STMTSTATUS_FILTER_MISS 7 +#define SQLITE_STMTSTATUS_FILTER_HIT 8 #define SQLITE_STMTSTATUS_MEMUSED 99 /* @@ -9012,8 +9291,9 @@ SQLITE_API void sqlcipher_sqlite3_log(int iErrCode, const char *zFormat, ...); ** ** A single database handle may have at most a single write-ahead log callback ** registered at one time. ^Calling [sqlcipher_sqlite3_wal_hook()] replaces any -** previously registered write-ahead log callback. ^Note that the -** [sqlcipher_sqlite3_wal_autocheckpoint()] interface and the +** previously registered write-ahead log callback. ^The return value is +** a copy of the third parameter from the previous call, if any, or 0. +** ^Note that the [sqlcipher_sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] both invoke [sqlcipher_sqlite3_wal_hook()] and will ** overwrite any prior [sqlcipher_sqlite3_wal_hook()] settings. */ @@ -9316,19 +9596,276 @@ SQLITE_API int sqlcipher_sqlite3_vtab_nochange(sqlcipher_sqlite3_context*); /* ** CAPI3REF: Determine The Collation For a Virtual Table Constraint +** METHOD: sqlcipher_sqlite3_index_info ** ** This function may only be called from within a call to the [xBestIndex] -** method of a [virtual table]. +** method of a [virtual table]. This function returns a pointer to a string +** that is the name of the appropriate collation sequence to use for text +** comparisons on the constraint identified by its arguments. +** +** The first argument must be the pointer to the [sqlcipher_sqlite3_index_info] object +** that is the first parameter to the xBestIndex() method. The second argument +** must be an index into the aConstraint[] array belonging to the +** sqlcipher_sqlite3_index_info structure passed to xBestIndex. +** +** Important: +** The first parameter must be the same pointer that is passed into the +** xBestMethod() method. The first parameter may not be a pointer to a +** different [sqlcipher_sqlite3_index_info] object, even an exact copy. ** -** The first argument must be the sqlcipher_sqlite3_index_info object that is the -** first parameter to the xBestIndex() method. The second argument must be -** an index into the aConstraint[] array belonging to the sqlcipher_sqlite3_index_info -** structure passed to xBestIndex. This function returns a pointer to a buffer -** containing the name of the collation sequence for the corresponding -** constraint. +** The return value is computed as follows: +** +**
      +**
    1. If the constraint comes from a WHERE clause expression that contains +** a [COLLATE operator], then the name of the collation specified by +** that COLLATE operator is returned. +**

    2. If there is no COLLATE operator, but the column that is the subject +** of the constraint specifies an alternative collating sequence via +** a [COLLATE clause] on the column definition within the CREATE TABLE +** statement that was passed into [sqlcipher_sqlite3_declare_vtab()], then the +** name of that alternative collating sequence is returned. +**

    3. Otherwise, "BINARY" is returned. +**

    */ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlcipher_sqlite3_vtab_collation(sqlcipher_sqlite3_index_info*,int); +/* +** CAPI3REF: Determine if a virtual table query is DISTINCT +** METHOD: sqlcipher_sqlite3_index_info +** +** This API may only be used from within an [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this +** interface from outside of xBestIndex() is undefined and probably harmful. +** +** ^The sqlcipher_sqlite3_vtab_distinct() interface returns an integer between 0 and +** 3. The integer returned by sqlcipher_sqlite3_vtab_distinct() +** gives the virtual table additional information about how the query +** planner wants the output to be ordered. As long as the virtual table +** can meet the ordering requirements of the query planner, it may set +** the "orderByConsumed" flag. +** +**
    1. +** ^If the sqlcipher_sqlite3_vtab_distinct() interface returns 0, that means +** that the query planner needs the virtual table to return all rows in the +** sort order defined by the "nOrderBy" and "aOrderBy" fields of the +** [sqlcipher_sqlite3_index_info] object. This is the default expectation. If the +** virtual table outputs all rows in sorted order, then it is always safe for +** the xBestIndex method to set the "orderByConsumed" flag, regardless of +** the return value from sqlcipher_sqlite3_vtab_distinct(). +**

    2. +** ^(If the sqlcipher_sqlite3_vtab_distinct() interface returns 1, that means +** that the query planner does not need the rows to be returned in sorted order +** as long as all rows with the same values in all columns identified by the +** "aOrderBy" field are adjacent.)^ This mode is used when the query planner +** is doing a GROUP BY. +**

    3. +** ^(If the sqlcipher_sqlite3_vtab_distinct() interface returns 2, that means +** that the query planner does not need the rows returned in any particular +** order, as long as rows with the same values in all "aOrderBy" columns +** are adjacent.)^ ^(Furthermore, only a single row for each particular +** combination of values in the columns identified by the "aOrderBy" field +** needs to be returned.)^ ^It is always ok for two or more rows with the same +** values in all "aOrderBy" columns to be returned, as long as all such rows +** are adjacent. ^The virtual table may, if it chooses, omit extra rows +** that have the same value for all columns identified by "aOrderBy". +** ^However omitting the extra rows is optional. +** This mode is used for a DISTINCT query. +**

    4. +** ^(If the sqlcipher_sqlite3_vtab_distinct() interface returns 3, that means +** that the query planner needs only distinct rows but it does need the +** rows to be sorted.)^ ^The virtual table implementation is free to omit +** rows that are identical in all aOrderBy columns, if it wants to, but +** it is not required to omit any rows. This mode is used for queries +** that have both DISTINCT and ORDER BY clauses. +**

    +** +** ^For the purposes of comparing virtual table output values to see if the +** values are same value for sorting purposes, two NULL values are considered +** to be the same. In other words, the comparison operator is "IS" +** (or "IS NOT DISTINCT FROM") and not "==". +** +** If a virtual table implementation is unable to meet the requirements +** specified above, then it must not set the "orderByConsumed" flag in the +** [sqlcipher_sqlite3_index_info] object or an incorrect answer may result. +** +** ^A virtual table implementation is always free to return rows in any order +** it wants, as long as the "orderByConsumed" flag is not set. ^When the +** the "orderByConsumed" flag is unset, the query planner will add extra +** [bytecode] to ensure that the final results returned by the SQL query are +** ordered correctly. The use of the "orderByConsumed" flag and the +** sqlcipher_sqlite3_vtab_distinct() interface is merely an optimization. ^Careful +** use of the sqlcipher_sqlite3_vtab_distinct() interface and the "orderByConsumed" +** flag might help queries against a virtual table to run faster. Being +** overly aggressive and setting the "orderByConsumed" flag when it is not +** valid to do so, on the other hand, might cause SQLite to return incorrect +** results. +*/ +SQLITE_API int sqlcipher_sqlite3_vtab_distinct(sqlcipher_sqlite3_index_info*); + +/* +** CAPI3REF: Identify and handle IN constraints in xBestIndex +** +** This interface may only be used from within an +** [xBestIndex|xBestIndex() method] of a [virtual table] implementation. +** The result of invoking this interface from any other context is +** undefined and probably harmful. +** +** ^(A constraint on a virtual table of the form +** "[IN operator|column IN (...)]" is +** communicated to the xBestIndex method as a +** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use +** this constraint, it must set the corresponding +** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under +** the usual mode of handling IN operators, SQLite generates [bytecode] +** that invokes the [xFilter|xFilter() method] once for each value +** on the right-hand side of the IN operator.)^ Thus the virtual table +** only sees a single value from the right-hand side of the IN operator +** at a time. +** +** In some cases, however, it would be advantageous for the virtual +** table to see all values on the right-hand of the IN operator all at +** once. The sqlcipher_sqlite3_vtab_in() interfaces facilitates this in two ways: +** +**
      +**
    1. +** ^A call to sqlcipher_sqlite3_vtab_in(P,N,-1) will return true (non-zero) +** if and only if the [sqlcipher_sqlite3_index_info|P->aConstraint][N] constraint +** is an [IN operator] that can be processed all at once. ^In other words, +** sqlcipher_sqlite3_vtab_in() with -1 in the third argument is a mechanism +** by which the virtual table can ask SQLite if all-at-once processing +** of the IN operator is even possible. +** +**

    2. +** ^A call to sqlcipher_sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates +** to SQLite that the virtual table does or does not want to process +** the IN operator all-at-once, respectively. ^Thus when the third +** parameter (F) is non-negative, this interface is the mechanism by +** which the virtual table tells SQLite how it wants to process the +** IN operator. +**

    +** +** ^The sqlcipher_sqlite3_vtab_in(P,N,F) interface can be invoked multiple times +** within the same xBestIndex method call. ^For any given P,N pair, +** the return value from sqlcipher_sqlite3_vtab_in(P,N,F) will always be the same +** within the same xBestIndex call. ^If the interface returns true +** (non-zero), that means that the constraint is an IN operator +** that can be processed all-at-once. ^If the constraint is not an IN +** operator or cannot be processed all-at-once, then the interface returns +** false. +** +** ^(All-at-once processing of the IN operator is selected if both of the +** following conditions are met: +** +**
      +**
    1. The P->aConstraintUsage[N].argvIndex value is set to a positive +** integer. This is how the virtual table tells SQLite that it wants to +** use the N-th constraint. +** +**

    2. The last call to sqlcipher_sqlite3_vtab_in(P,N,F) for which F was +** non-negative had F>=1. +**

    )^ +** +** ^If either or both of the conditions above are false, then SQLite uses +** the traditional one-at-a-time processing strategy for the IN constraint. +** ^If both conditions are true, then the argvIndex-th parameter to the +** xFilter method will be an [sqlcipher_sqlite3_value] that appears to be NULL, +** but which can be passed to [sqlcipher_sqlite3_vtab_in_first()] and +** [sqlcipher_sqlite3_vtab_in_next()] to find all values on the right-hand side +** of the IN constraint. +*/ +SQLITE_API int sqlcipher_sqlite3_vtab_in(sqlcipher_sqlite3_index_info*, int iCons, int bHandle); + +/* +** CAPI3REF: Find all elements on the right-hand side of an IN constraint. +** +** These interfaces are only useful from within the +** [xFilter|xFilter() method] of a [virtual table] implementation. +** The result of invoking these interfaces from any other context +** is undefined and probably harmful. +** +** The X parameter in a call to sqlcipher_sqlite3_vtab_in_first(X,P) or +** sqlcipher_sqlite3_vtab_in_next(X,P) must be one of the parameters to the +** xFilter method which invokes these routines, and specifically +** a parameter that was previously selected for all-at-once IN constraint +** processing use the [sqlcipher_sqlite3_vtab_in()] interface in the +** [xBestIndex|xBestIndex method]. ^(If the X parameter is not +** an xFilter argument that was selected for all-at-once IN constraint +** processing, then these routines return [SQLITE_MISUSE])^ or perhaps +** exhibit some other undefined or harmful behavior. +** +** ^(Use these routines to access all values on the right-hand side +** of the IN constraint using code like the following: +** +**
    +**    for(rc=sqlcipher_sqlite3_vtab_in_first(pList, &pVal);
    +**        rc==SQLITE_OK && pVal
    +**        rc=sqlcipher_sqlite3_vtab_in_next(pList, &pVal)
    +**    ){
    +**      // do something with pVal
    +**    }
    +**    if( rc!=SQLITE_OK ){
    +**      // an error has occurred
    +**    }
    +** 
    )^ +** +** ^On success, the sqlcipher_sqlite3_vtab_in_first(X,P) and sqlcipher_sqlite3_vtab_in_next(X,P) +** routines return SQLITE_OK and set *P to point to the first or next value +** on the RHS of the IN constraint. ^If there are no more values on the +** right hand side of the IN constraint, then *P is set to NULL and these +** routines return [SQLITE_DONE]. ^The return value might be +** some other value, such as SQLITE_NOMEM, in the event of a malfunction. +** +** The *ppOut values returned by these routines are only valid until the +** next call to either of these routines or until the end of the xFilter +** method from which these routines were called. If the virtual table +** implementation needs to retain the *ppOut values for longer, it must make +** copies. The *ppOut values are [protected sqlcipher_sqlite3_value|protected]. +*/ +SQLITE_API int sqlcipher_sqlite3_vtab_in_first(sqlcipher_sqlite3_value *pVal, sqlcipher_sqlite3_value **ppOut); +SQLITE_API int sqlcipher_sqlite3_vtab_in_next(sqlcipher_sqlite3_value *pVal, sqlcipher_sqlite3_value **ppOut); + +/* +** CAPI3REF: Constraint values in xBestIndex() +** METHOD: sqlcipher_sqlite3_index_info +** +** This API may only be used from within the [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this interface +** from outside of an xBestIndex method are undefined and probably harmful. +** +** ^When the sqlcipher_sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within +** the [xBestIndex] method of a [virtual table] implementation, with P being +** a copy of the [sqlcipher_sqlite3_index_info] object pointer passed into xBestIndex and +** J being a 0-based index into P->aConstraint[], then this routine +** attempts to set *V to the value of the right-hand operand of +** that constraint if the right-hand operand is known. ^If the +** right-hand operand is not known, then *V is set to a NULL pointer. +** ^The sqlcipher_sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if +** and only if *V is set to a value. ^The sqlcipher_sqlite3_vtab_rhs_value(P,J,V) +** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th +** constraint is not available. ^The sqlcipher_sqlite3_vtab_rhs_value() interface +** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if +** something goes wrong. +** +** The sqlcipher_sqlite3_vtab_rhs_value() interface is usually only successful if +** the right-hand operand of a constraint is a literal value in the original +** SQL statement. If the right-hand operand is an expression or a reference +** to some other column or a [host parameter], then sqlcipher_sqlite3_vtab_rhs_value() +** will probably return [SQLITE_NOTFOUND]. +** +** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and +** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such +** constraints, sqlcipher_sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^ +** +** ^The [sqlcipher_sqlite3_value] object returned in *V is a protected sqlcipher_sqlite3_value +** and remains valid for the duration of the xBestIndex method call. +** ^When xBestIndex returns, the sqlcipher_sqlite3_value object returned by +** sqlcipher_sqlite3_vtab_rhs_value() is automatically deallocated. +** +** The "_rhs_" in the name of this routine is an abbreviation for +** "Right-Hand Side". +*/ +SQLITE_API int sqlcipher_sqlite3_vtab_rhs_value(sqlcipher_sqlite3_index_info*, int, sqlcipher_sqlite3_value **ppVal); + /* ** CAPI3REF: Conflict resolution modes ** KEYWORDS: {conflict resolution mode} @@ -9564,6 +10101,15 @@ SQLITE_API int sqlcipher_sqlite3_db_cacheflush(sqlcipher_sqlite3*); ** triggers; or 2 for changes resulting from triggers called by top-level ** triggers; and so forth. ** +** When the [sqlcipher_sqlite3_blob_write()] API is used to update a blob column, +** the pre-update hook is invoked with SQLITE_DELETE. This is because the +** in this case the new values are not available. In this case, when a +** callback made with op==SQLITE_DELETE is actuall a write using the +** sqlcipher_sqlite3_blob_write() API, the [sqlcipher_sqlite3_preupdate_blobwrite()] returns +** the index of the column being written. In other cases, where the +** pre-update hook is being invoked for some other reason, including a +** regular DELETE, sqlcipher_sqlite3_preupdate_blobwrite() returns -1. +** ** See also: [sqlcipher_sqlite3_update_hook()] */ #if defined(SQLITE_ENABLE_PREUPDATE_HOOK) @@ -9584,6 +10130,7 @@ SQLITE_API int sqlcipher_sqlite3_preupdate_old(sqlcipher_sqlite3 *, int, sqlciph SQLITE_API int sqlcipher_sqlite3_preupdate_count(sqlcipher_sqlite3 *); SQLITE_API int sqlcipher_sqlite3_preupdate_depth(sqlcipher_sqlite3 *); SQLITE_API int sqlcipher_sqlite3_preupdate_new(sqlcipher_sqlite3 *, int, sqlcipher_sqlite3_value **); +SQLITE_API int sqlcipher_sqlite3_preupdate_blobwrite(sqlcipher_sqlite3 *); #endif /* @@ -9822,8 +10369,8 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlcipher_sqlite3_snapshot_recover(sqlcipher_ ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory ** allocation error occurs. ** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_DESERIALIZE] option. +** This interface is omitted if SQLite is compiled with the +** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API unsigned char *sqlcipher_sqlite3_serialize( sqlcipher_sqlite3 *db, /* The database connection */ @@ -9870,12 +10417,16 @@ SQLITE_API unsigned char *sqlcipher_sqlite3_serialize( ** database is currently in a read transaction or is involved in a backup ** operation. ** +** It is not possible to deserialized into the TEMP database. If the +** S argument to sqlcipher_sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the +** function returns SQLITE_ERROR. +** ** If sqlcipher_sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then ** [sqlcipher_sqlite3_free()] is invoked on argument P prior to returning. ** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_DESERIALIZE] option. +** This interface is omitted if SQLite is compiled with the +** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API int sqlcipher_sqlite3_deserialize( sqlcipher_sqlite3 *db, /* The database connection */ @@ -10124,6 +10675,38 @@ SQLITE_API int sqlcipher_sqlite3session_create( */ SQLITE_API void sqlcipher_sqlite3session_delete(sqlcipher_sqlite3_session *pSession); +/* +** CAPIREF: Conigure a Session Object +** METHOD: sqlcipher_sqlite3_session +** +** This method is used to configure a session object after it has been +** created. At present the only valid value for the second parameter is +** [SQLITE_SESSION_OBJCONFIG_SIZE]. +** +** Arguments for sqlcipher_sqlite3session_object_config() +** +** The following values may passed as the the 4th parameter to +** sqlcipher_sqlite3session_object_config(). +** +**
    SQLITE_SESSION_OBJCONFIG_SIZE
    +** This option is used to set, clear or query the flag that enables +** the [sqlcipher_sqlite3session_changeset_size()] API. Because it imposes some +** computational overhead, this API is disabled by default. Argument +** pArg must point to a value of type (int). If the value is initially +** 0, then the sqlcipher_sqlite3session_changeset_size() API is disabled. If it +** is greater than 0, then the same API is enabled. Or, if the initial +** value is less than zero, no change is made. In all cases the (int) +** variable is set to 1 if the sqlcipher_sqlite3session_changeset_size() API is +** enabled following the current call, or 0 otherwise. +** +** It is an error (SQLITE_MISUSE) to attempt to modify this setting after +** the first table has been attached to the session object. +*/ +SQLITE_API int sqlcipher_sqlite3session_object_config(sqlcipher_sqlite3_session*, int op, void *pArg); + +/* +*/ +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 /* ** CAPI3REF: Enable Or Disable A Session Object @@ -10368,6 +10951,22 @@ SQLITE_API int sqlcipher_sqlite3session_changeset( void **ppChangeset /* OUT: Buffer containing changeset */ ); +/* +** CAPI3REF: Return An Upper-limit For The Size Of The Changeset +** METHOD: sqlcipher_sqlite3_session +** +** By default, this function always returns 0. For it to return +** a useful result, the sqlcipher_sqlite3_session object must have been configured +** to enable this API using sqlcipher_sqlite3session_object_config() with the +** SQLITE_SESSION_OBJCONFIG_SIZE verb. +** +** When enabled, this function returns an upper limit, in bytes, for the size +** of the changeset that might be produced if sqlcipher_sqlite3session_changeset() were +** called. The final changeset size might be equal to or smaller than the +** size in bytes returned by this function. +*/ +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3session_changeset_size(sqlcipher_sqlite3_session *pSession); + /* ** CAPI3REF: Load The Difference Between Tables Into A Session ** METHOD: sqlcipher_sqlite3_session @@ -10485,6 +11084,14 @@ SQLITE_API int sqlcipher_sqlite3session_patchset( */ SQLITE_API int sqlcipher_sqlite3session_isempty(sqlcipher_sqlite3_session *pSession); +/* +** CAPI3REF: Query for the amount of heap memory used by a session object. +** +** This API returns the total amount of heap memory in bytes currently +** used by the session object passed as the only argument. +*/ +SQLITE_API sqlcipher_sqlite3_int64 sqlcipher_sqlite3session_memory_used(sqlcipher_sqlite3_session *pSession); + /* ** CAPI3REF: Create An Iterator To Traverse A Changeset ** CONSTRUCTOR: sqlcipher_sqlite3_changeset_iter @@ -10587,18 +11194,23 @@ SQLITE_API int sqlcipher_sqlite3changeset_next(sqlcipher_sqlite3_changeset_iter ** call to [sqlcipher_sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this ** is not the case, this function returns [SQLITE_MISUSE]. ** -** If argument pzTab is not NULL, then *pzTab is set to point to a -** nul-terminated utf-8 encoded string containing the name of the table -** affected by the current change. The buffer remains valid until either -** sqlcipher_sqlite3changeset_next() is called on the iterator or until the -** conflict-handler function returns. If pnCol is not NULL, then *pnCol is -** set to the number of columns in the table affected by the change. If -** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change +** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three +** outputs are set through these pointers: +** +** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], +** depending on the type of change that the iterator currently points to; +** +** *pnCol is set to the number of columns in the table affected by the change; and +** +** *pzTab is set to point to a nul-terminated utf-8 encoded string containing +** the name of the table affected by the current change. The buffer remains +** valid until either sqlcipher_sqlite3changeset_next() is called on the iterator +** or until the conflict-handler function returns. +** +** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change ** is an indirect change, or false (0) otherwise. See the documentation for ** [sqlcipher_sqlite3session_indirect()] for a description of direct and indirect -** changes. Finally, if pOp is not NULL, then *pOp is set to one of -** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the -** type of change that the iterator currently points to. +** changes. ** ** If no error occurs, SQLITE_OK is returned. If an error does occur, an ** SQLite error code is returned. The values of the output variables may not diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher.ts new file mode 100644 index 0000000..876a5b8 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher.ts @@ -0,0 +1,34 @@ + + + + + DbSqliteCipher + + + Password (key) + + + + + Leave empty to create or connect to decrypted database. + + + + + Encryption password + + + + + Cipher configuration (optional) + + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_af_ZA.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_af_ZA.ts new file mode 100644 index 0000000..bf0965e --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_af_ZA.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ar_SA.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ar_SA.ts new file mode 100644 index 0000000..9bd8d76 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ar_SA.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ca_ES.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ca_ES.ts new file mode 100644 index 0000000..57b2c49 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ca_ES.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_cs_CZ.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_cs_CZ.ts new file mode 100644 index 0000000..528339c --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_cs_CZ.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_da_DK.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_da_DK.ts new file mode 100644 index 0000000..18b81f3 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_da_DK.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_de_DE.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_de_DE.ts new file mode 100644 index 0000000..4a99fad --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_de_DE.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Passwort (Schlüssel) + + + + Leave empty to create or connect to decrypted database. + Leer lassen, um die entschlüsselte Datenbank zu erstellen oder sich damit zu verbinden. + + + + Encryption password + Verschlüsselungspasswort + + + + Cipher configuration (optional) + Cipher-Konfiguration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA Anweisungen zur Anpassung der SQLCipher Konfiguration, wie z. B. KDF-Iterationen, Legacy-Modus, etc. +Sie werden bei jedem Öffnen der Datenbank ausgeführt. +Siehe Dokumentation für SQLCipher für Details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_el_GR.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_el_GR.ts new file mode 100644 index 0000000..be2857b --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_el_GR.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_en_US.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_en_US.ts new file mode 100644 index 0000000..a39673b --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_en_US.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_es_ES.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_es_ES.ts new file mode 100644 index 0000000..5ba7ed5 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_es_ES.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Contraseña (clave) + + + + Leave empty to create or connect to decrypted database. + Dejar en blanco para crear o conectarse a una base de datos desencriptada. + + + + Encryption password + Contraseña de encriptado + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_fa_IR.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_fa_IR.ts new file mode 100644 index 0000000..cdb29fb --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_fa_IR.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_fi_FI.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_fi_FI.ts new file mode 100644 index 0000000..a46be5d --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_fi_FI.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_fr_FR.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_fr_FR.ts new file mode 100644 index 0000000..a424d07 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_fr_FR.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Mot de passe (clé) + + + + Leave empty to create or connect to decrypted database. + Laisser vide pour créer ou se connecter à la base de données déchiffrée. + + + + Encryption password + Mot de passe de chiffrement + + + + Cipher configuration (optional) + Configuration du chiffrement (optionnel) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_he_IL.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_he_IL.ts new file mode 100644 index 0000000..403982f --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_he_IL.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + ססמה (מפתח) + + + + Leave empty to create or connect to decrypted database. + להש×יר ריק ליצירת ×ו התחברות למסד × ×ª×•× ×™× ×œ× ×ž×•×¦×¤×Ÿ. + + + + Encryption password + ססמה מוצפנת + + + + Cipher configuration (optional) + תצורת צופן (חלופי) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + הצהרות PRAGMA להת×מה ×ישית של תצורת SQLCipher, כגון חִזְרוּר KDF, מצב מורשת ועוד. +יופעלו ×¢× ×›×œ פתיחה של מסד הנתוני×. +לפרטי×, × × ×¢×™×™×Ÿ בתיעוד SQLCipher. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_hu_HU.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_hu_HU.ts new file mode 100644 index 0000000..1ec5e9d --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_hu_HU.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Jelszó (kulcs) + + + + Leave empty to create or connect to decrypted database. + Titkosítás nélküli adatbázishoz csatlakozáshoz vagy létrehozásához hagyja üresen. + + + + Encryption password + Titkosító jelszó + + + + Cipher configuration (optional) + Titkosítás konfiguráció (opcionális) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA kifejezések az SQLCipher konfigurálásához, mint például KDF iterációk, legacy mód stb. +Az adatbázis minden megnyitásakor végre lesznek hajtva. +Lásd az SQLCipher dokumentációját a részletekhez. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_it_IT.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_it_IT.ts new file mode 100644 index 0000000..cb06d40 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_it_IT.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (chiave) + + + + Leave empty to create or connect to decrypted database. + Lasciare vuoto per creare o connettersi al database decriptato. + + + + Encryption password + Password di crittografia + + + + Cipher configuration (optional) + Configurazione di Cifratura (opzionale) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + Dichiarazioni PRAGMA per personalizzare la configurazione di SQLCipher, come iterazioni KDF, modalità legacy, ecc. +Esse verranno eseguite dopo ogni apertura del database. +Vedi la documentazione per SQLCipher per dettagli. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ja_JP.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ja_JP.ts new file mode 100644 index 0000000..f98f0a8 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ja_JP.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_kaa.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_kaa.ts new file mode 100644 index 0000000..7f2d7fb --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_kaa.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ko_KR.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ko_KR.ts new file mode 100644 index 0000000..8ea0420 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ko_KR.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_nl_NL.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_nl_NL.ts new file mode 100644 index 0000000..a4084e8 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_nl_NL.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_no_NO.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_no_NO.ts new file mode 100644 index 0000000..c84c172 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_no_NO.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_pl_PL.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_pl_PL.ts new file mode 100644 index 0000000..1db1a17 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_pl_PL.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + HasÅ‚o (klucz) + + + + Leave empty to create or connect to decrypted database. + Pozostaw puste, aby stworzyć lub połączyć siÄ™ z niezaszyfrowanÄ… bazÄ…. + + + + Encryption password + HasÅ‚o szyfrowania + + + + Cipher configuration (optional) + Konfiguracja szyfrowania (opcjonalna) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + Instrukcje PRAGMA dostosowujÄ…ce konfiguracjÄ™ SQLCipher, takie jak liczba iteracji KDF, starszy tryb, itp. +ZostanÄ… one wykonane po każdym otwarciu bazy danych. +Zobacz dokumentacjÄ™ dla SQLCipher, aby poznać wiÄ™cej szczegółów. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_pt_BR.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_pt_BR.ts new file mode 100644 index 0000000..2601958 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_pt_BR.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Senha (chave) + + + + Leave empty to create or connect to decrypted database. + Deixe em branco para criar ou conectar ao banco de dados descriptografado. + + + + Encryption password + Senha criptografada + + + + Cipher configuration (optional) + Configuração de cifra (opcional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + Declarações PRAGMA para personalizar a configuração SQLCipher, como iterações KDF, modo legado, etc. +Eles serão executados após cada abertura do banco de dados. +Consulte a documentação de SQLCipher para obter detalhes. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_pt_PT.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_pt_PT.ts new file mode 100644 index 0000000..88372ad --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_pt_PT.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ro_RO.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ro_RO.ts new file mode 100644 index 0000000..f89a267 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ro_RO.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ru_RU.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ru_RU.ts new file mode 100644 index 0000000..ce4991c --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_ru_RU.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Пароль (ключ) + + + + Leave empty to create or connect to decrypted database. + ОÑтавьте пуÑтым Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº раÑшифрованной базе данных. + + + + Encryption password + Пароль Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ + + + + Cipher configuration (optional) + ÐаÑтройки ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ (необÑзательно) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + КонÑтрукции PRAGMA Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ð°Ñтроек SQLCipher, таких как количеÑтво итераций KDF (функции Ñ„Ð¾Ñ€Ð¼Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ»ÑŽÑ‡Ð°), режим ÑовмеÑтимоÑти и Ñ‚. д. +Они будут выполнÑтьÑÑ Ð¿Ñ€Ð¸ каждом открытии базы данных. +Ð”Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации Ñм. документацию к SQLCipher. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_sk_SK.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_sk_SK.ts new file mode 100644 index 0000000..544a00a --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_sk_SK.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_sr_SP.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_sr_SP.ts new file mode 100644 index 0000000..86e02f0 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_sr_SP.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_sv_SE.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_sv_SE.ts new file mode 100644 index 0000000..c69c8e6 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_sv_SE.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_tr_TR.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_tr_TR.ts new file mode 100644 index 0000000..00fece1 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_tr_TR.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_uk_UA.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_uk_UA.ts new file mode 100644 index 0000000..39ca873 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_uk_UA.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_vi_VN.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_vi_VN.ts new file mode 100644 index 0000000..be657ba --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_vi_VN.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_zh_CN.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_zh_CN.ts new file mode 100644 index 0000000..01c5865 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_zh_CN.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + 密ç ï¼ˆå¯†é’¥ï¼‰ + + + + Leave empty to create or connect to decrypted database. + 留空则创建或连接到已解密的数æ®åº“。 + + + + Encryption password + åŠ å¯†å¯†ç  + + + + Cipher configuration (optional) + 加密算法é…置(å¯é€‰ï¼‰ + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + 用于自定义 SQLCiper é…置的 PRAGMA 语å¥ï¼Œå¦‚ KDF 迭代ã€ä¼ ç»Ÿæ¨¡å¼ç­‰ã€‚ +å°†åœ¨æ¯æ¬¡æ‰“开数æ®åº“时执行。 +细节请å‚考 SQLCipher 文档。 + + + diff --git a/Plugins/DbSqliteCipher/translations/DbSqliteCipher_zh_TW.ts b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_zh_TW.ts new file mode 100644 index 0000000..7945d66 --- /dev/null +++ b/Plugins/DbSqliteCipher/translations/DbSqliteCipher_zh_TW.ts @@ -0,0 +1,36 @@ + + + + + DbSqliteCipher + + + Password (key) + 密碼 (金鑰) + + + + Leave empty to create or connect to decrypted database. + 留空則建立或連線到已解密的資料庫。 + + + + Encryption password + 加密密碼 + + + + Cipher configuration (optional) + 加密演算法設定檔 (å¯é¸) + + + + PRAGMA statements to customize SQLCipher configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLCipher for details. + 用於自訂 SQLCiper 設定檔的 PRAGMA 語å¥ï¼Œå¦‚ KDF 迭代ã€å‚³çµ±æ¨¡å¼ç­‰ã€‚ +å°‡åœ¨æ¯æ¬¡é–‹å•Ÿè³‡æ–™åº«æ™‚執行。 +細節請åƒè€ƒ SQLCipher 文件。 + + + diff --git a/Plugins/DbSqliteCipher/update_sqlite_version.tcl b/Plugins/DbSqliteCipher/update_sqlite_version.tcl index d0d52a7..4b6b04e 100644 --- a/Plugins/DbSqliteCipher/update_sqlite_version.tcl +++ b/Plugins/DbSqliteCipher/update_sqlite_version.tcl @@ -5,7 +5,7 @@ # Download page: # https://github.com/sqlcipher/sqlcipher/releases -set THE_URL "https://codeload.github.com/sqlcipher/sqlcipher/zip/v4.4.3" +set THE_URL "https://codeload.github.com/sqlcipher/sqlcipher/zip/v4.5.3" proc process {} { if {[catch { diff --git a/Plugins/DbSqliteWx/dbsqlitewx.json b/Plugins/DbSqliteWx/dbsqlitewx.json index 307c513..ca60b78 100644 --- a/Plugins/DbSqliteWx/dbsqlitewx.json +++ b/Plugins/DbSqliteWx/dbsqlitewx.json @@ -3,6 +3,6 @@ "title": "WxSQLite3", "description": "Provides support for WxSQLite3/SQLCipher/System.Data.SQLite3 databases, including encryption.", "minAppVersion": 30300, - "version": 10000, + "version": 10002, "author": "SalSoft" } diff --git a/Plugins/DbSqliteWx/dbsqlitewxinstance.cpp b/Plugins/DbSqliteWx/dbsqlitewxinstance.cpp index 2462c77..0335976 100644 --- a/Plugins/DbSqliteWx/dbsqlitewxinstance.cpp +++ b/Plugins/DbSqliteWx/dbsqlitewxinstance.cpp @@ -6,6 +6,16 @@ DbSqliteWxInstance::DbSqliteWxInstance(const QString& name, const QString& path, { } +Db* DbSqliteWxInstance::clone() const +{ + return new DbSqliteWxInstance(name, path, connOptions); +} + +QString DbSqliteWxInstance::getTypeClassName() const +{ + return "DbSqliteWxInstance"; +} + void DbSqliteWxInstance::initAfterOpen() { SqlQueryPtr res; @@ -30,7 +40,7 @@ void DbSqliteWxInstance::initAfterOpen() QString key = connOptions[DbSqliteWx::PASSWORD_OPT].toString(); if (!key.isEmpty()) { - res = exec(QString("PRAGMA key = '%1';").arg(key), Flag::NO_LOCK); + res = exec(QString("PRAGMA key = '%1';").arg(escapeString(key)), Flag::NO_LOCK); if (res->isError()) qWarning() << "Error while defining WxSqlite3 key:" << res->getErrorText(); } diff --git a/Plugins/DbSqliteWx/dbsqlitewxinstance.h b/Plugins/DbSqliteWx/dbsqlitewxinstance.h index bc313af..63434f4 100644 --- a/Plugins/DbSqliteWx/dbsqlitewxinstance.h +++ b/Plugins/DbSqliteWx/dbsqlitewxinstance.h @@ -2,26 +2,29 @@ #define DBSQLITEWXINSTANCE_H #include "db/abstractdb3.h" - -#ifdef WXSQLITE_SYSTEM_LIB + +#ifdef WXSQLITE_SYSTEM_LIB # include "wxsqlite3_unmodified.h" -#else +#else # include "wxsqlite3.h" -#endif - +#endif + #include "db/stdsqlite3driver.h" -#ifdef WXSQLITE_SYSTEM_LIB +#ifdef WXSQLITE_SYSTEM_LIB STD_SQLITE3_DRIVER(WxSQLite, "WxSQLite3",,) -#else +#else STD_SQLITE3_DRIVER(WxSQLite, "WxSQLite3", wx_,) -#endif +#endif class DbSqliteWxInstance : public AbstractDb3 { public: DbSqliteWxInstance(const QString &name, const QString &path, const QHash &connOptions); + Db* clone() const; + QString getTypeClassName() const; + protected: void initAfterOpen(); QString getAttachSql(Db* otherDb, const QString& generatedAttachName); diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx.ts new file mode 100644 index 0000000..cd9a1ac --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx.ts @@ -0,0 +1,44 @@ + + + + + DbSqliteWx + + + Password (key) + + + + + Leave empty to create or connect to decrypted database. + + + + + Encryption password + + + + + Cipher + + + + + Cipher determines encryption algorithm used to encrypt the database. + + + + + Cipher configuration (optional) + + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_af_ZA.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_af_ZA.ts new file mode 100644 index 0000000..59d6c69 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_af_ZA.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_ar_SA.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_ar_SA.ts new file mode 100644 index 0000000..5b9669c --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_ar_SA.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_ca_ES.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_ca_ES.ts new file mode 100644 index 0000000..1de1af9 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_ca_ES.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_cs_CZ.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_cs_CZ.ts new file mode 100644 index 0000000..50c5b26 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_cs_CZ.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_da_DK.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_da_DK.ts new file mode 100644 index 0000000..ef03e31 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_da_DK.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_de_DE.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_de_DE.ts new file mode 100644 index 0000000..0a2c39e --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_de_DE.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Passwort (Schlüssel) + + + + Leave empty to create or connect to decrypted database. + Leer lassen, um die entschlüsselte Datenbank zu erstellen oder sich damit zu verbinden. + + + + Encryption password + Verschlüsselungspasswort + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher bestimmt den Verschlüsselungsalgorithmus, der zur Verschlüsselung der Datenbank verwendet wird. + + + + Cipher configuration (optional) + Cipher-Konfiguration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA-Anweisungen zur Anpassung der Konfiguration von SQLite3 Multiple Ciphers wie KDF-Iterationen, Legacy-Modus, etc. +Sie werden bei jedem Öffnen der Datenbank ausgeführt. +Siehe Dokumentation für SQLite3 Multiple Ciphers für Details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_el_GR.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_el_GR.ts new file mode 100644 index 0000000..dde5ffa --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_el_GR.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_en_US.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_en_US.ts new file mode 100644 index 0000000..db54852 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_en_US.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_es_ES.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_es_ES.ts new file mode 100644 index 0000000..a6e986e --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_es_ES.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_fa_IR.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_fa_IR.ts new file mode 100644 index 0000000..09f2599 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_fa_IR.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_fi_FI.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_fi_FI.ts new file mode 100644 index 0000000..5eba9a5 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_fi_FI.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_fr_FR.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_fr_FR.ts new file mode 100644 index 0000000..3e7f2f5 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_fr_FR.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Mot de passe (clé) + + + + Leave empty to create or connect to decrypted database. + Laisser vide pour créer ou se connecter à la base de données déchiffrée. + + + + Encryption password + Mot de passe de chiffrement + + + + Cipher + Chiffrement + + + + Cipher determines encryption algorithm used to encrypt the database. + Le chiffrement détermine l'algorithme de chiffrement utilisé pour chiffrer la base de données. + + + + Cipher configuration (optional) + Configuration du chiffrement (optionnel) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_he_IL.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_he_IL.ts new file mode 100644 index 0000000..4e43f65 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_he_IL.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + ססמה (מפתח) + + + + Leave empty to create or connect to decrypted database. + להש×יר ריק ליצירת ×ו התחברות למסד × ×ª×•× ×™× ×œ× ×ž×•×¦×¤×Ÿ. + + + + Encryption password + ססמה מוצפנת + + + + Cipher + צוֹפֶן + + + + Cipher determines encryption algorithm used to encrypt the database. + צופן קובעת ××œ×’×•×¨×™×ª× ×”×”×¦×¤× ×” שישמש להצפנת מסד הנתוני×. + + + + Cipher configuration (optional) + תצורת צופן (חלופי) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + הצהרות PRAGMA להת×מה ×ישית של תצורת ריבוי צופן SQLite3, כגון חִזְרוּר KDF, מצב מורשת ועוד. +יופעלו ×¢× ×›×œ פתיחה של מסד הנתוני×. +לפרטי×, × × ×¢×™×™×Ÿ בתיעוד ריבוי צופן SQLite3. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_hu_HU.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_hu_HU.ts new file mode 100644 index 0000000..8801c47 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_hu_HU.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_it_IT.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_it_IT.ts new file mode 100644 index 0000000..17b003f --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_it_IT.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (chiave) + + + + Leave empty to create or connect to decrypted database. + Lasciare vuoto per creare o connettersi al database decriptato. + + + + Encryption password + Password di crittografia + + + + Cipher + Cifratura + + + + Cipher determines encryption algorithm used to encrypt the database. + La cifratura determina l'algoritmo di crittografia utilizzato per crittografare il database. + + + + Cipher configuration (optional) + Configurazione di Cifratura (opzionale) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + Dichiarazioni PRAGMA per personalizzare la configurazione di SQLite3 Cifrature multiple, come iterazioni KDF, modalità legacy, ecc. +Verranno eseguite dopo ogni apertura del database. +Vedi la documentazione per SQLite3 Cifratori Multipli per i dettagli. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_ja_JP.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_ja_JP.ts new file mode 100644 index 0000000..8d8fcf0 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_ja_JP.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_kaa.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_kaa.ts new file mode 100644 index 0000000..0ac26a3 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_kaa.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_ko_KR.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_ko_KR.ts new file mode 100644 index 0000000..0a28236 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_ko_KR.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_nl_NL.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_nl_NL.ts new file mode 100644 index 0000000..faa2177 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_nl_NL.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_no_NO.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_no_NO.ts new file mode 100644 index 0000000..dda884f --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_no_NO.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_pl_PL.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_pl_PL.ts new file mode 100644 index 0000000..f6ae2ed --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_pl_PL.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + HasÅ‚o (klucz) + + + + Leave empty to create or connect to decrypted database. + Pozostaw puste, aby utworzyć lub połączyć siÄ™ z odszyfrowanÄ… bazÄ… danych. + + + + Encryption password + HasÅ‚o szyfrowania + + + + Cipher + Szyfr + + + + Cipher determines encryption algorithm used to encrypt the database. + Szyfr okreÅ›la algorytm szyfrowania używany do szyfrowania bazy danych. + + + + Cipher configuration (optional) + Konfiguracja szyfru (opcjonalnie) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + Instrukcje PRAGMA do dostosowywania konfiguracji SQLite3 Multiple Ciphers, takich jak iteracje KDF, tryb zgodnoÅ›ci itp. +ZostanÄ… one wykonane po każdym otwarciu bazy danych. +Zobacz dokumentacjÄ™ dla SQLite3 Multiple Ciphers, aby poznać szczegóły. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_pt_BR.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_pt_BR.ts new file mode 100644 index 0000000..18618a9 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_pt_BR.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Senha (chave) + + + + Leave empty to create or connect to decrypted database. + Deixe em branco para criar ou conectar ao banco de dados descriptografado. + + + + Encryption password + Senha criptografada + + + + Cipher + Cifra + + + + Cipher determines encryption algorithm used to encrypt the database. + A cifra determina o algoritmo de criptografia usado para criptografar o banco de dados. + + + + Cipher configuration (optional) + Configuração de cifra (opcional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + Declarações PRAGMA para personalizar a configuração de múltiplas Cifras SQLite3, como iterações KDF, modo legado, etc. +Eles serão executados após cada abertura do banco de dados. +Consulte a documentação de SQLite3 Múltiplas Cifras para obter detalhes. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_pt_PT.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_pt_PT.ts new file mode 100644 index 0000000..c0f9d4d --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_pt_PT.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_ro_RO.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_ro_RO.ts new file mode 100644 index 0000000..bd97806 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_ro_RO.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_ru_RU.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_ru_RU.ts new file mode 100644 index 0000000..ade5607 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_ru_RU.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Пароль (ключ) + + + + Leave empty to create or connect to decrypted database. + ОÑтавьте пуÑтым Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº раÑшифрованной базе данных. + + + + Encryption password + Пароль Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ + + + + Cipher + Шифр + + + + Cipher determines encryption algorithm used to encrypt the database. + Шифр определÑет алгоритм шифрованиÑ, иÑпользуемый Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных. + + + + Cipher configuration (optional) + ÐаÑтройки ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ (необÑзательно) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + КонÑтрукции PRAGMA Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ð°Ñтроек SQLite3 Multiple Ciphers, таких как количеÑтво итераций KDF (функции Ñ„Ð¾Ñ€Ð¼Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ»ÑŽÑ‡Ð°), режим ÑовмеÑтимоÑти и Ñ‚. д. +Они будут выполнÑтьÑÑ Ð¿Ñ€Ð¸ каждом открытии базы данных. +Ð”Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации Ñм. документацию к SQLite3 Multiple Ciphers. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_sk_SK.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_sk_SK.ts new file mode 100644 index 0000000..7115513 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_sk_SK.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_sr_SP.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_sr_SP.ts new file mode 100644 index 0000000..f02683c --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_sr_SP.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_sv_SE.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_sv_SE.ts new file mode 100644 index 0000000..76b6602 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_sv_SE.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_tr_TR.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_tr_TR.ts new file mode 100644 index 0000000..117e81b --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_tr_TR.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_uk_UA.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_uk_UA.ts new file mode 100644 index 0000000..bc4cb05 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_uk_UA.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_vi_VN.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_vi_VN.ts new file mode 100644 index 0000000..7584a82 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_vi_VN.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + Password (key) + + + + Leave empty to create or connect to decrypted database. + Leave empty to create or connect to decrypted database. + + + + Encryption password + Encryption password + + + + Cipher + Cipher + + + + Cipher determines encryption algorithm used to encrypt the database. + Cipher determines encryption algorithm used to encrypt the database. + + + + Cipher configuration (optional) + Cipher configuration (optional) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_zh_CN.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_zh_CN.ts new file mode 100644 index 0000000..3718398 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_zh_CN.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + 密ç ï¼ˆå¯†é’¥ï¼‰ + + + + Leave empty to create or connect to decrypted database. + 留空则创建或连接到已解密的数æ®åº“。 + + + + Encryption password + åŠ å¯†å¯†ç  + + + + Cipher + 加密算法 + + + + Cipher determines encryption algorithm used to encrypt the database. + 用æ¥åŠ å¯†è¯¥æ•°æ®åº“的加密算法。 + + + + Cipher configuration (optional) + 加密算法é…置(å¯é€‰ï¼‰ + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + 用于自定义 SQLite3 Multiple Ciphers é…置的 PRAGMA 语å¥ï¼Œå¦‚ KDF 迭代ã€ä¼ ç»Ÿæ¨¡å¼ç­‰ã€‚ +å°†åœ¨æ¯æ¬¡æ‰“开数æ®åº“时执行。 +细节请å‚考 SQLite3 Multiple Ciphers 文档。 + + + diff --git a/Plugins/DbSqliteWx/translations/DbSqliteWx_zh_TW.ts b/Plugins/DbSqliteWx/translations/DbSqliteWx_zh_TW.ts new file mode 100644 index 0000000..7558899 --- /dev/null +++ b/Plugins/DbSqliteWx/translations/DbSqliteWx_zh_TW.ts @@ -0,0 +1,46 @@ + + + + + DbSqliteWx + + + Password (key) + 密碼 (金鑰) + + + + Leave empty to create or connect to decrypted database. + 留空則建立或連線到已解密的資料庫。 + + + + Encryption password + 加密密碼 + + + + Cipher + 加密演算法 + + + + Cipher determines encryption algorithm used to encrypt the database. + 用來加密該資料庫的加密演算法。 + + + + Cipher configuration (optional) + 加密演算法設定檔 (å¯é¸) + + + + PRAGMA statements to customize SQLite3 Multiple Ciphers configuration, such as KDF iterations, legacy mode, etc. +They will be executed upon each opening of the database. +See documentation for SQLite3 Multiple Ciphers for details. + 用於自訂 SQLite3 Multiple Ciphers 設定檔的 PRAGMA 語å¥ï¼Œå¦‚ KDF 迭代ã€å‚³çµ±æ¨¡å¼ç­‰ã€‚ +å°‡åœ¨æ¯æ¬¡é–‹å•Ÿè³‡æ–™åº«æ™‚執行。 +細節請åƒè€ƒ SQLite3 Multiple Ciphers 文件。 + + + diff --git a/Plugins/DbSqliteWx/update_sqlite_version.tcl b/Plugins/DbSqliteWx/update_sqlite_version.tcl index 8a47129..9f4966f 100644 --- a/Plugins/DbSqliteWx/update_sqlite_version.tcl +++ b/Plugins/DbSqliteWx/update_sqlite_version.tcl @@ -4,7 +4,7 @@ # https://github.com/utelle/wxsqlite3/releases # Link from download page will redicrect to the codeload..... -set THE_URL "https://github.com/utelle/SQLite3MultipleCiphers/releases/download/v1.2.4/sqlite3mc-1.2.4-sqlite-3.35.4-amalgamation.zip" +set THE_URL "https://github.com/utelle/SQLite3MultipleCiphers/releases/download/v1.6.2/sqlite3mc-1.6.2-sqlite-3.41.2-amalgamation.zip" set SRC_DIR "src" set FILES [list \ diff --git a/Plugins/DbSqliteWx/wxsqlite3.c b/Plugins/DbSqliteWx/wxsqlite3.c index e3ee66b..1b306bf 100644 --- a/Plugins/DbSqliteWx/wxsqlite3.c +++ b/Plugins/DbSqliteWx/wxsqlite3.c @@ -3,7 +3,7 @@ ** Purpose: Amalgamation of the SQLite3 Multiple Ciphers encryption extension for SQLite ** Author: Ulrich Telle ** Created: 2020-02-28 -** Copyright: (c) 2006-2020 Ulrich Telle +** Copyright: (c) 2006-2022 Ulrich Telle ** License: MIT */ @@ -45,21 +45,44 @@ void wx_sqlite3mc_shutdown(void); /* ** Enable the user authentication feature */ +#if !SQLITE_USER_AUTHENTICATION +/* Option not defined or explicitly disabled */ #ifndef SQLITE_USER_AUTHENTICATION +/* Option not defined, therefore enable by default */ #define SQLITE_USER_AUTHENTICATION 1 +#else +/* Option defined and disabled, therefore undefine option */ +#undef SQLITE_USER_AUTHENTICATION +#endif #endif #if defined(_WIN32) || defined(WIN32) + +#ifndef SQLITE3MC_USE_RAND_S +#define SQLITE3MC_USE_RAND_S 1 +#endif + +#if SQLITE3MC_USE_RAND_S +/* Force header stdlib.h to define rand_s() */ +#if !defined(_CRT_RAND_S) +#define _CRT_RAND_S +#endif +#endif + +#ifndef SQLITE_API +#define SQLITE_API +#endif + #include /* SQLite functions only needed on Win32 */ -extern void wx_sqlite3_win32_write_debug(const char*, int); -extern char *wx_sqlite3_win32_unicode_to_utf8(LPCWSTR); -extern char *wx_sqlite3_win32_mbcs_to_utf8(const char*); -extern char *wx_sqlite3_win32_mbcs_to_utf8_v2(const char*, int); -extern char *wx_sqlite3_win32_utf8_to_mbcs(const char*); -extern char *wx_sqlite3_win32_utf8_to_mbcs_v2(const char*, int); -extern LPWSTR wx_sqlite3_win32_utf8_to_unicode(const char*); +extern SQLITE_API void wx_sqlite3_win32_write_debug(const char*, int); +extern SQLITE_API char *wx_sqlite3_win32_unicode_to_utf8(LPCWSTR); +extern SQLITE_API char *wx_sqlite3_win32_mbcs_to_utf8(const char*); +extern SQLITE_API char *wx_sqlite3_win32_mbcs_to_utf8_v2(const char*, int); +extern SQLITE_API char *wx_sqlite3_win32_utf8_to_mbcs(const char*); +extern SQLITE_API char *wx_sqlite3_win32_utf8_to_mbcs_v2(const char*, int); +extern SQLITE_API LPWSTR wx_sqlite3_win32_utf8_to_unicode(const char*); #endif /* @@ -69,7 +92,7 @@ extern LPWSTR wx_sqlite3_win32_utf8_to_unicode(const char*); /*** Begin of #include "wx_sqlite3patched.c" ***/ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.35.4. By combining all the individual C code files into this +** version 3.41.2. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -91,774 +114,6 @@ extern LPWSTR wx_sqlite3_win32_utf8_to_unicode(const char*); #ifndef SQLITE_PRIVATE # define SQLITE_PRIVATE static #endif -/************** Begin file ctime.c *******************************************/ -/* -** 2010 February 23 -** -** 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 routines used to report what compile-time options -** SQLite was built with. -*/ - -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* IMP: R-16824-07538 */ - -/* -** Include the configuration header output by 'configure' if we're using the -** autoconf-based build -*/ -#if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) -#include "config.h" -#define SQLITECONFIG_H 1 -#endif - -/* These macros are provided to "stringify" the value of the define -** for those options in which the value is meaningful. */ -#define CTIMEOPT_VAL_(opt) #opt -#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) - -/* Like CTIMEOPT_VAL, but especially for SQLITE_DEFAULT_LOOKASIDE. This -** option requires a separate macro because legal values contain a single -** comma. e.g. (-DSQLITE_DEFAULT_LOOKASIDE="100,100") */ -#define CTIMEOPT_VAL2_(opt1,opt2) #opt1 "," #opt2 -#define CTIMEOPT_VAL2(opt) CTIMEOPT_VAL2_(opt) - -/* -** An array of names of all compile-time options. This array should -** be sorted A-Z. -** -** This array looks large, but in a typical installation actually uses -** only a handful of compile-time options, so most times this array is usually -** rather short and uses little memory space. -*/ -static const char * const wx_sqlite3azCompileOpt[] = { - -/* -** BEGIN CODE GENERATED BY tool/mkctime.tcl -*/ -#if SQLITE_32BIT_ROWID - "32BIT_ROWID", -#endif -#if SQLITE_4_BYTE_ALIGNED_MALLOC - "4_BYTE_ALIGNED_MALLOC", -#endif -#if SQLITE_64BIT_STATS - "64BIT_STATS", -#endif -#if SQLITE_ALLOW_COVERING_INDEX_SCAN - "ALLOW_COVERING_INDEX_SCAN", -#endif -#if SQLITE_ALLOW_URI_AUTHORITY - "ALLOW_URI_AUTHORITY", -#endif -#ifdef SQLITE_BITMASK_TYPE - "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE), -#endif -#if SQLITE_BUG_COMPATIBLE_20160819 - "BUG_COMPATIBLE_20160819", -#endif -#if SQLITE_CASE_SENSITIVE_LIKE - "CASE_SENSITIVE_LIKE", -#endif -#if SQLITE_CHECK_PAGES - "CHECK_PAGES", -#endif -#if defined(__clang__) && defined(__clang_major__) - "COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "." - CTIMEOPT_VAL(__clang_minor__) "." - CTIMEOPT_VAL(__clang_patchlevel__), -#elif defined(_MSC_VER) - "COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER), -#elif defined(__GNUC__) && defined(__VERSION__) - "COMPILER=gcc-" __VERSION__, -#endif -#if SQLITE_COVERAGE_TEST - "COVERAGE_TEST", -#endif -#if SQLITE_DEBUG - "DEBUG", -#endif -#if SQLITE_DEFAULT_AUTOMATIC_INDEX - "DEFAULT_AUTOMATIC_INDEX", -#endif -#if SQLITE_DEFAULT_AUTOVACUUM - "DEFAULT_AUTOVACUUM", -#endif -#ifdef SQLITE_DEFAULT_CACHE_SIZE - "DEFAULT_CACHE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_CACHE_SIZE), -#endif -#if SQLITE_DEFAULT_CKPTFULLFSYNC - "DEFAULT_CKPTFULLFSYNC", -#endif -#ifdef SQLITE_DEFAULT_FILE_FORMAT - "DEFAULT_FILE_FORMAT=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_FORMAT), -#endif -#ifdef SQLITE_DEFAULT_FILE_PERMISSIONS - "DEFAULT_FILE_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_PERMISSIONS), -#endif -#if SQLITE_DEFAULT_FOREIGN_KEYS - "DEFAULT_FOREIGN_KEYS", -#endif -#ifdef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT - "DEFAULT_JOURNAL_SIZE_LIMIT=" CTIMEOPT_VAL(SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT), -#endif -#ifdef SQLITE_DEFAULT_LOCKING_MODE - "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), -#endif -#ifdef SQLITE_DEFAULT_LOOKASIDE - "DEFAULT_LOOKASIDE=" CTIMEOPT_VAL2(SQLITE_DEFAULT_LOOKASIDE), -#endif -#if SQLITE_DEFAULT_MEMSTATUS - "DEFAULT_MEMSTATUS", -#endif -#ifdef SQLITE_DEFAULT_MMAP_SIZE - "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), -#endif -#ifdef SQLITE_DEFAULT_PAGE_SIZE - "DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_PAGE_SIZE), -#endif -#ifdef SQLITE_DEFAULT_PCACHE_INITSZ - "DEFAULT_PCACHE_INITSZ=" CTIMEOPT_VAL(SQLITE_DEFAULT_PCACHE_INITSZ), -#endif -#ifdef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS - "DEFAULT_PROXYDIR_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_PROXYDIR_PERMISSIONS), -#endif -#if SQLITE_DEFAULT_RECURSIVE_TRIGGERS - "DEFAULT_RECURSIVE_TRIGGERS", -#endif -#ifdef SQLITE_DEFAULT_ROWEST - "DEFAULT_ROWEST=" CTIMEOPT_VAL(SQLITE_DEFAULT_ROWEST), -#endif -#ifdef SQLITE_DEFAULT_SECTOR_SIZE - "DEFAULT_SECTOR_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_SECTOR_SIZE), -#endif -#ifdef SQLITE_DEFAULT_SYNCHRONOUS - "DEFAULT_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_SYNCHRONOUS), -#endif -#ifdef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT - "DEFAULT_WAL_AUTOCHECKPOINT=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_AUTOCHECKPOINT), -#endif -#ifdef SQLITE_DEFAULT_WAL_SYNCHRONOUS - "DEFAULT_WAL_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_SYNCHRONOUS), -#endif -#ifdef SQLITE_DEFAULT_WORKER_THREADS - "DEFAULT_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WORKER_THREADS), -#endif -#if SQLITE_DIRECT_OVERFLOW_READ - "DIRECT_OVERFLOW_READ", -#endif -#if SQLITE_DISABLE_DIRSYNC - "DISABLE_DIRSYNC", -#endif -#if SQLITE_DISABLE_FTS3_UNICODE - "DISABLE_FTS3_UNICODE", -#endif -#if SQLITE_DISABLE_FTS4_DEFERRED - "DISABLE_FTS4_DEFERRED", -#endif -#if SQLITE_DISABLE_INTRINSIC - "DISABLE_INTRINSIC", -#endif -#if SQLITE_DISABLE_LFS - "DISABLE_LFS", -#endif -#if SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS - "DISABLE_PAGECACHE_OVERFLOW_STATS", -#endif -#if SQLITE_DISABLE_SKIPAHEAD_DISTINCT - "DISABLE_SKIPAHEAD_DISTINCT", -#endif -#ifdef SQLITE_ENABLE_8_3_NAMES - "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES), -#endif -#if SQLITE_ENABLE_API_ARMOR - "ENABLE_API_ARMOR", -#endif -#if SQLITE_ENABLE_ATOMIC_WRITE - "ENABLE_ATOMIC_WRITE", -#endif -#if SQLITE_ENABLE_BATCH_ATOMIC_WRITE - "ENABLE_BATCH_ATOMIC_WRITE", -#endif -#if SQLITE_ENABLE_BYTECODE_VTAB - "ENABLE_BYTECODE_VTAB", -#endif -#if SQLITE_ENABLE_CEROD - "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), -#endif -#if SQLITE_ENABLE_COLUMN_METADATA - "ENABLE_COLUMN_METADATA", -#endif -#if SQLITE_ENABLE_COLUMN_USED_MASK - "ENABLE_COLUMN_USED_MASK", -#endif -#if SQLITE_ENABLE_COSTMULT - "ENABLE_COSTMULT", -#endif -#if SQLITE_ENABLE_CURSOR_HINTS - "ENABLE_CURSOR_HINTS", -#endif -#if SQLITE_ENABLE_DBSTAT_VTAB - "ENABLE_DBSTAT_VTAB", -#endif -#if SQLITE_ENABLE_EXPENSIVE_ASSERT - "ENABLE_EXPENSIVE_ASSERT", -#endif -#if SQLITE_ENABLE_FTS1 - "ENABLE_FTS1", -#endif -#if SQLITE_ENABLE_FTS2 - "ENABLE_FTS2", -#endif -#if SQLITE_ENABLE_FTS3 - "ENABLE_FTS3", -#endif -#if SQLITE_ENABLE_FTS3_PARENTHESIS - "ENABLE_FTS3_PARENTHESIS", -#endif -#if SQLITE_ENABLE_FTS3_TOKENIZER - "ENABLE_FTS3_TOKENIZER", -#endif -#if SQLITE_ENABLE_FTS4 - "ENABLE_FTS4", -#endif -#if SQLITE_ENABLE_FTS5 - "ENABLE_FTS5", -#endif -#if SQLITE_ENABLE_GEOPOLY - "ENABLE_GEOPOLY", -#endif -#if SQLITE_ENABLE_HIDDEN_COLUMNS - "ENABLE_HIDDEN_COLUMNS", -#endif -#if SQLITE_ENABLE_ICU - "ENABLE_ICU", -#endif -#if SQLITE_ENABLE_IOTRACE - "ENABLE_IOTRACE", -#endif -#if SQLITE_ENABLE_JSON1 - "ENABLE_JSON1", -#endif -#if SQLITE_ENABLE_LOAD_EXTENSION - "ENABLE_LOAD_EXTENSION", -#endif -#ifdef SQLITE_ENABLE_LOCKING_STYLE - "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), -#endif -#if SQLITE_ENABLE_MATH_FUNCTIONS - "ENABLE_MATH_FUNCTIONS", -#endif -#if SQLITE_ENABLE_MEMORY_MANAGEMENT - "ENABLE_MEMORY_MANAGEMENT", -#endif -#if SQLITE_ENABLE_MEMSYS3 - "ENABLE_MEMSYS3", -#endif -#if SQLITE_ENABLE_MEMSYS5 - "ENABLE_MEMSYS5", -#endif -#if SQLITE_ENABLE_MULTIPLEX - "ENABLE_MULTIPLEX", -#endif -#if SQLITE_ENABLE_NORMALIZE - "ENABLE_NORMALIZE", -#endif -#if SQLITE_ENABLE_NULL_TRIM - "ENABLE_NULL_TRIM", -#endif -#if SQLITE_ENABLE_OVERSIZE_CELL_CHECK - "ENABLE_OVERSIZE_CELL_CHECK", -#endif -#if SQLITE_ENABLE_PREUPDATE_HOOK - "ENABLE_PREUPDATE_HOOK", -#endif -#if SQLITE_ENABLE_QPSG - "ENABLE_QPSG", -#endif -#if SQLITE_ENABLE_RBU - "ENABLE_RBU", -#endif -#if SQLITE_ENABLE_RTREE - "ENABLE_RTREE", -#endif -#if SQLITE_ENABLE_SELECTTRACE - "ENABLE_SELECTTRACE", -#endif -#if SQLITE_ENABLE_SESSION - "ENABLE_SESSION", -#endif -#if SQLITE_ENABLE_SNAPSHOT - "ENABLE_SNAPSHOT", -#endif -#if SQLITE_ENABLE_SORTER_REFERENCES - "ENABLE_SORTER_REFERENCES", -#endif -#if SQLITE_ENABLE_SQLLOG - "ENABLE_SQLLOG", -#endif -#if defined(SQLITE_ENABLE_STAT4) - "ENABLE_STAT4", -#endif -#if SQLITE_ENABLE_STMTVTAB - "ENABLE_STMTVTAB", -#endif -#if SQLITE_ENABLE_STMT_SCANSTATUS - "ENABLE_STMT_SCANSTATUS", -#endif -#if SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION - "ENABLE_UNKNOWN_SQL_FUNCTION", -#endif -#if SQLITE_ENABLE_UNLOCK_NOTIFY - "ENABLE_UNLOCK_NOTIFY", -#endif -#if SQLITE_ENABLE_UPDATE_DELETE_LIMIT - "ENABLE_UPDATE_DELETE_LIMIT", -#endif -#if SQLITE_ENABLE_URI_00_ERROR - "ENABLE_URI_00_ERROR", -#endif -#if SQLITE_ENABLE_VFSTRACE - "ENABLE_VFSTRACE", -#endif -#if SQLITE_ENABLE_WHERETRACE - "ENABLE_WHERETRACE", -#endif -#if SQLITE_ENABLE_ZIPVFS - "ENABLE_ZIPVFS", -#endif -#if SQLITE_EXPLAIN_ESTIMATED_ROWS - "EXPLAIN_ESTIMATED_ROWS", -#endif -#if SQLITE_EXTRA_IFNULLROW - "EXTRA_IFNULLROW", -#endif -#ifdef SQLITE_EXTRA_INIT - "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), -#endif -#ifdef SQLITE_EXTRA_SHUTDOWN - "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), -#endif -#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH - "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), -#endif -#if SQLITE_FTS5_ENABLE_TEST_MI - "FTS5_ENABLE_TEST_MI", -#endif -#if SQLITE_FTS5_NO_WITHOUT_ROWID - "FTS5_NO_WITHOUT_ROWID", -#endif -#if HAVE_ISNAN || SQLITE_HAVE_ISNAN - "HAVE_ISNAN", -#endif -#if SQLITE_HOMEGROWN_RECURSIVE_MUTEX - "HOMEGROWN_RECURSIVE_MUTEX", -#endif -#if SQLITE_IGNORE_AFP_LOCK_ERRORS - "IGNORE_AFP_LOCK_ERRORS", -#endif -#if SQLITE_IGNORE_FLOCK_LOCK_ERRORS - "IGNORE_FLOCK_LOCK_ERRORS", -#endif -#if SQLITE_INLINE_MEMCPY - "INLINE_MEMCPY", -#endif -#if SQLITE_INT64_TYPE - "INT64_TYPE", -#endif -#ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX - "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX), -#endif -#if SQLITE_LIKE_DOESNT_MATCH_BLOBS - "LIKE_DOESNT_MATCH_BLOBS", -#endif -#if SQLITE_LOCK_TRACE - "LOCK_TRACE", -#endif -#if SQLITE_LOG_CACHE_SPILL - "LOG_CACHE_SPILL", -#endif -#ifdef SQLITE_MALLOC_SOFT_LIMIT - "MALLOC_SOFT_LIMIT=" CTIMEOPT_VAL(SQLITE_MALLOC_SOFT_LIMIT), -#endif -#ifdef SQLITE_MAX_ATTACHED - "MAX_ATTACHED=" CTIMEOPT_VAL(SQLITE_MAX_ATTACHED), -#endif -#ifdef SQLITE_MAX_COLUMN - "MAX_COLUMN=" CTIMEOPT_VAL(SQLITE_MAX_COLUMN), -#endif -#ifdef SQLITE_MAX_COMPOUND_SELECT - "MAX_COMPOUND_SELECT=" CTIMEOPT_VAL(SQLITE_MAX_COMPOUND_SELECT), -#endif -#ifdef SQLITE_MAX_DEFAULT_PAGE_SIZE - "MAX_DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_DEFAULT_PAGE_SIZE), -#endif -#ifdef SQLITE_MAX_EXPR_DEPTH - "MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_EXPR_DEPTH), -#endif -#ifdef SQLITE_MAX_FUNCTION_ARG - "MAX_FUNCTION_ARG=" CTIMEOPT_VAL(SQLITE_MAX_FUNCTION_ARG), -#endif -#ifdef SQLITE_MAX_LENGTH - "MAX_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LENGTH), -#endif -#ifdef SQLITE_MAX_LIKE_PATTERN_LENGTH - "MAX_LIKE_PATTERN_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LIKE_PATTERN_LENGTH), -#endif -#ifdef SQLITE_MAX_MEMORY - "MAX_MEMORY=" CTIMEOPT_VAL(SQLITE_MAX_MEMORY), -#endif -#ifdef SQLITE_MAX_MMAP_SIZE - "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE), -#endif -#ifdef SQLITE_MAX_MMAP_SIZE_ - "MAX_MMAP_SIZE_=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE_), -#endif -#ifdef SQLITE_MAX_PAGE_COUNT - "MAX_PAGE_COUNT=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_COUNT), -#endif -#ifdef SQLITE_MAX_PAGE_SIZE - "MAX_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_SIZE), -#endif -#ifdef SQLITE_MAX_SCHEMA_RETRY - "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), -#endif -#ifdef SQLITE_MAX_SQL_LENGTH - "MAX_SQL_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_SQL_LENGTH), -#endif -#ifdef SQLITE_MAX_TRIGGER_DEPTH - "MAX_TRIGGER_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_TRIGGER_DEPTH), -#endif -#ifdef SQLITE_MAX_VARIABLE_NUMBER - "MAX_VARIABLE_NUMBER=" CTIMEOPT_VAL(SQLITE_MAX_VARIABLE_NUMBER), -#endif -#ifdef SQLITE_MAX_VDBE_OP - "MAX_VDBE_OP=" CTIMEOPT_VAL(SQLITE_MAX_VDBE_OP), -#endif -#ifdef SQLITE_MAX_WORKER_THREADS - "MAX_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_MAX_WORKER_THREADS), -#endif -#if SQLITE_MEMDEBUG - "MEMDEBUG", -#endif -#if SQLITE_MIXED_ENDIAN_64BIT_FLOAT - "MIXED_ENDIAN_64BIT_FLOAT", -#endif -#if SQLITE_MMAP_READWRITE - "MMAP_READWRITE", -#endif -#if SQLITE_MUTEX_NOOP - "MUTEX_NOOP", -#endif -#if SQLITE_MUTEX_NREF - "MUTEX_NREF", -#endif -#if SQLITE_MUTEX_OMIT - "MUTEX_OMIT", -#endif -#if SQLITE_MUTEX_PTHREADS - "MUTEX_PTHREADS", -#endif -#if SQLITE_MUTEX_W32 - "MUTEX_W32", -#endif -#if SQLITE_NEED_ERR_NAME - "NEED_ERR_NAME", -#endif -#if SQLITE_NOINLINE - "NOINLINE", -#endif -#if SQLITE_NO_SYNC - "NO_SYNC", -#endif -#if SQLITE_OMIT_ALTERTABLE - "OMIT_ALTERTABLE", -#endif -#if SQLITE_OMIT_ANALYZE - "OMIT_ANALYZE", -#endif -#if SQLITE_OMIT_ATTACH - "OMIT_ATTACH", -#endif -#if SQLITE_OMIT_AUTHORIZATION - "OMIT_AUTHORIZATION", -#endif -#if SQLITE_OMIT_AUTOINCREMENT - "OMIT_AUTOINCREMENT", -#endif -#if SQLITE_OMIT_AUTOINIT - "OMIT_AUTOINIT", -#endif -#if SQLITE_OMIT_AUTOMATIC_INDEX - "OMIT_AUTOMATIC_INDEX", -#endif -#if SQLITE_OMIT_AUTORESET - "OMIT_AUTORESET", -#endif -#if SQLITE_OMIT_AUTOVACUUM - "OMIT_AUTOVACUUM", -#endif -#if SQLITE_OMIT_BETWEEN_OPTIMIZATION - "OMIT_BETWEEN_OPTIMIZATION", -#endif -#if SQLITE_OMIT_BLOB_LITERAL - "OMIT_BLOB_LITERAL", -#endif -#if SQLITE_OMIT_CAST - "OMIT_CAST", -#endif -#if SQLITE_OMIT_CHECK - "OMIT_CHECK", -#endif -#if SQLITE_OMIT_COMPLETE - "OMIT_COMPLETE", -#endif -#if SQLITE_OMIT_COMPOUND_SELECT - "OMIT_COMPOUND_SELECT", -#endif -#if SQLITE_OMIT_CONFLICT_CLAUSE - "OMIT_CONFLICT_CLAUSE", -#endif -#if SQLITE_OMIT_CTE - "OMIT_CTE", -#endif -#if SQLITE_OMIT_DATETIME_FUNCS - "OMIT_DATETIME_FUNCS", -#endif -#if SQLITE_OMIT_DECLTYPE - "OMIT_DECLTYPE", -#endif -#if SQLITE_OMIT_DEPRECATED - "OMIT_DEPRECATED", -#endif -#if SQLITE_OMIT_DISKIO - "OMIT_DISKIO", -#endif -#if SQLITE_OMIT_EXPLAIN - "OMIT_EXPLAIN", -#endif -#if SQLITE_OMIT_FLAG_PRAGMAS - "OMIT_FLAG_PRAGMAS", -#endif -#if SQLITE_OMIT_FLOATING_POINT - "OMIT_FLOATING_POINT", -#endif -#if SQLITE_OMIT_FOREIGN_KEY - "OMIT_FOREIGN_KEY", -#endif -#if SQLITE_OMIT_GET_TABLE - "OMIT_GET_TABLE", -#endif -#if SQLITE_OMIT_HEX_INTEGER - "OMIT_HEX_INTEGER", -#endif -#if SQLITE_OMIT_INCRBLOB - "OMIT_INCRBLOB", -#endif -#if SQLITE_OMIT_INTEGRITY_CHECK - "OMIT_INTEGRITY_CHECK", -#endif -#if SQLITE_OMIT_LIKE_OPTIMIZATION - "OMIT_LIKE_OPTIMIZATION", -#endif -#if SQLITE_OMIT_LOAD_EXTENSION - "OMIT_LOAD_EXTENSION", -#endif -#if SQLITE_OMIT_LOCALTIME - "OMIT_LOCALTIME", -#endif -#if SQLITE_OMIT_LOOKASIDE - "OMIT_LOOKASIDE", -#endif -#if SQLITE_OMIT_MEMORYDB - "OMIT_MEMORYDB", -#endif -#if SQLITE_OMIT_OR_OPTIMIZATION - "OMIT_OR_OPTIMIZATION", -#endif -#if SQLITE_OMIT_PAGER_PRAGMAS - "OMIT_PAGER_PRAGMAS", -#endif -#if SQLITE_OMIT_PARSER_TRACE - "OMIT_PARSER_TRACE", -#endif -#if SQLITE_OMIT_POPEN - "OMIT_POPEN", -#endif -#if SQLITE_OMIT_PRAGMA - "OMIT_PRAGMA", -#endif -#if SQLITE_OMIT_PROGRESS_CALLBACK - "OMIT_PROGRESS_CALLBACK", -#endif -#if SQLITE_OMIT_QUICKBALANCE - "OMIT_QUICKBALANCE", -#endif -#if SQLITE_OMIT_REINDEX - "OMIT_REINDEX", -#endif -#if SQLITE_OMIT_SCHEMA_PRAGMAS - "OMIT_SCHEMA_PRAGMAS", -#endif -#if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS - "OMIT_SCHEMA_VERSION_PRAGMAS", -#endif -#if SQLITE_OMIT_SHARED_CACHE - "OMIT_SHARED_CACHE", -#endif -#if SQLITE_OMIT_SHUTDOWN_DIRECTORIES - "OMIT_SHUTDOWN_DIRECTORIES", -#endif -#if SQLITE_OMIT_SUBQUERY - "OMIT_SUBQUERY", -#endif -#if SQLITE_OMIT_TCL_VARIABLE - "OMIT_TCL_VARIABLE", -#endif -#if SQLITE_OMIT_TEMPDB - "OMIT_TEMPDB", -#endif -#if SQLITE_OMIT_TEST_CONTROL - "OMIT_TEST_CONTROL", -#endif -#if SQLITE_OMIT_TRACE - "OMIT_TRACE", -#endif -#if SQLITE_OMIT_TRIGGER - "OMIT_TRIGGER", -#endif -#if SQLITE_OMIT_TRUNCATE_OPTIMIZATION - "OMIT_TRUNCATE_OPTIMIZATION", -#endif -#if SQLITE_OMIT_UTF16 - "OMIT_UTF16", -#endif -#if SQLITE_OMIT_VACUUM - "OMIT_VACUUM", -#endif -#if SQLITE_OMIT_VIEW - "OMIT_VIEW", -#endif -#if SQLITE_OMIT_VIRTUALTABLE - "OMIT_VIRTUALTABLE", -#endif -#if SQLITE_OMIT_WAL - "OMIT_WAL", -#endif -#if SQLITE_OMIT_WSD - "OMIT_WSD", -#endif -#if SQLITE_OMIT_XFER_OPT - "OMIT_XFER_OPT", -#endif -#if SQLITE_PCACHE_SEPARATE_HEADER - "PCACHE_SEPARATE_HEADER", -#endif -#if SQLITE_PERFORMANCE_TRACE - "PERFORMANCE_TRACE", -#endif -#if SQLITE_POWERSAFE_OVERWRITE - "POWERSAFE_OVERWRITE", -#endif -#if SQLITE_PREFER_PROXY_LOCKING - "PREFER_PROXY_LOCKING", -#endif -#if SQLITE_PROXY_DEBUG - "PROXY_DEBUG", -#endif -#if SQLITE_REVERSE_UNORDERED_SELECTS - "REVERSE_UNORDERED_SELECTS", -#endif -#if SQLITE_RTREE_INT_ONLY - "RTREE_INT_ONLY", -#endif -#if SQLITE_SECURE_DELETE - "SECURE_DELETE", -#endif -#if SQLITE_SMALL_STACK - "SMALL_STACK", -#endif -#ifdef SQLITE_SORTER_PMASZ - "SORTER_PMASZ=" CTIMEOPT_VAL(SQLITE_SORTER_PMASZ), -#endif -#if SQLITE_SOUNDEX - "SOUNDEX", -#endif -#ifdef SQLITE_STAT4_SAMPLES - "STAT4_SAMPLES=" CTIMEOPT_VAL(SQLITE_STAT4_SAMPLES), -#endif -#ifdef SQLITE_STMTJRNL_SPILL - "STMTJRNL_SPILL=" CTIMEOPT_VAL(SQLITE_STMTJRNL_SPILL), -#endif -#if SQLITE_SUBSTR_COMPATIBILITY - "SUBSTR_COMPATIBILITY", -#endif -#if SQLITE_SYSTEM_MALLOC - "SYSTEM_MALLOC", -#endif -#if SQLITE_TCL - "TCL", -#endif -#ifdef SQLITE_TEMP_STORE - "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), -#endif -#if SQLITE_TEST - "TEST", -#endif -#if defined(SQLITE_THREADSAFE) - "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), -#elif defined(THREADSAFE) - "THREADSAFE=" CTIMEOPT_VAL(THREADSAFE), -#else - "THREADSAFE=1", -#endif -#if SQLITE_UNLINK_AFTER_CLOSE - "UNLINK_AFTER_CLOSE", -#endif -#if SQLITE_UNTESTABLE - "UNTESTABLE", -#endif -#if SQLITE_USER_AUTHENTICATION - "USER_AUTHENTICATION", -#endif -#if SQLITE_USE_ALLOCA - "USE_ALLOCA", -#endif -#if SQLITE_USE_FCNTL_TRACE - "USE_FCNTL_TRACE", -#endif -#if SQLITE_USE_URI - "USE_URI", -#endif -#if SQLITE_VDBE_COVERAGE - "VDBE_COVERAGE", -#endif -#if SQLITE_WIN32_MALLOC - "WIN32_MALLOC", -#endif -#if SQLITE_ZERO_MALLOC - "ZERO_MALLOC", -#endif -/* -** END CODE GENERATED BY tool/mkctime.tcl -*/ -}; - -SQLITE_PRIVATE const char **wx_sqlite3CompileOptions(int *pnOpt){ - *pnOpt = sizeof(wx_sqlite3azCompileOpt) / sizeof(wx_sqlite3azCompileOpt[0]); - return (const char**)wx_sqlite3azCompileOpt; -} - -#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ - -/************** End of ctime.c ***********************************************/ /************** Begin file sqliteInt.h ***************************************/ /* ** 2001 September 15 @@ -1124,6 +379,17 @@ SQLITE_PRIVATE const char **wx_sqlite3CompileOptions(int *pnOpt){ # define _USE_32BIT_TIME_T #endif +/* Optionally #include a user-defined header, whereby compilation options +** may be set prior to where they take effect, but after platform setup. +** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include +** file. +*/ +#ifdef SQLITE_CUSTOM_INCLUDE +# define INC_STRINGIFY_(f) #f +# define INC_STRINGIFY(f) INC_STRINGIFY_(f) +# include INC_STRINGIFY(SQLITE_CUSTOM_INCLUDE) +#endif + /* The public SQLite interface. The _FILE_OFFSET_BITS macro must appear ** first in QNX. Also, the _USE_32BIT_TIME_T macro must appear first for ** MinGW. @@ -1175,7 +441,30 @@ extern "C" { /* -** Provide the ability to override linkage features of the interface. +** Facilitate override of interface linkage and calling conventions. +** Be aware that these macros may not be used within this particular +** translation of the amalgamation and its associated header file. +** +** The SQLITE_EXTERN and SQLITE_API macros are used to instruct the +** compiler that the target identifier should have external linkage. +** +** The SQLITE_CDECL macro is used to set the calling convention for +** public functions that accept a variable number of arguments. +** +** The SQLITE_APICALL macro is used to set the calling convention for +** public functions that accept a fixed number of arguments. +** +** The SQLITE_STDCALL macro is no longer used and is now deprecated. +** +** The SQLITE_CALLBACK macro is used to set the calling convention for +** function pointers. +** +** The SQLITE_SYSAPI macro is used to set the calling convention for +** functions provided by the operating system. +** +** Currently, the SQLITE_CDECL, SQLITE_APICALL, SQLITE_CALLBACK, and +** SQLITE_SYSAPI macros are used only when building for environments +** that require non-default calling conventions. */ #ifndef SQLITE_EXTERN # define SQLITE_EXTERN extern @@ -1255,9 +544,9 @@ extern "C" { ** [wx_sqlite3_libversion_number()], [wx_sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.35.4" -#define SQLITE_VERSION_NUMBER 3035004 -#define SQLITE_SOURCE_ID "2021-04-02 15:20:15 5d4c65779dab868b285519b19e4cf9d451d50c6048f06f653aa701ec212df45e" +#define SQLITE_VERSION "3.41.2" +#define SQLITE_VERSION_NUMBER 3041002 +#define SQLITE_SOURCE_ID "2023-03-22 11:56:21 0d1fc92f94cb6b76bffe3ec34d69cffde2924203304e8ffc4155597af0c191da" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1669,12 +958,14 @@ SQLITE_API int wx_sqlite3_exec( #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) #define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8)) +#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) +#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) -#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) +#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ /* ** CAPI3REF: Flags For File Open Operations @@ -1682,6 +973,19 @@ SQLITE_API int wx_sqlite3_exec( ** These bit values are intended for use in the ** 3rd parameter to the [wx_sqlite3_open_v2()] interface and ** in the 4th parameter to the [wx_sqlite3_vfs.xOpen] method. +** +** Only those flags marked as "Ok for wx_sqlite3_open_v2()" may be +** used as the third argument to the [wx_sqlite3_open_v2()] interface. +** The other flags have historically been ignored by wx_sqlite3_open_v2(), +** though future versions of SQLite might change so that an error is +** raised if any of the disallowed bits are passed into wx_sqlite3_open_v2(). +** Applications should not depend on the historical behavior. +** +** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into +** [wx_sqlite3_open_v2()] does *not* cause the underlying database file +** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into +** [wx_sqlite3_open_v2()] has historically be a no-op and might become an +** error in future versions of SQLite. */ #define SQLITE_OPEN_READONLY 0x00000001 /* Ok for wx_sqlite3_open_v2() */ #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for wx_sqlite3_open_v2() */ @@ -1704,6 +1008,7 @@ SQLITE_API int wx_sqlite3_exec( #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for wx_sqlite3_open_v2() */ #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ #define SQLITE_OPEN_NOFOLLOW 0x01000000 /* Ok for wx_sqlite3_open_v2() */ +#define SQLITE_OPEN_EXRESCODE 0x02000000 /* Extended result codes */ /* Reserved: 0x00F00000 */ /* Legacy compatibility: */ @@ -1764,13 +1069,17 @@ SQLITE_API int wx_sqlite3_exec( ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods -** of an [wx_sqlite3_io_methods] object. +** of an [wx_sqlite3_io_methods] object. These values are ordered from +** lest restrictive to most restrictive. +** +** The argument to xLock() is always SHARED or higher. The argument to +** xUnlock is either SHARED or NONE. */ -#define SQLITE_LOCK_NONE 0 -#define SQLITE_LOCK_SHARED 1 -#define SQLITE_LOCK_RESERVED 2 -#define SQLITE_LOCK_PENDING 3 -#define SQLITE_LOCK_EXCLUSIVE 4 +#define SQLITE_LOCK_NONE 0 /* xUnlock() only */ +#define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */ +#define SQLITE_LOCK_RESERVED 2 /* xLock() only */ +#define SQLITE_LOCK_PENDING 3 /* xLock() only */ +#define SQLITE_LOCK_EXCLUSIVE 4 /* xLock() only */ /* ** CAPI3REF: Synchronization Type Flags @@ -1848,7 +1157,14 @@ struct wx_sqlite3_file { **
  • [SQLITE_LOCK_PENDING], or **
  • [SQLITE_LOCK_EXCLUSIVE]. ** -** xLock() increases the lock. xUnlock() decreases the lock. +** xLock() upgrades the database file lock. In other words, xLock() moves the +** database file lock in the direction NONE toward EXCLUSIVE. The argument to +** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never +** SQLITE_LOCK_NONE. If the database file lock is already at or above the +** requested lock, then the call to xLock() is a no-op. +** xUnlock() downgrades the database file lock to either SHARED or NONE. +* If the lock is already at or below the requested lock state, then the call +** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, ** PENDING, or EXCLUSIVE lock on the file. It returns true @@ -1953,9 +1269,8 @@ struct wx_sqlite3_io_methods { ** opcode causes the xFileControl method to write the current state of ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) -** into an integer that the pArg argument points to. This capability -** is used during testing and is only available when the SQLITE_TEST -** compile-time option is used. +** into an integer that the pArg argument points to. +** This capability is only available if SQLite is compiled with [SQLITE_DEBUG]. ** **
  • [[SQLITE_FCNTL_SIZE_HINT]] ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS @@ -2259,6 +1574,28 @@ struct wx_sqlite3_io_methods { ** in wal mode after the client has finished copying pages from the wal ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. +** +**
  • [[SQLITE_FCNTL_EXTERNAL_READER]] +** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect +** whether or not there is a database client in another process with a wal-mode +** transaction open on the database or not. It is only available on unix.The +** (void*) argument passed with this file-control should be a pointer to a +** value of type (int). The integer value is set to 1 if the database is a wal +** mode database and there exists at least one client in another process that +** currently has an SQL transaction open on the database. It is set to 0 if +** the database is not a wal-mode db, or if there is no such connection in any +** other process. This opcode cannot be used to detect transactions opened +** by clients within the current process, only within other processes. +** +**
  • [[SQLITE_FCNTL_CKSM_FILE]] +** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use interally by the +** [checksum VFS shim] only. +** +**
  • [[SQLITE_FCNTL_RESET_CACHE]] +** If there is currently no transaction open on the database, and the +** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control +** purges the contents of the in-memory page cache. If there is an open +** transaction, or if the db is a temp-db, this opcode is a no-op, not an error. ** */ #define SQLITE_FCNTL_LOCKSTATE 1 @@ -2299,6 +1636,9 @@ struct wx_sqlite3_io_methods { #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 +#define SQLITE_FCNTL_EXTERNAL_READER 40 +#define SQLITE_FCNTL_CKSM_FILE 41 +#define SQLITE_FCNTL_RESET_CACHE 42 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -2328,6 +1668,26 @@ typedef struct wx_sqlite3_mutex wx_sqlite3_mutex; */ typedef struct wx_sqlite3_api_routines wx_sqlite3_api_routines; +/* +** CAPI3REF: File Name +** +** Type [wx_sqlite3_filename] is used by SQLite to pass filenames to the +** xOpen method of a [VFS]. It may be cast to (const char*) and treated +** as a normal, nul-terminated, UTF-8 buffer containing the filename, but +** may also be passed to special APIs such as: +** +**
      +**
    • wx_sqlite3_filename_database() +**
    • wx_sqlite3_filename_journal() +**
    • wx_sqlite3_filename_wal() +**
    • wx_sqlite3_uri_parameter() +**
    • wx_sqlite3_uri_boolean() +**
    • wx_sqlite3_uri_int64() +**
    • wx_sqlite3_uri_key() +**
    +*/ +typedef const char *wx_sqlite3_filename; + /* ** CAPI3REF: OS Interface Object ** @@ -2506,7 +1866,7 @@ struct wx_sqlite3_vfs { wx_sqlite3_vfs *pNext; /* Next registered VFS */ const char *zName; /* Name of this virtual file system */ void *pAppData; /* Pointer to application-specific data */ - int (*xOpen)(wx_sqlite3_vfs*, const char *zName, wx_sqlite3_file*, + int (*xOpen)(wx_sqlite3_vfs*, wx_sqlite3_filename zName, wx_sqlite3_file*, int flags, int *pOutFlags); int (*xDelete)(wx_sqlite3_vfs*, const char *zName, int syncDir); int (*xAccess)(wx_sqlite3_vfs*, const char *zName, int flags, int *pResOut); @@ -3222,7 +2582,7 @@ struct wx_sqlite3_mem_methods { ** configuration for a database connection can only be changed when that ** connection is not currently using lookaside memory, or in other words ** when the "current value" returned by -** [wx_sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. +** [wx_sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero. ** Any attempt to change the lookaside memory configuration when lookaside ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^
  • @@ -3372,8 +2732,12 @@ struct wx_sqlite3_mem_methods { **
  • wx_sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); ** ** Because resetting a database is destructive and irreversible, the -** process requires the use of this obscure API and multiple steps to help -** ensure that it does not happen by accident. +** process requires the use of this obscure API and multiple steps to +** help ensure that it does not happen by accident. Because this +** feature must be capable of resetting corrupt databases, and +** shutting down virtual tables may require access to that corrupt +** storage, the library must abandon any installed virtual tables +** without calling their xDestroy() methods. ** ** [[SQLITE_DBCONFIG_DEFENSIVE]]
    SQLITE_DBCONFIG_DEFENSIVE
    **
    The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the @@ -3384,6 +2748,7 @@ struct wx_sqlite3_mem_methods { **
      **
    • The [PRAGMA writable_schema=ON] statement. **
    • The [PRAGMA journal_mode=OFF] statement. +**
    • The [PRAGMA schema_version=N] statement. **
    • Writes to the [sqlite_dbpage] virtual table. **
    • Direct writes to [shadow tables]. **
    @@ -3577,11 +2942,14 @@ SQLITE_API void wx_sqlite3_set_last_insert_rowid(wx_sqlite3*,wx_sqlite3_int64); ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: wx_sqlite3 ** -** ^This function returns the number of rows modified, inserted or +** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. -** ^Executing any other type of SQL statement does not modify the value -** returned by this function. +** The two functions are identical except for the type of the return value +** and that if the number of rows modified by the most recent INSERT, UPDATE +** or DELETE is greater than the maximum value supported by type "int", then +** the return value of wx_sqlite3_changes() is undefined. ^Executing any other +** type of SQL statement does not modify the value returned by these functions. ** ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], @@ -3630,16 +2998,21 @@ SQLITE_API void wx_sqlite3_set_last_insert_rowid(wx_sqlite3*,wx_sqlite3_int64); ** */ SQLITE_API int wx_sqlite3_changes(wx_sqlite3*); +SQLITE_API wx_sqlite3_int64 wx_sqlite3_changes64(wx_sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified ** METHOD: wx_sqlite3 ** -** ^This function returns the total number of rows inserted, modified or +** ^These functions return the total number of rows inserted, modified or ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed ** since the database connection was opened, including those executed as -** part of trigger programs. ^Executing any other type of SQL statement -** does not affect the value returned by wx_sqlite3_total_changes(). +** part of trigger programs. The two functions are identical except for the +** type of the return value and that if the number of rows modified by the +** connection exceeds the maximum value supported by type "int", then +** the return value of wx_sqlite3_total_changes() is undefined. ^Executing +** any other type of SQL statement does not affect the value returned by +** wx_sqlite3_total_changes(). ** ** ^Changes made as part of [foreign key actions] are included in the ** count, but those made as part of REPLACE constraint resolution are @@ -3667,6 +3040,7 @@ SQLITE_API int wx_sqlite3_changes(wx_sqlite3*); ** */ SQLITE_API int wx_sqlite3_total_changes(wx_sqlite3*); +SQLITE_API wx_sqlite3_int64 wx_sqlite3_total_changes64(wx_sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query @@ -3702,8 +3076,12 @@ SQLITE_API int wx_sqlite3_total_changes(wx_sqlite3*); ** ^A call to wx_sqlite3_interrupt(D) that occurs when there are no running ** SQL statements is a no-op and has no effect on SQL statements ** that are started after the wx_sqlite3_interrupt() call returns. +** +** ^The [wx_sqlite3_is_interrupted(D)] interface can be used to determine whether +** or not an interrupt is currently in effect for [database connection] D. */ SQLITE_API void wx_sqlite3_interrupt(wx_sqlite3*); +SQLITE_API int wx_sqlite3_is_interrupted(wx_sqlite3*); /* ** CAPI3REF: Determine If An SQL Statement Is Complete @@ -4321,8 +3699,8 @@ SQLITE_API SQLITE_DEPRECATED void *wx_sqlite3_profile(wx_sqlite3*, **
    ^An SQLITE_TRACE_PROFILE callback provides approximately the same ** information as is provided by the [wx_sqlite3_profile()] callback. ** ^The P argument is a pointer to the [prepared statement] and the -** X argument points to a 64-bit integer which is the estimated of -** the number of nanosecond that the prepared statement took to run. +** X argument points to a 64-bit integer which is approximately +** the number of nanoseconds that the prepared statement took to run. ** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes. ** ** [[SQLITE_TRACE_ROW]]
    SQLITE_TRACE_ROW
    @@ -4385,7 +3763,7 @@ SQLITE_API int wx_sqlite3_trace_v2( ** ** ^The wx_sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to -** [wx_sqlite3_exec()], [wx_sqlite3_step()] and [wx_sqlite3_get_table()] for +** [wx_sqlite3_step()] and [wx_sqlite3_prepare()] and similar for ** database connection D. An example use for this ** interface is to keep a GUI updated during a large query. ** @@ -4410,6 +3788,13 @@ SQLITE_API int wx_sqlite3_trace_v2( ** Note that [wx_sqlite3_prepare_v2()] and [wx_sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** +** The progress handler callback would originally only be invoked from the +** bytecode engine. It still might be invoked during [wx_sqlite3_prepare()] +** and similar because those routines might force a reparse of the schema +** which involves running the bytecode engine. However, beginning with +** SQLite version 3.41.0, the progress handler callback might also be +** invoked directly from [wx_sqlite3_prepare()] while analyzing and generating +** code for complex queries. */ SQLITE_API void wx_sqlite3_progress_handler(wx_sqlite3*, int, int(*)(void*), void*); @@ -4446,13 +3831,18 @@ SQLITE_API void wx_sqlite3_progress_handler(wx_sqlite3*, int, int(*)(void*), voi ** **
    ** ^(
    [SQLITE_OPEN_READONLY]
    -**
    The database is opened in read-only mode. If the database does not -** already exist, an error is returned.
    )^ +**
    The database is opened in read-only mode. If the database does +** not already exist, an error is returned.
    )^ ** ** ^(
    [SQLITE_OPEN_READWRITE]
    -**
    The database is opened for reading and writing if possible, or reading -** only if the file is write protected by the operating system. In either -** case the database must already exist, otherwise an error is returned.
    )^ +**
    The database is opened for reading and writing if possible, or +** reading only if the file is write protected by the operating +** system. In either case the database must already exist, otherwise +** an error is returned. For historical reasons, if opening in +** read-write mode fails due to OS-level permissions, an attempt is +** made to open it in read-only mode. [wx_sqlite3_db_readonly()] can be +** used to determine whether the database is actually +** read-write.
    )^ ** ** ^(
    [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
    **
    The database is opened for reading and writing, and is created if @@ -4490,20 +3880,39 @@ SQLITE_API void wx_sqlite3_progress_handler(wx_sqlite3*, int, int(*)(void*), voi **
    The database is opened [shared cache] enabled, overriding ** the default shared cache setting provided by ** [wx_sqlite3_enable_shared_cache()].)^ +** The [use of shared cache mode is discouraged] and hence shared cache +** capabilities may be omitted from many builds of SQLite. In such cases, +** this option is a no-op. ** ** ^(
    [SQLITE_OPEN_PRIVATECACHE]
    **
    The database is opened [shared cache] disabled, overriding ** the default shared cache setting provided by ** [wx_sqlite3_enable_shared_cache()].)^ ** +** [[OPEN_EXRESCODE]] ^(
    [SQLITE_OPEN_EXRESCODE]
    +**
    The database connection comes up in "extended result code mode". +** In other words, the database behaves has if +** [wx_sqlite3_extended_result_codes(db,1)] where called on the database +** connection as soon as the connection is created. In addition to setting +** the extended result code mode, this flag also causes [wx_sqlite3_open_v2()] +** to return an extended result code.
    +** ** [[OPEN_NOFOLLOW]] ^(
    [SQLITE_OPEN_NOFOLLOW]
    -**
    The database filename is not allowed to be a symbolic link
    +**
    The database filename is not allowed to contain a symbolic link
    **
    )^ ** ** If the 3rd parameter to wx_sqlite3_open_v2() is not one of the ** required combinations shown above optionally combined with other ** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] -** then the behavior is undefined. +** then the behavior is undefined. Historic versions of SQLite +** have silently ignored surplus bits in the flags parameter to +** wx_sqlite3_open_v2(), however that behavior might not be carried through +** into future versions of SQLite and so applications should not rely +** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op +** for wx_sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause +** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE +** flag is intended for use by the [wx_sqlite3_vfs|VFS interface] only, and not +** by wx_sqlite3_open_v2(). ** ** ^The fourth parameter to wx_sqlite3_open_v2() is the name of the ** [wx_sqlite3_vfs] object that defines the operating system interface that @@ -4748,10 +4157,10 @@ SQLITE_API int wx_sqlite3_open_v2( ** ** See the [URI filename] documentation for additional information. */ -SQLITE_API const char *wx_sqlite3_uri_parameter(const char *zFilename, const char *zParam); -SQLITE_API int wx_sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); -SQLITE_API wx_sqlite3_int64 wx_sqlite3_uri_int64(const char*, const char*, wx_sqlite3_int64); -SQLITE_API const char *wx_sqlite3_uri_key(const char *zFilename, int N); +SQLITE_API const char *wx_sqlite3_uri_parameter(wx_sqlite3_filename z, const char *zParam); +SQLITE_API int wx_sqlite3_uri_boolean(wx_sqlite3_filename z, const char *zParam, int bDefault); +SQLITE_API wx_sqlite3_int64 wx_sqlite3_uri_int64(wx_sqlite3_filename, const char*, wx_sqlite3_int64); +SQLITE_API const char *wx_sqlite3_uri_key(wx_sqlite3_filename z, int N); /* ** CAPI3REF: Translate filenames @@ -4780,9 +4189,9 @@ SQLITE_API const char *wx_sqlite3_uri_key(const char *zFilename, int N); ** return value from [wx_sqlite3_db_filename()], then the result is ** undefined and is likely a memory access violation. */ -SQLITE_API const char *wx_sqlite3_filename_database(const char*); -SQLITE_API const char *wx_sqlite3_filename_journal(const char*); -SQLITE_API const char *wx_sqlite3_filename_wal(const char*); +SQLITE_API const char *wx_sqlite3_filename_database(wx_sqlite3_filename); +SQLITE_API const char *wx_sqlite3_filename_journal(wx_sqlite3_filename); +SQLITE_API const char *wx_sqlite3_filename_wal(wx_sqlite3_filename); /* ** CAPI3REF: Database File Corresponding To A Journal @@ -4848,14 +4257,14 @@ SQLITE_API wx_sqlite3_file *wx_sqlite3_database_file_object(const char*); ** then the corresponding [wx_sqlite3_module.xClose() method should also be ** invoked prior to calling wx_sqlite3_free_filename(Y). */ -SQLITE_API char *wx_sqlite3_create_filename( +SQLITE_API wx_sqlite3_filename wx_sqlite3_create_filename( const char *zDatabase, const char *zJournal, const char *zWal, int nParam, const char **azParam ); -SQLITE_API void wx_sqlite3_free_filename(char*); +SQLITE_API void wx_sqlite3_free_filename(wx_sqlite3_filename); /* ** CAPI3REF: Error Codes And Messages @@ -4874,13 +4283,14 @@ SQLITE_API void wx_sqlite3_free_filename(char*); ** wx_sqlite3_extended_errcode() might change with each API call. ** Except, there are some interfaces that are guaranteed to never ** change the value of the error code. The error-code preserving -** interfaces are: +** interfaces include the following: ** **
      **
    • wx_sqlite3_errcode() **
    • wx_sqlite3_extended_errcode() **
    • wx_sqlite3_errmsg() **
    • wx_sqlite3_errmsg16() +**
    • wx_sqlite3_error_offset() **
    ** ** ^The wx_sqlite3_errmsg() and wx_sqlite3_errmsg16() return English-language @@ -4895,6 +4305,13 @@ SQLITE_API void wx_sqlite3_free_filename(char*); ** ^(Memory to hold the error message string is managed internally ** and must not be freed by the application)^. ** +** ^If the most recent error references a specific token in the input +** SQL, the wx_sqlite3_error_offset() interface returns the byte offset +** of the start of that token. ^The byte offset returned by +** wx_sqlite3_error_offset() assumes that the input SQL is UTF8. +** ^If the most recent error does not reference a specific token in the input +** SQL, then the wx_sqlite3_error_offset() function returns -1. +** ** When the serialized [threading mode] is in use, it might be the ** case that a second error occurs on a separate thread in between ** the time of the first error and the call to these interfaces. @@ -4914,6 +4331,7 @@ SQLITE_API int wx_sqlite3_extended_errcode(wx_sqlite3 *db); SQLITE_API const char *wx_sqlite3_errmsg(wx_sqlite3*); SQLITE_API const void *wx_sqlite3_errmsg16(wx_sqlite3*); SQLITE_API const char *wx_sqlite3_errstr(int); +SQLITE_API int wx_sqlite3_error_offset(wx_sqlite3 *db); /* ** CAPI3REF: Prepared Statement Object @@ -5271,12 +4689,17 @@ SQLITE_API int wx_sqlite3_prepare16_v3( ** are managed by SQLite and are automatically freed when the prepared ** statement is finalized. ** ^The string returned by wx_sqlite3_expanded_sql(P), on the other hand, -** is obtained from [wx_sqlite3_malloc()] and must be free by the application +** is obtained from [wx_sqlite3_malloc()] and must be freed by the application ** by passing it to [wx_sqlite3_free()]. +** +** ^The wx_sqlite3_normalized_sql() interface is only available if +** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined. */ SQLITE_API const char *wx_sqlite3_sql(wx_sqlite3_stmt *pStmt); SQLITE_API char *wx_sqlite3_expanded_sql(wx_sqlite3_stmt *pStmt); +#ifdef SQLITE_ENABLE_NORMALIZE SQLITE_API const char *wx_sqlite3_normalized_sql(wx_sqlite3_stmt *pStmt); +#endif /* ** CAPI3REF: Determine If An SQL Statement Writes The Database @@ -5311,6 +4734,19 @@ SQLITE_API const char *wx_sqlite3_normalized_sql(wx_sqlite3_stmt *pStmt); ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so ** wx_sqlite3_stmt_readonly() returns false for those commands. +** +** ^This routine returns false if there is any possibility that the +** statement might change the database file. ^A false return does +** not guarantee that the statement will change the database file. +** ^For example, an UPDATE statement might have a WHERE clause that +** makes it a no-op, but the wx_sqlite3_stmt_readonly() result would still +** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a +** read-only no-op if the table already exists, but +** wx_sqlite3_stmt_readonly() still returns false for such a statement. +** +** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN] +** statement, then wx_sqlite3_stmt_readonly(X) returns the same value as +** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted. */ SQLITE_API int wx_sqlite3_stmt_readonly(wx_sqlite3_stmt *pStmt); @@ -5379,6 +4815,8 @@ SQLITE_API int wx_sqlite3_stmt_busy(wx_sqlite3_stmt*); ** ** ^The wx_sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. +** ^The wx_sqlite3_value objects returned by [wx_sqlite3_vtab_rhs_value()] +** are protected. ** ^The wx_sqlite3_value object returned by ** [wx_sqlite3_column_value()] is unprotected. ** Unprotected wx_sqlite3_value objects may only be used as arguments @@ -5480,18 +4918,22 @@ typedef struct wx_sqlite3_context wx_sqlite3_context; ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. ** -** ^The fifth argument to the BLOB and string binding interfaces -** is a destructor used to dispose of the BLOB or -** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to the bind API fails, -** except the destructor is not called if the third parameter is a NULL -** pointer or the fourth parameter is negative. -** ^If the fifth argument is -** the special value [SQLITE_STATIC], then SQLite assumes that the -** information is in static, unmanaged space and does not need to be freed. -** ^If the fifth argument has the value [SQLITE_TRANSIENT], then -** SQLite makes its own private copy of the data immediately, before -** the wx_sqlite3_bind_*() routine returns. +** ^The fifth argument to the BLOB and string binding interfaces controls +** or indicates the lifetime of the object referenced by the third parameter. +** These three options exist: +** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished +** with it may be passed. ^It is called to dispose of the BLOB or string even +** if the call to the bind API fails, except the destructor is not called if +** the third parameter is a NULL pointer or the fourth parameter is negative. +** ^ (2) The special constant, [SQLITE_STATIC], may be passsed to indicate that +** the application remains responsible for disposing of the object. ^In this +** case, the object and the provided pointer to it must remain valid until +** either the prepared statement is finalized or the same SQL parameter is +** bound to something else, whichever occurs sooner. +** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the +** object is to be copied prior to the return from wx_sqlite3_bind_*(). ^The +** object and pointer to it must remain valid until then. ^SQLite will then +** manage the lifetime of its private copy. ** ** ^The sixth argument to wx_sqlite3_bind_text64() must be one of ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] @@ -5996,6 +5438,10 @@ SQLITE_API int wx_sqlite3_data_count(wx_sqlite3_stmt *pStmt); ** even empty strings, are always zero-terminated. ^The return ** value from wx_sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. ** +** ^Strings returned by wx_sqlite3_column_text16() always have the endianness +** which is native to the platform, regardless of the text encoding set +** for the database. +** ** Warning: ^The object returned by [wx_sqlite3_column_value()] is an ** [unprotected wx_sqlite3_value] object. In a multithreaded environment, ** an unprotected wx_sqlite3_value object may only be used safely with @@ -6009,7 +5455,7 @@ SQLITE_API int wx_sqlite3_data_count(wx_sqlite3_stmt *pStmt); ** [application-defined SQL functions] or [virtual tables], not within ** top-level application code. ** -** The these routines may attempt to convert the datatype of the result. +** These routines may attempt to convert the datatype of the result. ** ^For example, if the internal representation is FLOAT and a text result ** is requested, [wx_sqlite3_snprintf()] is used internally to perform the ** conversion automatically. ^(The following table details the conversions @@ -6034,7 +5480,7 @@ SQLITE_API int wx_sqlite3_data_count(wx_sqlite3_stmt *pStmt); ** TEXT BLOB No change ** BLOB INTEGER [CAST] to INTEGER ** BLOB FLOAT [CAST] to REAL -** BLOB TEXT Add a zero terminator if needed +** BLOB TEXT [CAST] to TEXT, ensure zero terminator ** ** )^ ** @@ -6233,7 +5679,6 @@ SQLITE_API int wx_sqlite3_reset(wx_sqlite3_stmt *pStmt); ** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions, ** index expressions, or the WHERE clause of partial indexes. ** -** ** For best security, the [SQLITE_DIRECTONLY] flag is recommended for ** all application-defined SQL functions that do not need to be ** used inside of triggers, view, CHECK constraints, or other elements of @@ -6243,7 +5688,6 @@ SQLITE_API int wx_sqlite3_reset(wx_sqlite3_stmt *pStmt); ** a database file to include invocations of the function with parameters ** chosen by the attacker, which the application will then execute when ** the database file is opened and read. -** ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [wx_sqlite3_user_data()].)^ @@ -6379,10 +5823,21 @@ SQLITE_API int wx_sqlite3_create_window_function( ** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in ** schema structures such as [CHECK constraints], [DEFAULT clauses], ** [expression indexes], [partial indexes], or [generated columns]. -** The SQLITE_DIRECTONLY flags is a security feature which is recommended -** for all [application-defined SQL functions], and especially for functions -** that have side-effects or that could potentially leak sensitive -** information. +**

    +** The SQLITE_DIRECTONLY flag is recommended for any +** [application-defined SQL function] +** that has side-effects or that could potentially leak sensitive information. +** This will prevent attacks in which an application is tricked +** into using a database file that has had its schema surreptiously +** modified to invoke the application-defined function in ways that are +** harmful. +**

    +** Some people say it is good practice to set SQLITE_DIRECTONLY on all +** [application-defined SQL functions], regardless of whether or not they +** are security sensitive, as doing so prevents those functions from being used +** inside of the database schema, and thus ensures that the database +** can be inspected and modified using generic tools (such as the [CLI]) +** that do not have access to the application-defined functions. **

  • ** ** [[SQLITE_INNOCUOUS]]
    SQLITE_INNOCUOUS
    @@ -6588,6 +6043,28 @@ SQLITE_API int wx_sqlite3_value_numeric_type(wx_sqlite3_value*); SQLITE_API int wx_sqlite3_value_nochange(wx_sqlite3_value*); SQLITE_API int wx_sqlite3_value_frombind(wx_sqlite3_value*); +/* +** CAPI3REF: Report the internal text encoding state of an wx_sqlite3_value object +** METHOD: wx_sqlite3_value +** +** ^(The wx_sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8], +** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding +** of the value X, assuming that X has type TEXT.)^ If wx_sqlite3_value_type(X) +** returns something other than SQLITE_TEXT, then the return value from +** wx_sqlite3_value_encoding(X) is meaningless. ^Calls to +** [wx_sqlite3_value_text(X)], [wx_sqlite3_value_text16(X)], [wx_sqlite3_value_text16be(X)], +** [wx_sqlite3_value_text16le(X)], [wx_sqlite3_value_bytes(X)], or +** [wx_sqlite3_value_bytes16(X)] might change the encoding of the value X and +** thus change the return from subsequent calls to wx_sqlite3_value_encoding(X). +** +** This routine is intended for used by applications that test and validate +** the SQLite implementation. This routine is inquiring about the opaque +** internal state of an [wx_sqlite3_value] object. Ordinary applications should +** not need to know what the internal state of an wx_sqlite3_value object is and +** hence should not need to use this interface. +*/ +SQLITE_API int wx_sqlite3_value_encoding(wx_sqlite3_value*); + /* ** CAPI3REF: Finding The Subtype Of SQL Values ** METHOD: wx_sqlite3_value @@ -6608,7 +6085,8 @@ SQLITE_API unsigned int wx_sqlite3_value_subtype(wx_sqlite3_value*); ** object D and returns a pointer to that copy. ^The [wx_sqlite3_value] returned ** is a [protected wx_sqlite3_value] object even if the input is not. ** ^The wx_sqlite3_value_dup(V) interface returns NULL if V is NULL or if a -** memory allocation fails. +** memory allocation fails. ^If V is a [pointer value], then the result +** of wx_sqlite3_value_dup(V) is a NULL value. ** ** ^The wx_sqlite3_value_free(V) interface frees an [wx_sqlite3_value] object ** previously obtained from [wx_sqlite3_value_dup()]. ^If V is a NULL pointer @@ -6639,7 +6117,7 @@ SQLITE_API void wx_sqlite3_value_free(wx_sqlite3_value*); ** ** ^The wx_sqlite3_aggregate_context(C,N) routine returns a NULL pointer ** when first called if N is less than or equal to zero or if a memory -** allocate error occurs. +** allocation error occurs. ** ** ^(The amount of space allocated by wx_sqlite3_aggregate_context(C,N) is ** determined by the N parameter on first successful call. Changing the @@ -6844,9 +6322,10 @@ typedef void (*wx_sqlite3_destructor_type)(void*); ** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. ** ^SQLite takes the text result from the application from ** the 2nd parameter of the wx_sqlite3_result_text* interfaces. -** ^If the 3rd parameter to the wx_sqlite3_result_text* interfaces -** is negative, then SQLite takes result text from the 2nd parameter -** through the first zero character. +** ^If the 3rd parameter to any of the wx_sqlite3_result_text* interfaces +** other than wx_sqlite3_result_text64() is negative, then SQLite computes +** the string length itself by searching the 2nd parameter for the first +** zero character. ** ^If the 3rd parameter to the wx_sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text ** pointed to by the 2nd parameter are taken as the application-defined @@ -7290,6 +6769,28 @@ SQLITE_API int wx_sqlite3_get_autocommit(wx_sqlite3*); */ SQLITE_API wx_sqlite3 *wx_sqlite3_db_handle(wx_sqlite3_stmt*); +/* +** CAPI3REF: Return The Schema Name For A Database Connection +** METHOD: wx_sqlite3 +** +** ^The wx_sqlite3_db_name(D,N) interface returns a pointer to the schema name +** for the N-th database on database connection D, or a NULL pointer of N is +** out of range. An N value of 0 means the main database file. An N of 1 is +** the "temp" schema. Larger values of N correspond to various ATTACH-ed +** databases. +** +** Space to hold the string that is returned by wx_sqlite3_db_name() is managed +** by SQLite itself. The string might be deallocated by any operation that +** changes the schema, including [ATTACH] or [DETACH] or calls to +** [wx_sqlite3_serialize()] or [wx_sqlite3_deserialize()], even operations that +** occur on a different thread. Applications that need to +** remember the string long-term should make their own copy. Applications that +** are accessing the same database connection simultaneously on multiple +** threads should mutex-protect calls to this API and should make their own +** private copy of the result prior to releasing the mutex. +*/ +SQLITE_API const char *wx_sqlite3_db_name(wx_sqlite3 *db, int N); + /* ** CAPI3REF: Return The Filename For A Database Connection ** METHOD: wx_sqlite3 @@ -7320,7 +6821,7 @@ SQLITE_API wx_sqlite3 *wx_sqlite3_db_handle(wx_sqlite3_stmt*); **
  • [wx_sqlite3_filename_wal()] ** */ -SQLITE_API const char *wx_sqlite3_db_filename(wx_sqlite3 *db, const char *zDbName); +SQLITE_API wx_sqlite3_filename wx_sqlite3_db_filename(wx_sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Determine if a database is read-only @@ -7449,6 +6950,72 @@ SQLITE_API wx_sqlite3_stmt *wx_sqlite3_next_stmt(wx_sqlite3 *pDb, wx_sqlite3_stm SQLITE_API void *wx_sqlite3_commit_hook(wx_sqlite3*, int(*)(void*), void*); SQLITE_API void *wx_sqlite3_rollback_hook(wx_sqlite3*, void(*)(void *), void*); +/* +** CAPI3REF: Autovacuum Compaction Amount Callback +** METHOD: wx_sqlite3 +** +** ^The wx_sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback +** function C that is invoked prior to each autovacuum of the database +** file. ^The callback is passed a copy of the generic data pointer (P), +** the schema-name of the attached database that is being autovacuumed, +** the size of the database file in pages, the number of free pages, +** and the number of bytes per page, respectively. The callback should +** return the number of free pages that should be removed by the +** autovacuum. ^If the callback returns zero, then no autovacuum happens. +** ^If the value returned is greater than or equal to the number of +** free pages, then a complete autovacuum happens. +** +**

    ^If there are multiple ATTACH-ed database files that are being +** modified as part of a transaction commit, then the autovacuum pages +** callback is invoked separately for each file. +** +**

    The callback is not reentrant. The callback function should +** not attempt to invoke any other SQLite interface. If it does, bad +** things may happen, including segmentation faults and corrupt database +** files. The callback function should be a simple function that +** does some arithmetic on its input parameters and returns a result. +** +** ^The X parameter to wx_sqlite3_autovacuum_pages(D,C,P,X) is an optional +** destructor for the P parameter. ^If X is not NULL, then X(P) is +** invoked whenever the database connection closes or when the callback +** is overwritten by another invocation of wx_sqlite3_autovacuum_pages(). +** +**

    ^There is only one autovacuum pages callback per database connection. +** ^Each call to the wx_sqlite3_autovacuum_pages() interface overrides all +** previous invocations for that database connection. ^If the callback +** argument (C) to wx_sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, +** then the autovacuum steps callback is cancelled. The return value +** from wx_sqlite3_autovacuum_pages() is normally SQLITE_OK, but might +** be some other error code if something goes wrong. The current +** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other +** return codes might be added in future releases. +** +**

    If no autovacuum pages callback is specified (the usual case) or +** a NULL pointer is provided for the callback, +** then the default behavior is to vacuum all free pages. So, in other +** words, the default behavior is the same as if the callback function +** were something like this: +** +**

    +**     unsigned int demonstration_autovac_pages_callback(
    +**       void *pClientData,
    +**       const char *zSchema,
    +**       unsigned int nDbPage,
    +**       unsigned int nFreePage,
    +**       unsigned int nBytePerPage
    +**     ){
    +**       return nFreePage;
    +**     }
    +** 
    +*/ +SQLITE_API int wx_sqlite3_autovacuum_pages( + wx_sqlite3 *db, + unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), + void*, + void(*)(void*) +); + + /* ** CAPI3REF: Data Change Notification Callbacks ** METHOD: wx_sqlite3 @@ -7512,6 +7079,11 @@ SQLITE_API void *wx_sqlite3_update_hook( ** to the same database. Sharing is enabled if the argument is true ** and disabled if the argument is false.)^ ** +** This interface is omitted if SQLite is compiled with +** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE] +** compile-time option is recommended because the +** [use of shared cache mode is discouraged]. +** ** ^Cache sharing is enabled and disabled for an entire process. ** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]). ** In prior versions of SQLite, @@ -7610,7 +7182,7 @@ SQLITE_API int wx_sqlite3_db_release_memory(wx_sqlite3*); ** ^The soft heap limit may not be greater than the hard heap limit. ** ^If the hard heap limit is enabled and if wx_sqlite3_soft_heap_limit(N) ** is invoked with a value of N that is greater than the hard heap limit, -** the the soft heap limit is set to the value of the hard heap limit. +** the soft heap limit is set to the value of the hard heap limit. ** ^The soft heap limit is automatically enabled whenever the hard heap ** limit is enabled. ^When wx_sqlite3_hard_heap_limit64(N) is invoked and ** the soft heap limit is outside the range of 1..N, then the soft heap @@ -7871,15 +7443,6 @@ SQLITE_API int wx_sqlite3_cancel_auto_extension(void(*xEntryPoint)(void)); */ SQLITE_API void wx_sqlite3_reset_auto_extension(void); -/* -** The interface to the virtual-table mechanism is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - /* ** Structures used by the virtual table interface */ @@ -7998,10 +7561,10 @@ struct wx_sqlite3_module { ** when the omit flag is true there is no guarantee that the constraint will ** not be checked again using byte code.)^ ** -** ^The idxNum and idxPtr values are recorded and passed into the +** ^The idxNum and idxStr values are recorded and passed into the ** [xFilter] method. -** ^[wx_sqlite3_free()] is used to free idxPtr if and only if -** needToFreeIdxPtr is true. +** ^[wx_sqlite3_free()] is used to free idxStr if and only if +** needToFreeIdxStr is true. ** ** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in ** the correct order to satisfy the ORDER BY clause so that no separate @@ -8090,24 +7653,56 @@ struct wx_sqlite3_index_info { ** ** These macros define the allowed values for the ** [wx_sqlite3_index_info].aConstraint[].op field. Each value represents -** an operator that is part of a constraint term in the wHERE clause of +** an operator that is part of a constraint term in the WHERE clause of ** a query that uses a [virtual table]. -*/ -#define SQLITE_INDEX_CONSTRAINT_EQ 2 -#define SQLITE_INDEX_CONSTRAINT_GT 4 -#define SQLITE_INDEX_CONSTRAINT_LE 8 -#define SQLITE_INDEX_CONSTRAINT_LT 16 -#define SQLITE_INDEX_CONSTRAINT_GE 32 -#define SQLITE_INDEX_CONSTRAINT_MATCH 64 -#define SQLITE_INDEX_CONSTRAINT_LIKE 65 -#define SQLITE_INDEX_CONSTRAINT_GLOB 66 -#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 -#define SQLITE_INDEX_CONSTRAINT_NE 68 -#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 -#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 -#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 -#define SQLITE_INDEX_CONSTRAINT_IS 72 -#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 +** +** ^The left-hand operand of the operator is given by the corresponding +** aConstraint[].iColumn field. ^An iColumn of -1 indicates the left-hand +** operand is the rowid. +** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET +** operators have no left-hand operand, and so for those operators the +** corresponding aConstraint[].iColumn is meaningless and should not be +** used. +** +** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through +** value 255 are reserved to represent functions that are overloaded +** by the [xFindFunction|xFindFunction method] of the virtual table +** implementation. +** +** The right-hand operands for each constraint might be accessible using +** the [wx_sqlite3_vtab_rhs_value()] interface. Usually the right-hand +** operand is only available if it appears as a single constant literal +** in the input SQL. If the right-hand operand is another column or an +** expression (even a constant expression) or a parameter, then the +** wx_sqlite3_vtab_rhs_value() probably will not be able to extract it. +** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and +** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand +** and hence calls to wx_sqlite3_vtab_rhs_value() for those operators will +** always return SQLITE_NOTFOUND. +** +** The collating sequence to be used for comparison can be found using +** the [wx_sqlite3_vtab_collation()] interface. For most real-world virtual +** tables, the collating sequence of constraints does not matter (for example +** because the constraints are numeric) and so the wx_sqlite3_vtab_collation() +** interface is not commonly needed. +*/ +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 +#define SQLITE_INDEX_CONSTRAINT_LIKE 65 +#define SQLITE_INDEX_CONSTRAINT_GLOB 66 +#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 +#define SQLITE_INDEX_CONSTRAINT_NE 68 +#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 +#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 +#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 +#define SQLITE_INDEX_CONSTRAINT_IS 72 +#define SQLITE_INDEX_CONSTRAINT_LIMIT 73 +#define SQLITE_INDEX_CONSTRAINT_OFFSET 74 +#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* ** CAPI3REF: Register A Virtual Table Implementation @@ -8136,7 +7731,7 @@ struct wx_sqlite3_index_info { ** destructor. ** ** ^If the third parameter (the pointer to the wx_sqlite3_module object) is -** NULL then no new module is create and any existing modules with the +** NULL then no new module is created and any existing modules with the ** same name are dropped. ** ** See also: [wx_sqlite3_drop_modules()] @@ -8248,16 +7843,6 @@ SQLITE_API int wx_sqlite3_declare_vtab(wx_sqlite3*, const char *zSQL); */ SQLITE_API int wx_sqlite3_overload_function(wx_sqlite3*, const char *zFuncName, int nArg); -/* -** The interface to the virtual-table mechanism defined above (back up -** to a comment remarkably similar to this one) is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - /* ** CAPI3REF: A Handle To An Open BLOB ** KEYWORDS: {BLOB handle} {BLOB handles} @@ -8911,7 +8496,9 @@ SQLITE_API int wx_sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 #define SQLITE_TESTCTRL_SEEK_COUNT 30 #define SQLITE_TESTCTRL_TRACEFLAGS 31 -#define SQLITE_TESTCTRL_LAST 31 /* Largest TESTCTRL */ +#define SQLITE_TESTCTRL_TUNE 32 +#define SQLITE_TESTCTRL_LOGEST 33 +#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking @@ -9434,6 +9021,16 @@ SQLITE_API int wx_sqlite3_stmt_status(wx_sqlite3_stmt*, int op,int resetFlg); ** The counter is incremented on the first [wx_sqlite3_step()] call of each ** cycle. ** +** [[SQLITE_STMTSTATUS_FILTER_MISS]] +** [[SQLITE_STMTSTATUS_FILTER HIT]] +**
    SQLITE_STMTSTATUS_FILTER_HIT
    +** SQLITE_STMTSTATUS_FILTER_MISS
    +**
    ^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join +** step was bypassed because a Bloom filter returned not-found. The +** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of +** times that the Bloom filter returned a find, and thus the join step +** had to be processed as normal. +** ** [[SQLITE_STMTSTATUS_MEMUSED]]
    SQLITE_STMTSTATUS_MEMUSED
    **
    ^This is the approximate number of bytes of heap memory ** used to store the prepared statement. ^This value is not actually @@ -9448,6 +9045,8 @@ SQLITE_API int wx_sqlite3_stmt_status(wx_sqlite3_stmt*, int op,int resetFlg); #define SQLITE_STMTSTATUS_VM_STEP 4 #define SQLITE_STMTSTATUS_REPREPARE 5 #define SQLITE_STMTSTATUS_RUN 6 +#define SQLITE_STMTSTATUS_FILTER_MISS 7 +#define SQLITE_STMTSTATUS_FILTER_HIT 8 #define SQLITE_STMTSTATUS_MEMUSED 99 /* @@ -9859,7 +9458,7 @@ typedef struct wx_sqlite3_backup wx_sqlite3_backup; ** if the application incorrectly accesses the destination [database connection] ** and so no error code is reported, but the operations may malfunction ** nevertheless. Use of the destination database connection while a -** backup is in progress might also also cause a mutex deadlock. +** backup is in progress might also cause a mutex deadlock. ** ** If running in [shared cache mode], the application must ** guarantee that the shared cache used by the destination database @@ -10111,8 +9710,9 @@ SQLITE_API void wx_sqlite3_log(int iErrCode, const char *zFormat, ...); ** ** A single database handle may have at most a single write-ahead log callback ** registered at one time. ^Calling [wx_sqlite3_wal_hook()] replaces any -** previously registered write-ahead log callback. ^Note that the -** [wx_sqlite3_wal_autocheckpoint()] interface and the +** previously registered write-ahead log callback. ^The return value is +** a copy of the third parameter from the previous call, if any, or 0. +** ^Note that the [wx_sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] both invoke [wx_sqlite3_wal_hook()] and will ** overwrite any prior [wx_sqlite3_wal_hook()] settings. */ @@ -10286,7 +9886,7 @@ SQLITE_API int wx_sqlite3_wal_checkpoint_v2( */ #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */ #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */ -#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */ +#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */ #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */ /* @@ -10415,18 +10015,274 @@ SQLITE_API int wx_sqlite3_vtab_nochange(wx_sqlite3_context*); /* ** CAPI3REF: Determine The Collation For a Virtual Table Constraint +** METHOD: wx_sqlite3_index_info ** ** This function may only be called from within a call to the [xBestIndex] -** method of a [virtual table]. +** method of a [virtual table]. This function returns a pointer to a string +** that is the name of the appropriate collation sequence to use for text +** comparisons on the constraint identified by its arguments. +** +** The first argument must be the pointer to the [wx_sqlite3_index_info] object +** that is the first parameter to the xBestIndex() method. The second argument +** must be an index into the aConstraint[] array belonging to the +** wx_sqlite3_index_info structure passed to xBestIndex. +** +** Important: +** The first parameter must be the same pointer that is passed into the +** xBestMethod() method. The first parameter may not be a pointer to a +** different [wx_sqlite3_index_info] object, even an exact copy. +** +** The return value is computed as follows: +** +**
      +**
    1. If the constraint comes from a WHERE clause expression that contains +** a [COLLATE operator], then the name of the collation specified by +** that COLLATE operator is returned. +**

    2. If there is no COLLATE operator, but the column that is the subject +** of the constraint specifies an alternative collating sequence via +** a [COLLATE clause] on the column definition within the CREATE TABLE +** statement that was passed into [wx_sqlite3_declare_vtab()], then the +** name of that alternative collating sequence is returned. +**

    3. Otherwise, "BINARY" is returned. +**

    +*/ +SQLITE_API const char *wx_sqlite3_vtab_collation(wx_sqlite3_index_info*,int); + +/* +** CAPI3REF: Determine if a virtual table query is DISTINCT +** METHOD: wx_sqlite3_index_info +** +** This API may only be used from within an [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this +** interface from outside of xBestIndex() is undefined and probably harmful. +** +** ^The wx_sqlite3_vtab_distinct() interface returns an integer between 0 and +** 3. The integer returned by wx_sqlite3_vtab_distinct() +** gives the virtual table additional information about how the query +** planner wants the output to be ordered. As long as the virtual table +** can meet the ordering requirements of the query planner, it may set +** the "orderByConsumed" flag. +** +**
    1. +** ^If the wx_sqlite3_vtab_distinct() interface returns 0, that means +** that the query planner needs the virtual table to return all rows in the +** sort order defined by the "nOrderBy" and "aOrderBy" fields of the +** [wx_sqlite3_index_info] object. This is the default expectation. If the +** virtual table outputs all rows in sorted order, then it is always safe for +** the xBestIndex method to set the "orderByConsumed" flag, regardless of +** the return value from wx_sqlite3_vtab_distinct(). +**

    2. +** ^(If the wx_sqlite3_vtab_distinct() interface returns 1, that means +** that the query planner does not need the rows to be returned in sorted order +** as long as all rows with the same values in all columns identified by the +** "aOrderBy" field are adjacent.)^ This mode is used when the query planner +** is doing a GROUP BY. +**

    3. +** ^(If the wx_sqlite3_vtab_distinct() interface returns 2, that means +** that the query planner does not need the rows returned in any particular +** order, as long as rows with the same values in all "aOrderBy" columns +** are adjacent.)^ ^(Furthermore, only a single row for each particular +** combination of values in the columns identified by the "aOrderBy" field +** needs to be returned.)^ ^It is always ok for two or more rows with the same +** values in all "aOrderBy" columns to be returned, as long as all such rows +** are adjacent. ^The virtual table may, if it chooses, omit extra rows +** that have the same value for all columns identified by "aOrderBy". +** ^However omitting the extra rows is optional. +** This mode is used for a DISTINCT query. +**

    4. +** ^(If the wx_sqlite3_vtab_distinct() interface returns 3, that means +** that the query planner needs only distinct rows but it does need the +** rows to be sorted.)^ ^The virtual table implementation is free to omit +** rows that are identical in all aOrderBy columns, if it wants to, but +** it is not required to omit any rows. This mode is used for queries +** that have both DISTINCT and ORDER BY clauses. +**

    +** +** ^For the purposes of comparing virtual table output values to see if the +** values are same value for sorting purposes, two NULL values are considered +** to be the same. In other words, the comparison operator is "IS" +** (or "IS NOT DISTINCT FROM") and not "==". +** +** If a virtual table implementation is unable to meet the requirements +** specified above, then it must not set the "orderByConsumed" flag in the +** [wx_sqlite3_index_info] object or an incorrect answer may result. +** +** ^A virtual table implementation is always free to return rows in any order +** it wants, as long as the "orderByConsumed" flag is not set. ^When the +** the "orderByConsumed" flag is unset, the query planner will add extra +** [bytecode] to ensure that the final results returned by the SQL query are +** ordered correctly. The use of the "orderByConsumed" flag and the +** wx_sqlite3_vtab_distinct() interface is merely an optimization. ^Careful +** use of the wx_sqlite3_vtab_distinct() interface and the "orderByConsumed" +** flag might help queries against a virtual table to run faster. Being +** overly aggressive and setting the "orderByConsumed" flag when it is not +** valid to do so, on the other hand, might cause SQLite to return incorrect +** results. +*/ +SQLITE_API int wx_sqlite3_vtab_distinct(wx_sqlite3_index_info*); + +/* +** CAPI3REF: Identify and handle IN constraints in xBestIndex +** +** This interface may only be used from within an +** [xBestIndex|xBestIndex() method] of a [virtual table] implementation. +** The result of invoking this interface from any other context is +** undefined and probably harmful. +** +** ^(A constraint on a virtual table of the form +** "[IN operator|column IN (...)]" is +** communicated to the xBestIndex method as a +** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use +** this constraint, it must set the corresponding +** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under +** the usual mode of handling IN operators, SQLite generates [bytecode] +** that invokes the [xFilter|xFilter() method] once for each value +** on the right-hand side of the IN operator.)^ Thus the virtual table +** only sees a single value from the right-hand side of the IN operator +** at a time. +** +** In some cases, however, it would be advantageous for the virtual +** table to see all values on the right-hand of the IN operator all at +** once. The wx_sqlite3_vtab_in() interfaces facilitates this in two ways: ** -** The first argument must be the wx_sqlite3_index_info object that is the -** first parameter to the xBestIndex() method. The second argument must be -** an index into the aConstraint[] array belonging to the wx_sqlite3_index_info -** structure passed to xBestIndex. This function returns a pointer to a buffer -** containing the name of the collation sequence for the corresponding -** constraint. +**
      +**
    1. +** ^A call to wx_sqlite3_vtab_in(P,N,-1) will return true (non-zero) +** if and only if the [wx_sqlite3_index_info|P->aConstraint][N] constraint +** is an [IN operator] that can be processed all at once. ^In other words, +** wx_sqlite3_vtab_in() with -1 in the third argument is a mechanism +** by which the virtual table can ask SQLite if all-at-once processing +** of the IN operator is even possible. +** +**

    2. +** ^A call to wx_sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates +** to SQLite that the virtual table does or does not want to process +** the IN operator all-at-once, respectively. ^Thus when the third +** parameter (F) is non-negative, this interface is the mechanism by +** which the virtual table tells SQLite how it wants to process the +** IN operator. +**

    +** +** ^The wx_sqlite3_vtab_in(P,N,F) interface can be invoked multiple times +** within the same xBestIndex method call. ^For any given P,N pair, +** the return value from wx_sqlite3_vtab_in(P,N,F) will always be the same +** within the same xBestIndex call. ^If the interface returns true +** (non-zero), that means that the constraint is an IN operator +** that can be processed all-at-once. ^If the constraint is not an IN +** operator or cannot be processed all-at-once, then the interface returns +** false. +** +** ^(All-at-once processing of the IN operator is selected if both of the +** following conditions are met: +** +**
      +**
    1. The P->aConstraintUsage[N].argvIndex value is set to a positive +** integer. This is how the virtual table tells SQLite that it wants to +** use the N-th constraint. +** +**

    2. The last call to wx_sqlite3_vtab_in(P,N,F) for which F was +** non-negative had F>=1. +**

    )^ +** +** ^If either or both of the conditions above are false, then SQLite uses +** the traditional one-at-a-time processing strategy for the IN constraint. +** ^If both conditions are true, then the argvIndex-th parameter to the +** xFilter method will be an [wx_sqlite3_value] that appears to be NULL, +** but which can be passed to [wx_sqlite3_vtab_in_first()] and +** [wx_sqlite3_vtab_in_next()] to find all values on the right-hand side +** of the IN constraint. */ -SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_index_info*,int); +SQLITE_API int wx_sqlite3_vtab_in(wx_sqlite3_index_info*, int iCons, int bHandle); + +/* +** CAPI3REF: Find all elements on the right-hand side of an IN constraint. +** +** These interfaces are only useful from within the +** [xFilter|xFilter() method] of a [virtual table] implementation. +** The result of invoking these interfaces from any other context +** is undefined and probably harmful. +** +** The X parameter in a call to wx_sqlite3_vtab_in_first(X,P) or +** wx_sqlite3_vtab_in_next(X,P) should be one of the parameters to the +** xFilter method which invokes these routines, and specifically +** a parameter that was previously selected for all-at-once IN constraint +** processing use the [wx_sqlite3_vtab_in()] interface in the +** [xBestIndex|xBestIndex method]. ^(If the X parameter is not +** an xFilter argument that was selected for all-at-once IN constraint +** processing, then these routines return [SQLITE_ERROR].)^ +** +** ^(Use these routines to access all values on the right-hand side +** of the IN constraint using code like the following: +** +**
    +**    for(rc=wx_sqlite3_vtab_in_first(pList, &pVal);
    +**        rc==SQLITE_OK && pVal;
    +**        rc=wx_sqlite3_vtab_in_next(pList, &pVal)
    +**    ){
    +**      // do something with pVal
    +**    }
    +**    if( rc!=SQLITE_OK ){
    +**      // an error has occurred
    +**    }
    +** 
    )^ +** +** ^On success, the wx_sqlite3_vtab_in_first(X,P) and wx_sqlite3_vtab_in_next(X,P) +** routines return SQLITE_OK and set *P to point to the first or next value +** on the RHS of the IN constraint. ^If there are no more values on the +** right hand side of the IN constraint, then *P is set to NULL and these +** routines return [SQLITE_DONE]. ^The return value might be +** some other value, such as SQLITE_NOMEM, in the event of a malfunction. +** +** The *ppOut values returned by these routines are only valid until the +** next call to either of these routines or until the end of the xFilter +** method from which these routines were called. If the virtual table +** implementation needs to retain the *ppOut values for longer, it must make +** copies. The *ppOut values are [protected wx_sqlite3_value|protected]. +*/ +SQLITE_API int wx_sqlite3_vtab_in_first(wx_sqlite3_value *pVal, wx_sqlite3_value **ppOut); +SQLITE_API int wx_sqlite3_vtab_in_next(wx_sqlite3_value *pVal, wx_sqlite3_value **ppOut); + +/* +** CAPI3REF: Constraint values in xBestIndex() +** METHOD: wx_sqlite3_index_info +** +** This API may only be used from within the [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this interface +** from outside of an xBestIndex method are undefined and probably harmful. +** +** ^When the wx_sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within +** the [xBestIndex] method of a [virtual table] implementation, with P being +** a copy of the [wx_sqlite3_index_info] object pointer passed into xBestIndex and +** J being a 0-based index into P->aConstraint[], then this routine +** attempts to set *V to the value of the right-hand operand of +** that constraint if the right-hand operand is known. ^If the +** right-hand operand is not known, then *V is set to a NULL pointer. +** ^The wx_sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if +** and only if *V is set to a value. ^The wx_sqlite3_vtab_rhs_value(P,J,V) +** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th +** constraint is not available. ^The wx_sqlite3_vtab_rhs_value() interface +** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if +** something goes wrong. +** +** The wx_sqlite3_vtab_rhs_value() interface is usually only successful if +** the right-hand operand of a constraint is a literal value in the original +** SQL statement. If the right-hand operand is an expression or a reference +** to some other column or a [host parameter], then wx_sqlite3_vtab_rhs_value() +** will probably return [SQLITE_NOTFOUND]. +** +** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and +** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such +** constraints, wx_sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^ +** +** ^The [wx_sqlite3_value] object returned in *V is a protected wx_sqlite3_value +** and remains valid for the duration of the xBestIndex method call. +** ^When xBestIndex returns, the wx_sqlite3_value object returned by +** wx_sqlite3_vtab_rhs_value() is automatically deallocated. +** +** The "_rhs_" in the name of this routine is an abbreviation for +** "Right-Hand Side". +*/ +SQLITE_API int wx_sqlite3_vtab_rhs_value(wx_sqlite3_index_info*, int, wx_sqlite3_value **ppVal); /* ** CAPI3REF: Conflict resolution modes @@ -10458,6 +10314,10 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ ** managed by the prepared statement S and will be automatically freed when ** S is finalized. ** +** Not all values are available for all query elements. When a value is +** not available, the output variable is set to -1 if the value is numeric, +** or to NULL if it is a string (SQLITE_SCANSTAT_NAME). +** **
    ** [[SQLITE_SCANSTAT_NLOOP]]
    SQLITE_SCANSTAT_NLOOP
    **
    ^The [wx_sqlite3_int64] variable pointed to by the V parameter will be @@ -10485,12 +10345,24 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] ** description for the X-th loop. ** -** [[SQLITE_SCANSTAT_SELECTID]]
    SQLITE_SCANSTAT_SELECT
    +** [[SQLITE_SCANSTAT_SELECTID]]
    SQLITE_SCANSTAT_SELECTID
    **
    ^The "int" variable pointed to by the V parameter will be set to the -** "select-id" for the X-th loop. The select-id identifies which query or -** subquery the loop is part of. The main query has a select-id of zero. -** The select-id is the same value as is output in the first column -** of an [EXPLAIN QUERY PLAN] query. +** id for the X-th query plan element. The id value is unique within the +** statement. The select-id is the same value as is output in the first +** column of an [EXPLAIN QUERY PLAN] query. +** +** [[SQLITE_SCANSTAT_PARENTID]]
    SQLITE_SCANSTAT_PARENTID
    +**
    The "int" variable pointed to by the V parameter will be set to the +** the id of the parent of the current query element, if applicable, or +** to zero if the query element has no parent. This is the same value as +** returned in the second column of an [EXPLAIN QUERY PLAN] query. +** +** [[SQLITE_SCANSTAT_NCYCLE]]
    SQLITE_SCANSTAT_NCYCLE
    +**
    The wx_sqlite3_int64 output value is set to the number of cycles, +** according to the processor time-stamp counter, that elapsed while the +** query element was being processed. This value is not available for +** all query elements - if it is unavailable the output variable is +** set to -1. **
    */ #define SQLITE_SCANSTAT_NLOOP 0 @@ -10499,12 +10371,14 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ #define SQLITE_SCANSTAT_NAME 3 #define SQLITE_SCANSTAT_EXPLAIN 4 #define SQLITE_SCANSTAT_SELECTID 5 +#define SQLITE_SCANSTAT_PARENTID 6 +#define SQLITE_SCANSTAT_NCYCLE 7 /* ** CAPI3REF: Prepared Statement Scan Status ** METHOD: wx_sqlite3_stmt ** -** This interface returns information about the predicted and measured +** These interfaces return information about the predicted and measured ** performance for pStmt. Advanced applications can use this ** interface to compare the predicted and the measured performance and ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. @@ -10515,19 +10389,25 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ ** ** The "iScanStatusOp" parameter determines which status information to return. ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior -** of this interface is undefined. -** ^The requested measurement is written into a variable pointed to by -** the "pOut" parameter. -** Parameter "idx" identifies the specific loop to retrieve statistics for. -** Loops are numbered starting from zero. ^If idx is out of range - less than -** zero or greater than or equal to the total number of loops used to implement -** the statement - a non-zero value is returned and the variable that pOut -** points to is unchanged. -** -** ^Statistics might not be available for all loops in all statements. ^In cases -** where there exist loops with no available statistics, this function behaves -** as if the loop did not exist - it returns non-zero and leave the variable -** that pOut points to unchanged. +** of this interface is undefined. ^The requested measurement is written into +** a variable pointed to by the "pOut" parameter. +** +** The "flags" parameter must be passed a mask of flags. At present only +** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX +** is specified, then status information is available for all elements +** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If +** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements +** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of +** the EXPLAIN QUERY PLAN output) are available. Invoking API +** wx_sqlite3_stmt_scanstatus() is equivalent to calling +** wx_sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter. +** +** Parameter "idx" identifies the specific query element to retrieve statistics +** for. Query elements are numbered starting from zero. A value of -1 may be +** to query for statistics regarding the entire query. ^If idx is out of range +** - less than -1 or greater than or equal to the total number of query +** elements used to implement the statement - a non-zero value is returned and +** the variable that pOut points to is unchanged. ** ** See also: [wx_sqlite3_stmt_scanstatus_reset()] */ @@ -10537,6 +10417,19 @@ SQLITE_API int wx_sqlite3_stmt_scanstatus( int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ void *pOut /* Result written here */ ); +SQLITE_API int wx_sqlite3_stmt_scanstatus_v2( + wx_sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ + int idx, /* Index of loop to report on */ + int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ + int flags, /* Mask of flags defined below */ + void *pOut /* Result written here */ +); + +/* +** CAPI3REF: Prepared Statement Scan Status +** KEYWORDS: {scan status flags} +*/ +#define SQLITE_SCANSTAT_COMPLEX 0x0001 /* ** CAPI3REF: Zero Scan-Status Counters @@ -10627,6 +10520,10 @@ SQLITE_API int wx_sqlite3_db_cacheflush(wx_sqlite3*); ** function is not defined for operations on WITHOUT ROWID tables, or for ** DELETE operations on rowid tables. ** +** ^The wx_sqlite3_preupdate_hook(D,C,P) function returns the P argument from +** the previous call on the same [database connection] D, or NULL for +** the first call on D. +** ** The [wx_sqlite3_preupdate_old()], [wx_sqlite3_preupdate_new()], ** [wx_sqlite3_preupdate_count()], and [wx_sqlite3_preupdate_depth()] interfaces ** provide additional information about a preupdate event. These routines @@ -10663,6 +10560,15 @@ SQLITE_API int wx_sqlite3_db_cacheflush(wx_sqlite3*); ** triggers; or 2 for changes resulting from triggers called by top-level ** triggers; and so forth. ** +** When the [wx_sqlite3_blob_write()] API is used to update a blob column, +** the pre-update hook is invoked with SQLITE_DELETE. This is because the +** in this case the new values are not available. In this case, when a +** callback made with op==SQLITE_DELETE is actuall a write using the +** wx_sqlite3_blob_write() API, the [wx_sqlite3_preupdate_blobwrite()] returns +** the index of the column being written. In other cases, where the +** pre-update hook is being invoked for some other reason, including a +** regular DELETE, wx_sqlite3_preupdate_blobwrite() returns -1. +** ** See also: [wx_sqlite3_update_hook()] */ #if defined(SQLITE_ENABLE_PREUPDATE_HOOK) @@ -10683,6 +10589,7 @@ SQLITE_API int wx_sqlite3_preupdate_old(wx_sqlite3 *, int, wx_sqlite3_value **); SQLITE_API int wx_sqlite3_preupdate_count(wx_sqlite3 *); SQLITE_API int wx_sqlite3_preupdate_depth(wx_sqlite3 *); SQLITE_API int wx_sqlite3_preupdate_new(wx_sqlite3 *, int, wx_sqlite3_value **); +SQLITE_API int wx_sqlite3_preupdate_blobwrite(wx_sqlite3 *); #endif /* @@ -10921,8 +10828,8 @@ SQLITE_API SQLITE_EXPERIMENTAL int wx_sqlite3_snapshot_recover(wx_sqlite3 *db, c ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory ** allocation error occurs. ** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_DESERIALIZE] option. +** This interface is omitted if SQLite is compiled with the +** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API unsigned char *wx_sqlite3_serialize( wx_sqlite3 *db, /* The database connection */ @@ -10969,12 +10876,16 @@ SQLITE_API unsigned char *wx_sqlite3_serialize( ** database is currently in a read transaction or is involved in a backup ** operation. ** +** It is not possible to deserialized into the TEMP database. If the +** S argument to wx_sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the +** function returns SQLITE_ERROR. +** ** If wx_sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then ** [wx_sqlite3_free()] is invoked on argument P prior to returning. ** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_DESERIALIZE] option. +** This interface is omitted if SQLite is compiled with the +** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API int wx_sqlite3_deserialize( wx_sqlite3 *db, /* The database connection */ @@ -11018,15 +10929,31 @@ SQLITE_API int wx_sqlite3_deserialize( # undef double #endif +#if defined(__wasi__) +# undef SQLITE_WASI +# define SQLITE_WASI 1 +# undef SQLITE_OMIT_WAL +# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */ +# ifndef SQLITE_OMIT_LOAD_EXTENSION +# define SQLITE_OMIT_LOAD_EXTENSION +# endif +# ifndef SQLITE_THREADSAFE +# define SQLITE_THREADSAFE 0 +# endif +#endif + #if 0 } /* End of the 'extern "C"' block */ #endif #endif /* SQLITE3_H */ /* Function prototypes of SQLite3 Multiple Ciphers */ +SQLITE_PRIVATE int wx_sqlite3mcCheckVfs(const char*); SQLITE_PRIVATE int wx_sqlite3mcFileControlPragma(wx_sqlite3*, const char*, int, void*); SQLITE_PRIVATE int wx_sqlite3mcHandleAttachKey(wx_sqlite3*, const char*, const char*, wx_sqlite3_value*, char**); SQLITE_PRIVATE int wx_sqlite3mcHandleMainKey(wx_sqlite3*, const char*); +typedef struct PgHdr PgHdrMC; +SQLITE_PRIVATE void* wx_sqlite3mcPagerCodec(PgHdrMC* pPg); /******** Begin file wx_sqlite3rtree.h *********/ /* @@ -11228,6 +11155,38 @@ SQLITE_API int wx_sqlite3session_create( */ SQLITE_API void wx_sqlite3session_delete(wx_sqlite3_session *pSession); +/* +** CAPIREF: Conigure a Session Object +** METHOD: wx_sqlite3_session +** +** This method is used to configure a session object after it has been +** created. At present the only valid value for the second parameter is +** [SQLITE_SESSION_OBJCONFIG_SIZE]. +** +** Arguments for wx_sqlite3session_object_config() +** +** The following values may passed as the the 4th parameter to +** wx_sqlite3session_object_config(). +** +**
    SQLITE_SESSION_OBJCONFIG_SIZE
    +** This option is used to set, clear or query the flag that enables +** the [wx_sqlite3session_changeset_size()] API. Because it imposes some +** computational overhead, this API is disabled by default. Argument +** pArg must point to a value of type (int). If the value is initially +** 0, then the wx_sqlite3session_changeset_size() API is disabled. If it +** is greater than 0, then the same API is enabled. Or, if the initial +** value is less than zero, no change is made. In all cases the (int) +** variable is set to 1 if the wx_sqlite3session_changeset_size() API is +** enabled following the current call, or 0 otherwise. +** +** It is an error (SQLITE_MISUSE) to attempt to modify this setting after +** the first table has been attached to the session object. +*/ +SQLITE_API int wx_sqlite3session_object_config(wx_sqlite3_session*, int op, void *pArg); + +/* +*/ +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 /* ** CAPI3REF: Enable Or Disable A Session Object @@ -11472,6 +11431,22 @@ SQLITE_API int wx_sqlite3session_changeset( void **ppChangeset /* OUT: Buffer containing changeset */ ); +/* +** CAPI3REF: Return An Upper-limit For The Size Of The Changeset +** METHOD: wx_sqlite3_session +** +** By default, this function always returns 0. For it to return +** a useful result, the wx_sqlite3_session object must have been configured +** to enable this API using wx_sqlite3session_object_config() with the +** SQLITE_SESSION_OBJCONFIG_SIZE verb. +** +** When enabled, this function returns an upper limit, in bytes, for the size +** of the changeset that might be produced if wx_sqlite3session_changeset() were +** called. The final changeset size might be equal to or smaller than the +** size in bytes returned by this function. +*/ +SQLITE_API wx_sqlite3_int64 wx_sqlite3session_changeset_size(wx_sqlite3_session *pSession); + /* ** CAPI3REF: Load The Difference Between Tables Into A Session ** METHOD: wx_sqlite3_session @@ -13403,12 +13378,17 @@ struct fts5_api { /************** End of wx_sqlite3.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ +/* +** Reuse the STATIC_LRU for mutex access to wx_sqlite3_temp_directory. +*/ +#define SQLITE_MUTEX_STATIC_TEMPDIR SQLITE_MUTEX_STATIC_VFS1 + /* ** Include the configuration header output by 'configure' if we're using the ** autoconf-based build */ #if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) -/* #include "config.h" */ +#include "sqlite_cfg.h" #define SQLITECONFIG_H 1 #endif @@ -13644,11 +13624,12 @@ struct fts5_api { #ifndef __has_extension # define __has_extension(x) 0 /* compatibility with non-clang compilers */ #endif -#if GCC_VERSION>=4007000 || \ - (__has_extension(c_atomic) && __has_extension(c_atomic_store_n)) +#if GCC_VERSION>=4007000 || __has_extension(c_atomic) +# define SQLITE_ATOMIC_INTRINSICS 1 # define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED) # define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED) #else +# define SQLITE_ATOMIC_INTRINSICS 0 # define AtomicLoad(PTR) (*(PTR)) # define AtomicStore(PTR,VAL) (*(PTR) = (VAL)) #endif @@ -13853,11 +13834,12 @@ struct fts5_api { ** is significant and used at least once. On switch statements ** where multiple cases go to the same block of code, testcase() ** can insure that all cases are evaluated. -** */ -#ifdef SQLITE_COVERAGE_TEST -SQLITE_PRIVATE void wx_sqlite3Coverage(int); -# define testcase(X) if( X ){ wx_sqlite3Coverage(__LINE__); } +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) +# ifndef SQLITE_AMALGAMATION + extern unsigned int wx_sqlite3CoverageCounter; +# endif +# define testcase(X) if( X ){ wx_sqlite3CoverageCounter += (unsigned)__LINE__; } #else # define testcase(X) #endif @@ -13887,6 +13869,14 @@ SQLITE_PRIVATE void wx_sqlite3Coverage(int); # define VVA_ONLY(X) #endif +/* +** Disable ALWAYS() and NEVER() (make them pass-throughs) for coverage +** and mutation testing +*/ +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 +#endif + /* ** The ALWAYS and NEVER macros surround boolean expressions which ** are intended to always be true or false, respectively. Such @@ -13902,7 +13892,7 @@ SQLITE_PRIVATE void wx_sqlite3Coverage(int); ** be true and false so that the unreachable code they specify will ** not be counted as untested code. */ -#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) @@ -13913,26 +13903,6 @@ SQLITE_PRIVATE void wx_sqlite3Coverage(int); # define NEVER(X) (X) #endif -/* -** The harmless(X) macro indicates that expression X is usually false -** but can be true without causing any problems, but we don't know of -** any way to cause X to be true. -** -** In debugging and testing builds, this macro will abort if X is ever -** true. In this way, developers are alerted to a possible test case -** that causes X to be true. If a harmless macro ever fails, that is -** an opportunity to change the macro into a testcase() and add a new -** test case to the test suite. -** -** For normal production builds, harmless(X) is a no-op, since it does -** not matter whether expression X is true or false. -*/ -#ifdef SQLITE_DEBUG -# define harmless(X) assert(!(X)); -#else -# define harmless(X) -#endif - /* ** Some conditionals are optimizations only. In other words, if the ** conditionals are replaced with a constant 1 (true) or 0 (false) then @@ -13996,6 +13966,13 @@ SQLITE_PRIVATE void wx_sqlite3Coverage(int); # undef SQLITE_ENABLE_EXPLAIN_COMMENTS #endif +/* +** SQLITE_OMIT_VIRTUALTABLE implies SQLITE_OMIT_ALTERTABLE +*/ +#if defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE) +# define SQLITE_OMIT_ALTERTABLE +#endif + /* ** Return true (non-zero) if the input is an integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() @@ -14108,7 +14085,7 @@ SQLITE_PRIVATE void wx_sqlite3HashClear(Hash*); /* ** Number of entries in a hash table */ -/* #define sqliteHashCount(H) ((H)->count) // NOT USED */ +#define sqliteHashCount(H) ((H)->count) #endif /* SQLITE_HASH_H */ @@ -14140,8 +14117,8 @@ SQLITE_PRIVATE void wx_sqlite3HashClear(Hash*); #define TK_LP 22 #define TK_RP 23 #define TK_AS 24 -#define TK_WITHOUT 25 -#define TK_COMMA 26 +#define TK_COMMA 25 +#define TK_WITHOUT 26 #define TK_ABORT 27 #define TK_ACTION 28 #define TK_AFTER 29 @@ -14227,77 +14204,79 @@ SQLITE_PRIVATE void wx_sqlite3HashClear(Hash*); #define TK_SLASH 109 #define TK_REM 110 #define TK_CONCAT 111 -#define TK_COLLATE 112 -#define TK_BITNOT 113 -#define TK_ON 114 -#define TK_INDEXED 115 -#define TK_STRING 116 -#define TK_JOIN_KW 117 -#define TK_CONSTRAINT 118 -#define TK_DEFAULT 119 -#define TK_NULL 120 -#define TK_PRIMARY 121 -#define TK_UNIQUE 122 -#define TK_CHECK 123 -#define TK_REFERENCES 124 -#define TK_AUTOINCR 125 -#define TK_INSERT 126 -#define TK_DELETE 127 -#define TK_UPDATE 128 -#define TK_SET 129 -#define TK_DEFERRABLE 130 -#define TK_FOREIGN 131 -#define TK_DROP 132 -#define TK_UNION 133 -#define TK_ALL 134 -#define TK_EXCEPT 135 -#define TK_INTERSECT 136 -#define TK_SELECT 137 -#define TK_VALUES 138 -#define TK_DISTINCT 139 -#define TK_DOT 140 -#define TK_FROM 141 -#define TK_JOIN 142 -#define TK_USING 143 -#define TK_ORDER 144 -#define TK_GROUP 145 -#define TK_HAVING 146 -#define TK_LIMIT 147 -#define TK_WHERE 148 -#define TK_RETURNING 149 -#define TK_INTO 150 -#define TK_NOTHING 151 -#define TK_FLOAT 152 -#define TK_BLOB 153 -#define TK_INTEGER 154 -#define TK_VARIABLE 155 -#define TK_CASE 156 -#define TK_WHEN 157 -#define TK_THEN 158 -#define TK_ELSE 159 -#define TK_INDEX 160 -#define TK_ALTER 161 -#define TK_ADD 162 -#define TK_WINDOW 163 -#define TK_OVER 164 -#define TK_FILTER 165 -#define TK_COLUMN 166 -#define TK_AGG_FUNCTION 167 -#define TK_AGG_COLUMN 168 -#define TK_TRUEFALSE 169 -#define TK_ISNOT 170 -#define TK_FUNCTION 171 -#define TK_UMINUS 172 -#define TK_UPLUS 173 -#define TK_TRUTH 174 -#define TK_REGISTER 175 -#define TK_VECTOR 176 -#define TK_SELECT_COLUMN 177 -#define TK_IF_NULL_ROW 178 -#define TK_ASTERISK 179 -#define TK_SPAN 180 -#define TK_SPACE 181 -#define TK_ILLEGAL 182 +#define TK_PTR 112 +#define TK_COLLATE 113 +#define TK_BITNOT 114 +#define TK_ON 115 +#define TK_INDEXED 116 +#define TK_STRING 117 +#define TK_JOIN_KW 118 +#define TK_CONSTRAINT 119 +#define TK_DEFAULT 120 +#define TK_NULL 121 +#define TK_PRIMARY 122 +#define TK_UNIQUE 123 +#define TK_CHECK 124 +#define TK_REFERENCES 125 +#define TK_AUTOINCR 126 +#define TK_INSERT 127 +#define TK_DELETE 128 +#define TK_UPDATE 129 +#define TK_SET 130 +#define TK_DEFERRABLE 131 +#define TK_FOREIGN 132 +#define TK_DROP 133 +#define TK_UNION 134 +#define TK_ALL 135 +#define TK_EXCEPT 136 +#define TK_INTERSECT 137 +#define TK_SELECT 138 +#define TK_VALUES 139 +#define TK_DISTINCT 140 +#define TK_DOT 141 +#define TK_FROM 142 +#define TK_JOIN 143 +#define TK_USING 144 +#define TK_ORDER 145 +#define TK_GROUP 146 +#define TK_HAVING 147 +#define TK_LIMIT 148 +#define TK_WHERE 149 +#define TK_RETURNING 150 +#define TK_INTO 151 +#define TK_NOTHING 152 +#define TK_FLOAT 153 +#define TK_BLOB 154 +#define TK_INTEGER 155 +#define TK_VARIABLE 156 +#define TK_CASE 157 +#define TK_WHEN 158 +#define TK_THEN 159 +#define TK_ELSE 160 +#define TK_INDEX 161 +#define TK_ALTER 162 +#define TK_ADD 163 +#define TK_WINDOW 164 +#define TK_OVER 165 +#define TK_FILTER 166 +#define TK_COLUMN 167 +#define TK_AGG_FUNCTION 168 +#define TK_AGG_COLUMN 169 +#define TK_TRUEFALSE 170 +#define TK_ISNOT 171 +#define TK_FUNCTION 172 +#define TK_UMINUS 173 +#define TK_UPLUS 174 +#define TK_TRUTH 175 +#define TK_REGISTER 176 +#define TK_VECTOR 177 +#define TK_SELECT_COLUMN 178 +#define TK_IF_NULL_ROW 179 +#define TK_ASTERISK 180 +#define TK_SPAN 181 +#define TK_ERROR 182 +#define TK_SPACE 183 +#define TK_ILLEGAL 184 /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -14403,7 +14382,7 @@ SQLITE_PRIVATE void wx_sqlite3HashClear(Hash*); ** number of pages. A negative number N translations means that a buffer ** of -1024*N bytes is allocated and used for as many pages as it will hold. ** -** The default value of "20" was choosen to minimize the run-time of the +** The default value of "20" was chosen to minimize the run-time of the ** speedtest1 test program with options: --shrink-memory --reprepare */ #ifndef SQLITE_DEFAULT_PCACHE_INITSZ @@ -14522,15 +14501,9 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */ /* ** The datatype used to store estimates of the number of rows in a -** table or index. This is an unsigned integer type. For 99.9% of -** the world, a 32-bit integer is sufficient. But a 64-bit integer -** can be used at compile-time if desired. +** table or index. */ -#ifdef SQLITE_64BIT_STATS - typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */ -#else - typedef u32 tRowcnt; /* 32-bit is the default */ -#endif +typedef u64 tRowcnt; /* ** Estimated quantities used for query planning are stored as 16-bit @@ -14565,6 +14538,7 @@ typedef INT16_TYPE LogEst; # define SQLITE_PTRSIZE __SIZEOF_POINTER__ # elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(_M_ARM) || defined(__arm__) || defined(__x86) || \ + (defined(__APPLE__) && defined(__POWERPC__)) || \ (defined(__TOS_AIX__) && !defined(__64BIT__)) # define SQLITE_PTRSIZE 4 # else @@ -14646,8 +14620,19 @@ typedef INT16_TYPE LogEst; /* ** Round up a number to the next larger multiple of 8. This is used ** to force 8-byte alignment on 64-bit architectures. +** +** ROUND8() always does the rounding, for any argument. +** +** ROUND8P() assumes that the argument is already an integer number of +** pointers in size, and so it is a no-op on systems where the pointer +** size is 8. */ #define ROUND8(x) (((x)+7)&~7) +#if SQLITE_PTRSIZE==8 +# define ROUND8P(x) (x) +#else +# define ROUND8P(x) (((x)+7)&~7) +#endif /* ** Round down to the nearest multiple of 8 @@ -14664,9 +14649,9 @@ typedef INT16_TYPE LogEst; ** pointers. In that case, only verify 4-byte alignment. */ #ifdef SQLITE_4_BYTE_ALIGNED_MALLOC -# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&3)==0) +# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) #else -# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) +# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) #endif /* @@ -14710,23 +14695,47 @@ typedef INT16_TYPE LogEst; #endif /* -** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not -** the Select query generator tracing logic is turned on. +** TREETRACE_ENABLED will be either 1 or 0 depending on whether or not +** the Abstract Syntax Tree tracing logic is turned on. */ #if !defined(SQLITE_AMALGAMATION) -SQLITE_PRIVATE u32 wx_sqlite3SelectTrace; +SQLITE_PRIVATE u32 wx_sqlite3TreeTrace; #endif #if defined(SQLITE_DEBUG) \ - && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE)) -# define SELECTTRACE_ENABLED 1 -# define SELECTTRACE(K,P,S,X) \ - if(wx_sqlite3SelectTrace&(K)) \ + && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE) \ + || defined(SQLITE_ENABLE_TREETRACE)) +# define TREETRACE_ENABLED 1 +# define TREETRACE(K,P,S,X) \ + if(wx_sqlite3TreeTrace&(K)) \ wx_sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ wx_sqlite3DebugPrintf X #else -# define SELECTTRACE(K,P,S,X) -# define SELECTTRACE_ENABLED 0 -#endif +# define TREETRACE(K,P,S,X) +# define TREETRACE_ENABLED 0 +#endif + +/* TREETRACE flag meanings: +** +** 0x00000001 Beginning and end of SELECT processing +** 0x00000002 WHERE clause processing +** 0x00000004 Query flattener +** 0x00000008 Result-set wildcard expansion +** 0x00000010 Query name resolution +** 0x00000020 Aggregate analysis +** 0x00000040 Window functions +** 0x00000080 Generated column names +** 0x00000100 Move HAVING terms into WHERE +** 0x00000200 Count-of-view optimization +** 0x00000400 Compound SELECT processing +** 0x00000800 Drop superfluous ORDER BY +** 0x00001000 LEFT JOIN simplifies to JOIN +** 0x00002000 Constant propagation +** 0x00004000 Push-down optimization +** 0x00008000 After all FROM-clause analysis +** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing +** 0x00020000 Transform DISTINCT into GROUP BY +** 0x00040000 SELECT tree dump after all code has been generated +*/ /* ** Macros for "wheretrace" @@ -14740,6 +14749,36 @@ SQLITE_PRIVATE u32 wx_sqlite3WhereTrace; # define WHERETRACE(K,X) #endif +/* +** Bits for the wx_sqlite3WhereTrace mask: +** +** (---any--) Top-level block structure +** 0x-------F High-level debug messages +** 0x----FFF- More detail +** 0xFFFF---- Low-level debug messages +** +** 0x00000001 Code generation +** 0x00000002 Solver +** 0x00000004 Solver costs +** 0x00000008 WhereLoop inserts +** +** 0x00000010 Display wx_sqlite3_index_info xBestIndex calls +** 0x00000020 Range an equality scan metrics +** 0x00000040 IN operator decisions +** 0x00000080 WhereLoop cost adjustements +** 0x00000100 +** 0x00000200 Covering index decisions +** 0x00000400 OR optimization +** 0x00000800 Index scanner +** 0x00001000 More details associated with code generation +** 0x00002000 +** 0x00004000 Show all WHERE terms at key points +** 0x00008000 Show the full SELECT statement at key places +** +** 0x00010000 Show more detail when printing WHERE terms +** 0x00020000 Show WHERE terms returned from whereScanNext() +*/ + /* ** An instance of the following structure is used to store the busy-handler @@ -14759,11 +14798,25 @@ struct BusyHandler { /* ** Name of table that holds the database schema. +** +** The PREFERRED names are used whereever possible. But LEGACY is also +** used for backwards compatibility. +** +** 1. Queries can use either the PREFERRED or the LEGACY names +** 2. The wx_sqlite3_set_authorizer() callback uses the LEGACY name +** 3. The PRAGMA table_list statement uses the PREFERRED name +** +** The LEGACY names are stored in the internal symbol hash table +** in support of (2). Names are translated using wx_sqlite3PreferredTableName() +** for (3). The wx_sqlite3FindTable() function takes care of translating +** names for (1). +** +** Note that "sqlite_temp_schema" can also be called "temp.sqlite_schema". */ -#define DFLT_SCHEMA_TABLE "sqlite_master" -#define DFLT_TEMP_SCHEMA_TABLE "sqlite_temp_master" -#define ALT_SCHEMA_TABLE "sqlite_schema" -#define ALT_TEMP_SCHEMA_TABLE "sqlite_temp_schema" +#define LEGACY_SCHEMA_TABLE "sqlite_master" +#define LEGACY_TEMP_SCHEMA_TABLE "sqlite_temp_master" +#define PREFERRED_SCHEMA_TABLE "sqlite_schema" +#define PREFERRED_TEMP_SCHEMA_TABLE "sqlite_temp_schema" /* @@ -14775,7 +14828,7 @@ struct BusyHandler { ** The name of the schema table. The name is different for TEMP. */ #define SCHEMA_TABLE(x) \ - ((!OMIT_TEMPDB)&&(x==1)?DFLT_TEMP_SCHEMA_TABLE:DFLT_SCHEMA_TABLE) + ((!OMIT_TEMPDB)&&(x==1)?LEGACY_TEMP_SCHEMA_TABLE:LEGACY_SCHEMA_TABLE) /* ** A convenience macro that returns the number of elements in @@ -14796,7 +14849,7 @@ struct BusyHandler { ** pointer will work here as long as it is distinct from SQLITE_STATIC ** and SQLITE_TRANSIENT. */ -#define SQLITE_DYNAMIC ((wx_sqlite3_destructor_type)wx_sqlite3OomFault) +#define SQLITE_DYNAMIC ((wx_sqlite3_destructor_type)wx_sqlite3OomClear) /* ** When SQLITE_OMIT_WSD is defined, it means that the target platform does @@ -14865,6 +14918,7 @@ typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; typedef struct IdList IdList; typedef struct Index Index; +typedef struct IndexedExpr IndexedExpr; typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; @@ -14872,6 +14926,7 @@ typedef struct Lookaside Lookaside; typedef struct LookasideSlot LookasideSlot; typedef struct Module Module; typedef struct NameContext NameContext; +typedef struct OnOrUsing OnOrUsing; typedef struct Parse Parse; typedef struct ParseCleanup ParseCleanup; typedef struct PreUpdate PreUpdate; @@ -14924,10 +14979,12 @@ typedef struct With With; /* ** A bit in a Bitmask */ -#define MASKBIT(n) (((Bitmask)1)<<(n)) -#define MASKBIT64(n) (((u64)1)<<(n)) -#define MASKBIT32(n) (((unsigned int)1)<<(n)) -#define ALLBITS ((Bitmask)-1) +#define MASKBIT(n) (((Bitmask)1)<<(n)) +#define MASKBIT64(n) (((u64)1)<<(n)) +#define MASKBIT32(n) (((unsigned int)1)<<(n)) +#define SMASKBIT32(n) ((n)<=31?((unsigned int)1)<<(n):0) +#define ALLBITS ((Bitmask)-1) +#define TOPBIT (((Bitmask)1)<<(BMS-1)) /* A VList object records a mapping between parameters/variables/wildcards ** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer @@ -14942,6 +14999,331 @@ typedef int VList; ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque ** pointer types (i.e. FuncDef) defined above. */ +/************** Include os.h in the middle of sqliteInt.h ********************/ +/************** Begin file os.h **********************************************/ +/* +** 2001 September 16 +** +** 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 header file (together with is companion C source-code file +** "os.c") attempt to abstract the underlying operating system so that +** the SQLite library will work on both POSIX and windows systems. +** +** This header file is #include-ed by sqliteInt.h and thus ends up +** being included by every source file. +*/ +#ifndef _SQLITE_OS_H_ +#define _SQLITE_OS_H_ + +/* +** Attempt to automatically detect the operating system and setup the +** necessary pre-processor macros for it. +*/ +/************** Include os_setup.h in the middle of os.h *********************/ +/************** Begin file os_setup.h ****************************************/ +/* +** 2013 November 25 +** +** 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 pre-processor directives related to operating system +** detection and/or setup. +*/ +#ifndef SQLITE_OS_SETUP_H +#define SQLITE_OS_SETUP_H + +/* +** Figure out if we are dealing with Unix, Windows, or some other operating +** system. +** +** After the following block of preprocess macros, all of +** +** SQLITE_OS_KV +** SQLITE_OS_OTHER +** SQLITE_OS_UNIX +** SQLITE_OS_WIN +** +** will defined to either 1 or 0. One of them will be 1. The others will be 0. +** If none of the macros are initially defined, then select either +** SQLITE_OS_UNIX or SQLITE_OS_WIN depending on the target platform. +** +** If SQLITE_OS_OTHER=1 is specified at compile-time, then the application +** must provide its own VFS implementation together with wx_sqlite3_os_init() +** and wx_sqlite3_os_end() routines. +*/ +#if !defined(SQLITE_OS_KV) && !defined(SQLITE_OS_OTHER) && \ + !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_WIN) +# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \ + defined(__MINGW32__) || defined(__BORLANDC__) +# define SQLITE_OS_WIN 1 +# define SQLITE_OS_UNIX 0 +# else +# define SQLITE_OS_WIN 0 +# define SQLITE_OS_UNIX 1 +# endif +#endif +#if SQLITE_OS_OTHER+1>1 +# undef SQLITE_OS_KV +# define SQLITE_OS_KV 0 +# undef SQLITE_OS_UNIX +# define SQLITE_OS_UNIX 0 +# undef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +#endif +#if SQLITE_OS_KV+1>1 +# undef SQLITE_OS_OTHER +# define SQLITE_OS_OTHER 0 +# undef SQLITE_OS_UNIX +# define SQLITE_OS_UNIX 0 +# undef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +# define SQLITE_OMIT_LOAD_EXTENSION 1 +# define SQLITE_OMIT_WAL 1 +# define SQLITE_OMIT_DEPRECATED 1 +# undef SQLITE_TEMP_STORE +# define SQLITE_TEMP_STORE 3 /* Always use memory for temporary storage */ +# define SQLITE_DQS 0 +# define SQLITE_OMIT_SHARED_CACHE 1 +# define SQLITE_OMIT_AUTOINIT 1 +#endif +#if SQLITE_OS_UNIX+1>1 +# undef SQLITE_OS_KV +# define SQLITE_OS_KV 0 +# undef SQLITE_OS_OTHER +# define SQLITE_OS_OTHER 0 +# undef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +#endif +#if SQLITE_OS_WIN+1>1 +# undef SQLITE_OS_KV +# define SQLITE_OS_KV 0 +# undef SQLITE_OS_OTHER +# define SQLITE_OS_OTHER 0 +# undef SQLITE_OS_UNIX +# define SQLITE_OS_UNIX 0 +#endif + + +#endif /* SQLITE_OS_SETUP_H */ + +/************** End of os_setup.h ********************************************/ +/************** Continuing where we left off in os.h *************************/ + +/* If the SET_FULLSYNC macro is not defined above, then make it +** a no-op +*/ +#ifndef SET_FULLSYNC +# define SET_FULLSYNC(x,y) +#endif + +/* Maximum pathname length. Note: FILENAME_MAX defined by stdio.h +*/ +#ifndef SQLITE_MAX_PATHLEN +# define SQLITE_MAX_PATHLEN FILENAME_MAX +#endif + +/* Maximum number of symlinks that will be resolved while trying to +** expand a filename in xFullPathname() in the VFS. +*/ +#ifndef SQLITE_MAX_SYMLINK +# define SQLITE_MAX_SYMLINK 200 +#endif + +/* +** The default size of a disk sector +*/ +#ifndef SQLITE_DEFAULT_SECTOR_SIZE +# define SQLITE_DEFAULT_SECTOR_SIZE 4096 +#endif + +/* +** Temporary files are named starting with this prefix followed by 16 random +** alphanumeric characters, and no file extension. They are stored in the +** OS's standard temporary file directory, and are deleted prior to exit. +** If sqlite is being embedded in another program, you may wish to change the +** prefix to reflect your program's name, so that if your program exits +** prematurely, old temporary files can be easily identified. This can be done +** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. +** +** 2006-10-31: The default prefix used to be "sqlite_". But then +** Mcafee started using SQLite in their anti-virus product and it +** started putting files with the "sqlite" name in the c:/temp folder. +** This annoyed many windows users. Those users would then do a +** Google search for "sqlite", find the telephone numbers of the +** developers and call to wake them up at night and complain. +** For this reason, the default name prefix is changed to be "sqlite" +** spelled backwards. So the temp files are still identified, but +** anybody smart enough to figure out the code is also likely smart +** enough to know that calling the developer will not help get rid +** of the file. +*/ +#ifndef SQLITE_TEMP_FILE_PREFIX +# define SQLITE_TEMP_FILE_PREFIX "etilqs_" +#endif + +/* +** The following values may be passed as the second argument to +** wx_sqlite3OsLock(). The various locks exhibit the following semantics: +** +** SHARED: Any number of processes may hold a SHARED lock simultaneously. +** RESERVED: A single process may hold a RESERVED lock on a file at +** any time. Other processes may hold and obtain new SHARED locks. +** PENDING: A single process may hold a PENDING lock on a file at +** any one time. Existing SHARED locks may persist, but no new +** SHARED locks may be obtained by other processes. +** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. +** +** PENDING_LOCK may not be passed directly to wx_sqlite3OsLock(). Instead, a +** process that requests an EXCLUSIVE lock may actually obtain a PENDING +** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to +** wx_sqlite3OsLock(). +*/ +#define NO_LOCK 0 +#define SHARED_LOCK 1 +#define RESERVED_LOCK 2 +#define PENDING_LOCK 3 +#define EXCLUSIVE_LOCK 4 + +/* +** File Locking Notes: (Mostly about windows but also some info for Unix) +** +** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because +** those functions are not available. So we use only LockFile() and +** UnlockFile(). +** +** LockFile() prevents not just writing but also reading by other processes. +** A SHARED_LOCK is obtained by locking a single randomly-chosen +** byte out of a specific range of bytes. The lock byte is obtained at +** random so two separate readers can probably access the file at the +** same time, unless they are unlucky and choose the same lock byte. +** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. +** There can only be one writer. A RESERVED_LOCK is obtained by locking +** a single byte of the file that is designated as the reserved lock byte. +** A PENDING_LOCK is obtained by locking a designated byte different from +** the RESERVED_LOCK byte. +** +** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, +** which means we can use reader/writer locks. When reader/writer locks +** are used, the lock is placed on the same range of bytes that is used +** for probabilistic locking in Win95/98/ME. Hence, the locking scheme +** will support two or more Win95 readers or two or more WinNT readers. +** But a single Win95 reader will lock out all WinNT readers and a single +** WinNT reader will lock out all other Win95 readers. +** +** The following #defines specify the range of bytes used for locking. +** SHARED_SIZE is the number of bytes available in the pool from which +** a random byte is selected for a shared lock. The pool of bytes for +** shared locks begins at SHARED_FIRST. +** +** The same locking strategy and +** byte ranges are used for Unix. This leaves open the possibility of having +** clients on win95, winNT, and unix all talking to the same shared file +** and all locking correctly. To do so would require that samba (or whatever +** tool is being used for file sharing) implements locks correctly between +** windows and unix. I'm guessing that isn't likely to happen, but by +** using the same locking range we are at least open to the possibility. +** +** Locking in windows is manditory. For this reason, we cannot store +** actual data in the bytes used for locking. The pager never allocates +** the pages involved in locking therefore. SHARED_SIZE is selected so +** that all locks will fit on a single page even at the minimum page size. +** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE +** is set high so that we don't have to allocate an unused page except +** for very large databases. But one should test the page skipping logic +** by setting PENDING_BYTE low and running the entire regression suite. +** +** Changing the value of PENDING_BYTE results in a subtly incompatible +** file format. Depending on how it is changed, you might not notice +** the incompatibility right away, even running a full regression test. +** The default location of PENDING_BYTE is the first byte past the +** 1GB boundary. +** +*/ +#ifdef SQLITE_OMIT_WSD +# define PENDING_BYTE (0x40000000) +#else +# define PENDING_BYTE wx_sqlite3PendingByte +#endif +#define RESERVED_BYTE (PENDING_BYTE+1) +#define SHARED_FIRST (PENDING_BYTE+2) +#define SHARED_SIZE 510 + +/* +** Wrapper around OS specific wx_sqlite3_os_init() function. +*/ +SQLITE_PRIVATE int wx_sqlite3OsInit(void); + +/* +** Functions for accessing wx_sqlite3_file methods +*/ +SQLITE_PRIVATE void wx_sqlite3OsClose(wx_sqlite3_file*); +SQLITE_PRIVATE int wx_sqlite3OsRead(wx_sqlite3_file*, void*, int amt, i64 offset); +SQLITE_PRIVATE int wx_sqlite3OsWrite(wx_sqlite3_file*, const void*, int amt, i64 offset); +SQLITE_PRIVATE int wx_sqlite3OsTruncate(wx_sqlite3_file*, i64 size); +SQLITE_PRIVATE int wx_sqlite3OsSync(wx_sqlite3_file*, int); +SQLITE_PRIVATE int wx_sqlite3OsFileSize(wx_sqlite3_file*, i64 *pSize); +SQLITE_PRIVATE int wx_sqlite3OsLock(wx_sqlite3_file*, int); +SQLITE_PRIVATE int wx_sqlite3OsUnlock(wx_sqlite3_file*, int); +SQLITE_PRIVATE int wx_sqlite3OsCheckReservedLock(wx_sqlite3_file *id, int *pResOut); +SQLITE_PRIVATE int wx_sqlite3OsFileControl(wx_sqlite3_file*,int,void*); +SQLITE_PRIVATE void wx_sqlite3OsFileControlHint(wx_sqlite3_file*,int,void*); +#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 +SQLITE_PRIVATE int wx_sqlite3OsSectorSize(wx_sqlite3_file *id); +SQLITE_PRIVATE int wx_sqlite3OsDeviceCharacteristics(wx_sqlite3_file *id); +#ifndef SQLITE_OMIT_WAL +SQLITE_PRIVATE int wx_sqlite3OsShmMap(wx_sqlite3_file *,int,int,int,void volatile **); +SQLITE_PRIVATE int wx_sqlite3OsShmLock(wx_sqlite3_file *id, int, int, int); +SQLITE_PRIVATE void wx_sqlite3OsShmBarrier(wx_sqlite3_file *id); +SQLITE_PRIVATE int wx_sqlite3OsShmUnmap(wx_sqlite3_file *id, int); +#endif /* SQLITE_OMIT_WAL */ +SQLITE_PRIVATE int wx_sqlite3OsFetch(wx_sqlite3_file *id, i64, int, void **); +SQLITE_PRIVATE int wx_sqlite3OsUnfetch(wx_sqlite3_file *, i64, void *); + + +/* +** Functions for accessing wx_sqlite3_vfs methods +*/ +SQLITE_PRIVATE int wx_sqlite3OsOpen(wx_sqlite3_vfs *, const char *, wx_sqlite3_file*, int, int *); +SQLITE_PRIVATE int wx_sqlite3OsDelete(wx_sqlite3_vfs *, const char *, int); +SQLITE_PRIVATE int wx_sqlite3OsAccess(wx_sqlite3_vfs *, const char *, int, int *pResOut); +SQLITE_PRIVATE int wx_sqlite3OsFullPathname(wx_sqlite3_vfs *, const char *, int, char *); +#ifndef SQLITE_OMIT_LOAD_EXTENSION +SQLITE_PRIVATE void *wx_sqlite3OsDlOpen(wx_sqlite3_vfs *, const char *); +SQLITE_PRIVATE void wx_sqlite3OsDlError(wx_sqlite3_vfs *, int, char *); +SQLITE_PRIVATE void (*wx_sqlite3OsDlSym(wx_sqlite3_vfs *, void *, const char *))(void); +SQLITE_PRIVATE void wx_sqlite3OsDlClose(wx_sqlite3_vfs *, void *); +#endif /* SQLITE_OMIT_LOAD_EXTENSION */ +SQLITE_PRIVATE int wx_sqlite3OsRandomness(wx_sqlite3_vfs *, int, char *); +SQLITE_PRIVATE int wx_sqlite3OsSleep(wx_sqlite3_vfs *, int); +SQLITE_PRIVATE int wx_sqlite3OsGetLastError(wx_sqlite3_vfs*); +SQLITE_PRIVATE int wx_sqlite3OsCurrentTimeInt64(wx_sqlite3_vfs *, wx_sqlite3_int64*); + +/* +** Convenience functions for opening and closing files using +** wx_sqlite3_malloc() to obtain space for the file-handle structure. +*/ +SQLITE_PRIVATE int wx_sqlite3OsOpenMalloc(wx_sqlite3_vfs *, const char *, wx_sqlite3_file **, int,int*); +SQLITE_PRIVATE void wx_sqlite3OsCloseFree(wx_sqlite3_file *); + +#endif /* _SQLITE_OS_H_ */ + +/************** End of os.h **************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ /************** Include pager.h in the middle of sqliteInt.h *****************/ /************** Begin file pager.h *******************************************/ /* @@ -14989,14 +15371,15 @@ typedef struct Pager Pager; typedef struct PgHdr DbPage; /* -** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is +** Page number PAGER_SJ_PGNO is never used in an SQLite database (it is ** reserved for working around a windows/posix incompatibility). It is ** used in the journal to signify that the remainder of the journal file ** is devoted to storing a super-journal name - there are no more pages to ** roll back. See comments for function writeSuperJournal() in pager.c ** for details. */ -#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) +#define PAGER_SJ_PGNO_COMPUTED(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) +#define PAGER_SJ_PGNO(x) ((x)->lckPgno) /* ** Allowed values for the flags parameter to wx_sqlite3PagerOpen(). @@ -15316,7 +15699,7 @@ SQLITE_PRIVATE int wx_sqlite3BtreeIncrVacuum(Btree *); #define BTREE_BLOBKEY 2 /* Table has keys only - no data */ SQLITE_PRIVATE int wx_sqlite3BtreeDropTable(Btree*, int, int*); -SQLITE_PRIVATE int wx_sqlite3BtreeClearTable(Btree*, int, int*); +SQLITE_PRIVATE int wx_sqlite3BtreeClearTable(Btree*, int, i64*); SQLITE_PRIVATE int wx_sqlite3BtreeClearTableOfCursor(BtCursor*); SQLITE_PRIVATE int wx_sqlite3BtreeTripAllCursors(Btree*, int, int); @@ -15376,7 +15759,7 @@ SQLITE_PRIVATE int wx_sqlite3BtreeNewDb(Btree *p); ** reduce network bandwidth. ** ** Note that BTREE_HINT_FLAGS with BTREE_BULKLOAD is the only hint used by -** standard SQLite. The other hints are provided for extentions that use +** standard SQLite. The other hints are provided for extensions that use ** the SQLite parser and code generator but substitute their own storage ** engine. */ @@ -15440,13 +15823,17 @@ SQLITE_PRIVATE void wx_sqlite3BtreeCursorHint(BtCursor*, int, ...); #endif SQLITE_PRIVATE int wx_sqlite3BtreeCloseCursor(BtCursor*); -SQLITE_PRIVATE int wx_sqlite3BtreeMovetoUnpacked( +SQLITE_PRIVATE int wx_sqlite3BtreeTableMoveto( BtCursor*, - UnpackedRecord *pUnKey, i64 intKey, int bias, int *pRes ); +SQLITE_PRIVATE int wx_sqlite3BtreeIndexMoveto( + BtCursor*, + UnpackedRecord *pUnKey, + int *pRes +); SQLITE_PRIVATE int wx_sqlite3BtreeCursorHasMoved(BtCursor*); SQLITE_PRIVATE int wx_sqlite3BtreeCursorRestore(BtCursor*, int*); SQLITE_PRIVATE int wx_sqlite3BtreeDelete(BtCursor*, u8 flags); @@ -15518,7 +15905,15 @@ SQLITE_PRIVATE const void *wx_sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); SQLITE_PRIVATE u32 wx_sqlite3BtreePayloadSize(BtCursor*); SQLITE_PRIVATE wx_sqlite3_int64 wx_sqlite3BtreeMaxRecordSize(BtCursor*); -SQLITE_PRIVATE char *wx_sqlite3BtreeIntegrityCheck(wx_sqlite3*,Btree*,Pgno*aRoot,int nRoot,int,int*); +SQLITE_PRIVATE int wx_sqlite3BtreeIntegrityCheck( + wx_sqlite3 *db, /* Database connection that is running the check */ + Btree *p, /* The btree to be checked */ + Pgno *aRoot, /* An array of root pages numbers for individual trees */ + int nRoot, /* Number of entries in aRoot[] */ + int mxErr, /* Stop reporting errors after this many */ + int *pnErr, /* OUT: Write number of errors seen to this variable */ + char **pzOut /* OUT: Write the error message string here */ +); SQLITE_PRIVATE struct Pager *wx_sqlite3BtreePager(Btree*); SQLITE_PRIVATE i64 wx_sqlite3BtreeRowCountEst(BtCursor*); @@ -15557,6 +15952,8 @@ SQLITE_PRIVATE int wx_sqlite3BtreeCheckpoint(Btree*, int, int *, int *); SQLITE_PRIVATE int wx_sqlite3BtreeTransferRow(BtCursor*, BtCursor*, i64); +SQLITE_PRIVATE void wx_sqlite3BtreeClearCache(Btree*); + /* ** If we are not using shared cache, then there is no need to ** use mutexes to access the BtShared structures. So make the @@ -15669,19 +16066,18 @@ struct VdbeOp { #ifdef SQLITE_ENABLE_CURSOR_HINTS Expr *pExpr; /* Used when p4type is P4_EXPR */ #endif - int (*xAdvance)(BtCursor *, int); } p4; #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS char *zComment; /* Comment to improve readability */ #endif -#ifdef VDBE_PROFILE - u32 cnt; /* Number of times this instruction was executed */ - u64 cycles; /* Total time spent executing this instruction */ -#endif #ifdef SQLITE_VDBE_COVERAGE u32 iSrcLine; /* Source-code line that generated this opcode ** with flags in the upper 8 bits */ #endif +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) + u64 nExec; + u64 nCycle; +#endif }; typedef struct VdbeOp VdbeOp; @@ -15720,21 +16116,19 @@ typedef struct VdbeOpList VdbeOpList; #define P4_COLLSEQ (-2) /* P4 is a pointer to a CollSeq structure */ #define P4_INT32 (-3) /* P4 is a 32-bit signed integer */ #define P4_SUBPROGRAM (-4) /* P4 is a pointer to a SubProgram structure */ -#define P4_ADVANCE (-5) /* P4 is a pointer to BtreeNext() or BtreePrev() */ -#define P4_TABLE (-6) /* P4 is a pointer to a Table structure */ +#define P4_TABLE (-5) /* P4 is a pointer to a Table structure */ /* Above do not own any resources. Must free those below */ -#define P4_FREE_IF_LE (-7) -#define P4_DYNAMIC (-7) /* Pointer to memory from sqliteMalloc() */ -#define P4_FUNCDEF (-8) /* P4 is a pointer to a FuncDef structure */ -#define P4_KEYINFO (-9) /* P4 is a pointer to a KeyInfo structure */ -#define P4_EXPR (-10) /* P4 is a pointer to an Expr tree */ -#define P4_MEM (-11) /* P4 is a pointer to a Mem* structure */ -#define P4_VTAB (-12) /* P4 is a pointer to an wx_sqlite3_vtab structure */ -#define P4_REAL (-13) /* P4 is a 64-bit floating point value */ -#define P4_INT64 (-14) /* P4 is a 64-bit signed integer */ -#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ -#define P4_FUNCCTX (-16) /* P4 is a pointer to an wx_sqlite3_context object */ -#define P4_DYNBLOB (-17) /* Pointer to memory from sqliteMalloc() */ +#define P4_FREE_IF_LE (-6) +#define P4_DYNAMIC (-6) /* Pointer to memory from sqliteMalloc() */ +#define P4_FUNCDEF (-7) /* P4 is a pointer to a FuncDef structure */ +#define P4_KEYINFO (-8) /* P4 is a pointer to a KeyInfo structure */ +#define P4_EXPR (-9) /* P4 is a pointer to an Expr tree */ +#define P4_MEM (-10) /* P4 is a pointer to a Mem* structure */ +#define P4_VTAB (-11) /* P4 is a pointer to an wx_sqlite3_vtab structure */ +#define P4_REAL (-12) /* P4 is a 64-bit floating point value */ +#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ +#define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ +#define P4_FUNCCTX (-15) /* P4 is a pointer to an wx_sqlite3_context object */ /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 @@ -15779,53 +16173,53 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Savepoint 0 #define OP_AutoCommit 1 #define OP_Transaction 2 -#define OP_SorterNext 3 /* jump */ -#define OP_Prev 4 /* jump */ -#define OP_Next 5 /* jump */ -#define OP_Checkpoint 6 -#define OP_JournalMode 7 -#define OP_Vacuum 8 -#define OP_VFilter 9 /* jump, synopsis: iplan=r[P3] zplan='P4' */ -#define OP_VUpdate 10 /* synopsis: data=r[P3@P2] */ -#define OP_Goto 11 /* jump */ -#define OP_Gosub 12 /* jump */ -#define OP_InitCoroutine 13 /* jump */ -#define OP_Yield 14 /* jump */ -#define OP_MustBeInt 15 /* jump */ -#define OP_Jump 16 /* jump */ -#define OP_Once 17 /* jump */ -#define OP_If 18 /* jump */ +#define OP_Checkpoint 3 +#define OP_JournalMode 4 +#define OP_Vacuum 5 +#define OP_VFilter 6 /* jump, synopsis: iplan=r[P3] zplan='P4' */ +#define OP_VUpdate 7 /* synopsis: data=r[P3@P2] */ +#define OP_Init 8 /* jump, synopsis: Start at P2 */ +#define OP_Goto 9 /* jump */ +#define OP_Gosub 10 /* jump */ +#define OP_InitCoroutine 11 /* jump */ +#define OP_Yield 12 /* jump */ +#define OP_MustBeInt 13 /* jump */ +#define OP_Jump 14 /* jump */ +#define OP_Once 15 /* jump */ +#define OP_If 16 /* jump */ +#define OP_IfNot 17 /* jump */ +#define OP_IsType 18 /* jump, synopsis: if typeof(P1.P3) in P5 goto P2 */ #define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */ -#define OP_IfNot 20 /* jump */ -#define OP_IfNullRow 21 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */ -#define OP_SeekLT 22 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekLE 23 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekGE 24 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekGT 25 /* jump, synopsis: key=r[P3@P4] */ -#define OP_IfNotOpen 26 /* jump, synopsis: if( !csr[P1] ) goto P2 */ -#define OP_IfNoHope 27 /* jump, synopsis: key=r[P3@P4] */ -#define OP_NoConflict 28 /* jump, synopsis: key=r[P3@P4] */ -#define OP_NotFound 29 /* jump, synopsis: key=r[P3@P4] */ -#define OP_Found 30 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekRowid 31 /* jump, synopsis: intkey=r[P3] */ -#define OP_NotExists 32 /* jump, synopsis: intkey=r[P3] */ -#define OP_Last 33 /* jump */ -#define OP_IfSmaller 34 /* jump */ -#define OP_SorterSort 35 /* jump */ -#define OP_Sort 36 /* jump */ -#define OP_Rewind 37 /* jump */ -#define OP_IdxLE 38 /* jump, synopsis: key=r[P3@P4] */ -#define OP_IdxGT 39 /* jump, synopsis: key=r[P3@P4] */ -#define OP_IdxLT 40 /* jump, synopsis: key=r[P3@P4] */ -#define OP_IdxGE 41 /* jump, synopsis: key=r[P3@P4] */ -#define OP_RowSetRead 42 /* jump, synopsis: r[P3]=rowset(P1) */ +#define OP_IfNullRow 20 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */ +#define OP_SeekLT 21 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekLE 22 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekGE 23 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekGT 24 /* jump, synopsis: key=r[P3@P4] */ +#define OP_IfNotOpen 25 /* jump, synopsis: if( !csr[P1] ) goto P2 */ +#define OP_IfNoHope 26 /* jump, synopsis: key=r[P3@P4] */ +#define OP_NoConflict 27 /* jump, synopsis: key=r[P3@P4] */ +#define OP_NotFound 28 /* jump, synopsis: key=r[P3@P4] */ +#define OP_Found 29 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekRowid 30 /* jump, synopsis: intkey=r[P3] */ +#define OP_NotExists 31 /* jump, synopsis: intkey=r[P3] */ +#define OP_Last 32 /* jump */ +#define OP_IfSmaller 33 /* jump */ +#define OP_SorterSort 34 /* jump */ +#define OP_Sort 35 /* jump */ +#define OP_Rewind 36 /* jump */ +#define OP_SorterNext 37 /* jump */ +#define OP_Prev 38 /* jump */ +#define OP_Next 39 /* jump */ +#define OP_IdxLE 40 /* jump, synopsis: key=r[P3@P4] */ +#define OP_IdxGT 41 /* jump, synopsis: key=r[P3@P4] */ +#define OP_IdxLT 42 /* jump, synopsis: key=r[P3@P4] */ #define OP_Or 43 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */ #define OP_And 44 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */ -#define OP_RowSetTest 45 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ -#define OP_Program 46 /* jump */ -#define OP_FkIfZero 47 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ -#define OP_IfPos 48 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ -#define OP_IfNotZero 49 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ +#define OP_IdxGE 45 /* jump, synopsis: key=r[P3@P4] */ +#define OP_RowSetRead 46 /* jump, synopsis: r[P3]=rowset(P1) */ +#define OP_RowSetTest 47 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ +#define OP_Program 48 /* jump */ +#define OP_FkIfZero 49 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ #define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ #define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ #define OP_Ne 52 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */ @@ -15834,50 +16228,50 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Le 55 /* jump, same as TK_LE, synopsis: IF r[P3]<=r[P1] */ #define OP_Lt 56 /* jump, same as TK_LT, synopsis: IF r[P3]=r[P1] */ -#define OP_ElseNotEq 58 /* jump, same as TK_ESCAPE */ -#define OP_DecrJumpZero 59 /* jump, synopsis: if (--r[P1])==0 goto P2 */ -#define OP_IncrVacuum 60 /* jump */ -#define OP_VNext 61 /* jump */ -#define OP_Init 62 /* jump, synopsis: Start at P2 */ -#define OP_PureFunc 63 /* synopsis: r[P3]=func(r[P2@NP]) */ -#define OP_Function 64 /* synopsis: r[P3]=func(r[P2@NP]) */ -#define OP_Return 65 -#define OP_EndCoroutine 66 -#define OP_HaltIfNull 67 /* synopsis: if r[P3]=null halt */ -#define OP_Halt 68 -#define OP_Integer 69 /* synopsis: r[P2]=P1 */ -#define OP_Int64 70 /* synopsis: r[P2]=P4 */ -#define OP_String 71 /* synopsis: r[P2]='P4' (len=P1) */ -#define OP_Null 72 /* synopsis: r[P2..P3]=NULL */ -#define OP_SoftNull 73 /* synopsis: r[P1]=NULL */ -#define OP_Blob 74 /* synopsis: r[P2]=P4 (len=P1) */ -#define OP_Variable 75 /* synopsis: r[P2]=parameter(P1,P4) */ -#define OP_Move 76 /* synopsis: r[P2@P3]=r[P1@P3] */ -#define OP_Copy 77 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ -#define OP_SCopy 78 /* synopsis: r[P2]=r[P1] */ -#define OP_IntCopy 79 /* synopsis: r[P2]=r[P1] */ -#define OP_ChngCntRow 80 /* synopsis: output=r[P1] */ -#define OP_ResultRow 81 /* synopsis: output=r[P1@P2] */ -#define OP_CollSeq 82 -#define OP_AddImm 83 /* synopsis: r[P1]=r[P1]+P2 */ -#define OP_RealAffinity 84 -#define OP_Cast 85 /* synopsis: affinity(r[P1]) */ -#define OP_Permutation 86 -#define OP_Compare 87 /* synopsis: r[P1@P3] <-> r[P2@P3] */ -#define OP_IsTrue 88 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ -#define OP_Offset 89 /* synopsis: r[P3] = sqlite_offset(P1) */ -#define OP_Column 90 /* synopsis: r[P3]=PX */ -#define OP_Affinity 91 /* synopsis: affinity(r[P1@P2]) */ -#define OP_MakeRecord 92 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ -#define OP_Count 93 /* synopsis: r[P2]=count() */ -#define OP_ReadCookie 94 -#define OP_SetCookie 95 -#define OP_ReopenIdx 96 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenRead 97 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenWrite 98 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenDup 99 -#define OP_OpenAutoindex 100 /* synopsis: nColumn=P2 */ -#define OP_OpenEphemeral 101 /* synopsis: nColumn=P2 */ +#define OP_ElseEq 58 /* jump, same as TK_ESCAPE */ +#define OP_IfPos 59 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ +#define OP_IfNotZero 60 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ +#define OP_DecrJumpZero 61 /* jump, synopsis: if (--r[P1])==0 goto P2 */ +#define OP_IncrVacuum 62 /* jump */ +#define OP_VNext 63 /* jump */ +#define OP_Filter 64 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */ +#define OP_PureFunc 65 /* synopsis: r[P3]=func(r[P2@NP]) */ +#define OP_Function 66 /* synopsis: r[P3]=func(r[P2@NP]) */ +#define OP_Return 67 +#define OP_EndCoroutine 68 +#define OP_HaltIfNull 69 /* synopsis: if r[P3]=null halt */ +#define OP_Halt 70 +#define OP_Integer 71 /* synopsis: r[P2]=P1 */ +#define OP_Int64 72 /* synopsis: r[P2]=P4 */ +#define OP_String 73 /* synopsis: r[P2]='P4' (len=P1) */ +#define OP_BeginSubrtn 74 /* synopsis: r[P2]=NULL */ +#define OP_Null 75 /* synopsis: r[P2..P3]=NULL */ +#define OP_SoftNull 76 /* synopsis: r[P1]=NULL */ +#define OP_Blob 77 /* synopsis: r[P2]=P4 (len=P1) */ +#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1,P4) */ +#define OP_Move 79 /* synopsis: r[P2@P3]=r[P1@P3] */ +#define OP_Copy 80 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ +#define OP_SCopy 81 /* synopsis: r[P2]=r[P1] */ +#define OP_IntCopy 82 /* synopsis: r[P2]=r[P1] */ +#define OP_FkCheck 83 +#define OP_ResultRow 84 /* synopsis: output=r[P1@P2] */ +#define OP_CollSeq 85 +#define OP_AddImm 86 /* synopsis: r[P1]=r[P1]+P2 */ +#define OP_RealAffinity 87 +#define OP_Cast 88 /* synopsis: affinity(r[P1]) */ +#define OP_Permutation 89 +#define OP_Compare 90 /* synopsis: r[P1@P3] <-> r[P2@P3] */ +#define OP_IsTrue 91 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ +#define OP_ZeroOrNull 92 /* synopsis: r[P2] = 0 OR NULL */ +#define OP_Offset 93 /* synopsis: r[P3] = sqlite_offset(P1) */ +#define OP_Column 94 /* synopsis: r[P3]=PX cursor P1 column P2 */ +#define OP_TypeCheck 95 /* synopsis: typecheck(r[P1@P2]) */ +#define OP_Affinity 96 /* synopsis: affinity(r[P1@P2]) */ +#define OP_MakeRecord 97 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ +#define OP_Count 98 /* synopsis: r[P2]=count() */ +#define OP_ReadCookie 99 +#define OP_SetCookie 100 +#define OP_ReopenIdx 101 /* synopsis: root=P2 iDb=P3 */ #define OP_BitAnd 102 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ #define OP_BitOr 103 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ #define OP_ShiftLeft 104 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ -#define OP_AggInverse 156 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ -#define OP_AggStep 157 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggStep1 158 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggValue 159 /* synopsis: r[P3]=value N=P2 */ -#define OP_AggFinal 160 /* synopsis: accum=r[P1] N=P2 */ -#define OP_Expire 161 -#define OP_CursorLock 162 -#define OP_CursorUnlock 163 -#define OP_TableLock 164 /* synopsis: iDb=P1 root=P2 write=P3 */ -#define OP_VBegin 165 -#define OP_VCreate 166 -#define OP_VDestroy 167 -#define OP_VOpen 168 -#define OP_VColumn 169 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VRename 170 -#define OP_Pagecount 171 -#define OP_MaxPgcnt 172 -#define OP_Trace 173 -#define OP_CursorHint 174 -#define OP_ReleaseReg 175 /* synopsis: release r[P1@P2] mask P3 */ -#define OP_Noop 176 -#define OP_Explain 177 -#define OP_Abortable 178 +#define OP_OpenRead 112 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenWrite 113 /* synopsis: root=P2 iDb=P3 */ +#define OP_BitNot 114 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ +#define OP_OpenDup 115 +#define OP_OpenAutoindex 116 /* synopsis: nColumn=P2 */ +#define OP_String8 117 /* same as TK_STRING, synopsis: r[P2]='P4' */ +#define OP_OpenEphemeral 118 /* synopsis: nColumn=P2 */ +#define OP_SorterOpen 119 +#define OP_SequenceTest 120 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */ +#define OP_OpenPseudo 121 /* synopsis: P3 columns in r[P2] */ +#define OP_Close 122 +#define OP_ColumnsUsed 123 +#define OP_SeekScan 124 /* synopsis: Scan-ahead up to P1 rows */ +#define OP_SeekHit 125 /* synopsis: set P2<=seekHit<=P3 */ +#define OP_Sequence 126 /* synopsis: r[P2]=cursor[P1].ctr++ */ +#define OP_NewRowid 127 /* synopsis: r[P2]=rowid */ +#define OP_Insert 128 /* synopsis: intkey=r[P3] data=r[P2] */ +#define OP_RowCell 129 +#define OP_Delete 130 +#define OP_ResetCount 131 +#define OP_SorterCompare 132 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ +#define OP_SorterData 133 /* synopsis: r[P2]=data */ +#define OP_RowData 134 /* synopsis: r[P2]=data */ +#define OP_Rowid 135 /* synopsis: r[P2]=PX rowid of P1 */ +#define OP_NullRow 136 +#define OP_SeekEnd 137 +#define OP_IdxInsert 138 /* synopsis: key=r[P2] */ +#define OP_SorterInsert 139 /* synopsis: key=r[P2] */ +#define OP_IdxDelete 140 /* synopsis: key=r[P2@P3] */ +#define OP_DeferredSeek 141 /* synopsis: Move P3 to P1.rowid if needed */ +#define OP_IdxRowid 142 /* synopsis: r[P2]=rowid */ +#define OP_FinishSeek 143 +#define OP_Destroy 144 +#define OP_Clear 145 +#define OP_ResetSorter 146 +#define OP_CreateBtree 147 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ +#define OP_SqlExec 148 +#define OP_ParseSchema 149 +#define OP_LoadAnalysis 150 +#define OP_DropTable 151 +#define OP_DropIndex 152 +#define OP_Real 153 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ +#define OP_DropTrigger 154 +#define OP_IntegrityCk 155 +#define OP_RowSetAdd 156 /* synopsis: rowset(P1)=r[P2] */ +#define OP_Param 157 +#define OP_FkCounter 158 /* synopsis: fkctr[P1]+=P2 */ +#define OP_MemMax 159 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +#define OP_OffsetLimit 160 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ +#define OP_AggInverse 161 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ +#define OP_AggStep 162 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggStep1 163 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggValue 164 /* synopsis: r[P3]=value N=P2 */ +#define OP_AggFinal 165 /* synopsis: accum=r[P1] N=P2 */ +#define OP_Expire 166 +#define OP_CursorLock 167 +#define OP_CursorUnlock 168 +#define OP_TableLock 169 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 170 +#define OP_VCreate 171 +#define OP_VDestroy 172 +#define OP_VOpen 173 +#define OP_VInitIn 174 /* synopsis: r[P2]=ValueList(P1,P3) */ +#define OP_VColumn 175 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VRename 176 +#define OP_Pagecount 177 +#define OP_MaxPgcnt 178 +#define OP_ClrSubtype 179 /* synopsis: r[P1].subtype = 0 */ +#define OP_FilterAdd 180 /* synopsis: filter(P1) += key(P3@P4) */ +#define OP_Trace 181 +#define OP_CursorHint 182 +#define OP_ReleaseReg 183 /* synopsis: release r[P1@P2] mask P3 */ +#define OP_Noop 184 +#define OP_Explain 185 +#define OP_Abortable 186 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c @@ -15966,38 +16368,40 @@ typedef struct VdbeOpList VdbeOpList; #define OPFLG_IN3 0x08 /* in3: P3 is an input */ #define OPFLG_OUT2 0x10 /* out2: P2 is an output */ #define OPFLG_OUT3 0x20 /* out3: P3 is an output */ +#define OPFLG_NCYCLE 0x40 /* ncycle:Cycles count against P1 */ #define OPFLG_INITIALIZER {\ -/* 0 */ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x10,\ -/* 8 */ 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03,\ -/* 16 */ 0x01, 0x01, 0x03, 0x12, 0x03, 0x01, 0x09, 0x09,\ -/* 24 */ 0x09, 0x09, 0x01, 0x09, 0x09, 0x09, 0x09, 0x09,\ -/* 32 */ 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\ -/* 40 */ 0x01, 0x01, 0x23, 0x26, 0x26, 0x0b, 0x01, 0x01,\ -/* 48 */ 0x03, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ -/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00,\ -/* 64 */ 0x00, 0x02, 0x02, 0x08, 0x00, 0x10, 0x10, 0x10,\ -/* 72 */ 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,\ -/* 80 */ 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\ -/* 88 */ 0x12, 0x20, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,\ -/* 96 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x26,\ +/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x41, 0x00,\ +/* 8 */ 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x01, 0x01,\ +/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x49, 0x49, 0x49,\ +/* 24 */ 0x49, 0x01, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,\ +/* 32 */ 0x41, 0x01, 0x01, 0x01, 0x41, 0x01, 0x41, 0x41,\ +/* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\ +/* 48 */ 0x01, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ +/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01, 0x41,\ +/* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\ +/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10, 0x00,\ +/* 80 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02,\ +/* 88 */ 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x40, 0x00,\ +/* 96 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x40, 0x26, 0x26,\ /* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\ -/* 112 */ 0x00, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\ -/* 120 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 128 */ 0x00, 0x00, 0x10, 0x00, 0x00, 0x04, 0x04, 0x00,\ -/* 136 */ 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00,\ -/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10,\ -/* 152 */ 0x10, 0x00, 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00,\ -/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 168 */ 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00,\ -/* 176 */ 0x00, 0x00, 0x00,} - -/* The wx_sqlite3P2Values() routine is able to run faster if it knows +/* 112 */ 0x40, 0x00, 0x12, 0x40, 0x40, 0x10, 0x40, 0x00,\ +/* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\ +/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,\ +/* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\ +/* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ +/* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ +/* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x50, 0x40,\ +/* 176 */ 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,\ +/* 184 */ 0x00, 0x00, 0x00,} + +/* The resolve3P2Values() routine is able to run faster if it knows ** the value of the largest JUMP opcode. The smaller the maximum ** JUMP opcode the better, so the mkopcodeh.tcl script that ** generated this include file strives to group all JUMP opcodes ** together near the beginning of the list. */ -#define SQLITE_MX_JUMP_OPCODE 62 /* Maximum JUMP opcode */ +#define SQLITE_MX_JUMP_OPCODE 64 /* Maximum JUMP opcode */ /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ @@ -16035,19 +16439,27 @@ SQLITE_PRIVATE void wx_sqlite3VdbeVerifyNoResultRow(Vdbe *p); #endif #if defined(SQLITE_DEBUG) SQLITE_PRIVATE void wx_sqlite3VdbeVerifyAbortable(Vdbe *p, int); +SQLITE_PRIVATE void wx_sqlite3VdbeNoJumpsOutsideSubrtn(Vdbe*,int,int,int); #else # define wx_sqlite3VdbeVerifyAbortable(A,B) +# define wx_sqlite3VdbeNoJumpsOutsideSubrtn(A,B,C,D) #endif SQLITE_PRIVATE VdbeOp *wx_sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno); #ifndef SQLITE_OMIT_EXPLAIN -SQLITE_PRIVATE void wx_sqlite3VdbeExplain(Parse*,u8,const char*,...); +SQLITE_PRIVATE int wx_sqlite3VdbeExplain(Parse*,u8,const char*,...); SQLITE_PRIVATE void wx_sqlite3VdbeExplainPop(Parse*); SQLITE_PRIVATE int wx_sqlite3VdbeExplainParent(Parse*); # define ExplainQueryPlan(P) wx_sqlite3VdbeExplain P +# ifdef SQLITE_ENABLE_STMT_SCANSTATUS +# define ExplainQueryPlan2(V,P) (V = wx_sqlite3VdbeExplain P) +# else +# define ExplainQueryPlan2(V,P) ExplainQueryPlan(P) +# endif # define ExplainQueryPlanPop(P) wx_sqlite3VdbeExplainPop(P) # define ExplainQueryPlanParent(P) wx_sqlite3VdbeExplainParent(P) #else # define ExplainQueryPlan(P) +# define ExplainQueryPlan2(V,P) # define ExplainQueryPlanPop(P) # define ExplainQueryPlanParent(P) 0 # define wx_sqlite3ExplainBreakpoint(A,B) /*no-op*/ @@ -16063,6 +16475,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); SQLITE_PRIVATE void wx_sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); SQLITE_PRIVATE void wx_sqlite3VdbeChangeP3(Vdbe*, int addr, int P3); SQLITE_PRIVATE void wx_sqlite3VdbeChangeP5(Vdbe*, u16 P5); +SQLITE_PRIVATE void wx_sqlite3VdbeTypeofColumn(Vdbe*, int); SQLITE_PRIVATE void wx_sqlite3VdbeJumpHere(Vdbe*, int addr); SQLITE_PRIVATE void wx_sqlite3VdbeJumpHereOrPopInst(Vdbe*, int addr); SQLITE_PRIVATE int wx_sqlite3VdbeChangeToNoop(Vdbe*, int addr); @@ -16077,11 +16490,11 @@ SQLITE_PRIVATE void wx_sqlite3VdbeAppendP4(Vdbe*, void *pP4, int p4type); SQLITE_PRIVATE void wx_sqlite3VdbeSetP4KeyInfo(Parse*, Index*); SQLITE_PRIVATE void wx_sqlite3VdbeUsesBtree(Vdbe*, int); SQLITE_PRIVATE VdbeOp *wx_sqlite3VdbeGetOp(Vdbe*, int); +SQLITE_PRIVATE VdbeOp *wx_sqlite3VdbeGetLastOp(Vdbe*); SQLITE_PRIVATE int wx_sqlite3VdbeMakeLabel(Parse*); SQLITE_PRIVATE void wx_sqlite3VdbeRunOnlyOnce(Vdbe*); SQLITE_PRIVATE void wx_sqlite3VdbeReusable(Vdbe*); SQLITE_PRIVATE void wx_sqlite3VdbeDelete(Vdbe*); -SQLITE_PRIVATE void wx_sqlite3VdbeClearObject(wx_sqlite3*,Vdbe*); SQLITE_PRIVATE void wx_sqlite3VdbeMakeReady(Vdbe*,Parse*); SQLITE_PRIVATE int wx_sqlite3VdbeFinalize(Vdbe*); SQLITE_PRIVATE void wx_sqlite3VdbeResolveLabel(Vdbe*, int); @@ -16219,8 +16632,12 @@ SQLITE_PRIVATE void wx_sqlite3VdbeSetLineNumber(Vdbe*,int); #ifdef SQLITE_ENABLE_STMT_SCANSTATUS SQLITE_PRIVATE void wx_sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*); +SQLITE_PRIVATE void wx_sqlite3VdbeScanStatusRange(Vdbe*, int, int, int); +SQLITE_PRIVATE void wx_sqlite3VdbeScanStatusCounters(Vdbe*, int, int, int); #else -# define wx_sqlite3VdbeScanStatus(a,b,c,d,e) +# define wx_sqlite3VdbeScanStatus(a,b,c,d,e,f) +# define wx_sqlite3VdbeScanStatusRange(a,b,c,d) +# define wx_sqlite3VdbeScanStatusCounters(a,b,c,d) #endif #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) @@ -16275,7 +16692,7 @@ struct PgHdr { ** private to pcache.c and should not be accessed by other modules. ** pCache is grouped with the public elements for efficiency. */ - i16 nRef; /* Number of users of this page */ + i64 nRef; /* Number of users of this page */ PgHdr *pDirtyNext; /* Next element in list of dirty pages */ PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ /* NB: pDirtyNext and pDirtyPrev are undefined if the @@ -16356,12 +16773,12 @@ SQLITE_PRIVATE void wx_sqlite3PcacheClearSyncFlags(PCache *); SQLITE_PRIVATE void wx_sqlite3PcacheClear(PCache*); /* Return the total number of outstanding page references */ -SQLITE_PRIVATE int wx_sqlite3PcacheRefCount(PCache*); +SQLITE_PRIVATE i64 wx_sqlite3PcacheRefCount(PCache*); /* Increment the reference count of an existing page */ SQLITE_PRIVATE void wx_sqlite3PcacheRef(PgHdr*); -SQLITE_PRIVATE int wx_sqlite3PcachePageRefcount(PgHdr*); +SQLITE_PRIVATE i64 wx_sqlite3PcachePageRefcount(PgHdr*); /* Return the total number of pages stored in the cache */ SQLITE_PRIVATE int wx_sqlite3PcachePagecount(PCache*); @@ -16426,284 +16843,6 @@ SQLITE_PRIVATE int wx_sqlite3PCacheIsDirty(PCache *pCache); /************** End of pcache.h **********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ -/************** Include os.h in the middle of sqliteInt.h ********************/ -/************** Begin file os.h **********************************************/ -/* -** 2001 September 16 -** -** 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 header file (together with is companion C source-code file -** "os.c") attempt to abstract the underlying operating system so that -** the SQLite library will work on both POSIX and windows systems. -** -** This header file is #include-ed by sqliteInt.h and thus ends up -** being included by every source file. -*/ -#ifndef _SQLITE_OS_H_ -#define _SQLITE_OS_H_ - -/* -** Attempt to automatically detect the operating system and setup the -** necessary pre-processor macros for it. -*/ -/************** Include os_setup.h in the middle of os.h *********************/ -/************** Begin file os_setup.h ****************************************/ -/* -** 2013 November 25 -** -** 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 pre-processor directives related to operating system -** detection and/or setup. -*/ -#ifndef SQLITE_OS_SETUP_H -#define SQLITE_OS_SETUP_H - -/* -** Figure out if we are dealing with Unix, Windows, or some other operating -** system. -** -** After the following block of preprocess macros, all of SQLITE_OS_UNIX, -** SQLITE_OS_WIN, and SQLITE_OS_OTHER will defined to either 1 or 0. One of -** the three will be 1. The other two will be 0. -*/ -#if defined(SQLITE_OS_OTHER) -# if SQLITE_OS_OTHER==1 -# undef SQLITE_OS_UNIX -# define SQLITE_OS_UNIX 0 -# undef SQLITE_OS_WIN -# define SQLITE_OS_WIN 0 -# else -# undef SQLITE_OS_OTHER -# endif -#endif -#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER) -# define SQLITE_OS_OTHER 0 -# ifndef SQLITE_OS_WIN -# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \ - defined(__MINGW32__) || defined(__BORLANDC__) -# define SQLITE_OS_WIN 1 -# define SQLITE_OS_UNIX 0 -# else -# define SQLITE_OS_WIN 0 -# define SQLITE_OS_UNIX 1 -# endif -# else -# define SQLITE_OS_UNIX 0 -# endif -#else -# ifndef SQLITE_OS_WIN -# define SQLITE_OS_WIN 0 -# endif -#endif - -#endif /* SQLITE_OS_SETUP_H */ - -/************** End of os_setup.h ********************************************/ -/************** Continuing where we left off in os.h *************************/ - -/* If the SET_FULLSYNC macro is not defined above, then make it -** a no-op -*/ -#ifndef SET_FULLSYNC -# define SET_FULLSYNC(x,y) -#endif - -/* -** The default size of a disk sector -*/ -#ifndef SQLITE_DEFAULT_SECTOR_SIZE -# define SQLITE_DEFAULT_SECTOR_SIZE 4096 -#endif - -/* -** Temporary files are named starting with this prefix followed by 16 random -** alphanumeric characters, and no file extension. They are stored in the -** OS's standard temporary file directory, and are deleted prior to exit. -** If sqlite is being embedded in another program, you may wish to change the -** prefix to reflect your program's name, so that if your program exits -** prematurely, old temporary files can be easily identified. This can be done -** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. -** -** 2006-10-31: The default prefix used to be "sqlite_". But then -** Mcafee started using SQLite in their anti-virus product and it -** started putting files with the "sqlite" name in the c:/temp folder. -** This annoyed many windows users. Those users would then do a -** Google search for "sqlite", find the telephone numbers of the -** developers and call to wake them up at night and complain. -** For this reason, the default name prefix is changed to be "sqlite" -** spelled backwards. So the temp files are still identified, but -** anybody smart enough to figure out the code is also likely smart -** enough to know that calling the developer will not help get rid -** of the file. -*/ -#ifndef SQLITE_TEMP_FILE_PREFIX -# define SQLITE_TEMP_FILE_PREFIX "etilqs_" -#endif - -/* -** The following values may be passed as the second argument to -** wx_sqlite3OsLock(). The various locks exhibit the following semantics: -** -** SHARED: Any number of processes may hold a SHARED lock simultaneously. -** RESERVED: A single process may hold a RESERVED lock on a file at -** any time. Other processes may hold and obtain new SHARED locks. -** PENDING: A single process may hold a PENDING lock on a file at -** any one time. Existing SHARED locks may persist, but no new -** SHARED locks may be obtained by other processes. -** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. -** -** PENDING_LOCK may not be passed directly to wx_sqlite3OsLock(). Instead, a -** process that requests an EXCLUSIVE lock may actually obtain a PENDING -** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to -** wx_sqlite3OsLock(). -*/ -#define NO_LOCK 0 -#define SHARED_LOCK 1 -#define RESERVED_LOCK 2 -#define PENDING_LOCK 3 -#define EXCLUSIVE_LOCK 4 - -/* -** File Locking Notes: (Mostly about windows but also some info for Unix) -** -** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because -** those functions are not available. So we use only LockFile() and -** UnlockFile(). -** -** LockFile() prevents not just writing but also reading by other processes. -** A SHARED_LOCK is obtained by locking a single randomly-chosen -** byte out of a specific range of bytes. The lock byte is obtained at -** random so two separate readers can probably access the file at the -** same time, unless they are unlucky and choose the same lock byte. -** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. -** There can only be one writer. A RESERVED_LOCK is obtained by locking -** a single byte of the file that is designated as the reserved lock byte. -** A PENDING_LOCK is obtained by locking a designated byte different from -** the RESERVED_LOCK byte. -** -** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, -** which means we can use reader/writer locks. When reader/writer locks -** are used, the lock is placed on the same range of bytes that is used -** for probabilistic locking in Win95/98/ME. Hence, the locking scheme -** will support two or more Win95 readers or two or more WinNT readers. -** But a single Win95 reader will lock out all WinNT readers and a single -** WinNT reader will lock out all other Win95 readers. -** -** The following #defines specify the range of bytes used for locking. -** SHARED_SIZE is the number of bytes available in the pool from which -** a random byte is selected for a shared lock. The pool of bytes for -** shared locks begins at SHARED_FIRST. -** -** The same locking strategy and -** byte ranges are used for Unix. This leaves open the possibility of having -** clients on win95, winNT, and unix all talking to the same shared file -** and all locking correctly. To do so would require that samba (or whatever -** tool is being used for file sharing) implements locks correctly between -** windows and unix. I'm guessing that isn't likely to happen, but by -** using the same locking range we are at least open to the possibility. -** -** Locking in windows is manditory. For this reason, we cannot store -** actual data in the bytes used for locking. The pager never allocates -** the pages involved in locking therefore. SHARED_SIZE is selected so -** that all locks will fit on a single page even at the minimum page size. -** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE -** is set high so that we don't have to allocate an unused page except -** for very large databases. But one should test the page skipping logic -** by setting PENDING_BYTE low and running the entire regression suite. -** -** Changing the value of PENDING_BYTE results in a subtly incompatible -** file format. Depending on how it is changed, you might not notice -** the incompatibility right away, even running a full regression test. -** The default location of PENDING_BYTE is the first byte past the -** 1GB boundary. -** -*/ -#ifdef SQLITE_OMIT_WSD -# define PENDING_BYTE (0x40000000) -#else -# define PENDING_BYTE wx_sqlite3PendingByte -#endif -#define RESERVED_BYTE (PENDING_BYTE+1) -#define SHARED_FIRST (PENDING_BYTE+2) -#define SHARED_SIZE 510 - -/* -** Wrapper around OS specific wx_sqlite3_os_init() function. -*/ -SQLITE_PRIVATE int wx_sqlite3OsInit(void); - -/* -** Functions for accessing wx_sqlite3_file methods -*/ -SQLITE_PRIVATE void wx_sqlite3OsClose(wx_sqlite3_file*); -SQLITE_PRIVATE int wx_sqlite3OsRead(wx_sqlite3_file*, void*, int amt, i64 offset); -SQLITE_PRIVATE int wx_sqlite3OsWrite(wx_sqlite3_file*, const void*, int amt, i64 offset); -SQLITE_PRIVATE int wx_sqlite3OsTruncate(wx_sqlite3_file*, i64 size); -SQLITE_PRIVATE int wx_sqlite3OsSync(wx_sqlite3_file*, int); -SQLITE_PRIVATE int wx_sqlite3OsFileSize(wx_sqlite3_file*, i64 *pSize); -SQLITE_PRIVATE int wx_sqlite3OsLock(wx_sqlite3_file*, int); -SQLITE_PRIVATE int wx_sqlite3OsUnlock(wx_sqlite3_file*, int); -SQLITE_PRIVATE int wx_sqlite3OsCheckReservedLock(wx_sqlite3_file *id, int *pResOut); -SQLITE_PRIVATE int wx_sqlite3OsFileControl(wx_sqlite3_file*,int,void*); -SQLITE_PRIVATE void wx_sqlite3OsFileControlHint(wx_sqlite3_file*,int,void*); -#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 -SQLITE_PRIVATE int wx_sqlite3OsSectorSize(wx_sqlite3_file *id); -SQLITE_PRIVATE int wx_sqlite3OsDeviceCharacteristics(wx_sqlite3_file *id); -#ifndef SQLITE_OMIT_WAL -SQLITE_PRIVATE int wx_sqlite3OsShmMap(wx_sqlite3_file *,int,int,int,void volatile **); -SQLITE_PRIVATE int wx_sqlite3OsShmLock(wx_sqlite3_file *id, int, int, int); -SQLITE_PRIVATE void wx_sqlite3OsShmBarrier(wx_sqlite3_file *id); -SQLITE_PRIVATE int wx_sqlite3OsShmUnmap(wx_sqlite3_file *id, int); -#endif /* SQLITE_OMIT_WAL */ -SQLITE_PRIVATE int wx_sqlite3OsFetch(wx_sqlite3_file *id, i64, int, void **); -SQLITE_PRIVATE int wx_sqlite3OsUnfetch(wx_sqlite3_file *, i64, void *); - - -/* -** Functions for accessing wx_sqlite3_vfs methods -*/ -SQLITE_PRIVATE int wx_sqlite3OsOpen(wx_sqlite3_vfs *, const char *, wx_sqlite3_file*, int, int *); -SQLITE_PRIVATE int wx_sqlite3OsDelete(wx_sqlite3_vfs *, const char *, int); -SQLITE_PRIVATE int wx_sqlite3OsAccess(wx_sqlite3_vfs *, const char *, int, int *pResOut); -SQLITE_PRIVATE int wx_sqlite3OsFullPathname(wx_sqlite3_vfs *, const char *, int, char *); -#ifndef SQLITE_OMIT_LOAD_EXTENSION -SQLITE_PRIVATE void *wx_sqlite3OsDlOpen(wx_sqlite3_vfs *, const char *); -SQLITE_PRIVATE void wx_sqlite3OsDlError(wx_sqlite3_vfs *, int, char *); -SQLITE_PRIVATE void (*wx_sqlite3OsDlSym(wx_sqlite3_vfs *, void *, const char *))(void); -SQLITE_PRIVATE void wx_sqlite3OsDlClose(wx_sqlite3_vfs *, void *); -#endif /* SQLITE_OMIT_LOAD_EXTENSION */ -SQLITE_PRIVATE int wx_sqlite3OsRandomness(wx_sqlite3_vfs *, int, char *); -SQLITE_PRIVATE int wx_sqlite3OsSleep(wx_sqlite3_vfs *, int); -SQLITE_PRIVATE int wx_sqlite3OsGetLastError(wx_sqlite3_vfs*); -SQLITE_PRIVATE int wx_sqlite3OsCurrentTimeInt64(wx_sqlite3_vfs *, wx_sqlite3_int64*); - -/* -** Convenience functions for opening and closing files using -** wx_sqlite3_malloc() to obtain space for the file-handle structure. -*/ -SQLITE_PRIVATE int wx_sqlite3OsOpenMalloc(wx_sqlite3_vfs *, const char *, wx_sqlite3_file **, int,int*); -SQLITE_PRIVATE void wx_sqlite3OsCloseFree(wx_sqlite3_file *); - -#endif /* _SQLITE_OS_H_ */ - -/************** End of os.h **************************************************/ -/************** Continuing where we left off in sqliteInt.h ******************/ /************** Include mutex.h in the middle of sqliteInt.h *****************/ /************** Begin file mutex.h *******************************************/ /* @@ -16949,6 +17088,7 @@ struct Lookaside { #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ void *pStart; /* First byte of available memory space */ void *pEnd; /* First byte past end of available space */ + void *pTrueEnd; /* True value of pEnd, when db->pnBytesFreed!=0 */ }; struct LookasideSlot { LookasideSlot *pNext; /* Next buffer in the list of free buffers */ @@ -17052,6 +17192,7 @@ struct wx_sqlite3 { u32 nSchemaLock; /* Do not reset the schema when non-zero */ unsigned int openFlags; /* Flags passed to wx_sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ + int errByteOffset; /* Byte offset of error in SQL statement */ int errMask; /* & result codes with this before returning */ int iSysErrno; /* Errno value from last system error */ u32 dbOptFlags; /* Flags to enable/disable optimizations */ @@ -17068,10 +17209,10 @@ struct wx_sqlite3 { u8 mTrace; /* zero or more SQLITE_TRACE flags */ u8 noSharedCache; /* True if no shared-cache backends */ u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */ + u8 eOpenState; /* Current condition of the connection */ int nextPagesize; /* Pagesize after VACUUM if >0 */ - u32 magic; /* Magic number for detect library misuse */ - int nChange; /* Value returned by wx_sqlite3_changes() */ - int nTotalChange; /* Value returned by wx_sqlite3_total_changes() */ + i64 nChange; /* Value returned by wx_sqlite3_changes() */ + i64 nTotalChange; /* Value returned by wx_sqlite3_total_changes() */ int aLimit[SQLITE_N_LIMIT]; /* Limits */ int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ struct wx_sqlite3InitInfo { /* Information used during initialization */ @@ -17081,10 +17222,7 @@ struct wx_sqlite3 { unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ unsigned imposterTable : 1; /* Building an imposter table */ unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ - unsigned bDropColumn : 1; /* Doing schema check after DROP COLUMN */ - char **azInit; /* "type", "name", and "tbl_name" columns */ - /* or if bDropColumn, then azInit[0] is the */ - /* name of the column being dropped */ + const char **azInit; /* "type", "name", and "tbl_name" columns */ } init; int nVdbeActive; /* Number of VDBEs currently running */ int nVdbeRead; /* Number of active VDBEs that read or write */ @@ -17094,10 +17232,10 @@ struct wx_sqlite3 { int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ union { - void (*xLegacy)(void*,const char*); /* Legacy trace function */ - int (*xV2)(u32,void*,void*,void*); /* V2 Trace function */ + void (*xLegacy)(void*,const char*); /* mTrace==SQLITE_TRACE_LEGACY */ + int (*xV2)(u32,void*,void*,void*); /* All other mTrace values */ } trace; - void *pTraceArg; /* Argument to the trace function */ + void *pTraceArg; /* Argument to the trace function */ #ifndef SQLITE_OMIT_DEPRECATED void (*xProfile)(void*,const char*,u64); /* Profiling function */ void *pProfileArg; /* Argument to profile function */ @@ -17108,6 +17246,9 @@ struct wx_sqlite3 { void (*xRollbackCallback)(void*); /* Invoked at every commit. */ void *pUpdateArg; void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); + void *pAutovacPagesArg; /* Client argument to autovac_pages */ + void (*xAutovacDestr)(void*); /* Destructor for pAutovacPAgesArg */ + unsigned int (*xAutovacPages)(void*,const char*,u32,u32,u32); Parse *pParse; /* Current parse */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK void *pPreUpdateArg; /* First argument to xPreUpdateCallback */ @@ -17237,6 +17378,7 @@ struct wx_sqlite3 { #define SQLITE_CountRows HI(0x00001) /* Count rows changed by INSERT, */ /* DELETE, or UPDATE and return */ /* the count using a callback. */ +#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */ /* Flags used only if debugging */ #ifdef SQLITE_DEBUG @@ -17282,7 +17424,17 @@ struct wx_sqlite3 { #define SQLITE_SkipScan 0x00004000 /* Skip-scans */ #define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */ #define SQLITE_MinMaxOpt 0x00010000 /* The min/max optimization */ -#define SQLITE_ExistsToIN 0x00020000 /* The EXISTS-to-IN optimization */ +#define SQLITE_SeekScan 0x00020000 /* The OP_SeekScan optimization */ +#define SQLITE_OmitOrderBy 0x00040000 /* Omit pointless ORDER BY */ + /* TH3 expects this value ^^^^^^^^^^ to be 0x40000. Coordinate any change */ +#define SQLITE_BloomFilter 0x00080000 /* Use a Bloom filter on searches */ +#define SQLITE_BloomPulldown 0x00100000 /* Run Bloom filters early */ +#define SQLITE_BalancedMerge 0x00200000 /* Balance multi-way merges */ +#define SQLITE_ReleaseReg 0x00400000 /* Use OP_ReleaseReg for testing */ +#define SQLITE_FlttnUnionAll 0x00800000 /* Disable the UNION ALL flattener */ + /* TH3 expects this value ^^^^^^^^^^ See flatten04.test */ +#define SQLITE_IndexedExpr 0x01000000 /* Pull exprs from index when able */ +#define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */ #define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* @@ -17297,17 +17449,16 @@ struct wx_sqlite3 { */ #define ConstFactorOk(P) ((P)->okConstFactor) -/* -** Possible values for the sqlite.magic field. -** The numbers are obtained at random and have no special meaning, other -** than being distinct from one another. +/* Possible values for the wx_sqlite3.eOpenState field. +** The numbers are randomly selected such that a minimum of three bits must +** change to convert any number to another or to zero */ -#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ -#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ -#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ -#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ -#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ -#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */ +#define SQLITE_STATE_OPEN 0x76 /* Database is open */ +#define SQLITE_STATE_CLOSED 0xce /* Database is closed */ +#define SQLITE_STATE_SICK 0xba /* Error and awaiting close */ +#define SQLITE_STATE_BUSY 0x6d /* Database currently in use */ +#define SQLITE_STATE_ERROR 0xd5 /* An SQLITE_MISUSE error occurred */ +#define SQLITE_STATE_ZOMBIE 0xa7 /* Close with last statement close */ /* ** Each SQL function is defined by an instance of the following @@ -17332,7 +17483,7 @@ struct FuncDef { union { FuncDef *pHash; /* Next with a different name but the same hash */ FuncDestructor *pDestructor; /* Reference counted destructor function */ - } u; + } u; /* pHash if SQLITE_FUNC_BUILTIN, pDestructor otherwise */ }; /* @@ -17362,13 +17513,20 @@ struct FuncDestructor { ** are assert() statements in the code to verify this. ** ** Value constraints (enforced via assert()): -** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg -** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG -** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG -** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API -** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API -** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS +** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg +** SQLITE_FUNC_ANYORDER == NC_OrderAgg == SF_OrderByReqd +** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG +** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG +** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API +** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API +** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS -- opposite meanings!!! ** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API +** +** Note that even though SQLITE_FUNC_UNSAFE and SQLITE_INNOCUOUS have the +** same bit value, their meanings are inverted. SQLITE_FUNC_UNSAFE is +** used internally and if set means tha the function has side effects. +** SQLITE_INNOCUOUS is used by application code and means "not unsafe". +** See multiple instances of tag-20230109-1. */ #define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */ #define SQLITE_FUNC_LIKE 0x0004 /* Candidate for the LIKE optimization */ @@ -17385,13 +17543,15 @@ struct FuncDestructor { #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a ** single query - might change over time */ #define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */ -#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */ +/* 0x8000 -- available for reuse */ #define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ #define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ #define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */ #define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */ #define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */ #define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */ +#define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */ +#define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */ /* Identifier numbers for each in-line function */ #define INLINEFUNC_coalesce 0 @@ -17400,6 +17560,7 @@ struct FuncDestructor { #define INLINEFUNC_expr_compare 3 #define INLINEFUNC_affinity 4 #define INLINEFUNC_iif 5 +#define INLINEFUNC_sqlite_offset 6 #define INLINEFUNC_unlikely 99 /* Default case */ /* @@ -17454,7 +17615,7 @@ struct FuncDestructor { ** are interpreted in the same way as the first 4 parameters to ** FUNCTION(). ** -** WFUNCTION(zName, nArg, iArg, xStep, xFinal, xValue, xInverse) +** WAGGREGATE(zName, nArg, iArg, xStep, xFinal, xValue, xInverse) ** Used to create an aggregate function definition implemented by ** the C functions xStep and xFinal. The first four parameters ** are interpreted in the same way as the first 4 parameters to @@ -17469,44 +17630,55 @@ struct FuncDestructor { ** parameter. */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define MFUNCTION(zName, nArg, xPtr, xFunc) \ - {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ xPtr, 0, xFunc, 0, 0, 0, #zName, {0} } +#define JFUNCTION(zName, nArg, iArg, xFunc) \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|\ + SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define INLINE_FUNC(zName, nArg, iArg, mFlags) \ - {nArg, SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} } #define TEST_FUNC(zName, nArg, iArg, mFlags) \ - {nArg, SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \ SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} } #define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \ 0, 0, xFunc, 0, 0, 0, #zName, {0} } #define PURE_DATE(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ (void*)&wx_sqlite3Config, 0, xFunc, 0, 0, 0, #zName, {0} } #define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ - {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ - {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ pArg, 0, xFunc, 0, 0, 0, #zName, } #define LIKEFUNC(zName, nArg, arg, flags) \ - {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ (void *)arg, 0, likeFunc, 0, 0, 0, #zName, {0} } #define WAGGREGATE(zName, nArg, arg, nc, xStep, xFinal, xValue, xInverse, f) \ - {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,xInverse,#zName, {0}} #define INTERNAL_FUNCTION(zName, nArg, xFunc) \ - {nArg, SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ + {nArg, SQLITE_FUNC_BUILTIN|\ + SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ 0, 0, xFunc, 0, 0, 0, #zName, {0} } @@ -17562,18 +17734,42 @@ struct Module { ** or equal to the table column index. It is ** equal if and only if there are no VIRTUAL ** columns to the left. +** +** Notes on zCnName: +** The zCnName field stores the name of the column, the datatype of the +** column, and the collating sequence for the column, in that order, all in +** a single allocation. Each string is 0x00 terminated. The datatype +** is only included if the COLFLAG_HASTYPE bit of colFlags is set and the +** collating sequence name is only included if the COLFLAG_HASCOLL bit is +** set. */ struct Column { - char *zName; /* Name of this column, \000, then the type */ - Expr *pDflt; /* Default value or GENERATED ALWAYS AS value */ - char *zColl; /* Collating sequence. If NULL, use the default */ - u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ - char affinity; /* One of the SQLITE_AFF_... values */ - u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */ - u8 hName; /* Column name hash for faster lookup */ - u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ + char *zCnName; /* Name of this column */ + unsigned notNull :4; /* An OE_ code for handling a NOT NULL constraint */ + unsigned eCType :4; /* One of the standard types */ + char affinity; /* One of the SQLITE_AFF_... values */ + u8 szEst; /* Est size of value in this column. sizeof(INT)==1 */ + u8 hName; /* Column name hash for faster lookup */ + u16 iDflt; /* 1-based index of DEFAULT. 0 means "none" */ + u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ }; +/* Allowed values for Column.eCType. +** +** Values must match entries in the global constant arrays +** wx_sqlite3StdTypeLen[] and wx_sqlite3StdType[]. Each value is one more +** than the offset into these arrays for the corresponding name. +** Adjust the SQLITE_N_STDTYPE value if adding or removing entries. +*/ +#define COLTYPE_CUSTOM 0 /* Type appended to zName */ +#define COLTYPE_ANY 1 +#define COLTYPE_BLOB 2 +#define COLTYPE_INT 3 +#define COLTYPE_INTEGER 4 +#define COLTYPE_REAL 5 +#define COLTYPE_TEXT 6 +#define SQLITE_N_STDTYPE 6 /* Number of standard types */ + /* Allowed values for Column.colFlags. ** ** Constraints: @@ -17590,6 +17786,8 @@ struct Column { #define COLFLAG_STORED 0x0040 /* GENERATED ALWAYS AS ... STORED */ #define COLFLAG_NOTAVAIL 0x0080 /* STORED column not yet calculated */ #define COLFLAG_BUSY 0x0100 /* Blocks recursion on GENERATED columns */ +#define COLFLAG_HASCOLL 0x0200 /* Has collating sequence name in zCnName */ +#define COLFLAG_NOEXPAND 0x0400 /* Omit this column when expanding "*" */ #define COLFLAG_GENERATED 0x0060 /* Combo: _STORED, _VIRTUAL */ #define COLFLAG_NOINSERT 0x0062 /* Combo: _HIDDEN, _STORED, _VIRTUAL */ @@ -17637,6 +17835,7 @@ struct CollSeq { #define SQLITE_AFF_NUMERIC 0x43 /* 'C' */ #define SQLITE_AFF_INTEGER 0x44 /* 'D' */ #define SQLITE_AFF_REAL 0x45 /* 'E' */ +#define SQLITE_AFF_FLEXNUM 0x46 /* 'F' */ #define wx_sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC) @@ -17655,9 +17854,7 @@ struct CollSeq { ** operator is NULL. It is added to certain comparison operators to ** prove that the operands are always NOT NULL. */ -#define SQLITE_KEEPNULL 0x08 /* Used by vector == or <> */ #define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */ -#define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */ #define SQLITE_NULLEQ 0x80 /* NULL=NULL */ #define SQLITE_NOTNULL 0x90 /* Assert that operands are never NULL */ @@ -17721,15 +17918,13 @@ struct VTable { #define SQLITE_VTABRISK_High 2 /* -** The schema for each SQL table and view is represented in memory -** by an instance of the following structure. +** The schema for each SQL table, virtual table, and view is represented +** in memory by an instance of the following structure. */ struct Table { char *zName; /* Name of the table or view */ Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ - Select *pSelect; /* NULL for tables. Points to definition if a view. */ - FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ ExprList *pCheck; /* All CHECK constraints */ /* ... also used as column name list in a VIEW */ @@ -17745,15 +17940,24 @@ struct Table { LogEst costMult; /* Cost multiplier for using this table */ #endif u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ -#ifndef SQLITE_OMIT_ALTERTABLE - int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ -#endif -#ifndef SQLITE_OMIT_VIRTUALTABLE - int nModuleArg; /* Number of arguments to the module */ - char **azModuleArg; /* 0: module 1: schema 2: vtab name 3...: args */ - VTable *pVTable; /* List of VTable objects. */ -#endif - Trigger *pTrigger; /* List of triggers stored in pSchema */ + u8 eTabType; /* 0: normal, 1: virtual, 2: view */ + union { + struct { /* Used by ordinary tables: */ + int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ + FKey *pFKey; /* Linked list of all foreign keys in this table */ + ExprList *pDfltList; /* DEFAULT clauses on various columns. + ** Or the AS clause for generated columns. */ + } tab; + struct { /* Used by views: */ + Select *pSelect; /* View definition */ + } view; + struct { /* Used by virtual tables only: */ + int nArg; /* Number of arguments to the module */ + char **azArg; /* 0: module 1: schema 2: vtab name 3...: args */ + VTable *p; /* List of VTable objects. */ + } vtab; + } u; + Trigger *pTrigger; /* List of triggers on this object */ Schema *pSchema; /* Schema that contains this table */ }; @@ -17772,23 +17976,35 @@ struct Table { ** TF_HasStored == COLFLAG_STORED ** TF_HasHidden == COLFLAG_HIDDEN */ -#define TF_Readonly 0x0001 /* Read-only system table */ -#define TF_HasHidden 0x0002 /* Has one or more hidden columns */ -#define TF_HasPrimaryKey 0x0004 /* Table has a primary key */ -#define TF_Autoincrement 0x0008 /* Integer primary key is autoincrement */ -#define TF_HasStat1 0x0010 /* nRowLogEst set from sqlite_stat1 */ -#define TF_HasVirtual 0x0020 /* Has one or more VIRTUAL columns */ -#define TF_HasStored 0x0040 /* Has one or more STORED columns */ -#define TF_HasGenerated 0x0060 /* Combo: HasVirtual + HasStored */ -#define TF_WithoutRowid 0x0080 /* No rowid. PRIMARY KEY is the key */ -#define TF_StatsUsed 0x0100 /* Query planner decisions affected by +#define TF_Readonly 0x00000001 /* Read-only system table */ +#define TF_HasHidden 0x00000002 /* Has one or more hidden columns */ +#define TF_HasPrimaryKey 0x00000004 /* Table has a primary key */ +#define TF_Autoincrement 0x00000008 /* Integer primary key is autoincrement */ +#define TF_HasStat1 0x00000010 /* nRowLogEst set from sqlite_stat1 */ +#define TF_HasVirtual 0x00000020 /* Has one or more VIRTUAL columns */ +#define TF_HasStored 0x00000040 /* Has one or more STORED columns */ +#define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */ +#define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */ +#define TF_StatsUsed 0x00000100 /* Query planner decisions affected by ** Index.aiRowLogEst[] values */ -#define TF_NoVisibleRowid 0x0200 /* No user-visible "rowid" column */ -#define TF_OOOHidden 0x0400 /* Out-of-Order hidden columns */ -#define TF_HasNotNull 0x0800 /* Contains NOT NULL constraints */ -#define TF_Shadow 0x1000 /* True for a shadow table */ -#define TF_HasStat4 0x2000 /* STAT4 info available for this table */ -#define TF_Ephemeral 0x4000 /* An ephemeral table */ +#define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */ +#define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ +#define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ +#define TF_Shadow 0x00001000 /* True for a shadow table */ +#define TF_HasStat4 0x00002000 /* STAT4 info available for this table */ +#define TF_Ephemeral 0x00004000 /* An ephemeral table */ +#define TF_Eponymous 0x00008000 /* An eponymous virtual table */ +#define TF_Strict 0x00010000 /* STRICT mode */ + +/* +** Allowed values for Table.eTabType +*/ +#define TABTYP_NORM 0 /* Ordinary table */ +#define TABTYP_VTAB 1 /* Virtual table */ +#define TABTYP_VIEW 2 /* A view */ + +#define IsView(X) ((X)->eTabType==TABTYP_VIEW) +#define IsOrdinaryTable(X) ((X)->eTabType==TABTYP_NORM) /* ** Test to see whether or not a table is a virtual table. This is @@ -17796,9 +18012,9 @@ struct Table { ** table support is omitted from the build. */ #ifndef SQLITE_OMIT_VIRTUALTABLE -# define IsVirtual(X) ((X)->nModuleArg) +# define IsVirtual(X) ((X)->eTabType==TABTYP_VTAB) # define ExprIsVtab(X) \ - ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->nModuleArg) + ((X)->op==TK_COLUMN && (X)->y.pTab->eTabType==TABTYP_VTAB) #else # define IsVirtual(X) 0 # define ExprIsVtab(X) 0 @@ -17979,6 +18195,11 @@ struct KeyInfo { struct UnpackedRecord { KeyInfo *pKeyInfo; /* Collation and sort-order information */ Mem *aMem; /* Values */ + union { + char *z; /* Cache of aMem[0].z for vdbeRecordCompareString() */ + i64 i; /* Cache of aMem[0].u.i for vdbeRecordCompareInt() */ + } u; + int n; /* Cache of aMem[0].n used by vdbeRecordCompareString() */ u16 nField; /* Number of entries in apMem[] */ i8 default_rc; /* Comparison result if keys are equal */ u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */ @@ -18010,10 +18231,22 @@ struct UnpackedRecord { ** The Index.onError field determines whether or not the indexed columns ** must be unique and what to do if they are not. When Index.onError=OE_None, ** it means this is not a unique index. Otherwise it is a unique index -** and the value of Index.onError indicate the which conflict resolution -** algorithm to employ whenever an attempt is made to insert a non-unique +** and the value of Index.onError indicates which conflict resolution +** algorithm to employ when an attempt is made to insert a non-unique ** element. ** +** The colNotIdxed bitmask is used in combination with SrcItem.colUsed +** for a fast test to see if an index can serve as a covering index. +** colNotIdxed has a 1 bit for every column of the original table that +** is *not* available in the index. Thus the expression +** "colUsed & colNotIdxed" will be non-zero if the index is not a +** covering index. The most significant bit of of colNotIdxed will always +** be true (note-20221022-a). If a column beyond the 63rd column of the +** table is used, the "colUsed & colNotIdxed" test will always be non-zero +** and we have to assume either that the index is not covering, or use +** an alternative (slower) algorithm to determine whether or not +** the index is covering. +** ** While parsing a CREATE TABLE or CREATE INDEX statement in order to ** generate VDBE code (as opposed to parsing one read from an sqlite_schema ** table as part of parsing an existing database schema), transient instances @@ -18049,6 +18282,8 @@ struct Index { unsigned bNoQuery:1; /* Do not use this index to optimize queries */ unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */ unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */ + unsigned bHasExpr:1; /* Index contains an expression, either a literal + ** expression, or a reference to a VIRTUAL column */ #ifdef SQLITE_ENABLE_STAT4 int nSample; /* Number of elements in aSample[] */ int nSampleCol; /* Size of IndexSample.anEq[] and so on */ @@ -18057,7 +18292,7 @@ struct Index { tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */ tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */ #endif - Bitmask colNotIdxed; /* 0 for unindexed columns in pTab */ + Bitmask colNotIdxed; /* Unindexed columns in pTab */ }; /* @@ -18132,16 +18367,15 @@ struct AggInfo { ** from source tables rather than from accumulators */ u8 useSortingIdx; /* In direct mode, reference the sorting index rather ** than the source table */ + u16 nSortingColumn; /* Number of columns in the sorting index */ int sortingIdx; /* Cursor number of the sorting index */ int sortingIdxPTab; /* Cursor number of pseudo-table */ - int nSortingColumn; /* Number of columns in the sorting index */ - int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */ + int iFirstReg; /* First register in range for aCol[] and aFunc[] */ ExprList *pGroupBy; /* The group by clause */ struct AggInfo_col { /* For each column used in source tables */ Table *pTab; /* Source table */ Expr *pCExpr; /* The original expression */ int iTable; /* Cursor number of the source table */ - int iMem; /* Memory location that acts as accumulator */ i16 iColumn; /* Column number within the source table */ i16 iSorterColumn; /* Column number in the sorting index */ } *aCol; @@ -18152,13 +18386,27 @@ struct AggInfo { struct AggInfo_func { /* For each aggregate function */ Expr *pFExpr; /* Expression encoding the function */ FuncDef *pFunc; /* The aggregate function implementation */ - int iMem; /* Memory location that acts as accumulator */ int iDistinct; /* Ephemeral table used to enforce DISTINCT */ + int iDistAddr; /* Address of OP_OpenEphemeral */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ u32 selId; /* Select to which this AggInfo belongs */ +#ifdef SQLITE_DEBUG + Select *pSelect; /* SELECT statement that this AggInfo supports */ +#endif }; +/* +** Macros to compute aCol[] and aFunc[] register numbers. +** +** These macros should not be used prior to the call to +** assignAggregateRegisters() that computes the value of pAggInfo->iFirstReg. +** The assert()s that are part of this macro verify that constraint. +*/ +#define AggInfoColumnReg(A,I) (assert((A)->iFirstReg),(A)->iFirstReg+(I)) +#define AggInfoFuncReg(A,I) \ + (assert((A)->iFirstReg),(A)->iFirstReg+(A)->nColumn+(I)) + /* ** The datatype ynVar is a signed integer, either 16-bit or 32-bit. ** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater @@ -18186,10 +18434,10 @@ typedef int ynVar; ** tree. ** ** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, -** or TK_STRING), then Expr.token contains the text of the SQL literal. If -** the expression is a variable (TK_VARIABLE), then Expr.token contains the +** or TK_STRING), then Expr.u.zToken contains the text of the SQL literal. If +** the expression is a variable (TK_VARIABLE), then Expr.u.zToken contains the ** variable name. Finally, if the expression is an SQL function (TK_FUNCTION), -** then Expr.token contains the name of the function. +** then Expr.u.zToken contains the name of the function. ** ** Expr.pRight and Expr.pLeft are the left and right subexpressions of a ** binary operator. Either or both may be NULL. @@ -18229,7 +18477,7 @@ typedef int ynVar; ** help reduce memory requirements, sometimes an Expr object will be ** truncated. And to reduce the number of memory allocations, sometimes ** two or more Expr objects will be stored in a single memory allocation, -** together with Expr.zToken strings. +** together with Expr.u.zToken strings. ** ** If the EP_Reduced and EP_TokenOnly flags are set when ** an Expr object is truncated. When EP_Reduced is set, then all @@ -18285,7 +18533,10 @@ struct Expr { ** TK_VARIABLE: variable number (always >= 1). ** TK_SELECT_COLUMN: column of the result vector */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ - int iRightJoinTable; /* If EP_FromJoin, the right table of the join */ + union { + int iJoin; /* If EP_OuterON or EP_InnerON, the right table */ + int iOfst; /* else: start of token from start of statement */ + } w; AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ union { Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL @@ -18298,36 +18549,35 @@ struct Expr { } y; }; -/* -** The following are the meanings of bits in the Expr.flags field. +/* The following are the meanings of bits in the Expr.flags field. ** Value restrictions: ** ** EP_Agg == NC_HasAgg == SF_HasAgg ** EP_Win == NC_HasWin */ -#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */ -#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */ -#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */ -#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */ +#define EP_OuterON 0x000001 /* Originates in ON/USING clause of outer join */ +#define EP_InnerON 0x000002 /* Originates in ON/USING of an inner join */ +#define EP_Distinct 0x000004 /* Aggregate function with DISTINCT keyword */ +#define EP_HasFunc 0x000008 /* Contains one or more functions of any kind */ #define EP_Agg 0x000010 /* Contains one or more aggregate functions */ -#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */ -#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */ -#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */ -#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */ -#define EP_Commuted 0x000200 /* Comparison operator has been commuted */ -#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */ -#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */ -#define EP_Skip 0x001000 /* Operator does not contribute to affinity */ -#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ -#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ +#define EP_FixedCol 0x000020 /* TK_Column with a known fixed value */ +#define EP_VarSelect 0x000040 /* pSelect is correlated, not constant */ +#define EP_DblQuoted 0x000080 /* token.z was originally in "..." */ +#define EP_InfixFunc 0x000100 /* True for an infix function: LIKE, GLOB, etc */ +#define EP_Collate 0x000200 /* Tree contains a TK_COLLATE operator */ +#define EP_Commuted 0x000400 /* Comparison operator has been commuted */ +#define EP_IntValue 0x000800 /* Integer value contained in u.iValue */ +#define EP_xIsSelect 0x001000 /* x.pSelect is valid (otherwise x.pList is) */ +#define EP_Skip 0x002000 /* Operator does not contribute to affinity */ +#define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ #define EP_Win 0x008000 /* Contains window functions */ -#define EP_MemToken 0x010000 /* Need to wx_sqlite3DbFree() Expr.zToken */ -#define EP_IfNullRow 0x020000 /* The TK_IF_NULL_ROW opcode */ -#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ -#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ -#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */ -#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */ - /* 0x400000 // Available */ +#define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ + /* 0x020000 // Available for reuse */ +#define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */ +#define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */ +#define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ +#define EP_CanBeNull 0x200000 /* Can be null despite NOT NULL constraint */ +#define EP_Subquery 0x400000 /* Tree contains a TK_SELECT operator */ #define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */ #define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ #define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */ @@ -18338,23 +18588,31 @@ struct Expr { #define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ /* 0x80000000 // Available */ -/* -** The EP_Propagate mask is a set of properties that automatically propagate +/* The EP_Propagate mask is a set of properties that automatically propagate ** upwards into parent nodes. */ #define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc) -/* -** These macros can be used to test, set, or clear bits in the +/* Macros can be used to test, set, or clear bits in the ** Expr.flags field. */ #define ExprHasProperty(E,P) (((E)->flags&(P))!=0) #define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P)) #define ExprSetProperty(E,P) (E)->flags|=(P) #define ExprClearProperty(E,P) (E)->flags&=~(P) -#define ExprAlwaysTrue(E) (((E)->flags&(EP_FromJoin|EP_IsTrue))==EP_IsTrue) -#define ExprAlwaysFalse(E) (((E)->flags&(EP_FromJoin|EP_IsFalse))==EP_IsFalse) +#define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue) +#define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse) +/* Macros used to ensure that the correct members of unions are accessed +** in Expr. +*/ +#define ExprUseUToken(E) (((E)->flags&EP_IntValue)==0) +#define ExprUseUValue(E) (((E)->flags&EP_IntValue)!=0) +#define ExprUseXList(E) (((E)->flags&EP_xIsSelect)==0) +#define ExprUseXSelect(E) (((E)->flags&EP_xIsSelect)!=0) +#define ExprUseYTab(E) (((E)->flags&(EP_WinFunc|EP_Subrtn))==0) +#define ExprUseYWin(E) (((E)->flags&EP_WinFunc)!=0) +#define ExprUseYSub(E) (((E)->flags&EP_Subrtn)!=0) /* Flags for use with Expr.vvaFlags */ @@ -18426,21 +18684,29 @@ struct Expr { */ struct ExprList { int nExpr; /* Number of expressions on the list */ + int nAlloc; /* Number of a[] slots allocated */ struct ExprList_item { /* For each expression in the list */ Expr *pExpr; /* The parse tree for this expression */ char *zEName; /* Token associated with this expression */ - u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */ - unsigned eEName :2; /* Meaning of zEName */ - unsigned done :1; /* A flag to indicate when processing is finished */ - unsigned reusable :1; /* Constant expression is reusable */ - unsigned bSorterRef :1; /* Defer evaluation until after sorting */ - unsigned bNulls: 1; /* True if explicit "NULLS FIRST/LAST" */ + struct { + u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */ + unsigned eEName :2; /* Meaning of zEName */ + unsigned done :1; /* Indicates when processing is finished */ + unsigned reusable :1; /* Constant expression is reusable */ + unsigned bSorterRef :1; /* Defer evaluation until after sorting */ + unsigned bNulls :1; /* True if explicit "NULLS FIRST/LAST" */ + unsigned bUsed :1; /* This column used in a SF_NestedFrom subquery */ + unsigned bUsingTerm:1; /* Term from the USING clause of a NestedFrom */ + unsigned bNoExpand: 1; /* Term is an auxiliary in NestedFrom and should + ** not be expanded by "*" in parent queries */ + } fg; union { - struct { + struct { /* Used by any ExprList other than Parse.pConsExpr */ u16 iOrderByCol; /* For ORDER BY, column number in result set */ u16 iAlias; /* Index into Parse.aAlias[] for zName */ } x; - int iConstExprReg; /* Register in which Expr value is cached */ + int iConstExprReg; /* Register in which Expr value is cached. Used only + ** by Parse.pConstExpr */ } u; } a[1]; /* One slot for each expression in the list */ }; @@ -18468,16 +18734,43 @@ struct ExprList { ** If "a" is the k-th column of table "t", then IdList.a[0].idx==k. */ struct IdList { + int nId; /* Number of identifiers on the list */ + u8 eU4; /* Which element of a.u4 is valid */ struct IdList_item { char *zName; /* Name of the identifier */ - int idx; /* Index in some Table.aCol[] of a column named zName */ - } *a; - int nId; /* Number of identifiers on the list */ + union { + int idx; /* Index in some Table.aCol[] of a column named zName */ + Expr *pExpr; /* Expr to implement a USING variable -- NOT USED */ + } u4; + } a[1]; }; +/* +** Allowed values for IdList.eType, which determines which value of the a.u4 +** is valid. +*/ +#define EU4_NONE 0 /* Does not use IdList.a.u4 */ +#define EU4_IDX 1 /* Uses IdList.a.u4.idx */ +#define EU4_EXPR 2 /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */ + /* ** The SrcItem object represents a single term in the FROM clause of a query. ** The SrcList object is mostly an array of SrcItems. +** +** The jointype starts out showing the join type between the current table +** and the next table on the list. The parser builds the list this way. +** But wx_sqlite3SrcListShiftJoinType() later shifts the jointypes so that each +** jointype expresses the join between the table and the previous table. +** +** In the colUsed field, the high-order bit (bit 63) is set if the table +** contains more than 63 columns and the 64-th or later column is used. +** +** Union member validity: +** +** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc +** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy +** u2.pIBIndex fg.isIndexedBy && !fg.isCte +** u2.pCteUse fg.isCte && !fg.isIndexedBy */ struct SrcItem { Schema *pSchema; /* Schema to which this item is fixed */ @@ -18495,43 +18788,48 @@ struct SrcItem { unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ unsigned isTabFunc :1; /* True if table-valued-function syntax */ unsigned isCorrelated :1; /* True if sub-query is correlated */ + unsigned isMaterialized:1; /* This is a materialized view */ unsigned viaCoroutine :1; /* Implemented as a co-routine */ unsigned isRecursive :1; /* True for recursive reference in WITH */ unsigned fromDDL :1; /* Comes from sqlite_schema */ unsigned isCte :1; /* This is a CTE */ + unsigned notCte :1; /* This item may not match a CTE */ + unsigned isUsing :1; /* u3.pUsing is valid */ + unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ + unsigned isSynthUsing :1; /* u3.pUsing is synthensized from NATURAL */ + unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ - Expr *pOn; /* The ON clause of a join */ - IdList *pUsing; /* The USING clause of a join */ - Bitmask colUsed; /* Bit N (1< The ON clause of a join */ + IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ + } u3; + Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */ union { char *zIndexedBy; /* Identifier from "INDEXED BY " clause */ ExprList *pFuncArg; /* Arguments to table-valued-function */ } u1; union { Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ - CteUse *pCteUse; /* CTE Usage info info fg.isCte is true */ + CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */ } u2; }; /* -** The following structure describes the FROM clause of a SELECT statement. -** Each table or subquery in the FROM clause is a separate element of -** the SrcList.a[] array. -** -** With the addition of multiple database support, the following structure -** can also be used to describe a particular table such as the table that -** is modified by an INSERT, DELETE, or UPDATE statement. In standard SQL, -** such a table must be a simple name: ID. But in SQLite, the table can -** now be identified by a database name, a dot, then the table name: ID.ID. -** -** The jointype starts out showing the join type between the current table -** and the next table on the list. The parser builds the list this way. -** But wx_sqlite3SrcListShiftJoinType() later shifts the jointypes so that each -** jointype expresses the join between the table and the previous table. +** The OnOrUsing object represents either an ON clause or a USING clause. +** It can never be both at the same time, but it can be neither. +*/ +struct OnOrUsing { + Expr *pOn; /* The ON clause of a join */ + IdList *pUsing; /* The USING clause of a join */ +}; + +/* +** This object represents one or more tables that are the source of +** content for an SQL statement. For example, a single SrcList object +** is used to hold the FROM clause of a SELECT statement. SrcList also +** represents the target tables for DELETE, INSERT, and UPDATE statements. ** -** In the colUsed field, the high-order bit (bit 63) is set if the table -** contains more than 63 columns and the 64-th or later column is used. */ struct SrcList { int nSrc; /* Number of tables or subqueries in the FROM clause */ @@ -18542,14 +18840,15 @@ struct SrcList { /* ** Permitted values of the SrcList.a.jointype field */ -#define JT_INNER 0x0001 /* Any kind of inner or cross join */ -#define JT_CROSS 0x0002 /* Explicit use of the CROSS keyword */ -#define JT_NATURAL 0x0004 /* True for a "natural" join */ -#define JT_LEFT 0x0008 /* Left outer join */ -#define JT_RIGHT 0x0010 /* Right outer join */ -#define JT_OUTER 0x0020 /* The "OUTER" keyword is present */ -#define JT_ERROR 0x0040 /* unknown or unsupported join type */ - +#define JT_INNER 0x01 /* Any kind of inner or cross join */ +#define JT_CROSS 0x02 /* Explicit use of the CROSS keyword */ +#define JT_NATURAL 0x04 /* True for a "natural" join */ +#define JT_LEFT 0x08 /* Left outer join */ +#define JT_RIGHT 0x10 /* Right outer join */ +#define JT_OUTER 0x20 /* The "OUTER" keyword is present */ +#define JT_LTORJ 0x40 /* One of the LEFT operands of a RIGHT JOIN + ** Mnemonic: Left Table Of Right Join */ +#define JT_ERROR 0x80 /* unknown or unsupported join type */ /* ** Flags appropriate for the wctrlFlags parameter of wx_sqlite3WhereBegin() @@ -18570,9 +18869,9 @@ struct SrcList { #define WHERE_DISTINCTBY 0x0080 /* pOrderby is really a DISTINCT clause */ #define WHERE_WANT_DISTINCT 0x0100 /* All output needs to be distinct */ #define WHERE_SORTBYGROUP 0x0200 /* Support wx_sqlite3WhereIsSorted() */ - /* 0x0400 not currently used */ +#define WHERE_AGG_DISTINCT 0x0400 /* Query is "SELECT agg(DISTINCT ...)" */ #define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */ - /* 0x1000 not currently used */ +#define WHERE_RIGHT_JOIN 0x1000 /* Processing a RIGHT JOIN */ /* 0x2000 not currently used */ #define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */ /* 0x8000 not currently used */ @@ -18616,7 +18915,7 @@ struct NameContext { } uNC; NameContext *pNext; /* Next outer name context. NULL for outermost */ int nRef; /* Number of names resolved by this context */ - int nErr; /* Number of errors encountered while resolving names */ + int nNcErr; /* Number of errors encountered while resolving names */ int ncFlags; /* Zero or more NC_* flags defined below */ Select *pWinSelect; /* SELECT statement for any window functions */ }; @@ -18625,30 +18924,33 @@ struct NameContext { ** Allowed values for the NameContext, ncFlags field. ** ** Value constraints (all checked via assert()): -** NC_HasAgg == SF_HasAgg == EP_Agg -** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX +** NC_HasAgg == SF_HasAgg == EP_Agg +** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX +** NC_OrderAgg == SF_OrderByReqd == SQLITE_FUNC_ANYORDER ** NC_HasWin == EP_Win ** */ -#define NC_AllowAgg 0x00001 /* Aggregate functions are allowed here */ -#define NC_PartIdx 0x00002 /* True if resolving a partial index WHERE */ -#define NC_IsCheck 0x00004 /* True if resolving a CHECK constraint */ -#define NC_GenCol 0x00008 /* True for a GENERATED ALWAYS AS clause */ -#define NC_HasAgg 0x00010 /* One or more aggregate functions seen */ -#define NC_IdxExpr 0x00020 /* True if resolving columns of CREATE INDEX */ -#define NC_SelfRef 0x0002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ -#define NC_VarSelect 0x00040 /* A correlated subquery has been seen */ -#define NC_UEList 0x00080 /* True if uNC.pEList is used */ -#define NC_UAggInfo 0x00100 /* True if uNC.pAggInfo is used */ -#define NC_UUpsert 0x00200 /* True if uNC.pUpsert is used */ -#define NC_UBaseReg 0x00400 /* True if uNC.iBaseReg is used */ -#define NC_MinMaxAgg 0x01000 /* min/max aggregates seen. See note above */ -#define NC_Complex 0x02000 /* True if a function or subquery seen */ -#define NC_AllowWin 0x04000 /* Window functions are allowed here */ -#define NC_HasWin 0x08000 /* One or more window functions seen */ -#define NC_IsDDL 0x10000 /* Resolving names in a CREATE statement */ -#define NC_InAggFunc 0x20000 /* True if analyzing arguments to an agg func */ -#define NC_FromDDL 0x40000 /* SQL text comes from sqlite_schema */ +#define NC_AllowAgg 0x000001 /* Aggregate functions are allowed here */ +#define NC_PartIdx 0x000002 /* True if resolving a partial index WHERE */ +#define NC_IsCheck 0x000004 /* True if resolving a CHECK constraint */ +#define NC_GenCol 0x000008 /* True for a GENERATED ALWAYS AS clause */ +#define NC_HasAgg 0x000010 /* One or more aggregate functions seen */ +#define NC_IdxExpr 0x000020 /* True if resolving columns of CREATE INDEX */ +#define NC_SelfRef 0x00002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ +#define NC_Subquery 0x000040 /* A subquery has been seen */ +#define NC_UEList 0x000080 /* True if uNC.pEList is used */ +#define NC_UAggInfo 0x000100 /* True if uNC.pAggInfo is used */ +#define NC_UUpsert 0x000200 /* True if uNC.pUpsert is used */ +#define NC_UBaseReg 0x000400 /* True if uNC.iBaseReg is used */ +#define NC_MinMaxAgg 0x001000 /* min/max aggregates seen. See note above */ +#define NC_Complex 0x002000 /* True if a function or subquery seen */ +#define NC_AllowWin 0x004000 /* Window functions are allowed here */ +#define NC_HasWin 0x008000 /* One or more window functions seen */ +#define NC_IsDDL 0x010000 /* Resolving names in a CREATE statement */ +#define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */ +#define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */ +#define NC_NoSelect 0x080000 /* Do not descend into sub-selects */ +#define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */ /* ** An instance of the following object describes a single ON CONFLICT @@ -18731,9 +19033,10 @@ struct Select { ** "Select Flag". ** ** Value constraints (all checked via assert()) -** SF_HasAgg == NC_HasAgg -** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX -** SF_FixedLimit == WHERE_USE_LIMIT +** SF_HasAgg == NC_HasAgg +** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX +** SF_OrderByReqd == NC_OrderAgg == SQLITE_FUNC_ANYORDER +** SF_FixedLimit == WHERE_USE_LIMIT */ #define SF_Distinct 0x0000001 /* Output should be DISTINCT */ #define SF_All 0x0000002 /* Includes the ALL keyword */ @@ -18758,9 +19061,15 @@ struct Select { #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ #define SF_View 0x0200000 /* SELECT statement is a view */ #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ -#define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */ +#define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ #define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */ #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ +#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ +#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ +#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ + +/* True if S exists and has SF_NestedFrom */ +#define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0) /* ** The results of a SELECT can be distributed in several ways, as defined @@ -18866,7 +19175,7 @@ struct SelectDest { int iSDParm2; /* A second parameter for the eDest disposal method */ int iSdst; /* Base register where results are written */ int nSdst; /* Number of registers allocated */ - char *zAffSdst; /* Affinity used when eDest==SRT_Set */ + char *zAffSdst; /* Affinity used for SRT_Set */ ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */ }; @@ -18925,11 +19234,34 @@ struct TriggerPrg { #else typedef unsigned int yDbMask; # define DbMaskTest(M,I) (((M)&(((yDbMask)1)<<(I)))!=0) -# define DbMaskZero(M) (M)=0 -# define DbMaskSet(M,I) (M)|=(((yDbMask)1)<<(I)) -# define DbMaskAllZero(M) (M)==0 -# define DbMaskNonZero(M) (M)!=0 +# define DbMaskZero(M) ((M)=0) +# define DbMaskSet(M,I) ((M)|=(((yDbMask)1)<<(I))) +# define DbMaskAllZero(M) ((M)==0) +# define DbMaskNonZero(M) ((M)!=0) +#endif + +/* +** For each index X that has as one of its arguments either an expression +** or the name of a virtual generated column, and if X is in scope such that +** the value of the expression can simply be read from the index, then +** there is an instance of this object on the Parse.pIdxExpr list. +** +** During code generation, while generating code to evaluate expressions, +** this list is consulted and if a matching expression is found, the value +** is read from the index rather than being recomputed. +*/ +struct IndexedExpr { + Expr *pExpr; /* The expression contained in the index */ + int iDataCur; /* The data cursor associated with the index */ + int iIdxCur; /* The index cursor */ + int iIdxCol; /* The index column that contains value of pExpr */ + u8 bMaybeNullRow; /* True if we need an OP_IfNullRow check */ + u8 aff; /* Affinity of the pExpr expression */ + IndexedExpr *pIENext; /* Next in a list of all indexed expressions */ +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + const char *zIdxName; /* Name of index, used only for bytecode comments */ #endif +}; /* ** An instance of the ParseCleanup object specifies an operation that @@ -18972,9 +19304,13 @@ struct Parse { u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */ u8 okConstFactor; /* OK to factor out constants */ u8 disableLookaside; /* Number of times lookaside has been disabled */ - u8 disableVtab; /* Disable all virtual tables for this parse */ + u8 prepFlags; /* SQLITE_PREPARE_* flags */ + u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside wx_sqlite3ParserAddCleanup() */ +#endif +#ifdef SQLITE_DEBUG + u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */ #endif int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ @@ -18988,6 +19324,7 @@ struct Parse { int nLabelAlloc; /* Number of slots in aLabel */ int *aLabel; /* Space to hold the labels */ ExprList *pConstExpr;/* Constant expressions */ + IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */ Token constraintName;/* Name of the constraint currently being parsed */ yDbMask writeMask; /* Start a write transaction on these databases */ yDbMask cookieMask; /* Bitmask of schema verified databases */ @@ -19002,7 +19339,8 @@ struct Parse { AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ - Parse *pParentParse; /* Parent parser if this parser is nested */ + TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ + ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */ union { int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */ Returning *pReturning; /* The RETURNING clause */ @@ -19010,6 +19348,9 @@ struct Parse { u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u32 oldmask; /* Mask of old.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */ +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + u32 nProgressSteps; /* xProgress steps taken during wx_sqlite3_prepare() */ +#endif u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ u8 bReturning; /* Coding a RETURNING trigger */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ @@ -19023,6 +19364,7 @@ struct Parse { **************************************************************************/ int aTempReg[8]; /* Holding area for temporary registers */ + Parse *pOuterParse; /* Outer Parse object when nested */ Token sNameToken; /* Token with unqualified schema object name */ /************************************************************************ @@ -19057,14 +19399,14 @@ struct Parse { Token sArg; /* Complete text of a module argument */ Table **apVtabLock; /* Pointer to virtual tables needing locking */ #endif - TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ With *pWith; /* Current WITH clause, or NULL */ - ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */ #ifndef SQLITE_OMIT_ALTERTABLE RenameToken *pRename; /* Tokens subject to renaming by ALTER TABLE */ #endif }; +/* Allowed values for Parse.eParseMode +*/ #define PARSE_MODE_NORMAL 0 #define PARSE_MODE_DECLARE_VTAB 1 #define PARSE_MODE_RENAME 2 @@ -19073,7 +19415,8 @@ struct Parse { /* ** Sizes and pointers of various parts of the Parse object. */ -#define PARSE_HDR_SZ offsetof(Parse,aTempReg) /* Recursive part w/o aColCache*/ +#define PARSE_HDR(X) (((char*)(X))+offsetof(Parse,zErrMsg)) +#define PARSE_HDR_SZ (offsetof(Parse,aTempReg)-offsetof(Parse,zErrMsg)) /* Recursive part w/o aColCache*/ #define PARSE_RECURSE_SZ offsetof(Parse,sLastToken) /* Recursive part */ #define PARSE_TAIL_SZ (sizeof(Parse)-PARSE_RECURSE_SZ) /* Non-recursive part */ #define PARSE_TAIL(X) (((char*)(X))+PARSE_RECURSE_SZ) /* Pointer to tail */ @@ -19142,20 +19485,20 @@ struct AuthContext { #define OPFLAG_PREFORMAT 0x80 /* OP_Insert uses preformatted cell */ /* - * Each trigger present in the database schema is stored as an instance of - * struct Trigger. - * - * Pointers to instances of struct Trigger are stored in two ways. - * 1. In the "trigHash" hash table (part of the wx_sqlite3* that represents the - * database). This allows Trigger structures to be retrieved by name. - * 2. All triggers associated with a single table form a linked list, using the - * pNext member of struct Trigger. A pointer to the first element of the - * linked list is stored as the "pTrigger" member of the associated - * struct Table. - * - * The "step_list" member points to the first element of a linked list - * containing the SQL statements specified as the trigger program. - */ +** Each trigger present in the database schema is stored as an instance of +** struct Trigger. +** +** Pointers to instances of struct Trigger are stored in two ways. +** 1. In the "trigHash" hash table (part of the wx_sqlite3* that represents the +** database). This allows Trigger structures to be retrieved by name. +** 2. All triggers associated with a single table form a linked list, using the +** pNext member of struct Trigger. A pointer to the first element of the +** linked list is stored as the "pTrigger" member of the associated +** struct Table. +** +** The "step_list" member points to the first element of a linked list +** containing the SQL statements specified as the trigger program. +*/ struct Trigger { char *zName; /* The name of the trigger */ char *table; /* The table or view to which the trigger applies */ @@ -19182,43 +19525,48 @@ struct Trigger { #define TRIGGER_AFTER 2 /* - * An instance of struct TriggerStep is used to store a single SQL statement - * that is a part of a trigger-program. - * - * Instances of struct TriggerStep are stored in a singly linked list (linked - * using the "pNext" member) referenced by the "step_list" member of the - * associated struct Trigger instance. The first element of the linked list is - * the first step of the trigger-program. - * - * The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or - * "SELECT" statement. The meanings of the other members is determined by the - * value of "op" as follows: - * - * (op == TK_INSERT) - * orconf -> stores the ON CONFLICT algorithm - * pSelect -> If this is an INSERT INTO ... SELECT ... statement, then - * this stores a pointer to the SELECT statement. Otherwise NULL. - * zTarget -> Dequoted name of the table to insert into. - * pExprList -> If this is an INSERT INTO ... VALUES ... statement, then - * this stores values to be inserted. Otherwise NULL. - * pIdList -> If this is an INSERT INTO ... () VALUES ... - * statement, then this stores the column-names to be - * inserted into. - * - * (op == TK_DELETE) - * zTarget -> Dequoted name of the table to delete from. - * pWhere -> The WHERE clause of the DELETE statement if one is specified. - * Otherwise NULL. - * - * (op == TK_UPDATE) - * zTarget -> Dequoted name of the table to update. - * pWhere -> The WHERE clause of the UPDATE statement if one is specified. - * Otherwise NULL. - * pExprList -> A list of the columns to update and the expressions to update - * them to. See wx_sqlite3Update() documentation of "pChanges" - * argument. - * - */ +** An instance of struct TriggerStep is used to store a single SQL statement +** that is a part of a trigger-program. +** +** Instances of struct TriggerStep are stored in a singly linked list (linked +** using the "pNext" member) referenced by the "step_list" member of the +** associated struct Trigger instance. The first element of the linked list is +** the first step of the trigger-program. +** +** The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or +** "SELECT" statement. The meanings of the other members is determined by the +** value of "op" as follows: +** +** (op == TK_INSERT) +** orconf -> stores the ON CONFLICT algorithm +** pSelect -> The content to be inserted - either a SELECT statement or +** a VALUES clause. +** zTarget -> Dequoted name of the table to insert into. +** pIdList -> If this is an INSERT INTO ... () VALUES ... +** statement, then this stores the column-names to be +** inserted into. +** pUpsert -> The ON CONFLICT clauses for an Upsert +** +** (op == TK_DELETE) +** zTarget -> Dequoted name of the table to delete from. +** pWhere -> The WHERE clause of the DELETE statement if one is specified. +** Otherwise NULL. +** +** (op == TK_UPDATE) +** zTarget -> Dequoted name of the table to update. +** pWhere -> The WHERE clause of the UPDATE statement if one is specified. +** Otherwise NULL. +** pExprList -> A list of the columns to update and the expressions to update +** them to. See wx_sqlite3Update() documentation of "pChanges" +** argument. +** +** (op == TK_SELECT) +** pSelect -> The SELECT statement +** +** (op == TK_RETURNING) +** pExprList -> The list of expressions that follow the RETURNING keyword. +** +*/ struct TriggerStep { u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT, ** or TK_RETURNING */ @@ -19286,8 +19634,26 @@ typedef struct { /* ** Allowed values for mInitFlags */ +#define INITFLAG_AlterMask 0x0003 /* Types of ALTER */ #define INITFLAG_AlterRename 0x0001 /* Reparse after a RENAME */ #define INITFLAG_AlterDrop 0x0002 /* Reparse after a DROP COLUMN */ +#define INITFLAG_AlterAdd 0x0003 /* Reparse after an ADD COLUMN */ + +/* Tuning parameters are set using SQLITE_TESTCTRL_TUNE and are controlled +** on debug-builds of the CLI using ".testctrl tune ID VALUE". Tuning +** parameters are for temporary use during development, to help find +** optimial values for parameters in the query planner. The should not +** be used on trunk check-ins. They are a temporary mechanism available +** for transient development builds only. +** +** Tuning parameters are numbered starting with 1. +*/ +#define SQLITE_NTUNE 6 /* Should be zero for all trunk check-ins */ +#ifdef SQLITE_DEBUG +# define Tuning(X) (wx_sqlite3Config.aTune[(X)-1]) +#else +# define Tuning(X) 0 +#endif /* ** Structure containing global configuration data for the SQLite library. @@ -19343,16 +19709,21 @@ struct Sqlite3Config { void (*xVdbeBranch)(void*,unsigned iSrcLine,u8 eThis,u8 eMx); /* Callback */ void *pVdbeBranchArg; /* 1st argument */ #endif -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE wx_sqlite3_int64 mxMemdbSize; /* Default max memdb size */ #endif #ifndef SQLITE_UNTESTABLE int (*xTestCallback)(int); /* Invoked by wx_sqlite3FaultSim() */ #endif int bLocaltimeFault; /* True to fail localtime() calls */ + int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */ int iOnceResetThreshold; /* When to reset OP_Once counters */ u32 szSorterRef; /* Min size in bytes to use sorter-refs */ unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */ + /* vvvv--- must be last ---vvv */ +#ifdef SQLITE_DEBUG + wx_sqlite3_int64 aTune[SQLITE_NTUNE]; /* Tuning parameters */ +#endif }; /* @@ -19388,19 +19759,19 @@ struct Walker { int n; /* A counter */ int iCur; /* A cursor number */ SrcList *pSrcList; /* FROM clause */ - struct SrcCount *pSrcCount; /* Counting column references */ struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ + struct RefSrcList *pRefSrcList; /* wx_sqlite3ReferencesSrcList() */ int *aiCol; /* array of column indexes */ struct IdxCover *pIdxCover; /* Check for index coverage */ - struct IdxExprTrans *pIdxTrans; /* Convert idxed expr to column */ ExprList *pGroupBy; /* GROUP BY clause */ Select *pSelect; /* HAVING to WHERE clause ctx */ struct WindowRewrite *pRewrite; /* Window rewrite context */ struct WhereConst *pConst; /* WHERE clause constants */ struct RenameCtx *pRename; /* RENAME COLUMN context */ struct Table *pTab; /* Table of generated column */ + struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */ SrcItem *pSrcItem; /* A single FROM clause item */ - DbFixer *pFix; + DbFixer *pFix; /* See wx_sqlite3FixSelect() */ } u; }; @@ -19430,11 +19801,18 @@ SQLITE_PRIVATE int wx_sqlite3SelectWalkNoop(Walker*, Select*); SQLITE_PRIVATE int wx_sqlite3SelectWalkFail(Walker*, Select*); SQLITE_PRIVATE int wx_sqlite3WalkerDepthIncrease(Walker*,Select*); SQLITE_PRIVATE void wx_sqlite3WalkerDepthDecrease(Walker*,Select*); +SQLITE_PRIVATE void wx_sqlite3WalkWinDefnDummyCallback(Walker*,Select*); #ifdef SQLITE_DEBUG SQLITE_PRIVATE void wx_sqlite3SelectWalkAssert2(Walker*, Select*); #endif +#ifndef SQLITE_OMIT_CTE +SQLITE_PRIVATE void wx_sqlite3SelectPopWith(Walker*, Select*); +#else +# define wx_sqlite3SelectPopWith 0 +#endif + /* ** Return code from the parse-tree walking primitives and their ** callbacks. @@ -19468,6 +19846,7 @@ struct Cte { */ struct With { int nCte; /* Number of CTEs in the WITH clause */ + int bView; /* Belongs to the outermost Select of a view */ With *pOuter; /* Containing WITH clause, or NULL */ Cte a[1]; /* For each CTE in the WITH clause.... */ }; @@ -19542,7 +19921,7 @@ struct Window { Window **ppThis; /* Pointer to this object in Select.pWin list */ Window *pNextWin; /* Next window function belonging to this SELECT */ Expr *pFilter; /* The FILTER expression */ - FuncDef *pFunc; /* The function */ + FuncDef *pWFunc; /* The function */ int iEphCsr; /* Partition buffer or Peer buffer */ int regAccum; /* Accumulator */ int regResult; /* Interim result */ @@ -19566,7 +19945,7 @@ SQLITE_PRIVATE void wx_sqlite3WindowListDelete(wx_sqlite3 *db, Window *p); SQLITE_PRIVATE Window *wx_sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); SQLITE_PRIVATE void wx_sqlite3WindowAttach(Parse*, Expr*, Window*); SQLITE_PRIVATE void wx_sqlite3WindowLink(Select *pSel, Window *pWin); -SQLITE_PRIVATE int wx_sqlite3WindowCompare(Parse*, Window*, Window*, int); +SQLITE_PRIVATE int wx_sqlite3WindowCompare(const Parse*, const Window*, const Window*, int); SQLITE_PRIVATE void wx_sqlite3WindowCodeInit(Parse*, Select*); SQLITE_PRIVATE void wx_sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int); SQLITE_PRIVATE int wx_sqlite3WindowRewrite(Parse*, Select*); @@ -19698,8 +20077,9 @@ SQLITE_PRIVATE void *wx_sqlite3DbReallocOrFree(wx_sqlite3 *, void *, u64); SQLITE_PRIVATE void *wx_sqlite3DbRealloc(wx_sqlite3 *, void *, u64); SQLITE_PRIVATE void wx_sqlite3DbFree(wx_sqlite3*, void*); SQLITE_PRIVATE void wx_sqlite3DbFreeNN(wx_sqlite3*, void*); -SQLITE_PRIVATE int wx_sqlite3MallocSize(void*); -SQLITE_PRIVATE int wx_sqlite3DbMallocSize(wx_sqlite3*, void*); +SQLITE_PRIVATE void wx_sqlite3DbNNFreeNN(wx_sqlite3*, void*); +SQLITE_PRIVATE int wx_sqlite3MallocSize(const void*); +SQLITE_PRIVATE int wx_sqlite3DbMallocSize(wx_sqlite3*, const void*); SQLITE_PRIVATE void *wx_sqlite3PageMalloc(int); SQLITE_PRIVATE void wx_sqlite3PageFree(void*); SQLITE_PRIVATE void wx_sqlite3MemSetDefault(void); @@ -19718,12 +20098,14 @@ SQLITE_PRIVATE int wx_sqlite3HeapNearlyFull(void); */ #ifdef SQLITE_USE_ALLOCA # define wx_sqlite3StackAllocRaw(D,N) alloca(N) -# define wx_sqlite3StackAllocZero(D,N) memset(alloca(N), 0, N) +# define wx_sqlite3StackAllocRawNN(D,N) alloca(N) # define wx_sqlite3StackFree(D,P) +# define wx_sqlite3StackFreeNN(D,P) #else # define wx_sqlite3StackAllocRaw(D,N) wx_sqlite3DbMallocRaw(D,N) -# define wx_sqlite3StackAllocZero(D,N) wx_sqlite3DbMallocZero(D,N) +# define wx_sqlite3StackAllocRawNN(D,N) wx_sqlite3DbMallocRawNN(D,N) # define wx_sqlite3StackFree(D,P) wx_sqlite3DbFree(D,P) +# define wx_sqlite3StackFreeNN(D,P) wx_sqlite3DbFreeNN(D,P) #endif /* Do not allow both MEMSYS5 and MEMSYS3 to be defined together. If they @@ -19797,27 +20179,64 @@ SQLITE_PRIVATE void *wx_sqlite3TestTextToPtr(const char*); #endif #if defined(SQLITE_DEBUG) +SQLITE_PRIVATE void wx_sqlite3TreeViewLine(TreeView*, const char *zFormat, ...); SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView*, const Expr*, u8); SQLITE_PRIVATE void wx_sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*); SQLITE_PRIVATE void wx_sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*); +SQLITE_PRIVATE void wx_sqlite3TreeViewBareIdList(TreeView*, const IdList*, const char*); +SQLITE_PRIVATE void wx_sqlite3TreeViewIdList(TreeView*, const IdList*, u8, const char*); +SQLITE_PRIVATE void wx_sqlite3TreeViewColumnList(TreeView*, const Column*, int, u8); SQLITE_PRIVATE void wx_sqlite3TreeViewSrcList(TreeView*, const SrcList*); SQLITE_PRIVATE void wx_sqlite3TreeViewSelect(TreeView*, const Select*, u8); SQLITE_PRIVATE void wx_sqlite3TreeViewWith(TreeView*, const With*, u8); +SQLITE_PRIVATE void wx_sqlite3TreeViewUpsert(TreeView*, const Upsert*, u8); +#if TREETRACE_ENABLED +SQLITE_PRIVATE void wx_sqlite3TreeViewDelete(const With*, const SrcList*, const Expr*, + const ExprList*,const Expr*, const Trigger*); +SQLITE_PRIVATE void wx_sqlite3TreeViewInsert(const With*, const SrcList*, + const IdList*, const Select*, const ExprList*, + int, const Upsert*, const Trigger*); +SQLITE_PRIVATE void wx_sqlite3TreeViewUpdate(const With*, const SrcList*, const ExprList*, + const Expr*, int, const ExprList*, const Expr*, + const Upsert*, const Trigger*); +#endif +#ifndef SQLITE_OMIT_TRIGGER +SQLITE_PRIVATE void wx_sqlite3TreeViewTriggerStep(TreeView*, const TriggerStep*, u8, u8); +SQLITE_PRIVATE void wx_sqlite3TreeViewTrigger(TreeView*, const Trigger*, u8, u8); +#endif #ifndef SQLITE_OMIT_WINDOWFUNC SQLITE_PRIVATE void wx_sqlite3TreeViewWindow(TreeView*, const Window*, u8); SQLITE_PRIVATE void wx_sqlite3TreeViewWinFunc(TreeView*, const Window*, u8); #endif +SQLITE_PRIVATE void wx_sqlite3ShowExpr(const Expr*); +SQLITE_PRIVATE void wx_sqlite3ShowExprList(const ExprList*); +SQLITE_PRIVATE void wx_sqlite3ShowIdList(const IdList*); +SQLITE_PRIVATE void wx_sqlite3ShowSrcList(const SrcList*); +SQLITE_PRIVATE void wx_sqlite3ShowSelect(const Select*); +SQLITE_PRIVATE void wx_sqlite3ShowWith(const With*); +SQLITE_PRIVATE void wx_sqlite3ShowUpsert(const Upsert*); +#ifndef SQLITE_OMIT_TRIGGER +SQLITE_PRIVATE void wx_sqlite3ShowTriggerStep(const TriggerStep*); +SQLITE_PRIVATE void wx_sqlite3ShowTriggerStepList(const TriggerStep*); +SQLITE_PRIVATE void wx_sqlite3ShowTrigger(const Trigger*); +SQLITE_PRIVATE void wx_sqlite3ShowTriggerList(const Trigger*); +#endif +#ifndef SQLITE_OMIT_WINDOWFUNC +SQLITE_PRIVATE void wx_sqlite3ShowWindow(const Window*); +SQLITE_PRIVATE void wx_sqlite3ShowWinFunc(const Window*); +#endif #endif - SQLITE_PRIVATE void wx_sqlite3SetString(char **, wx_sqlite3*, const char*); +SQLITE_PRIVATE void wx_sqlite3ProgressCheck(Parse*); SQLITE_PRIVATE void wx_sqlite3ErrorMsg(Parse*, const char*, ...); SQLITE_PRIVATE int wx_sqlite3ErrorToParser(wx_sqlite3*,int); SQLITE_PRIVATE void wx_sqlite3Dequote(char*); SQLITE_PRIVATE void wx_sqlite3DequoteExpr(Expr*); +SQLITE_PRIVATE void wx_sqlite3DequoteToken(Token*); SQLITE_PRIVATE void wx_sqlite3TokenInit(Token*,char*); SQLITE_PRIVATE int wx_sqlite3KeywordCode(const unsigned char*, int); -SQLITE_PRIVATE int wx_sqlite3RunParser(Parse*, const char*, char **); +SQLITE_PRIVATE int wx_sqlite3RunParser(Parse*, const char*); SQLITE_PRIVATE void wx_sqlite3FinishCoding(Parse*); SQLITE_PRIVATE int wx_sqlite3GetTempReg(Parse*); SQLITE_PRIVATE void wx_sqlite3ReleaseTempReg(Parse*,int); @@ -19834,16 +20253,17 @@ SQLITE_PRIVATE Expr *wx_sqlite3PExpr(Parse*, int, Expr*, Expr*); SQLITE_PRIVATE void wx_sqlite3PExprAddSelect(Parse*, Expr*, Select*); SQLITE_PRIVATE Expr *wx_sqlite3ExprAnd(Parse*,Expr*, Expr*); SQLITE_PRIVATE Expr *wx_sqlite3ExprSimplifiedAndOr(Expr*); -SQLITE_PRIVATE Expr *wx_sqlite3ExprFunction(Parse*,ExprList*, Token*, int); -SQLITE_PRIVATE void wx_sqlite3ExprFunctionUsable(Parse*,Expr*,FuncDef*); +SQLITE_PRIVATE Expr *wx_sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); +SQLITE_PRIVATE void wx_sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); SQLITE_PRIVATE void wx_sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); SQLITE_PRIVATE void wx_sqlite3ExprDelete(wx_sqlite3*, Expr*); SQLITE_PRIVATE void wx_sqlite3ExprDeferredDelete(Parse*, Expr*); SQLITE_PRIVATE void wx_sqlite3ExprUnmapAndDelete(Parse*, Expr*); SQLITE_PRIVATE ExprList *wx_sqlite3ExprListAppend(Parse*,ExprList*,Expr*); SQLITE_PRIVATE ExprList *wx_sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); +SQLITE_PRIVATE Select *wx_sqlite3ExprListToValues(Parse*, int, ExprList*); SQLITE_PRIVATE void wx_sqlite3ExprListSetSortOrder(ExprList*,int,int); -SQLITE_PRIVATE void wx_sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); +SQLITE_PRIVATE void wx_sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int); SQLITE_PRIVATE void wx_sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); SQLITE_PRIVATE void wx_sqlite3ExprListDelete(wx_sqlite3*, ExprList*); SQLITE_PRIVATE u32 wx_sqlite3ExprListFlags(const ExprList*); @@ -19859,9 +20279,14 @@ SQLITE_PRIVATE void wx_sqlite3ResetAllSchemasOfConnection(wx_sqlite3*); SQLITE_PRIVATE void wx_sqlite3ResetOneSchema(wx_sqlite3*,int); SQLITE_PRIVATE void wx_sqlite3CollapseDatabaseArray(wx_sqlite3*); SQLITE_PRIVATE void wx_sqlite3CommitInternalChanges(wx_sqlite3*); +SQLITE_PRIVATE void wx_sqlite3ColumnSetExpr(Parse*,Table*,Column*,Expr*); +SQLITE_PRIVATE Expr *wx_sqlite3ColumnExpr(Table*,Column*); +SQLITE_PRIVATE void wx_sqlite3ColumnSetColl(wx_sqlite3*,Column*,const char*zColl); +SQLITE_PRIVATE const char *wx_sqlite3ColumnColl(Column*); SQLITE_PRIVATE void wx_sqlite3DeleteColumnNames(wx_sqlite3*,Table*); +SQLITE_PRIVATE void wx_sqlite3GenerateColumnNames(Parse *pParse, Select *pSelect); SQLITE_PRIVATE int wx_sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); -SQLITE_PRIVATE void wx_sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*,char); +SQLITE_PRIVATE void wx_sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char); SQLITE_PRIVATE Table *wx_sqlite3ResultSetOfSelect(Parse*,Select*,char); SQLITE_PRIVATE void wx_sqlite3OpenSchemaTable(Parse *, int); SQLITE_PRIVATE Index *wx_sqlite3PrimaryKeyIndex(Table*); @@ -19879,14 +20304,14 @@ SQLITE_PRIVATE void wx_sqlite3ColumnPropertiesFromName(Table*, Column*); #else # define wx_sqlite3ColumnPropertiesFromName(T,C) /* no-op */ #endif -SQLITE_PRIVATE void wx_sqlite3AddColumn(Parse*,Token*,Token*); +SQLITE_PRIVATE void wx_sqlite3AddColumn(Parse*,Token,Token); SQLITE_PRIVATE void wx_sqlite3AddNotNull(Parse*, int); SQLITE_PRIVATE void wx_sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int); SQLITE_PRIVATE void wx_sqlite3AddCheckConstraint(Parse*, Expr*, const char*, const char*); SQLITE_PRIVATE void wx_sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*); SQLITE_PRIVATE void wx_sqlite3AddCollateType(Parse*, Token*); SQLITE_PRIVATE void wx_sqlite3AddGenerated(Parse*,Expr*,Token*); -SQLITE_PRIVATE void wx_sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); +SQLITE_PRIVATE void wx_sqlite3EndTable(Parse*,Token*,Token*,u32,Select*); SQLITE_PRIVATE void wx_sqlite3AddReturning(Parse*,ExprList*); SQLITE_PRIVATE int wx_sqlite3ParseUri(const char*,const char*,unsigned int*, wx_sqlite3_vfs**,char**,char **); @@ -19950,13 +20375,14 @@ SQLITE_PRIVATE SrcList *wx_sqlite3SrcListEnlarge(Parse*, SrcList*, int, int); SQLITE_PRIVATE SrcList *wx_sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2); SQLITE_PRIVATE SrcList *wx_sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE SrcList *wx_sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, - Token*, Select*, Expr*, IdList*); + Token*, Select*, OnOrUsing*); SQLITE_PRIVATE void wx_sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); SQLITE_PRIVATE void wx_sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*); SQLITE_PRIVATE int wx_sqlite3IndexedByLookup(Parse *, SrcItem *); -SQLITE_PRIVATE void wx_sqlite3SrcListShiftJoinType(SrcList*); +SQLITE_PRIVATE void wx_sqlite3SrcListShiftJoinType(Parse*,SrcList*); SQLITE_PRIVATE void wx_sqlite3SrcListAssignCursors(Parse*, SrcList*); SQLITE_PRIVATE void wx_sqlite3IdListDelete(wx_sqlite3*, IdList*); +SQLITE_PRIVATE void wx_sqlite3ClearOnOrUsing(wx_sqlite3*, OnOrUsing*); SQLITE_PRIVATE void wx_sqlite3SrcListDelete(wx_sqlite3*, SrcList*); SQLITE_PRIVATE Index *wx_sqlite3AllocateIndexObject(wx_sqlite3*,i16,int,char**); SQLITE_PRIVATE void wx_sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, @@ -19972,10 +20398,12 @@ SQLITE_PRIVATE void wx_sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) SQLITE_PRIVATE Expr *wx_sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*); #endif +SQLITE_PRIVATE void wx_sqlite3CodeChangeCount(Vdbe*,int,const char*); SQLITE_PRIVATE void wx_sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*); SQLITE_PRIVATE void wx_sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*, Upsert*); -SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int); +SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*, + ExprList*,Select*,u16,int); SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo*); SQLITE_PRIVATE LogEst wx_sqlite3WhereOutputRowCount(WhereInfo*); SQLITE_PRIVATE int wx_sqlite3WhereIsDistinct(WhereInfo*); @@ -19996,7 +20424,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, SQLITE_PRIVATE void wx_sqlite3ExprCodeMove(Parse*, int, int, int); SQLITE_PRIVATE void wx_sqlite3ExprCode(Parse*, Expr*, int); #ifndef SQLITE_OMIT_GENERATED_COLUMNS -SQLITE_PRIVATE void wx_sqlite3ExprCodeGeneratedColumn(Parse*, Column*, int); +SQLITE_PRIVATE void wx_sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int); #endif SQLITE_PRIVATE void wx_sqlite3ExprCodeCopy(Parse*, Expr*, int); SQLITE_PRIVATE void wx_sqlite3ExprCodeFactorable(Parse*, Expr*, int); @@ -20015,23 +20443,24 @@ SQLITE_PRIVATE Table *wx_sqlite3FindTable(wx_sqlite3*,const char*, const char*); #define LOCATE_VIEW 0x01 #define LOCATE_NOERR 0x02 SQLITE_PRIVATE Table *wx_sqlite3LocateTable(Parse*,u32 flags,const char*, const char*); +SQLITE_PRIVATE const char *wx_sqlite3PreferredTableName(const char*); SQLITE_PRIVATE Table *wx_sqlite3LocateTableItem(Parse*,u32 flags,SrcItem *); SQLITE_PRIVATE Index *wx_sqlite3FindIndex(wx_sqlite3*,const char*, const char*); SQLITE_PRIVATE void wx_sqlite3UnlinkAndDeleteTable(wx_sqlite3*,int,const char*); SQLITE_PRIVATE void wx_sqlite3UnlinkAndDeleteIndex(wx_sqlite3*,int,const char*); SQLITE_PRIVATE void wx_sqlite3Vacuum(Parse*,Token*,Expr*); SQLITE_PRIVATE int wx_sqlite3RunVacuum(char**, wx_sqlite3*, int, wx_sqlite3_value*); -SQLITE_PRIVATE char *wx_sqlite3NameFromToken(wx_sqlite3*, Token*); -SQLITE_PRIVATE int wx_sqlite3ExprCompare(Parse*,Expr*, Expr*, int); -SQLITE_PRIVATE int wx_sqlite3ExprCompareSkip(Expr*, Expr*, int); -SQLITE_PRIVATE int wx_sqlite3ExprListCompare(ExprList*, ExprList*, int); -SQLITE_PRIVATE int wx_sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int); +SQLITE_PRIVATE char *wx_sqlite3NameFromToken(wx_sqlite3*, const Token*); +SQLITE_PRIVATE int wx_sqlite3ExprCompare(const Parse*,const Expr*,const Expr*, int); +SQLITE_PRIVATE int wx_sqlite3ExprCompareSkip(Expr*,Expr*,int); +SQLITE_PRIVATE int wx_sqlite3ExprListCompare(const ExprList*,const ExprList*, int); +SQLITE_PRIVATE int wx_sqlite3ExprImpliesExpr(const Parse*,const Expr*,const Expr*, int); SQLITE_PRIVATE int wx_sqlite3ExprImpliesNonNullRow(Expr*,int); SQLITE_PRIVATE void wx_sqlite3AggInfoPersistWalkerInit(Walker*,Parse*); SQLITE_PRIVATE void wx_sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); SQLITE_PRIVATE void wx_sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); SQLITE_PRIVATE int wx_sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx); -SQLITE_PRIVATE int wx_sqlite3FunctionUsesThisSrc(Expr*, SrcList*); +SQLITE_PRIVATE int wx_sqlite3ReferencesSrcList(Parse*, Expr*, SrcList*); SQLITE_PRIVATE Vdbe *wx_sqlite3GetVdbe(Parse*); #ifndef SQLITE_UNTESTABLE SQLITE_PRIVATE void wx_sqlite3PrngSaveState(void); @@ -20053,10 +20482,11 @@ SQLITE_PRIVATE int wx_sqlite3ExprIsConstantNotJoin(Expr*); SQLITE_PRIVATE int wx_sqlite3ExprIsConstantOrFunction(Expr*, u8); SQLITE_PRIVATE int wx_sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); SQLITE_PRIVATE int wx_sqlite3ExprIsTableConstant(Expr*,int); +SQLITE_PRIVATE int wx_sqlite3ExprIsTableConstraint(Expr*,const SrcItem*); #ifdef SQLITE_ENABLE_CURSOR_HINTS SQLITE_PRIVATE int wx_sqlite3ExprContainsSubquery(Expr*); #endif -SQLITE_PRIVATE int wx_sqlite3ExprIsInteger(Expr*, int*); +SQLITE_PRIVATE int wx_sqlite3ExprIsInteger(const Expr*, int*); SQLITE_PRIVATE int wx_sqlite3ExprCanBeNull(const Expr*); SQLITE_PRIVATE int wx_sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int wx_sqlite3IsRowid(const char*); @@ -20081,20 +20511,26 @@ SQLITE_PRIVATE void wx_sqlite3MayAbort(Parse*); SQLITE_PRIVATE void wx_sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8); SQLITE_PRIVATE void wx_sqlite3UniqueConstraint(Parse*, int, Index*); SQLITE_PRIVATE void wx_sqlite3RowidConstraint(Parse*, int, Table*); -SQLITE_PRIVATE Expr *wx_sqlite3ExprDup(wx_sqlite3*,Expr*,int); -SQLITE_PRIVATE ExprList *wx_sqlite3ExprListDup(wx_sqlite3*,ExprList*,int); -SQLITE_PRIVATE SrcList *wx_sqlite3SrcListDup(wx_sqlite3*,SrcList*,int); -SQLITE_PRIVATE IdList *wx_sqlite3IdListDup(wx_sqlite3*,IdList*); -SQLITE_PRIVATE Select *wx_sqlite3SelectDup(wx_sqlite3*,Select*,int); +SQLITE_PRIVATE Expr *wx_sqlite3ExprDup(wx_sqlite3*,const Expr*,int); +SQLITE_PRIVATE ExprList *wx_sqlite3ExprListDup(wx_sqlite3*,const ExprList*,int); +SQLITE_PRIVATE SrcList *wx_sqlite3SrcListDup(wx_sqlite3*,const SrcList*,int); +SQLITE_PRIVATE IdList *wx_sqlite3IdListDup(wx_sqlite3*,const IdList*); +SQLITE_PRIVATE Select *wx_sqlite3SelectDup(wx_sqlite3*,const Select*,int); SQLITE_PRIVATE FuncDef *wx_sqlite3FunctionSearch(int,const char*); SQLITE_PRIVATE void wx_sqlite3InsertBuiltinFuncs(FuncDef*,int); SQLITE_PRIVATE FuncDef *wx_sqlite3FindFunction(wx_sqlite3*,const char*,int,u8,u8); +SQLITE_PRIVATE void wx_sqlite3QuoteValue(StrAccum*,wx_sqlite3_value*); SQLITE_PRIVATE void wx_sqlite3RegisterBuiltinFunctions(void); SQLITE_PRIVATE void wx_sqlite3RegisterDateTimeFunctions(void); +SQLITE_PRIVATE void wx_sqlite3RegisterJsonFunctions(void); SQLITE_PRIVATE void wx_sqlite3RegisterPerConnectionBuiltinFunctions(wx_sqlite3*); +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) +SQLITE_PRIVATE int wx_sqlite3JsonTableFunctions(wx_sqlite3*); +#endif SQLITE_PRIVATE int wx_sqlite3SafetyCheckOk(wx_sqlite3*); SQLITE_PRIVATE int wx_sqlite3SafetyCheckSickOrOk(wx_sqlite3*); SQLITE_PRIVATE void wx_sqlite3ChangeCookie(Parse*, int); +SQLITE_PRIVATE With *wx_sqlite3WithDup(wx_sqlite3 *db, With *p); #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) SQLITE_PRIVATE void wx_sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int); @@ -20144,7 +20580,8 @@ SQLITE_PRIVATE SrcList *wx_sqlite3TriggerStepSrc(Parse*, TriggerStep*); SQLITE_PRIVATE int wx_sqlite3JoinType(Parse*, Token*, Token*, Token*); SQLITE_PRIVATE int wx_sqlite3ColumnIndex(Table *pTab, const char *zCol); -SQLITE_PRIVATE void wx_sqlite3SetJoinExpr(Expr*,int); +SQLITE_PRIVATE void wx_sqlite3SrcItemColumnUsed(SrcItem*,int); +SQLITE_PRIVATE void wx_sqlite3SetJoinExpr(Expr*,int,u32); SQLITE_PRIVATE void wx_sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int); SQLITE_PRIVATE void wx_sqlite3DeferForeignKey(Parse*, int); #ifndef SQLITE_OMIT_AUTHORIZATION @@ -20168,7 +20605,8 @@ SQLITE_PRIVATE int wx_sqlite3FixSelect(DbFixer*, Select*); SQLITE_PRIVATE int wx_sqlite3FixExpr(DbFixer*, Expr*); SQLITE_PRIVATE int wx_sqlite3FixTriggerStep(DbFixer*, TriggerStep*); SQLITE_PRIVATE int wx_sqlite3RealSameAsInt(double,wx_sqlite3_int64); -SQLITE_PRIVATE void wx_sqlite3Int64ToText(i64,char*); +SQLITE_PRIVATE i64 wx_sqlite3RealToI64(double); +SQLITE_PRIVATE int wx_sqlite3Int64ToText(i64,char*); SQLITE_PRIVATE int wx_sqlite3AtoF(const char *z, double*, int, u8); SQLITE_PRIVATE int wx_sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int wx_sqlite3GetUInt32(const char*, u32*); @@ -20180,14 +20618,8 @@ SQLITE_PRIVATE int wx_sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE u32 wx_sqlite3Utf8Read(const u8**); SQLITE_PRIVATE LogEst wx_sqlite3LogEst(u64); SQLITE_PRIVATE LogEst wx_sqlite3LogEstAdd(LogEst,LogEst); -#ifndef SQLITE_OMIT_VIRTUALTABLE SQLITE_PRIVATE LogEst wx_sqlite3LogEstFromDouble(double); -#endif -#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ - defined(SQLITE_ENABLE_STAT4) || \ - defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) SQLITE_PRIVATE u64 wx_sqlite3LogEstToInt(LogEst); -#endif SQLITE_PRIVATE VList *wx_sqlite3VListAdd(wx_sqlite3*,VList*,const char*,int,int); SQLITE_PRIVATE const char *wx_sqlite3VListNumToName(VList*,int); SQLITE_PRIVATE int wx_sqlite3VListNameToNum(VList*,const char*,int); @@ -20219,11 +20651,13 @@ SQLITE_PRIVATE int wx_sqlite3VarintLen(u64 v); SQLITE_PRIVATE const char *wx_sqlite3IndexAffinityStr(wx_sqlite3*, Index*); +SQLITE_PRIVATE char *wx_sqlite3TableAffinityStr(wx_sqlite3*,const Table*); SQLITE_PRIVATE void wx_sqlite3TableAffinity(Vdbe*, Table*, int); SQLITE_PRIVATE char wx_sqlite3CompareAffinity(const Expr *pExpr, char aff2); SQLITE_PRIVATE int wx_sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity); -SQLITE_PRIVATE char wx_sqlite3TableColumnAffinity(Table*,int); +SQLITE_PRIVATE char wx_sqlite3TableColumnAffinity(const Table*,int); SQLITE_PRIVATE char wx_sqlite3ExprAffinity(const Expr *pExpr); +SQLITE_PRIVATE int wx_sqlite3ExprDataType(const Expr *pExpr); SQLITE_PRIVATE int wx_sqlite3Atoi64(const char*, i64*, int, u8); SQLITE_PRIVATE int wx_sqlite3DecOrHexToI64(const char*, i64*); SQLITE_PRIVATE void wx_sqlite3ErrorWithMsg(wx_sqlite3*, int, const char*,...); @@ -20238,8 +20672,11 @@ SQLITE_PRIVATE int wx_sqlite3TwoPartName(Parse *, Token *, Token *, Token **); SQLITE_PRIVATE const char *wx_sqlite3ErrName(int); #endif -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE SQLITE_PRIVATE int wx_sqlite3MemdbInit(void); +SQLITE_PRIVATE int wx_sqlite3IsMemdb(const wx_sqlite3_vfs*); +#else +# define wx_sqlite3IsMemdb(X) 0 #endif SQLITE_PRIVATE const char *wx_sqlite3ErrStr(int); @@ -20251,14 +20688,14 @@ SQLITE_PRIVATE void wx_sqlite3SetTextEncoding(wx_sqlite3 *db, u8); SQLITE_PRIVATE CollSeq *wx_sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr); SQLITE_PRIVATE CollSeq *wx_sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr); SQLITE_PRIVATE int wx_sqlite3ExprCollSeqMatch(Parse*,const Expr*,const Expr*); -SQLITE_PRIVATE Expr *wx_sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int); -SQLITE_PRIVATE Expr *wx_sqlite3ExprAddCollateString(Parse*,Expr*,const char*); +SQLITE_PRIVATE Expr *wx_sqlite3ExprAddCollateToken(const Parse *pParse, Expr*, const Token*, int); +SQLITE_PRIVATE Expr *wx_sqlite3ExprAddCollateString(const Parse*,Expr*,const char*); SQLITE_PRIVATE Expr *wx_sqlite3ExprSkipCollate(Expr*); SQLITE_PRIVATE Expr *wx_sqlite3ExprSkipCollateAndLikely(Expr*); SQLITE_PRIVATE int wx_sqlite3CheckCollSeq(Parse *, CollSeq *); SQLITE_PRIVATE int wx_sqlite3WritableSchema(wx_sqlite3*); SQLITE_PRIVATE int wx_sqlite3CheckObjectName(Parse*, const char*,const char*,const char*); -SQLITE_PRIVATE void wx_sqlite3VdbeSetChanges(wx_sqlite3 *, int); +SQLITE_PRIVATE void wx_sqlite3VdbeSetChanges(wx_sqlite3 *, i64); SQLITE_PRIVATE int wx_sqlite3AddInt64(i64*,i64); SQLITE_PRIVATE int wx_sqlite3SubInt64(i64*,i64); SQLITE_PRIVATE int wx_sqlite3MulInt64(i64*,i64); @@ -20283,12 +20720,18 @@ SQLITE_PRIVATE wx_sqlite3_value *wx_sqlite3ValueNew(wx_sqlite3 *); #ifndef SQLITE_OMIT_UTF16 SQLITE_PRIVATE char *wx_sqlite3Utf16to8(wx_sqlite3 *, const void*, int, u8); #endif -SQLITE_PRIVATE int wx_sqlite3ValueFromExpr(wx_sqlite3 *, Expr *, u8, u8, wx_sqlite3_value **); +SQLITE_PRIVATE int wx_sqlite3ValueFromExpr(wx_sqlite3 *, const Expr *, u8, u8, wx_sqlite3_value **); SQLITE_PRIVATE void wx_sqlite3ValueApplyAffinity(wx_sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION SQLITE_PRIVATE const unsigned char wx_sqlite3OpcodeProperty[]; SQLITE_PRIVATE const char wx_sqlite3StrBINARY[]; +SQLITE_PRIVATE const unsigned char wx_sqlite3StdTypeLen[]; +SQLITE_PRIVATE const char wx_sqlite3StdTypeAffinity[]; +SQLITE_PRIVATE const char *wx_sqlite3StdType[]; SQLITE_PRIVATE const unsigned char wx_sqlite3UpperToLower[]; +SQLITE_PRIVATE const unsigned char *wx_sqlite3aLTb; +SQLITE_PRIVATE const unsigned char *wx_sqlite3aEQb; +SQLITE_PRIVATE const unsigned char *wx_sqlite3aGTb; SQLITE_PRIVATE const unsigned char wx_sqlite3CtypeMap[]; SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config wx_sqlite3Config; SQLITE_PRIVATE FuncDefHash wx_sqlite3BuiltinFunctions; @@ -20328,9 +20771,9 @@ SQLITE_PRIVATE int wx_sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, con SQLITE_PRIVATE void wx_sqlite3ColumnDefault(Vdbe *, Table *, int, int); SQLITE_PRIVATE void wx_sqlite3AlterFinishAddColumn(Parse *, Token *); SQLITE_PRIVATE void wx_sqlite3AlterBeginAddColumn(Parse *, SrcList *); -SQLITE_PRIVATE void wx_sqlite3AlterDropColumn(Parse*, SrcList*, Token*); -SQLITE_PRIVATE void *wx_sqlite3RenameTokenMap(Parse*, void*, Token*); -SQLITE_PRIVATE void wx_sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom); +SQLITE_PRIVATE void wx_sqlite3AlterDropColumn(Parse*, SrcList*, const Token*); +SQLITE_PRIVATE const void *wx_sqlite3RenameTokenMap(Parse*, const void*, const Token*); +SQLITE_PRIVATE void wx_sqlite3RenameTokenRemap(Parse*, const void *pTo, const void *pFrom); SQLITE_PRIVATE void wx_sqlite3RenameExprUnmap(Parse*, Expr*); SQLITE_PRIVATE void wx_sqlite3RenameExprlistUnmap(Parse*, ExprList*); SQLITE_PRIVATE CollSeq *wx_sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); @@ -20367,15 +20810,20 @@ SQLITE_PRIVATE int wx_sqlite3CreateFunc(wx_sqlite3 *, const char *, int, int, vo FuncDestructor *pDestructor ); SQLITE_PRIVATE void wx_sqlite3NoopDestructor(void*); -SQLITE_PRIVATE void wx_sqlite3OomFault(wx_sqlite3*); +SQLITE_PRIVATE void *wx_sqlite3OomFault(wx_sqlite3*); SQLITE_PRIVATE void wx_sqlite3OomClear(wx_sqlite3*); SQLITE_PRIVATE int wx_sqlite3ApiExit(wx_sqlite3 *db, int); SQLITE_PRIVATE int wx_sqlite3OpenTempDatabase(Parse *); SQLITE_PRIVATE void wx_sqlite3StrAccumInit(StrAccum*, wx_sqlite3*, char*, int, int); +SQLITE_PRIVATE int wx_sqlite3StrAccumEnlarge(StrAccum*, i64); SQLITE_PRIVATE char *wx_sqlite3StrAccumFinish(StrAccum*); +SQLITE_PRIVATE void wx_sqlite3StrAccumSetError(StrAccum*, u8); +SQLITE_PRIVATE void wx_sqlite3ResultStrAccum(wx_sqlite3_context*,StrAccum*); SQLITE_PRIVATE void wx_sqlite3SelectDestInit(SelectDest*,int,int); SQLITE_PRIVATE Expr *wx_sqlite3CreateColumnExpr(wx_sqlite3 *, SrcList *, int, int); +SQLITE_PRIVATE void wx_sqlite3RecordErrorByteOffset(wx_sqlite3*,const char*); +SQLITE_PRIVATE void wx_sqlite3RecordErrorOffsetOfExpr(wx_sqlite3*,const Expr*); SQLITE_PRIVATE void wx_sqlite3BackupRestart(wx_sqlite3_backup *); SQLITE_PRIVATE void wx_sqlite3BackupUpdate(wx_sqlite3_backup *, Pgno, const u8 *); @@ -20426,7 +20874,7 @@ SQLITE_PRIVATE int wx_sqlite3Utf8To8(unsigned char*); #endif #ifdef SQLITE_OMIT_VIRTUALTABLE -# define wx_sqlite3VtabClear(Y) +# define wx_sqlite3VtabClear(D,T) # define wx_sqlite3VtabSync(X,Y) SQLITE_OK # define wx_sqlite3VtabRollback(X) # define wx_sqlite3VtabCommit(X) @@ -20463,9 +20911,11 @@ SQLITE_PRIVATE int wx_sqlite3ReadOnlyShadowTables(wx_sqlite3 *db); #ifndef SQLITE_OMIT_VIRTUALTABLE SQLITE_PRIVATE int wx_sqlite3ShadowTableName(wx_sqlite3 *db, const char *zName); SQLITE_PRIVATE int wx_sqlite3IsShadowTableOf(wx_sqlite3*,Table*,const char*); +SQLITE_PRIVATE void wx_sqlite3MarkAllShadowTablesOf(wx_sqlite3*, Table*); #else # define wx_sqlite3ShadowTableName(A,B) 0 # define wx_sqlite3IsShadowTableOf(A,B,C) 0 +# define wx_sqlite3MarkAllShadowTablesOf(A,B) #endif SQLITE_PRIVATE int wx_sqlite3VtabEponymousTableInit(Parse*,Module*); SQLITE_PRIVATE void wx_sqlite3VtabEponymousTableClear(wx_sqlite3*,Module*); @@ -20478,11 +20928,17 @@ SQLITE_PRIVATE int wx_sqlite3VtabCallCreate(wx_sqlite3*, int, const char *, char SQLITE_PRIVATE int wx_sqlite3VtabCallConnect(Parse*, Table*); SQLITE_PRIVATE int wx_sqlite3VtabCallDestroy(wx_sqlite3*, int, const char *); SQLITE_PRIVATE int wx_sqlite3VtabBegin(wx_sqlite3 *, VTable *); + SQLITE_PRIVATE FuncDef *wx_sqlite3VtabOverloadFunction(wx_sqlite3 *,FuncDef*, int nArg, Expr*); +#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \ + && !defined(SQLITE_OMIT_VIRTUALTABLE) +SQLITE_PRIVATE void wx_sqlite3VtabUsesAllSchemas(wx_sqlite3_index_info*); +#endif SQLITE_PRIVATE wx_sqlite3_int64 wx_sqlite3StmtCurrentTime(wx_sqlite3_context*); SQLITE_PRIVATE int wx_sqlite3VdbeParameterIndex(Vdbe*, const char*, int); SQLITE_PRIVATE int wx_sqlite3TransferBindings(wx_sqlite3_stmt *, wx_sqlite3_stmt *); -SQLITE_PRIVATE void wx_sqlite3ParserReset(Parse*); +SQLITE_PRIVATE void wx_sqlite3ParseObjectInit(Parse*,wx_sqlite3*); +SQLITE_PRIVATE void wx_sqlite3ParseObjectReset(Parse*); SQLITE_PRIVATE void *wx_sqlite3ParserAddCleanup(Parse*,void(*)(wx_sqlite3*,void*),void*); #ifdef SQLITE_ENABLE_NORMALIZE SQLITE_PRIVATE char *wx_sqlite3Normalize(Vdbe*, const char*); @@ -20502,13 +20958,13 @@ SQLITE_PRIVATE Cte *wx_sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); SQLITE_PRIVATE void wx_sqlite3CteDelete(wx_sqlite3*,Cte*); SQLITE_PRIVATE With *wx_sqlite3WithAdd(Parse*,With*,Cte*); SQLITE_PRIVATE void wx_sqlite3WithDelete(wx_sqlite3*,With*); -SQLITE_PRIVATE void wx_sqlite3WithPush(Parse*, With*, u8); +SQLITE_PRIVATE With *wx_sqlite3WithPush(Parse*, With*, u8); #else # define wx_sqlite3CteNew(P,T,E,S) ((void*)0) # define wx_sqlite3CteDelete(D,C) # define wx_sqlite3CteWithAdd(P,W,C) ((void*)0) # define wx_sqlite3WithDelete(x,y) -# define wx_sqlite3WithPush(x,y,z) +# define wx_sqlite3WithPush(x,y,z) ((void*)0) #endif #ifndef SQLITE_OMIT_UPSERT SQLITE_PRIVATE Upsert *wx_sqlite3UpsertNew(wx_sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*); @@ -20541,6 +20997,7 @@ SQLITE_PRIVATE void wx_sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, SQLITE_PRIVATE int wx_sqlite3FkRequired(Parse*, Table*, int*, int); SQLITE_PRIVATE u32 wx_sqlite3FkOldmask(Parse*, Table*); SQLITE_PRIVATE FKey *wx_sqlite3FkReferences(Table *); +SQLITE_PRIVATE void wx_sqlite3FkClearTriggerCache(wx_sqlite3*,int); #else #define wx_sqlite3FkActions(a,b,c,d,e,f) #define wx_sqlite3FkCheck(a,b,c,d,e,f) @@ -20548,6 +21005,7 @@ SQLITE_PRIVATE FKey *wx_sqlite3FkReferences(Table *); #define wx_sqlite3FkOldmask(a,b) 0 #define wx_sqlite3FkRequired(a,b,c,d) 0 #define wx_sqlite3FkReferences(a) 0 + #define wx_sqlite3FkClearTriggerCache(a,b) #endif #ifndef SQLITE_OMIT_FOREIGN_KEY SQLITE_PRIVATE void wx_sqlite3FkDelete(wx_sqlite3 *, Table*); @@ -20605,7 +21063,7 @@ SQLITE_PRIVATE void wx_sqlite3MemJournalOpen(wx_sqlite3_file *); SQLITE_PRIVATE void wx_sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p); #if SQLITE_MAX_EXPR_DEPTH>0 -SQLITE_PRIVATE int wx_sqlite3SelectExprHeight(Select *); +SQLITE_PRIVATE int wx_sqlite3SelectExprHeight(const Select *); SQLITE_PRIVATE int wx_sqlite3ExprCheckHeight(Parse*, int); #else #define wx_sqlite3SelectExprHeight(x) 0 @@ -20676,8 +21134,8 @@ SQLITE_API SQLITE_EXTERN void (SQLITE_CDECL *wx_sqlite3IoTrace)(const char*,...) */ #ifdef SQLITE_MEMDEBUG SQLITE_PRIVATE void wx_sqlite3MemdebugSetType(void*,u8); -SQLITE_PRIVATE int wx_sqlite3MemdebugHasType(void*,u8); -SQLITE_PRIVATE int wx_sqlite3MemdebugNoType(void*,u8); +SQLITE_PRIVATE int wx_sqlite3MemdebugHasType(const void*,u8); +SQLITE_PRIVATE int wx_sqlite3MemdebugNoType(const void*,u8); #else # define wx_sqlite3MemdebugSetType(X,Y) /* no-op */ # define wx_sqlite3MemdebugHasType(X,Y) 1 @@ -20702,19 +21160,921 @@ SQLITE_PRIVATE int wx_sqlite3DbpageRegister(wx_sqlite3*); SQLITE_PRIVATE int wx_sqlite3DbstatRegister(wx_sqlite3*); #endif -SQLITE_PRIVATE int wx_sqlite3ExprVectorSize(Expr *pExpr); -SQLITE_PRIVATE int wx_sqlite3ExprIsVector(Expr *pExpr); +SQLITE_PRIVATE int wx_sqlite3ExprVectorSize(const Expr *pExpr); +SQLITE_PRIVATE int wx_sqlite3ExprIsVector(const Expr *pExpr); SQLITE_PRIVATE Expr *wx_sqlite3VectorFieldSubexpr(Expr*, int); -SQLITE_PRIVATE Expr *wx_sqlite3ExprForVectorField(Parse*,Expr*,int); +SQLITE_PRIVATE Expr *wx_sqlite3ExprForVectorField(Parse*,Expr*,int,int); SQLITE_PRIVATE void wx_sqlite3VectorErrorMsg(Parse*, Expr*); #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_PRIVATE const char **wx_sqlite3CompileOptions(int *pnOpt); #endif +#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) +SQLITE_PRIVATE int wx_sqlite3KvvfsInit(void); +#endif + +#if defined(VDBE_PROFILE) \ + || defined(SQLITE_PERFORMANCE_TRACE) \ + || defined(SQLITE_ENABLE_STMT_SCANSTATUS) +SQLITE_PRIVATE wx_sqlite3_uint64 wx_sqlite3Hwtime(void); +#endif + #endif /* SQLITEINT_H */ /************** End of sqliteInt.h *******************************************/ +/************** Begin file os_common.h ***************************************/ +/* +** 2004 May 22 +** +** 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 macros and a little bit of code that is common to +** all of the platform-specific files (os_*.c) and is #included into those +** files. +** +** This file should be #included by the os_*.c files only. It is not a +** general purpose header file. +*/ +#ifndef _OS_COMMON_H_ +#define _OS_COMMON_H_ + +/* +** At least two bugs have slipped in because we changed the MEMORY_DEBUG +** macro to SQLITE_DEBUG and some older makefiles have not yet made the +** switch. The following code should catch this problem at compile-time. +*/ +#ifdef MEMORY_DEBUG +# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." +#endif + +/* +** Macros for performance tracing. Normally turned off. Only works +** on i486 hardware. +*/ +#ifdef SQLITE_PERFORMANCE_TRACE + +static sqlite_uint64 g_start; +static sqlite_uint64 g_elapsed; +#define TIMER_START g_start=wx_sqlite3Hwtime() +#define TIMER_END g_elapsed=wx_sqlite3Hwtime()-g_start +#define TIMER_ELAPSED g_elapsed +#else +#define TIMER_START +#define TIMER_END +#define TIMER_ELAPSED ((sqlite_uint64)0) +#endif + +/* +** If we compile with the SQLITE_TEST macro set, then the following block +** of code will give us the ability to simulate a disk I/O error. This +** is used for testing the I/O recovery logic. +*/ +#if defined(SQLITE_TEST) +SQLITE_API extern int wx_sqlite3_io_error_hit; +SQLITE_API extern int wx_sqlite3_io_error_hardhit; +SQLITE_API extern int wx_sqlite3_io_error_pending; +SQLITE_API extern int wx_sqlite3_io_error_persist; +SQLITE_API extern int wx_sqlite3_io_error_benign; +SQLITE_API extern int wx_sqlite3_diskfull_pending; +SQLITE_API extern int wx_sqlite3_diskfull; +#define SimulateIOErrorBenign(X) wx_sqlite3_io_error_benign=(X) +#define SimulateIOError(CODE) \ + if( (wx_sqlite3_io_error_persist && wx_sqlite3_io_error_hit) \ + || wx_sqlite3_io_error_pending-- == 1 ) \ + { local_ioerr(); CODE; } +static void local_ioerr(){ + IOTRACE(("IOERR\n")); + wx_sqlite3_io_error_hit++; + if( !wx_sqlite3_io_error_benign ) wx_sqlite3_io_error_hardhit++; +} +#define SimulateDiskfullError(CODE) \ + if( wx_sqlite3_diskfull_pending ){ \ + if( wx_sqlite3_diskfull_pending == 1 ){ \ + local_ioerr(); \ + wx_sqlite3_diskfull = 1; \ + wx_sqlite3_io_error_hit = 1; \ + CODE; \ + }else{ \ + wx_sqlite3_diskfull_pending--; \ + } \ + } +#else +#define SimulateIOErrorBenign(X) +#define SimulateIOError(A) +#define SimulateDiskfullError(A) +#endif /* defined(SQLITE_TEST) */ + +/* +** When testing, keep a count of the number of open files. +*/ +#if defined(SQLITE_TEST) +SQLITE_API extern int wx_sqlite3_open_file_count; +#define OpenCounter(X) wx_sqlite3_open_file_count+=(X) +#else +#define OpenCounter(X) +#endif /* defined(SQLITE_TEST) */ + +#endif /* !defined(_OS_COMMON_H_) */ + +/************** End of os_common.h *******************************************/ +/************** Begin file ctime.c *******************************************/ +/* DO NOT EDIT! +** This file is automatically generated by the script in the canonical +** SQLite source tree at tool/mkctimec.tcl. +** +** To modify this header, edit any of the various lists in that script +** which specify categories of generated conditionals in this file. +*/ + +/* +** 2010 February 23 +** +** 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 routines used to report what compile-time options +** SQLite was built with. +*/ +#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* IMP: R-16824-07538 */ + +/* +** Include the configuration header output by 'configure' if we're using the +** autoconf-based build +*/ +#if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) +/* #include "sqlite_cfg.h" */ +#define SQLITECONFIG_H 1 +#endif + +/* These macros are provided to "stringify" the value of the define +** for those options in which the value is meaningful. */ +#define CTIMEOPT_VAL_(opt) #opt +#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) + +/* Like CTIMEOPT_VAL, but especially for SQLITE_DEFAULT_LOOKASIDE. This +** option requires a separate macro because legal values contain a single +** comma. e.g. (-DSQLITE_DEFAULT_LOOKASIDE="100,100") */ +#define CTIMEOPT_VAL2_(opt1,opt2) #opt1 "," #opt2 +#define CTIMEOPT_VAL2(opt) CTIMEOPT_VAL2_(opt) +/* #include "sqliteInt.h" */ + +/* +** An array of names of all compile-time options. This array should +** be sorted A-Z. +** +** This array looks large, but in a typical installation actually uses +** only a handful of compile-time options, so most times this array is usually +** rather short and uses little memory space. +*/ +static const char * const wx_sqlite3azCompileOpt[] = { + +#ifdef SQLITE_32BIT_ROWID + "32BIT_ROWID", +#endif +#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC + "4_BYTE_ALIGNED_MALLOC", +#endif +#ifdef SQLITE_64BIT_STATS + "64BIT_STATS", +#endif +#ifdef SQLITE_ALLOW_COVERING_INDEX_SCAN +# if SQLITE_ALLOW_COVERING_INDEX_SCAN != 1 + "ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN), +# endif +#endif +#ifdef SQLITE_ALLOW_URI_AUTHORITY + "ALLOW_URI_AUTHORITY", +#endif +#ifdef SQLITE_ATOMIC_INTRINSICS + "ATOMIC_INTRINSICS=" CTIMEOPT_VAL(SQLITE_ATOMIC_INTRINSICS), +#endif +#ifdef SQLITE_BITMASK_TYPE + "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE), +#endif +#ifdef SQLITE_BUG_COMPATIBLE_20160819 + "BUG_COMPATIBLE_20160819", +#endif +#ifdef SQLITE_CASE_SENSITIVE_LIKE + "CASE_SENSITIVE_LIKE", +#endif +#ifdef SQLITE_CHECK_PAGES + "CHECK_PAGES", +#endif +#if defined(__clang__) && defined(__clang_major__) + "COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "." + CTIMEOPT_VAL(__clang_minor__) "." + CTIMEOPT_VAL(__clang_patchlevel__), +#elif defined(_MSC_VER) + "COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER), +#elif defined(__GNUC__) && defined(__VERSION__) + "COMPILER=gcc-" __VERSION__, +#endif +#ifdef SQLITE_COVERAGE_TEST + "COVERAGE_TEST", +#endif +#ifdef SQLITE_DEBUG + "DEBUG", +#endif +#ifdef SQLITE_DEFAULT_AUTOMATIC_INDEX + "DEFAULT_AUTOMATIC_INDEX", +#endif +#ifdef SQLITE_DEFAULT_AUTOVACUUM + "DEFAULT_AUTOVACUUM", +#endif +#ifdef SQLITE_DEFAULT_CACHE_SIZE + "DEFAULT_CACHE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_CACHE_SIZE), +#endif +#ifdef SQLITE_DEFAULT_CKPTFULLFSYNC + "DEFAULT_CKPTFULLFSYNC", +#endif +#ifdef SQLITE_DEFAULT_FILE_FORMAT + "DEFAULT_FILE_FORMAT=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_FORMAT), +#endif +#ifdef SQLITE_DEFAULT_FILE_PERMISSIONS + "DEFAULT_FILE_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_PERMISSIONS), +#endif +#ifdef SQLITE_DEFAULT_FOREIGN_KEYS + "DEFAULT_FOREIGN_KEYS", +#endif +#ifdef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT + "DEFAULT_JOURNAL_SIZE_LIMIT=" CTIMEOPT_VAL(SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT), +#endif +#ifdef SQLITE_DEFAULT_LOCKING_MODE + "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), +#endif +#ifdef SQLITE_DEFAULT_LOOKASIDE + "DEFAULT_LOOKASIDE=" CTIMEOPT_VAL2(SQLITE_DEFAULT_LOOKASIDE), +#endif +#ifdef SQLITE_DEFAULT_MEMSTATUS +# if SQLITE_DEFAULT_MEMSTATUS != 1 + "DEFAULT_MEMSTATUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_MEMSTATUS), +# endif +#endif +#ifdef SQLITE_DEFAULT_MMAP_SIZE + "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), +#endif +#ifdef SQLITE_DEFAULT_PAGE_SIZE + "DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_PAGE_SIZE), +#endif +#ifdef SQLITE_DEFAULT_PCACHE_INITSZ + "DEFAULT_PCACHE_INITSZ=" CTIMEOPT_VAL(SQLITE_DEFAULT_PCACHE_INITSZ), +#endif +#ifdef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS + "DEFAULT_PROXYDIR_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_PROXYDIR_PERMISSIONS), +#endif +#ifdef SQLITE_DEFAULT_RECURSIVE_TRIGGERS + "DEFAULT_RECURSIVE_TRIGGERS", +#endif +#ifdef SQLITE_DEFAULT_ROWEST + "DEFAULT_ROWEST=" CTIMEOPT_VAL(SQLITE_DEFAULT_ROWEST), +#endif +#ifdef SQLITE_DEFAULT_SECTOR_SIZE + "DEFAULT_SECTOR_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_SECTOR_SIZE), +#endif +#ifdef SQLITE_DEFAULT_SYNCHRONOUS + "DEFAULT_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_SYNCHRONOUS), +#endif +#ifdef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT + "DEFAULT_WAL_AUTOCHECKPOINT=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_AUTOCHECKPOINT), +#endif +#ifdef SQLITE_DEFAULT_WAL_SYNCHRONOUS + "DEFAULT_WAL_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_SYNCHRONOUS), +#endif +#ifdef SQLITE_DEFAULT_WORKER_THREADS + "DEFAULT_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WORKER_THREADS), +#endif +#ifdef SQLITE_DIRECT_OVERFLOW_READ + "DIRECT_OVERFLOW_READ", +#endif +#ifdef SQLITE_DISABLE_DIRSYNC + "DISABLE_DIRSYNC", +#endif +#ifdef SQLITE_DISABLE_FTS3_UNICODE + "DISABLE_FTS3_UNICODE", +#endif +#ifdef SQLITE_DISABLE_FTS4_DEFERRED + "DISABLE_FTS4_DEFERRED", +#endif +#ifdef SQLITE_DISABLE_INTRINSIC + "DISABLE_INTRINSIC", +#endif +#ifdef SQLITE_DISABLE_LFS + "DISABLE_LFS", +#endif +#ifdef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS + "DISABLE_PAGECACHE_OVERFLOW_STATS", +#endif +#ifdef SQLITE_DISABLE_SKIPAHEAD_DISTINCT + "DISABLE_SKIPAHEAD_DISTINCT", +#endif +#ifdef SQLITE_DQS + "DQS=" CTIMEOPT_VAL(SQLITE_DQS), +#endif +#ifdef SQLITE_ENABLE_8_3_NAMES + "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES), +#endif +#ifdef SQLITE_ENABLE_API_ARMOR + "ENABLE_API_ARMOR", +#endif +#ifdef SQLITE_ENABLE_ATOMIC_WRITE + "ENABLE_ATOMIC_WRITE", +#endif +#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE + "ENABLE_BATCH_ATOMIC_WRITE", +#endif +#ifdef SQLITE_ENABLE_BYTECODE_VTAB + "ENABLE_BYTECODE_VTAB", +#endif +#ifdef SQLITE_ENABLE_CEROD + "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), +#endif +#ifdef SQLITE_ENABLE_COLUMN_METADATA + "ENABLE_COLUMN_METADATA", +#endif +#ifdef SQLITE_ENABLE_COLUMN_USED_MASK + "ENABLE_COLUMN_USED_MASK", +#endif +#ifdef SQLITE_ENABLE_COSTMULT + "ENABLE_COSTMULT", +#endif +#ifdef SQLITE_ENABLE_CURSOR_HINTS + "ENABLE_CURSOR_HINTS", +#endif +#ifdef SQLITE_ENABLE_DBPAGE_VTAB + "ENABLE_DBPAGE_VTAB", +#endif +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + "ENABLE_DBSTAT_VTAB", +#endif +#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT + "ENABLE_EXPENSIVE_ASSERT", +#endif +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + "ENABLE_EXPLAIN_COMMENTS", +#endif +#ifdef SQLITE_ENABLE_FTS3 + "ENABLE_FTS3", +#endif +#ifdef SQLITE_ENABLE_FTS3_PARENTHESIS + "ENABLE_FTS3_PARENTHESIS", +#endif +#ifdef SQLITE_ENABLE_FTS3_TOKENIZER + "ENABLE_FTS3_TOKENIZER", +#endif +#ifdef SQLITE_ENABLE_FTS4 + "ENABLE_FTS4", +#endif +#ifdef SQLITE_ENABLE_FTS5 + "ENABLE_FTS5", +#endif +#ifdef SQLITE_ENABLE_GEOPOLY + "ENABLE_GEOPOLY", +#endif +#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS + "ENABLE_HIDDEN_COLUMNS", +#endif +#ifdef SQLITE_ENABLE_ICU + "ENABLE_ICU", +#endif +#ifdef SQLITE_ENABLE_IOTRACE + "ENABLE_IOTRACE", +#endif +#ifdef SQLITE_ENABLE_LOAD_EXTENSION + "ENABLE_LOAD_EXTENSION", +#endif +#ifdef SQLITE_ENABLE_LOCKING_STYLE + "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), +#endif +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + "ENABLE_MATH_FUNCTIONS", +#endif +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + "ENABLE_MEMORY_MANAGEMENT", +#endif +#ifdef SQLITE_ENABLE_MEMSYS3 + "ENABLE_MEMSYS3", +#endif +#ifdef SQLITE_ENABLE_MEMSYS5 + "ENABLE_MEMSYS5", +#endif +#ifdef SQLITE_ENABLE_MULTIPLEX + "ENABLE_MULTIPLEX", +#endif +#ifdef SQLITE_ENABLE_NORMALIZE + "ENABLE_NORMALIZE", +#endif +#ifdef SQLITE_ENABLE_NULL_TRIM + "ENABLE_NULL_TRIM", +#endif +#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC + "ENABLE_OFFSET_SQL_FUNC", +#endif +#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK + "ENABLE_OVERSIZE_CELL_CHECK", +#endif +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + "ENABLE_PREUPDATE_HOOK", +#endif +#ifdef SQLITE_ENABLE_QPSG + "ENABLE_QPSG", +#endif +#ifdef SQLITE_ENABLE_RBU + "ENABLE_RBU", +#endif +#ifdef SQLITE_ENABLE_RTREE + "ENABLE_RTREE", +#endif +#ifdef SQLITE_ENABLE_SESSION + "ENABLE_SESSION", +#endif +#ifdef SQLITE_ENABLE_SNAPSHOT + "ENABLE_SNAPSHOT", +#endif +#ifdef SQLITE_ENABLE_SORTER_REFERENCES + "ENABLE_SORTER_REFERENCES", +#endif +#ifdef SQLITE_ENABLE_SQLLOG + "ENABLE_SQLLOG", +#endif +#ifdef SQLITE_ENABLE_STAT4 + "ENABLE_STAT4", +#endif +#ifdef SQLITE_ENABLE_STMTVTAB + "ENABLE_STMTVTAB", +#endif +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + "ENABLE_STMT_SCANSTATUS", +#endif +#ifdef SQLITE_ENABLE_TREETRACE + "ENABLE_TREETRACE", +#endif +#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION + "ENABLE_UNKNOWN_SQL_FUNCTION", +#endif +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY + "ENABLE_UNLOCK_NOTIFY", +#endif +#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT + "ENABLE_UPDATE_DELETE_LIMIT", +#endif +#ifdef SQLITE_ENABLE_URI_00_ERROR + "ENABLE_URI_00_ERROR", +#endif +#ifdef SQLITE_ENABLE_VFSTRACE + "ENABLE_VFSTRACE", +#endif +#ifdef SQLITE_ENABLE_WHERETRACE + "ENABLE_WHERETRACE", +#endif +#ifdef SQLITE_ENABLE_ZIPVFS + "ENABLE_ZIPVFS", +#endif +#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS + "EXPLAIN_ESTIMATED_ROWS", +#endif +#ifdef SQLITE_EXTRA_IFNULLROW + "EXTRA_IFNULLROW", +#endif +#ifdef SQLITE_EXTRA_INIT + "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), +#endif +#ifdef SQLITE_EXTRA_SHUTDOWN + "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), +#endif +#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH + "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), +#endif +#ifdef SQLITE_FTS5_ENABLE_TEST_MI + "FTS5_ENABLE_TEST_MI", +#endif +#ifdef SQLITE_FTS5_NO_WITHOUT_ROWID + "FTS5_NO_WITHOUT_ROWID", +#endif +#if HAVE_ISNAN || SQLITE_HAVE_ISNAN + "HAVE_ISNAN", +#endif +#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX +# if SQLITE_HOMEGROWN_RECURSIVE_MUTEX != 1 + "HOMEGROWN_RECURSIVE_MUTEX=" CTIMEOPT_VAL(SQLITE_HOMEGROWN_RECURSIVE_MUTEX), +# endif +#endif +#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS + "IGNORE_AFP_LOCK_ERRORS", +#endif +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS + "IGNORE_FLOCK_LOCK_ERRORS", +#endif +#ifdef SQLITE_INLINE_MEMCPY + "INLINE_MEMCPY", +#endif +#ifdef SQLITE_INT64_TYPE + "INT64_TYPE", +#endif +#ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX + "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX), +#endif +#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS + "LIKE_DOESNT_MATCH_BLOBS", +#endif +#ifdef SQLITE_LOCK_TRACE + "LOCK_TRACE", +#endif +#ifdef SQLITE_LOG_CACHE_SPILL + "LOG_CACHE_SPILL", +#endif +#ifdef SQLITE_MALLOC_SOFT_LIMIT + "MALLOC_SOFT_LIMIT=" CTIMEOPT_VAL(SQLITE_MALLOC_SOFT_LIMIT), +#endif +#ifdef SQLITE_MAX_ATTACHED + "MAX_ATTACHED=" CTIMEOPT_VAL(SQLITE_MAX_ATTACHED), +#endif +#ifdef SQLITE_MAX_COLUMN + "MAX_COLUMN=" CTIMEOPT_VAL(SQLITE_MAX_COLUMN), +#endif +#ifdef SQLITE_MAX_COMPOUND_SELECT + "MAX_COMPOUND_SELECT=" CTIMEOPT_VAL(SQLITE_MAX_COMPOUND_SELECT), +#endif +#ifdef SQLITE_MAX_DEFAULT_PAGE_SIZE + "MAX_DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_DEFAULT_PAGE_SIZE), +#endif +#ifdef SQLITE_MAX_EXPR_DEPTH + "MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_EXPR_DEPTH), +#endif +#ifdef SQLITE_MAX_FUNCTION_ARG + "MAX_FUNCTION_ARG=" CTIMEOPT_VAL(SQLITE_MAX_FUNCTION_ARG), +#endif +#ifdef SQLITE_MAX_LENGTH + "MAX_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LENGTH), +#endif +#ifdef SQLITE_MAX_LIKE_PATTERN_LENGTH + "MAX_LIKE_PATTERN_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LIKE_PATTERN_LENGTH), +#endif +#ifdef SQLITE_MAX_MEMORY + "MAX_MEMORY=" CTIMEOPT_VAL(SQLITE_MAX_MEMORY), +#endif +#ifdef SQLITE_MAX_MMAP_SIZE + "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE), +#endif +#ifdef SQLITE_MAX_MMAP_SIZE_ + "MAX_MMAP_SIZE_=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE_), +#endif +#ifdef SQLITE_MAX_PAGE_COUNT + "MAX_PAGE_COUNT=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_COUNT), +#endif +#ifdef SQLITE_MAX_PAGE_SIZE + "MAX_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_SIZE), +#endif +#ifdef SQLITE_MAX_SCHEMA_RETRY + "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), +#endif +#ifdef SQLITE_MAX_SQL_LENGTH + "MAX_SQL_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_SQL_LENGTH), +#endif +#ifdef SQLITE_MAX_TRIGGER_DEPTH + "MAX_TRIGGER_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_TRIGGER_DEPTH), +#endif +#ifdef SQLITE_MAX_VARIABLE_NUMBER + "MAX_VARIABLE_NUMBER=" CTIMEOPT_VAL(SQLITE_MAX_VARIABLE_NUMBER), +#endif +#ifdef SQLITE_MAX_VDBE_OP + "MAX_VDBE_OP=" CTIMEOPT_VAL(SQLITE_MAX_VDBE_OP), +#endif +#ifdef SQLITE_MAX_WORKER_THREADS + "MAX_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_MAX_WORKER_THREADS), +#endif +#ifdef SQLITE_MEMDEBUG + "MEMDEBUG", +#endif +#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT + "MIXED_ENDIAN_64BIT_FLOAT", +#endif +#ifdef SQLITE_MMAP_READWRITE + "MMAP_READWRITE", +#endif +#ifdef SQLITE_MUTEX_NOOP + "MUTEX_NOOP", +#endif +#ifdef SQLITE_MUTEX_OMIT + "MUTEX_OMIT", +#endif +#ifdef SQLITE_MUTEX_PTHREADS + "MUTEX_PTHREADS", +#endif +#ifdef SQLITE_MUTEX_W32 + "MUTEX_W32", +#endif +#ifdef SQLITE_NEED_ERR_NAME + "NEED_ERR_NAME", +#endif +#ifdef SQLITE_NO_SYNC + "NO_SYNC", +#endif +#ifdef SQLITE_OMIT_ALTERTABLE + "OMIT_ALTERTABLE", +#endif +#ifdef SQLITE_OMIT_ANALYZE + "OMIT_ANALYZE", +#endif +#ifdef SQLITE_OMIT_ATTACH + "OMIT_ATTACH", +#endif +#ifdef SQLITE_OMIT_AUTHORIZATION + "OMIT_AUTHORIZATION", +#endif +#ifdef SQLITE_OMIT_AUTOINCREMENT + "OMIT_AUTOINCREMENT", +#endif +#ifdef SQLITE_OMIT_AUTOINIT + "OMIT_AUTOINIT", +#endif +#ifdef SQLITE_OMIT_AUTOMATIC_INDEX + "OMIT_AUTOMATIC_INDEX", +#endif +#ifdef SQLITE_OMIT_AUTORESET + "OMIT_AUTORESET", +#endif +#ifdef SQLITE_OMIT_AUTOVACUUM + "OMIT_AUTOVACUUM", +#endif +#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION + "OMIT_BETWEEN_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_BLOB_LITERAL + "OMIT_BLOB_LITERAL", +#endif +#ifdef SQLITE_OMIT_CAST + "OMIT_CAST", +#endif +#ifdef SQLITE_OMIT_CHECK + "OMIT_CHECK", +#endif +#ifdef SQLITE_OMIT_COMPLETE + "OMIT_COMPLETE", +#endif +#ifdef SQLITE_OMIT_COMPOUND_SELECT + "OMIT_COMPOUND_SELECT", +#endif +#ifdef SQLITE_OMIT_CONFLICT_CLAUSE + "OMIT_CONFLICT_CLAUSE", +#endif +#ifdef SQLITE_OMIT_CTE + "OMIT_CTE", +#endif +#if defined(SQLITE_OMIT_DATETIME_FUNCS) || defined(SQLITE_OMIT_FLOATING_POINT) + "OMIT_DATETIME_FUNCS", +#endif +#ifdef SQLITE_OMIT_DECLTYPE + "OMIT_DECLTYPE", +#endif +#ifdef SQLITE_OMIT_DEPRECATED + "OMIT_DEPRECATED", +#endif +#ifdef SQLITE_OMIT_DESERIALIZE + "OMIT_DESERIALIZE", +#endif +#ifdef SQLITE_OMIT_DISKIO + "OMIT_DISKIO", +#endif +#ifdef SQLITE_OMIT_EXPLAIN + "OMIT_EXPLAIN", +#endif +#ifdef SQLITE_OMIT_FLAG_PRAGMAS + "OMIT_FLAG_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_FLOATING_POINT + "OMIT_FLOATING_POINT", +#endif +#ifdef SQLITE_OMIT_FOREIGN_KEY + "OMIT_FOREIGN_KEY", +#endif +#ifdef SQLITE_OMIT_GET_TABLE + "OMIT_GET_TABLE", +#endif +#ifdef SQLITE_OMIT_HEX_INTEGER + "OMIT_HEX_INTEGER", +#endif +#ifdef SQLITE_OMIT_INCRBLOB + "OMIT_INCRBLOB", +#endif +#ifdef SQLITE_OMIT_INTEGRITY_CHECK + "OMIT_INTEGRITY_CHECK", +#endif +#ifdef SQLITE_OMIT_INTROSPECTION_PRAGMAS + "OMIT_INTROSPECTION_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_JSON + "OMIT_JSON", +#endif +#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION + "OMIT_LIKE_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_LOAD_EXTENSION + "OMIT_LOAD_EXTENSION", +#endif +#ifdef SQLITE_OMIT_LOCALTIME + "OMIT_LOCALTIME", +#endif +#ifdef SQLITE_OMIT_LOOKASIDE + "OMIT_LOOKASIDE", +#endif +#ifdef SQLITE_OMIT_MEMORYDB + "OMIT_MEMORYDB", +#endif +#ifdef SQLITE_OMIT_OR_OPTIMIZATION + "OMIT_OR_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_PAGER_PRAGMAS + "OMIT_PAGER_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_PARSER_TRACE + "OMIT_PARSER_TRACE", +#endif +#ifdef SQLITE_OMIT_POPEN + "OMIT_POPEN", +#endif +#ifdef SQLITE_OMIT_PRAGMA + "OMIT_PRAGMA", +#endif +#ifdef SQLITE_OMIT_PROGRESS_CALLBACK + "OMIT_PROGRESS_CALLBACK", +#endif +#ifdef SQLITE_OMIT_QUICKBALANCE + "OMIT_QUICKBALANCE", +#endif +#ifdef SQLITE_OMIT_REINDEX + "OMIT_REINDEX", +#endif +#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS + "OMIT_SCHEMA_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS + "OMIT_SCHEMA_VERSION_PRAGMAS", +#endif +#ifdef SQLITE_OMIT_SHARED_CACHE + "OMIT_SHARED_CACHE", +#endif +#ifdef SQLITE_OMIT_SHUTDOWN_DIRECTORIES + "OMIT_SHUTDOWN_DIRECTORIES", +#endif +#ifdef SQLITE_OMIT_SUBQUERY + "OMIT_SUBQUERY", +#endif +#ifdef SQLITE_OMIT_TCL_VARIABLE + "OMIT_TCL_VARIABLE", +#endif +#ifdef SQLITE_OMIT_TEMPDB + "OMIT_TEMPDB", +#endif +#ifdef SQLITE_OMIT_TEST_CONTROL + "OMIT_TEST_CONTROL", +#endif +#ifdef SQLITE_OMIT_TRACE +# if SQLITE_OMIT_TRACE != 1 + "OMIT_TRACE=" CTIMEOPT_VAL(SQLITE_OMIT_TRACE), +# endif +#endif +#ifdef SQLITE_OMIT_TRIGGER + "OMIT_TRIGGER", +#endif +#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION + "OMIT_TRUNCATE_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_UTF16 + "OMIT_UTF16", +#endif +#ifdef SQLITE_OMIT_VACUUM + "OMIT_VACUUM", +#endif +#ifdef SQLITE_OMIT_VIEW + "OMIT_VIEW", +#endif +#ifdef SQLITE_OMIT_VIRTUALTABLE + "OMIT_VIRTUALTABLE", +#endif +#ifdef SQLITE_OMIT_WAL + "OMIT_WAL", +#endif +#ifdef SQLITE_OMIT_WSD + "OMIT_WSD", +#endif +#ifdef SQLITE_OMIT_XFER_OPT + "OMIT_XFER_OPT", +#endif +#ifdef SQLITE_PERFORMANCE_TRACE + "PERFORMANCE_TRACE", +#endif +#ifdef SQLITE_POWERSAFE_OVERWRITE +# if SQLITE_POWERSAFE_OVERWRITE != 1 + "POWERSAFE_OVERWRITE=" CTIMEOPT_VAL(SQLITE_POWERSAFE_OVERWRITE), +# endif +#endif +#ifdef SQLITE_PREFER_PROXY_LOCKING + "PREFER_PROXY_LOCKING", +#endif +#ifdef SQLITE_PROXY_DEBUG + "PROXY_DEBUG", +#endif +#ifdef SQLITE_REVERSE_UNORDERED_SELECTS + "REVERSE_UNORDERED_SELECTS", +#endif +#ifdef SQLITE_RTREE_INT_ONLY + "RTREE_INT_ONLY", +#endif +#ifdef SQLITE_SECURE_DELETE + "SECURE_DELETE", +#endif +#ifdef SQLITE_SMALL_STACK + "SMALL_STACK", +#endif +#ifdef SQLITE_SORTER_PMASZ + "SORTER_PMASZ=" CTIMEOPT_VAL(SQLITE_SORTER_PMASZ), +#endif +#ifdef SQLITE_SOUNDEX + "SOUNDEX", +#endif +#ifdef SQLITE_STAT4_SAMPLES + "STAT4_SAMPLES=" CTIMEOPT_VAL(SQLITE_STAT4_SAMPLES), +#endif +#ifdef SQLITE_STMTJRNL_SPILL + "STMTJRNL_SPILL=" CTIMEOPT_VAL(SQLITE_STMTJRNL_SPILL), +#endif +#ifdef SQLITE_SUBSTR_COMPATIBILITY + "SUBSTR_COMPATIBILITY", +#endif +#if (!defined(SQLITE_WIN32_MALLOC) \ + && !defined(SQLITE_ZERO_MALLOC) \ + && !defined(SQLITE_MEMDEBUG) \ + ) || defined(SQLITE_SYSTEM_MALLOC) + "SYSTEM_MALLOC", +#endif +#ifdef SQLITE_TCL + "TCL", +#endif +#ifdef SQLITE_TEMP_STORE + "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), +#endif +#ifdef SQLITE_TEST + "TEST", +#endif +#if defined(SQLITE_THREADSAFE) + "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), +#elif defined(THREADSAFE) + "THREADSAFE=" CTIMEOPT_VAL(THREADSAFE), +#else + "THREADSAFE=1", +#endif +#ifdef SQLITE_UNLINK_AFTER_CLOSE + "UNLINK_AFTER_CLOSE", +#endif +#ifdef SQLITE_UNTESTABLE + "UNTESTABLE", +#endif +#ifdef SQLITE_USER_AUTHENTICATION + "USER_AUTHENTICATION", +#endif +#ifdef SQLITE_USE_ALLOCA + "USE_ALLOCA", +#endif +#ifdef SQLITE_USE_FCNTL_TRACE + "USE_FCNTL_TRACE", +#endif +#ifdef SQLITE_USE_URI + "USE_URI", +#endif +#ifdef SQLITE_VDBE_COVERAGE + "VDBE_COVERAGE", +#endif +#ifdef SQLITE_WIN32_MALLOC + "WIN32_MALLOC", +#endif +#ifdef SQLITE_ZERO_MALLOC + "ZERO_MALLOC", +#endif + +} ; + +SQLITE_PRIVATE const char **wx_sqlite3CompileOptions(int *pnOpt){ + *pnOpt = sizeof(wx_sqlite3azCompileOpt) / sizeof(wx_sqlite3azCompileOpt[0]); + return (const char**)wx_sqlite3azCompileOpt; +} + +#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ + +/************** End of ctime.c ***********************************************/ /************** Begin file global.c ******************************************/ /* ** 2008 June 13 @@ -20755,7 +22115,7 @@ SQLITE_PRIVATE const unsigned char wx_sqlite3UpperToLower[] = { 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, - 252,253,254,255 + 252,253,254,255, #endif #ifdef SQLITE_EBCDIC 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */ @@ -20775,7 +22135,35 @@ SQLITE_PRIVATE const unsigned char wx_sqlite3UpperToLower[] = { 224,225,162,163,164,165,166,167,168,169,234,235,236,237,238,239, /* Ex */ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* Fx */ #endif +/* All of the upper-to-lower conversion data is above. The following +** 18 integers are completely unrelated. They are appended to the +** wx_sqlite3UpperToLower[] array to avoid UBSAN warnings. Here's what is +** going on: +** +** The SQL comparison operators (<>, =, >, <=, <, and >=) are implemented +** by invoking wx_sqlite3MemCompare(A,B) which compares values A and B and +** returns negative, zero, or positive if A is less then, equal to, or +** greater than B, respectively. Then the true false results is found by +** consulting wx_sqlite3aLTb[opcode], wx_sqlite3aEQb[opcode], or +** wx_sqlite3aGTb[opcode] depending on whether the result of compare(A,B) +** is negative, zero, or positive, where opcode is the specific opcode. +** The only works because the comparison opcodes are consecutive and in +** this order: NE EQ GT LE LT GE. Various assert()s throughout the code +** ensure that is the case. +** +** These elements must be appended to another array. Otherwise the +** index (here shown as [256-OP_Ne]) would be out-of-bounds and thus +** be undefined behavior. That's goofy, but the C-standards people thought +** it was a good idea, so here we are. +*/ +/* NE EQ GT LE LT GE */ + 1, 0, 0, 1, 1, 0, /* aLTb[]: Use when compare(A,B) less than zero */ + 0, 1, 0, 1, 0, 1, /* aEQb[]: Use when compare(A,B) equals zero */ + 1, 0, 1, 0, 0, 1 /* aGTb[]: Use when compare(A,B) greater than zero*/ }; +SQLITE_PRIVATE const unsigned char *wx_sqlite3aLTb = &wx_sqlite3UpperToLower[256-OP_Ne]; +SQLITE_PRIVATE const unsigned char *wx_sqlite3aEQb = &wx_sqlite3UpperToLower[256+6-OP_Ne]; +SQLITE_PRIVATE const unsigned char *wx_sqlite3aGTb = &wx_sqlite3UpperToLower[256+12-OP_Ne]; /* ** The following 256 byte lookup table is used to support SQLites built-in @@ -20969,16 +22357,20 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config wx_sqlite3Config = { 0, /* xVdbeBranch */ 0, /* pVbeBranchArg */ #endif -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ #endif #ifndef SQLITE_UNTESTABLE 0, /* xTestCallback */ #endif 0, /* bLocaltimeFault */ + 0, /* xAltLocaltime */ 0x7ffffffe, /* iOnceResetThreshold */ SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ 0, /* iPrngSeed */ +#ifdef SQLITE_DEBUG + {0,0,0,0,0,0} /* aTune */ +#endif }; /* @@ -20988,6 +22380,18 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config wx_sqlite3Config = { */ SQLITE_PRIVATE FuncDefHash wx_sqlite3BuiltinFunctions; +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) +/* +** Counter used for coverage testing. Does not come into play for +** release builds. +** +** Access to this global variable is not mutex protected. This might +** result in TSAN warnings. But as the variable does not exist in +** release builds, that should not be a concern. +*/ +SQLITE_PRIVATE unsigned int wx_sqlite3CoverageCounter; +#endif /* SQLITE_COVERAGE_TEST || SQLITE_DEBUG */ + #ifdef VDBE_PROFILE /* ** The following performance counter can be used in place of @@ -21021,7 +22425,7 @@ SQLITE_PRIVATE int wx_sqlite3PendingByte = 0x40000000; /* ** Tracing flags set by SQLITE_TESTCTRL_TRACEFLAGS. */ -SQLITE_PRIVATE u32 wx_sqlite3SelectTrace = 0; +SQLITE_PRIVATE u32 wx_sqlite3TreeTrace = 0; SQLITE_PRIVATE u32 wx_sqlite3WhereTrace = 0; /* #include "opcodes.h" */ @@ -21038,6 +22442,36 @@ SQLITE_PRIVATE const unsigned char wx_sqlite3OpcodeProperty[] = OPFLG_INITIALIZE */ SQLITE_PRIVATE const char wx_sqlite3StrBINARY[] = "BINARY"; +/* +** Standard typenames. These names must match the COLTYPE_* definitions. +** Adjust the SQLITE_N_STDTYPE value if adding or removing entries. +** +** wx_sqlite3StdType[] The actual names of the datatypes. +** +** wx_sqlite3StdTypeLen[] The length (in bytes) of each entry +** in wx_sqlite3StdType[]. +** +** wx_sqlite3StdTypeAffinity[] The affinity associated with each entry +** in wx_sqlite3StdType[]. +*/ +SQLITE_PRIVATE const unsigned char wx_sqlite3StdTypeLen[] = { 3, 4, 3, 7, 4, 4 }; +SQLITE_PRIVATE const char wx_sqlite3StdTypeAffinity[] = { + SQLITE_AFF_NUMERIC, + SQLITE_AFF_BLOB, + SQLITE_AFF_INTEGER, + SQLITE_AFF_INTEGER, + SQLITE_AFF_REAL, + SQLITE_AFF_TEXT +}; +SQLITE_PRIVATE const char *wx_sqlite3StdType[] = { + "ANY", + "BLOB", + "INT", + "INTEGER", + "REAL", + "TEXT" +}; + /************** End of global.c **********************************************/ /************** Begin file status.c ******************************************/ /* @@ -21135,7 +22569,7 @@ typedef struct AuxData AuxData; typedef struct VdbeCursor VdbeCursor; struct VdbeCursor { u8 eCurType; /* One of the CURTYPE_* values above */ - i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ + i8 iDb; /* Index of cursor database in db->aDb[] */ u8 nullRow; /* True if pointing to a row with no data */ u8 deferredMoveto; /* A call to wx_sqlite3BtreeMoveto() is needed */ u8 isTable; /* True for rowid tables. False for indexes */ @@ -21146,11 +22580,13 @@ struct VdbeCursor { Bool isEphemeral:1; /* True for an ephemeral table */ Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ - Bool hasBeenDuped:1; /* This cursor was source or target of OP_OpenDup */ + Bool noReuse:1; /* OpenEphemeral may not reuse this cursor */ u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ - Btree *pBtx; /* Separate file holding temporary table */ + union { /* pBtx for isEphermeral. pAltMap otherwise */ + Btree *pBtx; /* Separate file holding temporary table */ + u32 *aAltMap; /* Mapping from table to index column numbers */ + } ub; i64 seqCount; /* Sequence counter */ - u32 *aAltMap; /* Mapping from table to index column numbers */ /* Cached OP_Column parse information is only valid if cacheStatus matches ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of @@ -21192,6 +22628,11 @@ struct VdbeCursor { u32 aType[1]; /* Type values record decode. MUST BE LAST */ }; +/* Return true if P is a null-only cursor +*/ +#define IsNullCursor(P) \ + ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) + /* ** A value for VdbeCursor.cacheStatus that means the cache is always invalid. @@ -21224,7 +22665,6 @@ struct VdbeFrame { Vdbe *v; /* VM this frame belongs to */ VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ Op *aOp; /* Program instructions for parent frame */ - i64 *anExec; /* Event counters from parent frame */ Mem *aMem; /* Array of memory cells for parent frame */ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ u8 *aOnce; /* Bitmask used by OP_Once */ @@ -21240,8 +22680,8 @@ struct VdbeFrame { int nMem; /* Number of entries in aMem */ int nChildMem; /* Number of memory cells for child frame */ int nChildCsr; /* Number of cursors for child frame */ - int nChange; /* Statement changes (Vdbe.nChange) */ - int nDbChange; /* Value of db->nChange */ + i64 nChange; /* Statement changes (Vdbe.nChange) */ + i64 nDbChange; /* Value of db->nChange */ }; /* Magic number for sanity checking on VdbeFrame objects */ @@ -21266,16 +22706,16 @@ struct wx_sqlite3_value { const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */ FuncDef *pDef; /* Used only when flags==MEM_Agg */ } u; + char *z; /* String or BLOB value */ + int n; /* Number of characters in string value, excluding '\0' */ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ u8 eSubtype; /* Subtype for this value */ - int n; /* Number of characters in string value, excluding '\0' */ - char *z; /* String or BLOB value */ /* ShallowCopy only needs to copy the information above */ - char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */ + wx_sqlite3 *db; /* The associated database connection */ int szMalloc; /* Size of the zMalloc allocation */ u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */ - wx_sqlite3 *db; /* The associated database connection */ + char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */ void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */ #ifdef SQLITE_DEBUG Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ @@ -21287,11 +22727,43 @@ struct wx_sqlite3_value { ** Size of struct Mem not including the Mem.zMalloc member or anything that ** follows. */ -#define MEMCELLSIZE offsetof(Mem,zMalloc) +#define MEMCELLSIZE offsetof(Mem,db) -/* One or more of the following flags are set to indicate the validOK +/* One or more of the following flags are set to indicate the ** representations of the value stored in the Mem struct. ** +** * MEM_Null An SQL NULL value +** +** * MEM_Null|MEM_Zero An SQL NULL with the virtual table +** UPDATE no-change flag set +** +** * MEM_Null|MEM_Term| An SQL NULL, but also contains a +** MEM_Subtype pointer accessible using +** wx_sqlite3_value_pointer(). +** +** * MEM_Null|MEM_Cleared Special SQL NULL that compares non-equal +** to other NULLs even using the IS operator. +** +** * MEM_Str A string, stored in Mem.z with +** length Mem.n. Zero-terminated if +** MEM_Term is set. This flag is +** incompatible with MEM_Blob and +** MEM_Null, but can appear with MEM_Int, +** MEM_Real, and MEM_IntReal. +** +** * MEM_Blob A blob, stored in Mem.z length Mem.n. +** Incompatible with MEM_Str, MEM_Null, +** MEM_Int, MEM_Real, and MEM_IntReal. +** +** * MEM_Blob|MEM_Zero A blob in Mem.z of length Mem.n plus +** MEM.u.i extra 0x00 bytes at the end. +** +** * MEM_Int Integer stored in Mem.u.i. +** +** * MEM_Real Real stored in Mem.u.r. +** +** * MEM_IntReal Real stored as an integer in Mem.u.i. +** ** If the MEM_Null flag is set, then the value is an SQL NULL value. ** For a pointer type created using wx_sqlite3_bind_pointer() or ** wx_sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set. @@ -21302,6 +22774,7 @@ struct wx_sqlite3_value { ** set, then the string is nul terminated. The MEM_Int and MEM_Real ** flags may coexist with the MEM_Str flag. */ +#define MEM_Undefined 0x0000 /* Value is undefined */ #define MEM_Null 0x0001 /* Value is NULL (or a pointer) */ #define MEM_Str 0x0002 /* Value is a string */ #define MEM_Int 0x0004 /* Value is an integer */ @@ -21309,28 +22782,24 @@ struct wx_sqlite3_value { #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_IntReal 0x0020 /* MEM_Int that stringifies like MEM_Real */ #define MEM_AffMask 0x003f /* Mask of affinity bits */ + +/* Extra bits that modify the meanings of the core datatypes above +*/ #define MEM_FromBind 0x0040 /* Value originates from wx_sqlite3_bind() */ -#define MEM_Undefined 0x0080 /* Value is undefined */ + /* 0x0080 // Available */ #define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ -#define MEM_TypeMask 0xc1bf /* Mask of type bits */ - +#define MEM_Term 0x0200 /* String in Mem.z is zero terminated */ +#define MEM_Zero 0x0400 /* Mem.i contains count of 0s appended to blob */ +#define MEM_Subtype 0x0800 /* Mem.eSubtype is valid */ +#define MEM_TypeMask 0x0dbf /* Mask of type bits */ -/* Whenever Mem contains a valid string or blob representation, one of -** the following flags must be set to determine the memory management -** policy for Mem.z. The MEM_Term flag tells us whether or not the -** string is \000 or \u0000 terminated +/* Bits that determine the storage for Mem.z for a string or blob or +** aggregate accumulator. */ -#define MEM_Term 0x0200 /* String in Mem.z is zero terminated */ -#define MEM_Dyn 0x0400 /* Need to call Mem.xDel() on Mem.z */ -#define MEM_Static 0x0800 /* Mem.z points to a static string */ -#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ -#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ -#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */ -#define MEM_Subtype 0x8000 /* Mem.eSubtype is valid */ -#ifdef SQLITE_OMIT_INCRBLOB - #undef MEM_Zero - #define MEM_Zero 0x0000 -#endif +#define MEM_Dyn 0x1000 /* Need to call Mem.xDel() on Mem.z */ +#define MEM_Static 0x2000 /* Mem.z points to a static string */ +#define MEM_Ephem 0x4000 /* Mem.z points to an ephemeral string */ +#define MEM_Agg 0x8000 /* Mem.z points to an agg function context */ /* Return TRUE if Mem X contains dynamically allocated content - anything ** that needs to be deallocated to avoid a leak. @@ -21352,11 +22821,15 @@ struct wx_sqlite3_value { && (X)->n==0 && (X)->u.nZero==0) /* -** Return true if a memory cell is not marked as invalid. This macro +** Return true if a memory cell has been initialized and is valid. ** is for use inside assert() statements only. +** +** A Memory cell is initialized if at least one of the +** MEM_Null, MEM_Str, MEM_Int, MEM_Real, MEM_Blob, or MEM_IntReal bits +** is set. It is "undefined" if all those bits are zero. */ #ifdef SQLITE_DEBUG -#define memIsValid(M) ((M)->flags & MEM_Undefined)==0 +#define memIsValid(M) ((M)->flags & MEM_AffMask)!=0 #endif /* @@ -21394,6 +22867,7 @@ struct wx_sqlite3_context { Vdbe *pVdbe; /* The VM that owns this context */ int iOp; /* Instruction number of OP_Function */ int isError; /* Error code returned by the function. */ + u8 enc; /* Encoding to use for results */ u8 skipFlag; /* Skip accumulator loading if true */ u8 argc; /* Number of arguments */ wx_sqlite3_value *argv[1]; /* Argument set */ @@ -21406,10 +22880,19 @@ typedef unsigned bft; /* Bit Field Type */ /* The ScanStatus object holds a single value for the ** wx_sqlite3_stmt_scanstatus() interface. +** +** aAddrRange[]: +** This array is used by ScanStatus elements associated with EQP +** notes that make an SQLITE_SCANSTAT_NCYCLE value available. It is +** an array of up to 3 ranges of VM addresses for which the Vdbe.anCycle[] +** values should be summed to calculate the NCYCLE value. Each pair of +** integer addresses is a start and end address (both inclusive) for a range +** instructions. A start value of 0 indicates an empty range. */ typedef struct ScanStatus ScanStatus; struct ScanStatus { int addrExplain; /* OP_Explain for loop */ + int aAddrRange[6]; int addrLoop; /* Address of "loops" counter */ int addrVisit; /* Address of "rows visited" counter */ int iSelectID; /* The "Select-ID" for this loop */ @@ -21439,16 +22922,15 @@ struct DblquoteStr { */ struct Vdbe { wx_sqlite3 *db; /* The database connection that owns this statement */ - Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ + Vdbe **ppVPrev,*pVNext; /* Linked list of VDBEs with the same Vdbe.db */ Parse *pParse; /* Parsing context used to create this Vdbe */ ynVar nVar; /* Number of entries in aVar[] */ - u32 iVdbeMagic; /* Magic number defining state of the SQL statement */ int nMem; /* Number of memory locations currently allocated */ int nCursor; /* Number of slots in apCsr[] */ u32 cacheCtr; /* VdbeCursor row cache generation counter */ int pc; /* The program counter */ int rc; /* Value to return */ - int nChange; /* Number of db changes made since last reset */ + i64 nChange; /* Number of db changes made since last reset */ int iStatement; /* Statement number (or 0 if has no opened stmt) */ i64 iCurrentTime; /* Value of julianday('now') for this statement */ i64 nFkConstraint; /* Number of imm. FK constraints this VM */ @@ -21466,7 +22948,7 @@ struct Vdbe { int nOp; /* Number of instructions in the program */ int nOpAlloc; /* Slots allocated for aOp[] */ Mem *aColName; /* Column names to return */ - Mem *pResultSet; /* Pointer to an array of results */ + Mem *pResultRow; /* Current output row */ char *zErrMsg; /* Error message written here */ VList *pVList; /* Name of variables */ #ifndef SQLITE_OMIT_TRACE @@ -21480,17 +22962,16 @@ struct Vdbe { u8 errorAction; /* Recovery action to do in case of an error */ u8 minWriteFileFormat; /* Minimum file format for writable database files */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ - u8 doingRerun; /* True if rerunning after an auto-reprepare */ + u8 eVdbeState; /* On of the VDBE_*_STATE values */ bft expired:2; /* 1: recompile VM immediately 2: when convenient */ bft explain:2; /* True if EXPLAIN present on SQL command */ bft changeCntOn:1; /* True to update the change-counter */ - bft runOnlyOnce:1; /* Automatically expire on reset */ bft usesStmtJournal:1; /* True if uses a statement journal */ bft readOnly:1; /* True for statements that do not write */ bft bIsReader:1; /* True for statements that read */ yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ yDbMask lockMask; /* Subset of btreeMask that requires a lock */ - u32 aCounter[7]; /* Counters used by wx_sqlite3_stmt_status() */ + u32 aCounter[9]; /* Counters used by wx_sqlite3_stmt_status() */ char *zSql; /* Text of the SQL statement that generated this */ #ifdef SQLITE_ENABLE_NORMALIZE char *zNormSql; /* Normalization of the associated SQL statement */ @@ -21504,20 +22985,18 @@ struct Vdbe { SubProgram *pProgram; /* Linked list of all sub-programs used by VM */ AuxData *pAuxData; /* Linked list of auxdata allocations */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS - i64 *anExec; /* Number of times each op has been executed */ int nScan; /* Entries in aScan[] */ ScanStatus *aScan; /* Scan definitions for wx_sqlite3_stmt_scanstatus() */ #endif }; /* -** The following are allowed values for Vdbe.magic +** The following are allowed values for Vdbe.eVdbeState */ -#define VDBE_MAGIC_INIT 0x16bceaa5 /* Building a VDBE program */ -#define VDBE_MAGIC_RUN 0x2df20da3 /* VDBE is ready to execute */ -#define VDBE_MAGIC_HALT 0x319c2973 /* VDBE has completed execution */ -#define VDBE_MAGIC_RESET 0x48fa9f76 /* Reset and ready to run again */ -#define VDBE_MAGIC_DEAD 0x5606c3c8 /* The VDBE has been deallocated */ +#define VDBE_INIT_STATE 0 /* Prepared statement under construction */ +#define VDBE_READY_STATE 1 /* Ready to run but not yet started */ +#define VDBE_RUN_STATE 2 /* Run in progress */ +#define VDBE_HALT_STATE 3 /* Finished. Need reset() or finalize() */ /* ** Structure used to store the context required by the @@ -21532,6 +23011,7 @@ struct PreUpdate { UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ int iNewReg; /* Register for new.* values */ + int iBlobWrite; /* Value returned by preupdate_blobwrite() */ i64 iKey1; /* First key value passed to hook */ i64 iKey2; /* Second key value passed to hook */ Mem *aNew; /* Array of new.* values */ @@ -21539,19 +23019,50 @@ struct PreUpdate { Index *pPk; /* PK index if pTab is WITHOUT ROWID */ }; +/* +** An instance of this object is used to pass an vector of values into +** OP_VFilter, the xFilter method of a virtual table. The vector is the +** set of values on the right-hand side of an IN constraint. +** +** The value as passed into xFilter is an wx_sqlite3_value with a "pointer" +** type, such as is generated by wx_sqlite3_result_pointer() and read by +** wx_sqlite3_value_pointer. Such values have MEM_Term|MEM_Subtype|MEM_Null +** and a subtype of 'p'. The wx_sqlite3_vtab_in_first() and _next() interfaces +** know how to use this object to step through all the values in the +** right operand of the IN constraint. +*/ +typedef struct ValueList ValueList; +struct ValueList { + BtCursor *pCsr; /* An ephemeral table holding all values */ + wx_sqlite3_value *pOut; /* Register to hold each decoded output value */ +}; + +/* Size of content associated with serial types that fit into a +** single-byte varint. +*/ +#ifndef SQLITE_AMALGAMATION +SQLITE_PRIVATE const u8 wx_sqlite3SmallTypeSizes[]; +#endif + /* ** Function prototypes */ SQLITE_PRIVATE void wx_sqlite3VdbeError(Vdbe*, const char *, ...); SQLITE_PRIVATE void wx_sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); +SQLITE_PRIVATE void wx_sqlite3VdbeFreeCursorNN(Vdbe*,VdbeCursor*); void sqliteVdbePopStack(Vdbe*,int); +SQLITE_PRIVATE int SQLITE_NOINLINE wx_sqlite3VdbeHandleMovedCursor(VdbeCursor *p); SQLITE_PRIVATE int SQLITE_NOINLINE wx_sqlite3VdbeFinishMoveto(VdbeCursor*); -SQLITE_PRIVATE int wx_sqlite3VdbeCursorMoveto(VdbeCursor**, u32*); SQLITE_PRIVATE int wx_sqlite3VdbeCursorRestore(VdbeCursor*); SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialTypeLen(u32); SQLITE_PRIVATE u8 wx_sqlite3VdbeOneByteSerialTypeLen(u8); -SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialPut(unsigned char*, Mem*, u32); -SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); +#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT +SQLITE_PRIVATE u64 wx_sqlite3FloatSwap(u64 in); +# define swapMixedEndianFloat(X) X = wx_sqlite3FloatSwap(X) +#else +# define swapMixedEndianFloat(X) +#endif +SQLITE_PRIVATE void wx_sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); SQLITE_PRIVATE void wx_sqlite3VdbeDeleteAuxData(wx_sqlite3*, AuxData**, int, int); int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); @@ -21575,7 +23086,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemCopy(Mem*, const Mem*); SQLITE_PRIVATE void wx_sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); SQLITE_PRIVATE void wx_sqlite3VdbeMemMove(Mem*, Mem*); SQLITE_PRIVATE int wx_sqlite3VdbeMemNulTerminate(Mem*); -SQLITE_PRIVATE int wx_sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*)); +SQLITE_PRIVATE int wx_sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(*)(void*)); SQLITE_PRIVATE void wx_sqlite3VdbeMemSetInt64(Mem*, i64); #ifdef SQLITE_OMIT_FLOATING_POINT # define wx_sqlite3VdbeMemSetDouble wx_sqlite3VdbeMemSetInt64 @@ -21585,14 +23096,19 @@ SQLITE_PRIVATE void wx_sqlite3VdbeMemSetDouble(Mem*, double); SQLITE_PRIVATE void wx_sqlite3VdbeMemSetPointer(Mem*, void*, const char*, void(*)(void*)); SQLITE_PRIVATE void wx_sqlite3VdbeMemInit(Mem*,wx_sqlite3*,u16); SQLITE_PRIVATE void wx_sqlite3VdbeMemSetNull(Mem*); +#ifndef SQLITE_OMIT_INCRBLOB SQLITE_PRIVATE void wx_sqlite3VdbeMemSetZeroBlob(Mem*,int); +#else +SQLITE_PRIVATE int wx_sqlite3VdbeMemSetZeroBlob(Mem*,int); +#endif #ifdef SQLITE_DEBUG SQLITE_PRIVATE int wx_sqlite3VdbeMemIsRowSet(const Mem*); #endif SQLITE_PRIVATE int wx_sqlite3VdbeMemSetRowSet(Mem*); SQLITE_PRIVATE int wx_sqlite3VdbeMemMakeWriteable(Mem*); SQLITE_PRIVATE int wx_sqlite3VdbeMemStringify(Mem*, u8, u8); -SQLITE_PRIVATE i64 wx_sqlite3VdbeIntValue(Mem*); +SQLITE_PRIVATE int wx_sqlite3IntFloatCompare(i64,double); +SQLITE_PRIVATE i64 wx_sqlite3VdbeIntValue(const Mem*); SQLITE_PRIVATE int wx_sqlite3VdbeMemIntegerify(Mem*); SQLITE_PRIVATE double wx_sqlite3VdbeRealValue(Mem*); SQLITE_PRIVATE int wx_sqlite3VdbeBooleanValue(Mem*, int ifNull); @@ -21603,6 +23119,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemCast(Mem*,u8,u8); SQLITE_PRIVATE int wx_sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*); SQLITE_PRIVATE int wx_sqlite3VdbeMemFromBtreeZeroOffset(BtCursor*,u32,Mem*); SQLITE_PRIVATE void wx_sqlite3VdbeMemRelease(Mem *p); +SQLITE_PRIVATE void wx_sqlite3VdbeMemReleaseMalloc(Mem*p); SQLITE_PRIVATE int wx_sqlite3VdbeMemFinalize(Mem*, FuncDef*); #ifndef SQLITE_OMIT_WINDOWFUNC SQLITE_PRIVATE int wx_sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*); @@ -21620,7 +23137,8 @@ SQLITE_PRIVATE void wx_sqlite3VdbeFrameMemDel(void*); /* Destructor on Mem SQLITE_PRIVATE void wx_sqlite3VdbeFrameDelete(VdbeFrame*); /* Actually deletes the Frame */ SQLITE_PRIVATE int wx_sqlite3VdbeFrameRestore(VdbeFrame *); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK -SQLITE_PRIVATE void wx_sqlite3VdbePreUpdateHook(Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int); +SQLITE_PRIVATE void wx_sqlite3VdbePreUpdateHook( + Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int,int); #endif SQLITE_PRIVATE int wx_sqlite3VdbeTransferError(Vdbe *p); @@ -21633,6 +23151,8 @@ SQLITE_PRIVATE int wx_sqlite3VdbeSorterRewind(const VdbeCursor *, int *); SQLITE_PRIVATE int wx_sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *); SQLITE_PRIVATE int wx_sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); +SQLITE_PRIVATE void wx_sqlite3VdbeValueListFree(void*); + #ifdef SQLITE_DEBUG SQLITE_PRIVATE void wx_sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*); SQLITE_PRIVATE void wx_sqlite3VdbeAssertAbortable(Vdbe*); @@ -21961,6 +23481,8 @@ SQLITE_API int wx_sqlite3_db_status( wx_sqlite3BtreeEnterAll(db); db->pnBytesFreed = &nByte; + assert( db->lookaside.pEnd==db->lookaside.pTrueEnd ); + db->lookaside.pEnd = db->lookaside.pStart; for(i=0; inDb; i++){ Schema *pSchema = db->aDb[i].pSchema; if( ALWAYS(pSchema!=0) ){ @@ -21986,6 +23508,7 @@ SQLITE_API int wx_sqlite3_db_status( } } db->pnBytesFreed = 0; + db->lookaside.pEnd = db->lookaside.pTrueEnd; wx_sqlite3BtreeLeaveAll(db); *pHighwater = 0; @@ -22003,10 +23526,12 @@ SQLITE_API int wx_sqlite3_db_status( int nByte = 0; /* Used to accumulate return value */ db->pnBytesFreed = &nByte; - for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){ - wx_sqlite3VdbeClearObject(db, pVdbe); - wx_sqlite3DbFree(db, pVdbe); + assert( db->lookaside.pEnd==db->lookaside.pTrueEnd ); + db->lookaside.pEnd = db->lookaside.pStart; + for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pVNext){ + wx_sqlite3VdbeDelete(pVdbe); } + db->lookaside.pEnd = db->lookaside.pTrueEnd; db->pnBytesFreed = 0; *pHighwater = 0; /* IMP: R-64479-57858 */ @@ -22342,7 +23867,7 @@ static void computeJD(DateTime *p){ p->iJD = (wx_sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000); p->validJD = 1; if( p->validHMS ){ - p->iJD += p->h*3600000 + p->m*60000 + (wx_sqlite3_int64)(p->s*1000); + p->iJD += p->h*3600000 + p->m*60000 + (wx_sqlite3_int64)(p->s*1000 + 0.5); if( p->validTZ ){ p->iJD -= p->tz*60000; p->validYMD = 0; @@ -22569,8 +24094,10 @@ static void clearYMD_HMS_TZ(DateTime *p){ ** is available. This routine returns 0 on success and ** non-zero on any kind of error. ** -** If the wx_sqlite3GlobalConfig.bLocaltimeFault variable is true then this -** routine will always fail. +** If the wx_sqlite3GlobalConfig.bLocaltimeFault variable is non-zero then this +** routine will always fail. If bLocaltimeFault is nonzero and +** wx_sqlite3GlobalConfig.xAltLocaltime is not NULL, then xAltLocaltime() is +** invoked in place of the OS-defined localtime() function. ** ** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C ** library function localtime_r() is used to assist in the calculation of @@ -22586,14 +24113,30 @@ static int osLocaltime(time_t *t, struct tm *pTm){ wx_sqlite3_mutex_enter(mutex); pX = localtime(t); #ifndef SQLITE_UNTESTABLE - if( wx_sqlite3GlobalConfig.bLocaltimeFault ) pX = 0; + if( wx_sqlite3GlobalConfig.bLocaltimeFault ){ + if( wx_sqlite3GlobalConfig.xAltLocaltime!=0 + && 0==wx_sqlite3GlobalConfig.xAltLocaltime((const void*)t,(void*)pTm) + ){ + pX = pTm; + }else{ + pX = 0; + } + } #endif if( pX ) *pTm = *pX; +#if SQLITE_THREADSAFE>0 wx_sqlite3_mutex_leave(mutex); +#endif rc = pX==0; #else #ifndef SQLITE_UNTESTABLE - if( wx_sqlite3GlobalConfig.bLocaltimeFault ) return 1; + if( wx_sqlite3GlobalConfig.bLocaltimeFault ){ + if( wx_sqlite3GlobalConfig.xAltLocaltime!=0 ){ + return wx_sqlite3GlobalConfig.xAltLocaltime((const void*)t,(void*)pTm); + }else{ + return 1; + } + } #endif #if HAVE_LOCALTIME_R rc = localtime_r(t, pTm)==0; @@ -22608,67 +24151,56 @@ static int osLocaltime(time_t *t, struct tm *pTm){ #ifndef SQLITE_OMIT_LOCALTIME /* -** Compute the difference (in milliseconds) between localtime and UTC -** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs, -** return this value and set *pRc to SQLITE_OK. -** -** Or, if an error does occur, set *pRc to SQLITE_ERROR. The returned value -** is undefined in this case. +** Assuming the input DateTime is UTC, move it to its localtime equivalent. */ -static wx_sqlite3_int64 localtimeOffset( - DateTime *p, /* Date at which to calculate offset */ - wx_sqlite3_context *pCtx, /* Write error here if one occurs */ - int *pRc /* OUT: Error code. SQLITE_OK or ERROR */ +static int toLocaltime( + DateTime *p, /* Date at which to calculate offset */ + wx_sqlite3_context *pCtx /* Write error here if one occurs */ ){ - DateTime x, y; time_t t; struct tm sLocal; + int iYearDiff; /* Initialize the contents of sLocal to avoid a compiler warning. */ memset(&sLocal, 0, sizeof(sLocal)); - x = *p; - computeYMD_HMS(&x); - if( x.Y<1971 || x.Y>=2038 ){ + computeJD(p); + if( p->iJD<2108667600*(i64)100000 /* 1970-01-01 */ + || p->iJD>2130141456*(i64)100000 /* 2038-01-18 */ + ){ /* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only ** works for years between 1970 and 2037. For dates outside this range, ** SQLite attempts to map the year into an equivalent year within this ** range, do the calculation, then map the year back. */ - x.Y = 2000; - x.M = 1; - x.D = 1; - x.h = 0; - x.m = 0; - x.s = 0.0; - } else { - int s = (int)(x.s + 0.5); - x.s = s; + DateTime x = *p; + computeYMD_HMS(&x); + iYearDiff = (2000 + x.Y%4) - x.Y; + x.Y += iYearDiff; + x.validJD = 0; + computeJD(&x); + t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); + }else{ + iYearDiff = 0; + t = (time_t)(p->iJD/1000 - 21086676*(i64)10000); } - x.tz = 0; - x.validJD = 0; - computeJD(&x); - t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); if( osLocaltime(&t, &sLocal) ){ wx_sqlite3_result_error(pCtx, "local time unavailable", -1); - *pRc = SQLITE_ERROR; - return 0; + return SQLITE_ERROR; } - y.Y = sLocal.tm_year + 1900; - y.M = sLocal.tm_mon + 1; - y.D = sLocal.tm_mday; - y.h = sLocal.tm_hour; - y.m = sLocal.tm_min; - y.s = sLocal.tm_sec; - y.validYMD = 1; - y.validHMS = 1; - y.validJD = 0; - y.rawS = 0; - y.validTZ = 0; - y.isError = 0; - computeJD(&y); - *pRc = SQLITE_OK; - return y.iJD - x.iJD; + p->Y = sLocal.tm_year + 1900 - iYearDiff; + p->M = sLocal.tm_mon + 1; + p->D = sLocal.tm_mday; + p->h = sLocal.tm_hour; + p->m = sLocal.tm_min; + p->s = sLocal.tm_sec + (p->iJD%1000)*0.001; + p->validYMD = 1; + p->validHMS = 1; + p->validJD = 0; + p->rawS = 0; + p->validTZ = 0; + p->isError = 0; + return SQLITE_OK; } #endif /* SQLITE_OMIT_LOCALTIME */ @@ -22681,18 +24213,17 @@ static wx_sqlite3_int64 localtimeOffset( ** of several units of time. */ static const struct { - u8 eType; /* Transformation type code */ - u8 nName; /* Length of th name */ - char *zName; /* Name of the transformation */ - double rLimit; /* Maximum NNN value for this transform */ - double rXform; /* Constant used for this transform */ + u8 nName; /* Length of the name */ + char zName[7]; /* Name of the transformation */ + float rLimit; /* Maximum NNN value for this transform */ + float rXform; /* Constant used for this transform */ } aXformType[] = { - { 0, 6, "second", 464269060800.0, 1000.0 }, - { 0, 6, "minute", 7737817680.0, 60000.0 }, - { 0, 4, "hour", 128963628.0, 3600000.0 }, - { 0, 3, "day", 5373485.0, 86400000.0 }, - { 1, 5, "month", 176546.0, 2592000000.0 }, - { 2, 4, "year", 14713.0, 31536000000.0 }, + { 6, "second", 4.6427e+14, 1.0 }, + { 6, "minute", 7.7379e+12, 60.0 }, + { 4, "hour", 1.2897e+11, 3600.0 }, + { 3, "day", 5373485.0, 86400.0 }, + { 5, "month", 176546.0, 2592000.0 }, + { 4, "year", 14713.0, 31536000.0 }, }; /* @@ -22723,11 +24254,55 @@ static int parseModifier( wx_sqlite3_context *pCtx, /* Function context */ const char *z, /* The text of the modifier */ int n, /* Length of zMod in bytes */ - DateTime *p /* The date/time value to be modified */ + DateTime *p, /* The date/time value to be modified */ + int idx /* Parameter index of the modifier */ ){ int rc = 1; double r; switch(wx_sqlite3UpperToLower[(u8)z[0]] ){ + case 'a': { + /* + ** auto + ** + ** If rawS is available, then interpret as a julian day number, or + ** a unix timestamp, depending on its magnitude. + */ + if( wx_sqlite3_stricmp(z, "auto")==0 ){ + if( idx>1 ) return 1; /* IMP: R-33611-57934 */ + if( !p->rawS || p->validJD ){ + rc = 0; + p->rawS = 0; + }else if( p->s>=-21086676*(i64)10000 /* -4713-11-24 12:00:00 */ + && p->s<=(25340230*(i64)10000)+799 /* 9999-12-31 23:59:59 */ + ){ + r = p->s*1000.0 + 210866760000000.0; + clearYMD_HMS_TZ(p); + p->iJD = (wx_sqlite3_int64)(r + 0.5); + p->validJD = 1; + p->rawS = 0; + rc = 0; + } + } + break; + } + case 'j': { + /* + ** julianday + ** + ** Always interpret the prior number as a julian-day value. If this + ** is not the first modifier, or if the prior argument is not a numeric + ** value in the allowed range of julian day numbers understood by + ** SQLite (0..5373484.5) then the result will be NULL. + */ + if( wx_sqlite3_stricmp(z, "julianday")==0 ){ + if( idx>1 ) return 1; /* IMP: R-31176-64601 */ + if( p->validJD && p->rawS ){ + rc = 0; + p->rawS = 0; + } + } + break; + } #ifndef SQLITE_OMIT_LOCALTIME case 'l': { /* localtime @@ -22736,9 +24311,7 @@ static int parseModifier( ** show local time. */ if( wx_sqlite3_stricmp(z, "localtime")==0 && wx_sqlite3NotPureFunc(pCtx) ){ - computeJD(p); - p->iJD += localtimeOffset(p, pCtx, &rc); - clearYMD_HMS_TZ(p); + rc = toLocaltime(p, pCtx); } break; } @@ -22751,6 +24324,7 @@ static int parseModifier( ** seconds since 1970. Convert to a real julian day number. */ if( wx_sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){ + if( idx>1 ) return 1; /* IMP: R-49255-55373 */ r = p->s*1000.0 + 210866760000000.0; if( r>=0.0 && r<464269060800000.0 ){ clearYMD_HMS_TZ(p); @@ -22763,18 +24337,31 @@ static int parseModifier( #ifndef SQLITE_OMIT_LOCALTIME else if( wx_sqlite3_stricmp(z, "utc")==0 && wx_sqlite3NotPureFunc(pCtx) ){ if( p->tzSet==0 ){ - wx_sqlite3_int64 c1; + i64 iOrigJD; /* Original localtime */ + i64 iGuess; /* Guess at the corresponding utc time */ + int cnt = 0; /* Safety to prevent infinite loop */ + i64 iErr; /* Guess is off by this much */ + computeJD(p); - c1 = localtimeOffset(p, pCtx, &rc); - if( rc==SQLITE_OK ){ - p->iJD -= c1; - clearYMD_HMS_TZ(p); - p->iJD += c1 - localtimeOffset(p, pCtx, &rc); - } + iGuess = iOrigJD = p->iJD; + iErr = 0; + do{ + DateTime new; + memset(&new, 0, sizeof(new)); + iGuess -= iErr; + new.iJD = iGuess; + new.validJD = 1; + rc = toLocaltime(&new, pCtx); + if( rc ) return rc; + computeJD(&new); + iErr = new.iJD - iOrigJD; + }while( iErr && cnt++<3 ); + memset(p, 0, sizeof(*p)); + p->iJD = iGuess; + p->validJD = 1; p->tzSet = 1; - }else{ - rc = SQLITE_OK; } + rc = SQLITE_OK; } #endif break; @@ -22789,7 +24376,7 @@ static int parseModifier( */ if( wx_sqlite3_strnicmp(z, "weekday ", 8)==0 && wx_sqlite3AtoF(&z[8], &r, wx_sqlite3Strlen30(&z[8]), SQLITE_UTF8)>0 - && (n=(int)r)==r && n>=0 && r<7 ){ + && r>=0.0 && r<7.0 && (n=(int)r)==r ){ wx_sqlite3_int64 Z; computeYMD_HMS(p); p->validTZ = 0; @@ -22890,9 +24477,10 @@ static int parseModifier( && wx_sqlite3_strnicmp(aXformType[i].zName, z, n)==0 && r>-aXformType[i].rLimit && rM += (int)r; x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; @@ -22902,8 +24490,9 @@ static int parseModifier( r -= (int)r; break; } - case 2: { /* Special processing to add years */ + case 5: { /* Special processing to add years */ int y = (int)r; + assert( strcmp(aXformType[i].zName,"year")==0 ); computeYMD_HMS(p); p->Y += y; p->validJD = 0; @@ -22912,7 +24501,7 @@ static int parseModifier( } } computeJD(p); - p->iJD += (wx_sqlite3_int64)(r*aXformType[i].rXform + rRounder); + p->iJD += (wx_sqlite3_int64)(r*1000.0*aXformType[i].rXform + rRounder); rc = 0; break; } @@ -22962,7 +24551,7 @@ static int isDate( for(i=1; iisError || !validJulianDay(p->iJD) ) return 1; @@ -22992,6 +24581,24 @@ static void juliandayFunc( } } +/* +** unixepoch( TIMESTRING, MOD, MOD, ...) +** +** Return the number of seconds (including fractional seconds) since +** the unix epoch of 1970-01-01 00:00:00 GMT. +*/ +static void unixepochFunc( + wx_sqlite3_context *context, + int argc, + wx_sqlite3_value **argv +){ + DateTime x; + if( isDate(context, argc, argv, &x)==0 ){ + computeJD(&x); + wx_sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); + } +} + /* ** datetime( TIMESTRING, MOD, MOD, ...) ** @@ -23004,11 +24611,38 @@ static void datetimeFunc( ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ - char zBuf[100]; + int Y, s; + char zBuf[24]; computeYMD_HMS(&x); - wx_sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d %02d:%02d:%02d", - x.Y, x.M, x.D, x.h, x.m, (int)(x.s)); - wx_sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); + Y = x.Y; + if( Y<0 ) Y = -Y; + zBuf[1] = '0' + (Y/1000)%10; + zBuf[2] = '0' + (Y/100)%10; + zBuf[3] = '0' + (Y/10)%10; + zBuf[4] = '0' + (Y)%10; + zBuf[5] = '-'; + zBuf[6] = '0' + (x.M/10)%10; + zBuf[7] = '0' + (x.M)%10; + zBuf[8] = '-'; + zBuf[9] = '0' + (x.D/10)%10; + zBuf[10] = '0' + (x.D)%10; + zBuf[11] = ' '; + zBuf[12] = '0' + (x.h/10)%10; + zBuf[13] = '0' + (x.h)%10; + zBuf[14] = ':'; + zBuf[15] = '0' + (x.m/10)%10; + zBuf[16] = '0' + (x.m)%10; + zBuf[17] = ':'; + s = (int)x.s; + zBuf[18] = '0' + (s/10)%10; + zBuf[19] = '0' + (s)%10; + zBuf[20] = 0; + if( x.Y<0 ){ + zBuf[0] = '-'; + wx_sqlite3_result_text(context, zBuf, 20, SQLITE_TRANSIENT); + }else{ + wx_sqlite3_result_text(context, &zBuf[1], 19, SQLITE_TRANSIENT); + } } } @@ -23024,10 +24658,20 @@ static void timeFunc( ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ - char zBuf[100]; + int s; + char zBuf[16]; computeHMS(&x); - wx_sqlite3_snprintf(sizeof(zBuf), zBuf, "%02d:%02d:%02d", x.h, x.m, (int)x.s); - wx_sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); + zBuf[0] = '0' + (x.h/10)%10; + zBuf[1] = '0' + (x.h)%10; + zBuf[2] = ':'; + zBuf[3] = '0' + (x.m/10)%10; + zBuf[4] = '0' + (x.m)%10; + zBuf[5] = ':'; + s = (int)x.s; + zBuf[6] = '0' + (s/10)%10; + zBuf[7] = '0' + (s)%10; + zBuf[8] = 0; + wx_sqlite3_result_text(context, zBuf, 8, SQLITE_TRANSIENT); } } @@ -23043,10 +24687,28 @@ static void dateFunc( ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ - char zBuf[100]; + int Y; + char zBuf[16]; computeYMD(&x); - wx_sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D); - wx_sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); + Y = x.Y; + if( Y<0 ) Y = -Y; + zBuf[1] = '0' + (Y/1000)%10; + zBuf[2] = '0' + (Y/100)%10; + zBuf[3] = '0' + (Y/10)%10; + zBuf[4] = '0' + (Y)%10; + zBuf[5] = '-'; + zBuf[6] = '0' + (x.M/10)%10; + zBuf[7] = '0' + (x.M)%10; + zBuf[8] = '-'; + zBuf[9] = '0' + (x.D/10)%10; + zBuf[10] = '0' + (x.D)%10; + zBuf[11] = 0; + if( x.Y<0 ){ + zBuf[0] = '-'; + wx_sqlite3_result_text(context, zBuf, 11, SQLITE_TRANSIENT); + }else{ + wx_sqlite3_result_text(context, &zBuf[1], 10, SQLITE_TRANSIENT); + } } } @@ -23075,131 +24737,100 @@ static void strftimeFunc( wx_sqlite3_value **argv ){ DateTime x; - u64 n; size_t i,j; - char *z; wx_sqlite3 *db; const char *zFmt; - char zBuf[100]; + wx_sqlite3_str sRes; + + if( argc==0 ) return; zFmt = (const char*)wx_sqlite3_value_text(argv[0]); if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return; db = wx_sqlite3_context_db_handle(context); - for(i=0, n=1; zFmt[i]; i++, n++){ - if( zFmt[i]=='%' ){ - switch( zFmt[i+1] ){ - case 'd': - case 'H': - case 'm': - case 'M': - case 'S': - case 'W': - n++; - /* fall thru */ - case 'w': - case '%': - break; - case 'f': - n += 8; - break; - case 'j': - n += 3; - break; - case 'Y': - n += 8; - break; - case 's': - case 'J': - n += 50; - break; - default: - return; /* ERROR. return a NULL */ - } - i++; - } - } - testcase( n==sizeof(zBuf)-1 ); - testcase( n==sizeof(zBuf) ); - testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH]+1 ); - testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ); - if( n(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - wx_sqlite3_result_error_toobig(context); - return; - }else{ - z = wx_sqlite3DbMallocRawNN(db, (int)n); - if( z==0 ){ - wx_sqlite3_result_error_nomem(context); - return; - } - } + wx_sqlite3StrAccumInit(&sRes, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); + computeJD(&x); computeYMD_HMS(&x); for(i=j=0; zFmt[i]; i++){ - if( zFmt[i]!='%' ){ - z[j++] = zFmt[i]; - }else{ - i++; - switch( zFmt[i] ){ - case 'd': wx_sqlite3_snprintf(3, &z[j],"%02d",x.D); j+=2; break; - case 'f': { - double s = x.s; - if( s>59.999 ) s = 59.999; - wx_sqlite3_snprintf(7, &z[j],"%06.3f", s); - j += wx_sqlite3Strlen30(&z[j]); - break; - } - case 'H': wx_sqlite3_snprintf(3, &z[j],"%02d",x.h); j+=2; break; - case 'W': /* Fall thru */ - case 'j': { - int nDay; /* Number of days since 1st day of year */ - DateTime y = x; - y.validJD = 0; - y.M = 1; - y.D = 1; - computeJD(&y); - nDay = (int)((x.iJD-y.iJD+43200000)/86400000); - if( zFmt[i]=='W' ){ - int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ - wd = (int)(((x.iJD+43200000)/86400000)%7); - wx_sqlite3_snprintf(3, &z[j],"%02d",(nDay+7-wd)/7); - j += 2; - }else{ - wx_sqlite3_snprintf(4, &z[j],"%03d",nDay+1); - j += 3; - } - break; - } - case 'J': { - wx_sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0); - j+=wx_sqlite3Strlen30(&z[j]); - break; - } - case 'm': wx_sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; - case 'M': wx_sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; - case 's': { - i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); - wx_sqlite3Int64ToText(iS, &z[j]); - j += wx_sqlite3Strlen30(&z[j]); - break; - } - case 'S': wx_sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break; - case 'w': { - z[j++] = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; - break; - } - case 'Y': { - wx_sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=wx_sqlite3Strlen30(&z[j]); - break; + if( zFmt[i]!='%' ) continue; + if( j59.999 ) s = 59.999; + wx_sqlite3_str_appendf(&sRes, "%06.3f", s); + break; + } + case 'H': { + wx_sqlite3_str_appendf(&sRes, "%02d", x.h); + break; + } + case 'W': /* Fall thru */ + case 'j': { + int nDay; /* Number of days since 1st day of year */ + DateTime y = x; + y.validJD = 0; + y.M = 1; + y.D = 1; + computeJD(&y); + nDay = (int)((x.iJD-y.iJD+43200000)/86400000); + if( zFmt[i]=='W' ){ + int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ + wd = (int)(((x.iJD+43200000)/86400000)%7); + wx_sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7); + }else{ + wx_sqlite3_str_appendf(&sRes,"%03d",nDay+1); } - default: z[j++] = '%'; break; + break; + } + case 'J': { + wx_sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0); + break; + } + case 'm': { + wx_sqlite3_str_appendf(&sRes,"%02d",x.M); + break; + } + case 'M': { + wx_sqlite3_str_appendf(&sRes,"%02d",x.m); + break; + } + case 's': { + i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); + wx_sqlite3_str_appendf(&sRes,"%lld",iS); + break; + } + case 'S': { + wx_sqlite3_str_appendf(&sRes,"%02d",(int)x.s); + break; + } + case 'w': { + wx_sqlite3_str_appendchar(&sRes, 1, + (char)(((x.iJD+129600000)/86400000) % 7) + '0'); + break; + } + case 'Y': { + wx_sqlite3_str_appendf(&sRes,"%04d",x.Y); + break; + } + case '%': { + wx_sqlite3_str_appendchar(&sRes, 1, '%'); + break; + } + default: { + wx_sqlite3_str_reset(&sRes); + return; } } } - z[j] = 0; - wx_sqlite3_result_text(context, z, -1, - z==zBuf ? SQLITE_TRANSIENT : SQLITE_DYNAMIC); + if( j=SQLITE_LOCK_SHARED && lockType<=SQLITE_LOCK_EXCLUSIVE ); return id->pMethods->xLock(id, lockType); } SQLITE_PRIVATE int wx_sqlite3OsUnlock(wx_sqlite3_file *id, int lockType){ + assert( lockType==SQLITE_LOCK_NONE || lockType==SQLITE_LOCK_SHARED ); return id->pMethods->xUnlock(id, lockType); } SQLITE_PRIVATE int wx_sqlite3OsCheckReservedLock(wx_sqlite3_file *id, int *pResOut){ @@ -23480,6 +25114,7 @@ SQLITE_PRIVATE int wx_sqlite3OsSectorSize(wx_sqlite3_file *id){ return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE); } SQLITE_PRIVATE int wx_sqlite3OsDeviceCharacteristics(wx_sqlite3_file *id){ + if( NEVER(id->pMethods==0) ) return 0; return id->pMethods->xDeviceCharacteristics(id); } #ifndef SQLITE_OMIT_WAL @@ -23541,6 +25176,7 @@ SQLITE_PRIVATE int wx_sqlite3OsOpen( ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before ** reaching the VFS. */ + assert( zPath || (flags & SQLITE_OPEN_EXCLUSIVE) ); rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x1087f7f, pFlagsOut); assert( rc==SQLITE_OK || pFile->pMethods==0 ); return rc; @@ -23548,7 +25184,7 @@ SQLITE_PRIVATE int wx_sqlite3OsOpen( SQLITE_PRIVATE int wx_sqlite3OsDelete(wx_sqlite3_vfs *pVfs, const char *zPath, int dirSync){ DO_OS_MALLOC_TEST(0); assert( dirSync==0 || dirSync==1 ); - return pVfs->xDelete(pVfs, zPath, dirSync); + return pVfs->xDelete!=0 ? pVfs->xDelete(pVfs, zPath, dirSync) : SQLITE_OK; } SQLITE_PRIVATE int wx_sqlite3OsAccess( wx_sqlite3_vfs *pVfs, @@ -23571,6 +25207,8 @@ SQLITE_PRIVATE int wx_sqlite3OsFullPathname( } #ifndef SQLITE_OMIT_LOAD_EXTENSION SQLITE_PRIVATE void *wx_sqlite3OsDlOpen(wx_sqlite3_vfs *pVfs, const char *zPath){ + assert( zPath!=0 ); + assert( strlen(zPath)<=SQLITE_MAX_PATHLEN ); /* tag-20210611-1 */ return pVfs->xDlOpen(pVfs, zPath); } SQLITE_PRIVATE void wx_sqlite3OsDlError(wx_sqlite3_vfs *pVfs, int nByte, char *zBufOut){ @@ -23632,12 +25270,15 @@ SQLITE_PRIVATE int wx_sqlite3OsOpenMalloc( rc = wx_sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags); if( rc!=SQLITE_OK ){ wx_sqlite3_free(pFile); + *ppFile = 0; }else{ *ppFile = pFile; } }else{ + *ppFile = 0; rc = SQLITE_NOMEM_BKPT; } + assert( *ppFile!=0 || rc!=SQLITE_OK ); return rc; } SQLITE_PRIVATE void wx_sqlite3OsCloseFree(wx_sqlite3_file *pFile){ @@ -24355,7 +25996,7 @@ static void adjustStats(int iSize, int increment){ ** This routine checks the guards at either end of the allocation and ** if they are incorrect it asserts. */ -static struct MemBlockHdr *wx_sqlite3MemsysGetHeader(void *pAllocation){ +static struct MemBlockHdr *wx_sqlite3MemsysGetHeader(const void *pAllocation){ struct MemBlockHdr *p; int *pInt; u8 *pU8; @@ -24602,7 +26243,7 @@ SQLITE_PRIVATE void wx_sqlite3MemdebugSetType(void *p, u8 eType){ ** ** assert( wx_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); */ -SQLITE_PRIVATE int wx_sqlite3MemdebugHasType(void *p, u8 eType){ +SQLITE_PRIVATE int wx_sqlite3MemdebugHasType(const void *p, u8 eType){ int rc = 1; if( p && wx_sqlite3GlobalConfig.m.xFree==wx_sqlite3MemFree ){ struct MemBlockHdr *pHdr; @@ -24624,7 +26265,7 @@ SQLITE_PRIVATE int wx_sqlite3MemdebugHasType(void *p, u8 eType){ ** ** assert( wx_sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); */ -SQLITE_PRIVATE int wx_sqlite3MemdebugNoType(void *p, u8 eType){ +SQLITE_PRIVATE int wx_sqlite3MemdebugNoType(const void *p, u8 eType){ int rc = 1; if( p && wx_sqlite3GlobalConfig.m.xFree==wx_sqlite3MemFree ){ struct MemBlockHdr *pHdr; @@ -25847,8 +27488,17 @@ static void *memsys5Realloc(void *pPrior, int nBytes){ */ static int memsys5Roundup(int n){ int iFullSz; - if( n > 0x40000000 ) return 0; - for(iFullSz=mem5.szAtom; iFullSz0x10000000 ){ + if( n>0x40000000 ) return 0; + if( n>0x20000000 ) return 0x40000000; + return 0x20000000; + } + for(iFullSz=mem5.szAtom*8; iFullSz=(i64)n ) return iFullSz/2; return iFullSz; } @@ -27002,205 +28652,7 @@ SQLITE_PRIVATE wx_sqlite3_mutex_methods const *wx_sqlite3DefaultMutex(void){ /* ** Include code that is common to all os_*.c files */ -/************** Include os_common.h in the middle of mutex_w32.c *************/ -/************** Begin file os_common.h ***************************************/ -/* -** 2004 May 22 -** -** 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 macros and a little bit of code that is common to -** all of the platform-specific files (os_*.c) and is #included into those -** files. -** -** This file should be #included by the os_*.c files only. It is not a -** general purpose header file. -*/ -#ifndef _OS_COMMON_H_ -#define _OS_COMMON_H_ - -/* -** At least two bugs have slipped in because we changed the MEMORY_DEBUG -** macro to SQLITE_DEBUG and some older makefiles have not yet made the -** switch. The following code should catch this problem at compile-time. -*/ -#ifdef MEMORY_DEBUG -# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." -#endif - -/* -** Macros for performance tracing. Normally turned off. Only works -** on i486 hardware. -*/ -#ifdef SQLITE_PERFORMANCE_TRACE - -/* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of os_common.h ****************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** 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 inline asm code for retrieving "high-performance" -** counters for x86 and x86_64 class CPUs. -*/ -#ifndef SQLITE_HWTIME_H -#define SQLITE_HWTIME_H - -/* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if !defined(__STRICT_ANSI__) && \ - (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) - - #if defined(__GNUC__) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } - - #elif defined(_MSC_VER) - - __declspec(naked) __inline sqlite_uint64 __cdecl wx_sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } - } - - #endif - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; - } - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; - } - -#else - - /* - ** asm() is needed for hardware timing support. Without asm(), - ** disable the wx_sqlite3Hwtime() routine. - ** - ** wx_sqlite3Hwtime() is only used for some obscure debugging - ** and analysis configurations, not in any deliverable, so this - ** should not be a great loss. - */ -SQLITE_PRIVATE sqlite_uint64 wx_sqlite3Hwtime(void){ return ((sqlite_uint64)0); } - -#endif - -#endif /* !defined(SQLITE_HWTIME_H) */ - -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in os_common.h ******************/ - -static sqlite_uint64 g_start; -static sqlite_uint64 g_elapsed; -#define TIMER_START g_start=wx_sqlite3Hwtime() -#define TIMER_END g_elapsed=wx_sqlite3Hwtime()-g_start -#define TIMER_ELAPSED g_elapsed -#else -#define TIMER_START -#define TIMER_END -#define TIMER_ELAPSED ((sqlite_uint64)0) -#endif - -/* -** If we compile with the SQLITE_TEST macro set, then the following block -** of code will give us the ability to simulate a disk I/O error. This -** is used for testing the I/O recovery logic. -*/ -#if defined(SQLITE_TEST) -SQLITE_API extern int wx_sqlite3_io_error_hit; -SQLITE_API extern int wx_sqlite3_io_error_hardhit; -SQLITE_API extern int wx_sqlite3_io_error_pending; -SQLITE_API extern int wx_sqlite3_io_error_persist; -SQLITE_API extern int wx_sqlite3_io_error_benign; -SQLITE_API extern int wx_sqlite3_diskfull_pending; -SQLITE_API extern int wx_sqlite3_diskfull; -#define SimulateIOErrorBenign(X) wx_sqlite3_io_error_benign=(X) -#define SimulateIOError(CODE) \ - if( (wx_sqlite3_io_error_persist && wx_sqlite3_io_error_hit) \ - || wx_sqlite3_io_error_pending-- == 1 ) \ - { local_ioerr(); CODE; } -static void local_ioerr(){ - IOTRACE(("IOERR\n")); - wx_sqlite3_io_error_hit++; - if( !wx_sqlite3_io_error_benign ) wx_sqlite3_io_error_hardhit++; -} -#define SimulateDiskfullError(CODE) \ - if( wx_sqlite3_diskfull_pending ){ \ - if( wx_sqlite3_diskfull_pending == 1 ){ \ - local_ioerr(); \ - wx_sqlite3_diskfull = 1; \ - wx_sqlite3_io_error_hit = 1; \ - CODE; \ - }else{ \ - wx_sqlite3_diskfull_pending--; \ - } \ - } -#else -#define SimulateIOErrorBenign(X) -#define SimulateIOError(A) -#define SimulateDiskfullError(A) -#endif /* defined(SQLITE_TEST) */ - -/* -** When testing, keep a count of the number of open files. -*/ -#if defined(SQLITE_TEST) -SQLITE_API extern int wx_sqlite3_open_file_count; -#define OpenCounter(X) wx_sqlite3_open_file_count+=(X) -#else -#define OpenCounter(X) -#endif /* defined(SQLITE_TEST) */ - -#endif /* !defined(_OS_COMMON_H_) */ - -/************** End of os_common.h *******************************************/ -/************** Continuing where we left off in mutex_w32.c ******************/ +/* #include "os_common.h" */ /* ** Include the header file for the Windows VFS. @@ -27838,7 +29290,6 @@ SQLITE_PRIVATE int wx_sqlite3MallocInit(void){ if( wx_sqlite3GlobalConfig.m.xMalloc==0 ){ wx_sqlite3MemSetDefault(); } - memset(&mem0, 0, sizeof(mem0)); mem0.mutex = wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); if( wx_sqlite3GlobalConfig.pPage==0 || wx_sqlite3GlobalConfig.szPage<512 || wx_sqlite3GlobalConfig.nPage<=0 ){ @@ -27948,18 +29399,34 @@ static void mallocWithAlarm(int n, void **pp){ *pp = p; } +/* +** Maximum size of any single memory allocation. +** +** This is not a limit on the total amount of memory used. This is +** a limit on the size parameter to wx_sqlite3_malloc() and wx_sqlite3_realloc(). +** +** The upper bound is slightly less than 2GiB: 0x7ffffeff == 2,147,483,391 +** This provides a 256-byte safety margin for defense against 32-bit +** signed integer overflow bugs when computing memory allocation sizes. +** Paranoid applications might want to reduce the maximum allocation size +** further for an even larger safety margin. 0x3fffffff or 0x0fffffff +** or even smaller would be reasonable upper bounds on the size of a memory +** allocations for most applications. +*/ +#ifndef SQLITE_MAX_ALLOCATION_SIZE +# define SQLITE_MAX_ALLOCATION_SIZE 2147483391 +#endif +#if SQLITE_MAX_ALLOCATION_SIZE>2147483391 +# error Maximum size for SQLITE_MAX_ALLOCATION_SIZE is 2147483391 +#endif + /* ** Allocate memory. This routine is like wx_sqlite3_malloc() except that it ** assumes the memory subsystem has already been initialized. */ SQLITE_PRIVATE void *wx_sqlite3Malloc(u64 n){ void *p; - if( n==0 || n>=0x7fffff00 ){ - /* A memory allocation of a number of bytes which is near the maximum - ** signed integer value might cause an integer overflow inside of the - ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving - ** 255 bytes of overhead. SQLite itself will never use anything near - ** this amount. The only way to reach the limit is with wx_sqlite3_malloc() */ + if( n==0 || n>SQLITE_MAX_ALLOCATION_SIZE ){ p = 0; }else if( wx_sqlite3GlobalConfig.bMemstat ){ wx_sqlite3_mutex_enter(mem0.mutex); @@ -27994,8 +29461,8 @@ SQLITE_API void *wx_sqlite3_malloc64(wx_sqlite3_uint64 n){ ** TRUE if p is a lookaside memory allocation from db */ #ifndef SQLITE_OMIT_LOOKASIDE -static int isLookaside(wx_sqlite3 *db, void *p){ - return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd); +static int isLookaside(wx_sqlite3 *db, const void *p){ + return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pTrueEnd); } #else #define isLookaside(A,B) 0 @@ -28005,32 +29472,30 @@ static int isLookaside(wx_sqlite3 *db, void *p){ ** Return the size of a memory allocation previously obtained from ** wx_sqlite3Malloc() or wx_sqlite3_malloc(). */ -SQLITE_PRIVATE int wx_sqlite3MallocSize(void *p){ +SQLITE_PRIVATE int wx_sqlite3MallocSize(const void *p){ assert( wx_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - return wx_sqlite3GlobalConfig.m.xSize(p); + return wx_sqlite3GlobalConfig.m.xSize((void*)p); } -static int lookasideMallocSize(wx_sqlite3 *db, void *p){ +static int lookasideMallocSize(wx_sqlite3 *db, const void *p){ #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE return plookaside.pMiddle ? db->lookaside.szTrue : LOOKASIDE_SMALL; #else return db->lookaside.szTrue; #endif } -SQLITE_PRIVATE int wx_sqlite3DbMallocSize(wx_sqlite3 *db, void *p){ +SQLITE_PRIVATE int wx_sqlite3DbMallocSize(wx_sqlite3 *db, const void *p){ assert( p!=0 ); #ifdef SQLITE_DEBUG - if( db==0 || !isLookaside(db,p) ){ - if( db==0 ){ - assert( wx_sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); - assert( wx_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - }else{ - assert( wx_sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( wx_sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - } + if( db==0 ){ + assert( wx_sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); + assert( wx_sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); + }else if( !isLookaside(db,p) ){ + assert( wx_sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( wx_sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); } #endif if( db ){ - if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ + if( ((uptr)p)<(uptr)(db->lookaside.pTrueEnd) ){ #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ assert( wx_sqlite3_mutex_held(db->mutex) ); @@ -28043,7 +29508,7 @@ SQLITE_PRIVATE int wx_sqlite3DbMallocSize(wx_sqlite3 *db, void *p){ } } } - return wx_sqlite3GlobalConfig.m.xSize(p); + return wx_sqlite3GlobalConfig.m.xSize((void*)p); } SQLITE_API wx_sqlite3_uint64 wx_sqlite3_msize(void *p){ assert( wx_sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); @@ -28086,14 +29551,11 @@ SQLITE_PRIVATE void wx_sqlite3DbFreeNN(wx_sqlite3 *db, void *p){ assert( db==0 || wx_sqlite3_mutex_held(db->mutex) ); assert( p!=0 ); if( db ){ - if( db->pnBytesFreed ){ - measureAllocationSize(db, p); - return; - } if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ LookasideSlot *pBuf = (LookasideSlot*)p; + assert( db->pnBytesFreed==0 ); #ifdef SQLITE_DEBUG memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */ #endif @@ -28104,6 +29566,7 @@ SQLITE_PRIVATE void wx_sqlite3DbFreeNN(wx_sqlite3 *db, void *p){ #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){ LookasideSlot *pBuf = (LookasideSlot*)p; + assert( db->pnBytesFreed==0 ); #ifdef SQLITE_DEBUG memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */ #endif @@ -28112,6 +29575,10 @@ SQLITE_PRIVATE void wx_sqlite3DbFreeNN(wx_sqlite3 *db, void *p){ return; } } + if( db->pnBytesFreed ){ + measureAllocationSize(db, p); + return; + } } assert( wx_sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); assert( wx_sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); @@ -28119,6 +29586,43 @@ SQLITE_PRIVATE void wx_sqlite3DbFreeNN(wx_sqlite3 *db, void *p){ wx_sqlite3MemdebugSetType(p, MEMTYPE_HEAP); wx_sqlite3_free(p); } +SQLITE_PRIVATE void wx_sqlite3DbNNFreeNN(wx_sqlite3 *db, void *p){ + assert( db!=0 ); + assert( wx_sqlite3_mutex_held(db->mutex) ); + assert( p!=0 ); + if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ +#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ + LookasideSlot *pBuf = (LookasideSlot*)p; + assert( db->pnBytesFreed==0 ); +#ifdef SQLITE_DEBUG + memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */ +#endif + pBuf->pNext = db->lookaside.pSmallFree; + db->lookaside.pSmallFree = pBuf; + return; + } +#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ + if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){ + LookasideSlot *pBuf = (LookasideSlot*)p; + assert( db->pnBytesFreed==0 ); +#ifdef SQLITE_DEBUG + memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */ +#endif + pBuf->pNext = db->lookaside.pFree; + db->lookaside.pFree = pBuf; + return; + } + } + if( db->pnBytesFreed ){ + measureAllocationSize(db, p); + return; + } + assert( wx_sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( wx_sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + wx_sqlite3MemdebugSetType(p, MEMTYPE_HEAP); + wx_sqlite3_free(p); +} SQLITE_PRIVATE void wx_sqlite3DbFree(wx_sqlite3 *db, void *p){ assert( db==0 || wx_sqlite3_mutex_held(db->mutex) ); if( p ) wx_sqlite3DbFreeNN(db, p); @@ -28418,9 +29922,14 @@ SQLITE_PRIVATE char *wx_sqlite3DbStrNDup(wx_sqlite3 *db, const char *z, u64 n){ */ SQLITE_PRIVATE char *wx_sqlite3DbSpanDup(wx_sqlite3 *db, const char *zStart, const char *zEnd){ int n; +#ifdef SQLITE_DEBUG + /* Because of the way the parser works, the span is guaranteed to contain + ** at least one non-space character */ + for(n=0; wx_sqlite3Isspace(zStart[n]); n++){ assert( &zStart[n]0) && wx_sqlite3Isspace(zStart[n-1]) ) n--; + while( wx_sqlite3Isspace(zStart[n-1]) ) n--; return wx_sqlite3DbStrNDup(db, zStart, n); } @@ -28428,8 +29937,9 @@ SQLITE_PRIVATE char *wx_sqlite3DbSpanDup(wx_sqlite3 *db, const char *zStart, con ** Free any prior content in *pz and replace it with a copy of zNew. */ SQLITE_PRIVATE void wx_sqlite3SetString(char **pz, wx_sqlite3 *db, const char *zNew){ + char *z = wx_sqlite3DbStrDup(db, zNew); wx_sqlite3DbFree(db, *pz); - *pz = wx_sqlite3DbStrDup(db, zNew); + *pz = z; } /* @@ -28437,8 +29947,15 @@ SQLITE_PRIVATE void wx_sqlite3SetString(char **pz, wx_sqlite3 *db, const char *z ** has happened. This routine will set db->mallocFailed, and also ** temporarily disable the lookaside memory allocator and interrupt ** any running VDBEs. +** +** Always return a NULL pointer so that this routine can be invoked using +** +** return wx_sqlite3OomFault(db); +** +** and thereby avoid unnecessary stack frame allocations for the overwhelmingly +** common case where no OOM occurs. */ -SQLITE_PRIVATE void wx_sqlite3OomFault(wx_sqlite3 *db){ +SQLITE_PRIVATE void *wx_sqlite3OomFault(wx_sqlite3 *db){ if( db->mallocFailed==0 && db->bBenignMalloc==0 ){ db->mallocFailed = 1; if( db->nVdbeExec>0 ){ @@ -28446,9 +29963,16 @@ SQLITE_PRIVATE void wx_sqlite3OomFault(wx_sqlite3 *db){ } DisableLookaside; if( db->pParse ){ + Parse *pParse; + wx_sqlite3ErrorMsg(db->pParse, "out of memory"); db->pParse->rc = SQLITE_NOMEM_BKPT; + for(pParse=db->pParse->pOuterParse; pParse; pParse = pParse->pOuterParse){ + pParse->nErr++; + pParse->rc = SQLITE_NOMEM; + } } } + return 0; } /* @@ -28537,7 +30061,7 @@ SQLITE_PRIVATE int wx_sqlite3ApiExit(wx_sqlite3* db, int rc){ #define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '', NULL pointers replaced by SQL NULL. %Q */ #define etTOKEN 11 /* a pointer to a Token structure */ -#define etSRCLIST 12 /* a pointer to a SrcList */ +#define etSRCITEM 12 /* a pointer to a SrcItem */ #define etPOINTER 13 /* The %p conversion */ #define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */ #define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ @@ -28603,10 +30127,16 @@ static const et_info fmtinfo[] = { /* All the rest are undocumented and are for internal use only */ { 'T', 0, 0, etTOKEN, 0, 0 }, - { 'S', 0, 0, etSRCLIST, 0, 0 }, + { 'S', 0, 0, etSRCITEM, 0, 0 }, { 'r', 10, 1, etORDINAL, 0, 0 }, }; +/* Notes: +** +** %S Takes a pointer to SrcItem. Shows name or database.name +** %!S Like %S but prefer the zName over the zAlias +*/ + /* Floating point constants used for rounding */ static const double arRound[] = { 5.0e-01, 5.0e-02, 5.0e-03, 5.0e-04, 5.0e-05, @@ -28647,7 +30177,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ /* ** Set the StrAccum object to an error mode. */ -static void setStrAccumError(StrAccum *p, u8 eError){ +SQLITE_PRIVATE void wx_sqlite3StrAccumSetError(StrAccum *p, u8 eError){ assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG ); p->accError = eError; if( p->mxAlloc ) wx_sqlite3_str_reset(p); @@ -28683,12 +30213,12 @@ static char *printfTempBuf(wx_sqlite3_str *pAccum, wx_sqlite3_int64 n){ char *z; if( pAccum->accError ) return 0; if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){ - setStrAccumError(pAccum, SQLITE_TOOBIG); + wx_sqlite3StrAccumSetError(pAccum, SQLITE_TOOBIG); return 0; } z = wx_sqlite3DbMallocRaw(pAccum->db, n); if( z==0 ){ - setStrAccumError(pAccum, SQLITE_NOMEM); + wx_sqlite3StrAccumSetError(pAccum, SQLITE_NOMEM); } return z; } @@ -29238,13 +30768,26 @@ SQLITE_API void wx_sqlite3_str_vappendf( } } if( precision>1 ){ + i64 nPrior = 1; width -= precision-1; if( width>1 && !flag_leftjustify ){ wx_sqlite3_str_appendchar(pAccum, width-1, ' '); width = 0; } - while( precision-- > 1 ){ - wx_sqlite3_str_append(pAccum, buf, length); + wx_sqlite3_str_append(pAccum, buf, length); + precision--; + while( precision > 1 ){ + i64 nCopyBytes; + if( nPrior > precision-1 ) nPrior = precision - 1; + nCopyBytes = length*nPrior; + if( nCopyBytes + pAccum->nChar >= pAccum->nAlloc ){ + wx_sqlite3StrAccumEnlarge(pAccum, nCopyBytes); + } + if( pAccum->accError ) break; + wx_sqlite3_str_append(pAccum, + &pAccum->zText[pAccum->nChar-nCopyBytes], nCopyBytes); + precision -= nPrior; + nPrior *= 2; } } bufpt = buf; @@ -29305,8 +30848,8 @@ SQLITE_API void wx_sqlite3_str_vappendf( case etSQLESCAPE: /* %q: Escape ' characters */ case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */ case etSQLESCAPE3: { /* %w: Escape " characters */ - int i, j, k, n, isnull; - int needQuote; + i64 i, j, k, n; + int needQuote, isnull; char ch; char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ char *escarg; @@ -29351,31 +30894,50 @@ SQLITE_API void wx_sqlite3_str_vappendf( goto adjust_width_for_utf8; } case etTOKEN: { - Token *pToken; if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; - pToken = va_arg(ap, Token*); - assert( bArgList==0 ); - if( pToken && pToken->n ){ - wx_sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n); + if( flag_alternateform ){ + /* %#T means an Expr pointer that uses Expr.u.zToken */ + Expr *pExpr = va_arg(ap,Expr*); + if( ALWAYS(pExpr) && ALWAYS(!ExprHasProperty(pExpr,EP_IntValue)) ){ + wx_sqlite3_str_appendall(pAccum, (const char*)pExpr->u.zToken); + wx_sqlite3RecordErrorOffsetOfExpr(pAccum->db, pExpr); + } + }else{ + /* %T means a Token pointer */ + Token *pToken = va_arg(ap, Token*); + assert( bArgList==0 ); + if( pToken && pToken->n ){ + wx_sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n); + wx_sqlite3RecordErrorByteOffset(pAccum->db, pToken->z); + } } length = width = 0; break; } - case etSRCLIST: { - SrcList *pSrc; - int k; + case etSRCITEM: { SrcItem *pItem; if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; - pSrc = va_arg(ap, SrcList*); - k = va_arg(ap, int); - pItem = &pSrc->a[k]; + pItem = va_arg(ap, SrcItem*); assert( bArgList==0 ); - assert( k>=0 && knSrc ); - if( pItem->zDatabase ){ - wx_sqlite3_str_appendall(pAccum, pItem->zDatabase); - wx_sqlite3_str_append(pAccum, ".", 1); + if( pItem->zAlias && !flag_altform2 ){ + wx_sqlite3_str_appendall(pAccum, pItem->zAlias); + }else if( pItem->zName ){ + if( pItem->zDatabase ){ + wx_sqlite3_str_appendall(pAccum, pItem->zDatabase); + wx_sqlite3_str_append(pAccum, ".", 1); + } + wx_sqlite3_str_appendall(pAccum, pItem->zName); + }else if( pItem->zAlias ){ + wx_sqlite3_str_appendall(pAccum, pItem->zAlias); + }else{ + Select *pSel = pItem->pSelect; + assert( pSel!=0 ); + if( pSel->selFlags & SF_NestedFrom ){ + wx_sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); + }else{ + wx_sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId); + } } - wx_sqlite3_str_appendall(pAccum, pItem->zName); length = width = 0; break; } @@ -29408,6 +30970,44 @@ SQLITE_API void wx_sqlite3_str_vappendf( }/* End for loop over the format string */ } /* End of function */ + +/* +** The z string points to the first character of a token that is +** associated with an error. If db does not already have an error +** byte offset recorded, try to compute the error byte offset for +** z and set the error byte offset in db. +*/ +SQLITE_PRIVATE void wx_sqlite3RecordErrorByteOffset(wx_sqlite3 *db, const char *z){ + const Parse *pParse; + const char *zText; + const char *zEnd; + assert( z!=0 ); + if( NEVER(db==0) ) return; + if( db->errByteOffset!=(-2) ) return; + pParse = db->pParse; + if( NEVER(pParse==0) ) return; + zText =pParse->zTail; + if( NEVER(zText==0) ) return; + zEnd = &zText[strlen(zText)]; + if( SQLITE_WITHIN(z,zText,zEnd) ){ + db->errByteOffset = (int)(z-zText); + } +} + +/* +** If pExpr has a byte offset for the start of a token, record that as +** as the error offset. +*/ +SQLITE_PRIVATE void wx_sqlite3RecordErrorOffsetOfExpr(wx_sqlite3 *db, const Expr *pExpr){ + while( pExpr + && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0) + ){ + pExpr = pExpr->pLeft; + } + if( pExpr==0 ) return; + db->errByteOffset = pExpr->w.iOfst; +} + /* ** Enlarge the memory allocation on a StrAccum object so that it is ** able to accept at least N more bytes of text. @@ -29415,21 +31015,20 @@ SQLITE_API void wx_sqlite3_str_vappendf( ** Return the number of bytes of text that StrAccum is able to accept ** after the attempted enlargement. The value returned might be zero. */ -static int wx_sqlite3StrAccumEnlarge(StrAccum *p, int N){ +SQLITE_PRIVATE int wx_sqlite3StrAccumEnlarge(StrAccum *p, i64 N){ char *zNew; - assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */ + assert( p->nChar+N >= p->nAlloc ); /* Only called if really needed */ if( p->accError ){ testcase(p->accError==SQLITE_TOOBIG); testcase(p->accError==SQLITE_NOMEM); return 0; } if( p->mxAlloc==0 ){ - setStrAccumError(p, SQLITE_TOOBIG); + wx_sqlite3StrAccumSetError(p, SQLITE_TOOBIG); return p->nAlloc - p->nChar - 1; }else{ char *zOld = isMalloced(p) ? p->zText : 0; - i64 szNew = p->nChar; - szNew += (wx_sqlite3_int64)N + 1; + i64 szNew = p->nChar + N + 1; if( szNew+p->nChar<=p->mxAlloc ){ /* Force exponential buffer size growth as long as it does not overflow, ** to avoid having to call this routine too often */ @@ -29437,7 +31036,7 @@ static int wx_sqlite3StrAccumEnlarge(StrAccum *p, int N){ } if( szNew > p->mxAlloc ){ wx_sqlite3_str_reset(p); - setStrAccumError(p, SQLITE_TOOBIG); + wx_sqlite3StrAccumSetError(p, SQLITE_TOOBIG); return 0; }else{ p->nAlloc = (int)szNew; @@ -29455,11 +31054,12 @@ static int wx_sqlite3StrAccumEnlarge(StrAccum *p, int N){ p->printfFlags |= SQLITE_PRINTF_MALLOCED; }else{ wx_sqlite3_str_reset(p); - setStrAccumError(p, SQLITE_NOMEM); + wx_sqlite3StrAccumSetError(p, SQLITE_NOMEM); return 0; } } - return N; + assert( N>=0 && N<=0x7fffffff ); + return (int)N; } /* @@ -29528,7 +31128,7 @@ static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){ memcpy(zText, p->zText, p->nChar+1); p->printfFlags |= SQLITE_PRINTF_MALLOCED; }else{ - setStrAccumError(p, SQLITE_NOMEM); + wx_sqlite3StrAccumSetError(p, SQLITE_NOMEM); } p->zText = zText; return zText; @@ -29543,6 +31143,22 @@ SQLITE_PRIVATE char *wx_sqlite3StrAccumFinish(StrAccum *p){ return p->zText; } +/* +** Use the content of the StrAccum passed as the second argument +** as the result of an SQL function. +*/ +SQLITE_PRIVATE void wx_sqlite3ResultStrAccum(wx_sqlite3_context *pCtx, StrAccum *p){ + if( p->accError ){ + wx_sqlite3_result_error_code(pCtx, p->accError); + wx_sqlite3_str_reset(p); + }else if( isMalloced(p) ){ + wx_sqlite3_result_text(pCtx, p->zText, p->nChar, SQLITE_DYNAMIC); + }else{ + wx_sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC); + wx_sqlite3_str_reset(p); + } +} + /* ** This singleton is an wx_sqlite3_str object that is returned if ** wx_sqlite3_malloc() fails to provide space for a real one. This @@ -29845,40 +31461,44 @@ SQLITE_API void wx_sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ ** Add a new subitem to the tree. The moreToFollow flag indicates that this ** is not the last item in the tree. */ -static TreeView *wx_sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){ +static void wx_sqlite3TreeViewPush(TreeView **pp, u8 moreToFollow){ + TreeView *p = *pp; if( p==0 ){ - p = wx_sqlite3_malloc64( sizeof(*p) ); - if( p==0 ) return 0; + *pp = p = wx_sqlite3_malloc64( sizeof(*p) ); + if( p==0 ) return; memset(p, 0, sizeof(*p)); }else{ p->iLevel++; } assert( moreToFollow==0 || moreToFollow==1 ); - if( p->iLevelbLine) ) p->bLine[p->iLevel] = moreToFollow; - return p; + if( p->iLevel<(int)sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow; } /* ** Finished with one layer of the tree */ -static void wx_sqlite3TreeViewPop(TreeView *p){ +static void wx_sqlite3TreeViewPop(TreeView **pp){ + TreeView *p = *pp; if( p==0 ) return; p->iLevel--; - if( p->iLevel<0 ) wx_sqlite3_free(p); + if( p->iLevel<0 ){ + wx_sqlite3_free(p); + *pp = 0; + } } /* ** Generate a single line of output for the tree, with a prefix that contains ** all the appropriate tree lines */ -static void wx_sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ +SQLITE_PRIVATE void wx_sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ va_list ap; int i; StrAccum acc; - char zBuf[500]; + char zBuf[1000]; wx_sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); if( p ){ - for(i=0; iiLevel && ibLine)-1; i++){ + for(i=0; iiLevel && i<(int)sizeof(p->bLine)-1; i++){ wx_sqlite3_str_append(&acc, p->bLine[i] ? "| " : " ", 4); } wx_sqlite3_str_append(&acc, p->bLine[i] ? "|-- " : "'-- ", 4); @@ -29899,10 +31519,57 @@ static void wx_sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ ** Shorthand for starting a new tree item that consists of a single label */ static void wx_sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){ - p = wx_sqlite3TreeViewPush(p, moreFollows); + wx_sqlite3TreeViewPush(&p, moreFollows); wx_sqlite3TreeViewLine(p, "%s", zLabel); } +/* +** Show a list of Column objects in tree format. +*/ +SQLITE_PRIVATE void wx_sqlite3TreeViewColumnList( + TreeView *pView, + const Column *aCol, + int nCol, + u8 moreToFollow +){ + int i; + wx_sqlite3TreeViewPush(&pView, moreToFollow); + wx_sqlite3TreeViewLine(pView, "COLUMNS"); + for(i=0; inCte>0 ){ - pView = wx_sqlite3TreeViewPush(pView, 1); + wx_sqlite3TreeViewPush(&pView, moreToFollow); for(i=0; inCte; i++){ StrAccum x; char zLine[1000]; @@ -29932,6 +31599,10 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewWith(TreeView *pView, const With *pWith, u } wx_sqlite3_str_appendf(&x, ")"); } + if( pCte->eM10d!=M10d_Any ){ + wx_sqlite3_str_appendf(&x, " %sMATERIALIZED", + pCte->eM10d==M10d_No ? "NOT " : ""); + } if( pCte->pUse ){ wx_sqlite3_str_appendf(&x, " (pUse=0x%p, nUse=%d)", pCte->pUse, pCte->pUse->nUse); @@ -29939,9 +31610,9 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewWith(TreeView *pView, const With *pWith, u wx_sqlite3StrAccumFinish(&x); wx_sqlite3TreeViewItem(pView, zLine, inCte-1); wx_sqlite3TreeViewSelect(pView, pCte->pSelect, 0); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } } @@ -29950,26 +31621,30 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewWith(TreeView *pView, const With *pWith, u */ SQLITE_PRIVATE void wx_sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){ int i; + if( pSrc==0 ) return; for(i=0; inSrc; i++){ const SrcItem *pItem = &pSrc->a[i]; StrAccum x; - char zLine[100]; + int n = 0; + char zLine[1000]; wx_sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); - wx_sqlite3_str_appendf(&x, "{%d:*}", pItem->iCursor); - if( pItem->zDatabase ){ - wx_sqlite3_str_appendf(&x, " %s.%s", pItem->zDatabase, pItem->zName); - }else if( pItem->zName ){ - wx_sqlite3_str_appendf(&x, " %s", pItem->zName); - } + x.printfFlags |= SQLITE_PRINTF_INTERNAL; + wx_sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); if( pItem->pTab ){ wx_sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx", pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed); } - if( pItem->zAlias ){ - wx_sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias); - } - if( pItem->fg.jointype & JT_LEFT ){ + if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){ + wx_sqlite3_str_appendf(&x, " FULL-OUTER-JOIN"); + }else if( pItem->fg.jointype & JT_LEFT ){ wx_sqlite3_str_appendf(&x, " LEFT-JOIN"); + }else if( pItem->fg.jointype & JT_RIGHT ){ + wx_sqlite3_str_appendf(&x, " RIGHT-JOIN"); + }else if( pItem->fg.jointype & JT_CROSS ){ + wx_sqlite3_str_appendf(&x, " CROSS-JOIN"); + } + if( pItem->fg.jointype & JT_LTORJ ){ + wx_sqlite3_str_appendf(&x, " LTORJ"); } if( pItem->fg.fromDDL ){ wx_sqlite3_str_appendf(&x, " DDL"); @@ -29977,15 +31652,37 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pS if( pItem->fg.isCte ){ wx_sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse); } + if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ + wx_sqlite3_str_appendf(&x, " ON"); + } + if( pItem->fg.isTabFunc ) wx_sqlite3_str_appendf(&x, " isTabFunc"); + if( pItem->fg.isCorrelated ) wx_sqlite3_str_appendf(&x, " isCorrelated"); + if( pItem->fg.isMaterialized ) wx_sqlite3_str_appendf(&x, " isMaterialized"); + if( pItem->fg.viaCoroutine ) wx_sqlite3_str_appendf(&x, " viaCoroutine"); + if( pItem->fg.notCte ) wx_sqlite3_str_appendf(&x, " notCte"); + if( pItem->fg.isNestedFrom ) wx_sqlite3_str_appendf(&x, " isNestedFrom"); + wx_sqlite3StrAccumFinish(&x); wx_sqlite3TreeViewItem(pView, zLine, inSrc-1); + n = 0; + if( pItem->pSelect ) n++; + if( pItem->fg.isTabFunc ) n++; + if( pItem->fg.isUsing ) n++; + if( pItem->fg.isUsing ){ + wx_sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); + } if( pItem->pSelect ){ - wx_sqlite3TreeViewSelect(pView, pItem->pSelect, 0); + if( pItem->pTab ){ + Table *pTab = pItem->pTab; + wx_sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); + } + assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); + wx_sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0); } if( pItem->fg.isTabFunc ){ wx_sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); } - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } } @@ -29999,11 +31696,11 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewSelect(TreeView *pView, const Select *p, u wx_sqlite3TreeViewLine(pView, "nil-SELECT"); return; } - pView = wx_sqlite3TreeViewPush(pView, moreToFollow); + wx_sqlite3TreeViewPush(&pView, moreToFollow); if( p->pWith ){ wx_sqlite3TreeViewWith(pView, p->pWith, 1); cnt = 1; - wx_sqlite3TreeViewPush(pView, 1); + wx_sqlite3TreeViewPush(&pView, 1); } do{ if( p->selFlags & SF_WhereBegin ){ @@ -30017,7 +31714,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewSelect(TreeView *pView, const Select *p, u (int)p->nSelectRow ); } - if( cnt++ ) wx_sqlite3TreeViewPop(pView); + if( cnt++ ) wx_sqlite3TreeViewPop(&pView); if( p->pPrior ){ n = 1000; }else{ @@ -30040,24 +31737,24 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewSelect(TreeView *pView, const Select *p, u #ifndef SQLITE_OMIT_WINDOWFUNC if( p->pWin ){ Window *pX; - pView = wx_sqlite3TreeViewPush(pView, (n--)>0); + wx_sqlite3TreeViewPush(&pView, (n--)>0); wx_sqlite3TreeViewLine(pView, "window-functions"); for(pX=p->pWin; pX; pX=pX->pNextWin){ wx_sqlite3TreeViewWinFunc(pView, pX, pX->pNextWin!=0); } - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } #endif if( p->pSrc && p->pSrc->nSrc ){ - pView = wx_sqlite3TreeViewPush(pView, (n--)>0); + wx_sqlite3TreeViewPush(&pView, (n--)>0); wx_sqlite3TreeViewLine(pView, "FROM"); wx_sqlite3TreeViewSrcList(pView, p->pSrc); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } if( p->pWhere ){ wx_sqlite3TreeViewItem(pView, "WHERE", (n--)>0); wx_sqlite3TreeViewExpr(pView, p->pWhere, 0); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } if( p->pGroupBy ){ wx_sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY"); @@ -30065,7 +31762,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewSelect(TreeView *pView, const Select *p, u if( p->pHaving ){ wx_sqlite3TreeViewItem(pView, "HAVING", (n--)>0); wx_sqlite3TreeViewExpr(pView, p->pHaving, 0); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } #ifndef SQLITE_OMIT_WINDOWFUNC if( p->pWinDefn ){ @@ -30074,7 +31771,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewSelect(TreeView *pView, const Select *p, u for(pX=p->pWinDefn; pX; pX=pX->pNextWin){ wx_sqlite3TreeViewWindow(pView, pX, pX->pNextWin!=0); } - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } #endif if( p->pOrderBy ){ @@ -30086,9 +31783,9 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewSelect(TreeView *pView, const Select *p, u if( p->pLimit->pRight ){ wx_sqlite3TreeViewItem(pView, "OFFSET", (n--)>0); wx_sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } if( p->pPrior ){ const char *zOp = "UNION"; @@ -30101,7 +31798,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewSelect(TreeView *pView, const Select *p, u } p = p->pPrior; }while( p!=0 ); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } #ifndef SQLITE_OMIT_WINDOWFUNC @@ -30117,24 +31814,24 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewBound( switch( eBound ){ case TK_UNBOUNDED: { wx_sqlite3TreeViewItem(pView, "UNBOUNDED", moreToFollow); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); break; } case TK_CURRENT: { wx_sqlite3TreeViewItem(pView, "CURRENT", moreToFollow); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); break; } case TK_PRECEDING: { wx_sqlite3TreeViewItem(pView, "PRECEDING", moreToFollow); wx_sqlite3TreeViewExpr(pView, pExpr, 0); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); break; } case TK_FOLLOWING: { wx_sqlite3TreeViewItem(pView, "FOLLOWING", moreToFollow); wx_sqlite3TreeViewExpr(pView, pExpr, 0); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); break; } } @@ -30147,12 +31844,13 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewBound( */ SQLITE_PRIVATE void wx_sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){ int nElement = 0; + if( pWin==0 ) return; if( pWin->pFilter ){ wx_sqlite3TreeViewItem(pView, "FILTER", 1); wx_sqlite3TreeViewExpr(pView, pWin->pFilter, 0); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } - pView = wx_sqlite3TreeViewPush(pView, more); + wx_sqlite3TreeViewPush(&pView, more); if( pWin->zName ){ wx_sqlite3TreeViewLine(pView, "OVER %s (%p)", pWin->zName, pWin); }else{ @@ -30163,9 +31861,9 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewWindow(TreeView *pView, const Window *pWin if( pWin->eFrmType ) nElement++; if( pWin->eExclude ) nElement++; if( pWin->zBase ){ - wx_sqlite3TreeViewPush(pView, (--nElement)>0); + wx_sqlite3TreeViewPush(&pView, (--nElement)>0); wx_sqlite3TreeViewLine(pView, "window: %s", pWin->zBase); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } if( pWin->pPartition ){ wx_sqlite3TreeViewExprList(pView, pWin->pPartition, nElement>0,"PARTITION-BY"); @@ -30183,7 +31881,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewWindow(TreeView *pView, const Window *pWin wx_sqlite3TreeViewItem(pView, zBuf, (--nElement)>0); wx_sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1); wx_sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } if( pWin->eExclude ){ char zBuf[30]; @@ -30198,11 +31896,11 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewWindow(TreeView *pView, const Window *pWin zExclude = zBuf; break; } - wx_sqlite3TreeViewPush(pView, 0); + wx_sqlite3TreeViewPush(&pView, 0); wx_sqlite3TreeViewLine(pView, "EXCLUDE %s", zExclude); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } #endif /* SQLITE_OMIT_WINDOWFUNC */ @@ -30211,11 +31909,12 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewWindow(TreeView *pView, const Window *pWin ** Generate a human-readable explanation for a Window Function object */ SQLITE_PRIVATE void wx_sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin, u8 more){ - pView = wx_sqlite3TreeViewPush(pView, more); + if( pWin==0 ) return; + wx_sqlite3TreeViewPush(&pView, more); wx_sqlite3TreeViewLine(pView, "WINFUNC %s(%d)", - pWin->pFunc->zName, pWin->pFunc->nArg); + pWin->pWFunc->zName, pWin->pWFunc->nArg); wx_sqlite3TreeViewWindow(pView, pWin, 0); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } #endif /* SQLITE_OMIT_WINDOWFUNC */ @@ -30226,19 +31925,22 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u const char *zBinOp = 0; /* Binary operator */ const char *zUniOp = 0; /* Unary operator */ char zFlgs[200]; - pView = wx_sqlite3TreeViewPush(pView, moreToFollow); + wx_sqlite3TreeViewPush(&pView, moreToFollow); if( pExpr==0 ){ wx_sqlite3TreeViewLine(pView, "nil"); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); return; } - if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){ + if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags || pExpr->pAggInfo ){ StrAccum x; wx_sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0); wx_sqlite3_str_appendf(&x, " fg.af=%x.%c", pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n'); - if( ExprHasProperty(pExpr, EP_FromJoin) ){ - wx_sqlite3_str_appendf(&x, " iRJT=%d", pExpr->iRightJoinTable); + if( ExprHasProperty(pExpr, EP_OuterON) ){ + wx_sqlite3_str_appendf(&x, " outer.iJoin=%d", pExpr->w.iJoin); + } + if( ExprHasProperty(pExpr, EP_InnerON) ){ + wx_sqlite3_str_appendf(&x, " inner.iJoin=%d", pExpr->w.iJoin); } if( ExprHasProperty(pExpr, EP_FromDDL) ){ wx_sqlite3_str_appendf(&x, " DDL"); @@ -30246,6 +31948,9 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u if( ExprHasVVAProperty(pExpr, EP_Immutable) ){ wx_sqlite3_str_appendf(&x, " IMMUTABLE"); } + if( pExpr->pAggInfo!=0 ){ + wx_sqlite3_str_appendf(&x, " agg-column[%d]", pExpr->iAgg); + } wx_sqlite3StrAccumFinish(&x); }else{ zFlgs[0] = 0; @@ -30268,6 +31973,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u wx_sqlite3TreeViewLine(pView, "COLUMN(%d)%s%s", pExpr->iColumn, zFlgs, zOp2); }else{ + assert( ExprUseYTab(pExpr) ); wx_sqlite3TreeViewLine(pView, "{%d:%d} pTab=%p%s", pExpr->iTable, pExpr->iColumn, pExpr->y.pTab, zFlgs); @@ -30287,11 +31993,13 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u } #ifndef SQLITE_OMIT_FLOATING_POINT case TK_FLOAT: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); wx_sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); break; } #endif case TK_STRING: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); wx_sqlite3TreeViewLine(pView,"%Q", pExpr->u.zToken); break; } @@ -30300,17 +32008,19 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u break; } case TK_TRUEFALSE: { - wx_sqlite3TreeViewLine(pView, - wx_sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE"); + wx_sqlite3TreeViewLine(pView,"%s%s", + wx_sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE", zFlgs); break; } #ifndef SQLITE_OMIT_BLOB_LITERAL case TK_BLOB: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); wx_sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); break; } #endif case TK_VARIABLE: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); wx_sqlite3TreeViewLine(pView,"VARIABLE(%s,%d)", pExpr->u.zToken, pExpr->iColumn); break; @@ -30320,12 +32030,14 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u break; } case TK_ID: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); wx_sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken); break; } #ifndef SQLITE_OMIT_CAST case TK_CAST: { /* Expressions of the form: CAST(pLeft AS token) */ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); wx_sqlite3TreeViewLine(pView,"CAST %Q", pExpr->u.zToken); wx_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); break; @@ -30375,6 +32087,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u } case TK_SPAN: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); wx_sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken); wx_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); break; @@ -30386,6 +32099,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u ** up in the treeview output as "SOFT-COLLATE". Explicit COLLATE ** operators that appear in the original SQL always have the ** EP_Collate bit set and appear in treeview output as just "COLLATE" */ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); wx_sqlite3TreeViewLine(pView, "%sCOLLATE %Q%s", !ExprHasProperty(pExpr, EP_Collate) ? "SOFT-" : "", pExpr->u.zToken, zFlgs); @@ -30401,6 +32115,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u pFarg = 0; pWin = 0; }else{ + assert( ExprUseXList(pExpr) ); pFarg = pExpr->x.pList; #ifndef SQLITE_OMIT_WINDOWFUNC pWin = ExprHasProperty(pExpr, EP_WinFunc) ? pExpr->y.pWin : 0; @@ -30408,6 +32123,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u pWin = 0; #endif } + assert( !ExprHasProperty(pExpr, EP_IntValue) ); if( pExpr->op==TK_AGG_FUNCTION ){ wx_sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s agg=%d[%d]/%p", pExpr->op2, pExpr->u.zToken, zFlgs, @@ -30439,19 +32155,31 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u } #ifndef SQLITE_OMIT_SUBQUERY case TK_EXISTS: { + assert( ExprUseXSelect(pExpr) ); wx_sqlite3TreeViewLine(pView, "EXISTS-expr flags=0x%x", pExpr->flags); wx_sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); break; } case TK_SELECT: { + assert( ExprUseXSelect(pExpr) ); wx_sqlite3TreeViewLine(pView, "subquery-expr flags=0x%x", pExpr->flags); wx_sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); break; } case TK_IN: { - wx_sqlite3TreeViewLine(pView, "IN flags=0x%x", pExpr->flags); + wx_sqlite3_str *pStr = wx_sqlite3_str_new(0); + char *z; + wx_sqlite3_str_appendf(pStr, "IN flags=0x%x", pExpr->flags); + if( pExpr->iTable ) wx_sqlite3_str_appendf(pStr, " iTable=%d",pExpr->iTable); + if( ExprHasProperty(pExpr, EP_Subrtn) ){ + wx_sqlite3_str_appendf(pStr, " subrtn(%d,%d)", + pExpr->y.sub.regReturn, pExpr->y.sub.iAddr); + } + z = wx_sqlite3_str_finish(pStr); + wx_sqlite3TreeViewLine(pView, z); + wx_sqlite3_free(z); wx_sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ wx_sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); }else{ wx_sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); @@ -30472,9 +32200,12 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u ** Z is stored in pExpr->pList->a[1].pExpr. */ case TK_BETWEEN: { - Expr *pX = pExpr->pLeft; - Expr *pY = pExpr->x.pList->a[0].pExpr; - Expr *pZ = pExpr->x.pList->a[1].pExpr; + const Expr *pX, *pY, *pZ; + pX = pExpr->pLeft; + assert( ExprUseXList(pExpr) ); + assert( pExpr->x.pList->nExpr==2 ); + pY = pExpr->x.pList->a[0].pExpr; + pZ = pExpr->x.pList->a[1].pExpr; wx_sqlite3TreeViewLine(pView, "BETWEEN"); wx_sqlite3TreeViewExpr(pView, pX, 1); wx_sqlite3TreeViewExpr(pView, pY, 1); @@ -30496,6 +32227,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u case TK_CASE: { wx_sqlite3TreeViewLine(pView, "CASE"); wx_sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); + assert( ExprUseXList(pExpr) ); wx_sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); break; } @@ -30508,6 +32240,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u case OE_Fail: zType = "fail"; break; case OE_Ignore: zType = "ignore"; break; } + assert( !ExprHasProperty(pExpr, EP_IntValue) ); wx_sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken); break; } @@ -30520,12 +32253,16 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u } case TK_VECTOR: { char *z = wx_sqlite3_mprintf("VECTOR%s",zFlgs); + assert( ExprUseXList(pExpr) ); wx_sqlite3TreeViewBareExprList(pView, pExpr->x.pList, z); wx_sqlite3_free(z); break; } case TK_SELECT_COLUMN: { - wx_sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn); + wx_sqlite3TreeViewLine(pView, "SELECT-COLUMN %d of [0..%d]%s", + pExpr->iColumn, pExpr->iTable-1, + pExpr->pRight==pExpr->pLeft ? " (SELECT-owner)" : ""); + assert( ExprUseXSelect(pExpr->pLeft) ); wx_sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0); break; } @@ -30534,6 +32271,23 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u wx_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); break; } + case TK_ERROR: { + Expr tmp; + wx_sqlite3TreeViewLine(pView, "ERROR"); + tmp = *pExpr; + tmp.op = pExpr->op2; + wx_sqlite3TreeViewExpr(pView, &tmp, 0); + break; + } + case TK_ROW: { + if( pExpr->iColumn<=0 ){ + wx_sqlite3TreeViewLine(pView, "First FROM table rowid"); + }else{ + wx_sqlite3TreeViewLine(pView, "First FROM table column %d", + pExpr->iColumn-1); + } + break; + } default: { wx_sqlite3TreeViewLine(pView, "op=%d", pExpr->op); break; @@ -30547,7 +32301,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u wx_sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs); wx_sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); } - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } @@ -30569,13 +32323,25 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewBareExprList( int j = pList->a[i].u.x.iOrderByCol; char *zName = pList->a[i].zEName; int moreToFollow = inExpr - 1; - if( pList->a[i].eEName!=ENAME_NAME ) zName = 0; if( j || zName ){ - wx_sqlite3TreeViewPush(pView, moreToFollow); + wx_sqlite3TreeViewPush(&pView, moreToFollow); moreToFollow = 0; wx_sqlite3TreeViewLine(pView, 0); if( zName ){ - fprintf(stdout, "AS %s ", zName); + switch( pList->a[i].fg.eEName ){ + default: + fprintf(stdout, "AS %s ", zName); + break; + case ENAME_TAB: + fprintf(stdout, "TABLE-ALIAS-NAME(\"%s\") ", zName); + if( pList->a[i].fg.bUsed ) fprintf(stdout, "(used) "); + if( pList->a[i].fg.bUsingTerm ) fprintf(stdout, "(USING-term) "); + if( pList->a[i].fg.bNoExpand ) fprintf(stdout, "(NoExpand) "); + break; + case ENAME_SPAN: + fprintf(stdout, "SPAN(\"%s\") ", zName); + break; + } } if( j ){ fprintf(stdout, "iOrderByCol=%d", j); @@ -30585,7 +32351,7 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewBareExprList( } wx_sqlite3TreeViewExpr(pView, pList->a[i].pExpr, moreToFollow); if( j || zName ){ - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); } } } @@ -30596,10 +32362,377 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExprList( u8 moreToFollow, const char *zLabel ){ - pView = wx_sqlite3TreeViewPush(pView, moreToFollow); + wx_sqlite3TreeViewPush(&pView, moreToFollow); wx_sqlite3TreeViewBareExprList(pView, pList, zLabel); - wx_sqlite3TreeViewPop(pView); + wx_sqlite3TreeViewPop(&pView); +} + +/* +** Generate a human-readable explanation of an id-list. +*/ +SQLITE_PRIVATE void wx_sqlite3TreeViewBareIdList( + TreeView *pView, + const IdList *pList, + const char *zLabel +){ + if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST"; + if( pList==0 ){ + wx_sqlite3TreeViewLine(pView, "%s (empty)", zLabel); + }else{ + int i; + wx_sqlite3TreeViewLine(pView, "%s", zLabel); + for(i=0; inId; i++){ + char *zName = pList->a[i].zName; + int moreToFollow = inId - 1; + if( zName==0 ) zName = "(null)"; + wx_sqlite3TreeViewPush(&pView, moreToFollow); + wx_sqlite3TreeViewLine(pView, 0); + if( pList->eU4==EU4_NONE ){ + fprintf(stdout, "%s\n", zName); + }else if( pList->eU4==EU4_IDX ){ + fprintf(stdout, "%s (%d)\n", zName, pList->a[i].u4.idx); + }else{ + assert( pList->eU4==EU4_EXPR ); + if( pList->a[i].u4.pExpr==0 ){ + fprintf(stdout, "%s (pExpr=NULL)\n", zName); + }else{ + fprintf(stdout, "%s\n", zName); + wx_sqlite3TreeViewPush(&pView, inId-1); + wx_sqlite3TreeViewExpr(pView, pList->a[i].u4.pExpr, 0); + wx_sqlite3TreeViewPop(&pView); + } + } + wx_sqlite3TreeViewPop(&pView); + } + } } +SQLITE_PRIVATE void wx_sqlite3TreeViewIdList( + TreeView *pView, + const IdList *pList, + u8 moreToFollow, + const char *zLabel +){ + wx_sqlite3TreeViewPush(&pView, moreToFollow); + wx_sqlite3TreeViewBareIdList(pView, pList, zLabel); + wx_sqlite3TreeViewPop(&pView); +} + +/* +** Generate a human-readable explanation of a list of Upsert objects +*/ +SQLITE_PRIVATE void wx_sqlite3TreeViewUpsert( + TreeView *pView, + const Upsert *pUpsert, + u8 moreToFollow +){ + if( pUpsert==0 ) return; + wx_sqlite3TreeViewPush(&pView, moreToFollow); + while( pUpsert ){ + int n; + wx_sqlite3TreeViewPush(&pView, pUpsert->pNextUpsert!=0 || moreToFollow); + wx_sqlite3TreeViewLine(pView, "ON CONFLICT DO %s", + pUpsert->isDoUpdate ? "UPDATE" : "NOTHING"); + n = (pUpsert->pUpsertSet!=0) + (pUpsert->pUpsertWhere!=0); + wx_sqlite3TreeViewExprList(pView, pUpsert->pUpsertTarget, (n--)>0, "TARGET"); + wx_sqlite3TreeViewExprList(pView, pUpsert->pUpsertSet, (n--)>0, "SET"); + if( pUpsert->pUpsertWhere ){ + wx_sqlite3TreeViewItem(pView, "WHERE", (n--)>0); + wx_sqlite3TreeViewExpr(pView, pUpsert->pUpsertWhere, 0); + wx_sqlite3TreeViewPop(&pView); + } + wx_sqlite3TreeViewPop(&pView); + pUpsert = pUpsert->pNextUpsert; + } + wx_sqlite3TreeViewPop(&pView); +} + +#if TREETRACE_ENABLED +/* +** Generate a human-readable diagram of the data structure that go +** into generating an DELETE statement. +*/ +SQLITE_PRIVATE void wx_sqlite3TreeViewDelete( + const With *pWith, + const SrcList *pTabList, + const Expr *pWhere, + const ExprList *pOrderBy, + const Expr *pLimit, + const Trigger *pTrigger +){ + int n = 0; + TreeView *pView = 0; + wx_sqlite3TreeViewPush(&pView, 0); + wx_sqlite3TreeViewLine(pView, "DELETE"); + if( pWith ) n++; + if( pTabList ) n++; + if( pWhere ) n++; + if( pOrderBy ) n++; + if( pLimit ) n++; + if( pTrigger ) n++; + if( pWith ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewWith(pView, pWith, 0); + wx_sqlite3TreeViewPop(&pView); + } + if( pTabList ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewLine(pView, "FROM"); + wx_sqlite3TreeViewSrcList(pView, pTabList); + wx_sqlite3TreeViewPop(&pView); + } + if( pWhere ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewLine(pView, "WHERE"); + wx_sqlite3TreeViewExpr(pView, pWhere, 0); + wx_sqlite3TreeViewPop(&pView); + } + if( pOrderBy ){ + wx_sqlite3TreeViewExprList(pView, pOrderBy, (--n)>0, "ORDER-BY"); + } + if( pLimit ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewLine(pView, "LIMIT"); + wx_sqlite3TreeViewExpr(pView, pLimit, 0); + wx_sqlite3TreeViewPop(&pView); + } + if( pTrigger ){ + wx_sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); + } + wx_sqlite3TreeViewPop(&pView); +} +#endif /* TREETRACE_ENABLED */ + +#if TREETRACE_ENABLED +/* +** Generate a human-readable diagram of the data structure that go +** into generating an INSERT statement. +*/ +SQLITE_PRIVATE void wx_sqlite3TreeViewInsert( + const With *pWith, + const SrcList *pTabList, + const IdList *pColumnList, + const Select *pSelect, + const ExprList *pExprList, + int onError, + const Upsert *pUpsert, + const Trigger *pTrigger +){ + TreeView *pView = 0; + int n = 0; + const char *zLabel = "INSERT"; + switch( onError ){ + case OE_Replace: zLabel = "REPLACE"; break; + case OE_Ignore: zLabel = "INSERT OR IGNORE"; break; + case OE_Rollback: zLabel = "INSERT OR ROLLBACK"; break; + case OE_Abort: zLabel = "INSERT OR ABORT"; break; + case OE_Fail: zLabel = "INSERT OR FAIL"; break; + } + wx_sqlite3TreeViewPush(&pView, 0); + wx_sqlite3TreeViewLine(pView, zLabel); + if( pWith ) n++; + if( pTabList ) n++; + if( pColumnList ) n++; + if( pSelect ) n++; + if( pExprList ) n++; + if( pUpsert ) n++; + if( pTrigger ) n++; + if( pWith ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewWith(pView, pWith, 0); + wx_sqlite3TreeViewPop(&pView); + } + if( pTabList ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewLine(pView, "INTO"); + wx_sqlite3TreeViewSrcList(pView, pTabList); + wx_sqlite3TreeViewPop(&pView); + } + if( pColumnList ){ + wx_sqlite3TreeViewIdList(pView, pColumnList, (--n)>0, "COLUMNS"); + } + if( pSelect ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewLine(pView, "DATA-SOURCE"); + wx_sqlite3TreeViewSelect(pView, pSelect, 0); + wx_sqlite3TreeViewPop(&pView); + } + if( pExprList ){ + wx_sqlite3TreeViewExprList(pView, pExprList, (--n)>0, "VALUES"); + } + if( pUpsert ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewLine(pView, "UPSERT"); + wx_sqlite3TreeViewUpsert(pView, pUpsert, 0); + wx_sqlite3TreeViewPop(&pView); + } + if( pTrigger ){ + wx_sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); + } + wx_sqlite3TreeViewPop(&pView); +} +#endif /* TREETRACE_ENABLED */ + +#if TREETRACE_ENABLED +/* +** Generate a human-readable diagram of the data structure that go +** into generating an UPDATE statement. +*/ +SQLITE_PRIVATE void wx_sqlite3TreeViewUpdate( + const With *pWith, + const SrcList *pTabList, + const ExprList *pChanges, + const Expr *pWhere, + int onError, + const ExprList *pOrderBy, + const Expr *pLimit, + const Upsert *pUpsert, + const Trigger *pTrigger +){ + int n = 0; + TreeView *pView = 0; + const char *zLabel = "UPDATE"; + switch( onError ){ + case OE_Replace: zLabel = "UPDATE OR REPLACE"; break; + case OE_Ignore: zLabel = "UPDATE OR IGNORE"; break; + case OE_Rollback: zLabel = "UPDATE OR ROLLBACK"; break; + case OE_Abort: zLabel = "UPDATE OR ABORT"; break; + case OE_Fail: zLabel = "UPDATE OR FAIL"; break; + } + wx_sqlite3TreeViewPush(&pView, 0); + wx_sqlite3TreeViewLine(pView, zLabel); + if( pWith ) n++; + if( pTabList ) n++; + if( pChanges ) n++; + if( pWhere ) n++; + if( pOrderBy ) n++; + if( pLimit ) n++; + if( pUpsert ) n++; + if( pTrigger ) n++; + if( pWith ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewWith(pView, pWith, 0); + wx_sqlite3TreeViewPop(&pView); + } + if( pTabList ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewLine(pView, "FROM"); + wx_sqlite3TreeViewSrcList(pView, pTabList); + wx_sqlite3TreeViewPop(&pView); + } + if( pChanges ){ + wx_sqlite3TreeViewExprList(pView, pChanges, (--n)>0, "SET"); + } + if( pWhere ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewLine(pView, "WHERE"); + wx_sqlite3TreeViewExpr(pView, pWhere, 0); + wx_sqlite3TreeViewPop(&pView); + } + if( pOrderBy ){ + wx_sqlite3TreeViewExprList(pView, pOrderBy, (--n)>0, "ORDER-BY"); + } + if( pLimit ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewLine(pView, "LIMIT"); + wx_sqlite3TreeViewExpr(pView, pLimit, 0); + wx_sqlite3TreeViewPop(&pView); + } + if( pUpsert ){ + wx_sqlite3TreeViewPush(&pView, (--n)>0); + wx_sqlite3TreeViewLine(pView, "UPSERT"); + wx_sqlite3TreeViewUpsert(pView, pUpsert, 0); + wx_sqlite3TreeViewPop(&pView); + } + if( pTrigger ){ + wx_sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); + } + wx_sqlite3TreeViewPop(&pView); +} +#endif /* TREETRACE_ENABLED */ + +#ifndef SQLITE_OMIT_TRIGGER +/* +** Show a human-readable graph of a TriggerStep +*/ +SQLITE_PRIVATE void wx_sqlite3TreeViewTriggerStep( + TreeView *pView, + const TriggerStep *pStep, + u8 moreToFollow, + u8 showFullList +){ + int cnt = 0; + if( pStep==0 ) return; + wx_sqlite3TreeViewPush(&pView, + moreToFollow || (showFullList && pStep->pNext!=0)); + do{ + if( cnt++ && pStep->pNext==0 ){ + wx_sqlite3TreeViewPop(&pView); + wx_sqlite3TreeViewPush(&pView, 0); + } + wx_sqlite3TreeViewLine(pView, "%s", pStep->zSpan ? pStep->zSpan : "RETURNING"); + }while( showFullList && (pStep = pStep->pNext)!=0 ); + wx_sqlite3TreeViewPop(&pView); +} + +/* +** Show a human-readable graph of a Trigger +*/ +SQLITE_PRIVATE void wx_sqlite3TreeViewTrigger( + TreeView *pView, + const Trigger *pTrigger, + u8 moreToFollow, + u8 showFullList +){ + int cnt = 0; + if( pTrigger==0 ) return; + wx_sqlite3TreeViewPush(&pView, + moreToFollow || (showFullList && pTrigger->pNext!=0)); + do{ + if( cnt++ && pTrigger->pNext==0 ){ + wx_sqlite3TreeViewPop(&pView); + wx_sqlite3TreeViewPush(&pView, 0); + } + wx_sqlite3TreeViewLine(pView, "TRIGGER %s", pTrigger->zName); + wx_sqlite3TreeViewPush(&pView, 0); + wx_sqlite3TreeViewTriggerStep(pView, pTrigger->step_list, 0, 1); + wx_sqlite3TreeViewPop(&pView); + }while( showFullList && (pTrigger = pTrigger->pNext)!=0 ); + wx_sqlite3TreeViewPop(&pView); +} +#endif /* SQLITE_OMIT_TRIGGER */ + + +/* +** These simplified versions of the tree-view routines omit unnecessary +** parameters. These variants are intended to be used from a symbolic +** debugger, such as "gdb", during interactive debugging sessions. +** +** This routines are given external linkage so that they will always be +** accessible to the debugging, and to avoid warnings about unused +** functions. But these routines only exist in debugging builds, so they +** do not contaminate the interface. +*/ +SQLITE_PRIVATE void wx_sqlite3ShowExpr(const Expr *p){ wx_sqlite3TreeViewExpr(0,p,0); } +SQLITE_PRIVATE void wx_sqlite3ShowExprList(const ExprList *p){ wx_sqlite3TreeViewExprList(0,p,0,0);} +SQLITE_PRIVATE void wx_sqlite3ShowIdList(const IdList *p){ wx_sqlite3TreeViewIdList(0,p,0,0); } +SQLITE_PRIVATE void wx_sqlite3ShowSrcList(const SrcList *p){ wx_sqlite3TreeViewSrcList(0,p); } +SQLITE_PRIVATE void wx_sqlite3ShowSelect(const Select *p){ wx_sqlite3TreeViewSelect(0,p,0); } +SQLITE_PRIVATE void wx_sqlite3ShowWith(const With *p){ wx_sqlite3TreeViewWith(0,p,0); } +SQLITE_PRIVATE void wx_sqlite3ShowUpsert(const Upsert *p){ wx_sqlite3TreeViewUpsert(0,p,0); } +#ifndef SQLITE_OMIT_TRIGGER +SQLITE_PRIVATE void wx_sqlite3ShowTriggerStep(const TriggerStep *p){ + wx_sqlite3TreeViewTriggerStep(0,p,0,0); +} +SQLITE_PRIVATE void wx_sqlite3ShowTriggerStepList(const TriggerStep *p){ + wx_sqlite3TreeViewTriggerStep(0,p,0,1); +} +SQLITE_PRIVATE void wx_sqlite3ShowTrigger(const Trigger *p){ wx_sqlite3TreeViewTrigger(0,p,0,0); } +SQLITE_PRIVATE void wx_sqlite3ShowTriggerList(const Trigger *p){ wx_sqlite3TreeViewTrigger(0,p,0,1);} +#endif +#ifndef SQLITE_OMIT_WINDOWFUNC +SQLITE_PRIVATE void wx_sqlite3ShowWindow(const Window *p){ wx_sqlite3TreeViewWindow(0,p,0); } +SQLITE_PRIVATE void wx_sqlite3ShowWinFunc(const Window *p){ wx_sqlite3TreeViewWinFunc(0,p,0); } +#endif #endif /* SQLITE_DEBUG */ @@ -30629,16 +32762,41 @@ SQLITE_PRIVATE void wx_sqlite3TreeViewExprList( ** This structure is the current state of the generator. */ static SQLITE_WSD struct wx_sqlite3PrngType { - unsigned char isInit; /* True if initialized */ - unsigned char i, j; /* State variables */ - unsigned char s[256]; /* State variables */ + u32 s[16]; /* 64 bytes of chacha20 state */ + u8 out[64]; /* Output bytes */ + u8 n; /* Output bytes remaining */ } wx_sqlite3Prng; + +/* The RFC-7539 ChaCha20 block function +*/ +#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) +#define QR(a, b, c, d) ( \ + a += b, d ^= a, d = ROTL(d,16), \ + c += d, b ^= c, b = ROTL(b,12), \ + a += b, d ^= a, d = ROTL(d, 8), \ + c += d, b ^= c, b = ROTL(b, 7)) +static void chacha_block(u32 *out, const u32 *in){ + int i; + u32 x[16]; + memcpy(x, in, 64); + for(i=0; i<10; i++){ + QR(x[0], x[4], x[ 8], x[12]); + QR(x[1], x[5], x[ 9], x[13]); + QR(x[2], x[6], x[10], x[14]); + QR(x[3], x[7], x[11], x[15]); + QR(x[0], x[5], x[10], x[15]); + QR(x[1], x[6], x[11], x[12]); + QR(x[2], x[7], x[ 8], x[13]); + QR(x[3], x[4], x[ 9], x[14]); + } + for(i=0; i<16; i++) out[i] = x[i]+in[i]; +} + /* ** Return N random bytes. */ SQLITE_API void wx_sqlite3_randomness(int N, void *pBuf){ - unsigned char t; unsigned char *zBuf = pBuf; /* The "wsdPrng" macro will resolve to the pseudo-random number generator @@ -30668,48 +32826,46 @@ SQLITE_API void wx_sqlite3_randomness(int N, void *pBuf){ wx_sqlite3_mutex_enter(mutex); if( N<=0 || pBuf==0 ){ - wsdPrng.isInit = 0; + wsdPrng.s[0] = 0; wx_sqlite3_mutex_leave(mutex); return; } /* Initialize the state of the random number generator once, - ** the first time this routine is called. The seed value does - ** not need to contain a lot of randomness since we are not - ** trying to do secure encryption or anything like that... - ** - ** Nothing in this file or anywhere else in SQLite does any kind of - ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random - ** number generator) not as an encryption device. + ** the first time this routine is called. */ - if( !wsdPrng.isInit ){ - int i; - char k[256]; - wsdPrng.j = 0; - wsdPrng.i = 0; - wx_sqlite3OsRandomness(wx_sqlite3_vfs_find(0), 256, k); - for(i=0; i<256; i++){ - wsdPrng.s[i] = (u8)i; - } - for(i=0; i<256; i++){ - wsdPrng.j += wsdPrng.s[i] + k[i]; - t = wsdPrng.s[wsdPrng.j]; - wsdPrng.s[wsdPrng.j] = wsdPrng.s[i]; - wsdPrng.s[i] = t; + if( wsdPrng.s[0]==0 ){ + wx_sqlite3_vfs *pVfs = wx_sqlite3_vfs_find(0); + static const u32 chacha20_init[] = { + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 + }; + memcpy(&wsdPrng.s[0], chacha20_init, 16); + if( NEVER(pVfs==0) ){ + memset(&wsdPrng.s[4], 0, 44); + }else{ + wx_sqlite3OsRandomness(pVfs, 44, (char*)&wsdPrng.s[4]); } - wsdPrng.isInit = 1; + wsdPrng.s[15] = wsdPrng.s[12]; + wsdPrng.s[12] = 0; + wsdPrng.n = 0; } assert( N>0 ); - do{ - wsdPrng.i++; - t = wsdPrng.s[wsdPrng.i]; - wsdPrng.j += t; - wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j]; - wsdPrng.s[wsdPrng.j] = t; - t += wsdPrng.s[wsdPrng.i]; - *(zBuf++) = wsdPrng.s[t]; - }while( --N ); + while( 1 /* exit by break */ ){ + if( N<=wsdPrng.n ){ + memcpy(zBuf, &wsdPrng.out[wsdPrng.n-N], N); + wsdPrng.n -= N; + break; + } + if( wsdPrng.n>0 ){ + memcpy(zBuf, wsdPrng.out, wsdPrng.n); + N -= wsdPrng.n; + zBuf += wsdPrng.n; + } + wsdPrng.s[12]++; + chacha_block((u32*)wsdPrng.out, wsdPrng.s); + wsdPrng.n = 64; + } wx_sqlite3_mutex_leave(mutex); } @@ -31580,16 +33736,6 @@ SQLITE_PRIVATE void wx_sqlite3UtfSelfTest(void){ #include #endif -/* -** Routine needed to support the testcase() macro. -*/ -#ifdef SQLITE_COVERAGE_TEST -SQLITE_PRIVATE void wx_sqlite3Coverage(int x){ - static unsigned dummy = 0; - dummy += (unsigned)x; -} -#endif - /* ** Calls to wx_sqlite3FaultSim() are used to simulate a failure during testing, ** or to bypass normal error detection during testing in order to let @@ -31619,11 +33765,21 @@ SQLITE_PRIVATE int wx_sqlite3FaultSim(int iTest){ #ifndef SQLITE_OMIT_FLOATING_POINT /* ** Return true if the floating point value is Not a Number (NaN). +** +** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN. +** Otherwise, we have our own implementation that works on most systems. */ SQLITE_PRIVATE int wx_sqlite3IsNaN(double x){ + int rc; /* The value return */ +#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN u64 y; memcpy(&y,&x,sizeof(y)); - return IsNaN(y); + rc = IsNaN(y); +#else + rc = isnan(x); +#endif /* HAVE_ISNAN */ + testcase( rc ); + return rc; } #endif /* SQLITE_OMIT_FLOATING_POINT */ @@ -31648,8 +33804,14 @@ SQLITE_PRIVATE int wx_sqlite3Strlen30(const char *z){ ** the column name if and only if the COLFLAG_HASTYPE flag is set. */ SQLITE_PRIVATE char *wx_sqlite3ColumnType(Column *pCol, char *zDflt){ - if( (pCol->colFlags & COLFLAG_HASTYPE)==0 ) return zDflt; - return pCol->zName + strlen(pCol->zName) + 1; + if( pCol->colFlags & COLFLAG_HASTYPE ){ + return pCol->zCnName + strlen(pCol->zCnName) + 1; + }else if( pCol->eCType ){ + assert( pCol->eCType<=SQLITE_N_STDTYPE ); + return (char*)wx_sqlite3StdType[pCol->eCType-1]; + }else{ + return zDflt; + } } /* @@ -31670,7 +33832,11 @@ static SQLITE_NOINLINE void wx_sqlite3ErrorFinish(wx_sqlite3 *db, int err_code) SQLITE_PRIVATE void wx_sqlite3Error(wx_sqlite3 *db, int err_code){ assert( db!=0 ); db->errCode = err_code; - if( err_code || db->pErr ) wx_sqlite3ErrorFinish(db, err_code); + if( err_code || db->pErr ){ + wx_sqlite3ErrorFinish(db, err_code); + }else{ + db->errByteOffset = -1; + } } /* @@ -31680,6 +33846,7 @@ SQLITE_PRIVATE void wx_sqlite3Error(wx_sqlite3 *db, int err_code){ SQLITE_PRIVATE void wx_sqlite3ErrorClear(wx_sqlite3 *db){ assert( db!=0 ); db->errCode = SQLITE_OK; + db->errByteOffset = -1; if( db->pErr ) wx_sqlite3ValueSetNull(db->pErr); } @@ -31700,17 +33867,8 @@ SQLITE_PRIVATE void wx_sqlite3SystemError(wx_sqlite3 *db, int rc){ ** handle "db". The error code is set to "err_code". ** ** If it is not NULL, string zFormat specifies the format of the -** error string in the style of the printf functions: The following -** format characters are allowed: -** -** %s Insert a string -** %z A string that should be freed after use -** %d Insert an integer -** %T Insert a token -** %S Insert the first element of a SrcList -** -** zFormat and any string tokens that follow it are assumed to be -** encoded in UTF-8. +** error string. zFormat and any string tokens that follow it are +** assumed to be encoded in UTF-8. ** ** To clear the most recent error for sqlite handle "db", wx_sqlite3Error ** should be called with err_code set to SQLITE_OK and zFormat set @@ -31732,15 +33890,28 @@ SQLITE_PRIVATE void wx_sqlite3ErrorWithMsg(wx_sqlite3 *db, int err_code, const c } } +/* +** Check for interrupts and invoke progress callback. +*/ +SQLITE_PRIVATE void wx_sqlite3ProgressCheck(Parse *p){ + wx_sqlite3 *db = p->db; + if( AtomicLoad(&db->u1.isInterrupted) ){ + p->nErr++; + p->rc = SQLITE_INTERRUPT; + } +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + if( db->xProgress && (++p->nProgressSteps)>=db->nProgressOps ){ + if( db->xProgress(db->pProgressArg) ){ + p->nErr++; + p->rc = SQLITE_INTERRUPT; + } + p->nProgressSteps = 0; + } +#endif +} + /* ** Add an error message to pParse->zErrMsg and increment pParse->nErr. -** The following formatting characters are allowed: -** -** %s Insert a string -** %z A string that should be freed after use -** %d Insert an integer -** %T Insert a token -** %S Insert the first element of a SrcList ** ** This function should be used to report any error that occurs while ** compiling an SQL statement (i.e. within wx_sqlite3_prepare()). The @@ -31753,11 +33924,19 @@ SQLITE_PRIVATE void wx_sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ char *zMsg; va_list ap; wx_sqlite3 *db = pParse->db; + assert( db!=0 ); + assert( db->pParse==pParse || db->pParse->pToplevel==pParse ); + db->errByteOffset = -2; va_start(ap, zFormat); zMsg = wx_sqlite3VMPrintf(db, zFormat, ap); va_end(ap); + if( db->errByteOffset<-1 ) db->errByteOffset = -1; if( db->suppressErr ){ wx_sqlite3DbFree(db, zMsg); + if( db->mallocFailed ){ + pParse->nErr++; + pParse->rc = SQLITE_NOMEM; + } }else{ pParse->nErr++; wx_sqlite3DbFree(db, pParse->zErrMsg); @@ -31820,11 +33999,34 @@ SQLITE_PRIVATE void wx_sqlite3Dequote(char *z){ z[j] = 0; } SQLITE_PRIVATE void wx_sqlite3DequoteExpr(Expr *p){ + assert( !ExprHasProperty(p, EP_IntValue) ); assert( wx_sqlite3Isquote(p->u.zToken[0]) ); p->flags |= p->u.zToken[0]=='"' ? EP_Quoted|EP_DblQuoted : EP_Quoted; wx_sqlite3Dequote(p->u.zToken); } +/* +** If the input token p is quoted, try to adjust the token to remove +** the quotes. This is not always possible: +** +** "abc" -> abc +** "ab""cd" -> (not possible because of the interior "") +** +** Remove the quotes if possible. This is a optimization. The overall +** system should still return the correct answer even if this routine +** is always a no-op. +*/ +SQLITE_PRIVATE void wx_sqlite3DequoteToken(Token *p){ + unsigned int i; + if( p->n<2 ) return; + if( !wx_sqlite3Isquote(p->z[0]) ) return; + for(i=1; in-1; i++){ + if( wx_sqlite3Isquote(p->z[i]) ) return; + } + p->n -= 2; + p->z++; +} + /* ** Generate a Token object from a string */ @@ -32165,11 +34367,14 @@ do_atof_calc: #endif /* -** Render an signed 64-bit integer as text. Store the result in zOut[]. +** Render an signed 64-bit integer as text. Store the result in zOut[] and +** return the length of the string that was stored, in bytes. The value +** returned does not include the zero terminator at the end of the output +** string. ** ** The caller must ensure that zOut[] is at least 21 bytes in size. */ -SQLITE_PRIVATE void wx_sqlite3Int64ToText(i64 v, char *zOut){ +SQLITE_PRIVATE int wx_sqlite3Int64ToText(i64 v, char *zOut){ int i; u64 x; char zTemp[22]; @@ -32186,6 +34391,7 @@ SQLITE_PRIVATE void wx_sqlite3Int64ToText(i64 v, char *zOut){ }while( x ); if( v<0 ) zTemp[i--] = '-'; memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); + return sizeof(zTemp)-2-i; } /* @@ -32930,13 +35136,13 @@ static void logBadConnection(const char *zType){ ** used as an argument to wx_sqlite3_errmsg() or wx_sqlite3_close(). */ SQLITE_PRIVATE int wx_sqlite3SafetyCheckOk(wx_sqlite3 *db){ - u32 magic; + u8 eOpenState; if( db==0 ){ logBadConnection("NULL"); return 0; } - magic = db->magic; - if( magic!=SQLITE_MAGIC_OPEN ){ + eOpenState = db->eOpenState; + if( eOpenState!=SQLITE_STATE_OPEN ){ if( wx_sqlite3SafetyCheckSickOrOk(db) ){ testcase( wx_sqlite3GlobalConfig.xLog!=0 ); logBadConnection("unopened"); @@ -32947,11 +35153,11 @@ SQLITE_PRIVATE int wx_sqlite3SafetyCheckOk(wx_sqlite3 *db){ } } SQLITE_PRIVATE int wx_sqlite3SafetyCheckSickOrOk(wx_sqlite3 *db){ - u32 magic; - magic = db->magic; - if( magic!=SQLITE_MAGIC_SICK && - magic!=SQLITE_MAGIC_OPEN && - magic!=SQLITE_MAGIC_BUSY ){ + u8 eOpenState; + eOpenState = db->eOpenState; + if( eOpenState!=SQLITE_STATE_SICK && + eOpenState!=SQLITE_STATE_OPEN && + eOpenState!=SQLITE_STATE_BUSY ){ testcase( wx_sqlite3GlobalConfig.xLog!=0 ); logBadConnection("invalid"); return 0; @@ -33116,7 +35322,6 @@ SQLITE_PRIVATE LogEst wx_sqlite3LogEst(u64 x){ return a[x&7] + y - 10; } -#ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Convert a double into a LogEst ** In other words, compute an approximation for 10*log2(x). @@ -33131,16 +35336,9 @@ SQLITE_PRIVATE LogEst wx_sqlite3LogEstFromDouble(double x){ e = (a>>52) - 1022; return e*10; } -#endif /* SQLITE_OMIT_VIRTUALTABLE */ -#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ - defined(SQLITE_ENABLE_STAT4) || \ - defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) /* ** Convert a LogEst into an integer. -** -** Note that this routine is only used when one or more of various -** non-standard compile-time options is enabled. */ SQLITE_PRIVATE u64 wx_sqlite3LogEstToInt(LogEst x){ u64 n; @@ -33148,17 +35346,9 @@ SQLITE_PRIVATE u64 wx_sqlite3LogEstToInt(LogEst x){ x /= 10; if( n>=5 ) n -= 2; else if( n>=1 ) n -= 1; -#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ - defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) if( x>60 ) return (u64)LARGEST_INT64; -#else - /* If only SQLITE_ENABLE_STAT4 is on, then the largest input - ** possible to this routine is 310, resulting in a maximum x of 31 */ - assert( x<=60 ); -#endif return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x); } -#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */ /* ** Add a new name/number pair to a VList. This might require that the @@ -33263,6 +35453,104 @@ SQLITE_PRIVATE int wx_sqlite3VListNameToNum(VList *pIn, const char *zName, int n return 0; } +/* +** High-resolution hardware timer used for debugging and testing only. +*/ +#if defined(VDBE_PROFILE) \ + || defined(SQLITE_PERFORMANCE_TRACE) \ + || defined(SQLITE_ENABLE_STMT_SCANSTATUS) +/************** Include hwtime.h in the middle of util.c *********************/ +/************** Begin file hwtime.h ******************************************/ +/* +** 2008 May 27 +** +** 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 inline asm code for retrieving "high-performance" +** counters for x86 and x86_64 class CPUs. +*/ +#ifndef SQLITE_HWTIME_H +#define SQLITE_HWTIME_H + +/* +** The following routine only works on pentium-class (or newer) processors. +** It uses the RDTSC opcode to read the cycle count value out of the +** processor and returns that value. This can be used for high-res +** profiling. +*/ +#if !defined(__STRICT_ANSI__) && \ + (defined(__GNUC__) || defined(_MSC_VER)) && \ + (defined(i386) || defined(__i386__) || defined(_M_IX86)) + + #if defined(__GNUC__) + + __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ + unsigned int lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return (sqlite_uint64)hi << 32 | lo; + } + + #elif defined(_MSC_VER) + + __declspec(naked) __inline sqlite_uint64 __cdecl wx_sqlite3Hwtime(void){ + __asm { + rdtsc + ret ; return value at EDX:EAX + } + } + + #endif + +#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) + + __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ + unsigned int lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return (sqlite_uint64)hi << 32 | lo; + } + +#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) + + __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ + unsigned long long retval; + unsigned long junk; + __asm__ __volatile__ ("\n\ + 1: mftbu %1\n\ + mftb %L0\n\ + mftbu %0\n\ + cmpw %0,%1\n\ + bne 1b" + : "=r" (retval), "=r" (junk)); + return retval; + } + +#else + + /* + ** asm() is needed for hardware timing support. Without asm(), + ** disable the wx_sqlite3Hwtime() routine. + ** + ** wx_sqlite3Hwtime() is only used for some obscure debugging + ** and analysis configurations, not in any deliverable, so this + ** should not be a great loss. + */ +SQLITE_PRIVATE sqlite_uint64 wx_sqlite3Hwtime(void){ return ((sqlite_uint64)0); } + +#endif + +#endif /* !defined(SQLITE_HWTIME_H) */ + +/************** End of hwtime.h **********************************************/ +/************** Continuing where we left off in util.c ***********************/ +#endif + /************** End of util.c ************************************************/ /************** Begin file hash.c ********************************************/ /* @@ -33433,12 +35721,13 @@ static HashElem *findElementWithHash( count = pH->count; } if( pHash ) *pHash = h; - while( count-- ){ + while( count ){ assert( elem!=0 ); if( wx_sqlite3StrICmp(elem->pKey,pKey)==0 ){ return elem; } elem = elem->next; + count--; } return &nullElement; } @@ -33552,53 +35841,53 @@ SQLITE_PRIVATE const char *wx_sqlite3OpcodeName(int i){ /* 0 */ "Savepoint" OpHelp(""), /* 1 */ "AutoCommit" OpHelp(""), /* 2 */ "Transaction" OpHelp(""), - /* 3 */ "SorterNext" OpHelp(""), - /* 4 */ "Prev" OpHelp(""), - /* 5 */ "Next" OpHelp(""), - /* 6 */ "Checkpoint" OpHelp(""), - /* 7 */ "JournalMode" OpHelp(""), - /* 8 */ "Vacuum" OpHelp(""), - /* 9 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"), - /* 10 */ "VUpdate" OpHelp("data=r[P3@P2]"), - /* 11 */ "Goto" OpHelp(""), - /* 12 */ "Gosub" OpHelp(""), - /* 13 */ "InitCoroutine" OpHelp(""), - /* 14 */ "Yield" OpHelp(""), - /* 15 */ "MustBeInt" OpHelp(""), - /* 16 */ "Jump" OpHelp(""), - /* 17 */ "Once" OpHelp(""), - /* 18 */ "If" OpHelp(""), + /* 3 */ "Checkpoint" OpHelp(""), + /* 4 */ "JournalMode" OpHelp(""), + /* 5 */ "Vacuum" OpHelp(""), + /* 6 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"), + /* 7 */ "VUpdate" OpHelp("data=r[P3@P2]"), + /* 8 */ "Init" OpHelp("Start at P2"), + /* 9 */ "Goto" OpHelp(""), + /* 10 */ "Gosub" OpHelp(""), + /* 11 */ "InitCoroutine" OpHelp(""), + /* 12 */ "Yield" OpHelp(""), + /* 13 */ "MustBeInt" OpHelp(""), + /* 14 */ "Jump" OpHelp(""), + /* 15 */ "Once" OpHelp(""), + /* 16 */ "If" OpHelp(""), + /* 17 */ "IfNot" OpHelp(""), + /* 18 */ "IsType" OpHelp("if typeof(P1.P3) in P5 goto P2"), /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"), - /* 20 */ "IfNot" OpHelp(""), - /* 21 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"), - /* 22 */ "SeekLT" OpHelp("key=r[P3@P4]"), - /* 23 */ "SeekLE" OpHelp("key=r[P3@P4]"), - /* 24 */ "SeekGE" OpHelp("key=r[P3@P4]"), - /* 25 */ "SeekGT" OpHelp("key=r[P3@P4]"), - /* 26 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"), - /* 27 */ "IfNoHope" OpHelp("key=r[P3@P4]"), - /* 28 */ "NoConflict" OpHelp("key=r[P3@P4]"), - /* 29 */ "NotFound" OpHelp("key=r[P3@P4]"), - /* 30 */ "Found" OpHelp("key=r[P3@P4]"), - /* 31 */ "SeekRowid" OpHelp("intkey=r[P3]"), - /* 32 */ "NotExists" OpHelp("intkey=r[P3]"), - /* 33 */ "Last" OpHelp(""), - /* 34 */ "IfSmaller" OpHelp(""), - /* 35 */ "SorterSort" OpHelp(""), - /* 36 */ "Sort" OpHelp(""), - /* 37 */ "Rewind" OpHelp(""), - /* 38 */ "IdxLE" OpHelp("key=r[P3@P4]"), - /* 39 */ "IdxGT" OpHelp("key=r[P3@P4]"), - /* 40 */ "IdxLT" OpHelp("key=r[P3@P4]"), - /* 41 */ "IdxGE" OpHelp("key=r[P3@P4]"), - /* 42 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), + /* 20 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"), + /* 21 */ "SeekLT" OpHelp("key=r[P3@P4]"), + /* 22 */ "SeekLE" OpHelp("key=r[P3@P4]"), + /* 23 */ "SeekGE" OpHelp("key=r[P3@P4]"), + /* 24 */ "SeekGT" OpHelp("key=r[P3@P4]"), + /* 25 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"), + /* 26 */ "IfNoHope" OpHelp("key=r[P3@P4]"), + /* 27 */ "NoConflict" OpHelp("key=r[P3@P4]"), + /* 28 */ "NotFound" OpHelp("key=r[P3@P4]"), + /* 29 */ "Found" OpHelp("key=r[P3@P4]"), + /* 30 */ "SeekRowid" OpHelp("intkey=r[P3]"), + /* 31 */ "NotExists" OpHelp("intkey=r[P3]"), + /* 32 */ "Last" OpHelp(""), + /* 33 */ "IfSmaller" OpHelp(""), + /* 34 */ "SorterSort" OpHelp(""), + /* 35 */ "Sort" OpHelp(""), + /* 36 */ "Rewind" OpHelp(""), + /* 37 */ "SorterNext" OpHelp(""), + /* 38 */ "Prev" OpHelp(""), + /* 39 */ "Next" OpHelp(""), + /* 40 */ "IdxLE" OpHelp("key=r[P3@P4]"), + /* 41 */ "IdxGT" OpHelp("key=r[P3@P4]"), + /* 42 */ "IdxLT" OpHelp("key=r[P3@P4]"), /* 43 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"), /* 44 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"), - /* 45 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), - /* 46 */ "Program" OpHelp(""), - /* 47 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), - /* 48 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), - /* 49 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), + /* 45 */ "IdxGE" OpHelp("key=r[P3@P4]"), + /* 46 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), + /* 47 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), + /* 48 */ "Program" OpHelp(""), + /* 49 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), /* 50 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), /* 51 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), /* 52 */ "Ne" OpHelp("IF r[P3]!=r[P1]"), @@ -33607,50 +35896,50 @@ SQLITE_PRIVATE const char *wx_sqlite3OpcodeName(int i){ /* 55 */ "Le" OpHelp("IF r[P3]<=r[P1]"), /* 56 */ "Lt" OpHelp("IF r[P3]=r[P1]"), - /* 58 */ "ElseNotEq" OpHelp(""), - /* 59 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), - /* 60 */ "IncrVacuum" OpHelp(""), - /* 61 */ "VNext" OpHelp(""), - /* 62 */ "Init" OpHelp("Start at P2"), - /* 63 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"), - /* 64 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"), - /* 65 */ "Return" OpHelp(""), - /* 66 */ "EndCoroutine" OpHelp(""), - /* 67 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), - /* 68 */ "Halt" OpHelp(""), - /* 69 */ "Integer" OpHelp("r[P2]=P1"), - /* 70 */ "Int64" OpHelp("r[P2]=P4"), - /* 71 */ "String" OpHelp("r[P2]='P4' (len=P1)"), - /* 72 */ "Null" OpHelp("r[P2..P3]=NULL"), - /* 73 */ "SoftNull" OpHelp("r[P1]=NULL"), - /* 74 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), - /* 75 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), - /* 76 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), - /* 77 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), - /* 78 */ "SCopy" OpHelp("r[P2]=r[P1]"), - /* 79 */ "IntCopy" OpHelp("r[P2]=r[P1]"), - /* 80 */ "ChngCntRow" OpHelp("output=r[P1]"), - /* 81 */ "ResultRow" OpHelp("output=r[P1@P2]"), - /* 82 */ "CollSeq" OpHelp(""), - /* 83 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), - /* 84 */ "RealAffinity" OpHelp(""), - /* 85 */ "Cast" OpHelp("affinity(r[P1])"), - /* 86 */ "Permutation" OpHelp(""), - /* 87 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), - /* 88 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), - /* 89 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), - /* 90 */ "Column" OpHelp("r[P3]=PX"), - /* 91 */ "Affinity" OpHelp("affinity(r[P1@P2])"), - /* 92 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), - /* 93 */ "Count" OpHelp("r[P2]=count()"), - /* 94 */ "ReadCookie" OpHelp(""), - /* 95 */ "SetCookie" OpHelp(""), - /* 96 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), - /* 97 */ "OpenRead" OpHelp("root=P2 iDb=P3"), - /* 98 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), - /* 99 */ "OpenDup" OpHelp(""), - /* 100 */ "OpenAutoindex" OpHelp("nColumn=P2"), - /* 101 */ "OpenEphemeral" OpHelp("nColumn=P2"), + /* 58 */ "ElseEq" OpHelp(""), + /* 59 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), + /* 60 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), + /* 61 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), + /* 62 */ "IncrVacuum" OpHelp(""), + /* 63 */ "VNext" OpHelp(""), + /* 64 */ "Filter" OpHelp("if key(P3@P4) not in filter(P1) goto P2"), + /* 65 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"), + /* 66 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"), + /* 67 */ "Return" OpHelp(""), + /* 68 */ "EndCoroutine" OpHelp(""), + /* 69 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), + /* 70 */ "Halt" OpHelp(""), + /* 71 */ "Integer" OpHelp("r[P2]=P1"), + /* 72 */ "Int64" OpHelp("r[P2]=P4"), + /* 73 */ "String" OpHelp("r[P2]='P4' (len=P1)"), + /* 74 */ "BeginSubrtn" OpHelp("r[P2]=NULL"), + /* 75 */ "Null" OpHelp("r[P2..P3]=NULL"), + /* 76 */ "SoftNull" OpHelp("r[P1]=NULL"), + /* 77 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), + /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), + /* 79 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), + /* 80 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), + /* 81 */ "SCopy" OpHelp("r[P2]=r[P1]"), + /* 82 */ "IntCopy" OpHelp("r[P2]=r[P1]"), + /* 83 */ "FkCheck" OpHelp(""), + /* 84 */ "ResultRow" OpHelp("output=r[P1@P2]"), + /* 85 */ "CollSeq" OpHelp(""), + /* 86 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), + /* 87 */ "RealAffinity" OpHelp(""), + /* 88 */ "Cast" OpHelp("affinity(r[P1])"), + /* 89 */ "Permutation" OpHelp(""), + /* 90 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), + /* 91 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), + /* 92 */ "ZeroOrNull" OpHelp("r[P2] = 0 OR NULL"), + /* 93 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), + /* 94 */ "Column" OpHelp("r[P3]=PX cursor P1 column P2"), + /* 95 */ "TypeCheck" OpHelp("typecheck(r[P1@P2])"), + /* 96 */ "Affinity" OpHelp("affinity(r[P1@P2])"), + /* 97 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), + /* 98 */ "Count" OpHelp("r[P2]=count()"), + /* 99 */ "ReadCookie" OpHelp(""), + /* 100 */ "SetCookie" OpHelp(""), + /* 101 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), /* 102 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), /* 103 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), /* 104 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), - /* 156 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), - /* 157 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 158 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 159 */ "AggValue" OpHelp("r[P3]=value N=P2"), - /* 160 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), - /* 161 */ "Expire" OpHelp(""), - /* 162 */ "CursorLock" OpHelp(""), - /* 163 */ "CursorUnlock" OpHelp(""), - /* 164 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), - /* 165 */ "VBegin" OpHelp(""), - /* 166 */ "VCreate" OpHelp(""), - /* 167 */ "VDestroy" OpHelp(""), - /* 168 */ "VOpen" OpHelp(""), - /* 169 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 170 */ "VRename" OpHelp(""), - /* 171 */ "Pagecount" OpHelp(""), - /* 172 */ "MaxPgcnt" OpHelp(""), - /* 173 */ "Trace" OpHelp(""), - /* 174 */ "CursorHint" OpHelp(""), - /* 175 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), - /* 176 */ "Noop" OpHelp(""), - /* 177 */ "Explain" OpHelp(""), - /* 178 */ "Abortable" OpHelp(""), + /* 112 */ "OpenRead" OpHelp("root=P2 iDb=P3"), + /* 113 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), + /* 114 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), + /* 115 */ "OpenDup" OpHelp(""), + /* 116 */ "OpenAutoindex" OpHelp("nColumn=P2"), + /* 117 */ "String8" OpHelp("r[P2]='P4'"), + /* 118 */ "OpenEphemeral" OpHelp("nColumn=P2"), + /* 119 */ "SorterOpen" OpHelp(""), + /* 120 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), + /* 121 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), + /* 122 */ "Close" OpHelp(""), + /* 123 */ "ColumnsUsed" OpHelp(""), + /* 124 */ "SeekScan" OpHelp("Scan-ahead up to P1 rows"), + /* 125 */ "SeekHit" OpHelp("set P2<=seekHit<=P3"), + /* 126 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), + /* 127 */ "NewRowid" OpHelp("r[P2]=rowid"), + /* 128 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), + /* 129 */ "RowCell" OpHelp(""), + /* 130 */ "Delete" OpHelp(""), + /* 131 */ "ResetCount" OpHelp(""), + /* 132 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), + /* 133 */ "SorterData" OpHelp("r[P2]=data"), + /* 134 */ "RowData" OpHelp("r[P2]=data"), + /* 135 */ "Rowid" OpHelp("r[P2]=PX rowid of P1"), + /* 136 */ "NullRow" OpHelp(""), + /* 137 */ "SeekEnd" OpHelp(""), + /* 138 */ "IdxInsert" OpHelp("key=r[P2]"), + /* 139 */ "SorterInsert" OpHelp("key=r[P2]"), + /* 140 */ "IdxDelete" OpHelp("key=r[P2@P3]"), + /* 141 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), + /* 142 */ "IdxRowid" OpHelp("r[P2]=rowid"), + /* 143 */ "FinishSeek" OpHelp(""), + /* 144 */ "Destroy" OpHelp(""), + /* 145 */ "Clear" OpHelp(""), + /* 146 */ "ResetSorter" OpHelp(""), + /* 147 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), + /* 148 */ "SqlExec" OpHelp(""), + /* 149 */ "ParseSchema" OpHelp(""), + /* 150 */ "LoadAnalysis" OpHelp(""), + /* 151 */ "DropTable" OpHelp(""), + /* 152 */ "DropIndex" OpHelp(""), + /* 153 */ "Real" OpHelp("r[P2]=P4"), + /* 154 */ "DropTrigger" OpHelp(""), + /* 155 */ "IntegrityCk" OpHelp(""), + /* 156 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), + /* 157 */ "Param" OpHelp(""), + /* 158 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), + /* 159 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), + /* 160 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), + /* 161 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), + /* 162 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 163 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 164 */ "AggValue" OpHelp("r[P3]=value N=P2"), + /* 165 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 166 */ "Expire" OpHelp(""), + /* 167 */ "CursorLock" OpHelp(""), + /* 168 */ "CursorUnlock" OpHelp(""), + /* 169 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 170 */ "VBegin" OpHelp(""), + /* 171 */ "VCreate" OpHelp(""), + /* 172 */ "VDestroy" OpHelp(""), + /* 173 */ "VOpen" OpHelp(""), + /* 174 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), + /* 175 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 176 */ "VRename" OpHelp(""), + /* 177 */ "Pagecount" OpHelp(""), + /* 178 */ "MaxPgcnt" OpHelp(""), + /* 179 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"), + /* 180 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), + /* 181 */ "Trace" OpHelp(""), + /* 182 */ "CursorHint" OpHelp(""), + /* 183 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), + /* 184 */ "Noop" OpHelp(""), + /* 185 */ "Explain" OpHelp(""), + /* 186 */ "Abortable" OpHelp(""), }; return azName[i]; } #endif /************** End of opcodes.c *********************************************/ +/************** Begin file os_kv.c *******************************************/ +/* +** 2022-09-06 +** +** 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 experimental VFS layer that operates on a +** Key/Value storage engine where both keys and values must be pure +** text. +*/ +/* #include */ +#if SQLITE_OS_KV || (SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)) + +/***************************************************************************** +** Debugging logic +*/ + +/* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */ +#if 0 +#define SQLITE_KV_TRACE(X) printf X +#else +#define SQLITE_KV_TRACE(X) +#endif + +/* SQLITE_KV_LOG() is used for tracing calls to the VFS interface */ +#if 0 +#define SQLITE_KV_LOG(X) printf X +#else +#define SQLITE_KV_LOG(X) +#endif + + +/* +** Forward declaration of objects used by this VFS implementation +*/ +typedef struct KVVfsFile KVVfsFile; + +/* A single open file. There are only two files represented by this +** VFS - the database and the rollback journal. +*/ +struct KVVfsFile { + wx_sqlite3_file base; /* IO methods */ + const char *zClass; /* Storage class */ + int isJournal; /* True if this is a journal file */ + unsigned int nJrnl; /* Space allocated for aJrnl[] */ + char *aJrnl; /* Journal content */ + int szPage; /* Last known page size */ + wx_sqlite3_int64 szDb; /* Database file size. -1 means unknown */ + char *aData; /* Buffer to hold page data */ +}; +#define SQLITE_KVOS_SZ 133073 + +/* +** Methods for KVVfsFile +*/ +static int kvvfsClose(wx_sqlite3_file*); +static int kvvfsReadDb(wx_sqlite3_file*, void*, int iAmt, wx_sqlite3_int64 iOfst); +static int kvvfsReadJrnl(wx_sqlite3_file*, void*, int iAmt, wx_sqlite3_int64 iOfst); +static int kvvfsWriteDb(wx_sqlite3_file*,const void*,int iAmt, wx_sqlite3_int64); +static int kvvfsWriteJrnl(wx_sqlite3_file*,const void*,int iAmt, wx_sqlite3_int64); +static int kvvfsTruncateDb(wx_sqlite3_file*, wx_sqlite3_int64 size); +static int kvvfsTruncateJrnl(wx_sqlite3_file*, wx_sqlite3_int64 size); +static int kvvfsSyncDb(wx_sqlite3_file*, int flags); +static int kvvfsSyncJrnl(wx_sqlite3_file*, int flags); +static int kvvfsFileSizeDb(wx_sqlite3_file*, wx_sqlite3_int64 *pSize); +static int kvvfsFileSizeJrnl(wx_sqlite3_file*, wx_sqlite3_int64 *pSize); +static int kvvfsLock(wx_sqlite3_file*, int); +static int kvvfsUnlock(wx_sqlite3_file*, int); +static int kvvfsCheckReservedLock(wx_sqlite3_file*, int *pResOut); +static int kvvfsFileControlDb(wx_sqlite3_file*, int op, void *pArg); +static int kvvfsFileControlJrnl(wx_sqlite3_file*, int op, void *pArg); +static int kvvfsSectorSize(wx_sqlite3_file*); +static int kvvfsDeviceCharacteristics(wx_sqlite3_file*); + +/* +** Methods for wx_sqlite3_vfs +*/ +static int kvvfsOpen(wx_sqlite3_vfs*, const char *, wx_sqlite3_file*, int , int *); +static int kvvfsDelete(wx_sqlite3_vfs*, const char *zName, int syncDir); +static int kvvfsAccess(wx_sqlite3_vfs*, const char *zName, int flags, int *); +static int kvvfsFullPathname(wx_sqlite3_vfs*, const char *zName, int, char *zOut); +static void *kvvfsDlOpen(wx_sqlite3_vfs*, const char *zFilename); +static int kvvfsRandomness(wx_sqlite3_vfs*, int nByte, char *zOut); +static int kvvfsSleep(wx_sqlite3_vfs*, int microseconds); +static int kvvfsCurrentTime(wx_sqlite3_vfs*, double*); +static int kvvfsCurrentTimeInt64(wx_sqlite3_vfs*, wx_sqlite3_int64*); + +static wx_sqlite3_vfs wx_sqlite3OsKvvfsObject = { + 1, /* iVersion */ + sizeof(KVVfsFile), /* szOsFile */ + 1024, /* mxPathname */ + 0, /* pNext */ + "kvvfs", /* zName */ + 0, /* pAppData */ + kvvfsOpen, /* xOpen */ + kvvfsDelete, /* xDelete */ + kvvfsAccess, /* xAccess */ + kvvfsFullPathname, /* xFullPathname */ + kvvfsDlOpen, /* xDlOpen */ + 0, /* xDlError */ + 0, /* xDlSym */ + 0, /* xDlClose */ + kvvfsRandomness, /* xRandomness */ + kvvfsSleep, /* xSleep */ + kvvfsCurrentTime, /* xCurrentTime */ + 0, /* xGetLastError */ + kvvfsCurrentTimeInt64 /* xCurrentTimeInt64 */ +}; + +/* Methods for wx_sqlite3_file objects referencing a database file +*/ +static wx_sqlite3_io_methods kvvfs_db_io_methods = { + 1, /* iVersion */ + kvvfsClose, /* xClose */ + kvvfsReadDb, /* xRead */ + kvvfsWriteDb, /* xWrite */ + kvvfsTruncateDb, /* xTruncate */ + kvvfsSyncDb, /* xSync */ + kvvfsFileSizeDb, /* xFileSize */ + kvvfsLock, /* xLock */ + kvvfsUnlock, /* xUnlock */ + kvvfsCheckReservedLock, /* xCheckReservedLock */ + kvvfsFileControlDb, /* xFileControl */ + kvvfsSectorSize, /* xSectorSize */ + kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmMap */ + 0, /* xShmLock */ + 0, /* xShmBarrier */ + 0, /* xShmUnmap */ + 0, /* xFetch */ + 0 /* xUnfetch */ +}; + +/* Methods for wx_sqlite3_file objects referencing a rollback journal +*/ +static wx_sqlite3_io_methods kvvfs_jrnl_io_methods = { + 1, /* iVersion */ + kvvfsClose, /* xClose */ + kvvfsReadJrnl, /* xRead */ + kvvfsWriteJrnl, /* xWrite */ + kvvfsTruncateJrnl, /* xTruncate */ + kvvfsSyncJrnl, /* xSync */ + kvvfsFileSizeJrnl, /* xFileSize */ + kvvfsLock, /* xLock */ + kvvfsUnlock, /* xUnlock */ + kvvfsCheckReservedLock, /* xCheckReservedLock */ + kvvfsFileControlJrnl, /* xFileControl */ + kvvfsSectorSize, /* xSectorSize */ + kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmMap */ + 0, /* xShmLock */ + 0, /* xShmBarrier */ + 0, /* xShmUnmap */ + 0, /* xFetch */ + 0 /* xUnfetch */ +}; + +/****** Storage subsystem **************************************************/ +#include +#include +#include + +/* Forward declarations for the low-level storage engine +*/ +static int kvstorageWrite(const char*, const char *zKey, const char *zData); +static int kvstorageDelete(const char*, const char *zKey); +static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf); +#define KVSTORAGE_KEY_SZ 32 + +/* Expand the key name with an appropriate prefix and put the result +** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least +** KVSTORAGE_KEY_SZ bytes. +*/ +static void kvstorageMakeKey( + const char *zClass, + const char *zKeyIn, + char *zKeyOut +){ + wx_sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn); +} + +/* Write content into a key. zClass is the particular namespace of the +** underlying key/value store to use - either "local" or "session". +** +** Both zKey and zData are zero-terminated pure text strings. +** +** Return the number of errors. +*/ +static int kvstorageWrite( + const char *zClass, + const char *zKey, + const char *zData +){ + FILE *fd; + char zXKey[KVSTORAGE_KEY_SZ]; + kvstorageMakeKey(zClass, zKey, zXKey); + fd = fopen(zXKey, "wb"); + if( fd ){ + SQLITE_KV_TRACE(("KVVFS-WRITE %-15s (%d) %.50s%s\n", zXKey, + (int)strlen(zData), zData, + strlen(zData)>50 ? "..." : "")); + fputs(zData, fd); + fclose(fd); + return 0; + }else{ + return 1; + } +} + +/* Delete a key (with its corresponding data) from the key/value +** namespace given by zClass. If the key does not previously exist, +** this routine is a no-op. +*/ +static int kvstorageDelete(const char *zClass, const char *zKey){ + char zXKey[KVSTORAGE_KEY_SZ]; + kvstorageMakeKey(zClass, zKey, zXKey); + unlink(zXKey); + SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey)); + return 0; +} + +/* Read the value associated with a zKey from the key/value namespace given +** by zClass and put the text data associated with that key in the first +** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large +** enough to hold it all. The value put into zBuf must always be zero +** terminated, even if it gets truncated because nBuf is not large enough. +** +** Return the total number of bytes in the data, without truncation, and +** not counting the final zero terminator. Return -1 if the key does +** not exist. +** +** If nBuf<=0 then this routine simply returns the size of the data without +** actually reading it. +*/ +static int kvstorageRead( + const char *zClass, + const char *zKey, + char *zBuf, + int nBuf +){ + FILE *fd; + struct stat buf; + char zXKey[KVSTORAGE_KEY_SZ]; + kvstorageMakeKey(zClass, zKey, zXKey); + if( access(zXKey, R_OK)!=0 + || stat(zXKey, &buf)!=0 + || !S_ISREG(buf.st_mode) + ){ + SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); + return -1; + } + if( nBuf<=0 ){ + return (int)buf.st_size; + }else if( nBuf==1 ){ + zBuf[0] = 0; + SQLITE_KV_TRACE(("KVVFS-READ %-15s (%d)\n", zXKey, + (int)buf.st_size)); + return (int)buf.st_size; + } + if( nBuf > buf.st_size + 1 ){ + nBuf = buf.st_size + 1; + } + fd = fopen(zXKey, "rb"); + if( fd==0 ){ + SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); + return -1; + }else{ + wx_sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd); + fclose(fd); + zBuf[n] = 0; + SQLITE_KV_TRACE(("KVVFS-READ %-15s (%lld) %.50s%s\n", zXKey, + n, zBuf, n>50 ? "..." : "")); + return (int)n; + } +} + +/* +** An internal level of indirection which enables us to replace the +** kvvfs i/o methods with JavaScript implementations in WASM builds. +** Maintenance reminder: if this struct changes in any way, the JSON +** rendering of its structure must be updated in +** wx_sqlite3_wasm_enum_json(). There are no binary compatibility +** concerns, so it does not need an iVersion member. This file is +** necessarily always compiled together with wx_sqlite3_wasm_enum_json(), +** and JS code dynamically creates the mapping of members based on +** that JSON description. +*/ +typedef struct wx_sqlite3_kvvfs_methods wx_sqlite3_kvvfs_methods; +struct wx_sqlite3_kvvfs_methods { + int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf); + int (*xWrite)(const char *zClass, const char *zKey, const char *zData); + int (*xDelete)(const char *zClass, const char *zKey); + const int nKeySize; +}; + +/* +** This object holds the kvvfs I/O methods which may be swapped out +** for JavaScript-side implementations in WASM builds. In such builds +** it cannot be const, but in native builds it should be so that +** the compiler can hopefully optimize this level of indirection out. +** That said, kvvfs is intended primarily for use in WASM builds. +** +** Note that this is not explicitly flagged as static because the +** amalgamation build will tag it with SQLITE_PRIVATE. +*/ +#ifndef SQLITE_WASM +const +#endif +SQLITE_PRIVATE wx_sqlite3_kvvfs_methods wx_sqlite3KvvfsMethods = { +kvstorageRead, +kvstorageWrite, +kvstorageDelete, +KVSTORAGE_KEY_SZ +}; + +/****** Utility subroutines ************************************************/ + +/* +** Encode binary into the text encoded used to persist on disk. +** The output text is stored in aOut[], which must be at least +** nData+1 bytes in length. +** +** Return the actual length of the encoded text, not counting the +** zero terminator at the end. +** +** Encoding format +** --------------- +** +** * Non-zero bytes are encoded as upper-case hexadecimal +** +** * A sequence of one or more zero-bytes that are not at the +** beginning of the buffer are encoded as a little-endian +** base-26 number using a..z. "a" means 0. "b" means 1, +** "z" means 25. "ab" means 26. "ac" means 52. And so forth. +** +** * Because there is no overlap between the encoding characters +** of hexadecimal and base-26 numbers, it is always clear where +** one stops and the next begins. +*/ +static int kvvfsEncode(const char *aData, int nData, char *aOut){ + int i, j; + const unsigned char *a = (const unsigned char*)aData; + for(i=j=0; i>4]; + aOut[j++] = "0123456789ABCDEF"[c&0xf]; + }else{ + /* A sequence of 1 or more zeros is stored as a little-endian + ** base-26 number using a..z as the digits. So one zero is "b". + ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb", + ** and so forth. + */ + int k; + for(k=1; i+k0 ){ + aOut[j++] = 'a'+(k%26); + k /= 26; + } + } + } + aOut[j] = 0; + return j; +} + +static const signed char kvvfsHexValue[256] = { + -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, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -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, -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 +}; + +/* +** Decode the text encoding back to binary. The binary content is +** written into pOut, which must be at least nOut bytes in length. +** +** The return value is the number of bytes actually written into aOut[]. +*/ +static int kvvfsDecode(const char *a, char *aOut, int nOut){ + int i, j; + int c; + const unsigned char *aIn = (const unsigned char*)a; + i = 0; + j = 0; + while( 1 ){ + c = kvvfsHexValue[aIn[i]]; + if( c<0 ){ + int n = 0; + int mult = 1; + c = aIn[i]; + if( c==0 ) break; + while( c>='a' && c<='z' ){ + n += (c - 'a')*mult; + mult *= 26; + c = aIn[++i]; + } + if( j+n>nOut ) return -1; + memset(&aOut[j], 0, n); + j += n; + if( c==0 || mult==1 ) break; /* progress stalled if mult==1 */ + }else{ + aOut[j] = c<<4; + c = kvvfsHexValue[aIn[++i]]; + if( c<0 ) break; + aOut[j++] += c; + i++; + } + } + return j; +} + +/* +** Decode a complete journal file. Allocate space in pFile->aJrnl +** and store the decoding there. Or leave pFile->aJrnl set to NULL +** if an error is encountered. +** +** The first few characters of the text encoding will be a little-endian +** base-26 number (digits a..z) that is the total number of bytes +** in the decoded journal file image. This base-26 number is followed +** by a single space, then the encoding of the journal. The space +** separator is required to act as a terminator for the base-26 number. +*/ +static void kvvfsDecodeJournal( + KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */ + const char *zTxt, /* Text encoding. Zero-terminated */ + int nTxt /* Bytes in zTxt, excluding zero terminator */ +){ + unsigned int n = 0; + int c, i, mult; + i = 0; + mult = 1; + while( (c = zTxt[i++])>='a' && c<='z' ){ + n += (zTxt[i] - 'a')*mult; + mult *= 26; + } + wx_sqlite3_free(pFile->aJrnl); + pFile->aJrnl = wx_sqlite3_malloc64( n ); + if( pFile->aJrnl==0 ){ + pFile->nJrnl = 0; + return; + } + pFile->nJrnl = n; + n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl); + if( nnJrnl ){ + wx_sqlite3_free(pFile->aJrnl); + pFile->aJrnl = 0; + pFile->nJrnl = 0; + } +} + +/* +** Read or write the "sz" element, containing the database file size. +*/ +static wx_sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ + char zData[50]; + zData[0] = 0; + wx_sqlite3KvvfsMethods.xRead(pFile->zClass, "sz", zData, sizeof(zData)-1); + return strtoll(zData, 0, 0); +} +static int kvvfsWriteFileSize(KVVfsFile *pFile, wx_sqlite3_int64 sz){ + char zData[50]; + wx_sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); + return wx_sqlite3KvvfsMethods.xWrite(pFile->zClass, "sz", zData); +} + +/****** wx_sqlite3_io_methods methods ******************************************/ + +/* +** Close an kvvfs-file. +*/ +static int kvvfsClose(wx_sqlite3_file *pProtoFile){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + + SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass, + pFile->isJournal ? "journal" : "db")); + wx_sqlite3_free(pFile->aJrnl); + wx_sqlite3_free(pFile->aData); + return SQLITE_OK; +} + +/* +** Read from the -journal file. +*/ +static int kvvfsReadJrnl( + wx_sqlite3_file *pProtoFile, + void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + KVVfsFile *pFile = (KVVfsFile*)pProtoFile; + assert( pFile->isJournal ); + SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); + if( pFile->aJrnl==0 ){ + int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0); + char *aTxt; + if( szTxt<=4 ){ + return SQLITE_IOERR; + } + aTxt = wx_sqlite3_malloc64( szTxt+1 ); + if( aTxt==0 ) return SQLITE_NOMEM; + kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1); + kvvfsDecodeJournal(pFile, aTxt, szTxt); + wx_sqlite3_free(aTxt); + if( pFile->aJrnl==0 ) return SQLITE_IOERR; + } + if( iOfst+iAmt>pFile->nJrnl ){ + return SQLITE_IOERR_SHORT_READ; + } + memcpy(zBuf, pFile->aJrnl+iOfst, iAmt); + return SQLITE_OK; +} + +/* +** Read from the database file. +*/ +static int kvvfsReadDb( + wx_sqlite3_file *pProtoFile, + void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + KVVfsFile *pFile = (KVVfsFile*)pProtoFile; + unsigned int pgno; + int got, n; + char zKey[30]; + char *aData = pFile->aData; + assert( iOfst>=0 ); + assert( iAmt>=0 ); + SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); + if( iOfst+iAmt>=512 ){ + if( (iOfst % iAmt)!=0 ){ + return SQLITE_IOERR_READ; + } + if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){ + return SQLITE_IOERR_READ; + } + pFile->szPage = iAmt; + pgno = 1 + iOfst/iAmt; + }else{ + pgno = 1; + } + wx_sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); + got = wx_sqlite3KvvfsMethods.xRead(pFile->zClass, zKey, + aData, SQLITE_KVOS_SZ-1); + if( got<0 ){ + n = 0; + }else{ + aData[got] = 0; + if( iOfst+iAmt<512 ){ + int k = iOfst+iAmt; + aData[k*2] = 0; + n = kvvfsDecode(aData, &aData[2000], SQLITE_KVOS_SZ-2000); + if( n>=iOfst+iAmt ){ + memcpy(zBuf, &aData[2000+iOfst], iAmt); + n = iAmt; + }else{ + n = 0; + } + }else{ + n = kvvfsDecode(aData, zBuf, iAmt); + } + } + if( nzClass, iAmt, iOfst)); + if( iEnd>=0x10000000 ) return SQLITE_FULL; + if( pFile->aJrnl==0 || pFile->nJrnlaJrnl, iEnd); + if( aNew==0 ){ + return SQLITE_IOERR_NOMEM; + } + pFile->aJrnl = aNew; + if( pFile->nJrnlaJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl); + } + pFile->nJrnl = iEnd; + } + memcpy(pFile->aJrnl+iOfst, zBuf, iAmt); + return SQLITE_OK; +} + +/* +** Write into the database file. +*/ +static int kvvfsWriteDb( + wx_sqlite3_file *pProtoFile, + const void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + KVVfsFile *pFile = (KVVfsFile*)pProtoFile; + unsigned int pgno; + char zKey[30]; + char *aData = pFile->aData; + SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); + assert( iAmt>=512 && iAmt<=65536 ); + assert( (iAmt & (iAmt-1))==0 ); + assert( pFile->szPage<0 || pFile->szPage==iAmt ); + pFile->szPage = iAmt; + pgno = 1 + iOfst/iAmt; + wx_sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); + kvvfsEncode(zBuf, iAmt, aData); + if( wx_sqlite3KvvfsMethods.xWrite(pFile->zClass, zKey, aData) ){ + return SQLITE_IOERR; + } + if( iOfst+iAmt > pFile->szDb ){ + pFile->szDb = iOfst + iAmt; + } + return SQLITE_OK; +} + +/* +** Truncate an kvvfs-file. +*/ +static int kvvfsTruncateJrnl(wx_sqlite3_file *pProtoFile, sqlite_int64 size){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size)); + assert( size==0 ); + wx_sqlite3KvvfsMethods.xDelete(pFile->zClass, "jrnl"); + wx_sqlite3_free(pFile->aJrnl); + pFile->aJrnl = 0; + pFile->nJrnl = 0; + return SQLITE_OK; +} +static int kvvfsTruncateDb(wx_sqlite3_file *pProtoFile, sqlite_int64 size){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + if( pFile->szDb>size + && pFile->szPage>0 + && (size % pFile->szPage)==0 + ){ + char zKey[50]; + unsigned int pgno, pgnoMax; + SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size)); + pgno = 1 + size/pFile->szPage; + pgnoMax = 2 + pFile->szDb/pFile->szPage; + while( pgno<=pgnoMax ){ + wx_sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); + wx_sqlite3KvvfsMethods.xDelete(pFile->zClass, zKey); + pgno++; + } + pFile->szDb = size; + return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK; + } + return SQLITE_IOERR; +} + +/* +** Sync an kvvfs-file. +*/ +static int kvvfsSyncJrnl(wx_sqlite3_file *pProtoFile, int flags){ + int i, n; + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + char *zOut; + SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass)); + if( pFile->nJrnl<=0 ){ + return kvvfsTruncateJrnl(pProtoFile, 0); + } + zOut = wx_sqlite3_malloc64( pFile->nJrnl*2 + 50 ); + if( zOut==0 ){ + return SQLITE_IOERR_NOMEM; + } + n = pFile->nJrnl; + i = 0; + do{ + zOut[i++] = 'a' + (n%26); + n /= 26; + }while( n>0 ); + zOut[i++] = ' '; + kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); + i = wx_sqlite3KvvfsMethods.xWrite(pFile->zClass, "jrnl", zOut); + wx_sqlite3_free(zOut); + return i ? SQLITE_IOERR : SQLITE_OK; +} +static int kvvfsSyncDb(wx_sqlite3_file *pProtoFile, int flags){ + return SQLITE_OK; +} + +/* +** Return the current file-size of an kvvfs-file. +*/ +static int kvvfsFileSizeJrnl(wx_sqlite3_file *pProtoFile, sqlite_int64 *pSize){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass)); + *pSize = pFile->nJrnl; + return SQLITE_OK; +} +static int kvvfsFileSizeDb(wx_sqlite3_file *pProtoFile, sqlite_int64 *pSize){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass)); + if( pFile->szDb>=0 ){ + *pSize = pFile->szDb; + }else{ + *pSize = kvvfsReadFileSize(pFile); + } + return SQLITE_OK; +} + +/* +** Lock an kvvfs-file. +*/ +static int kvvfsLock(wx_sqlite3_file *pProtoFile, int eLock){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + assert( !pFile->isJournal ); + SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock)); + + if( eLock!=SQLITE_LOCK_NONE ){ + pFile->szDb = kvvfsReadFileSize(pFile); + } + return SQLITE_OK; +} + +/* +** Unlock an kvvfs-file. +*/ +static int kvvfsUnlock(wx_sqlite3_file *pProtoFile, int eLock){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + assert( !pFile->isJournal ); + SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock)); + if( eLock==SQLITE_LOCK_NONE ){ + pFile->szDb = -1; + } + return SQLITE_OK; +} + +/* +** Check if another file-handle holds a RESERVED lock on an kvvfs-file. +*/ +static int kvvfsCheckReservedLock(wx_sqlite3_file *pProtoFile, int *pResOut){ + SQLITE_KV_LOG(("xCheckReservedLock\n")); + *pResOut = 0; + return SQLITE_OK; +} + +/* +** File control method. For custom operations on an kvvfs-file. +*/ +static int kvvfsFileControlJrnl(wx_sqlite3_file *pProtoFile, int op, void *pArg){ + SQLITE_KV_LOG(("xFileControl(%d) on journal\n", op)); + return SQLITE_NOTFOUND; +} +static int kvvfsFileControlDb(wx_sqlite3_file *pProtoFile, int op, void *pArg){ + SQLITE_KV_LOG(("xFileControl(%d) on database\n", op)); + if( op==SQLITE_FCNTL_SYNC ){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + int rc = SQLITE_OK; + SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); + if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){ + rc = SQLITE_IOERR; + } + return rc; + } + return SQLITE_NOTFOUND; +} + +/* +** Return the sector-size in bytes for an kvvfs-file. +*/ +static int kvvfsSectorSize(wx_sqlite3_file *pFile){ + return 512; +} + +/* +** Return the device characteristic flags supported by an kvvfs-file. +*/ +static int kvvfsDeviceCharacteristics(wx_sqlite3_file *pProtoFile){ + return 0; +} + +/****** wx_sqlite3_vfs methods *************************************************/ + +/* +** Open an kvvfs file handle. +*/ +static int kvvfsOpen( + wx_sqlite3_vfs *pProtoVfs, + const char *zName, + wx_sqlite3_file *pProtoFile, + int flags, + int *pOutFlags +){ + KVVfsFile *pFile = (KVVfsFile*)pProtoFile; + if( zName==0 ) zName = ""; + SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName)); + if( strcmp(zName, "local")==0 + || strcmp(zName, "session")==0 + ){ + pFile->isJournal = 0; + pFile->base.pMethods = &kvvfs_db_io_methods; + }else + if( strcmp(zName, "local-journal")==0 + || strcmp(zName, "session-journal")==0 + ){ + pFile->isJournal = 1; + pFile->base.pMethods = &kvvfs_jrnl_io_methods; + }else{ + return SQLITE_CANTOPEN; + } + if( zName[0]=='s' ){ + pFile->zClass = "session"; + }else{ + pFile->zClass = "local"; + } + pFile->aData = wx_sqlite3_malloc64(SQLITE_KVOS_SZ); + if( pFile->aData==0 ){ + return SQLITE_NOMEM; + } + pFile->aJrnl = 0; + pFile->nJrnl = 0; + pFile->szPage = -1; + pFile->szDb = -1; + return SQLITE_OK; +} + +/* +** Delete the file located at zPath. If the dirSync argument is true, +** ensure the file-system modifications are synced to disk before +** returning. +*/ +static int kvvfsDelete(wx_sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + if( strcmp(zPath, "local-journal")==0 ){ + wx_sqlite3KvvfsMethods.xDelete("local", "jrnl"); + }else + if( strcmp(zPath, "session-journal")==0 ){ + wx_sqlite3KvvfsMethods.xDelete("session", "jrnl"); + } + return SQLITE_OK; +} + +/* +** Test for access permissions. Return true if the requested permission +** is available, or false otherwise. +*/ +static int kvvfsAccess( + wx_sqlite3_vfs *pProtoVfs, + const char *zPath, + int flags, + int *pResOut +){ + SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath)); + if( strcmp(zPath, "local-journal")==0 ){ + *pResOut = wx_sqlite3KvvfsMethods.xRead("local", "jrnl", 0, 0)>0; + }else + if( strcmp(zPath, "session-journal")==0 ){ + *pResOut = wx_sqlite3KvvfsMethods.xRead("session", "jrnl", 0, 0)>0; + }else + if( strcmp(zPath, "local")==0 ){ + *pResOut = wx_sqlite3KvvfsMethods.xRead("local", "sz", 0, 0)>0; + }else + if( strcmp(zPath, "session")==0 ){ + *pResOut = wx_sqlite3KvvfsMethods.xRead("session", "sz", 0, 0)>0; + }else + { + *pResOut = 0; + } + SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut)); + return SQLITE_OK; +} + +/* +** Populate buffer zOut with the full canonical pathname corresponding +** to the pathname in zPath. zOut is guaranteed to point to a buffer +** of at least (INST_MAX_PATHNAME+1) bytes. +*/ +static int kvvfsFullPathname( + wx_sqlite3_vfs *pVfs, + const char *zPath, + int nOut, + char *zOut +){ + size_t nPath; +#ifdef SQLITE_OS_KV_ALWAYS_LOCAL + zPath = "local"; +#endif + nPath = strlen(zPath); + SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath)); + if( nOut +static int kvvfsCurrentTimeInt64(wx_sqlite3_vfs *pVfs, wx_sqlite3_int64 *pTimeOut){ + static const wx_sqlite3_int64 unixEpoch = 24405875*(wx_sqlite3_int64)8640000; + struct timeval sNow; + (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */ + *pTimeOut = unixEpoch + 1000*(wx_sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; + return SQLITE_OK; +} +#endif /* SQLITE_OS_KV || SQLITE_OS_UNIX */ + +#if SQLITE_OS_KV +/* +** This routine is called initialize the KV-vfs as the default VFS. +*/ +SQLITE_API int wx_sqlite3_os_init(void){ + return wx_sqlite3_vfs_register(&wx_sqlite3OsKvvfsObject, 1); +} +SQLITE_API int wx_sqlite3_os_end(void){ + return SQLITE_OK; +} +#endif /* SQLITE_OS_KV */ + +#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) +SQLITE_PRIVATE int wx_sqlite3KvvfsInit(void){ + return wx_sqlite3_vfs_register(&wx_sqlite3OsKvvfsObject, 0); +} +#endif + +/************** End of os_kv.c ***********************************************/ /************** Begin file os_unix.c *****************************************/ /* ** 2004 May 22 @@ -33824,15 +37103,16 @@ SQLITE_PRIVATE const char *wx_sqlite3OpcodeName(int i){ /* ** standard include files. */ -#include -#include +#include /* amalgamator: keep */ +#include /* amalgamator: keep */ #include #include -#include +#include /* amalgamator: keep */ /* #include */ -#include +#include /* amalgamator: keep */ #include -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 +#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ + && !defined(SQLITE_WASI) # include #endif @@ -33913,16 +37193,57 @@ SQLITE_PRIVATE const char *wx_sqlite3OpcodeName(int i){ /* ** Maximum supported path-length. */ +#if SQLITE3MC_MAX_PATHNAME > 512 +#define MAX_PATHNAME SQLITE3MC_MAX_PATHNAME +#else #define MAX_PATHNAME 512 +#endif /* ** Maximum supported symbolic links */ #define SQLITE_MAX_SYMLINKS 100 +/* +** Remove and stub certain info for WASI (WebAssembly System +** Interface) builds. +*/ +#ifdef SQLITE_WASI +# undef HAVE_FCHMOD +# undef HAVE_FCHOWN +# undef HAVE_MREMAP +# define HAVE_MREMAP 0 +# ifndef SQLITE_DEFAULT_UNIX_VFS +# define SQLITE_DEFAULT_UNIX_VFS "unix-dotfile" + /* ^^^ should SQLITE_DEFAULT_UNIX_VFS be "unix-none"? */ +# endif +# ifndef F_RDLCK +# define F_RDLCK 0 +# define F_WRLCK 1 +# define F_UNLCK 2 +# if __LONG_MAX == 0x7fffffffL +# define F_GETLK 12 +# define F_SETLK 13 +# define F_SETLKW 14 +# else +# define F_GETLK 5 +# define F_SETLK 6 +# define F_SETLKW 7 +# endif +# endif +#else /* !SQLITE_WASI */ +# ifndef HAVE_FCHMOD +# define HAVE_FCHMOD +# endif +#endif /* SQLITE_WASI */ + +#ifdef SQLITE_WASI +# define osGetpid(X) (pid_t)1 +#else /* Always cast the getpid() return type for compatibility with ** kernel modules in VxWorks. */ -#define osGetpid(X) (pid_t)getpid() +# define osGetpid(X) (pid_t)getpid() +#endif /* ** Only set the lastErrno if the error code is a real error and not @@ -34034,205 +37355,7 @@ static pid_t randomnessPid = 0; /* ** Include code that is common to all os_*.c files */ -/************** Include os_common.h in the middle of os_unix.c ***************/ -/************** Begin file os_common.h ***************************************/ -/* -** 2004 May 22 -** -** 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 macros and a little bit of code that is common to -** all of the platform-specific files (os_*.c) and is #included into those -** files. -** -** This file should be #included by the os_*.c files only. It is not a -** general purpose header file. -*/ -#ifndef _OS_COMMON_H_ -#define _OS_COMMON_H_ - -/* -** At least two bugs have slipped in because we changed the MEMORY_DEBUG -** macro to SQLITE_DEBUG and some older makefiles have not yet made the -** switch. The following code should catch this problem at compile-time. -*/ -#ifdef MEMORY_DEBUG -# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." -#endif - -/* -** Macros for performance tracing. Normally turned off. Only works -** on i486 hardware. -*/ -#ifdef SQLITE_PERFORMANCE_TRACE - -/* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of os_common.h ****************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** 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 inline asm code for retrieving "high-performance" -** counters for x86 and x86_64 class CPUs. -*/ -#ifndef SQLITE_HWTIME_H -#define SQLITE_HWTIME_H - -/* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if !defined(__STRICT_ANSI__) && \ - (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) - - #if defined(__GNUC__) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } - - #elif defined(_MSC_VER) - - __declspec(naked) __inline sqlite_uint64 __cdecl wx_sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } - } - - #endif - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; - } - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; - } - -#else - - /* - ** asm() is needed for hardware timing support. Without asm(), - ** disable the wx_sqlite3Hwtime() routine. - ** - ** wx_sqlite3Hwtime() is only used for some obscure debugging - ** and analysis configurations, not in any deliverable, so this - ** should not be a great loss. - */ -SQLITE_PRIVATE sqlite_uint64 wx_sqlite3Hwtime(void){ return ((sqlite_uint64)0); } - -#endif - -#endif /* !defined(SQLITE_HWTIME_H) */ - -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in os_common.h ******************/ - -static sqlite_uint64 g_start; -static sqlite_uint64 g_elapsed; -#define TIMER_START g_start=wx_sqlite3Hwtime() -#define TIMER_END g_elapsed=wx_sqlite3Hwtime()-g_start -#define TIMER_ELAPSED g_elapsed -#else -#define TIMER_START -#define TIMER_END -#define TIMER_ELAPSED ((sqlite_uint64)0) -#endif - -/* -** If we compile with the SQLITE_TEST macro set, then the following block -** of code will give us the ability to simulate a disk I/O error. This -** is used for testing the I/O recovery logic. -*/ -#if defined(SQLITE_TEST) -SQLITE_API extern int wx_sqlite3_io_error_hit; -SQLITE_API extern int wx_sqlite3_io_error_hardhit; -SQLITE_API extern int wx_sqlite3_io_error_pending; -SQLITE_API extern int wx_sqlite3_io_error_persist; -SQLITE_API extern int wx_sqlite3_io_error_benign; -SQLITE_API extern int wx_sqlite3_diskfull_pending; -SQLITE_API extern int wx_sqlite3_diskfull; -#define SimulateIOErrorBenign(X) wx_sqlite3_io_error_benign=(X) -#define SimulateIOError(CODE) \ - if( (wx_sqlite3_io_error_persist && wx_sqlite3_io_error_hit) \ - || wx_sqlite3_io_error_pending-- == 1 ) \ - { local_ioerr(); CODE; } -static void local_ioerr(){ - IOTRACE(("IOERR\n")); - wx_sqlite3_io_error_hit++; - if( !wx_sqlite3_io_error_benign ) wx_sqlite3_io_error_hardhit++; -} -#define SimulateDiskfullError(CODE) \ - if( wx_sqlite3_diskfull_pending ){ \ - if( wx_sqlite3_diskfull_pending == 1 ){ \ - local_ioerr(); \ - wx_sqlite3_diskfull = 1; \ - wx_sqlite3_io_error_hit = 1; \ - CODE; \ - }else{ \ - wx_sqlite3_diskfull_pending--; \ - } \ - } -#else -#define SimulateIOErrorBenign(X) -#define SimulateIOError(A) -#define SimulateDiskfullError(A) -#endif /* defined(SQLITE_TEST) */ - -/* -** When testing, keep a count of the number of open files. -*/ -#if defined(SQLITE_TEST) -SQLITE_API extern int wx_sqlite3_open_file_count; -#define OpenCounter(X) wx_sqlite3_open_file_count+=(X) -#else -#define OpenCounter(X) -#endif /* defined(SQLITE_TEST) */ - -#endif /* !defined(_OS_COMMON_H_) */ - -/************** End of os_common.h *******************************************/ -/************** Continuing where we left off in os_unix.c ********************/ +/* #include "os_common.h" */ /* ** Define various macros that are missing from some systems. @@ -34392,7 +37515,11 @@ static struct unix_syscall { #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\ aSyscall[13].pCurrent) +#if defined(HAVE_FCHMOD) { "fchmod", (wx_sqlite3_syscall_ptr)fchmod, 0 }, +#else + { "fchmod", (wx_sqlite3_syscall_ptr)0, 0 }, +#endif #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE @@ -34428,14 +37555,16 @@ static struct unix_syscall { #endif #define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 +#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ + && !defined(SQLITE_WASI) { "mmap", (wx_sqlite3_syscall_ptr)mmap, 0 }, #else { "mmap", (wx_sqlite3_syscall_ptr)0, 0 }, #endif #define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent) -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 +#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ + && !defined(SQLITE_WASI) { "munmap", (wx_sqlite3_syscall_ptr)munmap, 0 }, #else { "munmap", (wx_sqlite3_syscall_ptr)0, 0 }, @@ -34621,6 +37750,9 @@ static int robust_open(const char *z, int f, mode_t m){ break; } if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break; + if( (f & (O_EXCL|O_CREAT))==(O_EXCL|O_CREAT) ){ + (void)osUnlink(z); + } osClose(fd); wx_sqlite3_log(SQLITE_WARNING, "attempt to open \"%s\" as file descriptor %d", z, fd); @@ -35583,7 +38715,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ ** ** UNLOCKED -> SHARED ** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE +** SHARED -> EXCLUSIVE ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** @@ -35616,19 +38748,20 @@ static int unixLock(wx_sqlite3_file *id, int eFileLock){ ** A RESERVED lock is implemented by grabbing a write-lock on the ** 'reserved byte'. ** - ** A process may only obtain a PENDING lock after it has obtained a - ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock - ** on the 'pending byte'. This ensures that no new SHARED locks can be - ** obtained, but existing SHARED locks are allowed to persist. A process - ** does not have to obtain a RESERVED lock on the way to a PENDING lock. - ** This property is used by the algorithm for rolling back a journal file - ** after a crash. + ** An EXCLUSIVE lock may only be requested after either a SHARED or + ** RESERVED lock is held. An EXCLUSIVE lock is implemented by obtaining + ** a write-lock on the entire 'shared byte range'. Since all other locks + ** require a read-lock on one of the bytes within this range, this ensures + ** that no other locks are held on the database. ** - ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is - ** implemented by obtaining a write-lock on the entire 'shared byte - ** range'. Since all other locks require a read-lock on one of the bytes - ** within this range, this ensures that no other locks are held on the - ** database. + ** If a process that holds a RESERVED lock requests an EXCLUSIVE, then + ** a PENDING lock is obtained first. A PENDING lock is implemented by + ** obtaining a write-lock on the 'pending byte'. This ensures that no new + ** SHARED locks can be obtained, but existing SHARED locks are allowed to + ** persist. If the call to this function fails to obtain the EXCLUSIVE + ** lock in this case, it holds the PENDING lock intead. The client may + ** then re-attempt the EXCLUSIVE lock later on, after existing SHARED + ** locks have cleared. */ int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; @@ -35699,7 +38832,7 @@ static int unixLock(wx_sqlite3_file *id, int eFileLock){ lock.l_len = 1L; lock.l_whence = SEEK_SET; if( eFileLock==SHARED_LOCK - || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLockeFileLock==RESERVED_LOCK) ){ lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); lock.l_start = PENDING_BYTE; @@ -35710,6 +38843,9 @@ static int unixLock(wx_sqlite3_file *id, int eFileLock){ storeLastErrno(pFile, tErrno); } goto end_lock; + }else if( eFileLock==EXCLUSIVE_LOCK ){ + pFile->eFileLock = PENDING_LOCK; + pInode->eFileLock = PENDING_LOCK; } } @@ -35797,13 +38933,9 @@ static int unixLock(wx_sqlite3_file *id, int eFileLock){ } #endif - if( rc==SQLITE_OK ){ pFile->eFileLock = eFileLock; pInode->eFileLock = eFileLock; - }else if( eFileLock==EXCLUSIVE_LOCK ){ - pFile->eFileLock = PENDING_LOCK; - pInode->eFileLock = PENDING_LOCK; } end_lock: @@ -37886,6 +41018,9 @@ static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ /* Forward declaration */ static int unixGetTempname(int nBuf, char *zBuf); +#ifndef SQLITE_OMIT_WAL + static int unixFcntlExternalReader(unixFile*, int*); +#endif /* ** Information and control of an open file handle. @@ -38002,6 +41137,15 @@ static int unixFileControl(wx_sqlite3_file *id, int op, void *pArg){ return proxyFileControl(id,op,pArg); } #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ + + case SQLITE_FCNTL_EXTERNAL_READER: { +#ifndef SQLITE_OMIT_WAL + return unixFcntlExternalReader((unixFile*)id, (int*)pArg); +#else + *(int*)pArg = 0; + return SQLITE_OK; +#endif + } } return SQLITE_NOTFOUND; } @@ -38247,6 +41391,40 @@ struct unixShm { #define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ #define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ +/* +** Use F_GETLK to check whether or not there are any readers with open +** wal-mode transactions in other processes on database file pFile. If +** no error occurs, return SQLITE_OK and set (*piOut) to 1 if there are +** such transactions, or 0 otherwise. If an error occurs, return an +** SQLite error code. The final value of *piOut is undefined in this +** case. +*/ +static int unixFcntlExternalReader(unixFile *pFile, int *piOut){ + int rc = SQLITE_OK; + *piOut = 0; + if( pFile->pShm){ + unixShmNode *pShmNode = pFile->pShm->pShmNode; + struct flock f; + + memset(&f, 0, sizeof(f)); + f.l_type = F_WRLCK; + f.l_whence = SEEK_SET; + f.l_start = UNIX_SHM_BASE + 3; + f.l_len = SQLITE_SHM_NLOCK - 3; + + wx_sqlite3_mutex_enter(pShmNode->pShmMutex); + if( osFcntl(pShmNode->hShm, F_GETLK, &f)<0 ){ + rc = SQLITE_IOERR_LOCK; + }else{ + *piOut = (f.l_type!=F_UNLCK); + } + wx_sqlite3_mutex_leave(pShmNode->pShmMutex); + } + + return rc; +} + + /* ** Apply posix advisory locks for all bytes from ofst through ofst+n-1. ** @@ -38799,11 +41977,17 @@ static int unixShmLock( int flags /* What to do with the lock */ ){ unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */ - unixShm *p = pDbFd->pShm; /* The shared memory being locked */ - unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */ + unixShm *p; /* The shared memory being locked */ + unixShmNode *pShmNode; /* The underlying file iNode */ int rc = SQLITE_OK; /* Result code */ u16 mask; /* Mask of locks to take or release */ - int *aLock = pShmNode->aLock; + int *aLock; + + p = pDbFd->pShm; + if( p==0 ) return SQLITE_IOERR_SHMLOCK; + pShmNode = p->pShmNode; + if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; + aLock = pShmNode->aLock; assert( pShmNode==pDbFd->pInode->pShmNode ); assert( pShmNode->pInode==pDbFd->pInode ); @@ -39687,25 +42871,35 @@ static int fillInUnixFile( return rc; } +/* +** Directories to consider for temp files. +*/ +static const char *azTempDirs[] = { + 0, + 0, + "/var/tmp", + "/usr/tmp", + "/tmp", + "." +}; + +/* +** Initialize first two members of azTempDirs[] array. +*/ +static void unixTempFileInit(void){ + azTempDirs[0] = getenv("SQLITE_TMPDIR"); + azTempDirs[1] = getenv("TMPDIR"); +} + /* ** Return the name of a directory in which to put temporary files. ** If no suitable temporary file directory can be found, return NULL. */ static const char *unixTempFileDir(void){ - static const char *azDirs[] = { - 0, - 0, - "/var/tmp", - "/usr/tmp", - "/tmp", - "." - }; unsigned int i = 0; struct stat buf; const char *zDir = wx_sqlite3_temp_directory; - if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); - if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); while(1){ if( zDir!=0 && osStat(zDir, &buf)==0 @@ -39714,8 +42908,8 @@ static const char *unixTempFileDir(void){ ){ return zDir; } - if( i>=sizeof(azDirs)/sizeof(azDirs[0]) ) break; - zDir = azDirs[i++]; + if( i>=sizeof(azTempDirs)/sizeof(azTempDirs[0]) ) break; + zDir = azTempDirs[i++]; } return 0; } @@ -39728,6 +42922,7 @@ static const char *unixTempFileDir(void){ static int unixGetTempname(int nBuf, char *zBuf){ const char *zDir; int iLimit = 0; + int rc = SQLITE_OK; /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this @@ -39736,18 +42931,26 @@ static int unixGetTempname(int nBuf, char *zBuf){ zBuf[0] = 0; SimulateIOError( return SQLITE_IOERR ); + wx_sqlite3_mutex_enter(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); zDir = unixTempFileDir(); - if( zDir==0 ) return SQLITE_IOERR_GETTEMPPATH; - do{ - u64 r; - wx_sqlite3_randomness(sizeof(r), &r); - assert( nBuf>2 ); - zBuf[nBuf-2] = 0; - wx_sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c", - zDir, r, 0); - if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR; - }while( osAccess(zBuf,0)==0 ); - return SQLITE_OK; + if( zDir==0 ){ + rc = SQLITE_IOERR_GETTEMPPATH; + }else{ + do{ + u64 r; + wx_sqlite3_randomness(sizeof(r), &r); + assert( nBuf>2 ); + zBuf[nBuf-2] = 0; + wx_sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c", + zDir, r, 0); + if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ){ + rc = SQLITE_ERROR; + break; + } + }while( osAccess(zBuf,0)==0 ); + } + wx_sqlite3_mutex_leave(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + return rc; } #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) @@ -39890,20 +43093,23 @@ static int findCreateFileMode( ** ** where NN is a decimal number. The NN naming schemes are ** used by the test_multiplex.c module. + ** + ** In normal operation, the journal file name will always contain + ** a '-' character. However in 8+3 filename mode, or if a corrupt + ** rollback journal specifies a super-journal with a goofy name, then + ** the '-' might be missing or the '-' might be the first character in + ** the filename. In that case, just return SQLITE_OK with *pMode==0. */ nDb = wx_sqlite3Strlen30(zPath) - 1; - while( zPath[nDb]!='-' ){ - /* In normal operation, the journal file name will always contain - ** a '-' character. However in 8+3 filename mode, or if a corrupt - ** rollback journal specifies a super-journal with a goofy name, then - ** the '-' might be missing. */ - if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK; + while( nDb>0 && zPath[nDb]!='.' ){ + if( zPath[nDb]=='-' ){ + memcpy(zDb, zPath, nDb); + zDb[nDb] = '\0'; + rc = getFileMode(zDb, pMode, pUid, pGid); + break; + } nDb--; } - memcpy(zDb, zPath, nDb); - zDb[nDb] = '\0'; - - rc = getFileMode(zDb, pMode, pUid, pGid); }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ *pMode = 0600; }else if( flags & SQLITE_OPEN_URI ){ @@ -40021,6 +43227,11 @@ static int unixOpen( } memset(p, 0, sizeof(unixFile)); +#ifdef SQLITE_ASSERT_NO_FILES + /* Applications that never read or write a persistent disk files */ + assert( zName==0 ); +#endif + if( eType==SQLITE_OPEN_MAIN_DB ){ UnixUnusedFd *pUnused; pUnused = findReusableFd(zName, flags); @@ -40288,86 +43499,97 @@ static int unixAccess( } /* -** If the last component of the pathname in z[0]..z[j-1] is something -** other than ".." then back it out and return true. If the last -** component is empty or if it is ".." then return false. +** A pathname under construction */ -static int unixBackupDir(const char *z, int *pJ){ - int j = *pJ; - int i; - if( j<=0 ) return 0; - for(i=j-1; i>0 && z[i-1]!='/'; i--){} - if( i==0 ) return 0; - if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; - *pJ = i-1; - return 1; -} +typedef struct DbPath DbPath; +struct DbPath { + int rc; /* Non-zero following any error */ + int nSymlink; /* Number of symlinks resolved */ + char *zOut; /* Write the pathname here */ + int nOut; /* Bytes of space available to zOut[] */ + int nUsed; /* Bytes of zOut[] currently being used */ +}; + +/* Forward reference */ +static void appendAllPathElements(DbPath*,const char*); /* -** Convert a relative pathname into a full pathname. Also -** simplify the pathname as follows: -** -** Remove all instances of /./ -** Remove all isntances of /X/../ for any X +** Append a single path element to the DbPath under construction */ -static int mkFullPathname( - const char *zPath, /* Input path */ - char *zOut, /* Output buffer */ - int nOut /* Allocated size of buffer zOut */ +static void appendOnePathElement( + DbPath *pPath, /* Path under construction, to which to append zName */ + const char *zName, /* Name to append to pPath. Not zero-terminated */ + int nName /* Number of significant bytes in zName */ ){ - int nPath = wx_sqlite3Strlen30(zPath); - int iOff = 0; - int i, j; - if( zPath[0]!='/' ){ - if( osGetcwd(zOut, nOut-2)==0 ){ - return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); + assert( nName>0 ); + assert( zName!=0 ); + if( zName[0]=='.' ){ + if( nName==1 ) return; + if( zName[1]=='.' && nName==2 ){ + if( pPath->nUsed>1 ){ + assert( pPath->zOut[0]=='/' ); + while( pPath->zOut[--pPath->nUsed]!='/' ){} + } + return; } - iOff = wx_sqlite3Strlen30(zOut); - zOut[iOff++] = '/'; - } - if( (iOff+nPath+1)>nOut ){ - /* SQLite assumes that xFullPathname() nul-terminates the output buffer - ** even if it returns an error. */ - zOut[iOff] = '\0'; - return SQLITE_CANTOPEN_BKPT; } - wx_sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); - - /* Remove duplicate '/' characters. Except, two // at the beginning - ** of a pathname is allowed since this is important on windows. */ - for(i=j=1; zOut[i]; i++){ - zOut[j++] = zOut[i]; - while( zOut[i]=='/' && zOut[i+1]=='/' ) i++; + if( pPath->nUsed + nName + 2 >= pPath->nOut ){ + pPath->rc = SQLITE_ERROR; + return; } - zOut[j] = 0; - - assert( zOut[0]=='/' ); - for(i=j=0; zOut[i]; i++){ - if( zOut[i]=='/' ){ - /* Skip over internal "/." directory components */ - if( zOut[i+1]=='.' && zOut[i+2]=='/' ){ - i += 1; - continue; + pPath->zOut[pPath->nUsed++] = '/'; + memcpy(&pPath->zOut[pPath->nUsed], zName, nName); + pPath->nUsed += nName; +#if defined(HAVE_READLINK) && defined(HAVE_LSTAT) + if( pPath->rc==SQLITE_OK ){ + const char *zIn; + struct stat buf; + pPath->zOut[pPath->nUsed] = 0; + zIn = pPath->zOut; + if( osLstat(zIn, &buf)!=0 ){ + if( errno!=ENOENT ){ + pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn); } - - /* If this is a "/.." directory component then back out the - ** previous term of the directory if it is something other than "..". - */ - if( zOut[i+1]=='.' - && zOut[i+2]=='.' - && zOut[i+3]=='/' - && unixBackupDir(zOut, &j) - ){ - i += 2; - continue; + }else if( S_ISLNK(buf.st_mode) ){ + ssize_t got; + char zLnk[SQLITE_MAX_PATHLEN+2]; + if( pPath->nSymlink++ > SQLITE_MAX_SYMLINK ){ + pPath->rc = SQLITE_CANTOPEN_BKPT; + return; + } + got = osReadlink(zIn, zLnk, sizeof(zLnk)-2); + if( got<=0 || got>=(ssize_t)sizeof(zLnk)-2 ){ + pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn); + return; + } + zLnk[got] = 0; + if( zLnk[0]=='/' ){ + pPath->nUsed = 0; + }else{ + pPath->nUsed -= nName + 1; } + appendAllPathElements(pPath, zLnk); } - if( ALWAYS(j>=0) ) zOut[j] = zOut[i]; - j++; } - if( NEVER(j==0) ) zOut[j++] = '/'; - zOut[j] = 0; - return SQLITE_OK; +#endif +} + +/* +** Append all path elements in zPath to the DbPath under construction. +*/ +static void appendAllPathElements( + DbPath *pPath, /* Path under construction, to which to append zName */ + const char *zPath /* Path to append to pPath. Is zero-terminated */ +){ + int i = 0; + int j = 0; + do{ + while( zPath[i] && zPath[i]!='/' ){ i++; } + if( i>j ){ + appendOnePathElement(pPath, &zPath[j], i-j); + } + j = i+1; + }while( zPath[i++] ); } /* @@ -40385,86 +43607,27 @@ static int unixFullPathname( int nOut, /* Size of output buffer in bytes */ char *zOut /* Output buffer */ ){ -#if !defined(HAVE_READLINK) || !defined(HAVE_LSTAT) - return mkFullPathname(zPath, zOut, nOut); -#else - int rc = SQLITE_OK; - int nByte; - int nLink = 0; /* Number of symbolic links followed so far */ - const char *zIn = zPath; /* Input path for each iteration of loop */ - char *zDel = 0; - - assert( pVfs->mxPathname==MAX_PATHNAME ); + DbPath path; UNUSED_PARAMETER(pVfs); - - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. This function could fail if, for example, the - ** current working directory has been unlinked. - */ - SimulateIOError( return SQLITE_ERROR ); - - do { - - /* Call stat() on path zIn. Set bLink to true if the path is a symbolic - ** link, or false otherwise. */ - int bLink = 0; - struct stat buf; - if( osLstat(zIn, &buf)!=0 ){ - if( errno!=ENOENT ){ - rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn); - } - }else{ - bLink = S_ISLNK(buf.st_mode); - } - - if( bLink ){ - nLink++; - if( zDel==0 ){ - zDel = wx_sqlite3_malloc(nOut); - if( zDel==0 ) rc = SQLITE_NOMEM_BKPT; - }else if( nLink>=SQLITE_MAX_SYMLINKS ){ - rc = SQLITE_CANTOPEN_BKPT; - } - - if( rc==SQLITE_OK ){ - nByte = osReadlink(zIn, zDel, nOut-1); - if( nByte<0 ){ - rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn); - }else{ - if( zDel[0]!='/' ){ - int n; - for(n = wx_sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--); - if( nByte+n+1>nOut ){ - rc = SQLITE_CANTOPEN_BKPT; - }else{ - memmove(&zDel[n], zDel, nByte+1); - memcpy(zDel, zIn, n); - nByte += n; - } - } - zDel[nByte] = '\0'; - } - } - - zIn = zDel; - } - - assert( rc!=SQLITE_OK || zIn!=zOut || zIn[0]=='/' ); - if( rc==SQLITE_OK && zIn!=zOut ){ - rc = mkFullPathname(zIn, zOut, nOut); + path.rc = 0; + path.nUsed = 0; + path.nSymlink = 0; + path.nOut = nOut; + path.zOut = zOut; + if( zPath[0]!='/' ){ + char zPwd[SQLITE_MAX_PATHLEN+2]; + if( osGetcwd(zPwd, sizeof(zPwd)-2)==0 ){ + return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); } - if( bLink==0 ) break; - zIn = zOut; - }while( rc==SQLITE_OK ); - - wx_sqlite3_free(zDel); - if( rc==SQLITE_OK && nLink ) rc = SQLITE_OK_SYMLINK; - return rc; -#endif /* HAVE_READLINK && HAVE_LSTAT */ + appendAllPathElements(&path, zPwd); + } + appendAllPathElements(&path, zPath); + zOut[path.nUsed] = 0; + if( path.rc || path.nUsed<2 ) return SQLITE_CANTOPEN_BKPT; + if( path.nSymlink ) return SQLITE_OK_SYMLINK; + return SQLITE_OK; } - #ifndef SQLITE_OMIT_LOAD_EXTENSION /* ** Interfaces for opening a shared library, finding entry points @@ -40578,7 +43741,7 @@ static int unixRandomness(wx_sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ ** than the argument. */ static int unixSleep(wx_sqlite3_vfs *NotUsed, int microseconds){ -#if OS_VXWORKS +#if OS_VXWORKS || _POSIX_C_SOURCE >= 199309L struct timespec sp; sp.tv_sec = microseconds / 1000000; @@ -41960,9 +45123,39 @@ SQLITE_API int wx_sqlite3_os_init(void){ /* Register all VFSes defined in the aVfs[] array */ for(i=0; i<(sizeof(aVfs)/sizeof(wx_sqlite3_vfs)); i++){ +#ifdef SQLITE_DEFAULT_UNIX_VFS + wx_sqlite3_vfs_register(&aVfs[i], + 0==strcmp(aVfs[i].zName,SQLITE_DEFAULT_UNIX_VFS)); +#else wx_sqlite3_vfs_register(&aVfs[i], i==0); +#endif } +#ifdef SQLITE_OS_KV_OPTIONAL + wx_sqlite3KvvfsInit(); +#endif unixBigLock = wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); + +#ifndef SQLITE_OMIT_WAL + /* Validate lock assumptions */ + assert( SQLITE_SHM_NLOCK==8 ); /* Number of available locks */ + assert( UNIX_SHM_BASE==120 ); /* Start of locking area */ + /* Locks: + ** WRITE UNIX_SHM_BASE 120 + ** CKPT UNIX_SHM_BASE+1 121 + ** RECOVER UNIX_SHM_BASE+2 122 + ** READ-0 UNIX_SHM_BASE+3 123 + ** READ-1 UNIX_SHM_BASE+4 124 + ** READ-2 UNIX_SHM_BASE+5 125 + ** READ-3 UNIX_SHM_BASE+6 126 + ** READ-4 UNIX_SHM_BASE+7 127 + ** DMS UNIX_SHM_BASE+8 128 + */ + assert( UNIX_SHM_DMS==128 ); /* Byte offset of the deadman-switch */ +#endif + + /* Initialize temp file dir array. */ + unixTempFileInit(); + return SQLITE_OK; } @@ -42002,205 +45195,7 @@ SQLITE_API int wx_sqlite3_os_end(void){ /* ** Include code that is common to all os_*.c files */ -/************** Include os_common.h in the middle of os_win.c ****************/ -/************** Begin file os_common.h ***************************************/ -/* -** 2004 May 22 -** -** 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 macros and a little bit of code that is common to -** all of the platform-specific files (os_*.c) and is #included into those -** files. -** -** This file should be #included by the os_*.c files only. It is not a -** general purpose header file. -*/ -#ifndef _OS_COMMON_H_ -#define _OS_COMMON_H_ - -/* -** At least two bugs have slipped in because we changed the MEMORY_DEBUG -** macro to SQLITE_DEBUG and some older makefiles have not yet made the -** switch. The following code should catch this problem at compile-time. -*/ -#ifdef MEMORY_DEBUG -# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." -#endif - -/* -** Macros for performance tracing. Normally turned off. Only works -** on i486 hardware. -*/ -#ifdef SQLITE_PERFORMANCE_TRACE - -/* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of os_common.h ****************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** 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 inline asm code for retrieving "high-performance" -** counters for x86 and x86_64 class CPUs. -*/ -#ifndef SQLITE_HWTIME_H -#define SQLITE_HWTIME_H - -/* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if !defined(__STRICT_ANSI__) && \ - (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) - - #if defined(__GNUC__) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } - - #elif defined(_MSC_VER) - - __declspec(naked) __inline sqlite_uint64 __cdecl wx_sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } - } - - #endif - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; - } - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; - } - -#else - - /* - ** asm() is needed for hardware timing support. Without asm(), - ** disable the wx_sqlite3Hwtime() routine. - ** - ** wx_sqlite3Hwtime() is only used for some obscure debugging - ** and analysis configurations, not in any deliverable, so this - ** should not be a great loss. - */ -SQLITE_PRIVATE sqlite_uint64 wx_sqlite3Hwtime(void){ return ((sqlite_uint64)0); } - -#endif - -#endif /* !defined(SQLITE_HWTIME_H) */ - -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in os_common.h ******************/ - -static sqlite_uint64 g_start; -static sqlite_uint64 g_elapsed; -#define TIMER_START g_start=wx_sqlite3Hwtime() -#define TIMER_END g_elapsed=wx_sqlite3Hwtime()-g_start -#define TIMER_ELAPSED g_elapsed -#else -#define TIMER_START -#define TIMER_END -#define TIMER_ELAPSED ((sqlite_uint64)0) -#endif - -/* -** If we compile with the SQLITE_TEST macro set, then the following block -** of code will give us the ability to simulate a disk I/O error. This -** is used for testing the I/O recovery logic. -*/ -#if defined(SQLITE_TEST) -SQLITE_API extern int wx_sqlite3_io_error_hit; -SQLITE_API extern int wx_sqlite3_io_error_hardhit; -SQLITE_API extern int wx_sqlite3_io_error_pending; -SQLITE_API extern int wx_sqlite3_io_error_persist; -SQLITE_API extern int wx_sqlite3_io_error_benign; -SQLITE_API extern int wx_sqlite3_diskfull_pending; -SQLITE_API extern int wx_sqlite3_diskfull; -#define SimulateIOErrorBenign(X) wx_sqlite3_io_error_benign=(X) -#define SimulateIOError(CODE) \ - if( (wx_sqlite3_io_error_persist && wx_sqlite3_io_error_hit) \ - || wx_sqlite3_io_error_pending-- == 1 ) \ - { local_ioerr(); CODE; } -static void local_ioerr(){ - IOTRACE(("IOERR\n")); - wx_sqlite3_io_error_hit++; - if( !wx_sqlite3_io_error_benign ) wx_sqlite3_io_error_hardhit++; -} -#define SimulateDiskfullError(CODE) \ - if( wx_sqlite3_diskfull_pending ){ \ - if( wx_sqlite3_diskfull_pending == 1 ){ \ - local_ioerr(); \ - wx_sqlite3_diskfull = 1; \ - wx_sqlite3_io_error_hit = 1; \ - CODE; \ - }else{ \ - wx_sqlite3_diskfull_pending--; \ - } \ - } -#else -#define SimulateIOErrorBenign(X) -#define SimulateIOError(A) -#define SimulateDiskfullError(A) -#endif /* defined(SQLITE_TEST) */ - -/* -** When testing, keep a count of the number of open files. -*/ -#if defined(SQLITE_TEST) -SQLITE_API extern int wx_sqlite3_open_file_count; -#define OpenCounter(X) wx_sqlite3_open_file_count+=(X) -#else -#define OpenCounter(X) -#endif /* defined(SQLITE_TEST) */ - -#endif /* !defined(_OS_COMMON_H_) */ - -/************** End of os_common.h *******************************************/ -/************** Continuing where we left off in os_win.c *********************/ +/* #include "os_common.h" */ /* ** Include the header file for the Windows VFS. @@ -44100,10 +47095,12 @@ SQLITE_API int wx_sqlite3_win32_set_directory8( const char *zValue /* New value for directory being set or reset */ ){ char **ppDirectory = 0; + int rc; #ifndef SQLITE_OMIT_AUTOINIT - int rc = wx_sqlite3_initialize(); + rc = wx_sqlite3_initialize(); if( rc ) return rc; #endif + wx_sqlite3_mutex_enter(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){ ppDirectory = &wx_sqlite3_data_directory; }else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){ @@ -44118,14 +47115,19 @@ SQLITE_API int wx_sqlite3_win32_set_directory8( if( zValue && zValue[0] ){ zCopy = wx_sqlite3_mprintf("%s", zValue); if ( zCopy==0 ){ - return SQLITE_NOMEM_BKPT; + rc = SQLITE_NOMEM_BKPT; + goto set_directory8_done; } } wx_sqlite3_free(*ppDirectory); *ppDirectory = zCopy; - return SQLITE_OK; + rc = SQLITE_OK; + }else{ + rc = SQLITE_ERROR; } - return SQLITE_ERROR; +set_directory8_done: + wx_sqlite3_mutex_leave(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + return rc; } /* @@ -46252,10 +49254,14 @@ static int winShmLock( winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */ winShm *p = pDbFd->pShm; /* The shared memory being locked */ winShm *pX; /* For looping over all siblings */ - winShmNode *pShmNode = p->pShmNode; + winShmNode *pShmNode; int rc = SQLITE_OK; /* Result code */ u16 mask; /* Mask of locks to take or release */ + if( p==0 ) return SQLITE_IOERR_SHMLOCK; + pShmNode = p->pShmNode; + if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; + assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); assert( n>=1 ); assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) @@ -46895,6 +49901,19 @@ static int winMakeEndInDirSep(int nBuf, char *zBuf){ return 0; } +/* +** If wx_sqlite3_temp_directory is defined, take the mutex and return true. +** +** If wx_sqlite3_temp_directory is NULL (undefined), omit the mutex and +** return false. +*/ +static int winTempDirDefined(void){ + wx_sqlite3_mutex_enter(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + if( wx_sqlite3_temp_directory!=0 ) return 1; + wx_sqlite3_mutex_leave(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + return 0; +} + /* ** Create a temporary file name and store the resulting pointer into pzBuf. ** The pointer returned in pzBuf must be freed via wx_sqlite3_free(). @@ -46931,20 +49950,23 @@ static int winGetTempname(wx_sqlite3_vfs *pVfs, char **pzBuf){ */ nDir = nMax - (nPre + 15); assert( nDir>0 ); - if( wx_sqlite3_temp_directory ){ + if( winTempDirDefined() ){ int nDirLen = wx_sqlite3Strlen30(wx_sqlite3_temp_directory); if( nDirLen>0 ){ if( !winIsDirSep(wx_sqlite3_temp_directory[nDirLen-1]) ){ nDirLen++; } if( nDirLen>nDir ){ + wx_sqlite3_mutex_leave(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); wx_sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0); } wx_sqlite3_snprintf(nMax, zBuf, "%s", wx_sqlite3_temp_directory); } + wx_sqlite3_mutex_leave(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); } + #if defined(__CYGWIN__) else{ static const char *azDirs[] = { @@ -47733,7 +50755,7 @@ static BOOL winIsVerbatimPathname( ** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname ** bytes in size. */ -static int winFullPathname( +static int winFullPathnameNoMutex( wx_sqlite3_vfs *pVfs, /* Pointer to vfs object */ const char *zRelative, /* Possibly relative input path */ int nFull, /* Size of output buffer in bytes */ @@ -47912,6 +50934,20 @@ static int winFullPathname( } #endif } +static int winFullPathname( + wx_sqlite3_vfs *pVfs, /* Pointer to vfs object */ + const char *zRelative, /* Possibly relative input path */ + int nFull, /* Size of output buffer in bytes */ + char *zFull /* Output buffer */ +){ + int rc; + MUTEX_LOGIC( wx_sqlite3_mutex *pMutex; ) + MUTEX_LOGIC( pMutex = wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR); ) + wx_sqlite3_mutex_enter(pMutex); + rc = winFullPathnameNoMutex(pVfs, zRelative, nFull, zFull); + wx_sqlite3_mutex_leave(pMutex); + return rc; +} #ifndef SQLITE_OMIT_LOAD_EXTENSION /* @@ -48356,31 +51392,88 @@ SQLITE_API int wx_sqlite3_os_end(void){ ** wx_sqlite3_deserialize(). */ /* #include "sqliteInt.h" */ -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE /* ** Forward declaration of objects used by this utility */ typedef struct wx_sqlite3_vfs MemVfs; typedef struct MemFile MemFile; +typedef struct MemStore MemStore; /* Access to a lower-level VFS that (might) implement dynamic loading, ** access to randomness, etc. */ #define ORIGVFS(p) ((wx_sqlite3_vfs*)((p)->pAppData)) -/* An open file */ -struct MemFile { - wx_sqlite3_file base; /* IO methods */ +/* Storage for a memdb file. +** +** An memdb object can be shared or separate. Shared memdb objects can be +** used by more than one database connection. Mutexes are used by shared +** memdb objects to coordinate access. Separate memdb objects are only +** connected to a single database connection and do not require additional +** mutexes. +** +** Shared memdb objects have .zFName!=0 and .pMutex!=0. They are created +** using "file:/name?vfs=memdb". The first character of the name must be +** "/" or else the object will be a separate memdb object. All shared +** memdb objects are stored in memdb_g.apMemStore[] in an arbitrary order. +** +** Separate memdb objects are created using a name that does not begin +** with "/" or using wx_sqlite3_deserialize(). +** +** Access rules for shared MemStore objects: +** +** * .zFName is initialized when the object is created and afterwards +** is unchanged until the object is destroyed. So it can be accessed +** at any time as long as we know the object is not being destroyed, +** which means while either the SQLITE_MUTEX_STATIC_VFS1 or +** .pMutex is held or the object is not part of memdb_g.apMemStore[]. +** +** * Can .pMutex can only be changed while holding the +** SQLITE_MUTEX_STATIC_VFS1 mutex or while the object is not part +** of memdb_g.apMemStore[]. +** +** * Other fields can only be changed while holding the .pMutex mutex +** or when the .nRef is less than zero and the object is not part of +** memdb_g.apMemStore[]. +** +** * The .aData pointer has the added requirement that it can can only +** be changed (for resizing) when nMmap is zero. +** +*/ +struct MemStore { wx_sqlite3_int64 sz; /* Size of the file */ wx_sqlite3_int64 szAlloc; /* Space allocated to aData */ wx_sqlite3_int64 szMax; /* Maximum allowed size of the file */ unsigned char *aData; /* content of the file */ + wx_sqlite3_mutex *pMutex; /* Used by shared stores only */ int nMmap; /* Number of memory mapped pages */ unsigned mFlags; /* Flags */ + int nRdLock; /* Number of readers */ + int nWrLock; /* Number of writers. (Always 0 or 1) */ + int nRef; /* Number of users of this MemStore */ + char *zFName; /* The filename for shared stores */ +}; + +/* An open file */ +struct MemFile { + wx_sqlite3_file base; /* IO methods */ + MemStore *pStore; /* The storage */ int eLock; /* Most recent lock against this file */ }; +/* +** File-scope variables for holding the memdb files that are accessible +** to multiple database connections in separate threads. +** +** Must hold SQLITE_MUTEX_STATIC_VFS1 to access any part of this object. +*/ +static struct MemFS { + int nMemStore; /* Number of shared MemStore objects */ + MemStore **apMemStore; /* Array of all shared MemStore objects */ +} memdb_g; + /* ** Methods for MemFile */ @@ -48391,6 +51484,7 @@ static int memdbTruncate(wx_sqlite3_file*, wx_sqlite3_int64 size); static int memdbSync(wx_sqlite3_file*, int flags); static int memdbFileSize(wx_sqlite3_file*, wx_sqlite3_int64 *pSize); static int memdbLock(wx_sqlite3_file*, int); +static int memdbUnlock(wx_sqlite3_file*, int); /* static int memdbCheckReservedLock(wx_sqlite3_file*, int *pResOut);// not used */ static int memdbFileControl(wx_sqlite3_file*, int op, void *pArg); /* static int memdbSectorSize(wx_sqlite3_file*); // not used */ @@ -48434,7 +51528,10 @@ static wx_sqlite3_vfs memdb_vfs = { memdbSleep, /* xSleep */ 0, /* memdbCurrentTime, */ /* xCurrentTime */ memdbGetLastError, /* xGetLastError */ - memdbCurrentTimeInt64 /* xCurrentTimeInt64 */ + memdbCurrentTimeInt64, /* xCurrentTimeInt64 */ + 0, /* xSetSystemCall */ + 0, /* xGetSystemCall */ + 0, /* xNextSystemCall */ }; static const wx_sqlite3_io_methods memdb_io_methods = { @@ -48446,7 +51543,7 @@ static const wx_sqlite3_io_methods memdb_io_methods = { memdbSync, /* xSync */ memdbFileSize, /* xFileSize */ memdbLock, /* xLock */ - memdbLock, /* xUnlock - same as xLock in this case */ + memdbUnlock, /* xUnlock */ 0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */ memdbFileControl, /* xFileControl */ 0, /* memdbSectorSize,*/ /* xSectorSize */ @@ -48459,19 +51556,67 @@ static const wx_sqlite3_io_methods memdb_io_methods = { memdbUnfetch /* xUnfetch */ }; +/* +** Enter/leave the mutex on a MemStore +*/ +#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0 +static void memdbEnter(MemStore *p){ + UNUSED_PARAMETER(p); +} +static void memdbLeave(MemStore *p){ + UNUSED_PARAMETER(p); +} +#else +static void memdbEnter(MemStore *p){ + wx_sqlite3_mutex_enter(p->pMutex); +} +static void memdbLeave(MemStore *p){ + wx_sqlite3_mutex_leave(p->pMutex); +} +#endif + /* ** Close an memdb-file. -** -** The pData pointer is owned by the application, so there is nothing -** to free. Unless the SQLITE_DESERIALIZE_FREEONCLOSE flag is set, -** in which case we own the pData pointer and need to free it. +** Free the underlying MemStore object when its refcount drops to zero +** or less. */ static int memdbClose(wx_sqlite3_file *pFile){ - MemFile *p = (MemFile *)pFile; - if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ){ - wx_sqlite3_free(p->aData); + MemStore *p = ((MemFile*)pFile)->pStore; + if( p->zFName ){ + int i; +#ifndef SQLITE_MUTEX_OMIT + wx_sqlite3_mutex *pVfsMutex = wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); +#endif + wx_sqlite3_mutex_enter(pVfsMutex); + for(i=0; ALWAYS(inRef==1 ){ + memdb_g.apMemStore[i] = memdb_g.apMemStore[--memdb_g.nMemStore]; + if( memdb_g.nMemStore==0 ){ + wx_sqlite3_free(memdb_g.apMemStore); + memdb_g.apMemStore = 0; + } + } + break; + } + } + wx_sqlite3_mutex_leave(pVfsMutex); + }else{ + memdbEnter(p); + } + p->nRef--; + if( p->nRef<=0 ){ + if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ){ + wx_sqlite3_free(p->aData); + } + memdbLeave(p); + wx_sqlite3_mutex_free(p->pMutex); + wx_sqlite3_free(p); + }else{ + memdbLeave(p); } return SQLITE_OK; } @@ -48485,22 +51630,25 @@ static int memdbRead( int iAmt, sqlite_int64 iOfst ){ - MemFile *p = (MemFile *)pFile; + MemStore *p = ((MemFile*)pFile)->pStore; + memdbEnter(p); if( iOfst+iAmt>p->sz ){ memset(zBuf, 0, iAmt); if( iOfstsz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst); + memdbLeave(p); return SQLITE_IOERR_SHORT_READ; } memcpy(zBuf, p->aData+iOfst, iAmt); + memdbLeave(p); return SQLITE_OK; } /* ** Try to enlarge the memory allocation to hold at least sz bytes */ -static int memdbEnlarge(MemFile *p, wx_sqlite3_int64 newSz){ +static int memdbEnlarge(MemStore *p, wx_sqlite3_int64 newSz){ unsigned char *pNew; - if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || p->nMmap>0 ){ + if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || NEVER(p->nMmap>0) ){ return SQLITE_FULL; } if( newSz>p->szMax ){ @@ -48509,7 +51657,7 @@ static int memdbEnlarge(MemFile *p, wx_sqlite3_int64 newSz){ newSz *= 2; if( newSz>p->szMax ) newSz = p->szMax; pNew = wx_sqlite3Realloc(p->aData, newSz); - if( pNew==0 ) return SQLITE_NOMEM; + if( pNew==0 ) return SQLITE_IOERR_NOMEM; p->aData = pNew; p->szAlloc = newSz; return SQLITE_OK; @@ -48524,19 +51672,27 @@ static int memdbWrite( int iAmt, sqlite_int64 iOfst ){ - MemFile *p = (MemFile *)pFile; - if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ) return SQLITE_READONLY; + MemStore *p = ((MemFile*)pFile)->pStore; + memdbEnter(p); + if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ){ + /* Can't happen: memdbLock() will return SQLITE_READONLY before + ** reaching this point */ + memdbLeave(p); + return SQLITE_IOERR_WRITE; + } if( iOfst+iAmt>p->sz ){ int rc; if( iOfst+iAmt>p->szAlloc && (rc = memdbEnlarge(p, iOfst+iAmt))!=SQLITE_OK ){ + memdbLeave(p); return rc; } if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz); p->sz = iOfst+iAmt; } memcpy(p->aData+iOfst, z, iAmt); + memdbLeave(p); return SQLITE_OK; } @@ -48548,16 +51704,25 @@ static int memdbWrite( ** the size of a file, never to increase the size. */ static int memdbTruncate(wx_sqlite3_file *pFile, sqlite_int64 size){ - MemFile *p = (MemFile *)pFile; - if( NEVER(size>p->sz) ) return SQLITE_FULL; - p->sz = size; - return SQLITE_OK; + MemStore *p = ((MemFile*)pFile)->pStore; + int rc = SQLITE_OK; + memdbEnter(p); + if( size>p->sz ){ + /* This can only happen with a corrupt wal mode db */ + rc = SQLITE_CORRUPT; + }else{ + p->sz = size; + } + memdbLeave(p); + return rc; } /* ** Sync an memdb-file. */ static int memdbSync(wx_sqlite3_file *pFile, int flags){ + UNUSED_PARAMETER(pFile); + UNUSED_PARAMETER(flags); return SQLITE_OK; } @@ -48565,8 +51730,10 @@ static int memdbSync(wx_sqlite3_file *pFile, int flags){ ** Return the current file-size of an memdb-file. */ static int memdbFileSize(wx_sqlite3_file *pFile, sqlite_int64 *pSize){ - MemFile *p = (MemFile *)pFile; + MemStore *p = ((MemFile*)pFile)->pStore; + memdbEnter(p); *pSize = p->sz; + memdbLeave(p); return SQLITE_OK; } @@ -48574,19 +51741,90 @@ static int memdbFileSize(wx_sqlite3_file *pFile, sqlite_int64 *pSize){ ** Lock an memdb-file. */ static int memdbLock(wx_sqlite3_file *pFile, int eLock){ - MemFile *p = (MemFile *)pFile; - if( eLock>SQLITE_LOCK_SHARED - && (p->mFlags & SQLITE_DESERIALIZE_READONLY)!=0 - ){ - return SQLITE_READONLY; + MemFile *pThis = (MemFile*)pFile; + MemStore *p = pThis->pStore; + int rc = SQLITE_OK; + if( eLock<=pThis->eLock ) return SQLITE_OK; + memdbEnter(p); + + assert( p->nWrLock==0 || p->nWrLock==1 ); + assert( pThis->eLock<=SQLITE_LOCK_SHARED || p->nWrLock==1 ); + assert( pThis->eLock==SQLITE_LOCK_NONE || p->nRdLock>=1 ); + + if( eLock>SQLITE_LOCK_SHARED && (p->mFlags & SQLITE_DESERIALIZE_READONLY) ){ + rc = SQLITE_READONLY; + }else{ + switch( eLock ){ + case SQLITE_LOCK_SHARED: { + assert( pThis->eLock==SQLITE_LOCK_NONE ); + if( p->nWrLock>0 ){ + rc = SQLITE_BUSY; + }else{ + p->nRdLock++; + } + break; + }; + + case SQLITE_LOCK_RESERVED: + case SQLITE_LOCK_PENDING: { + assert( pThis->eLock>=SQLITE_LOCK_SHARED ); + if( ALWAYS(pThis->eLock==SQLITE_LOCK_SHARED) ){ + if( p->nWrLock>0 ){ + rc = SQLITE_BUSY; + }else{ + p->nWrLock = 1; + } + } + break; + } + + default: { + assert( eLock==SQLITE_LOCK_EXCLUSIVE ); + assert( pThis->eLock>=SQLITE_LOCK_SHARED ); + if( p->nRdLock>1 ){ + rc = SQLITE_BUSY; + }else if( pThis->eLock==SQLITE_LOCK_SHARED ){ + p->nWrLock = 1; + } + break; + } + } + } + if( rc==SQLITE_OK ) pThis->eLock = eLock; + memdbLeave(p); + return rc; +} + +/* +** Unlock an memdb-file. +*/ +static int memdbUnlock(wx_sqlite3_file *pFile, int eLock){ + MemFile *pThis = (MemFile*)pFile; + MemStore *p = pThis->pStore; + if( eLock>=pThis->eLock ) return SQLITE_OK; + memdbEnter(p); + + assert( eLock==SQLITE_LOCK_SHARED || eLock==SQLITE_LOCK_NONE ); + if( eLock==SQLITE_LOCK_SHARED ){ + if( ALWAYS(pThis->eLock>SQLITE_LOCK_SHARED) ){ + p->nWrLock--; + } + }else{ + if( pThis->eLock>SQLITE_LOCK_SHARED ){ + p->nWrLock--; + } + p->nRdLock--; } - p->eLock = eLock; + + pThis->eLock = eLock; + memdbLeave(p); return SQLITE_OK; } -#if 0 /* Never used because memdbAccess() always returns false */ +#if 0 /* -** Check if another file-handle holds a RESERVED lock on an memdb-file. +** This interface is only used for crash recovery, which does not +** occur on an in-memory database. */ static int memdbCheckReservedLock(wx_sqlite3_file *pFile, int *pResOut){ *pResOut = 0; @@ -48594,12 +51832,14 @@ static int memdbCheckReservedLock(wx_sqlite3_file *pFile, int *pResOut){ } #endif + /* ** File control method. For custom operations on an memdb-file. */ static int memdbFileControl(wx_sqlite3_file *pFile, int op, void *pArg){ - MemFile *p = (MemFile *)pFile; + MemStore *p = ((MemFile*)pFile)->pStore; int rc = SQLITE_NOTFOUND; + memdbEnter(p); if( op==SQLITE_FCNTL_VFSNAME ){ *(char**)pArg = wx_sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz); rc = SQLITE_OK; @@ -48617,6 +51857,7 @@ static int memdbFileControl(wx_sqlite3_file *pFile, int op, void *pArg){ *(wx_sqlite3_int64*)pArg = iLimit; rc = SQLITE_OK; } + memdbLeave(p); return rc; } @@ -48633,6 +51874,7 @@ static int memdbSectorSize(wx_sqlite3_file *pFile){ ** Return the device characteristic flags supported by an memdb-file. */ static int memdbDeviceCharacteristics(wx_sqlite3_file *pFile){ + UNUSED_PARAMETER(pFile); return SQLITE_IOCAP_ATOMIC | SQLITE_IOCAP_POWERSAFE_OVERWRITE | SQLITE_IOCAP_SAFE_APPEND | @@ -48646,20 +51888,26 @@ static int memdbFetch( int iAmt, void **pp ){ - MemFile *p = (MemFile *)pFile; - if( iOfst+iAmt>p->sz ){ + MemStore *p = ((MemFile*)pFile)->pStore; + memdbEnter(p); + if( iOfst+iAmt>p->sz || (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)!=0 ){ *pp = 0; }else{ p->nMmap++; *pp = (void*)(p->aData + iOfst); } + memdbLeave(p); return SQLITE_OK; } /* Release a memory-mapped page */ static int memdbUnfetch(wx_sqlite3_file *pFile, wx_sqlite3_int64 iOfst, void *pPage){ - MemFile *p = (MemFile *)pFile; + MemStore *p = ((MemFile*)pFile)->pStore; + UNUSED_PARAMETER(iOfst); + UNUSED_PARAMETER(pPage); + memdbEnter(p); p->nMmap--; + memdbLeave(p); return SQLITE_OK; } @@ -48669,20 +51917,79 @@ static int memdbUnfetch(wx_sqlite3_file *pFile, wx_sqlite3_int64 iOfst, void *pP static int memdbOpen( wx_sqlite3_vfs *pVfs, const char *zName, - wx_sqlite3_file *pFile, + wx_sqlite3_file *pFd, int flags, int *pOutFlags ){ - MemFile *p = (MemFile*)pFile; - if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ - return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags); + MemFile *pFile = (MemFile*)pFd; + MemStore *p = 0; + int szName; + UNUSED_PARAMETER(pVfs); + + memset(pFile, 0, sizeof(*pFile)); + szName = wx_sqlite3Strlen30(zName); + if( szName>1 && (zName[0]=='/' || zName[0]=='\\') ){ + int i; +#ifndef SQLITE_MUTEX_OMIT + wx_sqlite3_mutex *pVfsMutex = wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); +#endif + wx_sqlite3_mutex_enter(pVfsMutex); + for(i=0; izFName,zName)==0 ){ + p = memdb_g.apMemStore[i]; + break; + } + } + if( p==0 ){ + MemStore **apNew; + p = wx_sqlite3Malloc( sizeof(*p) + szName + 3 ); + if( p==0 ){ + wx_sqlite3_mutex_leave(pVfsMutex); + return SQLITE_NOMEM; + } + apNew = wx_sqlite3Realloc(memdb_g.apMemStore, + sizeof(apNew[0])*(memdb_g.nMemStore+1) ); + if( apNew==0 ){ + wx_sqlite3_free(p); + wx_sqlite3_mutex_leave(pVfsMutex); + return SQLITE_NOMEM; + } + apNew[memdb_g.nMemStore++] = p; + memdb_g.apMemStore = apNew; + memset(p, 0, sizeof(*p)); + p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE|SQLITE_DESERIALIZE_FREEONCLOSE; + p->szMax = wx_sqlite3GlobalConfig.mxMemdbSize; + p->zFName = (char*)&p[1]; + memcpy(p->zFName, zName, szName+1); + p->pMutex = wx_sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( p->pMutex==0 ){ + memdb_g.nMemStore--; + wx_sqlite3_free(p); + wx_sqlite3_mutex_leave(pVfsMutex); + return SQLITE_NOMEM; + } + p->nRef = 1; + memdbEnter(p); + }else{ + memdbEnter(p); + p->nRef++; + } + wx_sqlite3_mutex_leave(pVfsMutex); + }else{ + p = wx_sqlite3Malloc( sizeof(*p) ); + if( p==0 ){ + return SQLITE_NOMEM; + } + memset(p, 0, sizeof(*p)); + p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; + p->szMax = wx_sqlite3GlobalConfig.mxMemdbSize; } - memset(p, 0, sizeof(*p)); - p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; - assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ - *pOutFlags = flags | SQLITE_OPEN_MEMORY; - pFile->pMethods = &memdb_io_methods; - p->szMax = wx_sqlite3GlobalConfig.mxMemdbSize; + pFile->pStore = p; + if( pOutFlags!=0 ){ + *pOutFlags = flags | SQLITE_OPEN_MEMORY; + } + pFd->pMethods = &memdb_io_methods; + memdbLeave(p); return SQLITE_OK; } @@ -48710,6 +52017,9 @@ static int memdbAccess( int flags, int *pResOut ){ + UNUSED_PARAMETER(pVfs); + UNUSED_PARAMETER(zPath); + UNUSED_PARAMETER(flags); *pResOut = 0; return SQLITE_OK; } @@ -48725,6 +52035,7 @@ static int memdbFullPathname( int nOut, char *zOut ){ + UNUSED_PARAMETER(pVfs); wx_sqlite3_snprintf(nOut, zOut, "%s", zPath); return SQLITE_OK; } @@ -48797,9 +52108,14 @@ static int memdbCurrentTimeInt64(wx_sqlite3_vfs *pVfs, wx_sqlite3_int64 *p){ */ static MemFile *memdbFromDbSchema(wx_sqlite3 *db, const char *zSchema){ MemFile *p = 0; + MemStore *pStore; int rc = wx_sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p); if( rc ) return 0; if( p->base.pMethods!=&memdb_io_methods ) return 0; + pStore = p->pStore; + memdbEnter(pStore); + if( pStore->zFName!=0 ) p = 0; + memdbLeave(pStore); return p; } @@ -48835,12 +52151,14 @@ SQLITE_API unsigned char *wx_sqlite3_serialize( if( piSize ) *piSize = -1; if( iDb<0 ) return 0; if( p ){ - if( piSize ) *piSize = p->sz; + MemStore *pStore = p->pStore; + assert( pStore->pMutex==0 ); + if( piSize ) *piSize = pStore->sz; if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ - pOut = p->aData; + pOut = pStore->aData; }else{ - pOut = wx_sqlite3_malloc64( p->sz ); - if( pOut ) memcpy(pOut, p->aData, p->sz); + pOut = wx_sqlite3_malloc64( pStore->sz ); + if( pOut ) memcpy(pOut, pStore->aData, pStore->sz); } return pOut; } @@ -48910,7 +52228,8 @@ SQLITE_API int wx_sqlite3_deserialize( wx_sqlite3_mutex_enter(db->mutex); if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; iDb = wx_sqlite3FindDbName(db, zSchema); - if( iDb<0 ){ + testcase( iDb==1 ); + if( iDb<2 && iDb!=0 ){ rc = SQLITE_ERROR; goto end_deserialize; } @@ -48934,15 +52253,16 @@ SQLITE_API int wx_sqlite3_deserialize( if( p==0 ){ rc = SQLITE_ERROR; }else{ - p->aData = pData; + MemStore *pStore = p->pStore; + pStore->aData = pData; pData = 0; - p->sz = szDb; - p->szAlloc = szBuf; - p->szMax = szBuf; - if( p->szMaxszMax = wx_sqlite3GlobalConfig.mxMemdbSize; + pStore->sz = szDb; + pStore->szAlloc = szBuf; + pStore->szMax = szBuf; + if( pStore->szMaxszMax = wx_sqlite3GlobalConfig.mxMemdbSize; } - p->mFlags = mFlags; + pStore->mFlags = mFlags; rc = SQLITE_OK; } @@ -48955,13 +52275,22 @@ end_deserialize: return rc; } +/* +** Return true if the VFS is the memvfs. +*/ +SQLITE_PRIVATE int wx_sqlite3IsMemdb(const wx_sqlite3_vfs *pVfs){ + return pVfs==&memdb_vfs; +} + /* ** This routine is called when the extension is loaded. ** Register the new VFS. */ SQLITE_PRIVATE int wx_sqlite3MemdbInit(void){ wx_sqlite3_vfs *pLower = wx_sqlite3_vfs_find(0); - int sz = pLower->szOsFile; + unsigned int sz; + if( NEVER(pLower==0) ) return SQLITE_ERROR; + sz = pLower->szOsFile; memdb_vfs.pAppData = pLower; /* The following conditional can only be true when compiled for ** Windows x86 and SQLITE_MAX_MMAP_SIZE=0. We always leave @@ -48971,7 +52300,7 @@ SQLITE_PRIVATE int wx_sqlite3MemdbInit(void){ memdb_vfs.szOsFile = sz; return wx_sqlite3_vfs_register(&memdb_vfs, 0); } -#endif /* SQLITE_ENABLE_DESERIALIZE */ +#endif /* SQLITE_OMIT_DESERIALIZE */ /************** End of memdb.c ***********************************************/ /************** Begin file bitvec.c ******************************************/ @@ -49330,7 +52659,7 @@ SQLITE_PRIVATE int wx_sqlite3BitvecBuiltinTest(int sz, int *aOp){ wx_sqlite3BitvecClear(0, 1, pTmpSpace); /* Run the program */ - pc = 0; + pc = i = 0; while( (op = aOp[pc])!=0 ){ switch( op ){ case 1: @@ -49432,7 +52761,7 @@ bitvec_end: struct PCache { PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ PgHdr *pSynced; /* Last synced page in dirty page list */ - int nRefSum; /* Sum of ref counts over all pages */ + i64 nRefSum; /* Sum of ref counts over all pages */ int szCache; /* Configured cache size */ int szSpill; /* Size before spilling occurs */ int szPage; /* Size of every page in this cache */ @@ -49457,12 +52786,20 @@ struct PCache { int wx_sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */ int wx_sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */ # define pcacheTrace(X) if(wx_sqlite3PcacheTrace){wx_sqlite3DebugPrintf X;} - void pcacheDump(PCache *pCache){ - int N; - int i, j; - wx_sqlite3_pcache_page *pLower; + static void pcachePageTrace(int i, wx_sqlite3_pcache_page *pLower){ PgHdr *pPg; unsigned char *a; + int j; + pPg = (PgHdr*)pLower->pExtra; + printf("%3lld: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); + a = (unsigned char *)pLower->pBuf; + for(j=0; j<12; j++) printf("%02x", a[j]); + printf(" ptr %p\n", pPg); + } + static void pcacheDump(PCache *pCache){ + int N; + int i; + wx_sqlite3_pcache_page *pLower; if( wx_sqlite3PcacheTrace<2 ) return; if( pCache->pCache==0 ) return; @@ -49471,21 +52808,32 @@ struct PCache { for(i=1; i<=N; i++){ pLower = wx_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0); if( pLower==0 ) continue; - pPg = (PgHdr*)pLower->pExtra; - printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); - a = (unsigned char *)pLower->pBuf; - for(j=0; j<12; j++) printf("%02x", a[j]); - printf("\n"); - if( pPg->pPage==0 ){ + pcachePageTrace(i, pLower); + if( ((PgHdr*)pLower)->pPage==0 ){ wx_sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0); } } } - #else +#else # define pcacheTrace(X) +# define pcachePageTrace(PGNO, X) # define pcacheDump(X) #endif +/* +** Return 1 if pPg is on the dirty list for pCache. Return 0 if not. +** This routine runs inside of assert() statements only. +*/ +#ifdef SQLITE_DEBUG +static int pageOnDirtyList(PCache *pCache, PgHdr *pPg){ + PgHdr *p; + for(p=pCache->pDirty; p; p=p->pDirtyNext){ + if( p==pPg ) return 1; + } + return 0; +} +#endif + /* ** Check invariants on a PgHdr entry. Return true if everything is OK. ** Return false if any invariant is violated. @@ -49504,8 +52852,13 @@ SQLITE_PRIVATE int wx_sqlite3PcachePageSanity(PgHdr *pPg){ assert( pCache!=0 ); /* Every page has an associated PCache */ if( pPg->flags & PGHDR_CLEAN ){ assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */ - assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */ - assert( pCache->pDirtyTail!=pPg ); + assert( !pageOnDirtyList(pCache, pPg) );/* CLEAN pages not on dirty list */ + }else{ + assert( (pPg->flags & PGHDR_DIRTY)!=0 );/* If not CLEAN must be DIRTY */ + assert( pPg->pDirtyNext==0 || pPg->pDirtyNext->pDirtyPrev==pPg ); + assert( pPg->pDirtyPrev==0 || pPg->pDirtyPrev->pDirtyNext==pPg ); + assert( pPg->pDirtyPrev!=0 || pCache->pDirty==pPg ); + assert( pageOnDirtyList(pCache, pPg) ); } /* WRITEABLE pages must also be DIRTY */ if( pPg->flags & PGHDR_WRITEABLE ){ @@ -49634,11 +52987,14 @@ static int numberOfCachePages(PCache *p){ ** suggested cache size is set to N. */ return p->szCache; }else{ + i64 n; /* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the ** number of cache pages is adjusted to be a number of pages that would ** use approximately abs(N*1024) bytes of memory based on the current ** page size. */ - return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); + n = ((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); + if( n>1000000000 ) n = 1000000000; + return (int)n; } } @@ -49776,8 +53132,9 @@ SQLITE_PRIVATE wx_sqlite3_pcache_page *wx_sqlite3PcacheFetch( assert( createFlag==0 || pCache->eCreate==eCreate ); assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) ); pRes = wx_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); - pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno, + pcacheTrace(("%p.FETCH %d%s (result: %p) ",pCache,pgno, createFlag?" create":"",pRes)); + pcachePageTrace(pgno, pRes); return pRes; } @@ -49905,6 +53262,7 @@ SQLITE_PRIVATE void SQLITE_NOINLINE wx_sqlite3PcacheRelease(PgHdr *p){ pcacheUnpin(p); }else{ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); + assert( wx_sqlite3PcachePageSanity(p) ); } } } @@ -49948,6 +53306,7 @@ SQLITE_PRIVATE void wx_sqlite3PcacheMakeDirty(PgHdr *p){ pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno)); assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY ); pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD); + assert( wx_sqlite3PcachePageSanity(p) ); } assert( wx_sqlite3PcachePageSanity(p) ); } @@ -50010,14 +53369,24 @@ SQLITE_PRIVATE void wx_sqlite3PcacheClearSyncFlags(PCache *pCache){ */ SQLITE_PRIVATE void wx_sqlite3PcacheMove(PgHdr *p, Pgno newPgno){ PCache *pCache = p->pCache; + wx_sqlite3_pcache_page *pOther; assert( p->nRef>0 ); assert( newPgno>0 ); assert( wx_sqlite3PcachePageSanity(p) ); pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno)); + pOther = wx_sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, newPgno, 0); + if( pOther ){ + PgHdr *pXPage = (PgHdr*)pOther->pExtra; + assert( pXPage->nRef==0 ); + pXPage->nRef++; + pCache->nRefSum++; + wx_sqlite3PcacheDrop(pXPage); + } wx_sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); p->pgno = newPgno; if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); + assert( wx_sqlite3PcachePageSanity(p) ); } } @@ -50166,14 +53535,14 @@ SQLITE_PRIVATE PgHdr *wx_sqlite3PcacheDirtyList(PCache *pCache){ ** This is not the total number of pages referenced, but the sum of the ** reference count for all pages. */ -SQLITE_PRIVATE int wx_sqlite3PcacheRefCount(PCache *pCache){ +SQLITE_PRIVATE i64 wx_sqlite3PcacheRefCount(PCache *pCache){ return pCache->nRefSum; } /* ** Return the number of references to the page supplied as an argument. */ -SQLITE_PRIVATE int wx_sqlite3PcachePageRefcount(PgHdr *p){ +SQLITE_PRIVATE i64 wx_sqlite3PcachePageRefcount(PgHdr *p){ return p->nRef; } @@ -50315,12 +53684,13 @@ SQLITE_PRIVATE void wx_sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(P ** size can vary according to architecture, compile-time options, and ** SQLite library version number. ** -** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained -** using a separate memory allocation from the database page content. This -** seeks to overcome the "clownshoe" problem (also called "internal -** fragmentation" in academic literature) of allocating a few bytes more -** than a power of two with the memory allocator rounding up to the next -** power of two, and leaving the rounded-up space unused. +** Historical note: It used to be that if the SQLITE_PCACHE_SEPARATE_HEADER +** was defined, then the page content would be held in a separate memory +** allocation from the PgHdr1. This was intended to avoid clownshoe memory +** allocations. However, the btree layer needs a small (16-byte) overrun +** area after the page content buffer. The header serves as that overrun +** area. Therefore SQLITE_PCACHE_SEPARATE_HEADER was discontinued to avoid +** any possibility of a memory error. ** ** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates ** with this module. Information is passed back and forth as PgHdr1 pointers. @@ -50365,30 +53735,40 @@ typedef struct PGroup PGroup; /* ** Each cache entry is represented by an instance of the following -** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of -** PgHdr1.pCache->szPage bytes is allocated directly before this structure -** in memory. +** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated +** directly before this structure and is used to cache the page content. ** -** Note: Variables isBulkLocal and isAnchor were once type "u8". That works, +** When reading a corrupt database file, it is possible that SQLite might +** read a few bytes (no more than 16 bytes) past the end of the page buffer. +** It will only read past the end of the page buffer, never write. This +** object is positioned immediately after the page buffer to serve as an +** overrun area, so that overreads are harmless. +** +** Variables isBulkLocal and isAnchor were once type "u8". That works, ** but causes a 2-byte gap in the structure for most architectures (since ** pointers must be either 4 or 8-byte aligned). As this structure is located ** in memory directly after the associated page data, if the database is ** corrupt, code at the b-tree layer may overread the page buffer and ** read part of this structure before the corruption is detected. This ** can cause a valgrind error if the unitialized gap is accessed. Using u16 -** ensures there is no such gap, and therefore no bytes of unitialized memory -** in the structure. +** ensures there is no such gap, and therefore no bytes of uninitialized +** memory in the structure. +** +** The pLruNext and pLruPrev pointers form a double-linked circular list +** of all pages that are unpinned. The PGroup.lru element (which should be +** the only element on the list with PgHdr1.isAnchor set to 1) forms the +** beginning and the end of the list. */ struct PgHdr1 { - wx_sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ - unsigned int iKey; /* Key value (page number) */ - u16 isBulkLocal; /* This page from bulk local storage */ - u16 isAnchor; /* This is the PGroup.lru element */ - PgHdr1 *pNext; /* Next in hash table chain */ - PCache1 *pCache; /* Cache that currently owns this page */ - PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ - PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ - /* NB: pLruPrev is only valid if pLruNext!=0 */ + wx_sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ + unsigned int iKey; /* Key value (page number) */ + u16 isBulkLocal; /* This page from bulk local storage */ + u16 isAnchor; /* This is the PGroup.lru element */ + PgHdr1 *pNext; /* Next in hash table chain */ + PCache1 *pCache; /* Cache that currently owns this page */ + PgHdr1 *pLruNext; /* Next in circular LRU list of unpinned pages */ + PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ + /* NB: pLruPrev is only valid if pLruNext!=0 */ }; /* @@ -50714,25 +54094,13 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){ pcache1LeaveMutex(pCache->pGroup); #endif if( benignMalloc ){ wx_sqlite3BeginBenignMalloc(); } -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - pPg = pcache1Alloc(pCache->szPage); - p = wx_sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); - if( !pPg || !p ){ - pcache1Free(pPg); - wx_sqlite3_free(p); - pPg = 0; - } -#else pPg = pcache1Alloc(pCache->szAlloc); -#endif if( benignMalloc ){ wx_sqlite3EndBenignMalloc(); } #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT pcache1EnterMutex(pCache->pGroup); #endif if( pPg==0 ) return 0; -#ifndef SQLITE_PCACHE_SEPARATE_HEADER p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; -#endif p->page.pBuf = pPg; p->page.pExtra = &p[1]; p->isBulkLocal = 0; @@ -50756,9 +54124,6 @@ static void pcache1FreePage(PgHdr1 *p){ pCache->pFree = p; }else{ pcache1Free(p->page.pBuf); -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - wx_sqlite3_free(p); -#endif } (*pCache->pnPurgeable)--; } @@ -51093,12 +54458,18 @@ static wx_sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable) */ static void pcache1Cachesize(wx_sqlite3_pcache *p, int nMax){ PCache1 *pCache = (PCache1 *)p; + u32 n; + assert( nMax>=0 ); if( pCache->bPurgeable ){ PGroup *pGroup = pCache->pGroup; pcache1EnterMutex(pGroup); - pGroup->nMaxPage += (nMax - pCache->nMax); + n = (u32)nMax; + if( n > 0x7fff0000 - pGroup->nMaxPage + pCache->nMax ){ + n = 0x7fff0000 - pGroup->nMaxPage + pCache->nMax; + } + pGroup->nMaxPage += (n - pCache->nMax); pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; - pCache->nMax = nMax; + pCache->nMax = n; pCache->n90pct = pCache->nMax*9/10; pcache1EnforceMaxPage(pCache); pcache1LeaveMutex(pGroup); @@ -51114,7 +54485,7 @@ static void pcache1Shrink(wx_sqlite3_pcache *p){ PCache1 *pCache = (PCache1*)p; if( pCache->bPurgeable ){ PGroup *pGroup = pCache->pGroup; - int savedMaxPage; + unsigned int savedMaxPage; pcache1EnterMutex(pGroup); savedMaxPage = pGroup->nMaxPage; pGroup->nMaxPage = 0; @@ -51393,23 +54764,26 @@ static void pcache1Rekey( PCache1 *pCache = (PCache1 *)p; PgHdr1 *pPage = (PgHdr1 *)pPg; PgHdr1 **pp; - unsigned int h; + unsigned int hOld, hNew; assert( pPage->iKey==iOld ); assert( pPage->pCache==pCache ); + assert( iOld!=iNew ); /* The page number really is changing */ pcache1EnterMutex(pCache->pGroup); - h = iOld%pCache->nHash; - pp = &pCache->apHash[h]; + assert( pcache1FetchNoMutex(p, iOld, 0)==pPage ); /* pPg really is iOld */ + hOld = iOld%pCache->nHash; + pp = &pCache->apHash[hOld]; while( (*pp)!=pPage ){ pp = &(*pp)->pNext; } *pp = pPage->pNext; - h = iNew%pCache->nHash; + assert( pcache1FetchNoMutex(p, iNew, 0)==0 ); /* iNew not in cache */ + hNew = iNew%pCache->nHash; pPage->iKey = iNew; - pPage->pNext = pCache->apHash[h]; - pCache->apHash[h] = pPage; + pPage->pNext = pCache->apHash[hNew]; + pCache->apHash[hNew] = pPage; if( iNew>pCache->iMaxKey ){ pCache->iMaxKey = iNew; } @@ -51516,9 +54890,6 @@ SQLITE_PRIVATE int wx_sqlite3PcacheReleaseMemory(int nReq){ && p->isAnchor==0 ){ nFree += pcache1MemSize(p->page.pBuf); -#ifdef SQLITE_PCACHE_SEPARATE_HEADER - nFree += wx_sqlite3MemSize(p); -#endif assert( PAGE_IS_UNPINNED(p) ); pcache1PinPage(p); pcache1RemoveFromHash(p, 1); @@ -52851,6 +56222,7 @@ struct Pager { u8 noLock; /* Do not lock (except in WAL mode) */ u8 readOnly; /* True for a read-only database */ u8 memDb; /* True to inhibit all file I/O */ + u8 memVfs; /* VFS-implemented memory database */ /************************************************************************** ** The following block contains those class members that change during @@ -52900,8 +56272,9 @@ struct Pager { i16 nReserve; /* Number of unused bytes at end of each page */ u32 vfsFlags; /* Flags for wx_sqlite3_vfs.xOpen() */ u32 sectorSize; /* Assumed sector size during rollback */ - int pageSize; /* Number of bytes in a page */ Pgno mxPgno; /* Maximum allowed size of the database */ + Pgno lckPgno; /* Page number for the locking page */ + i64 pageSize; /* Number of bytes in a page */ i64 journalSizeLimit; /* Size limit for persistent journal files */ char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ @@ -53887,7 +57260,7 @@ static int readJournalHdr( ** journal file descriptor is advanced to the next sector boundary before ** anything is written. The format is: ** -** + 4 bytes: PAGER_MJ_PGNO. +** + 4 bytes: PAGER_SJ_PGNO. ** + N bytes: super-journal filename in utf-8. ** + 4 bytes: N (length of super-journal name in bytes, no nul-terminator). ** + 4 bytes: super-journal name checksum. @@ -53935,7 +57308,7 @@ static int writeSuperJournal(Pager *pPager, const char *zSuper){ /* Write the super-journal data to the end of the journal file. If ** an error occurs, return the error code to the caller. */ - if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_MJ_PGNO(pPager)))) + if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_SJ_PGNO(pPager)))) || (0 != (rc = wx_sqlite3OsWrite(pPager->jfd, zSuper, nSuper, iHdrOff+4))) || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper, nSuper))) || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper+4, cksum))) @@ -54445,7 +57818,7 @@ static u32 pager_cksum(Pager *pPager, const u8 *aData){ ** corrupted, SQLITE_DONE is returned. Data is considered corrupted in ** two circumstances: ** -** * If the record page-number is illegal (0 or PAGER_MJ_PGNO), or +** * If the record page-number is illegal (0 or PAGER_SJ_PGNO), or ** * If the record is being rolled back from the main journal file ** and the checksum field does not match the record content. ** @@ -54505,7 +57878,7 @@ static int pager_playback_one_page( ** it could cause invalid data to be written into the journal. We need to ** detect this invalid data (with high probability) and ignore it. */ - if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ + if( pgno==0 || pgno==PAGER_SJ_PGNO(pPager) ){ assert( !isSavepnt ); return SQLITE_DONE; } @@ -54842,6 +58215,7 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ memset(pTmp, 0, szPage); testcase( (newSize-szPage) == currentSize ); testcase( (newSize-szPage) > currentSize ); + wx_sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &newSize); rc = wx_sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); } if( rc==SQLITE_OK ){ @@ -55064,6 +58438,9 @@ static int pager_playback(Pager *pPager, int isHot){ goto end_playback; } pPager->dbSize = mxPg; + if( pPager->mxPgnomxPgno = mxPg; + } } /* Copy original pages out of the journal and back into the @@ -55150,7 +58527,7 @@ end_playback: ** see if it is possible to delete the super-journal. */ assert( zSuper==&pPager->pTmpSpace[4] ); - memset(&zSuper[-4], 0, 4); + memset(pPager->pTmpSpace, 0, 4); rc = pager_delsuper(pPager, zSuper); testcase( rc!=SQLITE_OK ); } @@ -55245,6 +58622,7 @@ static int readDbPage(PgHdr *pPg){ */ static void pager_write_changecounter(PgHdr *pPg){ u32 change_counter; + if( NEVER(pPg==0) ) return; /* Increment the value just read and write it back to byte 24. */ change_counter = wx_sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1; @@ -55770,7 +59148,6 @@ SQLITE_PRIVATE void wx_sqlite3PagerShrink(Pager *pPager){ ** Numeric values associated with these states are OFF==1, NORMAL=2, ** and FULL=3. */ -#ifndef SQLITE_OMIT_PAGER_PRAGMAS SQLITE_PRIVATE void wx_sqlite3PagerSetFlags( Pager *pPager, /* The pager to set safety level for */ unsigned pgFlags /* Various flags */ @@ -55805,7 +59182,6 @@ SQLITE_PRIVATE void wx_sqlite3PagerSetFlags( pPager->doNotSpill |= SPILLFLAG_OFF; } } -#endif /* ** The following global variable is incremented whenever the library @@ -55959,6 +59335,7 @@ SQLITE_PRIVATE int wx_sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int pPager->pTmpSpace = pNew; pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize); pPager->pageSize = pageSize; + pPager->lckPgno = (Pgno)(PENDING_BYTE/pageSize) + 1; }else{ wx_sqlite3PageFree(pNew); } @@ -56119,8 +59496,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ ** current database image, in pages, OR ** ** b) if the page content were written at this time, it would not -** be necessary to write the current content out to the sub-journal -** (as determined by function subjRequiresPage()). +** be necessary to write the current content out to the sub-journal. ** ** If the condition asserted by this function were not true, and the ** dirty page were to be discarded from the cache via the pagerStress() @@ -56135,8 +59511,16 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ */ #if defined(SQLITE_DEBUG) static void assertTruncateConstraintCb(PgHdr *pPg){ + Pager *pPager = pPg->pPager; assert( pPg->flags&PGHDR_DIRTY ); - assert( !subjRequiresPage(pPg) || pPg->pgno<=pPg->pPager->dbSize ); + if( pPg->pgno>pPager->dbSize ){ /* if (a) is false */ + Pgno pgno = pPg->pgno; + int i; + for(i=0; ipPager->nSavepoint; i++){ + PagerSavepoint *p = &pPager->aSavepoint[i]; + assert( p->nOrigpInSavepoint,pgno) ); + } + } } static void assertTruncateConstraint(Pager *pPager){ wx_sqlite3PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb); @@ -56157,7 +59541,7 @@ static void assertTruncateConstraint(Pager *pPager){ ** then continue writing to the database. */ SQLITE_PRIVATE void wx_sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ - assert( pPager->dbSize>=nPage ); + assert( pPager->dbSize>=nPage || CORRUPT_DB ); assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); pPager->dbSize = nPage; @@ -56885,7 +60269,7 @@ SQLITE_PRIVATE int wx_sqlite3PagerOpen( int rc = SQLITE_OK; /* Return code */ int tempFile = 0; /* True for temp files (incl. in-memory files) */ int memDb = 0; /* True if this is an in-memory file */ -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE int memJM = 0; /* Memory journal mode */ #else # define memJM 0 @@ -56899,7 +60283,6 @@ SQLITE_PRIVATE int wx_sqlite3PagerOpen( u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ const char *zUri = 0; /* URI args to copy */ int nUriByte = 1; /* Number of bytes of URI args at *zUri */ - int nUri = 0; /* Number of URI parameters */ /* Figure out how much space is required for each journal file-handle ** (there are two of them, the main journal and the sub-journal). */ @@ -56947,7 +60330,6 @@ SQLITE_PRIVATE int wx_sqlite3PagerOpen( while( *z ){ z += strlen(z)+1; z += strlen(z)+1; - nUri++; } nUriByte = (int)(&z[1] - zUri); assert( nUriByte>=1 ); @@ -57078,6 +60460,7 @@ SQLITE_PRIVATE int wx_sqlite3PagerOpen( pPager->zWal = 0; } #endif + (void)pPtr; /* Suppress warning about unused pPtr value */ if( nPathname ) wx_sqlite3DbFree(0, zPathname); pPager->pVfs = pVfs; @@ -57089,8 +60472,8 @@ SQLITE_PRIVATE int wx_sqlite3PagerOpen( int fout = 0; /* VFS flags returned by xOpen() */ rc = wx_sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); assert( !memDb ); -#ifdef SQLITE_ENABLE_DESERIALIZE - memJM = (fout&SQLITE_OPEN_MEMORY)!=0; +#ifndef SQLITE_OMIT_DESERIALIZE + pPager->memVfs = memJM = (fout&SQLITE_OPEN_MEMORY)!=0; #endif readOnly = (fout&SQLITE_OPEN_READONLY)!=0; @@ -57202,18 +60585,7 @@ act_like_temp_file: pPager->memDb = (u8)memDb; pPager->readOnly = (u8)readOnly; assert( useJournal || pPager->tempFile ); - pPager->noSync = pPager->tempFile; - if( pPager->noSync ){ - assert( pPager->fullSync==0 ); - assert( pPager->extraSync==0 ); - assert( pPager->syncFlags==0 ); - assert( pPager->walSyncFlags==0 ); - }else{ - pPager->fullSync = 1; - pPager->extraSync = 0; - pPager->syncFlags = SQLITE_SYNC_NORMAL; - pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2); - } + wx_sqlite3PagerSetFlags(pPager, (SQLITE_DEFAULT_SYNCHRONOUS+1)|PAGER_CACHESPILL); /* pPager->pFirst = 0; */ /* pPager->pFirstSynced = 0; */ /* pPager->pLast = 0; */ @@ -57475,7 +60847,7 @@ SQLITE_PRIVATE int wx_sqlite3PagerSharedLock(Pager *pPager){ ** may mean that the pager was in the error-state when this ** function was called and the journal file does not exist. */ - if( !isOpen(pPager->jfd) ){ + if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ wx_sqlite3_vfs * const pVfs = pPager->pVfs; int bExists; /* True if journal file exists */ rc = wx_sqlite3OsAccess( @@ -57720,7 +61092,7 @@ static int getPageNormal( if( pPg->pPager && !noContent ){ /* In this case the pcache already contains an initialized copy of ** the page. Return without further ado. */ - assert( pgno!=PAGER_MJ_PGNO(pPager) ); + assert( pgno!=PAGER_SJ_PGNO(pPager) ); pPager->aStat[PAGER_STAT_HIT]++; return SQLITE_OK; @@ -57731,7 +61103,7 @@ static int getPageNormal( ** (*) obsolete. Was: maximum page number is 2^31 ** (2) Never try to fetch the locking page */ - if( pgno==PAGER_MJ_PGNO(pPager) ){ + if( pgno==PAGER_SJ_PGNO(pPager) ){ rc = SQLITE_CORRUPT_BKPT; goto pager_acquire_err; } @@ -57877,6 +61249,7 @@ SQLITE_PRIVATE int wx_sqlite3PagerGet( DbPage **ppPage, /* Write a pointer to the page here */ int flags /* PAGER_GET_XXX flags */ ){ + /* printf("PAGE %u\n", pgno); fflush(stdout); */ return pPager->xGet(pPager, pgno, ppPage, flags); } @@ -57990,6 +61363,7 @@ static int pager_open_journal(Pager *pPager){ if( pPager->tempFile ){ flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL); + flags |= SQLITE_OPEN_EXCLUSIVE; nSpill = wx_sqlite3Config.nStmtSpill; }else{ flags |= SQLITE_OPEN_MAIN_JOURNAL; @@ -58025,6 +61399,7 @@ static int pager_open_journal(Pager *pPager){ if( rc!=SQLITE_OK ){ wx_sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; + pPager->journalOff = 0; }else{ assert( pPager->eState==PAGER_WRITER_LOCKED ); pPager->eState = PAGER_WRITER_CACHEMOD; @@ -58057,7 +61432,7 @@ SQLITE_PRIVATE int wx_sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMem assert( pPager->eState>=PAGER_READER && pPager->eStatesubjInMemory = (u8)subjInMemory; - if( ALWAYS(pPager->eState==PAGER_READER) ){ + if( pPager->eState==PAGER_READER ){ assert( pPager->pInJournal==0 ); if( pagerUseWal(pPager) ){ @@ -58129,7 +61504,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ /* We should never write to the journal file the page that ** contains the database locks. The following assert verifies ** that we do not. */ - assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); + assert( pPg->pgno!=PAGER_SJ_PGNO(pPager) ); assert( pPager->journalHdr<=pPager->journalOff ); pData2 = pPg->pData; @@ -58308,7 +61683,7 @@ static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){ Pgno pg = pg1+ii; PgHdr *pPage; if( pg==pPg->pgno || !wx_sqlite3BitvecTest(pPager->pInJournal, pg) ){ - if( pg!=PAGER_MJ_PGNO(pPager) ){ + if( pg!=PAGER_SJ_PGNO(pPager) ){ rc = wx_sqlite3PagerGet(pPager, pg, &pPage, 0); if( rc==SQLITE_OK ){ rc = pager_write(pPage); @@ -58471,7 +61846,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ # define DIRECT_MODE isDirectMode #endif - if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){ + if( !pPager->changeCountDone && pPager->dbSize>0 ){ PgHdr *pPgHdr; /* Reference to page 1 */ assert( !pPager->tempFile && isOpen(pPager->fd) ); @@ -58786,7 +62161,7 @@ SQLITE_PRIVATE int wx_sqlite3PagerCommitPhaseOne( ** last page is never written out to disk, leaving the database file ** undersized. Fix this now if it is the case. */ if( pPager->dbSize>pPager->dbFileSize ){ - Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); + Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_SJ_PGNO(pPager)); assert( pPager->eState==PAGER_WRITER_DBMOD ); rc = pager_truncate(pPager, nNew); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; @@ -58957,8 +62332,8 @@ SQLITE_PRIVATE int wx_sqlite3PagerRefcount(Pager *pPager){ ** used by the pager and its associated cache. */ SQLITE_PRIVATE int wx_sqlite3PagerMemUsed(Pager *pPager){ - int perPageSize = pPager->pageSize + pPager->nExtra + sizeof(PgHdr) - + 5*sizeof(void*); + int perPageSize = pPager->pageSize + pPager->nExtra + + (int)(sizeof(PgHdr) + 5*sizeof(void*)); return perPageSize*wx_sqlite3PcachePagecount(pPager->pPCache) + wx_sqlite3MallocSize(pPager) + pPager->pageSize; @@ -59027,7 +62402,7 @@ SQLITE_PRIVATE void wx_sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset ** Return true if this is an in-memory or temp-file backed pager. */ SQLITE_PRIVATE int wx_sqlite3PagerIsMemdb(Pager *pPager){ - return pPager->tempFile; + return pPager->tempFile || pPager->memVfs; } /* @@ -59152,14 +62527,14 @@ SQLITE_PRIVATE int wx_sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoin } pPager->nSavepoint = nNew; - /* If this is a release of the outermost savepoint, truncate - ** the sub-journal to zero bytes in size. */ + /* Truncate the sub-journal so that it only includes the parts + ** that are still in use. */ if( op==SAVEPOINT_RELEASE ){ PagerSavepoint *pRel = &pPager->aSavepoint[nNew]; if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){ /* Only truncate if it is an in-memory sub-journal. */ if( wx_sqlite3JournalIsInMemory(pPager->sjfd) ){ - i64 sz = (pPager->pageSize+4)*pRel->iSubRec; + i64 sz = (pPager->pageSize+4)*(i64)pRel->iSubRec; rc = wx_sqlite3OsTruncate(pPager->sjfd, sz); assert( rc==SQLITE_OK ); } @@ -59211,7 +62586,11 @@ SQLITE_PRIVATE int wx_sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoin */ SQLITE_PRIVATE const char *wx_sqlite3PagerFilename(const Pager *pPager, int nullIfMemDb){ static const char zFake[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - return (nullIfMemDb && pPager->memDb) ? &zFake[4] : pPager->zFilename; + if( nullIfMemDb && (pPager->memDb || wx_sqlite3IsMemdb(pPager->pVfs)) ){ + return &zFake[4]; + }else{ + return pPager->zFilename; + } } /* @@ -59347,7 +62726,7 @@ SQLITE_PRIVATE int wx_sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno pPgOld = wx_sqlite3PagerLookup(pPager, pgno); assert( !pPgOld || pPgOld->nRef==1 || CORRUPT_DB ); if( pPgOld ){ - if( pPgOld->nRef>1 ){ + if( NEVER(pPgOld->nRef>1) ){ wx_sqlite3PagerUnrefNotNull(pPgOld); return SQLITE_CORRUPT_BKPT; } @@ -59482,12 +62861,12 @@ SQLITE_PRIVATE int wx_sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ u8 eOld = pPager->journalMode; /* Prior journalmode */ /* The eMode parameter is always valid */ - assert( eMode==PAGER_JOURNALMODE_DELETE - || eMode==PAGER_JOURNALMODE_TRUNCATE - || eMode==PAGER_JOURNALMODE_PERSIST - || eMode==PAGER_JOURNALMODE_OFF - || eMode==PAGER_JOURNALMODE_WAL - || eMode==PAGER_JOURNALMODE_MEMORY ); + assert( eMode==PAGER_JOURNALMODE_DELETE /* 0 */ + || eMode==PAGER_JOURNALMODE_PERSIST /* 1 */ + || eMode==PAGER_JOURNALMODE_OFF /* 2 */ + || eMode==PAGER_JOURNALMODE_TRUNCATE /* 3 */ + || eMode==PAGER_JOURNALMODE_MEMORY /* 4 */ + || eMode==PAGER_JOURNALMODE_WAL /* 5 */ ); /* This routine is only called from the OP_JournalMode opcode, and ** the logic there will never allow a temporary file to be changed @@ -59524,7 +62903,6 @@ SQLITE_PRIVATE int wx_sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ assert( isOpen(pPager->fd) || pPager->exclusiveMode ); if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){ - /* In this case we would like to delete the journal file. If it is ** not possible, then that is not a problem. Deleting the journal file ** here is an optimization only. @@ -59636,6 +63014,18 @@ SQLITE_PRIVATE int wx_sqlite3PagerCheckpoint( int *pnCkpt /* OUT: Final number of checkpointed frames */ ){ int rc = SQLITE_OK; + if( pPager->pWal==0 && pPager->journalMode==PAGER_JOURNALMODE_WAL ){ + /* This only happens when a database file is zero bytes in size opened and + ** then "PRAGMA journal_mode=WAL" is run and then wx_sqlite3_wal_checkpoint() + ** is invoked without any intervening transactions. We need to start + ** a transaction to initialize pWal. The PRAGMA table_list statement is + ** used for this since it starts transactions on every database file, + ** including all ATTACHed databases. This seems expensive for a single + ** wx_sqlite3_wal_checkpoint() call, but it happens very rarely. + ** https://sqlite.org/forum/forumpost/fd0f19d229156939 + */ + wx_sqlite3_exec(db, "PRAGMA table_list",0,0,0); + } if( pPager->pWal ){ rc = wx_sqlite3WalCheckpoint(pPager->pWal, db, eMode, (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler), @@ -60093,7 +63483,10 @@ SQLITE_PRIVATE int wx_sqlite3PagerWalFramesize(Pager *pPager){ ** HASHTABLE_NPAGE_ONE frames. The values of HASHTABLE_NPAGE_ONE and ** HASHTABLE_NPAGE are selected so that together the wal-index header and ** first index block are the same size as all other index blocks in the -** wal-index. +** wal-index. The values are: +** +** HASHTABLE_NPAGE 4096 +** HASHTABLE_NPAGE_ONE 4062 ** ** Each index block contains two sections, a page-mapping that contains the ** database page number associated with each wal frame, and a hash-table @@ -60329,6 +63722,70 @@ struct WalCkptInfo { }; #define READMARK_NOT_USED 0xffffffff +/* +** This is a schematic view of the complete 136-byte header of the +** wal-index file (also known as the -shm file): +** +** +-----------------------------+ +** 0: | iVersion | \ +** +-----------------------------+ | +** 4: | (unused padding) | | +** +-----------------------------+ | +** 8: | iChange | | +** +-------+-------+-------------+ | +** 12: | bInit | bBig | szPage | | +** +-------+-------+-------------+ | +** 16: | mxFrame | | First copy of the +** +-----------------------------+ | WalIndexHdr object +** 20: | nPage | | +** +-----------------------------+ | +** 24: | aFrameCksum | | +** | | | +** +-----------------------------+ | +** 32: | aSalt | | +** | | | +** +-----------------------------+ | +** 40: | aCksum | | +** | | / +** +-----------------------------+ +** 48: | iVersion | \ +** +-----------------------------+ | +** 52: | (unused padding) | | +** +-----------------------------+ | +** 56: | iChange | | +** +-------+-------+-------------+ | +** 60: | bInit | bBig | szPage | | +** +-------+-------+-------------+ | Second copy of the +** 64: | mxFrame | | WalIndexHdr +** +-----------------------------+ | +** 68: | nPage | | +** +-----------------------------+ | +** 72: | aFrameCksum | | +** | | | +** +-----------------------------+ | +** 80: | aSalt | | +** | | | +** +-----------------------------+ | +** 88: | aCksum | | +** | | / +** +-----------------------------+ +** 96: | nBackfill | +** +-----------------------------+ +** 100: | 5 read marks | +** | | +** | | +** | | +** | | +** +-------+-------+------+------+ +** 120: | Write | Ckpt | Rcvr | Rd0 | \ +** +-------+-------+------+------+ ) 8 lock bytes +** | Read1 | Read2 | Rd3 | Rd4 | / +** +-------+-------+------+------+ +** 128: | nBackfillAttempted | +** +-----------------------------+ +** 132: | (unused padding) | +** +-----------------------------+ +*/ /* A block of WALINDEX_LOCK_RESERVED bytes beginning at ** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems @@ -60485,9 +63942,13 @@ struct WalIterator { ** so. It is safe to enlarge the wal-index if pWal->writeLock is true ** or pWal->exclusiveMode==WAL_HEAPMEMORY_MODE. ** -** If this call is successful, *ppPage is set to point to the wal-index -** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs, -** then an SQLite error code is returned and *ppPage is set to 0. +** Three possible result scenarios: +** +** (1) rc==SQLITE_OK and *ppPage==Requested-Wal-Index-Page +** (2) rc>=SQLITE_ERROR and *ppPage==NULL +** (3) rc==SQLITE_OK and *ppPage==NULL // only if iPage==0 +** +** Scenario (3) can only occur when pWal->writeLock is false and iPage==0 */ static SQLITE_NOINLINE int walIndexPageRealloc( Wal *pWal, /* The WAL context */ @@ -60520,7 +63981,9 @@ static SQLITE_NOINLINE int walIndexPageRealloc( rc = wx_sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] ); - assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); + assert( pWal->apWiData[iPage]!=0 + || rc!=SQLITE_OK + || (pWal->writeLock==0 && iPage==0) ); testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); if( rc==SQLITE_OK ){ if( iPage>0 && wx_sqlite3FaultSim(600) ) rc = SQLITE_NOMEM; @@ -60859,8 +64322,8 @@ struct WalHashLoc { ** slot in the hash table is set to N, it refers to frame number ** (pLoc->iZero+N) in the log. ** -** Finally, set pLoc->aPgno so that pLoc->aPgno[1] is the page number of the -** first frame indexed by the hash table, frame (pLoc->iZero+1). +** Finally, set pLoc->aPgno so that pLoc->aPgno[0] is the page number of the +** first frame indexed by the hash table, frame (pLoc->iZero). */ static int walHashGet( Wal *pWal, /* WAL handle */ @@ -60872,7 +64335,7 @@ static int walHashGet( rc = walIndexPage(pWal, iHash, &pLoc->aPgno); assert( rc==SQLITE_OK || iHash>0 ); - if( rc==SQLITE_OK ){ + if( pLoc->aPgno ){ pLoc->aHash = (volatile ht_slot *)&pLoc->aPgno[HASHTABLE_NPAGE]; if( iHash==0 ){ pLoc->aPgno = &pLoc->aPgno[WALINDEX_HDR_SIZE/sizeof(u32)]; @@ -60880,7 +64343,8 @@ static int walHashGet( }else{ pLoc->iZero = HASHTABLE_NPAGE_ONE + (iHash-1)*HASHTABLE_NPAGE; } - pLoc->aPgno = &pLoc->aPgno[-1]; + }else if( NEVER(rc==SQLITE_OK) ){ + rc = SQLITE_ERROR; } return rc; } @@ -60931,7 +64395,6 @@ static void walCleanupHash(Wal *pWal){ int iLimit = 0; /* Zero values greater than this */ int nByte; /* Number of bytes to zero in aPgno[] */ int i; /* Used to iterate through aHash[] */ - int rc; /* Return code form walHashGet() */ assert( pWal->writeLock ); testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 ); @@ -60946,8 +64409,8 @@ static void walCleanupHash(Wal *pWal){ */ assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) ); assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] ); - rc = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); - if( NEVER(rc) ) return; /* Defense-in-depth, in case (1) above is wrong */ + i = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); + if( NEVER(i) ) return; /* Defense-in-depth, in case (1) above is wrong */ /* Zero all hash-table entries that correspond to frame numbers greater ** than pWal->hdr.mxFrame. @@ -60963,8 +64426,9 @@ static void walCleanupHash(Wal *pWal){ /* Zero the entries in the aPgno array that correspond to frames with ** frame numbers greater than pWal->hdr.mxFrame. */ - nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit+1]); - memset((void *)&sLoc.aPgno[iLimit+1], 0, nByte); + nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit]); + assert( nByte>=0 ); + memset((void *)&sLoc.aPgno[iLimit], 0, nByte); #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT /* Verify that the every entry in the mapping region is still reachable @@ -60973,11 +64437,11 @@ static void walCleanupHash(Wal *pWal){ if( iLimit ){ int j; /* Loop counter */ int iKey; /* Hash key */ - for(j=1; j<=iLimit; j++){ + for(j=0; j=0 ); + memset((void*)sLoc.aPgno, 0, nByte); } /* If the entry in aPgno[] is already set, then the previous writer @@ -61020,9 +64484,9 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ ** Remove the remnants of that writers uncommitted transaction from ** the hash-table before writing any new entries. */ - if( sLoc.aPgno[idx] ){ + if( sLoc.aPgno[idx-1] ){ walCleanupHash(pWal); - assert( !sLoc.aPgno[idx] ); + assert( !sLoc.aPgno[idx-1] ); } /* Write the aPgno[] array entry and the hash-table slot. */ @@ -61030,7 +64494,7 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ for(iKey=walHash(iPage); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){ if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT; } - sLoc.aPgno[idx] = iPage; + sLoc.aPgno[idx-1] = iPage; AtomicStore(&sLoc.aHash[iKey], (ht_slot)idx); #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT @@ -61051,19 +64515,18 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ */ if( (idx&0x3ff)==0 ){ int i; /* Loop counter */ - for(i=1; i<=idx; i++){ + for(i=0; iapWiData[iPg] = aPrivate; for(iFrame=iFirst; iFrame<=iLast; iFrame++){ @@ -61343,14 +64807,43 @@ SQLITE_PRIVATE int wx_sqlite3WalOpen( assert( zWalName && zWalName[0] ); assert( pDbFd ); + /* Verify the values of various constants. Any changes to the values + ** of these constants would result in an incompatible on-disk format + ** for the -shm file. Any change that causes one of these asserts to + ** fail is a backward compatibility problem, even if the change otherwise + ** works. + ** + ** This table also serves as a helpful cross-reference when trying to + ** interpret hex dumps of the -shm file. + */ + assert( 48 == sizeof(WalIndexHdr) ); + assert( 40 == sizeof(WalCkptInfo) ); + assert( 120 == WALINDEX_LOCK_OFFSET ); + assert( 136 == WALINDEX_HDR_SIZE ); + assert( 4096 == HASHTABLE_NPAGE ); + assert( 4062 == HASHTABLE_NPAGE_ONE ); + assert( 8192 == HASHTABLE_NSLOT ); + assert( 383 == HASHTABLE_HASH_1 ); + assert( 32768 == WALINDEX_PGSZ ); + assert( 8 == SQLITE_SHM_NLOCK ); + assert( 5 == WAL_NREADER ); + assert( 24 == WAL_FRAME_HDRSIZE ); + assert( 32 == WAL_HDRSIZE ); + assert( 120 == WALINDEX_LOCK_OFFSET + WAL_WRITE_LOCK ); + assert( 121 == WALINDEX_LOCK_OFFSET + WAL_CKPT_LOCK ); + assert( 122 == WALINDEX_LOCK_OFFSET + WAL_RECOVER_LOCK ); + assert( 123 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(0) ); + assert( 124 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(1) ); + assert( 125 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(2) ); + assert( 126 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(3) ); + assert( 127 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(4) ); + /* In the amalgamation, the os_unix.c and os_win.c source files come before ** this source file. Verify that the #defines of the locking byte offsets ** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value. ** For that matter, if the lock offset ever changes from its initial design ** value of 120, we need to know that so there is an assert() to check it. */ - assert( 120==WALINDEX_LOCK_OFFSET ); - assert( 136==WALINDEX_HDR_SIZE ); #ifdef WIN_SHM_BASE assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET ); #endif @@ -61652,7 +65145,6 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ int nEntry; /* Number of entries in this segment */ ht_slot *aIndex; /* Sorted index for this segment */ - sLoc.aPgno++; if( (i+1)==nSegment ){ nEntry = (int)(iLast - sLoc.iZero); }else{ @@ -62433,7 +65925,9 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ } /* Allocate a buffer to read frames into */ - szFrame = pWal->hdr.szPage + WAL_FRAME_HDRSIZE; + assert( (pWal->szPage & (pWal->szPage-1))==0 ); + assert( pWal->szPage>=512 && pWal->szPage<=65536 ); + szFrame = pWal->szPage + WAL_FRAME_HDRSIZE; aFrame = (u8 *)wx_sqlite3_malloc64(szFrame); if( aFrame==0 ){ rc = SQLITE_NOMEM_BKPT; @@ -62447,7 +65941,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ ** the caller. */ aSaveCksum[0] = pWal->hdr.aFrameCksum[0]; aSaveCksum[1] = pWal->hdr.aFrameCksum[1]; - for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->hdr.szPage); + for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->szPage); iOffset+szFrame<=szWal; iOffset+=szFrame ){ @@ -62791,7 +66285,8 @@ SQLITE_PRIVATE int wx_sqlite3WalSnapshotRecover(Wal *pWal){ rc = walHashGet(pWal, walFramePage(i), &sLoc); if( rc!=SQLITE_OK ) break; - pgno = sLoc.aPgno[i-sLoc.iZero]; + assert( i - sLoc.iZero - 1 >=0 ); + pgno = sLoc.aPgno[i-sLoc.iZero-1]; iDbOff = (i64)(pgno-1) * szPage; if( iDbOff+szPage<=szDb ){ @@ -63024,7 +66519,7 @@ SQLITE_PRIVATE int wx_sqlite3WalFindFrame( iKey = walHash(pgno); while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ u32 iFrame = iH + sLoc.iZero; - if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH]==pgno ){ + if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){ assert( iFrame>iRead || CORRUPT_DB ); iRead = iFrame; } @@ -63362,7 +66857,7 @@ static int walWriteOneFrame( int rc; /* Result code from subfunctions */ void *pData; /* Data actually written */ u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */ - pData = pPage->pData; + if( (pData = wx_sqlite3mcPagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT; walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame); rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset); if( rc ) return rc; @@ -63545,7 +67040,7 @@ SQLITE_PRIVATE int wx_sqlite3WalFrames( if( pWal->iReCksum==0 || iWriteiReCksum ){ pWal->iReCksum = iWrite; } - pData = p->pData; + if( (pData = wx_sqlite3mcPagerCodec(p))==0 ) return SQLITE_NOMEM; rc = wx_sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOff); if( rc ) return rc; p->flags &= ~PGHDR_WAL_APPEND; @@ -64276,7 +67771,6 @@ typedef struct CellInfo CellInfo; */ struct MemPage { u8 isInit; /* True if previously initialized. MUST BE FIRST! */ - u8 bBusy; /* Prevent endless loops on corrupt database files */ u8 intKey; /* True if table b-trees. False for index b-trees */ u8 intKeyLeaf; /* True if the leaf of an intKey table */ Pgno pgno; /* Page number for this page */ @@ -64298,7 +67792,9 @@ struct MemPage { u8 *apOvfl[4]; /* Pointers to the body of overflow cells */ BtShared *pBt; /* Pointer to BtShared that this page is part of */ u8 *aData; /* Pointer to disk image of the page data */ - u8 *aDataEnd; /* One byte past the end of usable data */ + u8 *aDataEnd; /* One byte past the end of the entire page - not just + ** the usable space, the entire page. Used to prevent + ** corruption-induced buffer overflow. */ u8 *aCellIdx; /* The cell index area */ u8 *aDataOfst; /* Same as aData for leaves. aData+4 for interior */ DbPage *pDbPage; /* Pager page handle */ @@ -64603,7 +68099,7 @@ struct BtCursor { /* ** The database page the PENDING_BYTE occupies. This page is never used. */ -# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt) +#define PENDING_BYTE_PAGE(pBt) ((Pgno)((PENDING_BYTE/((pBt)->pageSize))+1)) /* ** These macros define the location of the pointer-map entry for a @@ -64677,15 +68173,15 @@ struct BtCursor { ** So, this macro is defined instead. */ #ifndef SQLITE_OMIT_AUTOVACUUM -#define ISAUTOVACUUM (pBt->autoVacuum) +#define ISAUTOVACUUM(pBt) (pBt->autoVacuum) #else -#define ISAUTOVACUUM 0 +#define ISAUTOVACUUM(pBt) 0 #endif /* -** This structure is passed around through all the sanity checking routines -** in order to keep track of some global state information. +** This structure is passed around through all the PRAGMA integrity_check +** checking routines in order to keep track of some global state information. ** ** The aRef[] array is allocated so that there is 1 bit for each page in ** the database. As the integrity-check proceeds, for each page used in @@ -64701,7 +68197,8 @@ struct IntegrityCk { Pgno nPage; /* Number of pages in the database */ int mxErr; /* Stop accumulating errors when this reaches zero */ int nErr; /* Number of messages written to zErrMsg so far */ - int bOomFault; /* A memory allocation error has occurred */ + int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */ + u32 nStep; /* Number of steps into the integrity_check process */ const char *zPfx; /* Error message prefix */ Pgno v1; /* Value for first %u substitution in zPfx */ int v2; /* Value for second %d substitution in zPfx */ @@ -64971,6 +68468,7 @@ SQLITE_PRIVATE int wx_sqlite3BtreeHoldsAllMutexes(wx_sqlite3 *db){ SQLITE_PRIVATE int wx_sqlite3SchemaMutexHeld(wx_sqlite3 *db, int iDb, Schema *pSchema){ Btree *p; assert( db!=0 ); + if( db->pVfs==0 && db->nDb==0 ) return 1; if( pSchema ) iDb = wx_sqlite3SchemaToIndex(db, pSchema); assert( iDb>=0 && iDbnDb ); if( !wx_sqlite3_mutex_held(db->mutex) ) return 0; @@ -65244,7 +68742,7 @@ static int hasSharedCacheTableLock( int bSeen = 0; for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ Index *pIdx = (Index *)sqliteHashData(p); - if( pIdx->tnum==(int)iRoot ){ + if( pIdx->tnum==iRoot ){ if( bSeen ){ /* Two or more indexes share the same root page. There must ** be imposter tables. So just return true. The assert is not @@ -65577,7 +69075,7 @@ static void invalidateIncrblobCursors( int isClearTable /* True if all rows are being deleted */ ){ BtCursor *p; - if( pBtree->hasIncrblobCur==0 ) return; + assert( pBtree->hasIncrblobCur ); assert( wx_sqlite3BtreeHoldsMutex(pBtree) ); pBtree->hasIncrblobCur = 0; for(p=pBtree->pBt->pCursor; p; p=p->pNext){ @@ -65837,7 +69335,7 @@ SQLITE_PRIVATE void wx_sqlite3BtreeClearCursor(BtCursor *pCur){ /* ** In this version of BtreeMoveto, pKey is a packed index record ** such as is generated by the OP_MakeRecord opcode. Unpack the -** record and then call BtreeMovetoUnpacked() to do the work. +** record and then call wx_sqlite3BtreeIndexMoveto() to do the work. */ static int btreeMoveto( BtCursor *pCur, /* Cursor open on the btree to be searched */ @@ -65857,15 +69355,13 @@ static int btreeMoveto( wx_sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey); if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){ rc = SQLITE_CORRUPT_BKPT; - goto moveto_done; + }else{ + rc = wx_sqlite3BtreeIndexMoveto(pCur, pIdxKey, pRes); } + wx_sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey); }else{ pIdxKey = 0; - } - rc = wx_sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); -moveto_done: - if( pIdxKey ){ - wx_sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey); + rc = wx_sqlite3BtreeTableMoveto(pCur, nKey, bias, pRes); } return rc; } @@ -66257,18 +69753,32 @@ static void btreeParseCellPtr( ** ** pIter += getVarint(pIter, (u64*)&pInfo->nKey); ** - ** The code is inlined to avoid a function call. + ** The code is inlined and the loop is unrolled for performance. + ** This routine is a high-runner. */ iKey = *pIter; if( iKey>=0x80 ){ - u8 *pEnd = &pIter[7]; - iKey &= 0x7f; - while(1){ - iKey = (iKey<<7) | (*++pIter & 0x7f); - if( (*pIter)<0x80 ) break; - if( pIter>=pEnd ){ - iKey = (iKey<<8) | *++pIter; - break; + u8 x; + iKey = ((iKey&0x7f)<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x =*++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + if( x>=0x80 ){ + iKey = (iKey<<8) | (*++pIter); + } + } + } + } + } } } } @@ -66278,7 +69788,7 @@ static void btreeParseCellPtr( pInfo->nPayload = nPayload; pInfo->pPayload = pIter; testcase( nPayload==pPage->maxLocal ); - testcase( nPayload==pPage->maxLocal+1 ); + testcase( nPayload==(u32)pPage->maxLocal+1 ); if( nPayload<=pPage->maxLocal ){ /* This is the (easy) common case where the entire payload fits ** on the local page. No overflow is required. @@ -66315,7 +69825,7 @@ static void btreeParseCellPtrIndex( pInfo->nPayload = nPayload; pInfo->pPayload = pIter; testcase( nPayload==pPage->maxLocal ); - testcase( nPayload==pPage->maxLocal+1 ); + testcase( nPayload==(u32)pPage->maxLocal+1 ); if( nPayload<=pPage->maxLocal ){ /* This is the (easy) common case where the entire payload fits ** on the local page. No overflow is required. @@ -66345,6 +69855,7 @@ static void btreeParseCell( ** the space used by the cell pointer. ** ** cellSizePtrNoPayload() => table internal nodes +** cellSizePtrTableLeaf() => table leaf nodes ** cellSizePtr() => all index nodes & table leaf nodes */ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ @@ -66370,15 +69881,8 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ }while( *(pIter)>=0x80 && pIterintKey ){ - /* pIter now points at the 64-bit integer key value, a variable length - ** integer. The following block moves pIter to point at the first byte - ** past the end of the key value. */ - pEnd = &pIter[9]; - while( (*pIter++)&0x80 && pItermaxLocal ); - testcase( nSize==pPage->maxLocal+1 ); + testcase( nSize==(u32)pPage->maxLocal+1 ); if( nSize<=pPage->maxLocal ){ nSize += (u32)(pIter - pCell); if( nSize<4 ) nSize = 4; @@ -66386,7 +69890,7 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ int minLocal = pPage->minLocal; nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); testcase( nSize==pPage->maxLocal ); - testcase( nSize==pPage->maxLocal+1 ); + testcase( nSize==(u32)pPage->maxLocal+1 ); if( nSize>pPage->maxLocal ){ nSize = minLocal; } @@ -66416,6 +69920,58 @@ static u16 cellSizePtrNoPayload(MemPage *pPage, u8 *pCell){ assert( debuginfo.nSize==(u16)(pIter - pCell) || CORRUPT_DB ); return (u16)(pIter - pCell); } +static u16 cellSizePtrTableLeaf(MemPage *pPage, u8 *pCell){ + u8 *pIter = pCell; /* For looping over bytes of pCell */ + u8 *pEnd; /* End mark for a varint */ + u32 nSize; /* Size value to return */ + +#ifdef SQLITE_DEBUG + /* The value returned by this function should always be the same as + ** the (CellInfo.nSize) value found by doing a full parse of the + ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of + ** this function verifies that this invariant is not violated. */ + CellInfo debuginfo; + pPage->xParseCell(pPage, pCell, &debuginfo); +#endif + + nSize = *pIter; + if( nSize>=0x80 ){ + pEnd = &pIter[8]; + nSize &= 0x7f; + do{ + nSize = (nSize<<7) | (*++pIter & 0x7f); + }while( *(pIter)>=0x80 && pItermaxLocal ); + testcase( nSize==(u32)pPage->maxLocal+1 ); + if( nSize<=pPage->maxLocal ){ + nSize += (u32)(pIter - pCell); + if( nSize<4 ) nSize = 4; + }else{ + int minLocal = pPage->minLocal; + nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); + testcase( nSize==pPage->maxLocal ); + testcase( nSize==(u32)pPage->maxLocal+1 ); + if( nSize>pPage->maxLocal ){ + nSize = minLocal; + } + nSize += 4 + (u16)(pIter - pCell); + } + assert( nSize==debuginfo.nSize || CORRUPT_DB ); + return (u16)nSize; +} #ifdef SQLITE_DEBUG @@ -66429,7 +69985,7 @@ static u16 cellSize(MemPage *pPage, int iCell){ #ifndef SQLITE_OMIT_AUTOVACUUM /* ** The cell pCell is currently part of page pSrc but will ultimately be part -** of pPage. (pSrc and pPager are often the same.) If pCell contains a +** of pPage. (pSrc and pPage are often the same.) If pCell contains a ** pointer to an overflow page, insert an entry into the pointer-map for ** the overflow page that will be valid after pCell has been moved to pPage. */ @@ -66478,14 +70034,14 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ unsigned char *src; /* Source of content */ int iCellFirst; /* First allowable cell index */ int iCellLast; /* Last possible cell index */ + int iCellStart; /* First cell offset in input */ assert( wx_sqlite3PagerIswriteable(pPage->pDbPage) ); assert( pPage->pBt!=0 ); assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); assert( pPage->nOverflow==0 ); assert( wx_sqlite3_mutex_held(pPage->pBt->mutex) ); - temp = 0; - src = data = pPage->aData; + data = pPage->aData; hdr = pPage->hdrOffset; cellOffset = pPage->cellOffset; nCell = pPage->nCell; @@ -66519,7 +70075,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage); memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz)); sz += sz2; - }else if( NEVER(iFree+sz>usableSize) ){ + }else if( iFree+sz>usableSize ){ return SQLITE_CORRUPT_PAGE(pPage); } @@ -66538,41 +70094,39 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ cbrk = usableSize; iCellLast = usableSize - 4; - for(i=0; iiCellLast ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - assert( pc>=iCellFirst && pc<=iCellLast ); - size = pPage->xCellSize(pPage, &src[pc]); - cbrk -= size; - if( cbrkusableSize ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); - testcase( cbrk+size==usableSize ); - testcase( pc+size==usableSize ); - put2byte(pAddr, cbrk); - if( temp==0 ){ - int x; - if( cbrk==pc ) continue; - temp = wx_sqlite3PagerTempSpace(pPage->pBt->pPager); - x = get2byte(&data[hdr+5]); - memcpy(&temp[x], &data[x], (cbrk+size) - x); - src = temp; + iCellStart = get2byte(&data[hdr+5]); + if( nCell>0 ){ + temp = wx_sqlite3PagerTempSpace(pPage->pBt->pPager); + memcpy(&temp[iCellStart], &data[iCellStart], usableSize - iCellStart); + src = temp; + for(i=0; iiCellLast ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + assert( pc>=iCellStart && pc<=iCellLast ); + size = pPage->xCellSize(pPage, &src[pc]); + cbrk -= size; + if( cbrkusableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + assert( cbrk+size<=usableSize && cbrk>=iCellStart ); + testcase( cbrk+size==usableSize ); + testcase( pc+size==usableSize ); + put2byte(pAddr, cbrk); + memcpy(&data[cbrk], &src[pc], size); } - memcpy(&data[cbrk], &src[pc], size); } data[hdr+7] = 0; - defragment_out: +defragment_out: assert( pPage->nFree>=0 ); if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){ return SQLITE_CORRUPT_PAGE(pPage); @@ -66604,7 +70158,8 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ const int hdr = pPg->hdrOffset; /* Offset to page header */ u8 * const aData = pPg->aData; /* Page data */ int iAddr = hdr + 1; /* Address of ptr to pc */ - int pc = get2byte(&aData[iAddr]); /* Address of a free slot */ + u8 *pTmp = &aData[iAddr]; /* Temporary ptr into aData[] */ + int pc = get2byte(pTmp); /* Address of a free slot */ int x; /* Excess size of the slot */ int maxPC = pPg->pBt->usableSize - nByte; /* Max address for a usable slot */ int size; /* Size of the free slot */ @@ -66614,7 +70169,8 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ /* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each ** freeblock form a big-endian integer which is the size of the freeblock ** in bytes, including the 4-byte header. */ - size = get2byte(&aData[pc+2]); + pTmp = &aData[pc+2]; + size = get2byte(pTmp); if( (x = size - nByte)>=0 ){ testcase( x==4 ); testcase( x==3 ); @@ -66627,6 +70183,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ ** fragmented bytes within the page. */ memcpy(&aData[iAddr], &aData[pc], 2); aData[hdr+7] += (u8)x; + return &aData[pc]; }else if( x+pc > maxPC ){ /* This slot extends off the end of the usable part of the page */ *pRc = SQLITE_CORRUPT_PAGE(pPg); @@ -66639,10 +70196,11 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ return &aData[pc + x]; } iAddr = pc; - pc = get2byte(&aData[pc]); - if( pc<=iAddr+size ){ + pTmp = &aData[pc]; + pc = get2byte(pTmp); + if( pc<=iAddr ){ if( pc ){ - /* The next slot in the chain is not past the end of the current slot */ + /* The next slot in the chain comes before the current slot */ *pRc = SQLITE_CORRUPT_PAGE(pPg); } return 0; @@ -66673,6 +70231,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ u8 * const data = pPage->aData; /* Local cache of pPage->aData */ int top; /* First byte of cell content area */ int rc = SQLITE_OK; /* Integer return code */ + u8 *pTmp; /* Temp ptr into data[] */ int gap; /* First byte of gap between cell pointers and cell content */ assert( wx_sqlite3PagerIswriteable(pPage->pDbPage) ); @@ -66691,7 +70250,8 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ ** then the cell content offset of an empty page wants to be 65536. ** However, that integer is too large to be stored in a 2-byte unsigned ** integer, so a value of 0 is used in its place. */ - top = get2byte(&data[hdr+5]); + pTmp = &data[hdr+5]; + top = get2byte(pTmp); assert( top<=(int)pPage->pBt->usableSize ); /* by btreeComputeFreeSpace() */ if( gap>top ){ if( top==0 && pPage->pBt->usableSize==65536 ){ @@ -66714,7 +70274,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ int g2; assert( pSpace+nByte<=data+pPage->pBt->usableSize ); *pIdx = g2 = (int)(pSpace-data); - if( NEVER(g2<=gap) ){ + if( g2<=gap ){ return SQLITE_CORRUPT_PAGE(pPage); }else{ return SQLITE_OK; @@ -66773,6 +70333,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ u16 x; /* Offset to cell content area */ u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */ unsigned char *data = pPage->aData; /* Page content */ + u8 *pTmp; /* Temporary ptr into data[] */ assert( pPage->pBt!=0 ); assert( wx_sqlite3PagerIswriteable(pPage->pDbPage) ); @@ -66791,7 +70352,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */ }else{ while( (iFreeBlk = get2byte(&data[iPtr]))pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */ return SQLITE_CORRUPT_PAGE(pPage); } - assert( iFreeBlk>iPtr || iFreeBlk==0 ); + assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB ); /* At this point: ** iFreeBlk: First freeblock after iStart, or zero if none @@ -66835,7 +70396,8 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage); data[hdr+7] -= nFrag; } - x = get2byte(&data[hdr+5]); + pTmp = &data[hdr+5]; + x = get2byte(pTmp); if( iStart<=x ){ /* The new freeblock is at the beginning of the cell content area, ** so just extend the cell content area rather than create another @@ -66866,57 +70428,67 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ ** Only the following combinations are supported. Anything different ** indicates a corrupt database files: ** -** PTF_ZERODATA -** PTF_ZERODATA | PTF_LEAF -** PTF_LEAFDATA | PTF_INTKEY -** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF +** PTF_ZERODATA (0x02, 2) +** PTF_LEAFDATA | PTF_INTKEY (0x05, 5) +** PTF_ZERODATA | PTF_LEAF (0x0a, 10) +** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF (0x0d, 13) */ static int decodeFlags(MemPage *pPage, int flagByte){ BtShared *pBt; /* A copy of pPage->pBt */ assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) ); assert( wx_sqlite3_mutex_held(pPage->pBt->mutex) ); - pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 ); - flagByte &= ~PTF_LEAF; - pPage->childPtrSize = 4-4*pPage->leaf; - pPage->xCellSize = cellSizePtr; pBt = pPage->pBt; - if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ - /* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an - ** interior table b-tree page. */ - assert( (PTF_LEAFDATA|PTF_INTKEY)==5 ); - /* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a - ** leaf table b-tree page. */ - assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 ); - pPage->intKey = 1; - if( pPage->leaf ){ + pPage->max1bytePayload = pBt->max1bytePayload; + if( flagByte>=(PTF_ZERODATA | PTF_LEAF) ){ + pPage->childPtrSize = 0; + pPage->leaf = 1; + if( flagByte==(PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF) ){ pPage->intKeyLeaf = 1; + pPage->xCellSize = cellSizePtrTableLeaf; pPage->xParseCell = btreeParseCellPtr; + pPage->intKey = 1; + pPage->maxLocal = pBt->maxLeaf; + pPage->minLocal = pBt->minLeaf; + }else if( flagByte==(PTF_ZERODATA | PTF_LEAF) ){ + pPage->intKey = 0; + pPage->intKeyLeaf = 0; + pPage->xCellSize = cellSizePtr; + pPage->xParseCell = btreeParseCellPtrIndex; + pPage->maxLocal = pBt->maxLocal; + pPage->minLocal = pBt->minLocal; }else{ + pPage->intKey = 0; + pPage->intKeyLeaf = 0; + pPage->xCellSize = cellSizePtr; + pPage->xParseCell = btreeParseCellPtrIndex; + return SQLITE_CORRUPT_PAGE(pPage); + } + }else{ + pPage->childPtrSize = 4; + pPage->leaf = 0; + if( flagByte==(PTF_ZERODATA) ){ + pPage->intKey = 0; + pPage->intKeyLeaf = 0; + pPage->xCellSize = cellSizePtr; + pPage->xParseCell = btreeParseCellPtrIndex; + pPage->maxLocal = pBt->maxLocal; + pPage->minLocal = pBt->minLocal; + }else if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ pPage->intKeyLeaf = 0; pPage->xCellSize = cellSizePtrNoPayload; pPage->xParseCell = btreeParseCellPtrNoPayload; + pPage->intKey = 1; + pPage->maxLocal = pBt->maxLeaf; + pPage->minLocal = pBt->minLeaf; + }else{ + pPage->intKey = 0; + pPage->intKeyLeaf = 0; + pPage->xCellSize = cellSizePtr; + pPage->xParseCell = btreeParseCellPtrIndex; + return SQLITE_CORRUPT_PAGE(pPage); } - pPage->maxLocal = pBt->maxLeaf; - pPage->minLocal = pBt->minLeaf; - }else if( flagByte==PTF_ZERODATA ){ - /* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an - ** interior index b-tree page. */ - assert( (PTF_ZERODATA)==2 ); - /* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a - ** leaf index b-tree page. */ - assert( (PTF_ZERODATA|PTF_LEAF)==10 ); - pPage->intKey = 0; - pPage->intKeyLeaf = 0; - pPage->xParseCell = btreeParseCellPtrIndex; - pPage->maxLocal = pBt->maxLocal; - pPage->minLocal = pBt->minLocal; - }else{ - /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is - ** an error. */ - return SQLITE_CORRUPT_PAGE(pPage); } - pPage->max1bytePayload = pBt->max1bytePayload; return SQLITE_OK; } @@ -67071,7 +70643,7 @@ static int btreeInitPage(MemPage *pPage){ pPage->nOverflow = 0; pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize; pPage->aCellIdx = data + pPage->childPtrSize + 8; - pPage->aDataEnd = pPage->aData + pBt->usableSize; + pPage->aDataEnd = pPage->aData + pBt->pageSize; pPage->aDataOfst = pPage->aData + pPage->childPtrSize; /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the ** number of cells on the page. */ @@ -67106,7 +70678,7 @@ static void zeroPage(MemPage *pPage, int flags){ u8 hdr = pPage->hdrOffset; u16 first; - assert( wx_sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno ); + assert( wx_sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB ); assert( wx_sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); assert( wx_sqlite3PagerGetData(pPage->pDbPage) == data ); assert( wx_sqlite3PagerIswriteable(pPage->pDbPage) ); @@ -67122,7 +70694,7 @@ static void zeroPage(MemPage *pPage, int flags){ pPage->nFree = (u16)(pBt->usableSize - first); decodeFlags(pPage, flags); pPage->cellOffset = first; - pPage->aDataEnd = &data[pBt->usableSize]; + pPage->aDataEnd = &data[pBt->pageSize]; pPage->aCellIdx = &data[first]; pPage->aDataOfst = &data[pPage->childPtrSize]; pPage->nOverflow = 0; @@ -67248,7 +70820,7 @@ static int getAndInitPage( goto getAndInitPage_error2; } } - assert( (*ppPage)->pgno==pgno ); + assert( (*ppPage)->pgno==pgno || CORRUPT_DB ); assert( (*ppPage)->aData==wx_sqlite3PagerGetData(pDbPage) ); /* If obtaining a child page for a cursor, we must verify that the page is @@ -67267,7 +70839,7 @@ getAndInitPage_error1: pCur->pPage = pCur->apPage[pCur->iPage]; } testcase( pgno==0 ); - assert( pgno!=0 || rc==SQLITE_CORRUPT ); + assert( pgno!=0 || rc!=SQLITE_OK ); return rc; } @@ -67725,30 +71297,38 @@ static int removeFromSharingList(BtShared *pBt){ ** MX_CELL_SIZE(pBt) bytes with a 4-byte prefix for a left-child ** pointer. */ -static void allocateTempSpace(BtShared *pBt){ - if( !pBt->pTmpSpace ){ - pBt->pTmpSpace = wx_sqlite3PageMalloc( pBt->pageSize ); - - /* One of the uses of pBt->pTmpSpace is to format cells before - ** inserting them into a leaf page (function fillInCell()). If - ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes - ** by the various routines that manipulate binary cells. Which - ** can mean that fillInCell() only initializes the first 2 or 3 - ** bytes of pTmpSpace, but that the first 4 bytes are copied from - ** it into a database page. This is not actually a problem, but it - ** does cause a valgrind error when the 1 or 2 bytes of unitialized - ** data is passed to system call write(). So to avoid this error, - ** zero the first 4 bytes of temp space here. - ** - ** Also: Provide four bytes of initialized space before the - ** beginning of pTmpSpace as an area available to prepend the - ** left-child pointer to the beginning of a cell. - */ - if( pBt->pTmpSpace ){ - memset(pBt->pTmpSpace, 0, 8); - pBt->pTmpSpace += 4; - } +static SQLITE_NOINLINE int allocateTempSpace(BtShared *pBt){ + assert( pBt!=0 ); + assert( pBt->pTmpSpace==0 ); + /* This routine is called only by btreeCursor() when allocating the + ** first write cursor for the BtShared object */ + assert( pBt->pCursor!=0 && (pBt->pCursor->curFlags & BTCF_WriteFlag)!=0 ); + pBt->pTmpSpace = wx_sqlite3PageMalloc( pBt->pageSize ); + if( pBt->pTmpSpace==0 ){ + BtCursor *pCur = pBt->pCursor; + pBt->pCursor = pCur->pNext; /* Unlink the cursor */ + memset(pCur, 0, sizeof(*pCur)); + return SQLITE_NOMEM_BKPT; } + + /* One of the uses of pBt->pTmpSpace is to format cells before + ** inserting them into a leaf page (function fillInCell()). If + ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes + ** by the various routines that manipulate binary cells. Which + ** can mean that fillInCell() only initializes the first 2 or 3 + ** bytes of pTmpSpace, but that the first 4 bytes are copied from + ** it into a database page. This is not actually a problem, but it + ** does cause a valgrind error when the 1 or 2 bytes of unitialized + ** data is passed to system call write(). So to avoid this error, + ** zero the first 4 bytes of temp space here. + ** + ** Also: Provide four bytes of initialized space before the + ** beginning of pTmpSpace as an area available to prepend the + ** left-child pointer to the beginning of a cell. + */ + memset(pBt->pTmpSpace, 0, 8); + pBt->pTmpSpace += 4; + return SQLITE_OK; } /* @@ -68127,7 +71707,6 @@ static int lockBtree(BtShared *pBt){ MemPage *pPage1; /* Page 1 of the database file */ u32 nPage; /* Number of pages in the database */ u32 nPageFile = 0; /* Number of pages in the database file */ - u32 nPageHeader; /* Number of pages in the database according to hdr */ assert( wx_sqlite3_mutex_held(pBt->mutex) ); assert( pBt->pPage1==0 ); @@ -68139,7 +71718,7 @@ static int lockBtree(BtShared *pBt){ /* Do some checking to help insure the file we opened really is ** a valid database file. */ - nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); + nPage = get4byte(28+(u8*)pPage1->aData); wx_sqlite3PagerPagecount(pBt->pPager, (int*)&nPageFile); if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){ nPage = nPageFile; @@ -68174,7 +71753,7 @@ static int lockBtree(BtShared *pBt){ goto page1_init_failed; } - /* If the write version is set to 2, this database should be accessed + /* If the read version is set to 2, this database should be accessed ** in WAL mode. If the log is not already open, open it now. Then ** return SQLITE_OK and return without populating BtShared.pPage1. ** The caller detects this and calls this function again. This is @@ -68246,9 +71825,13 @@ static int lockBtree(BtShared *pBt){ pageSize-usableSize); return rc; } - if( wx_sqlite3WritableSchema(pBt->db)==0 && nPage>nPageFile ){ - rc = SQLITE_CORRUPT_BKPT; - goto page1_init_failed; + if( nPage>nPageFile ){ + if( wx_sqlite3WritableSchema(pBt->db)==0 ){ + rc = SQLITE_CORRUPT_BKPT; + goto page1_init_failed; + }else{ + nPage = nPageFile; + } } /* EVIDENCE-OF: R-28312-64704 However, the usable size is not allowed to ** be less than 480. In other words, if the page size is 512, then the @@ -68692,6 +72275,9 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ } } }else{ + if( pCell+4 > pPage->aData+pPage->pBt->usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); + } if( get4byte(pCell)==iFrom ){ put4byte(pCell, iTo); break; @@ -68878,12 +72464,17 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){ } do { MemPage *pFreePg; + Pgno dbSize = btreePagecount(pBt); rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode); if( rc!=SQLITE_OK ){ releasePage(pLastPg); return rc; } releasePage(pFreePg); + if( iFreePg>dbSize ){ + releasePage(pLastPg); + return SQLITE_CORRUPT_BKPT; + } }while( bCommit && iFreePg>nFin ); assert( iFreePgpPager; - VVA_ONLY( int nRef = wx_sqlite3PagerRefcount(pPager); ) + Pager *pPager; + BtShared *pBt; + wx_sqlite3 *db; + VVA_ONLY( int nRef ); + + assert( p!=0 ); + pBt = p->pBt; + pPager = pBt->pPager; + VVA_ONLY( nRef = wx_sqlite3PagerRefcount(pPager); ) assert( wx_sqlite3_mutex_held(pBt->mutex) ); invalidateAllOverflowCache(pBt); @@ -68989,6 +72582,7 @@ static int autoVacuumCommit(BtShared *pBt){ if( !pBt->incrVacuum ){ Pgno nFin; /* Number of pages in database after autovacuuming */ Pgno nFree; /* Number of pages on the freelist initially */ + Pgno nVac; /* Number of pages to vacuum */ Pgno iFree; /* The next page to be freed */ Pgno nOrig; /* Database size before freeing */ @@ -69002,18 +72596,42 @@ static int autoVacuumCommit(BtShared *pBt){ } nFree = get4byte(&pBt->pPage1->aData[36]); - nFin = finalDbSize(pBt, nOrig, nFree); + db = p->db; + if( db->xAutovacPages ){ + int iDb; + for(iDb=0; ALWAYS(iDbnDb); iDb++){ + if( db->aDb[iDb].pBt==p ) break; + } + nVac = db->xAutovacPages( + db->pAutovacPagesArg, + db->aDb[iDb].zDbSName, + nOrig, + nFree, + pBt->pageSize + ); + if( nVac>nFree ){ + nVac = nFree; + } + if( nVac==0 ){ + return SQLITE_OK; + } + }else{ + nVac = nFree; + } + nFin = finalDbSize(pBt, nOrig, nVac); if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT; if( nFinnFin && rc==SQLITE_OK; iFree--){ - rc = incrVacuumStep(pBt, nFin, iFree, 1); + rc = incrVacuumStep(pBt, nFin, iFree, nVac==nFree); } if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){ rc = wx_sqlite3PagerWrite(pBt->pPage1->pDbPage); - put4byte(&pBt->pPage1->aData[32], 0); - put4byte(&pBt->pPage1->aData[36], 0); + if( nVac==nFree ){ + put4byte(&pBt->pPage1->aData[32], 0); + put4byte(&pBt->pPage1->aData[36], 0); + } put4byte(&pBt->pPage1->aData[28], nFin); pBt->bDoTruncate = 1; pBt->nPage = nFin; @@ -69064,7 +72682,7 @@ SQLITE_PRIVATE int wx_sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrn wx_sqlite3BtreeEnter(p); #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum ){ - rc = autoVacuumCommit(pBt); + rc = autoVacuumCommit(p); if( rc!=SQLITE_OK ){ wx_sqlite3BtreeLeave(p); return rc; @@ -69251,7 +72869,7 @@ static void btreeSetNPage(BtShared *pBt, MemPage *pPage1){ int nPage = get4byte(&pPage1->aData[28]); testcase( nPage==0 ); if( nPage==0 ) wx_sqlite3PagerPagecount(pBt->pPager, &nPage); - testcase( pBt->nPage!=nPage ); + testcase( pBt->nPage!=(u32)nPage ); pBt->nPage = nPage; } @@ -69463,10 +73081,6 @@ static int btreeCursor( assert( pBt->pPage1 && pBt->pPage1->aData ); assert( wrFlag==0 || (pBt->btsFlags & BTS_READ_ONLY)==0 ); - if( wrFlag ){ - allocateTempSpace(pBt); - if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM_BKPT; - } if( iTable<=1 ){ if( iTable<1 ){ return SQLITE_CORRUPT_BKPT; @@ -69483,19 +73097,25 @@ static int btreeCursor( pCur->pKeyInfo = pKeyInfo; pCur->pBtree = p; pCur->pBt = pBt; - pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0; - pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY; + pCur->curFlags = 0; /* If there are two or more cursors on the same btree, then all such ** cursors *must* have the BTCF_Multiple flag set. */ for(pX=pBt->pCursor; pX; pX=pX->pNext){ if( pX->pgnoRoot==iTable ){ pX->curFlags |= BTCF_Multiple; - pCur->curFlags |= BTCF_Multiple; + pCur->curFlags = BTCF_Multiple; } } + pCur->eState = CURSOR_INVALID; pCur->pNext = pBt->pCursor; pBt->pCursor = pCur; - pCur->eState = CURSOR_INVALID; + if( wrFlag ){ + pCur->curFlags |= BTCF_WriteFlag; + pCur->curPagerFlags = 0; + if( pBt->pTmpSpace==0 ) return allocateTempSpace(pBt); + }else{ + pCur->curPagerFlags = PAGER_GET_READONLY; + } return SQLITE_OK; } static int btreeCursorWithLock( @@ -69869,7 +73489,9 @@ static int accessPayload( assert( pPage ); assert( eOp==0 || eOp==1 ); assert( pCur->eState==CURSOR_VALID ); - assert( pCur->ixnCell ); + if( pCur->ix>=pPage->nCell ){ + return SQLITE_CORRUPT_PAGE(pPage); + } assert( cursorHoldsMutex(pCur) ); getCellInfo(pCur); @@ -70056,7 +73678,6 @@ SQLITE_PRIVATE int wx_sqlite3BtreePayload(BtCursor *pCur, u32 offset, u32 amt, v assert( cursorHoldsMutex(pCur) ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>=0 && pCur->pPage ); - assert( pCur->ixpPage->nCell ); return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); } @@ -70118,7 +73739,7 @@ static const void *fetchPayload( assert( pCur->eState==CURSOR_VALID ); assert( wx_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( cursorOwnsBtShared(pCur) ); - assert( pCur->ixpPage->nCell ); + assert( pCur->ixpPage->nCell || CORRUPT_DB ); assert( pCur->info.nSize>0 ); assert( pCur->info.pPayload>pCur->pPage->aData || CORRUPT_DB ); assert( pCur->info.pPayloadpPage->aDataEnd ||CORRUPT_DB); @@ -70163,8 +73784,6 @@ SQLITE_PRIVATE const void *wx_sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt ** vice-versa). */ static int moveToChild(BtCursor *pCur, u32 newPgno){ - BtShared *pBt = pCur->pBt; - assert( cursorOwnsBtShared(pCur) ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPageapPage[pCur->iPage] = pCur->pPage; pCur->ix = 0; pCur->iPage++; - return getAndInitPage(pBt, newPgno, &pCur->pPage, pCur, pCur->curPagerFlags); + return getAndInitPage(pCur->pBt, newPgno, &pCur->pPage, pCur, + pCur->curPagerFlags); } #ifdef SQLITE_DEBUG @@ -70269,7 +73889,7 @@ static int moveToRoot(BtCursor *pCur){ while( --pCur->iPage ){ releasePageNotNull(pCur->apPage[pCur->iPage]); } - pCur->pPage = pCur->apPage[0]; + pRoot = pCur->pPage = pCur->apPage[0]; goto skip_init; } }else if( pCur->pgnoRoot==0 ){ @@ -70284,7 +73904,7 @@ static int moveToRoot(BtCursor *pCur){ } wx_sqlite3BtreeClearCursor(pCur); } - rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->pPage, + rc = getAndInitPage(pCur->pBt, pCur->pgnoRoot, &pCur->pPage, 0, pCur->curPagerFlags); if( rc!=SQLITE_OK ){ pCur->eState = CURSOR_INVALID; @@ -70294,7 +73914,7 @@ static int moveToRoot(BtCursor *pCur){ pCur->curIntKey = pCur->pPage->intKey; } pRoot = pCur->pPage; - assert( pRoot->pgno==pCur->pgnoRoot ); + assert( pRoot->pgno==pCur->pgnoRoot || CORRUPT_DB ); /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is @@ -70316,7 +73936,6 @@ skip_init: pCur->info.nSize = 0; pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl); - pRoot = pCur->pPage; if( pRoot->nCell>0 ){ pCur->eState = CURSOR_VALID; }else if( !pRoot->leaf ){ @@ -70409,9 +74028,25 @@ SQLITE_PRIVATE int wx_sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. */ +static SQLITE_NOINLINE int btreeLast(BtCursor *pCur, int *pRes){ + int rc = moveToRoot(pCur); + if( rc==SQLITE_OK ){ + assert( pCur->eState==CURSOR_VALID ); + *pRes = 0; + rc = moveToRightmost(pCur); + if( rc==SQLITE_OK ){ + pCur->curFlags |= BTCF_AtLast; + }else{ + pCur->curFlags &= ~BTCF_AtLast; + } + }else if( rc==SQLITE_EMPTY ){ + assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); + *pRes = 1; + rc = SQLITE_OK; + } + return rc; +} SQLITE_PRIVATE int wx_sqlite3BtreeLast(BtCursor *pCur, int *pRes){ - int rc; - assert( cursorOwnsBtShared(pCur) ); assert( wx_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); @@ -70424,37 +74059,19 @@ SQLITE_PRIVATE int wx_sqlite3BtreeLast(BtCursor *pCur, int *pRes){ for(ii=0; iiiPage; ii++){ assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); } - assert( pCur->ix==pCur->pPage->nCell-1 ); + assert( pCur->ix==pCur->pPage->nCell-1 || CORRUPT_DB ); + testcase( pCur->ix!=pCur->pPage->nCell-1 ); + /* ^-- dbsqlfuzz b92b72e4de80b5140c30ab71372ca719b8feb618 */ assert( pCur->pPage->leaf ); #endif *pRes = 0; return SQLITE_OK; } - - rc = moveToRoot(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->eState==CURSOR_VALID ); - *pRes = 0; - rc = moveToRightmost(pCur); - if( rc==SQLITE_OK ){ - pCur->curFlags |= BTCF_AtLast; - }else{ - pCur->curFlags &= ~BTCF_AtLast; - } - }else if( rc==SQLITE_EMPTY ){ - assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); - *pRes = 1; - rc = SQLITE_OK; - } - return rc; + return btreeLast(pCur, pRes); } -/* Move the cursor so that it points to an entry near the key -** specified by pIdxKey or intKey. Return a success code. -** -** For INTKEY tables, the intKey parameter is used. pIdxKey -** must be NULL. For index tables, pIdxKey is used and intKey -** is ignored. +/* Move the cursor so that it points to an entry in a table (a.k.a INTKEY) +** table near the key intKey. Return a success code. ** ** If an exact match is not found, then the cursor is always ** left pointing at a leaf page which would hold the entry if it @@ -70467,39 +74084,32 @@ SQLITE_PRIVATE int wx_sqlite3BtreeLast(BtCursor *pCur, int *pRes){ ** *pRes is as follows: ** ** *pRes<0 The cursor is left pointing at an entry that -** is smaller than intKey/pIdxKey or if the table is empty +** is smaller than intKey or if the table is empty ** and the cursor is therefore left point to nothing. ** ** *pRes==0 The cursor is left pointing at an entry that -** exactly matches intKey/pIdxKey. +** exactly matches intKey. ** ** *pRes>0 The cursor is left pointing at an entry that -** is larger than intKey/pIdxKey. -** -** For index tables, the pIdxKey->eqSeen field is set to 1 if there -** exists an entry in the table that exactly matches pIdxKey. +** is larger than intKey. */ -SQLITE_PRIVATE int wx_sqlite3BtreeMovetoUnpacked( +SQLITE_PRIVATE int wx_sqlite3BtreeTableMoveto( BtCursor *pCur, /* The cursor to be moved */ - UnpackedRecord *pIdxKey, /* Unpacked index key */ i64 intKey, /* The table key */ int biasRight, /* If true, bias the search to the high end */ int *pRes /* Write search results here */ ){ int rc; - RecordCompare xRecordCompare; assert( cursorOwnsBtShared(pCur) ); assert( wx_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( pRes ); - assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); - assert( pCur->eState!=CURSOR_VALID || (pIdxKey==0)==(pCur->curIntKey!=0) ); + assert( pCur->pKeyInfo==0 ); + assert( pCur->eState!=CURSOR_VALID || pCur->curIntKey!=0 ); /* If the cursor is already positioned at the point we are trying ** to move to, then just return without doing any work */ - if( pIdxKey==0 - && pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 - ){ + if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){ if( pCur->info.nKey==intKey ){ *pRes = 0; return SQLITE_OK; @@ -70521,9 +74131,7 @@ SQLITE_PRIVATE int wx_sqlite3BtreeMovetoUnpacked( if( pCur->info.nKey==intKey ){ return SQLITE_OK; } - }else if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - }else{ + }else if( rc!=SQLITE_DONE ){ return rc; } } @@ -70534,17 +74142,6 @@ SQLITE_PRIVATE int wx_sqlite3BtreeMovetoUnpacked( pCur->pBtree->nSeek++; /* Performance measurement during testing */ #endif - if( pIdxKey ){ - xRecordCompare = wx_sqlite3VdbeFindCompare(pIdxKey); - pIdxKey->errCode = 0; - assert( pIdxKey->default_rc==1 - || pIdxKey->default_rc==0 - || pIdxKey->default_rc==-1 - ); - }else{ - xRecordCompare = 0; /* All keys are integers */ - } - rc = moveToRoot(pCur); if( rc ){ if( rc==SQLITE_EMPTY ){ @@ -70559,7 +74156,8 @@ SQLITE_PRIVATE int wx_sqlite3BtreeMovetoUnpacked( assert( pCur->eState==CURSOR_VALID ); assert( pCur->pPage->nCell > 0 ); assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey ); - assert( pCur->curIntKey || pIdxKey ); + assert( pCur->curIntKey ); + for(;;){ int lwr, upr, idx, c; Pgno chldPg; @@ -70573,144 +74171,348 @@ SQLITE_PRIVATE int wx_sqlite3BtreeMovetoUnpacked( ** be the right kind (index or table) of b-tree page. Otherwise ** a moveToChild() or moveToRoot() call would have detected corruption. */ assert( pPage->nCell>0 ); - assert( pPage->intKey==(pIdxKey==0) ); + assert( pPage->intKey ); lwr = 0; upr = pPage->nCell-1; assert( biasRight==0 || biasRight==1 ); idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ - pCur->ix = (u16)idx; - if( xRecordCompare==0 ){ - for(;;){ - i64 nCellKey; - pCell = findCellPastPtr(pPage, idx); - if( pPage->intKeyLeaf ){ - while( 0x80 <= *(pCell++) ){ - if( pCell>=pPage->aDataEnd ){ - return SQLITE_CORRUPT_PAGE(pPage); - } + for(;;){ + i64 nCellKey; + pCell = findCellPastPtr(pPage, idx); + if( pPage->intKeyLeaf ){ + while( 0x80 <= *(pCell++) ){ + if( pCell>=pPage->aDataEnd ){ + return SQLITE_CORRUPT_PAGE(pPage); } } - getVarint(pCell, (u64*)&nCellKey); - if( nCellKeyupr ){ c = -1; break; } - }else if( nCellKey>intKey ){ - upr = idx-1; - if( lwr>upr ){ c = +1; break; } + } + getVarint(pCell, (u64*)&nCellKey); + if( nCellKeyupr ){ c = -1; break; } + }else if( nCellKey>intKey ){ + upr = idx-1; + if( lwr>upr ){ c = +1; break; } + }else{ + assert( nCellKey==intKey ); + pCur->ix = (u16)idx; + if( !pPage->leaf ){ + lwr = idx; + goto moveto_table_next_layer; }else{ - assert( nCellKey==intKey ); - pCur->ix = (u16)idx; - if( !pPage->leaf ){ - lwr = idx; - goto moveto_next_layer; - }else{ - pCur->curFlags |= BTCF_ValidNKey; - pCur->info.nKey = nCellKey; - pCur->info.nSize = 0; - *pRes = 0; - return SQLITE_OK; - } + pCur->curFlags |= BTCF_ValidNKey; + pCur->info.nKey = nCellKey; + pCur->info.nSize = 0; + *pRes = 0; + return SQLITE_OK; } - assert( lwr+upr>=0 ); - idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */ } + assert( lwr+upr>=0 ); + idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */ + } + assert( lwr==upr+1 || !pPage->leaf ); + assert( pPage->isInit ); + if( pPage->leaf ){ + assert( pCur->ixpPage->nCell ); + pCur->ix = (u16)idx; + *pRes = c; + rc = SQLITE_OK; + goto moveto_table_finish; + } +moveto_table_next_layer: + if( lwr>=pPage->nCell ){ + chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); }else{ - for(;;){ - int nCell; /* Size of the pCell cell in bytes */ - pCell = findCellPastPtr(pPage, idx); - - /* The maximum supported page-size is 65536 bytes. This means that - ** the maximum number of record bytes stored on an index B-Tree - ** page is less than 16384 bytes and may be stored as a 2-byte - ** varint. This information is used to attempt to avoid parsing - ** the entire cell by checking for the cases where the record is - ** stored entirely within the b-tree page by inspecting the first - ** 2 bytes of the cell. - */ - nCell = pCell[0]; - if( nCell<=pPage->max1bytePayload ){ - /* This branch runs if the record-size field of the cell is a - ** single byte varint and the record fits entirely on the main - ** b-tree page. */ - testcase( pCell+nCell+1==pPage->aDataEnd ); - c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); - }else if( !(pCell[1] & 0x80) - && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal - ){ - /* The record-size field is a 2 byte varint and the record - ** fits entirely on the main b-tree page. */ - testcase( pCell+nCell+2==pPage->aDataEnd ); - c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); - }else{ - /* The record flows over onto one or more overflow pages. In - ** this case the whole cell needs to be parsed, a buffer allocated - ** and accessPayload() used to retrieve the record into the - ** buffer before VdbeRecordCompare() can be called. - ** - ** If the record is corrupt, the xRecordCompare routine may read - ** up to two varints past the end of the buffer. An extra 18 - ** bytes of padding is allocated at the end of the buffer in - ** case this happens. */ - void *pCellKey; - u8 * const pCellBody = pCell - pPage->childPtrSize; - const int nOverrun = 18; /* Size of the overrun padding */ - pPage->xParseCell(pPage, pCellBody, &pCur->info); - nCell = (int)pCur->info.nKey; - testcase( nCell<0 ); /* True if key size is 2^32 or more */ - testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ - testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ - testcase( nCell==2 ); /* Minimum legal index key size */ - if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){ - rc = SQLITE_CORRUPT_PAGE(pPage); - goto moveto_finish; - } - pCellKey = wx_sqlite3Malloc( nCell+nOverrun ); - if( pCellKey==0 ){ - rc = SQLITE_NOMEM_BKPT; - goto moveto_finish; - } - pCur->ix = (u16)idx; - rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); - memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */ - pCur->curFlags &= ~BTCF_ValidOvfl; - if( rc ){ - wx_sqlite3_free(pCellKey); - goto moveto_finish; - } - c = wx_sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); - wx_sqlite3_free(pCellKey); + chldPg = get4byte(findCell(pPage, lwr)); + } + pCur->ix = (u16)lwr; + rc = moveToChild(pCur, chldPg); + if( rc ) break; + } +moveto_table_finish: + pCur->info.nSize = 0; + assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); + return rc; +} + +/* +** Compare the "idx"-th cell on the page the cursor pCur is currently +** pointing to to pIdxKey using xRecordCompare. Return negative or +** zero if the cell is less than or equal pIdxKey. Return positive +** if unknown. +** +** Return value negative: Cell at pCur[idx] less than pIdxKey +** +** Return value is zero: Cell at pCur[idx] equals pIdxKey +** +** Return value positive: Nothing is known about the relationship +** of the cell at pCur[idx] and pIdxKey. +** +** This routine is part of an optimization. It is always safe to return +** a positive value as that will cause the optimization to be skipped. +*/ +static int indexCellCompare( + BtCursor *pCur, + int idx, + UnpackedRecord *pIdxKey, + RecordCompare xRecordCompare +){ + MemPage *pPage = pCur->pPage; + int c; + int nCell; /* Size of the pCell cell in bytes */ + u8 *pCell = findCellPastPtr(pPage, idx); + + nCell = pCell[0]; + if( nCell<=pPage->max1bytePayload ){ + /* This branch runs if the record-size field of the cell is a + ** single byte varint and the record fits entirely on the main + ** b-tree page. */ + testcase( pCell+nCell+1==pPage->aDataEnd ); + c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); + }else if( !(pCell[1] & 0x80) + && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal + ){ + /* The record-size field is a 2 byte varint and the record + ** fits entirely on the main b-tree page. */ + testcase( pCell+nCell+2==pPage->aDataEnd ); + c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); + }else{ + /* If the record extends into overflow pages, do not attempt + ** the optimization. */ + c = 99; + } + return c; +} + +/* +** Return true (non-zero) if pCur is current pointing to the last +** page of a table. +*/ +static int cursorOnLastPage(BtCursor *pCur){ + int i; + assert( pCur->eState==CURSOR_VALID ); + for(i=0; iiPage; i++){ + MemPage *pPage = pCur->apPage[i]; + if( pCur->aiIdx[i]nCell ) return 0; + } + return 1; +} + +/* Move the cursor so that it points to an entry in an index table +** near the key pIdxKey. Return a success code. +** +** If an exact match is not found, then the cursor is always +** left pointing at a leaf page which would hold the entry if it +** were present. The cursor might point to an entry that comes +** before or after the key. +** +** An integer is written into *pRes which is the result of +** comparing the key with the entry to which the cursor is +** pointing. The meaning of the integer written into +** *pRes is as follows: +** +** *pRes<0 The cursor is left pointing at an entry that +** is smaller than pIdxKey or if the table is empty +** and the cursor is therefore left point to nothing. +** +** *pRes==0 The cursor is left pointing at an entry that +** exactly matches pIdxKey. +** +** *pRes>0 The cursor is left pointing at an entry that +** is larger than pIdxKey. +** +** The pIdxKey->eqSeen field is set to 1 if there +** exists an entry in the table that exactly matches pIdxKey. +*/ +SQLITE_PRIVATE int wx_sqlite3BtreeIndexMoveto( + BtCursor *pCur, /* The cursor to be moved */ + UnpackedRecord *pIdxKey, /* Unpacked index key */ + int *pRes /* Write search results here */ +){ + int rc; + RecordCompare xRecordCompare; + + assert( cursorOwnsBtShared(pCur) ); + assert( wx_sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + assert( pRes ); + assert( pCur->pKeyInfo!=0 ); + +#ifdef SQLITE_DEBUG + pCur->pBtree->nSeek++; /* Performance measurement during testing */ +#endif + + xRecordCompare = wx_sqlite3VdbeFindCompare(pIdxKey); + pIdxKey->errCode = 0; + assert( pIdxKey->default_rc==1 + || pIdxKey->default_rc==0 + || pIdxKey->default_rc==-1 + ); + + + /* Check to see if we can skip a lot of work. Two cases: + ** + ** (1) If the cursor is already pointing to the very last cell + ** in the table and the pIdxKey search key is greater than or + ** equal to that last cell, then no movement is required. + ** + ** (2) If the cursor is on the last page of the table and the first + ** cell on that last page is less than or equal to the pIdxKey + ** search key, then we can start the search on the current page + ** without needing to go back to root. + */ + if( pCur->eState==CURSOR_VALID + && pCur->pPage->leaf + && cursorOnLastPage(pCur) + ){ + int c; + if( pCur->ix==pCur->pPage->nCell-1 + && (c = indexCellCompare(pCur, pCur->ix, pIdxKey, xRecordCompare))<=0 + && pIdxKey->errCode==SQLITE_OK + ){ + *pRes = c; + return SQLITE_OK; /* Cursor already pointing at the correct spot */ + } + if( pCur->iPage>0 + && indexCellCompare(pCur, 0, pIdxKey, xRecordCompare)<=0 + && pIdxKey->errCode==SQLITE_OK + ){ + pCur->curFlags &= ~BTCF_ValidOvfl; + if( !pCur->pPage->isInit ){ + return SQLITE_CORRUPT_BKPT; + } + goto bypass_moveto_root; /* Start search on the current page */ + } + pIdxKey->errCode = SQLITE_OK; + } + + rc = moveToRoot(pCur); + if( rc ){ + if( rc==SQLITE_EMPTY ){ + assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); + *pRes = -1; + return SQLITE_OK; + } + return rc; + } + +bypass_moveto_root: + assert( pCur->pPage ); + assert( pCur->pPage->isInit ); + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->pPage->nCell > 0 ); + assert( pCur->curIntKey==0 ); + assert( pIdxKey!=0 ); + for(;;){ + int lwr, upr, idx, c; + Pgno chldPg; + MemPage *pPage = pCur->pPage; + u8 *pCell; /* Pointer to current cell in pPage */ + + /* pPage->nCell must be greater than zero. If this is the root-page + ** the cursor would have been INVALID above and this for(;;) loop + ** not run. If this is not the root-page, then the moveToChild() routine + ** would have already detected db corruption. Similarly, pPage must + ** be the right kind (index or table) of b-tree page. Otherwise + ** a moveToChild() or moveToRoot() call would have detected corruption. */ + assert( pPage->nCell>0 ); + assert( pPage->intKey==0 ); + lwr = 0; + upr = pPage->nCell-1; + idx = upr>>1; /* idx = (lwr+upr)/2; */ + for(;;){ + int nCell; /* Size of the pCell cell in bytes */ + pCell = findCellPastPtr(pPage, idx); + + /* The maximum supported page-size is 65536 bytes. This means that + ** the maximum number of record bytes stored on an index B-Tree + ** page is less than 16384 bytes and may be stored as a 2-byte + ** varint. This information is used to attempt to avoid parsing + ** the entire cell by checking for the cases where the record is + ** stored entirely within the b-tree page by inspecting the first + ** 2 bytes of the cell. + */ + nCell = pCell[0]; + if( nCell<=pPage->max1bytePayload ){ + /* This branch runs if the record-size field of the cell is a + ** single byte varint and the record fits entirely on the main + ** b-tree page. */ + testcase( pCell+nCell+1==pPage->aDataEnd ); + c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); + }else if( !(pCell[1] & 0x80) + && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal + ){ + /* The record-size field is a 2 byte varint and the record + ** fits entirely on the main b-tree page. */ + testcase( pCell+nCell+2==pPage->aDataEnd ); + c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); + }else{ + /* The record flows over onto one or more overflow pages. In + ** this case the whole cell needs to be parsed, a buffer allocated + ** and accessPayload() used to retrieve the record into the + ** buffer before VdbeRecordCompare() can be called. + ** + ** If the record is corrupt, the xRecordCompare routine may read + ** up to two varints past the end of the buffer. An extra 18 + ** bytes of padding is allocated at the end of the buffer in + ** case this happens. */ + void *pCellKey; + u8 * const pCellBody = pCell - pPage->childPtrSize; + const int nOverrun = 18; /* Size of the overrun padding */ + pPage->xParseCell(pPage, pCellBody, &pCur->info); + nCell = (int)pCur->info.nKey; + testcase( nCell<0 ); /* True if key size is 2^32 or more */ + testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ + testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ + testcase( nCell==2 ); /* Minimum legal index key size */ + if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){ + rc = SQLITE_CORRUPT_PAGE(pPage); + goto moveto_index_finish; + } + pCellKey = wx_sqlite3Malloc( nCell+nOverrun ); + if( pCellKey==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto moveto_index_finish; } - assert( - (pIdxKey->errCode!=SQLITE_CORRUPT || c==0) - && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed) - ); - if( c<0 ){ - lwr = idx+1; - }else if( c>0 ){ - upr = idx-1; - }else{ - assert( c==0 ); - *pRes = 0; - rc = SQLITE_OK; - pCur->ix = (u16)idx; - if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT; - goto moveto_finish; + pCur->ix = (u16)idx; + rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); + memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */ + pCur->curFlags &= ~BTCF_ValidOvfl; + if( rc ){ + wx_sqlite3_free(pCellKey); + goto moveto_index_finish; } - if( lwr>upr ) break; - assert( lwr+upr>=0 ); - idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */ + c = wx_sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); + wx_sqlite3_free(pCellKey); } + assert( + (pIdxKey->errCode!=SQLITE_CORRUPT || c==0) + && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed) + ); + if( c<0 ){ + lwr = idx+1; + }else if( c>0 ){ + upr = idx-1; + }else{ + assert( c==0 ); + *pRes = 0; + rc = SQLITE_OK; + pCur->ix = (u16)idx; + if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT; + goto moveto_index_finish; + } + if( lwr>upr ) break; + assert( lwr+upr>=0 ); + idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */ } assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) ); assert( pPage->isInit ); if( pPage->leaf ){ - assert( pCur->ixpPage->nCell ); + assert( pCur->ixpPage->nCell || CORRUPT_DB ); pCur->ix = (u16)idx; *pRes = c; rc = SQLITE_OK; - goto moveto_finish; + goto moveto_index_finish; } -moveto_next_layer: if( lwr>=pPage->nCell ){ chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); }else{ @@ -70720,7 +74522,7 @@ moveto_next_layer: rc = moveToChild(pCur, chldPg); if( rc ) break; } -moveto_finish: +moveto_index_finish: pCur->info.nSize = 0; assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); return rc; @@ -70811,26 +74613,9 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ pPage = pCur->pPage; idx = ++pCur->ix; if( !pPage->isInit || wx_sqlite3FaultSim(412) ){ - /* The only known way for this to happen is for there to be a - ** recursive SQL function that does a DELETE operation as part of a - ** SELECT which deletes content out from under an active cursor - ** in a corrupt database file where the table being DELETE-ed from - ** has pages in common with the table being queried. See TH3 - ** module cov1/btree78.test testcase 220 (2018-06-08) for an - ** example. */ return SQLITE_CORRUPT_BKPT; } - /* If the database file is corrupt, it is possible for the value of idx - ** to be invalid here. This can only occur if a second cursor modifies - ** the page while cursor pCur is holding a reference to it. Which can - ** only happen if the database is corrupt in such a way as to link the - ** page into more than one b-tree structure. - ** - ** Update 2019-12-23: appears to long longer be possible after the - ** addition of anotherValidCursor() condition on balance_deeper(). */ - harmless( idx>pPage->nCell ); - if( idx>=pPage->nCell ){ if( !pPage->leaf ){ rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); @@ -71003,8 +74788,8 @@ static int allocateBtreePage( assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) ); pPage1 = pBt->pPage1; mxPage = btreePagecount(pBt); - /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36 - ** stores stores the total number of pages on the freelist. */ + /* EVIDENCE-OF: R-21003-45125 The 4-byte big-endian integer at offset 36 + ** stores the total number of pages on the freelist. */ n = get4byte(&pPage1->aData[36]); testcase( n==mxPage-1 ); if( n>=mxPage ){ @@ -71191,7 +74976,7 @@ static int allocateBtreePage( iPage = get4byte(&aData[8+closest*4]); testcase( iPage==mxPage ); - if( iPage>mxPage ){ + if( iPage>mxPage || iPage<2 ){ rc = SQLITE_CORRUPT_PGNO(iTrunk); goto end_allocate_page; } @@ -71349,7 +75134,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ /* If the database supports auto-vacuum, write an entry in the pointer-map ** to indicate that the page is free. */ - if( ISAUTOVACUUM ){ + if( ISAUTOVACUUM(pBt) ){ ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc); if( rc ) goto freepage_out; } @@ -71447,10 +75232,9 @@ static void freePage(MemPage *pPage, int *pRC){ } /* -** Free any overflow pages associated with the given Cell. Store -** size information about the cell in pInfo. +** Free the overflow pages associated with the given Cell. */ -static int clearCell( +static SQLITE_NOINLINE int clearCellOverflow( MemPage *pPage, /* The page that contains the Cell */ unsigned char *pCell, /* First byte of the Cell */ CellInfo *pInfo /* Size information about the cell */ @@ -71462,10 +75246,7 @@ static int clearCell( u32 ovflPageSize; assert( wx_sqlite3_mutex_held(pPage->pBt->mutex) ); - pPage->xParseCell(pPage, pCell, pInfo); - if( pInfo->nLocal==pInfo->nPayload ){ - return SQLITE_OK; /* No overflow pages. Return without doing anything */ - } + assert( pInfo->nLocal!=pInfo->nPayload ); testcase( pCell + pInfo->nSize == pPage->aDataEnd ); testcase( pCell + (pInfo->nSize-1) == pPage->aDataEnd ); if( pCell + pInfo->nSize > pPage->aDataEnd ){ @@ -71521,6 +75302,21 @@ static int clearCell( return SQLITE_OK; } +/* Call xParseCell to compute the size of a cell. If the cell contains +** overflow, then invoke cellClearOverflow to clear out that overflow. +** STore the result code (SQLITE_OK or some error code) in rc. +** +** Implemented as macro to force inlining for performance. +*/ +#define BTREE_CLEAR_CELL(rc, pPage, pCell, sInfo) \ + pPage->xParseCell(pPage, pCell, &sInfo); \ + if( sInfo.nLocal!=sInfo.nPayload ){ \ + rc = clearCellOverflow(pPage, pCell, &sInfo); \ + }else{ \ + rc = SQLITE_OK; \ + } + + /* ** Create the byte sequence used to represent a cell on page pPage ** and write that byte sequence into pCell[]. Overflow pages are @@ -71731,16 +75527,18 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ if( *pRC ) return; - assert( idx>=0 && idxnCell ); + assert( idx>=0 ); + assert( idxnCell ); assert( CORRUPT_DB || sz==cellSize(pPage, idx) ); assert( wx_sqlite3PagerIswriteable(pPage->pDbPage) ); assert( wx_sqlite3_mutex_held(pPage->pBt->mutex) ); assert( pPage->nFree>=0 ); data = pPage->aData; ptr = &pPage->aCellIdx[2*idx]; + assert( pPage->pBt->usableSize > (u32)(ptr-data) ); pc = get2byte(ptr); hdr = pPage->hdrOffset; - testcase( pc==get2byte(&data[hdr+5]) ); + testcase( pc==(u32)get2byte(&data[hdr+5]) ); testcase( pc+sz==pPage->pBt->usableSize ); if( pc+sz > pPage->pBt->usableSize ){ *pRC = SQLITE_CORRUPT_BKPT; @@ -71776,24 +75574,20 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ ** in pTemp or the original pCell) and also record its index. ** Allocating a new entry in pPage->aCell[] implies that ** pPage->nOverflow is incremented. -** -** *pRC must be SQLITE_OK when this routine is called. */ -static void insertCell( +static int insertCell( MemPage *pPage, /* Page into which we are copying */ int i, /* New cell becomes the i-th cell of the page */ u8 *pCell, /* Content of the new cell */ int sz, /* Bytes of content in pCell */ u8 *pTemp, /* Temp storage space for pCell, if needed */ - Pgno iChild, /* If non-zero, replace first 4 bytes with this value */ - int *pRC /* Read and write return code from here */ + Pgno iChild /* If non-zero, replace first 4 bytes with this value */ ){ int idx = 0; /* Where to write new cell content in data[] */ int j; /* Loop counter */ u8 *data; /* The content of the whole page */ u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */ - assert( *pRC==SQLITE_OK ); assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); assert( MX_CELL(pPage->pBt)<=10921 ); assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB ); @@ -71828,14 +75622,13 @@ static void insertCell( }else{ int rc = wx_sqlite3PagerWrite(pPage->pDbPage); if( rc!=SQLITE_OK ){ - *pRC = rc; - return; + return rc; } assert( wx_sqlite3PagerIswriteable(pPage->pDbPage) ); data = pPage->aData; assert( &data[pPage->cellOffset]==pPage->aCellIdx ); rc = allocateSpace(pPage, sz, &idx); - if( rc ){ *pRC = rc; return; } + if( rc ){ return rc; } /* The allocateSpace() routine guarantees the following properties ** if it returns successfully */ assert( idx >= 0 ); @@ -71862,13 +75655,16 @@ static void insertCell( assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB ); #ifndef SQLITE_OMIT_AUTOVACUUM if( pPage->pBt->autoVacuum ){ + int rc2 = SQLITE_OK; /* The cell may contain a pointer to an overflow page. If so, write ** the entry for the overflow page into the pointer map. */ - ptrmapPutOvflPtr(pPage, pPage, pCell, pRC); + ptrmapPutOvflPtr(pPage, pPage, pCell, &rc2); + if( rc2 ) return rc2; } #endif } + return SQLITE_OK; } /* @@ -71969,14 +75765,16 @@ struct CellArray { ** computed. */ static void populateCellCache(CellArray *p, int idx, int N){ + MemPage *pRef = p->pRef; + u16 *szCell = p->szCell; assert( idx>=0 && idx+N<=p->nCell ); while( N>0 ){ assert( p->apCell[idx]!=0 ); - if( p->szCell[idx]==0 ){ - p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]); + if( szCell[idx]==0 ){ + szCell[idx] = pRef->xCellSize(pRef, p->apCell[idx]); }else{ assert( CORRUPT_DB || - p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) ); + szCell[idx]==pRef->xCellSize(pRef, p->apCell[idx]) ); } idx++; N--; @@ -72032,7 +75830,7 @@ static int rebuildPage( assert( i(u32)usableSize) ){ j = 0; } + if( j>(u32)usableSize ){ j = 0; } memcpy(&pTmp[j], &aData[j], usableSize - j); for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kapCell[i]; u16 sz = pCArray->szCell[i]; assert( sz>0 ); - if( SQLITE_WITHIN(pCell,aData,pEnd) ){ + if( SQLITE_WITHIN(pCell,aData+j,pEnd) ){ if( ((uptr)(pCell+sz))>(uptr)pEnd ) return SQLITE_CORRUPT_BKPT; pCell = &pTmp[pCell - aData]; }else if( (uptr)(pCell+sz)>(uptr)pSrcEnd @@ -72056,9 +75854,8 @@ static int rebuildPage( put2byte(pCellptr, (pData - aData)); pCellptr += 2; if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT; - memcpy(pData, pCell, sz); + memmove(pData, pCell, sz); assert( sz==pPg->xCellSize(pPg, pCell) || CORRUPT_DB ); - testcase( sz!=pPg->xCellSize(pPg,pCell) ) i++; if( i>=iEnd ) break; if( pCArray->ixNx[k]<=i ){ @@ -72179,8 +75976,8 @@ static int pageFreeArray( int nRet = 0; int i; int iEnd = iFirst + nCell; - u8 *pFree = 0; - int szFree = 0; + u8 *pFree = 0; /* \__ Parameters for pending call to */ + int szFree = 0; /* / freeSpace() */ for(i=iFirst; iapCell[i]; @@ -72197,8 +75994,13 @@ static int pageFreeArray( } pFree = pCell; szFree = sz; - if( pFree+sz>pEnd ) return 0; + if( pFree+sz>pEnd ){ + return 0; + } }else{ + /* The current cell is adjacent to and before the pFree cell. + ** Combine the two regions into one to reduce the number of calls + ** to freeSpace(). */ pFree = pCell; szFree += sz; } @@ -72262,6 +76064,7 @@ static int editPage( pData = &aData[get2byteNotZero(&aData[hdr+5])]; if( pDatapPg->aDataEnd ) goto editpage_fail; /* Add cells to the start of the page */ if( iNewpgno, &rc); if( szCell>pNew->minLocal ){ ptrmapPutOvflPtr(pNew, pNew, pCell, &rc); @@ -72433,8 +76236,8 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ /* Insert the new divider cell into pParent. */ if( rc==SQLITE_OK ){ - insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), - 0, pPage->pgno, &rc); + rc = insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), + 0, pPage->pgno); } /* Set the right-child pointer of pParent to point to the new page. */ @@ -72543,7 +76346,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ /* If this is an auto-vacuum database, update the pointer-map entries ** for any b-tree or overflow pages that pTo now contains the pointers to. */ - if( ISAUTOVACUUM ){ + if( ISAUTOVACUUM(pBt) ){ *pRC = setChildPtrmaps(pTo); } } @@ -72621,13 +76424,10 @@ static int balance_nonroot( Pgno pgno; /* Temp var to store a page number in */ u8 abDone[NB+2]; /* True after i'th new page is populated */ Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */ - Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */ - u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */ - CellArray b; /* Parsed information on cells being balanced */ + CellArray b; /* Parsed information on cells being balanced */ memset(abDone, 0, sizeof(abDone)); - b.nCell = 0; - b.apCell = 0; + memset(&b, 0, sizeof(b)); pBt = pParent->pBt; assert( wx_sqlite3_mutex_held(pBt->mutex) ); assert( wx_sqlite3PagerIswriteable(pParent->pDbPage) ); @@ -72692,6 +76492,7 @@ static int balance_nonroot( goto balance_cleanup; } } + nMaxCells += apOld[i]->nCell + ArraySize(pParent->apOvfl); if( (i--)==0 ) break; if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){ @@ -72733,7 +76534,6 @@ static int balance_nonroot( /* Make nMaxCells a multiple of 4 in order to preserve 8-byte ** alignment */ - nMaxCells = nOld*(MX_CELL(pBt) + ArraySize(pParent->apOvfl)); nMaxCells = (nMaxCells + 3)&~3; /* @@ -72850,7 +76650,7 @@ static int balance_nonroot( b.szCell[b.nCell] = b.szCell[b.nCell] - leafCorrection; if( !pOld->leaf ){ assert( leafCorrection==0 ); - assert( pOld->hdrOffset==0 ); + assert( pOld->hdrOffset==0 || CORRUPT_DB ); /* The right pointer of the child page pOld becomes the left ** pointer of the divider cell */ memcpy(b.apCell[b.nCell], &pOld->aData[8], 4); @@ -72970,15 +76770,17 @@ static int balance_nonroot( d = r + 1 - leafData; (void)cachedCellSize(&b, d); do{ + int szR, szD; assert( d szLeft-(b.szCell[r]+(i==k-1?0:2)))){ + && (bBulk || szRight+szD+2 > szLeft-(szR+(i==k-1?0:2)))){ break; } - szRight += b.szCell[d] + 2; - szLeft -= b.szCell[r] + 2; + szRight += szD + 2; + szLeft -= szR + 2; cntNew[i-1] = r; r--; d--; @@ -73016,7 +76818,9 @@ static int balance_nonroot( apOld[i] = 0; rc = wx_sqlite3PagerWrite(pNew->pDbPage); nNew++; - if( wx_sqlite3PagerPageRefcount(pNew->pDbPage)!=1+(i==(iParentIdx-nxDiv)) ){ + if( wx_sqlite3PagerPageRefcount(pNew->pDbPage)!=1+(i==(iParentIdx-nxDiv)) + && rc==SQLITE_OK + ){ rc = SQLITE_CORRUPT_BKPT; } if( rc ) goto balance_cleanup; @@ -73030,7 +76834,7 @@ static int balance_nonroot( cntOld[i] = b.nCell; /* Set the pointer-map entry for the new sibling page. */ - if( ISAUTOVACUUM ){ + if( ISAUTOVACUUM(pBt) ){ ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc); if( rc!=SQLITE_OK ){ goto balance_cleanup; @@ -73045,42 +76849,39 @@ static int balance_nonroot( ** of the table is closer to a linear scan through the file. That in turn ** helps the operating system to deliver pages from the disk more rapidly. ** - ** An O(n^2) insertion sort algorithm is used, but since n is never more - ** than (NB+2) (a small constant), that should not be a problem. + ** An O(N*N) sort algorithm is used, but since N is never more than NB+2 + ** (5), that is not a performance concern. ** ** When NB==3, this one optimization makes the database about 25% faster ** for large insertions and deletions. */ for(i=0; ipgno; - aPgFlags[i] = apNew[i]->pDbPage->flags; - for(j=0; jpgno; + assert( apNew[i]->pDbPage->flags & PGHDR_WRITEABLE ); + assert( apNew[i]->pDbPage->flags & PGHDR_DIRTY ); } - for(i=0; ipgno < apNew[iB]->pgno ) iB = j; } - pgno = aPgOrder[iBest]; - aPgOrder[iBest] = 0xffffffff; - if( iBest!=i ){ - if( iBest>i ){ - wx_sqlite3PagerRekey(apNew[iBest]->pDbPage, pBt->nPage+iBest+1, 0); - } - wx_sqlite3PagerRekey(apNew[i]->pDbPage, pgno, aPgFlags[iBest]); - apNew[i]->pgno = pgno; + + /* If apNew[i] has a page number that is bigger than any of the + ** subsequence apNew[i] entries, then swap apNew[i] with the subsequent + ** entry that has the smallest page number (which we know to be + ** entry apNew[iB]). + */ + if( iB!=i ){ + Pgno pgnoA = apNew[i]->pgno; + Pgno pgnoB = apNew[iB]->pgno; + Pgno pgnoTemp = (PENDING_BYTE/pBt->pageSize)+1; + u16 fgA = apNew[i]->pDbPage->flags; + u16 fgB = apNew[iB]->pDbPage->flags; + wx_sqlite3PagerRekey(apNew[i]->pDbPage, pgnoTemp, fgB); + wx_sqlite3PagerRekey(apNew[iB]->pDbPage, pgnoA, fgA); + wx_sqlite3PagerRekey(apNew[i]->pDbPage, pgnoB, fgB); + apNew[i]->pgno = pgnoB; + apNew[iB]->pgno = pgnoA; } } @@ -73126,7 +76927,7 @@ static int balance_nonroot( ** updated. This happens below, after the sibling pages have been ** populated, not here. */ - if( ISAUTOVACUUM ){ + if( ISAUTOVACUUM(pBt) ){ MemPage *pOld; MemPage *pNew = pOld = apNew[0]; int cntOldNext = pNew->nCell + pNew->nOverflow; @@ -73173,6 +76974,7 @@ static int balance_nonroot( u8 *pCell; u8 *pTemp; int sz; + u8 *pSrcEnd; MemPage *pNew = apNew[i]; j = cntNew[i]; @@ -73216,7 +77018,13 @@ static int balance_nonroot( iOvflSpace += sz; assert( sz<=pBt->maxLocal+23 ); assert( iOvflSpace <= (int)pBt->pageSize ); - insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno, &rc); + for(k=0; b.ixNx[k]<=j && ALWAYS(kpgno); if( rc!=SQLITE_OK ) goto balance_cleanup; assert( wx_sqlite3PagerIswriteable(pParent->pDbPage) ); } @@ -73312,7 +77120,7 @@ static int balance_nonroot( ); copyNodeContent(apNew[0], pParent, &rc); freePage(apNew[0], &rc); - }else if( ISAUTOVACUUM && !leafCorrection ){ + }else if( ISAUTOVACUUM(pBt) && !leafCorrection ){ /* Fix the pointer map entries associated with the right-child of each ** sibling page. All other pointer map entries have already been taken ** care of. */ @@ -73333,7 +77141,7 @@ static int balance_nonroot( } #if 0 - if( ISAUTOVACUUM && rc==SQLITE_OK && apNew[0]->isInit ){ + if( ISAUTOVACUUM(pBt) && rc==SQLITE_OK && apNew[0]->isInit ){ /* The ptrmapCheckPages() contains assert() statements that verify that ** all pointer map pages are set correctly. This is helpful while ** debugging. This is usually disabled because a corrupt database may @@ -73395,7 +77203,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ if( rc==SQLITE_OK ){ rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0); copyNodeContent(pRoot, pChild, &rc); - if( ISAUTOVACUUM ){ + if( ISAUTOVACUUM(pBt) ){ ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc); } } @@ -73429,7 +77237,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ ** Return SQLITE_CORRUPT if any cursor other than pCur is currently valid ** on the same B-tree as pCur. ** -** This can if a database is corrupt with two or more SQL tables +** This can occur if a database is corrupt with two or more SQL tables ** pointing to the same b-tree. If an insert occurs on one SQL table ** and causes a BEFORE TRIGGER to do a secondary insert on the other SQL ** table linked to the same b-tree. If the secondary insert causes a @@ -73461,7 +77269,6 @@ static int anotherValidCursor(BtCursor *pCur){ */ static int balance(BtCursor *pCur){ int rc = SQLITE_OK; - const int nMin = pCur->pBt->usableSize * 2 / 3; u8 aBalanceQuickSpace[13]; u8 *pFree = 0; @@ -73473,7 +77280,11 @@ static int balance(BtCursor *pCur){ MemPage *pPage = pCur->pPage; if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break; - if( pPage->nOverflow==0 && pPage->nFree<=nMin ){ + if( pPage->nOverflow==0 && pPage->nFree*3<=(int)pCur->pBt->usableSize*2 ){ + /* No rebalance required as long as: + ** (1) There are no overflow cells + ** (2) The amount of free space on the page is less than 2/3rds of + ** the total usable space on the page. */ break; }else if( (iPage = pCur->iPage)==0 ){ if( pPage->nOverflow && (rc = anotherValidCursor(pCur))==SQLITE_OK ){ @@ -73496,6 +77307,11 @@ static int balance(BtCursor *pCur){ }else{ break; } + }else if( wx_sqlite3PagerPageRefcount(pPage->pDbPage)>1 ){ + /* The page being written is not a root page, and there is currently + ** more than one reference to it. This only happens if the page is one + ** of its own ancestor pages. Corruption. */ + rc = SQLITE_CORRUPT_BKPT; }else{ MemPage * const pParent = pCur->apPage[iPage-1]; int const iIdx = pCur->aiIdx[iPage-1]; @@ -73626,9 +77442,13 @@ static int btreeOverwriteContent( /* ** Overwrite the cell that cursor pCur is pointing to with fresh content -** contained in pX. +** contained in pX. In this variant, pCur is pointing to an overflow +** cell. */ -static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ +static SQLITE_NOINLINE int btreeOverwriteOverflowCell( + BtCursor *pCur, /* Cursor pointing to cell to ovewrite */ + const BtreePayload *pX /* Content to write into the cell */ +){ int iOffset; /* Next byte of pX->pData to write */ int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */ int rc; /* Return code */ @@ -73637,16 +77457,12 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ Pgno ovflPgno; /* Next overflow page to write */ u32 ovflPageSize; /* Size to write on overflow page */ - if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd - || pCur->info.pPayload < pPage->aData + pPage->cellOffset - ){ - return SQLITE_CORRUPT_BKPT; - } + assert( pCur->info.nLocalinfo.pPayload, pX, 0, pCur->info.nLocal); if( rc ) return rc; - if( pCur->info.nLocal==nTotal ) return SQLITE_OK; /* Now overwrite the overflow pages */ iOffset = pCur->info.nLocal; @@ -73658,7 +77474,7 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ do{ rc = btreeGetPage(pBt, ovflPgno, &pPage, 0); if( rc ) return rc; - if( wx_sqlite3PagerPageRefcount(pPage->pDbPage)!=1 ){ + if( wx_sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){ rc = SQLITE_CORRUPT_BKPT; }else{ if( iOffset+ovflPageSize<(u32)nTotal ){ @@ -73676,6 +77492,29 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ return SQLITE_OK; } +/* +** Overwrite the cell that cursor pCur is pointing to with fresh content +** contained in pX. +*/ +static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ + int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */ + MemPage *pPage = pCur->pPage; /* Page being written */ + + if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd + || pCur->info.pPayload < pPage->aData + pPage->cellOffset + ){ + return SQLITE_CORRUPT_BKPT; + } + if( pCur->info.nLocal==nTotal ){ + /* The entire cell is local */ + return btreeOverwriteContent(pPage, pCur->info.pPayload, pX, + 0, pCur->info.nLocal); + }else{ + /* The cell contains overflow content */ + return btreeOverwriteOverflowCell(pCur, pX); + } +} + /* ** Insert a new record into the BTree. The content of the new record @@ -73693,7 +77532,7 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ ** pX.pData,nData,nZero fields must be zero. ** ** If the seekResult parameter is non-zero, then a successful call to -** MovetoUnpacked() to seek cursor pCur to (pKey,nKey) has already +** wx_sqlite3BtreeIndexMoveto() to seek cursor pCur to (pKey,nKey) has already ** been performed. In other words, if seekResult!=0 then the cursor ** is currently pointing to a cell that will be adjacent to the cell ** to be inserted. If seekResult<0 then pCur points to a cell that is @@ -73711,7 +77550,7 @@ SQLITE_PRIVATE int wx_sqlite3BtreeInsert( BtCursor *pCur, /* Insert data into the table of this cursor */ const BtreePayload *pX, /* Content of the row to be inserted */ int flags, /* True if this is likely an append */ - int seekResult /* Result of prior MovetoUnpacked() call */ + int seekResult /* Result of prior IndexMoveto() call */ ){ int rc; int loc = seekResult; /* -1: before desired location +1: after */ @@ -73719,31 +77558,12 @@ SQLITE_PRIVATE int wx_sqlite3BtreeInsert( int idx; MemPage *pPage; Btree *p = pCur->pBtree; - BtShared *pBt = p->pBt; unsigned char *oldCell; unsigned char *newCell = 0; assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND|BTREE_PREFORMAT))==flags ); assert( (flags & BTREE_PREFORMAT)==0 || seekResult || pCur->pKeyInfo==0 ); - if( pCur->eState==CURSOR_FAULT ){ - assert( pCur->skipNext!=SQLITE_OK ); - return pCur->skipNext; - } - - assert( cursorOwnsBtShared(pCur) ); - assert( (pCur->curFlags & BTCF_WriteFlag)!=0 - && pBt->inTransaction==TRANS_WRITE - && (pBt->btsFlags & BTS_READ_ONLY)==0 ); - assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); - - /* Assert that the caller has been consistent. If this cursor was opened - ** expecting an index b-tree, then the caller should be inserting blob - ** keys with no associated data. If the cursor was opened expecting an - ** intkey table, the caller should be inserting integer keys with a - ** blob of associated data. */ - assert( (flags & BTREE_PREFORMAT) || (pX->pKey==0)==(pCur->pKeyInfo==0) ); - /* Save the positions of any other cursors open on this table. ** ** In some cases, the call to btreeMoveto() below is a no-op. For @@ -73756,15 +77576,48 @@ SQLITE_PRIVATE int wx_sqlite3BtreeInsert( ** not to clear the cursor here. */ if( pCur->curFlags & BTCF_Multiple ){ - rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); + rc = saveAllCursors(p->pBt, pCur->pgnoRoot, pCur); if( rc ) return rc; + if( loc && pCur->iPage<0 ){ + /* This can only happen if the schema is corrupt such that there is more + ** than one table or index with the same root page as used by the cursor. + ** Which can only happen if the SQLITE_NoSchemaError flag was set when + ** the schema was loaded. This cannot be asserted though, as a user might + ** set the flag, load the schema, and then unset the flag. */ + return SQLITE_CORRUPT_BKPT; + } } + /* Ensure that the cursor is not in the CURSOR_FAULT state and that it + ** points to a valid cell. + */ + if( pCur->eState>=CURSOR_REQUIRESEEK ){ + testcase( pCur->eState==CURSOR_REQUIRESEEK ); + testcase( pCur->eState==CURSOR_FAULT ); + rc = moveToRoot(pCur); + if( rc && rc!=SQLITE_EMPTY ) return rc; + } + + assert( cursorOwnsBtShared(pCur) ); + assert( (pCur->curFlags & BTCF_WriteFlag)!=0 + && p->pBt->inTransaction==TRANS_WRITE + && (p->pBt->btsFlags & BTS_READ_ONLY)==0 ); + assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); + + /* Assert that the caller has been consistent. If this cursor was opened + ** expecting an index b-tree, then the caller should be inserting blob + ** keys with no associated data. If the cursor was opened expecting an + ** intkey table, the caller should be inserting integer keys with a + ** blob of associated data. */ + assert( (flags & BTREE_PREFORMAT) || (pX->pKey==0)==(pCur->pKeyInfo==0) ); + if( pCur->pKeyInfo==0 ){ assert( pX->pKey==0 ); /* If this is an insert into a table b-tree, invalidate any incrblob ** cursors open on the row being replaced */ - invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0); + if( p->hasIncrblobCur ){ + invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0); + } /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing ** to a row with the same key as the new entry being inserted. @@ -73797,7 +77650,8 @@ SQLITE_PRIVATE int wx_sqlite3BtreeInsert( ** to an adjacent cell. Move the cursor so that it is pointing either ** to the cell to be overwritten or an adjacent cell. */ - rc = wx_sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc); + rc = wx_sqlite3BtreeTableMoveto(pCur, pX->nKey, + (flags & BTREE_APPEND)!=0, &loc); if( rc ) return rc; } }else{ @@ -73820,13 +77674,11 @@ SQLITE_PRIVATE int wx_sqlite3BtreeInsert( r.aMem = pX->aMem; r.nField = pX->nMem; r.default_rc = 0; - r.errCode = 0; - r.r1 = 0; - r.r2 = 0; r.eqSeen = 0; - rc = wx_sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc); + rc = wx_sqlite3BtreeIndexMoveto(pCur, &r, &loc); }else{ - rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc); + rc = btreeMoveto(pCur, pX->pKey, pX->nKey, + (flags & BTREE_APPEND)!=0, &loc); } if( rc ) return rc; } @@ -73845,17 +77697,16 @@ SQLITE_PRIVATE int wx_sqlite3BtreeInsert( return btreeOverwriteCell(pCur, &x2); } } - } assert( pCur->eState==CURSOR_VALID - || (pCur->eState==CURSOR_INVALID && loc) - || CORRUPT_DB ); + || (pCur->eState==CURSOR_INVALID && loc) ); pPage = pCur->pPage; assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) ); assert( pPage->leaf || !pPage->intKey ); if( pPage->nFree<0 ){ - if( pCur->eState>CURSOR_INVALID ){ + if( NEVER(pCur->eState>CURSOR_INVALID) ){ + /* ^^^^^--- due to the moveToRoot() call above */ rc = SQLITE_CORRUPT_BKPT; }else{ rc = btreeComputeFreeSpace(pPage); @@ -73866,31 +77717,37 @@ SQLITE_PRIVATE int wx_sqlite3BtreeInsert( TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, loc==0 ? "overwrite" : "new entry")); - assert( pPage->isInit ); - newCell = pBt->pTmpSpace; + assert( pPage->isInit || CORRUPT_DB ); + newCell = p->pBt->pTmpSpace; assert( newCell!=0 ); + assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT ); if( flags & BTREE_PREFORMAT ){ rc = SQLITE_OK; - szNew = pBt->nPreformatSize; + szNew = p->pBt->nPreformatSize; if( szNew<4 ) szNew = 4; - if( ISAUTOVACUUM && szNew>pPage->maxLocal ){ + if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){ CellInfo info; pPage->xParseCell(pPage, newCell, &info); if( info.nPayload!=info.nLocal ){ Pgno ovfl = get4byte(&newCell[szNew-4]); - ptrmapPut(pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc); + ptrmapPut(p->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc); + if( NEVER(rc) ) goto end_insert; } } }else{ rc = fillInCell(pPage, newCell, pX, &szNew); + if( rc ) goto end_insert; } - if( rc ) goto end_insert; assert( szNew==pPage->xCellSize(pPage, newCell) ); - assert( szNew <= MX_CELL_SIZE(pBt) ); + assert( szNew <= MX_CELL_SIZE(p->pBt) ); idx = pCur->ix; + pCur->info.nSize = 0; if( loc==0 ){ CellInfo info; - assert( idxnCell ); + assert( idx>=0 ); + if( idx>=pPage->nCell ){ + return SQLITE_CORRUPT_BKPT; + } rc = wx_sqlite3PagerWrite(pPage->pDbPage); if( rc ){ goto end_insert; @@ -73899,11 +77756,11 @@ SQLITE_PRIVATE int wx_sqlite3BtreeInsert( if( !pPage->leaf ){ memcpy(newCell, oldCell, 4); } - rc = clearCell(pPage, oldCell, &info); + BTREE_CLEAR_CELL(rc, pPage, oldCell, info); testcase( pCur->curFlags & BTCF_ValidOvfl ); invalidateOverflowCache(pCur); if( info.nSize==szNew && info.nLocal==info.nPayload - && (!ISAUTOVACUUM || szNewminLocal) + && (!ISAUTOVACUUM(p->pBt) || szNewminLocal) ){ /* Overwrite the old cell with the new if they are the same size. ** We could also try to do this if the old cell is smaller, then add @@ -73933,7 +77790,7 @@ SQLITE_PRIVATE int wx_sqlite3BtreeInsert( }else{ assert( pPage->leaf ); } - insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); + rc = insertCell(pPage, idx, newCell, szNew, 0, 0); assert( pPage->nOverflow==0 || rc==SQLITE_OK ); assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); @@ -73957,7 +77814,6 @@ SQLITE_PRIVATE int wx_sqlite3BtreeInsert( ** larger than the largest existing key, it is possible to insert the ** row without seeking the cursor. This can be a big performance boost. */ - pCur->info.nSize = 0; if( pPage->nOverflow ){ assert( rc==SQLITE_OK ); pCur->curFlags &= ~(BTCF_ValidNKey); @@ -74006,7 +77862,6 @@ end_insert: ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ SQLITE_PRIVATE int wx_sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){ - int rc = SQLITE_OK; BtShared *pBt = pDest->pBt; u8 *aOut = pBt->pTmpSpace; /* Pointer to next output buffer */ const u8 *aIn; /* Pointer to next input buffer */ @@ -74014,7 +77869,11 @@ SQLITE_PRIVATE int wx_sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i u32 nRem; /* Bytes of data still to copy */ getCellInfo(pSrc); - aOut += putVarint32(aOut, pSrc->info.nPayload); + if( pSrc->info.nPayload<0x80 ){ + *(aOut++) = pSrc->info.nPayload; + }else{ + aOut += wx_sqlite3PutVarint(aOut, pSrc->info.nPayload); + } if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey); nIn = pSrc->info.nLocal; aIn = pSrc->info.pPayload; @@ -74025,7 +77884,9 @@ SQLITE_PRIVATE int wx_sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i if( nIn==nRem && nInpPage->maxLocal ){ memcpy(aOut, aIn, nIn); pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace); + return SQLITE_OK; }else{ + int rc = SQLITE_OK; Pager *pSrcPager = pSrc->pBt->pPager; u8 *pPgnoOut = 0; Pgno ovflIn = 0; @@ -74072,12 +77933,12 @@ SQLITE_PRIVATE int wx_sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i } }while( rc==SQLITE_OK && nOut>0 ); - if( rc==SQLITE_OK && nRem>0 ){ + if( rc==SQLITE_OK && nRem>0 && ALWAYS(pPgnoOut) ){ Pgno pgnoNew; MemPage *pNew = 0; rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); put4byte(pPgnoOut, pgnoNew); - if( ISAUTOVACUUM && pPageOut ){ + if( ISAUTOVACUUM(pBt) && pPageOut ){ ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc); } releasePage(pPageOut); @@ -74093,9 +77954,8 @@ SQLITE_PRIVATE int wx_sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i releasePage(pPageOut); wx_sqlite3PagerUnref(pPageIn); + return rc; } - - return rc; } /* @@ -74118,14 +77978,13 @@ SQLITE_PRIVATE int wx_sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i SQLITE_PRIVATE int wx_sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ Btree *p = pCur->pBtree; BtShared *pBt = p->pBt; - int rc; /* Return code */ - MemPage *pPage; /* Page to delete cell from */ - unsigned char *pCell; /* Pointer to cell to delete */ - int iCellIdx; /* Index of cell to delete */ - int iCellDepth; /* Depth of node containing pCell */ - CellInfo info; /* Size of the cell being deleted */ - int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */ - u8 bPreserve = flags & BTREE_SAVEPOSITION; /* Keep cursor valid */ + int rc; /* Return code */ + MemPage *pPage; /* Page to delete cell from */ + unsigned char *pCell; /* Pointer to cell to delete */ + int iCellIdx; /* Index of cell to delete */ + int iCellDepth; /* Depth of node containing pCell */ + CellInfo info; /* Size of the cell being deleted */ + u8 bPreserve; /* Keep cursor valid. 2 for CURSOR_SKIPNEXT */ assert( cursorOwnsBtShared(pCur) ); assert( pBt->inTransaction==TRANS_WRITE ); @@ -74134,30 +77993,49 @@ SQLITE_PRIVATE int wx_sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); assert( !hasReadConflicts(p, pCur->pgnoRoot) ); assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); - if( pCur->eState==CURSOR_REQUIRESEEK ){ - rc = btreeRestoreCursorPosition(pCur); - if( rc ) return rc; + if( pCur->eState!=CURSOR_VALID ){ + if( pCur->eState>=CURSOR_REQUIRESEEK ){ + rc = btreeRestoreCursorPosition(pCur); + assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID ); + if( rc || pCur->eState!=CURSOR_VALID ) return rc; + }else{ + return SQLITE_CORRUPT_BKPT; + } } assert( pCur->eState==CURSOR_VALID ); iCellDepth = pCur->iPage; iCellIdx = pCur->ix; pPage = pCur->pPage; + if( pPage->nCell<=iCellIdx ){ + return SQLITE_CORRUPT_BKPT; + } pCell = findCell(pPage, iCellIdx); - if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ) return SQLITE_CORRUPT; + if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){ + return SQLITE_CORRUPT_BKPT; + } - /* If the bPreserve flag is set to true, then the cursor position must + /* If the BTREE_SAVEPOSITION bit is on, then the cursor position must ** be preserved following this delete operation. If the current delete ** will cause a b-tree rebalance, then this is done by saving the cursor ** key and leaving the cursor in CURSOR_REQUIRESEEK state before ** returning. ** - ** Or, if the current delete will not cause a rebalance, then the cursor + ** If the current delete will not cause a rebalance, then the cursor ** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately - ** before or after the deleted entry. In this case set bSkipnext to true. */ + ** before or after the deleted entry. + ** + ** The bPreserve value records which path is required: + ** + ** bPreserve==0 Not necessary to save the cursor position + ** bPreserve==1 Use CURSOR_REQUIRESEEK to save the cursor position + ** bPreserve==2 Cursor won't move. Set CURSOR_SKIPNEXT. + */ + bPreserve = (flags & BTREE_SAVEPOSITION)!=0; if( bPreserve ){ if( !pPage->leaf - || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3) + || (pPage->nFree+pPage->xCellSize(pPage,pCell)+2) > + (int)(pBt->usableSize*2/3) || pPage->nCell==1 /* See dbfuzz001.test for a test case */ ){ /* A b-tree rebalance will be required after deleting this entry. @@ -74165,7 +78043,7 @@ SQLITE_PRIVATE int wx_sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ rc = saveCursorKey(pCur); if( rc ) return rc; }else{ - bSkipnext = 1; + bPreserve = 2; } } @@ -74191,7 +78069,7 @@ SQLITE_PRIVATE int wx_sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ /* If this is a delete operation to remove a row from a table b-tree, ** invalidate any incrblob cursors open on the row being deleted. */ - if( pCur->pKeyInfo==0 ){ + if( pCur->pKeyInfo==0 && p->hasIncrblobCur ){ invalidateIncrblobCursors(p, pCur->pgnoRoot, pCur->info.nKey, 0); } @@ -74200,7 +78078,7 @@ SQLITE_PRIVATE int wx_sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ ** itself from within the page. */ rc = wx_sqlite3PagerWrite(pPage->pDbPage); if( rc ) return rc; - rc = clearCell(pPage, pCell, &info); + BTREE_CLEAR_CELL(rc, pPage, pCell, info); dropCell(pPage, iCellIdx, info.nSize, &rc); if( rc ) return rc; @@ -74232,7 +78110,7 @@ SQLITE_PRIVATE int wx_sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ assert( pTmp!=0 ); rc = wx_sqlite3PagerWrite(pLeaf->pDbPage); if( rc==SQLITE_OK ){ - insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); + rc = insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n); } dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc); if( rc ) return rc; @@ -74253,7 +78131,15 @@ SQLITE_PRIVATE int wx_sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ ** been corrected, so be it. Otherwise, after balancing the leaf node, ** walk the cursor up the tree to the internal node and balance it as ** well. */ - rc = balance(pCur); + assert( pCur->pPage->nOverflow==0 ); + assert( pCur->pPage->nFree>=0 ); + if( pCur->pPage->nFree*3<=(int)pCur->pBt->usableSize*2 ){ + /* Optimization: If the free space is less than 2/3rds of the page, + ** then balance() will always be a no-op. No need to invoke it. */ + rc = SQLITE_OK; + }else{ + rc = balance(pCur); + } if( rc==SQLITE_OK && pCur->iPage>iCellDepth ){ releasePageNotNull(pCur->pPage); pCur->iPage--; @@ -74265,8 +78151,8 @@ SQLITE_PRIVATE int wx_sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ } if( rc==SQLITE_OK ){ - if( bSkipnext ){ - assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) ); + if( bPreserve>1 ){ + assert( (pCur->iPage==iCellDepth || CORRUPT_DB) ); assert( pPage==pCur->pPage || CORRUPT_DB ); assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell ); pCur->eState = CURSOR_SKIPNEXT; @@ -74460,7 +78346,7 @@ static int clearDatabasePage( BtShared *pBt, /* The BTree that contains the table */ Pgno pgno, /* Page number to clear */ int freePageFlag, /* Deallocate page if true */ - int *pnChange /* Add number of Cells freed to this counter */ + i64 *pnChange /* Add number of Cells freed to this counter */ ){ MemPage *pPage; int rc; @@ -74475,11 +78361,12 @@ static int clearDatabasePage( } rc = getAndInitPage(pBt, pgno, &pPage, 0, 0); if( rc ) return rc; - if( pPage->bBusy ){ + if( (pBt->openFlags & BTREE_SINGLE)==0 + && wx_sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1)) + ){ rc = SQLITE_CORRUPT_BKPT; goto cleardatabasepage_out; } - pPage->bBusy = 1; hdr = pPage->hdrOffset; for(i=0; inCell; i++){ pCell = findCell(pPage, i); @@ -74487,14 +78374,15 @@ static int clearDatabasePage( rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange); if( rc ) goto cleardatabasepage_out; } - rc = clearCell(pPage, pCell, &info); + BTREE_CLEAR_CELL(rc, pPage, pCell, info); if( rc ) goto cleardatabasepage_out; } if( !pPage->leaf ){ rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange); if( rc ) goto cleardatabasepage_out; - }else if( pnChange ){ - assert( pPage->intKey || CORRUPT_DB ); + if( pPage->intKey ) pnChange = 0; + } + if( pnChange ){ testcase( !pPage->intKey ); *pnChange += pPage->nCell; } @@ -74505,7 +78393,6 @@ static int clearDatabasePage( } cleardatabasepage_out: - pPage->bBusy = 0; releasePage(pPage); return rc; } @@ -74519,11 +78406,10 @@ cleardatabasepage_out: ** read cursors on the table. Open write cursors are moved to the ** root of the table. ** -** If pnChange is not NULL, then table iTable must be an intkey table. The -** integer value pointed to by pnChange is incremented by the number of -** entries in the table. +** If pnChange is not NULL, then the integer value pointed to by pnChange +** is incremented by the number of entries in the table. */ -SQLITE_PRIVATE int wx_sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){ +SQLITE_PRIVATE int wx_sqlite3BtreeClearTable(Btree *p, int iTable, i64 *pnChange){ int rc; BtShared *pBt = p->pBt; wx_sqlite3BtreeEnter(p); @@ -74535,7 +78421,9 @@ SQLITE_PRIVATE int wx_sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange /* Invalidate all incrblob cursors open on table iTable (assuming iTable ** is the root of a table b-tree - if it is not, the following call is ** a no-op). */ - invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1); + if( p->hasIncrblobCur ){ + invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1); + } rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); } wx_sqlite3BtreeLeave(p); @@ -74583,10 +78471,10 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ return SQLITE_CORRUPT_BKPT; } - rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); - if( rc ) return rc; rc = wx_sqlite3BtreeClearTable(p, iTable, 0); - if( rc ){ + if( rc ) return rc; + rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); + if( NEVER(rc) ){ releasePage(pPage); return rc; } @@ -74821,6 +78709,41 @@ SQLITE_PRIVATE Pager *wx_sqlite3BtreePager(Btree *p){ } #ifndef SQLITE_OMIT_INTEGRITY_CHECK +/* +** Record an OOM error during integrity_check +*/ +static void checkOom(IntegrityCk *pCheck){ + pCheck->rc = SQLITE_NOMEM; + pCheck->mxErr = 0; /* Causes integrity_check processing to stop */ + if( pCheck->nErr==0 ) pCheck->nErr++; +} + +/* +** Invoke the progress handler, if appropriate. Also check for an +** interrupt. +*/ +static void checkProgress(IntegrityCk *pCheck){ + wx_sqlite3 *db = pCheck->db; + if( AtomicLoad(&db->u1.isInterrupted) ){ + pCheck->rc = SQLITE_INTERRUPT; + pCheck->nErr++; + pCheck->mxErr = 0; + } +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + if( db->xProgress ){ + assert( db->nProgressOps>0 ); + pCheck->nStep++; + if( (pCheck->nStep % db->nProgressOps)==0 + && db->xProgress(db->pProgressArg) + ){ + pCheck->rc = SQLITE_INTERRUPT; + pCheck->nErr++; + pCheck->mxErr = 0; + } + } +#endif +} + /* ** Append a message to the error message string. */ @@ -74830,6 +78753,7 @@ static void checkAppendMsg( ... ){ va_list ap; + checkProgress(pCheck); if( !pCheck->mxErr ) return; pCheck->mxErr--; pCheck->nErr++; @@ -74843,7 +78767,7 @@ static void checkAppendMsg( wx_sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap); va_end(ap); if( pCheck->errMsg.accError==SQLITE_NOMEM ){ - pCheck->bOomFault = 1; + checkOom(pCheck); } } #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ @@ -74885,7 +78809,6 @@ static int checkRef(IntegrityCk *pCheck, Pgno iPage){ checkAppendMsg(pCheck, "2nd reference to page %d", iPage); return 1; } - if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1; setPageReferenced(pCheck, iPage); return 0; } @@ -74908,7 +78831,7 @@ static void checkPtrmap( rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent); if( rc!=SQLITE_OK ){ - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->bOomFault = 1; + if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) checkOom(pCheck); checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild); return; } @@ -75015,7 +78938,9 @@ static void checkList( ** lower 16 bits are the index of the last byte of that range. */ static void btreeHeapInsert(u32 *aHeap, u32 x){ - u32 j, i = ++aHeap[0]; + u32 j, i; + assert( aHeap!=0 ); + i = ++aHeap[0]; aHeap[i] = x; while( (j = i/2)>0 && aHeap[j]>aHeap[i] ){ x = aHeap[j]; @@ -75092,6 +79017,8 @@ static int checkTreePage( /* Check that the page exists */ + checkProgress(pCheck); + if( pCheck->mxErr==0 ) goto end_of_check; pBt = pCheck->pBt; usableSize = pBt->usableSize; if( iPage==0 ) return 0; @@ -75337,13 +79264,14 @@ end_of_check: ** the unverified btrees. Except, if aRoot[1] is 1, then the freelist ** checks are still performed. */ -SQLITE_PRIVATE char *wx_sqlite3BtreeIntegrityCheck( +SQLITE_PRIVATE int wx_sqlite3BtreeIntegrityCheck( wx_sqlite3 *db, /* Database connection that is running the check */ Btree *p, /* The btree to be checked */ Pgno *aRoot, /* An array of root pages numbers for individual trees */ int nRoot, /* Number of entries in aRoot[] */ int mxErr, /* Stop reporting errors after this many */ - int *pnErr /* Write number of errors seen to this variable */ + int *pnErr, /* OUT: Write number of errors seen to this variable */ + char **pzOut /* OUT: Write the error message string here */ ){ Pgno i; IntegrityCk sCheck; @@ -75366,18 +79294,12 @@ SQLITE_PRIVATE char *wx_sqlite3BtreeIntegrityCheck( assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); VVA_ONLY( nRef = wx_sqlite3PagerRefcount(pBt->pPager) ); assert( nRef>=0 ); + memset(&sCheck, 0, sizeof(sCheck)); sCheck.db = db; sCheck.pBt = pBt; sCheck.pPager = pBt->pPager; sCheck.nPage = btreePagecount(sCheck.pBt); sCheck.mxErr = mxErr; - sCheck.nErr = 0; - sCheck.bOomFault = 0; - sCheck.zPfx = 0; - sCheck.v1 = 0; - sCheck.v2 = 0; - sCheck.aPgRef = 0; - sCheck.heap = 0; wx_sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL; if( sCheck.nPage==0 ){ @@ -75386,12 +79308,12 @@ SQLITE_PRIVATE char *wx_sqlite3BtreeIntegrityCheck( sCheck.aPgRef = wx_sqlite3MallocZero((sCheck.nPage / 8)+ 1); if( !sCheck.aPgRef ){ - sCheck.bOomFault = 1; + checkOom(&sCheck); goto integrity_ck_cleanup; } sCheck.heap = (u32*)wx_sqlite3PageMalloc( pBt->pageSize ); if( sCheck.heap==0 ){ - sCheck.bOomFault = 1; + checkOom(&sCheck); goto integrity_ck_cleanup; } @@ -75472,16 +79394,17 @@ SQLITE_PRIVATE char *wx_sqlite3BtreeIntegrityCheck( integrity_ck_cleanup: wx_sqlite3PageFree(sCheck.heap); wx_sqlite3_free(sCheck.aPgRef); - if( sCheck.bOomFault ){ + *pnErr = sCheck.nErr; + if( sCheck.nErr==0 ){ wx_sqlite3_str_reset(&sCheck.errMsg); - sCheck.nErr++; + *pzOut = 0; + }else{ + *pzOut = wx_sqlite3StrAccumFinish(&sCheck.errMsg); } - *pnErr = sCheck.nErr; - if( sCheck.nErr==0 ) wx_sqlite3_str_reset(&sCheck.errMsg); /* Make sure this analysis did not leave any unref() pages. */ assert( nRef==wx_sqlite3PagerRefcount(pBt->pPager) ); wx_sqlite3BtreeLeave(p); - return wx_sqlite3StrAccumFinish(&sCheck.errMsg); + return sCheck.rc; } #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ @@ -75746,6 +79669,17 @@ SQLITE_PRIVATE int wx_sqlite3BtreeIsReadonly(Btree *p){ */ SQLITE_PRIVATE int wx_sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } +/* +** If no transaction is active and the database is not a temp-db, clear +** the in-memory pager cache. +*/ +SQLITE_PRIVATE void wx_sqlite3BtreeClearCache(Btree *p){ + BtShared *pBt = p->pBt; + if( pBt->inTransaction==TRANS_NONE ){ + wx_sqlite3PagerClearCache(pBt->pPager); + } +} + #if !defined(SQLITE_OMIT_SHARED_CACHE) /* ** Return true if the Btree passed as the only argument is sharable. @@ -75854,14 +79788,13 @@ static Btree *findBtree(wx_sqlite3 *pErrorDb, wx_sqlite3 *pDb, const char *zDb){ if( i==1 ){ Parse sParse; int rc = 0; - memset(&sParse, 0, sizeof(sParse)); - sParse.db = pDb; + wx_sqlite3ParseObjectInit(&sParse,pDb); if( wx_sqlite3OpenTempDatabase(&sParse) ){ wx_sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg); rc = SQLITE_ERROR; } wx_sqlite3DbFree(pErrorDb, sParse.zErrMsg); - wx_sqlite3ParserReset(&sParse); + wx_sqlite3ParseObjectReset(&sParse); if( rc ){ return 0; } @@ -76618,7 +80551,9 @@ SQLITE_PRIVATE int wx_sqlite3VdbeCheckMemInvariants(Mem *p){ /* The szMalloc field holds the correct memory allocation size */ assert( p->szMalloc==0 - || p->szMalloc==wx_sqlite3DbMallocSize(p->db,p->zMalloc) ); + || (p->flags==MEM_Undefined + && p->szMalloc<=wx_sqlite3DbMallocSize(p->db,p->zMalloc)) + || p->szMalloc==wx_sqlite3DbMallocSize(p->db,p->zMalloc)); /* If p holds a string or blob, the Mem.z must point to exactly ** one of the following: @@ -76655,9 +80590,9 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ i64 x; assert( (p->flags&MEM_Int)*2==sizeof(x) ); memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2); - wx_sqlite3Int64ToText(x, zBuf); + p->n = wx_sqlite3Int64ToText(x, zBuf); #else - wx_sqlite3Int64ToText(p->u.i, zBuf); + p->n = wx_sqlite3Int64ToText(p->u.i, zBuf); #endif }else{ wx_sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); @@ -76665,6 +80600,7 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r); assert( acc.zText==zBuf && acc.mxAlloc<=0 ); zBuf[acc.nChar] = 0; /* Fast version of wx_sqlite3StrAccumFinish(&acc) */ + p->n = acc.nChar; } } @@ -76692,6 +80628,7 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ ** This routine is for use inside of assert() statements only. */ SQLITE_PRIVATE int wx_sqlite3VdbeMemValidStrRep(Mem *p){ + Mem tmp; char zBuf[100]; char *z; int i, j, incr; @@ -76708,7 +80645,8 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemValidStrRep(Mem *p){ assert( p->enc==SQLITE_UTF8 || p->z[((p->n+1)&~1)+1]==0 ); } if( (p->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ) return 1; - vdbeMemRenderNum(sizeof(zBuf), zBuf, p); + memcpy(&tmp, p, sizeof(tmp)); + vdbeMemRenderNum(sizeof(zBuf), zBuf, &tmp); z = p->z; i = j = 0; incr = 1; @@ -76741,10 +80679,15 @@ SQLITE_PRIVATE int wx_sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ #ifndef SQLITE_OMIT_UTF16 int rc; #endif + assert( pMem!=0 ); assert( !wx_sqlite3VdbeMemIsRowSet(pMem) ); assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE || desiredEnc==SQLITE_UTF16BE ); - if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){ + if( !(pMem->flags&MEM_Str) ){ + pMem->enc = desiredEnc; + return SQLITE_OK; + } + if( pMem->enc==desiredEnc ){ return SQLITE_OK; } assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); @@ -76782,7 +80725,9 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3VdbeMemGrow(Mem *pMem, int n, int b testcase( bPreserve && pMem->z==0 ); assert( pMem->szMalloc==0 - || pMem->szMalloc==wx_sqlite3DbMallocSize(pMem->db, pMem->zMalloc) ); + || (pMem->flags==MEM_Undefined + && pMem->szMalloc<=wx_sqlite3DbMallocSize(pMem->db,pMem->zMalloc)) + || pMem->szMalloc==wx_sqlite3DbMallocSize(pMem->db,pMem->zMalloc)); if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){ if( pMem->db ){ pMem->z = pMem->zMalloc = wx_sqlite3DbReallocOrFree(pMem->db, pMem->z, n); @@ -76871,6 +80816,7 @@ static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){ ** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. */ SQLITE_PRIVATE int wx_sqlite3VdbeMemMakeWriteable(Mem *pMem){ + assert( pMem!=0 ); assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); assert( !wx_sqlite3VdbeMemIsRowSet(pMem) ); if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){ @@ -76895,6 +80841,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemMakeWriteable(Mem *pMem){ #ifndef SQLITE_OMIT_INCRBLOB SQLITE_PRIVATE int wx_sqlite3VdbeMemExpandBlob(Mem *pMem){ int nByte; + assert( pMem!=0 ); assert( pMem->flags & MEM_Zero ); assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) ); testcase( wx_sqlite3_value_nochange(pMem) ); @@ -76910,6 +80857,8 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemExpandBlob(Mem *pMem){ if( wx_sqlite3VdbeMemGrow(pMem, nByte, 1) ){ return SQLITE_NOMEM_BKPT; } + assert( pMem->z!=0 ); + assert( wx_sqlite3DbMallocSize(pMem->db,pMem->z) >= nByte ); memset(&pMem->z[pMem->n], 0, pMem->u.nZero); pMem->n += pMem->u.nZero; @@ -76922,6 +80871,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemExpandBlob(Mem *pMem){ ** Make sure the given Mem is \u0000 terminated. */ SQLITE_PRIVATE int wx_sqlite3VdbeMemNulTerminate(Mem *pMem){ + assert( pMem!=0 ); assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); testcase( (pMem->flags & (MEM_Term|MEM_Str))==(MEM_Term|MEM_Str) ); testcase( (pMem->flags & (MEM_Term|MEM_Str))==0 ); @@ -76949,6 +80899,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemNulTerminate(Mem *pMem){ SQLITE_PRIVATE int wx_sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ const int nByte = 32; + assert( pMem!=0 ); assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); assert( !(pMem->flags&MEM_Zero) ); assert( !(pMem->flags&(MEM_Str|MEM_Blob)) ); @@ -76964,7 +80915,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ vdbeMemRenderNum(nByte, pMem->z, pMem); assert( pMem->z!=0 ); - pMem->n = wx_sqlite3Strlen30NN(pMem->z); + assert( pMem->n==wx_sqlite3Strlen30NN(pMem->z) ); pMem->enc = SQLITE_UTF8; pMem->flags |= MEM_Str|MEM_Term; if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); @@ -76984,9 +80935,11 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){ wx_sqlite3_context ctx; Mem t; assert( pFunc!=0 ); + assert( pMem!=0 ); + assert( pMem->db!=0 ); assert( pFunc->xFinalize!=0 ); assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef ); - assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); + assert( wx_sqlite3_mutex_held(pMem->db->mutex) ); memset(&ctx, 0, sizeof(ctx)); memset(&t, 0, sizeof(t)); t.flags = MEM_Null; @@ -76994,6 +80947,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){ ctx.pOut = &t; ctx.pMem = pMem; ctx.pFunc = pFunc; + ctx.enc = ENC(t.db); pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */ assert( (pMem->flags & MEM_Dyn)==0 ); if( pMem->szMalloc>0 ) wx_sqlite3DbFreeNN(pMem->db, pMem->zMalloc); @@ -77015,12 +80969,14 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pF assert( pFunc!=0 ); assert( pFunc->xValue!=0 ); assert( (pAccum->flags & MEM_Null)!=0 || pFunc==pAccum->u.pDef ); - assert( pAccum->db==0 || wx_sqlite3_mutex_held(pAccum->db->mutex) ); + assert( pAccum->db!=0 ); + assert( wx_sqlite3_mutex_held(pAccum->db->mutex) ); memset(&ctx, 0, sizeof(ctx)); wx_sqlite3VdbeMemSetNull(pOut); ctx.pOut = pOut; ctx.pMem = pAccum; ctx.pFunc = pFunc; + ctx.enc = ENC(pAccum->db); pFunc->xValue(&ctx); return ctx.isError; } @@ -77086,6 +81042,14 @@ SQLITE_PRIVATE void wx_sqlite3VdbeMemRelease(Mem *p){ } } +/* Like wx_sqlite3VdbeMemRelease() but faster for cases where we +** know in advance that the Mem is not MEM_Dyn or MEM_Agg. +*/ +SQLITE_PRIVATE void wx_sqlite3VdbeMemReleaseMalloc(Mem *p){ + assert( !VdbeMemDynamic(p) ); + if( p->szMalloc ) vdbeMemClear(p); +} + /* ** Convert a 64-bit IEEE double into a 64-bit signed integer. ** If the double is out of range of a 64-bit signed integer then @@ -77127,13 +81091,14 @@ static SQLITE_NOINLINE i64 doubleToInt64(double r){ ** ** If pMem represents a string value, its encoding might be changed. */ -static SQLITE_NOINLINE i64 memIntValue(Mem *pMem){ +static SQLITE_NOINLINE i64 memIntValue(const Mem *pMem){ i64 value = 0; wx_sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); return value; } -SQLITE_PRIVATE i64 wx_sqlite3VdbeIntValue(Mem *pMem){ +SQLITE_PRIVATE i64 wx_sqlite3VdbeIntValue(const Mem *pMem){ int flags; + assert( pMem!=0 ); assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); flags = pMem->flags; @@ -77162,6 +81127,7 @@ static SQLITE_NOINLINE double memRealValue(Mem *pMem){ return val; } SQLITE_PRIVATE double wx_sqlite3VdbeRealValue(Mem *pMem){ + assert( pMem!=0 ); assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); if( pMem->flags & MEM_Real ){ @@ -77189,31 +81155,35 @@ SQLITE_PRIVATE int wx_sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){ } /* -** The MEM structure is already a MEM_Real. Try to also make it a -** MEM_Int if we can. +** The MEM structure is already a MEM_Real or MEM_IntReal. Try to +** make it a MEM_Int if we can. */ SQLITE_PRIVATE void wx_sqlite3VdbeIntegerAffinity(Mem *pMem){ - i64 ix; - assert( pMem->flags & MEM_Real ); + assert( pMem!=0 ); + assert( pMem->flags & (MEM_Real|MEM_IntReal) ); assert( !wx_sqlite3VdbeMemIsRowSet(pMem) ); assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - ix = doubleToInt64(pMem->u.r); - - /* Only mark the value as an integer if - ** - ** (1) the round-trip conversion real->int->real is a no-op, and - ** (2) The integer is neither the largest nor the smallest - ** possible integer (ticket #3922) - ** - ** The second and third terms in the following conditional enforces - ** the second condition under the assumption that addition overflow causes - ** values to wrap around. - */ - if( pMem->u.r==ix && ix>SMALLEST_INT64 && ixu.i = ix; + if( pMem->flags & MEM_IntReal ){ MemSetTypeFlag(pMem, MEM_Int); + }else{ + i64 ix = doubleToInt64(pMem->u.r); + + /* Only mark the value as an integer if + ** + ** (1) the round-trip conversion real->int->real is a no-op, and + ** (2) The integer is neither the largest nor the smallest + ** possible integer (ticket #3922) + ** + ** The second and third terms in the following conditional enforces + ** the second condition under the assumption that addition overflow causes + ** values to wrap around. + */ + if( pMem->u.r==ix && ix>SMALLEST_INT64 && ixu.i = ix; + MemSetTypeFlag(pMem, MEM_Int); + } } } @@ -77221,6 +81191,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeIntegerAffinity(Mem *pMem){ ** Convert pMem to type integer. Invalidate any prior representations. */ SQLITE_PRIVATE int wx_sqlite3VdbeMemIntegerify(Mem *pMem){ + assert( pMem!=0 ); assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); assert( !wx_sqlite3VdbeMemIsRowSet(pMem) ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); @@ -77235,6 +81206,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemIntegerify(Mem *pMem){ ** Invalidate any prior representations. */ SQLITE_PRIVATE int wx_sqlite3VdbeMemRealify(Mem *pMem){ + assert( pMem!=0 ); assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); @@ -77259,6 +81231,16 @@ SQLITE_PRIVATE int wx_sqlite3RealSameAsInt(double r1, wx_sqlite3_int64 i){ && i >= -2251799813685248LL && i < 2251799813685248LL); } +/* Convert a floating point value to its closest integer. Do so in +** a way that avoids 'outside the range of representable values' warnings +** from UBSAN. +*/ +SQLITE_PRIVATE i64 wx_sqlite3RealToI64(double r){ + if( r<=(double)SMALLEST_INT64 ) return SMALLEST_INT64; + if( r>=(double)LARGEST_INT64) return LARGEST_INT64; + return (i64)r; +} + /* ** Convert pMem so that it has type MEM_Real or MEM_Int. ** Invalidate any prior representations. @@ -77268,6 +81250,7 @@ SQLITE_PRIVATE int wx_sqlite3RealSameAsInt(double r1, wx_sqlite3_int64 i){ ** as much of the string as we can and ignore the rest. */ SQLITE_PRIVATE int wx_sqlite3VdbeMemNumerify(Mem *pMem){ + assert( pMem!=0 ); testcase( pMem->flags & MEM_Int ); testcase( pMem->flags & MEM_Real ); testcase( pMem->flags & MEM_IntReal ); @@ -77279,7 +81262,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemNumerify(Mem *pMem){ assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); rc = wx_sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); if( ((rc==0 || rc==1) && wx_sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1) - || wx_sqlite3RealSameAsInt(pMem->u.r, (ix = (i64)pMem->u.r)) + || wx_sqlite3RealSameAsInt(pMem->u.r, (ix = wx_sqlite3RealToI64(pMem->u.r))) ){ pMem->u.i = ix; MemSetTypeFlag(pMem, MEM_Int); @@ -77331,6 +81314,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){ wx_sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding); assert( pMem->flags & MEM_Str || pMem->db->mallocFailed ); pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal|MEM_Blob|MEM_Zero); + if( encoding!=SQLITE_UTF8 ) pMem->n &= ~1; return wx_sqlite3VdbeChangeEncoding(pMem, encoding); } } @@ -77377,6 +81361,7 @@ SQLITE_PRIVATE void wx_sqlite3ValueSetNull(wx_sqlite3_value *p){ ** Delete any previous value and set the value to be a BLOB of length ** n containing all zeros. */ +#ifndef SQLITE_OMIT_INCRBLOB SQLITE_PRIVATE void wx_sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ wx_sqlite3VdbeMemRelease(pMem); pMem->flags = MEM_Blob|MEM_Zero; @@ -77386,6 +81371,21 @@ SQLITE_PRIVATE void wx_sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ pMem->enc = SQLITE_UTF8; pMem->z = 0; } +#else +SQLITE_PRIVATE int wx_sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ + int nByte = n>0?n:1; + if( wx_sqlite3VdbeMemGrow(pMem, nByte, 0) ){ + return SQLITE_NOMEM_BKPT; + } + assert( pMem->z!=0 ); + assert( wx_sqlite3DbMallocSize(pMem->db, pMem->z)>=nByte ); + memset(pMem->z, 0, nByte); + pMem->n = n>0?n:0; + pMem->flags = MEM_Blob; + pMem->enc = SQLITE_UTF8; + return SQLITE_OK; +} +#endif /* ** The pMem is known to contain content that needs to be destroyed prior @@ -77425,6 +81425,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeMemSetPointer( void (*xDestructor)(void*) ){ assert( pMem->flags==MEM_Null ); + vdbeMemClear(pMem); pMem->u.zPType = zPType ? zPType : ""; pMem->z = pPtr; pMem->flags = MEM_Null|MEM_Dyn|MEM_Subtype|MEM_Term; @@ -77607,20 +81608,29 @@ SQLITE_PRIVATE void wx_sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){ ** stored without allocating memory, then it is. If a memory allocation ** is required to store the string, then value of pMem is unchanged. In ** either case, SQLITE_TOOBIG is returned. +** +** The "enc" parameter is the text encoding for the string, or zero +** to store a blob. +** +** If n is negative, then the string consists of all bytes up to but +** excluding the first zero character. The n parameter must be +** non-negative for blobs. */ SQLITE_PRIVATE int wx_sqlite3VdbeMemSetStr( Mem *pMem, /* Memory cell to set to string value */ const char *z, /* String pointer */ - int n, /* Bytes in string, or negative */ + i64 n, /* Bytes in string, or negative */ u8 enc, /* Encoding of z. 0 for BLOBs */ void (*xDel)(void*) /* Destructor function */ ){ - int nByte = n; /* New value for pMem->n */ + i64 nByte = n; /* New value for pMem->n */ int iLimit; /* Maximum allowed string or blob size */ - u16 flags = 0; /* New value for pMem->flags */ + u16 flags; /* New value for pMem->flags */ + assert( pMem!=0 ); assert( pMem->db==0 || wx_sqlite3_mutex_held(pMem->db->mutex) ); assert( !wx_sqlite3VdbeMemIsRowSet(pMem) ); + assert( enc!=0 || n>=0 ); /* If z is a NULL pointer, set pMem to contain an SQL NULL. */ if( !z ){ @@ -77633,15 +81643,30 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemSetStr( }else{ iLimit = SQLITE_MAX_LENGTH; } - flags = (enc==0?MEM_Blob:MEM_Str); if( nByte<0 ){ assert( enc!=0 ); if( enc==SQLITE_UTF8 ){ - nByte = 0x7fffffff & (int)strlen(z); + nByte = strlen(z); }else{ for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} } - flags |= MEM_Term; + flags= MEM_Str|MEM_Term; + }else if( enc==0 ){ + flags = MEM_Blob; + enc = SQLITE_UTF8; + }else{ + flags = MEM_Str; + } + if( nByte>iLimit ){ + if( xDel && xDel!=SQLITE_TRANSIENT ){ + if( xDel==SQLITE_DYNAMIC ){ + wx_sqlite3DbFree(pMem->db, (void*)z); + }else{ + xDel((void*)z); + } + } + wx_sqlite3VdbeMemSetNull(pMem); + return wx_sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); } /* The following block sets the new values of Mem.z and Mem.xDel. It @@ -77649,13 +81674,10 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemSetStr( ** management (one of MEM_Dyn or MEM_Static). */ if( xDel==SQLITE_TRANSIENT ){ - u32 nAlloc = nByte; + i64 nAlloc = nByte; if( flags&MEM_Term ){ nAlloc += (enc==SQLITE_UTF8?1:2); } - if( nByte>iLimit ){ - return wx_sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); - } testcase( nAlloc==0 ); testcase( nAlloc==31 ); testcase( nAlloc==32 ); @@ -77675,18 +81697,9 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemSetStr( } } - pMem->n = nByte; + pMem->n = (int)(nByte & 0x7fffffff); pMem->flags = flags; - if( enc ){ - pMem->enc = enc; -#ifdef SQLITE_ENABLE_SESSION - }else if( pMem->db==0 ){ - pMem->enc = SQLITE_UTF8; -#endif - }else{ - assert( pMem->db!=0 ); - pMem->enc = ENC(pMem->db); - } + pMem->enc = enc; #ifndef SQLITE_OMIT_UTF16 if( enc>SQLITE_UTF8 && wx_sqlite3VdbeMemHandleBom(pMem) ){ @@ -77694,9 +81707,6 @@ SQLITE_PRIVATE int wx_sqlite3VdbeMemSetStr( } #endif - if( nByte>iLimit ){ - return SQLITE_TOOBIG; - } return SQLITE_OK; } @@ -77927,7 +81937,7 @@ static wx_sqlite3_value *valueNew(wx_sqlite3 *db, struct ValueNewStat4Ctx *p){ #ifdef SQLITE_ENABLE_STAT4 static int valueFromFunction( wx_sqlite3 *db, /* The database connection */ - Expr *p, /* The expression to evaluate */ + const Expr *p, /* The expression to evaluate */ u8 enc, /* Encoding to use */ u8 aff, /* Affinity to use */ wx_sqlite3_value **ppVal, /* Write the new value here */ @@ -77944,8 +81954,10 @@ static int valueFromFunction( assert( pCtx!=0 ); assert( (p->flags & EP_TokenOnly)==0 ); + assert( ExprUseXList(p) ); pList = p->x.pList; if( pList ) nVal = pList->nExpr; + assert( !ExprHasProperty(p, EP_IntValue) ); pFunc = wx_sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0); assert( pFunc ); if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 @@ -77972,10 +81984,10 @@ static int valueFromFunction( goto value_from_function_out; } - assert( pCtx->pParse->rc==SQLITE_OK ); memset(&ctx, 0, sizeof(ctx)); ctx.pOut = pVal; ctx.pFunc = pFunc; + ctx.enc = ENC(db); pFunc->xSFunc(&ctx, nVal, apVal); if( ctx.isError ){ rc = ctx.isError; @@ -77983,17 +81995,22 @@ static int valueFromFunction( }else{ wx_sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8); assert( rc==SQLITE_OK ); + assert( enc==pVal->enc + || (pVal->flags & MEM_Str)==0 + || db->mallocFailed ); +#if 0 /* Not reachable except after a prior failure */ rc = wx_sqlite3VdbeChangeEncoding(pVal, enc); if( rc==SQLITE_OK && wx_sqlite3VdbeMemTooBig(pVal) ){ rc = SQLITE_TOOBIG; pCtx->pParse->nErr++; } +#endif } - pCtx->pParse->rc = rc; value_from_function_out: if( rc!=SQLITE_OK ){ pVal = 0; + pCtx->pParse->rc = rc; } if( apVal ){ for(i=0; iop)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft; -#if defined(SQLITE_ENABLE_STAT4) if( op==TK_REGISTER ) op = pExpr->op2; -#else - if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; -#endif /* Compressed expressions only appear when parsing the DEFAULT clause ** on a table column definition, and hence only when pCtx==0. This @@ -78049,12 +82062,14 @@ static int valueFromExpr( assert( (pExpr->flags & EP_TokenOnly)==0 || pCtx==0 ); if( op==TK_CAST ){ - u8 aff = wx_sqlite3AffinityType(pExpr->u.zToken,0); + u8 aff; + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + aff = wx_sqlite3AffinityType(pExpr->u.zToken,0); rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx); testcase( rc!=SQLITE_OK ); if( *ppVal ){ - wx_sqlite3VdbeMemCast(*ppVal, aff, SQLITE_UTF8); - wx_sqlite3ValueApplyAffinity(*ppVal, affinity, SQLITE_UTF8); + wx_sqlite3VdbeMemCast(*ppVal, aff, enc); + wx_sqlite3ValueApplyAffinity(*ppVal, affinity, enc); } return rc; } @@ -78122,6 +82137,7 @@ static int valueFromExpr( #ifndef SQLITE_OMIT_BLOB_LITERAL else if( op==TK_BLOB ){ int nVal; + assert( !ExprHasProperty(pExpr, EP_IntValue) ); assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); assert( pExpr->u.zToken[1]=='\'' ); pVal = valueNew(db, pCtx); @@ -78139,6 +82155,7 @@ static int valueFromExpr( } #endif else if( op==TK_TRUEFALSE ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); pVal = valueNew(db, pCtx); if( pVal ){ pVal->flags = MEM_Int; @@ -78151,7 +82168,7 @@ static int valueFromExpr( no_mem: #ifdef SQLITE_ENABLE_STAT4 - if( pCtx==0 || pCtx->pParse->nErr==0 ) + if( pCtx==0 || NEVER(pCtx->pParse->nErr==0) ) #endif wx_sqlite3OomFault(db); wx_sqlite3DbFree(db, zVal); @@ -78176,7 +82193,7 @@ no_mem: */ SQLITE_PRIVATE int wx_sqlite3ValueFromExpr( wx_sqlite3 *db, /* The database connection */ - Expr *pExpr, /* The expression to evaluate */ + const Expr *pExpr, /* The expression to evaluate */ u8 enc, /* Encoding to use */ u8 affinity, /* Affinity to use */ wx_sqlite3_value **ppVal /* Write the new value here */ @@ -78436,6 +82453,9 @@ SQLITE_PRIVATE int wx_sqlite3ValueBytes(wx_sqlite3_value *pVal, u8 enc){ if( (p->flags & MEM_Str)!=0 && pVal->enc==enc ){ return p->n; } + if( (p->flags & MEM_Str)!=0 && enc!=SQLITE_UTF8 && pVal->enc!=SQLITE_UTF8 ){ + return p->n; + } if( (p->flags & MEM_Blob)!=0 ){ if( p->flags & MEM_Zero ){ return p->n + p->u.nZero; @@ -78481,12 +82501,12 @@ SQLITE_PRIVATE Vdbe *wx_sqlite3VdbeCreate(Parse *pParse){ memset(&p->aOp, 0, sizeof(Vdbe)-offsetof(Vdbe,aOp)); p->db = db; if( db->pVdbe ){ - db->pVdbe->pPrev = p; + db->pVdbe->ppVPrev = &p->pVNext; } - p->pNext = db->pVdbe; - p->pPrev = 0; + p->pVNext = db->pVdbe; + p->ppVPrev = &db->pVdbe; db->pVdbe = p; - p->iVdbeMagic = VDBE_MAGIC_INIT; + assert( p->eVdbeState==VDBE_INIT_STATE ); p->pParse = pParse; pParse->pVdbe = p; assert( pParse->aLabel==0 ); @@ -78566,21 +82586,28 @@ SQLITE_PRIVATE int wx_sqlite3VdbeUsesDoubleQuotedString( #endif /* -** Swap all content between two VDBE structures. +** Swap byte-code between two VDBE structures. +** +** This happens after pB was previously run and returned +** SQLITE_SCHEMA. The statement was then reprepared in pA. +** This routine transfers the new bytecode in pA over to pB +** so that pB can be run again. The old pB byte code is +** moved back to pA so that it will be cleaned up when pA is +** finalized. */ SQLITE_PRIVATE void wx_sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ - Vdbe tmp, *pTmp; + Vdbe tmp, *pTmp, **ppTmp; char *zTmp; assert( pA->db==pB->db ); tmp = *pA; *pA = *pB; *pB = tmp; - pTmp = pA->pNext; - pA->pNext = pB->pNext; - pB->pNext = pTmp; - pTmp = pA->pPrev; - pA->pPrev = pB->pPrev; - pB->pPrev = pTmp; + pTmp = pA->pVNext; + pA->pVNext = pB->pVNext; + pB->pVNext = pTmp; + ppTmp = pA->ppVPrev; + pA->ppVPrev = pB->ppVPrev; + pB->ppVPrev = ppTmp; zTmp = pA->zSql; pA->zSql = pB->zSql; pB->zSql = zTmp; @@ -78631,7 +82658,7 @@ static int growOpArray(Vdbe *v, int nOp){ return SQLITE_NOMEM; } - assert( nOp<=(1024/sizeof(Op)) ); + assert( nOp<=(int)(1024/sizeof(Op)) ); assert( nNew>=(v->nOpAlloc+nOp) ); pNew = wx_sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op)); if( pNew ){ @@ -78656,6 +82683,8 @@ static int growOpArray(Vdbe *v, int nOp){ */ static void test_addop_breakpoint(int pc, Op *pOp){ static int n = 0; + (void)pc; + (void)pOp; n++; } #endif @@ -78687,13 +82716,15 @@ SQLITE_PRIVATE int wx_sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3) VdbeOp *pOp; i = p->nOp; - assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); + assert( p->eVdbeState==VDBE_INIT_STATE ); assert( op>=0 && op<0xff ); if( p->nOpAlloc<=i ){ return growOp3(p, op, p1, p2, p3); } + assert( p->aOp!=0 ); p->nOp++; pOp = &p->aOp[i]; + assert( pOp!=0 ); pOp->opcode = (u8)op; pOp->p5 = 0; pOp->p1 = p1; @@ -78704,16 +82735,16 @@ SQLITE_PRIVATE int wx_sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3) #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS pOp->zComment = 0; #endif +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) + pOp->nExec = 0; + pOp->nCycle = 0; +#endif #ifdef SQLITE_DEBUG if( p->db->flags & SQLITE_VdbeAddopTrace ){ wx_sqlite3VdbePrintOp(0, i, &p->aOp[i]); test_addop_breakpoint(i, &p->aOp[i]); } #endif -#ifdef VDBE_PROFILE - pOp->cycles = 0; - pOp->cnt = 0; -#endif #ifdef SQLITE_VDBE_COVERAGE pOp->iSrcLine = 0; #endif @@ -78830,6 +82861,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeAddFunctionCall( addr = wx_sqlite3VdbeAddOp4(v, eCallCtx ? OP_PureFunc : OP_Function, p1, p2, p3, (char*)pCtx, P4_FUNCCTX); wx_sqlite3VdbeChangeP5(v, eCallCtx & NC_SelfRef); + wx_sqlite3MayAbort(pParse); return addr; } @@ -78880,8 +82912,9 @@ SQLITE_PRIVATE void wx_sqlite3ExplainBreakpoint(const char *z1, const char *z2){ ** If the bPush flag is true, then make this opcode the parent for ** subsequent Explains until wx_sqlite3VdbeExplainPop() is called. */ -SQLITE_PRIVATE void wx_sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ -#ifndef SQLITE_DEBUG +SQLITE_PRIVATE int wx_sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ + int addr = 0; +#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS) /* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined. ** But omit them (for performance) during production builds */ if( pParse->explain==2 ) @@ -78896,13 +82929,15 @@ SQLITE_PRIVATE void wx_sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *z va_end(ap); v = pParse->pVdbe; iThis = v->nOp; - wx_sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, + addr = wx_sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, zMsg, P4_DYNAMIC); - wx_sqlite3ExplainBreakpoint(bPush?"PUSH":"", wx_sqlite3VdbeGetOp(v,-1)->p4.z); + wx_sqlite3ExplainBreakpoint(bPush?"PUSH":"", wx_sqlite3VdbeGetLastOp(v)->p4.z); if( bPush){ pParse->addrExplain = iThis; } + wx_sqlite3VdbeScanStatus(v, iThis, 0, 0, 0, 0); } + return addr; } /* @@ -79010,6 +83045,9 @@ static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){ int i; for(i=p->nLabelAlloc; iaLabel[i] = -1; #endif + if( nNewSize>=100 && (nNewSize/100)>(p->nLabelAlloc/100) ){ + wx_sqlite3ProgressCheck(p); + } p->nLabelAlloc = nNewSize; p->aLabel[j] = v->nOp; } @@ -79017,7 +83055,7 @@ static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){ SQLITE_PRIVATE void wx_sqlite3VdbeResolveLabel(Vdbe *v, int x){ Parse *p = v->pParse; int j = ADDR(x); - assert( v->iVdbeMagic==VDBE_MAGIC_INIT ); + assert( v->eVdbeState==VDBE_INIT_STATE ); assert( j<-p->nLabel ); assert( j>=0 ); #ifdef SQLITE_DEBUG @@ -79037,14 +83075,20 @@ SQLITE_PRIVATE void wx_sqlite3VdbeResolveLabel(Vdbe *v, int x){ ** Mark the VDBE as one that can only be run one time. */ SQLITE_PRIVATE void wx_sqlite3VdbeRunOnlyOnce(Vdbe *p){ - p->runOnlyOnce = 1; + wx_sqlite3VdbeAddOp2(p, OP_Expire, 1, 1); } /* -** Mark the VDBE as one that can only be run multiple times. +** Mark the VDBE as one that can be run multiple times. */ SQLITE_PRIVATE void wx_sqlite3VdbeReusable(Vdbe *p){ - p->runOnlyOnce = 0; + int i; + for(i=1; ALWAYS(inOp); i++){ + if( ALWAYS(p->aOp[i].opcode==OP_Expire) ){ + p->aOp[1].opcode = OP_Noop; + break; + } + } } #ifdef SQLITE_DEBUG /* wx_sqlite3AssertMayAbort() logic */ @@ -79148,6 +83192,8 @@ SQLITE_PRIVATE int wx_sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ int hasInitCoroutine = 0; Op *pOp; VdbeOpIter sIter; + + if( v==0 ) return 0; memset(&sIter, 0, sizeof(sIter)); sIter.v = v; @@ -79157,6 +83203,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ || opcode==OP_VDestroy || opcode==OP_VCreate || opcode==OP_ParseSchema + || opcode==OP_Function || opcode==OP_PureFunc || ((opcode==OP_Halt || opcode==OP_HaltIfNull) && ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort)) ){ @@ -79231,7 +83278,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeAssertAbortable(Vdbe *p){ ** (3) Update the Vdbe.readOnly and Vdbe.bIsReader flags to accurately ** indicate what the prepared statement actually does. ** -** (4) Initialize the p4.xAdvance pointer on opcodes that use it. +** (4) (discontinued) ** ** (5) Reclaim the memory allocated for storing labels. ** @@ -79247,8 +83294,8 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ p->readOnly = 1; p->bIsReader = 0; pOp = &p->aOp[p->nOp-1]; - while(1){ - + assert( p->aOp[0].opcode==OP_Init ); + while( 1 /* Loop termates when it reaches the OP_Init opcode */ ){ /* Only JUMP opcodes and the short list of special opcodes in the switch ** below need to be considered. The mkopcodeh.tcl generator script groups ** all these opcodes together near the front of the opcode list. Skip @@ -79277,24 +83324,9 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ p->bIsReader = 1; break; } - case OP_Next: - case OP_SorterNext: { - pOp->p4.xAdvance = wx_sqlite3BtreeNext; - pOp->p4type = P4_ADVANCE; - /* The code generator never codes any of these opcodes as a jump - ** to a label. They are always coded as a jump backwards to a - ** known address */ - assert( pOp->p2>=0 ); - break; - } - case OP_Prev: { - pOp->p4.xAdvance = wx_sqlite3BtreePrevious; - pOp->p4type = P4_ADVANCE; - /* The code generator never codes any of these opcodes as a jump - ** to a label. They are always coded as a jump backwards to a - ** known address */ + case OP_Init: { assert( pOp->p2>=0 ); - break; + goto resolve_p2_values_loop_exit; } #ifndef SQLITE_OMIT_VIRTUALTABLE case OP_VUpdate: { @@ -79328,21 +83360,108 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ ** have non-negative values for P2. */ assert( (wx_sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0); } - if( pOp==p->aOp ) break; + assert( pOp>p->aOp ); pOp--; } - wx_sqlite3DbFree(p->db, pParse->aLabel); - pParse->aLabel = 0; +resolve_p2_values_loop_exit: + if( aLabel ){ + wx_sqlite3DbNNFreeNN(p->db, pParse->aLabel); + pParse->aLabel = 0; + } pParse->nLabel = 0; *pMaxFuncArgs = nMaxArgs; assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) ); } +#ifdef SQLITE_DEBUG +/* +** Check to see if a subroutine contains a jump to a location outside of +** the subroutine. If a jump outside the subroutine is detected, add code +** that will cause the program to halt with an error message. +** +** The subroutine consists of opcodes between iFirst and iLast. Jumps to +** locations within the subroutine are acceptable. iRetReg is a register +** that contains the return address. Jumps to outside the range of iFirst +** through iLast are also acceptable as long as the jump destination is +** an OP_Return to iReturnAddr. +** +** A jump to an unresolved label means that the jump destination will be +** beyond the current address. That is normally a jump to an early +** termination and is consider acceptable. +** +** This routine only runs during debug builds. The purpose is (of course) +** to detect invalid escapes out of a subroutine. The OP_Halt opcode +** is generated rather than an assert() or other error, so that ".eqp full" +** will still work to show the original bytecode, to aid in debugging. +*/ +SQLITE_PRIVATE void wx_sqlite3VdbeNoJumpsOutsideSubrtn( + Vdbe *v, /* The byte-code program under construction */ + int iFirst, /* First opcode of the subroutine */ + int iLast, /* Last opcode of the subroutine */ + int iRetReg /* Subroutine return address register */ +){ + VdbeOp *pOp; + Parse *pParse; + int i; + wx_sqlite3_str *pErr = 0; + assert( v!=0 ); + pParse = v->pParse; + assert( pParse!=0 ); + if( pParse->nErr ) return; + assert( iLast>=iFirst ); + assert( iLastnOp ); + pOp = &v->aOp[iFirst]; + for(i=iFirst; i<=iLast; i++, pOp++){ + if( (wx_sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ){ + int iDest = pOp->p2; /* Jump destination */ + if( iDest==0 ) continue; + if( pOp->opcode==OP_Gosub ) continue; + if( iDest<0 ){ + int j = ADDR(iDest); + assert( j>=0 ); + if( j>=-pParse->nLabel || pParse->aLabel[j]<0 ){ + continue; + } + iDest = pParse->aLabel[j]; + } + if( iDestiLast ){ + int j = iDest; + for(; jnOp; j++){ + VdbeOp *pX = &v->aOp[j]; + if( pX->opcode==OP_Return ){ + if( pX->p1==iRetReg ) break; + continue; + } + if( pX->opcode==OP_Noop ) continue; + if( pX->opcode==OP_Explain ) continue; + if( pErr==0 ){ + pErr = wx_sqlite3_str_new(0); + }else{ + wx_sqlite3_str_appendchar(pErr, 1, '\n'); + } + wx_sqlite3_str_appendf(pErr, + "Opcode at %d jumps to %d which is outside the " + "subroutine at %d..%d", + i, iDest, iFirst, iLast); + break; + } + } + } + } + if( pErr ){ + char *zErr = wx_sqlite3_str_finish(pErr); + wx_sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_INTERNAL, OE_Abort, 0, zErr, 0); + wx_sqlite3_free(zErr); + wx_sqlite3MayAbort(pParse); + } +} +#endif /* SQLITE_DEBUG */ + /* ** Return the address of the next instruction to be inserted. */ SQLITE_PRIVATE int wx_sqlite3VdbeCurrentAddr(Vdbe *p){ - assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); + assert( p->eVdbeState==VDBE_INIT_STATE ); return p->nOp; } @@ -79427,7 +83546,7 @@ SQLITE_PRIVATE VdbeOp *wx_sqlite3VdbeAddOpList( int i; VdbeOp *pOut, *pFirst; assert( nOp>0 ); - assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); + assert( p->eVdbeState==VDBE_INIT_STATE ); if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){ return 0; } @@ -79479,6 +83598,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeScanStatus( aNew = (ScanStatus*)wx_sqlite3DbRealloc(p->db, p->aScan, nByte); if( aNew ){ ScanStatus *pNew = &aNew[p->nScan++]; + memset(pNew, 0, sizeof(ScanStatus)); pNew->addrExplain = addrExplain; pNew->addrLoop = addrLoop; pNew->addrVisit = addrVisit; @@ -79487,6 +83607,62 @@ SQLITE_PRIVATE void wx_sqlite3VdbeScanStatus( p->aScan = aNew; } } + +/* +** Add the range of instructions from addrStart to addrEnd (inclusive) to +** the set of those corresponding to the wx_sqlite3_stmt_scanstatus() counters +** associated with the OP_Explain instruction at addrExplain. The +** sum of the wx_sqlite3Hwtime() values for each of these instructions +** will be returned for SQLITE_SCANSTAT_NCYCLE requests. +*/ +SQLITE_PRIVATE void wx_sqlite3VdbeScanStatusRange( + Vdbe *p, + int addrExplain, + int addrStart, + int addrEnd +){ + ScanStatus *pScan = 0; + int ii; + for(ii=p->nScan-1; ii>=0; ii--){ + pScan = &p->aScan[ii]; + if( pScan->addrExplain==addrExplain ) break; + pScan = 0; + } + if( pScan ){ + if( addrEnd<0 ) addrEnd = wx_sqlite3VdbeCurrentAddr(p)-1; + for(ii=0; iiaAddrRange); ii+=2){ + if( pScan->aAddrRange[ii]==0 ){ + pScan->aAddrRange[ii] = addrStart; + pScan->aAddrRange[ii+1] = addrEnd; + break; + } + } + } +} + +/* +** Set the addresses for the SQLITE_SCANSTAT_NLOOP and SQLITE_SCANSTAT_NROW +** counters for the query element associated with the OP_Explain at +** addrExplain. +*/ +SQLITE_PRIVATE void wx_sqlite3VdbeScanStatusCounters( + Vdbe *p, + int addrExplain, + int addrLoop, + int addrVisit +){ + ScanStatus *pScan = 0; + int ii; + for(ii=p->nScan-1; ii>=0; ii--){ + pScan = &p->aScan[ii]; + if( pScan->addrExplain==addrExplain ) break; + pScan = 0; + } + if( pScan ){ + pScan->addrLoop = addrLoop; + pScan->addrVisit = addrVisit; + } +} #endif @@ -79495,15 +83671,19 @@ SQLITE_PRIVATE void wx_sqlite3VdbeScanStatus( ** for a specific instruction. */ SQLITE_PRIVATE void wx_sqlite3VdbeChangeOpcode(Vdbe *p, int addr, u8 iNewOpcode){ + assert( addr>=0 ); wx_sqlite3VdbeGetOp(p,addr)->opcode = iNewOpcode; } SQLITE_PRIVATE void wx_sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){ + assert( addr>=0 ); wx_sqlite3VdbeGetOp(p,addr)->p1 = val; } SQLITE_PRIVATE void wx_sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){ + assert( addr>=0 || p->db->mallocFailed ); wx_sqlite3VdbeGetOp(p,addr)->p2 = val; } SQLITE_PRIVATE void wx_sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){ + assert( addr>=0 ); wx_sqlite3VdbeGetOp(p,addr)->p3 = val; } SQLITE_PRIVATE void wx_sqlite3VdbeChangeP5(Vdbe *p, u16 p5){ @@ -79511,6 +83691,18 @@ SQLITE_PRIVATE void wx_sqlite3VdbeChangeP5(Vdbe *p, u16 p5){ if( p->nOp>0 ) p->aOp[p->nOp-1].p5 = p5; } +/* +** If the previous opcode is an OP_Column that delivers results +** into register iDest, then add the OPFLAG_TYPEOFARG flag to that +** opcode. +*/ +SQLITE_PRIVATE void wx_sqlite3VdbeTypeofColumn(Vdbe *p, int iDest){ + VdbeOp *pOp = wx_sqlite3VdbeGetLastOp(p); + if( pOp->p3==iDest && pOp->opcode==OP_Column ){ + pOp->p5 |= OPFLAG_TYPEOFARG; + } +} + /* ** Change the P2 operand of instruction addr so that it points to ** the address of the next instruction to be coded. @@ -79539,7 +83731,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){ || p->aOp[addr].opcode==OP_FkIfZero ); assert( p->aOp[addr].p4type==0 ); #ifdef SQLITE_VDBE_COVERAGE - wx_sqlite3VdbeGetOp(p,-1)->iSrcLine = 0; /* Erase VdbeCoverage() macros */ + wx_sqlite3VdbeGetLastOp(p)->iSrcLine = 0; /* Erase VdbeCoverage() macros */ #endif p->nOp--; }else{ @@ -79553,8 +83745,9 @@ SQLITE_PRIVATE void wx_sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){ ** the FuncDef is not ephermal, then do nothing. */ static void freeEphemeralFunction(wx_sqlite3 *db, FuncDef *pDef){ + assert( db!=0 ); if( (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){ - wx_sqlite3DbFreeNN(db, pDef); + wx_sqlite3DbNNFreeNN(db, pDef); } } @@ -79563,11 +83756,12 @@ static void freeEphemeralFunction(wx_sqlite3 *db, FuncDef *pDef){ */ static SQLITE_NOINLINE void freeP4Mem(wx_sqlite3 *db, Mem *p){ if( p->szMalloc ) wx_sqlite3DbFree(db, p->zMalloc); - wx_sqlite3DbFreeNN(db, p); + wx_sqlite3DbNNFreeNN(db, p); } static SQLITE_NOINLINE void freeP4FuncCtx(wx_sqlite3 *db, wx_sqlite3_context *p){ + assert( db!=0 ); freeEphemeralFunction(db, p->pFunc); - wx_sqlite3DbFreeNN(db, p); + wx_sqlite3DbNNFreeNN(db, p); } static void freeP4(wx_sqlite3 *db, int p4type, void *p4){ assert( db ); @@ -79579,9 +83773,8 @@ static void freeP4(wx_sqlite3 *db, int p4type, void *p4){ case P4_REAL: case P4_INT64: case P4_DYNAMIC: - case P4_DYNBLOB: case P4_INTARRAY: { - wx_sqlite3DbFree(db, p4); + if( p4 ) wx_sqlite3DbNNFreeNN(db, p4); break; } case P4_KEYINFO: { @@ -79619,15 +83812,19 @@ static void freeP4(wx_sqlite3 *db, int p4type, void *p4){ ** nOp entries. */ static void vdbeFreeOpArray(wx_sqlite3 *db, Op *aOp, int nOp){ + assert( nOp>=0 ); + assert( db!=0 ); if( aOp ){ - Op *pOp; - for(pOp=&aOp[nOp-1]; pOp>=aOp; pOp--){ + Op *pOp = &aOp[nOp-1]; + while(1){ /* Exit via break */ if( pOp->p4type <= P4_FREE_IF_LE ) freeP4(db, pOp->p4type, pOp->p4.p); #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS wx_sqlite3DbFree(db, pOp->zComment); #endif + if( pOp==aOp ) break; + pOp--; } - wx_sqlite3DbFreeNN(db, aOp); + wx_sqlite3DbNNFreeNN(db, aOp); } } @@ -79687,7 +83884,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeReleaseRegisters( u32 mask, /* Mask of registers to NOT release */ int bUndefine /* If true, mark registers as undefined */ ){ - if( N==0 ) return; + if( N==0 || OptimizationDisabled(pParse->db, SQLITE_ReleaseReg) ) return; assert( pParse->pVdbe ); assert( iFirst>=1 ); assert( iFirst+N-1<=pParse->nMem ); @@ -79751,7 +83948,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, i wx_sqlite3 *db; assert( p!=0 ); db = p->db; - assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); + assert( p->eVdbeState==VDBE_INIT_STATE ); assert( p->aOp!=0 || db->mallocFailed ); if( db->mallocFailed ){ if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4); @@ -79796,7 +83993,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeAppendP4(Vdbe *p, void *pP4, int n){ if( p->db->mallocFailed ){ freeP4(p->db, n, pP4); }else{ - assert( pP4!=0 ); + assert( pP4!=0 || n==P4_DYNAMIC ); assert( p->nOp>0 ); pOp = &p->aOp[p->nOp-1]; assert( pOp->p4type==P4_NOTUSED ); @@ -79827,8 +84024,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){ */ static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){ assert( p->nOp>0 || p->aOp==0 ); - assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed - || p->pParse->nErr>0 ); + assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->pParse->nErr>0 ); if( p->nOp ){ assert( p->aOp ); wx_sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment); @@ -79859,13 +84055,13 @@ SQLITE_PRIVATE void wx_sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...) ** Set the value if the iSrcLine field for the previously coded instruction. */ SQLITE_PRIVATE void wx_sqlite3VdbeSetLineNumber(Vdbe *v, int iLine){ - wx_sqlite3VdbeGetOp(v,-1)->iSrcLine = iLine; + wx_sqlite3VdbeGetLastOp(v)->iSrcLine = iLine; } #endif /* SQLITE_VDBE_COVERAGE */ /* -** Return the opcode for a given address. If the address is -1, then -** return the most recently inserted opcode. +** Return the opcode for a given address. The address must be non-negative. +** See wx_sqlite3VdbeGetLastOp() to get the most recently added opcode. ** ** If a memory allocation error has occurred prior to the calling of this ** routine, then a pointer to a dummy VdbeOp will be returned. That opcode @@ -79880,10 +84076,7 @@ SQLITE_PRIVATE VdbeOp *wx_sqlite3VdbeGetOp(Vdbe *p, int addr){ /* C89 specifies that the constant "dummy" will be initialized to all ** zeros, which is correct. MSVC generates a warning, nevertheless. */ static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */ - assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); - if( addr<0 ){ - addr = p->nOp - 1; - } + assert( p->eVdbeState==VDBE_INIT_STATE ); assert( (addr>=0 && addrnOp) || p->db->mallocFailed ); if( p->db->mallocFailed ){ return (VdbeOp*)&dummy; @@ -79892,6 +84085,12 @@ SQLITE_PRIVATE VdbeOp *wx_sqlite3VdbeGetOp(Vdbe *p, int addr){ } } +/* Return the most recently added opcode +*/ +VdbeOp * wx_sqlite3VdbeGetLastOp(Vdbe *p){ + return wx_sqlite3VdbeGetOp(p, p->nOp - 1); +} + #if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) /* ** Return an integer value for one of the parameters to the opcode pOp @@ -79936,13 +84135,9 @@ SQLITE_PRIVATE char *wx_sqlite3VdbeDisplayComment( if( zOpName[nOpName+1] ){ int seenCom = 0; char c; - zSynopsis = zOpName += nOpName + 1; + zSynopsis = zOpName + nOpName + 1; if( strncmp(zSynopsis,"IF ",3)==0 ){ - if( pOp->p5 & SQLITE_STOREP2 ){ - wx_sqlite3_snprintf(sizeof(zAlt), zAlt, "r[P2] = (%s)", zSynopsis+3); - }else{ - wx_sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3); - } + wx_sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3); zSynopsis = zAlt; } for(ii=0; (c = zSynopsis[ii])!=0; ii++){ @@ -79951,8 +84146,11 @@ SQLITE_PRIVATE char *wx_sqlite3VdbeDisplayComment( if( c=='4' ){ wx_sqlite3_str_appendall(&x, zP4); }else if( c=='X' ){ - wx_sqlite3_str_appendall(&x, pOp->zComment); - seenCom = 1; + if( pOp->zComment && pOp->zComment[0] ){ + wx_sqlite3_str_appendall(&x, pOp->zComment); + seenCom = 1; + break; + } }else{ int v1 = translateP(c, pOp); int v2; @@ -80013,6 +84211,7 @@ static void displayP4Expr(StrAccum *p, Expr *pExpr){ const char *zOp = 0; switch( pExpr->op ){ case TK_STRING: + assert( !ExprHasProperty(pExpr, EP_IntValue) ); wx_sqlite3_str_appendf(p, "%Q", pExpr->u.zToken); break; case TK_INTEGER: @@ -80115,7 +84314,7 @@ SQLITE_PRIVATE char *wx_sqlite3VdbeDisplayP4(wx_sqlite3 *db, Op *pOp){ case P4_COLLSEQ: { static const char *const encnames[] = {"?", "8", "16LE", "16BE"}; CollSeq *pColl = pOp->p4.pColl; - assert( pColl->enc>=0 && pColl->enc<4 ); + assert( pColl->enc<4 ); wx_sqlite3_str_appendf(&x, "%.18s-%s", pColl->zName, encnames[pColl->enc]); break; @@ -80180,10 +84379,6 @@ SQLITE_PRIVATE char *wx_sqlite3VdbeDisplayP4(wx_sqlite3 *db, Op *pOp){ zP4 = "program"; break; } - case P4_DYNBLOB: - case P4_ADVANCE: { - break; - } case P4_TABLE: { zP4 = pOp->p4.pTab->zName; break; @@ -80315,21 +84510,40 @@ SQLITE_PRIVATE void wx_sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){ /* ** Initialize an array of N Mem element. +** +** This is a high-runner, so only those fields that really do need to +** be initialized are set. The Mem structure is organized so that +** the fields that get initialized are nearby and hopefully on the same +** cache line. +** +** Mem.flags = flags +** Mem.db = db +** Mem.szMalloc = 0 +** +** All other fields of Mem can safely remain uninitialized for now. They +** will be initialized before use. */ static void initMemArray(Mem *p, int N, wx_sqlite3 *db, u16 flags){ - while( (N--)>0 ){ - p->db = db; - p->flags = flags; - p->szMalloc = 0; + if( N>0 ){ + do{ + p->flags = flags; + p->db = db; + p->szMalloc = 0; #ifdef SQLITE_DEBUG - p->pScopyFrom = 0; + p->pScopyFrom = 0; #endif - p++; + p++; + }while( (--N)>0 ); } } /* -** Release an array of N Mem elements +** Release auxiliary memory held in an array of N Mem elements. +** +** After this routine returns, all Mem elements in the array will still +** be valid. Those Mem elements that were not holding auxiliary resources +** will be unchanged. Mem elements which had something freed will be +** set to MEM_Undefined. */ static void releaseMemArray(Mem *p, int N){ if( p && N ){ @@ -80359,15 +84573,20 @@ static void releaseMemArray(Mem *p, int N){ */ testcase( p->flags & MEM_Agg ); testcase( p->flags & MEM_Dyn ); - testcase( p->xDel==wx_sqlite3VdbeFrameMemDel ); if( p->flags&(MEM_Agg|MEM_Dyn) ){ + testcase( (p->flags & MEM_Dyn)!=0 && p->xDel==wx_sqlite3VdbeFrameMemDel ); wx_sqlite3VdbeMemRelease(p); + p->flags = MEM_Undefined; }else if( p->szMalloc ){ - wx_sqlite3DbFreeNN(db, p->zMalloc); + wx_sqlite3DbNNFreeNN(db, p->zMalloc); p->szMalloc = 0; + p->flags = MEM_Undefined; } - - p->flags = MEM_Undefined; +#ifdef SQLITE_DEBUG + else{ + p->flags = MEM_Undefined; + } +#endif }while( (++p)nChildMem]; assert( wx_sqlite3VdbeFrameIsValid(p) ); for(i=0; inChildCsr; i++){ - wx_sqlite3VdbeFreeCursor(p->v, apCsr[i]); + if( apCsr[i] ) wx_sqlite3VdbeFreeCursorNN(p->v, apCsr[i]); } releaseMemArray(aMem, p->nChildMem); wx_sqlite3VdbeDeleteAuxData(p->v->db, &p->pAuxData, -1, 0); @@ -80565,7 +84784,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeList( Op *pOp; /* Current opcode */ assert( p->explain ); - assert( p->iVdbeMagic==VDBE_MAGIC_RUN ); + assert( p->eVdbeState==VDBE_RUN_STATE ); assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); /* Even though this opcode does not use dynamic strings for @@ -80573,7 +84792,6 @@ SQLITE_PRIVATE int wx_sqlite3VdbeList( ** wx_sqlite3_column_text16(), causing a translation to UTF-16 encoding. */ releaseMemArray(pMem, 8); - p->pResultSet = 0; if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to wx_sqlite3_column_text() or @@ -80630,7 +84848,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeList( wx_sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, wx_sqlite3_free); p->nResColumn = 8; } - p->pResultSet = pMem; + p->pResultRow = pMem; if( db->mallocFailed ){ p->rc = SQLITE_NOMEM; rc = SQLITE_ERROR; @@ -80720,11 +84938,11 @@ struct ReusableSpace { static void *allocSpace( struct ReusableSpace *p, /* Bulk memory available for allocation */ void *pBuf, /* Pointer to a prior allocation */ - wx_sqlite3_int64 nByte /* Bytes of memory needed */ + wx_sqlite3_int64 nByte /* Bytes of memory needed. */ ){ assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) ); if( pBuf==0 ){ - nByte = ROUND8(nByte); + nByte = ROUND8P(nByte); if( nByte <= p->nFree ){ p->nFree -= nByte; pBuf = &p->pSpace[p->nFree]; @@ -80741,18 +84959,19 @@ static void *allocSpace( ** running it. */ SQLITE_PRIVATE void wx_sqlite3VdbeRewind(Vdbe *p){ -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) +#if defined(SQLITE_DEBUG) int i; #endif assert( p!=0 ); - assert( p->iVdbeMagic==VDBE_MAGIC_INIT || p->iVdbeMagic==VDBE_MAGIC_RESET ); + assert( p->eVdbeState==VDBE_INIT_STATE + || p->eVdbeState==VDBE_READY_STATE + || p->eVdbeState==VDBE_HALT_STATE ); /* There should be at least one opcode. */ assert( p->nOp>0 ); - /* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */ - p->iVdbeMagic = VDBE_MAGIC_RUN; + p->eVdbeState = VDBE_READY_STATE; #ifdef SQLITE_DEBUG for(i=0; inMem; i++){ @@ -80769,8 +84988,8 @@ SQLITE_PRIVATE void wx_sqlite3VdbeRewind(Vdbe *p){ p->nFkConstraint = 0; #ifdef VDBE_PROFILE for(i=0; inOp; i++){ - p->aOp[i].cnt = 0; - p->aOp[i].cycles = 0; + p->aOp[i].nExec = 0; + p->aOp[i].nCycle = 0; } #endif } @@ -80808,7 +85027,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeMakeReady( assert( p!=0 ); assert( p->nOp>0 ); assert( pParse!=0 ); - assert( p->iVdbeMagic==VDBE_MAGIC_INIT ); + assert( p->eVdbeState==VDBE_INIT_STATE ); assert( pParse==p->pParse ); p->pVList = pParse->pVList; pParse->pVList = 0; @@ -80831,7 +85050,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeMakeReady( ** opcode array. This extra memory will be reallocated for other elements ** of the prepared statement. */ - n = ROUND8(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */ + n = ROUND8P(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */ x.pSpace = &((u8*)p->aOp)[n]; /* Unused opcode memory */ assert( EIGHT_BYTE_ALIGNMENT(x.pSpace) ); x.nFree = ROUNDDOWN8(pParse->szOpAlloc - n); /* Bytes of unused memory */ @@ -80879,9 +85098,6 @@ SQLITE_PRIVATE void wx_sqlite3VdbeMakeReady( p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem)); p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*)); p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*)); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - p->anExec = allocSpace(&x, 0, p->nOp*sizeof(i64)); -#endif if( x.nNeeded ){ x.pSpace = p->pFree = wx_sqlite3DbMallocRawNN(db, x.nNeeded); x.nFree = x.nNeeded; @@ -80890,9 +85106,6 @@ SQLITE_PRIVATE void wx_sqlite3VdbeMakeReady( p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem)); p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*)); p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); -#endif } } @@ -80907,9 +85120,6 @@ SQLITE_PRIVATE void wx_sqlite3VdbeMakeReady( p->nMem = nMem; initMemArray(p->aMem, nMem, db, MEM_Undefined); memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*)); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - memset(p->anExec, 0, p->nOp*sizeof(i64)); -#endif } wx_sqlite3VdbeRewind(p); } @@ -80919,11 +85129,9 @@ SQLITE_PRIVATE void wx_sqlite3VdbeMakeReady( ** happens to hold. */ SQLITE_PRIVATE void wx_sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ - if( pCx==0 ){ - return; - } - assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE ); - assert( pCx->pBtx==0 || pCx->isEphemeral ); + if( pCx ) wx_sqlite3VdbeFreeCursorNN(p,pCx); +} +SQLITE_PRIVATE void wx_sqlite3VdbeFreeCursorNN(Vdbe *p, VdbeCursor *pCx){ switch( pCx->eCurType ){ case CURTYPE_SORTER: { wx_sqlite3VdbeSorterClose(p->db, pCx); @@ -80951,14 +85159,12 @@ SQLITE_PRIVATE void wx_sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ ** Close all cursors in the current frame. */ static void closeCursorsInFrame(Vdbe *p){ - if( p->apCsr ){ - int i; - for(i=0; inCursor; i++){ - VdbeCursor *pC = p->apCsr[i]; - if( pC ){ - wx_sqlite3VdbeFreeCursor(p, pC); - p->apCsr[i] = 0; - } + int i; + for(i=0; inCursor; i++){ + VdbeCursor *pC = p->apCsr[i]; + if( pC ){ + wx_sqlite3VdbeFreeCursorNN(p, pC); + p->apCsr[i] = 0; } } } @@ -80971,9 +85177,6 @@ static void closeCursorsInFrame(Vdbe *p){ SQLITE_PRIVATE int wx_sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ Vdbe *v = pFrame->v; closeCursorsInFrame(v); -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - v->anExec = pFrame->anExec; -#endif v->aOp = pFrame->aOp; v->nOp = pFrame->nOp; v->aMem = pFrame->aMem; @@ -81007,9 +85210,7 @@ static void closeAllCursors(Vdbe *p){ } assert( p->nFrame==0 ); closeCursorsInFrame(p); - if( p->aMem ){ - releaseMemArray(p->aMem, p->nMem); - } + releaseMemArray(p->aMem, p->nMem); while( p->pDelFrame ){ VdbeFrame *pDel = p->pDelFrame; p->pDelFrame = pDel->pParent; @@ -81356,7 +85557,7 @@ static void checkActiveVdbeCnt(wx_sqlite3 *db){ if( p->readOnly==0 ) nWrite++; if( p->bIsReader ) nRead++; } - p = p->pNext; + p = p->pVNext; } assert( cnt==db->nVdbeActive ); assert( nWrite==db->nVdbeWrite ); @@ -81449,7 +85650,8 @@ SQLITE_PRIVATE int wx_sqlite3VdbeCheckFk(Vdbe *p, int deferred){ p->rc = SQLITE_CONSTRAINT_FOREIGNKEY; p->errorAction = OE_Abort; wx_sqlite3VdbeError(p, "FOREIGN KEY constraint failed"); - return SQLITE_ERROR; + if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR; + return SQLITE_CONSTRAINT_FOREIGNKEY; } return SQLITE_OK; } @@ -81460,9 +85662,9 @@ SQLITE_PRIVATE int wx_sqlite3VdbeCheckFk(Vdbe *p, int deferred){ ** has made changes and is in autocommit mode, then commit those ** changes. If a rollback is needed, then do the rollback. ** -** This routine is the only way to move the state of a VM from -** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT. It is harmless to -** call this on a VM that is in the SQLITE_MAGIC_HALT state. +** This routine is the only way to move the wx_sqlite3eOpenState of a VM from +** SQLITE_STATE_RUN to SQLITE_STATE_HALT. It is harmless to +** call this on a VM that is in the SQLITE_STATE_HALT state. ** ** Return an error code. If the commit could not complete because of ** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it @@ -81488,9 +85690,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeHalt(Vdbe *p){ ** one, or the complete transaction if there is no statement transaction. */ - if( p->iVdbeMagic!=VDBE_MAGIC_RUN ){ - return SQLITE_OK; - } + assert( p->eVdbeState==VDBE_RUN_STATE ); if( db->mallocFailed ){ p->rc = SQLITE_NOMEM_BKPT; } @@ -81499,7 +85699,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeHalt(Vdbe *p){ /* No commit or rollback needed if the program never started or if the ** SQL statement does not read or write a database file. */ - if( p->pc>=0 && p->bIsReader ){ + if( p->bIsReader ){ int mrc; /* Primary error code from p->rc */ int eStatementOp = 0; int isSpecialError; /* Set to true if a 'special' error */ @@ -81508,9 +85708,15 @@ SQLITE_PRIVATE int wx_sqlite3VdbeHalt(Vdbe *p){ wx_sqlite3VdbeEnter(p); /* Check for one of the special errors */ - mrc = p->rc & 0xff; - isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR - || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; + if( p->rc ){ + mrc = p->rc & 0xff; + isSpecialError = mrc==SQLITE_NOMEM + || mrc==SQLITE_IOERR + || mrc==SQLITE_INTERRUPT + || mrc==SQLITE_FULL; + }else{ + mrc = isSpecialError = 0; + } if( isSpecialError ){ /* If the query was read-only and the error code is SQLITE_INTERRUPT, ** no rollback is necessary. Otherwise, at least a savepoint @@ -81562,6 +85768,9 @@ SQLITE_PRIVATE int wx_sqlite3VdbeHalt(Vdbe *p){ return SQLITE_ERROR; } rc = SQLITE_CONSTRAINT_FOREIGNKEY; + }else if( db->flags & SQLITE_CorruptRdOnly ){ + rc = SQLITE_CORRUPT; + db->flags &= ~SQLITE_CorruptRdOnly; }else{ /* The auto-commit flag is true, the vdbe program was successful ** or hit an 'OR FAIL' constraint and there are no deferred foreign @@ -81638,15 +85847,13 @@ SQLITE_PRIVATE int wx_sqlite3VdbeHalt(Vdbe *p){ } /* We have successfully halted and closed the VM. Record this fact. */ - if( p->pc>=0 ){ - db->nVdbeActive--; - if( !p->readOnly ) db->nVdbeWrite--; - if( p->bIsReader ) db->nVdbeRead--; - assert( db->nVdbeActive>=db->nVdbeRead ); - assert( db->nVdbeRead>=db->nVdbeWrite ); - assert( db->nVdbeWrite>=0 ); - } - p->iVdbeMagic = VDBE_MAGIC_HALT; + db->nVdbeActive--; + if( !p->readOnly ) db->nVdbeWrite--; + if( p->bIsReader ) db->nVdbeRead--; + assert( db->nVdbeActive>=db->nVdbeRead ); + assert( db->nVdbeRead>=db->nVdbeWrite ); + assert( db->nVdbeWrite>=0 ); + p->eVdbeState = VDBE_HALT_STATE; checkActiveVdbeCnt(db); if( db->mallocFailed ){ p->rc = SQLITE_NOMEM_BKPT; @@ -81695,6 +85902,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeTransferError(Vdbe *p){ wx_sqlite3ValueSetNull(db->pErr); } db->errCode = rc; + db->errByteOffset = -1; return rc; } @@ -81727,8 +85935,8 @@ static void vdbeInvokeSqllog(Vdbe *v){ ** again. ** ** To look at it another way, this routine resets the state of the -** virtual machine from VDBE_MAGIC_RUN or VDBE_MAGIC_HALT back to -** VDBE_MAGIC_INIT. +** virtual machine from VDBE_RUN_STATE or VDBE_HALT_STATE back to +** VDBE_READY_STATE. */ SQLITE_PRIVATE int wx_sqlite3VdbeReset(Vdbe *p){ #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) @@ -81742,7 +85950,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeReset(Vdbe *p){ ** error, then it might not have been halted properly. So halt ** it now. */ - wx_sqlite3VdbeHalt(p); + if( p->eVdbeState==VDBE_RUN_STATE ) wx_sqlite3VdbeHalt(p); /* If the VDBE has been run even partially, then transfer the error code ** and error message from the VDBE into the main database structure. But @@ -81756,13 +85964,6 @@ SQLITE_PRIVATE int wx_sqlite3VdbeReset(Vdbe *p){ }else{ db->errCode = p->rc; } - if( p->runOnlyOnce ) p->expired = 1; - }else if( p->rc && p->expired ){ - /* The expired flag was set on the VDBE before the first call - ** to wx_sqlite3_step(). For consistency (since wx_sqlite3_step() was - ** called), set the database error in this case as well. - */ - wx_sqlite3ErrorWithMsg(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg); } /* Reset register contents and reclaim error message memory. @@ -81779,7 +85980,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeReset(Vdbe *p){ wx_sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; } - p->pResultSet = 0; + p->pResultRow = 0; #ifdef SQLITE_DEBUG p->nWrite = 0; #endif @@ -81807,10 +86008,12 @@ SQLITE_PRIVATE int wx_sqlite3VdbeReset(Vdbe *p){ } for(i=0; inOp; i++){ char zHdr[100]; + i64 cnt = p->aOp[i].nExec; + i64 cycles = p->aOp[i].nCycle; wx_sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ", - p->aOp[i].cnt, - p->aOp[i].cycles, - p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0 + cnt, + cycles, + cnt>0 ? cycles/cnt : 0 ); fprintf(out, "%s", zHdr); wx_sqlite3VdbePrintOp(out, i, &p->aOp[i]); @@ -81819,7 +86022,6 @@ SQLITE_PRIVATE int wx_sqlite3VdbeReset(Vdbe *p){ } } #endif - p->iVdbeMagic = VDBE_MAGIC_RESET; return p->rc & db->errMask; } @@ -81829,7 +86031,10 @@ SQLITE_PRIVATE int wx_sqlite3VdbeReset(Vdbe *p){ */ SQLITE_PRIVATE int wx_sqlite3VdbeFinalize(Vdbe *p){ int rc = SQLITE_OK; - if( p->iVdbeMagic==VDBE_MAGIC_RUN || p->iVdbeMagic==VDBE_MAGIC_HALT ){ + assert( VDBE_RUN_STATE>VDBE_READY_STATE ); + assert( VDBE_HALT_STATE>VDBE_READY_STATE ); + assert( VDBE_INIT_STATEeVdbeState>=VDBE_READY_STATE ){ rc = wx_sqlite3VdbeReset(p); assert( (rc & p->db->errMask)==rc ); } @@ -81881,23 +86086,26 @@ SQLITE_PRIVATE void wx_sqlite3VdbeDeleteAuxData(wx_sqlite3 *db, AuxData **pp, in ** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with ** the database connection and frees the object itself. */ -SQLITE_PRIVATE void wx_sqlite3VdbeClearObject(wx_sqlite3 *db, Vdbe *p){ +static void wx_sqlite3VdbeClearObject(wx_sqlite3 *db, Vdbe *p){ SubProgram *pSub, *pNext; + assert( db!=0 ); assert( p->db==0 || p->db==db ); - releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); + if( p->aColName ){ + releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); + wx_sqlite3DbNNFreeNN(db, p->aColName); + } for(pSub=p->pProgram; pSub; pSub=pNext){ pNext = pSub->pNext; vdbeFreeOpArray(db, pSub->aOp, pSub->nOp); wx_sqlite3DbFree(db, pSub); } - if( p->iVdbeMagic!=VDBE_MAGIC_INIT ){ + if( p->eVdbeState!=VDBE_INIT_STATE ){ releaseMemArray(p->aVar, p->nVar); - wx_sqlite3DbFree(db, p->pVList); - wx_sqlite3DbFree(db, p->pFree); + if( p->pVList ) wx_sqlite3DbNNFreeNN(db, p->pVList); + if( p->pFree ) wx_sqlite3DbNNFreeNN(db, p->pFree); } vdbeFreeOpArray(db, p->aOp, p->nOp); - wx_sqlite3DbFree(db, p->aColName); - wx_sqlite3DbFree(db, p->zSql); + if( p->zSql ) wx_sqlite3DbNNFreeNN(db, p->zSql); #ifdef SQLITE_ENABLE_NORMALIZE wx_sqlite3DbFree(db, p->zNormSql); { @@ -81927,20 +86135,17 @@ SQLITE_PRIVATE void wx_sqlite3VdbeDelete(Vdbe *p){ assert( p!=0 ); db = p->db; + assert( db!=0 ); assert( wx_sqlite3_mutex_held(db->mutex) ); wx_sqlite3VdbeClearObject(db, p); - if( p->pPrev ){ - p->pPrev->pNext = p->pNext; - }else{ - assert( db->pVdbe==p ); - db->pVdbe = p->pNext; - } - if( p->pNext ){ - p->pNext->pPrev = p->pPrev; + if( db->pnBytesFreed==0 ){ + assert( p->ppVPrev!=0 ); + *p->ppVPrev = p->pVNext; + if( p->pVNext ){ + p->pVNext->ppVPrev = p->ppVPrev; + } } - p->iVdbeMagic = VDBE_MAGIC_DEAD; - p->db = 0; - wx_sqlite3DbFreeNN(db, p); + wx_sqlite3DbNNFreeNN(db, p); } /* @@ -81956,7 +86161,7 @@ SQLITE_PRIVATE int SQLITE_NOINLINE wx_sqlite3VdbeFinishMoveto(VdbeCursor *p){ assert( p->deferredMoveto ); assert( p->isTable ); assert( p->eCurType==CURTYPE_BTREE ); - rc = wx_sqlite3BtreeMovetoUnpacked(p->uc.pCursor, 0, p->movetoTarget, 0, &res); + rc = wx_sqlite3BtreeTableMoveto(p->uc.pCursor, p->movetoTarget, 0, &res); if( rc ) return rc; if( res!=0 ) return SQLITE_CORRUPT_BKPT; #ifdef SQLITE_TEST @@ -81974,7 +86179,7 @@ SQLITE_PRIVATE int SQLITE_NOINLINE wx_sqlite3VdbeFinishMoveto(VdbeCursor *p){ ** is supposed to be pointing. If the row was deleted out from under the ** cursor, set the cursor to point to a NULL row. */ -static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){ +SQLITE_PRIVATE int SQLITE_NOINLINE wx_sqlite3VdbeHandleMovedCursor(VdbeCursor *p){ int isDifferentRow, rc; assert( p->eCurType==CURTYPE_BTREE ); assert( p->uc.pCursor!=0 ); @@ -81990,41 +86195,9 @@ static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){ ** if need be. Return any I/O error from the restore operation. */ SQLITE_PRIVATE int wx_sqlite3VdbeCursorRestore(VdbeCursor *p){ - assert( p->eCurType==CURTYPE_BTREE ); - if( wx_sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ - return handleMovedCursor(p); - } - return SQLITE_OK; -} - -/* -** Make sure the cursor p is ready to read or write the row to which it -** was last positioned. Return an error code if an OOM fault or I/O error -** prevents us from positioning the cursor to its correct position. -** -** If a MoveTo operation is pending on the given cursor, then do that -** MoveTo now. If no move is pending, check to see if the row has been -** deleted out from under the cursor and if it has, mark the row as -** a NULL row. -** -** If the cursor is already pointing to the correct row and that row has -** not been deleted out from under the cursor, then this routine is a no-op. -*/ -SQLITE_PRIVATE int wx_sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ - VdbeCursor *p = *pp; - assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); - if( p->deferredMoveto ){ - u32 iMap; - assert( !p->isEphemeral ); - if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ - *pp = p->pAltCursor; - *piCol = iMap - 1; - return SQLITE_OK; - } - return wx_sqlite3VdbeFinishMoveto(p); - } + assert( p->eCurType==CURTYPE_BTREE || IsNullCursor(p) ); if( wx_sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ - return handleMovedCursor(p); + return wx_sqlite3VdbeHandleMovedCursor(p); } return SQLITE_OK; } @@ -82035,7 +86208,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ ** wx_sqlite3VdbeSerialType() ** wx_sqlite3VdbeSerialTypeLen() ** wx_sqlite3VdbeSerialLen() -** wx_sqlite3VdbeSerialPut() +** wx_sqlite3VdbeSerialPut() <--- in-lined into OP_MakeRecord as of 2022-04-02 ** wx_sqlite3VdbeSerialGet() ** ** encapsulate the code that serializes values for storage in SQLite @@ -82147,7 +86320,7 @@ SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLe /* ** The sizes for serial types less than 128 */ -static const u8 wx_sqlite3SmallTypeSizes[] = { +SQLITE_PRIVATE const u8 wx_sqlite3SmallTypeSizes[128] = { /* 0 1 2 3 4 5 6 7 8 9 */ /* 0 */ 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, /* 10 */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, @@ -82216,7 +86389,7 @@ SQLITE_PRIVATE u8 wx_sqlite3VdbeOneByteSerialTypeLen(u8 serial_type){ ** so we trust him. */ #ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT -static u64 floatSwap(u64 in){ +SQLITE_PRIVATE u64 wx_sqlite3FloatSwap(u64 in){ union { u64 r; u32 i[2]; @@ -82229,59 +86402,8 @@ static u64 floatSwap(u64 in){ u.i[1] = t; return u.r; } -# define swapMixedEndianFloat(X) X = floatSwap(X) -#else -# define swapMixedEndianFloat(X) -#endif - -/* -** Write the serialized data blob for the value stored in pMem into -** buf. It is assumed that the caller has allocated sufficient space. -** Return the number of bytes written. -** -** nBuf is the amount of space left in buf[]. The caller is responsible -** for allocating enough space to buf[] to hold the entire field, exclusive -** of the pMem->u.nZero bytes for a MEM_Zero value. -** -** Return the number of bytes actually written into buf[]. The number -** of bytes in the zero-filled tail is included in the return value only -** if those bytes were zeroed in buf[]. -*/ -SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){ - u32 len; +#endif /* SQLITE_MIXED_ENDIAN_64BIT_FLOAT */ - /* Integer and Real */ - if( serial_type<=7 && serial_type>0 ){ - u64 v; - u32 i; - if( serial_type==7 ){ - assert( sizeof(v)==sizeof(pMem->u.r) ); - memcpy(&v, &pMem->u.r, sizeof(v)); - swapMixedEndianFloat(v); - }else{ - v = pMem->u.i; - } - len = i = wx_sqlite3SmallTypeSizes[serial_type]; - assert( i>0 ); - do{ - buf[--i] = (u8)(v&0xFF); - v >>= 8; - }while( i ); - return len; - } - - /* String or blob */ - if( serial_type>=12 ){ - assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0) - == (int)wx_sqlite3VdbeSerialTypeLen(serial_type) ); - len = pMem->n; - if( len>0 ) memcpy(buf, pMem->z, len); - return len; - } - - /* NULL or constants 0 or 1 */ - return 0; -} /* Input "x" is a sequence of unsigned characters that represent a ** big-endian integer. Return the equivalent native integer @@ -82294,14 +86416,14 @@ SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){ /* ** Deserialize the data blob pointed to by buf as serial type serial_type -** and store the result in pMem. Return the number of bytes read. +** and store the result in pMem. ** ** This function is implemented as two separate routines for performance. ** The few cases that require local variables are broken out into a separate ** routine so that in most cases the overhead of moving the stack pointer ** is avoided. */ -static u32 serialGet( +static void serialGet( const unsigned char *buf, /* Buffer to deserialize from */ u32 serial_type, /* Serial type to deserialize */ Mem *pMem /* Memory cell to write value into */ @@ -82335,9 +86457,8 @@ static u32 serialGet( memcpy(&pMem->u.r, &x, sizeof(x)); pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real; } - return 8; } -SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialGet( +SQLITE_PRIVATE void wx_sqlite3VdbeSerialGet( const unsigned char *buf, /* Buffer to deserialize from */ u32 serial_type, /* Serial type to deserialize */ Mem *pMem /* Memory cell to write value into */ @@ -82348,13 +86469,13 @@ SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialGet( pMem->flags = MEM_Null|MEM_Zero; pMem->n = 0; pMem->u.nZero = 0; - break; + return; } case 11: /* Reserved for future use */ case 0: { /* Null */ /* EVIDENCE-OF: R-24078-09375 Value is a NULL. */ pMem->flags = MEM_Null; - break; + return; } case 1: { /* EVIDENCE-OF: R-44885-25196 Value is an 8-bit twos-complement @@ -82362,7 +86483,7 @@ SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialGet( pMem->u.i = ONE_BYTE_INT(buf); pMem->flags = MEM_Int; testcase( pMem->u.i<0 ); - return 1; + return; } case 2: { /* 2-byte signed integer */ /* EVIDENCE-OF: R-49794-35026 Value is a big-endian 16-bit @@ -82370,7 +86491,7 @@ SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialGet( pMem->u.i = TWO_BYTE_INT(buf); pMem->flags = MEM_Int; testcase( pMem->u.i<0 ); - return 2; + return; } case 3: { /* 3-byte signed integer */ /* EVIDENCE-OF: R-37839-54301 Value is a big-endian 24-bit @@ -82378,7 +86499,7 @@ SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialGet( pMem->u.i = THREE_BYTE_INT(buf); pMem->flags = MEM_Int; testcase( pMem->u.i<0 ); - return 3; + return; } case 4: { /* 4-byte signed integer */ /* EVIDENCE-OF: R-01849-26079 Value is a big-endian 32-bit @@ -82390,7 +86511,7 @@ SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialGet( #endif pMem->flags = MEM_Int; testcase( pMem->u.i<0 ); - return 4; + return; } case 5: { /* 6-byte signed integer */ /* EVIDENCE-OF: R-50385-09674 Value is a big-endian 48-bit @@ -82398,13 +86519,14 @@ SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialGet( pMem->u.i = FOUR_BYTE_UINT(buf+2) + (((i64)1)<<32)*TWO_BYTE_INT(buf); pMem->flags = MEM_Int; testcase( pMem->u.i<0 ); - return 6; + return; } case 6: /* 8-byte signed integer */ case 7: { /* IEEE floating point */ /* These use local variables, so do them in a separate routine ** to avoid having to move the frame pointer in the common case */ - return serialGet(buf,serial_type,pMem); + serialGet(buf,serial_type,pMem); + return; } case 8: /* Integer 0 */ case 9: { /* Integer 1 */ @@ -82412,7 +86534,7 @@ SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialGet( /* EVIDENCE-OF: R-18143-12121 Value is the integer 1. */ pMem->u.i = serial_type-8; pMem->flags = MEM_Int; - return 0; + return; } default: { /* EVIDENCE-OF: R-14606-31564 Value is a BLOB that is (N-12)/2 bytes in @@ -82423,10 +86545,10 @@ SQLITE_PRIVATE u32 wx_sqlite3VdbeSerialGet( pMem->z = (char *)buf; pMem->n = (serial_type-12)/2; pMem->flags = aFlag[serial_type&1]; - return pMem->n; + return; } } - return 0; + return; } /* ** This routine is used to allocate sufficient space for an UnpackedRecord @@ -82447,10 +86569,10 @@ SQLITE_PRIVATE UnpackedRecord *wx_sqlite3VdbeAllocUnpackedRecord( ){ UnpackedRecord *p; /* Unpacked record to return */ int nByte; /* Number of bytes required for *p */ - nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1); + nByte = ROUND8P(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1); p = (UnpackedRecord *)wx_sqlite3DbMallocRaw(pKeyInfo->db, nByte); if( !p ) return 0; - p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))]; + p->aMem = (Mem*)&((char*)p)[ROUND8P(sizeof(UnpackedRecord))]; assert( pKeyInfo->aSortFlags!=0 ); p->pKeyInfo = pKeyInfo; p->nField = pKeyInfo->nKeyField + 1; @@ -82489,7 +86611,8 @@ SQLITE_PRIVATE void wx_sqlite3VdbeRecordUnpack( /* pMem->flags = 0; // wx_sqlite3VdbeSerialGet() will set this for us */ pMem->szMalloc = 0; pMem->z = 0; - d += wx_sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); + wx_sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); + d += wx_sqlite3VdbeSerialTypeLen(serial_type); pMem++; if( (++u)>=p->nField ) break; } @@ -82573,7 +86696,8 @@ static int vdbeRecordCompareDebug( /* Extract the values to be compared. */ - d1 += wx_sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); + wx_sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); + d1 += wx_sqlite3VdbeSerialTypeLen(serial_type1); /* Do the comparison */ @@ -82684,8 +86808,8 @@ static int vdbeCompareMemString( }else{ rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2); } - wx_sqlite3VdbeMemRelease(&c1); - wx_sqlite3VdbeMemRelease(&c2); + wx_sqlite3VdbeMemReleaseMalloc(&c1); + wx_sqlite3VdbeMemReleaseMalloc(&c2); return rc; } } @@ -82740,7 +86864,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3BlobCompare(const Mem *pB1, const M ** number. Return negative, zero, or positive if the first (i64) is less than, ** equal to, or greater than the second (double). */ -static int wx_sqlite3IntFloatCompare(i64 i, double r){ +SQLITE_PRIVATE int wx_sqlite3IntFloatCompare(i64 i, double r){ if( sizeof(LONGDOUBLE_TYPE)>8 ){ LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; testcase( xpKeyInfo->aSortFlags!=0 ); assert( pPKey2->pKeyInfo->nKeyField>0 ); assert( idx1<=szHdr1 || CORRUPT_DB ); - do{ + while( 1 /*exit-by-break*/ ){ u32 serial_type; /* RHS is an integer */ @@ -82978,7 +87110,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeRecordCompareWithSkip( serial_type = aKey1[idx1]; testcase( serial_type==12 ); if( serial_type>=10 ){ - rc = +1; + rc = serial_type==10 ? -1 : +1; }else if( serial_type==0 ){ rc = -1; }else if( serial_type==7 ){ @@ -83003,7 +87135,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeRecordCompareWithSkip( ** numbers). Types 10 and 11 are currently "reserved for future ** use", so it doesn't really matter what the results of comparing ** them to numberic values are. */ - rc = +1; + rc = serial_type==10 ? -1 : +1; }else if( serial_type==0 ){ rc = -1; }else{ @@ -83084,7 +87216,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeRecordCompareWithSkip( /* RHS is null */ else{ serial_type = aKey1[idx1]; - rc = (serial_type!=0); + rc = (serial_type!=0 && serial_type!=10); } if( rc!=0 ){ @@ -83106,8 +87238,13 @@ SQLITE_PRIVATE int wx_sqlite3VdbeRecordCompareWithSkip( if( i==pPKey2->nField ) break; pRhs++; d1 += wx_sqlite3VdbeSerialTypeLen(serial_type); + if( d1>(unsigned)nKey1 ) break; idx1 += wx_sqlite3VarintLen(serial_type); - }while( idx1<(unsigned)szHdr1 && d1<=(unsigned)nKey1 ); + if( idx1>=(unsigned)szHdr1 ){ + pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; + return 0; /* Corrupt index */ + } + } /* No memory allocation is ever used on mem1. Prove this using ** the following assert(). If the assert() fails, it indicates a @@ -83209,7 +87346,8 @@ static int vdbeRecordCompareInt( return wx_sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2); } - v = pPKey2->aMem[0].u.i; + assert( pPKey2->u.i == pPKey2->aMem[0].u.i ); + v = pPKey2->u.i; if( v>lhs ){ res = pPKey2->r1; }else if( vaMem[0].flags & MEM_Str ); + assert( pPKey2->aMem[0].n == pPKey2->n ); + assert( pPKey2->aMem[0].z == pPKey2->u.z ); vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); - serial_type = (u8)(aKey1[1]); - if( serial_type >= 0x80 ){ - wx_sqlite3GetVarint32(&aKey1[1], (u32*)&serial_type); - } + serial_type = (signed char)(aKey1[1]); + +vrcs_restart: if( serial_type<12 ){ + if( serial_type<0 ){ + wx_sqlite3GetVarint32(&aKey1[1], (u32*)&serial_type); + if( serial_type>=12 ) goto vrcs_restart; + assert( CORRUPT_DB ); + } res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ }else if( !(serial_type & 0x01) ){ res = pPKey2->r2; /* (pKey1/nKey1) is a blob */ @@ -83263,15 +87407,15 @@ static int vdbeRecordCompareString( pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; return 0; /* Corruption */ } - nCmp = MIN( pPKey2->aMem[0].n, nStr ); - res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp); + nCmp = MIN( pPKey2->n, nStr ); + res = memcmp(&aKey1[szHdr], pPKey2->u.z, nCmp); if( res>0 ){ res = pPKey2->r2; }else if( res<0 ){ res = pPKey2->r1; }else{ - res = nStr - pPKey2->aMem[0].n; + res = nStr - pPKey2->n; if( res==0 ){ if( pPKey2->nField>1 ){ res = wx_sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); @@ -83326,6 +87470,7 @@ SQLITE_PRIVATE RecordCompare wx_sqlite3VdbeFindCompare(UnpackedRecord *p){ p->r2 = 1; } if( (flags & MEM_Int) ){ + p->u.i = p->aMem[0].u.i; return vdbeRecordCompareInt; } testcase( flags & MEM_Real ); @@ -83335,6 +87480,8 @@ SQLITE_PRIVATE RecordCompare wx_sqlite3VdbeFindCompare(UnpackedRecord *p){ && p->pKeyInfo->aColl[0]==0 ){ assert( flags & MEM_Str ); + p->u.z = p->aMem[0].z; + p->n = p->aMem[0].n; return vdbeRecordCompareString; } } @@ -83377,7 +87524,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeIdxRowid(wx_sqlite3 *db, BtCursor *pCur, i64 *r /* The index entry must begin with a header size */ getVarint32NR((u8*)m.z, szHdr); testcase( szHdr==3 ); - testcase( szHdr==m.n ); + testcase( szHdr==(u32)m.n ); testcase( szHdr>0x7fffffff ); assert( m.n>=0 ); if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){ @@ -83407,14 +87554,14 @@ SQLITE_PRIVATE int wx_sqlite3VdbeIdxRowid(wx_sqlite3 *db, BtCursor *pCur, i64 *r /* Fetch the integer off the end of the index record */ wx_sqlite3VdbeSerialGet((u8*)&m.z[m.n-lenRowid], typeRowid, &v); *rowid = v.u.i; - wx_sqlite3VdbeMemRelease(&m); + wx_sqlite3VdbeMemReleaseMalloc(&m); return SQLITE_OK; /* Jump here if database corruption is detected after m has been ** allocated. Free the m object and return SQLITE_CORRUPT. */ idx_rowid_corruption: testcase( m.szMalloc!=0 ); - wx_sqlite3VdbeMemRelease(&m); + wx_sqlite3VdbeMemReleaseMalloc(&m); return SQLITE_CORRUPT_BKPT; } @@ -83456,7 +87603,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeIdxKeyCompare( return rc; } *res = wx_sqlite3VdbeRecordCompareWithSkip(m.n, m.z, pUnpacked, 0); - wx_sqlite3VdbeMemRelease(&m); + wx_sqlite3VdbeMemReleaseMalloc(&m); return SQLITE_OK; } @@ -83464,7 +87611,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeIdxKeyCompare( ** This routine sets the value to be returned by subsequent calls to ** wx_sqlite3_changes() on the database handle 'db'. */ -SQLITE_PRIVATE void wx_sqlite3VdbeSetChanges(wx_sqlite3 *db, int nChange){ +SQLITE_PRIVATE void wx_sqlite3VdbeSetChanges(wx_sqlite3 *db, i64 nChange){ assert( wx_sqlite3_mutex_held(db->mutex) ); db->nChange = nChange; db->nTotalChange += nChange; @@ -83498,7 +87645,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbeCountChanges(Vdbe *v){ */ SQLITE_PRIVATE void wx_sqlite3ExpirePreparedStatements(wx_sqlite3 *db, int iCode){ Vdbe *p; - for(p = db->pVdbe; p; p=p->pNext){ + for(p = db->pVdbe; p; p=p->pVNext){ p->expired = iCode+1; } } @@ -83619,13 +87766,14 @@ SQLITE_PRIVATE void wx_sqlite3VtabImportErrmsg(Vdbe *p, wx_sqlite3_vtab *pVtab){ ** the vdbeUnpackRecord() function found in vdbeapi.c. */ static void vdbeFreeUnpacked(wx_sqlite3 *db, int nField, UnpackedRecord *p){ + assert( db!=0 ); if( p ){ int i; for(i=0; iaMem[i]; - if( pMem->zMalloc ) wx_sqlite3VdbeMemRelease(pMem); + if( pMem->zMalloc ) wx_sqlite3VdbeMemReleaseMalloc(pMem); } - wx_sqlite3DbFreeNN(db, p); + wx_sqlite3DbNNFreeNN(db, p); } } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ @@ -83644,7 +87792,8 @@ SQLITE_PRIVATE void wx_sqlite3VdbePreUpdateHook( const char *zDb, /* Database name */ Table *pTab, /* Modified table */ i64 iKey1, /* Initial key value */ - int iReg /* Register for new.* record */ + int iReg, /* Register for new.* record */ + int iBlobWrite ){ wx_sqlite3 *db = v->db; i64 iKey2; @@ -83665,6 +87814,8 @@ SQLITE_PRIVATE void wx_sqlite3VdbePreUpdateHook( } } + assert( pCsr!=0 ); + assert( pCsr->eCurType==CURTYPE_BTREE ); assert( pCsr->nField==pTab->nCol || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1) ); @@ -83680,6 +87831,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbePreUpdateHook( preupdate.iKey1 = iKey1; preupdate.iKey2 = iKey2; preupdate.pTab = pTab; + preupdate.iBlobWrite = iBlobWrite; db->pPreUpdate = &preupdate; db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); @@ -83692,7 +87844,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbePreUpdateHook( for(i=0; inField; i++){ wx_sqlite3VdbeMemRelease(&preupdate.aNew[i]); } - wx_sqlite3DbFreeNN(db, preupdate.aNew); + wx_sqlite3DbNNFreeNN(db, preupdate.aNew); } } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ @@ -83716,6 +87868,7 @@ SQLITE_PRIVATE void wx_sqlite3VdbePreUpdateHook( */ /* #include "sqliteInt.h" */ /* #include "vdbeInt.h" */ +/* #include "opcodes.h" */ #ifndef SQLITE_OMIT_DEPRECATED /* @@ -83809,7 +87962,9 @@ SQLITE_API int wx_sqlite3_finalize(wx_sqlite3_stmt *pStmt){ if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT; wx_sqlite3_mutex_enter(db->mutex); checkProfileCallback(db, v); - rc = wx_sqlite3VdbeFinalize(v); + assert( v->eVdbeState>=VDBE_READY_STATE ); + rc = wx_sqlite3VdbeReset(v); + wx_sqlite3VdbeDelete(v); rc = wx_sqlite3ApiExit(db, rc); wx_sqlite3LeaveMutexAndCloseZombie(db); } @@ -84017,6 +88172,9 @@ SQLITE_API int wx_sqlite3_value_type(wx_sqlite3_value* pVal){ #endif return aType[pVal->flags&MEM_AffMask]; } +SQLITE_API int wx_sqlite3_value_encoding(wx_sqlite3_value *pVal){ + return pVal->enc; +} /* Return true if a parameter to xUpdate represents an unchanged column */ SQLITE_API int wx_sqlite3_value_nochange(wx_sqlite3_value *pVal){ @@ -84046,6 +88204,9 @@ SQLITE_API wx_sqlite3_value *wx_sqlite3_value_dup(const wx_sqlite3_value *pOrig) wx_sqlite3ValueFree(pNew); pNew = 0; } + }else if( pNew->flags & MEM_Null ){ + /* Do not duplicate pointer values */ + pNew->flags &= ~(MEM_Term|MEM_Subtype); } return pNew; } @@ -84063,8 +88224,8 @@ SQLITE_API void wx_sqlite3_value_free(wx_sqlite3_value *pOld){ ** the function result. ** ** The setStrOrError() function calls wx_sqlite3VdbeMemSetStr() to store the -** result as a string or blob but if the string or blob is too large, it -** then sets the error code to SQLITE_TOOBIG +** result as a string or blob. Appropriate errors are set if the string/blob +** is too big or if an OOM occurs. ** ** The invokeValueDestructor(P,X) routine invokes destructor function X() ** on value P is not going to be used and need to be destroyed. @@ -84076,7 +88237,21 @@ static void setResultStrOrError( u8 enc, /* Encoding of z. 0 for BLOBs */ void (*xDel)(void*) /* Destructor function */ ){ - if( wx_sqlite3VdbeMemSetStr(pCtx->pOut, z, n, enc, xDel)==SQLITE_TOOBIG ){ + Mem *pOut = pCtx->pOut; + int rc = wx_sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel); + if( rc ){ + if( rc==SQLITE_TOOBIG ){ + wx_sqlite3_result_error_toobig(pCtx); + }else{ + /* The only errors possible from wx_sqlite3VdbeMemSetStr are + ** SQLITE_TOOBIG and SQLITE_NOMEM */ + assert( rc==SQLITE_NOMEM ); + wx_sqlite3_result_error_nomem(pCtx); + } + return; + } + wx_sqlite3VdbeChangeEncoding(pOut, pCtx->enc); + if( wx_sqlite3VdbeMemTooBig(pOut) ){ wx_sqlite3_result_error_toobig(pCtx); } } @@ -84093,7 +88268,7 @@ static int invokeValueDestructor( }else{ xDel((void*)p); } - if( pCtx ) wx_sqlite3_result_error_toobig(pCtx); + wx_sqlite3_result_error_toobig(pCtx); return SQLITE_TOOBIG; } SQLITE_API void wx_sqlite3_result_blob( @@ -84184,7 +88359,10 @@ SQLITE_API void wx_sqlite3_result_text64( ){ assert( wx_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); assert( xDel!=SQLITE_DYNAMIC ); - if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; + if( enc!=SQLITE_UTF8 ){ + if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; + n &= ~(u64)1; + } if( n>0x7fffffff ){ (void)invokeValueDestructor(z, xDel, pCtx); }else{ @@ -84199,7 +88377,7 @@ SQLITE_API void wx_sqlite3_result_text16( void (*xDel)(void *) ){ assert( wx_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel); + setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16NATIVE, xDel); } SQLITE_API void wx_sqlite3_result_text16be( wx_sqlite3_context *pCtx, @@ -84208,7 +88386,7 @@ SQLITE_API void wx_sqlite3_result_text16be( void (*xDel)(void *) ){ assert( wx_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel); + setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16BE, xDel); } SQLITE_API void wx_sqlite3_result_text16le( wx_sqlite3_context *pCtx, @@ -84217,25 +88395,34 @@ SQLITE_API void wx_sqlite3_result_text16le( void (*xDel)(void *) ){ assert( wx_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel); + setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16LE, xDel); } #endif /* SQLITE_OMIT_UTF16 */ SQLITE_API void wx_sqlite3_result_value(wx_sqlite3_context *pCtx, wx_sqlite3_value *pValue){ + Mem *pOut = pCtx->pOut; assert( wx_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - wx_sqlite3VdbeMemCopy(pCtx->pOut, pValue); + wx_sqlite3VdbeMemCopy(pOut, pValue); + wx_sqlite3VdbeChangeEncoding(pOut, pCtx->enc); + if( wx_sqlite3VdbeMemTooBig(pOut) ){ + wx_sqlite3_result_error_toobig(pCtx); + } } SQLITE_API void wx_sqlite3_result_zeroblob(wx_sqlite3_context *pCtx, int n){ - assert( wx_sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - wx_sqlite3VdbeMemSetZeroBlob(pCtx->pOut, n); + wx_sqlite3_result_zeroblob64(pCtx, n>0 ? n : 0); } SQLITE_API int wx_sqlite3_result_zeroblob64(wx_sqlite3_context *pCtx, u64 n){ Mem *pOut = pCtx->pOut; assert( wx_sqlite3_mutex_held(pOut->db->mutex) ); if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){ + wx_sqlite3_result_error_toobig(pCtx); return SQLITE_TOOBIG; } +#ifndef SQLITE_OMIT_INCRBLOB wx_sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n); return SQLITE_OK; +#else + return wx_sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n); +#endif } SQLITE_API void wx_sqlite3_result_error_code(wx_sqlite3_context *pCtx, int errCode){ pCtx->isError = errCode ? errCode : -1; @@ -84243,8 +88430,8 @@ SQLITE_API void wx_sqlite3_result_error_code(wx_sqlite3_context *pCtx, int errCo if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode; #endif if( pCtx->pOut->flags & MEM_Null ){ - wx_sqlite3VdbeMemSetStr(pCtx->pOut, wx_sqlite3ErrStr(errCode), -1, - SQLITE_UTF8, SQLITE_STATIC); + setResultStrOrError(pCtx, wx_sqlite3ErrStr(errCode), -1, SQLITE_UTF8, + SQLITE_STATIC); } } @@ -84318,80 +88505,83 @@ static int wx_sqlite3Step(Vdbe *p){ int rc; assert(p); - if( p->iVdbeMagic!=VDBE_MAGIC_RUN ){ - /* We used to require that wx_sqlite3_reset() be called before retrying - ** wx_sqlite3_step() after any error or after SQLITE_DONE. But beginning - ** with version 3.7.0, we changed this so that wx_sqlite3_reset() would - ** be called automatically instead of throwing the SQLITE_MISUSE error. - ** This "automatic-reset" change is not technically an incompatibility, - ** since any application that receives an SQLITE_MISUSE is broken by - ** definition. - ** - ** Nevertheless, some published applications that were originally written - ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE - ** returns, and those were broken by the automatic-reset change. As a - ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the - ** legacy behavior of returning SQLITE_MISUSE for cases where the - ** previous wx_sqlite3_step() returned something other than a SQLITE_LOCKED - ** or SQLITE_BUSY error. - */ -#ifdef SQLITE_OMIT_AUTORESET - if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){ - wx_sqlite3_reset((wx_sqlite3_stmt*)p); - }else{ - return SQLITE_MISUSE_BKPT; - } -#else - wx_sqlite3_reset((wx_sqlite3_stmt*)p); -#endif - } - - /* Check that malloc() has not failed. If it has, return early. */ db = p->db; - if( db->mallocFailed ){ - p->rc = SQLITE_NOMEM; - return SQLITE_NOMEM_BKPT; - } + if( p->eVdbeState!=VDBE_RUN_STATE ){ + restart_step: + if( p->eVdbeState==VDBE_READY_STATE ){ + if( p->expired ){ + p->rc = SQLITE_SCHEMA; + rc = SQLITE_ERROR; + if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ + /* If this statement was prepared using saved SQL and an + ** error has occurred, then return the error code in p->rc to the + ** caller. Set the error code in the database handle to the same + ** value. + */ + rc = wx_sqlite3VdbeTransferError(p); + } + goto end_of_step; + } - if( p->pc<0 && p->expired ){ - p->rc = SQLITE_SCHEMA; - rc = SQLITE_ERROR; - if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ - /* If this statement was prepared using saved SQL and an - ** error has occurred, then return the error code in p->rc to the - ** caller. Set the error code in the database handle to the same value. + /* If there are no other statements currently running, then + ** reset the interrupt flag. This prevents a call to wx_sqlite3_interrupt + ** from interrupting a statement that has not yet started. */ - rc = wx_sqlite3VdbeTransferError(p); - } - goto end_of_step; - } - if( p->pc<0 ){ - /* If there are no other statements currently running, then - ** reset the interrupt flag. This prevents a call to wx_sqlite3_interrupt - ** from interrupting a statement that has not yet started. - */ - if( db->nVdbeActive==0 ){ - AtomicStore(&db->u1.isInterrupted, 0); - } + if( db->nVdbeActive==0 ){ + AtomicStore(&db->u1.isInterrupted, 0); + } - assert( db->nVdbeWrite>0 || db->autoCommit==0 - || (db->nDeferredCons==0 && db->nDeferredImmCons==0) - ); + assert( db->nVdbeWrite>0 || db->autoCommit==0 + || (db->nDeferredCons==0 && db->nDeferredImmCons==0) + ); #ifndef SQLITE_OMIT_TRACE - if( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 - && !db->init.busy && p->zSql ){ - wx_sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); - }else{ - assert( p->startTime==0 ); - } + if( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 + && !db->init.busy && p->zSql ){ + wx_sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); + }else{ + assert( p->startTime==0 ); + } #endif - db->nVdbeActive++; - if( p->readOnly==0 ) db->nVdbeWrite++; - if( p->bIsReader ) db->nVdbeRead++; - p->pc = 0; + db->nVdbeActive++; + if( p->readOnly==0 ) db->nVdbeWrite++; + if( p->bIsReader ) db->nVdbeRead++; + p->pc = 0; + p->eVdbeState = VDBE_RUN_STATE; + }else + + if( ALWAYS(p->eVdbeState==VDBE_HALT_STATE) ){ + /* We used to require that wx_sqlite3_reset() be called before retrying + ** wx_sqlite3_step() after any error or after SQLITE_DONE. But beginning + ** with version 3.7.0, we changed this so that wx_sqlite3_reset() would + ** be called automatically instead of throwing the SQLITE_MISUSE error. + ** This "automatic-reset" change is not technically an incompatibility, + ** since any application that receives an SQLITE_MISUSE is broken by + ** definition. + ** + ** Nevertheless, some published applications that were originally written + ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE + ** returns, and those were broken by the automatic-reset change. As a + ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the + ** legacy behavior of returning SQLITE_MISUSE for cases where the + ** previous wx_sqlite3_step() returned something other than a SQLITE_LOCKED + ** or SQLITE_BUSY error. + */ +#ifdef SQLITE_OMIT_AUTORESET + if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){ + wx_sqlite3_reset((wx_sqlite3_stmt*)p); + }else{ + return SQLITE_MISUSE_BKPT; + } +#else + wx_sqlite3_reset((wx_sqlite3_stmt*)p); +#endif + assert( p->eVdbeState==VDBE_READY_STATE ); + goto restart_step; + } } + #ifdef SQLITE_DEBUG p->rcApp = SQLITE_OK; #endif @@ -84406,12 +88596,17 @@ static int wx_sqlite3Step(Vdbe *p){ db->nVdbeExec--; } - if( rc!=SQLITE_ROW ){ + if( rc==SQLITE_ROW ){ + assert( p->rc==SQLITE_OK ); + assert( db->mallocFailed==0 ); + db->errCode = SQLITE_ROW; + return SQLITE_ROW; + }else{ #ifndef SQLITE_OMIT_TRACE /* If the statement completed successfully, invoke the profile callback */ checkProfileCallback(db, p); #endif - + p->pResultRow = 0; if( rc==SQLITE_DONE && db->autoCommit ){ assert( p->rc==SQLITE_OK ); p->rc = doWalCallbacks(db); @@ -84458,7 +88653,6 @@ SQLITE_API int wx_sqlite3_step(wx_sqlite3_stmt *pStmt){ } db = v->db; wx_sqlite3_mutex_enter(db->mutex); - v->doingRerun = 0; while( (rc = wx_sqlite3Step(v))==SQLITE_SCHEMA && cnt++ < SQLITE_MAX_SCHEMA_RETRY ){ int savedPc = v->pc; @@ -84484,7 +88678,13 @@ SQLITE_API int wx_sqlite3_step(wx_sqlite3_stmt *pStmt){ break; } wx_sqlite3_reset(pStmt); - if( savedPc>=0 ) v->doingRerun = 1; + if( savedPc>=0 ){ + /* Setting minWriteFileFormat to 254 is a signal to the OP_Init and + ** OP_Trace opcodes to *not* perform SQLITE_TRACE_STMT because it has + ** already been done once on a prior invocation that failed due to + ** SQLITE_SCHEMA. tag-20220401a */ + v->minWriteFileFormat = 254; + } assert( v->expired==0 ); } wx_sqlite3_mutex_leave(db->mutex); @@ -84535,6 +88735,88 @@ SQLITE_API int wx_sqlite3_vtab_nochange(wx_sqlite3_context *p){ return wx_sqlite3_value_nochange(p->pOut); } +/* +** The destructor function for a ValueList object. This needs to be +** a separate function, unknowable to the application, to ensure that +** calls to wx_sqlite3_vtab_in_first()/wx_sqlite3_vtab_in_next() that are not +** preceeded by activation of IN processing via wx_sqlite3_vtab_int() do not +** try to access a fake ValueList object inserted by a hostile extension. +*/ +SQLITE_PRIVATE void wx_sqlite3VdbeValueListFree(void *pToDelete){ + wx_sqlite3_free(pToDelete); +} + +/* +** Implementation of wx_sqlite3_vtab_in_first() (if bNext==0) and +** wx_sqlite3_vtab_in_next() (if bNext!=0). +*/ +static int valueFromValueList( + wx_sqlite3_value *pVal, /* Pointer to the ValueList object */ + wx_sqlite3_value **ppOut, /* Store the next value from the list here */ + int bNext /* 1 for _next(). 0 for _first() */ +){ + int rc; + ValueList *pRhs; + + *ppOut = 0; + if( pVal==0 ) return SQLITE_MISUSE; + if( (pVal->flags & MEM_Dyn)==0 || pVal->xDel!=wx_sqlite3VdbeValueListFree ){ + return SQLITE_ERROR; + }else{ + assert( (pVal->flags&(MEM_TypeMask|MEM_Term|MEM_Subtype)) == + (MEM_Null|MEM_Term|MEM_Subtype) ); + assert( pVal->eSubtype=='p' ); + assert( pVal->u.zPType!=0 && strcmp(pVal->u.zPType,"ValueList")==0 ); + pRhs = (ValueList*)pVal->z; + } + if( bNext ){ + rc = wx_sqlite3BtreeNext(pRhs->pCsr, 0); + }else{ + int dummy = 0; + rc = wx_sqlite3BtreeFirst(pRhs->pCsr, &dummy); + assert( rc==SQLITE_OK || wx_sqlite3BtreeEof(pRhs->pCsr) ); + if( wx_sqlite3BtreeEof(pRhs->pCsr) ) rc = SQLITE_DONE; + } + if( rc==SQLITE_OK ){ + u32 sz; /* Size of current row in bytes */ + Mem sMem; /* Raw content of current row */ + memset(&sMem, 0, sizeof(sMem)); + sz = wx_sqlite3BtreePayloadSize(pRhs->pCsr); + rc = wx_sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,(int)sz,&sMem); + if( rc==SQLITE_OK ){ + u8 *zBuf = (u8*)sMem.z; + u32 iSerial; + wx_sqlite3_value *pOut = pRhs->pOut; + int iOff = 1 + getVarint32(&zBuf[1], iSerial); + wx_sqlite3VdbeSerialGet(&zBuf[iOff], iSerial, pOut); + pOut->enc = ENC(pOut->db); + if( (pOut->flags & MEM_Ephem)!=0 && wx_sqlite3VdbeMemMakeWriteable(pOut) ){ + rc = SQLITE_NOMEM; + }else{ + *ppOut = pOut; + } + } + wx_sqlite3VdbeMemRelease(&sMem); + } + return rc; +} + +/* +** Set the iterator value pVal to point to the first value in the set. +** Set (*ppOut) to point to this value before returning. +*/ +SQLITE_API int wx_sqlite3_vtab_in_first(wx_sqlite3_value *pVal, wx_sqlite3_value **ppOut){ + return valueFromValueList(pVal, ppOut, 0); +} + +/* +** Set the iterator value pVal to point to the next value in the set. +** Set (*ppOut) to point to this value before returning. +*/ +SQLITE_API int wx_sqlite3_vtab_in_next(wx_sqlite3_value *pVal, wx_sqlite3_value **ppOut){ + return valueFromValueList(pVal, ppOut, 1); +} + /* ** Return the current time for a statement. If the current time ** is requested more than once within the same run of a single prepared @@ -84706,7 +88988,7 @@ SQLITE_API int wx_sqlite3_column_count(wx_sqlite3_stmt *pStmt){ */ SQLITE_API int wx_sqlite3_data_count(wx_sqlite3_stmt *pStmt){ Vdbe *pVm = (Vdbe *)pStmt; - if( pVm==0 || pVm->pResultSet==0 ) return 0; + if( pVm==0 || pVm->pResultRow==0 ) return 0; return pVm->nResColumn; } @@ -84729,15 +89011,15 @@ static const Mem *columnNullValue(void){ #endif = { /* .u = */ {0}, + /* .z = */ (char*)0, + /* .n = */ (int)0, /* .flags = */ (u16)MEM_Null, /* .enc = */ (u8)0, /* .eSubtype = */ (u8)0, - /* .n = */ (int)0, - /* .z = */ (char*)0, - /* .zMalloc = */ (char*)0, + /* .db = */ (wx_sqlite3*)0, /* .szMalloc = */ (int)0, /* .uTemp = */ (u32)0, - /* .db = */ (wx_sqlite3*)0, + /* .zMalloc = */ (char*)0, /* .xDel = */ (void(*)(void*))0, #ifdef SQLITE_DEBUG /* .pScopyFrom = */ (Mem*)0, @@ -84761,8 +89043,8 @@ static Mem *columnMem(wx_sqlite3_stmt *pStmt, int i){ if( pVm==0 ) return (Mem*)columnNullValue(); assert( pVm->db ); wx_sqlite3_mutex_enter(pVm->db->mutex); - if( pVm->pResultSet!=0 && inResColumn && i>=0 ){ - pOut = &pVm->pResultSet[i]; + if( pVm->pResultRow!=0 && inResColumn && i>=0 ){ + pOut = &pVm->pResultRow[i]; }else{ wx_sqlite3Error(pVm->db, SQLITE_RANGE); pOut = (Mem*)columnNullValue(); @@ -85028,25 +89310,24 @@ SQLITE_API const void *wx_sqlite3_column_origin_name16(wx_sqlite3_stmt *pStmt, i ** The error code stored in database p->db is overwritten with the return ** value in any case. */ -static int vdbeUnbind(Vdbe *p, int i){ +static int vdbeUnbind(Vdbe *p, unsigned int i){ Mem *pVar; if( vdbeSafetyNotNull(p) ){ return SQLITE_MISUSE_BKPT; } wx_sqlite3_mutex_enter(p->db->mutex); - if( p->iVdbeMagic!=VDBE_MAGIC_RUN || p->pc>=0 ){ + if( p->eVdbeState!=VDBE_READY_STATE ){ wx_sqlite3Error(p->db, SQLITE_MISUSE); wx_sqlite3_mutex_leave(p->db->mutex); wx_sqlite3_log(SQLITE_MISUSE, "bind on a busy prepared statement: [%s]", p->zSql); return SQLITE_MISUSE_BKPT; } - if( i<1 || i>p->nVar ){ + if( i>=(unsigned int)p->nVar ){ wx_sqlite3Error(p->db, SQLITE_RANGE); wx_sqlite3_mutex_leave(p->db->mutex); return SQLITE_RANGE; } - i--; pVar = &p->aVar[i]; wx_sqlite3VdbeMemRelease(pVar); pVar->flags = MEM_Null; @@ -85075,7 +89356,7 @@ static int bindText( wx_sqlite3_stmt *pStmt, /* The statement to bind against */ int i, /* Index of the parameter to bind */ const void *zData, /* Pointer to the data to be bound */ - int nData, /* Number of bytes of data to be bound */ + i64 nData, /* Number of bytes of data to be bound */ void (*xDel)(void*), /* Destructor for the data */ u8 encoding /* Encoding for the data */ ){ @@ -85083,7 +89364,7 @@ static int bindText( Mem *pVar; int rc; - rc = vdbeUnbind(p, i); + rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ if( zData!=0 ){ pVar = &p->aVar[i-1]; @@ -85127,16 +89408,12 @@ SQLITE_API int wx_sqlite3_bind_blob64( void (*xDel)(void*) ){ assert( xDel!=SQLITE_DYNAMIC ); - if( nData>0x7fffffff ){ - return invokeValueDestructor(zData, xDel, 0); - }else{ - return bindText(pStmt, i, zData, (int)nData, xDel, 0); - } + return bindText(pStmt, i, zData, nData, xDel, 0); } SQLITE_API int wx_sqlite3_bind_double(wx_sqlite3_stmt *pStmt, int i, double rValue){ int rc; Vdbe *p = (Vdbe *)pStmt; - rc = vdbeUnbind(p, i); + rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ wx_sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue); wx_sqlite3_mutex_leave(p->db->mutex); @@ -85149,7 +89426,7 @@ SQLITE_API int wx_sqlite3_bind_int(wx_sqlite3_stmt *p, int i, int iValue){ SQLITE_API int wx_sqlite3_bind_int64(wx_sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ int rc; Vdbe *p = (Vdbe *)pStmt; - rc = vdbeUnbind(p, i); + rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ wx_sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue); wx_sqlite3_mutex_leave(p->db->mutex); @@ -85159,7 +89436,7 @@ SQLITE_API int wx_sqlite3_bind_int64(wx_sqlite3_stmt *pStmt, int i, sqlite_int64 SQLITE_API int wx_sqlite3_bind_null(wx_sqlite3_stmt *pStmt, int i){ int rc; Vdbe *p = (Vdbe*)pStmt; - rc = vdbeUnbind(p, i); + rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ wx_sqlite3_mutex_leave(p->db->mutex); } @@ -85174,7 +89451,7 @@ SQLITE_API int wx_sqlite3_bind_pointer( ){ int rc; Vdbe *p = (Vdbe*)pStmt; - rc = vdbeUnbind(p, i); + rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ wx_sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor); wx_sqlite3_mutex_leave(p->db->mutex); @@ -85201,22 +89478,21 @@ SQLITE_API int wx_sqlite3_bind_text64( unsigned char enc ){ assert( xDel!=SQLITE_DYNAMIC ); - if( nData>0x7fffffff ){ - return invokeValueDestructor(zData, xDel, 0); - }else{ + if( enc!=SQLITE_UTF8 ){ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; - return bindText(pStmt, i, zData, (int)nData, xDel, enc); + nData &= ~(u16)1; } + return bindText(pStmt, i, zData, nData, xDel, enc); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API int wx_sqlite3_bind_text16( wx_sqlite3_stmt *pStmt, int i, const void *zData, - int nData, + int n, void (*xDel)(void*) ){ - return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE); + return bindText(pStmt, i, zData, n & ~(u64)1, xDel, SQLITE_UTF16NATIVE); } #endif /* SQLITE_OMIT_UTF16 */ SQLITE_API int wx_sqlite3_bind_value(wx_sqlite3_stmt *pStmt, int i, const wx_sqlite3_value *pValue){ @@ -85227,7 +89503,10 @@ SQLITE_API int wx_sqlite3_bind_value(wx_sqlite3_stmt *pStmt, int i, const wx_sql break; } case SQLITE_FLOAT: { - rc = wx_sqlite3_bind_double(pStmt, i, pValue->u.r); + assert( pValue->flags & (MEM_Real|MEM_IntReal) ); + rc = wx_sqlite3_bind_double(pStmt, i, + (pValue->flags & MEM_Real) ? pValue->u.r : (double)pValue->u.i + ); break; } case SQLITE_BLOB: { @@ -85253,9 +89532,13 @@ SQLITE_API int wx_sqlite3_bind_value(wx_sqlite3_stmt *pStmt, int i, const wx_sql SQLITE_API int wx_sqlite3_bind_zeroblob(wx_sqlite3_stmt *pStmt, int i, int n){ int rc; Vdbe *p = (Vdbe *)pStmt; - rc = vdbeUnbind(p, i); + rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ +#ifndef SQLITE_OMIT_INCRBLOB wx_sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); +#else + rc = wx_sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); +#endif wx_sqlite3_mutex_leave(p->db->mutex); } return rc; @@ -85388,7 +89671,7 @@ SQLITE_API int wx_sqlite3_stmt_isexplain(wx_sqlite3_stmt *pStmt){ */ SQLITE_API int wx_sqlite3_stmt_busy(wx_sqlite3_stmt *pStmt){ Vdbe *v = (Vdbe*)pStmt; - return v!=0 && v->iVdbeMagic==VDBE_MAGIC_RUN && v->pc>=0; + return v!=0 && v->eVdbeState==VDBE_RUN_STATE; } /* @@ -85409,7 +89692,7 @@ SQLITE_API wx_sqlite3_stmt *wx_sqlite3_next_stmt(wx_sqlite3 *pDb, wx_sqlite3_stm if( pStmt==0 ){ pNext = (wx_sqlite3_stmt*)pDb->pVdbe; }else{ - pNext = (wx_sqlite3_stmt*)((Vdbe*)pStmt)->pNext; + pNext = (wx_sqlite3_stmt*)((Vdbe*)pStmt)->pVNext; } wx_sqlite3_mutex_leave(pDb->mutex); return pNext; @@ -85434,9 +89717,11 @@ SQLITE_API int wx_sqlite3_stmt_status(wx_sqlite3_stmt *pStmt, int op, int resetF wx_sqlite3_mutex_enter(db->mutex); v = 0; db->pnBytesFreed = (int*)&v; - wx_sqlite3VdbeClearObject(db, pVdbe); - wx_sqlite3DbFree(db, pVdbe); + assert( db->lookaside.pEnd==db->lookaside.pTrueEnd ); + db->lookaside.pEnd = db->lookaside.pStart; + wx_sqlite3VdbeDelete(pVdbe); db->pnBytesFreed = 0; + db->lookaside.pEnd = db->lookaside.pTrueEnd; wx_sqlite3_mutex_leave(db->mutex); }else{ v = pVdbe->aCounter[op]; @@ -85543,6 +89828,7 @@ SQLITE_API int wx_sqlite3_preupdate_old(wx_sqlite3 *db, int iIdx, wx_sqlite3_val u32 nRec; u8 *aRec; + assert( p->pCsr->eCurType==CURTYPE_BTREE ); nRec = wx_sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); aRec = wx_sqlite3DbMallocRaw(db, nRec); if( !aRec ) goto preupdate_old_out; @@ -85606,6 +89892,17 @@ SQLITE_API int wx_sqlite3_preupdate_depth(wx_sqlite3 *db){ } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +/* +** This function is designed to be called from within a pre-update callback +** only. +*/ +SQLITE_API int wx_sqlite3_preupdate_blobwrite(wx_sqlite3 *db){ + PreUpdate *p = db->pPreUpdate; + return (p ? p->iBlobWrite : -1); +} +#endif + #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** This function is called from within a pre-update callback to retrieve @@ -85686,23 +89983,60 @@ SQLITE_API int wx_sqlite3_preupdate_new(wx_sqlite3 *db, int iIdx, wx_sqlite3_val /* ** Return status data for a single loop within query pStmt. */ -SQLITE_API int wx_sqlite3_stmt_scanstatus( +SQLITE_API int wx_sqlite3_stmt_scanstatus_v2( wx_sqlite3_stmt *pStmt, /* Prepared statement being queried */ - int idx, /* Index of loop to report on */ + int iScan, /* Index of loop to report on */ int iScanStatusOp, /* Which metric to return */ + int flags, void *pOut /* OUT: Write the answer here */ ){ Vdbe *p = (Vdbe*)pStmt; ScanStatus *pScan; - if( idx<0 || idx>=p->nScan ) return 1; - pScan = &p->aScan[idx]; + int idx; + + if( iScan<0 ){ + int ii; + if( iScanStatusOp==SQLITE_SCANSTAT_NCYCLE ){ + i64 res = 0; + for(ii=0; iinOp; ii++){ + res += p->aOp[ii].nCycle; + } + *(i64*)pOut = res; + return 0; + } + return 1; + } + if( flags & SQLITE_SCANSTAT_COMPLEX ){ + idx = iScan; + pScan = &p->aScan[idx]; + }else{ + /* If the COMPLEX flag is clear, then this function must ignore any + ** ScanStatus structures with ScanStatus.addrLoop set to 0. */ + for(idx=0; idxnScan; idx++){ + pScan = &p->aScan[idx]; + if( pScan->zName ){ + iScan--; + if( iScan<0 ) break; + } + } + } + if( idx>=p->nScan ) return 1; + switch( iScanStatusOp ){ case SQLITE_SCANSTAT_NLOOP: { - *(wx_sqlite3_int64*)pOut = p->anExec[pScan->addrLoop]; + if( pScan->addrLoop>0 ){ + *(wx_sqlite3_int64*)pOut = p->aOp[pScan->addrLoop].nExec; + }else{ + *(wx_sqlite3_int64*)pOut = -1; + } break; } case SQLITE_SCANSTAT_NVISIT: { - *(wx_sqlite3_int64*)pOut = p->anExec[pScan->addrVisit]; + if( pScan->addrVisit>0 ){ + *(wx_sqlite3_int64*)pOut = p->aOp[pScan->addrVisit].nExec; + }else{ + *(wx_sqlite3_int64*)pOut = -1; + } break; } case SQLITE_SCANSTAT_EST: { @@ -85735,6 +90069,45 @@ SQLITE_API int wx_sqlite3_stmt_scanstatus( } break; } + case SQLITE_SCANSTAT_PARENTID: { + if( pScan->addrExplain ){ + *(int*)pOut = p->aOp[ pScan->addrExplain ].p2; + }else{ + *(int*)pOut = -1; + } + break; + } + case SQLITE_SCANSTAT_NCYCLE: { + i64 res = 0; + if( pScan->aAddrRange[0]==0 ){ + res = -1; + }else{ + int ii; + for(ii=0; iiaAddrRange); ii+=2){ + int iIns = pScan->aAddrRange[ii]; + int iEnd = pScan->aAddrRange[ii+1]; + if( iIns==0 ) break; + if( iIns>0 ){ + while( iIns<=iEnd ){ + res += p->aOp[iIns].nCycle; + iIns++; + } + }else{ + int iOp; + for(iOp=0; iOpnOp; iOp++){ + Op *pOp = &p->aOp[iOp]; + if( pOp->p1!=iEnd ) continue; + if( (wx_sqlite3OpcodeProperty[pOp->opcode] & OPFLG_NCYCLE)==0 ){ + continue; + } + res += p->aOp[iOp].nCycle; + } + } + } + } + *(i64*)pOut = res; + break; + } default: { return 1; } @@ -85742,12 +90115,29 @@ SQLITE_API int wx_sqlite3_stmt_scanstatus( return 0; } +/* +** Return status data for a single loop within query pStmt. +*/ +SQLITE_API int wx_sqlite3_stmt_scanstatus( + wx_sqlite3_stmt *pStmt, /* Prepared statement being queried */ + int iScan, /* Index of loop to report on */ + int iScanStatusOp, /* Which metric to return */ + void *pOut /* OUT: Write the answer here */ +){ + return wx_sqlite3_stmt_scanstatus_v2(pStmt, iScan, iScanStatusOp, 0, pOut); +} + /* ** Zero all counters associated with the wx_sqlite3_stmt_scanstatus() data. */ SQLITE_API void wx_sqlite3_stmt_scanstatus_reset(wx_sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe*)pStmt; - memset(p->anExec, 0, p->nOp * sizeof(i64)); + int ii; + for(ii=0; iinOp; ii++){ + Op *pOp = &p->aOp[ii]; + pOp->nExec = 0; + pOp->nCycle = 0; + } } #endif /* SQLITE_ENABLE_STMT_SCANSTATUS */ @@ -85839,11 +90229,9 @@ SQLITE_PRIVATE char *wx_sqlite3VdbeExpandSql( #ifndef SQLITE_OMIT_UTF16 Mem utf8; /* Used to convert UTF16 into UTF8 for display */ #endif - char zBase[100]; /* Initial working space */ db = p->db; - wx_sqlite3StrAccumInit(&out, 0, zBase, sizeof(zBase), - db->aLimit[SQLITE_LIMIT_LENGTH]); + wx_sqlite3StrAccumInit(&out, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); if( db->nVdbeExec>1 ){ while( *zRawSql ){ const char *zStart = zRawSql; @@ -86085,6 +90473,9 @@ SQLITE_API int wx_sqlite3_found_count = 0; */ static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){ static int n = 0; + (void)pc; + (void)pOp; + (void)v; n++; } #endif @@ -86193,7 +90584,6 @@ static VdbeCursor *allocateCursor( Vdbe *p, /* The virtual machine */ int iCur, /* Index of the new VdbeCursor */ int nField, /* Number of fields in the table or index */ - int iDb, /* Database the cursor belongs to, or -1 */ u8 eCurType /* Type of the new cursor */ ){ /* Find the memory cell that will be used to store the blob of memory @@ -86219,26 +90609,43 @@ static VdbeCursor *allocateCursor( int nByte; VdbeCursor *pCx = 0; nByte = - ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + + ROUND8P(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + (eCurType==CURTYPE_BTREE?wx_sqlite3BtreeCursorSize():0); assert( iCur>=0 && iCurnCursor ); if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ - wx_sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); + wx_sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]); p->apCsr[iCur] = 0; } - if( SQLITE_OK==wx_sqlite3VdbeMemClearAndResize(pMem, nByte) ){ - p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; - memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); - pCx->eCurType = eCurType; - pCx->iDb = iDb; - pCx->nField = nField; - pCx->aOffset = &pCx->aType[nField]; - if( eCurType==CURTYPE_BTREE ){ - pCx->uc.pCursor = (BtCursor*) - &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; - wx_sqlite3BtreeCursorZero(pCx->uc.pCursor); + + /* There used to be a call to wx_sqlite3VdbeMemClearAndResize() to make sure + ** the pMem used to hold space for the cursor has enough storage available + ** in pMem->zMalloc. But for the special case of the aMem[] entries used + ** to hold cursors, it is faster to in-line the logic. */ + assert( pMem->flags==MEM_Undefined ); + assert( (pMem->flags & MEM_Dyn)==0 ); + assert( pMem->szMalloc==0 || pMem->z==pMem->zMalloc ); + if( pMem->szMallocszMalloc>0 ){ + wx_sqlite3DbFreeNN(pMem->db, pMem->zMalloc); + } + pMem->z = pMem->zMalloc = wx_sqlite3DbMallocRaw(pMem->db, nByte); + if( pMem->zMalloc==0 ){ + pMem->szMalloc = 0; + return 0; } + pMem->szMalloc = nByte; + } + + p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->zMalloc; + memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); + pCx->eCurType = eCurType; + pCx->nField = nField; + pCx->aOffset = &pCx->aType[nField]; + if( eCurType==CURTYPE_BTREE ){ + pCx->uc.pCursor = (BtCursor*) + &pMem->z[ROUND8P(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; + wx_sqlite3BtreeCursorZero(pCx->uc.pCursor); } return pCx; } @@ -86250,7 +90657,8 @@ static VdbeCursor *allocateCursor( ** return false. */ static int alsoAnInt(Mem *pRec, double rValue, i64 *piValue){ - i64 iValue = (double)rValue; + i64 iValue; + iValue = wx_sqlite3RealToI64(rValue); if( wx_sqlite3RealSameAsInt(rValue,iValue) ){ *piValue = iValue; return 1; @@ -86306,6 +90714,10 @@ static void applyNumericAffinity(Mem *pRec, int bTryForInt){ ** always preferred, even if the affinity is REAL, because ** an integer representation is more space efficient on disk. ** +** SQLITE_AFF_FLEXNUM: +** If the value is text, then try to convert it into a number of +** some kind (integer or real) but do not make any other changes. +** ** SQLITE_AFF_TEXT: ** Convert pRec to a text representation. ** @@ -86320,11 +90732,11 @@ static void applyAffinity( ){ if( affinity>=SQLITE_AFF_NUMERIC ){ assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL - || affinity==SQLITE_AFF_NUMERIC ); + || affinity==SQLITE_AFF_NUMERIC || affinity==SQLITE_AFF_FLEXNUM ); if( (pRec->flags & MEM_Int)==0 ){ /*OPTIMIZATION-IF-FALSE*/ - if( (pRec->flags & MEM_Real)==0 ){ + if( (pRec->flags & (MEM_Real|MEM_IntReal))==0 ){ if( pRec->flags & MEM_Str ) applyNumericAffinity(pRec,1); - }else{ + }else if( affinity<=SQLITE_AFF_REAL ){ wx_sqlite3VdbeIntegerAffinity(pRec); } } @@ -86385,7 +90797,10 @@ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ wx_sqlite3_int64 ix; assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ); assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ); - ExpandBlob(pMem); + if( ExpandBlob(pMem) ){ + pMem->u.i = 0; + return MEM_Int; + } rc = wx_sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); if( rc<=0 ){ if( rc==0 && wx_sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1 ){ @@ -86409,17 +90824,18 @@ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ ** But it does set pMem->u.r and pMem->u.i appropriately. */ static u16 numericType(Mem *pMem){ - if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal) ){ + assert( (pMem->flags & MEM_Null)==0 + || pMem->db==0 || pMem->db->mallocFailed ); + if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null) ){ testcase( pMem->flags & MEM_Int ); testcase( pMem->flags & MEM_Real ); testcase( pMem->flags & MEM_IntReal ); - return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal); - } - if( pMem->flags & (MEM_Str|MEM_Blob) ){ - testcase( pMem->flags & MEM_Str ); - testcase( pMem->flags & MEM_Blob ); - return computeNumericType(pMem); + return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null); } + assert( pMem->flags & (MEM_Str|MEM_Blob) ); + testcase( pMem->flags & MEM_Str ); + testcase( pMem->flags & MEM_Blob ); + return computeNumericType(pMem); return 0; } @@ -86523,6 +90939,11 @@ static void registerTrace(int iReg, Mem *p){ printf("\n"); wx_sqlite3VdbeCheckMemInvariants(p); } +/**/ void wx_sqlite3PrintMem(Mem *pMem){ + memTracePrint(pMem); + printf("\n"); + fflush(stdout); +} #endif #ifdef SQLITE_DEBUG @@ -86543,106 +90964,6 @@ SQLITE_PRIVATE void wx_sqlite3VdbeRegisterDump(Vdbe *v){ # define REGISTER_TRACE(R,M) #endif - -#ifdef VDBE_PROFILE - -/* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of vdbe.c *********************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** 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 inline asm code for retrieving "high-performance" -** counters for x86 and x86_64 class CPUs. -*/ -#ifndef SQLITE_HWTIME_H -#define SQLITE_HWTIME_H - -/* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if !defined(__STRICT_ANSI__) && \ - (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) - - #if defined(__GNUC__) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } - - #elif defined(_MSC_VER) - - __declspec(naked) __inline sqlite_uint64 __cdecl wx_sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } - } - - #endif - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; - } - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) - - __inline__ sqlite_uint64 wx_sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; - } - -#else - - /* - ** asm() is needed for hardware timing support. Without asm(), - ** disable the wx_sqlite3Hwtime() routine. - ** - ** wx_sqlite3Hwtime() is only used for some obscure debugging - ** and analysis configurations, not in any deliverable, so this - ** should not be a great loss. - */ -SQLITE_PRIVATE sqlite_uint64 wx_sqlite3Hwtime(void){ return ((sqlite_uint64)0); } - -#endif - -#endif /* !defined(SQLITE_HWTIME_H) */ - -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in vdbe.c ***********************/ - -#endif - #ifndef NDEBUG /* ** This function is only called from within an assert() expression. It @@ -86686,6 +91007,41 @@ static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){ } } +/* +** Compute a bloom filter hash using pOp->p4.i registers from aMem[] beginning +** with pOp->p3. Return the hash. +*/ +static u64 filterHash(const Mem *aMem, const Op *pOp){ + int i, mx; + u64 h = 0; + + assert( pOp->p4type==P4_INT32 ); + for(i=pOp->p3, mx=i+pOp->p4.i; iflags & (MEM_Int|MEM_IntReal) ){ + h += p->u.i; + }else if( p->flags & MEM_Real ){ + h += wx_sqlite3VdbeIntValue(p); + }else if( p->flags & (MEM_Str|MEM_Blob) ){ + /* no-op */ + } + } + return h; +} + +/* +** Return the symbolic name for the data type of a pMem +*/ +static const char *vdbeMemTypeName(Mem *pMem){ + static const char *azTypes[] = { + /* SQLITE_INTEGER */ "INT", + /* SQLITE_FLOAT */ "REAL", + /* SQLITE_TEXT */ "TEXT", + /* SQLITE_BLOB */ "BLOB", + /* SQLITE_NULL */ "NULL" + }; + return azTypes[wx_sqlite3_value_type(pMem)-1]; +} /* ** Execute as much of a VDBE program as we can. @@ -86696,11 +91052,10 @@ SQLITE_PRIVATE int wx_sqlite3VdbeExec( ){ Op *aOp = p->aOp; /* Copy of p->aOp */ Op *pOp = aOp; /* Current operation */ -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) - Op *pOrigOp; /* Value of pOp at the top of the loop */ -#endif #ifdef SQLITE_DEBUG + Op *pOrigOp; /* Value of pOp at the top of the loop */ int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */ + u8 iCompareIsInit = 0; /* iCompare is initialized */ #endif int rc = SQLITE_OK; /* Value to return */ wx_sqlite3 *db = p->db; /* The database */ @@ -86716,13 +91071,15 @@ SQLITE_PRIVATE int wx_sqlite3VdbeExec( Mem *pIn2 = 0; /* 2nd input operand */ Mem *pIn3 = 0; /* 3rd input operand */ Mem *pOut = 0; /* Output operand */ -#ifdef VDBE_PROFILE - u64 start; /* CPU clock count at start of opcode */ +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) + u64 *pnCycle = 0; #endif /*** INSERT STACK UNION HERE ***/ - assert( p->iVdbeMagic==VDBE_MAGIC_RUN ); /* wx_sqlite3_step() verifies this */ - wx_sqlite3VdbeEnter(p); + assert( p->eVdbeState==VDBE_RUN_STATE ); /* wx_sqlite3_step() verifies this */ + if( DbMaskNonZero(p->lockMask) ){ + wx_sqlite3VdbeEnter(p); + } #ifndef SQLITE_OMIT_PROGRESS_CALLBACK if( db->xProgress ){ u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP]; @@ -86743,7 +91100,6 @@ SQLITE_PRIVATE int wx_sqlite3VdbeExec( assert( p->bIsReader || p->readOnly!=0 ); p->iCurrentTime = 0; assert( p->explain==0 ); - p->pResultSet = 0; db->busyHandler.nBusy = 0; if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; wx_sqlite3VdbeIOTraceSql(p); @@ -86780,12 +91136,14 @@ SQLITE_PRIVATE int wx_sqlite3VdbeExec( assert( rc==SQLITE_OK ); assert( pOp>=aOp && pOp<&aOp[p->nOp]); -#ifdef VDBE_PROFILE - start = wx_sqlite3NProfileCnt ? wx_sqlite3NProfileCnt : wx_sqlite3Hwtime(); -#endif nVmStep++; -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - if( p->anExec ) p->anExec[(int)(pOp-aOp)]++; +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) + pOp->nExec++; + pnCycle = &pOp->nCycle; +# ifdef VDBE_PROFILE + if( wx_sqlite3NProfileCnt==0 ) +# endif + *pnCycle -= wx_sqlite3Hwtime(); #endif /* Only allow tracing if SQLITE_DEBUG is defined. @@ -86847,7 +91205,7 @@ SQLITE_PRIVATE int wx_sqlite3VdbeExec( } } #endif -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) +#ifdef SQLITE_DEBUG pOrigOp = pOp; #endif @@ -86964,24 +91322,39 @@ case OP_Gosub: { /* jump */ pIn1->flags = MEM_Int; pIn1->u.i = (int)(pOp-aOp); REGISTER_TRACE(pOp->p1, pIn1); - - /* Most jump operations do a goto to this spot in order to update - ** the pOp pointer. */ -jump_to_p2: - pOp = &aOp[pOp->p2 - 1]; - break; + goto jump_to_p2_and_check_for_interrupt; } -/* Opcode: Return P1 * * * * +/* Opcode: Return P1 P2 P3 * * +** +** Jump to the address stored in register P1. If P1 is a return address +** register, then this accomplishes a return from a subroutine. +** +** If P3 is 1, then the jump is only taken if register P1 holds an integer +** values, otherwise execution falls through to the next opcode, and the +** OP_Return becomes a no-op. If P3 is 0, then register P1 must hold an +** integer or else an assert() is raised. P3 should be set to 1 when +** this opcode is used in combination with OP_BeginSubrtn, and set to 0 +** otherwise. ** -** Jump to the next instruction after the address in register P1. After -** the jump, register P1 becomes undefined. +** The value in register P1 is unchanged by this opcode. +** +** P2 is not used by the byte-code engine. However, if P2 is positive +** and also less than the current address, then the "EXPLAIN" output +** formatter in the CLI will indent all opcodes from the P2 opcode up +** to be not including the current Return. P2 should be the first opcode +** in the subroutine from which this opcode is returning. Thus the P2 +** value is a byte-code indentation hint. See tag-20220407a in +** wherecode.c and shell.c. */ case OP_Return: { /* in1 */ pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags==MEM_Int ); - pOp = &aOp[pIn1->u.i]; - pIn1->flags = MEM_Undefined; + if( pIn1->flags & MEM_Int ){ + if( pOp->p3 ){ VdbeBranchTaken(1, 2); } + pOp = &aOp[pIn1->u.i]; + }else if( ALWAYS(pOp->p3) ){ + VdbeBranchTaken(0, 2); + } break; } @@ -87004,7 +91377,14 @@ case OP_InitCoroutine: { /* jump */ assert( !VdbeMemDynamic(pOut) ); pOut->u.i = pOp->p3 - 1; pOut->flags = MEM_Int; - if( pOp->p2 ) goto jump_to_p2; + if( pOp->p2==0 ) break; + + /* Most jump operations do a goto to this spot in order to update + ** the pOp pointer. */ +jump_to_p2: + assert( pOp->p2>0 ); /* There are never any jumps to instruction 0 */ + assert( pOp->p2nOp ); /* Jumps must be in range */ + pOp = &aOp[pOp->p2 - 1]; break; } @@ -87106,11 +91486,16 @@ case OP_Halt: { VdbeFrame *pFrame; int pcx; - pcx = (int)(pOp - aOp); #ifdef SQLITE_DEBUG if( pOp->p2==OE_Abort ){ wx_sqlite3VdbeAssertAbortable(p); } #endif - if( pOp->p1==SQLITE_OK && p->pFrame ){ + + /* A deliberately coded "OP_Halt SQLITE_INTERNAL * * * *" opcode indicates + ** something is wrong with the code generator. Raise an assertion in order + ** to bring this to the attention of fuzzers and other testing tools. */ + assert( pOp->p1!=SQLITE_INTERNAL ); + + if( p->pFrame && pOp->p1==SQLITE_OK ){ /* Halt the sub-program. Return control to the parent frame. */ pFrame = p->pFrame; p->pFrame = pFrame->pParent; @@ -87132,7 +91517,6 @@ case OP_Halt: { } p->rc = pOp->p1; p->errorAction = (u8)pOp->p2; - p->pc = pcx; assert( pOp->p5<=4 ); if( p->rc ){ if( pOp->p5 ){ @@ -87149,6 +91533,7 @@ case OP_Halt: { }else{ wx_sqlite3VdbeError(p, "%s", pOp->p4.z); } + pcx = (int)(pOp - aOp); wx_sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg); } rc = wx_sqlite3VdbeHalt(p); @@ -87274,6 +91659,28 @@ case OP_String: { /* out2 */ break; } +/* Opcode: BeginSubrtn * P2 * * * +** Synopsis: r[P2]=NULL +** +** Mark the beginning of a subroutine that can be entered in-line +** or that can be called using OP_Gosub. The subroutine should +** be terminated by an OP_Return instruction that has a P1 operand that +** is the same as the P2 operand to this opcode and that has P3 set to 1. +** If the subroutine is entered in-line, then the OP_Return will simply +** fall through. But if the subroutine is entered using OP_Gosub, then +** the OP_Return will jump back to the first instruction after the OP_Gosub. +** +** This routine works by loading a NULL into the P2 register. When the +** return address register contains a NULL, the OP_Return instruction is +** a no-op that simply falls through to the next instruction (assuming that +** the OP_Return opcode has a P3 value of 1). Thus if the subroutine is +** entered in-line, then the OP_Return will cause in-line execution to +** continue. But if the subroutine is entered via OP_Gosub, then the +** OP_Return will cause a return to the address following the OP_Gosub. +** +** This opcode is identical to OP_Null. It has a different name +** only to make the byte code easier to read and verify. +*/ /* Opcode: Null P1 P2 P3 * * ** Synopsis: r[P2..P3]=NULL ** @@ -87286,6 +91693,7 @@ case OP_String: { /* out2 */ ** NULL values will not compare equal even if SQLITE_NULLEQ is set on ** OP_Ne or OP_Eq. */ +case OP_BeginSubrtn: case OP_Null: { /* out2 */ int cnt; u16 nullFlag; @@ -87327,12 +91735,18 @@ case OP_SoftNull: { ** Synopsis: r[P2]=P4 (len=P1) ** ** P4 points to a blob of data P1 bytes long. Store this -** blob in register P2. +** blob in register P2. If P4 is a NULL pointer, then construct +** a zero-filled blob that is P1 bytes long in P2. */ case OP_Blob: { /* out2 */ assert( pOp->p1 <= SQLITE_MAX_LENGTH ); pOut = out2Prerelease(p, pOp); - wx_sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); + if( pOp->p4.z==0 ){ + wx_sqlite3VdbeMemSetZeroBlob(pOut, pOp->p1); + if( wx_sqlite3VdbeMemExpandBlob(pOut) ) goto no_mem; + }else{ + wx_sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); + } pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; @@ -87410,11 +91824,16 @@ case OP_Move: { break; } -/* Opcode: Copy P1 P2 P3 * * +/* Opcode: Copy P1 P2 P3 * P5 ** Synopsis: r[P2@P3+1]=r[P1@P3+1] ** ** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. ** +** If the 0x0002 bit of P5 is set then also clear the MEM_Subtype flag in the +** destination. The 0x0001 bit of P5 indicates that this Copy opcode cannot +** be merged. The 0x0001 bit is used by the query planner and does not +** come into play during query execution. +** ** This instruction makes a deep copy of the value. A duplicate ** is made of any string or blob constant. See also OP_SCopy. */ @@ -87429,6 +91848,9 @@ case OP_Copy: { memAboutToChange(p, pOut); wx_sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); Deephemeralize(pOut); + if( (pOut->flags & MEM_Subtype)!=0 && (pOp->p5 & 0x0002)!=0 ){ + pOut->flags &= ~MEM_Subtype; + } #ifdef SQLITE_DEBUG pOut->pScopyFrom = 0; #endif @@ -87481,24 +91903,22 @@ case OP_IntCopy: { /* out2 */ break; } -/* Opcode: ChngCntRow P1 P2 * * * -** Synopsis: output=r[P1] +/* Opcode: FkCheck * * * * * ** -** Output value in register P1 as the chance count for a DML statement, -** due to the "PRAGMA count_changes=ON" setting. Or, if there was a -** foreign key error in the statement, trigger the error now. +** Halt with an SQLITE_CONSTRAINT error if there are any unresolved +** foreign key constraint violations. If there are no foreign key +** constraint violations, this is a no-op. ** -** This opcode is a variant of OP_ResultRow that checks the foreign key -** immediate constraint count and throws an error if the count is -** non-zero. The P2 opcode must be 1. +** FK constraint violations are also checked when the prepared statement +** exits. This opcode is used to raise foreign key constraint errors prior +** to returning results such as a row change count or the result of a +** RETURNING clause. */ -case OP_ChngCntRow: { - assert( pOp->p2==1 ); +case OP_FkCheck: { if( (rc = wx_sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){ goto abort_due_to_error; } - /* Fall through to the next case, OP_ResultRow */ - /* no break */ deliberate_fall_through + break; } /* Opcode: ResultRow P1 P2 * * * @@ -87511,45 +91931,32 @@ case OP_ChngCntRow: { ** the result row. */ case OP_ResultRow: { - Mem *pMem; - int i; assert( p->nResColumn==pOp->p2 ); - assert( pOp->p1>0 ); + assert( pOp->p1>0 || CORRUPT_DB ); assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); - /* Invalidate all ephemeral cursor row caches */ p->cacheCtr = (p->cacheCtr + 2)|1; - - /* Make sure the results of the current row are \000 terminated - ** and have an assigned type. The results are de-ephemeralized as - ** a side effect. - */ - pMem = p->pResultSet = &aMem[pOp->p1]; - for(i=0; ip2; i++){ - assert( memIsValid(&pMem[i]) ); - Deephemeralize(&pMem[i]); - assert( (pMem[i].flags & MEM_Ephem)==0 - || (pMem[i].flags & (MEM_Str|MEM_Blob))==0 ); - wx_sqlite3VdbeMemNulTerminate(&pMem[i]); - REGISTER_TRACE(pOp->p1+i, &pMem[i]); + p->pResultRow = &aMem[pOp->p1]; #ifdef SQLITE_DEBUG - /* The registers in the result will not be used again when the - ** prepared statement restarts. This is because wx_sqlite3_column() - ** APIs might have caused type conversions of made other changes to - ** the register values. Therefore, we can go ahead and break any - ** OP_SCopy dependencies. */ - pMem[i].pScopyFrom = 0; -#endif + { + Mem *pMem = p->pResultRow; + int i; + for(i=0; ip2; i++){ + assert( memIsValid(&pMem[i]) ); + REGISTER_TRACE(pOp->p1+i, &pMem[i]); + /* The registers in the result will not be used again when the + ** prepared statement restarts. This is because wx_sqlite3_column() + ** APIs might have caused type conversions of made other changes to + ** the register values. Therefore, we can go ahead and break any + ** OP_SCopy dependencies. */ + pMem[i].pScopyFrom = 0; + } } +#endif if( db->mallocFailed ) goto no_mem; - if( db->mTrace & SQLITE_TRACE_ROW ){ db->trace.xV2(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); } - - - /* Return SQLITE_ROW - */ p->pc = (int)(pOp - aOp) + 1; rc = SQLITE_ROW; goto vdbe_return; @@ -87604,7 +92011,7 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } - if( wx_sqlite3VdbeMemGrow(pOut, (int)nByte+3, pOut==pIn2) ){ + if( wx_sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){ goto no_mem; } MemSetTypeFlag(pOut, MEM_Str); @@ -87616,9 +92023,9 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n); assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); pIn1->flags = flags1; + if( encoding>SQLITE_UTF8 ) nByte &= ~1; pOut->z[nByte]=0; pOut->z[nByte+1] = 0; - pOut->z[nByte+2] = 0; pOut->flags |= MEM_Term; pOut->n = (int)nByte; pOut->enc = encoding; @@ -87669,7 +92076,6 @@ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */ case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */ case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ - u16 flags; /* Combined MEM_* flags from both inputs */ u16 type1; /* Numeric type of left operand */ u16 type2; /* Numeric type of right operand */ i64 iA; /* Integer value of left operand */ @@ -87678,12 +92084,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ double rB; /* Real value of right operand */ pIn1 = &aMem[pOp->p1]; - type1 = numericType(pIn1); + type1 = pIn1->flags; pIn2 = &aMem[pOp->p2]; - type2 = numericType(pIn2); + type2 = pIn2->flags; pOut = &aMem[pOp->p3]; - flags = pIn1->flags | pIn2->flags; if( (type1 & type2 & MEM_Int)!=0 ){ +int_math: iA = pIn1->u.i; iB = pIn2->u.i; switch( pOp->opcode ){ @@ -87705,9 +92111,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ } pOut->u.i = iB; MemSetTypeFlag(pOut, MEM_Int); - }else if( (flags & MEM_Null)!=0 ){ + }else if( ((type1 | type2) & MEM_Null)!=0 ){ goto arithmetic_result_is_null; }else{ + type1 = numericType(pIn1); + type2 = numericType(pIn2); + if( (type1 & type2 & MEM_Int)!=0 ) goto int_math; fp_math: rA = wx_sqlite3VdbeRealValue(pIn1); rB = wx_sqlite3VdbeRealValue(pIn2); @@ -87956,8 +92365,7 @@ case OP_Cast: { /* in1 */ ** Synopsis: IF r[P3]==r[P1] ** ** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then -** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then -** store the result of comparison in register P2. +** jump to address P2. ** ** The SQLITE_AFF_MASK portion of P5 must be an affinity character - ** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made @@ -87983,9 +92391,8 @@ case OP_Cast: { /* in1 */ ** If neither operand is NULL the result is the same as it would be if ** the SQLITE_NULLEQ flag were omitted from P5. ** -** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the -** content of r[P2] is only changed if the new value is NULL or 0 (false). -** In other words, a prior r[P2] value will not be overwritten by 1 (true). +** This opcode saves the result of comparison for use by the new +** OP_Jump opcode. */ /* Opcode: Ne P1 P2 P3 P4 P5 ** Synopsis: IF r[P3]!=r[P1] @@ -87993,17 +92400,12 @@ case OP_Cast: { /* in1 */ ** This works just like the Eq opcode except that the jump is taken if ** the operands in registers P1 and P3 are not equal. See the Eq opcode for ** additional information. -** -** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the -** content of r[P2] is only changed if the new value is NULL or 1 (true). -** In other words, a prior r[P2] value will not be overwritten by 0 (false). */ /* Opcode: Lt P1 P2 P3 P4 P5 ** Synopsis: IF r[P3]p3]; flags1 = pIn1->flags; flags3 = pIn3->flags; + if( (flags1 & flags3 & MEM_Int)!=0 ){ + /* Common case of comparison of two integers */ + if( pIn3->u.i > pIn1->u.i ){ + if( wx_sqlite3aGTb[pOp->opcode] ){ + VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); + goto jump_to_p2; + } + iCompare = +1; + VVA_ONLY( iCompareIsInit = 1; ) + }else if( pIn3->u.i < pIn1->u.i ){ + if( wx_sqlite3aLTb[pOp->opcode] ){ + VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); + goto jump_to_p2; + } + iCompare = -1; + VVA_ONLY( iCompareIsInit = 1; ) + }else{ + if( wx_sqlite3aEQb[pOp->opcode] ){ + VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); + goto jump_to_p2; + } + iCompare = 0; + VVA_ONLY( iCompareIsInit = 1; ) + } + VdbeBranchTaken(0, (pOp->p5 & SQLITE_NULLEQ)?2:3); + break; + } if( (flags1 | flags3)&MEM_Null ){ /* One or both operands are NULL */ if( pOp->p5 & SQLITE_NULLEQ ){ @@ -88085,43 +92517,30 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ ** then the result is always NULL. ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. */ - if( pOp->p5 & SQLITE_STOREP2 ){ - pOut = &aMem[pOp->p2]; - iCompare = 1; /* Operands are not equal */ - memAboutToChange(p, pOut); - MemSetTypeFlag(pOut, MEM_Null); - REGISTER_TRACE(pOp->p2, pOut); - }else{ - VdbeBranchTaken(2,3); - if( pOp->p5 & SQLITE_JUMPIFNULL ){ - goto jump_to_p2; - } + VdbeBranchTaken(2,3); + if( pOp->p5 & SQLITE_JUMPIFNULL ){ + goto jump_to_p2; } + iCompare = 1; /* Operands are not equal */ + VVA_ONLY( iCompareIsInit = 1; ) break; } }else{ - /* Neither operand is NULL. Do a comparison. */ + /* Neither operand is NULL and we couldn't do the special high-speed + ** integer comparison case. So do a general-case comparison. */ affinity = pOp->p5 & SQLITE_AFF_MASK; if( affinity>=SQLITE_AFF_NUMERIC ){ if( (flags1 | flags3)&MEM_Str ){ if( (flags1 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ applyNumericAffinity(pIn1,0); - testcase( flags3==pIn3->flags ); + assert( flags3==pIn3->flags || CORRUPT_DB ); flags3 = pIn3->flags; } if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ applyNumericAffinity(pIn3,0); } } - /* Handle the common case of integer comparison here, as an - ** optimization, to avoid a call to wx_sqlite3MemCompare() */ - if( (pIn1->flags & pIn3->flags & MEM_Int)!=0 ){ - if( pIn3->u.i > pIn1->u.i ){ res = +1; goto compare_op; } - if( pIn3->u.i < pIn1->u.i ){ res = -1; goto compare_op; } - res = 0; - goto compare_op; - } - }else if( affinity==SQLITE_AFF_TEXT ){ + }else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){ if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ testcase( pIn1->flags & MEM_Int ); testcase( pIn1->flags & MEM_Real ); @@ -88143,7 +92562,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); res = wx_sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); } -compare_op: + /* At this point, res is negative, zero, or positive if reg[P1] is ** less than, equal to, or greater than reg[P3], respectively. Compute ** the answer to this operator in res2, depending on what the comparison @@ -88152,16 +92571,15 @@ compare_op: ** order: NE, EQ, GT, LE, LT, GE */ assert( OP_Eq==OP_Ne+1 ); assert( OP_Gt==OP_Ne+2 ); assert( OP_Le==OP_Ne+3 ); assert( OP_Lt==OP_Ne+4 ); assert( OP_Ge==OP_Ne+5 ); - if( res<0 ){ /* ne, eq, gt, le, lt, ge */ - static const unsigned char aLTb[] = { 1, 0, 0, 1, 1, 0 }; - res2 = aLTb[pOp->opcode - OP_Ne]; + if( res<0 ){ + res2 = wx_sqlite3aLTb[pOp->opcode]; }else if( res==0 ){ - static const unsigned char aEQb[] = { 0, 1, 0, 1, 0, 1 }; - res2 = aEQb[pOp->opcode - OP_Ne]; + res2 = wx_sqlite3aEQb[pOp->opcode]; }else{ - static const unsigned char aGTb[] = { 1, 0, 1, 0, 0, 1 }; - res2 = aGTb[pOp->opcode - OP_Ne]; + res2 = wx_sqlite3aGTb[pOp->opcode]; } + iCompare = res; + VVA_ONLY( iCompareIsInit = 1; ) /* Undo any changes made by applyAffinity() to the input registers. */ assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) ); @@ -88169,67 +92587,40 @@ compare_op: assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); pIn1->flags = flags1; - if( pOp->p5 & SQLITE_STOREP2 ){ - pOut = &aMem[pOp->p2]; - iCompare = res; - if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){ - /* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1 - ** and prevents OP_Ne from overwriting NULL with 0. This flag - ** is only used in contexts where either: - ** (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0) - ** (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1) - ** Therefore it is not necessary to check the content of r[P2] for - ** NULL. */ - assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq ); - assert( res2==0 || res2==1 ); - testcase( res2==0 && pOp->opcode==OP_Eq ); - testcase( res2==1 && pOp->opcode==OP_Eq ); - testcase( res2==0 && pOp->opcode==OP_Ne ); - testcase( res2==1 && pOp->opcode==OP_Ne ); - if( (pOp->opcode==OP_Eq)==res2 ) break; - } - memAboutToChange(p, pOut); - MemSetTypeFlag(pOut, MEM_Int); - pOut->u.i = res2; - REGISTER_TRACE(pOp->p2, pOut); - }else{ - VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); - if( res2 ){ - goto jump_to_p2; - } + VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); + if( res2 ){ + goto jump_to_p2; } break; } -/* Opcode: ElseNotEq * P2 * * * +/* Opcode: ElseEq * P2 * * * ** ** This opcode must follow an OP_Lt or OP_Gt comparison operator. There ** can be zero or more OP_ReleaseReg opcodes intervening, but no other ** opcodes are allowed to occur between this instruction and the previous -** OP_Lt or OP_Gt. Furthermore, the prior OP_Lt or OP_Gt must have the -** SQLITE_STOREP2 bit set in the P5 field. +** OP_Lt or OP_Gt. ** ** If result of an OP_Eq comparison on the same two operands as the -** prior OP_Lt or OP_Gt would have been NULL or false (0), then then -** jump to P2. If the result of an OP_Eq comparison on the two previous -** operands would have been true (1), then fall through. +** prior OP_Lt or OP_Gt would have been true, then jump to P2. +** If the result of an OP_Eq comparison on the two previous +** operands would have been false or NULL, then fall through. */ -case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */ +case OP_ElseEq: { /* same as TK_ESCAPE, jump */ #ifdef SQLITE_DEBUG /* Verify the preconditions of this opcode - that it follows an OP_Lt or - ** OP_Gt with the SQLITE_STOREP2 flag set, with zero or more intervening - ** OP_ReleaseReg opcodes */ + ** OP_Gt with zero or more intervening OP_ReleaseReg opcodes */ int iAddr; for(iAddr = (int)(pOp - aOp) - 1; ALWAYS(iAddr>=0); iAddr--){ if( aOp[iAddr].opcode==OP_ReleaseReg ) continue; assert( aOp[iAddr].opcode==OP_Lt || aOp[iAddr].opcode==OP_Gt ); - assert( aOp[iAddr].p5 & SQLITE_STOREP2 ); break; } #endif /* SQLITE_DEBUG */ - VdbeBranchTaken(iCompare!=0, 2); - if( iCompare!=0 ) goto jump_to_p2; + assert( iCompareIsInit ); + VdbeBranchTaken(iCompare==0, 2); + if( iCompare==0 ) goto jump_to_p2; break; } @@ -88239,9 +92630,8 @@ case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */ ** Set the permutation used by the OP_Compare operator in the next ** instruction. The permutation is stored in the P4 operand. ** -** The permutation is only valid until the next OP_Compare that has -** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should -** occur immediately prior to the OP_Compare. +** The permutation is only valid for the next opcode which must be +** an OP_Compare that has the OPFLAG_PERMUTE bit set in P5. ** ** The first integer in the P4 integer array is the length of the array ** and does not become part of the permutation. @@ -88273,6 +92663,8 @@ case OP_Permutation: { ** The comparison is a sort comparison, so NULLs compare equal, ** NULLs are less than numbers, numbers are less than strings, ** and strings are less than blobs. +** +** This opcode must be immediately followed by an OP_Jump opcode. */ case OP_Compare: { int n; @@ -88321,6 +92713,7 @@ case OP_Compare: { pColl = pKeyInfo->aColl[i]; bRev = (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC); iCompare = wx_sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl); + VVA_ONLY( iCompareIsInit = 1; ) if( iCompare ){ if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) && ((aMem[p1+idx].flags & MEM_Null) || (aMem[p2+idx].flags & MEM_Null)) @@ -88331,6 +92724,7 @@ case OP_Compare: { break; } } + assert( pOp[1].opcode==OP_Jump ); break; } @@ -88339,8 +92733,12 @@ case OP_Compare: { ** Jump to the instruction at address P1, P2, or P3 depending on whether ** in the most recent OP_Compare instruction the P1 vector was less than ** equal to, or greater than the P2 vector, respectively. +** +** This opcode must immediately follow an OP_Compare opcode. */ case OP_Jump: { /* jump */ + assert( pOp>aOp && pOp[-1].opcode==OP_Compare ); + assert( iCompareIsInit ); if( iCompare<0 ){ VdbeBranchTaken(0,4); pOp = &aOp[pOp->p1 - 1]; }else if( iCompare==0 ){ @@ -88540,6 +92938,111 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ break; } +/* Opcode: IsType P1 P2 P3 P4 P5 +** Synopsis: if typeof(P1.P3) in P5 goto P2 +** +** Jump to P2 if the type of a column in a btree is one of the types specified +** by the P5 bitmask. +** +** P1 is normally a cursor on a btree for which the row decode cache is +** valid through at least column P3. In other words, there should have been +** a prior OP_Column for column P3 or greater. If the cursor is not valid, +** then this opcode might give spurious results. +** The the btree row has fewer than P3 columns, then use P4 as the +** datatype. +** +** If P1 is -1, then P3 is a register number and the datatype is taken +** from the value in that register. +** +** P5 is a bitmask of data types. SQLITE_INTEGER is the least significant +** (0x01) bit. SQLITE_FLOAT is the 0x02 bit. SQLITE_TEXT is 0x04. +** SQLITE_BLOB is 0x08. SQLITE_NULL is 0x10. +** +** Take the jump to address P2 if and only if the datatype of the +** value determined by P1 and P3 corresponds to one of the bits in the +** P5 bitmask. +** +*/ +case OP_IsType: { /* jump */ + VdbeCursor *pC; + u16 typeMask; + u32 serialType; + + assert( pOp->p1>=(-1) && pOp->p1nCursor ); + assert( pOp->p1>=0 || (pOp->p3>=0 && pOp->p3<=(p->nMem+1 - p->nCursor)) ); + if( pOp->p1>=0 ){ + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pOp->p3>=0 ); + if( pOp->p3nHdrParsed ){ + serialType = pC->aType[pOp->p3]; + if( serialType>=12 ){ + if( serialType&1 ){ + typeMask = 0x04; /* SQLITE_TEXT */ + }else{ + typeMask = 0x08; /* SQLITE_BLOB */ + } + }else{ + static const unsigned char aMask[] = { + 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x2, + 0x01, 0x01, 0x10, 0x10 + }; + testcase( serialType==0 ); + testcase( serialType==1 ); + testcase( serialType==2 ); + testcase( serialType==3 ); + testcase( serialType==4 ); + testcase( serialType==5 ); + testcase( serialType==6 ); + testcase( serialType==7 ); + testcase( serialType==8 ); + testcase( serialType==9 ); + testcase( serialType==10 ); + testcase( serialType==11 ); + typeMask = aMask[serialType]; + } + }else{ + typeMask = 1 << (pOp->p4.i - 1); + testcase( typeMask==0x01 ); + testcase( typeMask==0x02 ); + testcase( typeMask==0x04 ); + testcase( typeMask==0x08 ); + testcase( typeMask==0x10 ); + } + }else{ + assert( memIsValid(&aMem[pOp->p3]) ); + typeMask = 1 << (wx_sqlite3_value_type((wx_sqlite3_value*)&aMem[pOp->p3])-1); + testcase( typeMask==0x01 ); + testcase( typeMask==0x02 ); + testcase( typeMask==0x04 ); + testcase( typeMask==0x08 ); + testcase( typeMask==0x10 ); + } + VdbeBranchTaken( (typeMask & pOp->p5)!=0, 2); + if( typeMask & pOp->p5 ){ + goto jump_to_p2; + } + break; +} + +/* Opcode: ZeroOrNull P1 P2 P3 * * +** Synopsis: r[P2] = 0 OR NULL +** +** If all both registers P1 and P3 are NOT NULL, then store a zero in +** register P2. If either registers P1 or P3 are NULL then put +** a NULL in register P2. +*/ +case OP_ZeroOrNull: { /* in1, in2, out2, in3 */ + if( (aMem[pOp->p1].flags & MEM_Null)!=0 + || (aMem[pOp->p3].flags & MEM_Null)!=0 + ){ + wx_sqlite3VdbeMemSetNull(aMem + pOp->p2); + }else{ + wx_sqlite3VdbeMemSetInt64(aMem + pOp->p2, 0); + } + break; +} + /* Opcode: NotNull P1 P2 * * * ** Synopsis: if r[P1]!=NULL goto P2 ** @@ -88561,11 +93064,14 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ ** If it is, then set register P3 to NULL and jump immediately to P2. ** If P1 is not on a NULL row, then fall through without making any ** changes. +** +** If P1 is not an open cursor, then this opcode is a no-op. */ case OP_IfNullRow: { /* jump */ + VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( p->apCsr[pOp->p1]!=0 ); - if( p->apCsr[pOp->p1]->nullRow ){ + pC = p->apCsr[pOp->p1]; + if( ALWAYS(pC) && pC->nullRow ){ wx_sqlite3VdbeMemSetNull(aMem + pOp->p3); goto jump_to_p2; } @@ -88593,22 +93099,30 @@ case OP_Offset: { /* out3 */ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; pOut = &p->aMem[pOp->p3]; - if( NEVER(pC==0) || pC->eCurType!=CURTYPE_BTREE ){ + if( pC==0 || pC->eCurType!=CURTYPE_BTREE ){ wx_sqlite3VdbeMemSetNull(pOut); }else{ - wx_sqlite3VdbeMemSetInt64(pOut, wx_sqlite3BtreeOffset(pC->uc.pCursor)); + if( pC->deferredMoveto ){ + rc = wx_sqlite3VdbeFinishMoveto(pC); + if( rc ) goto abort_due_to_error; + } + if( wx_sqlite3BtreeEof(pC->uc.pCursor) ){ + wx_sqlite3VdbeMemSetNull(pOut); + }else{ + wx_sqlite3VdbeMemSetInt64(pOut, wx_sqlite3BtreeOffset(pC->uc.pCursor)); + } } break; } #endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */ /* Opcode: Column P1 P2 P3 P4 P5 -** Synopsis: r[P3]=PX +** Synopsis: r[P3]=PX cursor P1 column P2 ** ** Interpret the data that cursor P1 points to as a structure built using ** the MakeRecord instruction. (See the MakeRecord opcode for additional ** information about the format of the data.) Extract the P2-th column -** from this record. If there are less that (P2+1) +** from this record. If there are less than (P2+1) ** values in the record, extract a NULL. ** ** The value extracted is stored in register P3. @@ -88617,15 +93131,17 @@ case OP_Offset: { /* out3 */ ** if the P4 argument is a P4_MEM use the value of the P4 argument as ** the result. ** -** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then -** the result is guaranteed to only be used as the argument of a length() -** or typeof() function, respectively. The loading of large blobs can be -** skipped for length() and all content loading can be skipped for typeof(). +** If the OPFLAG_LENGTHARG bit is set in P5 then the result is guaranteed +** to only be used by the length() function or the equivalent. The content +** of large blobs is not loaded, thus saving CPU cycles. If the +** OPFLAG_TYPEOFARG bit is set then the result will only be used by the +** typeof() function or the IS NULL or IS NOT NULL operators or the +** equivalent. In this case, all content loading can be omitted. */ -case OP_Column: { +case OP_Column: { /* ncycle */ u32 p2; /* column number to retrieve */ VdbeCursor *pC; /* The VDBE cursor */ - BtCursor *pCrsr; /* The BTree cursor */ + BtCursor *pCrsr; /* The B-Tree cursor corresponding to pC */ u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ int len; /* The length of the serialized data for the column */ int i; /* Loop counter */ @@ -88639,43 +93155,53 @@ case OP_Column: { Mem *pReg; /* PseudoTable input register */ assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); p2 = (u32)pOp->p2; - /* If the cursor cache is stale (meaning it is not currently point at - ** the correct row) then bring it up-to-date by doing the necessary - ** B-Tree seek. */ - rc = wx_sqlite3VdbeCursorMoveto(&pC, &p2); - if( rc ) goto abort_due_to_error; - - assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); - pDest = &aMem[pOp->p3]; - memAboutToChange(p, pDest); +op_column_restart: assert( pC!=0 ); - assert( p2<(u32)pC->nField ); + assert( p2<(u32)pC->nField + || (pC->eCurType==CURTYPE_PSEUDO && pC->seekResult==0) ); aOffset = pC->aOffset; + assert( aOffset==pC->aType+pC->nField ); assert( pC->eCurType!=CURTYPE_VTAB ); assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); assert( pC->eCurType!=CURTYPE_SORTER ); if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/ if( pC->nullRow ){ - if( pC->eCurType==CURTYPE_PSEUDO ){ + if( pC->eCurType==CURTYPE_PSEUDO && pC->seekResult>0 ){ /* For the special case of as pseudo-cursor, the seekResult field ** identifies the register that holds the record */ - assert( pC->seekResult>0 ); pReg = &aMem[pC->seekResult]; assert( pReg->flags & MEM_Blob ); assert( memIsValid(pReg) ); pC->payloadSize = pC->szRow = pReg->n; pC->aRow = (u8*)pReg->z; }else{ + pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); wx_sqlite3VdbeMemSetNull(pDest); goto op_column_out; } }else{ pCrsr = pC->uc.pCursor; + if( pC->deferredMoveto ){ + u32 iMap; + assert( !pC->isEphemeral ); + if( pC->ub.aAltMap && (iMap = pC->ub.aAltMap[1+p2])>0 ){ + pC = pC->pAltCursor; + p2 = iMap - 1; + goto op_column_restart; + } + rc = wx_sqlite3VdbeFinishMoveto(pC); + if( rc ) goto abort_due_to_error; + }else if( wx_sqlite3BtreeCursorHasMoved(pCrsr) ){ + rc = wx_sqlite3VdbeHandleMovedCursor(pC); + if( rc ) goto abort_due_to_error; + goto op_column_restart; + } assert( pC->eCurType==CURTYPE_BTREE ); assert( pCrsr ); assert( wx_sqlite3BtreeCursorIsValid(pCrsr) ); @@ -88683,15 +93209,15 @@ case OP_Column: { pC->aRow = wx_sqlite3BtreePayloadFetch(pCrsr, &pC->szRow); assert( pC->szRow<=pC->payloadSize ); assert( pC->szRow<=65536 ); /* Maximum page size is 64KiB */ - if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } } pC->cacheStatus = p->cacheCtr; - pC->iHdrOffset = getVarint32(pC->aRow, aOffset[0]); + if( (aOffset[0] = pC->aRow[0])<0x80 ){ + pC->iHdrOffset = 1; + }else{ + pC->iHdrOffset = wx_sqlite3GetVarint32(pC->aRow, aOffset); + } pC->nHdrParsed = 0; - if( pC->szRowaRow does not have to hold the entire row, but it does at least ** need to cover the header of the record. If pC->aRow does not contain @@ -88731,6 +93257,10 @@ case OP_Column: { testcase( aOffset[0]==0 ); goto op_column_read_header; } + }else if( wx_sqlite3BtreeCursorHasMoved(pC->uc.pCursor) ){ + rc = wx_sqlite3VdbeHandleMovedCursor(pC); + if( rc ) goto abort_due_to_error; + goto op_column_restart; } /* Make sure at least the first p2+1 entries of the header have been @@ -88799,6 +93329,8 @@ case OP_Column: { ** columns. So the result will be either the default value or a NULL. */ if( pC->nHdrParsed<=p2 ){ + pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); if( pOp->p4type==P4_MEM ){ wx_sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static); }else{ @@ -88816,6 +93348,8 @@ case OP_Column: { */ assert( p2nHdrParsed ); assert( rc==SQLITE_OK ); + pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); assert( wx_sqlite3VdbeCheckMemInvariants(pDest) ); if( VdbeMemDynamic(pDest) ){ wx_sqlite3VdbeMemSetNull(pDest); @@ -88836,6 +93370,7 @@ case OP_Column: { pDest->n = len = (t-12)/2; pDest->enc = encoding; if( pDest->szMalloc < len+2 ){ + if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) goto too_big; pDest->flags = MEM_Null; if( wx_sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem; }else{ @@ -88868,6 +93403,7 @@ case OP_Column: { */ wx_sqlite3VdbeSerialGet((u8*)wx_sqlite3CtypeMap, t, pDest); }else{ + if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) goto too_big; rc = wx_sqlite3VdbeMemFromBtree(pC->uc.pCursor, aOffset[p2], len, pDest); if( rc!=SQLITE_OK ) goto abort_due_to_error; wx_sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); @@ -88890,6 +93426,110 @@ op_column_corrupt: } } +/* Opcode: TypeCheck P1 P2 P3 P4 * +** Synopsis: typecheck(r[P1@P2]) +** +** Apply affinities to the range of P2 registers beginning with P1. +** Take the affinities from the Table object in P4. If any value +** cannot be coerced into the correct type, then raise an error. +** +** This opcode is similar to OP_Affinity except that this opcode +** forces the register type to the Table column type. This is used +** to implement "strict affinity". +** +** GENERATED ALWAYS AS ... STATIC columns are only checked if P3 +** is zero. When P3 is non-zero, no type checking occurs for +** static generated columns. Virtual columns are computed at query time +** and so they are never checked. +** +** Preconditions: +** +**
      +**
    • P2 should be the number of non-virtual columns in the +** table of P4. +**
    • Table P4 should be a STRICT table. +**
    +** +** If any precondition is false, an assertion fault occurs. +*/ +case OP_TypeCheck: { + Table *pTab; + Column *aCol; + int i; + + assert( pOp->p4type==P4_TABLE ); + pTab = pOp->p4.pTab; + assert( pTab->tabFlags & TF_Strict ); + assert( pTab->nNVCol==pOp->p2 ); + aCol = pTab->aCol; + pIn1 = &aMem[pOp->p1]; + for(i=0; inCol; i++){ + if( aCol[i].colFlags & COLFLAG_GENERATED ){ + if( aCol[i].colFlags & COLFLAG_VIRTUAL ) continue; + if( pOp->p3 ){ pIn1++; continue; } + } + assert( pIn1 < &aMem[pOp->p1+pOp->p2] ); + applyAffinity(pIn1, aCol[i].affinity, encoding); + if( (pIn1->flags & MEM_Null)==0 ){ + switch( aCol[i].eCType ){ + case COLTYPE_BLOB: { + if( (pIn1->flags & MEM_Blob)==0 ) goto vdbe_type_error; + break; + } + case COLTYPE_INTEGER: + case COLTYPE_INT: { + if( (pIn1->flags & MEM_Int)==0 ) goto vdbe_type_error; + break; + } + case COLTYPE_TEXT: { + if( (pIn1->flags & MEM_Str)==0 ) goto vdbe_type_error; + break; + } + case COLTYPE_REAL: { + testcase( (pIn1->flags & (MEM_Real|MEM_IntReal))==MEM_Real ); + assert( (pIn1->flags & MEM_IntReal)==0 ); + if( pIn1->flags & MEM_Int ){ + /* When applying REAL affinity, if the result is still an MEM_Int + ** that will fit in 6 bytes, then change the type to MEM_IntReal + ** so that we keep the high-resolution integer value but know that + ** the type really wants to be REAL. */ + testcase( pIn1->u.i==140737488355328LL ); + testcase( pIn1->u.i==140737488355327LL ); + testcase( pIn1->u.i==-140737488355328LL ); + testcase( pIn1->u.i==-140737488355329LL ); + if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL){ + pIn1->flags |= MEM_IntReal; + pIn1->flags &= ~MEM_Int; + }else{ + pIn1->u.r = (double)pIn1->u.i; + pIn1->flags |= MEM_Real; + pIn1->flags &= ~MEM_Int; + } + }else if( (pIn1->flags & (MEM_Real|MEM_IntReal))==0 ){ + goto vdbe_type_error; + } + break; + } + default: { + /* COLTYPE_ANY. Accept anything. */ + break; + } + } + } + REGISTER_TRACE((int)(pIn1-aMem), pIn1); + pIn1++; + } + assert( pIn1 == &aMem[pOp->p1+pOp->p2] ); + break; + +vdbe_type_error: + wx_sqlite3VdbeError(p, "cannot store %s value in %s column %s.%s", + vdbeMemTypeName(pIn1), wx_sqlite3StdType[aCol[i].eCType-1], + pTab->zName, aCol[i].zCnName); + rc = SQLITE_CONSTRAINT_DATATYPE; + goto abort_due_to_error; +} + /* Opcode: Affinity P1 P2 * P4 * ** Synopsis: affinity(r[P1@P2]) ** @@ -88976,7 +93616,6 @@ case OP_MakeRecord: { Mem *pLast; /* Last field of the record */ int nField; /* Number of fields in the record */ char *zAffinity; /* The affinity string for the record */ - int file_format; /* File format to use for encoding */ u32 len; /* Length of a field */ u8 *zHdr; /* Where to write next byte of the header */ u8 *zPayload; /* Where to write next byte of the payload */ @@ -89005,7 +93644,6 @@ case OP_MakeRecord: { pData0 = &aMem[nField]; nField = pOp->p2; pLast = &pData0[nField-1]; - file_format = p->minWriteFileFormat; /* Identify the output register */ assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); @@ -89104,10 +93742,10 @@ case OP_MakeRecord: { testcase( uu==127 ); testcase( uu==128 ); testcase( uu==32767 ); testcase( uu==32768 ); testcase( uu==8388607 ); testcase( uu==8388608 ); - testcase( uu==2147483647 ); testcase( uu==2147483648 ); + testcase( uu==2147483647 ); testcase( uu==2147483648LL ); testcase( uu==140737488355327LL ); testcase( uu==140737488355328LL ); if( uu<=127 ){ - if( (i&1)==i && file_format>=4 ){ + if( (i&1)==i && p->minWriteFileFormat>=4 ){ pRec->uTemp = 8+(u32)uu; }else{ nData++; @@ -89212,18 +93850,60 @@ case OP_MakeRecord: { zPayload = zHdr + nHdr; /* Write the record */ - zHdr += putVarint32(zHdr, nHdr); + if( nHdr<0x80 ){ + *(zHdr++) = nHdr; + }else{ + zHdr += wx_sqlite3PutVarint(zHdr,nHdr); + } assert( pData0<=pLast ); pRec = pData0; - do{ + while( 1 /*exit-by-break*/ ){ serial_type = pRec->uTemp; /* EVIDENCE-OF: R-06529-47362 Following the size varint are one or more - ** additional varints, one per column. */ - zHdr += putVarint32(zHdr, serial_type); /* serial type */ - /* EVIDENCE-OF: R-64536-51728 The values for each column in the record + ** additional varints, one per column. + ** EVIDENCE-OF: R-64536-51728 The values for each column in the record ** immediately follow the header. */ - zPayload += wx_sqlite3VdbeSerialPut(zPayload, pRec, serial_type); /* content */ - }while( (++pRec)<=pLast ); + if( serial_type<=7 ){ + *(zHdr++) = serial_type; + if( serial_type==0 ){ + /* NULL value. No change in zPayload */ + }else{ + u64 v; + u32 i; + if( serial_type==7 ){ + assert( sizeof(v)==sizeof(pRec->u.r) ); + memcpy(&v, &pRec->u.r, sizeof(v)); + swapMixedEndianFloat(v); + }else{ + v = pRec->u.i; + } + len = i = wx_sqlite3SmallTypeSizes[serial_type]; + assert( i>0 ); + while( 1 /*exit-by-break*/ ){ + zPayload[--i] = (u8)(v&0xFF); + if( i==0 ) break; + v >>= 8; + } + zPayload += len; + } + }else if( serial_type<0x80 ){ + *(zHdr++) = serial_type; + if( serial_type>=14 && pRec->n>0 ){ + assert( pRec->z!=0 ); + memcpy(zPayload, pRec->z, pRec->n); + zPayload += pRec->n; + } + }else{ + zHdr += wx_sqlite3PutVarint(zHdr, serial_type); + if( pRec->n ){ + assert( pRec->z!=0 ); + memcpy(zPayload, pRec->z, pRec->n); + zPayload += pRec->n; + } + } + if( pRec==pLast ) break; + pRec++; + } assert( nHdr==(int)(zHdr - (u8*)pOut->z) ); assert( nByte==(int)(zPayload - (u8*)pOut->z) ); @@ -89232,7 +93912,7 @@ case OP_MakeRecord: { break; } -/* Opcode: Count P1 P2 p3 * * +/* Opcode: Count P1 P2 P3 * * ** Synopsis: r[P2]=count() ** ** Store the number of entries (an integer value) in the table or index @@ -89442,7 +94122,10 @@ case OP_Savepoint: { } } if( rc ) goto abort_due_to_error; - + if( p->eVdbeState==VDBE_HALT_STATE ){ + rc = SQLITE_DONE; + goto vdbe_return; + } break; } @@ -89546,6 +94229,7 @@ case OP_AutoCommit: { */ case OP_Transaction: { Btree *pBt; + Db *pDb; int iMeta = 0; assert( p->bIsReader ); @@ -89553,11 +94237,20 @@ case OP_Transaction: { assert( pOp->p2>=0 && pOp->p2<=2 ); assert( pOp->p1>=0 && pOp->p1nDb ); assert( DbMaskTest(p->btreeMask, pOp->p1) ); - if( pOp->p2 && (db->flags & SQLITE_QueryOnly)!=0 ){ - rc = SQLITE_READONLY; + assert( rc==SQLITE_OK ); + if( pOp->p2 && (db->flags & (SQLITE_QueryOnly|SQLITE_CorruptRdOnly))!=0 ){ + if( db->flags & SQLITE_QueryOnly ){ + /* Writes prohibited by the "PRAGMA query_only=TRUE" statement */ + rc = SQLITE_READONLY; + }else{ + /* Writes prohibited due to a prior SQLITE_CORRUPT in the current + ** transaction */ + rc = SQLITE_CORRUPT; + } goto abort_due_to_error; } - pBt = db->aDb[pOp->p1].pBt; + pDb = &db->aDb[pOp->p1]; + pBt = pDb->pBt; if( pBt ){ rc = wx_sqlite3BtreeBeginTrans(pBt, pOp->p2, &iMeta); @@ -89596,9 +94289,9 @@ case OP_Transaction: { } } assert( pOp->p5==0 || pOp->p4type==P4_INT32 ); - if( pOp->p5 - && (iMeta!=pOp->p3 - || db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i) + if( rc==SQLITE_OK + && pOp->p5 + && (iMeta!=pOp->p3 || pDb->pSchema->iGeneration!=pOp->p4.i) ){ /* ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema @@ -89625,6 +94318,11 @@ case OP_Transaction: { } p->expired = 1; rc = SQLITE_SCHEMA; + + /* Set changeCntOn to 0 to prevent the value returned by wx_sqlite3_changes() + ** from being modified in wx_sqlite3VdbeHalt(). If this statement is + ** reprepared, changeCntOn will be set again. */ + p->changeCntOn = 0; } if( rc ) goto abort_due_to_error; break; @@ -89691,8 +94389,9 @@ case OP_SetCookie: { rc = wx_sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3); if( pOp->p2==BTREE_SCHEMA_VERSION ){ /* When the schema cookie changes, record the new cookie internally */ - pDb->pSchema->schema_cookie = pOp->p3 - pOp->p5; + *(u32*)&pDb->pSchema->schema_cookie = *(u32*)&pOp->p3 - pOp->p5; db->mDbFlags |= DBFLAG_SchemaChange; + wx_sqlite3FkClearTriggerCache(db, pOp->p1); }else if( pOp->p2==BTREE_FILE_FORMAT ){ /* Record changes in the file format */ pDb->pSchema->file_format = pOp->p3; @@ -89791,7 +94490,7 @@ case OP_SetCookie: { ** ** See also: OP_OpenRead, OP_ReopenIdx */ -case OP_ReopenIdx: { +case OP_ReopenIdx: { /* ncycle */ int nField; KeyInfo *pKeyInfo; u32 p2; @@ -89806,11 +94505,13 @@ case OP_ReopenIdx: { pCur = p->apCsr[pOp->p1]; if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){ assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */ + assert( pCur->eCurType==CURTYPE_BTREE ); + wx_sqlite3BtreeClearCursor(pCur->uc.pCursor); goto open_cursor_set_hints; } /* If the cursor is not currently open or is open on a different ** index, then fall through into OP_OpenRead to force a reopen */ -case OP_OpenRead: +case OP_OpenRead: /* ncycle */ case OP_OpenWrite: assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); @@ -89868,8 +94569,9 @@ case OP_OpenWrite: assert( pOp->p1>=0 ); assert( nField>=0 ); testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */ - pCur = allocateCursor(p, pOp->p1, nField, iDb, CURTYPE_BTREE); + pCur = allocateCursor(p, pOp->p1, nField, CURTYPE_BTREE); if( pCur==0 ) goto no_mem; + pCur->iDb = iDb; pCur->nullRow = 1; pCur->isOrdered = 1; pCur->pgnoRoot = p2; @@ -89903,7 +94605,7 @@ open_cursor_set_hints: ** ** Duplicate ephemeral cursors are used for self-joins of materialized views. */ -case OP_OpenDup: { +case OP_OpenDup: { /* ncycle */ VdbeCursor *pOrig; /* The original cursor to be duplicated */ VdbeCursor *pCx; /* The new cursor */ @@ -89911,7 +94613,7 @@ case OP_OpenDup: { assert( pOrig ); assert( pOrig->isEphemeral ); /* Only ephemeral cursors can be duplicated */ - pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE); + pCx = allocateCursor(p, pOp->p1, pOrig->nField, CURTYPE_BTREE); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; pCx->isEphemeral = 1; @@ -89919,10 +94621,10 @@ case OP_OpenDup: { pCx->isTable = pOrig->isTable; pCx->pgnoRoot = pOrig->pgnoRoot; pCx->isOrdered = pOrig->isOrdered; - pCx->pBtx = pOrig->pBtx; - pCx->hasBeenDuped = 1; - pOrig->hasBeenDuped = 1; - rc = wx_sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, + pCx->ub.pBtx = pOrig->ub.pBtx; + pCx->noReuse = 1; + pOrig->noReuse = 1; + rc = wx_sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR, pCx->pKeyInfo, pCx->uc.pCursor); /* The wx_sqlite3BtreeCursor() routine can only fail for the first cursor ** opened for a database. Since there is already an open cursor when this @@ -89965,8 +94667,8 @@ case OP_OpenDup: { ** by this opcode will be used for automatically created transient ** indices in joins. */ -case OP_OpenAutoindex: -case OP_OpenEphemeral: { +case OP_OpenAutoindex: /* ncycle */ +case OP_OpenEphemeral: { /* ncycle */ VdbeCursor *pCx; KeyInfo *pKeyInfo; @@ -89988,23 +94690,23 @@ case OP_OpenEphemeral: { aMem[pOp->p3].z = ""; } pCx = p->apCsr[pOp->p1]; - if( pCx && !pCx->hasBeenDuped ){ + if( pCx && !pCx->noReuse && ALWAYS(pOp->p2<=pCx->nField) ){ /* If the ephermeral table is already open and has no duplicates from ** OP_OpenDup, then erase all existing content so that the table is ** empty again, rather than creating a new table. */ assert( pCx->isEphemeral ); pCx->seqCount = 0; pCx->cacheStatus = CACHE_STALE; - rc = wx_sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0); + rc = wx_sqlite3BtreeClearTable(pCx->ub.pBtx, pCx->pgnoRoot, 0); }else{ - pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); + pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_BTREE); if( pCx==0 ) goto no_mem; pCx->isEphemeral = 1; - rc = wx_sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, + rc = wx_sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->ub.pBtx, BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); if( rc==SQLITE_OK ){ - rc = wx_sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); + rc = wx_sqlite3BtreeBeginTrans(pCx->ub.pBtx, 1, 0); if( rc==SQLITE_OK ){ /* If a transient index is required, create it by calling ** wx_sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before @@ -90013,26 +94715,26 @@ case OP_OpenEphemeral: { */ if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ assert( pOp->p4type==P4_KEYINFO ); - rc = wx_sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, + rc = wx_sqlite3BtreeCreateTable(pCx->ub.pBtx, &pCx->pgnoRoot, BTREE_BLOBKEY | pOp->p5); if( rc==SQLITE_OK ){ assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); assert( pKeyInfo->db==db ); assert( pKeyInfo->enc==ENC(db) ); - rc = wx_sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, + rc = wx_sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR, pKeyInfo, pCx->uc.pCursor); } pCx->isTable = 0; }else{ pCx->pgnoRoot = SCHEMA_ROOT; - rc = wx_sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR, + rc = wx_sqlite3BtreeCursor(pCx->ub.pBtx, SCHEMA_ROOT, BTREE_WRCSR, 0, pCx->uc.pCursor); pCx->isTable = 1; } } pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); if( rc ){ - wx_sqlite3BtreeClose(pCx->pBtx); + wx_sqlite3BtreeClose(pCx->ub.pBtx); } } } @@ -90056,7 +94758,7 @@ case OP_SorterOpen: { assert( pOp->p1>=0 ); assert( pOp->p2>=0 ); - pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_SORTER); + pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_SORTER); if( pCx==0 ) goto no_mem; pCx->pKeyInfo = pOp->p4.pKeyInfo; assert( pCx->pKeyInfo->db==db ); @@ -90105,7 +94807,7 @@ case OP_OpenPseudo: { assert( pOp->p1>=0 ); assert( pOp->p3>=0 ); - pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, CURTYPE_PSEUDO); + pCx = allocateCursor(p, pOp->p1, pOp->p3, CURTYPE_PSEUDO); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; pCx->seekResult = pOp->p2; @@ -90124,7 +94826,7 @@ case OP_OpenPseudo: { ** Close a cursor previously opened as P1. If P1 is not ** currently open, this instruction is a no-op. */ -case OP_Close: { +case OP_Close: { /* ncycle */ assert( pOp->p1>=0 && pOp->p1nCursor ); wx_sqlite3VdbeFreeCursor(p, p->apCsr[pOp->p1]); p->apCsr[pOp->p1] = 0; @@ -90241,10 +94943,10 @@ case OP_ColumnsUsed: { ** ** See also: Found, NotFound, SeekGt, SeekGe, SeekLt */ -case OP_SeekLT: /* jump, in3, group */ -case OP_SeekLE: /* jump, in3, group */ -case OP_SeekGE: /* jump, in3, group */ -case OP_SeekGT: { /* jump, in3, group */ +case OP_SeekLT: /* jump, in3, group, ncycle */ +case OP_SeekLE: /* jump, in3, group, ncycle */ +case OP_SeekGE: /* jump, in3, group, ncycle */ +case OP_SeekGT: { /* jump, in3, group, ncycle */ int res; /* Comparison result */ int oc; /* Opcode */ VdbeCursor *pC; /* The cursor to seek */ @@ -90293,6 +94995,7 @@ case OP_SeekGT: { /* jump, in3, group */ /* If the P3 value could not be converted into an integer without ** loss of information, then special processing is required... */ if( (newType & (MEM_Int|MEM_IntReal))==0 ){ + int c; if( (newType & MEM_Real)==0 ){ if( (newType & MEM_Null) || oc>=OP_SeekGE ){ VdbeBranchTaken(1,2); @@ -90302,7 +95005,8 @@ case OP_SeekGT: { /* jump, in3, group */ if( rc!=SQLITE_OK ) goto abort_due_to_error; goto seek_not_found; } - }else + } + c = wx_sqlite3IntFloatCompare(iKey, pIn3->u.r); /* If the approximation iKey is larger than the actual real search ** term, substitute >= for > and < for <=. e.g. if the search term @@ -90311,7 +95015,7 @@ case OP_SeekGT: { /* jump, in3, group */ ** (x > 4.9) -> (x >= 5) ** (x <= 4.9) -> (x < 5) */ - if( pIn3->u.r<(double)iKey ){ + if( c>0 ){ assert( OP_SeekGE==(OP_SeekGT-1) ); assert( OP_SeekLT==(OP_SeekLE-1) ); assert( (OP_SeekLE & 0x0001)==(OP_SeekGT & 0x0001) ); @@ -90320,14 +95024,14 @@ case OP_SeekGT: { /* jump, in3, group */ /* If the approximation iKey is smaller than the actual real search ** term, substitute <= for < and > for >=. */ - else if( pIn3->u.r>(double)iKey ){ + else if( c<0 ){ assert( OP_SeekLE==(OP_SeekLT+1) ); assert( OP_SeekGT==(OP_SeekGE+1) ); assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) ); if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++; } } - rc = wx_sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)iKey, 0, &res); + rc = wx_sqlite3BtreeTableMoveto(pC->uc.pCursor, (u64)iKey, 0, &res); pC->movetoTarget = iKey; /* Used by OP_Delete */ if( rc!=SQLITE_OK ){ goto abort_due_to_error; @@ -90371,10 +95075,16 @@ case OP_SeekGT: { /* jump, in3, group */ r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG - { int i; for(i=0; i0 ) REGISTER_TRACE(pOp->p3+i, &r.aMem[i]); + } + } #endif r.eqSeen = 0; - rc = wx_sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, &r, 0, 0, &res); + rc = wx_sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } @@ -90434,7 +95144,7 @@ seek_not_found: } -/* Opcode: SeekScan P1 P2 * * * +/* Opcode: SeekScan P1 P2 * * P5 ** Synopsis: Scan-ahead up to P1 rows ** ** This opcode is a prefix opcode to OP_SeekGE. In other words, this @@ -90444,8 +95154,8 @@ seek_not_found: ** This opcode uses the P1 through P4 operands of the subsequent ** OP_SeekGE. In the text that follows, the operands of the subsequent ** OP_SeekGE opcode are denoted as SeekOP.P1 through SeekOP.P4. Only -** the P1 and P2 operands of this opcode are also used, and are called -** This.P1 and This.P2. +** the P1, P2 and P5 operands of this opcode are also used, and are called +** This.P1, This.P2 and This.P5. ** ** This opcode helps to optimize IN operators on a multi-column index ** where the IN operator is on the later terms of the index by avoiding @@ -90455,32 +95165,54 @@ seek_not_found: ** ** The SeekGE.P3 and SeekGE.P4 operands identify an unpacked key which ** is the desired entry that we want the cursor SeekGE.P1 to be pointing -** to. Call this SeekGE.P4/P5 row the "target". +** to. Call this SeekGE.P3/P4 row the "target". ** ** If the SeekGE.P1 cursor is not currently pointing to a valid row, ** then this opcode is a no-op and control passes through into the OP_SeekGE. ** ** If the SeekGE.P1 cursor is pointing to a valid row, then that row ** might be the target row, or it might be near and slightly before the -** target row. This opcode attempts to position the cursor on the target -** row by, perhaps by invoking wx_sqlite3BtreeStep() on the cursor -** between 0 and This.P1 times. -** -** There are three possible outcomes from this opcode:
      -** -**
    1. If after This.P1 steps, the cursor is still pointing to a place that -** is earlier in the btree than the target row, then fall through -** into the subsquence OP_SeekGE opcode. -** -**
    2. If the cursor is successfully moved to the target row by 0 or more -** wx_sqlite3BtreeNext() calls, then jump to This.P2, which will land just -** past the OP_IdxGT or OP_IdxGE opcode that follows the OP_SeekGE. -** -**
    3. If the cursor ends up past the target row (indicating the the target -** row does not exist in the btree) then jump to SeekOP.P2. +** target row, or it might be after the target row. If the cursor is +** currently before the target row, then this opcode attempts to position +** the cursor on or after the target row by invoking wx_sqlite3BtreeStep() +** on the cursor between 1 and This.P1 times. +** +** The This.P5 parameter is a flag that indicates what to do if the +** cursor ends up pointing at a valid row that is past the target +** row. If This.P5 is false (0) then a jump is made to SeekGE.P2. If +** This.P5 is true (non-zero) then a jump is made to This.P2. The P5==0 +** case occurs when there are no inequality constraints to the right of +** the IN constraing. The jump to SeekGE.P2 ends the loop. The P5!=0 case +** occurs when there are inequality constraints to the right of the IN +** operator. In that case, the This.P2 will point either directly to or +** to setup code prior to the OP_IdxGT or OP_IdxGE opcode that checks for +** loop terminate. +** +** Possible outcomes from this opcode:
        +** +**
      1. If the cursor is initally not pointed to any valid row, then +** fall through into the subsequent OP_SeekGE opcode. +** +**
      2. If the cursor is left pointing to a row that is before the target +** row, even after making as many as This.P1 calls to +** wx_sqlite3BtreeNext(), then also fall through into OP_SeekGE. +** +**
      3. If the cursor is left pointing at the target row, either because it +** was at the target row to begin with or because one or more +** wx_sqlite3BtreeNext() calls moved the cursor to the target row, +** then jump to This.P2.., +** +**
      4. If the cursor started out before the target row and a call to +** to wx_sqlite3BtreeNext() moved the cursor off the end of the index +** (indicating that the target row definitely does not exist in the +** btree) then jump to SeekGE.P2, ending the loop. +** +**
      5. If the cursor ends up on a valid row that is past the target row +** (indicating that the target row does not exist in the btree) then +** jump to SeekOP.P2 if This.P5==0 or to This.P2 if This.P5>0. **
      */ -case OP_SeekScan: { +case OP_SeekScan: { /* ncycle */ VdbeCursor *pC; int res; int nStep; @@ -90488,14 +95220,25 @@ case OP_SeekScan: { assert( pOp[1].opcode==OP_SeekGE ); - /* pOp->p2 points to the first instruction past the OP_IdxGT that - ** follows the OP_SeekGE. */ + /* If pOp->p5 is clear, then pOp->p2 points to the first instruction past the + ** OP_IdxGT that follows the OP_SeekGE. Otherwise, it points to the first + ** opcode past the OP_SeekGE itself. */ assert( pOp->p2>=(int)(pOp-aOp)+2 ); - assert( aOp[pOp->p2-1].opcode==OP_IdxGT || aOp[pOp->p2-1].opcode==OP_IdxGE ); - testcase( aOp[pOp->p2-1].opcode==OP_IdxGE ); - assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); - assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); - assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); +#ifdef SQLITE_DEBUG + if( pOp->p5==0 ){ + /* There are no inequality constraints following the IN constraint. */ + assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); + assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); + assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); + assert( aOp[pOp->p2-1].opcode==OP_IdxGT + || aOp[pOp->p2-1].opcode==OP_IdxGE ); + testcase( aOp[pOp->p2-1].opcode==OP_IdxGE ); + }else{ + /* There are inequality constraints. */ + assert( pOp->p2==(int)(pOp-aOp)+2 ); + assert( aOp[pOp->p2-1].opcode==OP_SeekGE ); + } +#endif assert( pOp->p1>0 ); pC = p->apCsr[pOp[1].p1]; @@ -90529,8 +95272,9 @@ case OP_SeekScan: { while(1){ rc = wx_sqlite3VdbeIdxKeyCompare(db, pC, &r, &res); if( rc ) goto abort_due_to_error; - if( res>0 ){ + if( res>0 && pOp->p5==0 ){ seekscan_search_fail: + /* Jump to SeekGE.P2, ending the loop */ #ifdef SQLITE_DEBUG if( db->flags&SQLITE_VdbeTrace ){ printf("... %d steps and then skip\n", pOp->p1 - nStep); @@ -90540,7 +95284,8 @@ case OP_SeekScan: { pOp++; goto jump_to_p2; } - if( res==0 ){ + if( res>=0 ){ + /* Jump to This.P2, bypassing the OP_SeekGE opcode */ #ifdef SQLITE_DEBUG if( db->flags&SQLITE_VdbeTrace ){ printf("... %d steps and then success\n", pOp->p1 - nStep); @@ -90589,15 +95334,25 @@ case OP_SeekScan: { ** ** P1 must be a valid b-tree cursor. */ -case OP_SeekHit: { +case OP_SeekHit: { /* ncycle */ VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pOp->p3>=pOp->p2 ); if( pC->seekHitp2 ){ +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + printf("seekHit changes from %d to %d\n", pC->seekHit, pOp->p2); + } +#endif pC->seekHit = pOp->p2; }else if( pC->seekHit>pOp->p3 ){ +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + printf("seekHit changes from %d to %d\n", pC->seekHit, pOp->p3); + } +#endif pC->seekHit = pOp->p3; } break; @@ -90606,12 +95361,16 @@ case OP_SeekHit: { /* Opcode: IfNotOpen P1 P2 * * * ** Synopsis: if( !csr[P1] ) goto P2 ** -** If cursor P1 is not open, jump to instruction P2. Otherwise, fall through. +** If cursor P1 is not open or if P1 is set to a NULL row using the +** OP_NullRow opcode, then jump to instruction P2. Otherwise, fall through. */ case OP_IfNotOpen: { /* jump */ + VdbeCursor *pCur; + assert( pOp->p1>=0 && pOp->p1nCursor ); - VdbeBranchTaken(p->apCsr[pOp->p1]==0, 2); - if( !p->apCsr[pOp->p1] ){ + pCur = p->apCsr[pOp->p1]; + VdbeBranchTaken(pCur==0 || pCur->nullRow, 2); + if( pCur==0 || pCur->nullRow ){ goto jump_to_p2_and_check_for_interrupt; } break; @@ -90707,24 +95466,26 @@ case OP_IfNotOpen: { /* jump */ ** ** See also: NotFound, Found, NotExists */ -case OP_IfNoHope: { /* jump, in3 */ +case OP_IfNoHope: { /* jump, in3, ncycle */ VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + printf("seekHit is %d\n", pC->seekHit); + } +#endif if( pC->seekHit>=pOp->p4.i ) break; /* Fall through into OP_NotFound */ /* no break */ deliberate_fall_through } -case OP_NoConflict: /* jump, in3 */ -case OP_NotFound: /* jump, in3 */ -case OP_Found: { /* jump, in3 */ +case OP_NoConflict: /* jump, in3, ncycle */ +case OP_NotFound: /* jump, in3, ncycle */ +case OP_Found: { /* jump, in3, ncycle */ int alreadyExists; - int takeJump; int ii; VdbeCursor *pC; - int res; - UnpackedRecord *pFree; UnpackedRecord *pIdxKey; UnpackedRecord r; @@ -90739,14 +95500,15 @@ case OP_Found: { /* jump, in3 */ #ifdef SQLITE_DEBUG pC->seekOp = pOp->opcode; #endif - pIn3 = &aMem[pOp->p3]; + r.aMem = &aMem[pOp->p3]; assert( pC->eCurType==CURTYPE_BTREE ); assert( pC->uc.pCursor!=0 ); assert( pC->isTable==0 ); - if( pOp->p4.i>0 ){ + r.nField = (u16)pOp->p4.i; + if( r.nField>0 ){ + /* Key values in an array of registers */ r.pKeyInfo = pC->pKeyInfo; - r.nField = (u16)pOp->p4.i; - r.aMem = pIn3; + r.default_rc = 0; #ifdef SQLITE_DEBUG for(ii=0; iip3+ii, &r.aMem[ii]); } #endif - pIdxKey = &r; - pFree = 0; + rc = wx_sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &pC->seekResult); }else{ - assert( pIn3->flags & MEM_Blob ); - rc = ExpandBlob(pIn3); + /* Composite key generated by OP_MakeRecord */ + assert( r.aMem->flags & MEM_Blob ); + assert( pOp->opcode!=OP_NoConflict ); + rc = ExpandBlob(r.aMem); assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); if( rc ) goto no_mem; - pFree = pIdxKey = wx_sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo); + pIdxKey = wx_sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo); if( pIdxKey==0 ) goto no_mem; - wx_sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); - } - pIdxKey->default_rc = 0; - takeJump = 0; - if( pOp->opcode==OP_NoConflict ){ - /* For the OP_NoConflict opcode, take the jump if any of the - ** input fields are NULL, since any key with a NULL will not - ** conflict */ - for(ii=0; iinField; ii++){ - if( pIdxKey->aMem[ii].flags & MEM_Null ){ - takeJump = 1; - break; - } - } + wx_sqlite3VdbeRecordUnpack(pC->pKeyInfo, r.aMem->n, r.aMem->z, pIdxKey); + pIdxKey->default_rc = 0; + rc = wx_sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &pC->seekResult); + wx_sqlite3DbFreeNN(db, pIdxKey); } - rc = wx_sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, pIdxKey, 0, 0, &res); - if( pFree ) wx_sqlite3DbFreeNN(db, pFree); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - pC->seekResult = res; - alreadyExists = (res==0); + alreadyExists = (pC->seekResult==0); pC->nullRow = 1-alreadyExists; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; @@ -90792,9 +95542,25 @@ case OP_Found: { /* jump, in3 */ VdbeBranchTaken(alreadyExists!=0,2); if( alreadyExists ) goto jump_to_p2; }else{ - VdbeBranchTaken(takeJump||alreadyExists==0,2); - if( takeJump || !alreadyExists ) goto jump_to_p2; - if( pOp->opcode==OP_IfNoHope ) pC->seekHit = pOp->p4.i; + if( !alreadyExists ){ + VdbeBranchTaken(1,2); + goto jump_to_p2; + } + if( pOp->opcode==OP_NoConflict ){ + /* For the OP_NoConflict opcode, take the jump if any of the + ** input fields are NULL, since any key with a NULL will not + ** conflict */ + for(ii=0; iiopcode==OP_IfNoHope ){ + pC->seekHit = pOp->p4.i; + } } break; } @@ -90846,7 +95612,7 @@ case OP_Found: { /* jump, in3 */ ** ** See also: Found, NotFound, NoConflict, SeekRowid */ -case OP_SeekRowid: { /* jump, in3 */ +case OP_SeekRowid: { /* jump, in3, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; @@ -90871,7 +95637,7 @@ case OP_SeekRowid: { /* jump, in3 */ } /* Fall through into OP_NotExists */ /* no break */ deliberate_fall_through -case OP_NotExists: /* jump, in3 */ +case OP_NotExists: /* jump, in3, ncycle */ pIn3 = &aMem[pOp->p3]; assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid ); assert( pOp->p1>=0 && pOp->p1nCursor ); @@ -90887,7 +95653,7 @@ notExistsWithKey: pCrsr = pC->uc.pCursor; assert( pCrsr!=0 ); res = 0; - rc = wx_sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res); + rc = wx_sqlite3BtreeTableMoveto(pCrsr, iKey, 0, &res); assert( rc==SQLITE_OK || res==0 ); pC->movetoTarget = iKey; /* Used by OP_Delete */ pC->nullRow = 0; @@ -91044,7 +95810,7 @@ case OP_NewRowid: { /* out2 */ do{ wx_sqlite3_randomness(sizeof(v), &v); v &= (MAX_ROWID>>1); v++; /* Ensure that v is greater than zero */ - }while( ((rc = wx_sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)v, + }while( ((rc = wx_sqlite3BtreeTableMoveto(pC->uc.pCursor, (u64)v, 0, &res))==SQLITE_OK) && (res==0) && (++cnt<100)); @@ -91134,14 +95900,14 @@ case OP_Insert: { assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) ); }else{ pTab = 0; - zDb = 0; /* Not needed. Silence a compiler warning. */ + zDb = 0; } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* Invoke the pre-update hook, if any */ if( pTab ){ if( db->xPreUpdateCallback && !(pOp->p5 & OPFLAG_ISUPDATE) ){ - wx_sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey,pOp->p2); + wx_sqlite3VdbePreUpdateHook(p,pC,SQLITE_INSERT,zDb,pTab,x.nKey,pOp->p2,-1); } if( db->xUpdateCallback==0 || pTab->aCol==0 ){ /* Prevent post-update hook from running in cases when it should not */ @@ -91151,8 +95917,11 @@ case OP_Insert: { if( pOp->p5 & OPFLAG_ISNOOP ) break; #endif - if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; + assert( (pOp->p5 & OPFLAG_LASTROWID)==0 || (pOp->p5 & OPFLAG_NCHANGE)!=0 ); + if( pOp->p5 & OPFLAG_NCHANGE ){ + p->nChange++; + if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; + } assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 ); x.pData = pData->z; x.nData = pData->n; @@ -91163,6 +95932,7 @@ case OP_Insert: { x.nZero = 0; } x.pKey = 0; + assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT ); rc = wx_sqlite3BtreeInsert(pC->uc.pCursor, &x, (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)), seekResult @@ -91287,13 +96057,14 @@ case OP_Delete: { pC->movetoTarget = wx_sqlite3BtreeIntegerKey(pC->uc.pCursor); } }else{ - zDb = 0; /* Not needed. Silence a compiler warning. */ - pTab = 0; /* Not needed. Silence a compiler warning. */ + zDb = 0; + pTab = 0; } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* Invoke the pre-update-hook if required. */ - if( db->xPreUpdateCallback && pOp->p4.pTab ){ + assert( db->xPreUpdateCallback==0 || pTab==pOp->p4.pTab ); + if( db->xPreUpdateCallback && pTab ){ assert( !(opflags & OPFLAG_ISUPDATE) || HasRowid(pTab)==0 || (aMem[pOp->p3].flags & MEM_Int) @@ -91301,7 +96072,7 @@ case OP_Delete: { wx_sqlite3VdbePreUpdateHook(p, pC, (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE, zDb, pTab, pC->movetoTarget, - pOp->p3 + pOp->p3, -1 ); } if( opflags & OPFLAG_ISNOOP ) break; @@ -91334,7 +96105,7 @@ case OP_Delete: { /* Invoke the update-hook if required. */ if( opflags & OPFLAG_NCHANGE ){ p->nChange++; - if( db->xUpdateCallback && HasRowid(pTab) ){ + if( db->xUpdateCallback && ALWAYS(pTab!=0) && HasRowid(pTab) ){ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName, pC->movetoTarget); assert( pC->iDb>=0 ); @@ -91484,7 +96255,7 @@ case OP_RowData: { } /* Opcode: Rowid P1 P2 * * * -** Synopsis: r[P2]=rowid +** Synopsis: r[P2]=PX rowid of P1 ** ** Store in register P2 an integer which is the key of the table entry that ** P1 is currently point to. @@ -91493,7 +96264,7 @@ case OP_RowData: { ** be a separate OP_VRowid opcode for use with virtual tables, but this ** one opcode now works for both table types. */ -case OP_Rowid: { /* out2 */ +case OP_Rowid: { /* out2, ncycle */ VdbeCursor *pC; i64 v; wx_sqlite3_vtab *pVtab; @@ -91539,13 +96310,25 @@ case OP_Rowid: { /* out2 */ ** Move the cursor P1 to a null row. Any OP_Column operations ** that occur while the cursor is on the null row will always ** write a NULL. +** +** If cursor P1 is not previously opened, open it now to a special +** pseudo-cursor that always returns NULL for every column. */ case OP_NullRow: { VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); + if( pC==0 ){ + /* If the cursor is not already open, create a special kind of + ** pseudo-cursor that always gives null rows. */ + pC = allocateCursor(p, pOp->p1, 1, CURTYPE_PSEUDO); + if( pC==0 ) goto no_mem; + pC->seekResult = 0; + pC->isTable = 1; + pC->noReuse = 1; + pC->uc.pCursor = wx_sqlite3BtreeFakeValidCursor(); + } pC->nullRow = 1; pC->cacheStatus = CACHE_STALE; if( pC->eCurType==CURTYPE_BTREE ){ @@ -91580,8 +96363,8 @@ case OP_NullRow: { ** from the end toward the beginning. In other words, the cursor is ** configured to use Prev, not Next. */ -case OP_SeekEnd: -case OP_Last: { /* jump */ +case OP_SeekEnd: /* ncycle */ +case OP_Last: { /* jump, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; @@ -91682,17 +96465,22 @@ case OP_Sort: { /* jump */ ** If the table or index is not empty, fall through to the following ** instruction. ** +** If P2 is zero, that is an assertion that the P1 table is never +** empty and hence the jump will never be taken. +** ** This opcode leaves the cursor configured to move in forward order, ** from the beginning toward the end. In other words, the cursor is ** configured to use Next, not Prev. */ -case OP_Rewind: { /* jump */ +case OP_Rewind: { /* jump, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p5==0 ); + assert( pOp->p2>=0 && pOp->p2nOp ); + pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) ); @@ -91712,13 +96500,14 @@ case OP_Rewind: { /* jump */ } if( rc ) goto abort_due_to_error; pC->nullRow = (u8)res; - assert( pOp->p2>0 && pOp->p2nOp ); - VdbeBranchTaken(res!=0,2); - if( res ) goto jump_to_p2; + if( pOp->p2>0 ){ + VdbeBranchTaken(res!=0,2); + if( res ) goto jump_to_p2; + } break; } -/* Opcode: Next P1 P2 P3 P4 P5 +/* Opcode: Next P1 P2 P3 * P5 ** ** Advance cursor P1 so that it points to the next key/data pair in its ** table or index. If there are no more key/value pairs then fall through @@ -91737,15 +96526,12 @@ case OP_Rewind: { /* jump */ ** omitted if that index had been unique. P3 is usually 0. P3 is ** always either 0 or 1. ** -** P4 is always of type P4_ADVANCE. The function pointer points to -** wx_sqlite3BtreeNext(). -** ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. ** ** See also: Prev */ -/* Opcode: Prev P1 P2 P3 P4 P5 +/* Opcode: Prev P1 P2 P3 * P5 ** ** Back up cursor P1 so that it points to the previous key/data pair in its ** table or index. If there is no previous key/value pairs then fall through @@ -91765,9 +96551,6 @@ case OP_Rewind: { /* jump */ ** omitted if that index had been unique. P3 is usually 0. P3 is ** always either 0 or 1. ** -** P4 is always of type P4_ADVANCE. The function pointer points to -** wx_sqlite3BtreePrevious(). -** ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. */ @@ -91785,30 +96568,37 @@ case OP_SorterNext: { /* jump */ assert( isSorter(pC) ); rc = wx_sqlite3VdbeSorterNext(db, pC); goto next_tail; -case OP_Prev: /* jump */ -case OP_Next: /* jump */ + +case OP_Prev: /* jump, ncycle */ assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p5aCounter) ); + assert( pOp->p5==0 + || pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP + || pOp->p5==SQLITE_STMTSTATUS_AUTOINDEX); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->deferredMoveto==0 ); assert( pC->eCurType==CURTYPE_BTREE ); - assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==wx_sqlite3BtreeNext ); - assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==wx_sqlite3BtreePrevious ); + assert( pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE + || pC->seekOp==OP_Last || pC->seekOp==OP_IfNoHope + || pC->seekOp==OP_NullRow); + rc = wx_sqlite3BtreePrevious(pC->uc.pCursor, pOp->p3); + goto next_tail; - /* The Next opcode is only used after SeekGT, SeekGE, Rewind, and Found. - ** The Prev opcode is only used after SeekLT, SeekLE, and Last. */ - assert( pOp->opcode!=OP_Next - || pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE +case OP_Next: /* jump, ncycle */ + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p5==0 + || pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP + || pOp->p5==SQLITE_STMTSTATUS_AUTOINDEX); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->deferredMoveto==0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid || pC->seekOp==OP_IfNoHope); - assert( pOp->opcode!=OP_Prev - || pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE - || pC->seekOp==OP_Last || pC->seekOp==OP_IfNoHope - || pC->seekOp==OP_NullRow); + rc = wx_sqlite3BtreeNext(pC->uc.pCursor, pOp->p3); - rc = pOp->p4.xAdvance(pC->uc.pCursor, pOp->p3); next_tail: pC->cacheStatus = CACHE_STALE; VdbeBranchTaken(rc==SQLITE_OK,2); @@ -91921,7 +96711,8 @@ case OP_SorterInsert: { /* in2 */ ** an UPDATE or DELETE statement and the index entry to be updated ** or deleted is not found. For some uses of IdxDelete ** (example: the EXCEPT operator) it does not matter that no matching -** entry is found. For those cases, P5 is zero. +** entry is found. For those cases, P5 is zero. Also, do not raise +** this (self-correcting and non-critical) error if in writable_schema mode. */ case OP_IdxDelete: { VdbeCursor *pC; @@ -91942,12 +96733,12 @@ case OP_IdxDelete: { r.nField = (u16)pOp->p3; r.default_rc = 0; r.aMem = &aMem[pOp->p2]; - rc = wx_sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); + rc = wx_sqlite3BtreeIndexMoveto(pCrsr, &r, &res); if( rc ) goto abort_due_to_error; if( res==0 ){ rc = wx_sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); if( rc ) goto abort_due_to_error; - }else if( pOp->p5 ){ + }else if( pOp->p5 && !wx_sqlite3WritableSchema(db) ){ rc = wx_sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption"); goto abort_due_to_error; } @@ -91985,8 +96776,8 @@ case OP_IdxDelete: { ** ** See also: Rowid, MakeRecord. */ -case OP_DeferredSeek: -case OP_IdxRowid: { /* out2 */ +case OP_DeferredSeek: /* ncycle */ +case OP_IdxRowid: { /* out2, ncycle */ VdbeCursor *pC; /* The P1 index cursor */ VdbeCursor *pTabCur; /* The P2 table cursor (OP_DeferredSeek only) */ i64 rowid; /* Rowid that P1 current points to */ @@ -91994,9 +96785,9 @@ case OP_IdxRowid: { /* out2 */ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->eCurType==CURTYPE_BTREE || IsNullCursor(pC) ); assert( pC->uc.pCursor!=0 ); - assert( pC->isTable==0 ); + assert( pC->isTable==0 || IsNullCursor(pC) ); assert( pC->deferredMoveto==0 ); assert( !pC->nullRow || pOp->opcode==OP_IdxRowid ); @@ -92004,10 +96795,10 @@ case OP_IdxRowid: { /* out2 */ ** of wx_sqlite3VdbeCursorRestore() and wx_sqlite3VdbeIdxRowid(). */ rc = wx_sqlite3VdbeCursorRestore(pC); - /* wx_sqlite3VbeCursorRestore() can only fail if the record has been deleted - ** out from under the cursor. That will never happens for an IdxRowid - ** or Seek opcode */ - if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; + /* wx_sqlite3VdbeCursorRestore() may fail if the cursor has been disturbed + ** since it was last positioned and an error (e.g. OOM or an IO error) + ** occurs while trying to reposition it. */ + if( rc!=SQLITE_OK ) goto abort_due_to_error; if( !pC->nullRow ){ rowid = 0; /* Not needed. Only used to silence a warning. */ @@ -92025,10 +96816,11 @@ case OP_IdxRowid: { /* out2 */ pTabCur->nullRow = 0; pTabCur->movetoTarget = rowid; pTabCur->deferredMoveto = 1; + pTabCur->cacheStatus = CACHE_STALE; assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 ); - pTabCur->aAltMap = pOp->p4.ai; - assert( !pC->isEphemeral ); assert( !pTabCur->isEphemeral ); + pTabCur->ub.aAltMap = pOp->p4.ai; + assert( !pC->isEphemeral ); pTabCur->pAltCursor = pC; }else{ pOut = out2Prerelease(p, pOp); @@ -92047,8 +96839,8 @@ case OP_IdxRowid: { /* out2 */ ** seek operation now, without further delay. If the cursor seek has ** already occurred, this instruction is a no-op. */ -case OP_FinishSeek: { - VdbeCursor *pC; /* The P1 index cursor */ +case OP_FinishSeek: { /* ncycle */ + VdbeCursor *pC; /* The P1 index cursor */ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; @@ -92103,10 +96895,10 @@ case OP_FinishSeek: { ** If the P1 index entry is less than or equal to the key value then jump ** to P2. Otherwise fall through to the next instruction. */ -case OP_IdxLE: /* jump */ -case OP_IdxGT: /* jump */ -case OP_IdxLT: /* jump */ -case OP_IdxGE: { /* jump */ +case OP_IdxLE: /* jump, ncycle */ +case OP_IdxGT: /* jump, ncycle */ +case OP_IdxLT: /* jump, ncycle */ +case OP_IdxGE: { /* jump, ncycle */ VdbeCursor *pC; int res; UnpackedRecord r; @@ -92159,7 +96951,7 @@ case OP_IdxGE: { /* jump */ rc = wx_sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m); if( rc ) goto abort_due_to_error; res = wx_sqlite3VdbeRecordCompareWithSkip(m.n, m.z, &r, 0); - wx_sqlite3VdbeMemRelease(&m); + wx_sqlite3VdbeMemReleaseMalloc(&m); } /* End of inlined wx_sqlite3VdbeIdxKeyCompare() */ @@ -92247,24 +97039,21 @@ case OP_Destroy: { /* out2 */ ** P2==1 then the table to be clear is in the auxiliary database file ** that is used to store tables create using CREATE TEMPORARY TABLE. ** -** If the P3 value is non-zero, then the table referred to must be an -** intkey table (an SQL table, not an index). In this case the row change -** count is incremented by the number of rows in the table being cleared. -** If P3 is greater than zero, then the value stored in register P3 is -** also incremented by the number of rows in the table being cleared. +** If the P3 value is non-zero, then the row change count is incremented +** by the number of rows in the table being cleared. If P3 is greater +** than zero, then the value stored in register P3 is also incremented +** by the number of rows in the table being cleared. ** ** See also: Destroy */ case OP_Clear: { - int nChange; + i64 nChange; wx_sqlite3VdbeIncrWriteCounter(p, 0); nChange = 0; assert( p->readOnly==0 ); assert( DbMaskTest(p->btreeMask, pOp->p2) ); - rc = wx_sqlite3BtreeClearTable( - db->aDb[pOp->p2].pBt, (u32)pOp->p1, (pOp->p3 ? &nChange : 0) - ); + rc = wx_sqlite3BtreeClearTable(db->aDb[pOp->p2].pBt, (u32)pOp->p1, &nChange); if( pOp->p3 ){ p->nChange += nChange; if( pOp->p3>0 ){ @@ -92370,7 +97159,9 @@ case OP_ParseSchema: { iDb = pOp->p1; assert( iDb>=0 && iDbnDb ); - assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); + assert( DbHasProperty(db, iDb, DB_SchemaLoaded) + || db->mallocFailed + || (CORRUPT_DB && (db->flags & SQLITE_NoSchemaError)!=0) ); #ifndef SQLITE_OMIT_ALTERTABLE if( pOp->p4.z==0 ){ @@ -92382,7 +97173,7 @@ case OP_ParseSchema: { }else #endif { - zSchema = DFLT_SCHEMA_TABLE; + zSchema = LEGACY_SCHEMA_TABLE; initData.db = db; initData.iDb = iDb; initData.pzErrMsg = &p->zErrMsg; @@ -92518,13 +97309,14 @@ case OP_IntegrityCk: { pIn1 = &aMem[pOp->p1]; assert( pOp->p5nDb ); assert( DbMaskTest(p->btreeMask, pOp->p5) ); - z = wx_sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot, - (int)pnErr->u.i+1, &nErr); + rc = wx_sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot, + (int)pnErr->u.i+1, &nErr, &z); wx_sqlite3VdbeMemSetNull(pIn1); if( nErr==0 ){ assert( z==0 ); - }else if( z==0 ){ - goto no_mem; + }else if( rc ){ + wx_sqlite3_free(z); + goto abort_due_to_error; }else{ pnErr->u.i -= nErr-1; wx_sqlite3VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, wx_sqlite3_free); @@ -92728,9 +97520,6 @@ case OP_Program: { /* jump */ pFrame->aOp = p->aOp; pFrame->nOp = p->nOp; pFrame->token = pProgram->token; -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - pFrame->anExec = p->anExec; -#endif #ifdef SQLITE_DEBUG pFrame->iFrameMagic = SQLITE_FRAME_MAGIC; #endif @@ -92767,9 +97556,6 @@ case OP_Program: { /* jump */ memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8); p->aOp = aOp = pProgram->aOp; p->nOp = pProgram->nOp; -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS - p->anExec = 0; -#endif #ifdef SQLITE_DEBUG /* Verify that second and subsequent executions of the same trigger do not ** try to reuse register values from the first use. */ @@ -92909,7 +97695,7 @@ case OP_IfPos: { /* jump, in1 */ ** Synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) ** ** This opcode performs a commonly used computation associated with -** LIMIT and OFFSET process. r[P1] holds the limit counter. r[P3] +** LIMIT and OFFSET processing. r[P1] holds the limit counter. r[P3] ** holds the offset counter. The opcode computes the combined value ** of the LIMIT and OFFSET and stores that value in r[P2]. The r[P2] ** value computed is the total number of rows that will need to be @@ -93041,6 +97827,7 @@ case OP_AggStep: { pCtx->pVdbe = p; pCtx->skipFlag = 0; pCtx->isError = 0; + pCtx->enc = encoding; pCtx->argc = n; pOp->p4type = P4_FUNCCTX; pOp->p4.pCtx = pCtx; @@ -93170,9 +97957,6 @@ case OP_AggFinal: { } wx_sqlite3VdbeChangeEncoding(pMem, encoding); UPDATE_MAX_BLOBSIZE(pMem); - if( wx_sqlite3VdbeMemTooBig(pMem) ){ - goto too_big; - } break; } @@ -93252,6 +98036,7 @@ case OP_JournalMode: { /* out2 */ pPager = wx_sqlite3BtreePager(pBt); eOld = wx_sqlite3PagerGetJournalMode(pPager); if( eNew==PAGER_JOURNALMODE_QUERY ) eNew = eOld; + assert( wx_sqlite3BtreeHoldsMutex(pBt) ); if( !wx_sqlite3PagerOkToChangeJournalMode(pPager) ) eNew = eOld; #ifndef SQLITE_OMIT_WAL @@ -93527,7 +98312,7 @@ case OP_VDestroy: { ** P1 is a cursor number. This opcode opens a cursor to the virtual ** table and stores that cursor in P1. */ -case OP_VOpen: { +case OP_VOpen: { /* ncycle */ VdbeCursor *pCur; wx_sqlite3_vtab_cursor *pVCur; wx_sqlite3_vtab *pVtab; @@ -93550,7 +98335,7 @@ case OP_VOpen: { pVCur->pVtab = pVtab; /* Initialize vdbe cursor object */ - pCur = allocateCursor(p, pOp->p1, 0, -1, CURTYPE_VTAB); + pCur = allocateCursor(p, pOp->p1, 0, CURTYPE_VTAB); if( pCur ){ pCur->uc.pVCur = pVCur; pVtab->nRef++; @@ -93563,6 +98348,34 @@ case OP_VOpen: { } #endif /* SQLITE_OMIT_VIRTUALTABLE */ +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VInitIn P1 P2 P3 * * +** Synopsis: r[P2]=ValueList(P1,P3) +** +** Set register P2 to be a pointer to a ValueList object for cursor P1 +** with cache register P3 and output register P3+1. This ValueList object +** can be used as the first argument to wx_sqlite3_vtab_in_first() and +** wx_sqlite3_vtab_in_next() to extract all of the values stored in the P1 +** cursor. Register P3 is used to hold the values returned by +** wx_sqlite3_vtab_in_first() and wx_sqlite3_vtab_in_next(). +*/ +case OP_VInitIn: { /* out2, ncycle */ + VdbeCursor *pC; /* The cursor containing the RHS values */ + ValueList *pRhs; /* New ValueList object to put in reg[P2] */ + + pC = p->apCsr[pOp->p1]; + pRhs = wx_sqlite3_malloc64( sizeof(*pRhs) ); + if( pRhs==0 ) goto no_mem; + pRhs->pCsr = pC->uc.pCursor; + pRhs->pOut = &aMem[pOp->p3]; + pOut = out2Prerelease(p, pOp); + pOut->flags = MEM_Null; + wx_sqlite3VdbeMemSetPointer(pOut, pRhs, "ValueList", wx_sqlite3VdbeValueListFree); + break; +} +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + + #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VFilter P1 P2 P3 P4 * ** Synopsis: iplan=r[P3] zplan='P4' @@ -93583,7 +98396,7 @@ case OP_VOpen: { ** ** A jump is made to P2 if the result set after filtering would be empty. */ -case OP_VFilter: { /* jump */ +case OP_VFilter: { /* jump, ncycle */ int nArg; int iQuery; const wx_sqlite3_module *pModule; @@ -93601,6 +98414,7 @@ case OP_VFilter: { /* jump */ pCur = p->apCsr[pOp->p1]; assert( memIsValid(pQuery) ); REGISTER_TRACE(pOp->p3, pQuery); + assert( pCur!=0 ); assert( pCur->eCurType==CURTYPE_VTAB ); pVCur = pCur->uc.pVCur; pVtab = pVCur->pVtab; @@ -93612,7 +98426,6 @@ case OP_VFilter: { /* jump */ iQuery = (int)pQuery->u.i; /* Invoke the xFilter method */ - res = 0; apArg = p->apArg; for(i = 0; iapCsr[pOp->p1]; - assert( pCur->eCurType==CURTYPE_VTAB ); + assert( pCur!=0 ); assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); @@ -93658,11 +98471,13 @@ case OP_VColumn: { wx_sqlite3VdbeMemSetNull(pDest); break; } + assert( pCur->eCurType==CURTYPE_VTAB ); pVtab = pCur->uc.pVCur->pVtab; pModule = pVtab->pModule; assert( pModule->xColumn ); memset(&sContext, 0, sizeof(sContext)); sContext.pOut = pDest; + sContext.enc = encoding; assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 ); if( pOp->p5 & OPFLAG_NOCHNG ){ wx_sqlite3VdbeMemSetNull(pDest); @@ -93681,9 +98496,6 @@ case OP_VColumn: { REGISTER_TRACE(pOp->p3, pDest); UPDATE_MAX_BLOBSIZE(pDest); - if( wx_sqlite3VdbeMemTooBig(pDest) ){ - goto too_big; - } if( rc ) goto abort_due_to_error; break; } @@ -93696,14 +98508,14 @@ case OP_VColumn: { ** jump to instruction P2. Or, if the virtual table has reached ** the end of its result set, then fall through to the next instruction. */ -case OP_VNext: { /* jump */ +case OP_VNext: { /* jump, ncycle */ wx_sqlite3_vtab *pVtab; const wx_sqlite3_module *pModule; int res; VdbeCursor *pCur; - res = 0; pCur = p->apCsr[pOp->p1]; + assert( pCur!=0 ); assert( pCur->eCurType==CURTYPE_VTAB ); if( pCur->nullRow ){ break; @@ -93799,7 +98611,7 @@ case OP_VUpdate: { const wx_sqlite3_module *pModule; int nArg; int i; - sqlite_int64 rowid; + sqlite_int64 rowid = 0; Mem **apArg; Mem *pX; @@ -93950,6 +98762,7 @@ case OP_Function: { /* group */ if( pCtx->pOut != pOut ){ pCtx->pVdbe = p; pCtx->pOut = pOut; + pCtx->enc = encoding; for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i]; } assert( pCtx->pVdbe==p ); @@ -93976,17 +98789,98 @@ case OP_Function: { /* group */ if( rc ) goto abort_due_to_error; } - /* Copy the result of the function into register P3 */ - if( pOut->flags & (MEM_Str|MEM_Blob) ){ - wx_sqlite3VdbeChangeEncoding(pOut, encoding); - if( wx_sqlite3VdbeMemTooBig(pOut) ) goto too_big; - } + assert( (pOut->flags&MEM_Str)==0 + || pOut->enc==encoding + || db->mallocFailed ); + assert( !wx_sqlite3VdbeMemTooBig(pOut) ); REGISTER_TRACE(pOp->p3, pOut); UPDATE_MAX_BLOBSIZE(pOut); break; } +/* Opcode: ClrSubtype P1 * * * * +** Synopsis: r[P1].subtype = 0 +** +** Clear the subtype from register P1. +*/ +case OP_ClrSubtype: { /* in1 */ + pIn1 = &aMem[pOp->p1]; + pIn1->flags &= ~MEM_Subtype; + break; +} + +/* Opcode: FilterAdd P1 * P3 P4 * +** Synopsis: filter(P1) += key(P3@P4) +** +** Compute a hash on the P4 registers starting with r[P3] and +** add that hash to the bloom filter contained in r[P1]. +*/ +case OP_FilterAdd: { + u64 h; + + assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); + pIn1 = &aMem[pOp->p1]; + assert( pIn1->flags & MEM_Blob ); + assert( pIn1->n>0 ); + h = filterHash(aMem, pOp); +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + int ii; + for(ii=pOp->p3; iip3+pOp->p4.i; ii++){ + registerTrace(ii, &aMem[ii]); + } + printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n)); + } +#endif + h %= pIn1->n; + pIn1->z[h/8] |= 1<<(h&7); + break; +} + +/* Opcode: Filter P1 P2 P3 P4 * +** Synopsis: if key(P3@P4) not in filter(P1) goto P2 +** +** Compute a hash on the key contained in the P4 registers starting +** with r[P3]. Check to see if that hash is found in the +** bloom filter hosted by register P1. If it is not present then +** maybe jump to P2. Otherwise fall through. +** +** False negatives are harmless. It is always safe to fall through, +** even if the value is in the bloom filter. A false negative causes +** more CPU cycles to be used, but it should still yield the correct +** answer. However, an incorrect answer may well arise from a +** false positive - if the jump is taken when it should fall through. +*/ +case OP_Filter: { /* jump */ + u64 h; + + assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); + pIn1 = &aMem[pOp->p1]; + assert( (pIn1->flags & MEM_Blob)!=0 ); + assert( pIn1->n >= 1 ); + h = filterHash(aMem, pOp); +#ifdef SQLITE_DEBUG + if( db->flags&SQLITE_VdbeTrace ){ + int ii; + for(ii=pOp->p3; iip3+pOp->p4.i; ii++){ + registerTrace(ii, &aMem[ii]); + } + printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n)); + } +#endif + h %= pIn1->n; + if( (pIn1->z[h/8] & (1<<(h&7)))==0 ){ + VdbeBranchTaken(1, 2); + p->aCounter[SQLITE_STMTSTATUS_FILTER_HIT]++; + goto jump_to_p2; + }else{ + p->aCounter[SQLITE_STMTSTATUS_FILTER_MISS]++; + VdbeBranchTaken(0, 2); + } + break; +} + /* Opcode: Trace P1 P2 * P4 * ** ** Write P4 on the statement trace output if statement tracing is @@ -94035,7 +98929,7 @@ case OP_Init: { /* jump */ #ifndef SQLITE_OMIT_TRACE if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0 - && !p->doingRerun + && p->minWriteFileFormat!=254 /* tag-20220401a */ && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){ #ifndef SQLITE_OMIT_DEPRECATED @@ -94197,12 +99091,12 @@ default: { /* This is really OP_Noop, OP_Explain */ *****************************************************************************/ } -#ifdef VDBE_PROFILE - { - u64 endTime = wx_sqlite3NProfileCnt ? wx_sqlite3NProfileCnt : wx_sqlite3Hwtime(); - if( endTime>start ) pOrigOp->cycles += endTime - start; - pOrigOp->cnt++; - } +#if defined(VDBE_PROFILE) + *pnCycle += wx_sqlite3NProfileCnt ? wx_sqlite3NProfileCnt : wx_sqlite3Hwtime(); + pnCycle = 0; +#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) + *pnCycle += wx_sqlite3Hwtime(); + pnCycle = 0; #endif /* The following code adds nothing to the actual functionality @@ -94244,6 +99138,18 @@ abort_due_to_error: rc = SQLITE_CORRUPT_BKPT; } assert( rc ); +#ifdef SQLITE_DEBUG + if( db->flags & SQLITE_VdbeTrace ){ + const char *zTrace = p->zSql; + if( zTrace==0 ){ + if( aOp[0].opcode==OP_Trace ){ + zTrace = aOp[0].p4.z; + } + if( zTrace==0 ) zTrace = "???"; + } + printf("ABORT-due-to-error (rc=%d): %s\n", rc, zTrace); + } +#endif if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ wx_sqlite3VdbeError(p, "%s", wx_sqlite3ErrStr(rc)); } @@ -94252,8 +99158,11 @@ abort_due_to_error: testcase( wx_sqlite3GlobalConfig.xLog!=0 ); wx_sqlite3_log(rc, "statement aborts at %d: [%s] %s", (int)(pOp - aOp), p->zSql, p->zErrMsg); - wx_sqlite3VdbeHalt(p); + if( p->eVdbeState==VDBE_RUN_STATE ) wx_sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) wx_sqlite3OomFault(db); + if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){ + db->flags |= SQLITE_CorruptRdOnly; + } rc = SQLITE_ERROR; if( resetSchemaOnFault>0 ){ wx_sqlite3ResetOneSchema(db, resetSchemaOnFault-1); @@ -94263,6 +99172,18 @@ abort_due_to_error: ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: +#if defined(VDBE_PROFILE) + if( pnCycle ){ + *pnCycle += wx_sqlite3NProfileCnt ? wx_sqlite3NProfileCnt : wx_sqlite3Hwtime(); + pnCycle = 0; + } +#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) + if( pnCycle ){ + *pnCycle += wx_sqlite3Hwtime(); + pnCycle = 0; + } +#endif + #ifndef SQLITE_OMIT_PROGRESS_CALLBACK while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ nProgressLimit += db->nProgressOps; @@ -94274,7 +99195,9 @@ vdbe_return: } #endif p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep; - wx_sqlite3VdbeLeave(p); + if( DbMaskNonZero(p->lockMask) ){ + wx_sqlite3VdbeLeave(p); + } assert( rc!=SQLITE_OK || nExtraDelete==0 || wx_sqlite3_strlike("DELETE%",p->zSql,0)!=0 ); @@ -94385,7 +99308,10 @@ static int blobSeekToRow(Incrblob *p, wx_sqlite3_int64 iRow, char **pzErr){ } if( rc==SQLITE_ROW ){ VdbeCursor *pC = v->apCsr[0]; - u32 type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0; + u32 type; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0; testcase( pC->nHdrParsed==p->iCol ); testcase( pC->nHdrParsed==p->iCol+1 ); if( type<12 ){ @@ -94459,10 +99385,9 @@ SQLITE_API int wx_sqlite3_blob_open( wx_sqlite3_mutex_enter(db->mutex); pBlob = (Incrblob *)wx_sqlite3DbMallocZero(db, sizeof(Incrblob)); - do { - memset(&sParse, 0, sizeof(Parse)); + while(1){ + wx_sqlite3ParseObjectInit(&sParse,db); if( !pBlob ) goto blob_open_out; - sParse.db = db; wx_sqlite3DbFree(db, zErr); zErr = 0; @@ -94477,7 +99402,7 @@ SQLITE_API int wx_sqlite3_blob_open( wx_sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable); } #ifndef SQLITE_OMIT_VIEW - if( pTab && pTab->pSelect ){ + if( pTab && IsView(pTab) ){ pTab = 0; wx_sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); } @@ -94497,7 +99422,7 @@ SQLITE_API int wx_sqlite3_blob_open( /* Now search pTab for the exact column. */ for(iCol=0; iColnCol; iCol++) { - if( wx_sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ + if( wx_sqlite3StrICmp(pTab->aCol[iCol].zCnName, zColumn)==0 ){ break; } } @@ -94522,7 +99447,8 @@ SQLITE_API int wx_sqlite3_blob_open( ** key columns must be indexed. The check below will pick up this ** case. */ FKey *pFKey; - for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + assert( IsOrdinaryTable(pTab) ); + for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ int j; for(j=0; jnCol; j++){ if( pFKey->aCol[j].iFrom==iCol ){ @@ -94638,7 +99564,9 @@ SQLITE_API int wx_sqlite3_blob_open( goto blob_open_out; } rc = blobSeekToRow(pBlob, iRow, &zErr); - } while( (++nAttempt)=SQLITE_MAX_SCHEMA_RETRY || rc!=SQLITE_SCHEMA ) break; + wx_sqlite3ParseObjectReset(&sParse); + } blob_open_out: if( rc==SQLITE_OK && db->mallocFailed==0 ){ @@ -94649,7 +99577,7 @@ blob_open_out: } wx_sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); wx_sqlite3DbFree(db, zErr); - wx_sqlite3ParserReset(&sParse); + wx_sqlite3ParseObjectReset(&sParse); rc = wx_sqlite3ApiExit(db, rc); wx_sqlite3_mutex_leave(db->mutex); return rc; @@ -94729,8 +99657,10 @@ static int blobReadWrite( */ wx_sqlite3_int64 iKey; iKey = wx_sqlite3BtreeIntegerKey(p->pCsr); + assert( v->apCsr[0]!=0 ); + assert( v->apCsr[0]->eCurType==CURTYPE_BTREE ); wx_sqlite3VdbePreUpdateHook( - v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1 + v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol ); } #endif @@ -94801,6 +99731,7 @@ SQLITE_API int wx_sqlite3_blob_reopen(wx_sqlite3_blob *pBlob, wx_sqlite3_int64 i rc = SQLITE_ABORT; }else{ char *zErr; + ((Vdbe*)p->pStmt)->rc = SQLITE_OK; rc = blobSeekToRow(p, iRow, &zErr); if( rc!=SQLITE_OK ){ wx_sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); @@ -95781,7 +100712,8 @@ SQLITE_PRIVATE int wx_sqlite3VdbeSorterInit( } #endif - assert( pCsr->pKeyInfo && pCsr->pBtx==0 ); + assert( pCsr->pKeyInfo ); + assert( !pCsr->isEphemeral ); assert( pCsr->eCurType==CURTYPE_SORTER ); szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*); sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); @@ -95894,8 +100826,9 @@ static void vdbeSorterWorkDebug(SortSubtask *pTask, const char *zEvent){ fprintf(stderr, "%lld:%d %s\n", t, iTask, zEvent); } static void vdbeSorterRewindDebug(const char *zEvent){ - i64 t; - wx_sqlite3OsCurrentTimeInt64(wx_sqlite3_vfs_find(0), &t); + i64 t = 0; + wx_sqlite3_vfs *pVfs = wx_sqlite3_vfs_find(0); + if( ALWAYS(pVfs) ) wx_sqlite3OsCurrentTimeInt64(pVfs, &t); fprintf(stderr, "%lld:X %s\n", t, zEvent); } static void vdbeSorterPopulateDebug( @@ -96109,7 +101042,7 @@ static void vdbeSorterExtendFile(wx_sqlite3 *db, wx_sqlite3_file *pFd, i64 nByte wx_sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_CHUNK_SIZE, &chunksize); wx_sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_SIZE_HINT, &nByte); wx_sqlite3OsFetch(pFd, 0, (int)nByte, &p); - wx_sqlite3OsUnfetch(pFd, 0, p); + if( p ) wx_sqlite3OsUnfetch(pFd, 0, p); } } #else @@ -96827,6 +101760,7 @@ static int vdbeIncrMergerNew( vdbeMergeEngineFree(pMerger); rc = SQLITE_NOMEM_BKPT; } + assert( *ppOut!=0 || rc!=SQLITE_OK ); return rc; } @@ -97670,6 +102604,9 @@ static int bytecodevtabConnect( ");" }; + (void)argc; + (void)argv; + (void)pzErr; rc = wx_sqlite3_declare_vtab(db, azSchema[isTabUsed]); if( rc==SQLITE_OK ){ pNew = wx_sqlite3_malloc( sizeof(*pNew) ); @@ -97905,6 +102842,7 @@ static int bytecodevtabFilter( bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor; bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab; int rc = SQLITE_OK; + (void)idxStr; bytecodevtabCursorClear(pCur); pCur->iRowid = 0; @@ -98192,6 +103130,9 @@ static int memjrnlCreateFile(MemJournal *p){ } +/* Forward reference */ +static int memjrnlTruncate(wx_sqlite3_file *pJfd, sqlite_int64 size); + /* ** Write data to the file. */ @@ -98222,22 +103163,20 @@ static int memjrnlWrite( ** the in-memory journal is being used by a connection using the ** atomic-write optimization. In this case the first 28 bytes of the ** journal file may be written as part of committing the transaction. */ - assert( iOfst==p->endpoint.iOffset || iOfst==0 ); -#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ - || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) + assert( iOfst<=p->endpoint.iOffset ); + if( iOfst>0 && iOfst!=p->endpoint.iOffset ){ + memjrnlTruncate(pJfd, iOfst); + } if( iOfst==0 && p->pFirst ){ assert( p->nChunkSize>iAmt ); memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt); - }else -#else - assert( iOfst>0 || p->pFirst==0 ); -#endif - { + }else{ while( nWrite>0 ){ FileChunk *pChunk = p->endpoint.pChunk; int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize); int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset); + assert( pChunk!=0 || iChunkOffset==0 ); if( iChunkOffset==0 ){ /* New chunk is required to extend the file. */ FileChunk *pNew = wx_sqlite3_malloc(fileChunkSize(p->nChunkSize)); @@ -98252,10 +103191,11 @@ static int memjrnlWrite( assert( !p->pFirst ); p->pFirst = pNew; } - p->endpoint.pChunk = pNew; + pChunk = p->endpoint.pChunk = pNew; } - memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace); + assert( pChunk!=0 ); + memcpy((u8*)pChunk->zChunk + iChunkOffset, zWrite, iSpace); zWrite += iSpace; nWrite -= iSpace; p->endpoint.iOffset += iSpace; @@ -98271,26 +103211,28 @@ static int memjrnlWrite( */ static int memjrnlTruncate(wx_sqlite3_file *pJfd, sqlite_int64 size){ MemJournal *p = (MemJournal *)pJfd; - FileChunk *pIter = 0; - - if( size==0 ){ - memjrnlFreeChunks(p->pFirst); - p->pFirst = 0; - }else{ - i64 iOff = p->nChunkSize; - for(pIter=p->pFirst; ALWAYS(pIter) && iOff<=size; pIter=pIter->pNext){ - iOff += p->nChunkSize; - } - if( ALWAYS(pIter) ){ - memjrnlFreeChunks(pIter->pNext); - pIter->pNext = 0; + assert( p->endpoint.pChunk==0 || p->endpoint.pChunk->pNext==0 ); + if( sizeendpoint.iOffset ){ + FileChunk *pIter = 0; + if( size==0 ){ + memjrnlFreeChunks(p->pFirst); + p->pFirst = 0; + }else{ + i64 iOff = p->nChunkSize; + for(pIter=p->pFirst; ALWAYS(pIter) && iOffpNext){ + iOff += p->nChunkSize; + } + if( ALWAYS(pIter) ){ + memjrnlFreeChunks(pIter->pNext); + pIter->pNext = 0; + } } - } - p->endpoint.pChunk = pIter; - p->endpoint.iOffset = size; - p->readpoint.pChunk = 0; - p->readpoint.iOffset = 0; + p->endpoint.pChunk = pIter; + p->endpoint.iOffset = size; + p->readpoint.pChunk = 0; + p->readpoint.iOffset = 0; + } return SQLITE_OK; } @@ -98369,6 +103311,8 @@ SQLITE_PRIVATE int wx_sqlite3JournalOpen( ){ MemJournal *p = (MemJournal*)pJfd; + assert( zName || nSpill<0 || (flags & SQLITE_OPEN_EXCLUSIVE) ); + /* Zero the file-handle object. If nSpill was passed zero, initialize ** it using the wx_sqlite3OsOpen() function of the underlying VFS. In this ** case none of the code in this module is executed as a result of calls @@ -98483,15 +103427,10 @@ static int walkWindowList(Walker *pWalker, Window *pList, int bOneOnly){ if( rc ) return WRC_Abort; rc = wx_sqlite3WalkExpr(pWalker, pWin->pFilter); if( rc ) return WRC_Abort; - - /* The next two are purely for calls to wx_sqlite3RenameExprUnmap() - ** within wx_sqlite3WindowOffsetExpr(). Because of constraints imposed - ** by wx_sqlite3WindowOffsetExpr(), they can never fail. The results do - ** not matter anyhow. */ rc = wx_sqlite3WalkExpr(pWalker, pWin->pStart); - if( NEVER(rc) ) return WRC_Abort; + if( rc ) return WRC_Abort; rc = wx_sqlite3WalkExpr(pWalker, pWin->pEnd); - if( NEVER(rc) ) return WRC_Abort; + if( rc ) return WRC_Abort; if( bOneOnly ) break; } return WRC_Continue; @@ -98531,7 +103470,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ assert( !ExprHasProperty(pExpr, EP_WinFunc) ); pExpr = pExpr->pRight; continue; - }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + }else if( ExprUseXSelect(pExpr) ){ assert( !ExprHasProperty(pExpr, EP_WinFunc) ); if( wx_sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort; }else{ @@ -98568,6 +103507,16 @@ SQLITE_PRIVATE int wx_sqlite3WalkExprList(Walker *pWalker, ExprList *p){ return WRC_Continue; } +/* +** This is a no-op callback for Walker->xSelectCallback2. If this +** callback is set, then the Select->pWinDefn list is traversed. +*/ +SQLITE_PRIVATE void wx_sqlite3WalkWinDefnDummyCallback(Walker *pWalker, Select *p){ + UNUSED_PARAMETER(pWalker); + UNUSED_PARAMETER(p); + /* No-op */ +} + /* ** Walk all expressions associated with SELECT statement p. Do ** not invoke the SELECT callback on p, but do (of course) invoke @@ -98581,10 +103530,15 @@ SQLITE_PRIVATE int wx_sqlite3WalkSelectExpr(Walker *pWalker, Select *p){ if( wx_sqlite3WalkExpr(pWalker, p->pHaving) ) return WRC_Abort; if( wx_sqlite3WalkExprList(pWalker, p->pOrderBy) ) return WRC_Abort; if( wx_sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort; -#if !defined(SQLITE_OMIT_WINDOWFUNC) && !defined(SQLITE_OMIT_ALTERTABLE) - { - Parse *pParse = pWalker->pParse; - if( pParse && IN_RENAME_OBJECT ){ +#if !defined(SQLITE_OMIT_WINDOWFUNC) + if( p->pWinDefn ){ + Parse *pParse; + if( pWalker->xSelectCallback2==wx_sqlite3WalkWinDefnDummyCallback + || ((pParse = pWalker->pParse)!=0 && IN_RENAME_OBJECT) +#ifndef SQLITE_OMIT_CTE + || pWalker->xSelectCallback2==wx_sqlite3SelectPopWith +#endif + ){ /* The following may return WRC_Abort if there are unresolvable ** symbols (e.g. a table that does not exist) in a window definition. */ int rc = walkWindowList(pWalker, p->pWinDefn, 0); @@ -98608,7 +103562,7 @@ SQLITE_PRIVATE int wx_sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ SrcItem *pItem; pSrc = p->pSrc; - if( pSrc ){ + if( ALWAYS(pSrc) ){ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ if( pItem->pSelect && wx_sqlite3WalkSelect(pWalker, pItem->pSelect) ){ return WRC_Abort; @@ -98782,55 +103736,28 @@ static void resolveAlias( assert( pOrig!=0 ); db = pParse->db; pDup = wx_sqlite3ExprDup(db, pOrig, 0); - if( pDup!=0 ){ + if( db->mallocFailed ){ + wx_sqlite3ExprDelete(db, pDup); + pDup = 0; + }else{ + Expr temp; incrAggFunctionDepth(pDup, nSubquery); if( pExpr->op==TK_COLLATE ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); pDup = wx_sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken); } - - /* Before calling wx_sqlite3ExprDelete(), set the EP_Static flag. This - ** prevents ExprDelete() from deleting the Expr structure itself, - ** allowing it to be repopulated by the memcpy() on the following line. - ** The pExpr->u.zToken might point into memory that will be freed by the - ** wx_sqlite3DbFree(db, pDup) on the last line of this block, so be sure to - ** make a copy of the token before doing the wx_sqlite3DbFree(). - */ - ExprSetProperty(pExpr, EP_Static); - wx_sqlite3ExprDelete(db, pExpr); - memcpy(pExpr, pDup, sizeof(*pExpr)); - if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){ - assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 ); - pExpr->u.zToken = wx_sqlite3DbStrDup(db, pExpr->u.zToken); - pExpr->flags |= EP_MemToken; - } + memcpy(&temp, pDup, sizeof(Expr)); + memcpy(pDup, pExpr, sizeof(Expr)); + memcpy(pExpr, &temp, sizeof(Expr)); if( ExprHasProperty(pExpr, EP_WinFunc) ){ - if( pExpr->y.pWin!=0 ){ + if( ALWAYS(pExpr->y.pWin!=0) ){ pExpr->y.pWin->pOwner = pExpr; - }else{ - assert( db->mallocFailed ); } } - wx_sqlite3DbFree(db, pDup); + wx_sqlite3ExprDeferredDelete(pParse, pDup); } } - -/* -** Return TRUE if the name zCol occurs anywhere in the USING clause. -** -** Return FALSE if the USING clause is NULL or if it does not contain -** zCol. -*/ -static int nameInUsingClause(IdList *pUsing, const char *zCol){ - if( pUsing ){ - int k; - for(k=0; knId; k++){ - if( wx_sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1; - } - } - return 0; -} - /* ** Subqueries stores the original database, table and column names for their ** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". @@ -98846,7 +103773,7 @@ SQLITE_PRIVATE int wx_sqlite3MatchEName( ){ int n; const char *zSpan; - if( pItem->eEName!=ENAME_TAB ) return 0; + if( pItem->fg.eEName!=ENAME_TAB ) return 0; zSpan = pItem->zEName; for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} if( zDb && (wx_sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){ @@ -98890,6 +103817,7 @@ SQLITE_PRIVATE Bitmask wx_sqlite3ExprColUsed(Expr *pExpr){ Table *pExTab; n = pExpr->iColumn; + assert( ExprUseYTab(pExpr) ); pExTab = pExpr->y.pTab; assert( pExTab!=0 ); if( (pExTab->tabFlags & TF_HasGenerated)!=0 @@ -98906,6 +103834,55 @@ SQLITE_PRIVATE Bitmask wx_sqlite3ExprColUsed(Expr *pExpr){ } } +/* +** Create a new expression term for the column specified by pMatch and +** iColumn. Append this new expression term to the FULL JOIN Match set +** in *ppList. Create a new *ppList if this is the first term in the +** set. +*/ +static void extendFJMatch( + Parse *pParse, /* Parsing context */ + ExprList **ppList, /* ExprList to extend */ + SrcItem *pMatch, /* Source table containing the column */ + i16 iColumn /* The column number */ +){ + Expr *pNew = wx_sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0); + if( pNew ){ + pNew->iTable = pMatch->iCursor; + pNew->iColumn = iColumn; + pNew->y.pTab = pMatch->pTab; + assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ); + ExprSetProperty(pNew, EP_CanBeNull); + *ppList = wx_sqlite3ExprListAppend(pParse, *ppList, pNew); + } +} + +/* +** Return TRUE (non-zero) if zTab is a valid name for the schema table pTab. +*/ +static SQLITE_NOINLINE int isValidSchemaTableName( + const char *zTab, /* Name as it appears in the SQL */ + Table *pTab, /* The schema table we are trying to match */ + Schema *pSchema /* non-NULL if a database qualifier is present */ +){ + const char *zLegacy; + assert( pTab!=0 ); + assert( pTab->tnum==1 ); + if( wx_sqlite3StrNICmp(zTab, "sqlite_", 7)!=0 ) return 0; + zLegacy = pTab->zName; + if( strcmp(zLegacy+7, &LEGACY_TEMP_SCHEMA_TABLE[7])==0 ){ + if( wx_sqlite3StrICmp(zTab+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){ + return 1; + } + if( pSchema==0 ) return 0; + if( wx_sqlite3StrICmp(zTab+7, &LEGACY_SCHEMA_TABLE[7])==0 ) return 1; + if( wx_sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1; + }else{ + if( wx_sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1; + } + return 0; +} + /* ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up ** that name in the set of source tables in pSrcList and make the pExpr @@ -98951,11 +103928,13 @@ static int lookupName( NameContext *pTopNC = pNC; /* First namecontext in the list */ Schema *pSchema = 0; /* Schema of the expression */ int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */ - Table *pTab = 0; /* Table hold the row */ + Table *pTab = 0; /* Table holding the row */ Column *pCol; /* A column of pTab */ + ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */ assert( pNC ); /* the name context cannot be NULL. */ assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ + assert( zDb==0 || zTab!=0 ); assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); /* Initialize the node to no-match */ @@ -99003,62 +103982,126 @@ static int lookupName( u8 hCol; pTab = pItem->pTab; assert( pTab!=0 && pTab->zName!=0 ); - assert( pTab->nCol>0 ); - if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ + assert( pTab->nCol>0 || pParse->nErr ); + assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); + if( pItem->fg.isNestedFrom ){ + /* In this case, pItem is a subquery that has been formed from a + ** parenthesized subset of the FROM clause terms. Example: + ** .... FROM t1 LEFT JOIN (t2 RIGHT JOIN t3 USING(x)) USING(y) ... + ** \_________________________/ + ** This pItem -------------^ + */ int hit = 0; + assert( pItem->pSelect!=0 ); pEList = pItem->pSelect->pEList; + assert( pEList!=0 ); + assert( pEList->nExpr==pTab->nCol ); for(j=0; jnExpr; j++){ - if( wx_sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){ - cnt++; - cntTab = 2; - pMatch = pItem; - pExpr->iColumn = j; - hit = 1; + if( !wx_sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){ + continue; } + if( cnt>0 ){ + if( pItem->fg.isUsing==0 + || wx_sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 + ){ + /* Two or more tables have the same column name which is + ** not joined by USING. This is an error. Signal as much + ** by clearing pFJMatch and letting cnt go above 1. */ + wx_sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else + if( (pItem->fg.jointype & JT_RIGHT)==0 ){ + /* An INNER or LEFT JOIN. Use the left-most table */ + continue; + }else + if( (pItem->fg.jointype & JT_LEFT)==0 ){ + /* A RIGHT JOIN. Use the right-most table */ + cnt = 0; + wx_sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else{ + /* For a FULL JOIN, we must construct a coalesce() func */ + extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); + } + } + cnt++; + cntTab = 2; + pMatch = pItem; + pExpr->iColumn = j; + pEList->a[j].fg.bUsed = 1; + hit = 1; + if( pEList->a[j].fg.bUsingTerm ) break; } if( hit || zTab==0 ) continue; } - if( zDb && pTab->pSchema!=pSchema ){ - continue; - } + assert( zDb==0 || zTab!=0 ); if( zTab ){ - const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; - assert( zTabName!=0 ); - if( wx_sqlite3StrICmp(zTabName, zTab)!=0 ){ - continue; + if( zDb ){ + if( pTab->pSchema!=pSchema ) continue; + if( pSchema==0 && strcmp(zDb,"*")!=0 ) continue; + } + if( pItem->zAlias!=0 ){ + if( wx_sqlite3StrICmp(zTab, pItem->zAlias)!=0 ){ + continue; + } + }else if( wx_sqlite3StrICmp(zTab, pTab->zName)!=0 ){ + if( pTab->tnum!=1 ) continue; + if( !isValidSchemaTableName(zTab, pTab, pSchema) ) continue; } + assert( ExprUseYTab(pExpr) ); if( IN_RENAME_OBJECT && pItem->zAlias ){ wx_sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab); } } - if( 0==(cntTab++) ){ - pMatch = pItem; - } hCol = wx_sqlite3StrIHash(zCol); for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ - if( pCol->hName==hCol && wx_sqlite3StrICmp(pCol->zName, zCol)==0 ){ - /* If there has been exactly one prior match and this match - ** is for the right-hand table of a NATURAL JOIN or is in a - ** USING clause, then skip this match. - */ - if( cnt==1 ){ - if( pItem->fg.jointype & JT_NATURAL ) continue; - if( nameInUsingClause(pItem->pUsing, zCol) ) continue; + if( pCol->hName==hCol + && wx_sqlite3StrICmp(pCol->zCnName, zCol)==0 + ){ + if( cnt>0 ){ + if( pItem->fg.isUsing==0 + || wx_sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 + ){ + /* Two or more tables have the same column name which is + ** not joined by USING. This is an error. Signal as much + ** by clearing pFJMatch and letting cnt go above 1. */ + wx_sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else + if( (pItem->fg.jointype & JT_RIGHT)==0 ){ + /* An INNER or LEFT JOIN. Use the left-most table */ + continue; + }else + if( (pItem->fg.jointype & JT_LEFT)==0 ){ + /* A RIGHT JOIN. Use the right-most table */ + cnt = 0; + wx_sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else{ + /* For a FULL JOIN, we must construct a coalesce() func */ + extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); + } } cnt++; pMatch = pItem; /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; + if( pItem->fg.isNestedFrom ){ + wx_sqlite3SrcItemColumnUsed(pItem, j); + } break; } } + if( 0==cnt && VisibleRowid(pTab) ){ + cntTab++; + pMatch = pItem; + } } if( pMatch ){ pExpr->iTable = pMatch->iCursor; + assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pMatch->pTab; - /* RIGHT JOIN not (yet) supported */ - assert( (pMatch->fg.jointype & JT_RIGHT)==0 ); - if( (pMatch->fg.jointype & JT_LEFT)!=0 ){ + if( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ){ ExprSetProperty(pExpr, EP_CanBeNull); } pSchema = pExpr->y.pTab->pSchema; @@ -99109,7 +104152,9 @@ static int lookupName( pSchema = pTab->pSchema; cntTab++; for(iCol=0, pCol=pTab->aCol; iColnCol; iCol++, pCol++){ - if( pCol->hName==hCol && wx_sqlite3StrICmp(pCol->zName, zCol)==0 ){ + if( pCol->hName==hCol + && wx_sqlite3StrICmp(pCol->zCnName, zCol)==0 + ){ if( iCol==pTab->iPKey ){ iCol = -1; } @@ -99126,6 +104171,7 @@ static int lookupName( #ifndef SQLITE_OMIT_UPSERT if( pExpr->iTable==EXCLUDED_TABLE_NUMBER ){ testcase( iCol==(-1) ); + assert( ExprUseYTab(pExpr) ); if( IN_RENAME_OBJECT ){ pExpr->iColumn = iCol; pExpr->y.pTab = pTab; @@ -99138,9 +104184,12 @@ static int lookupName( }else #endif /* SQLITE_OMIT_UPSERT */ { + assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pTab; if( pParse->bReturning ){ eNewExprOp = TK_REGISTER; + pExpr->op2 = TK_COLUMN; + pExpr->iColumn = iCol; pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable + wx_sqlite3TableColumnToStorage(pTab, iCol) + 1; }else{ @@ -99174,7 +104223,7 @@ static int lookupName( && pMatch && (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0 && wx_sqlite3IsRowid(zCol) - && VisibleRowid(pMatch->pTab) + && ALWAYS(VisibleRowid(pMatch->pTab)) ){ cnt = 1; pExpr->iColumn = -1; @@ -99199,21 +104248,21 @@ static int lookupName( ** is supported for backwards compatibility only. Hence, we issue a warning ** on wx_sqlite3_log() whenever the capability is used. */ - if( (pNC->ncFlags & NC_UEList)!=0 - && cnt==0 + if( cnt==0 + && (pNC->ncFlags & NC_UEList)!=0 && zTab==0 ){ pEList = pNC->uNC.pEList; assert( pEList!=0 ); for(j=0; jnExpr; j++){ char *zAs = pEList->a[j].zEName; - if( pEList->a[j].eEName==ENAME_NAME + if( pEList->a[j].fg.eEName==ENAME_NAME && wx_sqlite3_stricmp(zAs, zCol)==0 ){ Expr *pOrig; assert( pExpr->pLeft==0 && pExpr->pRight==0 ); - assert( pExpr->x.pList==0 ); - assert( pExpr->x.pSelect==0 ); + assert( ExprUseXList(pExpr)==0 || pExpr->x.pList==0 ); + assert( ExprUseXSelect(pExpr)==0 || pExpr->x.pSelect==0 ); pOrig = pEList->a[j].pExpr; if( (pNC->ncFlags&NC_AllowAgg)==0 && ExprHasProperty(pOrig, EP_Agg) ){ wx_sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); @@ -99264,7 +104313,6 @@ static int lookupName( assert( pExpr->op==TK_ID ); if( ExprHasProperty(pExpr,EP_DblQuoted) && areDoubleQuotedStringsEnabled(db, pTopNC) - && (db->init.bDropColumn==0 || wx_sqlite3StrICmp(zCol, db->init.azInit[0])!=0) ){ /* If a double-quoted identifier does not match any known column name, ** then treat it as a string. @@ -99279,11 +104327,6 @@ static int lookupName( ** Someday, I hope to get rid of this hack. Unfortunately there is ** a huge amount of legacy SQL that uses it. So for now, we just ** issue a warning. - ** - ** 2021-03-15: ticket 1c24a659e6d7f3a1 - ** Do not do the ID-to-STRING conversion when doing the schema - ** sanity check following a DROP COLUMN if the identifer name matches - ** the name of the column being dropped. */ wx_sqlite3_log(SQLITE_WARNING, "double-quoted string literal: \"%w\"", zCol); @@ -99291,7 +104334,7 @@ static int lookupName( wx_sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol); #endif pExpr->op = TK_STRING; - pExpr->y.pTab = 0; + memset(&pExpr->y, 0, sizeof(pExpr->y)); return WRC_Prune; } if( wx_sqlite3ExprIdToTrueFalse(pExpr) ){ @@ -99300,11 +104343,37 @@ static int lookupName( } /* - ** cnt==0 means there was not match. cnt>1 means there were two or - ** more matches. Either way, we have an error. + ** cnt==0 means there was not match. + ** cnt>1 means there were two or more matches. + ** + ** cnt==0 is always an error. cnt>1 is often an error, but might + ** be multiple matches for a NATURAL LEFT JOIN or a LEFT JOIN USING. */ + assert( pFJMatch==0 || cnt>0 ); + assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); if( cnt!=1 ){ const char *zErr; + if( pFJMatch ){ + if( pFJMatch->nExpr==cnt-1 ){ + if( ExprHasProperty(pExpr,EP_Leaf) ){ + ExprClearProperty(pExpr,EP_Leaf); + }else{ + wx_sqlite3ExprDelete(db, pExpr->pLeft); + pExpr->pLeft = 0; + wx_sqlite3ExprDelete(db, pExpr->pRight); + pExpr->pRight = 0; + } + extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); + pExpr->op = TK_FUNCTION; + pExpr->u.zToken = "coalesce"; + pExpr->x.pList = pFJMatch; + cnt = 1; + goto lookupname_end; + }else{ + wx_sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + } + } zErr = cnt==0 ? "no such column" : "ambiguous column name"; if( zDb ){ wx_sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); @@ -99313,8 +104382,19 @@ static int lookupName( }else{ wx_sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); } + wx_sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); pParse->checkSchema = 1; - pTopNC->nErr++; + pTopNC->nNcErr++; + } + assert( pFJMatch==0 ); + + /* Remove all substructure from pExpr */ + if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ + wx_sqlite3ExprDelete(db, pExpr->pLeft); + pExpr->pLeft = 0; + wx_sqlite3ExprDelete(db, pExpr->pRight); + pExpr->pRight = 0; + ExprSetProperty(pExpr, EP_Leaf); } /* If a column from a table in pSrcList is referenced, then record @@ -99335,16 +104415,7 @@ static int lookupName( pMatch->colUsed |= wx_sqlite3ExprColUsed(pExpr); } - /* Clean up and return - */ - if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ - wx_sqlite3ExprDelete(db, pExpr->pLeft); - pExpr->pLeft = 0; - wx_sqlite3ExprDelete(db, pExpr->pRight); - pExpr->pRight = 0; - } pExpr->op = eNewExprOp; - ExprSetProperty(pExpr, EP_Leaf); lookupname_end: if( cnt==1 ){ assert( pNC!=0 ); @@ -99377,7 +104448,9 @@ SQLITE_PRIVATE Expr *wx_sqlite3CreateColumnExpr(wx_sqlite3 *db, SrcList *pSrc, i Expr *p = wx_sqlite3ExprAlloc(db, TK_COLUMN, 0, 0); if( p ){ SrcItem *pItem = &pSrc->a[iSrc]; - Table *pTab = p->y.pTab = pItem->pTab; + Table *pTab; + assert( ExprUseYTab(p) ); + pTab = p->y.pTab = pItem->pTab; p->iTable = pItem->iCursor; if( p->y.pTab->iPKey==iCol ){ p->iColumn = -1; @@ -99419,7 +104492,8 @@ static void notValidImpl( Parse *pParse, /* Leave error message here */ NameContext *pNC, /* The name context */ const char *zMsg, /* Type of error */ - Expr *pExpr /* Invalidate this expression on error */ + Expr *pExpr, /* Invalidate this expression on error */ + Expr *pError /* Associate error with this expression */ ){ const char *zIn = "partial index WHERE clauses"; if( pNC->ncFlags & NC_IdxExpr ) zIn = "index expressions"; @@ -99431,10 +104505,11 @@ static void notValidImpl( #endif wx_sqlite3ErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn); if( pExpr ) pExpr->op = TK_NULL; + wx_sqlite3RecordErrorOffsetOfExpr(pParse->db, pError); } -#define wx_sqlite3ResolveNotValid(P,N,M,X,E) \ +#define wx_sqlite3ResolveNotValid(P,N,M,X,E,R) \ assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \ - if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E); + if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R); /* ** Expression p should encode a floating point value between 1.0 and 0.0. @@ -99444,6 +104519,7 @@ static void notValidImpl( static int exprProbability(Expr *p){ double r = -1.0; if( p->op!=TK_FLOAT ) return -1; + assert( !ExprHasProperty(p, EP_IntValue) ); wx_sqlite3AtoF(p->u.zToken, &r, wx_sqlite3Strlen30(p->u.zToken), SQLITE_UTF8); assert( r>=0.0 ); if( r>1.0 ) return -1; @@ -99492,6 +104568,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ assert( pSrcList && pSrcList->nSrc>=1 ); pItem = pSrcList->a; pExpr->op = TK_COLUMN; + assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pItem->pTab; pExpr->iTable = pItem->iCursor; pExpr->iColumn--; @@ -99523,14 +104600,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ } wx_sqlite3WalkExpr(pWalker, pExpr->pLeft); if( 0==wx_sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){ - if( pExpr->op==TK_NOTNULL ){ - pExpr->u.zToken = "true"; - ExprSetProperty(pExpr, EP_IsTrue); - }else{ - pExpr->u.zToken = "false"; - ExprSetProperty(pExpr, EP_IsFalse); - } - pExpr->op = TK_TRUEFALSE; + testcase( ExprHasProperty(pExpr, EP_OuterON) ); + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + pExpr->u.iValue = (pExpr->op==TK_NOTNULL); + pExpr->flags |= EP_IntValue; + pExpr->op = TK_INTEGER; + for(i=0, p=pNC; p && ipNext, i++){ p->nRef = anRef[i]; } @@ -99558,24 +104633,28 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ if( pExpr->op==TK_ID ){ zDb = 0; zTable = 0; + assert( !ExprHasProperty(pExpr, EP_IntValue) ); zColumn = pExpr->u.zToken; }else{ Expr *pLeft = pExpr->pLeft; testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); wx_sqlite3ResolveNotValid(pParse, pNC, "the \".\" operator", - NC_IdxExpr|NC_GenCol, 0); + NC_IdxExpr|NC_GenCol, 0, pExpr); pRight = pExpr->pRight; if( pRight->op==TK_ID ){ zDb = 0; }else{ assert( pRight->op==TK_DOT ); + assert( !ExprHasProperty(pRight, EP_IntValue) ); zDb = pLeft->u.zToken; pLeft = pRight->pLeft; pRight = pRight->pRight; } + assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) ); zTable = pLeft->u.zToken; zColumn = pRight->u.zToken; + assert( ExprUseYTab(pExpr) ); if( IN_RENAME_OBJECT ){ wx_sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight); wx_sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft); @@ -99592,7 +104671,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ int no_such_func = 0; /* True if no such function exists */ int wrong_num_args = 0; /* True if wrong number of arguments */ int is_agg = 0; /* True if is an aggregate function */ - int nId; /* Number of characters in function name */ const char *zId; /* The function name. */ FuncDef *pDef; /* Information about the function */ u8 enc = ENC(pParse->db); /* The database encoding */ @@ -99600,9 +104678,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #ifndef SQLITE_OMIT_WINDOWFUNC Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0); #endif - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); zId = pExpr->u.zToken; - nId = wx_sqlite3Strlen30(zId); pDef = wx_sqlite3FindFunction(pParse->db, zId, n, enc, 0); if( pDef==0 ){ pDef = wx_sqlite3FindFunction(pParse->db, zId, -2, enc, 0); @@ -99619,9 +104696,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pExpr->iTable = exprProbability(pList->a[1].pExpr); if( pExpr->iTable<0 ){ wx_sqlite3ErrorMsg(pParse, - "second argument to likelihood() must be a " - "constant between 0.0 and 1.0"); - pNC->nErr++; + "second argument to %#T() must be a " + "constant between 0.0 and 1.0", pExpr); + pNC->nNcErr++; } }else{ /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is @@ -99641,9 +104718,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ int auth = wx_sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0,pDef->zName,0); if( auth!=SQLITE_OK ){ if( auth==SQLITE_DENY ){ - wx_sqlite3ErrorMsg(pParse, "not authorized to use function: %s", - pDef->zName); - pNC->nErr++; + wx_sqlite3ErrorMsg(pParse, "not authorized to use function: %#T", + pExpr); + pNC->nNcErr++; } pExpr->op = TK_NULL; return WRC_Prune; @@ -99665,7 +104742,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ ** in a CHECK constraint. SQLServer, MySQL, and PostgreSQL all ** all this. */ wx_sqlite3ResolveNotValid(pParse, pNC, "non-deterministic functions", - NC_IdxExpr|NC_PartIdx|NC_GenCol, 0); + NC_IdxExpr|NC_PartIdx|NC_GenCol, 0, pExpr); }else{ assert( (NC_SelfRef & 0xff)==NC_SelfRef ); /* Must fit in 8 bits */ pExpr->op2 = pNC->ncFlags & NC_SelfRef; @@ -99678,7 +104755,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ /* Internal-use-only functions are disallowed unless the ** SQL is being compiled using wx_sqlite3NestedParse() or ** the SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test-control has be - ** used to activate internal functionsn for testing purposes */ + ** used to activate internal functions for testing purposes */ no_such_func = 1; pDef = 0; }else @@ -99697,9 +104774,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ ); if( pDef && pDef->xValue==0 && pWin ){ wx_sqlite3ErrorMsg(pParse, - "%.*s() may not be used as a window function", nId, zId + "%#T() may not be used as a window function", pExpr ); - pNC->nErr++; + pNC->nNcErr++; }else if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pWin) @@ -99711,14 +104788,14 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ }else{ zType = "aggregate"; } - wx_sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId); - pNC->nErr++; + wx_sqlite3ErrorMsg(pParse, "misuse of %s function %#T()",zType,pExpr); + pNC->nNcErr++; is_agg = 0; } #else if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){ - wx_sqlite3ErrorMsg(pParse,"misuse of aggregate function %.*s()",nId,zId); - pNC->nErr++; + wx_sqlite3ErrorMsg(pParse,"misuse of aggregate function %#T()",pExpr); + pNC->nNcErr++; is_agg = 0; } #endif @@ -99727,20 +104804,20 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ && pParse->explain==0 #endif ){ - wx_sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); - pNC->nErr++; + wx_sqlite3ErrorMsg(pParse, "no such function: %#T", pExpr); + pNC->nNcErr++; }else if( wrong_num_args ){ - wx_sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", - nId, zId); - pNC->nErr++; + wx_sqlite3ErrorMsg(pParse,"wrong number of arguments to function %#T()", + pExpr); + pNC->nNcErr++; } #ifndef SQLITE_OMIT_WINDOWFUNC else if( is_agg==0 && ExprHasProperty(pExpr, EP_WinFunc) ){ wx_sqlite3ErrorMsg(pParse, - "FILTER may not be used with non-aggregate %.*s()", - nId, zId + "FILTER may not be used with non-aggregate %#T()", + pExpr ); - pNC->nErr++; + pNC->nNcErr++; } #endif if( is_agg ){ @@ -99764,7 +104841,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #ifndef SQLITE_OMIT_WINDOWFUNC if( pWin ){ Select *pSel = pNC->pWinSelect; - assert( pWin==pExpr->y.pWin ); + assert( pWin==0 || (ExprUseYWin(pExpr) && pWin==pExpr->y.pWin) ); if( IN_RENAME_OBJECT==0 ){ wx_sqlite3WindowUpdate(pParse, pSel ? pSel->pWinDefn : 0, pWin, pDef); if( pParse->db->mallocFailed ) break; @@ -99777,7 +104854,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ }else #endif /* SQLITE_OMIT_WINDOWFUNC */ { - NameContext *pNC2 = pNC; + NameContext *pNC2; /* For looping up thru outer contexts */ pExpr->op = TK_AGG_FUNCTION; pExpr->op2 = 0; #ifndef SQLITE_OMIT_WINDOWFUNC @@ -99785,16 +104862,22 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ wx_sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter); } #endif - while( pNC2 && !wx_sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){ + pNC2 = pNC; + while( pNC2 + && wx_sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0 + ){ pExpr->op2++; pNC2 = pNC2->pNext; } assert( pDef!=0 || IN_RENAME_OBJECT ); if( pNC2 && pDef ){ assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg ); + assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg ); testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 ); - pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX); - + testcase( (pDef->funcFlags & SQLITE_FUNC_ANYORDER)!=0 ); + pNC2->ncFlags |= NC_HasAgg + | ((pDef->funcFlags^SQLITE_FUNC_ANYORDER) + & (SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER)); } } pNC->ncFlags |= savedAllowFlags; @@ -99810,20 +104893,22 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #endif case TK_IN: { testcase( pExpr->op==TK_IN ); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ int nRef = pNC->nRef; testcase( pNC->ncFlags & NC_IsCheck ); testcase( pNC->ncFlags & NC_PartIdx ); testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); - wx_sqlite3ResolveNotValid(pParse, pNC, "subqueries", - NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr); - wx_sqlite3WalkSelect(pWalker, pExpr->x.pSelect); + if( pNC->ncFlags & NC_SelfRef ){ + notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr); + }else{ + wx_sqlite3WalkSelect(pWalker, pExpr->x.pSelect); + } assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); - pNC->ncFlags |= NC_VarSelect; } + pNC->ncFlags |= NC_Subquery; } break; } @@ -99833,7 +104918,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); wx_sqlite3ResolveNotValid(pParse, pNC, "parameters", - NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr); + NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr, pExpr); break; } case TK_IS: @@ -99865,6 +104950,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ assert( pExpr->pLeft!=0 ); nLeft = wx_sqlite3ExprVectorSize(pExpr->pLeft); if( pExpr->op==TK_BETWEEN ){ + assert( ExprUseXList(pExpr) ); nRight = wx_sqlite3ExprVectorSize(pExpr->x.pList->a[0].pExpr); if( nRight==nLeft ){ nRight = wx_sqlite3ExprVectorSize(pExpr->x.pList->a[1].pExpr); @@ -99884,11 +104970,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_ISNOT ); testcase( pExpr->op==TK_BETWEEN ); wx_sqlite3ErrorMsg(pParse, "row value misused"); + wx_sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); } break; } } - return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; + assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); + return pParse->nErr ? WRC_Abort : WRC_Continue; } /* @@ -99913,9 +105001,11 @@ static int resolveAsName( UNUSED_PARAMETER(pParse); if( pE->op==TK_ID ){ - char *zCol = pE->u.zToken; + const char *zCol; + assert( !ExprHasProperty(pE, EP_IntValue) ); + zCol = pE->u.zToken; for(i=0; inExpr; i++){ - if( pEList->a[i].eEName==ENAME_NAME + if( pEList->a[i].fg.eEName==ENAME_NAME && wx_sqlite3_stricmp(pEList->a[i].zEName, zCol)==0 ){ return i+1; @@ -99964,11 +105054,11 @@ static int resolveOrderByTermToExprList( nc.pParse = pParse; nc.pSrcList = pSelect->pSrc; nc.uNC.pEList = pEList; - nc.ncFlags = NC_AllowAgg|NC_UEList; - nc.nErr = 0; + nc.ncFlags = NC_AllowAgg|NC_UEList|NC_NoSelect; + nc.nNcErr = 0; db = pParse->db; savedSuppErr = db->suppressErr; - if( IN_RENAME_OBJECT==0 ) db->suppressErr = 1; + db->suppressErr = 1; rc = wx_sqlite3ResolveExprNames(&nc, pE); db->suppressErr = savedSuppErr; if( rc ) return 0; @@ -99994,11 +105084,13 @@ static void resolveOutOfRangeError( Parse *pParse, /* The error context into which to write the error */ const char *zType, /* "ORDER" or "GROUP" */ int i, /* The index (1-based) of the term out of range */ - int mx /* Largest permissible value of i */ + int mx, /* Largest permissible value of i */ + Expr *pError /* Associate the error with the expression */ ){ wx_sqlite3ErrorMsg(pParse, "%r %s BY term out of range - should be " "between 1 and %d", i, zType, mx); + wx_sqlite3RecordErrorOffsetOfExpr(pParse->db, pError); } /* @@ -100034,7 +105126,7 @@ static int resolveCompoundOrderBy( return 1; } for(i=0; inExpr; i++){ - pOrderBy->a[i].done = 0; + pOrderBy->a[i].fg.done = 0; } pSelect->pNext = 0; while( pSelect->pPrior ){ @@ -100049,12 +105141,12 @@ static int resolveCompoundOrderBy( for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ int iCol = -1; Expr *pE, *pDup; - if( pItem->done ) continue; + if( pItem->fg.done ) continue; pE = wx_sqlite3ExprSkipCollateAndLikely(pItem->pExpr); if( NEVER(pE==0) ) continue; if( wx_sqlite3ExprIsInteger(pE, &iCol) ){ if( iCol<=0 || iCol>pEList->nExpr ){ - resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr); + resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr, pE); return 1; } }else{ @@ -100067,29 +105159,24 @@ static int resolveCompoundOrderBy( ** Once the comparisons are finished, the duplicate expression ** is deleted. ** - ** Or, if this is running as part of an ALTER TABLE operation, - ** resolve the symbols in the actual expression, not a duplicate. - ** And, if one of the comparisons is successful, leave the expression - ** as is instead of transforming it to an integer as in the usual - ** case. This allows the code in alter.c to modify column - ** refererences within the ORDER BY expression as required. */ - if( IN_RENAME_OBJECT ){ - pDup = pE; - }else{ - pDup = wx_sqlite3ExprDup(db, pE, 0); - } + ** If this is running as part of an ALTER TABLE operation and + ** the symbols resolve successfully, also resolve the symbols in the + ** actual expression. This allows the code in alter.c to modify + ** column references within the ORDER BY expression as required. */ + pDup = wx_sqlite3ExprDup(db, pE, 0); if( !db->mallocFailed ){ assert(pDup); iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup); + if( IN_RENAME_OBJECT && iCol>0 ){ + resolveOrderByTermToExprList(pParse, pSelect, pE); + } } - if( !IN_RENAME_OBJECT ){ - wx_sqlite3ExprDelete(db, pDup); - } + wx_sqlite3ExprDelete(db, pDup); } } if( iCol>0 ){ /* Convert the ORDER BY term into an integer column number iCol, - ** taking care to preserve the COLLATE clause if it exists */ + ** taking care to preserve the COLLATE clause if it exists. */ if( !IN_RENAME_OBJECT ){ Expr *pNew = wx_sqlite3Expr(db, TK_INTEGER, 0); if( pNew==0 ) return 1; @@ -100107,7 +105194,7 @@ static int resolveCompoundOrderBy( wx_sqlite3ExprDelete(db, pE); pItem->u.x.iOrderByCol = (u16)iCol; } - pItem->done = 1; + pItem->fg.done = 1; }else{ moreToDo = 1; } @@ -100115,7 +105202,7 @@ static int resolveCompoundOrderBy( pSelect = pSelect->pNext; } for(i=0; inExpr; i++){ - if( pOrderBy->a[i].done==0 ){ + if( pOrderBy->a[i].fg.done==0 ){ wx_sqlite3ErrorMsg(pParse, "%r ORDER BY term does not match any " "column in the result set", i+1); return 1; @@ -100155,7 +105242,7 @@ SQLITE_PRIVATE int wx_sqlite3ResolveOrderGroupBy( for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ if( pItem->u.x.iOrderByCol ){ if( pItem->u.x.iOrderByCol>pEList->nExpr ){ - resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); + resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr, 0); return 1; } resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,0); @@ -100224,7 +105311,7 @@ static int resolveOrderGroupBy( Parse *pParse; /* Parsing context */ int nResult; /* Number of terms in the result set */ - if( pOrderBy==0 ) return 0; + assert( pOrderBy!=0 ); nResult = pSelect->pEList->nExpr; pParse = pNC->pParse; for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ @@ -100247,7 +105334,7 @@ static int resolveOrderGroupBy( ** number so that wx_sqlite3ResolveOrderGroupBy() will convert the ** order-by term to a copy of the result-set expression */ if( iCol<1 || iCol>0xffff ){ - resolveOutOfRangeError(pParse, zType, i+1, nResult); + resolveOutOfRangeError(pParse, zType, i+1, nResult, pE2); return 1; } pItem->u.x.iOrderByCol = (u16)iCol; @@ -100305,7 +105392,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ */ if( (p->selFlags & SF_Expanded)==0 ){ wx_sqlite3SelectPrep(pParse, p, pOuterNC); - return (pParse->nErr || db->mallocFailed) ? WRC_Abort : WRC_Prune; + return pParse->nErr ? WRC_Abort : WRC_Prune; } isCompound = p->pPrior!=0; @@ -100314,8 +105401,10 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ while( p ){ assert( (p->selFlags & SF_Expanded)!=0 ); assert( (p->selFlags & SF_Resolved)==0 ); + assert( db->suppressErr==0 ); /* SF_Resolved not set if errors suppressed */ p->selFlags |= SF_Resolved; + /* Resolve the expressions in the LIMIT and OFFSET clauses. These ** are not allowed to refer to any names, so pass an empty NameContext. */ @@ -100340,7 +105429,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ p->pOrderBy = 0; } - /* Recursively resolve names in all subqueries + /* Recursively resolve names in all subqueries in the FROM clause */ for(i=0; ipSrc->nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; @@ -100351,7 +105440,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ if( pItem->zName ) pParse->zAuthContext = pItem->zName; wx_sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); pParse->zAuthContext = zSavedContext; - if( pParse->nErr || db->mallocFailed ) return WRC_Abort; + if( pParse->nErr ) return WRC_Abort; + assert( db->mallocFailed==0 ); /* If the number of references to the outer context changed when ** expressions in the sub-select were resolved, the sub-select @@ -100384,18 +105474,12 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ pGroupBy = p->pGroupBy; if( pGroupBy || (sNC.ncFlags & NC_HasAgg)!=0 ){ assert( NC_MinMaxAgg==SF_MinMaxAgg ); - p->selFlags |= SF_Aggregate | (sNC.ncFlags&NC_MinMaxAgg); + assert( NC_OrderAgg==SF_OrderByReqd ); + p->selFlags |= SF_Aggregate | (sNC.ncFlags&(NC_MinMaxAgg|NC_OrderAgg)); }else{ sNC.ncFlags &= ~NC_AllowAgg; } - /* If a HAVING clause is present, then there must be a GROUP BY clause. - */ - if( p->pHaving && !pGroupBy ){ - wx_sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); - return WRC_Abort; - } - /* Add the output column list to the name-context before parsing the ** other expressions in the SELECT statement. This is so that ** expressions in the WHERE clause (etc.) can refer to expressions by @@ -100407,7 +105491,13 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert|NC_UBaseReg))==0 ); sNC.uNC.pEList = p->pEList; sNC.ncFlags |= NC_UEList; - if( wx_sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; + if( p->pHaving ){ + if( (p->selFlags & SF_Aggregate)==0 ){ + wx_sqlite3ErrorMsg(pParse, "HAVING clause on a non-aggregate query"); + return WRC_Abort; + } + if( wx_sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; + } if( wx_sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; /* Resolve names in table-valued-function arguments */ @@ -100420,6 +105510,19 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } +#ifndef SQLITE_OMIT_WINDOWFUNC + if( IN_RENAME_OBJECT ){ + Window *pWin; + for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){ + if( wx_sqlite3ResolveExprListNames(&sNC, pWin->pOrderBy) + || wx_sqlite3ResolveExprListNames(&sNC, pWin->pPartition) + ){ + return WRC_Abort; + } + } + } +#endif + /* The ORDER BY and GROUP BY clauses may not refer to terms in ** outer queries */ @@ -100447,7 +105550,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ ** is not detected until much later, and so we need to go ahead and ** resolve those symbols on the incorrect ORDER BY for consistency. */ - if( isCompound<=nCompound /* Defer right-most ORDER BY of a compound */ + if( p->pOrderBy!=0 + && isCompound<=nCompound /* Defer right-most ORDER BY of a compound */ && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") ){ return WRC_Abort; @@ -100475,19 +105579,6 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } -#ifndef SQLITE_OMIT_WINDOWFUNC - if( IN_RENAME_OBJECT ){ - Window *pWin; - for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){ - if( wx_sqlite3ResolveExprListNames(&sNC, pWin->pOrderBy) - || wx_sqlite3ResolveExprListNames(&sNC, pWin->pPartition) - ){ - return WRC_Abort; - } - } - } -#endif - /* If this is part of a compound SELECT, check that it has the right ** number of expressions in the select list. */ if( p->pNext && p->pEList->nExpr!=p->pNext->pEList->nExpr ){ @@ -100567,11 +105658,11 @@ SQLITE_PRIVATE int wx_sqlite3ResolveExprNames( Walker w; if( pExpr==0 ) return SQLITE_OK; - savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); - pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); w.pParse = pNC->pParse; w.xExprCallback = resolveExprStep; - w.xSelectCallback = resolveSelectStep; + w.xSelectCallback = (pNC->ncFlags & NC_NoSelect) ? 0 : resolveSelectStep; w.xSelectCallback2 = 0; w.u.pNC = pNC; #if SQLITE_MAX_EXPR_DEPTH>0 @@ -100590,7 +105681,7 @@ SQLITE_PRIVATE int wx_sqlite3ResolveExprNames( testcase( pNC->ncFlags & NC_HasWin ); ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); pNC->ncFlags |= savedHasAgg; - return pNC->nErr>0 || w.pParse->nErr>0; + return pNC->nNcErr>0 || w.pParse->nErr>0; } /* @@ -100611,8 +105702,8 @@ SQLITE_PRIVATE int wx_sqlite3ResolveExprListNames( w.xSelectCallback = resolveSelectStep; w.xSelectCallback2 = 0; w.u.pNC = pNC; - savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); - pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); for(i=0; inExpr; i++){ Expr *pExpr = pList->a[i].pExpr; if( pExpr==0 ) continue; @@ -100630,12 +105721,13 @@ SQLITE_PRIVATE int wx_sqlite3ResolveExprListNames( assert( EP_Win==NC_HasWin ); testcase( pNC->ncFlags & NC_HasAgg ); testcase( pNC->ncFlags & NC_HasWin ); - if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin) ){ + if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg) ){ ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); - savedHasAgg |= pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); - pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + savedHasAgg |= pNC->ncFlags & + (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); } - if( pNC->nErr>0 || w.pParse->nErr>0 ) return WRC_Abort; + if( w.pParse->nErr>0 ) return WRC_Abort; } pNC->ncFlags |= savedHasAgg; return WRC_Continue; @@ -100747,9 +105839,9 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree); /* ** Return the affinity character for a single column of a table. */ -SQLITE_PRIVATE char wx_sqlite3TableColumnAffinity(Table *pTab, int iCol){ - assert( iColnCol ); - return iCol>=0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER; +SQLITE_PRIVATE char wx_sqlite3TableColumnAffinity(const Table *pTab, int iCol){ + if( iCol<0 || NEVER(iCol>=pTab->nCol) ) return SQLITE_AFF_INTEGER; + return pTab->aCol[iCol].affinity; } /* @@ -100770,43 +105862,121 @@ SQLITE_PRIVATE char wx_sqlite3TableColumnAffinity(Table *pTab, int iCol){ */ SQLITE_PRIVATE char wx_sqlite3ExprAffinity(const Expr *pExpr){ int op; - while( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){ - assert( pExpr->op==TK_COLLATE - || pExpr->op==TK_IF_NULL_ROW - || (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) ); - pExpr = pExpr->pLeft; - assert( pExpr!=0 ); - } op = pExpr->op; - if( op==TK_SELECT ){ - assert( pExpr->flags&EP_xIsSelect ); - assert( pExpr->x.pSelect!=0 ); - assert( pExpr->x.pSelect->pEList!=0 ); - assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 ); - return wx_sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); - } - if( op==TK_REGISTER ) op = pExpr->op2; + while( 1 /* exit-by-break */ ){ + if( op==TK_COLUMN || (op==TK_AGG_COLUMN && pExpr->y.pTab!=0) ){ + assert( ExprUseYTab(pExpr) ); + assert( pExpr->y.pTab!=0 ); + return wx_sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); + } + if( op==TK_SELECT ){ + assert( ExprUseXSelect(pExpr) ); + assert( pExpr->x.pSelect!=0 ); + assert( pExpr->x.pSelect->pEList!=0 ); + assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 ); + return wx_sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); + } #ifndef SQLITE_OMIT_CAST - if( op==TK_CAST ){ - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - return wx_sqlite3AffinityType(pExpr->u.zToken, 0); - } + if( op==TK_CAST ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + return wx_sqlite3AffinityType(pExpr->u.zToken, 0); + } #endif - if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->y.pTab ){ - return wx_sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); - } - if( op==TK_SELECT_COLUMN ){ - assert( pExpr->pLeft->flags&EP_xIsSelect ); - return wx_sqlite3ExprAffinity( - pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr - ); - } - if( op==TK_VECTOR ){ - return wx_sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr); + if( op==TK_SELECT_COLUMN ){ + assert( pExpr->pLeft!=0 && ExprUseXSelect(pExpr->pLeft) ); + assert( pExpr->iColumn < pExpr->iTable ); + assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr ); + return wx_sqlite3ExprAffinity( + pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr + ); + } + if( op==TK_VECTOR ){ + assert( ExprUseXList(pExpr) ); + return wx_sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr); + } + if( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){ + assert( pExpr->op==TK_COLLATE + || pExpr->op==TK_IF_NULL_ROW + || (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) ); + pExpr = pExpr->pLeft; + op = pExpr->op; + continue; + } + if( op!=TK_REGISTER || (op = pExpr->op2)==TK_REGISTER ) break; } return pExpr->affExpr; } +/* +** Make a guess at all the possible datatypes of the result that could +** be returned by an expression. Return a bitmask indicating the answer: +** +** 0x01 Numeric +** 0x02 Text +** 0x04 Blob +** +** If the expression must return NULL, then 0x00 is returned. +*/ +SQLITE_PRIVATE int wx_sqlite3ExprDataType(const Expr *pExpr){ + while( pExpr ){ + switch( pExpr->op ){ + case TK_COLLATE: + case TK_IF_NULL_ROW: + case TK_UPLUS: { + pExpr = pExpr->pLeft; + break; + } + case TK_NULL: { + pExpr = 0; + break; + } + case TK_STRING: { + return 0x02; + } + case TK_BLOB: { + return 0x04; + } + case TK_CONCAT: { + return 0x06; + } + case TK_VARIABLE: + case TK_AGG_FUNCTION: + case TK_FUNCTION: { + return 0x07; + } + case TK_COLUMN: + case TK_AGG_COLUMN: + case TK_SELECT: + case TK_CAST: + case TK_SELECT_COLUMN: + case TK_VECTOR: { + int aff = wx_sqlite3ExprAffinity(pExpr); + if( aff>=SQLITE_AFF_NUMERIC ) return 0x05; + if( aff==SQLITE_AFF_TEXT ) return 0x06; + return 0x07; + } + case TK_CASE: { + int res = 0; + int ii; + ExprList *pList = pExpr->x.pList; + assert( ExprUseXList(pExpr) && pList!=0 ); + assert( pList->nExpr > 0); + for(ii=1; iinExpr; ii+=2){ + res |= wx_sqlite3ExprDataType(pList->a[ii].pExpr); + } + if( pList->nExpr % 2 ){ + res |= wx_sqlite3ExprDataType(pList->a[pList->nExpr-1].pExpr); + } + return res; + } + default: { + return 0x01; + } + } /* End of switch(op) */ + } /* End of while(pExpr) */ + return 0x00; +} + /* ** Set the collating sequence for expression pExpr to be the collating ** sequence named by pToken. Return a pointer to a new Expr node that @@ -100816,23 +105986,12 @@ SQLITE_PRIVATE char wx_sqlite3ExprAffinity(const Expr *pExpr){ ** and the pExpr parameter is returned unchanged. */ SQLITE_PRIVATE Expr *wx_sqlite3ExprAddCollateToken( - Parse *pParse, /* Parsing context */ + const Parse *pParse, /* Parsing context */ Expr *pExpr, /* Add the "COLLATE" clause to this expression */ const Token *pCollName, /* Name of collating sequence */ int dequote /* True to dequote pCollName */ ){ - assert( pExpr!=0 || pParse->db->mallocFailed ); - if( pExpr==0 ) return 0; - if( pExpr->op==TK_VECTOR ){ - ExprList *pList = pExpr->x.pList; - if( ALWAYS(pList!=0) ){ - int i; - for(i=0; inExpr; i++){ - pList->a[i].pExpr = wx_sqlite3ExprAddCollateToken(pParse,pList->a[i].pExpr, - pCollName, dequote); - } - } - }else if( pCollName->n>0 ){ + if( pCollName->n>0 ){ Expr *pNew = wx_sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote); if( pNew ){ pNew->pLeft = pExpr; @@ -100842,7 +106001,11 @@ SQLITE_PRIVATE Expr *wx_sqlite3ExprAddCollateToken( } return pExpr; } -SQLITE_PRIVATE Expr *wx_sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){ +SQLITE_PRIVATE Expr *wx_sqlite3ExprAddCollateString( + const Parse *pParse, /* Parsing context */ + Expr *pExpr, /* Add the "COLLATE" clause to this expression */ + const char *zC /* The collating sequence name */ +){ Token s; assert( zC!=0 ); wx_sqlite3TokenInit(&s, (char*)zC); @@ -100868,7 +106031,7 @@ SQLITE_PRIVATE Expr *wx_sqlite3ExprSkipCollate(Expr *pExpr){ SQLITE_PRIVATE Expr *wx_sqlite3ExprSkipCollateAndLikely(Expr *pExpr){ while( pExpr && ExprHasProperty(pExpr, EP_Skip|EP_Unlikely) ){ if( ExprHasProperty(pExpr, EP_Unlikely) ){ - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( ExprUseXList(pExpr) ); assert( pExpr->x.pList->nExpr>0 ); assert( pExpr->op==TK_FUNCTION ); pExpr = pExpr->x.pList->a[0].pExpr; @@ -100901,14 +106064,14 @@ SQLITE_PRIVATE CollSeq *wx_sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ while( p ){ int op = p->op; if( op==TK_REGISTER ) op = p->op2; - if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER) - && p->y.pTab!=0 + if( (op==TK_AGG_COLUMN && p->y.pTab!=0) + || op==TK_COLUMN || op==TK_TRIGGER ){ - /* op==TK_REGISTER && p->y.pTab!=0 happens when pExpr was originally - ** a TK_COLUMN but was previously evaluated and cached in a register */ - int j = p->iColumn; - if( j>=0 ){ - const char *zColl = p->y.pTab->aCol[j].zColl; + int j; + assert( ExprUseYTab(p) ); + assert( p->y.pTab!=0 ); + if( (j = p->iColumn)>=0 ){ + const char *zColl = wx_sqlite3ColumnColl(&p->y.pTab->aCol[j]); pColl = wx_sqlite3FindCollSeq(db, ENC(db), zColl, 0); } break; @@ -100918,10 +106081,12 @@ SQLITE_PRIVATE CollSeq *wx_sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ continue; } if( op==TK_VECTOR ){ + assert( ExprUseXList(p) ); p = p->x.pList->a[0].pExpr; continue; } if( op==TK_COLLATE ){ + assert( !ExprHasProperty(p, EP_IntValue) ); pColl = wx_sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); break; } @@ -100931,11 +106096,9 @@ SQLITE_PRIVATE CollSeq *wx_sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ }else{ Expr *pNext = p->pRight; /* The Expr.x union is never used at the same time as Expr.pRight */ + assert( ExprUseXList(p) ); assert( p->x.pList==0 || p->pRight==0 ); - if( p->x.pList!=0 - && !db->mallocFailed - && ALWAYS(!ExprHasProperty(p, EP_xIsSelect)) - ){ + if( p->x.pList!=0 && !db->mallocFailed ){ int i; for(i=0; ALWAYS(ix.pList->nExpr); i++){ if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){ @@ -101018,7 +106181,7 @@ static char comparisonAffinity(const Expr *pExpr){ aff = wx_sqlite3ExprAffinity(pExpr->pLeft); if( pExpr->pRight ){ aff = wx_sqlite3CompareAffinity(pExpr->pRight, aff); - }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + }else if( ExprUseXSelect(pExpr) ){ aff = wx_sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff); }else if( aff==0 ){ aff = SQLITE_AFF_BLOB; @@ -101144,7 +106307,7 @@ static int codeCompare( ** But a TK_SELECT might be either a vector or a scalar. It is only ** considered a vector if it has two or more result columns. */ -SQLITE_PRIVATE int wx_sqlite3ExprIsVector(Expr *pExpr){ +SQLITE_PRIVATE int wx_sqlite3ExprIsVector(const Expr *pExpr){ return wx_sqlite3ExprVectorSize(pExpr)>1; } @@ -101154,12 +106317,14 @@ SQLITE_PRIVATE int wx_sqlite3ExprIsVector(Expr *pExpr){ ** is a sub-select, return the number of columns in the sub-select. For ** any other type of expression, return 1. */ -SQLITE_PRIVATE int wx_sqlite3ExprVectorSize(Expr *pExpr){ +SQLITE_PRIVATE int wx_sqlite3ExprVectorSize(const Expr *pExpr){ u8 op = pExpr->op; if( op==TK_REGISTER ) op = pExpr->op2; if( op==TK_VECTOR ){ + assert( ExprUseXList(pExpr) ); return pExpr->x.pList->nExpr; }else if( op==TK_SELECT ){ + assert( ExprUseXSelect(pExpr) ); return pExpr->x.pSelect->pEList->nExpr; }else{ return 1; @@ -101182,12 +106347,14 @@ SQLITE_PRIVATE int wx_sqlite3ExprVectorSize(Expr *pExpr){ ** been positioned. */ SQLITE_PRIVATE Expr *wx_sqlite3VectorFieldSubexpr(Expr *pVector, int i){ - assert( iop==TK_ERROR ); if( wx_sqlite3ExprIsVector(pVector) ){ assert( pVector->op2==0 || pVector->op==TK_REGISTER ); if( pVector->op==TK_SELECT || pVector->op2==TK_SELECT ){ + assert( ExprUseXSelect(pVector) ); return pVector->x.pSelect->pEList->a[i].pExpr; }else{ + assert( ExprUseXList(pVector) ); return pVector->x.pList->a[i].pExpr; } } @@ -101218,11 +106385,12 @@ SQLITE_PRIVATE Expr *wx_sqlite3VectorFieldSubexpr(Expr *pVector, int i){ SQLITE_PRIVATE Expr *wx_sqlite3ExprForVectorField( Parse *pParse, /* Parsing context */ Expr *pVector, /* The vector. List of expressions or a sub-SELECT */ - int iField /* Which column of the vector to return */ + int iField, /* Which column of the vector to return */ + int nField /* Total number of columns in the vector */ ){ Expr *pRet; if( pVector->op==TK_SELECT ){ - assert( pVector->flags & EP_xIsSelect ); + assert( ExprUseXSelect(pVector) ); /* The TK_SELECT_COLUMN Expr node: ** ** pLeft: pVector containing TK_SELECT. Not deleted. @@ -101241,14 +106409,23 @@ SQLITE_PRIVATE Expr *wx_sqlite3ExprForVectorField( */ pRet = wx_sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0); if( pRet ){ + pRet->iTable = nField; pRet->iColumn = iField; pRet->pLeft = pVector; } - assert( pRet==0 || pRet->iTable==0 ); }else{ - if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr; + if( pVector->op==TK_VECTOR ){ + Expr **ppVector; + assert( ExprUseXList(pVector) ); + ppVector = &pVector->x.pList->a[iField].pExpr; + pVector = *ppVector; + if( IN_RENAME_OBJECT ){ + /* This must be a vector UPDATE inside a trigger */ + *ppVector = 0; + return pVector; + } + } pRet = wx_sqlite3ExprDup(pParse->db, pVector, 0); - wx_sqlite3RenameTokenRemap(pParse, pRet, pVector); } return pRet; } @@ -101298,17 +106475,22 @@ static int exprVectorRegister( int *pRegFree /* OUT: Temp register to free */ ){ u8 op = pVector->op; - assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT ); + assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT || op==TK_ERROR ); if( op==TK_REGISTER ){ *ppExpr = wx_sqlite3VectorFieldSubexpr(pVector, iField); return pVector->iTable+iField; } if( op==TK_SELECT ){ + assert( ExprUseXSelect(pVector) ); *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr; return regSelect+iField; } - *ppExpr = pVector->x.pList->a[iField].pExpr; - return wx_sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree); + if( op==TK_VECTOR ){ + assert( ExprUseXList(pVector) ); + *ppExpr = pVector->x.pList->a[iField].pExpr; + return wx_sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree); + } + return 0; } /* @@ -101337,6 +106519,7 @@ static void codeVectorCompare( int regLeft = 0; int regRight = 0; u8 opx = op; + int addrCmp = 0; int addrDone = wx_sqlite3VdbeMakeLabel(pParse); int isCommuted = ExprHasProperty(pExpr,EP_Commuted); @@ -101356,21 +106539,24 @@ static void codeVectorCompare( assert( p5==0 || pExpr->op!=op ); assert( p5==SQLITE_NULLEQ || pExpr->op==op ); - p5 |= SQLITE_STOREP2; - if( opx==TK_LE ) opx = TK_LT; - if( opx==TK_GE ) opx = TK_GT; + if( op==TK_LE ) opx = TK_LT; + if( op==TK_GE ) opx = TK_GT; + if( op==TK_NE ) opx = TK_EQ; regLeft = exprCodeSubselect(pParse, pLeft); regRight = exprCodeSubselect(pParse, pRight); + wx_sqlite3VdbeAddOp2(v, OP_Integer, 1, dest); for(i=0; 1 /*Loop exits by "break"*/; i++){ int regFree1 = 0, regFree2 = 0; - Expr *pL, *pR; + Expr *pL = 0, *pR = 0; int r1, r2; assert( i>=0 && i0 @@ -101428,14 +106620,14 @@ SQLITE_PRIVATE int wx_sqlite3ExprCheckHeight(Parse *pParse, int nHeight){ ** to by pnHeight, the second parameter, then set *pnHeight to that ** value. */ -static void heightOfExpr(Expr *p, int *pnHeight){ +static void heightOfExpr(const Expr *p, int *pnHeight){ if( p ){ if( p->nHeight>*pnHeight ){ *pnHeight = p->nHeight; } } } -static void heightOfExprList(ExprList *p, int *pnHeight){ +static void heightOfExprList(const ExprList *p, int *pnHeight){ if( p ){ int i; for(i=0; inExpr; i++){ @@ -101443,8 +106635,8 @@ static void heightOfExprList(ExprList *p, int *pnHeight){ } } } -static void heightOfSelect(Select *pSelect, int *pnHeight){ - Select *p; +static void heightOfSelect(const Select *pSelect, int *pnHeight){ + const Select *p; for(p=pSelect; p; p=p->pPrior){ heightOfExpr(p->pWhere, pnHeight); heightOfExpr(p->pHaving, pnHeight); @@ -101466,10 +106658,11 @@ static void heightOfSelect(Select *pSelect, int *pnHeight){ ** if appropriate. */ static void exprSetHeight(Expr *p){ - int nHeight = 0; - heightOfExpr(p->pLeft, &nHeight); - heightOfExpr(p->pRight, &nHeight); - if( ExprHasProperty(p, EP_xIsSelect) ){ + int nHeight = p->pLeft ? p->pLeft->nHeight : 0; + if( NEVER(p->pRight) && p->pRight->nHeight>nHeight ){ + nHeight = p->pRight->nHeight; + } + if( ExprUseXSelect(p) ){ heightOfSelect(p->x.pSelect, &nHeight); }else if( p->x.pList ){ heightOfExprList(p->x.pList, &nHeight); @@ -101496,7 +106689,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ ** Return the maximum height of any expression tree referenced ** by the select statement passed as an argument. */ -SQLITE_PRIVATE int wx_sqlite3SelectExprHeight(Select *p){ +SQLITE_PRIVATE int wx_sqlite3SelectExprHeight(const Select *p){ int nHeight = 0; heightOfSelect(p, &nHeight); return nHeight; @@ -101508,7 +106701,7 @@ SQLITE_PRIVATE int wx_sqlite3SelectExprHeight(Select *p){ */ SQLITE_PRIVATE void wx_sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ if( pParse->nErr ) return; - if( p && p->x.pList && !ExprHasProperty(p, EP_xIsSelect) ){ + if( p && ExprUseXList(p) && p->x.pList ){ p->flags |= EP_Propagate & wx_sqlite3ExprListFlags(p->x.pList); } } @@ -101611,15 +106804,26 @@ SQLITE_PRIVATE void wx_sqlite3ExprAttachSubtrees( wx_sqlite3ExprDelete(db, pLeft); wx_sqlite3ExprDelete(db, pRight); }else{ + assert( ExprUseXList(pRoot) ); + assert( pRoot->x.pSelect==0 ); if( pRight ){ pRoot->pRight = pRight; pRoot->flags |= EP_Propagate & pRight->flags; +#if SQLITE_MAX_EXPR_DEPTH>0 + pRoot->nHeight = pRight->nHeight+1; + }else{ + pRoot->nHeight = 1; +#endif } if( pLeft ){ pRoot->pLeft = pLeft; pRoot->flags |= EP_Propagate & pLeft->flags; +#if SQLITE_MAX_EXPR_DEPTH>0 + if( pLeft->nHeight>=pRoot->nHeight ){ + pRoot->nHeight = pLeft->nHeight+1; + } +#endif } - exprSetHeight(pRoot); } } @@ -101666,6 +106870,63 @@ SQLITE_PRIVATE void wx_sqlite3PExprAddSelect(Parse *pParse, Expr *pExpr, Select } } +/* +** Expression list pEList is a list of vector values. This function +** converts the contents of pEList to a VALUES(...) Select statement +** returning 1 row for each element of the list. For example, the +** expression list: +** +** ( (1,2), (3,4) (5,6) ) +** +** is translated to the equivalent of: +** +** VALUES(1,2), (3,4), (5,6) +** +** Each of the vector values in pEList must contain exactly nElem terms. +** If a list element that is not a vector or does not contain nElem terms, +** an error message is left in pParse. +** +** This is used as part of processing IN(...) expressions with a list +** of vectors on the RHS. e.g. "... IN ((1,2), (3,4), (5,6))". +*/ +SQLITE_PRIVATE Select *wx_sqlite3ExprListToValues(Parse *pParse, int nElem, ExprList *pEList){ + int ii; + Select *pRet = 0; + assert( nElem>1 ); + for(ii=0; iinExpr; ii++){ + Select *pSel; + Expr *pExpr = pEList->a[ii].pExpr; + int nExprElem; + if( pExpr->op==TK_VECTOR ){ + assert( ExprUseXList(pExpr) ); + nExprElem = pExpr->x.pList->nExpr; + }else{ + nExprElem = 1; + } + if( nExprElem!=nElem ){ + wx_sqlite3ErrorMsg(pParse, "IN(...) element has %d term%s - expected %d", + nExprElem, nExprElem>1?"s":"", nElem + ); + break; + } + assert( ExprUseXList(pExpr) ); + pSel = wx_sqlite3SelectNew(pParse, pExpr->x.pList, 0, 0, 0, 0, 0, SF_Values,0); + pExpr->x.pList = 0; + if( pSel ){ + if( pRet ){ + pSel->op = TK_ALL; + pSel->pPrior = pRet; + } + pRet = pSel; + } + } + + if( pRet && pRet->pPrior ){ + pRet->selFlags |= SF_MultiValue; + } + wx_sqlite3ExprListDelete(pParse->db, pEList); + return pRet; +} /* ** Join two expressions using an AND operator. If either expression is @@ -101699,7 +106960,7 @@ SQLITE_PRIVATE Expr *wx_sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight) SQLITE_PRIVATE Expr *wx_sqlite3ExprFunction( Parse *pParse, /* Parsing context */ ExprList *pList, /* Argument list */ - Token *pToken, /* Name of the function */ + const Token *pToken, /* Name of the function */ int eDistinct /* SF_Distinct or SF_ALL or 0 */ ){ Expr *pNew; @@ -101710,12 +106971,17 @@ SQLITE_PRIVATE Expr *wx_sqlite3ExprFunction( wx_sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */ return 0; } - if( pList && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ + assert( !ExprHasProperty(pNew, EP_InnerON|EP_OuterON) ); + pNew->w.iOfst = (int)(pToken->z - pParse->zTail); + if( pList + && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] + && !pParse->nested + ){ wx_sqlite3ErrorMsg(pParse, "too many arguments on function %T", pToken); } pNew->x.pList = pList; ExprSetProperty(pNew, EP_HasFunc); - assert( !ExprHasProperty(pNew, EP_xIsSelect) ); + assert( ExprUseXList(pNew) ); wx_sqlite3ExprSetHeightAndFlags(pParse, pNew); if( eDistinct==SF_Distinct ) ExprSetProperty(pNew, EP_Distinct); return pNew; @@ -101734,8 +107000,8 @@ SQLITE_PRIVATE Expr *wx_sqlite3ExprFunction( */ SQLITE_PRIVATE void wx_sqlite3ExprFunctionUsable( Parse *pParse, /* Parsing and code generating context */ - Expr *pExpr, /* The function invocation */ - FuncDef *pDef /* The function being invoked */ + const Expr *pExpr, /* The function invocation */ + const FuncDef *pDef /* The function being invoked */ ){ assert( !IN_RENAME_OBJECT ); assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 ); @@ -101750,7 +107016,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprFunctionUsable( ** SQLITE_DBCONFIG_TRUSTED_SCHEMA is off (meaning ** that the schema is possibly tainted). */ - wx_sqlite3ErrorMsg(pParse, "unsafe use of %s()", pDef->zName); + wx_sqlite3ErrorMsg(pParse, "unsafe use of %#T()", pExpr); } } } @@ -101806,6 +107072,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u3 if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ wx_sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]); + wx_sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); return; } x = (ynVar)i; @@ -101833,6 +107100,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u3 pExpr->iColumn = x; if( x>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ wx_sqlite3ErrorMsg(pParse, "too many SQL variables"); + wx_sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); } } @@ -101841,27 +107109,27 @@ SQLITE_PRIVATE void wx_sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u3 */ static SQLITE_NOINLINE void wx_sqlite3ExprDeleteNN(wx_sqlite3 *db, Expr *p){ assert( p!=0 ); - /* Sanity check: Assert that the IntValue is non-negative if it exists */ - assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 ); - - assert( !ExprHasProperty(p, EP_WinFunc) || p->y.pWin!=0 || db->mallocFailed ); - assert( p->op!=TK_FUNCTION || ExprHasProperty(p, EP_TokenOnly|EP_Reduced) - || p->y.pWin==0 || ExprHasProperty(p, EP_WinFunc) ); + assert( db!=0 ); + assert( !ExprUseUValue(p) || p->u.iValue>=0 ); + assert( !ExprUseYWin(p) || !ExprUseYSub(p) ); + assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed ); + assert( p->op!=TK_FUNCTION || !ExprUseYSub(p) ); #ifdef SQLITE_DEBUG if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){ assert( p->pLeft==0 ); assert( p->pRight==0 ); - assert( p->x.pSelect==0 ); + assert( !ExprUseXSelect(p) || p->x.pSelect==0 ); + assert( !ExprUseXList(p) || p->x.pList==0 ); } #endif if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ - assert( p->x.pList==0 || p->pRight==0 ); + assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); if( p->pLeft && p->op!=TK_SELECT_COLUMN ) wx_sqlite3ExprDeleteNN(db, p->pLeft); if( p->pRight ){ assert( !ExprHasProperty(p, EP_WinFunc) ); wx_sqlite3ExprDeleteNN(db, p->pRight); - }else if( ExprHasProperty(p, EP_xIsSelect) ){ + }else if( ExprUseXSelect(p) ){ assert( !ExprHasProperty(p, EP_WinFunc) ); wx_sqlite3SelectDelete(db, p->x.pSelect); }else{ @@ -101873,15 +107141,26 @@ static SQLITE_NOINLINE void wx_sqlite3ExprDeleteNN(wx_sqlite3 *db, Expr *p){ #endif } } - if( ExprHasProperty(p, EP_MemToken) ) wx_sqlite3DbFree(db, p->u.zToken); if( !ExprHasProperty(p, EP_Static) ){ - wx_sqlite3DbFreeNN(db, p); + wx_sqlite3DbNNFreeNN(db, p); } } SQLITE_PRIVATE void wx_sqlite3ExprDelete(wx_sqlite3 *db, Expr *p){ if( p ) wx_sqlite3ExprDeleteNN(db, p); } +/* +** Clear both elements of an OnOrUsing object +*/ +SQLITE_PRIVATE void wx_sqlite3ClearOnOrUsing(wx_sqlite3 *db, OnOrUsing *p){ + if( p==0 ){ + /* Nothing to clear */ + }else if( p->pOn ){ + wx_sqlite3ExprDeleteNN(db, p->pOn); + }else if( p->pUsing ){ + wx_sqlite3IdListDelete(db, p->pUsing); + } +} /* ** Arrange to cause pExpr to be deleted when the pParse is deleted. @@ -101894,8 +107173,9 @@ SQLITE_PRIVATE void wx_sqlite3ExprDelete(wx_sqlite3 *db, Expr *p){ ** pExpr to the pParse->pConstExpr list with a register number of 0. */ SQLITE_PRIVATE void wx_sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ - pParse->pConstExpr = - wx_sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); + wx_sqlite3ParserAddCleanup(pParse, + (void(*)(wx_sqlite3*,void*))wx_sqlite3ExprDelete, + pExpr); } /* Invoke wx_sqlite3RenameExprUnmap() and wx_sqlite3ExprDelete() on the @@ -101915,7 +107195,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){ ** passed as the first argument. This is always one of EXPR_FULLSIZE, ** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE. */ -static int exprStructSize(Expr *p){ +static int exprStructSize(const Expr *p){ if( ExprHasProperty(p, EP_TokenOnly) ) return EXPR_TOKENONLYSIZE; if( ExprHasProperty(p, EP_Reduced) ) return EXPR_REDUCEDSIZE; return EXPR_FULLSIZE; @@ -101955,7 +107235,7 @@ static int exprStructSize(Expr *p){ ** of dupedExprStructSize() contain multiple assert() statements that attempt ** to enforce this constraint. */ -static int dupedExprStructSize(Expr *p, int flags){ +static int dupedExprStructSize(const Expr *p, int flags){ int nSize; assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */ assert( EXPR_FULLSIZE<=0xfff ); @@ -101968,8 +107248,7 @@ static int dupedExprStructSize(Expr *p, int flags){ nSize = EXPR_FULLSIZE; }else{ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); - assert( !ExprHasProperty(p, EP_FromJoin) ); - assert( !ExprHasProperty(p, EP_MemToken) ); + assert( !ExprHasProperty(p, EP_OuterON) ); assert( !ExprHasVVAProperty(p, EP_NoReduce) ); if( p->pLeft || p->x.pList ){ nSize = EXPR_REDUCEDSIZE | EP_Reduced; @@ -101986,7 +107265,7 @@ static int dupedExprStructSize(Expr *p, int flags){ ** of the Expr structure and a copy of the Expr.u.zToken string (if that ** string is defined.) */ -static int dupedExprNodeSize(Expr *p, int flags){ +static int dupedExprNodeSize(const Expr *p, int flags){ int nByte = dupedExprStructSize(p, flags) & 0xfff; if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ nByte += wx_sqlite3Strlen30NN(p->u.zToken)+1; @@ -102007,7 +107286,7 @@ static int dupedExprNodeSize(Expr *p, int flags){ ** and Expr.pRight variables (but not for any structures pointed to or ** descended from the Expr.x.pList or Expr.x.pSelect variables). */ -static int dupedExprSize(Expr *p, int flags){ +static int dupedExprSize(const Expr *p, int flags){ int nByte = 0; if( p ){ nByte = dupedExprNodeSize(p, flags); @@ -102026,7 +107305,7 @@ static int dupedExprSize(Expr *p, int flags){ ** if any. Before returning, *pzBuffer is set to the first byte past the ** portion of the buffer copied into by this function. */ -static Expr *exprDup(wx_sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ +static Expr *exprDup(wx_sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){ Expr *pNew; /* Value to return */ u8 *zAlloc; /* Memory space from which to build Expr object */ u32 staticFlag; /* EP_Static if space not obtained from malloc */ @@ -102040,6 +107319,7 @@ static Expr *exprDup(wx_sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ if( pzBuffer ){ zAlloc = *pzBuffer; staticFlag = EP_Static; + assert( zAlloc!=0 ); }else{ zAlloc = wx_sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags)); staticFlag = 0; @@ -102072,7 +107352,7 @@ static Expr *exprDup(wx_sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ } /* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */ - pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken); + pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static); pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly); pNew->flags |= staticFlag; ExprClearVVAProperties(pNew); @@ -102088,7 +107368,7 @@ static Expr *exprDup(wx_sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_Leaf)) ){ /* Fill in the pNew->x.pSelect or pNew->x.pList member. */ - if( ExprHasProperty(p, EP_xIsSelect) ){ + if( ExprUseXSelect(p) ){ pNew->x.pSelect = wx_sqlite3SelectDup(db, p->x.pSelect, dupFlags); }else{ pNew->x.pList = wx_sqlite3ExprListDup(db, p->x.pList, dupFlags); @@ -102117,8 +107397,8 @@ static Expr *exprDup(wx_sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ if( pNew->op==TK_SELECT_COLUMN ){ pNew->pLeft = p->pLeft; - assert( p->iColumn==0 || p->pRight==0 ); - assert( p->pRight==0 || p->pRight==p->pLeft ); + assert( p->pRight==0 || p->pRight==p->pLeft + || ExprHasProperty(p->pLeft, EP_Subquery) ); }else{ pNew->pLeft = wx_sqlite3ExprDup(db, p->pLeft, 0); } @@ -102135,7 +107415,7 @@ static Expr *exprDup(wx_sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ ** and the db->mallocFailed flag set. */ #ifndef SQLITE_OMIT_CTE -static With *withDup(wx_sqlite3 *db, With *p){ +SQLITE_PRIVATE With *wx_sqlite3WithDup(wx_sqlite3 *db, With *p){ With *pRet = 0; if( p ){ wx_sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); @@ -102147,13 +107427,14 @@ static With *withDup(wx_sqlite3 *db, With *p){ pRet->a[i].pSelect = wx_sqlite3SelectDup(db, p->a[i].pSelect, 0); pRet->a[i].pCols = wx_sqlite3ExprListDup(db, p->a[i].pCols, 0); pRet->a[i].zName = wx_sqlite3DbStrDup(db, p->a[i].zName); + pRet->a[i].eM10d = p->a[i].eM10d; } } } return pRet; } #else -# define withDup(x,y) 0 +# define wx_sqlite3WithDup(x,y) 0 #endif #ifndef SQLITE_OMIT_WINDOWFUNC @@ -102206,20 +107487,23 @@ static void gatherSelectWindows(Select *p){ ** truncated version of the usual Expr structure that will be stored as ** part of the in-memory representation of the database schema. */ -SQLITE_PRIVATE Expr *wx_sqlite3ExprDup(wx_sqlite3 *db, Expr *p, int flags){ +SQLITE_PRIVATE Expr *wx_sqlite3ExprDup(wx_sqlite3 *db, const Expr *p, int flags){ assert( flags==0 || flags==EXPRDUP_REDUCE ); return p ? exprDup(db, p, flags, 0) : 0; } -SQLITE_PRIVATE ExprList *wx_sqlite3ExprListDup(wx_sqlite3 *db, ExprList *p, int flags){ +SQLITE_PRIVATE ExprList *wx_sqlite3ExprListDup(wx_sqlite3 *db, const ExprList *p, int flags){ ExprList *pNew; - struct ExprList_item *pItem, *pOldItem; + struct ExprList_item *pItem; + const struct ExprList_item *pOldItem; int i; - Expr *pPriorSelectCol = 0; + Expr *pPriorSelectColOld = 0; + Expr *pPriorSelectColNew = 0; assert( db!=0 ); if( p==0 ) return 0; pNew = wx_sqlite3DbMallocRawNN(db, wx_sqlite3DbMallocSize(db, p)); if( pNew==0 ) return 0; pNew->nExpr = p->nExpr; + pNew->nAlloc = p->nAlloc; pItem = pNew->a; pOldItem = p->a; for(i=0; inExpr; i++, pItem++, pOldItem++){ @@ -102230,24 +107514,22 @@ SQLITE_PRIVATE ExprList *wx_sqlite3ExprListDup(wx_sqlite3 *db, ExprList *p, int && pOldExpr->op==TK_SELECT_COLUMN && (pNewExpr = pItem->pExpr)!=0 ){ - assert( pNewExpr->iColumn==0 || i>0 ); - if( pNewExpr->iColumn==0 ){ - assert( pOldExpr->pLeft==pOldExpr->pRight ); - pPriorSelectCol = pNewExpr->pLeft = pNewExpr->pRight; + if( pNewExpr->pRight ){ + pPriorSelectColOld = pOldExpr->pRight; + pPriorSelectColNew = pNewExpr->pRight; + pNewExpr->pLeft = pNewExpr->pRight; }else{ - assert( i>0 ); - assert( pItem[-1].pExpr!=0 ); - assert( pNewExpr->iColumn==pItem[-1].pExpr->iColumn+1 ); - assert( pPriorSelectCol==pItem[-1].pExpr->pLeft ); - pNewExpr->pLeft = pPriorSelectCol; + if( pOldExpr->pLeft!=pPriorSelectColOld ){ + pPriorSelectColOld = pOldExpr->pLeft; + pPriorSelectColNew = wx_sqlite3ExprDup(db, pPriorSelectColOld, flags); + pNewExpr->pRight = pPriorSelectColNew; + } + pNewExpr->pLeft = pPriorSelectColNew; } } pItem->zEName = wx_sqlite3DbStrDup(db, pOldItem->zEName); - pItem->sortFlags = pOldItem->sortFlags; - pItem->eEName = pOldItem->eEName; - pItem->done = 0; - pItem->bNulls = pOldItem->bNulls; - pItem->bSorterRef = pOldItem->bSorterRef; + pItem->fg = pOldItem->fg; + pItem->fg.done = 0; pItem->u = pOldItem->u; } return pNew; @@ -102261,7 +107543,7 @@ SQLITE_PRIVATE ExprList *wx_sqlite3ExprListDup(wx_sqlite3 *db, ExprList *p, int */ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ || !defined(SQLITE_OMIT_SUBQUERY) -SQLITE_PRIVATE SrcList *wx_sqlite3SrcListDup(wx_sqlite3 *db, SrcList *p, int flags){ +SQLITE_PRIVATE SrcList *wx_sqlite3SrcListDup(wx_sqlite3 *db, const SrcList *p, int flags){ SrcList *pNew; int i; int nByte; @@ -102273,7 +107555,7 @@ SQLITE_PRIVATE SrcList *wx_sqlite3SrcListDup(wx_sqlite3 *db, SrcList *p, int fla pNew->nSrc = pNew->nAlloc = p->nSrc; for(i=0; inSrc; i++){ SrcItem *pNewItem = &pNew->a[i]; - SrcItem *pOldItem = &p->a[i]; + const SrcItem *pOldItem = &p->a[i]; Table *pTab; pNewItem->pSchema = pOldItem->pSchema; pNewItem->zDatabase = wx_sqlite3DbStrDup(db, pOldItem->zDatabase); @@ -102299,41 +107581,39 @@ SQLITE_PRIVATE SrcList *wx_sqlite3SrcListDup(wx_sqlite3 *db, SrcList *p, int fla pTab->nTabRef++; } pNewItem->pSelect = wx_sqlite3SelectDup(db, pOldItem->pSelect, flags); - pNewItem->pOn = wx_sqlite3ExprDup(db, pOldItem->pOn, flags); - pNewItem->pUsing = wx_sqlite3IdListDup(db, pOldItem->pUsing); + if( pOldItem->fg.isUsing ){ + assert( pNewItem->fg.isUsing ); + pNewItem->u3.pUsing = wx_sqlite3IdListDup(db, pOldItem->u3.pUsing); + }else{ + pNewItem->u3.pOn = wx_sqlite3ExprDup(db, pOldItem->u3.pOn, flags); + } pNewItem->colUsed = pOldItem->colUsed; } return pNew; } -SQLITE_PRIVATE IdList *wx_sqlite3IdListDup(wx_sqlite3 *db, IdList *p){ +SQLITE_PRIVATE IdList *wx_sqlite3IdListDup(wx_sqlite3 *db, const IdList *p){ IdList *pNew; int i; assert( db!=0 ); if( p==0 ) return 0; - pNew = wx_sqlite3DbMallocRawNN(db, sizeof(*pNew) ); + assert( p->eU4!=EU4_EXPR ); + pNew = wx_sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) ); if( pNew==0 ) return 0; pNew->nId = p->nId; - pNew->a = wx_sqlite3DbMallocRawNN(db, p->nId*sizeof(p->a[0]) ); - if( pNew->a==0 ){ - wx_sqlite3DbFreeNN(db, pNew); - return 0; - } - /* Note that because the size of the allocation for p->a[] is not - ** necessarily a power of two, wx_sqlite3IdListAppend() may not be called - ** on the duplicate created by this function. */ + pNew->eU4 = p->eU4; for(i=0; inId; i++){ struct IdList_item *pNewItem = &pNew->a[i]; - struct IdList_item *pOldItem = &p->a[i]; + const struct IdList_item *pOldItem = &p->a[i]; pNewItem->zName = wx_sqlite3DbStrDup(db, pOldItem->zName); - pNewItem->idx = pOldItem->idx; + pNewItem->u4 = pOldItem->u4; } return pNew; } -SQLITE_PRIVATE Select *wx_sqlite3SelectDup(wx_sqlite3 *db, Select *pDup, int flags){ +SQLITE_PRIVATE Select *wx_sqlite3SelectDup(wx_sqlite3 *db, const Select *pDup, int flags){ Select *pRet = 0; Select *pNext = 0; Select **pp = &pRet; - Select *p; + const Select *p; assert( db!=0 ); for(p=pDup; p; p=p->pPrior){ @@ -102355,13 +107635,21 @@ SQLITE_PRIVATE Select *wx_sqlite3SelectDup(wx_sqlite3 *db, Select *pDup, int fla pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; pNew->nSelectRow = p->nSelectRow; - pNew->pWith = withDup(db, p->pWith); + pNew->pWith = wx_sqlite3WithDup(db, p->pWith); #ifndef SQLITE_OMIT_WINDOWFUNC pNew->pWin = 0; pNew->pWinDefn = wx_sqlite3WindowListDup(db, p->pWinDefn); if( p->pWin && db->mallocFailed==0 ) gatherSelectWindows(pNew); #endif pNew->selId = p->selId; + if( db->mallocFailed ){ + /* Any prior OOM might have left the Select object incomplete. + ** Delete the whole thing rather than allow an incomplete Select + ** to be used by the code generator. */ + pNew->pNext = 0; + wx_sqlite3SelectDelete(db, pNew); + break; + } *pp = pNew; pp = &pNew->pPrior; pNext = pNew; @@ -102370,7 +107658,7 @@ SQLITE_PRIVATE Select *wx_sqlite3SelectDup(wx_sqlite3 *db, Select *pDup, int fla return pRet; } #else -SQLITE_PRIVATE Select *wx_sqlite3SelectDup(wx_sqlite3 *db, Select *p, int flags){ +SQLITE_PRIVATE Select *wx_sqlite3SelectDup(wx_sqlite3 *db, const Select *p, int flags){ assert( p==0 ); return 0; } @@ -102392,41 +107680,64 @@ SQLITE_PRIVATE Select *wx_sqlite3SelectDup(wx_sqlite3 *db, Select *p, int flags) ** NULL is returned. If non-NULL is returned, then it is guaranteed ** that the new entry was successfully appended. */ +static const struct ExprList_item zeroItem = {0}; +SQLITE_PRIVATE SQLITE_NOINLINE ExprList *wx_sqlite3ExprListAppendNew( + wx_sqlite3 *db, /* Database handle. Used for memory allocation */ + Expr *pExpr /* Expression to be appended. Might be NULL */ +){ + struct ExprList_item *pItem; + ExprList *pList; + + pList = wx_sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 ); + if( pList==0 ){ + wx_sqlite3ExprDelete(db, pExpr); + return 0; + } + pList->nAlloc = 4; + pList->nExpr = 1; + pItem = &pList->a[0]; + *pItem = zeroItem; + pItem->pExpr = pExpr; + return pList; +} +SQLITE_PRIVATE SQLITE_NOINLINE ExprList *wx_sqlite3ExprListAppendGrow( + wx_sqlite3 *db, /* Database handle. Used for memory allocation */ + ExprList *pList, /* List to which to append. Might be NULL */ + Expr *pExpr /* Expression to be appended. Might be NULL */ +){ + struct ExprList_item *pItem; + ExprList *pNew; + pList->nAlloc *= 2; + pNew = wx_sqlite3DbRealloc(db, pList, + sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0])); + if( pNew==0 ){ + wx_sqlite3ExprListDelete(db, pList); + wx_sqlite3ExprDelete(db, pExpr); + return 0; + }else{ + pList = pNew; + } + pItem = &pList->a[pList->nExpr++]; + *pItem = zeroItem; + pItem->pExpr = pExpr; + return pList; +} SQLITE_PRIVATE ExprList *wx_sqlite3ExprListAppend( Parse *pParse, /* Parsing context */ ExprList *pList, /* List to which to append. Might be NULL */ Expr *pExpr /* Expression to be appended. Might be NULL */ ){ struct ExprList_item *pItem; - wx_sqlite3 *db = pParse->db; - assert( db!=0 ); if( pList==0 ){ - pList = wx_sqlite3DbMallocRawNN(db, sizeof(ExprList) ); - if( pList==0 ){ - goto no_mem; - } - pList->nExpr = 0; - }else if( (pList->nExpr & (pList->nExpr-1))==0 ){ - ExprList *pNew; - pNew = wx_sqlite3DbRealloc(db, pList, - sizeof(*pList)+(2*(wx_sqlite3_int64)pList->nExpr-1)*sizeof(pList->a[0])); - if( pNew==0 ){ - goto no_mem; - } - pList = pNew; + return wx_sqlite3ExprListAppendNew(pParse->db,pExpr); + } + if( pList->nAllocnExpr+1 ){ + return wx_sqlite3ExprListAppendGrow(pParse->db,pList,pExpr); } pItem = &pList->a[pList->nExpr++]; - assert( offsetof(struct ExprList_item,zEName)==sizeof(pItem->pExpr) ); - assert( offsetof(struct ExprList_item,pExpr)==0 ); - memset(&pItem->zEName,0,sizeof(*pItem)-offsetof(struct ExprList_item,zEName)); + *pItem = zeroItem; pItem->pExpr = pExpr; return pList; - -no_mem: - /* Avoid leaking memory if malloc has failed. */ - wx_sqlite3ExprDelete(db, pExpr); - wx_sqlite3ExprListDelete(db, pList); - return 0; } /* @@ -102467,11 +107778,9 @@ SQLITE_PRIVATE ExprList *wx_sqlite3ExprListAppendVector( } for(i=0; inId; i++){ - Expr *pSubExpr = wx_sqlite3ExprForVectorField(pParse, pExpr, i); + Expr *pSubExpr = wx_sqlite3ExprForVectorField(pParse, pExpr, i, pColumns->nId); assert( pSubExpr!=0 || db->mallocFailed ); - assert( pSubExpr==0 || pSubExpr->iTable==0 ); if( pSubExpr==0 ) continue; - pSubExpr->iTable = pColumns->nId; pList = wx_sqlite3ExprListAppend(pParse, pList, pSubExpr); if( pList ){ assert( pList->nExpr==iFirst+i+1 ); @@ -102520,16 +107829,16 @@ SQLITE_PRIVATE void wx_sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder, ); pItem = &p->a[p->nExpr-1]; - assert( pItem->bNulls==0 ); + assert( pItem->fg.bNulls==0 ); if( iSortOrder==SQLITE_SO_UNDEFINED ){ iSortOrder = SQLITE_SO_ASC; } - pItem->sortFlags = (u8)iSortOrder; + pItem->fg.sortFlags = (u8)iSortOrder; if( eNulls!=SQLITE_SO_UNDEFINED ){ - pItem->bNulls = 1; + pItem->fg.bNulls = 1; if( iSortOrder!=eNulls ){ - pItem->sortFlags |= KEYINFO_ORDER_BIGNULL; + pItem->fg.sortFlags |= KEYINFO_ORDER_BIGNULL; } } } @@ -102545,7 +107854,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder, SQLITE_PRIVATE void wx_sqlite3ExprListSetName( Parse *pParse, /* Parsing context */ ExprList *pList, /* List to which to add the span. */ - Token *pName, /* Name to be added */ + const Token *pName, /* Name to be added */ int dequote /* True to cause the name to be dequoted */ ){ assert( pList!=0 || pParse->db->mallocFailed!=0 ); @@ -102555,7 +107864,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprListSetName( assert( pList->nExpr>0 ); pItem = &pList->a[pList->nExpr-1]; assert( pItem->zEName==0 ); - assert( pItem->eEName==ENAME_NAME ); + assert( pItem->fg.eEName==ENAME_NAME ); pItem->zEName = wx_sqlite3DbStrNDup(pParse->db, pName->z, pName->n); if( dequote ){ /* If dequote==0, then pName->z does not point to part of a DDL @@ -102563,7 +107872,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprListSetName( ** to the token-map. */ wx_sqlite3Dequote(pItem->zEName); if( IN_RENAME_OBJECT ){ - wx_sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName); + wx_sqlite3RenameTokenMap(pParse, (const void*)pItem->zEName, pName); } } } @@ -102590,7 +107899,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprListSetSpan( assert( pList->nExpr>0 ); if( pItem->zEName==0 ){ pItem->zEName = wx_sqlite3DbSpanDup(db, zStart, zEnd); - pItem->eEName = ENAME_SPAN; + pItem->fg.eEName = ENAME_SPAN; } } } @@ -102619,12 +107928,13 @@ static SQLITE_NOINLINE void exprListDeleteNN(wx_sqlite3 *db, ExprList *pList){ int i = pList->nExpr; struct ExprList_item *pItem = pList->a; assert( pList->nExpr>0 ); + assert( db!=0 ); do{ wx_sqlite3ExprDelete(db, pItem->pExpr); - wx_sqlite3DbFree(db, pItem->zEName); + if( pItem->zEName ) wx_sqlite3DbNNFreeNN(db, pItem->zEName); pItem++; }while( --i>0 ); - wx_sqlite3DbFreeNN(db, pList); + wx_sqlite3DbNNFreeNN(db, pList); } SQLITE_PRIVATE void wx_sqlite3ExprListDelete(wx_sqlite3 *db, ExprList *pList){ if( pList ) exprListDeleteNN(db, pList); @@ -102682,7 +107992,7 @@ SQLITE_PRIVATE u32 wx_sqlite3IsTrueOrFalse(const char *zIn){ SQLITE_PRIVATE int wx_sqlite3ExprIdToTrueFalse(Expr *pExpr){ u32 v; assert( pExpr->op==TK_ID || pExpr->op==TK_STRING ); - if( !ExprHasProperty(pExpr, EP_Quoted) + if( !ExprHasProperty(pExpr, EP_Quoted|EP_IntValue) && (v = wx_sqlite3IsTrueOrFalse(pExpr->u.zToken))!=0 ){ pExpr->op = TK_TRUEFALSE; @@ -102699,6 +108009,7 @@ SQLITE_PRIVATE int wx_sqlite3ExprIdToTrueFalse(Expr *pExpr){ SQLITE_PRIVATE int wx_sqlite3ExprTruthValue(const Expr *pExpr){ pExpr = wx_sqlite3ExprSkipCollate((Expr*)pExpr); assert( pExpr->op==TK_TRUEFALSE ); + assert( !ExprHasProperty(pExpr, EP_IntValue) ); assert( wx_sqlite3StrICmp(pExpr->u.zToken,"true")==0 || wx_sqlite3StrICmp(pExpr->u.zToken,"false")==0 ); return pExpr->u.zToken[4]==0; @@ -102761,9 +108072,9 @@ SQLITE_PRIVATE Expr *wx_sqlite3ExprSimplifiedAndOr(Expr *pExpr){ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ /* If pWalker->eCode is 2 then any term of the expression that comes from - ** the ON or USING clauses of a left join disqualifies the expression + ** the ON or USING clauses of an outer join disqualifies the expression ** from being considered constant. */ - if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_FromJoin) ){ + if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_OuterON) ){ pWalker->eCode = 0; return WRC_Abort; } @@ -102882,6 +108193,42 @@ SQLITE_PRIVATE int wx_sqlite3ExprIsTableConstant(Expr *p, int iCur){ return exprIsConst(p, 3, iCur); } +/* +** Check pExpr to see if it is an invariant constraint on data source pSrc. +** This is an optimization. False negatives will perhaps cause slower +** queries, but false positives will yield incorrect answers. So when in +** doubt, return 0. +** +** To be an invariant constraint, the following must be true: +** +** (1) pExpr cannot refer to any table other than pSrc->iCursor. +** +** (2) pExpr cannot use subqueries or non-deterministic functions. +** +** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. +** (Is there some way to relax this constraint?) +** +** (4) If pSrc is the right operand of a LEFT JOIN, then... +** (4a) pExpr must come from an ON clause.. + (4b) and specifically the ON clause associated with the LEFT JOIN. +** +** (5) If pSrc is not the right operand of a LEFT JOIN or the left +** operand of a RIGHT JOIN, then pExpr must be from the WHERE +** clause, not an ON clause. +*/ +SQLITE_PRIVATE int wx_sqlite3ExprIsTableConstraint(Expr *pExpr, const SrcItem *pSrc){ + if( pSrc->fg.jointype & JT_LTORJ ){ + return 0; /* rule (3) */ + } + if( pSrc->fg.jointype & JT_LEFT ){ + if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */ + if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */ + }else{ + if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */ + } + return wx_sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */ +} + /* ** wx_sqlite3WalkExpr() callback used by wx_sqlite3ExprIsConstantOrGroupBy(). @@ -102903,7 +108250,7 @@ static int exprNodeIsConstantOrGroupBy(Walker *pWalker, Expr *pExpr){ } /* Check if pExpr is a sub-select. If so, consider it variable. */ - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ pWalker->eCode = 0; return WRC_Abort; } @@ -102991,7 +108338,7 @@ SQLITE_PRIVATE int wx_sqlite3ExprContainsSubquery(Expr *p){ ** in *pValue. If the expression is not an integer or if it is too big ** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. */ -SQLITE_PRIVATE int wx_sqlite3ExprIsInteger(Expr *p, int *pValue){ +SQLITE_PRIVATE int wx_sqlite3ExprIsInteger(const Expr *p, int *pValue){ int rc = 0; if( NEVER(p==0) ) return 0; /* Used to only happen following on OOM */ @@ -103010,9 +108357,9 @@ SQLITE_PRIVATE int wx_sqlite3ExprIsInteger(Expr *p, int *pValue){ break; } case TK_UMINUS: { - int v; + int v = 0; if( wx_sqlite3ExprIsInteger(p->pLeft, &v) ){ - assert( v!=(-2147483647-1) ); + assert( ((unsigned int)v)!=0x80000000 ); *pValue = -v; rc = 1; } @@ -103039,8 +108386,10 @@ SQLITE_PRIVATE int wx_sqlite3ExprIsInteger(Expr *p, int *pValue){ */ SQLITE_PRIVATE int wx_sqlite3ExprCanBeNull(const Expr *p){ u8 op; + assert( p!=0 ); while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; + assert( p!=0 ); } op = p->op; if( op==TK_REGISTER ) op = p->op2; @@ -103051,10 +108400,11 @@ SQLITE_PRIVATE int wx_sqlite3ExprCanBeNull(const Expr *p){ case TK_BLOB: return 0; case TK_COLUMN: + assert( ExprUseYTab(p) ); return ExprHasProperty(p, EP_CanBeNull) || p->y.pTab==0 || /* Reference to column of index on expression */ (p->iColumn>=0 - && ALWAYS(p->y.pTab->aCol!=0) /* Defense against OOM problems */ + && p->y.pTab->aCol!=0 /* Possible due to prior error */ && p->y.pTab->aCol[p->iColumn].notNull==0); default: return 1; @@ -103122,13 +108472,13 @@ SQLITE_PRIVATE int wx_sqlite3IsRowid(const char *z){ ** table, then return NULL. */ #ifndef SQLITE_OMIT_SUBQUERY -static Select *isCandidateForInOpt(Expr *pX){ +static Select *isCandidateForInOpt(const Expr *pX){ Select *p; SrcList *pSrc; ExprList *pEList; Table *pTab; int i; - if( !ExprHasProperty(pX, EP_xIsSelect) ) return 0; /* Not a subquery */ + if( !ExprUseXSelect(pX) ) return 0; /* Not a subquery */ if( ExprHasProperty(pX, EP_VarSelect) ) return 0; /* Correlated subq */ p = pX->x.pSelect; if( p->pPrior ) return 0; /* Not a compound SELECT */ @@ -103146,7 +108496,7 @@ static Select *isCandidateForInOpt(Expr *pX){ if( pSrc->a[0].pSelect ) return 0; /* FROM is not a subquery or view */ pTab = pSrc->a[0].pTab; assert( pTab!=0 ); - assert( pTab->pSelect==0 ); /* FROM clause is not a view */ + assert( !IsView(pTab) ); /* FROM clause is not a view */ if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */ pEList = p->pEList; assert( pEList!=0 ); @@ -103206,7 +108556,7 @@ static int wx_sqlite3InRhsIsConstant(Expr *pIn){ ** all members of the RHS set, skipping duplicates. ** ** A cursor is opened on the b-tree object that is the RHS of the IN operator -** and pX->iTable is set to the index of that cursor. +** and the *piTab parameter is set to the index of that cursor. ** ** The returned value of this function indicates the b-tree type, as follows: ** @@ -103226,7 +108576,10 @@ static int wx_sqlite3InRhsIsConstant(Expr *pIn){ ** If the RHS of the IN operator is a list or a more complex subquery, then ** an ephemeral table might need to be generated from the RHS and then ** pX->iTable made to point to the ephemeral table instead of an -** existing table. +** existing table. In this case, the creation and initialization of the +** ephmeral table might be put inside of a subroutine, the EP_Subrtn flag +** will be set on pX and the pX->y.sub fields will be set to show where +** the subroutine is coded. ** ** The inFlags parameter must contain, at a minimum, one of the bits ** IN_INDEX_MEMBERSHIP or IN_INDEX_LOOP but not both. If inFlags contains @@ -103287,19 +108640,20 @@ SQLITE_PRIVATE int wx_sqlite3FindInIndex( ){ Select *p; /* SELECT to the right of IN operator */ int eType = 0; /* Type of RHS table. IN_INDEX_* */ - int iTab = pParse->nTab++; /* Cursor of the RHS table */ + int iTab; /* Cursor of the RHS table */ int mustBeUnique; /* True if RHS must be unique */ Vdbe *v = wx_sqlite3GetVdbe(pParse); /* Virtual machine being coded */ assert( pX->op==TK_IN ); mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0; + iTab = pParse->nTab++; /* If the RHS of this IN(...) operator is a SELECT, and if it matters ** whether or not the SELECT result contains NULL values, check whether ** or not NULL is actually possible (it may not be, for example, due ** to NOT NULL constraints in the schema). If no NULL values are possible, ** set prRhsHasNull to 0 before continuing. */ - if( prRhsHasNull && (pX->flags & EP_xIsSelect) ){ + if( prRhsHasNull && ExprUseXSelect(pX) ){ int i; ExprList *pEList = pX->x.pSelect->pEList; for(i=0; inExpr; i++){ @@ -103455,9 +108809,11 @@ SQLITE_PRIVATE int wx_sqlite3FindInIndex( */ if( eType==0 && (inFlags & IN_INDEX_NOOP_OK) - && !ExprHasProperty(pX, EP_xIsSelect) + && ExprUseXList(pX) && (!wx_sqlite3InRhsIsConstant(pX) || pX->x.pList->nExpr<=2) ){ + pParse->nTab--; /* Back out the allocation of the unused cursor */ + iTab = -1; /* Cursor is not allocated */ eType = IN_INDEX_NOOP; } @@ -103500,10 +108856,10 @@ SQLITE_PRIVATE int wx_sqlite3FindInIndex( ** It is the responsibility of the caller to ensure that the returned ** string is eventually freed using wx_sqlite3DbFree(). */ -static char *exprINAffinity(Parse *pParse, Expr *pExpr){ +static char *exprINAffinity(Parse *pParse, const Expr *pExpr){ Expr *pLeft = pExpr->pLeft; int nVal = wx_sqlite3ExprVectorSize(pLeft); - Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0; + Select *pSelect = ExprUseXSelect(pExpr) ? pExpr->x.pSelect : 0; char *zRet; assert( pExpr->op==TK_IN ); @@ -103553,7 +108909,7 @@ SQLITE_PRIVATE void wx_sqlite3SubselectError(Parse *pParse, int nActual, int nEx */ SQLITE_PRIVATE void wx_sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ #ifndef SQLITE_OMIT_SUBQUERY - if( pExpr->flags & EP_xIsSelect ){ + if( ExprUseXSelect(pExpr) ){ wx_sqlite3SubselectError(pParse, pExpr->x.pSelect->pEList->nExpr, 1); }else #endif @@ -103617,24 +108973,26 @@ SQLITE_PRIVATE void wx_sqlite3CodeRhsOfIN( */ if( ExprHasProperty(pExpr, EP_Subrtn) ){ addrOnce = wx_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ ExplainQueryPlan((pParse, 0, "REUSE LIST SUBQUERY %d", pExpr->x.pSelect->selId)); } + assert( ExprUseYSub(pExpr) ); wx_sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, pExpr->y.sub.iAddr); + assert( iTab!=pExpr->iTable ); wx_sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable); wx_sqlite3VdbeJumpHere(v, addrOnce); return; } /* Begin coding the subroutine */ + assert( !ExprUseYWin(pExpr) ); ExprSetProperty(pExpr, EP_Subrtn); assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); pExpr->y.sub.regReturn = ++pParse->nMem; pExpr->y.sub.iAddr = - wx_sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; - VdbeComment((v, "return address")); + wx_sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1; addrOnce = wx_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); } @@ -103649,7 +109007,7 @@ SQLITE_PRIVATE void wx_sqlite3CodeRhsOfIN( pExpr->iTable = iTab; addr = wx_sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, nVal); #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ VdbeComment((v, "Result of SELECT %u", pExpr->x.pSelect->selId)); }else{ VdbeComment((v, "RHS of IN operator")); @@ -103657,7 +109015,7 @@ SQLITE_PRIVATE void wx_sqlite3CodeRhsOfIN( #endif pKeyInfo = wx_sqlite3KeyInfoAlloc(pParse->db, nVal, 1); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ /* Case 1: expr IN (SELECT ...) ** ** Generate code to write the results of the select into the temporary @@ -103672,19 +109030,23 @@ SQLITE_PRIVATE void wx_sqlite3CodeRhsOfIN( /* If the LHS and RHS of the IN operator do not match, that ** error will have been caught long before we reach this point. */ if( ALWAYS(pEList->nExpr==nVal) ){ + Select *pCopy; SelectDest dest; int i; + int rc; wx_sqlite3SelectDestInit(&dest, SRT_Set, iTab); dest.zAffSdst = exprINAffinity(pParse, pExpr); pSelect->iLimit = 0; testcase( pSelect->selFlags & SF_Distinct ); testcase( pKeyInfo==0 ); /* Caused by OOM in wx_sqlite3KeyInfoAlloc() */ - if( wx_sqlite3Select(pParse, pSelect, &dest) ){ - wx_sqlite3DbFree(pParse->db, dest.zAffSdst); + pCopy = wx_sqlite3SelectDup(pParse->db, pSelect, 0); + rc = pParse->db->mallocFailed ? 1 :wx_sqlite3Select(pParse, pCopy, &dest); + wx_sqlite3SelectDelete(pParse->db, pCopy); + wx_sqlite3DbFree(pParse->db, dest.zAffSdst); + if( rc ){ wx_sqlite3KeyInfoUnref(pKeyInfo); return; } - wx_sqlite3DbFree(pParse->db, dest.zAffSdst); assert( pKeyInfo!=0 ); /* OOM will cause exit after wx_sqlite3Select() */ assert( pEList!=0 ); assert( pEList->nExpr>0 ); @@ -103732,6 +109094,7 @@ SQLITE_PRIVATE void wx_sqlite3CodeRhsOfIN( ** expression we need to rerun this code each time. */ if( addrOnce && !wx_sqlite3ExprIsConstant(pE2) ){ + wx_sqlite3VdbeChangeToNoop(v, addrOnce-1); wx_sqlite3VdbeChangeToNoop(v, addrOnce); ExprClearProperty(pExpr, EP_Subrtn); addrOnce = 0; @@ -103749,10 +109112,15 @@ SQLITE_PRIVATE void wx_sqlite3CodeRhsOfIN( wx_sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); } if( addrOnce ){ + wx_sqlite3VdbeAddOp1(v, OP_NullRow, iTab); wx_sqlite3VdbeJumpHere(v, addrOnce); /* Subroutine return */ - wx_sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); - wx_sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, wx_sqlite3VdbeCurrentAddr(v)-1); + assert( ExprUseYSub(pExpr) ); + assert( wx_sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn + || pParse->nErr ); + wx_sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn, + pExpr->y.sub.iAddr, 1); + VdbeCoverage(v); wx_sqlite3ClearTempRegCache(pParse); } } @@ -103780,15 +109148,37 @@ SQLITE_PRIVATE int wx_sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ SelectDest dest; /* How to deal with SELECT result */ int nReg; /* Registers to allocate */ Expr *pLimit; /* New limit expression */ +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + int addrExplain; /* Address of OP_Explain instruction */ +#endif Vdbe *v = pParse->pVdbe; assert( v!=0 ); + if( pParse->nErr ) return 0; testcase( pExpr->op==TK_EXISTS ); testcase( pExpr->op==TK_SELECT ); assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); - assert( ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( ExprUseXSelect(pExpr) ); pSel = pExpr->x.pSelect; + /* If this routine has already been coded, then invoke it as a + ** subroutine. */ + if( ExprHasProperty(pExpr, EP_Subrtn) ){ + ExplainQueryPlan((pParse, 0, "REUSE SUBQUERY %d", pSel->selId)); + assert( ExprUseYSub(pExpr) ); + wx_sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, + pExpr->y.sub.iAddr); + return pExpr->iTable; + } + + /* Begin coding the subroutine */ + assert( !ExprUseYWin(pExpr) ); + assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) ); + ExprSetProperty(pExpr, EP_Subrtn); + pExpr->y.sub.regReturn = ++pParse->nMem; + pExpr->y.sub.iAddr = + wx_sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1; + /* The evaluation of the EXISTS/SELECT must be repeated every time it ** is encountered if any of the following is true: ** @@ -103800,22 +109190,6 @@ SQLITE_PRIVATE int wx_sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ ** save the results, and reuse the same result on subsequent invocations. */ if( !ExprHasProperty(pExpr, EP_VarSelect) ){ - /* If this routine has already been coded, then invoke it as a - ** subroutine. */ - if( ExprHasProperty(pExpr, EP_Subrtn) ){ - ExplainQueryPlan((pParse, 0, "REUSE SUBQUERY %d", pSel->selId)); - wx_sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, - pExpr->y.sub.iAddr); - return pExpr->iTable; - } - - /* Begin coding the subroutine */ - ExprSetProperty(pExpr, EP_Subrtn); - pExpr->y.sub.regReturn = ++pParse->nMem; - pExpr->y.sub.iAddr = - wx_sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; - VdbeComment((v, "return address")); - addrOnce = wx_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); } @@ -103829,8 +109203,9 @@ SQLITE_PRIVATE int wx_sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ ** In both cases, the query is augmented with "LIMIT 1". Any ** preexisting limit is discarded in place of the new LIMIT 1. */ - ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY %d", + ExplainQueryPlan2(addrExplain, (pParse, 1, "%sSCALAR SUBQUERY %d", addrOnce?"":"CORRELATED ", pSel->selId)); + wx_sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, -1); nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1; wx_sqlite3SelectDestInit(&dest, 0, pParse->nMem+1); pParse->nMem += nReg; @@ -103855,7 +109230,7 @@ SQLITE_PRIVATE int wx_sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ pLimit = wx_sqlite3PExpr(pParse, TK_NE, wx_sqlite3ExprDup(db, pSel->pLimit->pLeft, 0), pLimit); } - wx_sqlite3ExprDelete(db, pSel->pLimit->pLeft); + wx_sqlite3ExprDeferredDelete(pParse, pSel->pLimit->pLeft); pSel->pLimit->pLeft = pLimit; }else{ /* If there is no pre-existing limit add a limit of 1 */ @@ -103864,19 +109239,25 @@ SQLITE_PRIVATE int wx_sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ } pSel->iLimit = 0; if( wx_sqlite3Select(pParse, pSel, &dest) ){ + pExpr->op2 = pExpr->op; + pExpr->op = TK_ERROR; return 0; } pExpr->iTable = rReg = dest.iSDParm; ExprSetVVAProperty(pExpr, EP_NoReduce); if( addrOnce ){ wx_sqlite3VdbeJumpHere(v, addrOnce); - - /* Subroutine return */ - wx_sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); - wx_sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, wx_sqlite3VdbeCurrentAddr(v)-1); - wx_sqlite3ClearTempRegCache(pParse); } + wx_sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); + /* Subroutine return */ + assert( ExprUseYSub(pExpr) ); + assert( wx_sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn + || pParse->nErr ); + wx_sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn, + pExpr->y.sub.iAddr, 1); + VdbeCoverage(v); + wx_sqlite3ClearTempRegCache(pParse); return rReg; } #endif /* SQLITE_OMIT_SUBQUERY */ @@ -103890,7 +109271,7 @@ SQLITE_PRIVATE int wx_sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ */ SQLITE_PRIVATE int wx_sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){ int nVector = wx_sqlite3ExprVectorSize(pIn->pLeft); - if( (pIn->flags & EP_xIsSelect) ){ + if( ExprUseXSelect(pIn) && !pParse->db->mallocFailed ){ if( nVector!=pIn->x.pSelect->pEList->nExpr ){ wx_sqlite3SubselectError(pParse, pIn->x.pSelect->pEList->nExpr, nVector); return 1; @@ -104024,13 +109405,15 @@ static void wx_sqlite3ExprCodeIN( ** This is step (1) in the in-operator.md optimized algorithm. */ if( eType==IN_INDEX_NOOP ){ - ExprList *pList = pExpr->x.pList; - CollSeq *pColl = wx_sqlite3ExprCollSeq(pParse, pExpr->pLeft); + ExprList *pList; + CollSeq *pColl; int labelOk = wx_sqlite3VdbeMakeLabel(pParse); int r2, regToFree; int regCkNull = 0; int ii; - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( ExprUseXList(pExpr) ); + pList = pExpr->x.pList; + pColl = wx_sqlite3ExprCollSeq(pParse, pExpr->pLeft); if( destIfNull!=destIfFalse ){ regCkNull = wx_sqlite3GetTempReg(pParse); wx_sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull); @@ -104078,9 +109461,9 @@ static void wx_sqlite3ExprCodeIN( }else{ destStep2 = destStep6 = wx_sqlite3VdbeMakeLabel(pParse); } - if( pParse->nErr ) goto wx_sqlite3ExprCodeIN_finished; for(i=0; ipLeft, i); + if( pParse->nErr ) goto wx_sqlite3ExprCodeIN_oom_error; if( wx_sqlite3ExprCanBeNull(p) ){ wx_sqlite3VdbeAddOp2(v, OP_IsNull, rLhs+i, destStep2); VdbeCoverage(v); @@ -104218,11 +109601,12 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ c = wx_sqlite3DecOrHexToI64(z, &value); if( (c==3 && !negFlag) || (c==2) || (negFlag && value==SMALLEST_INT64)){ #ifdef SQLITE_OMIT_FLOATING_POINT - wx_sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); + wx_sqlite3ErrorMsg(pParse, "oversized integer: %s%#T", negFlag?"-":"",pExpr); #else #ifndef SQLITE_OMIT_HEX_INTEGER if( wx_sqlite3_strnicmp(z,"0x",2)==0 ){ - wx_sqlite3ErrorMsg(pParse, "hex literal too big: %s%s", negFlag?"-":"",z); + wx_sqlite3ErrorMsg(pParse, "hex literal too big: %s%#T", + negFlag?"-":"",pExpr); }else #endif { @@ -104266,12 +109650,14 @@ SQLITE_PRIVATE void wx_sqlite3ExprCodeLoadIndexColumn( ** and store the result in register regOut */ SQLITE_PRIVATE void wx_sqlite3ExprCodeGeneratedColumn( - Parse *pParse, - Column *pCol, - int regOut + Parse *pParse, /* Parsing context */ + Table *pTab, /* Table containing the generated column */ + Column *pCol, /* The generated column */ + int regOut /* Put the result in this register */ ){ int iAddr; Vdbe *v = pParse->pVdbe; + int nErr = pParse->nErr; assert( v!=0 ); assert( pParse->iSelfTab!=0 ); if( pParse->iSelfTab>0 ){ @@ -104279,11 +109665,12 @@ SQLITE_PRIVATE void wx_sqlite3ExprCodeGeneratedColumn( }else{ iAddr = 0; } - wx_sqlite3ExprCodeCopy(pParse, pCol->pDflt, regOut); + wx_sqlite3ExprCodeCopy(pParse, wx_sqlite3ColumnExpr(pTab,pCol), regOut); if( pCol->affinity>=SQLITE_AFF_TEXT ){ wx_sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1); } if( iAddr ) wx_sqlite3VdbeJumpHere(v, iAddr); + if( pParse->nErr>nErr ) pParse->db->errByteOffset = -1; } #endif /* SQLITE_OMIT_GENERATED_COLUMNS */ @@ -104299,12 +109686,11 @@ SQLITE_PRIVATE void wx_sqlite3ExprCodeGetColumnOfTable( ){ Column *pCol; assert( v!=0 ); - if( pTab==0 ){ - wx_sqlite3VdbeAddOp3(v, OP_Column, iTabCur, iCol, regOut); - return; - } + assert( pTab!=0 ); + assert( iCol!=XN_EXPR ); if( iCol<0 || iCol==pTab->iPKey ){ wx_sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut); + VdbeComment((v, "%s.rowid", pTab->zName)); }else{ int op; int x; @@ -104315,12 +109701,13 @@ SQLITE_PRIVATE void wx_sqlite3ExprCodeGetColumnOfTable( }else if( (pCol = &pTab->aCol[iCol])->colFlags & COLFLAG_VIRTUAL ){ Parse *pParse = wx_sqlite3VdbeParser(v); if( pCol->colFlags & COLFLAG_BUSY ){ - wx_sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pCol->zName); + wx_sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", + pCol->zCnName); }else{ int savedSelfTab = pParse->iSelfTab; pCol->colFlags |= COLFLAG_BUSY; pParse->iSelfTab = iTabCur+1; - wx_sqlite3ExprCodeGeneratedColumn(pParse, pCol, regOut); + wx_sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, regOut); pParse->iSelfTab = savedSelfTab; pCol->colFlags &= ~COLFLAG_BUSY; } @@ -104358,7 +109745,7 @@ SQLITE_PRIVATE int wx_sqlite3ExprCodeGetColumn( assert( pParse->pVdbe!=0 ); wx_sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pTab, iTable, iColumn, iReg); if( p5 ){ - VdbeOp *pOp = wx_sqlite3VdbeGetOp(pParse->pVdbe,-1); + VdbeOp *pOp = wx_sqlite3VdbeGetLastOp(pParse->pVdbe); if( pOp->opcode==OP_Column ) pOp->p5 = p5; } return iReg; @@ -104413,6 +109800,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ int i; iResult = pParse->nMem+1; pParse->nMem += nResult; + assert( ExprUseXList(p) ); for(i=0; ix.pList->a[i].pExpr, i+iResult); } @@ -104426,7 +109814,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ ** so that a subsequent copy will not be merged into this one. */ static void setDoNotMergeFlagOnCopy(Vdbe *v){ - if( wx_sqlite3VdbeGetOp(v, -1)->opcode==OP_Copy ){ + if( wx_sqlite3VdbeGetLastOp(v)->opcode==OP_Copy ){ wx_sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergable */ } } @@ -104473,7 +109861,17 @@ static int exprCodeInlineFunction( caseExpr.x.pList = pFarg; return wx_sqlite3ExprCodeTarget(pParse, &caseExpr, target); } - +#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC + case INLINEFUNC_sqlite_offset: { + Expr *pArg = pFarg->a[0].pExpr; + if( pArg->op==TK_COLUMN && pArg->iTable>=0 ){ + wx_sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target); + }else{ + wx_sqlite3VdbeAddOp2(v, OP_Null, 0, target); + } + break; + } +#endif default: { /* The UNLIKELY() function is a no-op. The result is the value ** of the first argument. @@ -104487,6 +109885,7 @@ static int exprCodeInlineFunction( ** Test-only SQL functions that are only usable if enabled ** via SQLITE_TESTCTRL_INTERNAL_FUNCTIONS */ +#if !defined(SQLITE_UNTESTABLE) case INLINEFUNC_expr_compare: { /* Compare two expressions using wx_sqlite3ExprCompare() */ assert( nFarg==2 ); @@ -104520,25 +109919,85 @@ static int exprCodeInlineFunction( break; } -#ifdef SQLITE_DEBUG case INLINEFUNC_affinity: { /* The AFFINITY() function evaluates to a string that describes ** the type affinity of the argument. This is used for testing of ** the SQLite type logic. */ - const char *azAff[] = { "blob", "text", "numeric", "integer", "real" }; + const char *azAff[] = { "blob", "text", "numeric", "integer", + "real", "flexnum" }; char aff; assert( nFarg==1 ); aff = wx_sqlite3ExprAffinity(pFarg->a[0].pExpr); + assert( aff<=SQLITE_AFF_NONE + || (aff>=SQLITE_AFF_BLOB && aff<=SQLITE_AFF_FLEXNUM) ); wx_sqlite3VdbeLoadString(v, target, (aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]); break; } -#endif +#endif /* !defined(SQLITE_UNTESTABLE) */ } return target; } +/* +** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr. +** If it is, then resolve the expression by reading from the index and +** return the register into which the value has been read. If pExpr is +** not an indexed expression, then return negative. +*/ +static SQLITE_NOINLINE int wx_sqlite3IndexedExprLookup( + Parse *pParse, /* The parsing context */ + Expr *pExpr, /* The expression to potentially bypass */ + int target /* Where to store the result of the expression */ +){ + IndexedExpr *p; + Vdbe *v; + for(p=pParse->pIdxEpr; p; p=p->pIENext){ + u8 exprAff; + int iDataCur = p->iDataCur; + if( iDataCur<0 ) continue; + if( pParse->iSelfTab ){ + if( p->iDataCur!=pParse->iSelfTab-1 ) continue; + iDataCur = -1; + } + if( wx_sqlite3ExprCompare(0, pExpr, p->pExpr, iDataCur)!=0 ) continue; + assert( p->aff>=SQLITE_AFF_BLOB && p->aff<=SQLITE_AFF_NUMERIC ); + exprAff = wx_sqlite3ExprAffinity(pExpr); + if( (exprAff<=SQLITE_AFF_BLOB && p->aff!=SQLITE_AFF_BLOB) + || (exprAff==SQLITE_AFF_TEXT && p->aff!=SQLITE_AFF_TEXT) + || (exprAff>=SQLITE_AFF_NUMERIC && p->aff!=SQLITE_AFF_NUMERIC) + ){ + /* Affinity mismatch on a generated column */ + continue; + } + + v = pParse->pVdbe; + assert( v!=0 ); + if( p->bMaybeNullRow ){ + /* If the index is on a NULL row due to an outer join, then we + ** cannot extract the value from the index. The value must be + ** computed using the original expression. */ + int addr = wx_sqlite3VdbeCurrentAddr(v); + wx_sqlite3VdbeAddOp3(v, OP_IfNullRow, p->iIdxCur, addr+3, target); + VdbeCoverage(v); + wx_sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target); + VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol)); + wx_sqlite3VdbeGoto(v, 0); + p = pParse->pIdxEpr; + pParse->pIdxEpr = 0; + wx_sqlite3ExprCode(pParse, pExpr, target); + pParse->pIdxEpr = p; + wx_sqlite3VdbeJumpHere(v, addr+2); + }else{ + wx_sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target); + VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol)); + } + return target; + } + return -1; /* Not found */ +} + /* ** Generate code into the current Vdbe to evaluate the given @@ -104567,6 +110026,11 @@ SQLITE_PRIVATE int wx_sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int targ expr_code_doover: if( pExpr==0 ){ op = TK_NULL; + }else if( pParse->pIdxEpr!=0 + && !ExprHasProperty(pExpr, EP_Leaf) + && (r1 = wx_sqlite3IndexedExprLookup(pParse, pExpr, target))>=0 + ){ + return r1; }else{ assert( !ExprHasVVAProperty(pExpr,EP_Immutable) ); op = pExpr->op; @@ -104579,21 +110043,28 @@ expr_code_doover: assert( pExpr->iAgg>=0 && pExpr->iAggnColumn ); pCol = &pAggInfo->aCol[pExpr->iAgg]; if( !pAggInfo->directMode ){ - assert( pCol->iMem>0 ); - return pCol->iMem; + return AggInfoColumnReg(pAggInfo, pExpr->iAgg); }else if( pAggInfo->useSortingIdx ){ Table *pTab = pCol->pTab; wx_sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab, pCol->iSorterColumn, target); - if( pCol->iColumn<0 ){ + if( pTab==0 ){ + /* No comment added */ + }else if( pCol->iColumn<0 ){ VdbeComment((v,"%s.rowid",pTab->zName)); }else{ - VdbeComment((v,"%s.%s",pTab->zName,pTab->aCol[pCol->iColumn].zName)); + VdbeComment((v,"%s.%s", + pTab->zName, pTab->aCol[pCol->iColumn].zCnName)); if( pTab->aCol[pCol->iColumn].affinity==SQLITE_AFF_REAL ){ wx_sqlite3VdbeAddOp1(v, OP_RealAffinity, target); } } return target; + }else if( pExpr->y.pTab==0 ){ + /* This case happens when the argument to an aggregate function + ** is rewritten by aggregateConvertIndexedExprRefToColumn() */ + wx_sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, pExpr->iColumn, target); + return target; } /* Otherwise, fall thru into the TK_COLUMN case */ /* no break */ deliberate_fall_through @@ -104610,13 +110081,11 @@ expr_code_doover: */ int aff; iReg = wx_sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target); - if( pExpr->y.pTab ){ - aff = wx_sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); - }else{ - aff = pExpr->affExpr; - } + assert( ExprUseYTab(pExpr) ); + assert( pExpr->y.pTab!=0 ); + aff = wx_sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); if( aff>SQLITE_AFF_BLOB ){ - static const char zAff[] = "B\000C\000D\000E"; + static const char zAff[] = "B\000C\000D\000E\000F"; assert( SQLITE_AFF_BLOB=='A' ); assert( SQLITE_AFF_TEXT=='B' ); wx_sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0, @@ -104633,9 +110102,11 @@ expr_code_doover: ** immediately prior to the first column. */ Column *pCol; - Table *pTab = pExpr->y.pTab; + Table *pTab; int iSrc; int iCol = pExpr->iColumn; + assert( ExprUseYTab(pExpr) ); + pTab = pExpr->y.pTab; assert( pTab!=0 ); assert( iCol>=XN_ROWID ); assert( iColnCol ); @@ -104649,12 +110120,12 @@ expr_code_doover: if( pCol->colFlags & COLFLAG_GENERATED ){ if( pCol->colFlags & COLFLAG_BUSY ){ wx_sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", - pCol->zName); + pCol->zCnName); return 0; } pCol->colFlags |= COLFLAG_BUSY; if( pCol->colFlags & COLFLAG_NOTAVAIL ){ - wx_sqlite3ExprCodeGeneratedColumn(pParse, pCol, iSrc); + wx_sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, iSrc); } pCol->colFlags &= ~(COLFLAG_BUSY|COLFLAG_NOTAVAIL); return iSrc; @@ -104673,12 +110144,11 @@ expr_code_doover: iTab = pParse->iSelfTab - 1; } } + assert( ExprUseYTab(pExpr) ); + assert( pExpr->y.pTab!=0 ); iReg = wx_sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab, pExpr->iColumn, iTab, target, pExpr->op2); - if( pExpr->y.pTab==0 && pExpr->affExpr==SQLITE_AFF_REAL ){ - wx_sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); - } return iReg; } case TK_INTEGER: { @@ -104706,7 +110176,7 @@ expr_code_doover: ** Expr node to be passed into this function, it will be handled ** sanely and not crash. But keep the assert() to bring the problem ** to the attention of the developers. */ - assert( op==TK_NULL ); + assert( op==TK_NULL || op==TK_ERROR || pParse->db->mallocFailed ); wx_sqlite3VdbeAddOp2(v, OP_Null, 0, target); return target; } @@ -104750,6 +110220,7 @@ expr_code_doover: wx_sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); inReg = target; } + assert( !ExprHasProperty(pExpr, EP_IntValue) ); wx_sqlite3VdbeAddOp2(v, OP_Cast, target, wx_sqlite3AffinityType(pExpr->u.zToken, 0)); return inReg; @@ -104772,8 +110243,9 @@ expr_code_doover: }else{ r1 = wx_sqlite3ExprCodeTemp(pParse, pLeft, ®Free1); r2 = wx_sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); - codeCompare(pParse, pLeft, pExpr->pRight, op, - r1, r2, inReg, SQLITE_STOREP2 | p5, + wx_sqlite3VdbeAddOp2(v, OP_Integer, 1, inReg); + codeCompare(pParse, pLeft, pExpr->pRight, op, r1, r2, + wx_sqlite3VdbeCurrentAddr(v)+2, p5, ExprHasProperty(pExpr,EP_Commuted)); assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); @@ -104781,6 +110253,11 @@ expr_code_doover: assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); + if( p5==SQLITE_NULLEQ ){ + wx_sqlite3VdbeAddOp2(v, OP_Integer, 0, inReg); + }else{ + wx_sqlite3VdbeAddOp3(v, OP_ZeroOrNull, r1, inReg, r2); + } testcase( regFree1==0 ); testcase( regFree2==0 ); } @@ -104883,9 +110360,9 @@ expr_code_doover: || NEVER(pExpr->iAgg>=pInfo->nFunc) ){ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - wx_sqlite3ErrorMsg(pParse, "misuse of aggregate: %s()", pExpr->u.zToken); + wx_sqlite3ErrorMsg(pParse, "misuse of aggregate: %#T()", pExpr); }else{ - return pInfo->aFunc[pExpr->iAgg].iMem; + return AggInfoFuncReg(pInfo, pExpr->iAgg); } break; } @@ -104911,8 +110388,8 @@ expr_code_doover: ** multiple times if we know they always give the same result */ return wx_sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); } - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); assert( !ExprHasProperty(pExpr, EP_TokenOnly) ); + assert( ExprUseXList(pExpr) ); pFarg = pExpr->x.pList; nFarg = pFarg ? pFarg->nExpr : 0; assert( !ExprHasProperty(pExpr, EP_IntValue) ); @@ -104924,7 +110401,7 @@ expr_code_doover: } #endif if( pDef==0 || pDef->xFinalize!=0 ){ - wx_sqlite3ErrorMsg(pParse, "unknown function: %s()", zId); + wx_sqlite3ErrorMsg(pParse, "unknown function: %#T()", pExpr); break; } if( pDef->funcFlags & SQLITE_FUNC_INLINE ){ @@ -105000,20 +110477,8 @@ expr_code_doover: if( !pColl ) pColl = db->pDfltColl; wx_sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ); } -#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC - if( pDef->funcFlags & SQLITE_FUNC_OFFSET ){ - Expr *pArg = pFarg->a[0].pExpr; - if( pArg->op==TK_COLUMN ){ - wx_sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target); - }else{ - wx_sqlite3VdbeAddOp2(v, OP_Null, 0, target); - } - }else -#endif - { - wx_sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg, - pDef, pExpr->op2); - } + wx_sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg, + pDef, pExpr->op2); if( nFarg ){ if( constMask==0 ){ wx_sqlite3ReleaseTempRange(pParse, r1, nFarg); @@ -105031,7 +110496,10 @@ expr_code_doover: testcase( op==TK_SELECT ); if( pParse->db->mallocFailed ){ return 0; - }else if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){ + }else if( op==TK_SELECT + && ALWAYS( ExprUseXSelect(pExpr) ) + && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 + ){ wx_sqlite3SubselectError(pParse, nCol, 1); }else{ return wx_sqlite3CodeSubselect(pParse, pExpr); @@ -105040,17 +110508,18 @@ expr_code_doover: } case TK_SELECT_COLUMN: { int n; - if( pExpr->pLeft->iTable==0 ){ - pExpr->pLeft->iTable = wx_sqlite3CodeSubselect(pParse, pExpr->pLeft); + Expr *pLeft = pExpr->pLeft; + if( pLeft->iTable==0 || pParse->withinRJSubrtn > pLeft->op2 ){ + pLeft->iTable = wx_sqlite3CodeSubselect(pParse, pLeft); + pLeft->op2 = pParse->withinRJSubrtn; } - assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT ); - if( pExpr->iTable!=0 - && pExpr->iTable!=(n = wx_sqlite3ExprVectorSize(pExpr->pLeft)) - ){ + assert( pLeft->op==TK_SELECT || pLeft->op==TK_ERROR ); + n = wx_sqlite3ExprVectorSize(pLeft); + if( pExpr->iTable!=n ){ wx_sqlite3ErrorMsg(pParse, "%d columns assigned %d values", pExpr->iTable, n); } - return pExpr->pLeft->iTable + pExpr->iColumn; + return pLeft->iTable + pExpr->iColumn; } case TK_IN: { int destIfFalse = wx_sqlite3VdbeMakeLabel(pParse); @@ -105081,8 +110550,27 @@ expr_code_doover: exprCodeBetween(pParse, pExpr, target, 0, 0); return target; } + case TK_COLLATE: { + if( !ExprHasProperty(pExpr, EP_Collate) ){ + /* A TK_COLLATE Expr node without the EP_Collate tag is a so-called + ** "SOFT-COLLATE" that is added to constraints that are pushed down + ** from outer queries into sub-queries by the push-down optimization. + ** Clear subtypes as subtypes may not cross a subquery boundary. + */ + assert( pExpr->pLeft ); + inReg = wx_sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); + if( inReg!=target ){ + wx_sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); + inReg = target; + } + wx_sqlite3VdbeAddOp1(v, OP_ClrSubtype, inReg); + return inReg; + }else{ + pExpr = pExpr->pLeft; + goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. */ + } + } case TK_SPAN: - case TK_COLLATE: case TK_UPLUS: { pExpr = pExpr->pLeft; goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. OSSFuzz. */ @@ -105114,9 +110602,14 @@ expr_code_doover: ** p1==1 -> old.a p1==4 -> new.a ** p1==2 -> old.b p1==5 -> new.b */ - Table *pTab = pExpr->y.pTab; - int iCol = pExpr->iColumn; - int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + Table *pTab; + int iCol; + int p1; + + assert( ExprUseYTab(pExpr) ); + pTab = pExpr->y.pTab; + iCol = pExpr->iColumn; + p1 = pExpr->iTable * (pTab->nCol+1) + 1 + wx_sqlite3TableColumnToStorage(pTab, iCol); assert( pExpr->iTable==0 || pExpr->iTable==1 ); @@ -105127,7 +110620,7 @@ expr_code_doover: wx_sqlite3VdbeAddOp2(v, OP_Param, p1, target); VdbeComment((v, "r[%d]=%s.%s", target, (pExpr->iTable ? "new" : "old"), - (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[iCol].zName) + (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[iCol].zCnName) )); #ifndef SQLITE_OMIT_FLOATING_POINT @@ -105157,16 +110650,37 @@ expr_code_doover: case TK_IF_NULL_ROW: { int addrINR; u8 okConstFactor = pParse->okConstFactor; - addrINR = wx_sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable); - /* Temporarily disable factoring of constant expressions, since - ** even though expressions may appear to be constant, they are not - ** really constant because they originate from the right-hand side - ** of a LEFT JOIN. */ - pParse->okConstFactor = 0; + AggInfo *pAggInfo = pExpr->pAggInfo; + if( pAggInfo ){ + assert( pExpr->iAgg>=0 && pExpr->iAggnColumn ); + if( !pAggInfo->directMode ){ + inReg = AggInfoColumnReg(pAggInfo, pExpr->iAgg); + break; + } + if( pExpr->pAggInfo->useSortingIdx ){ + wx_sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab, + pAggInfo->aCol[pExpr->iAgg].iSorterColumn, + target); + inReg = target; + break; + } + } + addrINR = wx_sqlite3VdbeAddOp3(v, OP_IfNullRow, pExpr->iTable, 0, target); + /* The OP_IfNullRow opcode above can overwrite the result register with + ** NULL. So we have to ensure that the result register is not a value + ** that is suppose to be a constant. Two defenses are needed: + ** (1) Temporarily disable factoring of constant expressions + ** (2) Make sure the computed value really is stored in register + ** "target" and not someplace else. + */ + pParse->okConstFactor = 0; /* note (1) above */ inReg = wx_sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); pParse->okConstFactor = okConstFactor; + if( inReg!=target ){ /* note (2) above */ + wx_sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); + inReg = target; + } wx_sqlite3VdbeJumpHere(v, addrINR); - wx_sqlite3VdbeChangeP3(v, addrINR, inReg); break; } @@ -105204,7 +110718,7 @@ expr_code_doover: Expr *pDel = 0; wx_sqlite3 *db = pParse->db; - assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList ); + assert( ExprUseXList(pExpr) && pExpr->x.pList!=0 ); assert(pExpr->x.pList->nExpr > 0); pEList = pExpr->x.pList; aListelem = pEList->a; @@ -105318,7 +110832,9 @@ SQLITE_PRIVATE int wx_sqlite3ExprCodeRunJustOnce( struct ExprList_item *pItem; int i; for(pItem=p->a, i=p->nExpr; i>0; pItem++, i--){ - if( pItem->reusable && wx_sqlite3ExprCompare(0,pItem->pExpr,pExpr,-1)==0 ){ + if( pItem->fg.reusable + && wx_sqlite3ExprCompare(0,pItem->pExpr,pExpr,-1)==0 + ){ return pItem->u.iConstExprReg; } } @@ -105341,7 +110857,7 @@ SQLITE_PRIVATE int wx_sqlite3ExprCodeRunJustOnce( p = wx_sqlite3ExprListAppend(pParse, p, pExpr); if( p ){ struct ExprList_item *pItem = &p->a[p->nExpr-1]; - pItem->reusable = regDest<0; + pItem->fg.reusable = regDest<0; if( regDest<0 ) regDest = ++pParse->nMem; pItem->u.iConstExprReg = regDest; } @@ -105401,7 +110917,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ inReg = wx_sqlite3ExprCodeTarget(pParse, pExpr, target); if( inReg!=target ){ u8 op; - if( ExprHasProperty(pExpr,EP_Subquery) ){ + if( ALWAYS(pExpr) && ExprHasProperty(pExpr,EP_Subquery) ){ op = OP_Copy; }else{ op = OP_SCopy; @@ -105475,7 +110991,7 @@ SQLITE_PRIVATE int wx_sqlite3ExprCodeExprList( for(pItem=pList->a, i=0; ipExpr; #ifdef SQLITE_ENABLE_SORTER_REFERENCES - if( pItem->bSorterRef ){ + if( pItem->fg.bSorterRef ){ i--; n--; }else @@ -105496,7 +111012,7 @@ SQLITE_PRIVATE int wx_sqlite3ExprCodeExprList( if( inReg!=target+i ){ VdbeOp *pOp; if( copyOp==OP_Copy - && (pOp=wx_sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy + && (pOp=wx_sqlite3VdbeGetLastOp(v))->opcode==OP_Copy && pOp->p1+pOp->p3+1==inReg && pOp->p2+pOp->p3+1==target+i && pOp->p5==0 /* The do-not-merge flag must be clear */ @@ -105549,7 +111065,7 @@ static void exprCodeBetween( memset(&compRight, 0, sizeof(Expr)); memset(&exprAnd, 0, sizeof(Expr)); - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( ExprUseXList(pExpr) ); pDel = wx_sqlite3ExprDup(db, pExpr->pLeft, 0); if( db->mallocFailed==0 ){ exprAnd.op = TK_AND; @@ -105569,8 +111085,8 @@ static void exprCodeBetween( ** so that the wx_sqlite3ExprCodeTarget() routine will not attempt to move ** it into the Parse.pConstExpr list. We should use a new bit for this, ** for clarity, but we are out of bits in the Expr.flags field so we - ** have to reuse the EP_FromJoin bit. Bummer. */ - pDel->flags |= EP_FromJoin; + ** have to reuse the EP_OuterON bit. Bummer. */ + pDel->flags |= EP_OuterON; wx_sqlite3ExprCodeTarget(pParse, &exprAnd, dest); } wx_sqlite3ReleaseTempReg(pParse, regFree1); @@ -105695,6 +111211,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, i assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL ); assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL ); r1 = wx_sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); + wx_sqlite3VdbeTypeofColumn(v, r1); wx_sqlite3VdbeAddOp2(v, op, r1, dest); VdbeCoverageIf(v, op==TK_ISNULL); VdbeCoverageIf(v, op==TK_NOTNULL); @@ -105869,6 +111386,7 @@ SQLITE_PRIVATE void wx_sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, case TK_ISNULL: case TK_NOTNULL: { r1 = wx_sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); + wx_sqlite3VdbeTypeofColumn(v, r1); wx_sqlite3VdbeAddOp2(v, op, r1, dest); testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL); testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL); @@ -105939,7 +111457,11 @@ SQLITE_PRIVATE void wx_sqlite3ExprIfFalseDup(Parse *pParse, Expr *pExpr, int des ** Otherwise, if the values are not the same or if pExpr is not a simple ** SQL value, zero is returned. */ -static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ +static int exprCompareVariable( + const Parse *pParse, + const Expr *pVar, + const Expr *pExpr +){ int res = 0; int iVar; wx_sqlite3_value *pL, *pR = 0; @@ -105991,7 +111513,12 @@ static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ ** Argument pParse should normally be NULL. If it is not NULL and pA or ** pB causes a return value of 2. */ -SQLITE_PRIVATE int wx_sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){ +SQLITE_PRIVATE int wx_sqlite3ExprCompare( + const Parse *pParse, + const Expr *pA, + const Expr *pB, + int iTab +){ u32 combinedFlags; if( pA==0 || pB==0 ){ return pB==pA ? 0 : 2; @@ -106013,9 +111540,17 @@ SQLITE_PRIVATE int wx_sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int if( pB->op==TK_COLLATE && wx_sqlite3ExprCompare(pParse, pA,pB->pLeft,iTab)<2 ){ return 1; } - return 2; + if( pA->op==TK_AGG_COLUMN && pB->op==TK_COLUMN + && pB->iTable<0 && pA->iTable==iTab + ){ + /* fall through */ + }else{ + return 2; + } } - if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){ + assert( !ExprHasProperty(pA, EP_IntValue) ); + assert( !ExprHasProperty(pB, EP_IntValue) ); + if( pA->u.zToken ){ if( pA->op==TK_FUNCTION || pA->op==TK_AGG_FUNCTION ){ if( wx_sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; #ifndef SQLITE_OMIT_WINDOWFUNC @@ -106033,7 +111568,12 @@ SQLITE_PRIVATE int wx_sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int return 0; }else if( pA->op==TK_COLLATE ){ if( wx_sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; - }else if( ALWAYS(pB->u.zToken!=0) && strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ + }else + if( pB->u.zToken!=0 + && pA->op!=TK_COLUMN + && pA->op!=TK_AGG_COLUMN + && strcmp(pA->u.zToken,pB->u.zToken)!=0 + ){ return 2; } } @@ -106075,7 +111615,7 @@ SQLITE_PRIVATE int wx_sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int ** Two NULL pointers are considered to be the same. But a NULL pointer ** always differs from a non-NULL pointer. */ -SQLITE_PRIVATE int wx_sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){ +SQLITE_PRIVATE int wx_sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB, int iTab){ int i; if( pA==0 && pB==0 ) return 0; if( pA==0 || pB==0 ) return 1; @@ -106084,7 +111624,7 @@ SQLITE_PRIVATE int wx_sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTa int res; Expr *pExprA = pA->a[i].pExpr; Expr *pExprB = pB->a[i].pExpr; - if( pA->a[i].sortFlags!=pB->a[i].sortFlags ) return 1; + if( pA->a[i].fg.sortFlags!=pB->a[i].fg.sortFlags ) return 1; if( (res = wx_sqlite3ExprCompare(0, pExprA, pExprB, iTab)) ) return res; } return 0; @@ -106094,7 +111634,7 @@ SQLITE_PRIVATE int wx_sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTa ** Like wx_sqlite3ExprCompare() except COLLATE operators at the top-level ** are ignored. */ -SQLITE_PRIVATE int wx_sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){ +SQLITE_PRIVATE int wx_sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){ return wx_sqlite3ExprCompare(0, wx_sqlite3ExprSkipCollateAndLikely(pA), wx_sqlite3ExprSkipCollateAndLikely(pB), @@ -106108,9 +111648,9 @@ SQLITE_PRIVATE int wx_sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){ ** non-NULL if pNN is not NULL */ static int exprImpliesNotNull( - Parse *pParse, /* Parsing context */ - Expr *p, /* The expression to be checked */ - Expr *pNN, /* The expression that is NOT NULL */ + const Parse *pParse,/* Parsing context */ + const Expr *p, /* The expression to be checked */ + const Expr *pNN, /* The expression that is NOT NULL */ int iTab, /* Table being evaluated */ int seenNot /* Return true only if p can be any non-NULL value */ ){ @@ -106122,12 +111662,13 @@ static int exprImpliesNotNull( switch( p->op ){ case TK_IN: { if( seenNot && ExprHasProperty(p, EP_xIsSelect) ) return 0; - assert( ExprHasProperty(p,EP_xIsSelect) - || (p->x.pList!=0 && p->x.pList->nExpr>0) ); + assert( ExprUseXSelect(p) || (p->x.pList!=0 && p->x.pList->nExpr>0) ); return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1); } case TK_BETWEEN: { - ExprList *pList = p->x.pList; + ExprList *pList; + assert( ExprUseXList(p) ); + pList = p->x.pList; assert( pList!=0 ); assert( pList->nExpr==2 ); if( seenNot ) return 0; @@ -106203,7 +111744,12 @@ static int exprImpliesNotNull( ** improvement. Returning false might cause a performance reduction, but ** it will always give the correct answer and is hence always safe. */ -SQLITE_PRIVATE int wx_sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, int iTab){ +SQLITE_PRIVATE int wx_sqlite3ExprImpliesExpr( + const Parse *pParse, + const Expr *pE1, + const Expr *pE2, + int iTab +){ if( wx_sqlite3ExprCompare(pParse, pE1, pE2, iTab)==0 ){ return 1; } @@ -106233,7 +111779,7 @@ SQLITE_PRIVATE int wx_sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2 static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_AGG_COLUMN ); testcase( pExpr->op==TK_AGG_FUNCTION ); - if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune; + if( ExprHasProperty(pExpr, EP_OuterON) ) return WRC_Prune; switch( pExpr->op ){ case TK_ISNOT: case TK_ISNULL: @@ -106299,10 +111845,14 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_GE ); /* The y.pTab=0 assignment in wherecode.c always happens after the ** impliesNotNullRow() test */ - if( (pLeft->op==TK_COLUMN && ALWAYS(pLeft->y.pTab!=0) - && IsVirtual(pLeft->y.pTab)) - || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) - && IsVirtual(pRight->y.pTab)) + assert( pLeft->op!=TK_COLUMN || ExprUseYTab(pLeft) ); + assert( pRight->op!=TK_COLUMN || ExprUseYTab(pRight) ); + if( (pLeft->op==TK_COLUMN + && ALWAYS(pLeft->y.pTab!=0) + && IsVirtual(pLeft->y.pTab)) + || (pRight->op==TK_COLUMN + && ALWAYS(pRight->y.pTab!=0) + && IsVirtual(pRight->y.pTab)) ){ return WRC_Prune; } @@ -106326,8 +111876,8 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ ** False positives are not allowed, however. A false positive may result ** in an incorrect answer. ** -** Terms of p that are marked with EP_FromJoin (and hence that come from -** the ON or USING clauses of LEFT JOINS) are excluded from the analysis. +** Terms of p that are marked with EP_OuterON (and hence that come from +** the ON or USING clauses of OUTER JOINS) are excluded from the analysis. ** ** This routine is used to check if a LEFT JOIN can be converted into ** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE @@ -106411,88 +111961,126 @@ SQLITE_PRIVATE int wx_sqlite3ExprCoveredByIndex( } -/* -** An instance of the following structure is used by the tree walker -** to count references to table columns in the arguments of an -** aggregate function, in order to implement the -** wx_sqlite3FunctionThisSrc() routine. -*/ -struct SrcCount { - SrcList *pSrc; /* One particular FROM clause in a nested query */ - int iSrcInner; /* Smallest cursor number in this context */ - int nThis; /* Number of references to columns in pSrcList */ - int nOther; /* Number of references to columns in other FROM clauses */ +/* Structure used to pass information throught the Walker in order to +** implement wx_sqlite3ReferencesSrcList(). +*/ +struct RefSrcList { + wx_sqlite3 *db; /* Database connection used for wx_sqlite3DbRealloc() */ + SrcList *pRef; /* Looking for references to these tables */ + i64 nExclude; /* Number of tables to exclude from the search */ + int *aiExclude; /* Cursor IDs for tables to exclude from the search */ }; /* -** xSelect callback for wx_sqlite3FunctionUsesThisSrc(). If this is the first -** SELECT with a FROM clause encountered during this iteration, set -** SrcCount.iSrcInner to the cursor number of the leftmost object in -** the FROM cause. +** Walker SELECT callbacks for wx_sqlite3ReferencesSrcList(). +** +** When entering a new subquery on the pExpr argument, add all FROM clause +** entries for that subquery to the exclude list. +** +** When leaving the subquery, remove those entries from the exclude list. */ -static int selectSrcCount(Walker *pWalker, Select *pSel){ - struct SrcCount *p = pWalker->u.pSrcCount; - if( p->iSrcInner==0x7FFFFFFF && ALWAYS(pSel->pSrc) && pSel->pSrc->nSrc ){ - pWalker->u.pSrcCount->iSrcInner = pSel->pSrc->a[0].iCursor; +static int selectRefEnter(Walker *pWalker, Select *pSelect){ + struct RefSrcList *p = pWalker->u.pRefSrcList; + SrcList *pSrc = pSelect->pSrc; + i64 i, j; + int *piNew; + if( pSrc->nSrc==0 ) return WRC_Continue; + j = p->nExclude; + p->nExclude += pSrc->nSrc; + piNew = wx_sqlite3DbRealloc(p->db, p->aiExclude, p->nExclude*sizeof(int)); + if( piNew==0 ){ + p->nExclude = 0; + return WRC_Abort; + }else{ + p->aiExclude = piNew; + } + for(i=0; inSrc; i++, j++){ + p->aiExclude[j] = pSrc->a[i].iCursor; } return WRC_Continue; } +static void selectRefLeave(Walker *pWalker, Select *pSelect){ + struct RefSrcList *p = pWalker->u.pRefSrcList; + SrcList *pSrc = pSelect->pSrc; + if( p->nExclude ){ + assert( p->nExclude>=pSrc->nSrc ); + p->nExclude -= pSrc->nSrc; + } +} -/* -** Count the number of references to columns. +/* This is the Walker EXPR callback for wx_sqlite3ReferencesSrcList(). +** +** Set the 0x01 bit of pWalker->eCode if there is a reference to any +** of the tables shown in RefSrcList.pRef. +** +** Set the 0x02 bit of pWalker->eCode if there is a reference to a +** table is in neither RefSrcList.pRef nor RefSrcList.aiExclude. */ -static int exprSrcCount(Walker *pWalker, Expr *pExpr){ - /* There was once a NEVER() on the second term on the grounds that - ** wx_sqlite3FunctionUsesThisSrc() was always called before - ** wx_sqlite3ExprAnalyzeAggregates() and so the TK_COLUMNs have not yet - ** been converted into TK_AGG_COLUMN. But this is no longer true due - ** to window functions - wx_sqlite3WindowRewrite() may now indirectly call - ** FunctionUsesThisSrc() when creating a new sub-select. */ - if( pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN ){ +static int exprRefToSrcList(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_COLUMN + || pExpr->op==TK_AGG_COLUMN + ){ int i; - struct SrcCount *p = pWalker->u.pSrcCount; - SrcList *pSrc = p->pSrc; + struct RefSrcList *p = pWalker->u.pRefSrcList; + SrcList *pSrc = p->pRef; int nSrc = pSrc ? pSrc->nSrc : 0; for(i=0; iiTable==pSrc->a[i].iCursor ) break; + if( pExpr->iTable==pSrc->a[i].iCursor ){ + pWalker->eCode |= 1; + return WRC_Continue; + } } - if( inThis++; - }else if( pExpr->iTableiSrcInner ){ - /* In a well-formed parse tree (no name resolution errors), - ** TK_COLUMN nodes with smaller Expr.iTable values are in an - ** outer context. Those are the only ones to count as "other" */ - p->nOther++; + for(i=0; inExclude && p->aiExclude[i]!=pExpr->iTable; i++){} + if( i>=p->nExclude ){ + pWalker->eCode |= 2; } } return WRC_Continue; } /* -** Determine if any of the arguments to the pExpr Function reference -** pSrcList. Return true if they do. Also return true if the function -** has no arguments or has only constant arguments. Return false if pExpr -** references columns but not columns of tables found in pSrcList. +** Check to see if pExpr references any tables in pSrcList. +** Possible return values: +** +** 1 pExpr does references a table in pSrcList. +** +** 0 pExpr references some table that is not defined in either +** pSrcList or in subqueries of pExpr itself. +** +** -1 pExpr only references no tables at all, or it only +** references tables defined in subqueries of pExpr itself. +** +** As currently used, pExpr is always an aggregate function call. That +** fact is exploited for efficiency. */ -SQLITE_PRIVATE int wx_sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ +SQLITE_PRIVATE int wx_sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList *pSrcList){ Walker w; - struct SrcCount cnt; - assert( pExpr->op==TK_AGG_FUNCTION ); + struct RefSrcList x; + assert( pParse->db!=0 ); memset(&w, 0, sizeof(w)); - w.xExprCallback = exprSrcCount; - w.xSelectCallback = selectSrcCount; - w.u.pSrcCount = &cnt; - cnt.pSrc = pSrcList; - cnt.iSrcInner = (pSrcList&&pSrcList->nSrc)?pSrcList->a[0].iCursor:0x7FFFFFFF; - cnt.nThis = 0; - cnt.nOther = 0; + memset(&x, 0, sizeof(x)); + w.xExprCallback = exprRefToSrcList; + w.xSelectCallback = selectRefEnter; + w.xSelectCallback2 = selectRefLeave; + w.u.pRefSrcList = &x; + x.db = pParse->db; + x.pRef = pSrcList; + assert( pExpr->op==TK_AGG_FUNCTION ); + assert( ExprUseXList(pExpr) ); wx_sqlite3WalkExprList(&w, pExpr->x.pList); #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(pExpr, EP_WinFunc) ){ wx_sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter); } #endif - return cnt.nThis>0 || cnt.nOther==0; + if( x.aiExclude ) wx_sqlite3DbNNFreeNN(pParse->db, x.aiExclude); + if( w.eCode & 0x01 ){ + return 1; + }else if( w.eCode ){ + return 0; + }else{ + return -1; + } } /* @@ -106503,10 +112091,8 @@ SQLITE_PRIVATE int wx_sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList) ** it does, make a copy. This is done because the pExpr argument is ** subject to change. ** -** The copy is stored on pParse->pConstExpr with a register number of 0. -** This will cause the expression to be deleted automatically when the -** Parse object is destroyed, but the zero register number means that it -** will not generate any code in the preamble. +** The copy is scheduled for deletion using the wx_sqlite3ExprDeferredDelete() +** which builds on the wx_sqlite3ParserAddCleanup() mechanism. */ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced)) @@ -106516,8 +112102,7 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ int iAgg = pExpr->iAgg; Parse *pParse = pWalker->pParse; wx_sqlite3 *db = pParse->db; - assert( pExpr->op==TK_AGG_COLUMN || pExpr->op==TK_AGG_FUNCTION ); - if( pExpr->op==TK_AGG_COLUMN ){ + if( pExpr->op!=TK_AGG_FUNCTION ){ assert( iAgg>=0 && iAggnColumn ); if( pAggInfo->aCol[iAgg].pCExpr==pExpr ){ pExpr = wx_sqlite3ExprDup(db, pExpr, 0); @@ -106527,6 +112112,7 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ } } }else{ + assert( pExpr->op==TK_AGG_FUNCTION ); assert( iAgg>=0 && iAggnFunc ); if( pAggInfo->aFunc[iAgg].pFExpr==pExpr ){ pExpr = wx_sqlite3ExprDup(db, pExpr, 0); @@ -106583,6 +112169,73 @@ static int addAggInfoFunc(wx_sqlite3 *db, AggInfo *pInfo){ return i; } +/* +** Search the AggInfo object for an aCol[] entry that has iTable and iColumn. +** Return the index in aCol[] of the entry that describes that column. +** +** If no prior entry is found, create a new one and return -1. The +** new column will have an idex of pAggInfo->nColumn-1. +*/ +static void findOrCreateAggInfoColumn( + Parse *pParse, /* Parsing context */ + AggInfo *pAggInfo, /* The AggInfo object to search and/or modify */ + Expr *pExpr /* Expr describing the column to find or insert */ +){ + struct AggInfo_col *pCol; + int k; + + assert( pAggInfo->iFirstReg==0 ); + pCol = pAggInfo->aCol; + for(k=0; knColumn; k++, pCol++){ + if( pCol->iTable==pExpr->iTable + && pCol->iColumn==pExpr->iColumn + && pExpr->op!=TK_IF_NULL_ROW + ){ + goto fix_up_expr; + } + } + k = addAggInfoColumn(pParse->db, pAggInfo); + if( k<0 ){ + /* OOM on resize */ + assert( pParse->db->mallocFailed ); + return; + } + pCol = &pAggInfo->aCol[k]; + assert( ExprUseYTab(pExpr) ); + pCol->pTab = pExpr->y.pTab; + pCol->iTable = pExpr->iTable; + pCol->iColumn = pExpr->iColumn; + pCol->iSorterColumn = -1; + pCol->pCExpr = pExpr; + if( pAggInfo->pGroupBy && pExpr->op!=TK_IF_NULL_ROW ){ + int j, n; + ExprList *pGB = pAggInfo->pGroupBy; + struct ExprList_item *pTerm = pGB->a; + n = pGB->nExpr; + for(j=0; jpExpr; + if( pE->op==TK_COLUMN + && pE->iTable==pExpr->iTable + && pE->iColumn==pExpr->iColumn + ){ + pCol->iSorterColumn = j; + break; + } + } + } + if( pCol->iSorterColumn<0 ){ + pCol->iSorterColumn = pAggInfo->nSortingColumn++; + } +fix_up_expr: + ExprSetVVAProperty(pExpr, EP_NoReduce); + assert( pExpr->pAggInfo==0 || pExpr->pAggInfo==pAggInfo ); + pExpr->pAggInfo = pAggInfo; + if( pExpr->op==TK_COLUMN ){ + pExpr->op = TK_AGG_COLUMN; + } + pExpr->iAgg = (i16)k; +} + /* ** This is the xExprCallback for a tree walker. It is used to ** implement wx_sqlite3ExprAnalyzeAggregates(). See wx_sqlite3ExprAnalyzeAggregates @@ -106596,70 +112249,51 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ AggInfo *pAggInfo = pNC->uNC.pAggInfo; assert( pNC->ncFlags & NC_UAggInfo ); + assert( pAggInfo->iFirstReg==0 ); switch( pExpr->op ){ + default: { + IndexedExpr *pIEpr; + Expr tmp; + assert( pParse->iSelfTab==0 ); + if( (pNC->ncFlags & NC_InAggFunc)==0 ) break; + if( pParse->pIdxEpr==0 ) break; + for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){ + int iDataCur = pIEpr->iDataCur; + if( iDataCur<0 ) continue; + if( wx_sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break; + } + if( pIEpr==0 ) break; + if( NEVER(!ExprUseYTab(pExpr)) ) break; + if( pExpr->pAggInfo!=0 ) break; /* Already resolved by outer context */ + + /* If we reach this point, it means that expression pExpr can be + ** translated into a reference to an index column as described by + ** pIEpr. + */ + memset(&tmp, 0, sizeof(tmp)); + tmp.op = TK_AGG_COLUMN; + tmp.iTable = pIEpr->iIdxCur; + tmp.iColumn = pIEpr->iIdxCol; + findOrCreateAggInfoColumn(pParse, pAggInfo, &tmp); + pAggInfo->aCol[tmp.iAgg].pCExpr = pExpr; + pExpr->pAggInfo = pAggInfo; + pExpr->iAgg = tmp.iAgg; + return WRC_Prune; + } + case TK_IF_NULL_ROW: case TK_AGG_COLUMN: case TK_COLUMN: { testcase( pExpr->op==TK_AGG_COLUMN ); testcase( pExpr->op==TK_COLUMN ); + testcase( pExpr->op==TK_IF_NULL_ROW ); /* Check to see if the column is in one of the tables in the FROM ** clause of the aggregate query */ if( ALWAYS(pSrcList!=0) ){ SrcItem *pItem = pSrcList->a; for(i=0; inSrc; i++, pItem++){ - struct AggInfo_col *pCol; assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); if( pExpr->iTable==pItem->iCursor ){ - /* If we reach this point, it means that pExpr refers to a table - ** that is in the FROM clause of the aggregate query. - ** - ** Make an entry for the column in pAggInfo->aCol[] if there - ** is not an entry there already. - */ - int k; - pCol = pAggInfo->aCol; - for(k=0; knColumn; k++, pCol++){ - if( pCol->iTable==pExpr->iTable && - pCol->iColumn==pExpr->iColumn ){ - break; - } - } - if( (k>=pAggInfo->nColumn) - && (k = addAggInfoColumn(pParse->db, pAggInfo))>=0 - ){ - pCol = &pAggInfo->aCol[k]; - pCol->pTab = pExpr->y.pTab; - pCol->iTable = pExpr->iTable; - pCol->iColumn = pExpr->iColumn; - pCol->iMem = ++pParse->nMem; - pCol->iSorterColumn = -1; - pCol->pCExpr = pExpr; - if( pAggInfo->pGroupBy ){ - int j, n; - ExprList *pGB = pAggInfo->pGroupBy; - struct ExprList_item *pTerm = pGB->a; - n = pGB->nExpr; - for(j=0; jpExpr; - if( pE->op==TK_COLUMN && pE->iTable==pExpr->iTable && - pE->iColumn==pExpr->iColumn ){ - pCol->iSorterColumn = j; - break; - } - } - } - if( pCol->iSorterColumn<0 ){ - pCol->iSorterColumn = pAggInfo->nSortingColumn++; - } - } - /* There is now an entry for pExpr in pAggInfo->aCol[] (either - ** because it was there before or because we just created it). - ** Convert the pExpr to be a TK_AGG_COLUMN referring to that - ** pAggInfo->aCol[] entry. - */ - ExprSetVVAProperty(pExpr, EP_NoReduce); - pExpr->pAggInfo = pAggInfo; - pExpr->op = TK_AGG_COLUMN; - pExpr->iAgg = (i16)k; + findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr); break; } /* endif pExpr->iTable==pItem->iCursor */ } /* end loop over pSrcList */ @@ -106675,6 +112309,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ */ struct AggInfo_func *pItem = pAggInfo->aFunc; for(i=0; inFunc; i++, pItem++){ + if( pItem->pFExpr==pExpr ) break; if( wx_sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){ break; } @@ -106688,8 +112323,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); pItem = &pAggInfo->aFunc[i]; pItem->pFExpr = pExpr; - pItem->iMem = ++pParse->nMem; - assert( !ExprHasProperty(pExpr, EP_IntValue) ); + assert( ExprUseUToken(pExpr) ); pItem->pFunc = wx_sqlite3FindFunction(pParse->db, pExpr->u.zToken, pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0); @@ -106875,6 +112509,7 @@ SQLITE_PRIVATE int wx_sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast static int isAlterableTable(Parse *pParse, Table *pTab){ if( 0==wx_sqlite3StrNICmp(pTab->zName, "sqlite_", 7) #ifndef SQLITE_OMIT_VIRTUALTABLE + || (pTab->tabFlags & TF_Eponymous)!=0 || ( (pTab->tabFlags & TF_Shadow)!=0 && wx_sqlite3ReadOnlyShadowTables(pParse->db) ) @@ -106898,27 +112533,51 @@ static void renameTestSchema( const char *zDb, /* Name of db to verify schema of */ int bTemp, /* True if this is the temp db */ const char *zWhen, /* "when" part of error message */ - const char *zDropColumn /* Name of column being dropped */ + int bNoDQS /* Do not allow DQS in the schema */ ){ pParse->colNamesSet = 1; wx_sqlite3NestedParse(pParse, "SELECT 1 " - "FROM \"%w\"." DFLT_SCHEMA_TABLE " " + "FROM \"%w\"." LEGACY_SCHEMA_TABLE " " "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" " AND sql NOT LIKE 'create virtual%%'" - " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %Q)=NULL ", + " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %d)=NULL ", zDb, - zDb, bTemp, zWhen, zDropColumn + zDb, bTemp, zWhen, bNoDQS ); if( bTemp==0 ){ wx_sqlite3NestedParse(pParse, "SELECT 1 " - "FROM temp." DFLT_SCHEMA_TABLE " " + "FROM temp." LEGACY_SCHEMA_TABLE " " "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" " AND sql NOT LIKE 'create virtual%%'" - " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %Q)=NULL ", - zDb, zWhen, zDropColumn + " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %d)=NULL ", + zDb, zWhen, bNoDQS + ); + } +} + +/* +** Generate VM code to replace any double-quoted strings (but not double-quoted +** identifiers) within the "sql" column of the sqlite_schema table in +** database zDb with their single-quoted equivalents. If argument bTemp is +** not true, similarly update all SQL statements in the sqlite_schema table +** of the temp db. +*/ +static void renameFixQuotes(Parse *pParse, const char *zDb, int bTemp){ + wx_sqlite3NestedParse(pParse, + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE + " SET sql = sqlite_rename_quotefix(%Q, sql)" + "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" + " AND sql NOT LIKE 'create virtual%%'" , zDb, zDb + ); + if( bTemp==0 ){ + wx_sqlite3NestedParse(pParse, + "UPDATE temp." LEGACY_SCHEMA_TABLE + " SET sql = sqlite_rename_quotefix('temp', sql)" + "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" + " AND sql NOT LIKE 'create virtual%%'" ); } } @@ -106954,9 +112613,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterRenameTable( const char *zTabName; /* Original name of the table */ Vdbe *v; VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ - u32 savedDbFlags; /* Saved value of db->mDbFlags */ - savedDbFlags = db->mDbFlags; if( NEVER(db->mallocFailed) ) goto exit_rename_table; assert( pSrc->nSrc==1 ); assert( wx_sqlite3BtreeHoldsAllMutexes(pParse->db) ); @@ -106965,7 +112622,6 @@ SQLITE_PRIVATE void wx_sqlite3AlterRenameTable( if( !pTab ) goto exit_rename_table; iDb = wx_sqlite3SchemaToIndex(pParse->db, pTab->pSchema); zDb = db->aDb[iDb].zDbSName; - db->mDbFlags |= DBFLAG_PreferBuiltin; /* Get a NULL terminated version of the new table name. */ zName = wx_sqlite3NameFromToken(db, pName); @@ -106994,7 +112650,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterRenameTable( } #ifndef SQLITE_OMIT_VIEW - if( pTab->pSelect ){ + if( IsView(pTab) ){ wx_sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName); goto exit_rename_table; } @@ -107036,7 +112692,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterRenameTable( /* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in ** the schema to use the new table name. */ wx_sqlite3NestedParse(pParse, - "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) " "WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)" "AND name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" @@ -107046,7 +112702,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterRenameTable( /* Update the tbl_name and name columns of the sqlite_schema table ** as required. */ wx_sqlite3NestedParse(pParse, - "UPDATE %Q." DFLT_SCHEMA_TABLE " SET " + "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET " "tbl_name = %Q, " "name = CASE " "WHEN type='table' THEN %Q " @@ -107081,7 +112737,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterRenameTable( "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), " "tbl_name = " "CASE WHEN tbl_name=%Q COLLATE nocase AND " - " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename',0) " + " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename', 0) " "THEN %Q ELSE tbl_name END " "WHERE type IN ('view', 'trigger')" , zDb, zTabName, zName, zTabName, zDb, zName); @@ -107106,7 +112762,6 @@ SQLITE_PRIVATE void wx_sqlite3AlterRenameTable( exit_rename_table: wx_sqlite3SrcListDelete(db, pSrc); wx_sqlite3DbFree(db, zName); - db->mDbFlags = savedDbFlags; } /* @@ -107147,7 +112802,9 @@ SQLITE_PRIVATE void wx_sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef int r1; /* Temporary registers */ db = pParse->db; - if( pParse->nErr || db->mallocFailed ) return; + assert( db->pParse==pParse ); + if( pParse->nErr ) return; + assert( db->mallocFailed==0 ); pNew = pParse->pNewTable; assert( pNew ); @@ -107156,7 +112813,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef zDb = db->aDb[iDb].zDbSName; zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */ pCol = &pNew->aCol[pNew->nCol-1]; - pDflt = pCol->pDflt; + pDflt = wx_sqlite3ColumnExpr(pNew, pCol); pTab = wx_sqlite3FindTable(db, zTab, zDb); assert( pTab ); @@ -107190,7 +112847,8 @@ SQLITE_PRIVATE void wx_sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef if( pDflt && pDflt->pLeft->op==TK_NULL ){ pDflt = 0; } - if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ + assert( IsOrdinaryTable(pNew) ); + if( (db->flags&SQLITE_ForeignKeys) && pNew->u.tab.pFKey && pDflt ){ wx_sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "Cannot add a REFERENCES column with non-NULL default value"); } @@ -107227,31 +112885,30 @@ SQLITE_PRIVATE void wx_sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef zCol = wx_sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); if( zCol ){ char *zEnd = &zCol[pColDef->n-1]; - u32 savedDbFlags = db->mDbFlags; while( zEnd>zCol && (*zEnd==';' || wx_sqlite3Isspace(*zEnd)) ){ *zEnd-- = '\0'; } - db->mDbFlags |= DBFLAG_PreferBuiltin; /* substr() operations on characters, but addColOffset is in bytes. So we ** have to use printf() to translate between these units: */ + assert( IsOrdinaryTable(pTab) ); + assert( IsOrdinaryTable(pNew) ); wx_sqlite3NestedParse(pParse, - "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " "sql = printf('%%.%ds, ',sql) || %Q" " || substr(sql,1+length(printf('%%.%ds',sql))) " "WHERE type = 'table' AND name = %Q", - zDb, pNew->addColOffset, zCol, pNew->addColOffset, + zDb, pNew->u.tab.addColOffset, zCol, pNew->u.tab.addColOffset, zTab ); wx_sqlite3DbFree(db, zCol); - db->mDbFlags = savedDbFlags; } - /* Make sure the schema version is at least 3. But do not upgrade - ** from less than 3 to 4, as that will corrupt any preexisting DESC - ** index. - */ v = wx_sqlite3GetVdbe(pParse); if( v ){ + /* Make sure the schema version is at least 3. But do not upgrade + ** from less than 3 to 4, as that will corrupt any preexisting DESC + ** index. + */ r1 = wx_sqlite3GetTempReg(pParse); wx_sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT); wx_sqlite3VdbeUsesBtree(v, iDb); @@ -107260,10 +112917,25 @@ SQLITE_PRIVATE void wx_sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef VdbeCoverage(v); wx_sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3); wx_sqlite3ReleaseTempReg(pParse, r1); - } - /* Reload the table definition */ - renameReloadSchema(pParse, iDb, INITFLAG_AlterRename); + /* Reload the table definition */ + renameReloadSchema(pParse, iDb, INITFLAG_AlterAdd); + + /* Verify that constraints are still satisfied */ + if( pNew->pCheck!=0 + || (pCol->notNull && (pCol->colFlags & COLFLAG_GENERATED)!=0) + ){ + wx_sqlite3NestedParse(pParse, + "SELECT CASE WHEN quick_check GLOB 'CHECK*'" + " THEN raise(ABORT,'CHECK constraint failed')" + " ELSE raise(ABORT,'NOT NULL constraint failed')" + " END" + " FROM pragma_quick_check(%Q,%Q)" + " WHERE quick_check GLOB 'CHECK*' OR quick_check GLOB 'NULL*'", + zTab, zDb + ); + } + } } /* @@ -107304,7 +112976,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ #endif /* Make sure this is not an attempt to ALTER a view. */ - if( pTab->pSelect ){ + if( IsView(pTab) ){ wx_sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); goto exit_begin_add_column; } @@ -107313,7 +112985,8 @@ SQLITE_PRIVATE void wx_sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ } wx_sqlite3MayAbort(pParse); - assert( pTab->addColOffset>0 ); + assert( IsOrdinaryTable(pTab) ); + assert( pTab->u.tab.addColOffset>0 ); iDb = wx_sqlite3SchemaToIndex(db, pTab->pSchema); /* Put a copy of the Table struct in Parse.pNewTable for the @@ -107340,13 +113013,13 @@ SQLITE_PRIVATE void wx_sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); for(i=0; inCol; i++){ Column *pCol = &pNew->aCol[i]; - pCol->zName = wx_sqlite3DbStrDup(db, pCol->zName); - pCol->hName = wx_sqlite3StrIHash(pCol->zName); - pCol->zColl = 0; - pCol->pDflt = 0; + pCol->zCnName = wx_sqlite3DbStrDup(db, pCol->zCnName); + pCol->hName = wx_sqlite3StrIHash(pCol->zCnName); } + assert( IsOrdinaryTable(pNew) ); + pNew->u.tab.pDfltList = wx_sqlite3ExprListDup(db, pTab->u.tab.pDfltList, 0); pNew->pSchema = db->aDb[iDb].pSchema; - pNew->addColOffset = pTab->addColOffset; + pNew->u.tab.addColOffset = pTab->u.tab.addColOffset; pNew->nTabRef = 1; exit_begin_add_column: @@ -107366,7 +113039,7 @@ exit_begin_add_column: static int isRealTable(Parse *pParse, Table *pTab, int bDrop){ const char *zType = 0; #ifndef SQLITE_OMIT_VIEW - if( pTab->pSelect ){ + if( IsView(pTab) ){ zType = "view"; } #endif @@ -107433,13 +113106,17 @@ SQLITE_PRIVATE void wx_sqlite3AlterRenameColumn( zOld = wx_sqlite3NameFromToken(db, pOld); if( !zOld ) goto exit_rename_column; for(iCol=0; iColnCol; iCol++){ - if( 0==wx_sqlite3StrICmp(pTab->aCol[iCol].zName, zOld) ) break; + if( 0==wx_sqlite3StrICmp(pTab->aCol[iCol].zCnName, zOld) ) break; } if( iCol==pTab->nCol ){ - wx_sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zOld); + wx_sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pOld); goto exit_rename_column; } + /* Ensure the schema contains no double-quoted strings */ + renameTestSchema(pParse, zDb, iSchema==1, "", 0); + renameFixQuotes(pParse, zDb, iSchema==1); + /* Do the rename operation using a recursive UPDATE statement that ** uses the sqlite_rename_column() SQL function to compute the new ** CREATE statement text for the sqlite_schema table. @@ -107450,18 +113127,17 @@ SQLITE_PRIVATE void wx_sqlite3AlterRenameColumn( assert( pNew->n>0 ); bQuote = wx_sqlite3Isquote(pNew->z[0]); wx_sqlite3NestedParse(pParse, - "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) " "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' " - " AND (type != 'index' OR tbl_name = %Q)" - " AND sql NOT LIKE 'create virtual%%'", + " AND (type != 'index' OR tbl_name = %Q)", zDb, zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1, pTab->zName ); wx_sqlite3NestedParse(pParse, - "UPDATE temp." DFLT_SCHEMA_TABLE " SET " + "UPDATE temp." LEGACY_SCHEMA_TABLE " SET " "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, 1) " "WHERE type IN ('trigger', 'view')", zDb, pTab->zName, iCol, zNew, bQuote @@ -107469,7 +113145,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterRenameColumn( /* Drop and reload the database schema. */ renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename); - renameTestSchema(pParse, zDb, iSchema==1, "after rename", 0); + renameTestSchema(pParse, zDb, iSchema==1, "after rename", 1); exit_rename_column: wx_sqlite3SrcListDelete(db, pSrc); @@ -107496,7 +113172,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterRenameColumn( ** the parse tree. */ struct RenameToken { - void *p; /* Parse tree element created by token t */ + const void *p; /* Parse tree element created by token t */ Token t; /* The token that created parse tree element p */ RenameToken *pNext; /* Next is a list of all RenameToken objects */ }; @@ -107538,16 +113214,19 @@ struct RenameCtx { ** Technically, as x no longer points into a valid object or to the byte ** following a valid object, it may not be used in comparison operations. */ -static void renameTokenCheckAll(Parse *pParse, void *pPtr){ - if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){ - RenameToken *p; - u8 i = 0; +static void renameTokenCheckAll(Parse *pParse, const void *pPtr){ + assert( pParse==pParse->db->pParse ); + assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); + if( pParse->nErr==0 ){ + const RenameToken *p; + u32 i = 1; for(p=pParse->pRename; p; p=p->pNext){ if( p->p ){ assert( p->p!=pPtr ); - i += *(u8*)(p->p); + i += *(u8*)(p->p) | 1; } } + assert( i>0 ); } } #else @@ -107566,7 +113245,11 @@ static void renameTokenCheckAll(Parse *pParse, void *pPtr){ ** with tail recursion in tokenExpr() routine, for a small performance ** improvement. */ -SQLITE_PRIVATE void *wx_sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ +SQLITE_PRIVATE const void *wx_sqlite3RenameTokenMap( + Parse *pParse, + const void *pPtr, + const Token *pToken +){ RenameToken *pNew; assert( pPtr || pParse->db->mallocFailed ); renameTokenCheckAll(pParse, pPtr); @@ -107588,7 +113271,7 @@ SQLITE_PRIVATE void *wx_sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token * ** with parse tree element pFrom. This function remaps the associated token ** to parse tree element pTo. */ -SQLITE_PRIVATE void wx_sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){ +SQLITE_PRIVATE void wx_sqlite3RenameTokenRemap(Parse *pParse, const void *pTo, const void *pFrom){ RenameToken *p; renameTokenCheckAll(pParse, pTo); for(p=pParse->pRename; p; p=p->pNext){ @@ -107604,7 +113287,10 @@ SQLITE_PRIVATE void wx_sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *p */ static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){ Parse *pParse = pWalker->pParse; - wx_sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr); + wx_sqlite3RenameTokenRemap(pParse, 0, (const void*)pExpr); + if( ExprUseYTab(pExpr) ){ + wx_sqlite3RenameTokenRemap(pParse, 0, (const void*)&pExpr->y.pTab); + } return WRC_Continue; } @@ -107615,15 +113301,31 @@ static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){ static void renameWalkWith(Walker *pWalker, Select *pSelect){ With *pWith = pSelect->pWith; if( pWith ){ + Parse *pParse = pWalker->pParse; int i; + With *pCopy = 0; + assert( pWith->nCte>0 ); + if( (pWith->a[0].pSelect->selFlags & SF_Expanded)==0 ){ + /* Push a copy of the With object onto the with-stack. We use a copy + ** here as the original will be expanded and resolved (flags SF_Expanded + ** and SF_Resolved) below. And the parser code that uses the with-stack + ** fails if the Select objects on it have already been expanded and + ** resolved. */ + pCopy = wx_sqlite3WithDup(pParse->db, pWith); + pCopy = wx_sqlite3WithPush(pParse, pCopy, 1); + } for(i=0; inCte; i++){ Select *p = pWith->a[i].pSelect; NameContext sNC; memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pWalker->pParse; - wx_sqlite3SelectPrep(sNC.pParse, p, &sNC); + sNC.pParse = pParse; + if( pCopy ) wx_sqlite3SelectPrep(sNC.pParse, p, &sNC); + if( sNC.pParse->db->mallocFailed ) return; wx_sqlite3WalkSelect(pWalker, p); - wx_sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols); + wx_sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols); + } + if( pCopy && pParse->pWith==pCopy ){ + pParse->pWith = pCopy->pOuter; } } } @@ -107633,13 +113335,12 @@ static void renameWalkWith(Walker *pWalker, Select *pSelect){ */ static void unmapColumnIdlistNames( Parse *pParse, - IdList *pIdList + const IdList *pIdList ){ - if( pIdList ){ - int ii; - for(ii=0; iinId; ii++){ - wx_sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName); - } + int ii; + assert( pIdList!=0 ); + for(ii=0; iinId; ii++){ + wx_sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName); } } @@ -107650,11 +113351,15 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){ Parse *pParse = pWalker->pParse; int i; if( pParse->nErr ) return WRC_Abort; - if( NEVER(p->selFlags & SF_View) ) return WRC_Prune; + testcase( p->selFlags & SF_View ); + testcase( p->selFlags & SF_CopyCte ); + if( p->selFlags & (SF_View|SF_CopyCte) ){ + return WRC_Prune; + } if( ALWAYS(p->pEList) ){ ExprList *pList = p->pEList; for(i=0; inExpr; i++){ - if( pList->a[i].zEName && pList->a[i].eEName==ENAME_NAME ){ + if( pList->a[i].zEName && pList->a[i].fg.eEName==ENAME_NAME ){ wx_sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName); } } @@ -107663,8 +113368,11 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){ SrcList *pSrc = p->pSrc; for(i=0; inSrc; i++){ wx_sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName); - if( wx_sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort; - unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing); + if( pSrc->a[i].fg.isUsing==0 ){ + wx_sqlite3WalkExpr(pWalker, pSrc->a[i].u3.pOn); + }else{ + unmapColumnIdlistNames(pParse, pSrc->a[i].u3.pUsing); + } } } @@ -107700,7 +113408,7 @@ SQLITE_PRIVATE void wx_sqlite3RenameExprlistUnmap(Parse *pParse, ExprList *pELis sWalker.xExprCallback = renameUnmapExprCb; wx_sqlite3WalkExprList(&sWalker, pEList); for(i=0; inExpr; i++){ - if( ALWAYS(pEList->a[i].eEName==ENAME_NAME) ){ + if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) ){ wx_sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zEName); } } @@ -107731,10 +113439,12 @@ static void renameTokenFree(wx_sqlite3 *db, RenameToken *pToken){ static RenameToken *renameTokenFind( Parse *pParse, struct RenameCtx *pCtx, - void *pPtr + const void *pPtr ){ RenameToken **pp; - assert( pPtr!=0 ); + if( NEVER(pPtr==0) ){ + return 0; + } for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){ if( (*pp)->p==pPtr ){ RenameToken *pToken = *pp; @@ -107756,7 +113466,11 @@ static RenameToken *renameTokenFind( ** descend into sub-select statements. */ static int renameColumnSelectCb(Walker *pWalker, Select *p){ - if( p->selFlags & SF_View ) return WRC_Prune; + if( p->selFlags & (SF_View|SF_CopyCte) ){ + testcase( p->selFlags & SF_View ); + testcase( p->selFlags & SF_CopyCte ); + return WRC_Prune; + } renameWalkWith(pWalker, p); return WRC_Continue; } @@ -107779,6 +113493,7 @@ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ renameTokenFind(pWalker->pParse, p, (void*)pExpr); }else if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol + && ALWAYS(ExprUseYTab(pExpr)) && p->pTab==pExpr->y.pTab ){ renameTokenFind(pWalker->pParse, p, (void*)pExpr); @@ -107827,12 +113542,12 @@ static void renameColumnParseError( const char *zN = (const char*)wx_sqlite3_value_text(pObject); char *zErr; - zErr = wx_sqlite3_mprintf("error in %s %s%s%s: %s", + zErr = wx_sqlite3MPrintf(pParse->db, "error in %s %s%s%s: %s", zT, zN, (zWhen[0] ? " " : ""), zWhen, pParse->zErrMsg ); wx_sqlite3_result_error(pCtx, zErr, -1); - wx_sqlite3_free(zErr); + wx_sqlite3DbFree(pParse->db, zErr); } /* @@ -107844,18 +113559,18 @@ static void renameColumnParseError( static void renameColumnElistNames( Parse *pParse, RenameCtx *pCtx, - ExprList *pEList, + const ExprList *pEList, const char *zOld ){ if( pEList ){ int i; for(i=0; inExpr; i++){ - char *zName = pEList->a[i].zEName; - if( ALWAYS(pEList->a[i].eEName==ENAME_NAME) + const char *zName = pEList->a[i].zEName; + if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) && ALWAYS(zName!=0) && 0==wx_sqlite3_stricmp(zName, zOld) ){ - renameTokenFind(pParse, pCtx, (void*)zName); + renameTokenFind(pParse, pCtx, (const void*)zName); } } } @@ -107869,15 +113584,15 @@ static void renameColumnElistNames( static void renameColumnIdlistNames( Parse *pParse, RenameCtx *pCtx, - IdList *pIdList, + const IdList *pIdList, const char *zOld ){ if( pIdList ){ int i; for(i=0; inId; i++){ - char *zName = pIdList->a[i].zName; + const char *zName = pIdList->a[i].zName; if( 0==wx_sqlite3_stricmp(zName, zOld) ){ - renameTokenFind(pParse, pCtx, (void*)zName); + renameTokenFind(pParse, pCtx, (const void*)zName); } } } @@ -107893,32 +113608,25 @@ static int renameParseSql( const char *zDb, /* Name of schema SQL belongs to */ wx_sqlite3 *db, /* Database handle */ const char *zSql, /* SQL to parse */ - int bTemp, /* True if SQL is from temp schema */ - const char *zDropColumn /* Name of column being dropped */ + int bTemp /* True if SQL is from temp schema */ ){ int rc; - char *zErr = 0; - db->init.iDb = bTemp ? 1 : wx_sqlite3FindDbName(db, zDb); - if( zDropColumn ){ - db->init.bDropColumn = 1; - db->init.azInit = (char**)&zDropColumn; + wx_sqlite3ParseObjectInit(p, db); + if( zSql==0 ){ + return SQLITE_NOMEM; } - - /* Parse the SQL statement passed as the first argument. If no error - ** occurs and the parse does not result in a new table, index or - ** trigger object, the database must be corrupt. */ - memset(p, 0, sizeof(Parse)); + if( wx_sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){ + return SQLITE_CORRUPT_BKPT; + } + db->init.iDb = bTemp ? 1 : wx_sqlite3FindDbName(db, zDb); p->eParseMode = PARSE_MODE_RENAME; p->db = db; p->nQueryLoop = 1; - rc = zSql ? wx_sqlite3RunParser(p, zSql, &zErr) : SQLITE_NOMEM; - assert( p->zErrMsg==0 ); - assert( rc!=SQLITE_OK || zErr==0 ); - p->zErrMsg = zErr; + rc = wx_sqlite3RunParser(p, zSql); if( db->mallocFailed ) rc = SQLITE_NOMEM; if( rc==SQLITE_OK - && p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0 + && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0) ){ rc = SQLITE_CORRUPT_BKPT; } @@ -107936,7 +113644,6 @@ static int renameParseSql( #endif db->init.iDb = 0; - db->init.bDropColumn = 0; return rc; } @@ -107956,51 +113663,76 @@ static int renameEditSql( const char *zNew, /* New token text */ int bQuote /* True to always quote token */ ){ - int nNew = wx_sqlite3Strlen30(zNew); - int nSql = wx_sqlite3Strlen30(zSql); + i64 nNew = wx_sqlite3Strlen30(zNew); + i64 nSql = wx_sqlite3Strlen30(zSql); wx_sqlite3 *db = wx_sqlite3_context_db_handle(pCtx); int rc = SQLITE_OK; - char *zQuot; + char *zQuot = 0; char *zOut; - int nQuot; - - /* Set zQuot to point to a buffer containing a quoted copy of the - ** identifier zNew. If the corresponding identifier in the original - ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to - ** point to zQuot so that all substitutions are made using the - ** quoted version of the new column name. */ - zQuot = wx_sqlite3MPrintf(db, "\"%w\"", zNew); - if( zQuot==0 ){ - return SQLITE_NOMEM; + i64 nQuot = 0; + char *zBuf1 = 0; + char *zBuf2 = 0; + + if( zNew ){ + /* Set zQuot to point to a buffer containing a quoted copy of the + ** identifier zNew. If the corresponding identifier in the original + ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to + ** point to zQuot so that all substitutions are made using the + ** quoted version of the new column name. */ + zQuot = wx_sqlite3MPrintf(db, "\"%w\" ", zNew); + if( zQuot==0 ){ + return SQLITE_NOMEM; + }else{ + nQuot = wx_sqlite3Strlen30(zQuot)-1; + } + + assert( nQuot>=nNew ); + zOut = wx_sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); }else{ - nQuot = wx_sqlite3Strlen30(zQuot); - } - if( bQuote ){ - zNew = zQuot; - nNew = nQuot; + zOut = (char*)wx_sqlite3DbMallocZero(db, (nSql*2+1) * 3); + if( zOut ){ + zBuf1 = &zOut[nSql*2+1]; + zBuf2 = &zOut[nSql*4+2]; + } } /* At this point pRename->pList contains a list of RenameToken objects ** corresponding to all tokens in the input SQL that must be replaced - ** with the new column name. All that remains is to construct and - ** return the edited SQL string. */ - assert( nQuot>=nNew ); - zOut = wx_sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); + ** with the new column name, or with single-quoted versions of themselves. + ** All that remains is to construct and return the edited SQL string. */ if( zOut ){ int nOut = nSql; memcpy(zOut, zSql, nSql); while( pRename->pList ){ int iOff; /* Offset of token to replace in zOut */ - RenameToken *pBest = renameColumnTokenNext(pRename); - u32 nReplace; const char *zReplace; - if( wx_sqlite3IsIdChar(*pBest->t.z) ){ - nReplace = nNew; - zReplace = zNew; + RenameToken *pBest = renameColumnTokenNext(pRename); + + if( zNew ){ + if( bQuote==0 && wx_sqlite3IsIdChar(*pBest->t.z) ){ + nReplace = nNew; + zReplace = zNew; + }else{ + nReplace = nQuot; + zReplace = zQuot; + if( pBest->t.z[pBest->t.n]=='"' ) nReplace++; + } }else{ - nReplace = nQuot; - zReplace = zQuot; + /* Dequote the double-quoted token. Then requote it again, this time + ** using single quotes. If the character immediately following the + ** original token within the input SQL was a single quote ('), then + ** add another space after the new, single-quoted version of the + ** token. This is so that (SELECT "string"'alias') maps to + ** (SELECT 'string' 'alias'), and not (SELECT 'string''alias'). */ + memcpy(zBuf1, pBest->t.z, pBest->t.n); + zBuf1[pBest->t.n] = 0; + wx_sqlite3Dequote(zBuf1); + wx_sqlite3_snprintf(nSql*2, zBuf2, "%Q%s", zBuf1, + pBest->t.z[pBest->t.n]=='\'' ? " " : "" + ); + zReplace = zBuf2; + nReplace = wx_sqlite3Strlen30(zReplace); } iOff = pBest->t.z - zSql; @@ -108064,26 +113796,35 @@ static int renameResolveTrigger(Parse *pParse){ if( rc==SQLITE_OK && pStep->zTarget ){ SrcList *pSrc = wx_sqlite3TriggerStepSrc(pParse, pStep); if( pSrc ){ - int i; - for(i=0; inSrc && rc==SQLITE_OK; i++){ - SrcItem *p = &pSrc->a[i]; - p->iCursor = pParse->nTab++; - if( p->pSelect ){ - wx_sqlite3SelectPrep(pParse, p->pSelect, 0); - wx_sqlite3ExpandSubquery(pParse, p); - assert( i>0 ); - assert( pStep->pFrom->a[i-1].pSelect ); - wx_sqlite3SelectPrep(pParse, pStep->pFrom->a[i-1].pSelect, 0); - }else{ - p->pTab = wx_sqlite3LocateTableItem(pParse, 0, p); - if( p->pTab==0 ){ - rc = SQLITE_ERROR; - }else{ - p->pTab->nTabRef++; - rc = wx_sqlite3ViewGetColumnNames(pParse, p->pTab); + Select *pSel = wx_sqlite3SelectNew( + pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0 + ); + if( pSel==0 ){ + pStep->pExprList = 0; + pSrc = 0; + rc = SQLITE_NOMEM; + }else{ + wx_sqlite3SelectPrep(pParse, pSel, 0); + rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK; + assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList ); + assert( pSrc==pSel->pSrc ); + if( pStep->pExprList ) pSel->pEList = 0; + pSel->pSrc = 0; + wx_sqlite3SelectDelete(db, pSel); + } + if( pStep->pFrom ){ + int i; + for(i=0; ipFrom->nSrc && rc==SQLITE_OK; i++){ + SrcItem *p = &pStep->pFrom->a[i]; + if( p->pSelect ){ + wx_sqlite3SelectPrep(pParse, p->pSelect, 0); } } } + + if( db->mallocFailed ){ + rc = SQLITE_NOMEM; + } sNC.pSrcList = pSrc; if( rc==SQLITE_OK && pStep->pWhere ){ rc = wx_sqlite3ResolveExprNames(&sNC, pStep->pWhere); @@ -108169,13 +113910,13 @@ static void renameParseCleanup(Parse *pParse){ wx_sqlite3DeleteTrigger(db, pParse->pNewTrigger); wx_sqlite3DbFree(db, pParse->zErrMsg); renameTokenFree(db, pParse->pRename); - wx_sqlite3ParserReset(pParse); + wx_sqlite3ParseObjectReset(pParse); } /* ** SQL function: ** -** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld) +** sqlite_rename_column(SQL,TYPE,OBJ,DB,TABLE,COL,NEWNAME,QUOTE,TEMP) ** ** 0. zSql: SQL statement to rewrite ** 1. type: Type of object ("table", "view" etc.) @@ -108193,7 +113934,8 @@ static void renameParseCleanup(Parse *pParse){ ** ** This function is used internally by the ALTER TABLE RENAME COLUMN command. ** It is only accessible to SQL created using wx_sqlite3NestedParse(). It is -** not reachable from ordinary SQL passed into wx_sqlite3_prepare(). +** not reachable from ordinary SQL passed into wx_sqlite3_prepare() unless the +** SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test setting is enabled. */ static void renameColumnFunc( wx_sqlite3_context *context, @@ -108231,14 +113973,14 @@ static void renameColumnFunc( wx_sqlite3BtreeLeaveAll(db); return; } - zOld = pTab->aCol[iCol].zName; + zOld = pTab->aCol[iCol].zCnName; memset(&sCtx, 0, sizeof(sCtx)); sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); #ifndef SQLITE_OMIT_AUTHORIZATION db->xAuth = 0; #endif - rc = renameParseSql(&sParse, zDb, db, zSql, bTemp, 0); + rc = renameParseSql(&sParse, zDb, db, zSql, bTemp); /* Find tokens that need to be replaced. */ memset(&sWalker, 0, sizeof(Walker)); @@ -108250,8 +113992,8 @@ static void renameColumnFunc( sCtx.pTab = pTab; if( rc!=SQLITE_OK ) goto renameColumnFunc_done; if( sParse.pNewTable ){ - Select *pSelect = sParse.pNewTable->pSelect; - if( pSelect ){ + if( IsView(sParse.pNewTable) ){ + Select *pSelect = sParse.pNewTable->u.view.pSelect; pSelect->selFlags &= ~SF_View; sParse.rc = SQLITE_OK; wx_sqlite3SelectPrep(&sParse, pSelect, 0); @@ -108260,16 +114002,17 @@ static void renameColumnFunc( wx_sqlite3WalkSelect(&sWalker, pSelect); } if( rc!=SQLITE_OK ) goto renameColumnFunc_done; - }else{ + }else if( IsOrdinaryTable(sParse.pNewTable) ){ /* A regular table */ int bFKOnly = wx_sqlite3_stricmp(zTable, sParse.pNewTable->zName); FKey *pFKey; - assert( sParse.pNewTable->pSelect==0 ); sCtx.pTab = sParse.pNewTable; if( bFKOnly==0 ){ - renameTokenFind( - &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zName - ); + if( iColnCol ){ + renameTokenFind( + &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zCnName + ); + } if( sCtx.iCol<0 ){ renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey); } @@ -108282,12 +114025,15 @@ static void renameColumnFunc( } #ifndef SQLITE_OMIT_GENERATED_COLUMNS for(i=0; inCol; i++){ - wx_sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt); + Expr *pExpr = wx_sqlite3ColumnExpr(sParse.pNewTable, + &sParse.pNewTable->aCol[i]); + wx_sqlite3WalkExpr(&sWalker, pExpr); } #endif } - for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + assert( IsOrdinaryTable(sParse.pNewTable) ); + for(pFKey=sParse.pNewTable->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ for(i=0; inCol; i++){ if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){ renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]); @@ -108338,7 +114084,9 @@ static void renameColumnFunc( renameColumnFunc_done: if( rc!=SQLITE_OK ){ - if( sParse.zErrMsg ){ + if( rc==SQLITE_ERROR && wx_sqlite3WritableSchema(db) ){ + wx_sqlite3_result_value(context, argv[0]); + }else if( sParse.zErrMsg ){ renameColumnParseError(context, "", argv[1], argv[2], &sParse); }else{ wx_sqlite3_result_error_code(context, rc); @@ -108358,7 +114106,10 @@ renameColumnFunc_done: */ static int renameTableExprCb(Walker *pWalker, Expr *pExpr){ RenameCtx *p = pWalker->u.pRename; - if( pExpr->op==TK_COLUMN && p->pTab==pExpr->y.pTab ){ + if( pExpr->op==TK_COLUMN + && ALWAYS(ExprUseYTab(pExpr)) + && p->pTab==pExpr->y.pTab + ){ renameTokenFind(pWalker->pParse, p, (void*)&pExpr->y.pTab); } return WRC_Continue; @@ -108371,8 +114122,12 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ int i; RenameCtx *p = pWalker->u.pRename; SrcList *pSrc = pSelect->pSrc; - if( pSelect->selFlags & SF_View ) return WRC_Prune; - if( pSrc==0 ){ + if( pSelect->selFlags & (SF_View|SF_CopyCte) ){ + testcase( pSelect->selFlags & SF_View ); + testcase( pSelect->selFlags & SF_CopyCte ); + return WRC_Prune; + } + if( NEVER(pSrc==0) ){ assert( pWalker->pParse->db->mallocFailed ); return WRC_Abort; } @@ -108442,35 +114197,38 @@ static void renameTableFunc( sWalker.xSelectCallback = renameTableSelectCb; sWalker.u.pRename = &sCtx; - rc = renameParseSql(&sParse, zDb, db, zInput, bTemp, 0); + rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); if( rc==SQLITE_OK ){ int isLegacy = (db->flags & SQLITE_LegacyAlter); if( sParse.pNewTable ){ Table *pTab = sParse.pNewTable; - if( pTab->pSelect ){ + if( IsView(pTab) ){ if( isLegacy==0 ){ - Select *pSelect = pTab->pSelect; + Select *pSelect = pTab->u.view.pSelect; NameContext sNC; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = &sParse; assert( pSelect->selFlags & SF_View ); pSelect->selFlags &= ~SF_View; - wx_sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC); + wx_sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC); if( sParse.nErr ){ rc = sParse.rc; }else{ - wx_sqlite3WalkSelect(&sWalker, pTab->pSelect); + wx_sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect); } } }else{ /* Modify any FK definitions to point to the new table. */ #ifndef SQLITE_OMIT_FOREIGN_KEY - if( isLegacy==0 || (db->flags & SQLITE_ForeignKeys) ){ + if( (isLegacy==0 || (db->flags & SQLITE_ForeignKeys)) + && !IsVirtual(pTab) + ){ FKey *pFKey; - for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + assert( IsOrdinaryTable(pTab) ); + for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ if( wx_sqlite3_stricmp(pFKey->zTo, zOld)==0 ){ renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo); } @@ -108516,6 +114274,15 @@ static void renameTableFunc( if( pStep->zTarget && 0==wx_sqlite3_stricmp(pStep->zTarget, zOld) ){ renameTokenFind(&sParse, &sCtx, pStep->zTarget); } + if( pStep->pFrom ){ + int i; + for(i=0; ipFrom->nSrc; i++){ + SrcItem *pItem = &pStep->pFrom->a[i]; + if( 0==wx_sqlite3_stricmp(pItem->zName, zOld) ){ + renameTokenFind(&sParse, &sCtx, pItem->zName); + } + } + } } } } @@ -108527,7 +114294,9 @@ static void renameTableFunc( rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote); } if( rc!=SQLITE_OK ){ - if( sParse.zErrMsg ){ + if( rc==SQLITE_ERROR && wx_sqlite3WritableSchema(db) ){ + wx_sqlite3_result_value(context, argv[3]); + }else if( sParse.zErrMsg ){ renameColumnParseError(context, "", argv[1], argv[2], &sParse); }else{ wx_sqlite3_result_error_code(context, rc); @@ -108545,7 +114314,131 @@ static void renameTableFunc( return; } -/* +static int renameQuotefixExprCb(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_STRING && (pExpr->flags & EP_DblQuoted) ){ + renameTokenFind(pWalker->pParse, pWalker->u.pRename, (const void*)pExpr); + } + return WRC_Continue; +} + +/* SQL function: sqlite_rename_quotefix(DB,SQL) +** +** Rewrite the DDL statement "SQL" so that any string literals that use +** double-quotes use single quotes instead. +** +** Two arguments must be passed: +** +** 0: Database name ("main", "temp" etc.). +** 1: SQL statement to edit. +** +** The returned value is the modified SQL statement. For example, given +** the database schema: +** +** CREATE TABLE t1(a, b, c); +** +** SELECT sqlite_rename_quotefix('main', +** 'CREATE VIEW v1 AS SELECT "a", "string" FROM t1' +** ); +** +** returns the string: +** +** CREATE VIEW v1 AS SELECT "a", 'string' FROM t1 +** +** If there is a error in the input SQL, then raise an error, except +** if PRAGMA writable_schema=ON, then just return the input string +** unmodified following an error. +*/ +static void renameQuotefixFunc( + wx_sqlite3_context *context, + int NotUsed, + wx_sqlite3_value **argv +){ + wx_sqlite3 *db = wx_sqlite3_context_db_handle(context); + char const *zDb = (const char*)wx_sqlite3_value_text(argv[0]); + char const *zInput = (const char*)wx_sqlite3_value_text(argv[1]); + +#ifndef SQLITE_OMIT_AUTHORIZATION + wx_sqlite3_xauth xAuth = db->xAuth; + db->xAuth = 0; +#endif + + wx_sqlite3BtreeEnterAll(db); + + UNUSED_PARAMETER(NotUsed); + if( zDb && zInput ){ + int rc; + Parse sParse; + rc = renameParseSql(&sParse, zDb, db, zInput, 0); + + if( rc==SQLITE_OK ){ + RenameCtx sCtx; + Walker sWalker; + + /* Walker to find tokens that need to be replaced. */ + memset(&sCtx, 0, sizeof(RenameCtx)); + memset(&sWalker, 0, sizeof(Walker)); + sWalker.pParse = &sParse; + sWalker.xExprCallback = renameQuotefixExprCb; + sWalker.xSelectCallback = renameColumnSelectCb; + sWalker.u.pRename = &sCtx; + + if( sParse.pNewTable ){ + if( IsView(sParse.pNewTable) ){ + Select *pSelect = sParse.pNewTable->u.view.pSelect; + pSelect->selFlags &= ~SF_View; + sParse.rc = SQLITE_OK; + wx_sqlite3SelectPrep(&sParse, pSelect, 0); + rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); + if( rc==SQLITE_OK ){ + wx_sqlite3WalkSelect(&sWalker, pSelect); + } + }else{ + int i; + wx_sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck); +#ifndef SQLITE_OMIT_GENERATED_COLUMNS + for(i=0; inCol; i++){ + wx_sqlite3WalkExpr(&sWalker, + wx_sqlite3ColumnExpr(sParse.pNewTable, + &sParse.pNewTable->aCol[i])); + } +#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ + } + }else if( sParse.pNewIndex ){ + wx_sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); + wx_sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); + }else{ +#ifndef SQLITE_OMIT_TRIGGER + rc = renameResolveTrigger(&sParse); + if( rc==SQLITE_OK ){ + renameWalkTrigger(&sWalker, sParse.pNewTrigger); + } +#endif /* SQLITE_OMIT_TRIGGER */ + } + + if( rc==SQLITE_OK ){ + rc = renameEditSql(context, &sCtx, zInput, 0, 0); + } + renameTokenFree(db, sCtx.pList); + } + if( rc!=SQLITE_OK ){ + if( wx_sqlite3WritableSchema(db) && rc==SQLITE_ERROR ){ + wx_sqlite3_result_value(context, argv[1]); + }else{ + wx_sqlite3_result_error_code(context, rc); + } + } + renameParseCleanup(&sParse); + } + +#ifndef SQLITE_OMIT_AUTHORIZATION + db->xAuth = xAuth; +#endif + + wx_sqlite3BtreeLeaveAll(db); +} + +/* Function: sqlite_rename_test(DB,SQL,TYPE,NAME,ISTEMP,WHEN,DQS) +** ** An SQL user function that checks that there are no parse or symbol ** resolution problems in a CREATE TRIGGER|TABLE|VIEW|INDEX statement. ** After an ALTER TABLE .. RENAME operation is performed and the schema @@ -108558,13 +114451,15 @@ static void renameTableFunc( ** 3: Object name. ** 4: True if object is from temp schema. ** 5: "when" part of error message. -** 6: Name of column being dropped, or NULL. +** 6: True to disable the DQS quirk when parsing SQL. ** -** Unless it finds an error, this function normally returns NULL. However, it -** returns integer value 1 if: +** The return value is computed as follows: ** -** * the SQL argument creates a trigger, and -** * the table that the trigger is attached to is in database zDb. +** A. If an error is seen and not in PRAGMA writable_schema=ON mode, +** then raise the error. +** B. Else if a trigger is created and the the table that the trigger is +** attached to is in database zDb, then return 1. +** C. Otherwise return NULL. */ static void renameTableTest( wx_sqlite3_context *context, @@ -108577,7 +114472,7 @@ static void renameTableTest( int bTemp = wx_sqlite3_value_int(argv[4]); int isLegacy = (db->flags & SQLITE_LegacyAlter); char const *zWhen = (const char*)wx_sqlite3_value_text(argv[5]); - char const *zDropColumn = (const char*)wx_sqlite3_value_text(argv[6]); + int bNoDQS = wx_sqlite3_value_int(argv[6]); #ifndef SQLITE_OMIT_AUTHORIZATION wx_sqlite3_xauth xAuth = db->xAuth; @@ -108585,16 +114480,20 @@ static void renameTableTest( #endif UNUSED_PARAMETER(NotUsed); + if( zDb && zInput ){ int rc; Parse sParse; - rc = renameParseSql(&sParse, zDb, db, zInput, bTemp, zDropColumn); + int flags = db->flags; + if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); + rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); + db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL)); if( rc==SQLITE_OK ){ - if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){ + if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){ NameContext sNC; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = &sParse; - wx_sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC); + wx_sqlite3SelectPrep(&sParse, sParse.pNewTable->u.view.pSelect, &sNC); if( sParse.nErr ) rc = sParse.rc; } @@ -108605,12 +114504,16 @@ static void renameTableTest( if( rc==SQLITE_OK ){ int i1 = wx_sqlite3SchemaToIndex(db, sParse.pNewTrigger->pTabSchema); int i2 = wx_sqlite3FindDbName(db, zDb); - if( i1==i2 ) wx_sqlite3_result_int(context, 1); + if( i1==i2 ){ + /* Handle output case B */ + wx_sqlite3_result_int(context, 1); + } } } } - if( rc!=SQLITE_OK && zWhen ){ + if( rc!=SQLITE_OK && zWhen && !wx_sqlite3WritableSchema(db) ){ + /* Output case A */ renameColumnParseError(context, zWhen, argv[2], argv[3],&sParse); } renameParseCleanup(&sParse); @@ -108656,7 +114559,7 @@ static void dropColumnFunc( #endif UNUSED_PARAMETER(NotUsed); - rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1, 0); + rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1); if( rc!=SQLITE_OK ) goto drop_column_done; pTab = sParse.pNewTable; if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){ @@ -108665,13 +114568,14 @@ static void dropColumnFunc( goto drop_column_done; } - pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zName); + pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName); if( iColnCol-1 ){ RenameToken *pEnd; - pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zName); + pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zCnName); zEnd = (const char*)pEnd->t.z; }else{ - zEnd = (const char*)&zSql[pTab->addColOffset]; + assert( IsOrdinaryTable(pTab) ); + zEnd = (const char*)&zSql[pTab->u.tab.addColOffset]; while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--; } @@ -108697,7 +114601,7 @@ drop_column_done: ** statement. Argument pSrc contains the possibly qualified name of the ** table being edited, and token pName the name of the column to drop. */ -SQLITE_PRIVATE void wx_sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, Token *pName){ +SQLITE_PRIVATE void wx_sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const Token *pName){ wx_sqlite3 *db = pParse->db; /* Database handle */ Table *pTab; /* Table to modify */ int iDb; /* Index of db containing pTab in aDb[] */ @@ -108725,7 +114629,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, Toke } iCol = wx_sqlite3ColumnIndex(pTab, zCol); if( iCol<0 ){ - wx_sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zCol); + wx_sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pName); goto exit_drop_column; } @@ -108749,9 +114653,16 @@ SQLITE_PRIVATE void wx_sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, Toke iDb = wx_sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); zDb = db->aDb[iDb].zDbSName; +#ifndef SQLITE_OMIT_AUTHORIZATION + /* Invoke the authorization callback. */ + if( wx_sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){ + goto exit_drop_column; + } +#endif renameTestSchema(pParse, zDb, iDb==1, "", 0); + renameFixQuotes(pParse, zDb, iDb==1); wx_sqlite3NestedParse(pParse, - "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " "sql = sqlite_drop_column(%d, sql, %d) " "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)" , zDb, iDb, iCol, pTab->zName @@ -108759,7 +114670,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, Toke /* Drop and reload the database schema. */ renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop); - renameTestSchema(pParse, zDb, iDb==1, "after drop column", zCol); + renameTestSchema(pParse, zDb, iDb==1, "after drop column", 1); /* Edit rows of table on disk */ if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){ @@ -108775,33 +114686,50 @@ SQLITE_PRIVATE void wx_sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, Toke wx_sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); addr = wx_sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); reg = ++pParse->nMem; - pParse->nMem += pTab->nCol; if( HasRowid(pTab) ){ wx_sqlite3VdbeAddOp2(v, OP_Rowid, iCur, reg); + pParse->nMem += pTab->nCol; }else{ pPk = wx_sqlite3PrimaryKeyIndex(pTab); + pParse->nMem += pPk->nColumn; + for(i=0; inKeyCol; i++){ + wx_sqlite3VdbeAddOp3(v, OP_Column, iCur, i, reg+i+1); + } + nField = pPk->nKeyCol; } + regRec = ++pParse->nMem; for(i=0; inCol; i++){ if( i!=iCol && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ int regOut; if( pPk ){ int iPos = wx_sqlite3TableColumnToIndex(pPk, i); int iColPos = wx_sqlite3TableColumnToIndex(pPk, iCol); + if( iPosnKeyCol ) continue; regOut = reg+1+iPos-(iPos>iColPos); }else{ regOut = reg+1+nField; } - wx_sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut); + if( i==pTab->iPKey ){ + wx_sqlite3VdbeAddOp2(v, OP_Null, 0, regOut); + }else{ + wx_sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut); + } nField++; } } - regRec = reg + pTab->nCol; + if( nField==0 ){ + /* dbsqlfuzz 5f09e7bcc78b4954d06bf9f2400d7715f48d1fef */ + pParse->nMem++; + wx_sqlite3VdbeAddOp2(v, OP_Null, 0, reg+1); + nField = 1; + } wx_sqlite3VdbeAddOp3(v, OP_MakeRecord, reg+1, nField, regRec); if( pPk ){ wx_sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iCur, regRec, reg+1, pPk->nKeyCol); }else{ wx_sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, reg); } + wx_sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION); wx_sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v); wx_sqlite3VdbeJumpHere(v, addr); @@ -108821,6 +114749,7 @@ SQLITE_PRIVATE void wx_sqlite3AlterFunctions(void){ INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest), INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc), + INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc), }; wx_sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); } @@ -109263,7 +115192,6 @@ static void statInit( + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample); } #endif - db = wx_sqlite3_context_db_handle(context); p = wx_sqlite3DbMallocZero(db, n); if( p==0 ){ wx_sqlite3_result_error_nomem(context); @@ -109678,32 +115606,29 @@ static void statGet( ** * "WHERE a=? AND b=?" matches 2 rows. ** ** If D is the count of distinct values and K is the total number of - ** rows, then each estimate is computed as: + ** rows, then each estimate is usually computed as: ** ** I = (K+D-1)/D + ** + ** In other words, I is K/D rounded up to the next whole integer. + ** However, if I is between 1.0 and 1.1 (in other words if I is + ** close to 1.0 but just a little larger) then do not round up but + ** instead keep the I value at 1.0. */ - char *z; - int i; - - char *zRet = wx_sqlite3MallocZero( (p->nKeyCol+1)*25 ); - if( zRet==0 ){ - wx_sqlite3_result_error_nomem(context); - return; - } + wx_sqlite3_str sStat; /* Text of the constructed "stat" line */ + int i; /* Loop counter */ - wx_sqlite3_snprintf(24, zRet, "%llu", + wx_sqlite3StrAccumInit(&sStat, 0, 0, 0, (p->nKeyCol+1)*100); + wx_sqlite3_str_appendf(&sStat, "%llu", p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow); - z = zRet + wx_sqlite3Strlen30(zRet); for(i=0; inKeyCol; i++){ u64 nDistinct = p->current.anDLt[i] + 1; u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; - wx_sqlite3_snprintf(24, z, " %llu", iVal); - z += wx_sqlite3Strlen30(z); + if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1; + wx_sqlite3_str_appendf(&sStat, " %llu", iVal); assert( p->current.anEq[i] ); } - assert( z[0]=='\0' && z>zRet ); - - wx_sqlite3_result_text(context, zRet, -1, wx_sqlite3_free); + wx_sqlite3ResultStrAccum(context, &sStat); } #ifdef SQLITE_ENABLE_STAT4 else if( eCall==STAT_GET_ROWID ){ @@ -109722,6 +115647,8 @@ static void statGet( } }else{ tRowcnt *aCnt = 0; + wx_sqlite3_str sStat; + int i; assert( p->iGetnSample ); switch( eCall ){ @@ -109733,23 +115660,12 @@ static void statGet( break; } } - - { - char *zRet = wx_sqlite3MallocZero(p->nCol * 25); - if( zRet==0 ){ - wx_sqlite3_result_error_nomem(context); - }else{ - int i; - char *z = zRet; - for(i=0; inCol; i++){ - wx_sqlite3_snprintf(24, z, "%llu ", (u64)aCnt[i]); - z += wx_sqlite3Strlen30(z); - } - assert( z[0]=='\0' && z>zRet ); - z[-1] = '\0'; - wx_sqlite3_result_text(context, zRet, -1, wx_sqlite3_free); - } + wx_sqlite3StrAccumInit(&sStat, 0, 0, 0, p->nCol*100); + for(i=0; inCol; i++){ + wx_sqlite3_str_appendf(&sStat, "%llu ", (u64)aCnt[i]); } + if( sStat.nChar ) sStat.nChar--; + wx_sqlite3ResultStrAccum(context, &sStat); } #endif /* SQLITE_ENABLE_STAT4 */ #ifndef SQLITE_DEBUG @@ -109796,9 +115712,10 @@ static void analyzeVdbeCommentIndexWithColumnName( if( NEVER(i==XN_ROWID) ){ VdbeComment((v,"%s.rowid",pIdx->zName)); }else if( i==XN_EXPR ){ + assert( pIdx->bHasExpr ); VdbeComment((v,"%s.expr(%d)",pIdx->zName, k)); }else{ - VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zName)); + VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zCnName)); } } #else @@ -109845,7 +115762,7 @@ static void analyzeOneTable( if( v==0 || NEVER(pTab==0) ){ return; } - if( pTab->tnum==0 ){ + if( !IsOrdinaryTable(pTab) ){ /* Do not gather statistics on views or virtual tables */ return; } @@ -109872,7 +115789,7 @@ static void analyzeOneTable( memcpy(pStat1->zName, "sqlite_stat1", 13); pStat1->nCol = 3; pStat1->iPKey = -1; - wx_sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNBLOB); + wx_sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNAMIC); } #endif @@ -110439,6 +116356,8 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ ** and its contents. */ SQLITE_PRIVATE void wx_sqlite3DeleteIndexSamples(wx_sqlite3 *db, Index *pIdx){ + assert( db!=0 ); + assert( pIdx!=0 ); #ifdef SQLITE_ENABLE_STAT4 if( pIdx->aSample ){ int j; @@ -110448,7 +116367,7 @@ SQLITE_PRIVATE void wx_sqlite3DeleteIndexSamples(wx_sqlite3 *db, Index *pIdx){ } wx_sqlite3DbFree(db, pIdx->aSample); } - if( db && db->pnBytesFreed==0 ){ + if( db->pnBytesFreed==0 ){ pIdx->nSample = 0; pIdx->aSample = 0; } @@ -110670,9 +116589,12 @@ static int loadStatTbl( */ static int loadStat4(wx_sqlite3 *db, const char *zDb){ int rc = SQLITE_OK; /* Result codes from subroutines */ + const Table *pStat4; assert( db->lookaside.bDisable ); - if( wx_sqlite3FindTable(db, "sqlite_stat4", zDb) ){ + if( (pStat4 = wx_sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 + && IsOrdinaryTable(pStat4) + ){ rc = loadStatTbl(db, "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx", "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", @@ -110709,6 +116631,7 @@ SQLITE_PRIVATE int wx_sqlite3AnalysisLoad(wx_sqlite3 *db, int iDb){ char *zSql; int rc = SQLITE_OK; Schema *pSchema = db->aDb[iDb].pSchema; + const Table *pStat1; assert( iDb>=0 && iDbnDb ); assert( db->aDb[iDb].pBt!=0 ); @@ -110731,7 +116654,9 @@ SQLITE_PRIVATE int wx_sqlite3AnalysisLoad(wx_sqlite3 *db, int iDb){ /* Load new statistics out of the sqlite_stat1 table */ sInfo.db = db; sInfo.zDatabase = db->aDb[iDb].zDbSName; - if( wx_sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){ + if( (pStat1 = wx_sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)) + && IsOrdinaryTable(pStat1) + ){ zSql = wx_sqlite3MPrintf(db, "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase); if( zSql==0 ){ @@ -110861,7 +116786,7 @@ static void attachFunc( char *zErr = 0; unsigned int flags; Db *aNew; /* New array of Db pointers */ - Db *pNew; /* Db object for the newly attached database */ + Db *pNew = 0; /* Db object for the newly attached database */ char *zErrDyn = 0; wx_sqlite3_vfs *pVfs; @@ -110871,7 +116796,7 @@ static void attachFunc( if( zFile==0 ) zFile = ""; if( zName==0 ) zName = ""; -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE # define REOPEN_AS_MEMDB(db) (db->init.reopenMemdb) #else # define REOPEN_AS_MEMDB(db) (0) @@ -110881,13 +116806,26 @@ static void attachFunc( /* This is not a real ATTACH. Instead, this routine is being called ** from wx_sqlite3_deserialize() to close database db->init.iDb and ** reopen it as a MemDB */ + Btree *pNewBt = 0; pVfs = wx_sqlite3_vfs_find("memdb"); if( pVfs==0 ) return; - pNew = &db->aDb[db->init.iDb]; - if( pNew->pBt ) wx_sqlite3BtreeClose(pNew->pBt); - pNew->pBt = 0; - pNew->pSchema = 0; - rc = wx_sqlite3BtreeOpen(pVfs, "x\0", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB); + rc = wx_sqlite3BtreeOpen(pVfs, "x\0", db, &pNewBt, 0, SQLITE_OPEN_MAIN_DB); + if( rc==SQLITE_OK ){ + Schema *pNewSchema = wx_sqlite3SchemaGet(db, pNewBt); + if( pNewSchema ){ + /* Both the Btree and the new Schema were allocated successfully. + ** Close the old db and update the aDb[] slot with the new memdb + ** values. */ + pNew = &db->aDb[db->init.iDb]; + if( ALWAYS(pNew->pBt) ) wx_sqlite3BtreeClose(pNew->pBt); + pNew->pBt = pNewBt; + pNew->pSchema = pNewSchema; + }else{ + wx_sqlite3BtreeClose(pNewBt); + rc = SQLITE_NOMEM; + } + } + if( rc ) goto attach_error; }else{ /* This is a real ATTACH ** @@ -111005,7 +116943,7 @@ static void attachFunc( } #endif if( rc ){ - if( !REOPEN_AS_MEMDB(db) ){ + if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){ int iDb = db->nDb - 1; assert( iDb>=2 ); if( db->aDb[iDb].pBt ){ @@ -111122,22 +117060,25 @@ static void codeAttach( wx_sqlite3* db = pParse->db; int regArgs; + if( SQLITE_OK!=wx_sqlite3ReadSchema(pParse) ) goto attach_end; + if( pParse->nErr ) goto attach_end; memset(&sName, 0, sizeof(NameContext)); sName.pParse = pParse; if( - SQLITE_OK!=(rc = resolveAttachExpr(&sName, pFilename)) || - SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) || - SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey)) + SQLITE_OK!=resolveAttachExpr(&sName, pFilename) || + SQLITE_OK!=resolveAttachExpr(&sName, pDbname) || + SQLITE_OK!=resolveAttachExpr(&sName, pKey) ){ goto attach_end; } #ifndef SQLITE_OMIT_AUTHORIZATION - if( pAuthArg ){ + if( ALWAYS(pAuthArg) ){ char *zAuthArg; if( pAuthArg->op==TK_STRING ){ + assert( !ExprHasProperty(pAuthArg, EP_IntValue) ); zAuthArg = pAuthArg->u.zToken; }else{ zAuthArg = 0; @@ -111245,19 +117186,26 @@ static int fixSelectCb(Walker *p, Select *pSelect){ if( NEVER(pList==0) ) return WRC_Continue; for(i=0, pItem=pList->a; inSrc; i++, pItem++){ if( pFix->bTemp==0 ){ - if( pItem->zDatabase && iDb!=wx_sqlite3FindDbName(db, pItem->zDatabase) ){ - wx_sqlite3ErrorMsg(pFix->pParse, - "%s %T cannot reference objects in database %s", - pFix->zType, pFix->pName, pItem->zDatabase); - return WRC_Abort; + if( pItem->zDatabase ){ + if( iDb!=wx_sqlite3FindDbName(db, pItem->zDatabase) ){ + wx_sqlite3ErrorMsg(pFix->pParse, + "%s %T cannot reference objects in database %s", + pFix->zType, pFix->pName, pItem->zDatabase); + return WRC_Abort; + } + wx_sqlite3DbFree(db, pItem->zDatabase); + pItem->zDatabase = 0; + pItem->fg.notCte = 1; } - wx_sqlite3DbFree(db, pItem->zDatabase); - pItem->zDatabase = 0; pItem->pSchema = pFix->pSchema; pItem->fg.fromDDL = 1; } #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) - if( wx_sqlite3WalkExpr(&pFix->w, pList->a[i].pOn) ) return WRC_Abort; + if( pList->a[i].fg.isUsing==0 + && wx_sqlite3WalkExpr(&pFix->w, pList->a[i].u3.pOn) + ){ + return WRC_Abort; + } #endif } if( pSelect->pWith ){ @@ -111292,7 +117240,7 @@ SQLITE_PRIVATE void wx_sqlite3FixInit( pFix->w.pParse = pParse; pFix->w.xExprCallback = fixExprCb; pFix->w.xSelectCallback = fixSelectCb; - pFix->w.xSelectCallback2 = 0; + pFix->w.xSelectCallback2 = wx_sqlite3WalkWinDefnDummyCallback; pFix->w.walkerDepth = 0; pFix->w.eCode = 0; pFix->w.u.pFix = pFix; @@ -111354,14 +117302,16 @@ SQLITE_PRIVATE int wx_sqlite3FixTriggerStep( return 1; } #ifndef SQLITE_OMIT_UPSERT - if( pStep->pUpsert ){ - Upsert *pUp = pStep->pUpsert; - if( wx_sqlite3WalkExprList(&pFix->w, pUp->pUpsertTarget) - || wx_sqlite3WalkExpr(&pFix->w, pUp->pUpsertTargetWhere) - || wx_sqlite3WalkExprList(&pFix->w, pUp->pUpsertSet) - || wx_sqlite3WalkExpr(&pFix->w, pUp->pUpsertWhere) - ){ - return 1; + { + Upsert *pUp; + for(pUp=pStep->pUpsert; pUp; pUp=pUp->pNextUpsert){ + if( wx_sqlite3WalkExprList(&pFix->w, pUp->pUpsertTarget) + || wx_sqlite3WalkExpr(&pFix->w, pUp->pUpsertTargetWhere) + || wx_sqlite3WalkExprList(&pFix->w, pUp->pUpsertSet) + || wx_sqlite3WalkExpr(&pFix->w, pUp->pUpsertWhere) + ){ + return 1; + } } } #endif @@ -111551,10 +117501,10 @@ SQLITE_PRIVATE void wx_sqlite3AuthRead( if( iCol>=0 ){ assert( iColnCol ); - zCol = pTab->aCol[iCol].zName; + zCol = pTab->aCol[iCol].zCnName; }else if( pTab->iPKey>=0 ){ assert( pTab->iPKeynCol ); - zCol = pTab->aCol[pTab->iPKey].zName; + zCol = pTab->aCol[pTab->iPKey].zCnName; }else{ zCol = "ROWID"; } @@ -111693,7 +117643,7 @@ struct TableLock { ** code to make the lock occur is generated by a later call to ** codeTableLocks() which occurs during wx_sqlite3FinishCoding(). */ -SQLITE_PRIVATE void wx_sqlite3TableLock( +static SQLITE_NOINLINE void lockTable( Parse *pParse, /* Parsing context */ int iDb, /* Index of the database containing the table to lock */ Pgno iTab, /* Root page number of the table to be locked */ @@ -111706,8 +117656,6 @@ SQLITE_PRIVATE void wx_sqlite3TableLock( TableLock *p; assert( iDb>=0 ); - if( iDb==1 ) return; - if( !wx_sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return; pToplevel = wx_sqlite3ParseToplevel(pParse); for(i=0; inTableLock; i++){ p = &pToplevel->aTableLock[i]; @@ -111731,6 +117679,17 @@ SQLITE_PRIVATE void wx_sqlite3TableLock( wx_sqlite3OomFault(pToplevel->db); } } +SQLITE_PRIVATE void wx_sqlite3TableLock( + Parse *pParse, /* Parsing context */ + int iDb, /* Index of the database containing the table to lock */ + Pgno iTab, /* Root page number of the table to be locked */ + u8 isWriteLock, /* True for a write lock */ + const char *zName /* Name of the table to be locked */ +){ + if( iDb==1 ) return; + if( !wx_sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return; + lockTable(pParse, iDb, iTab, isWriteLock, zName); +} /* ** Code an OP_TableLock instruction for each table locked by the @@ -111778,14 +117737,17 @@ SQLITE_PRIVATE int wx_sqlite3DbMaskAllZero(yDbMask m){ SQLITE_PRIVATE void wx_sqlite3FinishCoding(Parse *pParse){ wx_sqlite3 *db; Vdbe *v; + int iDb, i; assert( pParse->pToplevel==0 ); db = pParse->db; + assert( db->pParse==pParse ); if( pParse->nested ) return; - if( db->mallocFailed || pParse->nErr ){ - if( pParse->rc==SQLITE_OK ) pParse->rc = SQLITE_ERROR; + if( pParse->nErr ){ + if( db->mallocFailed ) pParse->rc = SQLITE_NOMEM; return; } + assert( db->mallocFailed==0 ); /* Begin by generating some termination code at the end of the ** vdbe program @@ -111805,20 +117767,22 @@ SQLITE_PRIVATE void wx_sqlite3FinishCoding(Parse *pParse){ if( pParse->bReturning ){ Returning *pReturning = pParse->u1.pReturning; int addrRewind; - int i; int reg; - addrRewind = - wx_sqlite3VdbeAddOp1(v, OP_Rewind, pReturning->iRetCur); - VdbeCoverage(v); - reg = pReturning->iRetReg; - for(i=0; inRetCol; i++){ - wx_sqlite3VdbeAddOp3(v, OP_Column, pReturning->iRetCur, i, reg+i); + if( pReturning->nRetCol ){ + wx_sqlite3VdbeAddOp0(v, OP_FkCheck); + addrRewind = + wx_sqlite3VdbeAddOp1(v, OP_Rewind, pReturning->iRetCur); + VdbeCoverage(v); + reg = pReturning->iRetReg; + for(i=0; inRetCol; i++){ + wx_sqlite3VdbeAddOp3(v, OP_Column, pReturning->iRetCur, i, reg+i); + } + wx_sqlite3VdbeAddOp2(v, OP_ResultRow, reg, i); + wx_sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1); + VdbeCoverage(v); + wx_sqlite3VdbeJumpHere(v, addrRewind); } - wx_sqlite3VdbeAddOp2(v, OP_ResultRow, reg, i); - wx_sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1); - VdbeCoverage(v); - wx_sqlite3VdbeJumpHere(v, addrRewind); } wx_sqlite3VdbeAddOp0(v, OP_Halt); @@ -111839,77 +117803,76 @@ SQLITE_PRIVATE void wx_sqlite3FinishCoding(Parse *pParse){ ** transaction on each used database and to verify the schema cookie ** on each used database. */ - if( db->mallocFailed==0 - && (DbMaskNonZero(pParse->cookieMask) || pParse->pConstExpr) - ){ - int iDb, i; - assert( wx_sqlite3VdbeGetOp(v, 0)->opcode==OP_Init ); - wx_sqlite3VdbeJumpHere(v, 0); - for(iDb=0; iDbnDb; iDb++){ - Schema *pSchema; - if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue; - wx_sqlite3VdbeUsesBtree(v, iDb); - pSchema = db->aDb[iDb].pSchema; - wx_sqlite3VdbeAddOp4Int(v, - OP_Transaction, /* Opcode */ - iDb, /* P1 */ - DbMaskTest(pParse->writeMask,iDb), /* P2 */ - pSchema->schema_cookie, /* P3 */ - pSchema->iGeneration /* P4 */ - ); - if( db->init.busy==0 ) wx_sqlite3VdbeChangeP5(v, 1); - VdbeComment((v, - "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite)); - } + assert( pParse->nErr>0 || wx_sqlite3VdbeGetOp(v, 0)->opcode==OP_Init ); + wx_sqlite3VdbeJumpHere(v, 0); + assert( db->nDb>0 ); + iDb = 0; + do{ + Schema *pSchema; + if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue; + wx_sqlite3VdbeUsesBtree(v, iDb); + pSchema = db->aDb[iDb].pSchema; + wx_sqlite3VdbeAddOp4Int(v, + OP_Transaction, /* Opcode */ + iDb, /* P1 */ + DbMaskTest(pParse->writeMask,iDb), /* P2 */ + pSchema->schema_cookie, /* P3 */ + pSchema->iGeneration /* P4 */ + ); + if( db->init.busy==0 ) wx_sqlite3VdbeChangeP5(v, 1); + VdbeComment((v, + "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite)); + }while( ++iDbnDb ); #ifndef SQLITE_OMIT_VIRTUALTABLE - for(i=0; inVtabLock; i++){ - char *vtab = (char *)wx_sqlite3GetVTable(db, pParse->apVtabLock[i]); - wx_sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); - } - pParse->nVtabLock = 0; + for(i=0; inVtabLock; i++){ + char *vtab = (char *)wx_sqlite3GetVTable(db, pParse->apVtabLock[i]); + wx_sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); + } + pParse->nVtabLock = 0; #endif - /* Once all the cookies have been verified and transactions opened, - ** obtain the required table-locks. This is a no-op unless the - ** shared-cache feature is enabled. - */ - codeTableLocks(pParse); + /* Once all the cookies have been verified and transactions opened, + ** obtain the required table-locks. This is a no-op unless the + ** shared-cache feature is enabled. + */ + codeTableLocks(pParse); - /* Initialize any AUTOINCREMENT data structures required. - */ - wx_sqlite3AutoincrementBegin(pParse); + /* Initialize any AUTOINCREMENT data structures required. + */ + wx_sqlite3AutoincrementBegin(pParse); - /* Code constant expressions that where factored out of inner loops. - ** - ** The pConstExpr list might also contain expressions that we simply - ** want to keep around until the Parse object is deleted. Such - ** expressions have iConstExprReg==0. Do not generate code for - ** those expressions, of course. - */ - if( pParse->pConstExpr ){ - ExprList *pEL = pParse->pConstExpr; - pParse->okConstFactor = 0; - for(i=0; inExpr; i++){ - int iReg = pEL->a[i].u.iConstExprReg; - if( iReg>0 ){ - wx_sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg); - } - } + /* Code constant expressions that where factored out of inner loops. + ** + ** The pConstExpr list might also contain expressions that we simply + ** want to keep around until the Parse object is deleted. Such + ** expressions have iConstExprReg==0. Do not generate code for + ** those expressions, of course. + */ + if( pParse->pConstExpr ){ + ExprList *pEL = pParse->pConstExpr; + pParse->okConstFactor = 0; + for(i=0; inExpr; i++){ + int iReg = pEL->a[i].u.iConstExprReg; + wx_sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg); } + } - if( pParse->bReturning ){ - Returning *pRet = pParse->u1.pReturning; + if( pParse->bReturning ){ + Returning *pRet = pParse->u1.pReturning; + if( pRet->nRetCol ){ wx_sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol); } - - /* Finally, jump back to the beginning of the executable code. */ - wx_sqlite3VdbeGoto(v, 1); } + + /* Finally, jump back to the beginning of the executable code. */ + wx_sqlite3VdbeGoto(v, 1); } /* Get the VDBE program ready for execution */ - if( v && pParse->nErr==0 && !db->mallocFailed ){ + assert( v!=0 || pParse->nErr ); + assert( db->mallocFailed==0 || pParse->nErr ); + if( pParse->nErr==0 ){ /* A minimum of one cursor is required if autoincrement is used * See ticket [a696379c1f08866] */ assert( pParse->pAinc==0 || pParse->nTab>0 ); @@ -111923,23 +117886,25 @@ SQLITE_PRIVATE void wx_sqlite3FinishCoding(Parse *pParse){ /* ** Run the parser and code generator recursively in order to generate ** code for the SQL statement given onto the end of the pParse context -** currently under construction. When the parser is run recursively -** this way, the final OP_Halt is not appended and other initialization -** and finalization steps are omitted because those are handling by the -** outermost parser. +** currently under construction. Notes: ** -** Not everything is nestable. This facility is designed to permit -** INSERT, UPDATE, and DELETE operations against the schema table. Use -** care if you decide to try to use this routine for some other purposes. +** * The final OP_Halt is not appended and other initialization +** and finalization steps are omitted because those are handling by the +** outermost parser. +** +** * Built-in SQL functions always take precedence over application-defined +** SQL functions. In other words, it is not possible to override a +** built-in function. */ SQLITE_PRIVATE void wx_sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){ va_list ap; char *zSql; - char *zErrMsg = 0; wx_sqlite3 *db = pParse->db; + u32 savedDbFlags = db->mDbFlags; char saveBuf[PARSE_TAIL_SZ]; if( pParse->nErr ) return; + if( pParse->eParseMode ) return; assert( pParse->nested<10 ); /* Nesting should only be of limited depth */ va_start(ap, zFormat); zSql = wx_sqlite3VMPrintf(db, zFormat, ap); @@ -111955,8 +117920,9 @@ SQLITE_PRIVATE void wx_sqlite3NestedParse(Parse *pParse, const char *zFormat, .. pParse->nested++; memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ); memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ); - wx_sqlite3RunParser(pParse, zSql, &zErrMsg); - wx_sqlite3DbFree(db, zErrMsg); + db->mDbFlags |= DBFLAG_PreferBuiltin; + wx_sqlite3RunParser(pParse, zSql); + db->mDbFlags = savedDbFlags; wx_sqlite3DbFree(db, zSql); memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ); pParse->nested--; @@ -112013,17 +117979,17 @@ SQLITE_PRIVATE Table *wx_sqlite3FindTable(wx_sqlite3 *db, const char *zName, con p = wx_sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName); if( p==0 && wx_sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ if( i==1 ){ - if( wx_sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0 - || wx_sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 - || wx_sqlite3StrICmp(zName+7, &DFLT_SCHEMA_TABLE[7])==0 + if( wx_sqlite3StrICmp(zName+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 + || wx_sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 + || wx_sqlite3StrICmp(zName+7, &LEGACY_SCHEMA_TABLE[7])==0 ){ p = wx_sqlite3HashFind(&db->aDb[1].pSchema->tblHash, - DFLT_TEMP_SCHEMA_TABLE); + LEGACY_TEMP_SCHEMA_TABLE); } }else{ - if( wx_sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){ + if( wx_sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 ){ p = wx_sqlite3HashFind(&db->aDb[i].pSchema->tblHash, - DFLT_SCHEMA_TABLE); + LEGACY_SCHEMA_TABLE); } } } @@ -112041,11 +118007,11 @@ SQLITE_PRIVATE Table *wx_sqlite3FindTable(wx_sqlite3 *db, const char *zName, con if( p ) break; } if( p==0 && wx_sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ - if( wx_sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){ - p = wx_sqlite3HashFind(&db->aDb[0].pSchema->tblHash, DFLT_SCHEMA_TABLE); - }else if( wx_sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0 ){ + if( wx_sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 ){ + p = wx_sqlite3HashFind(&db->aDb[0].pSchema->tblHash, LEGACY_SCHEMA_TABLE); + }else if( wx_sqlite3StrICmp(zName+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){ p = wx_sqlite3HashFind(&db->aDb[1].pSchema->tblHash, - DFLT_TEMP_SCHEMA_TABLE); + LEGACY_TEMP_SCHEMA_TABLE); } } } @@ -112085,19 +118051,20 @@ SQLITE_PRIVATE Table *wx_sqlite3LocateTable( /* If zName is the not the name of a table in the schema created using ** CREATE, then check to see if it is the name of an virtual table that ** can be an eponymous virtual table. */ - if( pParse->disableVtab==0 ){ + if( (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)==0 && db->init.busy==0 ){ Module *pMod = (Module*)wx_sqlite3HashFind(&db->aModule, zName); if( pMod==0 && wx_sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ pMod = wx_sqlite3PragmaVtabRegister(db, zName); } if( pMod && wx_sqlite3VtabEponymousTableInit(pParse, pMod) ){ + testcase( pMod->pEpoTab==0 ); return pMod->pEpoTab; } } #endif if( flags & LOCATE_NOERR ) return 0; pParse->checkSchema = 1; - }else if( IsVirtual(p) && pParse->disableVtab ){ + }else if( IsVirtual(p) && (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)!=0 ){ p = 0; } @@ -112108,6 +118075,8 @@ SQLITE_PRIVATE Table *wx_sqlite3LocateTable( }else{ wx_sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); } + }else{ + assert( HasRowid(p) || p->iPKey<0 ); } return p; @@ -112138,6 +118107,22 @@ SQLITE_PRIVATE Table *wx_sqlite3LocateTableItem( return wx_sqlite3LocateTable(pParse, flags, p->zName, zDb); } +/* +** Return the preferred table name for system tables. Translate legacy +** names into the new preferred names, as appropriate. +*/ +SQLITE_PRIVATE const char *wx_sqlite3PreferredTableName(const char *zName){ + if( wx_sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ + if( wx_sqlite3StrICmp(zName+7, &LEGACY_SCHEMA_TABLE[7])==0 ){ + return PREFERRED_SCHEMA_TABLE; + } + if( wx_sqlite3StrICmp(zName+7, &LEGACY_TEMP_SCHEMA_TABLE[7])==0 ){ + return PREFERRED_TEMP_SCHEMA_TABLE; + } + } + return zName; +} + /* ** Locate the in-memory structure that describes ** a particular index given the name of that index @@ -112302,6 +118287,84 @@ SQLITE_PRIVATE void wx_sqlite3CommitInternalChanges(wx_sqlite3 *db){ db->mDbFlags &= ~DBFLAG_SchemaChange; } +/* +** Set the expression associated with a column. This is usually +** the DEFAULT value, but might also be the expression that computes +** the value for a generated column. +*/ +SQLITE_PRIVATE void wx_sqlite3ColumnSetExpr( + Parse *pParse, /* Parsing context */ + Table *pTab, /* The table containing the column */ + Column *pCol, /* The column to receive the new DEFAULT expression */ + Expr *pExpr /* The new default expression */ +){ + ExprList *pList; + assert( IsOrdinaryTable(pTab) ); + pList = pTab->u.tab.pDfltList; + if( pCol->iDflt==0 + || NEVER(pList==0) + || NEVER(pList->nExpriDflt) + ){ + pCol->iDflt = pList==0 ? 1 : pList->nExpr+1; + pTab->u.tab.pDfltList = wx_sqlite3ExprListAppend(pParse, pList, pExpr); + }else{ + wx_sqlite3ExprDelete(pParse->db, pList->a[pCol->iDflt-1].pExpr); + pList->a[pCol->iDflt-1].pExpr = pExpr; + } +} + +/* +** Return the expression associated with a column. The expression might be +** the DEFAULT clause or the AS clause of a generated column. +** Return NULL if the column has no associated expression. +*/ +SQLITE_PRIVATE Expr *wx_sqlite3ColumnExpr(Table *pTab, Column *pCol){ + if( pCol->iDflt==0 ) return 0; + if( NEVER(!IsOrdinaryTable(pTab)) ) return 0; + if( NEVER(pTab->u.tab.pDfltList==0) ) return 0; + if( NEVER(pTab->u.tab.pDfltList->nExpriDflt) ) return 0; + return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; +} + +/* +** Set the collating sequence name for a column. +*/ +SQLITE_PRIVATE void wx_sqlite3ColumnSetColl( + wx_sqlite3 *db, + Column *pCol, + const char *zColl +){ + i64 nColl; + i64 n; + char *zNew; + assert( zColl!=0 ); + n = wx_sqlite3Strlen30(pCol->zCnName) + 1; + if( pCol->colFlags & COLFLAG_HASTYPE ){ + n += wx_sqlite3Strlen30(pCol->zCnName+n) + 1; + } + nColl = wx_sqlite3Strlen30(zColl) + 1; + zNew = wx_sqlite3DbRealloc(db, pCol->zCnName, nColl+n); + if( zNew ){ + pCol->zCnName = zNew; + memcpy(pCol->zCnName + n, zColl, nColl); + pCol->colFlags |= COLFLAG_HASCOLL; + } +} + +/* +** Return the collating squence name for a column +*/ +SQLITE_PRIVATE const char *wx_sqlite3ColumnColl(Column *pCol){ + const char *z; + if( (pCol->colFlags & COLFLAG_HASCOLL)==0 ) return 0; + z = pCol->zCnName; + while( *z ){ z++; } + if( pCol->colFlags & COLFLAG_HASTYPE ){ + do{ z++; }while( *z ); + } + return z+1; +} + /* ** Delete memory allocated for the column names of a table or view (the ** Table.aCol[] array). @@ -112310,14 +118373,23 @@ SQLITE_PRIVATE void wx_sqlite3DeleteColumnNames(wx_sqlite3 *db, Table *pTable){ int i; Column *pCol; assert( pTable!=0 ); + assert( db!=0 ); if( (pCol = pTable->aCol)!=0 ){ for(i=0; inCol; i++, pCol++){ - assert( pCol->zName==0 || pCol->hName==wx_sqlite3StrIHash(pCol->zName) ); - wx_sqlite3DbFree(db, pCol->zName); - wx_sqlite3ExprDelete(db, pCol->pDflt); - wx_sqlite3DbFree(db, pCol->zColl); + assert( pCol->zCnName==0 || pCol->hName==wx_sqlite3StrIHash(pCol->zCnName) ); + wx_sqlite3DbFree(db, pCol->zCnName); + } + wx_sqlite3DbNNFreeNN(db, pTable->aCol); + if( IsOrdinaryTable(pTable) ){ + wx_sqlite3ExprListDelete(db, pTable->u.tab.pDfltList); + } + if( db->pnBytesFreed==0 ){ + pTable->aCol = 0; + pTable->nCol = 0; + if( IsOrdinaryTable(pTable) ){ + pTable->u.tab.pDfltList = 0; + } } - wx_sqlite3DbFree(db, pTable->aCol); } } @@ -112348,7 +118420,8 @@ static void SQLITE_NOINLINE deleteTable(wx_sqlite3 *db, Table *pTable){ ** a Table object that was going to be marked ephemeral. So do not check ** that no lookaside memory is used in this case either. */ int nLookaside = 0; - if( db && !db->mallocFailed && (pTable->tabFlags & TF_Ephemeral)==0 ){ + assert( db!=0 ); + if( !db->mallocFailed && (pTable->tabFlags & TF_Ephemeral)==0 ){ nLookaside = wx_sqlite3LookasideUsed(db, 0); } #endif @@ -112358,7 +118431,7 @@ static void SQLITE_NOINLINE deleteTable(wx_sqlite3 *db, Table *pTable){ pNext = pIndex->pNext; assert( pIndex->pSchema==pTable->pSchema || (IsVirtual(pTable) && pIndex->idxType!=SQLITE_IDXTYPE_APPDEF) ); - if( (db==0 || db->pnBytesFreed==0) && !IsVirtual(pTable) ){ + if( db->pnBytesFreed==0 && !IsVirtual(pTable) ){ char *zName = pIndex->zName; TESTONLY ( Index *pOld = ) wx_sqlite3HashInsert( &pIndex->pSchema->idxHash, zName, 0 @@ -112369,19 +118442,25 @@ static void SQLITE_NOINLINE deleteTable(wx_sqlite3 *db, Table *pTable){ wx_sqlite3FreeIndex(db, pIndex); } - /* Delete any foreign keys attached to this table. */ - wx_sqlite3FkDelete(db, pTable); + if( IsOrdinaryTable(pTable) ){ + wx_sqlite3FkDelete(db, pTable); + } +#ifndef SQLITE_OMIT_VIRTUAL_TABLE + else if( IsVirtual(pTable) ){ + wx_sqlite3VtabClear(db, pTable); + } +#endif + else{ + assert( IsView(pTable) ); + wx_sqlite3SelectDelete(db, pTable->u.view.pSelect); + } /* Delete the Table structure itself. */ wx_sqlite3DeleteColumnNames(db, pTable); wx_sqlite3DbFree(db, pTable->zName); wx_sqlite3DbFree(db, pTable->zColAff); - wx_sqlite3SelectDelete(db, pTable->pSelect); wx_sqlite3ExprListDelete(db, pTable->pCheck); -#ifndef SQLITE_OMIT_VIRTUALTABLE - wx_sqlite3VtabClear(db, pTable); -#endif wx_sqlite3DbFree(db, pTable); /* Verify that no lookaside memory was used by schema tables */ @@ -112389,8 +118468,9 @@ static void SQLITE_NOINLINE deleteTable(wx_sqlite3 *db, Table *pTable){ } SQLITE_PRIVATE void wx_sqlite3DeleteTable(wx_sqlite3 *db, Table *pTable){ /* Do not delete the table until the reference count reaches zero. */ + assert( db!=0 ); if( !pTable ) return; - if( ((!db || db->pnBytesFreed==0) && (--pTable->nTabRef)>0) ) return; + if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return; deleteTable(db, pTable); } @@ -112427,10 +118507,10 @@ SQLITE_PRIVATE void wx_sqlite3UnlinkAndDeleteTable(wx_sqlite3 *db, int iDb, cons ** are not \000 terminated and are not persistent. The returned string ** is \000 terminated and is persistent. */ -SQLITE_PRIVATE char *wx_sqlite3NameFromToken(wx_sqlite3 *db, Token *pName){ +SQLITE_PRIVATE char *wx_sqlite3NameFromToken(wx_sqlite3 *db, const Token *pName){ char *zName; if( pName ){ - zName = wx_sqlite3DbStrNDup(db, (char*)pName->z, pName->n); + zName = wx_sqlite3DbStrNDup(db, (const char*)pName->z, pName->n); wx_sqlite3Dequote(zName); }else{ zName = 0; @@ -112444,7 +118524,7 @@ SQLITE_PRIVATE char *wx_sqlite3NameFromToken(wx_sqlite3 *db, Token *pName){ */ SQLITE_PRIVATE void wx_sqlite3OpenSchemaTable(Parse *p, int iDb){ Vdbe *v = wx_sqlite3GetVdbe(p); - wx_sqlite3TableLock(p, iDb, SCHEMA_ROOT, 1, DFLT_SCHEMA_TABLE); + wx_sqlite3TableLock(p, iDb, SCHEMA_ROOT, 1, LEGACY_SCHEMA_TABLE); wx_sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, SCHEMA_ROOT, iDb, 5); if( p->nTab==0 ){ p->nTab = 1; @@ -112524,7 +118604,7 @@ SQLITE_PRIVATE int wx_sqlite3TwoPartName( return -1; } }else{ - assert( db->init.iDb==0 || db->init.busy || IN_RENAME_OBJECT + assert( db->init.iDb==0 || db->init.busy || IN_SPECIAL_PARSE || (db->mDbFlags & DBFLAG_Vacuum)!=0); iDb = db->init.iDb; *pUnqual = pName1; @@ -112693,6 +118773,23 @@ SQLITE_PRIVATE i16 wx_sqlite3TableColumnToStorage(Table *pTab, i16 iCol){ } #endif +/* +** Insert a single OP_JournalMode query opcode in order to force the +** prepared statement to return false for wx_sqlite3_stmt_readonly(). This +** is used by CREATE TABLE IF NOT EXISTS and similar if the table already +** exists, so that the prepared statement for CREATE TABLE IF NOT EXISTS +** will return false for wx_sqlite3_stmt_readonly() even if that statement +** is a read-only no-op. +*/ +static void wx_sqlite3ForceNotReadOnly(Parse *pParse){ + int iReg = ++pParse->nMem; + Vdbe *v = wx_sqlite3GetVdbe(pParse); + if( v ){ + wx_sqlite3VdbeAddOp3(v, OP_JournalMode, 0, iReg, PAGER_JOURNALMODE_QUERY); + wx_sqlite3VdbeUsesBtree(v, 0); + } +} + /* ** Begin constructing a new table representation in memory. This is ** the first of several action routines that get called in response @@ -112788,10 +118885,12 @@ SQLITE_PRIVATE void wx_sqlite3StartTable( pTable = wx_sqlite3FindTable(db, zName, zDb); if( pTable ){ if( !noErr ){ - wx_sqlite3ErrorMsg(pParse, "table %T already exists", pName); + wx_sqlite3ErrorMsg(pParse, "%s %T already exists", + (IsView(pTable)? "view" : "table"), pName); }else{ assert( !db->init.busy || CORRUPT_DB ); wx_sqlite3CodeVerifySchema(pParse, iDb); + wx_sqlite3ForceNotReadOnly(pParse); } goto begin_table_error; } @@ -112820,17 +118919,6 @@ SQLITE_PRIVATE void wx_sqlite3StartTable( assert( pParse->pNewTable==0 ); pParse->pNewTable = pTable; - /* If this is the magic sqlite_sequence table used by autoincrement, - ** then record a pointer to this table in the main database structure - ** so that INSERT can find the table easily. - */ -#ifndef SQLITE_OMIT_AUTOINCREMENT - if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ - assert( wx_sqlite3SchemaMutexHeld(db, iDb, 0) ); - pTable->pSchema->pSeqTab = pTable; - } -#endif - /* Begin generating the code that will insert the table record into ** the schema table. Note in particular that we must go ahead ** and allocate the record number for the table entry now. Before any @@ -112900,6 +118988,7 @@ SQLITE_PRIVATE void wx_sqlite3StartTable( /* If an error occurs, we jump here */ begin_table_error: + pParse->checkSchema = 1; wx_sqlite3DbFree(db, zName); return; } @@ -112909,7 +118998,7 @@ begin_table_error: */ #if SQLITE_ENABLE_HIDDEN_COLUMNS SQLITE_PRIVATE void wx_sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){ - if( wx_sqlite3_strnicmp(pCol->zName, "__hidden__", 10)==0 ){ + if( wx_sqlite3_strnicmp(pCol->zCnName, "__hidden__", 10)==0 ){ pCol->colFlags |= COLFLAG_HIDDEN; if( pTab ) pTab->tabFlags |= TF_HasHidden; }else if( pTab && pCol!=pTab->aCol && (pCol[-1].colFlags & COLFLAG_HIDDEN) ){ @@ -112959,7 +119048,7 @@ SQLITE_PRIVATE void wx_sqlite3AddReturning(Parse *pParse, ExprList *pList){ if( pParse->pNewTrigger ){ wx_sqlite3ErrorMsg(pParse, "cannot use RETURNING in a trigger"); }else{ - assert( pParse->bReturning==0 ); + assert( pParse->bReturning==0 || pParse->ifNotExists ); } pParse->bReturning = 1; pRet = wx_sqlite3DbMallocZero(db, sizeof(*pRet)); @@ -112979,12 +119068,14 @@ SQLITE_PRIVATE void wx_sqlite3AddReturning(Parse *pParse, ExprList *pList){ pRet->retTrig.tr_tm = TRIGGER_AFTER; pRet->retTrig.bReturning = 1; pRet->retTrig.pSchema = db->aDb[1].pSchema; + pRet->retTrig.pTabSchema = db->aDb[1].pSchema; pRet->retTrig.step_list = &pRet->retTStep; pRet->retTStep.op = TK_RETURNING; pRet->retTStep.pTrig = &pRet->retTrig; pRet->retTStep.pExprList = pList; pHash = &(db->aDb[1].pSchema->trigHash); - assert( wx_sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0 || pParse->nErr ); + assert( wx_sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0 + || pParse->nErr || pParse->ifNotExists ); if( wx_sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig) ==&pRet->retTrig ){ wx_sqlite3OomFault(db); @@ -112999,7 +119090,7 @@ SQLITE_PRIVATE void wx_sqlite3AddReturning(Parse *pParse, ExprList *pList){ ** first to get things going. Then this routine is called for each ** column. */ -SQLITE_PRIVATE void wx_sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){ +SQLITE_PRIVATE void wx_sqlite3AddColumn(Parse *pParse, Token sName, Token sType){ Table *p; int i; char *z; @@ -113007,55 +119098,96 @@ SQLITE_PRIVATE void wx_sqlite3AddColumn(Parse *pParse, Token *pName, Token *pTyp Column *pCol; wx_sqlite3 *db = pParse->db; u8 hName; + Column *aNew; + u8 eType = COLTYPE_CUSTOM; + u8 szEst = 1; + char affinity = SQLITE_AFF_BLOB; if( (p = pParse->pNewTable)==0 ) return; if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){ wx_sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName); return; } - z = wx_sqlite3DbMallocRaw(db, pName->n + pType->n + 2); + if( !IN_RENAME_OBJECT ) wx_sqlite3DequoteToken(&sName); + + /* Because keywords GENERATE ALWAYS can be converted into indentifiers + ** by the parser, we can sometimes end up with a typename that ends + ** with "generated always". Check for this case and omit the surplus + ** text. */ + if( sType.n>=16 + && wx_sqlite3_strnicmp(sType.z+(sType.n-6),"always",6)==0 + ){ + sType.n -= 6; + while( ALWAYS(sType.n>0) && wx_sqlite3Isspace(sType.z[sType.n-1]) ) sType.n--; + if( sType.n>=9 + && wx_sqlite3_strnicmp(sType.z+(sType.n-9),"generated",9)==0 + ){ + sType.n -= 9; + while( sType.n>0 && wx_sqlite3Isspace(sType.z[sType.n-1]) ) sType.n--; + } + } + + /* Check for standard typenames. For standard typenames we will + ** set the Column.eType field rather than storing the typename after + ** the column name, in order to save space. */ + if( sType.n>=3 ){ + wx_sqlite3DequoteToken(&sType); + for(i=0; i0) ); if( z==0 ) return; - if( IN_RENAME_OBJECT ) wx_sqlite3RenameTokenMap(pParse, (void*)z, pName); - memcpy(z, pName->z, pName->n); - z[pName->n] = 0; + if( IN_RENAME_OBJECT ) wx_sqlite3RenameTokenMap(pParse, (void*)z, &sName); + memcpy(z, sName.z, sName.n); + z[sName.n] = 0; wx_sqlite3Dequote(z); hName = wx_sqlite3StrIHash(z); for(i=0; inCol; i++){ - if( p->aCol[i].hName==hName && wx_sqlite3StrICmp(z, p->aCol[i].zName)==0 ){ + if( p->aCol[i].hName==hName && wx_sqlite3StrICmp(z, p->aCol[i].zCnName)==0 ){ wx_sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); wx_sqlite3DbFree(db, z); return; } } - if( (p->nCol & 0x7)==0 ){ - Column *aNew; - aNew = wx_sqlite3DbRealloc(db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0])); - if( aNew==0 ){ - wx_sqlite3DbFree(db, z); - return; - } - p->aCol = aNew; + aNew = wx_sqlite3DbRealloc(db,p->aCol,((i64)p->nCol+1)*sizeof(p->aCol[0])); + if( aNew==0 ){ + wx_sqlite3DbFree(db, z); + return; } + p->aCol = aNew; pCol = &p->aCol[p->nCol]; memset(pCol, 0, sizeof(p->aCol[0])); - pCol->zName = z; + pCol->zCnName = z; pCol->hName = hName; wx_sqlite3ColumnPropertiesFromName(p, pCol); - if( pType->n==0 ){ + if( sType.n==0 ){ /* If there is no type specified, columns have the default affinity ** 'BLOB' with a default size of 4 bytes. */ - pCol->affinity = SQLITE_AFF_BLOB; - pCol->szEst = 1; + pCol->affinity = affinity; + pCol->eCType = eType; + pCol->szEst = szEst; #ifdef SQLITE_ENABLE_SORTER_REFERENCES - if( 4>=wx_sqlite3GlobalConfig.szSorterRef ){ - pCol->colFlags |= COLFLAG_SORTERREF; + if( affinity==SQLITE_AFF_BLOB ){ + if( 4>=wx_sqlite3GlobalConfig.szSorterRef ){ + pCol->colFlags |= COLFLAG_SORTERREF; + } } #endif }else{ zType = z + wx_sqlite3Strlen30(z) + 1; - memcpy(zType, pType->z, pType->n); - zType[pType->n] = 0; + memcpy(zType, sType.z, sType.n); + zType[sType.n] = 0; wx_sqlite3Dequote(zType); pCol->affinity = wx_sqlite3AffinityType(zType, pCol); pCol->colFlags |= COLFLAG_HASTYPE; @@ -113210,7 +119342,7 @@ SQLITE_PRIVATE void wx_sqlite3AddDefaultValue( pCol = &(p->aCol[p->nCol-1]); if( !wx_sqlite3ExprIsConstantOrFunction(pExpr, isInit) ){ wx_sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant", - pCol->zName); + pCol->zCnName); #ifndef SQLITE_OMIT_GENERATED_COLUMNS }else if( pCol->colFlags & COLFLAG_GENERATED ){ testcase( pCol->colFlags & COLFLAG_VIRTUAL ); @@ -113221,15 +119353,15 @@ SQLITE_PRIVATE void wx_sqlite3AddDefaultValue( /* A copy of pExpr is used instead of the original, as pExpr contains ** tokens that point to volatile memory. */ - Expr x; - wx_sqlite3ExprDelete(db, pCol->pDflt); + Expr x, *pDfltExpr; memset(&x, 0, sizeof(x)); x.op = TK_SPAN; x.u.zToken = wx_sqlite3DbSpanDup(db, zStart, zEnd); x.pLeft = pExpr; x.flags = EP_Skip; - pCol->pDflt = wx_sqlite3ExprDup(db, &x, EXPRDUP_REDUCE); + pDfltExpr = wx_sqlite3ExprDup(db, &x, EXPRDUP_REDUCE); wx_sqlite3DbFree(db, x.u.zToken); + wx_sqlite3ColumnSetExpr(pParse, p, pCol, pDfltExpr); } } if( IN_RENAME_OBJECT ){ @@ -113325,9 +119457,11 @@ SQLITE_PRIVATE void wx_sqlite3AddPrimaryKey( assert( pCExpr!=0 ); wx_sqlite3StringToId(pCExpr); if( pCExpr->op==TK_ID ){ - const char *zCName = pCExpr->u.zToken; + const char *zCName; + assert( !ExprHasProperty(pCExpr, EP_IntValue) ); + zCName = pCExpr->u.zToken; for(iCol=0; iColnCol; iCol++){ - if( wx_sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){ + if( wx_sqlite3StrICmp(zCName, pTab->aCol[iCol].zCnName)==0 ){ pCol = &pTab->aCol[iCol]; makeColumnPartOfPrimaryKey(pParse, pCol); break; @@ -113338,7 +119472,7 @@ SQLITE_PRIVATE void wx_sqlite3AddPrimaryKey( } if( nTerm==1 && pCol - && wx_sqlite3StrICmp(wx_sqlite3ColumnType(pCol,""), "INTEGER")==0 + && pCol->eCType==COLTYPE_INTEGER && sortOrder!=SQLITE_SO_DESC ){ if( IN_RENAME_OBJECT && pList ){ @@ -113349,7 +119483,7 @@ SQLITE_PRIVATE void wx_sqlite3AddPrimaryKey( pTab->keyConf = (u8)onError; assert( autoInc==0 || autoInc==1 ); pTab->tabFlags |= autoInc*TF_Autoincrement; - if( pList ) pParse->iPkSortOrder = pList->a[0].sortFlags; + if( pList ) pParse->iPkSortOrder = pList->a[0].fg.sortFlags; (void)wx_sqlite3HasExplicitNulls(pParse, pList); }else if( autoInc ){ #ifndef SQLITE_OMIT_AUTOINCREMENT @@ -113418,8 +119552,7 @@ SQLITE_PRIVATE void wx_sqlite3AddCollateType(Parse *pParse, Token *pToken){ if( wx_sqlite3LocateCollSeq(pParse, zColl) ){ Index *pIdx; - wx_sqlite3DbFree(db, p->aCol[i].zColl); - p->aCol[i].zColl = zColl; + wx_sqlite3ColumnSetColl(db, &p->aCol[i], zColl); /* If the column is declared as " PRIMARY KEY COLLATE ", ** then an index may have been created on this column before the @@ -113428,12 +119561,11 @@ SQLITE_PRIVATE void wx_sqlite3AddCollateType(Parse *pParse, Token *pToken){ for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){ assert( pIdx->nKeyCol==1 ); if( pIdx->aiColumn[0]==i ){ - pIdx->azColl[0] = p->aCol[i].zColl; + pIdx->azColl[0] = wx_sqlite3ColumnColl(&p->aCol[i]); } } - }else{ - wx_sqlite3DbFree(db, zColl); } + wx_sqlite3DbFree(db, zColl); } /* Change the most recently parsed column to be a GENERATED ALWAYS AS @@ -113453,7 +119585,7 @@ SQLITE_PRIVATE void wx_sqlite3AddGenerated(Parse *pParse, Expr *pExpr, Token *pT wx_sqlite3ErrorMsg(pParse, "virtual tables cannot use computed columns"); goto generated_done; } - if( pCol->pDflt ) goto generated_error; + if( pCol->iDflt>0 ) goto generated_error; if( pType ){ if( pType->n==7 && wx_sqlite3StrNICmp("virtual",pType->z,7)==0 ){ /* no-op */ @@ -113471,13 +119603,21 @@ SQLITE_PRIVATE void wx_sqlite3AddGenerated(Parse *pParse, Expr *pExpr, Token *pT if( pCol->colFlags & COLFLAG_PRIMKEY ){ makeColumnPartOfPrimaryKey(pParse, pCol); /* For the error message */ } - pCol->pDflt = pExpr; + if( ALWAYS(pExpr) && pExpr->op==TK_ID ){ + /* The value of a generated column needs to be a real expression, not + ** just a reference to another column, in order for covering index + ** optimizations to work correctly. So if the value is not an expression, + ** turn it into one by adding a unary "+" operator. */ + pExpr = wx_sqlite3PExpr(pParse, TK_UPLUS, pExpr, 0); + } + if( pExpr && pExpr->op!=TK_RAISE ) pExpr->affExpr = pCol->affinity; + wx_sqlite3ColumnSetExpr(pParse, pTab, pCol, pExpr); pExpr = 0; goto generated_done; generated_error: wx_sqlite3ErrorMsg(pParse, "error in generated column \"%s\"", - pCol->zName); + pCol->zCnName); generated_done: wx_sqlite3ExprDelete(pParse->db, pExpr); #else @@ -113579,7 +119719,7 @@ static char *createTableStmt(wx_sqlite3 *db, Table *p){ Column *pCol; n = 0; for(pCol = p->aCol, i=0; inCol; i++, pCol++){ - n += identLength(pCol->zName) + 5; + n += identLength(pCol->zCnName) + 5; } n += identLength(p->zName); if( n<50 ){ @@ -113607,7 +119747,8 @@ static char *createTableStmt(wx_sqlite3 *db, Table *p){ /* SQLITE_AFF_TEXT */ " TEXT", /* SQLITE_AFF_NUMERIC */ " NUM", /* SQLITE_AFF_INTEGER */ " INT", - /* SQLITE_AFF_REAL */ " REAL" + /* SQLITE_AFF_REAL */ " REAL", + /* SQLITE_AFF_FLEXNUM */ " NUM", }; int len; const char *zType; @@ -113615,7 +119756,7 @@ static char *createTableStmt(wx_sqlite3 *db, Table *p){ wx_sqlite3_snprintf(n-k, &zStmt[k], zSep); k += wx_sqlite3Strlen30(&zStmt[k]); zSep = zSep2; - identPut(zStmt, &k, pCol->zName); + identPut(zStmt, &k, pCol->zCnName); assert( pCol->affinity-SQLITE_AFF_BLOB >= 0 ); assert( pCol->affinity-SQLITE_AFF_BLOB < ArraySize(azType) ); testcase( pCol->affinity==SQLITE_AFF_BLOB ); @@ -113623,10 +119764,12 @@ static char *createTableStmt(wx_sqlite3 *db, Table *p){ testcase( pCol->affinity==SQLITE_AFF_NUMERIC ); testcase( pCol->affinity==SQLITE_AFF_INTEGER ); testcase( pCol->affinity==SQLITE_AFF_REAL ); + testcase( pCol->affinity==SQLITE_AFF_FLEXNUM ); zType = azType[pCol->affinity - SQLITE_AFF_BLOB]; len = wx_sqlite3Strlen30(zType); assert( pCol->affinity==SQLITE_AFF_BLOB + || pCol->affinity==SQLITE_AFF_FLEXNUM || pCol->affinity==wx_sqlite3AffinityType(zType, 0) ); memcpy(&zStmt[k], zType, len); k += len; @@ -113699,7 +119842,6 @@ static void estimateIndexWidth(Index *pIdx){ */ static int hasColumn(const i16 *aiCol, int nCol, int x){ while( nCol-- > 0 ){ - assert( aiCol[0]>=0 ); if( x==*(aiCol++) ){ return 1; } @@ -113744,7 +119886,8 @@ static int isDupColumn(Index *pIdx, int nKey, Index *pPk, int iCol){ /* Recompute the colNotIdxed field of the Index. ** ** colNotIdxed is a bitmask that has a 0 bit representing each indexed -** columns that are within the first 63 columns of the table. The +** columns that are within the first 63 columns of the table and a 1 for +** all other bits (all columns that are not in the index). The ** high-order bit of colNotIdxed is always 1. All unindexed columns ** of the table have a 1. ** @@ -113772,7 +119915,7 @@ static void recomputeColumnsNotIndexed(Index *pIdx){ } } pIdx->colNotIdxed = ~m; - assert( (pIdx->colNotIdxed>>63)==1 ); + assert( (pIdx->colNotIdxed>>63)==1 ); /* See note-20221022-a */ } /* @@ -113812,7 +119955,9 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ */ if( !db->init.imposterTable ){ for(i=0; inCol; i++){ - if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 ){ + if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 + && (pTab->aCol[i].notNull==OE_None) + ){ pTab->aCol[i].notNull = OE_Abort; } } @@ -113834,19 +119979,26 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ if( pTab->iPKey>=0 ){ ExprList *pList; Token ipkToken; - wx_sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zName); + wx_sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zCnName); pList = wx_sqlite3ExprListAppend(pParse, 0, wx_sqlite3ExprAlloc(db, TK_ID, &ipkToken, 0)); - if( pList==0 ) return; + if( pList==0 ){ + pTab->tabFlags &= ~TF_WithoutRowid; + return; + } if( IN_RENAME_OBJECT ){ wx_sqlite3RenameTokenRemap(pParse, pList->a[0].pExpr, &pTab->iPKey); } - pList->a[0].sortFlags = pParse->iPkSortOrder; + pList->a[0].fg.sortFlags = pParse->iPkSortOrder; assert( pParse->pNewTable==pTab ); pTab->iPKey = -1; wx_sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0, SQLITE_IDXTYPE_PRIMARYKEY); - if( db->mallocFailed || pParse->nErr ) return; + if( pParse->nErr ){ + pTab->tabFlags &= ~TF_WithoutRowid; + return; + } + assert( db->mallocFailed==0 ); pPk = wx_sqlite3PrimaryKeyIndex(pTab); assert( pPk->nKeyCol==1 ); }else{ @@ -113958,7 +120110,7 @@ SQLITE_PRIVATE int wx_sqlite3IsShadowTableOf(wx_sqlite3 *db, Table *pTab, const nName = wx_sqlite3Strlen30(pTab->zName); if( wx_sqlite3_strnicmp(zName, pTab->zName, nName)!=0 ) return 0; if( zName[nName]!='_' ) return 0; - pMod = (Module*)wx_sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]); + pMod = (Module*)wx_sqlite3HashFind(&db->aModule, pTab->u.vtab.azArg[0]); if( pMod==0 ) return 0; if( pMod->pModule->iVersion<3 ) return 0; if( pMod->pModule->xShadowName==0 ) return 0; @@ -113966,6 +120118,41 @@ SQLITE_PRIVATE int wx_sqlite3IsShadowTableOf(wx_sqlite3 *db, Table *pTab, const } #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Table pTab is a virtual table. If it the virtual table implementation +** exists and has an xShadowName method, then loop over all other ordinary +** tables within the same schema looking for shadow tables of pTab, and mark +** any shadow tables seen using the TF_Shadow flag. +*/ +SQLITE_PRIVATE void wx_sqlite3MarkAllShadowTablesOf(wx_sqlite3 *db, Table *pTab){ + int nName; /* Length of pTab->zName */ + Module *pMod; /* Module for the virtual table */ + HashElem *k; /* For looping through the symbol table */ + + assert( IsVirtual(pTab) ); + pMod = (Module*)wx_sqlite3HashFind(&db->aModule, pTab->u.vtab.azArg[0]); + if( pMod==0 ) return; + if( NEVER(pMod->pModule==0) ) return; + if( pMod->pModule->iVersion<3 ) return; + if( pMod->pModule->xShadowName==0 ) return; + assert( pTab->zName!=0 ); + nName = wx_sqlite3Strlen30(pTab->zName); + for(k=sqliteHashFirst(&pTab->pSchema->tblHash); k; k=sqliteHashNext(k)){ + Table *pOther = sqliteHashData(k); + assert( pOther->zName!=0 ); + if( !IsOrdinaryTable(pOther) ) continue; + if( pOther->tabFlags & TF_Shadow ) continue; + if( wx_sqlite3StrNICmp(pOther->zName, pTab->zName, nName)==0 + && pOther->zName[nName]=='_' + && pMod->pModule->xShadowName(pOther->zName+nName+1) + ){ + pOther->tabFlags |= TF_Shadow; + } + } +} +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ + #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Return true if zName is a shadow table name in the current database @@ -113997,6 +120184,7 @@ SQLITE_PRIVATE int wx_sqlite3ShadowTableName(wx_sqlite3 *db, const char *zName){ ** not pass them into code generator routines by mistake. */ static int markImmutableExprStep(Walker *pWalker, Expr *pExpr){ + (void)pWalker; ExprSetVVAProperty(pExpr, EP_Immutable); return WRC_Continue; } @@ -114039,7 +120227,7 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( Parse *pParse, /* Parse context */ Token *pCons, /* The ',' token after the last column defn. */ Token *pEnd, /* The ')' before options in the CREATE TABLE */ - u8 tabOpts, /* Extra table options. Usually 0. */ + u32 tabOpts, /* Extra table options. Usually 0. */ Select *pSelect /* Select from a "CREATE ... AS SELECT" */ ){ Table *p; /* The new table */ @@ -114050,7 +120238,6 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( if( pEnd==0 && pSelect==0 ){ return; } - assert( !db->mallocFailed ); p = pParse->pNewTable; if( p==0 ) return; @@ -114068,7 +120255,7 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( ** table itself. So mark it read-only. */ if( db->init.busy ){ - if( pSelect ){ + if( pSelect || (!IsOrdinaryTable(p) && db->init.newTnum) ){ wx_sqlite3ErrorMsg(pParse, ""); return; } @@ -114076,6 +120263,44 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( if( p->tnum==1 ) p->tabFlags |= TF_Readonly; } + /* Special processing for tables that include the STRICT keyword: + ** + ** * Do not allow custom column datatypes. Every column must have + ** a datatype that is one of INT, INTEGER, REAL, TEXT, or BLOB. + ** + ** * If a PRIMARY KEY is defined, other than the INTEGER PRIMARY KEY, + ** then all columns of the PRIMARY KEY must have a NOT NULL + ** constraint. + */ + if( tabOpts & TF_Strict ){ + int ii; + p->tabFlags |= TF_Strict; + for(ii=0; iinCol; ii++){ + Column *pCol = &p->aCol[ii]; + if( pCol->eCType==COLTYPE_CUSTOM ){ + if( pCol->colFlags & COLFLAG_HASTYPE ){ + wx_sqlite3ErrorMsg(pParse, + "unknown datatype for %s.%s: \"%s\"", + p->zName, pCol->zCnName, wx_sqlite3ColumnType(pCol, "") + ); + }else{ + wx_sqlite3ErrorMsg(pParse, "missing datatype for %s.%s", + p->zName, pCol->zCnName); + } + return; + }else if( pCol->eCType==COLTYPE_ANY ){ + pCol->affinity = SQLITE_AFF_BLOB; + } + if( (pCol->colFlags & COLFLAG_PRIMKEY)!=0 + && p->iPKey!=ii + && pCol->notNull == OE_None + ){ + pCol->notNull = OE_Abort; + p->tabFlags |= TF_HasNotNull; + } + } + } + assert( (p->tabFlags & TF_HasPrimaryKey)==0 || p->iPKey>=0 || wx_sqlite3PrimaryKeyIndex(p)!=0 ); assert( (p->tabFlags & TF_HasPrimaryKey)!=0 @@ -114120,7 +120345,7 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( for(ii=0; iinCol; ii++){ u32 colFlags = p->aCol[ii].colFlags; if( (colFlags & COLFLAG_GENERATED)!=0 ){ - Expr *pX = p->aCol[ii].pDflt; + Expr *pX = wx_sqlite3ColumnExpr(p, &p->aCol[ii]); testcase( colFlags & COLFLAG_VIRTUAL ); testcase( colFlags & COLFLAG_STORED ); if( wx_sqlite3ResolveSelfReference(pParse, p, NC_GenCol, pX, 0) ){ @@ -114130,8 +120355,8 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( ** tree that have been allocated from lookaside memory, which is ** illegal in a schema and will lead to errors or heap corruption ** when the database connection closes. */ - wx_sqlite3ExprDelete(db, pX); - p->aCol[ii].pDflt = wx_sqlite3ExprAlloc(db, TK_NULL, 0, 0); + wx_sqlite3ColumnSetExpr(pParse, p, &p->aCol[ii], + wx_sqlite3ExprAlloc(db, TK_NULL, 0, 0)); } }else{ nNG++; @@ -114171,7 +120396,7 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( /* ** Initialize zType for the new view or table. */ - if( p->pSelect==0 ){ + if( IsOrdinaryTable(p) ){ /* A regular table */ zType = "table"; zType2 = "TABLE"; @@ -114205,6 +120430,11 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( int addrInsLoop; /* Top of the loop for inserting rows */ Table *pSelTab; /* A table that describes the SELECT results */ + if( IN_SPECIAL_PARSE ){ + pParse->rc = SQLITE_ERROR; + pParse->nErr++; + return; + } regYield = ++pParse->nMem; regRec = ++pParse->nMem; regRowid = ++pParse->nMem; @@ -114257,7 +120487,7 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( ** the information we've collected. */ wx_sqlite3NestedParse(pParse, - "UPDATE %Q." DFLT_SCHEMA_TABLE + "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q" " WHERE rowid=#%d", db->aDb[iDb].zDbSName, @@ -114275,7 +120505,7 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( /* Check to see if we need to create an sqlite_sequence table for ** keeping track of autoincrement keys. */ - if( (p->tabFlags & TF_Autoincrement)!=0 ){ + if( (p->tabFlags & TF_Autoincrement)!=0 && !IN_SPECIAL_PARSE ){ Db *pDb = &db->aDb[iDb]; assert( wx_sqlite3SchemaMutexHeld(db, iDb, 0) ); if( pDb->pSchema->pSeqTab==0 ){ @@ -114298,6 +120528,7 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( Table *pOld; Schema *pSchema = p->pSchema; assert( wx_sqlite3SchemaMutexHeld(db, iDb, 0) ); + assert( HasRowid(p) || p->iPKey<0 ); pOld = wx_sqlite3HashInsert(&pSchema->tblHash, p->zName, p); if( pOld ){ assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ @@ -114306,15 +120537,26 @@ SQLITE_PRIVATE void wx_sqlite3EndTable( } pParse->pNewTable = 0; db->mDbFlags |= DBFLAG_SchemaChange; + + /* If this is the magic sqlite_sequence table used by autoincrement, + ** then record a pointer to this table in the main database structure + ** so that INSERT can find the table easily. */ + assert( !pParse->nested ); +#ifndef SQLITE_OMIT_AUTOINCREMENT + if( strcmp(p->zName, "sqlite_sequence")==0 ){ + assert( wx_sqlite3SchemaMutexHeld(db, iDb, 0) ); + p->pSchema->pSeqTab = p; + } +#endif } #ifndef SQLITE_OMIT_ALTERTABLE - if( !pSelect && !p->pSelect ){ + if( !pSelect && IsOrdinaryTable(p) ){ assert( pCons && pEnd ); if( pCons->z==0 ){ pCons = pEnd; } - p->addColOffset = 13 + (int)(pCons->z - pParse->sNameToken.z); + p->u.tab.addColOffset = 13 + (int)(pCons->z - pParse->sNameToken.z); } #endif } @@ -114349,6 +120591,16 @@ SQLITE_PRIVATE void wx_sqlite3CreateView( wx_sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); p = pParse->pNewTable; if( p==0 || pParse->nErr ) goto create_view_fail; + + /* Legacy versions of SQLite allowed the use of the magic "rowid" column + ** on a view, even though views do not have rowids. The following flag + ** setting fixes this problem. But the fix can be disabled by compiling + ** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that + ** depend upon the old buggy behavior. */ +#ifndef SQLITE_ALLOW_ROWID_IN_VIEW + p->tabFlags |= TF_NoVisibleRowid; +#endif + wx_sqlite3TwoPartName(pParse, pName1, pName2, &pName); iDb = wx_sqlite3SchemaToIndex(db, p->pSchema); wx_sqlite3FixInit(&sFix, pParse, iDb, "view", pName); @@ -114361,12 +120613,13 @@ SQLITE_PRIVATE void wx_sqlite3CreateView( */ pSelect->selFlags |= SF_View; if( IN_RENAME_OBJECT ){ - p->pSelect = pSelect; + p->u.view.pSelect = pSelect; pSelect = 0; }else{ - p->pSelect = wx_sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); + p->u.view.pSelect = wx_sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); } p->pCheck = wx_sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE); + p->eTabType = TABTYP_VIEW; if( db->mallocFailed ) goto create_view_fail; /* Locate the end of the CREATE VIEW statement. Make sEnd point to @@ -114404,11 +120657,10 @@ create_view_fail: ** the columns of the view in the pTable structure. Return the number ** of errors. If an error is seen leave an error message in pParse->zErrMsg. */ -SQLITE_PRIVATE int wx_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ +static SQLITE_NOINLINE int viewGetColumnNames(Parse *pParse, Table *pTable){ Table *pSelTab; /* A fake table from which we get the result set */ Select *pSel; /* Copy of the SELECT that implements the view */ int nErr = 0; /* Number of errors encountered */ - int n; /* Temporarily holds the number of cursors assigned */ wx_sqlite3 *db = pParse->db; /* Database connection for malloc errors */ #ifndef SQLITE_OMIT_VIRTUALTABLE int rc; @@ -114420,20 +120672,20 @@ SQLITE_PRIVATE int wx_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ assert( pTable ); #ifndef SQLITE_OMIT_VIRTUALTABLE - db->nSchemaLock++; - rc = wx_sqlite3VtabCallConnect(pParse, pTable); - db->nSchemaLock--; - if( rc ){ - return 1; + if( IsVirtual(pTable) ){ + db->nSchemaLock++; + rc = wx_sqlite3VtabCallConnect(pParse, pTable); + db->nSchemaLock--; + return rc; } - if( IsVirtual(pTable) ) return 0; #endif #ifndef SQLITE_OMIT_VIEW /* A positive nCol means the columns names for this view are - ** already known. + ** already known. This routine is not called unless either the + ** table is virtual or nCol is zero. */ - if( pTable->nCol>0 ) return 0; + assert( pTable->nCol<=0 ); /* A negative nCol is a special marker meaning that we are currently ** trying to compute the column names. If we enter this routine with @@ -114463,12 +120715,13 @@ SQLITE_PRIVATE int wx_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ ** to be permanent. So the computation is done on a copy of the SELECT ** statement that defines the view. */ - assert( pTable->pSelect ); - pSel = wx_sqlite3SelectDup(db, pTable->pSelect, 0); + assert( IsView(pTable) ); + pSel = wx_sqlite3SelectDup(db, pTable->u.view.pSelect, 0); if( pSel ){ u8 eParseMode = pParse->eParseMode; + int nTab = pParse->nTab; + int nSelect = pParse->nSelect; pParse->eParseMode = PARSE_MODE_NORMAL; - n = pParse->nTab; wx_sqlite3SrcListAssignCursors(pParse, pSel->pSrc); pTable->nCol = -1; DisableLookaside; @@ -114480,7 +120733,8 @@ SQLITE_PRIVATE int wx_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ #else pSelTab = wx_sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE); #endif - pParse->nTab = n; + pParse->nTab = nTab; + pParse->nSelect = nSelect; if( pSelTab==0 ){ pTable->nCol = 0; nErr++; @@ -114493,12 +120747,11 @@ SQLITE_PRIVATE int wx_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ */ wx_sqlite3ColumnsFromExprList(pParse, pTable->pCheck, &pTable->nCol, &pTable->aCol); - if( db->mallocFailed==0 - && pParse->nErr==0 + if( pParse->nErr==0 && pTable->nCol==pSel->pEList->nExpr ){ - wx_sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel, - SQLITE_AFF_NONE); + assert( db->mallocFailed==0 ); + wx_sqlite3SubqueryColumnTypes(pParse, pTable, pSel, SQLITE_AFF_NONE); } }else{ /* CREATE VIEW name AS... without an argument list. Construct @@ -114523,12 +120776,15 @@ SQLITE_PRIVATE int wx_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ pTable->pSchema->schemaFlags |= DB_UnresetViews; if( db->mallocFailed ){ wx_sqlite3DeleteColumnNames(db, pTable); - pTable->aCol = 0; - pTable->nCol = 0; } #endif /* SQLITE_OMIT_VIEW */ return nErr; } +SQLITE_PRIVATE int wx_sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ + assert( pTable!=0 ); + if( !IsVirtual(pTable) && pTable->nCol>0 ) return 0; + return viewGetColumnNames(pParse, pTable); +} #endif /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */ #ifndef SQLITE_OMIT_VIEW @@ -114541,10 +120797,8 @@ static void sqliteViewResetAll(wx_sqlite3 *db, int idx){ if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); - if( pTab->pSelect ){ + if( IsView(pTab) ){ wx_sqlite3DeleteColumnNames(db, pTab); - pTab->aCol = 0; - pTab->nCol = 0; } } DbClearProperty(db, idx, DB_UnresetViews); @@ -114618,7 +120872,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){ ** token for additional information. */ wx_sqlite3NestedParse(pParse, - "UPDATE %Q." DFLT_SCHEMA_TABLE + "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET rootpage=%d WHERE #%d AND rootpage=#%d", pParse->db->aDb[iDb].zDbSName, iTable, r1, r1); #endif @@ -114753,7 +121007,7 @@ SQLITE_PRIVATE void wx_sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, ** database. */ wx_sqlite3NestedParse(pParse, - "DELETE FROM %Q." DFLT_SCHEMA_TABLE + "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE tbl_name=%Q and type!='trigger'", pDb->zDbSName, pTab->zName); if( !isView && !IsVirtual(pTab) ){ @@ -114781,6 +121035,7 @@ SQLITE_PRIVATE int wx_sqlite3ReadOnlyShadowTables(wx_sqlite3 *db){ if( (db->flags & SQLITE_Defensive)!=0 && db->pVtabCtx==0 && db->nVdbeExec==0 + && !wx_sqlite3VtabInSync(db) ){ return 1; } @@ -114800,6 +121055,9 @@ static int tableMayNotBeDropped(wx_sqlite3 *db, Table *pTab){ if( (pTab->tabFlags & TF_Shadow)!=0 && wx_sqlite3ReadOnlyShadowTables(db) ){ return 1; } + if( pTab->tabFlags & TF_Eponymous ){ + return 1; + } return 0; } @@ -114825,7 +121083,10 @@ SQLITE_PRIVATE void wx_sqlite3DropTable(Parse *pParse, SrcList *pName, int isVie if( noErr ) db->suppressErr--; if( pTab==0 ){ - if( noErr ) wx_sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + if( noErr ){ + wx_sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + wx_sqlite3ForceNotReadOnly(pParse); + } goto exit_drop_table; } iDb = wx_sqlite3SchemaToIndex(db, pTab->pSchema); @@ -114881,11 +121142,11 @@ SQLITE_PRIVATE void wx_sqlite3DropTable(Parse *pParse, SrcList *pName, int isVie /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used ** on a table. */ - if( isView && pTab->pSelect==0 ){ + if( isView && !IsView(pTab) ){ wx_sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName); goto exit_drop_table; } - if( !isView && pTab->pSelect ){ + if( !isView && IsView(pTab) ){ wx_sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName); goto exit_drop_table; } @@ -114936,7 +121197,7 @@ SQLITE_PRIVATE void wx_sqlite3CreateForeignKey( FKey *pFKey = 0; FKey *pNextTo; Table *p = pParse->pNewTable; - int nByte; + i64 nByte; int i; int nCol; char *z; @@ -114949,7 +121210,7 @@ SQLITE_PRIVATE void wx_sqlite3CreateForeignKey( if( pToCol && pToCol->nExpr!=1 ){ wx_sqlite3ErrorMsg(pParse, "foreign key on %s" " should reference only one column of table %T", - p->aCol[iCol].zName, pTo); + p->aCol[iCol].zCnName, pTo); goto fk_end; } nCol = 1; @@ -114972,7 +121233,8 @@ SQLITE_PRIVATE void wx_sqlite3CreateForeignKey( goto fk_end; } pFKey->pFrom = p; - pFKey->pNextFrom = p->pFKey; + assert( IsOrdinaryTable(p) ); + pFKey->pNextFrom = p->u.tab.pFKey; z = (char*)&pFKey->aCol[nCol]; pFKey->zTo = z; if( IN_RENAME_OBJECT ){ @@ -114989,7 +121251,7 @@ SQLITE_PRIVATE void wx_sqlite3CreateForeignKey( for(i=0; inCol; j++){ - if( wx_sqlite3StrICmp(p->aCol[j].zName, pFromCol->a[i].zEName)==0 ){ + if( wx_sqlite3StrICmp(p->aCol[j].zCnName, pFromCol->a[i].zEName)==0 ){ pFKey->aCol[i].iFrom = j; break; } @@ -115037,7 +121299,8 @@ SQLITE_PRIVATE void wx_sqlite3CreateForeignKey( /* Link the foreign key to the table as the last step. */ - p->pFKey = pFKey; + assert( IsOrdinaryTable(p) ); + p->u.tab.pFKey = pFKey; pFKey = 0; fk_end: @@ -115058,7 +121321,9 @@ SQLITE_PRIVATE void wx_sqlite3DeferForeignKey(Parse *pParse, int isDeferred){ #ifndef SQLITE_OMIT_FOREIGN_KEY Table *pTab; FKey *pFKey; - if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return; + if( (pTab = pParse->pNewTable)==0 ) return; + if( NEVER(!IsOrdinaryTable(pTab)) ) return; + if( (pFKey = pTab->u.tab.pFKey)==0 ) return; assert( isDeferred==0 || isDeferred==1 ); /* EV: R-30323-21917 */ pFKey->isDeferred = (u8)isDeferred; #endif @@ -115108,7 +121373,7 @@ static void wx_sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage) tnum = pIndex->tnum; } pKey = wx_sqlite3KeyInfoOfIndex(pParse, pIndex); - assert( pKey!=0 || db->mallocFailed || pParse->nErr ); + assert( pKey!=0 || pParse->nErr ); /* Open the sorter cursor if we are to use one. */ iSorter = pParse->nTab++; @@ -115218,8 +121483,8 @@ SQLITE_PRIVATE int wx_sqlite3HasExplicitNulls(Parse *pParse, ExprList *pList){ if( pList ){ int i; for(i=0; inExpr; i++){ - if( pList->a[i].bNulls ){ - u8 sf = pList->a[i].sortFlags; + if( pList->a[i].fg.bNulls ){ + u8 sf = pList->a[i].fg.sortFlags; wx_sqlite3ErrorMsg(pParse, "unsupported use of NULLS %s", (sf==0 || sf==3) ? "FIRST" : "LAST" ); @@ -115272,9 +121537,11 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( char *zExtra = 0; /* Extra space after the Index object */ Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */ - if( db->mallocFailed || pParse->nErr>0 ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto exit_create_index; } + assert( db->mallocFailed==0 ); if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){ goto exit_create_index; } @@ -115338,7 +121605,6 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( pDb = &db->aDb[iDb]; assert( pTab!=0 ); - assert( pParse->nErr==0 ); if( wx_sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 && db->init.busy==0 && pTblName!=0 @@ -115350,7 +121616,7 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( goto exit_create_index; } #ifndef SQLITE_OMIT_VIEW - if( pTab->pSelect ){ + if( IsView(pTab) ){ wx_sqlite3ErrorMsg(pParse, "views may not be indexed"); goto exit_create_index; } @@ -115384,7 +121650,7 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( } if( !IN_RENAME_OBJECT ){ if( !db->init.busy ){ - if( wx_sqlite3FindTable(db, zName, 0)!=0 ){ + if( wx_sqlite3FindTable(db, zName, pDb->zDbSName)!=0 ){ wx_sqlite3ErrorMsg(pParse, "there is already a table named %s", zName); goto exit_create_index; } @@ -115395,6 +121661,7 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( }else{ assert( !db->init.busy ); wx_sqlite3CodeVerifySchema(pParse, iDb); + wx_sqlite3ForceNotReadOnly(pParse); } goto exit_create_index; } @@ -115440,7 +121707,7 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( Token prevCol; Column *pCol = &pTab->aCol[pTab->nCol-1]; pCol->colFlags |= COLFLAG_UNIQUE; - wx_sqlite3TokenInit(&prevCol, pCol->zName); + wx_sqlite3TokenInit(&prevCol, pCol->zCnName); pList = wx_sqlite3ExprListAppend(pParse, 0, wx_sqlite3ExprAlloc(db, TK_ID, &prevCol, 0)); if( pList==0 ) goto exit_create_index; @@ -115458,6 +121725,7 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( Expr *pExpr = pList->a[i].pExpr; assert( pExpr!=0 ); if( pExpr->op==TK_COLLATE ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); nExtra += (1 + wx_sqlite3Strlen30(pExpr->u.zToken)); } } @@ -115535,6 +121803,7 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( j = XN_EXPR; pIndex->aiColumn[i] = XN_EXPR; pIndex->uniqNotNull = 0; + pIndex->bHasExpr = 1; }else{ j = pCExpr->iColumn; assert( j<=0x7fff ); @@ -115546,6 +121815,7 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( } if( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ){ pIndex->bHasVCol = 1; + pIndex->bHasExpr = 1; } } pIndex->aiColumn[i] = (i16)j; @@ -115553,6 +121823,7 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( zColl = 0; if( pListItem->pExpr->op==TK_COLLATE ){ int nColl; + assert( !ExprHasProperty(pListItem->pExpr, EP_IntValue) ); zColl = pListItem->pExpr->u.zToken; nColl = wx_sqlite3Strlen30(zColl) + 1; assert( nExtra>=nColl ); @@ -115561,14 +121832,14 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( zExtra += nColl; nExtra -= nColl; }else if( j>=0 ){ - zColl = pTab->aCol[j].zColl; + zColl = wx_sqlite3ColumnColl(&pTab->aCol[j]); } if( !zColl ) zColl = wx_sqlite3StrBINARY; if( !db->init.busy && !wx_sqlite3LocateCollSeq(pParse, zColl) ){ goto exit_create_index; } pIndex->azColl[i] = zColl; - requestedSortOrder = pListItem->sortFlags & sortOrderMask; + requestedSortOrder = pListItem->fg.sortFlags & sortOrderMask; pIndex->aSortOrder[i] = (u8)requestedSortOrder; } @@ -115759,13 +122030,13 @@ SQLITE_PRIVATE void wx_sqlite3CreateIndex( /* Add an entry in sqlite_schema for this index */ wx_sqlite3NestedParse(pParse, - "INSERT INTO %Q." DFLT_SCHEMA_TABLE " VALUES('index',%Q,%Q,#%d,%Q);", - db->aDb[iDb].zDbSName, - pIndex->zName, - pTab->zName, - iMem, - zStmt - ); + "INSERT INTO %Q." LEGACY_SCHEMA_TABLE " VALUES('index',%Q,%Q,#%d,%Q);", + db->aDb[iDb].zDbSName, + pIndex->zName, + pTab->zName, + iMem, + zStmt + ); wx_sqlite3DbFree(db, zStmt); /* Fill the index with data and reparse the schema. Code an OP_Expire @@ -115801,7 +122072,7 @@ exit_create_index: ** The list was already ordered when this routine was entered, so at this ** point at most a single index (the newly added index) will be out of ** order. So we have to reorder at most one index. */ - Index **ppFrom = &pTab->pIndex; + Index **ppFrom; Index *pThis; for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){ Index *pNext; @@ -115875,7 +122146,7 @@ SQLITE_PRIVATE void wx_sqlite3DefaultRowEst(Index *pIdx){ if( x<99 ){ pIdx->pTable->nRowLogEst = x = 99; } - if( pIdx->pPartIdxWhere!=0 ) x -= 10; assert( 10==wx_sqlite3LogEst(2) ); + if( pIdx->pPartIdxWhere!=0 ){ x -= 10; assert( 10==wx_sqlite3LogEst(2) ); } a[0] = x; /* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is @@ -115899,10 +122170,10 @@ SQLITE_PRIVATE void wx_sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExi wx_sqlite3 *db = pParse->db; int iDb; - assert( pParse->nErr==0 ); /* Never called with prior errors */ if( db->mallocFailed ){ goto exit_drop_index; } + assert( pParse->nErr==0 ); /* Never called with prior non-OOM errors */ assert( pName->nSrc==1 ); if( SQLITE_OK!=wx_sqlite3ReadSchema(pParse) ){ goto exit_drop_index; @@ -115910,9 +122181,10 @@ SQLITE_PRIVATE void wx_sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExi pIndex = wx_sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase); if( pIndex==0 ){ if( !ifExists ){ - wx_sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0); + wx_sqlite3ErrorMsg(pParse, "no such index: %S", pName->a); }else{ wx_sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + wx_sqlite3ForceNotReadOnly(pParse); } pParse->checkSchema = 1; goto exit_drop_index; @@ -115932,7 +122204,7 @@ SQLITE_PRIVATE void wx_sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExi if( wx_sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ goto exit_drop_index; } - if( !OMIT_TEMPDB && iDb ) code = SQLITE_DROP_TEMP_INDEX; + if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX; if( wx_sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ goto exit_drop_index; } @@ -115944,7 +122216,7 @@ SQLITE_PRIVATE void wx_sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExi if( v ){ wx_sqlite3BeginWriteOperation(pParse, 1, iDb); wx_sqlite3NestedParse(pParse, - "DELETE FROM %Q." DFLT_SCHEMA_TABLE " WHERE name=%Q AND type='index'", + "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='index'", db->aDb[iDb].zDbSName, pIndex->zName ); wx_sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName); @@ -116010,18 +122282,17 @@ SQLITE_PRIVATE IdList *wx_sqlite3IdListAppend(Parse *pParse, IdList *pList, Toke if( pList==0 ){ pList = wx_sqlite3DbMallocZero(db, sizeof(IdList) ); if( pList==0 ) return 0; + }else{ + IdList *pNew; + pNew = wx_sqlite3DbRealloc(db, pList, + sizeof(IdList) + pList->nId*sizeof(pList->a)); + if( pNew==0 ){ + wx_sqlite3IdListDelete(db, pList); + return 0; + } + pList = pNew; } - pList->a = wx_sqlite3ArrayAllocate( - db, - pList->a, - sizeof(pList->a[0]), - &pList->nId, - &i - ); - if( i<0 ){ - wx_sqlite3IdListDelete(db, pList); - return 0; - } + i = pList->nId++; pList->a[i].zName = wx_sqlite3NameFromToken(db, pToken); if( IN_RENAME_OBJECT && pList->a[i].zName ){ wx_sqlite3RenameTokenMap(pParse, (void*)pList->a[i].zName, pToken); @@ -116034,12 +122305,13 @@ SQLITE_PRIVATE IdList *wx_sqlite3IdListAppend(Parse *pParse, IdList *pList, Toke */ SQLITE_PRIVATE void wx_sqlite3IdListDelete(wx_sqlite3 *db, IdList *pList){ int i; + assert( db!=0 ); if( pList==0 ) return; + assert( pList->eU4!=EU4_EXPR ); /* EU4_EXPR mode is not currently used */ for(i=0; inId; i++){ wx_sqlite3DbFree(db, pList->a[i].zName); } - wx_sqlite3DbFree(db, pList->a); - wx_sqlite3DbFreeNN(db, pList); + wx_sqlite3DbNNFreeNN(db, pList); } /* @@ -116048,7 +122320,7 @@ SQLITE_PRIVATE void wx_sqlite3IdListDelete(wx_sqlite3 *db, IdList *pList){ */ SQLITE_PRIVATE int wx_sqlite3IdListIndex(IdList *pList, const char *zName){ int i; - if( pList==0 ) return -1; + assert( pList!=0 ); for(i=0; inId; i++){ if( wx_sqlite3StrICmp(pList->a[i].zName, zName)==0 ) return i; } @@ -116224,8 +122496,8 @@ SQLITE_PRIVATE SrcList *wx_sqlite3SrcListAppend( SQLITE_PRIVATE void wx_sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){ int i; SrcItem *pItem; - assert(pList || pParse->db->mallocFailed ); - if( pList ){ + assert( pList || pParse->db->mallocFailed ); + if( ALWAYS(pList) ){ for(i=0, pItem=pList->a; inSrc; i++, pItem++){ if( pItem->iCursor>=0 ) continue; pItem->iCursor = pParse->nTab++; @@ -116242,19 +122514,23 @@ SQLITE_PRIVATE void wx_sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList SQLITE_PRIVATE void wx_sqlite3SrcListDelete(wx_sqlite3 *db, SrcList *pList){ int i; SrcItem *pItem; + assert( db!=0 ); if( pList==0 ) return; for(pItem=pList->a, i=0; inSrc; i++, pItem++){ - if( pItem->zDatabase ) wx_sqlite3DbFreeNN(db, pItem->zDatabase); - wx_sqlite3DbFree(db, pItem->zName); - if( pItem->zAlias ) wx_sqlite3DbFreeNN(db, pItem->zAlias); + if( pItem->zDatabase ) wx_sqlite3DbNNFreeNN(db, pItem->zDatabase); + if( pItem->zName ) wx_sqlite3DbNNFreeNN(db, pItem->zName); + if( pItem->zAlias ) wx_sqlite3DbNNFreeNN(db, pItem->zAlias); if( pItem->fg.isIndexedBy ) wx_sqlite3DbFree(db, pItem->u1.zIndexedBy); if( pItem->fg.isTabFunc ) wx_sqlite3ExprListDelete(db, pItem->u1.pFuncArg); wx_sqlite3DeleteTable(db, pItem->pTab); if( pItem->pSelect ) wx_sqlite3SelectDelete(db, pItem->pSelect); - if( pItem->pOn ) wx_sqlite3ExprDelete(db, pItem->pOn); - if( pItem->pUsing ) wx_sqlite3IdListDelete(db, pItem->pUsing); + if( pItem->fg.isUsing ){ + wx_sqlite3IdListDelete(db, pItem->u3.pUsing); + }else if( pItem->u3.pOn ){ + wx_sqlite3ExprDelete(db, pItem->u3.pOn); + } } - wx_sqlite3DbFreeNN(db, pList); + wx_sqlite3DbNNFreeNN(db, pList); } /* @@ -116280,14 +122556,13 @@ SQLITE_PRIVATE SrcList *wx_sqlite3SrcListAppendFromTerm( Token *pDatabase, /* Name of the database containing pTable */ Token *pAlias, /* The right-hand side of the AS subexpression */ Select *pSubquery, /* A subquery used in place of a table name */ - Expr *pOn, /* The ON clause of a join */ - IdList *pUsing /* The USING clause of a join */ + OnOrUsing *pOnUsing /* Either the ON clause or the USING clause */ ){ SrcItem *pItem; wx_sqlite3 *db = pParse->db; - if( !p && (pOn || pUsing) ){ + if( !p && pOnUsing!=0 && (pOnUsing->pOn || pOnUsing->pUsing) ){ wx_sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s", - (pOn ? "ON" : "USING") + (pOnUsing->pOn ? "ON" : "USING") ); goto append_from_error; } @@ -116307,15 +122582,27 @@ SQLITE_PRIVATE SrcList *wx_sqlite3SrcListAppendFromTerm( if( pAlias->n ){ pItem->zAlias = wx_sqlite3NameFromToken(db, pAlias); } - pItem->pSelect = pSubquery; - pItem->pOn = pOn; - pItem->pUsing = pUsing; + if( pSubquery ){ + pItem->pSelect = pSubquery; + if( pSubquery->selFlags & SF_NestedFrom ){ + pItem->fg.isNestedFrom = 1; + } + } + assert( pOnUsing==0 || pOnUsing->pOn==0 || pOnUsing->pUsing==0 ); + assert( pItem->fg.isUsing==0 ); + if( pOnUsing==0 ){ + pItem->u3.pOn = 0; + }else if( pOnUsing->pUsing ){ + pItem->fg.isUsing = 1; + pItem->u3.pUsing = pOnUsing->pUsing; + }else{ + pItem->u3.pOn = pOnUsing->pOn; + } return p; - append_from_error: +append_from_error: assert( p==0 ); - wx_sqlite3ExprDelete(db, pOn); - wx_sqlite3IdListDelete(db, pUsing); + wx_sqlite3ClearOnOrUsing(db, pOnUsing); wx_sqlite3SelectDelete(db, pSubquery); return 0; } @@ -116340,6 +122627,7 @@ SQLITE_PRIVATE void wx_sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token }else{ pItem->u1.zIndexedBy = wx_sqlite3NameFromToken(pParse->db, pIndexedBy); pItem->fg.isIndexedBy = 1; + assert( pItem->fg.isCte==0 ); /* No collision on union u2 */ } } } @@ -116359,6 +122647,7 @@ SQLITE_PRIVATE SrcList *wx_sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, p1 = pNew; memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem)); wx_sqlite3DbFree(pParse->db, p2); + p1->a[0].fg.jointype |= (JT_LTORJ & p1->a[1].fg.jointype); } } return p1; @@ -116395,14 +122684,34 @@ SQLITE_PRIVATE void wx_sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprLis ** The operator is "natural cross join". The A and B operands are stored ** in p->a[0] and p->a[1], respectively. The parser initially stores the ** operator with A. This routine shifts that operator over to B. +** +** Additional changes: +** +** * All tables to the left of the right-most RIGHT JOIN are tagged with +** JT_LTORJ (mnemonic: Left Table Of Right Join) so that the +** code generator can easily tell that the table is part of +** the left operand of at least one RIGHT JOIN. */ -SQLITE_PRIVATE void wx_sqlite3SrcListShiftJoinType(SrcList *p){ - if( p ){ - int i; - for(i=p->nSrc-1; i>0; i--){ - p->a[i].fg.jointype = p->a[i-1].fg.jointype; - } +SQLITE_PRIVATE void wx_sqlite3SrcListShiftJoinType(Parse *pParse, SrcList *p){ + (void)pParse; + if( p && p->nSrc>1 ){ + int i = p->nSrc-1; + u8 allFlags = 0; + do{ + allFlags |= p->a[i].fg.jointype = p->a[i-1].fg.jointype; + }while( (--i)>0 ); p->a[0].fg.jointype = 0; + + /* All terms to the left of a RIGHT JOIN should be tagged with the + ** JT_LTORJ flags */ + if( allFlags & JT_RIGHT ){ + for(i=p->nSrc-1; ALWAYS(i>0) && (p->a[i].fg.jointype&JT_RIGHT)==0; i--){} + i--; + assert( i>=0 ); + do{ + p->a[i].fg.jointype |= JT_LTORJ; + }while( (--i)>=0 ); + } } } @@ -116652,7 +122961,7 @@ SQLITE_PRIVATE void wx_sqlite3UniqueConstraint( for(j=0; jnKeyCol; j++){ char *zCol; assert( pIdx->aiColumn[j]>=0 ); - zCol = pTab->aCol[pIdx->aiColumn[j]].zName; + zCol = pTab->aCol[pIdx->aiColumn[j]].zCnName; if( j ) wx_sqlite3_str_append(&errMsg, ", ", 2); wx_sqlite3_str_appendall(&errMsg, pTab->zName); wx_sqlite3_str_append(&errMsg, ".", 1); @@ -116679,7 +122988,7 @@ SQLITE_PRIVATE void wx_sqlite3RowidConstraint( int rc; if( pTab->iPKey>=0 ){ zMsg = wx_sqlite3MPrintf(pParse->db, "%s.%s", pTab->zName, - pTab->aCol[pTab->iPKey].zName); + pTab->aCol[pTab->iPKey].zCnName); rc = SQLITE_CONSTRAINT_PRIMARYKEY; }else{ zMsg = wx_sqlite3MPrintf(pParse->db, "%s.rowid", pTab->zName); @@ -117320,6 +123629,7 @@ SQLITE_PRIVATE FuncDef *wx_sqlite3FunctionSearch( ){ FuncDef *p; for(p=wx_sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){ + assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); if( wx_sqlite3StrICmp(p->zName, zFunc)==0 ){ return p; } @@ -117340,7 +123650,7 @@ SQLITE_PRIVATE void wx_sqlite3InsertBuiltinFuncs( const char *zName = aDef[i].zName; int nName = wx_sqlite3Strlen30(zName); int h = SQLITE_FUNC_HASH(zName[0], nName); - assert( zName[0]>='a' && zName[0]<='z' ); + assert( aDef[i].funcFlags & SQLITE_FUNC_BUILTIN ); pOther = wx_sqlite3FunctionSearch(h, zName); if( pOther ){ assert( pOther!=&aDef[i] && pOther->pNext!=&aDef[i] ); @@ -117472,19 +123782,21 @@ SQLITE_PRIVATE void wx_sqlite3SchemaClear(void *p){ Hash temp2; HashElem *pElem; Schema *pSchema = (Schema *)p; + wx_sqlite3 xdb; + memset(&xdb, 0, sizeof(xdb)); temp1 = pSchema->tblHash; temp2 = pSchema->trigHash; wx_sqlite3HashInit(&pSchema->trigHash); wx_sqlite3HashClear(&pSchema->idxHash); for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){ - wx_sqlite3DeleteTrigger(0, (Trigger*)sqliteHashData(pElem)); + wx_sqlite3DeleteTrigger(&xdb, (Trigger*)sqliteHashData(pElem)); } wx_sqlite3HashClear(&temp2); wx_sqlite3HashInit(&pSchema->tblHash); for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ Table *pTab = sqliteHashData(pElem); - wx_sqlite3DeleteTable(0, pTab); + wx_sqlite3DeleteTable(&xdb, pTab); } wx_sqlite3HashClear(&temp1); wx_sqlite3HashClear(&pSchema->fkeyHash); @@ -117566,6 +123878,16 @@ SQLITE_PRIVATE Table *wx_sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ return pTab; } +/* Generate byte-code that will report the number of rows modified +** by a DELETE, INSERT, or UPDATE statement. +*/ +SQLITE_PRIVATE void wx_sqlite3CodeChangeCount(Vdbe *v, int regCounter, const char *zColName){ + wx_sqlite3VdbeAddOp0(v, OP_FkCheck); + wx_sqlite3VdbeAddOp2(v, OP_ResultRow, regCounter, 1); + wx_sqlite3VdbeSetNumCols(v, 1); + wx_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zColName, SQLITE_STATIC); +} + /* Return true if table pTab is read-only. ** ** A table is read-only if any of the following are true: @@ -117573,18 +123895,42 @@ SQLITE_PRIVATE Table *wx_sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ ** 1) It is a virtual table and no implementation of the xUpdate method ** has been provided ** -** 2) It is a system table (i.e. sqlite_schema), this call is not +** 2) A trigger is currently being coded and the table is a virtual table +** that is SQLITE_VTAB_DIRECTONLY or if PRAGMA trusted_schema=OFF and +** the table is not SQLITE_VTAB_INNOCUOUS. +** +** 3) It is a system table (i.e. sqlite_schema), this call is not ** part of a nested parse and writable_schema pragma has not ** been specified ** -** 3) The table is a shadow table, the database connection is in +** 4) The table is a shadow table, the database connection is in ** defensive mode, and the current wx_sqlite3_prepare() ** is for a top-level SQL statement. */ +static int vtabIsReadOnly(Parse *pParse, Table *pTab){ + if( wx_sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){ + return 1; + } + + /* Within triggers: + ** * Do not allow DELETE, INSERT, or UPDATE of SQLITE_VTAB_DIRECTONLY + ** virtual tables + ** * Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS + ** virtual tables if PRAGMA trusted_schema=ON. + */ + if( pParse->pToplevel!=0 + && pTab->u.vtab.p->eVtabRisk > + ((pParse->db->flags & SQLITE_TrustedSchema)!=0) + ){ + wx_sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", + pTab->zName); + } + return 0; +} static int tabIsReadOnly(Parse *pParse, Table *pTab){ wx_sqlite3 *db; if( IsVirtual(pTab) ){ - return wx_sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0; + return vtabIsReadOnly(pParse, pTab); } if( (pTab->tabFlags & (TF_Readonly|TF_Shadow))==0 ) return 0; db = pParse->db; @@ -117596,9 +123942,11 @@ static int tabIsReadOnly(Parse *pParse, Table *pTab){ } /* -** Check to make sure the given table is writable. If it is not -** writable, generate an error message and return 1. If it is -** writable return 0; +** Check to make sure the given table is writable. +** +** If pTab is not writable -> generate an error message and return 1. +** If pTab is writable but other errors have occurred -> return 1. +** If pTab is writable and no prior errors -> return 0; */ SQLITE_PRIVATE int wx_sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ if( tabIsReadOnly(pParse, pTab) ){ @@ -117606,7 +123954,7 @@ SQLITE_PRIVATE int wx_sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ return 1; } #ifndef SQLITE_OMIT_VIEW - if( !viewOk && pTab->pSelect ){ + if( !viewOk && IsView(pTab) ){ wx_sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); return 1; } @@ -117640,8 +123988,8 @@ SQLITE_PRIVATE void wx_sqlite3MaterializeView( assert( pFrom->nSrc==1 ); pFrom->a[0].zName = wx_sqlite3DbStrDup(db, pView->zName); pFrom->a[0].zDatabase = wx_sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); - assert( pFrom->a[0].pOn==0 ); - assert( pFrom->a[0].pUsing==0 ); + assert( pFrom->a[0].fg.isUsing==0 ); + assert( pFrom->a[0].u3.pOn==0 ); } pSel = wx_sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy, SF_IncludeHidden, pLimit); @@ -117710,13 +124058,13 @@ SQLITE_PRIVATE Expr *wx_sqlite3LimitWhere( }else{ Index *pPk = wx_sqlite3PrimaryKeyIndex(pTab); if( pPk->nKeyCol==1 ){ - const char *zName = pTab->aCol[pPk->aiColumn[0]].zName; + const char *zName = pTab->aCol[pPk->aiColumn[0]].zCnName; pLhs = wx_sqlite3Expr(db, TK_ID, zName); pEList = wx_sqlite3ExprListAppend(pParse, 0, wx_sqlite3Expr(db, TK_ID, zName)); }else{ int i; for(i=0; inKeyCol; i++){ - Expr *p = wx_sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName); + Expr *p = wx_sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zCnName); pEList = wx_sqlite3ExprListAppend(pParse, pEList, p); } pLhs = wx_sqlite3PExpr(pParse, TK_VECTOR, 0, 0); @@ -117732,6 +124080,7 @@ SQLITE_PRIVATE Expr *wx_sqlite3LimitWhere( pSelectSrc = wx_sqlite3SrcListDup(db, pSrc, 0); pSrc->a[0].pTab = pTab; if( pSrc->a[0].fg.isIndexedBy ){ + assert( pSrc->a[0].fg.isCte==0 ); pSrc->a[0].u2.pIBIndex = 0; pSrc->a[0].fg.isIndexedBy = 0; wx_sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy); @@ -117804,12 +124153,13 @@ SQLITE_PRIVATE void wx_sqlite3DeleteFrom( memset(&sContext, 0, sizeof(sContext)); db = pParse->db; - if( pParse->nErr || db->mallocFailed ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto delete_from_cleanup; } + assert( db->mallocFailed==0 ); assert( pTabList->nSrc==1 ); - /* Locate the table which we want to delete. This table has to be ** put in an SrcList structure because some of the subroutines we ** will be calling are designed to work with multiple tables and expect @@ -117823,7 +124173,7 @@ SQLITE_PRIVATE void wx_sqlite3DeleteFrom( */ #ifndef SQLITE_OMIT_TRIGGER pTrigger = wx_sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); - isView = pTab->pSelect!=0; + isView = IsView(pTab); #else # define pTrigger 0 # define isView 0 @@ -117834,6 +124184,14 @@ SQLITE_PRIVATE void wx_sqlite3DeleteFrom( # define isView 0 #endif +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x10000 ){ + wx_sqlite3TreeViewLine(0, "In wx_sqlite3Delete() at %s:%d", __FILE__, __LINE__); + wx_sqlite3TreeViewDelete(pParse->pWith, pTabList, pWhere, + pOrderBy, pLimit, pTrigger); + } +#endif + #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT if( !isView ){ pWhere = wx_sqlite3LimitWhere( @@ -117949,13 +124307,17 @@ SQLITE_PRIVATE void wx_sqlite3DeleteFrom( } for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ assert( pIdx->pSchema==pTab->pSchema ); - wx_sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); + if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ + wx_sqlite3VdbeAddOp3(v, OP_Clear, pIdx->tnum, iDb, memCnt ? memCnt : -1); + }else{ + wx_sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); + } } }else #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ { u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK; - if( sNC.ncFlags & NC_VarSelect ) bComplex = 1; + if( sNC.ncFlags & NC_Subquery ) bComplex = 1; wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW); if( HasRowid(pTab) ){ /* For a rowid table, initialize the RowSet to an empty set */ @@ -117984,7 +124346,7 @@ SQLITE_PRIVATE void wx_sqlite3DeleteFrom( ** ONEPASS_SINGLE: One-pass approach - at most one row deleted. ** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted. */ - pWInfo = wx_sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1); + pWInfo = wx_sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,0,wcf,iTabCur+1); if( pWInfo==0 ) goto delete_from_cleanup; eOnePass = wx_sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI ); @@ -118070,7 +124432,7 @@ SQLITE_PRIVATE void wx_sqlite3DeleteFrom( if( eOnePass!=ONEPASS_OFF ){ assert( nKey==nPk ); /* OP_Found will use an unpacked key */ if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){ - assert( pPk!=0 || pTab->pSelect!=0 ); + assert( pPk!=0 || IsView(pTab) ); wx_sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); VdbeCoverage(v); } @@ -118137,9 +124499,7 @@ SQLITE_PRIVATE void wx_sqlite3DeleteFrom( ** invoke the callback function. */ if( memCnt ){ - wx_sqlite3VdbeAddOp2(v, OP_ChngCntRow, memCnt, 1); - wx_sqlite3VdbeSetNumCols(v, 1); - wx_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC); + wx_sqlite3CodeChangeCount(v, memCnt, "rows deleted"); } delete_from_cleanup: @@ -118150,7 +124510,7 @@ delete_from_cleanup: wx_sqlite3ExprListDelete(db, pOrderBy); wx_sqlite3ExprDelete(db, pLimit); #endif - wx_sqlite3DbFree(db, aToOpen); + if( aToOpen ) wx_sqlite3DbNNFreeNN(db, aToOpen); return; } /* Make sure "isView" and other macros defined above are undefined. Otherwise @@ -118304,7 +124664,7 @@ SQLITE_PRIVATE void wx_sqlite3GenerateRowDelete( ** the update-hook is not invoked for rows removed by REPLACE, but the ** pre-update-hook is. */ - if( pTab->pSelect==0 ){ + if( !IsView(pTab) ){ u8 p5 = 0; wx_sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek); wx_sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0)); @@ -118461,13 +124821,15 @@ SQLITE_PRIVATE int wx_sqlite3GenerateIndexKey( continue; } wx_sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j); - /* If the column affinity is REAL but the number is an integer, then it - ** might be stored in the table as an integer (using a compact - ** representation) then converted to REAL by an OP_RealAffinity opcode. - ** But we are getting ready to store this value back into an index, where - ** it should be converted by to INTEGER again. So omit the OP_RealAffinity - ** opcode if it is present */ - wx_sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); + if( pIdx->aiColumn[j]>=0 ){ + /* If the column affinity is REAL but the number is an integer, then it + ** might be stored in the table as an integer (using a compact + ** representation) then converted to REAL by an OP_RealAffinity opcode. + ** But we are getting ready to store this value back into an index, where + ** it should be converted by to INTEGER again. So omit the + ** OP_RealAffinity opcode if it is present */ + wx_sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); + } } if( regOut ){ wx_sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); @@ -118588,6 +124950,18 @@ static void typeofFunc( wx_sqlite3_result_text(context, azType[i], -1, SQLITE_STATIC); } +/* subtype(X) +** +** Return the subtype of X +*/ +static void subtypeFunc( + wx_sqlite3_context *context, + int argc, + wx_sqlite3_value **argv +){ + UNUSED_PARAMETER(argc); + wx_sqlite3_result_int(context, wx_sqlite3_value_subtype(argv[0])); +} /* ** Implementation of the length() function @@ -118749,7 +125123,7 @@ endInstrOOM: } /* -** Implementation of the printf() function. +** Implementation of the printf() (a.k.a. format()) SQL function. */ static void printfFunc( wx_sqlite3_context *context, @@ -119062,9 +125436,9 @@ static void last_insert_rowid( /* ** Implementation of the changes() SQL function. ** -** IMP: R-62073-11209 The changes() SQL function is a wrapper -** around the wx_sqlite3_changes() C/C++ function and hence follows the same -** rules for counting changes. +** IMP: R-32760-32347 The changes() SQL function is a wrapper +** around the wx_sqlite3_changes64() C/C++ function and hence follows the +** same rules for counting changes. */ static void changes( wx_sqlite3_context *context, @@ -119073,12 +125447,12 @@ static void changes( ){ wx_sqlite3 *db = wx_sqlite3_context_db_handle(context); UNUSED_PARAMETER2(NotUsed, NotUsed2); - wx_sqlite3_result_int(context, wx_sqlite3_changes(db)); + wx_sqlite3_result_int64(context, wx_sqlite3_changes64(db)); } /* ** Implementation of the total_changes() SQL function. The return value is -** the same as the wx_sqlite3_total_changes() API function. +** the same as the wx_sqlite3_total_changes64() API function. */ static void total_changes( wx_sqlite3_context *context, @@ -119087,9 +125461,9 @@ static void total_changes( ){ wx_sqlite3 *db = wx_sqlite3_context_db_handle(context); UNUSED_PARAMETER2(NotUsed, NotUsed2); - /* IMP: R-52756-41993 This function is a wrapper around the - ** wx_sqlite3_total_changes() C/C++ interface. */ - wx_sqlite3_result_int(context, wx_sqlite3_total_changes(db)); + /* IMP: R-11217-42568 This function is a wrapper around the + ** wx_sqlite3_total_changes64() C/C++ interface. */ + wx_sqlite3_result_int64(context, wx_sqlite3_total_changes64(db)); } /* @@ -119219,7 +125593,7 @@ static int patternCompare( ** c but in the other case and search the input string for either ** c or cx. */ - if( c<=0x80 ){ + if( c<0x80 ){ char zStop[3]; int bMatch; if( noCase ){ @@ -119302,7 +125676,13 @@ static int patternCompare( ** non-zero if there is no match. */ SQLITE_API int wx_sqlite3_strglob(const char *zGlobPattern, const char *zString){ - return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '['); + if( zString==0 ){ + return zGlobPattern!=0; + }else if( zGlobPattern==0 ){ + return 1; + }else { + return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '['); + } } /* @@ -119310,7 +125690,13 @@ SQLITE_API int wx_sqlite3_strglob(const char *zGlobPattern, const char *zString) ** a miss - like strcmp(). */ SQLITE_API int wx_sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc){ - return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc); + if( zStr==0 ){ + return zPattern!=0; + }else if( zPattern==0 ){ + return 1; + }else{ + return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc); + } } /* @@ -119518,39 +125904,42 @@ static const char hexdigits[] = { }; /* -** Implementation of the QUOTE() function. This function takes a single -** argument. If the argument is numeric, the return value is the same as -** the argument. If the argument is NULL, the return value is the string -** "NULL". Otherwise, the argument is enclosed in single quotes with -** single-quote escapes. +** Append to pStr text that is the SQL literal representation of the +** value contained in pValue. */ -static void quoteFunc(wx_sqlite3_context *context, int argc, wx_sqlite3_value **argv){ - assert( argc==1 ); - UNUSED_PARAMETER(argc); - switch( wx_sqlite3_value_type(argv[0]) ){ +SQLITE_PRIVATE void wx_sqlite3QuoteValue(StrAccum *pStr, wx_sqlite3_value *pValue){ + /* As currently implemented, the string must be initially empty. + ** we might relax this requirement in the future, but that will + ** require enhancements to the implementation. */ + assert( pStr!=0 && pStr->nChar==0 ); + + switch( wx_sqlite3_value_type(pValue) ){ case SQLITE_FLOAT: { double r1, r2; - char zBuf[50]; - r1 = wx_sqlite3_value_double(argv[0]); - wx_sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1); - wx_sqlite3AtoF(zBuf, &r2, 20, SQLITE_UTF8); - if( r1!=r2 ){ - wx_sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.20e", r1); - } - wx_sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); + const char *zVal; + r1 = wx_sqlite3_value_double(pValue); + wx_sqlite3_str_appendf(pStr, "%!.15g", r1); + zVal = wx_sqlite3_str_value(pStr); + if( zVal ){ + wx_sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8); + if( r1!=r2 ){ + wx_sqlite3_str_reset(pStr); + wx_sqlite3_str_appendf(pStr, "%!.20e", r1); + } + } break; } case SQLITE_INTEGER: { - wx_sqlite3_result_value(context, argv[0]); + wx_sqlite3_str_appendf(pStr, "%lld", wx_sqlite3_value_int64(pValue)); break; } case SQLITE_BLOB: { - char *zText = 0; - char const *zBlob = wx_sqlite3_value_blob(argv[0]); - int nBlob = wx_sqlite3_value_bytes(argv[0]); - assert( zBlob==wx_sqlite3_value_blob(argv[0]) ); /* No encoding change */ - zText = (char *)contextMalloc(context, (2*(i64)nBlob)+4); - if( zText ){ + char const *zBlob = wx_sqlite3_value_blob(pValue); + i64 nBlob = wx_sqlite3_value_bytes(pValue); + assert( zBlob==wx_sqlite3_value_blob(pValue) ); /* No encoding change */ + wx_sqlite3StrAccumEnlarge(pStr, nBlob*2 + 4); + if( pStr->accError==0 ){ + char *zText = pStr->zText; int i; for(i=0; i>4)&0x0F]; @@ -119560,42 +125949,48 @@ static void quoteFunc(wx_sqlite3_context *context, int argc, wx_sqlite3_value ** zText[(nBlob*2)+3] = '\0'; zText[0] = 'X'; zText[1] = '\''; - wx_sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); - wx_sqlite3_free(zText); + pStr->nChar = nBlob*2 + 3; } break; } case SQLITE_TEXT: { - int i,j; - u64 n; - const unsigned char *zArg = wx_sqlite3_value_text(argv[0]); - char *z; - - if( zArg==0 ) return; - for(i=0, n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; } - z = contextMalloc(context, ((i64)i)+((i64)n)+3); - if( z ){ - z[0] = '\''; - for(i=0, j=1; zArg[i]; i++){ - z[j++] = zArg[i]; - if( zArg[i]=='\'' ){ - z[j++] = '\''; - } - } - z[j++] = '\''; - z[j] = 0; - wx_sqlite3_result_text(context, z, j, wx_sqlite3_free); - } + const unsigned char *zArg = wx_sqlite3_value_text(pValue); + wx_sqlite3_str_appendf(pStr, "%Q", zArg); break; } default: { - assert( wx_sqlite3_value_type(argv[0])==SQLITE_NULL ); - wx_sqlite3_result_text(context, "NULL", 4, SQLITE_STATIC); + assert( wx_sqlite3_value_type(pValue)==SQLITE_NULL ); + wx_sqlite3_str_append(pStr, "NULL", 4); break; } } } +/* +** Implementation of the QUOTE() function. +** +** The quote(X) function returns the text of an SQL literal which is the +** value of its argument suitable for inclusion into an SQL statement. +** Strings are surrounded by single-quotes with escapes on interior quotes +** as needed. BLOBs are encoded as hexadecimal literals. Strings with +** embedded NUL characters cannot be represented as string literals in SQL +** and hence the returned string literal is truncated prior to the first NUL. +*/ +static void quoteFunc(wx_sqlite3_context *context, int argc, wx_sqlite3_value **argv){ + wx_sqlite3_str str; + wx_sqlite3 *db = wx_sqlite3_context_db_handle(context); + assert( argc==1 ); + UNUSED_PARAMETER(argc); + wx_sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); + wx_sqlite3QuoteValue(&str,argv[0]); + wx_sqlite3_result_text(context, wx_sqlite3StrAccumFinish(&str), str.nChar, + SQLITE_DYNAMIC); + if( str.accError!=SQLITE_OK ){ + wx_sqlite3_result_null(context); + wx_sqlite3_result_error_code(context, str.accError); + } +} + /* ** The unicode() function. Return the integer unicode code-point value ** for the first character of the input string. @@ -119681,6 +126076,96 @@ static void hexFunc( } } +/* +** Buffer zStr contains nStr bytes of utf-8 encoded text. Return 1 if zStr +** contains character ch, or 0 if it does not. +*/ +static int strContainsChar(const u8 *zStr, int nStr, u32 ch){ + const u8 *zEnd = &zStr[nStr]; + const u8 *z = zStr; + while( z0 ){ - azChar = contextMalloc(context, ((i64)nChar)*(sizeof(char*)+1)); + azChar = contextMalloc(context, + ((i64)nChar)*(sizeof(char*)+sizeof(unsigned))); if( azChar==0 ){ return; } - aLen = (unsigned char*)&azChar[nChar]; + aLen = (unsigned*)&azChar[nChar]; for(z=zCharSet, nChar=0; *z; nChar++){ azChar[nChar] = (unsigned char *)z; SQLITE_SKIP_UTF8(z); - aLen[nChar] = (u8)(z - azChar[nChar]); + aLen[nChar] = (unsigned)(z - azChar[nChar]); } } } @@ -119852,7 +126338,7 @@ static void trimFunc( flags = SQLITE_PTR_TO_INT(wx_sqlite3_user_data(context)); if( flags & 1 ){ while( nIn>0 ){ - int len = 0; + unsigned int len = 0; for(i=0; i0 ){ - int len = 0; + unsigned int len = 0; for(i=0; imxAlloc==0; - pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; - if( !firstTerm ){ - if( argc==2 ){ - zSep = (char*)wx_sqlite3_value_text(argv[1]); - nSep = wx_sqlite3_value_bytes(argv[1]); - }else{ - zSep = ","; - nSep = 1; + int firstTerm = pGCC->str.mxAlloc==0; + pGCC->str.mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; + if( argc==1 ){ + if( !firstTerm ){ + wx_sqlite3_str_appendchar(&pGCC->str, 1, ','); + } +#ifndef SQLITE_OMIT_WINDOWFUNC + else{ + pGCC->nFirstSepLength = 1; + } +#endif + }else if( !firstTerm ){ + zSep = (char*)wx_sqlite3_value_text(argv[1]); + nSep = wx_sqlite3_value_bytes(argv[1]); + if( zSep ){ + wx_sqlite3_str_append(&pGCC->str, zSep, nSep); + } +#ifndef SQLITE_OMIT_WINDOWFUNC + else{ + nSep = 0; + } + if( nSep != pGCC->nFirstSepLength || pGCC->pnSepLengths != 0 ){ + int *pnsl = pGCC->pnSepLengths; + if( pnsl == 0 ){ + /* First separator length variation seen, start tracking them. */ + pnsl = (int*)wx_sqlite3_malloc64((pGCC->nAccum+1) * sizeof(int)); + if( pnsl!=0 ){ + int i = 0, nA = pGCC->nAccum-1; + while( inFirstSepLength; + } + }else{ + pnsl = (int*)wx_sqlite3_realloc64(pnsl, pGCC->nAccum * sizeof(int)); + } + if( pnsl!=0 ){ + if( ALWAYS(pGCC->nAccum>0) ){ + pnsl[pGCC->nAccum-1] = nSep; + } + pGCC->pnSepLengths = pnsl; + }else{ + wx_sqlite3StrAccumSetError(&pGCC->str, SQLITE_NOMEM); + } } - if( zSep ) wx_sqlite3_str_append(pAccum, zSep, nSep); +#endif + } +#ifndef SQLITE_OMIT_WINDOWFUNC + else{ + pGCC->nFirstSepLength = wx_sqlite3_value_bytes(argv[1]); } + pGCC->nAccum += 1; +#endif zVal = (char*)wx_sqlite3_value_text(argv[0]); nVal = wx_sqlite3_value_bytes(argv[0]); - if( zVal ) wx_sqlite3_str_append(pAccum, zVal, nVal); + if( zVal ) wx_sqlite3_str_append(&pGCC->str, zVal, nVal); } } + #ifndef SQLITE_OMIT_WINDOWFUNC static void groupConcatInverse( wx_sqlite3_context *context, int argc, wx_sqlite3_value **argv ){ - int n; - StrAccum *pAccum; + GroupConcatCtx *pGCC; assert( argc==1 || argc==2 ); + (void)argc; /* Suppress unused parameter warning */ if( wx_sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - pAccum = (StrAccum*)wx_sqlite3_aggregate_context(context, sizeof(*pAccum)); - /* pAccum is always non-NULL since groupConcatStep() will have always + pGCC = (GroupConcatCtx*)wx_sqlite3_aggregate_context(context, sizeof(*pGCC)); + /* pGCC is always non-NULL since groupConcatStep() will have always ** run frist to initialize it */ - if( ALWAYS(pAccum) ){ - n = wx_sqlite3_value_bytes(argv[0]); - if( argc==2 ){ - n += wx_sqlite3_value_bytes(argv[1]); + if( ALWAYS(pGCC) ){ + int nVS; + /* Must call wx_sqlite3_value_text() to convert the argument into text prior + ** to invoking wx_sqlite3_value_bytes(), in case the text encoding is UTF16 */ + (void)wx_sqlite3_value_text(argv[0]); + nVS = wx_sqlite3_value_bytes(argv[0]); + pGCC->nAccum -= 1; + if( pGCC->pnSepLengths!=0 ){ + assert(pGCC->nAccum >= 0); + if( pGCC->nAccum>0 ){ + nVS += *pGCC->pnSepLengths; + memmove(pGCC->pnSepLengths, pGCC->pnSepLengths+1, + (pGCC->nAccum-1)*sizeof(int)); + } }else{ - n++; + /* If removing single accumulated string, harmlessly over-do. */ + nVS += pGCC->nFirstSepLength; } - if( n>=(int)pAccum->nChar ){ - pAccum->nChar = 0; + if( nVS>=(int)pGCC->str.nChar ){ + pGCC->str.nChar = 0; }else{ - pAccum->nChar -= n; - memmove(pAccum->zText, &pAccum->zText[n], pAccum->nChar); + pGCC->str.nChar -= nVS; + memmove(pGCC->str.zText, &pGCC->str.zText[nVS], pGCC->str.nChar); + } + if( pGCC->str.nChar==0 ){ + pGCC->str.mxAlloc = 0; + wx_sqlite3_free(pGCC->pnSepLengths); + pGCC->pnSepLengths = 0; } - if( pAccum->nChar==0 ) pAccum->mxAlloc = 0; } } #else # define groupConcatInverse 0 #endif /* SQLITE_OMIT_WINDOWFUNC */ static void groupConcatFinalize(wx_sqlite3_context *context){ - StrAccum *pAccum; - pAccum = wx_sqlite3_aggregate_context(context, 0); - if( pAccum ){ - if( pAccum->accError==SQLITE_TOOBIG ){ - wx_sqlite3_result_error_toobig(context); - }else if( pAccum->accError==SQLITE_NOMEM ){ - wx_sqlite3_result_error_nomem(context); - }else{ - wx_sqlite3_result_text(context, wx_sqlite3StrAccumFinish(pAccum), -1, - wx_sqlite3_free); - } + GroupConcatCtx *pGCC + = (GroupConcatCtx*)wx_sqlite3_aggregate_context(context, 0); + if( pGCC ){ + wx_sqlite3ResultStrAccum(context, &pGCC->str); +#ifndef SQLITE_OMIT_WINDOWFUNC + wx_sqlite3_free(pGCC->pnSepLengths); +#endif } } #ifndef SQLITE_OMIT_WINDOWFUNC static void groupConcatValue(wx_sqlite3_context *context){ - wx_sqlite3_str *pAccum; - pAccum = (wx_sqlite3_str*)wx_sqlite3_aggregate_context(context, 0); - if( pAccum ){ + GroupConcatCtx *pGCC + = (GroupConcatCtx*)wx_sqlite3_aggregate_context(context, 0); + if( pGCC ){ + StrAccum *pAccum = &pGCC->str; if( pAccum->accError==SQLITE_TOOBIG ){ wx_sqlite3_result_error_toobig(context); }else if( pAccum->accError==SQLITE_NOMEM ){ wx_sqlite3_result_error_nomem(context); }else{ const char *zText = wx_sqlite3_str_value(pAccum); - wx_sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); + wx_sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT); } } } @@ -120359,11 +126918,12 @@ SQLITE_PRIVATE int wx_sqlite3IsLikeFunction(wx_sqlite3 *db, Expr *pExpr, int *pI int nExpr; assert( pExpr!=0 ); assert( pExpr->op==TK_FUNCTION ); + assert( ExprUseXList(pExpr) ); if( !pExpr->x.pList ){ return 0; } - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); nExpr = pExpr->x.pList->nExpr; + assert( !ExprHasProperty(pExpr, EP_IntValue) ); pDef = wx_sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0); #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION if( pDef==0 ) return 0; @@ -120387,6 +126947,7 @@ SQLITE_PRIVATE int wx_sqlite3IsLikeFunction(wx_sqlite3 *db, Expr *pExpr, int *pI Expr *pEscape = pExpr->x.pList->a[2].pExpr; char *zEscape; if( pEscape->op!=TK_STRING ) return 0; + assert( !ExprHasProperty(pEscape, EP_IntValue) ); zEscape = pEscape->u.zToken; if( zEscape[0]==0 || zEscape[1]!=0 ) return 0; if( zEscape[0]==aWc[0] ) return 0; @@ -120453,6 +127014,18 @@ static void ceilingFunc( static double xCeil(double x){ return ceil(x); } static double xFloor(double x){ return floor(x); } +/* +** Some systems do not have log2() and log10() in their standard math +** libraries. +*/ +#if defined(HAVE_LOG10) && HAVE_LOG10==0 +# define log10(X) (0.4342944819032517867*log(X)) +#endif +#if defined(HAVE_LOG2) && HAVE_LOG2==0 +# define log2(X) (1.442695040888963456*log(X)) +#endif + + /* ** Implementation of SQL functions: ** @@ -120491,17 +127064,15 @@ static void logFunc( } ans = log(x)/b; }else{ - ans = log(x); switch( SQLITE_PTR_TO_INT(wx_sqlite3_user_data(context)) ){ case 1: - /* Convert from natural logarithm to log base 10 */ - ans *= 1.0/M_LN10; + ans = log10(x); break; case 2: - /* Convert from natural logarithm to log base 2 */ - ans *= 1.0/M_LN2; + ans = log2(x); break; default: + ans = log(x); break; } } @@ -120562,9 +127133,7 @@ static void math2Func( } /* -** Implementation of 2-argument SQL math functions: -** -** power(X,Y) - Compute X to the Y-th power +** Implementation of 0-argument pi() function. */ static void piFunc( wx_sqlite3_context *context, @@ -120572,6 +127141,7 @@ static void piFunc( wx_sqlite3_value **argv ){ assert( argc==0 ); + (void)argv; wx_sqlite3_result_double(context, M_PI); } @@ -120615,12 +127185,12 @@ SQLITE_PRIVATE void wx_sqlite3RegisterBuiltinFunctions(void){ */ static FuncDef aBuiltinFunc[] = { /***** Functions only available with SQLITE_TESTCTRL_INTERNAL_FUNCTIONS *****/ +#if !defined(SQLITE_UNTESTABLE) TEST_FUNC(implies_nonnull_row, 2, INLINEFUNC_implies_nonnull_row, 0), TEST_FUNC(expr_compare, 2, INLINEFUNC_expr_compare, 0), TEST_FUNC(expr_implies_expr, 2, INLINEFUNC_expr_implies_expr, 0), -#ifdef SQLITE_DEBUG - TEST_FUNC(affinity, 1, INLINEFUNC_affinity, 0), -#endif + TEST_FUNC(affinity, 1, INLINEFUNC_affinity, 0), +#endif /* !defined(SQLITE_UNTESTABLE) */ /***** Regular functions *****/ #ifdef SQLITE_SOUNDEX FUNCTION(soundex, 1, 0, 0, soundexFunc ), @@ -120640,8 +127210,7 @@ SQLITE_PRIVATE void wx_sqlite3RegisterBuiltinFunctions(void){ INLINE_FUNC(likelihood, 2, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY), INLINE_FUNC(likely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY), #ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC - FUNCTION2(sqlite_offset, 1, 0, 0, noopFunc, SQLITE_FUNC_OFFSET| - SQLITE_FUNC_TYPEOF), + INLINE_FUNC(sqlite_offset, 1, INLINEFUNC_sqlite_offset, 0 ), #endif FUNCTION(ltrim, 1, 1, 0, trimFunc ), FUNCTION(ltrim, 2, 1, 0, trimFunc ), @@ -120652,15 +127221,17 @@ SQLITE_PRIVATE void wx_sqlite3RegisterBuiltinFunctions(void){ FUNCTION(min, -1, 0, 1, minmaxFunc ), FUNCTION(min, 0, 0, 1, 0 ), WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, - SQLITE_FUNC_MINMAX ), + SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), FUNCTION(max, -1, 1, 1, minmaxFunc ), FUNCTION(max, 0, 1, 1, 0 ), WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, - SQLITE_FUNC_MINMAX ), + SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), + FUNCTION2(subtype, 1, 0, 0, subtypeFunc, SQLITE_FUNC_TYPEOF), FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), FUNCTION(instr, 2, 0, 0, instrFunc ), FUNCTION(printf, -1, 0, 0, printfFunc ), + FUNCTION(format, -1, 0, 0, printfFunc ), FUNCTION(unicode, 1, 0, 0, unicodeFunc ), FUNCTION(char, -1, 0, 0, charFunc ), FUNCTION(abs, 1, 0, 0, absFunc ), @@ -120671,6 +127242,8 @@ SQLITE_PRIVATE void wx_sqlite3RegisterBuiltinFunctions(void){ FUNCTION(upper, 1, 0, 0, upperFunc ), FUNCTION(lower, 1, 0, 0, lowerFunc ), FUNCTION(hex, 1, 0, 0, hexFunc ), + FUNCTION(unhex, 1, 0, 0, unhexFunc ), + FUNCTION(unhex, 2, 0, 0, unhexFunc ), INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ), VFUNCTION(random, 0, 0, 0, randomFunc ), VFUNCTION(randomblob, 1, 0, 0, randomBlob ), @@ -120692,9 +127265,10 @@ SQLITE_PRIVATE void wx_sqlite3RegisterBuiltinFunctions(void){ WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0), WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0), WAGGREGATE(count, 0,0,0, countStep, - countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ), + countFinalize, countFinalize, countInverse, + SQLITE_FUNC_COUNT|SQLITE_FUNC_ANYORDER ), WAGGREGATE(count, 1,0,0, countStep, - countFinalize, countFinalize, countInverse, 0 ), + countFinalize, countFinalize, countInverse, SQLITE_FUNC_ANYORDER ), WAGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize, groupConcatValue, groupConcatInverse, 0), WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep, @@ -120758,6 +127332,7 @@ SQLITE_PRIVATE void wx_sqlite3RegisterBuiltinFunctions(void){ #endif wx_sqlite3WindowFunctions(); wx_sqlite3RegisterDateTimeFunctions(); + wx_sqlite3RegisterJsonFunctions(); wx_sqlite3InsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc)); #if 0 /* Enable to print out how the built-in functions are hashed */ @@ -120769,6 +127344,7 @@ SQLITE_PRIVATE void wx_sqlite3RegisterBuiltinFunctions(void){ for(p=wx_sqlite3BuiltinFunctions.a[i]; p; p=p->u.pHash){ int n = wx_sqlite3Strlen30(p->zName); int h = p->zName[0] + n; + assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); printf(" %s(%d)", p->zName, h); } printf("\n"); @@ -120996,7 +127572,9 @@ SQLITE_PRIVATE int wx_sqlite3FkLocateIndex( */ if( pParent->iPKey>=0 ){ if( !zKey ) return 0; - if( !wx_sqlite3StrICmp(pParent->aCol[pParent->iPKey].zName, zKey) ) return 0; + if( !wx_sqlite3StrICmp(pParent->aCol[pParent->iPKey].zCnName, zKey) ){ + return 0; + } } }else if( paiCol ){ assert( nCol>1 ); @@ -121038,11 +127616,11 @@ SQLITE_PRIVATE int wx_sqlite3FkLocateIndex( /* If the index uses a collation sequence that is different from ** the default collation sequence for the column, this index is ** unusable. Bail out early in this case. */ - zDfltColl = pParent->aCol[iCol].zColl; + zDfltColl = wx_sqlite3ColumnColl(&pParent->aCol[iCol]); if( !zDfltColl ) zDfltColl = wx_sqlite3StrBINARY; if( wx_sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break; - zIdxCol = pParent->aCol[iCol].zName; + zIdxCol = pParent->aCol[iCol].zCnName; for(j=0; jaCol[j].zCol, zIdxCol)==0 ){ if( aiCol ) aiCol[i] = pFKey->aCol[j].iFrom; @@ -121169,7 +127747,6 @@ static void fkLookupParent( }else{ int nCol = pFKey->nCol; int regTemp = wx_sqlite3GetTempRange(pParse, nCol); - int regRec = wx_sqlite3GetTempReg(pParse); wx_sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); wx_sqlite3VdbeSetP4KeyInfo(pParse, pIdx); @@ -121209,11 +127786,10 @@ static void fkLookupParent( wx_sqlite3VdbeGoto(v, iOk); } - wx_sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec, + wx_sqlite3VdbeAddOp4(v, OP_Affinity, regTemp, nCol, 0, wx_sqlite3IndexAffinityStr(pParse->db,pIdx), nCol); - wx_sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v); - - wx_sqlite3ReleaseTempReg(pParse, regRec); + wx_sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regTemp, nCol); + VdbeCoverage(v); wx_sqlite3ReleaseTempRange(pParse, regTemp, nCol); } } @@ -121266,7 +127842,7 @@ static Expr *exprTableRegister( pCol = &pTab->aCol[iCol]; pExpr->iTable = regBase + wx_sqlite3TableColumnToStorage(pTab,iCol) + 1; pExpr->affExpr = pCol->affinity; - zColl = pCol->zColl; + zColl = wx_sqlite3ColumnColl(pCol); if( zColl==0 ) zColl = db->pDfltColl->zName; pExpr = wx_sqlite3ExprAddCollateString(pParse, pExpr, zColl); }else{ @@ -121289,6 +127865,7 @@ static Expr *exprTableColumn( ){ Expr *pExpr = wx_sqlite3Expr(db, TK_COLUMN, 0); if( pExpr ){ + assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pTab; pExpr->iTable = iCursor; pExpr->iColumn = iCol; @@ -121314,14 +127891,10 @@ static Expr *exprTableColumn( ** Operation | FK type | Action taken ** -------------------------------------------------------------------------- ** DELETE immediate Increment the "immediate constraint counter". -** Or, if the ON (UPDATE|DELETE) action is RESTRICT, -** throw a "FOREIGN KEY constraint failed" exception. ** ** INSERT immediate Decrement the "immediate constraint counter". ** ** DELETE deferred Increment the "deferred constraint counter". -** Or, if the ON (UPDATE|DELETE) action is RESTRICT, -** throw a "FOREIGN KEY constraint failed" exception. ** ** INSERT deferred Decrement the "deferred constraint counter". ** @@ -121375,7 +127948,7 @@ static void fkScanChildren( pLeft = exprTableRegister(pParse, pTab, regData, iCol); iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; assert( iCol>=0 ); - zCol = pFKey->pFrom->aCol[iCol].zName; + zCol = pFKey->pFrom->aCol[iCol].zCnName; pRight = wx_sqlite3Expr(db, TK_ID, zCol); pEq = wx_sqlite3PExpr(pParse, TK_EQ, pLeft, pRight); pWhere = wx_sqlite3ExprAnd(pParse, pWhere, pEq); @@ -121410,7 +127983,7 @@ static void fkScanChildren( i16 iCol = pIdx->aiColumn[i]; assert( iCol>=0 ); pLeft = exprTableRegister(pParse, pTab, regData, iCol); - pRight = wx_sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zName); + pRight = wx_sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zCnName); pEq = wx_sqlite3PExpr(pParse, TK_IS, pLeft, pRight); pAll = wx_sqlite3ExprAnd(pParse, pAll, pEq); } @@ -121429,7 +128002,7 @@ static void fkScanChildren( ** clause. For each row found, increment either the deferred or immediate ** foreign key constraint counter. */ if( pParse->nErr==0 ){ - pWInfo = wx_sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); + pWInfo = wx_sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0, 0); wx_sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); if( pWInfo ){ wx_sqlite3WhereEnd(pWInfo); @@ -121480,6 +128053,25 @@ static void fkTriggerDelete(wx_sqlite3 *dbMem, Trigger *p){ } } +/* +** Clear the apTrigger[] cache of CASCADE triggers for all foreign keys +** in a particular database. This needs to happen when the schema +** changes. +*/ +SQLITE_PRIVATE void wx_sqlite3FkClearTriggerCache(wx_sqlite3 *db, int iDb){ + HashElem *k; + Hash *pHash = &db->aDb[iDb].pSchema->tblHash; + for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k)){ + Table *pTab = sqliteHashData(k); + FKey *pFKey; + if( !IsOrdinaryTable(pTab) ) continue; + for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ + fkTriggerDelete(db, pFKey->apTrigger[0]); pFKey->apTrigger[0] = 0; + fkTriggerDelete(db, pFKey->apTrigger[1]); pFKey->apTrigger[1] = 0; + } + } +} + /* ** This function is called to generate code that runs when table pTab is ** being dropped from the database. The SrcList passed as the second argument @@ -121499,12 +128091,12 @@ static void fkTriggerDelete(wx_sqlite3 *dbMem, Trigger *p){ */ SQLITE_PRIVATE void wx_sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){ wx_sqlite3 *db = pParse->db; - if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) ){ + if( (db->flags&SQLITE_ForeignKeys) && IsOrdinaryTable(pTab) ){ int iSkip = 0; Vdbe *v = wx_sqlite3GetVdbe(pParse); assert( v ); /* VDBE has already been allocated */ - assert( pTab->pSelect==0 ); /* Not a view */ + assert( IsOrdinaryTable(pTab) ); if( wx_sqlite3FkReferences(pTab)==0 ){ /* Search for a deferred foreign key constraint for which this table ** is the child table. If one cannot be found, return without @@ -121512,7 +128104,7 @@ SQLITE_PRIVATE void wx_sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table * ** the entire DELETE if there are no outstanding deferred constraints ** when this statement is run. */ FKey *p; - for(p=pTab->pFKey; p; p=p->pNextFrom){ + for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break; } if( !p ) return; @@ -121601,7 +128193,7 @@ static int fkParentIsModified( if( aChange[iKey]>=0 || (iKey==pTab->iPKey && bChngRowid) ){ Column *pCol = &pTab->aCol[iKey]; if( zKey ){ - if( 0==wx_sqlite3StrICmp(pCol->zName, zKey) ) return 1; + if( 0==wx_sqlite3StrICmp(pCol->zCnName, zKey) ) return 1; }else if( pCol->colFlags & COLFLAG_PRIMKEY ){ return 1; } @@ -121668,13 +128260,14 @@ SQLITE_PRIVATE void wx_sqlite3FkCheck( /* If foreign-keys are disabled, this function is a no-op. */ if( (db->flags&SQLITE_ForeignKeys)==0 ) return; + if( !IsOrdinaryTable(pTab) ) return; iDb = wx_sqlite3SchemaToIndex(db, pTab->pSchema); zDb = db->aDb[iDb].zDbSName; /* Loop through all the foreign key constraints for which pTab is the ** child table (the table that the foreign key definition is part of). */ - for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ Table *pTo; /* Parent table of foreign key pFKey */ Index *pIdx = 0; /* Index on key columns in pTo */ int *aiFree = 0; @@ -121741,7 +128334,7 @@ SQLITE_PRIVATE void wx_sqlite3FkCheck( ** values read from the parent table are NULL. */ if( db->xAuth ){ int rcauth; - char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; + char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zCnName; rcauth = wx_sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); bIgnore = (rcauth==SQLITE_IGNORE); } @@ -121856,10 +128449,10 @@ SQLITE_PRIVATE u32 wx_sqlite3FkOldmask( Table *pTab /* Table being modified */ ){ u32 mask = 0; - if( pParse->db->flags&SQLITE_ForeignKeys ){ + if( pParse->db->flags&SQLITE_ForeignKeys && IsOrdinaryTable(pTab) ){ FKey *p; int i; - for(p=pTab->pFKey; p; p=p->pNextFrom){ + for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ for(i=0; inCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom); } for(p=wx_sqlite3FkReferences(pTab); p; p=p->pNextTo){ @@ -121909,19 +128502,19 @@ SQLITE_PRIVATE int wx_sqlite3FkRequired( ){ int eRet = 1; /* Value to return if bHaveFK is true */ int bHaveFK = 0; /* If FK processing is required */ - if( pParse->db->flags&SQLITE_ForeignKeys ){ + if( pParse->db->flags&SQLITE_ForeignKeys && IsOrdinaryTable(pTab) ){ if( !aChange ){ /* A DELETE operation. Foreign key processing is required if the ** table in question is either the child or parent table for any ** foreign key constraint. */ - bHaveFK = (wx_sqlite3FkReferences(pTab) || pTab->pFKey); + bHaveFK = (wx_sqlite3FkReferences(pTab) || pTab->u.tab.pFKey); }else{ /* This is an UPDATE. Foreign key processing is only required if the ** operation modifies one or more child or parent key columns. */ FKey *p; /* Check if any child key columns are being modified. */ - for(p=pTab->pFKey; p; p=p->pNextFrom){ + for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ if( fkChildIsModified(pTab, p, aChange, chngRowid) ){ if( 0==wx_sqlite3_stricmp(pTab->zName, p->zTo) ) eRet = 2; bHaveFK = 1; @@ -121949,9 +128542,9 @@ SQLITE_PRIVATE int wx_sqlite3FkRequired( ** ** It returns a pointer to a Trigger structure containing a trigger ** equivalent to the ON UPDATE or ON DELETE action specified by pFKey. -** If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is -** returned (these actions require no special handling by the triggers -** sub-system, code for them is created by fkScanChildren()). +** If the action is "NO ACTION" then a NULL pointer is returned (these actions +** require no special handling by the triggers sub-system, code for them is +** created by fkScanChildren()). ** ** For example, if pFKey is the foreign key and pTab is table "p" in ** the following schema: @@ -122014,8 +128607,8 @@ static Trigger *fkActionTrigger( assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKeynCol) ); assert( pIdx==0 || pIdx->aiColumn[i]>=0 ); wx_sqlite3TokenInit(&tToCol, - pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName); - wx_sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zName); + pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zCnName); + wx_sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zCnName); /* Create the expression "OLD.zToCol = zFromCol". It is important ** that the "OLD.zToCol" term is on the LHS of the = operator, so @@ -122060,7 +128653,7 @@ static Trigger *fkActionTrigger( testcase( pCol->colFlags & COLFLAG_STORED ); pDflt = 0; }else{ - pDflt = pCol->pDflt; + pDflt = wx_sqlite3ColumnExpr(pFKey->pFrom, pCol); } if( pDflt ){ pNew = wx_sqlite3ExprDup(db, pDflt, 0); @@ -122080,18 +128673,23 @@ static Trigger *fkActionTrigger( nFrom = wx_sqlite3Strlen30(zFrom); if( action==OE_Restrict ){ + int iDb = wx_sqlite3SchemaToIndex(db, pTab->pSchema); Token tFrom; + Token tDb; Expr *pRaise; tFrom.z = zFrom; tFrom.n = nFrom; + tDb.z = db->aDb[iDb].zDbSName; + tDb.n = wx_sqlite3Strlen30(tDb.z); + pRaise = wx_sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed"); if( pRaise ){ pRaise->affExpr = OE_Abort; } pSelect = wx_sqlite3SelectNew(pParse, wx_sqlite3ExprListAppend(pParse, 0, pRaise), - wx_sqlite3SrcListAppend(pParse, 0, &tFrom, 0), + wx_sqlite3SrcListAppend(pParse, 0, &tDb, &tFrom), pWhere, 0, 0, 0, 0, 0 ); @@ -122197,12 +128795,13 @@ SQLITE_PRIVATE void wx_sqlite3FkDelete(wx_sqlite3 *db, Table *pTab){ FKey *pFKey; /* Iterator variable */ FKey *pNext; /* Copy of pFKey->pNextFrom */ - assert( db==0 || IsVirtual(pTab) - || wx_sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); - for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ + assert( IsOrdinaryTable(pTab) ); + assert( db!=0 ); + for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pNext){ + assert( db==0 || wx_sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); /* Remove the FK from the fkeyHash hash table. */ - if( !db || db->pnBytesFreed==0 ){ + if( db->pnBytesFreed==0 ){ if( pFKey->pPrevTo ){ pFKey->pPrevTo->pNextTo = pFKey->pNextTo; }else{ @@ -122279,7 +128878,7 @@ SQLITE_PRIVATE void wx_sqlite3OpenTable( }else{ Index *pPk = wx_sqlite3PrimaryKeyIndex(pTab); assert( pPk!=0 ); - assert( pPk->tnum==pTab->tnum ); + assert( pPk->tnum==pTab->tnum || CORRUPT_DB ); wx_sqlite3VdbeAddOp3(v, opcode, iCur, pPk->tnum, iDb); wx_sqlite3VdbeSetP4KeyInfo(pParse, pPk); VdbeComment((v, "%s", pTab->zName)); @@ -122332,6 +128931,7 @@ SQLITE_PRIVATE const char *wx_sqlite3IndexAffinityStr(wx_sqlite3 *db, Index *pId aff = SQLITE_AFF_INTEGER; }else{ assert( x==XN_EXPR ); + assert( pIdx->bHasExpr ); assert( pIdx->aColExpr!=0 ); aff = wx_sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr); } @@ -122346,45 +128946,96 @@ SQLITE_PRIVATE const char *wx_sqlite3IndexAffinityStr(wx_sqlite3 *db, Index *pId } /* +** Compute an affinity string for a table. Space is obtained +** from wx_sqlite3DbMalloc(). The caller is responsible for freeing +** the space when done. +*/ +SQLITE_PRIVATE char *wx_sqlite3TableAffinityStr(wx_sqlite3 *db, const Table *pTab){ + char *zColAff; + zColAff = (char *)wx_sqlite3DbMallocRaw(db, pTab->nCol+1); + if( zColAff ){ + int i, j; + for(i=j=0; inCol; i++){ + if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ + zColAff[j++] = pTab->aCol[i].affinity; + } + } + do{ + zColAff[j--] = 0; + }while( j>=0 && zColAff[j]<=SQLITE_AFF_BLOB ); + } + return zColAff; +} + +/* +** Make changes to the evolving bytecode to do affinity transformations +** of values that are about to be gathered into a row for table pTab. +** +** For ordinary (legacy, non-strict) tables: +** ----------------------------------------- +** ** Compute the affinity string for table pTab, if it has not already been ** computed. As an optimization, omit trailing SQLITE_AFF_BLOB affinities. ** -** If the affinity exists (if it is no entirely SQLITE_AFF_BLOB values) and -** if iReg>0 then code an OP_Affinity opcode that will set the affinities -** for register iReg and following. Or if affinities exists and iReg==0, +** If the affinity string is empty (because it was all SQLITE_AFF_BLOB entries +** which were then optimized out) then this routine becomes a no-op. +** +** Otherwise if iReg>0 then code an OP_Affinity opcode that will set the +** affinities for register iReg and following. Or if iReg==0, ** then just set the P4 operand of the previous opcode (which should be ** an OP_MakeRecord) to the affinity string. ** ** A column affinity string has one character per column: ** -** Character Column affinity -** ------------------------------ -** 'A' BLOB -** 'B' TEXT -** 'C' NUMERIC -** 'D' INTEGER -** 'E' REAL +** Character Column affinity +** --------- --------------- +** 'A' BLOB +** 'B' TEXT +** 'C' NUMERIC +** 'D' INTEGER +** 'E' REAL +** +** For STRICT tables: +** ------------------ +** +** Generate an appropropriate OP_TypeCheck opcode that will verify the +** datatypes against the column definitions in pTab. If iReg==0, that +** means an OP_MakeRecord opcode has already been generated and should be +** the last opcode generated. The new OP_TypeCheck needs to be inserted +** before the OP_MakeRecord. The new OP_TypeCheck should use the same +** register set as the OP_MakeRecord. If iReg>0 then register iReg is +** the first of a series of registers that will form the new record. +** Apply the type checking to that array of registers. */ SQLITE_PRIVATE void wx_sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){ - int i, j; - char *zColAff = pTab->zColAff; + int i; + char *zColAff; + if( pTab->tabFlags & TF_Strict ){ + if( iReg==0 ){ + /* Move the previous opcode (which should be OP_MakeRecord) forward + ** by one slot and insert a new OP_TypeCheck where the current + ** OP_MakeRecord is found */ + VdbeOp *pPrev; + wx_sqlite3VdbeAppendP4(v, pTab, P4_TABLE); + pPrev = wx_sqlite3VdbeGetLastOp(v); + assert( pPrev!=0 ); + assert( pPrev->opcode==OP_MakeRecord || wx_sqlite3VdbeDb(v)->mallocFailed ); + pPrev->opcode = OP_TypeCheck; + wx_sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, pPrev->p3); + }else{ + /* Insert an isolated OP_Typecheck */ + wx_sqlite3VdbeAddOp2(v, OP_TypeCheck, iReg, pTab->nNVCol); + wx_sqlite3VdbeAppendP4(v, pTab, P4_TABLE); + } + return; + } + zColAff = pTab->zColAff; if( zColAff==0 ){ - wx_sqlite3 *db = wx_sqlite3VdbeDb(v); - zColAff = (char *)wx_sqlite3DbMallocRaw(0, pTab->nCol+1); + zColAff = wx_sqlite3TableAffinityStr(0, pTab); if( !zColAff ){ - wx_sqlite3OomFault(db); + wx_sqlite3OomFault(wx_sqlite3VdbeDb(v)); return; } - - for(i=j=0; inCol; i++){ - assert( pTab->aCol[i].affinity!=0 ); - if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ - zColAff[j++] = pTab->aCol[i].affinity; - } - } - do{ - zColAff[j--] = 0; - }while( j>=0 && zColAff[j]<=SQLITE_AFF_BLOB ); pTab->zColAff = zColAff; } assert( zColAff!=0 ); @@ -122393,6 +129044,8 @@ SQLITE_PRIVATE void wx_sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){ if( iReg ){ wx_sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i); }else{ + assert( wx_sqlite3VdbeGetLastOp(v)->opcode==OP_MakeRecord + || wx_sqlite3VdbeDb(v)->mallocFailed ); wx_sqlite3VdbeChangeP4(v, -1, zColAff, i); } } @@ -122476,24 +129129,30 @@ SQLITE_PRIVATE void wx_sqlite3ComputeGeneratedColumns( ** that appropriate affinity has been applied to the regular columns */ wx_sqlite3TableAffinity(pParse->pVdbe, pTab, iRegStore); - if( (pTab->tabFlags & TF_HasStored)!=0 - && (pOp = wx_sqlite3VdbeGetOp(pParse->pVdbe,-1))->opcode==OP_Affinity - ){ - /* Change the OP_Affinity argument to '@' (NONE) for all stored - ** columns. '@' is the no-op affinity and those columns have not - ** yet been computed. */ - int ii, jj; - char *zP4 = pOp->p4.z; - assert( zP4!=0 ); - assert( pOp->p4type==P4_DYNAMIC ); - for(ii=jj=0; zP4[jj]; ii++){ - if( pTab->aCol[ii].colFlags & COLFLAG_VIRTUAL ){ - continue; - } - if( pTab->aCol[ii].colFlags & COLFLAG_STORED ){ - zP4[jj] = SQLITE_AFF_NONE; + if( (pTab->tabFlags & TF_HasStored)!=0 ){ + pOp = wx_sqlite3VdbeGetLastOp(pParse->pVdbe); + if( pOp->opcode==OP_Affinity ){ + /* Change the OP_Affinity argument to '@' (NONE) for all stored + ** columns. '@' is the no-op affinity and those columns have not + ** yet been computed. */ + int ii, jj; + char *zP4 = pOp->p4.z; + assert( zP4!=0 ); + assert( pOp->p4type==P4_DYNAMIC ); + for(ii=jj=0; zP4[jj]; ii++){ + if( pTab->aCol[ii].colFlags & COLFLAG_VIRTUAL ){ + continue; + } + if( pTab->aCol[ii].colFlags & COLFLAG_STORED ){ + zP4[jj] = SQLITE_AFF_NONE; + } + jj++; } - jj++; + }else if( pOp->opcode==OP_TypeCheck ){ + /* If an OP_TypeCheck was generated because the table is STRICT, + ** then set the P3 operand to indicate that generated columns should + ** not be checked */ + pOp->p3 = 1; } } @@ -122529,7 +129188,7 @@ SQLITE_PRIVATE void wx_sqlite3ComputeGeneratedColumns( int x; pCol->colFlags |= COLFLAG_BUSY; w.eCode = 0; - wx_sqlite3WalkExpr(&w, pCol->pDflt); + wx_sqlite3WalkExpr(&w, wx_sqlite3ColumnExpr(pTab, pCol)); pCol->colFlags &= ~COLFLAG_BUSY; if( w.eCode & COLFLAG_NOTAVAIL ){ pRedo = pCol; @@ -122538,13 +129197,13 @@ SQLITE_PRIVATE void wx_sqlite3ComputeGeneratedColumns( eProgress = 1; assert( pCol->colFlags & COLFLAG_GENERATED ); x = wx_sqlite3TableColumnToStorage(pTab, i) + iRegStore; - wx_sqlite3ExprCodeGeneratedColumn(pParse, pCol, x); + wx_sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, x); pCol->colFlags &= ~COLFLAG_NOTAVAIL; } } }while( pRedo && eProgress ); if( pRedo ){ - wx_sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pRedo->zName); + wx_sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pRedo->zCnName); } pParse->iSelfTab = 0; } @@ -122594,7 +129253,7 @@ static int autoIncBegin( ** Ticket d8dc2b3a58cd5dc2918a1d4acb 2018-05-23 */ if( pSeqTab==0 || !HasRowid(pSeqTab) - || IsVirtual(pSeqTab) + || NEVER(IsVirtual(pSeqTab)) || pSeqTab->nCol!=2 ){ pParse->nErr++; @@ -122903,9 +129562,11 @@ SQLITE_PRIVATE void wx_sqlite3Insert( #endif db = pParse->db; - if( pParse->nErr || db->mallocFailed ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto insert_cleanup; } + assert( db->mallocFailed==0 ); dest.iSDParm = 0; /* Suppress a harmless compiler warning */ /* If the Select object is really just a simple VALUES() list with a @@ -122939,7 +129600,7 @@ SQLITE_PRIVATE void wx_sqlite3Insert( */ #ifndef SQLITE_OMIT_TRIGGER pTrigger = wx_sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask); - isView = pTab->pSelect!=0; + isView = IsView(pTab); #else # define pTrigger 0 # define tmask 0 @@ -122951,6 +129612,14 @@ SQLITE_PRIVATE void wx_sqlite3Insert( #endif assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x10000 ){ + wx_sqlite3TreeViewLine(0, "In wx_sqlite3Insert() at %s:%d", __FILE__, __LINE__); + wx_sqlite3TreeViewInsert(pParse->pWith, pTabList, pColumn, pSelect, pList, + onError, pUpsert, pTrigger); + } +#endif + /* If pTab is really a view, make sure it has been initialized. ** ViewGetColumnNames() is a no-op if pTab is not a view. */ @@ -122981,7 +129650,11 @@ SQLITE_PRIVATE void wx_sqlite3Insert( ** ** This is the 2nd template. */ - if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){ + if( pColumn==0 + && pSelect!=0 + && pTrigger==0 + && xferOptimization(pParse, pTab, pSelect, onError, iDb) + ){ assert( !pTrigger ); assert( pList==0 ); goto insert_end; @@ -123025,13 +129698,15 @@ SQLITE_PRIVATE void wx_sqlite3Insert( */ bIdListInOrder = (pTab->tabFlags & (TF_OOOHidden|TF_HasStored))==0; if( pColumn ){ + assert( pColumn->eU4!=EU4_EXPR ); + pColumn->eU4 = EU4_IDX; for(i=0; inId; i++){ - pColumn->a[i].idx = -1; + pColumn->a[i].u4.idx = -1; } for(i=0; inId; i++){ for(j=0; jnCol; j++){ - if( wx_sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ - pColumn->a[i].idx = j; + if( wx_sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zCnName)==0 ){ + pColumn->a[i].u4.idx = j; if( i!=j ) bIdListInOrder = 0; if( j==pTab->iPKey ){ ipkColumn = i; assert( !withoutRowid ); @@ -123040,7 +129715,7 @@ SQLITE_PRIVATE void wx_sqlite3Insert( if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){ wx_sqlite3ErrorMsg(pParse, "cannot INSERT into generated column \"%s\"", - pTab->aCol[j].zName); + pTab->aCol[j].zCnName); goto insert_cleanup; } #endif @@ -123053,7 +129728,7 @@ SQLITE_PRIVATE void wx_sqlite3Insert( bIdListInOrder = 0; }else{ wx_sqlite3ErrorMsg(pParse, "table %S has no column named %s", - pTabList, 0, pColumn->a[i].zName); + pTabList->a, pColumn->a[i].zName); pParse->checkSchema = 1; goto insert_cleanup; } @@ -123081,7 +129756,9 @@ SQLITE_PRIVATE void wx_sqlite3Insert( dest.nSdst = pTab->nCol; rc = wx_sqlite3Select(pParse, pSelect, &dest); regFromSelect = dest.iSdst; - if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup; + assert( db->pParse==pParse ); + if( rc || pParse->nErr ) goto insert_cleanup; + assert( db->mallocFailed==0 ); wx_sqlite3VdbeEndCoroutine(v, regYield); wx_sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ assert( pSelect->pEList ); @@ -123181,7 +129858,7 @@ SQLITE_PRIVATE void wx_sqlite3Insert( if( nColumn!=(pTab->nCol-nHidden) ){ wx_sqlite3ErrorMsg(pParse, "table %S has %d columns but %d values were supplied", - pTabList, 0, pTab->nCol-nHidden, nColumn); + pTabList->a, pTab->nCol-nHidden, nColumn); goto insert_cleanup; } } @@ -123225,7 +129902,7 @@ SQLITE_PRIVATE void wx_sqlite3Insert( pTab->zName); goto insert_cleanup; } - if( pTab->pSelect ){ + if( IsView(pTab) ){ wx_sqlite3ErrorMsg(pParse, "cannot UPSERT a view"); goto insert_cleanup; } @@ -123324,22 +130001,29 @@ SQLITE_PRIVATE void wx_sqlite3Insert( }else if( pColumn==0 ){ /* Hidden columns that are not explicitly named in the INSERT ** get there default value */ - wx_sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); + wx_sqlite3ExprCodeFactorable(pParse, + wx_sqlite3ColumnExpr(pTab, &pTab->aCol[i]), + iRegStore); continue; } } if( pColumn ){ - for(j=0; jnId && pColumn->a[j].idx!=i; j++){} + assert( pColumn->eU4==EU4_IDX ); + for(j=0; jnId && pColumn->a[j].u4.idx!=i; j++){} if( j>=pColumn->nId ){ /* A column not named in the insert column list gets its ** default value */ - wx_sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); + wx_sqlite3ExprCodeFactorable(pParse, + wx_sqlite3ColumnExpr(pTab, &pTab->aCol[i]), + iRegStore); continue; } k = j; }else if( nColumn==0 ){ /* This is INSERT INTO ... DEFAULT VALUES. Load the default value. */ - wx_sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); + wx_sqlite3ExprCodeFactorable(pParse, + wx_sqlite3ColumnExpr(pTab, &pTab->aCol[i]), + iRegStore); continue; }else{ k = i - nHidden; @@ -123352,7 +130036,12 @@ SQLITE_PRIVATE void wx_sqlite3Insert( wx_sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+k, iRegStore); } }else{ - wx_sqlite3ExprCode(pParse, pList->a[k].pExpr, iRegStore); + Expr *pX = pList->a[k].pExpr; + int y = wx_sqlite3ExprCodeTarget(pParse, pX, iRegStore); + if( y!=iRegStore ){ + wx_sqlite3VdbeAddOp2(v, + ExprHasProperty(pX, EP_Subquery) ? OP_Copy : OP_SCopy, y, iRegStore); + } } } @@ -123484,12 +130173,14 @@ SQLITE_PRIVATE void wx_sqlite3Insert( }else #endif { - int isReplace; /* Set to true if constraints may cause a replace */ + int isReplace = 0;/* Set to true if constraints may cause a replace */ int bUseSeek; /* True to use OPFLAG_SEEKRESULT */ wx_sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0, pUpsert ); - wx_sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0); + if( db->flags & SQLITE_ForeignKeys ){ + wx_sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0); + } /* Set the OPFLAG_USESEEKRESULT flag if either (a) there are no REPLACE ** constraints or (b) there are no triggers and this table is not a @@ -123504,6 +130195,13 @@ SQLITE_PRIVATE void wx_sqlite3Insert( regIns, aRegIdx, 0, appendFlag, bUseSeek ); } +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + }else if( pParse->bReturning ){ + /* If there is a RETURNING clause, populate the rowid register with + ** constant value -1, in case one or more of the returned expressions + ** refer to the "rowid" of the view. */ + wx_sqlite3VdbeAddOp2(v, OP_Integer, -1, regRowid); +#endif } /* Update the count of rows that are inserted @@ -123557,9 +130255,7 @@ insert_end: ** invoke the callback function. */ if( regRowCount ){ - wx_sqlite3VdbeAddOp2(v, OP_ChngCntRow, regRowCount, 1); - wx_sqlite3VdbeSetNumCols(v, 1); - wx_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC); + wx_sqlite3CodeChangeCount(v, regRowCount, "rows inserted"); } insert_cleanup: @@ -123568,7 +130264,7 @@ insert_cleanup: wx_sqlite3UpsertDelete(db, pUpsert); wx_sqlite3SelectDelete(db, pSelect); wx_sqlite3IdListDelete(db, pColumn); - wx_sqlite3DbFree(db, aRegIdx); + if( aRegIdx ) wx_sqlite3DbNNFreeNN(db, aRegIdx); } /* Make sure "isView" and other macros defined above are undefined. Otherwise @@ -123847,7 +130543,7 @@ SQLITE_PRIVATE void wx_sqlite3GenerateConstraintChecks( db = pParse->db; v = pParse->pVdbe; assert( v!=0 ); - assert( pTab->pSelect==0 ); /* This table is not a VIEW */ + assert( !IsView(pTab) ); /* This table is not a VIEW */ nCol = pTab->nCol; /* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for @@ -123898,7 +130594,7 @@ SQLITE_PRIVATE void wx_sqlite3GenerateConstraintChecks( } if( onError==OE_Replace ){ if( b2ndPass /* REPLACE becomes ABORT on the 2nd pass */ - || pCol->pDflt==0 /* REPLACE is ABORT if no DEFAULT value */ + || pCol->iDflt==0 /* REPLACE is ABORT if no DEFAULT value */ ){ testcase( pCol->colFlags & COLFLAG_VIRTUAL ); testcase( pCol->colFlags & COLFLAG_STORED ); @@ -123920,7 +130616,8 @@ SQLITE_PRIVATE void wx_sqlite3GenerateConstraintChecks( VdbeCoverage(v); assert( (pCol->colFlags & COLFLAG_GENERATED)==0 ); nSeenReplace++; - wx_sqlite3ExprCodeCopy(pParse, pCol->pDflt, iReg); + wx_sqlite3ExprCodeCopy(pParse, + wx_sqlite3ColumnExpr(pTab, pCol), iReg); wx_sqlite3VdbeJumpHere(v, addr1); break; } @@ -123930,7 +130627,8 @@ SQLITE_PRIVATE void wx_sqlite3GenerateConstraintChecks( case OE_Rollback: case OE_Fail: { char *zMsg = wx_sqlite3MPrintf(db, "%s.%s", pTab->zName, - pCol->zName); + pCol->zCnName); + testcase( zMsg==0 && db->mallocFailed==0 ); wx_sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError, iReg); wx_sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC); @@ -124183,6 +130881,7 @@ SQLITE_PRIVATE void wx_sqlite3GenerateConstraintChecks( if( onError==OE_Replace /* IPK rule is REPLACE */ && onError!=overrideError /* Rules for other constraints are different */ && pTab->pIndex /* There exist other constraints */ + && !upsertIpkDelay /* IPK check already deferred by UPSERT */ ){ ipkTop = wx_sqlite3VdbeAddOp0(v, OP_Goto)+1; VdbeComment((v, "defer IPK REPLACE until last")); @@ -124348,7 +131047,7 @@ SQLITE_PRIVATE void wx_sqlite3GenerateConstraintChecks( testcase( wx_sqlite3TableColumnToStorage(pTab, iField)!=iField ); x = wx_sqlite3TableColumnToStorage(pTab, iField) + regNewData + 1; wx_sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i); - VdbeComment((v, "%s", pTab->aCol[iField].zName)); + VdbeComment((v, "%s", pTab->aCol[iField].zCnName)); } } wx_sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]); @@ -124400,6 +131099,7 @@ SQLITE_PRIVATE void wx_sqlite3GenerateConstraintChecks( ** This is not possible for ENABLE_PREUPDATE_HOOK builds, as the row ** must be explicitly deleted in order to ensure any pre-update hook ** is invoked. */ + assert( IsOrdinaryTable(pTab) ); #ifndef SQLITE_ENABLE_PREUPDATE_HOOK if( (ix==0 && pIdx->pNext==0) /* Condition 3 */ && pPk==pIdx /* Condition 2 */ @@ -124407,7 +131107,7 @@ SQLITE_PRIVATE void wx_sqlite3GenerateConstraintChecks( && ( 0==(db->flags&SQLITE_RecTriggers) || /* Condition 4 */ 0==wx_sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0)) && ( 0==(db->flags&SQLITE_ForeignKeys) || /* Condition 5 */ - (0==pTab->pFKey && 0==wx_sqlite3FkReferences(pTab))) + (0==pTab->u.tab.pFKey && 0==wx_sqlite3FkReferences(pTab))) ){ wx_sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; @@ -124442,13 +131142,13 @@ SQLITE_PRIVATE void wx_sqlite3GenerateConstraintChecks( x = wx_sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]); wx_sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); VdbeComment((v, "%s.%s", pTab->zName, - pTab->aCol[pPk->aiColumn[i]].zName)); + pTab->aCol[pPk->aiColumn[i]].zCnName)); } } if( isUpdate ){ /* If currently processing the PRIMARY KEY of a WITHOUT ROWID ** table, only conflict if the new PRIMARY KEY values are actually - ** different from the old. + ** different from the old. See TH3 withoutrowid04.test. ** ** For a UNIQUE index, only conflict if the PRIMARY KEY values ** of the matched index row are different from the original PRIMARY @@ -124506,7 +131206,8 @@ SQLITE_PRIVATE void wx_sqlite3GenerateConstraintChecks( assert( onError==OE_Replace ); nConflictCk = wx_sqlite3VdbeCurrentAddr(v) - addrConflictCk; - assert( nConflictCk>0 ); + assert( nConflictCk>0 || db->mallocFailed ); + testcase( nConflictCk<=0 ); testcase( nConflictCk>1 ); if( regTrigCnt ){ wx_sqlite3MultiWrite(pParse); @@ -124589,6 +131290,7 @@ SQLITE_PRIVATE void wx_sqlite3GenerateConstraintChecks( if( ipkTop ){ wx_sqlite3VdbeGoto(v, ipkTop); VdbeComment((v, "Do IPK REPLACE")); + assert( ipkBottom>0 ); wx_sqlite3VdbeJumpHere(v, ipkBottom); } @@ -124641,7 +131343,7 @@ SQLITE_PRIVATE void wx_sqlite3SetMakeRecordP5(Vdbe *v, Table *pTab){ if( pTab->pSchema->file_format<2 ) return; for(i=pTab->nCol-1; i>0; i--){ - if( pTab->aCol[i].pDflt!=0 ) break; + if( pTab->aCol[i].iDflt!=0 ) break; if( pTab->aCol[i].colFlags & COLFLAG_PRIMKEY ) break; } wx_sqlite3VdbeChangeP5(v, i+1); @@ -124706,7 +131408,7 @@ SQLITE_PRIVATE void wx_sqlite3CompleteInsertion( v = pParse->pVdbe; assert( v!=0 ); - assert( pTab->pSelect==0 ); /* This table is not a VIEW */ + assert( !IsView(pTab) ); /* This table is not a VIEW */ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ /* All REPLACE indexes are at the end of the list */ assert( pIdx->onError!=OE_Replace @@ -124719,7 +131421,6 @@ SQLITE_PRIVATE void wx_sqlite3CompleteInsertion( } pik_flags = (useSeekResult ? OPFLAG_USESEEKRESULT : 0); if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ - assert( pParse->nested==0 ); pik_flags |= OPFLAG_NCHANGE; pik_flags |= (update_flags & OPFLAG_SAVEPOSITION); if( update_flags==0 ){ @@ -124792,8 +131493,9 @@ SQLITE_PRIVATE int wx_sqlite3OpenTableAndIndices( assert( op==OP_OpenWrite || p5==0 ); if( IsVirtual(pTab) ){ /* This routine is a no-op for virtual tables. Leave the output - ** variables *piDataCur and *piIdxCur uninitialized so that valgrind - ** can detect if they are used by mistake in the caller. */ + ** variables *piDataCur and *piIdxCur set to illegal cursor numbers + ** for improved error detection. */ + *piDataCur = *piIdxCur = -999; return 0; } iDb = wx_sqlite3SchemaToIndex(pParse->db, pTab->pSchema); @@ -124934,18 +131636,13 @@ static int xferOptimization( int destHasUniqueIdx = 0; /* True if pDest has a UNIQUE index */ int regData, regRowid; /* Registers holding data and rowid */ - if( pSelect==0 ){ - return 0; /* Must be of the form INSERT INTO ... SELECT ... */ - } + assert( pSelect!=0 ); if( pParse->pWith || pSelect->pWith ){ /* Do not attempt to process this query if there are an WITH clauses ** attached to it. Proceeding may generate a false "no such table: xxx" ** error if pSelect reads from a CTE named "xxx". */ return 0; } - if( wx_sqlite3TriggerList(pParse, pDest) ){ - return 0; /* tab1 must not have triggers */ - } #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pDest) ){ return 0; /* tab1 must not be a virtual table */ @@ -125008,13 +131705,8 @@ static int xferOptimization( if( HasRowid(pDest)!=HasRowid(pSrc) ){ return 0; /* source and destination must both be WITHOUT ROWID or not */ } -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pSrc) ){ - return 0; /* tab2 must not be a virtual table */ - } -#endif - if( pSrc->pSelect ){ - return 0; /* tab2 may not be a view */ + if( !IsOrdinaryTable(pSrc) ){ + return 0; /* tab2 may not be a view or virtual table */ } if( pDest->nCol!=pSrc->nCol ){ return 0; /* Number of columns must be the same in tab1 and tab2 */ @@ -125022,6 +131714,9 @@ static int xferOptimization( if( pDest->iPKey!=pSrc->iPKey ){ return 0; /* Both tables must have the same INTEGER PRIMARY KEY */ } + if( (pDest->tabFlags & TF_Strict)!=0 && (pSrc->tabFlags & TF_Strict)==0 ){ + return 0; /* Cannot feed from a non-strict into a strict table */ + } for(i=0; inCol; i++){ Column *pDestCol = &pDest->aCol[i]; Column *pSrcCol = &pSrc->aCol[i]; @@ -125058,7 +131753,9 @@ static int xferOptimization( ** This requirement could be relaxed for VIRTUAL columns, I suppose. */ if( (pDestCol->colFlags & COLFLAG_GENERATED)!=0 ){ - if( wx_sqlite3ExprCompare(0, pSrcCol->pDflt, pDestCol->pDflt, -1)!=0 ){ + if( wx_sqlite3ExprCompare(0, + wx_sqlite3ColumnExpr(pSrc, pSrcCol), + wx_sqlite3ColumnExpr(pDest, pDestCol), -1)!=0 ){ testcase( pDestCol->colFlags & COLFLAG_VIRTUAL ); testcase( pDestCol->colFlags & COLFLAG_STORED ); return 0; /* Different generator expressions */ @@ -125068,7 +131765,8 @@ static int xferOptimization( if( pDestCol->affinity!=pSrcCol->affinity ){ return 0; /* Affinity must be the same on all columns */ } - if( wx_sqlite3_stricmp(pDestCol->zColl, pSrcCol->zColl)!=0 ){ + if( wx_sqlite3_stricmp(wx_sqlite3ColumnColl(pDestCol), + wx_sqlite3ColumnColl(pSrcCol))!=0 ){ return 0; /* Collating sequence must be the same on all columns */ } if( pDestCol->notNull && !pSrcCol->notNull ){ @@ -125076,11 +131774,15 @@ static int xferOptimization( } /* Default values for second and subsequent columns need to match. */ if( (pDestCol->colFlags & COLFLAG_GENERATED)==0 && i>0 ){ - assert( pDestCol->pDflt==0 || pDestCol->pDflt->op==TK_SPAN ); - assert( pSrcCol->pDflt==0 || pSrcCol->pDflt->op==TK_SPAN ); - if( (pDestCol->pDflt==0)!=(pSrcCol->pDflt==0) - || (pDestCol->pDflt && strcmp(pDestCol->pDflt->u.zToken, - pSrcCol->pDflt->u.zToken)!=0) + Expr *pDestExpr = wx_sqlite3ColumnExpr(pDest, pDestCol); + Expr *pSrcExpr = wx_sqlite3ColumnExpr(pSrc, pSrcCol); + assert( pDestExpr==0 || pDestExpr->op==TK_SPAN ); + assert( pDestExpr==0 || !ExprHasProperty(pDestExpr, EP_IntValue) ); + assert( pSrcExpr==0 || pSrcExpr->op==TK_SPAN ); + assert( pSrcExpr==0 || !ExprHasProperty(pSrcExpr, EP_IntValue) ); + if( (pDestExpr==0)!=(pSrcExpr==0) + || (pDestExpr!=0 && strcmp(pDestExpr->u.zToken, + pSrcExpr->u.zToken)!=0) ){ return 0; /* Default values must be the same for all columns */ } @@ -125117,7 +131819,8 @@ static int xferOptimization( ** the extra complication to make this rule less restrictive is probably ** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e] */ - if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ + assert( IsOrdinaryTable(pDest) ); + if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->u.tab.pFKey!=0 ){ return 0; } #endif @@ -125789,12 +132492,36 @@ struct wx_sqlite3_api_routines { const char *(*filename_journal)(const char*); const char *(*filename_wal)(const char*); /* Version 3.32.0 and later */ - char *(*create_filename)(const char*,const char*,const char*, + const char *(*create_filename)(const char*,const char*,const char*, int,const char**); - void (*free_filename)(char*); + void (*free_filename)(const char*); wx_sqlite3_file *(*database_file_object)(const char*); /* Version 3.34.0 and later */ int (*txn_state)(wx_sqlite3*,const char*); + /* Version 3.36.1 and later */ + wx_sqlite3_int64 (*changes64)(wx_sqlite3*); + wx_sqlite3_int64 (*total_changes64)(wx_sqlite3*); + /* Version 3.37.0 and later */ + int (*autovacuum_pages)(wx_sqlite3*, + unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), + void*, void(*)(void*)); + /* Version 3.38.0 and later */ + int (*error_offset)(wx_sqlite3*); + int (*vtab_rhs_value)(wx_sqlite3_index_info*,int,wx_sqlite3_value**); + int (*vtab_distinct)(wx_sqlite3_index_info*); + int (*vtab_in)(wx_sqlite3_index_info*,int,int); + int (*vtab_in_first)(wx_sqlite3_value*,wx_sqlite3_value**); + int (*vtab_in_next)(wx_sqlite3_value*,wx_sqlite3_value**); + /* Version 3.39.0 and later */ + int (*deserialize)(wx_sqlite3*,const char*,unsigned char*, + wx_sqlite3_int64,wx_sqlite3_int64,unsigned); + unsigned char *(*serialize)(wx_sqlite3*,const char *,wx_sqlite3_int64*, + unsigned int); + const char *(*db_name)(wx_sqlite3*,int); + /* Version 3.40.0 and later */ + int (*value_encoding)(wx_sqlite3_value*); + /* Version 3.41.0 and later */ + int (*is_interrupted)(wx_sqlite3*); }; /* @@ -126101,6 +132828,28 @@ typedef int (*wx_sqlite3_loadext_entry)( #define wx_sqlite3_database_file_object wx_sqlite3_api->database_file_object /* Version 3.34.0 and later */ #define wx_sqlite3_txn_state wx_sqlite3_api->txn_state +/* Version 3.36.1 and later */ +#define wx_sqlite3_changes64 wx_sqlite3_api->changes64 +#define wx_sqlite3_total_changes64 wx_sqlite3_api->total_changes64 +/* Version 3.37.0 and later */ +#define wx_sqlite3_autovacuum_pages wx_sqlite3_api->autovacuum_pages +/* Version 3.38.0 and later */ +#define wx_sqlite3_error_offset wx_sqlite3_api->error_offset +#define wx_sqlite3_vtab_rhs_value wx_sqlite3_api->vtab_rhs_value +#define wx_sqlite3_vtab_distinct wx_sqlite3_api->vtab_distinct +#define wx_sqlite3_vtab_in wx_sqlite3_api->vtab_in +#define wx_sqlite3_vtab_in_first wx_sqlite3_api->vtab_in_first +#define wx_sqlite3_vtab_in_next wx_sqlite3_api->vtab_in_next +/* Version 3.39.0 and later */ +#ifndef SQLITE_OMIT_DESERIALIZE +#define wx_sqlite3_deserialize wx_sqlite3_api->deserialize +#define wx_sqlite3_serialize wx_sqlite3_api->serialize +#endif +#define wx_sqlite3_db_name wx_sqlite3_api->db_name +/* Version 3.40.0 and later */ +#define wx_sqlite3_value_encoding wx_sqlite3_api->value_encoding +/* Version 3.41.0 and later */ +#define wx_sqlite3_is_interrupted wx_sqlite3_api->is_interrupted #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -126585,6 +133334,39 @@ static const wx_sqlite3_api_routines wx_sqlite3Apis = { wx_sqlite3_database_file_object, /* Version 3.34.0 and later */ wx_sqlite3_txn_state, + /* Version 3.36.1 and later */ + wx_sqlite3_changes64, + wx_sqlite3_total_changes64, + /* Version 3.37.0 and later */ + wx_sqlite3_autovacuum_pages, + /* Version 3.38.0 and later */ + wx_sqlite3_error_offset, +#ifndef SQLITE_OMIT_VIRTUALTABLE + wx_sqlite3_vtab_rhs_value, + wx_sqlite3_vtab_distinct, + wx_sqlite3_vtab_in, + wx_sqlite3_vtab_in_first, + wx_sqlite3_vtab_in_next, +#else + 0, + 0, + 0, + 0, + 0, +#endif + /* Version 3.39.0 and later */ +#ifndef SQLITE_OMIT_DESERIALIZE + wx_sqlite3_deserialize, + wx_sqlite3_serialize, +#else + 0, + 0, +#endif + wx_sqlite3_db_name, + /* Version 3.40.0 and later */ + wx_sqlite3_value_encoding, + /* Version 3.41.0 and later */ + wx_sqlite3_is_interrupted }; /* True if x is the directory separator character @@ -126620,7 +133402,7 @@ static int wx_sqlite3LoadExtension( const char *zEntry; char *zAltEntry = 0; void **aHandle; - u64 nMsg = 300 + wx_sqlite3Strlen30(zFile); + u64 nMsg = strlen(zFile); int ii; int rc; @@ -126654,6 +133436,12 @@ static int wx_sqlite3LoadExtension( zEntry = zProc ? zProc : "wx_sqlite3_extension_init"; + /* tag-20210611-1. Some dlopen() implementations will segfault if given + ** an oversize filename. Most filesystems have a pathname limit of 4K, + ** so limit the extension filename length to about twice that. + ** https://sqlite.org/forum/forumpost/08a0d6d9bf */ + if( nMsg>SQLITE_MAX_PATHLEN ) goto extension_not_found; + handle = wx_sqlite3OsDlOpen(pVfs, zFile); #if SQLITE_OS_UNIX || SQLITE_OS_WIN for(ii=0; iiaExtension[db->nExtension++] = handle; return SQLITE_OK; + +extension_not_found: + if( pzErrMsg ){ + nMsg += 300; + *pzErrMsg = zErrmsg = wx_sqlite3_malloc64(nMsg); + if( zErrmsg ){ + assert( nMsg<0x7fffffff ); /* zErrmsg would be NULL if not so */ + wx_sqlite3_snprintf((int)nMsg, zErrmsg, + "unable to open shared library [%.*s]", SQLITE_MAX_PATHLEN, zFile); + wx_sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); + } + } + return SQLITE_ERROR; } SQLITE_API int wx_sqlite3_load_extension( wx_sqlite3 *db, /* Load the extension into this database connection */ @@ -127037,13 +133829,14 @@ SQLITE_PRIVATE void wx_sqlite3AutoLoadExtensions(wx_sqlite3 *db){ #define PragTyp_SOFT_HEAP_LIMIT 35 #define PragTyp_SYNCHRONOUS 36 #define PragTyp_TABLE_INFO 37 -#define PragTyp_TEMP_STORE 38 -#define PragTyp_TEMP_STORE_DIRECTORY 39 -#define PragTyp_THREADS 40 -#define PragTyp_WAL_AUTOCHECKPOINT 41 -#define PragTyp_WAL_CHECKPOINT 42 -#define PragTyp_LOCK_STATUS 43 -#define PragTyp_STATS 44 +#define PragTyp_TABLE_LIST 38 +#define PragTyp_TEMP_STORE 39 +#define PragTyp_TEMP_STORE_DIRECTORY 40 +#define PragTyp_THREADS 41 +#define PragTyp_WAL_AUTOCHECKPOINT 42 +#define PragTyp_WAL_CHECKPOINT 43 +#define PragTyp_LOCK_STATUS 44 +#define PragTyp_STATS 45 /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ @@ -127076,45 +133869,51 @@ static const char *const pragCName[] = { /* 13 */ "pk", /* 14 */ "hidden", /* table_info reuses 8 */ - /* 15 */ "seqno", /* Used by: index_xinfo */ - /* 16 */ "cid", - /* 17 */ "name", - /* 18 */ "desc", - /* 19 */ "coll", - /* 20 */ "key", - /* 21 */ "name", /* Used by: function_list */ - /* 22 */ "builtin", - /* 23 */ "type", - /* 24 */ "enc", - /* 25 */ "narg", - /* 26 */ "flags", - /* 27 */ "tbl", /* Used by: stats */ - /* 28 */ "idx", - /* 29 */ "wdth", - /* 30 */ "hght", - /* 31 */ "flgs", - /* 32 */ "seq", /* Used by: index_list */ - /* 33 */ "name", - /* 34 */ "unique", - /* 35 */ "origin", - /* 36 */ "partial", - /* 37 */ "table", /* Used by: foreign_key_check */ - /* 38 */ "rowid", - /* 39 */ "parent", - /* 40 */ "fkid", - /* index_info reuses 15 */ - /* 41 */ "seq", /* Used by: database_list */ - /* 42 */ "name", - /* 43 */ "file", - /* 44 */ "busy", /* Used by: wal_checkpoint */ - /* 45 */ "log", - /* 46 */ "checkpointed", - /* collation_list reuses 32 */ - /* 47 */ "database", /* Used by: lock_status */ - /* 48 */ "status", - /* 49 */ "cache_size", /* Used by: default_cache_size */ + /* 15 */ "schema", /* Used by: table_list */ + /* 16 */ "name", + /* 17 */ "type", + /* 18 */ "ncol", + /* 19 */ "wr", + /* 20 */ "strict", + /* 21 */ "seqno", /* Used by: index_xinfo */ + /* 22 */ "cid", + /* 23 */ "name", + /* 24 */ "desc", + /* 25 */ "coll", + /* 26 */ "key", + /* 27 */ "name", /* Used by: function_list */ + /* 28 */ "builtin", + /* 29 */ "type", + /* 30 */ "enc", + /* 31 */ "narg", + /* 32 */ "flags", + /* 33 */ "tbl", /* Used by: stats */ + /* 34 */ "idx", + /* 35 */ "wdth", + /* 36 */ "hght", + /* 37 */ "flgs", + /* 38 */ "seq", /* Used by: index_list */ + /* 39 */ "name", + /* 40 */ "unique", + /* 41 */ "origin", + /* 42 */ "partial", + /* 43 */ "table", /* Used by: foreign_key_check */ + /* 44 */ "rowid", + /* 45 */ "parent", + /* 46 */ "fkid", + /* index_info reuses 21 */ + /* 47 */ "seq", /* Used by: database_list */ + /* 48 */ "name", + /* 49 */ "file", + /* 50 */ "busy", /* Used by: wal_checkpoint */ + /* 51 */ "log", + /* 52 */ "checkpointed", + /* collation_list reuses 38 */ + /* 53 */ "database", /* Used by: lock_status */ + /* 54 */ "status", + /* 55 */ "cache_size", /* Used by: default_cache_size */ /* module_list pragma_list reuses 9 */ - /* 50 */ "timeout", /* Used by: busy_timeout */ + /* 56 */ "timeout", /* Used by: busy_timeout */ }; /* Definitions of all built-in pragmas */ @@ -127165,7 +133964,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "busy_timeout", /* ePragTyp: */ PragTyp_BUSY_TIMEOUT, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 50, 1, + /* ColNames: */ 56, 1, /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "cache_size", @@ -127204,7 +134003,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "collation_list", /* ePragTyp: */ PragTyp_COLLATION_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 32, 2, + /* ColNames: */ 38, 2, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) @@ -127238,15 +134037,15 @@ static const PragmaName aPragmaName[] = { #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) {/* zName: */ "database_list", /* ePragTyp: */ PragTyp_DATABASE_LIST, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, - /* ColNames: */ 41, 3, + /* ePragFlg: */ PragFlg_Result0, + /* ColNames: */ 47, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) {/* zName: */ "default_cache_size", /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, - /* ColNames: */ 49, 1, + /* ColNames: */ 55, 1, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) @@ -127276,7 +134075,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "foreign_key_check", /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 37, 4, + /* ColNames: */ 43, 4, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FOREIGN_KEY) @@ -127319,7 +134118,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "function_list", /* ePragTyp: */ PragTyp_FUNCTION_LIST, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 21, 6, + /* ColNames: */ 27, 6, /* iArg: */ 0 }, #endif #endif @@ -127348,23 +134147,23 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "index_info", /* ePragTyp: */ PragTyp_INDEX_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 15, 3, + /* ColNames: */ 21, 3, /* iArg: */ 0 }, {/* zName: */ "index_list", /* ePragTyp: */ PragTyp_INDEX_LIST, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 32, 5, + /* ColNames: */ 38, 5, /* iArg: */ 0 }, {/* zName: */ "index_xinfo", /* ePragTyp: */ PragTyp_INDEX_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 15, 6, + /* ColNames: */ 21, 6, /* iArg: */ 1 }, #endif #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) {/* zName: */ "integrity_check", /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif @@ -127398,7 +134197,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "lock_status", /* ePragTyp: */ PragTyp_LOCK_STATUS, /* ePragFlg: */ PragFlg_Result0, - /* ColNames: */ 47, 2, + /* ColNames: */ 53, 2, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) @@ -127472,7 +134271,7 @@ static const PragmaName aPragmaName[] = { #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) {/* zName: */ "quick_check", /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif @@ -127537,7 +134336,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "stats", /* ePragTyp: */ PragTyp_STATS, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, - /* ColNames: */ 27, 5, + /* ColNames: */ 33, 5, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) @@ -127553,6 +134352,11 @@ static const PragmaName aPragmaName[] = { /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 8, 6, /* iArg: */ 0 }, + {/* zName: */ "table_list", + /* ePragTyp: */ PragTyp_TABLE_LIST, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1, + /* ColNames: */ 15, 6, + /* iArg: */ 0 }, {/* zName: */ "table_xinfo", /* ePragTyp: */ PragTyp_TABLE_INFO, /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, @@ -127628,7 +134432,7 @@ static const PragmaName aPragmaName[] = { {/* zName: */ "wal_checkpoint", /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, /* ePragFlg: */ PragFlg_NeedSchema, - /* ColNames: */ 44, 3, + /* ColNames: */ 50, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) @@ -127639,7 +134443,7 @@ static const PragmaName aPragmaName[] = { /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, #endif }; -/* Number of pragmas: 67 on by default, 77 total. */ +/* Number of pragmas: 68 on by default, 78 total. */ /************** End of pragma.h **********************************************/ /************** Continuing where we left off in pragma.c *********************/ @@ -127921,15 +134725,16 @@ static void pragmaFunclistLine( int isBuiltin, /* True if this is a built-in function */ int showInternFuncs /* True if showing internal functions */ ){ + u32 mask = + SQLITE_DETERMINISTIC | + SQLITE_DIRECTONLY | + SQLITE_SUBTYPE | + SQLITE_INNOCUOUS | + SQLITE_FUNC_INTERNAL + ; + if( showInternFuncs ) mask = 0xffffffff; for(; p; p=p->pNext){ const char *zType; - static const u32 mask = - SQLITE_DETERMINISTIC | - SQLITE_DIRECTONLY | - SQLITE_SUBTYPE | - SQLITE_INNOCUOUS | - SQLITE_FUNC_INTERNAL - ; static const char *azEnc[] = { 0, "utf8", "utf16le", "utf16be" }; assert( SQLITE_FUNC_ENCMASK==0x3 ); @@ -128081,7 +134886,11 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( /* Locate the pragma in the lookup table */ pPragma = pragmaLocate(zLeft); - if( pPragma==0 ) goto pragma_out; + if( pPragma==0 ){ + /* IMP: R-43042-22504 No error messages are generated if an + ** unknown pragma is issued. */ + goto pragma_out; + } /* Make sure the database schema is loaded if the pragma requires that */ if( (pPragma->mPragFlg & PragFlg_NeedSchema)!=0 ){ @@ -128417,7 +135226,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( */ #ifndef SQLITE_OMIT_AUTOVACUUM case PragTyp_INCREMENTAL_VACUUM: { - int iLimit, addr; + int iLimit = 0, addr; if( zRight==0 || !wx_sqlite3GetInt32(zRight, &iLimit) || iLimit<=0 ){ iLimit = 0x7fffffff; } @@ -128574,6 +135383,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( ** */ case PragTyp_TEMP_STORE_DIRECTORY: { + wx_sqlite3_mutex_enter(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); if( !zRight ){ returnSingleText(v, wx_sqlite3_temp_directory); }else{ @@ -128583,6 +135393,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( rc = wx_sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); if( rc!=SQLITE_OK || res==0 ){ wx_sqlite3ErrorMsg(pParse, "not a writable directory"); + wx_sqlite3_mutex_leave(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); goto pragma_out; } } @@ -128600,6 +135411,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( } #endif /* SQLITE_OMIT_WSD */ } + wx_sqlite3_mutex_leave(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); break; } @@ -128618,6 +135430,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( ** */ case PragTyp_DATA_STORE_DIRECTORY: { + wx_sqlite3_mutex_enter(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); if( !zRight ){ returnSingleText(v, wx_sqlite3_data_directory); }else{ @@ -128627,6 +135440,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( rc = wx_sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); if( rc!=SQLITE_OK || res==0 ){ wx_sqlite3ErrorMsg(pParse, "not a writable directory"); + wx_sqlite3_mutex_leave(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); goto pragma_out; } } @@ -128638,6 +135452,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( } #endif /* SQLITE_OMIT_WSD */ } + wx_sqlite3_mutex_leave(wx_sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); break; } #endif @@ -128731,6 +135546,14 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( }else{ db->flags &= ~mask; if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0; + if( (mask & SQLITE_WriteSchema)!=0 + && wx_sqlite3_stricmp(zRight, "reset")==0 + ){ + /* IMP: R-60817-01178 If the argument is "RESET" then schema + ** writing is disabled (as with "PRAGMA writable_schema=OFF") and, + ** in addition, the schema is reloaded. */ + wx_sqlite3ResetAllSchemasOfConnection(db); + } } /* Many of the flag-pragmas modify the code generated by the SQL @@ -128771,6 +135594,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( wx_sqlite3ViewGetColumnNames(pParse, pTab); for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ int isHidden = 0; + const Expr *pColExpr; if( pCol->colFlags & COLFLAG_NOINSERT ){ if( pPragma->iArg==0 ){ nHidden++; @@ -128791,13 +135615,16 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( }else{ for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){} } - assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN || isHidden>=2 ); + pColExpr = wx_sqlite3ColumnExpr(pTab,pCol); + assert( pColExpr==0 || pColExpr->op==TK_SPAN || isHidden>=2 ); + assert( pColExpr==0 || !ExprHasProperty(pColExpr, EP_IntValue) + || isHidden>=2 ); wx_sqlite3VdbeMultiLoad(v, 1, pPragma->iArg ? "issisii" : "issisi", i-nHidden, - pCol->zName, + pCol->zCnName, wx_sqlite3ColumnType(pCol,""), pCol->notNull ? 1 : 0, - pCol->pDflt && isHidden<2 ? pCol->pDflt->u.zToken : 0, + (isHidden>=2 || pColExpr==0) ? 0 : pColExpr->u.zToken, k, isHidden); } @@ -128805,6 +135632,85 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( } break; + /* + ** PRAGMA table_list + ** + ** Return a single row for each table, virtual table, or view in the + ** entire schema. + ** + ** schema: Name of attached database hold this table + ** name: Name of the table itself + ** type: "table", "view", "virtual", "shadow" + ** ncol: Number of columns + ** wr: True for a WITHOUT ROWID table + ** strict: True for a STRICT table + */ + case PragTyp_TABLE_LIST: { + int ii; + pParse->nMem = 6; + wx_sqlite3CodeVerifyNamedSchema(pParse, zDb); + for(ii=0; iinDb; ii++){ + HashElem *k; + Hash *pHash; + int initNCol; + if( zDb && wx_sqlite3_stricmp(zDb, db->aDb[ii].zDbSName)!=0 ) continue; + + /* Ensure that the Table.nCol field is initialized for all views + ** and virtual tables. Each time we initialize a Table.nCol value + ** for a table, that can potentially disrupt the hash table, so restart + ** the initialization scan. + */ + pHash = &db->aDb[ii].pSchema->tblHash; + initNCol = sqliteHashCount(pHash); + while( initNCol-- ){ + for(k=sqliteHashFirst(pHash); 1; k=sqliteHashNext(k) ){ + Table *pTab; + if( k==0 ){ initNCol = 0; break; } + pTab = sqliteHashData(k); + if( pTab->nCol==0 ){ + char *zSql = wx_sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName); + if( zSql ){ + wx_sqlite3_stmt *pDummy = 0; + (void)wx_sqlite3_prepare(db, zSql, -1, &pDummy, 0); + (void)wx_sqlite3_finalize(pDummy); + wx_sqlite3DbFree(db, zSql); + } + if( db->mallocFailed ){ + wx_sqlite3ErrorMsg(db->pParse, "out of memory"); + db->pParse->rc = SQLITE_NOMEM_BKPT; + } + pHash = &db->aDb[ii].pSchema->tblHash; + break; + } + } + } + + for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k) ){ + Table *pTab = sqliteHashData(k); + const char *zType; + if( zRight && wx_sqlite3_stricmp(zRight, pTab->zName)!=0 ) continue; + if( IsView(pTab) ){ + zType = "view"; + }else if( IsVirtual(pTab) ){ + zType = "virtual"; + }else if( pTab->tabFlags & TF_Shadow ){ + zType = "shadow"; + }else{ + zType = "table"; + } + wx_sqlite3VdbeMultiLoad(v, 1, "sssiii", + db->aDb[ii].zDbSName, + wx_sqlite3PreferredTableName(pTab->zName), + zType, + pTab->nCol, + (pTab->tabFlags & TF_WithoutRowid)!=0, + (pTab->tabFlags & TF_Strict)!=0 + ); + } + } + } + break; + #ifdef SQLITE_DEBUG case PragTyp_STATS: { Index *pIdx; @@ -128814,7 +135720,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); wx_sqlite3VdbeMultiLoad(v, 1, "ssiii", - pTab->zName, + wx_sqlite3PreferredTableName(pTab->zName), 0, pTab->szTabRow, pTab->nRowLogEst, @@ -128864,7 +135770,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( for(i=0; iaiColumn[i]; wx_sqlite3VdbeMultiLoad(v, 1, "iisX", i, cnum, - cnum<0 ? 0 : pTab->aCol[cnum].zName); + cnum<0 ? 0 : pTab->aCol[cnum].zCnName); if( pPragma->iArg ){ wx_sqlite3VdbeMultiLoad(v, 4, "isiX", pIdx->aSortOrder[i], @@ -128933,11 +135839,13 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( pParse->nMem = 6; for(i=0; iu.pHash ){ + assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); pragmaFunclistLine(v, p, 1, showInternFunc); } } for(j=sqliteHashFirst(&db->aFunc); j; j=sqliteHashNext(j)){ p = (FuncDef*)sqliteHashData(j); + assert( (p->funcFlags & SQLITE_FUNC_BUILTIN)==0 ); pragmaFunclistLine(v, p, 0, showInternFunc); } } @@ -128971,8 +135879,8 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( FKey *pFK; Table *pTab; pTab = wx_sqlite3FindTable(db, zRight, zDb); - if( pTab ){ - pFK = pTab->pFKey; + if( pTab && IsOrdinaryTable(pTab) ){ + pFK = pTab->u.tab.pFKey; if( pFK ){ int iTabDb = wx_sqlite3SchemaToIndex(db, pTab->pSchema); int i = 0; @@ -128985,7 +135893,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( i, j, pFK->zTo, - pTab->aCol[pFK->aCol[j].iFrom].zName, + pTab->aCol[pFK->aCol[j].iFrom].zCnName, pFK->aCol[j].zCol, actionName(pFK->aAction[1]), /* ON UPDATE */ actionName(pFK->aAction[0]), /* ON DELETE */ @@ -129012,7 +135920,6 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( HashElem *k; /* Loop counter: Next table in schema */ int x; /* result variable */ int regResult; /* 3 registers to hold a result row */ - int regKey; /* Register to hold key for checking the FK */ int regRow; /* Registers to hold a row from pTab */ int addrTop; /* Top of a loop checking foreign keys */ int addrOk; /* Jump here if the key is OK */ @@ -129020,7 +135927,6 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( regResult = pParse->nMem+1; pParse->nMem += 4; - regKey = ++pParse->nMem; regRow = ++pParse->nMem; k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); while( k ){ @@ -129031,7 +135937,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( pTab = (Table*)sqliteHashData(k); k = sqliteHashNext(k); } - if( pTab==0 || pTab->pFKey==0 ) continue; + if( pTab==0 || !IsOrdinaryTable(pTab) || pTab->u.tab.pFKey==0 ) continue; iDb = wx_sqlite3SchemaToIndex(db, pTab->pSchema); zDb = db->aDb[iDb].zDbSName; wx_sqlite3CodeVerifySchema(pParse, iDb); @@ -129039,7 +135945,8 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; wx_sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); wx_sqlite3VdbeLoadString(v, regResult, pTab->zName); - for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ + assert( IsOrdinaryTable(pTab) ); + for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){ pParent = wx_sqlite3FindTable(db, pFK->zTo, zDb); if( pParent==0 ) continue; pIdx = 0; @@ -129061,7 +135968,8 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( if( pFK ) break; if( pParse->nTabnTab = i; addrTop = wx_sqlite3VdbeAddOp1(v, OP_Rewind, 0); VdbeCoverage(v); - for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ + assert( IsOrdinaryTable(pTab) ); + for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){ pParent = wx_sqlite3FindTable(db, pFK->zTo, zDb); pIdx = 0; aiCols = 0; @@ -129075,6 +135983,7 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( ** regRow..regRow+n. If any of the child key values are NULL, this ** row cannot cause an FK violation. Jump directly to addrOk in ** this case. */ + if( regRow+pFK->nCol>pParse->nMem ) pParse->nMem = regRow+pFK->nCol; for(j=0; jnCol; j++){ int iCol = aiCols ? aiCols[j] : pFK->aCol[j].iFrom; wx_sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, iCol, regRow+j); @@ -129084,9 +135993,9 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( /* Generate code to query the parent index for a matching parent ** key. If a match is found, jump to addrOk. */ if( pIdx ){ - wx_sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey, + wx_sqlite3VdbeAddOp4(v, OP_Affinity, regRow, pFK->nCol, 0, wx_sqlite3IndexAffinityStr(db,pIdx), pFK->nCol); - wx_sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); + wx_sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regRow, pFK->nCol); VdbeCoverage(v); }else if( pParent ){ int jmp = wx_sqlite3VdbeCurrentAddr(v)+2; @@ -129257,14 +136166,24 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); Index *pIdx, *pPk; - Index *pPrior = 0; + Index *pPrior = 0; /* Previous index */ int loopTop; int iDataCur, iIdxCur; int r1 = -1; + int bStrict; /* True for a STRICT table */ + int r2; /* Previous key for WITHOUT ROWID tables */ + int mxCol; /* Maximum non-virtual column number */ - if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ + if( !IsOrdinaryTable(pTab) ) continue; if( pObjTab && pObjTab!=pTab ) continue; - pPk = HasRowid(pTab) ? 0 : wx_sqlite3PrimaryKeyIndex(pTab); + if( isQuick || HasRowid(pTab) ){ + pPk = 0; + r2 = 0; + }else{ + pPk = wx_sqlite3PrimaryKeyIndex(pTab); + r2 = wx_sqlite3GetTempRange(pParse, pPk->nKeyCol); + wx_sqlite3VdbeAddOp3(v, OP_Null, 1, r2, r2+pPk->nKeyCol-1); + } wx_sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, 1, 0, &iDataCur, &iIdxCur); /* reg[7] counts the number of entries in the table. @@ -129278,27 +136197,166 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( assert( wx_sqlite3NoTempsInRange(pParse,1,7+j) ); wx_sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v); loopTop = wx_sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1); + + /* Fetch the right-most column from the table. This will cause + ** the entire record header to be parsed and sanity checked. It + ** will also prepopulate the cursor column cache that is used + ** by the OP_IsType code, so it is a required step. + */ + assert( !IsVirtual(pTab) ); + if( HasRowid(pTab) ){ + mxCol = -1; + for(j=0; jnCol; j++){ + if( (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)==0 ) mxCol++; + } + if( mxCol==pTab->iPKey ) mxCol--; + }else{ + /* COLFLAG_VIRTUAL columns are not included in the WITHOUT ROWID + ** PK index column-count, so there is no need to account for them + ** in this case. */ + mxCol = wx_sqlite3PrimaryKeyIndex(pTab)->nColumn-1; + } + if( mxCol>=0 ){ + wx_sqlite3VdbeAddOp3(v, OP_Column, iDataCur, mxCol, 3); + wx_sqlite3VdbeTypeofColumn(v, 3); + } + if( !isQuick ){ - /* Sanity check on record header decoding */ - wx_sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nNVCol-1,3); - wx_sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); + if( pPk ){ + /* Verify WITHOUT ROWID keys are in ascending order */ + int a1; + char *zErr; + a1 = wx_sqlite3VdbeAddOp4Int(v, OP_IdxGT, iDataCur, 0,r2,pPk->nKeyCol); + VdbeCoverage(v); + wx_sqlite3VdbeAddOp1(v, OP_IsNull, r2); VdbeCoverage(v); + zErr = wx_sqlite3MPrintf(db, + "row not in PRIMARY KEY order for %s", + pTab->zName); + wx_sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); + integrityCheckResultRow(v); + wx_sqlite3VdbeJumpHere(v, a1); + wx_sqlite3VdbeJumpHere(v, a1+1); + for(j=0; jnKeyCol; j++){ + wx_sqlite3ExprCodeLoadIndexColumn(pParse, pPk, iDataCur, j, r2+j); + } + } } - /* Verify that all NOT NULL columns really are NOT NULL */ + /* Verify datatypes for all columns: + ** + ** (1) NOT NULL columns may not contain a NULL + ** (2) Datatype must be exact for non-ANY columns in STRICT tables + ** (3) Datatype for TEXT columns in non-STRICT tables must be + ** NULL, TEXT, or BLOB. + ** (4) Datatype for numeric columns in non-STRICT tables must not + ** be a TEXT value that can be losslessly converted to numeric. + */ + bStrict = (pTab->tabFlags & TF_Strict)!=0; for(j=0; jnCol; j++){ char *zErr; - int jmp2; + Column *pCol = pTab->aCol + j; /* The column to be checked */ + int labelError; /* Jump here to report an error */ + int labelOk; /* Jump here if all looks ok */ + int p1, p3, p4; /* Operands to the OP_IsType opcode */ + int doTypeCheck; /* Check datatypes (besides NOT NULL) */ + if( j==pTab->iPKey ) continue; - if( pTab->aCol[j].notNull==0 ) continue; - wx_sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); - if( wx_sqlite3VdbeGetOp(v,-1)->opcode==OP_Column ){ - wx_sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); + if( bStrict ){ + doTypeCheck = pCol->eCType>COLTYPE_ANY; + }else{ + doTypeCheck = pCol->affinity>SQLITE_AFF_BLOB; } - jmp2 = wx_sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v); - zErr = wx_sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, - pTab->aCol[j].zName); - wx_sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); + if( pCol->notNull==0 && !doTypeCheck ) continue; + + /* Compute the operands that will be needed for OP_IsType */ + p4 = SQLITE_NULL; + if( pCol->colFlags & COLFLAG_VIRTUAL ){ + wx_sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); + p1 = -1; + p3 = 3; + }else{ + if( pCol->iDflt ){ + wx_sqlite3_value *pDfltValue = 0; + wx_sqlite3ValueFromExpr(db, wx_sqlite3ColumnExpr(pTab,pCol), ENC(db), + pCol->affinity, &pDfltValue); + if( pDfltValue ){ + p4 = wx_sqlite3_value_type(pDfltValue); + wx_sqlite3ValueFree(pDfltValue); + } + } + p1 = iDataCur; + if( !HasRowid(pTab) ){ + testcase( j!=wx_sqlite3TableColumnToStorage(pTab, j) ); + p3 = wx_sqlite3TableColumnToIndex(wx_sqlite3PrimaryKeyIndex(pTab), j); + }else{ + p3 = wx_sqlite3TableColumnToStorage(pTab,j); + testcase( p3!=j); + } + } + + labelError = wx_sqlite3VdbeMakeLabel(pParse); + labelOk = wx_sqlite3VdbeMakeLabel(pParse); + if( pCol->notNull ){ + /* (1) NOT NULL columns may not contain a NULL */ + int jmp2 = wx_sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); + wx_sqlite3VdbeChangeP5(v, 0x0f); + VdbeCoverage(v); + zErr = wx_sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, + pCol->zCnName); + wx_sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); + if( doTypeCheck ){ + wx_sqlite3VdbeGoto(v, labelError); + wx_sqlite3VdbeJumpHere(v, jmp2); + }else{ + /* VDBE byte code will fall thru */ + } + } + if( bStrict && doTypeCheck ){ + /* (2) Datatype must be exact for non-ANY columns in STRICT tables*/ + static unsigned char aStdTypeMask[] = { + 0x1f, /* ANY */ + 0x18, /* BLOB */ + 0x11, /* INT */ + 0x11, /* INTEGER */ + 0x13, /* REAL */ + 0x14 /* TEXT */ + }; + wx_sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); + assert( pCol->eCType>=1 && pCol->eCType<=sizeof(aStdTypeMask) ); + wx_sqlite3VdbeChangeP5(v, aStdTypeMask[pCol->eCType-1]); + VdbeCoverage(v); + zErr = wx_sqlite3MPrintf(db, "non-%s value in %s.%s", + wx_sqlite3StdType[pCol->eCType-1], + pTab->zName, pTab->aCol[j].zCnName); + wx_sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); + }else if( !bStrict && pCol->affinity==SQLITE_AFF_TEXT ){ + /* (3) Datatype for TEXT columns in non-STRICT tables must be + ** NULL, TEXT, or BLOB. */ + wx_sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); + wx_sqlite3VdbeChangeP5(v, 0x1c); /* NULL, TEXT, or BLOB */ + VdbeCoverage(v); + zErr = wx_sqlite3MPrintf(db, "NUMERIC value in %s.%s", + pTab->zName, pTab->aCol[j].zCnName); + wx_sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); + }else if( !bStrict && pCol->affinity>=SQLITE_AFF_NUMERIC ){ + /* (4) Datatype for numeric columns in non-STRICT tables must not + ** be a TEXT value that can be converted to numeric. */ + wx_sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); + wx_sqlite3VdbeChangeP5(v, 0x1b); /* NULL, INT, FLOAT, or BLOB */ + VdbeCoverage(v); + if( p1>=0 ){ + wx_sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); + } + wx_sqlite3VdbeAddOp4(v, OP_Affinity, 3, 1, 0, "C", P4_STATIC); + wx_sqlite3VdbeAddOp4Int(v, OP_IsType, -1, labelOk, 3, p4); + wx_sqlite3VdbeChangeP5(v, 0x1c); /* NULL, TEXT, or BLOB */ + VdbeCoverage(v); + zErr = wx_sqlite3MPrintf(db, "TEXT value in %s.%s", + pTab->zName, pTab->aCol[j].zCnName); + wx_sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); + } + wx_sqlite3VdbeResolveLabel(v, labelError); integrityCheckResultRow(v); - wx_sqlite3VdbeJumpHere(v, jmp2); + wx_sqlite3VdbeResolveLabel(v, labelOk); } /* Verify CHECK constraints */ if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ @@ -129327,7 +136385,8 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( if( !isQuick ){ /* Omit the remaining tests for quick_check */ /* Validate index entries for the current row */ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - int jmp2, jmp3, jmp4, jmp5; + int jmp2, jmp3, jmp4, jmp5, label6; + int kk; int ckUniq = wx_sqlite3VdbeMakeLabel(pParse); if( pPk==pIdx ) continue; r1 = wx_sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3, @@ -129345,13 +136404,49 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( wx_sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3); jmp4 = integrityCheckResultRow(v); wx_sqlite3VdbeJumpHere(v, jmp2); + + /* The OP_IdxRowid opcode is an optimized version of OP_Column + ** that extracts the rowid off the end of the index record. + ** But it only works correctly if index record does not have + ** any extra bytes at the end. Verify that this is the case. */ + if( HasRowid(pTab) ){ + int jmp7; + wx_sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur+j, 3); + jmp7 = wx_sqlite3VdbeAddOp3(v, OP_Eq, 3, 0, r1+pIdx->nColumn-1); + VdbeCoverage(v); + wx_sqlite3VdbeLoadString(v, 3, + "rowid not at end-of-record for row "); + wx_sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); + wx_sqlite3VdbeLoadString(v, 4, " of index "); + wx_sqlite3VdbeGoto(v, jmp5-1); + wx_sqlite3VdbeJumpHere(v, jmp7); + } + + /* Any indexed columns with non-BINARY collations must still hold + ** the exact same text value as the table. */ + label6 = 0; + for(kk=0; kknKeyCol; kk++){ + if( pIdx->azColl[kk]==wx_sqlite3StrBINARY ) continue; + if( label6==0 ) label6 = wx_sqlite3VdbeMakeLabel(pParse); + wx_sqlite3VdbeAddOp3(v, OP_Column, iIdxCur+j, kk, 3); + wx_sqlite3VdbeAddOp3(v, OP_Ne, 3, label6, r1+kk); VdbeCoverage(v); + } + if( label6 ){ + int jmp6 = wx_sqlite3VdbeAddOp0(v, OP_Goto); + wx_sqlite3VdbeResolveLabel(v, label6); + wx_sqlite3VdbeLoadString(v, 3, "row "); + wx_sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); + wx_sqlite3VdbeLoadString(v, 4, " values differ from index "); + wx_sqlite3VdbeGoto(v, jmp5-1); + wx_sqlite3VdbeJumpHere(v, jmp6); + } + /* For UNIQUE indexes, verify that only one entry exists with the ** current key. The entry is unique if (1) any column is NULL ** or (2) the next entry has a different key */ if( IsUniqueIndex(pIdx) ){ int uniqOk = wx_sqlite3VdbeMakeLabel(pParse); int jmp6; - int kk; for(kk=0; kknKeyCol; kk++){ int iCol = pIdx->aiColumn[kk]; assert( iCol!=XN_ROWID && iColnCol ); @@ -129386,6 +136481,9 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( integrityCheckResultRow(v); wx_sqlite3VdbeJumpHere(v, addr); } + if( pPk ){ + wx_sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol); + } } } } @@ -129536,6 +136634,11 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( aOp[1].p2 = iCookie; aOp[1].p3 = wx_sqlite3Atoi(zRight); aOp[1].p5 = 1; + if( iCookie==BTREE_SCHEMA_VERSION && (db->flags & SQLITE_Defensive)!=0 ){ + /* Do not allow the use of PRAGMA schema_version=VALUE in defensive + ** mode. Change the OP_SetCookie opcode into a no-op. */ + aOp[1].opcode = OP_Noop; + } }else{ /* Read the specified cookie value */ static const VdbeOpList readCookie[] = { @@ -129832,12 +136935,12 @@ SQLITE_PRIVATE void wx_sqlite3Pragma( case PragTyp_ANALYSIS_LIMIT: { wx_sqlite3_int64 N; if( zRight - && wx_sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK + && wx_sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK /* IMP: R-40975-20399 */ && N>=0 ){ db->nAnalysisLimit = (int)(N&0x7fffffff); } - returnSingleInt(v, db->nAnalysisLimit); + returnSingleInt(v, db->nAnalysisLimit); /* IMP: R-57594-65522 */ break; } @@ -130239,10 +137342,15 @@ static void corruptSchema( pData->rc = SQLITE_NOMEM_BKPT; }else if( pData->pzErrMsg[0]!=0 ){ /* A error message has already been generated. Do not overwrite it */ - }else if( pData->mInitFlags & (INITFLAG_AlterRename|INITFLAG_AlterDrop) ){ + }else if( pData->mInitFlags & (INITFLAG_AlterMask) ){ + static const char *azAlterType[] = { + "rename", + "drop column", + "add column" + }; *pData->pzErrMsg = wx_sqlite3MPrintf(db, "error in %s %s after %s: %s", azObj[0], azObj[1], - (pData->mInitFlags & INITFLAG_AlterRename) ? "rename" : "drop column", + azAlterType[(pData->mInitFlags&INITFLAG_AlterMask)-1], zExtra ); pData->rc = SQLITE_ERROR; @@ -130306,6 +137414,7 @@ SQLITE_PRIVATE int wx_sqlite3InitCallback(void *pInit, int argc, char **argv, ch UNUSED_PARAMETER2(NotUsed, argc); assert( wx_sqlite3_mutex_held(db->mutex) ); db->mDbFlags |= DBFLAG_EncodingFixed; + if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ pData->nInitRow++; if( db->mallocFailed ){ corruptSchema(pData, argv, 0); @@ -130313,7 +137422,6 @@ SQLITE_PRIVATE int wx_sqlite3InitCallback(void *pInit, int argc, char **argv, ch } assert( iDb>=0 && iDbnDb ); - if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ if( argv[3]==0 ){ corruptSchema(pData, argv, 0); }else if( argv[4] @@ -130344,7 +137452,7 @@ SQLITE_PRIVATE int wx_sqlite3InitCallback(void *pInit, int argc, char **argv, ch } } db->init.orphanTrigger = 0; - db->init.azInit = argv; + db->init.azInit = (const char**)argv; pStmt = 0; TESTONLY(rcp = ) wx_sqlite3Prepare(db, argv[4], -1, 0, 0, &pStmt, 0); rc = db->errCode; @@ -130363,6 +137471,7 @@ SQLITE_PRIVATE int wx_sqlite3InitCallback(void *pInit, int argc, char **argv, ch } } } + db->init.azInit = wx_sqlite3StdType; /* Any array of string ptrs will do */ wx_sqlite3_finalize(pStmt); }else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){ corruptSchema(pData, argv, 0); @@ -130510,7 +137619,12 @@ SQLITE_PRIVATE int wx_sqlite3InitOne(wx_sqlite3 *db, int iDb, char **pzErrMsg, u #else encoding = SQLITE_UTF8; #endif - wx_sqlite3SetTextEncoding(db, encoding); + if( db->nVdbeActive>0 && encoding!=ENC(db) ){ + rc = SQLITE_LOCKED; + goto initone_error_out; + }else{ + wx_sqlite3SetTextEncoding(db, encoding); + } }else{ /* If opening an attached database, the encoding much match ENC(db) */ if( (meta[BTREE_TEXT_ENCODING-1] & 3)!=ENC(db) ){ @@ -130587,18 +137701,22 @@ SQLITE_PRIVATE int wx_sqlite3InitOne(wx_sqlite3 *db, int iDb, char **pzErrMsg, u } #endif } + assert( pDb == &(db->aDb[iDb]) ); if( db->mallocFailed ){ rc = SQLITE_NOMEM_BKPT; wx_sqlite3ResetAllSchemasOfConnection(db); - } - if( rc==SQLITE_OK || (db->flags&SQLITE_NoSchemaError)){ - /* Black magic: If the SQLITE_NoSchemaError flag is set, then consider - ** the schema loaded, even if errors occurred. In this situation the - ** current wx_sqlite3_prepare() operation will fail, but the following one - ** will attempt to compile the supplied statement against whatever subset - ** of the schema was loaded before the error occurred. The primary - ** purpose of this is to allow access to the sqlite_schema table - ** even when its contents have been corrupted. + pDb = &db->aDb[iDb]; + }else + if( rc==SQLITE_OK || ((db->flags&SQLITE_NoSchemaError) && rc!=SQLITE_NOMEM)){ + /* Hack: If the SQLITE_NoSchemaError flag is set, then consider + ** the schema loaded, even if errors (other than OOM) occurred. In + ** this situation the current wx_sqlite3_prepare() operation will fail, + ** but the following one will attempt to compile the supplied statement + ** against whatever subset of the schema was loaded before the error + ** occurred. + ** + ** The primary purpose of this is to allow access to the sqlite_schema + ** table even when its contents have been corrupted. */ DbSetProperty(db, iDb, DB_SchemaLoaded); rc = SQLITE_OK; @@ -130708,6 +137826,7 @@ static void schemaIsValid(Parse *pParse){ rc = wx_sqlite3BtreeBeginTrans(pBt, 0, 0); if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ wx_sqlite3OomFault(db); + pParse->rc = SQLITE_NOMEM; } if( rc!=SQLITE_OK ) return; openedTransaction = 1; @@ -130719,8 +137838,8 @@ static void schemaIsValid(Parse *pParse){ wx_sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); assert( wx_sqlite3SchemaMutexHeld(db, iDb, 0) ); if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ + if( DbHasProperty(db, iDb, DB_SchemaLoaded) ) pParse->rc = SQLITE_SCHEMA; wx_sqlite3ResetOneSchema(db, iDb); - pParse->rc = SQLITE_SCHEMA; } /* Close the transaction, if one was opened. */ @@ -130767,23 +137886,30 @@ SQLITE_PRIVATE int wx_sqlite3SchemaToIndex(wx_sqlite3 *db, Schema *pSchema){ /* ** Free all memory allocations in the pParse object */ -SQLITE_PRIVATE void wx_sqlite3ParserReset(Parse *pParse){ +SQLITE_PRIVATE void wx_sqlite3ParseObjectReset(Parse *pParse){ wx_sqlite3 *db = pParse->db; + assert( db!=0 ); + assert( db->pParse==pParse ); + assert( pParse->nested==0 ); +#ifndef SQLITE_OMIT_SHARED_CACHE + if( pParse->aTableLock ) wx_sqlite3DbNNFreeNN(db, pParse->aTableLock); +#endif while( pParse->pCleanup ){ ParseCleanup *pCleanup = pParse->pCleanup; pParse->pCleanup = pCleanup->pNext; pCleanup->xCleanup(db, pCleanup->pPtr); - wx_sqlite3DbFreeNN(db, pCleanup); + wx_sqlite3DbNNFreeNN(db, pCleanup); } - wx_sqlite3DbFree(db, pParse->aLabel); + if( pParse->aLabel ) wx_sqlite3DbNNFreeNN(db, pParse->aLabel); if( pParse->pConstExpr ){ wx_sqlite3ExprListDelete(db, pParse->pConstExpr); } - if( db ){ - assert( db->lookaside.bDisable >= pParse->disableLookaside ); - db->lookaside.bDisable -= pParse->disableLookaside; - db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue; - } + assert( db->lookaside.bDisable >= pParse->disableLookaside ); + db->lookaside.bDisable -= pParse->disableLookaside; + db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue; + assert( pParse->db->pParse==pParse ); + db->pParse = pParse->pOuterParse; + pParse->db = 0; pParse->disableLookaside = 0; } @@ -130796,7 +137922,7 @@ SQLITE_PRIVATE void wx_sqlite3ParserReset(Parse *pParse){ ** cost for this mechansim (an extra malloc), so it should not be used ** for common cleanups that happen on most calls. But for less ** common cleanups, we save a single NULL-pointer comparison in -** wx_sqlite3ParserReset(), which reduces the total CPU cycle count. +** wx_sqlite3ParseObjectReset(), which reduces the total CPU cycle count. ** ** If a memory allocation error occurs, then the cleanup happens immediately. ** When either SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the @@ -130836,6 +137962,33 @@ SQLITE_PRIVATE void *wx_sqlite3ParserAddCleanup( return pPtr; } +/* +** Turn bulk memory into a valid Parse object and link that Parse object +** into database connection db. +** +** Call wx_sqlite3ParseObjectReset() to undo this operation. +** +** Caution: Do not confuse this routine with wx_sqlite3ParseObjectInit() which +** is generated by Lemon. +*/ +SQLITE_PRIVATE void wx_sqlite3ParseObjectInit(Parse *pParse, wx_sqlite3 *db){ + memset(PARSE_HDR(pParse), 0, PARSE_HDR_SZ); + memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ); + assert( db->pParse!=pParse ); + pParse->pOuterParse = db->pParse; + db->pParse = pParse; + pParse->db = db; + if( db->mallocFailed ) wx_sqlite3ErrorMsg(pParse, "out of memory"); +} + +/* +** Maximum number of times that we will try again to prepare a statement +** that returns SQLITE_ERROR_RETRY. +*/ +#ifndef SQLITE_MAX_PREPARE_RETRY +# define SQLITE_MAX_PREPARE_RETRY 25 +#endif + /* ** Compile the UTF-8 encoded SQL statement zSql into a statement handle. */ @@ -130848,16 +138001,19 @@ static int wx_sqlite3Prepare( wx_sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ - char *zErrMsg = 0; /* Error message */ int rc = SQLITE_OK; /* Result code */ int i; /* Loop counter */ Parse sParse; /* Parsing context */ - memset(&sParse, 0, PARSE_HDR_SZ); + /* wx_sqlite3ParseObjectInit(&sParse, db); // inlined for performance */ + memset(PARSE_HDR(&sParse), 0, PARSE_HDR_SZ); memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ); + sParse.pOuterParse = db->pParse; + db->pParse = &sParse; + sParse.db = db; sParse.pReprepare = pReprepare; assert( ppStmt && *ppStmt==0 ); - /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */ + if( db->mallocFailed ) wx_sqlite3ErrorMsg(&sParse, "out of memory"); assert( wx_sqlite3_mutex_held(db->mutex) ); /* For a long-term use prepared statement avoid the use of @@ -130867,7 +138023,7 @@ static int wx_sqlite3Prepare( sParse.disableLookaside++; DisableLookaside; } - sParse.disableVtab = (prepFlags & SQLITE_PREPARE_NO_VTAB)!=0; + sParse.prepFlags = prepFlags & 0xff; /* Check to verify that it is possible to get a read lock on all ** database schemas. The inability to get a read lock indicates that @@ -130908,9 +138064,10 @@ static int wx_sqlite3Prepare( } } - wx_sqlite3VtabUnlockList(db); +#ifndef SQLITE_OMIT_VIRTUALTABLE + if( db->pDisconnect ) wx_sqlite3VtabUnlockList(db); +#endif - sParse.db = db; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ char *zSqlCopy; int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; @@ -130923,14 +138080,14 @@ static int wx_sqlite3Prepare( } zSqlCopy = wx_sqlite3DbStrNDup(db, zSql, nBytes); if( zSqlCopy ){ - wx_sqlite3RunParser(&sParse, zSqlCopy, &zErrMsg); + wx_sqlite3RunParser(&sParse, zSqlCopy); sParse.zTail = &zSql[sParse.zTail-zSqlCopy]; wx_sqlite3DbFree(db, zSqlCopy); }else{ sParse.zTail = &zSql[nBytes]; } }else{ - wx_sqlite3RunParser(&sParse, zSql, &zErrMsg); + wx_sqlite3RunParser(&sParse, zSql); } assert( 0==sParse.nQueryLoop ); @@ -130943,9 +138100,10 @@ static int wx_sqlite3Prepare( } if( db->mallocFailed ){ sParse.rc = SQLITE_NOMEM_BKPT; + sParse.checkSchema = 0; } if( sParse.rc!=SQLITE_OK && sParse.rc!=SQLITE_DONE ){ - if( sParse.checkSchema ){ + if( sParse.checkSchema && db->init.busy==0 ){ schemaIsValid(&sParse); } if( sParse.pVdbe ){ @@ -130953,14 +138111,14 @@ static int wx_sqlite3Prepare( } assert( 0==(*ppStmt) ); rc = sParse.rc; - if( zErrMsg ){ - wx_sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg); - wx_sqlite3DbFree(db, zErrMsg); + if( sParse.zErrMsg ){ + wx_sqlite3ErrorWithMsg(db, rc, "%s", sParse.zErrMsg); + wx_sqlite3DbFree(db, sParse.zErrMsg); }else{ wx_sqlite3Error(db, rc); } }else{ - assert( zErrMsg==0 ); + assert( sParse.zErrMsg==0 ); *ppStmt = (wx_sqlite3_stmt*)sParse.pVdbe; rc = SQLITE_OK; wx_sqlite3ErrorClear(db); @@ -130976,7 +138134,7 @@ static int wx_sqlite3Prepare( end_prepare: - wx_sqlite3ParserReset(&sParse); + wx_sqlite3ParseObjectReset(&sParse); return rc; } static int wx_sqlite3LockAndPrepare( @@ -131006,7 +138164,8 @@ static int wx_sqlite3LockAndPrepare( ** reset is considered a permanent error. */ rc = wx_sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail); assert( rc==SQLITE_OK || *ppStmt==0 ); - }while( rc==SQLITE_ERROR_RETRY + if( rc==SQLITE_OK || db->mallocFailed ) break; + }while( (rc==SQLITE_ERROR_RETRY && (cnt++)pPrior; wx_sqlite3ExprListDelete(db, p->pEList); @@ -131321,7 +138485,7 @@ static void clearSelect(wx_sqlite3 *db, Select *p, int bFree){ wx_sqlite3WindowUnlinkFromSelect(p->pWin); } #endif - if( bFree ) wx_sqlite3DbFreeNN(db, p); + if( bFree ) wx_sqlite3DbNNFreeNN(db, p); p = pPrior; bFree = 1; } @@ -131430,6 +138594,52 @@ static Select *findRightmost(Select *p){ ** ** If an illegal or unsupported join type is seen, then still return ** a join type, but put an error in the pParse structure. +** +** These are the valid join types: +** +** +** pA pB pC Return Value +** ------- ----- ----- ------------ +** CROSS - - JT_CROSS +** INNER - - JT_INNER +** LEFT - - JT_LEFT|JT_OUTER +** LEFT OUTER - JT_LEFT|JT_OUTER +** RIGHT - - JT_RIGHT|JT_OUTER +** RIGHT OUTER - JT_RIGHT|JT_OUTER +** FULL - - JT_LEFT|JT_RIGHT|JT_OUTER +** FULL OUTER - JT_LEFT|JT_RIGHT|JT_OUTER +** NATURAL INNER - JT_NATURAL|JT_INNER +** NATURAL LEFT - JT_NATURAL|JT_LEFT|JT_OUTER +** NATURAL LEFT OUTER JT_NATURAL|JT_LEFT|JT_OUTER +** NATURAL RIGHT - JT_NATURAL|JT_RIGHT|JT_OUTER +** NATURAL RIGHT OUTER JT_NATURAL|JT_RIGHT|JT_OUTER +** NATURAL FULL - JT_NATURAL|JT_LEFT|JT_RIGHT +** NATURAL FULL OUTER JT_NATRUAL|JT_LEFT|JT_RIGHT +** +** To preserve historical compatibly, SQLite also accepts a variety +** of other non-standard and in many cases non-sensical join types. +** This routine makes as much sense at it can from the nonsense join +** type and returns a result. Examples of accepted nonsense join types +** include but are not limited to: +** +** INNER CROSS JOIN -> same as JOIN +** NATURAL CROSS JOIN -> same as NATURAL JOIN +** OUTER LEFT JOIN -> same as LEFT JOIN +** LEFT NATURAL JOIN -> same as NATURAL LEFT JOIN +** LEFT RIGHT JOIN -> same as FULL JOIN +** RIGHT OUTER FULL JOIN -> same as FULL JOIN +** CROSS CROSS CROSS JOIN -> same as JOIN +** +** The only restrictions on the join type name are: +** +** * "INNER" cannot appear together with "OUTER", "LEFT", "RIGHT", +** or "FULL". +** +** * "CROSS" cannot appear together with "OUTER", "LEFT", "RIGHT, +** or "FULL". +** +** * If "OUTER" is present then there must also be one of +** "LEFT", "RIGHT", or "FULL" */ SQLITE_PRIVATE int wx_sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){ int jointype = 0; @@ -131442,13 +138652,13 @@ SQLITE_PRIVATE int wx_sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token u8 nChar; /* Length of the keyword in characters */ u8 code; /* Join type mask */ } aKeyword[] = { - /* natural */ { 0, 7, JT_NATURAL }, - /* left */ { 6, 4, JT_LEFT|JT_OUTER }, - /* outer */ { 10, 5, JT_OUTER }, - /* right */ { 14, 5, JT_RIGHT|JT_OUTER }, - /* full */ { 19, 4, JT_LEFT|JT_RIGHT|JT_OUTER }, - /* inner */ { 23, 5, JT_INNER }, - /* cross */ { 28, 5, JT_INNER|JT_CROSS }, + /* (0) natural */ { 0, 7, JT_NATURAL }, + /* (1) left */ { 6, 4, JT_LEFT|JT_OUTER }, + /* (2) outer */ { 10, 5, JT_OUTER }, + /* (3) right */ { 14, 5, JT_RIGHT|JT_OUTER }, + /* (4) full */ { 19, 4, JT_LEFT|JT_RIGHT|JT_OUTER }, + /* (5) inner */ { 23, 5, JT_INNER }, + /* (6) cross */ { 28, 5, JT_INNER|JT_CROSS }, }; int i, j; apAll[0] = pA; @@ -131471,18 +138681,15 @@ SQLITE_PRIVATE int wx_sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token } if( (jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) || - (jointype & JT_ERROR)!=0 + (jointype & JT_ERROR)!=0 || + (jointype & (JT_OUTER|JT_LEFT|JT_RIGHT))==JT_OUTER ){ - const char *zSp = " "; - assert( pB!=0 ); - if( pC==0 ){ zSp++; } - wx_sqlite3ErrorMsg(pParse, "unknown or unsupported join type: " - "%T %T%s%T", pA, pB, zSp, pC); - jointype = JT_INNER; - }else if( (jointype & JT_OUTER)!=0 - && (jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ){ - wx_sqlite3ErrorMsg(pParse, - "RIGHT and FULL OUTER JOINs are not currently supported"); + const char *zSp1 = " "; + const char *zSp2 = " "; + if( pB==0 ){ zSp1++; } + if( pC==0 ){ zSp2++; } + wx_sqlite3ErrorMsg(pParse, "unknown join type: " + "%T%s%T%s%T", pA, zSp1, pB, zSp2, pC); jointype = JT_INNER; } return jointype; @@ -131497,14 +138704,31 @@ SQLITE_PRIVATE int wx_sqlite3ColumnIndex(Table *pTab, const char *zCol){ u8 h = wx_sqlite3StrIHash(zCol); Column *pCol; for(pCol=pTab->aCol, i=0; inCol; pCol++, i++){ - if( pCol->hName==h && wx_sqlite3StrICmp(pCol->zName, zCol)==0 ) return i; + if( pCol->hName==h && wx_sqlite3StrICmp(pCol->zCnName, zCol)==0 ) return i; } return -1; } /* -** Search the first N tables in pSrc, from left to right, looking for a -** table that has a column named zCol. +** Mark a subquery result column as having been used. +*/ +SQLITE_PRIVATE void wx_sqlite3SrcItemColumnUsed(SrcItem *pItem, int iCol){ + assert( pItem!=0 ); + assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); + if( pItem->fg.isNestedFrom ){ + ExprList *pResults; + assert( pItem->pSelect!=0 ); + pResults = pItem->pSelect->pEList; + assert( pResults!=0 ); + assert( iCol>=0 && iColnExpr ); + pResults->a[iCol].fg.bUsed = 1; + } +} + +/* +** Search the tables iStart..iEnd (inclusive) in pSrc, looking for a +** table that has a column named zCol. The search is left-to-right. +** The first match found is returned. ** ** When found, set *piTab and *piCol to the table index and column index ** of the matching column and return TRUE. @@ -131513,22 +138737,27 @@ SQLITE_PRIVATE int wx_sqlite3ColumnIndex(Table *pTab, const char *zCol){ */ static int tableAndColumnIndex( SrcList *pSrc, /* Array of tables to search */ - int N, /* Number of tables in pSrc->a[] to search */ + int iStart, /* First member of pSrc->a[] to check */ + int iEnd, /* Last member of pSrc->a[] to check */ const char *zCol, /* Name of the column we are looking for */ int *piTab, /* Write index of pSrc->a[] here */ int *piCol, /* Write index of pSrc->a[*piTab].pTab->aCol[] here */ - int bIgnoreHidden /* True to ignore hidden columns */ + int bIgnoreHidden /* Ignore hidden columns */ ){ int i; /* For looping over tables in pSrc */ int iCol; /* Index of column matching zCol */ + assert( iEndnSrc ); + assert( iStart>=0 ); assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */ - for(i=0; ia[i].pTab, zCol); if( iCol>=0 && (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0) ){ if( piTab ){ + wx_sqlite3SrcItemColumnUsed(&pSrc->a[i], iCol); *piTab = i; *piCol = iCol; } @@ -131539,63 +138768,19 @@ static int tableAndColumnIndex( } /* -** This function is used to add terms implied by JOIN syntax to the -** WHERE clause expression of a SELECT statement. The new term, which -** is ANDed with the existing WHERE clause, is of the form: -** -** (tab1.col1 = tab2.col2) -** -** where tab1 is the iSrc'th table in SrcList pSrc and tab2 is the -** (iSrc+1)'th. Column col1 is column iColLeft of tab1, and col2 is -** column iColRight of tab2. -*/ -static void addWhereTerm( - Parse *pParse, /* Parsing context */ - SrcList *pSrc, /* List of tables in FROM clause */ - int iLeft, /* Index of first table to join in pSrc */ - int iColLeft, /* Index of column in first table */ - int iRight, /* Index of second table in pSrc */ - int iColRight, /* Index of column in second table */ - int isOuterJoin, /* True if this is an OUTER join */ - Expr **ppWhere /* IN/OUT: The WHERE clause to add to */ -){ - wx_sqlite3 *db = pParse->db; - Expr *pE1; - Expr *pE2; - Expr *pEq; - - assert( iLeftnSrc>iRight ); - assert( pSrc->a[iLeft].pTab ); - assert( pSrc->a[iRight].pTab ); - - pE1 = wx_sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft); - pE2 = wx_sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight); - - pEq = wx_sqlite3PExpr(pParse, TK_EQ, pE1, pE2); - if( pEq && isOuterJoin ){ - ExprSetProperty(pEq, EP_FromJoin); - assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) ); - ExprSetVVAProperty(pEq, EP_NoReduce); - pEq->iRightJoinTable = pE2->iTable; - } - *ppWhere = wx_sqlite3ExprAnd(pParse, *ppWhere, pEq); -} - -/* -** Set the EP_FromJoin property on all terms of the given expression. -** And set the Expr.iRightJoinTable to iTable for every term in the +** Set the EP_OuterON property on all terms of the given expression. +** And set the Expr.w.iJoin to iTable for every term in the ** expression. ** -** The EP_FromJoin property is used on terms of an expression to tell -** the LEFT OUTER JOIN processing logic that this term is part of the +** The EP_OuterON property is used on terms of an expression to tell +** the OUTER JOIN processing logic that this term is part of the ** join restriction specified in the ON or USING clause and not a part ** of the more general WHERE clause. These terms are moved over to the ** WHERE clause during join processing but we need to remember that they ** originated in the ON or USING clause. ** -** The Expr.iRightJoinTable tells the WHERE clause processing that the -** expression depends on table iRightJoinTable even if that table is not +** The Expr.w.iJoin tells the WHERE clause processing that the +** expression depends on table w.iJoin even if that table is not ** explicitly mentioned in the expression. That information is needed ** for cases like this: ** @@ -131608,64 +138793,86 @@ static void addWhereTerm( ** after the t1 loop and rows with t1.x!=5 will never appear in ** the output, which is incorrect. */ -SQLITE_PRIVATE void wx_sqlite3SetJoinExpr(Expr *p, int iTable){ +SQLITE_PRIVATE void wx_sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){ + assert( joinFlag==EP_OuterON || joinFlag==EP_InnerON ); while( p ){ - ExprSetProperty(p, EP_FromJoin); + ExprSetProperty(p, joinFlag); assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); ExprSetVVAProperty(p, EP_NoReduce); - p->iRightJoinTable = iTable; - if( p->op==TK_FUNCTION && p->x.pList ){ - int i; - for(i=0; ix.pList->nExpr; i++){ - wx_sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable); + p->w.iJoin = iTable; + if( p->op==TK_FUNCTION ){ + assert( ExprUseXList(p) ); + if( p->x.pList ){ + int i; + for(i=0; ix.pList->nExpr; i++){ + wx_sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag); + } } } - wx_sqlite3SetJoinExpr(p->pLeft, iTable); + wx_sqlite3SetJoinExpr(p->pLeft, iTable, joinFlag); p = p->pRight; } } -/* Undo the work of wx_sqlite3SetJoinExpr(). In the expression p, convert every -** term that is marked with EP_FromJoin and iRightJoinTable==iTable into -** an ordinary term that omits the EP_FromJoin mark. +/* Undo the work of wx_sqlite3SetJoinExpr(). This is used when a LEFT JOIN +** is simplified into an ordinary JOIN, and when an ON expression is +** "pushed down" into the WHERE clause of a subquery. +** +** Convert every term that is marked with EP_OuterON and w.iJoin==iTable into +** an ordinary term that omits the EP_OuterON mark. Or if iTable<0, then +** just clear every EP_OuterON and EP_InnerON mark from the expression tree. ** -** This happens when a LEFT JOIN is simplified into an ordinary JOIN. +** If nullable is true, that means that Expr p might evaluate to NULL even +** if it is a reference to a NOT NULL column. This can happen, for example, +** if the table that p references is on the left side of a RIGHT JOIN. +** If nullable is true, then take care to not remove the EP_CanBeNull bit. +** See forum thread https://sqlite.org/forum/forumpost/b40696f50145d21c */ -static void unsetJoinExpr(Expr *p, int iTable){ +static void unsetJoinExpr(Expr *p, int iTable, int nullable){ while( p ){ - if( ExprHasProperty(p, EP_FromJoin) - && (iTable<0 || p->iRightJoinTable==iTable) ){ - ExprClearProperty(p, EP_FromJoin); + if( iTable<0 || (ExprHasProperty(p, EP_OuterON) && p->w.iJoin==iTable) ){ + ExprClearProperty(p, EP_OuterON|EP_InnerON); + if( iTable>=0 ) ExprSetProperty(p, EP_InnerON); } - if( p->op==TK_COLUMN && p->iTable==iTable ){ + if( p->op==TK_COLUMN && p->iTable==iTable && !nullable ){ ExprClearProperty(p, EP_CanBeNull); } - if( p->op==TK_FUNCTION && p->x.pList ){ - int i; - for(i=0; ix.pList->nExpr; i++){ - unsetJoinExpr(p->x.pList->a[i].pExpr, iTable); + if( p->op==TK_FUNCTION ){ + assert( ExprUseXList(p) ); + if( p->x.pList ){ + int i; + for(i=0; ix.pList->nExpr; i++){ + unsetJoinExpr(p->x.pList->a[i].pExpr, iTable, nullable); + } } } - unsetJoinExpr(p->pLeft, iTable); + unsetJoinExpr(p->pLeft, iTable, nullable); p = p->pRight; } } /* ** This routine processes the join information for a SELECT statement. -** ON and USING clauses are converted into extra terms of the WHERE clause. -** NATURAL joins also create extra WHERE clause terms. +** +** * A NATURAL join is converted into a USING join. After that, we +** do not need to be concerned with NATURAL joins and we only have +** think about USING joins. +** +** * ON and USING clauses result in extra terms being added to the +** WHERE clause to enforce the specified constraints. The extra +** WHERE clause terms will be tagged with EP_OuterON or +** EP_InnerON so that we know that they originated in ON/USING. ** ** The terms of a FROM clause are contained in the Select.pSrc structure. ** The left most table is the first entry in Select.pSrc. The right-most ** table is the last entry. The join operator is held in the entry to -** the left. Thus entry 0 contains the join operator for the join between +** the right. Thus entry 1 contains the join operator for the join between ** entries 0 and 1. Any ON or USING clauses associated with the join are -** also attached to the left entry. +** also attached to the right entry. ** ** This routine returns the number of errors encountered. */ -static int sqliteProcessJoin(Parse *pParse, Select *p){ +static int wx_sqlite3ProcessJoin(Parse *pParse, Select *p){ SrcList *pSrc; /* All tables in the FROM clause */ int i, j; /* Loop counters */ SrcItem *pLeft; /* Left table being joined */ @@ -131676,49 +138883,41 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ pRight = &pLeft[1]; for(i=0; inSrc-1; i++, pRight++, pLeft++){ Table *pRightTab = pRight->pTab; - int isOuter; + u32 joinType; if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue; - isOuter = (pRight->fg.jointype & JT_OUTER)!=0; + joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_OuterON : EP_InnerON; - /* When the NATURAL keyword is present, add WHERE clause terms for - ** every column that the two tables have in common. + /* If this is a NATURAL join, synthesize an approprate USING clause + ** to specify which columns should be joined. */ if( pRight->fg.jointype & JT_NATURAL ){ - if( pRight->pOn || pRight->pUsing ){ + IdList *pUsing = 0; + if( pRight->fg.isUsing || pRight->u3.pOn ){ wx_sqlite3ErrorMsg(pParse, "a NATURAL join may not have " "an ON or USING clause", 0); return 1; } for(j=0; jnCol; j++){ char *zName; /* Name of column in the right table */ - int iLeft; /* Matching left table */ - int iLeftCol; /* Matching column in the left table */ if( IsHiddenColumn(&pRightTab->aCol[j]) ) continue; - zName = pRightTab->aCol[j].zName; - if( tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 1) ){ - addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, j, - isOuter, &p->pWhere); + zName = pRightTab->aCol[j].zCnName; + if( tableAndColumnIndex(pSrc, 0, i, zName, 0, 0, 1) ){ + pUsing = wx_sqlite3IdListAppend(pParse, pUsing, 0); + if( pUsing ){ + assert( pUsing->nId>0 ); + assert( pUsing->a[pUsing->nId-1].zName==0 ); + pUsing->a[pUsing->nId-1].zName = wx_sqlite3DbStrDup(pParse->db, zName); + } } } - } - - /* Disallow both ON and USING clauses in the same join - */ - if( pRight->pOn && pRight->pUsing ){ - wx_sqlite3ErrorMsg(pParse, "cannot have both ON and USING " - "clauses in the same join"); - return 1; - } - - /* Add the ON clause to the end of the WHERE clause, connected by - ** an AND operator. - */ - if( pRight->pOn ){ - if( isOuter ) wx_sqlite3SetJoinExpr(pRight->pOn, pRight->iCursor); - p->pWhere = wx_sqlite3ExprAnd(pParse, p->pWhere, pRight->pOn); - pRight->pOn = 0; + if( pUsing ){ + pRight->fg.isUsing = 1; + pRight->fg.isSynthUsing = 1; + pRight->u3.pUsing = pUsing; + } + if( pParse->nErr ) return 1; } /* Create extra terms on the WHERE clause for each column named @@ -131728,27 +138927,88 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ ** Report an error if any column mentioned in the USING clause is ** not contained in both tables to be joined. */ - if( pRight->pUsing ){ - IdList *pList = pRight->pUsing; + if( pRight->fg.isUsing ){ + IdList *pList = pRight->u3.pUsing; + wx_sqlite3 *db = pParse->db; + assert( pList!=0 ); for(j=0; jnId; j++){ char *zName; /* Name of the term in the USING clause */ int iLeft; /* Table on the left with matching column name */ int iLeftCol; /* Column number of matching column on the left */ int iRightCol; /* Column number of matching column on the right */ + Expr *pE1; /* Reference to the column on the LEFT of the join */ + Expr *pE2; /* Reference to the column on the RIGHT of the join */ + Expr *pEq; /* Equality constraint. pE1 == pE2 */ zName = pList->a[j].zName; iRightCol = wx_sqlite3ColumnIndex(pRightTab, zName); if( iRightCol<0 - || !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 0) + || tableAndColumnIndex(pSrc, 0, i, zName, &iLeft, &iLeftCol, + pRight->fg.isSynthUsing)==0 ){ wx_sqlite3ErrorMsg(pParse, "cannot join using column %s - column " "not present in both tables", zName); return 1; } - addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, iRightCol, - isOuter, &p->pWhere); + pE1 = wx_sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol); + wx_sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol); + if( (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ + /* This branch runs if the query contains one or more RIGHT or FULL + ** JOINs. If only a single table on the left side of this join + ** contains the zName column, then this branch is a no-op. + ** But if there are two or more tables on the left side + ** of the join, construct a coalesce() function that gathers all + ** such tables. Raise an error if more than one of those references + ** to zName is not also within a prior USING clause. + ** + ** We really ought to raise an error if there are two or more + ** non-USING references to zName on the left of an INNER or LEFT + ** JOIN. But older versions of SQLite do not do that, so we avoid + ** adding a new error so as to not break legacy applications. + */ + ExprList *pFuncArgs = 0; /* Arguments to the coalesce() */ + static const Token tkCoalesce = { "coalesce", 8 }; + while( tableAndColumnIndex(pSrc, iLeft+1, i, zName, &iLeft, &iLeftCol, + pRight->fg.isSynthUsing)!=0 ){ + if( pSrc->a[iLeft].fg.isUsing==0 + || wx_sqlite3IdListIndex(pSrc->a[iLeft].u3.pUsing, zName)<0 + ){ + wx_sqlite3ErrorMsg(pParse, "ambiguous reference to %s in USING()", + zName); + break; + } + pFuncArgs = wx_sqlite3ExprListAppend(pParse, pFuncArgs, pE1); + pE1 = wx_sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol); + wx_sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol); + } + if( pFuncArgs ){ + pFuncArgs = wx_sqlite3ExprListAppend(pParse, pFuncArgs, pE1); + pE1 = wx_sqlite3ExprFunction(pParse, pFuncArgs, &tkCoalesce, 0); + } + } + pE2 = wx_sqlite3CreateColumnExpr(db, pSrc, i+1, iRightCol); + wx_sqlite3SrcItemColumnUsed(pRight, iRightCol); + pEq = wx_sqlite3PExpr(pParse, TK_EQ, pE1, pE2); + assert( pE2!=0 || pEq==0 ); + if( pEq ){ + ExprSetProperty(pEq, joinType); + assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) ); + ExprSetVVAProperty(pEq, EP_NoReduce); + pEq->w.iJoin = pE2->iTable; + } + p->pWhere = wx_sqlite3ExprAnd(pParse, p->pWhere, pEq); } } + + /* Add the ON clause to the end of the WHERE clause, connected by + ** an AND operator. + */ + else if( pRight->u3.pOn ){ + wx_sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType); + p->pWhere = wx_sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn); + pRight->u3.pOn = 0; + pRight->fg.isOn = 1; + } } return 0; } @@ -131850,6 +139110,10 @@ static void pushOntoSorter( */ assert( nData==1 || regData==regOrigData || regOrigData==0 ); +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + pSort->addrPush = wx_sqlite3VdbeCurrentAddr(v); +#endif + if( nPrefixReg ){ assert( nPrefixReg==nExpr+bSeq ); regBase = regData - nPrefixReg; @@ -131950,6 +139214,9 @@ static void pushOntoSorter( wx_sqlite3VdbeChangeP2(v, iSkip, pSort->labelOBLopt ? pSort->labelOBLopt : wx_sqlite3VdbeCurrentAddr(v)); } +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + pSort->addrPushEnd = wx_sqlite3VdbeCurrentAddr(v)-1; +#endif } /* @@ -131967,31 +139234,157 @@ static void codeOffset( } /* -** Add code that will check to make sure the N registers starting at iMem -** form a distinct entry. iTab is a sorting index that holds previously -** seen combinations of the N values. A new entry is made in iTab -** if the current N values are new. +** Add code that will check to make sure the array of registers starting at +** iMem form a distinct entry. This is used by both "SELECT DISTINCT ..." and +** distinct aggregates ("SELECT count(DISTINCT ) ..."). Three strategies +** are available. Which is used depends on the value of parameter eTnctType, +** as follows: ** -** A jump to addrRepeat is made and the N+1 values are popped from the -** stack if the top N elements are not distinct. -*/ -static void codeDistinct( +** WHERE_DISTINCT_UNORDERED/WHERE_DISTINCT_NOOP: +** Build an ephemeral table that contains all entries seen before and +** skip entries which have been seen before. +** +** Parameter iTab is the cursor number of an ephemeral table that must +** be opened before the VM code generated by this routine is executed. +** The ephemeral cursor table is queried for a record identical to the +** record formed by the current array of registers. If one is found, +** jump to VM address addrRepeat. Otherwise, insert a new record into +** the ephemeral cursor and proceed. +** +** The returned value in this case is a copy of parameter iTab. +** +** WHERE_DISTINCT_ORDERED: +** In this case rows are being delivered sorted order. The ephermal +** table is not required. Instead, the current set of values +** is compared against previous row. If they match, the new row +** is not distinct and control jumps to VM address addrRepeat. Otherwise, +** the VM program proceeds with processing the new row. +** +** The returned value in this case is the register number of the first +** in an array of registers used to store the previous result row so that +** it can be compared to the next. The caller must ensure that this +** register is initialized to NULL. (The fixDistinctOpenEph() routine +** will take care of this initialization.) +** +** WHERE_DISTINCT_UNIQUE: +** In this case it has already been determined that the rows are distinct. +** No special action is required. The return value is zero. +** +** Parameter pEList is the list of expressions used to generated the +** contents of each row. It is used by this routine to determine (a) +** how many elements there are in the array of registers and (b) the +** collation sequences that should be used for the comparisons if +** eTnctType is WHERE_DISTINCT_ORDERED. +*/ +static int codeDistinct( Parse *pParse, /* Parsing and code generating context */ + int eTnctType, /* WHERE_DISTINCT_* value */ int iTab, /* A sorting index used to test for distinctness */ int addrRepeat, /* Jump to here if not distinct */ - int N, /* Number of elements */ - int iMem /* First element */ + ExprList *pEList, /* Expression for each element */ + int regElem /* First element */ ){ - Vdbe *v; - int r1; + int iRet = 0; + int nResultCol = pEList->nExpr; + Vdbe *v = pParse->pVdbe; - v = pParse->pVdbe; - r1 = wx_sqlite3GetTempReg(pParse); - wx_sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); VdbeCoverage(v); - wx_sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); - wx_sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r1, iMem, N); - wx_sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); - wx_sqlite3ReleaseTempReg(pParse, r1); + switch( eTnctType ){ + case WHERE_DISTINCT_ORDERED: { + int i; + int iJump; /* Jump destination */ + int regPrev; /* Previous row content */ + + /* Allocate space for the previous row */ + iRet = regPrev = pParse->nMem+1; + pParse->nMem += nResultCol; + + iJump = wx_sqlite3VdbeCurrentAddr(v) + nResultCol; + for(i=0; ia[i].pExpr); + if( idb->mallocFailed ); + wx_sqlite3VdbeAddOp3(v, OP_Copy, regElem, regPrev, nResultCol-1); + break; + } + + case WHERE_DISTINCT_UNIQUE: { + /* nothing to do */ + break; + } + + default: { + int r1 = wx_sqlite3GetTempReg(pParse); + wx_sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, regElem, nResultCol); + VdbeCoverage(v); + wx_sqlite3VdbeAddOp3(v, OP_MakeRecord, regElem, nResultCol, r1); + wx_sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r1, regElem, nResultCol); + wx_sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); + wx_sqlite3ReleaseTempReg(pParse, r1); + iRet = iTab; + break; + } + } + + return iRet; +} + +/* +** This routine runs after codeDistinct(). It makes necessary +** adjustments to the OP_OpenEphemeral opcode that the codeDistinct() +** routine made use of. This processing must be done separately since +** sometimes codeDistinct is called before the OP_OpenEphemeral is actually +** laid down. +** +** WHERE_DISTINCT_NOOP: +** WHERE_DISTINCT_UNORDERED: +** +** No adjustments necessary. This function is a no-op. +** +** WHERE_DISTINCT_UNIQUE: +** +** The ephemeral table is not needed. So change the +** OP_OpenEphemeral opcode into an OP_Noop. +** +** WHERE_DISTINCT_ORDERED: +** +** The ephemeral table is not needed. But we do need register +** iVal to be initialized to NULL. So change the OP_OpenEphemeral +** into an OP_Null on the iVal register. +*/ +static void fixDistinctOpenEph( + Parse *pParse, /* Parsing and code generating context */ + int eTnctType, /* WHERE_DISTINCT_* value */ + int iVal, /* Value returned by codeDistinct() */ + int iOpenEphAddr /* Address of OP_OpenEphemeral instruction for iTab */ +){ + if( pParse->nErr==0 + && (eTnctType==WHERE_DISTINCT_UNIQUE || eTnctType==WHERE_DISTINCT_ORDERED) + ){ + Vdbe *v = pParse->pVdbe; + wx_sqlite3VdbeChangeToNoop(v, iOpenEphAddr); + if( wx_sqlite3VdbeGetOp(v, iOpenEphAddr+1)->opcode==OP_Explain ){ + wx_sqlite3VdbeChangeToNoop(v, iOpenEphAddr+1); + } + if( eTnctType==WHERE_DISTINCT_ORDERED ){ + /* Change the OP_OpenEphemeral to an OP_Null that sets the MEM_Cleared + ** bit on the first register of the previous value. This will cause the + ** OP_Ne added in codeDistinct() to always fail on the first iteration of + ** the loop even if the first row is all NULLs. */ + VdbeOp *pOp = wx_sqlite3VdbeGetOp(v, iOpenEphAddr); + pOp->opcode = OP_Null; + pOp->p1 = 1; + pOp->p2 = iVal; + } + } } #ifdef SQLITE_ENABLE_SORTER_REFERENCES @@ -132011,7 +139404,7 @@ static void codeDistinct( ** retrieved directly from table t1. If the values are very large, this ** can be more efficient than storing them directly in the sorter records. ** -** The ExprList_item.bSorterRef flag is set for each expression in pEList +** The ExprList_item.fg.bSorterRef flag is set for each expression in pEList ** for which the sorter-reference optimization should be enabled. ** Additionally, the pSort->aDefer[] array is populated with entries ** for all cursors required to evaluate all selected expressions. Finally. @@ -132032,9 +139425,13 @@ static void selectExprDefer( struct ExprList_item *pItem = &pEList->a[i]; if( pItem->u.x.iOrderByCol==0 ){ Expr *pExpr = pItem->pExpr; - Table *pTab = pExpr->y.pTab; - if( pExpr->op==TK_COLUMN && pExpr->iColumn>=0 && pTab && !IsVirtual(pTab) - && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF) + Table *pTab; + if( pExpr->op==TK_COLUMN + && pExpr->iColumn>=0 + && ALWAYS( ExprUseYTab(pExpr) ) + && (pTab = pExpr->y.pTab)!=0 + && IsOrdinaryTable(pTab) + && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF)!=0 ){ int j; for(j=0; jiTable = pExpr->iTable; + assert( ExprUseYTab(pNew) ); pNew->y.pTab = pExpr->y.pTab; pNew->iColumn = pPk ? pPk->aiColumn[k] : -1; pExtra = wx_sqlite3ExprListAppend(pParse, pExtra, pNew); @@ -132066,7 +139464,7 @@ static void selectExprDefer( nDefer++; } } - pItem->bSorterRef = 1; + pItem->fg.bSorterRef = 1; } } } @@ -132197,7 +139595,7 @@ static void selectInnerLoop( for(i=0; inExpr; i++){ if( pEList->a[i].u.x.iOrderByCol>0 #ifdef SQLITE_ENABLE_SORTER_REFERENCES - || pEList->a[i].bSorterRef + || pEList->a[i].fg.bSorterRef #endif ){ nResultCol--; @@ -132239,59 +139637,11 @@ static void selectInnerLoop( ** part of the result. */ if( hasDistinct ){ - switch( pDistinct->eTnctType ){ - case WHERE_DISTINCT_ORDERED: { - VdbeOp *pOp; /* No longer required OpenEphemeral instr. */ - int iJump; /* Jump destination */ - int regPrev; /* Previous row content */ - - /* Allocate space for the previous row */ - regPrev = pParse->nMem+1; - pParse->nMem += nResultCol; - - /* Change the OP_OpenEphemeral coded earlier to an OP_Null - ** sets the MEM_Cleared bit on the first register of the - ** previous value. This will cause the OP_Ne below to always - ** fail on the first iteration of the loop even if the first - ** row is all NULLs. - */ - wx_sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct); - pOp = wx_sqlite3VdbeGetOp(v, pDistinct->addrTnct); - pOp->opcode = OP_Null; - pOp->p1 = 1; - pOp->p2 = regPrev; - pOp = 0; /* Ensure pOp is not used after wx_sqlite3VdbeAddOp() */ - - iJump = wx_sqlite3VdbeCurrentAddr(v) + nResultCol; - for(i=0; ipEList->a[i].pExpr); - if( idb->mallocFailed ); - wx_sqlite3VdbeAddOp3(v, OP_Copy, regResult, regPrev, nResultCol-1); - break; - } - - case WHERE_DISTINCT_UNIQUE: { - wx_sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct); - break; - } - - default: { - assert( pDistinct->eTnctType==WHERE_DISTINCT_UNORDERED ); - codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol, - regResult); - break; - } - } + int eType = pDistinct->eTnctType; + int iTab = pDistinct->tabTnct; + assert( nResultCol==p->pEList->nExpr ); + iTab = codeDistinct(pParse, eType, iTab, iContinue, p->pEList, regResult); + fixDistinctOpenEph(pParse, eType, iTab, pDistinct->addrTnct); if( pSort==0 ){ codeOffset(v, p->iOffset, iContinue); } @@ -132538,7 +139888,7 @@ SQLITE_PRIVATE KeyInfo *wx_sqlite3KeyInfoAlloc(wx_sqlite3 *db, int N, int X){ p->nRef = 1; memset(&p[1], 0, nExtra); }else{ - wx_sqlite3OomFault(db); + return (KeyInfo*)wx_sqlite3OomFault(db); } return p; } @@ -132548,9 +139898,10 @@ SQLITE_PRIVATE KeyInfo *wx_sqlite3KeyInfoAlloc(wx_sqlite3 *db, int N, int X){ */ SQLITE_PRIVATE void wx_sqlite3KeyInfoUnref(KeyInfo *p){ if( p ){ + assert( p->db!=0 ); assert( p->nRef>0 ); p->nRef--; - if( p->nRef==0 ) wx_sqlite3DbFreeNN(p->db, p); + if( p->nRef==0 ) wx_sqlite3DbNNFreeNN(p->db, p); } } @@ -132607,7 +139958,7 @@ SQLITE_PRIVATE KeyInfo *wx_sqlite3KeyInfoFromExprList( assert( wx_sqlite3KeyInfoIsWriteable(pInfo) ); for(i=iStart, pItem=pList->a+iStart; iaColl[i-iStart] = wx_sqlite3ExprNNCollSeq(pParse, pItem->pExpr); - pInfo->aSortFlags[i-iStart] = pItem->sortFlags; + pInfo->aSortFlags[i-iStart] = pItem->fg.sortFlags; } } return pInfo; @@ -132689,6 +140040,16 @@ static void generateSortTail( int bSeq; /* True if sorter record includes seq. no. */ int nRefKey = 0; struct ExprList_item *aOutEx = p->pEList->a; +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + int addrExplain; /* Address of OP_Explain instruction */ +#endif + + ExplainQueryPlan2(addrExplain, (pParse, 0, + "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat>0?"RIGHT PART OF ":"") + ); + wx_sqlite3VdbeScanStatusRange(v, addrExplain,pSort->addrPush,pSort->addrPushEnd); + wx_sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, pSort->addrPush); + assert( addrBreak<0 ); if( pSort->labelBkOut ){ @@ -132709,6 +140070,9 @@ static void generateSortTail( iTab = pSort->iECursor; if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){ + if( eDest==SRT_Mem && p->iOffset ){ + wx_sqlite3VdbeAddOp2(v, OP_Null, 0, pDest->iSdst); + } regRowid = 0; regRow = pDest->iSdst; }else{ @@ -132732,7 +140096,7 @@ static void generateSortTail( if( addrOnce ) wx_sqlite3VdbeJumpHere(v, addrOnce); addr = 1 + wx_sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); VdbeCoverage(v); - codeOffset(v, p->iOffset, addrContinue); + assert( p->iLimit==0 && p->iOffset==0 ); wx_sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab); bSeq = 0; }else{ @@ -132740,10 +140104,13 @@ static void generateSortTail( codeOffset(v, p->iOffset, addrContinue); iSortTab = iTab; bSeq = 1; + if( p->iOffset>0 ){ + wx_sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1); + } } for(i=0, iCol=nKey+bSeq-1; i=0; i--){ #ifdef SQLITE_ENABLE_SORTER_REFERENCES - if( aOutEx[i].bSorterRef ){ + if( aOutEx[i].fg.bSorterRef ){ wx_sqlite3ExprCode(pParse, aOutEx[i].pExpr, regRow+i); }else #endif @@ -132795,6 +140162,7 @@ static void generateSortTail( VdbeComment((v, "%s", aOutEx[i].zEName)); } } + wx_sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); switch( eDest ){ case SRT_Table: case SRT_EphemTab: { @@ -132856,6 +140224,7 @@ static void generateSortTail( }else{ wx_sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); VdbeCoverage(v); } + wx_sqlite3VdbeScanStatusRange(v, addrExplain, wx_sqlite3VdbeCurrentAddr(v)-1, -1); if( pSort->regReturn ) wx_sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn); wx_sqlite3VdbeResolveLabel(v, addrBreak); } @@ -132864,9 +140233,6 @@ static void generateSortTail( ** Return a pointer to a string containing the 'declaration type' of the ** expression pExpr. The string may be treated as static by the caller. ** -** Also try to estimate the size of the returned value and return that -** result in *pEstWidth. -** ** The declaration type is the exact datatype definition extracted from the ** original CREATE TABLE statement if the expression is a column. The ** declaration type for a ROWID field is INTEGER. Exactly when an expression @@ -132951,13 +140317,19 @@ static const char *columnTypeImpl( break; } - assert( pTab && pExpr->y.pTab==pTab ); + assert( pTab && ExprUseYTab(pExpr) && pExpr->y.pTab==pTab ); if( pS ){ /* The "table" is actually a sub-select or a view in the FROM clause ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ - if( iCol>=0 && iColpEList->nExpr ){ + if( iColpEList->nExpr +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + && iCol>=0 +#else + && ALWAYS(iCol>=0) +#endif + ){ /* If iCol is less than zero, then the expression requests the ** rowid of the sub-select or view. This expression is legal (see ** test case misc2.2.2) - it always evaluates to NULL. @@ -132979,7 +140351,7 @@ static const char *columnTypeImpl( zType = "INTEGER"; zOrigCol = "rowid"; }else{ - zOrigCol = pTab->aCol[iCol].zName; + zOrigCol = pTab->aCol[iCol].zCnName; zType = wx_sqlite3ColumnType(&pTab->aCol[iCol],0); } zOrigTab = pTab->zName; @@ -133005,9 +140377,11 @@ static const char *columnTypeImpl( ** statement. */ NameContext sNC; - Select *pS = pExpr->x.pSelect; - Expr *p = pS->pEList->a[0].pExpr; - assert( ExprHasProperty(pExpr, EP_xIsSelect) ); + Select *pS; + Expr *p; + assert( ExprUseXSelect(pExpr) ); + pS = pExpr->x.pSelect; + p = pS->pEList->a[0].pExpr; sNC.pSrcList = pS->pSrc; sNC.pNext = pNC; sNC.pParse = pNC->pParse; @@ -133099,7 +140473,7 @@ static void generateColumnTypes( ** then the result column name with the table name ** prefix, ex: TABLE.COLUMN. Otherwise use zSpan. */ -static void generateColumnNames( +SQLITE_PRIVATE void wx_sqlite3GenerateColumnNames( Parse *pParse, /* Parser context */ Select *pSelect /* Generate column names for this SELECT statement */ ){ @@ -133122,7 +140496,7 @@ static void generateColumnNames( if( pParse->colNamesSet ) return; /* Column names are determined by the left-most term of a compound select */ while( pSelect->pPrior ) pSelect = pSelect->pPrior; - SELECTTRACE(1,pParse,pSelect,("generating column names\n")); + TREETRACE(0x80,pParse,pSelect,("generating column names\n")); pTabList = pSelect->pSrc; pEList = pSelect->pEList; assert( v!=0 ); @@ -133136,8 +140510,9 @@ static void generateColumnNames( assert( p!=0 ); assert( p->op!=TK_AGG_COLUMN ); /* Agg processing has not run yet */ - assert( p->op!=TK_COLUMN || p->y.pTab!=0 ); /* Covering idx not yet coded */ - if( pEList->a[i].zEName && pEList->a[i].eEName==ENAME_NAME ){ + assert( p->op!=TK_COLUMN + || (ExprUseYTab(p) && p->y.pTab!=0) ); /* Covering idx not yet coded */ + if( pEList->a[i].zEName && pEList->a[i].fg.eEName==ENAME_NAME ){ /* An AS clause always takes first priority */ char *zName = pEList->a[i].zEName; wx_sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT); @@ -133151,7 +140526,7 @@ static void generateColumnNames( if( iCol<0 ){ zCol = "rowid"; }else{ - zCol = pTab->aCol[iCol].zName; + zCol = pTab->aCol[iCol].zCnName; } if( fullName ){ char *zName = 0; @@ -133189,7 +140564,7 @@ static void generateColumnNames( ** and will break if those assumptions changes. Hence, use extreme caution ** when modifying this routine to avoid breaking legacy. ** -** See Also: generateColumnNames() +** See Also: wx_sqlite3GenerateColumnNames() */ SQLITE_PRIVATE int wx_sqlite3ColumnsFromExprList( Parse *pParse, /* Parsing context */ @@ -133221,28 +140596,34 @@ SQLITE_PRIVATE int wx_sqlite3ColumnsFromExprList( *pnCol = nCol; *paCol = aCol; - for(i=0, pCol=aCol; imallocFailed; i++, pCol++){ + for(i=0, pCol=aCol; inErr; i++, pCol++){ + struct ExprList_item *pX = &pEList->a[i]; + struct ExprList_item *pCollide; /* Get an appropriate name for the column */ - if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){ + if( (zName = pX->zEName)!=0 && pX->fg.eEName==ENAME_NAME ){ /* If the column contains an "AS " phrase, use as the name */ }else{ - Expr *pColExpr = wx_sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr); + Expr *pColExpr = wx_sqlite3ExprSkipCollateAndLikely(pX->pExpr); while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){ pColExpr = pColExpr->pRight; assert( pColExpr!=0 ); } - if( pColExpr->op==TK_COLUMN && (pTab = pColExpr->y.pTab)!=0 ){ + if( pColExpr->op==TK_COLUMN + && ALWAYS( ExprUseYTab(pColExpr) ) + && ALWAYS( pColExpr->y.pTab!=0 ) + ){ /* For columns use the column name name */ int iCol = pColExpr->iColumn; + pTab = pColExpr->y.pTab; if( iCol<0 ) iCol = pTab->iPKey; - zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; + zName = iCol>=0 ? pTab->aCol[iCol].zCnName : "rowid"; }else if( pColExpr->op==TK_ID ){ assert( !ExprHasProperty(pColExpr, EP_IntValue) ); zName = pColExpr->u.zToken; }else{ /* Use the original text of the column expression as its name */ - zName = pEList->a[i].zEName; + assert( zName==pX->zEName ); /* pointer comparison intended */ } } if( zName && !wx_sqlite3IsTrueOrFalse(zName) ){ @@ -133255,88 +140636,136 @@ SQLITE_PRIVATE int wx_sqlite3ColumnsFromExprList( ** append an integer to the name so that it becomes unique. */ cnt = 0; - while( zName && wx_sqlite3HashFind(&ht, zName)!=0 ){ + while( zName && (pCollide = wx_sqlite3HashFind(&ht, zName))!=0 ){ + if( pCollide->fg.bUsingTerm ){ + pCol->colFlags |= COLFLAG_NOEXPAND; + } nName = wx_sqlite3Strlen30(zName); if( nName>0 ){ for(j=nName-1; j>0 && wx_sqlite3Isdigit(zName[j]); j--){} if( zName[j]==':' ) nName = j; } zName = wx_sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt); - if( cnt>3 ) wx_sqlite3_randomness(sizeof(cnt), &cnt); + wx_sqlite3ProgressCheck(pParse); + if( cnt>3 ){ + wx_sqlite3_randomness(sizeof(cnt), &cnt); + } } - pCol->zName = zName; + pCol->zCnName = zName; pCol->hName = wx_sqlite3StrIHash(zName); + if( pX->fg.bNoExpand ){ + pCol->colFlags |= COLFLAG_NOEXPAND; + } wx_sqlite3ColumnPropertiesFromName(0, pCol); - if( zName && wx_sqlite3HashInsert(&ht, zName, pCol)==pCol ){ + if( zName && wx_sqlite3HashInsert(&ht, zName, pX)==pX ){ wx_sqlite3OomFault(db); } } wx_sqlite3HashClear(&ht); - if( db->mallocFailed ){ + if( pParse->nErr ){ for(j=0; jrc; } return SQLITE_OK; } /* -** Add type and collation information to a column list based on -** a SELECT statement. -** -** The column list presumably came from selectColumnNamesFromExprList(). -** The column list has only names, not types or collations. This -** routine goes through and adds the types and collations. +** pTab is a transient Table object that represents a subquery of some +** kind (maybe a parenthesized subquery in the FROM clause of a larger +** query, or a VIEW, or a CTE). This routine computes type information +** for that Table object based on the Select object that implements the +** subquery. For the purposes of this routine, "type infomation" means: ** -** This routine requires that all identifiers in the SELECT -** statement be resolved. +** * The datatype name, as it might appear in a CREATE TABLE statement +** * Which collating sequence to use for the column +** * The affinity of the column */ -SQLITE_PRIVATE void wx_sqlite3SelectAddColumnTypeAndCollation( - Parse *pParse, /* Parsing contexts */ - Table *pTab, /* Add column type information to this table */ - Select *pSelect, /* SELECT used to determine types and collations */ - char aff /* Default affinity for columns */ +SQLITE_PRIVATE void wx_sqlite3SubqueryColumnTypes( + Parse *pParse, /* Parsing contexts */ + Table *pTab, /* Add column type information to this table */ + Select *pSelect, /* SELECT used to determine types and collations */ + char aff /* Default affinity. */ ){ wx_sqlite3 *db = pParse->db; - NameContext sNC; Column *pCol; CollSeq *pColl; - int i; + int i,j; Expr *p; struct ExprList_item *a; + NameContext sNC; assert( pSelect!=0 ); assert( (pSelect->selFlags & SF_Resolved)!=0 ); - assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed ); + assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); + assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); if( db->mallocFailed ) return; + while( pSelect->pPrior ) pSelect = pSelect->pPrior; + a = pSelect->pEList->a; memset(&sNC, 0, sizeof(sNC)); sNC.pSrcList = pSelect->pSrc; - a = pSelect->pEList->a; for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ const char *zType; - int n, m; + i64 n; pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT); p = a[i].pExpr; - zType = columnType(&sNC, p, 0, 0, 0); /* pCol->szEst = ... // Column size est for SELECT tables never used */ pCol->affinity = wx_sqlite3ExprAffinity(p); - if( zType ){ - m = wx_sqlite3Strlen30(zType); - n = wx_sqlite3Strlen30(pCol->zName); - pCol->zName = wx_sqlite3DbReallocOrFree(db, pCol->zName, n+m+2); - if( pCol->zName ){ - memcpy(&pCol->zName[n+1], zType, m+1); - pCol->colFlags |= COLFLAG_HASTYPE; + if( pCol->affinity<=SQLITE_AFF_NONE ){ + pCol->affinity = aff; + } + if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){ + int m = 0; + Select *pS2; + for(m=0, pS2=pSelect->pNext; pS2; pS2=pS2->pNext){ + m |= wx_sqlite3ExprDataType(pS2->pEList->a[i].pExpr); + } + if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){ + pCol->affinity = SQLITE_AFF_BLOB; + }else + if( pCol->affinity>=SQLITE_AFF_NUMERIC && (m&0x02)!=0 ){ + pCol->affinity = SQLITE_AFF_BLOB; + } + if( pCol->affinity>=SQLITE_AFF_NUMERIC && p->op==TK_CAST ){ + pCol->affinity = SQLITE_AFF_FLEXNUM; + } + } + zType = columnType(&sNC, p, 0, 0, 0); + if( zType==0 || pCol->affinity!=wx_sqlite3AffinityType(zType, 0) ){ + if( pCol->affinity==SQLITE_AFF_NUMERIC + || pCol->affinity==SQLITE_AFF_FLEXNUM + ){ + zType = "NUM"; + }else{ + zType = 0; + for(j=1; jaffinity ){ + zType = wx_sqlite3StdType[j]; + break; + } + } + } + } + if( zType ){ + i64 m = wx_sqlite3Strlen30(zType); + n = wx_sqlite3Strlen30(pCol->zCnName); + pCol->zCnName = wx_sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2); + if( pCol->zCnName ){ + memcpy(&pCol->zCnName[n+1], zType, m+1); + pCol->colFlags |= COLFLAG_HASTYPE; + }else{ + testcase( pCol->colFlags & COLFLAG_HASTYPE ); + pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); } } - if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff; pColl = wx_sqlite3ExprCollSeq(pParse, p); - if( pColl && pCol->zColl==0 ){ - pCol->zColl = wx_sqlite3DbStrDup(db, pColl->zName); + if( pColl ){ + assert( pTab->pIndex==0 ); + wx_sqlite3ColumnSetColl(db, pCol, pColl->zName); } } pTab->szTabRow = 1; /* Any non-zero value works */ @@ -133366,7 +140795,7 @@ SQLITE_PRIVATE Table *wx_sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect pTab->zName = 0; pTab->nRowLogEst = 200; assert( 200==wx_sqlite3LogEst(1048576) ); wx_sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); - wx_sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect, aff); + wx_sqlite3SubqueryColumnTypes(pParse, pTab, pSelect, aff); pTab->iPKey = -1; if( db->mallocFailed ){ wx_sqlite3DeleteTable(db, pTab); @@ -133500,7 +140929,7 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){ */ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ ExprList *pOrderBy = p->pOrderBy; - int nOrderBy = p->pOrderBy->nExpr; + int nOrderBy = ALWAYS(pOrderBy!=0) ? pOrderBy->nExpr : 0; wx_sqlite3 *db = pParse->db; KeyInfo *pRet = wx_sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1); if( pRet ){ @@ -133520,7 +140949,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ } assert( wx_sqlite3KeyInfoIsWriteable(pRet) ); pRet->aColl[i] = pColl; - pRet->aSortFlags[i] = pOrderBy->a[i].sortFlags; + pRet->aSortFlags[i] = pOrderBy->a[i].fg.sortFlags; } } @@ -133572,7 +141001,7 @@ static void generateWithRecursiveQuery( SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */ int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */ Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */ - Select *pSetup = p->pPrior; /* The setup query */ + Select *pSetup; /* The setup query */ Select *pFirstRec; /* Left-most recursive term */ int addrTop; /* Top of the loop */ int addrCont, addrBreak; /* CONTINUE and BREAK addresses */ @@ -133656,7 +141085,6 @@ static void generateWithRecursiveQuery( ** iDistinct table. pFirstRec is left pointing to the left-most ** recursive term of the CTE. */ - pFirstRec = p; for(pFirstRec=p; ALWAYS(pFirstRec!=0); pFirstRec=pFirstRec->pPrior){ if( pFirstRec->selFlags & SF_Aggregate ){ wx_sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported"); @@ -133739,7 +141167,7 @@ static int multiSelectOrderBy( ** The "LIMIT of exactly 1" case of condition (1) comes about when a VALUES ** clause occurs within scalar expression (ex: "SELECT (VALUES(1),(2),(3))"). ** The wx_sqlite3CodeSubselect will have added the LIMIT 1 clause in tht case. -** Since the limit is exactly 1, we only need to evalutes the left-most VALUES. +** Since the limit is exactly 1, we only need to evaluate the left-most VALUES. */ static int multiSelectValues( Parse *pParse, /* Parsing context */ @@ -133887,11 +141315,12 @@ static int multiSelect( switch( p->op ){ case TK_ALL: { int addr = 0; - int nLimit; + int nLimit = 0; /* Initialize to suppress harmless compiler warning */ assert( !pPrior->pLimit ); pPrior->iLimit = p->iLimit; pPrior->iOffset = p->iOffset; pPrior->pLimit = p->pLimit; + TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n")); rc = wx_sqlite3Select(pParse, pPrior, &dest); pPrior->pLimit = 0; if( rc ){ @@ -133909,6 +141338,7 @@ static int multiSelect( } } ExplainQueryPlan((pParse, 1, "UNION ALL")); + TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n")); rc = wx_sqlite3Select(pParse, p, &dest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; @@ -133961,6 +141391,7 @@ static int multiSelect( */ assert( !pPrior->pOrderBy ); wx_sqlite3SelectDestInit(&uniondest, priorOp, unionTab); + TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION left...\n")); rc = wx_sqlite3Select(pParse, pPrior, &uniondest); if( rc ){ goto multi_select_end; @@ -133980,6 +141411,7 @@ static int multiSelect( uniondest.eDest = op; ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", wx_sqlite3SelectOpName(p->op))); + TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION right...\n")); rc = wx_sqlite3Select(pParse, p, &uniondest); testcase( rc!=SQLITE_OK ); assert( p->pOrderBy==0 ); @@ -134040,6 +141472,7 @@ static int multiSelect( /* Code the SELECTs to our left into temporary table "tab1". */ wx_sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); + TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT left...\n")); rc = wx_sqlite3Select(pParse, pPrior, &intersectdest); if( rc ){ goto multi_select_end; @@ -134056,6 +141489,7 @@ static int multiSelect( intersectdest.iSDParm = tab2; ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", wx_sqlite3SelectOpName(p->op))); + TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT right...\n")); rc = wx_sqlite3Select(pParse, p, &intersectdest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; @@ -134116,6 +141550,7 @@ static int multiSelect( int nCol; /* Number of columns in result set */ assert( p->pNext==0 ); + assert( p->pEList!=0 ); nCol = p->pEList->nExpr; pKeyInfo = wx_sqlite3KeyInfoAlloc(db, nCol, 1); if( !pKeyInfo ){ @@ -134150,7 +141585,11 @@ static int multiSelect( multi_select_end: pDest->iSdst = dest.iSdst; pDest->nSdst = dest.nSdst; - wx_sqlite3SelectDelete(db, pDelete); + if( pDelete ){ + wx_sqlite3ParserAddCleanup(pParse, + (void(*)(wx_sqlite3*,void*))wx_sqlite3SelectDelete, + pDelete); + } return rc; } #endif /* SQLITE_OMIT_COMPOUND_SELECT */ @@ -134404,6 +141843,8 @@ static int multiSelectOrderBy( ){ int i, j; /* Loop counters */ Select *pPrior; /* Another SELECT immediately to our left */ + Select *pSplit; /* Left-most SELECT in the right-hand group */ + int nSelect; /* Number of SELECT statements in the compound */ Vdbe *v; /* Generate code to this VDBE */ SelectDest destA; /* Destination for coroutine A */ SelectDest destB; /* Destination for coroutine B */ @@ -134449,8 +141890,7 @@ static int multiSelectOrderBy( /* Patch up the ORDER BY clause */ op = p->op; - pPrior = p->pPrior; - assert( pPrior->pOrderBy==0 ); + assert( p->pPrior->pOrderBy==0 ); pOrderBy = p->pOrderBy; assert( pOrderBy ); nOrderBy = pOrderBy->nExpr; @@ -134463,6 +141903,7 @@ static int multiSelectOrderBy( for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){ struct ExprList_item *pItem; for(j=0, pItem=pOrderBy->a; ju.x.iOrderByCol>0 ); if( pItem->u.x.iOrderByCol==i ) break; } @@ -134489,6 +141930,7 @@ static int multiSelectOrderBy( struct ExprList_item *pItem; aPermute[0] = nOrderBy; for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ + assert( pItem!=0 ); assert( pItem->u.x.iOrderByCol>0 ); assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ); aPermute[i] = pItem->u.x.iOrderByCol - 1; @@ -134498,11 +141940,6 @@ static int multiSelectOrderBy( pKeyMerge = 0; } - /* Reattach the ORDER BY clause to the query. - */ - p->pOrderBy = pOrderBy; - pPrior->pOrderBy = wx_sqlite3ExprListDup(pParse->db, pOrderBy, 0); - /* Allocate a range of temporary registers and the KeyInfo needed ** for the logic that removes duplicate result rows when the ** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL). @@ -134527,12 +141964,30 @@ static int multiSelectOrderBy( /* Separate the left and the right query from one another */ - p->pPrior = 0; + nSelect = 1; + if( (op==TK_ALL || op==TK_UNION) + && OptimizationEnabled(db, SQLITE_BalancedMerge) + ){ + for(pSplit=p; pSplit->pPrior!=0 && pSplit->op==op; pSplit=pSplit->pPrior){ + nSelect++; + assert( pSplit->pPrior->pNext==pSplit ); + } + } + if( nSelect<=3 ){ + pSplit = p; + }else{ + pSplit = p; + for(i=2; ipPrior; } + } + pPrior = pSplit->pPrior; + assert( pPrior!=0 ); + pSplit->pPrior = 0; pPrior->pNext = 0; + assert( p->pOrderBy == pOrderBy ); + assert( pOrderBy!=0 || db->mallocFailed ); + pPrior->pOrderBy = wx_sqlite3ExprListDup(pParse->db, pOrderBy, 0); wx_sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER"); - if( pPrior->pPrior==0 ){ - wx_sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); - } + wx_sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); /* Compute the limit registers */ computeLimitRegisters(pParse, p, labelEnd); @@ -134681,13 +142136,16 @@ static int multiSelectOrderBy( */ wx_sqlite3VdbeResolveLabel(v, labelEnd); - /* Reassembly the compound query so that it will be freed correctly - ** by the calling function */ - if( p->pPrior ){ - wx_sqlite3SelectDelete(db, p->pPrior); + /* Make arrangements to free the 2nd and subsequent arms of the compound + ** after the parse has finished */ + if( pSplit->pPrior ){ + wx_sqlite3ParserAddCleanup(pParse, + (void(*)(wx_sqlite3*,void*))wx_sqlite3SelectDelete, pSplit->pPrior); } - p->pPrior = pPrior; - pPrior->pNext = p; + pSplit->pPrior = pPrior; + pPrior->pNext = pSplit; + wx_sqlite3ExprListDelete(db, pPrior->pOrderBy); + pPrior->pOrderBy = 0; /*** TBD: Insert subroutine calls to close cursors on incomplete **** subqueries ****/ @@ -134703,13 +142161,42 @@ static int multiSelectOrderBy( ** ** All references to columns in table iTable are to be replaced by corresponding ** expressions in pEList. +** +** ## About "isOuterJoin": +** +** The isOuterJoin column indicates that the replacement will occur into a +** position in the parent that NULL-able due to an OUTER JOIN. Either the +** target slot in the parent is the right operand of a LEFT JOIN, or one of +** the left operands of a RIGHT JOIN. In either case, we need to potentially +** bypass the substituted expression with OP_IfNullRow. +** +** Suppose the original expression is an integer constant. Even though the table +** has the nullRow flag set, because the expression is an integer constant, +** it will not be NULLed out. So instead, we insert an OP_IfNullRow opcode +** that checks to see if the nullRow flag is set on the table. If the nullRow +** flag is set, then the value in the register is set to NULL and the original +** expression is bypassed. If the nullRow flag is not set, then the original +** expression runs to populate the register. +** +** Example where this is needed: +** +** CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT); +** CREATE TABLE t2(x INT UNIQUE); +** +** SELECT a,b,m,x FROM t1 LEFT JOIN (SELECT 59 AS m,x FROM t2) ON b=x; +** +** When the subquery on the right side of the LEFT JOIN is flattened, we +** have to add OP_IfNullRow in front of the OP_Integer that implements the +** "m" value of the subquery so that a NULL will be loaded instead of 59 +** when processing a non-matched row of the left. */ typedef struct SubstContext { Parse *pParse; /* The parsing context */ int iTable; /* Replace references to this table */ int iNewTable; /* New table number */ - int isLeftJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */ + int isOuterJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */ ExprList *pEList; /* Replacement expressions */ + ExprList *pCList; /* Collation sequences for replacement expr */ } SubstContext; /* Forward Declarations */ @@ -134734,57 +142221,78 @@ static Expr *substExpr( Expr *pExpr /* Expr in which substitution occurs */ ){ if( pExpr==0 ) return 0; - if( ExprHasProperty(pExpr, EP_FromJoin) - && pExpr->iRightJoinTable==pSubst->iTable + if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) + && pExpr->w.iJoin==pSubst->iTable ){ - pExpr->iRightJoinTable = pSubst->iNewTable; + testcase( ExprHasProperty(pExpr, EP_InnerON) ); + pExpr->w.iJoin = pSubst->iNewTable; } if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable && !ExprHasProperty(pExpr, EP_FixedCol) ){ +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW if( pExpr->iColumn<0 ){ pExpr->op = TK_NULL; - }else{ + }else +#endif + { Expr *pNew; - Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr; + int iColumn = pExpr->iColumn; + Expr *pCopy = pSubst->pEList->a[iColumn].pExpr; Expr ifNullRow; - assert( pSubst->pEList!=0 && pExpr->iColumnpEList->nExpr ); + assert( pSubst->pEList!=0 && iColumnpEList->nExpr ); assert( pExpr->pRight==0 ); if( wx_sqlite3ExprIsVector(pCopy) ){ wx_sqlite3VectorErrorMsg(pSubst->pParse, pCopy); }else{ wx_sqlite3 *db = pSubst->pParse->db; - if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){ + if( pSubst->isOuterJoin + && (pCopy->op!=TK_COLUMN || pCopy->iTable!=pSubst->iNewTable) + ){ memset(&ifNullRow, 0, sizeof(ifNullRow)); ifNullRow.op = TK_IF_NULL_ROW; ifNullRow.pLeft = pCopy; ifNullRow.iTable = pSubst->iNewTable; + ifNullRow.iColumn = -99; ifNullRow.flags = EP_IfNullRow; pCopy = &ifNullRow; } testcase( ExprHasProperty(pCopy, EP_Subquery) ); pNew = wx_sqlite3ExprDup(db, pCopy, 0); - if( pNew && pSubst->isLeftJoin ){ + if( db->mallocFailed ){ + wx_sqlite3ExprDelete(db, pNew); + return pExpr; + } + if( pSubst->isOuterJoin ){ ExprSetProperty(pNew, EP_CanBeNull); } - if( pNew && ExprHasProperty(pExpr,EP_FromJoin) ){ - wx_sqlite3SetJoinExpr(pNew, pExpr->iRightJoinTable); + if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){ + wx_sqlite3SetJoinExpr(pNew, pExpr->w.iJoin, + pExpr->flags & (EP_OuterON|EP_InnerON)); } wx_sqlite3ExprDelete(db, pExpr); pExpr = pNew; + if( pExpr->op==TK_TRUEFALSE ){ + pExpr->u.iValue = wx_sqlite3ExprTruthValue(pExpr); + pExpr->op = TK_INTEGER; + ExprSetProperty(pExpr, EP_IntValue); + } /* Ensure that the expression now has an implicit collation sequence, ** just as it did when it was a column of a view or sub-query. */ - if( pExpr ){ - if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){ - CollSeq *pColl = wx_sqlite3ExprCollSeq(pSubst->pParse, pExpr); + { + CollSeq *pNat = wx_sqlite3ExprCollSeq(pSubst->pParse, pExpr); + CollSeq *pColl = wx_sqlite3ExprCollSeq(pSubst->pParse, + pSubst->pCList->a[iColumn].pExpr + ); + if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){ pExpr = wx_sqlite3ExprAddCollateString(pSubst->pParse, pExpr, (pColl ? pColl->zName : "BINARY") ); } - ExprClearProperty(pExpr, EP_Collate); } + ExprClearProperty(pExpr, EP_Collate); } } }else{ @@ -134793,7 +142301,7 @@ static Expr *substExpr( } pExpr->pLeft = substExpr(pSubst, pExpr->pLeft); pExpr->pRight = substExpr(pSubst, pExpr->pRight); - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ substSelect(pSubst, pExpr->x.pSelect, 1); }else{ substExprList(pSubst, pExpr->x.pList); @@ -134884,10 +142392,10 @@ static void recomputeColumnsUsed( ** new cursor number assigned, set an entry in the aCsrMap[] array ** to map the old cursor number to the new: ** -** aCsrMap[iOld] = iNew; +** aCsrMap[iOld+1] = iNew; ** ** The array is guaranteed by the caller to be large enough for all -** existing cursor numbers in pSrc. +** existing cursor numbers in pSrc. aCsrMap[0] is the array size. ** ** If pSrc contains any sub-selects, call this routine recursively ** on the FROM clause of each such sub-select, with iExcept set to -1. @@ -134903,7 +142411,11 @@ static void srclistRenumberCursors( for(i=0, pItem=pSrc->a; inSrc; i++, pItem++){ if( i!=iExcept ){ Select *p; - pItem->iCursor = aCsrMap[pItem->iCursor] = pParse->nTab++; + assert( pItem->iCursor < aCsrMap[0] ); + if( !pItem->fg.isRecursive || aCsrMap[pItem->iCursor+1]==0 ){ + aCsrMap[pItem->iCursor+1] = pParse->nTab++; + } + pItem->iCursor = aCsrMap[pItem->iCursor+1]; for(p=pItem->pSelect; p; p=p->pPrior){ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1); } @@ -134911,18 +142423,28 @@ static void srclistRenumberCursors( } } +/* +** *piCursor is a cursor number. Change it if it needs to be mapped. +*/ +static void renumberCursorDoMapping(Walker *pWalker, int *piCursor){ + int *aCsrMap = pWalker->u.aiCol; + int iCsr = *piCursor; + if( iCsr < aCsrMap[0] && aCsrMap[iCsr+1]>0 ){ + *piCursor = aCsrMap[iCsr+1]; + } +} + /* ** Expression walker callback used by renumberCursors() to update ** Expr objects to match newly assigned cursor numbers. */ static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){ - int *aCsrMap = pWalker->u.aiCol; int op = pExpr->op; - if( (op==TK_COLUMN || op==TK_IF_NULL_ROW) && aCsrMap[pExpr->iTable] ){ - pExpr->iTable = aCsrMap[pExpr->iTable]; + if( op==TK_COLUMN || op==TK_IF_NULL_ROW ){ + renumberCursorDoMapping(pWalker, &pExpr->iTable); } - if( ExprHasProperty(pExpr, EP_FromJoin) && aCsrMap[pExpr->iRightJoinTable] ){ - pExpr->iRightJoinTable = aCsrMap[pExpr->iRightJoinTable]; + if( ExprHasProperty(pExpr, EP_OuterON) ){ + renumberCursorDoMapping(pWalker, &pExpr->w.iJoin); } return WRC_Continue; } @@ -134961,6 +142483,46 @@ static void renumberCursors( } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ +/* +** If pSel is not part of a compound SELECT, return a pointer to its +** expression list. Otherwise, return a pointer to the expression list +** of the leftmost SELECT in the compound. +*/ +static ExprList *findLeftmostExprlist(Select *pSel){ + while( pSel->pPrior ){ + pSel = pSel->pPrior; + } + return pSel->pEList; +} + +/* +** Return true if any of the result-set columns in the compound query +** have incompatible affinities on one or more arms of the compound. +*/ +static int compoundHasDifferentAffinities(Select *p){ + int ii; + ExprList *pList; + assert( p!=0 ); + assert( p->pEList!=0 ); + assert( p->pPrior!=0 ); + pList = p->pEList; + for(ii=0; iinExpr; ii++){ + char aff; + Select *pSub1; + assert( pList->a[ii].pExpr!=0 ); + aff = wx_sqlite3ExprAffinity(pList->a[ii].pExpr); + for(pSub1=p->pPrior; pSub1; pSub1=pSub1->pPrior){ + assert( pSub1->pEList!=0 ); + assert( pSub1->pEList->nExpr>ii ); + assert( pSub1->pEList->a[ii].pExpr!=0 ); + if( wx_sqlite3ExprAffinity(pSub1->pEList->a[ii].pExpr)!=aff ){ + return 1; + } + } + } + return 0; +} + #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* ** This routine attempts to flatten subqueries as a performance optimization. @@ -135005,8 +142567,10 @@ static void renumberCursors( ** (3a) the subquery may not be a join and ** (3b) the FROM clause of the subquery may not contain a virtual ** table and -** (3c) the outer query may not be an aggregate. +** (**) Was: "The outer query may not have a GROUP BY." This case +** is now managed correctly ** (3d) the outer query may not be DISTINCT. +** See also (26) for restrictions on RIGHT JOIN. ** ** (4) The subquery can not be DISTINCT. ** @@ -135058,6 +142622,12 @@ static void renumberCursors( ** (17d2) DISTINCT ** (17e) the subquery may not contain window functions, and ** (17f) the subquery must not be the RHS of a LEFT JOIN. +** (17g) either the subquery is the first element of the outer +** query or there are no RIGHT or FULL JOINs in any arm +** of the subquery. (This is a duplicate of condition (27b).) +** (17h) The corresponding result set expressions in all arms of the +** compound must have the same affinity. (See restriction (9) +** on the push-down optimization.) ** ** The parent and sub-query may contain WHERE clauses. Subject to ** rules (11), (13) and (14), they may also contain ORDER BY, @@ -135105,6 +142675,17 @@ static void renumberCursors( ** function in the select list or ORDER BY clause, flattening ** is not attempted. ** +** (26) The subquery may not be the right operand of a RIGHT JOIN. +** See also (3) for restrictions on LEFT JOIN. +** +** (27) The subquery may not contain a FULL or RIGHT JOIN unless it +** is the first element of the parent query. Two subcases: +** (27a) the subquery is not a compound query. +** (27b) the subquery is a compound query and the RIGHT JOIN occurs +** in any arm of the compound query. (See also (17g).) +** +** (28) The subquery is not a MATERIALIZED CTE. +** ** ** In this routine, the "p" parameter is a pointer to the outer query. ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query @@ -135130,7 +142711,7 @@ static int flattenSubquery( SrcList *pSubSrc; /* The FROM clause of the subquery */ int iParent; /* VDBE cursor number of the pSub result set temp table */ int iNewParent = -1;/* Replacement table for iParent */ - int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */ + int isOuterJoin = 0; /* True if pSub is the right side of a LEFT JOIN */ int i; /* Loop counter */ Expr *pWhere; /* The WHERE clause */ SrcItem *pSubitem; /* The subquery */ @@ -135196,32 +142777,26 @@ static int flattenSubquery( ** ** which is not at all the same thing. ** - ** If the subquery is the right operand of a LEFT JOIN, then the outer - ** query cannot be an aggregate. (3c) This is an artifact of the way - ** aggregates are processed - there is no mechanism to determine if - ** the LEFT JOIN table should be all-NULL. - ** ** See also tickets #306, #350, and #3300. */ - if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){ - isLeftJoin = 1; - if( pSubSrc->nSrc>1 /* (3a) */ - || isAgg /* (3b) */ - || IsVirtual(pSubSrc->a[0].pTab) /* (3c) */ - || (p->selFlags & SF_Distinct)!=0 /* (3d) */ + if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){ + if( pSubSrc->nSrc>1 /* (3a) */ + || IsVirtual(pSubSrc->a[0].pTab) /* (3b) */ + || (p->selFlags & SF_Distinct)!=0 /* (3d) */ + || (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */ ){ return 0; } + isOuterJoin = 1; } -#ifdef SQLITE_EXTRA_IFNULLROW - else if( iFrom>0 && !isAgg ){ - /* Setting isLeftJoin to -1 causes OP_IfNullRow opcodes to be generated for - ** every reference to any result column from subquery in a join, even - ** though they are not necessary. This will stress-test the OP_IfNullRow - ** opcode. */ - isLeftJoin = -1; + + assert( pSubSrc->nSrc>0 ); /* True by restriction (7) */ + if( iFrom>0 && (pSubSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ + return 0; /* Restriction (27a) */ + } + if( pSubitem->fg.isCte && pSubitem->u2.pCteUse->eM10d==M10d_Yes ){ + return 0; /* (28) */ } -#endif /* Restriction (17): If the sub-query is a compound SELECT, then it must ** use only the UNION ALL operator. And none of the simple select queries @@ -135229,10 +142804,11 @@ static int flattenSubquery( ** queries. */ if( pSub->pPrior ){ + int ii; if( pSub->pOrderBy ){ return 0; /* Restriction (20) */ } - if( isAgg || (p->selFlags & SF_Distinct)!=0 || isLeftJoin>0 ){ + if( isAgg || (p->selFlags & SF_Distinct)!=0 || isOuterJoin>0 ){ return 0; /* (17d1), (17d2), or (17f) */ } for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){ @@ -135250,12 +142826,17 @@ static int flattenSubquery( ){ return 0; } + if( iFrom>0 && (pSub1->pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ + /* Without this restriction, the JT_LTORJ flag would end up being + ** omitted on left-hand tables of the right join that is being + ** flattened. */ + return 0; /* Restrictions (17g), (27b) */ + } testcase( pSub1->pSrc->nSrc>1 ); } /* Restriction (18). */ if( p->pOrderBy ){ - int ii; for(ii=0; iipOrderBy->nExpr; ii++){ if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0; } @@ -135264,14 +142845,19 @@ static int flattenSubquery( /* Restriction (23) */ if( (p->selFlags & SF_Recursive) ) return 0; + /* Restriction (17h) */ + if( compoundHasDifferentAffinities(pSub) ) return 0; + if( pSrc->nSrc>1 ){ if( pParse->nSelect>500 ) return 0; - aCsrMap = wx_sqlite3DbMallocZero(db, pParse->nTab*sizeof(int)); + if( OptimizationDisabled(db, SQLITE_FlttnUnionAll) ) return 0; + aCsrMap = wx_sqlite3DbMallocZero(db, ((i64)pParse->nTab+1)*sizeof(int)); + if( aCsrMap ) aCsrMap[0] = pParse->nTab; } } /***** If we reach this point, flattening is permitted. *****/ - SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n", + TREETRACE(0x4,pParse,p,("flatten %u.%p from term %d\n", pSub->selId, pSub, iFrom)); /* Authorize the subquery */ @@ -135289,7 +142875,7 @@ static int flattenSubquery( pSubitem->zName = 0; pSubitem->zAlias = 0; pSubitem->pSelect = 0; - assert( pSubitem->pOn==0 ); + assert( pSubitem->fg.isUsing!=0 || pSubitem->u3.pOn==0 ); /* If the sub-query is a compound SELECT statement, then (by restrictions ** 17 and 18 above) it must be a UNION ALL and the parent query must @@ -135343,14 +142929,14 @@ static int flattenSubquery( p->pPrior = pPrior; }else{ pNew->selId = ++pParse->nSelect; - if( aCsrMap && db->mallocFailed==0 ){ + if( aCsrMap && ALWAYS(db->mallocFailed==0) ){ renumberCursors(pParse, pNew, iFrom, aCsrMap); } pNew->pPrior = pPrior; if( pPrior ) pPrior->pNext = pNew; pNew->pNext = p; p->pPrior = pNew; - SELECTTRACE(2,pParse,p,("compound-subquery flattener" + TREETRACE(0x4,pParse,p,("compound-subquery flattener" " creates %u as peer\n",pNew->selId)); } assert( pSubitem->pSelect==0 ); @@ -135399,6 +142985,7 @@ static int flattenSubquery( for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){ int nSubSrc; u8 jointype = 0; + u8 ltorj = pSrc->a[iFrom].fg.jointype & JT_LTORJ; assert( pSub!=0 ); pSubSrc = pSub->pSrc; /* FROM clause of subquery */ nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */ @@ -135433,13 +143020,16 @@ static int flattenSubquery( ** outer query. */ for(i=0; ia[i+iFrom].pUsing); - assert( pSrc->a[i+iFrom].fg.isTabFunc==0 ); - pSrc->a[i+iFrom] = pSubSrc->a[i]; + SrcItem *pItem = &pSrc->a[i+iFrom]; + if( pItem->fg.isUsing ) wx_sqlite3IdListDelete(db, pItem->u3.pUsing); + assert( pItem->fg.isTabFunc==0 ); + *pItem = pSubSrc->a[i]; + pItem->fg.jointype |= ltorj; iNewParent = pSubSrc->a[i].iCursor; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } - pSrc->a[iFrom].fg.jointype = jointype; + pSrc->a[iFrom].fg.jointype &= JT_LTORJ; + pSrc->a[iFrom].fg.jointype |= jointype | ltorj; /* Now begin substituting subquery result set expressions for ** references to the iParent in the outer query. @@ -135474,8 +143064,8 @@ static int flattenSubquery( } pWhere = pSub->pWhere; pSub->pWhere = 0; - if( isLeftJoin>0 ){ - wx_sqlite3SetJoinExpr(pWhere, iNewParent); + if( isOuterJoin>0 ){ + wx_sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON); } if( pWhere ){ if( pParent->pWhere ){ @@ -135489,8 +143079,9 @@ static int flattenSubquery( x.pParse = pParse; x.iTable = iParent; x.iNewTable = iNewParent; - x.isLeftJoin = isLeftJoin; + x.isOuterJoin = isOuterJoin; x.pEList = pSub->pEList; + x.pCList = findLeftmostExprlist(pSub); substSelect(&x, pParent, 0); } @@ -135510,7 +143101,7 @@ static int flattenSubquery( pSub->pLimit = 0; } - /* Recompute the SrcList_item.colUsed masks for the flattened + /* Recompute the SrcItem.colUsed masks for the flattened ** tables. */ for(i=0; ia[i+iFrom]); @@ -135524,9 +143115,9 @@ static int flattenSubquery( wx_sqlite3WalkSelect(&w,pSub1); wx_sqlite3SelectDelete(db, pSub1); -#if SELECTTRACE_ENABLED - if( wx_sqlite3SelectTrace & 0x100 ){ - SELECTTRACE(0x100,pParse,p,("After flattening:\n")); +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x4 ){ + TREETRACE(0x4,pParse,p,("After flattening:\n")); wx_sqlite3TreeViewSelect(0, p, 0); } #endif @@ -135542,8 +143133,12 @@ static int flattenSubquery( typedef struct WhereConst WhereConst; struct WhereConst { Parse *pParse; /* Parsing context */ + u8 *pOomFault; /* Pointer to pParse->db->mallocFailed */ int nConst; /* Number for COLUMN=CONSTANT terms */ int nChng; /* Number of times a constant is propagated */ + int bHasAffBlob; /* At least one column in apExpr[] as affinity BLOB */ + u32 mExcludeOn; /* Which ON expressions to exclude from considertion. + ** Either EP_OuterON or EP_InnerON|EP_OuterON */ Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */ }; @@ -135582,6 +143177,9 @@ static void constInsert( return; /* Already present. Return without doing anything. */ } } + if( wx_sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){ + pConst->bHasAffBlob = 1; + } pConst->nConst++; pConst->apExpr = wx_sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr, @@ -135602,8 +143200,12 @@ static void constInsert( */ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ Expr *pRight, *pLeft; - if( pExpr==0 ) return; - if( ExprHasProperty(pExpr, EP_FromJoin) ) return; + if( NEVER(pExpr==0) ) return; + if( ExprHasProperty(pExpr, pConst->mExcludeOn) ){ + testcase( ExprHasProperty(pExpr, EP_OuterON) ); + testcase( ExprHasProperty(pExpr, EP_InnerON) ); + return; + } if( pExpr->op==TK_AND ){ findConstInWhere(pConst, pExpr->pRight); findConstInWhere(pConst, pExpr->pLeft); @@ -135623,37 +143225,84 @@ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ } /* -** This is a Walker expression callback. pExpr is a candidate expression -** to be replaced by a value. If pExpr is equivalent to one of the -** columns named in pWalker->u.pConst, then overwrite it with its -** corresponding value. +** This is a helper function for Walker callback propagateConstantExprRewrite(). +** +** Argument pExpr is a candidate expression to be replaced by a value. If +** pExpr is equivalent to one of the columns named in pWalker->u.pConst, +** then overwrite it with the corresponding value. Except, do not do so +** if argument bIgnoreAffBlob is non-zero and the affinity of pExpr +** is SQLITE_AFF_BLOB. */ -static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ +static int propagateConstantExprRewriteOne( + WhereConst *pConst, + Expr *pExpr, + int bIgnoreAffBlob +){ int i; - WhereConst *pConst; + if( pConst->pOomFault[0] ) return WRC_Prune; if( pExpr->op!=TK_COLUMN ) return WRC_Continue; - if( ExprHasProperty(pExpr, EP_FixedCol|EP_FromJoin) ){ + if( ExprHasProperty(pExpr, EP_FixedCol|pConst->mExcludeOn) ){ testcase( ExprHasProperty(pExpr, EP_FixedCol) ); - testcase( ExprHasProperty(pExpr, EP_FromJoin) ); + testcase( ExprHasProperty(pExpr, EP_OuterON) ); + testcase( ExprHasProperty(pExpr, EP_InnerON) ); return WRC_Continue; } - pConst = pWalker->u.pConst; for(i=0; inConst; i++){ Expr *pColumn = pConst->apExpr[i*2]; if( pColumn==pExpr ) continue; if( pColumn->iTable!=pExpr->iTable ) continue; if( pColumn->iColumn!=pExpr->iColumn ) continue; + if( bIgnoreAffBlob && wx_sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){ + break; + } /* A match is found. Add the EP_FixedCol property */ pConst->nChng++; ExprClearProperty(pExpr, EP_Leaf); ExprSetProperty(pExpr, EP_FixedCol); assert( pExpr->pLeft==0 ); pExpr->pLeft = wx_sqlite3ExprDup(pConst->pParse->db, pConst->apExpr[i*2+1], 0); + if( pConst->pParse->db->mallocFailed ) return WRC_Prune; break; } return WRC_Prune; } +/* +** This is a Walker expression callback. pExpr is a node from the WHERE +** clause of a SELECT statement. This function examines pExpr to see if +** any substitutions based on the contents of pWalker->u.pConst should +** be made to pExpr or its immediate children. +** +** A substitution is made if: +** +** + pExpr is a column with an affinity other than BLOB that matches +** one of the columns in pWalker->u.pConst, or +** +** + pExpr is a binary comparison operator (=, <=, >=, <, >) that +** uses an affinity other than TEXT and one of its immediate +** children is a column that matches one of the columns in +** pWalker->u.pConst. +*/ +static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ + WhereConst *pConst = pWalker->u.pConst; + assert( TK_GT==TK_EQ+1 ); + assert( TK_LE==TK_EQ+2 ); + assert( TK_LT==TK_EQ+3 ); + assert( TK_GE==TK_EQ+4 ); + if( pConst->bHasAffBlob ){ + if( (pExpr->op>=TK_EQ && pExpr->op<=TK_GE) + || pExpr->op==TK_IS + ){ + propagateConstantExprRewriteOne(pConst, pExpr->pLeft, 0); + if( pConst->pOomFault[0] ) return WRC_Prune; + if( wx_sqlite3ExprAffinity(pExpr->pLeft)!=SQLITE_AFF_TEXT ){ + propagateConstantExprRewriteOne(pConst, pExpr->pRight, 0); + } + } + } + return propagateConstantExprRewriteOne(pConst, pExpr, pConst->bHasAffBlob); +} + /* ** The WHERE-clause constant propagation optimization. ** @@ -135689,6 +143338,21 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ ** routines know to generate the constant "123" instead of looking up the ** column value. Also, to avoid collation problems, this optimization is ** only attempted if the "a=123" term uses the default BINARY collation. +** +** 2021-05-25 forum post 6a06202608: Another troublesome case is... +** +** CREATE TABLE t1(x); +** INSERT INTO t1 VALUES(10.0); +** SELECT 1 FROM t1 WHERE x=10 AND x LIKE 10; +** +** The query should return no rows, because the t1.x value is '10.0' not '10' +** and '10.0' is not LIKE '10'. But if we are not careful, the first WHERE +** term "x=10" will cause the second WHERE term to become "10 LIKE 10", +** resulting in a false positive. To avoid this, constant propagation for +** columns with BLOB affinity is only allowed if the constant is used with +** operators ==, <=, <, >=, >, or IS in a way that will cause the correct +** type conversions to occur. See logic associated with the bHasAffBlob flag +** for details. */ static int propagateConstants( Parse *pParse, /* The parsing context */ @@ -135698,10 +143362,23 @@ static int propagateConstants( Walker w; int nChng = 0; x.pParse = pParse; + x.pOomFault = &pParse->db->mallocFailed; do{ x.nConst = 0; x.nChng = 0; x.apExpr = 0; + x.bHasAffBlob = 0; + if( ALWAYS(p->pSrc!=0) + && p->pSrc->nSrc>0 + && (p->pSrc->a[0].fg.jointype & JT_LTORJ)!=0 + ){ + /* Do not propagate constants on any ON clause if there is a + ** RIGHT JOIN anywhere in the query */ + x.mExcludeOn = EP_InnerON | EP_OuterON; + }else{ + /* Do not propagate constants through the ON clause of a LEFT JOIN */ + x.mExcludeOn = EP_OuterON; + } findConstInWhere(&x, p->pWhere); if( x.nConst ){ memset(&w, 0, sizeof(w)); @@ -135814,6 +143491,15 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ ** be materialized. (This restriction is implemented in the calling ** routine.) ** +** (8) If the subquery is a compound that uses UNION, INTERSECT, +** or EXCEPT, then all of the result set columns for all arms of +** the compound must use the BINARY collating sequence. +** +** (9) If the subquery is a compound, then all arms of the compound must +** have the same affinity. (This is the same as restriction (17h) +** for query flattening.) +** +** ** Return 0 if no changes are made and non-zero if one or more WHERE clause ** terms are duplicated into the subquery. */ @@ -135821,24 +143507,52 @@ static int pushDownWhereTerms( Parse *pParse, /* Parse context (for malloc() and error reporting) */ Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ Expr *pWhere, /* The WHERE clause of the outer query */ - int iCursor, /* Cursor number of the subquery */ - int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */ + SrcItem *pSrc /* The subquery term of the outer FROM clause */ ){ Expr *pNew; int nChng = 0; if( pWhere==0 ) return 0; if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0; + if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ) return 0; -#ifndef SQLITE_OMIT_WINDOWFUNC if( pSubq->pPrior ){ Select *pSel; + int notUnionAll = 0; for(pSel=pSubq; pSel; pSel=pSel->pPrior){ + u8 op = pSel->op; + assert( op==TK_ALL || op==TK_SELECT + || op==TK_UNION || op==TK_INTERSECT || op==TK_EXCEPT ); + if( op!=TK_ALL && op!=TK_SELECT ){ + notUnionAll = 1; + } +#ifndef SQLITE_OMIT_WINDOWFUNC if( pSel->pWin ) return 0; /* restriction (6b) */ +#endif + } + if( compoundHasDifferentAffinities(pSubq) ){ + return 0; /* restriction (9) */ + } + if( notUnionAll ){ + /* If any of the compound arms are connected using UNION, INTERSECT, + ** or EXCEPT, then we must ensure that none of the columns use a + ** non-BINARY collating sequence. */ + for(pSel=pSubq; pSel; pSel=pSel->pPrior){ + int ii; + const ExprList *pList = pSel->pEList; + assert( pList!=0 ); + for(ii=0; iinExpr; ii++){ + CollSeq *pColl = wx_sqlite3ExprCollSeq(pParse, pList->a[ii].pExpr); + if( !wx_sqlite3IsBinary(pColl) ){ + return 0; /* Restriction (8) */ + } + } + } } }else{ +#ifndef SQLITE_OMIT_WINDOWFUNC if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0; - } #endif + } #ifdef SQLITE_DEBUG /* Only the first term of a compound can have a WITH clause. But make @@ -135857,31 +143571,37 @@ static int pushDownWhereTerms( return 0; /* restriction (3) */ } while( pWhere->op==TK_AND ){ - nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, - iCursor, isLeftJoin); + nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrc); pWhere = pWhere->pLeft; } + +#if 0 /* Legacy code. Checks now done by wx_sqlite3ExprIsTableConstraint() */ if( isLeftJoin - && (ExprHasProperty(pWhere,EP_FromJoin)==0 - || pWhere->iRightJoinTable!=iCursor) + && (ExprHasProperty(pWhere,EP_OuterON)==0 + || pWhere->w.iJoin!=iCursor) ){ return 0; /* restriction (4) */ } - if( ExprHasProperty(pWhere,EP_FromJoin) && pWhere->iRightJoinTable!=iCursor ){ + if( ExprHasProperty(pWhere,EP_OuterON) + && pWhere->w.iJoin!=iCursor + ){ return 0; /* restriction (5) */ } - if( wx_sqlite3ExprIsTableConstant(pWhere, iCursor) ){ +#endif + + if( wx_sqlite3ExprIsTableConstraint(pWhere, pSrc) ){ nChng++; pSubq->selFlags |= SF_PushDown; while( pSubq ){ SubstContext x; pNew = wx_sqlite3ExprDup(pParse->db, pWhere, 0); - unsetJoinExpr(pNew, -1); + unsetJoinExpr(pNew, -1, 1); x.pParse = pParse; - x.iTable = iCursor; - x.iNewTable = iCursor; - x.isLeftJoin = 0; + x.iTable = pSrc->iCursor; + x.iNewTable = pSrc->iCursor; + x.isOuterJoin = 0; x.pEList = pSubq->pEList; + x.pCList = findLeftmostExprlist(pSubq); pNew = substExpr(&x, pNew); #ifndef SQLITE_OMIT_WINDOWFUNC if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){ @@ -135921,7 +143641,7 @@ static int pushDownWhereTerms( */ static u8 minMaxQuery(wx_sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ int eRet = WHERE_ORDERBY_NORMAL; /* Return value */ - ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */ + ExprList *pEList; /* Arguments to agg function */ const char *zFunc; /* Name of aggregate function pFunc */ ExprList *pOrderBy; u8 sortFlags = 0; @@ -135929,6 +143649,8 @@ static u8 minMaxQuery(wx_sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ assert( *ppMinMax==0 ); assert( pFunc->op==TK_AGG_FUNCTION ); assert( !IsWindowFunc(pFunc) ); + assert( ExprUseXList(pFunc) ); + pEList = pFunc->x.pList; if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_WinFunc) @@ -135936,6 +143658,7 @@ static u8 minMaxQuery(wx_sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ ){ return eRet; } + assert( !ExprHasProperty(pFunc, EP_IntValue) ); zFunc = pFunc->u.zToken; if( wx_sqlite3StrICmp(zFunc, "min")==0 ){ eRet = WHERE_ORDERBY_MIN; @@ -135950,7 +143673,7 @@ static u8 minMaxQuery(wx_sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ } *ppMinMax = pOrderBy = wx_sqlite3ExprListDup(db, pEList, 0); assert( pOrderBy!=0 || db->mallocFailed ); - if( pOrderBy ) pOrderBy->a[0].sortFlags = sortFlags; + if( pOrderBy ) pOrderBy->a[0].fg.sortFlags = sortFlags; return eRet; } @@ -135963,7 +143686,13 @@ static u8 minMaxQuery(wx_sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ ** ** where table is a database table, not a sub-select or view. If the query ** does match this pattern, then a pointer to the Table object representing -** is returned. Otherwise, 0 is returned. +** is returned. Otherwise, NULL is returned. +** +** This routine checks to see if it is safe to use the count optimization. +** A correct answer is still obtained (though perhaps more slowly) if +** this routine returns NULL when it could have returned a table pointer. +** But returning the pointer when NULL should have been returned can +** result in incorrect answers and/or crashes. So, when in doubt, return NULL. */ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ Table *pTab; @@ -135971,19 +143700,27 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ assert( !p->pGroupBy ); - if( p->pWhere || p->pEList->nExpr!=1 - || p->pSrc->nSrc!=1 || p->pSrc->a[0].pSelect + if( p->pWhere + || p->pEList->nExpr!=1 + || p->pSrc->nSrc!=1 + || p->pSrc->a[0].pSelect + || pAggInfo->nFunc!=1 + || p->pHaving ){ return 0; } pTab = p->pSrc->a[0].pTab; + assert( pTab!=0 ); + assert( !IsView(pTab) ); + if( !IsOrdinaryTable(pTab) ) return 0; pExpr = p->pEList->a[0].pExpr; - assert( pTab && !pTab->pSelect && pExpr ); - - if( IsVirtual(pTab) ) return 0; + assert( pExpr!=0 ); if( pExpr->op!=TK_AGG_FUNCTION ) return 0; - if( NEVER(pAggInfo->nFunc==0) ) return 0; + if( pExpr->pAggInfo!=pAggInfo ) return 0; if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0; + assert( pAggInfo->aFunc[0].pFExpr==pExpr ); + testcase( ExprHasProperty(pExpr, EP_Distinct) ); + testcase( ExprHasProperty(pExpr, EP_WinFunc) ); if( ExprHasProperty(pExpr, EP_Distinct|EP_WinFunc) ) return 0; return pTab; @@ -136012,6 +143749,7 @@ SQLITE_PRIVATE int wx_sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){ pParse->checkSchema = 1; return SQLITE_ERROR; } + assert( pFrom->fg.isCte==0 ); pFrom->u2.pIBIndex = pIdx; return SQLITE_OK; } @@ -136072,7 +143810,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ pNew = wx_sqlite3DbMallocZero(db, sizeof(*pNew) ); if( pNew==0 ) return WRC_Abort; memset(&dummy, 0, sizeof(dummy)); - pNewSrc = wx_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0,0); + pNewSrc = wx_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0); if( pNewSrc==0 ) return WRC_Abort; *pNew = *p; p->pSrc = pNewSrc; @@ -136138,6 +143876,7 @@ static struct Cte *searchWith( return &p->a[i]; } } + if( p->bView ) break; } return 0; } @@ -136147,23 +143886,33 @@ static struct Cte *searchWith( ** ** This routine pushes the WITH clause passed as the second argument ** onto the top of the stack. If argument bFree is true, then this -** WITH clause will never be popped from the stack. In this case it -** should be freed along with the Parse object. In other cases, when +** WITH clause will never be popped from the stack but should instead +** be freed along with the Parse object. In other cases, when ** bFree==0, the With object will be freed along with the SELECT ** statement with which it is associated. +** +** This routine returns a copy of pWith. Or, if bFree is true and +** the pWith object is destroyed immediately due to an OOM condition, +** then this routine return NULL. +** +** If bFree is true, do not continue to use the pWith pointer after +** calling this routine, Instead, use only the return value. */ -SQLITE_PRIVATE void wx_sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ +SQLITE_PRIVATE With *wx_sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ if( pWith ){ - assert( pParse->pWith!=pWith ); - pWith->pOuter = pParse->pWith; - pParse->pWith = pWith; if( bFree ){ - wx_sqlite3ParserAddCleanup(pParse, - (void(*)(wx_sqlite3*,void*))wx_sqlite3WithDelete, - pWith); - testcase( pParse->earlyCleanup ); + pWith = (With*)wx_sqlite3ParserAddCleanup(pParse, + (void(*)(wx_sqlite3*,void*))wx_sqlite3WithDelete, + pWith); + if( pWith==0 ) return 0; + } + if( pParse->nErr==0 ){ + assert( pParse->pWith!=pWith ); + pWith->pOuter = pParse->pWith; + pParse->pWith = pWith; } } + return pWith; } /* @@ -136193,11 +143942,24 @@ static int resolveFromTermToCte( /* There are no WITH clauses in the stack. No match is possible */ return 0; } + if( pParse->nErr ){ + /* Prior errors might have left pParse->pWith in a goofy state, so + ** go no further. */ + return 0; + } if( pFrom->zDatabase!=0 ){ /* The FROM term contains a schema qualifier (ex: main.t1) and so ** it cannot possibly be a CTE reference. */ return 0; } + if( pFrom->fg.notCte ){ + /* The FROM term is specifically excluded from matching a CTE. + ** (1) It is part of a trigger that used to have zDatabase but had + ** zDatabase removed by wx_sqlite3FixTriggerStep(). + ** (2) This is the first term in the FROM clause of an UPDATE. + */ + return 0; + } pCte = searchWith(pParse->pWith, pFrom, &pWith); if( pCte ){ wx_sqlite3 *db = pParse->db; @@ -136243,13 +144005,15 @@ static int resolveFromTermToCte( pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; pFrom->pSelect = wx_sqlite3SelectDup(db, pCte->pSelect, 0); if( db->mallocFailed ) return 2; + pFrom->pSelect->selFlags |= SF_CopyCte; assert( pFrom->pSelect ); + if( pFrom->fg.isIndexedBy ){ + wx_sqlite3ErrorMsg(pParse, "no such index: \"%s\"", pFrom->u1.zIndexedBy); + return 2; + } pFrom->fg.isCte = 1; pFrom->u2.pCteUse = pCteUse; pCteUse->nUse++; - if( pCteUse->nUse>=2 && pCteUse->eM10d==M10d_Any ){ - pCteUse->eM10d = M10d_Yes; - } /* Check if this is a recursive CTE. */ pRecTerm = pSel = pFrom->pSelect; @@ -136346,7 +144110,7 @@ static int resolveFromTermToCte( ** wx_sqlite3SelectExpand() when walking a SELECT tree to resolve table ** names and other FROM clause elements. */ -static void selectPopWith(Walker *pWalker, Select *p){ +SQLITE_PRIVATE void wx_sqlite3SelectPopWith(Walker *pWalker, Select *p){ Parse *pParse = pWalker->pParse; if( OK_IF_ALWAYS_TRUE(pParse->pWith) && p->pPrior==0 ){ With *pWith = findRightmost(p)->pWith; @@ -136356,14 +144120,12 @@ static void selectPopWith(Walker *pWalker, Select *p){ } } } -#else -#define selectPopWith 0 #endif /* -** The SrcList_item structure passed as the second argument represents a +** The SrcItem structure passed as the second argument represents a ** sub-query in the FROM clause of a SELECT statement. This function -** allocates and populates the SrcList_item.pTab object. If successful, +** allocates and populates the SrcItem.pTab object. If successful, ** SQLITE_OK is returned. Otherwise, if an OOM error is encountered, ** SQLITE_NOMEM. */ @@ -136378,17 +144140,47 @@ SQLITE_PRIVATE int wx_sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){ if( pFrom->zAlias ){ pTab->zName = wx_sqlite3DbStrDup(pParse->db, pFrom->zAlias); }else{ - pTab->zName = wx_sqlite3MPrintf(pParse->db, "subquery_%u", pSel->selId); + pTab->zName = wx_sqlite3MPrintf(pParse->db, "%!S", pFrom); } while( pSel->pPrior ){ pSel = pSel->pPrior; } wx_sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); pTab->iPKey = -1; pTab->nRowLogEst = 200; assert( 200==wx_sqlite3LogEst(1048576) ); - pTab->tabFlags |= TF_Ephemeral; - +#ifndef SQLITE_ALLOW_ROWID_IN_VIEW + /* The usual case - do not allow ROWID on a subquery */ + pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; +#else + pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */ +#endif return pParse->nErr ? SQLITE_ERROR : SQLITE_OK; } + +/* +** Check the N SrcItem objects to the right of pBase. (N might be zero!) +** If any of those SrcItem objects have a USING clause containing zName +** then return true. +** +** If N is zero, or none of the N SrcItem objects to the right of pBase +** contains a USING clause, or if none of the USING clauses contain zName, +** then return false. +*/ +static int inAnyUsingClause( + const char *zName, /* Name we are looking for */ + SrcItem *pBase, /* The base SrcItem. Looking at pBase[1] and following */ + int N /* How many SrcItems to check */ +){ + while( N>0 ){ + N--; + pBase++; + if( pBase->fg.isUsing==0 ) continue; + if( NEVER(pBase->u3.pUsing==0) ) continue; + if( wx_sqlite3IdListIndex(pBase->u3.pUsing, zName)>=0 ) return 1; + } + return 0; +} + + /* ** This routine is a Walker callback for "expanding" a SELECT statement. ** "Expanding" means to do the following: @@ -136438,6 +144230,15 @@ static int selectExpander(Walker *pWalker, Select *p){ } pTabList = p->pSrc; pEList = p->pEList; + if( pParse->pWith && (p->selFlags & SF_View) ){ + if( p->pWith==0 ){ + p->pWith = (With*)wx_sqlite3DbMallocZero(db, sizeof(With)); + if( p->pWith==0 ){ + return WRC_Abort; + } + } + p->pWith->bView = 1; + } wx_sqlite3WithPush(pParse, p->pWith, 0); /* Make sure cursor numbers have been assigned to all entries in @@ -136485,29 +144286,31 @@ static int selectExpander(Walker *pWalker, Select *p){ return WRC_Abort; } #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) - if( IsVirtual(pTab) || pTab->pSelect ){ + if( !IsOrdinaryTable(pTab) ){ i16 nCol; u8 eCodeOrig = pWalker->eCode; if( wx_sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort; assert( pFrom->pSelect==0 ); - if( pTab->pSelect - && (db->flags & SQLITE_EnableView)==0 - && pTab->pSchema!=db->aDb[1].pSchema - ){ - wx_sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", - pTab->zName); + if( IsView(pTab) ){ + if( (db->flags & SQLITE_EnableView)==0 + && pTab->pSchema!=db->aDb[1].pSchema + ){ + wx_sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", + pTab->zName); + } + pFrom->pSelect = wx_sqlite3SelectDup(db, pTab->u.view.pSelect, 0); } #ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTab) + else if( ALWAYS(IsVirtual(pTab)) && pFrom->fg.fromDDL - && ALWAYS(pTab->pVTable!=0) - && pTab->pVTable->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) + && ALWAYS(pTab->u.vtab.p!=0) + && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) ){ wx_sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", pTab->zName); } + assert( SQLITE_VTABRISK_Normal==1 && SQLITE_VTABRISK_High==2 ); #endif - pFrom->pSelect = wx_sqlite3SelectDup(db, pTab->pSelect, 0); nCol = pTab->nCol; pTab->nCol = -1; pWalker->eCode = 1; /* Turn on Select.selId renumbering */ @@ -136526,7 +144329,8 @@ static int selectExpander(Walker *pWalker, Select *p){ /* Process NATURAL keywords, and ON and USING clauses of joins. */ - if( pParse->nErr || db->mallocFailed || sqliteProcessJoin(pParse, p) ){ + assert( db->mallocFailed==0 || pParse->nErr!=0 ); + if( pParse->nErr || wx_sqlite3ProcessJoin(pParse, p) ){ return WRC_Abort; } @@ -136574,7 +144378,7 @@ static int selectExpander(Walker *pWalker, Select *p){ pNew = wx_sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); if( pNew ){ pNew->a[pNew->nExpr-1].zEName = a[k].zEName; - pNew->a[pNew->nExpr-1].eEName = a[k].eEName; + pNew->a[pNew->nExpr-1].fg.eEName = a[k].fg.eEName; a[k].zEName = 0; } a[k].pExpr = 0; @@ -136589,32 +144393,60 @@ static int selectExpander(Walker *pWalker, Select *p){ zTName = pE->pLeft->u.zToken; } for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ - Table *pTab = pFrom->pTab; - Select *pSub = pFrom->pSelect; - char *zTabName = pFrom->zAlias; - const char *zSchemaName = 0; - int iDb; - if( zTabName==0 ){ + Table *pTab = pFrom->pTab; /* Table for this data source */ + ExprList *pNestedFrom; /* Result-set of a nested FROM clause */ + char *zTabName; /* AS name for this data source */ + const char *zSchemaName = 0; /* Schema name for this data source */ + int iDb; /* Schema index for this data src */ + IdList *pUsing; /* USING clause for pFrom[1] */ + + if( (zTabName = pFrom->zAlias)==0 ){ zTabName = pTab->zName; } if( db->mallocFailed ) break; - if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ - pSub = 0; + assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom->pSelect) ); + if( pFrom->fg.isNestedFrom ){ + assert( pFrom->pSelect!=0 ); + pNestedFrom = pFrom->pSelect->pEList; + assert( pNestedFrom!=0 ); + assert( pNestedFrom->nExpr==pTab->nCol ); + }else{ if( zTName && wx_sqlite3StrICmp(zTName, zTabName)!=0 ){ continue; } + pNestedFrom = 0; iDb = wx_sqlite3SchemaToIndex(db, pTab->pSchema); zSchemaName = iDb>=0 ? db->aDb[iDb].zDbSName : "*"; } + if( i+1nSrc + && pFrom[1].fg.isUsing + && (selFlags & SF_NestedFrom)!=0 + ){ + int ii; + pUsing = pFrom[1].u3.pUsing; + for(ii=0; iinId; ii++){ + const char *zUName = pUsing->a[ii].zName; + pRight = wx_sqlite3Expr(db, TK_ID, zUName); + pNew = wx_sqlite3ExprListAppend(pParse, pNew, pRight); + if( pNew ){ + struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; + assert( pX->zEName==0 ); + pX->zEName = wx_sqlite3MPrintf(db,"..%s", zUName); + pX->fg.eEName = ENAME_TAB; + pX->fg.bUsingTerm = 1; + } + } + }else{ + pUsing = 0; + } for(j=0; jnCol; j++){ - char *zName = pTab->aCol[j].zName; - char *zColname; /* The computed column name */ - char *zToFree; /* Malloced string that needs to be freed */ - Token sColname; /* Computed column name as a token */ + char *zName = pTab->aCol[j].zCnName; + struct ExprList_item *pX; /* Newly added ExprList term */ assert( zName ); - if( zTName && pSub - && wx_sqlite3MatchEName(&pSub->pEList->a[j], 0, zTName, 0)==0 + if( zTName + && pNestedFrom + && wx_sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0)==0 ){ continue; } @@ -136628,57 +144460,75 @@ static int selectExpander(Walker *pWalker, Select *p){ ){ continue; } + if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0 + && zTName==0 + && (selFlags & (SF_NestedFrom))==0 + ){ + continue; + } tableSeen = 1; - if( i>0 && zTName==0 ){ - if( (pFrom->fg.jointype & JT_NATURAL)!=0 - && tableAndColumnIndex(pTabList, i, zName, 0, 0, 1) + if( i>0 && zTName==0 && (selFlags & SF_NestedFrom)==0 ){ + if( pFrom->fg.isUsing + && wx_sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0 ){ - /* In a NATURAL join, omit the join columns from the - ** table to the right of the join */ - continue; - } - if( wx_sqlite3IdListIndex(pFrom->pUsing, zName)>=0 ){ /* In a join with a USING clause, omit columns in the ** using clause from the table on the right. */ continue; } } pRight = wx_sqlite3Expr(db, TK_ID, zName); - zColname = zName; - zToFree = 0; - if( longNames || pTabList->nSrc>1 ){ + if( (pTabList->nSrc>1 + && ( (pFrom->fg.jointype & JT_LTORJ)==0 + || (selFlags & SF_NestedFrom)!=0 + || !inAnyUsingClause(zName,pFrom,pTabList->nSrc-i-1) + ) + ) + || IN_RENAME_OBJECT + ){ Expr *pLeft; pLeft = wx_sqlite3Expr(db, TK_ID, zTabName); pExpr = wx_sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); + if( IN_RENAME_OBJECT && pE->pLeft ){ + wx_sqlite3RenameTokenRemap(pParse, pLeft, pE->pLeft); + } if( zSchemaName ){ pLeft = wx_sqlite3Expr(db, TK_ID, zSchemaName); pExpr = wx_sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr); } - if( longNames ){ - zColname = wx_sqlite3MPrintf(db, "%s.%s", zTabName, zName); - zToFree = zColname; - } }else{ pExpr = pRight; } pNew = wx_sqlite3ExprListAppend(pParse, pNew, pExpr); - wx_sqlite3TokenInit(&sColname, zColname); - wx_sqlite3ExprListSetName(pParse, pNew, &sColname, 0); - if( pNew && (p->selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ - struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; - wx_sqlite3DbFree(db, pX->zEName); - if( pSub ){ - pX->zEName = wx_sqlite3DbStrDup(db, pSub->pEList->a[j].zEName); + if( pNew==0 ){ + break; /* OOM */ + } + pX = &pNew->a[pNew->nExpr-1]; + assert( pX->zEName==0 ); + if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ + if( pNestedFrom ){ + pX->zEName = wx_sqlite3DbStrDup(db, pNestedFrom->a[j].zEName); testcase( pX->zEName==0 ); }else{ pX->zEName = wx_sqlite3MPrintf(db, "%s.%s.%s", - zSchemaName, zTabName, zColname); + zSchemaName, zTabName, zName); testcase( pX->zEName==0 ); } - pX->eEName = ENAME_TAB; + pX->fg.eEName = ENAME_TAB; + if( (pFrom->fg.isUsing + && wx_sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0) + || (pUsing && wx_sqlite3IdListIndex(pUsing, zName)>=0) + || (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0 + ){ + pX->fg.bNoExpand = 1; + } + }else if( longNames ){ + pX->zEName = wx_sqlite3MPrintf(db, "%s.%s", zTabName, zName); + pX->fg.eEName = ENAME_NAME; + }else{ + pX->zEName = wx_sqlite3DbStrDup(db, zName); + pX->fg.eEName = ENAME_NAME; } - wx_sqlite3DbFree(db, zToFree); } } if( !tableSeen ){ @@ -136702,6 +144552,12 @@ static int selectExpander(Walker *pWalker, Select *p){ p->selFlags |= SF_ComplexResult; } } +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x8 ){ + TREETRACE(0x8,pParse,p,("After result-set wildcard expansion:\n")); + wx_sqlite3TreeViewSelect(0, p, 0); + } +#endif return WRC_Continue; } @@ -136738,7 +144594,7 @@ static void wx_sqlite3SelectExpand(Parse *pParse, Select *pSelect){ wx_sqlite3WalkSelect(&w, pSelect); } w.xSelectCallback = selectExpander; - w.xSelectCallback2 = selectPopWith; + w.xSelectCallback2 = wx_sqlite3SelectPopWith; w.eCode = 0; wx_sqlite3WalkSelect(&w, pSelect); } @@ -136749,14 +144605,14 @@ static void wx_sqlite3SelectExpand(Parse *pParse, Select *pSelect){ ** This is a Walker.xSelectCallback callback for the wx_sqlite3SelectTypeInfo() ** interface. ** -** For each FROM-clause subquery, add Column.zType and Column.zColl -** information to the Table structure that represents the result set -** of that subquery. +** For each FROM-clause subquery, add Column.zType, Column.zColl, and +** Column.affinity information to the Table structure that represents +** the result set of that subquery. ** ** The Table structure that represents the result set was constructed -** by selectExpander() but the type and collation information was omitted -** at that point because identifiers had not yet been resolved. This -** routine is called after identifier resolution. +** by selectExpander() but the type and collation and affinity information +** was omitted at that point because identifiers had not yet been resolved. +** This routine is called after identifier resolution. */ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ Parse *pParse; @@ -136776,9 +144632,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ /* A sub-query in the FROM clause of a SELECT */ Select *pSel = pFrom->pSelect; if( pSel ){ - while( pSel->pPrior ) pSel = pSel->pPrior; - wx_sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel, - SQLITE_AFF_NONE); + wx_sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE); } } } @@ -136823,15 +144677,185 @@ SQLITE_PRIVATE void wx_sqlite3SelectPrep( NameContext *pOuterNC /* Name context for container */ ){ assert( p!=0 || pParse->db->mallocFailed ); + assert( pParse->db->pParse==pParse ); if( pParse->db->mallocFailed ) return; if( p->selFlags & SF_HasTypeInfo ) return; wx_sqlite3SelectExpand(pParse, p); - if( pParse->nErr || pParse->db->mallocFailed ) return; + if( pParse->nErr ) return; wx_sqlite3ResolveSelectNames(pParse, p, pOuterNC); - if( pParse->nErr || pParse->db->mallocFailed ) return; + if( pParse->nErr ) return; wx_sqlite3SelectAddTypeInfo(pParse, p); } +#if TREETRACE_ENABLED +/* +** Display all information about an AggInfo object +*/ +static void printAggInfo(AggInfo *pAggInfo){ + int ii; + for(ii=0; iinColumn; ii++){ + struct AggInfo_col *pCol = &pAggInfo->aCol[ii]; + wx_sqlite3DebugPrintf( + "agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d" + " iSorterColumn=%d %s\n", + ii, pCol->pTab ? pCol->pTab->zName : "NULL", + pCol->iTable, pCol->iColumn, pAggInfo->iFirstReg+ii, + pCol->iSorterColumn, + ii>=pAggInfo->nAccumulator ? "" : " Accumulator"); + wx_sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0); + } + for(ii=0; iinFunc; ii++){ + wx_sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n", + ii, pAggInfo->iFirstReg+pAggInfo->nColumn+ii); + wx_sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0); + } +} +#endif /* TREETRACE_ENABLED */ + +/* +** Analyze the arguments to aggregate functions. Create new pAggInfo->aCol[] +** entries for columns that are arguments to aggregate functions but which +** are not otherwise used. +** +** The aCol[] entries in AggInfo prior to nAccumulator are columns that +** are referenced outside of aggregate functions. These might be columns +** that are part of the GROUP by clause, for example. Other database engines +** would throw an error if there is a column reference that is not in the +** GROUP BY clause and that is not part of an aggregate function argument. +** But SQLite allows this. +** +** The aCol[] entries beginning with the aCol[nAccumulator] and following +** are column references that are used exclusively as arguments to +** aggregate functions. This routine is responsible for computing +** (or recomputing) those aCol[] entries. +*/ +static void analyzeAggFuncArgs( + AggInfo *pAggInfo, + NameContext *pNC +){ + int i; + assert( pAggInfo!=0 ); + assert( pAggInfo->iFirstReg==0 ); + pNC->ncFlags |= NC_InAggFunc; + for(i=0; inFunc; i++){ + Expr *pExpr = pAggInfo->aFunc[i].pFExpr; + assert( ExprUseXList(pExpr) ); + wx_sqlite3ExprAnalyzeAggList(pNC, pExpr->x.pList); +#ifndef SQLITE_OMIT_WINDOWFUNC + assert( !IsWindowFunc(pExpr) ); + if( ExprHasProperty(pExpr, EP_WinFunc) ){ + wx_sqlite3ExprAnalyzeAggregates(pNC, pExpr->y.pWin->pFilter); + } +#endif + } + pNC->ncFlags &= ~NC_InAggFunc; +} + +/* +** An index on expressions is being used in the inner loop of an +** aggregate query with a GROUP BY clause. This routine attempts +** to adjust the AggInfo object to take advantage of index and to +** perhaps use the index as a covering index. +** +*/ +static void optimizeAggregateUseOfIndexedExpr( + Parse *pParse, /* Parsing context */ + Select *pSelect, /* The SELECT statement being processed */ + AggInfo *pAggInfo, /* The aggregate info */ + NameContext *pNC /* Name context used to resolve agg-func args */ +){ + assert( pAggInfo->iFirstReg==0 ); + assert( pSelect!=0 ); + assert( pSelect->pGroupBy!=0 ); + pAggInfo->nColumn = pAggInfo->nAccumulator; + if( ALWAYS(pAggInfo->nSortingColumn>0) ){ + if( pAggInfo->nColumn==0 ){ + pAggInfo->nSortingColumn = pSelect->pGroupBy->nExpr; + }else{ + pAggInfo->nSortingColumn = + pAggInfo->aCol[pAggInfo->nColumn-1].iSorterColumn+1; + } + } + analyzeAggFuncArgs(pAggInfo, pNC); +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x20 ){ + IndexedExpr *pIEpr; + TREETRACE(0x20, pParse, pSelect, + ("AggInfo (possibly) adjusted for Indexed Exprs\n")); + wx_sqlite3TreeViewSelect(0, pSelect, 0); + for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){ + printf("data-cursor=%d index={%d,%d}\n", + pIEpr->iDataCur, pIEpr->iIdxCur, pIEpr->iIdxCol); + wx_sqlite3TreeViewExpr(0, pIEpr->pExpr, 0); + } + printAggInfo(pAggInfo); + } +#else + UNUSED_PARAMETER(pSelect); + UNUSED_PARAMETER(pParse); +#endif +} + +/* +** Walker callback for aggregateConvertIndexedExprRefToColumn(). +*/ +static int aggregateIdxEprRefToColCallback(Walker *pWalker, Expr *pExpr){ + AggInfo *pAggInfo; + struct AggInfo_col *pCol; + UNUSED_PARAMETER(pWalker); + if( pExpr->pAggInfo==0 ) return WRC_Continue; + if( pExpr->op==TK_AGG_COLUMN ) return WRC_Continue; + if( pExpr->op==TK_AGG_FUNCTION ) return WRC_Continue; + if( pExpr->op==TK_IF_NULL_ROW ) return WRC_Continue; + pAggInfo = pExpr->pAggInfo; + assert( pExpr->iAgg>=0 && pExpr->iAggnColumn ); + pCol = &pAggInfo->aCol[pExpr->iAgg]; + pExpr->op = TK_AGG_COLUMN; + pExpr->iTable = pCol->iTable; + pExpr->iColumn = pCol->iColumn; + return WRC_Prune; +} + +/* +** Convert every pAggInfo->aFunc[].pExpr such that any node within +** those expressions that has pAppInfo set is changed into a TK_AGG_COLUMN +** opcode. +*/ +static void aggregateConvertIndexedExprRefToColumn(AggInfo *pAggInfo){ + int i; + Walker w; + memset(&w, 0, sizeof(w)); + w.xExprCallback = aggregateIdxEprRefToColCallback; + for(i=0; inFunc; i++){ + wx_sqlite3WalkExpr(&w, pAggInfo->aFunc[i].pFExpr); + } +} + + +/* +** Allocate a block of registers so that there is one register for each +** pAggInfo->aCol[] and pAggInfo->aFunc[] entry in pAggInfo. The first +** register in this block is stored in pAggInfo->iFirstReg. +** +** This routine may only be called once for each AggInfo object. Prior +** to calling this routine: +** +** * The aCol[] and aFunc[] arrays may be modified +** * The AggInfoColumnReg() and AggInfoFuncReg() macros may not be used +** +** After clling this routine: +** +** * The aCol[] and aFunc[] arrays are fixed +** * The AggInfoColumnReg() and AggInfoFuncReg() macros may be used +** +*/ +static void assignAggregateRegisters(Parse *pParse, AggInfo *pAggInfo){ + assert( pAggInfo!=0 ); + assert( pAggInfo->iFirstReg==0 ); + pAggInfo->iFirstReg = pParse->nMem + 1; + pParse->nMem += pAggInfo->nColumn + pAggInfo->nFunc; +} + /* ** Reset the aggregate accumulator. ** @@ -136845,34 +144869,27 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ int i; struct AggInfo_func *pFunc; int nReg = pAggInfo->nFunc + pAggInfo->nColumn; + assert( pAggInfo->iFirstReg>0 ); + assert( pParse->db->pParse==pParse ); + assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); if( nReg==0 ) return; - if( pParse->nErr || pParse->db->mallocFailed ) return; -#ifdef SQLITE_DEBUG - /* Verify that all AggInfo registers are within the range specified by - ** AggInfo.mnReg..AggInfo.mxReg */ - assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 ); - for(i=0; inColumn; i++){ - assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg - && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg ); - } - for(i=0; inFunc; i++){ - assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg - && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg ); - } -#endif - wx_sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg); + if( pParse->nErr ) return; + wx_sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->iFirstReg, + pAggInfo->iFirstReg+nReg-1); for(pFunc=pAggInfo->aFunc, i=0; inFunc; i++, pFunc++){ if( pFunc->iDistinct>=0 ){ Expr *pE = pFunc->pFExpr; - assert( !ExprHasProperty(pE, EP_xIsSelect) ); + assert( ExprUseXList(pE) ); if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ wx_sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " "argument"); pFunc->iDistinct = -1; }else{ KeyInfo *pKeyInfo = wx_sqlite3KeyInfoFromExprList(pParse, pE->x.pList,0,0); - wx_sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, - (char*)pKeyInfo, P4_KEYINFO); + pFunc->iDistAddr = wx_sqlite3VdbeAddOp4(v, OP_OpenEphemeral, + pFunc->iDistinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO); + ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(DISTINCT)", + pFunc->pFunc->zName)); } } } @@ -136887,24 +144904,31 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ int i; struct AggInfo_func *pF; for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ - ExprList *pList = pF->pFExpr->x.pList; - assert( !ExprHasProperty(pF->pFExpr, EP_xIsSelect) ); - wx_sqlite3VdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0); + ExprList *pList; + assert( ExprUseXList(pF->pFExpr) ); + pList = pF->pFExpr->x.pList; + wx_sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i), + pList ? pList->nExpr : 0); wx_sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); } } /* -** Update the accumulator memory cells for an aggregate based on -** the current cursor position. +** Generate code that will update the accumulator memory cells for an +** aggregate based on the current cursor position. ** ** If regAcc is non-zero and there are no min() or max() aggregates ** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator ** registers if register regAcc contains 0. The caller will take care ** of setting and clearing regAcc. */ -static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ +static void updateAccumulator( + Parse *pParse, + int regAcc, + AggInfo *pAggInfo, + int eDistinctType +){ Vdbe *v = pParse->pVdbe; int i; int regHit = 0; @@ -136912,14 +144936,17 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ struct AggInfo_func *pF; struct AggInfo_col *pC; + assert( pAggInfo->iFirstReg>0 ); + if( pParse->nErr ) return; pAggInfo->directMode = 1; for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ int nArg; int addrNext = 0; int regAgg; - ExprList *pList = pF->pFExpr->x.pList; - assert( !ExprHasProperty(pF->pFExpr, EP_xIsSelect) ); + ExprList *pList; + assert( ExprUseXList(pF->pFExpr) ); assert( !IsWindowFunc(pF->pFExpr) ); + pList = pF->pFExpr->x.pList; if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){ Expr *pFilter = pF->pFExpr->y.pWin->pFilter; if( pAggInfo->nAccumulator @@ -136950,13 +144977,12 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ nArg = 0; regAgg = 0; } - if( pF->iDistinct>=0 ){ + if( pF->iDistinct>=0 && pList ){ if( addrNext==0 ){ addrNext = wx_sqlite3VdbeMakeLabel(pParse); } - testcase( nArg==0 ); /* Error condition */ - testcase( nArg>1 ); /* Also an error */ - codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg); + pF->iDistinct = codeDistinct(pParse, eDistinctType, + pF->iDistinct, addrNext, pList, regAgg); } if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ CollSeq *pColl = 0; @@ -136972,7 +144998,7 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem; wx_sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ); } - wx_sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, pF->iMem); + wx_sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); wx_sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); wx_sqlite3VdbeChangeP5(v, (u8)nArg); wx_sqlite3ReleaseTempRange(pParse, regAgg, nArg); @@ -136987,7 +145013,7 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ addrHitTest = wx_sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v); } for(i=0, pC=pAggInfo->aCol; inAccumulator; i++, pC++){ - wx_sqlite3ExprCode(pParse, pC->pCExpr, pC->iMem); + wx_sqlite3ExprCode(pParse, pC->pCExpr, AggInfoColumnReg(pAggInfo,i)); } pAggInfo->directMode = 0; @@ -137008,7 +145034,7 @@ static void explainSimpleCount( ){ if( pParse->explain==2 ){ int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx))); - wx_sqlite3VdbeExplain(pParse, 0, "SCAN TABLE %s%s%s", + wx_sqlite3VdbeExplain(pParse, 0, "SCAN %s%s%s", pTab->zName, bCover ? " USING COVERING INDEX " : "", bCover ? pIdx->zName : "" @@ -137033,8 +145059,16 @@ static void explainSimpleCount( static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){ if( pExpr->op!=TK_AND ){ Select *pS = pWalker->u.pSelect; + /* This routine is called before the HAVING clause of the current + ** SELECT is analyzed for aggregates. So if pExpr->pAggInfo is set + ** here, it indicates that the expression is a correlated reference to a + ** column from an outer aggregate query, or an aggregate function that + ** belongs to an outer query. Do not move the expression to the WHERE + ** clause in this obscure case, as doing so may corrupt the outer Select + ** statements AggInfo structure. */ if( wx_sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) && ExprAlwaysFalse(pExpr)==0 + && pExpr->pAggInfo==0 ){ wx_sqlite3 *db = pWalker->pParse->db; Expr *pNew = wx_sqlite3Expr(db, TK_INTEGER, "1"); @@ -137073,28 +145107,33 @@ static void havingToWhere(Parse *pParse, Select *p){ sWalker.xExprCallback = havingToWhereExprCb; sWalker.u.pSelect = p; wx_sqlite3WalkExpr(&sWalker, p->pHaving); -#if SELECTTRACE_ENABLED - if( sWalker.eCode && (wx_sqlite3SelectTrace & 0x100)!=0 ){ - SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); +#if TREETRACE_ENABLED + if( sWalker.eCode && (wx_sqlite3TreeTrace & 0x100)!=0 ){ + TREETRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); wx_sqlite3TreeViewSelect(0, p, 0); } #endif } /* -** Check to see if the pThis entry of pTabList is a self-join of a prior view. -** If it is, then return the SrcList_item for the prior view. If it is not, -** then return 0. +** Check to see if the pThis entry of pTabList is a self-join of another view. +** Search FROM-clause entries in the range of iFirst..iEnd, including iFirst +** but stopping before iEnd. +** +** If pThis is a self-join, then return the SrcItem for the first other +** instance of that view found. If pThis is not a self-join then return 0. */ static SrcItem *isSelfJoinView( SrcList *pTabList, /* Search for self-joins in this FROM clause */ - SrcItem *pThis /* Search for prior reference to this subquery */ + SrcItem *pThis, /* Search for prior reference to this subquery */ + int iFirst, int iEnd /* Range of FROM-clause entries to search. */ ){ SrcItem *pItem; assert( pThis->pSelect!=0 ); if( pThis->pSelect->selFlags & SF_PushDown ) return 0; - for(pItem = pTabList->a; pItema[iFirst++]; if( pItem->pSelect==0 ) continue; if( pItem->fg.viaCoroutine ) continue; if( pItem->zName==0 ) continue; @@ -137156,14 +145195,19 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ if( p->pEList->nExpr!=1 ) return 0; /* Single result column */ if( p->pWhere ) return 0; if( p->pGroupBy ) return 0; + if( p->pOrderBy ) return 0; pExpr = p->pEList->a[0].pExpr; if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */ + assert( ExprUseUToken(pExpr) ); if( wx_sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Is count() */ + assert( ExprUseXList(pExpr) ); if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */ if( p->pSrc->nSrc!=1 ) return 0; /* One table in FROM */ + if( ExprHasProperty(pExpr, EP_WinFunc) ) return 0;/* Not a window function */ pSub = p->pSrc->a[0].pSelect; if( pSub==0 ) return 0; /* The FROM is a subquery */ - if( pSub->pPrior==0 ) return 0; /* Must be a compound ry */ + if( pSub->pPrior==0 ) return 0; /* Must be a compound */ + if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */ do{ if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */ if( pSub->pWhere ) return 0; /* No WHERE clause */ @@ -137204,9 +145248,9 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ p->pEList->a[0].pExpr = pExpr; p->selFlags &= ~SF_Aggregate; -#if SELECTTRACE_ENABLED - if( wx_sqlite3SelectTrace & 0x400 ){ - SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x200 ){ + TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n")); wx_sqlite3TreeViewSelect(0, p, 0); } #endif @@ -137214,6 +145258,91 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ } #endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */ +/* +** If any term of pSrc, or any SF_NestedFrom sub-query, is not the same +** as pSrcItem but has the same alias as p0, then return true. +** Otherwise return false. +*/ +static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){ + int i; + for(i=0; inSrc; i++){ + SrcItem *p1 = &pSrc->a[i]; + if( p1==p0 ) continue; + if( p0->pTab==p1->pTab && 0==wx_sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ + return 1; + } + if( p1->pSelect + && (p1->pSelect->selFlags & SF_NestedFrom)!=0 + && sameSrcAlias(p0, p1->pSelect->pSrc) + ){ + return 1; + } + } + return 0; +} + +/* +** Return TRUE (non-zero) if the i-th entry in the pTabList SrcList can +** be implemented as a co-routine. The i-th entry is guaranteed to be +** a subquery. +** +** The subquery is implemented as a co-routine if all of the following are +** true: +** +** (1) The subquery will likely be implemented in the outer loop of +** the query. This will be the case if any one of the following +** conditions hold: +** (a) The subquery is the only term in the FROM clause +** (b) The subquery is the left-most term and a CROSS JOIN or similar +** requires it to be the outer loop +** (c) All of the following are true: +** (i) The subquery is the left-most subquery in the FROM clause +** (ii) There is nothing that would prevent the subquery from +** being used as the outer loop if the wx_sqlite3WhereBegin() +** routine nominates it to that position. +** (iii) The query is not a UPDATE ... FROM +** (2) The subquery is not a CTE that should be materialized because +** (a) the AS MATERIALIZED keyword is used, or +** (b) the CTE is used multiple times and does not have the +** NOT MATERIALIZED keyword +** (3) The subquery is not part of a left operand for a RIGHT JOIN +** (4) The SQLITE_Coroutine optimization disable flag is not set +** (5) The subquery is not self-joined +*/ +static int fromClauseTermCanBeCoroutine( + Parse *pParse, /* Parsing context */ + SrcList *pTabList, /* FROM clause */ + int i, /* Which term of the FROM clause holds the subquery */ + int selFlags /* Flags on the SELECT statement */ +){ + SrcItem *pItem = &pTabList->a[i]; + if( pItem->fg.isCte ){ + const CteUse *pCteUse = pItem->u2.pCteUse; + if( pCteUse->eM10d==M10d_Yes ) return 0; /* (2a) */ + if( pCteUse->nUse>=2 && pCteUse->eM10d!=M10d_No ) return 0; /* (2b) */ + } + if( pTabList->a[0].fg.jointype & JT_LTORJ ) return 0; /* (3) */ + if( OptimizationDisabled(pParse->db, SQLITE_Coroutines) ) return 0; /* (4) */ + if( isSelfJoinView(pTabList, pItem, i+1, pTabList->nSrc)!=0 ){ + return 0; /* (5) */ + } + if( i==0 ){ + if( pTabList->nSrc==1 ) return 1; /* (1a) */ + if( pTabList->a[1].fg.jointype & JT_CROSS ) return 1; /* (1b) */ + if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ + return 1; + } + if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ + while( 1 /*exit-by-break*/ ){ + if( pItem->fg.jointype & (JT_OUTER|JT_CROSS) ) return 0; /* (1c-ii) */ + if( i==0 ) break; + i--; + pItem--; + if( pItem->pSelect!=0 ) return 0; /* (1c-i) */ + } + return 1; +} + /* ** Generate code for the SELECT statement given in the p argument. ** @@ -137251,15 +145380,21 @@ SQLITE_PRIVATE int wx_sqlite3Select( u8 minMaxFlag; /* Flag for min/max queries */ db = pParse->db; + assert( pParse==db->pParse ); v = wx_sqlite3GetVdbe(pParse); - if( p==0 || db->mallocFailed || pParse->nErr ){ + if( p==0 || pParse->nErr ){ return 1; } + assert( db->mallocFailed==0 ); if( wx_sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; -#if SELECTTRACE_ENABLED - SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); - if( wx_sqlite3SelectTrace & 0x100 ){ - wx_sqlite3TreeViewSelect(0, p, 0); +#if TREETRACE_ENABLED + TREETRACE(0x1,pParse,p, ("begin processing:\n", pParse->addrExplain)); + if( wx_sqlite3TreeTrace & 0x10000 ){ + if( (wx_sqlite3TreeTrace & 0x10001)==0x10000 ){ + wx_sqlite3TreeViewLine(0, "In wx_sqlite3Select() at %s:%d", + __FILE__, __LINE__); + } + wx_sqlite3ShowSelect(p); } #endif @@ -137273,9 +145408,9 @@ SQLITE_PRIVATE int wx_sqlite3Select( pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo ); /* All of these destinations are also able to ignore the ORDER BY clause */ if( p->pOrderBy ){ -#if SELECTTRACE_ENABLED - SELECTTRACE(1,pParse,p, ("dropping superfluous ORDER BY:\n")); - if( wx_sqlite3SelectTrace & 0x100 ){ +#if TREETRACE_ENABLED + TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n")); + if( wx_sqlite3TreeTrace & 0x800 ){ wx_sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY"); } #endif @@ -137289,48 +145424,56 @@ SQLITE_PRIVATE int wx_sqlite3Select( p->selFlags |= SF_NoopOrderBy; } wx_sqlite3SelectPrep(pParse, p, 0); - if( pParse->nErr || db->mallocFailed ){ + if( pParse->nErr ){ goto select_end; } + assert( db->mallocFailed==0 ); assert( p->pEList!=0 ); -#if SELECTTRACE_ENABLED - if( wx_sqlite3SelectTrace & 0x104 ){ - SELECTTRACE(0x104,pParse,p, ("after name resolution:\n")); +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x10 ){ + TREETRACE(0x10,pParse,p, ("after name resolution:\n")); wx_sqlite3TreeViewSelect(0, p, 0); } #endif - /* If the SF_UpdateFrom flag is set, then this function is being called + /* If the SF_UFSrcCheck flag is set, then this function is being called ** as part of populating the temp table for an UPDATE...FROM statement. ** In this case, it is an error if the target object (pSrc->a[0]) name - ** or alias is duplicated within FROM clause (pSrc->a[1..n]). */ - if( p->selFlags & SF_UpdateFrom ){ + ** or alias is duplicated within FROM clause (pSrc->a[1..n]). + ** + ** Postgres disallows this case too. The reason is that some other + ** systems handle this case differently, and not all the same way, + ** which is just confusing. To avoid this, we follow PG's lead and + ** disallow it altogether. */ + if( p->selFlags & SF_UFSrcCheck ){ SrcItem *p0 = &p->pSrc->a[0]; - for(i=1; ipSrc->nSrc; i++){ - SrcItem *p1 = &p->pSrc->a[i]; - if( p0->pTab==p1->pTab && 0==wx_sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ - wx_sqlite3ErrorMsg(pParse, - "target object/alias may not appear in FROM clause: %s", - p0->zAlias ? p0->zAlias : p0->pTab->zName - ); - goto select_end; - } + if( sameSrcAlias(p0, p->pSrc) ){ + wx_sqlite3ErrorMsg(pParse, + "target object/alias may not appear in FROM clause: %s", + p0->zAlias ? p0->zAlias : p0->pTab->zName + ); + goto select_end; } + + /* Clear the SF_UFSrcCheck flag. The check has already been performed, + ** and leaving this flag set can cause errors if a compound sub-query + ** in p->pSrc is flattened into this query and this function called + ** again as part of compound SELECT processing. */ + p->selFlags &= ~SF_UFSrcCheck; } if( pDest->eDest==SRT_Output ){ - generateColumnNames(pParse, p); + wx_sqlite3GenerateColumnNames(pParse, p); } #ifndef SQLITE_OMIT_WINDOWFUNC - rc = wx_sqlite3WindowRewrite(pParse, p); - if( rc ){ - assert( db->mallocFailed || pParse->nErr>0 ); + if( wx_sqlite3WindowRewrite(pParse, p) ){ + assert( pParse->nErr ); goto select_end; } -#if SELECTTRACE_ENABLED - if( p->pWin && (wx_sqlite3SelectTrace & 0x108)!=0 ){ - SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n")); +#if TREETRACE_ENABLED + if( p->pWin && (wx_sqlite3TreeTrace & 0x40)!=0 ){ + TREETRACE(0x40,pParse,p, ("after window rewrite:\n")); wx_sqlite3TreeViewSelect(0, p, 0); } #endif @@ -137357,14 +145500,16 @@ SQLITE_PRIVATE int wx_sqlite3Select( /* Convert LEFT JOIN into JOIN if there are terms of the right table ** of the LEFT JOIN used in the WHERE clause. */ - if( (pItem->fg.jointype & JT_LEFT)!=0 + if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==JT_LEFT && wx_sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor) && OptimizationEnabled(db, SQLITE_SimplifyJoin) ){ - SELECTTRACE(0x100,pParse,p, + TREETRACE(0x1000,pParse,p, ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); - unsetJoinExpr(p->pWhere, pItem->iCursor); + assert( pItem->iCursor>=0 ); + unsetJoinExpr(p->pWhere, pItem->iCursor, + pTabList->a[0].fg.jointype & JT_LTORJ); } /* No futher action if this term of the FROM clause is no a subquery */ @@ -137388,6 +145533,41 @@ SQLITE_PRIVATE int wx_sqlite3Select( if( (pSub->selFlags & SF_Aggregate)!=0 ) continue; assert( pSub->pGroupBy==0 ); + /* If a FROM-clause subquery has an ORDER BY clause that is not + ** really doing anything, then delete it now so that it does not + ** interfere with query flattening. See the discussion at + ** https://sqlite.org/forum/forumpost/2d76f2bcf65d256a + ** + ** Beware of these cases where the ORDER BY clause may not be safely + ** omitted: + ** + ** (1) There is also a LIMIT clause + ** (2) The subquery was added to help with window-function + ** processing + ** (3) The subquery is in the FROM clause of an UPDATE + ** (4) The outer query uses an aggregate function other than + ** the built-in count(), min(), or max(). + ** (5) The ORDER BY isn't going to accomplish anything because + ** one of: + ** (a) The outer query has a different ORDER BY clause + ** (b) The subquery is part of a join + ** See forum post 062d576715d277c8 + */ + if( pSub->pOrderBy!=0 + && (p->pOrderBy!=0 || pTabList->nSrc>1) /* Condition (5) */ + && pSub->pLimit==0 /* Condition (1) */ + && (pSub->selFlags & SF_OrderByReqd)==0 /* Condition (2) */ + && (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */ + && OptimizationEnabled(db, SQLITE_OmitOrderBy) + ){ + TREETRACE(0x800,pParse,p, + ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1)); + wx_sqlite3ParserAddCleanup(pParse, + (void(*)(wx_sqlite3*,void*))wx_sqlite3ExprListDelete, + pSub->pOrderBy); + pSub->pOrderBy = 0; + } + /* If the outer query contains a "complex" result set (that is, ** if the result set of the outer query uses functions or subqueries) ** and if the subquery contains an ORDER BY clause and if @@ -137410,7 +145590,7 @@ SQLITE_PRIVATE int wx_sqlite3Select( && i==0 && (p->selFlags & SF_ComplexResult)!=0 && (pTabList->nSrc==1 - || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) + || (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0) ){ continue; } @@ -137434,9 +145614,9 @@ SQLITE_PRIVATE int wx_sqlite3Select( */ if( p->pPrior ){ rc = multiSelect(pParse, p, pDest); -#if SELECTTRACE_ENABLED - SELECTTRACE(0x1,pParse,p,("end compound-select processing\n")); - if( (wx_sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ +#if TREETRACE_ENABLED + TREETRACE(0x400,pParse,p,("end compound-select processing\n")); + if( (wx_sqlite3TreeTrace & 0x400)!=0 && ExplainQueryPlanParent(pParse)==0 ){ wx_sqlite3TreeViewSelect(0, p, 0); } #endif @@ -137450,18 +145630,19 @@ SQLITE_PRIVATE int wx_sqlite3Select( ** as the equivalent optimization will be handled by query planner in ** wx_sqlite3WhereBegin(). */ - if( pTabList->nSrc>1 + if( p->pWhere!=0 + && p->pWhere->op==TK_AND && OptimizationEnabled(db, SQLITE_PropagateConst) && propagateConstants(pParse, p) ){ -#if SELECTTRACE_ENABLED - if( wx_sqlite3SelectTrace & 0x100 ){ - SELECTTRACE(0x100,pParse,p,("After constant propagation:\n")); +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x2000 ){ + TREETRACE(0x2000,pParse,p,("After constant propagation:\n")); wx_sqlite3TreeViewSelect(0, p, 0); } #endif }else{ - SELECTTRACE(0x100,pParse,p,("Constant propagation not helpful\n")); + TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n")); } #ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION @@ -137469,7 +145650,6 @@ SQLITE_PRIVATE int wx_sqlite3Select( && countOfViewOptimization(pParse, p) ){ if( db->mallocFailed ) goto select_end; - pEList = p->pEList; pTabList = p->pSrc; } #endif @@ -137513,19 +145693,8 @@ SQLITE_PRIVATE int wx_sqlite3Select( pSub = pItem->pSelect; if( pSub==0 ) continue; - /* The code for a subquery should only be generated once, though it is - ** technically harmless for it to be generated multiple times. The - ** following assert() will detect if something changes to cause - ** the same subquery to be coded multiple times, as a signal to the - ** developers to try to optimize the situation. - ** - ** Update 2019-07-24: - ** See ticket https://sqlite.org/src/tktview/c52b09c7f38903b1311cec40. - ** The dbsqlfuzz fuzzer found a case where the same subquery gets - ** coded twice. So this assert() now becomes a testcase(). It should - ** be very rare, though. - */ - testcase( pItem->addrFillSub!=0 ); + /* The code for a subquery should only be generated once. */ + assert( pItem->addrFillSub==0 ); /* Increment Parse.nHeight by the height of the largest expression ** tree referred to by this, the parent select. The child select @@ -137540,40 +145709,28 @@ SQLITE_PRIVATE int wx_sqlite3Select( ** inside the subquery. This can help the subquery to run more efficiently. */ if( OptimizationEnabled(db, SQLITE_PushDown) - && (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) - && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, - (pItem->fg.jointype & JT_OUTER)!=0) + && (pItem->fg.isCte==0 + || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2)) + && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem) ){ -#if SELECTTRACE_ENABLED - if( wx_sqlite3SelectTrace & 0x100 ){ - SELECTTRACE(0x100,pParse,p, +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x4000 ){ + TREETRACE(0x4000,pParse,p, ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); wx_sqlite3TreeViewSelect(0, p, 0); } #endif assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 ); }else{ - SELECTTRACE(0x100,pParse,p,("Push-down not possible\n")); + TREETRACE(0x4000,pParse,p,("Push-down not possible\n")); } zSavedAuthContext = pParse->zAuthContext; pParse->zAuthContext = pItem->zName; /* Generate code to implement the subquery - ** - ** The subquery is implemented as a co-routine if: - ** (1) the subquery is guaranteed to be the outer loop (so that - ** it does not need to be computed more than once), and - ** (2) the subquery is not a CTE that should be materialized - ** - ** TODO: Are there other reasons beside (1) and (2) to use a co-routine - ** implementation? */ - if( i==0 - && (pTabList->nSrc==1 - || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */ - && (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) /* (2) */ - ){ + if( fromClauseTermCanBeCoroutine(pParse, pTabList, i, p->selFlags) ){ /* Implement a co-routine that will return a single row of the result ** set on each invocation. */ @@ -137581,10 +145738,10 @@ SQLITE_PRIVATE int wx_sqlite3Select( pItem->regReturn = ++pParse->nMem; wx_sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop); - VdbeComment((v, "%s", pItem->pTab->zName)); + VdbeComment((v, "%!S", pItem)); pItem->addrFillSub = addrTop; wx_sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); - ExplainQueryPlan((pParse, 1, "CO-ROUTINE %u", pSub->selId)); + ExplainQueryPlan((pParse, 1, "CO-ROUTINE %!S", pItem)); wx_sqlite3Select(pParse, pSub, &dest); pItem->pTab->nRowLogEst = pSub->nSelectRow; pItem->fg.viaCoroutine = 1; @@ -137601,9 +145758,10 @@ SQLITE_PRIVATE int wx_sqlite3Select( wx_sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e); if( pItem->iCursor!=pCteUse->iCur ){ wx_sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pCteUse->iCur); + VdbeComment((v, "%!S", pItem)); } pSub->nSelectRow = pCteUse->nRowEst; - }else if( (pPrior = isSelfJoinView(pTabList, pItem))!=0 ){ + }else if( (pPrior = isSelfJoinView(pTabList, pItem, 0, i))!=0 ){ /* This view has already been materialized by a prior entry in ** this same FROM clause. Reuse it. */ if( pPrior->addrFillSub ){ @@ -137612,34 +145770,38 @@ SQLITE_PRIVATE int wx_sqlite3Select( wx_sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); pSub->nSelectRow = pPrior->pSelect->nSelectRow; }else{ - /* Materalize the view. If the view is not correlated, generate a + /* Materialize the view. If the view is not correlated, generate a ** subroutine to do the materialization so that subsequent uses of ** the same view can reuse the materialization. */ int topAddr; int onceAddr = 0; - int retAddr; +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + int addrExplain; +#endif - testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */ pItem->regReturn = ++pParse->nMem; - topAddr = wx_sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn); + topAddr = wx_sqlite3VdbeAddOp0(v, OP_Goto); pItem->addrFillSub = topAddr+1; + pItem->fg.isMaterialized = 1; if( pItem->fg.isCorrelated==0 ){ /* If the subquery is not correlated and if we are not inside of ** a trigger, then we only need to compute the value of the subquery ** once. */ onceAddr = wx_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); - VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName)); + VdbeComment((v, "materialize %!S", pItem)); }else{ - VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName)); + VdbeNoopComment((v, "materialize %!S", pItem)); } wx_sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); - ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId)); + + ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem)); wx_sqlite3Select(pParse, pSub, &dest); pItem->pTab->nRowLogEst = pSub->nSelectRow; if( onceAddr ) wx_sqlite3VdbeJumpHere(v, onceAddr); - retAddr = wx_sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); - VdbeComment((v, "end %s", pItem->pTab->zName)); - wx_sqlite3VdbeChangeP1(v, topAddr, retAddr); + wx_sqlite3VdbeAddOp2(v, OP_Return, pItem->regReturn, topAddr+1); + VdbeComment((v, "end %!S", pItem)); + wx_sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); + wx_sqlite3VdbeJumpHere(v, topAddr); wx_sqlite3ClearTempRegCache(pParse); if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ CteUse *pCteUse = pItem->u2.pCteUse; @@ -137663,9 +145825,9 @@ SQLITE_PRIVATE int wx_sqlite3Select( pHaving = p->pHaving; sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; -#if SELECTTRACE_ENABLED - if( wx_sqlite3SelectTrace & 0x400 ){ - SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n")); +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x8000 ){ + TREETRACE(0x8000,pParse,p,("After all FROM-clause analysis:\n")); wx_sqlite3TreeViewSelect(0, p, 0); } #endif @@ -137698,10 +145860,11 @@ SQLITE_PRIVATE int wx_sqlite3Select( ** the sDistinct.isTnct is still set. Hence, isTnct represents the ** original setting of the SF_Distinct flag, not the current setting */ assert( sDistinct.isTnct ); + sDistinct.isTnct = 2; -#if SELECTTRACE_ENABLED - if( wx_sqlite3SelectTrace & 0x400 ){ - SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n")); +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x20000 ){ + TREETRACE(0x20000,pParse,p,("Transform DISTINCT into GROUP BY:\n")); wx_sqlite3TreeViewSelect(0, p, 0); } #endif @@ -137733,6 +145896,18 @@ SQLITE_PRIVATE int wx_sqlite3Select( */ if( pDest->eDest==SRT_EphemTab ){ wx_sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr); + if( p->selFlags & SF_NestedFrom ){ + /* Delete or NULL-out result columns that will never be used */ + int ii; + for(ii=pEList->nExpr-1; ii>0 && pEList->a[ii].fg.bUsed==0; ii--){ + wx_sqlite3ExprDelete(db, pEList->a[ii].pExpr); + wx_sqlite3DbFree(db, pEList->a[ii].zEName); + pEList->nExpr--; + } + for(ii=0; iinExpr; ii++){ + if( pEList->a[ii].fg.bUsed==0 ) pEList->a[ii].pExpr->op = TK_NULL; + } + } } /* Set the limiter. @@ -137741,7 +145916,7 @@ SQLITE_PRIVATE int wx_sqlite3Select( if( (p->selFlags & SF_FixedLimit)==0 ){ p->nSelectRow = 320; /* 4 billion rows */ } - computeLimitRegisters(pParse, p, iEnd); + if( p->pLimit ) computeLimitRegisters(pParse, p, iEnd); if( p->iLimit==0 && sSort.addrSortIndex>=0 ){ wx_sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen); sSort.sortFlags |= SORTFLAG_UseSorter; @@ -137775,9 +145950,9 @@ SQLITE_PRIVATE int wx_sqlite3Select( /* Begin the database scan. */ - SELECTTRACE(1,pParse,p,("WhereBegin\n")); + TREETRACE(0x2,pParse,p,("WhereBegin\n")); pWInfo = wx_sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy, - p->pEList, wctrlFlags, p->nSelectRow); + p->pEList, p, wctrlFlags, p->nSelectRow); if( pWInfo==0 ) goto select_end; if( wx_sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){ p->nSelectRow = wx_sqlite3WhereOutputRowCount(pWInfo); @@ -137792,7 +145967,7 @@ SQLITE_PRIVATE int wx_sqlite3Select( sSort.pOrderBy = 0; } } - SELECTTRACE(1,pParse,p,("WhereBegin returns\n")); + TREETRACE(0x2,pParse,p,("WhereBegin returns\n")); /* If sorting index that was created by a prior OP_OpenEphemeral ** instruction ended up not being needed, then change the OP_OpenEphemeral @@ -137831,7 +146006,7 @@ SQLITE_PRIVATE int wx_sqlite3Select( /* End the database scan loop. */ - SELECTTRACE(1,pParse,p,("WhereEnd\n")); + TREETRACE(0x2,pParse,p,("WhereEnd\n")); wx_sqlite3WhereEnd(pWInfo); } }else{ @@ -137882,8 +146057,9 @@ SQLITE_PRIVATE int wx_sqlite3Select( ** ORDER BY to maximize the chances of rows being delivered in an ** order that makes the ORDER BY redundant. */ for(ii=0; iinExpr; ii++){ - u8 sortFlags = sSort.pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_DESC; - pGroupBy->a[ii].sortFlags = sortFlags; + u8 sortFlags; + sortFlags = sSort.pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC; + pGroupBy->a[ii].fg.sortFlags = sortFlags; } if( wx_sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){ orderByGrp = 1; @@ -137911,12 +146087,14 @@ SQLITE_PRIVATE int wx_sqlite3Select( goto select_end; } pAggInfo->selId = p->selId; +#ifdef SQLITE_DEBUG + pAggInfo->pSelect = p; +#endif memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; sNC.pSrcList = pTabList; sNC.uNC.pAggInfo = pAggInfo; VVA_ONLY( sNC.ncFlags = NC_UAggInfo; ) - pAggInfo->mnReg = pParse->nMem+1; pAggInfo->nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0; pAggInfo->pGroupBy = pGroupBy; wx_sqlite3ExprAnalyzeAggList(&sNC, pEList); @@ -137937,40 +146115,17 @@ SQLITE_PRIVATE int wx_sqlite3Select( }else{ minMaxFlag = WHERE_ORDERBY_NORMAL; } - for(i=0; inFunc; i++){ - Expr *pExpr = pAggInfo->aFunc[i].pFExpr; - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - sNC.ncFlags |= NC_InAggFunc; - wx_sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList); -#ifndef SQLITE_OMIT_WINDOWFUNC - assert( !IsWindowFunc(pExpr) ); - if( ExprHasProperty(pExpr, EP_WinFunc) ){ - wx_sqlite3ExprAnalyzeAggregates(&sNC, pExpr->y.pWin->pFilter); - } -#endif - sNC.ncFlags &= ~NC_InAggFunc; - } - pAggInfo->mxReg = pParse->nMem; + analyzeAggFuncArgs(pAggInfo, &sNC); if( db->mallocFailed ) goto select_end; -#if SELECTTRACE_ENABLED - if( wx_sqlite3SelectTrace & 0x400 ){ - int ii; - SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x20 ){ + TREETRACE(0x20,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); wx_sqlite3TreeViewSelect(0, p, 0); if( minMaxFlag ){ wx_sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag); wx_sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY"); } - for(ii=0; iinColumn; ii++){ - wx_sqlite3DebugPrintf("agg-column[%d] iMem=%d\n", - ii, pAggInfo->aCol[ii].iMem); - wx_sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0); - } - for(ii=0; iinFunc; ii++){ - wx_sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n", - ii, pAggInfo->aFunc[ii].iMem); - wx_sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0); - } + printAggInfo(pAggInfo); } #endif @@ -137988,6 +146143,22 @@ SQLITE_PRIVATE int wx_sqlite3Select( int addrSortingIdx; /* The OP_OpenEphemeral for the sorting index */ int addrReset; /* Subroutine for resetting the accumulator */ int regReset; /* Return address register for reset subroutine */ + ExprList *pDistinct = 0; + u16 distFlag = 0; + int eDist = WHERE_DISTINCT_NOOP; + + if( pAggInfo->nFunc==1 + && pAggInfo->aFunc[0].iDistinct>=0 + && ALWAYS(pAggInfo->aFunc[0].pFExpr!=0) + && ALWAYS(ExprUseXList(pAggInfo->aFunc[0].pFExpr)) + && pAggInfo->aFunc[0].pFExpr->x.pList!=0 + ){ + Expr *pExpr = pAggInfo->aFunc[0].pFExpr->x.pList->a[0].pExpr; + pExpr = wx_sqlite3ExprDup(db, pExpr, 0); + pDistinct = wx_sqlite3ExprListDup(db, pGroupBy, 0); + pDistinct = wx_sqlite3ExprListAppend(pParse, pDistinct, pExpr); + distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0; + } /* If there is a GROUP BY clause we might need a sorting index to ** implement it. Allocate that sorting index now. If it turns out @@ -138023,12 +146194,21 @@ SQLITE_PRIVATE int wx_sqlite3Select( ** in the right order to begin with. */ wx_sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); - SELECTTRACE(1,pParse,p,("WhereBegin\n")); - pWInfo = wx_sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, - WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0 + TREETRACE(0x2,pParse,p,("WhereBegin\n")); + pWInfo = wx_sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct, + p, (sDistinct.isTnct==2 ? WHERE_DISTINCTBY : WHERE_GROUPBY) + | (orderByGrp ? WHERE_SORTBYGROUP : 0) | distFlag, 0 ); - if( pWInfo==0 ) goto select_end; - SELECTTRACE(1,pParse,p,("WhereBegin returns\n")); + if( pWInfo==0 ){ + wx_sqlite3ExprListDelete(db, pDistinct); + goto select_end; + } + if( pParse->pIdxEpr ){ + optimizeAggregateUseOfIndexedExpr(pParse, p, pAggInfo, &sNC); + } + assignAggregateRegisters(pParse, pAggInfo); + eDist = wx_sqlite3WhereIsDistinct(pWInfo); + TREETRACE(0x2,pParse,p,("WhereBegin returns\n")); if( wx_sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){ /* The optimizer is able to deliver rows in group by order so ** we do not have to sort. The OP_OpenEphemeral table will be @@ -138063,21 +146243,21 @@ SQLITE_PRIVATE int wx_sqlite3Select( regBase = wx_sqlite3GetTempRange(pParse, nCol); wx_sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0); j = nGroupBy; + pAggInfo->directMode = 1; for(i=0; inColumn; i++){ struct AggInfo_col *pCol = &pAggInfo->aCol[i]; if( pCol->iSorterColumn>=j ){ - int r1 = j + regBase; - wx_sqlite3ExprCodeGetColumnOfTable(v, - pCol->pTab, pCol->iTable, pCol->iColumn, r1); + wx_sqlite3ExprCode(pParse, pCol->pCExpr, j + regBase); j++; } } + pAggInfo->directMode = 0; regRecord = wx_sqlite3GetTempReg(pParse); wx_sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord); wx_sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord); wx_sqlite3ReleaseTempReg(pParse, regRecord); wx_sqlite3ReleaseTempRange(pParse, regBase, nCol); - SELECTTRACE(1,pParse,p,("WhereEnd\n")); + TREETRACE(0x2,pParse,p,("WhereEnd\n")); wx_sqlite3WhereEnd(pWInfo); pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++; sortOut = wx_sqlite3GetTempReg(pParse); @@ -138087,6 +146267,23 @@ SQLITE_PRIVATE int wx_sqlite3Select( pAggInfo->useSortingIdx = 1; } + /* If there are entries in pAgggInfo->aFunc[] that contain subexpressions + ** that are indexed (and that were previously identified and tagged + ** in optimizeAggregateUseOfIndexedExpr()) then those subexpressions + ** must now be converted into a TK_AGG_COLUMN node so that the value + ** is correctly pulled from the index rather than being recomputed. */ + if( pParse->pIdxEpr ){ + aggregateConvertIndexedExprRefToColumn(pAggInfo); +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x20 ){ + TREETRACE(0x20, pParse, p, + ("AggInfo function expressions converted to reference index\n")); + wx_sqlite3TreeViewSelect(0, p, 0); + printAggInfo(pAggInfo); + } +#endif + } + /* If the index or temporary table used by the GROUP BY sort ** will naturally deliver rows in the order required by the ORDER BY ** clause, cancel the ephemeral table open coded earlier. @@ -138145,7 +146342,7 @@ SQLITE_PRIVATE int wx_sqlite3Select( ** the current row */ wx_sqlite3VdbeJumpHere(v, addr1); - updateAccumulator(pParse, iUseFlag, pAggInfo); + updateAccumulator(pParse, iUseFlag, pAggInfo, eDist); wx_sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag); VdbeComment((v, "indicate data in accumulator")); @@ -138155,10 +146352,11 @@ SQLITE_PRIVATE int wx_sqlite3Select( wx_sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop); VdbeCoverage(v); }else{ - SELECTTRACE(1,pParse,p,("WhereEnd\n")); + TREETRACE(0x2,pParse,p,("WhereEnd\n")); wx_sqlite3WhereEnd(pWInfo); wx_sqlite3VdbeChangeToNoop(v, addrSortingIdx); } + wx_sqlite3ExprListDelete(db, pDistinct); /* Output the final row of result */ @@ -138202,6 +146400,10 @@ SQLITE_PRIVATE int wx_sqlite3Select( VdbeComment((v, "indicate accumulator empty")); wx_sqlite3VdbeAddOp1(v, OP_Return, regReset); + if( distFlag!=0 && eDist!=WHERE_DISTINCT_NOOP ){ + struct AggInfo_func *pF = &pAggInfo->aFunc[0]; + fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); + } } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ else { Table *pTab; @@ -138260,11 +146462,15 @@ SQLITE_PRIVATE int wx_sqlite3Select( if( pKeyInfo ){ wx_sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); } - wx_sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem); + assignAggregateRegisters(pParse, pAggInfo); + wx_sqlite3VdbeAddOp2(v, OP_Count, iCsr, AggInfoFuncReg(pAggInfo,0)); wx_sqlite3VdbeAddOp1(v, OP_Close, iCsr); explainSimpleCount(pParse, pTab, pBest); }else{ int regAcc = 0; /* "populate accumulators" flag */ + ExprList *pDistinct = 0; + u16 distFlag = 0; + int eDist; /* If there are accumulator registers but no min() or max() functions ** without FILTER clauses, allocate register regAcc. Register regAcc @@ -138288,7 +146494,12 @@ SQLITE_PRIVATE int wx_sqlite3Select( regAcc = ++pParse->nMem; wx_sqlite3VdbeAddOp2(v, OP_Integer, 0, regAcc); } + }else if( pAggInfo->nFunc==1 && pAggInfo->aFunc[0].iDistinct>=0 ){ + assert( ExprUseXList(pAggInfo->aFunc[0].pFExpr) ); + pDistinct = pAggInfo->aFunc[0].pFExpr->x.pList; + distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0; } + assignAggregateRegisters(pParse, pAggInfo); /* This case runs if the aggregate has no GROUP BY clause. The ** processing is much simpler since there is only a single row @@ -138305,19 +146516,27 @@ SQLITE_PRIVATE int wx_sqlite3Select( assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 ); assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 ); - SELECTTRACE(1,pParse,p,("WhereBegin\n")); + TREETRACE(0x2,pParse,p,("WhereBegin\n")); pWInfo = wx_sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy, - 0, minMaxFlag, 0); + pDistinct, p, minMaxFlag|distFlag, 0); if( pWInfo==0 ){ goto select_end; } - SELECTTRACE(1,pParse,p,("WhereBegin returns\n")); - updateAccumulator(pParse, regAcc, pAggInfo); + TREETRACE(0x2,pParse,p,("WhereBegin returns\n")); + eDist = wx_sqlite3WhereIsDistinct(pWInfo); + updateAccumulator(pParse, regAcc, pAggInfo, eDist); + if( eDist!=WHERE_DISTINCT_NOOP ){ + struct AggInfo_func *pF = pAggInfo->aFunc; + if( pF ){ + fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); + } + } + if( regAcc ) wx_sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc); if( minMaxFlag ){ wx_sqlite3WhereMinMaxOptEarlyOut(v, pWInfo); } - SELECTTRACE(1,pParse,p,("WhereEnd\n")); + TREETRACE(0x2,pParse,p,("WhereEnd\n")); wx_sqlite3WhereEnd(pWInfo); finalizeAggFunctions(pParse, pAggInfo); } @@ -138339,8 +146558,6 @@ SQLITE_PRIVATE int wx_sqlite3Select( ** and send them to the callback one by one. */ if( sSort.pOrderBy ){ - explainTempTable(pParse, - sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY"); assert( p->pEList==pEList ); generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest); } @@ -138357,12 +146574,14 @@ SQLITE_PRIVATE int wx_sqlite3Select( ** successful coding of the SELECT. */ select_end: + assert( db->mallocFailed==0 || db->mallocFailed==1 ); + assert( db->mallocFailed==0 || pParse->nErr!=0 ); wx_sqlite3ExprListDelete(db, pMinMaxOrderBy); #ifdef SQLITE_DEBUG if( pAggInfo && !db->mallocFailed ){ for(i=0; inColumn; i++){ Expr *pExpr = pAggInfo->aCol[i].pCExpr; - assert( pExpr!=0 ); + if( pExpr==0 ) continue; assert( pExpr->pAggInfo==pAggInfo ); assert( pExpr->iAgg==i ); } @@ -138375,9 +146594,9 @@ select_end: } #endif -#if SELECTTRACE_ENABLED - SELECTTRACE(0x1,pParse,p,("end processing\n")); - if( (wx_sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ +#if TREETRACE_ENABLED + TREETRACE(0x1,pParse,p,("end processing\n")); + if( (wx_sqlite3TreeTrace & 0x40000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ wx_sqlite3TreeViewSelect(0, p, 0); } #endif @@ -138642,34 +146861,43 @@ SQLITE_PRIVATE Trigger *wx_sqlite3TriggerList(Parse *pParse, Table *pTab){ Trigger *pList; /* List of triggers to return */ HashElem *p; /* Loop variable for TEMP triggers */ - if( pParse->disableTriggers ){ - return 0; - } + assert( pParse->disableTriggers==0 ); pTmpSchema = pParse->db->aDb[1].pSchema; p = sqliteHashFirst(&pTmpSchema->trigHash); - if( p==0 ){ - return pTab->pTrigger; - } pList = pTab->pTrigger; - if( pTmpSchema!=pTab->pSchema ){ - while( p ){ - Trigger *pTrig = (Trigger *)sqliteHashData(p); - if( pTrig->pTabSchema==pTab->pSchema - && 0==wx_sqlite3StrICmp(pTrig->table, pTab->zName) - ){ - pTrig->pNext = pList; - pList = pTrig; - }else if( pTrig->op==TK_RETURNING ){ - assert( pParse->bReturning ); - assert( &(pParse->u1.pReturning->retTrig) == pTrig ); - pTrig->table = pTab->zName; - pTrig->pTabSchema = pTab->pSchema; - pTrig->pNext = pList; - pList = pTrig; - } - p = sqliteHashNext(p); + while( p ){ + Trigger *pTrig = (Trigger *)sqliteHashData(p); + if( pTrig->pTabSchema==pTab->pSchema + && pTrig->table + && 0==wx_sqlite3StrICmp(pTrig->table, pTab->zName) + && (pTrig->pTabSchema!=pTmpSchema || pTrig->bReturning) + ){ + pTrig->pNext = pList; + pList = pTrig; + }else if( pTrig->op==TK_RETURNING ){ +#ifndef SQLITE_OMIT_VIRTUALTABLE + assert( pParse->db->pVtabCtx==0 ); +#endif + assert( pParse->bReturning ); + assert( &(pParse->u1.pReturning->retTrig) == pTrig ); + pTrig->table = pTab->zName; + pTrig->pTabSchema = pTab->pSchema; + pTrig->pNext = pList; + pList = pTrig; + } + p = sqliteHashNext(p); + } +#if 0 + if( pList ){ + Trigger *pX; + printf("Triggers for %s:", pTab->zName); + for(pX=pList; pX; pX=pX->pNext){ + printf(" %s", pX->zName); } + printf("\n"); + fflush(stdout); } +#endif return pList; } @@ -138783,6 +147011,7 @@ SQLITE_PRIVATE void wx_sqlite3BeginTrigger( }else{ assert( !db->init.busy ); wx_sqlite3CodeVerifySchema(pParse, iDb); + VVA_ONLY( pParse->ifNotExists = 1; ) } goto trigger_cleanup; } @@ -138797,14 +147026,14 @@ SQLITE_PRIVATE void wx_sqlite3BeginTrigger( /* INSTEAD of triggers are only for views and views only support INSTEAD ** of triggers. */ - if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ + if( IsView(pTab) && tr_tm!=TK_INSTEAD ){ wx_sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", - (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); + (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName->a); goto trigger_orphan_error; } - if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ + if( !IsView(pTab) && tr_tm==TK_INSTEAD ){ wx_sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" - " trigger on table: %S", pTableName, 0); + " trigger on table: %S", pTableName->a); goto trigger_orphan_error; } @@ -138932,6 +147161,23 @@ SQLITE_PRIVATE void wx_sqlite3FinishTrigger( Vdbe *v; char *z; + /* If this is a new CREATE TABLE statement, and if shadow tables + ** are read-only, and the trigger makes a change to a shadow table, + ** then raise an error - do not allow the trigger to be created. */ + if( wx_sqlite3ReadOnlyShadowTables(db) ){ + TriggerStep *pStep; + for(pStep=pTrig->step_list; pStep; pStep=pStep->pNext){ + if( pStep->zTarget!=0 + && wx_sqlite3ShadowTableName(db, pStep->zTarget) + ){ + wx_sqlite3ErrorMsg(pParse, + "trigger \"%s\" may not write to shadow table \"%s\"", + pTrig->zName, pStep->zTarget); + goto triggerfinish_cleanup; + } + } + } + /* Make an entry in the sqlite_schema table */ v = wx_sqlite3GetVdbe(pParse); if( v==0 ) goto triggerfinish_cleanup; @@ -138939,7 +147185,7 @@ SQLITE_PRIVATE void wx_sqlite3FinishTrigger( z = wx_sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); testcase( z==0 ); wx_sqlite3NestedParse(pParse, - "INSERT INTO %Q." DFLT_SCHEMA_TABLE + "INSERT INTO %Q." LEGACY_SCHEMA_TABLE " VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", db->aDb[iDb].zDbSName, zName, pTrig->table, z); @@ -139024,6 +147270,7 @@ static TriggerStep *triggerStepAllocate( wx_sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; + if( pParse->nErr ) return 0; pTriggerStep = wx_sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1); if( pTriggerStep ){ char *z = (char*)&pTriggerStep[1]; @@ -139094,7 +147341,7 @@ SQLITE_PRIVATE TriggerStep *wx_sqlite3TriggerInsertStep( SQLITE_PRIVATE TriggerStep *wx_sqlite3TriggerUpdateStep( Parse *pParse, /* Parser */ Token *pTableName, /* Name of the table to be updated */ - SrcList *pFrom, + SrcList *pFrom, /* FROM clause for an UPDATE-FROM, or NULL */ ExprList *pEList, /* The SET clause: list of column and new values */ Expr *pWhere, /* The WHERE clause */ u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ @@ -139201,7 +147448,7 @@ SQLITE_PRIVATE void wx_sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noE } if( !pTrigger ){ if( !noErr ){ - wx_sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0); + wx_sqlite3ErrorMsg(pParse, "no such trigger: %S", pName->a); }else{ wx_sqlite3CodeVerifyNamedSchema(pParse, zDb); } @@ -139253,7 +147500,7 @@ SQLITE_PRIVATE void wx_sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ */ if( (v = wx_sqlite3GetVdbe(pParse))!=0 ){ wx_sqlite3NestedParse(pParse, - "DELETE FROM %Q." DFLT_SCHEMA_TABLE " WHERE name=%Q AND type='trigger'", + "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='trigger'", db->aDb[iDb].zDbSName, pTrigger->zName ); wx_sqlite3ChangeCookie(pParse, iDb); @@ -139307,13 +147554,22 @@ static int checkColumnOverlap(IdList *pIdList, ExprList *pEList){ return 0; } +/* +** Return true if any TEMP triggers exist +*/ +static int tempTriggersExist(wx_sqlite3 *db){ + if( NEVER(db->aDb[1].pSchema==0) ) return 0; + if( sqliteHashFirst(&db->aDb[1].pSchema->trigHash)==0 ) return 0; + return 1; +} + /* ** Return a list of all triggers on table pTab if there exists at least ** one trigger that must be fired when an operation of type 'op' is ** performed on the table, and, if that operation is an UPDATE, if at ** least one of the columns in pChanges is being modified. */ -SQLITE_PRIVATE Trigger *wx_sqlite3TriggersExist( +static SQLITE_NOINLINE Trigger *triggersReallyExist( Parse *pParse, /* Parse context */ Table *pTab, /* The table the contains the triggers */ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ @@ -139376,6 +147632,22 @@ exit_triggers_exist: } return (mask ? pList : 0); } +SQLITE_PRIVATE Trigger *wx_sqlite3TriggersExist( + Parse *pParse, /* Parse context */ + Table *pTab, /* The table the contains the triggers */ + int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ + ExprList *pChanges, /* Columns that change in an UPDATE statement */ + int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ +){ + assert( pTab!=0 ); + if( (pTab->pTrigger==0 && !tempTriggersExist(pParse->db)) + || pParse->disableTriggers + ){ + if( pMask ) *pMask = 0; + return 0; + } + return triggersReallyExist(pParse,pTab,op,pChanges,pMask); +} /* ** Convert the pStep->zTarget string into a SrcList and return a pointer @@ -139405,6 +147677,14 @@ SQLITE_PRIVATE SrcList *wx_sqlite3TriggerStepSrc( } if( pStep->pFrom ){ SrcList *pDup = wx_sqlite3SrcListDup(db, pStep->pFrom, 0); + if( pDup && pDup->nSrc>1 && !IN_RENAME_OBJECT ){ + Select *pSubquery; + Token as; + pSubquery = wx_sqlite3SelectNew(pParse,0,pDup,0,0,0,0,SF_NestedFrom,0); + as.n = 0; + as.z = 0; + pDup = wx_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); + } pSrc = wx_sqlite3SrcListAppendList(pParse, pSrc, pDup); } }else{ @@ -139455,12 +147735,12 @@ static ExprList *wx_sqlite3ExpandReturning( for(jj=0; jjnCol; jj++){ Expr *pNewExpr; if( IsHiddenColumn(pTab->aCol+jj) ) continue; - pNewExpr = wx_sqlite3Expr(db, TK_ID, pTab->aCol[jj].zName); + pNewExpr = wx_sqlite3Expr(db, TK_ID, pTab->aCol[jj].zCnName); pNew = wx_sqlite3ExprListAppend(pParse, pNew, pNewExpr); if( !db->mallocFailed ){ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1]; - pItem->zEName = wx_sqlite3DbStrDup(db, pTab->aCol[jj].zName); - pItem->eEName = ENAME_NAME; + pItem->zEName = wx_sqlite3DbStrDup(db, pTab->aCol[jj].zCnName); + pItem->fg.eEName = ENAME_NAME; } } }else{ @@ -139469,19 +147749,10 @@ static ExprList *wx_sqlite3ExpandReturning( if( !db->mallocFailed && ALWAYS(pList->a[i].zEName!=0) ){ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1]; pItem->zEName = wx_sqlite3DbStrDup(db, pList->a[i].zEName); - pItem->eEName = pList->a[i].eEName; + pItem->fg.eEName = pList->a[i].fg.eEName; } } } - if( !db->mallocFailed ){ - Vdbe *v = pParse->pVdbe; - assert( v!=0 ); - wx_sqlite3VdbeSetNumCols(v, pNew->nExpr); - for(i=0; inExpr; i++){ - wx_sqlite3VdbeSetColName(v, i, COLNAME_NAME, pNew->a[i].zEName, - SQLITE_TRANSIENT); - } - } return pNew; } @@ -139497,15 +147768,32 @@ static void codeReturningTrigger( int regIn /* The first in an array of registers */ ){ Vdbe *v = pParse->pVdbe; + wx_sqlite3 *db = pParse->db; ExprList *pNew; Returning *pReturning; + Select sSelect; + SrcList sFrom; assert( v!=0 ); assert( pParse->bReturning ); + assert( db->pParse==pParse ); pReturning = pParse->u1.pReturning; assert( pTrigger == &(pReturning->retTrig) ); + memset(&sSelect, 0, sizeof(sSelect)); + memset(&sFrom, 0, sizeof(sFrom)); + sSelect.pEList = wx_sqlite3ExprListDup(db, pReturning->pReturnEL, 0); + sSelect.pSrc = &sFrom; + sFrom.nSrc = 1; + sFrom.a[0].pTab = pTab; + sFrom.a[0].iCursor = -1; + wx_sqlite3SelectPrep(pParse, &sSelect, 0); + if( pParse->nErr==0 ){ + assert( db->mallocFailed==0 ); + wx_sqlite3GenerateColumnNames(pParse, &sSelect); + } + wx_sqlite3ExprListDelete(db, sSelect.pEList); pNew = wx_sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab); - if( pNew ){ + if( pParse->nErr==0 ){ NameContext sNC; memset(&sNC, 0, sizeof(sNC)); if( pReturning->nRetCol==0 ){ @@ -139517,23 +147805,30 @@ static void codeReturningTrigger( sNC.ncFlags = NC_UBaseReg; pParse->eTriggerOp = pTrigger->op; pParse->pTriggerTab = pTab; - if( wx_sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK ){ + if( wx_sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK + && ALWAYS(!db->mallocFailed) + ){ int i; int nCol = pNew->nExpr; int reg = pParse->nMem+1; pParse->nMem += nCol+2; pReturning->iRetReg = reg; for(i=0; ia[i].pExpr, reg+i); + Expr *pCol = pNew->a[i].pExpr; + assert( pCol!=0 ); /* Due to !db->mallocFailed ~9 lines above */ + wx_sqlite3ExprCodeFactorable(pParse, pCol, reg+i); + if( wx_sqlite3ExprAffinity(pCol)==SQLITE_AFF_REAL ){ + wx_sqlite3VdbeAddOp1(v, OP_RealAffinity, reg+i); + } } wx_sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, i, reg+i); wx_sqlite3VdbeAddOp2(v, OP_NewRowid, pReturning->iRetCur, reg+i+1); wx_sqlite3VdbeAddOp3(v, OP_Insert, pReturning->iRetCur, reg+i, reg+i+1); } - wx_sqlite3ExprListDelete(pParse->db, pNew); - pParse->eTriggerOp = 0; - pParse->pTriggerTab = 0; } + wx_sqlite3ExprListDelete(db, pNew); + pParse->eTriggerOp = 0; + pParse->pTriggerTab = 0; } @@ -139675,8 +147970,8 @@ static TriggerPrg *codeRowTrigger( Vdbe *v; /* Temporary VM */ NameContext sNC; /* Name context for sub-vdbe */ SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */ - Parse *pSubParse; /* Parse context for sub-vdbe */ int iEndTrigger = 0; /* Label to jump to if WHEN is false */ + Parse sSubParse; /* Parse context for sub-vdbe */ assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); assert( pTop->pVdbe ); @@ -139698,19 +147993,17 @@ static TriggerPrg *codeRowTrigger( /* Allocate and populate a new Parse context to use for coding the ** trigger sub-program. */ - pSubParse = wx_sqlite3StackAllocZero(db, sizeof(Parse)); - if( !pSubParse ) return 0; + wx_sqlite3ParseObjectInit(&sSubParse, db); memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pSubParse; - pSubParse->db = db; - pSubParse->pTriggerTab = pTab; - pSubParse->pToplevel = pTop; - pSubParse->zAuthContext = pTrigger->zName; - pSubParse->eTriggerOp = pTrigger->op; - pSubParse->nQueryLoop = pParse->nQueryLoop; - pSubParse->disableVtab = pParse->disableVtab; - - v = wx_sqlite3GetVdbe(pSubParse); + sNC.pParse = &sSubParse; + sSubParse.pTriggerTab = pTab; + sSubParse.pToplevel = pTop; + sSubParse.zAuthContext = pTrigger->zName; + sSubParse.eTriggerOp = pTrigger->op; + sSubParse.nQueryLoop = pParse->nQueryLoop; + sSubParse.prepFlags = pParse->prepFlags; + + v = wx_sqlite3GetVdbe(&sSubParse); if( v ){ VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", pTrigger->zName, onErrorText(orconf), @@ -139733,17 +148026,17 @@ static TriggerPrg *codeRowTrigger( ** OP_Halt inserted at the end of the program. */ if( pTrigger->pWhen ){ pWhen = wx_sqlite3ExprDup(db, pTrigger->pWhen, 0); - if( SQLITE_OK==wx_sqlite3ResolveExprNames(&sNC, pWhen) - && db->mallocFailed==0 + if( db->mallocFailed==0 + && SQLITE_OK==wx_sqlite3ResolveExprNames(&sNC, pWhen) ){ - iEndTrigger = wx_sqlite3VdbeMakeLabel(pSubParse); - wx_sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); + iEndTrigger = wx_sqlite3VdbeMakeLabel(&sSubParse); + wx_sqlite3ExprIfFalse(&sSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); } wx_sqlite3ExprDelete(db, pWhen); } /* Code the trigger program into the sub-vdbe. */ - codeTriggerProgram(pSubParse, pTrigger->step_list, orconf); + codeTriggerProgram(&sSubParse, pTrigger->step_list, orconf); /* Insert an OP_Halt at the end of the sub-program. */ if( iEndTrigger ){ @@ -139751,23 +148044,24 @@ static TriggerPrg *codeRowTrigger( } wx_sqlite3VdbeAddOp0(v, OP_Halt); VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf))); + transferParseError(pParse, &sSubParse); - transferParseError(pParse, pSubParse); - if( db->mallocFailed==0 && pParse->nErr==0 ){ + if( pParse->nErr==0 ){ + assert( db->mallocFailed==0 ); pProgram->aOp = wx_sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); } - pProgram->nMem = pSubParse->nMem; - pProgram->nCsr = pSubParse->nTab; + pProgram->nMem = sSubParse.nMem; + pProgram->nCsr = sSubParse.nTab; pProgram->token = (void *)pTrigger; - pPrg->aColmask[0] = pSubParse->oldmask; - pPrg->aColmask[1] = pSubParse->newmask; + pPrg->aColmask[0] = sSubParse.oldmask; + pPrg->aColmask[1] = sSubParse.newmask; wx_sqlite3VdbeDelete(v); + }else{ + transferParseError(pParse, &sSubParse); } - assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); - wx_sqlite3ParserReset(pSubParse); - wx_sqlite3StackFree(db, pSubParse); - + assert( !sSubParse.pTriggerPrg && !sSubParse.nMaxArg ); + wx_sqlite3ParseObjectReset(&sSubParse); return pPrg; } @@ -139800,6 +148094,7 @@ static TriggerPrg *getRowTrigger( /* If an existing TriggerPrg could not be located, create a new one. */ if( !pPrg ){ pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf); + pParse->db->errByteOffset = -1; } return pPrg; @@ -139822,7 +148117,7 @@ SQLITE_PRIVATE void wx_sqlite3CodeRowTriggerDirect( Vdbe *v = wx_sqlite3GetVdbe(pParse); /* Main VM */ TriggerPrg *pPrg; pPrg = getRowTrigger(pParse, p, pTab, orconf); - assert( pPrg || pParse->nErr || pParse->db->mallocFailed ); + assert( pPrg || pParse->nErr ); /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program ** is a pointer to the sub-vdbe containing the trigger program. */ @@ -140052,21 +148347,25 @@ static void updateVirtualTable( ** it has been converted into REAL. */ SQLITE_PRIVATE void wx_sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ + Column *pCol; assert( pTab!=0 ); - if( !pTab->pSelect ){ + assert( pTab->nCol>i ); + pCol = &pTab->aCol[i]; + if( pCol->iDflt ){ wx_sqlite3_value *pValue = 0; u8 enc = ENC(wx_sqlite3VdbeDb(v)); - Column *pCol = &pTab->aCol[i]; - VdbeComment((v, "%s.%s", pTab->zName, pCol->zName)); + assert( !IsView(pTab) ); + VdbeComment((v, "%s.%s", pTab->zName, pCol->zCnName)); assert( inCol ); - wx_sqlite3ValueFromExpr(wx_sqlite3VdbeDb(v), pCol->pDflt, enc, + wx_sqlite3ValueFromExpr(wx_sqlite3VdbeDb(v), + wx_sqlite3ColumnExpr(pTab,pCol), enc, pCol->affinity, &pValue); if( pValue ){ wx_sqlite3VdbeAppendP4(v, pValue, P4_MEM); } } #ifndef SQLITE_OMIT_FLOATING_POINT - if( pTab->aCol[i].affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){ + if( pCol->affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){ wx_sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); } #endif @@ -140213,6 +148512,7 @@ static void updateFromSelect( assert( pTabList->nSrc>1 ); if( pSrc ){ + pSrc->a[0].fg.notCte = 1; pSrc->a[0].iCursor = -1; pSrc->a[0].pTab->nTabRef--; pSrc->a[0].pTab = 0; @@ -140228,7 +148528,7 @@ static void updateFromSelect( pList = wx_sqlite3ExprListAppend(pParse, pList, pNew); } eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom; - }else if( pTab->pSelect ){ + }else if( IsView(pTab) ){ for(i=0; inCol; i++){ pList = wx_sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i)); } @@ -140242,7 +148542,8 @@ static void updateFromSelect( } #endif } - if( ALWAYS(pChanges) ){ + assert( pChanges!=0 || pParse->db->mallocFailed ); + if( pChanges ){ for(i=0; inExpr; i++){ pList = wx_sqlite3ExprListAppend(pParse, pList, wx_sqlite3ExprDup(db, pChanges->a[i].pExpr, 0) @@ -140250,8 +148551,10 @@ static void updateFromSelect( } } pSelect = wx_sqlite3SelectNew(pParse, pList, - pSrc, pWhere2, pGrp, 0, pOrderBy2, SF_UpdateFrom|SF_IncludeHidden, pLimit2 + pSrc, pWhere2, pGrp, 0, pOrderBy2, + SF_UFSrcCheck|SF_IncludeHidden|SF_UpdateFrom, pLimit2 ); + if( pSelect ) pSelect->selFlags |= SF_OrderByReqd; wx_sqlite3SelectDestInit(&dest, eDest, iEph); dest.iSDParm2 = (pPk ? pPk->nKeyCol : -1); wx_sqlite3Select(pParse, pSelect, &dest); @@ -140336,9 +148639,11 @@ SQLITE_PRIVATE void wx_sqlite3Update( memset(&sContext, 0, sizeof(sContext)); db = pParse->db; - if( pParse->nErr || db->mallocFailed ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto update_cleanup; } + assert( db->mallocFailed==0 ); /* Locate the table which we want to update. */ @@ -140351,7 +148656,7 @@ SQLITE_PRIVATE void wx_sqlite3Update( */ #ifndef SQLITE_OMIT_TRIGGER pTrigger = wx_sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, &tmask); - isView = pTab->pSelect!=0; + isView = IsView(pTab); assert( pTrigger || tmask==0 ); #else # define pTrigger 0 @@ -140363,6 +148668,14 @@ SQLITE_PRIVATE void wx_sqlite3Update( # define isView 0 #endif +#if TREETRACE_ENABLED + if( wx_sqlite3TreeTrace & 0x10000 ){ + wx_sqlite3TreeViewLine(0, "In wx_sqlite3Update() at %s:%d", __FILE__, __LINE__); + wx_sqlite3TreeViewUpdate(pParse->pWith, pTabList, pChanges, pWhere, + onError, pOrderBy, pLimit, pUpsert, pTrigger); + } +#endif + /* If there was a FROM clause, set nChangeFrom to the number of expressions ** in the change-list. Otherwise, set it to 0. There cannot be a FROM ** clause if this function is being called to generate code for part of @@ -140440,13 +148753,16 @@ SQLITE_PRIVATE void wx_sqlite3Update( */ chngRowid = chngPk = 0; for(i=0; inExpr; i++){ + u8 hCol = wx_sqlite3StrIHash(pChanges->a[i].zEName); /* If this is an UPDATE with a FROM clause, do not resolve expressions ** here. The call to wx_sqlite3Select() below will do that. */ if( nChangeFrom==0 && wx_sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){ goto update_cleanup; } for(j=0; jnCol; j++){ - if( wx_sqlite3StrICmp(pTab->aCol[j].zName, pChanges->a[i].zEName)==0 ){ + if( pTab->aCol[j].hName==hCol + && wx_sqlite3StrICmp(pTab->aCol[j].zCnName, pChanges->a[i].zEName)==0 + ){ if( j==pTab->iPKey ){ chngRowid = 1; pRowidExpr = pChanges->a[i].pExpr; @@ -140460,7 +148776,7 @@ SQLITE_PRIVATE void wx_sqlite3Update( testcase( pTab->aCol[j].colFlags & COLFLAG_STORED ); wx_sqlite3ErrorMsg(pParse, "cannot UPDATE generated column \"%s\"", - pTab->aCol[j].zName); + pTab->aCol[j].zCnName); goto update_cleanup; } #endif @@ -140484,7 +148800,7 @@ SQLITE_PRIVATE void wx_sqlite3Update( { int rc; rc = wx_sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, - j<0 ? "ROWID" : pTab->aCol[j].zName, + j<0 ? "ROWID" : pTab->aCol[j].zCnName, db->aDb[iDb].zDbSName); if( rc==SQLITE_DENY ){ goto update_cleanup; @@ -140516,8 +148832,10 @@ SQLITE_PRIVATE void wx_sqlite3Update( for(i=0; inCol; i++){ if( aXRef[i]>=0 ) continue; if( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)==0 ) continue; - if( wx_sqlite3ExprReferencesUpdatedColumn(pTab->aCol[i].pDflt, - aXRef, chngRowid) ){ + if( wx_sqlite3ExprReferencesUpdatedColumn( + wx_sqlite3ColumnExpr(pTab, &pTab->aCol[i]), + aXRef, chngRowid) + ){ aXRef[i] = 99999; bProgress = 1; } @@ -140697,15 +149015,25 @@ SQLITE_PRIVATE void wx_sqlite3Update( /* Begin the database scan. ** ** Do not consider a single-pass strategy for a multi-row update if - ** there are any triggers or foreign keys to process, or rows may - ** be deleted as a result of REPLACE conflict handling. Any of these - ** things might disturb a cursor being used to scan through the table - ** or index, causing a single-pass approach to malfunction. */ + ** there is anything that might disrupt the cursor being used to do + ** the UPDATE: + ** (1) This is a nested UPDATE + ** (2) There are triggers + ** (3) There are FOREIGN KEY constraints + ** (4) There are REPLACE conflict handlers + ** (5) There are subqueries in the WHERE clause + */ flags = WHERE_ONEPASS_DESIRED; - if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ + if( !pParse->nested + && !pTrigger + && !hasFK + && !chngKey + && !bReplace + && (sNC.ncFlags & NC_Subquery)==0 + ){ flags |= WHERE_ONEPASS_MULTIROW; } - pWInfo = wx_sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags,iIdxCur); + pWInfo = wx_sqlite3WhereBegin(pParse, pTabList, pWhere,0,0,0,flags,iIdxCur); if( pWInfo==0 ) goto update_cleanup; /* A one-pass strategy that might update more than one row may not @@ -140792,7 +149120,12 @@ SQLITE_PRIVATE void wx_sqlite3Update( /* Top of the update loop */ if( eOnePass!=ONEPASS_OFF ){ - if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){ + if( aiCurOnePass[0]!=iDataCur + && aiCurOnePass[1]!=iDataCur +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + && !isView +#endif + ){ assert( pPk ); wx_sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey,nKey); VdbeCoverage(v); @@ -140997,7 +149330,7 @@ SQLITE_PRIVATE void wx_sqlite3Update( }else{ wx_sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid); } - VdbeCoverageNeverTaken(v); + VdbeCoverage(v); } /* Do FK constraint checks. */ @@ -141100,9 +149433,7 @@ SQLITE_PRIVATE void wx_sqlite3Update( ** that information. */ if( regRowCount ){ - wx_sqlite3VdbeAddOp2(v, OP_ChngCntRow, regRowCount, 1); - wx_sqlite3VdbeSetNumCols(v, 1); - wx_sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC); + wx_sqlite3CodeChangeCount(v, regRowCount, "rows updated"); } update_cleanup: @@ -141224,7 +149555,9 @@ static void updateVirtualTable( regRowid = ++pParse->nMem; /* Start scanning the virtual table */ - pWInfo = wx_sqlite3WhereBegin(pParse, pSrc,pWhere,0,0,WHERE_ONEPASS_DESIRED,0); + pWInfo = wx_sqlite3WhereBegin( + pParse, pSrc, pWhere, 0, 0, 0, WHERE_ONEPASS_DESIRED, 0 + ); if( pWInfo==0 ) return; /* Populate the argument registers. */ @@ -141483,6 +149816,7 @@ SQLITE_PRIVATE int wx_sqlite3UpsertAnalyzeTarget( if( pIdx->aiColumn[ii]==XN_EXPR ){ assert( pIdx->aColExpr!=0 ); assert( pIdx->aColExpr->nExpr>ii ); + assert( pIdx->bHasExpr ); pExpr = pIdx->aColExpr->a[ii].pExpr; if( pExpr->op!=TK_COLLATE ){ sCol[0].pLeft = pExpr; @@ -141604,7 +149938,7 @@ SQLITE_PRIVATE void wx_sqlite3UpsertDoUpdate( k = wx_sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]); wx_sqlite3VdbeAddOp3(v, OP_Column, iCur, k, iPk+i); VdbeComment((v, "%s.%s", pIdx->zName, - pTab->aCol[pPk->aiColumn[i]].zName)); + pTab->aCol[pPk->aiColumn[i]].zCnName)); } wx_sqlite3VdbeVerifyAbortable(v, OE_Abort); i = wx_sqlite3VdbeAddOp4Int(v, OP_Found, iDataCur, 0, iPk, nPk); @@ -141786,8 +150120,8 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3RunVacuum( Btree *pTemp; /* The temporary database we vacuum into */ u32 saved_mDbFlags; /* Saved value of db->mDbFlags */ u64 saved_flags; /* Saved value of db->flags */ - int saved_nChange; /* Saved value of db->nChange */ - int saved_nTotalChange; /* Saved value of db->nTotalChange */ + i64 saved_nChange; /* Saved value of db->nChange */ + i64 saved_nTotalChange; /* Saved value of db->nTotalChange */ u32 saved_openFlags; /* Saved value of db->openFlags */ u8 saved_mTrace; /* Saved trace settings */ Db *pDb = 0; /* Database to detach at end of vacuum */ @@ -141796,6 +150130,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3RunVacuum( int nDb; /* Number of attached databases */ const char *zDbMain; /* Schema name of database to vacuum */ const char *zOut; /* Name of output file */ + u32 pgflags = PAGER_SYNCHRONOUS_OFF; /* sync flags for output db */ if( !db->autoCommit ){ wx_sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); @@ -141867,12 +150202,17 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3RunVacuum( goto end_of_vacuum; } db->mDbFlags |= DBFLAG_VacuumInto; + + /* For a VACUUM INTO, the pager-flags are set to the same values as + ** they are for the database being vacuumed, except that PAGER_CACHESPILL + ** is always set. */ + pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK); } nRes = wx_sqlite3BtreeGetRequestedReserve(pMain); wx_sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); wx_sqlite3BtreeSetSpillSize(pTemp, wx_sqlite3BtreeSetSpillSize(pMain,0)); - wx_sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL); + wx_sqlite3BtreeSetPagerFlags(pTemp, pgflags|PAGER_CACHESPILL); /* Begin a transaction and take an exclusive lock on the main database ** file. This is done before the wx_sqlite3BtreeGetPageSize(pMain) call below, @@ -141885,7 +150225,9 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3RunVacuum( /* Do not attempt to change the page size for a WAL database */ if( wx_sqlite3PagerGetJournalMode(wx_sqlite3BtreePager(pMain)) - ==PAGER_JOURNALMODE_WAL ){ + ==PAGER_JOURNALMODE_WAL + && pOut==0 + ){ db->nextPagesize = 0; } @@ -142001,6 +150343,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3RunVacuum( assert( rc==SQLITE_OK ); if( pOut==0 ){ + nRes = wx_sqlite3BtreeGetRequestedReserve(pTemp); rc = wx_sqlite3BtreeSetPageSize(pMain, wx_sqlite3BtreeGetPageSize(pTemp), nRes,1); } @@ -142234,7 +150577,7 @@ SQLITE_PRIVATE void wx_sqlite3VtabLock(VTable *pVTab){ SQLITE_PRIVATE VTable *wx_sqlite3GetVTable(wx_sqlite3 *db, Table *pTab){ VTable *pVtab; assert( IsVirtual(pTab) ); - for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); + for(pVtab=pTab->u.vtab.p; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); return pVtab; } @@ -142247,36 +150590,40 @@ SQLITE_PRIVATE void wx_sqlite3VtabUnlock(VTable *pVTab){ assert( db ); assert( pVTab->nRef>0 ); - assert( db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ZOMBIE ); + assert( db->eOpenState==SQLITE_STATE_OPEN + || db->eOpenState==SQLITE_STATE_ZOMBIE ); pVTab->nRef--; if( pVTab->nRef==0 ){ wx_sqlite3_vtab *p = pVTab->pVtab; - wx_sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod); if( p ){ p->pModule->xDisconnect(p); } + wx_sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod); wx_sqlite3DbFree(db, pVTab); } } /* ** Table p is a virtual table. This function moves all elements in the -** p->pVTable list to the wx_sqlite3.pDisconnect lists of their associated +** p->u.vtab.p list to the wx_sqlite3.pDisconnect lists of their associated ** database connections to be disconnected at the next opportunity. ** Except, if argument db is not NULL, then the entry associated with -** connection db is left in the p->pVTable list. +** connection db is left in the p->u.vtab.p list. */ static VTable *vtabDisconnectAll(wx_sqlite3 *db, Table *p){ VTable *pRet = 0; - VTable *pVTable = p->pVTable; - p->pVTable = 0; + VTable *pVTable; + + assert( IsVirtual(p) ); + pVTable = p->u.vtab.p; + p->u.vtab.p = 0; /* Assert that the mutex (if any) associated with the BtShared database ** that contains table p is held by the caller. See header comments ** above function wx_sqlite3VtabUnlockList() for an explanation of why ** this makes it safe to access the wx_sqlite3.pDisconnect list of any - ** database connection that may have an entry in the p->pVTable list. + ** database connection that may have an entry in the p->u.vtab.p list. */ assert( db==0 || wx_sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); @@ -142286,7 +150633,7 @@ static VTable *vtabDisconnectAll(wx_sqlite3 *db, Table *p){ assert( db2 ); if( db2==db ){ pRet = pVTable; - p->pVTable = pRet; + p->u.vtab.p = pRet; pRet->pNext = 0; }else{ pVTable->pNext = db2->pDisconnect; @@ -142314,7 +150661,7 @@ SQLITE_PRIVATE void wx_sqlite3VtabDisconnect(wx_sqlite3 *db, Table *p){ assert( wx_sqlite3BtreeHoldsAllMutexes(db) ); assert( wx_sqlite3_mutex_held(db->mutex) ); - for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){ + for(ppVTab=&p->u.vtab.p; *ppVTab; ppVTab=&(*ppVTab)->pNext){ if( (*ppVTab)->db==db ){ VTable *pVTab = *ppVTab; *ppVTab = pVTab->pNext; @@ -142377,37 +150724,42 @@ SQLITE_PRIVATE void wx_sqlite3VtabUnlockList(wx_sqlite3 *db){ ** database connection. */ SQLITE_PRIVATE void wx_sqlite3VtabClear(wx_sqlite3 *db, Table *p){ - if( !db || db->pnBytesFreed==0 ) vtabDisconnectAll(0, p); - if( p->azModuleArg ){ + assert( IsVirtual(p) ); + assert( db!=0 ); + if( db->pnBytesFreed==0 ) vtabDisconnectAll(0, p); + if( p->u.vtab.azArg ){ int i; - for(i=0; inModuleArg; i++){ - if( i!=1 ) wx_sqlite3DbFree(db, p->azModuleArg[i]); + for(i=0; iu.vtab.nArg; i++){ + if( i!=1 ) wx_sqlite3DbFree(db, p->u.vtab.azArg[i]); } - wx_sqlite3DbFree(db, p->azModuleArg); + wx_sqlite3DbFree(db, p->u.vtab.azArg); } } /* -** Add a new module argument to pTable->azModuleArg[]. +** Add a new module argument to pTable->u.vtab.azArg[]. ** The string is not copied - the pointer is stored. The ** string will be freed automatically when the table is ** deleted. */ static void addModuleArgument(Parse *pParse, Table *pTable, char *zArg){ - wx_sqlite3_int64 nBytes = sizeof(char *)*(2+pTable->nModuleArg); + wx_sqlite3_int64 nBytes; char **azModuleArg; wx_sqlite3 *db = pParse->db; - if( pTable->nModuleArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){ + + assert( IsVirtual(pTable) ); + nBytes = sizeof(char *)*(2+pTable->u.vtab.nArg); + if( pTable->u.vtab.nArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){ wx_sqlite3ErrorMsg(pParse, "too many columns on %s", pTable->zName); } - azModuleArg = wx_sqlite3DbRealloc(db, pTable->azModuleArg, nBytes); + azModuleArg = wx_sqlite3DbRealloc(db, pTable->u.vtab.azArg, nBytes); if( azModuleArg==0 ){ wx_sqlite3DbFree(db, zArg); }else{ - int i = pTable->nModuleArg++; + int i = pTable->u.vtab.nArg++; azModuleArg[i] = zArg; azModuleArg[i+1] = 0; - pTable->azModuleArg = azModuleArg; + pTable->u.vtab.azArg = azModuleArg; } } @@ -142430,10 +150782,11 @@ SQLITE_PRIVATE void wx_sqlite3VtabBeginParse( pTable = pParse->pNewTable; if( pTable==0 ) return; assert( 0==pTable->pIndex ); + pTable->eTabType = TABTYP_VTAB; db = pParse->db; - assert( pTable->nModuleArg==0 ); + assert( pTable->u.vtab.nArg==0 ); addModuleArgument(pParse, pTable, wx_sqlite3NameFromToken(db, pModuleName)); addModuleArgument(pParse, pTable, 0); addModuleArgument(pParse, pTable, wx_sqlite3DbStrDup(db, pTable->zName)); @@ -142450,11 +150803,11 @@ SQLITE_PRIVATE void wx_sqlite3VtabBeginParse( ** sqlite_schema table, has already been made by wx_sqlite3StartTable(). ** The second call, to obtain permission to create the table, is made now. */ - if( pTable->azModuleArg ){ + if( pTable->u.vtab.azArg ){ int iDb = wx_sqlite3SchemaToIndex(db, pTable->pSchema); assert( iDb>=0 ); /* The database the table is being created in */ wx_sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName, - pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName); + pTable->u.vtab.azArg[0], pParse->db->aDb[iDb].zDbSName); } #endif } @@ -142482,9 +150835,10 @@ SQLITE_PRIVATE void wx_sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ wx_sqlite3 *db = pParse->db; /* The database connection */ if( pTab==0 ) return; + assert( IsVirtual(pTab) ); addArgumentToVtab(pParse); pParse->sArg.z = 0; - if( pTab->nModuleArg<1 ) return; + if( pTab->u.vtab.nArg<1 ) return; /* If the CREATE VIRTUAL TABLE statement is being entered for the ** first time (in other words if the virtual table is actually being @@ -142517,7 +150871,7 @@ SQLITE_PRIVATE void wx_sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ */ iDb = wx_sqlite3SchemaToIndex(db, pTab->pSchema); wx_sqlite3NestedParse(pParse, - "UPDATE %Q." DFLT_SCHEMA_TABLE " " + "UPDATE %Q." LEGACY_SCHEMA_TABLE " " "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " "WHERE rowid=#%d", db->aDb[iDb].zDbSName, @@ -142537,18 +150891,14 @@ SQLITE_PRIVATE void wx_sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ iReg = ++pParse->nMem; wx_sqlite3VdbeLoadString(v, iReg, pTab->zName); wx_sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg); - } - - /* If we are rereading the sqlite_schema table create the in-memory - ** record of the table. The xConnect() method is not called until - ** the first time the virtual table is used in an SQL statement. This - ** allows a schema that contains virtual tables to be loaded before - ** the required virtual table implementations are registered. */ - else { + }else{ + /* If we are rereading the sqlite_schema table create the in-memory + ** record of the table. */ Table *pOld; Schema *pSchema = pTab->pSchema; const char *zName = pTab->zName; - assert( wx_sqlite3SchemaMutexHeld(db, 0, pSchema) ); + assert( zName!=0 ); + wx_sqlite3MarkAllShadowTablesOf(db, pTab); pOld = wx_sqlite3HashInsert(&pSchema->tblHash, zName, pTab); if( pOld ){ wx_sqlite3OomFault(db); @@ -142599,13 +150949,16 @@ static int vtabCallConstructor( VtabCtx sCtx; VTable *pVTable; int rc; - const char *const*azArg = (const char *const*)pTab->azModuleArg; - int nArg = pTab->nModuleArg; + const char *const*azArg; + int nArg = pTab->u.vtab.nArg; char *zErr = 0; char *zModuleName; int iDb; VtabCtx *pCtx; + assert( IsVirtual(pTab) ); + azArg = (const char *const*)pTab->u.vtab.azArg; + /* Check that the virtual-table is not already being initialized */ for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){ if( pCtx->pTab==pTab ){ @@ -142632,7 +150985,7 @@ static int vtabCallConstructor( pVTable->eVtabRisk = SQLITE_VTABRISK_Normal; iDb = wx_sqlite3SchemaToIndex(db, pTab->pSchema); - pTab->azModuleArg[1] = db->aDb[iDb].zDbSName; + pTab->u.vtab.azArg[1] = db->aDb[iDb].zDbSName; /* Invoke the virtual table constructor */ assert( &db->pVtabCtx ); @@ -142642,7 +150995,9 @@ static int vtabCallConstructor( sCtx.pPrior = db->pVtabCtx; sCtx.bDeclared = 0; db->pVtabCtx = &sCtx; + pTab->nTabRef++; rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); + wx_sqlite3DeleteTable(db, pTab); db->pVtabCtx = sCtx.pPrior; if( rc==SQLITE_NOMEM ) wx_sqlite3OomFault(db); assert( sCtx.pTab==pTab ); @@ -142671,12 +151026,12 @@ static int vtabCallConstructor( int iCol; u16 oooHidden = 0; /* If everything went according to plan, link the new VTable structure - ** into the linked list headed by pTab->pVTable. Then loop through the + ** into the linked list headed by pTab->u.vtab.p. Then loop through the ** columns of the table to see if any of them contain the token "hidden". ** If so, set the Column COLFLAG_HIDDEN flag and remove the token from ** the type string. */ - pVTable->pNext = pTab->pVTable; - pTab->pVTable = pVTable; + pVTable->pNext = pTab->u.vtab.p; + pTab->u.vtab.p = pVTable; for(iCol=0; iColnCol; iCol++){ char *zType = wx_sqlite3ColumnType(&pTab->aCol[iCol], ""); @@ -142729,16 +151084,17 @@ SQLITE_PRIVATE int wx_sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ int rc; assert( pTab ); - if( !IsVirtual(pTab) || wx_sqlite3GetVTable(db, pTab) ){ + assert( IsVirtual(pTab) ); + if( wx_sqlite3GetVTable(db, pTab) ){ return SQLITE_OK; } /* Locate the required virtual table module */ - zMod = pTab->azModuleArg[0]; + zMod = pTab->u.vtab.azArg[0]; pMod = (Module*)wx_sqlite3HashFind(&db->aModule, zMod); if( !pMod ){ - const char *zModule = pTab->azModuleArg[0]; + const char *zModule = pTab->u.vtab.azArg[0]; wx_sqlite3ErrorMsg(pParse, "no such module: %s", zModule); rc = SQLITE_ERROR; }else{ @@ -142801,10 +151157,10 @@ SQLITE_PRIVATE int wx_sqlite3VtabCallCreate(wx_sqlite3 *db, int iDb, const char const char *zMod; pTab = wx_sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName); - assert( pTab && IsVirtual(pTab) && !pTab->pVTable ); + assert( pTab && IsVirtual(pTab) && !pTab->u.vtab.p ); /* Locate the required virtual table module */ - zMod = pTab->azModuleArg[0]; + zMod = pTab->u.vtab.azArg[0]; pMod = (Module*)wx_sqlite3HashFind(&db->aModule, zMod); /* If the module has been registered and includes a Create method, @@ -142839,8 +151195,8 @@ SQLITE_API int wx_sqlite3_declare_vtab(wx_sqlite3 *db, const char *zCreateTable) VtabCtx *pCtx; int rc = SQLITE_OK; Table *pTab; - char *zErr = 0; Parse sParse; + int initBusy; #ifdef SQLITE_ENABLE_API_ARMOR if( !wx_sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ @@ -142857,20 +151213,27 @@ SQLITE_API int wx_sqlite3_declare_vtab(wx_sqlite3 *db, const char *zCreateTable) pTab = pCtx->pTab; assert( IsVirtual(pTab) ); - memset(&sParse, 0, sizeof(sParse)); + wx_sqlite3ParseObjectInit(&sParse, db); sParse.eParseMode = PARSE_MODE_DECLARE_VTAB; - sParse.db = db; + sParse.disableTriggers = 1; + /* We should never be able to reach this point while loading the + ** schema. Nevertheless, defend against that (turn off db->init.busy) + ** in case a bug arises. */ + assert( db->init.busy==0 ); + initBusy = db->init.busy; + db->init.busy = 0; sParse.nQueryLoop = 1; - if( SQLITE_OK==wx_sqlite3RunParser(&sParse, zCreateTable, &zErr) - && sParse.pNewTable - && !db->mallocFailed - && !sParse.pNewTable->pSelect - && !IsVirtual(sParse.pNewTable) + if( SQLITE_OK==wx_sqlite3RunParser(&sParse, zCreateTable) + && ALWAYS(sParse.pNewTable!=0) + && ALWAYS(!db->mallocFailed) + && IsOrdinaryTable(sParse.pNewTable) ){ + assert( sParse.zErrMsg==0 ); if( !pTab->aCol ){ Table *pNew = sParse.pNewTable; Index *pIdx; pTab->aCol = pNew->aCol; + wx_sqlite3ExprListDelete(db, pNew->u.tab.pDfltList); pTab->nNVCol = pTab->nCol = pNew->nCol; pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid); pNew->nCol = 0; @@ -142895,8 +151258,9 @@ SQLITE_API int wx_sqlite3_declare_vtab(wx_sqlite3 *db, const char *zCreateTable) } pCtx->bDeclared = 1; }else{ - wx_sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); - wx_sqlite3DbFree(db, zErr); + wx_sqlite3ErrorWithMsg(db, SQLITE_ERROR, + (sParse.zErrMsg ? "%s" : 0), sParse.zErrMsg); + wx_sqlite3DbFree(db, sParse.zErrMsg); rc = SQLITE_ERROR; } sParse.eParseMode = PARSE_MODE_NORMAL; @@ -142905,7 +151269,8 @@ SQLITE_API int wx_sqlite3_declare_vtab(wx_sqlite3 *db, const char *zCreateTable) wx_sqlite3VdbeFinalize(sParse.pVdbe); } wx_sqlite3DeleteTable(db, sParse.pNewTable); - wx_sqlite3ParserReset(&sParse); + wx_sqlite3ParseObjectReset(&sParse); + db->init.busy = initBusy; assert( (rc&0xff)==rc ); rc = wx_sqlite3ApiExit(db, rc); @@ -142925,10 +151290,13 @@ SQLITE_PRIVATE int wx_sqlite3VtabCallDestroy(wx_sqlite3 *db, int iDb, const char Table *pTab; pTab = wx_sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName); - if( pTab!=0 && ALWAYS(pTab->pVTable!=0) ){ + if( ALWAYS(pTab!=0) + && ALWAYS(IsVirtual(pTab)) + && ALWAYS(pTab->u.vtab.p!=0) + ){ VTable *p; int (*xDestroy)(wx_sqlite3_vtab *); - for(p=pTab->pVTable; p; p=p->pNext){ + for(p=pTab->u.vtab.p; p; p=p->pNext){ assert( p->pVtab ); if( p->pVtab->nRef>0 ){ return SQLITE_LOCKED; @@ -142942,9 +151310,9 @@ SQLITE_PRIVATE int wx_sqlite3VtabCallDestroy(wx_sqlite3 *db, int iDb, const char rc = xDestroy(p->pVtab); /* Remove the wx_sqlite3_vtab* from the aVTrans[] array, if applicable */ if( rc==SQLITE_OK ){ - assert( pTab->pVTable==p && p->pNext==0 ); + assert( pTab->u.vtab.p==p && p->pNext==0 ); p->pVtab = 0; - pTab->pVTable = 0; + pTab->u.vtab.p = 0; wx_sqlite3VtabUnlock(p); } wx_sqlite3DeleteTable(db, pTab); @@ -143158,8 +151526,9 @@ SQLITE_PRIVATE FuncDef *wx_sqlite3VtabOverloadFunction( /* Check to see the left operand is a column in a virtual table */ if( NEVER(pExpr==0) ) return pDef; if( pExpr->op!=TK_COLUMN ) return pDef; + assert( ExprUseYTab(pExpr) ); pTab = pExpr->y.pTab; - if( pTab==0 ) return pDef; + if( NEVER(pTab==0) ) return pDef; if( !IsVirtual(pTab) ) return pDef; pVtab = wx_sqlite3GetVTable(db, pTab)->pVtab; assert( pVtab!=0 ); @@ -143232,8 +151601,9 @@ SQLITE_PRIVATE void wx_sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ /* ** Check to see if virtual table module pMod can be have an eponymous ** virtual table instance. If it can, create one if one does not already -** exist. Return non-zero if the eponymous virtual table instance exists -** when this routine returns, and return zero if it does not exist. +** exist. Return non-zero if either the eponymous virtual table instance +** exists when this routine returns or if an attempt to create it failed +** and an error message was left in pParse. ** ** An eponymous virtual table instance is one that is named after its ** module, and more importantly, does not require a CREATE VIRTUAL TABLE @@ -143260,9 +151630,11 @@ SQLITE_PRIVATE int wx_sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod) } pMod->pEpoTab = pTab; pTab->nTabRef = 1; + pTab->eTabType = TABTYP_VTAB; pTab->pSchema = db->aDb[0].pSchema; - assert( pTab->nModuleArg==0 ); + assert( pTab->u.vtab.nArg==0 ); pTab->iPKey = -1; + pTab->tabFlags |= TF_Eponymous; addModuleArgument(pParse, pTab, wx_sqlite3DbStrDup(db, pTab->zName)); addModuleArgument(pParse, pTab, 0); addModuleArgument(pParse, pTab, wx_sqlite3DbStrDup(db, pTab->zName)); @@ -143271,7 +151643,6 @@ SQLITE_PRIVATE int wx_sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod) wx_sqlite3ErrorMsg(pParse, "%s", zErr); wx_sqlite3DbFree(db, zErr); wx_sqlite3VtabEponymousTableClear(db, pMod); - return 0; } return 1; } @@ -143418,6 +151789,28 @@ typedef struct WhereLoopBuilder WhereLoopBuilder; typedef struct WhereScan WhereScan; typedef struct WhereOrCost WhereOrCost; typedef struct WhereOrSet WhereOrSet; +typedef struct WhereMemBlock WhereMemBlock; +typedef struct WhereRightJoin WhereRightJoin; + +/* +** This object is a header on a block of allocated memory that will be +** automatically freed when its WInfo oject is destructed. +*/ +struct WhereMemBlock { + WhereMemBlock *pNext; /* Next block in the chain */ + u64 sz; /* Bytes of space */ +}; + +/* +** Extra information attached to a WhereLevel that is a RIGHT JOIN. +*/ +struct WhereRightJoin { + int iMatch; /* Cursor used to determine prior matched rows */ + int regBloom; /* Bloom filter for iRJMatch */ + int regReturn; /* Return register for the interior subroutine */ + int addrSubrtn; /* Starting address for the interior subroutine */ + int endSubrtn; /* The last opcode in the interior subroutine */ +}; /* ** This object contains information needed to implement a single nested @@ -143450,6 +151843,8 @@ struct WhereLevel { u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */ int addrLikeRep; /* LIKE range processing address */ #endif + int regFilter; /* Bloom filter */ + WhereRightJoin *pRJ; /* Extra information for RIGHT JOIN */ u8 iFrom; /* Which entry in the FROM clause */ u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */ int p1, p2; /* Operands of the opcode used to end the loop */ @@ -143464,7 +151859,7 @@ struct WhereLevel { u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */ } *aInLoop; /* Information about each nested IN operator */ } in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */ - Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */ + Index *pCoveringIdx; /* Possible covering index for WHERE_MULTI_OR */ } u; struct WhereLoop *pWLoop; /* The selected WhereLoop object */ Bitmask notReady; /* FROM entries not usable at this level */ @@ -143508,10 +151903,12 @@ struct WhereLoop { } btree; struct { /* Information for virtual tables */ int idxNum; /* Index number */ - u8 needFree; /* True if wx_sqlite3_free(idxStr) is needed */ + u32 needFree : 1; /* True if wx_sqlite3_free(idxStr) is needed */ + u32 bOmitOffset : 1; /* True to let virtual table handle offset */ i8 isOrdered; /* True if satisfies ORDER BY */ u16 omitMask; /* Terms that may be omitted */ char *idxStr; /* Index identifier string */ + u32 mHandleIn; /* Terms to handle as IN(...) instead of == */ } vtab; } u; u32 wsFlags; /* WHERE_* flags describing the plan */ @@ -143655,7 +152052,7 @@ struct WhereTerm { #define TERM_COPIED 0x0008 /* Has a child */ #define TERM_ORINFO 0x0010 /* Need to free the WhereTerm.u.pOrInfo object */ #define TERM_ANDINFO 0x0020 /* Need to free the WhereTerm.u.pAndInfo obj */ -#define TERM_OR_OK 0x0040 /* Used during OR-clause processing */ +#define TERM_OK 0x0040 /* Used during OR-clause processing */ #define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */ #define TERM_LIKEOPT 0x0100 /* Virtual terms from the LIKE optimization */ #define TERM_LIKECOND 0x0200 /* Conditionally this LIKE operator term */ @@ -143668,6 +152065,7 @@ struct WhereTerm { #else # define TERM_HIGHTRUTH 0 /* Only used with STAT4 */ #endif +#define TERM_SLICE 0x8000 /* One slice of a row-value/vector comparison */ /* ** An instance of the WhereScan object is used as an iterator for locating @@ -143678,11 +152076,11 @@ struct WhereScan { WhereClause *pWC; /* WhereClause currently being scanned */ const char *zCollName; /* Required collating sequence, if not NULL */ Expr *pIdxExpr; /* Search for this index expression */ - char idxaff; /* Must match this affinity, if zCollName!=NULL */ - unsigned char nEquiv; /* Number of entries in aEquiv[] */ - unsigned char iEquiv; /* Next unused slot in aEquiv[] */ - u32 opMask; /* Acceptable operators */ int k; /* Resume scanning at this->pWC->a[this->k] */ + u32 opMask; /* Acceptable operators */ + char idxaff; /* Must match this affinity, if zCollName!=NULL */ + unsigned char iEquiv; /* Current slot in aiCur[] and aiColumn[] */ + unsigned char nEquiv; /* Number of entries in aiCur[] and aiColumn[] */ int aiCur[11]; /* Cursors in the equivalence class */ i16 aiColumn[11]; /* Corresponding column number in the eq-class */ }; @@ -143706,6 +152104,7 @@ struct WhereClause { u8 hasOr; /* True if any a[].eOperator is WO_OR */ int nTerm; /* Number of terms */ int nSlot; /* Number of entries in a[] */ + int nBase; /* Number of terms through the last non-Virtual */ WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */ #if defined(SQLITE_SMALL_STACK) WhereTerm aStatic[1]; /* Initial static space for a[] */ @@ -143736,7 +152135,7 @@ struct WhereAndInfo { ** between VDBE cursor numbers and bits of the bitmasks in WhereTerm. ** ** The VDBE cursor numbers are small integers contained in -** SrcList_item.iCursor and Expr.iTable fields. For any given WHERE +** SrcItem.iCursor and Expr.iTable fields. For any given WHERE ** clause, the cursor numbers might not begin with 0 and they might ** contain gaps in the numbering sequence. But we want to make maximum ** use of the bits in our bitmasks. This structure provides a mapping @@ -143763,11 +152162,6 @@ struct WhereMaskSet { int ix[BMS]; /* Cursor assigned to each bit */ }; -/* -** Initialize a WhereMaskSet object -*/ -#define initMaskSet(P) (P)->n=0 - /* ** This object is a convenience wrapper holding all information needed ** to construct WhereLoop objects for a particular query. @@ -143775,7 +152169,6 @@ struct WhereMaskSet { struct WhereLoopBuilder { WhereInfo *pWInfo; /* Information about this WHERE */ WhereClause *pWC; /* WHERE clause terms */ - ExprList *pOrderBy; /* ORDER BY clause */ WhereLoop *pNew; /* Template WhereLoop */ WhereOrSet *pOrSet; /* Record best loops here, if not NULL */ #ifdef SQLITE_ENABLE_STAT4 @@ -143813,20 +152206,6 @@ struct WhereLoopBuilder { # define SQLITE_QUERY_PLANNER_LIMIT_INCR 1000 #endif -/* -** Each instance of this object records a change to a single node -** in an expression tree to cause that node to point to a column -** of an index rather than an expression or a virtual column. All -** such transformations need to be undone at the end of WHERE clause -** processing. -*/ -typedef struct WhereExprMod WhereExprMod; -struct WhereExprMod { - WhereExprMod *pNext; /* Next translation on a list of them all */ - Expr *pExpr; /* The Expr node that was transformed */ - Expr orig; /* Original value of the Expr node */ -}; - /* ** The WHERE clause processing routine has two halves. The ** first part does the start of the WHERE loop and the second @@ -143842,7 +152221,10 @@ struct WhereInfo { SrcList *pTabList; /* List of tables in the join */ ExprList *pOrderBy; /* The ORDER BY clause or NULL */ ExprList *pResultSet; /* Result set of the query */ +#if WHERETRACE_ENABLED Expr *pWhere; /* The complete WHERE clause */ +#endif + Select *pSelect; /* The entire SELECT statement containing WHERE */ int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ @@ -143861,7 +152243,7 @@ struct WhereInfo { int iTop; /* The very beginning of the WHERE loop */ int iEndWhere; /* End of the WHERE clause itself */ WhereLoop *pLoops; /* List of all WhereLoop objects */ - WhereExprMod *pExprMods; /* Expression modifications */ + WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */ Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ WhereClause sWC; /* Decomposition of the WHERE clause */ WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ @@ -143887,6 +152269,8 @@ SQLITE_PRIVATE WhereTerm *wx_sqlite3WhereFindTerm( u32 op, /* Mask of WO_xx values describing operator */ Index *pIdx /* Must be compatible with this index, if not NULL */ ); +SQLITE_PRIVATE void *wx_sqlite3WhereMalloc(WhereInfo *pWInfo, u64 nByte); +SQLITE_PRIVATE void *wx_sqlite3WhereRealloc(WhereInfo *pWInfo, void *pOld, u64 nByte); /* wherecode.c: */ #ifndef SQLITE_OMIT_EXPLAIN @@ -143896,8 +152280,14 @@ SQLITE_PRIVATE int wx_sqlite3WhereExplainOneScan( WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ u16 wctrlFlags /* Flags passed to wx_sqlite3WhereBegin() */ ); +SQLITE_PRIVATE int wx_sqlite3WhereExplainBloomFilter( + const Parse *pParse, /* Parse context */ + const WhereInfo *pWInfo, /* WHERE clause */ + const WhereLevel *pLevel /* Bloom filter on this level */ +); #else # define wx_sqlite3WhereExplainOneScan(u,v,w,x) 0 +# define wx_sqlite3WhereExplainBloomFilter(u,v,w) 0 #endif /* SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS SQLITE_PRIVATE void wx_sqlite3WhereAddScanStatus( @@ -143917,11 +152307,17 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( WhereLevel *pLevel, /* The current level pointer */ Bitmask notReady /* Which tables are currently available */ ); +SQLITE_PRIVATE SQLITE_NOINLINE void wx_sqlite3WhereRightJoinLoop( + WhereInfo *pWInfo, + int iLevel, + WhereLevel *pLevel +); /* whereexpr.c: */ SQLITE_PRIVATE void wx_sqlite3WhereClauseInit(WhereClause*,WhereInfo*); SQLITE_PRIVATE void wx_sqlite3WhereClauseClear(WhereClause*); SQLITE_PRIVATE void wx_sqlite3WhereSplit(WhereClause*,Expr*,u8); +SQLITE_PRIVATE void wx_sqlite3WhereAddLimit(WhereClause*, Select*); SQLITE_PRIVATE Bitmask wx_sqlite3WhereExprUsage(WhereMaskSet*, Expr*); SQLITE_PRIVATE Bitmask wx_sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*); SQLITE_PRIVATE Bitmask wx_sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*); @@ -143958,8 +152354,9 @@ SQLITE_PRIVATE void wx_sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*); #define WO_AND 0x0400 /* Two or more AND-connected terms */ #define WO_EQUIV 0x0800 /* Of the form A==B, both columns */ #define WO_NOOP 0x1000 /* This term does not restrict search space */ +#define WO_ROWVAL 0x2000 /* A row-value term */ -#define WO_ALL 0x1fff /* Mask of all possible WO_* values */ +#define WO_ALL 0x3fff /* Mask of all possible WO_* values */ #define WO_SINGLE 0x01ff /* Mask of all non-compound WO_* values */ /* @@ -143989,6 +152386,12 @@ SQLITE_PRIVATE void wx_sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*); #define WHERE_IN_EARLYOUT 0x00040000 /* Perhaps quit IN loops early */ #define WHERE_BIGNULL_SORT 0x00080000 /* Column nEq of index is BIGNULL */ #define WHERE_IN_SEEKSCAN 0x00100000 /* Seek-scan optimization for IN */ +#define WHERE_TRANSCONS 0x00200000 /* Uses a transitive constraint */ +#define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */ +#define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */ +#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */ +#define WHERE_VIEWSCAN 0x02000000 /* A full-scan of a VIEW or subquery */ +#define WHERE_EXPRIDX 0x04000000 /* Uses an index-on-expressions */ #endif /* !defined(SQLITE_WHEREINT_H) */ @@ -144004,7 +152407,7 @@ static const char *explainIndexColumnName(Index *pIdx, int i){ i = pIdx->aiColumn[i]; if( i==XN_EXPR ) return ""; if( i==XN_ROWID ) return "rowid"; - return pIdx->pTable->aCol[i].zName; + return pIdx->pTable->aCol[i].zCnName; } /* @@ -144123,16 +152526,8 @@ SQLITE_PRIVATE int wx_sqlite3WhereExplainOneScan( || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); wx_sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); - wx_sqlite3_str_appendall(&str, isSearch ? "SEARCH" : "SCAN"); - if( pItem->pSelect ){ - wx_sqlite3_str_appendf(&str, " SUBQUERY %u", pItem->pSelect->selId); - }else{ - wx_sqlite3_str_appendf(&str, " TABLE %s", pItem->zName); - } - - if( pItem->zAlias ){ - wx_sqlite3_str_appendf(&str, " AS %s", pItem->zAlias); - } + str.printfFlags = SQLITE_PRINTF_INTERNAL; + wx_sqlite3_str_appendf(&str, "%s %S", isSearch ? "SEARCH" : "SCAN", pItem); if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){ const char *zFmt = 0; Index *pIdx; @@ -144159,19 +152554,27 @@ SQLITE_PRIVATE int wx_sqlite3WhereExplainOneScan( explainIndexRange(&str, pLoop); } }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){ - const char *zRangeOp; + char cRangeOp; +#if 0 /* Better output, but breaks many tests */ + const Table *pTab = pItem->pTab; + const char *zRowid = pTab->iPKey>=0 ? pTab->aCol[pTab->iPKey].zCnName: + "rowid"; +#else + const char *zRowid = "rowid"; +#endif + wx_sqlite3_str_appendf(&str, " USING INTEGER PRIMARY KEY (%s", zRowid); if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){ - zRangeOp = "="; + cRangeOp = '='; }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){ - zRangeOp = ">? AND rowid<"; + wx_sqlite3_str_appendf(&str, ">? AND %s", zRowid); + cRangeOp = '<'; }else if( flags&WHERE_BTM_LIMIT ){ - zRangeOp = ">"; + cRangeOp = '>'; }else{ assert( flags&WHERE_TOP_LIMIT); - zRangeOp = "<"; + cRangeOp = '<'; } - wx_sqlite3_str_appendf(&str, - " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp); + wx_sqlite3_str_appendf(&str, "%c?)", cRangeOp); } #ifndef SQLITE_OMIT_VIRTUALTABLE else if( (flags & WHERE_VIRTUALTABLE)!=0 ){ @@ -144179,6 +152582,9 @@ SQLITE_PRIVATE int wx_sqlite3WhereExplainOneScan( pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr); } #endif + if( pItem->fg.jointype & JT_LEFT ){ + wx_sqlite3_str_appendf(&str, " LEFT-JOIN"); + } #ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS if( pLoop->nOut>=10 ){ wx_sqlite3_str_appendf(&str, " (~%llu rows)", @@ -144194,6 +152600,58 @@ SQLITE_PRIVATE int wx_sqlite3WhereExplainOneScan( } return ret; } + +/* +** Add a single OP_Explain opcode that describes a Bloom filter. +** +** Or if not processing EXPLAIN QUERY PLAN and not in a SQLITE_DEBUG and/or +** SQLITE_ENABLE_STMT_SCANSTATUS build, then OP_Explain opcodes are not +** required and this routine is a no-op. +** +** If an OP_Explain opcode is added to the VM, its address is returned. +** Otherwise, if no OP_Explain is coded, zero is returned. +*/ +SQLITE_PRIVATE int wx_sqlite3WhereExplainBloomFilter( + const Parse *pParse, /* Parse context */ + const WhereInfo *pWInfo, /* WHERE clause */ + const WhereLevel *pLevel /* Bloom filter on this level */ +){ + int ret = 0; + SrcItem *pItem = &pWInfo->pTabList->a[pLevel->iFrom]; + Vdbe *v = pParse->pVdbe; /* VM being constructed */ + wx_sqlite3 *db = pParse->db; /* Database handle */ + char *zMsg; /* Text to add to EQP output */ + int i; /* Loop counter */ + WhereLoop *pLoop; /* The where loop */ + StrAccum str; /* EQP output string */ + char zBuf[100]; /* Initial space for EQP output string */ + + wx_sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); + str.printfFlags = SQLITE_PRINTF_INTERNAL; + wx_sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem); + pLoop = pLevel->pWLoop; + if( pLoop->wsFlags & WHERE_IPK ){ + const Table *pTab = pItem->pTab; + if( pTab->iPKey>=0 ){ + wx_sqlite3_str_appendf(&str, "%s=?", pTab->aCol[pTab->iPKey].zCnName); + }else{ + wx_sqlite3_str_appendf(&str, "rowid=?"); + } + }else{ + for(i=pLoop->nSkip; iu.btree.nEq; i++){ + const char *z = explainIndexColumnName(pLoop->u.btree.pIndex, i); + if( i>pLoop->nSkip ) wx_sqlite3_str_append(&str, " AND ", 5); + wx_sqlite3_str_appendf(&str, "%s=?", z); + } + } + wx_sqlite3_str_append(&str, ")", 1); + zMsg = wx_sqlite3StrAccumFinish(&str); + ret = wx_sqlite3VdbeAddOp4(v, OP_Explain, wx_sqlite3VdbeCurrentAddr(v), + pParse->addrExplain, 0, zMsg,P4_DYNAMIC); + + wx_sqlite3VdbeScanStatus(v, wx_sqlite3VdbeCurrentAddr(v)-1, 0, 0, 0, 0); + return ret; +} #endif /* SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS @@ -144214,14 +152672,27 @@ SQLITE_PRIVATE void wx_sqlite3WhereAddScanStatus( ){ const char *zObj = 0; WhereLoop *pLoop = pLvl->pWLoop; - if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){ + int wsFlags = pLoop->wsFlags; + int viaCoroutine = 0; + + if( (wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){ zObj = pLoop->u.btree.pIndex->zName; }else{ zObj = pSrclist->a[pLvl->iFrom].zName; + viaCoroutine = pSrclist->a[pLvl->iFrom].fg.viaCoroutine; } wx_sqlite3VdbeScanStatus( v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj ); + + if( viaCoroutine==0 ){ + if( (wsFlags & (WHERE_MULTI_OR|WHERE_AUTO_INDEX))==0 ){ + wx_sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur); + } + if( wsFlags & WHERE_INDEXED ){ + wx_sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur); + } + } } #endif @@ -144272,7 +152743,7 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ int nLoop = 0; assert( pTerm!=0 ); while( (pTerm->wtFlags & TERM_CODED)==0 - && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin)) + && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_OuterON)) && (pLevel->notReady & pTerm->prereqAll)==0 ){ if( nLoop && (pTerm->wtFlags & TERM_LIKE)!=0 ){ @@ -144280,6 +152751,12 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ }else{ pTerm->wtFlags |= TERM_CODED; } +#ifdef WHERETRACE_ENABLED + if( (wx_sqlite3WhereTrace & 0x4001)==0x4001 ){ + wx_sqlite3DebugPrintf("DISABLE-"); + wx_sqlite3WhereTermPrint(pTerm, (int)(pTerm - (pTerm->pWC->a))); + } +#endif if( pTerm->iParent<0 ) break; pTerm = &pTerm->pWC->a[pTerm->iParent]; assert( pTerm!=0 ); @@ -144390,61 +152867,75 @@ static Expr *removeUnindexableInClauseTerms( Expr *pX /* The IN expression to be reduced */ ){ wx_sqlite3 *db = pParse->db; + Select *pSelect; /* Pointer to the SELECT on the RHS */ Expr *pNew; pNew = wx_sqlite3ExprDup(db, pX, 0); if( db->mallocFailed==0 ){ - ExprList *pOrigRhs = pNew->x.pSelect->pEList; /* Original unmodified RHS */ - ExprList *pOrigLhs = pNew->pLeft->x.pList; /* Original unmodified LHS */ - ExprList *pRhs = 0; /* New RHS after modifications */ - ExprList *pLhs = 0; /* New LHS after mods */ - int i; /* Loop counter */ - Select *pSelect; /* Pointer to the SELECT on the RHS */ - - for(i=iEq; inLTerm; i++){ - if( pLoop->aLTerm[i]->pExpr==pX ){ - int iField = pLoop->aLTerm[i]->u.x.iField - 1; - if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */ - pRhs = wx_sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr); - pOrigRhs->a[iField].pExpr = 0; - assert( pOrigLhs->a[iField].pExpr!=0 ); - pLhs = wx_sqlite3ExprListAppend(pParse, pLhs, pOrigLhs->a[iField].pExpr); - pOrigLhs->a[iField].pExpr = 0; - } - } - wx_sqlite3ExprListDelete(db, pOrigRhs); - wx_sqlite3ExprListDelete(db, pOrigLhs); - pNew->pLeft->x.pList = pLhs; - pNew->x.pSelect->pEList = pRhs; - if( pLhs && pLhs->nExpr==1 ){ - /* Take care here not to generate a TK_VECTOR containing only a - ** single value. Since the parser never creates such a vector, some - ** of the subroutines do not handle this case. */ - Expr *p = pLhs->a[0].pExpr; - pLhs->a[0].pExpr = 0; - wx_sqlite3ExprDelete(db, pNew->pLeft); - pNew->pLeft = p; - } - pSelect = pNew->x.pSelect; - if( pSelect->pOrderBy ){ - /* If the SELECT statement has an ORDER BY clause, zero the - ** iOrderByCol variables. These are set to non-zero when an - ** ORDER BY term exactly matches one of the terms of the - ** result-set. Since the result-set of the SELECT statement may - ** have been modified or reordered, these variables are no longer - ** set correctly. Since setting them is just an optimization, - ** it's easiest just to zero them here. */ - ExprList *pOrderBy = pSelect->pOrderBy; - for(i=0; inExpr; i++){ - pOrderBy->a[i].u.x.iOrderByCol = 0; + for(pSelect=pNew->x.pSelect; pSelect; pSelect=pSelect->pPrior){ + ExprList *pOrigRhs; /* Original unmodified RHS */ + ExprList *pOrigLhs = 0; /* Original unmodified LHS */ + ExprList *pRhs = 0; /* New RHS after modifications */ + ExprList *pLhs = 0; /* New LHS after mods */ + int i; /* Loop counter */ + + assert( ExprUseXSelect(pNew) ); + pOrigRhs = pSelect->pEList; + assert( pNew->pLeft!=0 ); + assert( ExprUseXList(pNew->pLeft) ); + if( pSelect==pNew->x.pSelect ){ + pOrigLhs = pNew->pLeft->x.pList; + } + for(i=iEq; inLTerm; i++){ + if( pLoop->aLTerm[i]->pExpr==pX ){ + int iField; + assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 ); + iField = pLoop->aLTerm[i]->u.x.iField - 1; + if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */ + pRhs = wx_sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr); + pOrigRhs->a[iField].pExpr = 0; + if( pOrigLhs ){ + assert( pOrigLhs->a[iField].pExpr!=0 ); + pLhs = wx_sqlite3ExprListAppend(pParse,pLhs,pOrigLhs->a[iField].pExpr); + pOrigLhs->a[iField].pExpr = 0; + } + } + } + wx_sqlite3ExprListDelete(db, pOrigRhs); + if( pOrigLhs ){ + wx_sqlite3ExprListDelete(db, pOrigLhs); + pNew->pLeft->x.pList = pLhs; + } + pSelect->pEList = pRhs; + if( pLhs && pLhs->nExpr==1 ){ + /* Take care here not to generate a TK_VECTOR containing only a + ** single value. Since the parser never creates such a vector, some + ** of the subroutines do not handle this case. */ + Expr *p = pLhs->a[0].pExpr; + pLhs->a[0].pExpr = 0; + wx_sqlite3ExprDelete(db, pNew->pLeft); + pNew->pLeft = p; + } + if( pSelect->pOrderBy ){ + /* If the SELECT statement has an ORDER BY clause, zero the + ** iOrderByCol variables. These are set to non-zero when an + ** ORDER BY term exactly matches one of the terms of the + ** result-set. Since the result-set of the SELECT statement may + ** have been modified or reordered, these variables are no longer + ** set correctly. Since setting them is just an optimization, + ** it's easiest just to zero them here. */ + ExprList *pOrderBy = pSelect->pOrderBy; + for(i=0; inExpr; i++){ + pOrderBy->a[i].u.x.iOrderByCol = 0; + } } - } #if 0 - printf("For indexing, change the IN expr:\n"); - wx_sqlite3TreeViewExpr(0, pX, 0); - printf("Into:\n"); - wx_sqlite3TreeViewExpr(0, pNew, 0); + printf("For indexing, change the IN expr:\n"); + wx_sqlite3TreeViewExpr(0, pX, 0); + printf("Into:\n"); + wx_sqlite3TreeViewExpr(0, pNew, 0); #endif + } } return pNew; } @@ -144517,19 +153008,25 @@ static int codeEqualityTerm( } iTab = 0; - if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){ + if( !ExprUseXSelect(pX) || pX->x.pSelect->pEList->nExpr==1 ){ eType = wx_sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab); }else{ - wx_sqlite3 *db = pParse->db; - pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); - - if( !db->mallocFailed ){ - aiMap = (int*)wx_sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); + Expr *pExpr = pTerm->pExpr; + if( pExpr->iTable==0 || !ExprHasProperty(pExpr, EP_Subrtn) ){ + wx_sqlite3 *db = pParse->db; + pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); + if( !db->mallocFailed ){ + aiMap = (int*)wx_sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); + eType = wx_sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap,&iTab); + pExpr->iTable = iTab; + } + wx_sqlite3ExprDelete(db, pX); + }else{ + int n = wx_sqlite3ExprVectorSize(pX->pLeft); + aiMap = (int*)wx_sqlite3DbMallocZero(pParse->db, sizeof(int)*MAX(nEq,n)); eType = wx_sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab); - pTerm->pExpr->iTable = iTab; } - wx_sqlite3ExprDelete(db, pX); - pX = pTerm->pExpr; + pX = pExpr; } if( eType==IN_INDEX_INDEX_DESC ){ @@ -144539,8 +153036,8 @@ static int codeEqualityTerm( wx_sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); VdbeCoverageIf(v, bRev); VdbeCoverageIf(v, !bRev); - assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); + assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); pLoop->wsFlags |= WHERE_IN_ABLE; if( pLevel->u.in.nIn==0 ){ pLevel->addrNxt = wx_sqlite3VdbeMakeLabel(pParse); @@ -144552,8 +153049,9 @@ static int codeEqualityTerm( i = pLevel->u.in.nIn; pLevel->u.in.nIn += nEq; pLevel->u.in.aInLoop = - wx_sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop, - sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn); + wx_sqlite3WhereRealloc(pTerm->pWC->pWInfo, + pLevel->u.in.aInLoop, + sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn); pIn = pLevel->u.in.aInLoop; if( pIn ){ int iMap = 0; /* Index in aiMap[] */ @@ -144597,7 +153095,22 @@ static int codeEqualityTerm( wx_sqlite3DbFree(pParse->db, aiMap); #endif } - disableTerm(pLevel, pTerm); + + /* As an optimization, try to disable the WHERE clause term that is + ** driving the index as it will always be true. The correct answer is + ** obtained regardless, but we might get the answer with fewer CPU cycles + ** by omitting the term. + ** + ** But do not disable the term unless we are certain that the term is + ** not a transitive constraint. For an example of where that does not + ** work, see https://sqlite.org/forum/forumpost/eb8613976a (2021-05-04) + */ + if( (pLevel->pWLoop->wsFlags & WHERE_TRANSCONS)==0 + || (pTerm->eOperator & WO_EQUIV)==0 + ){ + disableTerm(pLevel, pTerm); + } + return iReg; } @@ -144683,11 +153196,13 @@ static int codeAllEqualityTerms( if( nSkip ){ int iIdxCur = pLevel->iIdxCur; + wx_sqlite3VdbeAddOp3(v, OP_Null, 0, regBase, regBase+nSkip-1); wx_sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur); VdbeCoverageIf(v, bRev==0); VdbeCoverageIf(v, bRev!=0); VdbeComment((v, "begin skip-scan on %s", pIdx->zName)); j = wx_sqlite3VdbeAddOp0(v, OP_Goto); + assert( pLevel->addrSkip==0 ); pLevel->addrSkip = wx_sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLT:OP_SeekGT), iIdxCur, 0, regBase, nSkip); VdbeCoverageIf(v, bRev==0); @@ -144717,9 +153232,12 @@ static int codeAllEqualityTerms( wx_sqlite3ReleaseTempReg(pParse, regBase); regBase = r1; }else{ - wx_sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j); + wx_sqlite3VdbeAddOp2(v, OP_Copy, r1, regBase+j); } } + } + for(j=nSkip; jaLTerm[j]; if( pTerm->eOperator & WO_IN ){ if( pTerm->pExpr->flags & EP_xIsSelect ){ /* No affinity ever needs to be (or should be) applied to a value @@ -144734,7 +153252,8 @@ static int codeAllEqualityTerms( wx_sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); VdbeCoverage(v); } - if( zAff ){ + if( pParse->nErr==0 ){ + assert( pParse->db->mallocFailed==0 ); if( wx_sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){ zAff[j] = SQLITE_AFF_BLOB; } @@ -144774,7 +153293,7 @@ static void whereLikeOptimizationStringFixup( if( pTerm->wtFlags & TERM_LIKEOPT ){ VdbeOp *pOp; assert( pLevel->iLikeRepCntr>0 ); - pOp = wx_sqlite3VdbeGetOp(v, -1); + pOp = wx_sqlite3VdbeGetLastOp(v); assert( pOp!=0 ); assert( pOp->opcode==OP_String8 || pTerm->pWC->pWInfo->pParse->db->mallocFailed ); @@ -144924,7 +153443,7 @@ static void codeCursorHint( sWalker.pParse = pParse; sWalker.u.pCCurHint = &sHint; pWC = &pWInfo->sWC; - for(i=0; inTerm; i++){ + for(i=0; inBase; i++){ pTerm = &pWC->a[i]; if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( pTerm->prereqAll & pLevel->notReady ) continue; @@ -144953,8 +153472,8 @@ static void codeCursorHint( */ if( pTabItem->fg.jointype & JT_LEFT ){ Expr *pExpr = pTerm->pExpr; - if( !ExprHasProperty(pExpr, EP_FromJoin) - || pExpr->iRightJoinTable!=pTabItem->iCursor + if( !ExprHasProperty(pExpr, EP_OuterON) + || pExpr->w.iJoin!=pTabItem->iCursor ){ sWalker.eCode = 0; sWalker.xExprCallback = codeCursorHintIsOrFunction; @@ -144962,7 +153481,7 @@ static void codeCursorHint( if( sWalker.eCode ) continue; } }else{ - if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue; + if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) continue; } /* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize @@ -145010,13 +153529,21 @@ static void codeCursorHint( ** ** OP_DeferredSeek $iCur $iRowid ** +** Which causes a seek on $iCur to the row with rowid $iRowid. +** ** However, if the scan currently being coded is a branch of an OR-loop and -** the statement currently being coded is a SELECT, then P3 of OP_DeferredSeek -** is set to iIdxCur and P4 is set to point to an array of integers -** containing one entry for each column of the table cursor iCur is open -** on. For each table column, if the column is the i'th column of the -** index, then the corresponding array entry is set to (i+1). If the column -** does not appear in the index at all, the array entry is set to 0. +** the statement currently being coded is a SELECT, then additional information +** is added that might allow OP_Column to omit the seek and instead do its +** lookup on the index, thus avoiding an expensive seek operation. To +** enable this optimization, the P3 of OP_DeferredSeek is set to iIdxCur +** and P4 is set to an array of integers containing one entry for each column +** in the table. For each table column, if the column is the i'th +** column of the index, then the corresponding array entry is set to (i+1). +** If the column does not appear in the index at all, the array entry is set +** to 0. The OP_Column opcode can check this array to see if the column it +** wants is in the index and if it is, it will substitute the index cursor +** and column number and continue with those new values, rather than seeking +** the table cursor. */ static void codeDeferredSeek( WhereInfo *pWInfo, /* Where clause context */ @@ -145032,7 +153559,7 @@ static void codeDeferredSeek( pWInfo->bDeferredSeek = 1; wx_sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur); - if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) + if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) && DbMaskAllZero(wx_sqlite3ParseToplevel(pParse)->writeMask) ){ int i; @@ -145066,7 +153593,7 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ assert( nReg>0 ); if( p && wx_sqlite3ExprIsVector(p) ){ #ifndef SQLITE_OMIT_SUBQUERY - if( (p->flags & EP_xIsSelect) ){ + if( ExprUseXSelect(p) ){ Vdbe *v = pParse->pVdbe; int iSelect; assert( p->op==TK_SELECT ); @@ -145076,154 +153603,20 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ #endif { int i; - ExprList *pList = p->x.pList; + const ExprList *pList; + assert( ExprUseXList(p) ); + pList = p->x.pList; assert( nReg<=pList->nExpr ); for(i=0; ia[i].pExpr, iReg+i); } } }else{ - assert( nReg==1 ); + assert( nReg==1 || pParse->nErr ); wx_sqlite3ExprCode(pParse, p, iReg); } } -/* An instance of the IdxExprTrans object carries information about a -** mapping from an expression on table columns into a column in an index -** down through the Walker. -*/ -typedef struct IdxExprTrans { - Expr *pIdxExpr; /* The index expression */ - int iTabCur; /* The cursor of the corresponding table */ - int iIdxCur; /* The cursor for the index */ - int iIdxCol; /* The column for the index */ - int iTabCol; /* The column for the table */ - WhereInfo *pWInfo; /* Complete WHERE clause information */ - wx_sqlite3 *db; /* Database connection (for malloc()) */ -} IdxExprTrans; - -/* -** Preserve pExpr on the WhereETrans list of the WhereInfo. -*/ -static void preserveExpr(IdxExprTrans *pTrans, Expr *pExpr){ - WhereExprMod *pNew; - pNew = wx_sqlite3DbMallocRaw(pTrans->db, sizeof(*pNew)); - if( pNew==0 ) return; - pNew->pNext = pTrans->pWInfo->pExprMods; - pTrans->pWInfo->pExprMods = pNew; - pNew->pExpr = pExpr; - memcpy(&pNew->orig, pExpr, sizeof(*pExpr)); -} - -/* The walker node callback used to transform matching expressions into -** a reference to an index column for an index on an expression. -** -** If pExpr matches, then transform it into a reference to the index column -** that contains the value of pExpr. -*/ -static int whereIndexExprTransNode(Walker *p, Expr *pExpr){ - IdxExprTrans *pX = p->u.pIdxTrans; - if( wx_sqlite3ExprCompare(0, pExpr, pX->pIdxExpr, pX->iTabCur)==0 ){ - preserveExpr(pX, pExpr); - pExpr->affExpr = wx_sqlite3ExprAffinity(pExpr); - pExpr->op = TK_COLUMN; - pExpr->iTable = pX->iIdxCur; - pExpr->iColumn = pX->iIdxCol; - pExpr->y.pTab = 0; - testcase( ExprHasProperty(pExpr, EP_Skip) ); - testcase( ExprHasProperty(pExpr, EP_Unlikely) ); - ExprClearProperty(pExpr, EP_Skip|EP_Unlikely); - return WRC_Prune; - }else{ - return WRC_Continue; - } -} - -#ifndef SQLITE_OMIT_GENERATED_COLUMNS -/* A walker node callback that translates a column reference to a table -** into a corresponding column reference of an index. -*/ -static int whereIndexExprTransColumn(Walker *p, Expr *pExpr){ - if( pExpr->op==TK_COLUMN ){ - IdxExprTrans *pX = p->u.pIdxTrans; - if( pExpr->iTable==pX->iTabCur && pExpr->iColumn==pX->iTabCol ){ - assert( pExpr->y.pTab!=0 ); - preserveExpr(pX, pExpr); - pExpr->affExpr = wx_sqlite3TableColumnAffinity(pExpr->y.pTab,pExpr->iColumn); - pExpr->iTable = pX->iIdxCur; - pExpr->iColumn = pX->iIdxCol; - pExpr->y.pTab = 0; - } - } - return WRC_Continue; -} -#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ - -/* -** For an indexes on expression X, locate every instance of expression X -** in pExpr and change that subexpression into a reference to the appropriate -** column of the index. -** -** 2019-10-24: Updated to also translate references to a VIRTUAL column in -** the table into references to the corresponding (stored) column of the -** index. -*/ -static void whereIndexExprTrans( - Index *pIdx, /* The Index */ - int iTabCur, /* Cursor of the table that is being indexed */ - int iIdxCur, /* Cursor of the index itself */ - WhereInfo *pWInfo /* Transform expressions in this WHERE clause */ -){ - int iIdxCol; /* Column number of the index */ - ExprList *aColExpr; /* Expressions that are indexed */ - Table *pTab; - Walker w; - IdxExprTrans x; - aColExpr = pIdx->aColExpr; - if( aColExpr==0 && !pIdx->bHasVCol ){ - /* The index does not reference any expressions or virtual columns - ** so no translations are needed. */ - return; - } - pTab = pIdx->pTable; - memset(&w, 0, sizeof(w)); - w.u.pIdxTrans = &x; - x.iTabCur = iTabCur; - x.iIdxCur = iIdxCur; - x.pWInfo = pWInfo; - x.db = pWInfo->pParse->db; - for(iIdxCol=0; iIdxColnColumn; iIdxCol++){ - i16 iRef = pIdx->aiColumn[iIdxCol]; - if( iRef==XN_EXPR ){ - assert( aColExpr->a[iIdxCol].pExpr!=0 ); - x.pIdxExpr = aColExpr->a[iIdxCol].pExpr; - if( wx_sqlite3ExprIsConstant(x.pIdxExpr) ) continue; - w.xExprCallback = whereIndexExprTransNode; -#ifndef SQLITE_OMIT_GENERATED_COLUMNS - }else if( iRef>=0 - && (pTab->aCol[iRef].colFlags & COLFLAG_VIRTUAL)!=0 - && (pTab->aCol[iRef].zColl==0 - || wx_sqlite3StrICmp(pTab->aCol[iRef].zColl, wx_sqlite3StrBINARY)==0) - ){ - /* Check to see if there are direct references to generated columns - ** that are contained in the index. Pulling the generated column - ** out of the index is an optimization only - the main table is always - ** available if the index cannot be used. To avoid unnecessary - ** complication, omit this optimization if the collating sequence for - ** the column is non-standard */ - x.iTabCol = iRef; - w.xExprCallback = whereIndexExprTransColumn; -#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ - }else{ - continue; - } - x.iIdxCol = iIdxCol; - wx_sqlite3WalkExpr(&w, pWInfo->pWhere); - wx_sqlite3WalkExprList(&w, pWInfo->pOrderBy); - wx_sqlite3WalkExprList(&w, pWInfo->pResultSet); - } -} - /* ** The pTruth expression is always true because it is the WHERE clause ** a partial index that is driving a query loop. Look through all of the @@ -145252,6 +153645,70 @@ static void whereApplyPartialIndexConstraints( } } +/* +** This routine is called right after An OP_Filter has been generated and +** before the corresponding index search has been performed. This routine +** checks to see if there are additional Bloom filters in inner loops that +** can be checked prior to doing the index lookup. If there are available +** inner-loop Bloom filters, then evaluate those filters now, before the +** index lookup. The idea is that a Bloom filter check is way faster than +** an index lookup, and the Bloom filter might return false, meaning that +** the index lookup can be skipped. +** +** We know that an inner loop uses a Bloom filter because it has the +** WhereLevel.regFilter set. If an inner-loop Bloom filter is checked, +** then clear the WhereLevel.regFilter value to prevent the Bloom filter +** from being checked a second time when the inner loop is evaluated. +*/ +static SQLITE_NOINLINE void filterPullDown( + Parse *pParse, /* Parsing context */ + WhereInfo *pWInfo, /* Complete information about the WHERE clause */ + int iLevel, /* Which level of pWInfo->a[] should be coded */ + int addrNxt, /* Jump here to bypass inner loops */ + Bitmask notReady /* Loops that are not ready */ +){ + while( ++iLevel < pWInfo->nLevel ){ + WhereLevel *pLevel = &pWInfo->a[iLevel]; + WhereLoop *pLoop = pLevel->pWLoop; + if( pLevel->regFilter==0 ) continue; + if( pLevel->pWLoop->nSkip ) continue; + /* ,--- Because wx_sqlite3ConstructBloomFilter() has will not have set + ** vvvvv--' pLevel->regFilter if this were true. */ + if( NEVER(pLoop->prereq & notReady) ) continue; + assert( pLevel->addrBrk==0 ); + pLevel->addrBrk = addrNxt; + if( pLoop->wsFlags & WHERE_IPK ){ + WhereTerm *pTerm = pLoop->aLTerm[0]; + int regRowid; + assert( pTerm!=0 ); + assert( pTerm->pExpr!=0 ); + testcase( pTerm->wtFlags & TERM_VIRTUAL ); + regRowid = wx_sqlite3GetTempReg(pParse); + regRowid = codeEqualityTerm(pParse, pTerm, pLevel, 0, 0, regRowid); + wx_sqlite3VdbeAddOp2(pParse->pVdbe, OP_MustBeInt, regRowid, addrNxt); + VdbeCoverage(pParse->pVdbe); + wx_sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter, + addrNxt, regRowid, 1); + VdbeCoverage(pParse->pVdbe); + }else{ + u16 nEq = pLoop->u.btree.nEq; + int r1; + char *zStartAff; + + assert( pLoop->wsFlags & WHERE_INDEXED ); + assert( (pLoop->wsFlags & WHERE_COLUMN_IN)==0 ); + r1 = codeAllEqualityTerms(pParse,pLevel,0,0,&zStartAff); + codeApplyAffinity(pParse, r1, nEq, zStartAff); + wx_sqlite3DbFree(pParse->db, zStartAff); + wx_sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter, + addrNxt, r1, nEq); + VdbeCoverage(pParse->pVdbe); + } + pLevel->regFilter = 0; + pLevel->addrBrk = 0; + } +} + /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. @@ -145289,13 +153746,15 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( pLevel->notReady = notReady & ~wx_sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); bRev = (pWInfo->revMask>>iLevel)&1; VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName)); -#if WHERETRACE_ENABLED /* 0x20800 */ - if( wx_sqlite3WhereTrace & 0x800 ){ +#if WHERETRACE_ENABLED /* 0x4001 */ + if( wx_sqlite3WhereTrace & 0x1 ){ wx_sqlite3DebugPrintf("Coding level %d of %d: notReady=%llx iFrom=%d\n", iLevel, pWInfo->nLevel, (u64)notReady, pLevel->iFrom); - wx_sqlite3WhereLoopPrint(pLoop, pWC); + if( wx_sqlite3WhereTrace & 0x1000 ){ + wx_sqlite3WhereLoopPrint(pLoop, pWC); + } } - if( wx_sqlite3WhereTrace & 0x20000 ){ + if( (wx_sqlite3WhereTrace & 0x4001)==0x4001 ){ if( iLevel==0 ){ wx_sqlite3DebugPrintf("WHERE clause being coded:\n"); wx_sqlite3TreeViewExpr(0, pWInfo->pWhere, 0); @@ -145322,7 +153781,7 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( ** initialize a memory cell that records if this table matches any ** row of the left table of the join. */ - assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) + assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) || pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0 ); if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ @@ -145333,7 +153792,10 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( /* Compute a safe address to jump to if we discover that the table for ** this loop is empty and can never contribute content. */ - for(j=iLevel; j>0 && pWInfo->a[j].iLeftJoin==0; j--){} + for(j=iLevel; j>0; j--){ + if( pWInfo->a[j].iLeftJoin ) break; + if( pWInfo->a[j].pRJ ) break; + } addrHalt = pWInfo->a[j].addrBrk; /* Special case of a FROM clause subquery implemented as a co-routine */ @@ -145354,7 +153816,6 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( int iReg; /* P3 Value for OP_VFilter */ int addrNotFound; int nConstraint = pLoop->nLTerm; - int iIn; /* Counter for IN constraints */ iReg = wx_sqlite3GetTempRange(pParse, nConstraint+2); addrNotFound = pLevel->addrBrk; @@ -145363,11 +153824,27 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( pTerm = pLoop->aLTerm[j]; if( NEVER(pTerm==0) ) continue; if( pTerm->eOperator & WO_IN ){ - codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); - addrNotFound = pLevel->addrNxt; + if( SMASKBIT32(j) & pLoop->u.vtab.mHandleIn ){ + int iTab = pParse->nTab++; + int iCache = ++pParse->nMem; + wx_sqlite3CodeRhsOfIN(pParse, pTerm->pExpr, iTab); + wx_sqlite3VdbeAddOp3(v, OP_VInitIn, iTab, iTarget, iCache); + }else{ + codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); + addrNotFound = pLevel->addrNxt; + } }else{ Expr *pRight = pTerm->pExpr->pRight; codeExprOrVector(pParse, pRight, iTarget, 1); + if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET + && pLoop->u.vtab.bOmitOffset + ){ + assert( pTerm->eOperator==WO_AUX ); + assert( pWInfo->pSelect!=0 ); + assert( pWInfo->pSelect->iOffset>0 ); + wx_sqlite3VdbeAddOp2(v, OP_Integer, 0, pWInfo->pSelect->iOffset); + VdbeComment((v,"Zero OFFSET counter")); + } } } wx_sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); @@ -145383,40 +153860,55 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( pLevel->p1 = iCur; pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext; pLevel->p2 = wx_sqlite3VdbeCurrentAddr(v); - iIn = pLevel->u.in.nIn; - for(j=nConstraint-1; j>=0; j--){ + assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); + + for(j=0; jaLTerm[j]; - if( (pTerm->eOperator & WO_IN)!=0 ) iIn--; if( j<16 && (pLoop->u.vtab.omitMask>>j)&1 ){ disableTerm(pLevel, pTerm); - }else if( (pTerm->eOperator & WO_IN)!=0 - && wx_sqlite3ExprVectorSize(pTerm->pExpr->pLeft)==1 + continue; + } + if( (pTerm->eOperator & WO_IN)!=0 + && (SMASKBIT32(j) & pLoop->u.vtab.mHandleIn)==0 + && !db->mallocFailed ){ Expr *pCompare; /* The comparison operator */ Expr *pRight; /* RHS of the comparison */ VdbeOp *pOp; /* Opcode to access the value of the IN constraint */ + int iIn; /* IN loop corresponding to the j-th constraint */ /* Reload the constraint value into reg[iReg+j+2]. The same value ** was loaded into the same register prior to the OP_VFilter, but ** the xFilter implementation might have changed the datatype or - ** encoding of the value in the register, so it *must* be reloaded. */ - assert( pLevel->u.in.aInLoop!=0 || db->mallocFailed ); - if( !db->mallocFailed ){ - assert( iIn>=0 && iInu.in.nIn ); + ** encoding of the value in the register, so it *must* be reloaded. + */ + for(iIn=0; ALWAYS(iInu.in.nIn); iIn++){ pOp = wx_sqlite3VdbeGetOp(v, pLevel->u.in.aInLoop[iIn].addrInTop); - assert( pOp->opcode==OP_Column || pOp->opcode==OP_Rowid ); - assert( pOp->opcode!=OP_Column || pOp->p3==iReg+j+2 ); - assert( pOp->opcode!=OP_Rowid || pOp->p2==iReg+j+2 ); - testcase( pOp->opcode==OP_Rowid ); - wx_sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3); + if( (pOp->opcode==OP_Column && pOp->p3==iReg+j+2) + || (pOp->opcode==OP_Rowid && pOp->p2==iReg+j+2) + ){ + testcase( pOp->opcode==OP_Rowid ); + wx_sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3); + break; + } } /* Generate code that will continue to the next row if - ** the IN constraint is not satisfied */ + ** the IN constraint is not satisfied + */ pCompare = wx_sqlite3PExpr(pParse, TK_EQ, 0, 0); - assert( pCompare!=0 || db->mallocFailed ); - if( pCompare ){ - pCompare->pLeft = pTerm->pExpr->pLeft; + if( !db->mallocFailed ){ + int iFld = pTerm->u.x.iField; + Expr *pLeft = pTerm->pExpr->pLeft; + assert( pLeft!=0 ); + if( iFld>0 ){ + assert( pLeft->op==TK_VECTOR ); + assert( ExprUseXList(pLeft) ); + assert( iFld<=pLeft->x.pList->nExpr ); + pCompare->pLeft = pLeft->x.pList->a[iFld-1].pExpr; + }else{ + pCompare->pLeft = pLeft; + } pCompare->pRight = pRight = wx_sqlite3Expr(db, TK_REGISTER, 0); if( pRight ){ pRight->iTable = iReg+j+2; @@ -145425,11 +153917,11 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( ); } pCompare->pLeft = 0; - wx_sqlite3ExprDelete(db, pCompare); } + wx_sqlite3ExprDelete(db, pCompare); } } - assert( iIn==0 || db->mallocFailed ); + /* These registers need to be preserved in case there is an IN operator ** loop. So we could deallocate the registers here (and potentially ** reuse them later) if (pLoop->wsFlags & WHERE_IN_ABLE)==0. But it seems @@ -145457,12 +153949,17 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg); if( iRowidReg!=iReleaseReg ) wx_sqlite3ReleaseTempReg(pParse, iReleaseReg); addrNxt = pLevel->addrNxt; + if( pLevel->regFilter ){ + wx_sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); + VdbeCoverage(v); + wx_sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt, + iRowidReg, 1); + VdbeCoverage(v); + filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady); + } wx_sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg); VdbeCoverage(v); pLevel->op = OP_Noop; - if( (pTerm->prereqAll & pLevel->notReady)==0 ){ - pTerm->wtFlags |= TERM_CODED; - } }else if( (pLoop->wsFlags & WHERE_IPK)!=0 && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0 ){ @@ -145710,9 +154207,7 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( ** a forward order scan on a descending index, interchange the ** start and end terms (pRangeStart and pRangeEnd). */ - if( (nEqnKeyCol && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) - || (bRev && pIdx->nKeyCol==nEq) - ){ + if( (nEqnColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) ){ SWAP(WhereTerm *, pRangeEnd, pRangeStart); SWAP(u8, bSeekPastNull, bStopAtNull); SWAP(u8, nBtm, nTop); @@ -145787,6 +154282,12 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( wx_sqlite3VdbeAddOp2(v, OP_Integer, 1, regBignull); VdbeComment((v, "NULL-scan pass ctr")); } + if( pLevel->regFilter ){ + wx_sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt, + regBase, nEq); + VdbeCoverage(v); + filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady); + } op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; assert( op!=0 ); @@ -145802,6 +154303,11 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( ** guess. */ addrSeekScan = wx_sqlite3VdbeAddOp1(v, OP_SeekScan, (pIdx->aiRowLogEst[0]+9)/10); + if( pRangeStart ){ + wx_sqlite3VdbeChangeP5(v, 1); + wx_sqlite3VdbeChangeP2(v, addrSeekScan, wx_sqlite3VdbeCurrentAddr(v)+1); + addrSeekScan = 0; + } VdbeCoverage(v); } wx_sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); @@ -145835,8 +154341,19 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( ** range (if any). */ nConstraint = nEq; + assert( pLevel->p2==0 ); if( pRangeEnd ){ Expr *pRight = pRangeEnd->pExpr->pRight; + if( addrSeekScan ){ + /* For a seek-scan that has a range on the lowest term of the index, + ** we have to make the top of the loop be code that sets the end + ** condition of the range. Otherwise, the OP_SeekScan might jump + ** over that initialization, leaving the range-end value set to the + ** range-start value, resulting in a wrong answer. + ** See ticket 5981a8c041a3c2f3 (2021-11-02). + */ + pLevel->p2 = wx_sqlite3VdbeCurrentAddr(v); + } codeExprOrVector(pParse, pRight, regBase+nEq, nTop); whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); if( (pRangeEnd->wtFlags & TERM_VNULL)==0 @@ -145866,11 +154383,11 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( } nConstraint++; } - wx_sqlite3DbFree(db, zStartAff); - wx_sqlite3DbFree(db, zEndAff); + if( zStartAff ) wx_sqlite3DbNNFreeNN(db, zStartAff); + if( zEndAff ) wx_sqlite3DbNNFreeNN(db, zEndAff); /* Top of the loop body */ - pLevel->p2 = wx_sqlite3VdbeCurrentAddr(v); + if( pLevel->p2==0 ) pLevel->p2 = wx_sqlite3VdbeCurrentAddr(v); /* Check if the index cursor is past the end of the range. */ if( nConstraint ){ @@ -145912,7 +154429,7 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( /* Seek the table cursor, if required */ omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0; + && (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0; if( omitTable ){ /* pIdx is a covering index. No need to access the main table. */ }else if( HasRowid(pIdx->pTable) ){ @@ -145929,27 +154446,6 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( } if( pLevel->iLeftJoin==0 ){ - /* If pIdx is an index on one or more expressions, then look through - ** all the expressions in pWInfo and try to transform matching expressions - ** into reference to index columns. Also attempt to translate references - ** to virtual columns in the table into references to (stored) columns - ** of the index. - ** - ** Do not do this for the RHS of a LEFT JOIN. This is because the - ** expression may be evaluated after OP_NullRow has been executed on - ** the cursor. In this case it is important to do the full evaluation, - ** as the result of the expression may not be NULL, even if all table - ** column values are. https://www.sqlite.org/src/info/7fa8049685b50b5a - ** - ** Also, do not do this when processing one index an a multi-index - ** OR clause, since the transformation will become invalid once we - ** move forward to the next index. - ** https://sqlite.org/src/info/4e8e4857d32d401f - */ - if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ - whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo); - } - /* If a partial index is driving the loop, try to eliminate WHERE clause ** terms from the query that must be true due to the WHERE clause of ** the partial index. @@ -145965,7 +154461,7 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( /* The following assert() is not a requirement, merely an observation: ** The OR-optimization doesn't work for the right hand table of ** a LEFT JOIN: */ - assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ); + assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ); } /* Record the instruction used to terminate the loop. */ @@ -146062,7 +154558,7 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( int nNotReady; /* The number of notReady tables */ SrcItem *origSrc; /* Original list of tables */ nNotReady = pWInfo->nLevel - iLevel - 1; - pOrTab = wx_sqlite3StackAllocRaw(db, + pOrTab = wx_sqlite3DbMallocRawNN(db, sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); if( pOrTab==0 ) return notReady; pOrTab->nAlloc = (u8)(nNotReady + 1); @@ -146103,7 +154599,7 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( iRetInit = wx_sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn); /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y - ** Then for every term xN, evaluate as the subexpression: xN AND z + ** Then for every term xN, evaluate as the subexpression: xN AND y ** That way, terms in y that are factored into the disjunction will ** be picked up by the recursive calls to wx_sqlite3WhereBegin() below. ** @@ -146115,6 +154611,20 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( ** This optimization also only applies if the (x1 OR x2 OR ...) term ** is not contained in the ON clause of a LEFT JOIN. ** See ticket http://www.sqlite.org/src/info/f2369304e4 + ** + ** 2022-02-04: Do not push down slices of a row-value comparison. + ** In other words, "w" or "y" may not be a slice of a vector. Otherwise, + ** the initialization of the right-hand operand of the vector comparison + ** might not occur, or might occur only in an OR branch that is not + ** taken. dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1. + ** + ** 2022-03-03: Do not push down expressions that involve subqueries. + ** The subquery might get coded as a subroutine. Any table-references + ** in the subquery might be resolved to index-references for the index on + ** the OR branch in which the subroutine is coded. But if the subroutine + ** is invoked from a different OR branch that uses a different index, such + ** index-references will not work. tag-20220303a + ** https://sqlite.org/forum/forumpost/36937b197273d403 */ if( pWC->nTerm>1 ){ int iTerm; @@ -146123,9 +154633,12 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( if( &pWC->a[iTerm] == pTerm ) continue; testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL ); testcase( pWC->a[iTerm].wtFlags & TERM_CODED ); - if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue; + testcase( pWC->a[iTerm].wtFlags & TERM_SLICE ); + if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED|TERM_SLICE))!=0 ){ + continue; + } if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue; - testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO ); + if( ExprHasProperty(pExpr, EP_Subquery) ) continue; /* tag-20220303a */ pExpr = wx_sqlite3ExprDup(db, pExpr, 0); pAndExpr = wx_sqlite3ExprAnd(pParse, pAndExpr, pExpr); } @@ -146133,7 +154646,7 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( /* The extra 0x10000 bit on the opcode is masked off and does not ** become part of the new Expr.op. However, it does make the ** op==TK_AND comparison inside of wx_sqlite3PExpr() false, and this - ** prevents wx_sqlite3PExpr() from implementing AND short-circuit + ** prevents wx_sqlite3PExpr() from applying the AND short-circuit ** optimization, which we do not want here. */ pAndExpr = wx_sqlite3PExpr(pParse, TK_AND|0x10000, 0, pAndExpr); } @@ -146149,20 +154662,26 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */ + Expr *pDelete; /* Local copy of OR clause term */ int jmp1 = 0; /* Address of jump operation */ testcase( (pTabItem[0].fg.jointype & JT_LEFT)!=0 - && !ExprHasProperty(pOrExpr, EP_FromJoin) + && !ExprHasProperty(pOrExpr, EP_OuterON) ); /* See TH3 vtab25.400 and ticket 614b25314c766238 */ + pDelete = pOrExpr = wx_sqlite3ExprDup(db, pOrExpr, 0); + if( db->mallocFailed ){ + wx_sqlite3ExprDelete(db, pDelete); + continue; + } if( pAndExpr ){ pAndExpr->pLeft = pOrExpr; pOrExpr = pAndExpr; } /* Loop through table entries that match term pOrTerm. */ ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1)); - WHERETRACE(0xffff, ("Subplan for OR-clause:\n")); - pSubWInfo = wx_sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, + WHERETRACE(0xffffffff, ("Subplan for OR-clause:\n")); + pSubWInfo = wx_sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, 0, WHERE_OR_SUBCLAUSE, iCovCur); - assert( pSubWInfo || pParse->nErr || db->mallocFailed ); + assert( pSubWInfo || pParse->nErr ); if( pSubWInfo ){ WhereLoop *pSubLoop; int addrExplain = wx_sqlite3WhereExplainOneScan( @@ -146267,10 +154786,14 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( wx_sqlite3WhereEnd(pSubWInfo); ExplainQueryPlanPop(pParse); } + wx_sqlite3ExprDelete(db, pDelete); } } ExplainQueryPlanPop(pParse); - pLevel->u.pCovidx = pCov; + assert( pLevel->pWLoop==pLoop ); + assert( (pLoop->wsFlags & WHERE_MULTI_OR)!=0 ); + assert( (pLoop->wsFlags & WHERE_IN_ABLE)==0 ); + pLevel->u.pCoveringIdx = pCov; if( pCov ) pLevel->iIdxCur = iCovCur; if( pAndExpr ){ pAndExpr->pLeft = 0; @@ -146280,7 +154803,15 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( wx_sqlite3VdbeGoto(v, pLevel->addrBrk); wx_sqlite3VdbeResolveLabel(v, iLoopBody); - if( pWInfo->nLevel>1 ){ wx_sqlite3StackFree(db, pOrTab); } + /* Set the P2 operand of the OP_Return opcode that will end the current + ** loop to point to this spot, which is the top of the next containing + ** loop. The byte-code formatter will use that P2 value as a hint to + ** indent everything in between the this point and the final OP_Return. + ** See tag-20220407a in vdbe.c and shell.c */ + assert( pLevel->op==OP_Return ); + pLevel->p2 = wx_sqlite3VdbeCurrentAddr(v); + + if( pWInfo->nLevel>1 ){ wx_sqlite3DbFreeNN(db, pOrTab); } if( !untestedTerms ) disableTerm(pLevel, pTerm); }else #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ @@ -146342,10 +154873,22 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( } pE = pTerm->pExpr; assert( pE!=0 ); - if( (pTabItem->fg.jointype&JT_LEFT) && !ExprHasProperty(pE,EP_FromJoin) ){ - continue; + if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ){ + if( !ExprHasProperty(pE,EP_OuterON|EP_InnerON) ){ + /* Defer processing WHERE clause constraints until after outer + ** join processing. tag-20220513a */ + continue; + }else if( (pTabItem->fg.jointype & JT_LEFT)==JT_LEFT + && !ExprHasProperty(pE,EP_OuterON) ){ + continue; + }else{ + Bitmask m = wx_sqlite3WhereGetMask(&pWInfo->sMaskSet, pE->w.iJoin); + if( m & pLevel->notReady ){ + /* An ON clause that is not ripe */ + continue; + } + } } - if( iLoop==1 && !wx_sqlite3ExprCoveredByIndex(pE, pLevel->iTabCur, pIdx) ){ iNext = 2; continue; @@ -146372,12 +154915,12 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( } #endif } -#ifdef WHERETRACE_ENABLED /* 0xffff */ +#ifdef WHERETRACE_ENABLED /* 0xffffffff */ if( wx_sqlite3WhereTrace ){ VdbeNoopComment((v, "WhereTerm[%d] (%p) priority=%d", pWC->nTerm-j, pTerm, iLoop)); } - if( wx_sqlite3WhereTrace & 0x800 ){ + if( wx_sqlite3WhereTrace & 0x4000 ){ wx_sqlite3DebugPrintf("Coding auxiliary constraint:\n"); wx_sqlite3WhereTermPrint(pTerm, pWC->nTerm-j); } @@ -146397,29 +154940,30 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( ** then we cannot use the "t1.a=t2.b" constraint, but we can code ** the implied "t1.a=123" constraint. */ - for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ + for(pTerm=pWC->a, j=pWC->nBase; j>0; j--, pTerm++){ Expr *pE, sEAlt; WhereTerm *pAlt; if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue; if( (pTerm->eOperator & WO_EQUIV)==0 ) continue; if( pTerm->leftCursor!=iCur ) continue; - if( pTabItem->fg.jointype & JT_LEFT ) continue; + if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ) continue; pE = pTerm->pExpr; -#ifdef WHERETRACE_ENABLED /* 0x800 */ - if( wx_sqlite3WhereTrace & 0x800 ){ +#ifdef WHERETRACE_ENABLED /* 0x4001 */ + if( (wx_sqlite3WhereTrace & 0x4001)==0x4001 ){ wx_sqlite3DebugPrintf("Coding transitive constraint:\n"); wx_sqlite3WhereTermPrint(pTerm, pWC->nTerm-j); } #endif - assert( !ExprHasProperty(pE, EP_FromJoin) ); + assert( !ExprHasProperty(pE, EP_OuterON) ); assert( (pTerm->prereqRight & pLevel->notReady)!=0 ); + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); pAlt = wx_sqlite3WhereFindTerm(pWC, iCur, pTerm->u.x.leftColumn, notReady, WO_EQ|WO_IN|WO_IS, 0); if( pAlt==0 ) continue; if( pAlt->wtFlags & (TERM_CODED) ) continue; if( (pAlt->eOperator & WO_IN) - && (pAlt->pExpr->flags & EP_xIsSelect) + && ExprUseXSelect(pAlt->pExpr) && (pAlt->pExpr->x.pSelect->pEList->nExpr>1) ){ continue; @@ -146431,6 +154975,48 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( sEAlt = *pAlt->pExpr; sEAlt.pLeft = pE->pLeft; wx_sqlite3ExprIfFalse(pParse, &sEAlt, addrCont, SQLITE_JUMPIFNULL); + pAlt->wtFlags |= TERM_CODED; + } + + /* For a RIGHT OUTER JOIN, record the fact that the current row has + ** been matched at least once. + */ + if( pLevel->pRJ ){ + Table *pTab; + int nPk; + int r; + int jmp1 = 0; + WhereRightJoin *pRJ = pLevel->pRJ; + + /* pTab is the right-hand table of the RIGHT JOIN. Generate code that + ** will record that the current row of that table has been matched at + ** least once. This is accomplished by storing the PK for the row in + ** both the iMatch index and the regBloom Bloom filter. + */ + pTab = pWInfo->pTabList->a[pLevel->iFrom].pTab; + if( HasRowid(pTab) ){ + r = wx_sqlite3GetTempRange(pParse, 2); + wx_sqlite3ExprCodeGetColumnOfTable(v, pTab, pLevel->iTabCur, -1, r+1); + nPk = 1; + }else{ + int iPk; + Index *pPk = wx_sqlite3PrimaryKeyIndex(pTab); + nPk = pPk->nKeyCol; + r = wx_sqlite3GetTempRange(pParse, nPk+1); + for(iPk=0; iPkaiColumn[iPk]; + wx_sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+1+iPk); + } + } + jmp1 = wx_sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, 0, r+1, nPk); + VdbeCoverage(v); + VdbeComment((v, "match against %s", pTab->zName)); + wx_sqlite3VdbeAddOp3(v, OP_MakeRecord, r+1, nPk, r); + wx_sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pRJ->iMatch, r, r+1, nPk); + wx_sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pRJ->regBloom, 0, r+1, nPk); + wx_sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); + wx_sqlite3VdbeJumpHere(v, jmp1); + wx_sqlite3ReleaseTempRange(pParse, r, nPk+1); } /* For a LEFT OUTER JOIN, generate code that will record the fact that @@ -146440,7 +155026,31 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( pLevel->addrFirst = wx_sqlite3VdbeCurrentAddr(v); wx_sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin); VdbeComment((v, "record LEFT JOIN hit")); - for(pTerm=pWC->a, j=0; jnTerm; j++, pTerm++){ + if( pLevel->pRJ==0 ){ + goto code_outer_join_constraints; /* WHERE clause constraints */ + } + } + + if( pLevel->pRJ ){ + /* Create a subroutine used to process all interior loops and code + ** of the RIGHT JOIN. During normal operation, the subroutine will + ** be in-line with the rest of the code. But at the end, a separate + ** loop will run that invokes this subroutine for unmatched rows + ** of pTab, with all tables to left begin set to NULL. + */ + WhereRightJoin *pRJ = pLevel->pRJ; + wx_sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pRJ->regReturn); + pRJ->addrSubrtn = wx_sqlite3VdbeCurrentAddr(v); + assert( pParse->withinRJSubrtn < 255 ); + pParse->withinRJSubrtn++; + + /* WHERE clause constraints must be deferred until after outer join + ** row elimination has completed, since WHERE clause constraints apply + ** to the results of the OUTER JOIN. The following loop generates the + ** appropriate WHERE clause constraint checks. tag-20220513a. + */ + code_outer_join_constraints: + for(pTerm=pWC->a, j=0; jnBase; j++, pTerm++){ testcase( pTerm->wtFlags & TERM_VIRTUAL ); testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; @@ -146448,19 +155058,20 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( assert( pWInfo->untestedTerms ); continue; } + if( pTabItem->fg.jointype & JT_LTORJ ) continue; assert( pTerm->pExpr ); wx_sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); pTerm->wtFlags |= TERM_CODED; } } -#if WHERETRACE_ENABLED /* 0x20800 */ - if( wx_sqlite3WhereTrace & 0x20000 ){ +#if WHERETRACE_ENABLED /* 0x4001 */ + if( wx_sqlite3WhereTrace & 0x4000 ){ wx_sqlite3DebugPrintf("All WHERE-clause terms after coding level %d:\n", iLevel); wx_sqlite3WhereClausePrint(pWC); } - if( wx_sqlite3WhereTrace & 0x800 ){ + if( wx_sqlite3WhereTrace & 0x1 ){ wx_sqlite3DebugPrintf("End Coding level %d: notReady=%llx\n", iLevel, (u64)pLevel->notReady); } @@ -146468,6 +155079,96 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereCodeOneLoopStart( return pLevel->notReady; } +/* +** Generate the code for the loop that finds all non-matched terms +** for a RIGHT JOIN. +*/ +SQLITE_PRIVATE SQLITE_NOINLINE void wx_sqlite3WhereRightJoinLoop( + WhereInfo *pWInfo, + int iLevel, + WhereLevel *pLevel +){ + Parse *pParse = pWInfo->pParse; + Vdbe *v = pParse->pVdbe; + WhereRightJoin *pRJ = pLevel->pRJ; + Expr *pSubWhere = 0; + WhereClause *pWC = &pWInfo->sWC; + WhereInfo *pSubWInfo; + WhereLoop *pLoop = pLevel->pWLoop; + SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; + SrcList sFrom; + Bitmask mAll = 0; + int k; + + ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pTab->zName)); + wx_sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, + pRJ->regReturn); + for(k=0; ka[k].pWLoop->maskSelf; + wx_sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); + iIdxCur = pWInfo->a[k].iIdxCur; + if( iIdxCur ){ + wx_sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); + } + } + if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){ + mAll |= pLoop->maskSelf; + for(k=0; knTerm; k++){ + WhereTerm *pTerm = &pWC->a[k]; + if( (pTerm->wtFlags & (TERM_VIRTUAL|TERM_SLICE))!=0 + && pTerm->eOperator!=WO_ROWVAL + ){ + break; + } + if( pTerm->prereqAll & ~mAll ) continue; + if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue; + pSubWhere = wx_sqlite3ExprAnd(pParse, pSubWhere, + wx_sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); + } + } + sFrom.nSrc = 1; + sFrom.nAlloc = 1; + memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem)); + sFrom.a[0].fg.jointype = 0; + assert( pParse->withinRJSubrtn < 100 ); + pParse->withinRJSubrtn++; + pSubWInfo = wx_sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0, + WHERE_RIGHT_JOIN, 0); + if( pSubWInfo ){ + int iCur = pLevel->iTabCur; + int r = ++pParse->nMem; + int nPk; + int jmp; + int addrCont = wx_sqlite3WhereContinueLabel(pSubWInfo); + Table *pTab = pTabItem->pTab; + if( HasRowid(pTab) ){ + wx_sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, r); + nPk = 1; + }else{ + int iPk; + Index *pPk = wx_sqlite3PrimaryKeyIndex(pTab); + nPk = pPk->nKeyCol; + pParse->nMem += nPk - 1; + for(iPk=0; iPkaiColumn[iPk]; + wx_sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+iPk); + } + } + jmp = wx_sqlite3VdbeAddOp4Int(v, OP_Filter, pRJ->regBloom, 0, r, nPk); + VdbeCoverage(v); + wx_sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, addrCont, r, nPk); + VdbeCoverage(v); + wx_sqlite3VdbeJumpHere(v, jmp); + wx_sqlite3VdbeAddOp2(v, OP_Gosub, pRJ->regReturn, pRJ->addrSubrtn); + wx_sqlite3WhereEnd(pSubWInfo); + } + wx_sqlite3ExprDelete(pParse->db, pSubWhere); + ExplainQueryPlanPop(pParse); + assert( pParse->withinRJSubrtn>0 ); + pParse->withinRJSubrtn--; +} + /************** End of wherecode.c *******************************************/ /************** Begin file whereexpr.c ***************************************/ /* @@ -146536,7 +155237,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){ if( pWC->nTerm>=pWC->nSlot ){ WhereTerm *pOld = pWC->a; wx_sqlite3 *db = pWC->pWInfo->pParse->db; - pWC->a = wx_sqlite3DbMallocRawNN(db, sizeof(pWC->a[0])*pWC->nSlot*2 ); + pWC->a = wx_sqlite3WhereMalloc(pWC->pWInfo, sizeof(pWC->a[0])*pWC->nSlot*2 ); if( pWC->a==0 ){ if( wtFlags & TERM_DYNAMIC ){ wx_sqlite3ExprDelete(db, p); @@ -146545,12 +155246,10 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){ return 0; } memcpy(pWC->a, pOld, sizeof(pWC->a[0])*pWC->nTerm); - if( pOld!=pWC->aStatic ){ - wx_sqlite3DbFree(db, pOld); - } - pWC->nSlot = wx_sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]); + pWC->nSlot = pWC->nSlot*2; } pTerm = &pWC->a[idx = pWC->nTerm++]; + if( (wtFlags & TERM_VIRTUAL)==0 ) pWC->nBase = pWC->nTerm; if( p && ExprHasProperty(p, EP_Unlikely) ){ pTerm->truthProb = wx_sqlite3LogEst(p->iTable) - 270; }else{ @@ -146667,6 +155366,7 @@ static int isLikeOrGlob( #ifdef SQLITE_EBCDIC if( *pnoCase ) return 0; #endif + assert( ExprUseXList(pExpr) ); pList = pExpr->x.pList; pLeft = pList->a[1].pExpr; @@ -146682,7 +155382,8 @@ static int isLikeOrGlob( wx_sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); }else if( op==TK_STRING ){ - z = (u8*)pRight->u.zToken; + assert( !ExprHasProperty(pRight, EP_IntValue) ); + z = (u8*)pRight->u.zToken; } if( z ){ @@ -146711,7 +155412,9 @@ static int isLikeOrGlob( pPrefix = wx_sqlite3Expr(db, TK_STRING, (char*)z); if( pPrefix ){ int iFrom, iTo; - char *zNew = pPrefix->u.zToken; + char *zNew; + assert( !ExprHasProperty(pPrefix, EP_IntValue) ); + zNew = pPrefix->u.zToken; zNew[cnt] = 0; for(iFrom=iTo=0; iFromop!=TK_COLUMN || wx_sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT - || IsVirtual(pLeft->y.pTab) /* Value might be numeric */ + || (ALWAYS( ExprUseYTab(pLeft) ) + && ALWAYS(pLeft->y.pTab) + && IsVirtual(pLeft->y.pTab)) /* Might be numeric */ ){ int isNum; double rDummy; @@ -146763,6 +155468,7 @@ static int isLikeOrGlob( if( op==TK_VARIABLE ){ Vdbe *v = pParse->pVdbe; wx_sqlite3VdbeSetVarmask(v, pRight->iColumn); + assert( !ExprHasProperty(pRight, EP_IntValue) ); if( *pisComplete && pRight->u.zToken[1] ){ /* If the rhs of the LIKE expression is a variable, and the current ** value of the variable means there is no need to invoke the LIKE @@ -146836,6 +155542,7 @@ static int isAuxiliaryVtabOperator( Expr *pCol; /* Column reference */ int i; + assert( ExprUseXList(pExpr) ); pList = pExpr->x.pList; if( pList==0 || pList->nExpr!=2 ){ return 0; @@ -146849,9 +155556,10 @@ static int isAuxiliaryVtabOperator( ** MATCH(expression,vtab_column) */ pCol = pList->a[1].pExpr; - testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); + assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) ); if( ExprIsVtab(pCol) ){ for(i=0; iu.zToken, aOp[i].zOp)==0 ){ *peOp2 = aOp[i].eOp2; *ppRight = pList->a[0].pExpr; @@ -146872,7 +155580,8 @@ static int isAuxiliaryVtabOperator( ** with function names in an arbitrary case. */ pCol = pList->a[0].pExpr; - testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); + assert( pCol->op!=TK_COLUMN || ExprUseYTab(pCol) ); + assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) ); if( ExprIsVtab(pCol) ){ wx_sqlite3_vtab *pVtab; wx_sqlite3_module *pMod; @@ -146881,6 +155590,7 @@ static int isAuxiliaryVtabOperator( pVtab = wx_sqlite3GetVTable(db, pCol->y.pTab)->pVtab; assert( pVtab!=0 ); assert( pVtab->pModule!=0 ); + assert( !ExprHasProperty(pExpr, EP_IntValue) ); pMod = (wx_sqlite3_module *)pVtab->pModule; if( pMod->xFindFunction!=0 ){ i = pMod->xFindFunction(pVtab,2, pExpr->u.zToken, &xNotUsed, &pNotUsed); @@ -146896,11 +155606,12 @@ static int isAuxiliaryVtabOperator( int res = 0; Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; - testcase( pLeft->op==TK_COLUMN && pLeft->y.pTab==0 ); + assert( pLeft->op!=TK_COLUMN || (ExprUseYTab(pLeft) && pLeft->y.pTab!=0) ); if( ExprIsVtab(pLeft) ){ res++; } - testcase( pRight && pRight->op==TK_COLUMN && pRight->y.pTab==0 ); + assert( pRight==0 || pRight->op!=TK_COLUMN + || (ExprUseYTab(pRight) && pRight->y.pTab!=0) ); if( pRight && ExprIsVtab(pRight) ){ res++; SWAP(Expr*, pLeft, pRight); @@ -146921,9 +155632,9 @@ static int isAuxiliaryVtabOperator( ** a join, then transfer the appropriate markings over to derived. */ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){ - if( pDerived ){ - pDerived->flags |= pBase->flags & EP_FromJoin; - pDerived->iRightJoinTable = pBase->iRightJoinTable; + if( pDerived && ExprHasProperty(pBase, EP_OuterON|EP_InnerON) ){ + pDerived->flags |= pBase->flags & (EP_OuterON|EP_InnerON); + pDerived->w.iJoin = pBase->w.iJoin; } } @@ -146983,6 +155694,7 @@ static void whereCombineDisjuncts( int op; /* Operator for the combined expression */ int idxNew; /* Index in pWC of the next virtual term */ + if( (pOne->wtFlags | pTwo->wtFlags) & TERM_VNULL ) return; if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp @@ -147151,6 +155863,7 @@ static void exprAnalyzeOrTerm( pOrTerm->u.pAndInfo = pAndInfo; pOrTerm->wtFlags |= TERM_ANDINFO; pOrTerm->eOperator = WO_AND; + pOrTerm->leftCursor = -1; pAndWC = &pAndInfo->wc; memset(pAndWC->aStatic, 0, sizeof(pAndWC->aStatic)); wx_sqlite3WhereClauseInit(pAndWC, pWC->pWInfo); @@ -147193,11 +155906,10 @@ static void exprAnalyzeOrTerm( ** empty. */ pOrInfo->indexable = indexable; + pTerm->eOperator = WO_OR; + pTerm->leftCursor = -1; if( indexable ){ - pTerm->eOperator = WO_OR; pWC->hasOr = 1; - }else{ - pTerm->eOperator = WO_OR; } /* For a two-way OR, attempt to implementation case 2. @@ -147252,7 +155964,7 @@ static void exprAnalyzeOrTerm( pOrTerm = pOrWc->a; for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ assert( pOrTerm->eOperator & WO_EQ ); - pOrTerm->wtFlags &= ~TERM_OR_OK; + pOrTerm->wtFlags &= ~TERM_OK; if( pOrTerm->leftCursor==iCursor ){ /* This is the 2-bit case and we are on the second iteration and ** current term is from the first iteration. So skip this term. */ @@ -147270,6 +155982,7 @@ static void exprAnalyzeOrTerm( assert( pOrTerm->wtFlags & (TERM_COPIED|TERM_VIRTUAL) ); continue; } + assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); iColumn = pOrTerm->u.x.leftColumn; iCursor = pOrTerm->leftCursor; pLeft = pOrTerm->pExpr->pLeft; @@ -147290,8 +156003,9 @@ static void exprAnalyzeOrTerm( okToChngToIN = 1; for(; i>=0 && okToChngToIN; i--, pOrTerm++){ assert( pOrTerm->eOperator & WO_EQ ); + assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); if( pOrTerm->leftCursor!=iCursor ){ - pOrTerm->wtFlags &= ~TERM_OR_OK; + pOrTerm->wtFlags &= ~TERM_OK; }else if( pOrTerm->u.x.leftColumn!=iColumn || (iColumn==XN_EXPR && wx_sqlite3ExprCompare(pParse, pOrTerm->pExpr->pLeft, pLeft, -1) )){ @@ -147307,7 +156021,7 @@ static void exprAnalyzeOrTerm( if( affRight!=0 && affRight!=affLeft ){ okToChngToIN = 0; }else{ - pOrTerm->wtFlags |= TERM_OR_OK; + pOrTerm->wtFlags |= TERM_OK; } } } @@ -147324,8 +156038,9 @@ static void exprAnalyzeOrTerm( Expr *pNew; /* The complete IN operator */ for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ - if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; + if( (pOrTerm->wtFlags & TERM_OK)==0 ) continue; assert( pOrTerm->eOperator & WO_EQ ); + assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); assert( pOrTerm->leftCursor==iCursor ); assert( pOrTerm->u.x.leftColumn==iColumn ); pDup = wx_sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); @@ -147338,12 +156053,12 @@ static void exprAnalyzeOrTerm( if( pNew ){ int idxNew; transferJoinMarkings(pNew, pExpr); - assert( !ExprHasProperty(pNew, EP_xIsSelect) ); + assert( ExprUseXList(pNew) ); pNew->x.pList = pList; idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew==0 ); exprAnalyze(pSrc, pWC, idxNew); - /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where used again */ + /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where reused */ markTermAsChild(pWC, idxNew, idxTerm); }else{ wx_sqlite3ExprListDelete(db, pList); @@ -147373,7 +156088,7 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr){ CollSeq *pColl; if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0; if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0; - if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0; + if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; aff1 = wx_sqlite3ExprAffinity(pExpr->pLeft); aff2 = wx_sqlite3ExprAffinity(pExpr->pRight); if( aff1!=aff2 @@ -147404,7 +156119,9 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){ int i; for(i=0; inSrc; i++){ mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect); - mask |= wx_sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn); + if( pSrc->a[i].fg.isUsing==0 ){ + mask |= wx_sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].u3.pOn); + } if( pSrc->a[i].fg.isTabFunc ){ mask |= wx_sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg); } @@ -147430,35 +156147,40 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){ */ static SQLITE_NOINLINE int exprMightBeIndexed2( SrcList *pFrom, /* The FROM clause */ - Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */ int *aiCurCol, /* Write the referenced table cursor and column here */ - Expr *pExpr /* An operand of a comparison operator */ + Expr *pExpr, /* An operand of a comparison operator */ + int j /* Start looking with the j-th pFrom entry */ ){ Index *pIdx; int i; int iCur; - for(i=0; mPrereq>1; i++, mPrereq>>=1){} - iCur = pFrom->a[i].iCursor; - for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->aColExpr==0 ) continue; - for(i=0; inKeyCol; i++){ - if( pIdx->aiColumn[i]!=XN_EXPR ) continue; - if( wx_sqlite3ExprCompareSkip(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){ - aiCurCol[0] = iCur; - aiCurCol[1] = XN_EXPR; - return 1; + do{ + iCur = pFrom->a[j].iCursor; + for(pIdx=pFrom->a[j].pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->aColExpr==0 ) continue; + for(i=0; inKeyCol; i++){ + if( pIdx->aiColumn[i]!=XN_EXPR ) continue; + assert( pIdx->bHasExpr ); + if( wx_sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0 + && pExpr->op!=TK_STRING + ){ + aiCurCol[0] = iCur; + aiCurCol[1] = XN_EXPR; + return 1; + } } } - } + }while( ++j < pFrom->nSrc ); return 0; } static int exprMightBeIndexed( SrcList *pFrom, /* The FROM clause */ - Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */ int *aiCurCol, /* Write the referenced table cursor & column here */ Expr *pExpr, /* An operand of a comparison operator */ int op /* The specific comparison operator */ ){ + int i; + /* If this expression is a vector to the left or right of a ** inequality constraint (>, <, >= or <=), perform the processing ** on the first element of the vector. */ @@ -147466,6 +156188,7 @@ static int exprMightBeIndexed( assert( TK_ISop==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){ + assert( ExprUseXList(pExpr) ); pExpr = pExpr->x.pList->a[0].pExpr; } @@ -147474,281 +156197,18 @@ static int exprMightBeIndexed( aiCurCol[1] = pExpr->iColumn; return 1; } - if( mPrereq==0 ) return 0; /* No table references */ - if( (mPrereq&(mPrereq-1))!=0 ) return 0; /* Refs more than one table */ - return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr); -} - -/* -** Expression callback for exprUsesSrclist(). -*/ -static int exprUsesSrclistCb(Walker *p, Expr *pExpr){ - if( pExpr->op==TK_COLUMN ){ - SrcList *pSrc = p->u.pSrcList; - int iCsr = pExpr->iTable; - int ii; - for(ii=0; iinSrc; ii++){ - if( pSrc->a[ii].iCursor==iCsr ){ - return p->eCode ? WRC_Abort : WRC_Continue; - } - } - return p->eCode ? WRC_Continue : WRC_Abort; - } - return WRC_Continue; -} - -/* -** Select callback for exprUsesSrclist(). -*/ -static int exprUsesSrclistSelectCb(Walker *NotUsed1, Select *NotUsed2){ - UNUSED_PARAMETER(NotUsed1); - UNUSED_PARAMETER(NotUsed2); - return WRC_Abort; -} - -/* -** This function always returns true if expression pExpr contains -** a sub-select. -** -** If there is no sub-select in pExpr, then return true if pExpr -** contains a TK_COLUMN node for a table that is (bUses==1) -** or is not (bUses==0) in pSrc. -** -** Said another way: -** -** bUses Return Meaning -** -------- ------ ------------------------------------------------ -** -** bUses==1 true pExpr contains either a sub-select or a -** TK_COLUMN referencing pSrc. -** -** bUses==1 false pExpr contains no sub-selects and all TK_COLUMN -** nodes reference tables not found in pSrc -** -** bUses==0 true pExpr contains either a sub-select or a TK_COLUMN -** that references a table not in pSrc. -** -** bUses==0 false pExpr contains no sub-selects and all TK_COLUMN -** nodes reference pSrc -*/ -static int exprUsesSrclist(SrcList *pSrc, Expr *pExpr, int bUses){ - Walker sWalker; - memset(&sWalker, 0, sizeof(Walker)); - sWalker.eCode = bUses; - sWalker.u.pSrcList = pSrc; - sWalker.xExprCallback = exprUsesSrclistCb; - sWalker.xSelectCallback = exprUsesSrclistSelectCb; - return (wx_sqlite3WalkExpr(&sWalker, pExpr)==WRC_Abort); -} - -/* -** Context object used by exprExistsToInIter() as it iterates through an -** expression tree. -*/ -struct ExistsToInCtx { - SrcList *pSrc; /* The tables in an EXISTS(SELECT ... FROM ...) */ - Expr *pInLhs; /* OUT: Use this as the LHS of the IN operator */ - Expr *pEq; /* OUT: The == term that include pInLhs */ - Expr **ppAnd; /* OUT: The AND operator that includes pEq as a child */ - Expr **ppParent; /* The AND operator currently being examined */ -}; -/* -** Iterate through all AND connected nodes in the expression tree -** headed by (*ppExpr), populating the structure passed as the first -** argument with the values required by exprAnalyzeExistsFindEq(). -** -** This function returns non-zero if the expression tree does not meet -** the two conditions described by the header comment for -** exprAnalyzeExistsFindEq(), or zero if it does. -*/ -static int exprExistsToInIter(struct ExistsToInCtx *p, Expr **ppExpr){ - Expr *pExpr = *ppExpr; - switch( pExpr->op ){ - case TK_AND: - p->ppParent = ppExpr; - if( exprExistsToInIter(p, &pExpr->pLeft) ) return 1; - p->ppParent = ppExpr; - if( exprExistsToInIter(p, &pExpr->pRight) ) return 1; - break; - case TK_EQ: { - int bLeft = exprUsesSrclist(p->pSrc, pExpr->pLeft, 0); - int bRight = exprUsesSrclist(p->pSrc, pExpr->pRight, 0); - if( bLeft || bRight ){ - if( (bLeft && bRight) || p->pInLhs ) return 1; - p->pInLhs = bLeft ? pExpr->pLeft : pExpr->pRight; - if( exprUsesSrclist(p->pSrc, p->pInLhs, 1) ) return 1; - p->pEq = pExpr; - p->ppAnd = p->ppParent; + for(i=0; inSrc; i++){ + Index *pIdx; + for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->aColExpr ){ + return exprMightBeIndexed2(pFrom,aiCurCol,pExpr,i); } - break; } - default: - if( exprUsesSrclist(p->pSrc, pExpr, 0) ){ - return 1; - } - break; } - return 0; } -/* -** This function is used by exprAnalyzeExists() when creating virtual IN(...) -** terms equivalent to user-supplied EXIST(...) clauses. It splits the WHERE -** clause of the Select object passed as the first argument into one or more -** expressions joined by AND operators, and then tests if the following are -** true: -** -** 1. Exactly one of the AND separated terms refers to the outer -** query, and it is an == (TK_EQ) expression. -** -** 2. Only one side of the == expression refers to the outer query, and -** it does not refer to any columns from the inner query. -** -** If both these conditions are true, then a pointer to the side of the == -** expression that refers to the outer query is returned. The caller will -** use this expression as the LHS of the IN(...) virtual term. Or, if one -** or both of the above conditions are not true, NULL is returned. -** -** If non-NULL is returned and ppEq is non-NULL, *ppEq is set to point -** to the == expression node before returning. If pppAnd is non-NULL and -** the == node is not the root of the WHERE clause, then *pppAnd is set -** to point to the pointer to the AND node that is the parent of the == -** node within the WHERE expression tree. -*/ -static Expr *exprAnalyzeExistsFindEq( - Select *pSel, /* The SELECT of the EXISTS */ - Expr **ppEq, /* OUT: == node from WHERE clause */ - Expr ***pppAnd /* OUT: Pointer to parent of ==, if any */ -){ - struct ExistsToInCtx ctx; - memset(&ctx, 0, sizeof(ctx)); - ctx.pSrc = pSel->pSrc; - if( exprExistsToInIter(&ctx, &pSel->pWhere) ){ - return 0; - } - if( ppEq ) *ppEq = ctx.pEq; - if( pppAnd ) *pppAnd = ctx.ppAnd; - return ctx.pInLhs; -} - -/* -** Term idxTerm of the WHERE clause passed as the second argument is an -** EXISTS expression with a correlated SELECT statement on the RHS. -** This function analyzes the SELECT statement, and if possible adds an -** equivalent "? IN(SELECT...)" virtual term to the WHERE clause. -** -** For an EXISTS term such as the following: -** -** EXISTS (SELECT ... FROM WHERE = AND ) -** -** The virtual IN() term added is: -** -** IN (SELECT FROM WHERE ) -** -** The virtual term is only added if the following conditions are met: -** -** 1. The sub-select must not be an aggregate or use window functions, -** -** 2. The sub-select must not be a compound SELECT, -** -** 3. Expression must refer to at least one column from the outer -** query, and must not refer to any column from the inner query -** (i.e. from ). -** -** 4. and must not refer to any values from the outer query. -** In other words, once has been removed, the inner query -** must not be correlated. -** -*/ -static void exprAnalyzeExists( - SrcList *pSrc, /* the FROM clause */ - WhereClause *pWC, /* the WHERE clause */ - int idxTerm /* Index of the term to be analyzed */ -){ - Parse *pParse = pWC->pWInfo->pParse; - WhereTerm *pTerm = &pWC->a[idxTerm]; - Expr *pExpr = pTerm->pExpr; - Select *pSel = pExpr->x.pSelect; - Expr *pDup = 0; - Expr *pEq = 0; - Expr *pRet = 0; - Expr *pInLhs = 0; - Expr **ppAnd = 0; - int idxNew; - wx_sqlite3 *db = pParse->db; - - assert( pExpr->op==TK_EXISTS ); - assert( (pExpr->flags & EP_VarSelect) && (pExpr->flags & EP_xIsSelect) ); - - if( pSel->selFlags & SF_Aggregate ) return; -#ifndef SQLITE_OMIT_WINDOWFUNC - if( pSel->pWin ) return; -#endif - if( pSel->pPrior ) return; - if( pSel->pWhere==0 ) return; - if( pSel->pLimit ) return; - if( 0==exprAnalyzeExistsFindEq(pSel, 0, 0) ) return; - - pDup = wx_sqlite3ExprDup(db, pExpr, 0); - if( db->mallocFailed ){ - wx_sqlite3ExprDelete(db, pDup); - return; - } - pSel = pDup->x.pSelect; - wx_sqlite3ExprListDelete(db, pSel->pEList); - pSel->pEList = 0; - - pInLhs = exprAnalyzeExistsFindEq(pSel, &pEq, &ppAnd); - assert( pInLhs && pEq ); - assert( pEq==pSel->pWhere || ppAnd ); - if( pInLhs==pEq->pLeft ){ - pRet = pEq->pRight; - }else{ - CollSeq *p = wx_sqlite3ExprCompareCollSeq(pParse, pEq); - pInLhs = wx_sqlite3ExprAddCollateString(pParse, pInLhs, p?p->zName:"BINARY"); - pRet = pEq->pLeft; - } - - assert( pDup->pLeft==0 ); - pDup->op = TK_IN; - pDup->pLeft = pInLhs; - pDup->flags &= ~EP_VarSelect; - if( pRet->op==TK_VECTOR ){ - pSel->pEList = pRet->x.pList; - pRet->x.pList = 0; - wx_sqlite3ExprDelete(db, pRet); - }else{ - pSel->pEList = wx_sqlite3ExprListAppend(pParse, 0, pRet); - } - pEq->pLeft = 0; - pEq->pRight = 0; - if( ppAnd ){ - Expr *pAnd = *ppAnd; - Expr *pOther = (pAnd->pLeft==pEq) ? pAnd->pRight : pAnd->pLeft; - pAnd->pLeft = pAnd->pRight = 0; - wx_sqlite3ExprDelete(db, pAnd); - *ppAnd = pOther; - }else{ - assert( pSel->pWhere==pEq ); - pSel->pWhere = 0; - } - wx_sqlite3ExprDelete(db, pEq); - -#ifdef WHERETRACE_ENABLED /* 0x20 */ - if( wx_sqlite3WhereTrace & 0x20 ){ - wx_sqlite3DebugPrintf("Convert EXISTS:\n"); - wx_sqlite3TreeViewExpr(0, pExpr, 0); - wx_sqlite3DebugPrintf("into IN:\n"); - wx_sqlite3TreeViewExpr(0, pDup, 0); - } -#endif - idxNew = whereClauseInsert(pWC, pDup, TERM_VIRTUAL|TERM_DYNAMIC); - exprAnalyze(pSrc, pWC, idxNew); - markTermAsChild(pWC, idxNew, idxTerm); - pWC->a[idxTerm].wtFlags |= TERM_COPIED; -} /* ** The input to this routine is an WhereTerm structure with only the @@ -147792,36 +156252,67 @@ static void exprAnalyze( if( db->mallocFailed ){ return; } + assert( pWC->nTerm > idxTerm ); pTerm = &pWC->a[idxTerm]; pMaskSet = &pWInfo->sMaskSet; pExpr = pTerm->pExpr; + assert( pExpr!=0 ); /* Because malloc() has not failed */ assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE ); + pMaskSet->bVarSelect = 0; prereqLeft = wx_sqlite3WhereExprUsage(pMaskSet, pExpr->pLeft); op = pExpr->op; if( op==TK_IN ){ assert( pExpr->pRight==0 ); if( wx_sqlite3ExprCheckIN(pParse, pExpr) ) return; - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ pTerm->prereqRight = exprSelectUsage(pMaskSet, pExpr->x.pSelect); }else{ pTerm->prereqRight = wx_sqlite3WhereExprListUsage(pMaskSet, pExpr->x.pList); } - }else if( op==TK_ISNULL ){ - pTerm->prereqRight = 0; + prereqAll = prereqLeft | pTerm->prereqRight; }else{ pTerm->prereqRight = wx_sqlite3WhereExprUsage(pMaskSet, pExpr->pRight); + if( pExpr->pLeft==0 + || ExprHasProperty(pExpr, EP_xIsSelect|EP_IfNullRow) + || pExpr->x.pList!=0 + ){ + prereqAll = wx_sqlite3WhereExprUsageNN(pMaskSet, pExpr); + }else{ + prereqAll = prereqLeft | pTerm->prereqRight; + } } - pMaskSet->bVarSelect = 0; - prereqAll = wx_sqlite3WhereExprUsageNN(pMaskSet, pExpr); if( pMaskSet->bVarSelect ) pTerm->wtFlags |= TERM_VARSELECT; - if( ExprHasProperty(pExpr, EP_FromJoin) ){ - Bitmask x = wx_sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable); - prereqAll |= x; - extraRight = x-1; /* ON clause terms may not be used with an index - ** on left table of a LEFT JOIN. Ticket #3015 */ - if( (prereqAll>>1)>=x ){ - wx_sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); - return; + +#ifdef SQLITE_DEBUG + if( prereqAll!=wx_sqlite3WhereExprUsageNN(pMaskSet, pExpr) ){ + printf("\n*** Incorrect prereqAll computed for:\n"); + wx_sqlite3TreeViewExpr(0,pExpr,0); + assert( 0 ); + } +#endif + + if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ){ + Bitmask x = wx_sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin); + if( ExprHasProperty(pExpr, EP_OuterON) ){ + prereqAll |= x; + extraRight = x-1; /* ON clause terms may not be used with an index + ** on left table of a LEFT JOIN. Ticket #3015 */ + if( (prereqAll>>1)>=x ){ + wx_sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); + return; + } + }else if( (prereqAll>>1)>=x ){ + /* The ON clause of an INNER JOIN references a table to its right. + ** Most other SQL database engines raise an error. But SQLite versions + ** 3.0 through 3.38 just put the ON clause constraint into the WHERE + ** clause and carried on. Beginning with 3.39, raise an error only + ** if there is a RIGHT or FULL JOIN in the query. This makes SQLite + ** more like other systems, and also preserves legacy. */ + if( ALWAYS(pSrc->nSrc>0) && (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ + wx_sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); + return; + } + ExprClearProperty(pExpr, EP_InnerON); } } pTerm->prereqAll = prereqAll; @@ -147837,17 +156328,20 @@ static void exprAnalyze( if( pTerm->u.x.iField>0 ){ assert( op==TK_IN ); assert( pLeft->op==TK_VECTOR ); + assert( ExprUseXList(pLeft) ); pLeft = pLeft->x.pList->a[pTerm->u.x.iField-1].pExpr; } - if( exprMightBeIndexed(pSrc, prereqLeft, aiCurCol, pLeft, op) ){ + if( exprMightBeIndexed(pSrc, aiCurCol, pLeft, op) ){ pTerm->leftCursor = aiCurCol[0]; + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); pTerm->u.x.leftColumn = aiCurCol[1]; pTerm->eOperator = operatorMask(op) & opMask; } if( op==TK_IS ) pTerm->wtFlags |= TERM_IS; if( pRight - && exprMightBeIndexed(pSrc, pTerm->prereqRight, aiCurCol, pRight, op) + && exprMightBeIndexed(pSrc, aiCurCol, pRight, op) + && !ExprHasProperty(pRight, EP_FixedCol) ){ WhereTerm *pNew; Expr *pDup; @@ -147878,12 +156372,18 @@ static void exprAnalyze( } pNew->wtFlags |= exprCommute(pParse, pDup); pNew->leftCursor = aiCurCol[0]; + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); pNew->u.x.leftColumn = aiCurCol[1]; testcase( (prereqLeft | extraRight) != prereqLeft ); pNew->prereqRight = prereqLeft | extraRight; pNew->prereqAll = prereqAll; pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; - }else if( op==TK_ISNULL && 0==wx_sqlite3ExprCanBeNull(pLeft) ){ + }else + if( op==TK_ISNULL + && !ExprHasProperty(pExpr,EP_OuterON) + && 0==wx_sqlite3ExprCanBeNull(pLeft) + ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); pExpr->op = TK_TRUEFALSE; pExpr->u.zToken = "false"; ExprSetProperty(pExpr, EP_IsFalse); @@ -147909,9 +156409,11 @@ static void exprAnalyze( ** BETWEEN term is skipped. */ else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){ - ExprList *pList = pExpr->x.pList; + ExprList *pList; int i; static const u8 ops[] = {TK_GE, TK_LE}; + assert( ExprUseXList(pExpr) ); + pList = pExpr->x.pList; assert( pList!=0 ); assert( pList->nExpr==2 ); for(i=0; i<2; i++){ @@ -147940,16 +156442,6 @@ static void exprAnalyze( pTerm = &pWC->a[idxTerm]; } #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ - - else if( pExpr->op==TK_EXISTS ){ - /* Perhaps treat an EXISTS operator as an IN operator */ - if( (pExpr->flags & EP_VarSelect)!=0 - && OptimizationEnabled(db, SQLITE_ExistsToIN) - ){ - exprAnalyzeExists(pSrc, pWC, idxTerm); - } - } - /* The form "x IS NOT NULL" can sometimes be evaluated more efficiently ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a ** virtual term of that form. @@ -147959,7 +156451,7 @@ static void exprAnalyze( else if( pExpr->op==TK_NOTNULL ){ if( pExpr->pLeft->op==TK_COLUMN && pExpr->pLeft->iColumn>=0 - && !ExprHasProperty(pExpr, EP_FromJoin) + && !ExprHasProperty(pExpr, EP_OuterON) ){ Expr *pNewExpr; Expr *pLeft = pExpr->pLeft; @@ -148014,8 +156506,12 @@ static void exprAnalyze( const char *zCollSeqName; /* Name of collating sequence */ const u16 wtFlags = TERM_LIKEOPT | TERM_VIRTUAL | TERM_DYNAMIC; + assert( ExprUseXList(pExpr) ); pLeft = pExpr->x.pList->a[1].pExpr; pStr2 = wx_sqlite3ExprDup(db, pStr1, 0); + assert( pStr1==0 || !ExprHasProperty(pStr1, EP_IntValue) ); + assert( pStr2==0 || !ExprHasProperty(pStr2, EP_IntValue) ); + /* Convert the lower bound to upper-case and the upper bound to ** lower-case (upper-case is less than lower-case in ASCII) so that @@ -148055,7 +156551,6 @@ static void exprAnalyze( transferJoinMarkings(pNewExpr1, pExpr); idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags); testcase( idxNew1==0 ); - exprAnalyze(pSrc, pWC, idxNew1); pNewExpr2 = wx_sqlite3ExprDup(db, pLeft, 0); pNewExpr2 = wx_sqlite3PExpr(pParse, TK_LT, wx_sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName), @@ -148063,6 +156558,7 @@ static void exprAnalyze( transferJoinMarkings(pNewExpr2, pExpr); idxNew2 = whereClauseInsert(pWC, pNewExpr2, wtFlags); testcase( idxNew2==0 ); + exprAnalyze(pSrc, pWC, idxNew1); exprAnalyze(pSrc, pWC, idxNew2); pTerm = &pWC->a[idxTerm]; if( isComplete ){ @@ -148078,7 +156574,10 @@ static void exprAnalyze( ** no longer used. ** ** This is only required if at least one side of the comparison operation - ** is not a sub-select. */ + ** is not a sub-select. + ** + ** tag-20220128a + */ if( (pExpr->op==TK_EQ || pExpr->op==TK_IS) && (nLeft = wx_sqlite3ExprVectorSize(pExpr->pLeft))>1 && wx_sqlite3ExprVectorSize(pExpr->pRight)==nLeft @@ -148090,17 +156589,17 @@ static void exprAnalyze( for(i=0; ipLeft, i); - Expr *pRight = wx_sqlite3ExprForVectorField(pParse, pExpr->pRight, i); + Expr *pLeft = wx_sqlite3ExprForVectorField(pParse, pExpr->pLeft, i, nLeft); + Expr *pRight = wx_sqlite3ExprForVectorField(pParse, pExpr->pRight, i, nLeft); pNew = wx_sqlite3PExpr(pParse, pExpr->op, pLeft, pRight); transferJoinMarkings(pNew, pExpr); - idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); + idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_SLICE); exprAnalyze(pSrc, pWC, idxNew); } pTerm = &pWC->a[idxTerm]; pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */ - pTerm->eOperator = 0; + pTerm->eOperator = WO_ROWVAL; } /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create @@ -148115,7 +156614,8 @@ static void exprAnalyze( else if( pExpr->op==TK_IN && pTerm->u.x.iField==0 && pExpr->pLeft->op==TK_VECTOR - && pExpr->x.pSelect->pPrior==0 + && ALWAYS( ExprUseXSelect(pExpr) ) + && (pExpr->x.pSelect->pPrior==0 || (pExpr->x.pSelect->selFlags & SF_Values)) #ifndef SQLITE_OMIT_WINDOWFUNC && pExpr->x.pSelect->pWin==0 #endif @@ -148124,7 +156624,7 @@ static void exprAnalyze( int i; for(i=0; ipLeft); i++){ int idxNew; - idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL); + idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL|TERM_SLICE); pWC->a[idxNew].u.x.iField = i+1; exprAnalyze(pSrc, pWC, idxNew); markTermAsChild(pWC, idxNew, idxTerm); @@ -148155,9 +156655,9 @@ static void exprAnalyze( Expr *pNewExpr; pNewExpr = wx_sqlite3PExpr(pParse, TK_MATCH, 0, wx_sqlite3ExprDup(db, pRight, 0)); - if( ExprHasProperty(pExpr, EP_FromJoin) && pNewExpr ){ - ExprSetProperty(pNewExpr, EP_FromJoin); - pNewExpr->iRightJoinTable = pExpr->iRightJoinTable; + if( ExprHasProperty(pExpr, EP_OuterON) && pNewExpr ){ + ExprSetProperty(pNewExpr, EP_OuterON); + pNewExpr->w.iJoin = pExpr->w.iJoin; } idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew==0 ); @@ -148220,6 +156720,120 @@ SQLITE_PRIVATE void wx_sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ } } +/* +** Add either a LIMIT (if eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT) or +** OFFSET (if eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET) term to the +** where-clause passed as the first argument. The value for the term +** is found in register iReg. +** +** In the common case where the value is a simple integer +** (example: "LIMIT 5 OFFSET 10") then the expression codes as a +** TK_INTEGER so that it will be available to wx_sqlite3_vtab_rhs_value(). +** If not, then it codes as a TK_REGISTER expression. +*/ +static void whereAddLimitExpr( + WhereClause *pWC, /* Add the constraint to this WHERE clause */ + int iReg, /* Register that will hold value of the limit/offset */ + Expr *pExpr, /* Expression that defines the limit/offset */ + int iCsr, /* Cursor to which the constraint applies */ + int eMatchOp /* SQLITE_INDEX_CONSTRAINT_LIMIT or _OFFSET */ +){ + Parse *pParse = pWC->pWInfo->pParse; + wx_sqlite3 *db = pParse->db; + Expr *pNew; + int iVal = 0; + + if( wx_sqlite3ExprIsInteger(pExpr, &iVal) && iVal>=0 ){ + Expr *pVal = wx_sqlite3Expr(db, TK_INTEGER, 0); + if( pVal==0 ) return; + ExprSetProperty(pVal, EP_IntValue); + pVal->u.iValue = iVal; + pNew = wx_sqlite3PExpr(pParse, TK_MATCH, 0, pVal); + }else{ + Expr *pVal = wx_sqlite3Expr(db, TK_REGISTER, 0); + if( pVal==0 ) return; + pVal->iTable = iReg; + pNew = wx_sqlite3PExpr(pParse, TK_MATCH, 0, pVal); + } + if( pNew ){ + WhereTerm *pTerm; + int idx; + idx = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_VIRTUAL); + pTerm = &pWC->a[idx]; + pTerm->leftCursor = iCsr; + pTerm->eOperator = WO_AUX; + pTerm->eMatchOp = eMatchOp; + } +} + +/* +** Possibly add terms corresponding to the LIMIT and OFFSET clauses of the +** SELECT statement passed as the second argument. These terms are only +** added if: +** +** 1. The SELECT statement has a LIMIT clause, and +** 2. The SELECT statement is not an aggregate or DISTINCT query, and +** 3. The SELECT statement has exactly one object in its from clause, and +** that object is a virtual table, and +** 4. There are no terms in the WHERE clause that will not be passed +** to the virtual table xBestIndex method. +** 5. The ORDER BY clause, if any, will be made available to the xBestIndex +** method. +** +** LIMIT and OFFSET terms are ignored by most of the planner code. They +** exist only so that they may be passed to the xBestIndex method of the +** single virtual table in the FROM clause of the SELECT. +*/ +SQLITE_PRIVATE void SQLITE_NOINLINE wx_sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ + assert( p!=0 && p->pLimit!=0 ); /* 1 -- checked by caller */ + if( p->pGroupBy==0 + && (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */ + && (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pTab)) /* 3 */ + ){ + ExprList *pOrderBy = p->pOrderBy; + int iCsr = p->pSrc->a[0].iCursor; + int ii; + + /* Check condition (4). Return early if it is not met. */ + for(ii=0; iinTerm; ii++){ + if( pWC->a[ii].wtFlags & TERM_CODED ){ + /* This term is a vector operation that has been decomposed into + ** other, subsequent terms. It can be ignored. See tag-20220128a */ + assert( pWC->a[ii].wtFlags & TERM_VIRTUAL ); + assert( pWC->a[ii].eOperator==WO_ROWVAL ); + continue; + } + if( pWC->a[ii].nChild ){ + /* If this term has child terms, then they are also part of the + ** pWC->a[] array. So this term can be ignored, as a LIMIT clause + ** will only be added if each of the child terms passes the + ** (leftCursor==iCsr) test below. */ + continue; + } + if( pWC->a[ii].leftCursor!=iCsr ) return; + } + + /* Check condition (5). Return early if it is not met. */ + if( pOrderBy ){ + for(ii=0; iinExpr; ii++){ + Expr *pExpr = pOrderBy->a[ii].pExpr; + if( pExpr->op!=TK_COLUMN ) return; + if( pExpr->iTable!=iCsr ) return; + if( pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) return; + } + } + + /* All conditions are met. Add the terms to the where-clause object. */ + assert( p->pLimit->op==TK_LIMIT ); + whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, + iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); + if( p->iOffset>0 ){ + whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, + iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); + } + } +} + /* ** Initialize a preallocated WhereClause structure. */ @@ -148231,6 +156845,7 @@ SQLITE_PRIVATE void wx_sqlite3WhereClauseInit( pWC->hasOr = 0; pWC->pOuter = 0; pWC->nTerm = 0; + pWC->nBase = 0; pWC->nSlot = ArraySize(pWC->aStatic); pWC->a = pWC->aStatic; } @@ -148241,22 +156856,36 @@ SQLITE_PRIVATE void wx_sqlite3WhereClauseInit( ** wx_sqlite3WhereClauseInit(). */ SQLITE_PRIVATE void wx_sqlite3WhereClauseClear(WhereClause *pWC){ - int i; - WhereTerm *a; wx_sqlite3 *db = pWC->pWInfo->pParse->db; - for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){ - if( a->wtFlags & TERM_DYNAMIC ){ - wx_sqlite3ExprDelete(db, a->pExpr); + assert( pWC->nTerm>=pWC->nBase ); + if( pWC->nTerm>0 ){ + WhereTerm *a = pWC->a; + WhereTerm *aLast = &pWC->a[pWC->nTerm-1]; +#ifdef SQLITE_DEBUG + int i; + /* Verify that every term past pWC->nBase is virtual */ + for(i=pWC->nBase; inTerm; i++){ + assert( (pWC->a[i].wtFlags & TERM_VIRTUAL)!=0 ); } - if( a->wtFlags & TERM_ORINFO ){ - whereOrInfoDelete(db, a->u.pOrInfo); - }else if( a->wtFlags & TERM_ANDINFO ){ - whereAndInfoDelete(db, a->u.pAndInfo); +#endif + while(1){ + assert( a->eMatchOp==0 || a->eOperator==WO_AUX ); + if( a->wtFlags & TERM_DYNAMIC ){ + wx_sqlite3ExprDelete(db, a->pExpr); + } + if( a->wtFlags & (TERM_ORINFO|TERM_ANDINFO) ){ + if( a->wtFlags & TERM_ORINFO ){ + assert( (a->wtFlags & TERM_ANDINFO)==0 ); + whereOrInfoDelete(db, a->u.pOrInfo); + }else{ + assert( (a->wtFlags & TERM_ANDINFO)!=0 ); + whereAndInfoDelete(db, a->u.pAndInfo); + } + } + if( a==aLast ) break; + a++; } } - if( pWC->a!=pWC->aStatic ){ - wx_sqlite3DbFree(db, pWC->a); - } } @@ -148264,28 +156893,52 @@ SQLITE_PRIVATE void wx_sqlite3WhereClauseClear(WhereClause *pWC){ ** These routines walk (recursively) an expression tree and generate ** a bitmask indicating which tables are used in that expression ** tree. +** +** wx_sqlite3WhereExprUsage(MaskSet, Expr) -> +** +** Return a Bitmask of all tables referenced by Expr. Expr can be +** be NULL, in which case 0 is returned. +** +** wx_sqlite3WhereExprUsageNN(MaskSet, Expr) -> +** +** Same as wx_sqlite3WhereExprUsage() except that Expr must not be +** NULL. The "NN" suffix on the name stands for "Not Null". +** +** wx_sqlite3WhereExprListUsage(MaskSet, ExprList) -> +** +** Return a Bitmask of all tables referenced by every expression +** in the expression list ExprList. ExprList can be NULL, in which +** case 0 is returned. +** +** wx_sqlite3WhereExprUsageFull(MaskSet, ExprList) -> +** +** Internal use only. Called only by wx_sqlite3WhereExprUsageNN() for +** complex expressions that require pushing register values onto +** the stack. Many calls to wx_sqlite3WhereExprUsageNN() do not need +** the more complex analysis done by this routine. Hence, the +** computations done by this routine are broken out into a separate +** "no-inline" function to avoid the stack push overhead in the +** common case where it is not needed. */ -SQLITE_PRIVATE Bitmask wx_sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ +static SQLITE_NOINLINE Bitmask wx_sqlite3WhereExprUsageFull( + WhereMaskSet *pMaskSet, + Expr *p +){ Bitmask mask; - if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ - return wx_sqlite3WhereGetMask(pMaskSet, p->iTable); - }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ - assert( p->op!=TK_IF_NULL_ROW ); - return 0; - } mask = (p->op==TK_IF_NULL_ROW) ? wx_sqlite3WhereGetMask(pMaskSet, p->iTable) : 0; if( p->pLeft ) mask |= wx_sqlite3WhereExprUsageNN(pMaskSet, p->pLeft); if( p->pRight ){ mask |= wx_sqlite3WhereExprUsageNN(pMaskSet, p->pRight); assert( p->x.pList==0 ); - }else if( ExprHasProperty(p, EP_xIsSelect) ){ + }else if( ExprUseXSelect(p) ){ if( ExprHasProperty(p, EP_VarSelect) ) pMaskSet->bVarSelect = 1; mask |= exprSelectUsage(pMaskSet, p->x.pSelect); }else if( p->x.pList ){ mask |= wx_sqlite3WhereExprListUsage(pMaskSet, p->x.pList); } #ifndef SQLITE_OMIT_WINDOWFUNC - if( (p->op==TK_FUNCTION || p->op==TK_AGG_FUNCTION) && p->y.pWin ){ + if( (p->op==TK_FUNCTION || p->op==TK_AGG_FUNCTION) && ExprUseYWin(p) ){ + assert( p->y.pWin!=0 ); mask |= wx_sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pPartition); mask |= wx_sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pOrderBy); mask |= wx_sqlite3WhereExprUsage(pMaskSet, p->y.pWin->pFilter); @@ -148293,6 +156946,15 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr * #endif return mask; } +SQLITE_PRIVATE Bitmask wx_sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ + if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ + return wx_sqlite3WhereGetMask(pMaskSet, p->iTable); + }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ + assert( p->op!=TK_IF_NULL_ROW ); + return 0; + } + return wx_sqlite3WhereExprUsageFull(pMaskSet, p); +} SQLITE_PRIVATE Bitmask wx_sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){ return p ? wx_sqlite3WhereExprUsageNN(pMaskSet,p) : 0; } @@ -148350,6 +157012,7 @@ SQLITE_PRIVATE void wx_sqlite3WhereTabFuncArgs( if( pArgs==0 ) return; for(j=k=0; jnExpr; j++){ Expr *pRhs; + u32 joinType; while( knCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;} if( k>=pTab->nCol ){ wx_sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d", @@ -148360,13 +157023,18 @@ SQLITE_PRIVATE void wx_sqlite3WhereTabFuncArgs( if( pColRef==0 ) return; pColRef->iTable = pItem->iCursor; pColRef->iColumn = k++; + assert( ExprUseYTab(pColRef) ); pColRef->y.pTab = pTab; + pItem->colUsed |= wx_sqlite3ExprColUsed(pColRef); pRhs = wx_sqlite3PExpr(pParse, TK_UPLUS, wx_sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); pTerm = wx_sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs); - if( pItem->fg.jointype & JT_LEFT ){ - wx_sqlite3SetJoinExpr(pTerm, pItem->iCursor); + if( pItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ){ + joinType = EP_OuterON; + }else{ + joinType = EP_InnerON; } + wx_sqlite3SetJoinExpr(pTerm, pItem->iCursor, joinType); whereClauseInsert(pWC, pTerm, TERM_DYNAMIC); } } @@ -148405,8 +157073,14 @@ SQLITE_PRIVATE void wx_sqlite3WhereTabFuncArgs( */ typedef struct HiddenIndexInfo HiddenIndexInfo; struct HiddenIndexInfo { - WhereClause *pWC; /* The Where clause being analyzed */ - Parse *pParse; /* The parsing context */ + WhereClause *pWC; /* The Where clause being analyzed */ + Parse *pParse; /* The parsing context */ + int eDistinct; /* Value to return from wx_sqlite3_vtab_distinct() */ + u32 mIn; /* Mask of terms that are IN (...) */ + u32 mHandleIn; /* Terms that vtab will handle as IN (...) */ + wx_sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST + ** because extra space is allocated to hold up + ** to nTerm such values */ }; /* Forward declaration of methods */ @@ -148436,7 +157110,7 @@ SQLITE_PRIVATE int wx_sqlite3WhereIsDistinct(WhereInfo *pWInfo){ ** block sorting is required. */ SQLITE_PRIVATE int wx_sqlite3WhereIsOrdered(WhereInfo *pWInfo){ - return pWInfo->nOBSat; + return pWInfo->nOBSat<0 ? 0 : pWInfo->nOBSat; } /* @@ -148471,7 +157145,7 @@ SQLITE_PRIVATE int wx_sqlite3WhereOrderByLimitOptLabel(WhereInfo *pWInfo){ } pInner = &pWInfo->a[pWInfo->nLevel-1]; assert( pInner->addrNxt!=0 ); - return pInner->addrNxt; + return pInner->pRJ ? pWInfo->iContinue : pInner->addrNxt; } /* @@ -148609,7 +157283,12 @@ whereOrInsert_done: SQLITE_PRIVATE Bitmask wx_sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){ int i; assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 ); - for(i=0; in; i++){ + assert( pMaskSet->n>0 || pMaskSet->ix[0]<0 ); + assert( iCursor>=-1 ); + if( pMaskSet->ix[0]==iCursor ){ + return 1; + } + for(i=1; in; i++){ if( pMaskSet->ix[i]==iCursor ){ return MASKBIT(i); } @@ -148617,6 +157296,30 @@ SQLITE_PRIVATE Bitmask wx_sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCurso return 0; } +/* Allocate memory that is automatically freed when pWInfo is freed. +*/ +SQLITE_PRIVATE void *wx_sqlite3WhereMalloc(WhereInfo *pWInfo, u64 nByte){ + WhereMemBlock *pBlock; + pBlock = wx_sqlite3DbMallocRawNN(pWInfo->pParse->db, nByte+sizeof(*pBlock)); + if( pBlock ){ + pBlock->pNext = pWInfo->pMemToFree; + pBlock->sz = nByte; + pWInfo->pMemToFree = pBlock; + pBlock++; + } + return (void*)pBlock; +} +SQLITE_PRIVATE void *wx_sqlite3WhereRealloc(WhereInfo *pWInfo, void *pOld, u64 nByte){ + void *pNew = wx_sqlite3WhereMalloc(pWInfo, nByte); + if( pNew && pOld ){ + WhereMemBlock *pOldBlk = (WhereMemBlock*)pOld; + pOldBlk--; + assert( pOldBlk->szsz); + } + return pNew; +} + /* ** Create a new mask for cursor iCursor. ** @@ -148636,7 +157339,9 @@ static void createMask(WhereMaskSet *pMaskSet, int iCursor){ */ static Expr *whereRightSubexprIsColumn(Expr *p){ p = wx_sqlite3ExprSkipCollateAndLikely(p->pRight); - if( ALWAYS(p!=0) && p->op==TK_COLUMN ) return p; + if( ALWAYS(p!=0) && p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ + return p; + } return 0; } @@ -148659,14 +157364,16 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ iColumn = pScan->aiColumn[pScan->iEquiv-1]; iCur = pScan->aiCur[pScan->iEquiv-1]; assert( pWC!=0 ); + assert( iCur>=0 ); do{ for(pTerm=pWC->a+k; knTerm; k++, pTerm++){ + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 || pTerm->leftCursor<0 ); if( pTerm->leftCursor==iCur && pTerm->u.x.leftColumn==iColumn && (iColumn!=XN_EXPR || wx_sqlite3ExprCompareSkip(pTerm->pExpr->pLeft, pScan->pIdxExpr,iCur)==0) - && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) + && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_OuterON)) ){ if( (pTerm->eOperator & WO_EQUIV)!=0 && pScan->nEquivaiCur) @@ -148702,7 +157409,8 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ } } if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0 - && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN + && (pX = pTerm->pExpr->pRight, ALWAYS(pX!=0)) + && pX->op==TK_COLUMN && pX->iTable==pScan->aiCur[0] && pX->iColumn==pScan->aiColumn[0] ){ @@ -148711,6 +157419,18 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ } pScan->pWC = pWC; pScan->k = k+1; +#ifdef WHERETRACE_ENABLED + if( wx_sqlite3WhereTrace & 0x20000 ){ + int ii; + wx_sqlite3DebugPrintf("SCAN-TERM %p: nEquiv=%d", + pTerm, pScan->nEquiv); + for(ii=0; iinEquiv; ii++){ + wx_sqlite3DebugPrintf(" {%d:%d}", + pScan->aiCur[ii], pScan->aiColumn[ii]); + } + wx_sqlite3DebugPrintf("\n"); + } +#endif return pTerm; } } @@ -148777,16 +157497,16 @@ static WhereTerm *whereScanInit( if( pIdx ){ int j = iColumn; iColumn = pIdx->aiColumn[j]; - if( iColumn==XN_EXPR ){ - pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; - pScan->zCollName = pIdx->azColl[j]; - pScan->aiColumn[0] = XN_EXPR; - return whereScanInitIndexExpr(pScan); - }else if( iColumn==pIdx->pTable->iPKey ){ + if( iColumn==pIdx->pTable->iPKey ){ iColumn = XN_ROWID; }else if( iColumn>=0 ){ pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity; pScan->zCollName = pIdx->azColl[j]; + }else if( iColumn==XN_EXPR ){ + pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; + pScan->zCollName = pIdx->azColl[j]; + pScan->aiColumn[0] = XN_EXPR; + return whereScanInitIndexExpr(pScan); } }else if( iColumn==XN_EXPR ){ return 0; @@ -148867,7 +157587,7 @@ static int findIndexCol( for(i=0; inExpr; i++){ Expr *p = wx_sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr); if( ALWAYS(p!=0) - && p->op==TK_COLUMN + && (p->op==TK_COLUMN || p->op==TK_AGG_COLUMN) && p->iColumn==pIdx->aiColumn[iCol] && p->iTable==iBase ){ @@ -148932,7 +157652,8 @@ static int isDistinctRedundant( for(i=0; inExpr; i++){ Expr *p = wx_sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr); if( NEVER(p==0) ) continue; - if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; + if( p->op!=TK_COLUMN && p->op!=TK_AGG_COLUMN ) continue; + if( p->iTable==iBase && p->iColumn<0 ) return 1; } /* Loop through all indices on the table, checking each to see if it makes @@ -148950,6 +157671,7 @@ static int isDistinctRedundant( */ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( !IsUniqueIndex(pIdx) ) continue; + if( pIdx->pPartIdxWhere ) continue; for(i=0; inKeyCol; i++){ if( 0==wx_sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask)0, WO_EQ, pIdx) ){ if( findIndexCol(pParse, pDistinct, iBase, pIdx, i)<0 ) break; @@ -149003,15 +157725,16 @@ static void translateColumnToCopy( pOp->p1 = pOp->p2 + iRegister; pOp->p2 = pOp->p3; pOp->p3 = 0; + pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */ }else if( pOp->opcode==OP_Rowid ){ - if( iAutoidxCur ){ - pOp->opcode = OP_Sequence; - pOp->p1 = iAutoidxCur; - }else{ + pOp->opcode = OP_Sequence; + pOp->p1 = iAutoidxCur; +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + if( iAutoidxCur==0 ){ pOp->opcode = OP_Null; - pOp->p1 = 0; pOp->p3 = 0; } +#endif } } } @@ -149025,14 +157748,16 @@ static void translateColumnToCopy( #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED) static void whereTraceIndexInfoInputs(wx_sqlite3_index_info *p){ int i; - if( !wx_sqlite3WhereTrace ) return; + if( (wx_sqlite3WhereTrace & 0x10)==0 ) return; for(i=0; inConstraint; i++){ - wx_sqlite3DebugPrintf(" constraint[%d]: col=%d termid=%d op=%d usabled=%d\n", + wx_sqlite3DebugPrintf( + " constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n", i, p->aConstraint[i].iColumn, p->aConstraint[i].iTermOffset, p->aConstraint[i].op, - p->aConstraint[i].usable); + p->aConstraint[i].usable, + wx_sqlite3_vtab_collation(p,i)); } for(i=0; inOrderBy; i++){ wx_sqlite3DebugPrintf(" orderby[%d]: col=%d desc=%d\n", @@ -149043,7 +157768,7 @@ static void whereTraceIndexInfoInputs(wx_sqlite3_index_info *p){ } static void whereTraceIndexInfoOutputs(wx_sqlite3_index_info *p){ int i; - if( !wx_sqlite3WhereTrace ) return; + if( (wx_sqlite3WhereTrace & 0x10)==0 ) return; for(i=0; inConstraint; i++){ wx_sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n", i, @@ -149061,6 +157786,43 @@ static void whereTraceIndexInfoOutputs(wx_sqlite3_index_info *p){ #define whereTraceIndexInfoOutputs(A) #endif +/* +** We know that pSrc is an operand of an outer join. Return true if +** pTerm is a constraint that is compatible with that join. +** +** pTerm must be EP_OuterON if pSrc is the right operand of an +** outer join. pTerm can be either EP_OuterON or EP_InnerON if pSrc +** is the left operand of a RIGHT join. +** +** See https://sqlite.org/forum/forumpost/206d99a16dd9212f +** for an example of a WHERE clause constraints that may not be used on +** the right table of a RIGHT JOIN because the constraint implies a +** not-NULL condition on the left table of the RIGHT JOIN. +*/ +static int constraintCompatibleWithOuterJoin( + const WhereTerm *pTerm, /* WHERE clause term to check */ + const SrcItem *pSrc /* Table we are trying to access */ +){ + assert( (pSrc->fg.jointype&(JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ); /* By caller */ + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); + testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); + testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) + testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); + if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) + || pTerm->pExpr->w.iJoin != pSrc->iCursor + ){ + return 0; + } + if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0 + && ExprHasProperty(pTerm->pExpr, EP_InnerON) + ){ + return 0; + } + return 1; +} + + + #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* ** Return TRUE if the WHERE clause term pTerm is of a form where it @@ -149068,23 +157830,21 @@ static void whereTraceIndexInfoOutputs(wx_sqlite3_index_info *p){ ** index existed. */ static int termCanDriveIndex( - WhereTerm *pTerm, /* WHERE clause term to check */ - SrcItem *pSrc, /* Table we are trying to access */ - Bitmask notReady /* Tables in outer loops of the join */ + const WhereTerm *pTerm, /* WHERE clause term to check */ + const SrcItem *pSrc, /* Table we are trying to access */ + const Bitmask notReady /* Tables in outer loops of the join */ ){ char aff; if( pTerm->leftCursor!=pSrc->iCursor ) return 0; if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0; - if( (pSrc->fg.jointype & JT_LEFT) - && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) - && (pTerm->eOperator & WO_IS) + assert( (pSrc->fg.jointype & JT_RIGHT)==0 ); + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 + && !constraintCompatibleWithOuterJoin(pTerm,pSrc) ){ - /* Cannot use an IS term from the WHERE clause as an index driver for - ** the RHS of a LEFT JOIN. Such a term can only be used if it is from - ** the ON clause. */ - return 0; + return 0; /* See https://sqlite.org/forum/forumpost/51e6959f61 */ } if( (pTerm->prereqRight & notReady)!=0 ) return 0; + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); if( pTerm->u.x.leftColumn<0 ) return 0; aff = pSrc->pTab->aCol[pTerm->u.x.leftColumn].affinity; if( !wx_sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; @@ -149095,16 +157855,67 @@ static int termCanDriveIndex( #ifndef SQLITE_OMIT_AUTOMATIC_INDEX + +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +/* +** Argument pIdx represents an automatic index that the current statement +** will create and populate. Add an OP_Explain with text of the form: +** +** CREATE AUTOMATIC INDEX ON () [WHERE ] +** +** This is only required if wx_sqlite3_stmt_scanstatus() is enabled, to +** associate an SQLITE_SCANSTAT_NCYCLE and SQLITE_SCANSTAT_NLOOP +** values with. In order to avoid breaking legacy code and test cases, +** the OP_Explain is not added if this is an EXPLAIN QUERY PLAN command. +*/ +static void explainAutomaticIndex( + Parse *pParse, + Index *pIdx, /* Automatic index to explain */ + int bPartial, /* True if pIdx is a partial index */ + int *pAddrExplain /* OUT: Address of OP_Explain */ +){ + if( pParse->explain!=2 ){ + Table *pTab = pIdx->pTable; + const char *zSep = ""; + char *zText = 0; + int ii = 0; + wx_sqlite3_str *pStr = wx_sqlite3_str_new(pParse->db); + wx_sqlite3_str_appendf(pStr,"CREATE AUTOMATIC INDEX ON %s(", pTab->zName); + assert( pIdx->nColumn>1 ); + assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID ); + for(ii=0; ii<(pIdx->nColumn-1); ii++){ + const char *zName = 0; + int iCol = pIdx->aiColumn[ii]; + + zName = pTab->aCol[iCol].zCnName; + wx_sqlite3_str_appendf(pStr, "%s%s", zSep, zName); + zSep = ", "; + } + zText = wx_sqlite3_str_finish(pStr); + if( zText==0 ){ + wx_sqlite3OomFault(pParse->db); + }else{ + *pAddrExplain = wx_sqlite3VdbeExplain( + pParse, 0, "%s)%s", zText, (bPartial ? " WHERE " : "") + ); + wx_sqlite3_free(zText); + } + } +} +#else +# define explainAutomaticIndex(a,b,c,d) +#endif + /* ** Generate code to construct the Index object for an automatic index ** and to set up the WhereLevel object pLevel so that the code generator ** makes use of the automatic index. */ -static void constructAutomaticIndex( +static SQLITE_NOINLINE void constructAutomaticIndex( Parse *pParse, /* The parsing context */ - WhereClause *pWC, /* The WHERE clause */ - SrcItem *pSrc, /* The FROM clause term to get the next index */ - Bitmask notReady, /* Mask of cursors that are not available */ + const WhereClause *pWC, /* The WHERE clause */ + const SrcItem *pSrc, /* The FROM clause term to get the next index */ + const Bitmask notReady, /* Mask of cursors that are not available */ WhereLevel *pLevel /* Write new index here */ ){ int nKeyCol; /* Number of columns in the constructed index */ @@ -149130,6 +157941,9 @@ static void constructAutomaticIndex( SrcItem *pTabItem; /* FROM clause term being indexed */ int addrCounter = 0; /* Address where integer counter is initialized */ int regBase; /* Array of registers where record is assembled */ +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + int addrExp = 0; /* Address of OP_Explain */ +#endif /* Generate code to skip over the creation and initialization of the ** transient index on 2nd and subsequent iterations of the loop. */ @@ -149146,25 +157960,27 @@ static void constructAutomaticIndex( idxCols = 0; for(pTerm=pWC->a; pTermpExpr; - assert( !ExprHasProperty(pExpr, EP_FromJoin) /* prereq always non-zero */ - || pExpr->iRightJoinTable!=pSrc->iCursor /* for the right-hand */ - || pLoop->prereq!=0 ); /* table of a LEFT JOIN */ - if( pLoop->prereq==0 - && (pTerm->wtFlags & TERM_VIRTUAL)==0 - && !ExprHasProperty(pExpr, EP_FromJoin) - && wx_sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor) ){ + /* Make the automatic index a partial index if there are terms in the + ** WHERE clause (or the ON clause of a LEFT join) that constrain which + ** rows of the target table (pSrc) that can be used. */ + if( (pTerm->wtFlags & TERM_VIRTUAL)==0 + && wx_sqlite3ExprIsTableConstraint(pExpr, pSrc) + ){ pPartial = wx_sqlite3ExprAnd(pParse, pPartial, wx_sqlite3ExprDup(pParse->db, pExpr, 0)); } if( termCanDriveIndex(pTerm, pSrc, notReady) ){ - int iCol = pTerm->u.x.leftColumn; - Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); + int iCol; + Bitmask cMask; + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); + iCol = pTerm->u.x.leftColumn; + cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); testcase( iCol==BMS ); testcase( iCol==BMS-1 ); if( !sentWarning ){ wx_sqlite3_log(SQLITE_WARNING_AUTOINDEX, "automatic index on %s(%s)", pTable->zName, - pTable->aCol[iCol].zName); + pTable->aCol[iCol].zCnName); sentWarning = 1; } if( (idxCols & cMask)==0 ){ @@ -149176,7 +157992,7 @@ static void constructAutomaticIndex( } } } - assert( nKeyCol>0 ); + assert( nKeyCol>0 || pParse->db->mallocFailed ); pLoop->u.btree.nEq = pLoop->nLTerm = nKeyCol; pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED | WHERE_AUTO_INDEX; @@ -149210,8 +158026,11 @@ static void constructAutomaticIndex( idxCols = 0; for(pTerm=pWC->a; pTermu.x.leftColumn; - Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); + int iCol; + Bitmask cMask; + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); + iCol = pTerm->u.x.leftColumn; + cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); testcase( iCol==BMS-1 ); testcase( iCol==BMS ); if( (idxCols & cMask)==0 ){ @@ -149248,11 +158067,16 @@ static void constructAutomaticIndex( pIdx->azColl[n] = wx_sqlite3StrBINARY; /* Create the automatic index */ + explainAutomaticIndex(pParse, pIdx, pPartial!=0, &addrExp); assert( pLevel->iIdxCur>=0 ); pLevel->iIdxCur = pParse->nTab++; wx_sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); wx_sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "for %s", pTable->zName)); + if( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ){ + pLevel->regFilter = ++pParse->nMem; + wx_sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter); + } /* Fill the automatic index with content */ pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; @@ -149275,6 +158099,11 @@ static void constructAutomaticIndex( regBase = wx_sqlite3GenerateIndexKey( pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0 ); + if( pLevel->regFilter ){ + wx_sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, + regBase, pLoop->u.btree.nEq); + } + wx_sqlite3VdbeScanStatusCounters(v, addrExp, addrExp, wx_sqlite3VdbeCurrentAddr(v)); wx_sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); wx_sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); if( pPartial ) wx_sqlite3VdbeResolveLabel(v, iContinue); @@ -149295,28 +158124,160 @@ static void constructAutomaticIndex( /* Jump here when skipping the initialization */ wx_sqlite3VdbeJumpHere(v, addrInit); + wx_sqlite3VdbeScanStatusRange(v, addrExp, addrExp, -1); end_auto_index_create: wx_sqlite3ExprDelete(pParse->db, pPartial); } #endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ +/* +** Generate bytecode that will initialize a Bloom filter that is appropriate +** for pLevel. +** +** If there are inner loops within pLevel that have the WHERE_BLOOMFILTER +** flag set, initialize a Bloomfilter for them as well. Except don't do +** this recursive initialization if the SQLITE_BloomPulldown optimization has +** been turned off. +** +** When the Bloom filter is initialized, the WHERE_BLOOMFILTER flag is cleared +** from the loop, but the regFilter value is set to a register that implements +** the Bloom filter. When regFilter is positive, the +** wx_sqlite3WhereCodeOneLoopStart() will generate code to test the Bloom filter +** and skip the subsequence B-Tree seek if the Bloom filter indicates that +** no matching rows exist. +** +** This routine may only be called if it has previously been determined that +** the loop would benefit from a Bloom filter, and the WHERE_BLOOMFILTER bit +** is set. +*/ +static SQLITE_NOINLINE void wx_sqlite3ConstructBloomFilter( + WhereInfo *pWInfo, /* The WHERE clause */ + int iLevel, /* Index in pWInfo->a[] that is pLevel */ + WhereLevel *pLevel, /* Make a Bloom filter for this FROM term */ + Bitmask notReady /* Loops that are not ready */ +){ + int addrOnce; /* Address of opening OP_Once */ + int addrTop; /* Address of OP_Rewind */ + int addrCont; /* Jump here to skip a row */ + const WhereTerm *pTerm; /* For looping over WHERE clause terms */ + const WhereTerm *pWCEnd; /* Last WHERE clause term */ + Parse *pParse = pWInfo->pParse; /* Parsing context */ + Vdbe *v = pParse->pVdbe; /* VDBE under construction */ + WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */ + int iCur; /* Cursor for table getting the filter */ + IndexedExpr *saved_pIdxEpr; /* saved copy of Parse.pIdxEpr */ + + saved_pIdxEpr = pParse->pIdxEpr; + pParse->pIdxEpr = 0; + + assert( pLoop!=0 ); + assert( v!=0 ); + assert( pLoop->wsFlags & WHERE_BLOOMFILTER ); + + addrOnce = wx_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + do{ + const SrcItem *pItem; + const Table *pTab; + u64 sz; + wx_sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel); + addrCont = wx_sqlite3VdbeMakeLabel(pParse); + iCur = pLevel->iTabCur; + pLevel->regFilter = ++pParse->nMem; + + /* The Bloom filter is a Blob held in a register. Initialize it + ** to zero-filled blob of at least 80K bits, but maybe more if the + ** estimated size of the table is larger. We could actually + ** measure the size of the table at run-time using OP_Count with + ** P3==1 and use that value to initialize the blob. But that makes + ** testing complicated. By basing the blob size on the value in the + ** sqlite_stat1 table, testing is much easier. + */ + pItem = &pWInfo->pTabList->a[pLevel->iFrom]; + assert( pItem!=0 ); + pTab = pItem->pTab; + assert( pTab!=0 ); + sz = wx_sqlite3LogEstToInt(pTab->nRowLogEst); + if( sz<10000 ){ + sz = 10000; + }else if( sz>10000000 ){ + sz = 10000000; + } + wx_sqlite3VdbeAddOp2(v, OP_Blob, (int)sz, pLevel->regFilter); + + addrTop = wx_sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); + pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm]; + for(pTerm=pWInfo->sWC.a; pTermpExpr; + if( (pTerm->wtFlags & TERM_VIRTUAL)==0 + && wx_sqlite3ExprIsTableConstraint(pExpr, pItem) + ){ + wx_sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); + } + } + if( pLoop->wsFlags & WHERE_IPK ){ + int r1 = wx_sqlite3GetTempReg(pParse); + wx_sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1); + wx_sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, 1); + wx_sqlite3ReleaseTempReg(pParse, r1); + }else{ + Index *pIdx = pLoop->u.btree.pIndex; + int n = pLoop->u.btree.nEq; + int r1 = wx_sqlite3GetTempRange(pParse, n); + int jj; + for(jj=0; jjpTable==pItem->pTab ); + wx_sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iCur, jj, r1+jj); + } + wx_sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n); + wx_sqlite3ReleaseTempRange(pParse, r1, n); + } + wx_sqlite3VdbeResolveLabel(v, addrCont); + wx_sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); + VdbeCoverage(v); + wx_sqlite3VdbeJumpHere(v, addrTop); + pLoop->wsFlags &= ~WHERE_BLOOMFILTER; + if( OptimizationDisabled(pParse->db, SQLITE_BloomPulldown) ) break; + while( ++iLevel < pWInfo->nLevel ){ + const SrcItem *pTabItem; + pLevel = &pWInfo->a[iLevel]; + pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; + if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ) ) continue; + pLoop = pLevel->pWLoop; + if( NEVER(pLoop==0) ) continue; + if( pLoop->prereq & notReady ) continue; + if( (pLoop->wsFlags & (WHERE_BLOOMFILTER|WHERE_COLUMN_IN)) + ==WHERE_BLOOMFILTER + ){ + /* This is a candidate for bloom-filter pull-down (early evaluation). + ** The test that WHERE_COLUMN_IN is omitted is important, as we are + ** not able to do early evaluation of bloom filters that make use of + ** the IN operator */ + break; + } + } + }while( iLevel < pWInfo->nLevel ); + wx_sqlite3VdbeJumpHere(v, addrOnce); + pParse->pIdxEpr = saved_pIdxEpr; +} + + #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Allocate and populate an wx_sqlite3_index_info structure. It is the ** responsibility of the caller to eventually release the structure -** by passing the pointer returned by this function to wx_sqlite3_free(). +** by passing the pointer returned by this function to freeIndexInfo(). */ static wx_sqlite3_index_info *allocateIndexInfo( - Parse *pParse, /* The parsing context */ + WhereInfo *pWInfo, /* The WHERE clause */ WhereClause *pWC, /* The WHERE clause being analyzed */ Bitmask mUnusable, /* Ignore terms with these prereqs */ SrcItem *pSrc, /* The FROM clause term that is the vtab */ - ExprList *pOrderBy, /* The ORDER BY clause */ u16 *pmNoOmit /* Mask of terms not to omit */ ){ int i, j; int nTerm; + Parse *pParse = pWInfo->pParse; struct wx_sqlite3_index_constraint *pIdxCons; struct wx_sqlite3_index_orderby *pIdxOrderBy; struct wx_sqlite3_index_constraint_usage *pUsage; @@ -149325,10 +158286,21 @@ static wx_sqlite3_index_info *allocateIndexInfo( int nOrderBy; wx_sqlite3_index_info *pIdxInfo; u16 mNoOmit = 0; + const Table *pTab; + int eDistinct = 0; + ExprList *pOrderBy = pWInfo->pOrderBy; + + assert( pSrc!=0 ); + pTab = pSrc->pTab; + assert( pTab!=0 ); + assert( IsVirtual(pTab) ); - /* Count the number of possible WHERE clause constraints referring - ** to this virtual table */ + /* Find all WHERE clause constraints referring to this virtual table. + ** Mark each term with the TERM_OK flag. Set nTerm to the number of + ** terms found. + */ for(i=nTerm=0, pTerm=pWC->a; inTerm; i++, pTerm++){ + pTerm->wtFlags &= ~TERM_OK; if( pTerm->leftCursor != pSrc->iCursor ) continue; if( pTerm->prereqRight & mUnusable ) continue; assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); @@ -149338,8 +158310,17 @@ static wx_sqlite3_index_info *allocateIndexInfo( testcase( pTerm->eOperator & WO_ALL ); if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; - assert( pTerm->u.x.leftColumn>=(-1) ); + + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); + assert( pTerm->u.x.leftColumn>=XN_ROWID ); + assert( pTerm->u.x.leftColumnnCol ); + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 + && !constraintCompatibleWithOuterJoin(pTerm,pSrc) + ){ + continue; + } nTerm++; + pTerm->wtFlags |= TERM_OK; } /* If the ORDER BY clause contains only columns in the current @@ -149351,11 +158332,49 @@ static wx_sqlite3_index_info *allocateIndexInfo( int n = pOrderBy->nExpr; for(i=0; ia[i].pExpr; - if( pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor ) break; - if( pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL ) break; + Expr *pE2; + + /* Skip over constant terms in the ORDER BY clause */ + if( wx_sqlite3ExprIsConstant(pExpr) ){ + continue; + } + + /* Virtual tables are unable to deal with NULLS FIRST */ + if( pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) break; + + /* First case - a direct column references without a COLLATE operator */ + if( pExpr->op==TK_COLUMN && pExpr->iTable==pSrc->iCursor ){ + assert( pExpr->iColumn>=XN_ROWID && pExpr->iColumnnCol ); + continue; + } + + /* 2nd case - a column reference with a COLLATE operator. Only match + ** of the COLLATE operator matches the collation of the column. */ + if( pExpr->op==TK_COLLATE + && (pE2 = pExpr->pLeft)->op==TK_COLUMN + && pE2->iTable==pSrc->iCursor + ){ + const char *zColl; /* The collating sequence name */ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + assert( pExpr->u.zToken!=0 ); + assert( pE2->iColumn>=XN_ROWID && pE2->iColumnnCol ); + pExpr->iColumn = pE2->iColumn; + if( pE2->iColumn<0 ) continue; /* Collseq does not matter for rowid */ + zColl = wx_sqlite3ColumnColl(&pTab->aCol[pE2->iColumn]); + if( zColl==0 ) zColl = wx_sqlite3StrBINARY; + if( wx_sqlite3_stricmp(pExpr->u.zToken, zColl)==0 ) continue; + } + + /* No matches cause a break out of the loop */ + break; } - if( i==n){ + if( i==n ){ nOrderBy = n; + if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) ){ + eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0); + }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ + eDistinct = 1; + } } } @@ -149363,46 +158382,35 @@ static wx_sqlite3_index_info *allocateIndexInfo( */ pIdxInfo = wx_sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm - + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) ); + + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) + + sizeof(wx_sqlite3_value*)*nTerm ); if( pIdxInfo==0 ){ wx_sqlite3ErrorMsg(pParse, "out of memory"); return 0; } pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; - pIdxCons = (struct wx_sqlite3_index_constraint*)&pHidden[1]; + pIdxCons = (struct wx_sqlite3_index_constraint*)&pHidden->aRhs[nTerm]; pIdxOrderBy = (struct wx_sqlite3_index_orderby*)&pIdxCons[nTerm]; pUsage = (struct wx_sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy]; - pIdxInfo->nOrderBy = nOrderBy; pIdxInfo->aConstraint = pIdxCons; pIdxInfo->aOrderBy = pIdxOrderBy; pIdxInfo->aConstraintUsage = pUsage; pHidden->pWC = pWC; pHidden->pParse = pParse; + pHidden->eDistinct = eDistinct; + pHidden->mIn = 0; for(i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++){ u16 op; - if( pTerm->leftCursor != pSrc->iCursor ) continue; - if( pTerm->prereqRight & mUnusable ) continue; - assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); - testcase( pTerm->eOperator & WO_IN ); - testcase( pTerm->eOperator & WO_IS ); - testcase( pTerm->eOperator & WO_ISNULL ); - testcase( pTerm->eOperator & WO_ALL ); - if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; - if( pTerm->wtFlags & TERM_VNULL ) continue; - - /* tag-20191211-002: WHERE-clause constraints are not useful to the - ** right-hand table of a LEFT JOIN. See tag-20191211-001 for the - ** equivalent restriction for ordinary tables. */ - if( (pSrc->fg.jointype & JT_LEFT)!=0 - && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) - ){ - continue; - } - assert( pTerm->u.x.leftColumn>=(-1) ); + if( (pTerm->wtFlags & TERM_OK)==0 ) continue; pIdxCons[j].iColumn = pTerm->u.x.leftColumn; pIdxCons[j].iTermOffset = i; op = pTerm->eOperator & WO_ALL; - if( op==WO_IN ) op = WO_EQ; + if( op==WO_IN ){ + if( (pTerm->wtFlags & TERM_SLICE)==0 ){ + pHidden->mIn |= SMASKBIT32(j); + } + op = WO_EQ; + } if( op==WO_AUX ){ pIdxCons[j].op = pTerm->eMatchOp; }else if( op & (WO_ISNULL|WO_IS) ){ @@ -149435,17 +158443,42 @@ static wx_sqlite3_index_info *allocateIndexInfo( j++; } + assert( j==nTerm ); pIdxInfo->nConstraint = j; - for(i=0; ia[i].pExpr; - pIdxOrderBy[i].iColumn = pExpr->iColumn; - pIdxOrderBy[i].desc = pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC; + if( wx_sqlite3ExprIsConstant(pExpr) ) continue; + assert( pExpr->op==TK_COLUMN + || (pExpr->op==TK_COLLATE && pExpr->pLeft->op==TK_COLUMN + && pExpr->iColumn==pExpr->pLeft->iColumn) ); + pIdxOrderBy[j].iColumn = pExpr->iColumn; + pIdxOrderBy[j].desc = pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC; + j++; } + pIdxInfo->nOrderBy = j; *pmNoOmit = mNoOmit; return pIdxInfo; } +/* +** Free an wx_sqlite3_index_info structure allocated by allocateIndexInfo() +** and possibly modified by xBestIndex methods. +*/ +static void freeIndexInfo(wx_sqlite3 *db, wx_sqlite3_index_info *pIdxInfo){ + HiddenIndexInfo *pHidden; + int i; + assert( pIdxInfo!=0 ); + pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + assert( pHidden->pParse!=0 ); + assert( pHidden->pParse->db==db ); + for(i=0; inConstraint; i++){ + wx_sqlite3ValueFree(pHidden->aRhs[i]); /* IMP: R-14553-25174 */ + pHidden->aRhs[i] = 0; + } + wx_sqlite3DbFree(db, pIdxInfo); +} + /* ** The table object reference passed as the second argument to this function ** must represent a virtual table. This function invokes the xBestIndex() @@ -149467,7 +158500,9 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, wx_sqlite3_index_info *p){ int rc; whereTraceIndexInfoInputs(p); + pParse->db->nSchemaLock++; rc = pVtab->pModule->xBestIndex(pVtab, p); + pParse->db->nSchemaLock--; whereTraceIndexInfoOutputs(p); if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){ @@ -149521,7 +158556,8 @@ static int whereKeyStats( #endif assert( pRec!=0 ); assert( pIdx->nSample>0 ); - assert( pRec->nField>0 && pRec->nField<=pIdx->nSampleCol ); + assert( pRec->nField>0 ); + /* Do a binary search to find the first sample greater than or equal ** to pRec. If pRec contains a single field, the set of samples to search @@ -149567,7 +158603,12 @@ static int whereKeyStats( ** it is extended to two fields. The duplicates that this creates do not ** cause any problems. */ - nField = pRec->nField; + if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){ + nField = pIdx->nKeyCol; + }else{ + nField = pIdx->nColumn; + } + nField = MIN(pRec->nField, nField); iCol = 0; iSample = pIdx->nSample * nField; do{ @@ -149633,12 +158674,12 @@ static int whereKeyStats( if( iCol>0 ){ pRec->nField = iCol; assert( wx_sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0 - || pParse->db->mallocFailed ); + || pParse->db->mallocFailed || CORRUPT_DB ); } if( i>0 ){ pRec->nField = nField; assert( wx_sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0 - || pParse->db->mallocFailed ); + || pParse->db->mallocFailed || CORRUPT_DB ); } } } @@ -149655,7 +158696,7 @@ static int whereKeyStats( ** is larger than all samples in the array. */ tRowcnt iUpper, iGap; if( i>=pIdx->nSample ){ - iUpper = wx_sqlite3LogEstToInt(pIdx->aiRowLogEst[0]); + iUpper = pIdx->nRowEst0; }else{ iUpper = aSample[i].anLt[iCol]; } @@ -149811,7 +158852,7 @@ static int whereRangeSkipScanEst( int nAdjust = (wx_sqlite3LogEst(p->nSample) - wx_sqlite3LogEst(nDiff)); pLoop->nOut -= nAdjust; *pbDone = 1; - WHERETRACE(0x10, ("range skip-scan regions: %u..%u adjust=%d est=%d\n", + WHERETRACE(0x20, ("range skip-scan regions: %u..%u adjust=%d est=%d\n", nLower, nUpper, nAdjust*-1, pLoop->nOut)); } @@ -149989,7 +159030,7 @@ static int whereRangeScanEst( if( nNewnOut>nOut ){ - WHERETRACE(0x10,("Range scan lowers nOut from %d to %d\n", + WHERETRACE(0x20,("Range scan lowers nOut from %d to %d\n", pLoop->nOut, nOut)); } #endif @@ -150087,7 +159128,7 @@ static int whereEqualScanEst( pBuilder->nRecValid = nEq; whereKeyStats(pParse, p, pRec, 0, a); - WHERETRACE(0x10,("equality scan regions %s(%d): %d\n", + WHERETRACE(0x20,("equality scan regions %s(%d): %d\n", p->zName, nEq-1, (int)a[1])); *pnRow = a[1]; @@ -150135,9 +159176,9 @@ static int whereInScanEst( } if( rc==SQLITE_OK ){ - if( nRowEst > nRow0 ) nRowEst = nRow0; + if( nRowEst > (tRowcnt)nRow0 ) nRowEst = nRow0; *pnRow = nRowEst; - WHERETRACE(0x10,("IN row estimate: est=%d\n", nRowEst)); + WHERETRACE(0x20,("IN row estimate: est=%d\n", nRowEst)); } assert( pBuilder->nRecValid==nRecValid ); return rc; @@ -150158,9 +159199,10 @@ SQLITE_PRIVATE void wx_sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ memcpy(zType, "....", 5); if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V'; if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E'; - if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L'; + if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) zType[2] = 'L'; if( pTerm->wtFlags & TERM_CODED ) zType[3] = 'C'; if( pTerm->eOperator & WO_SINGLE ){ + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); wx_sqlite3_snprintf(sizeof(zLeft),zLeft,"left={%d:%d}", pTerm->leftCursor, pTerm->u.x.leftColumn); }else if( (pTerm->eOperator & WO_OR)!=0 && pTerm->u.pOrInfo!=0 ){ @@ -150178,7 +159220,7 @@ SQLITE_PRIVATE void wx_sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ wx_sqlite3DebugPrintf(" prob=%-3d prereq=%llx,%llx", pTerm->truthProb, (u64)pTerm->prereqAll, (u64)pTerm->prereqRight); } - if( pTerm->u.x.iField ){ + if( (pTerm->eOperator & (WO_OR|WO_AND))==0 && pTerm->u.x.iField ){ wx_sqlite3DebugPrintf(" iField=%d", pTerm->u.x.iField); } if( pTerm->iParent>=0 ){ @@ -150240,12 +159282,12 @@ SQLITE_PRIVATE void wx_sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){ wx_sqlite3_free(z); } if( p->wsFlags & WHERE_SKIPSCAN ){ - wx_sqlite3DebugPrintf(" f %05x %d-%d", p->wsFlags, p->nLTerm,p->nSkip); + wx_sqlite3DebugPrintf(" f %06x %d-%d", p->wsFlags, p->nLTerm,p->nSkip); }else{ - wx_sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm); + wx_sqlite3DebugPrintf(" f %06x N %d", p->wsFlags, p->nLTerm); } wx_sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); - if( p->nLTerm && (wx_sqlite3WhereTrace & 0x100)!=0 ){ + if( p->nLTerm && (wx_sqlite3WhereTrace & 0x4000)!=0 ){ int i; for(i=0; inLTerm; i++){ wx_sqlite3WhereTermPrint(p->aLTerm[i], i); @@ -150283,12 +159325,18 @@ static void whereLoopClearUnion(wx_sqlite3 *db, WhereLoop *p){ } /* -** Deallocate internal memory used by a WhereLoop object +** Deallocate internal memory used by a WhereLoop object. Leave the +** object in an initialized state, as if it had been newly allocated. */ static void whereLoopClear(wx_sqlite3 *db, WhereLoop *p){ - if( p->aLTerm!=p->aLTermSpace ) wx_sqlite3DbFreeNN(db, p->aLTerm); + if( p->aLTerm!=p->aLTermSpace ){ + wx_sqlite3DbFreeNN(db, p->aLTerm); + p->aLTerm = p->aLTermSpace; + p->nLSlot = ArraySize(p->aLTermSpace); + } whereLoopClearUnion(db, p); - whereLoopInit(p); + p->nLTerm = 0; + p->wsFlags = 0; } /* @@ -150312,8 +159360,10 @@ static int whereLoopResize(wx_sqlite3 *db, WhereLoop *p, int n){ */ static int whereLoopXfer(wx_sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){ whereLoopClearUnion(db, pTo); - if( whereLoopResize(db, pTo, pFrom->nLTerm) ){ - memset(&pTo->u, 0, sizeof(pTo->u)); + if( pFrom->nLTerm > pTo->nLSlot + && whereLoopResize(db, pTo, pFrom->nLTerm) + ){ + memset(pTo, 0, WHERE_LOOP_XFER_SZ); return SQLITE_NOMEM_BKPT; } memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ); @@ -150330,36 +159380,36 @@ static int whereLoopXfer(wx_sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){ ** Delete a WhereLoop object */ static void whereLoopDelete(wx_sqlite3 *db, WhereLoop *p){ + assert( db!=0 ); whereLoopClear(db, p); - wx_sqlite3DbFreeNN(db, p); + wx_sqlite3DbNNFreeNN(db, p); } /* ** Free a WhereInfo structure */ static void whereInfoFree(wx_sqlite3 *db, WhereInfo *pWInfo){ - int i; assert( pWInfo!=0 ); - for(i=0; inLevel; i++){ - WhereLevel *pLevel = &pWInfo->a[i]; - if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){ - wx_sqlite3DbFree(db, pLevel->u.in.aInLoop); - } - } + assert( db!=0 ); wx_sqlite3WhereClauseClear(&pWInfo->sWC); while( pWInfo->pLoops ){ WhereLoop *p = pWInfo->pLoops; pWInfo->pLoops = p->pNextLoop; whereLoopDelete(db, p); } - assert( pWInfo->pExprMods==0 ); - wx_sqlite3DbFreeNN(db, pWInfo); + while( pWInfo->pMemToFree ){ + WhereMemBlock *pNext = pWInfo->pMemToFree->pNext; + wx_sqlite3DbNNFreeNN(db, pWInfo->pMemToFree); + pWInfo->pMemToFree = pNext; + } + wx_sqlite3DbNNFreeNN(db, pWInfo); } /* ** Return TRUE if all of the following are true: ** -** (1) X has the same or lower cost that Y +** (1) X has the same or lower cost, or returns the same or fewer rows, +** than Y. ** (2) X uses fewer WHERE clause terms than Y ** (3) Every WHERE clause term used by X is also used by Y ** (4) X skips at least as many columns as Y @@ -150382,11 +159432,8 @@ static int whereLoopCheaperProperSubset( if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){ return 0; /* X is not a subset of Y */ } + if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; if( pY->nSkip > pX->nSkip ) return 0; - if( pX->rRun >= pY->rRun ){ - if( pX->rRun > pY->rRun ) return 0; /* X costs more than Y */ - if( pX->nOut > pY->nOut ) return 0; /* X costs more than Y */ - } for(i=pX->nLTerm-1; i>=0; i--){ if( pX->aLTerm[i]==0 ) continue; for(j=pY->nLTerm-1; j>=0; j--){ @@ -150402,8 +159449,8 @@ static int whereLoopCheaperProperSubset( } /* -** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so -** that: +** Try to adjust the cost and number of output rows of WhereLoop pTemplate +** upwards or downwards so that: ** ** (1) pTemplate costs less than any other WhereLoops that are a proper ** subset of pTemplate @@ -150424,16 +159471,20 @@ static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){ /* Adjust pTemplate cost downward so that it is cheaper than its ** subset p. */ WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", - pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut-1)); - pTemplate->rRun = p->rRun; - pTemplate->nOut = p->nOut - 1; + pTemplate->rRun, pTemplate->nOut, + MIN(p->rRun, pTemplate->rRun), + MIN(p->nOut - 1, pTemplate->nOut))); + pTemplate->rRun = MIN(p->rRun, pTemplate->rRun); + pTemplate->nOut = MIN(p->nOut - 1, pTemplate->nOut); }else if( whereLoopCheaperProperSubset(pTemplate, p) ){ /* Adjust pTemplate cost upward so that it is costlier than p since ** pTemplate is a proper subset of p */ WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", - pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut+1)); - pTemplate->rRun = p->rRun; - pTemplate->nOut = p->nOut + 1; + pTemplate->rRun, pTemplate->nOut, + MAX(p->rRun, pTemplate->rRun), + MAX(p->nOut + 1, pTemplate->nOut))); + pTemplate->rRun = MAX(p->rRun, pTemplate->rRun); + pTemplate->nOut = MAX(p->nOut + 1, pTemplate->nOut); } } } @@ -150688,11 +159739,11 @@ static void whereLoopOutputAdjust( LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */ assert( (pLoop->wsFlags & WHERE_AUTO_INDEX)==0 ); - for(i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++){ + for(i=pWC->nBase, pTerm=pWC->a; i>0; i--, pTerm++){ assert( pTerm!=0 ); - if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) break; - if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue; if( (pTerm->prereqAll & notAllowed)!=0 ) continue; + if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue; + if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) continue; for(j=pLoop->nLTerm-1; j>=0; j--){ pX = pLoop->aLTerm[j]; if( pX==0 ) continue; @@ -150700,6 +159751,24 @@ static void whereLoopOutputAdjust( if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break; } if( j<0 ){ + wx_sqlite3ProgressCheck(pWC->pWInfo->pParse); + if( pLoop->maskSelf==pTerm->prereqAll ){ + /* If there are extra terms in the WHERE clause not used by an index + ** that depend only on the table being scanned, and that will tend to + ** cause many rows to be omitted, then mark that table as + ** "self-culling". + ** + ** 2022-03-24: Self-culling only applies if either the extra terms + ** are straight comparison operators that are non-true with NULL + ** operand, or if the loop is not an OUTER JOIN. + */ + if( (pTerm->eOperator & 0x3f)!=0 + || (pWC->pWInfo->pTabList->a[pLoop->iTab].fg.jointype + & (JT_LEFT|JT_LTORJ))==0 + ){ + pLoop->wsFlags |= WHERE_SELFCULL; + } + } if( pTerm->truthProb<=0 ){ /* If a truth probability is specified using the likelihood() hints, ** then use the probability provided by the application. */ @@ -150727,7 +159796,9 @@ static void whereLoopOutputAdjust( } } } - if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce; + if( pLoop->nOut > nRow-iReduce ){ + pLoop->nOut = nRow - iReduce; + } } /* @@ -150764,9 +159835,12 @@ static int whereRangeVectorLen( char aff; /* Comparison affinity */ char idxaff = 0; /* Indexed columns affinity */ CollSeq *pColl; /* Comparison collation sequence */ - Expr *pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr; - Expr *pRhs = pTerm->pExpr->pRight; - if( pRhs->flags & EP_xIsSelect ){ + Expr *pLhs, *pRhs; + + assert( ExprUseXList(pTerm->pExpr->pLeft) ); + pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr; + pRhs = pTerm->pExpr->pRight; + if( ExprUseXSelect(pRhs) ){ pRhs = pRhs->x.pSelect->pEList->a[i].pExpr; }else{ pRhs = pRhs->x.pList->a[i].pExpr; @@ -150845,7 +159919,10 @@ static int whereLoopAddBtreeIndex( WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ pNew = pBuilder->pNew; - if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; + assert( db->mallocFailed==0 || pParse->nErr>0 ); + if( pParse->nErr ){ + return pParse->rc; + } WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d, rRun=%d\n", pProbe->pTable->zName,pProbe->zName, pNew->u.btree.nEq, pNew->nSkip, pNew->rRun)); @@ -150861,6 +159938,8 @@ static int whereLoopAddBtreeIndex( if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); assert( pNew->u.btree.nEqnColumn ); + assert( pNew->u.btree.nEqnKeyCol + || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY ); saved_nEq = pNew->u.btree.nEq; saved_nBtm = pNew->u.btree.nBtm; @@ -150894,15 +159973,11 @@ static int whereLoopAddBtreeIndex( ** to mix with a lower range bound from some other source */ if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue; - /* tag-20191211-001: Do not allow constraints from the WHERE clause to - ** be used by the right table of a LEFT JOIN. Only constraints in the - ** ON clause are allowed. See tag-20191211-002 for the vtab equivalent. */ - if( (pSrc->fg.jointype & JT_LEFT)!=0 - && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 + && !constraintCompatibleWithOuterJoin(pTerm,pSrc) ){ continue; } - if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){ pBuilder->bldFlags1 |= SQLITE_BLDF1_UNIQUE; }else{ @@ -150913,7 +159988,11 @@ static int whereLoopAddBtreeIndex( pNew->u.btree.nBtm = saved_nBtm; pNew->u.btree.nTop = saved_nTop; pNew->nLTerm = saved_nLTerm; - if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ + if( pNew->nLTerm>=pNew->nLSlot + && whereLoopResize(db, pNew, pNew->nLTerm+1) + ){ + break; /* OOM while trying to enlarge the pNew->aLTerm array */ + } pNew->aLTerm[pNew->nLTerm++] = pTerm; pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf; @@ -150925,7 +160004,7 @@ static int whereLoopAddBtreeIndex( if( eOp & WO_IN ){ Expr *pExpr = pTerm->pExpr; - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( ExprUseXSelect(pExpr) ){ /* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */ int i; nIn = 46; assert( 46==wx_sqlite3LogEst(25) ); @@ -150943,7 +160022,7 @@ static int whereLoopAddBtreeIndex( nIn = wx_sqlite3LogEst(pExpr->x.pList->nExpr); } if( pProbe->hasStat1 && rLogSize>=10 ){ - LogEst M, logK, safetyMargin; + LogEst M, logK, x; /* Let: ** N = the total number of rows in the table ** K = the number of entries on the RHS of the IN operator @@ -150966,16 +160045,25 @@ static int whereLoopAddBtreeIndex( */ M = pProbe->aiRowLogEst[saved_nEq]; logK = estLog(nIn); - safetyMargin = 10; /* TUNING: extra weight for indexed IN */ - if( M + logK + safetyMargin < nIn + rLogSize ){ + /* TUNING v----- 10 to bias toward indexed IN */ + x = M + logK + 10 - (nIn + rLogSize); + if( x>=0 ){ WHERETRACE(0x40, - ("Scan preferred over IN operator on column %d of \"%s\" (%d<%d)\n", - saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); + ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d) " + "prefers indexed lookup\n", + saved_nEq, M, logK, nIn, rLogSize, x)); + }else if( nInMul<2 && OptimizationEnabled(db, SQLITE_SeekScan) ){ + WHERETRACE(0x40, + ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d" + " nInMul=%d) prefers skip-scan\n", + saved_nEq, M, logK, nIn, rLogSize, x, nInMul)); pNew->wsFlags |= WHERE_IN_SEEKSCAN; }else{ WHERETRACE(0x40, - ("IN operator preferred on column %d of \"%s\" (%d>=%d)\n", - saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); + ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d" + " nInMul=%d) prefers normal scan\n", + saved_nEq, M, logK, nIn, rLogSize, x, nInMul)); + continue; } } pNew->wsFlags |= WHERE_COLUMN_IN; @@ -150994,40 +160082,42 @@ static int whereLoopAddBtreeIndex( pNew->wsFlags |= WHERE_UNQ_WANTED; } } + if( scan.iEquiv>1 ) pNew->wsFlags |= WHERE_TRANSCONS; }else if( eOp & WO_ISNULL ){ pNew->wsFlags |= WHERE_COLUMN_NULL; - }else if( eOp & (WO_GT|WO_GE) ){ - testcase( eOp & WO_GT ); - testcase( eOp & WO_GE ); - pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; - pNew->u.btree.nBtm = whereRangeVectorLen( - pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm - ); - pBtm = pTerm; - pTop = 0; - if( pTerm->wtFlags & TERM_LIKEOPT ){ - /* Range constraints that come from the LIKE optimization are - ** always used in pairs. */ - pTop = &pTerm[1]; - assert( (pTop-(pTerm->pWC->a))pWC->nTerm ); - assert( pTop->wtFlags & TERM_LIKEOPT ); - assert( pTop->eOperator==WO_LT ); - if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ - pNew->aLTerm[pNew->nLTerm++] = pTop; - pNew->wsFlags |= WHERE_TOP_LIMIT; - pNew->u.btree.nTop = 1; - } }else{ - assert( eOp & (WO_LT|WO_LE) ); - testcase( eOp & WO_LT ); - testcase( eOp & WO_LE ); - pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT; - pNew->u.btree.nTop = whereRangeVectorLen( + int nVecLen = whereRangeVectorLen( pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm ); - pTop = pTerm; - pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ? - pNew->aLTerm[pNew->nLTerm-2] : 0; + if( eOp & (WO_GT|WO_GE) ){ + testcase( eOp & WO_GT ); + testcase( eOp & WO_GE ); + pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; + pNew->u.btree.nBtm = nVecLen; + pBtm = pTerm; + pTop = 0; + if( pTerm->wtFlags & TERM_LIKEOPT ){ + /* Range constraints that come from the LIKE optimization are + ** always used in pairs. */ + pTop = &pTerm[1]; + assert( (pTop-(pTerm->pWC->a))pWC->nTerm ); + assert( pTop->wtFlags & TERM_LIKEOPT ); + assert( pTop->eOperator==WO_LT ); + if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ + pNew->aLTerm[pNew->nLTerm++] = pTop; + pNew->wsFlags |= WHERE_TOP_LIMIT; + pNew->u.btree.nTop = 1; + } + }else{ + assert( eOp & (WO_LT|WO_LE) ); + testcase( eOp & WO_LT ); + testcase( eOp & WO_LE ); + pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT; + pNew->u.btree.nTop = nVecLen; + pTop = pTerm; + pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ? + pNew->aLTerm[pNew->nLTerm-2] : 0; + } } /* At this point pNew->nOut is set to the number of rows expected to @@ -151055,8 +160145,8 @@ static int whereLoopAddBtreeIndex( tRowcnt nOut = 0; if( nInMul==0 && pProbe->nSample - && pNew->u.btree.nEq<=pProbe->nSampleCol - && ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect)) + && ALWAYS(pNew->u.btree.nEq<=pProbe->nSampleCol) + && ((eOp & WO_IN)==0 || ExprUseXList(pTerm->pExpr)) && OptimizationEnabled(db, SQLITE_Stat4) ){ Expr *pExpr = pTerm->pExpr; @@ -151079,7 +160169,7 @@ static int whereLoopAddBtreeIndex( && pNew->nOut+10 > pProbe->aiRowLogEst[0] ){ #if WHERETRACE_ENABLED /* 0x01 */ - if( wx_sqlite3WhereTrace & 0x01 ){ + if( wx_sqlite3WhereTrace & 0x20 ){ wx_sqlite3DebugPrintf( "STAT4 determines term has low selectivity:\n"); wx_sqlite3WhereTermPrint(pTerm, 999); @@ -151116,9 +160206,17 @@ static int whereLoopAddBtreeIndex( ** seek only. Then, if this is a non-covering index, add the cost of ** visiting the rows in the main table. */ assert( pSrc->pTab->szTabRow>0 ); - rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow; + if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ + /* The pProbe->szIdxRow is low for an IPK table since the interior + ** pages are small. Thuse szIdxRow gives a good estimate of seek cost. + ** But the leaf pages are full-size, so pProbe->szIdxRow would badly + ** under-estimate the scanning cost. */ + rCostIdx = pNew->nOut + 16; + }else{ + rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow; + } pNew->rRun = wx_sqlite3LogEstAdd(rLogSize, rCostIdx); - if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){ + if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK|WHERE_EXPRIDX))==0 ){ pNew->rRun = wx_sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16); } ApplyCostMultiplier(pNew->rRun, pProbe->pTable->costMult); @@ -151137,7 +160235,12 @@ static int whereLoopAddBtreeIndex( if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 && pNew->u.btree.nEqnColumn + && (pNew->u.btree.nEqnKeyCol || + pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY) ){ + if( pNew->u.btree.nEq>3 ){ + wx_sqlite3ProgressCheck(pParse); + } whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn); } pNew->nOut = saved_nOut; @@ -151240,24 +160343,48 @@ static int indexMightHelpWithOrderBy( */ static int whereUsablePartialIndex( int iTab, /* The table for which we want an index */ - int isLeft, /* True if iTab is the right table of a LEFT JOIN */ + u8 jointype, /* The JT_* flags on the join */ WhereClause *pWC, /* The WHERE clause of the query */ Expr *pWhere /* The WHERE clause from the partial index */ ){ int i; WhereTerm *pTerm; - Parse *pParse = pWC->pWInfo->pParse; + Parse *pParse; + + if( jointype & JT_LTORJ ) return 0; + pParse = pWC->pWInfo->pParse; while( pWhere->op==TK_AND ){ - if( !whereUsablePartialIndex(iTab,isLeft,pWC,pWhere->pLeft) ) return 0; + if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0; pWhere = pWhere->pRight; } if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0; for(i=0, pTerm=pWC->a; inTerm; i++, pTerm++){ Expr *pExpr; pExpr = pTerm->pExpr; - if( (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) - && (isLeft==0 || ExprHasProperty(pExpr, EP_FromJoin)) + if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab) + && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON)) && wx_sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab) + && (pTerm->wtFlags & TERM_VNULL)==0 + ){ + return 1; + } + } + return 0; +} + +/* +** pIdx is an index containing expressions. Check it see if any of the +** expressions in the index match the pExpr expression. +*/ +static int exprIsCoveredByIndex( + const Expr *pExpr, + const Index *pIdx, + int iTabCur +){ + int i; + for(i=0; inColumn; i++){ + if( pIdx->aiColumn[i]==XN_EXPR + && wx_sqlite3ExprCompare(0, pExpr, pIdx->aColExpr->a[i].pExpr, iTabCur)==0 ){ return 1; } @@ -151265,6 +160392,129 @@ static int whereUsablePartialIndex( return 0; } +/* +** Structure passed to the whereIsCoveringIndex Walker callback. +*/ +typedef struct CoveringIndexCheck CoveringIndexCheck; +struct CoveringIndexCheck { + Index *pIdx; /* The index */ + int iTabCur; /* Cursor number for the corresponding table */ + u8 bExpr; /* Uses an indexed expression */ + u8 bUnidx; /* Uses an unindexed column not within an indexed expr */ +}; + +/* +** Information passed in is pWalk->u.pCovIdxCk. Call it pCk. +** +** If the Expr node references the table with cursor pCk->iTabCur, then +** make sure that column is covered by the index pCk->pIdx. We know that +** all columns less than 63 (really BMS-1) are covered, so we don't need +** to check them. But we do need to check any column at 63 or greater. +** +** If the index does not cover the column, then set pWalk->eCode to +** non-zero and return WRC_Abort to stop the search. +** +** If this node does not disprove that the index can be a covering index, +** then just return WRC_Continue, to continue the search. +** +** If pCk->pIdx contains indexed expressions and one of those expressions +** matches pExpr, then prune the search. +*/ +static int whereIsCoveringIndexWalkCallback(Walker *pWalk, Expr *pExpr){ + int i; /* Loop counter */ + const Index *pIdx; /* The index of interest */ + const i16 *aiColumn; /* Columns contained in the index */ + u16 nColumn; /* Number of columns in the index */ + CoveringIndexCheck *pCk; /* Info about this search */ + + pCk = pWalk->u.pCovIdxCk; + pIdx = pCk->pIdx; + if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN) ){ + /* if( pExpr->iColumn<(BMS-1) && pIdx->bHasExpr==0 ) return WRC_Continue;*/ + if( pExpr->iTable!=pCk->iTabCur ) return WRC_Continue; + pIdx = pWalk->u.pCovIdxCk->pIdx; + aiColumn = pIdx->aiColumn; + nColumn = pIdx->nColumn; + for(i=0; iiColumn ) return WRC_Continue; + } + pCk->bUnidx = 1; + return WRC_Abort; + }else if( pIdx->bHasExpr + && exprIsCoveredByIndex(pExpr, pIdx, pWalk->u.pCovIdxCk->iTabCur) ){ + pCk->bExpr = 1; + return WRC_Prune; + } + return WRC_Continue; +} + + +/* +** pIdx is an index that covers all of the low-number columns used by +** pWInfo->pSelect (columns from 0 through 62) or an index that has +** expressions terms. Hence, we cannot determine whether or not it is +** a covering index by using the colUsed bitmasks. We have to do a search +** to see if the index is covering. This routine does that search. +** +** The return value is one of these: +** +** 0 The index is definitely not a covering index +** +** WHERE_IDX_ONLY The index is definitely a covering index +** +** WHERE_EXPRIDX The index is likely a covering index, but it is +** difficult to determine precisely because of the +** expressions that are indexed. Score it as a +** covering index, but still keep the main table open +** just in case we need it. +** +** This routine is an optimization. It is always safe to return zero. +** But returning one of the other two values when zero should have been +** returned can lead to incorrect bytecode and assertion faults. +*/ +static SQLITE_NOINLINE u32 whereIsCoveringIndex( + WhereInfo *pWInfo, /* The WHERE clause context */ + Index *pIdx, /* Index that is being tested */ + int iTabCur /* Cursor for the table being indexed */ +){ + int i, rc; + struct CoveringIndexCheck ck; + Walker w; + if( pWInfo->pSelect==0 ){ + /* We don't have access to the full query, so we cannot check to see + ** if pIdx is covering. Assume it is not. */ + return 0; + } + if( pIdx->bHasExpr==0 ){ + for(i=0; inColumn; i++){ + if( pIdx->aiColumn[i]>=BMS-1 ) break; + } + if( i>=pIdx->nColumn ){ + /* pIdx does not index any columns greater than 62, but we know from + ** colMask that columns greater than 62 are used, so this is not a + ** covering index */ + return 0; + } + } + ck.pIdx = pIdx; + ck.iTabCur = iTabCur; + ck.bExpr = 0; + ck.bUnidx = 0; + memset(&w, 0, sizeof(w)); + w.xExprCallback = whereIsCoveringIndexWalkCallback; + w.xSelectCallback = wx_sqlite3SelectWalkNoop; + w.u.pCovIdxCk = &ck; + wx_sqlite3WalkSelect(&w, pWInfo->pSelect); + if( ck.bUnidx ){ + rc = 0; + }else if( ck.bExpr ){ + rc = WHERE_EXPRIDX; + }else{ + rc = WHERE_IDX_ONLY; + } + return rc; +} + /* ** Add all WhereLoop objects for a single table of the join where the table ** is identified by pBuilder->pNew->iTab. That table is guaranteed to be @@ -151317,7 +160567,6 @@ static int whereLoopAddBtree( int iSortIdx = 1; /* Index number */ int b; /* A boolean value */ LogEst rSize; /* number of rows in the table */ - LogEst rLogSize; /* Logarithm of the number of rows in the table */ WhereClause *pWC; /* The parsed WHERE clause */ Table *pTab; /* Table being queried */ @@ -151330,6 +160579,7 @@ static int whereLoopAddBtree( assert( !IsVirtual(pSrc->pTab) ); if( pSrc->fg.isIndexedBy ){ + assert( pSrc->fg.isCte==0 ); /* An INDEXED BY clause specifies a particular index to use */ pProbe = pSrc->u2.pIBIndex; }else if( !HasRowid(pTab) ){ @@ -151347,7 +160597,7 @@ static int whereLoopAddBtree( sPk.aiRowLogEst = aiRowEstPk; sPk.onError = OE_Replace; sPk.pTable = pTab; - sPk.szIdxRow = pTab->szTabRow; + sPk.szIdxRow = 3; /* TUNING: Interior rows of IPK table are very small */ sPk.idxType = SQLITE_IDXTYPE_IPK; aiRowEstPk[0] = pTab->nRowLogEst; aiRowEstPk[1] = 0; @@ -151360,22 +160610,24 @@ static int whereLoopAddBtree( pProbe = &sPk; } rSize = pTab->nRowLogEst; - rLogSize = estLog(rSize); #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* Automatic indexes */ if( !pBuilder->pOrSet /* Not part of an OR optimization */ - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 + && (pWInfo->wctrlFlags & (WHERE_RIGHT_JOIN|WHERE_OR_SUBCLAUSE))==0 && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 && !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */ && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */ && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */ && !pSrc->fg.isCorrelated /* Not a correlated subquery */ && !pSrc->fg.isRecursive /* Not a recursive common table expression. */ + && (pSrc->fg.jointype & JT_RIGHT)==0 /* Not the right tab of a RIGHT JOIN */ ){ /* Generate auto-index WhereLoops */ + LogEst rLogSize; /* Logarithm of the number of rows in the table */ WhereTerm *pTerm; WhereTerm *pWCEnd = pWC->a + pWC->nTerm; + rLogSize = estLog(rSize); for(pTerm=pWC->a; rc==SQLITE_OK && pTermprereqRight & pNew->maskSelf ) continue; if( termCanDriveIndex(pTerm, pSrc, 0) ){ @@ -151393,10 +160645,11 @@ static int whereLoopAddBtree( ** those objects, since there is no opportunity to add schema ** indexes on subqueries and views. */ pNew->rSetup = rLogSize + rSize; - if( pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0 ){ + if( !IsView(pTab) && (pTab->tabFlags & TF_Ephemeral)==0 ){ pNew->rSetup += 28; }else{ - pNew->rSetup -= 10; + pNew->rSetup -= 25; /* Greatly reduced setup cost for auto indexes + ** on ephemeral materializations of views */ } ApplyCostMultiplier(pNew->rSetup, pTab->costMult); if( pNew->rSetup<0 ) pNew->rSetup = 0; @@ -151419,9 +160672,8 @@ static int whereLoopAddBtree( for(; rc==SQLITE_OK && pProbe; pProbe=(pSrc->fg.isIndexedBy ? 0 : pProbe->pNext), iSortIdx++ ){ - int isLeft = (pSrc->fg.jointype & JT_OUTER)!=0; if( pProbe->pPartIdxWhere!=0 - && !whereUsablePartialIndex(pSrc->iCursor, isLeft, pWC, + && !whereUsablePartialIndex(pSrc->iCursor, pSrc->fg.jointype, pWC, pProbe->pPartIdxWhere) ){ testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */ @@ -151466,6 +160718,9 @@ static int whereLoopAddBtree( #else pNew->rRun = rSize + 16; #endif + if( IsView(pTab) || (pTab->tabFlags & TF_Ephemeral)!=0 ){ + pNew->wsFlags |= WHERE_VIEWSCAN; + } ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); rc = whereLoopInsert(pBuilder, pNew); @@ -151474,11 +160729,38 @@ static int whereLoopAddBtree( }else{ Bitmask m; if( pProbe->isCovering ){ - pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; m = 0; + pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; }else{ m = pSrc->colUsed & pProbe->colNotIdxed; - pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED; + pNew->wsFlags = WHERE_INDEXED; + if( m==TOPBIT || (pProbe->bHasExpr && !pProbe->bHasVCol && m!=0) ){ + u32 isCov = whereIsCoveringIndex(pWInfo, pProbe, pSrc->iCursor); + if( isCov==0 ){ + WHERETRACE(0x200, + ("-> %s is not a covering index" + " according to whereIsCoveringIndex()\n", pProbe->zName)); + assert( m!=0 ); + }else{ + m = 0; + pNew->wsFlags |= isCov; + if( isCov & WHERE_IDX_ONLY ){ + WHERETRACE(0x200, + ("-> %s is a covering expression index" + " according to whereIsCoveringIndex()\n", pProbe->zName)); + }else{ + assert( isCov==WHERE_EXPRIDX ); + WHERETRACE(0x200, + ("-> %s might be a covering expression index" + " according to whereIsCoveringIndex()\n", pProbe->zName)); + } + } + }else if( m==0 ){ + WHERETRACE(0x200, + ("-> %s a covering index according to bitmasks\n", + pProbe->zName, m==0 ? "is" : "is not")); + pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; + } } /* Full scan via index */ @@ -151529,7 +160811,14 @@ static int whereLoopAddBtree( } ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); - rc = whereLoopInsert(pBuilder, pNew); + if( (pSrc->fg.jointype & JT_RIGHT)!=0 && pProbe->aColExpr ){ + /* Do not do an SCAN of a index-on-expression in a RIGHT JOIN + ** because the cursor used to access the index might not be + ** positioned to the correct row during the right-join no-match + ** loop. */ + }else{ + rc = whereLoopInsert(pBuilder, pNew); + } pNew->nOut = rSize; if( rc ) break; } @@ -151555,6 +160844,15 @@ static int whereLoopAddBtree( #ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Return true if pTerm is a virtual table LIMIT or OFFSET term. +*/ +static int isLimitTerm(WhereTerm *pTerm){ + assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 ); + return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT + && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; +} + /* ** Argument pIdxInfo is already populated with all constraints that may ** be used by the virtual table identified by pBuilder->pNew->iTab. This @@ -151582,9 +160880,11 @@ static int whereLoopAddVirtualOne( u16 mExclude, /* Exclude terms using these operators */ wx_sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */ u16 mNoOmit, /* Do not omit these constraints */ - int *pbIn /* OUT: True if plan uses an IN(...) op */ + int *pbIn, /* OUT: True if plan uses an IN(...) op */ + int *pbRetryLimit /* OUT: Retry without LIMIT/OFFSET */ ){ WhereClause *pWC = pBuilder->pWC; + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; struct wx_sqlite3_index_constraint *pIdxCons; struct wx_sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage; int i; @@ -151607,6 +160907,7 @@ static int whereLoopAddVirtualOne( pIdxCons->usable = 0; if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight && (pTerm->eOperator & mExclude)==0 + && (pbRetryLimit || !isLimitTerm(pTerm)) ){ pIdxCons->usable = 1; } @@ -151622,6 +160923,7 @@ static int whereLoopAddVirtualOne( pIdxInfo->estimatedRows = 25; pIdxInfo->idxFlags = 0; pIdxInfo->colUsed = (wx_sqlite3_int64)pSrc->colUsed; + pHidden->mHandleIn = 0; /* Invoke the virtual table xBestIndex() method */ rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo); @@ -151631,7 +160933,7 @@ static int whereLoopAddVirtualOne( ** that the particular combination of parameters provided is unusable. ** Make no entries in the loop table. */ - WHERETRACE(0xffff, (" ^^^^--- non-viable plan rejected!\n")); + WHERETRACE(0xffffffff, (" ^^^^--- non-viable plan rejected!\n")); return SQLITE_OK; } return rc; @@ -151639,8 +160941,8 @@ static int whereLoopAddVirtualOne( mxTerm = -1; assert( pNew->nLSlot>=nConstraint ); - for(i=0; iaLTerm[i] = 0; - pNew->u.vtab.omitMask = 0; + memset(pNew->aLTerm, 0, sizeof(pNew->aLTerm[0])*nConstraint ); + memset(&pNew->u.vtab, 0, sizeof(pNew->u.vtab)); pIdxCons = *(struct wx_sqlite3_index_constraint**)&pIdxInfo->aConstraint; for(i=0; ieMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET ){ + pNew->u.vtab.bOmitOffset = 1; + } } - if( (pTerm->eOperator & WO_IN)!=0 ){ + if( SMASKBIT32(i) & pHidden->mHandleIn ){ + pNew->u.vtab.mHandleIn |= MASKBIT32(iTerm); + }else if( (pTerm->eOperator & WO_IN)!=0 ){ /* A virtual table that is constrained by an IN clause may not ** consume the ORDER BY clause because (1) the order of IN terms ** is not necessarily related to the order of output terms and @@ -151685,6 +160992,22 @@ static int whereLoopAddVirtualOne( pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } + + assert( pbRetryLimit || !isLimitTerm(pTerm) ); + if( isLimitTerm(pTerm) && *pbIn ){ + /* If there is an IN(...) term handled as an == (separate call to + ** xFilter for each value on the RHS of the IN) and a LIMIT or + ** OFFSET term handled as well, the plan is unusable. Set output + ** variable *pbRetryLimit to true to tell the caller to retry with + ** LIMIT and OFFSET disabled. */ + if( pIdxInfo->needToFreeIdxStr ){ + wx_sqlite3_free(pIdxInfo->idxStr); + pIdxInfo->idxStr = 0; + pIdxInfo->needToFreeIdxStr = 0; + } + *pbRetryLimit = 1; + return SQLITE_OK; + } } } @@ -151721,7 +161044,7 @@ static int whereLoopAddVirtualOne( wx_sqlite3_free(pNew->u.vtab.idxStr); pNew->u.vtab.needFree = 0; } - WHERETRACE(0xffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n", + WHERETRACE(0xffffffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n", *pbIn, (wx_sqlite3_uint64)mPrereq, (wx_sqlite3_uint64)(pNew->prereq & ~mPrereq))); @@ -151729,11 +161052,19 @@ static int whereLoopAddVirtualOne( } /* -** If this function is invoked from within an xBestIndex() callback, it -** returns a pointer to a buffer containing the name of the collation -** sequence associated with element iCons of the wx_sqlite3_index_info.aConstraint -** array. Or, if iCons is out of range or there is no active xBestIndex -** call, return NULL. +** Return the collating sequence for a constraint passed into xBestIndex. +** +** pIdxInfo must be an wx_sqlite3_index_info structure passed into xBestIndex. +** This routine depends on there being a HiddenIndexInfo structure immediately +** following the wx_sqlite3_index_info structure. +** +** Return a pointer to the collation name: +** +** 1. If there is an explicit COLLATE operator on the constaint, return it. +** +** 2. Else, if the column has an alternative collation, return that. +** +** 3. Otherwise, return "BINARY". */ SQLITE_API const char *wx_sqlite3_vtab_collation(wx_sqlite3_index_info *pIdxInfo, int iCons){ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; @@ -151750,6 +161081,97 @@ SQLITE_API const char *wx_sqlite3_vtab_collation(wx_sqlite3_index_info *pIdxInfo return zRet; } +/* +** Return true if constraint iCons is really an IN(...) constraint, or +** false otherwise. If iCons is an IN(...) constraint, set (if bHandle!=0) +** or clear (if bHandle==0) the flag to handle it using an iterator. +*/ +SQLITE_API int wx_sqlite3_vtab_in(wx_sqlite3_index_info *pIdxInfo, int iCons, int bHandle){ + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + u32 m = SMASKBIT32(iCons); + if( m & pHidden->mIn ){ + if( bHandle==0 ){ + pHidden->mHandleIn &= ~m; + }else if( bHandle>0 ){ + pHidden->mHandleIn |= m; + } + return 1; + } + return 0; +} + +/* +** This interface is callable from within the xBestIndex callback only. +** +** If possible, set (*ppVal) to point to an object containing the value +** on the right-hand-side of constraint iCons. +*/ +SQLITE_API int wx_sqlite3_vtab_rhs_value( + wx_sqlite3_index_info *pIdxInfo, /* Copy of first argument to xBestIndex */ + int iCons, /* Constraint for which RHS is wanted */ + wx_sqlite3_value **ppVal /* Write value extracted here */ +){ + HiddenIndexInfo *pH = (HiddenIndexInfo*)&pIdxInfo[1]; + wx_sqlite3_value *pVal = 0; + int rc = SQLITE_OK; + if( iCons<0 || iCons>=pIdxInfo->nConstraint ){ + rc = SQLITE_MISUSE; /* EV: R-30545-25046 */ + }else{ + if( pH->aRhs[iCons]==0 ){ + WhereTerm *pTerm = &pH->pWC->a[pIdxInfo->aConstraint[iCons].iTermOffset]; + rc = wx_sqlite3ValueFromExpr( + pH->pParse->db, pTerm->pExpr->pRight, ENC(pH->pParse->db), + SQLITE_AFF_BLOB, &pH->aRhs[iCons] + ); + testcase( rc!=SQLITE_OK ); + } + pVal = pH->aRhs[iCons]; + } + *ppVal = pVal; + + if( rc==SQLITE_OK && pVal==0 ){ /* IMP: R-19933-32160 */ + rc = SQLITE_NOTFOUND; /* IMP: R-36424-56542 */ + } + + return rc; +} + +/* +** Return true if ORDER BY clause may be handled as DISTINCT. +*/ +SQLITE_API int wx_sqlite3_vtab_distinct(wx_sqlite3_index_info *pIdxInfo){ + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + assert( pHidden->eDistinct>=0 && pHidden->eDistinct<=3 ); + return pHidden->eDistinct; +} + +#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \ + && !defined(SQLITE_OMIT_VIRTUALTABLE) +/* +** Cause the prepared statement that is associated with a call to +** xBestIndex to potentially use all schemas. If the statement being +** prepared is read-only, then just start read transactions on all +** schemas. But if this is a write operation, start writes on all +** schemas. +** +** This is used by the (built-in) sqlite_dbpage virtual table. +*/ +SQLITE_PRIVATE void wx_sqlite3VtabUsesAllSchemas(wx_sqlite3_index_info *pIdxInfo){ + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + Parse *pParse = pHidden->pParse; + int nDb = pParse->db->nDb; + int i; + for(i=0; iwriteMask) ){ + for(i=0; ipNew->iTab. That table is guaranteed to be a virtual table. @@ -151791,6 +161213,7 @@ static int whereLoopAddVirtual( WhereLoop *pNew; Bitmask mBest; /* Tables used by best possible plan */ u16 mNoOmit; + int bRetry = 0; /* True to retry with LIMIT/OFFSET disabled */ assert( (mPrereq & mUnusable)==0 ); pWInfo = pBuilder->pWInfo; @@ -151799,8 +161222,7 @@ static int whereLoopAddVirtual( pNew = pBuilder->pNew; pSrc = &pWInfo->pTabList->a[pNew->iTab]; assert( IsVirtual(pSrc->pTab) ); - p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy, - &mNoOmit); + p = allocateIndexInfo(pWInfo, pWC, mUnusable, pSrc, &mNoOmit); if( p==0 ) return SQLITE_NOMEM_BKPT; pNew->rSetup = 0; pNew->wsFlags = WHERE_VIRTUALTABLE; @@ -151808,14 +161230,22 @@ static int whereLoopAddVirtual( pNew->u.vtab.needFree = 0; nConstraint = p->nConstraint; if( whereLoopResize(pParse->db, pNew, nConstraint) ){ - wx_sqlite3DbFree(pParse->db, p); + freeIndexInfo(pParse->db, p); return SQLITE_NOMEM_BKPT; } /* First call xBestIndex() with all constraints usable. */ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); - WHERETRACE(0x40, (" VirtualOne: all usable\n")); - rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); + WHERETRACE(0x800, (" VirtualOne: all usable\n")); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry + ); + if( bRetry ){ + assert( rc==SQLITE_OK ); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, 0 + ); + } /* If the call to xBestIndex() with all terms enabled produced a plan ** that does not require any source tables (IOW: a plan with mBest==0) @@ -151831,9 +161261,9 @@ static int whereLoopAddVirtual( /* If the plan produced by the earlier call uses an IN(...) term, call ** xBestIndex again, this time with IN(...) terms disabled. */ if( bIn ){ - WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n")); + WHERETRACE(0x800, (" VirtualOne: all usable w/o IN\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn); + pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn, 0); assert( bIn==0 ); mBestNoIn = pNew->prereq & ~mPrereq; if( mBestNoIn==0 ){ @@ -151857,10 +161287,10 @@ static int whereLoopAddVirtual( mPrev = mNext; if( mNext==ALLBITS ) break; if( mNext==mBest || mNext==mBestNoIn ) continue; - WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n", + WHERETRACE(0x800, (" VirtualOne: mPrev=%04llx mNext=%04llx\n", (wx_sqlite3_uint64)mPrev, (wx_sqlite3_uint64)mNext)); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn); + pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn, 0); if( pNew->prereq==mPrereq ){ seenZero = 1; if( bIn==0 ) seenZeroNoIN = 1; @@ -151871,9 +161301,9 @@ static int whereLoopAddVirtual( ** that requires no source tables at all (i.e. one guaranteed to be ** usable), make a call here with all source tables disabled */ if( rc==SQLITE_OK && seenZero==0 ){ - WHERETRACE(0x40, (" VirtualOne: all disabled\n")); + WHERETRACE(0x800, (" VirtualOne: all disabled\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn); + pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn, 0); if( bIn==0 ) seenZeroNoIN = 1; } @@ -151881,14 +161311,14 @@ static int whereLoopAddVirtual( ** that requires no source tables at all and does not use an IN(...) ** operator, make a final call to obtain one here. */ if( rc==SQLITE_OK && seenZeroNoIN==0 ){ - WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); + WHERETRACE(0x800, (" VirtualOne: all disabled and w/o IN\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn); + pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn, 0); } } if( p->needToFreeIdxStr ) wx_sqlite3_free(p->idxStr); - wx_sqlite3DbFreeNN(pParse->db, p); + freeIndexInfo(pParse->db, p); WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc)); return rc; } @@ -151921,6 +161351,9 @@ static int whereLoopAddOr( pItem = pWInfo->pTabList->a + pNew->iTab; iCur = pItem->iCursor; + /* The multi-index OR optimization does not work for RIGHT and FULL JOIN */ + if( pItem->fg.jointype & JT_RIGHT ) return SQLITE_OK; + for(pTerm=pWC->a; pTermeOperator & WO_OR)!=0 && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0 @@ -151932,10 +161365,9 @@ static int whereLoopAddOr( int i, j; sSubBuild = *pBuilder; - sSubBuild.pOrderBy = 0; sSubBuild.pOrSet = &sCur; - WHERETRACE(0x200, ("Begin processing OR-clause %p\n", pTerm)); + WHERETRACE(0x400, ("Begin processing OR-clause %p\n", pTerm)); for(pOrTerm=pOrWC->a; pOrTermeOperator & WO_AND)!=0 ){ sSubBuild.pWC = &pOrTerm->u.pAndInfo->wc; @@ -151944,6 +161376,7 @@ static int whereLoopAddOr( tempWC.pOuter = pWC; tempWC.op = TK_AND; tempWC.nTerm = 1; + tempWC.nBase = 1; tempWC.a = pOrTerm; sSubBuild.pWC = &tempWC; }else{ @@ -151951,9 +161384,9 @@ static int whereLoopAddOr( } sCur.n = 0; #ifdef WHERETRACE_ENABLED - WHERETRACE(0x200, ("OR-term %d of %p has %d subterms:\n", + WHERETRACE(0x400, ("OR-term %d of %p has %d subterms:\n", (int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm)); - if( wx_sqlite3WhereTrace & 0x400 ){ + if( wx_sqlite3WhereTrace & 0x20000 ){ wx_sqlite3WhereClausePrint(sSubBuild.pWC); } #endif @@ -151968,7 +161401,7 @@ static int whereLoopAddOr( if( rc==SQLITE_OK ){ rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable); } - assert( rc==SQLITE_OK || rc==SQLITE_DONE || sCur.n==0 ); + testcase( rc==SQLITE_NOMEM && sCur.n>0 ); testcase( rc==SQLITE_DONE ); if( sCur.n==0 ){ sSum.n = 0; @@ -152013,7 +161446,7 @@ static int whereLoopAddOr( pNew->prereq = sSum.a[i].prereq; rc = whereLoopInsert(pBuilder, pNew); } - WHERETRACE(0x200, ("End processing OR-clause %p\n", pTerm)); + WHERETRACE(0x400, ("End processing OR-clause %p\n", pTerm)); } } return rc; @@ -152032,29 +161465,50 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ SrcItem *pEnd = &pTabList->a[pWInfo->nLevel]; wx_sqlite3 *db = pWInfo->pParse->db; int rc = SQLITE_OK; + int bFirstPastRJ = 0; + int hasRightJoin = 0; WhereLoop *pNew; + /* Loop over the tables in the join, from left to right */ pNew = pBuilder->pNew; - whereLoopInit(pNew); + + /* Verify that pNew has already been initialized */ + assert( pNew->nLTerm==0 ); + assert( pNew->wsFlags==0 ); + assert( pNew->nLSlot>=ArraySize(pNew->aLTermSpace) ); + assert( pNew->aLTerm!=0 ); + pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT; for(iTab=0, pItem=pTabList->a; pItemiTab = iTab; pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; pNew->maskSelf = wx_sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); - if( (pItem->fg.jointype & (JT_LEFT|JT_CROSS))!=0 ){ - /* This condition is true when pItem is the FROM clause term on the - ** right-hand-side of a LEFT or CROSS JOIN. */ - mPrereq = mPrior; - }else{ + if( bFirstPastRJ + || (pItem->fg.jointype & (JT_OUTER|JT_CROSS|JT_LTORJ))!=0 + ){ + /* Add prerequisites to prevent reordering of FROM clause terms + ** across CROSS joins and outer joins. The bFirstPastRJ boolean + ** prevents the right operand of a RIGHT JOIN from being swapped with + ** other elements even further to the right. + ** + ** The JT_LTORJ case and the hasRightJoin flag work together to + ** prevent FROM-clause terms from moving from the right side of + ** a LEFT JOIN over to the left side of that join if the LEFT JOIN + ** is itself on the left side of a RIGHT JOIN. + */ + if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1; + mPrereq |= mPrior; + bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; + }else if( !hasRightJoin ){ mPrereq = 0; } #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pItem->pTab) ){ SrcItem *p; for(p=&pItem[1]; pfg.jointype & (JT_LEFT|JT_CROSS)) ){ + if( mUnusable || (p->fg.jointype & (JT_OUTER|JT_CROSS)) ){ mUnusable |= wx_sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); } } @@ -152179,7 +161633,9 @@ static i8 wherePathSatisfiesOrderBy( pLoop = pLast; } if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ - if( pLoop->u.vtab.isOrdered && (wctrlFlags & WHERE_DISTINCTBY)==0 ){ + if( pLoop->u.vtab.isOrdered + && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY) + ){ obSat = obDone; } break; @@ -152197,7 +161653,7 @@ static i8 wherePathSatisfiesOrderBy( if( MASKBIT(i) & obSat ) continue; pOBExpr = wx_sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); if( NEVER(pOBExpr==0) ) continue; - if( pOBExpr->op!=TK_COLUMN ) continue; + if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) continue; if( pOBExpr->iTable!=iCur ) continue; pTerm = wx_sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, ~ready, eqOpMask, 0); @@ -152237,6 +161693,10 @@ static i8 wherePathSatisfiesOrderBy( assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) ); assert( pIndex->aiColumn[nColumn-1]==XN_ROWID || !HasRowid(pIndex->pTable)); + /* All relevant terms of the index must also be non-NULL in order + ** for isOrderDistinct to be true. So the isOrderDistint value + ** computed here might be a false positive. Corrections will be + ** made at tag-20210426-1 below */ isOrderDistinct = IsUniqueIndex(pIndex) && (pLoop->wsFlags & WHERE_SKIPSCAN)==0; } @@ -152304,14 +161764,18 @@ static i8 wherePathSatisfiesOrderBy( } /* An unconstrained column that might be NULL means that this - ** WhereLoop is not well-ordered + ** WhereLoop is not well-ordered. tag-20210426-1 */ - if( isOrderDistinct - && iColumn>=0 - && j>=pLoop->u.btree.nEq - && pIndex->pTable->aCol[iColumn].notNull==0 - ){ - isOrderDistinct = 0; + if( isOrderDistinct ){ + if( iColumn>=0 + && j>=pLoop->u.btree.nEq + && pIndex->pTable->aCol[iColumn].notNull==0 + ){ + isOrderDistinct = 0; + } + if( iColumn==XN_EXPR ){ + isOrderDistinct = 0; + } } /* Find the ORDER BY term that corresponds to the j-th column @@ -152326,12 +161790,12 @@ static i8 wherePathSatisfiesOrderBy( if( NEVER(pOBExpr==0) ) continue; if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; if( iColumn>=XN_ROWID ){ - if( pOBExpr->op!=TK_COLUMN ) continue; + if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) continue; if( pOBExpr->iTable!=iCur ) continue; if( pOBExpr->iColumn!=iColumn ) continue; }else{ - Expr *pIdxExpr = pIndex->aColExpr->a[j].pExpr; - if( wx_sqlite3ExprCompareSkip(pOBExpr, pIdxExpr, iCur) ){ + Expr *pIxExpr = pIndex->aColExpr->a[j].pExpr; + if( wx_sqlite3ExprCompareSkip(pOBExpr, pIxExpr, iCur) ){ continue; } } @@ -152349,16 +161813,18 @@ static i8 wherePathSatisfiesOrderBy( /* Make sure the sort order is compatible in an ORDER BY clause. ** Sort order is irrelevant for a GROUP BY clause. */ if( revSet ){ - if( (rev ^ revIdx)!=(pOrderBy->a[i].sortFlags&KEYINFO_ORDER_DESC) ){ + if( (rev ^ revIdx) + != (pOrderBy->a[i].fg.sortFlags&KEYINFO_ORDER_DESC) + ){ isMatch = 0; } }else{ - rev = revIdx ^ (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC); + rev = revIdx ^ (pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC); if( rev ) *pRevMask |= MASKBIT(iLoop); revSet = 1; } } - if( isMatch && (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL) ){ + if( isMatch && (pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL) ){ if( j==pLoop->u.btree.nEq ){ pLoop->wsFlags |= WHERE_BIGNULL_SORT; }else{ @@ -152405,7 +161871,7 @@ static i8 wherePathSatisfiesOrderBy( if( obSat==obDone ) return (i8)nOrderBy; if( !isOrderDistinct ){ for(i=nOrderBy-1; i>0; i--){ - Bitmask m = MASKBIT(i) - 1; + Bitmask m = ALWAYS(iwctrlFlags & WHERE_GROUPBY ); + assert( pWInfo->wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY) ); assert( pWInfo->wctrlFlags & WHERE_SORTBYGROUP ); return pWInfo->sorted; } @@ -152461,41 +161927,60 @@ static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){ ** order. */ static LogEst whereSortingCost( - WhereInfo *pWInfo, - LogEst nRow, - int nOrderBy, - int nSorted + WhereInfo *pWInfo, /* Query planning context */ + LogEst nRow, /* Estimated number of rows to sort */ + int nOrderBy, /* Number of ORDER BY clause terms */ + int nSorted /* Number of initial ORDER BY terms naturally in order */ ){ - /* TUNING: Estimated cost of a full external sort, where N is + /* Estimated cost of a full external sort, where N is ** the number of rows to sort is: ** - ** cost = (3.0 * N * log(N)). + ** cost = (K * N * log(N)). ** ** Or, if the order-by clause has X terms but only the last Y ** terms are out of order, then block-sorting will reduce the ** sorting cost to: ** - ** cost = (3.0 * N * log(N)) * (Y/X) + ** cost = (K * N * log(N)) * (Y/X) ** - ** The (Y/X) term is implemented using stack variable rScale - ** below. + ** The constant K is at least 2.0 but will be larger if there are a + ** large number of columns to be sorted, as the sorting time is + ** proportional to the amount of content to be sorted. The algorithm + ** does not currently distinguish between fat columns (BLOBs and TEXTs) + ** and skinny columns (INTs). It just uses the number of columns as + ** an approximation for the row width. + ** + ** And extra factor of 2.0 or 3.0 is added to the sorting cost if the sort + ** is built using OP_IdxInsert and OP_Sort rather than with OP_SorterInsert. */ - LogEst rScale, rSortCost; - assert( nOrderBy>0 && 66==wx_sqlite3LogEst(100) ); - rScale = wx_sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; - rSortCost = nRow + rScale + 16; + LogEst rSortCost, nCol; + assert( pWInfo->pSelect!=0 ); + assert( pWInfo->pSelect->pEList!=0 ); + /* TUNING: sorting cost proportional to the number of output columns: */ + nCol = wx_sqlite3LogEst((pWInfo->pSelect->pEList->nExpr+59)/30); + rSortCost = nRow + nCol; + if( nSorted>0 ){ + /* Scale the result by (Y/X) */ + rSortCost += wx_sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; + } /* Multiple by log(M) where M is the number of output rows. ** Use the LIMIT for M if it is smaller. Or if this sort is for ** a DISTINCT operator, M will be the number of distinct output ** rows, so fudge it downwards a bit. */ - if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimitiLimit; + if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 ){ + rSortCost += 10; /* TUNING: Extra 2.0x if using LIMIT */ + if( nSorted!=0 ){ + rSortCost += 6; /* TUNING: Extra 1.5x if also using partial sort */ + } + if( pWInfo->iLimitiLimit; + } }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){ /* TUNING: In the sort for a DISTINCT operator, assume that the DISTINCT ** reduces the number of output rows by a factor of 2 */ - if( nRow>10 ) nRow -= 10; assert( 10==wx_sqlite3LogEst(2) ); + if( nRow>10 ){ nRow -= 10; assert( 10==wx_sqlite3LogEst(2) ); } } rSortCost += estLog(nRow); return rSortCost; @@ -152517,7 +162002,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ int mxChoice; /* Maximum number of simultaneous paths tracked */ int nLoop; /* Number of terms in the join */ Parse *pParse; /* Parsing context */ - wx_sqlite3 *db; /* The database connection */ int iLoop; /* Loop counter over the terms of the join */ int ii, jj; /* Loop counters */ int mxI = 0; /* Index of next entry to replace */ @@ -152536,7 +162020,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ int nSpace; /* Bytes of space allocated at pSpace */ pParse = pWInfo->pParse; - db = pParse->db; nLoop = pWInfo->nLevel; /* TUNING: For simple queries, only the best path is tracked. ** For 2-way joins, the 5 best paths are followed. @@ -152559,7 +162042,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ /* Allocate and initialize space for aTo, aFrom and aSortCost[] */ nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2; nSpace += sizeof(LogEst) * nOrderBy; - pSpace = wx_sqlite3DbMallocRawNN(db, nSpace); + pSpace = wx_sqlite3StackAllocRawNN(pParse->db, nSpace); if( pSpace==0 ) return SQLITE_NOMEM_BKPT; aTo = (WherePath*)pSpace; aFrom = aTo+mxChoice; @@ -152609,9 +162092,9 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ LogEst nOut; /* Rows visited by (pFrom+pWLoop) */ LogEst rCost; /* Cost of path (pFrom+pWLoop) */ LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */ - i8 isOrdered = pFrom->isOrdered; /* isOrdered for (pFrom+pWLoop) */ + i8 isOrdered; /* isOrdered for (pFrom+pWLoop) */ Bitmask maskNew; /* Mask of src visited by (..) */ - Bitmask revMask = 0; /* Mask of rev-order loops for (..) */ + Bitmask revMask; /* Mask of rev-order loops for (..) */ if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue; if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue; @@ -152630,7 +162113,9 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ rUnsorted = wx_sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted); nOut = pFrom->nRow + pWLoop->nOut; maskNew = pFrom->maskLoop | pWLoop->maskSelf; + isOrdered = pFrom->isOrdered; if( isOrdered<0 ){ + revMask = 0; isOrdered = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags, iLoop, pWLoop, &revMask); @@ -152643,11 +162128,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ pWInfo, nRowEst, nOrderBy, isOrdered ); } - /* TUNING: Add a small extra penalty (5) to sorting as an + /* TUNING: Add a small extra penalty (3) to sorting as an ** extra encouragment to the query planner to select a plan ** where the rows emerge in the correct order without any sorting ** required. */ - rCost = wx_sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 5; + rCost = wx_sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 3; WHERETRACE(0x002, ("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n", @@ -152658,6 +162143,13 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */ } + /* TUNING: A full-scan of a VIEW or subquery in the outer loop + ** is not so bad. */ + if( iLoop==0 && (pWLoop->wsFlags & WHERE_VIEWSCAN)!=0 ){ + rCost += -10; + nOut += -30; + } + /* Check to see if pWLoop should be added to the set of ** mxChoice best-so-far paths. ** @@ -152808,7 +162300,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( nFrom==0 ){ wx_sqlite3ErrorMsg(pParse, "no query solution"); - wx_sqlite3DbFreeNN(db, pSpace); + wx_sqlite3StackFreeNN(pParse->db, pSpace); return SQLITE_ERROR; } @@ -152839,12 +162331,16 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ } pWInfo->bOrderedInnerLoop = 0; if( pWInfo->pOrderBy ){ + pWInfo->nOBSat = pFrom->isOrdered; if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){ if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } + if( pWInfo->pSelect->pOrderBy + && pWInfo->nOBSat > pWInfo->pSelect->pOrderBy->nExpr ){ + pWInfo->nOBSat = pWInfo->pSelect->pOrderBy->nExpr; + } }else{ - pWInfo->nOBSat = pFrom->isOrdered; pWInfo->revMask = pFrom->revLoop; if( pWInfo->nOBSat<=0 ){ pWInfo->nOBSat = 0; @@ -152890,7 +162386,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ pWInfo->nRowOut = pFrom->nRow; /* Free temporary memory and return success */ - wx_sqlite3DbFreeNN(db, pSpace); + wx_sqlite3StackFreeNN(pParse->db, pSpace); return SQLITE_OK; } @@ -152915,6 +162411,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ int j; Table *pTab; Index *pIdx; + WhereScan scan; pWInfo = pBuilder->pWInfo; if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0; @@ -152922,13 +162419,18 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ pItem = pWInfo->pTabList->a; pTab = pItem->pTab; if( IsVirtual(pTab) ) return 0; - if( pItem->fg.isIndexedBy ) return 0; + if( pItem->fg.isIndexedBy || pItem->fg.notIndexed ){ + testcase( pItem->fg.isIndexedBy ); + testcase( pItem->fg.notIndexed ); + return 0; + } iCur = pItem->iCursor; pWC = &pWInfo->sWC; pLoop = pBuilder->pNew; pLoop->wsFlags = 0; pLoop->nSkip = 0; - pTerm = wx_sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0); + pTerm = whereScanInit(&scan, pWC, iCur, -1, WO_EQ|WO_IS, 0); + while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan); if( pTerm ){ testcase( pTerm->eOperator & WO_IS ); pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW; @@ -152947,7 +162449,8 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ ) continue; opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ; for(j=0; jnKeyCol; j++){ - pTerm = wx_sqlite3WhereFindTerm(pWC, iCur, j, 0, opMask, pIdx); + pTerm = whereScanInit(&scan, pWC, iCur, j, opMask, pIdx); + while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan); if( pTerm==0 ) break; testcase( pTerm->eOperator & WO_IS ); pLoop->aLTerm[j] = pTerm; @@ -152976,8 +162479,14 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } + if( scan.iEquiv>1 ) pLoop->wsFlags |= WHERE_TRANSCONS; #ifdef SQLITE_DEBUG pLoop->cId = '0'; +#endif +#ifdef WHERETRACE_ENABLED + if( wx_sqlite3WhereTrace & 0x02 ){ + wx_sqlite3DebugPrintf("whereShortCut() used to compute solution\n"); + } #endif return 1; } @@ -153032,6 +162541,229 @@ static void showAllWhereLoops(WhereInfo *pWInfo, WhereClause *pWC){ # define WHERETRACE_ALL_LOOPS(W,C) #endif +/* Attempt to omit tables from a join that do not affect the result. +** For a table to not affect the result, the following must be true: +** +** 1) The query must not be an aggregate. +** 2) The table must be the RHS of a LEFT JOIN. +** 3) Either the query must be DISTINCT, or else the ON or USING clause +** must contain a constraint that limits the scan of the table to +** at most a single row. +** 4) The table must not be referenced by any part of the query apart +** from its own USING or ON clause. +** +** For example, given: +** +** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); +** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); +** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); +** +** then table t2 can be omitted from the following: +** +** SELECT v1, v3 FROM t1 +** LEFT JOIN t2 ON (t1.ipk=t2.ipk) +** LEFT JOIN t3 ON (t1.ipk=t3.ipk) +** +** or from: +** +** SELECT DISTINCT v1, v3 FROM t1 +** LEFT JOIN t2 +** LEFT JOIN t3 ON (t1.ipk=t3.ipk) +*/ +static SQLITE_NOINLINE Bitmask whereOmitNoopJoin( + WhereInfo *pWInfo, + Bitmask notReady +){ + int i; + Bitmask tabUsed; + + /* Preconditions checked by the caller */ + assert( pWInfo->nLevel>=2 ); + assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_OmitNoopJoin) ); + + /* These two preconditions checked by the caller combine to guarantee + ** condition (1) of the header comment */ + assert( pWInfo->pResultSet!=0 ); + assert( 0==(pWInfo->wctrlFlags & WHERE_AGG_DISTINCT) ); + + tabUsed = wx_sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pResultSet); + if( pWInfo->pOrderBy ){ + tabUsed |= wx_sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy); + } + for(i=pWInfo->nLevel-1; i>=1; i--){ + WhereTerm *pTerm, *pEnd; + SrcItem *pItem; + WhereLoop *pLoop; + pLoop = pWInfo->a[i].pWLoop; + pItem = &pWInfo->pTabList->a[pLoop->iTab]; + if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue; + if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)==0 + && (pLoop->wsFlags & WHERE_ONEROW)==0 + ){ + continue; + } + if( (tabUsed & pLoop->maskSelf)!=0 ) continue; + pEnd = pWInfo->sWC.a + pWInfo->sWC.nTerm; + for(pTerm=pWInfo->sWC.a; pTermprereqAll & pLoop->maskSelf)!=0 ){ + if( !ExprHasProperty(pTerm->pExpr, EP_OuterON) + || pTerm->pExpr->w.iJoin!=pItem->iCursor + ){ + break; + } + } + } + if( pTerm drop loop %c not used\n", pLoop->cId)); + notReady &= ~pLoop->maskSelf; + for(pTerm=pWInfo->sWC.a; pTermprereqAll & pLoop->maskSelf)!=0 ){ + pTerm->wtFlags |= TERM_CODED; + } + } + if( i!=pWInfo->nLevel-1 ){ + int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); + memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); + } + pWInfo->nLevel--; + assert( pWInfo->nLevel>0 ); + } + return notReady; +} + +/* +** Check to see if there are any SEARCH loops that might benefit from +** using a Bloom filter. Consider a Bloom filter if: +** +** (1) The SEARCH happens more than N times where N is the number +** of rows in the table that is being considered for the Bloom +** filter. +** (2) Some searches are expected to find zero rows. (This is determined +** by the WHERE_SELFCULL flag on the term.) +** (3) Bloom-filter processing is not disabled. (Checked by the +** caller.) +** (4) The size of the table being searched is known by ANALYZE. +** +** This block of code merely checks to see if a Bloom filter would be +** appropriate, and if so sets the WHERE_BLOOMFILTER flag on the +** WhereLoop. The implementation of the Bloom filter comes further +** down where the code for each WhereLoop is generated. +*/ +static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( + const WhereInfo *pWInfo +){ + int i; + LogEst nSearch = 0; + + assert( pWInfo->nLevel>=2 ); + assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) ); + for(i=0; inLevel; i++){ + WhereLoop *pLoop = pWInfo->a[i].pWLoop; + const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ); + SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab]; + Table *pTab = pItem->pTab; + if( (pTab->tabFlags & TF_HasStat1)==0 ) break; + pTab->tabFlags |= TF_StatsUsed; + if( i>=1 + && (pLoop->wsFlags & reqFlags)==reqFlags + /* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */ + && ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0) + ){ + if( nSearch > pTab->nRowLogEst ){ + testcase( pItem->fg.jointype & JT_LEFT ); + pLoop->wsFlags |= WHERE_BLOOMFILTER; + pLoop->wsFlags &= ~WHERE_IDX_ONLY; + WHERETRACE(0xffffffff, ( + "-> use Bloom-filter on loop %c because there are ~%.1e " + "lookups into %s which has only ~%.1e rows\n", + pLoop->cId, (double)wx_sqlite3LogEstToInt(nSearch), pTab->zName, + (double)wx_sqlite3LogEstToInt(pTab->nRowLogEst))); + } + } + nSearch += pLoop->nOut; + } +} + +/* +** This is an wx_sqlite3ParserAddCleanup() callback that is invoked to +** free the Parse->pIdxEpr list when the Parse object is destroyed. +*/ +static void whereIndexedExprCleanup(wx_sqlite3 *db, void *pObject){ + Parse *pParse = (Parse*)pObject; + while( pParse->pIdxEpr!=0 ){ + IndexedExpr *p = pParse->pIdxEpr; + pParse->pIdxEpr = p->pIENext; + wx_sqlite3ExprDelete(db, p->pExpr); + wx_sqlite3DbFreeNN(db, p); + } +} + +/* +** The index pIdx is used by a query and contains one or more expressions. +** In other words pIdx is an index on an expression. iIdxCur is the cursor +** number for the index and iDataCur is the cursor number for the corresponding +** table. +** +** This routine adds IndexedExpr entries to the Parse->pIdxEpr field for +** each of the expressions in the index so that the expression code generator +** will know to replace occurrences of the indexed expression with +** references to the corresponding column of the index. +*/ +static SQLITE_NOINLINE void whereAddIndexedExpr( + Parse *pParse, /* Add IndexedExpr entries to pParse->pIdxEpr */ + Index *pIdx, /* The index-on-expression that contains the expressions */ + int iIdxCur, /* Cursor number for pIdx */ + SrcItem *pTabItem /* The FROM clause entry for the table */ +){ + int i; + IndexedExpr *p; + Table *pTab; + assert( pIdx->bHasExpr ); + pTab = pIdx->pTable; + for(i=0; inColumn; i++){ + Expr *pExpr; + int j = pIdx->aiColumn[i]; + int bMaybeNullRow; + if( j==XN_EXPR ){ + pExpr = pIdx->aColExpr->a[i].pExpr; + testcase( pTabItem->fg.jointype & JT_LEFT ); + testcase( pTabItem->fg.jointype & JT_RIGHT ); + testcase( pTabItem->fg.jointype & JT_LTORJ ); + bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0; + }else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){ + pExpr = wx_sqlite3ColumnExpr(pTab, &pTab->aCol[j]); + bMaybeNullRow = 0; + }else{ + continue; + } + if( wx_sqlite3ExprIsConstant(pExpr) ) continue; + p = wx_sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); + if( p==0 ) break; + p->pIENext = pParse->pIdxEpr; +#ifdef WHERETRACE_ENABLED + if( wx_sqlite3WhereTrace & 0x200 ){ + wx_sqlite3DebugPrintf("New pParse->pIdxEpr term {%d,%d}\n", iIdxCur, i); + if( wx_sqlite3WhereTrace & 0x5000 ) wx_sqlite3ShowExpr(pExpr); + } +#endif + p->pExpr = wx_sqlite3ExprDup(pParse->db, pExpr, 0); + p->iDataCur = pTabItem->iCursor; + p->iIdxCur = iIdxCur; + p->iIdxCol = i; + p->bMaybeNullRow = bMaybeNullRow; + if( wx_sqlite3IndexAffinityStr(pParse->db, pIdx) ){ + p->aff = pIdx->zColAff[i]; + } +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + p->zIdxName = pIdx->zName; +#endif + pParse->pIdxEpr = p; + if( p->pIENext==0 ){ + wx_sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pParse); + } + } +} + /* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains @@ -153126,6 +162858,7 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( Expr *pWhere, /* The WHERE clause */ ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */ + Select *pSelect, /* The entire SELECT statement */ u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */ int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number ** If WHERE_USE_LIMIT, then the limit amount */ @@ -153160,13 +162893,6 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0; - sWLB.pOrderBy = pOrderBy; - - /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via - ** wx_sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ - if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){ - wctrlFlags &= ~WHERE_WANT_DISTINCT; - } /* The number of tables in the FROM clause is limited by the number of ** bits in a Bitmask @@ -153191,7 +162917,7 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( ** field (type Bitmask) it must be aligned on an 8-byte boundary on ** some architectures. Hence the ROUND8() below. */ - nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); + nByteWInfo = ROUND8P(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); pWInfo = wx_sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); if( db->mallocFailed ){ wx_sqlite3DbFree(db, pWInfo); @@ -153201,7 +162927,9 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; pWInfo->pOrderBy = pOrderBy; +#if WHERETRACE_ENABLED pWInfo->pWhere = pWhere; +#endif pWInfo->pResultSet = pResultSet; pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1; pWInfo->nLevel = nTabList; @@ -153209,11 +162937,16 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( pWInfo->wctrlFlags = wctrlFlags; pWInfo->iLimit = iAuxArg; pWInfo->savedNQueryLoop = pParse->nQueryLoop; + pWInfo->pSelect = pSelect; memset(&pWInfo->nOBSat, 0, offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat)); memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel)); assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */ pMaskSet = &pWInfo->sMaskSet; + pMaskSet->n = 0; + pMaskSet->ix[0] = -99; /* Initialize ix[0] to a value that can never be + ** a valid cursor number, to avoid an initial + ** test for pMaskSet->n==0 in wx_sqlite3WhereGetMask() */ sWLB.pWInfo = pWInfo; sWLB.pWC = &pWInfo->sWC; sWLB.pNew = (WhereLoop*)(((char*)pWInfo)+nByteWInfo); @@ -153226,7 +162959,6 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( /* Split the WHERE clause into separate subexpressions where each ** subexpression is separated by an AND operator. */ - initMaskSet(pMaskSet); wx_sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo); wx_sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND); @@ -153234,7 +162966,9 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( */ if( nTabList==0 ){ if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr; - if( wctrlFlags & WHERE_WANT_DISTINCT ){ + if( (wctrlFlags & WHERE_WANT_DISTINCT)!=0 + && OptimizationEnabled(db, SQLITE_DistinctOpt) + ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); @@ -153272,7 +163006,10 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( /* Analyze all of the subexpressions. */ wx_sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC); - if( db->mallocFailed ) goto whereBeginError; + if( pSelect && pSelect->pLimit ){ + wx_sqlite3WhereAddLimit(&pWInfo->sWC, pSelect); + } + if( pParse->nErr ) goto whereBeginError; /* Special case: WHERE terms that do not refer to any tables in the join ** (constant expressions). Evaluate each such term, and jump over all the @@ -153285,7 +163022,7 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( ** FROM ... WHERE random()>0; -- eval random() once per row ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall */ - for(ii=0; iinTerm; ii++){ + for(ii=0; iinBase; ii++){ WhereTerm *pT = &sWLB.pWC->a[ii]; if( pT->wtFlags & TERM_VIRTUAL ) continue; if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){ @@ -153295,7 +163032,12 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( } if( wctrlFlags & WHERE_WANT_DISTINCT ){ - if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){ + if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){ + /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via + ** wx_sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ + wctrlFlags &= ~WHERE_WANT_DISTINCT; + pWInfo->wctrlFlags &= ~WHERE_WANT_DISTINCT; + }else if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){ /* The DISTINCT marking is pointless. Ignore it. */ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; }else if( pOrderBy==0 ){ @@ -153307,13 +163049,13 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( /* Construct the WhereLoop objects */ #if defined(WHERETRACE_ENABLED) - if( wx_sqlite3WhereTrace & 0xffff ){ + if( wx_sqlite3WhereTrace & 0xffffffff ){ wx_sqlite3DebugPrintf("*** Optimizer Start *** (wctrlFlags: 0x%x",wctrlFlags); if( wctrlFlags & WHERE_USE_LIMIT ){ wx_sqlite3DebugPrintf(", limit: %d", iAuxArg); } wx_sqlite3DebugPrintf(")\n"); - if( wx_sqlite3WhereTrace & 0x100 ){ + if( wx_sqlite3WhereTrace & 0x8000 ){ Select sSelect; memset(&sSelect, 0, sizeof(sSelect)); sSelect.selFlags = SF_WhereBegin; @@ -153323,10 +163065,10 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( sSelect.pEList = pResultSet; wx_sqlite3TreeViewSelect(0, &sSelect, 0); } - } - if( wx_sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */ - wx_sqlite3DebugPrintf("---- WHERE clause at start of analysis:\n"); - wx_sqlite3WhereClausePrint(sWLB.pWC); + if( wx_sqlite3WhereTrace & 0x4000 ){ /* Display all WHERE clause terms */ + wx_sqlite3DebugPrintf("---- WHERE clause at start of analysis:\n"); + wx_sqlite3WhereClausePrint(sWLB.pWC); + } } #endif @@ -153342,7 +163084,7 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( ** loops will be built using the revised truthProb values. */ if( sWLB.bldFlags2 & SQLITE_BLDF2_2NDPASS ){ WHERETRACE_ALL_LOOPS(pWInfo, sWLB.pWC); - WHERETRACE(0xffff, + WHERETRACE(0xffffffff, ("**** Redo all loop computations due to" " TERM_HIGHTRUTH changes ****\n")); while( pWInfo->pLoops ){ @@ -153366,9 +163108,10 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){ pWInfo->revMask = ALLBITS; } - if( pParse->nErr || db->mallocFailed ){ + if( pParse->nErr ){ goto whereBeginError; } + assert( db->mallocFailed==0 ); #ifdef WHERETRACE_ENABLED if( wx_sqlite3WhereTrace ){ wx_sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); @@ -153396,89 +163139,42 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( } #endif - /* Attempt to omit tables from the join that do not affect the result. - ** For a table to not affect the result, the following must be true: - ** - ** 1) The query must not be an aggregate. - ** 2) The table must be the RHS of a LEFT JOIN. - ** 3) Either the query must be DISTINCT, or else the ON or USING clause - ** must contain a constraint that limits the scan of the table to - ** at most a single row. - ** 4) The table must not be referenced by any part of the query apart - ** from its own USING or ON clause. - ** - ** For example, given: - ** - ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); - ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); - ** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); - ** - ** then table t2 can be omitted from the following: - ** - ** SELECT v1, v3 FROM t1 - ** LEFT JOIN t2 ON (t1.ipk=t2.ipk) - ** LEFT JOIN t3 ON (t1.ipk=t3.ipk) - ** - ** or from: + /* Attempt to omit tables from a join that do not affect the result. + ** See the comment on whereOmitNoopJoin() for further information. ** - ** SELECT DISTINCT v1, v3 FROM t1 - ** LEFT JOIN t2 - ** LEFT JOIN t3 ON (t1.ipk=t3.ipk) + ** This query optimization is factored out into a separate "no-inline" + ** procedure to keep the wx_sqlite3WhereBegin() procedure from becoming + ** too large. If wx_sqlite3WhereBegin() becomes too large, that prevents + ** some C-compiler optimizers from in-lining the + ** wx_sqlite3WhereCodeOneLoopStart() procedure, and it is important to + ** in-line wx_sqlite3WhereCodeOneLoopStart() for performance reasons. */ notReady = ~(Bitmask)0; if( pWInfo->nLevel>=2 - && pResultSet!=0 /* guarantees condition (1) above */ + && pResultSet!=0 /* these two combine to guarantee */ + && 0==(wctrlFlags & WHERE_AGG_DISTINCT) /* condition (1) above */ && OptimizationEnabled(db, SQLITE_OmitNoopJoin) ){ - int i; - Bitmask tabUsed = wx_sqlite3WhereExprListUsage(pMaskSet, pResultSet); - if( sWLB.pOrderBy ){ - tabUsed |= wx_sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy); - } - for(i=pWInfo->nLevel-1; i>=1; i--){ - WhereTerm *pTerm, *pEnd; - SrcItem *pItem; - pLoop = pWInfo->a[i].pWLoop; - pItem = &pWInfo->pTabList->a[pLoop->iTab]; - if( (pItem->fg.jointype & JT_LEFT)==0 ) continue; - if( (wctrlFlags & WHERE_WANT_DISTINCT)==0 - && (pLoop->wsFlags & WHERE_ONEROW)==0 - ){ - continue; - } - if( (tabUsed & pLoop->maskSelf)!=0 ) continue; - pEnd = sWLB.pWC->a + sWLB.pWC->nTerm; - for(pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0 ){ - if( !ExprHasProperty(pTerm->pExpr, EP_FromJoin) - || pTerm->pExpr->iRightJoinTable!=pItem->iCursor - ){ - break; - } - } - } - if( pTerm drop loop %c not used\n", pLoop->cId)); - notReady &= ~pLoop->maskSelf; - for(pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0 ){ - pTerm->wtFlags |= TERM_CODED; - } - } - if( i!=pWInfo->nLevel-1 ){ - int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); - memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); - } - pWInfo->nLevel--; - nTabList--; - } + notReady = whereOmitNoopJoin(pWInfo, notReady); + nTabList = pWInfo->nLevel; + assert( nTabList>0 ); } + + /* Check to see if there are any SEARCH loops that might benefit from + ** using a Bloom filter. + */ + if( pWInfo->nLevel>=2 + && OptimizationEnabled(db, SQLITE_BloomFilter) + ){ + whereCheckIfBloomFilterIsUseful(pWInfo); + } + #if defined(WHERETRACE_ENABLED) - if( wx_sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */ + if( wx_sqlite3WhereTrace & 0x4000 ){ /* Display all terms of the WHERE clause */ wx_sqlite3DebugPrintf("---- WHERE clause at end of analysis:\n"); wx_sqlite3WhereClausePrint(sWLB.pWC); } - WHERETRACE(0xffff,("*** Optimizer Finished ***\n")); + WHERETRACE(0xffffffff,("*** Optimizer Finished ***\n")); #endif pWInfo->pParse->nQueryLoop += pWInfo->nRowOut; @@ -153533,7 +163229,7 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( pTab = pTabItem->pTab; iDb = wx_sqlite3SchemaToIndex(db, pTab->pSchema); pLoop = pLevel->pWLoop; - if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){ + if( (pTab->tabFlags & TF_Ephemeral)!=0 || IsView(pTab) ){ /* Do nothing */ }else #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -153545,8 +163241,10 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( /* noop */ }else #endif - if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 - && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ + if( ((pLoop->wsFlags & WHERE_IDX_ONLY)==0 + && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0) + || (pTabItem->fg.jointype & (JT_LTORJ|JT_RIGHT))!=0 + ){ int op = OP_OpenRead; if( pWInfo->eOnePass!=ONEPASS_OFF ){ op = OP_OpenWrite; @@ -153559,6 +163257,7 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( if( pWInfo->eOnePass==ONEPASS_OFF && pTab->nColtabFlags & (TF_HasGenerated|TF_WithoutRowid))==0 + && (pLoop->wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))==0 ){ /* If we know that only a prefix of the record will be used, ** it is advantageous to reduce the "column count" field in @@ -153612,8 +163311,12 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( op = OP_ReopenIdx; }else{ iIndexCur = pParse->nTab++; + if( pIx->bHasExpr && OptimizationEnabled(db, SQLITE_IndexedExpr) ){ + whereAddIndexedExpr(pParse, pIx, iIndexCur, pTabItem); + } } pLevel->iIdxCur = iIndexCur; + assert( pIx!=0 ); assert( pIx->pSchema==pTab->pSchema ); assert( iIndexCur>=0 ); if( op ){ @@ -153647,6 +163350,37 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( } } if( iDb>=0 ) wx_sqlite3CodeVerifySchema(pParse, iDb); + if( (pTabItem->fg.jointype & JT_RIGHT)!=0 + && (pLevel->pRJ = wx_sqlite3WhereMalloc(pWInfo, sizeof(WhereRightJoin)))!=0 + ){ + WhereRightJoin *pRJ = pLevel->pRJ; + pRJ->iMatch = pParse->nTab++; + pRJ->regBloom = ++pParse->nMem; + wx_sqlite3VdbeAddOp2(v, OP_Blob, 65536, pRJ->regBloom); + pRJ->regReturn = ++pParse->nMem; + wx_sqlite3VdbeAddOp2(v, OP_Null, 0, pRJ->regReturn); + assert( pTab==pTabItem->pTab ); + if( HasRowid(pTab) ){ + KeyInfo *pInfo; + wx_sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, 1); + pInfo = wx_sqlite3KeyInfoAlloc(pParse->db, 1, 0); + if( pInfo ){ + pInfo->aColl[0] = 0; + pInfo->aSortFlags[0] = 0; + wx_sqlite3VdbeAppendP4(v, pInfo, P4_KEYINFO); + } + }else{ + Index *pPk = wx_sqlite3PrimaryKeyIndex(pTab); + wx_sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, pPk->nKeyCol); + wx_sqlite3VdbeSetP4KeyInfo(pParse, pPk); + } + pLoop->wsFlags &= ~WHERE_IDX_ONLY; + /* The nature of RIGHT JOIN processing is such that it messes up + ** the output order. So omit any ORDER BY/GROUP BY elimination + ** optimizations. We need to do an actual sort for RIGHT JOIN. */ + pWInfo->nOBSat = 0; + pWInfo->eDistinct = WHERE_DISTINCT_UNORDERED; + } } pWInfo->iTop = wx_sqlite3VdbeCurrentAddr(v); if( db->mallocFailed ) goto whereBeginError; @@ -153658,15 +163392,31 @@ SQLITE_PRIVATE WhereInfo *wx_sqlite3WhereBegin( for(ii=0; iinErr ) goto whereBeginError; pLevel = &pWInfo->a[ii]; wsFlags = pLevel->pWLoop->wsFlags; + pSrc = &pTabList->a[pLevel->iFrom]; + if( pSrc->fg.isMaterialized ){ + if( pSrc->fg.isCorrelated ){ + wx_sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub); + }else{ + int iOnce = wx_sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + wx_sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub); + wx_sqlite3VdbeJumpHere(v, iOnce); + } + } + if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ + if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){ #ifndef SQLITE_OMIT_AUTOMATIC_INDEX - if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){ - constructAutomaticIndex(pParse, &pWInfo->sWC, - &pTabList->a[pLevel->iFrom], notReady, pLevel); + constructAutomaticIndex(pParse, &pWInfo->sWC, + &pTabList->a[pLevel->iFrom], notReady, pLevel); +#endif + }else{ + wx_sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady); + } if( db->mallocFailed ) goto whereBeginError; } -#endif addrExplain = wx_sqlite3WhereExplainOneScan( pParse, pTabList, pLevel, wctrlFlags ); @@ -153712,6 +163462,26 @@ whereBeginError: } #endif +#ifdef SQLITE_DEBUG +/* +** Return true if cursor iCur is opened by instruction k of the +** bytecode. Used inside of assert() only. +*/ +static int cursorIsOpen(Vdbe *v, int iCur, int k){ + while( k>=0 ){ + VdbeOp *pOp = wx_sqlite3VdbeGetOp(v,k--); + if( pOp->p1!=iCur ) continue; + if( pOp->opcode==OP_Close ) return 0; + if( pOp->opcode==OP_OpenRead ) return 1; + if( pOp->opcode==OP_OpenWrite ) return 1; + if( pOp->opcode==OP_OpenDup ) return 1; + if( pOp->opcode==OP_OpenAutoindex ) return 1; + if( pOp->opcode==OP_OpenEphemeral ) return 1; + } + return 0; +} +#endif /* SQLITE_DEBUG */ + /* ** Generate the end of the WHERE loop. See comments on ** wx_sqlite3WhereBegin() for additional information. @@ -153725,6 +163495,7 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ SrcList *pTabList = pWInfo->pTabList; wx_sqlite3 *db = pParse->db; int iEnd = wx_sqlite3VdbeCurrentAddr(v); + int nRJ = 0; /* Generate loop termination code. */ @@ -153732,6 +163503,17 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ for(i=pWInfo->nLevel-1; i>=0; i--){ int addr; pLevel = &pWInfo->a[i]; + if( pLevel->pRJ ){ + /* Terminate the subroutine that forms the interior of the loop of + ** the RIGHT JOIN table */ + WhereRightJoin *pRJ = pLevel->pRJ; + wx_sqlite3VdbeResolveLabel(v, pLevel->addrCont); + pLevel->addrCont = 0; + pRJ->endSubrtn = wx_sqlite3VdbeCurrentAddr(v); + wx_sqlite3VdbeAddOp3(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn, 1); + VdbeCoverage(v); + nRJ++; + } pLoop = pLevel->pWLoop; if( pLevel->op!=OP_Noop ){ #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT @@ -153759,7 +163541,7 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ } #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ /* The common case: Advance to the next row */ - wx_sqlite3VdbeResolveLabel(v, pLevel->addrCont); + if( pLevel->addrCont ) wx_sqlite3VdbeResolveLabel(v, pLevel->addrCont); wx_sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); wx_sqlite3VdbeChangeP5(v, pLevel->p5); VdbeCoverage(v); @@ -153774,14 +163556,16 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT if( addrSeek ) wx_sqlite3VdbeJumpHere(v, addrSeek); #endif - }else{ + }else if( pLevel->addrCont ){ wx_sqlite3VdbeResolveLabel(v, pLevel->addrCont); } - if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){ + if( (pLoop->wsFlags & WHERE_IN_ABLE)!=0 && pLevel->u.in.nIn>0 ){ struct InLoop *pIn; int j; wx_sqlite3VdbeResolveLabel(v, pLevel->addrNxt); for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){ + assert( wx_sqlite3VdbeGetOp(v, pIn->addrInTop+1)->opcode==OP_IsNull + || pParse->db->mallocFailed ); wx_sqlite3VdbeJumpHere(v, pIn->addrInTop+1); if( pIn->eEndLoopOp!=OP_Noop ){ if( pIn->nPrefix ){ @@ -153806,6 +163590,11 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ wx_sqlite3VdbeCurrentAddr(v)+2, pIn->iBase, pIn->nPrefix); VdbeCoverage(v); + /* Retarget the OP_IsNull against the left operand of IN so + ** it jumps past the OP_IfNoHope. This is because the + ** OP_IsNull also bypasses the OP_Affinity opcode that is + ** required by OP_IfNoHope. */ + wx_sqlite3VdbeJumpHere(v, pIn->addrInTop+1); } } wx_sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop); @@ -153817,6 +163606,10 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ } } wx_sqlite3VdbeResolveLabel(v, pLevel->addrBrk); + if( pLevel->pRJ ){ + wx_sqlite3VdbeAddOp3(v, OP_Return, pLevel->pRJ->regReturn, 0, 1); + VdbeCoverage(v); + } if( pLevel->addrSkip ){ wx_sqlite3VdbeGoto(v, pLevel->addrSkip); VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName)); @@ -153839,8 +163632,14 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ wx_sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); } if( (ws & WHERE_INDEXED) - || ((ws & WHERE_MULTI_OR) && pLevel->u.pCovidx) + || ((ws & WHERE_MULTI_OR) && pLevel->u.pCoveringIdx) ){ + if( ws & WHERE_MULTI_OR ){ + Index *pIx = pLevel->u.pCoveringIdx; + int iDb = wx_sqlite3SchemaToIndex(db, pIx->pSchema); + wx_sqlite3VdbeAddOp3(v, OP_ReopenIdx, pLevel->iIdxCur, pIx->tnum, iDb); + wx_sqlite3VdbeSetP4KeyInfo(pParse, pIx); + } wx_sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur); } if( pLevel->op==OP_Return ){ @@ -153854,11 +163653,6 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); } - /* The "break" point is here, just past the end of the outer loop. - ** Set it. - */ - wx_sqlite3VdbeResolveLabel(v, pWInfo->iBreak); - assert( pWInfo->nLevel<=pTabList->nSrc ); for(i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++){ int k, last; @@ -153869,6 +163663,15 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ assert( pTab!=0 ); pLoop = pLevel->pWLoop; + /* Do RIGHT JOIN processing. Generate code that will output the + ** unmatched rows of the right operand of the RIGHT JOIN with + ** all of the columns of the left operand set to NULL. + */ + if( pLevel->pRJ ){ + wx_sqlite3WhereRightJoinLoop(pWInfo, i, pLevel); + continue; + } + /* For a co-routine, change all OP_Column references to the table of ** the co-routine into OP_Copy of result contained in a register. ** OP_Rowid becomes OP_Null. @@ -153880,29 +163683,6 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ continue; } -#ifdef SQLITE_ENABLE_EARLY_CURSOR_CLOSE - /* Close all of the cursors that were opened by wx_sqlite3WhereBegin. - ** Except, do not close cursors that will be reused by the OR optimization - ** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors - ** created for the ONEPASS optimization. - */ - if( (pTab->tabFlags & TF_Ephemeral)==0 - && pTab->pSelect==0 - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 - ){ - int ws = pLoop->wsFlags; - if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){ - wx_sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); - } - if( (ws & WHERE_INDEXED)!=0 - && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 - && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1] - ){ - wx_sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); - } - } -#endif - /* If this scan uses an index, make VDBE code substitutions to read data ** from the index instead of from the table where possible. In some cases ** this optimization prevents the table from ever being read, which can @@ -153917,7 +163697,7 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ if( pLoop->wsFlags & (WHERE_INDEXED|WHERE_IDX_ONLY) ){ pIdx = pLoop->u.btree.pIndex; }else if( pLoop->wsFlags & WHERE_MULTI_OR ){ - pIdx = pLevel->u.pCovidx; + pIdx = pLevel->u.pCoveringIdx; } if( pIdx && !db->mallocFailed @@ -153927,6 +163707,23 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ }else{ last = pWInfo->iEndWhere; } + if( pIdx->bHasExpr ){ + IndexedExpr *p = pParse->pIdxEpr; + while( p ){ + if( p->iIdxCur==pLevel->iIdxCur ){ +#ifdef WHERETRACE_ENABLED + if( wx_sqlite3WhereTrace & 0x200 ){ + wx_sqlite3DebugPrintf("Disable pParse->pIdxEpr term {%d,%d}\n", + p->iIdxCur, p->iIdxCol); + if( wx_sqlite3WhereTrace & 0x5000 ) wx_sqlite3ShowExpr(p->pExpr); + } +#endif + p->iDataCur = -1; + p->iIdxCur = -1; + } + p = p->pIENext; + } + } k = pLevel->addrBody + 1; #ifdef SQLITE_DEBUG if( db->flags & SQLITE_VdbeAddopTrace ){ @@ -153940,7 +163737,7 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ #endif pOp = wx_sqlite3VdbeGetOp(v, k); pLastOp = pOp + (last - k); - assert( pOpnErr>0 && pOp==pLastOp) ); + assert( pOp<=pLastOp ); do{ if( pOp->p1!=pLevel->iTabCur ){ /* no-op */ @@ -153951,6 +163748,11 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ ){ int x = pOp->p2; assert( pIdx->pTable==pTab ); +#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC + if( pOp->opcode==OP_Offset ){ + /* Do not need to translate the column number */ + }else +#endif if( !HasRowid(pTab) ){ Index *pPk = wx_sqlite3PrimaryKeyIndex(pTab); x = pPk->aiColumn[x]; @@ -153964,9 +163766,22 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ pOp->p2 = x; pOp->p1 = pLevel->iIdxCur; OpcodeRewriteTrace(db, k, pOp); + }else{ + /* Unable to translate the table reference into an index + ** reference. Verify that this is harmless - that the + ** table being referenced really is open. + */ +#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC + assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 + || cursorIsOpen(v,pOp->p1,k) + || pOp->opcode==OP_Offset + ); +#else + assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 + || cursorIsOpen(v,pOp->p1,k) + ); +#endif } - assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 - || pWInfo->eOnePass ); }else if( pOp->opcode==OP_Rowid ){ pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; @@ -153985,18 +163800,16 @@ SQLITE_PRIVATE void wx_sqlite3WhereEnd(WhereInfo *pWInfo){ } } - /* Undo all Expr node modifications */ - while( pWInfo->pExprMods ){ - WhereExprMod *p = pWInfo->pExprMods; - pWInfo->pExprMods = p->pNext; - memcpy(p->pExpr, &p->orig, sizeof(p->orig)); - wx_sqlite3DbFree(db, p); - } + /* The "break" point is here, just past the end of the outer loop. + ** Set it. + */ + wx_sqlite3VdbeResolveLabel(v, pWInfo->iBreak); /* Final cleanup */ pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); + pParse->withinRJSubrtn -= nRJ; return; } @@ -154585,7 +164398,7 @@ static void noopValueFunc(wx_sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ /* Window functions that use all window interfaces: xStep, xFinal, ** xValue, and xInverse */ #define WINDOWFUNCALL(name,nArg,extra) { \ - nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ + nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ name ## StepFunc, name ## FinalizeFunc, name ## ValueFunc, \ name ## InvFunc, name ## Name, {0} \ } @@ -154593,7 +164406,7 @@ static void noopValueFunc(wx_sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ /* Window functions that are implemented using bytecode and thus have ** no-op routines for their methods */ #define WINDOWFUNCNOOP(name,nArg,extra) { \ - nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ + nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ noopStepFunc, noopValueFunc, noopValueFunc, \ noopStepFunc, name ## Name, {0} \ } @@ -154602,7 +164415,7 @@ static void noopValueFunc(wx_sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ ** same routine for xFinalize and xValue and which never call ** xInverse. */ #define WINDOWFUNCX(name,nArg,extra) { \ - nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ + nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ name ## StepFunc, name ## ValueFunc, name ## ValueFunc, \ noopStepFunc, name ## Name, {0} \ } @@ -154728,7 +164541,7 @@ SQLITE_PRIVATE void wx_sqlite3WindowUpdate( } } } - pWin->pFunc = pFunc; + pWin->pWFunc = pFunc; } /* @@ -154792,6 +164605,7 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){ case TK_AGG_FUNCTION: case TK_COLUMN: { int iCol = -1; + if( pParse->db->mallocFailed ) return WRC_Abort; if( p->pSub ){ int i; for(i=0; ipSub->nExpr; i++){ @@ -154901,14 +164715,16 @@ static ExprList *exprListAppendList( int i; int nInit = pList ? pList->nExpr : 0; for(i=0; inExpr; i++){ - Expr *pDup = wx_sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); - assert( pDup==0 || !ExprHasProperty(pDup, EP_MemToken) ); - if( bIntToNull && pDup ){ + wx_sqlite3 *db = pParse->db; + Expr *pDup = wx_sqlite3ExprDup(db, pAppend->a[i].pExpr, 0); + if( db->mallocFailed ){ + wx_sqlite3ExprDelete(db, pDup); + break; + } + if( bIntToNull ){ int iDummy; Expr *pSub; - for(pSub=pDup; ExprHasProperty(pSub, EP_Skip); pSub=pSub->pLeft){ - assert( pSub ); - } + pSub = wx_sqlite3ExprSkipCollateAndLikely(pDup); if( wx_sqlite3ExprIsInteger(pSub, &iDummy) ){ pSub->op = TK_NULL; pSub->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); @@ -154916,7 +164732,7 @@ static ExprList *exprListAppendList( } } pList = wx_sqlite3ExprListAppend(pParse, pList, pDup); - if( pList ) pList->a[nInit+i].sortFlags = pAppend->a[i].sortFlags; + if( pList ) pList->a[nInit+i].fg.sortFlags = pAppend->a[i].fg.sortFlags; } } return pList; @@ -154939,6 +164755,15 @@ static int wx_sqlite3WindowExtraAggFuncDepth(Walker *pWalker, Expr *pExpr){ return WRC_Continue; } +static int disallowAggregatesInOrderByCb(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_AGG_FUNCTION && pExpr->pAggInfo==0 ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + wx_sqlite3ErrorMsg(pWalker->pParse, + "misuse of aggregate: %s()", pExpr->u.zToken); + } + return WRC_Continue; +} + /* ** If the SELECT statement passed as the second argument does not invoke ** any SQL window functions, this function is a no-op. Otherwise, it @@ -154948,7 +164773,11 @@ static int wx_sqlite3WindowExtraAggFuncDepth(Walker *pWalker, Expr *pExpr){ */ SQLITE_PRIVATE int wx_sqlite3WindowRewrite(Parse *pParse, Select *p){ int rc = SQLITE_OK; - if( p->pWin && p->pPrior==0 && (p->selFlags & SF_WinRewrite)==0 ){ + if( p->pWin + && p->pPrior==0 + && ALWAYS((p->selFlags & SF_WinRewrite)==0) + && ALWAYS(!IN_RENAME_OBJECT) + ){ Vdbe *v = wx_sqlite3GetVdbe(pParse); wx_sqlite3 *db = pParse->db; Select *pSub = 0; /* The subquery */ @@ -154972,6 +164801,11 @@ SQLITE_PRIVATE int wx_sqlite3WindowRewrite(Parse *pParse, Select *p){ } wx_sqlite3AggInfoPersistWalkerInit(&w, pParse); wx_sqlite3WalkSelect(&w, p); + if( (p->selFlags & SF_Aggregate)==0 ){ + w.xExprCallback = disallowAggregatesInOrderByCb; + w.xSelectCallback = 0; + wx_sqlite3WalkExprList(&w, p->pOrderBy); + } p->pSrc = 0; p->pWhere = 0; @@ -155016,8 +164850,11 @@ SQLITE_PRIVATE int wx_sqlite3WindowRewrite(Parse *pParse, Select *p){ ** window function - one for the accumulator, another for interim ** results. */ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - ExprList *pArgs = pWin->pOwner->x.pList; - if( pWin->pFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){ + ExprList *pArgs; + assert( ExprUseXList(pWin->pOwner) ); + assert( pWin->pWFunc!=0 ); + pArgs = pWin->pOwner->x.pList; + if( pWin->pWFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){ selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist); pWin->iArgCol = (pSublist ? pSublist->nExpr : 0); pWin->bExprArgs = 1; @@ -155049,15 +164886,19 @@ SQLITE_PRIVATE int wx_sqlite3WindowRewrite(Parse *pParse, Select *p){ pSub = wx_sqlite3SelectNew( pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0 ); - SELECTTRACE(1,pParse,pSub, + TREETRACE(0x40,pParse,pSub, ("New window-function subquery in FROM clause of (%u/%p)\n", p->selId, p)); p->pSrc = wx_sqlite3SrcListAppend(pParse, 0, 0, 0); + assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside + ** of wx_sqlite3DbMallocRawNN() called from + ** wx_sqlite3SrcListAppend() */ if( p->pSrc ){ Table *pTab2; p->pSrc->a[0].pSelect = pSub; + p->pSrc->a[0].fg.isCorrelated = 1; wx_sqlite3SrcListAssignCursors(pParse, p->pSrc); - pSub->selFlags |= SF_Expanded; + pSub->selFlags |= SF_Expanded|SF_OrderByReqd; pTab2 = wx_sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE); pSub->selFlags |= (selFlags & SF_Aggregate); if( pTab2==0 ){ @@ -155080,15 +164921,14 @@ SQLITE_PRIVATE int wx_sqlite3WindowRewrite(Parse *pParse, Select *p){ wx_sqlite3SelectDelete(db, pSub); } if( db->mallocFailed ) rc = SQLITE_NOMEM; - wx_sqlite3DbFree(db, pTab); - } - if( rc ){ - if( pParse->nErr==0 ){ - assert( pParse->db->mallocFailed ); - wx_sqlite3ErrorToParser(pParse->db, SQLITE_NOMEM); - } + /* Defer deleting the temporary table pTab because if an error occurred, + ** there could still be references to that table embedded in the + ** result-set or ORDER BY clause of the SELECT statement p. */ + wx_sqlite3ParserAddCleanup(pParse, wx_sqlite3DbFree, pTab); } + + assert( rc==SQLITE_OK || pParse->nErr!=0 ); return rc; } @@ -155329,7 +165169,12 @@ SQLITE_PRIVATE void wx_sqlite3WindowLink(Select *pSel, Window *pWin){ ** different, or 2 if it cannot be determined if the objects are identical ** or not. Identical window objects can be processed in a single scan. */ -SQLITE_PRIVATE int wx_sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2, int bFilter){ +SQLITE_PRIVATE int wx_sqlite3WindowCompare( + const Parse *pParse, + const Window *p1, + const Window *p2, + int bFilter +){ int res; if( NEVER(p1==0) || NEVER(p2==0) ) return 1; if( p1->eFrmType!=p2->eFrmType ) return 1; @@ -155392,7 +165237,7 @@ SQLITE_PRIVATE void wx_sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){ } for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *p = pWin->pFunc; + FuncDef *p = pWin->pWFunc; if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){ /* The inline versions of min() and max() require a single ephemeral ** table and 3 registers. The registers are used as follows: @@ -155401,12 +165246,15 @@ SQLITE_PRIVATE void wx_sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){ ** regApp+1: integer value used to ensure keys are unique ** regApp+2: output of MakeRecord */ - ExprList *pList = pWin->pOwner->x.pList; - KeyInfo *pKeyInfo = wx_sqlite3KeyInfoFromExprList(pParse, pList, 0, 0); + ExprList *pList; + KeyInfo *pKeyInfo; + assert( ExprUseXList(pWin->pOwner) ); + pList = pWin->pOwner->x.pList; + pKeyInfo = wx_sqlite3KeyInfoFromExprList(pParse, pList, 0, 0); pWin->csrApp = pParse->nTab++; pWin->regApp = pParse->nMem+1; pParse->nMem += 3; - if( pKeyInfo && pWin->pFunc->zName[1]=='i' ){ + if( pKeyInfo && pWin->pWFunc->zName[1]=='i' ){ assert( pKeyInfo->aSortFlags[0]==0 ); pKeyInfo->aSortFlags[0] = KEYINFO_ORDER_DESC; } @@ -155490,7 +165338,9 @@ static void windowCheckValue(Parse *pParse, int reg, int eCond){ ** with the object passed as the only argument to this function. */ static int windowArgCount(Window *pWin){ - ExprList *pList = pWin->pOwner->x.pList; + const ExprList *pList; + assert( ExprUseXList(pWin->pOwner) ); + pList = pWin->pOwner->x.pList; return (pList ? pList->nExpr : 0); } @@ -155568,6 +165418,7 @@ struct WindowCodeArg { int regGosub; /* Register used with OP_Gosub(addrGosub) */ int regArg; /* First in array of accumulator registers */ int eDelete; /* See above */ + int regRowid; WindowCsrAndReg start; WindowCsrAndReg current; @@ -155626,7 +165477,7 @@ static void windowAggStep( Vdbe *v = wx_sqlite3GetVdbe(pParse); Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; + FuncDef *pFunc = pWin->pWFunc; int regArg; int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin); int i; @@ -155674,6 +165525,7 @@ static void windowAggStep( int addrIf = 0; if( pWin->pFilter ){ int regTmp; + assert( ExprUseXList(pWin->pOwner) ); assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); regTmp = wx_sqlite3GetTempReg(pParse); @@ -155684,16 +165536,17 @@ static void windowAggStep( } if( pWin->bExprArgs ){ - int iStart = wx_sqlite3VdbeCurrentAddr(v); - VdbeOp *pOp, *pEnd; + int iOp = wx_sqlite3VdbeCurrentAddr(v); + int iEnd; + assert( ExprUseXList(pWin->pOwner) ); nArg = pWin->pOwner->x.pList->nExpr; regArg = wx_sqlite3GetTempRange(pParse, nArg); wx_sqlite3ExprCodeExprList(pParse, pWin->pOwner->x.pList, regArg, 0, 0); - pEnd = wx_sqlite3VdbeGetOp(v, -1); - for(pOp=wx_sqlite3VdbeGetOp(v, iStart); pOp<=pEnd; pOp++){ - if( pOp->opcode==OP_Column && pOp->p1==pWin->iEphCsr ){ + for(iEnd=wx_sqlite3VdbeCurrentAddr(v); iOpopcode==OP_Column && pOp->p1==pMWin->iEphCsr ){ pOp->p1 = csr; } } @@ -155701,6 +165554,7 @@ static void windowAggStep( if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ CollSeq *pColl; assert( nArg>0 ); + assert( ExprUseXList(pWin->pOwner) ); pColl = wx_sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr); wx_sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ); } @@ -155737,7 +165591,7 @@ static void windowAggFinal(WindowCodeArg *p, int bFin){ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ if( pMWin->regStartRowid==0 - && (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) + && (pWin->pWFunc->funcFlags & SQLITE_FUNC_MINMAX) && (pWin->eStart!=TK_UNBOUNDED) ){ wx_sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); @@ -155751,12 +165605,12 @@ static void windowAggFinal(WindowCodeArg *p, int bFin){ int nArg = windowArgCount(pWin); if( bFin ){ wx_sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, nArg); - wx_sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + wx_sqlite3VdbeAppendP4(v, pWin->pWFunc, P4_FUNCDEF); wx_sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult); wx_sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); }else{ wx_sqlite3VdbeAddOp3(v, OP_AggValue,pWin->regAccum,nArg,pWin->regResult); - wx_sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + wx_sqlite3VdbeAppendP4(v, pWin->pWFunc, P4_FUNCDEF); } } } @@ -155885,7 +165739,8 @@ static void windowReturnOneRow(WindowCodeArg *p){ Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; + FuncDef *pFunc = pWin->pWFunc; + assert( ExprUseXList(pWin->pOwner) ); if( pFunc->zName==nth_valueName || pFunc->zName==first_valueName ){ @@ -155956,7 +165811,7 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){ int nArg = 0; Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; + FuncDef *pFunc = pWin->pWFunc; assert( pWin->regAccum ); wx_sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); nArg = MAX(nArg, windowArgCount(pWin)); @@ -155986,7 +165841,7 @@ static int windowCacheFrame(Window *pMWin){ Window *pWin; if( pMWin->regStartRowid ) return 1; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; + FuncDef *pFunc = pWin->pWFunc; if( (pFunc->zName==nth_valueName) || (pFunc->zName==first_valueName) || (pFunc->zName==leadName) @@ -156051,7 +165906,7 @@ static void windowIfNewPeer( ** if( csr1.peerVal - regVal <= csr2.peerVal ) goto lbl; ** ** A special type of arithmetic is used such that if csr1.peerVal is not -** a numeric type (real or integer), then the result of the addition addition +** a numeric type (real or integer), then the result of the addition ** or subtraction is a a copy of csr1.peerVal. */ static void windowCodeRangeTest( @@ -156070,11 +165925,16 @@ static void windowCodeRangeTest( int regString = ++pParse->nMem; /* Reg. for constant value '' */ int arith = OP_Add; /* OP_Add or OP_Subtract */ int addrGe; /* Jump destination */ + int addrDone = wx_sqlite3VdbeMakeLabel(pParse); /* Address past OP_Ge */ CollSeq *pColl; + /* Read the peer-value from each cursor into a register */ + windowReadPeerValues(p, csr1, reg1); + windowReadPeerValues(p, csr2, reg2); + assert( op==OP_Ge || op==OP_Gt || op==OP_Le ); assert( pOrderBy && pOrderBy->nExpr==1 ); - if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){ + if( pOrderBy->a[0].fg.sortFlags & KEYINFO_ORDER_DESC ){ switch( op ){ case OP_Ge: op = OP_Le; break; case OP_Gt: op = OP_Lt; break; @@ -156083,34 +165943,11 @@ static void windowCodeRangeTest( arith = OP_Subtract; } - /* Read the peer-value from each cursor into a register */ - windowReadPeerValues(p, csr1, reg1); - windowReadPeerValues(p, csr2, reg2); - VdbeModuleComment((v, "CodeRangeTest: if( R%d %s R%d %s R%d ) goto lbl", reg1, (arith==OP_Add ? "+" : "-"), regVal, ((op==OP_Ge) ? ">=" : (op==OP_Le) ? "<=" : (op==OP_Gt) ? ">" : "<"), reg2 )); - /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1). - ** This block adds (or subtracts for DESC) the numeric value in regVal - ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob), - ** then leave reg1 as it is. In pseudo-code, this is implemented as: - ** - ** if( reg1>='' ) goto addrGe; - ** reg1 = reg1 +/- regVal - ** addrGe: - ** - ** Since all strings and blobs are greater-than-or-equal-to an empty string, - ** the add/subtract is skipped for these, as required. If reg1 is a NULL, - ** then the arithmetic is performed, but since adding or subtracting from - ** NULL is always NULL anyway, this case is handled as required too. */ - wx_sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); - addrGe = wx_sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1); - VdbeCoverage(v); - wx_sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1); - wx_sqlite3VdbeJumpHere(v, addrGe); - /* If the BIGNULL flag is set for the ORDER BY, then it is required to ** consider NULL values to be larger than all other values, instead of ** the usual smaller. The VDBE opcodes OP_Ge and so on do not handle this @@ -156130,7 +165967,7 @@ static void windowCodeRangeTest( ** Additionally, if either reg1 or reg2 are NULL but the jump to lbl is ** not taken, control jumps over the comparison operator coded below this ** block. */ - if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_BIGNULL ){ + if( pOrderBy->a[0].fg.sortFlags & KEYINFO_ORDER_BIGNULL ){ /* This block runs if reg1 contains a NULL. */ int addr = wx_sqlite3VdbeAddOp1(v, OP_NotNull, reg1); VdbeCoverage(v); switch( op ){ @@ -156147,15 +165984,36 @@ static void windowCodeRangeTest( break; default: assert( op==OP_Lt ); /* no-op */ break; } - wx_sqlite3VdbeAddOp2(v, OP_Goto, 0, wx_sqlite3VdbeCurrentAddr(v)+3); + wx_sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone); /* This block runs if reg1 is not NULL, but reg2 is. */ wx_sqlite3VdbeJumpHere(v, addr); - wx_sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl); VdbeCoverage(v); - if( op==OP_Gt || op==OP_Ge ){ - wx_sqlite3VdbeChangeP2(v, -1, wx_sqlite3VdbeCurrentAddr(v)+1); - } + wx_sqlite3VdbeAddOp2(v, OP_IsNull, reg2, + (op==OP_Gt || op==OP_Ge) ? addrDone : lbl); + VdbeCoverage(v); + } + + /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1). + ** This block adds (or subtracts for DESC) the numeric value in regVal + ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob), + ** then leave reg1 as it is. In pseudo-code, this is implemented as: + ** + ** if( reg1>='' ) goto addrGe; + ** reg1 = reg1 +/- regVal + ** addrGe: + ** + ** Since all strings and blobs are greater-than-or-equal-to an empty string, + ** the add/subtract is skipped for these, as required. If reg1 is a NULL, + ** then the arithmetic is performed, but since adding or subtracting from + ** NULL is always NULL anyway, this case is handled as required too. */ + wx_sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); + addrGe = wx_sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1); + VdbeCoverage(v); + if( (op==OP_Ge && arith==OP_Add) || (op==OP_Le && arith==OP_Subtract) ){ + wx_sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v); } + wx_sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1); + wx_sqlite3VdbeJumpHere(v, addrGe); /* Compare registers reg2 and reg1, taking the jump if required. Note that ** control skips over this test if the BIGNULL flag is set and either @@ -156164,6 +166022,7 @@ static void windowCodeRangeTest( pColl = wx_sqlite3ExprNNCollSeq(pParse, pOrderBy->a[0].pExpr); wx_sqlite3VdbeAppendP4(v, (void*)pColl, P4_COLLSEQ); wx_sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); + wx_sqlite3VdbeResolveLabel(v, addrDone); assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le ); testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge); @@ -156239,16 +166098,24 @@ static int windowCodeOp( /* If this is a (RANGE BETWEEN a FOLLOWING AND b FOLLOWING) or ** (RANGE BETWEEN b PRECEDING AND a PRECEDING) frame, ensure the ** start cursor does not advance past the end cursor within the - ** temporary table. It otherwise might, if (a>b). */ + ** temporary table. It otherwise might, if (a>b). Also ensure that, + ** if the input cursor is still finding new rows, that the end + ** cursor does not go past it to EOF. */ if( pMWin->eStart==pMWin->eEnd && regCountdown - && pMWin->eFrmType==TK_RANGE && op==WINDOW_AGGINVERSE + && pMWin->eFrmType==TK_RANGE ){ int regRowid1 = wx_sqlite3GetTempReg(pParse); int regRowid2 = wx_sqlite3GetTempReg(pParse); - wx_sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1); - wx_sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2); - wx_sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1); - VdbeCoverage(v); + if( op==WINDOW_AGGINVERSE ){ + wx_sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1); + wx_sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2); + wx_sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1); + VdbeCoverage(v); + }else if( p->regRowid ){ + wx_sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid1); + wx_sqlite3VdbeAddOp3(v, OP_Ge, p->regRowid, lblDone, regRowid1); + VdbeCoverageNeverNull(v); + } wx_sqlite3ReleaseTempReg(pParse, regRowid1); wx_sqlite3ReleaseTempReg(pParse, regRowid2); assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ); @@ -156331,7 +166198,7 @@ SQLITE_PRIVATE Window *wx_sqlite3WindowDup(wx_sqlite3 *db, Expr *pOwner, Window pNew->zName = wx_sqlite3DbStrDup(db, p->zName); pNew->zBase = wx_sqlite3DbStrDup(db, p->zBase); pNew->pFilter = wx_sqlite3ExprDup(db, p->pFilter, 0); - pNew->pFunc = p->pFunc; + pNew->pWFunc = p->pWFunc; pNew->pPartition = wx_sqlite3ExprListDup(db, p->pPartition, 0); pNew->pOrderBy = wx_sqlite3ExprListDup(db, p->pOrderBy, 0); pNew->eFrmType = p->eFrmType; @@ -156745,7 +166612,6 @@ SQLITE_PRIVATE void wx_sqlite3WindowCodeStep( int addrEmpty; /* Address of OP_Rewind in flush: */ int regNew; /* Array of registers holding new input row */ int regRecord; /* regNew array in record form */ - int regRowid; /* Rowid for regRecord in eph table */ int regNewPeer = 0; /* Peer values for new row (part of regNew) */ int regPeer = 0; /* Peer values for current row */ int regFlushPart = 0; /* Register for "Gosub flush_partition" */ @@ -156817,7 +166683,7 @@ SQLITE_PRIVATE void wx_sqlite3WindowCodeStep( regNew = pParse->nMem+1; pParse->nMem += nInput; regRecord = ++pParse->nMem; - regRowid = ++pParse->nMem; + s.regRowid = ++pParse->nMem; /* If the window frame contains an " PRECEDING" or " FOLLOWING" ** clause, allocate registers to store the results of evaluating each @@ -156873,9 +166739,9 @@ SQLITE_PRIVATE void wx_sqlite3WindowCodeStep( } /* Insert the new row into the ephemeral table */ - wx_sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, regRowid); - wx_sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, regRowid); - addrNe = wx_sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, regRowid); + wx_sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, s.regRowid); + wx_sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, s.regRowid); + addrNe = wx_sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, s.regRowid); VdbeCoverageNeverNull(v); /* This block is run for the first row of each partition */ @@ -156896,8 +166762,7 @@ SQLITE_PRIVATE void wx_sqlite3WindowCodeStep( VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound */ VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */ windowAggFinal(&s, 0); - wx_sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); - VdbeCoverageNeverTaken(v); + wx_sqlite3VdbeAddOp1(v, OP_Rewind, s.current.csr); windowReturnOneRow(&s); wx_sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); wx_sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd); @@ -156909,13 +166774,10 @@ SQLITE_PRIVATE void wx_sqlite3WindowCodeStep( } if( pMWin->eStart!=TK_UNBOUNDED ){ - wx_sqlite3VdbeAddOp2(v, OP_Rewind, s.start.csr, 1); - VdbeCoverageNeverTaken(v); + wx_sqlite3VdbeAddOp1(v, OP_Rewind, s.start.csr); } - wx_sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); - VdbeCoverageNeverTaken(v); - wx_sqlite3VdbeAddOp2(v, OP_Rewind, s.end.csr, 1); - VdbeCoverageNeverTaken(v); + wx_sqlite3VdbeAddOp1(v, OP_Rewind, s.current.csr); + wx_sqlite3VdbeAddOp1(v, OP_Rewind, s.end.csr); if( regPeer && pOrderBy ){ wx_sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, pOrderBy->nExpr-1); wx_sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.start.reg, pOrderBy->nExpr-1); @@ -156993,6 +166855,7 @@ SQLITE_PRIVATE void wx_sqlite3WindowCodeStep( wx_sqlite3VdbeJumpHere(v, addrGosubFlush); } + s.regRowid = 0; addrEmpty = wx_sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite); VdbeCoverage(v); if( pMWin->eEnd==TK_PRECEDING ){ @@ -157208,10 +167071,7 @@ static void updateDeleteLimitError( } - /* Construct a new Expr object from a single identifier. Use the - ** new Expr to populate pOut. Set the span of pOut to be the identifier - ** that created the expression. - */ + /* Construct a new Expr object from a single token */ static Expr *tokenExpr(Parse *pParse, int op, Token t){ Expr *p = wx_sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1); if( p ){ @@ -157220,17 +167080,18 @@ static void updateDeleteLimitError( p->affExpr = 0; p->flags = EP_Leaf; ExprClearVVAProperties(p); - p->iAgg = -1; + /* p->iAgg = -1; // Not required */ p->pLeft = p->pRight = 0; - p->x.pList = 0; p->pAggInfo = 0; - p->y.pTab = 0; + memset(&p->x, 0, sizeof(p->x)); + memset(&p->y, 0, sizeof(p->y)); p->op2 = 0; p->iTable = 0; p->iColumn = 0; p->u.zToken = (char*)&p[1]; memcpy(p->u.zToken, t.z, t.n); p->u.zToken[t.n] = 0; + p->w.iOfst = (int)(t.z - pParse->zTail); if( wx_sqlite3Isquote(p->u.zToken[0]) ){ wx_sqlite3DequoteExpr(p); } @@ -157310,8 +167171,8 @@ static void updateDeleteLimitError( #define TK_LP 22 #define TK_RP 23 #define TK_AS 24 -#define TK_WITHOUT 25 -#define TK_COMMA 26 +#define TK_COMMA 25 +#define TK_WITHOUT 26 #define TK_ABORT 27 #define TK_ACTION 28 #define TK_AFTER 29 @@ -157397,77 +167258,79 @@ static void updateDeleteLimitError( #define TK_SLASH 109 #define TK_REM 110 #define TK_CONCAT 111 -#define TK_COLLATE 112 -#define TK_BITNOT 113 -#define TK_ON 114 -#define TK_INDEXED 115 -#define TK_STRING 116 -#define TK_JOIN_KW 117 -#define TK_CONSTRAINT 118 -#define TK_DEFAULT 119 -#define TK_NULL 120 -#define TK_PRIMARY 121 -#define TK_UNIQUE 122 -#define TK_CHECK 123 -#define TK_REFERENCES 124 -#define TK_AUTOINCR 125 -#define TK_INSERT 126 -#define TK_DELETE 127 -#define TK_UPDATE 128 -#define TK_SET 129 -#define TK_DEFERRABLE 130 -#define TK_FOREIGN 131 -#define TK_DROP 132 -#define TK_UNION 133 -#define TK_ALL 134 -#define TK_EXCEPT 135 -#define TK_INTERSECT 136 -#define TK_SELECT 137 -#define TK_VALUES 138 -#define TK_DISTINCT 139 -#define TK_DOT 140 -#define TK_FROM 141 -#define TK_JOIN 142 -#define TK_USING 143 -#define TK_ORDER 144 -#define TK_GROUP 145 -#define TK_HAVING 146 -#define TK_LIMIT 147 -#define TK_WHERE 148 -#define TK_RETURNING 149 -#define TK_INTO 150 -#define TK_NOTHING 151 -#define TK_FLOAT 152 -#define TK_BLOB 153 -#define TK_INTEGER 154 -#define TK_VARIABLE 155 -#define TK_CASE 156 -#define TK_WHEN 157 -#define TK_THEN 158 -#define TK_ELSE 159 -#define TK_INDEX 160 -#define TK_ALTER 161 -#define TK_ADD 162 -#define TK_WINDOW 163 -#define TK_OVER 164 -#define TK_FILTER 165 -#define TK_COLUMN 166 -#define TK_AGG_FUNCTION 167 -#define TK_AGG_COLUMN 168 -#define TK_TRUEFALSE 169 -#define TK_ISNOT 170 -#define TK_FUNCTION 171 -#define TK_UMINUS 172 -#define TK_UPLUS 173 -#define TK_TRUTH 174 -#define TK_REGISTER 175 -#define TK_VECTOR 176 -#define TK_SELECT_COLUMN 177 -#define TK_IF_NULL_ROW 178 -#define TK_ASTERISK 179 -#define TK_SPAN 180 -#define TK_SPACE 181 -#define TK_ILLEGAL 182 +#define TK_PTR 112 +#define TK_COLLATE 113 +#define TK_BITNOT 114 +#define TK_ON 115 +#define TK_INDEXED 116 +#define TK_STRING 117 +#define TK_JOIN_KW 118 +#define TK_CONSTRAINT 119 +#define TK_DEFAULT 120 +#define TK_NULL 121 +#define TK_PRIMARY 122 +#define TK_UNIQUE 123 +#define TK_CHECK 124 +#define TK_REFERENCES 125 +#define TK_AUTOINCR 126 +#define TK_INSERT 127 +#define TK_DELETE 128 +#define TK_UPDATE 129 +#define TK_SET 130 +#define TK_DEFERRABLE 131 +#define TK_FOREIGN 132 +#define TK_DROP 133 +#define TK_UNION 134 +#define TK_ALL 135 +#define TK_EXCEPT 136 +#define TK_INTERSECT 137 +#define TK_SELECT 138 +#define TK_VALUES 139 +#define TK_DISTINCT 140 +#define TK_DOT 141 +#define TK_FROM 142 +#define TK_JOIN 143 +#define TK_USING 144 +#define TK_ORDER 145 +#define TK_GROUP 146 +#define TK_HAVING 147 +#define TK_LIMIT 148 +#define TK_WHERE 149 +#define TK_RETURNING 150 +#define TK_INTO 151 +#define TK_NOTHING 152 +#define TK_FLOAT 153 +#define TK_BLOB 154 +#define TK_INTEGER 155 +#define TK_VARIABLE 156 +#define TK_CASE 157 +#define TK_WHEN 158 +#define TK_THEN 159 +#define TK_ELSE 160 +#define TK_INDEX 161 +#define TK_ALTER 162 +#define TK_ADD 163 +#define TK_WINDOW 164 +#define TK_OVER 165 +#define TK_FILTER 166 +#define TK_COLUMN 167 +#define TK_AGG_FUNCTION 168 +#define TK_AGG_COLUMN 169 +#define TK_TRUEFALSE 170 +#define TK_ISNOT 171 +#define TK_FUNCTION 172 +#define TK_UMINUS 173 +#define TK_UPLUS 174 +#define TK_TRUTH 175 +#define TK_REGISTER 176 +#define TK_VECTOR 177 +#define TK_SELECT_COLUMN 178 +#define TK_IF_NULL_ROW 179 +#define TK_ASTERISK 180 +#define TK_SPAN 181 +#define TK_ERROR 182 +#define TK_SPACE 183 +#define TK_ILLEGAL 184 #endif /**************** End token definitions ***************************************/ @@ -157527,29 +167390,31 @@ static void updateDeleteLimitError( #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned short int -#define YYNOCODE 316 +#define YYNOCODE 319 #define YYACTIONTYPE unsigned short int #define YYWILDCARD 101 #define wx_sqlite3ParserTOKENTYPE Token typedef union { int yyinit; wx_sqlite3ParserTOKENTYPE yy0; - Window* yy19; - struct TrigEvent yy50; - int yy60; - struct FrameBound yy113; - Upsert* yy178; - With* yy195; - IdList* yy288; - SrcList* yy291; - Select* yy307; - ExprList* yy338; - TriggerStep* yy483; - const char* yy528; - u8 yy570; - Expr* yy602; - Cte* yy607; - struct {int value; int mask;} yy615; + TriggerStep* yy33; + Window* yy41; + Select* yy47; + SrcList* yy131; + struct TrigEvent yy180; + struct {int value; int mask;} yy231; + IdList* yy254; + u32 yy285; + ExprList* yy322; + Cte* yy385; + int yy394; + Upsert* yy444; + u8 yy516; + With* yy521; + const char* yy522; + Expr* yy528; + OnOrUsing yy561; + struct FrameBound yy595; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -157565,18 +167430,18 @@ typedef union { #define wx_sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; #define wx_sqlite3ParserCTX_STORE yypParser->pParse=pParse; #define YYFALLBACK 1 -#define YYNSTATE 570 -#define YYNRULE 398 -#define YYNRULE_WITH_ACTION 337 -#define YYNTOKEN 183 -#define YY_MAX_SHIFT 569 -#define YY_MIN_SHIFTREDUCE 825 -#define YY_MAX_SHIFTREDUCE 1222 -#define YY_ERROR_ACTION 1223 -#define YY_ACCEPT_ACTION 1224 -#define YY_NO_ACTION 1225 -#define YY_MIN_REDUCE 1226 -#define YY_MAX_REDUCE 1623 +#define YYNSTATE 576 +#define YYNRULE 405 +#define YYNRULE_WITH_ACTION 342 +#define YYNTOKEN 185 +#define YY_MAX_SHIFT 575 +#define YY_MIN_SHIFTREDUCE 835 +#define YY_MAX_SHIFTREDUCE 1239 +#define YY_ERROR_ACTION 1240 +#define YY_ACCEPT_ACTION 1241 +#define YY_NO_ACTION 1242 +#define YY_MIN_REDUCE 1243 +#define YY_MAX_REDUCE 1647 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -157643,600 +167508,618 @@ typedef union { ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (2020) +#define YY_ACTTAB_COUNT (2098) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 563, 1295, 563, 1274, 168, 361, 115, 112, 218, 373, - /* 10 */ 563, 1295, 374, 563, 488, 563, 115, 112, 218, 406, - /* 20 */ 1300, 1300, 41, 41, 41, 41, 514, 1504, 520, 1298, - /* 30 */ 1298, 959, 41, 41, 1257, 71, 71, 51, 51, 960, - /* 40 */ 557, 557, 557, 122, 123, 113, 1200, 1200, 1035, 1038, - /* 50 */ 1028, 1028, 120, 120, 121, 121, 121, 121, 414, 406, - /* 60 */ 273, 273, 273, 273, 115, 112, 218, 115, 112, 218, - /* 70 */ 197, 268, 545, 560, 515, 560, 1260, 563, 385, 248, - /* 80 */ 215, 521, 399, 122, 123, 113, 1200, 1200, 1035, 1038, - /* 90 */ 1028, 1028, 120, 120, 121, 121, 121, 121, 540, 13, - /* 100 */ 13, 1259, 119, 119, 119, 119, 118, 118, 117, 117, - /* 110 */ 117, 116, 441, 1176, 419, 1531, 446, 137, 512, 1539, - /* 120 */ 1545, 372, 1547, 6, 371, 1176, 1148, 1584, 1148, 406, - /* 130 */ 1545, 534, 115, 112, 218, 1267, 99, 441, 121, 121, - /* 140 */ 121, 121, 119, 119, 119, 119, 118, 118, 117, 117, - /* 150 */ 117, 116, 441, 122, 123, 113, 1200, 1200, 1035, 1038, - /* 160 */ 1028, 1028, 120, 120, 121, 121, 121, 121, 197, 1176, - /* 170 */ 1177, 1178, 241, 304, 554, 501, 498, 497, 473, 124, - /* 180 */ 394, 1176, 1177, 1178, 1176, 496, 119, 119, 119, 119, - /* 190 */ 118, 118, 117, 117, 117, 116, 441, 139, 540, 406, - /* 200 */ 121, 121, 121, 121, 114, 117, 117, 117, 116, 441, - /* 210 */ 541, 1532, 119, 119, 119, 119, 118, 118, 117, 117, - /* 220 */ 117, 116, 441, 122, 123, 113, 1200, 1200, 1035, 1038, - /* 230 */ 1028, 1028, 120, 120, 121, 121, 121, 121, 406, 320, - /* 240 */ 1176, 1177, 1178, 81, 342, 1590, 396, 80, 119, 119, - /* 250 */ 119, 119, 118, 118, 117, 117, 117, 116, 441, 1176, - /* 260 */ 211, 450, 122, 123, 113, 1200, 1200, 1035, 1038, 1028, - /* 270 */ 1028, 120, 120, 121, 121, 121, 121, 251, 450, 449, - /* 280 */ 273, 273, 119, 119, 119, 119, 118, 118, 117, 117, - /* 290 */ 117, 116, 441, 560, 1224, 1, 1, 569, 2, 1228, - /* 300 */ 317, 1176, 319, 1561, 305, 337, 140, 340, 406, 430, - /* 310 */ 469, 1533, 1197, 1308, 348, 1176, 1177, 1178, 168, 462, - /* 320 */ 330, 119, 119, 119, 119, 118, 118, 117, 117, 117, - /* 330 */ 116, 441, 122, 123, 113, 1200, 1200, 1035, 1038, 1028, - /* 340 */ 1028, 120, 120, 121, 121, 121, 121, 273, 273, 563, - /* 350 */ 83, 450, 416, 1564, 569, 2, 1228, 1176, 1177, 1178, - /* 360 */ 560, 305, 471, 140, 944, 995, 860, 563, 467, 1197, - /* 370 */ 1308, 13, 13, 137, 229, 118, 118, 117, 117, 117, - /* 380 */ 116, 441, 96, 318, 946, 504, 424, 361, 562, 71, - /* 390 */ 71, 119, 119, 119, 119, 118, 118, 117, 117, 117, - /* 400 */ 116, 441, 427, 205, 273, 273, 445, 1015, 259, 276, - /* 410 */ 356, 507, 351, 506, 246, 406, 959, 560, 328, 344, - /* 420 */ 347, 315, 860, 1006, 960, 126, 545, 1005, 313, 304, - /* 430 */ 554, 229, 538, 1539, 148, 544, 281, 6, 203, 122, - /* 440 */ 123, 113, 1200, 1200, 1035, 1038, 1028, 1028, 120, 120, - /* 450 */ 121, 121, 121, 121, 563, 217, 563, 12, 406, 1005, - /* 460 */ 1005, 1007, 502, 445, 119, 119, 119, 119, 118, 118, - /* 470 */ 117, 117, 117, 116, 441, 452, 71, 71, 70, 70, - /* 480 */ 944, 137, 122, 123, 113, 1200, 1200, 1035, 1038, 1028, - /* 490 */ 1028, 120, 120, 121, 121, 121, 121, 1530, 119, 119, - /* 500 */ 119, 119, 118, 118, 117, 117, 117, 116, 441, 403, - /* 510 */ 402, 241, 1176, 545, 501, 498, 497, 1468, 1143, 451, - /* 520 */ 267, 267, 513, 1540, 496, 142, 1176, 6, 406, 530, - /* 530 */ 194, 1143, 864, 560, 1143, 461, 182, 304, 554, 32, - /* 540 */ 379, 119, 119, 119, 119, 118, 118, 117, 117, 117, - /* 550 */ 116, 441, 122, 123, 113, 1200, 1200, 1035, 1038, 1028, - /* 560 */ 1028, 120, 120, 121, 121, 121, 121, 406, 1176, 1177, - /* 570 */ 1178, 857, 568, 1176, 1228, 925, 1176, 454, 361, 305, - /* 580 */ 189, 140, 1176, 1177, 1178, 519, 529, 404, 1308, 183, - /* 590 */ 1015, 122, 123, 113, 1200, 1200, 1035, 1038, 1028, 1028, - /* 600 */ 120, 120, 121, 121, 121, 121, 1006, 16, 16, 370, - /* 610 */ 1005, 119, 119, 119, 119, 118, 118, 117, 117, 117, - /* 620 */ 116, 441, 273, 273, 1537, 150, 1176, 98, 6, 1176, - /* 630 */ 1177, 1178, 1176, 1177, 1178, 560, 380, 406, 376, 438, - /* 640 */ 437, 1161, 1005, 1005, 1007, 1025, 1025, 1036, 1039, 229, - /* 650 */ 119, 119, 119, 119, 118, 118, 117, 117, 117, 116, - /* 660 */ 441, 122, 123, 113, 1200, 1200, 1035, 1038, 1028, 1028, - /* 670 */ 120, 120, 121, 121, 121, 121, 406, 1143, 1619, 392, - /* 680 */ 1016, 445, 1176, 1177, 1178, 1207, 525, 1207, 1530, 995, - /* 690 */ 1143, 304, 554, 1143, 5, 563, 543, 3, 361, 216, - /* 700 */ 122, 123, 113, 1200, 1200, 1035, 1038, 1028, 1028, 120, - /* 710 */ 120, 121, 121, 121, 121, 143, 563, 13, 13, 1029, - /* 720 */ 119, 119, 119, 119, 118, 118, 117, 117, 117, 116, - /* 730 */ 441, 1176, 426, 563, 1176, 563, 274, 274, 13, 13, - /* 740 */ 1078, 1176, 328, 457, 316, 147, 406, 211, 361, 560, - /* 750 */ 1000, 213, 511, 293, 477, 55, 55, 71, 71, 119, - /* 760 */ 119, 119, 119, 118, 118, 117, 117, 117, 116, 441, - /* 770 */ 122, 123, 113, 1200, 1200, 1035, 1038, 1028, 1028, 120, - /* 780 */ 120, 121, 121, 121, 121, 406, 455, 1176, 1177, 1178, - /* 790 */ 1176, 1177, 1178, 471, 526, 149, 404, 1176, 1177, 1178, - /* 800 */ 105, 270, 103, 563, 944, 563, 116, 441, 1530, 122, - /* 810 */ 123, 113, 1200, 1200, 1035, 1038, 1028, 1028, 120, 120, - /* 820 */ 121, 121, 121, 121, 945, 13, 13, 13, 13, 119, - /* 830 */ 119, 119, 119, 118, 118, 117, 117, 117, 116, 441, - /* 840 */ 191, 563, 192, 563, 416, 439, 439, 439, 1083, 1083, - /* 850 */ 485, 561, 285, 914, 914, 406, 462, 330, 1530, 830, - /* 860 */ 831, 832, 206, 71, 71, 71, 71, 286, 119, 119, - /* 870 */ 119, 119, 118, 118, 117, 117, 117, 116, 441, 122, - /* 880 */ 123, 113, 1200, 1200, 1035, 1038, 1028, 1028, 120, 120, - /* 890 */ 121, 121, 121, 121, 563, 217, 563, 1122, 1617, 406, - /* 900 */ 300, 1617, 301, 416, 1278, 1473, 244, 243, 242, 1249, - /* 910 */ 412, 556, 412, 282, 842, 279, 71, 71, 71, 71, - /* 920 */ 944, 1415, 1473, 1475, 101, 113, 1200, 1200, 1035, 1038, - /* 930 */ 1028, 1028, 120, 120, 121, 121, 121, 121, 119, 119, - /* 940 */ 119, 119, 118, 118, 117, 117, 117, 116, 441, 273, - /* 950 */ 273, 1099, 563, 436, 1143, 440, 563, 1122, 1618, 357, - /* 960 */ 1558, 1618, 560, 546, 488, 197, 1100, 1143, 378, 290, - /* 970 */ 1143, 1306, 284, 460, 71, 71, 1120, 405, 13, 13, - /* 980 */ 145, 1101, 119, 119, 119, 119, 118, 118, 117, 117, - /* 990 */ 117, 116, 441, 542, 104, 1473, 509, 273, 273, 294, - /* 1000 */ 1514, 294, 900, 273, 273, 273, 273, 563, 1503, 563, - /* 1010 */ 560, 545, 901, 464, 406, 1058, 560, 852, 560, 198, - /* 1020 */ 547, 1080, 920, 404, 1400, 1080, 146, 919, 38, 56, - /* 1030 */ 56, 15, 15, 563, 406, 12, 1120, 471, 122, 123, - /* 1040 */ 113, 1200, 1200, 1035, 1038, 1028, 1028, 120, 120, 121, - /* 1050 */ 121, 121, 121, 1460, 406, 43, 43, 483, 122, 123, - /* 1060 */ 113, 1200, 1200, 1035, 1038, 1028, 1028, 120, 120, 121, - /* 1070 */ 121, 121, 121, 563, 852, 9, 471, 251, 122, 111, - /* 1080 */ 113, 1200, 1200, 1035, 1038, 1028, 1028, 120, 120, 121, - /* 1090 */ 121, 121, 121, 563, 421, 57, 57, 119, 119, 119, - /* 1100 */ 119, 118, 118, 117, 117, 117, 116, 441, 1176, 493, - /* 1110 */ 563, 289, 1197, 478, 1516, 44, 44, 119, 119, 119, - /* 1120 */ 119, 118, 118, 117, 117, 117, 116, 441, 880, 563, - /* 1130 */ 536, 563, 58, 58, 488, 1414, 245, 119, 119, 119, - /* 1140 */ 119, 118, 118, 117, 117, 117, 116, 441, 563, 535, - /* 1150 */ 291, 59, 59, 60, 60, 438, 437, 406, 1154, 505, - /* 1160 */ 304, 554, 477, 1204, 1176, 1177, 1178, 881, 1206, 1197, - /* 1170 */ 61, 61, 1246, 357, 1558, 1538, 1205, 563, 1467, 6, - /* 1180 */ 1176, 488, 123, 113, 1200, 1200, 1035, 1038, 1028, 1028, - /* 1190 */ 120, 120, 121, 121, 121, 121, 1400, 1143, 410, 62, - /* 1200 */ 62, 1207, 1099, 1207, 411, 447, 273, 273, 537, 1154, - /* 1210 */ 1143, 108, 555, 1143, 4, 391, 1220, 1100, 1512, 560, - /* 1220 */ 347, 516, 428, 548, 308, 1307, 1536, 1077, 558, 1077, - /* 1230 */ 6, 488, 1101, 1400, 488, 309, 1176, 1177, 1178, 563, - /* 1240 */ 119, 119, 119, 119, 118, 118, 117, 117, 117, 116, - /* 1250 */ 441, 442, 278, 551, 563, 273, 273, 273, 273, 563, - /* 1260 */ 327, 45, 45, 552, 563, 528, 422, 563, 560, 1400, - /* 1270 */ 560, 108, 555, 137, 4, 1303, 46, 46, 335, 563, - /* 1280 */ 482, 47, 47, 477, 479, 307, 49, 49, 558, 50, - /* 1290 */ 50, 563, 1015, 563, 1221, 563, 1400, 563, 106, 106, - /* 1300 */ 8, 63, 63, 423, 563, 107, 312, 442, 565, 564, - /* 1310 */ 563, 442, 1005, 64, 64, 65, 65, 14, 14, 66, - /* 1320 */ 66, 391, 1121, 552, 1312, 1180, 128, 128, 563, 304, - /* 1330 */ 554, 563, 67, 67, 563, 359, 560, 532, 563, 484, - /* 1340 */ 563, 1196, 531, 222, 1005, 1005, 1007, 1008, 27, 522, - /* 1350 */ 52, 52, 1015, 68, 68, 563, 69, 69, 106, 106, - /* 1360 */ 53, 53, 156, 156, 563, 107, 434, 442, 565, 564, - /* 1370 */ 272, 215, 1005, 425, 563, 359, 563, 157, 157, 563, - /* 1380 */ 1535, 292, 1180, 98, 6, 1344, 76, 76, 1215, 475, - /* 1390 */ 413, 169, 226, 563, 245, 563, 54, 54, 72, 72, - /* 1400 */ 1221, 129, 129, 1343, 1005, 1005, 1007, 1008, 27, 1563, - /* 1410 */ 1165, 444, 456, 433, 277, 73, 73, 130, 130, 389, - /* 1420 */ 389, 388, 262, 386, 1165, 444, 839, 1519, 277, 108, - /* 1430 */ 555, 321, 4, 389, 389, 388, 262, 386, 563, 223, - /* 1440 */ 839, 311, 468, 84, 202, 523, 558, 1492, 303, 310, - /* 1450 */ 563, 110, 404, 223, 563, 311, 206, 30, 404, 277, - /* 1460 */ 131, 131, 411, 310, 389, 389, 388, 262, 386, 442, - /* 1470 */ 920, 839, 127, 127, 563, 919, 155, 155, 1491, 225, - /* 1480 */ 563, 552, 871, 563, 223, 476, 311, 161, 31, 563, - /* 1490 */ 135, 563, 480, 225, 310, 532, 154, 154, 332, 17, - /* 1500 */ 533, 161, 136, 136, 135, 134, 134, 224, 228, 355, - /* 1510 */ 1015, 132, 132, 133, 133, 1589, 106, 106, 889, 354, - /* 1520 */ 563, 224, 563, 107, 225, 442, 565, 564, 1117, 275, - /* 1530 */ 1005, 393, 161, 518, 563, 135, 108, 555, 417, 4, - /* 1540 */ 1340, 407, 75, 75, 77, 77, 304, 554, 867, 563, - /* 1550 */ 336, 563, 224, 558, 463, 407, 74, 74, 465, 1065, - /* 1560 */ 304, 554, 1005, 1005, 1007, 1008, 27, 962, 963, 543, - /* 1570 */ 448, 42, 42, 48, 48, 326, 442, 325, 98, 997, - /* 1580 */ 470, 287, 250, 250, 448, 1009, 407, 472, 552, 339, - /* 1590 */ 250, 304, 554, 879, 878, 331, 108, 555, 98, 4, - /* 1600 */ 1277, 494, 532, 345, 247, 867, 98, 531, 341, 886, - /* 1610 */ 887, 1126, 1076, 558, 1076, 448, 1065, 1015, 1061, 953, - /* 1620 */ 343, 247, 250, 106, 106, 1291, 917, 1276, 850, 110, - /* 1630 */ 107, 144, 442, 565, 564, 918, 442, 1005, 110, 1275, - /* 1640 */ 350, 360, 1009, 1331, 1352, 299, 1399, 1577, 552, 1327, - /* 1650 */ 1552, 550, 1338, 549, 1405, 1256, 1248, 1237, 1236, 1238, - /* 1660 */ 1571, 489, 265, 200, 1324, 363, 365, 367, 11, 1005, - /* 1670 */ 1005, 1007, 1008, 27, 390, 221, 1386, 1015, 280, 1391, - /* 1680 */ 1381, 208, 323, 106, 106, 924, 1374, 453, 283, 324, - /* 1690 */ 107, 474, 442, 565, 564, 1390, 499, 1005, 212, 288, - /* 1700 */ 1274, 397, 353, 108, 555, 195, 4, 1464, 369, 1463, - /* 1710 */ 1574, 1215, 1212, 329, 553, 171, 207, 383, 1511, 196, - /* 1720 */ 558, 254, 1509, 415, 100, 555, 83, 4, 204, 1005, - /* 1730 */ 1005, 1007, 1008, 27, 219, 79, 82, 1469, 180, 166, - /* 1740 */ 173, 558, 458, 442, 175, 176, 177, 178, 35, 1387, - /* 1750 */ 492, 459, 231, 1395, 96, 552, 1393, 1392, 395, 184, - /* 1760 */ 481, 466, 36, 235, 442, 89, 398, 266, 487, 1480, - /* 1770 */ 1458, 237, 188, 338, 508, 429, 552, 490, 400, 238, - /* 1780 */ 334, 1239, 239, 1294, 1015, 1293, 1292, 1285, 91, 871, - /* 1790 */ 106, 106, 213, 431, 1588, 432, 524, 107, 517, 442, - /* 1800 */ 565, 564, 401, 1264, 1005, 1015, 1263, 1587, 352, 1262, - /* 1810 */ 1557, 106, 106, 1586, 1284, 297, 298, 358, 107, 1335, - /* 1820 */ 442, 565, 564, 95, 362, 1005, 253, 252, 435, 125, - /* 1830 */ 543, 10, 1444, 1543, 377, 1542, 1005, 1005, 1007, 1008, - /* 1840 */ 27, 302, 102, 97, 527, 1336, 260, 1317, 364, 1245, - /* 1850 */ 1334, 34, 566, 1171, 366, 381, 375, 1005, 1005, 1007, - /* 1860 */ 1008, 27, 1333, 1359, 368, 1316, 199, 382, 261, 263, - /* 1870 */ 264, 1358, 158, 1496, 141, 1497, 1495, 567, 1234, 1229, - /* 1880 */ 1494, 295, 159, 209, 210, 78, 826, 443, 201, 306, - /* 1890 */ 220, 1075, 138, 1073, 160, 314, 162, 172, 1196, 174, - /* 1900 */ 903, 227, 230, 322, 1089, 179, 163, 164, 418, 85, - /* 1910 */ 420, 181, 170, 408, 409, 86, 87, 165, 88, 1092, - /* 1920 */ 232, 233, 1088, 151, 18, 234, 1081, 250, 333, 185, - /* 1930 */ 1209, 486, 236, 186, 37, 841, 491, 354, 240, 346, - /* 1940 */ 503, 187, 90, 167, 19, 495, 20, 869, 500, 349, - /* 1950 */ 92, 882, 296, 152, 93, 510, 1127, 1159, 153, 1041, - /* 1960 */ 214, 1128, 39, 94, 269, 271, 952, 190, 947, 110, - /* 1970 */ 1149, 1145, 1153, 249, 1133, 1147, 7, 33, 21, 193, - /* 1980 */ 22, 23, 24, 25, 1152, 539, 98, 1056, 26, 1042, - /* 1990 */ 1040, 1044, 1098, 1045, 1097, 256, 255, 28, 40, 387, - /* 2000 */ 1010, 851, 109, 29, 1167, 559, 384, 257, 913, 258, - /* 2010 */ 1166, 1579, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1578, + /* 0 */ 568, 208, 568, 118, 115, 229, 568, 118, 115, 229, + /* 10 */ 568, 1314, 377, 1293, 408, 562, 562, 562, 568, 409, + /* 20 */ 378, 1314, 1276, 41, 41, 41, 41, 208, 1526, 71, + /* 30 */ 71, 971, 419, 41, 41, 491, 303, 279, 303, 972, + /* 40 */ 397, 71, 71, 125, 126, 80, 1217, 1217, 1050, 1053, + /* 50 */ 1040, 1040, 123, 123, 124, 124, 124, 124, 476, 409, + /* 60 */ 1241, 1, 1, 575, 2, 1245, 550, 118, 115, 229, + /* 70 */ 317, 480, 146, 480, 524, 118, 115, 229, 529, 1327, + /* 80 */ 417, 523, 142, 125, 126, 80, 1217, 1217, 1050, 1053, + /* 90 */ 1040, 1040, 123, 123, 124, 124, 124, 124, 118, 115, + /* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120, + /* 110 */ 120, 119, 116, 444, 284, 284, 284, 284, 442, 442, + /* 120 */ 442, 1567, 376, 1569, 1192, 375, 1163, 565, 1163, 565, + /* 130 */ 409, 1567, 537, 259, 226, 444, 101, 145, 449, 316, + /* 140 */ 559, 240, 122, 122, 122, 122, 121, 121, 120, 120, + /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1217, 1217, 1050, + /* 160 */ 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, 142, + /* 170 */ 294, 1192, 339, 448, 120, 120, 120, 119, 116, 444, + /* 180 */ 127, 1192, 1193, 1194, 148, 441, 440, 568, 119, 116, + /* 190 */ 444, 124, 124, 124, 124, 117, 122, 122, 122, 122, + /* 200 */ 121, 121, 120, 120, 120, 119, 116, 444, 454, 113, + /* 210 */ 13, 13, 546, 122, 122, 122, 122, 121, 121, 120, + /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1192, 1193, + /* 230 */ 1194, 149, 1224, 409, 1224, 124, 124, 124, 124, 122, + /* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, + /* 250 */ 444, 465, 342, 1037, 1037, 1051, 1054, 125, 126, 80, + /* 260 */ 1217, 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, + /* 270 */ 124, 124, 1279, 522, 222, 1192, 568, 409, 224, 514, + /* 280 */ 175, 82, 83, 122, 122, 122, 122, 121, 121, 120, + /* 290 */ 120, 120, 119, 116, 444, 1007, 16, 16, 1192, 133, + /* 300 */ 133, 125, 126, 80, 1217, 1217, 1050, 1053, 1040, 1040, + /* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122, + /* 320 */ 121, 121, 120, 120, 120, 119, 116, 444, 1041, 546, + /* 330 */ 1192, 373, 1192, 1193, 1194, 252, 1434, 399, 504, 501, + /* 340 */ 500, 111, 560, 566, 4, 926, 926, 433, 499, 340, + /* 350 */ 460, 328, 360, 394, 1237, 1192, 1193, 1194, 563, 568, + /* 360 */ 122, 122, 122, 122, 121, 121, 120, 120, 120, 119, + /* 370 */ 116, 444, 284, 284, 369, 1580, 1607, 441, 440, 154, + /* 380 */ 409, 445, 71, 71, 1286, 565, 1221, 1192, 1193, 1194, + /* 390 */ 85, 1223, 271, 557, 543, 515, 1561, 568, 98, 1222, + /* 400 */ 6, 1278, 472, 142, 125, 126, 80, 1217, 1217, 1050, + /* 410 */ 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, 550, + /* 420 */ 13, 13, 1027, 507, 1224, 1192, 1224, 549, 109, 109, + /* 430 */ 222, 568, 1238, 175, 568, 427, 110, 197, 445, 570, + /* 440 */ 569, 430, 1552, 1017, 325, 551, 1192, 270, 287, 368, + /* 450 */ 510, 363, 509, 257, 71, 71, 543, 71, 71, 359, + /* 460 */ 316, 559, 1613, 122, 122, 122, 122, 121, 121, 120, + /* 470 */ 120, 120, 119, 116, 444, 1017, 1017, 1019, 1020, 27, + /* 480 */ 284, 284, 1192, 1193, 1194, 1158, 568, 1612, 409, 901, + /* 490 */ 190, 550, 356, 565, 550, 937, 533, 517, 1158, 516, + /* 500 */ 413, 1158, 552, 1192, 1193, 1194, 568, 544, 1554, 51, + /* 510 */ 51, 214, 125, 126, 80, 1217, 1217, 1050, 1053, 1040, + /* 520 */ 1040, 123, 123, 124, 124, 124, 124, 1192, 474, 135, + /* 530 */ 135, 409, 284, 284, 1490, 505, 121, 121, 120, 120, + /* 540 */ 120, 119, 116, 444, 1007, 565, 518, 217, 541, 1561, + /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1217, 1217, + /* 560 */ 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, + /* 570 */ 1555, 122, 122, 122, 122, 121, 121, 120, 120, 120, + /* 580 */ 119, 116, 444, 485, 1192, 1193, 1194, 482, 281, 1267, + /* 590 */ 957, 252, 1192, 373, 504, 501, 500, 1192, 340, 571, + /* 600 */ 1192, 571, 409, 292, 499, 957, 876, 191, 480, 316, + /* 610 */ 559, 384, 290, 380, 122, 122, 122, 122, 121, 121, + /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1217, + /* 630 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, + /* 640 */ 124, 409, 394, 1136, 1192, 869, 100, 284, 284, 1192, + /* 650 */ 1193, 1194, 373, 1093, 1192, 1193, 1194, 1192, 1193, 1194, + /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1217, 1217, + /* 670 */ 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, + /* 680 */ 1433, 959, 568, 228, 958, 122, 122, 122, 122, 121, + /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1158, 228, 1192, + /* 700 */ 157, 1192, 1193, 1194, 1553, 13, 13, 301, 957, 1232, + /* 710 */ 1158, 153, 409, 1158, 373, 1583, 1176, 5, 369, 1580, + /* 720 */ 429, 1238, 3, 957, 122, 122, 122, 122, 121, 121, + /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1217, + /* 740 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, + /* 750 */ 124, 409, 208, 567, 1192, 1028, 1192, 1193, 1194, 1192, + /* 760 */ 388, 852, 155, 1552, 286, 402, 1098, 1098, 488, 568, + /* 770 */ 465, 342, 1319, 1319, 1552, 125, 126, 80, 1217, 1217, + /* 780 */ 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, + /* 790 */ 129, 568, 13, 13, 374, 122, 122, 122, 122, 121, + /* 800 */ 121, 120, 120, 120, 119, 116, 444, 302, 568, 453, + /* 810 */ 528, 1192, 1193, 1194, 13, 13, 1192, 1193, 1194, 1297, + /* 820 */ 463, 1267, 409, 1317, 1317, 1552, 1012, 453, 452, 200, + /* 830 */ 299, 71, 71, 1265, 122, 122, 122, 122, 121, 121, + /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1217, + /* 850 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, + /* 860 */ 124, 409, 227, 1073, 1158, 284, 284, 419, 312, 278, + /* 870 */ 278, 285, 285, 1419, 406, 405, 382, 1158, 565, 568, + /* 880 */ 1158, 1196, 565, 1600, 565, 125, 126, 80, 1217, 1217, + /* 890 */ 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, + /* 900 */ 453, 1482, 13, 13, 1536, 122, 122, 122, 122, 121, + /* 910 */ 121, 120, 120, 120, 119, 116, 444, 201, 568, 354, + /* 920 */ 1586, 575, 2, 1245, 840, 841, 842, 1562, 317, 1212, + /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1327, 9, 1196, + /* 940 */ 262, 71, 71, 424, 122, 122, 122, 122, 121, 121, + /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1217, + /* 960 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, + /* 970 */ 124, 568, 284, 284, 568, 1213, 409, 574, 313, 1245, + /* 980 */ 349, 1296, 352, 419, 317, 565, 146, 491, 525, 1643, + /* 990 */ 395, 371, 491, 1327, 70, 70, 1295, 71, 71, 240, + /* 1000 */ 1325, 104, 80, 1217, 1217, 1050, 1053, 1040, 1040, 123, + /* 1010 */ 123, 124, 124, 124, 124, 122, 122, 122, 122, 121, + /* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1114, 284, 284, + /* 1030 */ 428, 448, 1525, 1213, 439, 284, 284, 1489, 1352, 311, + /* 1040 */ 474, 565, 1115, 971, 491, 491, 217, 1263, 565, 1538, + /* 1050 */ 568, 972, 207, 568, 1027, 240, 383, 1116, 519, 122, + /* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, + /* 1070 */ 444, 1018, 107, 71, 71, 1017, 13, 13, 912, 568, + /* 1080 */ 1495, 568, 284, 284, 97, 526, 491, 448, 913, 1326, + /* 1090 */ 1322, 545, 409, 284, 284, 565, 151, 209, 1495, 1497, + /* 1100 */ 262, 450, 55, 55, 56, 56, 565, 1017, 1017, 1019, + /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1217, + /* 1120 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, + /* 1130 */ 124, 347, 409, 864, 1534, 1213, 125, 126, 80, 1217, + /* 1140 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, + /* 1150 */ 124, 1137, 1641, 474, 1641, 371, 125, 114, 80, 1217, + /* 1160 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, + /* 1170 */ 124, 1495, 329, 474, 331, 122, 122, 122, 122, 121, + /* 1180 */ 121, 120, 120, 120, 119, 116, 444, 203, 1419, 568, + /* 1190 */ 1294, 864, 464, 1213, 436, 122, 122, 122, 122, 121, + /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1137, 1642, + /* 1210 */ 539, 1642, 15, 15, 892, 122, 122, 122, 122, 121, + /* 1220 */ 121, 120, 120, 120, 119, 116, 444, 568, 298, 538, + /* 1230 */ 1135, 1419, 1559, 1560, 1331, 409, 6, 6, 1169, 1268, + /* 1240 */ 415, 320, 284, 284, 1419, 508, 565, 525, 300, 457, + /* 1250 */ 43, 43, 568, 893, 12, 565, 330, 478, 425, 407, + /* 1260 */ 126, 80, 1217, 1217, 1050, 1053, 1040, 1040, 123, 123, + /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1192, 1419, + /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1135, 1558, 849, + /* 1290 */ 1169, 407, 6, 568, 321, 1158, 470, 44, 44, 1557, + /* 1300 */ 1114, 426, 234, 6, 323, 256, 540, 256, 1158, 431, + /* 1310 */ 568, 1158, 322, 17, 487, 1115, 58, 58, 122, 122, + /* 1320 */ 122, 122, 121, 121, 120, 120, 120, 119, 116, 444, + /* 1330 */ 1116, 216, 481, 59, 59, 1192, 1193, 1194, 111, 560, + /* 1340 */ 324, 4, 236, 456, 526, 568, 237, 456, 568, 437, + /* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1095, + /* 1360 */ 568, 293, 568, 1095, 531, 568, 872, 8, 60, 60, + /* 1370 */ 235, 61, 61, 568, 414, 568, 414, 568, 445, 62, + /* 1380 */ 62, 45, 45, 46, 46, 47, 47, 199, 49, 49, + /* 1390 */ 557, 568, 359, 568, 100, 486, 50, 50, 63, 63, + /* 1400 */ 64, 64, 561, 415, 535, 410, 568, 1027, 568, 534, + /* 1410 */ 316, 559, 316, 559, 65, 65, 14, 14, 568, 1027, + /* 1420 */ 568, 512, 932, 872, 1018, 109, 109, 931, 1017, 66, + /* 1430 */ 66, 131, 131, 110, 451, 445, 570, 569, 416, 177, + /* 1440 */ 1017, 132, 132, 67, 67, 568, 467, 568, 932, 471, + /* 1450 */ 1364, 283, 226, 931, 315, 1363, 407, 568, 459, 407, + /* 1460 */ 1017, 1017, 1019, 239, 407, 86, 213, 1350, 52, 52, + /* 1470 */ 68, 68, 1017, 1017, 1019, 1020, 27, 1585, 1180, 447, + /* 1480 */ 69, 69, 288, 97, 108, 1541, 106, 392, 392, 391, + /* 1490 */ 273, 389, 568, 879, 849, 883, 568, 111, 560, 466, + /* 1500 */ 4, 568, 152, 30, 38, 568, 1132, 234, 396, 323, + /* 1510 */ 111, 560, 527, 4, 563, 53, 53, 322, 568, 163, + /* 1520 */ 163, 568, 337, 468, 164, 164, 333, 563, 76, 76, + /* 1530 */ 568, 289, 1514, 568, 31, 1513, 568, 445, 338, 483, + /* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1080, 557, + /* 1550 */ 445, 879, 1360, 134, 134, 168, 73, 73, 141, 161, + /* 1560 */ 161, 1574, 557, 535, 568, 319, 568, 348, 536, 1009, + /* 1570 */ 473, 261, 261, 891, 890, 235, 535, 568, 1027, 568, + /* 1580 */ 475, 534, 261, 367, 109, 109, 521, 136, 136, 130, + /* 1590 */ 130, 1027, 110, 366, 445, 570, 569, 109, 109, 1017, + /* 1600 */ 162, 162, 156, 156, 568, 110, 1080, 445, 570, 569, + /* 1610 */ 410, 351, 1017, 568, 353, 316, 559, 568, 343, 568, + /* 1620 */ 100, 497, 357, 258, 100, 898, 899, 140, 140, 355, + /* 1630 */ 1310, 1017, 1017, 1019, 1020, 27, 139, 139, 362, 451, + /* 1640 */ 137, 137, 138, 138, 1017, 1017, 1019, 1020, 27, 1180, + /* 1650 */ 447, 568, 372, 288, 111, 560, 1021, 4, 392, 392, + /* 1660 */ 391, 273, 389, 568, 1141, 849, 568, 1076, 568, 258, + /* 1670 */ 492, 563, 568, 211, 75, 75, 555, 962, 234, 261, + /* 1680 */ 323, 111, 560, 929, 4, 113, 77, 77, 322, 74, + /* 1690 */ 74, 42, 42, 1373, 445, 48, 48, 1418, 563, 974, + /* 1700 */ 975, 1092, 1091, 1092, 1091, 862, 557, 150, 930, 1346, + /* 1710 */ 113, 1358, 554, 1424, 1021, 1275, 1266, 1254, 236, 1253, + /* 1720 */ 1255, 445, 1593, 1343, 308, 276, 168, 309, 11, 141, + /* 1730 */ 393, 310, 232, 557, 1405, 1027, 335, 291, 1400, 219, + /* 1740 */ 336, 109, 109, 936, 297, 1410, 235, 341, 477, 110, + /* 1750 */ 502, 445, 570, 569, 1393, 1409, 1017, 400, 1293, 365, + /* 1760 */ 223, 1486, 1027, 1485, 1355, 1356, 1354, 1353, 109, 109, + /* 1770 */ 204, 1596, 1232, 558, 265, 218, 110, 205, 445, 570, + /* 1780 */ 569, 410, 387, 1017, 1533, 179, 316, 559, 1017, 1017, + /* 1790 */ 1019, 1020, 27, 230, 1531, 1229, 79, 560, 85, 4, + /* 1800 */ 418, 215, 548, 81, 84, 188, 1406, 173, 181, 461, + /* 1810 */ 451, 35, 462, 563, 183, 1017, 1017, 1019, 1020, 27, + /* 1820 */ 184, 1491, 185, 186, 495, 242, 98, 398, 1412, 36, + /* 1830 */ 1411, 484, 91, 469, 401, 1414, 445, 192, 1480, 246, + /* 1840 */ 1502, 490, 346, 277, 248, 196, 493, 511, 557, 350, + /* 1850 */ 1256, 249, 250, 403, 1313, 1312, 111, 560, 432, 4, + /* 1860 */ 1311, 1304, 93, 1611, 883, 1610, 224, 404, 434, 520, + /* 1870 */ 263, 435, 1579, 563, 1283, 1282, 364, 1027, 306, 1281, + /* 1880 */ 264, 1609, 1565, 109, 109, 370, 1303, 307, 1564, 438, + /* 1890 */ 128, 110, 1378, 445, 570, 569, 445, 546, 1017, 10, + /* 1900 */ 1466, 105, 381, 1377, 34, 572, 99, 1336, 557, 314, + /* 1910 */ 1186, 530, 272, 274, 379, 210, 1335, 547, 385, 386, + /* 1920 */ 275, 573, 1251, 1246, 411, 412, 1518, 165, 178, 1519, + /* 1930 */ 1017, 1017, 1019, 1020, 27, 1517, 1516, 1027, 78, 147, + /* 1940 */ 166, 220, 221, 109, 109, 836, 304, 167, 446, 212, + /* 1950 */ 318, 110, 231, 445, 570, 569, 144, 1090, 1017, 1088, + /* 1960 */ 326, 180, 169, 1212, 182, 334, 238, 915, 241, 1104, + /* 1970 */ 187, 170, 171, 421, 87, 88, 423, 189, 89, 90, + /* 1980 */ 172, 1107, 243, 1103, 244, 158, 18, 245, 345, 247, + /* 1990 */ 1017, 1017, 1019, 1020, 27, 261, 1096, 193, 1226, 489, + /* 2000 */ 194, 37, 366, 851, 494, 251, 195, 506, 92, 19, + /* 2010 */ 498, 358, 20, 503, 881, 361, 94, 894, 305, 159, + /* 2020 */ 513, 39, 95, 1174, 160, 1056, 966, 1143, 96, 174, + /* 2030 */ 1142, 225, 280, 282, 198, 960, 113, 1164, 1160, 260, + /* 2040 */ 21, 22, 23, 1162, 1168, 1167, 1148, 24, 33, 25, + /* 2050 */ 202, 542, 26, 100, 1071, 102, 1057, 103, 7, 1055, + /* 2060 */ 1059, 1113, 1060, 1112, 266, 267, 28, 40, 390, 1022, + /* 2070 */ 863, 112, 29, 564, 1182, 1181, 268, 176, 143, 925, + /* 2080 */ 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, + /* 2090 */ 1242, 1242, 1242, 1242, 269, 1602, 1242, 1601, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 191, 220, 191, 222, 191, 191, 271, 272, 273, 216, - /* 10 */ 191, 230, 216, 191, 191, 191, 271, 272, 273, 19, - /* 20 */ 232, 233, 213, 214, 213, 214, 202, 292, 202, 232, - /* 30 */ 233, 31, 213, 214, 213, 213, 214, 213, 214, 39, - /* 40 */ 207, 208, 209, 43, 44, 45, 46, 47, 48, 49, - /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 235, 19, - /* 60 */ 236, 237, 236, 237, 271, 272, 273, 271, 272, 273, - /* 70 */ 191, 210, 250, 249, 250, 249, 213, 191, 199, 253, - /* 80 */ 254, 259, 203, 43, 44, 45, 46, 47, 48, 49, - /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 191, 213, - /* 100 */ 214, 213, 102, 103, 104, 105, 106, 107, 108, 109, - /* 110 */ 110, 111, 112, 59, 228, 301, 293, 81, 305, 306, - /* 120 */ 311, 312, 311, 310, 313, 59, 86, 212, 88, 19, - /* 130 */ 311, 312, 271, 272, 273, 220, 26, 112, 54, 55, - /* 140 */ 56, 57, 102, 103, 104, 105, 106, 107, 108, 109, - /* 150 */ 110, 111, 112, 43, 44, 45, 46, 47, 48, 49, - /* 160 */ 50, 51, 52, 53, 54, 55, 56, 57, 191, 115, - /* 170 */ 116, 117, 118, 137, 138, 121, 122, 123, 191, 69, - /* 180 */ 203, 115, 116, 117, 59, 131, 102, 103, 104, 105, - /* 190 */ 106, 107, 108, 109, 110, 111, 112, 72, 191, 19, - /* 200 */ 54, 55, 56, 57, 58, 108, 109, 110, 111, 112, - /* 210 */ 303, 304, 102, 103, 104, 105, 106, 107, 108, 109, - /* 220 */ 110, 111, 112, 43, 44, 45, 46, 47, 48, 49, - /* 230 */ 50, 51, 52, 53, 54, 55, 56, 57, 19, 16, - /* 240 */ 115, 116, 117, 24, 16, 227, 202, 67, 102, 103, - /* 250 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 59, - /* 260 */ 26, 191, 43, 44, 45, 46, 47, 48, 49, 50, - /* 270 */ 51, 52, 53, 54, 55, 56, 57, 24, 208, 209, - /* 280 */ 236, 237, 102, 103, 104, 105, 106, 107, 108, 109, - /* 290 */ 110, 111, 112, 249, 183, 184, 185, 186, 187, 188, - /* 300 */ 77, 59, 79, 191, 193, 77, 195, 79, 19, 19, - /* 310 */ 266, 304, 59, 202, 24, 115, 116, 117, 191, 127, - /* 320 */ 128, 102, 103, 104, 105, 106, 107, 108, 109, 110, - /* 330 */ 111, 112, 43, 44, 45, 46, 47, 48, 49, 50, - /* 340 */ 51, 52, 53, 54, 55, 56, 57, 236, 237, 191, - /* 350 */ 150, 281, 191, 185, 186, 187, 188, 115, 116, 117, - /* 360 */ 249, 193, 191, 195, 26, 73, 59, 191, 114, 116, - /* 370 */ 202, 213, 214, 81, 263, 106, 107, 108, 109, 110, - /* 380 */ 111, 112, 148, 160, 142, 95, 228, 191, 191, 213, - /* 390 */ 214, 102, 103, 104, 105, 106, 107, 108, 109, 110, - /* 400 */ 111, 112, 112, 149, 236, 237, 295, 100, 118, 119, - /* 410 */ 120, 121, 122, 123, 124, 19, 31, 249, 126, 23, - /* 420 */ 130, 260, 115, 116, 39, 22, 250, 120, 191, 137, - /* 430 */ 138, 263, 305, 306, 238, 259, 265, 310, 149, 43, - /* 440 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 450 */ 54, 55, 56, 57, 191, 117, 191, 210, 19, 152, - /* 460 */ 153, 154, 23, 295, 102, 103, 104, 105, 106, 107, - /* 470 */ 108, 109, 110, 111, 112, 266, 213, 214, 213, 214, - /* 480 */ 142, 81, 43, 44, 45, 46, 47, 48, 49, 50, - /* 490 */ 51, 52, 53, 54, 55, 56, 57, 301, 102, 103, - /* 500 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 106, - /* 510 */ 107, 118, 59, 250, 121, 122, 123, 280, 76, 119, - /* 520 */ 236, 237, 259, 306, 131, 72, 59, 310, 19, 87, - /* 530 */ 283, 89, 23, 249, 92, 288, 22, 137, 138, 22, - /* 540 */ 275, 102, 103, 104, 105, 106, 107, 108, 109, 110, - /* 550 */ 111, 112, 43, 44, 45, 46, 47, 48, 49, 50, - /* 560 */ 51, 52, 53, 54, 55, 56, 57, 19, 115, 116, - /* 570 */ 117, 23, 186, 59, 188, 108, 59, 241, 191, 193, - /* 580 */ 26, 195, 115, 116, 117, 191, 144, 251, 202, 22, - /* 590 */ 100, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 600 */ 52, 53, 54, 55, 56, 57, 116, 213, 214, 191, - /* 610 */ 120, 102, 103, 104, 105, 106, 107, 108, 109, 110, - /* 620 */ 111, 112, 236, 237, 306, 238, 59, 26, 310, 115, - /* 630 */ 116, 117, 115, 116, 117, 249, 246, 19, 248, 106, - /* 640 */ 107, 23, 152, 153, 154, 46, 47, 48, 49, 263, - /* 650 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 660 */ 112, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 670 */ 52, 53, 54, 55, 56, 57, 19, 76, 298, 299, - /* 680 */ 23, 295, 115, 116, 117, 152, 191, 154, 301, 73, - /* 690 */ 89, 137, 138, 92, 22, 191, 144, 22, 191, 191, - /* 700 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - /* 710 */ 53, 54, 55, 56, 57, 163, 191, 213, 214, 120, - /* 720 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 730 */ 112, 59, 228, 191, 59, 191, 236, 237, 213, 214, - /* 740 */ 11, 59, 126, 127, 128, 238, 19, 26, 191, 249, - /* 750 */ 23, 164, 165, 228, 191, 213, 214, 213, 214, 102, - /* 760 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 770 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - /* 780 */ 53, 54, 55, 56, 57, 19, 241, 115, 116, 117, - /* 790 */ 115, 116, 117, 191, 250, 238, 251, 115, 116, 117, - /* 800 */ 157, 23, 159, 191, 26, 191, 111, 112, 301, 43, - /* 810 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 820 */ 54, 55, 56, 57, 142, 213, 214, 213, 214, 102, - /* 830 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 840 */ 228, 191, 228, 191, 191, 207, 208, 209, 126, 127, - /* 850 */ 128, 133, 289, 135, 136, 19, 127, 128, 301, 7, - /* 860 */ 8, 9, 141, 213, 214, 213, 214, 265, 102, 103, - /* 870 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 43, - /* 880 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 890 */ 54, 55, 56, 57, 191, 117, 191, 22, 23, 19, - /* 900 */ 250, 26, 250, 191, 223, 191, 126, 127, 128, 205, - /* 910 */ 206, 205, 206, 260, 21, 202, 213, 214, 213, 214, - /* 920 */ 142, 270, 208, 209, 158, 45, 46, 47, 48, 49, - /* 930 */ 50, 51, 52, 53, 54, 55, 56, 57, 102, 103, - /* 940 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 236, - /* 950 */ 237, 12, 191, 250, 76, 250, 191, 22, 23, 308, - /* 960 */ 309, 26, 249, 202, 191, 191, 27, 89, 191, 202, - /* 970 */ 92, 202, 260, 80, 213, 214, 101, 203, 213, 214, - /* 980 */ 22, 42, 102, 103, 104, 105, 106, 107, 108, 109, - /* 990 */ 110, 111, 112, 228, 158, 281, 108, 236, 237, 225, - /* 1000 */ 191, 227, 63, 236, 237, 236, 237, 191, 235, 191, - /* 1010 */ 249, 250, 73, 241, 19, 122, 249, 59, 249, 24, - /* 1020 */ 259, 29, 134, 251, 191, 33, 22, 139, 24, 213, - /* 1030 */ 214, 213, 214, 191, 19, 210, 101, 191, 43, 44, - /* 1040 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - /* 1050 */ 55, 56, 57, 160, 19, 213, 214, 65, 43, 44, - /* 1060 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - /* 1070 */ 55, 56, 57, 191, 116, 22, 191, 24, 43, 44, - /* 1080 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - /* 1090 */ 55, 56, 57, 191, 261, 213, 214, 102, 103, 104, - /* 1100 */ 105, 106, 107, 108, 109, 110, 111, 112, 59, 19, - /* 1110 */ 191, 265, 59, 288, 191, 213, 214, 102, 103, 104, - /* 1120 */ 105, 106, 107, 108, 109, 110, 111, 112, 35, 191, - /* 1130 */ 66, 191, 213, 214, 191, 270, 46, 102, 103, 104, - /* 1140 */ 105, 106, 107, 108, 109, 110, 111, 112, 191, 85, - /* 1150 */ 265, 213, 214, 213, 214, 106, 107, 19, 94, 66, - /* 1160 */ 137, 138, 191, 114, 115, 116, 117, 74, 119, 116, - /* 1170 */ 213, 214, 202, 308, 309, 306, 127, 191, 235, 310, - /* 1180 */ 59, 191, 44, 45, 46, 47, 48, 49, 50, 51, - /* 1190 */ 52, 53, 54, 55, 56, 57, 191, 76, 196, 213, - /* 1200 */ 214, 152, 12, 154, 114, 191, 236, 237, 87, 145, - /* 1210 */ 89, 19, 20, 92, 22, 22, 23, 27, 191, 249, - /* 1220 */ 130, 202, 129, 202, 191, 235, 306, 152, 36, 154, - /* 1230 */ 310, 191, 42, 191, 191, 191, 115, 116, 117, 191, - /* 1240 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 1250 */ 112, 59, 99, 63, 191, 236, 237, 236, 237, 191, - /* 1260 */ 289, 213, 214, 71, 191, 144, 261, 191, 249, 191, - /* 1270 */ 249, 19, 20, 81, 22, 235, 213, 214, 235, 191, - /* 1280 */ 278, 213, 214, 191, 282, 132, 213, 214, 36, 213, - /* 1290 */ 214, 191, 100, 191, 101, 191, 191, 191, 106, 107, - /* 1300 */ 48, 213, 214, 261, 191, 113, 191, 115, 116, 117, - /* 1310 */ 191, 59, 120, 213, 214, 213, 214, 213, 214, 213, - /* 1320 */ 214, 22, 23, 71, 237, 59, 213, 214, 191, 137, - /* 1330 */ 138, 191, 213, 214, 191, 191, 249, 85, 191, 261, - /* 1340 */ 191, 26, 90, 15, 152, 153, 154, 155, 156, 19, - /* 1350 */ 213, 214, 100, 213, 214, 191, 213, 214, 106, 107, - /* 1360 */ 213, 214, 213, 214, 191, 113, 261, 115, 116, 117, - /* 1370 */ 253, 254, 120, 229, 191, 191, 191, 213, 214, 191, - /* 1380 */ 306, 289, 116, 26, 310, 191, 213, 214, 60, 19, - /* 1390 */ 296, 297, 24, 191, 46, 191, 213, 214, 213, 214, - /* 1400 */ 101, 213, 214, 191, 152, 153, 154, 155, 156, 0, - /* 1410 */ 1, 2, 191, 229, 5, 213, 214, 213, 214, 10, - /* 1420 */ 11, 12, 13, 14, 1, 2, 17, 191, 5, 19, - /* 1430 */ 20, 191, 22, 10, 11, 12, 13, 14, 191, 30, - /* 1440 */ 17, 32, 241, 148, 149, 115, 36, 191, 241, 40, - /* 1450 */ 191, 26, 251, 30, 191, 32, 141, 22, 251, 5, - /* 1460 */ 213, 214, 114, 40, 10, 11, 12, 13, 14, 59, - /* 1470 */ 134, 17, 213, 214, 191, 139, 213, 214, 191, 70, - /* 1480 */ 191, 71, 125, 191, 30, 115, 32, 78, 53, 191, - /* 1490 */ 81, 191, 191, 70, 40, 85, 213, 214, 191, 22, - /* 1500 */ 90, 78, 213, 214, 81, 213, 214, 98, 140, 120, - /* 1510 */ 100, 213, 214, 213, 214, 23, 106, 107, 26, 130, - /* 1520 */ 191, 98, 191, 113, 70, 115, 116, 117, 23, 22, - /* 1530 */ 120, 26, 78, 19, 191, 81, 19, 20, 61, 22, - /* 1540 */ 191, 132, 213, 214, 213, 214, 137, 138, 59, 191, - /* 1550 */ 191, 191, 98, 36, 128, 132, 213, 214, 128, 59, - /* 1560 */ 137, 138, 152, 153, 154, 155, 156, 83, 84, 144, - /* 1570 */ 161, 213, 214, 213, 214, 23, 59, 151, 26, 23, - /* 1580 */ 23, 151, 26, 26, 161, 59, 132, 23, 71, 191, - /* 1590 */ 26, 137, 138, 119, 120, 23, 19, 20, 26, 22, - /* 1600 */ 223, 23, 85, 23, 26, 116, 26, 90, 191, 7, - /* 1610 */ 8, 97, 152, 36, 154, 161, 116, 100, 23, 23, - /* 1620 */ 191, 26, 26, 106, 107, 191, 23, 223, 23, 26, - /* 1630 */ 113, 26, 115, 116, 117, 23, 59, 120, 26, 191, - /* 1640 */ 191, 191, 116, 255, 191, 252, 191, 140, 71, 191, - /* 1650 */ 315, 233, 191, 191, 191, 191, 191, 191, 191, 191, - /* 1660 */ 191, 285, 284, 239, 252, 252, 252, 252, 240, 152, - /* 1670 */ 153, 154, 155, 156, 189, 294, 268, 100, 242, 268, - /* 1680 */ 264, 211, 290, 106, 107, 108, 264, 256, 256, 243, - /* 1690 */ 113, 290, 115, 116, 117, 268, 217, 120, 226, 243, - /* 1700 */ 222, 268, 216, 19, 20, 246, 22, 216, 256, 216, - /* 1710 */ 194, 60, 38, 242, 277, 294, 240, 242, 198, 246, - /* 1720 */ 36, 140, 198, 198, 19, 20, 150, 22, 149, 152, - /* 1730 */ 153, 154, 155, 156, 294, 291, 291, 280, 22, 43, - /* 1740 */ 231, 36, 18, 59, 234, 234, 234, 234, 267, 269, - /* 1750 */ 18, 198, 197, 231, 148, 71, 269, 269, 243, 231, - /* 1760 */ 198, 243, 267, 197, 59, 157, 243, 198, 62, 287, - /* 1770 */ 243, 197, 22, 198, 114, 64, 71, 218, 218, 197, - /* 1780 */ 286, 198, 197, 215, 100, 215, 215, 224, 22, 125, - /* 1790 */ 106, 107, 164, 24, 221, 112, 143, 113, 302, 115, - /* 1800 */ 116, 117, 218, 215, 120, 100, 217, 221, 215, 215, - /* 1810 */ 309, 106, 107, 215, 224, 279, 279, 218, 113, 258, - /* 1820 */ 115, 116, 117, 114, 257, 120, 91, 198, 82, 147, - /* 1830 */ 144, 22, 274, 314, 198, 314, 152, 153, 154, 155, - /* 1840 */ 156, 276, 157, 146, 145, 258, 25, 247, 257, 201, - /* 1850 */ 258, 26, 200, 13, 257, 244, 246, 152, 153, 154, - /* 1860 */ 155, 156, 258, 262, 257, 247, 245, 243, 192, 192, - /* 1870 */ 6, 262, 204, 210, 219, 210, 210, 190, 190, 190, - /* 1880 */ 210, 219, 204, 211, 211, 210, 4, 3, 22, 162, - /* 1890 */ 15, 23, 16, 23, 204, 138, 129, 150, 26, 141, - /* 1900 */ 20, 24, 143, 16, 1, 141, 129, 129, 61, 53, - /* 1910 */ 37, 150, 297, 300, 300, 53, 53, 129, 53, 115, - /* 1920 */ 34, 140, 1, 5, 22, 114, 68, 26, 160, 68, - /* 1930 */ 75, 41, 140, 114, 24, 20, 19, 130, 124, 23, - /* 1940 */ 96, 22, 22, 37, 22, 67, 22, 59, 67, 24, - /* 1950 */ 22, 28, 67, 23, 148, 22, 97, 23, 23, 23, - /* 1960 */ 140, 23, 22, 26, 23, 23, 115, 22, 142, 26, - /* 1970 */ 75, 88, 75, 34, 23, 86, 44, 22, 34, 26, - /* 1980 */ 34, 34, 34, 34, 93, 24, 26, 23, 34, 23, - /* 1990 */ 23, 23, 23, 11, 23, 22, 26, 22, 22, 15, - /* 2000 */ 23, 23, 22, 22, 1, 26, 23, 140, 134, 140, - /* 2010 */ 1, 140, 316, 316, 316, 316, 316, 316, 316, 140, - /* 2020 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2030 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2040 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2050 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2060 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2070 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2080 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2090 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2100 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2110 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2120 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2130 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2140 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2150 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2160 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2170 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2180 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2190 */ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - /* 2200 */ 316, 316, 316, + /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276, + /* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19, + /* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216, + /* 30 */ 217, 31, 193, 216, 217, 193, 228, 213, 230, 39, + /* 40 */ 206, 216, 217, 43, 44, 45, 46, 47, 48, 49, + /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 193, 19, + /* 60 */ 185, 186, 187, 188, 189, 190, 253, 274, 275, 276, + /* 70 */ 195, 193, 197, 193, 261, 274, 275, 276, 253, 204, + /* 80 */ 238, 204, 81, 43, 44, 45, 46, 47, 48, 49, + /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 274, 275, + /* 100 */ 276, 262, 102, 103, 104, 105, 106, 107, 108, 109, + /* 110 */ 110, 111, 112, 113, 239, 240, 239, 240, 210, 211, + /* 120 */ 212, 314, 315, 314, 59, 316, 86, 252, 88, 252, + /* 130 */ 19, 314, 315, 256, 257, 113, 25, 72, 296, 138, + /* 140 */ 139, 266, 102, 103, 104, 105, 106, 107, 108, 109, + /* 150 */ 110, 111, 112, 113, 43, 44, 45, 46, 47, 48, + /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 81, + /* 170 */ 292, 59, 292, 298, 108, 109, 110, 111, 112, 113, + /* 180 */ 69, 116, 117, 118, 72, 106, 107, 193, 111, 112, + /* 190 */ 113, 54, 55, 56, 57, 58, 102, 103, 104, 105, + /* 200 */ 106, 107, 108, 109, 110, 111, 112, 113, 120, 25, + /* 210 */ 216, 217, 145, 102, 103, 104, 105, 106, 107, 108, + /* 220 */ 109, 110, 111, 112, 113, 231, 138, 139, 116, 117, + /* 230 */ 118, 164, 153, 19, 155, 54, 55, 56, 57, 102, + /* 240 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 250 */ 113, 128, 129, 46, 47, 48, 49, 43, 44, 45, + /* 260 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 270 */ 56, 57, 216, 193, 25, 59, 193, 19, 165, 166, + /* 280 */ 193, 67, 24, 102, 103, 104, 105, 106, 107, 108, + /* 290 */ 109, 110, 111, 112, 113, 73, 216, 217, 59, 216, + /* 300 */ 217, 43, 44, 45, 46, 47, 48, 49, 50, 51, + /* 310 */ 52, 53, 54, 55, 56, 57, 102, 103, 104, 105, + /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 121, 145, + /* 330 */ 59, 193, 116, 117, 118, 119, 273, 204, 122, 123, + /* 340 */ 124, 19, 20, 134, 22, 136, 137, 19, 132, 127, + /* 350 */ 128, 129, 24, 22, 23, 116, 117, 118, 36, 193, + /* 360 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + /* 370 */ 112, 113, 239, 240, 311, 312, 215, 106, 107, 241, + /* 380 */ 19, 59, 216, 217, 223, 252, 115, 116, 117, 118, + /* 390 */ 151, 120, 26, 71, 193, 308, 309, 193, 149, 128, + /* 400 */ 313, 216, 269, 81, 43, 44, 45, 46, 47, 48, + /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 253, + /* 420 */ 216, 217, 100, 95, 153, 59, 155, 261, 106, 107, + /* 430 */ 25, 193, 101, 193, 193, 231, 114, 25, 116, 117, + /* 440 */ 118, 113, 304, 121, 193, 204, 59, 119, 120, 121, + /* 450 */ 122, 123, 124, 125, 216, 217, 193, 216, 217, 131, + /* 460 */ 138, 139, 230, 102, 103, 104, 105, 106, 107, 108, + /* 470 */ 109, 110, 111, 112, 113, 153, 154, 155, 156, 157, + /* 480 */ 239, 240, 116, 117, 118, 76, 193, 23, 19, 25, + /* 490 */ 22, 253, 23, 252, 253, 108, 87, 204, 89, 261, + /* 500 */ 198, 92, 261, 116, 117, 118, 193, 306, 307, 216, + /* 510 */ 217, 150, 43, 44, 45, 46, 47, 48, 49, 50, + /* 520 */ 51, 52, 53, 54, 55, 56, 57, 59, 193, 216, + /* 530 */ 217, 19, 239, 240, 283, 23, 106, 107, 108, 109, + /* 540 */ 110, 111, 112, 113, 73, 252, 253, 142, 308, 309, + /* 550 */ 138, 139, 81, 313, 145, 43, 44, 45, 46, 47, + /* 560 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 570 */ 307, 102, 103, 104, 105, 106, 107, 108, 109, 110, + /* 580 */ 111, 112, 113, 281, 116, 117, 118, 285, 23, 193, + /* 590 */ 25, 119, 59, 193, 122, 123, 124, 59, 127, 203, + /* 600 */ 59, 205, 19, 268, 132, 25, 23, 22, 193, 138, + /* 610 */ 139, 249, 204, 251, 102, 103, 104, 105, 106, 107, + /* 620 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 630 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 640 */ 57, 19, 22, 23, 59, 23, 25, 239, 240, 116, + /* 650 */ 117, 118, 193, 11, 116, 117, 118, 116, 117, 118, + /* 660 */ 252, 269, 22, 193, 15, 43, 44, 45, 46, 47, + /* 670 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 680 */ 273, 143, 193, 118, 143, 102, 103, 104, 105, 106, + /* 690 */ 107, 108, 109, 110, 111, 112, 113, 76, 118, 59, + /* 700 */ 241, 116, 117, 118, 304, 216, 217, 292, 143, 60, + /* 710 */ 89, 241, 19, 92, 193, 193, 23, 22, 311, 312, + /* 720 */ 231, 101, 22, 143, 102, 103, 104, 105, 106, 107, + /* 730 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 740 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 750 */ 57, 19, 193, 193, 59, 23, 116, 117, 118, 59, + /* 760 */ 201, 21, 241, 304, 22, 206, 127, 128, 129, 193, + /* 770 */ 128, 129, 235, 236, 304, 43, 44, 45, 46, 47, + /* 780 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 790 */ 22, 193, 216, 217, 193, 102, 103, 104, 105, 106, + /* 800 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 193, + /* 810 */ 193, 116, 117, 118, 216, 217, 116, 117, 118, 226, + /* 820 */ 80, 193, 19, 235, 236, 304, 23, 211, 212, 231, + /* 830 */ 204, 216, 217, 205, 102, 103, 104, 105, 106, 107, + /* 840 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 850 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 860 */ 57, 19, 193, 123, 76, 239, 240, 193, 253, 239, + /* 870 */ 240, 239, 240, 193, 106, 107, 193, 89, 252, 193, + /* 880 */ 92, 59, 252, 141, 252, 43, 44, 45, 46, 47, + /* 890 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 900 */ 284, 161, 216, 217, 193, 102, 103, 104, 105, 106, + /* 910 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 16, + /* 920 */ 187, 188, 189, 190, 7, 8, 9, 309, 195, 25, + /* 930 */ 197, 313, 19, 127, 128, 129, 262, 204, 22, 117, + /* 940 */ 24, 216, 217, 263, 102, 103, 104, 105, 106, 107, + /* 950 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, + /* 960 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 970 */ 57, 193, 239, 240, 193, 59, 19, 188, 253, 190, + /* 980 */ 77, 226, 79, 193, 195, 252, 197, 193, 19, 301, + /* 990 */ 302, 193, 193, 204, 216, 217, 226, 216, 217, 266, + /* 1000 */ 204, 159, 45, 46, 47, 48, 49, 50, 51, 52, + /* 1010 */ 53, 54, 55, 56, 57, 102, 103, 104, 105, 106, + /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 12, 239, 240, + /* 1030 */ 232, 298, 238, 117, 253, 239, 240, 238, 259, 260, + /* 1040 */ 193, 252, 27, 31, 193, 193, 142, 204, 252, 193, + /* 1050 */ 193, 39, 262, 193, 100, 266, 278, 42, 204, 102, + /* 1060 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 1070 */ 113, 117, 159, 216, 217, 121, 216, 217, 63, 193, + /* 1080 */ 193, 193, 239, 240, 115, 116, 193, 298, 73, 238, + /* 1090 */ 238, 231, 19, 239, 240, 252, 22, 24, 211, 212, + /* 1100 */ 24, 193, 216, 217, 216, 217, 252, 153, 154, 155, + /* 1110 */ 253, 16, 19, 144, 213, 268, 43, 44, 45, 46, + /* 1120 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1130 */ 57, 238, 19, 59, 193, 59, 43, 44, 45, 46, + /* 1140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1150 */ 57, 22, 23, 193, 25, 193, 43, 44, 45, 46, + /* 1160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1170 */ 57, 284, 77, 193, 79, 102, 103, 104, 105, 106, + /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 286, 193, 193, + /* 1190 */ 193, 117, 291, 117, 232, 102, 103, 104, 105, 106, + /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 204, 22, 23, + /* 1210 */ 66, 25, 216, 217, 35, 102, 103, 104, 105, 106, + /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 193, 268, 85, + /* 1230 */ 101, 193, 309, 309, 240, 19, 313, 313, 94, 208, + /* 1240 */ 209, 193, 239, 240, 193, 66, 252, 19, 268, 244, + /* 1250 */ 216, 217, 193, 74, 213, 252, 161, 19, 263, 254, + /* 1260 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + /* 1270 */ 54, 55, 56, 57, 193, 216, 217, 5, 59, 193, + /* 1280 */ 19, 244, 10, 11, 12, 13, 14, 101, 309, 17, + /* 1290 */ 146, 254, 313, 193, 193, 76, 115, 216, 217, 309, + /* 1300 */ 12, 263, 30, 313, 32, 46, 87, 46, 89, 130, + /* 1310 */ 193, 92, 40, 22, 263, 27, 216, 217, 102, 103, + /* 1320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + /* 1330 */ 42, 150, 291, 216, 217, 116, 117, 118, 19, 20, + /* 1340 */ 193, 22, 70, 260, 116, 193, 24, 264, 193, 263, + /* 1350 */ 78, 63, 61, 81, 116, 36, 193, 260, 193, 29, + /* 1360 */ 193, 264, 193, 33, 145, 193, 59, 48, 216, 217, + /* 1370 */ 98, 216, 217, 193, 115, 193, 115, 193, 59, 216, + /* 1380 */ 217, 216, 217, 216, 217, 216, 217, 255, 216, 217, + /* 1390 */ 71, 193, 131, 193, 25, 65, 216, 217, 216, 217, + /* 1400 */ 216, 217, 208, 209, 85, 133, 193, 100, 193, 90, + /* 1410 */ 138, 139, 138, 139, 216, 217, 216, 217, 193, 100, + /* 1420 */ 193, 108, 135, 116, 117, 106, 107, 140, 121, 216, + /* 1430 */ 217, 216, 217, 114, 162, 116, 117, 118, 299, 300, + /* 1440 */ 121, 216, 217, 216, 217, 193, 244, 193, 135, 244, + /* 1450 */ 193, 256, 257, 140, 244, 193, 254, 193, 193, 254, + /* 1460 */ 153, 154, 155, 141, 254, 149, 150, 258, 216, 217, + /* 1470 */ 216, 217, 153, 154, 155, 156, 157, 0, 1, 2, + /* 1480 */ 216, 217, 5, 115, 158, 193, 160, 10, 11, 12, + /* 1490 */ 13, 14, 193, 59, 17, 126, 193, 19, 20, 129, + /* 1500 */ 22, 193, 22, 22, 24, 193, 23, 30, 25, 32, + /* 1510 */ 19, 20, 144, 22, 36, 216, 217, 40, 193, 216, + /* 1520 */ 217, 193, 152, 129, 216, 217, 193, 36, 216, 217, + /* 1530 */ 193, 99, 193, 193, 53, 193, 193, 59, 23, 193, + /* 1540 */ 25, 216, 217, 193, 216, 217, 152, 70, 59, 71, + /* 1550 */ 59, 117, 193, 216, 217, 78, 216, 217, 81, 216, + /* 1560 */ 217, 318, 71, 85, 193, 133, 193, 193, 90, 23, + /* 1570 */ 23, 25, 25, 120, 121, 98, 85, 193, 100, 193, + /* 1580 */ 23, 90, 25, 121, 106, 107, 19, 216, 217, 216, + /* 1590 */ 217, 100, 114, 131, 116, 117, 118, 106, 107, 121, + /* 1600 */ 216, 217, 216, 217, 193, 114, 117, 116, 117, 118, + /* 1610 */ 133, 193, 121, 193, 193, 138, 139, 193, 23, 193, + /* 1620 */ 25, 23, 23, 25, 25, 7, 8, 216, 217, 193, + /* 1630 */ 193, 153, 154, 155, 156, 157, 216, 217, 193, 162, + /* 1640 */ 216, 217, 216, 217, 153, 154, 155, 156, 157, 1, + /* 1650 */ 2, 193, 193, 5, 19, 20, 59, 22, 10, 11, + /* 1660 */ 12, 13, 14, 193, 97, 17, 193, 23, 193, 25, + /* 1670 */ 288, 36, 193, 242, 216, 217, 236, 23, 30, 25, + /* 1680 */ 32, 19, 20, 23, 22, 25, 216, 217, 40, 216, + /* 1690 */ 217, 216, 217, 193, 59, 216, 217, 193, 36, 83, + /* 1700 */ 84, 153, 153, 155, 155, 23, 71, 25, 23, 193, + /* 1710 */ 25, 193, 193, 193, 117, 193, 193, 193, 70, 193, + /* 1720 */ 193, 59, 193, 255, 255, 287, 78, 255, 243, 81, + /* 1730 */ 191, 255, 297, 71, 271, 100, 293, 245, 267, 214, + /* 1740 */ 246, 106, 107, 108, 246, 271, 98, 245, 293, 114, + /* 1750 */ 220, 116, 117, 118, 267, 271, 121, 271, 225, 219, + /* 1760 */ 229, 219, 100, 219, 259, 259, 259, 259, 106, 107, + /* 1770 */ 249, 196, 60, 280, 141, 243, 114, 249, 116, 117, + /* 1780 */ 118, 133, 245, 121, 200, 297, 138, 139, 153, 154, + /* 1790 */ 155, 156, 157, 297, 200, 38, 19, 20, 151, 22, + /* 1800 */ 200, 150, 140, 294, 294, 22, 272, 43, 234, 18, + /* 1810 */ 162, 270, 200, 36, 237, 153, 154, 155, 156, 157, + /* 1820 */ 237, 283, 237, 237, 18, 199, 149, 246, 272, 270, + /* 1830 */ 272, 200, 158, 246, 246, 234, 59, 234, 246, 199, + /* 1840 */ 290, 62, 289, 200, 199, 22, 221, 115, 71, 200, + /* 1850 */ 200, 199, 199, 221, 218, 218, 19, 20, 64, 22, + /* 1860 */ 218, 227, 22, 224, 126, 224, 165, 221, 24, 305, + /* 1870 */ 200, 113, 312, 36, 218, 220, 218, 100, 282, 218, + /* 1880 */ 91, 218, 317, 106, 107, 221, 227, 282, 317, 82, + /* 1890 */ 148, 114, 265, 116, 117, 118, 59, 145, 121, 22, + /* 1900 */ 277, 158, 200, 265, 25, 202, 147, 250, 71, 279, + /* 1910 */ 13, 146, 194, 194, 249, 248, 250, 140, 247, 246, + /* 1920 */ 6, 192, 192, 192, 303, 303, 213, 207, 300, 213, + /* 1930 */ 153, 154, 155, 156, 157, 213, 213, 100, 213, 222, + /* 1940 */ 207, 214, 214, 106, 107, 4, 222, 207, 3, 22, + /* 1950 */ 163, 114, 15, 116, 117, 118, 16, 23, 121, 23, + /* 1960 */ 139, 151, 130, 25, 142, 16, 24, 20, 144, 1, + /* 1970 */ 142, 130, 130, 61, 53, 53, 37, 151, 53, 53, + /* 1980 */ 130, 116, 34, 1, 141, 5, 22, 115, 161, 141, + /* 1990 */ 153, 154, 155, 156, 157, 25, 68, 68, 75, 41, + /* 2000 */ 115, 24, 131, 20, 19, 125, 22, 96, 22, 22, + /* 2010 */ 67, 23, 22, 67, 59, 24, 22, 28, 67, 23, + /* 2020 */ 22, 22, 149, 23, 23, 23, 116, 23, 25, 37, + /* 2030 */ 97, 141, 23, 23, 22, 143, 25, 75, 88, 34, + /* 2040 */ 34, 34, 34, 86, 75, 93, 23, 34, 22, 34, + /* 2050 */ 25, 24, 34, 25, 23, 142, 23, 142, 44, 23, + /* 2060 */ 23, 23, 11, 23, 25, 22, 22, 22, 15, 23, + /* 2070 */ 23, 22, 22, 25, 1, 1, 141, 25, 23, 135, + /* 2080 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2090 */ 319, 319, 319, 319, 141, 141, 319, 141, 319, 319, + /* 2100 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2110 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2120 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2130 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2140 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2150 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2160 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2170 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2180 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2190 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2200 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2210 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2220 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2230 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2240 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2250 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2260 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2270 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + /* 2280 */ 319, 319, 319, }; -#define YY_SHIFT_COUNT (569) +#define YY_SHIFT_COUNT (575) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (2009) +#define YY_SHIFT_MAX (2074) static const unsigned short int yy_shift_ofst[] = { - /* 0 */ 1423, 1409, 1454, 1192, 1192, 36, 1252, 1410, 1517, 1684, - /* 10 */ 1684, 1684, 292, 0, 0, 180, 1015, 1684, 1684, 1684, - /* 20 */ 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, - /* 30 */ 1049, 1049, 1121, 1121, 54, 400, 36, 36, 36, 36, - /* 40 */ 36, 40, 110, 219, 289, 396, 439, 509, 548, 618, - /* 50 */ 657, 727, 766, 836, 995, 1015, 1015, 1015, 1015, 1015, - /* 60 */ 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, - /* 70 */ 1015, 1015, 1015, 1035, 1015, 1138, 880, 880, 1577, 1684, - /* 80 */ 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, - /* 90 */ 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, - /* 100 */ 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, - /* 110 */ 1684, 1684, 1684, 1705, 1684, 1684, 1684, 1684, 1684, 1684, - /* 120 */ 1684, 1684, 1684, 1684, 1684, 1684, 1684, 146, 84, 84, - /* 130 */ 84, 84, 84, 362, 269, 125, 97, 453, 66, 66, - /* 140 */ 893, 1090, 66, 66, 533, 533, 66, 554, 554, 554, - /* 150 */ 554, 192, 587, 587, 695, 25, 2020, 2020, 290, 290, - /* 160 */ 290, 200, 514, 514, 514, 514, 939, 939, 442, 875, - /* 170 */ 935, 66, 66, 66, 66, 66, 66, 66, 66, 66, - /* 180 */ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - /* 190 */ 66, 601, 601, 66, 729, 878, 878, 1266, 1266, 552, - /* 200 */ 1023, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 307, 490, - /* 210 */ 490, 567, 393, 517, 467, 672, 242, 682, 675, 66, - /* 220 */ 66, 66, 66, 66, 66, 66, 66, 66, 66, 616, - /* 230 */ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - /* 240 */ 66, 66, 1093, 1093, 1093, 66, 66, 66, 778, 66, - /* 250 */ 66, 66, 1053, 1064, 66, 66, 1190, 66, 66, 66, - /* 260 */ 66, 66, 66, 66, 66, 722, 992, 718, 253, 253, - /* 270 */ 253, 253, 338, 718, 718, 888, 403, 852, 1328, 254, - /* 280 */ 1295, 721, 1330, 1295, 1330, 1370, 234, 254, 254, 234, - /* 290 */ 254, 721, 1370, 1357, 1492, 1348, 385, 385, 385, 1330, - /* 300 */ 1425, 1425, 643, 1315, 1336, 1004, 1651, 1651, 1581, 1581, - /* 310 */ 1674, 1674, 1581, 1576, 1579, 1716, 1696, 1724, 1724, 1724, - /* 320 */ 1724, 1581, 1732, 1606, 1579, 1579, 1606, 1716, 1696, 1606, - /* 330 */ 1696, 1606, 1581, 1732, 1608, 1706, 1581, 1732, 1750, 1581, - /* 340 */ 1732, 1581, 1732, 1750, 1660, 1660, 1660, 1711, 1766, 1766, - /* 350 */ 1750, 1660, 1664, 1660, 1711, 1660, 1660, 1628, 1769, 1683, - /* 360 */ 1683, 1750, 1653, 1709, 1653, 1709, 1653, 1709, 1653, 1709, - /* 370 */ 1581, 1735, 1735, 1746, 1746, 1682, 1686, 1809, 1581, 1685, - /* 380 */ 1682, 1697, 1699, 1606, 1821, 1825, 1840, 1840, 1864, 1864, - /* 390 */ 1864, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, - /* 400 */ 2020, 2020, 2020, 2020, 2020, 2020, 599, 223, 1193, 1299, - /* 410 */ 228, 780, 958, 1505, 1153, 1435, 1368, 1426, 1430, 1552, - /* 420 */ 1477, 1556, 1557, 1564, 1572, 1578, 1580, 1489, 1474, 1602, - /* 430 */ 1389, 1514, 1500, 1595, 1596, 1484, 1603, 1075, 1460, 1605, - /* 440 */ 1612, 1526, 1507, 1882, 1884, 1866, 1727, 1875, 1876, 1868, - /* 450 */ 1870, 1757, 1747, 1767, 1872, 1872, 1877, 1758, 1880, 1759, - /* 460 */ 1887, 1903, 1764, 1777, 1872, 1778, 1847, 1873, 1872, 1761, - /* 470 */ 1856, 1862, 1863, 1865, 1788, 1804, 1886, 1781, 1921, 1918, - /* 480 */ 1902, 1811, 1768, 1858, 1901, 1861, 1855, 1890, 1792, 1819, - /* 490 */ 1910, 1915, 1917, 1807, 1814, 1919, 1878, 1920, 1922, 1916, - /* 500 */ 1924, 1881, 1888, 1925, 1844, 1923, 1928, 1885, 1906, 1930, - /* 510 */ 1806, 1933, 1934, 1935, 1936, 1937, 1938, 1940, 1859, 1820, - /* 520 */ 1941, 1942, 1851, 1939, 1945, 1826, 1943, 1944, 1946, 1947, - /* 530 */ 1948, 1883, 1895, 1889, 1932, 1897, 1891, 1949, 1951, 1955, - /* 540 */ 1961, 1953, 1960, 1954, 1964, 1943, 1966, 1967, 1968, 1969, - /* 550 */ 1970, 1971, 1973, 1982, 1975, 1976, 1977, 1978, 1980, 1981, - /* 560 */ 1979, 1874, 1867, 1869, 1871, 1879, 1983, 1984, 2003, 2009, + /* 0 */ 1648, 1477, 1272, 322, 322, 1, 1319, 1478, 1491, 1837, + /* 10 */ 1837, 1837, 471, 0, 0, 214, 1093, 1837, 1837, 1837, + /* 20 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 30 */ 271, 271, 1219, 1219, 216, 88, 1, 1, 1, 1, + /* 40 */ 1, 40, 111, 258, 361, 469, 512, 583, 622, 693, + /* 50 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093, + /* 60 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, + /* 70 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635, 1662, + /* 80 */ 1777, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 90 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 100 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 110 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 120 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 130 */ 137, 181, 181, 181, 181, 181, 181, 181, 94, 430, + /* 140 */ 66, 65, 112, 366, 533, 533, 740, 1261, 533, 533, + /* 150 */ 79, 79, 533, 412, 412, 412, 77, 412, 123, 113, + /* 160 */ 113, 22, 22, 2098, 2098, 328, 328, 328, 239, 468, + /* 170 */ 468, 468, 468, 1015, 1015, 409, 366, 1129, 1186, 533, + /* 180 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, + /* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 969, + /* 200 */ 621, 621, 533, 642, 788, 788, 1228, 1228, 822, 822, + /* 210 */ 67, 1274, 2098, 2098, 2098, 2098, 2098, 2098, 2098, 1307, + /* 220 */ 954, 954, 585, 472, 640, 387, 695, 538, 541, 700, + /* 230 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, + /* 240 */ 222, 533, 533, 533, 533, 533, 533, 533, 533, 533, + /* 250 */ 533, 533, 533, 1179, 1179, 1179, 533, 533, 533, 565, + /* 260 */ 533, 533, 533, 916, 1144, 533, 533, 1288, 533, 533, + /* 270 */ 533, 533, 533, 533, 533, 533, 639, 1330, 209, 1076, + /* 280 */ 1076, 1076, 1076, 580, 209, 209, 1313, 768, 917, 649, + /* 290 */ 1181, 1316, 405, 1316, 1238, 249, 1181, 1181, 249, 1181, + /* 300 */ 405, 1238, 1369, 464, 1259, 1012, 1012, 1012, 1368, 1368, + /* 310 */ 1368, 1368, 184, 184, 1326, 904, 1287, 1480, 1712, 1712, + /* 320 */ 1633, 1633, 1757, 1757, 1633, 1647, 1651, 1783, 1764, 1791, + /* 330 */ 1791, 1791, 1791, 1633, 1806, 1677, 1651, 1651, 1677, 1783, + /* 340 */ 1764, 1677, 1764, 1677, 1633, 1806, 1674, 1779, 1633, 1806, + /* 350 */ 1823, 1633, 1806, 1633, 1806, 1823, 1732, 1732, 1732, 1794, + /* 360 */ 1840, 1840, 1823, 1732, 1738, 1732, 1794, 1732, 1732, 1701, + /* 370 */ 1844, 1758, 1758, 1823, 1633, 1789, 1789, 1807, 1807, 1742, + /* 380 */ 1752, 1877, 1633, 1743, 1742, 1759, 1765, 1677, 1879, 1897, + /* 390 */ 1897, 1914, 1914, 1914, 2098, 2098, 2098, 2098, 2098, 2098, + /* 400 */ 2098, 2098, 2098, 2098, 2098, 2098, 2098, 2098, 2098, 207, + /* 410 */ 1095, 331, 620, 903, 806, 1074, 1483, 1432, 1481, 1322, + /* 420 */ 1370, 1394, 1515, 1291, 1546, 1547, 1557, 1595, 1598, 1599, + /* 430 */ 1434, 1453, 1618, 1462, 1567, 1489, 1644, 1654, 1616, 1660, + /* 440 */ 1548, 1549, 1682, 1685, 1597, 742, 1941, 1945, 1927, 1787, + /* 450 */ 1937, 1940, 1934, 1936, 1821, 1810, 1832, 1938, 1938, 1942, + /* 460 */ 1822, 1947, 1824, 1949, 1968, 1828, 1841, 1938, 1842, 1912, + /* 470 */ 1939, 1938, 1826, 1921, 1922, 1925, 1926, 1850, 1865, 1948, + /* 480 */ 1843, 1982, 1980, 1964, 1872, 1827, 1928, 1970, 1929, 1923, + /* 490 */ 1958, 1848, 1885, 1977, 1983, 1985, 1871, 1880, 1984, 1943, + /* 500 */ 1986, 1987, 1988, 1990, 1946, 1955, 1991, 1911, 1989, 1994, + /* 510 */ 1951, 1992, 1996, 1873, 1998, 2000, 2001, 2002, 2003, 2004, + /* 520 */ 1999, 1933, 1890, 2009, 2010, 1910, 2005, 2012, 1892, 2011, + /* 530 */ 2006, 2007, 2008, 2013, 1950, 1962, 1957, 2014, 1969, 1952, + /* 540 */ 2015, 2023, 2026, 2027, 2025, 2028, 2018, 1913, 1915, 2031, + /* 550 */ 2011, 2033, 2036, 2037, 2038, 2039, 2040, 2043, 2051, 2044, + /* 560 */ 2045, 2046, 2047, 2049, 2050, 2048, 1944, 1935, 1953, 1954, + /* 570 */ 1956, 2052, 2055, 2053, 2073, 2074, }; -#define YY_REDUCE_COUNT (405) -#define YY_REDUCE_MIN (-265) -#define YY_REDUCE_MAX (1690) +#define YY_REDUCE_COUNT (408) +#define YY_REDUCE_MIN (-271) +#define YY_REDUCE_MAX (1740) static const short yy_reduce_ofst[] = { - /* 0 */ 111, 168, 386, 761, -176, -174, -191, -189, -181, -178, - /* 10 */ 176, 263, 44, -207, -204, -265, -139, -114, 158, 504, - /* 20 */ 525, 544, 612, 614, 650, 652, 765, 265, 703, 705, - /* 30 */ 70, 714, -187, 127, 774, 713, 767, 769, 970, 1019, - /* 40 */ 1021, -255, -255, -255, -255, -255, -255, -255, -255, -255, - /* 50 */ -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - /* 60 */ -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - /* 70 */ -255, -255, -255, -255, -255, -255, -255, -255, 394, 542, - /* 80 */ 816, 818, 842, 882, 902, 919, 938, 940, 957, 986, - /* 90 */ 1048, 1063, 1068, 1073, 1076, 1088, 1100, 1102, 1104, 1106, - /* 100 */ 1113, 1119, 1137, 1140, 1143, 1147, 1149, 1164, 1173, 1183, - /* 110 */ 1185, 1188, 1202, 1204, 1247, 1259, 1263, 1283, 1289, 1292, - /* 120 */ 1298, 1300, 1329, 1331, 1343, 1358, 1360, -255, -255, -255, - /* 130 */ -255, -255, -255, -255, -255, 196, -255, 387, -177, 507, - /* 140 */ 1002, -219, 557, -93, -167, 638, -121, 284, 500, 284, - /* 150 */ 500, 247, 651, 865, -255, -255, -255, -255, -85, -85, - /* 160 */ -85, 237, 171, 602, 846, 885, -212, -203, 217, 380, - /* 170 */ 380, -23, 161, 653, 712, 773, 943, 990, 1040, 563, - /* 180 */ 833, 971, 1005, 1042, 1092, 1078, 1043, 1144, 1184, -186, - /* 190 */ 1105, 318, 869, 7, 825, 920, 1074, 704, 706, 390, - /* 200 */ 1087, 1094, 336, 545, 772, 1201, 1117, 1207, -179, -137, - /* 210 */ -112, -13, 18, 112, 197, 418, 495, 508, 777, 809, - /* 220 */ 923, 1014, 1027, 1033, 1044, 1115, 1194, 1212, 1221, 209, - /* 230 */ 1236, 1240, 1256, 1287, 1301, 1307, 1349, 1359, 1398, 1417, - /* 240 */ 1429, 1434, 681, 1377, 1404, 1448, 1449, 1450, 1388, 1453, - /* 250 */ 1455, 1458, 1393, 1335, 1461, 1462, 1418, 1463, 197, 1464, - /* 260 */ 1465, 1466, 1467, 1468, 1469, 1376, 1378, 1424, 1412, 1413, - /* 270 */ 1414, 1415, 1388, 1424, 1424, 1428, 1470, 1485, 1381, 1408, - /* 280 */ 1416, 1436, 1431, 1422, 1432, 1392, 1446, 1411, 1427, 1456, - /* 290 */ 1433, 1471, 1401, 1479, 1472, 1478, 1486, 1491, 1493, 1452, - /* 300 */ 1459, 1473, 1437, 1475, 1476, 1516, 1421, 1440, 1520, 1524, - /* 310 */ 1444, 1445, 1525, 1457, 1480, 1481, 1509, 1510, 1511, 1512, - /* 320 */ 1513, 1553, 1555, 1515, 1487, 1488, 1518, 1495, 1522, 1523, - /* 330 */ 1528, 1527, 1562, 1566, 1482, 1494, 1569, 1574, 1559, 1575, - /* 340 */ 1582, 1583, 1585, 1560, 1568, 1570, 1571, 1563, 1573, 1586, - /* 350 */ 1584, 1588, 1589, 1593, 1590, 1594, 1598, 1501, 1496, 1536, - /* 360 */ 1537, 1599, 1561, 1567, 1587, 1591, 1592, 1597, 1604, 1607, - /* 370 */ 1629, 1519, 1521, 1601, 1609, 1600, 1610, 1558, 1636, 1565, - /* 380 */ 1618, 1621, 1611, 1624, 1648, 1652, 1676, 1677, 1687, 1688, - /* 390 */ 1689, 1613, 1614, 1615, 1668, 1663, 1665, 1666, 1670, 1678, - /* 400 */ 1655, 1662, 1672, 1673, 1675, 1690, + /* 0 */ -125, 733, 789, 241, 293, -123, -193, -191, -183, -187, + /* 10 */ 166, 238, 133, -207, -199, -267, -176, -6, 204, 489, + /* 20 */ 576, -175, 598, 686, 615, 725, 860, 778, 781, 857, + /* 30 */ 616, 887, 87, 240, -192, 408, 626, 796, 843, 854, + /* 40 */ 1003, -271, -271, -271, -271, -271, -271, -271, -271, -271, + /* 50 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + /* 60 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + /* 70 */ -271, -271, -271, -271, -271, -271, -271, -271, 80, 83, + /* 80 */ 313, 886, 888, 996, 1034, 1059, 1081, 1100, 1117, 1152, + /* 90 */ 1155, 1163, 1165, 1167, 1169, 1172, 1180, 1182, 1184, 1198, + /* 100 */ 1200, 1213, 1215, 1225, 1227, 1252, 1254, 1264, 1299, 1303, + /* 110 */ 1308, 1312, 1325, 1328, 1337, 1340, 1343, 1371, 1373, 1384, + /* 120 */ 1386, 1411, 1420, 1424, 1426, 1458, 1470, 1473, 1475, 1479, + /* 130 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + /* 140 */ -271, 138, 459, 396, -158, 470, 302, -212, 521, 201, + /* 150 */ -195, -92, 559, 630, 632, 630, -271, 632, 901, 63, + /* 160 */ 407, -271, -271, -271, -271, 161, 161, 161, 251, 335, + /* 170 */ 847, 960, 980, 537, 588, 618, 628, 688, 688, -166, + /* 180 */ -161, 674, 790, 794, 799, 851, 852, -122, 680, -120, + /* 190 */ 995, 1038, 415, 1051, 893, 798, 962, 400, 1086, 779, + /* 200 */ 923, 924, 263, 1041, 979, 990, 1083, 1097, 1031, 1194, + /* 210 */ 362, 994, 1139, 1005, 1037, 1202, 1205, 1195, 1210, -194, + /* 220 */ 56, 185, -135, 232, 522, 560, 601, 617, 669, 683, + /* 230 */ 711, 856, 908, 941, 1048, 1101, 1147, 1257, 1262, 1265, + /* 240 */ 392, 1292, 1333, 1339, 1342, 1346, 1350, 1359, 1374, 1418, + /* 250 */ 1421, 1436, 1437, 593, 755, 770, 997, 1445, 1459, 1209, + /* 260 */ 1500, 1504, 1516, 1132, 1243, 1518, 1519, 1440, 1520, 560, + /* 270 */ 1522, 1523, 1524, 1526, 1527, 1529, 1382, 1438, 1431, 1468, + /* 280 */ 1469, 1472, 1476, 1209, 1431, 1431, 1485, 1525, 1539, 1435, + /* 290 */ 1463, 1471, 1492, 1487, 1443, 1494, 1474, 1484, 1498, 1486, + /* 300 */ 1502, 1455, 1530, 1531, 1533, 1540, 1542, 1544, 1505, 1506, + /* 310 */ 1507, 1508, 1521, 1528, 1493, 1537, 1532, 1575, 1488, 1496, + /* 320 */ 1584, 1594, 1509, 1510, 1600, 1538, 1534, 1541, 1574, 1577, + /* 330 */ 1583, 1585, 1586, 1612, 1626, 1581, 1556, 1558, 1587, 1559, + /* 340 */ 1601, 1588, 1603, 1592, 1631, 1640, 1550, 1553, 1643, 1645, + /* 350 */ 1625, 1649, 1652, 1650, 1653, 1632, 1636, 1637, 1642, 1634, + /* 360 */ 1639, 1641, 1646, 1656, 1655, 1658, 1659, 1661, 1663, 1560, + /* 370 */ 1564, 1596, 1605, 1664, 1670, 1565, 1571, 1627, 1638, 1657, + /* 380 */ 1665, 1623, 1702, 1630, 1666, 1667, 1671, 1673, 1703, 1718, + /* 390 */ 1719, 1729, 1730, 1731, 1621, 1622, 1628, 1720, 1713, 1716, + /* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 1623, 1623, 1623, 1453, 1223, 1332, 1223, 1223, 1223, 1453, - /* 10 */ 1453, 1453, 1223, 1362, 1362, 1506, 1254, 1223, 1223, 1223, - /* 20 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1452, 1223, 1223, - /* 30 */ 1223, 1223, 1541, 1541, 1223, 1223, 1223, 1223, 1223, 1223, - /* 40 */ 1223, 1223, 1371, 1223, 1378, 1223, 1223, 1223, 1223, 1223, - /* 50 */ 1454, 1455, 1223, 1223, 1223, 1505, 1507, 1470, 1385, 1384, - /* 60 */ 1383, 1382, 1488, 1349, 1376, 1369, 1373, 1448, 1449, 1447, - /* 70 */ 1451, 1455, 1454, 1223, 1372, 1419, 1433, 1418, 1223, 1223, - /* 80 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 90 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 100 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 110 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 120 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1427, 1432, 1438, - /* 130 */ 1431, 1428, 1421, 1420, 1422, 1223, 1423, 1223, 1223, 1223, - /* 140 */ 1244, 1296, 1223, 1223, 1223, 1223, 1223, 1525, 1524, 1223, - /* 150 */ 1223, 1254, 1413, 1412, 1424, 1425, 1435, 1434, 1513, 1576, - /* 160 */ 1575, 1471, 1223, 1223, 1223, 1223, 1223, 1223, 1541, 1223, - /* 170 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 180 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 190 */ 1223, 1541, 1541, 1223, 1254, 1541, 1541, 1250, 1250, 1356, - /* 200 */ 1223, 1520, 1323, 1323, 1323, 1323, 1332, 1323, 1223, 1223, - /* 210 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 220 */ 1223, 1223, 1223, 1510, 1508, 1223, 1223, 1223, 1223, 1223, - /* 230 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 240 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 250 */ 1223, 1223, 1328, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 260 */ 1223, 1223, 1223, 1223, 1570, 1223, 1483, 1310, 1328, 1328, - /* 270 */ 1328, 1328, 1330, 1311, 1309, 1322, 1255, 1230, 1615, 1388, - /* 280 */ 1377, 1329, 1351, 1377, 1351, 1612, 1375, 1388, 1388, 1375, - /* 290 */ 1388, 1329, 1612, 1271, 1592, 1266, 1362, 1362, 1362, 1351, - /* 300 */ 1356, 1356, 1450, 1329, 1322, 1223, 1615, 1615, 1337, 1337, - /* 310 */ 1614, 1614, 1337, 1471, 1599, 1397, 1299, 1305, 1305, 1305, - /* 320 */ 1305, 1337, 1241, 1375, 1599, 1599, 1375, 1397, 1299, 1375, - /* 330 */ 1299, 1375, 1337, 1241, 1487, 1609, 1337, 1241, 1461, 1337, - /* 340 */ 1241, 1337, 1241, 1461, 1297, 1297, 1297, 1286, 1223, 1223, - /* 350 */ 1461, 1297, 1271, 1297, 1286, 1297, 1297, 1559, 1223, 1465, - /* 360 */ 1465, 1461, 1355, 1350, 1355, 1350, 1355, 1350, 1355, 1350, - /* 370 */ 1337, 1551, 1551, 1365, 1365, 1370, 1356, 1456, 1337, 1223, - /* 380 */ 1370, 1368, 1366, 1375, 1247, 1289, 1573, 1573, 1569, 1569, - /* 390 */ 1569, 1620, 1620, 1520, 1585, 1254, 1254, 1254, 1254, 1585, - /* 400 */ 1273, 1273, 1255, 1255, 1254, 1585, 1223, 1223, 1223, 1223, - /* 410 */ 1223, 1223, 1580, 1223, 1515, 1472, 1341, 1223, 1223, 1223, - /* 420 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 430 */ 1223, 1526, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 440 */ 1223, 1223, 1402, 1223, 1226, 1517, 1223, 1223, 1223, 1223, - /* 450 */ 1223, 1223, 1223, 1223, 1379, 1380, 1342, 1223, 1223, 1223, - /* 460 */ 1223, 1223, 1223, 1223, 1394, 1223, 1223, 1223, 1389, 1223, - /* 470 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1611, 1223, 1223, - /* 480 */ 1223, 1223, 1223, 1223, 1486, 1485, 1223, 1223, 1339, 1223, - /* 490 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 500 */ 1223, 1223, 1269, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 510 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 520 */ 1223, 1223, 1223, 1223, 1223, 1223, 1367, 1223, 1223, 1223, - /* 530 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 540 */ 1223, 1556, 1357, 1223, 1223, 1602, 1223, 1223, 1223, 1223, - /* 550 */ 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223, - /* 560 */ 1596, 1313, 1404, 1223, 1403, 1407, 1223, 1235, 1223, 1223, + /* 0 */ 1647, 1647, 1647, 1475, 1240, 1351, 1240, 1240, 1240, 1475, + /* 10 */ 1475, 1475, 1240, 1381, 1381, 1528, 1273, 1240, 1240, 1240, + /* 20 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1474, 1240, 1240, + /* 30 */ 1240, 1240, 1563, 1563, 1240, 1240, 1240, 1240, 1240, 1240, + /* 40 */ 1240, 1240, 1390, 1240, 1397, 1240, 1240, 1240, 1240, 1240, + /* 50 */ 1476, 1477, 1240, 1240, 1240, 1527, 1529, 1492, 1404, 1403, + /* 60 */ 1402, 1401, 1510, 1369, 1395, 1388, 1392, 1470, 1471, 1469, + /* 70 */ 1473, 1477, 1476, 1240, 1391, 1438, 1454, 1437, 1240, 1240, + /* 80 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 90 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 100 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 110 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 120 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 130 */ 1446, 1453, 1452, 1451, 1460, 1450, 1447, 1440, 1439, 1441, + /* 140 */ 1442, 1240, 1240, 1264, 1240, 1240, 1261, 1315, 1240, 1240, + /* 150 */ 1240, 1240, 1240, 1547, 1546, 1240, 1443, 1240, 1273, 1432, + /* 160 */ 1431, 1457, 1444, 1456, 1455, 1535, 1599, 1598, 1493, 1240, + /* 170 */ 1240, 1240, 1240, 1240, 1240, 1563, 1240, 1240, 1240, 1240, + /* 180 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 190 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1371, + /* 200 */ 1563, 1563, 1240, 1273, 1563, 1563, 1372, 1372, 1269, 1269, + /* 210 */ 1375, 1240, 1542, 1342, 1342, 1342, 1342, 1351, 1342, 1240, + /* 220 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 230 */ 1240, 1240, 1240, 1240, 1532, 1530, 1240, 1240, 1240, 1240, + /* 240 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 250 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 260 */ 1240, 1240, 1240, 1347, 1240, 1240, 1240, 1240, 1240, 1240, + /* 270 */ 1240, 1240, 1240, 1240, 1240, 1592, 1240, 1505, 1329, 1347, + /* 280 */ 1347, 1347, 1347, 1349, 1330, 1328, 1341, 1274, 1247, 1639, + /* 290 */ 1407, 1396, 1348, 1396, 1636, 1394, 1407, 1407, 1394, 1407, + /* 300 */ 1348, 1636, 1290, 1615, 1285, 1381, 1381, 1381, 1371, 1371, + /* 310 */ 1371, 1371, 1375, 1375, 1472, 1348, 1341, 1240, 1639, 1639, + /* 320 */ 1357, 1357, 1638, 1638, 1357, 1493, 1623, 1416, 1318, 1324, + /* 330 */ 1324, 1324, 1324, 1357, 1258, 1394, 1623, 1623, 1394, 1416, + /* 340 */ 1318, 1394, 1318, 1394, 1357, 1258, 1509, 1633, 1357, 1258, + /* 350 */ 1483, 1357, 1258, 1357, 1258, 1483, 1316, 1316, 1316, 1305, + /* 360 */ 1240, 1240, 1483, 1316, 1290, 1316, 1305, 1316, 1316, 1581, + /* 370 */ 1240, 1487, 1487, 1483, 1357, 1573, 1573, 1384, 1384, 1389, + /* 380 */ 1375, 1478, 1357, 1240, 1389, 1387, 1385, 1394, 1308, 1595, + /* 390 */ 1595, 1591, 1591, 1591, 1644, 1644, 1542, 1608, 1273, 1273, + /* 400 */ 1273, 1273, 1608, 1292, 1292, 1274, 1274, 1273, 1608, 1240, + /* 410 */ 1240, 1240, 1240, 1240, 1240, 1603, 1240, 1537, 1494, 1361, + /* 420 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 430 */ 1240, 1240, 1240, 1240, 1548, 1240, 1240, 1240, 1240, 1240, + /* 440 */ 1240, 1240, 1240, 1240, 1240, 1421, 1240, 1243, 1539, 1240, + /* 450 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1398, 1399, 1362, + /* 460 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1413, 1240, 1240, + /* 470 */ 1240, 1408, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 480 */ 1635, 1240, 1240, 1240, 1240, 1240, 1240, 1508, 1507, 1240, + /* 490 */ 1240, 1359, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 500 */ 1240, 1240, 1240, 1240, 1240, 1288, 1240, 1240, 1240, 1240, + /* 510 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 520 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1386, + /* 530 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 540 */ 1240, 1240, 1240, 1240, 1578, 1376, 1240, 1240, 1240, 1240, + /* 550 */ 1626, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, + /* 560 */ 1240, 1240, 1240, 1240, 1240, 1619, 1332, 1423, 1240, 1422, + /* 570 */ 1426, 1262, 1240, 1252, 1240, 1240, }; /********** End of lemon-generated parsing tables *****************************/ @@ -158281,8 +168164,8 @@ static const YYCODETYPE yyFallback[] = { 0, /* LP => nothing */ 0, /* RP => nothing */ 0, /* AS => nothing */ - 59, /* WITHOUT => ID */ 0, /* COMMA => nothing */ + 59, /* WITHOUT => ID */ 59, /* ABORT => ID */ 59, /* ACTION => ID */ 59, /* AFTER => ID */ @@ -158368,6 +168251,7 @@ static const YYCODETYPE yyFallback[] = { 0, /* SLASH => nothing */ 0, /* REM => nothing */ 0, /* CONCAT => nothing */ + 0, /* PTR => nothing */ 0, /* COLLATE => nothing */ 0, /* BITNOT => nothing */ 0, /* ON => nothing */ @@ -158437,6 +168321,7 @@ static const YYCODETYPE yyFallback[] = { 0, /* IF_NULL_ROW => nothing */ 0, /* ASTERISK => nothing */ 0, /* SPAN => nothing */ + 0, /* ERROR => nothing */ 0, /* SPACE => nothing */ 0, /* ILLEGAL => nothing */ }; @@ -158490,9 +168375,9 @@ struct yyParser { }; typedef struct yyParser yyParser; +/* #include */ #ifndef NDEBUG /* #include */ -/* #include */ static FILE *yyTraceFILE = 0; static char *yyTracePrompt = 0; #endif /* NDEBUG */ @@ -158552,8 +168437,8 @@ static const char *const yyTokenName[] = { /* 22 */ "LP", /* 23 */ "RP", /* 24 */ "AS", - /* 25 */ "WITHOUT", - /* 26 */ "COMMA", + /* 25 */ "COMMA", + /* 26 */ "WITHOUT", /* 27 */ "ABORT", /* 28 */ "ACTION", /* 29 */ "AFTER", @@ -158639,210 +168524,213 @@ static const char *const yyTokenName[] = { /* 109 */ "SLASH", /* 110 */ "REM", /* 111 */ "CONCAT", - /* 112 */ "COLLATE", - /* 113 */ "BITNOT", - /* 114 */ "ON", - /* 115 */ "INDEXED", - /* 116 */ "STRING", - /* 117 */ "JOIN_KW", - /* 118 */ "CONSTRAINT", - /* 119 */ "DEFAULT", - /* 120 */ "NULL", - /* 121 */ "PRIMARY", - /* 122 */ "UNIQUE", - /* 123 */ "CHECK", - /* 124 */ "REFERENCES", - /* 125 */ "AUTOINCR", - /* 126 */ "INSERT", - /* 127 */ "DELETE", - /* 128 */ "UPDATE", - /* 129 */ "SET", - /* 130 */ "DEFERRABLE", - /* 131 */ "FOREIGN", - /* 132 */ "DROP", - /* 133 */ "UNION", - /* 134 */ "ALL", - /* 135 */ "EXCEPT", - /* 136 */ "INTERSECT", - /* 137 */ "SELECT", - /* 138 */ "VALUES", - /* 139 */ "DISTINCT", - /* 140 */ "DOT", - /* 141 */ "FROM", - /* 142 */ "JOIN", - /* 143 */ "USING", - /* 144 */ "ORDER", - /* 145 */ "GROUP", - /* 146 */ "HAVING", - /* 147 */ "LIMIT", - /* 148 */ "WHERE", - /* 149 */ "RETURNING", - /* 150 */ "INTO", - /* 151 */ "NOTHING", - /* 152 */ "FLOAT", - /* 153 */ "BLOB", - /* 154 */ "INTEGER", - /* 155 */ "VARIABLE", - /* 156 */ "CASE", - /* 157 */ "WHEN", - /* 158 */ "THEN", - /* 159 */ "ELSE", - /* 160 */ "INDEX", - /* 161 */ "ALTER", - /* 162 */ "ADD", - /* 163 */ "WINDOW", - /* 164 */ "OVER", - /* 165 */ "FILTER", - /* 166 */ "COLUMN", - /* 167 */ "AGG_FUNCTION", - /* 168 */ "AGG_COLUMN", - /* 169 */ "TRUEFALSE", - /* 170 */ "ISNOT", - /* 171 */ "FUNCTION", - /* 172 */ "UMINUS", - /* 173 */ "UPLUS", - /* 174 */ "TRUTH", - /* 175 */ "REGISTER", - /* 176 */ "VECTOR", - /* 177 */ "SELECT_COLUMN", - /* 178 */ "IF_NULL_ROW", - /* 179 */ "ASTERISK", - /* 180 */ "SPAN", - /* 181 */ "SPACE", - /* 182 */ "ILLEGAL", - /* 183 */ "input", - /* 184 */ "cmdlist", - /* 185 */ "ecmd", - /* 186 */ "cmdx", - /* 187 */ "explain", - /* 188 */ "cmd", - /* 189 */ "transtype", - /* 190 */ "trans_opt", - /* 191 */ "nm", - /* 192 */ "savepoint_opt", - /* 193 */ "create_table", - /* 194 */ "create_table_args", - /* 195 */ "createkw", - /* 196 */ "temp", - /* 197 */ "ifnotexists", - /* 198 */ "dbnm", - /* 199 */ "columnlist", - /* 200 */ "conslist_opt", - /* 201 */ "table_options", - /* 202 */ "select", - /* 203 */ "columnname", - /* 204 */ "carglist", - /* 205 */ "typetoken", - /* 206 */ "typename", - /* 207 */ "signed", - /* 208 */ "plus_num", - /* 209 */ "minus_num", - /* 210 */ "scanpt", - /* 211 */ "scantok", - /* 212 */ "ccons", - /* 213 */ "term", - /* 214 */ "expr", - /* 215 */ "onconf", - /* 216 */ "sortorder", - /* 217 */ "autoinc", - /* 218 */ "eidlist_opt", - /* 219 */ "refargs", - /* 220 */ "defer_subclause", - /* 221 */ "generated", - /* 222 */ "refarg", - /* 223 */ "refact", - /* 224 */ "init_deferred_pred_opt", - /* 225 */ "conslist", - /* 226 */ "tconscomma", - /* 227 */ "tcons", - /* 228 */ "sortlist", - /* 229 */ "eidlist", - /* 230 */ "defer_subclause_opt", - /* 231 */ "orconf", - /* 232 */ "resolvetype", - /* 233 */ "raisetype", - /* 234 */ "ifexists", - /* 235 */ "fullname", - /* 236 */ "selectnowith", - /* 237 */ "oneselect", - /* 238 */ "wqlist", - /* 239 */ "multiselect_op", - /* 240 */ "distinct", - /* 241 */ "selcollist", - /* 242 */ "from", - /* 243 */ "where_opt", - /* 244 */ "groupby_opt", - /* 245 */ "having_opt", - /* 246 */ "orderby_opt", - /* 247 */ "limit_opt", - /* 248 */ "window_clause", - /* 249 */ "values", - /* 250 */ "nexprlist", - /* 251 */ "sclp", - /* 252 */ "as", - /* 253 */ "seltablist", - /* 254 */ "stl_prefix", - /* 255 */ "joinop", - /* 256 */ "indexed_opt", - /* 257 */ "on_opt", - /* 258 */ "using_opt", - /* 259 */ "exprlist", - /* 260 */ "xfullname", - /* 261 */ "idlist", - /* 262 */ "nulls", - /* 263 */ "with", - /* 264 */ "where_opt_ret", - /* 265 */ "setlist", - /* 266 */ "insert_cmd", - /* 267 */ "idlist_opt", - /* 268 */ "upsert", - /* 269 */ "returning", - /* 270 */ "filter_over", - /* 271 */ "likeop", - /* 272 */ "between_op", - /* 273 */ "in_op", - /* 274 */ "paren_exprlist", - /* 275 */ "case_operand", - /* 276 */ "case_exprlist", - /* 277 */ "case_else", - /* 278 */ "uniqueflag", - /* 279 */ "collate", - /* 280 */ "vinto", - /* 281 */ "nmnum", - /* 282 */ "trigger_decl", - /* 283 */ "trigger_cmd_list", - /* 284 */ "trigger_time", - /* 285 */ "trigger_event", - /* 286 */ "foreach_clause", - /* 287 */ "when_clause", - /* 288 */ "trigger_cmd", - /* 289 */ "trnm", - /* 290 */ "tridxby", - /* 291 */ "database_kw_opt", - /* 292 */ "key_opt", - /* 293 */ "add_column_fullname", - /* 294 */ "kwcolumn_opt", - /* 295 */ "create_vtab", - /* 296 */ "vtabarglist", - /* 297 */ "vtabarg", - /* 298 */ "vtabargtoken", - /* 299 */ "lp", - /* 300 */ "anylist", - /* 301 */ "wqitem", - /* 302 */ "wqas", - /* 303 */ "windowdefn_list", - /* 304 */ "windowdefn", - /* 305 */ "window", - /* 306 */ "frame_opt", - /* 307 */ "part_opt", - /* 308 */ "filter_clause", - /* 309 */ "over_clause", - /* 310 */ "range_or_rows", - /* 311 */ "frame_bound", - /* 312 */ "frame_bound_s", - /* 313 */ "frame_bound_e", - /* 314 */ "frame_exclude_opt", - /* 315 */ "frame_exclude", + /* 112 */ "PTR", + /* 113 */ "COLLATE", + /* 114 */ "BITNOT", + /* 115 */ "ON", + /* 116 */ "INDEXED", + /* 117 */ "STRING", + /* 118 */ "JOIN_KW", + /* 119 */ "CONSTRAINT", + /* 120 */ "DEFAULT", + /* 121 */ "NULL", + /* 122 */ "PRIMARY", + /* 123 */ "UNIQUE", + /* 124 */ "CHECK", + /* 125 */ "REFERENCES", + /* 126 */ "AUTOINCR", + /* 127 */ "INSERT", + /* 128 */ "DELETE", + /* 129 */ "UPDATE", + /* 130 */ "SET", + /* 131 */ "DEFERRABLE", + /* 132 */ "FOREIGN", + /* 133 */ "DROP", + /* 134 */ "UNION", + /* 135 */ "ALL", + /* 136 */ "EXCEPT", + /* 137 */ "INTERSECT", + /* 138 */ "SELECT", + /* 139 */ "VALUES", + /* 140 */ "DISTINCT", + /* 141 */ "DOT", + /* 142 */ "FROM", + /* 143 */ "JOIN", + /* 144 */ "USING", + /* 145 */ "ORDER", + /* 146 */ "GROUP", + /* 147 */ "HAVING", + /* 148 */ "LIMIT", + /* 149 */ "WHERE", + /* 150 */ "RETURNING", + /* 151 */ "INTO", + /* 152 */ "NOTHING", + /* 153 */ "FLOAT", + /* 154 */ "BLOB", + /* 155 */ "INTEGER", + /* 156 */ "VARIABLE", + /* 157 */ "CASE", + /* 158 */ "WHEN", + /* 159 */ "THEN", + /* 160 */ "ELSE", + /* 161 */ "INDEX", + /* 162 */ "ALTER", + /* 163 */ "ADD", + /* 164 */ "WINDOW", + /* 165 */ "OVER", + /* 166 */ "FILTER", + /* 167 */ "COLUMN", + /* 168 */ "AGG_FUNCTION", + /* 169 */ "AGG_COLUMN", + /* 170 */ "TRUEFALSE", + /* 171 */ "ISNOT", + /* 172 */ "FUNCTION", + /* 173 */ "UMINUS", + /* 174 */ "UPLUS", + /* 175 */ "TRUTH", + /* 176 */ "REGISTER", + /* 177 */ "VECTOR", + /* 178 */ "SELECT_COLUMN", + /* 179 */ "IF_NULL_ROW", + /* 180 */ "ASTERISK", + /* 181 */ "SPAN", + /* 182 */ "ERROR", + /* 183 */ "SPACE", + /* 184 */ "ILLEGAL", + /* 185 */ "input", + /* 186 */ "cmdlist", + /* 187 */ "ecmd", + /* 188 */ "cmdx", + /* 189 */ "explain", + /* 190 */ "cmd", + /* 191 */ "transtype", + /* 192 */ "trans_opt", + /* 193 */ "nm", + /* 194 */ "savepoint_opt", + /* 195 */ "create_table", + /* 196 */ "create_table_args", + /* 197 */ "createkw", + /* 198 */ "temp", + /* 199 */ "ifnotexists", + /* 200 */ "dbnm", + /* 201 */ "columnlist", + /* 202 */ "conslist_opt", + /* 203 */ "table_option_set", + /* 204 */ "select", + /* 205 */ "table_option", + /* 206 */ "columnname", + /* 207 */ "carglist", + /* 208 */ "typetoken", + /* 209 */ "typename", + /* 210 */ "signed", + /* 211 */ "plus_num", + /* 212 */ "minus_num", + /* 213 */ "scanpt", + /* 214 */ "scantok", + /* 215 */ "ccons", + /* 216 */ "term", + /* 217 */ "expr", + /* 218 */ "onconf", + /* 219 */ "sortorder", + /* 220 */ "autoinc", + /* 221 */ "eidlist_opt", + /* 222 */ "refargs", + /* 223 */ "defer_subclause", + /* 224 */ "generated", + /* 225 */ "refarg", + /* 226 */ "refact", + /* 227 */ "init_deferred_pred_opt", + /* 228 */ "conslist", + /* 229 */ "tconscomma", + /* 230 */ "tcons", + /* 231 */ "sortlist", + /* 232 */ "eidlist", + /* 233 */ "defer_subclause_opt", + /* 234 */ "orconf", + /* 235 */ "resolvetype", + /* 236 */ "raisetype", + /* 237 */ "ifexists", + /* 238 */ "fullname", + /* 239 */ "selectnowith", + /* 240 */ "oneselect", + /* 241 */ "wqlist", + /* 242 */ "multiselect_op", + /* 243 */ "distinct", + /* 244 */ "selcollist", + /* 245 */ "from", + /* 246 */ "where_opt", + /* 247 */ "groupby_opt", + /* 248 */ "having_opt", + /* 249 */ "orderby_opt", + /* 250 */ "limit_opt", + /* 251 */ "window_clause", + /* 252 */ "values", + /* 253 */ "nexprlist", + /* 254 */ "sclp", + /* 255 */ "as", + /* 256 */ "seltablist", + /* 257 */ "stl_prefix", + /* 258 */ "joinop", + /* 259 */ "on_using", + /* 260 */ "indexed_by", + /* 261 */ "exprlist", + /* 262 */ "xfullname", + /* 263 */ "idlist", + /* 264 */ "indexed_opt", + /* 265 */ "nulls", + /* 266 */ "with", + /* 267 */ "where_opt_ret", + /* 268 */ "setlist", + /* 269 */ "insert_cmd", + /* 270 */ "idlist_opt", + /* 271 */ "upsert", + /* 272 */ "returning", + /* 273 */ "filter_over", + /* 274 */ "likeop", + /* 275 */ "between_op", + /* 276 */ "in_op", + /* 277 */ "paren_exprlist", + /* 278 */ "case_operand", + /* 279 */ "case_exprlist", + /* 280 */ "case_else", + /* 281 */ "uniqueflag", + /* 282 */ "collate", + /* 283 */ "vinto", + /* 284 */ "nmnum", + /* 285 */ "trigger_decl", + /* 286 */ "trigger_cmd_list", + /* 287 */ "trigger_time", + /* 288 */ "trigger_event", + /* 289 */ "foreach_clause", + /* 290 */ "when_clause", + /* 291 */ "trigger_cmd", + /* 292 */ "trnm", + /* 293 */ "tridxby", + /* 294 */ "database_kw_opt", + /* 295 */ "key_opt", + /* 296 */ "add_column_fullname", + /* 297 */ "kwcolumn_opt", + /* 298 */ "create_vtab", + /* 299 */ "vtabarglist", + /* 300 */ "vtabarg", + /* 301 */ "vtabargtoken", + /* 302 */ "lp", + /* 303 */ "anylist", + /* 304 */ "wqitem", + /* 305 */ "wqas", + /* 306 */ "windowdefn_list", + /* 307 */ "windowdefn", + /* 308 */ "window", + /* 309 */ "frame_opt", + /* 310 */ "part_opt", + /* 311 */ "filter_clause", + /* 312 */ "over_clause", + /* 313 */ "range_or_rows", + /* 314 */ "frame_bound", + /* 315 */ "frame_bound_s", + /* 316 */ "frame_bound_e", + /* 317 */ "frame_exclude_opt", + /* 318 */ "frame_exclude", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ @@ -158869,385 +168757,392 @@ static const char *const yyRuleName[] = { /* 16 */ "ifnotexists ::= IF NOT EXISTS", /* 17 */ "temp ::= TEMP", /* 18 */ "temp ::=", - /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP table_options", + /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP table_option_set", /* 20 */ "create_table_args ::= AS select", - /* 21 */ "table_options ::=", - /* 22 */ "table_options ::= WITHOUT nm", - /* 23 */ "columnname ::= nm typetoken", - /* 24 */ "typetoken ::=", - /* 25 */ "typetoken ::= typename LP signed RP", - /* 26 */ "typetoken ::= typename LP signed COMMA signed RP", - /* 27 */ "typename ::= typename ID|STRING", - /* 28 */ "scanpt ::=", - /* 29 */ "scantok ::=", - /* 30 */ "ccons ::= CONSTRAINT nm", - /* 31 */ "ccons ::= DEFAULT scantok term", - /* 32 */ "ccons ::= DEFAULT LP expr RP", - /* 33 */ "ccons ::= DEFAULT PLUS scantok term", - /* 34 */ "ccons ::= DEFAULT MINUS scantok term", - /* 35 */ "ccons ::= DEFAULT scantok ID|INDEXED", - /* 36 */ "ccons ::= NOT NULL onconf", - /* 37 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", - /* 38 */ "ccons ::= UNIQUE onconf", - /* 39 */ "ccons ::= CHECK LP expr RP", - /* 40 */ "ccons ::= REFERENCES nm eidlist_opt refargs", - /* 41 */ "ccons ::= defer_subclause", - /* 42 */ "ccons ::= COLLATE ID|STRING", - /* 43 */ "generated ::= LP expr RP", - /* 44 */ "generated ::= LP expr RP ID", - /* 45 */ "autoinc ::=", - /* 46 */ "autoinc ::= AUTOINCR", - /* 47 */ "refargs ::=", - /* 48 */ "refargs ::= refargs refarg", - /* 49 */ "refarg ::= MATCH nm", - /* 50 */ "refarg ::= ON INSERT refact", - /* 51 */ "refarg ::= ON DELETE refact", - /* 52 */ "refarg ::= ON UPDATE refact", - /* 53 */ "refact ::= SET NULL", - /* 54 */ "refact ::= SET DEFAULT", - /* 55 */ "refact ::= CASCADE", - /* 56 */ "refact ::= RESTRICT", - /* 57 */ "refact ::= NO ACTION", - /* 58 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", - /* 59 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", - /* 60 */ "init_deferred_pred_opt ::=", - /* 61 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", - /* 62 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", - /* 63 */ "conslist_opt ::=", - /* 64 */ "tconscomma ::= COMMA", - /* 65 */ "tcons ::= CONSTRAINT nm", - /* 66 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf", - /* 67 */ "tcons ::= UNIQUE LP sortlist RP onconf", - /* 68 */ "tcons ::= CHECK LP expr RP onconf", - /* 69 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt", - /* 70 */ "defer_subclause_opt ::=", - /* 71 */ "onconf ::=", - /* 72 */ "onconf ::= ON CONFLICT resolvetype", - /* 73 */ "orconf ::=", - /* 74 */ "orconf ::= OR resolvetype", - /* 75 */ "resolvetype ::= IGNORE", - /* 76 */ "resolvetype ::= REPLACE", - /* 77 */ "cmd ::= DROP TABLE ifexists fullname", - /* 78 */ "ifexists ::= IF EXISTS", - /* 79 */ "ifexists ::=", - /* 80 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select", - /* 81 */ "cmd ::= DROP VIEW ifexists fullname", - /* 82 */ "cmd ::= select", - /* 83 */ "select ::= WITH wqlist selectnowith", - /* 84 */ "select ::= WITH RECURSIVE wqlist selectnowith", - /* 85 */ "select ::= selectnowith", - /* 86 */ "selectnowith ::= selectnowith multiselect_op oneselect", - /* 87 */ "multiselect_op ::= UNION", - /* 88 */ "multiselect_op ::= UNION ALL", - /* 89 */ "multiselect_op ::= EXCEPT|INTERSECT", - /* 90 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", - /* 91 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", - /* 92 */ "values ::= VALUES LP nexprlist RP", - /* 93 */ "values ::= values COMMA LP nexprlist RP", - /* 94 */ "distinct ::= DISTINCT", - /* 95 */ "distinct ::= ALL", - /* 96 */ "distinct ::=", - /* 97 */ "sclp ::=", - /* 98 */ "selcollist ::= sclp scanpt expr scanpt as", - /* 99 */ "selcollist ::= sclp scanpt STAR", - /* 100 */ "selcollist ::= sclp scanpt nm DOT STAR", - /* 101 */ "as ::= AS nm", - /* 102 */ "as ::=", - /* 103 */ "from ::=", - /* 104 */ "from ::= FROM seltablist", - /* 105 */ "stl_prefix ::= seltablist joinop", - /* 106 */ "stl_prefix ::=", - /* 107 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", - /* 108 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt", - /* 109 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", - /* 110 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", - /* 111 */ "dbnm ::=", - /* 112 */ "dbnm ::= DOT nm", - /* 113 */ "fullname ::= nm", - /* 114 */ "fullname ::= nm DOT nm", - /* 115 */ "xfullname ::= nm", - /* 116 */ "xfullname ::= nm DOT nm", - /* 117 */ "xfullname ::= nm DOT nm AS nm", - /* 118 */ "xfullname ::= nm AS nm", - /* 119 */ "joinop ::= COMMA|JOIN", - /* 120 */ "joinop ::= JOIN_KW JOIN", - /* 121 */ "joinop ::= JOIN_KW nm JOIN", - /* 122 */ "joinop ::= JOIN_KW nm nm JOIN", - /* 123 */ "on_opt ::= ON expr", - /* 124 */ "on_opt ::=", - /* 125 */ "indexed_opt ::=", - /* 126 */ "indexed_opt ::= INDEXED BY nm", - /* 127 */ "indexed_opt ::= NOT INDEXED", - /* 128 */ "using_opt ::= USING LP idlist RP", - /* 129 */ "using_opt ::=", - /* 130 */ "orderby_opt ::=", - /* 131 */ "orderby_opt ::= ORDER BY sortlist", - /* 132 */ "sortlist ::= sortlist COMMA expr sortorder nulls", - /* 133 */ "sortlist ::= expr sortorder nulls", - /* 134 */ "sortorder ::= ASC", - /* 135 */ "sortorder ::= DESC", - /* 136 */ "sortorder ::=", - /* 137 */ "nulls ::= NULLS FIRST", - /* 138 */ "nulls ::= NULLS LAST", - /* 139 */ "nulls ::=", - /* 140 */ "groupby_opt ::=", - /* 141 */ "groupby_opt ::= GROUP BY nexprlist", - /* 142 */ "having_opt ::=", - /* 143 */ "having_opt ::= HAVING expr", - /* 144 */ "limit_opt ::=", - /* 145 */ "limit_opt ::= LIMIT expr", - /* 146 */ "limit_opt ::= LIMIT expr OFFSET expr", - /* 147 */ "limit_opt ::= LIMIT expr COMMA expr", - /* 148 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret", - /* 149 */ "where_opt ::=", - /* 150 */ "where_opt ::= WHERE expr", - /* 151 */ "where_opt_ret ::=", - /* 152 */ "where_opt_ret ::= WHERE expr", - /* 153 */ "where_opt_ret ::= RETURNING selcollist", - /* 154 */ "where_opt_ret ::= WHERE expr RETURNING selcollist", - /* 155 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret", - /* 156 */ "setlist ::= setlist COMMA nm EQ expr", - /* 157 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", - /* 158 */ "setlist ::= nm EQ expr", - /* 159 */ "setlist ::= LP idlist RP EQ expr", - /* 160 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", - /* 161 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning", - /* 162 */ "upsert ::=", - /* 163 */ "upsert ::= RETURNING selcollist", - /* 164 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert", - /* 165 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert", - /* 166 */ "upsert ::= ON CONFLICT DO NOTHING returning", - /* 167 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning", - /* 168 */ "returning ::= RETURNING selcollist", - /* 169 */ "insert_cmd ::= INSERT orconf", - /* 170 */ "insert_cmd ::= REPLACE", - /* 171 */ "idlist_opt ::=", - /* 172 */ "idlist_opt ::= LP idlist RP", - /* 173 */ "idlist ::= idlist COMMA nm", - /* 174 */ "idlist ::= nm", - /* 175 */ "expr ::= LP expr RP", - /* 176 */ "expr ::= ID|INDEXED", - /* 177 */ "expr ::= JOIN_KW", - /* 178 */ "expr ::= nm DOT nm", - /* 179 */ "expr ::= nm DOT nm DOT nm", - /* 180 */ "term ::= NULL|FLOAT|BLOB", - /* 181 */ "term ::= STRING", - /* 182 */ "term ::= INTEGER", - /* 183 */ "expr ::= VARIABLE", - /* 184 */ "expr ::= expr COLLATE ID|STRING", - /* 185 */ "expr ::= CAST LP expr AS typetoken RP", - /* 186 */ "expr ::= ID|INDEXED LP distinct exprlist RP", - /* 187 */ "expr ::= ID|INDEXED LP STAR RP", - /* 188 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over", - /* 189 */ "expr ::= ID|INDEXED LP STAR RP filter_over", - /* 190 */ "term ::= CTIME_KW", - /* 191 */ "expr ::= LP nexprlist COMMA expr RP", - /* 192 */ "expr ::= expr AND expr", - /* 193 */ "expr ::= expr OR expr", - /* 194 */ "expr ::= expr LT|GT|GE|LE expr", - /* 195 */ "expr ::= expr EQ|NE expr", - /* 196 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", - /* 197 */ "expr ::= expr PLUS|MINUS expr", - /* 198 */ "expr ::= expr STAR|SLASH|REM expr", - /* 199 */ "expr ::= expr CONCAT expr", - /* 200 */ "likeop ::= NOT LIKE_KW|MATCH", - /* 201 */ "expr ::= expr likeop expr", - /* 202 */ "expr ::= expr likeop expr ESCAPE expr", - /* 203 */ "expr ::= expr ISNULL|NOTNULL", - /* 204 */ "expr ::= expr NOT NULL", - /* 205 */ "expr ::= expr IS expr", - /* 206 */ "expr ::= expr IS NOT expr", - /* 207 */ "expr ::= NOT expr", - /* 208 */ "expr ::= BITNOT expr", - /* 209 */ "expr ::= PLUS|MINUS expr", - /* 210 */ "between_op ::= BETWEEN", - /* 211 */ "between_op ::= NOT BETWEEN", - /* 212 */ "expr ::= expr between_op expr AND expr", - /* 213 */ "in_op ::= IN", - /* 214 */ "in_op ::= NOT IN", - /* 215 */ "expr ::= expr in_op LP exprlist RP", - /* 216 */ "expr ::= LP select RP", - /* 217 */ "expr ::= expr in_op LP select RP", - /* 218 */ "expr ::= expr in_op nm dbnm paren_exprlist", - /* 219 */ "expr ::= EXISTS LP select RP", - /* 220 */ "expr ::= CASE case_operand case_exprlist case_else END", - /* 221 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", - /* 222 */ "case_exprlist ::= WHEN expr THEN expr", - /* 223 */ "case_else ::= ELSE expr", - /* 224 */ "case_else ::=", - /* 225 */ "case_operand ::= expr", - /* 226 */ "case_operand ::=", - /* 227 */ "exprlist ::=", - /* 228 */ "nexprlist ::= nexprlist COMMA expr", - /* 229 */ "nexprlist ::= expr", - /* 230 */ "paren_exprlist ::=", - /* 231 */ "paren_exprlist ::= LP exprlist RP", - /* 232 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", - /* 233 */ "uniqueflag ::= UNIQUE", - /* 234 */ "uniqueflag ::=", - /* 235 */ "eidlist_opt ::=", - /* 236 */ "eidlist_opt ::= LP eidlist RP", - /* 237 */ "eidlist ::= eidlist COMMA nm collate sortorder", - /* 238 */ "eidlist ::= nm collate sortorder", - /* 239 */ "collate ::=", - /* 240 */ "collate ::= COLLATE ID|STRING", - /* 241 */ "cmd ::= DROP INDEX ifexists fullname", - /* 242 */ "cmd ::= VACUUM vinto", - /* 243 */ "cmd ::= VACUUM nm vinto", - /* 244 */ "vinto ::= INTO expr", - /* 245 */ "vinto ::=", - /* 246 */ "cmd ::= PRAGMA nm dbnm", - /* 247 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", - /* 248 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", - /* 249 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 250 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", - /* 251 */ "plus_num ::= PLUS INTEGER|FLOAT", - /* 252 */ "minus_num ::= MINUS INTEGER|FLOAT", - /* 253 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", - /* 254 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", - /* 255 */ "trigger_time ::= BEFORE|AFTER", - /* 256 */ "trigger_time ::= INSTEAD OF", - /* 257 */ "trigger_time ::=", - /* 258 */ "trigger_event ::= DELETE|INSERT", - /* 259 */ "trigger_event ::= UPDATE", - /* 260 */ "trigger_event ::= UPDATE OF idlist", - /* 261 */ "when_clause ::=", - /* 262 */ "when_clause ::= WHEN expr", - /* 263 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", - /* 264 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 265 */ "trnm ::= nm DOT nm", - /* 266 */ "tridxby ::= INDEXED BY nm", - /* 267 */ "tridxby ::= NOT INDEXED", - /* 268 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", - /* 269 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", - /* 270 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", - /* 271 */ "trigger_cmd ::= scanpt select scanpt", - /* 272 */ "expr ::= RAISE LP IGNORE RP", - /* 273 */ "expr ::= RAISE LP raisetype COMMA nm RP", - /* 274 */ "raisetype ::= ROLLBACK", - /* 275 */ "raisetype ::= ABORT", - /* 276 */ "raisetype ::= FAIL", - /* 277 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 278 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 279 */ "cmd ::= DETACH database_kw_opt expr", - /* 280 */ "key_opt ::=", - /* 281 */ "key_opt ::= KEY expr", - /* 282 */ "cmd ::= REINDEX", - /* 283 */ "cmd ::= REINDEX nm dbnm", - /* 284 */ "cmd ::= ANALYZE", - /* 285 */ "cmd ::= ANALYZE nm dbnm", - /* 286 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 287 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", - /* 288 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", - /* 289 */ "add_column_fullname ::= fullname", - /* 290 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", - /* 291 */ "cmd ::= create_vtab", - /* 292 */ "cmd ::= create_vtab LP vtabarglist RP", - /* 293 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", - /* 294 */ "vtabarg ::=", - /* 295 */ "vtabargtoken ::= ANY", - /* 296 */ "vtabargtoken ::= lp anylist RP", - /* 297 */ "lp ::= LP", - /* 298 */ "with ::= WITH wqlist", - /* 299 */ "with ::= WITH RECURSIVE wqlist", - /* 300 */ "wqas ::= AS", - /* 301 */ "wqas ::= AS MATERIALIZED", - /* 302 */ "wqas ::= AS NOT MATERIALIZED", - /* 303 */ "wqitem ::= nm eidlist_opt wqas LP select RP", - /* 304 */ "wqlist ::= wqitem", - /* 305 */ "wqlist ::= wqlist COMMA wqitem", - /* 306 */ "windowdefn_list ::= windowdefn", - /* 307 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", - /* 308 */ "windowdefn ::= nm AS LP window RP", - /* 309 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", - /* 310 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", - /* 311 */ "window ::= ORDER BY sortlist frame_opt", - /* 312 */ "window ::= nm ORDER BY sortlist frame_opt", - /* 313 */ "window ::= frame_opt", - /* 314 */ "window ::= nm frame_opt", - /* 315 */ "frame_opt ::=", - /* 316 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", - /* 317 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", - /* 318 */ "range_or_rows ::= RANGE|ROWS|GROUPS", - /* 319 */ "frame_bound_s ::= frame_bound", - /* 320 */ "frame_bound_s ::= UNBOUNDED PRECEDING", - /* 321 */ "frame_bound_e ::= frame_bound", - /* 322 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", - /* 323 */ "frame_bound ::= expr PRECEDING|FOLLOWING", - /* 324 */ "frame_bound ::= CURRENT ROW", - /* 325 */ "frame_exclude_opt ::=", - /* 326 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", - /* 327 */ "frame_exclude ::= NO OTHERS", - /* 328 */ "frame_exclude ::= CURRENT ROW", - /* 329 */ "frame_exclude ::= GROUP|TIES", - /* 330 */ "window_clause ::= WINDOW windowdefn_list", - /* 331 */ "filter_over ::= filter_clause over_clause", - /* 332 */ "filter_over ::= over_clause", - /* 333 */ "filter_over ::= filter_clause", - /* 334 */ "over_clause ::= OVER LP window RP", - /* 335 */ "over_clause ::= OVER nm", - /* 336 */ "filter_clause ::= FILTER LP WHERE expr RP", - /* 337 */ "input ::= cmdlist", - /* 338 */ "cmdlist ::= cmdlist ecmd", - /* 339 */ "cmdlist ::= ecmd", - /* 340 */ "ecmd ::= SEMI", - /* 341 */ "ecmd ::= cmdx SEMI", - /* 342 */ "ecmd ::= explain cmdx SEMI", - /* 343 */ "trans_opt ::=", - /* 344 */ "trans_opt ::= TRANSACTION", - /* 345 */ "trans_opt ::= TRANSACTION nm", - /* 346 */ "savepoint_opt ::= SAVEPOINT", - /* 347 */ "savepoint_opt ::=", - /* 348 */ "cmd ::= create_table create_table_args", - /* 349 */ "columnlist ::= columnlist COMMA columnname carglist", - /* 350 */ "columnlist ::= columnname carglist", - /* 351 */ "nm ::= ID|INDEXED", - /* 352 */ "nm ::= STRING", - /* 353 */ "nm ::= JOIN_KW", - /* 354 */ "typetoken ::= typename", - /* 355 */ "typename ::= ID|STRING", - /* 356 */ "signed ::= plus_num", - /* 357 */ "signed ::= minus_num", - /* 358 */ "carglist ::= carglist ccons", - /* 359 */ "carglist ::=", - /* 360 */ "ccons ::= NULL onconf", - /* 361 */ "ccons ::= GENERATED ALWAYS AS generated", - /* 362 */ "ccons ::= AS generated", - /* 363 */ "conslist_opt ::= COMMA conslist", - /* 364 */ "conslist ::= conslist tconscomma tcons", - /* 365 */ "conslist ::= tcons", - /* 366 */ "tconscomma ::=", - /* 367 */ "defer_subclause_opt ::= defer_subclause", - /* 368 */ "resolvetype ::= raisetype", - /* 369 */ "selectnowith ::= oneselect", - /* 370 */ "oneselect ::= values", - /* 371 */ "sclp ::= selcollist COMMA", - /* 372 */ "as ::= ID|STRING", - /* 373 */ "returning ::=", - /* 374 */ "expr ::= term", - /* 375 */ "likeop ::= LIKE_KW|MATCH", - /* 376 */ "exprlist ::= nexprlist", - /* 377 */ "nmnum ::= plus_num", - /* 378 */ "nmnum ::= nm", - /* 379 */ "nmnum ::= ON", - /* 380 */ "nmnum ::= DELETE", - /* 381 */ "nmnum ::= DEFAULT", - /* 382 */ "plus_num ::= INTEGER|FLOAT", - /* 383 */ "foreach_clause ::=", - /* 384 */ "foreach_clause ::= FOR EACH ROW", - /* 385 */ "trnm ::= nm", - /* 386 */ "tridxby ::=", - /* 387 */ "database_kw_opt ::= DATABASE", - /* 388 */ "database_kw_opt ::=", - /* 389 */ "kwcolumn_opt ::=", - /* 390 */ "kwcolumn_opt ::= COLUMNKW", - /* 391 */ "vtabarglist ::= vtabarg", - /* 392 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 393 */ "vtabarg ::= vtabarg vtabargtoken", - /* 394 */ "anylist ::=", - /* 395 */ "anylist ::= anylist LP anylist RP", - /* 396 */ "anylist ::= anylist ANY", - /* 397 */ "with ::=", + /* 21 */ "table_option_set ::=", + /* 22 */ "table_option_set ::= table_option_set COMMA table_option", + /* 23 */ "table_option ::= WITHOUT nm", + /* 24 */ "table_option ::= nm", + /* 25 */ "columnname ::= nm typetoken", + /* 26 */ "typetoken ::=", + /* 27 */ "typetoken ::= typename LP signed RP", + /* 28 */ "typetoken ::= typename LP signed COMMA signed RP", + /* 29 */ "typename ::= typename ID|STRING", + /* 30 */ "scanpt ::=", + /* 31 */ "scantok ::=", + /* 32 */ "ccons ::= CONSTRAINT nm", + /* 33 */ "ccons ::= DEFAULT scantok term", + /* 34 */ "ccons ::= DEFAULT LP expr RP", + /* 35 */ "ccons ::= DEFAULT PLUS scantok term", + /* 36 */ "ccons ::= DEFAULT MINUS scantok term", + /* 37 */ "ccons ::= DEFAULT scantok ID|INDEXED", + /* 38 */ "ccons ::= NOT NULL onconf", + /* 39 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", + /* 40 */ "ccons ::= UNIQUE onconf", + /* 41 */ "ccons ::= CHECK LP expr RP", + /* 42 */ "ccons ::= REFERENCES nm eidlist_opt refargs", + /* 43 */ "ccons ::= defer_subclause", + /* 44 */ "ccons ::= COLLATE ID|STRING", + /* 45 */ "generated ::= LP expr RP", + /* 46 */ "generated ::= LP expr RP ID", + /* 47 */ "autoinc ::=", + /* 48 */ "autoinc ::= AUTOINCR", + /* 49 */ "refargs ::=", + /* 50 */ "refargs ::= refargs refarg", + /* 51 */ "refarg ::= MATCH nm", + /* 52 */ "refarg ::= ON INSERT refact", + /* 53 */ "refarg ::= ON DELETE refact", + /* 54 */ "refarg ::= ON UPDATE refact", + /* 55 */ "refact ::= SET NULL", + /* 56 */ "refact ::= SET DEFAULT", + /* 57 */ "refact ::= CASCADE", + /* 58 */ "refact ::= RESTRICT", + /* 59 */ "refact ::= NO ACTION", + /* 60 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", + /* 61 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", + /* 62 */ "init_deferred_pred_opt ::=", + /* 63 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", + /* 64 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", + /* 65 */ "conslist_opt ::=", + /* 66 */ "tconscomma ::= COMMA", + /* 67 */ "tcons ::= CONSTRAINT nm", + /* 68 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf", + /* 69 */ "tcons ::= UNIQUE LP sortlist RP onconf", + /* 70 */ "tcons ::= CHECK LP expr RP onconf", + /* 71 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt", + /* 72 */ "defer_subclause_opt ::=", + /* 73 */ "onconf ::=", + /* 74 */ "onconf ::= ON CONFLICT resolvetype", + /* 75 */ "orconf ::=", + /* 76 */ "orconf ::= OR resolvetype", + /* 77 */ "resolvetype ::= IGNORE", + /* 78 */ "resolvetype ::= REPLACE", + /* 79 */ "cmd ::= DROP TABLE ifexists fullname", + /* 80 */ "ifexists ::= IF EXISTS", + /* 81 */ "ifexists ::=", + /* 82 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select", + /* 83 */ "cmd ::= DROP VIEW ifexists fullname", + /* 84 */ "cmd ::= select", + /* 85 */ "select ::= WITH wqlist selectnowith", + /* 86 */ "select ::= WITH RECURSIVE wqlist selectnowith", + /* 87 */ "select ::= selectnowith", + /* 88 */ "selectnowith ::= selectnowith multiselect_op oneselect", + /* 89 */ "multiselect_op ::= UNION", + /* 90 */ "multiselect_op ::= UNION ALL", + /* 91 */ "multiselect_op ::= EXCEPT|INTERSECT", + /* 92 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", + /* 93 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", + /* 94 */ "values ::= VALUES LP nexprlist RP", + /* 95 */ "values ::= values COMMA LP nexprlist RP", + /* 96 */ "distinct ::= DISTINCT", + /* 97 */ "distinct ::= ALL", + /* 98 */ "distinct ::=", + /* 99 */ "sclp ::=", + /* 100 */ "selcollist ::= sclp scanpt expr scanpt as", + /* 101 */ "selcollist ::= sclp scanpt STAR", + /* 102 */ "selcollist ::= sclp scanpt nm DOT STAR", + /* 103 */ "as ::= AS nm", + /* 104 */ "as ::=", + /* 105 */ "from ::=", + /* 106 */ "from ::= FROM seltablist", + /* 107 */ "stl_prefix ::= seltablist joinop", + /* 108 */ "stl_prefix ::=", + /* 109 */ "seltablist ::= stl_prefix nm dbnm as on_using", + /* 110 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using", + /* 111 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using", + /* 112 */ "seltablist ::= stl_prefix LP select RP as on_using", + /* 113 */ "seltablist ::= stl_prefix LP seltablist RP as on_using", + /* 114 */ "dbnm ::=", + /* 115 */ "dbnm ::= DOT nm", + /* 116 */ "fullname ::= nm", + /* 117 */ "fullname ::= nm DOT nm", + /* 118 */ "xfullname ::= nm", + /* 119 */ "xfullname ::= nm DOT nm", + /* 120 */ "xfullname ::= nm DOT nm AS nm", + /* 121 */ "xfullname ::= nm AS nm", + /* 122 */ "joinop ::= COMMA|JOIN", + /* 123 */ "joinop ::= JOIN_KW JOIN", + /* 124 */ "joinop ::= JOIN_KW nm JOIN", + /* 125 */ "joinop ::= JOIN_KW nm nm JOIN", + /* 126 */ "on_using ::= ON expr", + /* 127 */ "on_using ::= USING LP idlist RP", + /* 128 */ "on_using ::=", + /* 129 */ "indexed_opt ::=", + /* 130 */ "indexed_by ::= INDEXED BY nm", + /* 131 */ "indexed_by ::= NOT INDEXED", + /* 132 */ "orderby_opt ::=", + /* 133 */ "orderby_opt ::= ORDER BY sortlist", + /* 134 */ "sortlist ::= sortlist COMMA expr sortorder nulls", + /* 135 */ "sortlist ::= expr sortorder nulls", + /* 136 */ "sortorder ::= ASC", + /* 137 */ "sortorder ::= DESC", + /* 138 */ "sortorder ::=", + /* 139 */ "nulls ::= NULLS FIRST", + /* 140 */ "nulls ::= NULLS LAST", + /* 141 */ "nulls ::=", + /* 142 */ "groupby_opt ::=", + /* 143 */ "groupby_opt ::= GROUP BY nexprlist", + /* 144 */ "having_opt ::=", + /* 145 */ "having_opt ::= HAVING expr", + /* 146 */ "limit_opt ::=", + /* 147 */ "limit_opt ::= LIMIT expr", + /* 148 */ "limit_opt ::= LIMIT expr OFFSET expr", + /* 149 */ "limit_opt ::= LIMIT expr COMMA expr", + /* 150 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret", + /* 151 */ "where_opt ::=", + /* 152 */ "where_opt ::= WHERE expr", + /* 153 */ "where_opt_ret ::=", + /* 154 */ "where_opt_ret ::= WHERE expr", + /* 155 */ "where_opt_ret ::= RETURNING selcollist", + /* 156 */ "where_opt_ret ::= WHERE expr RETURNING selcollist", + /* 157 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret", + /* 158 */ "setlist ::= setlist COMMA nm EQ expr", + /* 159 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", + /* 160 */ "setlist ::= nm EQ expr", + /* 161 */ "setlist ::= LP idlist RP EQ expr", + /* 162 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", + /* 163 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning", + /* 164 */ "upsert ::=", + /* 165 */ "upsert ::= RETURNING selcollist", + /* 166 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert", + /* 167 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert", + /* 168 */ "upsert ::= ON CONFLICT DO NOTHING returning", + /* 169 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning", + /* 170 */ "returning ::= RETURNING selcollist", + /* 171 */ "insert_cmd ::= INSERT orconf", + /* 172 */ "insert_cmd ::= REPLACE", + /* 173 */ "idlist_opt ::=", + /* 174 */ "idlist_opt ::= LP idlist RP", + /* 175 */ "idlist ::= idlist COMMA nm", + /* 176 */ "idlist ::= nm", + /* 177 */ "expr ::= LP expr RP", + /* 178 */ "expr ::= ID|INDEXED", + /* 179 */ "expr ::= JOIN_KW", + /* 180 */ "expr ::= nm DOT nm", + /* 181 */ "expr ::= nm DOT nm DOT nm", + /* 182 */ "term ::= NULL|FLOAT|BLOB", + /* 183 */ "term ::= STRING", + /* 184 */ "term ::= INTEGER", + /* 185 */ "expr ::= VARIABLE", + /* 186 */ "expr ::= expr COLLATE ID|STRING", + /* 187 */ "expr ::= CAST LP expr AS typetoken RP", + /* 188 */ "expr ::= ID|INDEXED LP distinct exprlist RP", + /* 189 */ "expr ::= ID|INDEXED LP STAR RP", + /* 190 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over", + /* 191 */ "expr ::= ID|INDEXED LP STAR RP filter_over", + /* 192 */ "term ::= CTIME_KW", + /* 193 */ "expr ::= LP nexprlist COMMA expr RP", + /* 194 */ "expr ::= expr AND expr", + /* 195 */ "expr ::= expr OR expr", + /* 196 */ "expr ::= expr LT|GT|GE|LE expr", + /* 197 */ "expr ::= expr EQ|NE expr", + /* 198 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", + /* 199 */ "expr ::= expr PLUS|MINUS expr", + /* 200 */ "expr ::= expr STAR|SLASH|REM expr", + /* 201 */ "expr ::= expr CONCAT expr", + /* 202 */ "likeop ::= NOT LIKE_KW|MATCH", + /* 203 */ "expr ::= expr likeop expr", + /* 204 */ "expr ::= expr likeop expr ESCAPE expr", + /* 205 */ "expr ::= expr ISNULL|NOTNULL", + /* 206 */ "expr ::= expr NOT NULL", + /* 207 */ "expr ::= expr IS expr", + /* 208 */ "expr ::= expr IS NOT expr", + /* 209 */ "expr ::= expr IS NOT DISTINCT FROM expr", + /* 210 */ "expr ::= expr IS DISTINCT FROM expr", + /* 211 */ "expr ::= NOT expr", + /* 212 */ "expr ::= BITNOT expr", + /* 213 */ "expr ::= PLUS|MINUS expr", + /* 214 */ "expr ::= expr PTR expr", + /* 215 */ "between_op ::= BETWEEN", + /* 216 */ "between_op ::= NOT BETWEEN", + /* 217 */ "expr ::= expr between_op expr AND expr", + /* 218 */ "in_op ::= IN", + /* 219 */ "in_op ::= NOT IN", + /* 220 */ "expr ::= expr in_op LP exprlist RP", + /* 221 */ "expr ::= LP select RP", + /* 222 */ "expr ::= expr in_op LP select RP", + /* 223 */ "expr ::= expr in_op nm dbnm paren_exprlist", + /* 224 */ "expr ::= EXISTS LP select RP", + /* 225 */ "expr ::= CASE case_operand case_exprlist case_else END", + /* 226 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 227 */ "case_exprlist ::= WHEN expr THEN expr", + /* 228 */ "case_else ::= ELSE expr", + /* 229 */ "case_else ::=", + /* 230 */ "case_operand ::= expr", + /* 231 */ "case_operand ::=", + /* 232 */ "exprlist ::=", + /* 233 */ "nexprlist ::= nexprlist COMMA expr", + /* 234 */ "nexprlist ::= expr", + /* 235 */ "paren_exprlist ::=", + /* 236 */ "paren_exprlist ::= LP exprlist RP", + /* 237 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", + /* 238 */ "uniqueflag ::= UNIQUE", + /* 239 */ "uniqueflag ::=", + /* 240 */ "eidlist_opt ::=", + /* 241 */ "eidlist_opt ::= LP eidlist RP", + /* 242 */ "eidlist ::= eidlist COMMA nm collate sortorder", + /* 243 */ "eidlist ::= nm collate sortorder", + /* 244 */ "collate ::=", + /* 245 */ "collate ::= COLLATE ID|STRING", + /* 246 */ "cmd ::= DROP INDEX ifexists fullname", + /* 247 */ "cmd ::= VACUUM vinto", + /* 248 */ "cmd ::= VACUUM nm vinto", + /* 249 */ "vinto ::= INTO expr", + /* 250 */ "vinto ::=", + /* 251 */ "cmd ::= PRAGMA nm dbnm", + /* 252 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 253 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 254 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 255 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 256 */ "plus_num ::= PLUS INTEGER|FLOAT", + /* 257 */ "minus_num ::= MINUS INTEGER|FLOAT", + /* 258 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 259 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 260 */ "trigger_time ::= BEFORE|AFTER", + /* 261 */ "trigger_time ::= INSTEAD OF", + /* 262 */ "trigger_time ::=", + /* 263 */ "trigger_event ::= DELETE|INSERT", + /* 264 */ "trigger_event ::= UPDATE", + /* 265 */ "trigger_event ::= UPDATE OF idlist", + /* 266 */ "when_clause ::=", + /* 267 */ "when_clause ::= WHEN expr", + /* 268 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 269 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 270 */ "trnm ::= nm DOT nm", + /* 271 */ "tridxby ::= INDEXED BY nm", + /* 272 */ "tridxby ::= NOT INDEXED", + /* 273 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", + /* 274 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", + /* 275 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", + /* 276 */ "trigger_cmd ::= scanpt select scanpt", + /* 277 */ "expr ::= RAISE LP IGNORE RP", + /* 278 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 279 */ "raisetype ::= ROLLBACK", + /* 280 */ "raisetype ::= ABORT", + /* 281 */ "raisetype ::= FAIL", + /* 282 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 283 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 284 */ "cmd ::= DETACH database_kw_opt expr", + /* 285 */ "key_opt ::=", + /* 286 */ "key_opt ::= KEY expr", + /* 287 */ "cmd ::= REINDEX", + /* 288 */ "cmd ::= REINDEX nm dbnm", + /* 289 */ "cmd ::= ANALYZE", + /* 290 */ "cmd ::= ANALYZE nm dbnm", + /* 291 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 292 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", + /* 293 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", + /* 294 */ "add_column_fullname ::= fullname", + /* 295 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", + /* 296 */ "cmd ::= create_vtab", + /* 297 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 298 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 299 */ "vtabarg ::=", + /* 300 */ "vtabargtoken ::= ANY", + /* 301 */ "vtabargtoken ::= lp anylist RP", + /* 302 */ "lp ::= LP", + /* 303 */ "with ::= WITH wqlist", + /* 304 */ "with ::= WITH RECURSIVE wqlist", + /* 305 */ "wqas ::= AS", + /* 306 */ "wqas ::= AS MATERIALIZED", + /* 307 */ "wqas ::= AS NOT MATERIALIZED", + /* 308 */ "wqitem ::= nm eidlist_opt wqas LP select RP", + /* 309 */ "wqlist ::= wqitem", + /* 310 */ "wqlist ::= wqlist COMMA wqitem", + /* 311 */ "windowdefn_list ::= windowdefn", + /* 312 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 313 */ "windowdefn ::= nm AS LP window RP", + /* 314 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 315 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 316 */ "window ::= ORDER BY sortlist frame_opt", + /* 317 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 318 */ "window ::= frame_opt", + /* 319 */ "window ::= nm frame_opt", + /* 320 */ "frame_opt ::=", + /* 321 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", + /* 322 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", + /* 323 */ "range_or_rows ::= RANGE|ROWS|GROUPS", + /* 324 */ "frame_bound_s ::= frame_bound", + /* 325 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 326 */ "frame_bound_e ::= frame_bound", + /* 327 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 328 */ "frame_bound ::= expr PRECEDING|FOLLOWING", + /* 329 */ "frame_bound ::= CURRENT ROW", + /* 330 */ "frame_exclude_opt ::=", + /* 331 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", + /* 332 */ "frame_exclude ::= NO OTHERS", + /* 333 */ "frame_exclude ::= CURRENT ROW", + /* 334 */ "frame_exclude ::= GROUP|TIES", + /* 335 */ "window_clause ::= WINDOW windowdefn_list", + /* 336 */ "filter_over ::= filter_clause over_clause", + /* 337 */ "filter_over ::= over_clause", + /* 338 */ "filter_over ::= filter_clause", + /* 339 */ "over_clause ::= OVER LP window RP", + /* 340 */ "over_clause ::= OVER nm", + /* 341 */ "filter_clause ::= FILTER LP WHERE expr RP", + /* 342 */ "input ::= cmdlist", + /* 343 */ "cmdlist ::= cmdlist ecmd", + /* 344 */ "cmdlist ::= ecmd", + /* 345 */ "ecmd ::= SEMI", + /* 346 */ "ecmd ::= cmdx SEMI", + /* 347 */ "ecmd ::= explain cmdx SEMI", + /* 348 */ "trans_opt ::=", + /* 349 */ "trans_opt ::= TRANSACTION", + /* 350 */ "trans_opt ::= TRANSACTION nm", + /* 351 */ "savepoint_opt ::= SAVEPOINT", + /* 352 */ "savepoint_opt ::=", + /* 353 */ "cmd ::= create_table create_table_args", + /* 354 */ "table_option_set ::= table_option", + /* 355 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 356 */ "columnlist ::= columnname carglist", + /* 357 */ "nm ::= ID|INDEXED", + /* 358 */ "nm ::= STRING", + /* 359 */ "nm ::= JOIN_KW", + /* 360 */ "typetoken ::= typename", + /* 361 */ "typename ::= ID|STRING", + /* 362 */ "signed ::= plus_num", + /* 363 */ "signed ::= minus_num", + /* 364 */ "carglist ::= carglist ccons", + /* 365 */ "carglist ::=", + /* 366 */ "ccons ::= NULL onconf", + /* 367 */ "ccons ::= GENERATED ALWAYS AS generated", + /* 368 */ "ccons ::= AS generated", + /* 369 */ "conslist_opt ::= COMMA conslist", + /* 370 */ "conslist ::= conslist tconscomma tcons", + /* 371 */ "conslist ::= tcons", + /* 372 */ "tconscomma ::=", + /* 373 */ "defer_subclause_opt ::= defer_subclause", + /* 374 */ "resolvetype ::= raisetype", + /* 375 */ "selectnowith ::= oneselect", + /* 376 */ "oneselect ::= values", + /* 377 */ "sclp ::= selcollist COMMA", + /* 378 */ "as ::= ID|STRING", + /* 379 */ "indexed_opt ::= indexed_by", + /* 380 */ "returning ::=", + /* 381 */ "expr ::= term", + /* 382 */ "likeop ::= LIKE_KW|MATCH", + /* 383 */ "exprlist ::= nexprlist", + /* 384 */ "nmnum ::= plus_num", + /* 385 */ "nmnum ::= nm", + /* 386 */ "nmnum ::= ON", + /* 387 */ "nmnum ::= DELETE", + /* 388 */ "nmnum ::= DEFAULT", + /* 389 */ "plus_num ::= INTEGER|FLOAT", + /* 390 */ "foreach_clause ::=", + /* 391 */ "foreach_clause ::= FOR EACH ROW", + /* 392 */ "trnm ::= nm", + /* 393 */ "tridxby ::=", + /* 394 */ "database_kw_opt ::= DATABASE", + /* 395 */ "database_kw_opt ::=", + /* 396 */ "kwcolumn_opt ::=", + /* 397 */ "kwcolumn_opt ::= COLUMNKW", + /* 398 */ "vtabarglist ::= vtabarg", + /* 399 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 400 */ "vtabarg ::= vtabarg vtabargtoken", + /* 401 */ "anylist ::=", + /* 402 */ "anylist ::= anylist LP anylist RP", + /* 403 */ "anylist ::= anylist ANY", + /* 404 */ "with ::=", }; #endif /* NDEBUG */ @@ -159373,99 +169268,97 @@ static void yy_destructor( ** inside the C code. */ /********* Begin destructor definitions ***************************************/ - case 202: /* select */ - case 236: /* selectnowith */ - case 237: /* oneselect */ - case 249: /* values */ + case 204: /* select */ + case 239: /* selectnowith */ + case 240: /* oneselect */ + case 252: /* values */ { -wx_sqlite3SelectDelete(pParse->db, (yypminor->yy307)); +wx_sqlite3SelectDelete(pParse->db, (yypminor->yy47)); } break; - case 213: /* term */ - case 214: /* expr */ - case 243: /* where_opt */ - case 245: /* having_opt */ - case 257: /* on_opt */ - case 264: /* where_opt_ret */ - case 275: /* case_operand */ - case 277: /* case_else */ - case 280: /* vinto */ - case 287: /* when_clause */ - case 292: /* key_opt */ - case 308: /* filter_clause */ + case 216: /* term */ + case 217: /* expr */ + case 246: /* where_opt */ + case 248: /* having_opt */ + case 267: /* where_opt_ret */ + case 278: /* case_operand */ + case 280: /* case_else */ + case 283: /* vinto */ + case 290: /* when_clause */ + case 295: /* key_opt */ + case 311: /* filter_clause */ { -wx_sqlite3ExprDelete(pParse->db, (yypminor->yy602)); +wx_sqlite3ExprDelete(pParse->db, (yypminor->yy528)); } break; - case 218: /* eidlist_opt */ - case 228: /* sortlist */ - case 229: /* eidlist */ - case 241: /* selcollist */ - case 244: /* groupby_opt */ - case 246: /* orderby_opt */ - case 250: /* nexprlist */ - case 251: /* sclp */ - case 259: /* exprlist */ - case 265: /* setlist */ - case 274: /* paren_exprlist */ - case 276: /* case_exprlist */ - case 307: /* part_opt */ + case 221: /* eidlist_opt */ + case 231: /* sortlist */ + case 232: /* eidlist */ + case 244: /* selcollist */ + case 247: /* groupby_opt */ + case 249: /* orderby_opt */ + case 253: /* nexprlist */ + case 254: /* sclp */ + case 261: /* exprlist */ + case 268: /* setlist */ + case 277: /* paren_exprlist */ + case 279: /* case_exprlist */ + case 310: /* part_opt */ { -wx_sqlite3ExprListDelete(pParse->db, (yypminor->yy338)); +wx_sqlite3ExprListDelete(pParse->db, (yypminor->yy322)); } break; - case 235: /* fullname */ - case 242: /* from */ - case 253: /* seltablist */ - case 254: /* stl_prefix */ - case 260: /* xfullname */ + case 238: /* fullname */ + case 245: /* from */ + case 256: /* seltablist */ + case 257: /* stl_prefix */ + case 262: /* xfullname */ { -wx_sqlite3SrcListDelete(pParse->db, (yypminor->yy291)); +wx_sqlite3SrcListDelete(pParse->db, (yypminor->yy131)); } break; - case 238: /* wqlist */ + case 241: /* wqlist */ { -wx_sqlite3WithDelete(pParse->db, (yypminor->yy195)); +wx_sqlite3WithDelete(pParse->db, (yypminor->yy521)); } break; - case 248: /* window_clause */ - case 303: /* windowdefn_list */ + case 251: /* window_clause */ + case 306: /* windowdefn_list */ { -wx_sqlite3WindowListDelete(pParse->db, (yypminor->yy19)); +wx_sqlite3WindowListDelete(pParse->db, (yypminor->yy41)); } break; - case 258: /* using_opt */ - case 261: /* idlist */ - case 267: /* idlist_opt */ + case 263: /* idlist */ + case 270: /* idlist_opt */ { -wx_sqlite3IdListDelete(pParse->db, (yypminor->yy288)); +wx_sqlite3IdListDelete(pParse->db, (yypminor->yy254)); } break; - case 270: /* filter_over */ - case 304: /* windowdefn */ - case 305: /* window */ - case 306: /* frame_opt */ - case 309: /* over_clause */ + case 273: /* filter_over */ + case 307: /* windowdefn */ + case 308: /* window */ + case 309: /* frame_opt */ + case 312: /* over_clause */ { -wx_sqlite3WindowDelete(pParse->db, (yypminor->yy19)); +wx_sqlite3WindowDelete(pParse->db, (yypminor->yy41)); } break; - case 283: /* trigger_cmd_list */ - case 288: /* trigger_cmd */ + case 286: /* trigger_cmd_list */ + case 291: /* trigger_cmd */ { -wx_sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy483)); +wx_sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy33)); } break; - case 285: /* trigger_event */ + case 288: /* trigger_event */ { -wx_sqlite3IdListDelete(pParse->db, (yypminor->yy50).b); +wx_sqlite3IdListDelete(pParse->db, (yypminor->yy180).b); } break; - case 311: /* frame_bound */ - case 312: /* frame_bound_s */ - case 313: /* frame_bound_e */ + case 314: /* frame_bound */ + case 315: /* frame_bound_s */ + case 316: /* frame_bound_e */ { -wx_sqlite3ExprDelete(pParse->db, (yypminor->yy113).pExpr); +wx_sqlite3ExprDelete(pParse->db, (yypminor->yy595).pExpr); } break; /********* End destructor definitions *****************************************/ @@ -159756,404 +169649,411 @@ static void yy_shift( /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side ** of that rule */ static const YYCODETYPE yyRuleInfoLhs[] = { - 187, /* (0) explain ::= EXPLAIN */ - 187, /* (1) explain ::= EXPLAIN QUERY PLAN */ - 186, /* (2) cmdx ::= cmd */ - 188, /* (3) cmd ::= BEGIN transtype trans_opt */ - 189, /* (4) transtype ::= */ - 189, /* (5) transtype ::= DEFERRED */ - 189, /* (6) transtype ::= IMMEDIATE */ - 189, /* (7) transtype ::= EXCLUSIVE */ - 188, /* (8) cmd ::= COMMIT|END trans_opt */ - 188, /* (9) cmd ::= ROLLBACK trans_opt */ - 188, /* (10) cmd ::= SAVEPOINT nm */ - 188, /* (11) cmd ::= RELEASE savepoint_opt nm */ - 188, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ - 193, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ - 195, /* (14) createkw ::= CREATE */ - 197, /* (15) ifnotexists ::= */ - 197, /* (16) ifnotexists ::= IF NOT EXISTS */ - 196, /* (17) temp ::= TEMP */ - 196, /* (18) temp ::= */ - 194, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ - 194, /* (20) create_table_args ::= AS select */ - 201, /* (21) table_options ::= */ - 201, /* (22) table_options ::= WITHOUT nm */ - 203, /* (23) columnname ::= nm typetoken */ - 205, /* (24) typetoken ::= */ - 205, /* (25) typetoken ::= typename LP signed RP */ - 205, /* (26) typetoken ::= typename LP signed COMMA signed RP */ - 206, /* (27) typename ::= typename ID|STRING */ - 210, /* (28) scanpt ::= */ - 211, /* (29) scantok ::= */ - 212, /* (30) ccons ::= CONSTRAINT nm */ - 212, /* (31) ccons ::= DEFAULT scantok term */ - 212, /* (32) ccons ::= DEFAULT LP expr RP */ - 212, /* (33) ccons ::= DEFAULT PLUS scantok term */ - 212, /* (34) ccons ::= DEFAULT MINUS scantok term */ - 212, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */ - 212, /* (36) ccons ::= NOT NULL onconf */ - 212, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */ - 212, /* (38) ccons ::= UNIQUE onconf */ - 212, /* (39) ccons ::= CHECK LP expr RP */ - 212, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */ - 212, /* (41) ccons ::= defer_subclause */ - 212, /* (42) ccons ::= COLLATE ID|STRING */ - 221, /* (43) generated ::= LP expr RP */ - 221, /* (44) generated ::= LP expr RP ID */ - 217, /* (45) autoinc ::= */ - 217, /* (46) autoinc ::= AUTOINCR */ - 219, /* (47) refargs ::= */ - 219, /* (48) refargs ::= refargs refarg */ - 222, /* (49) refarg ::= MATCH nm */ - 222, /* (50) refarg ::= ON INSERT refact */ - 222, /* (51) refarg ::= ON DELETE refact */ - 222, /* (52) refarg ::= ON UPDATE refact */ - 223, /* (53) refact ::= SET NULL */ - 223, /* (54) refact ::= SET DEFAULT */ - 223, /* (55) refact ::= CASCADE */ - 223, /* (56) refact ::= RESTRICT */ - 223, /* (57) refact ::= NO ACTION */ - 220, /* (58) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ - 220, /* (59) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - 224, /* (60) init_deferred_pred_opt ::= */ - 224, /* (61) init_deferred_pred_opt ::= INITIALLY DEFERRED */ - 224, /* (62) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ - 200, /* (63) conslist_opt ::= */ - 226, /* (64) tconscomma ::= COMMA */ - 227, /* (65) tcons ::= CONSTRAINT nm */ - 227, /* (66) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ - 227, /* (67) tcons ::= UNIQUE LP sortlist RP onconf */ - 227, /* (68) tcons ::= CHECK LP expr RP onconf */ - 227, /* (69) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ - 230, /* (70) defer_subclause_opt ::= */ - 215, /* (71) onconf ::= */ - 215, /* (72) onconf ::= ON CONFLICT resolvetype */ - 231, /* (73) orconf ::= */ - 231, /* (74) orconf ::= OR resolvetype */ - 232, /* (75) resolvetype ::= IGNORE */ - 232, /* (76) resolvetype ::= REPLACE */ - 188, /* (77) cmd ::= DROP TABLE ifexists fullname */ - 234, /* (78) ifexists ::= IF EXISTS */ - 234, /* (79) ifexists ::= */ - 188, /* (80) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ - 188, /* (81) cmd ::= DROP VIEW ifexists fullname */ - 188, /* (82) cmd ::= select */ - 202, /* (83) select ::= WITH wqlist selectnowith */ - 202, /* (84) select ::= WITH RECURSIVE wqlist selectnowith */ - 202, /* (85) select ::= selectnowith */ - 236, /* (86) selectnowith ::= selectnowith multiselect_op oneselect */ - 239, /* (87) multiselect_op ::= UNION */ - 239, /* (88) multiselect_op ::= UNION ALL */ - 239, /* (89) multiselect_op ::= EXCEPT|INTERSECT */ - 237, /* (90) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - 237, /* (91) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ - 249, /* (92) values ::= VALUES LP nexprlist RP */ - 249, /* (93) values ::= values COMMA LP nexprlist RP */ - 240, /* (94) distinct ::= DISTINCT */ - 240, /* (95) distinct ::= ALL */ - 240, /* (96) distinct ::= */ - 251, /* (97) sclp ::= */ - 241, /* (98) selcollist ::= sclp scanpt expr scanpt as */ - 241, /* (99) selcollist ::= sclp scanpt STAR */ - 241, /* (100) selcollist ::= sclp scanpt nm DOT STAR */ - 252, /* (101) as ::= AS nm */ - 252, /* (102) as ::= */ - 242, /* (103) from ::= */ - 242, /* (104) from ::= FROM seltablist */ - 254, /* (105) stl_prefix ::= seltablist joinop */ - 254, /* (106) stl_prefix ::= */ - 253, /* (107) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ - 253, /* (108) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ - 253, /* (109) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ - 253, /* (110) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ - 198, /* (111) dbnm ::= */ - 198, /* (112) dbnm ::= DOT nm */ - 235, /* (113) fullname ::= nm */ - 235, /* (114) fullname ::= nm DOT nm */ - 260, /* (115) xfullname ::= nm */ - 260, /* (116) xfullname ::= nm DOT nm */ - 260, /* (117) xfullname ::= nm DOT nm AS nm */ - 260, /* (118) xfullname ::= nm AS nm */ - 255, /* (119) joinop ::= COMMA|JOIN */ - 255, /* (120) joinop ::= JOIN_KW JOIN */ - 255, /* (121) joinop ::= JOIN_KW nm JOIN */ - 255, /* (122) joinop ::= JOIN_KW nm nm JOIN */ - 257, /* (123) on_opt ::= ON expr */ - 257, /* (124) on_opt ::= */ - 256, /* (125) indexed_opt ::= */ - 256, /* (126) indexed_opt ::= INDEXED BY nm */ - 256, /* (127) indexed_opt ::= NOT INDEXED */ - 258, /* (128) using_opt ::= USING LP idlist RP */ - 258, /* (129) using_opt ::= */ - 246, /* (130) orderby_opt ::= */ - 246, /* (131) orderby_opt ::= ORDER BY sortlist */ - 228, /* (132) sortlist ::= sortlist COMMA expr sortorder nulls */ - 228, /* (133) sortlist ::= expr sortorder nulls */ - 216, /* (134) sortorder ::= ASC */ - 216, /* (135) sortorder ::= DESC */ - 216, /* (136) sortorder ::= */ - 262, /* (137) nulls ::= NULLS FIRST */ - 262, /* (138) nulls ::= NULLS LAST */ - 262, /* (139) nulls ::= */ - 244, /* (140) groupby_opt ::= */ - 244, /* (141) groupby_opt ::= GROUP BY nexprlist */ - 245, /* (142) having_opt ::= */ - 245, /* (143) having_opt ::= HAVING expr */ - 247, /* (144) limit_opt ::= */ - 247, /* (145) limit_opt ::= LIMIT expr */ - 247, /* (146) limit_opt ::= LIMIT expr OFFSET expr */ - 247, /* (147) limit_opt ::= LIMIT expr COMMA expr */ - 188, /* (148) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ - 243, /* (149) where_opt ::= */ - 243, /* (150) where_opt ::= WHERE expr */ - 264, /* (151) where_opt_ret ::= */ - 264, /* (152) where_opt_ret ::= WHERE expr */ - 264, /* (153) where_opt_ret ::= RETURNING selcollist */ - 264, /* (154) where_opt_ret ::= WHERE expr RETURNING selcollist */ - 188, /* (155) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ - 265, /* (156) setlist ::= setlist COMMA nm EQ expr */ - 265, /* (157) setlist ::= setlist COMMA LP idlist RP EQ expr */ - 265, /* (158) setlist ::= nm EQ expr */ - 265, /* (159) setlist ::= LP idlist RP EQ expr */ - 188, /* (160) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - 188, /* (161) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ - 268, /* (162) upsert ::= */ - 268, /* (163) upsert ::= RETURNING selcollist */ - 268, /* (164) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ - 268, /* (165) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ - 268, /* (166) upsert ::= ON CONFLICT DO NOTHING returning */ - 268, /* (167) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ - 269, /* (168) returning ::= RETURNING selcollist */ - 266, /* (169) insert_cmd ::= INSERT orconf */ - 266, /* (170) insert_cmd ::= REPLACE */ - 267, /* (171) idlist_opt ::= */ - 267, /* (172) idlist_opt ::= LP idlist RP */ - 261, /* (173) idlist ::= idlist COMMA nm */ - 261, /* (174) idlist ::= nm */ - 214, /* (175) expr ::= LP expr RP */ - 214, /* (176) expr ::= ID|INDEXED */ - 214, /* (177) expr ::= JOIN_KW */ - 214, /* (178) expr ::= nm DOT nm */ - 214, /* (179) expr ::= nm DOT nm DOT nm */ - 213, /* (180) term ::= NULL|FLOAT|BLOB */ - 213, /* (181) term ::= STRING */ - 213, /* (182) term ::= INTEGER */ - 214, /* (183) expr ::= VARIABLE */ - 214, /* (184) expr ::= expr COLLATE ID|STRING */ - 214, /* (185) expr ::= CAST LP expr AS typetoken RP */ - 214, /* (186) expr ::= ID|INDEXED LP distinct exprlist RP */ - 214, /* (187) expr ::= ID|INDEXED LP STAR RP */ - 214, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ - 214, /* (189) expr ::= ID|INDEXED LP STAR RP filter_over */ - 213, /* (190) term ::= CTIME_KW */ - 214, /* (191) expr ::= LP nexprlist COMMA expr RP */ - 214, /* (192) expr ::= expr AND expr */ - 214, /* (193) expr ::= expr OR expr */ - 214, /* (194) expr ::= expr LT|GT|GE|LE expr */ - 214, /* (195) expr ::= expr EQ|NE expr */ - 214, /* (196) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - 214, /* (197) expr ::= expr PLUS|MINUS expr */ - 214, /* (198) expr ::= expr STAR|SLASH|REM expr */ - 214, /* (199) expr ::= expr CONCAT expr */ - 271, /* (200) likeop ::= NOT LIKE_KW|MATCH */ - 214, /* (201) expr ::= expr likeop expr */ - 214, /* (202) expr ::= expr likeop expr ESCAPE expr */ - 214, /* (203) expr ::= expr ISNULL|NOTNULL */ - 214, /* (204) expr ::= expr NOT NULL */ - 214, /* (205) expr ::= expr IS expr */ - 214, /* (206) expr ::= expr IS NOT expr */ - 214, /* (207) expr ::= NOT expr */ - 214, /* (208) expr ::= BITNOT expr */ - 214, /* (209) expr ::= PLUS|MINUS expr */ - 272, /* (210) between_op ::= BETWEEN */ - 272, /* (211) between_op ::= NOT BETWEEN */ - 214, /* (212) expr ::= expr between_op expr AND expr */ - 273, /* (213) in_op ::= IN */ - 273, /* (214) in_op ::= NOT IN */ - 214, /* (215) expr ::= expr in_op LP exprlist RP */ - 214, /* (216) expr ::= LP select RP */ - 214, /* (217) expr ::= expr in_op LP select RP */ - 214, /* (218) expr ::= expr in_op nm dbnm paren_exprlist */ - 214, /* (219) expr ::= EXISTS LP select RP */ - 214, /* (220) expr ::= CASE case_operand case_exprlist case_else END */ - 276, /* (221) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - 276, /* (222) case_exprlist ::= WHEN expr THEN expr */ - 277, /* (223) case_else ::= ELSE expr */ - 277, /* (224) case_else ::= */ - 275, /* (225) case_operand ::= expr */ - 275, /* (226) case_operand ::= */ - 259, /* (227) exprlist ::= */ - 250, /* (228) nexprlist ::= nexprlist COMMA expr */ - 250, /* (229) nexprlist ::= expr */ - 274, /* (230) paren_exprlist ::= */ - 274, /* (231) paren_exprlist ::= LP exprlist RP */ - 188, /* (232) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - 278, /* (233) uniqueflag ::= UNIQUE */ - 278, /* (234) uniqueflag ::= */ - 218, /* (235) eidlist_opt ::= */ - 218, /* (236) eidlist_opt ::= LP eidlist RP */ - 229, /* (237) eidlist ::= eidlist COMMA nm collate sortorder */ - 229, /* (238) eidlist ::= nm collate sortorder */ - 279, /* (239) collate ::= */ - 279, /* (240) collate ::= COLLATE ID|STRING */ - 188, /* (241) cmd ::= DROP INDEX ifexists fullname */ - 188, /* (242) cmd ::= VACUUM vinto */ - 188, /* (243) cmd ::= VACUUM nm vinto */ - 280, /* (244) vinto ::= INTO expr */ - 280, /* (245) vinto ::= */ - 188, /* (246) cmd ::= PRAGMA nm dbnm */ - 188, /* (247) cmd ::= PRAGMA nm dbnm EQ nmnum */ - 188, /* (248) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - 188, /* (249) cmd ::= PRAGMA nm dbnm EQ minus_num */ - 188, /* (250) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - 208, /* (251) plus_num ::= PLUS INTEGER|FLOAT */ - 209, /* (252) minus_num ::= MINUS INTEGER|FLOAT */ - 188, /* (253) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - 282, /* (254) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - 284, /* (255) trigger_time ::= BEFORE|AFTER */ - 284, /* (256) trigger_time ::= INSTEAD OF */ - 284, /* (257) trigger_time ::= */ - 285, /* (258) trigger_event ::= DELETE|INSERT */ - 285, /* (259) trigger_event ::= UPDATE */ - 285, /* (260) trigger_event ::= UPDATE OF idlist */ - 287, /* (261) when_clause ::= */ - 287, /* (262) when_clause ::= WHEN expr */ - 283, /* (263) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - 283, /* (264) trigger_cmd_list ::= trigger_cmd SEMI */ - 289, /* (265) trnm ::= nm DOT nm */ - 290, /* (266) tridxby ::= INDEXED BY nm */ - 290, /* (267) tridxby ::= NOT INDEXED */ - 288, /* (268) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ - 288, /* (269) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - 288, /* (270) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - 288, /* (271) trigger_cmd ::= scanpt select scanpt */ - 214, /* (272) expr ::= RAISE LP IGNORE RP */ - 214, /* (273) expr ::= RAISE LP raisetype COMMA nm RP */ - 233, /* (274) raisetype ::= ROLLBACK */ - 233, /* (275) raisetype ::= ABORT */ - 233, /* (276) raisetype ::= FAIL */ - 188, /* (277) cmd ::= DROP TRIGGER ifexists fullname */ - 188, /* (278) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - 188, /* (279) cmd ::= DETACH database_kw_opt expr */ - 292, /* (280) key_opt ::= */ - 292, /* (281) key_opt ::= KEY expr */ - 188, /* (282) cmd ::= REINDEX */ - 188, /* (283) cmd ::= REINDEX nm dbnm */ - 188, /* (284) cmd ::= ANALYZE */ - 188, /* (285) cmd ::= ANALYZE nm dbnm */ - 188, /* (286) cmd ::= ALTER TABLE fullname RENAME TO nm */ - 188, /* (287) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - 188, /* (288) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ - 293, /* (289) add_column_fullname ::= fullname */ - 188, /* (290) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - 188, /* (291) cmd ::= create_vtab */ - 188, /* (292) cmd ::= create_vtab LP vtabarglist RP */ - 295, /* (293) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 297, /* (294) vtabarg ::= */ - 298, /* (295) vtabargtoken ::= ANY */ - 298, /* (296) vtabargtoken ::= lp anylist RP */ - 299, /* (297) lp ::= LP */ - 263, /* (298) with ::= WITH wqlist */ - 263, /* (299) with ::= WITH RECURSIVE wqlist */ - 302, /* (300) wqas ::= AS */ - 302, /* (301) wqas ::= AS MATERIALIZED */ - 302, /* (302) wqas ::= AS NOT MATERIALIZED */ - 301, /* (303) wqitem ::= nm eidlist_opt wqas LP select RP */ - 238, /* (304) wqlist ::= wqitem */ - 238, /* (305) wqlist ::= wqlist COMMA wqitem */ - 303, /* (306) windowdefn_list ::= windowdefn */ - 303, /* (307) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - 304, /* (308) windowdefn ::= nm AS LP window RP */ - 305, /* (309) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - 305, /* (310) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - 305, /* (311) window ::= ORDER BY sortlist frame_opt */ - 305, /* (312) window ::= nm ORDER BY sortlist frame_opt */ - 305, /* (313) window ::= frame_opt */ - 305, /* (314) window ::= nm frame_opt */ - 306, /* (315) frame_opt ::= */ - 306, /* (316) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - 306, /* (317) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - 310, /* (318) range_or_rows ::= RANGE|ROWS|GROUPS */ - 312, /* (319) frame_bound_s ::= frame_bound */ - 312, /* (320) frame_bound_s ::= UNBOUNDED PRECEDING */ - 313, /* (321) frame_bound_e ::= frame_bound */ - 313, /* (322) frame_bound_e ::= UNBOUNDED FOLLOWING */ - 311, /* (323) frame_bound ::= expr PRECEDING|FOLLOWING */ - 311, /* (324) frame_bound ::= CURRENT ROW */ - 314, /* (325) frame_exclude_opt ::= */ - 314, /* (326) frame_exclude_opt ::= EXCLUDE frame_exclude */ - 315, /* (327) frame_exclude ::= NO OTHERS */ - 315, /* (328) frame_exclude ::= CURRENT ROW */ - 315, /* (329) frame_exclude ::= GROUP|TIES */ - 248, /* (330) window_clause ::= WINDOW windowdefn_list */ - 270, /* (331) filter_over ::= filter_clause over_clause */ - 270, /* (332) filter_over ::= over_clause */ - 270, /* (333) filter_over ::= filter_clause */ - 309, /* (334) over_clause ::= OVER LP window RP */ - 309, /* (335) over_clause ::= OVER nm */ - 308, /* (336) filter_clause ::= FILTER LP WHERE expr RP */ - 183, /* (337) input ::= cmdlist */ - 184, /* (338) cmdlist ::= cmdlist ecmd */ - 184, /* (339) cmdlist ::= ecmd */ - 185, /* (340) ecmd ::= SEMI */ - 185, /* (341) ecmd ::= cmdx SEMI */ - 185, /* (342) ecmd ::= explain cmdx SEMI */ - 190, /* (343) trans_opt ::= */ - 190, /* (344) trans_opt ::= TRANSACTION */ - 190, /* (345) trans_opt ::= TRANSACTION nm */ - 192, /* (346) savepoint_opt ::= SAVEPOINT */ - 192, /* (347) savepoint_opt ::= */ - 188, /* (348) cmd ::= create_table create_table_args */ - 199, /* (349) columnlist ::= columnlist COMMA columnname carglist */ - 199, /* (350) columnlist ::= columnname carglist */ - 191, /* (351) nm ::= ID|INDEXED */ - 191, /* (352) nm ::= STRING */ - 191, /* (353) nm ::= JOIN_KW */ - 205, /* (354) typetoken ::= typename */ - 206, /* (355) typename ::= ID|STRING */ - 207, /* (356) signed ::= plus_num */ - 207, /* (357) signed ::= minus_num */ - 204, /* (358) carglist ::= carglist ccons */ - 204, /* (359) carglist ::= */ - 212, /* (360) ccons ::= NULL onconf */ - 212, /* (361) ccons ::= GENERATED ALWAYS AS generated */ - 212, /* (362) ccons ::= AS generated */ - 200, /* (363) conslist_opt ::= COMMA conslist */ - 225, /* (364) conslist ::= conslist tconscomma tcons */ - 225, /* (365) conslist ::= tcons */ - 226, /* (366) tconscomma ::= */ - 230, /* (367) defer_subclause_opt ::= defer_subclause */ - 232, /* (368) resolvetype ::= raisetype */ - 236, /* (369) selectnowith ::= oneselect */ - 237, /* (370) oneselect ::= values */ - 251, /* (371) sclp ::= selcollist COMMA */ - 252, /* (372) as ::= ID|STRING */ - 269, /* (373) returning ::= */ - 214, /* (374) expr ::= term */ - 271, /* (375) likeop ::= LIKE_KW|MATCH */ - 259, /* (376) exprlist ::= nexprlist */ - 281, /* (377) nmnum ::= plus_num */ - 281, /* (378) nmnum ::= nm */ - 281, /* (379) nmnum ::= ON */ - 281, /* (380) nmnum ::= DELETE */ - 281, /* (381) nmnum ::= DEFAULT */ - 208, /* (382) plus_num ::= INTEGER|FLOAT */ - 286, /* (383) foreach_clause ::= */ - 286, /* (384) foreach_clause ::= FOR EACH ROW */ - 289, /* (385) trnm ::= nm */ - 290, /* (386) tridxby ::= */ - 291, /* (387) database_kw_opt ::= DATABASE */ - 291, /* (388) database_kw_opt ::= */ - 294, /* (389) kwcolumn_opt ::= */ - 294, /* (390) kwcolumn_opt ::= COLUMNKW */ - 296, /* (391) vtabarglist ::= vtabarg */ - 296, /* (392) vtabarglist ::= vtabarglist COMMA vtabarg */ - 297, /* (393) vtabarg ::= vtabarg vtabargtoken */ - 300, /* (394) anylist ::= */ - 300, /* (395) anylist ::= anylist LP anylist RP */ - 300, /* (396) anylist ::= anylist ANY */ - 263, /* (397) with ::= */ + 189, /* (0) explain ::= EXPLAIN */ + 189, /* (1) explain ::= EXPLAIN QUERY PLAN */ + 188, /* (2) cmdx ::= cmd */ + 190, /* (3) cmd ::= BEGIN transtype trans_opt */ + 191, /* (4) transtype ::= */ + 191, /* (5) transtype ::= DEFERRED */ + 191, /* (6) transtype ::= IMMEDIATE */ + 191, /* (7) transtype ::= EXCLUSIVE */ + 190, /* (8) cmd ::= COMMIT|END trans_opt */ + 190, /* (9) cmd ::= ROLLBACK trans_opt */ + 190, /* (10) cmd ::= SAVEPOINT nm */ + 190, /* (11) cmd ::= RELEASE savepoint_opt nm */ + 190, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ + 195, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + 197, /* (14) createkw ::= CREATE */ + 199, /* (15) ifnotexists ::= */ + 199, /* (16) ifnotexists ::= IF NOT EXISTS */ + 198, /* (17) temp ::= TEMP */ + 198, /* (18) temp ::= */ + 196, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ + 196, /* (20) create_table_args ::= AS select */ + 203, /* (21) table_option_set ::= */ + 203, /* (22) table_option_set ::= table_option_set COMMA table_option */ + 205, /* (23) table_option ::= WITHOUT nm */ + 205, /* (24) table_option ::= nm */ + 206, /* (25) columnname ::= nm typetoken */ + 208, /* (26) typetoken ::= */ + 208, /* (27) typetoken ::= typename LP signed RP */ + 208, /* (28) typetoken ::= typename LP signed COMMA signed RP */ + 209, /* (29) typename ::= typename ID|STRING */ + 213, /* (30) scanpt ::= */ + 214, /* (31) scantok ::= */ + 215, /* (32) ccons ::= CONSTRAINT nm */ + 215, /* (33) ccons ::= DEFAULT scantok term */ + 215, /* (34) ccons ::= DEFAULT LP expr RP */ + 215, /* (35) ccons ::= DEFAULT PLUS scantok term */ + 215, /* (36) ccons ::= DEFAULT MINUS scantok term */ + 215, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ + 215, /* (38) ccons ::= NOT NULL onconf */ + 215, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + 215, /* (40) ccons ::= UNIQUE onconf */ + 215, /* (41) ccons ::= CHECK LP expr RP */ + 215, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ + 215, /* (43) ccons ::= defer_subclause */ + 215, /* (44) ccons ::= COLLATE ID|STRING */ + 224, /* (45) generated ::= LP expr RP */ + 224, /* (46) generated ::= LP expr RP ID */ + 220, /* (47) autoinc ::= */ + 220, /* (48) autoinc ::= AUTOINCR */ + 222, /* (49) refargs ::= */ + 222, /* (50) refargs ::= refargs refarg */ + 225, /* (51) refarg ::= MATCH nm */ + 225, /* (52) refarg ::= ON INSERT refact */ + 225, /* (53) refarg ::= ON DELETE refact */ + 225, /* (54) refarg ::= ON UPDATE refact */ + 226, /* (55) refact ::= SET NULL */ + 226, /* (56) refact ::= SET DEFAULT */ + 226, /* (57) refact ::= CASCADE */ + 226, /* (58) refact ::= RESTRICT */ + 226, /* (59) refact ::= NO ACTION */ + 223, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + 223, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 227, /* (62) init_deferred_pred_opt ::= */ + 227, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + 227, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 202, /* (65) conslist_opt ::= */ + 229, /* (66) tconscomma ::= COMMA */ + 230, /* (67) tcons ::= CONSTRAINT nm */ + 230, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + 230, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ + 230, /* (70) tcons ::= CHECK LP expr RP onconf */ + 230, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 233, /* (72) defer_subclause_opt ::= */ + 218, /* (73) onconf ::= */ + 218, /* (74) onconf ::= ON CONFLICT resolvetype */ + 234, /* (75) orconf ::= */ + 234, /* (76) orconf ::= OR resolvetype */ + 235, /* (77) resolvetype ::= IGNORE */ + 235, /* (78) resolvetype ::= REPLACE */ + 190, /* (79) cmd ::= DROP TABLE ifexists fullname */ + 237, /* (80) ifexists ::= IF EXISTS */ + 237, /* (81) ifexists ::= */ + 190, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + 190, /* (83) cmd ::= DROP VIEW ifexists fullname */ + 190, /* (84) cmd ::= select */ + 204, /* (85) select ::= WITH wqlist selectnowith */ + 204, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ + 204, /* (87) select ::= selectnowith */ + 239, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ + 242, /* (89) multiselect_op ::= UNION */ + 242, /* (90) multiselect_op ::= UNION ALL */ + 242, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ + 240, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + 240, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + 252, /* (94) values ::= VALUES LP nexprlist RP */ + 252, /* (95) values ::= values COMMA LP nexprlist RP */ + 243, /* (96) distinct ::= DISTINCT */ + 243, /* (97) distinct ::= ALL */ + 243, /* (98) distinct ::= */ + 254, /* (99) sclp ::= */ + 244, /* (100) selcollist ::= sclp scanpt expr scanpt as */ + 244, /* (101) selcollist ::= sclp scanpt STAR */ + 244, /* (102) selcollist ::= sclp scanpt nm DOT STAR */ + 255, /* (103) as ::= AS nm */ + 255, /* (104) as ::= */ + 245, /* (105) from ::= */ + 245, /* (106) from ::= FROM seltablist */ + 257, /* (107) stl_prefix ::= seltablist joinop */ + 257, /* (108) stl_prefix ::= */ + 256, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */ + 256, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + 256, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + 256, /* (112) seltablist ::= stl_prefix LP select RP as on_using */ + 256, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */ + 200, /* (114) dbnm ::= */ + 200, /* (115) dbnm ::= DOT nm */ + 238, /* (116) fullname ::= nm */ + 238, /* (117) fullname ::= nm DOT nm */ + 262, /* (118) xfullname ::= nm */ + 262, /* (119) xfullname ::= nm DOT nm */ + 262, /* (120) xfullname ::= nm DOT nm AS nm */ + 262, /* (121) xfullname ::= nm AS nm */ + 258, /* (122) joinop ::= COMMA|JOIN */ + 258, /* (123) joinop ::= JOIN_KW JOIN */ + 258, /* (124) joinop ::= JOIN_KW nm JOIN */ + 258, /* (125) joinop ::= JOIN_KW nm nm JOIN */ + 259, /* (126) on_using ::= ON expr */ + 259, /* (127) on_using ::= USING LP idlist RP */ + 259, /* (128) on_using ::= */ + 264, /* (129) indexed_opt ::= */ + 260, /* (130) indexed_by ::= INDEXED BY nm */ + 260, /* (131) indexed_by ::= NOT INDEXED */ + 249, /* (132) orderby_opt ::= */ + 249, /* (133) orderby_opt ::= ORDER BY sortlist */ + 231, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */ + 231, /* (135) sortlist ::= expr sortorder nulls */ + 219, /* (136) sortorder ::= ASC */ + 219, /* (137) sortorder ::= DESC */ + 219, /* (138) sortorder ::= */ + 265, /* (139) nulls ::= NULLS FIRST */ + 265, /* (140) nulls ::= NULLS LAST */ + 265, /* (141) nulls ::= */ + 247, /* (142) groupby_opt ::= */ + 247, /* (143) groupby_opt ::= GROUP BY nexprlist */ + 248, /* (144) having_opt ::= */ + 248, /* (145) having_opt ::= HAVING expr */ + 250, /* (146) limit_opt ::= */ + 250, /* (147) limit_opt ::= LIMIT expr */ + 250, /* (148) limit_opt ::= LIMIT expr OFFSET expr */ + 250, /* (149) limit_opt ::= LIMIT expr COMMA expr */ + 190, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + 246, /* (151) where_opt ::= */ + 246, /* (152) where_opt ::= WHERE expr */ + 267, /* (153) where_opt_ret ::= */ + 267, /* (154) where_opt_ret ::= WHERE expr */ + 267, /* (155) where_opt_ret ::= RETURNING selcollist */ + 267, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */ + 190, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + 268, /* (158) setlist ::= setlist COMMA nm EQ expr */ + 268, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */ + 268, /* (160) setlist ::= nm EQ expr */ + 268, /* (161) setlist ::= LP idlist RP EQ expr */ + 190, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + 190, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + 271, /* (164) upsert ::= */ + 271, /* (165) upsert ::= RETURNING selcollist */ + 271, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ + 271, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ + 271, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */ + 271, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ + 272, /* (170) returning ::= RETURNING selcollist */ + 269, /* (171) insert_cmd ::= INSERT orconf */ + 269, /* (172) insert_cmd ::= REPLACE */ + 270, /* (173) idlist_opt ::= */ + 270, /* (174) idlist_opt ::= LP idlist RP */ + 263, /* (175) idlist ::= idlist COMMA nm */ + 263, /* (176) idlist ::= nm */ + 217, /* (177) expr ::= LP expr RP */ + 217, /* (178) expr ::= ID|INDEXED */ + 217, /* (179) expr ::= JOIN_KW */ + 217, /* (180) expr ::= nm DOT nm */ + 217, /* (181) expr ::= nm DOT nm DOT nm */ + 216, /* (182) term ::= NULL|FLOAT|BLOB */ + 216, /* (183) term ::= STRING */ + 216, /* (184) term ::= INTEGER */ + 217, /* (185) expr ::= VARIABLE */ + 217, /* (186) expr ::= expr COLLATE ID|STRING */ + 217, /* (187) expr ::= CAST LP expr AS typetoken RP */ + 217, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP */ + 217, /* (189) expr ::= ID|INDEXED LP STAR RP */ + 217, /* (190) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ + 217, /* (191) expr ::= ID|INDEXED LP STAR RP filter_over */ + 216, /* (192) term ::= CTIME_KW */ + 217, /* (193) expr ::= LP nexprlist COMMA expr RP */ + 217, /* (194) expr ::= expr AND expr */ + 217, /* (195) expr ::= expr OR expr */ + 217, /* (196) expr ::= expr LT|GT|GE|LE expr */ + 217, /* (197) expr ::= expr EQ|NE expr */ + 217, /* (198) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + 217, /* (199) expr ::= expr PLUS|MINUS expr */ + 217, /* (200) expr ::= expr STAR|SLASH|REM expr */ + 217, /* (201) expr ::= expr CONCAT expr */ + 274, /* (202) likeop ::= NOT LIKE_KW|MATCH */ + 217, /* (203) expr ::= expr likeop expr */ + 217, /* (204) expr ::= expr likeop expr ESCAPE expr */ + 217, /* (205) expr ::= expr ISNULL|NOTNULL */ + 217, /* (206) expr ::= expr NOT NULL */ + 217, /* (207) expr ::= expr IS expr */ + 217, /* (208) expr ::= expr IS NOT expr */ + 217, /* (209) expr ::= expr IS NOT DISTINCT FROM expr */ + 217, /* (210) expr ::= expr IS DISTINCT FROM expr */ + 217, /* (211) expr ::= NOT expr */ + 217, /* (212) expr ::= BITNOT expr */ + 217, /* (213) expr ::= PLUS|MINUS expr */ + 217, /* (214) expr ::= expr PTR expr */ + 275, /* (215) between_op ::= BETWEEN */ + 275, /* (216) between_op ::= NOT BETWEEN */ + 217, /* (217) expr ::= expr between_op expr AND expr */ + 276, /* (218) in_op ::= IN */ + 276, /* (219) in_op ::= NOT IN */ + 217, /* (220) expr ::= expr in_op LP exprlist RP */ + 217, /* (221) expr ::= LP select RP */ + 217, /* (222) expr ::= expr in_op LP select RP */ + 217, /* (223) expr ::= expr in_op nm dbnm paren_exprlist */ + 217, /* (224) expr ::= EXISTS LP select RP */ + 217, /* (225) expr ::= CASE case_operand case_exprlist case_else END */ + 279, /* (226) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + 279, /* (227) case_exprlist ::= WHEN expr THEN expr */ + 280, /* (228) case_else ::= ELSE expr */ + 280, /* (229) case_else ::= */ + 278, /* (230) case_operand ::= expr */ + 278, /* (231) case_operand ::= */ + 261, /* (232) exprlist ::= */ + 253, /* (233) nexprlist ::= nexprlist COMMA expr */ + 253, /* (234) nexprlist ::= expr */ + 277, /* (235) paren_exprlist ::= */ + 277, /* (236) paren_exprlist ::= LP exprlist RP */ + 190, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + 281, /* (238) uniqueflag ::= UNIQUE */ + 281, /* (239) uniqueflag ::= */ + 221, /* (240) eidlist_opt ::= */ + 221, /* (241) eidlist_opt ::= LP eidlist RP */ + 232, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */ + 232, /* (243) eidlist ::= nm collate sortorder */ + 282, /* (244) collate ::= */ + 282, /* (245) collate ::= COLLATE ID|STRING */ + 190, /* (246) cmd ::= DROP INDEX ifexists fullname */ + 190, /* (247) cmd ::= VACUUM vinto */ + 190, /* (248) cmd ::= VACUUM nm vinto */ + 283, /* (249) vinto ::= INTO expr */ + 283, /* (250) vinto ::= */ + 190, /* (251) cmd ::= PRAGMA nm dbnm */ + 190, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */ + 190, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + 190, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */ + 190, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + 211, /* (256) plus_num ::= PLUS INTEGER|FLOAT */ + 212, /* (257) minus_num ::= MINUS INTEGER|FLOAT */ + 190, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + 285, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + 287, /* (260) trigger_time ::= BEFORE|AFTER */ + 287, /* (261) trigger_time ::= INSTEAD OF */ + 287, /* (262) trigger_time ::= */ + 288, /* (263) trigger_event ::= DELETE|INSERT */ + 288, /* (264) trigger_event ::= UPDATE */ + 288, /* (265) trigger_event ::= UPDATE OF idlist */ + 290, /* (266) when_clause ::= */ + 290, /* (267) when_clause ::= WHEN expr */ + 286, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + 286, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */ + 292, /* (270) trnm ::= nm DOT nm */ + 293, /* (271) tridxby ::= INDEXED BY nm */ + 293, /* (272) tridxby ::= NOT INDEXED */ + 291, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + 291, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + 291, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + 291, /* (276) trigger_cmd ::= scanpt select scanpt */ + 217, /* (277) expr ::= RAISE LP IGNORE RP */ + 217, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */ + 236, /* (279) raisetype ::= ROLLBACK */ + 236, /* (280) raisetype ::= ABORT */ + 236, /* (281) raisetype ::= FAIL */ + 190, /* (282) cmd ::= DROP TRIGGER ifexists fullname */ + 190, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 190, /* (284) cmd ::= DETACH database_kw_opt expr */ + 295, /* (285) key_opt ::= */ + 295, /* (286) key_opt ::= KEY expr */ + 190, /* (287) cmd ::= REINDEX */ + 190, /* (288) cmd ::= REINDEX nm dbnm */ + 190, /* (289) cmd ::= ANALYZE */ + 190, /* (290) cmd ::= ANALYZE nm dbnm */ + 190, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 190, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 190, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + 296, /* (294) add_column_fullname ::= fullname */ + 190, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 190, /* (296) cmd ::= create_vtab */ + 190, /* (297) cmd ::= create_vtab LP vtabarglist RP */ + 298, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 300, /* (299) vtabarg ::= */ + 301, /* (300) vtabargtoken ::= ANY */ + 301, /* (301) vtabargtoken ::= lp anylist RP */ + 302, /* (302) lp ::= LP */ + 266, /* (303) with ::= WITH wqlist */ + 266, /* (304) with ::= WITH RECURSIVE wqlist */ + 305, /* (305) wqas ::= AS */ + 305, /* (306) wqas ::= AS MATERIALIZED */ + 305, /* (307) wqas ::= AS NOT MATERIALIZED */ + 304, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */ + 241, /* (309) wqlist ::= wqitem */ + 241, /* (310) wqlist ::= wqlist COMMA wqitem */ + 306, /* (311) windowdefn_list ::= windowdefn */ + 306, /* (312) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 307, /* (313) windowdefn ::= nm AS LP window RP */ + 308, /* (314) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (315) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (316) window ::= ORDER BY sortlist frame_opt */ + 308, /* (317) window ::= nm ORDER BY sortlist frame_opt */ + 308, /* (318) window ::= frame_opt */ + 308, /* (319) window ::= nm frame_opt */ + 309, /* (320) frame_opt ::= */ + 309, /* (321) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + 309, /* (322) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + 313, /* (323) range_or_rows ::= RANGE|ROWS|GROUPS */ + 315, /* (324) frame_bound_s ::= frame_bound */ + 315, /* (325) frame_bound_s ::= UNBOUNDED PRECEDING */ + 316, /* (326) frame_bound_e ::= frame_bound */ + 316, /* (327) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 314, /* (328) frame_bound ::= expr PRECEDING|FOLLOWING */ + 314, /* (329) frame_bound ::= CURRENT ROW */ + 317, /* (330) frame_exclude_opt ::= */ + 317, /* (331) frame_exclude_opt ::= EXCLUDE frame_exclude */ + 318, /* (332) frame_exclude ::= NO OTHERS */ + 318, /* (333) frame_exclude ::= CURRENT ROW */ + 318, /* (334) frame_exclude ::= GROUP|TIES */ + 251, /* (335) window_clause ::= WINDOW windowdefn_list */ + 273, /* (336) filter_over ::= filter_clause over_clause */ + 273, /* (337) filter_over ::= over_clause */ + 273, /* (338) filter_over ::= filter_clause */ + 312, /* (339) over_clause ::= OVER LP window RP */ + 312, /* (340) over_clause ::= OVER nm */ + 311, /* (341) filter_clause ::= FILTER LP WHERE expr RP */ + 185, /* (342) input ::= cmdlist */ + 186, /* (343) cmdlist ::= cmdlist ecmd */ + 186, /* (344) cmdlist ::= ecmd */ + 187, /* (345) ecmd ::= SEMI */ + 187, /* (346) ecmd ::= cmdx SEMI */ + 187, /* (347) ecmd ::= explain cmdx SEMI */ + 192, /* (348) trans_opt ::= */ + 192, /* (349) trans_opt ::= TRANSACTION */ + 192, /* (350) trans_opt ::= TRANSACTION nm */ + 194, /* (351) savepoint_opt ::= SAVEPOINT */ + 194, /* (352) savepoint_opt ::= */ + 190, /* (353) cmd ::= create_table create_table_args */ + 203, /* (354) table_option_set ::= table_option */ + 201, /* (355) columnlist ::= columnlist COMMA columnname carglist */ + 201, /* (356) columnlist ::= columnname carglist */ + 193, /* (357) nm ::= ID|INDEXED */ + 193, /* (358) nm ::= STRING */ + 193, /* (359) nm ::= JOIN_KW */ + 208, /* (360) typetoken ::= typename */ + 209, /* (361) typename ::= ID|STRING */ + 210, /* (362) signed ::= plus_num */ + 210, /* (363) signed ::= minus_num */ + 207, /* (364) carglist ::= carglist ccons */ + 207, /* (365) carglist ::= */ + 215, /* (366) ccons ::= NULL onconf */ + 215, /* (367) ccons ::= GENERATED ALWAYS AS generated */ + 215, /* (368) ccons ::= AS generated */ + 202, /* (369) conslist_opt ::= COMMA conslist */ + 228, /* (370) conslist ::= conslist tconscomma tcons */ + 228, /* (371) conslist ::= tcons */ + 229, /* (372) tconscomma ::= */ + 233, /* (373) defer_subclause_opt ::= defer_subclause */ + 235, /* (374) resolvetype ::= raisetype */ + 239, /* (375) selectnowith ::= oneselect */ + 240, /* (376) oneselect ::= values */ + 254, /* (377) sclp ::= selcollist COMMA */ + 255, /* (378) as ::= ID|STRING */ + 264, /* (379) indexed_opt ::= indexed_by */ + 272, /* (380) returning ::= */ + 217, /* (381) expr ::= term */ + 274, /* (382) likeop ::= LIKE_KW|MATCH */ + 261, /* (383) exprlist ::= nexprlist */ + 284, /* (384) nmnum ::= plus_num */ + 284, /* (385) nmnum ::= nm */ + 284, /* (386) nmnum ::= ON */ + 284, /* (387) nmnum ::= DELETE */ + 284, /* (388) nmnum ::= DEFAULT */ + 211, /* (389) plus_num ::= INTEGER|FLOAT */ + 289, /* (390) foreach_clause ::= */ + 289, /* (391) foreach_clause ::= FOR EACH ROW */ + 292, /* (392) trnm ::= nm */ + 293, /* (393) tridxby ::= */ + 294, /* (394) database_kw_opt ::= DATABASE */ + 294, /* (395) database_kw_opt ::= */ + 297, /* (396) kwcolumn_opt ::= */ + 297, /* (397) kwcolumn_opt ::= COLUMNKW */ + 299, /* (398) vtabarglist ::= vtabarg */ + 299, /* (399) vtabarglist ::= vtabarglist COMMA vtabarg */ + 300, /* (400) vtabarg ::= vtabarg vtabargtoken */ + 303, /* (401) anylist ::= */ + 303, /* (402) anylist ::= anylist LP anylist RP */ + 303, /* (403) anylist ::= anylist ANY */ + 266, /* (404) with ::= */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number @@ -160178,385 +170078,392 @@ static const signed char yyRuleInfoNRhs[] = { -3, /* (16) ifnotexists ::= IF NOT EXISTS */ -1, /* (17) temp ::= TEMP */ 0, /* (18) temp ::= */ - -5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ + -5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ -2, /* (20) create_table_args ::= AS select */ - 0, /* (21) table_options ::= */ - -2, /* (22) table_options ::= WITHOUT nm */ - -2, /* (23) columnname ::= nm typetoken */ - 0, /* (24) typetoken ::= */ - -4, /* (25) typetoken ::= typename LP signed RP */ - -6, /* (26) typetoken ::= typename LP signed COMMA signed RP */ - -2, /* (27) typename ::= typename ID|STRING */ - 0, /* (28) scanpt ::= */ - 0, /* (29) scantok ::= */ - -2, /* (30) ccons ::= CONSTRAINT nm */ - -3, /* (31) ccons ::= DEFAULT scantok term */ - -4, /* (32) ccons ::= DEFAULT LP expr RP */ - -4, /* (33) ccons ::= DEFAULT PLUS scantok term */ - -4, /* (34) ccons ::= DEFAULT MINUS scantok term */ - -3, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */ - -3, /* (36) ccons ::= NOT NULL onconf */ - -5, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */ - -2, /* (38) ccons ::= UNIQUE onconf */ - -4, /* (39) ccons ::= CHECK LP expr RP */ - -4, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */ - -1, /* (41) ccons ::= defer_subclause */ - -2, /* (42) ccons ::= COLLATE ID|STRING */ - -3, /* (43) generated ::= LP expr RP */ - -4, /* (44) generated ::= LP expr RP ID */ - 0, /* (45) autoinc ::= */ - -1, /* (46) autoinc ::= AUTOINCR */ - 0, /* (47) refargs ::= */ - -2, /* (48) refargs ::= refargs refarg */ - -2, /* (49) refarg ::= MATCH nm */ - -3, /* (50) refarg ::= ON INSERT refact */ - -3, /* (51) refarg ::= ON DELETE refact */ - -3, /* (52) refarg ::= ON UPDATE refact */ - -2, /* (53) refact ::= SET NULL */ - -2, /* (54) refact ::= SET DEFAULT */ - -1, /* (55) refact ::= CASCADE */ - -1, /* (56) refact ::= RESTRICT */ - -2, /* (57) refact ::= NO ACTION */ - -3, /* (58) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ - -2, /* (59) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - 0, /* (60) init_deferred_pred_opt ::= */ - -2, /* (61) init_deferred_pred_opt ::= INITIALLY DEFERRED */ - -2, /* (62) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ - 0, /* (63) conslist_opt ::= */ - -1, /* (64) tconscomma ::= COMMA */ - -2, /* (65) tcons ::= CONSTRAINT nm */ - -7, /* (66) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ - -5, /* (67) tcons ::= UNIQUE LP sortlist RP onconf */ - -5, /* (68) tcons ::= CHECK LP expr RP onconf */ - -10, /* (69) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ - 0, /* (70) defer_subclause_opt ::= */ - 0, /* (71) onconf ::= */ - -3, /* (72) onconf ::= ON CONFLICT resolvetype */ - 0, /* (73) orconf ::= */ - -2, /* (74) orconf ::= OR resolvetype */ - -1, /* (75) resolvetype ::= IGNORE */ - -1, /* (76) resolvetype ::= REPLACE */ - -4, /* (77) cmd ::= DROP TABLE ifexists fullname */ - -2, /* (78) ifexists ::= IF EXISTS */ - 0, /* (79) ifexists ::= */ - -9, /* (80) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ - -4, /* (81) cmd ::= DROP VIEW ifexists fullname */ - -1, /* (82) cmd ::= select */ - -3, /* (83) select ::= WITH wqlist selectnowith */ - -4, /* (84) select ::= WITH RECURSIVE wqlist selectnowith */ - -1, /* (85) select ::= selectnowith */ - -3, /* (86) selectnowith ::= selectnowith multiselect_op oneselect */ - -1, /* (87) multiselect_op ::= UNION */ - -2, /* (88) multiselect_op ::= UNION ALL */ - -1, /* (89) multiselect_op ::= EXCEPT|INTERSECT */ - -9, /* (90) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - -10, /* (91) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ - -4, /* (92) values ::= VALUES LP nexprlist RP */ - -5, /* (93) values ::= values COMMA LP nexprlist RP */ - -1, /* (94) distinct ::= DISTINCT */ - -1, /* (95) distinct ::= ALL */ - 0, /* (96) distinct ::= */ - 0, /* (97) sclp ::= */ - -5, /* (98) selcollist ::= sclp scanpt expr scanpt as */ - -3, /* (99) selcollist ::= sclp scanpt STAR */ - -5, /* (100) selcollist ::= sclp scanpt nm DOT STAR */ - -2, /* (101) as ::= AS nm */ - 0, /* (102) as ::= */ - 0, /* (103) from ::= */ - -2, /* (104) from ::= FROM seltablist */ - -2, /* (105) stl_prefix ::= seltablist joinop */ - 0, /* (106) stl_prefix ::= */ - -7, /* (107) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ - -9, /* (108) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ - -7, /* (109) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ - -7, /* (110) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ - 0, /* (111) dbnm ::= */ - -2, /* (112) dbnm ::= DOT nm */ - -1, /* (113) fullname ::= nm */ - -3, /* (114) fullname ::= nm DOT nm */ - -1, /* (115) xfullname ::= nm */ - -3, /* (116) xfullname ::= nm DOT nm */ - -5, /* (117) xfullname ::= nm DOT nm AS nm */ - -3, /* (118) xfullname ::= nm AS nm */ - -1, /* (119) joinop ::= COMMA|JOIN */ - -2, /* (120) joinop ::= JOIN_KW JOIN */ - -3, /* (121) joinop ::= JOIN_KW nm JOIN */ - -4, /* (122) joinop ::= JOIN_KW nm nm JOIN */ - -2, /* (123) on_opt ::= ON expr */ - 0, /* (124) on_opt ::= */ - 0, /* (125) indexed_opt ::= */ - -3, /* (126) indexed_opt ::= INDEXED BY nm */ - -2, /* (127) indexed_opt ::= NOT INDEXED */ - -4, /* (128) using_opt ::= USING LP idlist RP */ - 0, /* (129) using_opt ::= */ - 0, /* (130) orderby_opt ::= */ - -3, /* (131) orderby_opt ::= ORDER BY sortlist */ - -5, /* (132) sortlist ::= sortlist COMMA expr sortorder nulls */ - -3, /* (133) sortlist ::= expr sortorder nulls */ - -1, /* (134) sortorder ::= ASC */ - -1, /* (135) sortorder ::= DESC */ - 0, /* (136) sortorder ::= */ - -2, /* (137) nulls ::= NULLS FIRST */ - -2, /* (138) nulls ::= NULLS LAST */ - 0, /* (139) nulls ::= */ - 0, /* (140) groupby_opt ::= */ - -3, /* (141) groupby_opt ::= GROUP BY nexprlist */ - 0, /* (142) having_opt ::= */ - -2, /* (143) having_opt ::= HAVING expr */ - 0, /* (144) limit_opt ::= */ - -2, /* (145) limit_opt ::= LIMIT expr */ - -4, /* (146) limit_opt ::= LIMIT expr OFFSET expr */ - -4, /* (147) limit_opt ::= LIMIT expr COMMA expr */ - -6, /* (148) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ - 0, /* (149) where_opt ::= */ - -2, /* (150) where_opt ::= WHERE expr */ - 0, /* (151) where_opt_ret ::= */ - -2, /* (152) where_opt_ret ::= WHERE expr */ - -2, /* (153) where_opt_ret ::= RETURNING selcollist */ - -4, /* (154) where_opt_ret ::= WHERE expr RETURNING selcollist */ - -9, /* (155) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ - -5, /* (156) setlist ::= setlist COMMA nm EQ expr */ - -7, /* (157) setlist ::= setlist COMMA LP idlist RP EQ expr */ - -3, /* (158) setlist ::= nm EQ expr */ - -5, /* (159) setlist ::= LP idlist RP EQ expr */ - -7, /* (160) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - -8, /* (161) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ - 0, /* (162) upsert ::= */ - -2, /* (163) upsert ::= RETURNING selcollist */ - -12, /* (164) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ - -9, /* (165) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ - -5, /* (166) upsert ::= ON CONFLICT DO NOTHING returning */ - -8, /* (167) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ - -2, /* (168) returning ::= RETURNING selcollist */ - -2, /* (169) insert_cmd ::= INSERT orconf */ - -1, /* (170) insert_cmd ::= REPLACE */ - 0, /* (171) idlist_opt ::= */ - -3, /* (172) idlist_opt ::= LP idlist RP */ - -3, /* (173) idlist ::= idlist COMMA nm */ - -1, /* (174) idlist ::= nm */ - -3, /* (175) expr ::= LP expr RP */ - -1, /* (176) expr ::= ID|INDEXED */ - -1, /* (177) expr ::= JOIN_KW */ - -3, /* (178) expr ::= nm DOT nm */ - -5, /* (179) expr ::= nm DOT nm DOT nm */ - -1, /* (180) term ::= NULL|FLOAT|BLOB */ - -1, /* (181) term ::= STRING */ - -1, /* (182) term ::= INTEGER */ - -1, /* (183) expr ::= VARIABLE */ - -3, /* (184) expr ::= expr COLLATE ID|STRING */ - -6, /* (185) expr ::= CAST LP expr AS typetoken RP */ - -5, /* (186) expr ::= ID|INDEXED LP distinct exprlist RP */ - -4, /* (187) expr ::= ID|INDEXED LP STAR RP */ - -6, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ - -5, /* (189) expr ::= ID|INDEXED LP STAR RP filter_over */ - -1, /* (190) term ::= CTIME_KW */ - -5, /* (191) expr ::= LP nexprlist COMMA expr RP */ - -3, /* (192) expr ::= expr AND expr */ - -3, /* (193) expr ::= expr OR expr */ - -3, /* (194) expr ::= expr LT|GT|GE|LE expr */ - -3, /* (195) expr ::= expr EQ|NE expr */ - -3, /* (196) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - -3, /* (197) expr ::= expr PLUS|MINUS expr */ - -3, /* (198) expr ::= expr STAR|SLASH|REM expr */ - -3, /* (199) expr ::= expr CONCAT expr */ - -2, /* (200) likeop ::= NOT LIKE_KW|MATCH */ - -3, /* (201) expr ::= expr likeop expr */ - -5, /* (202) expr ::= expr likeop expr ESCAPE expr */ - -2, /* (203) expr ::= expr ISNULL|NOTNULL */ - -3, /* (204) expr ::= expr NOT NULL */ - -3, /* (205) expr ::= expr IS expr */ - -4, /* (206) expr ::= expr IS NOT expr */ - -2, /* (207) expr ::= NOT expr */ - -2, /* (208) expr ::= BITNOT expr */ - -2, /* (209) expr ::= PLUS|MINUS expr */ - -1, /* (210) between_op ::= BETWEEN */ - -2, /* (211) between_op ::= NOT BETWEEN */ - -5, /* (212) expr ::= expr between_op expr AND expr */ - -1, /* (213) in_op ::= IN */ - -2, /* (214) in_op ::= NOT IN */ - -5, /* (215) expr ::= expr in_op LP exprlist RP */ - -3, /* (216) expr ::= LP select RP */ - -5, /* (217) expr ::= expr in_op LP select RP */ - -5, /* (218) expr ::= expr in_op nm dbnm paren_exprlist */ - -4, /* (219) expr ::= EXISTS LP select RP */ - -5, /* (220) expr ::= CASE case_operand case_exprlist case_else END */ - -5, /* (221) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - -4, /* (222) case_exprlist ::= WHEN expr THEN expr */ - -2, /* (223) case_else ::= ELSE expr */ - 0, /* (224) case_else ::= */ - -1, /* (225) case_operand ::= expr */ - 0, /* (226) case_operand ::= */ - 0, /* (227) exprlist ::= */ - -3, /* (228) nexprlist ::= nexprlist COMMA expr */ - -1, /* (229) nexprlist ::= expr */ - 0, /* (230) paren_exprlist ::= */ - -3, /* (231) paren_exprlist ::= LP exprlist RP */ - -12, /* (232) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - -1, /* (233) uniqueflag ::= UNIQUE */ - 0, /* (234) uniqueflag ::= */ - 0, /* (235) eidlist_opt ::= */ - -3, /* (236) eidlist_opt ::= LP eidlist RP */ - -5, /* (237) eidlist ::= eidlist COMMA nm collate sortorder */ - -3, /* (238) eidlist ::= nm collate sortorder */ - 0, /* (239) collate ::= */ - -2, /* (240) collate ::= COLLATE ID|STRING */ - -4, /* (241) cmd ::= DROP INDEX ifexists fullname */ - -2, /* (242) cmd ::= VACUUM vinto */ - -3, /* (243) cmd ::= VACUUM nm vinto */ - -2, /* (244) vinto ::= INTO expr */ - 0, /* (245) vinto ::= */ - -3, /* (246) cmd ::= PRAGMA nm dbnm */ - -5, /* (247) cmd ::= PRAGMA nm dbnm EQ nmnum */ - -6, /* (248) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - -5, /* (249) cmd ::= PRAGMA nm dbnm EQ minus_num */ - -6, /* (250) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - -2, /* (251) plus_num ::= PLUS INTEGER|FLOAT */ - -2, /* (252) minus_num ::= MINUS INTEGER|FLOAT */ - -5, /* (253) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - -11, /* (254) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - -1, /* (255) trigger_time ::= BEFORE|AFTER */ - -2, /* (256) trigger_time ::= INSTEAD OF */ - 0, /* (257) trigger_time ::= */ - -1, /* (258) trigger_event ::= DELETE|INSERT */ - -1, /* (259) trigger_event ::= UPDATE */ - -3, /* (260) trigger_event ::= UPDATE OF idlist */ - 0, /* (261) when_clause ::= */ - -2, /* (262) when_clause ::= WHEN expr */ - -3, /* (263) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - -2, /* (264) trigger_cmd_list ::= trigger_cmd SEMI */ - -3, /* (265) trnm ::= nm DOT nm */ - -3, /* (266) tridxby ::= INDEXED BY nm */ - -2, /* (267) tridxby ::= NOT INDEXED */ - -9, /* (268) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ - -8, /* (269) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - -6, /* (270) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - -3, /* (271) trigger_cmd ::= scanpt select scanpt */ - -4, /* (272) expr ::= RAISE LP IGNORE RP */ - -6, /* (273) expr ::= RAISE LP raisetype COMMA nm RP */ - -1, /* (274) raisetype ::= ROLLBACK */ - -1, /* (275) raisetype ::= ABORT */ - -1, /* (276) raisetype ::= FAIL */ - -4, /* (277) cmd ::= DROP TRIGGER ifexists fullname */ - -6, /* (278) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - -3, /* (279) cmd ::= DETACH database_kw_opt expr */ - 0, /* (280) key_opt ::= */ - -2, /* (281) key_opt ::= KEY expr */ - -1, /* (282) cmd ::= REINDEX */ - -3, /* (283) cmd ::= REINDEX nm dbnm */ - -1, /* (284) cmd ::= ANALYZE */ - -3, /* (285) cmd ::= ANALYZE nm dbnm */ - -6, /* (286) cmd ::= ALTER TABLE fullname RENAME TO nm */ - -7, /* (287) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - -6, /* (288) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ - -1, /* (289) add_column_fullname ::= fullname */ - -8, /* (290) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - -1, /* (291) cmd ::= create_vtab */ - -4, /* (292) cmd ::= create_vtab LP vtabarglist RP */ - -8, /* (293) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 0, /* (294) vtabarg ::= */ - -1, /* (295) vtabargtoken ::= ANY */ - -3, /* (296) vtabargtoken ::= lp anylist RP */ - -1, /* (297) lp ::= LP */ - -2, /* (298) with ::= WITH wqlist */ - -3, /* (299) with ::= WITH RECURSIVE wqlist */ - -1, /* (300) wqas ::= AS */ - -2, /* (301) wqas ::= AS MATERIALIZED */ - -3, /* (302) wqas ::= AS NOT MATERIALIZED */ - -6, /* (303) wqitem ::= nm eidlist_opt wqas LP select RP */ - -1, /* (304) wqlist ::= wqitem */ - -3, /* (305) wqlist ::= wqlist COMMA wqitem */ - -1, /* (306) windowdefn_list ::= windowdefn */ - -3, /* (307) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - -5, /* (308) windowdefn ::= nm AS LP window RP */ - -5, /* (309) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - -6, /* (310) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - -4, /* (311) window ::= ORDER BY sortlist frame_opt */ - -5, /* (312) window ::= nm ORDER BY sortlist frame_opt */ - -1, /* (313) window ::= frame_opt */ - -2, /* (314) window ::= nm frame_opt */ - 0, /* (315) frame_opt ::= */ - -3, /* (316) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - -6, /* (317) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - -1, /* (318) range_or_rows ::= RANGE|ROWS|GROUPS */ - -1, /* (319) frame_bound_s ::= frame_bound */ - -2, /* (320) frame_bound_s ::= UNBOUNDED PRECEDING */ - -1, /* (321) frame_bound_e ::= frame_bound */ - -2, /* (322) frame_bound_e ::= UNBOUNDED FOLLOWING */ - -2, /* (323) frame_bound ::= expr PRECEDING|FOLLOWING */ - -2, /* (324) frame_bound ::= CURRENT ROW */ - 0, /* (325) frame_exclude_opt ::= */ - -2, /* (326) frame_exclude_opt ::= EXCLUDE frame_exclude */ - -2, /* (327) frame_exclude ::= NO OTHERS */ - -2, /* (328) frame_exclude ::= CURRENT ROW */ - -1, /* (329) frame_exclude ::= GROUP|TIES */ - -2, /* (330) window_clause ::= WINDOW windowdefn_list */ - -2, /* (331) filter_over ::= filter_clause over_clause */ - -1, /* (332) filter_over ::= over_clause */ - -1, /* (333) filter_over ::= filter_clause */ - -4, /* (334) over_clause ::= OVER LP window RP */ - -2, /* (335) over_clause ::= OVER nm */ - -5, /* (336) filter_clause ::= FILTER LP WHERE expr RP */ - -1, /* (337) input ::= cmdlist */ - -2, /* (338) cmdlist ::= cmdlist ecmd */ - -1, /* (339) cmdlist ::= ecmd */ - -1, /* (340) ecmd ::= SEMI */ - -2, /* (341) ecmd ::= cmdx SEMI */ - -3, /* (342) ecmd ::= explain cmdx SEMI */ - 0, /* (343) trans_opt ::= */ - -1, /* (344) trans_opt ::= TRANSACTION */ - -2, /* (345) trans_opt ::= TRANSACTION nm */ - -1, /* (346) savepoint_opt ::= SAVEPOINT */ - 0, /* (347) savepoint_opt ::= */ - -2, /* (348) cmd ::= create_table create_table_args */ - -4, /* (349) columnlist ::= columnlist COMMA columnname carglist */ - -2, /* (350) columnlist ::= columnname carglist */ - -1, /* (351) nm ::= ID|INDEXED */ - -1, /* (352) nm ::= STRING */ - -1, /* (353) nm ::= JOIN_KW */ - -1, /* (354) typetoken ::= typename */ - -1, /* (355) typename ::= ID|STRING */ - -1, /* (356) signed ::= plus_num */ - -1, /* (357) signed ::= minus_num */ - -2, /* (358) carglist ::= carglist ccons */ - 0, /* (359) carglist ::= */ - -2, /* (360) ccons ::= NULL onconf */ - -4, /* (361) ccons ::= GENERATED ALWAYS AS generated */ - -2, /* (362) ccons ::= AS generated */ - -2, /* (363) conslist_opt ::= COMMA conslist */ - -3, /* (364) conslist ::= conslist tconscomma tcons */ - -1, /* (365) conslist ::= tcons */ - 0, /* (366) tconscomma ::= */ - -1, /* (367) defer_subclause_opt ::= defer_subclause */ - -1, /* (368) resolvetype ::= raisetype */ - -1, /* (369) selectnowith ::= oneselect */ - -1, /* (370) oneselect ::= values */ - -2, /* (371) sclp ::= selcollist COMMA */ - -1, /* (372) as ::= ID|STRING */ - 0, /* (373) returning ::= */ - -1, /* (374) expr ::= term */ - -1, /* (375) likeop ::= LIKE_KW|MATCH */ - -1, /* (376) exprlist ::= nexprlist */ - -1, /* (377) nmnum ::= plus_num */ - -1, /* (378) nmnum ::= nm */ - -1, /* (379) nmnum ::= ON */ - -1, /* (380) nmnum ::= DELETE */ - -1, /* (381) nmnum ::= DEFAULT */ - -1, /* (382) plus_num ::= INTEGER|FLOAT */ - 0, /* (383) foreach_clause ::= */ - -3, /* (384) foreach_clause ::= FOR EACH ROW */ - -1, /* (385) trnm ::= nm */ - 0, /* (386) tridxby ::= */ - -1, /* (387) database_kw_opt ::= DATABASE */ - 0, /* (388) database_kw_opt ::= */ - 0, /* (389) kwcolumn_opt ::= */ - -1, /* (390) kwcolumn_opt ::= COLUMNKW */ - -1, /* (391) vtabarglist ::= vtabarg */ - -3, /* (392) vtabarglist ::= vtabarglist COMMA vtabarg */ - -2, /* (393) vtabarg ::= vtabarg vtabargtoken */ - 0, /* (394) anylist ::= */ - -4, /* (395) anylist ::= anylist LP anylist RP */ - -2, /* (396) anylist ::= anylist ANY */ - 0, /* (397) with ::= */ + 0, /* (21) table_option_set ::= */ + -3, /* (22) table_option_set ::= table_option_set COMMA table_option */ + -2, /* (23) table_option ::= WITHOUT nm */ + -1, /* (24) table_option ::= nm */ + -2, /* (25) columnname ::= nm typetoken */ + 0, /* (26) typetoken ::= */ + -4, /* (27) typetoken ::= typename LP signed RP */ + -6, /* (28) typetoken ::= typename LP signed COMMA signed RP */ + -2, /* (29) typename ::= typename ID|STRING */ + 0, /* (30) scanpt ::= */ + 0, /* (31) scantok ::= */ + -2, /* (32) ccons ::= CONSTRAINT nm */ + -3, /* (33) ccons ::= DEFAULT scantok term */ + -4, /* (34) ccons ::= DEFAULT LP expr RP */ + -4, /* (35) ccons ::= DEFAULT PLUS scantok term */ + -4, /* (36) ccons ::= DEFAULT MINUS scantok term */ + -3, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ + -3, /* (38) ccons ::= NOT NULL onconf */ + -5, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + -2, /* (40) ccons ::= UNIQUE onconf */ + -4, /* (41) ccons ::= CHECK LP expr RP */ + -4, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ + -1, /* (43) ccons ::= defer_subclause */ + -2, /* (44) ccons ::= COLLATE ID|STRING */ + -3, /* (45) generated ::= LP expr RP */ + -4, /* (46) generated ::= LP expr RP ID */ + 0, /* (47) autoinc ::= */ + -1, /* (48) autoinc ::= AUTOINCR */ + 0, /* (49) refargs ::= */ + -2, /* (50) refargs ::= refargs refarg */ + -2, /* (51) refarg ::= MATCH nm */ + -3, /* (52) refarg ::= ON INSERT refact */ + -3, /* (53) refarg ::= ON DELETE refact */ + -3, /* (54) refarg ::= ON UPDATE refact */ + -2, /* (55) refact ::= SET NULL */ + -2, /* (56) refact ::= SET DEFAULT */ + -1, /* (57) refact ::= CASCADE */ + -1, /* (58) refact ::= RESTRICT */ + -2, /* (59) refact ::= NO ACTION */ + -3, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + -2, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 0, /* (62) init_deferred_pred_opt ::= */ + -2, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + -2, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 0, /* (65) conslist_opt ::= */ + -1, /* (66) tconscomma ::= COMMA */ + -2, /* (67) tcons ::= CONSTRAINT nm */ + -7, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + -5, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ + -5, /* (70) tcons ::= CHECK LP expr RP onconf */ + -10, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 0, /* (72) defer_subclause_opt ::= */ + 0, /* (73) onconf ::= */ + -3, /* (74) onconf ::= ON CONFLICT resolvetype */ + 0, /* (75) orconf ::= */ + -2, /* (76) orconf ::= OR resolvetype */ + -1, /* (77) resolvetype ::= IGNORE */ + -1, /* (78) resolvetype ::= REPLACE */ + -4, /* (79) cmd ::= DROP TABLE ifexists fullname */ + -2, /* (80) ifexists ::= IF EXISTS */ + 0, /* (81) ifexists ::= */ + -9, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + -4, /* (83) cmd ::= DROP VIEW ifexists fullname */ + -1, /* (84) cmd ::= select */ + -3, /* (85) select ::= WITH wqlist selectnowith */ + -4, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ + -1, /* (87) select ::= selectnowith */ + -3, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ + -1, /* (89) multiselect_op ::= UNION */ + -2, /* (90) multiselect_op ::= UNION ALL */ + -1, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ + -9, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + -10, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + -4, /* (94) values ::= VALUES LP nexprlist RP */ + -5, /* (95) values ::= values COMMA LP nexprlist RP */ + -1, /* (96) distinct ::= DISTINCT */ + -1, /* (97) distinct ::= ALL */ + 0, /* (98) distinct ::= */ + 0, /* (99) sclp ::= */ + -5, /* (100) selcollist ::= sclp scanpt expr scanpt as */ + -3, /* (101) selcollist ::= sclp scanpt STAR */ + -5, /* (102) selcollist ::= sclp scanpt nm DOT STAR */ + -2, /* (103) as ::= AS nm */ + 0, /* (104) as ::= */ + 0, /* (105) from ::= */ + -2, /* (106) from ::= FROM seltablist */ + -2, /* (107) stl_prefix ::= seltablist joinop */ + 0, /* (108) stl_prefix ::= */ + -5, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */ + -6, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + -8, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + -6, /* (112) seltablist ::= stl_prefix LP select RP as on_using */ + -6, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */ + 0, /* (114) dbnm ::= */ + -2, /* (115) dbnm ::= DOT nm */ + -1, /* (116) fullname ::= nm */ + -3, /* (117) fullname ::= nm DOT nm */ + -1, /* (118) xfullname ::= nm */ + -3, /* (119) xfullname ::= nm DOT nm */ + -5, /* (120) xfullname ::= nm DOT nm AS nm */ + -3, /* (121) xfullname ::= nm AS nm */ + -1, /* (122) joinop ::= COMMA|JOIN */ + -2, /* (123) joinop ::= JOIN_KW JOIN */ + -3, /* (124) joinop ::= JOIN_KW nm JOIN */ + -4, /* (125) joinop ::= JOIN_KW nm nm JOIN */ + -2, /* (126) on_using ::= ON expr */ + -4, /* (127) on_using ::= USING LP idlist RP */ + 0, /* (128) on_using ::= */ + 0, /* (129) indexed_opt ::= */ + -3, /* (130) indexed_by ::= INDEXED BY nm */ + -2, /* (131) indexed_by ::= NOT INDEXED */ + 0, /* (132) orderby_opt ::= */ + -3, /* (133) orderby_opt ::= ORDER BY sortlist */ + -5, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */ + -3, /* (135) sortlist ::= expr sortorder nulls */ + -1, /* (136) sortorder ::= ASC */ + -1, /* (137) sortorder ::= DESC */ + 0, /* (138) sortorder ::= */ + -2, /* (139) nulls ::= NULLS FIRST */ + -2, /* (140) nulls ::= NULLS LAST */ + 0, /* (141) nulls ::= */ + 0, /* (142) groupby_opt ::= */ + -3, /* (143) groupby_opt ::= GROUP BY nexprlist */ + 0, /* (144) having_opt ::= */ + -2, /* (145) having_opt ::= HAVING expr */ + 0, /* (146) limit_opt ::= */ + -2, /* (147) limit_opt ::= LIMIT expr */ + -4, /* (148) limit_opt ::= LIMIT expr OFFSET expr */ + -4, /* (149) limit_opt ::= LIMIT expr COMMA expr */ + -6, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + 0, /* (151) where_opt ::= */ + -2, /* (152) where_opt ::= WHERE expr */ + 0, /* (153) where_opt_ret ::= */ + -2, /* (154) where_opt_ret ::= WHERE expr */ + -2, /* (155) where_opt_ret ::= RETURNING selcollist */ + -4, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */ + -9, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + -5, /* (158) setlist ::= setlist COMMA nm EQ expr */ + -7, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */ + -3, /* (160) setlist ::= nm EQ expr */ + -5, /* (161) setlist ::= LP idlist RP EQ expr */ + -7, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + -8, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + 0, /* (164) upsert ::= */ + -2, /* (165) upsert ::= RETURNING selcollist */ + -12, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ + -9, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ + -5, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */ + -8, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ + -2, /* (170) returning ::= RETURNING selcollist */ + -2, /* (171) insert_cmd ::= INSERT orconf */ + -1, /* (172) insert_cmd ::= REPLACE */ + 0, /* (173) idlist_opt ::= */ + -3, /* (174) idlist_opt ::= LP idlist RP */ + -3, /* (175) idlist ::= idlist COMMA nm */ + -1, /* (176) idlist ::= nm */ + -3, /* (177) expr ::= LP expr RP */ + -1, /* (178) expr ::= ID|INDEXED */ + -1, /* (179) expr ::= JOIN_KW */ + -3, /* (180) expr ::= nm DOT nm */ + -5, /* (181) expr ::= nm DOT nm DOT nm */ + -1, /* (182) term ::= NULL|FLOAT|BLOB */ + -1, /* (183) term ::= STRING */ + -1, /* (184) term ::= INTEGER */ + -1, /* (185) expr ::= VARIABLE */ + -3, /* (186) expr ::= expr COLLATE ID|STRING */ + -6, /* (187) expr ::= CAST LP expr AS typetoken RP */ + -5, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP */ + -4, /* (189) expr ::= ID|INDEXED LP STAR RP */ + -6, /* (190) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ + -5, /* (191) expr ::= ID|INDEXED LP STAR RP filter_over */ + -1, /* (192) term ::= CTIME_KW */ + -5, /* (193) expr ::= LP nexprlist COMMA expr RP */ + -3, /* (194) expr ::= expr AND expr */ + -3, /* (195) expr ::= expr OR expr */ + -3, /* (196) expr ::= expr LT|GT|GE|LE expr */ + -3, /* (197) expr ::= expr EQ|NE expr */ + -3, /* (198) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + -3, /* (199) expr ::= expr PLUS|MINUS expr */ + -3, /* (200) expr ::= expr STAR|SLASH|REM expr */ + -3, /* (201) expr ::= expr CONCAT expr */ + -2, /* (202) likeop ::= NOT LIKE_KW|MATCH */ + -3, /* (203) expr ::= expr likeop expr */ + -5, /* (204) expr ::= expr likeop expr ESCAPE expr */ + -2, /* (205) expr ::= expr ISNULL|NOTNULL */ + -3, /* (206) expr ::= expr NOT NULL */ + -3, /* (207) expr ::= expr IS expr */ + -4, /* (208) expr ::= expr IS NOT expr */ + -6, /* (209) expr ::= expr IS NOT DISTINCT FROM expr */ + -5, /* (210) expr ::= expr IS DISTINCT FROM expr */ + -2, /* (211) expr ::= NOT expr */ + -2, /* (212) expr ::= BITNOT expr */ + -2, /* (213) expr ::= PLUS|MINUS expr */ + -3, /* (214) expr ::= expr PTR expr */ + -1, /* (215) between_op ::= BETWEEN */ + -2, /* (216) between_op ::= NOT BETWEEN */ + -5, /* (217) expr ::= expr between_op expr AND expr */ + -1, /* (218) in_op ::= IN */ + -2, /* (219) in_op ::= NOT IN */ + -5, /* (220) expr ::= expr in_op LP exprlist RP */ + -3, /* (221) expr ::= LP select RP */ + -5, /* (222) expr ::= expr in_op LP select RP */ + -5, /* (223) expr ::= expr in_op nm dbnm paren_exprlist */ + -4, /* (224) expr ::= EXISTS LP select RP */ + -5, /* (225) expr ::= CASE case_operand case_exprlist case_else END */ + -5, /* (226) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + -4, /* (227) case_exprlist ::= WHEN expr THEN expr */ + -2, /* (228) case_else ::= ELSE expr */ + 0, /* (229) case_else ::= */ + -1, /* (230) case_operand ::= expr */ + 0, /* (231) case_operand ::= */ + 0, /* (232) exprlist ::= */ + -3, /* (233) nexprlist ::= nexprlist COMMA expr */ + -1, /* (234) nexprlist ::= expr */ + 0, /* (235) paren_exprlist ::= */ + -3, /* (236) paren_exprlist ::= LP exprlist RP */ + -12, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + -1, /* (238) uniqueflag ::= UNIQUE */ + 0, /* (239) uniqueflag ::= */ + 0, /* (240) eidlist_opt ::= */ + -3, /* (241) eidlist_opt ::= LP eidlist RP */ + -5, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */ + -3, /* (243) eidlist ::= nm collate sortorder */ + 0, /* (244) collate ::= */ + -2, /* (245) collate ::= COLLATE ID|STRING */ + -4, /* (246) cmd ::= DROP INDEX ifexists fullname */ + -2, /* (247) cmd ::= VACUUM vinto */ + -3, /* (248) cmd ::= VACUUM nm vinto */ + -2, /* (249) vinto ::= INTO expr */ + 0, /* (250) vinto ::= */ + -3, /* (251) cmd ::= PRAGMA nm dbnm */ + -5, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */ + -6, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + -5, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */ + -6, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + -2, /* (256) plus_num ::= PLUS INTEGER|FLOAT */ + -2, /* (257) minus_num ::= MINUS INTEGER|FLOAT */ + -5, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + -11, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + -1, /* (260) trigger_time ::= BEFORE|AFTER */ + -2, /* (261) trigger_time ::= INSTEAD OF */ + 0, /* (262) trigger_time ::= */ + -1, /* (263) trigger_event ::= DELETE|INSERT */ + -1, /* (264) trigger_event ::= UPDATE */ + -3, /* (265) trigger_event ::= UPDATE OF idlist */ + 0, /* (266) when_clause ::= */ + -2, /* (267) when_clause ::= WHEN expr */ + -3, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + -2, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */ + -3, /* (270) trnm ::= nm DOT nm */ + -3, /* (271) tridxby ::= INDEXED BY nm */ + -2, /* (272) tridxby ::= NOT INDEXED */ + -9, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + -8, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + -6, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + -3, /* (276) trigger_cmd ::= scanpt select scanpt */ + -4, /* (277) expr ::= RAISE LP IGNORE RP */ + -6, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */ + -1, /* (279) raisetype ::= ROLLBACK */ + -1, /* (280) raisetype ::= ABORT */ + -1, /* (281) raisetype ::= FAIL */ + -4, /* (282) cmd ::= DROP TRIGGER ifexists fullname */ + -6, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + -3, /* (284) cmd ::= DETACH database_kw_opt expr */ + 0, /* (285) key_opt ::= */ + -2, /* (286) key_opt ::= KEY expr */ + -1, /* (287) cmd ::= REINDEX */ + -3, /* (288) cmd ::= REINDEX nm dbnm */ + -1, /* (289) cmd ::= ANALYZE */ + -3, /* (290) cmd ::= ANALYZE nm dbnm */ + -6, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */ + -7, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + -6, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + -1, /* (294) add_column_fullname ::= fullname */ + -8, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + -1, /* (296) cmd ::= create_vtab */ + -4, /* (297) cmd ::= create_vtab LP vtabarglist RP */ + -8, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 0, /* (299) vtabarg ::= */ + -1, /* (300) vtabargtoken ::= ANY */ + -3, /* (301) vtabargtoken ::= lp anylist RP */ + -1, /* (302) lp ::= LP */ + -2, /* (303) with ::= WITH wqlist */ + -3, /* (304) with ::= WITH RECURSIVE wqlist */ + -1, /* (305) wqas ::= AS */ + -2, /* (306) wqas ::= AS MATERIALIZED */ + -3, /* (307) wqas ::= AS NOT MATERIALIZED */ + -6, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */ + -1, /* (309) wqlist ::= wqitem */ + -3, /* (310) wqlist ::= wqlist COMMA wqitem */ + -1, /* (311) windowdefn_list ::= windowdefn */ + -3, /* (312) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + -5, /* (313) windowdefn ::= nm AS LP window RP */ + -5, /* (314) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + -6, /* (315) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + -4, /* (316) window ::= ORDER BY sortlist frame_opt */ + -5, /* (317) window ::= nm ORDER BY sortlist frame_opt */ + -1, /* (318) window ::= frame_opt */ + -2, /* (319) window ::= nm frame_opt */ + 0, /* (320) frame_opt ::= */ + -3, /* (321) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + -6, /* (322) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + -1, /* (323) range_or_rows ::= RANGE|ROWS|GROUPS */ + -1, /* (324) frame_bound_s ::= frame_bound */ + -2, /* (325) frame_bound_s ::= UNBOUNDED PRECEDING */ + -1, /* (326) frame_bound_e ::= frame_bound */ + -2, /* (327) frame_bound_e ::= UNBOUNDED FOLLOWING */ + -2, /* (328) frame_bound ::= expr PRECEDING|FOLLOWING */ + -2, /* (329) frame_bound ::= CURRENT ROW */ + 0, /* (330) frame_exclude_opt ::= */ + -2, /* (331) frame_exclude_opt ::= EXCLUDE frame_exclude */ + -2, /* (332) frame_exclude ::= NO OTHERS */ + -2, /* (333) frame_exclude ::= CURRENT ROW */ + -1, /* (334) frame_exclude ::= GROUP|TIES */ + -2, /* (335) window_clause ::= WINDOW windowdefn_list */ + -2, /* (336) filter_over ::= filter_clause over_clause */ + -1, /* (337) filter_over ::= over_clause */ + -1, /* (338) filter_over ::= filter_clause */ + -4, /* (339) over_clause ::= OVER LP window RP */ + -2, /* (340) over_clause ::= OVER nm */ + -5, /* (341) filter_clause ::= FILTER LP WHERE expr RP */ + -1, /* (342) input ::= cmdlist */ + -2, /* (343) cmdlist ::= cmdlist ecmd */ + -1, /* (344) cmdlist ::= ecmd */ + -1, /* (345) ecmd ::= SEMI */ + -2, /* (346) ecmd ::= cmdx SEMI */ + -3, /* (347) ecmd ::= explain cmdx SEMI */ + 0, /* (348) trans_opt ::= */ + -1, /* (349) trans_opt ::= TRANSACTION */ + -2, /* (350) trans_opt ::= TRANSACTION nm */ + -1, /* (351) savepoint_opt ::= SAVEPOINT */ + 0, /* (352) savepoint_opt ::= */ + -2, /* (353) cmd ::= create_table create_table_args */ + -1, /* (354) table_option_set ::= table_option */ + -4, /* (355) columnlist ::= columnlist COMMA columnname carglist */ + -2, /* (356) columnlist ::= columnname carglist */ + -1, /* (357) nm ::= ID|INDEXED */ + -1, /* (358) nm ::= STRING */ + -1, /* (359) nm ::= JOIN_KW */ + -1, /* (360) typetoken ::= typename */ + -1, /* (361) typename ::= ID|STRING */ + -1, /* (362) signed ::= plus_num */ + -1, /* (363) signed ::= minus_num */ + -2, /* (364) carglist ::= carglist ccons */ + 0, /* (365) carglist ::= */ + -2, /* (366) ccons ::= NULL onconf */ + -4, /* (367) ccons ::= GENERATED ALWAYS AS generated */ + -2, /* (368) ccons ::= AS generated */ + -2, /* (369) conslist_opt ::= COMMA conslist */ + -3, /* (370) conslist ::= conslist tconscomma tcons */ + -1, /* (371) conslist ::= tcons */ + 0, /* (372) tconscomma ::= */ + -1, /* (373) defer_subclause_opt ::= defer_subclause */ + -1, /* (374) resolvetype ::= raisetype */ + -1, /* (375) selectnowith ::= oneselect */ + -1, /* (376) oneselect ::= values */ + -2, /* (377) sclp ::= selcollist COMMA */ + -1, /* (378) as ::= ID|STRING */ + -1, /* (379) indexed_opt ::= indexed_by */ + 0, /* (380) returning ::= */ + -1, /* (381) expr ::= term */ + -1, /* (382) likeop ::= LIKE_KW|MATCH */ + -1, /* (383) exprlist ::= nexprlist */ + -1, /* (384) nmnum ::= plus_num */ + -1, /* (385) nmnum ::= nm */ + -1, /* (386) nmnum ::= ON */ + -1, /* (387) nmnum ::= DELETE */ + -1, /* (388) nmnum ::= DEFAULT */ + -1, /* (389) plus_num ::= INTEGER|FLOAT */ + 0, /* (390) foreach_clause ::= */ + -3, /* (391) foreach_clause ::= FOR EACH ROW */ + -1, /* (392) trnm ::= nm */ + 0, /* (393) tridxby ::= */ + -1, /* (394) database_kw_opt ::= DATABASE */ + 0, /* (395) database_kw_opt ::= */ + 0, /* (396) kwcolumn_opt ::= */ + -1, /* (397) kwcolumn_opt ::= COLUMNKW */ + -1, /* (398) vtabarglist ::= vtabarg */ + -3, /* (399) vtabarglist ::= vtabarglist COMMA vtabarg */ + -2, /* (400) vtabarg ::= vtabarg vtabargtoken */ + 0, /* (401) anylist ::= */ + -4, /* (402) anylist ::= anylist LP anylist RP */ + -2, /* (403) anylist ::= anylist ANY */ + 0, /* (404) with ::= */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -160608,16 +170515,16 @@ static YYACTIONTYPE yy_reduce( { wx_sqlite3FinishCoding(pParse); } break; case 3: /* cmd ::= BEGIN transtype trans_opt */ -{wx_sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy60);} +{wx_sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy394);} break; case 4: /* transtype ::= */ -{yymsp[1].minor.yy60 = TK_DEFERRED;} +{yymsp[1].minor.yy394 = TK_DEFERRED;} break; case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); - case 318: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==318); -{yymsp[0].minor.yy60 = yymsp[0].major; /*A-overwrites-X*/} + case 323: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==323); +{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); @@ -160640,7 +170547,7 @@ static YYACTIONTYPE yy_reduce( break; case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ { - wx_sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy60,0,0,yymsp[-2].minor.yy60); + wx_sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy394,0,0,yymsp[-2].minor.yy394); } break; case 14: /* createkw ::= CREATE */ @@ -160648,96 +170555,112 @@ static YYACTIONTYPE yy_reduce( break; case 15: /* ifnotexists ::= */ case 18: /* temp ::= */ yytestcase(yyruleno==18); - case 21: /* table_options ::= */ yytestcase(yyruleno==21); - case 45: /* autoinc ::= */ yytestcase(yyruleno==45); - case 60: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==60); - case 70: /* defer_subclause_opt ::= */ yytestcase(yyruleno==70); - case 79: /* ifexists ::= */ yytestcase(yyruleno==79); - case 96: /* distinct ::= */ yytestcase(yyruleno==96); - case 239: /* collate ::= */ yytestcase(yyruleno==239); -{yymsp[1].minor.yy60 = 0;} + case 47: /* autoinc ::= */ yytestcase(yyruleno==47); + case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62); + case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72); + case 81: /* ifexists ::= */ yytestcase(yyruleno==81); + case 98: /* distinct ::= */ yytestcase(yyruleno==98); + case 244: /* collate ::= */ yytestcase(yyruleno==244); +{yymsp[1].minor.yy394 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ -{yymsp[-2].minor.yy60 = 1;} +{yymsp[-2].minor.yy394 = 1;} break; case 17: /* temp ::= TEMP */ - case 46: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==46); -{yymsp[0].minor.yy60 = 1;} +{yymsp[0].minor.yy394 = pParse->db->init.busy==0;} break; - case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */ + case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */ { - wx_sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy60,0); + wx_sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy285,0); } break; case 20: /* create_table_args ::= AS select */ { - wx_sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy307); - wx_sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy307); + wx_sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy47); + wx_sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47); } break; - case 22: /* table_options ::= WITHOUT nm */ + case 21: /* table_option_set ::= */ +{yymsp[1].minor.yy285 = 0;} + break; + case 22: /* table_option_set ::= table_option_set COMMA table_option */ +{yylhsminor.yy285 = yymsp[-2].minor.yy285|yymsp[0].minor.yy285;} + yymsp[-2].minor.yy285 = yylhsminor.yy285; + break; + case 23: /* table_option ::= WITHOUT nm */ { if( yymsp[0].minor.yy0.n==5 && wx_sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ - yymsp[-1].minor.yy60 = TF_WithoutRowid | TF_NoVisibleRowid; + yymsp[-1].minor.yy285 = TF_WithoutRowid | TF_NoVisibleRowid; + }else{ + yymsp[-1].minor.yy285 = 0; + wx_sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); + } +} + break; + case 24: /* table_option ::= nm */ +{ + if( yymsp[0].minor.yy0.n==6 && wx_sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){ + yylhsminor.yy285 = TF_Strict; }else{ - yymsp[-1].minor.yy60 = 0; + yylhsminor.yy285 = 0; wx_sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } + yymsp[0].minor.yy285 = yylhsminor.yy285; break; - case 23: /* columnname ::= nm typetoken */ -{wx_sqlite3AddColumn(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} + case 25: /* columnname ::= nm typetoken */ +{wx_sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);} break; - case 24: /* typetoken ::= */ - case 63: /* conslist_opt ::= */ yytestcase(yyruleno==63); - case 102: /* as ::= */ yytestcase(yyruleno==102); + case 26: /* typetoken ::= */ + case 65: /* conslist_opt ::= */ yytestcase(yyruleno==65); + case 104: /* as ::= */ yytestcase(yyruleno==104); {yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;} break; - case 25: /* typetoken ::= typename LP signed RP */ + case 27: /* typetoken ::= typename LP signed RP */ { yymsp[-3].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z); } break; - case 26: /* typetoken ::= typename LP signed COMMA signed RP */ + case 28: /* typetoken ::= typename LP signed COMMA signed RP */ { yymsp[-5].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z); } break; - case 27: /* typename ::= typename ID|STRING */ + case 29: /* typename ::= typename ID|STRING */ {yymsp[-1].minor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);} break; - case 28: /* scanpt ::= */ + case 30: /* scanpt ::= */ { assert( yyLookahead!=YYNOCODE ); - yymsp[1].minor.yy528 = yyLookaheadToken.z; + yymsp[1].minor.yy522 = yyLookaheadToken.z; } break; - case 29: /* scantok ::= */ + case 31: /* scantok ::= */ { assert( yyLookahead!=YYNOCODE ); yymsp[1].minor.yy0 = yyLookaheadToken; } break; - case 30: /* ccons ::= CONSTRAINT nm */ - case 65: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==65); + case 32: /* ccons ::= CONSTRAINT nm */ + case 67: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==67); {pParse->constraintName = yymsp[0].minor.yy0;} break; - case 31: /* ccons ::= DEFAULT scantok term */ -{wx_sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy602,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} + case 33: /* ccons ::= DEFAULT scantok term */ +{wx_sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; - case 32: /* ccons ::= DEFAULT LP expr RP */ -{wx_sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy602,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} + case 34: /* ccons ::= DEFAULT LP expr RP */ +{wx_sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} break; - case 33: /* ccons ::= DEFAULT PLUS scantok term */ -{wx_sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy602,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} + case 35: /* ccons ::= DEFAULT PLUS scantok term */ +{wx_sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; - case 34: /* ccons ::= DEFAULT MINUS scantok term */ + case 36: /* ccons ::= DEFAULT MINUS scantok term */ { - Expr *p = wx_sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy602, 0); + Expr *p = wx_sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy528, 0); wx_sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]); } break; - case 35: /* ccons ::= DEFAULT scantok ID|INDEXED */ + case 37: /* ccons ::= DEFAULT scantok ID|INDEXED */ { Expr *p = tokenExpr(pParse, TK_STRING, yymsp[0].minor.yy0); if( p ){ @@ -160747,305 +170670,316 @@ static YYACTIONTYPE yy_reduce( wx_sqlite3AddDefaultValue(pParse,p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.z+yymsp[0].minor.yy0.n); } break; - case 36: /* ccons ::= NOT NULL onconf */ -{wx_sqlite3AddNotNull(pParse, yymsp[0].minor.yy60);} + case 38: /* ccons ::= NOT NULL onconf */ +{wx_sqlite3AddNotNull(pParse, yymsp[0].minor.yy394);} break; - case 37: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{wx_sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy60,yymsp[0].minor.yy60,yymsp[-2].minor.yy60);} + case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ +{wx_sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy394,yymsp[0].minor.yy394,yymsp[-2].minor.yy394);} break; - case 38: /* ccons ::= UNIQUE onconf */ -{wx_sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy60,0,0,0,0, + case 40: /* ccons ::= UNIQUE onconf */ +{wx_sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy394,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; - case 39: /* ccons ::= CHECK LP expr RP */ -{wx_sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy602,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} + case 41: /* ccons ::= CHECK LP expr RP */ +{wx_sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} break; - case 40: /* ccons ::= REFERENCES nm eidlist_opt refargs */ -{wx_sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy338,yymsp[0].minor.yy60);} + case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */ +{wx_sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy322,yymsp[0].minor.yy394);} break; - case 41: /* ccons ::= defer_subclause */ -{wx_sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy60);} + case 43: /* ccons ::= defer_subclause */ +{wx_sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy394);} break; - case 42: /* ccons ::= COLLATE ID|STRING */ + case 44: /* ccons ::= COLLATE ID|STRING */ {wx_sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} break; - case 43: /* generated ::= LP expr RP */ -{wx_sqlite3AddGenerated(pParse,yymsp[-1].minor.yy602,0);} + case 45: /* generated ::= LP expr RP */ +{wx_sqlite3AddGenerated(pParse,yymsp[-1].minor.yy528,0);} break; - case 44: /* generated ::= LP expr RP ID */ -{wx_sqlite3AddGenerated(pParse,yymsp[-2].minor.yy602,&yymsp[0].minor.yy0);} + case 46: /* generated ::= LP expr RP ID */ +{wx_sqlite3AddGenerated(pParse,yymsp[-2].minor.yy528,&yymsp[0].minor.yy0);} break; - case 47: /* refargs ::= */ -{ yymsp[1].minor.yy60 = OE_None*0x0101; /* EV: R-19803-45884 */} + case 48: /* autoinc ::= AUTOINCR */ +{yymsp[0].minor.yy394 = 1;} break; - case 48: /* refargs ::= refargs refarg */ -{ yymsp[-1].minor.yy60 = (yymsp[-1].minor.yy60 & ~yymsp[0].minor.yy615.mask) | yymsp[0].minor.yy615.value; } + case 49: /* refargs ::= */ +{ yymsp[1].minor.yy394 = OE_None*0x0101; /* EV: R-19803-45884 */} break; - case 49: /* refarg ::= MATCH nm */ -{ yymsp[-1].minor.yy615.value = 0; yymsp[-1].minor.yy615.mask = 0x000000; } + case 50: /* refargs ::= refargs refarg */ +{ yymsp[-1].minor.yy394 = (yymsp[-1].minor.yy394 & ~yymsp[0].minor.yy231.mask) | yymsp[0].minor.yy231.value; } break; - case 50: /* refarg ::= ON INSERT refact */ -{ yymsp[-2].minor.yy615.value = 0; yymsp[-2].minor.yy615.mask = 0x000000; } + case 51: /* refarg ::= MATCH nm */ +{ yymsp[-1].minor.yy231.value = 0; yymsp[-1].minor.yy231.mask = 0x000000; } break; - case 51: /* refarg ::= ON DELETE refact */ -{ yymsp[-2].minor.yy615.value = yymsp[0].minor.yy60; yymsp[-2].minor.yy615.mask = 0x0000ff; } + case 52: /* refarg ::= ON INSERT refact */ +{ yymsp[-2].minor.yy231.value = 0; yymsp[-2].minor.yy231.mask = 0x000000; } break; - case 52: /* refarg ::= ON UPDATE refact */ -{ yymsp[-2].minor.yy615.value = yymsp[0].minor.yy60<<8; yymsp[-2].minor.yy615.mask = 0x00ff00; } + case 53: /* refarg ::= ON DELETE refact */ +{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394; yymsp[-2].minor.yy231.mask = 0x0000ff; } break; - case 53: /* refact ::= SET NULL */ -{ yymsp[-1].minor.yy60 = OE_SetNull; /* EV: R-33326-45252 */} + case 54: /* refarg ::= ON UPDATE refact */ +{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394<<8; yymsp[-2].minor.yy231.mask = 0x00ff00; } break; - case 54: /* refact ::= SET DEFAULT */ -{ yymsp[-1].minor.yy60 = OE_SetDflt; /* EV: R-33326-45252 */} + case 55: /* refact ::= SET NULL */ +{ yymsp[-1].minor.yy394 = OE_SetNull; /* EV: R-33326-45252 */} break; - case 55: /* refact ::= CASCADE */ -{ yymsp[0].minor.yy60 = OE_Cascade; /* EV: R-33326-45252 */} + case 56: /* refact ::= SET DEFAULT */ +{ yymsp[-1].minor.yy394 = OE_SetDflt; /* EV: R-33326-45252 */} break; - case 56: /* refact ::= RESTRICT */ -{ yymsp[0].minor.yy60 = OE_Restrict; /* EV: R-33326-45252 */} + case 57: /* refact ::= CASCADE */ +{ yymsp[0].minor.yy394 = OE_Cascade; /* EV: R-33326-45252 */} break; - case 57: /* refact ::= NO ACTION */ -{ yymsp[-1].minor.yy60 = OE_None; /* EV: R-33326-45252 */} + case 58: /* refact ::= RESTRICT */ +{ yymsp[0].minor.yy394 = OE_Restrict; /* EV: R-33326-45252 */} break; - case 58: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -{yymsp[-2].minor.yy60 = 0;} + case 59: /* refact ::= NO ACTION */ +{ yymsp[-1].minor.yy394 = OE_None; /* EV: R-33326-45252 */} break; - case 59: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - case 74: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==74); - case 169: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==169); -{yymsp[-1].minor.yy60 = yymsp[0].minor.yy60;} + case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ +{yymsp[-2].minor.yy394 = 0;} break; - case 61: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ - case 78: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==78); - case 211: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==211); - case 214: /* in_op ::= NOT IN */ yytestcase(yyruleno==214); - case 240: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==240); -{yymsp[-1].minor.yy60 = 1;} + case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76); + case 171: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==171); +{yymsp[-1].minor.yy394 = yymsp[0].minor.yy394;} break; - case 62: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -{yymsp[-1].minor.yy60 = 0;} + case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ + case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); + case 216: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==216); + case 219: /* in_op ::= NOT IN */ yytestcase(yyruleno==219); + case 245: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==245); +{yymsp[-1].minor.yy394 = 1;} break; - case 64: /* tconscomma ::= COMMA */ + case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ +{yymsp[-1].minor.yy394 = 0;} + break; + case 66: /* tconscomma ::= COMMA */ {pParse->constraintName.n = 0;} break; - case 66: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -{wx_sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy338,yymsp[0].minor.yy60,yymsp[-2].minor.yy60,0);} + case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ +{wx_sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy322,yymsp[0].minor.yy394,yymsp[-2].minor.yy394,0);} break; - case 67: /* tcons ::= UNIQUE LP sortlist RP onconf */ -{wx_sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy338,yymsp[0].minor.yy60,0,0,0,0, + case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */ +{wx_sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy322,yymsp[0].minor.yy394,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; - case 68: /* tcons ::= CHECK LP expr RP onconf */ -{wx_sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy602,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} + case 70: /* tcons ::= CHECK LP expr RP onconf */ +{wx_sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy528,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} break; - case 69: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ { - wx_sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy338, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy338, yymsp[-1].minor.yy60); - wx_sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy60); + wx_sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy322, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[-1].minor.yy394); + wx_sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy394); } break; - case 71: /* onconf ::= */ - case 73: /* orconf ::= */ yytestcase(yyruleno==73); -{yymsp[1].minor.yy60 = OE_Default;} + case 73: /* onconf ::= */ + case 75: /* orconf ::= */ yytestcase(yyruleno==75); +{yymsp[1].minor.yy394 = OE_Default;} break; - case 72: /* onconf ::= ON CONFLICT resolvetype */ -{yymsp[-2].minor.yy60 = yymsp[0].minor.yy60;} + case 74: /* onconf ::= ON CONFLICT resolvetype */ +{yymsp[-2].minor.yy394 = yymsp[0].minor.yy394;} break; - case 75: /* resolvetype ::= IGNORE */ -{yymsp[0].minor.yy60 = OE_Ignore;} + case 77: /* resolvetype ::= IGNORE */ +{yymsp[0].minor.yy394 = OE_Ignore;} break; - case 76: /* resolvetype ::= REPLACE */ - case 170: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==170); -{yymsp[0].minor.yy60 = OE_Replace;} + case 78: /* resolvetype ::= REPLACE */ + case 172: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==172); +{yymsp[0].minor.yy394 = OE_Replace;} break; - case 77: /* cmd ::= DROP TABLE ifexists fullname */ + case 79: /* cmd ::= DROP TABLE ifexists fullname */ { - wx_sqlite3DropTable(pParse, yymsp[0].minor.yy291, 0, yymsp[-1].minor.yy60); + wx_sqlite3DropTable(pParse, yymsp[0].minor.yy131, 0, yymsp[-1].minor.yy394); } break; - case 80: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ { - wx_sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy338, yymsp[0].minor.yy307, yymsp[-7].minor.yy60, yymsp[-5].minor.yy60); + wx_sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[0].minor.yy47, yymsp[-7].minor.yy394, yymsp[-5].minor.yy394); } break; - case 81: /* cmd ::= DROP VIEW ifexists fullname */ + case 83: /* cmd ::= DROP VIEW ifexists fullname */ { - wx_sqlite3DropTable(pParse, yymsp[0].minor.yy291, 1, yymsp[-1].minor.yy60); + wx_sqlite3DropTable(pParse, yymsp[0].minor.yy131, 1, yymsp[-1].minor.yy394); } break; - case 82: /* cmd ::= select */ + case 84: /* cmd ::= select */ { SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0}; - wx_sqlite3Select(pParse, yymsp[0].minor.yy307, &dest); - wx_sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy307); + wx_sqlite3Select(pParse, yymsp[0].minor.yy47, &dest); + wx_sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47); } break; - case 83: /* select ::= WITH wqlist selectnowith */ -{yymsp[-2].minor.yy307 = attachWithToSelect(pParse,yymsp[0].minor.yy307,yymsp[-1].minor.yy195);} + case 85: /* select ::= WITH wqlist selectnowith */ +{yymsp[-2].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);} break; - case 84: /* select ::= WITH RECURSIVE wqlist selectnowith */ -{yymsp[-3].minor.yy307 = attachWithToSelect(pParse,yymsp[0].minor.yy307,yymsp[-1].minor.yy195);} + case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */ +{yymsp[-3].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);} break; - case 85: /* select ::= selectnowith */ + case 87: /* select ::= selectnowith */ { - Select *p = yymsp[0].minor.yy307; + Select *p = yymsp[0].minor.yy47; if( p ){ parserDoubleLinkSelect(pParse, p); } - yymsp[0].minor.yy307 = p; /*A-overwrites-X*/ + yymsp[0].minor.yy47 = p; /*A-overwrites-X*/ } break; - case 86: /* selectnowith ::= selectnowith multiselect_op oneselect */ + case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */ { - Select *pRhs = yymsp[0].minor.yy307; - Select *pLhs = yymsp[-2].minor.yy307; + Select *pRhs = yymsp[0].minor.yy47; + Select *pLhs = yymsp[-2].minor.yy47; if( pRhs && pRhs->pPrior ){ SrcList *pFrom; Token x; x.n = 0; parserDoubleLinkSelect(pParse, pRhs); - pFrom = wx_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0); + pFrom = wx_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0); pRhs = wx_sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); } if( pRhs ){ - pRhs->op = (u8)yymsp[-1].minor.yy60; + pRhs->op = (u8)yymsp[-1].minor.yy394; pRhs->pPrior = pLhs; if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; pRhs->selFlags &= ~SF_MultiValue; - if( yymsp[-1].minor.yy60!=TK_ALL ) pParse->hasCompound = 1; + if( yymsp[-1].minor.yy394!=TK_ALL ) pParse->hasCompound = 1; }else{ wx_sqlite3SelectDelete(pParse->db, pLhs); } - yymsp[-2].minor.yy307 = pRhs; + yymsp[-2].minor.yy47 = pRhs; } break; - case 87: /* multiselect_op ::= UNION */ - case 89: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==89); -{yymsp[0].minor.yy60 = yymsp[0].major; /*A-overwrites-OP*/} + case 89: /* multiselect_op ::= UNION */ + case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91); +{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-OP*/} break; - case 88: /* multiselect_op ::= UNION ALL */ -{yymsp[-1].minor.yy60 = TK_ALL;} + case 90: /* multiselect_op ::= UNION ALL */ +{yymsp[-1].minor.yy394 = TK_ALL;} break; - case 90: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yymsp[-8].minor.yy307 = wx_sqlite3SelectNew(pParse,yymsp[-6].minor.yy338,yymsp[-5].minor.yy291,yymsp[-4].minor.yy602,yymsp[-3].minor.yy338,yymsp[-2].minor.yy602,yymsp[-1].minor.yy338,yymsp[-7].minor.yy60,yymsp[0].minor.yy602); + yymsp[-8].minor.yy47 = wx_sqlite3SelectNew(pParse,yymsp[-6].minor.yy322,yymsp[-5].minor.yy131,yymsp[-4].minor.yy528,yymsp[-3].minor.yy322,yymsp[-2].minor.yy528,yymsp[-1].minor.yy322,yymsp[-7].minor.yy394,yymsp[0].minor.yy528); } break; - case 91: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ { - yymsp[-9].minor.yy307 = wx_sqlite3SelectNew(pParse,yymsp[-7].minor.yy338,yymsp[-6].minor.yy291,yymsp[-5].minor.yy602,yymsp[-4].minor.yy338,yymsp[-3].minor.yy602,yymsp[-1].minor.yy338,yymsp[-8].minor.yy60,yymsp[0].minor.yy602); - if( yymsp[-9].minor.yy307 ){ - yymsp[-9].minor.yy307->pWinDefn = yymsp[-2].minor.yy19; + yymsp[-9].minor.yy47 = wx_sqlite3SelectNew(pParse,yymsp[-7].minor.yy322,yymsp[-6].minor.yy131,yymsp[-5].minor.yy528,yymsp[-4].minor.yy322,yymsp[-3].minor.yy528,yymsp[-1].minor.yy322,yymsp[-8].minor.yy394,yymsp[0].minor.yy528); + if( yymsp[-9].minor.yy47 ){ + yymsp[-9].minor.yy47->pWinDefn = yymsp[-2].minor.yy41; }else{ - wx_sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy19); + wx_sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy41); } } break; - case 92: /* values ::= VALUES LP nexprlist RP */ + case 94: /* values ::= VALUES LP nexprlist RP */ { - yymsp[-3].minor.yy307 = wx_sqlite3SelectNew(pParse,yymsp[-1].minor.yy338,0,0,0,0,0,SF_Values,0); + yymsp[-3].minor.yy47 = wx_sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values,0); } break; - case 93: /* values ::= values COMMA LP nexprlist RP */ + case 95: /* values ::= values COMMA LP nexprlist RP */ { - Select *pRight, *pLeft = yymsp[-4].minor.yy307; - pRight = wx_sqlite3SelectNew(pParse,yymsp[-1].minor.yy338,0,0,0,0,0,SF_Values|SF_MultiValue,0); + Select *pRight, *pLeft = yymsp[-4].minor.yy47; + pRight = wx_sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values|SF_MultiValue,0); if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; if( pRight ){ pRight->op = TK_ALL; pRight->pPrior = pLeft; - yymsp[-4].minor.yy307 = pRight; + yymsp[-4].minor.yy47 = pRight; }else{ - yymsp[-4].minor.yy307 = pLeft; + yymsp[-4].minor.yy47 = pLeft; } } break; - case 94: /* distinct ::= DISTINCT */ -{yymsp[0].minor.yy60 = SF_Distinct;} + case 96: /* distinct ::= DISTINCT */ +{yymsp[0].minor.yy394 = SF_Distinct;} break; - case 95: /* distinct ::= ALL */ -{yymsp[0].minor.yy60 = SF_All;} + case 97: /* distinct ::= ALL */ +{yymsp[0].minor.yy394 = SF_All;} break; - case 97: /* sclp ::= */ - case 130: /* orderby_opt ::= */ yytestcase(yyruleno==130); - case 140: /* groupby_opt ::= */ yytestcase(yyruleno==140); - case 227: /* exprlist ::= */ yytestcase(yyruleno==227); - case 230: /* paren_exprlist ::= */ yytestcase(yyruleno==230); - case 235: /* eidlist_opt ::= */ yytestcase(yyruleno==235); -{yymsp[1].minor.yy338 = 0;} + case 99: /* sclp ::= */ + case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132); + case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142); + case 232: /* exprlist ::= */ yytestcase(yyruleno==232); + case 235: /* paren_exprlist ::= */ yytestcase(yyruleno==235); + case 240: /* eidlist_opt ::= */ yytestcase(yyruleno==240); +{yymsp[1].minor.yy322 = 0;} break; - case 98: /* selcollist ::= sclp scanpt expr scanpt as */ + case 100: /* selcollist ::= sclp scanpt expr scanpt as */ { - yymsp[-4].minor.yy338 = wx_sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy338, yymsp[-2].minor.yy602); - if( yymsp[0].minor.yy0.n>0 ) wx_sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy338, &yymsp[0].minor.yy0, 1); - wx_sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy338,yymsp[-3].minor.yy528,yymsp[-1].minor.yy528); + yymsp[-4].minor.yy322 = wx_sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); + if( yymsp[0].minor.yy0.n>0 ) wx_sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[0].minor.yy0, 1); + wx_sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy322,yymsp[-3].minor.yy522,yymsp[-1].minor.yy522); } break; - case 99: /* selcollist ::= sclp scanpt STAR */ + case 101: /* selcollist ::= sclp scanpt STAR */ { Expr *p = wx_sqlite3Expr(pParse->db, TK_ASTERISK, 0); - yymsp[-2].minor.yy338 = wx_sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy338, p); + yymsp[-2].minor.yy322 = wx_sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy322, p); } break; - case 100: /* selcollist ::= sclp scanpt nm DOT STAR */ + case 102: /* selcollist ::= sclp scanpt nm DOT STAR */ { Expr *pRight = wx_sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); - Expr *pLeft = wx_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); + Expr *pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0); Expr *pDot = wx_sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); - yymsp[-4].minor.yy338 = wx_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy338, pDot); + yymsp[-4].minor.yy322 = wx_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot); } break; - case 101: /* as ::= AS nm */ - case 112: /* dbnm ::= DOT nm */ yytestcase(yyruleno==112); - case 251: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==251); - case 252: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==252); + case 103: /* as ::= AS nm */ + case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115); + case 256: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==256); + case 257: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==257); {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} break; - case 103: /* from ::= */ - case 106: /* stl_prefix ::= */ yytestcase(yyruleno==106); -{yymsp[1].minor.yy291 = 0;} + case 105: /* from ::= */ + case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108); +{yymsp[1].minor.yy131 = 0;} break; - case 104: /* from ::= FROM seltablist */ + case 106: /* from ::= FROM seltablist */ { - yymsp[-1].minor.yy291 = yymsp[0].minor.yy291; - wx_sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy291); + yymsp[-1].minor.yy131 = yymsp[0].minor.yy131; + wx_sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy131); } break; - case 105: /* stl_prefix ::= seltablist joinop */ + case 107: /* stl_prefix ::= seltablist joinop */ { - if( ALWAYS(yymsp[-1].minor.yy291 && yymsp[-1].minor.yy291->nSrc>0) ) yymsp[-1].minor.yy291->a[yymsp[-1].minor.yy291->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy60; + if( ALWAYS(yymsp[-1].minor.yy131 && yymsp[-1].minor.yy131->nSrc>0) ) yymsp[-1].minor.yy131->a[yymsp[-1].minor.yy131->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy394; } break; - case 107: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + case 109: /* seltablist ::= stl_prefix nm dbnm as on_using */ { - yymsp[-6].minor.yy291 = wx_sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy291,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy602,yymsp[0].minor.yy288); - wx_sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy291, &yymsp[-2].minor.yy0); + yymsp[-4].minor.yy131 = wx_sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy131,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); } break; - case 108: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + case 110: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ { - yymsp[-8].minor.yy291 = wx_sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy291,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy602,yymsp[0].minor.yy288); - wx_sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy291, yymsp[-4].minor.yy338); + yymsp[-5].minor.yy131 = wx_sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy561); + wx_sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-1].minor.yy0); } break; - case 109: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + case 111: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ { - yymsp[-6].minor.yy291 = wx_sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy291,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy307,yymsp[-1].minor.yy602,yymsp[0].minor.yy288); + yymsp[-7].minor.yy131 = wx_sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy131,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); + wx_sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy131, yymsp[-3].minor.yy322); +} + break; + case 112: /* seltablist ::= stl_prefix LP select RP as on_using */ +{ + yymsp[-5].minor.yy131 = wx_sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy47,&yymsp[0].minor.yy561); } break; - case 110: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ { - if( yymsp[-6].minor.yy291==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy602==0 && yymsp[0].minor.yy288==0 ){ - yymsp[-6].minor.yy291 = yymsp[-4].minor.yy291; - }else if( yymsp[-4].minor.yy291->nSrc==1 ){ - yymsp[-6].minor.yy291 = wx_sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy291,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy602,yymsp[0].minor.yy288); - if( yymsp[-6].minor.yy291 ){ - SrcItem *pNew = &yymsp[-6].minor.yy291->a[yymsp[-6].minor.yy291->nSrc-1]; - SrcItem *pOld = yymsp[-4].minor.yy291->a; + if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){ + yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131; + }else if( yymsp[-3].minor.yy131->nSrc==1 ){ + yymsp[-5].minor.yy131 = wx_sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); + if( yymsp[-5].minor.yy131 ){ + SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1]; + SrcItem *pOld = yymsp[-3].minor.yy131->a; pNew->zName = pOld->zName; pNew->zDatabase = pOld->zDatabase; pNew->pSelect = pOld->pSelect; + if( pNew->pSelect && (pNew->pSelect->selFlags & SF_NestedFrom)!=0 ){ + pNew->fg.isNestedFrom = 1; + } if( pOld->fg.isTabFunc ){ pNew->u1.pFuncArg = pOld->u1.pFuncArg; pOld->u1.pFuncArg = 0; @@ -161055,428 +170989,458 @@ static YYACTIONTYPE yy_reduce( pOld->zName = pOld->zDatabase = 0; pOld->pSelect = 0; } - wx_sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy291); + wx_sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy131); }else{ Select *pSubquery; - wx_sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy291); - pSubquery = wx_sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy291,0,0,0,0,SF_NestedFrom,0); - yymsp[-6].minor.yy291 = wx_sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy291,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy602,yymsp[0].minor.yy288); + wx_sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy131); + pSubquery = wx_sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy131,0,0,0,0,SF_NestedFrom,0); + yymsp[-5].minor.yy131 = wx_sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy561); } } break; - case 111: /* dbnm ::= */ - case 125: /* indexed_opt ::= */ yytestcase(yyruleno==125); + case 114: /* dbnm ::= */ + case 129: /* indexed_opt ::= */ yytestcase(yyruleno==129); {yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;} break; - case 113: /* fullname ::= nm */ + case 116: /* fullname ::= nm */ { - yylhsminor.yy291 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); - if( IN_RENAME_OBJECT && yylhsminor.yy291 ) wx_sqlite3RenameTokenMap(pParse, yylhsminor.yy291->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy131 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); + if( IN_RENAME_OBJECT && yylhsminor.yy131 ) wx_sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[0].minor.yy291 = yylhsminor.yy291; + yymsp[0].minor.yy131 = yylhsminor.yy131; break; - case 114: /* fullname ::= nm DOT nm */ + case 117: /* fullname ::= nm DOT nm */ { - yylhsminor.yy291 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); - if( IN_RENAME_OBJECT && yylhsminor.yy291 ) wx_sqlite3RenameTokenMap(pParse, yylhsminor.yy291->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy131 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + if( IN_RENAME_OBJECT && yylhsminor.yy131 ) wx_sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[-2].minor.yy291 = yylhsminor.yy291; + yymsp[-2].minor.yy131 = yylhsminor.yy131; break; - case 115: /* xfullname ::= nm */ -{yymsp[0].minor.yy291 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} + case 118: /* xfullname ::= nm */ +{yymsp[0].minor.yy131 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} break; - case 116: /* xfullname ::= nm DOT nm */ -{yymsp[-2].minor.yy291 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 119: /* xfullname ::= nm DOT nm */ +{yymsp[-2].minor.yy131 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 117: /* xfullname ::= nm DOT nm AS nm */ + case 120: /* xfullname ::= nm DOT nm AS nm */ { - yymsp[-4].minor.yy291 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ - if( yymsp[-4].minor.yy291 ) yymsp[-4].minor.yy291->a[0].zAlias = wx_sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-4].minor.yy131 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ + if( yymsp[-4].minor.yy131 ) yymsp[-4].minor.yy131->a[0].zAlias = wx_sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; - case 118: /* xfullname ::= nm AS nm */ + case 121: /* xfullname ::= nm AS nm */ { - yymsp[-2].minor.yy291 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ - if( yymsp[-2].minor.yy291 ) yymsp[-2].minor.yy291->a[0].zAlias = wx_sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-2].minor.yy131 = wx_sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ + if( yymsp[-2].minor.yy131 ) yymsp[-2].minor.yy131->a[0].zAlias = wx_sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; - case 119: /* joinop ::= COMMA|JOIN */ -{ yymsp[0].minor.yy60 = JT_INNER; } + case 122: /* joinop ::= COMMA|JOIN */ +{ yymsp[0].minor.yy394 = JT_INNER; } break; - case 120: /* joinop ::= JOIN_KW JOIN */ -{yymsp[-1].minor.yy60 = wx_sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} + case 123: /* joinop ::= JOIN_KW JOIN */ +{yymsp[-1].minor.yy394 = wx_sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} break; - case 121: /* joinop ::= JOIN_KW nm JOIN */ -{yymsp[-2].minor.yy60 = wx_sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} + case 124: /* joinop ::= JOIN_KW nm JOIN */ +{yymsp[-2].minor.yy394 = wx_sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} break; - case 122: /* joinop ::= JOIN_KW nm nm JOIN */ -{yymsp[-3].minor.yy60 = wx_sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} + case 125: /* joinop ::= JOIN_KW nm nm JOIN */ +{yymsp[-3].minor.yy394 = wx_sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} break; - case 123: /* on_opt ::= ON expr */ - case 143: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==143); - case 150: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==150); - case 152: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==152); - case 223: /* case_else ::= ELSE expr */ yytestcase(yyruleno==223); - case 244: /* vinto ::= INTO expr */ yytestcase(yyruleno==244); -{yymsp[-1].minor.yy602 = yymsp[0].minor.yy602;} + case 126: /* on_using ::= ON expr */ +{yymsp[-1].minor.yy561.pOn = yymsp[0].minor.yy528; yymsp[-1].minor.yy561.pUsing = 0;} break; - case 124: /* on_opt ::= */ - case 142: /* having_opt ::= */ yytestcase(yyruleno==142); - case 144: /* limit_opt ::= */ yytestcase(yyruleno==144); - case 149: /* where_opt ::= */ yytestcase(yyruleno==149); - case 151: /* where_opt_ret ::= */ yytestcase(yyruleno==151); - case 224: /* case_else ::= */ yytestcase(yyruleno==224); - case 226: /* case_operand ::= */ yytestcase(yyruleno==226); - case 245: /* vinto ::= */ yytestcase(yyruleno==245); -{yymsp[1].minor.yy602 = 0;} + case 127: /* on_using ::= USING LP idlist RP */ +{yymsp[-3].minor.yy561.pOn = 0; yymsp[-3].minor.yy561.pUsing = yymsp[-1].minor.yy254;} break; - case 126: /* indexed_opt ::= INDEXED BY nm */ + case 128: /* on_using ::= */ +{yymsp[1].minor.yy561.pOn = 0; yymsp[1].minor.yy561.pUsing = 0;} + break; + case 130: /* indexed_by ::= INDEXED BY nm */ {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} break; - case 127: /* indexed_opt ::= NOT INDEXED */ + case 131: /* indexed_by ::= NOT INDEXED */ {yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;} break; - case 128: /* using_opt ::= USING LP idlist RP */ -{yymsp[-3].minor.yy288 = yymsp[-1].minor.yy288;} - break; - case 129: /* using_opt ::= */ - case 171: /* idlist_opt ::= */ yytestcase(yyruleno==171); -{yymsp[1].minor.yy288 = 0;} - break; - case 131: /* orderby_opt ::= ORDER BY sortlist */ - case 141: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==141); -{yymsp[-2].minor.yy338 = yymsp[0].minor.yy338;} - break; - case 132: /* sortlist ::= sortlist COMMA expr sortorder nulls */ -{ - yymsp[-4].minor.yy338 = wx_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy338,yymsp[-2].minor.yy602); - wx_sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy338,yymsp[-1].minor.yy60,yymsp[0].minor.yy60); -} - break; - case 133: /* sortlist ::= expr sortorder nulls */ -{ - yymsp[-2].minor.yy338 = wx_sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy602); /*A-overwrites-Y*/ - wx_sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy338,yymsp[-1].minor.yy60,yymsp[0].minor.yy60); -} - break; - case 134: /* sortorder ::= ASC */ -{yymsp[0].minor.yy60 = SQLITE_SO_ASC;} - break; - case 135: /* sortorder ::= DESC */ -{yymsp[0].minor.yy60 = SQLITE_SO_DESC;} - break; - case 136: /* sortorder ::= */ - case 139: /* nulls ::= */ yytestcase(yyruleno==139); -{yymsp[1].minor.yy60 = SQLITE_SO_UNDEFINED;} - break; - case 137: /* nulls ::= NULLS FIRST */ -{yymsp[-1].minor.yy60 = SQLITE_SO_ASC;} - break; - case 138: /* nulls ::= NULLS LAST */ -{yymsp[-1].minor.yy60 = SQLITE_SO_DESC;} - break; - case 145: /* limit_opt ::= LIMIT expr */ -{yymsp[-1].minor.yy602 = wx_sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy602,0);} - break; - case 146: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yymsp[-3].minor.yy602 = wx_sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy602,yymsp[0].minor.yy602);} - break; - case 147: /* limit_opt ::= LIMIT expr COMMA expr */ -{yymsp[-3].minor.yy602 = wx_sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy602,yymsp[-2].minor.yy602);} - break; - case 148: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ -{ - wx_sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy291, &yymsp[-1].minor.yy0); - wx_sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy291,yymsp[0].minor.yy602,0,0); -} - break; - case 153: /* where_opt_ret ::= RETURNING selcollist */ -{wx_sqlite3AddReturning(pParse,yymsp[0].minor.yy338); yymsp[-1].minor.yy602 = 0;} - break; - case 154: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ -{wx_sqlite3AddReturning(pParse,yymsp[0].minor.yy338); yymsp[-3].minor.yy602 = yymsp[-2].minor.yy602;} - break; - case 155: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ -{ - wx_sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy291, &yymsp[-4].minor.yy0); - wx_sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy338,"set list"); - yymsp[-5].minor.yy291 = wx_sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy291, yymsp[-1].minor.yy291); - wx_sqlite3Update(pParse,yymsp[-5].minor.yy291,yymsp[-2].minor.yy338,yymsp[0].minor.yy602,yymsp[-6].minor.yy60,0,0,0); -} - break; - case 156: /* setlist ::= setlist COMMA nm EQ expr */ -{ - yymsp[-4].minor.yy338 = wx_sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy338, yymsp[0].minor.yy602); - wx_sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy338, &yymsp[-2].minor.yy0, 1); -} - break; - case 157: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ -{ - yymsp[-6].minor.yy338 = wx_sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy338, yymsp[-3].minor.yy288, yymsp[0].minor.yy602); -} - break; - case 158: /* setlist ::= nm EQ expr */ -{ - yylhsminor.yy338 = wx_sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy602); - wx_sqlite3ExprListSetName(pParse, yylhsminor.yy338, &yymsp[-2].minor.yy0, 1); -} - yymsp[-2].minor.yy338 = yylhsminor.yy338; - break; - case 159: /* setlist ::= LP idlist RP EQ expr */ -{ - yymsp[-4].minor.yy338 = wx_sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy288, yymsp[0].minor.yy602); -} + case 133: /* orderby_opt ::= ORDER BY sortlist */ + case 143: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==143); +{yymsp[-2].minor.yy322 = yymsp[0].minor.yy322;} break; - case 160: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + case 134: /* sortlist ::= sortlist COMMA expr sortorder nulls */ { - wx_sqlite3Insert(pParse, yymsp[-3].minor.yy291, yymsp[-1].minor.yy307, yymsp[-2].minor.yy288, yymsp[-5].minor.yy60, yymsp[0].minor.yy178); + yymsp[-4].minor.yy322 = wx_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322,yymsp[-2].minor.yy528); + wx_sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394); } break; - case 161: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + case 135: /* sortlist ::= expr sortorder nulls */ { - wx_sqlite3Insert(pParse, yymsp[-4].minor.yy291, 0, yymsp[-3].minor.yy288, yymsp[-6].minor.yy60, 0); + yymsp[-2].minor.yy322 = wx_sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy528); /*A-overwrites-Y*/ + wx_sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394); } break; - case 162: /* upsert ::= */ -{ yymsp[1].minor.yy178 = 0; } + case 136: /* sortorder ::= ASC */ +{yymsp[0].minor.yy394 = SQLITE_SO_ASC;} break; - case 163: /* upsert ::= RETURNING selcollist */ -{ yymsp[-1].minor.yy178 = 0; wx_sqlite3AddReturning(pParse,yymsp[0].minor.yy338); } + case 137: /* sortorder ::= DESC */ +{yymsp[0].minor.yy394 = SQLITE_SO_DESC;} break; - case 164: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ -{ yymsp[-11].minor.yy178 = wx_sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy338,yymsp[-6].minor.yy602,yymsp[-2].minor.yy338,yymsp[-1].minor.yy602,yymsp[0].minor.yy178);} + case 138: /* sortorder ::= */ + case 141: /* nulls ::= */ yytestcase(yyruleno==141); +{yymsp[1].minor.yy394 = SQLITE_SO_UNDEFINED;} break; - case 165: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ -{ yymsp[-8].minor.yy178 = wx_sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy338,yymsp[-3].minor.yy602,0,0,yymsp[0].minor.yy178); } + case 139: /* nulls ::= NULLS FIRST */ +{yymsp[-1].minor.yy394 = SQLITE_SO_ASC;} break; - case 166: /* upsert ::= ON CONFLICT DO NOTHING returning */ -{ yymsp[-4].minor.yy178 = wx_sqlite3UpsertNew(pParse->db,0,0,0,0,0); } + case 140: /* nulls ::= NULLS LAST */ +{yymsp[-1].minor.yy394 = SQLITE_SO_DESC;} break; - case 167: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ -{ yymsp[-7].minor.yy178 = wx_sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy338,yymsp[-1].minor.yy602,0);} + case 144: /* having_opt ::= */ + case 146: /* limit_opt ::= */ yytestcase(yyruleno==146); + case 151: /* where_opt ::= */ yytestcase(yyruleno==151); + case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153); + case 229: /* case_else ::= */ yytestcase(yyruleno==229); + case 231: /* case_operand ::= */ yytestcase(yyruleno==231); + case 250: /* vinto ::= */ yytestcase(yyruleno==250); +{yymsp[1].minor.yy528 = 0;} break; - case 168: /* returning ::= RETURNING selcollist */ -{wx_sqlite3AddReturning(pParse,yymsp[0].minor.yy338);} + case 145: /* having_opt ::= HAVING expr */ + case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152); + case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154); + case 228: /* case_else ::= ELSE expr */ yytestcase(yyruleno==228); + case 249: /* vinto ::= INTO expr */ yytestcase(yyruleno==249); +{yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;} break; - case 172: /* idlist_opt ::= LP idlist RP */ -{yymsp[-2].minor.yy288 = yymsp[-1].minor.yy288;} + case 147: /* limit_opt ::= LIMIT expr */ +{yymsp[-1].minor.yy528 = wx_sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);} break; - case 173: /* idlist ::= idlist COMMA nm */ -{yymsp[-2].minor.yy288 = wx_sqlite3IdListAppend(pParse,yymsp[-2].minor.yy288,&yymsp[0].minor.yy0);} + case 148: /* limit_opt ::= LIMIT expr OFFSET expr */ +{yymsp[-3].minor.yy528 = wx_sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} break; - case 174: /* idlist ::= nm */ -{yymsp[0].minor.yy288 = wx_sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} + case 149: /* limit_opt ::= LIMIT expr COMMA expr */ +{yymsp[-3].minor.yy528 = wx_sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,yymsp[-2].minor.yy528);} break; - case 175: /* expr ::= LP expr RP */ -{yymsp[-2].minor.yy602 = yymsp[-1].minor.yy602;} - break; - case 176: /* expr ::= ID|INDEXED */ - case 177: /* expr ::= JOIN_KW */ yytestcase(yyruleno==177); -{yymsp[0].minor.yy602=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} - break; - case 178: /* expr ::= nm DOT nm */ + case 150: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ { - Expr *temp1 = wx_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); - Expr *temp2 = wx_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1); - if( IN_RENAME_OBJECT ){ - wx_sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0); - wx_sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0); - } - yylhsminor.yy602 = wx_sqlite3PExpr(pParse, TK_DOT, temp1, temp2); + wx_sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy131, &yymsp[-1].minor.yy0); + wx_sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy131,yymsp[0].minor.yy528,0,0); } - yymsp[-2].minor.yy602 = yylhsminor.yy602; break; - case 179: /* expr ::= nm DOT nm DOT nm */ -{ - Expr *temp1 = wx_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1); - Expr *temp2 = wx_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); - Expr *temp3 = wx_sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1); - Expr *temp4 = wx_sqlite3PExpr(pParse, TK_DOT, temp2, temp3); - if( IN_RENAME_OBJECT ){ - wx_sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0); - wx_sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0); - } - yylhsminor.yy602 = wx_sqlite3PExpr(pParse, TK_DOT, temp1, temp4); -} - yymsp[-4].minor.yy602 = yylhsminor.yy602; - break; - case 180: /* term ::= NULL|FLOAT|BLOB */ - case 181: /* term ::= STRING */ yytestcase(yyruleno==181); -{yymsp[0].minor.yy602=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 155: /* where_opt_ret ::= RETURNING selcollist */ +{wx_sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-1].minor.yy528 = 0;} break; - case 182: /* term ::= INTEGER */ -{ - yylhsminor.yy602 = wx_sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); -} - yymsp[0].minor.yy602 = yylhsminor.yy602; + case 156: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ +{wx_sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-3].minor.yy528 = yymsp[-2].minor.yy528;} break; - case 183: /* expr ::= VARIABLE */ + case 157: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ { - if( !(yymsp[0].minor.yy0.z[0]=='#' && wx_sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ - u32 n = yymsp[0].minor.yy0.n; - yymsp[0].minor.yy602 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); - wx_sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy602, n); - }else{ - /* When doing a nested parse, one can include terms in an expression - ** that look like this: #1 #2 ... These terms refer to registers - ** in the virtual machine. #N is the N-th register. */ - Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/ - assert( t.n>=2 ); - if( pParse->nested==0 ){ - wx_sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); - yymsp[0].minor.yy602 = 0; - }else{ - yymsp[0].minor.yy602 = wx_sqlite3PExpr(pParse, TK_REGISTER, 0, 0); - if( yymsp[0].minor.yy602 ) wx_sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy602->iTable); + wx_sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-4].minor.yy0); + wx_sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy322,"set list"); + if( yymsp[-1].minor.yy131 ){ + SrcList *pFromClause = yymsp[-1].minor.yy131; + if( pFromClause->nSrc>1 ){ + Select *pSubquery; + Token as; + pSubquery = wx_sqlite3SelectNew(pParse,0,pFromClause,0,0,0,0,SF_NestedFrom,0); + as.n = 0; + as.z = 0; + pFromClause = wx_sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); } + yymsp[-5].minor.yy131 = wx_sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy131, pFromClause); } + wx_sqlite3Update(pParse,yymsp[-5].minor.yy131,yymsp[-2].minor.yy322,yymsp[0].minor.yy528,yymsp[-6].minor.yy394,0,0,0); } break; - case 184: /* expr ::= expr COLLATE ID|STRING */ + case 158: /* setlist ::= setlist COMMA nm EQ expr */ { - yymsp[-2].minor.yy602 = wx_sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy602, &yymsp[0].minor.yy0, 1); + yymsp[-4].minor.yy322 = wx_sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[0].minor.yy528); + wx_sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, 1); } break; - case 185: /* expr ::= CAST LP expr AS typetoken RP */ + case 159: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ { - yymsp[-5].minor.yy602 = wx_sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); - wx_sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy602, yymsp[-3].minor.yy602, 0); + yymsp[-6].minor.yy322 = wx_sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy322, yymsp[-3].minor.yy254, yymsp[0].minor.yy528); } break; - case 186: /* expr ::= ID|INDEXED LP distinct exprlist RP */ + case 160: /* setlist ::= nm EQ expr */ { - yylhsminor.yy602 = wx_sqlite3ExprFunction(pParse, yymsp[-1].minor.yy338, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy60); + yylhsminor.yy322 = wx_sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy528); + wx_sqlite3ExprListSetName(pParse, yylhsminor.yy322, &yymsp[-2].minor.yy0, 1); } - yymsp[-4].minor.yy602 = yylhsminor.yy602; + yymsp[-2].minor.yy322 = yylhsminor.yy322; break; - case 187: /* expr ::= ID|INDEXED LP STAR RP */ + case 161: /* setlist ::= LP idlist RP EQ expr */ { - yylhsminor.yy602 = wx_sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); + yymsp[-4].minor.yy322 = wx_sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy254, yymsp[0].minor.yy528); } - yymsp[-3].minor.yy602 = yylhsminor.yy602; break; - case 188: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ + case 162: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ { - yylhsminor.yy602 = wx_sqlite3ExprFunction(pParse, yymsp[-2].minor.yy338, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy60); - wx_sqlite3WindowAttach(pParse, yylhsminor.yy602, yymsp[0].minor.yy19); + wx_sqlite3Insert(pParse, yymsp[-3].minor.yy131, yymsp[-1].minor.yy47, yymsp[-2].minor.yy254, yymsp[-5].minor.yy394, yymsp[0].minor.yy444); } - yymsp[-5].minor.yy602 = yylhsminor.yy602; break; - case 189: /* expr ::= ID|INDEXED LP STAR RP filter_over */ + case 163: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ { - yylhsminor.yy602 = wx_sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); - wx_sqlite3WindowAttach(pParse, yylhsminor.yy602, yymsp[0].minor.yy19); + wx_sqlite3Insert(pParse, yymsp[-4].minor.yy131, 0, yymsp[-3].minor.yy254, yymsp[-6].minor.yy394, 0); } - yymsp[-4].minor.yy602 = yylhsminor.yy602; break; - case 190: /* term ::= CTIME_KW */ + case 164: /* upsert ::= */ +{ yymsp[1].minor.yy444 = 0; } + break; + case 165: /* upsert ::= RETURNING selcollist */ +{ yymsp[-1].minor.yy444 = 0; wx_sqlite3AddReturning(pParse,yymsp[0].minor.yy322); } + break; + case 166: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ +{ yymsp[-11].minor.yy444 = wx_sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy322,yymsp[-6].minor.yy528,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,yymsp[0].minor.yy444);} + break; + case 167: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ +{ yymsp[-8].minor.yy444 = wx_sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy322,yymsp[-3].minor.yy528,0,0,yymsp[0].minor.yy444); } + break; + case 168: /* upsert ::= ON CONFLICT DO NOTHING returning */ +{ yymsp[-4].minor.yy444 = wx_sqlite3UpsertNew(pParse->db,0,0,0,0,0); } + break; + case 169: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ +{ yymsp[-7].minor.yy444 = wx_sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,0);} + break; + case 170: /* returning ::= RETURNING selcollist */ +{wx_sqlite3AddReturning(pParse,yymsp[0].minor.yy322);} + break; + case 173: /* idlist_opt ::= */ +{yymsp[1].minor.yy254 = 0;} + break; + case 174: /* idlist_opt ::= LP idlist RP */ +{yymsp[-2].minor.yy254 = yymsp[-1].minor.yy254;} + break; + case 175: /* idlist ::= idlist COMMA nm */ +{yymsp[-2].minor.yy254 = wx_sqlite3IdListAppend(pParse,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);} + break; + case 176: /* idlist ::= nm */ +{yymsp[0].minor.yy254 = wx_sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} + break; + case 177: /* expr ::= LP expr RP */ +{yymsp[-2].minor.yy528 = yymsp[-1].minor.yy528;} + break; + case 178: /* expr ::= ID|INDEXED */ + case 179: /* expr ::= JOIN_KW */ yytestcase(yyruleno==179); +{yymsp[0].minor.yy528=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} + break; + case 180: /* expr ::= nm DOT nm */ { - yylhsminor.yy602 = wx_sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); + Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); + Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); + yylhsminor.yy528 = wx_sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } - yymsp[0].minor.yy602 = yylhsminor.yy602; + yymsp[-2].minor.yy528 = yylhsminor.yy528; break; - case 191: /* expr ::= LP nexprlist COMMA expr RP */ + case 181: /* expr ::= nm DOT nm DOT nm */ { - ExprList *pList = wx_sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy338, yymsp[-1].minor.yy602); - yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_VECTOR, 0, 0); - if( yymsp[-4].minor.yy602 ){ - yymsp[-4].minor.yy602->x.pList = pList; + Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-4].minor.yy0); + Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); + Expr *temp3 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); + Expr *temp4 = wx_sqlite3PExpr(pParse, TK_DOT, temp2, temp3); + if( IN_RENAME_OBJECT ){ + wx_sqlite3RenameTokenRemap(pParse, 0, temp1); + } + yylhsminor.yy528 = wx_sqlite3PExpr(pParse, TK_DOT, temp1, temp4); +} + yymsp[-4].minor.yy528 = yylhsminor.yy528; + break; + case 182: /* term ::= NULL|FLOAT|BLOB */ + case 183: /* term ::= STRING */ yytestcase(yyruleno==183); +{yymsp[0].minor.yy528=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} + break; + case 184: /* term ::= INTEGER */ +{ + yylhsminor.yy528 = wx_sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); + if( yylhsminor.yy528 ) yylhsminor.yy528->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); +} + yymsp[0].minor.yy528 = yylhsminor.yy528; + break; + case 185: /* expr ::= VARIABLE */ +{ + if( !(yymsp[0].minor.yy0.z[0]=='#' && wx_sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ + u32 n = yymsp[0].minor.yy0.n; + yymsp[0].minor.yy528 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); + wx_sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy528, n); + }else{ + /* When doing a nested parse, one can include terms in an expression + ** that look like this: #1 #2 ... These terms refer to registers + ** in the virtual machine. #N is the N-th register. */ + Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/ + assert( t.n>=2 ); + if( pParse->nested==0 ){ + wx_sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); + yymsp[0].minor.yy528 = 0; + }else{ + yymsp[0].minor.yy528 = wx_sqlite3PExpr(pParse, TK_REGISTER, 0, 0); + if( yymsp[0].minor.yy528 ) wx_sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy528->iTable); + } + } +} + break; + case 186: /* expr ::= expr COLLATE ID|STRING */ +{ + yymsp[-2].minor.yy528 = wx_sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy528, &yymsp[0].minor.yy0, 1); +} + break; + case 187: /* expr ::= CAST LP expr AS typetoken RP */ +{ + yymsp[-5].minor.yy528 = wx_sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); + wx_sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy528, yymsp[-3].minor.yy528, 0); +} + break; + case 188: /* expr ::= ID|INDEXED LP distinct exprlist RP */ +{ + yylhsminor.yy528 = wx_sqlite3ExprFunction(pParse, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy394); +} + yymsp[-4].minor.yy528 = yylhsminor.yy528; + break; + case 189: /* expr ::= ID|INDEXED LP STAR RP */ +{ + yylhsminor.yy528 = wx_sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); +} + yymsp[-3].minor.yy528 = yylhsminor.yy528; + break; + case 190: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ +{ + yylhsminor.yy528 = wx_sqlite3ExprFunction(pParse, yymsp[-2].minor.yy322, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy394); + wx_sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); +} + yymsp[-5].minor.yy528 = yylhsminor.yy528; + break; + case 191: /* expr ::= ID|INDEXED LP STAR RP filter_over */ +{ + yylhsminor.yy528 = wx_sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); + wx_sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); +} + yymsp[-4].minor.yy528 = yylhsminor.yy528; + break; + case 192: /* term ::= CTIME_KW */ +{ + yylhsminor.yy528 = wx_sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); +} + yymsp[0].minor.yy528 = yylhsminor.yy528; + break; + case 193: /* expr ::= LP nexprlist COMMA expr RP */ +{ + ExprList *pList = wx_sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528); + yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_VECTOR, 0, 0); + if( yymsp[-4].minor.yy528 ){ + yymsp[-4].minor.yy528->x.pList = pList; if( ALWAYS(pList->nExpr) ){ - yymsp[-4].minor.yy602->flags |= pList->a[0].pExpr->flags & EP_Propagate; + yymsp[-4].minor.yy528->flags |= pList->a[0].pExpr->flags & EP_Propagate; } }else{ wx_sqlite3ExprListDelete(pParse->db, pList); } } break; - case 192: /* expr ::= expr AND expr */ -{yymsp[-2].minor.yy602=wx_sqlite3ExprAnd(pParse,yymsp[-2].minor.yy602,yymsp[0].minor.yy602);} + case 194: /* expr ::= expr AND expr */ +{yymsp[-2].minor.yy528=wx_sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} break; - case 193: /* expr ::= expr OR expr */ - case 194: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==194); - case 195: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==195); - case 196: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==196); - case 197: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==197); - case 198: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==198); - case 199: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==199); -{yymsp[-2].minor.yy602=wx_sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy602,yymsp[0].minor.yy602);} + case 195: /* expr ::= expr OR expr */ + case 196: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==196); + case 197: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==197); + case 198: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==198); + case 199: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==199); + case 200: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==200); + case 201: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==201); +{yymsp[-2].minor.yy528=wx_sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} break; - case 200: /* likeop ::= NOT LIKE_KW|MATCH */ + case 202: /* likeop ::= NOT LIKE_KW|MATCH */ {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} break; - case 201: /* expr ::= expr likeop expr */ + case 203: /* expr ::= expr likeop expr */ { ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; yymsp[-1].minor.yy0.n &= 0x7fffffff; - pList = wx_sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy602); - pList = wx_sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy602); - yymsp[-2].minor.yy602 = wx_sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); - if( bNot ) yymsp[-2].minor.yy602 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy602, 0); - if( yymsp[-2].minor.yy602 ) yymsp[-2].minor.yy602->flags |= EP_InfixFunc; + pList = wx_sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy528); + pList = wx_sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy528); + yymsp[-2].minor.yy528 = wx_sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + if( bNot ) yymsp[-2].minor.yy528 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy528, 0); + if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc; } break; - case 202: /* expr ::= expr likeop expr ESCAPE expr */ + case 204: /* expr ::= expr likeop expr ESCAPE expr */ { ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; yymsp[-3].minor.yy0.n &= 0x7fffffff; - pList = wx_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy602); - pList = wx_sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy602); - pList = wx_sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy602); - yymsp[-4].minor.yy602 = wx_sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); - if( bNot ) yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy602, 0); - if( yymsp[-4].minor.yy602 ) yymsp[-4].minor.yy602->flags |= EP_InfixFunc; + pList = wx_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); + pList = wx_sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy528); + pList = wx_sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); + yymsp[-4].minor.yy528 = wx_sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); + if( bNot ) yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); + if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->flags |= EP_InfixFunc; } break; - case 203: /* expr ::= expr ISNULL|NOTNULL */ -{yymsp[-1].minor.yy602 = wx_sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy602,0);} + case 205: /* expr ::= expr ISNULL|NOTNULL */ +{yymsp[-1].minor.yy528 = wx_sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);} break; - case 204: /* expr ::= expr NOT NULL */ -{yymsp[-2].minor.yy602 = wx_sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy602,0);} + case 206: /* expr ::= expr NOT NULL */ +{yymsp[-2].minor.yy528 = wx_sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);} break; - case 205: /* expr ::= expr IS expr */ + case 207: /* expr ::= expr IS expr */ { - yymsp[-2].minor.yy602 = wx_sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy602,yymsp[0].minor.yy602); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy602, yymsp[-2].minor.yy602, TK_ISNULL); + yymsp[-2].minor.yy528 = wx_sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy528,yymsp[0].minor.yy528); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-2].minor.yy528, TK_ISNULL); } break; - case 206: /* expr ::= expr IS NOT expr */ + case 208: /* expr ::= expr IS NOT expr */ { - yymsp[-3].minor.yy602 = wx_sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy602,yymsp[0].minor.yy602); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy602, yymsp[-3].minor.yy602, TK_NOTNULL); + yymsp[-3].minor.yy528 = wx_sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy528,yymsp[0].minor.yy528); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-3].minor.yy528, TK_NOTNULL); } break; - case 207: /* expr ::= NOT expr */ - case 208: /* expr ::= BITNOT expr */ yytestcase(yyruleno==208); -{yymsp[-1].minor.yy602 = wx_sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy602, 0);/*A-overwrites-B*/} + case 209: /* expr ::= expr IS NOT DISTINCT FROM expr */ +{ + yymsp[-5].minor.yy528 = wx_sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy528,yymsp[0].minor.yy528); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-5].minor.yy528, TK_ISNULL); +} break; - case 209: /* expr ::= PLUS|MINUS expr */ + case 210: /* expr ::= expr IS DISTINCT FROM expr */ { - yymsp[-1].minor.yy602 = wx_sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy602, 0); + yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy528,yymsp[0].minor.yy528); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-4].minor.yy528, TK_NOTNULL); +} + break; + case 211: /* expr ::= NOT expr */ + case 212: /* expr ::= BITNOT expr */ yytestcase(yyruleno==212); +{yymsp[-1].minor.yy528 = wx_sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy528, 0);/*A-overwrites-B*/} + break; + case 213: /* expr ::= PLUS|MINUS expr */ +{ + yymsp[-1].minor.yy528 = wx_sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy528, 0); /*A-overwrites-B*/ } break; - case 210: /* between_op ::= BETWEEN */ - case 213: /* in_op ::= IN */ yytestcase(yyruleno==213); -{yymsp[0].minor.yy60 = 0;} + case 214: /* expr ::= expr PTR expr */ +{ + ExprList *pList = wx_sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528); + pList = wx_sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528); + yylhsminor.yy528 = wx_sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); +} + yymsp[-2].minor.yy528 = yylhsminor.yy528; + break; + case 215: /* between_op ::= BETWEEN */ + case 218: /* in_op ::= IN */ yytestcase(yyruleno==218); +{yymsp[0].minor.yy394 = 0;} break; - case 212: /* expr ::= expr between_op expr AND expr */ + case 217: /* expr ::= expr between_op expr AND expr */ { - ExprList *pList = wx_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy602); - pList = wx_sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy602); - yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy602, 0); - if( yymsp[-4].minor.yy602 ){ - yymsp[-4].minor.yy602->x.pList = pList; + ExprList *pList = wx_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); + pList = wx_sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); + yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy528, 0); + if( yymsp[-4].minor.yy528 ){ + yymsp[-4].minor.yy528->x.pList = pList; }else{ wx_sqlite3ExprListDelete(pParse->db, pList); } - if( yymsp[-3].minor.yy60 ) yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy602, 0); + if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 215: /* expr ::= expr in_op LP exprlist RP */ + case 220: /* expr ::= expr in_op LP exprlist RP */ { - if( yymsp[-1].minor.yy338==0 ){ + if( yymsp[-1].minor.yy322==0 ){ /* Expressions of the form ** ** expr1 IN () @@ -161485,197 +171449,211 @@ static YYACTIONTYPE yy_reduce( ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ - wx_sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy602); - yymsp[-4].minor.yy602 = wx_sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy60 ? "1" : "0"); - }else if( yymsp[-1].minor.yy338->nExpr==1 && wx_sqlite3ExprIsConstant(yymsp[-1].minor.yy338->a[0].pExpr) ){ - Expr *pRHS = yymsp[-1].minor.yy338->a[0].pExpr; - yymsp[-1].minor.yy338->a[0].pExpr = 0; - wx_sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy338); - pRHS = wx_sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); - yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy602, pRHS); - if( yymsp[-3].minor.yy60 ) yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy602, 0); + wx_sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy528); + yymsp[-4].minor.yy528 = wx_sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy394 ? "true" : "false"); + if( yymsp[-4].minor.yy528 ) wx_sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy528); }else{ - yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy602, 0); - if( yymsp[-4].minor.yy602 ){ - yymsp[-4].minor.yy602->x.pList = yymsp[-1].minor.yy338; - wx_sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy602); + Expr *pRHS = yymsp[-1].minor.yy322->a[0].pExpr; + if( yymsp[-1].minor.yy322->nExpr==1 && wx_sqlite3ExprIsConstant(pRHS) && yymsp[-4].minor.yy528->op!=TK_VECTOR ){ + yymsp[-1].minor.yy322->a[0].pExpr = 0; + wx_sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); + pRHS = wx_sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); + yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy528, pRHS); + }else if( yymsp[-1].minor.yy322->nExpr==1 && pRHS->op==TK_SELECT ){ + yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); + wx_sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pRHS->x.pSelect); + pRHS->x.pSelect = 0; + wx_sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); }else{ - wx_sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy338); + yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); + if( yymsp[-4].minor.yy528==0 ){ + wx_sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); + }else if( yymsp[-4].minor.yy528->pLeft->op==TK_VECTOR ){ + int nExpr = yymsp[-4].minor.yy528->pLeft->x.pList->nExpr; + Select *pSelectRHS = wx_sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy322); + if( pSelectRHS ){ + parserDoubleLinkSelect(pParse, pSelectRHS); + wx_sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelectRHS); + } + }else{ + yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy322; + wx_sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528); + } } - if( yymsp[-3].minor.yy60 ) yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy602, 0); + if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } } break; - case 216: /* expr ::= LP select RP */ + case 221: /* expr ::= LP select RP */ { - yymsp[-2].minor.yy602 = wx_sqlite3PExpr(pParse, TK_SELECT, 0, 0); - wx_sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy602, yymsp[-1].minor.yy307); + yymsp[-2].minor.yy528 = wx_sqlite3PExpr(pParse, TK_SELECT, 0, 0); + wx_sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47); } break; - case 217: /* expr ::= expr in_op LP select RP */ + case 222: /* expr ::= expr in_op LP select RP */ { - yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy602, 0); - wx_sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy602, yymsp[-1].minor.yy307); - if( yymsp[-3].minor.yy60 ) yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy602, 0); + yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); + wx_sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, yymsp[-1].minor.yy47); + if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 218: /* expr ::= expr in_op nm dbnm paren_exprlist */ + case 223: /* expr ::= expr in_op nm dbnm paren_exprlist */ { SrcList *pSrc = wx_sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); Select *pSelect = wx_sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); - if( yymsp[0].minor.yy338 ) wx_sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy338); - yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy602, 0); - wx_sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy602, pSelect); - if( yymsp[-3].minor.yy60 ) yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy602, 0); + if( yymsp[0].minor.yy322 ) wx_sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy322); + yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); + wx_sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelect); + if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 219: /* expr ::= EXISTS LP select RP */ + case 224: /* expr ::= EXISTS LP select RP */ { Expr *p; - p = yymsp[-3].minor.yy602 = wx_sqlite3PExpr(pParse, TK_EXISTS, 0, 0); - wx_sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy307); + p = yymsp[-3].minor.yy528 = wx_sqlite3PExpr(pParse, TK_EXISTS, 0, 0); + wx_sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy47); } break; - case 220: /* expr ::= CASE case_operand case_exprlist case_else END */ + case 225: /* expr ::= CASE case_operand case_exprlist case_else END */ { - yymsp[-4].minor.yy602 = wx_sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy602, 0); - if( yymsp[-4].minor.yy602 ){ - yymsp[-4].minor.yy602->x.pList = yymsp[-1].minor.yy602 ? wx_sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy338,yymsp[-1].minor.yy602) : yymsp[-2].minor.yy338; - wx_sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy602); + yymsp[-4].minor.yy528 = wx_sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy528, 0); + if( yymsp[-4].minor.yy528 ){ + yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy528 ? wx_sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528) : yymsp[-2].minor.yy322; + wx_sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528); }else{ - wx_sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy338); - wx_sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy602); + wx_sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy322); + wx_sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); } } break; - case 221: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + case 226: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { - yymsp[-4].minor.yy338 = wx_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy338, yymsp[-2].minor.yy602); - yymsp[-4].minor.yy338 = wx_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy338, yymsp[0].minor.yy602); + yymsp[-4].minor.yy322 = wx_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); + yymsp[-4].minor.yy322 = wx_sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[0].minor.yy528); } break; - case 222: /* case_exprlist ::= WHEN expr THEN expr */ + case 227: /* case_exprlist ::= WHEN expr THEN expr */ { - yymsp[-3].minor.yy338 = wx_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy602); - yymsp[-3].minor.yy338 = wx_sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy338, yymsp[0].minor.yy602); + yymsp[-3].minor.yy322 = wx_sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); + yymsp[-3].minor.yy322 = wx_sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528); } break; - case 225: /* case_operand ::= expr */ -{yymsp[0].minor.yy602 = yymsp[0].minor.yy602; /*A-overwrites-X*/} + case 230: /* case_operand ::= expr */ +{yymsp[0].minor.yy528 = yymsp[0].minor.yy528; /*A-overwrites-X*/} break; - case 228: /* nexprlist ::= nexprlist COMMA expr */ -{yymsp[-2].minor.yy338 = wx_sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy338,yymsp[0].minor.yy602);} + case 233: /* nexprlist ::= nexprlist COMMA expr */ +{yymsp[-2].minor.yy322 = wx_sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);} break; - case 229: /* nexprlist ::= expr */ -{yymsp[0].minor.yy338 = wx_sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy602); /*A-overwrites-Y*/} + case 234: /* nexprlist ::= expr */ +{yymsp[0].minor.yy322 = wx_sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/} break; - case 231: /* paren_exprlist ::= LP exprlist RP */ - case 236: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==236); -{yymsp[-2].minor.yy338 = yymsp[-1].minor.yy338;} + case 236: /* paren_exprlist ::= LP exprlist RP */ + case 241: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==241); +{yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;} break; - case 232: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + case 237: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ { wx_sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, - wx_sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy338, yymsp[-10].minor.yy60, - &yymsp[-11].minor.yy0, yymsp[0].minor.yy602, SQLITE_SO_ASC, yymsp[-8].minor.yy60, SQLITE_IDXTYPE_APPDEF); + wx_sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394, + &yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF); if( IN_RENAME_OBJECT && pParse->pNewIndex ){ wx_sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); } } break; - case 233: /* uniqueflag ::= UNIQUE */ - case 275: /* raisetype ::= ABORT */ yytestcase(yyruleno==275); -{yymsp[0].minor.yy60 = OE_Abort;} + case 238: /* uniqueflag ::= UNIQUE */ + case 280: /* raisetype ::= ABORT */ yytestcase(yyruleno==280); +{yymsp[0].minor.yy394 = OE_Abort;} break; - case 234: /* uniqueflag ::= */ -{yymsp[1].minor.yy60 = OE_None;} + case 239: /* uniqueflag ::= */ +{yymsp[1].minor.yy394 = OE_None;} break; - case 237: /* eidlist ::= eidlist COMMA nm collate sortorder */ + case 242: /* eidlist ::= eidlist COMMA nm collate sortorder */ { - yymsp[-4].minor.yy338 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy338, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy60, yymsp[0].minor.yy60); + yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); } break; - case 238: /* eidlist ::= nm collate sortorder */ + case 243: /* eidlist ::= nm collate sortorder */ { - yymsp[-2].minor.yy338 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy60, yymsp[0].minor.yy60); /*A-overwrites-Y*/ + yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/ } break; - case 241: /* cmd ::= DROP INDEX ifexists fullname */ -{wx_sqlite3DropIndex(pParse, yymsp[0].minor.yy291, yymsp[-1].minor.yy60);} + case 246: /* cmd ::= DROP INDEX ifexists fullname */ +{wx_sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);} break; - case 242: /* cmd ::= VACUUM vinto */ -{wx_sqlite3Vacuum(pParse,0,yymsp[0].minor.yy602);} + case 247: /* cmd ::= VACUUM vinto */ +{wx_sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);} break; - case 243: /* cmd ::= VACUUM nm vinto */ -{wx_sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy602);} + case 248: /* cmd ::= VACUUM nm vinto */ +{wx_sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);} break; - case 246: /* cmd ::= PRAGMA nm dbnm */ + case 251: /* cmd ::= PRAGMA nm dbnm */ {wx_sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} break; - case 247: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + case 252: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ {wx_sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} break; - case 248: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + case 253: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ {wx_sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} break; - case 249: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ + case 254: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ {wx_sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} break; - case 250: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ + case 255: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ {wx_sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} break; - case 253: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + case 258: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ { Token all; all.z = yymsp[-3].minor.yy0.z; all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - wx_sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy483, &all); + wx_sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all); } break; - case 254: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 259: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { - wx_sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy60, yymsp[-4].minor.yy50.a, yymsp[-4].minor.yy50.b, yymsp[-2].minor.yy291, yymsp[0].minor.yy602, yymsp[-10].minor.yy60, yymsp[-8].minor.yy60); + wx_sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ } break; - case 255: /* trigger_time ::= BEFORE|AFTER */ -{ yymsp[0].minor.yy60 = yymsp[0].major; /*A-overwrites-X*/ } + case 260: /* trigger_time ::= BEFORE|AFTER */ +{ yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ } break; - case 256: /* trigger_time ::= INSTEAD OF */ -{ yymsp[-1].minor.yy60 = TK_INSTEAD;} + case 261: /* trigger_time ::= INSTEAD OF */ +{ yymsp[-1].minor.yy394 = TK_INSTEAD;} break; - case 257: /* trigger_time ::= */ -{ yymsp[1].minor.yy60 = TK_BEFORE; } + case 262: /* trigger_time ::= */ +{ yymsp[1].minor.yy394 = TK_BEFORE; } break; - case 258: /* trigger_event ::= DELETE|INSERT */ - case 259: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==259); -{yymsp[0].minor.yy50.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy50.b = 0;} + case 263: /* trigger_event ::= DELETE|INSERT */ + case 264: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==264); +{yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;} break; - case 260: /* trigger_event ::= UPDATE OF idlist */ -{yymsp[-2].minor.yy50.a = TK_UPDATE; yymsp[-2].minor.yy50.b = yymsp[0].minor.yy288;} + case 265: /* trigger_event ::= UPDATE OF idlist */ +{yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;} break; - case 261: /* when_clause ::= */ - case 280: /* key_opt ::= */ yytestcase(yyruleno==280); -{ yymsp[1].minor.yy602 = 0; } + case 266: /* when_clause ::= */ + case 285: /* key_opt ::= */ yytestcase(yyruleno==285); +{ yymsp[1].minor.yy528 = 0; } break; - case 262: /* when_clause ::= WHEN expr */ - case 281: /* key_opt ::= KEY expr */ yytestcase(yyruleno==281); -{ yymsp[-1].minor.yy602 = yymsp[0].minor.yy602; } + case 267: /* when_clause ::= WHEN expr */ + case 286: /* key_opt ::= KEY expr */ yytestcase(yyruleno==286); +{ yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; } break; - case 263: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 268: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { - assert( yymsp[-2].minor.yy483!=0 ); - yymsp[-2].minor.yy483->pLast->pNext = yymsp[-1].minor.yy483; - yymsp[-2].minor.yy483->pLast = yymsp[-1].minor.yy483; + assert( yymsp[-2].minor.yy33!=0 ); + yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33; + yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33; } break; - case 264: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 269: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - assert( yymsp[-1].minor.yy483!=0 ); - yymsp[-1].minor.yy483->pLast = yymsp[-1].minor.yy483; + assert( yymsp[-1].minor.yy33!=0 ); + yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33; } break; - case 265: /* trnm ::= nm DOT nm */ + case 270: /* trnm ::= nm DOT nm */ { yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; wx_sqlite3ErrorMsg(pParse, @@ -161683,364 +171661,370 @@ static YYACTIONTYPE yy_reduce( "statements within triggers"); } break; - case 266: /* tridxby ::= INDEXED BY nm */ + case 271: /* tridxby ::= INDEXED BY nm */ { wx_sqlite3ErrorMsg(pParse, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 267: /* tridxby ::= NOT INDEXED */ + case 272: /* tridxby ::= NOT INDEXED */ { wx_sqlite3ErrorMsg(pParse, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 268: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ -{yylhsminor.yy483 = wx_sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy291, yymsp[-3].minor.yy338, yymsp[-1].minor.yy602, yymsp[-7].minor.yy60, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy528);} - yymsp[-8].minor.yy483 = yylhsminor.yy483; + case 273: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ +{yylhsminor.yy33 = wx_sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);} + yymsp[-8].minor.yy33 = yylhsminor.yy33; break; - case 269: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + case 274: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ { - yylhsminor.yy483 = wx_sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy288,yymsp[-2].minor.yy307,yymsp[-6].minor.yy60,yymsp[-1].minor.yy178,yymsp[-7].minor.yy528,yymsp[0].minor.yy528);/*yylhsminor.yy483-overwrites-yymsp[-6].minor.yy60*/ + yylhsminor.yy33 = wx_sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/ } - yymsp[-7].minor.yy483 = yylhsminor.yy483; + yymsp[-7].minor.yy33 = yylhsminor.yy33; break; - case 270: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -{yylhsminor.yy483 = wx_sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy602, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy528);} - yymsp[-5].minor.yy483 = yylhsminor.yy483; + case 275: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ +{yylhsminor.yy33 = wx_sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);} + yymsp[-5].minor.yy33 = yylhsminor.yy33; break; - case 271: /* trigger_cmd ::= scanpt select scanpt */ -{yylhsminor.yy483 = wx_sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy307, yymsp[-2].minor.yy528, yymsp[0].minor.yy528); /*yylhsminor.yy483-overwrites-yymsp[-1].minor.yy307*/} - yymsp[-2].minor.yy483 = yylhsminor.yy483; + case 276: /* trigger_cmd ::= scanpt select scanpt */ +{yylhsminor.yy33 = wx_sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/} + yymsp[-2].minor.yy33 = yylhsminor.yy33; break; - case 272: /* expr ::= RAISE LP IGNORE RP */ + case 277: /* expr ::= RAISE LP IGNORE RP */ { - yymsp[-3].minor.yy602 = wx_sqlite3PExpr(pParse, TK_RAISE, 0, 0); - if( yymsp[-3].minor.yy602 ){ - yymsp[-3].minor.yy602->affExpr = OE_Ignore; + yymsp[-3].minor.yy528 = wx_sqlite3PExpr(pParse, TK_RAISE, 0, 0); + if( yymsp[-3].minor.yy528 ){ + yymsp[-3].minor.yy528->affExpr = OE_Ignore; } } break; - case 273: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 278: /* expr ::= RAISE LP raisetype COMMA nm RP */ { - yymsp[-5].minor.yy602 = wx_sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); - if( yymsp[-5].minor.yy602 ) { - yymsp[-5].minor.yy602->affExpr = (char)yymsp[-3].minor.yy60; + yymsp[-5].minor.yy528 = wx_sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); + if( yymsp[-5].minor.yy528 ) { + yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394; } } break; - case 274: /* raisetype ::= ROLLBACK */ -{yymsp[0].minor.yy60 = OE_Rollback;} + case 279: /* raisetype ::= ROLLBACK */ +{yymsp[0].minor.yy394 = OE_Rollback;} break; - case 276: /* raisetype ::= FAIL */ -{yymsp[0].minor.yy60 = OE_Fail;} + case 281: /* raisetype ::= FAIL */ +{yymsp[0].minor.yy394 = OE_Fail;} break; - case 277: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 282: /* cmd ::= DROP TRIGGER ifexists fullname */ { - wx_sqlite3DropTrigger(pParse,yymsp[0].minor.yy291,yymsp[-1].minor.yy60); + wx_sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394); } break; - case 278: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 283: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - wx_sqlite3Attach(pParse, yymsp[-3].minor.yy602, yymsp[-1].minor.yy602, yymsp[0].minor.yy602); + wx_sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528); } break; - case 279: /* cmd ::= DETACH database_kw_opt expr */ + case 284: /* cmd ::= DETACH database_kw_opt expr */ { - wx_sqlite3Detach(pParse, yymsp[0].minor.yy602); + wx_sqlite3Detach(pParse, yymsp[0].minor.yy528); } break; - case 282: /* cmd ::= REINDEX */ + case 287: /* cmd ::= REINDEX */ {wx_sqlite3Reindex(pParse, 0, 0);} break; - case 283: /* cmd ::= REINDEX nm dbnm */ + case 288: /* cmd ::= REINDEX nm dbnm */ {wx_sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 284: /* cmd ::= ANALYZE */ + case 289: /* cmd ::= ANALYZE */ {wx_sqlite3Analyze(pParse, 0, 0);} break; - case 285: /* cmd ::= ANALYZE nm dbnm */ + case 290: /* cmd ::= ANALYZE nm dbnm */ {wx_sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 286: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 291: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - wx_sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy291,&yymsp[0].minor.yy0); + wx_sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0); } break; - case 287: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + case 292: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ { yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; wx_sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); } break; - case 288: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + case 293: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ { - wx_sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy291, &yymsp[0].minor.yy0); + wx_sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0); } break; - case 289: /* add_column_fullname ::= fullname */ + case 294: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); - wx_sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy291); + wx_sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131); } break; - case 290: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + case 295: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ { - wx_sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy291, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); + wx_sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; - case 291: /* cmd ::= create_vtab */ + case 296: /* cmd ::= create_vtab */ {wx_sqlite3VtabFinishParse(pParse,0);} break; - case 292: /* cmd ::= create_vtab LP vtabarglist RP */ + case 297: /* cmd ::= create_vtab LP vtabarglist RP */ {wx_sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} break; - case 293: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + case 298: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { - wx_sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy60); + wx_sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394); } break; - case 294: /* vtabarg ::= */ + case 299: /* vtabarg ::= */ {wx_sqlite3VtabArgInit(pParse);} break; - case 295: /* vtabargtoken ::= ANY */ - case 296: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==296); - case 297: /* lp ::= LP */ yytestcase(yyruleno==297); + case 300: /* vtabargtoken ::= ANY */ + case 301: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==301); + case 302: /* lp ::= LP */ yytestcase(yyruleno==302); {wx_sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; - case 298: /* with ::= WITH wqlist */ - case 299: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==299); -{ wx_sqlite3WithPush(pParse, yymsp[0].minor.yy195, 1); } + case 303: /* with ::= WITH wqlist */ + case 304: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==304); +{ wx_sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); } break; - case 300: /* wqas ::= AS */ -{yymsp[0].minor.yy570 = M10d_Any;} + case 305: /* wqas ::= AS */ +{yymsp[0].minor.yy516 = M10d_Any;} break; - case 301: /* wqas ::= AS MATERIALIZED */ -{yymsp[-1].minor.yy570 = M10d_Yes;} + case 306: /* wqas ::= AS MATERIALIZED */ +{yymsp[-1].minor.yy516 = M10d_Yes;} break; - case 302: /* wqas ::= AS NOT MATERIALIZED */ -{yymsp[-2].minor.yy570 = M10d_No;} + case 307: /* wqas ::= AS NOT MATERIALIZED */ +{yymsp[-2].minor.yy516 = M10d_No;} break; - case 303: /* wqitem ::= nm eidlist_opt wqas LP select RP */ + case 308: /* wqitem ::= nm eidlist_opt wqas LP select RP */ { - yymsp[-5].minor.yy607 = wx_sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy338, yymsp[-1].minor.yy307, yymsp[-3].minor.yy570); /*A-overwrites-X*/ + yymsp[-5].minor.yy385 = wx_sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/ } break; - case 304: /* wqlist ::= wqitem */ + case 309: /* wqlist ::= wqitem */ { - yymsp[0].minor.yy195 = wx_sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy607); /*A-overwrites-X*/ + yymsp[0].minor.yy521 = wx_sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/ } break; - case 305: /* wqlist ::= wqlist COMMA wqitem */ + case 310: /* wqlist ::= wqlist COMMA wqitem */ { - yymsp[-2].minor.yy195 = wx_sqlite3WithAdd(pParse, yymsp[-2].minor.yy195, yymsp[0].minor.yy607); + yymsp[-2].minor.yy521 = wx_sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385); } break; - case 306: /* windowdefn_list ::= windowdefn */ -{ yylhsminor.yy19 = yymsp[0].minor.yy19; } - yymsp[0].minor.yy19 = yylhsminor.yy19; + case 311: /* windowdefn_list ::= windowdefn */ +{ yylhsminor.yy41 = yymsp[0].minor.yy41; } + yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 307: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 312: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { - assert( yymsp[0].minor.yy19!=0 ); - wx_sqlite3WindowChain(pParse, yymsp[0].minor.yy19, yymsp[-2].minor.yy19); - yymsp[0].minor.yy19->pNextWin = yymsp[-2].minor.yy19; - yylhsminor.yy19 = yymsp[0].minor.yy19; + assert( yymsp[0].minor.yy41!=0 ); + wx_sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41); + yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41; + yylhsminor.yy41 = yymsp[0].minor.yy41; } - yymsp[-2].minor.yy19 = yylhsminor.yy19; + yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 308: /* windowdefn ::= nm AS LP window RP */ + case 313: /* windowdefn ::= nm AS LP window RP */ { - if( ALWAYS(yymsp[-1].minor.yy19) ){ - yymsp[-1].minor.yy19->zName = wx_sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); + if( ALWAYS(yymsp[-1].minor.yy41) ){ + yymsp[-1].minor.yy41->zName = wx_sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); } - yylhsminor.yy19 = yymsp[-1].minor.yy19; + yylhsminor.yy41 = yymsp[-1].minor.yy41; } - yymsp[-4].minor.yy19 = yylhsminor.yy19; + yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 309: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + case 314: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ { - yymsp[-4].minor.yy19 = wx_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy19, yymsp[-2].minor.yy338, yymsp[-1].minor.yy338, 0); + yymsp[-4].minor.yy41 = wx_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0); } break; - case 310: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + case 315: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ { - yylhsminor.yy19 = wx_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy19, yymsp[-2].minor.yy338, yymsp[-1].minor.yy338, &yymsp[-5].minor.yy0); + yylhsminor.yy41 = wx_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0); } - yymsp[-5].minor.yy19 = yylhsminor.yy19; + yymsp[-5].minor.yy41 = yylhsminor.yy41; break; - case 311: /* window ::= ORDER BY sortlist frame_opt */ + case 316: /* window ::= ORDER BY sortlist frame_opt */ { - yymsp[-3].minor.yy19 = wx_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy19, 0, yymsp[-1].minor.yy338, 0); + yymsp[-3].minor.yy41 = wx_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0); } break; - case 312: /* window ::= nm ORDER BY sortlist frame_opt */ + case 317: /* window ::= nm ORDER BY sortlist frame_opt */ { - yylhsminor.yy19 = wx_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy19, 0, yymsp[-1].minor.yy338, &yymsp[-4].minor.yy0); + yylhsminor.yy41 = wx_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0); } - yymsp[-4].minor.yy19 = yylhsminor.yy19; + yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 313: /* window ::= frame_opt */ - case 332: /* filter_over ::= over_clause */ yytestcase(yyruleno==332); + case 318: /* window ::= frame_opt */ + case 337: /* filter_over ::= over_clause */ yytestcase(yyruleno==337); { - yylhsminor.yy19 = yymsp[0].minor.yy19; + yylhsminor.yy41 = yymsp[0].minor.yy41; } - yymsp[0].minor.yy19 = yylhsminor.yy19; + yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 314: /* window ::= nm frame_opt */ + case 319: /* window ::= nm frame_opt */ { - yylhsminor.yy19 = wx_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy19, 0, 0, &yymsp[-1].minor.yy0); + yylhsminor.yy41 = wx_sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0); } - yymsp[-1].minor.yy19 = yylhsminor.yy19; + yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 315: /* frame_opt ::= */ + case 320: /* frame_opt ::= */ { - yymsp[1].minor.yy19 = wx_sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); + yymsp[1].minor.yy41 = wx_sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); } break; - case 316: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + case 321: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ { - yylhsminor.yy19 = wx_sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy60, yymsp[-1].minor.yy113.eType, yymsp[-1].minor.yy113.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy570); + yylhsminor.yy41 = wx_sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516); } - yymsp[-2].minor.yy19 = yylhsminor.yy19; + yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 317: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + case 322: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ { - yylhsminor.yy19 = wx_sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy60, yymsp[-3].minor.yy113.eType, yymsp[-3].minor.yy113.pExpr, yymsp[-1].minor.yy113.eType, yymsp[-1].minor.yy113.pExpr, yymsp[0].minor.yy570); + yylhsminor.yy41 = wx_sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516); } - yymsp[-5].minor.yy19 = yylhsminor.yy19; + yymsp[-5].minor.yy41 = yylhsminor.yy41; break; - case 319: /* frame_bound_s ::= frame_bound */ - case 321: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==321); -{yylhsminor.yy113 = yymsp[0].minor.yy113;} - yymsp[0].minor.yy113 = yylhsminor.yy113; + case 324: /* frame_bound_s ::= frame_bound */ + case 326: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==326); +{yylhsminor.yy595 = yymsp[0].minor.yy595;} + yymsp[0].minor.yy595 = yylhsminor.yy595; break; - case 320: /* frame_bound_s ::= UNBOUNDED PRECEDING */ - case 322: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==322); - case 324: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==324); -{yylhsminor.yy113.eType = yymsp[-1].major; yylhsminor.yy113.pExpr = 0;} - yymsp[-1].minor.yy113 = yylhsminor.yy113; + case 325: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 327: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==327); + case 329: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==329); +{yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;} + yymsp[-1].minor.yy595 = yylhsminor.yy595; break; - case 323: /* frame_bound ::= expr PRECEDING|FOLLOWING */ -{yylhsminor.yy113.eType = yymsp[0].major; yylhsminor.yy113.pExpr = yymsp[-1].minor.yy602;} - yymsp[-1].minor.yy113 = yylhsminor.yy113; + case 328: /* frame_bound ::= expr PRECEDING|FOLLOWING */ +{yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;} + yymsp[-1].minor.yy595 = yylhsminor.yy595; break; - case 325: /* frame_exclude_opt ::= */ -{yymsp[1].minor.yy570 = 0;} + case 330: /* frame_exclude_opt ::= */ +{yymsp[1].minor.yy516 = 0;} break; - case 326: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ -{yymsp[-1].minor.yy570 = yymsp[0].minor.yy570;} + case 331: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ +{yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;} break; - case 327: /* frame_exclude ::= NO OTHERS */ - case 328: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==328); -{yymsp[-1].minor.yy570 = yymsp[-1].major; /*A-overwrites-X*/} + case 332: /* frame_exclude ::= NO OTHERS */ + case 333: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==333); +{yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/} break; - case 329: /* frame_exclude ::= GROUP|TIES */ -{yymsp[0].minor.yy570 = yymsp[0].major; /*A-overwrites-X*/} + case 334: /* frame_exclude ::= GROUP|TIES */ +{yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/} break; - case 330: /* window_clause ::= WINDOW windowdefn_list */ -{ yymsp[-1].minor.yy19 = yymsp[0].minor.yy19; } + case 335: /* window_clause ::= WINDOW windowdefn_list */ +{ yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; } break; - case 331: /* filter_over ::= filter_clause over_clause */ + case 336: /* filter_over ::= filter_clause over_clause */ { - yymsp[0].minor.yy19->pFilter = yymsp[-1].minor.yy602; - yylhsminor.yy19 = yymsp[0].minor.yy19; + if( yymsp[0].minor.yy41 ){ + yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528; + }else{ + wx_sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); + } + yylhsminor.yy41 = yymsp[0].minor.yy41; } - yymsp[-1].minor.yy19 = yylhsminor.yy19; + yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 333: /* filter_over ::= filter_clause */ + case 338: /* filter_over ::= filter_clause */ { - yylhsminor.yy19 = (Window*)wx_sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yylhsminor.yy19 ){ - yylhsminor.yy19->eFrmType = TK_FILTER; - yylhsminor.yy19->pFilter = yymsp[0].minor.yy602; + yylhsminor.yy41 = (Window*)wx_sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yylhsminor.yy41 ){ + yylhsminor.yy41->eFrmType = TK_FILTER; + yylhsminor.yy41->pFilter = yymsp[0].minor.yy528; }else{ - wx_sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy602); + wx_sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528); } } - yymsp[0].minor.yy19 = yylhsminor.yy19; + yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 334: /* over_clause ::= OVER LP window RP */ + case 339: /* over_clause ::= OVER LP window RP */ { - yymsp[-3].minor.yy19 = yymsp[-1].minor.yy19; - assert( yymsp[-3].minor.yy19!=0 ); + yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41; + assert( yymsp[-3].minor.yy41!=0 ); } break; - case 335: /* over_clause ::= OVER nm */ + case 340: /* over_clause ::= OVER nm */ { - yymsp[-1].minor.yy19 = (Window*)wx_sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yymsp[-1].minor.yy19 ){ - yymsp[-1].minor.yy19->zName = wx_sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); + yymsp[-1].minor.yy41 = (Window*)wx_sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yymsp[-1].minor.yy41 ){ + yymsp[-1].minor.yy41->zName = wx_sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); } } break; - case 336: /* filter_clause ::= FILTER LP WHERE expr RP */ -{ yymsp[-4].minor.yy602 = yymsp[-1].minor.yy602; } + case 341: /* filter_clause ::= FILTER LP WHERE expr RP */ +{ yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; } break; default: - /* (337) input ::= cmdlist */ yytestcase(yyruleno==337); - /* (338) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==338); - /* (339) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=339); - /* (340) ecmd ::= SEMI */ yytestcase(yyruleno==340); - /* (341) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==341); - /* (342) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=342); - /* (343) trans_opt ::= */ yytestcase(yyruleno==343); - /* (344) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==344); - /* (345) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==345); - /* (346) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==346); - /* (347) savepoint_opt ::= */ yytestcase(yyruleno==347); - /* (348) cmd ::= create_table create_table_args */ yytestcase(yyruleno==348); - /* (349) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==349); - /* (350) columnlist ::= columnname carglist */ yytestcase(yyruleno==350); - /* (351) nm ::= ID|INDEXED */ yytestcase(yyruleno==351); - /* (352) nm ::= STRING */ yytestcase(yyruleno==352); - /* (353) nm ::= JOIN_KW */ yytestcase(yyruleno==353); - /* (354) typetoken ::= typename */ yytestcase(yyruleno==354); - /* (355) typename ::= ID|STRING */ yytestcase(yyruleno==355); - /* (356) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=356); - /* (357) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=357); - /* (358) carglist ::= carglist ccons */ yytestcase(yyruleno==358); - /* (359) carglist ::= */ yytestcase(yyruleno==359); - /* (360) ccons ::= NULL onconf */ yytestcase(yyruleno==360); - /* (361) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==361); - /* (362) ccons ::= AS generated */ yytestcase(yyruleno==362); - /* (363) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==363); - /* (364) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==364); - /* (365) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=365); - /* (366) tconscomma ::= */ yytestcase(yyruleno==366); - /* (367) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=367); - /* (368) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=368); - /* (369) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=369); - /* (370) oneselect ::= values */ yytestcase(yyruleno==370); - /* (371) sclp ::= selcollist COMMA */ yytestcase(yyruleno==371); - /* (372) as ::= ID|STRING */ yytestcase(yyruleno==372); - /* (373) returning ::= */ yytestcase(yyruleno==373); - /* (374) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=374); - /* (375) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==375); - /* (376) exprlist ::= nexprlist */ yytestcase(yyruleno==376); - /* (377) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=377); - /* (378) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=378); - /* (379) nmnum ::= ON */ yytestcase(yyruleno==379); - /* (380) nmnum ::= DELETE */ yytestcase(yyruleno==380); - /* (381) nmnum ::= DEFAULT */ yytestcase(yyruleno==381); - /* (382) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==382); - /* (383) foreach_clause ::= */ yytestcase(yyruleno==383); - /* (384) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==384); - /* (385) trnm ::= nm */ yytestcase(yyruleno==385); - /* (386) tridxby ::= */ yytestcase(yyruleno==386); - /* (387) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==387); - /* (388) database_kw_opt ::= */ yytestcase(yyruleno==388); - /* (389) kwcolumn_opt ::= */ yytestcase(yyruleno==389); - /* (390) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==390); - /* (391) vtabarglist ::= vtabarg */ yytestcase(yyruleno==391); - /* (392) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==392); - /* (393) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==393); - /* (394) anylist ::= */ yytestcase(yyruleno==394); - /* (395) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==395); - /* (396) anylist ::= anylist ANY */ yytestcase(yyruleno==396); - /* (397) with ::= */ yytestcase(yyruleno==397); + /* (342) input ::= cmdlist */ yytestcase(yyruleno==342); + /* (343) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==343); + /* (344) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=344); + /* (345) ecmd ::= SEMI */ yytestcase(yyruleno==345); + /* (346) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==346); + /* (347) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=347); + /* (348) trans_opt ::= */ yytestcase(yyruleno==348); + /* (349) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==349); + /* (350) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==350); + /* (351) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==351); + /* (352) savepoint_opt ::= */ yytestcase(yyruleno==352); + /* (353) cmd ::= create_table create_table_args */ yytestcase(yyruleno==353); + /* (354) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=354); + /* (355) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==355); + /* (356) columnlist ::= columnname carglist */ yytestcase(yyruleno==356); + /* (357) nm ::= ID|INDEXED */ yytestcase(yyruleno==357); + /* (358) nm ::= STRING */ yytestcase(yyruleno==358); + /* (359) nm ::= JOIN_KW */ yytestcase(yyruleno==359); + /* (360) typetoken ::= typename */ yytestcase(yyruleno==360); + /* (361) typename ::= ID|STRING */ yytestcase(yyruleno==361); + /* (362) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=362); + /* (363) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=363); + /* (364) carglist ::= carglist ccons */ yytestcase(yyruleno==364); + /* (365) carglist ::= */ yytestcase(yyruleno==365); + /* (366) ccons ::= NULL onconf */ yytestcase(yyruleno==366); + /* (367) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==367); + /* (368) ccons ::= AS generated */ yytestcase(yyruleno==368); + /* (369) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==369); + /* (370) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==370); + /* (371) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=371); + /* (372) tconscomma ::= */ yytestcase(yyruleno==372); + /* (373) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=373); + /* (374) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=374); + /* (375) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=375); + /* (376) oneselect ::= values */ yytestcase(yyruleno==376); + /* (377) sclp ::= selcollist COMMA */ yytestcase(yyruleno==377); + /* (378) as ::= ID|STRING */ yytestcase(yyruleno==378); + /* (379) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=379); + /* (380) returning ::= */ yytestcase(yyruleno==380); + /* (381) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=381); + /* (382) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==382); + /* (383) exprlist ::= nexprlist */ yytestcase(yyruleno==383); + /* (384) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=384); + /* (385) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=385); + /* (386) nmnum ::= ON */ yytestcase(yyruleno==386); + /* (387) nmnum ::= DELETE */ yytestcase(yyruleno==387); + /* (388) nmnum ::= DEFAULT */ yytestcase(yyruleno==388); + /* (389) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==389); + /* (390) foreach_clause ::= */ yytestcase(yyruleno==390); + /* (391) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==391); + /* (392) trnm ::= nm */ yytestcase(yyruleno==392); + /* (393) tridxby ::= */ yytestcase(yyruleno==393); + /* (394) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==394); + /* (395) database_kw_opt ::= */ yytestcase(yyruleno==395); + /* (396) kwcolumn_opt ::= */ yytestcase(yyruleno==396); + /* (397) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==397); + /* (398) vtabarglist ::= vtabarg */ yytestcase(yyruleno==398); + /* (399) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==399); + /* (400) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==400); + /* (401) anylist ::= */ yytestcase(yyruleno==401); + /* (402) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==402); + /* (403) anylist ::= anylist ANY */ yytestcase(yyruleno==403); + /* (404) with ::= */ yytestcase(yyruleno==404); break; /********** End reduce actions ************************************************/ }; @@ -162198,8 +172182,8 @@ SQLITE_PRIVATE void wx_sqlite3Parser( yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact); if( yyact >= YY_MIN_REDUCE ){ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */ - assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); #ifndef NDEBUG + assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); if( yyTraceFILE ){ int yysize = yyRuleInfoNRhs[yyruleno]; if( yysize ){ @@ -162297,14 +172281,13 @@ SQLITE_PRIVATE void wx_sqlite3Parser( yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); yymajor = YYNOCODE; }else{ - while( yypParser->yytos >= yypParser->yystack - && (yyact = yy_find_reduce_action( - yypParser->yytos->stateno, - YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE - ){ + while( yypParser->yytos > yypParser->yystack ){ + yyact = yy_find_reduce_action(yypParser->yytos->stateno, + YYERRORSYMBOL); + if( yyact<=YY_MAX_SHIFTREDUCE ) break; yy_pop_parser_stack(yypParser); } - if( yypParser->yytos < yypParser->yystack || yymajor==0 ){ + if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); yy_parse_failed(yypParser); #ifndef YYNOERRORRECOVERY @@ -162444,6 +172427,7 @@ SQLITE_PRIVATE int wx_sqlite3ParserFallback(int iToken){ #define CC_ID 27 /* unicode characters usable in IDs */ #define CC_ILLEGAL 28 /* Illegal character */ #define CC_NUL 29 /* 0x00 */ +#define CC_BOM 30 /* First byte of UTF8 BOM: 0xEF 0xBB 0xBF */ static const unsigned char aiClass[] = { #ifdef SQLITE_ASCII @@ -162456,14 +172440,14 @@ static const unsigned char aiClass[] = { /* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 9, 28, 28, 28, 2, /* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 28, 10, 28, 25, 28, -/* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* 9x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Ax */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Bx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Cx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Dx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Ex */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -/* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 +/* 8x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* 9x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* Ax */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* Cx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* Dx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* Ex */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 30, +/* Fx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27 #endif #ifdef SQLITE_EBCDIC /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ @@ -163163,6 +173147,9 @@ SQLITE_PRIVATE int wx_sqlite3GetToken(const unsigned char *z, int *tokenType){ for(i=2; (c=z[i])!=0 && c!='\n'; i++){} *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; + }else if( z[1]=='>' ){ + *tokenType = TK_PTR; + return 2 + (z[2]=='>'); } *tokenType = TK_MINUS; return 1; @@ -163409,6 +173396,14 @@ SQLITE_PRIVATE int wx_sqlite3GetToken(const unsigned char *z, int *tokenType){ i = 1; break; } + case CC_BOM: { + if( z[1]==0xbb && z[2]==0xbf ){ + *tokenType = TK_SPACE; + return 3; + } + i = 1; + break; + } case CC_NUL: { *tokenType = TK_ILLEGAL; return 0; @@ -163424,13 +173419,9 @@ SQLITE_PRIVATE int wx_sqlite3GetToken(const unsigned char *z, int *tokenType){ } /* -** Run the parser on the given SQL string. The parser structure is -** passed in. An SQLITE_ status code is returned. If an error occurs -** then an and attempt is made to write an error message into -** memory obtained from wx_sqlite3_malloc() and to make *pzErrMsg point to that -** error message. +** Run the parser on the given SQL string. */ -SQLITE_PRIVATE int wx_sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ +SQLITE_PRIVATE int wx_sqlite3RunParser(Parse *pParse, const char *zSql){ int nErr = 0; /* Number of errors encountered */ void *pEngine; /* The LEMON-generated LALR(1) parser */ int n = 0; /* Length of the next token token */ @@ -163438,6 +173429,7 @@ SQLITE_PRIVATE int wx_sqlite3RunParser(Parse *pParse, const char *zSql, char **p int lastTokenParsed = -1; /* type of the previous token */ wx_sqlite3 *db = pParse->db; /* The database connection */ int mxSqlLen; /* Max length of an SQL string */ + Parse *pParentParse = 0; /* Outer parse context, if any */ #ifdef wx_sqlite3Parser_ENGINEALWAYSONSTACK yyParser sEngine; /* Space to hold the Lemon-generated Parser object */ #endif @@ -163450,7 +173442,6 @@ SQLITE_PRIVATE int wx_sqlite3RunParser(Parse *pParse, const char *zSql, char **p } pParse->rc = SQLITE_OK; pParse->zTail = zSql; - assert( pzErrMsg!=0 ); #ifdef SQLITE_DEBUG if( db->flags & SQLITE_ParserTrace ){ printf("parser: [[[%s]]]\n", zSql); @@ -163473,13 +173464,14 @@ SQLITE_PRIVATE int wx_sqlite3RunParser(Parse *pParse, const char *zSql, char **p assert( pParse->pNewTrigger==0 ); assert( pParse->nVar==0 ); assert( pParse->pVList==0 ); - pParse->pParentParse = db->pParse; + pParentParse = db->pParse; db->pParse = pParse; while( 1 ){ n = wx_sqlite3GetToken((u8*)zSql, &tokenType); mxSqlLen -= n; if( mxSqlLen<0 ){ pParse->rc = SQLITE_TOOBIG; + pParse->nErr++; break; } #ifndef SQLITE_OMIT_WINDOWFUNC @@ -163493,6 +173485,7 @@ SQLITE_PRIVATE int wx_sqlite3RunParser(Parse *pParse, const char *zSql, char **p #endif /* SQLITE_OMIT_WINDOWFUNC */ if( AtomicLoad(&db->u1.isInterrupted) ){ pParse->rc = SQLITE_INTERRUPT; + pParse->nErr++; break; } if( tokenType==TK_SPACE ){ @@ -163522,7 +173515,10 @@ SQLITE_PRIVATE int wx_sqlite3RunParser(Parse *pParse, const char *zSql, char **p tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); #endif /* SQLITE_OMIT_WINDOWFUNC */ }else{ - wx_sqlite3ErrorMsg(pParse, "unrecognized token: \"%.*s\"", n, zSql); + Token x; + x.z = zSql; + x.n = n; + wx_sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"", &x); break; } } @@ -163550,46 +173546,30 @@ SQLITE_PRIVATE int wx_sqlite3RunParser(Parse *pParse, const char *zSql, char **p if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM_BKPT; } - if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){ - pParse->zErrMsg = wx_sqlite3MPrintf(db, "%s", wx_sqlite3ErrStr(pParse->rc)); - } - assert( pzErrMsg!=0 ); - if( pParse->zErrMsg ){ - *pzErrMsg = pParse->zErrMsg; - wx_sqlite3_log(pParse->rc, "%s in \"%s\"", - *pzErrMsg, pParse->zTail); - pParse->zErrMsg = 0; + if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){ + if( pParse->zErrMsg==0 ){ + pParse->zErrMsg = wx_sqlite3MPrintf(db, "%s", wx_sqlite3ErrStr(pParse->rc)); + } + wx_sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail); nErr++; } pParse->zTail = zSql; - if( pParse->pVdbe && pParse->nErr>0 && pParse->nested==0 ){ - wx_sqlite3VdbeDelete(pParse->pVdbe); - pParse->pVdbe = 0; - } -#ifndef SQLITE_OMIT_SHARED_CACHE - if( pParse->nested==0 ){ - wx_sqlite3DbFree(db, pParse->aTableLock); - pParse->aTableLock = 0; - pParse->nTableLock = 0; - } -#endif #ifndef SQLITE_OMIT_VIRTUALTABLE wx_sqlite3_free(pParse->apVtabLock); #endif - if( !IN_SPECIAL_PARSE ){ + if( pParse->pNewTable && !IN_SPECIAL_PARSE ){ /* If the pParse->declareVtab flag is set, do not delete any table ** structure built up in pParse->pNewTable. The calling code (see vtab.c) ** will take responsibility for freeing the Table structure. */ wx_sqlite3DeleteTable(db, pParse->pNewTable); } - if( !IN_RENAME_OBJECT ){ + if( pParse->pNewTrigger && !IN_RENAME_OBJECT ){ wx_sqlite3DeleteTrigger(db, pParse->pNewTrigger); } - wx_sqlite3DbFree(db, pParse->pVList); - db->pParse = pParse->pParentParse; - pParse->pParentParse = 0; + if( pParse->pVList ) wx_sqlite3DbNNFreeNN(db, pParse->pVList); + db->pParse = pParentParse; assert( nErr==0 || pParse->rc!=SQLITE_OK ); return nErr; } @@ -164170,9 +174150,6 @@ SQLITE_PRIVATE int wx_sqlite3Fts2Init(wx_sqlite3*); #ifdef SQLITE_ENABLE_FTS5 SQLITE_PRIVATE int wx_sqlite3Fts5Init(wx_sqlite3*); #endif -#ifdef SQLITE_ENABLE_JSON1 -SQLITE_PRIVATE int wx_sqlite3Json1Init(wx_sqlite3*); -#endif #ifdef SQLITE_ENABLE_STMTVTAB SQLITE_PRIVATE int wx_sqlite3StmtVtabInit(wx_sqlite3*); #endif @@ -164207,8 +174184,8 @@ static int (*const wx_sqlite3BuiltinExtensions[])(wx_sqlite3*) = { wx_sqlite3DbstatRegister, #endif wx_sqlite3TestExtInit, -#ifdef SQLITE_ENABLE_JSON1 - wx_sqlite3Json1Init, +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) + wx_sqlite3JsonTableFunctions, #endif #ifdef SQLITE_ENABLE_STMTVTAB wx_sqlite3StmtVtabInit, @@ -164425,7 +174402,7 @@ SQLITE_API int wx_sqlite3_initialize(void){ wx_sqlite3GlobalConfig.isPCacheInit = 1; rc = wx_sqlite3OsInit(); } -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE if( rc==SQLITE_OK ){ rc = wx_sqlite3MemdbInit(); } @@ -164840,12 +174817,12 @@ SQLITE_API int wx_sqlite3_config(int op, ...){ } #endif /* SQLITE_ENABLE_SORTER_REFERENCES */ -#ifdef SQLITE_ENABLE_DESERIALIZE +#ifndef SQLITE_OMIT_DESERIALIZE case SQLITE_CONFIG_MEMDB_MAXSIZE: { wx_sqlite3GlobalConfig.mxMemdbSize = va_arg(ap, wx_sqlite3_int64); break; } -#endif /* SQLITE_ENABLE_DESERIALIZE */ +#endif /* SQLITE_OMIT_DESERIALIZE */ default: { rc = SQLITE_ERROR; @@ -164947,18 +174924,19 @@ static int setupLookaside(wx_sqlite3 *db, void *pBuf, int sz, int cnt){ db->lookaside.bMalloced = pBuf==0 ?1:0; db->lookaside.nSlot = nBig+nSm; }else{ - db->lookaside.pStart = db; + db->lookaside.pStart = 0; #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE db->lookaside.pSmallInit = 0; db->lookaside.pSmallFree = 0; - db->lookaside.pMiddle = db; + db->lookaside.pMiddle = 0; #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ - db->lookaside.pEnd = db; + db->lookaside.pEnd = 0; db->lookaside.bDisable = 1; db->lookaside.sz = 0; db->lookaside.bMalloced = 0; db->lookaside.nSlot = 0; } + db->lookaside.pTrueEnd = db->lookaside.pEnd; assert( wx_sqlite3LookasideUsed(db,0)==0 ); #endif /* SQLITE_OMIT_LOOKASIDE */ return SQLITE_OK; @@ -165037,6 +175015,7 @@ SQLITE_API int wx_sqlite3_db_cacheflush(wx_sqlite3 *db){ SQLITE_API int wx_sqlite3_db_config(wx_sqlite3 *db, int op, ...){ va_list ap; int rc; + wx_sqlite3_mutex_enter(db->mutex); va_start(ap, op); switch( op ){ case SQLITE_DBCONFIG_MAINDBNAME: { @@ -165102,6 +175081,7 @@ SQLITE_API int wx_sqlite3_db_config(wx_sqlite3 *db, int op, ...){ } } va_end(ap); + wx_sqlite3_mutex_leave(db->mutex); return rc; } @@ -165206,7 +175186,7 @@ SQLITE_API void wx_sqlite3_set_last_insert_rowid(wx_sqlite3 *db, wx_sqlite3_int6 /* ** Return the number of changes in the most recent call to wx_sqlite3_exec(). */ -SQLITE_API int wx_sqlite3_changes(wx_sqlite3 *db){ +SQLITE_API wx_sqlite3_int64 wx_sqlite3_changes64(wx_sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !wx_sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -165215,11 +175195,14 @@ SQLITE_API int wx_sqlite3_changes(wx_sqlite3 *db){ #endif return db->nChange; } +SQLITE_API int wx_sqlite3_changes(wx_sqlite3 *db){ + return (int)wx_sqlite3_changes64(db); +} /* ** Return the number of changes since the database handle was opened. */ -SQLITE_API int wx_sqlite3_total_changes(wx_sqlite3 *db){ +SQLITE_API wx_sqlite3_int64 wx_sqlite3_total_changes64(wx_sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !wx_sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -165228,6 +175211,9 @@ SQLITE_API int wx_sqlite3_total_changes(wx_sqlite3 *db){ #endif return db->nTotalChange; } +SQLITE_API int wx_sqlite3_total_changes(wx_sqlite3 *db){ + return (int)wx_sqlite3_total_changes64(db); +} /* ** Close all open savepoints. This function only manipulates fields of the @@ -165252,7 +175238,9 @@ SQLITE_PRIVATE void wx_sqlite3CloseSavepoints(wx_sqlite3 *db){ ** with SQLITE_ANY as the encoding. */ static void functionDestroy(wx_sqlite3 *db, FuncDef *p){ - FuncDestructor *pDestructor = p->u.pDestructor; + FuncDestructor *pDestructor; + assert( (p->funcFlags & SQLITE_FUNC_BUILTIN)==0 ); + pDestructor = p->u.pDestructor; if( pDestructor ){ pDestructor->nRef--; if( pDestructor->nRef==0 ){ @@ -165356,7 +175344,7 @@ static int wx_sqlite3Close(wx_sqlite3 *db, int forceZombie){ /* Convert the connection into a zombie and then close it. */ - db->magic = SQLITE_MAGIC_ZOMBIE; + db->eOpenState = SQLITE_STATE_ZOMBIE; wx_sqlite3LeaveMutexAndCloseZombie(db); return SQLITE_OK; } @@ -165394,7 +175382,7 @@ SQLITE_API int wx_sqlite3_txn_state(wx_sqlite3 *db, const char *zSchema){ /* ** Two variations on the public interface for closing a database ** connection. The wx_sqlite3_close() version returns SQLITE_BUSY and -** leaves the connection option if there are unfinalized prepared +** leaves the connection open if there are unfinalized prepared ** statements or unfinished wx_sqlite3_backups. The wx_sqlite3_close_v2() ** version forces the connection to become a zombie if there are ** unclosed resources, and arranges for deallocation when the last @@ -165420,7 +175408,7 @@ SQLITE_PRIVATE void wx_sqlite3LeaveMutexAndCloseZombie(wx_sqlite3 *db){ ** or if the connection has not yet been closed by wx_sqlite3_close_v2(), ** then just leave the mutex and return. */ - if( db->magic!=SQLITE_MAGIC_ZOMBIE || connectionIsBusy(db) ){ + if( db->eOpenState!=SQLITE_STATE_ZOMBIE || connectionIsBusy(db) ){ wx_sqlite3_mutex_leave(db->mutex); return; } @@ -165506,7 +175494,7 @@ SQLITE_PRIVATE void wx_sqlite3LeaveMutexAndCloseZombie(wx_sqlite3 *db){ wx_sqlite3_free(db->auth.zAuthPW); #endif - db->magic = SQLITE_MAGIC_ERROR; + db->eOpenState = SQLITE_STATE_ERROR; /* The temp-database schema is allocated differently from the other schema ** objects (using sqliteMalloc() directly, instead of wx_sqlite3BtreeSchema()). @@ -165515,8 +175503,11 @@ SQLITE_PRIVATE void wx_sqlite3LeaveMutexAndCloseZombie(wx_sqlite3 *db){ ** structure? */ wx_sqlite3DbFree(db, db->aDb[1].pSchema); + if( db->xAutovacDestr ){ + db->xAutovacDestr(db->pAutovacPagesArg); + } wx_sqlite3_mutex_leave(db->mutex); - db->magic = SQLITE_MAGIC_CLOSED; + db->eOpenState = SQLITE_STATE_CLOSED; wx_sqlite3_mutex_free(db->mutex); assert( wx_sqlite3LookasideUsed(db,0)==0 ); if( db->lookaside.bMalloced ){ @@ -165569,7 +175560,7 @@ SQLITE_PRIVATE void wx_sqlite3RollbackAll(wx_sqlite3 *db, int tripCode){ /* Any deferred constraint violations have now been resolved. */ db->nDeferredCons = 0; db->nDeferredImmCons = 0; - db->flags &= ~(u64)SQLITE_DeferFKs; + db->flags &= ~(u64)(SQLITE_DeferFKs|SQLITE_CorruptRdOnly); /* If one has been configured, invoke the rollback-hook callback */ if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){ @@ -165675,6 +175666,7 @@ SQLITE_PRIVATE const char *wx_sqlite3ErrName(int rc){ case SQLITE_NOTICE_RECOVER_WAL: zName = "SQLITE_NOTICE_RECOVER_WAL";break; case SQLITE_NOTICE_RECOVER_ROLLBACK: zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break; + case SQLITE_NOTICE_RBU: zName = "SQLITE_NOTICE_RBU"; break; case SQLITE_WARNING: zName = "SQLITE_WARNING"; break; case SQLITE_WARNING_AUTOINDEX: zName = "SQLITE_WARNING_AUTOINDEX"; break; case SQLITE_DONE: zName = "SQLITE_DONE"; break; @@ -165904,7 +175896,9 @@ SQLITE_API int wx_sqlite3_busy_timeout(wx_sqlite3 *db, int ms){ */ SQLITE_API void wx_sqlite3_interrupt(wx_sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR - if( !wx_sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){ + if( !wx_sqlite3SafetyCheckOk(db) + && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE) + ){ (void)SQLITE_MISUSE_BKPT; return; } @@ -165912,6 +175906,21 @@ SQLITE_API void wx_sqlite3_interrupt(wx_sqlite3 *db){ AtomicStore(&db->u1.isInterrupted, 1); } +/* +** Return true or false depending on whether or not an interrupt is +** pending on connection db. +*/ +SQLITE_API int wx_sqlite3_is_interrupted(wx_sqlite3 *db){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !wx_sqlite3SafetyCheckOk(db) + && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE) + ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif + return AtomicLoad(&db->u1.isInterrupted)!=0; +} /* ** This function is exactly the same as wx_sqlite3_create_function(), except @@ -165933,7 +175942,6 @@ SQLITE_PRIVATE int wx_sqlite3CreateFunc( FuncDestructor *pDestructor ){ FuncDef *p; - int nName; int extraFlags; assert( wx_sqlite3_mutex_held(db->mutex) ); @@ -165943,7 +175951,7 @@ SQLITE_PRIVATE int wx_sqlite3CreateFunc( || ((xFinal==0)!=(xStep==0)) /* Both or neither of xFinal and xStep */ || ((xValue==0)!=(xInverse==0)) /* Both or neither of xValue, xInverse */ || (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) - || (255<(nName = wx_sqlite3Strlen30( zFunctionName))) + || (255nRef==0 ){ - assert( rc!=SQLITE_OK ); + assert( rc!=SQLITE_OK || (xStep==0 && xFinal==0) ); xDestroy(p); wx_sqlite3_free(pArg); } @@ -166213,7 +176236,7 @@ SQLITE_API int wx_sqlite3_overload_function( rc = wx_sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)!=0; wx_sqlite3_mutex_leave(db->mutex); if( rc ) return SQLITE_OK; - zCopy = wx_sqlite3_mprintf(zName); + zCopy = wx_sqlite3_mprintf("%s", zName); if( zCopy==0 ) return SQLITE_NOMEM; return wx_sqlite3_create_function_v2(db, zName, nArg, SQLITE_UTF8, zCopy, wx_sqlite3InvalidFunction, 0, 0, wx_sqlite3_free); @@ -166402,6 +176425,34 @@ SQLITE_API void *wx_sqlite3_preupdate_hook( } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +/* +** Register a function to be invoked prior to each autovacuum that +** determines the number of pages to vacuum. +*/ +SQLITE_API int wx_sqlite3_autovacuum_pages( + wx_sqlite3 *db, /* Attach the hook to this database */ + unsigned int (*xCallback)(void*,const char*,u32,u32,u32), + void *pArg, /* Argument to the function */ + void (*xDestructor)(void*) /* Destructor for pArg */ +){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !wx_sqlite3SafetyCheckOk(db) ){ + if( xDestructor ) xDestructor(pArg); + return SQLITE_MISUSE_BKPT; + } +#endif + wx_sqlite3_mutex_enter(db->mutex); + if( db->xAutovacDestr ){ + db->xAutovacDestr(db->pAutovacPagesArg); + } + db->xAutovacPages = xCallback; + db->pAutovacPagesArg = pArg; + db->xAutovacDestr = xDestructor; + wx_sqlite3_mutex_leave(db->mutex); + return SQLITE_OK; +} + + #ifndef SQLITE_OMIT_WAL /* ** The wx_sqlite3_wal_hook() callback registered by wx_sqlite3_wal_autocheckpoint(). @@ -166664,6 +176715,19 @@ SQLITE_API const char *wx_sqlite3_errmsg(wx_sqlite3 *db){ return z; } +/* +** Return the byte offset of the most recent error +*/ +SQLITE_API int wx_sqlite3_error_offset(wx_sqlite3 *db){ + int iOffset = -1; + if( db && wx_sqlite3SafetyCheckSickOrOk(db) && db->errCode ){ + wx_sqlite3_mutex_enter(db->mutex); + iOffset = db->errByteOffset; + wx_sqlite3_mutex_leave(db->mutex); + } + return iOffset; +} + #ifndef SQLITE_OMIT_UTF16 /* ** Return UTF-16 encoded English language explanation of the most recent @@ -166924,6 +176988,8 @@ SQLITE_API int wx_sqlite3_limit(wx_sqlite3 *db, int limitId, int newLimit){ if( newLimit>=0 ){ /* IMP: R-52476-28732 */ if( newLimit>aHardLimit[limitId] ){ newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ + }else if( newLimit<1 && limitId==SQLITE_LIMIT_LENGTH ){ + newLimit = 1; } db->aLimit[limitId] = newLimit; } @@ -167174,6 +177240,9 @@ SQLITE_PRIVATE int wx_sqlite3ParseUri( flags &= ~SQLITE_OPEN_URI; } + /* Check VFS. */ + wx_sqlite3mcCheckVfs(zVfs); + *ppVfs = wx_sqlite3_vfs_find(zVfs); if( *ppVfs==0 ){ *pzErrMsg = wx_sqlite3_mprintf("no such vfs: %s", zVfs); @@ -167195,7 +177264,7 @@ SQLITE_PRIVATE int wx_sqlite3ParseUri( */ static const char *uriParameter(const char *zFilename, const char *zParam){ zFilename += wx_sqlite3Strlen30(zFilename) + 1; - while( zFilename[0] ){ + while( ALWAYS(zFilename!=0) && zFilename[0] ){ int x = strcmp(zFilename, zParam); zFilename += wx_sqlite3Strlen30(zFilename) + 1; if( x==0 ) return zFilename; @@ -167255,8 +177324,8 @@ static int openDatabase( ** dealt with in the previous code block. Besides these, the only ** valid input flags for wx_sqlite3_open_v2() are SQLITE_OPEN_READONLY, ** SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, SQLITE_OPEN_SHAREDCACHE, - ** SQLITE_OPEN_PRIVATECACHE, and some reserved bits. Silently mask - ** off all other flags. + ** SQLITE_OPEN_PRIVATECACHE, SQLITE_OPEN_EXRESCODE, and some reserved + ** bits. Silently mask off all other flags. */ flags &= ~( SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_EXCLUSIVE | @@ -167291,9 +177360,9 @@ static int openDatabase( } } wx_sqlite3_mutex_enter(db->mutex); - db->errMask = 0xff; + db->errMask = (flags & SQLITE_OPEN_EXRESCODE)!=0 ? 0xffffffff : 0xff; db->nDb = 2; - db->magic = SQLITE_MAGIC_BUSY; + db->eOpenState = SQLITE_STATE_BUSY; db->aDb = db->aDbStatic; db->lookaside.bDisable = 1; db->lookaside.sz = 0; @@ -167305,7 +177374,15 @@ static int openDatabase( db->nextAutovac = -1; db->szMmap = wx_sqlite3GlobalConfig.szMmap; db->nextPagesize = 0; + db->init.azInit = wx_sqlite3StdType; /* Any array of string ptrs will do */ +#ifdef SQLITE_ENABLE_SORTER_MMAP + /* Beginning with version 3.37.0, using the VFS xFetch() API to memory-map + ** the temporary files used to do external sorts (see code in vdbesort.c) + ** is disabled. It can still be used either by defining + ** SQLITE_ENABLE_SORTER_MMAP at compile time or by using the + ** SQLITE_TESTCTRL_SORTER_MMAP test-control at runtime. */ db->nMaxSorterMmap = 0x7FFFFFFF; +#endif db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_EnableView @@ -167396,6 +177473,19 @@ static int openDatabase( goto opendb_out; } +#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) + /* Process magic filenames ":localStorage:" and ":sessionStorage:" */ + if( zFilename && zFilename[0]==':' ){ + if( strcmp(zFilename, ":localStorage:")==0 ){ + zFilename = "file:local?vfs=kvvfs"; + flags |= SQLITE_OPEN_URI; + }else if( strcmp(zFilename, ":sessionStorage:")==0 ){ + zFilename = "file:session?vfs=kvvfs"; + flags |= SQLITE_OPEN_URI; + } + } +#endif /* SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) */ + /* Parse the filename/URI argument ** ** Only allow sensible combinations of bits in the flags argument. @@ -167426,6 +177516,12 @@ static int openDatabase( wx_sqlite3_free(zErrMsg); goto opendb_out; } + assert( db->pVfs!=0 ); +#if SQLITE_OS_KV || defined(SQLITE_OS_KV_OPTIONAL) + if( wx_sqlite3_stricmp(db->pVfs->zName, "kvvfs")==0 ){ + db->temp_store = 2; + } +#endif /* Open the backend database driver */ rc = wx_sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0, @@ -167453,7 +177549,7 @@ static int openDatabase( db->aDb[1].zDbSName = "temp"; db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF; - db->magic = SQLITE_MAGIC_OPEN; + db->eOpenState = SQLITE_STATE_OPEN; if( db->mallocFailed ){ goto opendb_out; } @@ -167515,12 +177611,12 @@ opendb_out: wx_sqlite3_mutex_leave(db->mutex); } rc = wx_sqlite3_errcode(db); - assert( db!=0 || rc==SQLITE_NOMEM ); - if( rc==SQLITE_NOMEM ){ + assert( db!=0 || (rc&0xff)==SQLITE_NOMEM ); + if( (rc&0xff)==SQLITE_NOMEM ){ wx_sqlite3_close(db); db = 0; }else if( rc!=SQLITE_OK ){ - db->magic = SQLITE_MAGIC_SICK; + db->eOpenState = SQLITE_STATE_SICK; } *ppDb = db; #ifdef SQLITE_ENABLE_SQLLOG @@ -167536,7 +177632,7 @@ opendb_out: rc = wx_sqlite3mcHandleMainKey(db, zOpen); } wx_sqlite3_free_filename(zOpen); - return rc & 0xff; + return rc; } @@ -167836,7 +177932,7 @@ SQLITE_API int wx_sqlite3_table_column_metadata( /* Locate the table in question */ pTab = wx_sqlite3FindTable(db, zTableName, zDbName); - if( !pTab || pTab->pSelect ){ + if( !pTab || IsView(pTab) ){ pTab = 0; goto error_out; } @@ -167847,7 +177943,7 @@ SQLITE_API int wx_sqlite3_table_column_metadata( }else{ for(iCol=0; iColnCol; iCol++){ pCol = &pTab->aCol[iCol]; - if( 0==wx_sqlite3StrICmp(pCol->zName, zColumnName) ){ + if( 0==wx_sqlite3StrICmp(pCol->zCnName, zColumnName) ){ break; } } @@ -167874,7 +177970,7 @@ SQLITE_API int wx_sqlite3_table_column_metadata( */ if( pCol ){ zDataType = wx_sqlite3ColumnType(pCol,0); - zCollSeq = pCol->zColl; + zCollSeq = wx_sqlite3ColumnColl(pCol); notnull = pCol->notNull!=0; primarykey = (pCol->colFlags & COLFLAG_PRIMKEY)!=0; autoinc = pTab->iPKey==iCol && (pTab->tabFlags & TF_Autoincrement)!=0; @@ -167980,6 +178076,9 @@ SQLITE_API int wx_sqlite3_file_control(wx_sqlite3 *db, const char *zDbName, int wx_sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0); } rc = SQLITE_OK; + }else if( op==SQLITE_FCNTL_RESET_CACHE ){ + wx_sqlite3BtreeClearCache(pBtree); + rc = SQLITE_OK; }else{ int nSave = db->busyHandler.nBusy; rc = wx_sqlite3OsFileControl(fd, op, pArg); @@ -168081,12 +178180,16 @@ SQLITE_API int wx_sqlite3_test_control(int op, ...){ ** wx_sqlite3_test_control(). */ case SQLITE_TESTCTRL_FAULT_INSTALL: { - /* MSVC is picky about pulling func ptrs from va lists. - ** http://support.microsoft.com/kb/47961 + /* A bug in MSVC prevents it from understanding pointers to functions + ** types in the second argument to va_arg(). Work around the problem + ** using a typedef. + ** http://support.microsoft.com/kb/47961 <-- dead hyperlink + ** Search at http://web.archive.org/ to find the 2015-03-16 archive + ** of the link above to see the original text. ** wx_sqlite3GlobalConfig.xTestCallback = va_arg(ap, int(*)(int)); */ - typedef int(*TESTCALLBACKFUNC_t)(int); - wx_sqlite3GlobalConfig.xTestCallback = va_arg(ap, TESTCALLBACKFUNC_t); + typedef int(*wx_sqlite3FaultFuncType)(int); + wx_sqlite3GlobalConfig.xTestCallback = va_arg(ap, wx_sqlite3FaultFuncType); rc = wx_sqlite3FaultSim(0); break; } @@ -168145,6 +178248,28 @@ SQLITE_API int wx_sqlite3_test_control(int op, ...){ volatile int x = 0; assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 ); rc = x; +#if defined(SQLITE_DEBUG) + /* Invoke these debugging routines so that the compiler does not + ** issue "defined but not used" warnings. */ + if( x==9999 ){ + wx_sqlite3ShowExpr(0); + wx_sqlite3ShowExpr(0); + wx_sqlite3ShowExprList(0); + wx_sqlite3ShowIdList(0); + wx_sqlite3ShowSrcList(0); + wx_sqlite3ShowWith(0); + wx_sqlite3ShowUpsert(0); + wx_sqlite3ShowTriggerStep(0); + wx_sqlite3ShowTriggerStepList(0); + wx_sqlite3ShowTrigger(0); + wx_sqlite3ShowTriggerList(0); +#ifndef SQLITE_OMIT_WINDOWFUNC + wx_sqlite3ShowWindow(0); + wx_sqlite3ShowWinFunc(0); +#endif + wx_sqlite3ShowSelect(0); + } +#endif break; } @@ -168213,13 +178338,27 @@ SQLITE_API int wx_sqlite3_test_control(int op, ...){ break; } - /* wx_sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff); + /* wx_sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, onoff, xAlt); + ** + ** If parameter onoff is 1, subsequent calls to localtime() fail. + ** If 2, then invoke xAlt() instead of localtime(). If 0, normal + ** processing. + ** + ** xAlt arguments are void pointers, but they really want to be: + ** + ** int xAlt(const time_t*, struct tm*); ** - ** If parameter onoff is non-zero, subsequent calls to localtime() - ** and its variants fail. If onoff is zero, undo this setting. + ** xAlt should write results in to struct tm object of its 2nd argument + ** and return zero on success, or return non-zero on failure. */ case SQLITE_TESTCTRL_LOCALTIME_FAULT: { wx_sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int); + if( wx_sqlite3GlobalConfig.bLocaltimeFault==2 ){ + typedef int(*wx_sqlite3LocaltimeType)(const void*,void*); + wx_sqlite3GlobalConfig.xAltLocaltime = va_arg(ap, wx_sqlite3LocaltimeType); + }else{ + wx_sqlite3GlobalConfig.xAltLocaltime = 0; + } break; } @@ -168324,12 +178463,16 @@ SQLITE_API int wx_sqlite3_test_control(int op, ...){ */ case SQLITE_TESTCTRL_IMPOSTER: { wx_sqlite3 *db = va_arg(ap, wx_sqlite3*); + int iDb; wx_sqlite3_mutex_enter(db->mutex); - db->init.iDb = wx_sqlite3FindDbName(db, va_arg(ap,const char*)); - db->init.busy = db->init.imposterTable = va_arg(ap,int); - db->init.newTnum = va_arg(ap,int); - if( db->init.busy==0 && db->init.newTnum>0 ){ - wx_sqlite3ResetAllSchemasOfConnection(db); + iDb = wx_sqlite3FindDbName(db, va_arg(ap,const char*)); + if( iDb>=0 ){ + db->init.iDb = iDb; + db->init.busy = db->init.imposterTable = va_arg(ap,int); + db->init.newTnum = va_arg(ap,int); + if( db->init.busy==0 && db->init.newTnum>0 ){ + wx_sqlite3ResetAllSchemasOfConnection(db); + } } wx_sqlite3_mutex_leave(db->mutex); break; @@ -168388,8 +178531,8 @@ SQLITE_API int wx_sqlite3_test_control(int op, ...){ ** ** "ptr" is a pointer to a u32. ** - ** op==0 Store the current wx_sqlite3SelectTrace in *ptr - ** op==1 Set wx_sqlite3SelectTrace to the value *ptr + ** op==0 Store the current wx_sqlite3TreeTrace in *ptr + ** op==1 Set wx_sqlite3TreeTrace to the value *ptr ** op==3 Store the current wx_sqlite3WhereTrace in *ptr ** op==3 Set wx_sqlite3WhereTrace to the value *ptr */ @@ -168397,13 +178540,65 @@ SQLITE_API int wx_sqlite3_test_control(int op, ...){ int opTrace = va_arg(ap, int); u32 *ptr = va_arg(ap, u32*); switch( opTrace ){ - case 0: *ptr = wx_sqlite3SelectTrace; break; - case 1: wx_sqlite3SelectTrace = *ptr; break; - case 2: *ptr = wx_sqlite3WhereTrace; break; - case 3: wx_sqlite3WhereTrace = *ptr; break; + case 0: *ptr = wx_sqlite3TreeTrace; break; + case 1: wx_sqlite3TreeTrace = *ptr; break; + case 2: *ptr = wx_sqlite3WhereTrace; break; + case 3: wx_sqlite3WhereTrace = *ptr; break; } break; } + + /* wx_sqlite3_test_control(SQLITE_TESTCTRL_LOGEST, + ** double fIn, // Input value + ** int *pLogEst, // wx_sqlite3LogEstFromDouble(fIn) + ** u64 *pInt, // wx_sqlite3LogEstToInt(*pLogEst) + ** int *pLogEst2 // wx_sqlite3LogEst(*pInt) + ** ); + ** + ** Test access for the LogEst conversion routines. + */ + case SQLITE_TESTCTRL_LOGEST: { + double rIn = va_arg(ap, double); + LogEst rLogEst = wx_sqlite3LogEstFromDouble(rIn); + int *pI1 = va_arg(ap,int*); + u64 *pU64 = va_arg(ap,u64*); + int *pI2 = va_arg(ap,int*); + *pI1 = rLogEst; + *pU64 = wx_sqlite3LogEstToInt(rLogEst); + *pI2 = wx_sqlite3LogEst(*pU64); + break; + } + + +#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) + /* wx_sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue) + ** + ** If "id" is an integer between 1 and SQLITE_NTUNE then set the value + ** of the id-th tuning parameter to *piValue. If "id" is between -1 + ** and -SQLITE_NTUNE, then write the current value of the (-id)-th + ** tuning parameter into *piValue. + ** + ** Tuning parameters are for use during transient development builds, + ** to help find the best values for constants in the query planner. + ** Access tuning parameters using the Tuning(ID) macro. Set the + ** parameters in the CLI using ".testctrl tune ID VALUE". + ** + ** Transient use only. Tuning parameters should not be used in + ** checked-in code. + */ + case SQLITE_TESTCTRL_TUNE: { + int id = va_arg(ap, int); + int *piValue = va_arg(ap, int*); + if( id>0 && id<=SQLITE_NTUNE ){ + Tuning(id) = *piValue; + }else if( id<0 && id>=-SQLITE_NTUNE ){ + *piValue = Tuning(-id); + }else{ + rc = SQLITE_NOTFOUND; + } + break; + } +#endif } va_end(ap); #endif /* SQLITE_UNTESTABLE */ @@ -168444,7 +178639,7 @@ static char *appendText(char *p, const char *z){ ** Memory layout must be compatible with that generated by the pager ** and expected by wx_sqlite3_uri_parameter() and databaseName(). */ -SQLITE_API char *wx_sqlite3_create_filename( +SQLITE_API const char *wx_sqlite3_create_filename( const char *zDatabase, const char *zJournal, const char *zWal, @@ -168480,10 +178675,10 @@ SQLITE_API char *wx_sqlite3_create_filename( ** error to call this routine with any parameter other than a pointer ** previously obtained from wx_sqlite3_create_filename() or a NULL pointer. */ -SQLITE_API void wx_sqlite3_free_filename(char *p){ +SQLITE_API void wx_sqlite3_free_filename(const char *p){ if( p==0 ) return; - p = (char*)databaseName(p); - wx_sqlite3_free(p - 4); + p = databaseName(p); + wx_sqlite3_free((char*)p - 4); } @@ -168511,7 +178706,7 @@ SQLITE_API const char *wx_sqlite3_uri_key(const char *zFilename, int N){ if( zFilename==0 || N<0 ) return 0; zFilename = databaseName(zFilename); zFilename += wx_sqlite3Strlen30(zFilename) + 1; - while( zFilename[0] && (N--)>0 ){ + while( ALWAYS(zFilename) && zFilename[0] && (N--)>0 ){ zFilename += wx_sqlite3Strlen30(zFilename) + 1; zFilename += wx_sqlite3Strlen30(zFilename) + 1; } @@ -168554,12 +178749,14 @@ SQLITE_API wx_sqlite3_int64 wx_sqlite3_uri_int64( ** corruption. */ SQLITE_API const char *wx_sqlite3_filename_database(const char *zFilename){ + if( zFilename==0 ) return 0; return databaseName(zFilename); } SQLITE_API const char *wx_sqlite3_filename_journal(const char *zFilename){ + if( zFilename==0 ) return 0; zFilename = databaseName(zFilename); zFilename += wx_sqlite3Strlen30(zFilename) + 1; - while( zFilename[0] ){ + while( ALWAYS(zFilename) && zFilename[0] ){ zFilename += wx_sqlite3Strlen30(zFilename) + 1; zFilename += wx_sqlite3Strlen30(zFilename) + 1; } @@ -168570,7 +178767,7 @@ SQLITE_API const char *wx_sqlite3_filename_wal(const char *zFilename){ return 0; #else zFilename = wx_sqlite3_filename_journal(zFilename); - zFilename += wx_sqlite3Strlen30(zFilename) + 1; + if( zFilename ) zFilename += wx_sqlite3Strlen30(zFilename) + 1; return zFilename; #endif } @@ -168583,6 +178780,24 @@ SQLITE_PRIVATE Btree *wx_sqlite3DbNameToBtree(wx_sqlite3 *db, const char *zDbNam return iDb<0 ? 0 : db->aDb[iDb].pBt; } +/* +** Return the name of the N-th database schema. Return NULL if N is out +** of range. +*/ +SQLITE_API const char *wx_sqlite3_db_name(wx_sqlite3 *db, int N){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !wx_sqlite3SafetyCheckOk(db) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif + if( N<0 || N>=db->nDb ){ + return 0; + }else{ + return db->aDb[N].zDbSName; + } +} + /* ** Return the filename of the database associated with a database ** connection. @@ -168714,8 +178929,8 @@ SQLITE_API int wx_sqlite3_snapshot_open( */ SQLITE_API int wx_sqlite3_snapshot_recover(wx_sqlite3 *db, const char *zDb){ int rc = SQLITE_ERROR; - int iDb; #ifndef SQLITE_OMIT_WAL + int iDb; #ifdef SQLITE_ENABLE_API_ARMOR if( !wx_sqlite3SafetyCheckOk(db) ){ @@ -169846,7 +180061,7 @@ SQLITE_PRIVATE Fts3HashElem *wx_sqlite3Fts3HashFindElem(const Fts3Hash *, const ** is used for assert() conditions that are true only if it can be ** guranteed that the database is not corrupt. */ -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) +#ifdef SQLITE_DEBUG SQLITE_API extern int wx_sqlite3_fts3_may_be_corrupt; # define assert_fts3_nc(x) assert(wx_sqlite3_fts3_may_be_corrupt || (x)) #else @@ -169863,17 +180078,18 @@ SQLITE_API extern int wx_sqlite3_fts3_may_be_corrupt; ** Macros indicating that conditional expressions are always true or ** false. */ -#ifdef SQLITE_COVERAGE_TEST -# define ALWAYS(x) (1) -# define NEVER(X) (0) -#elif defined(SQLITE_DEBUG) -# define ALWAYS(x) wx_sqlite3Fts3Always((x)!=0) -# define NEVER(x) wx_sqlite3Fts3Never((x)!=0) -SQLITE_PRIVATE int wx_sqlite3Fts3Always(int b); -SQLITE_PRIVATE int wx_sqlite3Fts3Never(int b); +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 +#endif +#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) +# 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) +# define ALWAYS(X) (X) +# define NEVER(X) (X) #endif /* @@ -170269,7 +180485,7 @@ struct Fts3MultiSegReader { int nAdvance; /* How many seg-readers to advance */ Fts3SegFilter *pFilter; /* Pointer to filter object */ char *aBuffer; /* Buffer to merge doclists in */ - int nBuffer; /* Allocated size of aBuffer[] in bytes */ + i64 nBuffer; /* Allocated size of aBuffer[] in bytes */ int iColFilter; /* If >=0, filter for this column */ int bRestart; @@ -170332,6 +180548,7 @@ SQLITE_PRIVATE void wx_sqlite3Fts3ExprFree(Fts3Expr *); SQLITE_PRIVATE int wx_sqlite3Fts3ExprInitTestInterface(wx_sqlite3 *db, Fts3Hash*); SQLITE_PRIVATE int wx_sqlite3Fts3InitTerm(wx_sqlite3 *db); #endif +SQLITE_PRIVATE void *wx_sqlite3Fts3MallocZero(i64 nByte); SQLITE_PRIVATE int wx_sqlite3Fts3OpenTokenizer(wx_sqlite3_tokenizer *, int, const char *, int, wx_sqlite3_tokenizer_cursor ** @@ -170351,7 +180568,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int SQLITE_PRIVATE int wx_sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr); /* fts3_tokenize_vtab.c */ -SQLITE_PRIVATE int wx_sqlite3Fts3InitTok(wx_sqlite3*, Fts3Hash *); +SQLITE_PRIVATE int wx_sqlite3Fts3InitTok(wx_sqlite3*, Fts3Hash *, void(*xDestroy)(void*)); /* fts3_unicode2.c (functions generated by parsing unicode text files) */ #ifndef SQLITE_DISABLE_FTS3_UNICODE @@ -170360,6 +180577,8 @@ SQLITE_PRIVATE int wx_sqlite3FtsUnicodeIsalnum(int); SQLITE_PRIVATE int wx_sqlite3FtsUnicodeIsdiacritic(int); #endif +SQLITE_PRIVATE int wx_sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*); + #endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */ #endif /* _FTSINT_H */ @@ -170384,25 +180603,26 @@ SQLITE_PRIVATE int wx_sqlite3FtsUnicodeIsdiacritic(int); SQLITE_EXTENSION_INIT1 #endif +typedef struct Fts3HashWrapper Fts3HashWrapper; +struct Fts3HashWrapper { + Fts3Hash hash; /* Hash table */ + int nRef; /* Number of pointers to this object */ +}; + static int fts3EvalNext(Fts3Cursor *pCsr); static int fts3EvalStart(Fts3Cursor *pCsr); static int fts3TermSegReaderCursor( Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **); -#ifndef SQLITE_AMALGAMATION -# if defined(SQLITE_DEBUG) -SQLITE_PRIVATE int wx_sqlite3Fts3Always(int b) { assert( b ); return b; } -SQLITE_PRIVATE int wx_sqlite3Fts3Never(int b) { assert( !b ); return b; } -# endif -#endif - /* ** This variable is set to false when running tests for which the on disk ** structures should not be corrupt. Otherwise, true. If it is false, extra ** assert() conditions in the fts3 code are activated - conditions that are ** only true if it is guaranteed that the fts3 database is not corrupt. */ +#ifdef SQLITE_DEBUG SQLITE_API int wx_sqlite3_fts3_may_be_corrupt = 1; +#endif /* ** Write a 64-bit variable-length integer to memory starting at p[0]. @@ -171253,7 +181473,7 @@ static int fts3InitVtab( wx_sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ char **pzErr /* Write any error message here */ ){ - Fts3Hash *pHash = (Fts3Hash *)pAux; + Fts3Hash *pHash = &((Fts3HashWrapper*)pAux)->hash; Fts3Table *p = 0; /* Pointer to allocated vtab */ int rc = SQLITE_OK; /* Return code */ int i; /* Iterator variable */ @@ -171973,7 +182193,7 @@ static int fts3ScanInteriorNode( char *zBuffer = 0; /* Buffer to load terms into */ i64 nAlloc = 0; /* Size of allocated buffer */ int isFirstTerm = 1; /* True when processing first term on page */ - wx_sqlite3_int64 iChild; /* Block id of child node to descend to */ + u64 iChild; /* Block id of child node to descend to */ int nBuffer = 0; /* Total term size */ /* Skip over the 'height' varint that occurs at the start of every @@ -171989,8 +182209,8 @@ static int fts3ScanInteriorNode( ** table, then there are always 20 bytes of zeroed padding following the ** nNode bytes of content (see wx_sqlite3Fts3ReadBlock() for details). */ - zCsr += wx_sqlite3Fts3GetVarint(zCsr, &iChild); - zCsr += wx_sqlite3Fts3GetVarint(zCsr, &iChild); + zCsr += wx_sqlite3Fts3GetVarintU(zCsr, &iChild); + zCsr += wx_sqlite3Fts3GetVarintU(zCsr, &iChild); if( zCsr>zEnd ){ return FTS_CORRUPT_VTAB; } @@ -172043,20 +182263,20 @@ static int fts3ScanInteriorNode( */ cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer)); if( piFirst && (cmp<0 || (cmp==0 && nBuffer>nTerm)) ){ - *piFirst = iChild; + *piFirst = (i64)iChild; piFirst = 0; } if( piLast && cmp<0 ){ - *piLast = iChild; + *piLast = (i64)iChild; piLast = 0; } iChild++; }; - if( piFirst ) *piFirst = iChild; - if( piLast ) *piLast = iChild; + if( piFirst ) *piFirst = (i64)iChild; + if( piLast ) *piLast = (i64)iChild; finish_scan: wx_sqlite3_free(zBuffer); @@ -172963,7 +183183,7 @@ static int fts3TermSelectMerge( ** ** Similar padding is added in the fts3DoclistOrMerge() function. */ - pTS->aaOutput[0] = wx_sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1); + pTS->aaOutput[0] = wx_sqlite3_malloc64((i64)nDoclist + FTS3_VARINT_MAX + 1); pTS->anOutput[0] = nDoclist; if( pTS->aaOutput[0] ){ memcpy(pTS->aaOutput[0], aDoclist, nDoclist); @@ -173662,14 +183882,20 @@ static int fts3SetHasStat(Fts3Table *p){ */ static int fts3BeginMethod(wx_sqlite3_vtab *pVtab){ Fts3Table *p = (Fts3Table*)pVtab; + int rc; UNUSED_PARAMETER(pVtab); assert( p->pSegments==0 ); assert( p->nPendingData==0 ); assert( p->inTransaction!=1 ); - TESTONLY( p->inTransaction = 1 ); - TESTONLY( p->mxSavepoint = -1; ); p->nLeafAdd = 0; - return fts3SetHasStat(p); + rc = fts3SetHasStat(p); +#ifdef SQLITE_DEBUG + if( rc==SQLITE_OK ){ + p->inTransaction = 1; + p->mxSavepoint = -1; + } +#endif + return rc; } /* @@ -174082,9 +184308,12 @@ static const wx_sqlite3_module fts3Module = { ** allocated for the tokenizer hash table. */ static void hashDestroy(void *p){ - Fts3Hash *pHash = (Fts3Hash *)p; - wx_sqlite3Fts3HashClear(pHash); - wx_sqlite3_free(pHash); + Fts3HashWrapper *pHash = (Fts3HashWrapper *)p; + pHash->nRef--; + if( pHash->nRef<=0 ){ + wx_sqlite3Fts3HashClear(&pHash->hash); + wx_sqlite3_free(pHash); + } } /* @@ -174114,7 +184343,7 @@ SQLITE_PRIVATE void wx_sqlite3Fts3IcuTokenizerModule(wx_sqlite3_tokenizer_module */ SQLITE_PRIVATE int wx_sqlite3Fts3Init(wx_sqlite3 *db){ int rc = SQLITE_OK; - Fts3Hash *pHash = 0; + Fts3HashWrapper *pHash = 0; const wx_sqlite3_tokenizer_module *pSimple = 0; const wx_sqlite3_tokenizer_module *pPorter = 0; #ifndef SQLITE_DISABLE_FTS3_UNICODE @@ -174142,23 +184371,24 @@ SQLITE_PRIVATE int wx_sqlite3Fts3Init(wx_sqlite3 *db){ wx_sqlite3Fts3PorterTokenizerModule(&pPorter); /* Allocate and initialize the hash-table used to store tokenizers. */ - pHash = wx_sqlite3_malloc(sizeof(Fts3Hash)); + pHash = wx_sqlite3_malloc(sizeof(Fts3HashWrapper)); if( !pHash ){ rc = SQLITE_NOMEM; }else{ - wx_sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); + wx_sqlite3Fts3HashInit(&pHash->hash, FTS3_HASH_STRING, 1); + pHash->nRef = 0; } /* Load the built-in tokenizers into the hash table */ if( rc==SQLITE_OK ){ - if( wx_sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple) - || wx_sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter) + if( wx_sqlite3Fts3HashInsert(&pHash->hash, "simple", 7, (void *)pSimple) + || wx_sqlite3Fts3HashInsert(&pHash->hash, "porter", 7, (void *)pPorter) #ifndef SQLITE_DISABLE_FTS3_UNICODE - || wx_sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode) + || wx_sqlite3Fts3HashInsert(&pHash->hash, "unicode61", 10, (void *)pUnicode) #endif #ifdef SQLITE_ENABLE_ICU - || (pIcu && wx_sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu)) + || (pIcu && wx_sqlite3Fts3HashInsert(&pHash->hash, "icu", 4, (void *)pIcu)) #endif ){ rc = SQLITE_NOMEM; @@ -174167,7 +184397,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3Init(wx_sqlite3 *db){ #ifdef SQLITE_TEST if( rc==SQLITE_OK ){ - rc = wx_sqlite3Fts3ExprInitTestInterface(db, pHash); + rc = wx_sqlite3Fts3ExprInitTestInterface(db, &pHash->hash); } #endif @@ -174176,23 +184406,26 @@ SQLITE_PRIVATE int wx_sqlite3Fts3Init(wx_sqlite3 *db){ ** module with sqlite. */ if( SQLITE_OK==rc - && SQLITE_OK==(rc = wx_sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) + && SQLITE_OK==(rc=wx_sqlite3Fts3InitHashTable(db,&pHash->hash,"fts3_tokenizer")) && SQLITE_OK==(rc = wx_sqlite3_overload_function(db, "snippet", -1)) && SQLITE_OK==(rc = wx_sqlite3_overload_function(db, "offsets", 1)) && SQLITE_OK==(rc = wx_sqlite3_overload_function(db, "matchinfo", 1)) && SQLITE_OK==(rc = wx_sqlite3_overload_function(db, "matchinfo", 2)) && SQLITE_OK==(rc = wx_sqlite3_overload_function(db, "optimize", 1)) ){ + pHash->nRef++; rc = wx_sqlite3_create_module_v2( db, "fts3", &fts3Module, (void *)pHash, hashDestroy ); if( rc==SQLITE_OK ){ + pHash->nRef++; rc = wx_sqlite3_create_module_v2( - db, "fts4", &fts3Module, (void *)pHash, 0 + db, "fts4", &fts3Module, (void *)pHash, hashDestroy ); } if( rc==SQLITE_OK ){ - rc = wx_sqlite3Fts3InitTok(db, (void *)pHash); + pHash->nRef++; + rc = wx_sqlite3Fts3InitTok(db, (void *)pHash, hashDestroy); } return rc; } @@ -174201,7 +184434,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3Init(wx_sqlite3 *db){ /* An error has occurred. Delete the hash table and return the error code. */ assert( rc!=SQLITE_OK ); if( pHash ){ - wx_sqlite3Fts3HashClear(pHash); + wx_sqlite3Fts3HashClear(&pHash->hash); wx_sqlite3_free(pHash); } return rc; @@ -174370,8 +184603,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ char *aPoslist = 0; /* Position list for deferred tokens */ int nPoslist = 0; /* Number of bytes in aPoslist */ int iPrev = -1; /* Token number of previous deferred token */ - - assert( pPhrase->doclist.bFreeList==0 ); + char *aFree = (pPhrase->doclist.bFreeList ? pPhrase->doclist.pList : 0); for(iToken=0; iTokennToken; iToken++){ Fts3PhraseToken *pToken = &pPhrase->aToken[iToken]; @@ -174385,6 +184617,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ if( pList==0 ){ wx_sqlite3_free(aPoslist); + wx_sqlite3_free(aFree); pPhrase->doclist.pList = 0; pPhrase->doclist.nList = 0; return SQLITE_OK; @@ -174405,6 +184638,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ nPoslist = (int)(aOut - aPoslist); if( nPoslist==0 ){ wx_sqlite3_free(aPoslist); + wx_sqlite3_free(aFree); pPhrase->doclist.pList = 0; pPhrase->doclist.nList = 0; return SQLITE_OK; @@ -174437,13 +184671,14 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ nDistance = iPrev - nMaxUndeferred; } - aOut = (char *)wx_sqlite3_malloc(nPoslist+8); + aOut = (char *)wx_sqlite3Fts3MallocZero(nPoslist+FTS3_BUFFER_PADDING); if( !aOut ){ wx_sqlite3_free(aPoslist); return SQLITE_NOMEM; } pPhrase->doclist.pList = aOut; + assert( p1 && p2 ); if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){ pPhrase->doclist.bFreeList = 1; pPhrase->doclist.nList = (int)(aOut - pPhrase->doclist.pList); @@ -174456,6 +184691,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ } } + if( pPhrase->doclist.pList!=aFree ) wx_sqlite3_free(aFree); return SQLITE_OK; } #endif /* SQLITE_DISABLE_FTS4_DEFERRED */ @@ -174548,7 +184784,7 @@ SQLITE_PRIVATE void wx_sqlite3Fts3DoclistPrev( assert( nDoclist>0 ); assert( *pbEof==0 ); - assert( p || *piDocid==0 ); + assert_fts3_nc( p || *piDocid==0 ); assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) ); if( p==0 ){ @@ -174804,7 +185040,7 @@ static int fts3EvalIncrPhraseNext( if( bEof==0 ){ int nList = 0; int nByte = a[p->nToken-1].nList; - char *aDoclist = wx_sqlite3_malloc(nByte+FTS3_BUFFER_PADDING); + char *aDoclist = wx_sqlite3_malloc64((i64)nByte+FTS3_BUFFER_PADDING); if( !aDoclist ) return SQLITE_NOMEM; memcpy(aDoclist, a[p->nToken-1].pList, nByte+1); memset(&aDoclist[nByte], 0, FTS3_BUFFER_PADDING); @@ -175198,16 +185434,15 @@ static int fts3EvalStart(Fts3Cursor *pCsr){ #ifndef SQLITE_DISABLE_FTS4_DEFERRED if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){ Fts3TokenAndCost *aTC; - Fts3Expr **apOr; aTC = (Fts3TokenAndCost *)wx_sqlite3_malloc64( sizeof(Fts3TokenAndCost) * nToken + sizeof(Fts3Expr *) * nOr * 2 ); - apOr = (Fts3Expr **)&aTC[nToken]; if( !aTC ){ rc = SQLITE_NOMEM; }else{ + Fts3Expr **apOr = (Fts3Expr **)&aTC[nToken]; int ii; Fts3TokenAndCost *pTC = aTC; Fts3Expr **ppOr = apOr; @@ -175347,9 +185582,8 @@ static void fts3EvalNextRow( Fts3Expr *pExpr, /* Expr. to advance to next matching row */ int *pRc /* IN/OUT: Error code */ ){ - if( *pRc==SQLITE_OK ){ + if( *pRc==SQLITE_OK && pExpr->bEof==0 ){ int bDescDoclist = pCsr->bDesc; /* Used by DOCID_CMP() macro */ - assert( pExpr->bEof==0 ); pExpr->bStart = 1; switch( pExpr->eType ){ @@ -175413,8 +185647,8 @@ static void fts3EvalNextRow( Fts3Expr *pRight = pExpr->pRight; wx_sqlite3_int64 iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid); - assert( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); - assert( pRight->bStart || pLeft->iDocid==pRight->iDocid ); + assert_fts3_nc( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); + assert_fts3_nc( pRight->bStart || pLeft->iDocid==pRight->iDocid ); if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){ fts3EvalNextRow(pCsr, pLeft, pRc); @@ -175631,11 +185865,10 @@ static int fts3EvalTestExpr( default: { #ifndef SQLITE_DISABLE_FTS4_DEFERRED - if( pCsr->pDeferred - && (pExpr->iDocid==pCsr->iPrevId || pExpr->bDeferred) - ){ + if( pCsr->pDeferred && (pExpr->bDeferred || ( + pExpr->iDocid==pCsr->iPrevId && pExpr->pPhrase->doclist.pList + ))){ Fts3Phrase *pPhrase = pExpr->pPhrase; - assert( pExpr->bDeferred || pPhrase->doclist.bFreeList==0 ); if( pExpr->bDeferred ){ fts3EvalInvalidatePoslist(pPhrase); } @@ -175826,6 +186059,22 @@ static void fts3EvalUpdateCounts(Fts3Expr *pExpr, int nCol){ } } +/* +** This is an wx_sqlite3Fts3ExprIterate() callback. If the Fts3Expr.aMI[] array +** has not yet been allocated, allocate and zero it. Otherwise, just zero +** it. +*/ +static int fts3AllocateMSI(Fts3Expr *pExpr, int iPhrase, void *pCtx){ + Fts3Table *pTab = (Fts3Table*)pCtx; + UNUSED_PARAMETER(iPhrase); + if( pExpr->aMI==0 ){ + pExpr->aMI = (u32 *)wx_sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32)); + if( pExpr->aMI==0 ) return SQLITE_NOMEM; + } + memset(pExpr->aMI, 0, pTab->nColumn * 3 * sizeof(u32)); + return SQLITE_OK; +} + /* ** Expression pExpr must be of type FTSQUERY_PHRASE. ** @@ -175847,7 +186096,6 @@ static int fts3EvalGatherStats( if( pExpr->aMI==0 ){ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; Fts3Expr *pRoot; /* Root of NEAR expression */ - Fts3Expr *p; /* Iterator used for several purposes */ wx_sqlite3_int64 iPrevId = pCsr->iPrevId; wx_sqlite3_int64 iDocid; @@ -175855,7 +186103,9 @@ static int fts3EvalGatherStats( /* Find the root of the NEAR expression */ pRoot = pExpr; - while( pRoot->pParent && pRoot->pParent->eType==FTSQUERY_NEAR ){ + while( pRoot->pParent + && (pRoot->pParent->eType==FTSQUERY_NEAR || pRoot->bDeferred) + ){ pRoot = pRoot->pParent; } iDocid = pRoot->iDocid; @@ -175863,14 +186113,8 @@ static int fts3EvalGatherStats( assert( pRoot->bStart ); /* Allocate space for the aMSI[] array of each FTSQUERY_PHRASE node */ - for(p=pRoot; p; p=p->pLeft){ - Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight); - assert( pE->aMI==0 ); - pE->aMI = (u32 *)wx_sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32)); - if( !pE->aMI ) return SQLITE_NOMEM; - memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32)); - } - + rc = wx_sqlite3Fts3ExprIterate(pRoot, fts3AllocateMSI, (void*)pTab); + if( rc!=SQLITE_OK ) return rc; fts3EvalRestart(pCsr, pRoot, &rc); while( pCsr->isEof==0 && rc==SQLITE_OK ){ @@ -176026,6 +186270,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3EvalPhrasePoslist( u8 bTreeEof = 0; Fts3Expr *p; /* Used to iterate from pExpr to root */ Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */ + Fts3Expr *pRun; /* Closest non-deferred ancestor of pNear */ int bMatch; /* Check if this phrase descends from an OR expression node. If not, @@ -176040,22 +186285,30 @@ SQLITE_PRIVATE int wx_sqlite3Fts3EvalPhrasePoslist( if( p->bEof ) bTreeEof = 1; } if( bOr==0 ) return SQLITE_OK; + pRun = pNear; + while( pRun->bDeferred ){ + assert( pRun->pParent ); + pRun = pRun->pParent; + } /* This is the descendent of an OR node. In this case we cannot use ** an incremental phrase. Load the entire doclist for the phrase ** into memory in this case. */ if( pPhrase->bIncr ){ - int bEofSave = pNear->bEof; - fts3EvalRestart(pCsr, pNear, &rc); - while( rc==SQLITE_OK && !pNear->bEof ){ - fts3EvalNextRow(pCsr, pNear, &rc); - if( bEofSave==0 && pNear->iDocid==iDocid ) break; + int bEofSave = pRun->bEof; + fts3EvalRestart(pCsr, pRun, &rc); + while( rc==SQLITE_OK && !pRun->bEof ){ + fts3EvalNextRow(pCsr, pRun, &rc); + if( bEofSave==0 && pRun->iDocid==iDocid ) break; } assert( rc!=SQLITE_OK || pPhrase->bIncr==0 ); + if( rc==SQLITE_OK && pRun->bEof!=bEofSave ){ + rc = FTS_CORRUPT_VTAB; + } } if( bTreeEof ){ - while( rc==SQLITE_OK && !pNear->bEof ){ - fts3EvalNextRow(pCsr, pNear, &rc); + while( rc==SQLITE_OK && !pRun->bEof ){ + fts3EvalNextRow(pCsr, pRun, &rc); } } if( rc!=SQLITE_OK ) return rc; @@ -176474,6 +186727,7 @@ static int fts3auxNextMethod(wx_sqlite3_vtab_cursor *pCursor){ if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM; memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat); iCol = 0; + rc = SQLITE_OK; while( iaStat[iCol+1].nDoc++; eState = 2; @@ -176525,7 +186783,6 @@ static int fts3auxNextMethod(wx_sqlite3_vtab_cursor *pCursor){ } pCsr->iCol = 0; - rc = SQLITE_OK; }else{ pCsr->isEof = 1; } @@ -176583,6 +186840,7 @@ static int fts3auxFilterMethod( wx_sqlite3Fts3SegReaderFinish(&pCsr->csr); wx_sqlite3_free((void *)pCsr->filter.zTerm); wx_sqlite3_free(pCsr->aStat); + wx_sqlite3_free(pCsr->zStop); memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr); pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; @@ -176853,7 +187111,7 @@ static int fts3isspace(char c){ ** zero the memory before returning a pointer to it. If unsuccessful, ** return NULL. */ -static void *fts3MallocZero(wx_sqlite3_int64 nByte){ +SQLITE_PRIVATE void *wx_sqlite3Fts3MallocZero(wx_sqlite3_int64 nByte){ void *pRet = wx_sqlite3_malloc64(nByte); if( pRet ) memset(pRet, 0, nByte); return pRet; @@ -176934,7 +187192,7 @@ static int getNextToken( rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); if( rc==SQLITE_OK ){ nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; - pRet = (Fts3Expr *)fts3MallocZero(nByte); + pRet = (Fts3Expr *)wx_sqlite3Fts3MallocZero(nByte); if( !pRet ){ rc = SQLITE_NOMEM; }else{ @@ -177189,7 +187447,7 @@ static int getNextNode( if( fts3isspace(cNext) || cNext=='"' || cNext=='(' || cNext==')' || cNext==0 ){ - pRet = (Fts3Expr *)fts3MallocZero(sizeof(Fts3Expr)); + pRet = (Fts3Expr *)wx_sqlite3Fts3MallocZero(sizeof(Fts3Expr)); if( !pRet ){ return SQLITE_NOMEM; } @@ -177368,7 +187626,7 @@ static int fts3ExprParse( && p->eType==FTSQUERY_PHRASE && pParse->isNot ){ /* Create an implicit NOT operator. */ - Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr)); + Fts3Expr *pNot = wx_sqlite3Fts3MallocZero(sizeof(Fts3Expr)); if( !pNot ){ wx_sqlite3Fts3ExprFree(p); rc = SQLITE_NOMEM; @@ -177402,7 +187660,7 @@ static int fts3ExprParse( /* Insert an implicit AND operator. */ Fts3Expr *pAnd; assert( pRet && pPrev ); - pAnd = fts3MallocZero(sizeof(Fts3Expr)); + pAnd = wx_sqlite3Fts3MallocZero(sizeof(Fts3Expr)); if( !pAnd ){ wx_sqlite3Fts3ExprFree(p); rc = SQLITE_NOMEM; @@ -179034,7 +189292,7 @@ static int porterNext( if( n>c->nAllocated ){ char *pNew; c->nAllocated = n+20; - pNew = wx_sqlite3_realloc(c->zToken, c->nAllocated); + pNew = wx_sqlite3_realloc64(c->zToken, c->nAllocated); if( !pNew ) return SQLITE_NOMEM; c->zToken = pNew; } @@ -179786,7 +190044,7 @@ static int simpleNext( if( n>c->nTokenAllocated ){ char *pNew; c->nTokenAllocated = n+20; - pNew = wx_sqlite3_realloc(c->pToken, c->nTokenAllocated); + pNew = wx_sqlite3_realloc64(c->pToken, c->nTokenAllocated); if( !pNew ) return SQLITE_NOMEM; c->pToken = pNew; } @@ -180258,7 +190516,7 @@ static int fts3tokRowidMethod( ** Register the fts3tok module with database connection db. Return SQLITE_OK ** if successful or an error code if wx_sqlite3_create_module() fails. */ -SQLITE_PRIVATE int wx_sqlite3Fts3InitTok(wx_sqlite3 *db, Fts3Hash *pHash){ +SQLITE_PRIVATE int wx_sqlite3Fts3InitTok(wx_sqlite3 *db, Fts3Hash *pHash, void(*xDestroy)(void*)){ static const wx_sqlite3_module fts3tok_module = { 0, /* iVersion */ fts3tokConnectMethod, /* xCreate */ @@ -180287,7 +190545,9 @@ SQLITE_PRIVATE int wx_sqlite3Fts3InitTok(wx_sqlite3 *db, Fts3Hash *pHash){ }; int rc; /* Return code */ - rc = wx_sqlite3_create_module(db, "fts3tokenize", &fts3tok_module, (void*)pHash); + rc = wx_sqlite3_create_module_v2( + db, "fts3tokenize", &fts3tok_module, (void*)pHash, xDestroy + ); return rc; } @@ -180946,7 +191206,7 @@ static int fts3PendingListAppendVarint( /* Allocate or grow the PendingList as required. */ if( !p ){ - p = wx_sqlite3_malloc(sizeof(*p) + 100); + p = wx_sqlite3_malloc64(sizeof(*p) + 100); if( !p ){ return SQLITE_NOMEM; } @@ -180955,14 +191215,14 @@ static int fts3PendingListAppendVarint( p->nData = 0; } else if( p->nData+FTS3_VARINT_MAX+1>p->nSpace ){ - int nNew = p->nSpace * 2; - p = wx_sqlite3_realloc(p, sizeof(*p) + nNew); + i64 nNew = p->nSpace * 2; + p = wx_sqlite3_realloc64(p, sizeof(*p) + nNew); if( !p ){ wx_sqlite3_free(*pp); *pp = 0; return SQLITE_NOMEM; } - p->nSpace = nNew; + p->nSpace = (int)nNew; p->aData = (char *)&p[1]; } @@ -181519,7 +191779,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3ReadBlock( int nByte = wx_sqlite3_blob_bytes(p->pSegments); *pnBlob = nByte; if( paBlob ){ - char *aByte = wx_sqlite3_malloc(nByte + FTS3_NODE_PADDING); + char *aByte = wx_sqlite3_malloc64((i64)nByte + FTS3_NODE_PADDING); if( !aByte ){ rc = SQLITE_NOMEM; }else{ @@ -181632,9 +191892,19 @@ static int fts3SegReaderNext( char *aCopy; PendingList *pList = (PendingList *)fts3HashData(pElem); int nCopy = pList->nData+1; - pReader->zTerm = (char *)fts3HashKey(pElem); - pReader->nTerm = fts3HashKeysize(pElem); - aCopy = (char*)wx_sqlite3_malloc(nCopy); + + int nTerm = fts3HashKeysize(pElem); + if( (nTerm+1)>pReader->nTermAlloc ){ + wx_sqlite3_free(pReader->zTerm); + pReader->zTerm = (char*)wx_sqlite3_malloc64(((i64)nTerm+1)*2); + if( !pReader->zTerm ) return SQLITE_NOMEM; + pReader->nTermAlloc = (nTerm+1)*2; + } + memcpy(pReader->zTerm, fts3HashKey(pElem), nTerm); + pReader->zTerm[nTerm] = '\0'; + pReader->nTerm = nTerm; + + aCopy = (char*)wx_sqlite3_malloc64(nCopy); if( !aCopy ) return SQLITE_NOMEM; memcpy(aCopy, pList->aData, nCopy); pReader->nNode = pReader->nDoclist = nCopy; @@ -181886,9 +192156,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3MsrOvfl( */ SQLITE_PRIVATE void wx_sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){ if( pReader ){ - if( !fts3SegReaderIsPending(pReader) ){ - wx_sqlite3_free(pReader->zTerm); - } + wx_sqlite3_free(pReader->zTerm); if( !fts3SegReaderIsRootOnly(pReader) ){ wx_sqlite3_free(pReader->aNode); } @@ -181923,7 +192191,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3SegReaderNew( nExtra = nRoot + FTS3_NODE_PADDING; } - pReader = (Fts3SegReader *)wx_sqlite3_malloc(sizeof(Fts3SegReader) + nExtra); + pReader = (Fts3SegReader *)wx_sqlite3_malloc64(sizeof(Fts3SegReader) + nExtra); if( !pReader ){ return SQLITE_NOMEM; } @@ -182015,7 +192283,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3SegReaderPending( if( nElem==nAlloc ){ Fts3HashElem **aElem2; nAlloc += 16; - aElem2 = (Fts3HashElem **)wx_sqlite3_realloc( + aElem2 = (Fts3HashElem **)wx_sqlite3_realloc64( aElem, nAlloc*sizeof(Fts3HashElem *) ); if( !aElem2 ){ @@ -182104,7 +192372,7 @@ static int fts3SegReaderCmp(Fts3SegReader *pLhs, Fts3SegReader *pRhs){ if( rc==0 ){ rc = pRhs->iIdx - pLhs->iIdx; } - assert( rc!=0 ); + assert_fts3_nc( rc!=0 ); return rc; } @@ -182300,8 +192568,8 @@ static int fts3PrefixCompress( int nNext /* Size of buffer zNext in bytes */ ){ int n; - UNUSED_PARAMETER(nNext); - for(n=0; naData==(char *)&pTree[1] ); - pTree->aData = (char *)wx_sqlite3_malloc(nReq); + pTree->aData = (char *)wx_sqlite3_malloc64(nReq); if( !pTree->aData ){ return SQLITE_NOMEM; } @@ -182367,7 +192635,7 @@ static int fts3NodeAddTerm( if( isCopyTerm ){ if( pTree->nMalloczMalloc, nTerm*2); + char *zNew = wx_sqlite3_realloc64(pTree->zMalloc, (i64)nTerm*2); if( !zNew ){ return SQLITE_NOMEM; } @@ -182393,7 +192661,7 @@ static int fts3NodeAddTerm( ** now. Instead, the term is inserted into the parent of pTree. If pTree ** has no parent, one is created here. */ - pNew = (SegmentNode *)wx_sqlite3_malloc(sizeof(SegmentNode) + p->nNodeSize); + pNew = (SegmentNode *)wx_sqlite3_malloc64(sizeof(SegmentNode) + p->nNodeSize); if( !pNew ){ return SQLITE_NOMEM; } @@ -182531,7 +192799,7 @@ static int fts3SegWriterAdd( ){ int nPrefix; /* Size of term prefix in bytes */ int nSuffix; /* Size of term suffix in bytes */ - int nReq; /* Number of bytes required on leaf page */ + i64 nReq; /* Number of bytes required on leaf page */ int nData; SegmentWriter *pWriter = *ppWriter; @@ -182540,13 +192808,13 @@ static int fts3SegWriterAdd( wx_sqlite3_stmt *pStmt; /* Allocate the SegmentWriter structure */ - pWriter = (SegmentWriter *)wx_sqlite3_malloc(sizeof(SegmentWriter)); + pWriter = (SegmentWriter *)wx_sqlite3_malloc64(sizeof(SegmentWriter)); if( !pWriter ) return SQLITE_NOMEM; memset(pWriter, 0, sizeof(SegmentWriter)); *ppWriter = pWriter; /* Allocate a buffer in which to accumulate data */ - pWriter->aData = (char *)wx_sqlite3_malloc(p->nNodeSize); + pWriter->aData = (char *)wx_sqlite3_malloc64(p->nNodeSize); if( !pWriter->aData ) return SQLITE_NOMEM; pWriter->nSize = p->nNodeSize; @@ -182621,7 +192889,7 @@ static int fts3SegWriterAdd( ** the buffer to make it large enough. */ if( nReq>pWriter->nSize ){ - char *aNew = wx_sqlite3_realloc(pWriter->aData, nReq); + char *aNew = wx_sqlite3_realloc64(pWriter->aData, nReq); if( !aNew ) return SQLITE_NOMEM; pWriter->aData = aNew; pWriter->nSize = nReq; @@ -182646,7 +192914,7 @@ static int fts3SegWriterAdd( */ if( isCopyTerm ){ if( nTerm>pWriter->nMalloc ){ - char *zNew = wx_sqlite3_realloc(pWriter->zMalloc, nTerm*2); + char *zNew = wx_sqlite3_realloc64(pWriter->zMalloc, (i64)nTerm*2); if( !zNew ){ return SQLITE_NOMEM; } @@ -182954,18 +193222,20 @@ static void fts3ColumnFilter( static int fts3MsrBufferData( Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */ char *pList, - int nList + i64 nList ){ - if( nList>pMsr->nBuffer ){ + if( (nList+FTS3_NODE_PADDING)>pMsr->nBuffer ){ char *pNew; - pMsr->nBuffer = nList*2; - pNew = (char *)wx_sqlite3_realloc(pMsr->aBuffer, pMsr->nBuffer); + int nNew = nList*2 + FTS3_NODE_PADDING; + pNew = (char *)wx_sqlite3_realloc64(pMsr->aBuffer, nNew); if( !pNew ) return SQLITE_NOMEM; pMsr->aBuffer = pNew; + pMsr->nBuffer = nNew; } assert( nList>0 ); memcpy(pMsr->aBuffer, pList, nList); + memset(&pMsr->aBuffer[nList], 0, FTS3_NODE_PADDING); return SQLITE_OK; } @@ -183015,7 +193285,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3MsrIncrNext( fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp); if( nList>0 && fts3SegReaderIsPending(apSegment[0]) ){ - rc = fts3MsrBufferData(pMsr, pList, nList+1); + rc = fts3MsrBufferData(pMsr, pList, (i64)nList+1); if( rc!=SQLITE_OK ) return rc; assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 ); pList = pMsr->aBuffer; @@ -183152,11 +193422,11 @@ SQLITE_PRIVATE int wx_sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){ return SQLITE_OK; } -static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, int nReq){ +static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, i64 nReq){ if( nReq>pCsr->nBuffer ){ char *aNew; pCsr->nBuffer = nReq*2; - aNew = wx_sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer); + aNew = wx_sqlite3_realloc64(pCsr->aBuffer, pCsr->nBuffer); if( !aNew ){ return SQLITE_NOMEM; } @@ -183247,7 +193517,8 @@ SQLITE_PRIVATE int wx_sqlite3Fts3SegReaderStep( ){ pCsr->nDoclist = apSegment[0]->nDoclist; if( fts3SegReaderIsPending(apSegment[0]) ){ - rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist); + rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, + (i64)pCsr->nDoclist); pCsr->aDoclist = pCsr->aBuffer; }else{ pCsr->aDoclist = apSegment[0]->aDoclist; @@ -183300,7 +193571,8 @@ SQLITE_PRIVATE int wx_sqlite3Fts3SegReaderStep( nByte = wx_sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0); - rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist); + rc = fts3GrowSegReaderBuffer(pCsr, + (i64)nByte+nDoclist+FTS3_NODE_PADDING); if( rc ) return rc; if( isFirst ){ @@ -183326,7 +193598,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3SegReaderStep( fts3SegReaderSort(apSegment, nMerge, j, xCmp); } if( nDoclist>0 ){ - rc = fts3GrowSegReaderBuffer(pCsr, nDoclist+FTS3_NODE_PADDING); + rc = fts3GrowSegReaderBuffer(pCsr, (i64)nDoclist+FTS3_NODE_PADDING); if( rc ) return rc; memset(&pCsr->aBuffer[nDoclist], 0, FTS3_NODE_PADDING); pCsr->aDoclist = pCsr->aBuffer; @@ -184039,7 +194311,7 @@ struct NodeReader { static void blobGrowBuffer(Blob *pBlob, int nMin, int *pRc){ if( *pRc==SQLITE_OK && nMin>pBlob->nAlloc ){ int nAlloc = nMin; - char *a = (char *)wx_sqlite3_realloc(pBlob->a, nAlloc); + char *a = (char *)wx_sqlite3_realloc64(pBlob->a, nAlloc); if( a ){ pBlob->nAlloc = nAlloc; pBlob->a = a; @@ -184080,7 +194352,7 @@ static int nodeReaderNext(NodeReader *p){ return FTS_CORRUPT_VTAB; } blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc); - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && ALWAYS(p->term.a!=0) ){ memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix); p->term.n = nPrefix+nSuffix; p->iOff += nSuffix; @@ -184188,6 +194460,8 @@ static int fts3IncrmergePush( pBlk->n += wx_sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nPrefix); } pBlk->n += wx_sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nSuffix); + assert( nPrefix+nSuffix<=nTerm ); + assert( nPrefix>=0 ); memcpy(&pBlk->a[pBlk->n], &zTerm[nPrefix], nSuffix); pBlk->n += nSuffix; @@ -184310,6 +194584,7 @@ static int fts3IncrmergeAppend( pLeaf = &pWriter->aNodeWriter[0]; nPrefix = fts3PrefixCompress(pLeaf->key.a, pLeaf->key.n, zTerm, nTerm); nSuffix = nTerm - nPrefix; + if(nSuffix<=0 ) return FTS_CORRUPT_VTAB; nSpace = wx_sqlite3Fts3VarintLen(nPrefix); nSpace += wx_sqlite3Fts3VarintLen(nSuffix) + nSuffix; @@ -184474,7 +194749,11 @@ static int fts3TermCmp( int nCmp = MIN(nLhs, nRhs); int res; - res = (nCmp ? memcmp(zLhs, zRhs, nCmp) : 0); + if( nCmp && ALWAYS(zLhs) && ALWAYS(zRhs) ){ + res = memcmp(zLhs, zRhs, nCmp); + }else{ + res = 0; + } if( res==0 ) res = nLhs - nRhs; return res; @@ -184829,7 +195108,7 @@ static int fts3RepackSegdirLevel( if( nIdx>=nAlloc ){ int *aNew; nAlloc += 16; - aNew = wx_sqlite3_realloc(aIdx, nAlloc*sizeof(int)); + aNew = wx_sqlite3_realloc64(aIdx, nAlloc*sizeof(int)); if( !aNew ){ rc = SQLITE_NOMEM; break; @@ -185118,7 +195397,7 @@ static int fts3IncrmergeHintLoad(Fts3Table *p, Blob *pHint){ if( aHint ){ blobGrowBuffer(pHint, nHint, &rc); if( rc==SQLITE_OK ){ - memcpy(pHint->a, aHint, nHint); + if( ALWAYS(pHint->a!=0) ) memcpy(pHint->a, aHint, nHint); pHint->n = nHint; } } @@ -185203,7 +195482,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){ /* Allocate space for the cursor, filter and writer objects */ const int nAlloc = sizeof(*pCsr) + sizeof(*pFilter) + sizeof(*pWriter); - pWriter = (IncrmergeWriter *)wx_sqlite3_malloc(nAlloc); + pWriter = (IncrmergeWriter *)wx_sqlite3_malloc64(nAlloc); if( !pWriter ) return SQLITE_NOMEM; pFilter = (Fts3SegFilter *)&pWriter[1]; pCsr = (Fts3MultiSegReader *)&pFilter[1]; @@ -185839,7 +196118,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3DeferredTokenList( return SQLITE_OK; } - pRet = (char *)wx_sqlite3_malloc(p->pList->nData); + pRet = (char *)wx_sqlite3_malloc64(p->pList->nData); if( !pRet ) return SQLITE_NOMEM; nSkip = wx_sqlite3Fts3GetVarint(p->pList->aData, &dummy); @@ -185859,7 +196138,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3DeferToken( int iCol /* Column that token must appear in (or -1) */ ){ Fts3DeferredToken *pDeferred; - pDeferred = wx_sqlite3_malloc(sizeof(*pDeferred)); + pDeferred = wx_sqlite3_malloc64(sizeof(*pDeferred)); if( !pDeferred ){ return SQLITE_NOMEM; } @@ -186114,6 +196393,10 @@ SQLITE_PRIVATE int wx_sqlite3Fts3Optimize(Fts3Table *p){ /* #include */ /* #include */ +#ifndef SQLITE_AMALGAMATION +typedef wx_sqlite3_int64 i64; +#endif + /* ** Characters that may appear in the second argument to matchinfo(). */ @@ -186134,7 +196417,7 @@ SQLITE_PRIVATE int wx_sqlite3Fts3Optimize(Fts3Table *p){ /* -** Used as an fts3ExprIterate() context when loading phrase doclists to +** Used as an wx_sqlite3Fts3ExprIterate() context when loading phrase doclists to ** Fts3Expr.aDoclist[]/nDoclist. */ typedef struct LoadDoclistCtx LoadDoclistCtx; @@ -186164,9 +196447,9 @@ struct SnippetIter { struct SnippetPhrase { int nToken; /* Number of tokens in phrase */ char *pList; /* Pointer to start of phrase position list */ - int iHead; /* Next value in position list */ + i64 iHead; /* Next value in position list */ char *pHead; /* Position list data following iHead */ - int iTail; /* Next value in trailing position list */ + i64 iTail; /* Next value in trailing position list */ char *pTail; /* Position list data following iTail */ }; @@ -186178,7 +196461,7 @@ struct SnippetFragment { }; /* -** This type is used as an fts3ExprIterate() context object while +** This type is used as an wx_sqlite3Fts3ExprIterate() context object while ** accumulating the data returned by the matchinfo() function. */ typedef struct MatchInfo MatchInfo; @@ -186231,9 +196514,8 @@ static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ + sizeof(MatchinfoBuffer); wx_sqlite3_int64 nStr = strlen(zMatchinfo); - pRet = wx_sqlite3_malloc64(nByte + nStr+1); + pRet = wx_sqlite3Fts3MallocZero(nByte + nStr+1); if( pRet ){ - memset(pRet, 0, nByte); pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet; pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + sizeof(u32)*((int)nElem+1); @@ -186331,14 +196613,14 @@ SQLITE_PRIVATE void wx_sqlite3Fts3MIBufferFree(MatchinfoBuffer *p){ ** After it returns, *piPos contains the value of the next element of the ** list and *pp is advanced to the following varint. */ -static void fts3GetDeltaPosition(char **pp, int *piPos){ +static void fts3GetDeltaPosition(char **pp, i64 *piPos){ int iVal; *pp += fts3GetVarint32(*pp, &iVal); *piPos += (iVal-2); } /* -** Helper function for fts3ExprIterate() (see below). +** Helper function for wx_sqlite3Fts3ExprIterate() (see below). */ static int fts3ExprIterate2( Fts3Expr *pExpr, /* Expression to iterate phrases of */ @@ -186372,7 +196654,7 @@ static int fts3ExprIterate2( ** Otherwise, SQLITE_OK is returned after a callback has been made for ** all eligible phrase nodes. */ -static int fts3ExprIterate( +SQLITE_PRIVATE int wx_sqlite3Fts3ExprIterate( Fts3Expr *pExpr, /* Expression to iterate phrases of */ int (*x)(Fts3Expr*,int,void*), /* Callback function to invoke for phrases */ void *pCtx /* Second argument to pass to callback */ @@ -186381,10 +196663,9 @@ static int fts3ExprIterate( return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx); } - /* -** This is an fts3ExprIterate() callback used while loading the doclists -** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also +** This is an wx_sqlite3Fts3ExprIterate() callback used while loading the +** doclists for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also ** fts3ExprLoadDoclists(). */ static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ @@ -186416,9 +196697,9 @@ static int fts3ExprLoadDoclists( int *pnToken /* OUT: Number of tokens in query */ ){ int rc; /* Return Code */ - LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */ + LoadDoclistCtx sCtx = {0,0,0}; /* Context for wx_sqlite3Fts3ExprIterate() */ sCtx.pCsr = pCsr; - rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb, (void *)&sCtx); + rc = wx_sqlite3Fts3ExprIterate(pCsr->pExpr,fts3ExprLoadDoclistsCb,(void*)&sCtx); if( pnPhrase ) *pnPhrase = sCtx.nPhrase; if( pnToken ) *pnToken = sCtx.nToken; return rc; @@ -186431,7 +196712,7 @@ static int fts3ExprPhraseCountCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ } static int fts3ExprPhraseCount(Fts3Expr *pExpr){ int nPhrase = 0; - (void)fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase); + (void)wx_sqlite3Fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase); return nPhrase; } @@ -186440,10 +196721,10 @@ static int fts3ExprPhraseCount(Fts3Expr *pExpr){ ** arguments so that it points to the first element with a value greater ** than or equal to parameter iNext. */ -static void fts3SnippetAdvance(char **ppIter, int *piIter, int iNext){ +static void fts3SnippetAdvance(char **ppIter, i64 *piIter, int iNext){ char *pIter = *ppIter; if( pIter ){ - int iIter = *piIter; + i64 iIter = *piIter; while( iIteraPhrase[i]; if( pPhrase->pTail ){ char *pCsr = pPhrase->pTail; - int iCsr = pPhrase->iTail; + i64 iCsr = pPhrase->iTail; while( iCsr<(iStart+pIter->nSnippet) && iCsr>=iStart ){ int j; @@ -186559,8 +196840,9 @@ static void fts3SnippetDetails( } /* -** This function is an fts3ExprIterate() callback used by fts3BestSnippet(). -** Each invocation populates an element of the SnippetIter.aPhrase[] array. +** This function is an wx_sqlite3Fts3ExprIterate() callback used by +** fts3BestSnippet(). Each invocation populates an element of the +** SnippetIter.aPhrase[] array. */ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ SnippetIter *p = (SnippetIter *)ctx; @@ -186572,7 +196854,7 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ rc = wx_sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pCsr); assert( rc==SQLITE_OK || pCsr==0 ); if( pCsr ){ - int iFirst = 0; + i64 iFirst = 0; pPhrase->pList = pCsr; fts3GetDeltaPosition(&pCsr, &iFirst); if( iFirst<0 ){ @@ -186637,11 +196919,10 @@ static int fts3BestSnippet( ** the required space using malloc(). */ nByte = sizeof(SnippetPhrase) * nList; - sIter.aPhrase = (SnippetPhrase *)wx_sqlite3_malloc64(nByte); + sIter.aPhrase = (SnippetPhrase *)wx_sqlite3Fts3MallocZero(nByte); if( !sIter.aPhrase ){ return SQLITE_NOMEM; } - memset(sIter.aPhrase, 0, nByte); /* Initialize the contents of the SnippetIter object. Then iterate through ** the set of phrases in the expression to populate the aPhrase[] array. @@ -186651,7 +196932,9 @@ static int fts3BestSnippet( sIter.nSnippet = nSnippet; sIter.nPhrase = nList; sIter.iCurrent = -1; - rc = fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter); + rc = wx_sqlite3Fts3ExprIterate( + pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter + ); if( rc==SQLITE_OK ){ /* Set the *pmSeen output variable. */ @@ -187012,10 +197295,10 @@ static int fts3ExprLHitGather( } /* -** fts3ExprIterate() callback used to collect the "global" matchinfo stats -** for a single query. +** wx_sqlite3Fts3ExprIterate() callback used to collect the "global" matchinfo +** stats for a single query. ** -** fts3ExprIterate() callback to load the 'global' elements of a +** wx_sqlite3Fts3ExprIterate() callback to load the 'global' elements of a ** FTS3_MATCHINFO_HITS matchinfo array. The global stats are those elements ** of the matchinfo array that are constant for all rows returned by the ** current query. @@ -187050,7 +197333,7 @@ static int fts3ExprGlobalHitsCb( } /* -** fts3ExprIterate() callback used to collect the "local" part of the +** wx_sqlite3Fts3ExprIterate() callback used to collect the "local" part of the ** FTS3_MATCHINFO_HITS array. The local stats are those elements of the ** array that are different for each row returned by the query. */ @@ -187205,10 +197488,12 @@ static int fts3MatchinfoLcsCb( ** position list for the next column. */ static int fts3LcsIteratorAdvance(LcsIterator *pIter){ - char *pRead = pIter->pRead; + char *pRead; wx_sqlite3_int64 iRead; int rc = 0; + if( NEVER(pIter==0) ) return 1; + pRead = pIter->pRead; pRead += wx_sqlite3Fts3GetVarint(pRead, &iRead); if( iRead==0 || iRead==1 ){ pRead = 0; @@ -187242,10 +197527,9 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ /* Allocate and populate the array of LcsIterator objects. The array ** contains one element for each matchable phrase in the query. **/ - aIter = wx_sqlite3_malloc64(sizeof(LcsIterator) * pCsr->nPhrase); + aIter = wx_sqlite3Fts3MallocZero(sizeof(LcsIterator) * pCsr->nPhrase); if( !aIter ) return SQLITE_NOMEM; - memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase); - (void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter); + (void)wx_sqlite3Fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter); for(i=0; inPhrase; i++){ LcsIterator *pIter = &aIter[i]; @@ -187422,11 +197706,11 @@ static int fts3MatchinfoValues( rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc,0,0); if( rc!=SQLITE_OK ) break; } - rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo); + rc = wx_sqlite3Fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo); wx_sqlite3Fts3EvalTestDeferred(pCsr, &rc); if( rc!=SQLITE_OK ) break; } - (void)fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo); + (void)wx_sqlite3Fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo); break; } } @@ -187636,8 +197920,8 @@ typedef struct TermOffsetCtx TermOffsetCtx; struct TermOffset { char *pList; /* Position-list */ - int iPos; /* Position just read from pList */ - int iOff; /* Offset of this term from read positions */ + i64 iPos; /* Position just read from pList */ + i64 iOff; /* Offset of this term from read positions */ }; struct TermOffsetCtx { @@ -187649,14 +197933,14 @@ struct TermOffsetCtx { }; /* -** This function is an fts3ExprIterate() callback used by wx_sqlite3Fts3Offsets(). +** This function is an wx_sqlite3Fts3ExprIterate() callback used by wx_sqlite3Fts3Offsets(). */ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ TermOffsetCtx *p = (TermOffsetCtx *)ctx; int nTerm; /* Number of tokens in phrase */ int iTerm; /* For looping through nTerm phrase terms */ char *pList; /* Pointer to position list for phrase */ - int iPos = 0; /* First position in position-list */ + i64 iPos = 0; /* First position in position-list */ int rc; UNUSED_PARAMETER(iPhrase); @@ -187705,7 +197989,7 @@ SQLITE_PRIVATE void wx_sqlite3Fts3Offsets( if( rc!=SQLITE_OK ) goto offsets_out; /* Allocate the array of TermOffset iterators. */ - sCtx.aTerm = (TermOffset *)wx_sqlite3_malloc64(sizeof(TermOffset)*nToken); + sCtx.aTerm = (TermOffset *)wx_sqlite3Fts3MallocZero(sizeof(TermOffset)*nToken); if( 0==sCtx.aTerm ){ rc = SQLITE_NOMEM; goto offsets_out; @@ -187726,13 +198010,15 @@ SQLITE_PRIVATE void wx_sqlite3Fts3Offsets( const char *zDoc; int nDoc; - /* Initialize the contents of sCtx.aTerm[] for column iCol. There is - ** no way that this operation can fail, so the return code from - ** fts3ExprIterate() can be discarded. + /* Initialize the contents of sCtx.aTerm[] for column iCol. This + ** operation may fail if the database contains corrupt records. */ sCtx.iCol = iCol; sCtx.iTerm = 0; - (void)fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx); + rc = wx_sqlite3Fts3ExprIterate( + pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx + ); + if( rc!=SQLITE_OK ) goto offsets_out; /* Retreive the text stored in column iCol. If an SQL NULL is stored ** in column iCol, jump immediately to the next iteration of the loop. @@ -188631,7 +198917,7 @@ SQLITE_PRIVATE int wx_sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ #endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */ /************** End of fts3_unicode2.c ***************************************/ -/************** Begin file json1.c *******************************************/ +/************** Begin file json.c ********************************************/ /* ** 2015-08-12 ** @@ -188644,10 +198930,10 @@ SQLITE_PRIVATE int wx_sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ ** ****************************************************************************** ** -** This SQLite extension implements JSON functions. The interface is -** modeled after MySQL JSON functions: +** This SQLite JSON functions. ** -** https://dev.mysql.com/doc/refman/5.7/en/json.html +** This file began as an extension in ext/misc/json1.c in 2015. That +** extension proved so useful that it has now been moved into the core. ** ** For the time being, all JSON is stored as pure text. (We might add ** a JSONB type in the future which stores a binary encoding of JSON in @@ -188655,48 +198941,8 @@ SQLITE_PRIVATE int wx_sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ ** This implementation parses JSON text at 250 MB/s, so it is hard to see ** how JSONB might improve on that.) */ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) -#if !defined(SQLITEINT_H) -/* #include "wx_sqlite3ext.h" */ -#endif -SQLITE_EXTENSION_INIT1 -/* #include */ -/* #include */ -/* #include */ -/* #include */ - -/* Mark a function parameter as unused, to suppress nuisance compiler -** warnings. */ -#ifndef UNUSED_PARAM -# define UNUSED_PARAM(X) (void)(X) -#endif - -#ifndef LARGEST_INT64 -# define LARGEST_INT64 (0xffffffff|(((wx_sqlite3_int64)0x7fffffff)<<32)) -# define SMALLEST_INT64 (((wx_sqlite3_int64)-1) - LARGEST_INT64) -#endif - -#ifndef deliberate_fall_through -# define deliberate_fall_through -#endif - -/* -** Versions of isspace(), isalnum() and isdigit() to which it is safe -** to pass signed char values. -*/ -#ifdef wx_sqlite3Isdigit - /* Use the SQLite core versions if this routine is part of the - ** SQLite amalgamation */ -# define safe_isdigit(x) wx_sqlite3Isdigit(x) -# define safe_isalnum(x) wx_sqlite3Isalnum(x) -# define safe_isxdigit(x) wx_sqlite3Isxdigit(x) -#else - /* Use the standard library for separate compilation */ -#include /* amalgamator: keep */ -# define safe_isdigit(x) isdigit((unsigned char)(x)) -# define safe_isalnum(x) isalnum((unsigned char)(x)) -# define safe_isxdigit(x) isxdigit((unsigned char)(x)) -#endif +#ifndef SQLITE_OMIT_JSON +/* #include "sqliteInt.h" */ /* ** Growing our own isspace() routine this way is twice as fast as @@ -188721,15 +198967,12 @@ static const char jsonIsSpace[] = { 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, }; -#define safe_isspace(x) (jsonIsSpace[(unsigned char)x]) +#define fast_isspace(x) (jsonIsSpace[(unsigned char)x]) -#ifndef SQLITE_AMALGAMATION - /* Unsigned integer types. These are already defined in the sqliteInt.h, - ** but the definitions need to be repeated for separate compilation. */ - typedef wx_sqlite3_uint64 u64; - typedef unsigned int u32; - typedef unsigned short int u16; - typedef unsigned char u8; +#if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST) +# define VVA(X) +#else +# define VVA(X) X #endif /* Objects */ @@ -188788,13 +199031,14 @@ static const char * const jsonType[] = { struct JsonNode { u8 eType; /* One of the JSON_ type values */ u8 jnFlags; /* JNODE flags */ + u8 eU; /* Which union element to use */ u32 n; /* Bytes of content, or number of sub-nodes */ union { - const char *zJContent; /* Content for INT, REAL, and STRING */ - u32 iAppend; /* More terms for ARRAY and OBJECT */ - u32 iKey; /* Key for ARRAY objects in json_tree() */ - u32 iReplace; /* Replacement content for JNODE_REPLACE */ - JsonNode *pPatch; /* Node chain of patch for JNODE_PATCH */ + const char *zJContent; /* 1: Content for INT, REAL, and STRING */ + u32 iAppend; /* 2: More terms for ARRAY and OBJECT */ + u32 iKey; /* 3: Key for ARRAY objects in json_tree() */ + u32 iReplace; /* 4: Replacement content for JNODE_REPLACE */ + JsonNode *pPatch; /* 5: Node chain of patch for JNODE_PATCH */ } u; }; @@ -188933,7 +199177,7 @@ static void jsonAppendSeparator(JsonString *p){ */ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ u32 i; - if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return; + if( zIn==0 || ((N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0) ) return; p->zBuf[p->nUsed++] = '"'; for(i=0; ijnFlags & (JNODE_REPLACE|JNODE_PATCH) ){ - if( pNode->jnFlags & JNODE_REPLACE ){ + if( (pNode->jnFlags & JNODE_REPLACE)!=0 && ALWAYS(aReplace!=0) ){ + assert( pNode->eU==4 ); jsonAppendValue(pOut, aReplace[pNode->u.iReplace]); return; } + assert( pNode->eU==5 ); pNode = pNode->u.pPatch; } switch( pNode->eType ){ @@ -189095,6 +199342,7 @@ static void jsonRenderNode( } case JSON_STRING: { if( pNode->jnFlags & JNODE_RAW ){ + assert( pNode->eU==1 ); jsonAppendString(pOut, pNode->u.zJContent, pNode->n); break; } @@ -189102,6 +199350,7 @@ static void jsonRenderNode( } case JSON_REAL: case JSON_INT: { + assert( pNode->eU==1 ); jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); break; } @@ -189117,6 +199366,7 @@ static void jsonRenderNode( j += jsonNodeSize(&pNode[j]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + assert( pNode->eU==2 ); pNode = &pNode[pNode->u.iAppend]; j = 1; } @@ -189137,6 +199387,7 @@ static void jsonRenderNode( j += 1 + jsonNodeSize(&pNode[j+1]); } if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + assert( pNode->eU==2 ); pNode = &pNode[pNode->u.iAppend]; j = 1; } @@ -189181,10 +199432,10 @@ static u8 jsonHexToInt(int h){ */ static u32 jsonHexToInt4(const char *z){ u32 v; - assert( safe_isxdigit(z[0]) ); - assert( safe_isxdigit(z[1]) ); - assert( safe_isxdigit(z[2]) ); - assert( safe_isxdigit(z[3]) ); + assert( wx_sqlite3Isxdigit(z[0]) ); + assert( wx_sqlite3Isxdigit(z[1]) ); + assert( wx_sqlite3Isxdigit(z[2]) ); + assert( wx_sqlite3Isxdigit(z[3]) ); v = (jsonHexToInt(z[0])<<12) + (jsonHexToInt(z[1])<<8) + (jsonHexToInt(z[2])<<4) @@ -189216,7 +199467,9 @@ static void jsonReturn( } case JSON_INT: { wx_sqlite3_int64 i = 0; - const char *z = pNode->u.zJContent; + const char *z; + assert( pNode->eU==1 ); + z = pNode->u.zJContent; if( z[0]=='-' ){ z++; } while( z[0]>='0' && z[0]<='9' ){ unsigned v = *(z++) - '0'; @@ -189239,14 +199492,17 @@ static void jsonReturn( wx_sqlite3_result_int64(pCtx, i); int_done: break; - int_as_real: i=0; /* no break */ deliberate_fall_through + int_as_real: ; /* no break */ deliberate_fall_through } case JSON_REAL: { double r; #ifdef SQLITE_AMALGAMATION - const char *z = pNode->u.zJContent; + const char *z; + assert( pNode->eU==1 ); + z = pNode->u.zJContent; wx_sqlite3AtoF(z, &r, wx_sqlite3Strlen30(z), SQLITE_UTF8); #else + assert( pNode->eU==1 ); r = strtod(pNode->u.zJContent, 0); #endif wx_sqlite3_result_double(pCtx, r); @@ -189257,6 +199513,7 @@ static void jsonReturn( ** json_insert() and json_replace() and those routines do not ** call jsonReturn() */ if( pNode->jnFlags & JNODE_RAW ){ + assert( pNode->eU==1 ); wx_sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, SQLITE_TRANSIENT); }else @@ -189264,15 +199521,18 @@ static void jsonReturn( assert( (pNode->jnFlags & JNODE_RAW)==0 ); if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ /* JSON formatted without any backslash-escapes */ + assert( pNode->eU==1 ); wx_sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, SQLITE_TRANSIENT); }else{ /* Translate JSON formatted string into raw text */ u32 i; u32 n = pNode->n; - const char *z = pNode->u.zJContent; + const char *z; char *zOut; u32 j; + assert( pNode->eU==1 ); + z = pNode->u.zJContent; zOut = wx_sqlite3_malloc( n+1 ); if( zOut==0 ){ wx_sqlite3_result_error_nomem(pCtx); @@ -189393,12 +199653,13 @@ static int jsonParseAddNode( const char *zContent /* Content */ ){ JsonNode *p; - if( pParse->nNode>=pParse->nAlloc ){ + if( pParse->aNode==0 || pParse->nNode>=pParse->nAlloc ){ return jsonParseAddNodeExpand(pParse, eType, n, zContent); } p = &pParse->aNode[pParse->nNode]; p->eType = (u8)eType; p->jnFlags = 0; + VVA( p->eU = zContent ? 1 : 0 ); p->n = n; p->u.zJContent = zContent; return pParse->nNode++; @@ -189409,7 +199670,7 @@ static int jsonParseAddNode( */ static int jsonIs4Hex(const char *z){ int i; - for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0; + for(i=0; i<4; i++) if( !wx_sqlite3Isxdigit(z[i]) ) return 0; return 1; } @@ -189428,13 +199689,13 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ int x; JsonNode *pNode; const char *z = pParse->zJson; - while( safe_isspace(z[i]) ){ i++; } + while( fast_isspace(z[i]) ){ i++; } if( (c = z[i])=='{' ){ /* Parse object */ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); if( iThis<0 ) return -1; for(j=i+1;;j++){ - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); if( x<0 ){ @@ -189447,14 +199708,14 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ if( pNode->eType!=JSON_STRING ) return -1; pNode->jnFlags |= JNODE_LABEL; j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( z[j]!=':' ) return -1; j++; x = jsonParseValue(pParse, j); pParse->iDepth--; if( x<0 ) return -1; j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } c = z[j]; if( c==',' ) continue; if( c!='}' ) return -1; @@ -189466,8 +199727,9 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ /* Parse array */ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); if( iThis<0 ) return -1; + memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); for(j=i+1;;j++){ - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); pParse->iDepth--; @@ -189476,7 +199738,7 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ return -1; } j = x; - while( safe_isspace(z[j]) ){ j++; } + while( fast_isspace(z[j]) ){ j++; } c = z[j]; if( c==',' ) continue; if( c!=']' ) return -1; @@ -189513,17 +199775,17 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ return j+1; }else if( c=='n' && strncmp(z+i,"null",4)==0 - && !safe_isalnum(z[i+4]) ){ + && !wx_sqlite3Isalnum(z[i+4]) ){ jsonParseAddNode(pParse, JSON_NULL, 0, 0); return i+4; }else if( c=='t' && strncmp(z+i,"true",4)==0 - && !safe_isalnum(z[i+4]) ){ + && !wx_sqlite3Isalnum(z[i+4]) ){ jsonParseAddNode(pParse, JSON_TRUE, 0, 0); return i+4; }else if( c=='f' && strncmp(z+i,"false",5)==0 - && !safe_isalnum(z[i+5]) ){ + && !wx_sqlite3Isalnum(z[i+5]) ){ jsonParseAddNode(pParse, JSON_FALSE, 0, 0); return i+5; }else if( c=='-' || (c>='0' && c<='9') ){ @@ -189594,7 +199856,7 @@ static int jsonParse( if( pParse->oom ) i = -1; if( i>0 ){ assert( pParse->iDepth==0 ); - while( safe_isspace(zJson[i]) ) i++; + while( fast_isspace(zJson[i]) ) i++; if( zJson[i] ) i = -1; } if( i<=0 ){ @@ -189730,6 +199992,7 @@ static JsonParse *jsonParseCached( ** a match. */ static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ + assert( pNode->eU==1 ); if( pNode->jnFlags & JNODE_RAW ){ if( pNode->n!=nKey ) return 0; return strncmp(pNode->u.zJContent, zKey, nKey)==0; @@ -189776,14 +200039,15 @@ static JsonNode *jsonLookupStep( *pzErr = zPath; return 0; } + testcase( nKey==0 ); }else{ zKey = zPath; for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} nKey = i; - } - if( nKey==0 ){ - *pzErr = zPath; - return 0; + if( nKey==0 ){ + *pzErr = zPath; + return 0; + } } j = 1; for(;;){ @@ -189795,6 +200059,7 @@ static JsonNode *jsonLookupStep( j += jsonNodeSize(&pRoot[j]); } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + assert( pRoot->eU==2 ); iRoot += pRoot->u.iAppend; pRoot = &pParse->aNode[iRoot]; j = 1; @@ -189809,8 +200074,10 @@ static JsonNode *jsonLookupStep( if( pParse->oom ) return 0; if( pNode ){ pRoot = &pParse->aNode[iRoot]; + assert( pRoot->eU==0 ); pRoot->u.iAppend = iStart - iRoot; pRoot->jnFlags |= JNODE_APPEND; + VVA( pRoot->eU = 2 ); pParse->aNode[iLabel].jnFlags |= JNODE_RAW; } return pNode; @@ -189818,7 +200085,7 @@ static JsonNode *jsonLookupStep( }else if( zPath[0]=='[' ){ i = 0; j = 1; - while( safe_isdigit(zPath[j]) ){ + while( wx_sqlite3Isdigit(zPath[j]) ){ i = i*10 + zPath[j] - '0'; j++; } @@ -189833,18 +200100,19 @@ static JsonNode *jsonLookupStep( j += jsonNodeSize(&pBase[j]); } if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; + assert( pBase->eU==2 ); iBase += pBase->u.iAppend; pBase = &pParse->aNode[iBase]; j = 1; } j = 2; - if( zPath[2]=='-' && safe_isdigit(zPath[3]) ){ + if( zPath[2]=='-' && wx_sqlite3Isdigit(zPath[3]) ){ unsigned int x = 0; j = 3; do{ x = x*10 + zPath[j] - '0'; j++; - }while( safe_isdigit(zPath[j]) ); + }while( wx_sqlite3Isdigit(zPath[j]) ); if( x>i ) return 0; i -= x; } @@ -189866,6 +200134,7 @@ static JsonNode *jsonLookupStep( j += jsonNodeSize(&pRoot[j]); } if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + assert( pRoot->eU==2 ); iRoot += pRoot->u.iAppend; pRoot = &pParse->aNode[iRoot]; j = 1; @@ -189881,8 +200150,10 @@ static JsonNode *jsonLookupStep( if( pParse->oom ) return 0; if( pNode ){ pRoot = &pParse->aNode[iRoot]; + assert( pRoot->eU==0 ); pRoot->u.iAppend = iStart - iRoot; pRoot->jnFlags |= JNODE_APPEND; + VVA( pRoot->eU = 2 ); } return pNode; } @@ -190036,9 +200307,13 @@ static void jsonParseFunc( } jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d", i, zType, x.aNode[i].n, x.aUp[i]); + assert( x.aNode[i].eU==0 || x.aNode[i].eU==1 ); if( x.aNode[i].u.zJContent!=0 ){ + assert( x.aNode[i].eU==1 ); jsonAppendRaw(&s, " ", 1); jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n); + }else{ + assert( x.aNode[i].eU==0 ); } jsonAppendRaw(&s, "\n", 1); } @@ -190056,7 +200331,7 @@ static void jsonTest1Func( int argc, wx_sqlite3_value **argv ){ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); wx_sqlite3_result_int(ctx, wx_sqlite3_value_subtype(argv[0])==JSON_SUBTYPE); } #endif /* SQLITE_DEBUG */ @@ -190077,7 +200352,7 @@ static void jsonQuoteFunc( wx_sqlite3_value **argv ){ JsonString jx; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); jsonInit(&jx, ctx); jsonAppendValue(&jx, argv[0]); @@ -190148,13 +200423,34 @@ static void jsonArrayLengthFunc( wx_sqlite3_result_int64(ctx, n); } +/* +** Bit values for the flags passed into jsonExtractFunc() or +** jsonSetFunc() via the user-data value. +*/ +#define JSON_JSON 0x01 /* Result is always JSON */ +#define JSON_SQL 0x02 /* Result is always SQL */ +#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ +#define JSON_ISSET 0x04 /* json_set(), not json_insert() */ + /* ** json_extract(JSON, PATH, ...) +** "->"(JSON,PATH) +** "->>"(JSON,PATH) ** -** Return the element described by PATH. Return NULL if there is no -** PATH element. If there are multiple PATHs, then return a JSON array -** with the result from each path. Throw an error if the JSON or any PATH -** is malformed. +** Return the element described by PATH. Return NULL if that PATH element +** is not found. +** +** If JSON_JSON is set or if more that one PATH argument is supplied then +** always return a JSON representation of the result. If JSON_SQL is set, +** then always return an SQL representation of the result. If neither flag +** is present and argc==2, then return JSON for objects and arrays and SQL +** for all other values. +** +** When multiple PATH arguments are supplied, the result is a JSON array +** containing the result of each PATH. +** +** Abbreviated JSON path expressions are allows if JSON_ABPATH, for +** compatibility with PG. */ static void jsonExtractFunc( wx_sqlite3_context *ctx, @@ -190164,35 +200460,77 @@ static void jsonExtractFunc( JsonParse *p; /* The parse */ JsonNode *pNode; const char *zPath; + int flags = SQLITE_PTR_TO_INT(wx_sqlite3_user_data(ctx)); JsonString jx; - int i; if( argc<2 ) return; p = jsonParseCached(ctx, argv, ctx); if( p==0 ) return; - jsonInit(&jx, ctx); - jsonAppendChar(&jx, '['); - for(i=1; inErr ) break; - if( argc>2 ){ + if( argc==2 ){ + /* With a single PATH argument */ + zPath = (const char*)wx_sqlite3_value_text(argv[1]); + if( zPath==0 ) return; + if( flags & JSON_ABPATH ){ + if( zPath[0]!='$' ){ + /* The -> and ->> operators accept abbreviated PATH arguments. This + ** is mostly for compatibility with PostgreSQL, but also for + ** convenience. + ** + ** NUMBER ==> $[NUMBER] // PG compatible + ** LABEL ==> $.LABEL // PG compatible + ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience + */ + jsonInit(&jx, ctx); + if( wx_sqlite3Isdigit(zPath[0]) ){ + jsonAppendRaw(&jx, "$[", 2); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendRaw(&jx, "]", 2); + }else{ + jsonAppendRaw(&jx, "$.", 1 + (zPath[0]!='[')); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendChar(&jx, 0); + } + pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx); + jsonReset(&jx); + }else{ + pNode = jsonLookup(p, zPath, 0, ctx); + } + if( pNode ){ + if( flags & JSON_JSON ){ + jsonReturnJson(pNode, ctx, 0); + }else{ + jsonReturn(pNode, ctx, 0); + wx_sqlite3_result_subtype(ctx, 0); + } + } + }else{ + pNode = jsonLookup(p, zPath, 0, ctx); + if( p->nErr==0 && pNode ) jsonReturn(pNode, ctx, 0); + } + }else{ + /* Two or more PATH arguments results in a JSON array with each + ** element of the array being the value selected by one of the PATHs */ + int i; + jsonInit(&jx, ctx); + jsonAppendChar(&jx, '['); + for(i=1; inErr ) break; jsonAppendSeparator(&jx); if( pNode ){ jsonRenderNode(pNode, &jx, 0); }else{ jsonAppendRaw(&jx, "null", 4); } - }else if( pNode ){ - jsonReturn(pNode, ctx, 0); } + if( i==argc ){ + jsonAppendChar(&jx, ']'); + jsonResult(&jx); + wx_sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } + jsonReset(&jx); } - if( argc>2 && i==argc ){ - jsonAppendChar(&jx, ']'); - jsonResult(&jx); - wx_sqlite3_result_subtype(ctx, JSON_SUBTYPE); - } - jsonReset(&jx); } /* This is the RFC 7396 MergePatch algorithm. @@ -190208,7 +200546,7 @@ static JsonNode *jsonMergePatch( if( pPatch->eType!=JSON_OBJECT ){ return pPatch; } - assert( iTarget>=0 && iTargetnNode ); + assert( iTargetnNode ); pTarget = &pParse->aNode[iTarget]; assert( (pPatch->jnFlags & JNODE_APPEND)==0 ); if( pTarget->eType!=JSON_OBJECT ){ @@ -190221,6 +200559,7 @@ static JsonNode *jsonMergePatch( const char *zKey; assert( pPatch[i].eType==JSON_STRING ); assert( pPatch[i].jnFlags & JNODE_LABEL ); + assert( pPatch[i].eU==1 ); nKey = pPatch[i].n; zKey = pPatch[i].u.zJContent; assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); @@ -190237,6 +200576,12 @@ static JsonNode *jsonMergePatch( if( pNew==0 ) return 0; pTarget = &pParse->aNode[iTarget]; if( pNew!=&pTarget[j+1] ){ + assert( pTarget[j+1].eU==0 + || pTarget[j+1].eU==1 + || pTarget[j+1].eU==2 ); + testcase( pTarget[j+1].eU==1 ); + testcase( pTarget[j+1].eU==2 ); + VVA( pTarget[j+1].eU = 5 ); pTarget[j+1].u.pPatch = pNew; pTarget[j+1].jnFlags |= JNODE_PATCH; } @@ -190252,9 +200597,14 @@ static JsonNode *jsonMergePatch( if( pParse->oom ) return 0; jsonRemoveAllNulls(pPatch); pTarget = &pParse->aNode[iTarget]; + assert( pParse->aNode[iRoot].eU==0 || pParse->aNode[iRoot].eU==2 ); + testcase( pParse->aNode[iRoot].eU==2 ); pParse->aNode[iRoot].jnFlags |= JNODE_APPEND; + VVA( pParse->aNode[iRoot].eU = 2 ); pParse->aNode[iRoot].u.iAppend = iStart - iRoot; iRoot = iStart; + assert( pParse->aNode[iPatch].eU==0 ); + VVA( pParse->aNode[iPatch].eU = 5 ); pParse->aNode[iPatch].jnFlags |= JNODE_PATCH; pParse->aNode[iPatch].u.pPatch = &pPatch[i+1]; } @@ -190276,7 +200626,7 @@ static void jsonPatchFunc( JsonParse y; /* The patch */ JsonNode *pResult; /* The result of the merge */ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); if( jsonParse(&x, ctx, (const char*)wx_sqlite3_value_text(argv[0])) ) return; if( jsonParse(&y, ctx, (const char*)wx_sqlite3_value_text(argv[1])) ){ jsonParseReset(&x); @@ -190396,11 +200746,15 @@ static void jsonReplaceFunc( pNode = jsonLookup(&x, zPath, 0, ctx); if( x.nErr ) goto replace_err; if( pNode ){ + assert( pNode->eU==0 || pNode->eU==1 || pNode->eU==4 ); + testcase( pNode->eU!=0 && pNode->eU!=1 ); pNode->jnFlags |= (u8)JNODE_REPLACE; + VVA( pNode->eU = 4 ); pNode->u.iReplace = i + 1; } } if( x.aNode[0].jnFlags & JNODE_REPLACE ){ + assert( x.aNode[0].eU==4 ); wx_sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); }else{ jsonReturnJson(x.aNode, ctx, argv); @@ -190409,6 +200763,7 @@ replace_err: jsonParseReset(&x); } + /* ** json_set(JSON, PATH, VALUE, ...) ** @@ -190431,7 +200786,7 @@ static void jsonSetFunc( const char *zPath; u32 i; int bApnd; - int bIsSet = *(int*)wx_sqlite3_user_data(ctx); + int bIsSet = wx_sqlite3_user_data(ctx)!=0; if( argc<1 ) return; if( (argc&1)==0 ) { @@ -190450,11 +200805,15 @@ static void jsonSetFunc( }else if( x.nErr ){ goto jsonSetDone; }else if( pNode && (bApnd || bIsSet) ){ + testcase( pNode->eU!=0 && pNode->eU!=1 ); + assert( pNode->eU!=3 && pNode->eU!=5 ); + VVA( pNode->eU = 4 ); pNode->jnFlags |= (u8)JNODE_REPLACE; pNode->u.iReplace = i + 1; } } if( x.aNode[0].jnFlags & JNODE_REPLACE ){ + assert( x.aNode[0].eU==4 ); wx_sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); }else{ jsonReturnJson(x.aNode, ctx, argv); @@ -190467,8 +200826,8 @@ jsonSetDone: ** json_type(JSON) ** json_type(JSON, PATH) ** -** Return the top-level "type" of a JSON string. Throw an error if -** either the JSON or PATH inputs are not well-formed. +** Return the top-level "type" of a JSON string. json_type() raises an +** error if either the JSON or PATH inputs are not well-formed. */ static void jsonTypeFunc( wx_sqlite3_context *ctx, @@ -190504,7 +200863,7 @@ static void jsonValidFunc( wx_sqlite3_value **argv ){ JsonParse *p; /* The parse */ - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); p = jsonParseCached(ctx, argv, 0); wx_sqlite3_result_int(ctx, p!=0); } @@ -190524,7 +200883,7 @@ static void jsonArrayStep( wx_sqlite3_value **argv ){ JsonString *pStr; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); pStr = (JsonString*)wx_sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ @@ -190532,8 +200891,8 @@ static void jsonArrayStep( jsonAppendChar(pStr, '['); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); - pStr->pCtx = ctx; } + pStr->pCtx = ctx; jsonAppendValue(pStr, argv[0]); } } @@ -190584,8 +200943,8 @@ static void jsonGroupInverse( char *z; char c; JsonString *pStr; - UNUSED_PARAM(argc); - UNUSED_PARAM(argv); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); pStr = (JsonString*)wx_sqlite3_aggregate_context(ctx, 0); #ifdef NEVER /* pStr is always non-NULL since jsonArrayStep() or jsonObjectStep() will @@ -190593,11 +200952,7 @@ static void jsonGroupInverse( if( NEVER(!pStr) ) return; #endif z = pStr->zBuf; - for(i=1; (c = z[i])!=',' || inStr || nNest; i++){ - if( i>=pStr->nUsed ){ - pStr->nUsed = 1; - return; - } + for(i=1; inUsed && ((c = z[i])!=',' || inStr || nNest); i++){ if( c=='"' ){ inStr = !inStr; }else if( c=='\\' ){ @@ -190607,8 +200962,13 @@ static void jsonGroupInverse( if( c=='}' || c==']' ) nNest--; } } - pStr->nUsed -= i; - memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1); + if( inUsed ){ + pStr->nUsed -= i; + memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1); + z[pStr->nUsed] = 0; + }else{ + pStr->nUsed = 1; + } } #else # define jsonGroupInverse 0 @@ -190628,7 +200988,7 @@ static void jsonObjectStep( JsonString *pStr; const char *z; u32 n; - UNUSED_PARAM(argc); + UNUSED_PARAMETER(argc); pStr = (JsonString*)wx_sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ @@ -190636,8 +200996,8 @@ static void jsonObjectStep( jsonAppendChar(pStr, '{'); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); - pStr->pCtx = ctx; } + pStr->pCtx = ctx; z = (const char*)wx_sqlite3_value_text(argv[0]); n = (u32)wx_sqlite3_value_bytes(argv[0]); jsonAppendString(pStr, z, n); @@ -190719,10 +201079,10 @@ static int jsonEachConnect( #define JEACH_JSON 8 #define JEACH_ROOT 9 - UNUSED_PARAM(pzErr); - UNUSED_PARAM(argv); - UNUSED_PARAM(argc); - UNUSED_PARAM(pAux); + UNUSED_PARAMETER(pzErr); + UNUSED_PARAMETER(argv); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(pAux); rc = wx_sqlite3_declare_vtab(db, "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path," "json HIDDEN,root HIDDEN)"); @@ -190745,7 +201105,7 @@ static int jsonEachDisconnect(wx_sqlite3_vtab *pVtab){ static int jsonEachOpenEach(wx_sqlite3_vtab *p, wx_sqlite3_vtab_cursor **ppCursor){ JsonEachCursor *pCur; - UNUSED_PARAM(p); + UNUSED_PARAMETER(p); pCur = wx_sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); @@ -190804,6 +201164,9 @@ static int jsonEachNext(wx_sqlite3_vtab_cursor *cur){ JsonNode *pUp = &p->sParse.aNode[iUp]; p->eType = pUp->eType; if( pUp->eType==JSON_ARRAY ){ + assert( pUp->eU==0 || pUp->eU==3 ); + testcase( pUp->eU==3 ); + VVA( pUp->eU = 3 ); if( iUp==p->i-1 ){ pUp->u.iKey = 0; }else{ @@ -190832,6 +201195,33 @@ static int jsonEachNext(wx_sqlite3_vtab_cursor *cur){ return SQLITE_OK; } +/* Append an object label to the JSON Path being constructed +** in pStr. +*/ +static void jsonAppendObjectPathElement( + JsonString *pStr, + JsonNode *pNode +){ + int jj, nn; + const char *z; + assert( pNode->eType==JSON_STRING ); + assert( pNode->jnFlags & JNODE_LABEL ); + assert( pNode->eU==1 ); + z = pNode->u.zJContent; + nn = pNode->n; + assert( nn>=2 ); + assert( z[0]=='"' ); + assert( z[nn-1]=='"' ); + if( nn>2 && wx_sqlite3Isalpha(z[1]) ){ + for(jj=2; jjsParse.aNode[i]; pUp = &p->sParse.aNode[iUp]; if( pUp->eType==JSON_ARRAY ){ + assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) ); + testcase( pUp->eU==0 ); jsonPrintf(30, pStr, "[%d]", pUp->u.iKey); }else{ assert( pUp->eType==JSON_OBJECT ); if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--; - assert( pNode->eType==JSON_STRING ); - assert( pNode->jnFlags & JNODE_LABEL ); - jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1); + jsonAppendObjectPathElement(pStr, pNode); } } @@ -190877,6 +201267,7 @@ static int jsonEachColumn( u32 iKey; if( p->bRecursive ){ if( p->iRowid==0 ) break; + assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 ); iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey; }else{ iKey = p->iRowid; @@ -190926,7 +201317,7 @@ static int jsonEachColumn( if( p->eType==JSON_ARRAY ){ jsonPrintf(30, &x, "[%d]", p->iRowid); }else if( p->eType==JSON_OBJECT ){ - jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1); + jsonAppendObjectPathElement(&x, pThis); } } jsonResult(&x); @@ -190984,7 +201375,7 @@ static int jsonEachBestIndex( /* This implementation assumes that JSON and ROOT are the last two ** columns in the table */ assert( JEACH_ROOT == JEACH_JSON+1 ); - UNUSED_PARAM(tab); + UNUSED_PARAMETER(tab); aIdx[0] = aIdx[1] = -1; pConstraint = pIdxInfo->aConstraint; for(i=0; inConstraint; i++, pConstraint++){ @@ -190993,6 +201384,7 @@ static int jsonEachBestIndex( if( pConstraint->iColumn < JEACH_JSON ) continue; iCol = pConstraint->iColumn - JEACH_JSON; assert( iCol==0 || iCol==1 ); + testcase( iCol==0 ); iMask = 1 << iCol; if( pConstraint->usable==0 ){ unusableMask |= iMask; @@ -191001,6 +201393,13 @@ static int jsonEachBestIndex( idxMask |= iMask; } } + if( pIdxInfo->nOrderBy>0 + && pIdxInfo->aOrderBy[0].iColumn<0 + && pIdxInfo->aOrderBy[0].desc==0 + ){ + pIdxInfo->orderByConsumed = 1; + } + if( (unusableMask & ~idxMask)!=0 ){ /* If there are any unusable constraints on JSON or ROOT, then reject ** this entire plan */ @@ -191039,8 +201438,8 @@ static int jsonEachFilter( const char *zRoot = 0; wx_sqlite3_int64 n; - UNUSED_PARAM(idxStr); - UNUSED_PARAM(argc); + UNUSED_PARAMETER(idxStr); + UNUSED_PARAMETER(argc); jsonEachCursorReset(p); if( idxNum==0 ) return SQLITE_OK; z = (const char*)wx_sqlite3_value_text(argv[0]); @@ -191090,6 +201489,8 @@ static int jsonEachFilter( p->iBegin = p->i = (int)(pNode - p->sParse.aNode); p->eType = pNode->eType; if( p->eType>=JSON_ARRAY ){ + assert( pNode->eU==0 ); + VVA( pNode->eU = 3 ); pNode->u.iKey = 0; p->iEnd = p->i + pNode->n + 1; if( p->bRecursive ){ @@ -191163,108 +201564,68 @@ static wx_sqlite3_module jsonTreeModule = { 0 /* xShadowName */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ - -/**************************************************************************** -** The following routines are the only publically visible identifiers in this -** file. Call the following routines in order to register the various SQL -** functions and the virtual table implemented by this file. -****************************************************************************/ - -SQLITE_PRIVATE int wx_sqlite3Json1Init(wx_sqlite3 *db){ - int rc = SQLITE_OK; - unsigned int i; - static const struct { - const char *zName; - int nArg; - int flag; - void (*xFunc)(wx_sqlite3_context*,int,wx_sqlite3_value**); - } aFunc[] = { - { "json", 1, 0, jsonRemoveFunc }, - { "json_array", -1, 0, jsonArrayFunc }, - { "json_array_length", 1, 0, jsonArrayLengthFunc }, - { "json_array_length", 2, 0, jsonArrayLengthFunc }, - { "json_extract", -1, 0, jsonExtractFunc }, - { "json_insert", -1, 0, jsonSetFunc }, - { "json_object", -1, 0, jsonObjectFunc }, - { "json_patch", 2, 0, jsonPatchFunc }, - { "json_quote", 1, 0, jsonQuoteFunc }, - { "json_remove", -1, 0, jsonRemoveFunc }, - { "json_replace", -1, 0, jsonReplaceFunc }, - { "json_set", -1, 1, jsonSetFunc }, - { "json_type", 1, 0, jsonTypeFunc }, - { "json_type", 2, 0, jsonTypeFunc }, - { "json_valid", 1, 0, jsonValidFunc }, - +#endif /* !defined(SQLITE_OMIT_JSON) */ + +/* +** Register JSON functions. +*/ +SQLITE_PRIVATE void wx_sqlite3RegisterJsonFunctions(void){ +#ifndef SQLITE_OMIT_JSON + static FuncDef aJsonFunc[] = { + JFUNCTION(json, 1, 0, jsonRemoveFunc), + JFUNCTION(json_array, -1, 0, jsonArrayFunc), + JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), + JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), + JFUNCTION(json_extract, -1, 0, jsonExtractFunc), + JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), + JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), + JFUNCTION(json_insert, -1, 0, jsonSetFunc), + JFUNCTION(json_object, -1, 0, jsonObjectFunc), + JFUNCTION(json_patch, 2, 0, jsonPatchFunc), + JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), + JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), + JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), + JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc), + JFUNCTION(json_type, 1, 0, jsonTypeFunc), + JFUNCTION(json_type, 2, 0, jsonTypeFunc), + JFUNCTION(json_valid, 1, 0, jsonValidFunc), #if SQLITE_DEBUG - /* DEBUG and TESTING functions */ - { "json_parse", 1, 0, jsonParseFunc }, - { "json_test1", 1, 0, jsonTest1Func }, -#endif + JFUNCTION(json_parse, 1, 0, jsonParseFunc), + JFUNCTION(json_test1, 1, 0, jsonTest1Func), +#endif + WAGGREGATE(json_group_array, 1, 0, 0, + jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), + WAGGREGATE(json_group_object, 2, 0, 0, + jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC) }; + wx_sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc)); +#endif +} + +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) +/* +** Register the JSON table-valued functions +*/ +SQLITE_PRIVATE int wx_sqlite3JsonTableFunctions(wx_sqlite3 *db){ + int rc = SQLITE_OK; static const struct { - const char *zName; - int nArg; - void (*xStep)(wx_sqlite3_context*,int,wx_sqlite3_value**); - void (*xFinal)(wx_sqlite3_context*); - void (*xValue)(wx_sqlite3_context*); - } aAgg[] = { - { "json_group_array", 1, - jsonArrayStep, jsonArrayFinal, jsonArrayValue }, - { "json_group_object", 2, - jsonObjectStep, jsonObjectFinal, jsonObjectValue }, - }; -#ifndef SQLITE_OMIT_VIRTUALTABLE - static const struct { - const char *zName; - wx_sqlite3_module *pModule; + const char *zName; + wx_sqlite3_module *pModule; } aMod[] = { { "json_each", &jsonEachModule }, { "json_tree", &jsonTreeModule }, }; -#endif - static const int enc = - SQLITE_UTF8 | - SQLITE_DETERMINISTIC | - SQLITE_INNOCUOUS; - for(i=0; i */ /* #include */ @@ -191403,7 +201781,9 @@ struct Rtree { u8 nBytesPerCell; /* Bytes consumed per cell */ u8 inWrTrans; /* True if inside write transaction */ u8 nAux; /* # of auxiliary columns in %_rowid */ +#ifdef SQLITE_ENABLE_GEOPOLY u8 nAuxNotNull; /* Number of initial not-null aux columns */ +#endif #ifdef SQLITE_DEBUG u8 bCorrupt; /* Shadow table corruption detected */ #endif @@ -191685,7 +202065,12 @@ struct RtreeMatchArg { ** it is not, make it a no-op. */ #ifndef SQLITE_AMALGAMATION -# define testcase(X) +# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) + unsigned int wx_sqlite3RtreeTestcase = 0; +# define testcase(X) if( X ){ wx_sqlite3RtreeTestcase += __LINE__; } +# else +# define testcase(X) +# endif #endif /* @@ -191745,7 +202130,7 @@ static int readInt16(u8 *p){ return (p[0]<<8) + p[1]; } static void readCoord(u8 *p, RtreeCoord *pCoord){ - assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */ + assert( (((wx_sqlite3_uint64)p)&3)==0 ); /* p is always 4-byte aligned */ #if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 pCoord->u = _byteswap_ulong(*(u32*)p); #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 @@ -191799,7 +202184,7 @@ static void writeInt16(u8 *p, int i){ } static int writeCoord(u8 *p, RtreeCoord *pCoord){ u32 i; - assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */ + assert( (((wx_sqlite3_uint64)p)&3)==0 ); /* p is always 4-byte aligned */ assert( sizeof(RtreeCoord)==4 ); assert( sizeof(u32)==4 ); #if SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 @@ -191934,18 +202319,6 @@ static void nodeBlobReset(Rtree *pRtree){ } } -/* -** Check to see if pNode is the same as pParent or any of the parents -** of pParent. -*/ -static int nodeInParentChain(const RtreeNode *pNode, const RtreeNode *pParent){ - do{ - if( pNode==pParent ) return 1; - pParent = pParent->pParent; - }while( pParent ); - return 0; -} - /* ** Obtain a reference to an r-tree node. */ @@ -191962,14 +202335,7 @@ static int nodeAcquire( ** increase its reference count and return it. */ if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){ - if( pParent && !pNode->pParent ){ - if( nodeInParentChain(pNode, pParent) ){ - RTREE_IS_CORRUPT(pRtree); - return SQLITE_CORRUPT_VTAB; - } - pParent->nRef++; - pNode->pParent = pParent; - }else if( pParent && pNode->pParent && pParent!=pNode->pParent ){ + if( pParent && pParent!=pNode->pParent ){ RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -192027,7 +202393,7 @@ static int nodeAcquire( ** are the leaves, and so on. If the depth as specified on the root node ** is greater than RTREE_MAX_DEPTH, the r-tree structure must be corrupt. */ - if( pNode && rc==SQLITE_OK && iNode==1 ){ + if( rc==SQLITE_OK && pNode && iNode==1 ){ pRtree->iDepth = readInt16(pNode->zData); if( pRtree->iDepth>RTREE_MAX_DEPTH ){ rc = SQLITE_CORRUPT_VTAB; @@ -192546,24 +202912,33 @@ static void rtreeNonleafConstraint( assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE || p->op==RTREE_FALSE ); - assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */ + assert( (((wx_sqlite3_uint64)pCellData)&3)==0 ); /* 4-byte aligned */ switch( p->op ){ case RTREE_TRUE: return; /* Always satisfied */ case RTREE_FALSE: break; /* Never satisfied */ + case RTREE_EQ: + RTREE_DECODE_COORD(eInt, pCellData, val); + /* val now holds the lower bound of the coordinate pair */ + if( p->u.rValue>=val ){ + pCellData += 4; + RTREE_DECODE_COORD(eInt, pCellData, val); + /* val now holds the upper bound of the coordinate pair */ + if( p->u.rValue<=val ) return; + } + break; case RTREE_LE: case RTREE_LT: - case RTREE_EQ: RTREE_DECODE_COORD(eInt, pCellData, val); /* val now holds the lower bound of the coordinate pair */ if( p->u.rValue>=val ) return; - if( p->op!=RTREE_EQ ) break; /* RTREE_LE and RTREE_LT end here */ - /* Fall through for the RTREE_EQ case */ + break; - default: /* RTREE_GT or RTREE_GE, or fallthrough of RTREE_EQ */ + default: pCellData += 4; RTREE_DECODE_COORD(eInt, pCellData, val); /* val now holds the upper bound of the coordinate pair */ if( p->u.rValue<=val ) return; + break; } *peWithin = NOT_WITHIN; } @@ -192590,7 +202965,7 @@ static void rtreeLeafConstraint( || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE || p->op==RTREE_FALSE ); pCellData += 8 + p->iCoord*4; - assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */ + assert( (((wx_sqlite3_uint64)pCellData)&3)==0 ); /* 4-byte aligned */ RTREE_DECODE_COORD(eInt, pCellData, xN); switch( p->op ){ case RTREE_TRUE: return; /* Always satisfied */ @@ -192633,11 +203008,12 @@ static int nodeRowidIndex( */ static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){ RtreeNode *pParent = pNode->pParent; - if( pParent ){ + if( ALWAYS(pParent) ){ return nodeRowidIndex(pRtree, pParent, pNode->iNode, piIndex); + }else{ + *piIndex = -1; + return SQLITE_OK; } - *piIndex = -1; - return SQLITE_OK; } /* @@ -192760,7 +203136,8 @@ static RtreeSearchPoint *rtreeSearchPointNew( pNew = rtreeEnqueue(pCur, rScore, iLevel); if( pNew==0 ) return 0; ii = (int)(pNew - pCur->aPoint) + 1; - if( iiaNode[ii]==0 ); pCur->aNode[ii] = pCur->aNode[0]; }else{ @@ -192821,7 +203198,7 @@ static void rtreeSearchPointPop(RtreeCursor *p){ if( p->bPoint ){ p->anQueue[p->sPoint.iLevel]--; p->bPoint = 0; - }else if( p->nPoint ){ + }else if( ALWAYS(p->nPoint) ){ p->anQueue[p->aPoint[0].iLevel]--; n = --p->nPoint; p->aPoint[0] = p->aPoint[n]; @@ -192962,7 +203339,7 @@ static int rtreeRowid(wx_sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid) RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); - if( rc==SQLITE_OK && p ){ + if( rc==SQLITE_OK && ALWAYS(p) ){ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); } return rc; @@ -192980,7 +203357,7 @@ static int rtreeColumn(wx_sqlite3_vtab_cursor *cur, wx_sqlite3_context *ctx, int RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc ) return rc; - if( p==0 ) return SQLITE_OK; + if( NEVER(p==0) ) return SQLITE_OK; if( i==0 ){ wx_sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); }else if( i<=pRtree->nDim2 ){ @@ -193179,8 +203556,11 @@ static int rtreeFilter( } if( rc==SQLITE_OK ){ RtreeSearchPoint *pNew; + assert( pCsr->bPoint==0 ); /* Due to the resetCursor() call above */ pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1)); - if( pNew==0 ) return SQLITE_NOMEM; + if( NEVER(pNew==0) ){ /* Because pCsr->bPoint was FALSE */ + return SQLITE_NOMEM; + } pNew->id = 1; pNew->iCell = 0; pNew->eWithin = PARTLY_WITHIN; @@ -193257,7 +203637,7 @@ static int rtreeBestIndex(wx_sqlite3_vtab *tab, wx_sqlite3_index_info *pIdxInfo) struct wx_sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; if( bMatch==0 && p->usable - && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ + && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){ /* We have an equality constraint on the rowid. Use strategy 1. */ int jj; @@ -193463,7 +203843,7 @@ static int ChooseLeaf( int nCell = NCELL(pNode); RtreeCell cell; - RtreeNode *pChild; + RtreeNode *pChild = 0; RtreeCell *aCell = 0; @@ -193510,12 +203890,19 @@ static int AdjustTree( ){ RtreeNode *p = pNode; int cnt = 0; + int rc; while( p->pParent ){ RtreeNode *pParent = p->pParent; RtreeCell cell; int iCell; - if( (++cnt)>1000 || nodeParentIndex(pRtree, p, &iCell) ){ + cnt++; + if( NEVER(cnt>100) ){ + RTREE_IS_CORRUPT(pRtree); + return SQLITE_CORRUPT_VTAB; + } + rc = nodeParentIndex(pRtree, p, &iCell); + if( NEVER(rc!=SQLITE_OK) ){ RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -193804,12 +204191,17 @@ static int updateMapping( xSetMapping = ((iHeight==0)?rowidWrite:parentWrite); if( iHeight>0 ){ RtreeNode *pChild = nodeHashLookup(pRtree, iRowid); + RtreeNode *p; + for(p=pNode; p; p=p->pParent){ + if( p==pChild ) return SQLITE_CORRUPT_VTAB; + } if( pChild ){ nodeRelease(pRtree, pChild->pParent); nodeReference(pNode); pChild->pParent = pNode; } } + if( NEVER(pNode==0) ) return SQLITE_ERROR; return xSetMapping(pRtree, iRowid, pNode->iNode); } @@ -193899,11 +204291,12 @@ static int SplitNode( RtreeNode *pParent = pLeft->pParent; int iCell; rc = nodeParentIndex(pRtree, pLeft, &iCell); - if( rc==SQLITE_OK ){ + if( ALWAYS(rc==SQLITE_OK) ){ nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell); rc = AdjustTree(pRtree, pParent, &leftbbox); + assert( rc==SQLITE_OK ); } - if( rc!=SQLITE_OK ){ + if( NEVER(rc!=SQLITE_OK) ){ goto splitnode_out; } } @@ -193978,7 +204371,7 @@ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ */ iNode = wx_sqlite3_column_int64(pRtree->pReadParent, 0); for(pTest=pLeaf; pTest && pTest->iNode!=iNode; pTest=pTest->pParent); - if( !pTest ){ + if( pTest==0 ){ rc2 = nodeAcquire(pRtree, iNode, 0, &pChild->pParent); } } @@ -194009,6 +204402,7 @@ static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){ pParent = pNode->pParent; pNode->pParent = 0; rc = deleteCell(pRtree, pParent, iCell, iHeight+1); + testcase( rc!=SQLITE_OK ); } rc2 = nodeRelease(pRtree, pParent); if( rc==SQLITE_OK ){ @@ -194231,7 +204625,7 @@ static int rtreeInsertCell( } }else{ rc = AdjustTree(pRtree, pNode, pCell); - if( rc==SQLITE_OK ){ + if( ALWAYS(rc==SQLITE_OK) ){ if( iHeight==0 ){ rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode); }else{ @@ -194337,7 +204731,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, wx_sqlite3_int64 iDelete){ int rc2; RtreeNode *pChild = 0; i64 iChild = nodeGetRowid(pRtree, pRoot, 0); - rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); + rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); /* tag-20210916a */ if( rc==SQLITE_OK ){ rc = removeNode(pRtree, pChild, pRtree->iDepth-1); } @@ -194470,7 +204864,7 @@ static int rtreeUpdate( rtreeReference(pRtree); assert(nData>=1); - cell.iRowid = 0; /* Used only to suppress a compiler warning */ + memset(&cell, 0, sizeof(cell)); /* Constraint handling. A write operation on an r-tree table may return ** SQLITE_CONSTRAINT for two reasons: @@ -194672,7 +205066,7 @@ static int rtreeQueryStat1(wx_sqlite3 *db, Rtree *pRtree){ char *zSql; wx_sqlite3_stmt *p; int rc; - i64 nRow = 0; + i64 nRow = RTREE_MIN_ROWEST; rc = wx_sqlite3_table_column_metadata( db, pRtree->zDb, "sqlite_stat1",0,0,0,0,0,0 @@ -194689,20 +205083,10 @@ static int rtreeQueryStat1(wx_sqlite3 *db, Rtree *pRtree){ if( rc==SQLITE_OK ){ if( wx_sqlite3_step(p)==SQLITE_ROW ) nRow = wx_sqlite3_column_int64(p, 0); rc = wx_sqlite3_finalize(p); - }else if( rc!=SQLITE_NOMEM ){ - rc = SQLITE_OK; - } - - if( rc==SQLITE_OK ){ - if( nRow==0 ){ - pRtree->nRowEst = RTREE_DEFAULT_ROWEST; - }else{ - pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); - } } wx_sqlite3_free(zSql); } - + pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); return rc; } @@ -194852,9 +205236,12 @@ static int rtreeSqlInit( wx_sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix); for(ii=0; iinAux; ii++){ if( ii ) wx_sqlite3_str_append(p, ",", 1); +#ifdef SQLITE_ENABLE_GEOPOLY if( iinAuxNotNull ){ wx_sqlite3_str_appendf(p,"a%d=coalesce(?%d,a%d)",ii,ii+2,ii); - }else{ + }else +#endif + { wx_sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2); } } @@ -195119,6 +205506,7 @@ static void rtreenode(wx_sqlite3_context *ctx, int nArg, wx_sqlite3_value **apAr tree.nDim2 = tree.nDim*2; tree.nBytesPerCell = 8 + 8 * tree.nDim; node.zData = (u8 *)wx_sqlite3_value_blob(apArg[1]); + if( node.zData==0 ) return; nData = wx_sqlite3_value_bytes(apArg[1]); if( nData<4 ) return; if( nDataz[0]) ) p->z++; + while( fast_isspace(p->z[0]) ) p->z++; return p->z[0]; } @@ -195943,11 +206335,16 @@ static GeoPoly *geopolyFuncParam( ){ GeoPoly *p = 0; int nByte; + testcase( pCtx==0 ); if( wx_sqlite3_value_type(pVal)==SQLITE_BLOB - && (nByte = wx_sqlite3_value_bytes(pVal))>=(4+6*sizeof(GeoCoord)) + && (nByte = wx_sqlite3_value_bytes(pVal))>=(int)(4+6*sizeof(GeoCoord)) ){ const unsigned char *a = wx_sqlite3_value_blob(pVal); int nVertex; + if( a==0 ){ + if( pCtx ) wx_sqlite3_result_error_nomem(pCtx); + return 0; + } nVertex = (a[1]<<16) + (a[2]<<8) + a[3]; if( (a[0]==0 || a[0]==1) && (nVertex*2*sizeof(GeoCoord) + 4)==(unsigned int)nByte @@ -195998,6 +206395,7 @@ static void geopolyBlobFunc( wx_sqlite3_value **argv ){ GeoPoly *p = geopolyFuncParam(context, argv[0], 0); + (void)argc; if( p ){ wx_sqlite3_result_blob(context, p->hdr, 4+8*p->nVertex, SQLITE_TRANSIENT); @@ -196017,6 +206415,7 @@ static void geopolyJsonFunc( wx_sqlite3_value **argv ){ GeoPoly *p = geopolyFuncParam(context, argv[0], 0); + (void)argc; if( p ){ wx_sqlite3 *db = wx_sqlite3_context_db_handle(context); wx_sqlite3_str *x = wx_sqlite3_str_new(db); @@ -196098,6 +206497,7 @@ static void geopolyXformFunc( double F = wx_sqlite3_value_double(argv[6]); GeoCoord x1, y1, x0, y0; int ii; + (void)argc; if( p ){ for(ii=0; iinVertex; ii++){ x0 = GeoX(p,ii); @@ -196148,6 +206548,7 @@ static void geopolyAreaFunc( wx_sqlite3_value **argv ){ GeoPoly *p = geopolyFuncParam(context, argv[0], 0); + (void)argc; if( p ){ wx_sqlite3_result_double(context, geopolyArea(p)); wx_sqlite3_free(p); @@ -196173,6 +206574,7 @@ static void geopolyCcwFunc( wx_sqlite3_value **argv ){ GeoPoly *p = geopolyFuncParam(context, argv[0], 0); + (void)argc; if( p ){ if( geopolyArea(p)<0.0 ){ int ii, jj; @@ -196227,6 +206629,7 @@ static void geopolyRegularFunc( int n = wx_sqlite3_value_int(argv[3]); int i; GeoPoly *p; + (void)argc; if( n<3 || r<=0.0 ) return; if( n>1000 ) n = 1000; @@ -196321,7 +206724,7 @@ static GeoPoly *geopolyBBox( aCoord[2].f = mnY; aCoord[3].f = mxY; } - }else{ + }else if( aCoord ){ memset(aCoord, 0, sizeof(RtreeCoord)*4); } return pOut; @@ -196336,6 +206739,7 @@ static void geopolyBBoxFunc( wx_sqlite3_value **argv ){ GeoPoly *p = geopolyBBox(context, argv[0], 0, 0); + (void)argc; if( p ){ wx_sqlite3_result_blob(context, p->hdr, 4+8*p->nVertex, SQLITE_TRANSIENT); @@ -196363,6 +206767,7 @@ static void geopolyBBoxStep( ){ RtreeCoord a[4]; int rc = SQLITE_OK; + (void)argc; (void)geopolyBBox(context, argv[0], a, &rc); if( rc==SQLITE_OK ){ GeoBBox *pBBox; @@ -196451,6 +206856,8 @@ static void geopolyContainsPointFunc( int v = 0; int cnt = 0; int ii; + (void)argc; + if( p1==0 ) return; for(ii=0; iinVertex-1; ii++){ v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii), @@ -196490,6 +206897,7 @@ static void geopolyWithinFunc( ){ GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0); GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0); + (void)argc; if( p1 && p2 ){ int x = geopolyOverlap(p1, p2); if( x<0 ){ @@ -196772,11 +207180,11 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){ }else{ /* Remove a segment */ if( pActive==pThisEvent->pSeg ){ - pActive = pActive->pNext; + pActive = ALWAYS(pActive) ? pActive->pNext : 0; }else{ for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){ if( pSeg->pNext==pThisEvent->pSeg ){ - pSeg->pNext = pSeg->pNext->pNext; + pSeg->pNext = ALWAYS(pSeg->pNext) ? pSeg->pNext->pNext : 0; break; } } @@ -196820,6 +207228,7 @@ static void geopolyOverlapFunc( ){ GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0); GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0); + (void)argc; if( p1 && p2 ){ int x = geopolyOverlap(p1, p2); if( x<0 ){ @@ -196840,8 +207249,12 @@ static void geopolyDebugFunc( int argc, wx_sqlite3_value **argv ){ + (void)context; + (void)argc; #ifdef GEOPOLY_ENABLE_DEBUG geo_debug = wx_sqlite3_value_int(argv[0]); +#else + (void)argv; #endif } @@ -196869,6 +207282,7 @@ static int geopolyInit( wx_sqlite3_str *pSql; char *zSql; int ii; + (void)pAux; wx_sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); @@ -196985,6 +207399,7 @@ static int geopolyFilter( RtreeNode *pRoot = 0; int rc = SQLITE_OK; int iCell = 0; + (void)idxStr; rtreeReference(pRtree); @@ -197020,6 +207435,7 @@ static int geopolyFilter( RtreeCoord bbox[4]; RtreeConstraint *p; assert( argc==1 ); + assert( argv[0]!=0 ); geopolyBBox(0, argv[0], bbox, &rc); if( rc ){ goto geopoly_filter_end; @@ -197110,6 +207526,7 @@ static int geopolyBestIndex(wx_sqlite3_vtab *tab, wx_sqlite3_index_info *pIdxInf int iRowidTerm = -1; int iFuncTerm = -1; int idxNum = 0; + (void)tab; for(ii=0; iinConstraint; ii++){ struct wx_sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; @@ -197247,6 +207664,7 @@ static int geopolyUpdate( || !wx_sqlite3_value_nochange(aData[2]) /* UPDATE _shape */ || oldRowid!=newRowid) /* Rowid change */ ){ + assert( aData[2]!=0 ); geopolyBBox(0, aData[2], cell.aCoord, &rc); if( rc ){ if( rc==SQLITE_ERROR ){ @@ -197329,7 +207747,7 @@ static int geopolyUpdate( wx_sqlite3_free(p); nChange = 1; } - for(jj=1; jjnAux; jj++){ + for(jj=1; jjxGeom = 0; pGeomCtx->xQueryFunc = xQueryFunc; pGeomCtx->xDestructor = xDestructor; @@ -197929,8 +208352,9 @@ static void icuRegexpFunc(wx_sqlite3_context *p, int nArg, wx_sqlite3_value **ap if( U_SUCCESS(status) ){ wx_sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete); - }else{ - assert(!pExpr); + pExpr = wx_sqlite3_get_auxdata(p, 0); + } + if( !pExpr ){ icuFunctionError(p, "uregex_open", status); return; } @@ -198641,7 +209065,7 @@ SQLITE_PRIVATE void wx_sqlite3Fts3IcuTokenizerModule( ** The order of the columns in the data_% table does not matter. ** ** Instead of a regular table, the RBU database may also contain virtual -** tables or view named using the data_ naming scheme. +** tables or views named using the data_ naming scheme. ** ** Instead of the plain data_ naming scheme, RBU database tables ** may also be named data_, where is any sequence @@ -198654,7 +209078,7 @@ SQLITE_PRIVATE void wx_sqlite3Fts3IcuTokenizerModule( ** ** If the target database table is a virtual table or a table that has no ** PRIMARY KEY declaration, the data_% table must also contain a column -** named "rbu_rowid". This column is mapped to the tables implicit primary +** named "rbu_rowid". This column is mapped to the table's implicit primary ** key column - "rowid". Virtual tables for which the "rowid" column does ** not function like a primary key value cannot be updated using RBU. For ** example, if the target db contains either of the following: @@ -199087,6 +209511,34 @@ SQLITE_API void wx_sqlite3rbu_bp_progress(wx_sqlite3rbu *pRbu, int *pnOne, int*p SQLITE_API int wx_sqlite3rbu_state(wx_sqlite3rbu *pRbu); +/* +** As part of applying an RBU update or performing an RBU vacuum operation, +** the system must at one point move the *-oal file to the equivalent *-wal +** path. Normally, it does this by invoking POSIX function rename(2) directly. +** Except on WINCE platforms, where it uses win32 API MoveFileW(). This +** function may be used to register a callback that the RBU module will invoke +** instead of one of these APIs. +** +** If a callback is registered with an RBU handle, it invokes it instead +** of rename(2) when it needs to move a file within the file-system. The +** first argument passed to the xRename() callback is a copy of the second +** argument (pArg) passed to this function. The second is the full path +** to the file to move and the third the full path to which it should be +** moved. The callback function should return SQLITE_OK to indicate +** success. If an error occurs, it should return an SQLite error code. +** In this case the RBU operation will be abandoned and the error returned +** to the RBU user. +** +** Passing a NULL pointer in place of the xRename argument to this function +** restores the default behaviour. +*/ +SQLITE_API void wx_sqlite3rbu_rename_handler( + wx_sqlite3rbu *pRbu, + void *pArg, + int (*xRename)(void *pArg, const char *zOld, const char *zNew) +); + + /* ** Create an RBU VFS named zName that accesses the underlying file-system ** via existing VFS zParent. Or, if the zParent parameter is passed NULL, @@ -199171,6 +209623,13 @@ SQLITE_API void wx_sqlite3rbu_destroy_vfs(const char *zName); # define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;} #endif +/* +** Name of the URI option that causes RBU to take an exclusive lock as +** part of the incremental checkpoint operation. +*/ +#define RBU_EXCLUSIVE_CHECKPOINT "rbu_exclusive_checkpoint" + + /* ** The rbu_state table is used to save the state of a partially applied ** update so that it can be resumed later. The table consists of integer @@ -199447,6 +209906,8 @@ struct wx_sqlite3rbu { int nPagePerSector; /* Pages per sector for pTargetFd */ i64 iOalSz; i64 nPhaseOneStep; + void *pRenameArg; + int (*xRename)(void*, const char*, const char*); /* The following state variables are used as part of the incremental ** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding @@ -200255,7 +210716,9 @@ static void rbuTableType( assert( p->rc==SQLITE_OK ); p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[0], &p->zErrmsg, wx_sqlite3_mprintf( - "SELECT (sql LIKE 'create virtual%%'), rootpage" + "SELECT " + " (sql COLLATE nocase BETWEEN 'CREATE VIRTUAL' AND 'CREATE VIRTUAM')," + " rootpage" " FROM sqlite_schema" " WHERE name=%Q", zTab )); @@ -200615,7 +211078,7 @@ static char *rbuVacuumTableStart( ** the caller has to use an OFFSET clause to extract only the required ** rows from the sourct table, just as it does for an RBU update operation. */ -char *rbuVacuumIndexStart( +static char *rbuVacuumIndexStart( wx_sqlite3rbu *p, /* RBU handle */ RbuObjIter *pIter /* RBU iterator object */ ){ @@ -200681,7 +211144,9 @@ char *rbuVacuumIndexStart( zSep = ""; for(iCol=0; iColnCol; iCol++){ const char *zQuoted = (const char*)wx_sqlite3_column_text(pSel, iCol); - if( zQuoted[0]=='N' ){ + if( zQuoted==0 ){ + p->rc = SQLITE_NOMEM; + }else if( zQuoted[0]=='N' ){ bFailed = 1; break; } @@ -201786,7 +212251,7 @@ static RbuState *rbuLoadState(wx_sqlite3rbu *p){ break; case RBU_STATE_OALSZ: - pRet->iOalSz = (u32)wx_sqlite3_column_int64(pStmt, 1); + pRet->iOalSz = wx_sqlite3_column_int64(pStmt, 1); break; case RBU_STATE_PHASEONESTEP: @@ -201813,19 +212278,25 @@ static RbuState *rbuLoadState(wx_sqlite3rbu *p){ /* ** Open the database handle and attach the RBU database as "rbu". If an ** error occurs, leave an error code and message in the RBU handle. +** +** If argument dbMain is not NULL, then it is a database handle already +** open on the target database. Use this handle instead of opening a new +** one. */ -static void rbuOpenDatabase(wx_sqlite3rbu *p, int *pbRetry){ +static void rbuOpenDatabase(wx_sqlite3rbu *p, wx_sqlite3 *dbMain, int *pbRetry){ assert( p->rc || (p->dbMain==0 && p->dbRbu==0) ); assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 ); + assert( dbMain==0 || rbuIsVacuum(p)==0 ); /* Open the RBU database */ p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1); + p->dbMain = dbMain; if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){ wx_sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p); if( p->zState==0 ){ const char *zFile = wx_sqlite3_db_filename(p->dbRbu, "main"); - p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile); + p->zState = rbuMPrintf(p, "file:///%s-vacuum?modeof=%s", zFile, zFile); } } @@ -202073,11 +212544,11 @@ static void rbuSetupCheckpoint(wx_sqlite3rbu *p, RbuState *pState){ ** no-ops. These locks will not be released until the connection ** is closed. ** - ** * Attempting to xSync() the database file causes an SQLITE_INTERNAL + ** * Attempting to xSync() the database file causes an SQLITE_NOTICE ** error. ** ** As a result, unless an error (i.e. OOM or SQLITE_BUSY) occurs, the - ** checkpoint below fails with SQLITE_INTERNAL, and leaves the aFrame[] + ** checkpoint below fails with SQLITE_NOTICE, and leaves the aFrame[] ** array populated with a set of (frame -> page) mappings. Because the ** WRITER, CHECKPOINT and READ0 locks are still held, it is safe to copy ** data from the wal file into the database file according to the @@ -202087,7 +212558,7 @@ static void rbuSetupCheckpoint(wx_sqlite3rbu *p, RbuState *pState){ int rc2; p->eStage = RBU_STAGE_CAPTURE; rc2 = wx_sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0); - if( rc2!=SQLITE_INTERNAL ) p->rc = rc2; + if( rc2!=SQLITE_NOTICE ) p->rc = rc2; } if( p->rc==SQLITE_OK && p->nFrame>0 ){ @@ -202133,7 +212604,7 @@ static int rbuCaptureWalRead(wx_sqlite3rbu *pRbu, i64 iOff, int iAmt){ if( pRbu->mLock!=mReq ){ pRbu->rc = SQLITE_BUSY; - return SQLITE_INTERNAL; + return SQLITE_NOTICE_RBU; } pRbu->pgsz = iAmt; @@ -202185,15 +212656,31 @@ static void rbuCheckpointFrame(wx_sqlite3rbu *p, RbuFrame *pFrame){ /* -** Take an EXCLUSIVE lock on the database file. +** Take an EXCLUSIVE lock on the database file. Return SQLITE_OK if +** successful, or an SQLite error code otherwise. */ -static void rbuLockDatabase(wx_sqlite3rbu *p){ - wx_sqlite3_file *pReal = p->pTargetFd->pReal; - assert( p->rc==SQLITE_OK ); - p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_SHARED); - if( p->rc==SQLITE_OK ){ - p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_EXCLUSIVE); +static int rbuLockDatabase(wx_sqlite3 *db){ + int rc = SQLITE_OK; + wx_sqlite3_file *fd = 0; + wx_sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); + + if( fd->pMethods ){ + rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); + if( rc==SQLITE_OK ){ + rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE); + } } + return rc; +} + +/* +** Return true if the database handle passed as the only argument +** was opened with the rbu_exclusive_checkpoint=1 URI parameter +** specified. Or false otherwise. +*/ +static int rbuExclusiveCheckpoint(wx_sqlite3 *db){ + const char *zUri = wx_sqlite3_db_filename(db, 0); + return wx_sqlite3_uri_boolean(zUri, RBU_EXCLUSIVE_CHECKPOINT, 0); } #if defined(_WIN32_WCE) @@ -202251,49 +212738,38 @@ static void rbuMoveOalFile(wx_sqlite3rbu *p){ ** In order to ensure that there are no database readers, an EXCLUSIVE ** lock is obtained here before the *-oal is moved to *-wal. */ - rbuLockDatabase(p); - if( p->rc==SQLITE_OK ){ - rbuFileSuffix3(zBase, zWal); - rbuFileSuffix3(zBase, zOal); + wx_sqlite3 *dbMain = 0; + rbuFileSuffix3(zBase, zWal); + rbuFileSuffix3(zBase, zOal); - /* Re-open the databases. */ - rbuObjIterFinalize(&p->objiter); - wx_sqlite3_close(p->dbRbu); - wx_sqlite3_close(p->dbMain); - p->dbMain = 0; - p->dbRbu = 0; + /* Re-open the databases. */ + rbuObjIterFinalize(&p->objiter); + wx_sqlite3_close(p->dbRbu); + wx_sqlite3_close(p->dbMain); + p->dbMain = 0; + p->dbRbu = 0; -#if defined(_WIN32_WCE) - { - LPWSTR zWideOal; - LPWSTR zWideWal; - - zWideOal = rbuWinUtf8ToUnicode(zOal); - if( zWideOal ){ - zWideWal = rbuWinUtf8ToUnicode(zWal); - if( zWideWal ){ - if( MoveFileW(zWideOal, zWideWal) ){ - p->rc = SQLITE_OK; - }else{ - p->rc = SQLITE_IOERR; - } - wx_sqlite3_free(zWideWal); - }else{ - p->rc = SQLITE_IOERR_NOMEM; - } - wx_sqlite3_free(zWideOal); - }else{ - p->rc = SQLITE_IOERR_NOMEM; - } - } -#else - p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK; -#endif + dbMain = rbuOpenDbhandle(p, p->zTarget, 1); + if( dbMain ){ + assert( p->rc==SQLITE_OK ); + p->rc = rbuLockDatabase(dbMain); + } - if( p->rc==SQLITE_OK ){ - rbuOpenDatabase(p, 0); - rbuSetupCheckpoint(p, 0); - } + if( p->rc==SQLITE_OK ){ + p->rc = p->xRename(p->pRenameArg, zOal, zWal); + } + + if( p->rc!=SQLITE_OK + || rbuIsVacuum(p) + || rbuExclusiveCheckpoint(dbMain)==0 + ){ + wx_sqlite3_close(dbMain); + dbMain = 0; + } + + if( p->rc==SQLITE_OK ){ + rbuOpenDatabase(p, dbMain, 0); + rbuSetupCheckpoint(p, 0); } } @@ -202867,7 +213343,8 @@ static void rbuSetupOal(wx_sqlite3rbu *p, RbuState *pState){ static void rbuDeleteOalFile(wx_sqlite3rbu *p){ char *zOal = rbuMPrintf(p, "%s-oal", p->zTarget); if( zOal ){ - wx_sqlite3_vfs *pVfs = wx_sqlite3_vfs_find(0); + wx_sqlite3_vfs *pVfs = 0; + wx_sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_VFS_POINTER, &pVfs); assert( pVfs && p->rc==SQLITE_OK && p->zErrmsg==0 ); pVfs->xDelete(pVfs, zOal, 0); wx_sqlite3_free(zOal); @@ -203019,6 +213496,7 @@ static wx_sqlite3rbu *openRbuHandle( /* Create the custom VFS. */ memset(p, 0, sizeof(wx_sqlite3rbu)); + wx_sqlite3rbu_rename_handler(p, 0, 0); rbuCreateVfs(p); /* Open the target, RBU and state databases */ @@ -203044,9 +213522,9 @@ static wx_sqlite3rbu *openRbuHandle( ** If this is the case, it will have been checkpointed and deleted ** when the handle was closed and a second attempt to open the ** database may succeed. */ - rbuOpenDatabase(p, &bRetry); + rbuOpenDatabase(p, 0, &bRetry); if( bRetry ){ - rbuOpenDatabase(p, 0); + rbuOpenDatabase(p, 0, 0); } } @@ -203141,6 +213619,14 @@ static wx_sqlite3rbu *openRbuHandle( }else if( p->eStage==RBU_STAGE_MOVE ){ /* no-op */ }else if( p->eStage==RBU_STAGE_CKPT ){ + if( !rbuIsVacuum(p) && rbuExclusiveCheckpoint(p->dbMain) ){ + /* If the rbu_exclusive_checkpoint=1 URI parameter was specified + ** and an incremental checkpoint is being resumed, attempt an + ** exclusive lock on the db file. If this fails, so be it. */ + p->eStage = RBU_STAGE_DONE; + rbuLockDatabase(p->dbMain); + p->eStage = RBU_STAGE_CKPT; + } rbuSetupCheckpoint(p, pState); }else if( p->eStage==RBU_STAGE_DONE ){ p->rc = SQLITE_DONE; @@ -203178,7 +213664,6 @@ SQLITE_API wx_sqlite3rbu *wx_sqlite3rbu_open( const char *zState ){ if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); } - /* TODO: Check that zTarget and zRbu are non-NULL */ return openRbuHandle(zTarget, zRbu, zState); } @@ -203403,6 +213888,54 @@ SQLITE_API int wx_sqlite3rbu_savestate(wx_sqlite3rbu *p){ return rc; } +/* +** Default xRename callback for RBU. +*/ +static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){ + int rc = SQLITE_OK; +#if defined(_WIN32_WCE) + { + LPWSTR zWideOld; + LPWSTR zWideNew; + + zWideOld = rbuWinUtf8ToUnicode(zOld); + if( zWideOld ){ + zWideNew = rbuWinUtf8ToUnicode(zNew); + if( zWideNew ){ + if( MoveFileW(zWideOld, zWideNew) ){ + rc = SQLITE_OK; + }else{ + rc = SQLITE_IOERR; + } + wx_sqlite3_free(zWideNew); + }else{ + rc = SQLITE_IOERR_NOMEM; + } + wx_sqlite3_free(zWideOld); + }else{ + rc = SQLITE_IOERR_NOMEM; + } + } +#else + rc = rename(zOld, zNew) ? SQLITE_IOERR : SQLITE_OK; +#endif + return rc; +} + +SQLITE_API void wx_sqlite3rbu_rename_handler( + wx_sqlite3rbu *pRbu, + void *pArg, + int (*xRename)(void *pArg, const char *zOld, const char *zNew) +){ + if( xRename ){ + pRbu->xRename = xRename; + pRbu->pRenameArg = pArg; + }else{ + pRbu->xRename = xDefaultRename; + pRbu->pRenameArg = 0; + } +} + /************************************************************************** ** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour ** of a standard VFS in the following ways: @@ -203459,7 +213992,7 @@ SQLITE_API int wx_sqlite3rbu_savestate(wx_sqlite3rbu *p){ ** database file are recorded. xShmLock() calls to unlock the same ** locks are no-ops (so that once obtained, these locks are never ** relinquished). Finally, calls to xSync() on the target database -** file fail with SQLITE_INTERNAL errors. +** file fail with SQLITE_NOTICE errors. */ static void rbuUnlockShm(rbu_file *p){ @@ -203568,9 +214101,12 @@ static int rbuVfsClose(wx_sqlite3_file *pFile){ wx_sqlite3_free(p->zDel); if( p->openFlags & SQLITE_OPEN_MAIN_DB ){ + const wx_sqlite3_io_methods *pMeth = p->pReal->pMethods; rbuMainlistRemove(p); rbuUnlockShm(p); - p->pReal->pMethods->xShmUnmap(p->pReal, 0); + if( pMeth->iVersion>1 && pMeth->xShmUnmap ){ + pMeth->xShmUnmap(p->pReal, 0); + } } else if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){ rbuUpdateTempSize(p, 0); @@ -203738,7 +214274,7 @@ static int rbuVfsSync(wx_sqlite3_file *pFile, int flags){ rbu_file *p = (rbu_file *)pFile; if( p->pRbu && p->pRbu->eStage==RBU_STAGE_CAPTURE ){ if( p->openFlags & SQLITE_OPEN_MAIN_DB ){ - return SQLITE_INTERNAL; + return SQLITE_NOTICE_RBU; } return SQLITE_OK; } @@ -204029,6 +214565,25 @@ static int rbuVfsOpen( rbuVfsShmUnmap, /* xShmUnmap */ 0, 0 /* xFetch, xUnfetch */ }; + static wx_sqlite3_io_methods rbuvfs_io_methods1 = { + 1, /* iVersion */ + rbuVfsClose, /* xClose */ + rbuVfsRead, /* xRead */ + rbuVfsWrite, /* xWrite */ + rbuVfsTruncate, /* xTruncate */ + rbuVfsSync, /* xSync */ + rbuVfsFileSize, /* xFileSize */ + rbuVfsLock, /* xLock */ + rbuVfsUnlock, /* xUnlock */ + rbuVfsCheckReservedLock, /* xCheckReservedLock */ + rbuVfsFileControl, /* xFileControl */ + rbuVfsSectorSize, /* xSectorSize */ + rbuVfsDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, 0, 0, 0, 0, 0 + }; + + + rbu_vfs *pRbuVfs = (rbu_vfs*)pVfs; wx_sqlite3_vfs *pRealVfs = pRbuVfs->pRealVfs; rbu_file *pFd = (rbu_file *)pFile; @@ -204053,28 +214608,14 @@ static int rbuVfsOpen( rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName, 0); if( pDb ){ if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){ - /* This call is to open a *-wal file. Intead, open the *-oal. This - ** code ensures that the string passed to xOpen() is terminated by a - ** pair of '\0' bytes in case the VFS attempts to extract a URI - ** parameter from it. */ - const char *zBase = zName; - size_t nCopy; - char *zCopy; + /* This call is to open a *-wal file. Intead, open the *-oal. */ + size_t nOpen; if( rbuIsVacuum(pDb->pRbu) ){ - zBase = wx_sqlite3_db_filename(pDb->pRbu->dbRbu, "main"); - zBase = wx_sqlite3_filename_wal(zBase); - } - nCopy = strlen(zBase); - zCopy = wx_sqlite3_malloc64(nCopy+2); - if( zCopy ){ - memcpy(zCopy, zBase, nCopy); - zCopy[nCopy-3] = 'o'; - zCopy[nCopy] = '\0'; - zCopy[nCopy+1] = '\0'; - zOpen = (const char*)(pFd->zDel = zCopy); - }else{ - rc = SQLITE_NOMEM; + zOpen = wx_sqlite3_db_filename(pDb->pRbu->dbRbu, "main"); + zOpen = wx_sqlite3_filename_wal(zOpen); } + nOpen = strlen(zOpen); + ((char*)zOpen)[nOpen-3] = 'o'; pFd->pRbu = pDb->pRbu; } pDb->pWalFd = pFd; @@ -204097,10 +214638,15 @@ static int rbuVfsOpen( rc = pRealVfs->xOpen(pRealVfs, zOpen, pFd->pReal, oflags, pOutFlags); } if( pFd->pReal->pMethods ){ + const wx_sqlite3_io_methods *pMeth = pFd->pReal->pMethods; /* The xOpen() operation has succeeded. Set the wx_sqlite3_file.pMethods ** pointer and, if the file is a main database file, link it into the ** mutex protected linked list of all such files. */ - pFile->pMethods = &rbuvfs_io_methods; + if( pMeth->iVersion<2 || pMeth->xShmLock==0 ){ + pFile->pMethods = &rbuvfs_io_methods1; + }else{ + pFile->pMethods = &rbuvfs_io_methods; + } if( flags & SQLITE_OPEN_MAIN_DB ){ rbuMainlistAdd(pFd); } @@ -204395,6 +214941,15 @@ SQLITE_API wx_sqlite3_int64 wx_sqlite3rbu_temp_size(wx_sqlite3rbu *pRbu){ #if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \ && !defined(SQLITE_OMIT_VIRTUALTABLE) +/* +** The pager and btree modules arrange objects in memory so that there are +** always approximately 200 bytes of addressable memory following each page +** buffer. This way small buffer overreads caused by corrupt database pages +** do not cause undefined behaviour. This module pads each page buffer +** by the following number of bytes for the same purpose. +*/ +#define DBSTAT_PAGE_PADDING_BYTES 256 + /* ** Page paths: ** @@ -204462,9 +215017,8 @@ struct StatCell { /* Size information for a single btree page */ struct StatPage { u32 iPgno; /* Page number */ - DbPage *pPg; /* Page content */ + u8 *aPg; /* Page buffer from wx_sqlite3_malloc() */ int iCell; /* Current cell */ - char *zPath; /* Path to this page */ /* Variables populated by statDecodePage(): */ @@ -204525,6 +215079,7 @@ static int statConnect( StatTable *pTab = 0; int rc = SQLITE_OK; int iDb; + (void)pAux; if( argc>=4 ){ Token nm; @@ -204578,6 +215133,7 @@ static int statBestIndex(wx_sqlite3_vtab *tab, wx_sqlite3_index_info *pIdxInfo){ int iSchema = -1; int iName = -1; int iAgg = -1; + (void)tab; /* Look for a valid schema=? constraint. If found, change the idxNum to ** 1 and request the value of that constraint be sent to xFilter. And @@ -204676,18 +215232,25 @@ static void statClearCells(StatPage *p){ } static void statClearPage(StatPage *p){ + u8 *aPg = p->aPg; statClearCells(p); - wx_sqlite3PagerUnref(p->pPg); wx_sqlite3_free(p->zPath); memset(p, 0, sizeof(StatPage)); + p->aPg = aPg; } static void statResetCsr(StatCursor *pCsr){ int i; - wx_sqlite3_reset(pCsr->pStmt); + /* In some circumstances, specifically if an OOM has occurred, the call + ** to wx_sqlite3_reset() may cause the pager to be reset (emptied). It is + ** important that statClearPage() is called to free any page refs before + ** this happens. dbsqlfuzz 9ed3e4e3816219d3509d711636c38542bf3f40b1. */ for(i=0; iaPage); i++){ statClearPage(&pCsr->aPage[i]); + wx_sqlite3_free(pCsr->aPage[i].aPg); + pCsr->aPage[i].aPg = 0; } + wx_sqlite3_reset(pCsr->pStmt); pCsr->iPage = 0; wx_sqlite3_free(pCsr->zPath); pCsr->zPath = 0; @@ -204752,7 +215315,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ int isLeaf; int szPage; - u8 *aData = wx_sqlite3PagerGetData(p->pPg); + u8 *aData = p->aPg; u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0]; p->flags = aHdr[0]; @@ -204823,7 +215386,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ if( nPayload>(u32)nLocal ){ int j; int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); - if( iOff+nLocal>nUsable || nPayload>0x7fffffff ){ + if( iOff+nLocal+4>nUsable || nPayload>0x7fffffff ){ goto statPageIsCorrupt; } pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); @@ -204882,6 +215445,38 @@ static void statSizeAndOffset(StatCursor *pCsr){ } } +/* +** Load a copy of the page data for page iPg into the buffer belonging +** to page object pPg. Allocate the buffer if necessary. Return SQLITE_OK +** if successful, or an SQLite error code otherwise. +*/ +static int statGetPage( + Btree *pBt, /* Load page from this b-tree */ + u32 iPg, /* Page number to load */ + StatPage *pPg /* Load page into this object */ +){ + int pgsz = wx_sqlite3BtreeGetPageSize(pBt); + DbPage *pDbPage = 0; + int rc; + + if( pPg->aPg==0 ){ + pPg->aPg = (u8*)wx_sqlite3_malloc(pgsz + DBSTAT_PAGE_PADDING_BYTES); + if( pPg->aPg==0 ){ + return SQLITE_NOMEM_BKPT; + } + memset(&pPg->aPg[pgsz], 0, DBSTAT_PAGE_PADDING_BYTES); + } + + rc = wx_sqlite3PagerGet(wx_sqlite3BtreePager(pBt), iPg, &pDbPage, 0); + if( rc==SQLITE_OK ){ + const u8 *a = wx_sqlite3PagerGetData(pDbPage); + memcpy(pPg->aPg, a, pgsz); + wx_sqlite3PagerUnref(pDbPage); + } + + return rc; +} + /* ** Move a DBSTAT cursor to the next entry. Normally, the next ** entry will be the next page, but in aggregated mode (pCsr->isAgg!=0), @@ -204900,7 +215495,7 @@ static int statNext(wx_sqlite3_vtab_cursor *pCursor){ pCsr->zPath = 0; statNextRestart: - if( pCsr->aPage[0].pPg==0 ){ + if( pCsr->iPage<0 ){ /* Start measuring space on the next btree */ statResetCounts(pCsr); rc = wx_sqlite3_step(pCsr->pStmt); @@ -204912,7 +215507,7 @@ statNextRestart: pCsr->isEof = 1; return wx_sqlite3_reset(pCsr->pStmt); } - rc = wx_sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0); + rc = statGetPage(pBt, iRoot, &pCsr->aPage[0]); pCsr->aPage[0].iPgno = iRoot; pCsr->aPage[0].iCell = 0; if( !pCsr->isAgg ){ @@ -204963,9 +215558,8 @@ statNextRestart: if( !p->iRightChildPg || p->iCell>p->nCell ){ statClearPage(p); - if( pCsr->iPage>0 ){ - pCsr->iPage--; - }else if( pCsr->isAgg ){ + pCsr->iPage--; + if( pCsr->isAgg && pCsr->iPage<0 ){ /* label-statNext-done: When computing aggregate space usage over ** an entire btree, this is the exit point from this function */ return SQLITE_OK; @@ -204984,7 +215578,7 @@ statNextRestart: }else{ p[1].iPgno = p->aCell[p->iCell].iChildPg; } - rc = wx_sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0); + rc = statGetPage(pBt, p[1].iPgno, &p[1]); pCsr->nPage++; p[1].iCell = 0; if( !pCsr->isAgg ){ @@ -205065,6 +215659,8 @@ static int statFilter( int iArg = 0; /* Count of argv[] parameters used so far */ int rc = SQLITE_OK; /* Result of this operation */ const char *zName = 0; /* Only provide analysis of this table */ + (void)argc; + (void)idxStr; statResetCsr(pCsr); wx_sqlite3_finalize(pCsr->pStmt); @@ -205114,6 +215710,7 @@ static int statFilter( } if( rc==SQLITE_OK ){ + pCsr->iPage = -1; rc = statNext(pCursor); } return rc; @@ -205147,16 +215744,16 @@ static int statColumn( } break; case 4: /* ncell */ - wx_sqlite3_result_int(ctx, pCsr->nCell); + wx_sqlite3_result_int64(ctx, pCsr->nCell); break; case 5: /* payload */ - wx_sqlite3_result_int(ctx, pCsr->nPayload); + wx_sqlite3_result_int64(ctx, pCsr->nPayload); break; case 6: /* unused */ - wx_sqlite3_result_int(ctx, pCsr->nUnused); + wx_sqlite3_result_int64(ctx, pCsr->nUnused); break; case 7: /* mx_payload */ - wx_sqlite3_result_int(ctx, pCsr->nMxPayload); + wx_sqlite3_result_int64(ctx, pCsr->nMxPayload); break; case 8: /* pgoffset */ if( !pCsr->isAgg ){ @@ -205164,7 +215761,7 @@ static int statColumn( } break; case 9: /* pgsize */ - wx_sqlite3_result_int(ctx, pCsr->szPage); + wx_sqlite3_result_int64(ctx, pCsr->szPage); break; case 10: { /* schema */ wx_sqlite3 *db = wx_sqlite3_context_db_handle(ctx); @@ -205298,6 +215895,10 @@ static int dbpageConnect( ){ DbpageTable *pTab = 0; int rc = SQLITE_OK; + (void)pAux; + (void)argc; + (void)argv; + (void)pzErr; wx_sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY); rc = wx_sqlite3_declare_vtab(db, @@ -205336,6 +215937,7 @@ static int dbpageDisconnect(wx_sqlite3_vtab *pVtab){ static int dbpageBestIndex(wx_sqlite3_vtab *tab, wx_sqlite3_index_info *pIdxInfo){ int i; int iPlan = 0; + (void)tab; /* If there is a schema= constraint, it must be honored. Report a ** ridiculously large estimated cost if the schema= constraint is @@ -205382,6 +215984,7 @@ static int dbpageBestIndex(wx_sqlite3_vtab *tab, wx_sqlite3_index_info *pIdxInfo ){ pIdxInfo->orderByConsumed = 1; } + wx_sqlite3VtabUsesAllSchemas(pIdxInfo); return SQLITE_OK; } @@ -205450,6 +216053,8 @@ static int dbpageFilter( wx_sqlite3 *db = pTab->db; Btree *pBt; + (void)idxStr; + /* Default setting is no rows of result */ pCsr->pgno = 1; pCsr->mxPgno = 0; @@ -205464,7 +216069,7 @@ static int dbpageFilter( pCsr->iDb = 0; } pBt = db->aDb[pCsr->iDb].pBt; - if( pBt==0 ) return SQLITE_OK; + if( NEVER(pBt==0) ) return SQLITE_OK; pCsr->pPager = wx_sqlite3BtreePager(pBt); pCsr->szPage = wx_sqlite3BtreeGetPageSize(pBt); pCsr->mxPgno = wx_sqlite3BtreeLastPage(pBt); @@ -205499,12 +216104,18 @@ static int dbpageColumn( } case 1: { /* data */ DbPage *pDbPage = 0; - rc = wx_sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0); - if( rc==SQLITE_OK ){ - wx_sqlite3_result_blob(ctx, wx_sqlite3PagerGetData(pDbPage), pCsr->szPage, - SQLITE_TRANSIENT); + if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){ + /* The pending byte page. Assume it is zeroed out. Attempting to + ** request this page from the page is an SQLITE_CORRUPT error. */ + wx_sqlite3_result_zeroblob(ctx, pCsr->szPage); + }else{ + rc = wx_sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0); + if( rc==SQLITE_OK ){ + wx_sqlite3_result_blob(ctx, wx_sqlite3PagerGetData(pDbPage), pCsr->szPage, + SQLITE_TRANSIENT); + } + wx_sqlite3PagerUnref(pDbPage); } - wx_sqlite3PagerUnref(pDbPage); break; } default: { /* schema */ @@ -205513,7 +216124,7 @@ static int dbpageColumn( break; } } - return SQLITE_OK; + return rc; } static int dbpageRowid(wx_sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ @@ -205539,6 +216150,7 @@ static int dbpageUpdate( Pager *pPager; int szPage; + (void)pRowid; if( pTab->db->flags & SQLITE_Defensive ){ zErr = "read-only"; goto update_fail; @@ -205548,18 +216160,20 @@ static int dbpageUpdate( goto update_fail; } pgno = wx_sqlite3_value_int(argv[0]); - if( (Pgno)wx_sqlite3_value_int(argv[1])!=pgno ){ + if( wx_sqlite3_value_type(argv[0])==SQLITE_NULL + || (Pgno)wx_sqlite3_value_int(argv[1])!=pgno + ){ zErr = "cannot insert"; goto update_fail; } zSchema = (const char*)wx_sqlite3_value_text(argv[4]); - iDb = zSchema ? wx_sqlite3FindDbName(pTab->db, zSchema) : -1; - if( iDb<0 ){ + iDb = ALWAYS(zSchema) ? wx_sqlite3FindDbName(pTab->db, zSchema) : -1; + if( NEVER(iDb<0) ){ zErr = "no such schema"; goto update_fail; } pBt = pTab->db->aDb[iDb].pBt; - if( pgno<1 || pBt==0 || pgno>(int)wx_sqlite3BtreeLastPage(pBt) ){ + if( NEVER(pgno<1) || NEVER(pBt==0) || NEVER(pgno>wx_sqlite3BtreeLastPage(pBt)) ){ zErr = "bad page number"; goto update_fail; } @@ -205573,11 +216187,12 @@ static int dbpageUpdate( pPager = wx_sqlite3BtreePager(pBt); rc = wx_sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0); if( rc==SQLITE_OK ){ - rc = wx_sqlite3PagerWrite(pDbPage); - if( rc==SQLITE_OK ){ - memcpy(wx_sqlite3PagerGetData(pDbPage), - wx_sqlite3_value_blob(argv[3]), - szPage); + const void *pData = wx_sqlite3_value_blob(argv[3]); + assert( pData!=0 || pTab->db->mallocFailed ); + if( pData + && (rc = wx_sqlite3PagerWrite(pDbPage))==SQLITE_OK + ){ + memcpy(wx_sqlite3PagerGetData(pDbPage), pData, szPage); } } wx_sqlite3PagerUnref(pDbPage); @@ -205599,7 +216214,7 @@ static int dbpageBegin(wx_sqlite3_vtab *pVtab){ int i; for(i=0; inDb; i++){ Btree *pBt = db->aDb[i].pBt; - if( pBt ) wx_sqlite3BtreeBeginTrans(pBt, 1, 0); + if( pBt ) (void)wx_sqlite3BtreeBeginTrans(pBt, 1, 0); } return SQLITE_OK; } @@ -205687,6 +216302,7 @@ struct SessionHook { struct wx_sqlite3_session { wx_sqlite3 *db; /* Database handle session is attached to */ char *zDb; /* Name of database session is attached to */ + int bEnableSize; /* True if changeset_size() enabled */ int bEnable; /* True if currently recording */ int bIndirect; /* True if all changes are indirect */ int bAutoAttach; /* True to auto-attach tables */ @@ -205694,6 +216310,7 @@ struct wx_sqlite3_session { void *pFilterCtx; /* First argument to pass to xTableFilter */ int (*xTableFilter)(void *pCtx, const char *zTab); i64 nMalloc; /* Number of bytes of data allocated */ + i64 nMaxChangesetSize; wx_sqlite3_value *pZeroBlob; /* Value containing X'' */ wx_sqlite3_session *pNext; /* Next session object on same db. */ SessionTable *pTable; /* List of attached tables */ @@ -205936,8 +216553,9 @@ struct SessionTable { ** this structure stored in a SessionTable.aChange[] hash table. */ struct SessionChange { - int op; /* One of UPDATE, DELETE, INSERT */ - int bIndirect; /* True if this change is "indirect" */ + u8 op; /* One of UPDATE, DELETE, INSERT */ + u8 bIndirect; /* True if this change is "indirect" */ + int nMaxSize; /* Max size of eventual changeset record */ int nRecord; /* Number of bytes in buffer aRecord[] */ u8 *aRecord; /* Buffer containing old.* record */ SessionChange *pNext; /* For hash-table collisions */ @@ -206062,7 +216680,7 @@ static int sessionSerializeValue( if( aBuf ){ sessionVarintPut(&aBuf[1], n); - if( n ) memcpy(&aBuf[nVarint + 1], z, n); + if( n>0 ) memcpy(&aBuf[nVarint + 1], z, n); } nByte = 1 + nVarint + n; @@ -206667,16 +217285,32 @@ static int sessionTableInfo( }else if( rc==SQLITE_ERROR ){ zPragma = wx_sqlite3_mprintf(""); }else{ + *pazCol = 0; + *pabPK = 0; + *pnCol = 0; + if( pzTab ) *pzTab = 0; return rc; } }else{ zPragma = wx_sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis); } - if( !zPragma ) return SQLITE_NOMEM; + if( !zPragma ){ + *pazCol = 0; + *pabPK = 0; + *pnCol = 0; + if( pzTab ) *pzTab = 0; + return SQLITE_NOMEM; + } rc = wx_sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0); wx_sqlite3_free(zPragma); - if( rc!=SQLITE_OK ) return rc; + if( rc!=SQLITE_OK ){ + *pazCol = 0; + *pabPK = 0; + *pnCol = 0; + if( pzTab ) *pzTab = 0; + return rc; + } nByte = nThis + 1; while( SQLITE_ROW==wx_sqlite3_step(pStmt) ){ @@ -206766,6 +217400,12 @@ static int sessionInitTable(wx_sqlite3_session *pSession, SessionTable *pTab){ if( 0==wx_sqlite3_stricmp("sqlite_stat1", pTab->zName) ){ pTab->bStat1 = 1; } + + if( pSession->bEnableSize ){ + pSession->nMaxChangesetSize += ( + 1 + sessionVarintLen(pTab->nCol) + pTab->nCol + strlen(pTab->zName)+1 + ); + } } } return (pSession->rc || pTab->abPK==0); @@ -206811,6 +217451,103 @@ static int sessionStat1Depth(void *pCtx){ return p->hook.xDepth(p->hook.pCtx); } +static int sessionUpdateMaxSize( + int op, + wx_sqlite3_session *pSession, /* Session object pTab is attached to */ + SessionTable *pTab, /* Table that change applies to */ + SessionChange *pC /* Update pC->nMaxSize */ +){ + i64 nNew = 2; + if( pC->op==SQLITE_INSERT ){ + if( op!=SQLITE_DELETE ){ + int ii; + for(ii=0; iinCol; ii++){ + wx_sqlite3_value *p = 0; + pSession->hook.xNew(pSession->hook.pCtx, ii, &p); + sessionSerializeValue(0, p, &nNew); + } + } + }else if( op==SQLITE_DELETE ){ + nNew += pC->nRecord; + if( wx_sqlite3_preupdate_blobwrite(pSession->db)>=0 ){ + nNew += pC->nRecord; + } + }else{ + int ii; + u8 *pCsr = pC->aRecord; + for(ii=0; iinCol; ii++){ + int bChanged = 1; + int nOld = 0; + int eType; + wx_sqlite3_value *p = 0; + pSession->hook.xNew(pSession->hook.pCtx, ii, &p); + if( p==0 ){ + return SQLITE_NOMEM; + } + + eType = *pCsr++; + switch( eType ){ + case SQLITE_NULL: + bChanged = wx_sqlite3_value_type(p)!=SQLITE_NULL; + break; + + case SQLITE_FLOAT: + case SQLITE_INTEGER: { + if( eType==wx_sqlite3_value_type(p) ){ + wx_sqlite3_int64 iVal = sessionGetI64(pCsr); + if( eType==SQLITE_INTEGER ){ + bChanged = (iVal!=wx_sqlite3_value_int64(p)); + }else{ + double dVal; + memcpy(&dVal, &iVal, 8); + bChanged = (dVal!=wx_sqlite3_value_double(p)); + } + } + nOld = 8; + pCsr += 8; + break; + } + + default: { + int nByte; + nOld = sessionVarintGet(pCsr, &nByte); + pCsr += nOld; + nOld += nByte; + assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); + if( eType==wx_sqlite3_value_type(p) + && nByte==wx_sqlite3_value_bytes(p) + && (nByte==0 || 0==memcmp(pCsr, wx_sqlite3_value_blob(p), nByte)) + ){ + bChanged = 0; + } + pCsr += nByte; + break; + } + } + + if( bChanged && pTab->abPK[ii] ){ + nNew = pC->nRecord + 2; + break; + } + + if( bChanged ){ + nNew += 1 + nOld; + sessionSerializeValue(0, p, &nNew); + }else if( pTab->abPK[ii] ){ + nNew += 2 + nOld; + }else{ + nNew += 2; + } + } + } + + if( nNew>pC->nMaxSize ){ + int nIncr = nNew - pC->nMaxSize; + pC->nMaxSize = nNew; + pSession->nMaxChangesetSize += nIncr; + } + return SQLITE_OK; +} /* ** This function is only called from with a pre-update-hook reporting a @@ -206884,7 +217621,6 @@ static void sessionPreupdateOneChange( /* Create a new change object containing all the old values (if ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK ** values (if this is an INSERT). */ - SessionChange *pChange; /* New change object */ wx_sqlite3_int64 nByte; /* Number of bytes to allocate */ int i; /* Used to iterate through columns */ @@ -206910,13 +217646,13 @@ static void sessionPreupdateOneChange( } /* Allocate the change object */ - pChange = (SessionChange *)sessionMalloc64(pSession, nByte); - if( !pChange ){ + pC = (SessionChange *)sessionMalloc64(pSession, nByte); + if( !pC ){ rc = SQLITE_NOMEM; goto error_out; }else{ - memset(pChange, 0, sizeof(SessionChange)); - pChange->aRecord = (u8 *)&pChange[1]; + memset(pC, 0, sizeof(SessionChange)); + pC->aRecord = (u8 *)&pC[1]; } /* Populate the change object. None of the preupdate_old(), @@ -206931,17 +217667,17 @@ static void sessionPreupdateOneChange( }else if( pTab->abPK[i] ){ pSession->hook.xNew(pSession->hook.pCtx, i, &p); } - sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte); + sessionSerializeValue(&pC->aRecord[nByte], p, &nByte); } /* Add the change to the hash-table */ if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){ - pChange->bIndirect = 1; + pC->bIndirect = 1; } - pChange->nRecord = nByte; - pChange->op = op; - pChange->pNext = pTab->apChange[iHash]; - pTab->apChange[iHash] = pChange; + pC->nRecord = nByte; + pC->op = op; + pC->pNext = pTab->apChange[iHash]; + pTab->apChange[iHash] = pC; }else if( pC->bIndirect ){ /* If the existing change is considered "indirect", but this current @@ -206952,8 +217688,14 @@ static void sessionPreupdateOneChange( pC->bIndirect = 0; } } + + assert( rc==SQLITE_OK ); + if( pSession->bEnableSize ){ + rc = sessionUpdateMaxSize(op, pSession, pTab, pC); + } } + /* If an error has occurred, mark the session object as failed. */ error_out: if( pTab->bStat1 ){ @@ -206986,7 +217728,11 @@ static int sessionFindTable( ){ rc = wx_sqlite3session_attach(pSession, zName); if( rc==SQLITE_OK ){ - for(pRet=pSession->pTable; pRet->pNext; pRet=pRet->pNext); + pRet = pSession->pTable; + while( ALWAYS(pRet) && pRet->pNext ){ + pRet = pRet->pNext; + } + assert( pRet!=0 ); assert( 0==wx_sqlite3_strnicmp(pRet->zName, zName, nName+1) ); } } @@ -207013,6 +217759,8 @@ static void xPreUpdate( int nDb = wx_sqlite3Strlen30(zDb); assert( wx_sqlite3_mutex_held(db->mutex) ); + (void)iKey1; + (void)iKey2; for(pSession=(wx_sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){ SessionTable *pTab; @@ -207089,6 +217837,7 @@ static int sessionDiffCount(void *pCtx){ return p->nOldOff ? p->nOldOff : wx_sqlite3_column_count(p->pStmt); } static int sessionDiffDepth(void *pCtx){ + (void)pCtx; return 0; } @@ -207162,7 +217911,6 @@ static char *sessionExprCompareOther( } static char *sessionSelectFindNew( - int nCol, const char *zDb1, /* Pick rows in this db only */ const char *zDb2, /* But not in this one */ const char *zTbl, /* Table name */ @@ -207186,7 +217934,7 @@ static int sessionDiffFindNew( char *zExpr ){ int rc = SQLITE_OK; - char *zStmt = sessionSelectFindNew(pTab->nCol, zDb1, zDb2, pTab->zName,zExpr); + char *zStmt = sessionSelectFindNew(zDb1, zDb2, pTab->zName,zExpr); if( zStmt==0 ){ rc = SQLITE_NOMEM; @@ -207508,13 +218256,29 @@ SQLITE_API int wx_sqlite3session_attach( ** If successful, return zero. Otherwise, if an OOM condition is encountered, ** set *pRc to SQLITE_NOMEM and return non-zero. */ -static int sessionBufferGrow(SessionBuffer *p, size_t nByte, int *pRc){ - if( *pRc==SQLITE_OK && (size_t)(p->nAlloc-p->nBuf)nBuf + nByte; + if( *pRc==SQLITE_OK && nReq>p->nAlloc ){ u8 *aNew; i64 nNew = p->nAlloc ? p->nAlloc : 128; + do { nNew = nNew*2; - }while( (size_t)(nNew-p->nBuf)SESSION_MAX_BUFFER_SZ ){ + nNew = SESSION_MAX_BUFFER_SZ; + if( nNewaBuf, nNew); if( 0==aNew ){ @@ -207743,6 +218507,7 @@ static int sessionAppendUpdate( int i; /* Used to iterate through columns */ u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */ + assert( abPK!=0 ); sessionAppendByte(pBuf, SQLITE_UPDATE, &rc); sessionAppendByte(pBuf, p->bIndirect, &rc); for(i=0; ipTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){ if( pTab->nEntry ){ const char *zName = pTab->zName; - int nCol; /* Number of columns in table */ - u8 *abPK; /* Primary key array */ + int nCol = 0; /* Number of columns in table */ + u8 *abPK = 0; /* Primary key array */ const char **azCol = 0; /* Table columns */ int i; /* Used to iterate through hash buckets */ wx_sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */ @@ -208105,6 +218872,7 @@ static int sessionGenerateChangeset( sessionAppendCol(&buf, pSel, iCol, &rc); } }else{ + assert( abPK!=0 ); /* Because sessionSelectStmt() returned ok */ rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK); } }else if( p->op!=SQLITE_INSERT ){ @@ -208165,7 +218933,14 @@ SQLITE_API int wx_sqlite3session_changeset( int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ void **ppChangeset /* OUT: Buffer containing changeset */ ){ - return sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset); + int rc; + + if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE; + rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset,ppChangeset); + assert( rc || pnChangeset==0 + || pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize + ); + return rc; } /* @@ -208176,6 +218951,7 @@ SQLITE_API int wx_sqlite3session_changeset_strm( int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ){ + if( xOutput==0 ) return SQLITE_MISUSE; return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0); } @@ -208187,6 +218963,7 @@ SQLITE_API int wx_sqlite3session_patchset_strm( int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ){ + if( xOutput==0 ) return SQLITE_MISUSE; return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0); } @@ -208202,6 +218979,7 @@ SQLITE_API int wx_sqlite3session_patchset( int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */ void **ppPatchset /* OUT: Buffer containing changeset */ ){ + if( pnPatchset==0 || ppPatchset==0 ) return SQLITE_MISUSE; return sessionGenerateChangeset(pSession, 1, 0, 0, pnPatchset, ppPatchset); } @@ -208257,6 +219035,39 @@ SQLITE_API wx_sqlite3_int64 wx_sqlite3session_memory_used(wx_sqlite3_session *pS return pSession->nMalloc; } +/* +** Configure the session object passed as the first argument. +*/ +SQLITE_API int wx_sqlite3session_object_config(wx_sqlite3_session *pSession, int op, void *pArg){ + int rc = SQLITE_OK; + switch( op ){ + case SQLITE_SESSION_OBJCONFIG_SIZE: { + int iArg = *(int*)pArg; + if( iArg>=0 ){ + if( pSession->pTable ){ + rc = SQLITE_MISUSE; + }else{ + pSession->bEnableSize = (iArg!=0); + } + } + *(int*)pArg = pSession->bEnableSize; + break; + } + + default: + rc = SQLITE_MISUSE; + } + + return rc; +} + +/* +** Return the maximum size of wx_sqlite3session_changeset() output. +*/ +SQLITE_API wx_sqlite3_int64 wx_sqlite3session_changeset_size(wx_sqlite3_session *pSession){ + return pSession->nMaxChangesetSize; +} + /* ** Do the work for either wx_sqlite3changeset_start() or start_strm(). */ @@ -208778,6 +219589,22 @@ static int sessionChangesetNextOne( if( p->op==SQLITE_INSERT ) p->op = SQLITE_DELETE; else if( p->op==SQLITE_DELETE ) p->op = SQLITE_INSERT; } + + /* If this is an UPDATE that is part of a changeset, then check that + ** there are no fields in the old.* record that are not (a) PK fields, + ** or (b) also present in the new.* record. + ** + ** Such records are technically corrupt, but the rebaser was at one + ** point generating them. Under most circumstances this is benign, but + ** can cause spurious SQLITE_RANGE errors when applying the changeset. */ + if( p->bPatchset==0 && p->op==SQLITE_UPDATE){ + for(i=0; inCol; i++){ + if( p->abPK[i]==0 && p->apValue[i+p->nCol]==0 ){ + wx_sqlite3ValueFree(p->apValue[i]); + p->apValue[i] = 0; + } + } + } } return SQLITE_ROW; @@ -209132,11 +219959,11 @@ static int sessionChangesetInvert( } assert( rc==SQLITE_OK ); - if( pnInverted ){ + if( pnInverted && ALWAYS(ppInverted) ){ *pnInverted = sOut.nBuf; *ppInverted = sOut.aBuf; sOut.aBuf = 0; - }else if( sOut.nBuf>0 ){ + }else if( sOut.nBuf>0 && ALWAYS(xOutput!=0) ){ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); } @@ -209592,7 +220419,7 @@ static int sessionBindRow( for(i=0; rc==SQLITE_OK && idb, pIter, p->abPK, p->pSelect); + rc = sessionSeekToRow(pIter, p->abPK, p->pSelect); }else{ rc = SQLITE_OK; } @@ -209928,7 +220754,7 @@ static int sessionApplyOneOp( /* Check if there is a conflicting row. For sqlite_stat1, this needs ** to be done using a SELECT, as there is no PRIMARY KEY in the ** database schema to throw an exception if a duplicate is inserted. */ - rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect); + rc = sessionSeekToRow(pIter, p->abPK, p->pSelect); if( rc==SQLITE_ROW ){ rc = SQLITE_CONSTRAINT; wx_sqlite3_reset(p->pSelect); @@ -210735,9 +221561,9 @@ static int sessionChangegroupOutput( if( rc==SQLITE_OK ){ if( xOutput ){ if( buf.nBuf>0 ) rc = xOutput(pOut, buf.aBuf, buf.nBuf); - }else{ + }else if( ppOut ){ *ppOut = buf.aBuf; - *pnOut = buf.nBuf; + if( pnOut ) *pnOut = buf.nBuf; buf.aBuf = 0; } } @@ -210974,7 +221800,7 @@ static void sessionAppendPartialUpdate( if( !pIter->abPK[i] && a1[0] ) bData = 1; memcpy(pOut, a1, n1); pOut += n1; - }else if( a2[0]!=0xFF ){ + }else if( a2[0]!=0xFF && a1[0] ){ bData = 1; memcpy(pOut, a2, n2); pOut += n2; @@ -211137,7 +221963,7 @@ static int sessionRebase( if( sOut.nBuf>0 ){ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); } - }else{ + }else if( ppOut ){ *ppOut = (void*)sOut.aBuf; *pnOut = sOut.nBuf; sOut.aBuf = 0; @@ -211880,8 +222706,20 @@ typedef wx_sqlite3_uint64 u64; #endif #define testcase(x) -#define ALWAYS(x) 1 -#define NEVER(x) 0 + +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 +#endif +#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) +# 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 #define MIN(x,y) (((x) < (y)) ? (x) : (y)) #define MAX(x,y) (((x) > (y)) ? (x) : (y)) @@ -211941,7 +222779,7 @@ SQLITE_API extern int wx_sqlite3_fts5_may_be_corrupt; ** A version of memcmp() that does not cause asan errors if one of the pointer ** parameters is NULL and the number of bytes to compare is zero. */ -#define fts5Memcmp(s1, s2, n) ((n)==0 ? 0 : memcmp((s1), (s2), (n))) +#define fts5Memcmp(s1, s2, n) ((n)<=0 ? 0 : memcmp((s1), (s2), (n))) /* Mark a function parameter as unused, to suppress nuisance compiler ** warnings. */ @@ -212119,7 +222957,7 @@ static void wx_sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, char *zFmt, ... static char *wx_sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...); #define fts5BufferZero(x) wx_sqlite3Fts5BufferZero(x) -#define fts5BufferAppendVarint(a,b,c) wx_sqlite3Fts5BufferAppendVarint(a,b,c) +#define fts5BufferAppendVarint(a,b,c) wx_sqlite3Fts5BufferAppendVarint(a,b,(i64)c) #define fts5BufferFree(a) wx_sqlite3Fts5BufferFree(a) #define fts5BufferAppendBlob(a,b,c,d) wx_sqlite3Fts5BufferAppendBlob(a,b,c,d) #define fts5BufferSet(a,b,c,d) wx_sqlite3Fts5BufferSet(a,b,c,d) @@ -212280,6 +223118,9 @@ static void wx_sqlite3Fts5IndexCloseReader(Fts5Index*); */ static const char *wx_sqlite3Fts5IterTerm(Fts5IndexIter*, int*); static int wx_sqlite3Fts5IterNextScan(Fts5IndexIter*); +static void *wx_sqlite3Fts5StructureRef(Fts5Index*); +static void wx_sqlite3Fts5StructureRelease(void*); +static int wx_sqlite3Fts5StructureTest(Fts5Index*, void*); /* @@ -213057,9 +223898,9 @@ struct fts5yyParser { }; typedef struct fts5yyParser fts5yyParser; +/* #include */ #ifndef NDEBUG /* #include */ -/* #include */ static FILE *fts5yyTraceFILE = 0; static char *fts5yyTracePrompt = 0; #endif /* NDEBUG */ @@ -213996,8 +224837,8 @@ static void wx_sqlite3Fts5Parser( fts5yyact = fts5yy_find_shift_action((fts5YYCODETYPE)fts5yymajor,fts5yyact); if( fts5yyact >= fts5YY_MIN_REDUCE ){ unsigned int fts5yyruleno = fts5yyact - fts5YY_MIN_REDUCE; /* Reduce by this rule */ - assert( fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ); #ifndef NDEBUG + assert( fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ); if( fts5yyTraceFILE ){ int fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno]; if( fts5yysize ){ @@ -214095,14 +224936,13 @@ static void wx_sqlite3Fts5Parser( fts5yy_destructor(fts5yypParser, (fts5YYCODETYPE)fts5yymajor, &fts5yyminorunion); fts5yymajor = fts5YYNOCODE; }else{ - while( fts5yypParser->fts5yytos >= fts5yypParser->fts5yystack - && (fts5yyact = fts5yy_find_reduce_action( - fts5yypParser->fts5yytos->stateno, - fts5YYERRORSYMBOL)) > fts5YY_MAX_SHIFTREDUCE - ){ + while( fts5yypParser->fts5yytos > fts5yypParser->fts5yystack ){ + fts5yyact = fts5yy_find_reduce_action(fts5yypParser->fts5yytos->stateno, + fts5YYERRORSYMBOL); + if( fts5yyact<=fts5YY_MAX_SHIFTREDUCE ) break; fts5yy_pop_parser_stack(fts5yypParser); } - if( fts5yypParser->fts5yytos < fts5yypParser->fts5yystack || fts5yymajor==0 ){ + if( fts5yypParser->fts5yytos <= fts5yypParser->fts5yystack || fts5yymajor==0 ){ fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion); fts5yy_parse_failed(fts5yypParser); #ifndef fts5YYNOERRORRECOVERY @@ -214965,7 +225805,6 @@ static void wx_sqlite3Fts5BufferAppendBlob( u32 nData, const u8 *pData ){ - assert_nc( *pRc || nData>=0 ); if( nData ){ if( fts5BufferGrow(pRc, pBuf, nData) ) return; memcpy(&pBuf->p[pBuf->n], pData, nData); @@ -215075,7 +225914,7 @@ static int wx_sqlite3Fts5PoslistNext64( return 1; }else{ i64 iOff = *piOff; - int iVal; + u32 iVal; fts5FastGetVarint32(a, i, iVal); if( iVal<=1 ){ if( iVal==0 ){ @@ -215084,15 +225923,19 @@ static int wx_sqlite3Fts5PoslistNext64( } fts5FastGetVarint32(a, i, iVal); iOff = ((i64)iVal) << 32; + assert( iOff>=0 ); fts5FastGetVarint32(a, i, iVal); if( iVal<2 ){ /* This is a corrupt record. So stop parsing it here. */ *piOff = -1; return 1; } + *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); + }else{ + *piOff = (iOff & (i64)0x7FFFFFFF<<32)+((iOff + (iVal-2)) & 0x7FFFFFFF); } - *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); *pi = i; + assert_nc( *piOff>=iOff ); return 0; } } @@ -215131,14 +225974,16 @@ static void wx_sqlite3Fts5PoslistSafeAppend( i64 *piPrev, i64 iPos ){ - static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; - if( (iPos & colmask) != (*piPrev & colmask) ){ - pBuf->p[pBuf->n++] = 1; - pBuf->n += wx_sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); - *piPrev = (iPos & colmask); + if( iPos>=*piPrev ){ + static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; + if( (iPos & colmask) != (*piPrev & colmask) ){ + pBuf->p[pBuf->n++] = 1; + pBuf->n += wx_sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); + *piPrev = (iPos & colmask); + } + pBuf->n += wx_sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); + *piPrev = iPos; } - pBuf->n += wx_sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); - *piPrev = iPos; } static int wx_sqlite3Fts5PoslistWriterAppend( @@ -215840,7 +226685,7 @@ static int wx_sqlite3Fts5ConfigParse( nByte = nArg * (sizeof(char*) + sizeof(u8)); pRet->azCol = (char**)wx_sqlite3Fts5MallocZero(&rc, nByte); - pRet->abUnindexed = (u8*)&pRet->azCol[nArg]; + pRet->abUnindexed = pRet->azCol ? (u8*)&pRet->azCol[nArg] : 0; pRet->zDb = wx_sqlite3Fts5Strndup(&rc, azArg[1], -1); pRet->zName = wx_sqlite3Fts5Strndup(&rc, azArg[2], -1); pRet->bColumnsize = 1; @@ -215865,6 +226710,7 @@ static int wx_sqlite3Fts5ConfigParse( z = fts5ConfigSkipWhitespace(z); if( z && *z=='=' ){ bOption = 1; + assert( zOne!=0 ); z++; if( bMustBeCol ) z = 0; } @@ -215881,7 +226727,11 @@ static int wx_sqlite3Fts5ConfigParse( rc = SQLITE_ERROR; }else{ if( bOption ){ - rc = fts5ConfigParseSpecial(pGlobal, pRet, zOne, zTwo?zTwo:"", pzErr); + rc = fts5ConfigParseSpecial(pGlobal, pRet, + ALWAYS(zOne)?zOne:"", + zTwo?zTwo:"", + pzErr + ); }else{ rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr); zOne = 0; @@ -216399,6 +227249,7 @@ static void wx_sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ va_list ap; va_start(ap, zFmt); if( pParse->rc==SQLITE_OK ){ + assert( pParse->zErr==0 ); pParse->zErr = wx_sqlite3_vmprintf(zFmt, ap); pParse->rc = SQLITE_ERROR; } @@ -216552,6 +227403,19 @@ static int wx_sqlite3Fts5ExprNew( return sParse.rc; } +/* +** Assuming that buffer z is at least nByte bytes in size and contains a +** valid utf-8 string, return the number of characters in the string. +*/ +static int fts5ExprCountChar(const char *z, int nByte){ + int nRet = 0; + int ii; + for(ii=0; ii=3 ){ + + if( fts5ExprCountChar(&zText[iFirst], i-iFirst)>=3 ){ int jj; zExpr[iOut++] = '"'; for(jj=iFirst; jjpSynonym ); assert( bDesc==0 || bDesc==1 ); for(p=pTerm; p; p=p->pSynonym){ @@ -217889,6 +228755,9 @@ static Fts5ExprNearset *wx_sqlite3Fts5ParseNearset( }else{ if( pRet->nPhrase>0 ){ Fts5ExprPhrase *pLast = pRet->apPhrase[pRet->nPhrase-1]; + assert( pParse!=0 ); + assert( pParse->apPhrase!=0 ); + assert( pParse->nPhrase>=2 ); assert( pLast==pParse->apPhrase[pParse->nPhrase-2] ); if( pPhrase->nTerm==0 ){ fts5ExprPhraseFree(pPhrase); @@ -218137,7 +229006,7 @@ static int wx_sqlite3Fts5ExprClonePhrase( sCtx.pPhrase = wx_sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase)); } - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ /* All the allocations succeeded. Put the expression object together. */ pNew->pIndex = pExpr->pIndex; pNew->pConfig = pExpr->pConfig; @@ -218408,9 +229277,8 @@ static void wx_sqlite3Fts5ParseSetColset( ){ Fts5Colset *pFree = pColset; if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){ - pParse->rc = SQLITE_ERROR; - pParse->zErr = wx_sqlite3_mprintf( - "fts5: column queries are not supported (detail=none)" + wx_sqlite3Fts5ParseError(pParse, + "fts5: column queries are not supported (detail=none)" ); }else{ fts5ParseSetColset(pParse, pExpr, pColset, &pFree); @@ -218584,13 +229452,10 @@ static Fts5ExprNode *wx_sqlite3Fts5ParseNode( || pPhrase->nTerm>1 || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst) ){ - assert( pParse->rc==SQLITE_OK ); - pParse->rc = SQLITE_ERROR; - assert( pParse->zErr==0 ); - pParse->zErr = wx_sqlite3_mprintf( + wx_sqlite3Fts5ParseError(pParse, "fts5: %s queries are not supported (detail!=full)", pNear->nPhrase==1 ? "phrase": "NEAR" - ); + ); wx_sqlite3_free(pRet); pRet = 0; } @@ -218676,6 +229541,7 @@ static Fts5ExprNode *wx_sqlite3Fts5ParseImplicitAnd( return pRet; } +#ifdef SQLITE_TEST static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ wx_sqlite3_int64 nByte = 0; Fts5ExprTerm *p; @@ -219042,12 +229908,14 @@ static void fts5ExprFold( wx_sqlite3_result_int(pCtx, wx_sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics)); } } +#endif /* ifdef SQLITE_TEST */ /* ** This is called during initialization to register the fts5_expr() scalar ** UDF with the SQLite handle passed as the only argument. */ static int wx_sqlite3Fts5ExprInit(Fts5Global *pGlobal, wx_sqlite3 *db){ +#ifdef SQLITE_TEST struct Fts5ExprFunc { const char *z; void (*x)(wx_sqlite3_context*,int,wx_sqlite3_value**); @@ -219065,6 +229933,10 @@ static int wx_sqlite3Fts5ExprInit(Fts5Global *pGlobal, wx_sqlite3 *db){ struct Fts5ExprFunc *p = &aFunc[i]; rc = wx_sqlite3_create_function(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0); } +#else + int rc = SQLITE_OK; + UNUSED_PARAM2(pGlobal,db); +#endif /* Avoid warnings indicating that wx_sqlite3Fts5ParserTrace() and ** wx_sqlite3Fts5ParserFallback() are unused */ @@ -219115,6 +229987,15 @@ struct Fts5PoslistPopulator { int bMiss; }; +/* +** Clear the position lists associated with all phrases in the expression +** passed as the first argument. Argument bLive is true if the expression +** might be pointing to a real entry, otherwise it has just been reset. +** +** At present this function is only used for detail=col and detail=none +** fts5 tables. This implies that all phrases must be at most 1 token +** in size, as phrase matches are not supported without detail=full. +*/ static Fts5PoslistPopulator *wx_sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){ Fts5PoslistPopulator *pRet; pRet = wx_sqlite3_malloc64(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); @@ -219124,7 +230005,7 @@ static Fts5PoslistPopulator *wx_sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, in for(i=0; inPhrase; i++){ Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist; Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; - assert( pExpr->apExprPhrase[i]->nTerm==1 ); + assert( pExpr->apExprPhrase[i]->nTerm<=1 ); if( bLive && (pBuf->n==0 || pNode->iRowid!=pExpr->pRoot->iRowid || pNode->bEof) ){ @@ -219675,7 +230556,7 @@ static int wx_sqlite3Fts5HashWrite( p->bContent = 1; }else{ /* Append a new column value, if necessary */ - assert( iCol>=p->iCol ); + assert_nc( iCol>=p->iCol ); if( iCol!=p->iCol ){ if( pHash->eDetail==FTS5_DETAIL_FULL ){ pPtr[p->nData++] = 0x01; @@ -219934,6 +230815,8 @@ static void wx_sqlite3Fts5HashScanEntry( # error "FTS5_MAX_PREFIX_INDEXES is too large" #endif +#define FTS5_MAX_LEVEL 64 + /* ** Details: ** @@ -220176,7 +231059,7 @@ struct Fts5Index { wx_sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */ wx_sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */ wx_sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ - wx_sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=? */ + wx_sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ wx_sqlite3_stmt *pIdxSelect; int nRead; /* Total number of blocks read */ @@ -220311,7 +231194,7 @@ struct Fts5SegIter { int iLeafPgno; /* Current leaf page number */ Fts5Data *pLeaf; /* Current leaf data */ Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ - int iLeafOffset; /* Byte offset within current leaf */ + i64 iLeafOffset; /* Byte offset within current leaf */ /* Next method */ void (*xNext)(Fts5Index*, Fts5SegIter*, int*); @@ -220480,8 +231363,11 @@ static int fts5BufferCompareBlob( ** res = *pLeft - *pRight */ static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){ - int nCmp = MIN(pLeft->n, pRight->n); - int res = fts5Memcmp(pLeft->p, pRight->p, nCmp); + int nCmp, res; + nCmp = MIN(pLeft->n, pRight->n); + assert( nCmp<=0 || pLeft->p!=0 ); + assert( nCmp<=0 || pRight->p!=0 ); + res = fts5Memcmp(pLeft->p, pRight->p, nCmp); return (res==0 ? (pLeft->n - pRight->n) : res); } @@ -220577,6 +231463,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ return pRet; } + /* ** Release a reference to data record returned by an earlier call to ** fts5DataRead(). @@ -220701,6 +231588,58 @@ static void fts5StructureRef(Fts5Structure *pStruct){ pStruct->nRef++; } +static void *wx_sqlite3Fts5StructureRef(Fts5Index *p){ + fts5StructureRef(p->pStruct); + return (void*)p->pStruct; +} +static void wx_sqlite3Fts5StructureRelease(void *p){ + if( p ){ + fts5StructureRelease((Fts5Structure*)p); + } +} +static int wx_sqlite3Fts5StructureTest(Fts5Index *p, void *pStruct){ + if( p->pStruct!=(Fts5Structure*)pStruct ){ + return SQLITE_ABORT; + } + return SQLITE_OK; +} + +/* +** Ensure that structure object (*pp) is writable. +** +** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. If +** an error occurs, (*pRc) is set to an SQLite error code before returning. +*/ +static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ + Fts5Structure *p = *pp; + if( *pRc==SQLITE_OK && p->nRef>1 ){ + i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel); + Fts5Structure *pNew; + pNew = (Fts5Structure*)wx_sqlite3Fts5MallocZero(pRc, nByte); + if( pNew ){ + int i; + memcpy(pNew, p, nByte); + for(i=0; inLevel; i++) pNew->aLevel[i].aSeg = 0; + for(i=0; inLevel; i++){ + Fts5StructureLevel *pLvl = &pNew->aLevel[i]; + nByte = sizeof(Fts5StructureSegment) * pNew->aLevel[i].nSeg; + pLvl->aSeg = (Fts5StructureSegment*)wx_sqlite3Fts5MallocZero(pRc, nByte); + if( pLvl->aSeg==0 ){ + for(i=0; inLevel; i++){ + wx_sqlite3_free(pNew->aLevel[i].aSeg); + } + wx_sqlite3_free(pNew); + return; + } + memcpy(pLvl->aSeg, p->aLevel[i].aSeg, nByte); + } + p->nRef--; + pNew->nRef = 1; + } + *pp = pNew; + } +} + /* ** Deserialize and return the structure record currently stored in serialized ** form within buffer pData/nData. @@ -220802,9 +231741,11 @@ static int fts5StructureDecode( } /* -** +** Add a level to the Fts5Structure.aLevel[] array of structure object +** (*ppStruct). */ static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ + fts5StructureMakeWritable(pRc, ppStruct); if( *pRc==SQLITE_OK ){ Fts5Structure *pStruct = *ppStruct; int nLevel = pStruct->nLevel; @@ -221491,7 +232432,7 @@ static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ - int iOff = pIter->iLeafOffset; + i64 iOff = pIter->iLeafOffset; ASSERT_SZLEAF_OK(pIter->pLeaf); if( iOff>=pIter->pLeaf->szLeaf ){ @@ -221524,7 +232465,7 @@ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ */ static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){ u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ - int iOff = pIter->iLeafOffset; /* Offset to read at */ + i64 iOff = pIter->iLeafOffset; /* Offset to read at */ int nNew; /* Bytes of new data */ iOff += fts5GetVarint32(&a[iOff], nNew); @@ -221598,6 +232539,7 @@ static void fts5SegIterInit( if( p->rc==SQLITE_OK ){ pIter->iLeafOffset = 4; + assert( pIter->pLeaf!=0 ); assert_nc( pIter->pLeaf->nn>4 ); assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 ); pIter->iPgidxOff = pIter->pLeaf->szLeaf+1; @@ -221700,8 +232642,12 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){ int iRowidOff; iRowidOff = fts5LeafFirstRowidOff(pNew); if( iRowidOff ){ - pIter->pLeaf = pNew; - pIter->iLeafOffset = iRowidOff; + if( iRowidOff>=pNew->szLeaf ){ + p->rc = FTS5_CORRUPT; + }else{ + pIter->pLeaf = pNew; + pIter->iLeafOffset = iRowidOff; + } } } @@ -221952,7 +232898,6 @@ static void fts5SegIterNext( ** this block is particularly performance critical, so equivalent ** code is inlined. */ int nSz; - assert( p->rc==SQLITE_OK ); assert_nc( pIter->iLeafOffset<=pIter->pLeaf->nn ); fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz); pIter->bDel = (nSz & 0x0001); @@ -221982,7 +232927,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ if( pDlidx ){ int iSegid = pIter->pSeg->iSegid; pgnoLast = fts5DlidxIterPgno(pDlidx); - pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); + pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); }else{ Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ @@ -222009,7 +232954,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ ** forward to find the page containing the last rowid. */ for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){ i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno); - Fts5Data *pNew = fts5DataRead(p, iAbs); + Fts5Data *pNew = fts5LeafRead(p, iAbs); if( pNew ){ int iRowid, bTermless; iRowid = fts5LeafFirstRowidOff(pNew); @@ -222040,6 +232985,10 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ pIter->pLeaf = pLast; pIter->iLeafPgno = pgnoLast; iOff = fts5LeafFirstRowidOff(pLast); + if( iOff>pLast->szLeaf ){ + p->rc = FTS5_CORRUPT; + return; + } iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid); pIter->iLeafOffset = iOff; @@ -222048,7 +232997,6 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ }else{ pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast); } - } fts5SegIterReverseInitPage(p, pIter); @@ -222100,21 +233048,20 @@ static void fts5LeafSeek( Fts5SegIter *pIter, /* Iterator to seek */ const u8 *pTerm, int nTerm /* Term to search for */ ){ - int iOff; + u32 iOff; const u8 *a = pIter->pLeaf->p; - int szLeaf = pIter->pLeaf->szLeaf; - int n = pIter->pLeaf->nn; + u32 n = (u32)pIter->pLeaf->nn; u32 nMatch = 0; u32 nKeep = 0; u32 nNew = 0; u32 iTermOff; - int iPgidx; /* Current offset in pgidx */ + u32 iPgidx; /* Current offset in pgidx */ int bEndOfPage = 0; assert( p->rc==SQLITE_OK ); - iPgidx = szLeaf; + iPgidx = (u32)pIter->pLeaf->szLeaf; iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff); iOff = iTermOff; if( iOff>n ){ @@ -222180,15 +233127,15 @@ static void fts5LeafSeek( if( pIter->pLeaf==0 ) return; a = pIter->pLeaf->p; if( fts5LeafIsTermless(pIter->pLeaf)==0 ){ - iPgidx = pIter->pLeaf->szLeaf; + iPgidx = (u32)pIter->pLeaf->szLeaf; iPgidx += fts5GetVarint32(&pIter->pLeaf->p[iPgidx], iOff); - if( iOff<4 || iOff>=pIter->pLeaf->szLeaf ){ + if( iOff<4 || (i64)iOff>=pIter->pLeaf->szLeaf ){ p->rc = FTS5_CORRUPT; return; }else{ nKeep = 0; iTermOff = iOff; - n = pIter->pLeaf->nn; + n = (u32)pIter->pLeaf->nn; iOff += fts5GetVarint32(&a[iOff], nNew); break; } @@ -222556,7 +233503,7 @@ static void fts5SegIterGotoPage( fts5SegIterNextPage(p, pIter); assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno ); - if( p->rc==SQLITE_OK ){ + if( p->rc==SQLITE_OK && ALWAYS(pIter->pLeaf!=0) ){ int iOff; u8 *a = pIter->pLeaf->p; int n = pIter->pLeaf->szLeaf; @@ -222988,7 +233935,11 @@ static void fts5SegiterPoslist( Fts5Colset *pColset, Fts5Buffer *pBuf ){ + assert( pBuf!=0 ); + assert( pSeg!=0 ); if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+FTS5_DATA_ZERO_PADDING) ){ + assert( pBuf->p!=0 ); + assert( pBuf->nSpace >= pBuf->n+pSeg->nPos+FTS5_DATA_ZERO_PADDING ); memset(&pBuf->p[pBuf->n+pSeg->nPos], 0, FTS5_DATA_ZERO_PADDING); if( pColset==0 ){ fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback); @@ -223064,7 +234015,7 @@ static void fts5IndexExtractColset( } fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy); } - if( p==pEnd ){ + if( p>=pEnd ){ pIter->base.pData = pIter->poslist.p; pIter->base.nData = pIter->poslist.n; return; @@ -223212,6 +234163,7 @@ static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){ } static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){ + assert( pIter!=0 || (*pRc)!=SQLITE_OK ); if( *pRc==SQLITE_OK ){ Fts5Config *pConfig = pIter->pIndex->pConfig; if( pConfig->eDetail==FTS5_DETAIL_NONE ){ @@ -223283,7 +234235,10 @@ static void fts5MultiIterNew( } } *ppOut = pNew = fts5MultiIterAlloc(p, nSeg); - if( pNew==0 ) return; + if( pNew==0 ){ + assert( p->rc!=SQLITE_OK ); + goto fts5MultiIterNew_post_check; + } pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC)); pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY)); pNew->pColset = pColset; @@ -223347,6 +234302,10 @@ static void fts5MultiIterNew( fts5MultiIterFree(pNew); *ppOut = 0; } + +fts5MultiIterNew_post_check: + assert( (*ppOut)!=0 || p->rc!=SQLITE_OK ); + return; } /* @@ -223394,7 +234353,8 @@ static void fts5MultiIterNew2( ** False otherwise. */ static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){ - assert( p->rc + assert( pIter!=0 || p->rc!=SQLITE_OK ); + assert( p->rc!=SQLITE_OK || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof ); return (p->rc || pIter->base.bEof); @@ -223890,7 +234850,9 @@ static void fts5WriteAppendRowid( fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid); }else{ assert_nc( p->rc || iRowid>pWriter->iPrevRowid ); - fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid); + fts5BufferAppendVarint(&p->rc, &pPage->buf, + (u64)iRowid - (u64)pWriter->iPrevRowid + ); } pWriter->iPrevRowid = iRowid; pWriter->bFirstRowidInDoclist = 0; @@ -224198,6 +235160,7 @@ static void fts5IndexMergeLevel( ** and last leaf page number at the same time. */ fts5WriteFinish(p, &writer, &pSeg->pgnoLast); + assert( pIter!=0 || p->rc!=SQLITE_OK ); if( fts5MultiIterEof(p, pIter) ){ int i; @@ -224298,7 +235261,7 @@ static void fts5IndexAutomerge( Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */ int nLeaf /* Number of output leaves just written */ ){ - if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 ){ + if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 && ALWAYS((*ppStruct)!=0) ){ Fts5Structure *pStruct = *ppStruct; u64 nWrite; /* Initial value of write-counter */ int nWork; /* Number of work-quanta to perform */ @@ -224421,14 +235384,14 @@ static void fts5FlushOneHash(Fts5Index *p){ fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); }else{ i64 iRowid = 0; - i64 iDelta = 0; + u64 iDelta = 0; int iOff = 0; /* The entire doclist will not fit on this leaf. The following ** loop iterates through the poslists that make up the current ** doclist. */ while( p->rc==SQLITE_OK && iOffnLevel = pStruct->nLevel+1; + pNew->nLevel = MIN(pStruct->nLevel+1, FTS5_MAX_LEVEL); pNew->nRef = 1; pNew->nWriteCounter = pStruct->nWriteCounter; - pLvl = &pNew->aLevel[pStruct->nLevel]; + pLvl = &pNew->aLevel[pNew->nLevel-1]; pLvl->aSeg = (Fts5StructureSegment*)wx_sqlite3Fts5MallocZero(&p->rc, nByte); if( pLvl->aSeg ){ int iLvl, iSeg; @@ -224653,7 +235616,7 @@ static int wx_sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ static void fts5AppendRowid( Fts5Index *p, - i64 iDelta, + u64 iDelta, Fts5Iter *pUnused, Fts5Buffer *pBuf ){ @@ -224663,7 +235626,7 @@ static void fts5AppendRowid( static void fts5AppendPoslist( Fts5Index *p, - i64 iDelta, + u64 iDelta, Fts5Iter *pMulti, Fts5Buffer *pBuf ){ @@ -224738,10 +235701,10 @@ static void fts5MergeAppendDocid( } #endif -#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \ - assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \ - fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \ - (iLastRowid) = (iRowid); \ +#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \ + assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \ + fts5BufferSafeAppendVarint((pBuf), (u64)(iRowid) - (u64)(iLastRowid)); \ + (iLastRowid) = (iRowid); \ } /* @@ -224860,7 +235823,7 @@ static void fts5MergePrefixLists( Fts5Buffer *aBuf /* Other lists to merge in */ ){ #define fts5PrefixMergerNextPosition(p) \ - wx_sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos); + wx_sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos) #define FTS5_MERGE_NLIST 16 PrefixMerger aMerger[FTS5_MERGE_NLIST]; PrefixMerger *pHead = 0; @@ -224873,7 +235836,7 @@ static void fts5MergePrefixLists( /* Initialize a doclist-iterator for each input buffer. Arrange them in ** a linked-list starting at pHead in ascending order of rowid. Avoid ** linking any iterators already at EOF into the linked list at all. */ - assert( nBuf+1<=sizeof(aMerger)/sizeof(aMerger[0]) ); + assert( nBuf+1<=(int)(sizeof(aMerger)/sizeof(aMerger[0])) ); memset(aMerger, 0, sizeof(PrefixMerger)*(nBuf+1)); pHead = &aMerger[nBuf]; fts5DoclistIterInit(p1, &pHead->iter); @@ -224959,7 +235922,8 @@ static void fts5MergePrefixLists( nTail = pHead->iter.nPoslist - pHead->iOff; /* WRITEPOSLISTSIZE */ - assert( tmp.n+nTail<=nTmp ); + assert_nc( tmp.n+nTail<=nTmp ); + assert( tmp.n+nTail<=nTmp+nMerge*10 ); if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING ){ if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; break; @@ -225011,7 +235975,7 @@ static void fts5SetupPrefixIter( int nMerge = 1; void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); - void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*); + void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ xMerge = fts5MergeRowidLists; xAppend = fts5AppendRowid; @@ -225050,7 +236014,7 @@ static void fts5SetupPrefixIter( Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; p1->xSetOutputs(p1, pSeg); if( p1->base.nData ){ - xAppend(p, p1->base.iRowid-iLastRowid, p1, &doclist); + xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist); iLastRowid = p1->base.iRowid; } } @@ -225098,7 +236062,7 @@ static void fts5SetupPrefixIter( iLastRowid = 0; } - xAppend(p, p1->base.iRowid-iLastRowid, p1, &doclist); + xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist); iLastRowid = p1->base.iRowid; } @@ -225366,7 +236330,7 @@ static int wx_sqlite3Fts5IndexQuery( if( wx_sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){ int iIdx = 0; /* Index to search */ int iPrefixIdx = 0; /* +1 prefix index */ - if( nToken ) memcpy(&buf.p[1], pToken, nToken); + if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); /* Figure out which index to search and set iIdx accordingly. If this ** is a prefix query for which there is no prefix index, set iIdx to @@ -225407,11 +236371,15 @@ static int wx_sqlite3Fts5IndexQuery( /* Scan multiple terms in the main index */ int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet); - assert( p->rc!=SQLITE_OK || pRet->pColset==0 ); - fts5IterSetOutputCb(&p->rc, pRet); - if( p->rc==SQLITE_OK ){ - Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; - if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg); + if( pRet==0 ){ + assert( p->rc!=SQLITE_OK ); + }else{ + assert( pRet->pColset==0 ); + fts5IterSetOutputCb(&p->rc, pRet); + if( p->rc==SQLITE_OK ){ + Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; + if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg); + } } } @@ -225659,7 +236627,7 @@ static int fts5QueryCksum( Fts5IndexIter *pIter = 0; int rc = wx_sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter); - while( rc==SQLITE_OK && 0==wx_sqlite3Fts5IterEof(pIter) ){ + while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==wx_sqlite3Fts5IterEof(pIter) ){ i64 rowid = pIter->iRowid; if( eDetail==FTS5_DETAIL_NONE ){ @@ -226024,6 +236992,7 @@ static int wx_sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCk Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */ Fts5Iter *pIter; /* Used to iterate through entire index */ Fts5Structure *pStruct; /* Index structure */ + int iLvl, iSeg; #ifdef SQLITE_DEBUG /* Used by extra internal tests only run if NDEBUG is not defined */ @@ -226034,15 +237003,16 @@ static int wx_sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCk /* Load the FTS index structure */ pStruct = fts5StructureRead(p); + if( pStruct==0 ){ + assert( p->rc!=SQLITE_OK ); + return fts5IndexReturn(p); + } /* Check that the internal nodes of each segment match the leaves */ - if( pStruct ){ - int iLvl, iSeg; - for(iLvl=0; iLvlnLevel; iLvl++){ - for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ - Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; - fts5IndexIntegrityCheckSegment(p, pSeg); - } + for(iLvl=0; iLvlnLevel; iLvl++){ + for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ + Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; + fts5IndexIntegrityCheckSegment(p, pSeg); } } @@ -226071,6 +237041,7 @@ static int wx_sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCk /* If this is a new term, query for it. Update cksum3 with the results. */ fts5TestTerm(p, &term, z, n, cksum2, &cksum3); + if( p->rc ) break; if( eDetail==FTS5_DETAIL_NONE ){ if( 0==fts5MultiIterIsEmpty(p, pIter) ){ @@ -226106,6 +237077,7 @@ static int wx_sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCk ** function only. */ +#ifdef SQLITE_TEST /* ** Decode a segment-data rowid from the %_data table. This function is ** the opposite of macro FTS5_SEGMENT_ROWID(). @@ -226128,7 +237100,9 @@ static void fts5DecodeRowid( *piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B) - 1)); } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ int iSegid, iHeight, iPgno, bDlidx; /* Rowid compenents */ fts5DecodeRowid(iKey, &iSegid, &bDlidx, &iHeight, &iPgno); @@ -226146,7 +237120,9 @@ static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ ); } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST static void fts5DebugStructure( int *pRc, /* IN/OUT: error code */ Fts5Buffer *pBuf, @@ -226168,7 +237144,9 @@ static void fts5DebugStructure( wx_sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** This is part of the fts5_decode() debugging aid. ** @@ -226193,7 +237171,9 @@ static void fts5DecodeStructure( fts5DebugStructure(pRc, pBuf, p); fts5StructureRelease(p); } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** This is part of the fts5_decode() debugging aid. ** @@ -226216,7 +237196,9 @@ static void fts5DecodeAverages( zSpace = " "; } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** Buffer (a/n) is assumed to contain a list of serialized varints. Read ** each varint and append its string representation to buffer pBuf. Return @@ -226233,7 +237215,9 @@ static int fts5DecodePoslist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ } return iOff; } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** The start of buffer (a/n) contains the start of a doclist. The doclist ** may or may not finish within the buffer. This function appends a text @@ -226266,7 +237250,9 @@ static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ return iOff; } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** This function is part of the fts5_decode() debugging function. It is ** only ever used with detail=none tables. @@ -226307,7 +237293,9 @@ static void fts5DecodeRowidList( wx_sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp); } } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** The implementation of user-defined scalar function fts5_decode(). */ @@ -226516,7 +237504,9 @@ static void fts5DecodeFunction( } fts5BufferFree(&s); } +#endif /* SQLITE_TEST */ +#ifdef SQLITE_TEST /* ** The implementation of user-defined scalar function fts5_rowid(). */ @@ -226550,6 +237540,7 @@ static void fts5RowidFunction( } } } +#endif /* SQLITE_TEST */ /* ** This is called as part of registering the FTS5 module with database @@ -226560,6 +237551,7 @@ static void fts5RowidFunction( ** SQLite error code is returned instead. */ static int wx_sqlite3Fts5IndexInit(wx_sqlite3 *db){ +#ifdef SQLITE_TEST int rc = wx_sqlite3_create_function( db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0 ); @@ -226577,6 +237569,10 @@ static int wx_sqlite3Fts5IndexInit(wx_sqlite3 *db){ ); } return rc; +#else + return SQLITE_OK; + UNUSED_PARAM(db); +#endif } @@ -226612,7 +237608,9 @@ static int wx_sqlite3Fts5IndexReset(Fts5Index *p){ ** assert() conditions in the fts5 code are activated - conditions that are ** only true if it is guaranteed that the fts5 database is not corrupt. */ +#ifdef SQLITE_DEBUG SQLITE_API int wx_sqlite3_fts5_may_be_corrupt = 1; +#endif typedef struct Fts5Auxdata Fts5Auxdata; @@ -226848,7 +237846,7 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ break; case FTS5_SYNC: - assert( p->ts.eState==1 ); + assert( p->ts.eState==1 || p->ts.eState==2 ); p->ts.eState = 2; break; @@ -226863,21 +237861,21 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ break; case FTS5_SAVEPOINT: - assert( p->ts.eState==1 ); + assert( p->ts.eState>=1 ); assert( iSavepoint>=0 ); assert( iSavepoint>=p->ts.iSavepoint ); p->ts.iSavepoint = iSavepoint; break; case FTS5_RELEASE: - assert( p->ts.eState==1 ); + assert( p->ts.eState>=1 ); assert( iSavepoint>=0 ); assert( iSavepoint<=p->ts.iSavepoint ); p->ts.iSavepoint = iSavepoint-1; break; case FTS5_ROLLBACKTO: - assert( p->ts.eState==1 ); + assert( p->ts.eState>=1 ); assert( iSavepoint>=-1 ); /* The following assert() can fail if another vtab strikes an error ** within an xSavepoint() call then SQLite calls xRollbackTo() - without @@ -227392,7 +238390,7 @@ static int fts5SorterNext(Fts5Cursor *pCsr){ rc = wx_sqlite3_step(pSorter->pStmt); if( rc==SQLITE_DONE ){ rc = SQLITE_OK; - CsrFlagSet(pCsr, FTS5CSR_EOF); + CsrFlagSet(pCsr, FTS5CSR_EOF|FTS5CSR_REQUIRE_CONTENT); }else if( rc==SQLITE_ROW ){ const u8 *a; const u8 *aBlob; @@ -227962,7 +238960,8 @@ static int fts5FilterMethod( pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg ); if( rc==SQLITE_OK ){ - if( pCsr->ePlan==FTS5_PLAN_ROWID ){ + if( pRowidEq!=0 ){ + assert( pCsr->ePlan==FTS5_PLAN_ROWID ); wx_sqlite3_bind_value(pCsr->pStmt, 1, pRowidEq); }else{ wx_sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid); @@ -228212,7 +239211,7 @@ static int fts5UpdateMethod( int rc = SQLITE_OK; /* Return code */ /* A transaction must be open when this is called. */ - assert( pTab->ts.eState==1 ); + assert( pTab->ts.eState==1 || pTab->ts.eState==2 ); assert( pVtab->zErrMsg==0 ); assert( nArg==1 || nArg==(2+pConfig->nCol+2) ); @@ -228537,13 +239536,15 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ nInst++; if( nInst>=pCsr->nInstAlloc ){ - pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; + int nNewSize = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; aInst = (int*)wx_sqlite3_realloc64( - pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 + pCsr->aInst, nNewSize*sizeof(int)*3 ); if( aInst ){ pCsr->aInst = aInst; + pCsr->nInstAlloc = nNewSize; }else{ + nInst--; rc = SQLITE_NOMEM; break; } @@ -229378,7 +240379,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - wx_sqlite3_result_text(pCtx, "fts5: 2021-04-02 15:20:15 5d4c65779dab868b285519b19e4cf9d451d50c6048f06f653aa701ec212df45e", -1, SQLITE_TRANSIENT); + wx_sqlite3_result_text(pCtx, "fts5: 2023-03-22 11:56:21 0d1fc92f94cb6b76bffe3ec34d69cffde2924203304e8ffc4155597af0c191da", -1, SQLITE_TRANSIENT); } /* @@ -229451,7 +240452,9 @@ static int fts5Init(wx_sqlite3 *db){ } if( rc==SQLITE_OK ){ rc = wx_sqlite3_create_function( - db, "fts5_source_id", 0, SQLITE_UTF8, p, fts5SourceIdFunc, 0, 0 + db, "fts5_source_id", 0, + SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS, + p, fts5SourceIdFunc, 0, 0 ); } } @@ -229929,12 +240932,16 @@ static int fts5StorageDeleteFromIndex( if( pConfig->abUnindexed[iCol-1]==0 ){ const char *zText; int nText; + assert( pSeek==0 || apVal==0 ); + assert( pSeek!=0 || apVal!=0 ); if( pSeek ){ zText = (const char*)wx_sqlite3_column_text(pSeek, iCol); nText = wx_sqlite3_column_bytes(pSeek, iCol); - }else{ + }else if( ALWAYS(apVal) ){ zText = (const char*)wx_sqlite3_value_text(apVal[iCol-1]); nText = wx_sqlite3_value_bytes(apVal[iCol-1]); + }else{ + continue; } ctx.szCol = 0; rc = wx_sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, @@ -230570,8 +241577,9 @@ static int wx_sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){ assert( p->pConfig->bColumnsize ); rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); - if( rc==SQLITE_OK ){ + if( pLookup ){ int bCorrupt = 1; + assert( rc==SQLITE_OK ); wx_sqlite3_bind_int64(pLookup, 1, iRowid); if( SQLITE_ROW==wx_sqlite3_step(pLookup) ){ const u8 *aBlob = wx_sqlite3_column_blob(pLookup, 0); @@ -230584,6 +241592,8 @@ static int wx_sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){ if( bCorrupt && rc==SQLITE_OK ){ rc = FTS5_CORRUPT; } + }else{ + assert( rc!=SQLITE_OK ); } return rc; @@ -233274,6 +244284,7 @@ struct Fts5VocabCursor { int bEof; /* True if this cursor is at EOF */ Fts5IndexIter *pIter; /* Term/rowid iterator object */ + void *pStruct; /* From wx_sqlite3Fts5StructureRef() */ int nLeTerm; /* Size of zLeTerm in bytes */ char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */ @@ -233587,7 +244598,7 @@ static int fts5VocabOpenMethod( } if( rc==SQLITE_OK ){ - int nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); + i64 nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); pCsr = (Fts5VocabCursor*)wx_sqlite3Fts5MallocZero(&rc, nByte); } @@ -233607,6 +244618,8 @@ static int fts5VocabOpenMethod( static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){ pCsr->rowid = 0; wx_sqlite3Fts5IterClose(pCsr->pIter); + wx_sqlite3Fts5StructureRelease(pCsr->pStruct); + pCsr->pStruct = 0; pCsr->pIter = 0; wx_sqlite3_free(pCsr->zLeTerm); pCsr->nLeTerm = -1; @@ -233684,9 +244697,11 @@ static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){ static int fts5VocabNextMethod(wx_sqlite3_vtab_cursor *pCursor){ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; - int rc = SQLITE_OK; int nCol = pCsr->pFts5->pConfig->nCol; + int rc; + rc = wx_sqlite3Fts5StructureTest(pCsr->pFts5->pIndex, pCsr->pStruct); + if( rc!=SQLITE_OK ) return rc; pCsr->rowid++; if( pTab->eType==FTS5_VOCAB_INSTANCE ){ @@ -233860,6 +244875,9 @@ static int fts5VocabFilterMethod( if( rc==SQLITE_OK ){ Fts5Index *pIndex = pCsr->pFts5->pIndex; rc = wx_sqlite3Fts5IndexQuery(pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); + if( rc==SQLITE_OK ){ + pCsr->pStruct = wx_sqlite3Fts5StructureRef(pIndex); + } } if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){ rc = fts5VocabInstanceNewTerm(pCsr); @@ -234034,6 +245052,16 @@ SQLITE_EXTENSION_INIT1 #ifndef SQLITE_OMIT_VIRTUALTABLE + +#define STMT_NUM_INTEGER_COLUMN 10 +typedef struct StmtRow StmtRow; +struct StmtRow { + wx_sqlite3_int64 iRowid; /* Rowid value */ + char *zSql; /* column "sql" */ + int aCol[STMT_NUM_INTEGER_COLUMN+1]; /* all other column values */ + StmtRow *pNext; /* Next row to return */ +}; + /* stmt_vtab is a subclass of wx_sqlite3_vtab which will ** serve as the underlying representation of a stmt virtual table */ @@ -234051,8 +245079,7 @@ typedef struct stmt_cursor stmt_cursor; struct stmt_cursor { wx_sqlite3_vtab_cursor base; /* Base class - must be first */ wx_sqlite3 *db; /* Database connection for this cursor */ - wx_sqlite3_stmt *pStmt; /* Statement cursor is currently pointing at */ - wx_sqlite3_int64 iRowid; /* The rowid */ + StmtRow *pRow; /* Current row */ }; /* @@ -234092,11 +245119,15 @@ static int stmtConnect( #define STMT_COLUMN_MEM 10 /* SQLITE_STMTSTATUS_MEMUSED */ + (void)pAux; + (void)argc; + (void)argv; + (void)pzErr; rc = wx_sqlite3_declare_vtab(db, "CREATE TABLE x(sql,ncol,ro,busy,nscan,nsort,naidx,nstep," "reprep,run,mem)"); if( rc==SQLITE_OK ){ - pNew = wx_sqlite3_malloc( sizeof(*pNew) ); + pNew = wx_sqlite3_malloc64( sizeof(*pNew) ); *ppVtab = (wx_sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); @@ -234118,7 +245149,7 @@ static int stmtDisconnect(wx_sqlite3_vtab *pVtab){ */ static int stmtOpen(wx_sqlite3_vtab *p, wx_sqlite3_vtab_cursor **ppCursor){ stmt_cursor *pCur; - pCur = wx_sqlite3_malloc( sizeof(*pCur) ); + pCur = wx_sqlite3_malloc64( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->db = ((stmt_vtab*)p)->db; @@ -234126,10 +245157,21 @@ static int stmtOpen(wx_sqlite3_vtab *p, wx_sqlite3_vtab_cursor **ppCursor){ return SQLITE_OK; } +static void stmtCsrReset(stmt_cursor *pCur){ + StmtRow *pRow = 0; + StmtRow *pNext = 0; + for(pRow=pCur->pRow; pRow; pRow=pNext){ + pNext = pRow->pNext; + wx_sqlite3_free(pRow); + } + pCur->pRow = 0; +} + /* ** Destructor for a stmt_cursor. */ static int stmtClose(wx_sqlite3_vtab_cursor *cur){ + stmtCsrReset((stmt_cursor*)cur); wx_sqlite3_free(cur); return SQLITE_OK; } @@ -234140,8 +245182,9 @@ static int stmtClose(wx_sqlite3_vtab_cursor *cur){ */ static int stmtNext(wx_sqlite3_vtab_cursor *cur){ stmt_cursor *pCur = (stmt_cursor*)cur; - pCur->iRowid++; - pCur->pStmt = wx_sqlite3_next_stmt(pCur->db, pCur->pStmt); + StmtRow *pNext = pCur->pRow->pNext; + wx_sqlite3_free(pCur->pRow); + pCur->pRow = pNext; return SQLITE_OK; } @@ -234155,39 +245198,11 @@ static int stmtColumn( int i /* Which column to return */ ){ stmt_cursor *pCur = (stmt_cursor*)cur; - switch( i ){ - case STMT_COLUMN_SQL: { - wx_sqlite3_result_text(ctx, wx_sqlite3_sql(pCur->pStmt), -1, SQLITE_TRANSIENT); - break; - } - case STMT_COLUMN_NCOL: { - wx_sqlite3_result_int(ctx, wx_sqlite3_column_count(pCur->pStmt)); - break; - } - case STMT_COLUMN_RO: { - wx_sqlite3_result_int(ctx, wx_sqlite3_stmt_readonly(pCur->pStmt)); - break; - } - case STMT_COLUMN_BUSY: { - wx_sqlite3_result_int(ctx, wx_sqlite3_stmt_busy(pCur->pStmt)); - break; - } - default: { - assert( i==STMT_COLUMN_MEM ); - i = SQLITE_STMTSTATUS_MEMUSED + - STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP; - /* Fall thru */ - } - case STMT_COLUMN_NSCAN: - case STMT_COLUMN_NSORT: - case STMT_COLUMN_NAIDX: - case STMT_COLUMN_NSTEP: - case STMT_COLUMN_REPREP: - case STMT_COLUMN_RUN: { - wx_sqlite3_result_int(ctx, wx_sqlite3_stmt_status(pCur->pStmt, - i-STMT_COLUMN_NSCAN+SQLITE_STMTSTATUS_FULLSCAN_STEP, 0)); - break; - } + StmtRow *pRow = pCur->pRow; + if( i==STMT_COLUMN_SQL ){ + wx_sqlite3_result_text(ctx, pRow->zSql, -1, SQLITE_TRANSIENT); + }else{ + wx_sqlite3_result_int(ctx, pRow->aCol[i]); } return SQLITE_OK; } @@ -234198,7 +245213,7 @@ static int stmtColumn( */ static int stmtRowid(wx_sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ stmt_cursor *pCur = (stmt_cursor*)cur; - *pRowid = pCur->iRowid; + *pRowid = pCur->pRow->iRowid; return SQLITE_OK; } @@ -234208,7 +245223,7 @@ static int stmtRowid(wx_sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ */ static int stmtEof(wx_sqlite3_vtab_cursor *cur){ stmt_cursor *pCur = (stmt_cursor*)cur; - return pCur->pStmt==0; + return pCur->pRow==0; } /* @@ -234223,9 +245238,57 @@ static int stmtFilter( int argc, wx_sqlite3_value **argv ){ stmt_cursor *pCur = (stmt_cursor *)pVtabCursor; - pCur->pStmt = 0; - pCur->iRowid = 0; - return stmtNext(pVtabCursor); + wx_sqlite3_stmt *p = 0; + wx_sqlite3_int64 iRowid = 1; + StmtRow **ppRow = 0; + + (void)idxNum; + (void)idxStr; + (void)argc; + (void)argv; + stmtCsrReset(pCur); + ppRow = &pCur->pRow; + for(p=wx_sqlite3_next_stmt(pCur->db, 0); p; p=wx_sqlite3_next_stmt(pCur->db, p)){ + const char *zSql = wx_sqlite3_sql(p); + wx_sqlite3_int64 nSql = zSql ? strlen(zSql)+1 : 0; + StmtRow *pNew = (StmtRow*)wx_sqlite3_malloc64(sizeof(StmtRow) + nSql); + + if( pNew==0 ) return SQLITE_NOMEM; + memset(pNew, 0, sizeof(StmtRow)); + if( zSql ){ + pNew->zSql = (char*)&pNew[1]; + memcpy(pNew->zSql, zSql, nSql); + } + pNew->aCol[STMT_COLUMN_NCOL] = wx_sqlite3_column_count(p); + pNew->aCol[STMT_COLUMN_RO] = wx_sqlite3_stmt_readonly(p); + pNew->aCol[STMT_COLUMN_BUSY] = wx_sqlite3_stmt_busy(p); + pNew->aCol[STMT_COLUMN_NSCAN] = wx_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_FULLSCAN_STEP, 0 + ); + pNew->aCol[STMT_COLUMN_NSORT] = wx_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_SORT, 0 + ); + pNew->aCol[STMT_COLUMN_NAIDX] = wx_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_AUTOINDEX, 0 + ); + pNew->aCol[STMT_COLUMN_NSTEP] = wx_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_VM_STEP, 0 + ); + pNew->aCol[STMT_COLUMN_REPREP] = wx_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_REPREPARE, 0 + ); + pNew->aCol[STMT_COLUMN_RUN] = wx_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_RUN, 0 + ); + pNew->aCol[STMT_COLUMN_MEM] = wx_sqlite3_stmt_status( + p, SQLITE_STMTSTATUS_MEMUSED, 0 + ); + pNew->iRowid = iRowid++; + *ppRow = pNew; + ppRow = &pNew->pNext; + } + + return SQLITE_OK; } /* @@ -234238,6 +245301,7 @@ static int stmtBestIndex( wx_sqlite3_vtab *tab, wx_sqlite3_index_info *pIdxInfo ){ + (void)tab; pIdxInfo->estimatedCost = (double)500; pIdxInfo->estimatedRows = 500; return SQLITE_OK; @@ -234304,10 +245368,6 @@ SQLITE_API int wx_sqlite3_stmt_init( #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ /************** End of stmt.c ************************************************/ -#if __LINE__!=234223 -#undef SQLITE_SOURCE_ID -#define SQLITE_SOURCE_ID "2021-04-02 15:20:15 5d4c65779dab868b285519b19e4cf9d451d50c6048f06f653aa701ec212dalt2" -#endif /* Return the source-id for this library */ SQLITE_API const char *wx_sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } /************************** End of wx_sqlite3.c ******************************/ @@ -234317,6 +245377,171 @@ SQLITE_API const char *wx_sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } /* ** Include SQLite3MultiCipher components */ +/* #include "wx_sqlite3mc_config.h" */ +/*** Begin of #include "wx_sqlite3mc_config.h" ***/ +/* +** Name: wx_sqlite3mc_config.h +** Purpose: Header file for SQLite3 Multiple Ciphers compile-time configuration +** Author: Ulrich Telle +** Created: 2021-09-27 +** Copyright: (c) 2019-2022 Ulrich Telle +** License: MIT +*/ + +#ifndef SQLITE3MC_CONFIG_H_ +#define SQLITE3MC_CONFIG_H_ + +/* +** Definitions of supported ciphers +*/ + +/* +** Compatibility with wxSQLite3 +*/ +#ifdef WXSQLITE3_HAVE_CIPHER_AES_128_CBC +#define HAVE_CIPHER_AES_128_CBC WXSQLITE3_HAVE_CIPHER_AES_128_CBC +#endif + +#ifdef WXSQLITE3_HAVE_CIPHER_AES_256_CBC +#define HAVE_CIPHER_AES_256_CBC WXSQLITE3_HAVE_CIPHER_AES_256_CBC +#endif + +#ifdef WXSQLITE3_HAVE_CIPHER_CHACHA20 +#define HAVE_CIPHER_CHACHA20 WXSQLITE3_HAVE_CIPHER_CHACHA20 +#endif + +#ifdef WXSQLITE3_HAVE_CIPHER_SQLCIPHER +#define HAVE_CIPHER_SQLCIPHER WXSQLITE3_HAVE_CIPHER_SQLCIPHER +#endif + +#ifdef WXSQLITE3_HAVE_CIPHER_RC4 +#define HAVE_CIPHER_RC4 WXSQLITE3_HAVE_CIPHER_RC4 +#endif + +/* +** Actual definitions of supported ciphers +*/ +#ifndef HAVE_CIPHER_AES_128_CBC +#define HAVE_CIPHER_AES_128_CBC 1 +#endif + +#ifndef HAVE_CIPHER_AES_256_CBC +#define HAVE_CIPHER_AES_256_CBC 1 +#endif + +#ifndef HAVE_CIPHER_CHACHA20 +#define HAVE_CIPHER_CHACHA20 1 +#endif + +#ifndef HAVE_CIPHER_SQLCIPHER +#define HAVE_CIPHER_SQLCIPHER 1 +#endif + +#ifndef HAVE_CIPHER_RC4 +#define HAVE_CIPHER_RC4 1 +#endif + +/* +** Disable all built-in ciphers on request +*/ + +#if 0 +#define SQLITE3MC_OMIT_BUILTIN_CIPHERS +#endif + +#ifdef SQLITE3MC_OMIT_BUILTIN_CIPHERS +#undef HAVE_CIPHER_AES_128_CBC +#undef HAVE_CIPHER_AES_256_CBC +#undef HAVE_CIPHER_CHACHA20 +#undef HAVE_CIPHER_SQLCIPHER +#undef HAVE_CIPHER_RC4 +#define HAVE_CIPHER_AES_128_CBC 0 +#define HAVE_CIPHER_AES_256_CBC 0 +#define HAVE_CIPHER_CHACHA20 0 +#define HAVE_CIPHER_SQLCIPHER 0 +#define HAVE_CIPHER_RC4 0 +#endif + +/* +** Check that at least one cipher is be supported +*/ +#if HAVE_CIPHER_AES_128_CBC == 0 && \ + HAVE_CIPHER_AES_256_CBC == 0 && \ + HAVE_CIPHER_CHACHA20 == 0 && \ + HAVE_CIPHER_SQLCIPHER == 0 && \ + HAVE_CIPHER_RC4 == 0 +#pragma message ("wx_sqlite3mc_config.h: WARNING - No built-in cipher scheme enabled!") +#endif + +/* +** Compile-time configuration +*/ + +/* +** Selection of default cipher scheme +** +** A specific default cipher scheme can be selected by defining +** the symbol CODEC_TYPE using one of the cipher scheme values +** CODEC_TYPE_AES128, CODEC_TYPE_AES256, CODEC_TYPE_CHACHA20, +** CODEC_TYPE_SQLCIPHER, or CODEC_TYPE_RC4. +** +** If the symbol CODEC_TYPE is not defined, CODEC_TYPE_CHACHA20 +** is selected as the default cipher scheme. +*/ +#if 0 +#define CODEC_TYPE CODEC_TYPE_CHACHA20 +#endif + +/* +** Selection of legacy mode +** +** A) CODEC_TYPE_AES128 and CODEC_TYPE_AES256 +** Defining the symbol WXSQLITE3_USE_OLD_ENCRYPTION_SCHEME +** selects the legacy mode for both cipher schemes. +** +** B) CODEC_TYPE_CHACHA20 +** Defining the symbol SQLITE3MC_USE_SQLEET_LEGACY +** selects the legacy mode. +** +** C) CODEC_TYPE_SQLCIPHER +** Defining the symbol SQLITE3MC_USE_SQLEET_LEGACY +** selects the legacy mode. +** +** D) CODEC_TYPE_RC4 +** This cipher scheme is available in legacy mode only. +*/ + +#if 0 +#define WXSQLITE3_USE_OLD_ENCRYPTION_SCHEME +#endif + +#if 0 +#define SQLITE3MC_USE_SQLEET_LEGACY +#endif + +#if 0 +#define SQLITE3MC_USE_SQLCIPHER_LEGACY +#endif + +/* +** Selection of default version for SQLCipher scheme +** +** A specific default version can be selected by defining +** the symbol SQLCIPHER_VERSION_DEFAULT using one of the +** supported version values (SQLCIPHER_VERSION_1, +** SQLCIPHER_VERSION_2, SQLCIPHER_VERSION_3, SQLCIPHER_VERSION_4). +** +** If the symbol SQLCIPHER_VERSION_DEFAULT is not defined, +** version 4 (SQLCIPHER_VERSION_4) is selected as the default value. +*/ + +#if 0 +#define SQLCIPHER_VERSION_DEFAULT SQLCIPHER_VERSION_4 +#endif + +#endif +/*** End of #include "wx_sqlite3mc_config.h" ***/ + /* #include "wx_sqlite3mc.h" */ /*** Begin of #include "wx_sqlite3mc.h" ***/ /* @@ -234324,13 +245549,45 @@ SQLITE_API const char *wx_sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } ** Purpose: Header file for SQLite3 Multiple Ciphers support ** Author: Ulrich Telle ** Created: 2020-03-01 -** Copyright: (c) 2019-2020 Ulrich Telle +** Copyright: (c) 2019-2022 Ulrich Telle ** License: MIT */ #ifndef SQLITE3MC_H_ #define SQLITE3MC_H_ +/* +** Define SQLite3 Multiple Ciphers version information +*/ +/* #include "wx_sqlite3mc_version.h" */ +/*** Begin of #include "wx_sqlite3mc_version.h" ***/ +/* +** Name: wx_sqlite3mc_version.h +** Purpose: SQLite3 Multiple Ciphers version numbers +** Author: Ulrich Telle +** Created: 2020-08-05 +** Copyright: (c) 2020-2023 Ulrich Telle +** License: MIT +*/ + +/// \file wx_sqlite3mc_version.h Version information for the SQLite3 Multiple Ciphers library + +#ifndef SQLITE3MC_VERSION_H_ +#define SQLITE3MC_VERSION_H_ + +#define SQLITE3MC_VERSION_MAJOR 1 +#define SQLITE3MC_VERSION_MINOR 6 +#define SQLITE3MC_VERSION_RELEASE 2 +#define SQLITE3MC_VERSION_SUBRELEASE 0 +#define SQLITE3MC_VERSION_STRING "SQLite3 Multiple Ciphers 1.6.2" + +#endif /* SQLITE3MC_VERSION_H_ */ +/*** End of #include "wx_sqlite3mc_version.h" ***/ + + +/* +** Define SQLite3 API +*/ /* #include "wx_sqlite3.h" */ /*** Begin of #include "wx_sqlite3.h" ***/ /* @@ -234378,7 +245635,30 @@ extern "C" { /* -** Provide the ability to override linkage features of the interface. +** Facilitate override of interface linkage and calling conventions. +** Be aware that these macros may not be used within this particular +** translation of the amalgamation and its associated header file. +** +** The SQLITE_EXTERN and SQLITE_API macros are used to instruct the +** compiler that the target identifier should have external linkage. +** +** The SQLITE_CDECL macro is used to set the calling convention for +** public functions that accept a variable number of arguments. +** +** The SQLITE_APICALL macro is used to set the calling convention for +** public functions that accept a fixed number of arguments. +** +** The SQLITE_STDCALL macro is no longer used and is now deprecated. +** +** The SQLITE_CALLBACK macro is used to set the calling convention for +** function pointers. +** +** The SQLITE_SYSAPI macro is used to set the calling convention for +** functions provided by the operating system. +** +** Currently, the SQLITE_CDECL, SQLITE_APICALL, SQLITE_CALLBACK, and +** SQLITE_SYSAPI macros are used only when building for environments +** that require non-default calling conventions. */ #ifndef SQLITE_EXTERN # define SQLITE_EXTERN extern @@ -234458,9 +245738,9 @@ extern "C" { ** [wx_sqlite3_libversion_number()], [wx_sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.35.4" -#define SQLITE_VERSION_NUMBER 3035004 -#define SQLITE_SOURCE_ID "2021-04-02 15:20:15 5d4c65779dab868b285519b19e4cf9d451d50c6048f06f653aa701ec212df45e" +#define SQLITE_VERSION "3.41.2" +#define SQLITE_VERSION_NUMBER 3041002 +#define SQLITE_SOURCE_ID "2023-03-22 11:56:21 0d1fc92f94cb6b76bffe3ec34d69cffde2924203304e8ffc4155597af0c191da" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -234872,12 +246152,14 @@ SQLITE_API int wx_sqlite3_exec( #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) #define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8)) +#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) +#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) -#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) +#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ /* ** CAPI3REF: Flags For File Open Operations @@ -234885,6 +246167,19 @@ SQLITE_API int wx_sqlite3_exec( ** These bit values are intended for use in the ** 3rd parameter to the [wx_sqlite3_open_v2()] interface and ** in the 4th parameter to the [wx_sqlite3_vfs.xOpen] method. +** +** Only those flags marked as "Ok for wx_sqlite3_open_v2()" may be +** used as the third argument to the [wx_sqlite3_open_v2()] interface. +** The other flags have historically been ignored by wx_sqlite3_open_v2(), +** though future versions of SQLite might change so that an error is +** raised if any of the disallowed bits are passed into wx_sqlite3_open_v2(). +** Applications should not depend on the historical behavior. +** +** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into +** [wx_sqlite3_open_v2()] does *not* cause the underlying database file +** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into +** [wx_sqlite3_open_v2()] has historically be a no-op and might become an +** error in future versions of SQLite. */ #define SQLITE_OPEN_READONLY 0x00000001 /* Ok for wx_sqlite3_open_v2() */ #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for wx_sqlite3_open_v2() */ @@ -234907,6 +246202,7 @@ SQLITE_API int wx_sqlite3_exec( #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for wx_sqlite3_open_v2() */ #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ #define SQLITE_OPEN_NOFOLLOW 0x01000000 /* Ok for wx_sqlite3_open_v2() */ +#define SQLITE_OPEN_EXRESCODE 0x02000000 /* Extended result codes */ /* Reserved: 0x00F00000 */ /* Legacy compatibility: */ @@ -234967,13 +246263,17 @@ SQLITE_API int wx_sqlite3_exec( ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods -** of an [wx_sqlite3_io_methods] object. +** of an [wx_sqlite3_io_methods] object. These values are ordered from +** lest restrictive to most restrictive. +** +** The argument to xLock() is always SHARED or higher. The argument to +** xUnlock is either SHARED or NONE. */ -#define SQLITE_LOCK_NONE 0 -#define SQLITE_LOCK_SHARED 1 -#define SQLITE_LOCK_RESERVED 2 -#define SQLITE_LOCK_PENDING 3 -#define SQLITE_LOCK_EXCLUSIVE 4 +#define SQLITE_LOCK_NONE 0 /* xUnlock() only */ +#define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */ +#define SQLITE_LOCK_RESERVED 2 /* xLock() only */ +#define SQLITE_LOCK_PENDING 3 /* xLock() only */ +#define SQLITE_LOCK_EXCLUSIVE 4 /* xLock() only */ /* ** CAPI3REF: Synchronization Type Flags @@ -235051,7 +246351,14 @@ struct wx_sqlite3_file { **
    4. [SQLITE_LOCK_PENDING], or **
    5. [SQLITE_LOCK_EXCLUSIVE]. ** -** xLock() increases the lock. xUnlock() decreases the lock. +** xLock() upgrades the database file lock. In other words, xLock() moves the +** database file lock in the direction NONE toward EXCLUSIVE. The argument to +** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never +** SQLITE_LOCK_NONE. If the database file lock is already at or above the +** requested lock, then the call to xLock() is a no-op. +** xUnlock() downgrades the database file lock to either SHARED or NONE. +* If the lock is already at or below the requested lock state, then the call +** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, ** PENDING, or EXCLUSIVE lock on the file. It returns true @@ -235156,9 +246463,8 @@ struct wx_sqlite3_io_methods { ** opcode causes the xFileControl method to write the current state of ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) -** into an integer that the pArg argument points to. This capability -** is used during testing and is only available when the SQLITE_TEST -** compile-time option is used. +** into an integer that the pArg argument points to. +** This capability is only available if SQLite is compiled with [SQLITE_DEBUG]. ** **
    6. [[SQLITE_FCNTL_SIZE_HINT]] ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS @@ -235462,6 +246768,28 @@ struct wx_sqlite3_io_methods { ** in wal mode after the client has finished copying pages from the wal ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. +** +**
    7. [[SQLITE_FCNTL_EXTERNAL_READER]] +** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect +** whether or not there is a database client in another process with a wal-mode +** transaction open on the database or not. It is only available on unix.The +** (void*) argument passed with this file-control should be a pointer to a +** value of type (int). The integer value is set to 1 if the database is a wal +** mode database and there exists at least one client in another process that +** currently has an SQL transaction open on the database. It is set to 0 if +** the database is not a wal-mode db, or if there is no such connection in any +** other process. This opcode cannot be used to detect transactions opened +** by clients within the current process, only within other processes. +** +**
    8. [[SQLITE_FCNTL_CKSM_FILE]] +** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use interally by the +** [checksum VFS shim] only. +** +**
    9. [[SQLITE_FCNTL_RESET_CACHE]] +** If there is currently no transaction open on the database, and the +** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control +** purges the contents of the in-memory page cache. If there is an open +** transaction, or if the db is a temp-db, this opcode is a no-op, not an error. ** */ #define SQLITE_FCNTL_LOCKSTATE 1 @@ -235502,6 +246830,9 @@ struct wx_sqlite3_io_methods { #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 +#define SQLITE_FCNTL_EXTERNAL_READER 40 +#define SQLITE_FCNTL_CKSM_FILE 41 +#define SQLITE_FCNTL_RESET_CACHE 42 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -235531,6 +246862,26 @@ typedef struct wx_sqlite3_mutex wx_sqlite3_mutex; */ typedef struct wx_sqlite3_api_routines wx_sqlite3_api_routines; +/* +** CAPI3REF: File Name +** +** Type [wx_sqlite3_filename] is used by SQLite to pass filenames to the +** xOpen method of a [VFS]. It may be cast to (const char*) and treated +** as a normal, nul-terminated, UTF-8 buffer containing the filename, but +** may also be passed to special APIs such as: +** +**
        +**
      • wx_sqlite3_filename_database() +**
      • wx_sqlite3_filename_journal() +**
      • wx_sqlite3_filename_wal() +**
      • wx_sqlite3_uri_parameter() +**
      • wx_sqlite3_uri_boolean() +**
      • wx_sqlite3_uri_int64() +**
      • wx_sqlite3_uri_key() +**
      +*/ +typedef const char *wx_sqlite3_filename; + /* ** CAPI3REF: OS Interface Object ** @@ -235709,7 +247060,7 @@ struct wx_sqlite3_vfs { wx_sqlite3_vfs *pNext; /* Next registered VFS */ const char *zName; /* Name of this virtual file system */ void *pAppData; /* Pointer to application-specific data */ - int (*xOpen)(wx_sqlite3_vfs*, const char *zName, wx_sqlite3_file*, + int (*xOpen)(wx_sqlite3_vfs*, wx_sqlite3_filename zName, wx_sqlite3_file*, int flags, int *pOutFlags); int (*xDelete)(wx_sqlite3_vfs*, const char *zName, int syncDir); int (*xAccess)(wx_sqlite3_vfs*, const char *zName, int flags, int *pResOut); @@ -236425,7 +247776,7 @@ struct wx_sqlite3_mem_methods { ** configuration for a database connection can only be changed when that ** connection is not currently using lookaside memory, or in other words ** when the "current value" returned by -** [wx_sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. +** [wx_sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero. ** Any attempt to change the lookaside memory configuration when lookaside ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^ @@ -236575,8 +247926,12 @@ struct wx_sqlite3_mem_methods { **
    10. wx_sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); ** ** Because resetting a database is destructive and irreversible, the -** process requires the use of this obscure API and multiple steps to help -** ensure that it does not happen by accident. +** process requires the use of this obscure API and multiple steps to +** help ensure that it does not happen by accident. Because this +** feature must be capable of resetting corrupt databases, and +** shutting down virtual tables may require access to that corrupt +** storage, the library must abandon any installed virtual tables +** without calling their xDestroy() methods. ** ** [[SQLITE_DBCONFIG_DEFENSIVE]]
      SQLITE_DBCONFIG_DEFENSIVE
      **
      The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the @@ -236587,6 +247942,7 @@ struct wx_sqlite3_mem_methods { **
        **
      • The [PRAGMA writable_schema=ON] statement. **
      • The [PRAGMA journal_mode=OFF] statement. +**
      • The [PRAGMA schema_version=N] statement. **
      • Writes to the [sqlite_dbpage] virtual table. **
      • Direct writes to [shadow tables]. **
      @@ -236780,11 +248136,14 @@ SQLITE_API void wx_sqlite3_set_last_insert_rowid(wx_sqlite3*,wx_sqlite3_int64); ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: wx_sqlite3 ** -** ^This function returns the number of rows modified, inserted or +** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. -** ^Executing any other type of SQL statement does not modify the value -** returned by this function. +** The two functions are identical except for the type of the return value +** and that if the number of rows modified by the most recent INSERT, UPDATE +** or DELETE is greater than the maximum value supported by type "int", then +** the return value of wx_sqlite3_changes() is undefined. ^Executing any other +** type of SQL statement does not modify the value returned by these functions. ** ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], @@ -236833,16 +248192,21 @@ SQLITE_API void wx_sqlite3_set_last_insert_rowid(wx_sqlite3*,wx_sqlite3_int64); ** */ SQLITE_API int wx_sqlite3_changes(wx_sqlite3*); +SQLITE_API wx_sqlite3_int64 wx_sqlite3_changes64(wx_sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified ** METHOD: wx_sqlite3 ** -** ^This function returns the total number of rows inserted, modified or +** ^These functions return the total number of rows inserted, modified or ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed ** since the database connection was opened, including those executed as -** part of trigger programs. ^Executing any other type of SQL statement -** does not affect the value returned by wx_sqlite3_total_changes(). +** part of trigger programs. The two functions are identical except for the +** type of the return value and that if the number of rows modified by the +** connection exceeds the maximum value supported by type "int", then +** the return value of wx_sqlite3_total_changes() is undefined. ^Executing +** any other type of SQL statement does not affect the value returned by +** wx_sqlite3_total_changes(). ** ** ^Changes made as part of [foreign key actions] are included in the ** count, but those made as part of REPLACE constraint resolution are @@ -236870,6 +248234,7 @@ SQLITE_API int wx_sqlite3_changes(wx_sqlite3*); ** */ SQLITE_API int wx_sqlite3_total_changes(wx_sqlite3*); +SQLITE_API wx_sqlite3_int64 wx_sqlite3_total_changes64(wx_sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query @@ -236905,8 +248270,12 @@ SQLITE_API int wx_sqlite3_total_changes(wx_sqlite3*); ** ^A call to wx_sqlite3_interrupt(D) that occurs when there are no running ** SQL statements is a no-op and has no effect on SQL statements ** that are started after the wx_sqlite3_interrupt() call returns. +** +** ^The [wx_sqlite3_is_interrupted(D)] interface can be used to determine whether +** or not an interrupt is currently in effect for [database connection] D. */ SQLITE_API void wx_sqlite3_interrupt(wx_sqlite3*); +SQLITE_API int wx_sqlite3_is_interrupted(wx_sqlite3*); /* ** CAPI3REF: Determine If An SQL Statement Is Complete @@ -237524,8 +248893,8 @@ SQLITE_API SQLITE_DEPRECATED void *wx_sqlite3_profile(wx_sqlite3*, **
      ^An SQLITE_TRACE_PROFILE callback provides approximately the same ** information as is provided by the [wx_sqlite3_profile()] callback. ** ^The P argument is a pointer to the [prepared statement] and the -** X argument points to a 64-bit integer which is the estimated of -** the number of nanosecond that the prepared statement took to run. +** X argument points to a 64-bit integer which is approximately +** the number of nanoseconds that the prepared statement took to run. ** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes. ** ** [[SQLITE_TRACE_ROW]]
      SQLITE_TRACE_ROW
      @@ -237588,7 +248957,7 @@ SQLITE_API int wx_sqlite3_trace_v2( ** ** ^The wx_sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to -** [wx_sqlite3_exec()], [wx_sqlite3_step()] and [wx_sqlite3_get_table()] for +** [wx_sqlite3_step()] and [wx_sqlite3_prepare()] and similar for ** database connection D. An example use for this ** interface is to keep a GUI updated during a large query. ** @@ -237613,6 +248982,13 @@ SQLITE_API int wx_sqlite3_trace_v2( ** Note that [wx_sqlite3_prepare_v2()] and [wx_sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** +** The progress handler callback would originally only be invoked from the +** bytecode engine. It still might be invoked during [wx_sqlite3_prepare()] +** and similar because those routines might force a reparse of the schema +** which involves running the bytecode engine. However, beginning with +** SQLite version 3.41.0, the progress handler callback might also be +** invoked directly from [wx_sqlite3_prepare()] while analyzing and generating +** code for complex queries. */ SQLITE_API void wx_sqlite3_progress_handler(wx_sqlite3*, int, int(*)(void*), void*); @@ -237649,13 +249025,18 @@ SQLITE_API void wx_sqlite3_progress_handler(wx_sqlite3*, int, int(*)(void*), voi ** **
      ** ^(
      [SQLITE_OPEN_READONLY]
      -**
      The database is opened in read-only mode. If the database does not -** already exist, an error is returned.
      )^ +**
      The database is opened in read-only mode. If the database does +** not already exist, an error is returned.
      )^ ** ** ^(
      [SQLITE_OPEN_READWRITE]
      -**
      The database is opened for reading and writing if possible, or reading -** only if the file is write protected by the operating system. In either -** case the database must already exist, otherwise an error is returned.
      )^ +**
      The database is opened for reading and writing if possible, or +** reading only if the file is write protected by the operating +** system. In either case the database must already exist, otherwise +** an error is returned. For historical reasons, if opening in +** read-write mode fails due to OS-level permissions, an attempt is +** made to open it in read-only mode. [wx_sqlite3_db_readonly()] can be +** used to determine whether the database is actually +** read-write.
      )^ ** ** ^(
      [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
      **
      The database is opened for reading and writing, and is created if @@ -237693,20 +249074,39 @@ SQLITE_API void wx_sqlite3_progress_handler(wx_sqlite3*, int, int(*)(void*), voi **
      The database is opened [shared cache] enabled, overriding ** the default shared cache setting provided by ** [wx_sqlite3_enable_shared_cache()].)^ +** The [use of shared cache mode is discouraged] and hence shared cache +** capabilities may be omitted from many builds of SQLite. In such cases, +** this option is a no-op. ** ** ^(
      [SQLITE_OPEN_PRIVATECACHE]
      **
      The database is opened [shared cache] disabled, overriding ** the default shared cache setting provided by ** [wx_sqlite3_enable_shared_cache()].)^ ** +** [[OPEN_EXRESCODE]] ^(
      [SQLITE_OPEN_EXRESCODE]
      +**
      The database connection comes up in "extended result code mode". +** In other words, the database behaves has if +** [wx_sqlite3_extended_result_codes(db,1)] where called on the database +** connection as soon as the connection is created. In addition to setting +** the extended result code mode, this flag also causes [wx_sqlite3_open_v2()] +** to return an extended result code.
      +** ** [[OPEN_NOFOLLOW]] ^(
      [SQLITE_OPEN_NOFOLLOW]
      -**
      The database filename is not allowed to be a symbolic link
      +**
      The database filename is not allowed to contain a symbolic link
      **
      )^ ** ** If the 3rd parameter to wx_sqlite3_open_v2() is not one of the ** required combinations shown above optionally combined with other ** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] -** then the behavior is undefined. +** then the behavior is undefined. Historic versions of SQLite +** have silently ignored surplus bits in the flags parameter to +** wx_sqlite3_open_v2(), however that behavior might not be carried through +** into future versions of SQLite and so applications should not rely +** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op +** for wx_sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause +** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE +** flag is intended for use by the [wx_sqlite3_vfs|VFS interface] only, and not +** by wx_sqlite3_open_v2(). ** ** ^The fourth parameter to wx_sqlite3_open_v2() is the name of the ** [wx_sqlite3_vfs] object that defines the operating system interface that @@ -237951,10 +249351,10 @@ SQLITE_API int wx_sqlite3_open_v2( ** ** See the [URI filename] documentation for additional information. */ -SQLITE_API const char *wx_sqlite3_uri_parameter(const char *zFilename, const char *zParam); -SQLITE_API int wx_sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); -SQLITE_API wx_sqlite3_int64 wx_sqlite3_uri_int64(const char*, const char*, wx_sqlite3_int64); -SQLITE_API const char *wx_sqlite3_uri_key(const char *zFilename, int N); +SQLITE_API const char *wx_sqlite3_uri_parameter(wx_sqlite3_filename z, const char *zParam); +SQLITE_API int wx_sqlite3_uri_boolean(wx_sqlite3_filename z, const char *zParam, int bDefault); +SQLITE_API wx_sqlite3_int64 wx_sqlite3_uri_int64(wx_sqlite3_filename, const char*, wx_sqlite3_int64); +SQLITE_API const char *wx_sqlite3_uri_key(wx_sqlite3_filename z, int N); /* ** CAPI3REF: Translate filenames @@ -237983,9 +249383,9 @@ SQLITE_API const char *wx_sqlite3_uri_key(const char *zFilename, int N); ** return value from [wx_sqlite3_db_filename()], then the result is ** undefined and is likely a memory access violation. */ -SQLITE_API const char *wx_sqlite3_filename_database(const char*); -SQLITE_API const char *wx_sqlite3_filename_journal(const char*); -SQLITE_API const char *wx_sqlite3_filename_wal(const char*); +SQLITE_API const char *wx_sqlite3_filename_database(wx_sqlite3_filename); +SQLITE_API const char *wx_sqlite3_filename_journal(wx_sqlite3_filename); +SQLITE_API const char *wx_sqlite3_filename_wal(wx_sqlite3_filename); /* ** CAPI3REF: Database File Corresponding To A Journal @@ -238051,14 +249451,14 @@ SQLITE_API wx_sqlite3_file *wx_sqlite3_database_file_object(const char*); ** then the corresponding [wx_sqlite3_module.xClose() method should also be ** invoked prior to calling wx_sqlite3_free_filename(Y). */ -SQLITE_API char *wx_sqlite3_create_filename( +SQLITE_API wx_sqlite3_filename wx_sqlite3_create_filename( const char *zDatabase, const char *zJournal, const char *zWal, int nParam, const char **azParam ); -SQLITE_API void wx_sqlite3_free_filename(char*); +SQLITE_API void wx_sqlite3_free_filename(wx_sqlite3_filename); /* ** CAPI3REF: Error Codes And Messages @@ -238077,13 +249477,14 @@ SQLITE_API void wx_sqlite3_free_filename(char*); ** wx_sqlite3_extended_errcode() might change with each API call. ** Except, there are some interfaces that are guaranteed to never ** change the value of the error code. The error-code preserving -** interfaces are: +** interfaces include the following: ** **
        **
      • wx_sqlite3_errcode() **
      • wx_sqlite3_extended_errcode() **
      • wx_sqlite3_errmsg() **
      • wx_sqlite3_errmsg16() +**
      • wx_sqlite3_error_offset() **
      ** ** ^The wx_sqlite3_errmsg() and wx_sqlite3_errmsg16() return English-language @@ -238098,6 +249499,13 @@ SQLITE_API void wx_sqlite3_free_filename(char*); ** ^(Memory to hold the error message string is managed internally ** and must not be freed by the application)^. ** +** ^If the most recent error references a specific token in the input +** SQL, the wx_sqlite3_error_offset() interface returns the byte offset +** of the start of that token. ^The byte offset returned by +** wx_sqlite3_error_offset() assumes that the input SQL is UTF8. +** ^If the most recent error does not reference a specific token in the input +** SQL, then the wx_sqlite3_error_offset() function returns -1. +** ** When the serialized [threading mode] is in use, it might be the ** case that a second error occurs on a separate thread in between ** the time of the first error and the call to these interfaces. @@ -238117,6 +249525,7 @@ SQLITE_API int wx_sqlite3_extended_errcode(wx_sqlite3 *db); SQLITE_API const char *wx_sqlite3_errmsg(wx_sqlite3*); SQLITE_API const void *wx_sqlite3_errmsg16(wx_sqlite3*); SQLITE_API const char *wx_sqlite3_errstr(int); +SQLITE_API int wx_sqlite3_error_offset(wx_sqlite3 *db); /* ** CAPI3REF: Prepared Statement Object @@ -238474,12 +249883,17 @@ SQLITE_API int wx_sqlite3_prepare16_v3( ** are managed by SQLite and are automatically freed when the prepared ** statement is finalized. ** ^The string returned by wx_sqlite3_expanded_sql(P), on the other hand, -** is obtained from [wx_sqlite3_malloc()] and must be free by the application +** is obtained from [wx_sqlite3_malloc()] and must be freed by the application ** by passing it to [wx_sqlite3_free()]. +** +** ^The wx_sqlite3_normalized_sql() interface is only available if +** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined. */ SQLITE_API const char *wx_sqlite3_sql(wx_sqlite3_stmt *pStmt); SQLITE_API char *wx_sqlite3_expanded_sql(wx_sqlite3_stmt *pStmt); +#ifdef SQLITE_ENABLE_NORMALIZE SQLITE_API const char *wx_sqlite3_normalized_sql(wx_sqlite3_stmt *pStmt); +#endif /* ** CAPI3REF: Determine If An SQL Statement Writes The Database @@ -238514,6 +249928,19 @@ SQLITE_API const char *wx_sqlite3_normalized_sql(wx_sqlite3_stmt *pStmt); ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so ** wx_sqlite3_stmt_readonly() returns false for those commands. +** +** ^This routine returns false if there is any possibility that the +** statement might change the database file. ^A false return does +** not guarantee that the statement will change the database file. +** ^For example, an UPDATE statement might have a WHERE clause that +** makes it a no-op, but the wx_sqlite3_stmt_readonly() result would still +** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a +** read-only no-op if the table already exists, but +** wx_sqlite3_stmt_readonly() still returns false for such a statement. +** +** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN] +** statement, then wx_sqlite3_stmt_readonly(X) returns the same value as +** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted. */ SQLITE_API int wx_sqlite3_stmt_readonly(wx_sqlite3_stmt *pStmt); @@ -238582,6 +250009,8 @@ SQLITE_API int wx_sqlite3_stmt_busy(wx_sqlite3_stmt*); ** ** ^The wx_sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. +** ^The wx_sqlite3_value objects returned by [wx_sqlite3_vtab_rhs_value()] +** are protected. ** ^The wx_sqlite3_value object returned by ** [wx_sqlite3_column_value()] is unprotected. ** Unprotected wx_sqlite3_value objects may only be used as arguments @@ -238683,18 +250112,22 @@ typedef struct wx_sqlite3_context wx_sqlite3_context; ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. ** -** ^The fifth argument to the BLOB and string binding interfaces -** is a destructor used to dispose of the BLOB or -** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to the bind API fails, -** except the destructor is not called if the third parameter is a NULL -** pointer or the fourth parameter is negative. -** ^If the fifth argument is -** the special value [SQLITE_STATIC], then SQLite assumes that the -** information is in static, unmanaged space and does not need to be freed. -** ^If the fifth argument has the value [SQLITE_TRANSIENT], then -** SQLite makes its own private copy of the data immediately, before -** the wx_sqlite3_bind_*() routine returns. +** ^The fifth argument to the BLOB and string binding interfaces controls +** or indicates the lifetime of the object referenced by the third parameter. +** These three options exist: +** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished +** with it may be passed. ^It is called to dispose of the BLOB or string even +** if the call to the bind API fails, except the destructor is not called if +** the third parameter is a NULL pointer or the fourth parameter is negative. +** ^ (2) The special constant, [SQLITE_STATIC], may be passsed to indicate that +** the application remains responsible for disposing of the object. ^In this +** case, the object and the provided pointer to it must remain valid until +** either the prepared statement is finalized or the same SQL parameter is +** bound to something else, whichever occurs sooner. +** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the +** object is to be copied prior to the return from wx_sqlite3_bind_*(). ^The +** object and pointer to it must remain valid until then. ^SQLite will then +** manage the lifetime of its private copy. ** ** ^The sixth argument to wx_sqlite3_bind_text64() must be one of ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] @@ -239199,6 +250632,10 @@ SQLITE_API int wx_sqlite3_data_count(wx_sqlite3_stmt *pStmt); ** even empty strings, are always zero-terminated. ^The return ** value from wx_sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. ** +** ^Strings returned by wx_sqlite3_column_text16() always have the endianness +** which is native to the platform, regardless of the text encoding set +** for the database. +** ** Warning: ^The object returned by [wx_sqlite3_column_value()] is an ** [unprotected wx_sqlite3_value] object. In a multithreaded environment, ** an unprotected wx_sqlite3_value object may only be used safely with @@ -239212,7 +250649,7 @@ SQLITE_API int wx_sqlite3_data_count(wx_sqlite3_stmt *pStmt); ** [application-defined SQL functions] or [virtual tables], not within ** top-level application code. ** -** The these routines may attempt to convert the datatype of the result. +** These routines may attempt to convert the datatype of the result. ** ^For example, if the internal representation is FLOAT and a text result ** is requested, [wx_sqlite3_snprintf()] is used internally to perform the ** conversion automatically. ^(The following table details the conversions @@ -239237,7 +250674,7 @@ SQLITE_API int wx_sqlite3_data_count(wx_sqlite3_stmt *pStmt); **
    11. TEXT BLOB No change **
      BLOB INTEGER [CAST] to INTEGER **
      BLOB FLOAT [CAST] to REAL -**
      BLOB TEXT Add a zero terminator if needed +**
      BLOB TEXT [CAST] to TEXT, ensure zero terminator **
      ** )^ ** @@ -239436,7 +250873,6 @@ SQLITE_API int wx_sqlite3_reset(wx_sqlite3_stmt *pStmt); ** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions, ** index expressions, or the WHERE clause of partial indexes. ** -** ** For best security, the [SQLITE_DIRECTONLY] flag is recommended for ** all application-defined SQL functions that do not need to be ** used inside of triggers, view, CHECK constraints, or other elements of @@ -239446,7 +250882,6 @@ SQLITE_API int wx_sqlite3_reset(wx_sqlite3_stmt *pStmt); ** a database file to include invocations of the function with parameters ** chosen by the attacker, which the application will then execute when ** the database file is opened and read. -** ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [wx_sqlite3_user_data()].)^ @@ -239582,10 +251017,21 @@ SQLITE_API int wx_sqlite3_create_window_function( ** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in ** schema structures such as [CHECK constraints], [DEFAULT clauses], ** [expression indexes], [partial indexes], or [generated columns]. -** The SQLITE_DIRECTONLY flags is a security feature which is recommended -** for all [application-defined SQL functions], and especially for functions -** that have side-effects or that could potentially leak sensitive -** information. +**

      +** The SQLITE_DIRECTONLY flag is recommended for any +** [application-defined SQL function] +** that has side-effects or that could potentially leak sensitive information. +** This will prevent attacks in which an application is tricked +** into using a database file that has had its schema surreptiously +** modified to invoke the application-defined function in ways that are +** harmful. +**

      +** Some people say it is good practice to set SQLITE_DIRECTONLY on all +** [application-defined SQL functions], regardless of whether or not they +** are security sensitive, as doing so prevents those functions from being used +** inside of the database schema, and thus ensures that the database +** can be inspected and modified using generic tools (such as the [CLI]) +** that do not have access to the application-defined functions. **

    ** ** [[SQLITE_INNOCUOUS]]
    SQLITE_INNOCUOUS
    @@ -239791,6 +251237,28 @@ SQLITE_API int wx_sqlite3_value_numeric_type(wx_sqlite3_value*); SQLITE_API int wx_sqlite3_value_nochange(wx_sqlite3_value*); SQLITE_API int wx_sqlite3_value_frombind(wx_sqlite3_value*); +/* +** CAPI3REF: Report the internal text encoding state of an wx_sqlite3_value object +** METHOD: wx_sqlite3_value +** +** ^(The wx_sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8], +** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding +** of the value X, assuming that X has type TEXT.)^ If wx_sqlite3_value_type(X) +** returns something other than SQLITE_TEXT, then the return value from +** wx_sqlite3_value_encoding(X) is meaningless. ^Calls to +** [wx_sqlite3_value_text(X)], [wx_sqlite3_value_text16(X)], [wx_sqlite3_value_text16be(X)], +** [wx_sqlite3_value_text16le(X)], [wx_sqlite3_value_bytes(X)], or +** [wx_sqlite3_value_bytes16(X)] might change the encoding of the value X and +** thus change the return from subsequent calls to wx_sqlite3_value_encoding(X). +** +** This routine is intended for used by applications that test and validate +** the SQLite implementation. This routine is inquiring about the opaque +** internal state of an [wx_sqlite3_value] object. Ordinary applications should +** not need to know what the internal state of an wx_sqlite3_value object is and +** hence should not need to use this interface. +*/ +SQLITE_API int wx_sqlite3_value_encoding(wx_sqlite3_value*); + /* ** CAPI3REF: Finding The Subtype Of SQL Values ** METHOD: wx_sqlite3_value @@ -239811,7 +251279,8 @@ SQLITE_API unsigned int wx_sqlite3_value_subtype(wx_sqlite3_value*); ** object D and returns a pointer to that copy. ^The [wx_sqlite3_value] returned ** is a [protected wx_sqlite3_value] object even if the input is not. ** ^The wx_sqlite3_value_dup(V) interface returns NULL if V is NULL or if a -** memory allocation fails. +** memory allocation fails. ^If V is a [pointer value], then the result +** of wx_sqlite3_value_dup(V) is a NULL value. ** ** ^The wx_sqlite3_value_free(V) interface frees an [wx_sqlite3_value] object ** previously obtained from [wx_sqlite3_value_dup()]. ^If V is a NULL pointer @@ -239842,7 +251311,7 @@ SQLITE_API void wx_sqlite3_value_free(wx_sqlite3_value*); ** ** ^The wx_sqlite3_aggregate_context(C,N) routine returns a NULL pointer ** when first called if N is less than or equal to zero or if a memory -** allocate error occurs. +** allocation error occurs. ** ** ^(The amount of space allocated by wx_sqlite3_aggregate_context(C,N) is ** determined by the N parameter on first successful call. Changing the @@ -240047,9 +251516,10 @@ typedef void (*wx_sqlite3_destructor_type)(void*); ** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. ** ^SQLite takes the text result from the application from ** the 2nd parameter of the wx_sqlite3_result_text* interfaces. -** ^If the 3rd parameter to the wx_sqlite3_result_text* interfaces -** is negative, then SQLite takes result text from the 2nd parameter -** through the first zero character. +** ^If the 3rd parameter to any of the wx_sqlite3_result_text* interfaces +** other than wx_sqlite3_result_text64() is negative, then SQLite computes +** the string length itself by searching the 2nd parameter for the first +** zero character. ** ^If the 3rd parameter to the wx_sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text ** pointed to by the 2nd parameter are taken as the application-defined @@ -240493,6 +251963,28 @@ SQLITE_API int wx_sqlite3_get_autocommit(wx_sqlite3*); */ SQLITE_API wx_sqlite3 *wx_sqlite3_db_handle(wx_sqlite3_stmt*); +/* +** CAPI3REF: Return The Schema Name For A Database Connection +** METHOD: wx_sqlite3 +** +** ^The wx_sqlite3_db_name(D,N) interface returns a pointer to the schema name +** for the N-th database on database connection D, or a NULL pointer of N is +** out of range. An N value of 0 means the main database file. An N of 1 is +** the "temp" schema. Larger values of N correspond to various ATTACH-ed +** databases. +** +** Space to hold the string that is returned by wx_sqlite3_db_name() is managed +** by SQLite itself. The string might be deallocated by any operation that +** changes the schema, including [ATTACH] or [DETACH] or calls to +** [wx_sqlite3_serialize()] or [wx_sqlite3_deserialize()], even operations that +** occur on a different thread. Applications that need to +** remember the string long-term should make their own copy. Applications that +** are accessing the same database connection simultaneously on multiple +** threads should mutex-protect calls to this API and should make their own +** private copy of the result prior to releasing the mutex. +*/ +SQLITE_API const char *wx_sqlite3_db_name(wx_sqlite3 *db, int N); + /* ** CAPI3REF: Return The Filename For A Database Connection ** METHOD: wx_sqlite3 @@ -240523,7 +252015,7 @@ SQLITE_API wx_sqlite3 *wx_sqlite3_db_handle(wx_sqlite3_stmt*); **
  • [wx_sqlite3_filename_wal()] ** */ -SQLITE_API const char *wx_sqlite3_db_filename(wx_sqlite3 *db, const char *zDbName); +SQLITE_API wx_sqlite3_filename wx_sqlite3_db_filename(wx_sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Determine if a database is read-only @@ -240652,6 +252144,72 @@ SQLITE_API wx_sqlite3_stmt *wx_sqlite3_next_stmt(wx_sqlite3 *pDb, wx_sqlite3_stm SQLITE_API void *wx_sqlite3_commit_hook(wx_sqlite3*, int(*)(void*), void*); SQLITE_API void *wx_sqlite3_rollback_hook(wx_sqlite3*, void(*)(void *), void*); +/* +** CAPI3REF: Autovacuum Compaction Amount Callback +** METHOD: wx_sqlite3 +** +** ^The wx_sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback +** function C that is invoked prior to each autovacuum of the database +** file. ^The callback is passed a copy of the generic data pointer (P), +** the schema-name of the attached database that is being autovacuumed, +** the size of the database file in pages, the number of free pages, +** and the number of bytes per page, respectively. The callback should +** return the number of free pages that should be removed by the +** autovacuum. ^If the callback returns zero, then no autovacuum happens. +** ^If the value returned is greater than or equal to the number of +** free pages, then a complete autovacuum happens. +** +**

    ^If there are multiple ATTACH-ed database files that are being +** modified as part of a transaction commit, then the autovacuum pages +** callback is invoked separately for each file. +** +**

    The callback is not reentrant. The callback function should +** not attempt to invoke any other SQLite interface. If it does, bad +** things may happen, including segmentation faults and corrupt database +** files. The callback function should be a simple function that +** does some arithmetic on its input parameters and returns a result. +** +** ^The X parameter to wx_sqlite3_autovacuum_pages(D,C,P,X) is an optional +** destructor for the P parameter. ^If X is not NULL, then X(P) is +** invoked whenever the database connection closes or when the callback +** is overwritten by another invocation of wx_sqlite3_autovacuum_pages(). +** +**

    ^There is only one autovacuum pages callback per database connection. +** ^Each call to the wx_sqlite3_autovacuum_pages() interface overrides all +** previous invocations for that database connection. ^If the callback +** argument (C) to wx_sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, +** then the autovacuum steps callback is cancelled. The return value +** from wx_sqlite3_autovacuum_pages() is normally SQLITE_OK, but might +** be some other error code if something goes wrong. The current +** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other +** return codes might be added in future releases. +** +**

    If no autovacuum pages callback is specified (the usual case) or +** a NULL pointer is provided for the callback, +** then the default behavior is to vacuum all free pages. So, in other +** words, the default behavior is the same as if the callback function +** were something like this: +** +**

    +**     unsigned int demonstration_autovac_pages_callback(
    +**       void *pClientData,
    +**       const char *zSchema,
    +**       unsigned int nDbPage,
    +**       unsigned int nFreePage,
    +**       unsigned int nBytePerPage
    +**     ){
    +**       return nFreePage;
    +**     }
    +** 
    +*/ +SQLITE_API int wx_sqlite3_autovacuum_pages( + wx_sqlite3 *db, + unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), + void*, + void(*)(void*) +); + + /* ** CAPI3REF: Data Change Notification Callbacks ** METHOD: wx_sqlite3 @@ -240715,6 +252273,11 @@ SQLITE_API void *wx_sqlite3_update_hook( ** to the same database. Sharing is enabled if the argument is true ** and disabled if the argument is false.)^ ** +** This interface is omitted if SQLite is compiled with +** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE] +** compile-time option is recommended because the +** [use of shared cache mode is discouraged]. +** ** ^Cache sharing is enabled and disabled for an entire process. ** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]). ** In prior versions of SQLite, @@ -240813,7 +252376,7 @@ SQLITE_API int wx_sqlite3_db_release_memory(wx_sqlite3*); ** ^The soft heap limit may not be greater than the hard heap limit. ** ^If the hard heap limit is enabled and if wx_sqlite3_soft_heap_limit(N) ** is invoked with a value of N that is greater than the hard heap limit, -** the the soft heap limit is set to the value of the hard heap limit. +** the soft heap limit is set to the value of the hard heap limit. ** ^The soft heap limit is automatically enabled whenever the hard heap ** limit is enabled. ^When wx_sqlite3_hard_heap_limit64(N) is invoked and ** the soft heap limit is outside the range of 1..N, then the soft heap @@ -241074,15 +252637,6 @@ SQLITE_API int wx_sqlite3_cancel_auto_extension(void(*xEntryPoint)(void)); */ SQLITE_API void wx_sqlite3_reset_auto_extension(void); -/* -** The interface to the virtual-table mechanism is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - /* ** Structures used by the virtual table interface */ @@ -241201,10 +252755,10 @@ struct wx_sqlite3_module { ** when the omit flag is true there is no guarantee that the constraint will ** not be checked again using byte code.)^ ** -** ^The idxNum and idxPtr values are recorded and passed into the +** ^The idxNum and idxStr values are recorded and passed into the ** [xFilter] method. -** ^[wx_sqlite3_free()] is used to free idxPtr if and only if -** needToFreeIdxPtr is true. +** ^[wx_sqlite3_free()] is used to free idxStr if and only if +** needToFreeIdxStr is true. ** ** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in ** the correct order to satisfy the ORDER BY clause so that no separate @@ -241293,24 +252847,56 @@ struct wx_sqlite3_index_info { ** ** These macros define the allowed values for the ** [wx_sqlite3_index_info].aConstraint[].op field. Each value represents -** an operator that is part of a constraint term in the wHERE clause of +** an operator that is part of a constraint term in the WHERE clause of ** a query that uses a [virtual table]. -*/ -#define SQLITE_INDEX_CONSTRAINT_EQ 2 -#define SQLITE_INDEX_CONSTRAINT_GT 4 -#define SQLITE_INDEX_CONSTRAINT_LE 8 -#define SQLITE_INDEX_CONSTRAINT_LT 16 -#define SQLITE_INDEX_CONSTRAINT_GE 32 -#define SQLITE_INDEX_CONSTRAINT_MATCH 64 -#define SQLITE_INDEX_CONSTRAINT_LIKE 65 -#define SQLITE_INDEX_CONSTRAINT_GLOB 66 -#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 -#define SQLITE_INDEX_CONSTRAINT_NE 68 -#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 -#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 -#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 -#define SQLITE_INDEX_CONSTRAINT_IS 72 -#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 +** +** ^The left-hand operand of the operator is given by the corresponding +** aConstraint[].iColumn field. ^An iColumn of -1 indicates the left-hand +** operand is the rowid. +** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET +** operators have no left-hand operand, and so for those operators the +** corresponding aConstraint[].iColumn is meaningless and should not be +** used. +** +** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through +** value 255 are reserved to represent functions that are overloaded +** by the [xFindFunction|xFindFunction method] of the virtual table +** implementation. +** +** The right-hand operands for each constraint might be accessible using +** the [wx_sqlite3_vtab_rhs_value()] interface. Usually the right-hand +** operand is only available if it appears as a single constant literal +** in the input SQL. If the right-hand operand is another column or an +** expression (even a constant expression) or a parameter, then the +** wx_sqlite3_vtab_rhs_value() probably will not be able to extract it. +** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and +** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand +** and hence calls to wx_sqlite3_vtab_rhs_value() for those operators will +** always return SQLITE_NOTFOUND. +** +** The collating sequence to be used for comparison can be found using +** the [wx_sqlite3_vtab_collation()] interface. For most real-world virtual +** tables, the collating sequence of constraints does not matter (for example +** because the constraints are numeric) and so the wx_sqlite3_vtab_collation() +** interface is not commonly needed. +*/ +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 +#define SQLITE_INDEX_CONSTRAINT_LIKE 65 +#define SQLITE_INDEX_CONSTRAINT_GLOB 66 +#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 +#define SQLITE_INDEX_CONSTRAINT_NE 68 +#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 +#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 +#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 +#define SQLITE_INDEX_CONSTRAINT_IS 72 +#define SQLITE_INDEX_CONSTRAINT_LIMIT 73 +#define SQLITE_INDEX_CONSTRAINT_OFFSET 74 +#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* ** CAPI3REF: Register A Virtual Table Implementation @@ -241339,7 +252925,7 @@ struct wx_sqlite3_index_info { ** destructor. ** ** ^If the third parameter (the pointer to the wx_sqlite3_module object) is -** NULL then no new module is create and any existing modules with the +** NULL then no new module is created and any existing modules with the ** same name are dropped. ** ** See also: [wx_sqlite3_drop_modules()] @@ -241451,16 +253037,6 @@ SQLITE_API int wx_sqlite3_declare_vtab(wx_sqlite3*, const char *zSQL); */ SQLITE_API int wx_sqlite3_overload_function(wx_sqlite3*, const char *zFuncName, int nArg); -/* -** The interface to the virtual-table mechanism defined above (back up -** to a comment remarkably similar to this one) is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - /* ** CAPI3REF: A Handle To An Open BLOB ** KEYWORDS: {BLOB handle} {BLOB handles} @@ -242114,7 +253690,9 @@ SQLITE_API int wx_sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 #define SQLITE_TESTCTRL_SEEK_COUNT 30 #define SQLITE_TESTCTRL_TRACEFLAGS 31 -#define SQLITE_TESTCTRL_LAST 31 /* Largest TESTCTRL */ +#define SQLITE_TESTCTRL_TUNE 32 +#define SQLITE_TESTCTRL_LOGEST 33 +#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking @@ -242637,6 +254215,16 @@ SQLITE_API int wx_sqlite3_stmt_status(wx_sqlite3_stmt*, int op,int resetFlg); ** The counter is incremented on the first [wx_sqlite3_step()] call of each ** cycle. ** +** [[SQLITE_STMTSTATUS_FILTER_MISS]] +** [[SQLITE_STMTSTATUS_FILTER HIT]] +**
    SQLITE_STMTSTATUS_FILTER_HIT
    +** SQLITE_STMTSTATUS_FILTER_MISS
    +**
    ^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join +** step was bypassed because a Bloom filter returned not-found. The +** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of +** times that the Bloom filter returned a find, and thus the join step +** had to be processed as normal. +** ** [[SQLITE_STMTSTATUS_MEMUSED]]
    SQLITE_STMTSTATUS_MEMUSED
    **
    ^This is the approximate number of bytes of heap memory ** used to store the prepared statement. ^This value is not actually @@ -242651,6 +254239,8 @@ SQLITE_API int wx_sqlite3_stmt_status(wx_sqlite3_stmt*, int op,int resetFlg); #define SQLITE_STMTSTATUS_VM_STEP 4 #define SQLITE_STMTSTATUS_REPREPARE 5 #define SQLITE_STMTSTATUS_RUN 6 +#define SQLITE_STMTSTATUS_FILTER_MISS 7 +#define SQLITE_STMTSTATUS_FILTER_HIT 8 #define SQLITE_STMTSTATUS_MEMUSED 99 /* @@ -243062,7 +254652,7 @@ typedef struct wx_sqlite3_backup wx_sqlite3_backup; ** if the application incorrectly accesses the destination [database connection] ** and so no error code is reported, but the operations may malfunction ** nevertheless. Use of the destination database connection while a -** backup is in progress might also also cause a mutex deadlock. +** backup is in progress might also cause a mutex deadlock. ** ** If running in [shared cache mode], the application must ** guarantee that the shared cache used by the destination database @@ -243314,8 +254904,9 @@ SQLITE_API void wx_sqlite3_log(int iErrCode, const char *zFormat, ...); ** ** A single database handle may have at most a single write-ahead log callback ** registered at one time. ^Calling [wx_sqlite3_wal_hook()] replaces any -** previously registered write-ahead log callback. ^Note that the -** [wx_sqlite3_wal_autocheckpoint()] interface and the +** previously registered write-ahead log callback. ^The return value is +** a copy of the third parameter from the previous call, if any, or 0. +** ^Note that the [wx_sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] both invoke [wx_sqlite3_wal_hook()] and will ** overwrite any prior [wx_sqlite3_wal_hook()] settings. */ @@ -243489,7 +255080,7 @@ SQLITE_API int wx_sqlite3_wal_checkpoint_v2( */ #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */ #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */ -#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */ +#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */ #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */ /* @@ -243618,18 +255209,274 @@ SQLITE_API int wx_sqlite3_vtab_nochange(wx_sqlite3_context*); /* ** CAPI3REF: Determine The Collation For a Virtual Table Constraint +** METHOD: wx_sqlite3_index_info ** ** This function may only be called from within a call to the [xBestIndex] -** method of a [virtual table]. +** method of a [virtual table]. This function returns a pointer to a string +** that is the name of the appropriate collation sequence to use for text +** comparisons on the constraint identified by its arguments. +** +** The first argument must be the pointer to the [wx_sqlite3_index_info] object +** that is the first parameter to the xBestIndex() method. The second argument +** must be an index into the aConstraint[] array belonging to the +** wx_sqlite3_index_info structure passed to xBestIndex. +** +** Important: +** The first parameter must be the same pointer that is passed into the +** xBestMethod() method. The first parameter may not be a pointer to a +** different [wx_sqlite3_index_info] object, even an exact copy. +** +** The return value is computed as follows: +** +**
      +**
    1. If the constraint comes from a WHERE clause expression that contains +** a [COLLATE operator], then the name of the collation specified by +** that COLLATE operator is returned. +**

    2. If there is no COLLATE operator, but the column that is the subject +** of the constraint specifies an alternative collating sequence via +** a [COLLATE clause] on the column definition within the CREATE TABLE +** statement that was passed into [wx_sqlite3_declare_vtab()], then the +** name of that alternative collating sequence is returned. +**

    3. Otherwise, "BINARY" is returned. +**

    +*/ +SQLITE_API const char *wx_sqlite3_vtab_collation(wx_sqlite3_index_info*,int); + +/* +** CAPI3REF: Determine if a virtual table query is DISTINCT +** METHOD: wx_sqlite3_index_info +** +** This API may only be used from within an [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this +** interface from outside of xBestIndex() is undefined and probably harmful. +** +** ^The wx_sqlite3_vtab_distinct() interface returns an integer between 0 and +** 3. The integer returned by wx_sqlite3_vtab_distinct() +** gives the virtual table additional information about how the query +** planner wants the output to be ordered. As long as the virtual table +** can meet the ordering requirements of the query planner, it may set +** the "orderByConsumed" flag. +** +**
    1. +** ^If the wx_sqlite3_vtab_distinct() interface returns 0, that means +** that the query planner needs the virtual table to return all rows in the +** sort order defined by the "nOrderBy" and "aOrderBy" fields of the +** [wx_sqlite3_index_info] object. This is the default expectation. If the +** virtual table outputs all rows in sorted order, then it is always safe for +** the xBestIndex method to set the "orderByConsumed" flag, regardless of +** the return value from wx_sqlite3_vtab_distinct(). +**

    2. +** ^(If the wx_sqlite3_vtab_distinct() interface returns 1, that means +** that the query planner does not need the rows to be returned in sorted order +** as long as all rows with the same values in all columns identified by the +** "aOrderBy" field are adjacent.)^ This mode is used when the query planner +** is doing a GROUP BY. +**

    3. +** ^(If the wx_sqlite3_vtab_distinct() interface returns 2, that means +** that the query planner does not need the rows returned in any particular +** order, as long as rows with the same values in all "aOrderBy" columns +** are adjacent.)^ ^(Furthermore, only a single row for each particular +** combination of values in the columns identified by the "aOrderBy" field +** needs to be returned.)^ ^It is always ok for two or more rows with the same +** values in all "aOrderBy" columns to be returned, as long as all such rows +** are adjacent. ^The virtual table may, if it chooses, omit extra rows +** that have the same value for all columns identified by "aOrderBy". +** ^However omitting the extra rows is optional. +** This mode is used for a DISTINCT query. +**

    4. +** ^(If the wx_sqlite3_vtab_distinct() interface returns 3, that means +** that the query planner needs only distinct rows but it does need the +** rows to be sorted.)^ ^The virtual table implementation is free to omit +** rows that are identical in all aOrderBy columns, if it wants to, but +** it is not required to omit any rows. This mode is used for queries +** that have both DISTINCT and ORDER BY clauses. +**

    +** +** ^For the purposes of comparing virtual table output values to see if the +** values are same value for sorting purposes, two NULL values are considered +** to be the same. In other words, the comparison operator is "IS" +** (or "IS NOT DISTINCT FROM") and not "==". +** +** If a virtual table implementation is unable to meet the requirements +** specified above, then it must not set the "orderByConsumed" flag in the +** [wx_sqlite3_index_info] object or an incorrect answer may result. +** +** ^A virtual table implementation is always free to return rows in any order +** it wants, as long as the "orderByConsumed" flag is not set. ^When the +** the "orderByConsumed" flag is unset, the query planner will add extra +** [bytecode] to ensure that the final results returned by the SQL query are +** ordered correctly. The use of the "orderByConsumed" flag and the +** wx_sqlite3_vtab_distinct() interface is merely an optimization. ^Careful +** use of the wx_sqlite3_vtab_distinct() interface and the "orderByConsumed" +** flag might help queries against a virtual table to run faster. Being +** overly aggressive and setting the "orderByConsumed" flag when it is not +** valid to do so, on the other hand, might cause SQLite to return incorrect +** results. +*/ +SQLITE_API int wx_sqlite3_vtab_distinct(wx_sqlite3_index_info*); + +/* +** CAPI3REF: Identify and handle IN constraints in xBestIndex +** +** This interface may only be used from within an +** [xBestIndex|xBestIndex() method] of a [virtual table] implementation. +** The result of invoking this interface from any other context is +** undefined and probably harmful. +** +** ^(A constraint on a virtual table of the form +** "[IN operator|column IN (...)]" is +** communicated to the xBestIndex method as a +** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use +** this constraint, it must set the corresponding +** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under +** the usual mode of handling IN operators, SQLite generates [bytecode] +** that invokes the [xFilter|xFilter() method] once for each value +** on the right-hand side of the IN operator.)^ Thus the virtual table +** only sees a single value from the right-hand side of the IN operator +** at a time. +** +** In some cases, however, it would be advantageous for the virtual +** table to see all values on the right-hand of the IN operator all at +** once. The wx_sqlite3_vtab_in() interfaces facilitates this in two ways: +** +**
      +**
    1. +** ^A call to wx_sqlite3_vtab_in(P,N,-1) will return true (non-zero) +** if and only if the [wx_sqlite3_index_info|P->aConstraint][N] constraint +** is an [IN operator] that can be processed all at once. ^In other words, +** wx_sqlite3_vtab_in() with -1 in the third argument is a mechanism +** by which the virtual table can ask SQLite if all-at-once processing +** of the IN operator is even possible. +** +**

    2. +** ^A call to wx_sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates +** to SQLite that the virtual table does or does not want to process +** the IN operator all-at-once, respectively. ^Thus when the third +** parameter (F) is non-negative, this interface is the mechanism by +** which the virtual table tells SQLite how it wants to process the +** IN operator. +**

    +** +** ^The wx_sqlite3_vtab_in(P,N,F) interface can be invoked multiple times +** within the same xBestIndex method call. ^For any given P,N pair, +** the return value from wx_sqlite3_vtab_in(P,N,F) will always be the same +** within the same xBestIndex call. ^If the interface returns true +** (non-zero), that means that the constraint is an IN operator +** that can be processed all-at-once. ^If the constraint is not an IN +** operator or cannot be processed all-at-once, then the interface returns +** false. +** +** ^(All-at-once processing of the IN operator is selected if both of the +** following conditions are met: ** -** The first argument must be the wx_sqlite3_index_info object that is the -** first parameter to the xBestIndex() method. The second argument must be -** an index into the aConstraint[] array belonging to the wx_sqlite3_index_info -** structure passed to xBestIndex. This function returns a pointer to a buffer -** containing the name of the collation sequence for the corresponding -** constraint. +**
      +**
    1. The P->aConstraintUsage[N].argvIndex value is set to a positive +** integer. This is how the virtual table tells SQLite that it wants to +** use the N-th constraint. +** +**

    2. The last call to wx_sqlite3_vtab_in(P,N,F) for which F was +** non-negative had F>=1. +**

    )^ +** +** ^If either or both of the conditions above are false, then SQLite uses +** the traditional one-at-a-time processing strategy for the IN constraint. +** ^If both conditions are true, then the argvIndex-th parameter to the +** xFilter method will be an [wx_sqlite3_value] that appears to be NULL, +** but which can be passed to [wx_sqlite3_vtab_in_first()] and +** [wx_sqlite3_vtab_in_next()] to find all values on the right-hand side +** of the IN constraint. */ -SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_index_info*,int); +SQLITE_API int wx_sqlite3_vtab_in(wx_sqlite3_index_info*, int iCons, int bHandle); + +/* +** CAPI3REF: Find all elements on the right-hand side of an IN constraint. +** +** These interfaces are only useful from within the +** [xFilter|xFilter() method] of a [virtual table] implementation. +** The result of invoking these interfaces from any other context +** is undefined and probably harmful. +** +** The X parameter in a call to wx_sqlite3_vtab_in_first(X,P) or +** wx_sqlite3_vtab_in_next(X,P) should be one of the parameters to the +** xFilter method which invokes these routines, and specifically +** a parameter that was previously selected for all-at-once IN constraint +** processing use the [wx_sqlite3_vtab_in()] interface in the +** [xBestIndex|xBestIndex method]. ^(If the X parameter is not +** an xFilter argument that was selected for all-at-once IN constraint +** processing, then these routines return [SQLITE_ERROR].)^ +** +** ^(Use these routines to access all values on the right-hand side +** of the IN constraint using code like the following: +** +**
    +**    for(rc=wx_sqlite3_vtab_in_first(pList, &pVal);
    +**        rc==SQLITE_OK && pVal;
    +**        rc=wx_sqlite3_vtab_in_next(pList, &pVal)
    +**    ){
    +**      // do something with pVal
    +**    }
    +**    if( rc!=SQLITE_OK ){
    +**      // an error has occurred
    +**    }
    +** 
    )^ +** +** ^On success, the wx_sqlite3_vtab_in_first(X,P) and wx_sqlite3_vtab_in_next(X,P) +** routines return SQLITE_OK and set *P to point to the first or next value +** on the RHS of the IN constraint. ^If there are no more values on the +** right hand side of the IN constraint, then *P is set to NULL and these +** routines return [SQLITE_DONE]. ^The return value might be +** some other value, such as SQLITE_NOMEM, in the event of a malfunction. +** +** The *ppOut values returned by these routines are only valid until the +** next call to either of these routines or until the end of the xFilter +** method from which these routines were called. If the virtual table +** implementation needs to retain the *ppOut values for longer, it must make +** copies. The *ppOut values are [protected wx_sqlite3_value|protected]. +*/ +SQLITE_API int wx_sqlite3_vtab_in_first(wx_sqlite3_value *pVal, wx_sqlite3_value **ppOut); +SQLITE_API int wx_sqlite3_vtab_in_next(wx_sqlite3_value *pVal, wx_sqlite3_value **ppOut); + +/* +** CAPI3REF: Constraint values in xBestIndex() +** METHOD: wx_sqlite3_index_info +** +** This API may only be used from within the [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this interface +** from outside of an xBestIndex method are undefined and probably harmful. +** +** ^When the wx_sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within +** the [xBestIndex] method of a [virtual table] implementation, with P being +** a copy of the [wx_sqlite3_index_info] object pointer passed into xBestIndex and +** J being a 0-based index into P->aConstraint[], then this routine +** attempts to set *V to the value of the right-hand operand of +** that constraint if the right-hand operand is known. ^If the +** right-hand operand is not known, then *V is set to a NULL pointer. +** ^The wx_sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if +** and only if *V is set to a value. ^The wx_sqlite3_vtab_rhs_value(P,J,V) +** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th +** constraint is not available. ^The wx_sqlite3_vtab_rhs_value() interface +** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if +** something goes wrong. +** +** The wx_sqlite3_vtab_rhs_value() interface is usually only successful if +** the right-hand operand of a constraint is a literal value in the original +** SQL statement. If the right-hand operand is an expression or a reference +** to some other column or a [host parameter], then wx_sqlite3_vtab_rhs_value() +** will probably return [SQLITE_NOTFOUND]. +** +** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and +** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such +** constraints, wx_sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^ +** +** ^The [wx_sqlite3_value] object returned in *V is a protected wx_sqlite3_value +** and remains valid for the duration of the xBestIndex method call. +** ^When xBestIndex returns, the wx_sqlite3_value object returned by +** wx_sqlite3_vtab_rhs_value() is automatically deallocated. +** +** The "_rhs_" in the name of this routine is an abbreviation for +** "Right-Hand Side". +*/ +SQLITE_API int wx_sqlite3_vtab_rhs_value(wx_sqlite3_index_info*, int, wx_sqlite3_value **ppVal); /* ** CAPI3REF: Conflict resolution modes @@ -243661,6 +255508,10 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ ** managed by the prepared statement S and will be automatically freed when ** S is finalized. ** +** Not all values are available for all query elements. When a value is +** not available, the output variable is set to -1 if the value is numeric, +** or to NULL if it is a string (SQLITE_SCANSTAT_NAME). +** **
    ** [[SQLITE_SCANSTAT_NLOOP]]
    SQLITE_SCANSTAT_NLOOP
    **
    ^The [wx_sqlite3_int64] variable pointed to by the V parameter will be @@ -243688,12 +255539,24 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] ** description for the X-th loop. ** -** [[SQLITE_SCANSTAT_SELECTID]]
    SQLITE_SCANSTAT_SELECT
    +** [[SQLITE_SCANSTAT_SELECTID]]
    SQLITE_SCANSTAT_SELECTID
    **
    ^The "int" variable pointed to by the V parameter will be set to the -** "select-id" for the X-th loop. The select-id identifies which query or -** subquery the loop is part of. The main query has a select-id of zero. -** The select-id is the same value as is output in the first column -** of an [EXPLAIN QUERY PLAN] query. +** id for the X-th query plan element. The id value is unique within the +** statement. The select-id is the same value as is output in the first +** column of an [EXPLAIN QUERY PLAN] query. +** +** [[SQLITE_SCANSTAT_PARENTID]]
    SQLITE_SCANSTAT_PARENTID
    +**
    The "int" variable pointed to by the V parameter will be set to the +** the id of the parent of the current query element, if applicable, or +** to zero if the query element has no parent. This is the same value as +** returned in the second column of an [EXPLAIN QUERY PLAN] query. +** +** [[SQLITE_SCANSTAT_NCYCLE]]
    SQLITE_SCANSTAT_NCYCLE
    +**
    The wx_sqlite3_int64 output value is set to the number of cycles, +** according to the processor time-stamp counter, that elapsed while the +** query element was being processed. This value is not available for +** all query elements - if it is unavailable the output variable is +** set to -1. **
    */ #define SQLITE_SCANSTAT_NLOOP 0 @@ -243702,12 +255565,14 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ #define SQLITE_SCANSTAT_NAME 3 #define SQLITE_SCANSTAT_EXPLAIN 4 #define SQLITE_SCANSTAT_SELECTID 5 +#define SQLITE_SCANSTAT_PARENTID 6 +#define SQLITE_SCANSTAT_NCYCLE 7 /* ** CAPI3REF: Prepared Statement Scan Status ** METHOD: wx_sqlite3_stmt ** -** This interface returns information about the predicted and measured +** These interfaces return information about the predicted and measured ** performance for pStmt. Advanced applications can use this ** interface to compare the predicted and the measured performance and ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. @@ -243718,19 +255583,25 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ ** ** The "iScanStatusOp" parameter determines which status information to return. ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior -** of this interface is undefined. -** ^The requested measurement is written into a variable pointed to by -** the "pOut" parameter. -** Parameter "idx" identifies the specific loop to retrieve statistics for. -** Loops are numbered starting from zero. ^If idx is out of range - less than -** zero or greater than or equal to the total number of loops used to implement -** the statement - a non-zero value is returned and the variable that pOut -** points to is unchanged. -** -** ^Statistics might not be available for all loops in all statements. ^In cases -** where there exist loops with no available statistics, this function behaves -** as if the loop did not exist - it returns non-zero and leave the variable -** that pOut points to unchanged. +** of this interface is undefined. ^The requested measurement is written into +** a variable pointed to by the "pOut" parameter. +** +** The "flags" parameter must be passed a mask of flags. At present only +** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX +** is specified, then status information is available for all elements +** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If +** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements +** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of +** the EXPLAIN QUERY PLAN output) are available. Invoking API +** wx_sqlite3_stmt_scanstatus() is equivalent to calling +** wx_sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter. +** +** Parameter "idx" identifies the specific query element to retrieve statistics +** for. Query elements are numbered starting from zero. A value of -1 may be +** to query for statistics regarding the entire query. ^If idx is out of range +** - less than -1 or greater than or equal to the total number of query +** elements used to implement the statement - a non-zero value is returned and +** the variable that pOut points to is unchanged. ** ** See also: [wx_sqlite3_stmt_scanstatus_reset()] */ @@ -243740,6 +255611,19 @@ SQLITE_API int wx_sqlite3_stmt_scanstatus( int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ void *pOut /* Result written here */ ); +SQLITE_API int wx_sqlite3_stmt_scanstatus_v2( + wx_sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ + int idx, /* Index of loop to report on */ + int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ + int flags, /* Mask of flags defined below */ + void *pOut /* Result written here */ +); + +/* +** CAPI3REF: Prepared Statement Scan Status +** KEYWORDS: {scan status flags} +*/ +#define SQLITE_SCANSTAT_COMPLEX 0x0001 /* ** CAPI3REF: Zero Scan-Status Counters @@ -243830,6 +255714,10 @@ SQLITE_API int wx_sqlite3_db_cacheflush(wx_sqlite3*); ** function is not defined for operations on WITHOUT ROWID tables, or for ** DELETE operations on rowid tables. ** +** ^The wx_sqlite3_preupdate_hook(D,C,P) function returns the P argument from +** the previous call on the same [database connection] D, or NULL for +** the first call on D. +** ** The [wx_sqlite3_preupdate_old()], [wx_sqlite3_preupdate_new()], ** [wx_sqlite3_preupdate_count()], and [wx_sqlite3_preupdate_depth()] interfaces ** provide additional information about a preupdate event. These routines @@ -243866,6 +255754,15 @@ SQLITE_API int wx_sqlite3_db_cacheflush(wx_sqlite3*); ** triggers; or 2 for changes resulting from triggers called by top-level ** triggers; and so forth. ** +** When the [wx_sqlite3_blob_write()] API is used to update a blob column, +** the pre-update hook is invoked with SQLITE_DELETE. This is because the +** in this case the new values are not available. In this case, when a +** callback made with op==SQLITE_DELETE is actuall a write using the +** wx_sqlite3_blob_write() API, the [wx_sqlite3_preupdate_blobwrite()] returns +** the index of the column being written. In other cases, where the +** pre-update hook is being invoked for some other reason, including a +** regular DELETE, wx_sqlite3_preupdate_blobwrite() returns -1. +** ** See also: [wx_sqlite3_update_hook()] */ #if defined(SQLITE_ENABLE_PREUPDATE_HOOK) @@ -243886,6 +255783,7 @@ SQLITE_API int wx_sqlite3_preupdate_old(wx_sqlite3 *, int, wx_sqlite3_value **); SQLITE_API int wx_sqlite3_preupdate_count(wx_sqlite3 *); SQLITE_API int wx_sqlite3_preupdate_depth(wx_sqlite3 *); SQLITE_API int wx_sqlite3_preupdate_new(wx_sqlite3 *, int, wx_sqlite3_value **); +SQLITE_API int wx_sqlite3_preupdate_blobwrite(wx_sqlite3 *); #endif /* @@ -244124,8 +256022,8 @@ SQLITE_API SQLITE_EXPERIMENTAL int wx_sqlite3_snapshot_recover(wx_sqlite3 *db, c ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory ** allocation error occurs. ** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_DESERIALIZE] option. +** This interface is omitted if SQLite is compiled with the +** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API unsigned char *wx_sqlite3_serialize( wx_sqlite3 *db, /* The database connection */ @@ -244172,12 +256070,16 @@ SQLITE_API unsigned char *wx_sqlite3_serialize( ** database is currently in a read transaction or is involved in a backup ** operation. ** +** It is not possible to deserialized into the TEMP database. If the +** S argument to wx_sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the +** function returns SQLITE_ERROR. +** ** If wx_sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then ** [wx_sqlite3_free()] is invoked on argument P prior to returning. ** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_DESERIALIZE] option. +** This interface is omitted if SQLite is compiled with the +** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API int wx_sqlite3_deserialize( wx_sqlite3 *db, /* The database connection */ @@ -244221,6 +256123,19 @@ SQLITE_API int wx_sqlite3_deserialize( # undef double #endif +#if defined(__wasi__) +# undef SQLITE_WASI +# define SQLITE_WASI 1 +# undef SQLITE_OMIT_WAL +# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */ +# ifndef SQLITE_OMIT_LOAD_EXTENSION +# define SQLITE_OMIT_LOAD_EXTENSION +# endif +# ifndef SQLITE_THREADSAFE +# define SQLITE_THREADSAFE 0 +# endif +#endif + #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif @@ -244426,6 +256341,38 @@ SQLITE_API int wx_sqlite3session_create( */ SQLITE_API void wx_sqlite3session_delete(wx_sqlite3_session *pSession); +/* +** CAPIREF: Conigure a Session Object +** METHOD: wx_sqlite3_session +** +** This method is used to configure a session object after it has been +** created. At present the only valid value for the second parameter is +** [SQLITE_SESSION_OBJCONFIG_SIZE]. +** +** Arguments for wx_sqlite3session_object_config() +** +** The following values may passed as the the 4th parameter to +** wx_sqlite3session_object_config(). +** +**
    SQLITE_SESSION_OBJCONFIG_SIZE
    +** This option is used to set, clear or query the flag that enables +** the [wx_sqlite3session_changeset_size()] API. Because it imposes some +** computational overhead, this API is disabled by default. Argument +** pArg must point to a value of type (int). If the value is initially +** 0, then the wx_sqlite3session_changeset_size() API is disabled. If it +** is greater than 0, then the same API is enabled. Or, if the initial +** value is less than zero, no change is made. In all cases the (int) +** variable is set to 1 if the wx_sqlite3session_changeset_size() API is +** enabled following the current call, or 0 otherwise. +** +** It is an error (SQLITE_MISUSE) to attempt to modify this setting after +** the first table has been attached to the session object. +*/ +SQLITE_API int wx_sqlite3session_object_config(wx_sqlite3_session*, int op, void *pArg); + +/* +*/ +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 /* ** CAPI3REF: Enable Or Disable A Session Object @@ -244670,6 +256617,22 @@ SQLITE_API int wx_sqlite3session_changeset( void **ppChangeset /* OUT: Buffer containing changeset */ ); +/* +** CAPI3REF: Return An Upper-limit For The Size Of The Changeset +** METHOD: wx_sqlite3_session +** +** By default, this function always returns 0. For it to return +** a useful result, the wx_sqlite3_session object must have been configured +** to enable this API using wx_sqlite3session_object_config() with the +** SQLITE_SESSION_OBJCONFIG_SIZE verb. +** +** When enabled, this function returns an upper limit, in bytes, for the size +** of the changeset that might be produced if wx_sqlite3session_changeset() were +** called. The final changeset size might be equal to or smaller than the +** size in bytes returned by this function. +*/ +SQLITE_API wx_sqlite3_int64 wx_sqlite3session_changeset_size(wx_sqlite3_session *pSession); + /* ** CAPI3REF: Load The Difference Between Tables Into A Session ** METHOD: wx_sqlite3_session @@ -246706,74 +258669,13 @@ int wx_sqlite3_user_delete( /* ** Symbols for ciphers */ -#define CODEC_TYPE_UNKNOWN 0 -#define CODEC_TYPE_AES128 1 -#define CODEC_TYPE_AES256 2 -#define CODEC_TYPE_CHACHA20 3 -#define CODEC_TYPE_SQLCIPHER 4 -#define CODEC_TYPE_RC4 5 -#define CODEC_TYPE_MAX 5 - -/* -** Definitions of supported ciphers -*/ - -/* -** Compatibility with wxSQLite3 -*/ -#ifdef WXSQLITE3_HAVE_CIPHER_AES_128_CBC -#define HAVE_CIPHER_AES_128_CBC WXSQLITE3_HAVE_CIPHER_AES_128_CBC -#endif - -#ifdef WXSQLITE3_HAVE_CIPHER_AES_256_CBC -#define HAVE_CIPHER_AES_256_CBC WXSQLITE3_HAVE_CIPHER_AES_256_CBC -#endif - -#ifdef WXSQLITE3_HAVE_CIPHER_CHACHA20 -#define HAVE_CIPHER_CHACHA20 WXSQLITE3_HAVE_CIPHER_CHACHA20 -#endif - -#ifdef WXSQLITE3_HAVE_CIPHER_SQLCIPHER -#define HAVE_CIPHER_SQLCIPHER WXSQLITE3_HAVE_CIPHER_SQLCIPHER -#endif - -#ifdef WXSQLITE3_HAVE_CIPHER_RC4 -#define HAVE_CIPHER_RC4 WXSQLITE3_HAVE_CIPHER_RC4 -#endif - -/* -** Actual definitions of supported ciphers -*/ -#ifndef HAVE_CIPHER_AES_128_CBC -#define HAVE_CIPHER_AES_128_CBC 1 -#endif - -#ifndef HAVE_CIPHER_AES_256_CBC -#define HAVE_CIPHER_AES_256_CBC 1 -#endif - -#ifndef HAVE_CIPHER_CHACHA20 -#define HAVE_CIPHER_CHACHA20 1 -#endif - -#ifndef HAVE_CIPHER_SQLCIPHER -#define HAVE_CIPHER_SQLCIPHER 1 -#endif - -#ifndef HAVE_CIPHER_RC4 -#define HAVE_CIPHER_RC4 1 -#endif - -/* -** Check that at least one cipher is be supported -*/ -#if HAVE_CIPHER_AES_128_CBC == 0 && \ - HAVE_CIPHER_AES_256_CBC == 0 && \ - HAVE_CIPHER_CHACHA20 == 0 && \ - HAVE_CIPHER_SQLCIPHER == 0 && \ - HAVE_CIPHER_RC4 == 0 -#error Enable at least one cipher scheme! -#endif +#define CODEC_TYPE_UNKNOWN 0 +#define CODEC_TYPE_AES128 1 +#define CODEC_TYPE_AES256 2 +#define CODEC_TYPE_CHACHA20 3 +#define CODEC_TYPE_SQLCIPHER 4 +#define CODEC_TYPE_RC4 5 +#define CODEC_TYPE_MAX_BUILTIN 5 /* ** Definition of API functions @@ -246842,6 +258744,9 @@ SQLITE_API void wx_sqlite3_activate_see(const char* zPassPhrase); /* ** Define functions for the configuration of the wxSQLite3 encryption extension */ +SQLITE_API int wx_sqlite3mc_cipher_count(); +SQLITE_API int wx_sqlite3mc_cipher_index(const char* cipherName); +SQLITE_API const char* wx_sqlite3mc_cipher_name(int cipherIndex); SQLITE_API int wx_sqlite3mc_config(wx_sqlite3* db, const char* paramName, int newValue); SQLITE_API int wx_sqlite3mc_config_cipher(wx_sqlite3* db, const char* cipherName, const char* paramName, int newValue); SQLITE_API unsigned char* wx_sqlite3mc_codec_data(wx_sqlite3* db, const char* zDbName, const char* paramName); @@ -246853,6 +258758,88 @@ SQLITE_API int wxwx_sqlite3_config_cipher(wx_sqlite3* db, const char* cipherName SQLITE_API unsigned char* wxwx_sqlite3_codec_data(wx_sqlite3* db, const char* zDbName, const char* paramName); #endif +/* +** Structures and functions to dynamically register a cipher +*/ + +/* +** Structure for a single cipher configuration parameter +** +** Components: +** m_name - name of parameter (1st char = alpha, rest = alphanumeric or underscore, max 63 characters) +** m_value - current/transient parameter value +** m_default - default parameter value +** m_minValue - minimum valid parameter value +** m_maxValue - maximum valid parameter value +*/ +typedef struct _CipherParams +{ + char* m_name; + int m_value; + int m_default; + int m_minValue; + int m_maxValue; +} CipherParams; + +/* +** Structure for a cipher API +** +** Components: +** m_name - name of cipher (1st char = alpha, rest = alphanumeric or underscore, max 63 characters) +** m_allocateCipher - Function pointer for function AllocateCipher +** m_freeCipher - Function pointer for function FreeCipher +** m_cloneCipher - Function pointer for function CloneCipher +** m_getLegacy - Function pointer for function GetLegacy +** m_getPageSize - Function pointer for function GetPageSize +** m_getReserved - Function pointer for function GetReserved +** m_getSalt - Function pointer for function GetSalt +** m_generateKey - Function pointer for function GenerateKey +** m_encryptPage - Function pointer for function EncryptPage +** m_decryptPage - Function pointer for function DecryptPage +*/ + +typedef struct BtShared BtSharedMC; + +typedef void* (*AllocateCipher_t)(wx_sqlite3* db); +typedef void (*FreeCipher_t)(void* cipher); +typedef void (*CloneCipher_t)(void* cipherTo, void* cipherFrom); +typedef int (*GetLegacy_t)(void* cipher); +typedef int (*GetPageSize_t)(void* cipher); +typedef int (*GetReserved_t)(void* cipher); +typedef unsigned char* (*GetSalt_t)(void* cipher); +typedef void (*GenerateKey_t)(void* cipher, BtSharedMC* pBt, char* userPassword, int passwordLength, int rekey, unsigned char* cipherSalt); +typedef int (*EncryptPage_t)(void* cipher, int page, unsigned char* data, int len, int reserved); +typedef int (*DecryptPage_t)(void* cipher, int page, unsigned char* data, int len, int reserved, int hmacCheck); + +typedef struct _CipherDescriptor +{ + char* m_name; + AllocateCipher_t m_allocateCipher; + FreeCipher_t m_freeCipher; + CloneCipher_t m_cloneCipher; + GetLegacy_t m_getLegacy; + GetPageSize_t m_getPageSize; + GetReserved_t m_getReserved; + GetSalt_t m_getSalt; + GenerateKey_t m_generateKey; + EncryptPage_t m_encryptPage; + DecryptPage_t m_decryptPage; +} CipherDescriptor; + +/* +** Register a cipher +** +** Arguments: +** desc - Cipher descriptor structure +** params - Cipher configuration parameter table +** makeDefault - flag whether to make the cipher the default cipher +** +** Returns: +** SQLITE_OK - the cipher could be registered successfully +** SQLITE_ERROR - the cipher could not be registered +*/ +SQLITE_API int wx_sqlite3mc_register_cipher(const CipherDescriptor* desc, const CipherParams* params, int makeDefault); + #ifdef __cplusplus } #endif @@ -246867,7 +258854,7 @@ SQLITE_API unsigned char* wxwx_sqlite3_codec_data(wx_sqlite3* db, const char* zD ** Purpose: Header file for VFS of SQLite3 Multiple Ciphers support ** Author: Ulrich Telle ** Created: 2020-03-01 -** Copyright: (c) 2020 Ulrich Telle +** Copyright: (c) 2020-2023 Ulrich Telle ** License: MIT */ @@ -246877,6 +258864,11 @@ SQLITE_API unsigned char* wxwx_sqlite3_codec_data(wx_sqlite3* db, const char* zD extern "C" { #endif +#ifndef SQLITE_PRIVATE +#define SQLITE_PRIVATE +#endif +SQLITE_PRIVATE int wx_sqlite3mcCheckVfs(const char* zVfs); + SQLITE_API int wx_sqlite3mc_vfs_create(const char* zVfsReal, int makeDefault); SQLITE_API void wx_sqlite3mc_vfs_destroy(const char* zName); SQLITE_API void wx_sqlite3mc_vfs_shutdown(); @@ -246892,31 +258884,6 @@ SQLITE_API void wx_sqlite3mc_vfs_shutdown(); #endif /*** End of #include "wx_sqlite3mc.h" ***/ -/* #include "wx_sqlite3mc_version.h" */ -/*** Begin of #include "wx_sqlite3mc_version.h" ***/ -/* -** Name: wx_sqlite3mc_version.h -** Purpose: SQLite3 Multiple Ciphers version numbers -** Author: Ulrich Telle -** Created: 2020-08-05 -** Copyright: (c) 2020-2021 Ulrich Telle -** License: MIT -*/ - -/// \file wx_sqlite3mc_version.h Version information for the SQLite3 Multiple Ciphers library - -#ifndef SQLITE3MC_VERSION_H_ -#define SQLITE3MC_VERSION_H_ - -#define SQLITE3MC_VERSION_MAJOR 1 -#define SQLITE3MC_VERSION_MINOR 2 -#define SQLITE3MC_VERSION_RELEASE 4 -#define SQLITE3MC_VERSION_SUBRELEASE 0 -#define SQLITE3MC_VERSION_STRING "SQLite3 Multiple Ciphers 1.2.4" - -#endif /* SQLITE3MC_VERSION_H_ */ -/*** End of #include "wx_sqlite3mc_version.h" ***/ - SQLITE_API const char* wx_sqlite3mc_version() @@ -247814,7 +259781,11 @@ void sha512(const unsigned char *message, unsigned int len, #define SHFR(x, n) (x >> n) #define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#if 0 +/* SQLite version 3.40.0 and later already defines this macro. */ +/* The macro isn't used here anyway, so simply inactivate it. */ #define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) +#endif #define CH(x, y, z) ((x & y) ^ (~x & z)) #define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) @@ -248842,7 +260813,7 @@ void sqlcipher_hmac(int algorithm, #include #include -#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__clang__) +#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__clang__) && !defined(__QNX__) #include #endif @@ -249357,7 +261328,8 @@ void chacha20_rng(void* out, size_t n); static void chacha20_block(uint32_t x[16]) { int i; - #define QR(x, a, b, c, d) \ + /* Macro renamed from QR to CC20QR to avoid name clashes. */ + #define CC20QR(x, a, b, c, d) \ x[a] += x[b]; x[d] ^= x[a]; x[d] = ROL32(x[d], 16); \ x[c] += x[d]; x[b] ^= x[c]; x[b] = ROL32(x[b], 12); \ x[a] += x[b]; x[d] ^= x[a]; x[d] = ROL32(x[d], 8); \ @@ -249365,17 +261337,17 @@ static void chacha20_block(uint32_t x[16]) for (i = 0; i < 10; i++) { /* Column round */ - QR(x, 0, 4, 8, 12) - QR(x, 1, 5, 9, 13) - QR(x, 2, 6, 10, 14) - QR(x, 3, 7, 11, 15) + CC20QR(x, 0, 4, 8, 12) + CC20QR(x, 1, 5, 9, 13) + CC20QR(x, 2, 6, 10, 14) + CC20QR(x, 3, 7, 11, 15) /* Diagonal round */ - QR(x, 0, 5, 10, 15) - QR(x, 1, 6, 11, 12) - QR(x, 2, 7, 8, 13) - QR(x, 3, 4, 9, 14) + CC20QR(x, 0, 5, 10, 15) + CC20QR(x, 1, 6, 11, 12) + CC20QR(x, 2, 7, 8, 13) + CC20QR(x, 3, 4, 9, 14) } - #undef QR + #undef CC20QR } void chacha20_xor(void* buffer, size_t n, const uint8_t key[32], @@ -249448,13 +261420,11 @@ void chacha20_xor(void* buffer, size_t n, const uint8_t key[32], void poly1305(const uint8_t* msg, size_t n, const uint8_t key[32], uint8_t tag[16]) { - uint32_t hibit; uint64_t d0, d1, d2, d3, d4; uint32_t h0, h1, h2, h3, h4; uint32_t r0, r1, r2, r3, r4; uint32_t s1, s2, s3, s4; - hibit = (uint32_t) 1 << 24; h0 = h1 = h2 = h3 = h4 = 0; r0 = (LOAD32_LE(key + 0) >> 0) & 0x03FFFFFF; r1 = (LOAD32_LE(key + 3) >> 2) & 0x03FFFF03; s1 = r1 * 5; @@ -249463,12 +261433,13 @@ void poly1305(const uint8_t* msg, size_t n, const uint8_t key[32], r4 = (LOAD32_LE(key + 12) >> 8) & 0x000FFFFF; s4 = r4 * 5; while (n >= 16) { + h4 += 0x01000000; process_block: h0 += (LOAD32_LE(msg + 0) >> 0) & 0x03FFFFFF; h1 += (LOAD32_LE(msg + 3) >> 2) & 0x03FFFFFF; h2 += (LOAD32_LE(msg + 6) >> 4) & 0x03FFFFFF; h3 += (LOAD32_LE(msg + 9) >> 6) & 0x03FFFFFF; - h4 += (LOAD32_LE(msg + 12) >> 8) | hibit; + h4 += (LOAD32_LE(msg + 12) >> 8); #define MUL(a,b) ((uint64_t)(a) * (b)) d0 = MUL(h0,r0) + MUL(h1,s4) + MUL(h2,s3) + MUL(h3,s2) + MUL(h4,s1); @@ -249493,32 +261464,26 @@ process_block: for (i = 0; i < n; tag[i] = msg[i], i++); for (tag[i++] = 1; i < 16; tag[i++] = 0); msg = tag; - hibit = 0; n = 16; goto process_block; } - r0 = h0 + 5; - r1 = h1 + (r0 >> 26); *(volatile uint32_t *)&r0 = 0; - r2 = h2 + (r1 >> 26); *(volatile uint32_t *)&r1 = 0; - r3 = h3 + (r2 >> 26); *(volatile uint32_t *)&r2 = 0; - r4 = h4 + (r3 >> 26); *(volatile uint32_t *)&r3 = 0; - h0 = h0 + (r4 >> 26) * 5; *(volatile uint32_t *)&r4 = 0; + r0 = (h0 + 5) >> 26; + r1 = (h1 + r0) >> 26; + r2 = (h2 + r1) >> 26; + r3 = (h3 + r2) >> 26; + r4 = (h4 + r3) >> 26; + h0 += r4 * 5; - d0 = (uint64_t)LOAD32_LE(key + 16) + (h0 >> 0) + (h1 << 26); - d1 = (uint64_t)LOAD32_LE(key + 20) + (h1 >> 6) + (h2 << 20) + (d0 >> 32); - d2 = (uint64_t)LOAD32_LE(key + 24) + (h2 >> 12) + (h3 << 14) + (d1 >> 32); - d3 = (uint64_t)LOAD32_LE(key + 28) + (h3 >> 18) + (h4 << 8) + (d2 >> 32); + d1 = (uint64_t)LOAD32_LE(key + 16) + (h0 >> 0) + (h1 << 26); + d2 = (uint64_t)LOAD32_LE(key + 20) + (h1 >> 6) + (h2 << 20) + (d1 >> 32); + d3 = (uint64_t)LOAD32_LE(key + 24) + (h2 >> 12) + (h3 << 14) + (d2 >> 32); + d4 = (uint64_t)LOAD32_LE(key + 28) + (h3 >> 18) + (h4 << 8) + (d3 >> 32); - STORE32_LE(tag + 0, d0); *(volatile uint32_t *)&s1 = 0; - STORE32_LE(tag + 4, d1); *(volatile uint32_t *)&s2 = 0; - STORE32_LE(tag + 8, d2); *(volatile uint32_t *)&s3 = 0; - STORE32_LE(tag + 12, d3); *(volatile uint32_t *)&s4 = 0; - *(volatile uint64_t *)&d0 = 0; *(volatile uint32_t *)&h0 = 0; - *(volatile uint64_t *)&d1 = 0; *(volatile uint32_t *)&h1 = 0; - *(volatile uint64_t *)&d2 = 0; *(volatile uint32_t *)&h2 = 0; - *(volatile uint64_t *)&d3 = 0; *(volatile uint32_t *)&h3 = 0; - *(volatile uint64_t *)&d4 = 0; *(volatile uint32_t *)&h4 = 0; + s1 = d1; STORE32_LE(tag + 0, s1); + s2 = d2; STORE32_LE(tag + 4, s2); + s3 = d3; STORE32_LE(tag + 8, s3); + s4 = d4; STORE32_LE(tag + 12, s4); } int poly1305_tagcmp(const uint8_t tag1[16], const uint8_t tag2[16]) @@ -249547,6 +261512,48 @@ int poly1305_tagcmp(const uint8_t tag1[16], const uint8_t tag2[16]) * Platform-specific entropy functions for seeding RNG */ #if defined(_WIN32) || defined(__CYGWIN__) + +#if SQLITE3MC_USE_RAND_S + +/* Force header stdlib.h to define rand_s() */ +#if !defined(_CRT_RAND_S) +#define _CRT_RAND_S +#endif +#include + +/* + Provide declaration of rand_s() for MinGW-32 (not 64). + MinGW-32 didn't declare it prior to version 5.3.0. +*/ +#if defined(__MINGW32__) && defined(__MINGW32_VERSION) && __MINGW32_VERSION < 5003000L && !defined(__MINGW64_VERSION_MAJOR) +__declspec(dllimport) int rand_s(unsigned int *); +#endif + +static size_t entropy(void* buf, size_t n) +{ + size_t totalBytes = 0; + while (totalBytes < n) + { + unsigned int random32 = 0; + size_t j = 0; + + if (rand_s(&random32)) + { + /* rand_s failed */ + return 0; + } + + for (; (j < sizeof(random32)) && (totalBytes < n); j++, totalBytes++) + { + const uint8_t random8 = (uint8_t)(random32 >> (j * 8)); + ((uint8_t*) buf)[totalBytes] = random8; + } + } + return n; +} + +#else + #include #define RtlGenRandom SystemFunction036 BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength); @@ -249555,7 +261562,11 @@ static size_t entropy(void* buf, size_t n) { return RtlGenRandom(buf, (ULONG) n) ? n : 0; } -#elif defined(__linux__) || defined(__unix__) || defined(__APPLE__) + +#endif + +#elif defined(__linux__) || defined(__unix__) || defined(__APPLE__) || defined(__QNX__) + #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif @@ -249635,7 +261646,7 @@ fail: return 0; } -#if defined(__APPLE__) && defined(__MAC_10_12) +#if defined(__APPLE__) && defined(__MAC_10_12) && !defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #include #endif @@ -249653,6 +261664,16 @@ static size_t entropy(void* buf, size_t n) #endif return read_urandom(buf, n); } + +#elif defined(__WASM__) + +extern size_t getentropy(void* buf, size_t n); + +static size_t entropy(void* buf, size_t n) +{ + return (getentropy(buf, n) == 0) ? n : 0; +} + #else # error "Secure pseudorandom number generator not implemented for this OS" #endif @@ -249796,7 +261817,7 @@ static int userAuthCheckLogin( int rc; *peAuth = UAUTH_Unknown; - if( !userTableExists(db, "main") ){ + if( !userTableExists(db, zDb) ){ *peAuth = UAUTH_Admin; /* No sqlite_user table. Everybody is admin. */ return SQLITE_OK; } @@ -250485,7 +262506,7 @@ static int aesHardwareCheck() { unsigned int CPUInfo[4]; - __cpuid(CPUInfo, 1); + __cpuid((int*) CPUInfo, 1); return (CPUInfo[2] & (1 << 25)) != 0 && (CPUInfo[2] & (1 << 19)) != 0; /* Check AES and SSE4.1 */ } @@ -250764,6 +262785,7 @@ aesDecryptCBC(const unsigned char* in, #ifdef USE_CLANG_ATTR_TARGET_AARCH64 #define __ARM_NEON 1 #define __ARM_FEATURE_CRYPTO 1 +#define __ARM_FEATURE_AES 1 #define FUNC_ISA __attribute__ ((target("neon,crypto"))) #endif /* USE_CLANG_ATTR_TARGET_AARCH64 */ @@ -252875,7 +264897,7 @@ void RijndaelInvalidate(Rijndael* rijndael) ** Purpose: Header for the ciphers of SQLite3 Multiple Ciphers ** Author: Ulrich Telle ** Created: 2020-02-02 -** Copyright: (c) 2006-2020 Ulrich Telle +** Copyright: (c) 2006-2022 Ulrich Telle ** License: MIT */ @@ -252897,10 +264919,31 @@ void RijndaelInvalidate(Rijndael* rijndael) #define CODEC_TYPE CODEC_TYPE_DEFAULT #endif -#if CODEC_TYPE < 1 || CODEC_TYPE > CODEC_TYPE_MAX +#if CODEC_TYPE < 1 || CODEC_TYPE > CODEC_TYPE_MAX_BUILTIN #error "Invalid codec type selected" #endif +/* +** Define the maximum number of ciphers that can be registered +*/ + +/* Use a reasonable upper limit for the maximum number of ciphers */ +#define CODEC_COUNT_LIMIT 16 + +#ifdef SQLITE3MC_MAX_CODEC_COUNT +/* Allow at least to register all built-in ciphers, but use a reasonable upper limit */ +#if SQLITE3MC_MAX_CODEC_COUNT >= CODEC_TYPE_MAX_BUILTIN && SQLITE3MC_MAX_CODEC_COUNT <= CODEC_COUNT_LIMIT +#define CODEC_COUNT_MAX SQLITE3MC_MAX_CODEC_COUNT +#else +#error "Maximum cipher count not in range [CODEC_TYPE_MAX_BUILTIN .. CODEC_COUNT_LIMIT]" +#endif +#else +#define CODEC_COUNT_MAX CODEC_COUNT_LIMIT +#endif + +#define CIPHER_NAME_MAXLEN 32 +#define CIPHER_PARAMS_COUNT_MAX 64 + #define MAXKEYLENGTH 32 #define KEYLENGTH_AES128 16 #define KEYLENGTH_AES256 32 @@ -252908,10 +264951,18 @@ void RijndaelInvalidate(Rijndael* rijndael) #define CODEC_SHA_ITER 4001 +typedef struct _CodecParameter +{ + char* m_name; + int m_id; + CipherParams* m_params; +} CodecParameter; + typedef struct _Codec { int m_isEncrypted; int m_hmacCheck; + int m_walLegacy; /* Read cipher */ int m_hasReadCipher; int m_readCipherType; @@ -252938,53 +264989,11 @@ typedef struct _Codec #define CIPHER_PARAMS_SENTINEL { "", 0, 0, 0, 0 } #define CIPHER_PAGE1_OFFSET 24 -typedef struct _CipherParams -{ - char* m_name; - int m_value; - int m_default; - int m_minValue; - int m_maxValue; -} CipherParams; - -typedef struct _CodecParameter -{ - char* m_name; - int m_id; - CipherParams* m_params; -} CodecParameter; - -typedef void* (*AllocateCipher_t)(wx_sqlite3* db); -typedef void (*FreeCipher_t)(void* cipher); -typedef void (*CloneCipher_t)(void* cipherTo, void* cipherFrom); -typedef int (*GetLegacy_t)(void* cipher); -typedef int (*GetPageSize_t)(void* cipher); -typedef int (*GetReserved_t)(void* cipher); -typedef unsigned char* (*GetSalt_t)(void* cipher); -typedef void (*GenerateKey_t)(void* cipher, BtShared* pBt, char* userPassword, int passwordLength, int rekey, unsigned char* cipherSalt); -typedef int (*EncryptPage_t)(void* cipher, int page, unsigned char* data, int len, int reserved); -typedef int (*DecryptPage_t)(void* cipher, int page, unsigned char* data, int len, int reserved, int hmacCheck); - -typedef struct _CodecDescriptor -{ - char m_name[32]; - AllocateCipher_t m_allocateCipher; - FreeCipher_t m_freeCipher; - CloneCipher_t m_cloneCipher; - GetLegacy_t m_getLegacy; - GetPageSize_t m_getPageSize; - GetReserved_t m_getReserved; - GetSalt_t m_getSalt; - GenerateKey_t m_generateKey; - EncryptPage_t m_encryptPage; - DecryptPage_t m_decryptPage; -} CipherDescriptor; - SQLITE_PRIVATE int wx_sqlite3mcGetCipherParameter(CipherParams* cipherParams, const char* paramName); SQLITE_PRIVATE int wx_sqlite3mcGetCipherType(wx_sqlite3* db); -SQLITE_PRIVATE CipherParams* wx_sqlite3mcGetCipherParams(wx_sqlite3* db, int cypherType); +SQLITE_PRIVATE CipherParams* wx_sqlite3mcGetCipherParams(wx_sqlite3* db, const char* cipherName); SQLITE_PRIVATE int wx_sqlite3mcCodecInit(Codec* codec); @@ -253112,6 +265121,33 @@ SQLITE_PRIVATE void wx_sqlite3mcCodecGetKey(wx_sqlite3* db, int nDb, void** zKey #define SQLITE3MC_DEBUG_HEX(DESC,BUFFER,LEN) #endif +/* +** If encryption was enabled and WAL journal mode was used, +** SQLite3 Multiple Ciphers encrypted the WAL journal frames up to version 1.2.5 +** within the VFS implementation. As a consequence the WAL journal file was not +** compatible with legacy encryption implementations (for example, System.Data.SQLite +** or SQLCipher). Additionally, the implementation of the WAL journal encryption +** was broken, because reading and writing of complete WAL frames was not handled +** correctly. Usually, operating in WAL journal mode worked nevertheless, but after +** crashes the WAL journal file could be corrupted leading to data loss. +** +** Version 1.3.0 introduced a new way to handle WAL journal encryption. The advantage +** is that the WAL journal file is now compatible with legacy encryption implementations. +** Unfortunately the new implementation is not compatible with that used up to version +** 1.2.5. To be able to access WAL journals created by prior versions, the configuration +** parameter 'mc_legacy_wal' was introduced. If the parameter is set to 1, then the +** prior WAL journal encryption mode is used. The default of this parameter can be set +** at compile time by setting the symbol SQLITE3MC_LEGACY_WAL accordingly, but the actual +** value can also be set at runtime using the pragma or the URI parameter 'mc_legacy_wal'. +** +** In principle, operating generally in WAL legacy mode is possible, but it is strongly +** recommended to use the WAL legacy mode only to recover WAL journals left behind by +** prior versions without data loss. +*/ +#ifndef SQLITE3MC_LEGACY_WAL +#define SQLITE3MC_LEGACY_WAL 0 +#endif + #endif /*** End of #include "cipher_common.h" ***/ @@ -253360,6 +265396,8 @@ wx_sqlite3mcConvertHex2Bin(const unsigned char* hex, int len, unsigned char* bin /* --- AES 128-bit cipher (wxSQLite3) --- */ #if HAVE_CIPHER_AES_128_CBC +#define CIPHER_NAME_AES128 "aes128cbc" + /* ** Configuration parameters for "aes128cbc" ** @@ -253410,7 +265448,7 @@ AllocateAES128Cipher(wx_sqlite3* db) } if (aesCipher != NULL) { - CipherParams* cipherParams = wx_sqlite3mcGetCipherParams(db, CODEC_TYPE_AES128); + CipherParams* cipherParams = wx_sqlite3mcGetCipherParams(db, CIPHER_NAME_AES128); aesCipher->m_legacy = wx_sqlite3mcGetCipherParameter(cipherParams, "legacy"); aesCipher->m_legacyPageSize = wx_sqlite3mcGetCipherParameter(cipherParams, "legacy_page_size"); } @@ -253614,16 +265652,17 @@ DecryptPageAES128Cipher(void* cipher, int page, unsigned char* data, int len, in SQLITE_PRIVATE const CipherDescriptor mcAES128Descriptor = { - "aes128cbc", AllocateAES128Cipher, - FreeAES128Cipher, - CloneAES128Cipher, - GetLegacyAES128Cipher, - GetPageSizeAES128Cipher, - GetReservedAES128Cipher, - GetSaltAES128Cipher, - GenerateKeyAES128Cipher, - EncryptPageAES128Cipher, - DecryptPageAES128Cipher + CIPHER_NAME_AES128, + AllocateAES128Cipher, + FreeAES128Cipher, + CloneAES128Cipher, + GetLegacyAES128Cipher, + GetPageSizeAES128Cipher, + GetReservedAES128Cipher, + GetSaltAES128Cipher, + GenerateKeyAES128Cipher, + EncryptPageAES128Cipher, + DecryptPageAES128Cipher }; #endif /*** End of #include "cipher_wxaes128.c" ***/ @@ -253645,6 +265684,8 @@ SQLITE_PRIVATE const CipherDescriptor mcAES128Descriptor = /* --- AES 256-bit cipher (wxSQLite3) --- */ #if HAVE_CIPHER_AES_256_CBC +#define CIPHER_NAME_AES256 "aes256cbc" + /* ** Configuration parameters for "aes256cbc" ** @@ -253699,7 +265740,7 @@ AllocateAES256Cipher(wx_sqlite3* db) } if (aesCipher != NULL) { - CipherParams* cipherParams = wx_sqlite3mcGetCipherParams(db, CODEC_TYPE_AES256); + CipherParams* cipherParams = wx_sqlite3mcGetCipherParams(db, CIPHER_NAME_AES256); aesCipher->m_legacy = wx_sqlite3mcGetCipherParameter(cipherParams, "legacy"); aesCipher->m_legacyPageSize = wx_sqlite3mcGetCipherParameter(cipherParams, "legacy_page_size"); aesCipher->m_kdfIter = wx_sqlite3mcGetCipherParameter(cipherParams, "kdf_iter"); @@ -253867,16 +265908,17 @@ DecryptPageAES256Cipher(void* cipher, int page, unsigned char* data, int len, in SQLITE_PRIVATE const CipherDescriptor mcAES256Descriptor = { - "aes256cbc", AllocateAES256Cipher, - FreeAES256Cipher, - CloneAES256Cipher, - GetLegacyAES256Cipher, - GetPageSizeAES256Cipher, - GetReservedAES256Cipher, - GetSaltAES256Cipher, - GenerateKeyAES256Cipher, - EncryptPageAES256Cipher, - DecryptPageAES256Cipher + CIPHER_NAME_AES256, + AllocateAES256Cipher, + FreeAES256Cipher, + CloneAES256Cipher, + GetLegacyAES256Cipher, + GetPageSizeAES256Cipher, + GetReservedAES256Cipher, + GetSaltAES256Cipher, + GenerateKeyAES256Cipher, + EncryptPageAES256Cipher, + DecryptPageAES256Cipher }; #endif /*** End of #include "cipher_wxaes256.c" ***/ @@ -253898,6 +265940,8 @@ SQLITE_PRIVATE const CipherDescriptor mcAES256Descriptor = /* --- ChaCha20-Poly1305 cipher (plus sqleet variant) --- */ #if HAVE_CIPHER_CHACHA20 +#define CIPHER_NAME_CHACHA20 "chacha20" + /* ** Configuration parameters for "chacha20" ** @@ -253954,7 +265998,7 @@ AllocateChaCha20Cipher(wx_sqlite3* db) } if (chacha20Cipher != NULL) { - CipherParams* cipherParams = wx_sqlite3mcGetCipherParams(db, CODEC_TYPE_CHACHA20); + CipherParams* cipherParams = wx_sqlite3mcGetCipherParams(db, CIPHER_NAME_CHACHA20); chacha20Cipher->m_legacy = wx_sqlite3mcGetCipherParameter(cipherParams, "legacy"); chacha20Cipher->m_legacyPageSize = wx_sqlite3mcGetCipherParameter(cipherParams, "legacy_page_size"); chacha20Cipher->m_kdfIter = wx_sqlite3mcGetCipherParameter(cipherParams, "kdf_iter"); @@ -254250,16 +266294,17 @@ DecryptPageChaCha20Cipher(void* cipher, int page, unsigned char* data, int len, SQLITE_PRIVATE const CipherDescriptor mcChaCha20Descriptor = { - "chacha20", AllocateChaCha20Cipher, - FreeChaCha20Cipher, - CloneChaCha20Cipher, - GetLegacyChaCha20Cipher, - GetPageSizeChaCha20Cipher, - GetReservedChaCha20Cipher, - GetSaltChaCha20Cipher, - GenerateKeyChaCha20Cipher, - EncryptPageChaCha20Cipher, - DecryptPageChaCha20Cipher + CIPHER_NAME_CHACHA20, + AllocateChaCha20Cipher, + FreeChaCha20Cipher, + CloneChaCha20Cipher, + GetLegacyChaCha20Cipher, + GetPageSizeChaCha20Cipher, + GetReservedChaCha20Cipher, + GetSaltChaCha20Cipher, + GenerateKeyChaCha20Cipher, + EncryptPageChaCha20Cipher, + DecryptPageChaCha20Cipher }; #endif /*** End of #include "cipher_chacha20.c" ***/ @@ -254281,6 +266326,8 @@ SQLITE_PRIVATE const CipherDescriptor mcChaCha20Descriptor = /* --- SQLCipher AES256CBC-HMAC cipher --- */ #if HAVE_CIPHER_SQLCIPHER +#define CIPHER_NAME_SQLCIPHER "sqlcipher" + /* ** Configuration parameters for "sqlcipher" ** @@ -254396,7 +266443,7 @@ AllocateSQLCipherCipher(wx_sqlite3* db) } if (sqlCipherCipher != NULL) { - CipherParams* cipherParams = wx_sqlite3mcGetCipherParams(db, CODEC_TYPE_SQLCIPHER); + CipherParams* cipherParams = wx_sqlite3mcGetCipherParams(db, CIPHER_NAME_SQLCIPHER); sqlCipherCipher->m_legacy = wx_sqlite3mcGetCipherParameter(cipherParams, "legacy"); sqlCipherCipher->m_legacyPageSize = wx_sqlite3mcGetCipherParameter(cipherParams, "legacy_page_size"); sqlCipherCipher->m_kdfIter = wx_sqlite3mcGetCipherParameter(cipherParams, "kdf_iter"); @@ -254769,16 +266816,17 @@ DecryptPageSQLCipherCipher(void* cipher, int page, unsigned char* data, int len, } SQLITE_PRIVATE const CipherDescriptor mcSQLCipherDescriptor = { - "sqlcipher", AllocateSQLCipherCipher, - FreeSQLCipherCipher, - CloneSQLCipherCipher, - GetLegacySQLCipherCipher, - GetPageSizeSQLCipherCipher, - GetReservedSQLCipherCipher, - GetSaltSQLCipherCipher, - GenerateKeySQLCipherCipher, - EncryptPageSQLCipherCipher, - DecryptPageSQLCipherCipher + CIPHER_NAME_SQLCIPHER, + AllocateSQLCipherCipher, + FreeSQLCipherCipher, + CloneSQLCipherCipher, + GetLegacySQLCipherCipher, + GetPageSizeSQLCipherCipher, + GetReservedSQLCipherCipher, + GetSaltSQLCipherCipher, + GenerateKeySQLCipherCipher, + EncryptPageSQLCipherCipher, + DecryptPageSQLCipherCipher }; #endif /*** End of #include "cipher_sqlcipher.c" ***/ @@ -254800,6 +266848,8 @@ SQLITE_PRIVATE const CipherDescriptor mcSQLCipherDescriptor = /* --- RC4 cipher (System.Data.SQLite) --- */ #if HAVE_CIPHER_RC4 +#define CIPHER_NAME_RC4 "rc4" + /* ** Configuration parameters for "rc4" ** @@ -254840,7 +266890,7 @@ AllocateRC4Cipher(wx_sqlite3* db) } if (rc4Cipher != NULL) { - CipherParams* cipherParams = wx_sqlite3mcGetCipherParams(db, CODEC_TYPE_RC4); + CipherParams* cipherParams = wx_sqlite3mcGetCipherParams(db, CIPHER_NAME_RC4); rc4Cipher->m_legacy = wx_sqlite3mcGetCipherParameter(cipherParams, "legacy"); rc4Cipher->m_legacyPageSize = wx_sqlite3mcGetCipherParameter(cipherParams, "legacy_page_size"); } @@ -254943,16 +266993,17 @@ DecryptPageRC4Cipher(void* cipher, int page, unsigned char* data, int len, int r SQLITE_PRIVATE const CipherDescriptor mcRC4Descriptor = { - "rc4", AllocateRC4Cipher, - FreeRC4Cipher, - CloneRC4Cipher, - GetLegacyRC4Cipher, - GetPageSizeRC4Cipher, - GetReservedRC4Cipher, - GetSaltRC4Cipher, - GenerateKeyRC4Cipher, - EncryptPageRC4Cipher, - DecryptPageRC4Cipher + CIPHER_NAME_RC4, + AllocateRC4Cipher, + FreeRC4Cipher, + CloneRC4Cipher, + GetLegacyRC4Cipher, + GetPageSizeRC4Cipher, + GetReservedRC4Cipher, + GetSaltRC4Cipher, + GenerateKeyRC4Cipher, + EncryptPageRC4Cipher, + DecryptPageRC4Cipher }; #endif /*** End of #include "cipher_sds_rc4.c" ***/ @@ -254964,7 +267015,7 @@ SQLITE_PRIVATE const CipherDescriptor mcRC4Descriptor = ** Purpose: Implementation of SQLite codecs ** Author: Ulrich Telle ** Created: 2020-02-02 -** Copyright: (c) 2006-2020 Ulrich Telle +** Copyright: (c) 2006-2022 Ulrich Telle ** License: MIT */ @@ -254985,20 +267036,26 @@ static unsigned char padding[] = static CipherParams commonParams[] = { - { "cipher", CODEC_TYPE, CODEC_TYPE, 1, CODEC_TYPE_MAX }, - { "hmac_check", 1, 1, 0, 1 }, + { "cipher", CODEC_TYPE_UNKNOWN, CODEC_TYPE_UNKNOWN, 1, CODEC_COUNT_MAX }, + { "hmac_check", 1, 1, 0, 1 }, + { "mc_legacy_wal", SQLITE3MC_LEGACY_WAL, SQLITE3MC_LEGACY_WAL, 0, 1 }, CIPHER_PARAMS_SENTINEL }; +#define CIPHER_NAME_GLOBAL "global" + +static CodecParameter globalCommonParams = { CIPHER_NAME_GLOBAL, CODEC_TYPE_UNKNOWN, commonParams }; +static CodecParameter globalSentinelParams = { "", CODEC_TYPE_UNKNOWN, NULL }; + SQLITE_PRIVATE int wx_sqlite3mcGetCipherParameter(CipherParams* cipherParams, const char* paramName) { int value = -1; - for (; strlen(cipherParams->m_name) > 0; ++cipherParams) + for (; cipherParams->m_name[0] != 0; ++cipherParams) { if (wx_sqlite3_stricmp(paramName, cipherParams->m_name) == 0) break; } - if (strlen(cipherParams->m_name) > 0) + if (cipherParams->m_name[0] != 0) { value = cipherParams->m_value; cipherParams->m_value = cipherParams->m_default; @@ -255006,26 +267063,15 @@ wx_sqlite3mcGetCipherParameter(CipherParams* cipherParams, const char* paramName return value; } -static CodecParameter globalCodecParameterTable[] = +typedef struct _CipherName { - { "global", CODEC_TYPE_UNKNOWN, commonParams }, -#if HAVE_CIPHER_AES_128_CBC - { "aes128cbc", CODEC_TYPE_AES128, mcAES128Params }, -#endif -#if HAVE_CIPHER_AES_256_CBC - { "aes256cbc", CODEC_TYPE_AES256, mcAES256Params }, -#endif -#if HAVE_CIPHER_CHACHA20 - { "chacha20", CODEC_TYPE_CHACHA20, mcChaCha20Params }, -#endif -#if HAVE_CIPHER_SQLCIPHER - { "sqlcipher", CODEC_TYPE_SQLCIPHER, mcSQLCipherParams }, -#endif -#if HAVE_CIPHER_RC4 - { "rc4", CODEC_TYPE_RC4, mcRC4Params }, -#endif - { "", CODEC_TYPE_UNKNOWN, NULL } -}; + char m_name[CIPHER_NAME_MAXLEN]; +} CipherName; + +static int globalCipherCount = 0; +static char* globalSentinelName = ""; +static CipherName globalCipherNameTable[CODEC_COUNT_LIMIT + 2] = { 0 }; +static CodecParameter globalCodecParameterTable[CODEC_COUNT_LIMIT + 2]; SQLITE_PRIVATE CodecParameter* wx_sqlite3mcCloneCodecParameterTable() @@ -255037,10 +267083,10 @@ wx_sqlite3mcCloneCodecParameterTable() CipherParams* cloneCipherParams; CodecParameter* cloneCodecParams; - for (j = 0; strlen(globalCodecParameterTable[j].m_name) > 0; ++j) + for (j = 0; globalCodecParameterTable[j].m_name[0] != 0; ++j) { CipherParams* params = globalCodecParameterTable[j].m_params; - for (k = 0; strlen(params[k].m_name) > 0; ++k); + for (k = 0; params[k].m_name[0] != 0; ++k); nParams += k; } nTables = j; @@ -255057,8 +267103,9 @@ wx_sqlite3mcCloneCodecParameterTable() { CipherParams* params = globalCodecParameterTable[j].m_params; cloneCodecParams[j].m_name = globalCodecParameterTable[j].m_name; + cloneCodecParams[j].m_id = globalCodecParameterTable[j].m_id; cloneCodecParams[j].m_params = &cloneCipherParams[offset]; - for (n = 0; strlen(params[n].m_name) > 0; ++n); + for (n = 0; params[n].m_name[0] != 0; ++n); /* Copy all parameters of the current table (including sentinel) */ for (k = 0; k <= n; ++k) { @@ -255071,6 +267118,7 @@ wx_sqlite3mcCloneCodecParameterTable() offset += (n + 1); } cloneCodecParams[nTables].m_name = globalCodecParameterTable[nTables].m_name; + cloneCodecParams[nTables].m_id = globalCodecParameterTable[nTables].m_id; cloneCodecParams[nTables].m_params = NULL; } else @@ -255097,35 +267145,7 @@ static const CipherDescriptor mcDummyDescriptor = "@dummy@", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -static const CipherDescriptor* codecDescriptorTable[] = -{ -#if HAVE_CIPHER_AES_128_CBC - &mcAES128Descriptor, -#else - &mcDummyDescriptor, -#endif -#if HAVE_CIPHER_AES_256_CBC - &mcAES256Descriptor, -#else - &mcDummyDescriptor, -#endif -#if HAVE_CIPHER_CHACHA20 - &mcChaCha20Descriptor, -#else - &mcDummyDescriptor, -#endif -#if HAVE_CIPHER_SQLCIPHER - &mcSQLCipherDescriptor, -#else - &mcDummyDescriptor, -#endif -#if HAVE_CIPHER_RC4 - &mcRC4Descriptor, -#else - &mcDummyDescriptor, -#endif - &mcSentinelDescriptor -}; +static CipherDescriptor globalCodecDescriptorTable[CODEC_COUNT_MAX + 1]; /* --- Codec --- */ @@ -255139,11 +267159,11 @@ wx_sqlite3mcGetCipherType(wx_sqlite3* db) CipherParams* cipherParamTable = (codecParams != NULL) ? codecParams[0].m_params : commonParams; int cipherType = CODEC_TYPE; CipherParams* cipher = cipherParamTable; - for (; strlen(cipher->m_name) > 0; ++cipher) + for (; cipher->m_name[0] != 0; ++cipher) { if (wx_sqlite3_stricmp("cipher", cipher->m_name) == 0) break; } - if (strlen(cipher->m_name) > 0) + if (cipher->m_name[0] != 0) { cipherType = cipher->m_value; cipher->m_value = cipher->m_default; @@ -255152,10 +267172,23 @@ wx_sqlite3mcGetCipherType(wx_sqlite3* db) } SQLITE_PRIVATE CipherParams* -wx_sqlite3mcGetCipherParams(wx_sqlite3* db, int cypherType) +wx_sqlite3mcGetCipherParams(wx_sqlite3* db, const char* cipherName) { + int j = 0; + int cipherType = wx_sqlite3mc_cipher_index(cipherName); CodecParameter* codecParams = (db != NULL) ? wx_sqlite3mcGetCodecParams(db) : globalCodecParameterTable; - CipherParams* cipherParamTable = (codecParams != NULL) ? codecParams[cypherType].m_params : globalCodecParameterTable[cypherType].m_params; + if (codecParams == NULL) + { + codecParams = globalCodecParameterTable; + } + if (cipherType > 0) + { + for (j = 1; codecParams[j].m_id > 0; ++j) + { + if (cipherType == codecParams[j].m_id) break; + } + } + CipherParams* cipherParamTable = codecParams[j].m_params; return cipherParamTable; } @@ -255167,6 +267200,7 @@ wx_sqlite3mcCodecInit(Codec* codec) { codec->m_isEncrypted = 0; codec->m_hmacCheck = 1; + codec->m_walLegacy = 0; codec->m_hasReadCipher = 0; codec->m_readCipherType = CODEC_TYPE_UNKNOWN; @@ -255201,12 +267235,12 @@ wx_sqlite3mcCodecTerm(Codec* codec) { if (codec->m_readCipher != NULL) { - codecDescriptorTable[codec->m_readCipherType - 1]->m_freeCipher(codec->m_readCipher); + globalCodecDescriptorTable[codec->m_readCipherType - 1].m_freeCipher(codec->m_readCipher); codec->m_readCipher = NULL; } if (codec->m_writeCipher != NULL) { - codecDescriptorTable[codec->m_writeCipherType - 1]->m_freeCipher(codec->m_writeCipher); + globalCodecDescriptorTable[codec->m_writeCipherType - 1].m_freeCipher(codec->m_writeCipher); codec->m_writeCipher = NULL; } memset(codec, 0, sizeof(Codec)); @@ -255223,13 +267257,14 @@ SQLITE_PRIVATE int wx_sqlite3mcCodecSetup(Codec* codec, int cipherType, char* userPassword, int passwordLength) { int rc = SQLITE_OK; - CipherParams* globalParams = wx_sqlite3mcGetCipherParams(codec->m_db, 0); + CipherParams* globalParams = wx_sqlite3mcGetCipherParams(codec->m_db, CIPHER_NAME_GLOBAL); codec->m_isEncrypted = 1; codec->m_hmacCheck = wx_sqlite3mcGetCipherParameter(globalParams, "hmac_check"); + codec->m_walLegacy = wx_sqlite3mcGetCipherParameter(globalParams, "mc_legacy_wal"); codec->m_hasReadCipher = 1; codec->m_hasWriteCipher = 1; codec->m_readCipherType = cipherType; - codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType-1]->m_allocateCipher(codec->m_db); + codec->m_readCipher = globalCodecDescriptorTable[codec->m_readCipherType-1].m_allocateCipher(codec->m_db); if (codec->m_readCipher != NULL) { unsigned char* keySalt = (codec->m_hasKeySalt != 0) ? codec->m_keySalt : NULL; @@ -255247,16 +267282,17 @@ SQLITE_PRIVATE int wx_sqlite3mcSetupWriteCipher(Codec* codec, int cipherType, char* userPassword, int passwordLength) { int rc = SQLITE_OK; - CipherParams* globalParams = wx_sqlite3mcGetCipherParams(codec->m_db, 0); + CipherParams* globalParams = wx_sqlite3mcGetCipherParams(codec->m_db, CIPHER_NAME_GLOBAL); if (codec->m_writeCipher != NULL) { - codecDescriptorTable[codec->m_writeCipherType-1]->m_freeCipher(codec->m_writeCipher); + globalCodecDescriptorTable[codec->m_writeCipherType-1].m_freeCipher(codec->m_writeCipher); } codec->m_isEncrypted = 1; codec->m_hmacCheck = wx_sqlite3mcGetCipherParameter(globalParams, "hmac_check"); + codec->m_walLegacy = wx_sqlite3mcGetCipherParameter(globalParams, "mc_legacy_wal"); codec->m_hasWriteCipher = 1; codec->m_writeCipherType = cipherType; - codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType-1]->m_allocateCipher(codec->m_db); + codec->m_writeCipher = globalCodecDescriptorTable[codec->m_writeCipherType-1].m_allocateCipher(codec->m_db); if (codec->m_writeCipher != NULL) { unsigned char* keySalt = (codec->m_hasKeySalt != 0) ? codec->m_keySalt : NULL; @@ -255377,57 +267413,57 @@ wx_sqlite3mcGetPageBuffer(Codec* codec) SQLITE_PRIVATE int wx_sqlite3mcGetLegacyReadCipher(Codec* codec) { - int legacy = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType - 1]->m_getLegacy(codec->m_readCipher) : 0; + int legacy = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? globalCodecDescriptorTable[codec->m_readCipherType - 1].m_getLegacy(codec->m_readCipher) : 0; return legacy; } SQLITE_PRIVATE int wx_sqlite3mcGetLegacyWriteCipher(Codec* codec) { - int legacy = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType - 1]->m_getLegacy(codec->m_writeCipher) : -1; + int legacy = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? globalCodecDescriptorTable[codec->m_writeCipherType - 1].m_getLegacy(codec->m_writeCipher) : -1; return legacy; } SQLITE_PRIVATE int wx_sqlite3mcGetPageSizeReadCipher(Codec* codec) { - int pageSize = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType - 1]->m_getPageSize(codec->m_readCipher) : 0; + int pageSize = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? globalCodecDescriptorTable[codec->m_readCipherType - 1].m_getPageSize(codec->m_readCipher) : 0; return pageSize; } SQLITE_PRIVATE int wx_sqlite3mcGetPageSizeWriteCipher(Codec* codec) { - int pageSize = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType - 1]->m_getPageSize(codec->m_writeCipher) : -1; + int pageSize = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? globalCodecDescriptorTable[codec->m_writeCipherType - 1].m_getPageSize(codec->m_writeCipher) : -1; return pageSize; } SQLITE_PRIVATE int wx_sqlite3mcGetReservedReadCipher(Codec* codec) { - int reserved = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType-1]->m_getReserved(codec->m_readCipher) : -1; + int reserved = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? globalCodecDescriptorTable[codec->m_readCipherType-1].m_getReserved(codec->m_readCipher) : -1; return reserved; } SQLITE_PRIVATE int wx_sqlite3mcGetReservedWriteCipher(Codec* codec) { - int reserved = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType-1]->m_getReserved(codec->m_writeCipher) : -1; + int reserved = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? globalCodecDescriptorTable[codec->m_writeCipherType-1].m_getReserved(codec->m_writeCipher) : -1; return reserved; } SQLITE_PRIVATE int wx_sqlite3mcReservedEqual(Codec* codec) { - int readReserved = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType-1]->m_getReserved(codec->m_readCipher) : -1; - int writeReserved = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType-1]->m_getReserved(codec->m_writeCipher) : -1; + int readReserved = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? globalCodecDescriptorTable[codec->m_readCipherType-1].m_getReserved(codec->m_readCipher) : -1; + int writeReserved = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? globalCodecDescriptorTable[codec->m_writeCipherType-1].m_getReserved(codec->m_writeCipher) : -1; return (readReserved == writeReserved); } SQLITE_PRIVATE unsigned char* wx_sqlite3mcGetSaltWriteCipher(Codec* codec) { - unsigned char* salt = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType - 1]->m_getSalt(codec->m_writeCipher) : NULL; + unsigned char* salt = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? globalCodecDescriptorTable[codec->m_writeCipherType - 1].m_getSalt(codec->m_writeCipher) : NULL; return salt; } @@ -255437,6 +267473,7 @@ wx_sqlite3mcCodecCopy(Codec* codec, Codec* other) int rc = SQLITE_OK; codec->m_isEncrypted = other->m_isEncrypted; codec->m_hmacCheck = other->m_hmacCheck; + codec->m_walLegacy = other->m_walLegacy; codec->m_hasReadCipher = other->m_hasReadCipher; codec->m_hasWriteCipher = other->m_hasWriteCipher; codec->m_readCipherType = other->m_readCipherType; @@ -255448,10 +267485,10 @@ wx_sqlite3mcCodecCopy(Codec* codec, Codec* other) if (codec->m_hasReadCipher) { - codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType - 1]->m_allocateCipher(codec->m_db); + codec->m_readCipher = globalCodecDescriptorTable[codec->m_readCipherType - 1].m_allocateCipher(codec->m_db); if (codec->m_readCipher != NULL) { - codecDescriptorTable[codec->m_readCipherType - 1]->m_cloneCipher(codec->m_readCipher, other->m_readCipher); + globalCodecDescriptorTable[codec->m_readCipherType - 1].m_cloneCipher(codec->m_readCipher, other->m_readCipher); } else { @@ -255461,10 +267498,10 @@ wx_sqlite3mcCodecCopy(Codec* codec, Codec* other) if (codec->m_hasWriteCipher) { - codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType - 1]->m_allocateCipher(codec->m_db); + codec->m_writeCipher = globalCodecDescriptorTable[codec->m_writeCipherType - 1].m_allocateCipher(codec->m_db); if (codec->m_writeCipher != NULL) { - codecDescriptorTable[codec->m_writeCipherType - 1]->m_cloneCipher(codec->m_writeCipher, other->m_writeCipher); + globalCodecDescriptorTable[codec->m_writeCipherType - 1].m_cloneCipher(codec->m_writeCipher, other->m_writeCipher); } else { @@ -255487,17 +267524,17 @@ wx_sqlite3mcCopyCipher(Codec* codec, int read2write) { if (codec->m_writeCipher != NULL && codec->m_writeCipherType != codec->m_readCipherType) { - codecDescriptorTable[codec->m_writeCipherType-1]->m_freeCipher(codec->m_writeCipher); + globalCodecDescriptorTable[codec->m_writeCipherType-1].m_freeCipher(codec->m_writeCipher); codec->m_writeCipher = NULL; } if (codec->m_writeCipher == NULL) { codec->m_writeCipherType = codec->m_readCipherType; - codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType-1]->m_allocateCipher(codec->m_db); + codec->m_writeCipher = globalCodecDescriptorTable[codec->m_writeCipherType-1].m_allocateCipher(codec->m_db); } if (codec->m_writeCipher != NULL) { - codecDescriptorTable[codec->m_writeCipherType-1]->m_cloneCipher(codec->m_writeCipher, codec->m_readCipher); + globalCodecDescriptorTable[codec->m_writeCipherType-1].m_cloneCipher(codec->m_writeCipher, codec->m_readCipher); } else { @@ -255508,17 +267545,17 @@ wx_sqlite3mcCopyCipher(Codec* codec, int read2write) { if (codec->m_readCipher != NULL && codec->m_readCipherType != codec->m_writeCipherType) { - codecDescriptorTable[codec->m_readCipherType-1]->m_freeCipher(codec->m_readCipher); + globalCodecDescriptorTable[codec->m_readCipherType-1].m_freeCipher(codec->m_readCipher); codec->m_readCipher = NULL; } if (codec->m_readCipher == NULL) { codec->m_readCipherType = codec->m_writeCipherType; - codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType-1]->m_allocateCipher(codec->m_db); + codec->m_readCipher = globalCodecDescriptorTable[codec->m_readCipherType-1].m_allocateCipher(codec->m_db); } if (codec->m_readCipher != NULL) { - codecDescriptorTable[codec->m_readCipherType-1]->m_cloneCipher(codec->m_readCipher, codec->m_writeCipher); + globalCodecDescriptorTable[codec->m_readCipherType-1].m_cloneCipher(codec->m_readCipher, codec->m_writeCipher); } else { @@ -255549,13 +267586,13 @@ wx_sqlite3mcPadPassword(char* password, int pswdlen, unsigned char pswd[32]) SQLITE_PRIVATE void wx_sqlite3mcGenerateReadKey(Codec* codec, char* userPassword, int passwordLength, unsigned char* cipherSalt) { - codecDescriptorTable[codec->m_readCipherType-1]->m_generateKey(codec->m_readCipher, codec->m_btShared, userPassword, passwordLength, 0, cipherSalt); + globalCodecDescriptorTable[codec->m_readCipherType-1].m_generateKey(codec->m_readCipher, codec->m_btShared, userPassword, passwordLength, 0, cipherSalt); } SQLITE_PRIVATE void wx_sqlite3mcGenerateWriteKey(Codec* codec, char* userPassword, int passwordLength, unsigned char* cipherSalt) { - codecDescriptorTable[codec->m_writeCipherType-1]->m_generateKey(codec->m_writeCipher, codec->m_btShared, userPassword, passwordLength, 1, cipherSalt); + globalCodecDescriptorTable[codec->m_writeCipherType-1].m_generateKey(codec->m_writeCipher, codec->m_btShared, userPassword, passwordLength, 1, cipherSalt); } SQLITE_PRIVATE int @@ -255565,7 +267602,7 @@ wx_sqlite3mcEncrypt(Codec* codec, int page, unsigned char* data, int len, int us void* cipher = (useWriteKey) ? codec->m_writeCipher : codec->m_readCipher; int reserved = (useWriteKey) ? (codec->m_writeReserved >= 0) ? codec->m_writeReserved : codec->m_reserved : (codec->m_readReserved >= 0) ? codec->m_readReserved : codec->m_reserved; - return codecDescriptorTable[cipherType-1]->m_encryptPage(cipher, page, data, len, reserved); + return globalCodecDescriptorTable[cipherType-1].m_encryptPage(cipher, page, data, len, reserved); } SQLITE_PRIVATE int @@ -255574,7 +267611,7 @@ wx_sqlite3mcDecrypt(Codec* codec, int page, unsigned char* data, int len) int cipherType = codec->m_readCipherType; void* cipher = codec->m_readCipher; int reserved = (codec->m_readReserved >= 0) ? codec->m_readReserved : codec->m_reserved; - return codecDescriptorTable[cipherType-1]->m_decryptPage(cipher, page, data, len, reserved, codec->m_hmacCheck); + return globalCodecDescriptorTable[cipherType-1].m_decryptPage(cipher, page, data, len, reserved, codec->m_hmacCheck); } #if HAVE_CIPHER_SQLCIPHER @@ -255613,7 +267650,7 @@ wx_sqlite3mcConfigureSQLCipherVersion(wx_sqlite3* db, int configDefault, int leg ** Purpose: Configuration of SQLite codecs ** Author: Ulrich Telle ** Created: 2020-03-02 -** Copyright: (c) 2006-2020 Ulrich Telle +** Copyright: (c) 2006-2023 Ulrich Telle ** License: MIT */ @@ -255657,6 +267694,8 @@ SQLITE_PRIVATE int wx_sqlite3mcHandleMainKey(wx_sqlite3* db, const char* zPath); /* --- Codec --- */ +SQLITE_PRIVATE int +wx_sqlite3mcGetGlobalCipherCount(); SQLITE_PRIVATE Codec* wx_sqlite3mcGetCodec(wx_sqlite3* db, const char* zDbName); @@ -255725,30 +267764,42 @@ wx_sqlite3mc_config(wx_sqlite3* db, const char* paramName, int newValue) } param = codecParams[0].m_params; - for (; strlen(param->m_name) > 0; ++param) + for (; param->m_name[0] != 0; ++param) { if (wx_sqlite3_stricmp(paramName, param->m_name) == 0) break; } - if (strlen(param->m_name) > 0) + if (param->m_name[0] != 0) { + int cipherCount = wx_sqlite3mcGetGlobalCipherCount(); if (db != NULL) { wx_sqlite3_mutex_enter(db->mutex); } else { - wx_sqlite3_mutex_enter(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); + wx_sqlite3_mutex_enter(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MAIN)); } value = (hasDefaultPrefix) ? param->m_default : (hasMinPrefix) ? param->m_minValue : (hasMaxPrefix) ? param->m_maxValue : param->m_value; if (!hasMinPrefix && !hasMaxPrefix && newValue >= 0 && newValue >= param->m_minValue && newValue <= param->m_maxValue) { - /* Do not allow to change the default value for parameter "hmac_check" */ - if (hasDefaultPrefix && (wx_sqlite3_stricmp(paramName, "hmac_check") != 0)) + int allowChange = 1; + + /* Allow cipher change only if new cipher is actually available */ + if (wx_sqlite3_stricmp(paramName, "cipher") == 0) { - param->m_default = newValue; + allowChange = newValue > 0 && newValue <= cipherCount; + } + + if (allowChange) + { + /* Do not allow to change the default value for parameter "hmac_check" */ + if (hasDefaultPrefix && (wx_sqlite3_stricmp(paramName, "hmac_check") != 0)) + { + param->m_default = newValue; + } + param->m_value = newValue; + value = newValue; } - param->m_value = newValue; - value = newValue; } if (db != NULL) { @@ -255756,12 +267807,52 @@ wx_sqlite3mc_config(wx_sqlite3* db, const char* paramName, int newValue) } else { - wx_sqlite3_mutex_leave(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); + wx_sqlite3_mutex_leave(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MAIN)); } } return value; } +SQLITE_API int +wx_sqlite3mc_cipher_count() +{ + return wx_sqlite3mcGetGlobalCipherCount(); +} + +SQLITE_API int +wx_sqlite3mc_cipher_index(const char* cipherName) +{ + int count = wx_sqlite3mcGetGlobalCipherCount(); + int j = 0; + for (j = 0; j < count && globalCodecDescriptorTable[j].m_name[0] != 0; ++j) + { + if (wx_sqlite3_stricmp(cipherName, globalCodecDescriptorTable[j].m_name) == 0) break; + } + return (j < count && globalCodecDescriptorTable[j].m_name[0] != 0) ? j + 1 : -1; +} + +SQLITE_API const char* +wx_sqlite3mc_cipher_name(int cipherIndex) +{ + static char cipherName[CIPHER_NAME_MAXLEN] = ""; + int count = wx_sqlite3mcGetGlobalCipherCount(); + int j = 0; + cipherName[0] = '\0'; + if (cipherIndex > 0 && cipherIndex <= count) + { + for (j = 0; j < count && globalCodecDescriptorTable[j].m_name[0] != 0; ++j) + { + if (cipherIndex == j + 1) break; + } + if (j < count && globalCodecDescriptorTable[j].m_name[0] != 0) + { + strncpy(cipherName, globalCodecDescriptorTable[j].m_name, CIPHER_NAME_MAXLEN - 1); + cipherName[CIPHER_NAME_MAXLEN - 1] = '\0'; + } + } + return cipherName; +} + SQLITE_API int wx_sqlite3mc_config_cipher(wx_sqlite3* db, const char* cipherName, const char* paramName, int newValue) { @@ -255793,11 +267884,11 @@ wx_sqlite3mc_config_cipher(wx_sqlite3* db, const char* cipherName, const char* p return value; } - for (j = 0; strlen(codecParams[j].m_name) > 0; ++j) + for (j = 0; codecParams[j].m_name[0] != 0; ++j) { if (wx_sqlite3_stricmp(cipherName, codecParams[j].m_name) == 0) break; } - if (strlen(codecParams[j].m_name) > 0) + if (codecParams[j].m_name[0] != 0) { cipherParamTable = codecParams[j].m_params; } @@ -255847,11 +267938,11 @@ wx_sqlite3mc_config_cipher(wx_sqlite3* db, const char* cipherName, const char* p } #endif - for (; strlen(param->m_name) > 0; ++param) + for (; param->m_name[0] != 0; ++param) { if (wx_sqlite3_stricmp(paramName, param->m_name) == 0) break; } - if (strlen(param->m_name) > 0) + if (param->m_name[0] != 0) { if (db != NULL) { @@ -256027,11 +268118,11 @@ wx_sqlite3mcConfigParams(wx_sqlite3_context* context, int argc, wx_sqlite3_value param1 = codecParams[0].m_params; cipherParamTable = NULL; - for (; strlen(param1->m_name) > 0; ++param1) + for (; param1->m_name[0] != 0; ++param1) { if (wx_sqlite3_stricmp(nameParam1, param1->m_name) == 0) break; } - isCommonParam1 = strlen(param1->m_name) > 0; + isCommonParam1 = param1->m_name[0] != 0; /* Check first argument whether it is a cipher name, if it wasn't a common parameter */ /* If the first argument is a cipher name, cipherParamTable will point to the corresponding cipher parameter table */ @@ -256040,11 +268131,11 @@ wx_sqlite3mcConfigParams(wx_sqlite3_context* context, int argc, wx_sqlite3_value if (!hasDefaultPrefix && !hasMinPrefix && !hasMaxPrefix) { int j = 0; - for (j = 0; strlen(codecParams[j].m_name) > 0; ++j) + for (j = 0; codecParams[j].m_name[0] != 0; ++j) { if (wx_sqlite3_stricmp(nameParam1, codecParams[j].m_name) == 0) break; } - isCipherParam1 = strlen(codecParams[j].m_name) > 0; + isCipherParam1 = codecParams[j].m_name[0] != 0; if (isCipherParam1) { cipherParamTable = codecParams[j].m_params; @@ -256066,7 +268157,7 @@ wx_sqlite3mcConfigParams(wx_sqlite3_context* context, int argc, wx_sqlite3_value int value = (hasDefaultPrefix) ? param1->m_default : (hasMinPrefix) ? param1->m_minValue : (hasMaxPrefix) ? param1->m_maxValue : param1->m_value; if (wx_sqlite3_stricmp(nameParam1, "cipher") == 0) { - wx_sqlite3_result_text(context, codecDescriptorTable[value - 1]->m_name, -1, SQLITE_STATIC); + wx_sqlite3_result_text(context, globalCodecDescriptorTable[value - 1].m_name, -1, SQLITE_STATIC); } else { @@ -256079,7 +268170,7 @@ wx_sqlite3mcConfigParams(wx_sqlite3_context* context, int argc, wx_sqlite3_value int nParams = 0; int lenTotal = 0; int j; - for (j = 0; strlen(cipherParamTable[j].m_name) > 0; ++j) + for (j = 0; cipherParamTable[j].m_name[0] != 0; ++j) { ++nParams; lenTotal += (int) strlen(cipherParamTable[j].m_name); @@ -256125,18 +268216,18 @@ wx_sqlite3mcConfigParams(wx_sqlite3_context* context, int argc, wx_sqlite3_value { const char* nameCipher = (const char*)wx_sqlite3_value_text(argv[1]); int j = 0; - for (j = 0; strlen(codecDescriptorTable[j]->m_name) > 0; ++j) + for (j = 0; globalCodecDescriptorTable[j].m_name[0] != 0; ++j) { - if (wx_sqlite3_stricmp(nameCipher, codecDescriptorTable[j]->m_name) == 0) break; + if (wx_sqlite3_stricmp(nameCipher, globalCodecDescriptorTable[j].m_name) == 0) break; } - if (strlen(codecDescriptorTable[j]->m_name) > 0) + if (globalCodecDescriptorTable[j].m_name[0] != 0) { if (hasDefaultPrefix) { param1->m_default = j + 1; } param1->m_value = j + 1; - wx_sqlite3_result_text(context, codecDescriptorTable[j]->m_name, -1, SQLITE_STATIC); + wx_sqlite3_result_text(context, globalCodecDescriptorTable[j].m_name, -1, SQLITE_STATIC); } else { @@ -256198,7 +268289,7 @@ wx_sqlite3mcConfigParams(wx_sqlite3_context* context, int argc, wx_sqlite3_value hasMaxPrefix = 1; nameParam2 += 4; } - for (; strlen(param2->m_name) > 0; ++param2) + for (; param2->m_name[0] != 0; ++param2) { if (wx_sqlite3_stricmp(nameParam2, param2->m_name) == 0) break; } @@ -256221,7 +268312,7 @@ wx_sqlite3mcConfigParams(wx_sqlite3_context* context, int argc, wx_sqlite3_value } #endif - if (strlen(param2->m_name) > 0) + if (param2->m_name[0] != 0) { if (argc == 2) { @@ -256285,13 +268376,13 @@ wx_sqlite3mcConfigureFromUri(wx_sqlite3* db, const char *zDbName, int configDefa CipherParams* cipherParams = NULL; /* Try to locate the cipher name */ - for (j = 1; strlen(globalCodecParameterTable[j].m_name) > 0; ++j) + for (j = 1; globalCodecParameterTable[j].m_name[0] != 0; ++j) { if (wx_sqlite3_stricmp(cipherName, globalCodecParameterTable[j].m_name) == 0) break; } /* j is the index of the cipher name, if found */ - cipherParams = (strlen(globalCodecParameterTable[j].m_name) > 0) ? globalCodecParameterTable[j].m_params : NULL; + cipherParams = (globalCodecParameterTable[j].m_name[0] != 0) ? globalCodecParameterTable[j].m_params : NULL; if (cipherParams != NULL) { /* @@ -256301,6 +268392,7 @@ wx_sqlite3mcConfigureFromUri(wx_sqlite3* db, const char *zDbName, int configDefa int skipLegacy = 0; /* Set global parameters (cipher and hmac_check) */ int hmacCheck = wx_sqlite3_uri_boolean(dbFileName, "hmac_check", 1); + int walLegacy = wx_sqlite3_uri_boolean(dbFileName, "mc_legacy_wal", 0); if (configDefault) { wx_sqlite3mc_config(db, "default:cipher", globalCodecParameterTable[j].m_id); @@ -256313,6 +268405,7 @@ wx_sqlite3mcConfigureFromUri(wx_sqlite3* db, const char *zDbName, int configDefa { wx_sqlite3mc_config(db, "hmac_check", hmacCheck); } + wx_sqlite3mc_config(db, "mc_legacy_wal", walLegacy); #if HAVE_CIPHER_SQLCIPHER /* Special handling for SQLCipher */ @@ -256329,7 +268422,7 @@ wx_sqlite3mcConfigureFromUri(wx_sqlite3* db, const char *zDbName, int configDefa #endif /* Check all cipher specific parameters */ - for (j = 0; strlen(cipherParams[j].m_name) > 0; ++j) + for (j = 0; cipherParams[j].m_name[0] != 0; ++j) { if (skipLegacy && wx_sqlite3_stricmp(cipherParams[j].m_name, "legacy") == 0) continue; @@ -256401,30 +268494,32 @@ wx_sqlite3mcFileControlPragma(wx_sqlite3* db, const char* zDbName, int op, void* pragmaValue = ((char**) pArg)[2]; if (wx_sqlite3StrICmp(pragmaName, "cipher") == 0) { - int j = -1; + int cipherId = -1; if (pragmaValue != NULL) { + int j = 1; /* Try to locate the cipher name */ - for (j = 1; strlen(globalCodecParameterTable[j].m_name) > 0; ++j) + for (j = 1; globalCodecParameterTable[j].m_name[0] != 0; ++j) { if (wx_sqlite3_stricmp(pragmaValue, globalCodecParameterTable[j].m_name) == 0) break; } + cipherId = (globalCodecParameterTable[j].m_name[0] != 0) ? globalCodecParameterTable[j].m_id : CODEC_TYPE_UNKNOWN; } - /* j is the index of the cipher name, if found */ - if ((j == -1) || (strlen(globalCodecParameterTable[j].m_name) > 0)) + /* cipherId is the numeric id of the cipher name, if found */ + if ((cipherId == -1) || (cipherId > 0 && cipherId <= CODEC_COUNT_MAX)) { int value; if (configDefault) { - value = wx_sqlite3mc_config(db, "default:cipher", j); + value = wx_sqlite3mc_config(db, "default:cipher", cipherId); } else { - value = wx_sqlite3mc_config(db, "cipher", j); + value = wx_sqlite3mc_config(db, "cipher", cipherId); } rc = SQLITE_OK; - ((char**)pArg)[0] = wx_sqlite3_mprintf("%s", codecDescriptorTable[value - 1]->m_name); + ((char**)pArg)[0] = wx_sqlite3_mprintf("%s", globalCodecDescriptorTable[value - 1].m_name); } else { @@ -256439,6 +268534,13 @@ wx_sqlite3mcFileControlPragma(wx_sqlite3* db, const char* zDbName, int op, void* ((char**)pArg)[0] = wx_sqlite3_mprintf("%d", value); rc = SQLITE_OK; } + else if (wx_sqlite3StrICmp(pragmaName, "mc_legacy_wal") == 0) + { + int walLegacy = (pragmaValue != NULL) ? wx_sqlite3GetBoolean(pragmaValue, 0) : -1; + int value = wx_sqlite3mc_config(db, "mc_legacy_wal", walLegacy); + ((char**)pArg)[0] = wx_sqlite3_mprintf("%d", value); + rc = SQLITE_OK; + } else if (wx_sqlite3StrICmp(pragmaName, "key") == 0) { rc = wx_sqlite3_key_v2(db, zDbName, pragmaValue, -1); @@ -256446,6 +268548,48 @@ wx_sqlite3mcFileControlPragma(wx_sqlite3* db, const char* zDbName, int op, void* { ((char**)pArg)[0] = wx_sqlite3_mprintf("ok"); } + else + { + if (db->pErr) + { + const char* z = (const char*)wx_sqlite3_value_text(db->pErr); + if (z && wx_sqlite3Strlen30(z) > 0) + { + ((char**)pArg)[0] = wx_sqlite3_mprintf(z); + } + } + } + } + else if (wx_sqlite3StrICmp(pragmaName, "hexkey") == 0) + { + int nValue = wx_sqlite3Strlen30(pragmaValue); + if (((nValue & 1) == 0) && (wx_sqlite3mcIsHexKey((const unsigned char*) pragmaValue, nValue) != 0)) + { + unsigned char* zHexKey = wx_sqlite3_malloc(nValue/2); + wx_sqlite3mcConvertHex2Bin((const unsigned char*) pragmaValue, nValue, zHexKey); + rc = wx_sqlite3_key_v2(db, zDbName, zHexKey, nValue/2); + wx_sqlite3_free(zHexKey); + if (rc == SQLITE_OK) + { + ((char**)pArg)[0] = wx_sqlite3_mprintf("ok"); + } + else + { + if (db->pErr) + { + const char* z = (const char*)wx_sqlite3_value_text(db->pErr); + if (z && wx_sqlite3Strlen30(z) > 0) + { + ((char**)pArg)[0] = wx_sqlite3_mprintf(z); + } + } + } + } + else + { + rc = SQLITE_ERROR; + ((char**)pArg)[0] = wx_sqlite3_mprintf("Malformed hex string"); + } } else if (wx_sqlite3StrICmp(pragmaName, "rekey") == 0) { @@ -256454,6 +268598,48 @@ wx_sqlite3mcFileControlPragma(wx_sqlite3* db, const char* zDbName, int op, void* { ((char**)pArg)[0] = wx_sqlite3_mprintf("ok"); } + else + { + if (db->pErr) + { + const char* z = (const char*) wx_sqlite3_value_text(db->pErr); + if (z && wx_sqlite3Strlen30(z) > 0) + { + ((char**)pArg)[0] = wx_sqlite3_mprintf(z); + } + } + } + } + else if (wx_sqlite3StrICmp(pragmaName, "hexrekey") == 0) + { + int nValue = wx_sqlite3Strlen30(pragmaValue); + if (((nValue & 1) == 0) && (wx_sqlite3mcIsHexKey((const unsigned char*) pragmaValue, nValue) != 0)) + { + unsigned char* zHexKey = wx_sqlite3_malloc(nValue/2); + wx_sqlite3mcConvertHex2Bin((const unsigned char*) pragmaValue, nValue, zHexKey); + rc = wx_sqlite3_rekey_v2(db, zDbName, zHexKey, nValue/2); + wx_sqlite3_free(zHexKey); + if (rc == SQLITE_OK) + { + ((char**)pArg)[0] = wx_sqlite3_mprintf("ok"); + } + else + { + if (db->pErr) + { + const char* z = (const char*)wx_sqlite3_value_text(db->pErr); + if (z && wx_sqlite3Strlen30(z) > 0) + { + ((char**)pArg)[0] = wx_sqlite3_mprintf(z); + } + } + } + } + else + { + rc = SQLITE_ERROR; + ((char**)pArg)[0] = wx_sqlite3_mprintf("Malformed hex string"); + } } else { @@ -256466,22 +268652,21 @@ wx_sqlite3mcFileControlPragma(wx_sqlite3* db, const char* zDbName, int op, void* CipherParams* cipherParams = NULL; /* Try to locate the cipher name */ - for (j = 1; strlen(globalCodecParameterTable[j].m_name) > 0; ++j) + for (j = 1; globalCodecParameterTable[j].m_name[0] != 0; ++j) { if (cipher == globalCodecParameterTable[j].m_id) break; } /* j is the index of the cipher name, if found */ - cipherParams = (strlen(globalCodecParameterTable[j].m_name) > 0) ? globalCodecParameterTable[j].m_params : NULL; + cipherParams = (globalCodecParameterTable[j].m_name[0] != 0) ? globalCodecParameterTable[j].m_params : NULL; if (cipherParams != NULL) { const char* cipherName = globalCodecParameterTable[j].m_name; - int j; - for (j = 0; strlen(cipherParams[j].m_name) > 0; ++j) + for (j = 0; cipherParams[j].m_name[0] != 0; ++j) { if (wx_sqlite3_stricmp(pragmaName, cipherParams[j].m_name) == 0) break; } - if (strlen(cipherParams[j].m_name) > 0) + if (cipherParams[j].m_name[0] != 0) { char* param = (configDefault) ? wx_sqlite3_mprintf("default:%s", pragmaName) : pragmaName; if (isIntValue) @@ -256520,13 +268705,15 @@ wx_sqlite3mcCodecQueryParameters(wx_sqlite3* db, const char* zDb, const char* zU { u8 iByte; int i; - char zDecoded[40]; - for (i = 0, iByte = 0; i < sizeof(zDecoded) * 2 && wx_sqlite3Isxdigit(zKey[i]); i++) + int nKey = wx_sqlite3Strlen30(zKey); + char* zDecoded = wx_sqlite3_malloc(nKey); + for (i = 0, iByte = 0; i < nKey && wx_sqlite3Isxdigit(zKey[i]); i++) { iByte = (iByte << 4) + wx_sqlite3HexToInt(zKey[i]); - if ((i & 1) != 0) zDecoded[i / 2] = iByte; + if ((i & 1) != 0) zDecoded[i/2] = iByte; } - wx_sqlite3_key_v2(db, zDb, zDecoded, i / 2); + wx_sqlite3_key_v2(db, zDb, zDecoded, i/2); + wx_sqlite3_free(zDecoded); } else if ((zKey = wx_sqlite3_uri_parameter(zUri, "key")) != 0) { @@ -256604,7 +268791,7 @@ wx_sqlite3mcHandleMainKey(wx_sqlite3* db, const char* zPath) ** Purpose: Implementation of SQLite codec API ** Author: Ulrich Telle ** Created: 2006-12-06 -** Copyright: (c) 2006-2020 Ulrich Telle +** Copyright: (c) 2006-2022 Ulrich Telle ** License: MIT */ @@ -256680,7 +268867,7 @@ wx_sqlite3mcBtreeSetPageSize(Btree* p, int pageSize, int nReserve, int iFix) ** Change 4: Call wx_sqlite3mcBtreeSetPageSize instead of wx_sqlite3BtreeSetPageSize for main database ** (wx_sqlite3mcBtreeSetPageSize allows to reduce the number of reserved bytes) ** -** This code is generated by the script rekeyvacuum.sh from SQLite version 3.34.0 amalgamation. +** This code is generated by the script rekeyvacuum.sh from SQLite version 3.41.2 amalgamation. */ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3mcRunVacuumForRekey( char **pzErrMsg, /* Write error message here */ @@ -256693,8 +268880,8 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3mcRunVacuumForRekey( Btree *pTemp; /* The temporary database we vacuum into */ u32 saved_mDbFlags; /* Saved value of db->mDbFlags */ u64 saved_flags; /* Saved value of db->flags */ - int saved_nChange; /* Saved value of db->nChange */ - int saved_nTotalChange; /* Saved value of db->nTotalChange */ + i64 saved_nChange; /* Saved value of db->nChange */ + i64 saved_nTotalChange; /* Saved value of db->nTotalChange */ u32 saved_openFlags; /* Saved value of db->openFlags */ u8 saved_mTrace; /* Saved trace settings */ Db *pDb = 0; /* Database to detach at end of vacuum */ @@ -256702,6 +268889,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3mcRunVacuumForRekey( int nDb; /* Number of attached databases */ const char *zDbMain; /* Schema name of database to vacuum */ const char *zOut; /* Name of output file */ + u32 pgflags = PAGER_SYNCHRONOUS_OFF; /* sync flags for output db */ if( !db->autoCommit ){ wx_sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); @@ -256773,6 +268961,11 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3mcRunVacuumForRekey( goto end_of_vacuum; } db->mDbFlags |= DBFLAG_VacuumInto; + + /* For a VACUUM INTO, the pager-flags are set to the same values as + ** they are for the database being vacuumed, except that PAGER_CACHESPILL + ** is always set. */ + pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK); } /* A VACUUM cannot change the pagesize of an encrypted database. */ @@ -256786,7 +268979,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3mcRunVacuumForRekey( wx_sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); wx_sqlite3BtreeSetSpillSize(pTemp, wx_sqlite3BtreeSetSpillSize(pMain,0)); - wx_sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL); + wx_sqlite3BtreeSetPagerFlags(pTemp, pgflags|PAGER_CACHESPILL); /* Begin a transaction and take an exclusive lock on the main database ** file. This is done before the wx_sqlite3BtreeGetPageSize(pMain) call below, @@ -256799,7 +268992,9 @@ SQLITE_PRIVATE SQLITE_NOINLINE int wx_sqlite3mcRunVacuumForRekey( /* Do not attempt to change the page size for a WAL database */ if( wx_sqlite3PagerGetJournalMode(wx_sqlite3BtreePager(pMain)) - ==PAGER_JOURNALMODE_WAL ){ + ==PAGER_JOURNALMODE_WAL + && pOut==0 + ){ db->nextPagesize = 0; } @@ -256986,9 +269181,18 @@ wx_sqlite3mcCodecSizeChange(void *pArg, int pageSize, int reservedSize) static void mcReportCodecError(BtShared* pBt, int error) { + pBt->db->errCode = error; pBt->pPager->errCode = error; + if (error != SQLITE_OK) + { + pBt->pPager->eState = PAGER_ERROR; + } setGetterMethod(pBt->pPager); - pBt->db->errCode = error; + if (error == SQLITE_OK) + { + /* Clear cache to force reread of database after a new passphrase has been set */ + wx_sqlite3PagerClearCache(pBt->pPager); + } } /* @@ -257061,7 +269265,7 @@ SQLITE_PRIVATE Codec* wx_sqlite3mcGetMainCodec(wx_sqlite3* db); SQLITE_PRIVATE void -wx_sqlite3mcSetCodec(wx_sqlite3* db, const char* zFileName, Codec* codec); +wx_sqlite3mcSetCodec(wx_sqlite3* db, const char* zDbName, const char* zFileName, Codec* codec); static int mcAdjustBtree(Btree* pBt, int nPageSize, int nReserved, int isLegacy) @@ -257076,7 +269280,7 @@ mcAdjustBtree(Btree* pBt, int nPageSize, int nReserved, int isLegacy) } /* Adjust the page size and the reserved area */ - if (pager->nReserve != nReserved) + if (pager->pageSize != pagesize || pager->nReserve != nReserved) { if (isLegacy != 0) { @@ -257123,7 +269327,7 @@ wx_sqlite3mcCodecAttach(wx_sqlite3* db, int nDb, const char* zPath, const void* wx_sqlite3mcSetBtree(codec, db->aDb[nDb].pBt); mcAdjustBtree(db->aDb[nDb].pBt, pageSize, reserved, wx_sqlite3mcGetLegacyWriteCipher(codec)); wx_sqlite3mcCodecSizeChange(codec, pageSize, reserved); - wx_sqlite3mcSetCodec(db, dbFileName, codec); + wx_sqlite3mcSetCodec(db, zDbName, dbFileName, codec); } else { @@ -257141,6 +269345,11 @@ wx_sqlite3mcCodecAttach(wx_sqlite3* db, int nDb, const char* zPath, const void* { /* Main database not encrypted, no key given for attached database */ wx_sqlite3mcCodecFree(codec); + /* Remove codec for main database */ + if (nDb == 0 && nKey == 0) + { + wx_sqlite3mcSetCodec(db, zDbName, dbFileName, NULL); + } } } else @@ -257174,7 +269383,7 @@ wx_sqlite3mcCodecAttach(wx_sqlite3* db, int nDb, const char* zPath, const void* int reserved = wx_sqlite3mcGetReservedWriteCipher(codec); mcAdjustBtree(db->aDb[nDb].pBt, pageSize, reserved, wx_sqlite3mcGetLegacyWriteCipher(codec)); wx_sqlite3mcCodecSizeChange(codec, pageSize, reserved); - wx_sqlite3mcSetCodec(db, dbFileName, codec); + wx_sqlite3mcSetCodec(db, zDbName, dbFileName, codec); } else { @@ -257220,10 +269429,16 @@ wx_sqlite3_key_v2(wx_sqlite3* db, const char* zDbName, const void* zKey, int nKe /* Key is zero-terminated string */ nKey = wx_sqlite3Strlen30((const char*) zKey); } - if ((db != NULL) && (zKey != NULL) && (nKey > 0)) + /* Database handle db and key must be given, but key length 0 is allowed */ + if ((db != NULL) && (zKey != NULL) && (nKey >= 0)) { int dbIndex; const char* dbFileName = wx_sqlite3_db_filename(db, zDbName); + if (dbFileName == NULL || dbFileName[0] == 0) + { + wx_sqlite3ErrorWithMsg(db, rc, "Setting key not supported for in-memory or temporary databases."); + return rc; + } /* Configure cipher from URI parameters if requested */ if (wx_sqlite3FindFunction(db, "wx_sqlite3mc_config_table", 0, SQLITE_UTF8, 0) == NULL) { @@ -257244,6 +269459,7 @@ wx_sqlite3_key_v2(wx_sqlite3* db, const char* zDbName, const void* zKey, int nKe else { rc = SQLITE_ERROR; + wx_sqlite3ErrorWithMsg(db, rc, "Setting key failed. Database '%s' not found.", zDbName); } } return rc; @@ -257270,6 +269486,12 @@ wx_sqlite3_rekey_v2(wx_sqlite3* db, const char* zDbName, const void* zKey, int n dbIndex = (zDbName) ? wx_sqlite3FindDbName(db, zDbName) : 0; if (dbIndex < 0) { + wx_sqlite3ErrorWithMsg(db, rc, "Rekeying failed. Database '%s' not found.", zDbName); + return rc; + } + if (dbFileName == NULL || dbFileName[0] == 0) + { + wx_sqlite3ErrorWithMsg(db, rc, "Rekeying not supported for in-memory or temporary databases."); return rc; } pBt = db->aDb[dbIndex].pBt; @@ -257282,6 +269504,12 @@ wx_sqlite3_rekey_v2(wx_sqlite3* db, const char* zDbName, const void* zKey, int n pPager = wx_sqlite3BtreePager(pBt); codec = wx_sqlite3mcGetCodec(db, zDbName); + if (pagerUseWal(pPager)) + { + wx_sqlite3ErrorWithMsg(db, rc, "Rekeying is not supported in WAL journal mode."); + return rc; + } + if ((zKey == NULL || nKey == 0) && (codec == NULL || !wx_sqlite3mcIsEncrypted(codec))) { /* Database not encrypted and key not specified, therefore do nothing */ @@ -257312,7 +269540,7 @@ wx_sqlite3_rekey_v2(wx_sqlite3* db, const char* zDbName, const void* zKey, int n int nReservedWriteCipher; wx_sqlite3mcSetHasReadCipher(codec, 0); /* Original database is not encrypted */ mcAdjustBtree(pBt, wx_sqlite3mcGetPageSizeWriteCipher(codec), wx_sqlite3mcGetReservedWriteCipher(codec), wx_sqlite3mcGetLegacyWriteCipher(codec)); - wx_sqlite3mcSetCodec(db, dbFileName, codec); + wx_sqlite3mcSetCodec(db, zDbName, dbFileName, codec); nReservedWriteCipher = wx_sqlite3mcGetReservedWriteCipher(codec); wx_sqlite3mcCodecSizeChange(codec, nPagesize, nReservedWriteCipher); if (nReserved != nReservedWriteCipher) @@ -257322,6 +269550,10 @@ wx_sqlite3_rekey_v2(wx_sqlite3* db, const char* zDbName, const void* zKey, int n wx_sqlite3mcSetReadReserved(codec, nReserved); wx_sqlite3mcSetWriteReserved(codec, nReservedWriteCipher); rc = wx_sqlite3mcRunVacuumForRekey(&err, db, dbIndex, NULL, nReservedWriteCipher); + if (rc != SQLITE_OK && err != NULL) + { + wx_sqlite3ErrorWithMsg(db, rc, err); + } goto leave_rekey; } } @@ -257329,6 +269561,7 @@ wx_sqlite3_rekey_v2(wx_sqlite3* db, const char* zDbName, const void* zKey, int n { /* Pagesize cannot be changed for an encrypted database */ rc = SQLITE_ERROR; + wx_sqlite3ErrorWithMsg(db, rc, "Rekeying failed. Pagesize cannot be changed for an encrypted database."); goto leave_rekey; } } @@ -257349,6 +269582,10 @@ wx_sqlite3_rekey_v2(wx_sqlite3* db, const char* zDbName, const void* zKey, int n wx_sqlite3mcSetReadReserved(codec, nReserved); wx_sqlite3mcSetWriteReserved(codec, 0); rc = wx_sqlite3mcRunVacuumForRekey(&err, db, dbIndex, NULL, 0); + if (rc != SQLITE_OK && err != NULL) + { + wx_sqlite3ErrorWithMsg(db, rc, err); + } goto leave_rekey; } } @@ -257370,6 +269607,10 @@ wx_sqlite3_rekey_v2(wx_sqlite3* db, const char* zDbName, const void* zKey, int n wx_sqlite3mcSetReadReserved(codec, nReserved); wx_sqlite3mcSetWriteReserved(codec, nReservedWriteCipher); rc = wx_sqlite3mcRunVacuumForRekey(&err, db, dbIndex, NULL, nReservedWriteCipher); + if (rc != SQLITE_OK && err != NULL) + { + wx_sqlite3ErrorWithMsg(db, rc, err); + } goto leave_rekey; } } @@ -257377,12 +269618,14 @@ wx_sqlite3_rekey_v2(wx_sqlite3* db, const char* zDbName, const void* zKey, int n { /* Pagesize cannot be changed for an encrypted database */ rc = SQLITE_ERROR; + wx_sqlite3ErrorWithMsg(db, rc, "Rekeying failed. Pagesize cannot be changed for an encrypted database."); goto leave_rekey; } } else { /* Setup of write cipher failed */ + wx_sqlite3ErrorWithMsg(db, rc, "Rekeying failed. Setup of write cipher failed."); goto leave_rekey; } } @@ -257460,7 +269703,7 @@ leave_rekey: if (!wx_sqlite3mcIsEncrypted(codec)) { /* Remove codec for unencrypted database */ - wx_sqlite3mcSetCodec(db, dbFileName, NULL); + wx_sqlite3mcSetCodec(db, zDbName, dbFileName, NULL); } return rc; } @@ -257947,12 +270190,36 @@ struct wx_sqlite3_api_routines { const char *(*filename_journal)(const char*); const char *(*filename_wal)(const char*); /* Version 3.32.0 and later */ - char *(*create_filename)(const char*,const char*,const char*, + const char *(*create_filename)(const char*,const char*,const char*, int,const char**); - void (*free_filename)(char*); + void (*free_filename)(const char*); wx_sqlite3_file *(*database_file_object)(const char*); /* Version 3.34.0 and later */ int (*txn_state)(wx_sqlite3*,const char*); + /* Version 3.36.1 and later */ + wx_sqlite3_int64 (*changes64)(wx_sqlite3*); + wx_sqlite3_int64 (*total_changes64)(wx_sqlite3*); + /* Version 3.37.0 and later */ + int (*autovacuum_pages)(wx_sqlite3*, + unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), + void*, void(*)(void*)); + /* Version 3.38.0 and later */ + int (*error_offset)(wx_sqlite3*); + int (*vtab_rhs_value)(wx_sqlite3_index_info*,int,wx_sqlite3_value**); + int (*vtab_distinct)(wx_sqlite3_index_info*); + int (*vtab_in)(wx_sqlite3_index_info*,int,int); + int (*vtab_in_first)(wx_sqlite3_value*,wx_sqlite3_value**); + int (*vtab_in_next)(wx_sqlite3_value*,wx_sqlite3_value**); + /* Version 3.39.0 and later */ + int (*deserialize)(wx_sqlite3*,const char*,unsigned char*, + wx_sqlite3_int64,wx_sqlite3_int64,unsigned); + unsigned char *(*serialize)(wx_sqlite3*,const char *,wx_sqlite3_int64*, + unsigned int); + const char *(*db_name)(wx_sqlite3*,int); + /* Version 3.40.0 and later */ + int (*value_encoding)(wx_sqlite3_value*); + /* Version 3.41.0 and later */ + int (*is_interrupted)(wx_sqlite3*); }; /* @@ -258259,6 +270526,28 @@ typedef int (*wx_sqlite3_loadext_entry)( #define wx_sqlite3_database_file_object wx_sqlite3_api->database_file_object /* Version 3.34.0 and later */ #define wx_sqlite3_txn_state wx_sqlite3_api->txn_state +/* Version 3.36.1 and later */ +#define wx_sqlite3_changes64 wx_sqlite3_api->changes64 +#define wx_sqlite3_total_changes64 wx_sqlite3_api->total_changes64 +/* Version 3.37.0 and later */ +#define wx_sqlite3_autovacuum_pages wx_sqlite3_api->autovacuum_pages +/* Version 3.38.0 and later */ +#define wx_sqlite3_error_offset wx_sqlite3_api->error_offset +#define wx_sqlite3_vtab_rhs_value wx_sqlite3_api->vtab_rhs_value +#define wx_sqlite3_vtab_distinct wx_sqlite3_api->vtab_distinct +#define wx_sqlite3_vtab_in wx_sqlite3_api->vtab_in +#define wx_sqlite3_vtab_in_first wx_sqlite3_api->vtab_in_first +#define wx_sqlite3_vtab_in_next wx_sqlite3_api->vtab_in_next +/* Version 3.39.0 and later */ +#ifndef SQLITE_OMIT_DESERIALIZE +#define wx_sqlite3_deserialize wx_sqlite3_api->deserialize +#define wx_sqlite3_serialize wx_sqlite3_api->serialize +#endif +#define wx_sqlite3_db_name wx_sqlite3_api->db_name +/* Version 3.40.0 and later */ +#define wx_sqlite3_value_encoding wx_sqlite3_api->value_encoding +/* Version 3.41.0 and later */ +#define wx_sqlite3_is_interrupted wx_sqlite3_api->is_interrupted #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -260463,6 +272752,7 @@ static char *csv_read_one_field(CsvReader *p){ } p->cTerm = (char)c; } + assert( p->z==0 || p->nnAlloc ); if( p->z ) p->z[p->n] = 0; p->bNotFirst = 1; return p->z; @@ -260933,7 +273223,7 @@ static int csvtabNext(wx_sqlite3_vtab_cursor *cur){ i++; } }while( pCur->rdr.cTerm==',' ); - if( z==0 || (pCur->rdr.cTerm==EOF && inCol) ){ + if( z==0 && i==0 ){ pCur->iRowid = -1; }else{ pCur->iRowid++; @@ -260994,6 +273284,12 @@ static int csvtabFilter( CsvCursor *pCur = (CsvCursor*)pVtabCursor; CsvTable *pTab = (CsvTable*)pVtabCursor->pVtab; pCur->iRowid = 0; + + /* Ensure the field buffer is always allocated. Otherwise, if the + ** first field is zero bytes in size, this may be mistaken for an OOM + ** error in csvtabNext(). */ + if( csv_append(&pCur->rdr, 0) ) return SQLITE_NOMEM; + if( pCur->rdr.in==0 ){ assert( pCur->rdr.zIn==pTab->zData ); assert( pTab->iStart>=0 ); @@ -261124,7 +273420,7 @@ int wx_sqlite3_csv_init( char **pzErrMsg, const wx_sqlite3_api_routines *pApi ){ -#ifndef SQLITE_OMIT_VIRTUALTABLE +#ifndef SQLITE_OMIT_VIRTUALTABLE int rc; SQLITE_EXTENSION_INIT2(pApi); rc = wx_sqlite3_create_module(db, "csv", &CsvModule, 0); @@ -261162,6 +273458,7 @@ int wx_sqlite3_vsv_init(wx_sqlite3* db, char** pzErrMsg, const wx_sqlite3_api_ro ** but subtly different. VSV supports a number of extensions to the ** CSV format as well as more processing options. ** +** http:\\www.dessus.com\files\vsv.c ** ** Usage: ** @@ -261220,9 +273517,10 @@ int wx_sqlite3_vsv_init(wx_sqlite3* db, char** pzErrMsg, const wx_sqlite3_api_ro ** ** STRING means a quoted string. The quote character may be either ** a single quote or a double quote. Two quote characters in a row -** will be replaced with a single quote character. STRINGS do not +** will be replaced with one quote character. STRINGS do not ** need to be quoted if it is obvious where they begin and end -** (that is, they do not contain a comma). Leading and trailing +** (that is, they do not contain a comma or other character that the +** parser treats especially, such as : or \). Leading and trailing ** spaces will be trimmed from unquoted strings. ** ** filename =./this/filename.here, ... @@ -261266,7 +273564,7 @@ int wx_sqlite3_vsv_init(wx_sqlite3* db, char** pzErrMsg, const wx_sqlite3_api_ro ** The nulls option will cause fields that do not contain anything ** to return NULL rather than an empty result. Two separators ** side-by-each with no intervening characters at all will be -** returned as NULL if nulls is true and if nulls is false or +** returned as NULL if nulls is true; if nulls is false or ** the contents are explicity empty ("") then a 0 length blob ** (if affinity=blob) or 0 length text string. ** @@ -261327,7 +273625,7 @@ int wx_sqlite3_vsv_init(wx_sqlite3* db, char** pzErrMsg, const wx_sqlite3_api_ro ** The nulls option will cause fields that do not contain anything ** to return NULL rather than an empty result. Two separators ** side-by-each with no intervening characters at all will be -** returned as NULL if nulls is true and if nulls is false or +** returned as NULL if nulls is true; if nulls is false or ** the contents are explicity empty ("") then a 0 length blob ** (if affinity=blob) or 0 length text string will be returned. ** @@ -261382,6 +273680,15 @@ SQLITE_EXTENSION_INIT1 #include #include +#ifdef SQLITE_HAVE_ZLIB +#include +#define fopen gzopen +#define fclose gzclose +#define fread gzfread +#define fseek gzseek +#define ftell gztell +#endif + #ifndef SQLITE_OMIT_VIRTUALTABLE /* @@ -261413,7 +273720,11 @@ SQLITE_EXTENSION_INIT1 typedef struct VsvReader VsvReader; struct VsvReader { +#ifdef SQLITE_HAVE_ZLIB + gzFile in; /* Read the VSV text from this compressed input stream */ +#else FILE *in; /* Read the VSV text from this input stream */ +#endif char *z; /* Accumulated text for a field */ int n; /* Number of bytes in z */ int nAlloc; /* Space allocated for z[] */ @@ -261705,6 +274016,7 @@ static char *vsv_read_one_field(VsvReader *p) } p->cTerm = (char)c; } + assert( p->z==0 || p->nnAlloc ); if (p->z) { p->z[p->n] = 0; @@ -262313,6 +274625,7 @@ static int vsvtabConnect( } else if (nCol<0) { + nCol = 0; do { vsv_read_one_field(&sRdr); @@ -262705,6 +275018,36 @@ static long long vsv_utf8IsValid(char *string) length++; continue; } +#if 0 // UTF-8 does not encode sequences longer than 4 bytes (yet) + if ((c & 0xFC) == 0xF8) + { + trailing = 4; + start++; + length++; + continue; + } + if ((c & 0xFE) == 0xFC) + { + trailing = 5; + start++; + length++; + continue; + } + if ((c & 0xFF) == 0xFE) + { + trailing = 6; + start++; + length++; + continue; + } + if ((c & 0xFF) == 0xFF) + { + trailing = 7; + start++; + length++; + continue; + } +#endif length = -1; break; } @@ -262940,6 +275283,12 @@ static int vsvtabFilter( VsvCursor *pCur = (VsvCursor*)pVtabCursor; VsvTable *pTab = (VsvTable*)pVtabCursor->pVtab; pCur->iRowid = 0; + + /* Ensure the field buffer is always allocated. Otherwise, if the + ** first field is zero bytes in size, this may be mistaken for an OOM + ** error in csvtabNext(). */ + if( vsv_append(&pCur->rdr, 0) ) return SQLITE_NOMEM; + if (pCur->rdr.in==0) { assert( pCur->rdr.zIn==pTab->zData ); @@ -263131,7 +275480,7 @@ int wx_sqlite3_shathree_init(wx_sqlite3* db, char** pzErrMsg, const wx_sqlite3_a ** 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 +** The sha3_query(Y) function evaluates 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 @@ -263549,6 +275898,7 @@ static void SHA3Update( unsigned int nData ){ unsigned int i = 0; + if( aData==0 ) return; #if SHA3_BYTEORDER==1234 if( (p->nLoaded % 8)==0 && ((aData - (const unsigned char*)0)&7)==0 ){ for(; i+7 #include - +#ifdef _WIN32 + struct iovec { + void *iov_base; + size_t iov_len; + }; +#else +# include +#endif + /* Allowed values for the mFlags parameter to wx_sqlite3_carray_bind(). ** Must exactly match the definitions in carray.h. */ -#define CARRAY_INT32 0 /* Data is 32-bit signed integers */ -#define CARRAY_INT64 1 /* Data is 64-bit signed integers */ -#define CARRAY_DOUBLE 2 /* Data is doubles */ -#define CARRAY_TEXT 3 /* Data is char* */ +#ifndef CARRAY_INT32 +# define CARRAY_INT32 0 /* Data is 32-bit signed integers */ +# define CARRAY_INT64 1 /* Data is 64-bit signed integers */ +# define CARRAY_DOUBLE 2 /* Data is doubles */ +# define CARRAY_TEXT 3 /* Data is char* */ +# define CARRAY_BLOB 4 /* Data is struct iovec* */ +#endif + +#ifndef SQLITE_API +# ifdef _WIN32 +# define SQLITE_API __declspec(dllexport) +# else +# define SQLITE_API +# endif +#endif #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Names of allowed datatypes */ -static const char *azType[] = { "int32", "int64", "double", "char*" }; +static const char *azType[] = { "int32", "int64", "double", "char*", + "struct iovec" }; /* ** Structure used to hold the wx_sqlite3_carray_bind() information @@ -264066,6 +276436,12 @@ static int carrayColumn( wx_sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT); return SQLITE_OK; } + case CARRAY_BLOB: { + const struct iovec *p = (struct iovec*)pCur->pPtr; + wx_sqlite3_result_blob(ctx, p[pCur->iRowid-1].iov_base, + (int)p[pCur->iRowid-1].iov_len, SQLITE_TRANSIENT); + return SQLITE_OK; + } } } } @@ -264110,7 +276486,7 @@ static int carrayFilter( if( pBind==0 ) break; pCur->pPtr = pBind->aData; pCur->iCnt = pBind->nData; - pCur->eType = pBind->mFlags & 0x03; + pCur->eType = pBind->mFlags & 0x07; break; } case 2: @@ -264252,10 +276628,7 @@ static void carrayBindDel(void *pPtr){ ** Invoke this interface in order to bind to the single-argument ** version of CARRAY(). */ -#ifdef _WIN32 -__declspec(dllexport) -#endif -int wx_sqlite3_carray_bind( +SQLITE_API int wx_sqlite3_carray_bind( wx_sqlite3_stmt *pStmt, int idx, void *aData, @@ -264276,24 +276649,29 @@ int wx_sqlite3_carray_bind( pNew->mFlags = mFlags; if( xDestroy==SQLITE_TRANSIENT ){ wx_sqlite3_int64 sz = nData; - switch( mFlags & 0x03 ){ - case CARRAY_INT32: sz *= 4; break; - case CARRAY_INT64: sz *= 8; break; - case CARRAY_DOUBLE: sz *= 8; break; - case CARRAY_TEXT: sz *= sizeof(char*); break; - } - if( (mFlags & 0x03)==CARRAY_TEXT ){ + switch( mFlags & 0x07 ){ + case CARRAY_INT32: sz *= 4; break; + case CARRAY_INT64: sz *= 8; break; + case CARRAY_DOUBLE: sz *= 8; break; + case CARRAY_TEXT: sz *= sizeof(char*); break; + case CARRAY_BLOB: sz *= sizeof(struct iovec); break; + } + if( (mFlags & 0x07)==CARRAY_TEXT ){ for(i=0; iaData = wx_sqlite3_malloc64( sz ); if( pNew->aData==0 ){ wx_sqlite3_free(pNew); return SQLITE_NOMEM; } - if( (mFlags & 0x03)==CARRAY_TEXT ){ + if( (mFlags & 0x07)==CARRAY_TEXT ){ char **az = (char**)pNew->aData; char *z = (char*)&az[nData]; for(i=0; iaData; + unsigned char *z = (unsigned char*)&p[nData]; + for(i=0; iaData, aData, sz*nData); + memcpy(pNew->aData, aData, sz); } pNew->xDel = wx_sqlite3_free; }else{ @@ -264350,10 +276738,7 @@ static void inttoptrFunc( #endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifdef _WIN32 -__declspec(dllexport) -#endif -int wx_sqlite3_carray_init( +SQLITE_API int wx_sqlite3_carray_init( wx_sqlite3 *db, char **pzErrMsg, const wx_sqlite3_api_routines *pApi @@ -264831,6 +277216,11 @@ INT closedir( ** $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. +** +** Notes on building this extension for Windows: +** Unless linked statically with the SQLite library, a preprocessor +** symbol, FILEIO_WIN32_DLL, must be #define'd to create a stand-alone +** DLL form of this extension for WIN32. See its use below for details. */ /* #include "wx_sqlite3ext.h" */ @@ -264986,6 +277376,22 @@ static wx_sqlite3_uint64 fileTimeToUnixTime( return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000; } + +#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32)) +# /* To allow a standalone DLL, use this next replacement function: */ +# undef wx_sqlite3_win32_utf8_to_unicode +# define wx_sqlite3_win32_utf8_to_unicode utf8_to_utf16 +# +LPWSTR utf8_to_utf16(const char *z){ + int nAllot = MultiByteToWideChar(CP_UTF8, 0, z, -1, NULL, 0); + LPWSTR rv = wx_sqlite3_malloc(nAllot * sizeof(WCHAR)); + if( rv!=0 && 0 < MultiByteToWideChar(CP_UTF8, 0, z, -1, rv, nAllot) ) + return rv; + wx_sqlite3_free(rv); + return 0; +} +#endif + /* ** This function attempts to normalize the time values found in the stat() ** buffer to UTC. This is necessary on Win32, where the runtime library @@ -265108,10 +277514,11 @@ static int writeFile( mode_t mode, /* MODE parameter passed to writefile() */ wx_sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */ ){ + if( zFile==0 ) return 1; #if !defined(_WIN32) && !defined(WIN32) if( S_ISLNK(mode) ){ const char *zTo = (const char*)wx_sqlite3_value_text(pData); - if( symlink(zTo, zFile)<0 ) return 1; + if( zTo==0 || symlink(zTo, zFile)<0 ) return 1; }else #endif { @@ -265759,6 +278166,15 @@ int wx_sqlite3_fileio_init( } return rc; } + +#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32)) +/* To allow a standalone DLL, make test_windirent.c use the same + * redefined SQLite API calls as the above extension code does. + * Just pull in this .c to accomplish this. As a beneficial side + * effect, this extension becomes a single translation unit. */ +/* # include "test_windirent.c" */ + +#endif /*** End of #include "fileio.c" ***/ #endif @@ -266100,11 +278516,12 @@ static int seriesFilter( ** (8) output in descending order */ static int seriesBestIndex( - wx_sqlite3_vtab *tabUnused, + wx_sqlite3_vtab *pVTab, wx_sqlite3_index_info *pIdxInfo ){ int i, j; /* Loop over constraints */ int idxNum = 0; /* The query plan bitmask */ + int bStartSeen = 0; /* EQ constraint seen on the START column */ int unusableMask = 0; /* Mask of unusable constraints */ int nArg = 0; /* Number of arguments that seriesFilter() expects */ int aIdx[3]; /* Constraints on start, stop, and step */ @@ -266114,7 +278531,7 @@ static int seriesBestIndex( ** are the last three columns in the virtual table. */ assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 ); assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 ); - (void)tabUnused; + aIdx[0] = aIdx[1] = aIdx[2] = -1; pConstraint = pIdxInfo->aConstraint; for(i=0; inConstraint; i++, pConstraint++){ @@ -266124,6 +278541,7 @@ static int seriesBestIndex( iCol = pConstraint->iColumn - SERIES_COLUMN_START; assert( iCol>=0 && iCol<=2 ); iMask = 1 << iCol; + if( iCol==0 ) bStartSeen = 1; if( pConstraint->usable==0 ){ unusableMask |= iMask; continue; @@ -266138,6 +278556,18 @@ static int seriesBestIndex( pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY; } } + /* The current generate_column() implementation requires at least one + ** argument (the START value). Legacy versions assumed START=0 if the + ** first argument was omitted. Compile with -DZERO_ARGUMENT_GENERATE_SERIES + ** to obtain the legacy behavior */ +#ifndef ZERO_ARGUMENT_GENERATE_SERIES + if( !bStartSeen ){ + wx_sqlite3_free(pVTab->zErrMsg); + pVTab->zErrMsg = wx_sqlite3_mprintf( + "first argument to \"generate_series()\" missing or unusable"); + return SQLITE_ERROR; + } +#endif if( (unusableMask & ~idxNum)!=0 ){ /* The start, stop, and step columns are inputs. Therefore if there ** are unusable constraints on any of start, stop, or step then @@ -266149,7 +278579,7 @@ static int seriesBestIndex( ** the preferred case */ pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0)); pIdxInfo->estimatedRows = 1000; - if( pIdxInfo->nOrderBy==1 ){ + if( pIdxInfo->nOrderBy>=1 && pIdxInfo->aOrderBy[0].iColumn==0 ){ if( pIdxInfo->aOrderBy[0].desc ){ idxNum |= 8; }else{ @@ -266211,7 +278641,7 @@ int wx_sqlite3_series_init( int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); #ifndef SQLITE_OMIT_VIRTUALTABLE - if( wx_sqlite3_libversion_number()<3008012 ){ + if( wx_sqlite3_libversion_number()<3008012 && pzErrMsg!=0 ){ *pzErrMsg = wx_sqlite3_mprintf( "generate_series() requires SQLite 3.8.12 or later"); return SQLITE_ERROR; @@ -266559,6 +278989,7 @@ SQLITE_EXTENSION_INIT1 /* The end-of-input character */ #define RE_EOF 0 /* End of input */ +#define RE_START 0xfffffff /* Start of input - larger than an UTF-8 */ /* The NFA is implemented as sequence of opcodes taken from the following ** set. Each opcode has a single integer argument. @@ -266580,6 +279011,33 @@ SQLITE_EXTENSION_INIT1 #define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */ #define RE_OP_NOTSPACE 16 /* Not a digit */ #define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */ +#define RE_OP_ATSTART 18 /* Currently at the start of the string */ + +#if defined(SQLITE_DEBUG) +/* Opcode names used for symbolic debugging */ +static const char *ReOpName[] = { + "EOF", + "MATCH", + "ANY", + "ANYSTAR", + "FORK", + "GOTO", + "ACCEPT", + "CC_INC", + "CC_EXC", + "CC_VALUE", + "CC_RANGE", + "WORD", + "NOTWORD", + "DIGIT", + "NOTDIGIT", + "SPACE", + "NOTSPACE", + "BOUNDARY", + "ATSTART", +}; +#endif /* SQLITE_DEBUG */ + /* Each opcode is a "state" in the NFA */ typedef unsigned short ReStateNumber; @@ -266614,7 +279072,7 @@ struct ReCompiled { int *aArg; /* Arguments to each operator */ unsigned (*xNextChar)(ReInput*); /* Next character function */ unsigned char zInit[12]; /* Initial text to match */ - int nInit; /* Number of characters in zInit */ + int nInit; /* Number of bytes in zInit */ unsigned nState; /* Number of entries in aOp[] and aArg[] */ unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */ }; @@ -266644,7 +279102,7 @@ static unsigned re_next_char(ReInput *p){ c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f); p->i += 2; if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd; - }else if( (c&0xf8)==0xf0 && p->i+3mx && (p->z[p->i]&0xc0)==0x80 + }else if( (c&0xf8)==0xf0 && p->i+2mx && (p->z[p->i]&0xc0)==0x80 && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){ c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6) | (p->z[p->i+2]&0x3f); @@ -266687,7 +279145,7 @@ static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ ReStateNumber *pToFree; unsigned int i = 0; unsigned int iSwap = 0; - int c = RE_EOF+1; + int c = RE_START; int cPrev = 0; int rc = 0; ReInput in; @@ -266706,6 +279164,7 @@ static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ in.i++; } if( in.i+pRe->nInit>in.mx ) return 0; + c = RE_START-1; } if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){ @@ -266734,8 +279193,12 @@ static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ if( pRe->aArg[x]==c ) re_add_state(pNext, x+1); break; } + case RE_OP_ATSTART: { + if( cPrev==RE_START ) re_add_state(pThis, x+1); + break; + } case RE_OP_ANY: { - re_add_state(pNext, x+1); + if( c!=0 ) re_add_state(pNext, x+1); break; } case RE_OP_WORD: { @@ -266743,7 +279206,7 @@ static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ break; } case RE_OP_NOTWORD: { - if( !re_word_char(c) ) re_add_state(pNext, x+1); + if( !re_word_char(c) && c!=0 ) re_add_state(pNext, x+1); break; } case RE_OP_DIGIT: { @@ -266751,7 +279214,7 @@ static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ break; } case RE_OP_NOTDIGIT: { - if( !re_digit_char(c) ) re_add_state(pNext, x+1); + if( !re_digit_char(c) && c!=0 ) re_add_state(pNext, x+1); break; } case RE_OP_SPACE: { @@ -266759,7 +279222,7 @@ static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ break; } case RE_OP_NOTSPACE: { - if( !re_space_char(c) ) re_add_state(pNext, x+1); + if( !re_space_char(c) && c!=0 ) re_add_state(pNext, x+1); break; } case RE_OP_BOUNDARY: { @@ -266784,8 +279247,11 @@ static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ rc = 1; goto re_match_end; } - case RE_OP_CC_INC: case RE_OP_CC_EXC: { + if( c==0 ) break; + /* fall-through */ goto re_op_cc_inc; + } + case RE_OP_CC_INC: re_op_cc_inc: { int j = 1; int n = pRe->aArg[x]; int hit = 0; @@ -266806,13 +279272,15 @@ static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ } if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit; if( hit ) re_add_state(pNext, x+n); - break; + break; } } } } for(i=0; inState; i++){ - if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; } + int x = pNext->aState[i]; + while( pRe->aOp[x]==RE_OP_GOTO ) x += pRe->aArg[x]; + if( pRe->aOp[x]==RE_OP_ACCEPT ){ rc = 1; break; } } re_match_end: wx_sqlite3_free(pToFree); @@ -266967,7 +279435,6 @@ static const char *re_subcompile_string(ReCompiled *p){ iStart = p->nState; switch( c ){ case '|': - case '$': case ')': { p->sIn.i--; return 0; @@ -266983,7 +279450,7 @@ static const char *re_subcompile_string(ReCompiled *p){ if( rePeek(p)=='*' ){ re_append(p, RE_OP_ANYSTAR, 0); p->sIn.i++; - }else{ + }else{ re_append(p, RE_OP_ANY, 0); } break; @@ -267004,6 +279471,14 @@ static const char *re_subcompile_string(ReCompiled *p){ re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1); break; } + case '$': { + re_append(p, RE_OP_MATCH, RE_EOF); + break; + } + case '^': { + re_append(p, RE_OP_ATSTART, 0); + break; + } case '{': { int m = 0, n = 0; int sz, j; @@ -267022,6 +279497,7 @@ static const char *re_subcompile_string(ReCompiled *p){ if( m==0 ){ if( n==0 ) return "both m and n are zero in '{m,n}'"; re_insert(p, iPrev, RE_OP_FORK, sz+1); + iPrev++; n--; }else{ for(j=1; jsIn.i+1>=pRe->sIn.mx ){ - re_append(pRe, RE_OP_MATCH, RE_EOF); - re_append(pRe, RE_OP_ACCEPT, 0); - *ppRe = pRe; - }else if( pRe->sIn.i>=pRe->sIn.mx ){ + if( pRe->sIn.i>=pRe->sIn.mx ){ re_append(pRe, RE_OP_ACCEPT, 0); *ppRe = pRe; }else{ @@ -267157,19 +279629,19 @@ static const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){ ** one or more matching characters, enter those matching characters into ** zInit[]. The re_match() routine can then search ahead in the input ** string looking for the initial match without having to run the whole - ** regex engine over the string. Do not worry able trying to match + ** regex engine over the string. Do not worry about trying to match ** unicode characters beyond plane 0 - those are very rare and this is ** just an optimization. */ - if( pRe->aOp[0]==RE_OP_ANYSTAR ){ - for(j=0, i=1; jzInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ + if( pRe->aOp[0]==RE_OP_ANYSTAR && !noCase ){ + for(j=0, i=1; j<(int)sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ unsigned x = pRe->aArg[i]; - if( x<=127 ){ + if( x<=0x7f ){ pRe->zInit[j++] = (unsigned char)x; - }else if( x<=0xfff ){ + }else if( x<=0x7ff ){ pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6)); pRe->zInit[j++] = 0x80 | (x&0x3f); }else if( x<=0xffff ){ - pRe->zInit[j++] = (unsigned char)(0xd0 | (x>>12)); + pRe->zInit[j++] = (unsigned char)(0xe0 | (x>>12)); pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); pRe->zInit[j++] = 0x80 | (x&0x3f); }else{ @@ -267192,8 +279664,8 @@ static const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){ ** is implemented as regexp(B,A). */ static void re_sql_func( - wx_sqlite3_context *context, - int argc, + wx_sqlite3_context *context, + int argc, wx_sqlite3_value **argv ){ ReCompiled *pRe; /* Compiled regular expression */ @@ -267202,11 +279674,12 @@ static void re_sql_func( const char *zErr; /* Compile error message */ int setAux = 0; /* True to invoke wx_sqlite3_set_auxdata() */ + (void)argc; /* Unused */ pRe = wx_sqlite3_get_auxdata(context, 0); if( pRe==0 ){ zPattern = (const char*)wx_sqlite3_value_text(argv[0]); if( zPattern==0 ) return; - zErr = re_compile(&pRe, zPattern, 0); + zErr = re_compile(&pRe, zPattern, wx_sqlite3_user_data(context)!=0); if( zErr ){ re_free(pRe); wx_sqlite3_result_error(context, zErr, -1); @@ -267227,6 +279700,68 @@ static void re_sql_func( } } +#if defined(SQLITE_DEBUG) +/* +** This function is used for testing and debugging only. It is only available +** if the SQLITE_DEBUG compile-time option is used. +** +** Compile a regular expression and then convert the compiled expression into +** text and return that text. +*/ +static void re_bytecode_func( + wx_sqlite3_context *context, + int argc, + wx_sqlite3_value **argv +){ + const char *zPattern; + const char *zErr; + ReCompiled *pRe; + wx_sqlite3_str *pStr; + int i; + int n; + char *z; + (void)argc; + + zPattern = (const char*)wx_sqlite3_value_text(argv[0]); + if( zPattern==0 ) return; + zErr = re_compile(&pRe, zPattern, wx_sqlite3_user_data(context)!=0); + if( zErr ){ + re_free(pRe); + wx_sqlite3_result_error(context, zErr, -1); + return; + } + if( pRe==0 ){ + wx_sqlite3_result_error_nomem(context); + return; + } + pStr = wx_sqlite3_str_new(0); + if( pStr==0 ) goto re_bytecode_func_err; + if( pRe->nInit>0 ){ + wx_sqlite3_str_appendf(pStr, "INIT "); + for(i=0; inInit; i++){ + wx_sqlite3_str_appendf(pStr, "%02x", pRe->zInit[i]); + } + wx_sqlite3_str_appendf(pStr, "\n"); + } + for(i=0; (unsigned)inState; i++){ + wx_sqlite3_str_appendf(pStr, "%-8s %4d\n", + ReOpName[(unsigned char)pRe->aOp[i]], pRe->aArg[i]); + } + n = wx_sqlite3_str_length(pStr); + z = wx_sqlite3_str_finish(pStr); + if( n==0 ){ + wx_sqlite3_free(z); + }else{ + wx_sqlite3_result_text(context, z, n-1, wx_sqlite3_free); + } + +re_bytecode_func_err: + re_free(pRe); +} + +#endif /* SQLITE_DEBUG */ + + /* ** Invoke this routine to register the regexp() function with the ** SQLite database connection. @@ -267241,1441 +279776,17567 @@ int wx_sqlite3_regexp_init( ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); - rc = wx_sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8|SQLITE_INNOCUOUS, - 0, re_sql_func, 0, 0); + (void)pzErrMsg; /* Unused */ + rc = wx_sqlite3_create_function(db, "regexp", 2, + SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, + 0, re_sql_func, 0, 0); + if( rc==SQLITE_OK ){ + /* The regexpi(PATTERN,STRING) function is a case-insensitive version + ** of regexp(PATTERN,STRING). */ + rc = wx_sqlite3_create_function(db, "regexpi", 2, + SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, + (void*)db, re_sql_func, 0, 0); +#if defined(SQLITE_DEBUG) + if( rc==SQLITE_OK ){ + rc = wx_sqlite3_create_function(db, "regexp_bytecode", 1, + SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, + 0, re_bytecode_func, 0, 0); + } +#endif /* SQLITE_DEBUG */ + } return rc; } /*** End of #include "regexp.c" ***/ #endif -/* -** Multi cipher VFS -*/ -/* #include "wx_sqlite3mc_vfs.c" */ -/*** Begin of #include "wx_sqlite3mc_vfs.c" ***/ -/* -** Name: wx_sqlite3mc_vfs.c -** Purpose: Implementation of SQLite VFS for Multiple Ciphers -** Author: Ulrich Telle -** Created: 2020-02-28 -** Copyright: (c) 2020 Ulrich Telle -** License: MIT -*/ +#if defined(SQLITE_ENABLE_COMPRESS) || defined(SQLITE_ENABLE_SQLAR) || defined(SQLITE_ENABLE_ZIPFILE) +#if SQLITE3MC_USE_MINIZ != 0 +/* #include "miniz.c" */ +/*** Begin of #include "miniz.c" ***/ +/* #include "miniz.h" */ +/*** Begin of #include "miniz.h" ***/ +#define MINIZ_EXPORT +/* miniz.c 2.2.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt -/* #include "wx_sqlite3mc_vfs.h" */ + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). -/* #include "wx_sqlite3.h" */ + * Low-level Deflate/Inflate implementation notes: -#include -#include -#include -/* #include "mystdint.h" */ + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. -/* -** Type definitions -*/ + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. -typedef struct wx_sqlite3mc_file wx_sqlite3mc_file; -typedef struct wx_sqlite3mc_vfs wx_sqlite3mc_vfs; + * zlib-style API notes: -/* -** SQLite3 Multiple Ciphers file structure + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateReset/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ -struct wx_sqlite3mc_file + + + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +MINIZ_EXPORT void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum { - wx_sqlite3_file base; /* wx_sqlite3_file I/O methods */ - wx_sqlite3_file* pFile; /* Real underlying OS file */ - const char* zFileName; /* File name */ - int openFlags; /* Open flags */ - wx_sqlite3mc_file* pMainNext; /* Next main db file */ - wx_sqlite3mc_file* pMainDb; /* Main database to which this one is attached */ - Codec* codec; /* Codec if encrypted */ - int pageNo; /* Page number (in case of journal files) */ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 }; -/* -** SQLite3 Multiple Ciphers VFS structure -*/ +/* Method */ +#define MZ_DEFLATED 8 -struct wx_sqlite3mc_vfs +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum { - wx_sqlite3_vfs base; /* Multiple Ciphers VFS shim methods */ - wx_sqlite3_mutex* mutex; /* Mutex to protect pMain */ - wx_sqlite3mc_file* pMain; /* List of main database files */ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 }; -#define REALVFS(p) ((wx_sqlite3_vfs*)(((wx_sqlite3mc_vfs*)(p))->base.pAppData)) -#define REALFILE(p) (((wx_sqlite3mc_file*)(p))->pFile) +#define MZ_VERSION "10.2.0" +#define MZ_VERNUM 0xA100 +#define MZ_VER_MAJOR 10 +#define MZ_VER_MINOR 2 +#define MZ_VER_REVISION 0 +#define MZ_VER_SUBREVISION 0 -#define ORIGVFS(p) ((wx_sqlite3_vfs*)((p)->pAppData)) -#define ORIGFILE(p) ((wx_sqlite3_file*)(((CksmFile*)(p))+1)) +#ifndef MINIZ_NO_ZLIB_APIS -/* -** Prototypes for VFS methods -*/ +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; -static int mcVfsOpen(wx_sqlite3_vfs* pVfs, const char* zName, wx_sqlite3_file* pFile, int flags, int* pOutFlags); -static int mcVfsDelete(wx_sqlite3_vfs* pVfs, const char* zName, int syncDir); -static int mcVfsAccess(wx_sqlite3_vfs* pVfs, const char* zName, int flags, int* pResOut); -static int mcVfsFullPathname(wx_sqlite3_vfs* pVfs, const char* zName, int nOut, char* zOut); -static void* mcVfsDlOpen(wx_sqlite3_vfs* pVfs, const char* zFilename); -static void mcVfsDlError(wx_sqlite3_vfs* pVfs, int nByte, char* zErrMsg); -static void (*mcVfsDlSym(wx_sqlite3_vfs* pVfs, void* p, const char* zSymbol))(void); -static void mcVfsDlClose(wx_sqlite3_vfs* pVfs, void* p); -static int mcVfsRandomness(wx_sqlite3_vfs* pVfs, int nByte, char* zOut); -static int mcVfsSleep(wx_sqlite3_vfs* pVfs, int microseconds); -static int mcVfsCurrentTime(wx_sqlite3_vfs* pVfs, double* pOut); -static int mcVfsGetLastError(wx_sqlite3_vfs* pVfs, int nErr, char* zOut); -static int mcVfsCurrentTimeInt64(wx_sqlite3_vfs* pVfs, wx_sqlite3_int64* pOut); -static int mcVfsSetSystemCall(wx_sqlite3_vfs* pVfs, const char* zName, wx_sqlite3_syscall_ptr pNewFunc); -static wx_sqlite3_syscall_ptr mcVfsGetSystemCall(wx_sqlite3_vfs* pVfs, const char* zName); -static const char* mcVfsNextSystemCall(wx_sqlite3_vfs* pVfs, const char* zName); +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; -/* -** Prototypes for IO methods -*/ +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 -static int mcIoClose(wx_sqlite3_file* pFile); -static int mcIoRead(wx_sqlite3_file* pFile, void*, int iAmt, wx_sqlite3_int64 iOfst); -static int mcIoWrite(wx_sqlite3_file* pFile,const void*,int iAmt, wx_sqlite3_int64 iOfst); -static int mcIoTruncate(wx_sqlite3_file* pFile, wx_sqlite3_int64 size); -static int mcIoSync(wx_sqlite3_file* pFile, int flags); -static int mcIoFileSize(wx_sqlite3_file* pFile, wx_sqlite3_int64* pSize); -static int mcIoLock(wx_sqlite3_file* pFile, int lock); -static int mcIoUnlock(wx_sqlite3_file* pFile, int lock); -static int mcIoCheckReservedLock(wx_sqlite3_file* pFile, int *pResOut); -static int mcIoFileControl(wx_sqlite3_file* pFile, int op, void *pArg); -static int mcIoSectorSize(wx_sqlite3_file* pFile); -static int mcIoDeviceCharacteristics(wx_sqlite3_file* pFile); -static int mcIoShmMap(wx_sqlite3_file* pFile, int iPg, int pgsz, int map, void volatile** p); -static int mcIoShmLock(wx_sqlite3_file* pFile, int offset, int n, int flags); -static void mcIoShmBarrier(wx_sqlite3_file* pFile); -static int mcIoShmUnmap(wx_sqlite3_file* pFile, int deleteFlag); -static int mcIoFetch(wx_sqlite3_file* pFile, wx_sqlite3_int64 iOfst, int iAmt, void** pp); -static int mcIoUnfetch(wx_sqlite3_file* pFile, wx_sqlite3_int64 iOfst, void* p); +struct mz_internal_state; -#define SQLITE3MC_VFS_NAME ("multipleciphers") +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +MINIZ_EXPORT const char *mz_version(void); + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); + +/* Initializes a decompressor. */ +MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ +MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +MINIZ_EXPORT const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflateReset mz_inflateReset +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define uncompress2 mz_uncompress2 +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ -/* -** Header sizes of WAL journal files -*/ -static const int walFrameHeaderSize = 24; -static const int walFileHeaderSize = 32; +#ifdef __cplusplus +} +#endif -/* -** Global VFS structure of SQLite3 Multiple Ciphers VFS -*/ -static wx_sqlite3mc_vfs mcVfsGlobal = + + + + + +#include +#include +#include +#include + + + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag { - { - 3, /* iVersion */ - 0, /* szOsFile */ - 1024, /* mxPathname */ - 0, /* pNext */ - SQLITE3MC_VFS_NAME, /* zName */ - 0, /* pAppData */ - mcVfsOpen, /* xOpen */ - mcVfsDelete, /* xDelete */ - mcVfsAccess, /* xAccess */ - mcVfsFullPathname, /* xFullPathname */ - mcVfsDlOpen, /* xDlOpen */ - mcVfsDlError, /* xDlError */ - mcVfsDlSym, /* xDlSym */ - mcVfsDlClose, /* xDlClose */ - mcVfsRandomness, /* xRandomness */ - mcVfsSleep, /* xSleep */ - mcVfsCurrentTime, /* xCurrentTime */ - mcVfsGetLastError, /* xGetLastError */ - mcVfsCurrentTimeInt64, /* xCurrentTimeInt64 */ - mcVfsSetSystemCall, /* xSetSystemCall */ - mcVfsGetSystemCall, /* xGetSystemCall */ - mcVfsNextSystemCall /* xNextSystemCall */ - }, - NULL + int m_dummy; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t +#endif + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); +extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF }; -static wx_sqlite3_io_methods mcIoMethodsGlobal = +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum { - 3, /* iVersion */ - mcIoClose, /* xClose */ - mcIoRead, /* xRead */ - mcIoWrite, /* xWrite */ - mcIoTruncate, /* xTruncate */ - mcIoSync, /* xSync */ - mcIoFileSize, /* xFileSize */ - mcIoLock, /* xLock */ - mcIoUnlock, /* xUnlock */ - mcIoCheckReservedLock, /* xCheckReservedLock */ - mcIoFileControl, /* xFileControl */ - mcIoSectorSize, /* xSectorSize */ - mcIoDeviceCharacteristics, /* xDeviceCharacteristics */ - mcIoShmMap, /* xShmMap */ - mcIoShmLock, /* xShmLock */ - mcIoShmBarrier, /* xShmBarrier */ - mcIoShmUnmap, /* xShmUnmap */ - mcIoFetch, /* xFetch */ - mcIoUnfetch, /* xUnfetch */ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; -/* -** Internal functions -*/ +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; -/* -** Add an item to the list of main database files, if it is not already present. -*/ -static void mcMainListAdd(wx_sqlite3mc_file* pFile) +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum { - assert( (pFile->openFlags & SQLITE_OPEN_MAIN_DB) ); - wx_sqlite3_mutex_enter(mcVfsGlobal.mutex); - pFile->pMainNext = mcVfsGlobal.pMain; - mcVfsGlobal.pMain = pFile; - wx_sqlite3_mutex_leave(mcVfsGlobal.mutex); -} + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif -/* -** Remove an item from the list of main database files. -*/ -static void mcMainListRemove(wx_sqlite3mc_file* pFile) +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct { - wx_sqlite3mc_file** pMainPrev; - wx_sqlite3_mutex_enter(mcVfsGlobal.mutex); - for (pMainPrev = &mcVfsGlobal.pMain; *pMainPrev && *pMainPrev != pFile; pMainPrev = &((*pMainPrev)->pMainNext)){} - if (*pMainPrev) *pMainPrev = pFile->pMainNext; - pFile->pMainNext = 0; - wx_sqlite3_mutex_leave(mcVfsGlobal.mutex); + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); +MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); +#endif + +#ifdef __cplusplus } +#endif + -/* -** Given that zFileName points to a buffer containing a database file name passed to -** either the xOpen() or xAccess() VFS method, search the list of main database files -** for a file handle opened by the same database connection on the corresponding -** database file. -*/ -static wx_sqlite3mc_file* mcFindDbMainFileName(wx_sqlite3mc_vfs* mcVfs, const char* zFileName) +/* ------------------- Low-level Decompression API Definitions */ + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum { - wx_sqlite3mc_file* pDb; - wx_sqlite3_mutex_enter(mcVfs->mutex); - for (pDb = mcVfs->pMain; pDb && pDb->zFileName != zFileName; pDb = pDb->pMainNext){} - wx_sqlite3_mutex_leave(mcVfs->mutex); - return pDb; -} + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; -/* -** Find the codec of the database file -** corresponding to the database schema name. -*/ -SQLITE_PRIVATE Codec* wx_sqlite3mcGetCodec(wx_sqlite3* db, const char* zDbName) +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); +MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); +#endif + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum { - Codec* codec = NULL; - const char* dbFileName = wx_sqlite3_db_filename(db, zDbName); - wx_sqlite3mc_file* pDbMain = mcFindDbMainFileName(&mcVfsGlobal, dbFileName); - if (pDbMain) - { - codec = pDbMain->codec; - } - return codec; -} + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; -/* -** Find the codec of the main database file. -*/ -SQLITE_PRIVATE Codec* wx_sqlite3mcGetMainCodec(wx_sqlite3* db) +typedef struct { - return wx_sqlite3mcGetCodec(db, "main"); -} + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; -/* -** Set the codec of the database file with the given database file name. -** -** The parameter db, the handle of the database connection, is currently -** not used to determine the database file handle, for which the codec -** should be set. The reason is that for shared cache mode the database -** connection handle is not unique, and it is not even clear which -** connection handle is actually valid, because the association between -** connection handles and database file handles is not maintained properly. -*/ -SQLITE_PRIVATE void wx_sqlite3mcSetCodec(wx_sqlite3* db, const char* zFileName, Codec* codec) +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag { - wx_sqlite3mc_file* pDbMain = mcFindDbMainFileName(&mcVfsGlobal, zFileName); - if (pDbMain) - { - if (pDbMain->codec) - { - /* - ** Free a codec that was already associated with this main database file handle - */ - wx_sqlite3mcCodecFree(pDbMain->codec); - } - pDbMain->codec = codec; - if (codec) - { - mcReportCodecError(wx_sqlite3mcGetBtShared(codec), SQLITE_OK); - } - } - else - { - /* - ** No main database file handle found, free codec - */ - wx_sqlite3mcCodecFree(codec); - } + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus } +#endif + -/* -** Implementation of VFS methods -*/ -static int mcVfsOpen(wx_sqlite3_vfs* pVfs, const char* zName, wx_sqlite3_file* pFile, int flags, int* pOutFlags) + +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum { - int rc; - wx_sqlite3mc_vfs* mcVfs = (wx_sqlite3mc_vfs*) pVfs; - wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; - mcFile->pFile = (wx_sqlite3_file*) &mcFile[1]; - mcFile->openFlags = flags; - mcFile->zFileName = zName; - mcFile->codec = 0; - mcFile->pMainDb = 0; - mcFile->pMainNext = 0; - mcFile->pageNo = 0; + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; - if (zName) - { - if (flags & SQLITE_OPEN_MAIN_DB) - { - mcFile->zFileName = zName; - SQLITE3MC_DEBUG_LOG("mcVfsOpen MAIN: mcFile=%p fileName=%s\n", mcFile, mcFile->zFileName); - } - else if (flags & SQLITE_OPEN_TEMP_DB) - { - mcFile->zFileName = zName; - SQLITE3MC_DEBUG_LOG("mcVfsOpen TEMP: mcFile=%p fileName=%s\n", mcFile, mcFile->zFileName); - } -#if 0 - else if (flags & SQLITE_OPEN_TRANSIENT_DB) - { - /* - ** TODO: When does SQLite open a transient DB? Could/Should it be encrypted? - */ - } +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; + + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; + + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + +#ifndef MINIZ_NO_TIME + MZ_TIME_T m_time; +#endif + + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; + + /* File's compressed size. */ + mz_uint64 m_comp_size; + + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; + + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; + + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; + + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; + + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, + /*After adding a compressed file, seek back + to local file header and set the correct sizes*/ + MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct +{ + mz_zip_archive *pZip; + mz_uint flags; + + int status; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32; #endif - else if (flags & SQLITE_OPEN_MAIN_JOURNAL) - { - const char* dbFileName = wx_sqlite3_filename_database(zName); - mcFile->pMainDb = mcFindDbMainFileName(&mcVfsGlobal, dbFileName); - mcFile->zFileName = zName; - SQLITE3MC_DEBUG_LOG("mcVfsOpen MAIN Journal: mcFile=%p fileName=%s dbFileName=%s\n", mcFile, mcFile->zFileName, dbFileName); - } + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +} mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif + #if 0 - else if (flags & SQLITE_OPEN_TEMP_JOURNAL) - { - /* - ** TODO: When does SQLite open a temporary journal? Could/Should it be encrypted? - */ - } +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); #endif - else if (flags & SQLITE_OPEN_SUBJOURNAL) + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ +/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ +MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a poiner to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINIZ_NO_ARCHIVE_APIS */ +/*** End of #include "miniz.h" ***/ + +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ + + + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API's */ + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) { - const char* dbFileName = wx_sqlite3_filename_database(zName); - mcFile->pMainDb = mcFindDbMainFileName(&mcVfsGlobal, dbFileName); - mcFile->zFileName = zName; - SQLITE3MC_DEBUG_LOG("mcVfsOpen SUB Journal: mcFile=%p fileName=%s dbFileName=%s\n", mcFile, mcFile->zFileName, dbFileName); + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; } + return (s2 << 16) + s1; +} + +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ #if 0 - else if (flags & SQLITE_OPEN_MASTER_JOURNAL) + mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { - /* - ** Master journal contains only administrative information - ** No encryption necessary - */ + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) + { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; } -#endif - else if (flags & SQLITE_OPEN_WAL) +#elif defined(USE_EXTERNAL_MZCRC) +/* If USE_EXTERNAL_CRC is defined, an external module will export the + * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. + * Depending on the impl, it may be necessary to ~ the input/output crc values. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); +#else +/* Faster, but larger CPU cache footprint. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; + const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; + + while (buf_len >= 4) { - const char* dbFileName = wx_sqlite3_filename_database(zName); - mcFile->pMainDb = mcFindDbMainFileName(&mcVfsGlobal, dbFileName); - mcFile->zFileName = zName; - SQLITE3MC_DEBUG_LOG("mcVfsOpen WAL Journal: mcFile=%p fileName=%s dbFileName=%s\n", mcFile, mcFile->zFileName, dbFileName); + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; + pByte_buf += 4; + buf_len -= 4; } - } - rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, mcFile->pFile, flags, pOutFlags); - if (rc == SQLITE_OK) - { - /* - ** Real open succeeded, initialize methods, register main database files - */ - pFile->pMethods = &mcIoMethodsGlobal; - if (flags & SQLITE_OPEN_MAIN_DB) + while (buf_len) { - mcMainListAdd(mcFile); + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + ++pByte_buf; + --buf_len; } - } - return rc; + + return ~crc32; } +#endif -static int mcVfsDelete(wx_sqlite3_vfs* pVfs, const char* zName, int syncDir) +void mz_free(void *p) { - return REALVFS(pVfs)->xDelete(REALVFS(pVfs), zName, syncDir); + MZ_FREE(p); } -static int mcVfsAccess(wx_sqlite3_vfs* pVfs, const char* zName, int flags, int* pResOut) +MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) { - return REALVFS(pVfs)->xAccess(REALVFS(pVfs), zName, flags, pResOut); + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); } - -static int mcVfsFullPathname(wx_sqlite3_vfs* pVfs, const char* zName, int nOut, char* zOut) +MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) { - return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zName, nOut, zOut); + (void)opaque, (void)address; + MZ_FREE(address); } - -static void* mcVfsDlOpen(wx_sqlite3_vfs* pVfs, const char* zFilename) +MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) { - return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zFilename); + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); } -static void mcVfsDlError(wx_sqlite3_vfs* pVfs, int nByte, char* zErrMsg) +const char *mz_version(void) { - REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg); + return MZ_VERSION; } -static void (*mcVfsDlSym(wx_sqlite3_vfs* pVfs, void* p, const char* zSymbol))(void) +#ifndef MINIZ_NO_ZLIB_APIS + +int mz_deflateInit(mz_streamp pStream, int level) { - return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSymbol); + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } -static void mcVfsDlClose(wx_sqlite3_vfs* pVfs, void* p) +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { - REALVFS(pVfs)->xDlClose(REALVFS(pVfs), p); + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; } -static int mcVfsRandomness(wx_sqlite3_vfs* pVfs, int nByte, char* zOut) +int mz_deflateReset(mz_streamp pStream) { - return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zOut); + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; } -static int mcVfsSleep(wx_sqlite3_vfs* pVfs, int microseconds) +int mz_deflate(mz_streamp pStream, int flush) { - return REALVFS(pVfs)->xSleep(REALVFS(pVfs), microseconds); + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; } -static int mcVfsCurrentTime(wx_sqlite3_vfs* pVfs, double* pOut) +int mz_deflateEnd(mz_streamp pStream) { - return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pOut); + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; } -static int mcVfsGetLastError(wx_sqlite3_vfs* pVfs, int code, char* pOut) +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { - return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), code, pOut); + (void)pStream; + /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } -static int mcVfsCurrentTimeInt64(wx_sqlite3_vfs* pVfs, wx_sqlite3_int64* pOut) +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) { - return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), pOut); + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); } -static int mcVfsSetSystemCall(wx_sqlite3_vfs* pVfs, const char* zName, wx_sqlite3_syscall_ptr pNewFunc) +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { - return REALVFS(pVfs)->xSetSystemCall(REALVFS(pVfs), zName, pNewFunc); + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } -static wx_sqlite3_syscall_ptr mcVfsGetSystemCall(wx_sqlite3_vfs* pVfs, const char* zName) +mz_ulong mz_compressBound(mz_ulong source_len) { - return REALVFS(pVfs)->xGetSystemCall(REALVFS(pVfs), zName); + return mz_deflateBound(NULL, source_len); } -static const char* mcVfsNextSystemCall(wx_sqlite3_vfs* pVfs, const char* zName) +typedef struct { - return REALVFS(pVfs)->xNextSystemCall(REALVFS(pVfs), zName); + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } -/* -** IO methods -*/ - -static int mcIoClose(wx_sqlite3_file* pFile) +int mz_inflateReset(mz_streamp pStream) { - int rc; - wx_sqlite3mc_file* p = (wx_sqlite3mc_file*) pFile; + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; - /* - ** Unregister main database files - */ - if (p->openFlags & SQLITE_OPEN_MAIN_DB) - { - mcMainListRemove(p); - } + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; - /* - ** Release codec memory - */ - if (p->codec) - { - wx_sqlite3mcCodecFree(p->codec); - p->codec = 0; - } + pDecomp = (inflate_state *)pStream->state; - assert(p->pMainNext == 0 && mcVfsGlobal.pMain != p); - rc = REALFILE(pFile)->pMethods->xClose(REALFILE(pFile)); - return rc; + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + /* pDecomp->m_window_bits = window_bits */; + + return MZ_OK; } -/* -** Read operation on main database file -*/ -static int mcReadMainDb(wx_sqlite3_file* pFile, void* buffer, int count, wx_sqlite3_int64 offset) +int mz_inflate(mz_streamp pStream, int flush) { - int rc = SQLITE_OK; - wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; - - /* - ** Special case: read 16 bytes salt from beginning of database file without decrypting - */ - if (offset == 0 && count == 16) - { - return rc; - } + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; - if (mcFile->codec != 0 && wx_sqlite3mcIsEncrypted(mcFile->codec)) - { - const int pageSize = wx_sqlite3mcGetPageSize(mcFile->codec); - const int deltaOffset = offset % pageSize; - const int deltaCount = count % pageSize; - if (deltaOffset || deltaCount) + if (pState->m_dict_avail) { - /* - ** Read partial page - */ - int pageNo = 0; - void* bufferDecrypted = 0; - const wx_sqlite3_int64 prevOffset = offset - deltaOffset; - unsigned char* pageBuffer = wx_sqlite3mcGetPageBuffer(mcFile->codec); + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } - /* - ** Read complete page from file - */ - rc = REALFILE(pFile)->pMethods->xRead(REALFILE(pFile), pageBuffer, pageSize, prevOffset); - if (rc == SQLITE_IOERR_SHORT_READ) - { - return rc; - } + for (;;) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) + { + /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } - /* - ** Determine page number and decrypt page buffer - */ - pageNo = prevOffset / pageSize + 1; - bufferDecrypted = wx_sqlite3mcCodec(mcFile->codec, pageBuffer, pageNo, 3); + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} - /* - ** Return the requested content - */ - if (deltaOffset) - { - memcpy(buffer, pageBuffer + deltaOffset, count); - } - else - { - memcpy(buffer, pageBuffer, count); - } +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; } - else + return MZ_OK; +} +int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((*pSource_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)*pSource_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + *pSource_len = *pSource_len - stream.avail_in; + if (status != MZ_STREAM_END) { - /* - ** Read full page(s) - ** - ** In fact, SQLite reads only one database page at a time. - ** This would allow to remove the page loop below. - */ - unsigned char* data = (unsigned char*) buffer; - int pageNo = offset / pageSize + 1; - int nPages = count / pageSize; - int iPage; - for (iPage = 0; iPage < nPages; ++iPage) - { - void* bufferDecrypted = wx_sqlite3mcCodec(mcFile->codec, data, pageNo, 3); - data += pageSize; - offset += pageSize; - ++pageNo; - } + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } - } - return rc; + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_uncompress2(pDest, pDest_len, pSource, &source_len); +} + +const char *mz_error(int err) +{ + static struct + { + int m_err; + const char *m_pDesc; + } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; } +#endif /*MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + /* -** Read operation on main journal file + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + 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 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. + + For more information, please refer to */ -static int mcReadMainJournal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) -{ - int rc = SQLITE_OK; - wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; - Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ - if (codec != 0 && wx_sqlite3mcIsEncrypted(codec)) - { - const int pageSize = wx_sqlite3mcGetPageSize(codec); - if (count == pageSize && mcFile->pageNo != 0) + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Compression (independent from all decompression API's) */ + +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = { - /* - ** Decrypt the page buffer, but only if the page number is valid - */ - void* bufferDecrypted = wx_sqlite3mcCodec(codec, (char*) buffer, mcFile->pageNo, 3); - mcFile->pageNo = 0; + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 + }; + +static const mz_uint8 s_tdefl_len_extra[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 + }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = + { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct +{ + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) + { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; } - else if (count == 4) + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { - /* - ** SQLite always reads the page number from the journal file - ** immediately before the corresponding page content is read. - */ - mcFile->pageNo = wx_sqlite3Get4byte(buffer); + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) + { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } } - } - return rc; + return pCur_syms; } -/* -** Read operation on subjournal file -*/ -static int mcReadSubJournal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { - int rc = SQLITE_OK; - wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; - Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; - - if (codec != 0 && wx_sqlite3mcIsEncrypted(codec)) - { - const int pageSize = wx_sqlite3mcGetPageSize(codec); - - if (count == pageSize && mcFile->pageNo != 0) + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) { - /* - ** Decrypt the page buffer, but only if the page number is valid - */ - void* bufferDecrypted = wx_sqlite3mcCodec(codec, (char*) buffer, mcFile->pageNo, 3); + A[0].m_key = 1; + return; } - else if (count == 4) + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) { - /* - ** SQLite always reads the page number from the journal file - ** immediately before the corresponding page content is read. - */ - mcFile->pageNo = wx_sqlite3Get4byte(buffer); + if (leaf >= n || A[root].m_key < A[leaf].m_key) + { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) + { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) + { + used++; + root--; + } + while (avbl > used) + { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; } - } - return rc; } -/* -** Read operation on WAL journal file -*/ -static int mcReadWal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +/* Limits canonical Huffman code table's max code size. */ +enum { - int rc = SQLITE_OK; - wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; - Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; - - if (codec != 0 && wx_sqlite3mcIsEncrypted(codec)) - { - const int pageSize = wx_sqlite3mcGetPageSize(codec); - - if (count == pageSize) + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 +}; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) { - int pageNo = 0; - unsigned char ac[4]; - - /* - ** Determine page number - ** - ** It is necessary to explicitly read the page number from the frame header. - */ - rc = REALFILE(pFile)->pMethods->xRead(REALFILE(pFile), ac, 4, offset - walFrameHeaderSize); - if (rc == SQLITE_OK) - { - pageNo = wx_sqlite3Get4byte(ac); - } - - /* - ** Decrypt page content if page number is valid - */ - if (pageNo != 0) - { - void* bufferDecrypted = wx_sqlite3mcCodec(codec, (char*) buffer, pageNo, 3); - } + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) + { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; } - } - return rc; } -static int mcIoRead(wx_sqlite3_file* pFile, void* buffer, int count, wx_sqlite3_int64 offset) +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { - wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; - int rc = REALFILE(pFile)->pMethods->xRead(REALFILE(pFile), buffer, count, offset); - if (rc == SQLITE_IOERR_SHORT_READ) - { - return rc; - } + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_OBJ(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) + { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } - if (mcFile->openFlags & SQLITE_OPEN_MAIN_DB) - { - rc = mcReadMainDb(pFile, buffer, count, offset); - } -#if 0 - else if (mcFile->openFlags & SQLITE_OPEN_TEMP_DB) - { - /* - ** TODO: Could/Should a temporary database file be encrypted? - */ - } -#endif -#if 0 - else if (mcFile->openFlags & SQLITE_OPEN_TRANSIENT_DB) - { - /* - ** TODO: Could/Should a transient database file be encrypted? - */ - } -#endif - else if (mcFile->openFlags & SQLITE_OPEN_MAIN_JOURNAL) - { - rc = mcReadMainJournal(pFile, buffer, count, offset); - } -#if 0 - else if (mcFile->openFlags & SQLITE_OPEN_TEMP_JOURNAL) - { - /* - ** TODO: Could/Should a temporary journal file be encrypted? - */ - } -#endif - else if (mcFile->openFlags & SQLITE_OPEN_SUBJOURNAL) - { - rc = mcReadSubJournal(pFile, buffer, count, offset); - } -#if 0 - else if (mcFile->openFlags & SQLITE_OPEN_MASTER_JOURNAL) - { - /* - ** Master journal contains only administrative information - ** No encryption necessary - */ - } -#endif - else if (mcFile->openFlags & SQLITE_OPEN_WAL) - { - rc = mcReadWal(pFile, buffer, count, offset); - } - return rc; -} + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); -/* -** Write operation on main database file -*/ -static int mcWriteMainDb(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do \ + { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) \ + { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) \ + { \ + if (rle_repeat_count < 3) \ + { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } \ + else \ + { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) \ + { \ + if (rle_z_count < 3) \ + { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } \ + else if (rle_z_count <= 10) \ + { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } \ + else \ + { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) { - int rc = SQLITE_OK; - wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; - if (mcFile->codec != 0 && wx_sqlite3mcIsEncrypted(mcFile->codec)) - { - const int pageSize = wx_sqlite3mcGetPageSize(mcFile->codec); - const int deltaOffset = offset % pageSize; - const int deltaCount = count % pageSize; + d->m_huff_count[0][256] = 1; - if (deltaOffset || deltaCount) + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) { - /* - ** Write partial page - ** - ** SQLite does never write partial database pages. - ** Therefore no encryption is required in this case. - */ - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) + { + TDEFL_RLE_PREV_CODE_SIZE(); } else { - /* - ** Write full page(s) - ** - ** In fact, SQLite writes only one database page at a time. - ** This would allow to remove the page loop below. - */ - char* data = (char*) buffer; - int pageNo = offset / pageSize + 1; - int nPages = count / pageSize; - int iPage; - for (iPage = 0; iPage < nPages; ++iPage) - { - void* bufferEncrypted = wx_sqlite3mcCodec(mcFile->codec, data, pageNo, 6); - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), bufferEncrypted, pageSize, offset); - data += pageSize; - offset += pageSize; - ++pageNo; - } + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } - } - else - { - /* - ** Write buffer without encryption - */ - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); - } - return rc; } -/* -** Write operation on main journal file -*/ -static int mcWriteMainJournal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +static void tdefl_start_static_block(tdefl_compressor *d) { - int rc = SQLITE_OK; - wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; - Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; - if (codec != 0 && wx_sqlite3mcIsEncrypted(codec)) - { - const int pageSize = wx_sqlite3mcGetPageSize(codec); - const int frameSize = pageSize + 4 + 4; + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; - if (count == pageSize && mcFile->pageNo != 0) - { - /* - ** Encrypt the page buffer, but only if the page number is valid - */ - void* bufferEncrypted = wx_sqlite3mcCodec(codec, (char*) buffer, mcFile->pageNo, 7); - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), bufferEncrypted, pageSize, offset); + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ } - else + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { - /* - ** Write buffer without encryption - */ - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); - if (count == 4) - { - /* - ** SQLite always writes the page number to the journal file - ** immediately before the corresponding page content is written. - */ - mcFile->pageNo = (rc == SQLITE_OK) ? wx_sqlite3Get4byte(buffer) : 0; - } + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64 *)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; } - } - else - { - /* - ** Write buffer without encryption - */ - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); - } - return rc; -} -/* -** Write operation on subjournal file -*/ -static int mcWriteSubJournal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) -{ - int rc = SQLITE_OK; - wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; - Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; +#undef TDEFL_PUT_BITS_FAST - if (codec != 0 && wx_sqlite3mcIsEncrypted(codec)) - { - const int pageSize = wx_sqlite3mcGetPageSize(codec); - const int frameSize = pageSize + 4; + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; - if (count == pageSize && mcFile->pageNo != 0) + while (bits_in) { - /* - ** Encrypt the page buffer, but only if the page number is valid - */ - void* bufferEncrypted = wx_sqlite3mcCodec(codec, (char*) buffer, mcFile->pageNo, 7); - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), bufferEncrypted, pageSize, offset); + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; } - else + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { - /* - ** Write buffer without encryption - */ - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); - if (count == 4) - { - /* - ** SQLite always writes the page number to the journal file - ** immediately before the corresponding page content is written. - */ - mcFile->pageNo = (rc == SQLITE_OK) ? wx_sqlite3Get4byte(buffer) : 0; - } + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } } - } - else - { - /* - ** Write buffer without encryption - */ - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); - } - return rc; + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); } +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ -/* -** Write operation on WAL journal file -*/ -static int mcWriteWal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { - int rc = SQLITE_OK; - wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; - Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} - if (codec != 0 && wx_sqlite3mcIsEncrypted(codec)) - { - const int pageSize = wx_sqlite3mcGetPageSize(codec); +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; - if (count == pageSize) + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { - int pageNo = 0; - unsigned char ac[4]; + TDEFL_PUT_BITS(0x78, 8); + TDEFL_PUT_BITS(0x01, 8); + } - /* - ** Read the corresponding page number from the file - ** - ** In WAL mode SQLite does not write the page number of a page to file - ** immediately before writing the corresponding page content. - ** Page numbers and checksums are written to file independently. - ** Therefore it is necessary to explicitly read the page number - ** on writing to file the contetn of a page. - */ - rc = REALFILE(pFile)->pMethods->xRead(REALFILE(pFile), ac, 4, offset - walFrameHeaderSize); - if (rc == SQLITE_OK) - { - pageNo = wx_sqlite3Get4byte(ac); - } + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); - if (pageNo != 0) - { - /* - ** Encrypt the page buffer, but only if the page number is valid - */ - void* bufferEncrypted = wx_sqlite3mcCodec(codec, (char*) buffer, pageNo, 7); - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), bufferEncrypted, pageSize, offset); - } - else - { - /* - ** Write buffer without encryption if the page number could not be determined - */ - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); - } + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) + { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } } - else + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) { - /* - ** Write buffer without encryption if it is not a database page - */ - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); } - } - else - { - /* - ** Write buffer without encryption - */ - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); - } - return rc; + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) + { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) + { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } + else + { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) + { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; } -static int mcIoWrite(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) { - int rc = SQLITE_OK; - int doDefault = 1; - wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; - - if (mcFile->openFlags & SQLITE_OPEN_MAIN_DB) - { - rc = mcWriteMainDb(pFile, buffer, count, offset); - } -#if 0 - else if (mcFile->openFlags & SQLITE_OPEN_TEMP_DB) - { - /* - ** TODO: Could/Should a temporary database file be encrypted? - */ - } -#endif -#if 0 - else if (mcFile->openFlags & SQLITE_OPEN_TRANSIENT_DB) - { - /* - ** TODO: Could/Should a transient database file be encrypted? - */ - } -#endif - else if (mcFile->openFlags & SQLITE_OPEN_MAIN_JOURNAL) - { - rc = mcWriteMainJournal(pFile, buffer, count, offset); - } -#if 0 - else if (mcFile->openFlags & SQLITE_OPEN_TEMP_JOURNAL) - { - /* - ** TODO: Could/Should a temporary journal file be encrypted? - */ - } -#endif - else if (mcFile->openFlags & SQLITE_OPEN_SUBJOURNAL) - { - rc = mcWriteSubJournal(pFile, buffer, count, offset); + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; } -#if 0 - else if (mcFile->openFlags & SQLITE_OPEN_MASTER_JOURNAL) - { - /* - ** Master journal contains only administrative information - ** No encryption necessary - */ - } +static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) #endif - else if (mcFile->openFlags & SQLITE_OPEN_WAL) - { - rc = mcWriteWal(pFile, buffer, count, offset); - } - else - { - rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); - } - return rc; +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) + continue; + p = s; + probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + if (!probe_len) + { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); + break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } } - -static int mcIoTruncate(wx_sqlite3_file* pFile, wx_sqlite3_int64 size) +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { - return REALFILE(pFile)->pMethods->xTruncate(REALFILE(pFile), size); + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } } +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ -static int mcIoSync(wx_sqlite3_file* pFile, int flags) +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p) { - return REALFILE(pFile)->pMethods->xSync(REALFILE(pFile), flags); + mz_uint32 ret; + memcpy(&ret, p, sizeof(mz_uint32)); + return ret; } +#else +#define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) +#endif +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; -static int mcIoFileSize(wx_sqlite3_file* pFile, wx_sqlite3_int64* pSize) + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); +#else + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; +#endif + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { - return REALFILE(pFile)->pMethods->xFileSize(REALFILE(pFile), pSize); + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; } -static int mcIoLock(wx_sqlite3_file* pFile, int lock) +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { - return REALFILE(pFile)->pMethods->xLock(REALFILE(pFile), lock); + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } -static int mcIoUnlock(wx_sqlite3_file* pFile, int lock) +static mz_bool tdefl_compress_normal(tdefl_compressor *d) { - return REALFILE(pFile)->pMethods->xUnlock(REALFILE(pFile), lock); + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) + { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) + { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; } -static int mcIoCheckReservedLock(wx_sqlite3_file* pFile, int* pResOut) +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { - return REALFILE(pFile)->pMethods->xCheckReservedLock(REALFILE(pFile), pResOut); + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } -static int mcIoFileControl(wx_sqlite3_file* pFile, int op, void* pArg) +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { - int rc = SQLITE_OK; - int doReal = 1; - wx_sqlite3mc_file* p = (wx_sqlite3mc_file*) pFile; + if (!d) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); - switch (op) - { - case SQLITE_FCNTL_PDB: - { -#if 0 - /* - ** pArg points to the wx_sqlite3* handle for which the database file was opened. - ** In shared cache mode this function is invoked for every use of the database - ** file in a connection. Unfortunately there is no notification, when a database - ** file is no longer used by a connection (close in normal mode). - ** - ** For now, the database handle will not be stored in the file object. - ** In the future, this behaviour may be changed, especially, if shared cache mode - ** is disabled. Shared cache mode is enabled for backward compatibility only, its - ** use is not recommended. A future version of SQLite might disable it by default. - */ - wx_sqlite3* db = *((wx_sqlite3**) pArg); -#endif + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; } - break; - case SQLITE_FCNTL_PRAGMA: - { - /* - ** Handle pragmas specific to this database file - */ -#if 0 - /* - ** SQLite invokes this function for all pragmas, which are related to the schema - ** associated with this database file. In case of an unknown pragma, this function - ** should return SQLITE_NOTFOUND. However, since this VFS is just a shim, handling - ** of the pragma is forwarded to the underlying real VFS in such a case. - ** - ** For now, all pragmas are handled at the connection level. - ** For this purpose the SQLite's pragma handling is intercepted. - ** The latter requires a patch of SQLite's amalgamation code. - ** Maybe a future version will be able to abandon the patch. - */ - char* pragmaName = ((char**) pArg)[1]; - char* pragmaValue = ((char**) pArg)[2]; - if (wx_sqlite3StrICmp(pragmaName, "...") == 0) + else +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) { - /* Action */ - /* ((char**) pArg)[0] = wx_sqlite3_mprintf("error msg.");*/ - doReal = 0; + MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_OBJ(d->m_next); + d->m_dict_size = 0; } -#endif - } - break; - default: - break; - } - if (doReal) - { - rc = REALFILE(pFile)->pMethods->xFileControl(REALFILE(pFile), op, pArg); - } - return rc; + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } -static int mcIoSectorSize(wx_sqlite3_file* pFile) +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { - return REALFILE(pFile)->pMethods->xSectorSize(REALFILE(pFile)); + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } -static int mcIoDeviceCharacteristics(wx_sqlite3_file* pFile) +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { - return REALFILE(pFile)->pMethods->xDeviceCharacteristics(REALFILE(pFile)); + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + *d->m_pLZ_flags = 0; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_dict); + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; } -static int mcIoShmMap(wx_sqlite3_file* pFile, int iPg, int pgsz, int map, void volatile** p) +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { - return REALFILE(pFile)->pMethods->xShmMap(REALFILE(pFile), iPg, pgsz, map, p); + return d->m_adler32; } -static int mcIoShmLock(wx_sqlite3_file* pFile, int offset, int n, int flags) +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { - return REALFILE(pFile)->pMethods->xShmLock(REALFILE(pFile), offset, n, flags); -} + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; -static void mcIoShmBarrier(wx_sqlite3_file* pFile) +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { - REALFILE(pFile)->pMethods->xShmBarrier(REALFILE(pFile)); + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do + { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; } -static int mcIoShmUnmap(wx_sqlite3_file* pFile, int deleteFlag) +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { - return REALFILE(pFile)->pMethods->xShmUnmap(REALFILE(pFile), deleteFlag); + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; } -static int mcIoFetch(wx_sqlite3_file* pFile, wx_sqlite3_int64 iOfst, int iAmt, void** pp) +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { - return REALFILE(pFile)->pMethods->xFetch(REALFILE(pFile), iOfst, iAmt, pp); + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; } -static int mcIoUnfetch( wx_sqlite3_file* pFile, wx_sqlite3_int64 iOfst, void* p) +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { - return REALFILE(pFile)->pMethods->xUnfetch(REALFILE(pFile), iOfst, p); + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; } -/* -** SQLite3 Multiple Ciphers external API functions -*/ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ +#endif -static void mcVfsDestroy(wx_sqlite3_vfs* pVfs) +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) { - if (pVfs && pVfs->xOpen == mcVfsOpen) - { - /* Destroy the VFS instance only if no file is referring to it any longer */ - if (((wx_sqlite3mc_vfs*) pVfs)->pMain == 0) + /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) { - wx_sqlite3_mutex_free(((wx_sqlite3mc_vfs*)pVfs)->mutex); - wx_sqlite3_vfs_unregister(pVfs); - wx_sqlite3_free(pVfs); + MZ_FREE(pComp); + return NULL; + } + /* write dummy header */ + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + /* compress image data */ + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) + { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } - } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) + { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + /* write real header */ + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; + mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, + 0x0a, 0x1a, 0x0a, 0x00, 0x00, + 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x44, 0x41, + 0x54 }; + pnghdr[18] = (mz_uint8)(w >> 8); + pnghdr[19] = (mz_uint8)w; + pnghdr[22] = (mz_uint8)(h >> 8); + pnghdr[23] = (mz_uint8)h; + pnghdr[25] = chans[num_chans]; + pnghdr[33] = (mz_uint8)(*pLen_out >> 24); + pnghdr[34] = (mz_uint8)(*pLen_out >> 16); + pnghdr[35] = (mz_uint8)(*pLen_out >> 8); + pnghdr[36] = (mz_uint8)*pLen_out; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + /* write footer (IDAT CRC-32, followed by IEND chunk) */ + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) + { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + /* compute final size of file, grab compressed data buffer and return */ + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); } -/* -** Unregister and destroy a Multiple Ciphers VFS -** created by an earlier call to wx_sqlite3mc_vfs_create(). -*/ -SQLITE_API void wx_sqlite3mc_vfs_destroy(const char* zName) +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor *tdefl_compressor_alloc() { - mcVfsDestroy(wx_sqlite3_vfs_find(zName)); + return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); } -/* -** Create a Multiple Ciphers VFS based on the underlying VFS with name given by zVfsReal. -** If makeDefault is true, the VFS is set as the default VFS. -*/ -SQLITE_API int wx_sqlite3mc_vfs_create(const char* zVfsReal, int makeDefault) +void tdefl_compressor_free(tdefl_compressor *pComp) { - static wx_sqlite3_vfs mcVfsTemplate = - { - 3, /* iVersion */ - 0, /* szOsFile */ - 1024, /* mxPathname */ - 0, /* pNext */ - 0, /* zName */ - 0, /* pAppData */ - mcVfsOpen, /* xOpen */ - mcVfsDelete, /* xDelete */ - mcVfsAccess, /* xAccess */ - mcVfsFullPathname, /* xFullPathname */ -#ifndef SQLITE_OMIT_LOAD_EXTENSION - mcVfsDlOpen, /* xDlOpen */ - mcVfsDlError, /* xDlError */ - mcVfsDlSym, /* xDlSym */ - mcVfsDlClose, /* xDlClose */ + MZ_FREE(pComp); +} +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __cplusplus +} +#endif + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Decompression (completely independent from all compression API's) */ + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) \ + { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do \ + { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do \ + { \ + for (;;) \ + { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +#define TINFL_GET_BYTE(state_index, c) \ + do \ + { \ + while (pIn_buf_cur >= pIn_buf_end) \ + { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do \ + { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do \ + { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) \ + { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } \ + else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ + do \ + { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) \ + { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) \ + { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } \ + else \ + { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) + { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) + { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) + { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) + { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) + { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) + { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) + { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; + tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; + pTable = &r->m_tables[r->m_type]; + MZ_CLEAR_OBJ(total_syms); + MZ_CLEAR_OBJ(pTable->m_look_up); + MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) + { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) + { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) + { + pTable->m_look_up[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + { + pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) + { + pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + else + tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); + pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) + { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); + if (dist < 16) + { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for (;;) + { + mz_uint8 *pSrc; + for (;;) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } #else - 0, 0, 0, 0, + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } #endif - mcVfsRandomness, /* xRandomness */ - mcVfsSleep, /* xSleep */ - mcVfsCurrentTime, /* xCurrentTime */ - mcVfsGetLastError, /* xGetLastError */ - mcVfsCurrentTimeInt64, /* xCurrentTimeInt64 */ - mcVfsSetSystemCall, /* xSetSystemCall */ - mcVfsGetSystemCall, /* xGetSystemCall */ - mcVfsNextSystemCall /* xNextSystemCall */ - }; - wx_sqlite3mc_vfs* pVfsNew = 0; /* Newly allocated VFS */ - wx_sqlite3_vfs* pVfsReal = wx_sqlite3_vfs_find(zVfsReal); /* Real VFS */ - int rc; + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; - if (pVfsReal) - { - size_t nPrefix = strlen(SQLITE3MC_VFS_NAME); - size_t nRealName = strlen(pVfsReal->zName); - size_t nName = nPrefix + nRealName + 1; - size_t nByte = sizeof(wx_sqlite3mc_vfs) + nName + 1; - pVfsNew = (wx_sqlite3mc_vfs*) wx_sqlite3_malloc64(nByte); - if (pVfsNew) + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2); +#else + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; +#endif + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + while(counter>2) + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + counter -= 3; + } + if (counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { - char* zSpace = (char*) &pVfsNew[1]; - memset(pVfsNew, 0, nByte); - memcpy(&pVfsNew->base, &mcVfsTemplate, sizeof(wx_sqlite3_vfs)); - pVfsNew->base.iVersion = pVfsReal->iVersion; - pVfsNew->base.pAppData = pVfsReal; - pVfsNew->base.mxPathname = pVfsReal->mxPathname; - pVfsNew->base.szOsFile = sizeof(wx_sqlite3mc_file) + pVfsReal->szOsFile; + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ - /* Set name of new VFS as combination of the multiple ciphers prefix and the name of the underlying VFS */ - pVfsNew->base.zName = (const char*) zSpace; - memcpy(zSpace, SQLITE3MC_VFS_NAME, nPrefix); - memcpy(zSpace + nPrefix, "-", 1); - memcpy(zSpace + nPrefix + 1, pVfsReal->zName, nRealName); + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + for (counter = 0; counter < 4; ++counter) + { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); - /* Allocate the mutex and register the new VFS */ - pVfsNew->mutex = wx_sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE); - if (pVfsNew->mutex) - { - rc = wx_sqlite3_vfs_register(&pVfsNew->base, makeDefault); - if (rc != SQLITE_OK) + TINFL_CR_FINISH + +common_exit: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) + { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { - wx_sqlite3_mutex_free(pVfsNew->mutex); + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; } - } - else - { - /* Mutex could not be allocated */ - rc = SQLITE_NOMEM; - } - if (rc != SQLITE_OK) - { - /* Mutex could not be allocated or new VFS could not be registered */ - wx_sqlite3_free(pVfsNew); - } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; } - else + return status; +} + +/* Higher level helper functions. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) { - /* New VFS could not be allocated */ - rc = SQLITE_NOMEM; + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; } - } - else - { - /* Underlying VFS not found */ - rc = SQLITE_NOTFOUND; - } - return rc; + return pBuf; } -/* -** Shutdown all registered SQLite3 Multiple Ciphers VFSs -*/ -SQLITE_API void wx_sqlite3mc_vfs_shutdown() +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { - wx_sqlite3_vfs* pVfs; - wx_sqlite3_vfs* pVfsNext; - for (pVfs = wx_sqlite3_vfs_find(0); pVfs; pVfs = pVfsNext) - { - pVfsNext = pVfs->pNext; - mcVfsDestroy(pVfs); - } + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } -/*** End of #include "wx_sqlite3mc_vfs.c" ***/ +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for (;;) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} -static int -mcRegisterCodecExtensions(wx_sqlite3* db, char** pzErrMsg, const wx_sqlite3_api_routines* pApi) +#ifndef MINIZ_NO_MALLOC +tinfl_decompressor *tinfl_decompressor_alloc() { - int rc = SQLITE_OK; - CodecParameter* codecParameterTable = NULL; + tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); + if (pDecomp) + tinfl_init(pDecomp); + return pDecomp; +} - if (wx_sqlite3FindFunction(db, "wx_sqlite3mc_config_table", 1, SQLITE_UTF8, 0) != NULL) - { - /* Return if codec extension functions are already defined */ - return rc; - } +void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +{ + MZ_FREE(pDecomp); +} +#endif - /* Generate copy of global codec parameter table */ - codecParameterTable = wx_sqlite3mcCloneCodecParameterTable(); - rc = (codecParameterTable != NULL) ? SQLITE_OK : SQLITE_NOMEM; - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_create_function_v2(db, "wx_sqlite3mc_config_table", 0, SQLITE_UTF8 | SQLITE_DETERMINISTIC, - codecParameterTable, wx_sqlite3mcConfigTable, 0, 0, (void(*)(void*)) wx_sqlite3mcFreeCodecParameterTable); - } +#ifdef __cplusplus +} +#endif + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ - rc = (codecParameterTable != NULL) ? SQLITE_OK : SQLITE_NOMEM; - if (rc == SQLITE_OK) + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- .ZIP archive reading */ + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include + +#if defined(_MSC_VER) || defined(__MINGW64__) +static FILE *mz_fopen(const char *pFilename, const char *pMode) +{ + FILE *pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; +} +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) +{ + FILE *pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) + return NULL; + return pFile; +} +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat64 +#define MZ_FILE_STAT _stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove +#elif defined(__MINGW32__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__USE_LARGEFILE64) /* gcc, clang */ +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__APPLE__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +#define MZ_DELETE_FILE remove + +#else +#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#ifdef __STRICT_ANSI__ +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#else +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif /* #ifdef _MSC_VER */ +#endif /* #ifdef MINIZ_NO_STDIO */ + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +enum +{ + /* ZIP archive identifiers and record sizes */ + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + /* Central directory header record offsets */ + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + + /* Local directory header offsets */ + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + + /* End of central directory offsets */ + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + uint32_t m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +#if defined(DEBUG) || defined(_DEBUG) +static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) +{ + MZ_ASSERT(index < pArray->m_size); + return index; +} +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +#else +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] +#endif + +static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +{ + memset(pArray, 0, sizeof(mz_zip_array)); + pArray->m_element_size = element_size; +} + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) + { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + if (n > 0) + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +{ + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) +{ + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + pZip->m_pState->m_init_flags = flags; + pZip->m_pState->m_zip64 = MZ_FALSE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do \ + { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices; + mz_uint32 start, end; + const mz_uint32 size = pZip->m_total_files; + + if (size <= 1U) + return; + + pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + + start = (size - 2U) >> 1U; + for (;;) + { + mz_uint64 child, root = start; + for (;;) + { + if ((child = (root << 1U) + 1U) >= size) + break; + child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + if (!start) + break; + start--; + } + + end = size - 1; + while (end > 0) + { + mz_uint64 child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) + { + if ((child = (root << 1U) + 1U) >= end) + break; + child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +{ + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) + { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) + { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + + if (i >= 0) + { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +{ + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) + { + zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) + { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) + { + mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data; + void* buf = NULL; + + if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n) + { + buf = MZ_MALLOC(ext_data_size); + if(buf==NULL) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (mz_uint8*)buf; + } + else + { + pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + } + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + + MZ_FREE(buf); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) + { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) + { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +void mz_zip_zero_struct(mz_zip_archive *pZip) +{ + if (pZip) + MZ_CLEAR_OBJ(*pZip); +} + +static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_bool status = MZ_TRUE; + + if (!pZip) + return MZ_FALSE; + + if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + + return MZ_FALSE; + } + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; + status = MZ_FALSE; + } + } + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return status; +} + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + return mz_zip_reader_end_internal(pZip, MZ_TRUE); +} +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_archive_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +{ + if (!pMem) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pNeeds_keepalive = NULL; + +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + + pZip->m_pState->m_mem_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +} + +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) +{ + mz_uint64 file_size; + MZ_FILE *pFile; + + if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + file_size = archive_size; + if (!file_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + } + + file_size = MZ_FTELL64(pFile); + } + + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) +{ + mz_uint64 cur_file_ofs; + + if ((!pZip) || (!pFile)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + cur_file_ofs = MZ_FTELL64(pFile); + + if (!archive_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + + archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + + if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = archive_size; + pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +} + +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint bit_flag; + mz_uint method; + + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); + bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + + if ((method != 0) && (method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + return MZ_FALSE; + } + + if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + return MZ_FALSE; + } + + if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, attribute_mapping_id, external_attr; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ + attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; + (void)attribute_mapping_id; + + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) + { + return MZ_TRUE; + } + + return MZ_FALSE; +} + +static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +{ + mz_uint n; + const mz_uint8 *p = pCentral_dir_header; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_FALSE; + + if ((!p) || (!pStat)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Extract fields from the central directory record. */ + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + /* Copy as much of the filename and comment as possible. */ + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); + pStat->m_comment[n] = '\0'; + + /* Set some flags for convienance */ + pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); + pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); + pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + + /* See if we need to read any zip64 extended information fields. */ + /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ + if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; + mz_uint32 field_data_remaining = field_data_size; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_TRUE; + + if (pStat->m_uncomp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_uncomp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_comp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_comp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_local_header_ofs == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const uint32_t size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + + if (pIndex) + *pIndex = 0; + + if (size) + { + /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ + /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ + mz_int64 l = 0, h = (mz_int64)size - 1; + + while (l <= h) + { + mz_int64 m = l + ((h - l) >> 1); + uint32_t file_index = pIndices[(uint32_t)m]; + + int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint32 index; + if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) + return -1; + else + return (int)index; +} + +mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +{ + mz_uint file_index; + size_t name_len, comment_len; + + if (pIndex) + *pIndex = 0; + + if ((!pZip) || (!pZip->m_pState) || (!pName)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* See if we can use a binary search */ + if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && + (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && + ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + { + return mz_zip_locate_file_binary_search(pZip, pName, pIndex); + } + + /* Locate the entry by scanning the entire central directory */ + name_len = strlen(pName); + if (name_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Ensure supplied output buffer is large enough. */ + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + + /* Read and parse the local directory entry. */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) + { + if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + } +#endif + + return MZ_TRUE; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + /* Read directly from the archive in memory. */ + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + /* Use a user provided read buffer. */ + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + /* Temporarily allocate a read buffer. */ + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return NULL; + } + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + return NULL; + } + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32 = MZ_CRC32_INIT; +#endif + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pState->m_pMem) + { + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + } + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +#endif + } + + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + } +#endif + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + status = TINFL_STATUS_FAILED; + } + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +#endif + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (file_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_reader_extract_iter_state *pState; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + /* Argument sanity check */ + if ((!pZip) || (!pZip->m_pState)) + return NULL; + + /* Allocate an iterator status structure */ + pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); + if (!pState) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + /* Fetch file details */ + if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Encryption and patch files are not supported. */ + if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Init state - save args */ + pState->pZip = pZip; + pState->flags = flags; + + /* Init state - reset variables to defaults */ + pState->status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + pState->file_crc32 = MZ_CRC32_INIT; +#endif + pState->read_buf_ofs = 0; + pState->out_buf_ofs = 0; + pState->pRead_buf = NULL; + pState->pWrite_buf = NULL; + pState->out_blk_remain = 0; + + /* Read and parse the local directory entry. */ + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; + pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + else + { + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, therefore intermediate read buffer required */ + pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + else + { + /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ + pState->read_buf_size = 0; + } + pState->read_buf_avail = 0; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, init decompressor */ + tinfl_init( &pState->inflator ); + + /* Allocate write buffer */ + if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + if (pState->pRead_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + + return pState; +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_uint32 file_index; + + /* Locate file index by name */ + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return NULL; + + /* Construct iterator */ + return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +} + +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +{ + size_t copied_to_caller = 0; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) + return 0; + + if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data, calc amount to return. */ + copied_to_caller = (size_t)MZ_MIN( buf_size, pState->comp_remaining ); + + /* Zip is in memory....or requires reading from a file? */ + if (pState->pZip->m_pState->m_pMem) + { + /* Copy data to caller's buffer */ + memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); + pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; + } + else + { + /* Read directly into caller's buffer */ + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) + { + /* Failed to read all that was asked for, flag failure and alert user */ + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + copied_to_caller = 0; + } + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Compute CRC if not returning compressed data only */ + if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +#endif + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += copied_to_caller; + pState->out_buf_ofs += copied_to_caller; + pState->comp_remaining -= copied_to_caller; + } + else + { + do + { + /* Calc ptr to write buffer - given current output pos and block size */ + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + /* Calc max output size - given current output pos and block size */ + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + if (!pState->out_blk_remain) + { + /* Read more data from file if none available (and reading from file) */ + if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) + { + /* Calc read size */ + pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += pState->read_buf_avail; + pState->comp_remaining -= pState->read_buf_avail; + pState->read_buf_ofs = 0; + } + + /* Perform decompression */ + in_buf_size = (size_t)pState->read_buf_avail; + pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + pState->read_buf_avail -= in_buf_size; + pState->read_buf_ofs += in_buf_size; + + /* Update current output block size remaining */ + pState->out_blk_remain = out_buf_size; + } + + if (pState->out_blk_remain) + { + /* Calc amount to return. */ + size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + + /* Copy data to caller's buffer */ + memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Perform CRC */ + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +#endif + + /* Decrement data consumed from block */ + pState->out_blk_remain -= to_copy; + + /* Inc output offset, while performing sanity check */ + if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Increment counter of data copied to caller */ + copied_to_caller += to_copy; + } + } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); + } + + /* Return how many bytes were copied into user buffer */ + return copied_to_caller; +} + +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +{ + int status; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) + return MZ_FALSE; + + /* Was decompression completed and requested? */ + if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + pState->status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (pState->file_crc32 != pState->file_stat.m_crc32) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + } +#endif + } + + /* Free buffers */ + if (!pState->pZip->m_pState->m_pMem) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); + if (pState->pWrite_buf) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + + /* Save status */ + status = pState->status; + + /* Free context */ + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + + return status == TINFL_STATUS_DONE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; + + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + + if (MZ_FCLOSE(pFile) == EOF) + { + if (status) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + + status = MZ_FALSE; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + + return status; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +} + +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_uint32 *p = (mz_uint32 *)pOpaque; + (void)file_ofs; + *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); + return n; +} + +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_zip_internal_state *pState; + const mz_uint8 *pCentral_dir_header; + mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint64 local_header_ofs = 0; + mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; + mz_bool has_data_descriptor; + mz_uint32 local_header_bit_flags; + + mz_zip_array file_data_array; + mz_zip_array_init(&file_data_array, 1); + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (file_index > pZip->m_total_files) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + + if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_is_encrypted) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports stored and deflate. */ + if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + if (!file_stat.m_is_supported) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + /* Read and parse the local directory entry. */ + local_header_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); + local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + has_data_descriptor = (local_header_bit_flags & 8) != 0; + + if (local_header_filename_len != strlen(file_stat.m_filename)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + goto handle_failure; + } + + if (local_header_filename_len) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ + if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_uint32 extra_size_remaining = local_header_extra_len; + const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ + /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ + if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) + { + mz_uint8 descriptor_buf[32]; + mz_bool has_id; + const mz_uint8 *pSrc; + mz_uint32 file_crc32; + mz_uint64 comp_size = 0, uncomp_size = 0; + + mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; + + file_crc32 = MZ_READ_LE32(pSrc); + + if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); + } + else + { + comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); + } + + if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + else + { + if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + mz_zip_array_clear(pZip, &file_data_array); + + if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) + { + if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) + return MZ_FALSE; + + /* 1 more check to be sure, although the extract checks too. */ + if (uncomp_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + return MZ_FALSE; + } + } + + return MZ_TRUE; + +handle_failure: + mz_zip_array_clear(pZip, &file_data_array); + return MZ_FALSE; +} + +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) +{ + mz_zip_internal_state *pState; + uint32_t i; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Basic sanity checks */ + if (!pState->m_zip64) + { + if (pZip->m_total_files > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pZip->m_archive_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + else + { + if (pZip->m_total_files >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + for (i = 0; i < pZip->m_total_files; i++) + { + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) + { + mz_uint32 found_index; + mz_zip_archive_file_stat stat; + + if (!mz_zip_reader_file_stat(pZip, i, &stat)) + return MZ_FALSE; + + if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ + if (found_index != i) + return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + } + + if (!mz_zip_validate_file(pZip, i, flags)) + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if ((!pMem) || (!size)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if (!pFilename) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +/* ------------------- .ZIP archive writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +{ + mz_write_le32(p, (mz_uint32)v); + mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +} + +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if (!n) + return 0; + + /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ + if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + return 0; + } + + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + + while (new_capacity < new_size) + new_capacity *= 2; + + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return 0; + } + + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + status = MZ_FALSE; + } + } + + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) +{ + mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + { + if (!pZip->m_pRead) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (pZip->m_file_offset_alignment) + { + /* Ensure user specified file offset alignment is a power of 2. */ + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + + pZip->m_pState->m_zip64 = zip64; + pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + return mz_zip_writer_init_v2(pZip, existing_size, 0); +} + +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_mem_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + return 0; + } + + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +} + +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) +{ + MZ_FILE *pFile; + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + + pZip->m_pState->m_pFile = pFile; + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; + char buf[4096]; + + MZ_CLEAR_OBJ(buf); + + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, 0, flags)) + return MZ_FALSE; + + pZip->m_pState->m_pFile = pFile; + pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_zip_internal_state *pState; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) + { + /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ + if (!pZip->m_pState->m_zip64) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* No sense in trying to write to an archive that's already at the support max size */ + if (pZip->m_pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + (void)pFilename; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +#else + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (!pFilename) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + } + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; +#endif /* #ifdef MINIZ_NO_STDIO */ + } + else if (pState->m_pMem) + { + /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + } + /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ + else if (!pZip->m_pWrite) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Start writing new files at the archive's current central directory location. */ + /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_central_directory_file_ofs = 0; + + /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ + /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ + /* TODO: We could easily maintain the sorted central directory offsets. */ + mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +} + +/* TODO: pArchive_name is a terrible name here! */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) +{ + mz_uint8 *pDst = pBuf; + mz_uint32 field_size = 0; + + MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + MZ_WRITE_LE16(pDst + 2, 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + MZ_WRITE_LE64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pComp_size) + { + MZ_WRITE_LE64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + MZ_WRITE_LE64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + MZ_WRITE_LE16(pBuf + 2, field_size); + + return (mz_uint32)(pDst - pBuf); +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, + mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes, + const char *user_extra_data, mz_uint user_extra_data_len) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + if (!pZip->m_pState->m_zip64) + { + if (local_header_ofs > 0xFFFFFFFF) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + /* Try to resize the central directory array back into its original state. */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ + if (*pArchive_name == '/') + return MZ_FALSE; + + /* Making sure the name does not contain drive letters or DOS style backward slashes is the responsibility of the program using miniz*/ + + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +} + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_uint16 bit_flags = 0; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +#ifndef MINIZ_NO_TIME + if (last_modified != NULL) + { + mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); + } + else + { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif /* #ifndef MINIZ_NO_TIME */ + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + /* Set DOS Subdirectory attribute bit. */ + ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + + /* Subdirectories cannot contain data. */ + if ((buf_size) || (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + cur_archive_file_ofs += num_alignment_padding_bytes; + + MZ_CLEAR_OBJ(local_dir_header); + + if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + method = MZ_DEFLATED; + } + + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + + if (pExtra_data != NULL) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + if (uncomp_size) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_zip_internal_state *pState; + mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX)) + { + /* Source file is too large for non-zip64 */ + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + pState->m_zip64 = MZ_TRUE; + } + + /* We could support this, but why? */ + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + +#ifndef MINIZ_NO_TIME + if (pFile_time) + { + mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); + } +#endif + + if (max_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_archive_file_ofs; + + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + if (max_size && level) + { + method = MZ_DEFLATED; + } + + MZ_CLEAR_OBJ(local_dir_header); + if (pState->m_zip64) + { + if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + else + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, NULL, + NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (max_size) + { + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!level) + { + while (1) + { + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if (n == 0) + break; + + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + cur_archive_file_ofs += n; + } + uncomp_size = file_ofs; + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + } + + for (;;) + { + tdefl_status status; + tdefl_flush flush = TDEFL_NO_FLUSH; + + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + break; + } + + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + + if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) + flush = TDEFL_FULL_FLUSH; + + if (n == 0) + flush = TDEFL_FINISH; + + status = tdefl_compress_buffer(pComp, pRead_buf, n, flush); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + break; + } + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return MZ_FALSE; + } + + uncomp_size = file_ofs; + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + { + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, + (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size, + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size, + uncomp_crc32, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + cur_archive_header_file_ofs = local_dir_header_ofs; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + if (pExtra_data != NULL) + { + cur_archive_header_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_header_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_header_file_ofs += extra_size; + } + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO + +static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pSrc_file); + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pSrc_file); +} + +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + return mz_zip_writer_add_read_buf_callback(pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags, + user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len); +} + +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + MZ_FILE *pSrc_file = NULL; + mz_uint64 uncomp_size = 0; + MZ_TIME_T file_modified_time; + MZ_TIME_T *pFile_time = NULL; + mz_bool status; + + memset(&file_modified_time, 0, sizeof(file_modified_time)); + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + pFile_time = &file_modified_time; + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +#endif + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); + + MZ_FCLOSE(pSrc_file); + + return status; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) +{ + /* + 64 should be enough for any new zip64 data */ + if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); + + if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) + { + mz_uint8 new_ext_block[64]; + mz_uint8 *pDst = new_ext_block; + mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + mz_write_le16(pDst + sizeof(mz_uint16), 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + mz_write_le64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + } + + if (pComp_size) + { + mz_write_le64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + mz_write_le64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + } + + if (pDisk_start) + { + mz_write_le32(pDst, *pDisk_start); + pDst += sizeof(mz_uint32); + } + + mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); + + if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if ((pExt) && (ext_len)) + { + mz_uint32 extra_size_remaining = ext_len; + const mz_uint8 *pExtra_data = pExt; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + return MZ_TRUE; +} + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; + mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + mz_zip_archive_file_stat src_file_stat; + mz_uint32 src_filename_len, src_comment_len, src_ext_len; + mz_uint32 local_header_filename_size, local_header_extra_len; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ + if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Get pointer to the source central dir header and crack it */ + if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); + src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); + src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + + /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ + if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + if (!pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) + return MZ_FALSE; + + cur_src_file_ofs = src_file_stat.m_local_header_ofs; + cur_dst_file_ofs = pZip->m_archive_size; + + /* Read the source archive's local dir header */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Compute the total size we need to copy (filename+extra data+compressed data) */ + local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; + + /* Try to find a zip64 extended information field */ + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_zip_array file_data_array; + const mz_uint8 *pExtra_data; + mz_uint32 extra_size_remaining = local_header_extra_len; + + mz_zip_array_init(&file_data_array, 1); + if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + + mz_zip_array_clear(pZip, &file_data_array); + } + + if (!pState->m_zip64) + { + /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ + /* We also check when the archive is finalized so this doesn't need to be perfect. */ + mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; + + if (approx_new_archive_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + /* Write dest archive padding */ + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + + cur_dst_file_ofs += num_alignment_padding_bytes; + + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + while (src_archive_bytes_remaining) + { + n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_dst_file_ofs += n; + + src_archive_bytes_remaining -= n; + } + + /* Now deal with the optional data descriptor */ + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + /* Copy data descriptor */ + if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + /* src is zip64, dest must be zip64 */ + + /* name uint32_t's */ + /* id 1 (optional in zip64?) */ + /* crc 1 */ + /* comp_size 2 */ + /* uncomp_size 2 */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); + } + else + { + /* src is NOT zip64 */ + mz_bool has_id; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + + if (pZip->m_pState->m_zip64) + { + /* dest is zip64, so upgrade the data descriptor */ + const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); + const mz_uint32 src_crc32 = pSrc_descriptor[0]; + const mz_uint64 src_comp_size = pSrc_descriptor[1]; + const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; + + mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); + mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + + n = sizeof(mz_uint32) * 6; + } + else + { + /* dest is NOT zip64, just copy it as-is */ + n = sizeof(mz_uint32) * (has_id ? 4 : 3); + } + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + /* Finally, add the new central dir header */ + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + + if (pState->m_zip64) + { + /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ + const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; + mz_zip_array new_ext_block; + + mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); + + if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return MZ_FALSE; + } + + MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + mz_zip_array_clear(pZip, &new_ext_block); + } + else + { + /* sanity checks */ + if (cur_dst_file_ofs > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (local_dir_header_ofs >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + } + + /* This shouldn't trigger unless we screwed up during the initial sanity checks */ + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + { + /* TODO: Support central dirs >= 32-bits in size */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + } + + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[256]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + /* Write central directory */ + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += central_dir_size; + } + + if (pState->m_zip64) + { + /* Write zip64 end of central directory header */ + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; + + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + + /* Write zip64 end of central directory locator */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; + } + + /* Write end of central directory record */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) +{ + if ((!ppBuf) || (!pSize)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + *ppBuf = NULL; + *pSize = 0; + + if ((!pZip) || (!pZip->m_pState)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_pWrite != mz_zip_heap_write_func) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *ppBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + return mz_zip_writer_end_internal(pZip, MZ_TRUE); +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); +} + +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + mz_zip_zero_struct(&zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_FILENAME; + return MZ_FALSE; + } + + /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ + /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + /* Create a new archive. */ + if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + created_new_archive = MZ_TRUE; + } + else + { + /* Append to an existing archive. */ + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + + return MZ_FALSE; + } + } + + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + actual_err = zip_archive.m_last_error; + + /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ + if (!mz_zip_writer_finalize_archive(&zip_archive)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if (!mz_zip_writer_end_internal(&zip_archive, status)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if ((!status) && (created_new_archive)) + { + /* It's a new archive and something went wrong, so just delete it. */ + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + + if (pErr) + *pErr = actual_err; + + return status; +} + +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) +{ + mz_uint32 file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + + return NULL; + } + + mz_zip_zero_struct(&zip_archive); + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + return NULL; + } + + if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) + { + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + } + + mz_zip_reader_end_internal(&zip_archive, p != NULL); + + if (pErr) + *pErr = zip_archive.m_last_error; + + return p; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* ------------------- Misc utils */ + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +} + +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +} + +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = err_num; + return prev_err; +} + +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + return pZip->m_last_error; +} + +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) +{ + return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +} + +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = MZ_ZIP_NO_ERROR; + return prev_err; +} + +const char *mz_zip_get_error_string(mz_zip_error mz_err) +{ + switch (mz_err) + { + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return "undefined error"; + case MZ_ZIP_TOO_MANY_FILES: + return "too many files"; + case MZ_ZIP_FILE_TOO_LARGE: + return "file too large"; + case MZ_ZIP_UNSUPPORTED_METHOD: + return "unsupported method"; + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return "unsupported encryption"; + case MZ_ZIP_UNSUPPORTED_FEATURE: + return "unsupported feature"; + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return "failed finding central directory"; + case MZ_ZIP_NOT_AN_ARCHIVE: + return "not a ZIP archive"; + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return "invalid header or archive is corrupted"; + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return "unsupported multidisk archive"; + case MZ_ZIP_DECOMPRESSION_FAILED: + return "decompression failed or archive is corrupted"; + case MZ_ZIP_COMPRESSION_FAILED: + return "compression failed"; + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return "unexpected decompressed size"; + case MZ_ZIP_CRC_CHECK_FAILED: + return "CRC-32 check failed"; + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return "unsupported central directory size"; + case MZ_ZIP_ALLOC_FAILED: + return "allocation failed"; + case MZ_ZIP_FILE_OPEN_FAILED: + return "file open failed"; + case MZ_ZIP_FILE_CREATE_FAILED: + return "file create failed"; + case MZ_ZIP_FILE_WRITE_FAILED: + return "file write failed"; + case MZ_ZIP_FILE_READ_FAILED: + return "file read failed"; + case MZ_ZIP_FILE_CLOSE_FAILED: + return "file close failed"; + case MZ_ZIP_FILE_SEEK_FAILED: + return "file seek failed"; + case MZ_ZIP_FILE_STAT_FAILED: + return "file stat failed"; + case MZ_ZIP_INVALID_PARAMETER: + return "invalid parameter"; + case MZ_ZIP_INVALID_FILENAME: + return "invalid filename"; + case MZ_ZIP_BUF_TOO_SMALL: + return "buffer too small"; + case MZ_ZIP_INTERNAL_ERROR: + return "internal error"; + case MZ_ZIP_FILE_NOT_FOUND: + return "file not found"; + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return "archive is too large"; + case MZ_ZIP_VALIDATION_FAILED: + return "validation failed"; + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return "write calledback failed"; + default: + break; + } + + return "unknown error"; +} + +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return MZ_FALSE; + + return pZip->m_pState->m_zip64; +} + +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + + return pZip->m_pState->m_central_dir.m_size; +} + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) +{ + if (!pZip) + return 0; + return pZip->m_archive_size; +} + +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_file_archive_start_ofs; +} + +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_pFile; +} + +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + if (filename_buf_size) + pFilename[0] = '\0'; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +} + +mz_bool mz_zip_end(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_FALSE; + + if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) + return mz_zip_reader_end(pZip); +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) + return mz_zip_writer_end(pZip); +#endif + + return MZ_FALSE; +} + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ +/*** End of #include "miniz.c" ***/ + +#endif +#endif + +/* +** COMPRESS +*/ +#ifdef SQLITE_ENABLE_COMPRESS +#ifdef _WIN32 +__declspec(dllexport) +#endif +int wx_sqlite3_compress_init(wx_sqlite3 *db, char **pzErrMsg, const wx_sqlite3_api_routines *pApi); +/* #include "compress.c" */ +/*** Begin of #include "compress.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 compression functions +** compress() and uncompress() using ZLIB. +*/ +/* #include "wx_sqlite3ext.h" */ + +SQLITE_EXTENSION_INIT1 +/* #include "zlibwrap.h" */ +/*** Begin of #include "zlibwrap.h" ***/ +/* +** Name: zlibwrap.h +** Purpose: Include wrapper for miniz.h +** Author: Ulrich Telle +** Created: 2022-05-09 +** Copyright: (c) 2022 Ulrich Telle +** License: MIT +*/ + +/// \file zlibwrap.h Include wrapper for using miniz.h instead of the original zlib.h + +#ifndef SQLITE3MC_ZLIBWRAP_H_ +#define SQLITE3MC_ZLIBWRAP_H_ + +#if SQLITE3MC_USE_MINIZ != 0 +/* #include "miniz.h" */ + +#else +#include +#endif + + +#endif /* SQLITE3MC_ZLIBWRAP_H_ */ +/*** End of #include "zlibwrap.h" ***/ + + +/* +** Implementation of the "compress(X)" SQL function. The input X is +** compressed using zLib and the output is returned. +** +** The output is a BLOB that begins with a variable-length integer that +** is the input size in bytes (the size of X before compression). The +** variable-length integer is implemented as 1 to 5 bytes. There are +** seven bits per integer stored in the lower seven bits of each byte. +** More significant bits occur first. The most significant bit (0x80) +** is a flag to indicate the end of the integer. +** +** This function, SQLAR, and ZIP all use the same "deflate" compression +** algorithm, but each is subtly different: +** +** * ZIP uses raw deflate. +** +** * SQLAR uses the "zlib format" which is raw deflate with a two-byte +** algorithm-identification header and a four-byte checksum at the end. +** +** * This utility uses the "zlib format" like SQLAR, but adds the variable- +** length integer uncompressed size value at the beginning. +** +** This function might be extended in the future to support compression +** formats other than deflate, by providing a different algorithm-id +** mark following the variable-length integer size parameter. +*/ +static void compressFunc( + wx_sqlite3_context *context, + int argc, + wx_sqlite3_value **argv +){ + const unsigned char *pIn; + unsigned char *pOut; + unsigned int nIn; + unsigned long int nOut; + unsigned char x[8]; + int rc; + int i, j; + + pIn = wx_sqlite3_value_blob(argv[0]); + nIn = wx_sqlite3_value_bytes(argv[0]); + nOut = 13 + nIn + (nIn+999)/1000; + pOut = wx_sqlite3_malloc( nOut+5 ); + for(i=4; i>=0; i--){ + x[i] = (nIn >> (7*(4-i)))&0x7f; + } + for(i=0; i<4 && x[i]==0; i++){} + for(j=0; i<=4; i++, j++) pOut[j] = x[i]; + pOut[j-1] |= 0x80; + rc = compress(&pOut[j], &nOut, pIn, nIn); + if( rc==Z_OK ){ + wx_sqlite3_result_blob(context, pOut, nOut+j, wx_sqlite3_free); + }else{ + wx_sqlite3_free(pOut); + } +} + +/* +** Implementation of the "uncompress(X)" SQL function. The argument X +** is a blob which was obtained from compress(Y). The output will be +** the value Y. +*/ +static void uncompressFunc( + wx_sqlite3_context *context, + int argc, + wx_sqlite3_value **argv +){ + const unsigned char *pIn; + unsigned char *pOut; + unsigned int nIn; + unsigned long int nOut; + int rc; + unsigned int i; + + pIn = wx_sqlite3_value_blob(argv[0]); + nIn = wx_sqlite3_value_bytes(argv[0]); + nOut = 0; + for(i=0; i + +/* +** 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( + wx_sqlite3_context *context, + int argc, + wx_sqlite3_value **argv +){ + assert( argc==1 ); + if( wx_sqlite3_value_type(argv[0])==SQLITE_BLOB ){ + const Bytef *pData = wx_sqlite3_value_blob(argv[0]); + uLong nData = wx_sqlite3_value_bytes(argv[0]); + uLongf nOut = compressBound(nData); + Bytef *pOut; + + pOut = (Bytef*)wx_sqlite3_malloc(nOut); + if( pOut==0 ){ + wx_sqlite3_result_error_nomem(context); + return; + }else{ + if( Z_OK!=compress(pOut, &nOut, pData, nData) ){ + wx_sqlite3_result_error(context, "error in compress()", -1); + }else if( nOut +#include +#include + +/* #include "zlibwrap.h" */ + + +#ifndef SQLITE_OMIT_VIRTUALTABLE + +#ifndef SQLITE_AMALGAMATION + +#ifndef UINT32_TYPE +# ifdef HAVE_UINT32_T +# define UINT32_TYPE uint32_t +# else +# define UINT32_TYPE unsigned int +# endif +#endif +#ifndef UINT16_TYPE +# ifdef HAVE_UINT16_T +# define UINT16_TYPE uint16_t +# else +# define UINT16_TYPE unsigned short int +# endif +#endif +typedef wx_sqlite3_int64 i64; +typedef unsigned char u8; +typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ +typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ +#define MIN(a,b) ((a)<(b) ? (a) : (b)) + +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 +#endif +#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) +# 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 (wx_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 { + wx_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 { + wx_sqlite3_vtab base; /* Base class - must be first */ + char *zFile; /* Zip file this table accesses (may be NULL) */ + wx_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(wx_sqlite3_context *ctx, const char *zFmt, ...){ + char *zMsg = 0; + va_list ap; + va_start(ap, zFmt); + zMsg = wx_sqlite3_vmprintf(zFmt, ap); + wx_sqlite3_result_error(ctx, zMsg, -1); + wx_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( + wx_sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + wx_sqlite3_vtab **ppVtab, + char **pzErr +){ + int nByte = sizeof(ZipfileTab) + ZIPFILE_BUFFER_SIZE; + int nFile = 0; + const char *zFile = 0; + ZipfileTab *pNew = 0; + int rc; + (void)pAux; + + /* 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==wx_sqlite3_stricmp(argv[0], "zipfile") ); + if( (0!=wx_sqlite3_stricmp(argv[2], "zipfile") && argc<4) || argc>4 ){ + *pzErr = wx_sqlite3_mprintf("zipfile constructor requires one argument"); + return SQLITE_ERROR; + } + + if( argc>3 ){ + zFile = argv[3]; + nFile = (int)strlen(zFile)+1; + } + + rc = wx_sqlite3_declare_vtab(db, ZIPFILE_SCHEMA); + if( rc==SQLITE_OK ){ + pNew = (ZipfileTab*)wx_sqlite3_malloc64((wx_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); + } + } + wx_sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY); + *ppVtab = (wx_sqlite3_vtab*)pNew; + return rc; +} + +/* +** Free the ZipfileEntry structure indicated by the only argument. +*/ +static void zipfileEntryFree(ZipfileEntry *p){ + if( p ){ + wx_sqlite3_free(p->cds.zFile); + wx_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(wx_sqlite3_vtab *pVtab){ + zipfileCleanupTransaction((ZipfileTab*)pVtab); + wx_sqlite3_free(pVtab); + return SQLITE_OK; +} + +/* +** Constructor for a new ZipfileCsr object. +*/ +static int zipfileOpen(wx_sqlite3_vtab *p, wx_sqlite3_vtab_cursor **ppCsr){ + ZipfileTab *pTab = (ZipfileTab*)p; + ZipfileCsr *pCsr; + pCsr = wx_sqlite3_malloc(sizeof(*pCsr)); + *ppCsr = (wx_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(wx_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; + + wx_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); + wx_sqlite3_free(pTab->base.zErrMsg); + pTab->base.zErrMsg = wx_sqlite3_vmprintf(zFmt, ap); + va_end(ap); +} +static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){ + va_list ap; + va_start(ap, zFmt); + wx_sqlite3_free(pCsr->base.pVtab->zErrMsg); + pCsr->base.pVtab->zErrMsg = wx_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 +** wx_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 wx_sqlite3_malloc) */ +){ + size_t n; + fseek(pFile, (long)iOff, SEEK_SET); + n = fread(aRead, 1, nRead, pFile); + if( (int)n!=nRead ){ + *pzErrmsg = wx_sqlite3_mprintf("error in fread()"); + return SQLITE_ERROR; + } + return SQLITE_OK; +} + +static int zipfileAppendData( + ZipfileTab *pTab, + const u8 *aWrite, + int nWrite +){ + if( nWrite>0 ){ + size_t n = nWrite; + fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET); + n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd); + if( (int)n!=nWrite ){ + pTab->base.zErrMsg = wx_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){ + if( aBuf==0 ) return 0; + 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,M,D,X1,X2,A,B,sec,min,hr; + i64 JDsec; + Y = (1980 + ((pCDS->mDate >> 9) & 0x7F)); + M = ((pCDS->mDate >> 5) & 0x0F); + D = (pCDS->mDate & 0x1F); + sec = (pCDS->mTime & 0x1F)*2; + min = (pCDS->mTime >> 5) & 0x3F; + hr = (pCDS->mTime >> 11) & 0x1F; + if( M<=2 ){ + Y--; + M += 12; + } + X1 = 36525*(Y+4716)/100; + X2 = 306001*(M+1)/10000; + A = Y/100; + B = 2 - A + (A/4); + JDsec = (i64)((X1 + X2 + D + B - 1524.5)*86400) + hr*3600 + min*60 + sec; + return (u32)(JDsec - (i64)24405875*(i64)8640); +} + +/* +** 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; + (void)nBlob; + + 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 ){ + wx_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*)wx_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 = wx_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 = wx_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]; + } + + if( rc==SQLITE_OK ) 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 = wx_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(wx_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) { + wx_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( + wx_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 = wx_sqlite3_malloc(nOut); + if( aRes==0 ){ + wx_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{ + wx_sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree); + aRes = 0; + } + } + wx_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 +** wx_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; + wx_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*)wx_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{ + wx_sqlite3_free(aOut); + *pzErr = wx_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( + wx_sqlite3_vtab_cursor *cur, /* The cursor */ + wx_sqlite3_context *ctx, /* First argument to wx_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 */ + wx_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. */ + wx_sqlite3_result_int(ctx, pCDS->iExternalAttr >> 16); + break; + case 2: { /* mtime */ + wx_sqlite3_result_int64(ctx, pCsr->pCurrent->mUnixTime); + break; + } + case 3: { /* sz */ + if( wx_sqlite3_vtab_nochange(ctx)==0 ){ + wx_sqlite3_result_int64(ctx, pCDS->szUncompressed); + } + break; + } + case 4: /* rawdata */ + if( wx_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 = wx_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{ + wx_sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT); + } + } + wx_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]!='/' ){ + wx_sqlite3_result_blob(ctx, "", 0, SQLITE_STATIC); + } + } + } + break; + } + case 6: /* method */ + wx_sqlite3_result_int(ctx, pCDS->iCompression); + break; + default: /* z */ + assert( i==7 ); + wx_sqlite3_result_int64(ctx, pCsr->iId); + break; + } + + return rc; +} + +/* +** Return TRUE if the cursor is at EOF. +*/ +static int zipfileEof(wx_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; + + memset(pEOCD, 0, sizeof(ZipfileEOCD)); + 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 ){ + 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 = wx_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( + wx_sqlite3_vtab_cursor *cur, + int idxNum, const char *idxStr, + int argc, wx_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 */ + + (void)idxStr; + (void)argc; + + zipfileResetCursor(pCsr); + + if( pTab->zFile ){ + zFile = pTab->zFile; + }else if( idxNum==0 ){ + zipfileCursorErr(pCsr, "zipfile() function requires an argument"); + return SQLITE_ERROR; + }else if( wx_sqlite3_value_type(argv[0])==SQLITE_BLOB ){ + static const u8 aEmptyBlob = 0; + const u8 *aBlob = (const u8*)wx_sqlite3_value_blob(argv[0]); + int nBlob = wx_sqlite3_value_bytes(argv[0]); + assert( pTab->pFirstEntry==0 ); + if( aBlob==0 ){ + aBlob = &aEmptyBlob; + nBlob = 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*)wx_sqlite3_value_text(argv[0]); + } + + if( 0==pTab->pWriteFd && 0==bInMemory ){ + pCsr->pFile = zFile ? fopen(zFile, "rb") : 0; + 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( + wx_sqlite3_vtab *tab, + wx_sqlite3_index_info *pIdxInfo +){ + int i; + int idx = -1; + int unusable = 0; + (void)tab; + + for(i=0; inConstraint; i++){ + const struct wx_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 = wx_sqlite3_malloc(sizeof(ZipfileEntry)); + if( pNew ){ + memset(pNew, 0, sizeof(ZipfileEntry)); + pNew->cds.zFile = wx_sqlite3_mprintf("%s", zPath); + if( pNew->cds.zFile==0 ){ + wx_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( + wx_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*)wx_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)wx_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 = wx_sqlite3_mprintf("zipfile: mode does not match data"); + return SQLITE_CONSTRAINT; + } + *pMode = mode; + return SQLITE_OK; + + parse_error: + *pzErr = wx_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(wx_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 = wx_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 = wx_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){ + wx_sqlite3_vfs *pVfs = wx_sqlite3_vfs_find(0); + u32 ret; + if( pVfs==0 ) return 0; + 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(wx_sqlite3_value *pVal){ + if( pVal==0 || wx_sqlite3_value_type(pVal)==SQLITE_NULL ){ + return zipfileTime(); + } + return (u32)wx_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( + wx_sqlite3_vtab *pVtab, + int nVal, + wx_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; + + (void)pRowid; + + 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( wx_sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ + const char *zDelete = (const char*)wx_sqlite3_value_text(apVal[0]); + int nDelete = (int)strlen(zDelete); + if( nVal>1 ){ + const char *zUpdate = (const char*)wx_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( wx_sqlite3_value_type(apVal[5])!=SQLITE_NULL ){ + zipfileTableErr(pTab, "sz must be NULL"); + rc = SQLITE_CONSTRAINT; + } + if( wx_sqlite3_value_type(apVal[6])!=SQLITE_NULL ){ + zipfileTableErr(pTab, "rawdata must be NULL"); + rc = SQLITE_CONSTRAINT; + } + + if( rc==SQLITE_OK ){ + if( wx_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 = wx_sqlite3_value_blob(apVal[7]); + int nIn = wx_sqlite3_value_bytes(apVal[7]); + int bAuto = wx_sqlite3_value_type(apVal[8])==SQLITE_NULL; + + iMethod = wx_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*)wx_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 = wx_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( wx_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: + wx_sqlite3_free(pFree); + wx_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(wx_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(wx_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( + wx_sqlite3_context *context, + int argc, + wx_sqlite3_value **argv +){ + ZipfileCsr *pCsr; + ZipfileTab *pTab = (ZipfileTab*)wx_sqlite3_user_data(context); + assert( argc>0 ); + + pCsr = zipfileFindCursor(pTab, wx_sqlite3_value_int64(argv[0])); + if( pCsr ){ + ZipfileCDS *p = &pCsr->pCurrent->cds; + char *zRes = wx_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 ){ + wx_sqlite3_result_error_nomem(context); + }else{ + wx_sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT); + wx_sqlite3_free(zRes); + } + } +} + +/* +** xFindFunction method. +*/ +static int zipfileFindFunction( + wx_sqlite3_vtab *pVtab, /* Virtual table handle */ + int nArg, /* Number of SQL function arguments */ + const char *zName, /* Name of SQL function */ + void (**pxFunc)(wx_sqlite3_context*,int,wx_sqlite3_value**), /* OUT: Result */ + void **ppArg /* OUT: User data for *pxFunc */ +){ + (void)nArg; + if( wx_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; + wx_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) ... +*/ +static void zipfileStep(wx_sqlite3_context *pCtx, int nVal, wx_sqlite3_value **apVal){ + ZipfileCtx *p; /* Aggregate function context */ + ZipfileEntry e; /* New entry to add to zip archive */ + + wx_sqlite3_value *pName = 0; + wx_sqlite3_value *pMode = 0; + wx_sqlite3_value *pMtime = 0; + wx_sqlite3_value *pData = 0; + wx_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*)wx_sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx)); + if( p==0 ) return; + + /* Martial the arguments into stack variables */ + if( nVal!=2 && nVal!=4 && nVal!=5 ){ + zErr = wx_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*)wx_sqlite3_value_text(pName); + nName = wx_sqlite3_value_bytes(pName); + if( zName==0 ){ + zErr = wx_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!=wx_sqlite3_value_type(pMethod) ){ + iMethod = (int)wx_sqlite3_value_int64(pMethod); + if( iMethod!=0 && iMethod!=8 ){ + zErr = wx_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( wx_sqlite3_value_type(pData)==SQLITE_NULL ){ + bIsDir = 1; + iMethod = 0; + }else{ + aData = wx_sqlite3_value_blob(pData); + szUncompressed = nData = wx_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 = wx_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 = wx_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: + wx_sqlite3_free(aFree); + wx_sqlite3_free(zFree); + if( rc ){ + if( zErr ){ + wx_sqlite3_result_error(pCtx, zErr, -1); + }else{ + wx_sqlite3_result_error_code(pCtx, rc); + } + } + wx_sqlite3_free(zErr); +} + +/* +** xFinalize() callback for zipfile aggregate function. +*/ +static void zipfileFinal(wx_sqlite3_context *pCtx){ + ZipfileCtx *p; + ZipfileEOCD eocd; + wx_sqlite3_int64 nZip; + u8 *aZip; + + p = (ZipfileCtx*)wx_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*)wx_sqlite3_malloc64(nZip); + if( aZip==0 ){ + wx_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]); + wx_sqlite3_result_blob(pCtx, aZip, (int)nZip, zipfileFree); + } + } + + wx_sqlite3_free(p->body.a); + wx_sqlite3_free(p->cds.a); +} + + +/* +** Register the "zipfile" virtual table. +*/ +static int zipfileRegister(wx_sqlite3 *db){ + static wx_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 */ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollback */ + 0 /* xShadowName */ + }; + + int rc = wx_sqlite3_create_module(db, "zipfile" , &zipfileModule, 0); + if( rc==SQLITE_OK ) rc = wx_sqlite3_overload_function(db, "zipfile_cds", -1); + if( rc==SQLITE_OK ){ + rc = wx_sqlite3_create_function(db, "zipfile", -1, SQLITE_UTF8, 0, 0, + zipfileStep, zipfileFinal + ); + } + assert( sizeof(i64)==8 ); + assert( sizeof(u32)==4 ); + assert( sizeof(u16)==2 ); + assert( sizeof(u8)==1 ); + return rc; +} +#else /* SQLITE_OMIT_VIRTUALTABLE */ +# define zipfileRegister(x) SQLITE_OK +#endif + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int wx_sqlite3_zipfile_init( + wx_sqlite3 *db, + char **pzErrMsg, + const wx_sqlite3_api_routines *pApi +){ + SQLITE_EXTENSION_INIT2(pApi); + (void)pzErrMsg; /* Unused parameter */ + return zipfileRegister(db); +} +/*** End of #include "zipfile.c" ***/ + +#endif + +/* +** Multi cipher VFS +*/ +/* #include "wx_sqlite3mc_vfs.c" */ +/*** Begin of #include "wx_sqlite3mc_vfs.c" ***/ +/* +** Name: wx_sqlite3mc_vfs.c +** Purpose: Implementation of SQLite VFS for Multiple Ciphers +** Author: Ulrich Telle +** Created: 2020-02-28 +** Copyright: (c) 2020-2023 Ulrich Telle +** License: MIT +*/ + +/* #include "wx_sqlite3mc_vfs.h" */ + +/* #include "wx_sqlite3.h" */ + +#include +#include +#include +/* #include "mystdint.h" */ + + +/* +** Type definitions +*/ + +typedef struct wx_sqlite3mc_file wx_sqlite3mc_file; +typedef struct wx_sqlite3mc_vfs wx_sqlite3mc_vfs; + +/* +** SQLite3 Multiple Ciphers file structure +*/ + +struct wx_sqlite3mc_file +{ + wx_sqlite3_file base; /* wx_sqlite3_file I/O methods */ + wx_sqlite3_file* pFile; /* Real underlying OS file */ + wx_sqlite3mc_vfs* pVfsMC; /* Pointer to the wx_sqlite3mc_vfs object */ + const char* zFileName; /* File name */ + int openFlags; /* Open flags */ + wx_sqlite3mc_file* pMainNext; /* Next main db file */ + wx_sqlite3mc_file* pMainDb; /* Main database to which this one is attached */ + Codec* codec; /* Codec if encrypted */ + int pageNo; /* Page number (in case of journal files) */ +}; + +/* +** SQLite3 Multiple Ciphers VFS structure +*/ + +struct wx_sqlite3mc_vfs +{ + wx_sqlite3_vfs base; /* Multiple Ciphers VFS shim methods */ + wx_sqlite3_mutex* mutex; /* Mutex to protect pMain */ + wx_sqlite3mc_file* pMain; /* List of main database files */ +}; + +#define REALVFS(p) ((wx_sqlite3_vfs*)(((wx_sqlite3mc_vfs*)(p))->base.pAppData)) +#define REALFILE(p) (((wx_sqlite3mc_file*)(p))->pFile) + +/* +** Prototypes for VFS methods +*/ + +static int mcVfsOpen(wx_sqlite3_vfs* pVfs, const char* zName, wx_sqlite3_file* pFile, int flags, int* pOutFlags); +static int mcVfsDelete(wx_sqlite3_vfs* pVfs, const char* zName, int syncDir); +static int mcVfsAccess(wx_sqlite3_vfs* pVfs, const char* zName, int flags, int* pResOut); +static int mcVfsFullPathname(wx_sqlite3_vfs* pVfs, const char* zName, int nOut, char* zOut); +static void* mcVfsDlOpen(wx_sqlite3_vfs* pVfs, const char* zFilename); +static void mcVfsDlError(wx_sqlite3_vfs* pVfs, int nByte, char* zErrMsg); +static void (*mcVfsDlSym(wx_sqlite3_vfs* pVfs, void* p, const char* zSymbol))(void); +static void mcVfsDlClose(wx_sqlite3_vfs* pVfs, void* p); +static int mcVfsRandomness(wx_sqlite3_vfs* pVfs, int nByte, char* zOut); +static int mcVfsSleep(wx_sqlite3_vfs* pVfs, int microseconds); +static int mcVfsCurrentTime(wx_sqlite3_vfs* pVfs, double* pOut); +static int mcVfsGetLastError(wx_sqlite3_vfs* pVfs, int nErr, char* zOut); +static int mcVfsCurrentTimeInt64(wx_sqlite3_vfs* pVfs, wx_sqlite3_int64* pOut); +static int mcVfsSetSystemCall(wx_sqlite3_vfs* pVfs, const char* zName, wx_sqlite3_syscall_ptr pNewFunc); +static wx_sqlite3_syscall_ptr mcVfsGetSystemCall(wx_sqlite3_vfs* pVfs, const char* zName); +static const char* mcVfsNextSystemCall(wx_sqlite3_vfs* pVfs, const char* zName); + +/* +** Prototypes for IO methods +*/ + +static int mcIoClose(wx_sqlite3_file* pFile); +static int mcIoRead(wx_sqlite3_file* pFile, void*, int iAmt, wx_sqlite3_int64 iOfst); +static int mcIoWrite(wx_sqlite3_file* pFile,const void*,int iAmt, wx_sqlite3_int64 iOfst); +static int mcIoTruncate(wx_sqlite3_file* pFile, wx_sqlite3_int64 size); +static int mcIoSync(wx_sqlite3_file* pFile, int flags); +static int mcIoFileSize(wx_sqlite3_file* pFile, wx_sqlite3_int64* pSize); +static int mcIoLock(wx_sqlite3_file* pFile, int lock); +static int mcIoUnlock(wx_sqlite3_file* pFile, int lock); +static int mcIoCheckReservedLock(wx_sqlite3_file* pFile, int *pResOut); +static int mcIoFileControl(wx_sqlite3_file* pFile, int op, void *pArg); +static int mcIoSectorSize(wx_sqlite3_file* pFile); +static int mcIoDeviceCharacteristics(wx_sqlite3_file* pFile); +static int mcIoShmMap(wx_sqlite3_file* pFile, int iPg, int pgsz, int map, void volatile** p); +static int mcIoShmLock(wx_sqlite3_file* pFile, int offset, int n, int flags); +static void mcIoShmBarrier(wx_sqlite3_file* pFile); +static int mcIoShmUnmap(wx_sqlite3_file* pFile, int deleteFlag); +static int mcIoFetch(wx_sqlite3_file* pFile, wx_sqlite3_int64 iOfst, int iAmt, void** pp); +static int mcIoUnfetch(wx_sqlite3_file* pFile, wx_sqlite3_int64 iOfst, void* p); + +#define SQLITE3MC_VFS_NAME ("multipleciphers") + +/* +** Header sizes of WAL journal files +*/ +static const int walFrameHeaderSize = 24; +static const int walFileHeaderSize = 32; + +/* +** Global I/O method structure of SQLite3 Multiple Ciphers VFS +*/ +static wx_sqlite3_io_methods mcIoMethodsGlobal = +{ + 3, /* iVersion */ + mcIoClose, /* xClose */ + mcIoRead, /* xRead */ + mcIoWrite, /* xWrite */ + mcIoTruncate, /* xTruncate */ + mcIoSync, /* xSync */ + mcIoFileSize, /* xFileSize */ + mcIoLock, /* xLock */ + mcIoUnlock, /* xUnlock */ + mcIoCheckReservedLock, /* xCheckReservedLock */ + mcIoFileControl, /* xFileControl */ + mcIoSectorSize, /* xSectorSize */ + mcIoDeviceCharacteristics, /* xDeviceCharacteristics */ + mcIoShmMap, /* xShmMap */ + mcIoShmLock, /* xShmLock */ + mcIoShmBarrier, /* xShmBarrier */ + mcIoShmUnmap, /* xShmUnmap */ + mcIoFetch, /* xFetch */ + mcIoUnfetch, /* xUnfetch */ +}; + +/* +** Internal functions +*/ + +/* +** Add an item to the list of main database files, if it is not already present. +*/ +static void mcMainListAdd(wx_sqlite3mc_file* pFile) +{ + assert( (pFile->openFlags & SQLITE_OPEN_MAIN_DB) ); + wx_sqlite3_mutex_enter(pFile->pVfsMC->mutex); + pFile->pMainNext = pFile->pVfsMC->pMain; + pFile->pVfsMC->pMain = pFile; + wx_sqlite3_mutex_leave(pFile->pVfsMC->mutex); +} + +/* +** Remove an item from the list of main database files. +*/ +static void mcMainListRemove(wx_sqlite3mc_file* pFile) +{ + wx_sqlite3mc_file** pMainPrev; + wx_sqlite3_mutex_enter(pFile->pVfsMC->mutex); + for (pMainPrev = &pFile->pVfsMC->pMain; *pMainPrev && *pMainPrev != pFile; pMainPrev = &((*pMainPrev)->pMainNext)){} + if (*pMainPrev) *pMainPrev = pFile->pMainNext; + pFile->pMainNext = 0; + wx_sqlite3_mutex_leave(pFile->pVfsMC->mutex); +} + +/* +** Given that zFileName points to a buffer containing a database file name passed to +** either the xOpen() or xAccess() VFS method, search the list of main database files +** for a file handle opened by the same database connection on the corresponding +** database file. +*/ +static wx_sqlite3mc_file* mcFindDbMainFileName(wx_sqlite3mc_vfs* mcVfs, const char* zFileName) +{ + wx_sqlite3mc_file* pDb; + wx_sqlite3_mutex_enter(mcVfs->mutex); + for (pDb = mcVfs->pMain; pDb && pDb->zFileName != zFileName; pDb = pDb->pMainNext){} + wx_sqlite3_mutex_leave(mcVfs->mutex); + return pDb; +} + +/* +** Find a pointer to the Multiple Ciphers VFS in use for a database connection. +*/ +static wx_sqlite3mc_vfs* mcFindVfs(wx_sqlite3* db, const char* zDbName) +{ + wx_sqlite3mc_vfs* pVfsMC = NULL; + if (db->pVfs && db->pVfs->xOpen == mcVfsOpen) + { + /* The top level VFS is a Multiple Ciphers VFS */ + pVfsMC = (wx_sqlite3mc_vfs*)(db->pVfs); + } + else + { + /* + ** The top level VFS is not a Multiple Ciphers VFS. + ** Retrieve the VFS names stack. + */ + char* zVfsNameStack = 0; + if ((wx_sqlite3_file_control(db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsNameStack) == SQLITE_OK) && (zVfsNameStack != NULL)) + { + /* Search for the name prefix of a Multiple Ciphers VFS. */ + char* zVfsName = strstr(zVfsNameStack, SQLITE3MC_VFS_NAME); + if (zVfsName != NULL) + { + /* The prefix was found, now determine the full VFS name. */ + char* zVfsNameEnd = zVfsName + strlen(SQLITE3MC_VFS_NAME); + if (*zVfsNameEnd == '-') + { + for (++zVfsNameEnd; *zVfsNameEnd != '/' && *zVfsNameEnd != 0; ++zVfsNameEnd); + if (*zVfsNameEnd == '/') *zVfsNameEnd = 0; + + /* Find a pointer to the VFS with the determined name. */ + wx_sqlite3_vfs* pVfs = wx_sqlite3_vfs_find(zVfsName); + if (pVfs && pVfs->xOpen == mcVfsOpen) + { + pVfsMC = (wx_sqlite3mc_vfs*) pVfs; + } + } + } + wx_sqlite3_free(zVfsNameStack); + } + } + return pVfsMC; +} + +/* +** Find the codec of the database file +** corresponding to the database schema name. +*/ +SQLITE_PRIVATE Codec* wx_sqlite3mcGetCodec(wx_sqlite3* db, const char* zDbName) +{ + Codec* codec = NULL; + wx_sqlite3mc_vfs* pVfsMC = mcFindVfs(db, zDbName); + + if (pVfsMC) + { + const char* dbFileName = wx_sqlite3_db_filename(db, zDbName); + wx_sqlite3mc_file* pDbMain = mcFindDbMainFileName(pVfsMC, dbFileName); + if (pDbMain) + { + codec = pDbMain->codec; + } + } + return codec; +} + +/* +** Find the codec of the main database file. +*/ +SQLITE_PRIVATE Codec* wx_sqlite3mcGetMainCodec(wx_sqlite3* db) +{ + return wx_sqlite3mcGetCodec(db, "main"); +} + +/* +** Set the codec of the database file with the given database file name. +** +** The parameter db, the handle of the database connection, is currently +** not used to determine the database file handle, for which the codec +** should be set. The reason is that for shared cache mode the database +** connection handle is not unique, and it is not even clear which +** connection handle is actually valid, because the association between +** connection handles and database file handles is not maintained properly. +*/ +SQLITE_PRIVATE void wx_sqlite3mcSetCodec(wx_sqlite3* db, const char* zDbName, const char* zFileName, Codec* codec) +{ + wx_sqlite3mc_file* pDbMain = NULL; + wx_sqlite3mc_vfs* pVfsMC = mcFindVfs(db, zDbName); + if (pVfsMC) + { + pDbMain = mcFindDbMainFileName((wx_sqlite3mc_vfs*)(db->pVfs), zFileName); + } + if (pDbMain) + { + Codec* prevCodec = pDbMain->codec; + Codec* msgCodec = (codec) ? codec : prevCodec; + if (msgCodec) + { + /* Reset error state of pager */ + mcReportCodecError(wx_sqlite3mcGetBtShared(msgCodec), SQLITE_OK); + } + if (prevCodec) + { + /* + ** Free a codec that was already associated with this main database file handle + */ + wx_sqlite3mcCodecFree(prevCodec); + } + pDbMain->codec = codec; + } + else + { + /* + ** No main database file handle found, free codec + */ + wx_sqlite3mcCodecFree(codec); + } +} + +/* +** This function is called by the wal module when writing page content +** into the log file. +** +** This function returns a pointer to a buffer containing the encrypted +** page content. If a malloc fails, this function may return NULL. +*/ +SQLITE_PRIVATE void* wx_sqlite3mcPagerCodec(PgHdrMC* pPg) +{ + wx_sqlite3_file* pFile = wx_sqlite3PagerFile(pPg->pPager); + void* aData = 0; + if (pFile->pMethods == &mcIoMethodsGlobal) + { + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + Codec* codec = mcFile->codec; + if (codec != 0 && codec->m_walLegacy == 0 && wx_sqlite3mcIsEncrypted(codec)) + { + aData = wx_sqlite3mcCodec(codec, pPg->pData, pPg->pgno, 6); + } + else + { + aData = (char*) pPg->pData; + } + } + else + { + aData = (char*) pPg->pData; + } + return aData; +} + +/* +** Implementation of VFS methods +*/ + +static int mcVfsOpen(wx_sqlite3_vfs* pVfs, const char* zName, wx_sqlite3_file* pFile, int flags, int* pOutFlags) +{ + int rc; + wx_sqlite3mc_vfs* mcVfs = (wx_sqlite3mc_vfs*) pVfs; + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + mcFile->pFile = (wx_sqlite3_file*) &mcFile[1]; + mcFile->pVfsMC = mcVfs; + mcFile->openFlags = flags; + mcFile->zFileName = zName; + mcFile->codec = 0; + mcFile->pMainDb = 0; + mcFile->pMainNext = 0; + mcFile->pageNo = 0; + + if (zName) + { + if (flags & SQLITE_OPEN_MAIN_DB) + { + mcFile->zFileName = zName; + SQLITE3MC_DEBUG_LOG("mcVfsOpen MAIN: mcFile=%p fileName=%s\n", mcFile, mcFile->zFileName); + } + else if (flags & SQLITE_OPEN_TEMP_DB) + { + mcFile->zFileName = zName; + SQLITE3MC_DEBUG_LOG("mcVfsOpen TEMP: mcFile=%p fileName=%s\n", mcFile, mcFile->zFileName); + } +#if 0 + else if (flags & SQLITE_OPEN_TRANSIENT_DB) + { + /* + ** TODO: When does SQLite open a transient DB? Could/Should it be encrypted? + */ + } +#endif + else if (flags & SQLITE_OPEN_MAIN_JOURNAL) + { + const char* dbFileName = wx_sqlite3_filename_database(zName); + mcFile->pMainDb = mcFindDbMainFileName(mcFile->pVfsMC, dbFileName); + mcFile->zFileName = zName; + SQLITE3MC_DEBUG_LOG("mcVfsOpen MAIN Journal: mcFile=%p fileName=%s dbFileName=%s\n", mcFile, mcFile->zFileName, dbFileName); + } +#if 0 + else if (flags & SQLITE_OPEN_TEMP_JOURNAL) + { + /* + ** TODO: When does SQLite open a temporary journal? Could/Should it be encrypted? + */ + } +#endif + else if (flags & SQLITE_OPEN_SUBJOURNAL) + { + const char* dbFileName = wx_sqlite3_filename_database(zName); + mcFile->pMainDb = mcFindDbMainFileName(mcFile->pVfsMC, dbFileName); + mcFile->zFileName = zName; + SQLITE3MC_DEBUG_LOG("mcVfsOpen SUB Journal: mcFile=%p fileName=%s dbFileName=%s\n", mcFile, mcFile->zFileName, dbFileName); + } +#if 0 + else if (flags & SQLITE_OPEN_MASTER_JOURNAL) + { + /* + ** Master journal contains only administrative information + ** No encryption necessary + */ + } +#endif + else if (flags & SQLITE_OPEN_WAL) + { + const char* dbFileName = wx_sqlite3_filename_database(zName); + mcFile->pMainDb = mcFindDbMainFileName(mcFile->pVfsMC, dbFileName); + mcFile->zFileName = zName; + SQLITE3MC_DEBUG_LOG("mcVfsOpen WAL Journal: mcFile=%p fileName=%s dbFileName=%s\n", mcFile, mcFile->zFileName, dbFileName); + } + } + + rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, mcFile->pFile, flags, pOutFlags); + if (rc == SQLITE_OK) + { + /* + ** Real open succeeded, initialize methods, register main database files + */ + pFile->pMethods = &mcIoMethodsGlobal; + if (flags & SQLITE_OPEN_MAIN_DB) + { + mcMainListAdd(mcFile); + } + } + return rc; +} + +static int mcVfsDelete(wx_sqlite3_vfs* pVfs, const char* zName, int syncDir) +{ + return REALVFS(pVfs)->xDelete(REALVFS(pVfs), zName, syncDir); +} + +static int mcVfsAccess(wx_sqlite3_vfs* pVfs, const char* zName, int flags, int* pResOut) +{ + return REALVFS(pVfs)->xAccess(REALVFS(pVfs), zName, flags, pResOut); +} + +static int mcVfsFullPathname(wx_sqlite3_vfs* pVfs, const char* zName, int nOut, char* zOut) +{ + return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zName, nOut, zOut); +} + +static void* mcVfsDlOpen(wx_sqlite3_vfs* pVfs, const char* zFilename) +{ + return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zFilename); +} + +static void mcVfsDlError(wx_sqlite3_vfs* pVfs, int nByte, char* zErrMsg) +{ + REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg); +} + +static void (*mcVfsDlSym(wx_sqlite3_vfs* pVfs, void* p, const char* zSymbol))(void) +{ + return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSymbol); +} + +static void mcVfsDlClose(wx_sqlite3_vfs* pVfs, void* p) +{ + REALVFS(pVfs)->xDlClose(REALVFS(pVfs), p); +} + +static int mcVfsRandomness(wx_sqlite3_vfs* pVfs, int nByte, char* zOut) +{ + return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zOut); +} + +static int mcVfsSleep(wx_sqlite3_vfs* pVfs, int microseconds) +{ + return REALVFS(pVfs)->xSleep(REALVFS(pVfs), microseconds); +} + +static int mcVfsCurrentTime(wx_sqlite3_vfs* pVfs, double* pOut) +{ + return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pOut); +} + +static int mcVfsGetLastError(wx_sqlite3_vfs* pVfs, int code, char* pOut) +{ + return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), code, pOut); +} + +static int mcVfsCurrentTimeInt64(wx_sqlite3_vfs* pVfs, wx_sqlite3_int64* pOut) +{ + return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), pOut); +} + +static int mcVfsSetSystemCall(wx_sqlite3_vfs* pVfs, const char* zName, wx_sqlite3_syscall_ptr pNewFunc) +{ + return REALVFS(pVfs)->xSetSystemCall(REALVFS(pVfs), zName, pNewFunc); +} + +static wx_sqlite3_syscall_ptr mcVfsGetSystemCall(wx_sqlite3_vfs* pVfs, const char* zName) +{ + return REALVFS(pVfs)->xGetSystemCall(REALVFS(pVfs), zName); +} + +static const char* mcVfsNextSystemCall(wx_sqlite3_vfs* pVfs, const char* zName) +{ + return REALVFS(pVfs)->xNextSystemCall(REALVFS(pVfs), zName); +} + +/* +** IO methods +*/ + +static int mcIoClose(wx_sqlite3_file* pFile) +{ + int rc; + wx_sqlite3mc_file* p = (wx_sqlite3mc_file*) pFile; + + /* + ** Unregister main database files + */ + if (p->openFlags & SQLITE_OPEN_MAIN_DB) + { + mcMainListRemove(p); + } + + /* + ** Release codec memory + */ + if (p->codec) + { + wx_sqlite3mcCodecFree(p->codec); + p->codec = 0; + } + + assert(p->pMainNext == 0 && p->pVfsMC->pMain != p); + rc = REALFILE(pFile)->pMethods->xClose(REALFILE(pFile)); + return rc; +} + +/* +** Read operation on main database file +*/ +static int mcReadMainDb(wx_sqlite3_file* pFile, void* buffer, int count, wx_sqlite3_int64 offset) +{ + int rc = SQLITE_OK; + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + + /* + ** Special case: read 16 bytes salt from beginning of database file without decrypting + */ + if (offset == 0 && count == 16) + { + return rc; + } + + if (mcFile->codec != 0 && wx_sqlite3mcIsEncrypted(mcFile->codec)) + { + const int pageSize = wx_sqlite3mcGetPageSize(mcFile->codec); + const int deltaOffset = offset % pageSize; + const int deltaCount = count % pageSize; + if (deltaOffset || deltaCount) + { + /* + ** Read partial page + */ + int pageNo = 0; + void* bufferDecrypted = 0; + const wx_sqlite3_int64 prevOffset = offset - deltaOffset; + unsigned char* pageBuffer = wx_sqlite3mcGetPageBuffer(mcFile->codec); + + /* + ** Read complete page from file + */ + rc = REALFILE(pFile)->pMethods->xRead(REALFILE(pFile), pageBuffer, pageSize, prevOffset); + if (rc == SQLITE_IOERR_SHORT_READ) + { + return rc; + } + + /* + ** Determine page number and decrypt page buffer + */ + pageNo = prevOffset / pageSize + 1; + bufferDecrypted = wx_sqlite3mcCodec(mcFile->codec, pageBuffer, pageNo, 3); + + /* + ** Return the requested content + */ + if (deltaOffset) + { + memcpy(buffer, pageBuffer + deltaOffset, count); + } + else + { + memcpy(buffer, pageBuffer, count); + } + } + else + { + /* + ** Read full page(s) + ** + ** In fact, SQLite reads only one database page at a time. + ** This would allow to remove the page loop below. + */ + unsigned char* data = (unsigned char*) buffer; + int pageNo = offset / pageSize + 1; + int nPages = count / pageSize; + int iPage; + for (iPage = 0; iPage < nPages; ++iPage) + { + void* bufferDecrypted = wx_sqlite3mcCodec(mcFile->codec, data, pageNo, 3); + data += pageSize; + offset += pageSize; + ++pageNo; + } + } + } + return rc; +} + +/* +** Read operation on main journal file +*/ +static int mcReadMainJournal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +{ + int rc = SQLITE_OK; + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; + + if (codec != 0 && wx_sqlite3mcIsEncrypted(codec)) + { + const int pageSize = wx_sqlite3mcGetPageSize(codec); + + if (count == pageSize && mcFile->pageNo != 0) + { + /* + ** Decrypt the page buffer, but only if the page number is valid + */ + void* bufferDecrypted = wx_sqlite3mcCodec(codec, (char*) buffer, mcFile->pageNo, 3); + mcFile->pageNo = 0; + } + else if (count == 4) + { + /* + ** SQLite always reads the page number from the journal file + ** immediately before the corresponding page content is read. + */ + mcFile->pageNo = wx_sqlite3Get4byte(buffer); + } + } + return rc; +} + +/* +** Read operation on subjournal file +*/ +static int mcReadSubJournal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +{ + int rc = SQLITE_OK; + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; + + if (codec != 0 && wx_sqlite3mcIsEncrypted(codec)) + { + const int pageSize = wx_sqlite3mcGetPageSize(codec); + + if (count == pageSize && mcFile->pageNo != 0) + { + /* + ** Decrypt the page buffer, but only if the page number is valid + */ + void* bufferDecrypted = wx_sqlite3mcCodec(codec, (char*) buffer, mcFile->pageNo, 3); + } + else if (count == 4) + { + /* + ** SQLite always reads the page number from the journal file + ** immediately before the corresponding page content is read. + */ + mcFile->pageNo = wx_sqlite3Get4byte(buffer); + } + } + return rc; +} + +/* +** Read operation on WAL journal file +*/ +static int mcReadWal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +{ + int rc = SQLITE_OK; + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; + + if (codec != 0 && wx_sqlite3mcIsEncrypted(codec)) + { + const int pageSize = wx_sqlite3mcGetPageSize(codec); + + if (count == pageSize) + { + int pageNo = 0; + unsigned char ac[4]; + + /* + ** Determine page number + ** + ** It is necessary to explicitly read the page number from the frame header. + */ + rc = REALFILE(pFile)->pMethods->xRead(REALFILE(pFile), ac, 4, offset - walFrameHeaderSize); + if (rc == SQLITE_OK) + { + pageNo = wx_sqlite3Get4byte(ac); + } + + /* + ** Decrypt page content if page number is valid + */ + if (pageNo != 0) + { + void* bufferDecrypted = wx_sqlite3mcCodec(codec, (char*)buffer, pageNo, 3); + } + } + else if (codec->m_walLegacy != 0 && count == pageSize + walFrameHeaderSize) + { + int pageNo = wx_sqlite3Get4byte(buffer); + + /* + ** Decrypt page content if page number is valid + */ + if (pageNo != 0) + { + void* bufferDecrypted = wx_sqlite3mcCodec(codec, (char*)buffer+walFrameHeaderSize, pageNo, 3); + } + } + } + return rc; +} + +static int mcIoRead(wx_sqlite3_file* pFile, void* buffer, int count, wx_sqlite3_int64 offset) +{ + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + int rc = REALFILE(pFile)->pMethods->xRead(REALFILE(pFile), buffer, count, offset); + if (rc == SQLITE_IOERR_SHORT_READ) + { + return rc; + } + + if (mcFile->openFlags & SQLITE_OPEN_MAIN_DB) + { + rc = mcReadMainDb(pFile, buffer, count, offset); + } +#if 0 + else if (mcFile->openFlags & SQLITE_OPEN_TEMP_DB) + { + /* + ** TODO: Could/Should a temporary database file be encrypted? + */ + } +#endif +#if 0 + else if (mcFile->openFlags & SQLITE_OPEN_TRANSIENT_DB) + { + /* + ** TODO: Could/Should a transient database file be encrypted? + */ + } +#endif + else if (mcFile->openFlags & SQLITE_OPEN_MAIN_JOURNAL) + { + rc = mcReadMainJournal(pFile, buffer, count, offset); + } +#if 0 + else if (mcFile->openFlags & SQLITE_OPEN_TEMP_JOURNAL) + { + /* + ** TODO: Could/Should a temporary journal file be encrypted? + */ + } +#endif + else if (mcFile->openFlags & SQLITE_OPEN_SUBJOURNAL) + { + rc = mcReadSubJournal(pFile, buffer, count, offset); + } +#if 0 + else if (mcFile->openFlags & SQLITE_OPEN_MASTER_JOURNAL) + { + /* + ** Master journal contains only administrative information + ** No encryption necessary + */ + } +#endif + else if (mcFile->openFlags & SQLITE_OPEN_WAL) + { + rc = mcReadWal(pFile, buffer, count, offset); + } + return rc; +} + +/* +** Write operation on main database file +*/ +static int mcWriteMainDb(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +{ + int rc = SQLITE_OK; + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + + if (mcFile->codec != 0 && wx_sqlite3mcIsEncrypted(mcFile->codec)) + { + const int pageSize = wx_sqlite3mcGetPageSize(mcFile->codec); + const int deltaOffset = offset % pageSize; + const int deltaCount = count % pageSize; + + if (deltaOffset || deltaCount) + { + /* + ** Write partial page + ** + ** SQLite does never write partial database pages. + ** Therefore no encryption is required in this case. + */ + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + } + else + { + /* + ** Write full page(s) + ** + ** In fact, SQLite writes only one database page at a time. + ** This would allow to remove the page loop below. + */ + char* data = (char*) buffer; + int pageNo = offset / pageSize + 1; + int nPages = count / pageSize; + int iPage; + for (iPage = 0; iPage < nPages; ++iPage) + { + void* bufferEncrypted = wx_sqlite3mcCodec(mcFile->codec, data, pageNo, 6); + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), bufferEncrypted, pageSize, offset); + data += pageSize; + offset += pageSize; + ++pageNo; + } + } + } + else + { + /* + ** Write buffer without encryption + */ + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + } + return rc; +} + +/* +** Write operation on main journal file +*/ +static int mcWriteMainJournal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +{ + int rc = SQLITE_OK; + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; + + if (codec != 0 && wx_sqlite3mcIsEncrypted(codec)) + { + const int pageSize = wx_sqlite3mcGetPageSize(codec); + const int frameSize = pageSize + 4 + 4; + + if (count == pageSize && mcFile->pageNo != 0) + { + /* + ** Encrypt the page buffer, but only if the page number is valid + */ + void* bufferEncrypted = wx_sqlite3mcCodec(codec, (char*) buffer, mcFile->pageNo, 7); + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), bufferEncrypted, pageSize, offset); + } + else + { + /* + ** Write buffer without encryption + */ + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + if (count == 4) + { + /* + ** SQLite always writes the page number to the journal file + ** immediately before the corresponding page content is written. + */ + mcFile->pageNo = (rc == SQLITE_OK) ? wx_sqlite3Get4byte(buffer) : 0; + } + } + } + else + { + /* + ** Write buffer without encryption + */ + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + } + return rc; +} + +/* +** Write operation on subjournal file +*/ +static int mcWriteSubJournal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +{ + int rc = SQLITE_OK; + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; + + if (codec != 0 && wx_sqlite3mcIsEncrypted(codec)) + { + const int pageSize = wx_sqlite3mcGetPageSize(codec); + const int frameSize = pageSize + 4; + + if (count == pageSize && mcFile->pageNo != 0) + { + /* + ** Encrypt the page buffer, but only if the page number is valid + */ + void* bufferEncrypted = wx_sqlite3mcCodec(codec, (char*) buffer, mcFile->pageNo, 7); + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), bufferEncrypted, pageSize, offset); + } + else + { + /* + ** Write buffer without encryption + */ + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + if (count == 4) + { + /* + ** SQLite always writes the page number to the journal file + ** immediately before the corresponding page content is written. + */ + mcFile->pageNo = (rc == SQLITE_OK) ? wx_sqlite3Get4byte(buffer) : 0; + } + } + } + else + { + /* + ** Write buffer without encryption + */ + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + } + return rc; +} + +/* +** Write operation on WAL journal file +*/ +static int mcWriteWal(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +{ + int rc = SQLITE_OK; + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + Codec* codec = (mcFile->pMainDb) ? mcFile->pMainDb->codec : 0; + + if (codec != 0 && codec->m_walLegacy != 0 && wx_sqlite3mcIsEncrypted(codec)) + { + const int pageSize = wx_sqlite3mcGetPageSize(codec); + + if (count == pageSize) + { + int pageNo = 0; + unsigned char ac[4]; + + /* + ** Read the corresponding page number from the file + ** + ** In WAL mode SQLite does not write the page number of a page to file + ** immediately before writing the corresponding page content. + ** Page numbers and checksums are written to file independently. + ** Therefore it is necessary to explicitly read the page number + ** on writing to file the content of a page. + */ + rc = REALFILE(pFile)->pMethods->xRead(REALFILE(pFile), ac, 4, offset - walFrameHeaderSize); + if (rc == SQLITE_OK) + { + pageNo = wx_sqlite3Get4byte(ac); + } + + if (pageNo != 0) + { + /* + ** Encrypt the page buffer, but only if the page number is valid + */ + void* bufferEncrypted = wx_sqlite3mcCodec(codec, (char*) buffer, pageNo, 7); + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), bufferEncrypted, pageSize, offset); + } + else + { + /* + ** Write buffer without encryption if the page number could not be determined + */ + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + } + } + else if (count == pageSize + walFrameHeaderSize) + { + int pageNo = wx_sqlite3Get4byte(buffer); + if (pageNo != 0) + { + /* + ** Encrypt the page buffer, but only if the page number is valid + */ + void* bufferEncrypted = wx_sqlite3mcCodec(codec, (char*)buffer+walFrameHeaderSize, pageNo, 7); + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, walFrameHeaderSize, offset); + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), bufferEncrypted, pageSize, offset+walFrameHeaderSize); + } + else + { + /* + ** Write buffer without encryption if the page number could not be determined + */ + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + } + } + else + { + /* + ** Write buffer without encryption if it is not a database page + */ + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + } + } + else + { + /* + ** Write buffer without encryption + */ + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + } + return rc; +} + +static int mcIoWrite(wx_sqlite3_file* pFile, const void* buffer, int count, wx_sqlite3_int64 offset) +{ + int rc = SQLITE_OK; + int doDefault = 1; + wx_sqlite3mc_file* mcFile = (wx_sqlite3mc_file*) pFile; + + if (mcFile->openFlags & SQLITE_OPEN_MAIN_DB) + { + rc = mcWriteMainDb(pFile, buffer, count, offset); + } +#if 0 + else if (mcFile->openFlags & SQLITE_OPEN_TEMP_DB) + { + /* + ** TODO: Could/Should a temporary database file be encrypted? + */ + } +#endif +#if 0 + else if (mcFile->openFlags & SQLITE_OPEN_TRANSIENT_DB) + { + /* + ** TODO: Could/Should a transient database file be encrypted? + */ + } +#endif + else if (mcFile->openFlags & SQLITE_OPEN_MAIN_JOURNAL) + { + rc = mcWriteMainJournal(pFile, buffer, count, offset); + } +#if 0 + else if (mcFile->openFlags & SQLITE_OPEN_TEMP_JOURNAL) + { + /* + ** TODO: Could/Should a temporary journal file be encrypted? + */ + } +#endif + else if (mcFile->openFlags & SQLITE_OPEN_SUBJOURNAL) + { + rc = mcWriteSubJournal(pFile, buffer, count, offset); +} +#if 0 + else if (mcFile->openFlags & SQLITE_OPEN_MASTER_JOURNAL) + { + /* + ** Master journal contains only administrative information + ** No encryption necessary + */ + } +#endif + /* + ** The page content is encrypted in memory in the WAL journal handler. + ** This provides for compatibility with legacy applications using the + ** previous SQLITE_HAS_CODEC encryption API. + */ + else if (mcFile->openFlags & SQLITE_OPEN_WAL) + { + rc = mcWriteWal(pFile, buffer, count, offset); + } + else + { + rc = REALFILE(pFile)->pMethods->xWrite(REALFILE(pFile), buffer, count, offset); + } + return rc; +} + +static int mcIoTruncate(wx_sqlite3_file* pFile, wx_sqlite3_int64 size) +{ + return REALFILE(pFile)->pMethods->xTruncate(REALFILE(pFile), size); +} + +static int mcIoSync(wx_sqlite3_file* pFile, int flags) +{ + return REALFILE(pFile)->pMethods->xSync(REALFILE(pFile), flags); +} + +static int mcIoFileSize(wx_sqlite3_file* pFile, wx_sqlite3_int64* pSize) +{ + return REALFILE(pFile)->pMethods->xFileSize(REALFILE(pFile), pSize); +} + +static int mcIoLock(wx_sqlite3_file* pFile, int lock) +{ + return REALFILE(pFile)->pMethods->xLock(REALFILE(pFile), lock); +} + +static int mcIoUnlock(wx_sqlite3_file* pFile, int lock) +{ + return REALFILE(pFile)->pMethods->xUnlock(REALFILE(pFile), lock); +} + +static int mcIoCheckReservedLock(wx_sqlite3_file* pFile, int* pResOut) +{ + return REALFILE(pFile)->pMethods->xCheckReservedLock(REALFILE(pFile), pResOut); +} + +static int mcIoFileControl(wx_sqlite3_file* pFile, int op, void* pArg) +{ + int rc = SQLITE_OK; + int doReal = 1; + wx_sqlite3mc_file* p = (wx_sqlite3mc_file*) pFile; + + switch (op) + { + case SQLITE_FCNTL_PDB: + { +#if 0 + /* + ** pArg points to the wx_sqlite3* handle for which the database file was opened. + ** In shared cache mode this function is invoked for every use of the database + ** file in a connection. Unfortunately there is no notification, when a database + ** file is no longer used by a connection (close in normal mode). + ** + ** For now, the database handle will not be stored in the file object. + ** In the future, this behaviour may be changed, especially, if shared cache mode + ** is disabled. Shared cache mode is enabled for backward compatibility only, its + ** use is not recommended. A future version of SQLite might disable it by default. + */ + wx_sqlite3* db = *((wx_sqlite3**) pArg); +#endif + } + break; + case SQLITE_FCNTL_PRAGMA: + { + /* + ** Handle pragmas specific to this database file + */ +#if 0 + /* + ** SQLite invokes this function for all pragmas, which are related to the schema + ** associated with this database file. In case of an unknown pragma, this function + ** should return SQLITE_NOTFOUND. However, since this VFS is just a shim, handling + ** of the pragma is forwarded to the underlying real VFS in such a case. + ** + ** For now, all pragmas are handled at the connection level. + ** For this purpose the SQLite's pragma handling is intercepted. + ** The latter requires a patch of SQLite's amalgamation code. + ** Maybe a future version will be able to abandon the patch. + */ + char* pragmaName = ((char**) pArg)[1]; + char* pragmaValue = ((char**) pArg)[2]; + if (wx_sqlite3StrICmp(pragmaName, "...") == 0) + { + /* Action */ + /* ((char**) pArg)[0] = wx_sqlite3_mprintf("error msg.");*/ + doReal = 0; + } +#endif + } + break; + default: + break; + } + if (doReal) + { + rc = REALFILE(pFile)->pMethods->xFileControl(REALFILE(pFile), op, pArg); + if (rc == SQLITE_OK && op == SQLITE_FCNTL_VFSNAME) + { + wx_sqlite3mc_vfs* pVfsMC = p->pVfsMC; + char* zIn = *(char**)pArg; + char* zOut = wx_sqlite3_mprintf("%s/%z", pVfsMC->base.zName, zIn); + *(char**)pArg = zOut; + if (zOut == 0) rc = SQLITE_NOMEM; + } + } + return rc; +} + +static int mcIoSectorSize(wx_sqlite3_file* pFile) +{ + return REALFILE(pFile)->pMethods->xSectorSize(REALFILE(pFile)); +} + +static int mcIoDeviceCharacteristics(wx_sqlite3_file* pFile) +{ + return REALFILE(pFile)->pMethods->xDeviceCharacteristics(REALFILE(pFile)); +} + +static int mcIoShmMap(wx_sqlite3_file* pFile, int iPg, int pgsz, int map, void volatile** p) +{ + return REALFILE(pFile)->pMethods->xShmMap(REALFILE(pFile), iPg, pgsz, map, p); +} + +static int mcIoShmLock(wx_sqlite3_file* pFile, int offset, int n, int flags) +{ + return REALFILE(pFile)->pMethods->xShmLock(REALFILE(pFile), offset, n, flags); +} + +static void mcIoShmBarrier(wx_sqlite3_file* pFile) +{ + REALFILE(pFile)->pMethods->xShmBarrier(REALFILE(pFile)); +} + +static int mcIoShmUnmap(wx_sqlite3_file* pFile, int deleteFlag) +{ + return REALFILE(pFile)->pMethods->xShmUnmap(REALFILE(pFile), deleteFlag); +} + +static int mcIoFetch(wx_sqlite3_file* pFile, wx_sqlite3_int64 iOfst, int iAmt, void** pp) +{ + return REALFILE(pFile)->pMethods->xFetch(REALFILE(pFile), iOfst, iAmt, pp); +} + +static int mcIoUnfetch( wx_sqlite3_file* pFile, wx_sqlite3_int64 iOfst, void* p) +{ + return REALFILE(pFile)->pMethods->xUnfetch(REALFILE(pFile), iOfst, p); +} + +/* +** SQLite3 Multiple Ciphers internal API functions +*/ + +/* +** Check the requested VFS +*/ +SQLITE_PRIVATE int +wx_sqlite3mcCheckVfs(const char* zVfs) +{ + int rc = SQLITE_OK; + wx_sqlite3_vfs* pVfs = wx_sqlite3_vfs_find(zVfs); + if (pVfs == NULL) + { + /* VFS not found */ + int prefixLen = (int) strlen(SQLITE3MC_VFS_NAME); + if (strncmp(zVfs, SQLITE3MC_VFS_NAME, prefixLen) == 0) + { + /* VFS name starts with prefix. */ + const char* zVfsNameEnd = zVfs + strlen(SQLITE3MC_VFS_NAME); + if (*zVfsNameEnd == '-') + { + /* Prefix separator found, determine the name of the real VFS. */ + const char* zVfsReal = zVfsNameEnd + 1; + pVfs = wx_sqlite3_vfs_find(zVfsReal); + if (pVfs != NULL) + { + /* Real VFS exists */ + /* Create VFS with encryption support based on real VFS */ + rc = wx_sqlite3mc_vfs_create(zVfsReal, 0); + } + } + } + } + return rc; +} + +/* +** SQLite3 Multiple Ciphers external API functions +*/ + +static void mcVfsDestroy(wx_sqlite3_vfs* pVfs) +{ + if (pVfs && pVfs->xOpen == mcVfsOpen) + { + /* Destroy the VFS instance only if no file is referring to it any longer */ + if (((wx_sqlite3mc_vfs*) pVfs)->pMain == 0) + { + wx_sqlite3_mutex_free(((wx_sqlite3mc_vfs*)pVfs)->mutex); + wx_sqlite3_vfs_unregister(pVfs); + wx_sqlite3_free(pVfs); + } + } +} + +/* +** Unregister and destroy a Multiple Ciphers VFS +** created by an earlier call to wx_sqlite3mc_vfs_create(). +*/ +SQLITE_API void wx_sqlite3mc_vfs_destroy(const char* zName) +{ + mcVfsDestroy(wx_sqlite3_vfs_find(zName)); +} + +/* +** Create a Multiple Ciphers VFS based on the underlying VFS with name given by zVfsReal. +** If makeDefault is true, the VFS is set as the default VFS. +*/ +SQLITE_API int wx_sqlite3mc_vfs_create(const char* zVfsReal, int makeDefault) +{ + static wx_sqlite3_vfs mcVfsTemplate = + { + 3, /* iVersion */ + 0, /* szOsFile */ + 1024, /* mxPathname */ + 0, /* pNext */ + 0, /* zName */ + 0, /* pAppData */ + mcVfsOpen, /* xOpen */ + mcVfsDelete, /* xDelete */ + mcVfsAccess, /* xAccess */ + mcVfsFullPathname, /* xFullPathname */ +#ifndef SQLITE_OMIT_LOAD_EXTENSION + mcVfsDlOpen, /* xDlOpen */ + mcVfsDlError, /* xDlError */ + mcVfsDlSym, /* xDlSym */ + mcVfsDlClose, /* xDlClose */ +#else + 0, 0, 0, 0, +#endif + mcVfsRandomness, /* xRandomness */ + mcVfsSleep, /* xSleep */ + mcVfsCurrentTime, /* xCurrentTime */ + mcVfsGetLastError, /* xGetLastError */ + mcVfsCurrentTimeInt64, /* xCurrentTimeInt64 */ + mcVfsSetSystemCall, /* xSetSystemCall */ + mcVfsGetSystemCall, /* xGetSystemCall */ + mcVfsNextSystemCall /* xNextSystemCall */ + }; + wx_sqlite3mc_vfs* pVfsNew = 0; /* Newly allocated VFS */ + wx_sqlite3_vfs* pVfsReal = wx_sqlite3_vfs_find(zVfsReal); /* Real VFS */ + int rc; + + if (pVfsReal) + { + size_t nPrefix = strlen(SQLITE3MC_VFS_NAME); + size_t nRealName = strlen(pVfsReal->zName); + size_t nName = nPrefix + nRealName + 1; + size_t nByte = sizeof(wx_sqlite3mc_vfs) + nName + 1; + pVfsNew = (wx_sqlite3mc_vfs*) wx_sqlite3_malloc64(nByte); + if (pVfsNew) + { + char* zSpace = (char*) &pVfsNew[1]; + memset(pVfsNew, 0, nByte); + memcpy(&pVfsNew->base, &mcVfsTemplate, sizeof(wx_sqlite3_vfs)); + pVfsNew->base.iVersion = pVfsReal->iVersion; + pVfsNew->base.pAppData = pVfsReal; + pVfsNew->base.mxPathname = pVfsReal->mxPathname; + pVfsNew->base.szOsFile = sizeof(wx_sqlite3mc_file) + pVfsReal->szOsFile; + + /* Set name of new VFS as combination of the multiple ciphers prefix and the name of the underlying VFS */ + pVfsNew->base.zName = (const char*) zSpace; + memcpy(zSpace, SQLITE3MC_VFS_NAME, nPrefix); + memcpy(zSpace + nPrefix, "-", 1); + memcpy(zSpace + nPrefix + 1, pVfsReal->zName, nRealName); + + /* Allocate the mutex and register the new VFS */ + pVfsNew->mutex = wx_sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE); + if (pVfsNew->mutex) + { + rc = wx_sqlite3_vfs_register(&pVfsNew->base, makeDefault); + if (rc != SQLITE_OK) + { + wx_sqlite3_mutex_free(pVfsNew->mutex); + } + } + else + { + /* Mutex could not be allocated */ + rc = SQLITE_NOMEM; + } + if (rc != SQLITE_OK) + { + /* Mutex could not be allocated or new VFS could not be registered */ + wx_sqlite3_free(pVfsNew); + } + } + else + { + /* New VFS could not be allocated */ + rc = SQLITE_NOMEM; + } + } + else + { + /* Underlying VFS not found */ + rc = SQLITE_NOTFOUND; + } + return rc; +} + +/* +** Shutdown all registered SQLite3 Multiple Ciphers VFSs +*/ +SQLITE_API void wx_sqlite3mc_vfs_shutdown() +{ + wx_sqlite3_vfs* pVfs; + wx_sqlite3_vfs* pVfsNext; + for (pVfs = wx_sqlite3_vfs_find(0); pVfs; pVfs = pVfsNext) + { + pVfsNext = pVfs->pNext; + mcVfsDestroy(pVfs); + } +} +/*** End of #include "wx_sqlite3mc_vfs.c" ***/ + + +static int +mcRegisterCodecExtensions(wx_sqlite3* db, char** pzErrMsg, const wx_sqlite3_api_routines* pApi) +{ + int rc = SQLITE_OK; + CodecParameter* codecParameterTable = NULL; + + if (wx_sqlite3FindFunction(db, "wx_sqlite3mc_config_table", 1, SQLITE_UTF8, 0) != NULL) + { + /* Return if codec extension functions are already defined */ + return rc; + } + + /* Generate copy of global codec parameter table */ + codecParameterTable = wx_sqlite3mcCloneCodecParameterTable(); + rc = (codecParameterTable != NULL) ? SQLITE_OK : SQLITE_NOMEM; + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_create_function_v2(db, "wx_sqlite3mc_config_table", 0, SQLITE_UTF8 | SQLITE_DETERMINISTIC, + codecParameterTable, wx_sqlite3mcConfigTable, 0, 0, (void(*)(void*)) wx_sqlite3mcFreeCodecParameterTable); + } + + rc = (codecParameterTable != NULL) ? SQLITE_OK : SQLITE_NOMEM; + if (rc == SQLITE_OK) { rc = wx_sqlite3_create_function(db, "wx_sqlite3mc_config", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, codecParameterTable, wx_sqlite3mcConfigParams, 0, 0); } - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_create_function(db, "wx_sqlite3mc_config", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, - codecParameterTable, wx_sqlite3mcConfigParams, 0, 0); + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_create_function(db, "wx_sqlite3mc_config", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, + codecParameterTable, wx_sqlite3mcConfigParams, 0, 0); + } + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_create_function(db, "wx_sqlite3mc_config", 3, SQLITE_UTF8 | SQLITE_DETERMINISTIC, + codecParameterTable, wx_sqlite3mcConfigParams, 0, 0); + } + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_create_function(db, "wx_sqlite3mc_codec_data", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, + NULL, wx_sqlite3mcCodecDataSql, 0, 0); + } + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_create_function(db, "wx_sqlite3mc_codec_data", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, + NULL, wx_sqlite3mcCodecDataSql, 0, 0); + } + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_create_function(db, "wx_sqlite3mc_version", 0, SQLITE_UTF8 | SQLITE_DETERMINISTIC, + NULL, wx_sqlite3mcVersion, 0, 0); + } + return rc; +} + +#ifdef SQLITE_ENABLE_EXTFUNC +static int +wx_sqlite3_extfunc_init(wx_sqlite3 *db, char **pzErrMsg, const wx_sqlite3_api_routines *pApi) +{ + return RegisterExtensionFunctions(db); +} +#endif + +static int +mcCheckValidName(char* name) +{ + size_t nl; + if (!name) + return SQLITE_ERROR; + + /* Check for valid cipher name length */ + nl = strlen(name); + if (nl < 1 || nl >= CIPHER_NAME_MAXLEN) + return SQLITE_ERROR; + + /* Check for already registered names */ + CipherName* cipherNameTable = &globalCipherNameTable[0]; + for (; cipherNameTable->m_name[0] != 0; ++cipherNameTable) + { + if (wx_sqlite3_stricmp(name, cipherNameTable->m_name) == 0) break; + } + if (cipherNameTable->m_name[0] != 0) + return SQLITE_ERROR; + + /* Check for valid character set (1st char = alpha, rest = alpha-numeric or underscore) */ + if (wx_sqlite3Isalpha(name[0])) + { + size_t j; + for (j = 1; j < nl && (name[j] == '_' || wx_sqlite3Isalnum(name[j])); ++j) {} + if (j == nl) + return SQLITE_OK; + } + return SQLITE_ERROR; +} + +SQLITE_PRIVATE int +wx_sqlite3mcGetGlobalCipherCount() +{ + int cipherCount = 0; + wx_sqlite3_mutex_enter(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MAIN)); + cipherCount = globalCipherCount; + wx_sqlite3_mutex_leave(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MAIN)); + return cipherCount; +} + +static int +wx_sqlite3mcRegisterCipher(const CipherDescriptor* desc, const CipherParams* params, int makeDefault) +{ + int rc; + int np; + CipherParams* cipherParams; + + /* Sanity checks */ + + /* Cipher description AND parameter are required */ + if (!desc || !params) + return SQLITE_ERROR; + + /* ALL methods of the cipher descriptor need to be defined */ + if (!desc->m_name || + !desc->m_allocateCipher || + !desc->m_freeCipher || + !desc->m_cloneCipher || + !desc->m_getLegacy || + !desc->m_getPageSize || + !desc->m_getReserved || + !desc->m_getSalt || + !desc->m_generateKey || + !desc->m_encryptPage || + !desc->m_decryptPage) + return SQLITE_ERROR; + + /* Check for valid cipher name */ + if (mcCheckValidName(desc->m_name) != SQLITE_OK) + return SQLITE_ERROR; + + /* Check cipher parameters */ + for (np = 0; np < CIPHER_PARAMS_COUNT_MAX; ++np) + { + CipherParams entry = params[np]; + /* Check for sentinel parameter */ + if (entry.m_name == 0 || entry.m_name[0] == 0) + break; + /* Check for valid parameter name */ + if (mcCheckValidName(entry.m_name) != SQLITE_OK) + return SQLITE_ERROR; + /* Check for valid parameter specification */ + if (!(entry.m_minValue >= 0 && entry.m_maxValue >= 0 && entry.m_minValue <= entry.m_maxValue && + entry.m_value >= entry.m_minValue && entry.m_value <= entry.m_maxValue && + entry.m_default >= entry.m_minValue && entry.m_default <= entry.m_maxValue)) + return SQLITE_ERROR; + } + + /* Check for parameter count in valid range and valid sentinel parameter */ + if (np >= CIPHER_PARAMS_COUNT_MAX || params[np].m_name == 0) + return SQLITE_ERROR; + + /* Sanity checks were successful, now register cipher */ + + cipherParams = (CipherParams*) wx_sqlite3_malloc((np+1) * sizeof(CipherParams)); + if (!cipherParams) + return SQLITE_NOMEM; + + wx_sqlite3_mutex_enter(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MAIN)); + + /* Check for */ + if (globalCipherCount < CODEC_COUNT_MAX) + { + int n; + char* cipherName; + ++globalCipherCount; + cipherName = globalCipherNameTable[globalCipherCount].m_name; + strcpy(cipherName, desc->m_name); + + globalCodecDescriptorTable[globalCipherCount - 1] = *desc; + globalCodecDescriptorTable[globalCipherCount - 1].m_name = cipherName; + + globalCodecParameterTable[globalCipherCount].m_name = cipherName; + globalCodecParameterTable[globalCipherCount].m_id = globalCipherCount; + globalCodecParameterTable[globalCipherCount].m_params = cipherParams; + + /* Copy parameters */ + for (n = 0; n < np; ++n) + { + cipherParams[n] = params[n]; + cipherParams[n].m_name = (char*) wx_sqlite3_malloc((int) strlen(params[n].m_name) + 1); + strcpy(cipherParams[n].m_name, params[n].m_name); + } + /* Add sentinel */ + cipherParams[n] = params[n]; + cipherParams[n].m_name = globalSentinelName; + + /* Make cipher default, if requested */ + if (makeDefault) + { + CipherParams* param = globalCodecParameterTable[0].m_params; + for (; param->m_name[0] != 0; ++param) + { + if (wx_sqlite3_stricmp("cipher", param->m_name) == 0) break; + } + if (param->m_name[0] != 0) + { + param->m_value = param->m_default = globalCipherCount; + } + } + + rc = SQLITE_OK; + } + else + { + rc = SQLITE_NOMEM; + } + + wx_sqlite3_mutex_leave(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MAIN)); + + return rc; +} + +SQLITE_API int +wx_sqlite3mc_register_cipher(const CipherDescriptor* desc, const CipherParams* params, int makeDefault) +{ + int rc; +#ifndef SQLITE_OMIT_AUTOINIT + rc = wx_sqlite3_initialize(); + if (rc) return rc; +#endif + return wx_sqlite3mcRegisterCipher(desc, params, makeDefault); +} + +SQLITE_PRIVATE int +wx_sqlite3mcInitCipherTables() +{ + size_t n; + + /* Initialize cipher name table */ + strcpy(globalCipherNameTable[0].m_name, "global"); + for (n = 1; n < CODEC_COUNT_MAX + 2; ++n) + { + strcpy(globalCipherNameTable[n].m_name, ""); + } + + /* Initialize cipher descriptor table */ + for (n = 0; n < CODEC_COUNT_MAX + 1; ++n) + { + globalCodecDescriptorTable[n] = mcSentinelDescriptor; + } + + /* Initialize cipher parameter table */ + globalCodecParameterTable[0] = globalCommonParams; + for (n = 1; n < CODEC_COUNT_MAX + 2; ++n) + { + globalCodecParameterTable[n] = globalSentinelParams; + } + + return SQLITE_OK; +} + +SQLITE_PRIVATE void +wx_sqlite3mcTermCipherTables() +{ + size_t n; + for (n = CODEC_COUNT_MAX+1; n > 0; --n) + { + if (globalCodecParameterTable[n].m_name[0] != 0) + { + int k; + CipherParams* params = globalCodecParameterTable[n].m_params; + for (k = 0; params[k].m_name[0] != 0; ++k) + { + wx_sqlite3_free(params[k].m_name); + } + wx_sqlite3_free(globalCodecParameterTable[n].m_params); + } + } +} + +int +wx_sqlite3mc_initialize(const char* arg) +{ + int rc = wx_sqlite3mcInitCipherTables(); +#if HAVE_CIPHER_AES_128_CBC + if (rc == SQLITE_OK) + { + rc = wx_sqlite3mcRegisterCipher(&mcAES128Descriptor, mcAES128Params, (CODEC_TYPE_AES128 == CODEC_TYPE)); + } +#endif +#if HAVE_CIPHER_AES_256_CBC + if (rc == SQLITE_OK) + { + rc = wx_sqlite3mcRegisterCipher(&mcAES256Descriptor, mcAES256Params, (CODEC_TYPE_AES256 == CODEC_TYPE)); + } +#endif +#if HAVE_CIPHER_CHACHA20 + if (rc == SQLITE_OK) + { + rc = wx_sqlite3mcRegisterCipher(&mcChaCha20Descriptor, mcChaCha20Params, (CODEC_TYPE_CHACHA20 == CODEC_TYPE)); + } +#endif +#if HAVE_CIPHER_SQLCIPHER + if (rc == SQLITE_OK) + { + rc = wx_sqlite3mcRegisterCipher(&mcSQLCipherDescriptor, mcSQLCipherParams, (CODEC_TYPE_SQLCIPHER == CODEC_TYPE)); + } +#endif +#if HAVE_CIPHER_RC4 + if (rc == SQLITE_OK) + { + rc = wx_sqlite3mcRegisterCipher(&mcRC4Descriptor, mcRC4Params, (CODEC_TYPE_RC4 == CODEC_TYPE)); + } +#endif + + /* + ** Initialize and register MultiCipher VFS as default VFS + ** if it isn't already registered + */ + if (rc == SQLITE_OK) + { + rc = wx_sqlite3mc_vfs_create(NULL, 1); + } + + /* + ** Register Multi Cipher extension + */ + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) mcRegisterCodecExtensions); + } +#ifdef SQLITE_ENABLE_EXTFUNC + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_extfunc_init); + } +#endif +#ifdef SQLITE_ENABLE_CSV + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_csv_init); + } +#endif +#ifdef SQLITE_ENABLE_VSV + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_vsv_init); + } +#endif +#ifdef SQLITE_ENABLE_SHA3 + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_shathree_init); + } +#endif +#ifdef SQLITE_ENABLE_CARRAY + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_carray_init); + } +#endif +#ifdef SQLITE_ENABLE_FILEIO + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_fileio_init); + } +#endif +#ifdef SQLITE_ENABLE_SERIES + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_series_init); + } +#endif +#ifdef SQLITE_ENABLE_UUID + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_uuid_init); + } +#endif +#ifdef SQLITE_ENABLE_REGEXP + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_regexp_init); + } +#endif +#ifdef SQLITE_ENABLE_COMPRESS + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_compress_init); + } +#endif +#ifdef SQLITE_ENABLE_SQLAR + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_sqlar_init); + } +#endif +#ifdef SQLITE_ENABLE_ZIPFILE + if (rc == SQLITE_OK) + { + rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_zipfile_init); + } +#endif + return rc; +} + +void +wx_sqlite3mc_shutdown(void) +{ + wx_sqlite3mc_vfs_shutdown(); + wx_sqlite3mcTermCipherTables(); +} + +/* +** TCL/TK Extension and/or Shell +*/ +#ifdef SQLITE_ENABLE_TCL +/* #include "tclsqlite.c" */ +/*** Begin of #include "tclsqlite.c" ***/ +/* +** 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. +** +************************************************************************* +** A TCL Interface to SQLite. Append this file to wx_sqlite3.c and +** compile the whole thing to build a TCL-enabled version of SQLite. +** +** Compile-time options: +** +** -DTCLSH Add a "main()" routine that works as a tclsh. +** +** -DTCLSH_INIT_PROC=name +** +** Invoke name(interp) to initialize the Tcl interpreter. +** If name(interp) returns a non-NULL string, then run +** that string as a Tcl script to launch the application. +** If name(interp) returns NULL, then run the regular +** tclsh-emulator code. +*/ +#ifdef TCLSH_INIT_PROC +# define TCLSH 1 +#endif + +/* +** If requested, include the SQLite compiler options file for MSVC. +*/ +#if defined(INCLUDE_MSVC_H) +# include "msvc.h" +#endif + +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif +#include + +/* +** Some additional include files are needed if this file is not +** appended to the amalgamation. +*/ +#ifndef SQLITE_AMALGAMATION +/* # include "wx_sqlite3.h" */ + +# include +# include +# include + typedef unsigned char u8; +#endif +#include + +/* Used to get the current process ID */ +#if !defined(_WIN32) +# include +# include +# define GETPID getpid +#elif !defined(_WIN32_WCE) +# ifndef SQLITE_AMALGAMATION +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# endif +# include +# define isatty(h) _isatty(h) +# define GETPID (int)GetCurrentProcessId +#endif + +/* + * Windows needs to know which symbols to export. Unix does not. + * BUILD_sqlite should be undefined for Unix. + */ +#ifdef BUILD_sqlite +#undef TCL_STORAGE_CLASS +#define TCL_STORAGE_CLASS DLLEXPORT +#endif /* BUILD_sqlite */ + +#define NUM_PREPARED_STMTS 10 +#define MAX_PREPARED_STMTS 100 + +/* Forward declaration */ +typedef struct SqliteDb SqliteDb; + +/* +** New SQL functions can be created as TCL scripts. Each such function +** is described by an instance of the following structure. +** +** Variable eType may be set to SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, +** SQLITE_BLOB or SQLITE_NULL. If it is SQLITE_NULL, then the implementation +** attempts to determine the type of the result based on the Tcl object. +** If it is SQLITE_TEXT or SQLITE_BLOB, then a text (wx_sqlite3_result_text()) +** or blob (wx_sqlite3_result_blob()) is returned. If it is SQLITE_INTEGER +** or SQLITE_FLOAT, then an attempt is made to return an integer or float +** value, falling back to float and then text if this is not possible. +*/ +typedef struct SqlFunc SqlFunc; +struct SqlFunc { + Tcl_Interp *interp; /* The TCL interpret to execute the function */ + Tcl_Obj *pScript; /* The Tcl_Obj representation of the script */ + SqliteDb *pDb; /* Database connection that owns this function */ + int useEvalObjv; /* True if it is safe to use Tcl_EvalObjv */ + int eType; /* Type of value to return */ + char *zName; /* Name of this function */ + SqlFunc *pNext; /* Next function on the list of them all */ +}; + +/* +** New collation sequences function can be created as TCL scripts. Each such +** function is described by an instance of the following structure. +*/ +typedef struct SqlCollate SqlCollate; +struct SqlCollate { + Tcl_Interp *interp; /* The TCL interpret to execute the function */ + char *zScript; /* The script to be run */ + SqlCollate *pNext; /* Next function on the list of them all */ +}; + +/* +** Prepared statements are cached for faster execution. Each prepared +** statement is described by an instance of the following structure. +*/ +typedef struct SqlPreparedStmt SqlPreparedStmt; +struct SqlPreparedStmt { + SqlPreparedStmt *pNext; /* Next in linked list */ + SqlPreparedStmt *pPrev; /* Previous on the list */ + wx_sqlite3_stmt *pStmt; /* The prepared statement */ + int nSql; /* chars in zSql[] */ + const char *zSql; /* Text of the SQL statement */ + int nParm; /* Size of apParm array */ + Tcl_Obj **apParm; /* Array of referenced object pointers */ +}; + +typedef struct IncrblobChannel IncrblobChannel; + +/* +** There is one instance of this structure for each SQLite database +** that has been opened by the SQLite TCL interface. +** +** If this module is built with SQLITE_TEST defined (to create the SQLite +** testfixture executable), then it may be configured to use either +** wx_sqlite3_prepare_v2() or wx_sqlite3_prepare() to prepare SQL statements. +** If SqliteDb.bLegacyPrepare is true, wx_sqlite3_prepare() is used. +*/ +struct SqliteDb { + wx_sqlite3 *db; /* The "real" database structure. MUST BE FIRST */ + Tcl_Interp *interp; /* The interpreter used for this database */ + char *zBusy; /* The busy callback routine */ + char *zCommit; /* The commit hook callback routine */ + char *zTrace; /* The trace callback routine */ + char *zTraceV2; /* The trace_v2 callback routine */ + char *zProfile; /* The profile callback routine */ + char *zProgress; /* The progress callback routine */ + char *zBindFallback; /* Callback to invoke on a binding miss */ + char *zAuth; /* The authorization callback routine */ + int disableAuth; /* Disable the authorizer if it exists */ + char *zNull; /* Text to substitute for an SQL NULL value */ + SqlFunc *pFunc; /* List of SQL functions */ + Tcl_Obj *pUpdateHook; /* Update hook script (if any) */ + Tcl_Obj *pPreUpdateHook; /* Pre-update hook script (if any) */ + Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */ + Tcl_Obj *pWalHook; /* WAL hook script (if any) */ + Tcl_Obj *pUnlockNotify; /* Unlock notify script (if any) */ + SqlCollate *pCollate; /* List of SQL collation functions */ + int rc; /* Return code of most recent wx_sqlite3_exec() */ + Tcl_Obj *pCollateNeeded; /* Collation needed script */ + SqlPreparedStmt *stmtList; /* List of prepared statements*/ + SqlPreparedStmt *stmtLast; /* Last statement in the list */ + int maxStmt; /* The next maximum number of stmtList */ + int nStmt; /* Number of statements in stmtList */ + IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */ + int nStep, nSort, nIndex; /* Statistics for most recent operation */ + int nVMStep; /* Another statistic for most recent operation */ + int nTransaction; /* Number of nested [transaction] methods */ + int openFlags; /* Flags used to open. (SQLITE_OPEN_URI) */ + int nRef; /* Delete object when this reaches 0 */ +#ifdef SQLITE_TEST + int bLegacyPrepare; /* True to use wx_sqlite3_prepare() */ +#endif +}; + +struct IncrblobChannel { + wx_sqlite3_blob *pBlob; /* wx_sqlite3 blob handle */ + SqliteDb *pDb; /* Associated database connection */ + int iSeek; /* Current seek offset */ + Tcl_Channel channel; /* Channel identifier */ + IncrblobChannel *pNext; /* Linked list of all open incrblob channels */ + IncrblobChannel *pPrev; /* Linked list of all open incrblob channels */ +}; + +/* +** 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); +} + + +#ifndef SQLITE_OMIT_INCRBLOB +/* +** Close all incrblob channels opened using database connection pDb. +** This is called when shutting down the database connection. +*/ +static void closeIncrblobChannels(SqliteDb *pDb){ + IncrblobChannel *p; + IncrblobChannel *pNext; + + for(p=pDb->pIncrblob; p; p=pNext){ + pNext = p->pNext; + + /* Note: Calling unregister here call Tcl_Close on the incrblob channel, + ** which deletes the IncrblobChannel structure at *p. So do not + ** call Tcl_Free() here. + */ + Tcl_UnregisterChannel(pDb->interp, p->channel); + } +} + +/* +** Close an incremental blob channel. +*/ +static int SQLITE_TCLAPI incrblobClose( + ClientData instanceData, + Tcl_Interp *interp +){ + IncrblobChannel *p = (IncrblobChannel *)instanceData; + int rc = wx_sqlite3_blob_close(p->pBlob); + wx_sqlite3 *db = p->pDb->db; + + /* Remove the channel from the SqliteDb.pIncrblob list. */ + if( p->pNext ){ + p->pNext->pPrev = p->pPrev; + } + if( p->pPrev ){ + p->pPrev->pNext = p->pNext; + } + if( p->pDb->pIncrblob==p ){ + p->pDb->pIncrblob = p->pNext; + } + + /* Free the IncrblobChannel structure */ + Tcl_Free((char *)p); + + if( rc!=SQLITE_OK ){ + Tcl_SetResult(interp, (char *)wx_sqlite3_errmsg(db), TCL_VOLATILE); + return TCL_ERROR; + } + return TCL_OK; +} + +/* +** Read data from an incremental blob channel. +*/ +static int SQLITE_TCLAPI incrblobInput( + ClientData instanceData, + char *buf, + int bufSize, + int *errorCodePtr +){ + IncrblobChannel *p = (IncrblobChannel *)instanceData; + int nRead = bufSize; /* Number of bytes to read */ + int nBlob; /* Total size of the blob */ + int rc; /* sqlite error code */ + + nBlob = wx_sqlite3_blob_bytes(p->pBlob); + if( (p->iSeek+nRead)>nBlob ){ + nRead = nBlob-p->iSeek; + } + if( nRead<=0 ){ + return 0; + } + + rc = wx_sqlite3_blob_read(p->pBlob, (void *)buf, nRead, p->iSeek); + if( rc!=SQLITE_OK ){ + *errorCodePtr = rc; + return -1; + } + + p->iSeek += nRead; + return nRead; +} + +/* +** Write data to an incremental blob channel. +*/ +static int SQLITE_TCLAPI incrblobOutput( + ClientData instanceData, + CONST char *buf, + int toWrite, + int *errorCodePtr +){ + IncrblobChannel *p = (IncrblobChannel *)instanceData; + int nWrite = toWrite; /* Number of bytes to write */ + int nBlob; /* Total size of the blob */ + int rc; /* sqlite error code */ + + nBlob = wx_sqlite3_blob_bytes(p->pBlob); + if( (p->iSeek+nWrite)>nBlob ){ + *errorCodePtr = EINVAL; + return -1; + } + if( nWrite<=0 ){ + return 0; + } + + rc = wx_sqlite3_blob_write(p->pBlob, (void *)buf, nWrite, p->iSeek); + if( rc!=SQLITE_OK ){ + *errorCodePtr = EIO; + return -1; + } + + p->iSeek += nWrite; + return nWrite; +} + +/* +** Seek an incremental blob channel. +*/ +static int SQLITE_TCLAPI incrblobSeek( + ClientData instanceData, + long offset, + int seekMode, + int *errorCodePtr +){ + IncrblobChannel *p = (IncrblobChannel *)instanceData; + + switch( seekMode ){ + case SEEK_SET: + p->iSeek = offset; + break; + case SEEK_CUR: + p->iSeek += offset; + break; + case SEEK_END: + p->iSeek = wx_sqlite3_blob_bytes(p->pBlob) + offset; + break; + + default: assert(!"Bad seekMode"); + } + + return p->iSeek; +} + + +static void SQLITE_TCLAPI incrblobWatch( + ClientData instanceData, + int mode +){ + /* NO-OP */ +} +static int SQLITE_TCLAPI incrblobHandle( + ClientData instanceData, + int dir, + ClientData *hPtr +){ + return TCL_ERROR; +} + +static Tcl_ChannelType IncrblobChannelType = { + "incrblob", /* typeName */ + TCL_CHANNEL_VERSION_2, /* version */ + incrblobClose, /* closeProc */ + incrblobInput, /* inputProc */ + incrblobOutput, /* outputProc */ + incrblobSeek, /* seekProc */ + 0, /* setOptionProc */ + 0, /* getOptionProc */ + incrblobWatch, /* watchProc (this is a no-op) */ + incrblobHandle, /* getHandleProc (always returns error) */ + 0, /* close2Proc */ + 0, /* blockModeProc */ + 0, /* flushProc */ + 0, /* handlerProc */ + 0, /* wideSeekProc */ +}; + +/* +** Create a new incrblob channel. +*/ +static int createIncrblobChannel( + Tcl_Interp *interp, + SqliteDb *pDb, + const char *zDb, + const char *zTable, + const char *zColumn, + sqlite_int64 iRow, + int isReadonly +){ + IncrblobChannel *p; + wx_sqlite3 *db = pDb->db; + wx_sqlite3_blob *pBlob; + int rc; + int flags = TCL_READABLE|(isReadonly ? 0 : TCL_WRITABLE); + + /* This variable is used to name the channels: "incrblob_[incr count]" */ + static int count = 0; + char zChannel[64]; + + rc = wx_sqlite3_blob_open(db, zDb, zTable, zColumn, iRow, !isReadonly, &pBlob); + if( rc!=SQLITE_OK ){ + Tcl_SetResult(interp, (char *)wx_sqlite3_errmsg(pDb->db), TCL_VOLATILE); + return TCL_ERROR; + } + + p = (IncrblobChannel *)Tcl_Alloc(sizeof(IncrblobChannel)); + p->iSeek = 0; + p->pBlob = pBlob; + + wx_sqlite3_snprintf(sizeof(zChannel), zChannel, "incrblob_%d", ++count); + p->channel = Tcl_CreateChannel(&IncrblobChannelType, zChannel, p, flags); + Tcl_RegisterChannel(interp, p->channel); + + /* Link the new channel into the SqliteDb.pIncrblob list. */ + p->pNext = pDb->pIncrblob; + p->pPrev = 0; + if( p->pNext ){ + p->pNext->pPrev = p; + } + pDb->pIncrblob = p; + p->pDb = pDb; + + Tcl_SetResult(interp, (char *)Tcl_GetChannelName(p->channel), TCL_VOLATILE); + return TCL_OK; +} +#else /* else clause for "#ifndef SQLITE_OMIT_INCRBLOB" */ + #define closeIncrblobChannels(pDb) +#endif + +/* +** Look at the script prefix in pCmd. We will be executing this script +** after first appending one or more arguments. This routine analyzes +** the script to see if it is safe to use Tcl_EvalObjv() on the script +** rather than the more general Tcl_EvalEx(). Tcl_EvalObjv() is much +** faster. +** +** Scripts that are safe to use with Tcl_EvalObjv() consists of a +** command name followed by zero or more arguments with no [...] or $ +** or {...} or ; to be seen anywhere. Most callback scripts consist +** of just a single procedure name and they meet this requirement. +*/ +static int safeToUseEvalObjv(Tcl_Interp *interp, Tcl_Obj *pCmd){ + /* We could try to do something with Tcl_Parse(). But we will instead + ** just do a search for forbidden characters. If any of the forbidden + ** characters appear in pCmd, we will report the string as unsafe. + */ + const char *z; + int n; + z = Tcl_GetStringFromObj(pCmd, &n); + while( n-- > 0 ){ + int c = *(z++); + if( c=='$' || c=='[' || c==';' ) return 0; + } + return 1; +} + +/* +** Find an SqlFunc structure with the given name. Or create a new +** one if an existing one cannot be found. Return a pointer to the +** structure. +*/ +static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){ + SqlFunc *p, *pNew; + int nName = strlen30(zName); + pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + nName + 1 ); + pNew->zName = (char*)&pNew[1]; + memcpy(pNew->zName, zName, nName+1); + for(p=pDb->pFunc; p; p=p->pNext){ + if( wx_sqlite3_stricmp(p->zName, pNew->zName)==0 ){ + Tcl_Free((char*)pNew); + return p; + } + } + pNew->interp = pDb->interp; + pNew->pDb = pDb; + pNew->pScript = 0; + pNew->pNext = pDb->pFunc; + pDb->pFunc = pNew; + return pNew; +} + +/* +** Free a single SqlPreparedStmt object. +*/ +static void dbFreeStmt(SqlPreparedStmt *pStmt){ +#ifdef SQLITE_TEST + if( wx_sqlite3_sql(pStmt->pStmt)==0 ){ + Tcl_Free((char *)pStmt->zSql); + } +#endif + wx_sqlite3_finalize(pStmt->pStmt); + Tcl_Free((char *)pStmt); +} + +/* +** Finalize and free a list of prepared statements +*/ +static void flushStmtCache(SqliteDb *pDb){ + SqlPreparedStmt *pPreStmt; + SqlPreparedStmt *pNext; + + for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pNext){ + pNext = pPreStmt->pNext; + dbFreeStmt(pPreStmt); + } + pDb->nStmt = 0; + pDb->stmtLast = 0; + pDb->stmtList = 0; +} + +/* +** Increment the reference counter on the SqliteDb object. The reference +** should be released by calling delDatabaseRef(). +*/ +static void addDatabaseRef(SqliteDb *pDb){ + pDb->nRef++; +} + +/* +** Decrement the reference counter associated with the SqliteDb object. +** If it reaches zero, delete the object. +*/ +static void delDatabaseRef(SqliteDb *pDb){ + assert( pDb->nRef>0 ); + pDb->nRef--; + if( pDb->nRef==0 ){ + flushStmtCache(pDb); + closeIncrblobChannels(pDb); + wx_sqlite3_close(pDb->db); + while( pDb->pFunc ){ + SqlFunc *pFunc = pDb->pFunc; + pDb->pFunc = pFunc->pNext; + assert( pFunc->pDb==pDb ); + Tcl_DecrRefCount(pFunc->pScript); + Tcl_Free((char*)pFunc); + } + while( pDb->pCollate ){ + SqlCollate *pCollate = pDb->pCollate; + pDb->pCollate = pCollate->pNext; + Tcl_Free((char*)pCollate); + } + if( pDb->zBusy ){ + Tcl_Free(pDb->zBusy); + } + if( pDb->zTrace ){ + Tcl_Free(pDb->zTrace); + } + if( pDb->zTraceV2 ){ + Tcl_Free(pDb->zTraceV2); + } + if( pDb->zProfile ){ + Tcl_Free(pDb->zProfile); + } + if( pDb->zBindFallback ){ + Tcl_Free(pDb->zBindFallback); + } + if( pDb->zAuth ){ + Tcl_Free(pDb->zAuth); + } + if( pDb->zNull ){ + Tcl_Free(pDb->zNull); + } + if( pDb->pUpdateHook ){ + Tcl_DecrRefCount(pDb->pUpdateHook); + } + if( pDb->pPreUpdateHook ){ + Tcl_DecrRefCount(pDb->pPreUpdateHook); + } + if( pDb->pRollbackHook ){ + Tcl_DecrRefCount(pDb->pRollbackHook); + } + if( pDb->pWalHook ){ + Tcl_DecrRefCount(pDb->pWalHook); + } + if( pDb->pCollateNeeded ){ + Tcl_DecrRefCount(pDb->pCollateNeeded); + } + Tcl_Free((char*)pDb); + } +} + +/* +** TCL calls this procedure when an wx_sqlite3 database command is +** deleted. +*/ +static void SQLITE_TCLAPI DbDeleteCmd(void *db){ + SqliteDb *pDb = (SqliteDb*)db; + delDatabaseRef(pDb); +} + +/* +** This routine is called when a database file is locked while trying +** to execute SQL. +*/ +static int DbBusyHandler(void *cd, int nTries){ + SqliteDb *pDb = (SqliteDb*)cd; + int rc; + char zVal[30]; + + wx_sqlite3_snprintf(sizeof(zVal), zVal, "%d", nTries); + rc = Tcl_VarEval(pDb->interp, pDb->zBusy, " ", zVal, (char*)0); + if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){ + return 0; + } + return 1; +} + +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK +/* +** This routine is invoked as the 'progress callback' for the database. +*/ +static int DbProgressHandler(void *cd){ + SqliteDb *pDb = (SqliteDb*)cd; + int rc; + + assert( pDb->zProgress ); + rc = Tcl_Eval(pDb->interp, pDb->zProgress); + if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){ + return 1; + } + return 0; +} +#endif + +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \ + !defined(SQLITE_OMIT_DEPRECATED) +/* +** This routine is called by the SQLite trace handler whenever a new +** block of SQL is executed. The TCL script in pDb->zTrace is executed. +*/ +static void DbTraceHandler(void *cd, const char *zSql){ + SqliteDb *pDb = (SqliteDb*)cd; + Tcl_DString str; + + Tcl_DStringInit(&str); + Tcl_DStringAppend(&str, pDb->zTrace, -1); + Tcl_DStringAppendElement(&str, zSql); + Tcl_Eval(pDb->interp, Tcl_DStringValue(&str)); + Tcl_DStringFree(&str); + Tcl_ResetResult(pDb->interp); +} +#endif + +#ifndef SQLITE_OMIT_TRACE +/* +** This routine is called by the SQLite trace_v2 handler whenever a new +** supported event is generated. Unsupported event types are ignored. +** The TCL script in pDb->zTraceV2 is executed, with the arguments for +** the event appended to it (as list elements). +*/ +static int DbTraceV2Handler( + unsigned type, /* One of the SQLITE_TRACE_* event types. */ + void *cd, /* The original context data pointer. */ + void *pd, /* Primary event data, depends on event type. */ + void *xd /* Extra event data, depends on event type. */ +){ + SqliteDb *pDb = (SqliteDb*)cd; + Tcl_Obj *pCmd; + + switch( type ){ + case SQLITE_TRACE_STMT: { + wx_sqlite3_stmt *pStmt = (wx_sqlite3_stmt *)pd; + char *zSql = (char *)xd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)pStmt)); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewStringObj(zSql, -1)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + case SQLITE_TRACE_PROFILE: { + wx_sqlite3_stmt *pStmt = (wx_sqlite3_stmt *)pd; + wx_sqlite3_int64 ns = *(wx_sqlite3_int64*)xd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)pStmt)); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)ns)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + case SQLITE_TRACE_ROW: { + wx_sqlite3_stmt *pStmt = (wx_sqlite3_stmt *)pd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)pStmt)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + case SQLITE_TRACE_CLOSE: { + wx_sqlite3 *db = (wx_sqlite3 *)pd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)db)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + } + return SQLITE_OK; +} +#endif + +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \ + !defined(SQLITE_OMIT_DEPRECATED) +/* +** This routine is called by the SQLite profile handler after a statement +** SQL has executed. The TCL script in pDb->zProfile is evaluated. +*/ +static void DbProfileHandler(void *cd, const char *zSql, sqlite_uint64 tm){ + SqliteDb *pDb = (SqliteDb*)cd; + Tcl_DString str; + char zTm[100]; + + wx_sqlite3_snprintf(sizeof(zTm)-1, zTm, "%lld", tm); + Tcl_DStringInit(&str); + Tcl_DStringAppend(&str, pDb->zProfile, -1); + Tcl_DStringAppendElement(&str, zSql); + Tcl_DStringAppendElement(&str, zTm); + Tcl_Eval(pDb->interp, Tcl_DStringValue(&str)); + Tcl_DStringFree(&str); + Tcl_ResetResult(pDb->interp); +} +#endif + +/* +** This routine is called when a transaction is committed. The +** TCL script in pDb->zCommit is executed. If it returns non-zero or +** if it throws an exception, the transaction is rolled back instead +** of being committed. +*/ +static int DbCommitHandler(void *cd){ + SqliteDb *pDb = (SqliteDb*)cd; + int rc; + + rc = Tcl_Eval(pDb->interp, pDb->zCommit); + if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){ + return 1; + } + return 0; +} + +static void DbRollbackHandler(void *clientData){ + SqliteDb *pDb = (SqliteDb*)clientData; + assert(pDb->pRollbackHook); + if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){ + Tcl_BackgroundError(pDb->interp); + } +} + +/* +** This procedure handles wal_hook callbacks. +*/ +static int DbWalHandler( + void *clientData, + wx_sqlite3 *db, + const char *zDb, + int nEntry +){ + int ret = SQLITE_OK; + Tcl_Obj *p; + SqliteDb *pDb = (SqliteDb*)clientData; + Tcl_Interp *interp = pDb->interp; + assert(pDb->pWalHook); + + assert( db==pDb->db ); + p = Tcl_DuplicateObj(pDb->pWalHook); + Tcl_IncrRefCount(p); + Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1)); + Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry)); + if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0) + || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret) + ){ + Tcl_BackgroundError(interp); + } + Tcl_DecrRefCount(p); + + return ret; +} + +#if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) +static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){ + char zBuf[64]; + wx_sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", iArg); + Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY); + wx_sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", nArg); + Tcl_SetVar(interp, "sqlite_unlock_notify_argcount", zBuf, TCL_GLOBAL_ONLY); +} +#else +# define setTestUnlockNotifyVars(x,y,z) +#endif + +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY +static void DbUnlockNotify(void **apArg, int nArg){ + int i; + for(i=0; iinterp, i, nArg); + assert( pDb->pUnlockNotify); + Tcl_EvalObjEx(pDb->interp, pDb->pUnlockNotify, flags); + Tcl_DecrRefCount(pDb->pUnlockNotify); + pDb->pUnlockNotify = 0; + } +} +#endif + +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +/* +** Pre-update hook callback. +*/ +static void DbPreUpdateHandler( + void *p, + wx_sqlite3 *db, + int op, + const char *zDb, + const char *zTbl, + sqlite_int64 iKey1, + sqlite_int64 iKey2 +){ + SqliteDb *pDb = (SqliteDb *)p; + Tcl_Obj *pCmd; + static const char *azStr[] = {"DELETE", "INSERT", "UPDATE"}; + + assert( (SQLITE_DELETE-1)/9 == 0 ); + assert( (SQLITE_INSERT-1)/9 == 1 ); + assert( (SQLITE_UPDATE-1)/9 == 2 ); + assert( pDb->pPreUpdateHook ); + assert( db==pDb->db ); + assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE ); + + pCmd = Tcl_DuplicateObj(pDb->pPreUpdateHook); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(azStr[(op-1)/9], -1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(iKey1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(iKey2)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); +} +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + +static void DbUpdateHandler( + void *p, + int op, + const char *zDb, + const char *zTbl, + sqlite_int64 rowid +){ + SqliteDb *pDb = (SqliteDb *)p; + Tcl_Obj *pCmd; + static const char *azStr[] = {"DELETE", "INSERT", "UPDATE"}; + + assert( (SQLITE_DELETE-1)/9 == 0 ); + assert( (SQLITE_INSERT-1)/9 == 1 ); + assert( (SQLITE_UPDATE-1)/9 == 2 ); + + assert( pDb->pUpdateHook ); + assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE ); + + pCmd = Tcl_DuplicateObj(pDb->pUpdateHook); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(azStr[(op-1)/9], -1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1)); + Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(rowid)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); +} + +static void tclCollateNeeded( + void *pCtx, + wx_sqlite3 *db, + int enc, + const char *zName +){ + SqliteDb *pDb = (SqliteDb *)pCtx; + Tcl_Obj *pScript = Tcl_DuplicateObj(pDb->pCollateNeeded); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(zName, -1)); + Tcl_EvalObjEx(pDb->interp, pScript, 0); + Tcl_DecrRefCount(pScript); +} + +/* +** This routine is called to evaluate an SQL collation function implemented +** using TCL script. +*/ +static int tclSqlCollate( + void *pCtx, + int nA, + const void *zA, + int nB, + const void *zB +){ + SqlCollate *p = (SqlCollate *)pCtx; + Tcl_Obj *pCmd; + + pCmd = Tcl_NewStringObj(p->zScript, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zA, nA)); + Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zB, nB)); + Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + return (atoi(Tcl_GetStringResult(p->interp))); +} + +/* +** This routine is called to evaluate an SQL function implemented +** using TCL script. +*/ +static void tclSqlFunc(wx_sqlite3_context *context, int argc, wx_sqlite3_value**argv){ + SqlFunc *p = wx_sqlite3_user_data(context); + Tcl_Obj *pCmd; + int i; + int rc; + + if( argc==0 ){ + /* If there are no arguments to the function, call Tcl_EvalObjEx on the + ** script object directly. This allows the TCL compiler to generate + ** bytecode for the command on the first invocation and thus make + ** subsequent invocations much faster. */ + pCmd = p->pScript; + Tcl_IncrRefCount(pCmd); + rc = Tcl_EvalObjEx(p->interp, pCmd, 0); + Tcl_DecrRefCount(pCmd); + }else{ + /* If there are arguments to the function, make a shallow copy of the + ** script object, lappend the arguments, then evaluate the copy. + ** + ** By "shallow" copy, we mean only the outer list Tcl_Obj is duplicated. + ** The new Tcl_Obj contains pointers to the original list elements. + ** That way, when Tcl_EvalObjv() is run and shimmers the first element + ** of the list to tclCmdNameType, that alternate representation will + ** be preserved and reused on the next invocation. + */ + Tcl_Obj **aArg; + int nArg; + if( Tcl_ListObjGetElements(p->interp, p->pScript, &nArg, &aArg) ){ + wx_sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + return; + } + pCmd = Tcl_NewListObj(nArg, aArg); + Tcl_IncrRefCount(pCmd); + for(i=0; i=-2147483647 && v<=2147483647 ){ + pVal = Tcl_NewIntObj((int)v); + }else{ + pVal = Tcl_NewWideIntObj(v); + } + break; + } + case SQLITE_FLOAT: { + double r = wx_sqlite3_value_double(pIn); + pVal = Tcl_NewDoubleObj(r); + break; + } + case SQLITE_NULL: { + pVal = Tcl_NewStringObj(p->pDb->zNull, -1); + break; + } + default: { + int bytes = wx_sqlite3_value_bytes(pIn); + pVal = Tcl_NewStringObj((char *)wx_sqlite3_value_text(pIn), bytes); + break; + } + } + rc = Tcl_ListObjAppendElement(p->interp, pCmd, pVal); + if( rc ){ + Tcl_DecrRefCount(pCmd); + wx_sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + return; + } + } + if( !p->useEvalObjv ){ + /* Tcl_EvalObjEx() will automatically call Tcl_EvalObjv() if pCmd + ** is a list without a string representation. To prevent this from + ** happening, make sure pCmd has a valid string representation */ + Tcl_GetString(pCmd); + } + rc = Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + } + + if( rc && rc!=TCL_RETURN ){ + wx_sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + }else{ + Tcl_Obj *pVar = Tcl_GetObjResult(p->interp); + int n; + u8 *data; + const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); + char c = zType[0]; + int eType = p->eType; + + if( eType==SQLITE_NULL ){ + if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){ + /* Only return a BLOB type if the Tcl variable is a bytearray and + ** has no string representation. */ + eType = SQLITE_BLOB; + }else if( (c=='b' && strcmp(zType,"boolean")==0) + || (c=='w' && strcmp(zType,"wideInt")==0) + || (c=='i' && strcmp(zType,"int")==0) + ){ + eType = SQLITE_INTEGER; + }else if( c=='d' && strcmp(zType,"double")==0 ){ + eType = SQLITE_FLOAT; + }else{ + eType = SQLITE_TEXT; + } + } + + switch( eType ){ + case SQLITE_BLOB: { + data = Tcl_GetByteArrayFromObj(pVar, &n); + wx_sqlite3_result_blob(context, data, n, SQLITE_TRANSIENT); + break; + } + case SQLITE_INTEGER: { + Tcl_WideInt v; + if( TCL_OK==Tcl_GetWideIntFromObj(0, pVar, &v) ){ + wx_sqlite3_result_int64(context, v); + break; + } + /* fall-through */ + } + case SQLITE_FLOAT: { + double r; + if( TCL_OK==Tcl_GetDoubleFromObj(0, pVar, &r) ){ + wx_sqlite3_result_double(context, r); + break; + } + /* fall-through */ + } + default: { + data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); + wx_sqlite3_result_text(context, (char *)data, n, SQLITE_TRANSIENT); + break; + } + } + } - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_create_function(db, "wx_sqlite3mc_config", 3, SQLITE_UTF8 | SQLITE_DETERMINISTIC, - codecParameterTable, wx_sqlite3mcConfigParams, 0, 0); +} + +#ifndef SQLITE_OMIT_AUTHORIZATION +/* +** This is the authentication function. It appends the authentication +** type code and the two arguments to zCmd[] then invokes the result +** on the interpreter. The reply is examined to determine if the +** authentication fails or succeeds. +*/ +static int auth_callback( + void *pArg, + int code, + const char *zArg1, + const char *zArg2, + const char *zArg3, + const char *zArg4 +#ifdef SQLITE_USER_AUTHENTICATION + ,const char *zArg5 +#endif +){ + const char *zCode; + Tcl_DString str; + int rc; + const char *zReply; + /* EVIDENCE-OF: R-38590-62769 The first parameter to the authorizer + ** callback is a copy of the third parameter to the + ** wx_sqlite3_set_authorizer() interface. + */ + SqliteDb *pDb = (SqliteDb*)pArg; + if( pDb->disableAuth ) return SQLITE_OK; + + /* EVIDENCE-OF: R-56518-44310 The second parameter to the callback is an + ** integer action code that specifies the particular action to be + ** authorized. */ + switch( code ){ + case SQLITE_COPY : zCode="SQLITE_COPY"; break; + case SQLITE_CREATE_INDEX : zCode="SQLITE_CREATE_INDEX"; break; + case SQLITE_CREATE_TABLE : zCode="SQLITE_CREATE_TABLE"; break; + case SQLITE_CREATE_TEMP_INDEX : zCode="SQLITE_CREATE_TEMP_INDEX"; break; + case SQLITE_CREATE_TEMP_TABLE : zCode="SQLITE_CREATE_TEMP_TABLE"; break; + case SQLITE_CREATE_TEMP_TRIGGER: zCode="SQLITE_CREATE_TEMP_TRIGGER"; break; + case SQLITE_CREATE_TEMP_VIEW : zCode="SQLITE_CREATE_TEMP_VIEW"; break; + case SQLITE_CREATE_TRIGGER : zCode="SQLITE_CREATE_TRIGGER"; break; + case SQLITE_CREATE_VIEW : zCode="SQLITE_CREATE_VIEW"; break; + case SQLITE_DELETE : zCode="SQLITE_DELETE"; break; + case SQLITE_DROP_INDEX : zCode="SQLITE_DROP_INDEX"; break; + case SQLITE_DROP_TABLE : zCode="SQLITE_DROP_TABLE"; break; + case SQLITE_DROP_TEMP_INDEX : zCode="SQLITE_DROP_TEMP_INDEX"; break; + case SQLITE_DROP_TEMP_TABLE : zCode="SQLITE_DROP_TEMP_TABLE"; break; + case SQLITE_DROP_TEMP_TRIGGER : zCode="SQLITE_DROP_TEMP_TRIGGER"; break; + case SQLITE_DROP_TEMP_VIEW : zCode="SQLITE_DROP_TEMP_VIEW"; break; + case SQLITE_DROP_TRIGGER : zCode="SQLITE_DROP_TRIGGER"; break; + case SQLITE_DROP_VIEW : zCode="SQLITE_DROP_VIEW"; break; + case SQLITE_INSERT : zCode="SQLITE_INSERT"; break; + case SQLITE_PRAGMA : zCode="SQLITE_PRAGMA"; break; + case SQLITE_READ : zCode="SQLITE_READ"; break; + case SQLITE_SELECT : zCode="SQLITE_SELECT"; break; + case SQLITE_TRANSACTION : zCode="SQLITE_TRANSACTION"; break; + case SQLITE_UPDATE : zCode="SQLITE_UPDATE"; break; + case SQLITE_ATTACH : zCode="SQLITE_ATTACH"; break; + case SQLITE_DETACH : zCode="SQLITE_DETACH"; break; + case SQLITE_ALTER_TABLE : zCode="SQLITE_ALTER_TABLE"; break; + case SQLITE_REINDEX : zCode="SQLITE_REINDEX"; break; + case SQLITE_ANALYZE : zCode="SQLITE_ANALYZE"; break; + case SQLITE_CREATE_VTABLE : zCode="SQLITE_CREATE_VTABLE"; break; + case SQLITE_DROP_VTABLE : zCode="SQLITE_DROP_VTABLE"; break; + case SQLITE_FUNCTION : zCode="SQLITE_FUNCTION"; break; + case SQLITE_SAVEPOINT : zCode="SQLITE_SAVEPOINT"; break; + case SQLITE_RECURSIVE : zCode="SQLITE_RECURSIVE"; break; + default : zCode="????"; break; + } + Tcl_DStringInit(&str); + Tcl_DStringAppend(&str, pDb->zAuth, -1); + Tcl_DStringAppendElement(&str, zCode); + Tcl_DStringAppendElement(&str, zArg1 ? zArg1 : ""); + Tcl_DStringAppendElement(&str, zArg2 ? zArg2 : ""); + Tcl_DStringAppendElement(&str, zArg3 ? zArg3 : ""); + Tcl_DStringAppendElement(&str, zArg4 ? zArg4 : ""); +#ifdef SQLITE_USER_AUTHENTICATION + Tcl_DStringAppendElement(&str, zArg5 ? zArg5 : ""); +#endif + rc = Tcl_GlobalEval(pDb->interp, Tcl_DStringValue(&str)); + Tcl_DStringFree(&str); + zReply = rc==TCL_OK ? Tcl_GetStringResult(pDb->interp) : "SQLITE_DENY"; + if( strcmp(zReply,"SQLITE_OK")==0 ){ + rc = SQLITE_OK; + }else if( strcmp(zReply,"SQLITE_DENY")==0 ){ + rc = SQLITE_DENY; + }else if( strcmp(zReply,"SQLITE_IGNORE")==0 ){ + rc = SQLITE_IGNORE; + }else{ + rc = 999; } - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_create_function(db, "wx_sqlite3mc_codec_data", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, - NULL, wx_sqlite3mcCodecDataSql, 0, 0); + return rc; +} +#endif /* SQLITE_OMIT_AUTHORIZATION */ + +/* +** 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. +** +** The interface is like "readline" but no command-line editing +** is done. +** +** copied from shell.c from '.import' command +*/ +static char *local_getline(char *zPrompt, FILE *in){ + char *zLine; + int nLine; + int n; + + nLine = 100; + zLine = malloc( nLine ); + if( zLine==0 ) return 0; + n = 0; + while( 1 ){ + if( n+100>nLine ){ + nLine = nLine*2 + 100; + zLine = realloc(zLine, nLine); + if( zLine==0 ) return 0; + } + 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--; + zLine[n] = 0; + break; + } } - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_create_function(db, "wx_sqlite3mc_codec_data", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, - NULL, wx_sqlite3mcCodecDataSql, 0, 0); + zLine = realloc( zLine, n+1 ); + return zLine; +} + + +/* +** This function is part of the implementation of the command: +** +** $db transaction [-deferred|-immediate|-exclusive] SCRIPT +** +** It is invoked after evaluating the script SCRIPT to commit or rollback +** the transaction or savepoint opened by the [transaction] command. +*/ +static int SQLITE_TCLAPI DbTransPostCmd( + ClientData data[], /* data[0] is the Sqlite3Db* for $db */ + Tcl_Interp *interp, /* Tcl interpreter */ + int result /* Result of evaluating SCRIPT */ +){ + static const char *const azEnd[] = { + "RELEASE _tcl_transaction", /* rc==TCL_ERROR, nTransaction!=0 */ + "COMMIT", /* rc!=TCL_ERROR, nTransaction==0 */ + "ROLLBACK TO _tcl_transaction ; RELEASE _tcl_transaction", + "ROLLBACK" /* rc==TCL_ERROR, nTransaction==0 */ + }; + SqliteDb *pDb = (SqliteDb*)data[0]; + int rc = result; + const char *zEnd; + + pDb->nTransaction--; + zEnd = azEnd[(rc==TCL_ERROR)*2 + (pDb->nTransaction==0)]; + + pDb->disableAuth++; + if( wx_sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){ + /* This is a tricky scenario to handle. The most likely cause of an + ** error is that the exec() above was an attempt to commit the + ** top-level transaction that returned SQLITE_BUSY. Or, less likely, + ** that an IO-error has occurred. In either case, throw a Tcl exception + ** and try to rollback the transaction. + ** + ** But it could also be that the user executed one or more BEGIN, + ** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing + ** this method's logic. Not clear how this would be best handled. + */ + if( rc!=TCL_ERROR ){ + Tcl_AppendResult(interp, wx_sqlite3_errmsg(pDb->db), (char*)0); + rc = TCL_ERROR; + } + wx_sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0); } - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_create_function(db, "wx_sqlite3mc_version", 0, SQLITE_UTF8 | SQLITE_DETERMINISTIC, - NULL, wx_sqlite3mcVersion, 0, 0); + pDb->disableAuth--; + + delDatabaseRef(pDb); + return rc; +} + +/* +** Unless SQLITE_TEST is defined, this function is a simple wrapper around +** wx_sqlite3_prepare_v2(). If SQLITE_TEST is defined, then it uses either +** wx_sqlite3_prepare_v2() or legacy interface wx_sqlite3_prepare(), depending +** on whether or not the [db_use_legacy_prepare] command has been used to +** configure the connection. +*/ +static int dbPrepare( + SqliteDb *pDb, /* Database object */ + const char *zSql, /* SQL to compile */ + wx_sqlite3_stmt **ppStmt, /* OUT: Prepared statement */ + const char **pzOut /* OUT: Pointer to next SQL statement */ +){ + unsigned int prepFlags = 0; +#ifdef SQLITE_TEST + if( pDb->bLegacyPrepare ){ + return wx_sqlite3_prepare(pDb->db, zSql, -1, ppStmt, pzOut); + } +#endif + /* If the statement cache is large, use the SQLITE_PREPARE_PERSISTENT + ** flags, which uses less lookaside memory. But if the cache is small, + ** omit that flag to make full use of lookaside */ + if( pDb->maxStmt>5 ) prepFlags = SQLITE_PREPARE_PERSISTENT; + + return wx_sqlite3_prepare_v3(pDb->db, zSql, -1, prepFlags, ppStmt, pzOut); +} + +/* +** Search the cache for a prepared-statement object that implements the +** first SQL statement in the buffer pointed to by parameter zIn. If +** no such prepared-statement can be found, allocate and prepare a new +** one. In either case, bind the current values of the relevant Tcl +** variables to any $var, :var or @var variables in the statement. Before +** returning, set *ppPreStmt to point to the prepared-statement object. +** +** Output parameter *pzOut is set to point to the next SQL statement in +** buffer zIn, or to the '\0' byte at the end of zIn if there is no +** next statement. +** +** If successful, TCL_OK is returned. Otherwise, TCL_ERROR is returned +** and an error message loaded into interpreter pDb->interp. +*/ +static int dbPrepareAndBind( + SqliteDb *pDb, /* Database object */ + char const *zIn, /* SQL to compile */ + char const **pzOut, /* OUT: Pointer to next SQL statement */ + SqlPreparedStmt **ppPreStmt /* OUT: Object used to cache statement */ +){ + const char *zSql = zIn; /* Pointer to first SQL statement in zIn */ + wx_sqlite3_stmt *pStmt = 0; /* Prepared statement object */ + SqlPreparedStmt *pPreStmt; /* Pointer to cached statement */ + int nSql; /* Length of zSql in bytes */ + int nVar = 0; /* Number of variables in statement */ + int iParm = 0; /* Next free entry in apParm */ + char c; + int i; + int needResultReset = 0; /* Need to invoke Tcl_ResetResult() */ + int rc = SQLITE_OK; /* Value to return */ + Tcl_Interp *interp = pDb->interp; + + *ppPreStmt = 0; + + /* Trim spaces from the start of zSql and calculate the remaining length. */ + while( (c = zSql[0])==' ' || c=='\t' || c=='\r' || c=='\n' ){ zSql++; } + nSql = strlen30(zSql); + + for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){ + int n = pPreStmt->nSql; + if( nSql>=n + && memcmp(pPreStmt->zSql, zSql, n)==0 + && (zSql[n]==0 || zSql[n-1]==';') + ){ + pStmt = pPreStmt->pStmt; + *pzOut = &zSql[pPreStmt->nSql]; + + /* When a prepared statement is found, unlink it from the + ** cache list. It will later be added back to the beginning + ** of the cache list in order to implement LRU replacement. + */ + if( pPreStmt->pPrev ){ + pPreStmt->pPrev->pNext = pPreStmt->pNext; + }else{ + pDb->stmtList = pPreStmt->pNext; + } + if( pPreStmt->pNext ){ + pPreStmt->pNext->pPrev = pPreStmt->pPrev; + }else{ + pDb->stmtLast = pPreStmt->pPrev; + } + pDb->nStmt--; + nVar = wx_sqlite3_bind_parameter_count(pStmt); + break; + } + } + + /* If no prepared statement was found. Compile the SQL text. Also allocate + ** a new SqlPreparedStmt structure. */ + if( pPreStmt==0 ){ + int nByte; + + if( SQLITE_OK!=dbPrepare(pDb, zSql, &pStmt, pzOut) ){ + Tcl_SetObjResult(interp, Tcl_NewStringObj(wx_sqlite3_errmsg(pDb->db), -1)); + return TCL_ERROR; + } + if( pStmt==0 ){ + if( SQLITE_OK!=wx_sqlite3_errcode(pDb->db) ){ + /* A compile-time error in the statement. */ + Tcl_SetObjResult(interp, Tcl_NewStringObj(wx_sqlite3_errmsg(pDb->db), -1)); + return TCL_ERROR; + }else{ + /* The statement was a no-op. Continue to the next statement + ** in the SQL string. + */ + return TCL_OK; + } + } + + assert( pPreStmt==0 ); + nVar = wx_sqlite3_bind_parameter_count(pStmt); + nByte = sizeof(SqlPreparedStmt) + nVar*sizeof(Tcl_Obj *); + pPreStmt = (SqlPreparedStmt*)Tcl_Alloc(nByte); + memset(pPreStmt, 0, nByte); + + pPreStmt->pStmt = pStmt; + pPreStmt->nSql = (int)(*pzOut - zSql); + pPreStmt->zSql = wx_sqlite3_sql(pStmt); + pPreStmt->apParm = (Tcl_Obj **)&pPreStmt[1]; +#ifdef SQLITE_TEST + if( pPreStmt->zSql==0 ){ + char *zCopy = Tcl_Alloc(pPreStmt->nSql + 1); + memcpy(zCopy, zSql, pPreStmt->nSql); + zCopy[pPreStmt->nSql] = '\0'; + pPreStmt->zSql = zCopy; + } +#endif + } + assert( pPreStmt ); + assert( strlen30(pPreStmt->zSql)==pPreStmt->nSql ); + assert( 0==memcmp(pPreStmt->zSql, zSql, pPreStmt->nSql) ); + + /* Bind values to parameters that begin with $ or : */ + for(i=1; i<=nVar; i++){ + const char *zVar = wx_sqlite3_bind_parameter_name(pStmt, i); + if( zVar!=0 && (zVar[0]=='$' || zVar[0]==':' || zVar[0]=='@') ){ + Tcl_Obj *pVar = Tcl_GetVar2Ex(interp, &zVar[1], 0, 0); + if( pVar==0 && pDb->zBindFallback!=0 ){ + Tcl_Obj *pCmd; + int rx; + pCmd = Tcl_NewStringObj(pDb->zBindFallback, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(interp, pCmd, Tcl_NewStringObj(zVar,-1)); + if( needResultReset ) Tcl_ResetResult(interp); + needResultReset = 1; + rx = Tcl_EvalObjEx(interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + if( rx==TCL_OK ){ + pVar = Tcl_GetObjResult(interp); + }else if( rx==TCL_ERROR ){ + rc = TCL_ERROR; + break; + }else{ + pVar = 0; + } + } + if( pVar ){ + int n; + u8 *data; + const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); + c = zType[0]; + if( zVar[0]=='@' || + (c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0) ){ + /* Load a BLOB type if the Tcl variable is a bytearray and + ** it has no string representation or the host + ** parameter name begins with "@". */ + data = Tcl_GetByteArrayFromObj(pVar, &n); + wx_sqlite3_bind_blob(pStmt, i, data, n, SQLITE_STATIC); + Tcl_IncrRefCount(pVar); + pPreStmt->apParm[iParm++] = pVar; + }else if( c=='b' && strcmp(zType,"boolean")==0 ){ + Tcl_GetIntFromObj(interp, pVar, &n); + wx_sqlite3_bind_int(pStmt, i, n); + }else if( c=='d' && strcmp(zType,"double")==0 ){ + double r; + Tcl_GetDoubleFromObj(interp, pVar, &r); + wx_sqlite3_bind_double(pStmt, i, r); + }else if( (c=='w' && strcmp(zType,"wideInt")==0) || + (c=='i' && strcmp(zType,"int")==0) ){ + Tcl_WideInt v; + Tcl_GetWideIntFromObj(interp, pVar, &v); + wx_sqlite3_bind_int64(pStmt, i, v); + }else{ + data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); + wx_sqlite3_bind_text(pStmt, i, (char *)data, n, SQLITE_STATIC); + Tcl_IncrRefCount(pVar); + pPreStmt->apParm[iParm++] = pVar; + } + }else{ + wx_sqlite3_bind_null(pStmt, i); + } + if( needResultReset ) Tcl_ResetResult(pDb->interp); + } } + pPreStmt->nParm = iParm; + *ppPreStmt = pPreStmt; + if( needResultReset && rc==TCL_OK ) Tcl_ResetResult(pDb->interp); + return rc; } -#ifdef SQLITE_ENABLE_EXTFUNC -static int -wx_sqlite3_extfunc_init(wx_sqlite3 *db, char **pzErrMsg, const wx_sqlite3_api_routines *pApi) -{ - return RegisterExtensionFunctions(db); +/* +** Release a statement reference obtained by calling dbPrepareAndBind(). +** There should be exactly one call to this function for each call to +** dbPrepareAndBind(). +** +** If the discard parameter is non-zero, then the statement is deleted +** immediately. Otherwise it is added to the LRU list and may be returned +** by a subsequent call to dbPrepareAndBind(). +*/ +static void dbReleaseStmt( + SqliteDb *pDb, /* Database handle */ + SqlPreparedStmt *pPreStmt, /* Prepared statement handle to release */ + int discard /* True to delete (not cache) the pPreStmt */ +){ + int i; + + /* Free the bound string and blob parameters */ + for(i=0; inParm; i++){ + Tcl_DecrRefCount(pPreStmt->apParm[i]); + } + pPreStmt->nParm = 0; + + if( pDb->maxStmt<=0 || discard ){ + /* If the cache is turned off, deallocated the statement */ + dbFreeStmt(pPreStmt); + }else{ + /* Add the prepared statement to the beginning of the cache list. */ + pPreStmt->pNext = pDb->stmtList; + pPreStmt->pPrev = 0; + if( pDb->stmtList ){ + pDb->stmtList->pPrev = pPreStmt; + } + pDb->stmtList = pPreStmt; + if( pDb->stmtLast==0 ){ + assert( pDb->nStmt==0 ); + pDb->stmtLast = pPreStmt; + }else{ + assert( pDb->nStmt>0 ); + } + pDb->nStmt++; + + /* If we have too many statement in cache, remove the surplus from + ** the end of the cache list. */ + while( pDb->nStmt>pDb->maxStmt ){ + SqlPreparedStmt *pLast = pDb->stmtLast; + pDb->stmtLast = pLast->pPrev; + pDb->stmtLast->pNext = 0; + pDb->nStmt--; + dbFreeStmt(pLast); + } + } +} + +/* +** Structure used with dbEvalXXX() functions: +** +** dbEvalInit() +** dbEvalStep() +** dbEvalFinalize() +** dbEvalRowInfo() +** dbEvalColumnValue() +*/ +typedef struct DbEvalContext DbEvalContext; +struct DbEvalContext { + SqliteDb *pDb; /* Database handle */ + Tcl_Obj *pSql; /* Object holding string zSql */ + const char *zSql; /* Remaining SQL to execute */ + SqlPreparedStmt *pPreStmt; /* Current statement */ + int nCol; /* Number of columns returned by pStmt */ + int evalFlags; /* Flags used */ + Tcl_Obj *pArray; /* Name of array variable */ + Tcl_Obj **apColName; /* Array of column names */ +}; + +#define SQLITE_EVAL_WITHOUTNULLS 0x00001 /* Unset array(*) for NULL */ + +/* +** Release any cache of column names currently held as part of +** the DbEvalContext structure passed as the first argument. +*/ +static void dbReleaseColumnNames(DbEvalContext *p){ + if( p->apColName ){ + int i; + for(i=0; inCol; i++){ + Tcl_DecrRefCount(p->apColName[i]); + } + Tcl_Free((char *)p->apColName); + p->apColName = 0; + } + p->nCol = 0; +} + +/* +** Initialize a DbEvalContext structure. +** +** If pArray is not NULL, then it contains the name of a Tcl array +** variable. The "*" member of this array is set to a list containing +** the names of the columns returned by the statement as part of each +** call to dbEvalStep(), in order from left to right. e.g. if the names +** of the returned columns are a, b and c, it does the equivalent of the +** tcl command: +** +** set ${pArray}(*) {a b c} +*/ +static void dbEvalInit( + DbEvalContext *p, /* Pointer to structure to initialize */ + SqliteDb *pDb, /* Database handle */ + Tcl_Obj *pSql, /* Object containing SQL script */ + Tcl_Obj *pArray, /* Name of Tcl array to set (*) element of */ + int evalFlags /* Flags controlling evaluation */ +){ + memset(p, 0, sizeof(DbEvalContext)); + p->pDb = pDb; + p->zSql = Tcl_GetString(pSql); + p->pSql = pSql; + Tcl_IncrRefCount(pSql); + if( pArray ){ + p->pArray = pArray; + Tcl_IncrRefCount(pArray); + } + p->evalFlags = evalFlags; + addDatabaseRef(p->pDb); +} + +/* +** Obtain information about the row that the DbEvalContext passed as the +** first argument currently points to. +*/ +static void dbEvalRowInfo( + DbEvalContext *p, /* Evaluation context */ + int *pnCol, /* OUT: Number of column names */ + Tcl_Obj ***papColName /* OUT: Array of column names */ +){ + /* Compute column names */ + if( 0==p->apColName ){ + wx_sqlite3_stmt *pStmt = p->pPreStmt->pStmt; + int i; /* Iterator variable */ + int nCol; /* Number of columns returned by pStmt */ + Tcl_Obj **apColName = 0; /* Array of column names */ + + p->nCol = nCol = wx_sqlite3_column_count(pStmt); + if( nCol>0 && (papColName || p->pArray) ){ + apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol ); + for(i=0; iapColName = apColName; + } + + /* If results are being stored in an array variable, then create + ** the array(*) entry for that array + */ + if( p->pArray ){ + Tcl_Interp *interp = p->pDb->interp; + Tcl_Obj *pColList = Tcl_NewObj(); + Tcl_Obj *pStar = Tcl_NewStringObj("*", -1); + + for(i=0; ipArray, pStar, pColList, 0); + Tcl_DecrRefCount(pStar); + } + } + + if( papColName ){ + *papColName = p->apColName; + } + if( pnCol ){ + *pnCol = p->nCol; + } } + +/* +** Return one of TCL_OK, TCL_BREAK or TCL_ERROR. If TCL_ERROR is +** returned, then an error message is stored in the interpreter before +** returning. +** +** A return value of TCL_OK means there is a row of data available. The +** data may be accessed using dbEvalRowInfo() and dbEvalColumnValue(). This +** is analogous to a return of SQLITE_ROW from wx_sqlite3_step(). If TCL_BREAK +** is returned, then the SQL script has finished executing and there are +** no further rows available. This is similar to SQLITE_DONE. +*/ +static int dbEvalStep(DbEvalContext *p){ + const char *zPrevSql = 0; /* Previous value of p->zSql */ + + while( p->zSql[0] || p->pPreStmt ){ + int rc; + if( p->pPreStmt==0 ){ + zPrevSql = (p->zSql==zPrevSql ? 0 : p->zSql); + rc = dbPrepareAndBind(p->pDb, p->zSql, &p->zSql, &p->pPreStmt); + if( rc!=TCL_OK ) return rc; + }else{ + int rcs; + SqliteDb *pDb = p->pDb; + SqlPreparedStmt *pPreStmt = p->pPreStmt; + wx_sqlite3_stmt *pStmt = pPreStmt->pStmt; + + rcs = wx_sqlite3_step(pStmt); + if( rcs==SQLITE_ROW ){ + return TCL_OK; + } + if( p->pArray ){ + dbEvalRowInfo(p, 0, 0); + } + rcs = wx_sqlite3_reset(pStmt); + + pDb->nStep = wx_sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1); + pDb->nSort = wx_sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1); + pDb->nIndex = wx_sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_AUTOINDEX,1); + pDb->nVMStep = wx_sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_VM_STEP,1); + dbReleaseColumnNames(p); + p->pPreStmt = 0; + + if( rcs!=SQLITE_OK ){ + /* If a run-time error occurs, report the error and stop reading + ** the SQL. */ + dbReleaseStmt(pDb, pPreStmt, 1); +#if SQLITE_TEST + if( p->pDb->bLegacyPrepare && rcs==SQLITE_SCHEMA && zPrevSql ){ + /* If the runtime error was an SQLITE_SCHEMA, and the database + ** handle is configured to use the legacy wx_sqlite3_prepare() + ** interface, retry prepare()/step() on the same SQL statement. + ** This only happens once. If there is a second SQLITE_SCHEMA + ** error, the error will be returned to the caller. */ + p->zSql = zPrevSql; + continue; + } #endif + Tcl_SetObjResult(pDb->interp, + Tcl_NewStringObj(wx_sqlite3_errmsg(pDb->db), -1)); + return TCL_ERROR; + }else{ + dbReleaseStmt(pDb, pPreStmt, 0); + } + } + } -int -wx_sqlite3mc_initialize(const char* arg) -{ - int rc = SQLITE_OK; + /* Finished */ + return TCL_BREAK; +} + +/* +** Free all resources currently held by the DbEvalContext structure passed +** as the first argument. There should be exactly one call to this function +** for each call to dbEvalInit(). +*/ +static void dbEvalFinalize(DbEvalContext *p){ + if( p->pPreStmt ){ + wx_sqlite3_reset(p->pPreStmt->pStmt); + dbReleaseStmt(p->pDb, p->pPreStmt, 0); + p->pPreStmt = 0; + } + if( p->pArray ){ + Tcl_DecrRefCount(p->pArray); + p->pArray = 0; + } + Tcl_DecrRefCount(p->pSql); + dbReleaseColumnNames(p); + delDatabaseRef(p->pDb); +} + +/* +** Return a pointer to a Tcl_Obj structure with ref-count 0 that contains +** the value for the iCol'th column of the row currently pointed to by +** the DbEvalContext structure passed as the first argument. +*/ +static Tcl_Obj *dbEvalColumnValue(DbEvalContext *p, int iCol){ + wx_sqlite3_stmt *pStmt = p->pPreStmt->pStmt; + switch( wx_sqlite3_column_type(pStmt, iCol) ){ + case SQLITE_BLOB: { + int bytes = wx_sqlite3_column_bytes(pStmt, iCol); + const char *zBlob = wx_sqlite3_column_blob(pStmt, iCol); + if( !zBlob ) bytes = 0; + return Tcl_NewByteArrayObj((u8*)zBlob, bytes); + } + case SQLITE_INTEGER: { + sqlite_int64 v = wx_sqlite3_column_int64(pStmt, iCol); + if( v>=-2147483647 && v<=2147483647 ){ + return Tcl_NewIntObj((int)v); + }else{ + return Tcl_NewWideIntObj(v); + } + } + case SQLITE_FLOAT: { + return Tcl_NewDoubleObj(wx_sqlite3_column_double(pStmt, iCol)); + } + case SQLITE_NULL: { + return Tcl_NewStringObj(p->pDb->zNull, -1); + } + } + + return Tcl_NewStringObj((char*)wx_sqlite3_column_text(pStmt, iCol), -1); +} + +/* +** If using Tcl version 8.6 or greater, use the NR functions to avoid +** recursive evalution of scripts by the [db eval] and [db trans] +** commands. Even if the headers used while compiling the extension +** are 8.6 or newer, the code still tests the Tcl version at runtime. +** This allows stubs-enabled builds to be used with older Tcl libraries. +*/ +#if TCL_MAJOR_VERSION>8 || (TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION>=6) +# define SQLITE_TCL_NRE 1 +static int DbUseNre(void){ + int major, minor; + Tcl_GetVersion(&major, &minor, 0, 0); + return( (major==8 && minor>=6) || major>8 ); +} +#else +/* +** Compiling using headers earlier than 8.6. In this case NR cannot be +** used, so DbUseNre() to always return zero. Add #defines for the other +** Tcl_NRxxx() functions to prevent them from causing compilation errors, +** even though the only invocations of them are within conditional blocks +** of the form: +** +** if( DbUseNre() ) { ... } +*/ +# define SQLITE_TCL_NRE 0 +# define DbUseNre() 0 +# define Tcl_NRAddCallback(a,b,c,d,e,f) (void)0 +# define Tcl_NREvalObj(a,b,c) 0 +# define Tcl_NRCreateCommand(a,b,c,d,e,f) (void)0 +#endif + +/* +** This function is part of the implementation of the command: +** +** $db eval SQL ?ARRAYNAME? SCRIPT +*/ +static int SQLITE_TCLAPI DbEvalNextCmd( + ClientData data[], /* data[0] is the (DbEvalContext*) */ + Tcl_Interp *interp, /* Tcl interpreter */ + int result /* Result so far */ +){ + int rc = result; /* Return code */ + + /* The first element of the data[] array is a pointer to a DbEvalContext + ** structure allocated using Tcl_Alloc(). The second element of data[] + ** is a pointer to a Tcl_Obj containing the script to run for each row + ** returned by the queries encapsulated in data[0]. */ + DbEvalContext *p = (DbEvalContext *)data[0]; + Tcl_Obj *pScript = (Tcl_Obj *)data[1]; + Tcl_Obj *pArray = p->pArray; + + while( (rc==TCL_OK || rc==TCL_CONTINUE) && TCL_OK==(rc = dbEvalStep(p)) ){ + int i; + int nCol; + Tcl_Obj **apColName; + dbEvalRowInfo(p, &nCol, &apColName); + for(i=0; ievalFlags & SQLITE_EVAL_WITHOUTNULLS)!=0 + && wx_sqlite3_column_type(p->pPreStmt->pStmt, i)==SQLITE_NULL + ){ + Tcl_UnsetVar2(interp, Tcl_GetString(pArray), + Tcl_GetString(apColName[i]), 0); + }else{ + Tcl_ObjSetVar2(interp, pArray, apColName[i], dbEvalColumnValue(p,i), 0); + } + } + + /* The required interpreter variables are now populated with the data + ** from the current row. If using NRE, schedule callbacks to evaluate + ** script pScript, then to invoke this function again to fetch the next + ** row (or clean up if there is no next row or the script throws an + ** exception). After scheduling the callbacks, return control to the + ** caller. + ** + ** If not using NRE, evaluate pScript directly and continue with the + ** next iteration of this while(...) loop. */ + if( DbUseNre() ){ + Tcl_NRAddCallback(interp, DbEvalNextCmd, (void*)p, (void*)pScript, 0, 0); + return Tcl_NREvalObj(interp, pScript, 0); + }else{ + rc = Tcl_EvalObjEx(interp, pScript, 0); + } + } + + Tcl_DecrRefCount(pScript); + dbEvalFinalize(p); + Tcl_Free((char *)p); + + if( rc==TCL_OK || rc==TCL_BREAK ){ + Tcl_ResetResult(interp); + rc = TCL_OK; + } + return rc; +} + +/* +** This function is used by the implementations of the following database +** handle sub-commands: +** +** $db update_hook ?SCRIPT? +** $db wal_hook ?SCRIPT? +** $db commit_hook ?SCRIPT? +** $db preupdate hook ?SCRIPT? +*/ +static void DbHookCmd( + Tcl_Interp *interp, /* Tcl interpreter */ + SqliteDb *pDb, /* Database handle */ + Tcl_Obj *pArg, /* SCRIPT argument (or NULL) */ + Tcl_Obj **ppHook /* Pointer to member of SqliteDb */ +){ + wx_sqlite3 *db = pDb->db; + + if( *ppHook ){ + Tcl_SetObjResult(interp, *ppHook); + if( pArg ){ + Tcl_DecrRefCount(*ppHook); + *ppHook = 0; + } + } + if( pArg ){ + assert( !(*ppHook) ); + if( Tcl_GetCharLength(pArg)>0 ){ + *ppHook = pArg; + Tcl_IncrRefCount(*ppHook); + } + } + +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + wx_sqlite3_preupdate_hook(db, (pDb->pPreUpdateHook?DbPreUpdateHandler:0), pDb); +#endif + wx_sqlite3_update_hook(db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb); + wx_sqlite3_rollback_hook(db, (pDb->pRollbackHook?DbRollbackHandler:0), pDb); + wx_sqlite3_wal_hook(db, (pDb->pWalHook?DbWalHandler:0), pDb); +} + +/* +** The "sqlite" command below creates a new Tcl command for each +** connection it opens to an SQLite database. This routine is invoked +** whenever one of those connection-specific commands is executed +** in Tcl. For example, if you run Tcl code like this: +** +** wx_sqlite3 db1 "my_database" +** db1 close +** +** The first command opens a connection to the "my_database" database +** and calls that connection "db1". The second command causes this +** subroutine to be invoked. +*/ +static int SQLITE_TCLAPI DbObjCmd( + void *cd, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const*objv +){ + SqliteDb *pDb = (SqliteDb*)cd; + int choice; + int rc = TCL_OK; + static const char *DB_strs[] = { + "authorizer", "backup", "bind_fallback", + "busy", "cache", "changes", + "close", "collate", "collation_needed", + "commit_hook", "complete", "config", + "copy", "deserialize", "enable_load_extension", + "errorcode", "erroroffset", "eval", + "exists", "function", "incrblob", + "interrupt", "last_insert_rowid", "nullvalue", + "onecolumn", "preupdate", "profile", + "progress", "rekey", "restore", + "rollback_hook", "serialize", "status", + "timeout", "total_changes", "trace", + "trace_v2", "transaction", "unlock_notify", + "update_hook", "version", "wal_hook", + 0 + }; + enum DB_enum { + DB_AUTHORIZER, DB_BACKUP, DB_BIND_FALLBACK, + DB_BUSY, DB_CACHE, DB_CHANGES, + DB_CLOSE, DB_COLLATE, DB_COLLATION_NEEDED, + DB_COMMIT_HOOK, DB_COMPLETE, DB_CONFIG, + DB_COPY, DB_DESERIALIZE, DB_ENABLE_LOAD_EXTENSION, + DB_ERRORCODE, DB_ERROROFFSET, DB_EVAL, + DB_EXISTS, DB_FUNCTION, DB_INCRBLOB, + DB_INTERRUPT, DB_LAST_INSERT_ROWID, DB_NULLVALUE, + DB_ONECOLUMN, DB_PREUPDATE, DB_PROFILE, + DB_PROGRESS, DB_REKEY, DB_RESTORE, + DB_ROLLBACK_HOOK, DB_SERIALIZE, DB_STATUS, + DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE, + DB_TRACE_V2, DB_TRANSACTION, DB_UNLOCK_NOTIFY, + DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK, + }; + /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ + + if( objc<2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); + return TCL_ERROR; + } + if( Tcl_GetIndexFromObj(interp, objv[1], DB_strs, "option", 0, &choice) ){ + return TCL_ERROR; + } + + switch( (enum DB_enum)choice ){ + + /* $db authorizer ?CALLBACK? + ** + ** Invoke the given callback to authorize each SQL operation as it is + ** compiled. 5 arguments are appended to the callback before it is + ** invoked: + ** + ** (1) The authorization type (ex: SQLITE_CREATE_TABLE, SQLITE_INSERT, ...) + ** (2) First descriptive name (depends on authorization type) + ** (3) Second descriptive name + ** (4) Name of the database (ex: "main", "temp") + ** (5) Name of trigger that is doing the access + ** + ** The callback should return on of the following strings: SQLITE_OK, + ** SQLITE_IGNORE, or SQLITE_DENY. Any other return value is an error. + ** + ** If this method is invoked with no arguments, the current authorization + ** callback string is returned. + */ + case DB_AUTHORIZER: { +#ifdef SQLITE_OMIT_AUTHORIZATION + Tcl_AppendResult(interp, "authorization not available in this build", + (char*)0); + return TCL_ERROR; +#else + if( objc>3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); + return TCL_ERROR; + }else if( objc==2 ){ + if( pDb->zAuth ){ + Tcl_AppendResult(interp, pDb->zAuth, (char*)0); + } + }else{ + char *zAuth; + int len; + if( pDb->zAuth ){ + Tcl_Free(pDb->zAuth); + } + zAuth = Tcl_GetStringFromObj(objv[2], &len); + if( zAuth && len>0 ){ + pDb->zAuth = Tcl_Alloc( len + 1 ); + memcpy(pDb->zAuth, zAuth, len+1); + }else{ + pDb->zAuth = 0; + } + if( pDb->zAuth ){ + typedef int (*wx_sqlite3_auth_cb)( + void*,int,const char*,const char*, + const char*,const char*); + pDb->interp = interp; + wx_sqlite3_set_authorizer(pDb->db,(wx_sqlite3_auth_cb)auth_callback,pDb); + }else{ + wx_sqlite3_set_authorizer(pDb->db, 0, 0); + } + } +#endif + break; + } + + /* $db backup ?DATABASE? FILENAME + ** + ** Open or create a database file named FILENAME. Transfer the + ** content of local database DATABASE (default: "main") into the + ** FILENAME database. + */ + case DB_BACKUP: { + const char *zDestFile; + const char *zSrcDb; + wx_sqlite3 *pDest; + wx_sqlite3_backup *pBackup; + + if( objc==3 ){ + zSrcDb = "main"; + zDestFile = Tcl_GetString(objv[2]); + }else if( objc==4 ){ + zSrcDb = Tcl_GetString(objv[2]); + zDestFile = Tcl_GetString(objv[3]); + }else{ + Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? FILENAME"); + return TCL_ERROR; + } + rc = wx_sqlite3_open_v2(zDestFile, &pDest, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE| pDb->openFlags, 0); + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp, "cannot open target database: ", + wx_sqlite3_errmsg(pDest), (char*)0); + wx_sqlite3_close(pDest); + return TCL_ERROR; + } + pBackup = wx_sqlite3_backup_init(pDest, "main", pDb->db, zSrcDb); + if( pBackup==0 ){ + Tcl_AppendResult(interp, "backup failed: ", + wx_sqlite3_errmsg(pDest), (char*)0); + wx_sqlite3_close(pDest); + return TCL_ERROR; + } + while( (rc = wx_sqlite3_backup_step(pBackup,100))==SQLITE_OK ){} + wx_sqlite3_backup_finish(pBackup); + if( rc==SQLITE_DONE ){ + rc = TCL_OK; + }else{ + Tcl_AppendResult(interp, "backup failed: ", + wx_sqlite3_errmsg(pDest), (char*)0); + rc = TCL_ERROR; + } + wx_sqlite3_close(pDest); + break; + } + + /* $db bind_fallback ?CALLBACK? + ** + ** When resolving bind parameters in an SQL statement, if the parameter + ** cannot be associated with a TCL variable then invoke CALLBACK with a + ** single argument that is the name of the parameter and use the return + ** value of the CALLBACK as the binding. If CALLBACK returns something + ** other than TCL_OK or TCL_ERROR then bind a NULL. + ** + ** If CALLBACK is an empty string, then revert to the default behavior + ** which is to set the binding to NULL. + ** + ** If CALLBACK returns an error, that causes the statement execution to + ** abort. Hence, to configure a connection so that it throws an error + ** on an attempt to bind an unknown variable, do something like this: + ** + ** proc bind_error {name} {error "no such variable: $name"} + ** db bind_fallback bind_error + */ + case DB_BIND_FALLBACK: { + if( objc>3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); + return TCL_ERROR; + }else if( objc==2 ){ + if( pDb->zBindFallback ){ + Tcl_AppendResult(interp, pDb->zBindFallback, (char*)0); + } + }else{ + char *zCallback; + int len; + if( pDb->zBindFallback ){ + Tcl_Free(pDb->zBindFallback); + } + zCallback = Tcl_GetStringFromObj(objv[2], &len); + if( zCallback && len>0 ){ + pDb->zBindFallback = Tcl_Alloc( len + 1 ); + memcpy(pDb->zBindFallback, zCallback, len+1); + }else{ + pDb->zBindFallback = 0; + } + } + break; + } + + /* $db busy ?CALLBACK? + ** + ** Invoke the given callback if an SQL statement attempts to open + ** a locked database file. + */ + case DB_BUSY: { + if( objc>3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "CALLBACK"); + return TCL_ERROR; + }else if( objc==2 ){ + if( pDb->zBusy ){ + Tcl_AppendResult(interp, pDb->zBusy, (char*)0); + } + }else{ + char *zBusy; + int len; + if( pDb->zBusy ){ + Tcl_Free(pDb->zBusy); + } + zBusy = Tcl_GetStringFromObj(objv[2], &len); + if( zBusy && len>0 ){ + pDb->zBusy = Tcl_Alloc( len + 1 ); + memcpy(pDb->zBusy, zBusy, len+1); + }else{ + pDb->zBusy = 0; + } + if( pDb->zBusy ){ + pDb->interp = interp; + wx_sqlite3_busy_handler(pDb->db, DbBusyHandler, pDb); + }else{ + wx_sqlite3_busy_handler(pDb->db, 0, 0); + } + } + break; + } + + /* $db cache flush + ** $db cache size n + ** + ** Flush the prepared statement cache, or set the maximum number of + ** cached statements. + */ + case DB_CACHE: { + char *subCmd; + int n; + + if( objc<=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "cache option ?arg?"); + return TCL_ERROR; + } + subCmd = Tcl_GetStringFromObj( objv[2], 0 ); + if( *subCmd=='f' && strcmp(subCmd,"flush")==0 ){ + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "flush"); + return TCL_ERROR; + }else{ + flushStmtCache( pDb ); + } + }else if( *subCmd=='s' && strcmp(subCmd,"size")==0 ){ + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "size n"); + return TCL_ERROR; + }else{ + if( TCL_ERROR==Tcl_GetIntFromObj(interp, objv[3], &n) ){ + Tcl_AppendResult( interp, "cannot convert \"", + Tcl_GetStringFromObj(objv[3],0), "\" to integer", (char*)0); + return TCL_ERROR; + }else{ + if( n<0 ){ + flushStmtCache( pDb ); + n = 0; + }else if( n>MAX_PREPARED_STMTS ){ + n = MAX_PREPARED_STMTS; + } + pDb->maxStmt = n; + } + } + }else{ + Tcl_AppendResult( interp, "bad option \"", + Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size", + (char*)0); + return TCL_ERROR; + } + break; + } + + /* $db changes + ** + ** Return the number of rows that were modified, inserted, or deleted by + ** the most recent INSERT, UPDATE or DELETE statement, not including + ** any changes made by trigger programs. + */ + case DB_CHANGES: { + Tcl_Obj *pResult; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + pResult = Tcl_GetObjResult(interp); + Tcl_SetWideIntObj(pResult, wx_sqlite3_changes64(pDb->db)); + break; + } + + /* $db close + ** + ** Shutdown the database + */ + case DB_CLOSE: { + Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], 0)); + break; + } /* - ** Initialize and register MultiCipher VFS as default VFS - ** if it isn't already registered + ** $db collate NAME SCRIPT + ** + ** Create a new SQL collation function called NAME. Whenever + ** that function is called, invoke SCRIPT to evaluate the function. */ - rc = wx_sqlite3mc_vfs_create(NULL, 1); + case DB_COLLATE: { + SqlCollate *pCollate; + char *zName; + char *zScript; + int nScript; + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT"); + return TCL_ERROR; + } + zName = Tcl_GetStringFromObj(objv[2], 0); + zScript = Tcl_GetStringFromObj(objv[3], &nScript); + pCollate = (SqlCollate*)Tcl_Alloc( sizeof(*pCollate) + nScript + 1 ); + if( pCollate==0 ) return TCL_ERROR; + pCollate->interp = interp; + pCollate->pNext = pDb->pCollate; + pCollate->zScript = (char*)&pCollate[1]; + pDb->pCollate = pCollate; + memcpy(pCollate->zScript, zScript, nScript+1); + if( wx_sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8, + pCollate, tclSqlCollate) ){ + Tcl_SetResult(interp, (char *)wx_sqlite3_errmsg(pDb->db), TCL_VOLATILE); + return TCL_ERROR; + } + break; + } /* - ** Register Multi Cipher extension + ** $db collation_needed SCRIPT + ** + ** Create a new SQL collation function called NAME. Whenever + ** that function is called, invoke SCRIPT to evaluate the function. */ - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_auto_extension((void(*)(void)) mcRegisterCodecExtensions); + case DB_COLLATION_NEEDED: { + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "SCRIPT"); + return TCL_ERROR; + } + if( pDb->pCollateNeeded ){ + Tcl_DecrRefCount(pDb->pCollateNeeded); + } + pDb->pCollateNeeded = Tcl_DuplicateObj(objv[2]); + Tcl_IncrRefCount(pDb->pCollateNeeded); + wx_sqlite3_collation_needed(pDb->db, pDb, tclCollateNeeded); + break; } -#ifdef SQLITE_ENABLE_EXTFUNC - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_extfunc_init); + + /* $db commit_hook ?CALLBACK? + ** + ** Invoke the given callback just before committing every SQL transaction. + ** If the callback throws an exception or returns non-zero, then the + ** transaction is aborted. If CALLBACK is an empty string, the callback + ** is disabled. + */ + case DB_COMMIT_HOOK: { + if( objc>3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); + return TCL_ERROR; + }else if( objc==2 ){ + if( pDb->zCommit ){ + Tcl_AppendResult(interp, pDb->zCommit, (char*)0); + } + }else{ + const char *zCommit; + int len; + if( pDb->zCommit ){ + Tcl_Free(pDb->zCommit); + } + zCommit = Tcl_GetStringFromObj(objv[2], &len); + if( zCommit && len>0 ){ + pDb->zCommit = Tcl_Alloc( len + 1 ); + memcpy(pDb->zCommit, zCommit, len+1); + }else{ + pDb->zCommit = 0; + } + if( pDb->zCommit ){ + pDb->interp = interp; + wx_sqlite3_commit_hook(pDb->db, DbCommitHandler, pDb); + }else{ + wx_sqlite3_commit_hook(pDb->db, 0, 0); + } + } + break; } + + /* $db complete SQL + ** + ** Return TRUE if SQL is a complete SQL statement. Return FALSE if + ** additional lines of input are needed. This is similar to the + ** built-in "info complete" command of Tcl. + */ + case DB_COMPLETE: { +#ifndef SQLITE_OMIT_COMPLETE + Tcl_Obj *pResult; + int isComplete; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "SQL"); + return TCL_ERROR; + } + isComplete = wx_sqlite3_complete( Tcl_GetStringFromObj(objv[2], 0) ); + pResult = Tcl_GetObjResult(interp); + Tcl_SetBooleanObj(pResult, isComplete); #endif -#ifdef SQLITE_ENABLE_CSV - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_csv_init); + break; + } + + /* $db config ?OPTION? ?BOOLEAN? + ** + ** Configure the database connection using the wx_sqlite3_db_config() + ** interface. + */ + case DB_CONFIG: { + 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 }, + }; + Tcl_Obj *pResult; + int ii; + if( objc>4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?OPTION? ?BOOLEAN?"); + return TCL_ERROR; + } + if( objc==2 ){ + /* With no arguments, list all configuration options and with the + ** current value */ + pResult = Tcl_NewListObj(0,0); + for(ii=0; iidb, aDbConfig[ii].op, -1, &v); + Tcl_ListObjAppendElement(interp, pResult, + Tcl_NewStringObj(aDbConfig[ii].zName,-1)); + Tcl_ListObjAppendElement(interp, pResult, + Tcl_NewIntObj(v)); + } + }else{ + const char *zOpt = Tcl_GetString(objv[2]); + int onoff = -1; + int v = 0; + if( zOpt[0]=='-' ) zOpt++; + for(ii=0; ii=sizeof(aDbConfig)/sizeof(aDbConfig[0]) ){ + Tcl_AppendResult(interp, "unknown config option: \"", zOpt, + "\"", (void*)0); + return TCL_ERROR; + } + if( objc==4 ){ + if( Tcl_GetBooleanFromObj(interp, objv[3], &onoff) ){ + return TCL_ERROR; + } + } + wx_sqlite3_db_config(pDb->db, aDbConfig[ii].op, onoff, &v); + pResult = Tcl_NewIntObj(v); + } + Tcl_SetObjResult(interp, pResult); + break; + } + + /* $db copy conflict-algorithm table filename ?SEPARATOR? ?NULLINDICATOR? + ** + ** Copy data into table from filename, optionally using SEPARATOR + ** as column separators. If a column contains a null string, or the + ** value of NULLINDICATOR, a NULL is inserted for the column. + ** conflict-algorithm is one of the sqlite conflict algorithms: + ** rollback, abort, fail, ignore, replace + ** On success, return the number of lines processed, not necessarily same + ** as 'db changes' due to conflict-algorithm selected. + ** + ** This code is basically an implementation/enhancement of + ** the wx_sqlite3 shell.c ".import" command. + ** + ** This command usage is equivalent to the sqlite2.x COPY statement, + ** which imports file data into a table using the PostgreSQL COPY file format: + ** $db copy $conflit_algo $table_name $filename \t \\N + */ + case DB_COPY: { + char *zTable; /* Insert data into this table */ + char *zFile; /* The file from which to extract data */ + char *zConflict; /* The conflict algorithm to use */ + wx_sqlite3_stmt *pStmt; /* 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 nSep; /* Number of bytes in zSep[] */ + int nNull; /* Number of bytes in zNull[] */ + char *zSql; /* An SQL statement */ + char *zLine; /* A single line of input from the file */ + char **azCol; /* zLine[] broken up into columns */ + const char *zCommit; /* How to commit changes */ + FILE *in; /* The input file */ + int lineno = 0; /* Line number of input file */ + char zLineNum[80]; /* Line number print buffer */ + Tcl_Obj *pResult; /* interp result */ + + const char *zSep; + const char *zNull; + if( objc<5 || objc>7 ){ + Tcl_WrongNumArgs(interp, 2, objv, + "CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?"); + return TCL_ERROR; + } + if( objc>=6 ){ + zSep = Tcl_GetStringFromObj(objv[5], 0); + }else{ + zSep = "\t"; + } + if( objc>=7 ){ + zNull = Tcl_GetStringFromObj(objv[6], 0); + }else{ + zNull = ""; + } + zConflict = Tcl_GetStringFromObj(objv[2], 0); + zTable = Tcl_GetStringFromObj(objv[3], 0); + zFile = Tcl_GetStringFromObj(objv[4], 0); + nSep = strlen30(zSep); + nNull = strlen30(zNull); + if( nSep==0 ){ + Tcl_AppendResult(interp,"Error: non-null separator required for copy", + (char*)0); + return TCL_ERROR; + } + if(strcmp(zConflict, "rollback") != 0 && + strcmp(zConflict, "abort" ) != 0 && + strcmp(zConflict, "fail" ) != 0 && + strcmp(zConflict, "ignore" ) != 0 && + strcmp(zConflict, "replace" ) != 0 ) { + Tcl_AppendResult(interp, "Error: \"", zConflict, + "\", conflict-algorithm must be one of: rollback, " + "abort, fail, ignore, or replace", (char*)0); + return TCL_ERROR; + } + zSql = wx_sqlite3_mprintf("SELECT * FROM '%q'", zTable); + if( zSql==0 ){ + Tcl_AppendResult(interp, "Error: no such table: ", zTable, (char*)0); + return TCL_ERROR; + } + nByte = strlen30(zSql); + rc = wx_sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0); + wx_sqlite3_free(zSql); + if( rc ){ + Tcl_AppendResult(interp, "Error: ", wx_sqlite3_errmsg(pDb->db), (char*)0); + nCol = 0; + }else{ + nCol = wx_sqlite3_column_count(pStmt); + } + wx_sqlite3_finalize(pStmt); + if( nCol==0 ) { + return TCL_ERROR; + } + zSql = malloc( nByte + 50 + nCol*2 ); + if( zSql==0 ) { + Tcl_AppendResult(interp, "Error: can't malloc()", (char*)0); + return TCL_ERROR; + } + wx_sqlite3_snprintf(nByte+50, zSql, "INSERT OR %q INTO '%q' VALUES(?", + zConflict, zTable); + j = strlen30(zSql); + for(i=1; idb, zSql, -1, &pStmt, 0); + free(zSql); + if( rc ){ + Tcl_AppendResult(interp, "Error: ", wx_sqlite3_errmsg(pDb->db), (char*)0); + wx_sqlite3_finalize(pStmt); + return TCL_ERROR; + } + in = fopen(zFile, "rb"); + if( in==0 ){ + Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, (char*)0); + wx_sqlite3_finalize(pStmt); + return TCL_ERROR; + } + azCol = malloc( sizeof(azCol[0])*(nCol+1) ); + if( azCol==0 ) { + Tcl_AppendResult(interp, "Error: can't malloc()", (char*)0); + fclose(in); + return TCL_ERROR; + } + (void)wx_sqlite3_exec(pDb->db, "BEGIN", 0, 0, 0); + zCommit = "COMMIT"; + while( (zLine = local_getline(0, in))!=0 ){ + char *z; + lineno++; + azCol[0] = zLine; + for(i=0, z=zLine; *z; z++){ + if( *z==zSep[0] && strncmp(z, zSep, nSep)==0 ){ + *z = 0; + i++; + if( i0 && strcmp(azCol[i], zNull)==0) + || strlen30(azCol[i])==0 + ){ + wx_sqlite3_bind_null(pStmt, i+1); + }else{ + wx_sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC); + } + } + wx_sqlite3_step(pStmt); + rc = wx_sqlite3_reset(pStmt); + free(zLine); + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp,"Error: ", wx_sqlite3_errmsg(pDb->db), (char*)0); + zCommit = "ROLLBACK"; + break; + } + } + free(azCol); + fclose(in); + wx_sqlite3_finalize(pStmt); + (void)wx_sqlite3_exec(pDb->db, zCommit, 0, 0, 0); + + if( zCommit[0] == 'C' ){ + /* success, set result as number of lines processed */ + pResult = Tcl_GetObjResult(interp); + Tcl_SetIntObj(pResult, lineno); + rc = TCL_OK; + }else{ + /* failure, append lineno where failed */ + wx_sqlite3_snprintf(sizeof(zLineNum), zLineNum,"%d",lineno); + Tcl_AppendResult(interp,", failed while processing line: ",zLineNum, + (char*)0); + rc = TCL_ERROR; + } + break; } + + /* + ** $db deserialize ?-maxsize N? ?-readonly BOOL? ?DATABASE? VALUE + ** + ** Reopen DATABASE (default "main") using the content in $VALUE + */ + case DB_DESERIALIZE: { +#ifdef SQLITE_OMIT_DESERIALIZE + Tcl_AppendResult(interp, "MEMDB not available in this build", + (char*)0); + rc = TCL_ERROR; +#else + const char *zSchema = 0; + Tcl_Obj *pValue = 0; + unsigned char *pBA; + unsigned char *pData; + int len, xrc; + wx_sqlite3_int64 mxSize = 0; + int i; + int isReadonly = 0; + + + if( objc<3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? VALUE"); + rc = TCL_ERROR; + break; + } + for(i=2; i0 ){ + Tcl_AppendResult(interp, "out of memory", (char*)0); + rc = TCL_ERROR; + }else{ + int flags; + if( len>0 ) memcpy(pData, pBA, len); + if( isReadonly ){ + flags = SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_READONLY; + }else{ + flags = SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_RESIZEABLE; + } + xrc = wx_sqlite3_deserialize(pDb->db, zSchema, pData, len, len, flags); + if( xrc ){ + Tcl_AppendResult(interp, "unable to set MEMDB content", (char*)0); + rc = TCL_ERROR; + } + if( mxSize>0 ){ + wx_sqlite3_file_control(pDb->db, zSchema,SQLITE_FCNTL_SIZE_LIMIT,&mxSize); + } + } +deserialize_error: #endif -#ifdef SQLITE_ENABLE_VSV - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_vsv_init); + break; } + + /* + ** $db enable_load_extension BOOLEAN + ** + ** Turn the extension loading feature on or off. It if off by + ** default. + */ + case DB_ENABLE_LOAD_EXTENSION: { +#ifndef SQLITE_OMIT_LOAD_EXTENSION + int onoff; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "BOOLEAN"); + return TCL_ERROR; + } + if( Tcl_GetBooleanFromObj(interp, objv[2], &onoff) ){ + return TCL_ERROR; + } + wx_sqlite3_enable_load_extension(pDb->db, onoff); + break; +#else + Tcl_AppendResult(interp, "extension loading is turned off at compile-time", + (char*)0); + return TCL_ERROR; #endif -#ifdef SQLITE_ENABLE_SHA3 - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_shathree_init); } + + /* + ** $db errorcode + ** + ** Return the numeric error code that was returned by the most recent + ** call to wx_sqlite3_exec(). + */ + case DB_ERRORCODE: { + Tcl_SetObjResult(interp, Tcl_NewIntObj(wx_sqlite3_errcode(pDb->db))); + break; + } + + /* + ** $db erroroffset + ** + ** Return the numeric error code that was returned by the most recent + ** call to wx_sqlite3_exec(). + */ + case DB_ERROROFFSET: { + Tcl_SetObjResult(interp, Tcl_NewIntObj(wx_sqlite3_error_offset(pDb->db))); + break; + } + + /* + ** $db exists $sql + ** $db onecolumn $sql + ** + ** The onecolumn method is the equivalent of: + ** lindex [$db eval $sql] 0 + */ + case DB_EXISTS: + case DB_ONECOLUMN: { + Tcl_Obj *pResult = 0; + DbEvalContext sEval; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "SQL"); + return TCL_ERROR; + } + + dbEvalInit(&sEval, pDb, objv[2], 0, 0); + rc = dbEvalStep(&sEval); + if( choice==DB_ONECOLUMN ){ + if( rc==TCL_OK ){ + pResult = dbEvalColumnValue(&sEval, 0); + }else if( rc==TCL_BREAK ){ + Tcl_ResetResult(interp); + } + }else if( rc==TCL_BREAK || rc==TCL_OK ){ + pResult = Tcl_NewBooleanObj(rc==TCL_OK); + } + dbEvalFinalize(&sEval); + if( pResult ) Tcl_SetObjResult(interp, pResult); + + if( rc==TCL_BREAK ){ + rc = TCL_OK; + } + break; + } + + /* + ** $db eval ?options? $sql ?array? ?{ ...code... }? + ** + ** The SQL statement in $sql is evaluated. For each row, the values are + ** placed in elements of the array named "array" and ...code... is executed. + ** If "array" and "code" are omitted, then no callback is every invoked. + ** If "array" is an empty string, then the values are placed in variables + ** that have the same name as the fields extracted by the query. + */ + case DB_EVAL: { + int evalFlags = 0; + const char *zOpt; + while( objc>3 && (zOpt = Tcl_GetString(objv[2]))!=0 && zOpt[0]=='-' ){ + if( strcmp(zOpt, "-withoutnulls")==0 ){ + evalFlags |= SQLITE_EVAL_WITHOUTNULLS; + } + else{ + Tcl_AppendResult(interp, "unknown option: \"", zOpt, "\"", (void*)0); + return TCL_ERROR; + } + objc--; + objv++; + } + if( objc<3 || objc>5 ){ + Tcl_WrongNumArgs(interp, 2, objv, + "?OPTIONS? SQL ?ARRAY-NAME? ?SCRIPT?"); + return TCL_ERROR; + } + + if( objc==3 ){ + DbEvalContext sEval; + Tcl_Obj *pRet = Tcl_NewObj(); + Tcl_IncrRefCount(pRet); + dbEvalInit(&sEval, pDb, objv[2], 0, 0); + while( TCL_OK==(rc = dbEvalStep(&sEval)) ){ + int i; + int nCol; + dbEvalRowInfo(&sEval, &nCol, 0); + for(i=0; i=5 && *(char *)Tcl_GetString(objv[3]) ){ + pArray = objv[3]; + } + pScript = objv[objc-1]; + Tcl_IncrRefCount(pScript); + + p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext)); + dbEvalInit(p, pDb, objv[2], pArray, evalFlags); + + cd2[0] = (void *)p; + cd2[1] = (void *)pScript; + rc = DbEvalNextCmd(cd2, interp, TCL_OK); + } + break; + } + + /* + ** $db function NAME [OPTIONS] SCRIPT + ** + ** Create a new SQL function called NAME. Whenever that function is + ** called, invoke SCRIPT to evaluate the function. + ** + ** Options: + ** --argcount N Function has exactly N arguments + ** --deterministic The function is pure + ** --directonly Prohibit use inside triggers and views + ** --innocuous Has no side effects or information leaks + ** --returntype TYPE Specify the return type of the function + */ + case DB_FUNCTION: { + int flags = SQLITE_UTF8; + SqlFunc *pFunc; + Tcl_Obj *pScript; + char *zName; + int nArg = -1; + int i; + int eType = SQLITE_NULL; + if( objc<4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "NAME ?SWITCHES? SCRIPT"); + return TCL_ERROR; + } + for(i=3; i<(objc-1); i++){ + const char *z = Tcl_GetString(objv[i]); + int n = strlen30(z); + if( n>1 && strncmp(z, "-argcount",n)==0 ){ + if( i==(objc-2) ){ + Tcl_AppendResult(interp, "option requires an argument: ", z,(char*)0); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[i+1], &nArg) ) return TCL_ERROR; + if( nArg<0 ){ + Tcl_AppendResult(interp, "number of arguments must be non-negative", + (char*)0); + return TCL_ERROR; + } + i++; + }else + if( n>1 && strncmp(z, "-deterministic",n)==0 ){ + flags |= SQLITE_DETERMINISTIC; + }else + if( n>1 && strncmp(z, "-directonly",n)==0 ){ + flags |= SQLITE_DIRECTONLY; + }else + if( n>1 && strncmp(z, "-innocuous",n)==0 ){ + flags |= SQLITE_INNOCUOUS; + }else + if( n>1 && strncmp(z, "-returntype", n)==0 ){ + const char *azType[] = {"integer", "real", "text", "blob", "any", 0}; + assert( SQLITE_INTEGER==1 && SQLITE_FLOAT==2 && SQLITE_TEXT==3 ); + assert( SQLITE_BLOB==4 && SQLITE_NULL==5 ); + if( i==(objc-2) ){ + Tcl_AppendResult(interp, "option requires an argument: ", z,(char*)0); + return TCL_ERROR; + } + i++; + if( Tcl_GetIndexFromObj(interp, objv[i], azType, "type", 0, &eType) ){ + return TCL_ERROR; + } + eType++; + }else{ + Tcl_AppendResult(interp, "bad option \"", z, + "\": must be -argcount, -deterministic, -directonly," + " -innocuous, or -returntype", (char*)0 + ); + return TCL_ERROR; + } + } + + pScript = objv[objc-1]; + zName = Tcl_GetStringFromObj(objv[2], 0); + pFunc = findSqlFunc(pDb, zName); + if( pFunc==0 ) return TCL_ERROR; + if( pFunc->pScript ){ + Tcl_DecrRefCount(pFunc->pScript); + } + pFunc->pScript = pScript; + Tcl_IncrRefCount(pScript); + pFunc->useEvalObjv = safeToUseEvalObjv(interp, pScript); + pFunc->eType = eType; + rc = wx_sqlite3_create_function(pDb->db, zName, nArg, flags, + pFunc, tclSqlFunc, 0, 0); + if( rc!=SQLITE_OK ){ + rc = TCL_ERROR; + Tcl_SetResult(interp, (char *)wx_sqlite3_errmsg(pDb->db), TCL_VOLATILE); + } + break; + } + + /* + ** $db incrblob ?-readonly? ?DB? TABLE COLUMN ROWID + */ + case DB_INCRBLOB: { +#ifdef SQLITE_OMIT_INCRBLOB + Tcl_AppendResult(interp, "incrblob not available in this build", (char*)0); + return TCL_ERROR; +#else + int isReadonly = 0; + const char *zDb = "main"; + const char *zTable; + const char *zColumn; + Tcl_WideInt iRow; + + /* Check for the -readonly option */ + if( objc>3 && strcmp(Tcl_GetString(objv[2]), "-readonly")==0 ){ + isReadonly = 1; + } + + if( objc!=(5+isReadonly) && objc!=(6+isReadonly) ){ + Tcl_WrongNumArgs(interp, 2, objv, "?-readonly? ?DB? TABLE COLUMN ROWID"); + return TCL_ERROR; + } + + if( objc==(6+isReadonly) ){ + zDb = Tcl_GetString(objv[2+isReadonly]); + } + zTable = Tcl_GetString(objv[objc-3]); + zColumn = Tcl_GetString(objv[objc-2]); + rc = Tcl_GetWideIntFromObj(interp, objv[objc-1], &iRow); + + if( rc==TCL_OK ){ + rc = createIncrblobChannel( + interp, pDb, zDb, zTable, zColumn, (wx_sqlite3_int64)iRow, isReadonly + ); + } #endif -#ifdef SQLITE_ENABLE_CARRAY - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_carray_init); + break; + } + + /* + ** $db interrupt + ** + ** Interrupt the execution of the inner-most SQL interpreter. This + ** causes the SQL statement to return an error of SQLITE_INTERRUPT. + */ + case DB_INTERRUPT: { + wx_sqlite3_interrupt(pDb->db); + break; + } + + /* + ** $db nullvalue ?STRING? + ** + ** Change text used when a NULL comes back from the database. If ?STRING? + ** is not present, then the current string used for NULL is returned. + ** If STRING is present, then STRING is returned. + ** + */ + case DB_NULLVALUE: { + if( objc!=2 && objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "NULLVALUE"); + return TCL_ERROR; + } + if( objc==3 ){ + int len; + char *zNull = Tcl_GetStringFromObj(objv[2], &len); + if( pDb->zNull ){ + Tcl_Free(pDb->zNull); + } + if( zNull && len>0 ){ + pDb->zNull = Tcl_Alloc( len + 1 ); + memcpy(pDb->zNull, zNull, len); + pDb->zNull[len] = '\0'; + }else{ + pDb->zNull = 0; + } + } + Tcl_SetObjResult(interp, Tcl_NewStringObj(pDb->zNull, -1)); + break; + } + + /* + ** $db last_insert_rowid + ** + ** Return an integer which is the ROWID for the most recent insert. + */ + case DB_LAST_INSERT_ROWID: { + Tcl_Obj *pResult; + Tcl_WideInt rowid; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + rowid = wx_sqlite3_last_insert_rowid(pDb->db); + pResult = Tcl_GetObjResult(interp); + Tcl_SetWideIntObj(pResult, rowid); + break; } + + /* + ** The DB_ONECOLUMN method is implemented together with DB_EXISTS. + */ + + /* $db progress ?N CALLBACK? + ** + ** Invoke the given callback every N virtual machine opcodes while executing + ** queries. + */ + case DB_PROGRESS: { + if( objc==2 ){ + if( pDb->zProgress ){ + Tcl_AppendResult(interp, pDb->zProgress, (char*)0); + } +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + wx_sqlite3_progress_handler(pDb->db, 0, 0, 0); +#endif + }else if( objc==4 ){ + char *zProgress; + int len; + int N; + if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &N) ){ + return TCL_ERROR; + }; + if( pDb->zProgress ){ + Tcl_Free(pDb->zProgress); + } + zProgress = Tcl_GetStringFromObj(objv[3], &len); + if( zProgress && len>0 ){ + pDb->zProgress = Tcl_Alloc( len + 1 ); + memcpy(pDb->zProgress, zProgress, len+1); + }else{ + pDb->zProgress = 0; + } +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + if( pDb->zProgress ){ + pDb->interp = interp; + wx_sqlite3_progress_handler(pDb->db, N, DbProgressHandler, pDb); + }else{ + wx_sqlite3_progress_handler(pDb->db, 0, 0, 0); + } #endif -#ifdef SQLITE_ENABLE_FILEIO - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_fileio_init); + }else{ + Tcl_WrongNumArgs(interp, 2, objv, "N CALLBACK"); + return TCL_ERROR; + } + break; } + + /* $db profile ?CALLBACK? + ** + ** Make arrangements to invoke the CALLBACK routine after each SQL statement + ** that has run. The text of the SQL and the amount of elapse time are + ** appended to CALLBACK before the script is run. + */ + case DB_PROFILE: { + if( objc>3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); + return TCL_ERROR; + }else if( objc==2 ){ + if( pDb->zProfile ){ + Tcl_AppendResult(interp, pDb->zProfile, (char*)0); + } + }else{ + char *zProfile; + int len; + if( pDb->zProfile ){ + Tcl_Free(pDb->zProfile); + } + zProfile = Tcl_GetStringFromObj(objv[2], &len); + if( zProfile && len>0 ){ + pDb->zProfile = Tcl_Alloc( len + 1 ); + memcpy(pDb->zProfile, zProfile, len+1); + }else{ + pDb->zProfile = 0; + } +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \ + !defined(SQLITE_OMIT_DEPRECATED) + if( pDb->zProfile ){ + pDb->interp = interp; + wx_sqlite3_profile(pDb->db, DbProfileHandler, pDb); + }else{ + wx_sqlite3_profile(pDb->db, 0, 0); + } #endif -#ifdef SQLITE_ENABLE_SERIES - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_series_init); + } + break; + } + + /* + ** $db rekey KEY + ** + ** Change the encryption key on the currently open database. + */ + case DB_REKEY: { + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "KEY"); + return TCL_ERROR; + } + break; + } + + /* $db restore ?DATABASE? FILENAME + ** + ** Open a database file named FILENAME. Transfer the content + ** of FILENAME into the local database DATABASE (default: "main"). + */ + case DB_RESTORE: { + const char *zSrcFile; + const char *zDestDb; + wx_sqlite3 *pSrc; + wx_sqlite3_backup *pBackup; + int nTimeout = 0; + + if( objc==3 ){ + zDestDb = "main"; + zSrcFile = Tcl_GetString(objv[2]); + }else if( objc==4 ){ + zDestDb = Tcl_GetString(objv[2]); + zSrcFile = Tcl_GetString(objv[3]); + }else{ + Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? FILENAME"); + return TCL_ERROR; + } + rc = wx_sqlite3_open_v2(zSrcFile, &pSrc, + SQLITE_OPEN_READONLY | pDb->openFlags, 0); + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp, "cannot open source database: ", + wx_sqlite3_errmsg(pSrc), (char*)0); + wx_sqlite3_close(pSrc); + return TCL_ERROR; + } + pBackup = wx_sqlite3_backup_init(pDb->db, zDestDb, pSrc, "main"); + if( pBackup==0 ){ + Tcl_AppendResult(interp, "restore failed: ", + wx_sqlite3_errmsg(pDb->db), (char*)0); + wx_sqlite3_close(pSrc); + return TCL_ERROR; + } + while( (rc = wx_sqlite3_backup_step(pBackup,100))==SQLITE_OK + || rc==SQLITE_BUSY ){ + if( rc==SQLITE_BUSY ){ + if( nTimeout++ >= 3 ) break; + wx_sqlite3_sleep(100); + } + } + wx_sqlite3_backup_finish(pBackup); + if( rc==SQLITE_DONE ){ + rc = TCL_OK; + }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ + Tcl_AppendResult(interp, "restore failed: source database busy", + (char*)0); + rc = TCL_ERROR; + }else{ + Tcl_AppendResult(interp, "restore failed: ", + wx_sqlite3_errmsg(pDb->db), (char*)0); + rc = TCL_ERROR; + } + wx_sqlite3_close(pSrc); + break; } + + /* + ** $db serialize ?DATABASE? + ** + ** Return a serialization of a database. + */ + case DB_SERIALIZE: { +#ifdef SQLITE_OMIT_DESERIALIZE + Tcl_AppendResult(interp, "MEMDB not available in this build", + (char*)0); + rc = TCL_ERROR; +#else + const char *zSchema = objc>=3 ? Tcl_GetString(objv[2]) : "main"; + wx_sqlite3_int64 sz = 0; + unsigned char *pData; + if( objc!=2 && objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE?"); + rc = TCL_ERROR; + }else{ + int needFree; + pData = wx_sqlite3_serialize(pDb->db, zSchema, &sz, SQLITE_SERIALIZE_NOCOPY); + if( pData ){ + needFree = 0; + }else{ + pData = wx_sqlite3_serialize(pDb->db, zSchema, &sz, 0); + needFree = 1; + } + Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pData,sz)); + if( needFree ) wx_sqlite3_free(pData); + } #endif -#ifdef SQLITE_ENABLE_UUID - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_uuid_init); + break; + } + + /* + ** $db status (step|sort|autoindex|vmstep) + ** + ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or + ** SQLITE_STMTSTATUS_SORT for the most recent eval. + */ + case DB_STATUS: { + int v; + const char *zOp; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "(step|sort|autoindex)"); + return TCL_ERROR; + } + zOp = Tcl_GetString(objv[2]); + if( strcmp(zOp, "step")==0 ){ + v = pDb->nStep; + }else if( strcmp(zOp, "sort")==0 ){ + v = pDb->nSort; + }else if( strcmp(zOp, "autoindex")==0 ){ + v = pDb->nIndex; + }else if( strcmp(zOp, "vmstep")==0 ){ + v = pDb->nVMStep; + }else{ + Tcl_AppendResult(interp, + "bad argument: should be autoindex, step, sort or vmstep", + (char*)0); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(v)); + break; + } + + /* + ** $db timeout MILLESECONDS + ** + ** Delay for the number of milliseconds specified when a file is locked. + */ + case DB_TIMEOUT: { + int ms; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "MILLISECONDS"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[2], &ms) ) return TCL_ERROR; + wx_sqlite3_busy_timeout(pDb->db, ms); + break; + } + + /* + ** $db total_changes + ** + ** Return the number of rows that were modified, inserted, or deleted + ** since the database handle was created. + */ + case DB_TOTAL_CHANGES: { + Tcl_Obj *pResult; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + pResult = Tcl_GetObjResult(interp); + Tcl_SetWideIntObj(pResult, wx_sqlite3_total_changes64(pDb->db)); + break; } + + /* $db trace ?CALLBACK? + ** + ** Make arrangements to invoke the CALLBACK routine for each SQL statement + ** that is executed. The text of the SQL is appended to CALLBACK before + ** it is executed. + */ + case DB_TRACE: { + if( objc>3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); + return TCL_ERROR; + }else if( objc==2 ){ + if( pDb->zTrace ){ + Tcl_AppendResult(interp, pDb->zTrace, (char*)0); + } + }else{ + char *zTrace; + int len; + if( pDb->zTrace ){ + Tcl_Free(pDb->zTrace); + } + zTrace = Tcl_GetStringFromObj(objv[2], &len); + if( zTrace && len>0 ){ + pDb->zTrace = Tcl_Alloc( len + 1 ); + memcpy(pDb->zTrace, zTrace, len+1); + }else{ + pDb->zTrace = 0; + } +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) && \ + !defined(SQLITE_OMIT_DEPRECATED) + if( pDb->zTrace ){ + pDb->interp = interp; + wx_sqlite3_trace(pDb->db, DbTraceHandler, pDb); + }else{ + wx_sqlite3_trace(pDb->db, 0, 0); + } #endif -#ifdef SQLITE_ENABLE_REGEXP - if (rc == SQLITE_OK) - { - rc = wx_sqlite3_auto_extension((void(*)(void)) wx_sqlite3_regexp_init); + } + break; + } + + /* $db trace_v2 ?CALLBACK? ?MASK? + ** + ** Make arrangements to invoke the CALLBACK routine for each trace event + ** matching the mask that is generated. The parameters are appended to + ** CALLBACK before it is executed. + */ + case DB_TRACE_V2: { + if( objc>4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK? ?MASK?"); + return TCL_ERROR; + }else if( objc==2 ){ + if( pDb->zTraceV2 ){ + Tcl_AppendResult(interp, pDb->zTraceV2, (char*)0); + } + }else{ + char *zTraceV2; + int len; + Tcl_WideInt wMask = 0; + if( objc==4 ){ + static const char *TTYPE_strs[] = { + "statement", "profile", "row", "close", 0 + }; + enum TTYPE_enum { + TTYPE_STMT, TTYPE_PROFILE, TTYPE_ROW, TTYPE_CLOSE + }; + int i; + if( TCL_OK!=Tcl_ListObjLength(interp, objv[3], &len) ){ + return TCL_ERROR; + } + for(i=0; izTraceV2 ){ + Tcl_Free(pDb->zTraceV2); + } + zTraceV2 = Tcl_GetStringFromObj(objv[2], &len); + if( zTraceV2 && len>0 ){ + pDb->zTraceV2 = Tcl_Alloc( len + 1 ); + memcpy(pDb->zTraceV2, zTraceV2, len+1); + }else{ + pDb->zTraceV2 = 0; + } +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) + if( pDb->zTraceV2 ){ + pDb->interp = interp; + wx_sqlite3_trace_v2(pDb->db, (unsigned)wMask, DbTraceV2Handler, pDb); + }else{ + wx_sqlite3_trace_v2(pDb->db, 0, 0, 0); + } +#endif + } + break; + } + + /* $db transaction [-deferred|-immediate|-exclusive] SCRIPT + ** + ** Start a new transaction (if we are not already in the midst of a + ** transaction) and execute the TCL script SCRIPT. After SCRIPT + ** completes, either commit the transaction or roll it back if SCRIPT + ** throws an exception. Or if no new transation was started, do nothing. + ** pass the exception on up the stack. + ** + ** This command was inspired by Dave Thomas's talk on Ruby at the + ** 2005 O'Reilly Open Source Convention (OSCON). + */ + case DB_TRANSACTION: { + Tcl_Obj *pScript; + const char *zBegin = "SAVEPOINT _tcl_transaction"; + if( objc!=3 && objc!=4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "[TYPE] SCRIPT"); + return TCL_ERROR; + } + + if( pDb->nTransaction==0 && objc==4 ){ + static const char *TTYPE_strs[] = { + "deferred", "exclusive", "immediate", 0 + }; + enum TTYPE_enum { + TTYPE_DEFERRED, TTYPE_EXCLUSIVE, TTYPE_IMMEDIATE + }; + int ttype; + if( Tcl_GetIndexFromObj(interp, objv[2], TTYPE_strs, "transaction type", + 0, &ttype) ){ + return TCL_ERROR; + } + switch( (enum TTYPE_enum)ttype ){ + case TTYPE_DEFERRED: /* no-op */; break; + case TTYPE_EXCLUSIVE: zBegin = "BEGIN EXCLUSIVE"; break; + case TTYPE_IMMEDIATE: zBegin = "BEGIN IMMEDIATE"; break; + } + } + pScript = objv[objc-1]; + + /* Run the SQLite BEGIN command to open a transaction or savepoint. */ + pDb->disableAuth++; + rc = wx_sqlite3_exec(pDb->db, zBegin, 0, 0, 0); + pDb->disableAuth--; + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp, wx_sqlite3_errmsg(pDb->db), (char*)0); + return TCL_ERROR; + } + pDb->nTransaction++; + + /* If using NRE, schedule a callback to invoke the script pScript, then + ** a second callback to commit (or rollback) the transaction or savepoint + ** opened above. If not using NRE, evaluate the script directly, then + ** call function DbTransPostCmd() to commit (or rollback) the transaction + ** or savepoint. */ + addDatabaseRef(pDb); /* DbTransPostCmd() calls delDatabaseRef() */ + if( DbUseNre() ){ + Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0); + (void)Tcl_NREvalObj(interp, pScript, 0); + }else{ + rc = DbTransPostCmd(&cd, interp, Tcl_EvalObjEx(interp, pScript, 0)); + } + break; } + + /* + ** $db unlock_notify ?script? + */ + case DB_UNLOCK_NOTIFY: { +#ifndef SQLITE_ENABLE_UNLOCK_NOTIFY + Tcl_AppendResult(interp, "unlock_notify not available in this build", + (char*)0); + rc = TCL_ERROR; +#else + if( objc!=2 && objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); + rc = TCL_ERROR; + }else{ + void (*xNotify)(void **, int) = 0; + void *pNotifyArg = 0; + + if( pDb->pUnlockNotify ){ + Tcl_DecrRefCount(pDb->pUnlockNotify); + pDb->pUnlockNotify = 0; + } + + if( objc==3 ){ + xNotify = DbUnlockNotify; + pNotifyArg = (void *)pDb; + pDb->pUnlockNotify = objv[2]; + Tcl_IncrRefCount(pDb->pUnlockNotify); + } + + if( wx_sqlite3_unlock_notify(pDb->db, xNotify, pNotifyArg) ){ + Tcl_AppendResult(interp, wx_sqlite3_errmsg(pDb->db), (char*)0); + rc = TCL_ERROR; + } + } #endif + break; + } + + /* + ** $db preupdate_hook count + ** $db preupdate_hook hook ?SCRIPT? + ** $db preupdate_hook new INDEX + ** $db preupdate_hook old INDEX + */ + case DB_PREUPDATE: { +#ifndef SQLITE_ENABLE_PREUPDATE_HOOK + Tcl_AppendResult(interp, "preupdate_hook was omitted at compile-time", + (char*)0); + rc = TCL_ERROR; +#else + static const char *azSub[] = {"count", "depth", "hook", "new", "old", 0}; + enum DbPreupdateSubCmd { + PRE_COUNT, PRE_DEPTH, PRE_HOOK, PRE_NEW, PRE_OLD + }; + int iSub; + + if( objc<3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "SUB-COMMAND ?ARGS?"); + } + if( Tcl_GetIndexFromObj(interp, objv[2], azSub, "sub-command", 0, &iSub) ){ + return TCL_ERROR; + } + + switch( (enum DbPreupdateSubCmd)iSub ){ + case PRE_COUNT: { + int nCol = wx_sqlite3_preupdate_count(pDb->db); + Tcl_SetObjResult(interp, Tcl_NewIntObj(nCol)); + break; + } + + case PRE_HOOK: { + if( objc>4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "hook ?SCRIPT?"); + return TCL_ERROR; + } + DbHookCmd(interp, pDb, (objc==4 ? objv[3] : 0), &pDb->pPreUpdateHook); + break; + } + + case PRE_DEPTH: { + Tcl_Obj *pRet; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 3, objv, ""); + return TCL_ERROR; + } + pRet = Tcl_NewIntObj(wx_sqlite3_preupdate_depth(pDb->db)); + Tcl_SetObjResult(interp, pRet); + break; + } + + case PRE_NEW: + case PRE_OLD: { + int iIdx; + wx_sqlite3_value *pValue; + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 3, objv, "INDEX"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[3], &iIdx) ){ + return TCL_ERROR; + } + + if( iSub==PRE_OLD ){ + rc = wx_sqlite3_preupdate_old(pDb->db, iIdx, &pValue); + }else{ + assert( iSub==PRE_NEW ); + rc = wx_sqlite3_preupdate_new(pDb->db, iIdx, &pValue); + } + + if( rc==SQLITE_OK ){ + Tcl_Obj *pObj; + pObj = Tcl_NewStringObj((char*)wx_sqlite3_value_text(pValue), -1); + Tcl_SetObjResult(interp, pObj); + }else{ + Tcl_AppendResult(interp, wx_sqlite3_errmsg(pDb->db), (char*)0); + return TCL_ERROR; + } + } + } +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + break; + } + + /* + ** $db wal_hook ?script? + ** $db update_hook ?script? + ** $db rollback_hook ?script? + */ + case DB_WAL_HOOK: + case DB_UPDATE_HOOK: + case DB_ROLLBACK_HOOK: { + /* set ppHook to point at pUpdateHook or pRollbackHook, depending on + ** whether [$db update_hook] or [$db rollback_hook] was invoked. + */ + Tcl_Obj **ppHook = 0; + if( choice==DB_WAL_HOOK ) ppHook = &pDb->pWalHook; + if( choice==DB_UPDATE_HOOK ) ppHook = &pDb->pUpdateHook; + if( choice==DB_ROLLBACK_HOOK ) ppHook = &pDb->pRollbackHook; + if( objc>3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); + return TCL_ERROR; + } + + DbHookCmd(interp, pDb, (objc==3 ? objv[2] : 0), ppHook); + break; + } + + /* $db version + ** + ** Return the version string for this database. + */ + case DB_VERSION: { + int i; + for(i=2; ibLegacyPrepare) ){ + return TCL_ERROR; + } + }else + + /* $db version -last-stmt-ptr + ** + ** Return a string which is a hex encoding of the pointer to the + ** most recent wx_sqlite3_stmt in the statement cache. + */ + if( strcmp(zArg, "-last-stmt-ptr")==0 ){ + char zBuf[100]; + wx_sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", + pDb->stmtList ? pDb->stmtList->pStmt: 0); + Tcl_SetResult(interp, zBuf, TCL_VOLATILE); + }else +#endif /* SQLITE_TEST */ + { + Tcl_AppendResult(interp, "unknown argument: ", zArg, (char*)0); + return TCL_ERROR; + } + } + if( i==2 ){ + Tcl_SetResult(interp, (char *)wx_sqlite3_libversion(), TCL_STATIC); + } + break; + } + + + } /* End of the SWITCH statement */ return rc; } -void -wx_sqlite3mc_shutdown(void) -{ - wx_sqlite3mc_vfs_shutdown(); +#if SQLITE_TCL_NRE +/* +** Adaptor that provides an objCmd interface to the NRE-enabled +** interface implementation. +*/ +static int SQLITE_TCLAPI DbObjCmdAdaptor( + void *cd, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const*objv +){ + return Tcl_NRCallObjProc(interp, DbObjCmd, cd, objc, objv); +} +#endif /* SQLITE_TCL_NRE */ + +/* +** Issue the usage message when the "wx_sqlite3" command arguments are +** incorrect. +*/ +static int sqliteCmdUsage( + Tcl_Interp *interp, + Tcl_Obj *const*objv +){ + Tcl_WrongNumArgs(interp, 1, objv, + "HANDLE ?FILENAME? ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?" + " ?-nofollow BOOLEAN?" + " ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?" + ); + return TCL_ERROR; +} + +/* +** wx_sqlite3 DBNAME FILENAME ?-vfs VFSNAME? ?-key KEY? ?-readonly BOOLEAN? +** ?-create BOOLEAN? ?-nomutex BOOLEAN? +** ?-nofollow BOOLEAN? +** +** This is the main Tcl command. When the "sqlite" Tcl command is +** invoked, this routine runs to process that command. +** +** The first argument, DBNAME, is an arbitrary name for a new +** database connection. This command creates a new command named +** DBNAME that is used to control that connection. The database +** connection is deleted when the DBNAME command is deleted. +** +** The second argument is the name of the database file. +** +*/ +static int SQLITE_TCLAPI DbMain( + void *cd, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const*objv +){ + SqliteDb *p; + const char *zArg; + char *zErrMsg; + int i; + const char *zFile = 0; + const char *zVfs = 0; + int flags; + int bTranslateFileName = 1; + Tcl_DString translatedFilename; + int rc; + + /* In normal use, each TCL interpreter runs in a single thread. So + ** by default, we can turn off mutexing on SQLite database connections. + ** However, for testing purposes it is useful to have mutexes turned + ** on. So, by default, mutexes default off. But if compiled with + ** SQLITE_TCL_DEFAULT_FULLMUTEX then mutexes default on. + */ +#ifdef SQLITE_TCL_DEFAULT_FULLMUTEX + flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX; +#else + flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; +#endif + + if( objc==1 ) return sqliteCmdUsage(interp, objv); + if( objc==2 ){ + zArg = Tcl_GetStringFromObj(objv[1], 0); + if( strcmp(zArg,"-version")==0 ){ + Tcl_AppendResult(interp,wx_sqlite3_libversion(), (char*)0); + return TCL_OK; + } + if( strcmp(zArg,"-sourceid")==0 ){ + Tcl_AppendResult(interp,wx_sqlite3_sourceid(), (char*)0); + return TCL_OK; + } + if( strcmp(zArg,"-has-codec")==0 ){ + Tcl_AppendResult(interp,"0",(char*)0); + return TCL_OK; + } + if( zArg[0]=='-' ) return sqliteCmdUsage(interp, objv); + } + for(i=2; idb, flags, zVfs); + if( bTranslateFileName ){ + Tcl_DStringFree(&translatedFilename); + } + if( p->db ){ + if( SQLITE_OK!=wx_sqlite3_errcode(p->db) ){ + zErrMsg = wx_sqlite3_mprintf("%s", wx_sqlite3_errmsg(p->db)); + wx_sqlite3_close(p->db); + p->db = 0; + } + }else{ + zErrMsg = wx_sqlite3_mprintf("%s", wx_sqlite3_errstr(rc)); + } + if( p->db==0 ){ + Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); + Tcl_Free((char*)p); + wx_sqlite3_free(zErrMsg); + return TCL_ERROR; + } + p->maxStmt = NUM_PREPARED_STMTS; + p->openFlags = flags & SQLITE_OPEN_URI; + p->interp = interp; + zArg = Tcl_GetStringFromObj(objv[1], 0); + if( DbUseNre() ){ + Tcl_NRCreateCommand(interp, zArg, DbObjCmdAdaptor, DbObjCmd, + (char*)p, DbDeleteCmd); + }else{ + Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd); + } + p->nRef = 1; + return TCL_OK; } /* -** TCL/TK Shell +** Provide a dummy Tcl_InitStubs if we are using this as a static +** library. +*/ +#ifndef USE_TCL_STUBS +# undef Tcl_InitStubs +# define Tcl_InitStubs(a,b,c) TCL_VERSION +#endif + +/* +** Make sure we have a PACKAGE_VERSION macro defined. This will be +** defined automatically by the TEA makefile. But other makefiles +** do not define it. +*/ +#ifndef PACKAGE_VERSION +# define PACKAGE_VERSION SQLITE_VERSION +#endif + +/* +** Initialize this module. +** +** This Tcl module contains only a single new Tcl command named "sqlite". +** (Hence there is no namespace. There is no point in using a namespace +** if the extension only supplies one new name!) The "sqlite" command is +** used to open a new SQLite database. See the DbMain() routine above +** for additional information. +** +** The EXTERN macros are required by TCL in order to work on windows. */ -#ifdef TCLSH -#define BUILD_tcl -#include "tclsqlite.c" +EXTERN int Sqlite3_Init(Tcl_Interp *interp){ + int rc = Tcl_InitStubs(interp, "8.4", 0) ? TCL_OK : TCL_ERROR; + if( rc==TCL_OK ){ + Tcl_CreateObjCommand(interp, "wx_sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0); +#ifndef SQLITE_3_SUFFIX_ONLY + /* The "sqlite" alias is undocumented. It is here only to support + ** legacy scripts. All new scripts should use only the "wx_sqlite3" + ** command. */ + Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0); +#endif + rc = Tcl_PkgProvide(interp, "wx_sqlite3", PACKAGE_VERSION); + } + return rc; +} +EXTERN int Tclwx_sqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } +EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } +EXTERN int Tclwx_sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } + +/* Because it accesses the file-system and uses persistent state, SQLite +** is not considered appropriate for safe interpreters. Hence, we cause +** the _SafeInit() interfaces return TCL_ERROR. +*/ +EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_ERROR; } +EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){return TCL_ERROR;} + + + +#ifndef SQLITE_3_SUFFIX_ONLY +int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } +int Tclsqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } +int Sqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } +int Tclsqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } +#endif + +/* +** If the TCLSH macro is defined, add code to make a stand-alone program. +*/ +#if defined(TCLSH) + +/* This is the main routine for an ordinary TCL shell. If there are +** are arguments, run the first argument as a script. Otherwise, +** read TCL commands from standard input +*/ +static const char *tclsh_main_loop(void){ + static const char zMainloop[] = + "if {[llength $argv]>=1} {\n" + "set argv0 [lindex $argv 0]\n" + "set argv [lrange $argv 1 end]\n" + "source $argv0\n" + "} else {\n" + "set line {}\n" + "while {![eof stdin]} {\n" + "if {$line!=\"\"} {\n" + "puts -nonewline \"> \"\n" + "} else {\n" + "puts -nonewline \"% \"\n" + "}\n" + "flush stdout\n" + "append line [gets stdin]\n" + "if {[info complete $line]} {\n" + "if {[catch {uplevel #0 $line} result]} {\n" + "puts stderr \"Error: $result\"\n" + "} elseif {$result!=\"\"} {\n" + "puts $result\n" + "}\n" + "set line {}\n" + "} else {\n" + "append line \\n\n" + "}\n" + "}\n" + "}\n" + ; + return zMainloop; +} + +#ifndef TCLSH_MAIN +# define TCLSH_MAIN main +#endif +int SQLITE_CDECL TCLSH_MAIN(int argc, char **argv){ + Tcl_Interp *interp; + int i; + const char *zScript = 0; + char zArgc[32]; +#if defined(TCLSH_INIT_PROC) + extern const char *TCLSH_INIT_PROC(Tcl_Interp*); +#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) + DebugBreak(); +#elif defined(SIGTRAP) + raise(SIGTRAP); +#endif + } + } +#endif + + /* Call wx_sqlite3_shutdown() once before doing anything else. This is to + ** test that wx_sqlite3_shutdown() can be safely called by a process before + ** wx_sqlite3_initialize() is. */ + wx_sqlite3_shutdown(); + + Tcl_FindExecutable(argv[0]); + Tcl_SetSystemEncoding(NULL, "utf-8"); + interp = Tcl_CreateInterp(); + Sqlite3_Init(interp); + + wx_sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-1); + Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp,"argv0",argv[0],TCL_GLOBAL_ONLY); + Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY); + for(i=1; i [SQLITE_LOCK_PENDING], or **
  • [SQLITE_LOCK_EXCLUSIVE]. ** -** xLock() increases the lock. xUnlock() decreases the lock. +** xLock() upgrades the database file lock. In other words, xLock() moves the +** database file lock in the direction NONE toward EXCLUSIVE. The argument to +** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never +** SQLITE_LOCK_NONE. If the database file lock is already at or above the +** requested lock, then the call to xLock() is a no-op. +** xUnlock() downgrades the database file lock to either SHARED or NONE. +* If the lock is already at or below the requested lock state, then the call +** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, ** PENDING, or EXCLUSIVE lock on the file. It returns true @@ -835,9 +917,8 @@ struct wx_sqlite3_io_methods { ** opcode causes the xFileControl method to write the current state of ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) -** into an integer that the pArg argument points to. This capability -** is used during testing and is only available when the SQLITE_TEST -** compile-time option is used. +** into an integer that the pArg argument points to. +** This capability is only available if SQLite is compiled with [SQLITE_DEBUG]. ** **
  • [[SQLITE_FCNTL_SIZE_HINT]] ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS @@ -1141,6 +1222,28 @@ struct wx_sqlite3_io_methods { ** in wal mode after the client has finished copying pages from the wal ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. +** +**
  • [[SQLITE_FCNTL_EXTERNAL_READER]] +** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect +** whether or not there is a database client in another process with a wal-mode +** transaction open on the database or not. It is only available on unix.The +** (void*) argument passed with this file-control should be a pointer to a +** value of type (int). The integer value is set to 1 if the database is a wal +** mode database and there exists at least one client in another process that +** currently has an SQL transaction open on the database. It is set to 0 if +** the database is not a wal-mode db, or if there is no such connection in any +** other process. This opcode cannot be used to detect transactions opened +** by clients within the current process, only within other processes. +** +**
  • [[SQLITE_FCNTL_CKSM_FILE]] +** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use interally by the +** [checksum VFS shim] only. +** +**
  • [[SQLITE_FCNTL_RESET_CACHE]] +** If there is currently no transaction open on the database, and the +** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control +** purges the contents of the in-memory page cache. If there is an open +** transaction, or if the db is a temp-db, this opcode is a no-op, not an error. ** */ #define SQLITE_FCNTL_LOCKSTATE 1 @@ -1181,6 +1284,9 @@ struct wx_sqlite3_io_methods { #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 +#define SQLITE_FCNTL_EXTERNAL_READER 40 +#define SQLITE_FCNTL_CKSM_FILE 41 +#define SQLITE_FCNTL_RESET_CACHE 42 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -1210,6 +1316,26 @@ typedef struct wx_sqlite3_mutex wx_sqlite3_mutex; */ typedef struct wx_sqlite3_api_routines wx_sqlite3_api_routines; +/* +** CAPI3REF: File Name +** +** Type [wx_sqlite3_filename] is used by SQLite to pass filenames to the +** xOpen method of a [VFS]. It may be cast to (const char*) and treated +** as a normal, nul-terminated, UTF-8 buffer containing the filename, but +** may also be passed to special APIs such as: +** +**
      +**
    • wx_sqlite3_filename_database() +**
    • wx_sqlite3_filename_journal() +**
    • wx_sqlite3_filename_wal() +**
    • wx_sqlite3_uri_parameter() +**
    • wx_sqlite3_uri_boolean() +**
    • wx_sqlite3_uri_int64() +**
    • wx_sqlite3_uri_key() +**
    +*/ +typedef const char *wx_sqlite3_filename; + /* ** CAPI3REF: OS Interface Object ** @@ -1388,7 +1514,7 @@ struct wx_sqlite3_vfs { wx_sqlite3_vfs *pNext; /* Next registered VFS */ const char *zName; /* Name of this virtual file system */ void *pAppData; /* Pointer to application-specific data */ - int (*xOpen)(wx_sqlite3_vfs*, const char *zName, wx_sqlite3_file*, + int (*xOpen)(wx_sqlite3_vfs*, wx_sqlite3_filename zName, wx_sqlite3_file*, int flags, int *pOutFlags); int (*xDelete)(wx_sqlite3_vfs*, const char *zName, int syncDir); int (*xAccess)(wx_sqlite3_vfs*, const char *zName, int flags, int *pResOut); @@ -2104,7 +2230,7 @@ struct wx_sqlite3_mem_methods { ** configuration for a database connection can only be changed when that ** connection is not currently using lookaside memory, or in other words ** when the "current value" returned by -** [wx_sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. +** [wx_sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero. ** Any attempt to change the lookaside memory configuration when lookaside ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^
  • @@ -2254,8 +2380,12 @@ struct wx_sqlite3_mem_methods { **
  • wx_sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); ** ** Because resetting a database is destructive and irreversible, the -** process requires the use of this obscure API and multiple steps to help -** ensure that it does not happen by accident. +** process requires the use of this obscure API and multiple steps to +** help ensure that it does not happen by accident. Because this +** feature must be capable of resetting corrupt databases, and +** shutting down virtual tables may require access to that corrupt +** storage, the library must abandon any installed virtual tables +** without calling their xDestroy() methods. ** ** [[SQLITE_DBCONFIG_DEFENSIVE]]
    SQLITE_DBCONFIG_DEFENSIVE
    **
    The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the @@ -2266,6 +2396,7 @@ struct wx_sqlite3_mem_methods { **
      **
    • The [PRAGMA writable_schema=ON] statement. **
    • The [PRAGMA journal_mode=OFF] statement. +**
    • The [PRAGMA schema_version=N] statement. **
    • Writes to the [sqlite_dbpage] virtual table. **
    • Direct writes to [shadow tables]. **
    @@ -2459,11 +2590,14 @@ SQLITE_API void wx_sqlite3_set_last_insert_rowid(wx_sqlite3*,wx_sqlite3_int64); ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: wx_sqlite3 ** -** ^This function returns the number of rows modified, inserted or +** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. -** ^Executing any other type of SQL statement does not modify the value -** returned by this function. +** The two functions are identical except for the type of the return value +** and that if the number of rows modified by the most recent INSERT, UPDATE +** or DELETE is greater than the maximum value supported by type "int", then +** the return value of wx_sqlite3_changes() is undefined. ^Executing any other +** type of SQL statement does not modify the value returned by these functions. ** ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], @@ -2512,16 +2646,21 @@ SQLITE_API void wx_sqlite3_set_last_insert_rowid(wx_sqlite3*,wx_sqlite3_int64); ** */ SQLITE_API int wx_sqlite3_changes(wx_sqlite3*); +SQLITE_API wx_sqlite3_int64 wx_sqlite3_changes64(wx_sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified ** METHOD: wx_sqlite3 ** -** ^This function returns the total number of rows inserted, modified or +** ^These functions return the total number of rows inserted, modified or ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed ** since the database connection was opened, including those executed as -** part of trigger programs. ^Executing any other type of SQL statement -** does not affect the value returned by wx_sqlite3_total_changes(). +** part of trigger programs. The two functions are identical except for the +** type of the return value and that if the number of rows modified by the +** connection exceeds the maximum value supported by type "int", then +** the return value of wx_sqlite3_total_changes() is undefined. ^Executing +** any other type of SQL statement does not affect the value returned by +** wx_sqlite3_total_changes(). ** ** ^Changes made as part of [foreign key actions] are included in the ** count, but those made as part of REPLACE constraint resolution are @@ -2549,6 +2688,7 @@ SQLITE_API int wx_sqlite3_changes(wx_sqlite3*); ** */ SQLITE_API int wx_sqlite3_total_changes(wx_sqlite3*); +SQLITE_API wx_sqlite3_int64 wx_sqlite3_total_changes64(wx_sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query @@ -2584,8 +2724,12 @@ SQLITE_API int wx_sqlite3_total_changes(wx_sqlite3*); ** ^A call to wx_sqlite3_interrupt(D) that occurs when there are no running ** SQL statements is a no-op and has no effect on SQL statements ** that are started after the wx_sqlite3_interrupt() call returns. +** +** ^The [wx_sqlite3_is_interrupted(D)] interface can be used to determine whether +** or not an interrupt is currently in effect for [database connection] D. */ SQLITE_API void wx_sqlite3_interrupt(wx_sqlite3*); +SQLITE_API int wx_sqlite3_is_interrupted(wx_sqlite3*); /* ** CAPI3REF: Determine If An SQL Statement Is Complete @@ -3203,8 +3347,8 @@ SQLITE_API SQLITE_DEPRECATED void *wx_sqlite3_profile(wx_sqlite3*, **
    ^An SQLITE_TRACE_PROFILE callback provides approximately the same ** information as is provided by the [wx_sqlite3_profile()] callback. ** ^The P argument is a pointer to the [prepared statement] and the -** X argument points to a 64-bit integer which is the estimated of -** the number of nanosecond that the prepared statement took to run. +** X argument points to a 64-bit integer which is approximately +** the number of nanoseconds that the prepared statement took to run. ** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes. ** ** [[SQLITE_TRACE_ROW]]
    SQLITE_TRACE_ROW
    @@ -3267,7 +3411,7 @@ SQLITE_API int wx_sqlite3_trace_v2( ** ** ^The wx_sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to -** [wx_sqlite3_exec()], [wx_sqlite3_step()] and [wx_sqlite3_get_table()] for +** [wx_sqlite3_step()] and [wx_sqlite3_prepare()] and similar for ** database connection D. An example use for this ** interface is to keep a GUI updated during a large query. ** @@ -3292,6 +3436,13 @@ SQLITE_API int wx_sqlite3_trace_v2( ** Note that [wx_sqlite3_prepare_v2()] and [wx_sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** +** The progress handler callback would originally only be invoked from the +** bytecode engine. It still might be invoked during [wx_sqlite3_prepare()] +** and similar because those routines might force a reparse of the schema +** which involves running the bytecode engine. However, beginning with +** SQLite version 3.41.0, the progress handler callback might also be +** invoked directly from [wx_sqlite3_prepare()] while analyzing and generating +** code for complex queries. */ SQLITE_API void wx_sqlite3_progress_handler(wx_sqlite3*, int, int(*)(void*), void*); @@ -3328,13 +3479,18 @@ SQLITE_API void wx_sqlite3_progress_handler(wx_sqlite3*, int, int(*)(void*), voi ** **
    ** ^(
    [SQLITE_OPEN_READONLY]
    -**
    The database is opened in read-only mode. If the database does not -** already exist, an error is returned.
    )^ +**
    The database is opened in read-only mode. If the database does +** not already exist, an error is returned.
    )^ ** ** ^(
    [SQLITE_OPEN_READWRITE]
    -**
    The database is opened for reading and writing if possible, or reading -** only if the file is write protected by the operating system. In either -** case the database must already exist, otherwise an error is returned.
    )^ +**
    The database is opened for reading and writing if possible, or +** reading only if the file is write protected by the operating +** system. In either case the database must already exist, otherwise +** an error is returned. For historical reasons, if opening in +** read-write mode fails due to OS-level permissions, an attempt is +** made to open it in read-only mode. [wx_sqlite3_db_readonly()] can be +** used to determine whether the database is actually +** read-write.
    )^ ** ** ^(
    [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
    **
    The database is opened for reading and writing, and is created if @@ -3372,20 +3528,39 @@ SQLITE_API void wx_sqlite3_progress_handler(wx_sqlite3*, int, int(*)(void*), voi **
    The database is opened [shared cache] enabled, overriding ** the default shared cache setting provided by ** [wx_sqlite3_enable_shared_cache()].)^ +** The [use of shared cache mode is discouraged] and hence shared cache +** capabilities may be omitted from many builds of SQLite. In such cases, +** this option is a no-op. ** ** ^(
    [SQLITE_OPEN_PRIVATECACHE]
    **
    The database is opened [shared cache] disabled, overriding ** the default shared cache setting provided by ** [wx_sqlite3_enable_shared_cache()].)^ ** +** [[OPEN_EXRESCODE]] ^(
    [SQLITE_OPEN_EXRESCODE]
    +**
    The database connection comes up in "extended result code mode". +** In other words, the database behaves has if +** [wx_sqlite3_extended_result_codes(db,1)] where called on the database +** connection as soon as the connection is created. In addition to setting +** the extended result code mode, this flag also causes [wx_sqlite3_open_v2()] +** to return an extended result code.
    +** ** [[OPEN_NOFOLLOW]] ^(
    [SQLITE_OPEN_NOFOLLOW]
    -**
    The database filename is not allowed to be a symbolic link
    +**
    The database filename is not allowed to contain a symbolic link
    **
    )^ ** ** If the 3rd parameter to wx_sqlite3_open_v2() is not one of the ** required combinations shown above optionally combined with other ** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] -** then the behavior is undefined. +** then the behavior is undefined. Historic versions of SQLite +** have silently ignored surplus bits in the flags parameter to +** wx_sqlite3_open_v2(), however that behavior might not be carried through +** into future versions of SQLite and so applications should not rely +** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op +** for wx_sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause +** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE +** flag is intended for use by the [wx_sqlite3_vfs|VFS interface] only, and not +** by wx_sqlite3_open_v2(). ** ** ^The fourth parameter to wx_sqlite3_open_v2() is the name of the ** [wx_sqlite3_vfs] object that defines the operating system interface that @@ -3630,10 +3805,10 @@ SQLITE_API int wx_sqlite3_open_v2( ** ** See the [URI filename] documentation for additional information. */ -SQLITE_API const char *wx_sqlite3_uri_parameter(const char *zFilename, const char *zParam); -SQLITE_API int wx_sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); -SQLITE_API wx_sqlite3_int64 wx_sqlite3_uri_int64(const char*, const char*, wx_sqlite3_int64); -SQLITE_API const char *wx_sqlite3_uri_key(const char *zFilename, int N); +SQLITE_API const char *wx_sqlite3_uri_parameter(wx_sqlite3_filename z, const char *zParam); +SQLITE_API int wx_sqlite3_uri_boolean(wx_sqlite3_filename z, const char *zParam, int bDefault); +SQLITE_API wx_sqlite3_int64 wx_sqlite3_uri_int64(wx_sqlite3_filename, const char*, wx_sqlite3_int64); +SQLITE_API const char *wx_sqlite3_uri_key(wx_sqlite3_filename z, int N); /* ** CAPI3REF: Translate filenames @@ -3662,9 +3837,9 @@ SQLITE_API const char *wx_sqlite3_uri_key(const char *zFilename, int N); ** return value from [wx_sqlite3_db_filename()], then the result is ** undefined and is likely a memory access violation. */ -SQLITE_API const char *wx_sqlite3_filename_database(const char*); -SQLITE_API const char *wx_sqlite3_filename_journal(const char*); -SQLITE_API const char *wx_sqlite3_filename_wal(const char*); +SQLITE_API const char *wx_sqlite3_filename_database(wx_sqlite3_filename); +SQLITE_API const char *wx_sqlite3_filename_journal(wx_sqlite3_filename); +SQLITE_API const char *wx_sqlite3_filename_wal(wx_sqlite3_filename); /* ** CAPI3REF: Database File Corresponding To A Journal @@ -3730,14 +3905,14 @@ SQLITE_API wx_sqlite3_file *wx_sqlite3_database_file_object(const char*); ** then the corresponding [wx_sqlite3_module.xClose() method should also be ** invoked prior to calling wx_sqlite3_free_filename(Y). */ -SQLITE_API char *wx_sqlite3_create_filename( +SQLITE_API wx_sqlite3_filename wx_sqlite3_create_filename( const char *zDatabase, const char *zJournal, const char *zWal, int nParam, const char **azParam ); -SQLITE_API void wx_sqlite3_free_filename(char*); +SQLITE_API void wx_sqlite3_free_filename(wx_sqlite3_filename); /* ** CAPI3REF: Error Codes And Messages @@ -3756,13 +3931,14 @@ SQLITE_API void wx_sqlite3_free_filename(char*); ** wx_sqlite3_extended_errcode() might change with each API call. ** Except, there are some interfaces that are guaranteed to never ** change the value of the error code. The error-code preserving -** interfaces are: +** interfaces include the following: ** **
      **
    • wx_sqlite3_errcode() **
    • wx_sqlite3_extended_errcode() **
    • wx_sqlite3_errmsg() **
    • wx_sqlite3_errmsg16() +**
    • wx_sqlite3_error_offset() **
    ** ** ^The wx_sqlite3_errmsg() and wx_sqlite3_errmsg16() return English-language @@ -3777,6 +3953,13 @@ SQLITE_API void wx_sqlite3_free_filename(char*); ** ^(Memory to hold the error message string is managed internally ** and must not be freed by the application)^. ** +** ^If the most recent error references a specific token in the input +** SQL, the wx_sqlite3_error_offset() interface returns the byte offset +** of the start of that token. ^The byte offset returned by +** wx_sqlite3_error_offset() assumes that the input SQL is UTF8. +** ^If the most recent error does not reference a specific token in the input +** SQL, then the wx_sqlite3_error_offset() function returns -1. +** ** When the serialized [threading mode] is in use, it might be the ** case that a second error occurs on a separate thread in between ** the time of the first error and the call to these interfaces. @@ -3796,6 +3979,7 @@ SQLITE_API int wx_sqlite3_extended_errcode(wx_sqlite3 *db); SQLITE_API const char *wx_sqlite3_errmsg(wx_sqlite3*); SQLITE_API const void *wx_sqlite3_errmsg16(wx_sqlite3*); SQLITE_API const char *wx_sqlite3_errstr(int); +SQLITE_API int wx_sqlite3_error_offset(wx_sqlite3 *db); /* ** CAPI3REF: Prepared Statement Object @@ -4153,12 +4337,17 @@ SQLITE_API int wx_sqlite3_prepare16_v3( ** are managed by SQLite and are automatically freed when the prepared ** statement is finalized. ** ^The string returned by wx_sqlite3_expanded_sql(P), on the other hand, -** is obtained from [wx_sqlite3_malloc()] and must be free by the application +** is obtained from [wx_sqlite3_malloc()] and must be freed by the application ** by passing it to [wx_sqlite3_free()]. +** +** ^The wx_sqlite3_normalized_sql() interface is only available if +** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined. */ SQLITE_API const char *wx_sqlite3_sql(wx_sqlite3_stmt *pStmt); SQLITE_API char *wx_sqlite3_expanded_sql(wx_sqlite3_stmt *pStmt); +#ifdef SQLITE_ENABLE_NORMALIZE SQLITE_API const char *wx_sqlite3_normalized_sql(wx_sqlite3_stmt *pStmt); +#endif /* ** CAPI3REF: Determine If An SQL Statement Writes The Database @@ -4193,6 +4382,19 @@ SQLITE_API const char *wx_sqlite3_normalized_sql(wx_sqlite3_stmt *pStmt); ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so ** wx_sqlite3_stmt_readonly() returns false for those commands. +** +** ^This routine returns false if there is any possibility that the +** statement might change the database file. ^A false return does +** not guarantee that the statement will change the database file. +** ^For example, an UPDATE statement might have a WHERE clause that +** makes it a no-op, but the wx_sqlite3_stmt_readonly() result would still +** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a +** read-only no-op if the table already exists, but +** wx_sqlite3_stmt_readonly() still returns false for such a statement. +** +** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN] +** statement, then wx_sqlite3_stmt_readonly(X) returns the same value as +** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted. */ SQLITE_API int wx_sqlite3_stmt_readonly(wx_sqlite3_stmt *pStmt); @@ -4261,6 +4463,8 @@ SQLITE_API int wx_sqlite3_stmt_busy(wx_sqlite3_stmt*); ** ** ^The wx_sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. +** ^The wx_sqlite3_value objects returned by [wx_sqlite3_vtab_rhs_value()] +** are protected. ** ^The wx_sqlite3_value object returned by ** [wx_sqlite3_column_value()] is unprotected. ** Unprotected wx_sqlite3_value objects may only be used as arguments @@ -4362,18 +4566,22 @@ typedef struct wx_sqlite3_context wx_sqlite3_context; ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. ** -** ^The fifth argument to the BLOB and string binding interfaces -** is a destructor used to dispose of the BLOB or -** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to the bind API fails, -** except the destructor is not called if the third parameter is a NULL -** pointer or the fourth parameter is negative. -** ^If the fifth argument is -** the special value [SQLITE_STATIC], then SQLite assumes that the -** information is in static, unmanaged space and does not need to be freed. -** ^If the fifth argument has the value [SQLITE_TRANSIENT], then -** SQLite makes its own private copy of the data immediately, before -** the wx_sqlite3_bind_*() routine returns. +** ^The fifth argument to the BLOB and string binding interfaces controls +** or indicates the lifetime of the object referenced by the third parameter. +** These three options exist: +** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished +** with it may be passed. ^It is called to dispose of the BLOB or string even +** if the call to the bind API fails, except the destructor is not called if +** the third parameter is a NULL pointer or the fourth parameter is negative. +** ^ (2) The special constant, [SQLITE_STATIC], may be passsed to indicate that +** the application remains responsible for disposing of the object. ^In this +** case, the object and the provided pointer to it must remain valid until +** either the prepared statement is finalized or the same SQL parameter is +** bound to something else, whichever occurs sooner. +** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the +** object is to be copied prior to the return from wx_sqlite3_bind_*(). ^The +** object and pointer to it must remain valid until then. ^SQLite will then +** manage the lifetime of its private copy. ** ** ^The sixth argument to wx_sqlite3_bind_text64() must be one of ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] @@ -4878,6 +5086,10 @@ SQLITE_API int wx_sqlite3_data_count(wx_sqlite3_stmt *pStmt); ** even empty strings, are always zero-terminated. ^The return ** value from wx_sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. ** +** ^Strings returned by wx_sqlite3_column_text16() always have the endianness +** which is native to the platform, regardless of the text encoding set +** for the database. +** ** Warning: ^The object returned by [wx_sqlite3_column_value()] is an ** [unprotected wx_sqlite3_value] object. In a multithreaded environment, ** an unprotected wx_sqlite3_value object may only be used safely with @@ -4891,7 +5103,7 @@ SQLITE_API int wx_sqlite3_data_count(wx_sqlite3_stmt *pStmt); ** [application-defined SQL functions] or [virtual tables], not within ** top-level application code. ** -** The these routines may attempt to convert the datatype of the result. +** These routines may attempt to convert the datatype of the result. ** ^For example, if the internal representation is FLOAT and a text result ** is requested, [wx_sqlite3_snprintf()] is used internally to perform the ** conversion automatically. ^(The following table details the conversions @@ -4916,7 +5128,7 @@ SQLITE_API int wx_sqlite3_data_count(wx_sqlite3_stmt *pStmt); ** TEXT BLOB No change ** BLOB INTEGER [CAST] to INTEGER ** BLOB FLOAT [CAST] to REAL -** BLOB TEXT Add a zero terminator if needed +** BLOB TEXT [CAST] to TEXT, ensure zero terminator ** ** )^ ** @@ -5115,7 +5327,6 @@ SQLITE_API int wx_sqlite3_reset(wx_sqlite3_stmt *pStmt); ** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions, ** index expressions, or the WHERE clause of partial indexes. ** -** ** For best security, the [SQLITE_DIRECTONLY] flag is recommended for ** all application-defined SQL functions that do not need to be ** used inside of triggers, view, CHECK constraints, or other elements of @@ -5125,7 +5336,6 @@ SQLITE_API int wx_sqlite3_reset(wx_sqlite3_stmt *pStmt); ** a database file to include invocations of the function with parameters ** chosen by the attacker, which the application will then execute when ** the database file is opened and read. -** ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [wx_sqlite3_user_data()].)^ @@ -5261,10 +5471,21 @@ SQLITE_API int wx_sqlite3_create_window_function( ** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in ** schema structures such as [CHECK constraints], [DEFAULT clauses], ** [expression indexes], [partial indexes], or [generated columns]. -** The SQLITE_DIRECTONLY flags is a security feature which is recommended -** for all [application-defined SQL functions], and especially for functions -** that have side-effects or that could potentially leak sensitive -** information. +**

    +** The SQLITE_DIRECTONLY flag is recommended for any +** [application-defined SQL function] +** that has side-effects or that could potentially leak sensitive information. +** This will prevent attacks in which an application is tricked +** into using a database file that has had its schema surreptiously +** modified to invoke the application-defined function in ways that are +** harmful. +**

    +** Some people say it is good practice to set SQLITE_DIRECTONLY on all +** [application-defined SQL functions], regardless of whether or not they +** are security sensitive, as doing so prevents those functions from being used +** inside of the database schema, and thus ensures that the database +** can be inspected and modified using generic tools (such as the [CLI]) +** that do not have access to the application-defined functions. **

  • ** ** [[SQLITE_INNOCUOUS]]
    SQLITE_INNOCUOUS
    @@ -5470,6 +5691,28 @@ SQLITE_API int wx_sqlite3_value_numeric_type(wx_sqlite3_value*); SQLITE_API int wx_sqlite3_value_nochange(wx_sqlite3_value*); SQLITE_API int wx_sqlite3_value_frombind(wx_sqlite3_value*); +/* +** CAPI3REF: Report the internal text encoding state of an wx_sqlite3_value object +** METHOD: wx_sqlite3_value +** +** ^(The wx_sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8], +** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding +** of the value X, assuming that X has type TEXT.)^ If wx_sqlite3_value_type(X) +** returns something other than SQLITE_TEXT, then the return value from +** wx_sqlite3_value_encoding(X) is meaningless. ^Calls to +** [wx_sqlite3_value_text(X)], [wx_sqlite3_value_text16(X)], [wx_sqlite3_value_text16be(X)], +** [wx_sqlite3_value_text16le(X)], [wx_sqlite3_value_bytes(X)], or +** [wx_sqlite3_value_bytes16(X)] might change the encoding of the value X and +** thus change the return from subsequent calls to wx_sqlite3_value_encoding(X). +** +** This routine is intended for used by applications that test and validate +** the SQLite implementation. This routine is inquiring about the opaque +** internal state of an [wx_sqlite3_value] object. Ordinary applications should +** not need to know what the internal state of an wx_sqlite3_value object is and +** hence should not need to use this interface. +*/ +SQLITE_API int wx_sqlite3_value_encoding(wx_sqlite3_value*); + /* ** CAPI3REF: Finding The Subtype Of SQL Values ** METHOD: wx_sqlite3_value @@ -5490,7 +5733,8 @@ SQLITE_API unsigned int wx_sqlite3_value_subtype(wx_sqlite3_value*); ** object D and returns a pointer to that copy. ^The [wx_sqlite3_value] returned ** is a [protected wx_sqlite3_value] object even if the input is not. ** ^The wx_sqlite3_value_dup(V) interface returns NULL if V is NULL or if a -** memory allocation fails. +** memory allocation fails. ^If V is a [pointer value], then the result +** of wx_sqlite3_value_dup(V) is a NULL value. ** ** ^The wx_sqlite3_value_free(V) interface frees an [wx_sqlite3_value] object ** previously obtained from [wx_sqlite3_value_dup()]. ^If V is a NULL pointer @@ -5521,7 +5765,7 @@ SQLITE_API void wx_sqlite3_value_free(wx_sqlite3_value*); ** ** ^The wx_sqlite3_aggregate_context(C,N) routine returns a NULL pointer ** when first called if N is less than or equal to zero or if a memory -** allocate error occurs. +** allocation error occurs. ** ** ^(The amount of space allocated by wx_sqlite3_aggregate_context(C,N) is ** determined by the N parameter on first successful call. Changing the @@ -5726,9 +5970,10 @@ typedef void (*wx_sqlite3_destructor_type)(void*); ** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. ** ^SQLite takes the text result from the application from ** the 2nd parameter of the wx_sqlite3_result_text* interfaces. -** ^If the 3rd parameter to the wx_sqlite3_result_text* interfaces -** is negative, then SQLite takes result text from the 2nd parameter -** through the first zero character. +** ^If the 3rd parameter to any of the wx_sqlite3_result_text* interfaces +** other than wx_sqlite3_result_text64() is negative, then SQLite computes +** the string length itself by searching the 2nd parameter for the first +** zero character. ** ^If the 3rd parameter to the wx_sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text ** pointed to by the 2nd parameter are taken as the application-defined @@ -6172,6 +6417,28 @@ SQLITE_API int wx_sqlite3_get_autocommit(wx_sqlite3*); */ SQLITE_API wx_sqlite3 *wx_sqlite3_db_handle(wx_sqlite3_stmt*); +/* +** CAPI3REF: Return The Schema Name For A Database Connection +** METHOD: wx_sqlite3 +** +** ^The wx_sqlite3_db_name(D,N) interface returns a pointer to the schema name +** for the N-th database on database connection D, or a NULL pointer of N is +** out of range. An N value of 0 means the main database file. An N of 1 is +** the "temp" schema. Larger values of N correspond to various ATTACH-ed +** databases. +** +** Space to hold the string that is returned by wx_sqlite3_db_name() is managed +** by SQLite itself. The string might be deallocated by any operation that +** changes the schema, including [ATTACH] or [DETACH] or calls to +** [wx_sqlite3_serialize()] or [wx_sqlite3_deserialize()], even operations that +** occur on a different thread. Applications that need to +** remember the string long-term should make their own copy. Applications that +** are accessing the same database connection simultaneously on multiple +** threads should mutex-protect calls to this API and should make their own +** private copy of the result prior to releasing the mutex. +*/ +SQLITE_API const char *wx_sqlite3_db_name(wx_sqlite3 *db, int N); + /* ** CAPI3REF: Return The Filename For A Database Connection ** METHOD: wx_sqlite3 @@ -6202,7 +6469,7 @@ SQLITE_API wx_sqlite3 *wx_sqlite3_db_handle(wx_sqlite3_stmt*); **
  • [wx_sqlite3_filename_wal()] ** */ -SQLITE_API const char *wx_sqlite3_db_filename(wx_sqlite3 *db, const char *zDbName); +SQLITE_API wx_sqlite3_filename wx_sqlite3_db_filename(wx_sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Determine if a database is read-only @@ -6331,6 +6598,72 @@ SQLITE_API wx_sqlite3_stmt *wx_sqlite3_next_stmt(wx_sqlite3 *pDb, wx_sqlite3_stm SQLITE_API void *wx_sqlite3_commit_hook(wx_sqlite3*, int(*)(void*), void*); SQLITE_API void *wx_sqlite3_rollback_hook(wx_sqlite3*, void(*)(void *), void*); +/* +** CAPI3REF: Autovacuum Compaction Amount Callback +** METHOD: wx_sqlite3 +** +** ^The wx_sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback +** function C that is invoked prior to each autovacuum of the database +** file. ^The callback is passed a copy of the generic data pointer (P), +** the schema-name of the attached database that is being autovacuumed, +** the size of the database file in pages, the number of free pages, +** and the number of bytes per page, respectively. The callback should +** return the number of free pages that should be removed by the +** autovacuum. ^If the callback returns zero, then no autovacuum happens. +** ^If the value returned is greater than or equal to the number of +** free pages, then a complete autovacuum happens. +** +**

    ^If there are multiple ATTACH-ed database files that are being +** modified as part of a transaction commit, then the autovacuum pages +** callback is invoked separately for each file. +** +**

    The callback is not reentrant. The callback function should +** not attempt to invoke any other SQLite interface. If it does, bad +** things may happen, including segmentation faults and corrupt database +** files. The callback function should be a simple function that +** does some arithmetic on its input parameters and returns a result. +** +** ^The X parameter to wx_sqlite3_autovacuum_pages(D,C,P,X) is an optional +** destructor for the P parameter. ^If X is not NULL, then X(P) is +** invoked whenever the database connection closes or when the callback +** is overwritten by another invocation of wx_sqlite3_autovacuum_pages(). +** +**

    ^There is only one autovacuum pages callback per database connection. +** ^Each call to the wx_sqlite3_autovacuum_pages() interface overrides all +** previous invocations for that database connection. ^If the callback +** argument (C) to wx_sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, +** then the autovacuum steps callback is cancelled. The return value +** from wx_sqlite3_autovacuum_pages() is normally SQLITE_OK, but might +** be some other error code if something goes wrong. The current +** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other +** return codes might be added in future releases. +** +**

    If no autovacuum pages callback is specified (the usual case) or +** a NULL pointer is provided for the callback, +** then the default behavior is to vacuum all free pages. So, in other +** words, the default behavior is the same as if the callback function +** were something like this: +** +**

    +**     unsigned int demonstration_autovac_pages_callback(
    +**       void *pClientData,
    +**       const char *zSchema,
    +**       unsigned int nDbPage,
    +**       unsigned int nFreePage,
    +**       unsigned int nBytePerPage
    +**     ){
    +**       return nFreePage;
    +**     }
    +** 
    +*/ +SQLITE_API int wx_sqlite3_autovacuum_pages( + wx_sqlite3 *db, + unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), + void*, + void(*)(void*) +); + + /* ** CAPI3REF: Data Change Notification Callbacks ** METHOD: wx_sqlite3 @@ -6394,6 +6727,11 @@ SQLITE_API void *wx_sqlite3_update_hook( ** to the same database. Sharing is enabled if the argument is true ** and disabled if the argument is false.)^ ** +** This interface is omitted if SQLite is compiled with +** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE] +** compile-time option is recommended because the +** [use of shared cache mode is discouraged]. +** ** ^Cache sharing is enabled and disabled for an entire process. ** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]). ** In prior versions of SQLite, @@ -6492,7 +6830,7 @@ SQLITE_API int wx_sqlite3_db_release_memory(wx_sqlite3*); ** ^The soft heap limit may not be greater than the hard heap limit. ** ^If the hard heap limit is enabled and if wx_sqlite3_soft_heap_limit(N) ** is invoked with a value of N that is greater than the hard heap limit, -** the the soft heap limit is set to the value of the hard heap limit. +** the soft heap limit is set to the value of the hard heap limit. ** ^The soft heap limit is automatically enabled whenever the hard heap ** limit is enabled. ^When wx_sqlite3_hard_heap_limit64(N) is invoked and ** the soft heap limit is outside the range of 1..N, then the soft heap @@ -6753,15 +7091,6 @@ SQLITE_API int wx_sqlite3_cancel_auto_extension(void(*xEntryPoint)(void)); */ SQLITE_API void wx_sqlite3_reset_auto_extension(void); -/* -** The interface to the virtual-table mechanism is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - /* ** Structures used by the virtual table interface */ @@ -6880,10 +7209,10 @@ struct wx_sqlite3_module { ** when the omit flag is true there is no guarantee that the constraint will ** not be checked again using byte code.)^ ** -** ^The idxNum and idxPtr values are recorded and passed into the +** ^The idxNum and idxStr values are recorded and passed into the ** [xFilter] method. -** ^[wx_sqlite3_free()] is used to free idxPtr if and only if -** needToFreeIdxPtr is true. +** ^[wx_sqlite3_free()] is used to free idxStr if and only if +** needToFreeIdxStr is true. ** ** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in ** the correct order to satisfy the ORDER BY clause so that no separate @@ -6972,24 +7301,56 @@ struct wx_sqlite3_index_info { ** ** These macros define the allowed values for the ** [wx_sqlite3_index_info].aConstraint[].op field. Each value represents -** an operator that is part of a constraint term in the wHERE clause of +** an operator that is part of a constraint term in the WHERE clause of ** a query that uses a [virtual table]. -*/ -#define SQLITE_INDEX_CONSTRAINT_EQ 2 -#define SQLITE_INDEX_CONSTRAINT_GT 4 -#define SQLITE_INDEX_CONSTRAINT_LE 8 -#define SQLITE_INDEX_CONSTRAINT_LT 16 -#define SQLITE_INDEX_CONSTRAINT_GE 32 -#define SQLITE_INDEX_CONSTRAINT_MATCH 64 -#define SQLITE_INDEX_CONSTRAINT_LIKE 65 -#define SQLITE_INDEX_CONSTRAINT_GLOB 66 -#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 -#define SQLITE_INDEX_CONSTRAINT_NE 68 -#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 -#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 -#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 -#define SQLITE_INDEX_CONSTRAINT_IS 72 -#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 +** +** ^The left-hand operand of the operator is given by the corresponding +** aConstraint[].iColumn field. ^An iColumn of -1 indicates the left-hand +** operand is the rowid. +** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET +** operators have no left-hand operand, and so for those operators the +** corresponding aConstraint[].iColumn is meaningless and should not be +** used. +** +** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through +** value 255 are reserved to represent functions that are overloaded +** by the [xFindFunction|xFindFunction method] of the virtual table +** implementation. +** +** The right-hand operands for each constraint might be accessible using +** the [wx_sqlite3_vtab_rhs_value()] interface. Usually the right-hand +** operand is only available if it appears as a single constant literal +** in the input SQL. If the right-hand operand is another column or an +** expression (even a constant expression) or a parameter, then the +** wx_sqlite3_vtab_rhs_value() probably will not be able to extract it. +** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and +** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand +** and hence calls to wx_sqlite3_vtab_rhs_value() for those operators will +** always return SQLITE_NOTFOUND. +** +** The collating sequence to be used for comparison can be found using +** the [wx_sqlite3_vtab_collation()] interface. For most real-world virtual +** tables, the collating sequence of constraints does not matter (for example +** because the constraints are numeric) and so the wx_sqlite3_vtab_collation() +** interface is not commonly needed. +*/ +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 +#define SQLITE_INDEX_CONSTRAINT_LIKE 65 +#define SQLITE_INDEX_CONSTRAINT_GLOB 66 +#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 +#define SQLITE_INDEX_CONSTRAINT_NE 68 +#define SQLITE_INDEX_CONSTRAINT_ISNOT 69 +#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 +#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 +#define SQLITE_INDEX_CONSTRAINT_IS 72 +#define SQLITE_INDEX_CONSTRAINT_LIMIT 73 +#define SQLITE_INDEX_CONSTRAINT_OFFSET 74 +#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* ** CAPI3REF: Register A Virtual Table Implementation @@ -7018,7 +7379,7 @@ struct wx_sqlite3_index_info { ** destructor. ** ** ^If the third parameter (the pointer to the wx_sqlite3_module object) is -** NULL then no new module is create and any existing modules with the +** NULL then no new module is created and any existing modules with the ** same name are dropped. ** ** See also: [wx_sqlite3_drop_modules()] @@ -7130,16 +7491,6 @@ SQLITE_API int wx_sqlite3_declare_vtab(wx_sqlite3*, const char *zSQL); */ SQLITE_API int wx_sqlite3_overload_function(wx_sqlite3*, const char *zFuncName, int nArg); -/* -** The interface to the virtual-table mechanism defined above (back up -** to a comment remarkably similar to this one) is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - /* ** CAPI3REF: A Handle To An Open BLOB ** KEYWORDS: {BLOB handle} {BLOB handles} @@ -7793,7 +8144,9 @@ SQLITE_API int wx_sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 #define SQLITE_TESTCTRL_SEEK_COUNT 30 #define SQLITE_TESTCTRL_TRACEFLAGS 31 -#define SQLITE_TESTCTRL_LAST 31 /* Largest TESTCTRL */ +#define SQLITE_TESTCTRL_TUNE 32 +#define SQLITE_TESTCTRL_LOGEST 33 +#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking @@ -8316,6 +8669,16 @@ SQLITE_API int wx_sqlite3_stmt_status(wx_sqlite3_stmt*, int op,int resetFlg); ** The counter is incremented on the first [wx_sqlite3_step()] call of each ** cycle. ** +** [[SQLITE_STMTSTATUS_FILTER_MISS]] +** [[SQLITE_STMTSTATUS_FILTER HIT]] +**
    SQLITE_STMTSTATUS_FILTER_HIT
    +** SQLITE_STMTSTATUS_FILTER_MISS
    +**
    ^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join +** step was bypassed because a Bloom filter returned not-found. The +** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of +** times that the Bloom filter returned a find, and thus the join step +** had to be processed as normal. +** ** [[SQLITE_STMTSTATUS_MEMUSED]]
    SQLITE_STMTSTATUS_MEMUSED
    **
    ^This is the approximate number of bytes of heap memory ** used to store the prepared statement. ^This value is not actually @@ -8330,6 +8693,8 @@ SQLITE_API int wx_sqlite3_stmt_status(wx_sqlite3_stmt*, int op,int resetFlg); #define SQLITE_STMTSTATUS_VM_STEP 4 #define SQLITE_STMTSTATUS_REPREPARE 5 #define SQLITE_STMTSTATUS_RUN 6 +#define SQLITE_STMTSTATUS_FILTER_MISS 7 +#define SQLITE_STMTSTATUS_FILTER_HIT 8 #define SQLITE_STMTSTATUS_MEMUSED 99 /* @@ -8741,7 +9106,7 @@ typedef struct wx_sqlite3_backup wx_sqlite3_backup; ** if the application incorrectly accesses the destination [database connection] ** and so no error code is reported, but the operations may malfunction ** nevertheless. Use of the destination database connection while a -** backup is in progress might also also cause a mutex deadlock. +** backup is in progress might also cause a mutex deadlock. ** ** If running in [shared cache mode], the application must ** guarantee that the shared cache used by the destination database @@ -8993,8 +9358,9 @@ SQLITE_API void wx_sqlite3_log(int iErrCode, const char *zFormat, ...); ** ** A single database handle may have at most a single write-ahead log callback ** registered at one time. ^Calling [wx_sqlite3_wal_hook()] replaces any -** previously registered write-ahead log callback. ^Note that the -** [wx_sqlite3_wal_autocheckpoint()] interface and the +** previously registered write-ahead log callback. ^The return value is +** a copy of the third parameter from the previous call, if any, or 0. +** ^Note that the [wx_sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] both invoke [wx_sqlite3_wal_hook()] and will ** overwrite any prior [wx_sqlite3_wal_hook()] settings. */ @@ -9168,7 +9534,7 @@ SQLITE_API int wx_sqlite3_wal_checkpoint_v2( */ #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */ #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */ -#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */ +#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */ #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */ /* @@ -9297,18 +9663,274 @@ SQLITE_API int wx_sqlite3_vtab_nochange(wx_sqlite3_context*); /* ** CAPI3REF: Determine The Collation For a Virtual Table Constraint +** METHOD: wx_sqlite3_index_info ** ** This function may only be called from within a call to the [xBestIndex] -** method of a [virtual table]. +** method of a [virtual table]. This function returns a pointer to a string +** that is the name of the appropriate collation sequence to use for text +** comparisons on the constraint identified by its arguments. +** +** The first argument must be the pointer to the [wx_sqlite3_index_info] object +** that is the first parameter to the xBestIndex() method. The second argument +** must be an index into the aConstraint[] array belonging to the +** wx_sqlite3_index_info structure passed to xBestIndex. ** -** The first argument must be the wx_sqlite3_index_info object that is the -** first parameter to the xBestIndex() method. The second argument must be -** an index into the aConstraint[] array belonging to the wx_sqlite3_index_info -** structure passed to xBestIndex. This function returns a pointer to a buffer -** containing the name of the collation sequence for the corresponding -** constraint. +** Important: +** The first parameter must be the same pointer that is passed into the +** xBestMethod() method. The first parameter may not be a pointer to a +** different [wx_sqlite3_index_info] object, even an exact copy. +** +** The return value is computed as follows: +** +**
      +**
    1. If the constraint comes from a WHERE clause expression that contains +** a [COLLATE operator], then the name of the collation specified by +** that COLLATE operator is returned. +**

    2. If there is no COLLATE operator, but the column that is the subject +** of the constraint specifies an alternative collating sequence via +** a [COLLATE clause] on the column definition within the CREATE TABLE +** statement that was passed into [wx_sqlite3_declare_vtab()], then the +** name of that alternative collating sequence is returned. +**

    3. Otherwise, "BINARY" is returned. +**

    */ -SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_index_info*,int); +SQLITE_API const char *wx_sqlite3_vtab_collation(wx_sqlite3_index_info*,int); + +/* +** CAPI3REF: Determine if a virtual table query is DISTINCT +** METHOD: wx_sqlite3_index_info +** +** This API may only be used from within an [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this +** interface from outside of xBestIndex() is undefined and probably harmful. +** +** ^The wx_sqlite3_vtab_distinct() interface returns an integer between 0 and +** 3. The integer returned by wx_sqlite3_vtab_distinct() +** gives the virtual table additional information about how the query +** planner wants the output to be ordered. As long as the virtual table +** can meet the ordering requirements of the query planner, it may set +** the "orderByConsumed" flag. +** +**
    1. +** ^If the wx_sqlite3_vtab_distinct() interface returns 0, that means +** that the query planner needs the virtual table to return all rows in the +** sort order defined by the "nOrderBy" and "aOrderBy" fields of the +** [wx_sqlite3_index_info] object. This is the default expectation. If the +** virtual table outputs all rows in sorted order, then it is always safe for +** the xBestIndex method to set the "orderByConsumed" flag, regardless of +** the return value from wx_sqlite3_vtab_distinct(). +**

    2. +** ^(If the wx_sqlite3_vtab_distinct() interface returns 1, that means +** that the query planner does not need the rows to be returned in sorted order +** as long as all rows with the same values in all columns identified by the +** "aOrderBy" field are adjacent.)^ This mode is used when the query planner +** is doing a GROUP BY. +**

    3. +** ^(If the wx_sqlite3_vtab_distinct() interface returns 2, that means +** that the query planner does not need the rows returned in any particular +** order, as long as rows with the same values in all "aOrderBy" columns +** are adjacent.)^ ^(Furthermore, only a single row for each particular +** combination of values in the columns identified by the "aOrderBy" field +** needs to be returned.)^ ^It is always ok for two or more rows with the same +** values in all "aOrderBy" columns to be returned, as long as all such rows +** are adjacent. ^The virtual table may, if it chooses, omit extra rows +** that have the same value for all columns identified by "aOrderBy". +** ^However omitting the extra rows is optional. +** This mode is used for a DISTINCT query. +**

    4. +** ^(If the wx_sqlite3_vtab_distinct() interface returns 3, that means +** that the query planner needs only distinct rows but it does need the +** rows to be sorted.)^ ^The virtual table implementation is free to omit +** rows that are identical in all aOrderBy columns, if it wants to, but +** it is not required to omit any rows. This mode is used for queries +** that have both DISTINCT and ORDER BY clauses. +**

    +** +** ^For the purposes of comparing virtual table output values to see if the +** values are same value for sorting purposes, two NULL values are considered +** to be the same. In other words, the comparison operator is "IS" +** (or "IS NOT DISTINCT FROM") and not "==". +** +** If a virtual table implementation is unable to meet the requirements +** specified above, then it must not set the "orderByConsumed" flag in the +** [wx_sqlite3_index_info] object or an incorrect answer may result. +** +** ^A virtual table implementation is always free to return rows in any order +** it wants, as long as the "orderByConsumed" flag is not set. ^When the +** the "orderByConsumed" flag is unset, the query planner will add extra +** [bytecode] to ensure that the final results returned by the SQL query are +** ordered correctly. The use of the "orderByConsumed" flag and the +** wx_sqlite3_vtab_distinct() interface is merely an optimization. ^Careful +** use of the wx_sqlite3_vtab_distinct() interface and the "orderByConsumed" +** flag might help queries against a virtual table to run faster. Being +** overly aggressive and setting the "orderByConsumed" flag when it is not +** valid to do so, on the other hand, might cause SQLite to return incorrect +** results. +*/ +SQLITE_API int wx_sqlite3_vtab_distinct(wx_sqlite3_index_info*); + +/* +** CAPI3REF: Identify and handle IN constraints in xBestIndex +** +** This interface may only be used from within an +** [xBestIndex|xBestIndex() method] of a [virtual table] implementation. +** The result of invoking this interface from any other context is +** undefined and probably harmful. +** +** ^(A constraint on a virtual table of the form +** "[IN operator|column IN (...)]" is +** communicated to the xBestIndex method as a +** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use +** this constraint, it must set the corresponding +** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under +** the usual mode of handling IN operators, SQLite generates [bytecode] +** that invokes the [xFilter|xFilter() method] once for each value +** on the right-hand side of the IN operator.)^ Thus the virtual table +** only sees a single value from the right-hand side of the IN operator +** at a time. +** +** In some cases, however, it would be advantageous for the virtual +** table to see all values on the right-hand of the IN operator all at +** once. The wx_sqlite3_vtab_in() interfaces facilitates this in two ways: +** +**
      +**
    1. +** ^A call to wx_sqlite3_vtab_in(P,N,-1) will return true (non-zero) +** if and only if the [wx_sqlite3_index_info|P->aConstraint][N] constraint +** is an [IN operator] that can be processed all at once. ^In other words, +** wx_sqlite3_vtab_in() with -1 in the third argument is a mechanism +** by which the virtual table can ask SQLite if all-at-once processing +** of the IN operator is even possible. +** +**

    2. +** ^A call to wx_sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates +** to SQLite that the virtual table does or does not want to process +** the IN operator all-at-once, respectively. ^Thus when the third +** parameter (F) is non-negative, this interface is the mechanism by +** which the virtual table tells SQLite how it wants to process the +** IN operator. +**

    +** +** ^The wx_sqlite3_vtab_in(P,N,F) interface can be invoked multiple times +** within the same xBestIndex method call. ^For any given P,N pair, +** the return value from wx_sqlite3_vtab_in(P,N,F) will always be the same +** within the same xBestIndex call. ^If the interface returns true +** (non-zero), that means that the constraint is an IN operator +** that can be processed all-at-once. ^If the constraint is not an IN +** operator or cannot be processed all-at-once, then the interface returns +** false. +** +** ^(All-at-once processing of the IN operator is selected if both of the +** following conditions are met: +** +**
      +**
    1. The P->aConstraintUsage[N].argvIndex value is set to a positive +** integer. This is how the virtual table tells SQLite that it wants to +** use the N-th constraint. +** +**

    2. The last call to wx_sqlite3_vtab_in(P,N,F) for which F was +** non-negative had F>=1. +**

    )^ +** +** ^If either or both of the conditions above are false, then SQLite uses +** the traditional one-at-a-time processing strategy for the IN constraint. +** ^If both conditions are true, then the argvIndex-th parameter to the +** xFilter method will be an [wx_sqlite3_value] that appears to be NULL, +** but which can be passed to [wx_sqlite3_vtab_in_first()] and +** [wx_sqlite3_vtab_in_next()] to find all values on the right-hand side +** of the IN constraint. +*/ +SQLITE_API int wx_sqlite3_vtab_in(wx_sqlite3_index_info*, int iCons, int bHandle); + +/* +** CAPI3REF: Find all elements on the right-hand side of an IN constraint. +** +** These interfaces are only useful from within the +** [xFilter|xFilter() method] of a [virtual table] implementation. +** The result of invoking these interfaces from any other context +** is undefined and probably harmful. +** +** The X parameter in a call to wx_sqlite3_vtab_in_first(X,P) or +** wx_sqlite3_vtab_in_next(X,P) should be one of the parameters to the +** xFilter method which invokes these routines, and specifically +** a parameter that was previously selected for all-at-once IN constraint +** processing use the [wx_sqlite3_vtab_in()] interface in the +** [xBestIndex|xBestIndex method]. ^(If the X parameter is not +** an xFilter argument that was selected for all-at-once IN constraint +** processing, then these routines return [SQLITE_ERROR].)^ +** +** ^(Use these routines to access all values on the right-hand side +** of the IN constraint using code like the following: +** +**
    +**    for(rc=wx_sqlite3_vtab_in_first(pList, &pVal);
    +**        rc==SQLITE_OK && pVal;
    +**        rc=wx_sqlite3_vtab_in_next(pList, &pVal)
    +**    ){
    +**      // do something with pVal
    +**    }
    +**    if( rc!=SQLITE_OK ){
    +**      // an error has occurred
    +**    }
    +** 
    )^ +** +** ^On success, the wx_sqlite3_vtab_in_first(X,P) and wx_sqlite3_vtab_in_next(X,P) +** routines return SQLITE_OK and set *P to point to the first or next value +** on the RHS of the IN constraint. ^If there are no more values on the +** right hand side of the IN constraint, then *P is set to NULL and these +** routines return [SQLITE_DONE]. ^The return value might be +** some other value, such as SQLITE_NOMEM, in the event of a malfunction. +** +** The *ppOut values returned by these routines are only valid until the +** next call to either of these routines or until the end of the xFilter +** method from which these routines were called. If the virtual table +** implementation needs to retain the *ppOut values for longer, it must make +** copies. The *ppOut values are [protected wx_sqlite3_value|protected]. +*/ +SQLITE_API int wx_sqlite3_vtab_in_first(wx_sqlite3_value *pVal, wx_sqlite3_value **ppOut); +SQLITE_API int wx_sqlite3_vtab_in_next(wx_sqlite3_value *pVal, wx_sqlite3_value **ppOut); + +/* +** CAPI3REF: Constraint values in xBestIndex() +** METHOD: wx_sqlite3_index_info +** +** This API may only be used from within the [xBestIndex|xBestIndex method] +** of a [virtual table] implementation. The result of calling this interface +** from outside of an xBestIndex method are undefined and probably harmful. +** +** ^When the wx_sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within +** the [xBestIndex] method of a [virtual table] implementation, with P being +** a copy of the [wx_sqlite3_index_info] object pointer passed into xBestIndex and +** J being a 0-based index into P->aConstraint[], then this routine +** attempts to set *V to the value of the right-hand operand of +** that constraint if the right-hand operand is known. ^If the +** right-hand operand is not known, then *V is set to a NULL pointer. +** ^The wx_sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if +** and only if *V is set to a value. ^The wx_sqlite3_vtab_rhs_value(P,J,V) +** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th +** constraint is not available. ^The wx_sqlite3_vtab_rhs_value() interface +** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if +** something goes wrong. +** +** The wx_sqlite3_vtab_rhs_value() interface is usually only successful if +** the right-hand operand of a constraint is a literal value in the original +** SQL statement. If the right-hand operand is an expression or a reference +** to some other column or a [host parameter], then wx_sqlite3_vtab_rhs_value() +** will probably return [SQLITE_NOTFOUND]. +** +** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and +** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such +** constraints, wx_sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^ +** +** ^The [wx_sqlite3_value] object returned in *V is a protected wx_sqlite3_value +** and remains valid for the duration of the xBestIndex method call. +** ^When xBestIndex returns, the wx_sqlite3_value object returned by +** wx_sqlite3_vtab_rhs_value() is automatically deallocated. +** +** The "_rhs_" in the name of this routine is an abbreviation for +** "Right-Hand Side". +*/ +SQLITE_API int wx_sqlite3_vtab_rhs_value(wx_sqlite3_index_info*, int, wx_sqlite3_value **ppVal); /* ** CAPI3REF: Conflict resolution modes @@ -9340,6 +9962,10 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ ** managed by the prepared statement S and will be automatically freed when ** S is finalized. ** +** Not all values are available for all query elements. When a value is +** not available, the output variable is set to -1 if the value is numeric, +** or to NULL if it is a string (SQLITE_SCANSTAT_NAME). +** **
    ** [[SQLITE_SCANSTAT_NLOOP]]
    SQLITE_SCANSTAT_NLOOP
    **
    ^The [wx_sqlite3_int64] variable pointed to by the V parameter will be @@ -9367,12 +9993,24 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] ** description for the X-th loop. ** -** [[SQLITE_SCANSTAT_SELECTID]]
    SQLITE_SCANSTAT_SELECT
    +** [[SQLITE_SCANSTAT_SELECTID]]
    SQLITE_SCANSTAT_SELECTID
    **
    ^The "int" variable pointed to by the V parameter will be set to the -** "select-id" for the X-th loop. The select-id identifies which query or -** subquery the loop is part of. The main query has a select-id of zero. -** The select-id is the same value as is output in the first column -** of an [EXPLAIN QUERY PLAN] query. +** id for the X-th query plan element. The id value is unique within the +** statement. The select-id is the same value as is output in the first +** column of an [EXPLAIN QUERY PLAN] query. +** +** [[SQLITE_SCANSTAT_PARENTID]]
    SQLITE_SCANSTAT_PARENTID
    +**
    The "int" variable pointed to by the V parameter will be set to the +** the id of the parent of the current query element, if applicable, or +** to zero if the query element has no parent. This is the same value as +** returned in the second column of an [EXPLAIN QUERY PLAN] query. +** +** [[SQLITE_SCANSTAT_NCYCLE]]
    SQLITE_SCANSTAT_NCYCLE
    +**
    The wx_sqlite3_int64 output value is set to the number of cycles, +** according to the processor time-stamp counter, that elapsed while the +** query element was being processed. This value is not available for +** all query elements - if it is unavailable the output variable is +** set to -1. **
    */ #define SQLITE_SCANSTAT_NLOOP 0 @@ -9381,12 +10019,14 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ #define SQLITE_SCANSTAT_NAME 3 #define SQLITE_SCANSTAT_EXPLAIN 4 #define SQLITE_SCANSTAT_SELECTID 5 +#define SQLITE_SCANSTAT_PARENTID 6 +#define SQLITE_SCANSTAT_NCYCLE 7 /* ** CAPI3REF: Prepared Statement Scan Status ** METHOD: wx_sqlite3_stmt ** -** This interface returns information about the predicted and measured +** These interfaces return information about the predicted and measured ** performance for pStmt. Advanced applications can use this ** interface to compare the predicted and the measured performance and ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. @@ -9397,19 +10037,25 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *wx_sqlite3_vtab_collation(wx_sqlite3_ ** ** The "iScanStatusOp" parameter determines which status information to return. ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior -** of this interface is undefined. -** ^The requested measurement is written into a variable pointed to by -** the "pOut" parameter. -** Parameter "idx" identifies the specific loop to retrieve statistics for. -** Loops are numbered starting from zero. ^If idx is out of range - less than -** zero or greater than or equal to the total number of loops used to implement -** the statement - a non-zero value is returned and the variable that pOut -** points to is unchanged. -** -** ^Statistics might not be available for all loops in all statements. ^In cases -** where there exist loops with no available statistics, this function behaves -** as if the loop did not exist - it returns non-zero and leave the variable -** that pOut points to unchanged. +** of this interface is undefined. ^The requested measurement is written into +** a variable pointed to by the "pOut" parameter. +** +** The "flags" parameter must be passed a mask of flags. At present only +** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX +** is specified, then status information is available for all elements +** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If +** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements +** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of +** the EXPLAIN QUERY PLAN output) are available. Invoking API +** wx_sqlite3_stmt_scanstatus() is equivalent to calling +** wx_sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter. +** +** Parameter "idx" identifies the specific query element to retrieve statistics +** for. Query elements are numbered starting from zero. A value of -1 may be +** to query for statistics regarding the entire query. ^If idx is out of range +** - less than -1 or greater than or equal to the total number of query +** elements used to implement the statement - a non-zero value is returned and +** the variable that pOut points to is unchanged. ** ** See also: [wx_sqlite3_stmt_scanstatus_reset()] */ @@ -9419,6 +10065,19 @@ SQLITE_API int wx_sqlite3_stmt_scanstatus( int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ void *pOut /* Result written here */ ); +SQLITE_API int wx_sqlite3_stmt_scanstatus_v2( + wx_sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ + int idx, /* Index of loop to report on */ + int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ + int flags, /* Mask of flags defined below */ + void *pOut /* Result written here */ +); + +/* +** CAPI3REF: Prepared Statement Scan Status +** KEYWORDS: {scan status flags} +*/ +#define SQLITE_SCANSTAT_COMPLEX 0x0001 /* ** CAPI3REF: Zero Scan-Status Counters @@ -9509,6 +10168,10 @@ SQLITE_API int wx_sqlite3_db_cacheflush(wx_sqlite3*); ** function is not defined for operations on WITHOUT ROWID tables, or for ** DELETE operations on rowid tables. ** +** ^The wx_sqlite3_preupdate_hook(D,C,P) function returns the P argument from +** the previous call on the same [database connection] D, or NULL for +** the first call on D. +** ** The [wx_sqlite3_preupdate_old()], [wx_sqlite3_preupdate_new()], ** [wx_sqlite3_preupdate_count()], and [wx_sqlite3_preupdate_depth()] interfaces ** provide additional information about a preupdate event. These routines @@ -9545,6 +10208,15 @@ SQLITE_API int wx_sqlite3_db_cacheflush(wx_sqlite3*); ** triggers; or 2 for changes resulting from triggers called by top-level ** triggers; and so forth. ** +** When the [wx_sqlite3_blob_write()] API is used to update a blob column, +** the pre-update hook is invoked with SQLITE_DELETE. This is because the +** in this case the new values are not available. In this case, when a +** callback made with op==SQLITE_DELETE is actuall a write using the +** wx_sqlite3_blob_write() API, the [wx_sqlite3_preupdate_blobwrite()] returns +** the index of the column being written. In other cases, where the +** pre-update hook is being invoked for some other reason, including a +** regular DELETE, wx_sqlite3_preupdate_blobwrite() returns -1. +** ** See also: [wx_sqlite3_update_hook()] */ #if defined(SQLITE_ENABLE_PREUPDATE_HOOK) @@ -9565,6 +10237,7 @@ SQLITE_API int wx_sqlite3_preupdate_old(wx_sqlite3 *, int, wx_sqlite3_value **); SQLITE_API int wx_sqlite3_preupdate_count(wx_sqlite3 *); SQLITE_API int wx_sqlite3_preupdate_depth(wx_sqlite3 *); SQLITE_API int wx_sqlite3_preupdate_new(wx_sqlite3 *, int, wx_sqlite3_value **); +SQLITE_API int wx_sqlite3_preupdate_blobwrite(wx_sqlite3 *); #endif /* @@ -9803,8 +10476,8 @@ SQLITE_API SQLITE_EXPERIMENTAL int wx_sqlite3_snapshot_recover(wx_sqlite3 *db, c ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory ** allocation error occurs. ** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_DESERIALIZE] option. +** This interface is omitted if SQLite is compiled with the +** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API unsigned char *wx_sqlite3_serialize( wx_sqlite3 *db, /* The database connection */ @@ -9851,12 +10524,16 @@ SQLITE_API unsigned char *wx_sqlite3_serialize( ** database is currently in a read transaction or is involved in a backup ** operation. ** +** It is not possible to deserialized into the TEMP database. If the +** S argument to wx_sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the +** function returns SQLITE_ERROR. +** ** If wx_sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then ** [wx_sqlite3_free()] is invoked on argument P prior to returning. ** -** This interface is only available if SQLite is compiled with the -** [SQLITE_ENABLE_DESERIALIZE] option. +** This interface is omitted if SQLite is compiled with the +** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API int wx_sqlite3_deserialize( wx_sqlite3 *db, /* The database connection */ @@ -9900,6 +10577,19 @@ SQLITE_API int wx_sqlite3_deserialize( # undef double #endif +#if defined(__wasi__) +# undef SQLITE_WASI +# define SQLITE_WASI 1 +# undef SQLITE_OMIT_WAL +# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */ +# ifndef SQLITE_OMIT_LOAD_EXTENSION +# define SQLITE_OMIT_LOAD_EXTENSION +# endif +# ifndef SQLITE_THREADSAFE +# define SQLITE_THREADSAFE 0 +# endif +#endif + #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif @@ -10105,6 +10795,38 @@ SQLITE_API int wx_sqlite3session_create( */ SQLITE_API void wx_sqlite3session_delete(wx_sqlite3_session *pSession); +/* +** CAPIREF: Conigure a Session Object +** METHOD: wx_sqlite3_session +** +** This method is used to configure a session object after it has been +** created. At present the only valid value for the second parameter is +** [SQLITE_SESSION_OBJCONFIG_SIZE]. +** +** Arguments for wx_sqlite3session_object_config() +** +** The following values may passed as the the 4th parameter to +** wx_sqlite3session_object_config(). +** +**
    SQLITE_SESSION_OBJCONFIG_SIZE
    +** This option is used to set, clear or query the flag that enables +** the [wx_sqlite3session_changeset_size()] API. Because it imposes some +** computational overhead, this API is disabled by default. Argument +** pArg must point to a value of type (int). If the value is initially +** 0, then the wx_sqlite3session_changeset_size() API is disabled. If it +** is greater than 0, then the same API is enabled. Or, if the initial +** value is less than zero, no change is made. In all cases the (int) +** variable is set to 1 if the wx_sqlite3session_changeset_size() API is +** enabled following the current call, or 0 otherwise. +** +** It is an error (SQLITE_MISUSE) to attempt to modify this setting after +** the first table has been attached to the session object. +*/ +SQLITE_API int wx_sqlite3session_object_config(wx_sqlite3_session*, int op, void *pArg); + +/* +*/ +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 /* ** CAPI3REF: Enable Or Disable A Session Object @@ -10349,6 +11071,22 @@ SQLITE_API int wx_sqlite3session_changeset( void **ppChangeset /* OUT: Buffer containing changeset */ ); +/* +** CAPI3REF: Return An Upper-limit For The Size Of The Changeset +** METHOD: wx_sqlite3_session +** +** By default, this function always returns 0. For it to return +** a useful result, the wx_sqlite3_session object must have been configured +** to enable this API using wx_sqlite3session_object_config() with the +** SQLITE_SESSION_OBJCONFIG_SIZE verb. +** +** When enabled, this function returns an upper limit, in bytes, for the size +** of the changeset that might be produced if wx_sqlite3session_changeset() were +** called. The final changeset size might be equal to or smaller than the +** size in bytes returned by this function. +*/ +SQLITE_API wx_sqlite3_int64 wx_sqlite3session_changeset_size(wx_sqlite3_session *pSession); + /* ** CAPI3REF: Load The Difference Between Tables Into A Session ** METHOD: wx_sqlite3_session @@ -12385,74 +13123,13 @@ int wx_sqlite3_user_delete( /* ** Symbols for ciphers */ -#define CODEC_TYPE_UNKNOWN 0 -#define CODEC_TYPE_AES128 1 -#define CODEC_TYPE_AES256 2 -#define CODEC_TYPE_CHACHA20 3 -#define CODEC_TYPE_SQLCIPHER 4 -#define CODEC_TYPE_RC4 5 -#define CODEC_TYPE_MAX 5 - -/* -** Definitions of supported ciphers -*/ - -/* -** Compatibility with wxSQLite3 -*/ -#ifdef WXSQLITE3_HAVE_CIPHER_AES_128_CBC -#define HAVE_CIPHER_AES_128_CBC WXSQLITE3_HAVE_CIPHER_AES_128_CBC -#endif - -#ifdef WXSQLITE3_HAVE_CIPHER_AES_256_CBC -#define HAVE_CIPHER_AES_256_CBC WXSQLITE3_HAVE_CIPHER_AES_256_CBC -#endif - -#ifdef WXSQLITE3_HAVE_CIPHER_CHACHA20 -#define HAVE_CIPHER_CHACHA20 WXSQLITE3_HAVE_CIPHER_CHACHA20 -#endif - -#ifdef WXSQLITE3_HAVE_CIPHER_SQLCIPHER -#define HAVE_CIPHER_SQLCIPHER WXSQLITE3_HAVE_CIPHER_SQLCIPHER -#endif - -#ifdef WXSQLITE3_HAVE_CIPHER_RC4 -#define HAVE_CIPHER_RC4 WXSQLITE3_HAVE_CIPHER_RC4 -#endif - -/* -** Actual definitions of supported ciphers -*/ -#ifndef HAVE_CIPHER_AES_128_CBC -#define HAVE_CIPHER_AES_128_CBC 1 -#endif - -#ifndef HAVE_CIPHER_AES_256_CBC -#define HAVE_CIPHER_AES_256_CBC 1 -#endif - -#ifndef HAVE_CIPHER_CHACHA20 -#define HAVE_CIPHER_CHACHA20 1 -#endif - -#ifndef HAVE_CIPHER_SQLCIPHER -#define HAVE_CIPHER_SQLCIPHER 1 -#endif - -#ifndef HAVE_CIPHER_RC4 -#define HAVE_CIPHER_RC4 1 -#endif - -/* -** Check that at least one cipher is be supported -*/ -#if HAVE_CIPHER_AES_128_CBC == 0 && \ - HAVE_CIPHER_AES_256_CBC == 0 && \ - HAVE_CIPHER_CHACHA20 == 0 && \ - HAVE_CIPHER_SQLCIPHER == 0 && \ - HAVE_CIPHER_RC4 == 0 -#error Enable at least one cipher scheme! -#endif +#define CODEC_TYPE_UNKNOWN 0 +#define CODEC_TYPE_AES128 1 +#define CODEC_TYPE_AES256 2 +#define CODEC_TYPE_CHACHA20 3 +#define CODEC_TYPE_SQLCIPHER 4 +#define CODEC_TYPE_RC4 5 +#define CODEC_TYPE_MAX_BUILTIN 5 /* ** Definition of API functions @@ -12521,6 +13198,9 @@ SQLITE_API void wx_sqlite3_activate_see(const char* zPassPhrase); /* ** Define functions for the configuration of the wxSQLite3 encryption extension */ +SQLITE_API int wx_sqlite3mc_cipher_count(); +SQLITE_API int wx_sqlite3mc_cipher_index(const char* cipherName); +SQLITE_API const char* wx_sqlite3mc_cipher_name(int cipherIndex); SQLITE_API int wx_sqlite3mc_config(wx_sqlite3* db, const char* paramName, int newValue); SQLITE_API int wx_sqlite3mc_config_cipher(wx_sqlite3* db, const char* cipherName, const char* paramName, int newValue); SQLITE_API unsigned char* wx_sqlite3mc_codec_data(wx_sqlite3* db, const char* zDbName, const char* paramName); @@ -12532,6 +13212,88 @@ SQLITE_API int wxwx_sqlite3_config_cipher(wx_sqlite3* db, const char* cipherName SQLITE_API unsigned char* wxwx_sqlite3_codec_data(wx_sqlite3* db, const char* zDbName, const char* paramName); #endif +/* +** Structures and functions to dynamically register a cipher +*/ + +/* +** Structure for a single cipher configuration parameter +** +** Components: +** m_name - name of parameter (1st char = alpha, rest = alphanumeric or underscore, max 63 characters) +** m_value - current/transient parameter value +** m_default - default parameter value +** m_minValue - minimum valid parameter value +** m_maxValue - maximum valid parameter value +*/ +typedef struct _CipherParams +{ + char* m_name; + int m_value; + int m_default; + int m_minValue; + int m_maxValue; +} CipherParams; + +/* +** Structure for a cipher API +** +** Components: +** m_name - name of cipher (1st char = alpha, rest = alphanumeric or underscore, max 63 characters) +** m_allocateCipher - Function pointer for function AllocateCipher +** m_freeCipher - Function pointer for function FreeCipher +** m_cloneCipher - Function pointer for function CloneCipher +** m_getLegacy - Function pointer for function GetLegacy +** m_getPageSize - Function pointer for function GetPageSize +** m_getReserved - Function pointer for function GetReserved +** m_getSalt - Function pointer for function GetSalt +** m_generateKey - Function pointer for function GenerateKey +** m_encryptPage - Function pointer for function EncryptPage +** m_decryptPage - Function pointer for function DecryptPage +*/ + +typedef struct BtShared BtSharedMC; + +typedef void* (*AllocateCipher_t)(wx_sqlite3* db); +typedef void (*FreeCipher_t)(void* cipher); +typedef void (*CloneCipher_t)(void* cipherTo, void* cipherFrom); +typedef int (*GetLegacy_t)(void* cipher); +typedef int (*GetPageSize_t)(void* cipher); +typedef int (*GetReserved_t)(void* cipher); +typedef unsigned char* (*GetSalt_t)(void* cipher); +typedef void (*GenerateKey_t)(void* cipher, BtSharedMC* pBt, char* userPassword, int passwordLength, int rekey, unsigned char* cipherSalt); +typedef int (*EncryptPage_t)(void* cipher, int page, unsigned char* data, int len, int reserved); +typedef int (*DecryptPage_t)(void* cipher, int page, unsigned char* data, int len, int reserved, int hmacCheck); + +typedef struct _CipherDescriptor +{ + char* m_name; + AllocateCipher_t m_allocateCipher; + FreeCipher_t m_freeCipher; + CloneCipher_t m_cloneCipher; + GetLegacy_t m_getLegacy; + GetPageSize_t m_getPageSize; + GetReserved_t m_getReserved; + GetSalt_t m_getSalt; + GenerateKey_t m_generateKey; + EncryptPage_t m_encryptPage; + DecryptPage_t m_decryptPage; +} CipherDescriptor; + +/* +** Register a cipher +** +** Arguments: +** desc - Cipher descriptor structure +** params - Cipher configuration parameter table +** makeDefault - flag whether to make the cipher the default cipher +** +** Returns: +** SQLITE_OK - the cipher could be registered successfully +** SQLITE_ERROR - the cipher could not be registered +*/ +SQLITE_API int wx_sqlite3mc_register_cipher(const CipherDescriptor* desc, const CipherParams* params, int makeDefault); + #ifdef __cplusplus } #endif @@ -12546,7 +13308,7 @@ SQLITE_API unsigned char* wxwx_sqlite3_codec_data(wx_sqlite3* db, const char* zD ** Purpose: Header file for VFS of SQLite3 Multiple Ciphers support ** Author: Ulrich Telle ** Created: 2020-03-01 -** Copyright: (c) 2020 Ulrich Telle +** Copyright: (c) 2020-2023 Ulrich Telle ** License: MIT */ @@ -12556,6 +13318,11 @@ SQLITE_API unsigned char* wxwx_sqlite3_codec_data(wx_sqlite3* db, const char* zD extern "C" { #endif +#ifndef SQLITE_PRIVATE +#define SQLITE_PRIVATE +#endif +SQLITE_PRIVATE int wx_sqlite3mcCheckVfs(const char* zVfs); + SQLITE_API int wx_sqlite3mc_vfs_create(const char* zVfsReal, int makeDefault); SQLITE_API void wx_sqlite3mc_vfs_destroy(const char* zName); SQLITE_API void wx_sqlite3mc_vfs_shutdown(); diff --git a/Plugins/FusionDarkStyle/fusiondarkplugin.cpp b/Plugins/FusionDarkStyle/fusiondarkplugin.cpp index 2b2a2e8..b22c223 100644 --- a/Plugins/FusionDarkStyle/fusiondarkplugin.cpp +++ b/Plugins/FusionDarkStyle/fusiondarkplugin.cpp @@ -58,12 +58,12 @@ QPalette FusionDarkStyle::standardPalette() const FusionDarkPlugin::FusionDarkPlugin(QObject *parent) : QStylePlugin(parent) { - THEME_TUNER->registerQWizardThemeTuneRequired(STYLE_NAME); + ThemeTuner::registerQWizardThemeTuneRequired(STYLE_NAME); } FusionDarkPlugin::~FusionDarkPlugin() { - THEME_TUNER->deregisterQWizardThemeTuneRequired(STYLE_NAME); + ThemeTuner::deregisterQWizardThemeTuneRequired(STYLE_NAME); } QStyle *FusionDarkPlugin::create(const QString &key) diff --git a/Plugins/HtmlExport/HtmlExport.pro b/Plugins/HtmlExport/HtmlExport.pro index 7e84021..1264279 100644 --- a/Plugins/HtmlExport/HtmlExport.pro +++ b/Plugins/HtmlExport/HtmlExport.pro @@ -29,16 +29,14 @@ FORMS += \ htmlexport.ui -TRANSLATIONS += HtmlExport_ro_RO.ts \ - HtmlExport_de.ts \ - HtmlExport_it.ts \ - HtmlExport_zh_CN.ts \ - HtmlExport_sk.ts \ - HtmlExport_ru.ts \ - HtmlExport_pt_BR.ts \ - HtmlExport_fr.ts \ - HtmlExport_es.ts \ - HtmlExport_pl.ts + + + + + + + + diff --git a/Plugins/HtmlExport/HtmlExport_de.qm b/Plugins/HtmlExport/HtmlExport_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/HtmlExport/HtmlExport_de.qm and /dev/null differ diff --git a/Plugins/HtmlExport/HtmlExport_de.ts b/Plugins/HtmlExport/HtmlExport_de.ts deleted file mode 100644 index 8e64155..0000000 --- a/Plugins/HtmlExport/HtmlExport_de.ts +++ /dev/null @@ -1,173 +0,0 @@ - - - - - HtmlExport - - - SQL query results - - - - - - no type - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - virtual - - - - - Exported database: %1 - - - - - Index: %1 - - - - - For table: - - - - - Unique: - - - - - Yes - - - - - No - - - - - Column - - - - - Collating - - - - - Sort order - - - - - Trigger: %1 - - - - - Activated: - - - - - Action: - - - - - On view: - - - - - On table: - - - - - Activate condition: - - - - - Code executed: - - - - - View: %1 - - - - - Document generated by SQLiteStudio v%1 on %2 - - - - - HtmlExportConfig - - - Maximum number of characters per cell: - - - - - Include data types in first row - - - - - Column names as first row - - - - - Row numbers as first column - - - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> - - - - - Don't escape HTML characters - - - - diff --git a/Plugins/HtmlExport/HtmlExport_es.qm b/Plugins/HtmlExport/HtmlExport_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/HtmlExport/HtmlExport_es.qm and /dev/null differ diff --git a/Plugins/HtmlExport/HtmlExport_es.ts b/Plugins/HtmlExport/HtmlExport_es.ts deleted file mode 100644 index e022d24..0000000 --- a/Plugins/HtmlExport/HtmlExport_es.ts +++ /dev/null @@ -1,173 +0,0 @@ - - - - - HtmlExport - - - SQL query results - - - - - - no type - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - virtual - - - - - Exported database: %1 - - - - - Index: %1 - - - - - For table: - - - - - Unique: - - - - - Yes - - - - - No - - - - - Column - - - - - Collating - - - - - Sort order - - - - - Trigger: %1 - - - - - Activated: - - - - - Action: - - - - - On view: - - - - - On table: - - - - - Activate condition: - - - - - Code executed: - - - - - View: %1 - - - - - Document generated by SQLiteStudio v%1 on %2 - - - - - HtmlExportConfig - - - Maximum number of characters per cell: - - - - - Include data types in first row - - - - - Column names as first row - - - - - Row numbers as first column - - - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> - - - - - Don't escape HTML characters - - - - diff --git a/Plugins/HtmlExport/HtmlExport_fr.qm b/Plugins/HtmlExport/HtmlExport_fr.qm deleted file mode 100644 index 7852caa..0000000 Binary files a/Plugins/HtmlExport/HtmlExport_fr.qm and /dev/null differ diff --git a/Plugins/HtmlExport/HtmlExport_fr.ts b/Plugins/HtmlExport/HtmlExport_fr.ts deleted file mode 100644 index 06d663f..0000000 --- a/Plugins/HtmlExport/HtmlExport_fr.ts +++ /dev/null @@ -1,173 +0,0 @@ - - - - - HtmlExport - - - SQL query results - Résultats de la requête SQL - - - - - no type - Aucun type - - - - - Exported table: %1 - Table exportée : %1 - - - - - Table: %1 - Table : %1 - - - - virtual - virtuel - - - - Exported database: %1 - Base de données exportée : %1 - - - - Index: %1 - Index : %1 - - - - For table: - Pour la table : - - - - Unique: - Unique : - - - - Yes - Oui - - - - No - Non - - - - Column - Colonne - - - - Collating - Collation - - - - Sort order - Ordre de tri - - - - Trigger: %1 - Déclencheur : %1 - - - - Activated: - Activé : - - - - Action: - Action : - - - - On view: - Sur la vue : - - - - On table: - Sur la table : - - - - Activate condition: - Activé la condition : - - - - Code executed: - Exécuter le code : - - - - View: %1 - Vue : %1 - - - - Document generated by SQLiteStudio v%1 on %2 - Document généré par SQLiteStudio v%1 de %2 - - - - HtmlExportConfig - - - Maximum number of characters per cell: - Nombre de caractères maximum par cellule : - - - - Include data types in first row - Types données incluses dans la première ligne - - - - Column names as first row - Nom des colonnes dans la première ligne - - - - Row numbers as first column - Numero de ligne dans la première colonne - - - - Output format - Format d’affichage - - - - Format document (new lines, indentation) - Format document (nouvelles ligne, indentation) - - - - Compress (everything in one line) - Condensé (tout en une ligne) - - - - <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> - <p>Lorsqu’il y a des caractères HTML tel que <, > et & ils ne sont pas enlevés des valeurs exportées. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of < and > characters).Soyez attentif.</p> - - - - Don't escape HTML characters - N’enlevez pas les caractères HTML - - - diff --git a/Plugins/HtmlExport/HtmlExport_it.qm b/Plugins/HtmlExport/HtmlExport_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/HtmlExport/HtmlExport_it.qm and /dev/null differ diff --git a/Plugins/HtmlExport/HtmlExport_it.ts b/Plugins/HtmlExport/HtmlExport_it.ts deleted file mode 100644 index a451fc3..0000000 --- a/Plugins/HtmlExport/HtmlExport_it.ts +++ /dev/null @@ -1,173 +0,0 @@ - - - - - HtmlExport - - - SQL query results - - - - - - no type - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - virtual - - - - - Exported database: %1 - - - - - Index: %1 - - - - - For table: - - - - - Unique: - - - - - Yes - - - - - No - - - - - Column - - - - - Collating - - - - - Sort order - - - - - Trigger: %1 - - - - - Activated: - - - - - Action: - - - - - On view: - - - - - On table: - - - - - Activate condition: - - - - - Code executed: - - - - - View: %1 - - - - - Document generated by SQLiteStudio v%1 on %2 - - - - - HtmlExportConfig - - - Maximum number of characters per cell: - - - - - Include data types in first row - - - - - Column names as first row - - - - - Row numbers as first column - - - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> - - - - - Don't escape HTML characters - - - - diff --git a/Plugins/HtmlExport/HtmlExport_pl.qm b/Plugins/HtmlExport/HtmlExport_pl.qm deleted file mode 100644 index a0cac5e..0000000 Binary files a/Plugins/HtmlExport/HtmlExport_pl.qm and /dev/null differ diff --git a/Plugins/HtmlExport/HtmlExport_pl.ts b/Plugins/HtmlExport/HtmlExport_pl.ts deleted file mode 100644 index 3fba2f9..0000000 --- a/Plugins/HtmlExport/HtmlExport_pl.ts +++ /dev/null @@ -1,173 +0,0 @@ - - - - - HtmlExport - - - SQL query results - Wyniki zapytania SQL - - - - - no type - brak typu - - - - - Exported table: %1 - Eksportowana tabela: %1 - - - - - Table: %1 - Tabela: %1 - - - - virtual - wirtualna - - - - Exported database: %1 - Eksportowana baza: %1 - - - - Index: %1 - Indeks: %1 - - - - For table: - Dla tabeli: - - - - Unique: - Unikalny: - - - - Yes - Tak - - - - No - Nie - - - - Column - Kolumna - - - - Collating - Zestawienie - - - - Sort order - Sortowanie - - - - Trigger: %1 - Wyzwalacz: %1 - - - - Activated: - Aktywowany: - - - - Action: - Akcja: - - - - On view: - Na widoku: - - - - On table: - Na tabeli: - - - - Activate condition: - Warunek aktywacji: - - - - Code executed: - Wykonywany kod: - - - - View: %1 - Widok: %1 - - - - Document generated by SQLiteStudio v%1 on %2 - Dokument wygenerowany przez SQLiteStudio v%1 dnia %2 - - - - HtmlExportConfig - - - Maximum number of characters per cell: - Maksymalna liczba znaków w komórce: - - - - Include data types in first row - Dodaj typy danych w pierwszym wierszu - - - - Column names as first row - Nazwy kolumn w pierwszym wierszu - - - - Row numbers as first column - Numery wierszy w pierwszej kolumnie - - - - Output format - Format wyjÅ›ciowy - - - - Format document (new lines, indentation) - Formatuj dokument (nowe linie, wciÄ™cia) - - - - Compress (everything in one line) - Kompresuj (wszystko w jednej linii) - - - - <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> - <p>Kiedy włączone, znaki HTML takie jak &lt;, &gt; i &amp; nie bÄ™dÄ… podmieniane w wyeksportowanych wartoÅ›ciach. Pozwala to przykÅ‚adowo na eksportowanie odnoÅ›ników w dokumencie, ale może też spowodować, ze dokument HTML bÄ™dzie niepoprawny (niedopasowane pary znaków &lt; i &gt;). Miej tego Å›wiadomość.</p> - - - - Don't escape HTML characters - Nie podmieniaj znaków HTML. - - - diff --git a/Plugins/HtmlExport/HtmlExport_pt_BR.qm b/Plugins/HtmlExport/HtmlExport_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/HtmlExport/HtmlExport_pt_BR.qm and /dev/null differ diff --git a/Plugins/HtmlExport/HtmlExport_pt_BR.ts b/Plugins/HtmlExport/HtmlExport_pt_BR.ts deleted file mode 100644 index 560fd85..0000000 --- a/Plugins/HtmlExport/HtmlExport_pt_BR.ts +++ /dev/null @@ -1,173 +0,0 @@ - - - - - HtmlExport - - - SQL query results - - - - - - no type - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - virtual - - - - - Exported database: %1 - - - - - Index: %1 - - - - - For table: - - - - - Unique: - - - - - Yes - - - - - No - - - - - Column - - - - - Collating - - - - - Sort order - - - - - Trigger: %1 - - - - - Activated: - - - - - Action: - - - - - On view: - - - - - On table: - - - - - Activate condition: - - - - - Code executed: - - - - - View: %1 - - - - - Document generated by SQLiteStudio v%1 on %2 - - - - - HtmlExportConfig - - - Maximum number of characters per cell: - - - - - Include data types in first row - - - - - Column names as first row - - - - - Row numbers as first column - - - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> - - - - - Don't escape HTML characters - - - - diff --git a/Plugins/HtmlExport/HtmlExport_ro_RO.qm b/Plugins/HtmlExport/HtmlExport_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/HtmlExport/HtmlExport_ro_RO.qm and /dev/null differ diff --git a/Plugins/HtmlExport/HtmlExport_ro_RO.ts b/Plugins/HtmlExport/HtmlExport_ro_RO.ts deleted file mode 100644 index 2860272..0000000 --- a/Plugins/HtmlExport/HtmlExport_ro_RO.ts +++ /dev/null @@ -1,173 +0,0 @@ - - - - - HtmlExport - - - SQL query results - - - - - - no type - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - virtual - - - - - Exported database: %1 - - - - - Index: %1 - - - - - For table: - - - - - Unique: - - - - - Yes - - - - - No - - - - - Column - - - - - Collating - - - - - Sort order - - - - - Trigger: %1 - - - - - Activated: - - - - - Action: - - - - - On view: - - - - - On table: - - - - - Activate condition: - - - - - Code executed: - - - - - View: %1 - - - - - Document generated by SQLiteStudio v%1 on %2 - - - - - HtmlExportConfig - - - Maximum number of characters per cell: - - - - - Include data types in first row - - - - - Column names as first row - - - - - Row numbers as first column - - - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> - - - - - Don't escape HTML characters - - - - diff --git a/Plugins/HtmlExport/HtmlExport_ru.qm b/Plugins/HtmlExport/HtmlExport_ru.qm deleted file mode 100644 index 97efccd..0000000 Binary files a/Plugins/HtmlExport/HtmlExport_ru.qm and /dev/null differ diff --git a/Plugins/HtmlExport/HtmlExport_ru.ts b/Plugins/HtmlExport/HtmlExport_ru.ts deleted file mode 100644 index 5bd37f7..0000000 --- a/Plugins/HtmlExport/HtmlExport_ru.ts +++ /dev/null @@ -1,173 +0,0 @@ - - - - - HtmlExport - - - SQL query results - Результаты запроÑа SQL - - - - - no type - без типа - - - - - Exported table: %1 - ЭкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°: %1 - - - - - Table: %1 - Таблица: %1 - - - - virtual - Ð²Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ð°Ñ - - - - Exported database: %1 - ЭкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных: %1 - - - - Index: %1 - ИндекÑ: %1 - - - - For table: - Ð”Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹: - - - - Unique: - Уникальный: - - - - Yes - Да - - - - No - Ðет - - - - Column - Столбец - - - - Collating - Сравнение - - - - Sort order - ПорÑдок Ñортировки - - - - Trigger: %1 - Триггер: %1 - - - - Activated: - Ðктивирован: - - - - Action: - ДейÑтвие: - - - - On view: - Ð”Ð»Ñ Ð¿Ñ€ÐµÐ´ÑтавлениÑ: - - - - On table: - Ð”Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹: - - - - Activate condition: - УÑловие активации: - - - - Code executed: - ИÑполненный код: - - - - View: %1 - ПредÑтавление: %1 - - - - Document generated by SQLiteStudio v%1 on %2 - Документ Ñгенерирован Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SQLiteStudio v%1 в %2 - - - - HtmlExportConfig - - - Maximum number of characters per cell: - МакÑимальное количеÑтво Ñимволов в Ñчейке: - - - - Include data types in first row - Включить типы данных в первую Ñтроку - - - - Column names as first row - Имена Ñтолбцов в первой Ñтроке - - - - Row numbers as first column - Ðомера Ñтрок в первом Ñтолбце - - - - Output format - Выходной формат - - - - Format document (new lines, indentation) - Форматировать документ (переноÑÑ‹ Ñтрок, отÑтупы) - - - - Compress (everything in one line) - Сжать (вÑÑ‘ в одну Ñтроку) - - - - <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> - <p>Когда Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, Ñимволы HTML, такие как &lt;, &gt; и &amp;, не ÑкранируютÑÑ Ð² ÑкÑпортируемых значениÑÑ…. Это позволÑет, к примеру, Ñоздавать документы Ñо ÑÑылками, но также может привеÑти к порче документа HTML (при незакрытых Ñимволах &lt; и &gt;). Будьте внимательны.</p> - - - - Don't escape HTML characters - Ðе Ñкранировать Ñимволы HTML - - - diff --git a/Plugins/HtmlExport/HtmlExport_sk.qm b/Plugins/HtmlExport/HtmlExport_sk.qm deleted file mode 100644 index 29c8745..0000000 Binary files a/Plugins/HtmlExport/HtmlExport_sk.qm and /dev/null differ diff --git a/Plugins/HtmlExport/HtmlExport_sk.ts b/Plugins/HtmlExport/HtmlExport_sk.ts deleted file mode 100644 index 8d427a7..0000000 --- a/Plugins/HtmlExport/HtmlExport_sk.ts +++ /dev/null @@ -1,173 +0,0 @@ - - - - - HtmlExport - - - SQL query results - - - - - - no type - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - virtual - - - - - Exported database: %1 - - - - - Index: %1 - - - - - For table: - - - - - Unique: - - - - - Yes - - - - - No - - - - - Column - - - - - Collating - - - - - Sort order - - - - - Trigger: %1 - - - - - Activated: - - - - - Action: - - - - - On view: - - - - - On table: - - - - - Activate condition: - - - - - Code executed: - - - - - View: %1 - - - - - Document generated by SQLiteStudio v%1 on %2 - - - - - HtmlExportConfig - - - Maximum number of characters per cell: - Maximálny poÄet znakov na bunku: - - - - Include data types in first row - Dátové typy pridaÅ¥ do prvého riadka k názvom stĺpcov - - - - Column names as first row - Názvy stĺpcov ako prvý riadok - - - - Row numbers as first column - Číslovanie riadkov ako prvý stĺpec - - - - Output format - Výstupný formát - - - - Format document (new lines, indentation) - FormátovaÅ¥ dokument (nové riadky, odsadenie) - - - - Compress (everything in one line) - ZhustiÅ¥ (vÅ¡etko do jedného riadka) - - - - <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> - - - - - Don't escape HTML characters - - - - diff --git a/Plugins/HtmlExport/HtmlExport_zh_CN.qm b/Plugins/HtmlExport/HtmlExport_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/Plugins/HtmlExport/HtmlExport_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/Plugins/HtmlExport/HtmlExport_zh_CN.ts b/Plugins/HtmlExport/HtmlExport_zh_CN.ts deleted file mode 100644 index 3ba949c..0000000 --- a/Plugins/HtmlExport/HtmlExport_zh_CN.ts +++ /dev/null @@ -1,173 +0,0 @@ - - - - - HtmlExport - - - SQL query results - - - - - - no type - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - virtual - - - - - Exported database: %1 - - - - - Index: %1 - - - - - For table: - - - - - Unique: - - - - - Yes - - - - - No - - - - - Column - - - - - Collating - - - - - Sort order - - - - - Trigger: %1 - - - - - Activated: - - - - - Action: - - - - - On view: - - - - - On table: - - - - - Activate condition: - - - - - Code executed: - - - - - View: %1 - - - - - Document generated by SQLiteStudio v%1 on %2 - - - - - HtmlExportConfig - - - Maximum number of characters per cell: - - - - - Include data types in first row - - - - - Column names as first row - - - - - Row numbers as first column - - - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> - - - - - Don't escape HTML characters - - - - diff --git a/Plugins/HtmlExport/htmlexport.cpp b/Plugins/HtmlExport/htmlexport.cpp index 86ac13e..e9e4bb5 100644 --- a/Plugins/HtmlExport/htmlexport.cpp +++ b/Plugins/HtmlExport/htmlexport.cpp @@ -608,11 +608,11 @@ QString HtmlExport::compressCss(QString css) bool HtmlExport::init() { - Q_INIT_RESOURCE(htmlexport); + SQLS_INIT_RESOURCE(htmlexport); return GenericExportPlugin::init(); } void HtmlExport::deinit() { - Q_CLEANUP_RESOURCE(htmlexport); + SQLS_CLEANUP_RESOURCE(htmlexport); } diff --git a/Plugins/HtmlExport/htmlexport.qrc b/Plugins/HtmlExport/htmlexport.qrc index 2138f41..77f9482 100644 --- a/Plugins/HtmlExport/htmlexport.qrc +++ b/Plugins/HtmlExport/htmlexport.qrc @@ -5,19 +5,4 @@ htmlexport.ui - - HtmlExport_ro_RO.qm - HtmlExport_de.qm - - - HtmlExport_pl.qm - HtmlExport_ru.qm - HtmlExport_fr.qm - HtmlExport_sk.qm - HtmlExport_zh_CN.qm - - - - - diff --git a/Plugins/HtmlExport/translations/HtmlExport.ts b/Plugins/HtmlExport/translations/HtmlExport.ts new file mode 100644 index 0000000..626cc48 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + + + + + + no type + + + + + + Exported table: %1 + + + + + + Table: %1 + + + + + virtual + + + + + Exported database: %1 + + + + + Index: %1 + + + + + For table: + + + + + Unique: + + + + + Yes + + + + + No + + + + + Column + + + + + Collating + + + + + Sort order + + + + + Trigger: %1 + + + + + Activated: + + + + + Action: + + + + + On view: + + + + + On table: + + + + + Activate condition: + + + + + Code executed: + + + + + View: %1 + + + + + Document generated by SQLiteStudio v%1 on %2 + + + + + HtmlExportConfig + + + Maximum number of characters per cell: + + + + + Include data types in first row + + + + + Column names as first row + + + + + Row numbers as first column + + + + + Output format + + + + + Format document (new lines, indentation) + + + + + Compress (everything in one line) + + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + + Don't escape HTML characters + + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_af_ZA.ts b/Plugins/HtmlExport/translations/HtmlExport_af_ZA.ts new file mode 100644 index 0000000..a0ad2f5 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_af_ZA.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_ar_SA.ts b/Plugins/HtmlExport/translations/HtmlExport_ar_SA.ts new file mode 100644 index 0000000..3213e4e --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_ar_SA.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_ca_ES.ts b/Plugins/HtmlExport/translations/HtmlExport_ca_ES.ts new file mode 100644 index 0000000..6c8d7c9 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_ca_ES.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_cs_CZ.ts b/Plugins/HtmlExport/translations/HtmlExport_cs_CZ.ts new file mode 100644 index 0000000..ce1369f --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_cs_CZ.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_da_DK.ts b/Plugins/HtmlExport/translations/HtmlExport_da_DK.ts new file mode 100644 index 0000000..b48aaab --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_da_DK.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_de_DE.ts b/Plugins/HtmlExport/translations/HtmlExport_de_DE.ts new file mode 100644 index 0000000..0d34916 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_de_DE.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL Abfragenergebnis + + + + + no type + kein Typ + + + + + Exported table: %1 + Exportierte Tabelle: %1 + + + + + Table: %1 + Tabelle: %1 + + + + virtual + virtuell + + + + Exported database: %1 + Exportierte Datenbank: %1 + + + + Index: %1 + Index: %1 + + + + For table: + Für Tabelle: + + + + Unique: + Einzigartig: + + + + Yes + Ja + + + + No + Nein + + + + Column + Spalte + + + + Collating + Sortierung + + + + Sort order + Sortierreihenfolge + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Aktiviert: + + + + Action: + Aktion: + + + + On view: + Auf Ansicht: + + + + On table: + Auf Tabelle: + + + + Activate condition: + Ausführungsbedingung: + + + + Code executed: + Code ausgeführt: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Dokument erstellt von SQLiteStudio v%1 am %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximale Zeichenanzahl pro Zelle: + + + + Include data types in first row + Datentypen in der ersten Zeile einbeziehen + + + + Column names as first row + Spaltenname als erste Zeile verwenden + + + + Row numbers as first column + Zeilennummern als erste Spalte + + + + Output format + Ausgabeformat + + + + Format document (new lines, indentation) + Dokument formatieren (neue Zeilen, Einrückung) + + + + Compress (everything in one line) + Komprimieren (alles in einer Zeile) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + HTML-Zeichen nicht maskieren + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_el_GR.ts b/Plugins/HtmlExport/translations/HtmlExport_el_GR.ts new file mode 100644 index 0000000..23eaa98 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_el_GR.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_en_US.ts b/Plugins/HtmlExport/translations/HtmlExport_en_US.ts new file mode 100644 index 0000000..f210669 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_en_US.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_es_ES.ts b/Plugins/HtmlExport/translations/HtmlExport_es_ES.ts new file mode 100644 index 0000000..0bd4f89 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_es_ES.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + Resultados de la consulta SQL + + + + + no type + sin tipo + + + + + Exported table: %1 + Tabla exportada: %1 + + + + + Table: %1 + Tabla: %1 + + + + virtual + virtual + + + + Exported database: %1 + Base de datos exportada: %1 + + + + Index: %1 + Ãndice: %1 + + + + For table: + Para la tabla: + + + + Unique: + Único: + + + + Yes + Sí + + + + No + No + + + + Column + Columna + + + + Collating + Cotejamiento + + + + Sort order + Ordenar por + + + + Trigger: %1 + Disparador: %1 + + + + Activated: + Activado: + + + + Action: + Acción: + + + + On view: + En la vista: + + + + On table: + En la tabla: + + + + Activate condition: + Condición de activación: + + + + Code executed: + Código ejecutado: + + + + View: %1 + Vista: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Documento generado por SQLiteStudio v%1 el %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Número máximo de caracteres por celda: + + + + Include data types in first row + Incluir tipos de dato en la primera fila + + + + Column names as first row + Nombres de columna como la primera fila + + + + Row numbers as first column + Números de fila como la primera columna + + + + Output format + Formato de salida + + + + Format document (new lines, indentation) + Formatear documento (nuevas líneas, indentación) + + + + Compress (everything in one line) + Comprimir (todo en una sola línea) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>Cuando se activa, caracteres HTML como &lt;, &gt; y & no son escapados en los valores exportados. Esto te permite por ejemplo, exportar documentos con hiper-vínculos funcionales, pero también podría resultar en un documento HTML incorrecto (pares no relacionados de caracteres &lt; y &gt;). Está advertido.</p> + + + + Don't escape HTML characters + No escapar caracteres HTML + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_fa_IR.ts b/Plugins/HtmlExport/translations/HtmlExport_fa_IR.ts new file mode 100644 index 0000000..015fa9e --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_fa_IR.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_fi_FI.ts b/Plugins/HtmlExport/translations/HtmlExport_fi_FI.ts new file mode 100644 index 0000000..5991040 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_fi_FI.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_fr_FR.ts b/Plugins/HtmlExport/translations/HtmlExport_fr_FR.ts new file mode 100644 index 0000000..f3d7900 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_fr_FR.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + Résultats de la requête SQL + + + + + no type + Aucun type + + + + + Exported table: %1 + Tableau exporté : %1 + + + + + Table: %1 + Tableau : %1 + + + + virtual + virtuel + + + + Exported database: %1 + Base de données exportée : %1 + + + + Index: %1 + Index : %1 + + + + For table: + Pour le tableau : + + + + Unique: + Unique : + + + + Yes + Oui + + + + No + Non + + + + Column + Colonne + + + + Collating + Collation + + + + Sort order + Ordre de tri + + + + Trigger: %1 + Déclencheur : %1 + + + + Activated: + Activé : + + + + Action: + Action : + + + + On view: + Sur la vue : + + + + On table: + Sur le tableau : + + + + Activate condition: + Activé la condition : + + + + Code executed: + Exécuter le code : + + + + View: %1 + Vue : %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document généré par SQLiteStudio v%1 de %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Nombre de caractères maximum par cellule : + + + + Include data types in first row + Types données incluses dans la première ligne + + + + Column names as first row + Nom des colonnes dans la première ligne + + + + Row numbers as first column + Numero de ligne dans la première colonne + + + + Output format + Format d’affichage + + + + Format document (new lines, indentation) + Format document (nouvelles ligne, indentation) + + + + Compress (everything in one line) + Condensé (tout en une ligne) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>Lorsqu’il y a des caractères HTML tel que <, > et & ils ne sont pas enlevés des valeurs exportées. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of < and > characters).Soyez attentif.</p> + + + + Don't escape HTML characters + N'enlevez pas les caractères HTML + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_he_IL.ts b/Plugins/HtmlExport/translations/HtmlExport_he_IL.ts new file mode 100644 index 0000000..2eefff1 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_he_IL.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + תוצ×ות ש×ילתת SQL + + + + + no type + ×œ×œ× ×¡×•×’ + + + + + Exported table: %1 + טבלה מיוצת: %1 + + + + + Table: %1 + טבלה: %1 + + + + virtual + מְדֻמֶּה + + + + Exported database: %1 + מסד × ×ª×•× ×™× ×ž×™×•×¦×: %1 + + + + Index: %1 + מִפְתֵּחַ: %1 + + + + For table: + עבור טבלה: + + + + Unique: + ייחודי: + + + + Yes + כן + + + + No + ×œ× + + + + Column + עמודה + + + + Collating + ×רגון + + + + Sort order + סדר מיון + + + + Trigger: %1 + מַזְנֵק: %1 + + + + Activated: + מופעל: + + + + Action: + פעולה: + + + + On view: + ×ין מצג: + + + + On table: + ×ין טבלה: + + + + Activate condition: + שפעול תנ××™: + + + + Code executed: + הקוד בוצע: + + + + View: %1 + מצג: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + מסמך חולל על ידי SQLiteStudio v%1 ב %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + מספר ×ª×•×•×™× ×ž×–×¢×¨×™ בכל ת×: + + + + Include data types in first row + הכללת סוגי × ×ª×•× ×™× ×‘×©×•×¨×” ר×שונה + + + + Column names as first row + שמות עמודות בשורה ר×שונה + + + + Row numbers as first column + מספר שורה כעמודה ר×שונה + + + + Output format + תבנית פלט + + + + Format document (new lines, indentation) + תבנות מסמך (שורה חדשה, ×”×–×—×”) + + + + Compress (everything in one line) + דחיסה (הכל בשורה ×חת) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>×›×שר ×פשרות זו מופעלת, תווי HTML כגון &lt;, &gt; ו- & ×œ× ×™×—×ž×§×• ×‘×™×¦×•× ×¢×¨×›×™×. דבר מ×פשר ×œ×™×™×¦× ×ž×¡×ž×›×™× ×”×ž×•×¤×¢×œ×™× ×‘×מצעות היפר-קישור, ×ך ×–×” עלול ×œ×”×¡×ª×™×™× ×‘×ž×¡×ž×š HTML שגוי (זוגות ×ª×•×•×™× &lt; and &gt; ×œ× ×ª×•×מי×). לתשומת לב. </p> + + + + Don't escape HTML characters + ×œ× ×œ×—×ž×•×§ מתווי HTML + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_hu_HU.ts b/Plugins/HtmlExport/translations/HtmlExport_hu_HU.ts new file mode 100644 index 0000000..2f1f1b5 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_hu_HU.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_it_IT.ts b/Plugins/HtmlExport/translations/HtmlExport_it_IT.ts new file mode 100644 index 0000000..5b9fc82 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_it_IT.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + Risultati della query SQL + + + + + no type + nessun tipo + + + + + Exported table: %1 + Tabella esportata: %1 + + + + + Table: %1 + Tabella: %1 + + + + virtual + virtuale + + + + Exported database: %1 + Database esportato: %1 + + + + Index: %1 + Indice: %1 + + + + For table: + Per tabella: + + + + Unique: + Unico: + + + + Yes + Sì + + + + No + No + + + + Column + Colonna + + + + Collating + Collating + + + + Sort order + Ordina per + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Attivato: + + + + Action: + Azione: + + + + On view: + Su vista: + + + + On table: + Su tabella: + + + + Activate condition: + Condizioni di attivazione: + + + + Code executed: + Codice eseguito: + + + + View: %1 + Vista: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Documento generato da SQLiteStudio v%1 su %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Numero massimo di caratteri per cella: + + + + Include data types in first row + Includi i tipi di dati nella prima riga + + + + Column names as first row + Nomi delle colonne come prima riga + + + + Row numbers as first column + Numeri di riga come prima colonna + + + + Output format + Formato di uscita + + + + Format document (new lines, indentation) + Formato documento (nuova riga, rientro) + + + + Compress (everything in one line) + Comprimi (tutto in una riga) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>Quando abilitato, i caratteri HTML come &lt;, &gt; e & non vengono tolti nei valori esportati. Questo ti permette ad esempio di esportare i documenti abilitati per il collegamento ipertestuale, ma può anche causare un documento HTML errato (con coppie non corrispondenti di &lt; e &gt; caratteri). Sei avvisato.</p> + + + + Don't escape HTML characters + Non togliere caratteri HTML + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_ja_JP.ts b/Plugins/HtmlExport/translations/HtmlExport_ja_JP.ts new file mode 100644 index 0000000..a34560b --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_ja_JP.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_kaa.ts b/Plugins/HtmlExport/translations/HtmlExport_kaa.ts new file mode 100644 index 0000000..ee2b0e1 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_kaa.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_ko_KR.ts b/Plugins/HtmlExport/translations/HtmlExport_ko_KR.ts new file mode 100644 index 0000000..54c8455 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_ko_KR.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + 예 + + + + No + 아니오 + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_nl_NL.ts b/Plugins/HtmlExport/translations/HtmlExport_nl_NL.ts new file mode 100644 index 0000000..67b1063 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_nl_NL.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_no_NO.ts b/Plugins/HtmlExport/translations/HtmlExport_no_NO.ts new file mode 100644 index 0000000..a845703 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_no_NO.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_pl_PL.ts b/Plugins/HtmlExport/translations/HtmlExport_pl_PL.ts new file mode 100644 index 0000000..01d86c4 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_pl_PL.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + Wyniki zapytania SQL + + + + + no type + brak typu + + + + + Exported table: %1 + Eksportowana tabela: %1 + + + + + Table: %1 + Tabela: %1 + + + + virtual + wirtualna + + + + Exported database: %1 + Eksportowana baza: %1 + + + + Index: %1 + Indeks: %1 + + + + For table: + Dla tabeli: + + + + Unique: + Unikalny: + + + + Yes + Tak + + + + No + Nie + + + + Column + Kolumna + + + + Collating + Zestawienie + + + + Sort order + Sortowanie + + + + Trigger: %1 + Wyzwalacz: %1 + + + + Activated: + Aktywowany: + + + + Action: + Akcja: + + + + On view: + Na widoku: + + + + On table: + Na tabeli: + + + + Activate condition: + Warunek aktywacji: + + + + Code executed: + Wykonywany kod: + + + + View: %1 + Widok: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Dokument wygenerowany przez SQLiteStudio v%1 dnia %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maksymalna liczba znaków w komórce: + + + + Include data types in first row + Dodaj typy danych w pierwszym wierszu + + + + Column names as first row + Nazwy kolumn w pierwszym wierszu + + + + Row numbers as first column + Numery wierszy w pierwszej kolumnie + + + + Output format + Format wyjÅ›ciowy + + + + Format document (new lines, indentation) + Formatuj dokument (nowe linie, wciÄ™cia) + + + + Compress (everything in one line) + Kompresuj (wszystko w jednej linii) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>Kiedy włączone, znaki HTML takie jak &lt;, &gt; i & nie bÄ™dÄ… podmieniane w wyeksportowanych wartoÅ›ciach. Pozwala to przykÅ‚adowo na eksportowanie odnoÅ›ników w dokumencie, ale może też spowodować, ze dokument HTML bÄ™dzie niepoprawny (niedopasowane pary znaków &lt; i &gt;). Miej tego Å›wiadomość.</p> + + + + Don't escape HTML characters + Nie podmieniaj znaków HTML. + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_pt_BR.ts b/Plugins/HtmlExport/translations/HtmlExport_pt_BR.ts new file mode 100644 index 0000000..c358d8e --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_pt_BR.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + Resultados da consulta SQL + + + + + no type + sem tipo + + + + + Exported table: %1 + Tabela exportada: %1 + + + + + Table: %1 + Tabela: %1 + + + + virtual + virtual + + + + Exported database: %1 + Banco de dados exportado: %1 + + + + Index: %1 + Ãndice: %1 + + + + For table: + Para a tabela: + + + + Unique: + Único: + + + + Yes + Sim + + + + No + Não + + + + Column + Coluna + + + + Collating + Collating + + + + Sort order + Ordem de classificação + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Ativado: + + + + Action: + Ação: + + + + On view: + Na visualização: + + + + On table: + Na tabela: + + + + Activate condition: + Ativar condição: + + + + Code executed: + Código executado: + + + + View: %1 + Visualizar: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Documento gerado por SQLiteStudio v%1 em %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Número máximo de caracteres por célula: + + + + Include data types in first row + Incluir tipos de dados na primeira linha + + + + Column names as first row + Nomes de coluna como primeira linha + + + + Row numbers as first column + Linha de números como a primeira coluna + + + + Output format + Formato de saída + + + + Format document (new lines, indentation) + Formatar documento (novas linhas, recuo) + + + + Compress (everything in one line) + Comprimir (tudo em uma linha) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>Quando habilitado, caracteres HTML como &lt;, &gt; e & não são escapados nos valores exportados. Isso permite a você, por exemplo, exportar documentos habilitados de hiper-links, mas também pode resultar em documentos HTML incorretos (pares não correspondentes de &lt; e &gt; caracteres). Estejam avisados.</p> + + + + Don't escape HTML characters + Não é possível caracteres HTML + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_pt_PT.ts b/Plugins/HtmlExport/translations/HtmlExport_pt_PT.ts new file mode 100644 index 0000000..cc40c09 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_pt_PT.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_ro_RO.ts b/Plugins/HtmlExport/translations/HtmlExport_ro_RO.ts new file mode 100644 index 0000000..28cfd86 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_ro_RO.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_ru_RU.ts b/Plugins/HtmlExport/translations/HtmlExport_ru_RU.ts new file mode 100644 index 0000000..879f711 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_ru_RU.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + Результаты запроÑа SQL + + + + + no type + без типа + + + + + Exported table: %1 + ЭкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°: %1 + + + + + Table: %1 + Таблица: %1 + + + + virtual + Ð²Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ð°Ñ + + + + Exported database: %1 + ЭкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных: %1 + + + + Index: %1 + ИндекÑ: %1 + + + + For table: + Ð”Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹: + + + + Unique: + Уникальный: + + + + Yes + Да + + + + No + Ðет + + + + Column + Столбец + + + + Collating + Сравнение + + + + Sort order + ПорÑдок Ñортировки + + + + Trigger: %1 + Триггер: %1 + + + + Activated: + Ðктивирован: + + + + Action: + ДейÑтвие: + + + + On view: + Ð”Ð»Ñ Ð¿Ñ€ÐµÐ´ÑтавлениÑ: + + + + On table: + Ð”Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹: + + + + Activate condition: + УÑловие активации: + + + + Code executed: + ИÑполненный код: + + + + View: %1 + ПредÑтавление: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Документ Ñгенерирован Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SQLiteStudio v%1 в %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + МакÑимальное количеÑтво Ñимволов в Ñчейке: + + + + Include data types in first row + Включить типы данных в первую Ñтроку + + + + Column names as first row + Имена Ñтолбцов в первой Ñтроке + + + + Row numbers as first column + Ðомера Ñтрок в первом Ñтолбце + + + + Output format + Выходной формат + + + + Format document (new lines, indentation) + Форматировать документ (переноÑÑ‹ Ñтрок, отÑтупы) + + + + Compress (everything in one line) + Сжать (вÑÑ‘ в одну Ñтроку) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>Когда Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, Ñимволы HTML, такие как &lt;, &gt; и &, не ÑкранируютÑÑ Ð² ÑкÑпортируемых значениÑÑ…. Это позволÑет, к примеру, Ñоздавать документы Ñо ÑÑылками, но также может привеÑти к порче документа HTML (при незакрытых Ñимволах &lt; и &gt;). Будьте внимательны.</p> + + + + Don't escape HTML characters + Ðе Ñкранировать Ñимволы HTML + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_sk_SK.ts b/Plugins/HtmlExport/translations/HtmlExport_sk_SK.ts new file mode 100644 index 0000000..8173485 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_sk_SK.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximálny poÄet znakov na bunku: + + + + Include data types in first row + Dátové typy pridaÅ¥ do prvého riadka k názvom stĺpcov + + + + Column names as first row + Názvy stĺpcov ako prvý riadok + + + + Row numbers as first column + Číslovanie riadkov ako prvý stĺpec + + + + Output format + Výstupný formát + + + + Format document (new lines, indentation) + FormátovaÅ¥ dokument (nové riadky, odsadenie) + + + + Compress (everything in one line) + ZhustiÅ¥ (vÅ¡etko do jedného riadka) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_sr_SP.ts b/Plugins/HtmlExport/translations/HtmlExport_sr_SP.ts new file mode 100644 index 0000000..d12c46d --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_sr_SP.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_sv_SE.ts b/Plugins/HtmlExport/translations/HtmlExport_sv_SE.ts new file mode 100644 index 0000000..8ebe10a --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_sv_SE.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_tr_TR.ts b/Plugins/HtmlExport/translations/HtmlExport_tr_TR.ts new file mode 100644 index 0000000..91ed1dc --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_tr_TR.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL sorgu sonucu + + + + + no type + tip yok + + + + + Exported table: %1 + Çıkartılan tablo: %1 + + + + + Table: %1 + Tablo: %1 + + + + virtual + sanal + + + + Exported database: %1 + Çıkartılan veritabanı: %1 + + + + Index: %1 + Indeks: %1 + + + + For table: + Hedef tablo: + + + + Unique: + Unique: + + + + Yes + Evet + + + + No + Hayır + + + + Column + Kolon + + + + Collating + Collating + + + + Sort order + Sıralama düzeni + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + EtkinleÅŸtirildi: + + + + Action: + Eylem: + + + + On view: + View: + + + + On table: + Tablo: + + + + Activate condition: + AktifleÅŸtirme koÅŸulu: + + + + Code executed: + Çalıştırılan kod: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + SQLiteStudio ile üretilen döküman %2 üzerinde v%1 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Hücre başına maksimum karakter sayısı: + + + + Include data types in first row + İlk satırda veri tiplerini içer + + + + Column names as first row + İlk satıda kolon isimleri + + + + Row numbers as first column + İlk kolon satır sayıları + + + + Output format + Çıktı formatı + + + + Format document (new lines, indentation) + Döküman biçimi (yeni satırlar, girintiler) + + + + Compress (everything in one line) + Sıkıştır (her ÅŸeyi bir satırda) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>Aktif olduÄŸunda, &lt;, &gt; ve & gibi HTML karakterleri çıkartılan dosyalarda kaçış karakteri olarak deÄŸerlendirilmez. Bu size hyper-link içeren dökümanları çıkarmayı saÄŸlar, fakat düzgün olmayan HTLM dökümanına da sebep olabilir (eÅŸleÅŸmeyen &lt; ve &gt; karakterleri gibi). Dikkat edin.</p> + + + + Don't escape HTML characters + HTML karakterlerini ayırma + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_uk_UA.ts b/Plugins/HtmlExport/translations/HtmlExport_uk_UA.ts new file mode 100644 index 0000000..8467bb7 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_uk_UA.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_vi_VN.ts b/Plugins/HtmlExport/translations/HtmlExport_vi_VN.ts new file mode 100644 index 0000000..19e296b --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_vi_VN.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL query results + + + + + no type + no type + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + virtual + virtual + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + For table: + For table: + + + + Unique: + Unique: + + + + Yes + Yes + + + + No + No + + + + Column + Column + + + + Collating + Collating + + + + Sort order + Sort order + + + + Trigger: %1 + Trigger: %1 + + + + Activated: + Activated: + + + + Action: + Action: + + + + On view: + On view: + + + + On table: + On table: + + + + Activate condition: + Activate condition: + + + + Code executed: + Code executed: + + + + View: %1 + View: %1 + + + + Document generated by SQLiteStudio v%1 on %2 + Document generated by SQLiteStudio v%1 on %2 + + + + HtmlExportConfig + + + Maximum number of characters per cell: + Maximum number of characters per cell: + + + + Include data types in first row + Include data types in first row + + + + Column names as first row + Column names as first row + + + + Row numbers as first column + Row numbers as first column + + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>When enabled, HTML characters such as &lt;, &gt; and & are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + + + + Don't escape HTML characters + Don't escape HTML characters + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_zh_CN.ts b/Plugins/HtmlExport/translations/HtmlExport_zh_CN.ts new file mode 100644 index 0000000..2289b04 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_zh_CN.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL 查询结果 + + + + + no type + 无类型 + + + + + Exported table: %1 + 导出的表:%1 + + + + + Table: %1 + 表:%1 + + + + virtual + 虚拟 + + + + Exported database: %1 + 导出的数æ®åº“:%1 + + + + Index: %1 + 索引: %1 + + + + For table: + 表: + + + + Unique: + 唯一: + + + + Yes + 是 + + + + No + å¦ + + + + Column + 列 + + + + Collating + å­—ç¬¦åº + + + + Sort order + æŽ’åºæ–¹å¼ + + + + Trigger: %1 + 触å‘器:%1 + + + + Activated: + 激活时间: + + + + Action: + æ“作: + + + + On view: + 视图: + + + + On table: + 表: + + + + Activate condition: + 激活æ¡ä»¶ï¼š + + + + Code executed: + ä»£ç æ‰§è¡Œï¼š + + + + View: %1 + 视图:%1 + + + + Document generated by SQLiteStudio v%1 on %2 + ç”± SQLiteStudio v%1 在 %2 生æˆçš„æ–‡æ¡£ + + + + HtmlExportConfig + + + Maximum number of characters per cell: + æ¯ä¸ªå•元格的最大字符数: + + + + Include data types in first row + ç¬¬ä¸€è¡ŒåŒ…å«æ•°æ®ç±»åž‹ + + + + Column names as first row + 列å作为首行 + + + + Row numbers as first column + 行å·ä½œä¸ºé¦–列 + + + + Output format + è¾“å‡ºæ ¼å¼ + + + + Format document (new lines, indentation) + æ ¼å¼åŒ–的文档 (新行,缩进) + + + + Compress (everything in one line) + 压缩(å•行文件) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>如果å¯ç”¨ï¼Œå¯¼å‡ºå€¼ä¸­ä¸ä¼šè½¬ä¹‰è¯¸å¦‚ &lt;ã€&gt; å’Œ & ç­‰ HTML 字符。请å°å¿ƒï¼Œè¿™ä½¿æ‚¨èƒ½å¯¼å‡ºæœ‰è¶…链接的文档,但也å¯èƒ½å¯¼è‡´ä¸æ­£ç¡®çš„ HTML 文档(ä¸é…对的 &lt; å’Œ &gt; 字符)。</p> + + + + Don't escape HTML characters + ä¸è½¬ä¹‰ HTML 字符 + + + diff --git a/Plugins/HtmlExport/translations/HtmlExport_zh_TW.ts b/Plugins/HtmlExport/translations/HtmlExport_zh_TW.ts new file mode 100644 index 0000000..c0d4753 --- /dev/null +++ b/Plugins/HtmlExport/translations/HtmlExport_zh_TW.ts @@ -0,0 +1,173 @@ + + + + + HtmlExport + + + SQL query results + SQL æŸ¥è©¢çµæžœ + + + + + no type + 無型別 + + + + + Exported table: %1 + 匯出的表:%1 + + + + + Table: %1 + 表:%1 + + + + virtual + 虛擬 + + + + Exported database: %1 + 匯出的資料庫:%1 + + + + Index: %1 + 索引: %1 + + + + For table: + 表: + + + + Unique: + 唯一: + + + + Yes + 是 + + + + No + å¦ + + + + Column + 列 + + + + Collating + å­—å…ƒåº + + + + Sort order + æŽ’åºæ–¹å¼ + + + + Trigger: %1 + 觸發器:%1 + + + + Activated: + 啟用時間: + + + + Action: + æ“作: + + + + On view: + 於檢視: + + + + On table: + 於表: + + + + Activate condition: + 啟用æ¢ä»¶ï¼š + + + + Code executed: + 執行程å¼ç¢¼ï¼š + + + + View: %1 + 檢視:%1 + + + + Document generated by SQLiteStudio v%1 on %2 + ç”± SQLiteStudio v%1 在 %2 生æˆçš„æ–‡ä»¶ + + + + HtmlExportConfig + + + Maximum number of characters per cell: + æ¯å€‹å–®å…ƒæ ¼çš„æœ€å¤§å­—元數: + + + + Include data types in first row + 第一行包å«è³‡æ–™åž‹åˆ¥ + + + + Column names as first row + 列å作為首行 + + + + Row numbers as first column + 行號作為首列 + + + + Output format + è¼¸å‡ºæ ¼å¼ + + + + Format document (new lines, indentation) + æ ¼å¼åŒ–的文件 (新行,縮排) + + + + Compress (everything in one line) + 壓縮 (單行檔案) + + + + <p>When enabled, HTML characters such as &lt;, &gt; and &amp; are not escaped in exported values. This allows you for example to export hyper-link enabled documents, but it also may result in incorrect HTML document (unmatched pairs of &lt; and &gt; characters). Be warned.</p> + <p>å¦‚æžœå•Ÿç”¨ï¼ŒåŒ¯å‡ºå€¼ä¸­ä¸æœƒè½‰ç¾©è«¸å¦‚ &lt;ã€&gt; å’Œ & ç­‰ HTML 字元。請å°å¿ƒï¼Œé€™ä½¿æ‚¨èƒ½åŒ¯å‡ºæœ‰è¶…連çµçš„æ–‡ä»¶ï¼Œä½†ä¹Ÿå¯èƒ½å°Žè‡´ä¸æ­£ç¢ºçš„ HTML 文件 (ä¸é…å°çš„ &lt; å’Œ &gt; å­—å…ƒ)。</p> + + + + Don't escape HTML characters + ä¸è½‰ç¾© HTML å­—å…ƒ + + + diff --git a/Plugins/JsonExport/JsonExport.pro b/Plugins/JsonExport/JsonExport.pro index 9ecf077..e496894 100644 --- a/Plugins/JsonExport/JsonExport.pro +++ b/Plugins/JsonExport/JsonExport.pro @@ -26,31 +26,3 @@ FORMS += \ RESOURCES += \ jsonexport.qrc - - -TRANSLATIONS += JsonExport_ro_RO.ts \ - JsonExport_de.ts \ - JsonExport_it.ts \ - JsonExport_zh_CN.ts \ - JsonExport_sk.ts \ - JsonExport_ru.ts \ - JsonExport_pt_BR.ts \ - JsonExport_fr.ts \ - JsonExport_es.ts \ - JsonExport_pl.ts - - - - - - - - - - - - - - - - diff --git a/Plugins/JsonExport/JsonExport_de.qm b/Plugins/JsonExport/JsonExport_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/JsonExport/JsonExport_de.qm and /dev/null differ diff --git a/Plugins/JsonExport/JsonExport_de.ts b/Plugins/JsonExport/JsonExport_de.ts deleted file mode 100644 index 15fece3..0000000 --- a/Plugins/JsonExport/JsonExport_de.ts +++ /dev/null @@ -1,22 +0,0 @@ - - - - - JsonExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - diff --git a/Plugins/JsonExport/JsonExport_es.qm b/Plugins/JsonExport/JsonExport_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/JsonExport/JsonExport_es.qm and /dev/null differ diff --git a/Plugins/JsonExport/JsonExport_es.ts b/Plugins/JsonExport/JsonExport_es.ts deleted file mode 100644 index 38209b9..0000000 --- a/Plugins/JsonExport/JsonExport_es.ts +++ /dev/null @@ -1,22 +0,0 @@ - - - - - JsonExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - diff --git a/Plugins/JsonExport/JsonExport_fr.qm b/Plugins/JsonExport/JsonExport_fr.qm deleted file mode 100644 index eb8c8f5..0000000 Binary files a/Plugins/JsonExport/JsonExport_fr.qm and /dev/null differ diff --git a/Plugins/JsonExport/JsonExport_fr.ts b/Plugins/JsonExport/JsonExport_fr.ts deleted file mode 100644 index 171ddf4..0000000 --- a/Plugins/JsonExport/JsonExport_fr.ts +++ /dev/null @@ -1,22 +0,0 @@ - - - - - JsonExportConfig - - - Output format - Format d’affichage - - - - Format document (new lines, indentation) - Format document (nouvelles lignes, indentation) - - - - Compress (everything in one line) - Condensé (tout sur une ligne) - - - diff --git a/Plugins/JsonExport/JsonExport_it.qm b/Plugins/JsonExport/JsonExport_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/JsonExport/JsonExport_it.qm and /dev/null differ diff --git a/Plugins/JsonExport/JsonExport_it.ts b/Plugins/JsonExport/JsonExport_it.ts deleted file mode 100644 index 74b0169..0000000 --- a/Plugins/JsonExport/JsonExport_it.ts +++ /dev/null @@ -1,22 +0,0 @@ - - - - - JsonExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - diff --git a/Plugins/JsonExport/JsonExport_pl.qm b/Plugins/JsonExport/JsonExport_pl.qm deleted file mode 100644 index 3ed3bcd..0000000 Binary files a/Plugins/JsonExport/JsonExport_pl.qm and /dev/null differ diff --git a/Plugins/JsonExport/JsonExport_pl.ts b/Plugins/JsonExport/JsonExport_pl.ts deleted file mode 100644 index 4a28cc2..0000000 --- a/Plugins/JsonExport/JsonExport_pl.ts +++ /dev/null @@ -1,22 +0,0 @@ - - - - - JsonExportConfig - - - Output format - Format wyjÅ›ciowy - - - - Format document (new lines, indentation) - Formatuj dokument (nowe linie, wciÄ™cia) - - - - Compress (everything in one line) - Kompresuj (wszystko w jednej linii) - - - diff --git a/Plugins/JsonExport/JsonExport_pt_BR.qm b/Plugins/JsonExport/JsonExport_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/JsonExport/JsonExport_pt_BR.qm and /dev/null differ diff --git a/Plugins/JsonExport/JsonExport_pt_BR.ts b/Plugins/JsonExport/JsonExport_pt_BR.ts deleted file mode 100644 index ba1735d..0000000 --- a/Plugins/JsonExport/JsonExport_pt_BR.ts +++ /dev/null @@ -1,22 +0,0 @@ - - - - - JsonExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - diff --git a/Plugins/JsonExport/JsonExport_ro_RO.qm b/Plugins/JsonExport/JsonExport_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/JsonExport/JsonExport_ro_RO.qm and /dev/null differ diff --git a/Plugins/JsonExport/JsonExport_ro_RO.ts b/Plugins/JsonExport/JsonExport_ro_RO.ts deleted file mode 100644 index 1264580..0000000 --- a/Plugins/JsonExport/JsonExport_ro_RO.ts +++ /dev/null @@ -1,22 +0,0 @@ - - - - - JsonExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - diff --git a/Plugins/JsonExport/JsonExport_ru.qm b/Plugins/JsonExport/JsonExport_ru.qm deleted file mode 100644 index 57acd3e..0000000 Binary files a/Plugins/JsonExport/JsonExport_ru.qm and /dev/null differ diff --git a/Plugins/JsonExport/JsonExport_ru.ts b/Plugins/JsonExport/JsonExport_ru.ts deleted file mode 100644 index 61b24eb..0000000 --- a/Plugins/JsonExport/JsonExport_ru.ts +++ /dev/null @@ -1,22 +0,0 @@ - - - - - JsonExportConfig - - - Output format - Выходной формат - - - - Format document (new lines, indentation) - Форматировать документ (переноÑÑ‹ Ñтрок, отÑтупы) - - - - Compress (everything in one line) - Сжать (вÑÑ‘ в одну Ñтроку) - - - diff --git a/Plugins/JsonExport/JsonExport_sk.qm b/Plugins/JsonExport/JsonExport_sk.qm deleted file mode 100644 index c854235..0000000 Binary files a/Plugins/JsonExport/JsonExport_sk.qm and /dev/null differ diff --git a/Plugins/JsonExport/JsonExport_sk.ts b/Plugins/JsonExport/JsonExport_sk.ts deleted file mode 100644 index 9049c29..0000000 --- a/Plugins/JsonExport/JsonExport_sk.ts +++ /dev/null @@ -1,22 +0,0 @@ - - - - - JsonExportConfig - - - Output format - Výstupný formát - - - - Format document (new lines, indentation) - FormátovaÅ¥ dokument (nové riadky, odsadenie) - - - - Compress (everything in one line) - ZhustiÅ¥ (vÅ¡etko do jedného riadka) - - - diff --git a/Plugins/JsonExport/JsonExport_zh_CN.qm b/Plugins/JsonExport/JsonExport_zh_CN.qm deleted file mode 100644 index 3d08187..0000000 Binary files a/Plugins/JsonExport/JsonExport_zh_CN.qm and /dev/null differ diff --git a/Plugins/JsonExport/JsonExport_zh_CN.ts b/Plugins/JsonExport/JsonExport_zh_CN.ts deleted file mode 100644 index 82b3b1d..0000000 --- a/Plugins/JsonExport/JsonExport_zh_CN.ts +++ /dev/null @@ -1,22 +0,0 @@ - - - - - JsonExportConfig - - - Output format - è¾“å‡ºæ ¼å¼ - - - - Format document (new lines, indentation) - æ ¼å¼åŒ–的文本(多行,缩进) - - - - Compress (everything in one line) - 压缩(产生å•行文件) - - - diff --git a/Plugins/JsonExport/jsonexport.cpp b/Plugins/JsonExport/jsonexport.cpp index 961169e..92842da 100644 --- a/Plugins/JsonExport/jsonexport.cpp +++ b/Plugins/JsonExport/jsonexport.cpp @@ -95,7 +95,8 @@ bool JsonExport::exportTable(const QString& database, const QString& table, cons writeValue("type", "table"); writeValue("database", database); writeValue("name", table); - writeValue("withoutRowId", createTable->withOutRowId.isNull()); + writeValue("withoutRowId", createTable->withOutRowId); + writeValue("strict", createTable->strict); writeValue("ddl", ddl); beginArray("columns"); @@ -266,13 +267,13 @@ bool JsonExport::beforeExport() bool JsonExport::init() { - Q_INIT_RESOURCE(jsonexport); + SQLS_INIT_RESOURCE(jsonexport); return GenericExportPlugin::init(); } void JsonExport::deinit() { - Q_CLEANUP_RESOURCE(jsonexport); + SQLS_CLEANUP_RESOURCE(jsonexport); } void JsonExport::setupConfig() diff --git a/Plugins/JsonExport/jsonexport.qrc b/Plugins/JsonExport/jsonexport.qrc index feabbc0..19ea921 100644 --- a/Plugins/JsonExport/jsonexport.qrc +++ b/Plugins/JsonExport/jsonexport.qrc @@ -2,19 +2,4 @@ jsonexport.ui - - JsonExport_ro_RO.qm - JsonExport_de.qm - - - JsonExport_pl.qm - JsonExport_ru.qm - JsonExport_fr.qm - JsonExport_sk.qm - JsonExport_zh_CN.qm - - - - - diff --git a/Plugins/JsonExport/translations/JsonExport.ts b/Plugins/JsonExport/translations/JsonExport.ts new file mode 100644 index 0000000..5c1a943 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + + + + + Format document (new lines, indentation) + + + + + Compress (everything in one line) + + + + diff --git a/Plugins/JsonExport/translations/JsonExport_af_ZA.ts b/Plugins/JsonExport/translations/JsonExport_af_ZA.ts new file mode 100644 index 0000000..e9bc162 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_af_ZA.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_ar_SA.ts b/Plugins/JsonExport/translations/JsonExport_ar_SA.ts new file mode 100644 index 0000000..bf8df73 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_ar_SA.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_ca_ES.ts b/Plugins/JsonExport/translations/JsonExport_ca_ES.ts new file mode 100644 index 0000000..9201d3e --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_ca_ES.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_cs_CZ.ts b/Plugins/JsonExport/translations/JsonExport_cs_CZ.ts new file mode 100644 index 0000000..a1e0c06 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_cs_CZ.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_da_DK.ts b/Plugins/JsonExport/translations/JsonExport_da_DK.ts new file mode 100644 index 0000000..790ff5e --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_da_DK.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_de_DE.ts b/Plugins/JsonExport/translations/JsonExport_de_DE.ts new file mode 100644 index 0000000..d0ee79e --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_de_DE.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Ausgabeformat + + + + Format document (new lines, indentation) + Dokument formatieren (neue Zeilen, Einrückung) + + + + Compress (everything in one line) + Komprimieren (alles in einer Zeile) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_el_GR.ts b/Plugins/JsonExport/translations/JsonExport_el_GR.ts new file mode 100644 index 0000000..16a856e --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_el_GR.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_en_US.ts b/Plugins/JsonExport/translations/JsonExport_en_US.ts new file mode 100644 index 0000000..054c08c --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_en_US.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_es_ES.ts b/Plugins/JsonExport/translations/JsonExport_es_ES.ts new file mode 100644 index 0000000..41f91bd --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_es_ES.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Formato de salida + + + + Format document (new lines, indentation) + Formatear documento (nuevas líneas, indentación) + + + + Compress (everything in one line) + Comprimir (todo en una sola línea) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_fa_IR.ts b/Plugins/JsonExport/translations/JsonExport_fa_IR.ts new file mode 100644 index 0000000..4420680 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_fa_IR.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_fi_FI.ts b/Plugins/JsonExport/translations/JsonExport_fi_FI.ts new file mode 100644 index 0000000..b600487 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_fi_FI.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_fr_FR.ts b/Plugins/JsonExport/translations/JsonExport_fr_FR.ts new file mode 100644 index 0000000..b1fac8e --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_fr_FR.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Format d’affichage + + + + Format document (new lines, indentation) + Format document (nouvelles lignes, indentation) + + + + Compress (everything in one line) + Condensé (tout sur une ligne) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_he_IL.ts b/Plugins/JsonExport/translations/JsonExport_he_IL.ts new file mode 100644 index 0000000..b997e9d --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_he_IL.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + תבנית פלט + + + + Format document (new lines, indentation) + תבנות מסמך (שורה חדשה, ×”×–×—×”) + + + + Compress (everything in one line) + דחיסה (הכל בשורה ×חת) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_hu_HU.ts b/Plugins/JsonExport/translations/JsonExport_hu_HU.ts new file mode 100644 index 0000000..c19a4ca --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_hu_HU.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Kimenet formátum + + + + Format document (new lines, indentation) + Dokumentum formázása (új sorok, behúzások) + + + + Compress (everything in one line) + Tömör forma (mindent egy sorba) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_it_IT.ts b/Plugins/JsonExport/translations/JsonExport_it_IT.ts new file mode 100644 index 0000000..5eb098d --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_it_IT.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Formato di uscita + + + + Format document (new lines, indentation) + Formato documento (nuove linee, indentazione) + + + + Compress (everything in one line) + Comprimi (tutto in una riga) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_ja_JP.ts b/Plugins/JsonExport/translations/JsonExport_ja_JP.ts new file mode 100644 index 0000000..adee048 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_ja_JP.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_kaa.ts b/Plugins/JsonExport/translations/JsonExport_kaa.ts new file mode 100644 index 0000000..69e59e7 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_kaa.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_ko_KR.ts b/Plugins/JsonExport/translations/JsonExport_ko_KR.ts new file mode 100644 index 0000000..7d76012 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_ko_KR.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_nl_NL.ts b/Plugins/JsonExport/translations/JsonExport_nl_NL.ts new file mode 100644 index 0000000..0128d5f --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_nl_NL.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_no_NO.ts b/Plugins/JsonExport/translations/JsonExport_no_NO.ts new file mode 100644 index 0000000..92bef14 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_no_NO.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_pl_PL.ts b/Plugins/JsonExport/translations/JsonExport_pl_PL.ts new file mode 100644 index 0000000..3891180 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_pl_PL.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Format wyjÅ›ciowy + + + + Format document (new lines, indentation) + Formatuj dokument (nowe linie, wciÄ™cia) + + + + Compress (everything in one line) + Kompresuj (wszystko w jednej linii) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_pt_BR.ts b/Plugins/JsonExport/translations/JsonExport_pt_BR.ts new file mode 100644 index 0000000..68c17c0 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_pt_BR.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Formato de saída + + + + Format document (new lines, indentation) + Formatar documento (novas linhas, recuo) + + + + Compress (everything in one line) + Comprimir (tudo em uma linha) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_pt_PT.ts b/Plugins/JsonExport/translations/JsonExport_pt_PT.ts new file mode 100644 index 0000000..7e89e41 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_pt_PT.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_ro_RO.ts b/Plugins/JsonExport/translations/JsonExport_ro_RO.ts new file mode 100644 index 0000000..805e20d --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_ro_RO.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_ru_RU.ts b/Plugins/JsonExport/translations/JsonExport_ru_RU.ts new file mode 100644 index 0000000..4c93197 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_ru_RU.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Выходной формат + + + + Format document (new lines, indentation) + Форматировать документ (переноÑÑ‹ Ñтрок, отÑтупы) + + + + Compress (everything in one line) + Сжать (вÑÑ‘ в одну Ñтроку) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_sk_SK.ts b/Plugins/JsonExport/translations/JsonExport_sk_SK.ts new file mode 100644 index 0000000..dc99e74 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_sk_SK.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Výstupný formát + + + + Format document (new lines, indentation) + FormátovaÅ¥ dokument (nové riadky, odsadenie) + + + + Compress (everything in one line) + ZhustiÅ¥ (vÅ¡etko do jedného riadka) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_sr_SP.ts b/Plugins/JsonExport/translations/JsonExport_sr_SP.ts new file mode 100644 index 0000000..84720a6 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_sr_SP.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_sv_SE.ts b/Plugins/JsonExport/translations/JsonExport_sv_SE.ts new file mode 100644 index 0000000..6cb045d --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_sv_SE.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_tr_TR.ts b/Plugins/JsonExport/translations/JsonExport_tr_TR.ts new file mode 100644 index 0000000..924fd85 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_tr_TR.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Çıktı formatı + + + + Format document (new lines, indentation) + Döküman biçimi (yeni satırlar, girintiler) + + + + Compress (everything in one line) + Sıkıştır (her ÅŸeyi bir satırda) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_uk_UA.ts b/Plugins/JsonExport/translations/JsonExport_uk_UA.ts new file mode 100644 index 0000000..b10ecd4 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_uk_UA.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_vi_VN.ts b/Plugins/JsonExport/translations/JsonExport_vi_VN.ts new file mode 100644 index 0000000..741d86f --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_vi_VN.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_zh_CN.ts b/Plugins/JsonExport/translations/JsonExport_zh_CN.ts new file mode 100644 index 0000000..092ab94 --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_zh_CN.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + è¾“å‡ºæ ¼å¼ + + + + Format document (new lines, indentation) + æ ¼å¼åŒ–的文本(多行,缩进) + + + + Compress (everything in one line) + 压缩(å•行文件) + + + diff --git a/Plugins/JsonExport/translations/JsonExport_zh_TW.ts b/Plugins/JsonExport/translations/JsonExport_zh_TW.ts new file mode 100644 index 0000000..dd5d74a --- /dev/null +++ b/Plugins/JsonExport/translations/JsonExport_zh_TW.ts @@ -0,0 +1,22 @@ + + + + + JsonExportConfig + + + Output format + è¼¸å‡ºæ ¼å¼ + + + + Format document (new lines, indentation) + æ ¼å¼åŒ–的文字 (多行,縮排) + + + + Compress (everything in one line) + 壓縮 (單行檔案) + + + diff --git a/Plugins/MultiEditorImage/MultiEditorImage.pro b/Plugins/MultiEditorImage/MultiEditorImage.pro index 09b15f3..c337e48 100644 --- a/Plugins/MultiEditorImage/MultiEditorImage.pro +++ b/Plugins/MultiEditorImage/MultiEditorImage.pro @@ -17,16 +17,3 @@ OTHER_FILES += \ RESOURCES += \ multieditorimage.qrc - -TRANSLATIONS += \ - MultiEditorImage_ro_RO.ts\ - MultiEditorImage_de.ts\ - MultiEditorImage_it.ts\ - MultiEditorImage_zh_CN.ts\ - MultiEditorImage_sk.ts\ - MultiEditorImage_ru.ts\ - MultiEditorImage_pt_BR.ts\ - MultiEditorImage_fr.ts\ - MultiEditorImage_es.ts\ - MultiEditorImage_pl.ts - diff --git a/Plugins/MultiEditorImage/MultiEditorImage_de.qm b/Plugins/MultiEditorImage/MultiEditorImage_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/MultiEditorImage/MultiEditorImage_de.qm and /dev/null differ diff --git a/Plugins/MultiEditorImage/MultiEditorImage_de.ts b/Plugins/MultiEditorImage/MultiEditorImage_de.ts deleted file mode 100644 index dd7fa7e..0000000 --- a/Plugins/MultiEditorImage/MultiEditorImage_de.ts +++ /dev/null @@ -1,81 +0,0 @@ - - - - - MultiEditorImage - - - Load from file - - - - - Store in a file - - - - - Zoom in by 25% - - - - - Zoom out by 25% - - - - - Reset zoom - - - - - Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) - - - - - Open image - - - - - Could not open file %1 for reading. - Could not nopen file %1 for reading. - - - - - All files (*) - - - - - Save image - - - - - Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) - - - - - Could not nopen file %1 for writting. - - - - - Could not write image into the file %1 - - - - - MultiEditorImagePlugin - - - Image - - - - diff --git a/Plugins/MultiEditorImage/MultiEditorImage_es.qm b/Plugins/MultiEditorImage/MultiEditorImage_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/MultiEditorImage/MultiEditorImage_es.qm and /dev/null differ diff --git a/Plugins/MultiEditorImage/MultiEditorImage_es.ts b/Plugins/MultiEditorImage/MultiEditorImage_es.ts deleted file mode 100644 index 6656179..0000000 --- a/Plugins/MultiEditorImage/MultiEditorImage_es.ts +++ /dev/null @@ -1,81 +0,0 @@ - - - - - MultiEditorImage - - - Load from file - - - - - Store in a file - - - - - Zoom in by 25% - - - - - Zoom out by 25% - - - - - Reset zoom - - - - - Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) - - - - - Open image - - - - - Could not open file %1 for reading. - Could not nopen file %1 for reading. - - - - - All files (*) - - - - - Save image - - - - - Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) - - - - - Could not nopen file %1 for writting. - - - - - Could not write image into the file %1 - - - - - MultiEditorImagePlugin - - - Image - - - - diff --git a/Plugins/MultiEditorImage/MultiEditorImage_fr.qm b/Plugins/MultiEditorImage/MultiEditorImage_fr.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/MultiEditorImage/MultiEditorImage_fr.qm and /dev/null differ diff --git a/Plugins/MultiEditorImage/MultiEditorImage_fr.ts b/Plugins/MultiEditorImage/MultiEditorImage_fr.ts deleted file mode 100644 index 4eec2b0..0000000 --- a/Plugins/MultiEditorImage/MultiEditorImage_fr.ts +++ /dev/null @@ -1,81 +0,0 @@ - - - - - MultiEditorImage - - - Load from file - - - - - Store in a file - - - - - Zoom in by 25% - - - - - Zoom out by 25% - - - - - Reset zoom - - - - - Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) - - - - - Open image - - - - - Could not open file %1 for reading. - Could not nopen file %1 for reading. - - - - - All files (*) - - - - - Save image - - - - - Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) - - - - - Could not nopen file %1 for writting. - - - - - Could not write image into the file %1 - - - - - MultiEditorImagePlugin - - - Image - - - - diff --git a/Plugins/MultiEditorImage/MultiEditorImage_it.qm b/Plugins/MultiEditorImage/MultiEditorImage_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/MultiEditorImage/MultiEditorImage_it.qm and /dev/null differ diff --git a/Plugins/MultiEditorImage/MultiEditorImage_it.ts b/Plugins/MultiEditorImage/MultiEditorImage_it.ts deleted file mode 100644 index 168db58..0000000 --- a/Plugins/MultiEditorImage/MultiEditorImage_it.ts +++ /dev/null @@ -1,81 +0,0 @@ - - - - - MultiEditorImage - - - Load from file - - - - - Store in a file - - - - - Zoom in by 25% - - - - - Zoom out by 25% - - - - - Reset zoom - - - - - Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) - - - - - Open image - - - - - Could not open file %1 for reading. - Could not nopen file %1 for reading. - - - - - All files (*) - - - - - Save image - - - - - Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) - - - - - Could not nopen file %1 for writting. - - - - - Could not write image into the file %1 - - - - - MultiEditorImagePlugin - - - Image - - - - diff --git a/Plugins/MultiEditorImage/MultiEditorImage_pl.qm b/Plugins/MultiEditorImage/MultiEditorImage_pl.qm deleted file mode 100644 index 5f02c32..0000000 Binary files a/Plugins/MultiEditorImage/MultiEditorImage_pl.qm and /dev/null differ diff --git a/Plugins/MultiEditorImage/MultiEditorImage_pl.ts b/Plugins/MultiEditorImage/MultiEditorImage_pl.ts deleted file mode 100644 index 2d70c19..0000000 --- a/Plugins/MultiEditorImage/MultiEditorImage_pl.ts +++ /dev/null @@ -1,81 +0,0 @@ - - - - - MultiEditorImage - - - Load from file - Wczytaj z pliku - - - - Store in a file - Zapisz do pliku - - - - Zoom in by 25% - PowiÄ™ksz o 25% - - - - Zoom out by 25% - Pomniejsz o 25% - - - - Reset zoom - Zresetuj powiÄ™kszenie - - - - Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) - Obrazy (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;Wszystkie pliki (*) - - - - Open image - Otwórz obraz - - - - Could not open file %1 for reading. - Could not nopen file %1 for reading. - Nie można otworzyć pliku %1 do odczytu. - - - - All files (*) - Wszystkie pliki (*) - - - - Save image - Zapisz obraz - - - - Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) - Próbowano zapisać obraz używajÄ…c innego formatu (%1) niż oryginaÅ‚ (%2), lecz aplikacji nie udaÅ‚o siÄ™ dokonać konwersji. Obraz z niezmienionym formacie (%3) zostanie zapisany pod podanÄ… nazwÄ… (%4) - - - - Could not nopen file %1 for writting. - Nie można otworzyć pliku %1 do zapisu. - - - - Could not write image into the file %1 - Nie można zapisać obrazu do pliku %1 - - - - MultiEditorImagePlugin - - - Image - Obraz - - - diff --git a/Plugins/MultiEditorImage/MultiEditorImage_pt_BR.qm b/Plugins/MultiEditorImage/MultiEditorImage_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/MultiEditorImage/MultiEditorImage_pt_BR.qm and /dev/null differ diff --git a/Plugins/MultiEditorImage/MultiEditorImage_pt_BR.ts b/Plugins/MultiEditorImage/MultiEditorImage_pt_BR.ts deleted file mode 100644 index ca226bd..0000000 --- a/Plugins/MultiEditorImage/MultiEditorImage_pt_BR.ts +++ /dev/null @@ -1,81 +0,0 @@ - - - - - MultiEditorImage - - - Load from file - - - - - Store in a file - - - - - Zoom in by 25% - - - - - Zoom out by 25% - - - - - Reset zoom - - - - - Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) - - - - - Open image - - - - - Could not open file %1 for reading. - Could not nopen file %1 for reading. - - - - - All files (*) - - - - - Save image - - - - - Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) - - - - - Could not nopen file %1 for writting. - - - - - Could not write image into the file %1 - - - - - MultiEditorImagePlugin - - - Image - - - - diff --git a/Plugins/MultiEditorImage/MultiEditorImage_ro_RO.qm b/Plugins/MultiEditorImage/MultiEditorImage_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/MultiEditorImage/MultiEditorImage_ro_RO.qm and /dev/null differ diff --git a/Plugins/MultiEditorImage/MultiEditorImage_ro_RO.ts b/Plugins/MultiEditorImage/MultiEditorImage_ro_RO.ts deleted file mode 100644 index 7b672e5..0000000 --- a/Plugins/MultiEditorImage/MultiEditorImage_ro_RO.ts +++ /dev/null @@ -1,81 +0,0 @@ - - - - - MultiEditorImage - - - Load from file - - - - - Store in a file - - - - - Zoom in by 25% - - - - - Zoom out by 25% - - - - - Reset zoom - - - - - Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) - - - - - Open image - - - - - Could not open file %1 for reading. - Could not nopen file %1 for reading. - - - - - All files (*) - - - - - Save image - - - - - Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) - - - - - Could not nopen file %1 for writting. - - - - - Could not write image into the file %1 - - - - - MultiEditorImagePlugin - - - Image - - - - diff --git a/Plugins/MultiEditorImage/MultiEditorImage_ru.qm b/Plugins/MultiEditorImage/MultiEditorImage_ru.qm deleted file mode 100644 index 7431612..0000000 Binary files a/Plugins/MultiEditorImage/MultiEditorImage_ru.qm and /dev/null differ diff --git a/Plugins/MultiEditorImage/MultiEditorImage_ru.ts b/Plugins/MultiEditorImage/MultiEditorImage_ru.ts deleted file mode 100644 index a43e653..0000000 --- a/Plugins/MultiEditorImage/MultiEditorImage_ru.ts +++ /dev/null @@ -1,81 +0,0 @@ - - - - - MultiEditorImage - - - Load from file - Загрузить из файла - - - - Store in a file - Сохранить в файл - - - - Zoom in by 25% - Приблизить на 25% - - - - Zoom out by 25% - Отдалить на 25% - - - - Reset zoom - СброÑить маÑштаб - - - - Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) - Ð˜Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;Ð’Ñе файлы (*) - - - - Open image - Открыть изображение - - - - Could not open file %1 for reading. - Could not nopen file %1 for reading. - Ðевозможно открыть файл %1 Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ. - - - - All files (*) - Ð’Ñе файлы (*) - - - - Save image - Сохранить изображение - - - - Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) - ОÑущеÑтвлена попытка Ñохранить изображение в формате (%1), отличном от иÑходного (%2), однако приложению не удалоÑÑŒ произвеÑти конвертацию. Изображение в иÑходном формате (%3) будет Ñохранено Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ð¼ именем (%4) - - - - Could not nopen file %1 for writting. - Ðевозможно открыть файл %1 Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи. - - - - Could not write image into the file %1 - Ðевозможно запиÑать изображение в файл %1 - - - - MultiEditorImagePlugin - - - Image - Изображение - - - diff --git a/Plugins/MultiEditorImage/MultiEditorImage_sk.qm b/Plugins/MultiEditorImage/MultiEditorImage_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/Plugins/MultiEditorImage/MultiEditorImage_sk.qm and /dev/null differ diff --git a/Plugins/MultiEditorImage/MultiEditorImage_sk.ts b/Plugins/MultiEditorImage/MultiEditorImage_sk.ts deleted file mode 100644 index 3a0b3af..0000000 --- a/Plugins/MultiEditorImage/MultiEditorImage_sk.ts +++ /dev/null @@ -1,81 +0,0 @@ - - - - - MultiEditorImage - - - Load from file - - - - - Store in a file - - - - - Zoom in by 25% - - - - - Zoom out by 25% - - - - - Reset zoom - - - - - Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) - - - - - Open image - - - - - Could not open file %1 for reading. - Could not nopen file %1 for reading. - - - - - All files (*) - - - - - Save image - - - - - Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) - - - - - Could not nopen file %1 for writting. - - - - - Could not write image into the file %1 - - - - - MultiEditorImagePlugin - - - Image - - - - diff --git a/Plugins/MultiEditorImage/MultiEditorImage_zh_CN.qm b/Plugins/MultiEditorImage/MultiEditorImage_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/Plugins/MultiEditorImage/MultiEditorImage_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/Plugins/MultiEditorImage/MultiEditorImage_zh_CN.ts b/Plugins/MultiEditorImage/MultiEditorImage_zh_CN.ts deleted file mode 100644 index be7365d..0000000 --- a/Plugins/MultiEditorImage/MultiEditorImage_zh_CN.ts +++ /dev/null @@ -1,81 +0,0 @@ - - - - - MultiEditorImage - - - Load from file - - - - - Store in a file - - - - - Zoom in by 25% - - - - - Zoom out by 25% - - - - - Reset zoom - - - - - Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) - - - - - Open image - - - - - Could not open file %1 for reading. - Could not nopen file %1 for reading. - - - - - All files (*) - - - - - Save image - - - - - Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) - - - - - Could not nopen file %1 for writting. - - - - - Could not write image into the file %1 - - - - - MultiEditorImagePlugin - - - Image - - - - diff --git a/Plugins/MultiEditorImage/multieditorimage.cpp b/Plugins/MultiEditorImage/multieditorimage.cpp index 68ed77b..9571b8f 100644 --- a/Plugins/MultiEditorImage/multieditorimage.cpp +++ b/Plugins/MultiEditorImage/multieditorimage.cpp @@ -1,5 +1,4 @@ #include "multieditorimage.h" -#include "common/unused.h" #include "iconmanager.h" #include "uiconfig.h" #include "services/notifymanager.h" @@ -163,7 +162,7 @@ void MultiEditorImage::saveFile() QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) { - notifyError(tr("Could not nopen file %1 for writting.").arg(fileName)); + notifyError(tr("Could not open file %1 for writting.").arg(fileName)); return; } @@ -205,6 +204,7 @@ bool MultiEditorImagePlugin::validFor(const DataType& dataType) switch (dataType.getType()) { case DataType::BLOB: + case DataType::ANY: case DataType::NONE: case DataType::unknown: return true; @@ -235,6 +235,7 @@ int MultiEditorImagePlugin::getPriority(const DataType& dataType) case DataType::BLOB: return 10; case DataType::NONE: + case DataType::ANY: case DataType::unknown: return 50; case DataType::BOOLEAN: @@ -264,17 +265,17 @@ QString MultiEditorImagePlugin::getTabLabel() bool MultiEditorImagePlugin::init() { - Q_INIT_RESOURCE(multieditorimage); + SQLS_INIT_RESOURCE(multieditorimage); return GenericPlugin::init(); } void MultiEditorImagePlugin::deinit() { - for (MultiEditorImage* editor : instances) + for (MultiEditorImage*& editor : instances) { editor->notifyAboutUnload(); delete editor; } - Q_CLEANUP_RESOURCE(multieditorimage); + SQLS_CLEANUP_RESOURCE(multieditorimage); } diff --git a/Plugins/MultiEditorImage/multieditorimage.qrc b/Plugins/MultiEditorImage/multieditorimage.qrc index 0ab156b..4a561e8 100644 --- a/Plugins/MultiEditorImage/multieditorimage.qrc +++ b/Plugins/MultiEditorImage/multieditorimage.qrc @@ -1,15 +1,3 @@ - - MultiEditorImage_de.qm - MultiEditorImage_es.qm - MultiEditorImage_fr.qm - MultiEditorImage_it.qm - MultiEditorImage_pl.qm - MultiEditorImage_pt_BR.qm - MultiEditorImage_ro_RO.qm - MultiEditorImage_ru.qm - MultiEditorImage_sk.qm - MultiEditorImage_zh_CN.qm - diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage.ts new file mode 100644 index 0000000..afb4006 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + + + + + Store in a file + + + + + Zoom in by 25% + + + + + Zoom out by 25% + + + + + Reset zoom + + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + + Open image + + + + + Could not open file %1 for reading. + + + + + All files (*) + + + + + Save image + + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + + Could not open file %1 for writting. + + + + + Could not write image into the file %1 + + + + + MultiEditorImagePlugin + + + Image + + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_af_ZA.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_af_ZA.ts new file mode 100644 index 0000000..87d7d74 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_af_ZA.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_ar_SA.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_ar_SA.ts new file mode 100644 index 0000000..afa393e --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_ar_SA.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_ca_ES.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_ca_ES.ts new file mode 100644 index 0000000..4fb590d --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_ca_ES.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_cs_CZ.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_cs_CZ.ts new file mode 100644 index 0000000..9aec3d9 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_cs_CZ.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_da_DK.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_da_DK.ts new file mode 100644 index 0000000..dc6bf47 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_da_DK.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_de_DE.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_de_DE.ts new file mode 100644 index 0000000..81df4a7 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_de_DE.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Aus Datei laden + + + + Store in a file + In einer Datei speichern + + + + Zoom in by 25% + Um 25% vergrößern + + + + Zoom out by 25% + Um 25% verkleinern + + + + Reset zoom + Zoom zurücksetzen + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Bilder (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;Alle Dateien (*) + + + + Open image + Bild öffnen + + + + Could not open file %1 for reading. + Datei %1 konnte nicht zum Lesen geöffnet werden. + + + + All files (*) + Alle Dateien (*) + + + + Save image + Bild speichern + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Es wurde versucht, das Bild in anderem Format (%1) als das Original (%2) zu speichern, aber die Anwendung konnte es nicht konvertieren. Das Bild mit unverändertem Format (%3) wird unter dem angegebenen Namen (%4) gespeichert. + + + + Could not open file %1 for writting. + Datei %1 konnte nicht zum Schreiben geöffnet werden. + + + + Could not write image into the file %1 + Bild konnte nicht in die Datei %1 geschrieben werden + + + + MultiEditorImagePlugin + + + Image + Bild + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_el_GR.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_el_GR.ts new file mode 100644 index 0000000..36df96d --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_el_GR.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_en_US.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_en_US.ts new file mode 100644 index 0000000..970ffea --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_en_US.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_es_ES.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_es_ES.ts new file mode 100644 index 0000000..19ceddf --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_es_ES.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Cargar desde archivo + + + + Store in a file + Guardar en un archivo + + + + Zoom in by 25% + Acercar un 25% + + + + Zoom out by 25% + Alejar un 25% + + + + Reset zoom + Restablecer zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Imágenes (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;Todos los archivos (*) + + + + Open image + Abrir imagen + + + + Could not open file %1 for reading. + No se pudo abrir el archivo %1 para su lectura. + + + + All files (*) + Todos los archivos (*) + + + + Save image + Guardar imagen + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Se intentó guardar en un formato (%1) diferente del original (%2), pero la aplicación falló en la conversión. La imagen con su formato sin cambiar (%3) se guardará con el siguiente nombre (%4) + + + + Could not open file %1 for writting. + No se pudo abrir el archivo %1 para su escritura. + + + + Could not write image into the file %1 + No se pudo escribir la imagen en el archivo %1 + + + + MultiEditorImagePlugin + + + Image + Imagen + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_fa_IR.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_fa_IR.ts new file mode 100644 index 0000000..d824c61 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_fa_IR.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_fi_FI.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_fi_FI.ts new file mode 100644 index 0000000..8c866c8 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_fi_FI.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_fr_FR.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_fr_FR.ts new file mode 100644 index 0000000..8447b1f --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_fr_FR.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Charger à partir d’un fichier + + + + Store in a file + Stocker dans un fichier + + + + Zoom in by 25% + Zoom avant de 25% + + + + Zoom out by 25% + Zoom arrière de 25% + + + + Reset zoom + Réinitialiser le zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;Tous les fichiers (*) + + + + Open image + Ouvrir une image + + + + Could not open file %1 for reading. + Impossible d'ouvrir le fichier %1 pour la lecture. + + + + All files (*) + Tous les fichiers (*) + + + + Save image + Enregistrer l'image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Impossible d'ouvrir le fichier %1 pour écriture. + + + + Could not write image into the file %1 + Impossible d'écrire l'image dans le fichier %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_he_IL.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_he_IL.ts new file mode 100644 index 0000000..b8553d5 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_he_IL.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + טעינה מקובץ + + + + Store in a file + ×חסון בקובץ + + + + Zoom in by 25% + קרוב מיקוד ב 25% + + + + Zoom out by 25% + רחוק מיקוד ב 25% + + + + Reset zoom + ×יפוס מיקוד + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + תמונות (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);כל ×”×§×‘×¦×™× (*) + + + + Open image + פתיחת תמונה + + + + Could not open file %1 for reading. + ×œ× × ×™×ª×Ÿ לפתוח קובץ%1 לקרי××”. + + + + All files (*) + כל ×”×§×‘×¦×™× (*) + + + + Save image + שמירת תמונה + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + נעשה ניסה לשמירת תמונה בתבנית (%1) השונה מהמקור (%2), ×ך ×”×™×™×©×•× ×œ× ×”×¦×œ×™×— להמיר ×ותה. התמונה ×œ×œ× ×©×™× ×•×™ בתבנית (%3) תישמר ×‘×©× (%4) + + + + Could not open file %1 for writting. + ×œ× × ×™×ª×Ÿ לפתוח קובץ%1 לכתיבה. + + + + Could not write image into the file %1 + ×œ× × ×™×ª×Ÿ לכתוב תמונה לתוך קובץ %1 + + + + MultiEditorImagePlugin + + + Image + תמונה + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_hu_HU.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_hu_HU.ts new file mode 100644 index 0000000..5fe246d --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_hu_HU.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_it_IT.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_it_IT.ts new file mode 100644 index 0000000..f0b2b23 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_it_IT.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Carica da file + + + + Store in a file + Memorizza in un file + + + + Zoom in by 25% + Aumenta ingrandimento del 25% + + + + Zoom out by 25% + Riduci ingrandimento del 25% + + + + Reset zoom + Reimposta zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Immagini (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;Tutti i file (*) + + + + Open image + Apri immagine + + + + Could not open file %1 for reading. + Impossibile aprire il file %1 in lettura. + + + + All files (*) + Tutti i file (*) + + + + Save image + Salva immagine + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Ho cercato di salvare l'immagine in un formato diverso (%1) rispetto all'originale (%2), ma l'applicazione non è riuscita a convertirla. L'immagine con formato immutato (%3) verrà salvata con il nome specificato (%4) + + + + Could not open file %1 for writting. + Impossibile aprire il file %1 in scrittura. + + + + Could not write image into the file %1 + Impossibile scrivere l'immagine nel file %1 + + + + MultiEditorImagePlugin + + + Image + Immagine + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_ja_JP.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_ja_JP.ts new file mode 100644 index 0000000..5185666 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_ja_JP.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_kaa.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_kaa.ts new file mode 100644 index 0000000..191ba09 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_kaa.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Súwretti saqlaw + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Súwret + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_ko_KR.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_ko_KR.ts new file mode 100644 index 0000000..582c1da --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_ko_KR.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_nl_NL.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_nl_NL.ts new file mode 100644 index 0000000..bd156c0 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_nl_NL.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_no_NO.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_no_NO.ts new file mode 100644 index 0000000..5f37b01 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_no_NO.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_pl_PL.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_pl_PL.ts new file mode 100644 index 0000000..c7efda0 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_pl_PL.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Wczytaj z pliku + + + + Store in a file + Zapisz do pliku + + + + Zoom in by 25% + PowiÄ™ksz o 25% + + + + Zoom out by 25% + Pomniejsz o 25% + + + + Reset zoom + Zresetuj powiÄ™kszenie + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Obrazy (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;Wszystkie pliki (*) + + + + Open image + Otwórz obraz + + + + Could not open file %1 for reading. + Nie można otworzyć pliku %1 do odczytu. + + + + All files (*) + Wszystkie pliki (*) + + + + Save image + Zapisz obraz + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Próbowano zapisać obraz używajÄ…c innego formatu (%1) niż oryginaÅ‚ (%2), lecz aplikacji nie udaÅ‚o siÄ™ dokonać konwersji. Obraz z niezmienionym formacie (%3) zostanie zapisany pod podanÄ… nazwÄ… (%4) + + + + Could not open file %1 for writting. + Nie można otworzyć pliku %1 do zapisu. + + + + Could not write image into the file %1 + Nie można zapisać obrazu do pliku %1 + + + + MultiEditorImagePlugin + + + Image + Obraz + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_pt_BR.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_pt_BR.ts new file mode 100644 index 0000000..1933f04 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_pt_BR.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Carregar do arquivo + + + + Store in a file + Armazenar em um arquivo + + + + Zoom in by 25% + Aumentar o zoom em 25% + + + + Zoom out by 25% + Diminuir o zoom em 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Imagens (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;Todos os arquivos (*) + + + + Open image + Abrir imagem + + + + Could not open file %1 for reading. + Não foi possível abrir o arquivo %1 para leitura. + + + + All files (*) + Todos os arquivos (*) + + + + Save image + Salvar imagem + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tentou salvar a imagem em formato diferente (%1) do que original (%2), mas a aplicação não conseguiu convertê-la. A imagem com formato inalterado (%3) será salva sob o nome fornecido (%4) + + + + Could not open file %1 for writting. + Não foi possível abrir o arquivo %1 para escrita. + + + + Could not write image into the file %1 + Não foi possível escrever a imagem no arquivo %1 + + + + MultiEditorImagePlugin + + + Image + Imagem + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_pt_PT.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_pt_PT.ts new file mode 100644 index 0000000..3a1deb9 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_pt_PT.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_ro_RO.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_ro_RO.ts new file mode 100644 index 0000000..0500228 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_ro_RO.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_ru_RU.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_ru_RU.ts new file mode 100644 index 0000000..39e3cbe --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_ru_RU.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Загрузить из файла + + + + Store in a file + Сохранить в файл + + + + Zoom in by 25% + Приблизить на 25% + + + + Zoom out by 25% + Отдалить на 25% + + + + Reset zoom + СброÑить маÑштаб + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Ð˜Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;Ð’Ñе файлы (*) + + + + Open image + Открыть изображение + + + + Could not open file %1 for reading. + Ðевозможно открыть файл %1 Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ. + + + + All files (*) + Ð’Ñе файлы (*) + + + + Save image + Сохранить изображение + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + ОÑущеÑтвлена попытка Ñохранить изображение в формате (%1), отличном от иÑходного (%2), однако приложению не удалоÑÑŒ произвеÑти конвертацию. Изображение в иÑходном формате (%3) будет Ñохранено Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ð¼ именем (%4) + + + + Could not open file %1 for writting. + Ðевозможно открыть файл %1 Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи. + + + + Could not write image into the file %1 + Ðевозможно запиÑать изображение в файл %1 + + + + MultiEditorImagePlugin + + + Image + Изображение + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_sk_SK.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_sk_SK.ts new file mode 100644 index 0000000..41d5b31 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_sk_SK.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_sr_SP.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_sr_SP.ts new file mode 100644 index 0000000..9f829c0 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_sr_SP.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_sv_SE.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_sv_SE.ts new file mode 100644 index 0000000..66b42f1 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_sv_SE.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_tr_TR.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_tr_TR.ts new file mode 100644 index 0000000..aa16caa --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_tr_TR.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Dosyadan yükle + + + + Store in a file + Dosyada sakla + + + + Zoom in by 25% + %25 yakınlaÅŸtır + + + + Zoom out by 25% + %25 uzaklaÅŸtır + + + + Reset zoom + Zoom Sıfırla + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Resimler (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;DiÄŸer dosyalar (*) + + + + Open image + Resmi aç + + + + Could not open file %1 for reading. + %1 dosyası okumak için açılamadı. + + + + All files (*) + Tüm Dosyalar (*) + + + + Save image + Resmi kaydet + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Görüntüyü orjinalinden (%2) farklı bir formatta kaydetmeye çalıştı (%1), fakat uygulama dönüştürürken hata aldı. DeÄŸiÅŸtirilmemiÅŸ formattaki görüntü (%3) verilen isimde kaydedilecek (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + %1 dosyasına resim yazılamadı + + + + MultiEditorImagePlugin + + + Image + Resim + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_uk_UA.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_uk_UA.ts new file mode 100644 index 0000000..18aaf19 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_uk_UA.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_vi_VN.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_vi_VN.ts new file mode 100644 index 0000000..cca8e23 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_vi_VN.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + Load from file + + + + Store in a file + Store in a file + + + + Zoom in by 25% + Zoom in by 25% + + + + Zoom out by 25% + Zoom out by 25% + + + + Reset zoom + Reset zoom + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + + + + Open image + Open image + + + + Could not open file %1 for reading. + Could not open file %1 for reading. + + + + All files (*) + All files (*) + + + + Save image + Save image + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + + + + Could not open file %1 for writting. + Could not open file %1 for writting. + + + + Could not write image into the file %1 + Could not write image into the file %1 + + + + MultiEditorImagePlugin + + + Image + Image + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_zh_CN.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_zh_CN.ts new file mode 100644 index 0000000..6ce3cda --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_zh_CN.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + 从文件加载 + + + + Store in a file + ä¿å­˜ä¸ºæ–‡ä»¶ + + + + Zoom in by 25% + 放大25% + + + + Zoom out by 25% + 缩å°25% + + + + Reset zoom + é‡ç½®ç¼©æ”¾ + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + 图片 (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;所有文件 (*) + + + + Open image + 打开图片 + + + + Could not open file %1 for reading. + æ— æ³•ä»¥è¯»æ¨¡å¼æ‰“开文件 %1。 + + + + All files (*) + 所有文件 (*) + + + + Save image + ä¿å­˜å›¾ç‰‡ + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + è¯•å›¾ä»¥ä¸ŽåŽŸå§‹æ ¼å¼ (%2) ä¸åŒçš„æ ¼å¼ (%1) ä¿å­˜å›¾åƒï¼Œä½†åº”用程åºè½¬æ¢å¤±è´¥ã€‚ 图åƒå°†ä»¥æœªæ”¹å˜çš„æ ¼å¼ (%3) ä¿å­˜åœ¨ç»™å®šå称下 (%4) + + + + Could not open file %1 for writting. + æ— æ³•ä»¥å†™æ¨¡å¼æ‰“开文件 %1。 + + + + Could not write image into the file %1 + 无法将图åƒå†™å…¥æ–‡ä»¶ %1 + + + + MultiEditorImagePlugin + + + Image + 图片 + + + diff --git a/Plugins/MultiEditorImage/translations/MultiEditorImage_zh_TW.ts b/Plugins/MultiEditorImage/translations/MultiEditorImage_zh_TW.ts new file mode 100644 index 0000000..6fa5139 --- /dev/null +++ b/Plugins/MultiEditorImage/translations/MultiEditorImage_zh_TW.ts @@ -0,0 +1,80 @@ + + + + + MultiEditorImage + + + Load from file + 從檔案載入 + + + + Store in a file + 儲存為檔案 + + + + Zoom in by 25% + 放大 25% + + + + Zoom out by 25% + ç¸®å° 25% + + + + Reset zoom + é‡ç½®ç¸®æ”¾ + + + + Images (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;All files (*) + 圖片 (*.jpeg *.jpg *.png *.bmp *.gif *.tiff *.jp2 *.svg *.tga *.icns *.webp *.wbmp *.mng);;所有檔案 (*) + + + + Open image + 開啟圖片 + + + + Could not open file %1 for reading. + 無法以讀模å¼é–‹å•Ÿæª”案 %1。 + + + + All files (*) + 所有檔案 (*) + + + + Save image + 儲存圖片 + + + + Tried to save image under different format (%1) than original (%2), but application failed to convert it. The image with unchanged format (%3) will be saved under the given name (%4) + è©¦åœ–ä»¥èˆ‡åŽŸå§‹æ ¼å¼ (%2) ä¸åŒçš„æ ¼å¼ (%1) 儲存影象,但應用程å¼è½‰æ›å¤±æ•—ã€‚å½±è±¡å°‡ä»¥æœªæ”¹è®Šçš„æ ¼å¼ (%3) 儲存在給定å稱下 (%4) + + + + Could not open file %1 for writting. + 無法以寫模å¼é–‹å•Ÿæª”案 %1。 + + + + Could not write image into the file %1 + 無法將影象寫入檔案 %1 + + + + MultiEditorImagePlugin + + + Image + 圖片 + + + diff --git a/Plugins/PdfExport/PdfExport.pro b/Plugins/PdfExport/PdfExport.pro index b92c38b..46dda37 100644 --- a/Plugins/PdfExport/PdfExport.pro +++ b/Plugins/PdfExport/PdfExport.pro @@ -24,31 +24,3 @@ FORMS += \ RESOURCES += \ pdfexport.qrc - - -TRANSLATIONS += PdfExport_ro_RO.ts \ - PdfExport_de.ts \ - PdfExport_it.ts \ - PdfExport_zh_CN.ts \ - PdfExport_sk.ts \ - PdfExport_ru.ts \ - PdfExport_pt_BR.ts \ - PdfExport_fr.ts \ - PdfExport_es.ts \ - PdfExport_pl.ts - - - - - - - - - - - - - - - - diff --git a/Plugins/PdfExport/PdfExport_de.qm b/Plugins/PdfExport/PdfExport_de.qm deleted file mode 100644 index ccab910..0000000 Binary files a/Plugins/PdfExport/PdfExport_de.qm and /dev/null differ diff --git a/Plugins/PdfExport/PdfExport_de.ts b/Plugins/PdfExport/PdfExport_de.ts deleted file mode 100644 index d06a30c..0000000 --- a/Plugins/PdfExport/PdfExport_de.ts +++ /dev/null @@ -1,258 +0,0 @@ - - - - - PdfExport - - - SQLiteStudio v%1 - SQLiteStudio v%1 - - - - SQL query results - SQL Abfragenergebnis - - - - - Exported table: %1 - Exportierte Tabelle: %1 - - - - - Table: %1 - Tabelle: %1 - - - - - Column - Spalte - - - - Data type - Datentyp - - - - Constraints - Bedingungen - - - - Global table constraints - Globale Tabellenbedingungen - - - - Exported database: %1 - Exportierte Datenbank: %1 - - - - Index: %1 - Index: %1 - - - - Property - index header - Eigenschaft - - - - Value - index header - Wert - - - - Indexed table - Indexierte Tabelle - - - - Unique index - Singularer Index? Fuer Unique finde ich keine richtige Übersetzung. - Index (duplikatfrei) - - - - Yes - Ja - - - - No - Nein - - - - Collation - Kollation - - - - Sort order - Sortierreihenfolge - - - - Partial index condition - Teilindexbedingung - - - - Trigger: %1 - Trigger: %1 - - - - Property - trigger header - Eigenschaft - - - - Value - trigger header - Wert - - - - Activation time - Ausführungszeit - - - - For action - Für Aktion - - - - On view - auf Index - - - - On table - auf Tabelle - - - - Activation condition - Ausführungskondition - - - - Code executed - Code ausgeführt - - - - View: %1 - View: %1 - - - - Query: - Abfrage: - - - - Document generated with SQLiteStudio v%1 - Dokument mit SQLiteStudio v%1 generiert - - - - PdfExportConfig - - - Size and layout - Format und Layout - - - - Page size: - Format: - - - - - Right margin: - Seitenabstand Rechts: - - - - Left margin: - Seitenabstand Links: - - - - Cell padding: - Zellenabstand: - - - - Limit characters in single cell: - Maximale Anzahl an Zeichen pro Zelle: - - - - - - - - mm - mm - - - - Bottom margin: - Seitenabstand Unten: - - - - Top margin: - Seitenabstand oben: - - - - Font - Schriftart - - - - Colors - Farben - - - - Headers background: - Hintergrundfarbe Kopfzeile: - - - - NULL value color: - NULL Wert Farbe: - - - - Other settings - Sonstige Einstellungen - - - - Print row numbers for data - Zeilennummerierung drucken - - - - Print page numbers - Seitenzahl drucken - - - diff --git a/Plugins/PdfExport/PdfExport_es.qm b/Plugins/PdfExport/PdfExport_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/PdfExport/PdfExport_es.qm and /dev/null differ diff --git a/Plugins/PdfExport/PdfExport_es.ts b/Plugins/PdfExport/PdfExport_es.ts deleted file mode 100644 index 6535f76..0000000 --- a/Plugins/PdfExport/PdfExport_es.ts +++ /dev/null @@ -1,256 +0,0 @@ - - - - - PdfExport - - - SQLiteStudio v%1 - - - - - SQL query results - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - - Column - - - - - Data type - - - - - Constraints - - - - - Global table constraints - - - - - Exported database: %1 - - - - - Index: %1 - - - - - Property - index header - - - - - Value - index header - - - - - Indexed table - - - - - Unique index - - - - - Yes - - - - - No - - - - - Collation - - - - - Sort order - - - - - Partial index condition - - - - - Trigger: %1 - - - - - Property - trigger header - - - - - Value - trigger header - - - - - Activation time - - - - - For action - - - - - On view - - - - - On table - - - - - Activation condition - - - - - Code executed - - - - - View: %1 - - - - - Query: - - - - - Document generated with SQLiteStudio v%1 - - - - - PdfExportConfig - - - Size and layout - - - - - Page size: - - - - - Right margin: - - - - - Left margin: - - - - - Cell padding: - - - - - Limit characters in single cell: - - - - - - - - - mm - - - - - Bottom margin: - - - - - Top margin: - - - - - Font - - - - - Colors - - - - - Headers background: - - - - - NULL value color: - - - - - Other settings - - - - - Print row numbers for data - - - - - Print page numbers - - - - diff --git a/Plugins/PdfExport/PdfExport_fr.qm b/Plugins/PdfExport/PdfExport_fr.qm deleted file mode 100644 index 4d1bbc6..0000000 Binary files a/Plugins/PdfExport/PdfExport_fr.qm and /dev/null differ diff --git a/Plugins/PdfExport/PdfExport_fr.ts b/Plugins/PdfExport/PdfExport_fr.ts deleted file mode 100644 index 63d7810..0000000 --- a/Plugins/PdfExport/PdfExport_fr.ts +++ /dev/null @@ -1,256 +0,0 @@ - - - - - PdfExport - - - SQLiteStudio v%1 - SQLiteStudio v%1 - - - - SQL query results - Résultats de la requête SQL - - - - - Exported table: %1 - Table exportée : %1 - - - - - Table: %1 - Table : %1 - - - - - Column - Colonne - - - - Data type - Type de données - - - - Constraints - Contraintes - - - - Global table constraints - Contraintes globales de la table - - - - Exported database: %1 - Base de données exportée : %1 - - - - Index: %1 - Index : %1 - - - - Property - index header - Proprièté - - - - Value - index header - Valeur - - - - Indexed table - Table indexée - - - - Unique index - Index unique - - - - Yes - Oui - - - - No - Non - - - - Collation - Collation - - - - Sort order - Ordre de tri - - - - Partial index condition - Condition de l’index partiel - - - - Trigger: %1 - Déchencheur : %1 - - - - Property - trigger header - Proprièté - - - - Value - trigger header - Valeur - - - - Activation time - Activation temps - - - - For action - Pour action - - - - On view - De la vue - - - - On table - De la vue - - - - Activation condition - Activation de la condition - - - - Code executed - Code exécuté - - - - View: %1 - Vue : %1 - - - - Query: - Requête : - - - - Document generated with SQLiteStudio v%1 - Documentation générée avec SQLiteStudio v%1 - - - - PdfExportConfig - - - Size and layout - Dimension et mise en page - - - - Page size: - Dimension page : - - - - Right margin: - Marge droite : - - - - Left margin: - Marge gauche : - - - - Cell padding: - Remplissage cellule : - - - - Limit characters in single cell: - Limite de caractères dans une cellule unique : - - - - - - - - mm - mm - - - - Bottom margin: - Marge inférieure : - - - - Top margin: - Marge supérieure : - - - - Font - Police - - - - Colors - Couleurs - - - - Headers background: - Arrière plan des en-têtes : - - - - NULL value color: - Couleur pour valeur NULL : - - - - Other settings - Autres paramètres - - - - Print row numbers for data - Nombre de lignes de données à imprimer - - - - Print page numbers - Imprimer nombre de pages - - - diff --git a/Plugins/PdfExport/PdfExport_it.qm b/Plugins/PdfExport/PdfExport_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/PdfExport/PdfExport_it.qm and /dev/null differ diff --git a/Plugins/PdfExport/PdfExport_it.ts b/Plugins/PdfExport/PdfExport_it.ts deleted file mode 100644 index 8b13829..0000000 --- a/Plugins/PdfExport/PdfExport_it.ts +++ /dev/null @@ -1,256 +0,0 @@ - - - - - PdfExport - - - SQLiteStudio v%1 - - - - - SQL query results - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - - Column - - - - - Data type - - - - - Constraints - - - - - Global table constraints - - - - - Exported database: %1 - - - - - Index: %1 - - - - - Property - index header - - - - - Value - index header - - - - - Indexed table - - - - - Unique index - - - - - Yes - - - - - No - - - - - Collation - - - - - Sort order - - - - - Partial index condition - - - - - Trigger: %1 - - - - - Property - trigger header - - - - - Value - trigger header - - - - - Activation time - - - - - For action - - - - - On view - - - - - On table - - - - - Activation condition - - - - - Code executed - - - - - View: %1 - - - - - Query: - - - - - Document generated with SQLiteStudio v%1 - - - - - PdfExportConfig - - - Size and layout - - - - - Page size: - - - - - Right margin: - - - - - Left margin: - - - - - Cell padding: - - - - - Limit characters in single cell: - - - - - - - - - mm - - - - - Bottom margin: - - - - - Top margin: - - - - - Font - - - - - Colors - - - - - Headers background: - - - - - NULL value color: - - - - - Other settings - - - - - Print row numbers for data - - - - - Print page numbers - - - - diff --git a/Plugins/PdfExport/PdfExport_pl.qm b/Plugins/PdfExport/PdfExport_pl.qm deleted file mode 100644 index ff4fc42..0000000 Binary files a/Plugins/PdfExport/PdfExport_pl.qm and /dev/null differ diff --git a/Plugins/PdfExport/PdfExport_pl.ts b/Plugins/PdfExport/PdfExport_pl.ts deleted file mode 100644 index ece6123..0000000 --- a/Plugins/PdfExport/PdfExport_pl.ts +++ /dev/null @@ -1,256 +0,0 @@ - - - - - PdfExport - - - SQLiteStudio v%1 - SQLiteStudio v%1 - - - - SQL query results - Wyniki zapytania SQL - - - - - Exported table: %1 - Eksportowana tabela: %1 - - - - - Table: %1 - Tabela: %1 - - - - - Column - Kolumna - - - - Data type - Typ danych - - - - Constraints - Ograniczenia - - - - Global table constraints - Globalne ograniczenia tabeli - - - - Exported database: %1 - Eksportowana baza: %1 - - - - Index: %1 - Indeks: %1 - - - - Property - index header - WÅ‚asność - - - - Value - index header - Wartość - - - - Indexed table - Zaindeksowana tabela - - - - Unique index - Indeks unikalny - - - - Yes - Tak - - - - No - Nie - - - - Collation - Zestawienie - - - - Sort order - Sortowanie - - - - Partial index condition - Warunek indeksu częściowego - - - - Trigger: %1 - Wyzwalacz: %1 - - - - Property - trigger header - WÅ‚asność - - - - Value - trigger header - Wartość - - - - Activation time - Moment aktywacji - - - - For action - Dla akcji - - - - On view - Na widoku - - - - On table - Na tabeli - - - - Activation condition - Warunek aktywacji - - - - Code executed - Kod do wykonania - - - - View: %1 - Widok: %1 - - - - Query: - Zapytanie: - - - - Document generated with SQLiteStudio v%1 - Dokument wygenerowany przy pomocy SQLiteStudio v%1 - - - - PdfExportConfig - - - Size and layout - Rozmiar i ukÅ‚ad - - - - Page size: - Rozmiar strony: - - - - Right margin: - Prawy margines: - - - - Left margin: - Lewy margines: - - - - Cell padding: - OdstÄ™p wewnÄ…trz komórki: - - - - Limit characters in single cell: - Ogranicz liczbÄ™ znaków w komórce: - - - - - - - - mm - mm - - - - Bottom margin: - Dolny margines: - - - - Top margin: - Górny margines: - - - - Font - Czcionka - - - - Colors - Kolory - - - - Headers background: - TÅ‚o nagłówków: - - - - NULL value color: - Kolor wartoÅ›ci NULL: - - - - Other settings - Inne ustawienia - - - - Print row numbers for data - Drukuj numery wierszy dla danych - - - - Print page numbers - Drukuj numery stron - - - diff --git a/Plugins/PdfExport/PdfExport_pt_BR.qm b/Plugins/PdfExport/PdfExport_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/PdfExport/PdfExport_pt_BR.qm and /dev/null differ diff --git a/Plugins/PdfExport/PdfExport_pt_BR.ts b/Plugins/PdfExport/PdfExport_pt_BR.ts deleted file mode 100644 index 8534c76..0000000 --- a/Plugins/PdfExport/PdfExport_pt_BR.ts +++ /dev/null @@ -1,256 +0,0 @@ - - - - - PdfExport - - - SQLiteStudio v%1 - - - - - SQL query results - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - - Column - - - - - Data type - - - - - Constraints - - - - - Global table constraints - - - - - Exported database: %1 - - - - - Index: %1 - - - - - Property - index header - - - - - Value - index header - - - - - Indexed table - - - - - Unique index - - - - - Yes - - - - - No - - - - - Collation - - - - - Sort order - - - - - Partial index condition - - - - - Trigger: %1 - - - - - Property - trigger header - - - - - Value - trigger header - - - - - Activation time - - - - - For action - - - - - On view - - - - - On table - - - - - Activation condition - - - - - Code executed - - - - - View: %1 - - - - - Query: - - - - - Document generated with SQLiteStudio v%1 - - - - - PdfExportConfig - - - Size and layout - - - - - Page size: - - - - - Right margin: - - - - - Left margin: - - - - - Cell padding: - - - - - Limit characters in single cell: - - - - - - - - - mm - - - - - Bottom margin: - - - - - Top margin: - - - - - Font - - - - - Colors - - - - - Headers background: - - - - - NULL value color: - - - - - Other settings - - - - - Print row numbers for data - - - - - Print page numbers - - - - diff --git a/Plugins/PdfExport/PdfExport_ro_RO.qm b/Plugins/PdfExport/PdfExport_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/PdfExport/PdfExport_ro_RO.qm and /dev/null differ diff --git a/Plugins/PdfExport/PdfExport_ro_RO.ts b/Plugins/PdfExport/PdfExport_ro_RO.ts deleted file mode 100644 index 189fdc6..0000000 --- a/Plugins/PdfExport/PdfExport_ro_RO.ts +++ /dev/null @@ -1,256 +0,0 @@ - - - - - PdfExport - - - SQLiteStudio v%1 - - - - - SQL query results - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - - Column - - - - - Data type - - - - - Constraints - - - - - Global table constraints - - - - - Exported database: %1 - - - - - Index: %1 - - - - - Property - index header - - - - - Value - index header - - - - - Indexed table - - - - - Unique index - - - - - Yes - - - - - No - - - - - Collation - - - - - Sort order - - - - - Partial index condition - - - - - Trigger: %1 - - - - - Property - trigger header - - - - - Value - trigger header - - - - - Activation time - - - - - For action - - - - - On view - - - - - On table - - - - - Activation condition - - - - - Code executed - - - - - View: %1 - - - - - Query: - - - - - Document generated with SQLiteStudio v%1 - - - - - PdfExportConfig - - - Size and layout - - - - - Page size: - - - - - Right margin: - - - - - Left margin: - - - - - Cell padding: - - - - - Limit characters in single cell: - - - - - - - - - mm - - - - - Bottom margin: - - - - - Top margin: - - - - - Font - - - - - Colors - - - - - Headers background: - - - - - NULL value color: - - - - - Other settings - - - - - Print row numbers for data - - - - - Print page numbers - - - - diff --git a/Plugins/PdfExport/PdfExport_ru.qm b/Plugins/PdfExport/PdfExport_ru.qm deleted file mode 100644 index b74886b..0000000 Binary files a/Plugins/PdfExport/PdfExport_ru.qm and /dev/null differ diff --git a/Plugins/PdfExport/PdfExport_ru.ts b/Plugins/PdfExport/PdfExport_ru.ts deleted file mode 100644 index a904ee5..0000000 --- a/Plugins/PdfExport/PdfExport_ru.ts +++ /dev/null @@ -1,256 +0,0 @@ - - - - - PdfExport - - - SQLiteStudio v%1 - SQLiteStudio v%1 - - - - SQL query results - Результаты запроÑа SQL - - - - - Exported table: %1 - ЭкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°: %1 - - - - - Table: %1 - Таблица: %1 - - - - - Column - Столбец - - - - Data type - Тип данных - - - - Constraints - ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ - - - - Global table constraints - Глобальные Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð½Ð° таблицу - - - - Exported database: %1 - ЭкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных: %1 - - - - Index: %1 - ИндекÑ: %1 - - - - Property - index header - СвойÑтво - - - - Value - index header - Значение - - - - Indexed table - ПроиндекÑÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° - - - - Unique index - Уникальный Ð¸Ð½Ð´ÐµÐºÑ - - - - Yes - Да - - - - No - Ðет - - - - Collation - Сравнение - - - - Sort order - ПорÑдок Ñортировки - - - - Partial index condition - УÑловие Ð´Ð»Ñ Ñ‡Ð°Ñтичного индекÑа - - - - Trigger: %1 - Триггер: %1 - - - - Property - trigger header - СвойÑтво - - - - Value - trigger header - Значение - - - - Activation time - Ð’Ñ€ÐµÐ¼Ñ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ð¸Ð¸ - - - - For action - Ð”Ð»Ñ Ð´ÐµÐ¹ÑÑ‚Ð²Ð¸Ñ - - - - On view - Ð”Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ - - - - On table - Ð”Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ - - - - Activation condition - УÑловие активации - - - - Code executed - ИÑполненный код - - - - View: %1 - ПредÑтавление: %1 - - - - Query: - ЗапроÑ: - - - - Document generated with SQLiteStudio v%1 - Документ Ñгенерирован Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SQLiteStudio v%1 - - - - PdfExportConfig - - - Size and layout - Размеры и разметка - - - - Page size: - Размер Ñтраницы: - - - - Right margin: - Правое поле: - - - - Left margin: - Левое поле: - - - - Cell padding: - ОтÑтуп в Ñчейке: - - - - Limit characters in single cell: - Ограничение количеÑтва Ñимволов в Ñчейке: - - - - - - - - mm - мм - - - - Bottom margin: - Ðижнее поле: - - - - Top margin: - Верхнее поле: - - - - Font - Шрифт - - - - Colors - Цвета - - - - Headers background: - Фон заголовков: - - - - NULL value color: - Цвет Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL: - - - - Other settings - Прочие наÑтройки - - - - Print row numbers for data - Выводить номера Ñтрок данных - - - - Print page numbers - Выводить номера Ñтраниц - - - diff --git a/Plugins/PdfExport/PdfExport_sk.qm b/Plugins/PdfExport/PdfExport_sk.qm deleted file mode 100644 index 63e21f9..0000000 Binary files a/Plugins/PdfExport/PdfExport_sk.qm and /dev/null differ diff --git a/Plugins/PdfExport/PdfExport_sk.ts b/Plugins/PdfExport/PdfExport_sk.ts deleted file mode 100644 index 8de8acd..0000000 --- a/Plugins/PdfExport/PdfExport_sk.ts +++ /dev/null @@ -1,256 +0,0 @@ - - - - - PdfExport - - - SQLiteStudio v%1 - SQLiteStudio v%1 - - - - SQL query results - Výsledky SQL dotazu - - - - - Exported table: %1 - Vyexportovaná tabuľka: %1 - - - - - Table: %1 - Tabuľka: %1 - - - - - Column - Stĺpec - - - - Data type - Dátový typ - - - - Constraints - Obmedzenia - - - - Global table constraints - Globálne tabuľkové obmedzenia - - - - Exported database: %1 - Vyexportovaná databáza: %1 - - - - Index: %1 - Index: %1 - - - - Property - index header - Typ - - - - Value - index header - Hodnota - - - - Indexed table - Indexovaná tabuľka - - - - Unique index - JedineÄný index - - - - Yes - Ãno - - - - No - Nie - - - - Collation - Porovnávanie - - - - Sort order - Zoradenie - - - - Partial index condition - Podmienka parciálneho indexu - - - - Trigger: %1 - SpúšťaÄ: %1 - - - - Property - trigger header - Typ - - - - Value - trigger header - Hodnota - - - - Activation time - AktivaÄný Äas - - - - For action - Pre akciu - - - - On view - Na pohľade - - - - On table - Na tabuľke - - - - Activation condition - AktivaÄná podmienka - - - - Code executed - Vykonaný kód - - - - View: %1 - Pohľad: %1 - - - - Query: - Dotaz: - - - - Document generated with SQLiteStudio v%1 - Dokument vygenerovaný programom SQLiteStudio v%1 - - - - PdfExportConfig - - - Size and layout - VeľkosÅ¥ a rozvrhnutie - - - - Page size: - VeľkosÅ¥ strany: - - - - Right margin: - Pravý okraj: - - - - Left margin: - Ľavý okraj: - - - - Cell padding: - Odsadenie od okrajov bunky: - - - - Limit characters in single cell: - Maximálny poÄet znakov v jednej bunke: - - - - - - - - mm - mm - - - - Bottom margin: - Spodný okraj: - - - - Top margin: - Horný okraj: - - - - Font - Písmo - - - - Colors - Farby - - - - Headers background: - Farba pozadia Å¡truktúr: - - - - NULL value color: - Farba NULL hodnoty: - - - - Other settings - Iné nastavenia - - - - Print row numbers for data - Číslovanie dátových riadkov - - - - Print page numbers - Číslovanie strán - - - diff --git a/Plugins/PdfExport/PdfExport_zh_CN.qm b/Plugins/PdfExport/PdfExport_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/Plugins/PdfExport/PdfExport_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/Plugins/PdfExport/PdfExport_zh_CN.ts b/Plugins/PdfExport/PdfExport_zh_CN.ts deleted file mode 100644 index 8da2d8e..0000000 --- a/Plugins/PdfExport/PdfExport_zh_CN.ts +++ /dev/null @@ -1,256 +0,0 @@ - - - - - PdfExport - - - SQLiteStudio v%1 - - - - - SQL query results - - - - - - Exported table: %1 - - - - - - Table: %1 - - - - - - Column - - - - - Data type - - - - - Constraints - - - - - Global table constraints - - - - - Exported database: %1 - - - - - Index: %1 - - - - - Property - index header - - - - - Value - index header - - - - - Indexed table - - - - - Unique index - - - - - Yes - - - - - No - - - - - Collation - - - - - Sort order - - - - - Partial index condition - - - - - Trigger: %1 - - - - - Property - trigger header - - - - - Value - trigger header - - - - - Activation time - - - - - For action - - - - - On view - - - - - On table - - - - - Activation condition - - - - - Code executed - - - - - View: %1 - - - - - Query: - - - - - Document generated with SQLiteStudio v%1 - - - - - PdfExportConfig - - - Size and layout - - - - - Page size: - - - - - Right margin: - - - - - Left margin: - - - - - Cell padding: - - - - - Limit characters in single cell: - - - - - - - - - mm - - - - - Bottom margin: - - - - - Top margin: - - - - - Font - - - - - Colors - - - - - Headers background: - - - - - NULL value color: - - - - - Other settings - - - - - Print row numbers for data - - - - - Print page numbers - - - - diff --git a/Plugins/PdfExport/pdfexport.cpp b/Plugins/PdfExport/pdfexport.cpp index eea5b61..cc055a8 100644 --- a/Plugins/PdfExport/pdfexport.cpp +++ b/Plugins/PdfExport/pdfexport.cpp @@ -1,7 +1,6 @@ #include "pdfexport.h" #include "common/unused.h" #include "uiutils.h" -#include "log.h" #include #include #include @@ -9,8 +8,12 @@ QString PdfExport::bulletChar = "\u2022"; +CFG_DEFINE(PdfExportConfig) +#define PDFEXPORT_CFG CFG_INSTANCE(PdfExportConfig) + bool PdfExport::init() { + SQLS_INIT_RESOURCE(pdfexport); textOption = new QTextOption(); textOption->setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); return GenericExportPlugin::init(); @@ -19,6 +22,8 @@ bool PdfExport::init() void PdfExport::deinit() { safe_delete(textOption); + CFG_DELETE_INSTANCE(PdfExportConfig); + SQLS_CLEANUP_RESOURCE(pdfexport); } QPagedPaintDevice* PdfExport::createPaintDevice(const QString& documentTitle, bool &takeOwnership) @@ -100,7 +105,7 @@ bool PdfExport::exportTable(const QString& database, const QString& table, const QStringList columnsAndTypes; int colNamesLength = 0; int dataTypeLength = 0; - for (SqliteCreateTable::Column* col : createTable->columns) + for (SqliteCreateTable::Column*& col : createTable->columns) { colDef = col->name; colNamesLength = qMax(colNamesLength, colDef.size()); @@ -118,7 +123,7 @@ bool PdfExport::exportTable(const QString& database, const QString& table, const QList columnDataLengths = {colNamesLength, dataTypeLength, 0}; calculateDataColumnWidths(tableDdlColumns, columnDataLengths, 2); - for (SqliteCreateTable::Column* col : createTable->columns) + for (SqliteCreateTable::Column*& col : createTable->columns) exportTableColumnRow(col); if (createTable->constraints.size() > 0) @@ -232,7 +237,7 @@ bool PdfExport::exportIndex(const QString& database, const QString& name, const exportObjectColumnsHeader(indexColumns); QString sort; - for (SqliteOrderBy* idxCol : createIndex->indexedColumns) + for (SqliteOrderBy*& idxCol : createIndex->indexedColumns) { if (idxCol->order != SqliteSortOrder::null) sort = sqliteSortOrder(idxCol->order); @@ -279,7 +284,7 @@ bool PdfExport::exportTrigger(const QString& database, const QString& name, cons exportObjectRow({tr("Activation condition"), cond}); QStringList queryStrings; - for (SqliteQuery* q : createTrigger->queries) + for (SqliteQuery*& q : createTrigger->queries) queryStrings << q->detokenize(); exportObjectColumnsHeader({tr("Code executed")}); @@ -340,36 +345,36 @@ void PdfExport::cleanupAfterExport() void PdfExport::setupConfig() { - pagedWriter->setPageSize(convertPageSize(cfg.PdfExport.PageSize.get())); + pagedWriter->setPageSize(convertPageSize(PDFEXPORT_CFG.PdfExport.PageSize.get())); pageWidth = pagedWriter->width(); pageHeight = pagedWriter->height(); - pointsPerMm = pageWidth / pagedWriter->pageSizeMM().width(); + pointsPerMm = pageWidth / pagedWriter->pageLayout().pageSize().size(QPageSize::Millimeter).width(); - stdFont = cfg.PdfExport.Font.get(); - stdFont.setPointSize(cfg.PdfExport.FontSize.get()); + stdFont = PDFEXPORT_CFG.PdfExport.Font.get(); + stdFont.setPointSize(PDFEXPORT_CFG.PdfExport.FontSize.get()); boldFont = stdFont; boldFont.setBold(true); italicFont = stdFont; italicFont.setItalic(true); painter->setFont(stdFont); - topMargin = mmToPoints(cfg.PdfExport.TopMargin.get()); - rightMargin = mmToPoints(cfg.PdfExport.RightMargin.get()); - leftMargin = mmToPoints(cfg.PdfExport.LeftMargin.get()); - bottomMargin = mmToPoints(cfg.PdfExport.BottomMargin.get()); + topMargin = mmToPoints(PDFEXPORT_CFG.PdfExport.TopMargin.get()); + rightMargin = mmToPoints(PDFEXPORT_CFG.PdfExport.RightMargin.get()); + leftMargin = mmToPoints(PDFEXPORT_CFG.PdfExport.LeftMargin.get()); + bottomMargin = mmToPoints(PDFEXPORT_CFG.PdfExport.BottomMargin.get()); updateMargins(); maxColWidth = pageWidth / 5; - padding = mmToPoints(cfg.PdfExport.Padding.get()); + padding = mmToPoints(PDFEXPORT_CFG.PdfExport.Padding.get()); QRectF rect = painter->boundingRect(QRectF(padding, padding, pageWidth - 2 * padding, 1), "X", *textOption); minRowHeight = rect.height() + padding * 2; maxRowHeight = qMax((int)(pageHeight * 0.225), minRowHeight); rowsToPrebuffer = (int)qCeil((double)pageHeight / minRowHeight); - cellDataLimit = cfg.PdfExport.MaxCellBytes.get(); - printRowNum = cfg.PdfExport.PrintRowNum.get(); - printPageNumbers = cfg.PdfExport.PrintPageNumbers.get(); + cellDataLimit = PDFEXPORT_CFG.PdfExport.MaxCellBytes.get(); + printRowNum = PDFEXPORT_CFG.PdfExport.PrintRowNum.get(); + printPageNumbers = PDFEXPORT_CFG.PdfExport.PrintPageNumbers.get(); lastRowY = getContentsTop(); currentPage = -1; @@ -504,7 +509,7 @@ void PdfExport::exportTableColumnRow(SqliteCreateTable::Column* column) if (column->constraints.size() > 0) { - for (SqliteCreateTable::Column::Constraint* constr : column->constraints) + for (SqliteCreateTable::Column::Constraint*& constr : column->constraints) cell.contents << constr->detokenize(); cell.type = ObjectCell::Type::LIST; @@ -643,7 +648,7 @@ void PdfExport::drawObjectTopLine(int y) void PdfExport::drawObjectCellHeaderBackground(int x1, int y1, int x2, int y2) { painter->save(); - painter->setBrush(QBrush(cfg.PdfExport.HeaderBgColor.get(), Qt::SolidPattern)); + painter->setBrush(QBrush(PDFEXPORT_CFG.PdfExport.HeaderBgColor.get(), Qt::SolidPattern)); painter->setPen(Qt::NoPen); painter->drawRect(x1, y1, x2 - x1, y2 - y1); painter->restore(); @@ -709,7 +714,7 @@ void PdfExport::flushObjectRow(const PdfExport::ObjectRow& row, int y) x = left; painter->drawLine(x, y, x, bottom); - for (int w : calculatedObjectColumnWidths) + for (int& w : calculatedObjectColumnWidths) { x += w; painter->drawLine(x, y, x, bottom); @@ -755,7 +760,6 @@ void PdfExport::flushObjectCell(const PdfExport::ObjectCell& cell, int x, int y, x += padding; y += padding; w -= 2 * padding; - h -= 2 * padding; int txtX = x + prefixWidth; int txtW = w - prefixWidth; @@ -815,7 +819,7 @@ void PdfExport::calculateObjectColumnWidths(int columnToExpand) int PdfExport::correctMaxObjectColumnWidths(int colCount, int columnToExpand) { int totalWidth = 0; - for (int w : calculatedObjectColumnWidths) + for (int& w : calculatedObjectColumnWidths) totalWidth += w; int maxWidth = pageWidth / colCount; @@ -845,7 +849,6 @@ int PdfExport::correctMaxObjectColumnWidths(int colCount, int columnToExpand) if (columnToExpand > -1 && totalWidth > pageWidth) { - tmpWidth = calculatedObjectColumnWidths[columnToExpand]; if ((totalWidth - calculatedObjectColumnWidths[columnToExpand] + maxWidth) <= pageWidth) calculatedObjectColumnWidths[columnToExpand] -= (pageWidth - totalWidth + calculatedObjectColumnWidths[columnToExpand] - maxWidth); else @@ -954,7 +957,7 @@ void PdfExport::flushDataRowsPage(int columnStart, int columnEndBefore, int rows // Draw header background int x = getDataColumnsStartX(); painter->save(); - painter->setBrush(QBrush(cfg.PdfExport.HeaderBgColor.get(), Qt::SolidPattern)); + painter->setBrush(QBrush(PDFEXPORT_CFG.PdfExport.HeaderBgColor.get(), Qt::SolidPattern)); painter->setPen(Qt::NoPen); painter->drawRect(QRect(x, top, totalColumnsWidth, totalHeaderRowsHeight)); painter->restore(); @@ -963,7 +966,7 @@ void PdfExport::flushDataRowsPage(int columnStart, int columnEndBefore, int rows if (printRowNum) { painter->save(); - painter->setBrush(QBrush(cfg.PdfExport.HeaderBgColor.get(), Qt::SolidPattern)); + painter->setBrush(QBrush(PDFEXPORT_CFG.PdfExport.HeaderBgColor.get(), Qt::SolidPattern)); painter->setPen(Qt::NoPen); painter->drawRect(QRect(left, top, rowNumColumnWidth, totalRowsHeight)); painter->restore(); @@ -1071,7 +1074,7 @@ void PdfExport::flushDataCell(const QRect& rect, const PdfExport::DataCell& cell painter->save(); if (cell.isNull) { - painter->setPen(cfg.PdfExport.NullValueColor.get()); + painter->setPen(PDFEXPORT_CFG.PdfExport.NullValueColor.get()); painter->setFont(italicFont); } @@ -1405,7 +1408,7 @@ qreal PdfExport::mmToPoints(qreal sizeMM) CfgMain* PdfExport::getConfig() { - return &cfg; + return &PDFEXPORT_CFG; } QString PdfExport::getExportConfigFormName() const diff --git a/Plugins/PdfExport/pdfexport.h b/Plugins/PdfExport/pdfexport.h index 05f4938..ae6a46d 100644 --- a/Plugins/PdfExport/pdfexport.h +++ b/Plugins/PdfExport/pdfexport.h @@ -180,7 +180,7 @@ class PDFEXPORTSHARED_EXPORT PdfExport : public GenericExportPlugin int getContentsBottom() const; qreal mmToPoints(qreal sizeMM); - CFG_LOCAL_PERSISTABLE(PdfExportConfig, cfg) + //CFG_LOCAL_PERSISTABLE(PdfExportConfig, cfg) QPagedPaintDevice* pagedWriter = nullptr; bool takeDeviceOwnership = true; QPainter* painter = nullptr; diff --git a/Plugins/PdfExport/pdfexport.qrc b/Plugins/PdfExport/pdfexport.qrc index f3805fb..fce27e6 100644 --- a/Plugins/PdfExport/pdfexport.qrc +++ b/Plugins/PdfExport/pdfexport.qrc @@ -2,19 +2,4 @@ pdfexport.ui - - PdfExport_ro_RO.qm - PdfExport_de.qm - - - PdfExport_pl.qm - PdfExport_ru.qm - PdfExport_fr.qm - PdfExport_sk.qm - PdfExport_zh_CN.qm - - - - - diff --git a/Plugins/PdfExport/translations/PdfExport.ts b/Plugins/PdfExport/translations/PdfExport.ts new file mode 100644 index 0000000..6c06a01 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + + + + + SQL query results + + + + + + Exported table: %1 + + + + + + Table: %1 + + + + + + Column + + + + + Data type + + + + + Constraints + + + + + Global table constraints + + + + + Exported database: %1 + + + + + Index: %1 + + + + + Property + index header + + + + + Value + index header + + + + + Indexed table + + + + + Unique index + + + + + Yes + + + + + No + + + + + Collation + + + + + Sort order + + + + + Partial index condition + + + + + Trigger: %1 + + + + + Property + trigger header + + + + + Value + trigger header + + + + + Activation time + + + + + For action + + + + + On view + + + + + On table + + + + + Activation condition + + + + + Code executed + + + + + View: %1 + + + + + Query: + + + + + Document generated with SQLiteStudio v%1 + + + + + PdfExportConfig + + + Size and layout + + + + + Page size: + + + + + Right margin: + + + + + Left margin: + + + + + Cell padding: + + + + + Limit characters in single cell: + + + + + + + + + mm + + + + + Bottom margin: + + + + + Top margin: + + + + + Font + + + + + Colors + + + + + Headers background: + + + + + NULL value color: + + + + + Other settings + + + + + Print row numbers for data + + + + + Print page numbers + + + + diff --git a/Plugins/PdfExport/translations/PdfExport_af_ZA.ts b/Plugins/PdfExport/translations/PdfExport_af_ZA.ts new file mode 100644 index 0000000..a5784dd --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_af_ZA.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_ar_SA.ts b/Plugins/PdfExport/translations/PdfExport_ar_SA.ts new file mode 100644 index 0000000..65698f8 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_ar_SA.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_ca_ES.ts b/Plugins/PdfExport/translations/PdfExport_ca_ES.ts new file mode 100644 index 0000000..4dfbe2b --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_ca_ES.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_cs_CZ.ts b/Plugins/PdfExport/translations/PdfExport_cs_CZ.ts new file mode 100644 index 0000000..ab695d5 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_cs_CZ.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_da_DK.ts b/Plugins/PdfExport/translations/PdfExport_da_DK.ts new file mode 100644 index 0000000..9441475 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_da_DK.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_de_DE.ts b/Plugins/PdfExport/translations/PdfExport_de_DE.ts new file mode 100644 index 0000000..638cf08 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_de_DE.ts @@ -0,0 +1,257 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL Abfragenergebnis + + + + + Exported table: %1 + Exportierte Tabelle: %1 + + + + + Table: %1 + Tabelle: %1 + + + + + Column + Spalte + + + + Data type + Datentyp + + + + Constraints + Bedingungen + + + + Global table constraints + Globale Tabellenbedingungen + + + + Exported database: %1 + Exportierte Datenbank: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Eigenschaft + + + + Value + index header + Wert + + + + Indexed table + Indexierte Tabelle + + + + Unique index + Index (duplikatfrei) + + + + Yes + Ja + + + + No + Nein + + + + Collation + Kollation + + + + Sort order + Sortierreihenfolge + + + + Partial index condition + Teilindexbedingung + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Eigenschaft + + + + Value + trigger header + Wert + + + + Activation time + Ausführungszeit + + + + For action + Für Aktion + + + + On view + auf Index + + + + On table + auf Tabelle + + + + Activation condition + Ausführungskondition + + + + Code executed + Code ausgeführt + + + + View: %1 + View: %1 + + + + Query: + Abfrage: + + + + Document generated with SQLiteStudio v%1 + Dokument mit SQLiteStudio v%1 generiert + + + + PdfExportConfig + + + Size and layout + Format und Layout + + + + Page size: + Format: + + + + + Right margin: + Seitenabstand Rechts: + + + + Left margin: + Seitenabstand Links: + + + + Cell padding: + Zellenabstand: + + + + Limit characters in single cell: + Maximale Anzahl an Zeichen pro Zelle: + + + + + + + + mm + mm + + + + Bottom margin: + Seitenabstand Unten: + + + + Top margin: + Seitenabstand oben: + + + + Font + Schriftart + + + + Colors + Farben + + + + Headers background: + Hintergrundfarbe Kopfzeile: + + + + NULL value color: + NULL Wert Farbe: + + + + Other settings + Sonstige Einstellungen + + + + Print row numbers for data + Zeilennummerierung drucken + + + + Print page numbers + Seitenzahl drucken + + + diff --git a/Plugins/PdfExport/translations/PdfExport_el_GR.ts b/Plugins/PdfExport/translations/PdfExport_el_GR.ts new file mode 100644 index 0000000..03ba281 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_el_GR.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_en_US.ts b/Plugins/PdfExport/translations/PdfExport_en_US.ts new file mode 100644 index 0000000..7b1e720 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_en_US.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_es_ES.ts b/Plugins/PdfExport/translations/PdfExport_es_ES.ts new file mode 100644 index 0000000..67dfc49 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_es_ES.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + Resultados de la consulta SQL + + + + + Exported table: %1 + Tabla exportada: %1 + + + + + Table: %1 + Tabla: %1 + + + + + Column + Columna + + + + Data type + Tipo de dato + + + + Constraints + Restricciones + + + + Global table constraints + Restricciones de tabla globales + + + + Exported database: %1 + Base de datos exportada: %1 + + + + Index: %1 + Ãndice: %1 + + + + Property + index header + Propiedad + + + + Value + index header + Valor + + + + Indexed table + Tabla indexada + + + + Unique index + Ãndice único + + + + Yes + Sí + + + + No + No + + + + Collation + Cotejamiento + + + + Sort order + Ordenar por + + + + Partial index condition + Condición de índice parcial + + + + Trigger: %1 + Disparador: %1 + + + + Property + trigger header + Propiedad + + + + Value + trigger header + Valor + + + + Activation time + Hora de activación + + + + For action + Para la acción + + + + On view + En la vista + + + + On table + En la tabla + + + + Activation condition + Condición de activación + + + + Code executed + Código ejecutado + + + + View: %1 + Vista: %1 + + + + Query: + Consulta: + + + + Document generated with SQLiteStudio v%1 + Documento generado con SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Tamaño y diseño + + + + Page size: + Tamaño de la página: + + + + Right margin: + Margen derecho: + + + + Left margin: + Margen izquierdo: + + + + Cell padding: + Padding de la celda: + + + + Limit characters in single cell: + Limitar caracteres en una sola celda: + + + + + + + + mm + mm + + + + Bottom margin: + Margin inferior: + + + + Top margin: + Margen superior: + + + + Font + Fuente + + + + Colors + Colores + + + + Headers background: + Fondo de las cabeceras: + + + + NULL value color: + Color del valor NULO: + + + + Other settings + Otros ajustes + + + + Print row numbers for data + Imprimir números de fila para los datos + + + + Print page numbers + Imprimir números de página + + + diff --git a/Plugins/PdfExport/translations/PdfExport_fa_IR.ts b/Plugins/PdfExport/translations/PdfExport_fa_IR.ts new file mode 100644 index 0000000..e2b318b --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_fa_IR.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_fi_FI.ts b/Plugins/PdfExport/translations/PdfExport_fi_FI.ts new file mode 100644 index 0000000..79d24bd --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_fi_FI.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_fr_FR.ts b/Plugins/PdfExport/translations/PdfExport_fr_FR.ts new file mode 100644 index 0000000..b7235ce --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_fr_FR.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + Résultats de la requête SQL + + + + + Exported table: %1 + Tableau exporté : %1 + + + + + Table: %1 + Tableau : %1 + + + + + Column + Colonne + + + + Data type + Type de données + + + + Constraints + Contraintes + + + + Global table constraints + Contraintes globales du tableau + + + + Exported database: %1 + Base de données exportée : %1 + + + + Index: %1 + Index : %1 + + + + Property + index header + Proprièté + + + + Value + index header + Valeur + + + + Indexed table + Tableau indexé + + + + Unique index + Index unique + + + + Yes + Oui + + + + No + Non + + + + Collation + Classement + + + + Sort order + Ordre de tri + + + + Partial index condition + Condition de l’index partiel + + + + Trigger: %1 + Déchencheur : %1 + + + + Property + trigger header + Proprièté + + + + Value + trigger header + Valeur + + + + Activation time + Activation temps + + + + For action + Pour action + + + + On view + De la vue + + + + On table + Sur le tableau + + + + Activation condition + Activation de la condition + + + + Code executed + Code exécuté + + + + View: %1 + Vue : %1 + + + + Query: + Requête : + + + + Document generated with SQLiteStudio v%1 + Documentation générée avec SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Dimension et mise en page + + + + Page size: + Dimension page : + + + + Right margin: + Marge droite : + + + + Left margin: + Marge gauche : + + + + Cell padding: + Remplissage cellule : + + + + Limit characters in single cell: + Limite de caractères dans une cellule unique : + + + + + + + + mm + mm + + + + Bottom margin: + Marge inférieure : + + + + Top margin: + Marge supérieure : + + + + Font + Police + + + + Colors + Couleurs + + + + Headers background: + Arrière plan des en-têtes : + + + + NULL value color: + Couleur pour valeur NULL : + + + + Other settings + Autres paramètres + + + + Print row numbers for data + Nombre de lignes de données à imprimer + + + + Print page numbers + Imprimer nombre de pages + + + diff --git a/Plugins/PdfExport/translations/PdfExport_he_IL.ts b/Plugins/PdfExport/translations/PdfExport_he_IL.ts new file mode 100644 index 0000000..f578b0c --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_he_IL.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + תוצ×ות ש×ילתת SQL + + + + + Exported table: %1 + טבלה מיוצת: %1 + + + + + Table: %1 + טבלה: %1 + + + + + Column + עמודה + + + + Data type + סוג מידע + + + + Constraints + ××™×œ×•×¦×™× + + + + Global table constraints + ×ילוצי טבלה ×›×œ×œ×™×™× + + + + Exported database: %1 + מסד × ×ª×•× ×™× ×ž×™×•×¦×: %1 + + + + Index: %1 + מִפְתֵּחַ: %1 + + + + Property + index header + מ×פיין + + + + Value + index header + ערך + + + + Indexed table + טבלה ממופתחת + + + + Unique index + מפתח יחוד××™ + + + + Yes + כן + + + + No + ×œ× + + + + Collation + ×רגון + + + + Sort order + סדר מיון + + + + Partial index condition + תנ××™ מפתח חלקי + + + + Trigger: %1 + מַזְנֵק: %1 + + + + Property + trigger header + מ×פיין + + + + Value + trigger header + ערך + + + + Activation time + זמן שפעול + + + + For action + על פעולה + + + + On view + על מצג + + + + On table + על טבלה + + + + Activation condition + תנ××™ שפעול + + + + Code executed + הקוד בוצע + + + + View: %1 + מצג: %1 + + + + Query: + ש×ילתה: + + + + Document generated with SQLiteStudio v%1 + מסמך חולל על ידי SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + גודל ופריסה + + + + Page size: + גודל עמוד: + + + + Right margin: + ×©×•×œ×™×™× ×™×ž× ×™×™×: + + + + Left margin: + ×©×•×œ×™×™× ×©×ž×ליי×: + + + + Cell padding: + ריפוד ת×: + + + + Limit characters in single cell: + הגבלת ×ª×•×•×™× ×‘×ª× ×‘×•×“×“: + + + + + + + + mm + מ"מ + + + + Bottom margin: + ×©×•×œ×™×™× ×ª×—×ª×•× ×™×: + + + + Top margin: + ×©×•×œ×™×™× ×¢×œ×™×•× ×™×: + + + + Font + גופן + + + + Colors + ×¦×‘×¢×™× + + + + Headers background: + רקע כותרות: + + + + NULL value color: + צבע ערך NULL: + + + + Other settings + הגדרות ×חרות + + + + Print row numbers for data + הדפסת מספרי שורות ×œ× ×ª×•× ×™× + + + + Print page numbers + הדפסת מספרי עמוד + + + diff --git a/Plugins/PdfExport/translations/PdfExport_hu_HU.ts b/Plugins/PdfExport/translations/PdfExport_hu_HU.ts new file mode 100644 index 0000000..1323700 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_hu_HU.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_it_IT.ts b/Plugins/PdfExport/translations/PdfExport_it_IT.ts new file mode 100644 index 0000000..f06aba4 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_it_IT.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + Risultati della query SQL + + + + + Exported table: %1 + Tabella esportata: %1 + + + + + Table: %1 + Tabella: %1 + + + + + Column + Colonna + + + + Data type + Tipo di dati + + + + Constraints + Constraints + + + + Global table constraints + Constraints globali della tabella + + + + Exported database: %1 + Database esportato: %1 + + + + Index: %1 + Indice: %1 + + + + Property + index header + Proprietà + + + + Value + index header + Valore + + + + Indexed table + Tabella indicizzata + + + + Unique index + Indice unico + + + + Yes + Sì + + + + No + No + + + + Collation + Collation + + + + Sort order + Ordina per + + + + Partial index condition + Condizione indice parziale + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Proprietà + + + + Value + trigger header + Valore + + + + Activation time + Tempo di attivazione + + + + For action + Per azione + + + + On view + Su vista + + + + On table + Su tabella + + + + Activation condition + Condizione di attivazione + + + + Code executed + Codice eseguito + + + + View: %1 + Vista: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Documento generato con SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Dimensione e disposizione + + + + Page size: + Dimensione pagina: + + + + Right margin: + Margine destro: + + + + Left margin: + Margine sinistro: + + + + Cell padding: + Spaziatura cella: + + + + Limit characters in single cell: + Limite di caratteri in una singola cella: + + + + + + + + mm + mm + + + + Bottom margin: + Margine inferiore: + + + + Top margin: + Margine superiore: + + + + Font + Font + + + + Colors + Colori + + + + Headers background: + Sfondo intestazioni: + + + + NULL value color: + Colore del valore NULL: + + + + Other settings + Altre impostazioni + + + + Print row numbers for data + Stampa numeri di righe per i dati + + + + Print page numbers + Stampa numeri pagina + + + diff --git a/Plugins/PdfExport/translations/PdfExport_ja_JP.ts b/Plugins/PdfExport/translations/PdfExport_ja_JP.ts new file mode 100644 index 0000000..d089e14 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_ja_JP.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_kaa.ts b/Plugins/PdfExport/translations/PdfExport_kaa.ts new file mode 100644 index 0000000..bc56f76 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_kaa.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Qatar + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Awa + + + + No + Yaq + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_ko_KR.ts b/Plugins/PdfExport/translations/PdfExport_ko_KR.ts new file mode 100644 index 0000000..0ccfead --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_ko_KR.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_nl_NL.ts b/Plugins/PdfExport/translations/PdfExport_nl_NL.ts new file mode 100644 index 0000000..caabd5f --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_nl_NL.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_no_NO.ts b/Plugins/PdfExport/translations/PdfExport_no_NO.ts new file mode 100644 index 0000000..8e6d19d --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_no_NO.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_pl_PL.ts b/Plugins/PdfExport/translations/PdfExport_pl_PL.ts new file mode 100644 index 0000000..c8946b9 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_pl_PL.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + Wyniki zapytania SQL + + + + + Exported table: %1 + Eksportowana tabela: %1 + + + + + Table: %1 + Tabela: %1 + + + + + Column + Kolumna + + + + Data type + Typ danych + + + + Constraints + Ograniczenia + + + + Global table constraints + Globalne ograniczenia tabeli + + + + Exported database: %1 + Eksportowana baza: %1 + + + + Index: %1 + Indeks: %1 + + + + Property + index header + WÅ‚asność + + + + Value + index header + Wartość + + + + Indexed table + Zaindeksowana tabela + + + + Unique index + Indeks unikalny + + + + Yes + Tak + + + + No + Nie + + + + Collation + Zestawienie + + + + Sort order + Sortowanie + + + + Partial index condition + Warunek indeksu częściowego + + + + Trigger: %1 + Wyzwalacz: %1 + + + + Property + trigger header + WÅ‚asność + + + + Value + trigger header + Wartość + + + + Activation time + Moment aktywacji + + + + For action + Dla akcji + + + + On view + Na widoku + + + + On table + Na tabeli + + + + Activation condition + Warunek aktywacji + + + + Code executed + Kod do wykonania + + + + View: %1 + Widok: %1 + + + + Query: + Zapytanie: + + + + Document generated with SQLiteStudio v%1 + Dokument wygenerowany przy pomocy SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Rozmiar i ukÅ‚ad + + + + Page size: + Rozmiar strony: + + + + Right margin: + Prawy margines: + + + + Left margin: + Lewy margines: + + + + Cell padding: + OdstÄ™p wewnÄ…trz komórki: + + + + Limit characters in single cell: + Ogranicz liczbÄ™ znaków w komórce: + + + + + + + + mm + mm + + + + Bottom margin: + Dolny margines: + + + + Top margin: + Górny margines: + + + + Font + Czcionka + + + + Colors + Kolory + + + + Headers background: + TÅ‚o nagłówków: + + + + NULL value color: + Kolor wartoÅ›ci NULL: + + + + Other settings + Inne ustawienia + + + + Print row numbers for data + Drukuj numery wierszy dla danych + + + + Print page numbers + Drukuj numery stron + + + diff --git a/Plugins/PdfExport/translations/PdfExport_pt_BR.ts b/Plugins/PdfExport/translations/PdfExport_pt_BR.ts new file mode 100644 index 0000000..93495d6 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_pt_BR.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + Resultados da consulta SQL + + + + + Exported table: %1 + Tabela exportada: %1 + + + + + Table: %1 + Tabela: %1 + + + + + Column + Coluna + + + + Data type + Tipo de dados + + + + Constraints + Restrições + + + + Global table constraints + Restrições de tabela global + + + + Exported database: %1 + Banco de dados exportado: %1 + + + + Index: %1 + Ãndice: %1 + + + + Property + index header + Propriedade + + + + Value + index header + Valor + + + + Indexed table + Tabela de indexação + + + + Unique index + Ãndice exclusivo + + + + Yes + Sim + + + + No + Não + + + + Collation + Collation + + + + Sort order + Ordem de classificação + + + + Partial index condition + Condição do índice parcial + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Propriedade + + + + Value + trigger header + Valor + + + + Activation time + Tempo de ativação + + + + For action + Para a ação + + + + On view + Na visualização + + + + On table + Na tabela + + + + Activation condition + Condições para ativação + + + + Code executed + Código executado + + + + View: %1 + Visualizar: %1 + + + + Query: + Consulta: + + + + Document generated with SQLiteStudio v%1 + Documento gerado com SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Tamanho e layout + + + + Page size: + Tamanho da página: + + + + Right margin: + Margem direita: + + + + Left margin: + Margem esquerda: + + + + Cell padding: + Preenchimento da célula: + + + + Limit characters in single cell: + Limitar caracteres em uma única célula: + + + + + + + + mm + mm + + + + Bottom margin: + Margem inferior: + + + + Top margin: + Margem superior: + + + + Font + Fonte + + + + Colors + Cores + + + + Headers background: + Fundo dos cabeçalhos: + + + + NULL value color: + Cor do valor NULL: + + + + Other settings + Outras configurações + + + + Print row numbers for data + Imprimir números de linhas para dados + + + + Print page numbers + Imprimir número de páginas + + + diff --git a/Plugins/PdfExport/translations/PdfExport_pt_PT.ts b/Plugins/PdfExport/translations/PdfExport_pt_PT.ts new file mode 100644 index 0000000..d149604 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_pt_PT.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_ro_RO.ts b/Plugins/PdfExport/translations/PdfExport_ro_RO.ts new file mode 100644 index 0000000..b4f8d8d --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_ro_RO.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_ru_RU.ts b/Plugins/PdfExport/translations/PdfExport_ru_RU.ts new file mode 100644 index 0000000..0dd58b7 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_ru_RU.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + Результаты запроÑа SQL + + + + + Exported table: %1 + ЭкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°: %1 + + + + + Table: %1 + Таблица: %1 + + + + + Column + Столбец + + + + Data type + Тип данных + + + + Constraints + ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ + + + + Global table constraints + Глобальные Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð½Ð° таблицу + + + + Exported database: %1 + ЭкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных: %1 + + + + Index: %1 + ИндекÑ: %1 + + + + Property + index header + СвойÑтво + + + + Value + index header + Значение + + + + Indexed table + ПроиндекÑÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° + + + + Unique index + Уникальный Ð¸Ð½Ð´ÐµÐºÑ + + + + Yes + Да + + + + No + Ðет + + + + Collation + Сравнение + + + + Sort order + ПорÑдок Ñортировки + + + + Partial index condition + УÑловие Ð´Ð»Ñ Ñ‡Ð°Ñтичного индекÑа + + + + Trigger: %1 + Триггер: %1 + + + + Property + trigger header + СвойÑтво + + + + Value + trigger header + Значение + + + + Activation time + Ð’Ñ€ÐµÐ¼Ñ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ð¸Ð¸ + + + + For action + Ð”Ð»Ñ Ð´ÐµÐ¹ÑÑ‚Ð²Ð¸Ñ + + + + On view + Ð”Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ + + + + On table + Ð”Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ + + + + Activation condition + УÑловие активации + + + + Code executed + ИÑполненный код + + + + View: %1 + ПредÑтавление: %1 + + + + Query: + ЗапроÑ: + + + + Document generated with SQLiteStudio v%1 + Документ Ñгенерирован Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Размеры и разметка + + + + Page size: + Размер Ñтраницы: + + + + Right margin: + Правое поле: + + + + Left margin: + Левое поле: + + + + Cell padding: + ОтÑтуп в Ñчейке: + + + + Limit characters in single cell: + Ограничение количеÑтва Ñимволов в Ñчейке: + + + + + + + + mm + мм + + + + Bottom margin: + Ðижнее поле: + + + + Top margin: + Верхнее поле: + + + + Font + Шрифт + + + + Colors + Цвета + + + + Headers background: + Фон заголовков: + + + + NULL value color: + Цвет Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL: + + + + Other settings + Прочие наÑтройки + + + + Print row numbers for data + Выводить номера Ñтрок данных + + + + Print page numbers + Выводить номера Ñтраниц + + + diff --git a/Plugins/PdfExport/translations/PdfExport_sk_SK.ts b/Plugins/PdfExport/translations/PdfExport_sk_SK.ts new file mode 100644 index 0000000..8a16d63 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_sk_SK.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + Výsledky SQL dotazu + + + + + Exported table: %1 + Vyexportovaná tabuľka: %1 + + + + + Table: %1 + Tabuľka: %1 + + + + + Column + Stĺpec + + + + Data type + Dátový typ + + + + Constraints + Obmedzenia + + + + Global table constraints + Globálne tabuľkové obmedzenia + + + + Exported database: %1 + Vyexportovaná databáza: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Typ + + + + Value + index header + Hodnota + + + + Indexed table + Indexovaná tabuľka + + + + Unique index + JedineÄný index + + + + Yes + Ãno + + + + No + Nie + + + + Collation + Porovnávanie + + + + Sort order + Zoradenie + + + + Partial index condition + Podmienka parciálneho indexu + + + + Trigger: %1 + SpúšťaÄ: %1 + + + + Property + trigger header + Typ + + + + Value + trigger header + Hodnota + + + + Activation time + AktivaÄný Äas + + + + For action + Pre akciu + + + + On view + Na pohľade + + + + On table + Na tabuľke + + + + Activation condition + AktivaÄná podmienka + + + + Code executed + Vykonaný kód + + + + View: %1 + Pohľad: %1 + + + + Query: + Dotaz: + + + + Document generated with SQLiteStudio v%1 + Dokument vygenerovaný programom SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + VeľkosÅ¥ a rozvrhnutie + + + + Page size: + VeľkosÅ¥ strany: + + + + Right margin: + Pravý okraj: + + + + Left margin: + Ľavý okraj: + + + + Cell padding: + Odsadenie od okrajov bunky: + + + + Limit characters in single cell: + Maximálny poÄet znakov v jednej bunke: + + + + + + + + mm + mm + + + + Bottom margin: + Spodný okraj: + + + + Top margin: + Horný okraj: + + + + Font + Písmo + + + + Colors + Farby + + + + Headers background: + Farba pozadia Å¡truktúr: + + + + NULL value color: + Farba NULL hodnoty: + + + + Other settings + Iné nastavenia + + + + Print row numbers for data + Číslovanie dátových riadkov + + + + Print page numbers + Číslovanie strán + + + diff --git a/Plugins/PdfExport/translations/PdfExport_sr_SP.ts b/Plugins/PdfExport/translations/PdfExport_sr_SP.ts new file mode 100644 index 0000000..bff953f --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_sr_SP.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_sv_SE.ts b/Plugins/PdfExport/translations/PdfExport_sv_SE.ts new file mode 100644 index 0000000..8d30fd8 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_sv_SE.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_tr_TR.ts b/Plugins/PdfExport/translations/PdfExport_tr_TR.ts new file mode 100644 index 0000000..fe973cd --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_tr_TR.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL sorgu sonuçları + + + + + Exported table: %1 + Çıkartılan tablo: %1 + + + + + Table: %1 + Tablo: %1 + + + + + Column + Kolon + + + + Data type + Veri tipi + + + + Constraints + Constraints + + + + Global table constraints + Global tablo constraints + + + + Exported database: %1 + Çıkartılan veritabanı: %1 + + + + Index: %1 + Indeks: %1 + + + + Property + index header + Özellik + + + + Value + index header + DeÄŸer + + + + Indexed table + İndekslenen tablo + + + + Unique index + Unique index + + + + Yes + Evet + + + + No + Hayır + + + + Collation + Collation + + + + Sort order + Sıralama düzeni + + + + Partial index condition + Partial indeks koÅŸulu + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Özellik + + + + Value + trigger header + DeÄŸer + + + + Activation time + Aktivasyon zamanı + + + + For action + İşlemi için + + + + On view + View + + + + On table + Tablo + + + + Activation condition + Aktivasyon koÅŸulu + + + + Code executed + Çalıştırılan kod + + + + View: %1 + View: %1 + + + + Query: + Sorgu: + + + + Document generated with SQLiteStudio v%1 + SQLiteStudio ile üretilen döküman üzerinde v%1 + + + + PdfExportConfig + + + Size and layout + Ölçü ve düzen + + + + Page size: + Sayfa boyutu: + + + + Right margin: + SaÄŸ kenar boÅŸluÄŸu: + + + + Left margin: + Sol kenar boÅŸluÄŸu: + + + + Cell padding: + Hücre boÅŸluÄŸu: + + + + Limit characters in single cell: + Bir hücredeki karakter limiti: + + + + + + + + mm + mm + + + + Bottom margin: + Alt kenar boÅŸluÄŸu: + + + + Top margin: + Üst kenar boÅŸluÄŸu: + + + + Font + Yazı tipi + + + + Colors + Renkler + + + + Headers background: + BaÅŸlık arkaplanı: + + + + NULL value color: + NULL deÄŸer rengi: + + + + Other settings + DiÄŸer ayarlar + + + + Print row numbers for data + Veriler için satır numarasını yazdır + + + + Print page numbers + Sayfa numaralarını yazdır + + + diff --git a/Plugins/PdfExport/translations/PdfExport_uk_UA.ts b/Plugins/PdfExport/translations/PdfExport_uk_UA.ts new file mode 100644 index 0000000..a9e3e02 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_uk_UA.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_vi_VN.ts b/Plugins/PdfExport/translations/PdfExport_vi_VN.ts new file mode 100644 index 0000000..92abf41 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_vi_VN.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL query results + + + + + Exported table: %1 + Exported table: %1 + + + + + Table: %1 + Table: %1 + + + + + Column + Column + + + + Data type + Data type + + + + Constraints + Constraints + + + + Global table constraints + Global table constraints + + + + Exported database: %1 + Exported database: %1 + + + + Index: %1 + Index: %1 + + + + Property + index header + Property + + + + Value + index header + Value + + + + Indexed table + Indexed table + + + + Unique index + Unique index + + + + Yes + Yes + + + + No + No + + + + Collation + Collation + + + + Sort order + Sort order + + + + Partial index condition + Partial index condition + + + + Trigger: %1 + Trigger: %1 + + + + Property + trigger header + Property + + + + Value + trigger header + Value + + + + Activation time + Activation time + + + + For action + For action + + + + On view + On view + + + + On table + On table + + + + Activation condition + Activation condition + + + + Code executed + Code executed + + + + View: %1 + View: %1 + + + + Query: + Query: + + + + Document generated with SQLiteStudio v%1 + Document generated with SQLiteStudio v%1 + + + + PdfExportConfig + + + Size and layout + Size and layout + + + + Page size: + Page size: + + + + Right margin: + Right margin: + + + + Left margin: + Left margin: + + + + Cell padding: + Cell padding: + + + + Limit characters in single cell: + Limit characters in single cell: + + + + + + + + mm + mm + + + + Bottom margin: + Bottom margin: + + + + Top margin: + Top margin: + + + + Font + Font + + + + Colors + Colors + + + + Headers background: + Headers background: + + + + NULL value color: + NULL value color: + + + + Other settings + Other settings + + + + Print row numbers for data + Print row numbers for data + + + + Print page numbers + Print page numbers + + + diff --git a/Plugins/PdfExport/translations/PdfExport_zh_CN.ts b/Plugins/PdfExport/translations/PdfExport_zh_CN.ts new file mode 100644 index 0000000..1ea90a4 --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_zh_CN.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL 查询结果 + + + + + Exported table: %1 + 导出的表:%1 + + + + + Table: %1 + 表:%1 + + + + + Column + 列 + + + + Data type + æ•°æ®ç±»åž‹ + + + + Constraints + çº¦æŸ + + + + Global table constraints + å…¨å±€è¡¨çº¦æŸ + + + + Exported database: %1 + 导出的数æ®åº“:%1 + + + + Index: %1 + 索引:%1 + + + + Property + index header + 属性 + + + + Value + index header + 值 + + + + Indexed table + 索引表 + + + + Unique index + 唯一索引 + + + + Yes + 是 + + + + No + å¦ + + + + Collation + å­—ç¬¦åº + + + + Sort order + æŽ’åºæ–¹å¼ + + + + Partial index condition + 部分索引æ¡ä»¶ + + + + Trigger: %1 + 触å‘器:%1 + + + + Property + trigger header + 属性 + + + + Value + trigger header + 值 + + + + Activation time + 激活时间 + + + + For action + 对动作 + + + + On view + 视图 + + + + On table + 表 + + + + Activation condition + 激活æ¡ä»¶ + + + + Code executed + æ‰§è¡Œä»£ç  + + + + View: %1 + 视图:%1 + + + + Query: + 查询: + + + + Document generated with SQLiteStudio v%1 + 使用 SQLiteStudio v%1 生æˆçš„æ–‡æ¡£ + + + + PdfExportConfig + + + Size and layout + 大å°ä¸Žå¸ƒå±€ + + + + Page size: + 页é¢å¤§å°ï¼š + + + + Right margin: + å³è¾¹è·ï¼š + + + + Left margin: + 左边è·ï¼š + + + + Cell padding: + å•元格间è·ï¼š + + + + Limit characters in single cell: + é™åˆ¶å•个å•元格字符数: + + + + + + + + mm + 毫米 + + + + Bottom margin: + 下边è·ï¼š + + + + Top margin: + 上边è·ï¼š + + + + Font + 字体 + + + + Colors + 颜色 + + + + Headers background: + 标题背景色: + + + + NULL value color: + NULL 值颜色: + + + + Other settings + 其他设置 + + + + Print row numbers for data + æ‰“å°æ•°æ®è¡Œå· + + + + Print page numbers + 打å°é¡µç  + + + diff --git a/Plugins/PdfExport/translations/PdfExport_zh_TW.ts b/Plugins/PdfExport/translations/PdfExport_zh_TW.ts new file mode 100644 index 0000000..fec7aae --- /dev/null +++ b/Plugins/PdfExport/translations/PdfExport_zh_TW.ts @@ -0,0 +1,256 @@ + + + + + PdfExport + + + SQLiteStudio v%1 + SQLiteStudio v%1 + + + + SQL query results + SQL æŸ¥è©¢çµæžœ + + + + + Exported table: %1 + 匯出的表:%1 + + + + + Table: %1 + 表:%1 + + + + + Column + 列 + + + + Data type + 資料型別 + + + + Constraints + ç´„æŸ + + + + Global table constraints + å…¨åŸŸæ€§è¡¨ç´„æŸ + + + + Exported database: %1 + 匯出的資料庫:%1 + + + + Index: %1 + 索引:%1 + + + + Property + index header + 屬性 + + + + Value + index header + 值 + + + + Indexed table + 索引表 + + + + Unique index + 唯一索引 + + + + Yes + 是 + + + + No + å¦ + + + + Collation + å­—å…ƒåº + + + + Sort order + æŽ’åºæ–¹å¼ + + + + Partial index condition + 部分索引æ¢ä»¶ + + + + Trigger: %1 + 觸發器:%1 + + + + Property + trigger header + 屬性 + + + + Value + trigger header + 值 + + + + Activation time + 啟用時間 + + + + For action + å°å‹•作 + + + + On view + 檢視 + + + + On table + 表 + + + + Activation condition + 啟用æ¢ä»¶ + + + + Code executed + 執行程å¼ç¢¼ + + + + View: %1 + 檢視:%1 + + + + Query: + 查詢: + + + + Document generated with SQLiteStudio v%1 + 使用 SQLiteStudio v%1 生æˆçš„æ–‡ä»¶ + + + + PdfExportConfig + + + Size and layout + 大å°èˆ‡ä½ˆå±€ + + + + Page size: + é é¢å¤§å°ï¼š + + + + Right margin: + å³é‚Šè·ï¼š + + + + Left margin: + 左邊è·ï¼š + + + + Cell padding: + 單元格間è·ï¼š + + + + Limit characters in single cell: + é™åˆ¶å–®å€‹å–®å…ƒæ ¼å­—元數: + + + + + + + + mm + 毫米 + + + + Bottom margin: + 下邊è·ï¼š + + + + Top margin: + 上邊è·ï¼š + + + + Font + å­—åž‹ + + + + Colors + é¡è‰² + + + + Headers background: + 標題背景色: + + + + NULL value color: + NULL 值é¡è‰²ï¼š + + + + Other settings + 其他設定 + + + + Print row numbers for data + 列å°è³‡æ–™è¡Œè™Ÿ + + + + Print page numbers + 列å°é ç¢¼ + + + diff --git a/Plugins/Plugins.pro b/Plugins/Plugins.pro index a88e000..2f0ab22 100644 --- a/Plugins/Plugins.pro +++ b/Plugins/Plugins.pro @@ -17,8 +17,10 @@ SUBDIRS += \ SqlEnterpriseFormatter \ ConfigMigration \ ScriptingTcl \ + ScriptingPython \ DbAndroid \ DbSqliteCipher \ DbSqliteWx \ - MultiEditorImage + MultiEditorImage \ + PythonSyntaxHighlighter diff --git a/Plugins/Printing/Printing.pro b/Plugins/Printing/Printing.pro index 73d0d4a..cba1af2 100644 --- a/Plugins/Printing/Printing.pro +++ b/Plugins/Printing/Printing.pro @@ -33,31 +33,3 @@ win32|macx: { RESOURCES += \ printing.qrc - - -TRANSLATIONS += Printing_ro_RO.ts \ - Printing_de.ts \ - Printing_it.ts \ - Printing_zh_CN.ts \ - Printing_sk.ts \ - Printing_ru.ts \ - Printing_pt_BR.ts \ - Printing_fr.ts \ - Printing_es.ts \ - Printing_pl.ts - - - - - - - - - - - - - - - - diff --git a/Plugins/Printing/Printing_de.qm b/Plugins/Printing/Printing_de.qm deleted file mode 100644 index 59adbab..0000000 Binary files a/Plugins/Printing/Printing_de.qm and /dev/null differ diff --git a/Plugins/Printing/Printing_de.ts b/Plugins/Printing/Printing_de.ts deleted file mode 100644 index 1bf6ffd..0000000 --- a/Plugins/Printing/Printing_de.ts +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Printing - - - Print data - Daten drucken - - - - Print query - Abfrage drucken - - - - No data to print. - Keine Daten, die gedruckt werden müssen. - - - - Printing data. - Drucke Daten. - - - - Printing query. - Drucke Abfrage. - - - - PrintingExport - - - Printing - Drucken - - - diff --git a/Plugins/Printing/Printing_es.qm b/Plugins/Printing/Printing_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/Printing/Printing_es.qm and /dev/null differ diff --git a/Plugins/Printing/Printing_es.ts b/Plugins/Printing/Printing_es.ts deleted file mode 100644 index 561c158..0000000 --- a/Plugins/Printing/Printing_es.ts +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Printing - - - Print data - - - - - Print query - - - - - No data to print. - - - - - Printing data. - - - - - Printing query. - - - - - PrintingExport - - - Printing - - - - diff --git a/Plugins/Printing/Printing_fr.qm b/Plugins/Printing/Printing_fr.qm deleted file mode 100644 index 2f50430..0000000 Binary files a/Plugins/Printing/Printing_fr.qm and /dev/null differ diff --git a/Plugins/Printing/Printing_fr.ts b/Plugins/Printing/Printing_fr.ts deleted file mode 100644 index 2064034..0000000 --- a/Plugins/Printing/Printing_fr.ts +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Printing - - - Print data - Imprimer les données - - - - Print query - Requête imprimée - - - - No data to print. - Aucune données à imrpimer.. - - - - Printing data. - Impression des données. - - - - Printing query. - Impression de la requête. - - - - PrintingExport - - - Printing - Impression - - - diff --git a/Plugins/Printing/Printing_it.qm b/Plugins/Printing/Printing_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/Printing/Printing_it.qm and /dev/null differ diff --git a/Plugins/Printing/Printing_it.ts b/Plugins/Printing/Printing_it.ts deleted file mode 100644 index fb5861d..0000000 --- a/Plugins/Printing/Printing_it.ts +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Printing - - - Print data - - - - - Print query - - - - - No data to print. - - - - - Printing data. - - - - - Printing query. - - - - - PrintingExport - - - Printing - - - - diff --git a/Plugins/Printing/Printing_pl.qm b/Plugins/Printing/Printing_pl.qm deleted file mode 100644 index 0a736b9..0000000 Binary files a/Plugins/Printing/Printing_pl.qm and /dev/null differ diff --git a/Plugins/Printing/Printing_pl.ts b/Plugins/Printing/Printing_pl.ts deleted file mode 100644 index dcacb58..0000000 --- a/Plugins/Printing/Printing_pl.ts +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Printing - - - Print data - Drukuj dane - - - - Print query - Drukuj zapytanie - - - - No data to print. - Brak danych do wydruku. - - - - Printing data. - Drukuj dane. - - - - Printing query. - Drukuj zapytanie. - - - - PrintingExport - - - Printing - Drukuj - - - diff --git a/Plugins/Printing/Printing_pt_BR.qm b/Plugins/Printing/Printing_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/Printing/Printing_pt_BR.qm and /dev/null differ diff --git a/Plugins/Printing/Printing_pt_BR.ts b/Plugins/Printing/Printing_pt_BR.ts deleted file mode 100644 index ed93a6d..0000000 --- a/Plugins/Printing/Printing_pt_BR.ts +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Printing - - - Print data - - - - - Print query - - - - - No data to print. - - - - - Printing data. - - - - - Printing query. - - - - - PrintingExport - - - Printing - - - - diff --git a/Plugins/Printing/Printing_ro_RO.qm b/Plugins/Printing/Printing_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/Printing/Printing_ro_RO.qm and /dev/null differ diff --git a/Plugins/Printing/Printing_ro_RO.ts b/Plugins/Printing/Printing_ro_RO.ts deleted file mode 100644 index da6fd7d..0000000 --- a/Plugins/Printing/Printing_ro_RO.ts +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Printing - - - Print data - - - - - Print query - - - - - No data to print. - - - - - Printing data. - - - - - Printing query. - - - - - PrintingExport - - - Printing - - - - diff --git a/Plugins/Printing/Printing_ru.qm b/Plugins/Printing/Printing_ru.qm deleted file mode 100644 index 4e6e864..0000000 Binary files a/Plugins/Printing/Printing_ru.qm and /dev/null differ diff --git a/Plugins/Printing/Printing_ru.ts b/Plugins/Printing/Printing_ru.ts deleted file mode 100644 index 0644f5b..0000000 --- a/Plugins/Printing/Printing_ru.ts +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Printing - - - Print data - Печать данных - - - - Print query - Печать запроÑа - - - - No data to print. - Ðет данных Ð´Ð»Ñ Ð¿ÐµÑ‡Ð°Ñ‚Ð¸. - - - - Printing data. - Печать данных. - - - - Printing query. - Печать запроÑа. - - - - PrintingExport - - - Printing - Печать - - - diff --git a/Plugins/Printing/Printing_sk.qm b/Plugins/Printing/Printing_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/Plugins/Printing/Printing_sk.qm and /dev/null differ diff --git a/Plugins/Printing/Printing_sk.ts b/Plugins/Printing/Printing_sk.ts deleted file mode 100644 index 0bfaae9..0000000 --- a/Plugins/Printing/Printing_sk.ts +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Printing - - - Print data - - - - - Print query - - - - - No data to print. - - - - - Printing data. - - - - - Printing query. - - - - - PrintingExport - - - Printing - - - - diff --git a/Plugins/Printing/Printing_zh_CN.qm b/Plugins/Printing/Printing_zh_CN.qm deleted file mode 100644 index ec96017..0000000 Binary files a/Plugins/Printing/Printing_zh_CN.qm and /dev/null differ diff --git a/Plugins/Printing/Printing_zh_CN.ts b/Plugins/Printing/Printing_zh_CN.ts deleted file mode 100644 index 162dbfb..0000000 --- a/Plugins/Printing/Printing_zh_CN.ts +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Printing - - - Print data - æ‰“å°æ•°æ® - - - - Print query - æ‰“å° query - - - - No data to print. - 没有数æ®å¯æ‰“å°ã€‚ - - - - Printing data. - æ‰“å°æ•°æ®ä¸­ã€‚ - - - - Printing query. - 打å°query中。 - - - - PrintingExport - - - Printing - æ‰“å° - - - diff --git a/Plugins/Printing/printing.cpp b/Plugins/Printing/printing.cpp index c24fc89..a035078 100644 --- a/Plugins/Printing/printing.cpp +++ b/Plugins/Printing/printing.cpp @@ -17,7 +17,7 @@ bool Printing::init() { - Q_INIT_RESOURCE(printing); + SQLS_INIT_RESOURCE(printing); printingExport = new PrintingExport(); bool printingExportInit = printingExport->init(); @@ -57,7 +57,7 @@ void Printing::deinit() safe_delete(printDataAction); safe_delete(separatorAction); safe_delete(printQueryAction); - Q_CLEANUP_RESOURCE(printing); + SQLS_CLEANUP_RESOURCE(printing); } void Printing::dataPrintRequested(ExtActionContainer* actionContainer) diff --git a/Plugins/Printing/printing.qrc b/Plugins/Printing/printing.qrc index e03e60d..faf09c1 100644 --- a/Plugins/Printing/printing.qrc +++ b/Plugins/Printing/printing.qrc @@ -2,19 +2,4 @@ printer.png - - Printing_ro_RO.qm - Printing_de.qm - - - Printing_pl.qm - Printing_ru.qm - Printing_fr.qm - Printing_sk.qm - Printing_zh_CN.qm - - - - - diff --git a/Plugins/Printing/printingexport.cpp b/Plugins/Printing/printingexport.cpp index b932e28..ec46fc2 100644 --- a/Plugins/Printing/printingexport.cpp +++ b/Plugins/Printing/printingexport.cpp @@ -1,7 +1,5 @@ #include "printingexport.h" #include "common/unused.h" -#include "mainwindow.h" -#include "services/notifymanager.h" QPagedPaintDevice* PrintingExport::createPaintDevice(const QString& documentTitle, bool& takeOwnership) { diff --git a/Plugins/Printing/translations/Printing.ts b/Plugins/Printing/translations/Printing.ts new file mode 100644 index 0000000..1908226 --- /dev/null +++ b/Plugins/Printing/translations/Printing.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + + + + + Print query + + + + + No data to print. + + + + + Printing data. + + + + + Printing query. + + + + + PrintingExport + + + Printing + + + + diff --git a/Plugins/Printing/translations/Printing_af_ZA.ts b/Plugins/Printing/translations/Printing_af_ZA.ts new file mode 100644 index 0000000..b0ee78f --- /dev/null +++ b/Plugins/Printing/translations/Printing_af_ZA.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_ar_SA.ts b/Plugins/Printing/translations/Printing_ar_SA.ts new file mode 100644 index 0000000..e6a1ad8 --- /dev/null +++ b/Plugins/Printing/translations/Printing_ar_SA.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_ca_ES.ts b/Plugins/Printing/translations/Printing_ca_ES.ts new file mode 100644 index 0000000..fa73be3 --- /dev/null +++ b/Plugins/Printing/translations/Printing_ca_ES.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_cs_CZ.ts b/Plugins/Printing/translations/Printing_cs_CZ.ts new file mode 100644 index 0000000..cc286e0 --- /dev/null +++ b/Plugins/Printing/translations/Printing_cs_CZ.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_da_DK.ts b/Plugins/Printing/translations/Printing_da_DK.ts new file mode 100644 index 0000000..96e9ee5 --- /dev/null +++ b/Plugins/Printing/translations/Printing_da_DK.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_de_DE.ts b/Plugins/Printing/translations/Printing_de_DE.ts new file mode 100644 index 0000000..14268f9 --- /dev/null +++ b/Plugins/Printing/translations/Printing_de_DE.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Daten drucken + + + + Print query + Abfrage drucken + + + + No data to print. + Keine Daten, die gedruckt werden müssen. + + + + Printing data. + Drucke Daten. + + + + Printing query. + Drucke Abfrage. + + + + PrintingExport + + + Printing + Drucken + + + diff --git a/Plugins/Printing/translations/Printing_el_GR.ts b/Plugins/Printing/translations/Printing_el_GR.ts new file mode 100644 index 0000000..4798c63 --- /dev/null +++ b/Plugins/Printing/translations/Printing_el_GR.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_en_US.ts b/Plugins/Printing/translations/Printing_en_US.ts new file mode 100644 index 0000000..2ba5dbe --- /dev/null +++ b/Plugins/Printing/translations/Printing_en_US.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_es_ES.ts b/Plugins/Printing/translations/Printing_es_ES.ts new file mode 100644 index 0000000..f2f8c3c --- /dev/null +++ b/Plugins/Printing/translations/Printing_es_ES.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Imprimir datos + + + + Print query + Imprimir consulta + + + + No data to print. + Sin datos para imprimir. + + + + Printing data. + Imprimiendo datos. + + + + Printing query. + Imprimiendo consulta. + + + + PrintingExport + + + Printing + Imprimiendo + + + diff --git a/Plugins/Printing/translations/Printing_fa_IR.ts b/Plugins/Printing/translations/Printing_fa_IR.ts new file mode 100644 index 0000000..e22d0ee --- /dev/null +++ b/Plugins/Printing/translations/Printing_fa_IR.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_fi_FI.ts b/Plugins/Printing/translations/Printing_fi_FI.ts new file mode 100644 index 0000000..2364f10 --- /dev/null +++ b/Plugins/Printing/translations/Printing_fi_FI.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_fr_FR.ts b/Plugins/Printing/translations/Printing_fr_FR.ts new file mode 100644 index 0000000..bf342ef --- /dev/null +++ b/Plugins/Printing/translations/Printing_fr_FR.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Imprimer les données + + + + Print query + Requête imprimée + + + + No data to print. + Aucune données à imrpimer.. + + + + Printing data. + Impression des données. + + + + Printing query. + Impression de la requête. + + + + PrintingExport + + + Printing + Impression + + + diff --git a/Plugins/Printing/translations/Printing_he_IL.ts b/Plugins/Printing/translations/Printing_he_IL.ts new file mode 100644 index 0000000..b95fd1e --- /dev/null +++ b/Plugins/Printing/translations/Printing_he_IL.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + הדפסת × ×ª×•× ×™× + + + + Print query + הדפסת ש×ילתה + + + + No data to print. + ×ין × ×ª×•× ×™× ×œ×”×“×¤×¡×”. + + + + Printing data. + הדפסת נתוני×. + + + + Printing query. + הדפסת ש×ילתה. + + + + PrintingExport + + + Printing + מדפיס + + + diff --git a/Plugins/Printing/translations/Printing_hu_HU.ts b/Plugins/Printing/translations/Printing_hu_HU.ts new file mode 100644 index 0000000..5b55317 --- /dev/null +++ b/Plugins/Printing/translations/Printing_hu_HU.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Adatok nyomtatása + + + + Print query + Lekérdezés nyomtatása + + + + No data to print. + Nincs nyomtatni való adat. + + + + Printing data. + Adatok nyomtatása folyamatban. + + + + Printing query. + Lekérdezés nyomtatása folyamatban. + + + + PrintingExport + + + Printing + Nyomtatás folyamatban + + + diff --git a/Plugins/Printing/translations/Printing_it_IT.ts b/Plugins/Printing/translations/Printing_it_IT.ts new file mode 100644 index 0000000..f222641 --- /dev/null +++ b/Plugins/Printing/translations/Printing_it_IT.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Stampa dati + + + + Print query + Query di stampa + + + + No data to print. + Nessun dato da stampare. + + + + Printing data. + Stampa dei dati. + + + + Printing query. + Stampa query. + + + + PrintingExport + + + Printing + Stampa in corso... + + + diff --git a/Plugins/Printing/translations/Printing_ja_JP.ts b/Plugins/Printing/translations/Printing_ja_JP.ts new file mode 100644 index 0000000..34ee06f --- /dev/null +++ b/Plugins/Printing/translations/Printing_ja_JP.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_kaa.ts b/Plugins/Printing/translations/Printing_kaa.ts new file mode 100644 index 0000000..b8f897e --- /dev/null +++ b/Plugins/Printing/translations/Printing_kaa.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_ko_KR.ts b/Plugins/Printing/translations/Printing_ko_KR.ts new file mode 100644 index 0000000..f7723f5 --- /dev/null +++ b/Plugins/Printing/translations/Printing_ko_KR.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_nl_NL.ts b/Plugins/Printing/translations/Printing_nl_NL.ts new file mode 100644 index 0000000..f2f8a13 --- /dev/null +++ b/Plugins/Printing/translations/Printing_nl_NL.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_no_NO.ts b/Plugins/Printing/translations/Printing_no_NO.ts new file mode 100644 index 0000000..606772d --- /dev/null +++ b/Plugins/Printing/translations/Printing_no_NO.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_pl_PL.ts b/Plugins/Printing/translations/Printing_pl_PL.ts new file mode 100644 index 0000000..425896f --- /dev/null +++ b/Plugins/Printing/translations/Printing_pl_PL.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Drukuj dane + + + + Print query + Drukuj zapytanie + + + + No data to print. + Brak danych do wydruku. + + + + Printing data. + Drukuj dane. + + + + Printing query. + Drukuj zapytanie. + + + + PrintingExport + + + Printing + Drukuj + + + diff --git a/Plugins/Printing/translations/Printing_pt_BR.ts b/Plugins/Printing/translations/Printing_pt_BR.ts new file mode 100644 index 0000000..b1df67a --- /dev/null +++ b/Plugins/Printing/translations/Printing_pt_BR.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Imprimir dados + + + + Print query + Consulta de impressão + + + + No data to print. + Sem dados para imprimir. + + + + Printing data. + Imprimindo dados. + + + + Printing query. + Imprimindo a consulta. + + + + PrintingExport + + + Printing + Imprimindo + + + diff --git a/Plugins/Printing/translations/Printing_pt_PT.ts b/Plugins/Printing/translations/Printing_pt_PT.ts new file mode 100644 index 0000000..a87b515 --- /dev/null +++ b/Plugins/Printing/translations/Printing_pt_PT.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_ro_RO.ts b/Plugins/Printing/translations/Printing_ro_RO.ts new file mode 100644 index 0000000..bb84411 --- /dev/null +++ b/Plugins/Printing/translations/Printing_ro_RO.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_ru_RU.ts b/Plugins/Printing/translations/Printing_ru_RU.ts new file mode 100644 index 0000000..9cf0f24 --- /dev/null +++ b/Plugins/Printing/translations/Printing_ru_RU.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Печать данных + + + + Print query + Печать запроÑа + + + + No data to print. + Ðет данных Ð´Ð»Ñ Ð¿ÐµÑ‡Ð°Ñ‚Ð¸. + + + + Printing data. + Печать данных. + + + + Printing query. + Печать запроÑа. + + + + PrintingExport + + + Printing + Печать + + + diff --git a/Plugins/Printing/translations/Printing_sk_SK.ts b/Plugins/Printing/translations/Printing_sk_SK.ts new file mode 100644 index 0000000..c4dce3a --- /dev/null +++ b/Plugins/Printing/translations/Printing_sk_SK.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_sr_SP.ts b/Plugins/Printing/translations/Printing_sr_SP.ts new file mode 100644 index 0000000..6aaf2da --- /dev/null +++ b/Plugins/Printing/translations/Printing_sr_SP.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_sv_SE.ts b/Plugins/Printing/translations/Printing_sv_SE.ts new file mode 100644 index 0000000..bfc22a6 --- /dev/null +++ b/Plugins/Printing/translations/Printing_sv_SE.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_tr_TR.ts b/Plugins/Printing/translations/Printing_tr_TR.ts new file mode 100644 index 0000000..d8ede38 --- /dev/null +++ b/Plugins/Printing/translations/Printing_tr_TR.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Veriyi yazdır + + + + Print query + Sorguyu yazdır + + + + No data to print. + Yazdırılacak veri yok. + + + + Printing data. + Veri yazdırılıyor. + + + + Printing query. + Sorgu yazdırılıyor. + + + + PrintingExport + + + Printing + Yazdırılıyor + + + diff --git a/Plugins/Printing/translations/Printing_uk_UA.ts b/Plugins/Printing/translations/Printing_uk_UA.ts new file mode 100644 index 0000000..a925671 --- /dev/null +++ b/Plugins/Printing/translations/Printing_uk_UA.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_vi_VN.ts b/Plugins/Printing/translations/Printing_vi_VN.ts new file mode 100644 index 0000000..a714519 --- /dev/null +++ b/Plugins/Printing/translations/Printing_vi_VN.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + Print data + + + + Print query + Print query + + + + No data to print. + No data to print. + + + + Printing data. + Printing data. + + + + Printing query. + Printing query. + + + + PrintingExport + + + Printing + Printing + + + diff --git a/Plugins/Printing/translations/Printing_zh_CN.ts b/Plugins/Printing/translations/Printing_zh_CN.ts new file mode 100644 index 0000000..94407c3 --- /dev/null +++ b/Plugins/Printing/translations/Printing_zh_CN.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + æ‰“å°æ•°æ® + + + + Print query + æ‰“å°æŸ¥è¯¢ + + + + No data to print. + æ²¡æœ‰å¯æ‰“å°çš„æ•°æ®ã€‚ + + + + Printing data. + æ­£åœ¨æ‰“å°æ•°æ®ã€‚ + + + + Printing query. + æ­£åœ¨æ‰“å°æŸ¥è¯¢ã€‚ + + + + PrintingExport + + + Printing + æ‰“å° + + + diff --git a/Plugins/Printing/translations/Printing_zh_TW.ts b/Plugins/Printing/translations/Printing_zh_TW.ts new file mode 100644 index 0000000..fd18b33 --- /dev/null +++ b/Plugins/Printing/translations/Printing_zh_TW.ts @@ -0,0 +1,40 @@ + + + + + Printing + + + Print data + 列å°è³‡æ–™ + + + + Print query + åˆ—å°æŸ¥è©¢ + + + + No data to print. + 沒有å¯åˆ—å°çš„資料。 + + + + Printing data. + 正在列å°è³‡æ–™ã€‚ + + + + Printing query. + æ­£åœ¨åˆ—å°æŸ¥è©¢ã€‚ + + + + PrintingExport + + + Printing + åˆ—å° + + + diff --git a/Plugins/PythonSyntaxHighlighter/PythonSyntaxHighlighter.pro b/Plugins/PythonSyntaxHighlighter/PythonSyntaxHighlighter.pro new file mode 100644 index 0000000..981b26e --- /dev/null +++ b/Plugins/PythonSyntaxHighlighter/PythonSyntaxHighlighter.pro @@ -0,0 +1,20 @@ +include($$PWD/../../SQLiteStudio3/plugins.pri) + +QT += widgets + +TARGET = PythonSyntaxHighlighter +TEMPLATE = lib + +DEFINES += PYTHONSYNTAXHIGHLIGHTER_LIBRARY + +SOURCES += pythonsyntaxhighlighter.cpp + +HEADERS += pythonsyntaxhighlighter.h\ + pythonsyntaxhighlighter_global.h + +OTHER_FILES += \ + pythonsyntaxhighlighter.json + +win32: { + LIBS += -lcoreSQLiteStudio -lguiSQLiteStudio +} diff --git a/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.cpp b/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.cpp new file mode 100644 index 0000000..eb9012a --- /dev/null +++ b/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.cpp @@ -0,0 +1,298 @@ +#include "pythonsyntaxhighlighter.h" +#include "uiconfig.h" +#include "services/config.h" +#include +#include +#include + +// Started from Qt Syntax Highlighter example and then ported https://wiki.python.org/moin/PyQt/Python%20syntax%20highlighting +// Ported code copied from https://forum.qt.io/topic/96285/c-highlighter-for-python +// and then adjusted for SQLiteStudio (i.e. migrated from QRegExp to QRegularExpression). +class PythonHighlighter : public QSyntaxHighlighter +{ + public: + typedef QMap FormatMap; + + PythonHighlighter(QTextDocument *parent, const QMap* styles); + + protected: + void highlightBlock(const QString &text) override; + + private: + struct HighlightingRule + { + QRegularExpression pattern; + PythonSyntaxHighlighterPlugin::State state; + int matchIndex = 0; + + HighlightingRule() { } + HighlightingRule(const QRegularExpression &r, int i, PythonSyntaxHighlighterPlugin::State state) : pattern(r), state(state), matchIndex(i) { } + HighlightingRule(const QString &p, int i, PythonSyntaxHighlighterPlugin::State state) : pattern(QRegularExpression(p)), state(state), matchIndex(i) { } + }; + + const QMap* styles; + static const QStringList keywords; + static const QStringList operators; + static const QStringList braces; + + void initialize(); + void highlightPythonBlock(const QString &text); + bool matchMultiLine(const QString &text, const HighlightingRule &rule); + + QVector _pythonHighlightingRules; + HighlightingRule _triSingle, _triDouble; +}; + +// Python keywords +const QStringList PythonHighlighter::keywords = { + "and", "assert", "break", "class", "continue", "def", + "del", "elif", "else", "except", "exec", "finally", + "for", "from", "global", "if", "import", "in", + "is", "lambda", "not", "or", "pass", "print", + "raise", "return", "try", "while", "yield", + "None", "True", "False" +}; + +// Python operators +const QStringList PythonHighlighter::operators = { + "\\=", + // Comparison + "\\=\\=", "\\!\\=", "\\<", "\\<\\=", "\\>", "\\>\\=", + // Arithmetic + "\\+", "\\-", "\\*", "\\/", "\\/\\/", "\\%", "\\*\\*", + // In-place + "\\+\\=", "\\-\\=", "\\*\\=", "\\/\\=", "\\%\\=", + // Bitwise + "\\^", "\\|", "\\&", "\\~", "\\>\\>", "\\<\\<" +}; + +// Python braces +const QStringList PythonHighlighter::braces = { + "\\{", "\\}", "\\(", "\\)", "\\[", "\\]" +}; + +PythonHighlighter::PythonHighlighter(QTextDocument *parent, const QMap* styles) + : QSyntaxHighlighter(parent), styles(styles) +{ + initialize(); +} + +void PythonHighlighter::highlightBlock(const QString &text) +{ + highlightPythonBlock(text); +} + +void PythonHighlighter::initialize() +{ + // Multi-line strings (expression, flag, style) + // FIXME: The triple-quotes in these two lines will mess up the + // syntax highlighting from this point onward + _triSingle = HighlightingRule("'''", 1, PythonSyntaxHighlighterPlugin::STRING); + _triDouble = HighlightingRule("\"\"\"", 2, PythonSyntaxHighlighterPlugin::STRING); + + // Keyword, operator, and brace rules + for (const QString &keyword : keywords) + { + QString pattern = QString("\\b%1\\b").arg(keyword); + _pythonHighlightingRules += HighlightingRule(pattern, 0, PythonSyntaxHighlighterPlugin::KEYWORD); + } + + for (const QString &pattern: operators) + _pythonHighlightingRules += HighlightingRule(pattern, 0, PythonSyntaxHighlighterPlugin::OPERATOR); + + for (const QString &pattern: braces) + _pythonHighlightingRules += HighlightingRule(pattern, 0, PythonSyntaxHighlighterPlugin::BRACE); + + // All other rules + + // 'self' + _pythonHighlightingRules += HighlightingRule("\\bself\\b", 0, PythonSyntaxHighlighterPlugin::SELF); + + // Double-quoted string, possibly containing escape sequences + _pythonHighlightingRules += HighlightingRule("\"([^\"\\\\]|\\\\.)*\"", 0, PythonSyntaxHighlighterPlugin::STRING); + // Single-quoted string, possibly containing escape sequences + _pythonHighlightingRules += HighlightingRule("'([^'\\\\]|\\\\.)*'", 0, PythonSyntaxHighlighterPlugin::STRING); + + // 'def' followed by an identifier + _pythonHighlightingRules += HighlightingRule("\\bdef\\b\\s*(\\w+)", 1, PythonSyntaxHighlighterPlugin::DEFCLASS); + // 'class' followed by an identifier + _pythonHighlightingRules += HighlightingRule("\\bclass\\b\\s*(\\w+)", 1, PythonSyntaxHighlighterPlugin::DEFCLASS); + + // From '#' until a newline + _pythonHighlightingRules += HighlightingRule("#[^\\n]*", 0, PythonSyntaxHighlighterPlugin::COMMENT); + + // Numeric literals + _pythonHighlightingRules += HighlightingRule("\\b[+-]?[0-9]+[lL]?\\b", 0, PythonSyntaxHighlighterPlugin::NUMBER); + _pythonHighlightingRules += HighlightingRule("\\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\\b", 0, PythonSyntaxHighlighterPlugin::NUMBER); + _pythonHighlightingRules += HighlightingRule("\\b[+-]?[0-9]+(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b", 0, PythonSyntaxHighlighterPlugin::NUMBER); +} + +void PythonHighlighter::highlightPythonBlock(const QString &text) +{ + if (text.isEmpty()) + return; + + int index = -1; + setFormat(0, text.length(), styles->value(PythonSyntaxHighlighterPlugin::STANDARD)); + + // Do other syntax formatting + for (HighlightingRule& rule : _pythonHighlightingRules) + { + QRegularExpressionMatchIterator iter = rule.pattern.globalMatch(text, 0); + while (iter.hasNext()) + { + QRegularExpressionMatch match = iter.next(); + index = match.capturedStart(rule.matchIndex); + int length = match.capturedLength(rule.matchIndex); + if (length > 0) + setFormat(index, length, styles->value(rule.state)); + } + } + + setCurrentBlockState(0); + + // Do multi-line strings + bool in_multiline = matchMultiLine(text, _triSingle); + if (!in_multiline) + matchMultiLine(text, _triDouble); +} + +/*Do highlighting of multi-line strings. ``delimiter`` should be a +``QRegExp`` for triple-single-quotes or triple-double-quotes, and +``in_state`` should be a unique integer to represent the corresponding +state changes when inside those strings. Returns True if we're still +inside a multi-line string when this function is finished. +*/ +bool PythonHighlighter::matchMultiLine(const QString &text, const HighlightingRule &rule) +{ + int start, add, end, length; + + // If inside triple-single quotes, start at 0 + if (previousBlockState() == rule.matchIndex) + { + start = 0; + add = 0; + } + // Otherwise, look for the delimiter on this line + else + { + QRegularExpressionMatch match = rule.pattern.match(text); + start = match.capturedStart(); + // Move past this match + add = match.capturedLength(); + } + + // As long as there's a delimiter match on this line... + while (start >= 0) + { + QRegularExpressionMatch match = rule.pattern.match(text, start + add); + // Look for the ending delimiter + end = match.capturedStart(); + // Ending delimiter on this line? + if(end >= add) + { + length = end - start + add + match.capturedLength(); + setCurrentBlockState(0); + } + // No; multi-line string + else + { + setCurrentBlockState(rule.matchIndex); + length = text.length() - start + add; + } + + // Apply formatting + setFormat(start, length, styles->value(rule.state)); + + // Look for the next match + match = rule.pattern.match(text, start + length); + start = match.capturedStart(); + } + + // Return True if still inside a multi-line string, False otherwise + if (currentBlockState() == rule.matchIndex) + return true; + else + return false; +} + + +PythonSyntaxHighlighterPlugin::PythonSyntaxHighlighterPlugin() +{ +} + +QString PythonSyntaxHighlighterPlugin::getLanguageName() const +{ + return QStringLiteral("Python"); +} + +QSyntaxHighlighter* PythonSyntaxHighlighterPlugin::createSyntaxHighlighter(QWidget* textEdit) const +{ + QPlainTextEdit* plainEdit = dynamic_cast(textEdit); + if (plainEdit) + return new PythonHighlighter(plainEdit->document(), &styles); + + QTextEdit* edit = dynamic_cast(textEdit); + if (edit) + return new PythonHighlighter(edit->document(), &styles); + + return nullptr; +} + +void PythonSyntaxHighlighterPlugin::refreshFormats() +{ + QTextCharFormat format; + + // Standard + format.setForeground(Cfg::getSyntaxForeground()); + format.setFontWeight(QFont::Normal); + format.setFontItalic(false); + styles[PythonSyntaxHighlighterPlugin::STANDARD] = format; + + // Class + format.setForeground(Cfg::getSyntaxKeywordFg()); + format.setFontWeight(QFont::Bold); + styles[PythonSyntaxHighlighterPlugin::DEFCLASS] = format; + styles[PythonSyntaxHighlighterPlugin::KEYWORD] = format; + + // Self + format.setForeground(Cfg::getSyntaxKeywordFg()); + format.setFontWeight(QFont::Normal); + format.setFontItalic(true); + styles[PythonSyntaxHighlighterPlugin::SELF] = format; + + // Operator + format.setForeground(Cfg::getSyntaxForeground()); + format.setFontItalic(false); + styles[PythonSyntaxHighlighterPlugin::OPERATOR] = format; + + // Parenthesis + format.setForeground(Cfg::getSyntaxForeground()); + styles[PythonSyntaxHighlighterPlugin::BRACE] = format; + + // String + format.setForeground(Cfg::getSyntaxStringFg()); + styles[PythonSyntaxHighlighterPlugin::STRING] = format; + + // Numbers + format.setForeground(Cfg::getSyntaxNumberFg()); + styles[PythonSyntaxHighlighterPlugin::NUMBER] = format; + + // Comment + format.setForeground(Cfg::getSyntaxCommentFg()); + format.setFontItalic(true); + styles[PythonSyntaxHighlighterPlugin::COMMENT] = format; +} + +QString PythonSyntaxHighlighterPlugin::previewSampleCode() const +{ + static_qstring(code, + "class MyClass:\n" + " \"\"\"A simple example class\"\"\"\n" + " i = 12345\n" + "\n" + " def f(self):\n" + " return 'hello world'""" + ); + return code; +} diff --git a/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.h b/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.h new file mode 100644 index 0000000..b9ecc1d --- /dev/null +++ b/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.h @@ -0,0 +1,42 @@ +#ifndef PYTHONSYNTAXHIGHLIGHTER_H +#define PYTHONSYNTAXHIGHLIGHTER_H + +#include "pythonsyntaxhighlighter_global.h" +#include "syntaxhighlighterplugin.h" +#include "plugins/genericplugin.h" +#include +#include +#include +#include + +class PYTHONSYNTAXHIGHLIGHTERSHARED_EXPORT PythonSyntaxHighlighterPlugin : public GenericPlugin, public SyntaxHighlighterPlugin +{ + Q_OBJECT + SQLITESTUDIO_PLUGIN("pythonsyntaxhighlighter.json") + + public: + enum State + { + STANDARD, + KEYWORD, + DEFCLASS, + SELF, + OPERATOR, + BRACE, + STRING, + NUMBER, + COMMENT + }; + + PythonSyntaxHighlighterPlugin(); + + QString getLanguageName() const; + QSyntaxHighlighter* createSyntaxHighlighter(QWidget* textEdit) const; + QString previewSampleCode() const; + void refreshFormats(); + + private: + QMap styles; +}; + +#endif // PYTHONSYNTAXHIGHLIGHTER_H diff --git a/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.json b/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.json new file mode 100644 index 0000000..64fd958 --- /dev/null +++ b/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter.json @@ -0,0 +1,7 @@ +{ + "type": "SyntaxHighlighterPlugin", + "title": "Python syntax highlighting", + "description": "Provides Python syntax highlighting support.", + "version": 10100, + "author": "SalSoft" +} diff --git a/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter_global.h b/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter_global.h new file mode 100644 index 0000000..531564a --- /dev/null +++ b/Plugins/PythonSyntaxHighlighter/pythonsyntaxhighlighter_global.h @@ -0,0 +1,12 @@ +#ifndef PYTHONSYNTAXHIGHLIGHTER_GLOBAL_H +#define PYTHONSYNTAXHIGHLIGHTER_GLOBAL_H + +#include + +#if defined(PYTHONSYNTAXHIGHLIGHTER_LIBRARY) +# define PYTHONSYNTAXHIGHLIGHTERSHARED_EXPORT Q_DECL_EXPORT +#else +# define PYTHONSYNTAXHIGHLIGHTERSHARED_EXPORT Q_DECL_IMPORT +#endif + +#endif // PYTHONSYNTAXHIGHLIGHTER_GLOBAL_H diff --git a/Plugins/RegExpImport/RegExpImport.pro b/Plugins/RegExpImport/RegExpImport.pro index c3d1bf8..a27290e 100644 --- a/Plugins/RegExpImport/RegExpImport.pro +++ b/Plugins/RegExpImport/RegExpImport.pro @@ -26,31 +26,3 @@ FORMS += \ RESOURCES += \ regexpimport.qrc - - -TRANSLATIONS += RegExpImport_ro_RO.ts \ - RegExpImport_de.ts \ - RegExpImport_it.ts \ - RegExpImport_zh_CN.ts \ - RegExpImport_sk.ts \ - RegExpImport_ru.ts \ - RegExpImport_pt_BR.ts \ - RegExpImport_fr.ts \ - RegExpImport_es.ts \ - RegExpImport_pl.ts - - - - - - - - - - - - - - - - diff --git a/Plugins/RegExpImport/RegExpImport_de.qm b/Plugins/RegExpImport/RegExpImport_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/RegExpImport/RegExpImport_de.qm and /dev/null differ diff --git a/Plugins/RegExpImport/RegExpImport_de.ts b/Plugins/RegExpImport/RegExpImport_de.ts deleted file mode 100644 index 2a30835..0000000 --- a/Plugins/RegExpImport/RegExpImport_de.ts +++ /dev/null @@ -1,83 +0,0 @@ - - - - - RegExpImport - - - Text files (*.txt);;All files (*) - - - - - Cannot read file %1 - - - - - Enter the regular expression pattern. - - - - - Invalid pattern: %1 - - - - - Requested capture index %1 is out of range. - - - - - <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> - - - - - RegExpImportConfig - - - Capture groups - - - - - Treat all RegExp capture groups as columns - - - - - Import only following groups: - - - - - <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> -<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> - - - - - Example: 1, 3, 4 - - - - - Pattern: - - - - - <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. - -You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> - - - - - Example: (\d+)\s+((\d+)\w+)\s+(\w+) - - - - diff --git a/Plugins/RegExpImport/RegExpImport_es.qm b/Plugins/RegExpImport/RegExpImport_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/RegExpImport/RegExpImport_es.qm and /dev/null differ diff --git a/Plugins/RegExpImport/RegExpImport_es.ts b/Plugins/RegExpImport/RegExpImport_es.ts deleted file mode 100644 index c58d204..0000000 --- a/Plugins/RegExpImport/RegExpImport_es.ts +++ /dev/null @@ -1,83 +0,0 @@ - - - - - RegExpImport - - - Text files (*.txt);;All files (*) - - - - - Cannot read file %1 - - - - - Enter the regular expression pattern. - - - - - Invalid pattern: %1 - - - - - Requested capture index %1 is out of range. - - - - - <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> - - - - - RegExpImportConfig - - - Capture groups - - - - - Treat all RegExp capture groups as columns - - - - - Import only following groups: - - - - - <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> -<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> - - - - - Example: 1, 3, 4 - - - - - Pattern: - - - - - <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. - -You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> - - - - - Example: (\d+)\s+((\d+)\w+)\s+(\w+) - - - - diff --git a/Plugins/RegExpImport/RegExpImport_fr.qm b/Plugins/RegExpImport/RegExpImport_fr.qm deleted file mode 100644 index 2843e37..0000000 Binary files a/Plugins/RegExpImport/RegExpImport_fr.qm and /dev/null differ diff --git a/Plugins/RegExpImport/RegExpImport_fr.ts b/Plugins/RegExpImport/RegExpImport_fr.ts deleted file mode 100644 index b7f0aef..0000000 --- a/Plugins/RegExpImport/RegExpImport_fr.ts +++ /dev/null @@ -1,83 +0,0 @@ - - - - - RegExpImport - - - Text files (*.txt);;All files (*) - Fichiers (*.txt);;Tous les fichiers (*) - - - - Cannot read file %1 - Impossible de lire le fichier : %1 - - - - Enter the regular expression pattern. - Saisissez une expression valide. - - - - Invalid pattern: %1 - Expression invalide : %1 - - - - Requested capture index %1 is out of range. - Allocation d’index %1 hors limites. - - - - <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> - - - - - RegExpImportConfig - - - Capture groups - Groupes sélectionnés - - - - Treat all RegExp capture groups as columns - Traiter tout RegExp de groupes sélectionnés comme des colonnes - - - - Import only following groups: - Importer seulement les groupes suivant : - - - - <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> -<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> - - - - - Example: 1, 3, 4 - Exemple : 1, 3, 4 - - - - Pattern: - Modèle : - - - - <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. - -You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> - - - - - Example: (\d+)\s+((\d+)\w+)\s+(\w+) - Exemple : (\d+)\s+((\d+)\w+)\s+(\w+) - - - diff --git a/Plugins/RegExpImport/RegExpImport_it.qm b/Plugins/RegExpImport/RegExpImport_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/RegExpImport/RegExpImport_it.qm and /dev/null differ diff --git a/Plugins/RegExpImport/RegExpImport_it.ts b/Plugins/RegExpImport/RegExpImport_it.ts deleted file mode 100644 index 00869f1..0000000 --- a/Plugins/RegExpImport/RegExpImport_it.ts +++ /dev/null @@ -1,83 +0,0 @@ - - - - - RegExpImport - - - Text files (*.txt);;All files (*) - - - - - Cannot read file %1 - - - - - Enter the regular expression pattern. - - - - - Invalid pattern: %1 - - - - - Requested capture index %1 is out of range. - - - - - <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> - - - - - RegExpImportConfig - - - Capture groups - - - - - Treat all RegExp capture groups as columns - - - - - Import only following groups: - - - - - <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> -<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> - - - - - Example: 1, 3, 4 - - - - - Pattern: - - - - - <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. - -You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> - - - - - Example: (\d+)\s+((\d+)\w+)\s+(\w+) - - - - diff --git a/Plugins/RegExpImport/RegExpImport_pl.qm b/Plugins/RegExpImport/RegExpImport_pl.qm deleted file mode 100644 index b71fbf8..0000000 Binary files a/Plugins/RegExpImport/RegExpImport_pl.qm and /dev/null differ diff --git a/Plugins/RegExpImport/RegExpImport_pl.ts b/Plugins/RegExpImport/RegExpImport_pl.ts deleted file mode 100644 index d55ed25..0000000 --- a/Plugins/RegExpImport/RegExpImport_pl.ts +++ /dev/null @@ -1,86 +0,0 @@ - - - - - RegExpImport - - - Text files (*.txt);;All files (*) - Pliki tekstowe (*.txt);;Wszystkie pliki (*) - - - - Cannot read file %1 - Nie można odczytać pliku %1 - - - - Enter the regular expression pattern. - Wprowadź wzorzec wyrażenia regularnego. - - - - Invalid pattern: %1 - Niepoprawny wzorzec: %1 - - - - Requested capture index %1 is out of range. - Żądany indeks przechwytywania %1 jest poza zakresem. - - - - <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> - <p>Zażądano grupy do przechwycenia o nazwie '%1', ale nie jest ona zdefiniowana we wzorcu: <pre>%2</pre></p> - - - - RegExpImportConfig - - - Capture groups - Grupy przechwytujÄ…ce - - - - Treat all RegExp capture groups as columns - Traktuj wszystkie grupy przechwytujÄ…ce jako kolumny - - - - Import only following groups: - Importuj tylko nastÄ™pujÄ…ce grupy: - - - - <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> -<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> - <p>Wprowadź listÄ™ indeksów grup oddzielonÄ… przecinkami. Indeks 0 odpowiada caÅ‚emu dopasowanemu Å‚aÅ„cuchowi.</p> -<p>JeÅ›li użyÅ‚eÅ› nazwanych grup we wzorcu, to możesz używać nazw, zamiast indeksów. Możesz mieszać inseksy i nazwy na liÅ›cie.</p> - - - - Example: 1, 3, 4 - PrzykÅ‚ad: 1, 3, 4 - - - - Pattern: - Wzorzec: - - - - <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. - -You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> - <p>Użyj grup przechwytujÄ…cych WyrażeÅ„ Regularnych, aby otoczyć części wyrażenia, które chcesz zaimportować. JeÅ›li chcesz użyć grupy, której nie chcesz zaimportować, to użyj opcji "importuj tylko nastÄ™pujÄ…ce grupy" poniżej. - -Możesz użyć grup nazwanych i odwoÅ‚ywać siÄ™ do nich w liÅ›cie grup poniżej. Aby nazwać grupÄ™, użyj: <pre>(?&lt;nazwaGrupy&gt;\s+\d+\s+)</pre></p> - - - - Example: (\d+)\s+((\d+)\w+)\s+(\w+) - PrzykÅ‚ad: (\d+)\s+((\d+)\w+)\s+(\w+) - - - diff --git a/Plugins/RegExpImport/RegExpImport_pt_BR.qm b/Plugins/RegExpImport/RegExpImport_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/RegExpImport/RegExpImport_pt_BR.qm and /dev/null differ diff --git a/Plugins/RegExpImport/RegExpImport_pt_BR.ts b/Plugins/RegExpImport/RegExpImport_pt_BR.ts deleted file mode 100644 index 0af41e7..0000000 --- a/Plugins/RegExpImport/RegExpImport_pt_BR.ts +++ /dev/null @@ -1,83 +0,0 @@ - - - - - RegExpImport - - - Text files (*.txt);;All files (*) - - - - - Cannot read file %1 - - - - - Enter the regular expression pattern. - - - - - Invalid pattern: %1 - - - - - Requested capture index %1 is out of range. - - - - - <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> - - - - - RegExpImportConfig - - - Capture groups - - - - - Treat all RegExp capture groups as columns - - - - - Import only following groups: - - - - - <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> -<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> - - - - - Example: 1, 3, 4 - - - - - Pattern: - - - - - <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. - -You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> - - - - - Example: (\d+)\s+((\d+)\w+)\s+(\w+) - - - - diff --git a/Plugins/RegExpImport/RegExpImport_ro_RO.qm b/Plugins/RegExpImport/RegExpImport_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/RegExpImport/RegExpImport_ro_RO.qm and /dev/null differ diff --git a/Plugins/RegExpImport/RegExpImport_ro_RO.ts b/Plugins/RegExpImport/RegExpImport_ro_RO.ts deleted file mode 100644 index bc6288f..0000000 --- a/Plugins/RegExpImport/RegExpImport_ro_RO.ts +++ /dev/null @@ -1,83 +0,0 @@ - - - - - RegExpImport - - - Text files (*.txt);;All files (*) - - - - - Cannot read file %1 - - - - - Enter the regular expression pattern. - - - - - Invalid pattern: %1 - - - - - Requested capture index %1 is out of range. - - - - - <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> - - - - - RegExpImportConfig - - - Capture groups - - - - - Treat all RegExp capture groups as columns - - - - - Import only following groups: - - - - - <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> -<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> - - - - - Example: 1, 3, 4 - - - - - Pattern: - - - - - <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. - -You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> - - - - - Example: (\d+)\s+((\d+)\w+)\s+(\w+) - - - - diff --git a/Plugins/RegExpImport/RegExpImport_ru.qm b/Plugins/RegExpImport/RegExpImport_ru.qm deleted file mode 100644 index f44a89e..0000000 Binary files a/Plugins/RegExpImport/RegExpImport_ru.qm and /dev/null differ diff --git a/Plugins/RegExpImport/RegExpImport_ru.ts b/Plugins/RegExpImport/RegExpImport_ru.ts deleted file mode 100644 index d8d4ca8..0000000 --- a/Plugins/RegExpImport/RegExpImport_ru.ts +++ /dev/null @@ -1,86 +0,0 @@ - - - - - RegExpImport - - - Text files (*.txt);;All files (*) - ТекÑтовые файлы (*.txt);;Ð’Ñе файлы (*) - - - - Cannot read file %1 - Ðевозможно прочитать файл %1 - - - - Enter the regular expression pattern. - Введите шаблон регулÑрного выражениÑ. - - - - Invalid pattern: %1 - Ðекорректный шаблон: %1 - - - - Requested capture index %1 is out of range. - Запрошенный Ð¸Ð½Ð´ÐµÐºÑ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ %1 вне доÑÑгаемоÑти. - - - - <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> - <p>Запрошено Ð¸Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ '%1', но оно не определено в шаблоне: <pre>%2</pre></p> - - - - RegExpImportConfig - - - Capture groups - Группы в шаблоне - - - - Treat all RegExp capture groups as columns - РаÑÑматривать вÑе группы в выражении как Ñтолбцы - - - - Import only following groups: - Импортировать только Ñледующие группы: - - - - <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> -<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> - <p>Введите разделённый запÑтыми ÑпиÑок индекÑов групп. Ð˜Ð½Ð´ÐµÐºÑ 0 Ñлужит Ð´Ð»Ñ Ð´Ð¾Ñтупа ко вÑей найденной Ñтроке.</p> -<p>При иÑпользовании именованных групп в шаблоне, вы можете указывать имена групп вмеÑто индекÑов. Имена групп и индекÑÑ‹ можно иÑпользовать вмеÑте.</p> - - - - Example: 1, 3, 4 - Пример: 1, 3, 4 - - - - Pattern: - Шаблон: - - - - <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. - -You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> - <p>ИÑпользуйте группировку в регулÑрном выражении Ð´Ð»Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ñ‡Ð°Ñтей выражениÑ, которые необходимо импортировать. ЕÑли необходимо иÑпользовать группу, иÑключаемую при импорте, иÑпользуйте опцию "Импортировать только Ñледующие группы" ниже. - -Можно также иÑпользовать именованные группы Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² ÑпиÑке ниже. Ð”Ð»Ñ Ð¿Ñ€Ð¸ÑÐ²Ð¾ÐµÐ½Ð¸Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ðµ имени иÑпользуйте: <pre>(?&lt;моёИмÑГруппы&gt;\s+\d+\s+)</pre></p> - - - - Example: (\d+)\s+((\d+)\w+)\s+(\w+) - Пример: (\d+)\s+((\d+)\w+)\s+(\w+) - - - diff --git a/Plugins/RegExpImport/RegExpImport_sk.qm b/Plugins/RegExpImport/RegExpImport_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/Plugins/RegExpImport/RegExpImport_sk.qm and /dev/null differ diff --git a/Plugins/RegExpImport/RegExpImport_sk.ts b/Plugins/RegExpImport/RegExpImport_sk.ts deleted file mode 100644 index 25068a5..0000000 --- a/Plugins/RegExpImport/RegExpImport_sk.ts +++ /dev/null @@ -1,83 +0,0 @@ - - - - - RegExpImport - - - Text files (*.txt);;All files (*) - - - - - Cannot read file %1 - - - - - Enter the regular expression pattern. - - - - - Invalid pattern: %1 - - - - - Requested capture index %1 is out of range. - - - - - <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> - - - - - RegExpImportConfig - - - Capture groups - - - - - Treat all RegExp capture groups as columns - - - - - Import only following groups: - - - - - <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> -<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> - - - - - Example: 1, 3, 4 - - - - - Pattern: - - - - - <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. - -You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> - - - - - Example: (\d+)\s+((\d+)\w+)\s+(\w+) - - - - diff --git a/Plugins/RegExpImport/RegExpImport_zh_CN.qm b/Plugins/RegExpImport/RegExpImport_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/Plugins/RegExpImport/RegExpImport_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/Plugins/RegExpImport/RegExpImport_zh_CN.ts b/Plugins/RegExpImport/RegExpImport_zh_CN.ts deleted file mode 100644 index c361148..0000000 --- a/Plugins/RegExpImport/RegExpImport_zh_CN.ts +++ /dev/null @@ -1,83 +0,0 @@ - - - - - RegExpImport - - - Text files (*.txt);;All files (*) - - - - - Cannot read file %1 - - - - - Enter the regular expression pattern. - - - - - Invalid pattern: %1 - - - - - Requested capture index %1 is out of range. - - - - - <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> - - - - - RegExpImportConfig - - - Capture groups - - - - - Treat all RegExp capture groups as columns - - - - - Import only following groups: - - - - - <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> -<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> - - - - - Example: 1, 3, 4 - - - - - Pattern: - - - - - <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. - -You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> - - - - - Example: (\d+)\s+((\d+)\w+)\s+(\w+) - - - - diff --git a/Plugins/RegExpImport/regexpimport.cpp b/Plugins/RegExpImport/regexpimport.cpp index cd761b1..5a5257b 100644 --- a/Plugins/RegExpImport/regexpimport.cpp +++ b/Plugins/RegExpImport/regexpimport.cpp @@ -13,13 +13,13 @@ RegExpImport::RegExpImport() bool RegExpImport::init() { - Q_INIT_RESOURCE(regexpimport); + SQLS_INIT_RESOURCE(regexpimport); return GenericPlugin::init(); } void RegExpImport::deinit() { - Q_CLEANUP_RESOURCE(regexpimport); + SQLS_CLEANUP_RESOURCE(regexpimport); } QString RegExpImport::getDataSourceTypeName() const diff --git a/Plugins/RegExpImport/regexpimport.qrc b/Plugins/RegExpImport/regexpimport.qrc index a505f8f..9ece86e 100644 --- a/Plugins/RegExpImport/regexpimport.qrc +++ b/Plugins/RegExpImport/regexpimport.qrc @@ -2,19 +2,4 @@ regexpimport.ui - - RegExpImport_ro_RO.qm - RegExpImport_de.qm - - - RegExpImport_pl.qm - RegExpImport_ru.qm - RegExpImport_fr.qm - RegExpImport_sk.qm - RegExpImport_zh_CN.qm - - - - - diff --git a/Plugins/RegExpImport/translations/RegExpImport.ts b/Plugins/RegExpImport/translations/RegExpImport.ts new file mode 100644 index 0000000..6b8acc3 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport.ts @@ -0,0 +1,83 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + + + + + Cannot read file %1 + + + + + Enter the regular expression pattern. + + + + + Invalid pattern: %1 + + + + + Requested capture index %1 is out of range. + + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + + RegExpImportConfig + + + Capture groups + + + + + Treat all RegExp capture groups as columns + + + + + Import only following groups: + + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + + Example: 1, 3, 4 + + + + + Pattern: + + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_af_ZA.ts b/Plugins/RegExpImport/translations/RegExpImport_af_ZA.ts new file mode 100644 index 0000000..d5f6d47 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_af_ZA.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_ar_SA.ts b/Plugins/RegExpImport/translations/RegExpImport_ar_SA.ts new file mode 100644 index 0000000..573f4b3 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_ar_SA.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_ca_ES.ts b/Plugins/RegExpImport/translations/RegExpImport_ca_ES.ts new file mode 100644 index 0000000..f571835 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_ca_ES.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_cs_CZ.ts b/Plugins/RegExpImport/translations/RegExpImport_cs_CZ.ts new file mode 100644 index 0000000..ea8afa6 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_cs_CZ.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_da_DK.ts b/Plugins/RegExpImport/translations/RegExpImport_da_DK.ts new file mode 100644 index 0000000..8517537 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_da_DK.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_de_DE.ts b/Plugins/RegExpImport/translations/RegExpImport_de_DE.ts new file mode 100644 index 0000000..6fd0960 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_de_DE.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Textdateien (*.txt);;Alle Dateien (*.*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Ungültiges Muster: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Beispiel: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_el_GR.ts b/Plugins/RegExpImport/translations/RegExpImport_el_GR.ts new file mode 100644 index 0000000..4bd12dc --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_el_GR.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_en_US.ts b/Plugins/RegExpImport/translations/RegExpImport_en_US.ts new file mode 100644 index 0000000..f233e24 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_en_US.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_es_ES.ts b/Plugins/RegExpImport/translations/RegExpImport_es_ES.ts new file mode 100644 index 0000000..8aa0b16 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_es_ES.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Archivos de texto (*.txt);;Todos los archivos (*) + + + + Cannot read file %1 + No se puede leer el archivo %1 + + + + Enter the regular expression pattern. + Ingresa el patrón para la expresión regular. + + + + Invalid pattern: %1 + Patrón inválido: %1 + + + + Requested capture index %1 is out of range. + El índice de captura solicitado %1 está fuera del rango. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Se solicitó el nombre '%1' del grupo de captura, pero no está definido en el patrón: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capturar grupos + + + + Treat all RegExp capture groups as columns + Tratar todos los grupos de captura de RegExp como columnas + + + + Import only following groups: + Importar solamente los siguientes grupos: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Ingresa una lista separada por comas para los índices de los grupos de captura. El índice 0 hace referencia a toda la cadena que coincida.</p> +<p>Si usaste nombres de grupos en el patrón, puedes usar nombres en vez de índices. Puedes combinar índices y nombres en esta lista.</p> + + + + Example: 1, 3, 4 + Ejemplo: 1, 3, 4 + + + + Pattern: + Patrón: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Usa los grupos de las Expresiones Regulares para encerrar partes de la expresión que quieres importar. Si quieres usar un grupo, sin tener que importarlo, entonces usa la opción "importar solamente los siguientes grupos" presentada abajo. + +Puedes usar nombres de grupo y referirte a ellos en la lista de abajo. Para nombrar un grupo usa: <pre>(?&lt;miNombreDeGrupo&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Ejemplo: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_fa_IR.ts b/Plugins/RegExpImport/translations/RegExpImport_fa_IR.ts new file mode 100644 index 0000000..fdc8346 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_fa_IR.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_fi_FI.ts b/Plugins/RegExpImport/translations/RegExpImport_fi_FI.ts new file mode 100644 index 0000000..75a966f --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_fi_FI.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_fr_FR.ts b/Plugins/RegExpImport/translations/RegExpImport_fr_FR.ts new file mode 100644 index 0000000..4041d20 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_fr_FR.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Fichiers (*.txt);;Tous les fichiers (*) + + + + Cannot read file %1 + Impossible de lire le fichier : %1 + + + + Enter the regular expression pattern. + Saisissez une expression valide. + + + + Invalid pattern: %1 + Expression invalide : %1 + + + + Requested capture index %1 is out of range. + Allocation d’index %1 hors limites. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Nom de groupe de saisie demandé '%1', mais il est indéfini dans le modèle : <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Groupes sélectionnés + + + + Treat all RegExp capture groups as columns + Traiter tout RegExp de groupes sélectionnés comme des colonnes + + + + Import only following groups: + Importer seulement les groupes suivant : + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Exemple : 1, 3, 4 + + + + Pattern: + Modèle : + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Exemple : (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_he_IL.ts b/Plugins/RegExpImport/translations/RegExpImport_he_IL.ts new file mode 100644 index 0000000..8bf960e --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_he_IL.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + קבצי מלל (*.txt);;כל ×”×§×‘×¦×™× (*) + + + + Cannot read file %1 + ×œ× × ×™×ª×Ÿ ×œ×§×¨×•× ×§×•×‘×¥ %1 + + + + Enter the regular expression pattern. + הזנת דפוס ביטוי-רגיל. + + + + Invalid pattern: %1 + דפוס שגוי: %1 + + + + Requested capture index %1 is out of range. + מפתח לכידה מבוקש %1 מחוץ לטווח. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>×©× ×§×‘×•×¦×ª הלכידה המבוקש '%1', ×ך ×”×•× ' ×ינו מוגדר בתבנית: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + לכידת קבוצות + + + + Treat all RegExp capture groups as columns + להתייחס לכל קבוצות הלכידה של ביטוי-רגיל כעמודות + + + + Import only following groups: + ×™×‘×•× ×”×§×‘×•×¦×•×ª הב×ות בלבד: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>× × ×œ×”×–×™×Ÿ רשימה מופרדת ×‘×¤×¡×™×§×™× ×©×œ מפתחי קבוצות לכידה. מפתח 0 מתייחס לכל המחרוזת התו×מת.</p> +<p> ×× × ×¢×©×” שימוש בקבוצות ×©× ×‘×ª×‘× ×™×ª, ניתן להשתמש בשמות ×‘×ž×§×•× ×‘×ž×¤×ª×—×™×. ניתן לערבב ×ž×¤×ª×—×™× ×•×©×ž×•×ª ברשימה זו.</p> + + + + Example: 1, 3, 4 + דוגמה: 1, 3, 4 + + + + Pattern: + תבנית: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>ניתן להשתמש בקבוצות ביטוי רגיל כדי למסגר ×—×œ×§×™× ×ž×”×‘×™×˜×•×™ ××•×ª× ×ž×ª×›×•×•× ×™× ×œ×™×™×‘×. ×× ×”×›×•×•× ×” להשתמש בקבוצה ש×ותה ×œ× ×ž×¢×•× ×™×™× ×™× ×œ×™×™×‘×, × × ×œ×”×©×ª×ž×© ב×פשרות "×™×™×‘×•× ×”×§×‘×•×¦×•×ª הב×ות בלבד" מטה. + +ניתן להשתמש בקבוצות ×©× ×•×œ×”×ª×™×™×—×¡ ××œ×™×”× ×‘×¨×©×™×ž×ª הקבוצות שלמטה. כדי לתת ×©× ×œ×§×‘×•×¦×” × × ×œ×”×©×ª×ž×© ב: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + דוגמה: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_hu_HU.ts b/Plugins/RegExpImport/translations/RegExpImport_hu_HU.ts new file mode 100644 index 0000000..b8853b6 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_hu_HU.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_it_IT.ts b/Plugins/RegExpImport/translations/RegExpImport_it_IT.ts new file mode 100644 index 0000000..150a8b3 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_it_IT.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + File di testo (*.txt);;Tutti i file (*) + + + + Cannot read file %1 + Impossibile leggere il file %1 + + + + Enter the regular expression pattern. + Inserisci il modello di espressione regolare. + + + + Invalid pattern: %1 + Modello non valido: %1 + + + + Requested capture index %1 is out of range. + L'indice di cattura richiesto %1 è fuori dall'intervallo. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Nome del gruppo di acquisizione richiesto '%1', ma non è definito nel modello: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Gruppi di cattura + + + + Treat all RegExp capture groups as columns + Tratta tutti i gruppi di acquisizione RegExp come colonne + + + + Import only following groups: + Importa solo i seguenti gruppi: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Inserisci l'elenco separato da virgola degli indici dei gruppi di acquisizione. L'indice 0 si riferisce all'intera stringa corrispondente.</p> +<p>Se hai usato i gruppi denominati nel modello, puoi usare i nomi al posto degli indici. È possibile mescolare indici e nomi in questa lista.</p> + + + + Example: 1, 3, 4 + Esempio: 1, 3, 4 + + + + Pattern: + Modello: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Usa gruppi di espressioni regolari per racchiudere parti dell'espressione che vuoi importare. Se si desidera utilizzare un gruppo, che non si desidera importare, allora utilizzare l'opzione "importa solo i gruppi seguenti" sottostante. + +Puoi usare i gruppi con nome e fare riferimento ad essi nella lista dei gruppi qui sotto. Per nominare un gruppo usa: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Esempio: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_ja_JP.ts b/Plugins/RegExpImport/translations/RegExpImport_ja_JP.ts new file mode 100644 index 0000000..28a71a5 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_ja_JP.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_kaa.ts b/Plugins/RegExpImport/translations/RegExpImport_kaa.ts new file mode 100644 index 0000000..2f60651 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_kaa.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_ko_KR.ts b/Plugins/RegExpImport/translations/RegExpImport_ko_KR.ts new file mode 100644 index 0000000..b931b74 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_ko_KR.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_nl_NL.ts b/Plugins/RegExpImport/translations/RegExpImport_nl_NL.ts new file mode 100644 index 0000000..6920e48 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_nl_NL.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_no_NO.ts b/Plugins/RegExpImport/translations/RegExpImport_no_NO.ts new file mode 100644 index 0000000..44fd6d9 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_no_NO.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_pl_PL.ts b/Plugins/RegExpImport/translations/RegExpImport_pl_PL.ts new file mode 100644 index 0000000..dce3265 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_pl_PL.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Pliki tekstowe (*.txt);;Wszystkie pliki (*) + + + + Cannot read file %1 + Nie można odczytać pliku %1 + + + + Enter the regular expression pattern. + Wprowadź wzorzec wyrażenia regularnego. + + + + Invalid pattern: %1 + Niepoprawny wzorzec: %1 + + + + Requested capture index %1 is out of range. + Żądany indeks przechwytywania %1 jest poza zakresem. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Zażądano grupy do przechwycenia o nazwie '%1', ale nie jest ona zdefiniowana we wzorcu: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Grupy przechwytujÄ…ce + + + + Treat all RegExp capture groups as columns + Traktuj wszystkie grupy przechwytujÄ…ce jako kolumny + + + + Import only following groups: + Importuj tylko nastÄ™pujÄ…ce grupy: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Wprowadź listÄ™ indeksów grup oddzielonÄ… przecinkami. Indeks 0 odpowiada caÅ‚emu dopasowanemu Å‚aÅ„cuchowi.</p> +<p>JeÅ›li użyÅ‚eÅ› nazwanych grup we wzorcu, to możesz używać nazw, zamiast indeksów. Możesz mieszać inseksy i nazwy na liÅ›cie.</p> + + + + Example: 1, 3, 4 + PrzykÅ‚ad: 1, 3, 4 + + + + Pattern: + Wzorzec: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Użyj grup przechwytujÄ…cych WyrażeÅ„ Regularnych, aby otoczyć części wyrażenia, które chcesz zaimportować. JeÅ›li chcesz użyć grupy, której nie chcesz zaimportować, to użyj opcji "importuj tylko nastÄ™pujÄ…ce grupy" poniżej. + +Możesz użyć grup nazwanych i odwoÅ‚ywać siÄ™ do nich w liÅ›cie grup poniżej. Aby nazwać grupÄ™, użyj: <pre>(?&lt;nazwaGrupy&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + PrzykÅ‚ad: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_pt_BR.ts b/Plugins/RegExpImport/translations/RegExpImport_pt_BR.ts new file mode 100644 index 0000000..6c97aee --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_pt_BR.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Arquivos de texto (*.txt);;Todos os arquivos (*) + + + + Cannot read file %1 + Não é possível ler o arquivo %1 + + + + Enter the regular expression pattern. + Digite o padrão de expressão regular. + + + + Invalid pattern: %1 + Padrão inválido: %1 + + + + Requested capture index %1 is out of range. + O índice de captura solicitado %1 está fora do alcance. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Nome do grupo de captura requisitado '%1', mas'não definido no padrão: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Grupos de captura + + + + Treat all RegExp capture groups as columns + Tratar todos os grupos de captura RegExp como colunas + + + + Import only following groups: + Importar apenas os seguintes grupos: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Insira uma lista separada por vírgulas de índices de grupo de captura. O índice 0 refere-se a toda a string correspondente.</p> +<p>Se você usou grupos nomeados no padrão, você pode usar nomes ao invés de indexes. Você pode misturar índices e nomes nesta lista.</p> + + + + Example: 1, 3, 4 + Exemplo: 1, 3, 4 + + + + Pattern: + Padrão: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use grupos de expressão regular para incluir partes da expressão que você deseja importar. Se você quiser usar um grupo, que você usa't para importar, em seguida, use "importar apenas seguindo grupos" opção abaixo. + +Você pode usar grupos nomeados e se referir a eles na lista de grupos abaixo. Para nomear um grupo, use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Exemplo: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_pt_PT.ts b/Plugins/RegExpImport/translations/RegExpImport_pt_PT.ts new file mode 100644 index 0000000..524d301 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_pt_PT.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_ro_RO.ts b/Plugins/RegExpImport/translations/RegExpImport_ro_RO.ts new file mode 100644 index 0000000..a54d223 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_ro_RO.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_ru_RU.ts b/Plugins/RegExpImport/translations/RegExpImport_ru_RU.ts new file mode 100644 index 0000000..aac0315 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_ru_RU.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + ТекÑтовые файлы (*.txt);;Ð’Ñе файлы (*) + + + + Cannot read file %1 + Ðевозможно прочитать файл %1 + + + + Enter the regular expression pattern. + Введите шаблон регулÑрного выражениÑ. + + + + Invalid pattern: %1 + Ðекорректный шаблон: %1 + + + + Requested capture index %1 is out of range. + Запрошенный Ð¸Ð½Ð´ÐµÐºÑ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ %1 вне доÑÑгаемоÑти. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Запрошено Ð¸Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ '%1', но оно не определено в шаблоне: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Группы в шаблоне + + + + Treat all RegExp capture groups as columns + РаÑÑматривать вÑе группы в выражении как Ñтолбцы + + + + Import only following groups: + Импортировать только Ñледующие группы: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Введите разделённый запÑтыми ÑпиÑок индекÑов групп. Ð˜Ð½Ð´ÐµÐºÑ 0 Ñлужит Ð´Ð»Ñ Ð´Ð¾Ñтупа ко вÑей найденной Ñтроке.</p> +<p>При иÑпользовании именованных групп в шаблоне, вы можете указывать имена групп вмеÑто индекÑов. Имена групп и индекÑÑ‹ можно иÑпользовать вмеÑте.</p> + + + + Example: 1, 3, 4 + Пример: 1, 3, 4 + + + + Pattern: + Шаблон: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>ИÑпользуйте группировку в регулÑрном выражении Ð´Ð»Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ñ‡Ð°Ñтей выражениÑ, которые необходимо импортировать. ЕÑли необходимо иÑпользовать группу, иÑключаемую при импорте, иÑпользуйте опцию "Импортировать только Ñледующие группы" ниже. + +Можно также иÑпользовать именованные группы Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² ÑпиÑке ниже. Ð”Ð»Ñ Ð¿Ñ€Ð¸ÑÐ²Ð¾ÐµÐ½Ð¸Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ðµ имени иÑпользуйте: <pre>(?&lt;моёИмÑГруппы&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Пример: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_sk_SK.ts b/Plugins/RegExpImport/translations/RegExpImport_sk_SK.ts new file mode 100644 index 0000000..6fba8c3 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_sk_SK.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_sr_SP.ts b/Plugins/RegExpImport/translations/RegExpImport_sr_SP.ts new file mode 100644 index 0000000..9e439c5 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_sr_SP.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_sv_SE.ts b/Plugins/RegExpImport/translations/RegExpImport_sv_SE.ts new file mode 100644 index 0000000..1f660be --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_sv_SE.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_tr_TR.ts b/Plugins/RegExpImport/translations/RegExpImport_tr_TR.ts new file mode 100644 index 0000000..491594d --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_tr_TR.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Metin dosyaları (*.txt);;Tüm Dosyalar (*) + + + + Cannot read file %1 + %1 dosyası okunamadı + + + + Enter the regular expression pattern. + Normal ifade desenini girin. + + + + Invalid pattern: %1 + Geçersiz desen: %1 + + + + Requested capture index %1 is out of range. + İstenen yakalama dizini %1 aralık dışında. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>İstenen yakalama grubu adı '%1', ama o ' desende tanımlanmadı: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Yakalama grupları + + + + Treat all RegExp capture groups as columns + Tüm RegExp yakalama gruplarını sütun olarak iÅŸle + + + + Import only following groups: + Yalnızca aÅŸağıdaki grupları içe aktar: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Yakalama grubu dizinlerinin virgülle ayrılmış listesini girin. 0 Dizini, eÅŸleÅŸen dizenin tamamını ifade eder.</p> +<p>Desende adlandırılmış gruplar kullandıysanız, dizinler yerine adları kullanabilirsiniz. Bu listedeki dizinleri ve adları karıştırabilirsiniz.</p> + + + + Example: 1, 3, 4 + ÖrneÄŸin: 1, 3, 4 + + + + Pattern: + Desen: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Almak istediÄŸiniz ifadenin bölümlerini içine almak için Normal İfade gruplarını kullanın. EÄŸer bir grup kullanmak istiyorsanız, o zaman't almak istemiyorsanız, o zaman kullanın "yalnızca aÅŸağıdaki grupları içe aktar" aÅŸağıdaki seçenek. + +Adlandırılmış grupları kullanabilir ve bunlara aÅŸağıdaki grup listesinden baÅŸvurabilirsiniz. Bir grubu adlandırmak için kullanın: <pre>(?&lt;benimGrupAdım&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + ÖrneÄŸin: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_uk_UA.ts b/Plugins/RegExpImport/translations/RegExpImport_uk_UA.ts new file mode 100644 index 0000000..8130662 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_uk_UA.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_vi_VN.ts b/Plugins/RegExpImport/translations/RegExpImport_vi_VN.ts new file mode 100644 index 0000000..ffb31c1 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_vi_VN.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + Text files (*.txt);;All files (*) + + + + Cannot read file %1 + Cannot read file %1 + + + + Enter the regular expression pattern. + Enter the regular expression pattern. + + + + Invalid pattern: %1 + Invalid pattern: %1 + + + + Requested capture index %1 is out of range. + Requested capture index %1 is out of range. + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + Capture groups + + + + Treat all RegExp capture groups as columns + Treat all RegExp capture groups as columns + + + + Import only following groups: + Import only following groups: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + + + + Example: 1, 3, 4 + Example: 1, 3, 4 + + + + Pattern: + Pattern: + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_zh_CN.ts b/Plugins/RegExpImport/translations/RegExpImport_zh_CN.ts new file mode 100644 index 0000000..a205322 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_zh_CN.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + 文本文件 (*.txt);;所有文件 (*) + + + + Cannot read file %1 + æ— æ³•è¯»å–æ–‡ä»¶ %1 + + + + Enter the regular expression pattern. + è¯·è¾“å…¥æ­£åˆ™è¡¨è¾¾å¼æ¨¡å¼ã€‚ + + + + Invalid pattern: %1 + 无效模å¼ï¼š%1 + + + + Requested capture index %1 is out of range. + 请求的æ•获索引 %1 超出范围。 + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>请求的æ•获组åç§° '%1' 未在该模å¼ä¸­å®šä¹‰ï¼š<pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + æ•获组 + + + + Treat all RegExp capture groups as columns + æ‰€æœ‰çš„æ­£åˆ™è¡¨è¾¾å¼æ•获组视作列 + + + + Import only following groups: + 仅导入下列组: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>请输入以英文逗å·åˆ†éš”çš„æ•获组索引åºå·åˆ—表。索引 0 表示匹é…的整个字符串。</p> +<p>如果在模å¼ä¸­ä½¿ç”¨äº†å‘½å组,则此处å¯ä»¥ä½¿ç”¨å称而éžç´¢å¼•åºå·ã€‚å¯ä»¥åœ¨æ­¤åˆ—表中混åˆä½¿ç”¨ç´¢å¼•åºå·å’Œç´¢å¼•å称。</p> + + + + Example: 1, 3, 4 + 示例:1, 3, 4 + + + + Pattern: + 模å¼ï¼š + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>使用正则表达å¼çš„æ•èŽ·ç»„æ¥è¡¨ç¤ºè¦å¯¼å…¥çš„部分。如果包å«ä¸æƒ³å¯¼å…¥çš„æ•èŽ·ç»„ï¼Œä½¿ç”¨ä¸‹æ–¹çš„â€œä»…å¯¼å…¥ä¸‹åˆ—ç»„â€é€‰é¡¹ã€‚ + +您å¯ä»¥ä½¿ç”¨å‘½å组特性并在下方的列表中引用。引用命åçš„æ•获组:<pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + 示例:(\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/RegExpImport/translations/RegExpImport_zh_TW.ts b/Plugins/RegExpImport/translations/RegExpImport_zh_TW.ts new file mode 100644 index 0000000..9d75305 --- /dev/null +++ b/Plugins/RegExpImport/translations/RegExpImport_zh_TW.ts @@ -0,0 +1,86 @@ + + + + + RegExpImport + + + Text files (*.txt);;All files (*) + 文字檔案 (*.txt);;所有檔案 (*) + + + + Cannot read file %1 + ç„¡æ³•è®€å–æª”案 %1 + + + + Enter the regular expression pattern. + è«‹è¼¸å…¥æ­£å‰‡è¡¨ç¤ºå¼æ¨¡å¼ã€‚ + + + + Invalid pattern: %1 + 無效模å¼ï¼š%1 + + + + Requested capture index %1 is out of range. + 請求的æ•ç²ç´¢å¼• %1 超出範åœã€‚ + + + + <p>Requested capture group name '%1', but it's not defined in the pattern: <pre>%2</pre></p> + <p>請求的æ•ç²çµ„å稱 '%1' 未在該模å¼ä¸­å®šç¾©ï¼š<pre>%2</pre></p> + + + + RegExpImportConfig + + + Capture groups + æ•ç²çµ„ + + + + Treat all RegExp capture groups as columns + æ‰€æœ‰çš„æ­£å‰‡è¡¨ç¤ºå¼æ•ç²çµ„視作列 + + + + Import only following groups: + 僅匯入下列組: + + + + <p>Enter comma separated list of capture group indexes. The 0 index refers to the entire matched string.</p> +<p>If you used named groups in the pattern, you can use names instead of indexes. You can mix indexes and names in this list.</p> + <p>請輸入以英文逗號分隔的æ•ç²çµ„索引åºè™Ÿæ¸…單。索引 0 表示匹é…的整個字串。</p> +<p>如果在模å¼ä¸­ä½¿ç”¨äº†å‘½å組,則此處å¯ä»¥ä½¿ç”¨å稱而éžç´¢å¼•åºè™Ÿã€‚å¯ä»¥åœ¨æ­¤æ¸…單中混åˆä½¿ç”¨ç´¢å¼•åºè™Ÿå’Œç´¢å¼•å稱。</p> + + + + Example: 1, 3, 4 + 示例:1, 3, 4 + + + + Pattern: + 模å¼ï¼š + + + + <p>Use Regular Expression groups to enclose parts of the expression that you want to import. If you want to use a group, that you don't want to import, then use "import only following groups" option below. + +You can use named groups and refer to them in group list below. To name a group use: <pre>(?&lt;myGroupName&gt;\s+\d+\s+)</pre></p> + <p>使用正則表示å¼çš„æ•ç²çµ„來表示è¦åŒ¯å…¥çš„部分。如果包å«ä¸æƒ³åŒ¯å…¥çš„æ•ç²çµ„,使用下方的“僅匯入下列組â€é¸é …。 + +您å¯ä»¥ä½¿ç”¨å‘½å組特性並在下方的清單中引用。引用命åçš„æ•ç²çµ„:<pre>(?&lt;組å&gt;\s+\d+\s+)</pre></p> + + + + Example: (\d+)\s+((\d+)\w+)\s+(\w+) + 示例:(\d+)\s+((\d+)\w+)\s+(\w+) + + + diff --git a/Plugins/ScriptingPython/ScriptingPython.pro b/Plugins/ScriptingPython/ScriptingPython.pro new file mode 100644 index 0000000..02fb5d3 --- /dev/null +++ b/Plugins/ScriptingPython/ScriptingPython.pro @@ -0,0 +1,34 @@ +QT -= gui + +include($$PWD/../../SQLiteStudio3/plugins.pri) + +TARGET = ScriptingPython +TEMPLATE = lib + +DEFINES += SCRIPTINGPYTHON_LIBRARY + +QMAKE_CXXFLAGS += -Wno-cast-function-type + +SOURCES += scriptingpython.cpp + +HEADERS += scriptingpython.h\ + scriptingpython_global.h + +OTHER_FILES += \ + scriptingpython.json + +linux: { + LIBS += -lpython3.9 +} + +macx: { + LIBS += -lpython3.9 +} + +win32: { + INCLUDEPATH += $$PWD/../../../include/python + LIBS += -lpython39 -L$$PWD/../../../lib/python +} + +RESOURCES += \ + scriptingpython.qrc diff --git a/Plugins/ScriptingPython/icon_attribution.txt b/Plugins/ScriptingPython/icon_attribution.txt new file mode 100644 index 0000000..8211625 --- /dev/null +++ b/Plugins/ScriptingPython/icon_attribution.txt @@ -0,0 +1 @@ +Python icon was made by Freepik - https://www.freepik.com diff --git a/Plugins/ScriptingPython/scriptingpython.cpp b/Plugins/ScriptingPython/scriptingpython.cpp new file mode 100644 index 0000000..1dedb5c --- /dev/null +++ b/Plugins/ScriptingPython/scriptingpython.cpp @@ -0,0 +1,666 @@ +#include "scriptingpython.h" +#include "common/global.h" +#include "common/unused.h" +#include "db/db.h" +#include "db/sqlite3.h" +#include "parser/lexer.h" +#include "parser/token.h" +#include "common/utils_sql.h" +#include +#include +#include + +static PyMethodDef pyDbMethods[] = { + {"eval", reinterpret_cast(ScriptingPython::dbEval), METH_FASTCALL, ""}, + {nullptr, nullptr, 0, nullptr} +}; + +static PyModuleDef pyDbModule = { + PyModuleDef_HEAD_INIT, "db", nullptr, -1, pyDbMethods, + nullptr, nullptr, nullptr, nullptr +}; + +static PyObject* pyDbModuleInit(void) +{ + return PyModule_Create(&pyDbModule); +} + +QHash ScriptingPython::contexts; + +ScriptingPython::ScriptingPython() +{ + mainInterpMutex = new QMutex(); +} + +ScriptingPython::~ScriptingPython() +{ + safe_delete(mainInterpMutex); +} + +bool ScriptingPython::init() +{ + SQLS_INIT_RESOURCE(scriptingpython); + QMutexLocker locker(mainInterpMutex); + + PyImport_AppendInittab("db", &pyDbModuleInit); + Py_Initialize(); + PyRun_SimpleString("import db"); + + mainContext = new ContextPython(); + contexts[mainContext->interp] = mainContext; + return true; +} + +void ScriptingPython::deinit() +{ + QMutexLocker locker(mainInterpMutex); + contexts.clear(); + Py_Finalize(); + SQLS_CLEANUP_RESOURCE(scriptingpython); +} + +QString ScriptingPython::getLanguage() const +{ + return "Python"; +} + +ScriptingPlugin::Context* ScriptingPython::createContext() +{ + ContextPython* ctx = new ContextPython(); + contexts[ctx->interp] = ctx; + return ctx; +} + +void ScriptingPython::releaseContext(ScriptingPlugin::Context* context) +{ + ContextPython* ctx = getContext(context); + if (!ctx) + return; + + contexts.remove(ctx->interp); + delete ctx; + PyThreadState_Swap(mainContext->interp); +} + +void ScriptingPython::resetContext(ScriptingPlugin::Context* context) +{ + ContextPython* ctx = getContext(context); + if (!ctx) + return; + + ctx->reset(); +} + +void ScriptingPython::setVariable(ScriptingPlugin::Context* context, const QString& name, const QVariant& value) +{ + ContextPython* ctx = getContext(context); + if (!ctx) + return; + + PyObject* obj = variantToPythonObj(value); + PyDict_SetItemString(ctx->envDict, name.toUtf8().constData(), obj); + Py_DECREF(obj); +} + + +QVariant ScriptingPython::getVariable(ScriptingPlugin::Context* context, const QString& name) +{ + ContextPython* ctx = getContext(context); + if (!ctx) + return QVariant(); + + PyThreadState_Swap(ctx->interp); + return getVariable(name); +} + +bool ScriptingPython::hasError(ScriptingPlugin::Context* context) const +{ + ContextPython* ctx = getContext(context); + if (!ctx) + return false; + + return !ctx->error.isEmpty(); +} + +QString ScriptingPython::getErrorMessage(ScriptingPlugin::Context* context) const +{ + ContextPython* ctx = getContext(context); + if (!ctx) + return QString(); + + return ctx->error; +} + +QString ScriptingPython::getIconPath() const +{ + return ":/scriptingpython/scriptingpython.png"; +} + +QVariant ScriptingPython::evaluate(ScriptingPlugin::Context* context, const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking) +{ + ContextPython* ctx = getContext(context); + if (!ctx) + return QVariant(); + + return compileAndEval(ctx, code, funcInfo, args, db, locking); +} + +QVariant ScriptingPython::evaluate(const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking, QString* errorMessage) +{ + QMutexLocker locker(mainInterpMutex); + QVariant results = compileAndEval(mainContext, code, funcInfo, args, db, locking); + + if (errorMessage && !mainContext->error.isEmpty()) + *errorMessage = mainContext->error; + + return results; +} + +ScriptingPython::ContextPython* ScriptingPython::getContext(ScriptingPlugin::Context* context) const +{ + ContextPython* ctx = dynamic_cast(context); + if (!ctx) + qDebug() << "Invalid context passed to ScriptingPython:" << context; + + return ctx; +} + +QVariant ScriptingPython::compileAndEval(ScriptingPython::ContextPython* ctx, const QString& code, const FunctionInfo& funcInfo, + const QList& args, Db* db, bool locking) +{ + PyThreadState_Swap(ctx->interp); + clearError(ctx); + + ScriptObject* scriptObj = getScriptObject(code, funcInfo, ctx); + if (PyErr_Occurred() || !scriptObj->getCompiled()) + { + ctx->error = extractError(); + return QVariant(); + } + + ctx->db = db; + ctx->useDbLocking = locking; + + PyObject* pyArgs = argsToPyArgs(args, funcInfo.getArguments()); + PyObject* result = PyObject_CallObject(scriptObj->getCompiled(), pyArgs); + Py_DECREF(pyArgs); + + ctx->db = nullptr; + ctx->useDbLocking = false; + + if (PyErr_Occurred()) + { + Py_XDECREF(result); + ctx->error = extractError(); + return QVariant(); + } + + QVariant resultVariant = pythonObjToVariant(result); + Py_XDECREF(result); + return resultVariant; +} + +QString ScriptingPython::extractError() +{ + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + if (!value) + return QString(); + + PyObject* strErr = PyObject_Repr(value); + QString err = QString::fromUtf8(PyUnicode_AsUTF8(strErr)); + PyErr_Clear(); + + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); + Py_XDECREF(strErr); + + return err; +} + +void ScriptingPython::clearError(ContextPython* ctx) +{ + PyErr_Clear(); + ctx->error.clear(); +} + +ScriptingPython::ScriptObject* ScriptingPython::getScriptObject(const QString code, const ScriptingPlugin::FunctionInfo& funcInfo, ContextPython* ctx) +{ + static const QString keyTpl = QStringLiteral("{%1} %2"); + + QString key = keyTpl.arg(funcInfo.getArguments().join("#"), code); + if (ctx->scriptCache.contains(key)) + return ctx->scriptCache[key]; + + ScriptObject* scriptObj = new ScriptObject(code, funcInfo, ctx); + ctx->scriptCache.insert(key, scriptObj); + return scriptObj; +} + +PyObject* ScriptingPython::argsToPyArgs(const QVariantList& args, const QStringList& namedParameters) +{ + PyObject* result = PyTuple_New(args.size()); + PyObject* namedParamTuple = namedParameters.isEmpty() ? nullptr : PyTuple_New(namedParameters.size() + 1); + int i = 0; + for (const QVariant& value : args) + { + PyObject* valueObj = variantToPythonObj(value); + PyTuple_SetItem(result, i, valueObj); + if (namedParamTuple && i < namedParameters.size()) + { + Py_INCREF(valueObj); + PyTuple_SetItem(namedParamTuple, i, valueObj); + } + i++; + } + + // If function has named parameters, than named param tuple takes precedense + // and positional tuple becomes last argument. + if (namedParamTuple) + { + PyTuple_SetItem(namedParamTuple, namedParameters.size(), result); + result = namedParamTuple; + } + + return result; +} + +QVariant ScriptingPython::pythonObjToVariant(PyObject* obj) +{ + if (!obj) + return QVariant(); + + if (PyUnicode_Check(obj)) + return QString::fromUtf8(PyUnicode_AsUTF8(obj)); + + if (PyLong_Check(obj)) + return PyLong_AsLongLong(obj); + + if (PyBool_Check(obj)) + return PyObject_IsTrue(obj) != 0; + + if (PyFloat_Check(obj)) + return PyFloat_AsDouble(obj); + + if (PyBytes_Check(obj)) + { + char* buf; + Py_ssize_t size; + if (PyBytes_AsStringAndSize(obj, &buf, &size) != 0) + return QVariant(); + + return QByteArray::fromRawData(buf, size); + } + + if (PyByteArray_Check(obj)) + { + char* buf = PyByteArray_AsString(obj); + Py_ssize_t size = PyByteArray_Size(obj); + return QByteArray::fromRawData(buf, size); + } + + if (PyTuple_Check(obj)) + { + QList result; + + Py_ssize_t size = PyTuple_Size(obj); + for (Py_ssize_t i = 0; i < size; i++) + result << pythonObjToVariant(PyTuple_GetItem(obj, i)); + + return result; + } + + if (PyList_Check(obj)) + { + QList result; + + Py_ssize_t size = PyList_Size(obj); + for (Py_ssize_t i = 0; i < size; i++) + result << pythonObjToVariant(PyList_GetItem(obj, i)); + + return result; + } + + if (PySet_Check(obj)) + { + QSet result; + + PyObject *iterator = PyObject_GetIter(obj); + PyObject *item; + + if (iterator == NULL) + return QVariant(); + + while ((item = PyIter_Next(iterator))) + { + result << pythonObjToVariant(item); + Py_DECREF(item); + } + + Py_DECREF(iterator); + + return QVariant::fromValue(result); + } + + if (PyDict_Check(obj)) + { + QHash result; + + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next(obj, &pos, &key, &value)) + result[pythonObjToString(key)] = pythonObjToVariant(value); + + return result; + } + + PyObject* strObj = PyObject_Repr(obj); + QString result = QString::fromUtf8(PyUnicode_AsUTF8(strObj)); + Py_DECREF(strObj); + return result; +} + +QString ScriptingPython::pythonObjToString(PyObject* obj) +{ + PyObject* strObj = PyObject_Repr(obj); + if (!strObj) + return QString(); + + QString result = QString::fromUtf8(PyUnicode_AsUTF8(strObj)); + Py_DECREF(strObj); + return result; +} + +PyObject* ScriptingPython::variantToPythonObj(const QVariant& value) +{ + PyObject* obj = nullptr; + switch (value.type()) + { + case QVariant::Bool: + obj = PyBool_FromLong(value.toBool() ? 1L : 0L); + break; + case QVariant::Int: + case QVariant::LongLong: + obj = PyLong_FromLongLong(value.toLongLong()); + break; + case QVariant::UInt: + case QVariant::ULongLong: + obj = PyLong_FromUnsignedLongLong(value.toULongLong()); + break; + case QVariant::Double: + obj = PyFloat_FromDouble(value.toDouble()); + break; + case QVariant::ByteArray: + { + QByteArray bytes = value.toByteArray(); + char* data = bytes.data(); + obj = PyBytes_FromStringAndSize(data, bytes.size()); + break; + } + case QVariant::List: + { + QList list = value.toList(); + int listSize = list.size(); + obj = PyList_New(listSize); + int pos = 0; + for (const QVariant& item : list) + { + PyObject* subObj = variantToPythonObj(item); + PyList_SetItem(obj, pos++, subObj); + Py_DECREF(subObj); + } + + break; + } + case QVariant::StringList: + { + QStringList list = value.toStringList(); + int listSize = list.size(); + obj = PyList_New(listSize); + int pos = 0; + for (const QVariant& item : list) + { + PyObject* subObj = variantToPythonObj(item); + PyList_SetItem(obj, pos++, subObj); + Py_DECREF(subObj); + } + + break; + } + case QVariant::Hash: + { + QHash hash = value.toHash(); + obj = PyDict_New(); + QHashIterator it(hash); + while (it.hasNext()) + { + it.next(); + PyObject* subObj = variantToPythonObj(it.value()); + PyDict_SetItemString(obj, it.key().toUtf8().constData(), subObj); + Py_DECREF(subObj); + } + break; + } + case QVariant::Map: + { + QMap map = value.toMap(); + obj = PyDict_New(); + QMapIterator it(map); + while (it.hasNext()) + { + it.next(); + PyObject* subObj = variantToPythonObj(it.value()); + PyDict_SetItemString(obj, it.key().toUtf8().constData(), subObj); + Py_DECREF(subObj); + } + break; + } + case QVariant::String: + default: + obj = stringToPythonObj(value.toString()); + break; + } + + if (!obj) + obj = stringToPythonObj(value.toString()); + + return obj; +} + +PyObject* ScriptingPython::stringToPythonObj(const QString& value) +{ + QByteArray bytes = value.toUtf8(); + return PyUnicode_FromStringAndSize(bytes.constData(), bytes.size()); +} + +PyObject* ScriptingPython::dbEval(PyObject* self, PyObject *const *args, Py_ssize_t nargs) +{ + UNUSED(self); + + if (nargs != 1) + { + PyErr_SetString(PyExc_RuntimeError, QObject::tr("Invalid use of %1 function. Expected %2 arguments, but got %3.") + .arg("db.eval()", QString::number(1), QString::number(nargs)) + .toUtf8().constData()); + return nullptr; + } + + SqlQueryPtr execResults = dbCommonEval(args[0], "db.eval()"); + if (!execResults) + { + PyErr_SetString(PyExc_RuntimeError, QObject::tr("Unknown error from function %1.").arg("db.eval()").toUtf8().constData()); + return nullptr; + } + + if (execResults->isError()) + { + PyErr_SetString(PyExc_RuntimeError, execResults->getErrorText().toUtf8().constData()); + return nullptr; + } + + QList tuples; + while (execResults->hasNext()) + { + SqlResultsRowPtr row = execResults->next(); + PyObject* tuple = PyTuple_New(row->valueList().size()); + int pos = 0; + for (const QVariant& cell : row->valueList()) + PyTuple_SetItem(tuple, pos++, variantToPythonObj(cell)); + + tuples << tuple; + } + + PyObject* resultObject = PyList_New(0); + for (PyObject* tuple : tuples) + { + PyList_Append(resultObject, tuple); + Py_DECREF(tuple); + } + + return resultObject; +} + +SqlQueryPtr ScriptingPython::dbCommonEval(PyObject* sqlArg, const char* fnName) +{ + QString sql; + if (!PyUnicode_Check(sqlArg)) + { + PyObject* strObj = PyObject_Repr(sqlArg); + if (!strObj) + { + return SqlQuery::error( + QObject::tr("Could not calculate string representation of the Python object passed as argument to the function %1.") + .arg(fnName), + SQLITE_ERROR); + } + + sql = QString::fromUtf8(PyUnicode_AsUTF8(strObj)); + Py_DECREF(strObj); + } + else + sql = QString::fromUtf8(PyUnicode_AsUTF8(sqlArg)); + + PyThreadState* state = PyThreadState_Get(); + ContextPython* ctx = contexts[state]; + if (!ctx) + { + return SqlQuery::error( + QObject::tr("Could not find execution context for function %1. This is a bug of Python plugin. Please report it.") + .arg(fnName), + SQLITE_ERROR); + } + + Db::Flags flags; + if (!ctx->useDbLocking) + flags |= Db::Flag::NO_LOCK; + + TokenList bindTokens = Lexer::tokenize(sql).filter(Token::BIND_PARAM); + QString bindVarName; + QHash queryArgs; + for (const TokenPtr& token : bindTokens) + { + bindVarName = getBindTokenName(token); + if (bindVarName == "?") + continue; + + queryArgs[token->value] = getVariable(bindVarName); + } + + SqlQueryPtr execResults = ctx->db->exec(sql, queryArgs, flags); + if (execResults->isError()) + { + return SqlQuery::error( + QObject::tr("Error from Python function %1: %2") + .arg(fnName, execResults->getErrorText()), + SQLITE_ERROR); + } + return execResults; +} + +QVariant ScriptingPython::getVariable(const QString& name) +{ + PyThreadState* state = PyThreadState_Get(); + if (!state->frame) + return QVariant(); + + const char* varName = name.toUtf8().constData(); + PyObject* obj = nullptr; + + PyFrame_FastToLocals(state->frame); + PyObject* locals = state->frame->f_locals; + PyObject* globals = state->frame->f_globals; + if (PyMapping_Check(locals)) + obj = PyMapping_GetItemString(locals, varName); + else if (PyDict_Check(globals)) + obj = PyDict_GetItemString(globals, varName); + + if (!obj) + return QVariant(); + + return pythonObjToVariant(obj); +} + +ScriptingPython::ScriptObject::ScriptObject(const QString& code, const ScriptingPlugin::FunctionInfo& funcInfo, ContextPython* context) +{ + static_qstring(fnTpl, "def fn(%1%2*args):\n%3"); + + QString indentedCode = indentMultiline(code); + QByteArray utf8Bytes = funcInfo.getUndefinedArgs() ? + fnTpl.arg("", "", indentedCode).toUtf8() : + fnTpl.arg(funcInfo.getArguments().join(", "), ", ", indentedCode).toUtf8(); + + PyObject* result = PyRun_String(utf8Bytes.constData(), Py_file_input, context->envDict, context->envDict); + if (!result) + return; + + Py_DECREF(result); + compiled = PyObject_GetAttrString(context->mainModule, "fn"); +} + +ScriptingPython::ScriptObject::~ScriptObject() +{ + Py_CLEAR(compiled); +} + +PyObject* ScriptingPython::ScriptObject::getCompiled() const +{ + return compiled; +} + +ScriptingPython::ContextPython::ContextPython() +{ + scriptCache.setMaxCost(cacheSize); + init(); +} + +ScriptingPython::ContextPython::~ContextPython() +{ + clear(); +} + +void ScriptingPython::ContextPython::reset() +{ + clear(); + init(); +} + +void ScriptingPython::ContextPython::init() +{ + interp = Py_NewInterpreter(); + PyThreadState_Swap(interp); + mainModule = PyImport_AddModule("__main__"); + envDict = PyModule_GetDict(mainModule); + PyRun_SimpleString("import db"); +} + +void ScriptingPython::ContextPython::clear() +{ + PyThreadState_Swap(interp); + PyDict_Clear(envDict); + scriptCache.clear(); + PyErr_Clear(); + Py_EndInterpreter(interp); + error = QString(); +} diff --git a/Plugins/ScriptingPython/scriptingpython.h b/Plugins/ScriptingPython/scriptingpython.h new file mode 100644 index 0000000..ef88a20 --- /dev/null +++ b/Plugins/ScriptingPython/scriptingpython.h @@ -0,0 +1,96 @@ +#ifndef SCRIPTINGPYTHON_H +#define SCRIPTINGPYTHON_H + +#include + +#include "scriptingpython_global.h" +#include "plugins/genericplugin.h" +#include "plugins/scriptingplugin.h" +#include "db/sqlquery.h" +#include + +class QMutex; + +class SCRIPTINGPYTHONSHARED_EXPORT ScriptingPython : public GenericPlugin, public DbAwareScriptingPlugin +{ + Q_OBJECT + SQLITESTUDIO_PLUGIN("scriptingpython.json") + + public: + static PyObject* dbEval(PyObject *self, PyObject* const* args, Py_ssize_t nargs); + + ScriptingPython(); + ~ScriptingPython(); + + bool init(); + void deinit(); + QString getLanguage() const; + Context* createContext(); + void releaseContext(Context* context); + void resetContext(Context* context); + void setVariable(Context* context, const QString& name, const QVariant& value); + QVariant getVariable(Context* context, const QString& name); + bool hasError(Context* context) const; + QString getErrorMessage(Context* context) const; + QString getIconPath() const; + QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking = false); + QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking = false, QString* errorMessage = nullptr); + + private: + class ContextPython; + class ScriptObject + { + public: + ScriptObject(const QString& code, const FunctionInfo& funcInfo, ContextPython* context); + ~ScriptObject(); + + PyObject* getCompiled() const; + + private: + PyObject* compiled = nullptr; + }; + + class ContextPython : public ScriptingPlugin::Context + { + public: + ContextPython(); + ~ContextPython(); + + void reset(); + + PyThreadState* interp = nullptr; + PyObject* mainModule = nullptr; + PyObject* envDict = nullptr; + QCache scriptCache; + QString error; + Db* db = nullptr; + bool useDbLocking = false; + + private: + void init(); + void clear(); + }; + + ContextPython* getContext(ScriptingPlugin::Context* context) const; + QVariant compileAndEval(ContextPython* ctx, const QString& code, const FunctionInfo& funcInfo, + const QList& args, Db* db, bool locking); + void clearError(ContextPython* ctx); + ScriptObject* getScriptObject(const QString code, const ScriptingPlugin::FunctionInfo& funcInfo, ContextPython* ctx); + + static QString extractError(); + static PyObject* argsToPyArgs(const QVariantList& args, const QStringList& namedParameters); + static QVariant pythonObjToVariant(PyObject* obj); + static QString pythonObjToString(PyObject* obj); + static PyObject* variantToPythonObj(const QVariant& value); + static PyObject* stringToPythonObj(const QString& value); + static SqlQueryPtr dbCommonEval(PyObject* sqlArg, const char* fnName); + static QVariant getVariable(const QString& name); + + static const constexpr int cacheSize = 5; + static QHash contexts; + + ContextPython* mainContext = nullptr; + QMutex* mainInterpMutex = nullptr; +}; + +#endif // SCRIPTINGPYTHON_H diff --git a/Plugins/ScriptingPython/scriptingpython.json b/Plugins/ScriptingPython/scriptingpython.json new file mode 100644 index 0000000..c7c67bd --- /dev/null +++ b/Plugins/ScriptingPython/scriptingpython.json @@ -0,0 +1,8 @@ +{ + "type": "ScriptingPlugin", + "title": "Python scripting", + "description": "Provides Python scripting language support for SQLiteStudio.", + "version": 10000, + "author": "SalSoft", + "loadByDefault": false +} diff --git a/Plugins/ScriptingPython/scriptingpython.png b/Plugins/ScriptingPython/scriptingpython.png new file mode 100644 index 0000000..fb510e4 Binary files /dev/null and b/Plugins/ScriptingPython/scriptingpython.png differ diff --git a/Plugins/ScriptingPython/scriptingpython.qrc b/Plugins/ScriptingPython/scriptingpython.qrc new file mode 100644 index 0000000..24ae6da --- /dev/null +++ b/Plugins/ScriptingPython/scriptingpython.qrc @@ -0,0 +1,5 @@ + + + scriptingpython.png + + diff --git a/Plugins/ScriptingPython/scriptingpython_global.h b/Plugins/ScriptingPython/scriptingpython_global.h new file mode 100644 index 0000000..63c42e0 --- /dev/null +++ b/Plugins/ScriptingPython/scriptingpython_global.h @@ -0,0 +1,12 @@ +#ifndef SCRIPTINGPYTHON_GLOBAL_H +#define SCRIPTINGPYTHON_GLOBAL_H + +#include + +#if defined(SCRIPTINGPYTHON_LIBRARY) +# define SCRIPTINGPYTHONSHARED_EXPORT Q_DECL_EXPORT +#else +# define SCRIPTINGPYTHONSHARED_EXPORT Q_DECL_IMPORT +#endif + +#endif // SCRIPTINGPYTHON_GLOBAL_H diff --git a/Plugins/ScriptingPython/translations/ScriptingPython.ts b/Plugins/ScriptingPython/translations/ScriptingPython.ts new file mode 100644 index 0000000..7d4c86c --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + + Unknown error from function %1. + + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + + Error from Python function %1: %2 + + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_af_ZA.ts b/Plugins/ScriptingPython/translations/ScriptingPython_af_ZA.ts new file mode 100644 index 0000000..ddd0cf0 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_af_ZA.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_ar_SA.ts b/Plugins/ScriptingPython/translations/ScriptingPython_ar_SA.ts new file mode 100644 index 0000000..bd7a411 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_ar_SA.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_ca_ES.ts b/Plugins/ScriptingPython/translations/ScriptingPython_ca_ES.ts new file mode 100644 index 0000000..2beab4f --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_ca_ES.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_cs_CZ.ts b/Plugins/ScriptingPython/translations/ScriptingPython_cs_CZ.ts new file mode 100644 index 0000000..a840976 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_cs_CZ.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_da_DK.ts b/Plugins/ScriptingPython/translations/ScriptingPython_da_DK.ts new file mode 100644 index 0000000..63d5776 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_da_DK.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_de_DE.ts b/Plugins/ScriptingPython/translations/ScriptingPython_de_DE.ts new file mode 100644 index 0000000..5798d98 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_de_DE.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_el_GR.ts b/Plugins/ScriptingPython/translations/ScriptingPython_el_GR.ts new file mode 100644 index 0000000..369d7a2 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_el_GR.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_en_US.ts b/Plugins/ScriptingPython/translations/ScriptingPython_en_US.ts new file mode 100644 index 0000000..ebb0fbd --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_en_US.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_es_ES.ts b/Plugins/ScriptingPython/translations/ScriptingPython_es_ES.ts new file mode 100644 index 0000000..26d0df2 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_es_ES.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_fa_IR.ts b/Plugins/ScriptingPython/translations/ScriptingPython_fa_IR.ts new file mode 100644 index 0000000..0cb1654 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_fa_IR.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_fi_FI.ts b/Plugins/ScriptingPython/translations/ScriptingPython_fi_FI.ts new file mode 100644 index 0000000..c8a3691 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_fi_FI.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_fr_FR.ts b/Plugins/ScriptingPython/translations/ScriptingPython_fr_FR.ts new file mode 100644 index 0000000..2bf1d89 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_fr_FR.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Utilisation non valide de la fonction %1. Attendait %2 arguments, mais a obtenu %3. + + + + Unknown error from function %1. + Erreur inconnue de la fonction %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Impossible de calculer la représentation de la chaîne de caractères de l'objet Python passé comme argument à la fonction %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Impossible de trouver le contexte d'exécution de la fonction %1. Ceci est un bug du plugin Python. Veuillez le signaler. + + + + Error from Python function %1: %2 + Erreur de la fonction Python %1 : %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_he_IL.ts b/Plugins/ScriptingPython/translations/ScriptingPython_he_IL.ts new file mode 100644 index 0000000..a6a6fd7 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_he_IL.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + שימוש ×œ× ×ª×§×™×Ÿ בפונקציה %1. הציפיה הייתה לקבלת משתנה %2, ×ך נתקבל %3. + + + + Unknown error from function %1. + שגי××” ×œ× ×™×“×•×¢×” מפונקציה %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + ×œ× × ×™×ª×Ÿ ×”×™×” לחשב מחרוזות המייצגת ×ת ×ובייקט פייתון שהועבר כמשתנה לפונקציה %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + ×œ× × ×™×ª×Ÿ ×œ×ž×¦×•× ×”×§×©×¨ הפעלת פונקציה %1. זהו תקל במתקע פייתון. × × ×œ×“×•×•×— על כך. + + + + Error from Python function %1: %2 + שגי×ת פונקציית פייתון %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_hu_HU.ts b/Plugins/ScriptingPython/translations/ScriptingPython_hu_HU.ts new file mode 100644 index 0000000..e7b147d --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_hu_HU.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_it_IT.ts b/Plugins/ScriptingPython/translations/ScriptingPython_it_IT.ts new file mode 100644 index 0000000..c56d38c --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_it_IT.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Uso non valido della funzione %1. Atteso %2 argomenti, ma ottenuto %3. + + + + Unknown error from function %1. + Errore sconosciuto dalla funzione %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Impossibile calcolare la rappresentazione della stringa dell'oggetto Python passato come argomento alla funzione %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Impossibile trovare il contesto di esecuzione per la funzione %1. Questo è un bug del plugin Python. Si prega di segnalarlo. + + + + Error from Python function %1: %2 + Errore dalla funzione Python %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_ja_JP.ts b/Plugins/ScriptingPython/translations/ScriptingPython_ja_JP.ts new file mode 100644 index 0000000..a0487de --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_ja_JP.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_kaa.ts b/Plugins/ScriptingPython/translations/ScriptingPython_kaa.ts new file mode 100644 index 0000000..77cf961 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_kaa.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_ko_KR.ts b/Plugins/ScriptingPython/translations/ScriptingPython_ko_KR.ts new file mode 100644 index 0000000..caabe75 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_ko_KR.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_nl_NL.ts b/Plugins/ScriptingPython/translations/ScriptingPython_nl_NL.ts new file mode 100644 index 0000000..9e43453 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_nl_NL.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_no_NO.ts b/Plugins/ScriptingPython/translations/ScriptingPython_no_NO.ts new file mode 100644 index 0000000..cb16c40 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_no_NO.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_pl_PL.ts b/Plugins/ScriptingPython/translations/ScriptingPython_pl_PL.ts new file mode 100644 index 0000000..222819c --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_pl_PL.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + NieprawidÅ‚owe użycie funkcji %1. Oczekiwano %2 argumentów, ale otrzymano %3. + + + + Unknown error from function %1. + Nieznany błąd z funkcji %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Nie można przedstawić obiektu Pythona przekazanego jako argument do funkcji %1 jako reprezentacji ciÄ…gu znaków. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Nie można odnaleźć kontekstu wykonania funkcji %1. Jest to błąd wtyczki Pythona. ProszÄ™, zgÅ‚oÅ› to. + + + + Error from Python function %1: %2 + Błąd z funkcji Pythona %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_pt_BR.ts b/Plugins/ScriptingPython/translations/ScriptingPython_pt_BR.ts new file mode 100644 index 0000000..cc25e10 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_pt_BR.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Uso inválido de %1 função. Esperado %2 argumentos, mas recebeu %3. + + + + Unknown error from function %1. + Erro desconhecido da função %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Não foi possível calcular a representação de cadeia de caracteres do objeto Python passado como argumento para a função %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Não foi possível encontrar o contexto de execução para a função %1. Este é um erro do plugin Python. Por favor, relate o problema. + + + + Error from Python function %1: %2 + Erro da função Python %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_pt_PT.ts b/Plugins/ScriptingPython/translations/ScriptingPython_pt_PT.ts new file mode 100644 index 0000000..02fc86c --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_pt_PT.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_ro_RO.ts b/Plugins/ScriptingPython/translations/ScriptingPython_ro_RO.ts new file mode 100644 index 0000000..ae0dca8 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_ro_RO.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_ru_RU.ts b/Plugins/ScriptingPython/translations/ScriptingPython_ru_RU.ts new file mode 100644 index 0000000..6994f5a --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_ru_RU.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Ðекорректное иÑпользование функции %1. ОжидаетÑÑ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð¾Ð²: %2, передано аргументов: %3. + + + + Unknown error from function %1. + ÐеизвеÑÑ‚Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° при выполнении функции %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Ðевозможно Ñформировать Ñтроковое предÑтавление объекта Python, переданного в виде аргумента в функцию %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Ðе найден контекÑÑ‚ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ %1. Это ошибка Ð¼Ð¾Ð´ÑƒÐ»Ñ Python. ПожалуйÑта, Ñообщите о ней. + + + + Error from Python function %1: %2 + Ошибка при выполнении функции Python %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_sk_SK.ts b/Plugins/ScriptingPython/translations/ScriptingPython_sk_SK.ts new file mode 100644 index 0000000..73ca8fb --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_sk_SK.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_sr_SP.ts b/Plugins/ScriptingPython/translations/ScriptingPython_sr_SP.ts new file mode 100644 index 0000000..dce058c --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_sr_SP.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_sv_SE.ts b/Plugins/ScriptingPython/translations/ScriptingPython_sv_SE.ts new file mode 100644 index 0000000..0808c90 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_sv_SE.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_tr_TR.ts b/Plugins/ScriptingPython/translations/ScriptingPython_tr_TR.ts new file mode 100644 index 0000000..6d3c907 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_tr_TR.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + %1 iÅŸlevinin geçersiz kullanımı. Beklenen %2 argümanları, ancak %3 var. + + + + Unknown error from function %1. + %1 fonksiyonundan bilinmeyen hata. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + %1 iÅŸlevine bağımsız deÄŸiÅŸken olarak geçirilen Python nesnesinin dize gösterimi hesaplanamadı. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + %1 iÅŸlevinin yürütme içeriÄŸi bulunamadı. Bu bir Python eklentisi hatasıdır. Lütfen rapor edin. + + + + Error from Python function %1: %2 + Python iÅŸlevinden %1 hatası: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_uk_UA.ts b/Plugins/ScriptingPython/translations/ScriptingPython_uk_UA.ts new file mode 100644 index 0000000..5c1c035 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_uk_UA.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_vi_VN.ts b/Plugins/ScriptingPython/translations/ScriptingPython_vi_VN.ts new file mode 100644 index 0000000..8a12cd0 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_vi_VN.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + Invalid use of %1 function. Expected %2 arguments, but got %3. + + + + Unknown error from function %1. + Unknown error from function %1. + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + Could not calculate string representation of the Python object passed as argument to the function %1. + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + + + + Error from Python function %1: %2 + Error from Python function %1: %2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_zh_CN.ts b/Plugins/ScriptingPython/translations/ScriptingPython_zh_CN.ts new file mode 100644 index 0000000..c9923a0 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_zh_CN.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + 无效的 %1 函数用法。预期是 %2 傿•°ï¼Œä½†å®žé™…是 %3。 + + + + Unknown error from function %1. + 函数 %1 的未知错误。 + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + æ— æ³•è®¡ç®—ä½œä¸ºå‚æ•°ä¼ é€’ç»™ %1 函数的 Python 对象所表示的字符串。 + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + 找ä¸åˆ°å‡½æ•° %1 的执行环境。这是 Python æ’ä»¶çš„ bug,请报告该问题。 + + + + Error from Python function %1: %2 + Python 函数 %1 错误:%2 + + + diff --git a/Plugins/ScriptingPython/translations/ScriptingPython_zh_TW.ts b/Plugins/ScriptingPython/translations/ScriptingPython_zh_TW.ts new file mode 100644 index 0000000..d8b9b89 --- /dev/null +++ b/Plugins/ScriptingPython/translations/ScriptingPython_zh_TW.ts @@ -0,0 +1,32 @@ + + + + + QObject + + + Invalid use of %1 function. Expected %2 arguments, but got %3. + 無效的 %1 函å¼ç”¨æ³•。期望 %2 個引數,得到 %3。 + + + + Unknown error from function %1. + å‡½å¼ %1 的未知錯誤。 + + + + Could not calculate string representation of the Python object passed as argument to the function %1. + 無法計算作為引數傳éžçµ¦å‡½å¼ %1 çš„ Python 物件所表示的字串。 + + + + Could not find execution context for function %1. This is a bug of Python plugin. Please report it. + 找ä¸åˆ°å‡½å¼ %1 的執行環境。這是 Python 外掛的 bug,請報告該å•題。 + + + + Error from Python function %1: %2 + Python å‡½å¼ %1 錯誤:%2 + + + diff --git a/Plugins/ScriptingTcl/ScriptingTcl.pro b/Plugins/ScriptingTcl/ScriptingTcl.pro index 85d7740..80a11fe 100644 --- a/Plugins/ScriptingTcl/ScriptingTcl.pro +++ b/Plugins/ScriptingTcl/ScriptingTcl.pro @@ -42,9 +42,8 @@ linux: { message("Looking for $$TCL_CONFIG") } !exists($$TCL_CONFIG) { - # Debian case - DEBIAN_ARCH_PATH=$$system(dpkg-architecture -qDEB_HOST_MULTIARCH) - TCL_CONFIG = /usr/lib/$$DEBIAN_ARCH_PATH/tcl$$TCL_VERSION/tclConfig.sh + # Debian, FreeBSD, Ubuntu Bionic case + TCL_CONFIG = $$system(echo "puts [::tcl::pkgconfig get libdir,runtime]" | tclsh)/tcl$$TCL_VERSION/tclConfig.sh } message("Looking for $$TCL_CONFIG") !exists($$TCL_CONFIG) { @@ -81,27 +80,18 @@ linux: { macx: { # Find tclsh - #TCLSH = $$system(echo "puts 1" | tclsh) - #!contains(TCLSH, 1): { - # error("Could not find tclsh executable. ScriptingTcl plugin requires it to find out all Tcl libraries and headers. Make tclsh available in PATH.") - #} - #TCLSH = $$system(which tclsh) + TCLSH = $$system(echo "puts 1" | tclsh) + !contains(TCLSH, 1): { + error("Could not find tclsh executable. ScriptingTcl plugin requires it to find out all Tcl libraries and headers. Make tclsh available in PATH.") + } + TCLSH = $$system(which tclsh) # Find its version - #TCL_VERSION = $$system(echo "puts [info tclversion]" | tclsh) - #message("Found tclsh: $$TCLSH (version: $$TCL_VERSION)") + TCL_VERSION = $$system(echo "puts [info tclversion]" | tclsh) + message("Found tclsh: $$TCLSH (version: $$TCL_VERSION)") # Find tclConfig.sh - #TCL_CONFIG_DIR = $$system(echo "puts [info library]" | tclsh) - #TCL_CONFIG = $$TCL_CONFIG_DIR/../../tclConfig.sh - - XCRUN = $$system(xcrun --version) - !contains(XCRUN, xcrun): { - error("Could not find xcrun executable. ScriptingTcl plugin requires it to find out all Tcl libraries and headers.") - } - - SDK_PATH = $$system(xcrun --show-sdk-path) - TCL_CONFIG = $$SDK_PATH/System/Library/Frameworks/Tcl.framework/Versions/Current/tclConfig.sh + TCL_CONFIG = $$system(echo "puts [::tcl::pkgconfig get libdir,runtime]" | tclsh)/tclConfig.sh # Define other libs required when linking with Tcl eval($$system(cat $$TCL_CONFIG | grep TCL_LIBS)) @@ -148,31 +138,3 @@ win32: { RESOURCES += \ scriptingtcl.qrc - - -TRANSLATIONS += ScriptingTcl_ro_RO.ts \ - ScriptingTcl_de.ts \ - ScriptingTcl_it.ts \ - ScriptingTcl_zh_CN.ts \ - ScriptingTcl_sk.ts \ - ScriptingTcl_ru.ts \ - ScriptingTcl_pt_BR.ts \ - ScriptingTcl_fr.ts \ - ScriptingTcl_es.ts \ - ScriptingTcl_pl.ts - - - - - - - - - - - - - - - - diff --git a/Plugins/ScriptingTcl/ScriptingTcl_de.qm b/Plugins/ScriptingTcl/ScriptingTcl_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/ScriptingTcl/ScriptingTcl_de.qm and /dev/null differ diff --git a/Plugins/ScriptingTcl/ScriptingTcl_de.ts b/Plugins/ScriptingTcl/ScriptingTcl_de.ts deleted file mode 100644 index 4433954..0000000 --- a/Plugins/ScriptingTcl/ScriptingTcl_de.ts +++ /dev/null @@ -1,23 +0,0 @@ - - - - - ScriptingTcl - - - No database available in current context, while called Tcl's '%1' command. - - - - - Invalid '%1' command sytax. Should be: %2 - - - - - - Error from Tcl's' '%1' command: %2 - - - - diff --git a/Plugins/ScriptingTcl/ScriptingTcl_es.qm b/Plugins/ScriptingTcl/ScriptingTcl_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/ScriptingTcl/ScriptingTcl_es.qm and /dev/null differ diff --git a/Plugins/ScriptingTcl/ScriptingTcl_es.ts b/Plugins/ScriptingTcl/ScriptingTcl_es.ts deleted file mode 100644 index c1244be..0000000 --- a/Plugins/ScriptingTcl/ScriptingTcl_es.ts +++ /dev/null @@ -1,23 +0,0 @@ - - - - - ScriptingTcl - - - No database available in current context, while called Tcl's '%1' command. - - - - - Invalid '%1' command sytax. Should be: %2 - - - - - - Error from Tcl's' '%1' command: %2 - - - - diff --git a/Plugins/ScriptingTcl/ScriptingTcl_fr.qm b/Plugins/ScriptingTcl/ScriptingTcl_fr.qm deleted file mode 100644 index f172932..0000000 Binary files a/Plugins/ScriptingTcl/ScriptingTcl_fr.qm and /dev/null differ diff --git a/Plugins/ScriptingTcl/ScriptingTcl_fr.ts b/Plugins/ScriptingTcl/ScriptingTcl_fr.ts deleted file mode 100644 index a810f23..0000000 --- a/Plugins/ScriptingTcl/ScriptingTcl_fr.ts +++ /dev/null @@ -1,23 +0,0 @@ - - - - - ScriptingTcl - - - No database available in current context, while called Tcl's '%1' command. - Aucune base de données valide dans le contexte courant, appeler la commande Tcl’s « %1 ». - - - - Invalid '%1' command sytax. Should be: %2 - Syntaxe de commande invalide « %1 ». Devrait être : %2 - - - - - Error from Tcl's' '%1' command: %2 - Erreur de la commande Tcl « %1 » : %2 - - - diff --git a/Plugins/ScriptingTcl/ScriptingTcl_it.qm b/Plugins/ScriptingTcl/ScriptingTcl_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/ScriptingTcl/ScriptingTcl_it.qm and /dev/null differ diff --git a/Plugins/ScriptingTcl/ScriptingTcl_it.ts b/Plugins/ScriptingTcl/ScriptingTcl_it.ts deleted file mode 100644 index ae772c8..0000000 --- a/Plugins/ScriptingTcl/ScriptingTcl_it.ts +++ /dev/null @@ -1,23 +0,0 @@ - - - - - ScriptingTcl - - - No database available in current context, while called Tcl's '%1' command. - - - - - Invalid '%1' command sytax. Should be: %2 - - - - - - Error from Tcl's' '%1' command: %2 - - - - diff --git a/Plugins/ScriptingTcl/ScriptingTcl_pl.qm b/Plugins/ScriptingTcl/ScriptingTcl_pl.qm deleted file mode 100644 index 1232971..0000000 Binary files a/Plugins/ScriptingTcl/ScriptingTcl_pl.qm and /dev/null differ diff --git a/Plugins/ScriptingTcl/ScriptingTcl_pl.ts b/Plugins/ScriptingTcl/ScriptingTcl_pl.ts deleted file mode 100644 index 7766394..0000000 --- a/Plugins/ScriptingTcl/ScriptingTcl_pl.ts +++ /dev/null @@ -1,26 +0,0 @@ - - - - - ScriptingTcl - - - No database available in current context, while called Tcl's '%1' command. - No database available in current context, while called Tcl's 'db' command. - Brak dostÄ™pnej bazy w bieżącym kontekÅ›cie, podczas wywoÅ‚ania polecenie Tcl '%1'. - - - - Invalid '%1' command sytax. Should be: %2 - Invalid 'db' command sytax. Should be: db eval sql - Niepoprawna skÅ‚adnia polecenia '%1'. Powinno być: %2 - - - - - Error from Tcl's' '%1' command: %2 - Error from Tcl's' 'db' command: %1 - WystÄ…piÅ‚ błąd w poleceniu Tcl '%1': %2 - - - diff --git a/Plugins/ScriptingTcl/ScriptingTcl_pt_BR.qm b/Plugins/ScriptingTcl/ScriptingTcl_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/ScriptingTcl/ScriptingTcl_pt_BR.qm and /dev/null differ diff --git a/Plugins/ScriptingTcl/ScriptingTcl_pt_BR.ts b/Plugins/ScriptingTcl/ScriptingTcl_pt_BR.ts deleted file mode 100644 index 513e55b..0000000 --- a/Plugins/ScriptingTcl/ScriptingTcl_pt_BR.ts +++ /dev/null @@ -1,23 +0,0 @@ - - - - - ScriptingTcl - - - No database available in current context, while called Tcl's '%1' command. - - - - - Invalid '%1' command sytax. Should be: %2 - - - - - - Error from Tcl's' '%1' command: %2 - - - - diff --git a/Plugins/ScriptingTcl/ScriptingTcl_ro_RO.qm b/Plugins/ScriptingTcl/ScriptingTcl_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/ScriptingTcl/ScriptingTcl_ro_RO.qm and /dev/null differ diff --git a/Plugins/ScriptingTcl/ScriptingTcl_ro_RO.ts b/Plugins/ScriptingTcl/ScriptingTcl_ro_RO.ts deleted file mode 100644 index 5396db4..0000000 --- a/Plugins/ScriptingTcl/ScriptingTcl_ro_RO.ts +++ /dev/null @@ -1,23 +0,0 @@ - - - - - ScriptingTcl - - - No database available in current context, while called Tcl's '%1' command. - - - - - Invalid '%1' command sytax. Should be: %2 - - - - - - Error from Tcl's' '%1' command: %2 - - - - diff --git a/Plugins/ScriptingTcl/ScriptingTcl_ru.qm b/Plugins/ScriptingTcl/ScriptingTcl_ru.qm deleted file mode 100644 index df87278..0000000 Binary files a/Plugins/ScriptingTcl/ScriptingTcl_ru.qm and /dev/null differ diff --git a/Plugins/ScriptingTcl/ScriptingTcl_ru.ts b/Plugins/ScriptingTcl/ScriptingTcl_ru.ts deleted file mode 100644 index 7a2c2d9..0000000 --- a/Plugins/ScriptingTcl/ScriptingTcl_ru.ts +++ /dev/null @@ -1,23 +0,0 @@ - - - - - ScriptingTcl - - - No database available in current context, while called Tcl's '%1' command. - При вызове команды Tcl %1 в текущем контекÑте нет доÑтупных баз данных. - - - - Invalid '%1' command sytax. Should be: %2 - Ðекорректный ÑинтакÑÐ¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹ '%1'. Должно быть: %2 - - - - - Error from Tcl's' '%1' command: %2 - Ошибка в команде Tcl '%1': %2 - - - diff --git a/Plugins/ScriptingTcl/ScriptingTcl_sk.qm b/Plugins/ScriptingTcl/ScriptingTcl_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/Plugins/ScriptingTcl/ScriptingTcl_sk.qm and /dev/null differ diff --git a/Plugins/ScriptingTcl/ScriptingTcl_sk.ts b/Plugins/ScriptingTcl/ScriptingTcl_sk.ts deleted file mode 100644 index 6e9733e..0000000 --- a/Plugins/ScriptingTcl/ScriptingTcl_sk.ts +++ /dev/null @@ -1,23 +0,0 @@ - - - - - ScriptingTcl - - - No database available in current context, while called Tcl's '%1' command. - - - - - Invalid '%1' command sytax. Should be: %2 - - - - - - Error from Tcl's' '%1' command: %2 - - - - diff --git a/Plugins/ScriptingTcl/ScriptingTcl_zh_CN.qm b/Plugins/ScriptingTcl/ScriptingTcl_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/Plugins/ScriptingTcl/ScriptingTcl_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/Plugins/ScriptingTcl/ScriptingTcl_zh_CN.ts b/Plugins/ScriptingTcl/ScriptingTcl_zh_CN.ts deleted file mode 100644 index 8c120c9..0000000 --- a/Plugins/ScriptingTcl/ScriptingTcl_zh_CN.ts +++ /dev/null @@ -1,23 +0,0 @@ - - - - - ScriptingTcl - - - No database available in current context, while called Tcl's '%1' command. - - - - - Invalid '%1' command sytax. Should be: %2 - - - - - - Error from Tcl's' '%1' command: %2 - - - - diff --git a/Plugins/ScriptingTcl/scriptingtcl.cpp b/Plugins/ScriptingTcl/scriptingtcl.cpp index cc48aa0..2754afe 100644 --- a/Plugins/ScriptingTcl/scriptingtcl.cpp +++ b/Plugins/ScriptingTcl/scriptingtcl.cpp @@ -20,7 +20,7 @@ ScriptingTcl::~ScriptingTcl() bool ScriptingTcl::init() { - Q_INIT_RESOURCE(scriptingtcl); + SQLS_INIT_RESOURCE(scriptingtcl); QMutexLocker locker(mainInterpMutex); mainContext = new ContextTcl(); return true; @@ -31,7 +31,7 @@ void ScriptingTcl::deinit() QMutexLocker locker(mainInterpMutex); safe_delete(mainContext); Tcl_Finalize(); - Q_CLEANUP_RESOURCE(scriptingtcl); + SQLS_CLEANUP_RESOURCE(scriptingtcl); } QString ScriptingTcl::getLanguage() const @@ -106,21 +106,21 @@ QString ScriptingTcl::getIconPath() const return ":/scriptingtcl/scriptingtcl.png"; } -QVariant ScriptingTcl::evaluate(ScriptingPlugin::Context* context, const QString& code, const QList& args, Db* db, bool locking) +QVariant ScriptingTcl::evaluate(ScriptingPlugin::Context* context, const QString& code, const FunctionInfo& funcInfo, + const QList& args, Db* db, bool locking) { ContextTcl* ctx = getContext(context); if (!ctx) return QVariant(); - setArgs(ctx, args); - return compileAndEval(ctx, code, db, locking); + return compileAndEval(ctx, code, funcInfo, args, db, locking); } -QVariant ScriptingTcl::evaluate(const QString& code, const QList& args, Db* db, bool locking, QString* errorMessage) +QVariant ScriptingTcl::evaluate(const QString& code, const FunctionInfo& funcInfo, const QList& args, + Db* db, bool locking, QString* errorMessage) { QMutexLocker locker(mainInterpMutex); - setArgs(mainContext, args); - QVariant results = compileAndEval(mainContext, code, db, locking); + QVariant results = compileAndEval(mainContext, code, funcInfo, args, db, locking); if (errorMessage && !mainContext->error.isEmpty()) *errorMessage = mainContext->error; @@ -137,21 +137,25 @@ ScriptingTcl::ContextTcl* ScriptingTcl::getContext(ScriptingPlugin::Context* con return ctx; } -QVariant ScriptingTcl::compileAndEval(ScriptingTcl::ContextTcl* ctx, const QString& code, Db* db, bool locking) +QVariant ScriptingTcl::compileAndEval(ScriptingTcl::ContextTcl* ctx, const QString& code, const FunctionInfo& funcInfo, + const QList& args, Db* db, bool locking) { - ScriptObject* scriptObj = nullptr; - if (!ctx->scriptCache.contains(code)) - { - scriptObj = new ScriptObject(code); - ctx->scriptCache.insert(code, scriptObj); - } - else - { - scriptObj = ctx->scriptCache[code]; - } + ScriptObject* scriptObj = getScript(code, funcInfo, ctx); + Tcl_ResetResult(ctx->interp); ctx->error.clear(); + setArgs(ctx, args); + + int i = 0; + for (const QString& key : funcInfo.getArguments()) + { + if (i >= args.size()) + break; + + setVariable(ctx, key, args[i++]); + } + ctx->db = db; ctx->useDbLocking = locking; @@ -180,6 +184,19 @@ void ScriptingTcl::setArgs(ScriptingTcl::ContextTcl* ctx, const QList& setVariable(ctx, "argv", args); } +ScriptingTcl::ScriptObject* ScriptingTcl::getScript(const QString code, const ScriptingPlugin::FunctionInfo& funcInfo, ContextTcl* ctx) +{ + static const QString keyTpl = QStringLiteral("{%1} %2"); + + QString key = keyTpl.arg(funcInfo.getArguments().join(" "), code); + if (ctx->scriptCache.contains(key)) + return ctx->scriptCache[key]; + + ScriptObject* scriptObj = new ScriptObject(code); + ctx->scriptCache.insert(key, scriptObj); + return scriptObj; +} + Tcl_Obj* ScriptingTcl::argsToList(const QList& args) { Tcl_Obj** objArray = new Tcl_Obj*[args.size()]; @@ -431,7 +448,7 @@ int ScriptingTcl::dbCommand(ClientData clientData, Tcl_Interp* interp, int objc, return dbEvalOneColumn(ctx, interp, objv); } - result = Tcl_NewStringObj(tr("Invalid '%1' command sytax. Should be: %2").arg("db", "db eval sql").toUtf8().constData(), -1); + result = Tcl_NewStringObj(tr("Invalid '%1' command syntax. Should be: %2").arg("db", "db eval sql").toUtf8().constData(), -1); Tcl_SetObjResult(interp, result); return TCL_ERROR; } @@ -443,7 +460,7 @@ int ScriptingTcl::initTclCommand(ClientData clientData, Tcl_Interp* interp, int if (objc > 1) { - Tcl_Obj* result = Tcl_NewStringObj(tr("Error from Tcl's' '%1' command: %2").arg("tcl_init", "invalid # args: tcl_init").toUtf8().constData(), -1); + Tcl_Obj* result = Tcl_NewStringObj(tr("Error from Tcl's '%1' command: %2").arg("tcl_init", "invalid # args: tcl_init").toUtf8().constData(), -1); Tcl_SetObjResult(interp, result); return TCL_ERROR; } diff --git a/Plugins/ScriptingTcl/scriptingtcl.h b/Plugins/ScriptingTcl/scriptingtcl.h index f853f07..eb81540 100644 --- a/Plugins/ScriptingTcl/scriptingtcl.h +++ b/Plugins/ScriptingTcl/scriptingtcl.h @@ -32,8 +32,10 @@ class SCRIPTINGTCLSHARED_EXPORT ScriptingTcl : public GenericPlugin, public DbAw bool hasError(Context* context) const; QString getErrorMessage(Context* context) const; QString getIconPath() const; - QVariant evaluate(Context* context, const QString& code, const QList& args, Db* db, bool locking = false); - QVariant evaluate(const QString& code, const QList& args, Db* db, bool locking = false, QString* errorMessage = nullptr); + QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, + const QList& args, Db* db, bool locking = false); + QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, const QList& args, + Db* db, bool locking = false, QString* errorMessage = nullptr); private: class ScriptObject @@ -82,9 +84,10 @@ class SCRIPTINGTCLSHARED_EXPORT ScriptingTcl : public GenericPlugin, public DbAw }; ContextTcl* getContext(ScriptingPlugin::Context* context) const; - QVariant compileAndEval(ContextTcl* ctx, const QString& code, Db* db, bool locking); + QVariant compileAndEval(ContextTcl* ctx, const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking); QVariant extractResult(ContextTcl* ctx); void setArgs(ContextTcl* ctx, const QList& args); + ScriptObject* getScript(const QString code, const FunctionInfo& funcInfo, ContextTcl* ctx); static Tcl_Obj* argsToList(const QList& args); static QVariant tclObjToVariant(Tcl_Obj* obj); diff --git a/Plugins/ScriptingTcl/scriptingtcl.qrc b/Plugins/ScriptingTcl/scriptingtcl.qrc index f0da6f5..8a0d047 100644 --- a/Plugins/ScriptingTcl/scriptingtcl.qrc +++ b/Plugins/ScriptingTcl/scriptingtcl.qrc @@ -2,19 +2,4 @@ scriptingtcl.png - - ScriptingTcl_ro_RO.qm - ScriptingTcl_de.qm - - - ScriptingTcl_pl.qm - ScriptingTcl_ru.qm - ScriptingTcl_fr.qm - ScriptingTcl_sk.qm - ScriptingTcl_zh_CN.qm - - - - - diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl.ts new file mode 100644 index 0000000..2b6df14 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + + + + + Invalid '%1' command syntax. Should be: %2 + + + + + Error from Tcl's '%1' command: %2 + + + + + Error from Tcl's' '%1' command: %2 + + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_af_ZA.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_af_ZA.ts new file mode 100644 index 0000000..0c2a7fc --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_af_ZA.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_ar_SA.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_ar_SA.ts new file mode 100644 index 0000000..6ebc14c --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_ar_SA.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_ca_ES.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_ca_ES.ts new file mode 100644 index 0000000..1df64bc --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_ca_ES.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_cs_CZ.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_cs_CZ.ts new file mode 100644 index 0000000..c410384 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_cs_CZ.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_da_DK.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_da_DK.ts new file mode 100644 index 0000000..1b8f5bf --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_da_DK.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_de_DE.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_de_DE.ts new file mode 100644 index 0000000..7cb0a8b --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_de_DE.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_el_GR.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_el_GR.ts new file mode 100644 index 0000000..f218ab3 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_el_GR.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_en_US.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_en_US.ts new file mode 100644 index 0000000..afd699a --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_en_US.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_es_ES.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_es_ES.ts new file mode 100644 index 0000000..6f043cf --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_es_ES.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No hay una base de datos disponible en el contexto actual mientras se llamaba al comando de Tcl's '%1'. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error del comando de Tcl's' '%1': %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_fa_IR.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_fa_IR.ts new file mode 100644 index 0000000..78e47d6 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_fa_IR.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_fi_FI.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_fi_FI.ts new file mode 100644 index 0000000..dc8df47 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_fi_FI.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_fr_FR.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_fr_FR.ts new file mode 100644 index 0000000..e963cae --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_fr_FR.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + Aucune base de données disponible dans le contexte actuel, tant que TCL '%1' est utilisé. + + + + Invalid '%1' command syntax. Should be: %2 + Syntaxe de commande '%1' invalide. Devrait être : %2 + + + + Error from Tcl's '%1' command: %2 + Erreur de TCL '%1' commande : %2 + + + + Error from Tcl's' '%1' command: %2 + Erreur de TCL '%1' commande : %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_he_IL.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_he_IL.ts new file mode 100644 index 0000000..4d78b02 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_he_IL.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + ×ין מסד × ×ª×•× ×™× ×–×ž×™×Ÿ בהקשר נוכחי, בקרי××” לפקודת Tcl '%1'. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + שגי××” מפקודת Tcl'''%1': %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_hu_HU.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_hu_HU.ts new file mode 100644 index 0000000..0a84624 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_hu_HU.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_it_IT.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_it_IT.ts new file mode 100644 index 0000000..d48fbc7 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_it_IT.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + Nessun database disponibile nel contesto attuale, mentre si richiamava il comando QtScript %1. + + + + Invalid '%1' command syntax. Should be: %2 + Sintassi del comando non valida '%1' dovrebbe essere: %2 + + + + Error from Tcl's '%1' command: %2 + Errore da Tcls' '%1' comando: %2 + + + + Error from Tcl's' '%1' command: %2 + Errore da Tcl's' '%1' comando: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_ja_JP.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_ja_JP.ts new file mode 100644 index 0000000..79d7062 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_ja_JP.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_kaa.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_kaa.ts new file mode 100644 index 0000000..f3230e1 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_kaa.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_ko_KR.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_ko_KR.ts new file mode 100644 index 0000000..49f4df9 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_ko_KR.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_nl_NL.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_nl_NL.ts new file mode 100644 index 0000000..920739f --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_nl_NL.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_no_NO.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_no_NO.ts new file mode 100644 index 0000000..4af53f3 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_no_NO.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_pl_PL.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_pl_PL.ts new file mode 100644 index 0000000..0fb9377 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_pl_PL.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + Brak dostÄ™pnej bazy w bieżącym kontekÅ›cie, podczas wywoÅ‚ania polecenie Tcl '%1'. + + + + Invalid '%1' command syntax. Should be: %2 + Niepoprawna skÅ‚adnia polecenia '%1'. Powinno być: %2 + + + + Error from Tcl's '%1' command: %2 + WystÄ…piÅ‚ błąd w poleceniu Tcl '%1': %2 + + + + Error from Tcl's' '%1' command: %2 + WystÄ…piÅ‚ błąd w poleceniu Tcl '%1': %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_pt_BR.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_pt_BR.ts new file mode 100644 index 0000000..59d495c --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_pt_BR.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + Nenhum banco de dados disponível no contexto atual, enquanto chamado comando Tcl 's '%1' + + + + Invalid '%1' command syntax. Should be: %2 + Síntaxe inválida do comando '%1'. Tente: %2 + + + + Error from Tcl's '%1' command: %2 + Erro Tcl %1' comando: %2 + + + + Error from Tcl's' '%1' command: %2 + Erro de Tcl 's '%1' comando: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_pt_PT.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_pt_PT.ts new file mode 100644 index 0000000..6141c94 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_pt_PT.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_ro_RO.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_ro_RO.ts new file mode 100644 index 0000000..343f6dc --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_ro_RO.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_ru_RU.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_ru_RU.ts new file mode 100644 index 0000000..3eaf3df --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_ru_RU.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + При вызове команды Tcl %1 в текущем контекÑте нет доÑтупных баз данных. + + + + Invalid '%1' command syntax. Should be: %2 + Ðекорректный ÑинтакÑÐ¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹ '%1'. Должно быть: %2 + + + + Error from Tcl's '%1' command: %2 + Ошибка в команде Tcl '%1': %2 + + + + Error from Tcl's' '%1' command: %2 + Ошибка в команде Tcl '%1': %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_sk_SK.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_sk_SK.ts new file mode 100644 index 0000000..6899532 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_sk_SK.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_sr_SP.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_sr_SP.ts new file mode 100644 index 0000000..b03f665 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_sr_SP.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_sv_SE.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_sv_SE.ts new file mode 100644 index 0000000..4c3357a --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_sv_SE.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_tr_TR.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_tr_TR.ts new file mode 100644 index 0000000..7e5d4ba --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_tr_TR.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + Tcl's '%1' komutu sonucunda, uygun bir veritabanı bulunmuyor. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Tcl's' '%1'komutunda hata komut: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_uk_UA.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_uk_UA.ts new file mode 100644 index 0000000..11c4085 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_uk_UA.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_vi_VN.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_vi_VN.ts new file mode 100644 index 0000000..bf2c657 --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_vi_VN.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + No database available in current context, while called Tcl's '%1' command. + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Error from Tcl's' '%1' command: %2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_zh_CN.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_zh_CN.ts new file mode 100644 index 0000000..fd5f54c --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_zh_CN.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + 调用 Tcl çš„ '%1' 命令期间,当å‰ä¸Šä¸‹æ–‡æ²¡æœ‰å¯ç”¨çš„æ•°æ®åº“。 + + + + Invalid '%1' command syntax. Should be: %2 + 命令'%1' 语法错误,应改正为:%2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Tcl çš„ '%1' 命令错误:%2 + + + diff --git a/Plugins/ScriptingTcl/translations/ScriptingTcl_zh_TW.ts b/Plugins/ScriptingTcl/translations/ScriptingTcl_zh_TW.ts new file mode 100644 index 0000000..96e252c --- /dev/null +++ b/Plugins/ScriptingTcl/translations/ScriptingTcl_zh_TW.ts @@ -0,0 +1,27 @@ + + + + + ScriptingTcl + + + No database available in current context, while called Tcl's '%1' command. + å‘¼å« Tcl 命令 '%1' 期間,當å‰ä¸Šä¸‹æ–‡æ²’有å¯ç”¨çš„資料庫。 + + + + Invalid '%1' command syntax. Should be: %2 + Invalid '%1' command syntax. Should be: %2 + + + + Error from Tcl's '%1' command: %2 + Error from Tcl's '%1' command: %2 + + + + Error from Tcl's' '%1' command: %2 + Tcl 命令 '%1' 錯誤:%2 + + + diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter.pro b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter.pro index 0515768..3c19ae1 100644 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter.pro +++ b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter.pro @@ -33,7 +33,6 @@ SOURCES += sqlenterpriseformatter.cpp \ formatattach.cpp \ formatbegintrans.cpp \ formatcommittrans.cpp \ - formatcopy.cpp \ formatcreateindex.cpp \ formatcreatetrigger.cpp \ formatdelete.cpp \ @@ -75,7 +74,6 @@ HEADERS += sqlenterpriseformatter.h\ formatattach.h \ formatbegintrans.h \ formatcommittrans.h \ - formatcopy.h \ formatcreateindex.h \ formatcreatetrigger.h \ formatdelete.h \ @@ -106,16 +104,11 @@ RESOURCES += \ sqlenterpriseformatter.qrc -TRANSLATIONS += SqlEnterpriseFormatter_ro_RO.ts \ - SqlEnterpriseFormatter_de.ts \ - SqlEnterpriseFormatter_it.ts \ - SqlEnterpriseFormatter_zh_CN.ts \ - SqlEnterpriseFormatter_sk.ts \ - SqlEnterpriseFormatter_ru.ts \ - SqlEnterpriseFormatter_pt_BR.ts \ - SqlEnterpriseFormatter_fr.ts \ - SqlEnterpriseFormatter_es.ts \ - SqlEnterpriseFormatter_pl.ts + + + + + diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_de.qm b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_de.qm and /dev/null differ diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_de.ts b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_de.ts deleted file mode 100644 index 10e3b39..0000000 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_de.ts +++ /dev/null @@ -1,228 +0,0 @@ - - - - - QObject - - - - name - example name wrapper - - - - - SqlEnterpriseFormatter - - - Indentation - - - - - Line up keywords in multi-line queries - - - - - Indent contents of parenthesis block - - - - - Tab size: - - - - - New lines - - - - - Before opening parenthesis in column definitions - - - - - After opening parenthesis in column definitions - - - - - Before closing parenthesis in column definitions - - - - - After closing parenthesis in column definitions - - - - - Before opening parenthesis in expressions - - - - - After opening parenthesis in expressions - - - - - Before closing parenthesis in expressions - - - - - After closing parenthesis in expressions - - - - - After JOIN keywords in FROM clause - - - - - Put each column constraint in CREATE TABLE into new line - - - - - After comma - - - - - After comma in expressions - - - - - After semicolon - - - - - - Never before semicolon - - - - - White spaces - - - - - Before comma in lists - - - - - After comma in lists - - - - - Before opening parenthesis - - - - - After opening parenthesis - - - - - Before closing parenthesis - - - - - After closing parenthesis - - - - - No space between SQL function name and opening parenthesis - - - - - Before dot operator (in path to database object) - - - - - After dot operator (in path to database object) - - - - - Before mathematical operator - - - - - After mathematical operator - - - - - Never before comma - - - - - Names - - - - - Preferred name wrapper - - - - - Always use name wrapping - - - - - Uppercase data type names - - - - - Uppercase keywords - - - - - Comments - - - - - Preferred comment marker (where possible): - - - - - Move all comments to the line end - - - - - Line up comments at the line end - - - - - Preview - - - - diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_es.qm b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_es.qm and /dev/null differ diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_es.ts b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_es.ts deleted file mode 100644 index cd19bf1..0000000 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_es.ts +++ /dev/null @@ -1,228 +0,0 @@ - - - - - QObject - - - - name - example name wrapper - - - - - SqlEnterpriseFormatter - - - Indentation - - - - - Line up keywords in multi-line queries - - - - - Indent contents of parenthesis block - - - - - Tab size: - - - - - New lines - - - - - Before opening parenthesis in column definitions - - - - - After opening parenthesis in column definitions - - - - - Before closing parenthesis in column definitions - - - - - After closing parenthesis in column definitions - - - - - Before opening parenthesis in expressions - - - - - After opening parenthesis in expressions - - - - - Before closing parenthesis in expressions - - - - - After closing parenthesis in expressions - - - - - After JOIN keywords in FROM clause - - - - - Put each column constraint in CREATE TABLE into new line - - - - - After comma - - - - - After comma in expressions - - - - - After semicolon - - - - - - Never before semicolon - - - - - White spaces - - - - - Before comma in lists - - - - - After comma in lists - - - - - Before opening parenthesis - - - - - After opening parenthesis - - - - - Before closing parenthesis - - - - - After closing parenthesis - - - - - No space between SQL function name and opening parenthesis - - - - - Before dot operator (in path to database object) - - - - - After dot operator (in path to database object) - - - - - Before mathematical operator - - - - - After mathematical operator - - - - - Never before comma - - - - - Names - - - - - Preferred name wrapper - - - - - Always use name wrapping - - - - - Uppercase data type names - - - - - Uppercase keywords - - - - - Comments - - - - - Preferred comment marker (where possible): - - - - - Move all comments to the line end - - - - - Line up comments at the line end - - - - - Preview - - - - diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_fr.qm b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_fr.qm deleted file mode 100644 index 974d221..0000000 Binary files a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_fr.qm and /dev/null differ diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_fr.ts b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_fr.ts deleted file mode 100644 index 5468163..0000000 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_fr.ts +++ /dev/null @@ -1,228 +0,0 @@ - - - - - QObject - - - - name - example name wrapper - Nom - - - - SqlEnterpriseFormatter - - - Indentation - Identation - - - - Line up keywords in multi-line queries - Ligne de mots-clefs en majuscule dans une requête multiligne - - - - Indent contents of parenthesis block - Indenter le contenu du bloc entre parenthèses - - - - Tab size: - taille d’indentation : - - - - New lines - Nouvelles lignes - - - - Before opening parenthesis in column definitions - Avant l’ouverture des parenthèses dans la définition de la colonne - - - - After opening parenthesis in column definitions - Après l’ouverture des parenthèses dans la définition de la colonne - - - - Before closing parenthesis in column definitions - Avant la fermeture des parenthèses dans la définition de la colonne - - - - After closing parenthesis in column definitions - Après la fermeture des parenthèses dans la définition de la colonne - - - - Before opening parenthesis in expressions - Avant l’ouverture des parenthèses dans l’expression - - - - After opening parenthesis in expressions - Après l’ouverture des parenthèses dans l’expression - - - - Before closing parenthesis in expressions - Avant la fermeture des parenthèses dans l’expression - - - - After closing parenthesis in expressions - Après la fermeture des parenthèses dans l’expression - - - - After JOIN keywords in FROM clause - Après le mot-clef JOIN dans la clause FROM - - - - Put each column constraint in CREATE TABLE into new line - Mettez chaque contrainte de colonne dans CREATE TABLE à une nouvelleligne - - - - After comma - Après virgule - - - - After comma in expressions - Après la virgule dans l’exoression - - - - After semicolon - Après un point virgule - - - - - Never before semicolon - Jamais avant un point virgule - - - - White spaces - Espaces - - - - Before comma in lists - Après virgule dans la liste - - - - After comma in lists - Avant virgule dans la liste - - - - Before opening parenthesis - Avant l’ouverture des parenthèses - - - - After opening parenthesis - Après l’ouverture des parenthèses - - - - Before closing parenthesis - Avant la fermeture des parenthèses - - - - After closing parenthesis - Après la fermeture des parenthèses - - - - No space between SQL function name and opening parenthesis - aucun espance entre le nom de la fonction SQL et l’ouverture des paranthèses - - - - Before dot operator (in path to database object) - Avant le point (dans le chemin de la base de données) - - - - After dot operator (in path to database object) - Après - - - - Before mathematical operator - Avant opérateurmathématique - - - - After mathematical operator - Après opérateurmathématique - - - - Never before comma - Jamais avant virgule - - - - Names - noms - - - - Preferred name wrapper - Échappement préféré - - - - Always use name wrapping - Toujours échapper les identifiants - - - - Uppercase data type names - Nom de type de données en majuscule - - - - Uppercase keywords - Mots-clefs en majuscule - - - - Comments - Commentaires - - - - Preferred comment marker (where possible): - - - - - Move all comments to the line end - Déplacer tous les commentaires en fin de ligne - - - - Line up comments at the line end - Alignez les commentaires en fin de ligne - - - - Preview - Aperçu - - - diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_it.qm b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_it.qm and /dev/null differ diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_it.ts b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_it.ts deleted file mode 100644 index d37b3a0..0000000 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_it.ts +++ /dev/null @@ -1,228 +0,0 @@ - - - - - QObject - - - - name - example name wrapper - - - - - SqlEnterpriseFormatter - - - Indentation - - - - - Line up keywords in multi-line queries - - - - - Indent contents of parenthesis block - - - - - Tab size: - - - - - New lines - - - - - Before opening parenthesis in column definitions - - - - - After opening parenthesis in column definitions - - - - - Before closing parenthesis in column definitions - - - - - After closing parenthesis in column definitions - - - - - Before opening parenthesis in expressions - - - - - After opening parenthesis in expressions - - - - - Before closing parenthesis in expressions - - - - - After closing parenthesis in expressions - - - - - After JOIN keywords in FROM clause - - - - - Put each column constraint in CREATE TABLE into new line - - - - - After comma - - - - - After comma in expressions - - - - - After semicolon - - - - - - Never before semicolon - - - - - White spaces - - - - - Before comma in lists - - - - - After comma in lists - - - - - Before opening parenthesis - - - - - After opening parenthesis - - - - - Before closing parenthesis - - - - - After closing parenthesis - - - - - No space between SQL function name and opening parenthesis - - - - - Before dot operator (in path to database object) - - - - - After dot operator (in path to database object) - - - - - Before mathematical operator - - - - - After mathematical operator - - - - - Never before comma - - - - - Names - - - - - Preferred name wrapper - - - - - Always use name wrapping - - - - - Uppercase data type names - - - - - Uppercase keywords - - - - - Comments - - - - - Preferred comment marker (where possible): - - - - - Move all comments to the line end - - - - - Line up comments at the line end - - - - - Preview - - - - diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pl.qm b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pl.qm deleted file mode 100644 index 7c215a2..0000000 Binary files a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pl.qm and /dev/null differ diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pl.ts b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pl.ts deleted file mode 100644 index 0acec1b..0000000 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pl.ts +++ /dev/null @@ -1,229 +0,0 @@ - - - - - QObject - - - - name - example name wrapper - nazwa - - - - SqlEnterpriseFormatter - - - Indentation - WciÄ™cia - - - - Line up keywords in multi-line queries - Wyrównaj sÅ‚owa kluczowe w wielolinijkowych zapytaniach - - - - Indent contents of parenthesis block - Dodaj wciÄ™cia do zawartoÅ›ci bloków w nawiasach - - - - Tab size: - Rozmiar tabulacji: - - - - New lines - Nowe linie - - - - Before opening parenthesis in column definitions - Przed nawiasem otwierajÄ…cym w definicjach kolumn - - - - After opening parenthesis in column definitions - Po nawiasie otwierajÄ…cym w definicjach kolumn - - - - Before closing parenthesis in column definitions - Przed nawiasem zamykajÄ…cym w definicjach kolumn - - - - After closing parenthesis in column definitions - Po nawiasie zamykajÄ…cym w definicjach kolumn - - - - Before opening parenthesis in expressions - Przed nawiasem otwierajÄ…cym w wyrażeniach - - - - After opening parenthesis in expressions - Po nawiasie otwierajÄ…cym w wyrażeniach - - - - Before closing parenthesis in expressions - Przed nawiasem zamykajÄ…cym w wyrażeniach - - - - After closing parenthesis in expressions - Po nawiasie zamykajÄ…cym w wyrażeniach - - - - After JOIN keywords in FROM clause - After *JOIN keywords in FROM clause - Po sÅ‚owach kluczowych JOIN w klauzuli FROM - - - - Put each column constraint in CREATE TABLE into new line - Ustaw każde ograniczenie kolumny w CREATE TABLE w nowej linii - - - - After comma - Po przecinku - - - - After comma in expressions - Po przecinku w wyrażeniach - - - - After semicolon - Po Å›redniku - - - - - Never before semicolon - Nigdy przed Å›rednikiem - - - - White spaces - Spacje - - - - Before comma in lists - Przed przecinkiem w listach - - - - After comma in lists - Po przecinku w listach - - - - Before opening parenthesis - Przed nawiasem otwierajÄ…cym - - - - After opening parenthesis - Po nawiasie otwierajÄ…cym - - - - Before closing parenthesis - Przed nawiasem zamykajÄ…cym - - - - After closing parenthesis - Po nawiasie zamykajÄ…cym - - - - No space between SQL function name and opening parenthesis - Bez spacji miÄ™dzy nazwÄ… funkcji SQL i nawiasem otwierajÄ…cym - - - - Before dot operator (in path to database object) - Przed operatorem kropki (w Å›cieżce do obiektu bazodanowego) - - - - After dot operator (in path to database object) - Po operatorze kropki (w Å›cieżce do obiektu bazodanowego) - - - - Before mathematical operator - Przed operatorem matematycznym - - - - After mathematical operator - Po operatorze matematycznym - - - - Never before comma - Nigdy przed przecinkiem - - - - Names - Nazwy - - - - Preferred name wrapper - Preferowane opakowanie nazwy - - - - Always use name wrapping - Zawsze opakowuj nazwy - - - - Uppercase data type names - Zmieniaj litery nazw typów danych na duże - - - - Uppercase keywords - Zmieniaj litery słów kluczowych na duże - - - - Comments - Komentarze - - - - Preferred comment marker (where possible): - Preferowany znacznik komentarzy (gdzie możliwy): - - - - Move all comments to the line end - PrzenieÅ› wszystkie komentarze na koniec linii - - - - Line up comments at the line end - Wyrównaj komentarze na koÅ„cu linii - - - - Preview - PodglÄ…d - - - diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pt_BR.qm b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pt_BR.qm and /dev/null differ diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pt_BR.ts b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pt_BR.ts deleted file mode 100644 index 5dcd199..0000000 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_pt_BR.ts +++ /dev/null @@ -1,228 +0,0 @@ - - - - - QObject - - - - name - example name wrapper - - - - - SqlEnterpriseFormatter - - - Indentation - - - - - Line up keywords in multi-line queries - - - - - Indent contents of parenthesis block - - - - - Tab size: - - - - - New lines - - - - - Before opening parenthesis in column definitions - - - - - After opening parenthesis in column definitions - - - - - Before closing parenthesis in column definitions - - - - - After closing parenthesis in column definitions - - - - - Before opening parenthesis in expressions - - - - - After opening parenthesis in expressions - - - - - Before closing parenthesis in expressions - - - - - After closing parenthesis in expressions - - - - - After JOIN keywords in FROM clause - - - - - Put each column constraint in CREATE TABLE into new line - - - - - After comma - - - - - After comma in expressions - - - - - After semicolon - - - - - - Never before semicolon - - - - - White spaces - - - - - Before comma in lists - - - - - After comma in lists - - - - - Before opening parenthesis - - - - - After opening parenthesis - - - - - Before closing parenthesis - - - - - After closing parenthesis - - - - - No space between SQL function name and opening parenthesis - - - - - Before dot operator (in path to database object) - - - - - After dot operator (in path to database object) - - - - - Before mathematical operator - - - - - After mathematical operator - - - - - Never before comma - - - - - Names - - - - - Preferred name wrapper - - - - - Always use name wrapping - - - - - Uppercase data type names - - - - - Uppercase keywords - - - - - Comments - - - - - Preferred comment marker (where possible): - - - - - Move all comments to the line end - - - - - Line up comments at the line end - - - - - Preview - - - - diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ro_RO.qm b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ro_RO.qm and /dev/null differ diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ro_RO.ts b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ro_RO.ts deleted file mode 100644 index c1d1b7b..0000000 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ro_RO.ts +++ /dev/null @@ -1,228 +0,0 @@ - - - - - QObject - - - - name - example name wrapper - - - - - SqlEnterpriseFormatter - - - Indentation - - - - - Line up keywords in multi-line queries - - - - - Indent contents of parenthesis block - - - - - Tab size: - - - - - New lines - - - - - Before opening parenthesis in column definitions - - - - - After opening parenthesis in column definitions - - - - - Before closing parenthesis in column definitions - - - - - After closing parenthesis in column definitions - - - - - Before opening parenthesis in expressions - - - - - After opening parenthesis in expressions - - - - - Before closing parenthesis in expressions - - - - - After closing parenthesis in expressions - - - - - After JOIN keywords in FROM clause - - - - - Put each column constraint in CREATE TABLE into new line - - - - - After comma - - - - - After comma in expressions - - - - - After semicolon - - - - - - Never before semicolon - - - - - White spaces - - - - - Before comma in lists - - - - - After comma in lists - - - - - Before opening parenthesis - - - - - After opening parenthesis - - - - - Before closing parenthesis - - - - - After closing parenthesis - - - - - No space between SQL function name and opening parenthesis - - - - - Before dot operator (in path to database object) - - - - - After dot operator (in path to database object) - - - - - Before mathematical operator - - - - - After mathematical operator - - - - - Never before comma - - - - - Names - - - - - Preferred name wrapper - - - - - Always use name wrapping - - - - - Uppercase data type names - - - - - Uppercase keywords - - - - - Comments - - - - - Preferred comment marker (where possible): - - - - - Move all comments to the line end - - - - - Line up comments at the line end - - - - - Preview - - - - diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ru.qm b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ru.qm deleted file mode 100644 index 07f5b2e..0000000 Binary files a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ru.qm and /dev/null differ diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ru.ts b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ru.ts deleted file mode 100644 index 54cf8ad..0000000 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_ru.ts +++ /dev/null @@ -1,232 +0,0 @@ - - - - - QObject - - - - name - example name wrapper - Ð¸Ð¼Ñ - - - - SqlEnterpriseFormatter - - - Indentation - ОтÑтупы - - - - Line up keywords in multi-line queries - Выравнивать ключевые Ñлова в многоÑтрочных запроÑах - - - - Indent contents of parenthesis block - Выравнивать Ñодержимое внутри Ñкобок - - - - Tab size: - Шаг табулÑции: - - - - New lines - ПереноÑÑ‹ Ñтрок - - - - Before opening parenthesis in column definitions - Перед открывающей Ñкобкой в определениÑÑ… Ñтолбцов - - - - After opening parenthesis in column definitions - ПоÑле открывающей Ñкобки в определениÑÑ… Ñтолбцов - - - - Before closing parenthesis in column definitions - Перед закрывающей Ñкобкой в определениÑÑ… Ñтолбцов - - - - After closing parenthesis in column definitions - ПоÑле закрывающей Ñкобки в определениÑÑ… Ñтолбцов - - - - Before opening parenthesis in expressions - Перед открывающей Ñкобкой в выражениÑÑ… - - - - After opening parenthesis in expressions - ПоÑле открывающей Ñкобки в выражениÑÑ… - - - - Before closing parenthesis in expressions - Перед закрывающей Ñкобкой в выражениÑÑ… - - - - After closing parenthesis in expressions - ПоÑле закрывающей Ñкобки в выражениÑÑ… - - - - After JOIN keywords in FROM clause - ПоÑле ключевых Ñлов JOIN в операторе FROM - - - - Put each column constraint in CREATE TABLE into new line - РазмеÑтить каждое ограничение на Ñтолбец в отдельной Ñтроке в конÑтрукции CREATE TABLE - - - - After comma - ПоÑле запÑтой - - - - After comma in expressions - ПоÑле запÑтой в выражениÑÑ… - - - - After semicolon - ПоÑле точки Ñ Ð·Ð°Ð¿Ñтой - - - - - Never before semicolon - Ðикогда перед точкой Ñ Ð·Ð°Ð¿Ñтой - - - - White spaces - Пробелы - - - - Before comma in lists - Перед запÑтой в ÑпиÑках - - - - After comma in lists - ПоÑле запÑтой в ÑпиÑках - - - - Before opening parenthesis - Перед открывающей Ñкобкой - - - - After opening parenthesis - ПоÑле открывающей Ñкобки - - - - Before closing parenthesis - Перед закрывающей Ñкобкой - - - - After closing parenthesis - ПоÑле закрывающей Ñкобки - - - - No space between SQL function name and opening parenthesis - Ðе Ñтавить пробел между именем функции SQL и открывающей Ñкобкой - - - - Before dot operator (in path to database object) - Перед оператором '.' (в путÑÑ… к объектам базы данных) - - - - After dot operator (in path to database object) - ПоÑле оператора '.' (в путÑÑ… к объектам базы данных) - - - - Before mathematical operator - Перед математичеÑким оператором - - - - After mathematical operator - ПоÑле математичеÑкого оператора - - - - Never before comma - Ðикогда перед запÑтой - - - - Names - Имена - - - - Preferred name wrapper - Предпочитаемое обрамление имён - - - - Always use name wrapping - Ð’Ñегда обрамлÑть имена - - - - Uppercase data type names - Приводить имена типов данных к верхнему региÑтру - - - - Uppercase keywords - Приводить ключевые Ñлова к верхнему региÑтру - - - - Comments - Комментарии - - - - Preferred comment marker (where possible): - Предпочитаемый Ñимвол ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ (где применимо): - - - SqlEnterpriseFormatter.CommentMarkers - SqlEnterpriseFormatter.CommentMarkers - - - - Move all comments to the line end - Перемещать вÑе комментарии в конец Ñтроки - - - - Line up comments at the line end - Выравнивать комментарии в конце Ñтроки - - - - Preview - ПредпроÑмотр - - - diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_sk.qm b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_sk.qm and /dev/null differ diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_sk.ts b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_sk.ts deleted file mode 100644 index 9d78cfc..0000000 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_sk.ts +++ /dev/null @@ -1,228 +0,0 @@ - - - - - QObject - - - - name - example name wrapper - - - - - SqlEnterpriseFormatter - - - Indentation - - - - - Line up keywords in multi-line queries - - - - - Indent contents of parenthesis block - - - - - Tab size: - - - - - New lines - - - - - Before opening parenthesis in column definitions - - - - - After opening parenthesis in column definitions - - - - - Before closing parenthesis in column definitions - - - - - After closing parenthesis in column definitions - - - - - Before opening parenthesis in expressions - - - - - After opening parenthesis in expressions - - - - - Before closing parenthesis in expressions - - - - - After closing parenthesis in expressions - - - - - After JOIN keywords in FROM clause - - - - - Put each column constraint in CREATE TABLE into new line - - - - - After comma - - - - - After comma in expressions - - - - - After semicolon - - - - - - Never before semicolon - - - - - White spaces - - - - - Before comma in lists - - - - - After comma in lists - - - - - Before opening parenthesis - - - - - After opening parenthesis - - - - - Before closing parenthesis - - - - - After closing parenthesis - - - - - No space between SQL function name and opening parenthesis - - - - - Before dot operator (in path to database object) - - - - - After dot operator (in path to database object) - - - - - Before mathematical operator - - - - - After mathematical operator - - - - - Never before comma - - - - - Names - - - - - Preferred name wrapper - - - - - Always use name wrapping - - - - - Uppercase data type names - - - - - Uppercase keywords - - - - - Comments - - - - - Preferred comment marker (where possible): - - - - - Move all comments to the line end - - - - - Line up comments at the line end - - - - - Preview - - - - diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_zh_CN.qm b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_zh_CN.ts b/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_zh_CN.ts deleted file mode 100644 index ad44b3c..0000000 --- a/Plugins/SqlEnterpriseFormatter/SqlEnterpriseFormatter_zh_CN.ts +++ /dev/null @@ -1,228 +0,0 @@ - - - - - QObject - - - - name - example name wrapper - - - - - SqlEnterpriseFormatter - - - Indentation - - - - - Line up keywords in multi-line queries - - - - - Indent contents of parenthesis block - - - - - Tab size: - - - - - New lines - - - - - Before opening parenthesis in column definitions - - - - - After opening parenthesis in column definitions - - - - - Before closing parenthesis in column definitions - - - - - After closing parenthesis in column definitions - - - - - Before opening parenthesis in expressions - - - - - After opening parenthesis in expressions - - - - - Before closing parenthesis in expressions - - - - - After closing parenthesis in expressions - - - - - After JOIN keywords in FROM clause - - - - - Put each column constraint in CREATE TABLE into new line - - - - - After comma - - - - - After comma in expressions - - - - - After semicolon - - - - - - Never before semicolon - - - - - White spaces - - - - - Before comma in lists - - - - - After comma in lists - - - - - Before opening parenthesis - - - - - After opening parenthesis - - - - - Before closing parenthesis - - - - - After closing parenthesis - - - - - No space between SQL function name and opening parenthesis - - - - - Before dot operator (in path to database object) - - - - - After dot operator (in path to database object) - - - - - Before mathematical operator - - - - - After mathematical operator - - - - - Never before comma - - - - - Names - - - - - Preferred name wrapper - - - - - Always use name wrapping - - - - - Uppercase data type names - - - - - Uppercase keywords - - - - - Comments - - - - - Preferred comment marker (where possible): - - - - - Move all comments to the line end - - - - - Line up comments at the line end - - - - - Preview - - - - diff --git a/Plugins/SqlEnterpriseFormatter/formatcopy.cpp b/Plugins/SqlEnterpriseFormatter/formatcopy.cpp deleted file mode 100644 index 18ce28c..0000000 --- a/Plugins/SqlEnterpriseFormatter/formatcopy.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "formatcopy.h" -#include "parser/ast/sqlitecopy.h" - -FormatCopy::FormatCopy(SqliteCopy* copy) : - copy(copy) -{ -} - -void FormatCopy::formatInternal() -{ - handleExplainQuery(copy); - withKeyword("COPY"); - if (copy->onConflict != SqliteConflictAlgo::null) - withKeyword("OR").withKeyword(sqliteConflictAlgo(copy->onConflict)); - - if (!copy->database.isNull()) - withId(copy->database); - - withId(copy->table).withKeyword("FROM").withString(copy->file); - - if (!copy->delimiter.isNull()) - withKeyword("USING").withKeyword("DELIMITERS").withString(copy->delimiter); - - withSemicolon(); -} diff --git a/Plugins/SqlEnterpriseFormatter/formatcopy.h b/Plugins/SqlEnterpriseFormatter/formatcopy.h deleted file mode 100644 index c0125e9..0000000 --- a/Plugins/SqlEnterpriseFormatter/formatcopy.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef FORMATCOPY_H -#define FORMATCOPY_H - -#include "formatstatement.h" - -class SqliteCopy; - -class FormatCopy : public FormatStatement -{ - public: - FormatCopy(SqliteCopy* copy); - - protected: - void formatInternal(); - - private: - SqliteCopy* copy = nullptr; -}; - -#endif // FORMATCOPY_H diff --git a/Plugins/SqlEnterpriseFormatter/formatcreatetable.cpp b/Plugins/SqlEnterpriseFormatter/formatcreatetable.cpp index e6fd189..28020af 100644 --- a/Plugins/SqlEnterpriseFormatter/formatcreatetable.cpp +++ b/Plugins/SqlEnterpriseFormatter/formatcreatetable.cpp @@ -36,8 +36,22 @@ void FormatCreateTable::formatInternal() withParDefRight(); - if (!createTable->withOutRowId.isNull()) + markAndKeepIndent("tableOptions"); + bool atLeastOneOption = false; + if (createTable->withOutRowId) + { withKeyword("WITHOUT").withId("ROWID"); + atLeastOneOption = true; + } + + if (createTable->strict) + { + if (atLeastOneOption) + withListComma(FormatToken::NO_SPACE_BEFORE); + + withId("STRICT"); + //atLeastOneOption = true; // to uncomment if there are further options down below + } } withSemicolon(); @@ -206,12 +220,12 @@ void FormatCreateTableConstraint::formatInternal() { case SqliteCreateTable::Constraint::PRIMARY_KEY: { - withKeyword("PRIMARY").withKeyword("KEY").withParDefLeft().withStatementList(constr->indexedColumns).withParDefRight(); + withKeyword("PRIMARY").withKeyword("KEY").withParDefLeft().withStatementList(constr->indexedColumns); if (constr->autoincrKw) withKeyword("AUTOINCREMENT"); - withConflict(constr->onConflict); + withParDefRight().withConflict(constr->onConflict); break; } case SqliteCreateTable::Constraint::UNIQUE: diff --git a/Plugins/SqlEnterpriseFormatter/formatcreatevirtualtable.cpp b/Plugins/SqlEnterpriseFormatter/formatcreatevirtualtable.cpp index f6495a0..6efd863 100644 --- a/Plugins/SqlEnterpriseFormatter/formatcreatevirtualtable.cpp +++ b/Plugins/SqlEnterpriseFormatter/formatcreatevirtualtable.cpp @@ -98,6 +98,7 @@ void FormatCreateVirtualTable::handleToken(const TokenPtr& token) case Token::CTX_FK_MATCH: case Token::CTX_PRAGMA: case Token::CTX_ROWID_KW: + case Token::CTX_STRICT_KW: case Token::CTX_NEW_KW: case Token::CTX_OLD_KW: case Token::CTX_ERROR_MESSAGE: diff --git a/Plugins/SqlEnterpriseFormatter/formatdelete.cpp b/Plugins/SqlEnterpriseFormatter/formatdelete.cpp index fc154e2..09cf06f 100644 --- a/Plugins/SqlEnterpriseFormatter/formatdelete.cpp +++ b/Plugins/SqlEnterpriseFormatter/formatdelete.cpp @@ -30,5 +30,11 @@ void FormatDelete::formatInternal() if (del->where) withNewLine().withLinedUpKeyword("WHERE").withStatement(del->where); + if (!del->returning.isEmpty()) + { + withNewLine().withLinedUpKeyword("RETURNING"); + withStatementList(del->returning, "returningColumns"); + } + withSemicolon(); } diff --git a/Plugins/SqlEnterpriseFormatter/formatexpr.cpp b/Plugins/SqlEnterpriseFormatter/formatexpr.cpp index 53c7421..52a1334 100644 --- a/Plugins/SqlEnterpriseFormatter/formatexpr.cpp +++ b/Plugins/SqlEnterpriseFormatter/formatexpr.cpp @@ -78,6 +78,11 @@ void FormatExpr::formatInternal() withDecrIndent(); break; } + case SqliteExpr::Mode::PTR_OP: + { + withStatement(expr->expr1).withOperator(expr->ptrOp).withStatement(expr->expr2); + break; + } case SqliteExpr::Mode::FUNCTION: { withFuncId(expr->function).withParFuncLeft(); @@ -165,6 +170,16 @@ void FormatExpr::formatInternal() withStatement(expr->expr2, "is"); break; } + case SqliteExpr::Mode::DISTINCT: + { + withStatement(expr->expr1).withKeyword("IS"); + if (expr->notKw) + withKeyword("NOT"); + + withKeyword("DISTINCT").withKeyword("FROM"); + withStatement(expr->expr2, "isDistinct"); + break; + } case SqliteExpr::Mode::BETWEEN: { withStatement(expr->expr1); diff --git a/Plugins/SqlEnterpriseFormatter/formatinsert.cpp b/Plugins/SqlEnterpriseFormatter/formatinsert.cpp index 709ee1b..c24452d 100644 --- a/Plugins/SqlEnterpriseFormatter/formatinsert.cpp +++ b/Plugins/SqlEnterpriseFormatter/formatinsert.cpp @@ -52,5 +52,10 @@ void FormatInsert::formatInternal() withDecrIndent(); } + if (!insert->returning.isEmpty()) + { + withNewLine().withLinedUpKeyword("RETURNING"); + withStatementList(insert->returning, "returningColumns"); + } withSemicolon(); } diff --git a/Plugins/SqlEnterpriseFormatter/formatselect.cpp b/Plugins/SqlEnterpriseFormatter/formatselect.cpp index 37559eb..385f1a0 100644 --- a/Plugins/SqlEnterpriseFormatter/formatselect.cpp +++ b/Plugins/SqlEnterpriseFormatter/formatselect.cpp @@ -1,5 +1,4 @@ #include "formatselect.h" -#include "formatwith.h" #include "parser/ast/sqlitewith.h" #include "parser/ast/sqlitewindowdefinition.h" @@ -208,9 +207,15 @@ void FormatSelectCoreJoinOp::formatInternal() if (joinOp->naturalKw) keywords << "NATURAL"; - if (joinOp->leftKw) + if (joinOp->leftKw || joinOp->fullKw || joinOp->rightKw) { - keywords << "LEFT"; + if (joinOp->leftKw) + keywords << "LEFT"; + else if (joinOp->fullKw) + keywords << "FULL"; + else if (joinOp->rightKw) + keywords << "RIGHT"; + if (joinOp->outerKw) keywords << "OUTER"; } diff --git a/Plugins/SqlEnterpriseFormatter/formatstatement.cpp b/Plugins/SqlEnterpriseFormatter/formatstatement.cpp index be924e1..250fde4 100644 --- a/Plugins/SqlEnterpriseFormatter/formatstatement.cpp +++ b/Plugins/SqlEnterpriseFormatter/formatstatement.cpp @@ -16,7 +16,6 @@ #include "formatattach.h" #include "formatbegintrans.h" #include "formatcommittrans.h" -#include "formatcopy.h" #include "formatcreateindex.h" #include "formatcreatetrigger.h" #include "formatcreateview.h" @@ -48,7 +47,6 @@ #include "parser/ast/sqliteattach.h" #include "parser/ast/sqlitebegintrans.h" #include "parser/ast/sqlitecommittrans.h" -#include "parser/ast/sqlitecopy.h" #include "parser/ast/sqlitecreateindex.h" #include "parser/ast/sqlitecreatetrigger.h" #include "parser/ast/sqlitecreateview.h" @@ -145,7 +143,6 @@ FormatStatement *FormatStatement::forQuery(SqliteStatement *query) FORMATTER_FACTORY_ENTRY(query, SqliteAttach, FormatAttach); FORMATTER_FACTORY_ENTRY(query, SqliteBeginTrans, FormatBeginTrans); FORMATTER_FACTORY_ENTRY(query, SqliteCommitTrans, FormatCommitTrans); - FORMATTER_FACTORY_ENTRY(query, SqliteCopy, FormatCopy); FORMATTER_FACTORY_ENTRY(query, SqliteCreateVirtualTable, FormatCreateVirtualTable); FORMATTER_FACTORY_ENTRY(query, SqliteCreateIndex, FormatCreateIndex); FORMATTER_FACTORY_ENTRY(query, SqliteCreateTrigger, FormatCreateTrigger); @@ -362,6 +359,13 @@ FormatStatement& FormatStatement::withLiteral(const QVariant& value) return *this; } + if (value.userType() == QVariant::ByteArray) + { + static_qstring(blobLiteral, "X'%1'"); + withBlob(blobLiteral.arg(QString::fromLatin1(value.toByteArray().toHex()))); + return *this; + } + bool ok; if (value.userType() == QVariant::Double) { @@ -380,14 +384,7 @@ FormatStatement& FormatStatement::withLiteral(const QVariant& value) return *this; } - QString str = value.toString(); - if (str.startsWith("x'", Qt::CaseInsensitive) && str.endsWith("'")) - { - withBlob(str); - return *this; - } - - withString(str); + withString(value.toString()); return *this; } diff --git a/Plugins/SqlEnterpriseFormatter/formatupdate.cpp b/Plugins/SqlEnterpriseFormatter/formatupdate.cpp index 0adeb17..cb802d8 100644 --- a/Plugins/SqlEnterpriseFormatter/formatupdate.cpp +++ b/Plugins/SqlEnterpriseFormatter/formatupdate.cpp @@ -55,5 +55,10 @@ void FormatUpdate::formatInternal() if (upd->where) withNewLine().withLinedUpKeyword("WHERE").withStatement(upd->where); + if (!upd->returning.isEmpty()) + { + withNewLine().withLinedUpKeyword("RETURNING"); + withStatementList(upd->returning, "returningColumns"); + } withSemicolon(); } diff --git a/Plugins/SqlEnterpriseFormatter/formatwith.cpp b/Plugins/SqlEnterpriseFormatter/formatwith.cpp index d3d99d5..c992d33 100644 --- a/Plugins/SqlEnterpriseFormatter/formatwith.cpp +++ b/Plugins/SqlEnterpriseFormatter/formatwith.cpp @@ -34,5 +34,16 @@ void FormatWithCommonTableExpression::formatInternal() if (cte->indexedColumns.size() > 0) withParDefLeft().withStatementList(cte->indexedColumns, "idxCols").withParDefRight(); - withKeyword("AS").withParDefLeft().withStatement(cte->select).withParDefRight(); + withKeyword("AS"); + switch (cte->asMode) { + case SqliteWith::CommonTableExpression::ANY: + break; + case SqliteWith::CommonTableExpression::MATERIALIZED: + withKeyword("MATERIALIZED"); + break; + case SqliteWith::CommonTableExpression::NOT_MATERIALIZED: + withKeyword("NOT").withKeyword("MATERIALIZED"); + break; + } + withParDefLeft().withStatement(cte->select).withParDefRight(); } diff --git a/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.cpp b/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.cpp index 62517c6..b33f286 100644 --- a/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.cpp +++ b/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.cpp @@ -36,7 +36,7 @@ QString SqlEnterpriseFormatter::format(SqliteQueryPtr query) bool SqlEnterpriseFormatter::init() { - Q_INIT_RESOURCE(sqlenterpriseformatter); + SQLS_INIT_RESOURCE(sqlenterpriseformatter); static_qstring(query1, "SELECT (2 + 4) AND (3 + 5), 4 NOT IN (SELECT t1.'some[_]name' + t2.[some'name2] FROM xyz t1 JOIN zxc t2 ON (t1.aaa = t2.aaa)) " "FROM a, (SELECT id FROM table2);"); @@ -65,14 +65,13 @@ bool SqlEnterpriseFormatter::init() void SqlEnterpriseFormatter::deinit() { - Q_CLEANUP_RESOURCE(sqlenterpriseformatter); + SQLS_CLEANUP_RESOURCE(sqlenterpriseformatter); } - void SqlEnterpriseFormatter::updatePreview() { QStringList output; - for (const SqliteQueryPtr& q : previewQueries) + for (SqliteQueryPtr& q : previewQueries) output << format(q); cfg.SqlEnterpriseFormatter.PreviewCode.set(output.join("\n\n")); diff --git a/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.qrc b/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.qrc index 6fde81d..bd23092 100644 --- a/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.qrc +++ b/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.qrc @@ -2,19 +2,4 @@ sqlenterpriseformatter.ui - - SqlEnterpriseFormatter_ro_RO.qm - SqlEnterpriseFormatter_de.qm - - - SqlEnterpriseFormatter_pl.qm - SqlEnterpriseFormatter_ru.qm - SqlEnterpriseFormatter_fr.qm - SqlEnterpriseFormatter_sk.qm - SqlEnterpriseFormatter_zh_CN.qm - - - - - diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter.ts new file mode 100644 index 0000000..5d78b16 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + + + + + SqlEnterpriseFormatter + + + Indentation + + + + + Line up keywords in multi-line queries + + + + + Indent contents of parenthesis block + + + + + Tab size: + + + + + New lines + + + + + Before opening parenthesis in column definitions + + + + + After opening parenthesis in column definitions + + + + + Before closing parenthesis in column definitions + + + + + After closing parenthesis in column definitions + + + + + Before opening parenthesis in expressions + + + + + After opening parenthesis in expressions + + + + + Before closing parenthesis in expressions + + + + + After closing parenthesis in expressions + + + + + After JOIN keywords in FROM clause + + + + + Put each column constraint in CREATE TABLE into new line + + + + + After comma + + + + + After comma in expressions + + + + + After semicolon + + + + + + Never before semicolon + + + + + White spaces + + + + + Before comma in lists + + + + + After comma in lists + + + + + Before opening parenthesis + + + + + After opening parenthesis + + + + + Before closing parenthesis + + + + + After closing parenthesis + + + + + No space between SQL function name and opening parenthesis + + + + + Before dot operator (in path to database object) + + + + + After dot operator (in path to database object) + + + + + Before mathematical operator + + + + + After mathematical operator + + + + + Never before comma + + + + + Names + + + + + Preferred name wrapper + + + + + Always use name wrapping + + + + + Uppercase data type names + + + + + Uppercase keywords + + + + + Comments + + + + + Preferred comment marker (where possible): + + + + + Move all comments to the line end + + + + + Line up comments at the line end + + + + + Preview + + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_af_ZA.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_af_ZA.ts new file mode 100644 index 0000000..47b66eb --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_af_ZA.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ar_SA.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ar_SA.ts new file mode 100644 index 0000000..726ae44 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ar_SA.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ca_ES.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ca_ES.ts new file mode 100644 index 0000000..a5ea728 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ca_ES.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_cs_CZ.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_cs_CZ.ts new file mode 100644 index 0000000..ba55846 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_cs_CZ.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_da_DK.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_da_DK.ts new file mode 100644 index 0000000..d4c044d --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_da_DK.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_de_DE.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_de_DE.ts new file mode 100644 index 0000000..15d4920 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_de_DE.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Einrückung + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tabulatorgröße: + + + + New lines + Neue Zeilen + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + Nach Komma + + + + After comma in expressions + Nach Komma in Ausdrücken + + + + After semicolon + Nach Semikolon + + + + + Never before semicolon + Nie vor Semikolon + + + + White spaces + Leerzeichen + + + + Before comma in lists + Vor Komma in Listen + + + + After comma in lists + Nach Komma in Listen + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Nie vor Komma + + + + Names + Namen + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Kommentare + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Vorschau + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_el_GR.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_el_GR.ts new file mode 100644 index 0000000..b20e806 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_el_GR.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_en_US.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_en_US.ts new file mode 100644 index 0000000..eff8ce6 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_en_US.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_es_ES.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_es_ES.ts new file mode 100644 index 0000000..b97b3c0 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_es_ES.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + nombre + + + + SqlEnterpriseFormatter + + + Indentation + Indentación + + + + Line up keywords in multi-line queries + Poner en una misma línea las palabras claves en consultas de múltiples líneas + + + + Indent contents of parenthesis block + Indentar los contenidos del bloque de paréntesis + + + + Tab size: + Tamaño de la tabulación: + + + + New lines + Nuevas líneas + + + + Before opening parenthesis in column definitions + Antes de abrir paréntesis en las definiciones de columna + + + + After opening parenthesis in column definitions + Luego de abrir paréntesis en las definiciones de columna + + + + Before closing parenthesis in column definitions + Antes de cerrar paréntesis en las definiciones de columna + + + + After closing parenthesis in column definitions + Luego de cerrar paréntesis en las definiciones de columna + + + + Before opening parenthesis in expressions + Antes de abrir paréntesis en las expresiones + + + + After opening parenthesis in expressions + Luego de abrir paréntesis en las expresiones + + + + Before closing parenthesis in expressions + Antes de cerrar paréntesis en las expresiones + + + + After closing parenthesis in expressions + Luego de cerrar paréntesis en las expresiones + + + + After JOIN keywords in FROM clause + Después de las palabras clave JOIN en la cláusula FROM + + + + Put each column constraint in CREATE TABLE into new line + Poner cada restricción de columna en una nueva línea en la sentencia CREATE TABLE + + + + After comma + Después de la coma + + + + After comma in expressions + Después de la coma en las expresiones + + + + After semicolon + Después del punto y coma + + + + + Never before semicolon + Nunca antes de un punto y coma + + + + White spaces + Espacios en blanco + + + + Before comma in lists + Antes de una coma en las listas + + + + After comma in lists + Después de una coma en las listas + + + + Before opening parenthesis + Antes de abrir paréntesis + + + + After opening parenthesis + Después de abrir paréntesis + + + + Before closing parenthesis + Antes de cerrar paréntesis + + + + After closing parenthesis + Después de cerrar paréntesis + + + + No space between SQL function name and opening parenthesis + Sin espacios entre el nombre de la función SQL y el paréntesis de apertura + + + + Before dot operator (in path to database object) + Antes del operador punto (en la ruta al objeto de la base de datos) + + + + After dot operator (in path to database object) + Después del operador punto (en la ruta al objeto de base de datos) + + + + Before mathematical operator + Antes del operador matemático + + + + After mathematical operator + Después del operador matemático + + + + Never before comma + Nunca antes de la coma + + + + Names + Nombres + + + + Preferred name wrapper + Envoltura de nombre preferida + + + + Always use name wrapping + Siempre envolver nombres + + + + Uppercase data type names + Nombres de tipos de dato en mayúsculas + + + + Uppercase keywords + Palabras clave en mayúsculas + + + + Comments + Comentarios + + + + Preferred comment marker (where possible): + Maracador de comentarios preferido (cuando sea posible): + + + + Move all comments to the line end + Mover todos los comentarios al final de la línea + + + + Line up comments at the line end + Alinear los comentarios al final de la línea + + + + Preview + Vista previa + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fa_IR.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fa_IR.ts new file mode 100644 index 0000000..66f6d5e --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fa_IR.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fi_FI.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fi_FI.ts new file mode 100644 index 0000000..1895476 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fi_FI.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fr_FR.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fr_FR.ts new file mode 100644 index 0000000..dee0e5c --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_fr_FR.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + Nom + + + + SqlEnterpriseFormatter + + + Indentation + Identation + + + + Line up keywords in multi-line queries + Aligner les mots-clés dans les requêtes de plusieurs lignes + + + + Indent contents of parenthesis block + Indenter le contenu du bloc entre parenthèses + + + + Tab size: + taille d’indentation : + + + + New lines + Nouvelles lignes + + + + Before opening parenthesis in column definitions + Avant l’ouverture des parenthèses dans la définition de la colonne + + + + After opening parenthesis in column definitions + Après l’ouverture des parenthèses dans la définition de la colonne + + + + Before closing parenthesis in column definitions + Avant la fermeture des parenthèses dans la définition de la colonne + + + + After closing parenthesis in column definitions + Après la fermeture des parenthèses dans la définition de la colonne + + + + Before opening parenthesis in expressions + Avant l’ouverture des parenthèses dans l’expression + + + + After opening parenthesis in expressions + Après l’ouverture des parenthèses dans l’expression + + + + Before closing parenthesis in expressions + Avant la fermeture des parenthèses dans l’expression + + + + After closing parenthesis in expressions + Après la fermeture des parenthèses dans l’expression + + + + After JOIN keywords in FROM clause + Après avoir JOINDRE les mots-clés dans la clause DEPUIS + + + + Put each column constraint in CREATE TABLE into new line + Mettez chaque contrainte de colonne de CREATE TABLE dans une nouvelle ligne + + + + After comma + Après virgule + + + + After comma in expressions + Après la virgule dans l’exoression + + + + After semicolon + Après un point virgule + + + + + Never before semicolon + Jamais avant un point virgule + + + + White spaces + Espaces + + + + Before comma in lists + Après virgule dans la liste + + + + After comma in lists + Avant virgule dans la liste + + + + Before opening parenthesis + Avant l’ouverture des parenthèses + + + + After opening parenthesis + Après l’ouverture des parenthèses + + + + Before closing parenthesis + Avant la fermeture des parenthèses + + + + After closing parenthesis + Après la fermeture des parenthèses + + + + No space between SQL function name and opening parenthesis + aucun espance entre le nom de la fonction SQL et l’ouverture des paranthèses + + + + Before dot operator (in path to database object) + Avant le point (dans le chemin de la base de données) + + + + After dot operator (in path to database object) + Après + + + + Before mathematical operator + Avant opérateurmathématique + + + + After mathematical operator + Après opérateurmathématique + + + + Never before comma + Jamais avant virgule + + + + Names + noms + + + + Preferred name wrapper + Échappement préféré + + + + Always use name wrapping + Toujours échapper les identifiants + + + + Uppercase data type names + Nom de type de données en majuscule + + + + Uppercase keywords + Mots-clés en majuscule + + + + Comments + Commentaires + + + + Preferred comment marker (where possible): + Marqueur de commentaire préféré (si possible): + + + + Move all comments to the line end + Déplacer tous les commentaires en fin de ligne + + + + Line up comments at the line end + Alignez les commentaires en fin de ligne + + + + Preview + Aperçu + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_he_IL.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_he_IL.ts new file mode 100644 index 0000000..b3baa50 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_he_IL.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + ×©× + + + + SqlEnterpriseFormatter + + + Indentation + ×”×–×—×” + + + + Line up keywords in multi-line queries + סידור מלות־מפתח בשורה בש×ילתות רבות־שורות + + + + Indent contents of parenthesis block + הזחת תוכן הגוש ×”×—×¡×•× ×‘×¡×•×’×¨×™×™× + + + + Tab size: + גודל ט×ב: + + + + New lines + שורות חדשות + + + + Before opening parenthesis in column definitions + לפני פתיחת ×¡×•×’×¨×™×™× ×‘×”×’×“×¨×•×ª עמודה + + + + After opening parenthesis in column definitions + ×חרי פתיחת ×¡×•×’×¨×™×™× ×‘×”×’×“×¨×•×ª עמודה + + + + Before closing parenthesis in column definitions + לפני סגירת ×¡×•×’×¨×™×™× ×‘×”×’×“×¨×•×ª עמודה + + + + After closing parenthesis in column definitions + ×חרי סגירת ×¡×•×’×¨×™×™× ×‘×”×’×“×¨×•×ª עמודה + + + + Before opening parenthesis in expressions + לפני פתיחת ×¡×•×’×¨×™×™× ×‘×‘×™×˜×•×™×™× + + + + After opening parenthesis in expressions + ×חרי פתיחת ×¡×•×’×¨×™×™× ×‘×‘×™×˜×•×™×™× + + + + Before closing parenthesis in expressions + לפני סגירת ×¡×•×’×¨×™×™× ×‘×‘×™×˜×•×™×™× + + + + After closing parenthesis in expressions + ×חרי סגירת ×¡×•×’×¨×™×™× ×‘×‘×™×˜×•×™×™× + + + + After JOIN keywords in FROM clause + ×חרי מילות־מפתח JOIN בפסקת FROM + + + + Put each column constraint in CREATE TABLE into new line + הכנסת כל ×ילוץ עמודה ב 'יצירת טבלה' לשורה חדשה + + + + After comma + ל×חר פסיק + + + + After comma in expressions + ל×חר פסיק ×‘×‘×™×˜×•×™×™× + + + + After semicolon + ל×חר ×תנח + + + + + Never before semicolon + ×œ×¢×•×œ× ×œ× ×œ×¤× ×™ ×תנח + + + + White spaces + מרווח לבן + + + + Before comma in lists + לפני פסיק ברשימות + + + + After comma in lists + ×חרי פסיק ברשימות + + + + Before opening parenthesis + לפני פתיחת ×¡×•×’×¨×™×™× + + + + After opening parenthesis + ×חרי פתיחת ×¡×•×’×¨×™×™× + + + + Before closing parenthesis + לפני סגירת ×¡×•×’×¨×™×™× + + + + After closing parenthesis + ×חרי סגירת ×¡×•×’×¨×™×™× + + + + No space between SQL function name and opening parenthesis + ×ין רווח בין ×©× ×¤×•× ×§×¦×™×™×ª SQL לסוגר פתיחה + + + + Before dot operator (in path to database object) + לפני סימן־פעולה נקודה (בנתיב ×œ×¢×¦× ×ž×¡×“ נתוני×) + + + + After dot operator (in path to database object) + ל×חר סימן־פעולה נקודה (בנתיב ×œ×¢×¦× ×ž×¡×“ נתוני×) + + + + Before mathematical operator + לפני סימן מתמטי + + + + After mathematical operator + ×חרי סימן מתמטי + + + + Never before comma + ×œ×¢×•×œ× ×œ× ×œ×¤× ×™ פסיק + + + + Names + שמות + + + + Preferred name wrapper + עטיפת שמות מועדפת + + + + Always use name wrapping + להשתמש תמיד בעטיפת שמות + + + + Uppercase data type names + שמות סוגי × ×ª×•× ×™× ×‘×ותיות רישיות + + + + Uppercase keywords + מילות מפתח ב×ותיות רישיות + + + + Comments + הערות + + + + Preferred comment marker (where possible): + מדגש הערות מועדף (היכן שניתן): + + + + Move all comments to the line end + העברת כל ההערות לסוף השורה + + + + Line up comments at the line end + סידור הערות בטור בסוף השורה + + + + Preview + תצוגה מקדימה + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_hu_HU.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_hu_HU.ts new file mode 100644 index 0000000..0d0c64a --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_hu_HU.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_it_IT.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_it_IT.ts new file mode 100644 index 0000000..9f26e0d --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_it_IT.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + nome + + + + SqlEnterpriseFormatter + + + Indentation + Indentazione + + + + Line up keywords in multi-line queries + Allineare le parole chiave nelle interrogazioni multi-riga + + + + Indent contents of parenthesis block + Indentazione dei contenuti del blocco parentesi + + + + Tab size: + Dimensione tab: + + + + New lines + Nuove linee + + + + Before opening parenthesis in column definitions + Prima di aprire le parentesi nelle definizioni delle colonne + + + + After opening parenthesis in column definitions + Dopo aver aperto le parentesi nelle definizioni delle colonne + + + + Before closing parenthesis in column definitions + Prima di chiudere le parentesi nelle definizioni delle colonne + + + + After closing parenthesis in column definitions + Dopo aver chiuso le parentesi nelle definizioni delle colonne + + + + Before opening parenthesis in expressions + Prima di aprire la parentesi nelle espressioni + + + + After opening parenthesis in expressions + Dopo aver aperto la parentesi nelle espressioni + + + + Before closing parenthesis in expressions + Prima di chiudere le parentesi nelle espressioni + + + + After closing parenthesis in expressions + Dopo aver chiuso la parentesi nelle espressioni + + + + After JOIN keywords in FROM clause + Dopo le parole chiave JOIN nella clausola FROM + + + + Put each column constraint in CREATE TABLE into new line + Metti ogni constraints di colonna nella CREATE TABLE nella nuova riga + + + + After comma + Dopo la virgola + + + + After comma in expressions + Dopo la virgola nelle espressioni + + + + After semicolon + Dopo punto e virgola + + + + + Never before semicolon + Mai prima del punto e virgola + + + + White spaces + Spazi bianchi + + + + Before comma in lists + Prima della virgola nelle liste + + + + After comma in lists + Dopo la virgola nelle liste + + + + Before opening parenthesis + Prima di aprire la parentesi + + + + After opening parenthesis + Dopo l'apertura della parentesi + + + + Before closing parenthesis + Prima di chiudere la parentesi + + + + After closing parenthesis + Dopo aver chiuso la parentesi + + + + No space between SQL function name and opening parenthesis + Nessuno spazio tra il nome della funzione SQL e l'apertura della parentesi + + + + Before dot operator (in path to database object) + Prima dell'operatore punto (nel percorso dell'oggetto database) + + + + After dot operator (in path to database object) + Dopo l'operatore punto (nel percorso dell'oggetto database) + + + + Before mathematical operator + Prima dell’operatore matematico + + + + After mathematical operator + Dopo operatore matematico + + + + Never before comma + Mai prima della virgola + + + + Names + Nomi + + + + Preferred name wrapper + Wrapper nome preferito + + + + Always use name wrapping + Usa sempre a capo dei nomi + + + + Uppercase data type names + Nomi dei tipi di dati maiuscoli + + + + Uppercase keywords + Parole chiave maiuscole + + + + Comments + Commenti + + + + Preferred comment marker (where possible): + Indicatore di commento preferito (dove possibile): + + + + Move all comments to the line end + Sposta tutti i commenti alla fine della riga + + + + Line up comments at the line end + Allinea i commenti alla fine della riga + + + + Preview + Anteprima + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ja_JP.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ja_JP.ts new file mode 100644 index 0000000..09ab254 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ja_JP.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_kaa.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_kaa.ts new file mode 100644 index 0000000..15bea59 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_kaa.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ko_KR.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ko_KR.ts new file mode 100644 index 0000000..aea1aa4 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ko_KR.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_nl_NL.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_nl_NL.ts new file mode 100644 index 0000000..32eec7c --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_nl_NL.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_no_NO.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_no_NO.ts new file mode 100644 index 0000000..ffc315d --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_no_NO.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pl_PL.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pl_PL.ts new file mode 100644 index 0000000..4fb7e00 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pl_PL.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + nazwa + + + + SqlEnterpriseFormatter + + + Indentation + WciÄ™cia + + + + Line up keywords in multi-line queries + Wyrównaj sÅ‚owa kluczowe w wielolinijkowych zapytaniach + + + + Indent contents of parenthesis block + Dodaj wciÄ™cia do zawartoÅ›ci bloków w nawiasach + + + + Tab size: + Rozmiar tabulacji: + + + + New lines + Nowe linie + + + + Before opening parenthesis in column definitions + Przed nawiasem otwierajÄ…cym w definicjach kolumn + + + + After opening parenthesis in column definitions + Po nawiasie otwierajÄ…cym w definicjach kolumn + + + + Before closing parenthesis in column definitions + Przed nawiasem zamykajÄ…cym w definicjach kolumn + + + + After closing parenthesis in column definitions + Po nawiasie zamykajÄ…cym w definicjach kolumn + + + + Before opening parenthesis in expressions + Przed nawiasem otwierajÄ…cym w wyrażeniach + + + + After opening parenthesis in expressions + Po nawiasie otwierajÄ…cym w wyrażeniach + + + + Before closing parenthesis in expressions + Przed nawiasem zamykajÄ…cym w wyrażeniach + + + + After closing parenthesis in expressions + Po nawiasie zamykajÄ…cym w wyrażeniach + + + + After JOIN keywords in FROM clause + Po sÅ‚owach kluczowych JOIN w klauzuli FROM + + + + Put each column constraint in CREATE TABLE into new line + Ustaw każde ograniczenie kolumny w CREATE TABLE w nowej linii + + + + After comma + Po przecinku + + + + After comma in expressions + Po przecinku w wyrażeniach + + + + After semicolon + Po Å›redniku + + + + + Never before semicolon + Nigdy przed Å›rednikiem + + + + White spaces + Spacje + + + + Before comma in lists + Przed przecinkiem w listach + + + + After comma in lists + Po przecinku w listach + + + + Before opening parenthesis + Przed nawiasem otwierajÄ…cym + + + + After opening parenthesis + Po nawiasie otwierajÄ…cym + + + + Before closing parenthesis + Przed nawiasem zamykajÄ…cym + + + + After closing parenthesis + Po nawiasie zamykajÄ…cym + + + + No space between SQL function name and opening parenthesis + Bez spacji miÄ™dzy nazwÄ… funkcji SQL i nawiasem otwierajÄ…cym + + + + Before dot operator (in path to database object) + Przed operatorem kropki (w Å›cieżce do obiektu bazodanowego) + + + + After dot operator (in path to database object) + Po operatorze kropki (w Å›cieżce do obiektu bazodanowego) + + + + Before mathematical operator + Przed operatorem matematycznym + + + + After mathematical operator + Po operatorze matematycznym + + + + Never before comma + Nigdy przed przecinkiem + + + + Names + Nazwy + + + + Preferred name wrapper + Preferowane opakowanie nazwy + + + + Always use name wrapping + Zawsze opakowuj nazwy + + + + Uppercase data type names + Zmieniaj litery nazw typów danych na duże + + + + Uppercase keywords + Zmieniaj litery słów kluczowych na duże + + + + Comments + Komentarze + + + + Preferred comment marker (where possible): + Preferowany znacznik komentarzy (gdzie możliwy): + + + + Move all comments to the line end + PrzenieÅ› wszystkie komentarze na koniec linii + + + + Line up comments at the line end + Wyrównaj komentarze na koÅ„cu linii + + + + Preview + PodglÄ…d + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pt_BR.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pt_BR.ts new file mode 100644 index 0000000..11b348b --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pt_BR.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + nome + + + + SqlEnterpriseFormatter + + + Indentation + Indentação + + + + Line up keywords in multi-line queries + Agrupar palavras-chave em consultas multi-linha + + + + Indent contents of parenthesis block + Indentar conteúdo do bloco parênteses + + + + Tab size: + Tamanho do guia: + + + + New lines + Novas linhas + + + + Before opening parenthesis in column definitions + Antes de abrir parênteses nas definições de coluna + + + + After opening parenthesis in column definitions + Depois de abrir parênteses nas definições de coluna + + + + Before closing parenthesis in column definitions + Antes de fechar parênteses nas definições de coluna + + + + After closing parenthesis in column definitions + Depois de fechar parênteses nas definições de coluna + + + + Before opening parenthesis in expressions + Antes de abrir parênteses em expressões + + + + After opening parenthesis in expressions + Após a abertura parênteses em expressões + + + + Before closing parenthesis in expressions + Antes de fechar parênteses em expressões + + + + After closing parenthesis in expressions + Depois de fechar parênteses em expressões + + + + After JOIN keywords in FROM clause + Depois de JOIN palavras-chave na cláusula FROM + + + + Put each column constraint in CREATE TABLE into new line + Coloque cada restrição de coluna em CREATE TABLE em nova linha + + + + After comma + Depois da vírgula + + + + After comma in expressions + Após vírgula em expressões + + + + After semicolon + Após ponto e vírgula + + + + + Never before semicolon + Nunca antes ponto e vírgula + + + + White spaces + Espaço em branco + + + + Before comma in lists + Antes da vírgula em listas + + + + After comma in lists + Após vírgula em listas + + + + Before opening parenthesis + Antes de abrir parênteses + + + + After opening parenthesis + Depois de abrir parênteses + + + + Before closing parenthesis + Antes de fechar parênteses + + + + After closing parenthesis + Depois de fechar parênteses + + + + No space between SQL function name and opening parenthesis + Não há espaço entre o nome da função SQL e a abertura de parênteses + + + + Before dot operator (in path to database object) + Operador antes do ponto (em caminho para o objeto do banco de dados) + + + + After dot operator (in path to database object) + Operador depois do ponto (em caminho para o objeto do banco de dados) + + + + Before mathematical operator + Antes do operador matemático + + + + After mathematical operator + Depois do operador matemático + + + + Never before comma + Nunca antes da vírgula + + + + Names + Nomes + + + + Preferred name wrapper + Nome do empacotador preferencial + + + + Always use name wrapping + Sempre usar quebra de nome + + + + Uppercase data type names + Nomes de tipos de dados maiúsculos + + + + Uppercase keywords + Palavras-chave em maiúsculas + + + + Comments + Comentários + + + + Preferred comment marker (where possible): + Marcador de comentário preferido (quando possível): + + + + Move all comments to the line end + Mover todos os comentários para o final da linha + + + + Line up comments at the line end + Agrupar comentários no final da linha + + + + Preview + Pré-visualizar + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pt_PT.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pt_PT.ts new file mode 100644 index 0000000..124ad37 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_pt_PT.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ro_RO.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ro_RO.ts new file mode 100644 index 0000000..08a2d77 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ro_RO.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ru_RU.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ru_RU.ts new file mode 100644 index 0000000..bb5fc83 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_ru_RU.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + Ð¸Ð¼Ñ + + + + SqlEnterpriseFormatter + + + Indentation + ОтÑтупы + + + + Line up keywords in multi-line queries + Выравнивать ключевые Ñлова в многоÑтрочных запроÑах + + + + Indent contents of parenthesis block + Выравнивать Ñодержимое внутри Ñкобок + + + + Tab size: + Шаг табулÑции: + + + + New lines + ПереноÑÑ‹ Ñтрок + + + + Before opening parenthesis in column definitions + Перед открывающей Ñкобкой в определениÑÑ… Ñтолбцов + + + + After opening parenthesis in column definitions + ПоÑле открывающей Ñкобки в определениÑÑ… Ñтолбцов + + + + Before closing parenthesis in column definitions + Перед закрывающей Ñкобкой в определениÑÑ… Ñтолбцов + + + + After closing parenthesis in column definitions + ПоÑле закрывающей Ñкобки в определениÑÑ… Ñтолбцов + + + + Before opening parenthesis in expressions + Перед открывающей Ñкобкой в выражениÑÑ… + + + + After opening parenthesis in expressions + ПоÑле открывающей Ñкобки в выражениÑÑ… + + + + Before closing parenthesis in expressions + Перед закрывающей Ñкобкой в выражениÑÑ… + + + + After closing parenthesis in expressions + ПоÑле закрывающей Ñкобки в выражениÑÑ… + + + + After JOIN keywords in FROM clause + ПоÑле ключевых Ñлов JOIN в операторе FROM + + + + Put each column constraint in CREATE TABLE into new line + РазмеÑтить каждое ограничение на Ñтолбец в отдельной Ñтроке в конÑтрукции CREATE TABLE + + + + After comma + ПоÑле запÑтой + + + + After comma in expressions + ПоÑле запÑтой в выражениÑÑ… + + + + After semicolon + ПоÑле точки Ñ Ð·Ð°Ð¿Ñтой + + + + + Never before semicolon + Ðикогда перед точкой Ñ Ð·Ð°Ð¿Ñтой + + + + White spaces + Пробелы + + + + Before comma in lists + Перед запÑтой в ÑпиÑках + + + + After comma in lists + ПоÑле запÑтой в ÑпиÑках + + + + Before opening parenthesis + Перед открывающей Ñкобкой + + + + After opening parenthesis + ПоÑле открывающей Ñкобки + + + + Before closing parenthesis + Перед закрывающей Ñкобкой + + + + After closing parenthesis + ПоÑле закрывающей Ñкобки + + + + No space between SQL function name and opening parenthesis + Ðе Ñтавить пробел между именем функции SQL и открывающей Ñкобкой + + + + Before dot operator (in path to database object) + Перед оператором '.' (в путÑÑ… к объектам базы данных) + + + + After dot operator (in path to database object) + ПоÑле оператора '.' (в путÑÑ… к объектам базы данных) + + + + Before mathematical operator + Перед математичеÑким оператором + + + + After mathematical operator + ПоÑле математичеÑкого оператора + + + + Never before comma + Ðикогда перед запÑтой + + + + Names + Имена + + + + Preferred name wrapper + Предпочитаемое обрамление имён + + + + Always use name wrapping + Ð’Ñегда обрамлÑть имена + + + + Uppercase data type names + Приводить имена типов данных к верхнему региÑтру + + + + Uppercase keywords + Приводить ключевые Ñлова к верхнему региÑтру + + + + Comments + Комментарии + + + + Preferred comment marker (where possible): + Предпочитаемый Ñимвол ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ (где применимо): + + + + Move all comments to the line end + Перемещать вÑе комментарии в конец Ñтроки + + + + Line up comments at the line end + Выравнивать комментарии в конце Ñтроки + + + + Preview + ПредпроÑмотр + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sk_SK.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sk_SK.ts new file mode 100644 index 0000000..ac53c07 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sk_SK.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sr_SP.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sr_SP.ts new file mode 100644 index 0000000..65fa0ed --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sr_SP.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sv_SE.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sv_SE.ts new file mode 100644 index 0000000..64ede95 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_sv_SE.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_tr_TR.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_tr_TR.ts new file mode 100644 index 0000000..2bc0ed8 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_tr_TR.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_uk_UA.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_uk_UA.ts new file mode 100644 index 0000000..94559ca --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_uk_UA.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_vi_VN.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_vi_VN.ts new file mode 100644 index 0000000..27e6b35 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_vi_VN.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + name + + + + SqlEnterpriseFormatter + + + Indentation + Indentation + + + + Line up keywords in multi-line queries + Line up keywords in multi-line queries + + + + Indent contents of parenthesis block + Indent contents of parenthesis block + + + + Tab size: + Tab size: + + + + New lines + New lines + + + + Before opening parenthesis in column definitions + Before opening parenthesis in column definitions + + + + After opening parenthesis in column definitions + After opening parenthesis in column definitions + + + + Before closing parenthesis in column definitions + Before closing parenthesis in column definitions + + + + After closing parenthesis in column definitions + After closing parenthesis in column definitions + + + + Before opening parenthesis in expressions + Before opening parenthesis in expressions + + + + After opening parenthesis in expressions + After opening parenthesis in expressions + + + + Before closing parenthesis in expressions + Before closing parenthesis in expressions + + + + After closing parenthesis in expressions + After closing parenthesis in expressions + + + + After JOIN keywords in FROM clause + After JOIN keywords in FROM clause + + + + Put each column constraint in CREATE TABLE into new line + Put each column constraint in CREATE TABLE into new line + + + + After comma + After comma + + + + After comma in expressions + After comma in expressions + + + + After semicolon + After semicolon + + + + + Never before semicolon + Never before semicolon + + + + White spaces + White spaces + + + + Before comma in lists + Before comma in lists + + + + After comma in lists + After comma in lists + + + + Before opening parenthesis + Before opening parenthesis + + + + After opening parenthesis + After opening parenthesis + + + + Before closing parenthesis + Before closing parenthesis + + + + After closing parenthesis + After closing parenthesis + + + + No space between SQL function name and opening parenthesis + No space between SQL function name and opening parenthesis + + + + Before dot operator (in path to database object) + Before dot operator (in path to database object) + + + + After dot operator (in path to database object) + After dot operator (in path to database object) + + + + Before mathematical operator + Before mathematical operator + + + + After mathematical operator + After mathematical operator + + + + Never before comma + Never before comma + + + + Names + Names + + + + Preferred name wrapper + Preferred name wrapper + + + + Always use name wrapping + Always use name wrapping + + + + Uppercase data type names + Uppercase data type names + + + + Uppercase keywords + Uppercase keywords + + + + Comments + Comments + + + + Preferred comment marker (where possible): + Preferred comment marker (where possible): + + + + Move all comments to the line end + Move all comments to the line end + + + + Line up comments at the line end + Line up comments at the line end + + + + Preview + Preview + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_zh_CN.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_zh_CN.ts new file mode 100644 index 0000000..d836928 --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_zh_CN.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + åç§° + + + + SqlEnterpriseFormatter + + + Indentation + 缩进 + + + + Line up keywords in multi-line queries + 多行查询中æå‡å…³é”®å­— + + + + Indent contents of parenthesis block + 缩进括å·å—的内容 + + + + Tab size: + 制表符宽度: + + + + New lines + 新行 + + + + Before opening parenthesis in column definitions + 列定义左括å·å‰ + + + + After opening parenthesis in column definitions + 列定义左括å·åŽ + + + + Before closing parenthesis in column definitions + åˆ—å®šä¹‰å³æ‹¬å·å‰ + + + + After closing parenthesis in column definitions + åˆ—å®šä¹‰å³æ‹¬å·åŽ + + + + Before opening parenthesis in expressions + 表达å¼å·¦æ‹¬å·å‰ + + + + After opening parenthesis in expressions + 表达å¼å·¦æ‹¬å·åŽ + + + + Before closing parenthesis in expressions + 表达å¼å³æ‹¬å·å‰ + + + + After closing parenthesis in expressions + 表达å¼å³æ‹¬å·åŽ + + + + After JOIN keywords in FROM clause + FROM 语å¥ä¸­ JOIN å…³é”®å­—åŽ + + + + Put each column constraint in CREATE TABLE into new line + CREATE TABLE çš„æ¯ä¸ªåˆ—约æŸå•列一行 + + + + After comma + 逗å·åŽ + + + + After comma in expressions + 表达å¼ä¸­çš„逗å·åŽ + + + + After semicolon + 分å·åŽ + + + + + Never before semicolon + ä¸åœ¨åˆ†å·å‰ + + + + White spaces + 空格 + + + + Before comma in lists + 列表中的逗å·å‰ + + + + After comma in lists + 列表中的逗å·åŽ + + + + Before opening parenthesis + 左括å·å‰ + + + + After opening parenthesis + 左括å·åŽ + + + + Before closing parenthesis + 峿‹¬å·å‰ + + + + After closing parenthesis + 峿‹¬å·åŽ + + + + No space between SQL function name and opening parenthesis + SQL 函数å称与左括å·ä¹‹é—´æ— ç©ºæ ¼ + + + + Before dot operator (in path to database object) + æ•°æ®åº“对象路径的.æ“ä½œç¬¦å‰ + + + + After dot operator (in path to database object) + æ•°æ®åº“对象路径的.æ“ä½œç¬¦åŽ + + + + Before mathematical operator + æ•°å­¦è¿ç®—ç¬¦å‰ + + + + After mathematical operator + æ•°å­¦è¿ç®—ç¬¦åŽ + + + + Never before comma + ä¸åœ¨é€—å·å‰ + + + + Names + åç§° + + + + Preferred name wrapper + 首选å称包裹符 + + + + Always use name wrapping + 始终包裹åç§° + + + + Uppercase data type names + 大写数æ®ç±»åž‹å + + + + Uppercase keywords + 大写关键字 + + + + Comments + 注释 + + + + Preferred comment marker (where possible): + 首选注释标记(如å¯èƒ½ï¼‰ï¼š + + + + Move all comments to the line end + 所有注释移动到行尾 + + + + Line up comments at the line end + æå‡è¡Œå°¾çš„æ³¨é‡Š + + + + Preview + 预览 + + + diff --git a/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_zh_TW.ts b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_zh_TW.ts new file mode 100644 index 0000000..39b3c9f --- /dev/null +++ b/Plugins/SqlEnterpriseFormatter/translations/SqlEnterpriseFormatter_zh_TW.ts @@ -0,0 +1,228 @@ + + + + + QObject + + + + name + example name wrapper + å稱 + + + + SqlEnterpriseFormatter + + + Indentation + 縮排 + + + + Line up keywords in multi-line queries + 多行查詢中æå‡é—œéµå­— + + + + Indent contents of parenthesis block + 縮排括號塊的內容 + + + + Tab size: + 製表符寬度: + + + + New lines + 新行 + + + + Before opening parenthesis in column definitions + åˆ—å®šç¾©å·¦æ‹¬è™Ÿå‰ + + + + After opening parenthesis in column definitions + 列定義左括號後 + + + + Before closing parenthesis in column definitions + åˆ—å®šç¾©å³æ‹¬è™Ÿå‰ + + + + After closing parenthesis in column definitions + åˆ—å®šç¾©å³æ‹¬è™Ÿå¾Œ + + + + Before opening parenthesis in expressions + 表示å¼å·¦æ‹¬è™Ÿå‰ + + + + After opening parenthesis in expressions + 表示å¼å·¦æ‹¬è™Ÿå¾Œ + + + + Before closing parenthesis in expressions + 表示å¼å³æ‹¬è™Ÿå‰ + + + + After closing parenthesis in expressions + 表示å¼å³æ‹¬è™Ÿå¾Œ + + + + After JOIN keywords in FROM clause + FROM 語å¥ä¸­ JOIN é—œéµå­—後 + + + + Put each column constraint in CREATE TABLE into new line + CREATE TABLE çš„æ¯å€‹åˆ—ç´„æŸå–®åˆ—一行 + + + + After comma + 逗號後 + + + + After comma in expressions + 表示å¼ä¸­çš„逗號後 + + + + After semicolon + 分號後 + + + + + Never before semicolon + ä¸åœ¨åˆ†è™Ÿå‰ + + + + White spaces + 空格 + + + + Before comma in lists + æ¸…å–®ä¸­çš„é€—è™Ÿå‰ + + + + After comma in lists + 清單中的逗號後 + + + + Before opening parenthesis + å·¦æ‹¬è™Ÿå‰ + + + + After opening parenthesis + 左括號後 + + + + Before closing parenthesis + 峿‹¬è™Ÿå‰ + + + + After closing parenthesis + 峿‹¬è™Ÿå¾Œ + + + + No space between SQL function name and opening parenthesis + SQL 函å¼å稱與左括號之間無空格 + + + + Before dot operator (in path to database object) + 資料庫物件路徑的.é‹ç®—å­å‰ + + + + After dot operator (in path to database object) + 資料庫物件路徑的.é‹ç®—å­å¾Œ + + + + Before mathematical operator + 數學é‹ç®—å­å‰ + + + + After mathematical operator + 數學é‹ç®—å­å¾Œ + + + + Never before comma + ä¸åœ¨é€—è™Ÿå‰ + + + + Names + å稱 + + + + Preferred name wrapper + 首é¸å稱包裹符 + + + + Always use name wrapping + 始終包裹å稱 + + + + Uppercase data type names + 大寫資料型別å + + + + Uppercase keywords + 大寫關éµå­— + + + + Comments + 註釋 + + + + Preferred comment marker (where possible): + 首é¸è¨»é‡‹æ¨™è¨˜ (如å¯èƒ½): + + + + Move all comments to the line end + 所有註釋移動到行尾 + + + + Line up comments at the line end + æå‡è¡Œå°¾çš„註釋 + + + + Preview + é è¦½ + + + diff --git a/Plugins/SqlExport/SqlExport.pro b/Plugins/SqlExport/SqlExport.pro index 93e4af9..173b5fc 100644 --- a/Plugins/SqlExport/SqlExport.pro +++ b/Plugins/SqlExport/SqlExport.pro @@ -27,31 +27,3 @@ OTHER_FILES += \ RESOURCES += \ sqlexport.qrc - - -TRANSLATIONS += SqlExport_ro_RO.ts \ - SqlExport_de.ts \ - SqlExport_it.ts \ - SqlExport_zh_CN.ts \ - SqlExport_sk.ts \ - SqlExport_ru.ts \ - SqlExport_pt_BR.ts \ - SqlExport_fr.ts \ - SqlExport_es.ts \ - SqlExport_pl.ts - - - - - - - - - - - - - - - - diff --git a/Plugins/SqlExport/SqlExportCommon.ui b/Plugins/SqlExport/SqlExportCommon.ui index 891c26a..7fee728 100644 --- a/Plugins/SqlExport/SqlExportCommon.ui +++ b/Plugins/SqlExport/SqlExportCommon.ui @@ -7,14 +7,24 @@ 0 0 467 - 86 + 125 Form - + + + + Use SQL formatter to format exported SQL statements + + + SqlExport.UseFormatter + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement @@ -34,13 +44,13 @@ - - + + - Use SQL formatter to format exported SQL statements + Add "IF NOT EXISTS" clause to "CREATE" statement - SqlExport.UseFormatter + SqlExport.GenerateIfNotExists diff --git a/Plugins/SqlExport/SqlExportQuery.ui b/Plugins/SqlExport/SqlExportQuery.ui index ff874bd..51569c0 100644 --- a/Plugins/SqlExport/SqlExportQuery.ui +++ b/Plugins/SqlExport/SqlExportQuery.ui @@ -7,57 +7,47 @@ 0 0 467 - 163 + 210 Form - - + + - Use SQL formatter to format exported SQL statements + Include the query in comments - SqlExport.UseFormatter - - - - - - - Table name to use for INSERT statements: + SqlExport.IncludeQueryInComments - - + + - Generate "CREATE TABLE" statement at the begining + Generate "DROP IF EXISTS" statement before "CREATE" statement - SqlExport.GenerateCreateTable + SqlExport.GenerateDrop - - + + - Include the query in comments - - - SqlExport.IncludeQueryInComments + Table name to use for INSERT statements: - - + + - Generate "DROP IF EXISTS" statement before "CREATE" statement + Use SQL formatter to format exported SQL statements - SqlExport.GenerateDrop + SqlExport.UseFormatter @@ -68,6 +58,16 @@ + + + + Generate "CREATE TABLE" statement at the begining + + + SqlExport.GenerateCreateTable + + + @@ -78,6 +78,16 @@ + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + + + SqlExport.GenerateIfNotExists + + + diff --git a/Plugins/SqlExport/SqlExport_de.qm b/Plugins/SqlExport/SqlExport_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/SqlExport/SqlExport_de.qm and /dev/null differ diff --git a/Plugins/SqlExport/SqlExport_de.ts b/Plugins/SqlExport/SqlExport_de.ts deleted file mode 100644 index 5927ee3..0000000 --- a/Plugins/SqlExport/SqlExport_de.ts +++ /dev/null @@ -1,98 +0,0 @@ - - - - - SqlExport - - - -- Results of query: - - - - - -- Table: %1 - - - - - -- Index: %1 - - - - - -- Trigger: %1 - - - - - -- View: %1 - - - - - -- File generated with SQLiteStudio v%1 on %2 - - - - - -- Text encoding used: %1 - - - - - Table name for INSERT statements is mandatory. - - - - - sqlExportCommonConfig - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - - Use SQL formatter to format exported SQL statements - - - - - sqlExportQueryConfig - - - Use SQL formatter to format exported SQL statements - - - - - Table name to use for INSERT statements: - - - - - Generate "CREATE TABLE" statement at the begining - - - - - Include the query in comments - - - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - diff --git a/Plugins/SqlExport/SqlExport_es.qm b/Plugins/SqlExport/SqlExport_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/SqlExport/SqlExport_es.qm and /dev/null differ diff --git a/Plugins/SqlExport/SqlExport_es.ts b/Plugins/SqlExport/SqlExport_es.ts deleted file mode 100644 index 4fe864c..0000000 --- a/Plugins/SqlExport/SqlExport_es.ts +++ /dev/null @@ -1,98 +0,0 @@ - - - - - SqlExport - - - -- Results of query: - - - - - -- Table: %1 - - - - - -- Index: %1 - - - - - -- Trigger: %1 - - - - - -- View: %1 - - - - - -- File generated with SQLiteStudio v%1 on %2 - - - - - -- Text encoding used: %1 - - - - - Table name for INSERT statements is mandatory. - - - - - sqlExportCommonConfig - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - - Use SQL formatter to format exported SQL statements - - - - - sqlExportQueryConfig - - - Use SQL formatter to format exported SQL statements - - - - - Table name to use for INSERT statements: - - - - - Generate "CREATE TABLE" statement at the begining - - - - - Include the query in comments - - - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - diff --git a/Plugins/SqlExport/SqlExport_fr.qm b/Plugins/SqlExport/SqlExport_fr.qm deleted file mode 100644 index a7cdb56..0000000 Binary files a/Plugins/SqlExport/SqlExport_fr.qm and /dev/null differ diff --git a/Plugins/SqlExport/SqlExport_fr.ts b/Plugins/SqlExport/SqlExport_fr.ts deleted file mode 100644 index 817590f..0000000 --- a/Plugins/SqlExport/SqlExport_fr.ts +++ /dev/null @@ -1,98 +0,0 @@ - - - - - SqlExport - - - -- Results of query: - -- Résultats de la requête : - - - - -- Table: %1 - -- Table : %1 - - - - -- Index: %1 - -- Index : %1 - - - - -- Trigger: %1 - -- Déclencheur : %1 - - - - -- View: %1 - -- Vue : %1 - - - - -- File generated with SQLiteStudio v%1 on %2 - -- Fichier généré par SQLiteStudio v%1 sur %2 - - - - -- Text encoding used: %1 - -- Encodage texte utilisé : %1 - - - - Table name for INSERT statements is mandatory. - Nom de table pour la déclaration INSERT est obligatoire. - - - - sqlExportCommonConfig - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - Genere "DROP IF EXISTS" Déclaration avant "CREATE"déclaration - - - - Format DDL statements only (excludes "INSERT" statements) - Format DDL déclarations seulement (déclaration exclus "INSERT") - - - - Use SQL formatter to format exported SQL statements - Utilisez le formatage SQL pour formater des déclarations SQL exportables - - - - sqlExportQueryConfig - - - Use SQL formatter to format exported SQL statements - Utilisez le formatage SQL pour formater des déclarations SQL exportables - - - - Table name to use for INSERT statements: - Nom de table pour utiliser la déclaration INSERT : - - - - Generate "CREATE TABLE" statement at the begining - Genre la déclaration "CREATE TABLE" au debut - - - - Include the query in comments - Inclus la requête dans les commentaires - - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - Génere la déclaration "DROP IF EXISTS" avant la déclaration "CREATE" - - - - Format DDL statements only (excludes "INSERT" statements) - Format DDL seulement déclaration (exclus déclaration "INSERT") - - - diff --git a/Plugins/SqlExport/SqlExport_it.qm b/Plugins/SqlExport/SqlExport_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/SqlExport/SqlExport_it.qm and /dev/null differ diff --git a/Plugins/SqlExport/SqlExport_it.ts b/Plugins/SqlExport/SqlExport_it.ts deleted file mode 100644 index 4178684..0000000 --- a/Plugins/SqlExport/SqlExport_it.ts +++ /dev/null @@ -1,98 +0,0 @@ - - - - - SqlExport - - - -- Results of query: - - - - - -- Table: %1 - - - - - -- Index: %1 - - - - - -- Trigger: %1 - - - - - -- View: %1 - - - - - -- File generated with SQLiteStudio v%1 on %2 - - - - - -- Text encoding used: %1 - - - - - Table name for INSERT statements is mandatory. - - - - - sqlExportCommonConfig - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - - Use SQL formatter to format exported SQL statements - - - - - sqlExportQueryConfig - - - Use SQL formatter to format exported SQL statements - - - - - Table name to use for INSERT statements: - - - - - Generate "CREATE TABLE" statement at the begining - - - - - Include the query in comments - - - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - diff --git a/Plugins/SqlExport/SqlExport_pl.qm b/Plugins/SqlExport/SqlExport_pl.qm deleted file mode 100644 index 5647c1c..0000000 Binary files a/Plugins/SqlExport/SqlExport_pl.qm and /dev/null differ diff --git a/Plugins/SqlExport/SqlExport_pl.ts b/Plugins/SqlExport/SqlExport_pl.ts deleted file mode 100644 index 504fe2d..0000000 --- a/Plugins/SqlExport/SqlExport_pl.ts +++ /dev/null @@ -1,99 +0,0 @@ - - - - - SqlExport - - - -- Results of query: - -- Wyniki zapytania: - - - - -- Table: %1 - -- Tabela: %1 - - - - -- Index: %1 - -- Indeks: %1 - - - - -- Trigger: %1 - -- Wyzwalacz: %1 - - - - -- View: %1 - -- Widok: %1 - - - - -- File generated with SQLiteStudio v%1 on %2 - -- Plik wygenerowany przez SQLiteStudio v%1 dnia %2 - - - - -- Text encoding used: %1 - -- Użyte kodowanie tekstu: %1 - - - - Table name for INSERT statements is mandatory. - Nazwa tabeli dla zapytania INSERT jest obowiÄ…zkowa. - - - - sqlExportCommonConfig - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - Generuj zapytanie "DROP IF EXISTS" przed zapytaniem "CREATE" - - - - Format DDL statements only (excludes "INSERT" statements) - Formatuj tylko zapytania DDL (wyklucza zapytania "INSERT") - - - - Use SQL formatter to format exported SQL statements - Użyj formatera SQL do formatowania eksportowanych zapytaÅ„ SQL - - - - sqlExportQueryConfig - - - Use SQL formatter to format exported SQL statements - Użyj formatera SQL do formatowania eksportowanych zapytaÅ„ SQL - - - - Table name to use for INSERT statements: - Table name to use for insert statements: - Nazwa tabeli dla zapytaÅ„ INSERT: - - - - Generate "CREATE TABLE" statement at the begining - Generuj zapytanie "CREATE TABLE" na poczÄ…tku - - - - Include the query in comments - Dodaj zapytanie w komentarzach - - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - Generuj zapytanie "DROP IF EXISTS" przed zapytaniem "CREATE" - - - - Format DDL statements only (excludes "INSERT" statements) - Formatuj tylko zapytania DDL (wyklucza zapytania "INSERT") - - - diff --git a/Plugins/SqlExport/SqlExport_pt_BR.qm b/Plugins/SqlExport/SqlExport_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/SqlExport/SqlExport_pt_BR.qm and /dev/null differ diff --git a/Plugins/SqlExport/SqlExport_pt_BR.ts b/Plugins/SqlExport/SqlExport_pt_BR.ts deleted file mode 100644 index 1941e03..0000000 --- a/Plugins/SqlExport/SqlExport_pt_BR.ts +++ /dev/null @@ -1,98 +0,0 @@ - - - - - SqlExport - - - -- Results of query: - - - - - -- Table: %1 - - - - - -- Index: %1 - - - - - -- Trigger: %1 - - - - - -- View: %1 - - - - - -- File generated with SQLiteStudio v%1 on %2 - - - - - -- Text encoding used: %1 - - - - - Table name for INSERT statements is mandatory. - - - - - sqlExportCommonConfig - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - - Use SQL formatter to format exported SQL statements - - - - - sqlExportQueryConfig - - - Use SQL formatter to format exported SQL statements - - - - - Table name to use for INSERT statements: - - - - - Generate "CREATE TABLE" statement at the begining - - - - - Include the query in comments - - - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - diff --git a/Plugins/SqlExport/SqlExport_ro_RO.qm b/Plugins/SqlExport/SqlExport_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/SqlExport/SqlExport_ro_RO.qm and /dev/null differ diff --git a/Plugins/SqlExport/SqlExport_ro_RO.ts b/Plugins/SqlExport/SqlExport_ro_RO.ts deleted file mode 100644 index 6bba8e5..0000000 --- a/Plugins/SqlExport/SqlExport_ro_RO.ts +++ /dev/null @@ -1,98 +0,0 @@ - - - - - SqlExport - - - -- Results of query: - - - - - -- Table: %1 - - - - - -- Index: %1 - - - - - -- Trigger: %1 - - - - - -- View: %1 - - - - - -- File generated with SQLiteStudio v%1 on %2 - - - - - -- Text encoding used: %1 - - - - - Table name for INSERT statements is mandatory. - - - - - sqlExportCommonConfig - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - - Use SQL formatter to format exported SQL statements - - - - - sqlExportQueryConfig - - - Use SQL formatter to format exported SQL statements - - - - - Table name to use for INSERT statements: - - - - - Generate "CREATE TABLE" statement at the begining - - - - - Include the query in comments - - - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - diff --git a/Plugins/SqlExport/SqlExport_ru.qm b/Plugins/SqlExport/SqlExport_ru.qm deleted file mode 100644 index bc56dd6..0000000 Binary files a/Plugins/SqlExport/SqlExport_ru.qm and /dev/null differ diff --git a/Plugins/SqlExport/SqlExport_ru.ts b/Plugins/SqlExport/SqlExport_ru.ts deleted file mode 100644 index 87ccb4b..0000000 --- a/Plugins/SqlExport/SqlExport_ru.ts +++ /dev/null @@ -1,98 +0,0 @@ - - - - - SqlExport - - - -- Results of query: - -- Результаты запроÑа: - - - - -- Table: %1 - -- Таблица: %1 - - - - -- Index: %1 - -- ИндекÑ: %1 - - - - -- Trigger: %1 - -- Триггер: %1 - - - - -- View: %1 - -- ПредÑтавление: %1 - - - - -- File generated with SQLiteStudio v%1 on %2 - -- Файл Ñгенерирован Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SQLiteStudio v%1 в %2 - - - - -- Text encoding used: %1 - -- ИÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð½Ð°Ñ ÐºÐ¾Ð´Ð¸Ñ€Ð¾Ð²ÐºÐ° текÑта: %1 - - - - Table name for INSERT statements is mandatory. - Ð˜Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ Ð´Ð»Ñ ÐºÐ¾Ð½Ñтрукций INSERT обÑзательно. - - - - sqlExportCommonConfig - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - Сгенерировать конÑтрукцию "DROP IF EXISTS" перед конÑтрукцией "CREATE" - - - - Format DDL statements only (excludes "INSERT" statements) - Формировать только конÑтрукции DDL (иÑÐºÐ»ÑŽÑ‡Ð°Ñ ÐºÐ¾Ð½Ñтрукции "INSERT") - - - - Use SQL formatter to format exported SQL statements - ИÑпользовать модуль Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ SQL Ð´Ð»Ñ ÑкÑпортируемых конÑтрукций SQL - - - - sqlExportQueryConfig - - - Use SQL formatter to format exported SQL statements - ИÑпользовать модуль Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ SQL Ð´Ð»Ñ ÑкÑпортируемых конÑтрукций SQL - - - - Table name to use for INSERT statements: - Ð˜Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² конÑтрукции INSERT: - - - - Generate "CREATE TABLE" statement at the begining - Сгенерировать конÑтрукцию "CREATE TABLE" в начале - - - - Include the query in comments - Ð’Ñтавить текÑÑ‚ запроÑа в виде ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ð¸Ñ - - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - Сгенерировать конÑтрукцию "DROP IF EXISTS" перед конÑтрукцией "CREATE" - - - - Format DDL statements only (excludes "INSERT" statements) - Формировать только конÑтрукции DDL (иÑÐºÐ»ÑŽÑ‡Ð°Ñ ÐºÐ¾Ð½Ñтрукции "INSERT") - - - diff --git a/Plugins/SqlExport/SqlExport_sk.qm b/Plugins/SqlExport/SqlExport_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/Plugins/SqlExport/SqlExport_sk.qm and /dev/null differ diff --git a/Plugins/SqlExport/SqlExport_sk.ts b/Plugins/SqlExport/SqlExport_sk.ts deleted file mode 100644 index b74dda4..0000000 --- a/Plugins/SqlExport/SqlExport_sk.ts +++ /dev/null @@ -1,98 +0,0 @@ - - - - - SqlExport - - - -- Results of query: - - - - - -- Table: %1 - - - - - -- Index: %1 - - - - - -- Trigger: %1 - - - - - -- View: %1 - - - - - -- File generated with SQLiteStudio v%1 on %2 - - - - - -- Text encoding used: %1 - - - - - Table name for INSERT statements is mandatory. - - - - - sqlExportCommonConfig - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - - Use SQL formatter to format exported SQL statements - - - - - sqlExportQueryConfig - - - Use SQL formatter to format exported SQL statements - - - - - Table name to use for INSERT statements: - - - - - Generate "CREATE TABLE" statement at the begining - - - - - Include the query in comments - - - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - diff --git a/Plugins/SqlExport/SqlExport_zh_CN.qm b/Plugins/SqlExport/SqlExport_zh_CN.qm deleted file mode 100644 index 2a6959c..0000000 Binary files a/Plugins/SqlExport/SqlExport_zh_CN.qm and /dev/null differ diff --git a/Plugins/SqlExport/SqlExport_zh_CN.ts b/Plugins/SqlExport/SqlExport_zh_CN.ts deleted file mode 100644 index 8ee787a..0000000 --- a/Plugins/SqlExport/SqlExport_zh_CN.ts +++ /dev/null @@ -1,98 +0,0 @@ - - - - - SqlExport - - - -- Results of query: - -- 执行结果: - - - - -- Table: %1 - -- 表:%1 - - - - -- Index: %1 - -- 索引:%1 - - - - -- Trigger: %1 - -- 触å‘器:%1 - - - - -- View: %1 - -- 视图:%1 - - - - -- File generated with SQLiteStudio v%1 on %2 - -- ç”±SQLiteStudio v%1 产生的文件 %2 - - - - -- Text encoding used: %1 - -- 文本编ç ï¼š%1 - - - - Table name for INSERT statements is mandatory. - - - - - sqlExportCommonConfig - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - - Use SQL formatter to format exported SQL statements - - - - - sqlExportQueryConfig - - - Use SQL formatter to format exported SQL statements - - - - - Table name to use for INSERT statements: - - - - - Generate "CREATE TABLE" statement at the begining - - - - - Include the query in comments - - - - - Generate "DROP IF EXISTS" statement before "CREATE" statement - - - - - Format DDL statements only (excludes "INSERT" statements) - - - - diff --git a/Plugins/SqlExport/sqlexport.cpp b/Plugins/SqlExport/sqlexport.cpp index 331bfd6..b47706d 100644 --- a/Plugins/SqlExport/sqlexport.cpp +++ b/Plugins/SqlExport/sqlexport.cpp @@ -43,9 +43,11 @@ bool SqlExport::beforeExportQueryResults(const QString& query, QListdisplayName); this->columns = colDefs.join(", "); @@ -64,7 +66,7 @@ bool SqlExport::beforeExportQueryResults(const QString& query, QListcolumns + ");"; + QString ddl = createDdl.arg(cfg.SqlExport.GenerateIfNotExists.get() ? ifNotExists : "", theTable, this->columns); writeln(""); if (cfg.SqlExport.GenerateDrop.get()) @@ -89,7 +91,7 @@ bool SqlExport::exportTable(const QString& database, const QString& table, const UNUSED(providedData); tableGeneratedColumns.clear(); - for (SqliteCreateTable::Column* col : createTable->columns) + for (SqliteCreateTable::Column*& col : createTable->columns) { if (col->hasConstraint(SqliteCreateTable::Column::Constraint::GENERATED)) tableGeneratedColumns << col->name; @@ -108,6 +110,7 @@ bool SqlExport::exportVirtualTable(const QString& database, const QString& table bool SqlExport::exportTable(const QString& database, const QString& table, const QStringList& columnNames, const QString& ddl) { static_qstring(dropDdl, "DROP TABLE IF EXISTS %1;"); + static_qstring(ifNotExists, " IF NOT EXISTS"); generatedColumnIndexes.clear(); QStringList colList; @@ -142,7 +145,14 @@ bool SqlExport::exportTable(const QString& database, const QString& table, const if (cfg.SqlExport.GenerateDrop.get()) writeln(formatQuery(dropDdl.arg(theTable))); - writeln(formatQuery(ddl)); + QString finalDdl = ddl; + if (cfg.SqlExport.GenerateIfNotExists.get()) + { + int idx = finalDdl.indexOf("table", 0, Qt::CaseInsensitive); + finalDdl.insert(idx + 5, ifNotExists); + } + + writeln(formatQuery(finalDdl)); return true; } @@ -178,6 +188,7 @@ bool SqlExport::exportIndex(const QString& database, const QString& name, const { UNUSED(createIndex); static_qstring(dropDdl, "DROP INDEX IF EXISTS %1;"); + static_qstring(ifNotExists, " IF NOT EXISTS"); QString index = getNameForObject(database, name, false); writeln(""); @@ -187,7 +198,14 @@ bool SqlExport::exportIndex(const QString& database, const QString& name, const if (cfg.SqlExport.GenerateDrop.get()) writeln(formatQuery(dropDdl.arg(fullName))); - writeln(formatQuery(ddl)); + QString finalDdl = ddl; + if (cfg.SqlExport.GenerateIfNotExists.get()) + { + int idx = finalDdl.indexOf("index", 0, Qt::CaseInsensitive); + finalDdl.insert(idx + 5, ifNotExists); + } + + writeln(formatQuery(finalDdl)); return true; } @@ -195,6 +213,7 @@ bool SqlExport::exportTrigger(const QString& database, const QString& name, cons { UNUSED(createTrigger); static_qstring(dropDdl, "DROP TRIGGER IF EXISTS %1;"); + static_qstring(ifNotExists, " IF NOT EXISTS"); QString trig = getNameForObject(database, name, false); writeln(""); @@ -204,7 +223,14 @@ bool SqlExport::exportTrigger(const QString& database, const QString& name, cons if (cfg.SqlExport.GenerateDrop.get()) writeln(dropDdl.arg(fullName)); - writeln(formatQuery(formatQuery(ddl))); + QString finalDdl = ddl; + if (cfg.SqlExport.GenerateIfNotExists.get()) + { + int idx = finalDdl.indexOf("trigger", 0, Qt::CaseInsensitive); + finalDdl.insert(idx + 7, ifNotExists); + } + + writeln(formatQuery(finalDdl)); return true; } @@ -212,6 +238,7 @@ bool SqlExport::exportView(const QString& database, const QString& name, const Q { UNUSED(createView); static_qstring(dropDdl, "DROP VIEW IF EXISTS %1;"); + static_qstring(ifNotExists, " IF NOT EXISTS"); QString view = getNameForObject(database, name, false); writeln(""); @@ -221,7 +248,13 @@ bool SqlExport::exportView(const QString& database, const QString& name, const Q if (cfg.SqlExport.GenerateDrop.get()) writeln(dropDdl.arg(fullName)); - writeln(formatQuery(formatQuery(ddl))); + QString finalDdl = ddl; + if (cfg.SqlExport.GenerateIfNotExists.get()) + { + int idx = finalDdl.indexOf("view", 0, Qt::CaseInsensitive); + finalDdl.insert(idx + 4, ifNotExists); + } + writeln(formatQuery(finalDdl)); return true; } @@ -315,18 +348,22 @@ void SqlExport::validateOptions() { bool generateCreate = cfg.SqlExport.GenerateCreateTable.get(); EXPORT_MANAGER->updateVisibilityAndEnabled(cfg.SqlExport.GenerateDrop, true, generateCreate); + EXPORT_MANAGER->updateVisibilityAndEnabled(cfg.SqlExport.GenerateIfNotExists, true, generateCreate); if (!generateCreate) + { cfg.SqlExport.GenerateDrop.set(false); + cfg.SqlExport.GenerateIfNotExists.set(false); + } } } bool SqlExport::init() { - Q_INIT_RESOURCE(sqlexport); + SQLS_INIT_RESOURCE(sqlexport); return GenericExportPlugin::init(); } void SqlExport::deinit() { - Q_CLEANUP_RESOURCE(sqlexport); + SQLS_CLEANUP_RESOURCE(sqlexport); } diff --git a/Plugins/SqlExport/sqlexport.h b/Plugins/SqlExport/sqlexport.h index 49e2669..3238f8a 100644 --- a/Plugins/SqlExport/sqlexport.h +++ b/Plugins/SqlExport/sqlexport.h @@ -12,6 +12,7 @@ CFG_CATEGORIES(SqlExportConfig, CFG_ENTRY(bool, IncludeQueryInComments, true) CFG_ENTRY(bool, UseFormatter, false) CFG_ENTRY(bool, FormatDdlsOnly, false) + CFG_ENTRY(bool, GenerateIfNotExists, true) CFG_ENTRY(bool, GenerateDrop, false) ) ) diff --git a/Plugins/SqlExport/sqlexport.json b/Plugins/SqlExport/sqlexport.json index 8b8898c..73dfe11 100644 --- a/Plugins/SqlExport/sqlexport.json +++ b/Plugins/SqlExport/sqlexport.json @@ -2,6 +2,6 @@ "type": "ExportPlugin", "title": "SQL export", "description": "Provides SQL format for exporting", - "version": 10102, + "version": 10103, "author": "SalSoft" } diff --git a/Plugins/SqlExport/sqlexport.qrc b/Plugins/SqlExport/sqlexport.qrc index 5a7d22c..447617f 100644 --- a/Plugins/SqlExport/sqlexport.qrc +++ b/Plugins/SqlExport/sqlexport.qrc @@ -3,19 +3,4 @@ SqlExportQuery.ui SqlExportCommon.ui - - SqlExport_ro_RO.qm - SqlExport_de.qm - - - SqlExport_pl.qm - SqlExport_ru.qm - SqlExport_fr.qm - SqlExport_sk.qm - SqlExport_zh_CN.qm - - - - - diff --git a/Plugins/SqlExport/translations/SqlExport.ts b/Plugins/SqlExport/translations/SqlExport.ts new file mode 100644 index 0000000..8c82ec7 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + + + + + -- Table: %1 + + + + + -- Index: %1 + + + + + -- Trigger: %1 + + + + + -- View: %1 + + + + + -- File generated with SQLiteStudio v%1 on %2 + + + + + -- Text encoding used: %1 + + + + + Table name for INSERT statements is mandatory. + + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + + Format DDL statements only (excludes "INSERT" statements) + + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + + Use SQL formatter to format exported SQL statements + + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + + + + + Table name to use for INSERT statements: + + + + + Generate "CREATE TABLE" statement at the begining + + + + + Include the query in comments + + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + + Format DDL statements only (excludes "INSERT" statements) + + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + diff --git a/Plugins/SqlExport/translations/SqlExport_af_ZA.ts b/Plugins/SqlExport/translations/SqlExport_af_ZA.ts new file mode 100644 index 0000000..861a143 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_af_ZA.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_ar_SA.ts b/Plugins/SqlExport/translations/SqlExport_ar_SA.ts new file mode 100644 index 0000000..054e7f7 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_ar_SA.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_ca_ES.ts b/Plugins/SqlExport/translations/SqlExport_ca_ES.ts new file mode 100644 index 0000000..810ae7e --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_ca_ES.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_cs_CZ.ts b/Plugins/SqlExport/translations/SqlExport_cs_CZ.ts new file mode 100644 index 0000000..3cfca6b --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_cs_CZ.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_da_DK.ts b/Plugins/SqlExport/translations/SqlExport_da_DK.ts new file mode 100644 index 0000000..25403b6 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_da_DK.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_de_DE.ts b/Plugins/SqlExport/translations/SqlExport_de_DE.ts new file mode 100644 index 0000000..38dd8d6 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_de_DE.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Ergebnisse der Abfrage: + + + + -- Table: %1 + -- Tabelle: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- Datei mit SQLiteStudio v%1 am %2 erstellt + + + + -- Text encoding used: %1 + -- verwendete Textkodierung: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_el_GR.ts b/Plugins/SqlExport/translations/SqlExport_el_GR.ts new file mode 100644 index 0000000..b87426e --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_el_GR.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_en_US.ts b/Plugins/SqlExport/translations/SqlExport_en_US.ts new file mode 100644 index 0000000..69f7b38 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_en_US.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_es_ES.ts b/Plugins/SqlExport/translations/SqlExport_es_ES.ts new file mode 100644 index 0000000..0813924 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_es_ES.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Resultados de la consulta: + + + + -- Table: %1 + -- Tabla: %1 + + + + -- Index: %1 + -- Ãndice: %1 + + + + -- Trigger: %1 + -- Disparador: %1 + + + + -- View: %1 + -- Vista: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- Archivo generado con SQLiteStudio v%1 el %2 + + + + -- Text encoding used: %1 + -- Codificación de texto usada: %1 + + + + Table name for INSERT statements is mandatory. + Se requiere el nombre de la tabla para las sentencias INSERT. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generar sentencia "DROP IF EXISTS" antes de la sentencia "CREATE" + + + + Format DDL statements only (excludes "INSERT" statements) + Formatear sentencias DDL solamente (excluye sentencias "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Usar el formateador SQL para dar formato a las sentencias SQL exportadas + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Usar el formateador SQL para dar formato a las sentencias SQL exportadas + + + + Table name to use for INSERT statements: + Nombre de tabla a usar para sentencias INSERT: + + + + Generate "CREATE TABLE" statement at the begining + Generar sentencia "CREATE TABLE" al comienzo + + + + Include the query in comments + Incluir la consulta en los comentarios + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generar sentencia "DROP IF EXISTS" antes de la sentencia "CREATE" + + + + Format DDL statements only (excludes "INSERT" statements) + Formatear solamente las sentencias DDL (excluye las sentencias "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_fa_IR.ts b/Plugins/SqlExport/translations/SqlExport_fa_IR.ts new file mode 100644 index 0000000..c4ec671 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_fa_IR.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_fi_FI.ts b/Plugins/SqlExport/translations/SqlExport_fi_FI.ts new file mode 100644 index 0000000..8e60a27 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_fi_FI.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_fr_FR.ts b/Plugins/SqlExport/translations/SqlExport_fr_FR.ts new file mode 100644 index 0000000..6c20356 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_fr_FR.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Résultats de la requête : + + + + -- Table: %1 + -- Tableau : %1 + + + + -- Index: %1 + -- Index : %1 + + + + -- Trigger: %1 + -- Déclencheur : %1 + + + + -- View: %1 + -- Vue : %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- Fichier généré par SQLiteStudio v%1 sur %2 + + + + -- Text encoding used: %1 + -- Encodage texte utilisé : %1 + + + + Table name for INSERT statements is mandatory. + Nom du tableau pour l'instruction INSERT est obligatoire. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Genere "DROP IF EXISTS" Déclaration avant "CREATE"déclaration + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL déclarations seulement (déclaration exclus "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Ajouter la clause "SI N'EXISTE PAS" à l'instruction "CRÉER" + + + + Use SQL formatter to format exported SQL statements + Utiliser le formatage SQL pour formater les instructions SQL exportées + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Utiliser le formatage SQL pour formater les instructions SQL exportées + + + + Table name to use for INSERT statements: + Nom du tableau à utiliser pour l'instruction INSERT : + + + + Generate "CREATE TABLE" statement at the begining + Générer une instruction "CREATE TABLE" au début + + + + Include the query in comments + Inclus la requête dans les commentaires + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Génere la déclaration "DROP IF EXISTS" avant la déclaration "CREATE" + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL seulement déclaration (exclus déclaration "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Ajouter la clause "SI N'EXISTE PAS" à l'instruction "CRÉER" + + + diff --git a/Plugins/SqlExport/translations/SqlExport_he_IL.ts b/Plugins/SqlExport/translations/SqlExport_he_IL.ts new file mode 100644 index 0000000..c1a8cab --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_he_IL.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- תוצ×ות ש×ילתה: + + + + -- Table: %1 + -- טבלה: %1 + + + + -- Index: %1 + -- מִפְתֵּחַ: %1 + + + + -- Trigger: %1 + -- מַזְנֵק: %1 + + + + -- View: %1 + -- מצג: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- המסמך חולל ב×מצעות SQLiteStudio v%1 ב %2 + + + + -- Text encoding used: %1 + קידוד מלל ב×מצעות: %1 + + + + Table name for INSERT statements is mandatory. + ×©× ×”×˜×‘×œ×” להצהרות INSERT ×”×•× ×—×•×‘×”. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + ×©× ×”×˜×‘×œ×” לשימוש להצהרות INSERT: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + הכללת הש×ילתה בהערות + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_hu_HU.ts b/Plugins/SqlExport/translations/SqlExport_hu_HU.ts new file mode 100644 index 0000000..34fe5dd --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_hu_HU.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_it_IT.ts b/Plugins/SqlExport/translations/SqlExport_it_IT.ts new file mode 100644 index 0000000..4db8690 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_it_IT.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Risultati della query: + + + + -- Table: %1 + -- Tabella: %1 + + + + -- Index: %1 + -- Indice: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- Vista: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generato con SQLiteStudio v%1 su %2 + + + + -- Text encoding used: %1 + -- Codifica del testo utilizzata: %1 + + + + Table name for INSERT statements is mandatory. + Il nome della tabella per le istruzioni INSERT è obbligatorio. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Genera l'istruzione "DROP IF EXISTS" prima dell'istruzione "CREATE" + + + + Format DDL statements only (excludes "INSERT" statements) + Forma solo dichiarazioni DDL (escludendo dichiarazioni "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Aggiungi la clausola "IF NOT EXISTS" all"istruzione "CREATE" + + + + Use SQL formatter to format exported SQL statements + Usa il formattatore SQL per formattare le istruzioni SQL esportate + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Usa il formattatore SQL per formattare le istruzioni SQL esportate + + + + Table name to use for INSERT statements: + Nome tabella da usare per le istruzioni INSERT: + + + + Generate "CREATE TABLE" statement at the begining + Genera dichiarazione "CREATE TABLE" all'inizio + + + + Include the query in comments + Includi la query nei commenti + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Genera l'istruzione "DROP IF EXISTS" prima dell'istruzione "CREATE" + + + + Format DDL statements only (excludes "INSERT" statements) + Forma solo dichiarazioni DDL (escludendo dichiarazioni "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Aggiungi la clausola "IF NOT EXISTS" all"istruzione "CREATE" + + + diff --git a/Plugins/SqlExport/translations/SqlExport_ja_JP.ts b/Plugins/SqlExport/translations/SqlExport_ja_JP.ts new file mode 100644 index 0000000..60baab4 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_ja_JP.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_kaa.ts b/Plugins/SqlExport/translations/SqlExport_kaa.ts new file mode 100644 index 0000000..15311ee --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_kaa.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_ko_KR.ts b/Plugins/SqlExport/translations/SqlExport_ko_KR.ts new file mode 100644 index 0000000..d8a40df --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_ko_KR.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_nl_NL.ts b/Plugins/SqlExport/translations/SqlExport_nl_NL.ts new file mode 100644 index 0000000..8c35b1b --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_nl_NL.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_no_NO.ts b/Plugins/SqlExport/translations/SqlExport_no_NO.ts new file mode 100644 index 0000000..ddbfe2f --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_no_NO.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_pl_PL.ts b/Plugins/SqlExport/translations/SqlExport_pl_PL.ts new file mode 100644 index 0000000..a018f0e --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_pl_PL.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Wyniki zapytania: + + + + -- Table: %1 + -- Tabela: %1 + + + + -- Index: %1 + -- Indeks: %1 + + + + -- Trigger: %1 + -- Wyzwalacz: %1 + + + + -- View: %1 + -- Widok: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- Plik wygenerowany przez SQLiteStudio v%1 dnia %2 + + + + -- Text encoding used: %1 + -- Użyte kodowanie tekstu: %1 + + + + Table name for INSERT statements is mandatory. + Nazwa tabeli dla zapytania INSERT jest obowiÄ…zkowa. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generuj zapytanie "DROP IF EXISTS" przed zapytaniem "CREATE" + + + + Format DDL statements only (excludes "INSERT" statements) + Formatuj tylko zapytania DDL (wyklucza zapytania "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Dodaj klauzulÄ™ "IF NOT EXISTS" do instrukcji "CREATE" + + + + Use SQL formatter to format exported SQL statements + Użyj formatera SQL do formatowania eksportowanych zapytaÅ„ SQL + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Użyj formatera SQL do formatowania eksportowanych zapytaÅ„ SQL + + + + Table name to use for INSERT statements: + Nazwa tabeli dla zapytaÅ„ INSERT: + + + + Generate "CREATE TABLE" statement at the begining + Generuj zapytanie "CREATE TABLE" na poczÄ…tku + + + + Include the query in comments + Dodaj zapytanie w komentarzach + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generuj zapytanie "DROP IF EXISTS" przed zapytaniem "CREATE" + + + + Format DDL statements only (excludes "INSERT" statements) + Formatuj tylko zapytania DDL (wyklucza zapytania "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Dodaj klauzulÄ™ "IF NOT EXISTS" do instrukcji "CREATE" + + + diff --git a/Plugins/SqlExport/translations/SqlExport_pt_BR.ts b/Plugins/SqlExport/translations/SqlExport_pt_BR.ts new file mode 100644 index 0000000..4f2a403 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_pt_BR.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Resultados da consulta: + + + + -- Table: %1 + -- Tabela: %1 + + + + -- Index: %1 + -- Ãndice: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- Visualizar: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- Arquivo gerado com SQLiteStudio v%1 em %2 + + + + -- Text encoding used: %1 + -- Codificação de texto usada: %1 + + + + Table name for INSERT statements is mandatory. + O nome da tabela para instruções INSERT é obrigatório. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Gerar "DROP IF EXISTS" declaração antes "CREATE" instrução + + + + Format DDL statements only (excludes "INSERT" statements) + Formatar declarações DDL apenas (excluir declarações "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Adicionar " IF NOT EXISTS " cláusula para " CREATE " declaração + + + + Use SQL formatter to format exported SQL statements + Use o formatador SQL para formatar as instruções SQL exportadas + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use o formatador SQL para formatar as instruções SQL exportadas + + + + Table name to use for INSERT statements: + Nome da tabela a ser usada para instruções INSERT: + + + + Generate "CREATE TABLE" statement at the begining + Gerar "CREATE TABLE" declaração no início + + + + Include the query in comments + Incluir a consulta nos comentários + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Gerar declaração "DROP IF EXISTS" antes da instrução "CREATE" + + + + Format DDL statements only (excludes "INSERT" statements) + Formatar apenas declarações DDL (excluir declarações "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Adicionar " IF NOT EXISTS " cláusula para " CREATE " declaração + + + diff --git a/Plugins/SqlExport/translations/SqlExport_pt_PT.ts b/Plugins/SqlExport/translations/SqlExport_pt_PT.ts new file mode 100644 index 0000000..dd68290 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_pt_PT.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_ro_RO.ts b/Plugins/SqlExport/translations/SqlExport_ro_RO.ts new file mode 100644 index 0000000..80ae99e --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_ro_RO.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_ru_RU.ts b/Plugins/SqlExport/translations/SqlExport_ru_RU.ts new file mode 100644 index 0000000..88b9cb9 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_ru_RU.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Результаты запроÑа: + + + + -- Table: %1 + -- Таблица: %1 + + + + -- Index: %1 + -- ИндекÑ: %1 + + + + -- Trigger: %1 + -- Триггер: %1 + + + + -- View: %1 + -- ПредÑтавление: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- Файл Ñгенерирован Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SQLiteStudio v%1 в %2 + + + + -- Text encoding used: %1 + -- ИÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð½Ð°Ñ ÐºÐ¾Ð´Ð¸Ñ€Ð¾Ð²ÐºÐ° текÑта: %1 + + + + Table name for INSERT statements is mandatory. + Ð˜Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ Ð´Ð»Ñ ÐºÐ¾Ð½Ñтрукций INSERT обÑзательно. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Сгенерировать конÑтрукцию "DROP IF EXISTS" перед конÑтрукцией "CREATE" + + + + Format DDL statements only (excludes "INSERT" statements) + Формировать только конÑтрукции DDL (иÑÐºÐ»ÑŽÑ‡Ð°Ñ ÐºÐ¾Ð½Ñтрукции "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Добавить оператор "IF NOT EXISTS" в конÑтрукцию "CREATE" + + + + Use SQL formatter to format exported SQL statements + ИÑпользовать модуль Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ SQL Ð´Ð»Ñ ÑкÑпортируемых конÑтрукций SQL + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + ИÑпользовать модуль Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ SQL Ð´Ð»Ñ ÑкÑпортируемых конÑтрукций SQL + + + + Table name to use for INSERT statements: + Ð˜Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² конÑтрукции INSERT: + + + + Generate "CREATE TABLE" statement at the begining + Сгенерировать конÑтрукцию "CREATE TABLE" в начале + + + + Include the query in comments + Ð’Ñтавить текÑÑ‚ запроÑа в виде ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ð¸Ñ + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Сгенерировать конÑтрукцию "DROP IF EXISTS" перед конÑтрукцией "CREATE" + + + + Format DDL statements only (excludes "INSERT" statements) + Формировать только конÑтрукции DDL (иÑÐºÐ»ÑŽÑ‡Ð°Ñ ÐºÐ¾Ð½Ñтрукции "INSERT") + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Добавить оператор "IF NOT EXISTS" в конÑтрукцию "CREATE" + + + diff --git a/Plugins/SqlExport/translations/SqlExport_sk_SK.ts b/Plugins/SqlExport/translations/SqlExport_sk_SK.ts new file mode 100644 index 0000000..84cb75a --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_sk_SK.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_sr_SP.ts b/Plugins/SqlExport/translations/SqlExport_sr_SP.ts new file mode 100644 index 0000000..bc82a6b --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_sr_SP.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_sv_SE.ts b/Plugins/SqlExport/translations/SqlExport_sv_SE.ts new file mode 100644 index 0000000..fcfea26 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_sv_SE.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_tr_TR.ts b/Plugins/SqlExport/translations/SqlExport_tr_TR.ts new file mode 100644 index 0000000..1d18462 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_tr_TR.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Sorgu sonuçları: + + + + -- Table: %1 + -- Tablo: %1 + + + + -- Index: %1 + -- Indeks: %1 + + + + -- Trigger: %1 + -- Tetikleyici: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- SQLiteStudio ile üretilen dosya %2 üzerinde v%1 + + + + -- Text encoding used: %1 + -- Kullanılan metin kodlaması: %1 + + + + Table name for INSERT statements is mandatory. + INSERT komutlarında tablo adı zorunludur. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + "CREATE" komutundan önce "DROP IF EXISTS" oluÅŸtur + + + + Format DDL statements only (excludes "INSERT" statements) + Sadece DDL komutlarını biçimlendir("INSERT" komutu hariç) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Çıkartılan SQL komutları için SQL biçimlendirmesini kullan + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Çıkartılan SQL komutları için SQL biçimlendirmesini kullan + + + + Table name to use for INSERT statements: + INSERT komutları için kullanılacak tablo adı: + + + + Generate "CREATE TABLE" statement at the begining + En baÅŸta "CREATE TABLE" komutlarını üret + + + + Include the query in comments + Sorguyu yorumlara ekle + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + "CREATE" komutundan önce "DROP IF EXISTS" komutunu üret + + + + Format DDL statements only (excludes "INSERT" statements) + Sadece DDL komutlarını biçimlendir("INSERT" komutları dışında) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_uk_UA.ts b/Plugins/SqlExport/translations/SqlExport_uk_UA.ts new file mode 100644 index 0000000..7ba42a3 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_uk_UA.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_vi_VN.ts b/Plugins/SqlExport/translations/SqlExport_vi_VN.ts new file mode 100644 index 0000000..2f1ceb5 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_vi_VN.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- Results of query: + + + + -- Table: %1 + -- Table: %1 + + + + -- Index: %1 + -- Index: %1 + + + + -- Trigger: %1 + -- Trigger: %1 + + + + -- View: %1 + -- View: %1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- File generated with SQLiteStudio v%1 on %2 + + + + -- Text encoding used: %1 + -- Text encoding used: %1 + + + + Table name for INSERT statements is mandatory. + Table name for INSERT statements is mandatory. + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + Use SQL formatter to format exported SQL statements + + + + Table name to use for INSERT statements: + Table name to use for INSERT statements: + + + + Generate "CREATE TABLE" statement at the begining + Generate "CREATE TABLE" statement at the begining + + + + Include the query in comments + Include the query in comments + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + Generate "DROP IF EXISTS" statement before "CREATE" statement + + + + Format DDL statements only (excludes "INSERT" statements) + Format DDL statements only (excludes "INSERT" statements) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_zh_CN.ts b/Plugins/SqlExport/translations/SqlExport_zh_CN.ts new file mode 100644 index 0000000..36e90c1 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_zh_CN.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- 查询的执行结果: + + + + -- Table: %1 + -- 表:%1 + + + + -- Index: %1 + -- 索引:%1 + + + + -- Trigger: %1 + -- 触å‘器:%1 + + + + -- View: %1 + -- 视图:%1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- SQLiteStudio v%1 生æˆçš„æ–‡ä»¶ï¼Œ%2 + + + + -- Text encoding used: %1 + -- 所用的文本编ç ï¼š%1 + + + + Table name for INSERT statements is mandatory. + INSERT 语å¥çš„表å称是强制性的。 + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + 在 "CREATE" 语å¥å‰ç”Ÿæˆ "DROP IF EXISTS" è¯­å¥ + + + + Format DDL statements only (excludes "INSERT" statements) + ä»…æ ¼å¼ DDL 语å¥ï¼ˆæŽ’除 "INSERT" 语å¥ï¼‰ + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + 使用 SQL æ ¼å¼åŒ–器格å¼åŒ–导出的 SQL è¯­å¥ + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + 使用 SQL æ ¼å¼åŒ–器格å¼åŒ–导出的 SQL è¯­å¥ + + + + Table name to use for INSERT statements: + INSERT 语å¥çš„表å: + + + + Generate "CREATE TABLE" statement at the begining + åœ¨å¯¼å‡ºæ–‡ä»¶å¼€å§‹éƒ¨åˆ†ç”Ÿæˆ "CREATE TABLE" è¯­å¥ + + + + Include the query in comments + åœ¨æ³¨é‡Šä¸­åŒ…å«æŸ¥è¯¢ + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + 在 "CREATE" 语å¥å‰ç”Ÿæˆ "DROP IF EXISTS" è¯­å¥ + + + + Format DDL statements only (excludes "INSERT" statements) + ä»…æ ¼å¼ DDL 语å¥ï¼ˆæŽ’除 "INSERT" 语å¥ï¼‰ + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlExport/translations/SqlExport_zh_TW.ts b/Plugins/SqlExport/translations/SqlExport_zh_TW.ts new file mode 100644 index 0000000..5ef2ab2 --- /dev/null +++ b/Plugins/SqlExport/translations/SqlExport_zh_TW.ts @@ -0,0 +1,108 @@ + + + + + SqlExport + + + -- Results of query: + -- æŸ¥è©¢çš„åŸ·è¡Œçµæžœï¼š + + + + -- Table: %1 + -- 表:%1 + + + + -- Index: %1 + -- 索引:%1 + + + + -- Trigger: %1 + -- 觸發器:%1 + + + + -- View: %1 + -- 檢視:%1 + + + + -- File generated with SQLiteStudio v%1 on %2 + -- SQLiteStudio v%1 生æˆçš„æª”案,%2 + + + + -- Text encoding used: %1 + -- 所用的文字編碼:%1 + + + + Table name for INSERT statements is mandatory. + INSERT 語å¥çš„表å稱是強制性的。 + + + + sqlExportCommonConfig + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + 在 "CREATE" 語å¥å‰ç”Ÿæˆ "DROP IF EXISTS" èªžå¥ + + + + Format DDL statements only (excludes "INSERT" statements) + åƒ…æ ¼å¼ DDL èªžå¥ (排除 "INSERT" 語å¥) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + + Use SQL formatter to format exported SQL statements + 使用 SQL æ ¼å¼åŒ–器格å¼åŒ–匯出的 SQL èªžå¥ + + + + sqlExportQueryConfig + + + Use SQL formatter to format exported SQL statements + 使用 SQL æ ¼å¼åŒ–器格å¼åŒ–匯出的 SQL èªžå¥ + + + + Table name to use for INSERT statements: + INSERT 語å¥çš„表å: + + + + Generate "CREATE TABLE" statement at the begining + åœ¨åŒ¯å‡ºæª”æ¡ˆé–‹å§‹éƒ¨åˆ†ç”Ÿæˆ "CREATE TABLE" èªžå¥ + + + + Include the query in comments + åœ¨è¨»é‡‹ä¸­åŒ…å«æŸ¥è©¢ + + + + Generate "DROP IF EXISTS" statement before "CREATE" statement + 在 "CREATE" 語å¥å‰ç”Ÿæˆ "DROP IF EXISTS" èªžå¥ + + + + Format DDL statements only (excludes "INSERT" statements) + åƒ…æ ¼å¼ DDL èªžå¥ (排除 "INSERT" 語å¥) + + + + Add "IF NOT EXISTS" clause to "CREATE" statement + Add "IF NOT EXISTS" clause to "CREATE" statement + + + diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple.pro b/Plugins/SqlFormatterSimple/SqlFormatterSimple.pro index 8b69ad9..99074d8 100644 --- a/Plugins/SqlFormatterSimple/SqlFormatterSimple.pro +++ b/Plugins/SqlFormatterSimple/SqlFormatterSimple.pro @@ -26,31 +26,3 @@ OTHER_FILES += \ RESOURCES += \ sqlformattersimple.qrc - - -TRANSLATIONS += SqlFormatterSimple_ro_RO.ts \ - SqlFormatterSimple_de.ts \ - SqlFormatterSimple_it.ts \ - SqlFormatterSimple_zh_CN.ts \ - SqlFormatterSimple_sk.ts \ - SqlFormatterSimple_ru.ts \ - SqlFormatterSimple_pt_BR.ts \ - SqlFormatterSimple_fr.ts \ - SqlFormatterSimple_es.ts \ - SqlFormatterSimple_pl.ts - - - - - - - - - - - - - - - - diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_de.qm b/Plugins/SqlFormatterSimple/SqlFormatterSimple_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/SqlFormatterSimple/SqlFormatterSimple_de.qm and /dev/null differ diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_de.ts b/Plugins/SqlFormatterSimple/SqlFormatterSimple_de.ts deleted file mode 100644 index 97a671f..0000000 --- a/Plugins/SqlFormatterSimple/SqlFormatterSimple_de.ts +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SqlFormatterSimplePlugin - - - Upper case keywords - - - - - Reduce multiple whitespaces to single whitespace - - - - diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_es.qm b/Plugins/SqlFormatterSimple/SqlFormatterSimple_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/SqlFormatterSimple/SqlFormatterSimple_es.qm and /dev/null differ diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_es.ts b/Plugins/SqlFormatterSimple/SqlFormatterSimple_es.ts deleted file mode 100644 index e6d4b25..0000000 --- a/Plugins/SqlFormatterSimple/SqlFormatterSimple_es.ts +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SqlFormatterSimplePlugin - - - Upper case keywords - - - - - Reduce multiple whitespaces to single whitespace - - - - diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_fr.qm b/Plugins/SqlFormatterSimple/SqlFormatterSimple_fr.qm deleted file mode 100644 index f4d64ab..0000000 Binary files a/Plugins/SqlFormatterSimple/SqlFormatterSimple_fr.qm and /dev/null differ diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_fr.ts b/Plugins/SqlFormatterSimple/SqlFormatterSimple_fr.ts deleted file mode 100644 index d2c02e2..0000000 --- a/Plugins/SqlFormatterSimple/SqlFormatterSimple_fr.ts +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SqlFormatterSimplePlugin - - - Upper case keywords - Mots-clefs en majuscule - - - - Reduce multiple whitespaces to single whitespace - Réduisez les espaces multiples à un seul espace - - - diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_it.qm b/Plugins/SqlFormatterSimple/SqlFormatterSimple_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/SqlFormatterSimple/SqlFormatterSimple_it.qm and /dev/null differ diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_it.ts b/Plugins/SqlFormatterSimple/SqlFormatterSimple_it.ts deleted file mode 100644 index 91208f1..0000000 --- a/Plugins/SqlFormatterSimple/SqlFormatterSimple_it.ts +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SqlFormatterSimplePlugin - - - Upper case keywords - - - - - Reduce multiple whitespaces to single whitespace - - - - diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_pl.qm b/Plugins/SqlFormatterSimple/SqlFormatterSimple_pl.qm deleted file mode 100644 index a88fc0f..0000000 Binary files a/Plugins/SqlFormatterSimple/SqlFormatterSimple_pl.qm and /dev/null differ diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_pl.ts b/Plugins/SqlFormatterSimple/SqlFormatterSimple_pl.ts deleted file mode 100644 index a456bdd..0000000 --- a/Plugins/SqlFormatterSimple/SqlFormatterSimple_pl.ts +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SqlFormatterSimplePlugin - - - Upper case keywords - ZmieÅ„ litery słów kluczowych na duże - - - - Reduce multiple whitespaces to single whitespace - Zredukuj wiele znaków biaÅ‚ych do pojedynczego - - - diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_pt_BR.qm b/Plugins/SqlFormatterSimple/SqlFormatterSimple_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/SqlFormatterSimple/SqlFormatterSimple_pt_BR.qm and /dev/null differ diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_pt_BR.ts b/Plugins/SqlFormatterSimple/SqlFormatterSimple_pt_BR.ts deleted file mode 100644 index 246c044..0000000 --- a/Plugins/SqlFormatterSimple/SqlFormatterSimple_pt_BR.ts +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SqlFormatterSimplePlugin - - - Upper case keywords - - - - - Reduce multiple whitespaces to single whitespace - - - - diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_ro_RO.qm b/Plugins/SqlFormatterSimple/SqlFormatterSimple_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/SqlFormatterSimple/SqlFormatterSimple_ro_RO.qm and /dev/null differ diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_ro_RO.ts b/Plugins/SqlFormatterSimple/SqlFormatterSimple_ro_RO.ts deleted file mode 100644 index 547d44d..0000000 --- a/Plugins/SqlFormatterSimple/SqlFormatterSimple_ro_RO.ts +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SqlFormatterSimplePlugin - - - Upper case keywords - - - - - Reduce multiple whitespaces to single whitespace - - - - diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_ru.qm b/Plugins/SqlFormatterSimple/SqlFormatterSimple_ru.qm deleted file mode 100644 index 2f4ef6c..0000000 Binary files a/Plugins/SqlFormatterSimple/SqlFormatterSimple_ru.qm and /dev/null differ diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_ru.ts b/Plugins/SqlFormatterSimple/SqlFormatterSimple_ru.ts deleted file mode 100644 index cea4179..0000000 --- a/Plugins/SqlFormatterSimple/SqlFormatterSimple_ru.ts +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SqlFormatterSimplePlugin - - - Upper case keywords - Ключевые Ñлова в верхнем региÑтре - - - - Reduce multiple whitespaces to single whitespace - Сокращать неÑколько непечатаемых Ñимволов в один - - - diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_sk.qm b/Plugins/SqlFormatterSimple/SqlFormatterSimple_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/Plugins/SqlFormatterSimple/SqlFormatterSimple_sk.qm and /dev/null differ diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_sk.ts b/Plugins/SqlFormatterSimple/SqlFormatterSimple_sk.ts deleted file mode 100644 index c07fd9f..0000000 --- a/Plugins/SqlFormatterSimple/SqlFormatterSimple_sk.ts +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SqlFormatterSimplePlugin - - - Upper case keywords - - - - - Reduce multiple whitespaces to single whitespace - - - - diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_zh_CN.qm b/Plugins/SqlFormatterSimple/SqlFormatterSimple_zh_CN.qm deleted file mode 100644 index e3a8510..0000000 Binary files a/Plugins/SqlFormatterSimple/SqlFormatterSimple_zh_CN.qm and /dev/null differ diff --git a/Plugins/SqlFormatterSimple/SqlFormatterSimple_zh_CN.ts b/Plugins/SqlFormatterSimple/SqlFormatterSimple_zh_CN.ts deleted file mode 100644 index 40c7f1e..0000000 --- a/Plugins/SqlFormatterSimple/SqlFormatterSimple_zh_CN.ts +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SqlFormatterSimplePlugin - - - Upper case keywords - 大写关键字 - - - - Reduce multiple whitespaces to single whitespace - 将多个空白转æ¢ä¸ºä¸€ä¸ªç©ºç™½ - - - diff --git a/Plugins/SqlFormatterSimple/sqlformattersimple.qrc b/Plugins/SqlFormatterSimple/sqlformattersimple.qrc index 2234dfd..febfbd2 100644 --- a/Plugins/SqlFormatterSimple/sqlformattersimple.qrc +++ b/Plugins/SqlFormatterSimple/sqlformattersimple.qrc @@ -2,19 +2,4 @@ SqlFormatterSimple.ui - - SqlFormatterSimple_ro_RO.qm - SqlFormatterSimple_de.qm - - - SqlFormatterSimple_pl.qm - SqlFormatterSimple_ru.qm - SqlFormatterSimple_fr.qm - SqlFormatterSimple_sk.qm - SqlFormatterSimple_zh_CN.qm - - - - - diff --git a/Plugins/SqlFormatterSimple/sqlformattersimpleplugin.cpp b/Plugins/SqlFormatterSimple/sqlformattersimpleplugin.cpp index 943f047..e5983b0 100644 --- a/Plugins/SqlFormatterSimple/sqlformattersimpleplugin.cpp +++ b/Plugins/SqlFormatterSimple/sqlformattersimpleplugin.cpp @@ -1,5 +1,5 @@ #include "sqlformattersimpleplugin.h" - +#include "common/global.h" SqlFormatterSimplePlugin::SqlFormatterSimplePlugin() { } @@ -30,13 +30,13 @@ QString SqlFormatterSimplePlugin::format(SqliteQueryPtr query) bool SqlFormatterSimplePlugin::init() { - Q_INIT_RESOURCE(sqlformattersimple); + SQLS_INIT_RESOURCE(sqlformattersimple); return GenericPlugin::init(); } void SqlFormatterSimplePlugin::deinit() { - Q_CLEANUP_RESOURCE(sqlformattersimple); + SQLS_CLEANUP_RESOURCE(sqlformattersimple); } QString SqlFormatterSimplePlugin::getConfigUiForm() const diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple.ts new file mode 100644 index 0000000..012b162 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + + + + + Reduce multiple whitespaces to single whitespace + + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_af_ZA.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_af_ZA.ts new file mode 100644 index 0000000..ae21b05 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_af_ZA.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ar_SA.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ar_SA.ts new file mode 100644 index 0000000..097f552 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ar_SA.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ca_ES.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ca_ES.ts new file mode 100644 index 0000000..1fb23f6 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ca_ES.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_cs_CZ.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_cs_CZ.ts new file mode 100644 index 0000000..d26aed2 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_cs_CZ.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_da_DK.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_da_DK.ts new file mode 100644 index 0000000..2cee87d --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_da_DK.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_de_DE.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_de_DE.ts new file mode 100644 index 0000000..c71342f --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_de_DE.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Schlüsselwörter groß schreiben + + + + Reduce multiple whitespaces to single whitespace + Mehrere Leerzeichen zu einem Leerzeichen reduzieren + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_el_GR.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_el_GR.ts new file mode 100644 index 0000000..f59daaa --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_el_GR.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_en_US.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_en_US.ts new file mode 100644 index 0000000..cba615c --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_en_US.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_es_ES.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_es_ES.ts new file mode 100644 index 0000000..80f46ae --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_es_ES.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Palabras clave en mayúsculas + + + + Reduce multiple whitespaces to single whitespace + Reducir a un sólo espacio en blanco los múltiples espacios en blanco + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fa_IR.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fa_IR.ts new file mode 100644 index 0000000..9932633 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fa_IR.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fi_FI.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fi_FI.ts new file mode 100644 index 0000000..152c051 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fi_FI.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fr_FR.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fr_FR.ts new file mode 100644 index 0000000..39a6a5d --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_fr_FR.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Mots-clés en majuscule + + + + Reduce multiple whitespaces to single whitespace + Réduisez les espaces multiples à un seul espace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_he_IL.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_he_IL.ts new file mode 100644 index 0000000..5371385 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_he_IL.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + מילות מפתח ב×ותיות רישיות + + + + Reduce multiple whitespaces to single whitespace + ×¦×ž×¦×•× ×¨×•×•×—×™× ×œ×‘× ×™× ×ž×¨×•×‘×™× ×œ×ž×¨×•×•×— לבן יחיד + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_hu_HU.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_hu_HU.ts new file mode 100644 index 0000000..57f90ee --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_hu_HU.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_it_IT.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_it_IT.ts new file mode 100644 index 0000000..56122dc --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_it_IT.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Parole chiave maiuscole + + + + Reduce multiple whitespaces to single whitespace + Riduce spazi bianchi multipli a spazi bianchi singoli + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ja_JP.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ja_JP.ts new file mode 100644 index 0000000..e063b2e --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ja_JP.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_kaa.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_kaa.ts new file mode 100644 index 0000000..b0eb8d6 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_kaa.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ko_KR.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ko_KR.ts new file mode 100644 index 0000000..37093db --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ko_KR.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_nl_NL.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_nl_NL.ts new file mode 100644 index 0000000..58ea265 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_nl_NL.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_no_NO.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_no_NO.ts new file mode 100644 index 0000000..c73e571 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_no_NO.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pl_PL.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pl_PL.ts new file mode 100644 index 0000000..7ed7269 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pl_PL.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + ZmieÅ„ litery słów kluczowych na duże + + + + Reduce multiple whitespaces to single whitespace + Zredukuj wiele znaków biaÅ‚ych do pojedynczego + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pt_BR.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pt_BR.ts new file mode 100644 index 0000000..bddaf72 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pt_BR.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Palavras-chave em maiúsculas + + + + Reduce multiple whitespaces to single whitespace + Reduzir múltiplos espaços em branco para um único espaço em branco + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pt_PT.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pt_PT.ts new file mode 100644 index 0000000..bfdb798 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_pt_PT.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ro_RO.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ro_RO.ts new file mode 100644 index 0000000..69985eb --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ro_RO.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ru_RU.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ru_RU.ts new file mode 100644 index 0000000..5a4d48a --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_ru_RU.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Ключевые Ñлова в верхнем региÑтре + + + + Reduce multiple whitespaces to single whitespace + Сокращать неÑколько непечатаемых Ñимволов в один + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sk_SK.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sk_SK.ts new file mode 100644 index 0000000..b7a3b79 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sk_SK.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sr_SP.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sr_SP.ts new file mode 100644 index 0000000..0340397 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sr_SP.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sv_SE.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sv_SE.ts new file mode 100644 index 0000000..f1147fb --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_sv_SE.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_tr_TR.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_tr_TR.ts new file mode 100644 index 0000000..30e2f05 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_tr_TR.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Büyük harfli anahtar kelimeler + + + + Reduce multiple whitespaces to single whitespace + Birden çok boÅŸluÄŸu tek boÅŸluÄŸa düşür + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_uk_UA.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_uk_UA.ts new file mode 100644 index 0000000..baf556a --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_uk_UA.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_vi_VN.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_vi_VN.ts new file mode 100644 index 0000000..fba3a9f --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_vi_VN.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + Upper case keywords + + + + Reduce multiple whitespaces to single whitespace + Reduce multiple whitespaces to single whitespace + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_zh_CN.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_zh_CN.ts new file mode 100644 index 0000000..289a513 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_zh_CN.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + 大写关键字 + + + + Reduce multiple whitespaces to single whitespace + 连续的多个空白符转化为一个空白符 + + + diff --git a/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_zh_TW.ts b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_zh_TW.ts new file mode 100644 index 0000000..eb3b831 --- /dev/null +++ b/Plugins/SqlFormatterSimple/translations/SqlFormatterSimple_zh_TW.ts @@ -0,0 +1,17 @@ + + + + + SqlFormatterSimplePlugin + + + Upper case keywords + 大寫關éµå­— + + + + Reduce multiple whitespaces to single whitespace + 連續的多個空白符轉化為一個空白符 + + + diff --git a/Plugins/XmlExport/XmlExport.pro b/Plugins/XmlExport/XmlExport.pro index b7ae73a..32f700f 100644 --- a/Plugins/XmlExport/XmlExport.pro +++ b/Plugins/XmlExport/XmlExport.pro @@ -25,31 +25,3 @@ OTHER_FILES += \ RESOURCES += \ xmlexport.qrc - - -TRANSLATIONS += XmlExport_ro_RO.ts \ - XmlExport_de.ts \ - XmlExport_it.ts \ - XmlExport_zh_CN.ts \ - XmlExport_sk.ts \ - XmlExport_ru.ts \ - XmlExport_pt_BR.ts \ - XmlExport_fr.ts \ - XmlExport_es.ts \ - XmlExport_pl.ts - - - - - - - - - - - - - - - - diff --git a/Plugins/XmlExport/XmlExport_de.qm b/Plugins/XmlExport/XmlExport_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/XmlExport/XmlExport_de.qm and /dev/null differ diff --git a/Plugins/XmlExport/XmlExport_de.ts b/Plugins/XmlExport/XmlExport_de.ts deleted file mode 100644 index 587f54c..0000000 --- a/Plugins/XmlExport/XmlExport_de.ts +++ /dev/null @@ -1,70 +0,0 @@ - - - - - XmlExport - - - Enter the namespace to use (for example: http://my.namespace.org) - - - - - XmlExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - Special characters escaping - - - - - <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> - - - - - Use CDATA and ampersands - - - - - <p>Every value requiring character escepe will be enclosed in CDATA block.</p> - - - - - Always use CDATA - - - - - <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> - - - - - Always use ampersand - - - - - Define XML namespace - - - - diff --git a/Plugins/XmlExport/XmlExport_es.qm b/Plugins/XmlExport/XmlExport_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/XmlExport/XmlExport_es.qm and /dev/null differ diff --git a/Plugins/XmlExport/XmlExport_es.ts b/Plugins/XmlExport/XmlExport_es.ts deleted file mode 100644 index c7c3fdf..0000000 --- a/Plugins/XmlExport/XmlExport_es.ts +++ /dev/null @@ -1,70 +0,0 @@ - - - - - XmlExport - - - Enter the namespace to use (for example: http://my.namespace.org) - - - - - XmlExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - Special characters escaping - - - - - <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> - - - - - Use CDATA and ampersands - - - - - <p>Every value requiring character escepe will be enclosed in CDATA block.</p> - - - - - Always use CDATA - - - - - <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> - - - - - Always use ampersand - - - - - Define XML namespace - - - - diff --git a/Plugins/XmlExport/XmlExport_fr.qm b/Plugins/XmlExport/XmlExport_fr.qm deleted file mode 100644 index a25d4db..0000000 Binary files a/Plugins/XmlExport/XmlExport_fr.qm and /dev/null differ diff --git a/Plugins/XmlExport/XmlExport_fr.ts b/Plugins/XmlExport/XmlExport_fr.ts deleted file mode 100644 index de33184..0000000 --- a/Plugins/XmlExport/XmlExport_fr.ts +++ /dev/null @@ -1,70 +0,0 @@ - - - - - XmlExport - - - Enter the namespace to use (for example: http://my.namespace.org) - Saississez l’espace nom (par exemple : http://my.namespace.org) - - - - XmlExportConfig - - - Output format - Format de sortie - - - - Format document (new lines, indentation) - Format document (nouvelles lignes, indentation) - - - - Compress (everything in one line) - Compression (tout en une ligne) - - - - Special characters escaping - Caractères spécial d’échappement - - - - <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> - <p>Esperluette sera utilisée pour les valeurs les plus courtes et CDATA pour les plus longues. Ceci appliqué seulement aux valeurs qui requirent un caractère d’échapement. Les autres valeurs seront exportées telque.</p> - - - - Use CDATA and ampersands - Utiliser les esperluettes et CDATA - - - - <p>Every value requiring character escepe will be enclosed in CDATA block.</p> - <p>Toute valeur requierant un carctère d’échappement sera incluse dans le bloc CDATA.</p> - - - - Always use CDATA - Toujours utiliser CDATA - - - - <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> - <p> Chaque caractère requirant un échappement sera encadré avec une séquence esperluette. Aucun bloc CDATA ne sera utilisé </p> - - - - Always use ampersand - Toujours utiliser l’esperluette - - - - Define XML namespace - Définir le domaine XML - - - diff --git a/Plugins/XmlExport/XmlExport_it.qm b/Plugins/XmlExport/XmlExport_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/Plugins/XmlExport/XmlExport_it.qm and /dev/null differ diff --git a/Plugins/XmlExport/XmlExport_it.ts b/Plugins/XmlExport/XmlExport_it.ts deleted file mode 100644 index 58265f6..0000000 --- a/Plugins/XmlExport/XmlExport_it.ts +++ /dev/null @@ -1,70 +0,0 @@ - - - - - XmlExport - - - Enter the namespace to use (for example: http://my.namespace.org) - - - - - XmlExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - Special characters escaping - - - - - <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> - - - - - Use CDATA and ampersands - - - - - <p>Every value requiring character escepe will be enclosed in CDATA block.</p> - - - - - Always use CDATA - - - - - <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> - - - - - Always use ampersand - - - - - Define XML namespace - - - - diff --git a/Plugins/XmlExport/XmlExport_pl.qm b/Plugins/XmlExport/XmlExport_pl.qm deleted file mode 100644 index 40b5b24..0000000 Binary files a/Plugins/XmlExport/XmlExport_pl.qm and /dev/null differ diff --git a/Plugins/XmlExport/XmlExport_pl.ts b/Plugins/XmlExport/XmlExport_pl.ts deleted file mode 100644 index 8f2104e..0000000 --- a/Plugins/XmlExport/XmlExport_pl.ts +++ /dev/null @@ -1,71 +0,0 @@ - - - - - XmlExport - - - Enter the namespace to use (for example: http://my.namespace.org) - Enter the namespace to use (for example: http://my.namespace.org - Wprowadź przestrzeÅ„ nazw (na przykÅ‚ad: http://moja.przestrzen.nazw.org) - - - - XmlExportConfig - - - Output format - Format wyjÅ›ciowy - - - - Format document (new lines, indentation) - Formatuj dokument (nowe linie, wciÄ™cia) - - - - Compress (everything in one line) - Kompresuj (wszystko w jednej linii) - - - - Special characters escaping - Podmiana znaków specjalnych - - - - <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> - <p>Dla krótszych wartoÅ›ci bÄ™dzie użyty ampersand, a dla dÅ‚uższych CDATA. Dotyczy to tylko wartoÅ›ci, które wymagajÄ… podmiany znaków. Inne wartoÅ›ci bÄ™dÄ… wyeksportowane bez zmian.</p> - - - - Use CDATA and ampersands - Użyj CDATA i ampersandów - - - - <p>Every value requiring character escepe will be enclosed in CDATA block.</p> - <p>Każda wartość wymagajÄ…ca podmiany znaków bÄ™dzie zamkniÄ™ta w bloku CDATA.</p> - - - - Always use CDATA - Zawsze używaj CDATA - - - - <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> - <p>Każdy znak wymagajÄ…cy podmiany bÄ™dzie zastÄ…piony odpowiedniÄ… sekwencjÄ… z ampersandem. Bloki CDATA nie bÄ™dÄ… używane.</p> - - - - Always use ampersand - Zawsze używaj ampersanda - - - - Define XML namespace - Zdefiniuj przestrzeÅ„ nazw XML - - - diff --git a/Plugins/XmlExport/XmlExport_pt_BR.qm b/Plugins/XmlExport/XmlExport_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/Plugins/XmlExport/XmlExport_pt_BR.qm and /dev/null differ diff --git a/Plugins/XmlExport/XmlExport_pt_BR.ts b/Plugins/XmlExport/XmlExport_pt_BR.ts deleted file mode 100644 index f4faf44..0000000 --- a/Plugins/XmlExport/XmlExport_pt_BR.ts +++ /dev/null @@ -1,70 +0,0 @@ - - - - - XmlExport - - - Enter the namespace to use (for example: http://my.namespace.org) - - - - - XmlExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - Special characters escaping - - - - - <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> - - - - - Use CDATA and ampersands - - - - - <p>Every value requiring character escepe will be enclosed in CDATA block.</p> - - - - - Always use CDATA - - - - - <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> - - - - - Always use ampersand - - - - - Define XML namespace - - - - diff --git a/Plugins/XmlExport/XmlExport_ro_RO.qm b/Plugins/XmlExport/XmlExport_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/Plugins/XmlExport/XmlExport_ro_RO.qm and /dev/null differ diff --git a/Plugins/XmlExport/XmlExport_ro_RO.ts b/Plugins/XmlExport/XmlExport_ro_RO.ts deleted file mode 100644 index 6c69e46..0000000 --- a/Plugins/XmlExport/XmlExport_ro_RO.ts +++ /dev/null @@ -1,70 +0,0 @@ - - - - - XmlExport - - - Enter the namespace to use (for example: http://my.namespace.org) - - - - - XmlExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - Special characters escaping - - - - - <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> - - - - - Use CDATA and ampersands - - - - - <p>Every value requiring character escepe will be enclosed in CDATA block.</p> - - - - - Always use CDATA - - - - - <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> - - - - - Always use ampersand - - - - - Define XML namespace - - - - diff --git a/Plugins/XmlExport/XmlExport_ru.qm b/Plugins/XmlExport/XmlExport_ru.qm deleted file mode 100644 index 15cdb78..0000000 Binary files a/Plugins/XmlExport/XmlExport_ru.qm and /dev/null differ diff --git a/Plugins/XmlExport/XmlExport_ru.ts b/Plugins/XmlExport/XmlExport_ru.ts deleted file mode 100644 index 28480fe..0000000 --- a/Plugins/XmlExport/XmlExport_ru.ts +++ /dev/null @@ -1,70 +0,0 @@ - - - - - XmlExport - - - Enter the namespace to use (for example: http://my.namespace.org) - Укажите иÑпользуемое проÑтранÑтво имён (например http://my.namespace.org) - - - - XmlExportConfig - - - Output format - Выходной формат - - - - Format document (new lines, indentation) - Форматировать документ (переноÑÑ‹ Ñтрок, отÑтупы) - - - - Compress (everything in one line) - Сжать (вÑÑ‘ в одну Ñтроку) - - - - Special characters escaping - Экранирование ÑпецÑимволов - - - - <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> - <p>ÐмперÑанды будут иÑпользованы Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ¸Ñ… значений, CDATA — Ð´Ð»Ñ Ð´Ð»Ð¸Ð½Ð½Ñ‹Ñ…. Это отноÑитÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ к значениÑм, которые необходимо Ñкранировать. ОÑтальные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÑƒÑ‚ ÑкÑпортированы как еÑть.</p> - - - - Use CDATA and ampersands - ИÑпользовать CDATA и амперÑанды - - - - <p>Every value requiring character escepe will be enclosed in CDATA block.</p> - <p>Ð’Ñе значениÑ, требующие ÑкранированиÑ, будут помещены в блок CDATA.</p> - - - - Always use CDATA - Ð’Ñегда иÑпользовать CDATA - - - - <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> - <p>Ð’Ñе значениÑ, требующие ÑкранированиÑ, будут заменены Ñкранирующими поÑледовательноÑÑ‚Ñми Ñ Ð°Ð¼Ð¿ÐµÑ€Ñандом. Блоки CDATA иÑпользованы не будут.</p> - - - - Always use ampersand - Ð’Ñегда иÑпользовать амперÑанд - - - - Define XML namespace - Указать проÑтранÑтво имён XML - - - diff --git a/Plugins/XmlExport/XmlExport_sk.qm b/Plugins/XmlExport/XmlExport_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/Plugins/XmlExport/XmlExport_sk.qm and /dev/null differ diff --git a/Plugins/XmlExport/XmlExport_sk.ts b/Plugins/XmlExport/XmlExport_sk.ts deleted file mode 100644 index 07d694d..0000000 --- a/Plugins/XmlExport/XmlExport_sk.ts +++ /dev/null @@ -1,70 +0,0 @@ - - - - - XmlExport - - - Enter the namespace to use (for example: http://my.namespace.org) - - - - - XmlExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - Special characters escaping - - - - - <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> - - - - - Use CDATA and ampersands - - - - - <p>Every value requiring character escepe will be enclosed in CDATA block.</p> - - - - - Always use CDATA - - - - - <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> - - - - - Always use ampersand - - - - - Define XML namespace - - - - diff --git a/Plugins/XmlExport/XmlExport_zh_CN.qm b/Plugins/XmlExport/XmlExport_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/Plugins/XmlExport/XmlExport_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/Plugins/XmlExport/XmlExport_zh_CN.ts b/Plugins/XmlExport/XmlExport_zh_CN.ts deleted file mode 100644 index 34023fb..0000000 --- a/Plugins/XmlExport/XmlExport_zh_CN.ts +++ /dev/null @@ -1,70 +0,0 @@ - - - - - XmlExport - - - Enter the namespace to use (for example: http://my.namespace.org) - - - - - XmlExportConfig - - - Output format - - - - - Format document (new lines, indentation) - - - - - Compress (everything in one line) - - - - - Special characters escaping - - - - - <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> - - - - - Use CDATA and ampersands - - - - - <p>Every value requiring character escepe will be enclosed in CDATA block.</p> - - - - - Always use CDATA - - - - - <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> - - - - - Always use ampersand - - - - - Define XML namespace - - - - diff --git a/Plugins/XmlExport/translations/XmlExport.ts b/Plugins/XmlExport/translations/XmlExport.ts new file mode 100644 index 0000000..a4a52cd --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + + + + + XmlExportConfig + + + Output format + + + + + Format document (new lines, indentation) + + + + + Compress (everything in one line) + + + + + Special characters escaping + + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + + Use CDATA and ampersands + + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + + Always use CDATA + + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + + Always use ampersand + + + + + Define XML namespace + + + + diff --git a/Plugins/XmlExport/translations/XmlExport_af_ZA.ts b/Plugins/XmlExport/translations/XmlExport_af_ZA.ts new file mode 100644 index 0000000..166d9a0 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_af_ZA.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_ar_SA.ts b/Plugins/XmlExport/translations/XmlExport_ar_SA.ts new file mode 100644 index 0000000..522ec3e --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_ar_SA.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_ca_ES.ts b/Plugins/XmlExport/translations/XmlExport_ca_ES.ts new file mode 100644 index 0000000..b376761 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_ca_ES.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_cs_CZ.ts b/Plugins/XmlExport/translations/XmlExport_cs_CZ.ts new file mode 100644 index 0000000..9d685b0 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_cs_CZ.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_da_DK.ts b/Plugins/XmlExport/translations/XmlExport_da_DK.ts new file mode 100644 index 0000000..e003d12 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_da_DK.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_de_DE.ts b/Plugins/XmlExport/translations/XmlExport_de_DE.ts new file mode 100644 index 0000000..9235c94 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_de_DE.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_el_GR.ts b/Plugins/XmlExport/translations/XmlExport_el_GR.ts new file mode 100644 index 0000000..6214931 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_el_GR.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_en_US.ts b/Plugins/XmlExport/translations/XmlExport_en_US.ts new file mode 100644 index 0000000..a343eb5 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_en_US.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_es_ES.ts b/Plugins/XmlExport/translations/XmlExport_es_ES.ts new file mode 100644 index 0000000..8ace72e --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_es_ES.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Ingresa el espacio de nombres a usar (por ejemplo: http://mi.espaciodenombres.org) + + + + XmlExportConfig + + + Output format + Formato de salida + + + + Format document (new lines, indentation) + Formatear documento (nuevas líneas, indentación) + + + + Compress (everything in one line) + Comprimir (todo en una sola línea) + + + + Special characters escaping + Escapar caracteres especiales + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Se usarán ampersands para valores más pequeños y CDATA se usará para valores más grandes. Esto se aplica solamente a valores que requieran escapar caracteres. Otros valores se exportarán tal como están.</p> + + + + Use CDATA and ampersands + Usar CDATA y ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Cada valor que requiera ser escapado se encerrará en un bloque CDATA.</p> + + + + Always use CDATA + Usar CDATA siempre + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Cada caracter que requiera ser escapado será reemplazado con su secuencia de escape ampersand. No se usará ningún bloque CDATA.</p> + + + + Always use ampersand + Usar siempre ampersand + + + + Define XML namespace + Definir espacio de nombres XML + + + diff --git a/Plugins/XmlExport/translations/XmlExport_fa_IR.ts b/Plugins/XmlExport/translations/XmlExport_fa_IR.ts new file mode 100644 index 0000000..e47a6ae --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_fa_IR.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_fi_FI.ts b/Plugins/XmlExport/translations/XmlExport_fi_FI.ts new file mode 100644 index 0000000..2a0d3b6 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_fi_FI.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_fr_FR.ts b/Plugins/XmlExport/translations/XmlExport_fr_FR.ts new file mode 100644 index 0000000..294d60d --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_fr_FR.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Saississez l’espace nom (par exemple : http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Format de sortie + + + + Format document (new lines, indentation) + Format document (nouvelles lignes, indentation) + + + + Compress (everything in one line) + Compression (tout en une ligne) + + + + Special characters escaping + Caractères spécial d’échappement + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Esperluette sera utilisée pour les valeurs les plus courtes et CDATA pour les plus longues. Ceci appliqué seulement aux valeurs qui requirent un caractère d’échapement. Les autres valeurs seront exportées telque.</p> + + + + Use CDATA and ampersands + Utiliser les esperluettes et CDATA + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Toute valeur requierant un carctère d’échappement sera incluse dans le bloc CDATA.</p> + + + + Always use CDATA + Toujours utiliser CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p> Chaque caractère requirant un échappement sera encadré avec une séquence esperluette. Aucun bloc CDATA ne sera utilisé </p> + + + + Always use ampersand + Toujours utiliser l’esperluette + + + + Define XML namespace + Définir le domaine XML + + + diff --git a/Plugins/XmlExport/translations/XmlExport_he_IL.ts b/Plugins/XmlExport/translations/XmlExport_he_IL.ts new file mode 100644 index 0000000..11dc6c3 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_he_IL.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + הזנת ×ž×¨×—×‘Ö¾×”×©× ×œ×©×™×ž×•×© (לדוגמה: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + תבנית פלט + + + + Format document (new lines, indentation) + תבנות מסמך (שורה חדשה, ×”×–×—×”) + + + + Compress (everything in one line) + דחיסה (הכל בשורה ×חת) + + + + Special characters escaping + בֵּ××•Ö¼×¨Ö¾×ªÖ¼Ö¸×•Ö´×™× ×ž×™×•×—×“×™× + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + להשתמש ב- CDATA וב- "וג×" (&) + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + להשתמש תמיד ב CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + להשתמש תמיד ב- "וג×" (&) + + + + Define XML namespace + הגדרת ×ž×¨×—×‘Ö¾×©× XML + + + diff --git a/Plugins/XmlExport/translations/XmlExport_hu_HU.ts b/Plugins/XmlExport/translations/XmlExport_hu_HU.ts new file mode 100644 index 0000000..0aed9e3 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_hu_HU.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_it_IT.ts b/Plugins/XmlExport/translations/XmlExport_it_IT.ts new file mode 100644 index 0000000..9131a3e --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_it_IT.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Inserisci lo spazio dei nomi da usare (per esempio: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Formato di uscita + + + + Format document (new lines, indentation) + Formato documento (nuova riga, indentazione) + + + + Compress (everything in one line) + Comprimi (tutto in una riga) + + + + Special characters escaping + Caratteri speciali ammessi + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Gli ampersand saranno usati per valori più brevi e i CDATA per valori più grandi. Questo si applica solo ai valori che richiedono l'escape dei caratteri. Altri valori saranno esportati così come sono.</p> + + + + Use CDATA and ampersands + Usa CDATA e ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Ogni valore che richiede l'escape dei caratteri sarà racchiuso nel blocco CDATA.</p> + + + + Always use CDATA + Usa sempre CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Ogni carattere che richiede l'escaping verrà sostituito con la sua sequenza di ampersand escape. Non verranno utilizzati blocchi CDATA.</p> + + + + Always use ampersand + Usa sempre ampersand + + + + Define XML namespace + Definisci l' XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_ja_JP.ts b/Plugins/XmlExport/translations/XmlExport_ja_JP.ts new file mode 100644 index 0000000..a0e5180 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_ja_JP.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_kaa.ts b/Plugins/XmlExport/translations/XmlExport_kaa.ts new file mode 100644 index 0000000..292e5cf --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_kaa.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_ko_KR.ts b/Plugins/XmlExport/translations/XmlExport_ko_KR.ts new file mode 100644 index 0000000..2b965ba --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_ko_KR.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_nl_NL.ts b/Plugins/XmlExport/translations/XmlExport_nl_NL.ts new file mode 100644 index 0000000..4a09b04 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_nl_NL.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_no_NO.ts b/Plugins/XmlExport/translations/XmlExport_no_NO.ts new file mode 100644 index 0000000..3c0b49e --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_no_NO.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_pl_PL.ts b/Plugins/XmlExport/translations/XmlExport_pl_PL.ts new file mode 100644 index 0000000..26ab32e --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_pl_PL.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Wprowadź przestrzeÅ„ nazw (na przykÅ‚ad: http://moja.przestrzen.nazw.org) + + + + XmlExportConfig + + + Output format + Format wyjÅ›ciowy + + + + Format document (new lines, indentation) + Formatuj dokument (nowe linie, wciÄ™cia) + + + + Compress (everything in one line) + Kompresuj (wszystko w jednej linii) + + + + Special characters escaping + Podmiana znaków specjalnych + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Dla krótszych wartoÅ›ci bÄ™dzie użyty ampersand, a dla dÅ‚uższych CDATA. Dotyczy to tylko wartoÅ›ci, które wymagajÄ… podmiany znaków. Inne wartoÅ›ci bÄ™dÄ… wyeksportowane bez zmian.</p> + + + + Use CDATA and ampersands + Użyj CDATA i ampersandów + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Każda wartość wymagajÄ…ca podmiany znaków bÄ™dzie zamkniÄ™ta w bloku CDATA.</p> + + + + Always use CDATA + Zawsze używaj CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Każdy znak wymagajÄ…cy podmiany bÄ™dzie zastÄ…piony odpowiedniÄ… sekwencjÄ… z ampersandem. Bloki CDATA nie bÄ™dÄ… używane.</p> + + + + Always use ampersand + Zawsze używaj ampersanda + + + + Define XML namespace + Zdefiniuj przestrzeÅ„ nazw XML + + + diff --git a/Plugins/XmlExport/translations/XmlExport_pt_BR.ts b/Plugins/XmlExport/translations/XmlExport_pt_BR.ts new file mode 100644 index 0000000..7c41690 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_pt_BR.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Digite o domínio a ser usado (por exemplo: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Formato de saída + + + + Format document (new lines, indentation) + Formatar documento (novas linhas, recuo) + + + + Compress (everything in one line) + Comprimir (tudo em uma linha) + + + + Special characters escaping + Incompatibilidade de caracteres especiais + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>O & será usado para valores mais curtos e <> será usado para valores maiores. Isso só se aplica a valores que exigem escapar do caractere. Outros valores serão exportados como estão.</p> + + + + Use CDATA and ampersands + Usar <> e & + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Todo valor que exija aprovação de caráter será incluído no bloco <>.</p> + + + + Always use CDATA + Sempre usar <> + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Todos os caracteres que precisarem de aprovação serão substituídos por & e sua sequência de escape. Nenhum bloco <> será usado.</p> + + + + Always use ampersand + Sempre usar & + + + + Define XML namespace + Definir domínio XML + + + diff --git a/Plugins/XmlExport/translations/XmlExport_pt_PT.ts b/Plugins/XmlExport/translations/XmlExport_pt_PT.ts new file mode 100644 index 0000000..520c0e3 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_pt_PT.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_ro_RO.ts b/Plugins/XmlExport/translations/XmlExport_ro_RO.ts new file mode 100644 index 0000000..b9f7091 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_ro_RO.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_ru_RU.ts b/Plugins/XmlExport/translations/XmlExport_ru_RU.ts new file mode 100644 index 0000000..e8d78bf --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_ru_RU.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Укажите иÑпользуемое проÑтранÑтво имён (например http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Выходной формат + + + + Format document (new lines, indentation) + Форматировать документ (переноÑÑ‹ Ñтрок, отÑтупы) + + + + Compress (everything in one line) + Сжать (вÑÑ‘ в одну Ñтроку) + + + + Special characters escaping + Экранирование ÑпецÑимволов + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>ÐмперÑанды будут иÑпользованы Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ¸Ñ… значений, CDATA — Ð´Ð»Ñ Ð´Ð»Ð¸Ð½Ð½Ñ‹Ñ…. Это отноÑитÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ к значениÑм, которые необходимо Ñкранировать. ОÑтальные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÑƒÑ‚ ÑкÑпортированы как еÑть.</p> + + + + Use CDATA and ampersands + ИÑпользовать CDATA и амперÑанды + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Ð’Ñе значениÑ, требующие ÑкранированиÑ, будут помещены в блок CDATA.</p> + + + + Always use CDATA + Ð’Ñегда иÑпользовать CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Ð’Ñе значениÑ, требующие ÑкранированиÑ, будут заменены Ñкранирующими поÑледовательноÑÑ‚Ñми Ñ Ð°Ð¼Ð¿ÐµÑ€Ñандом. Блоки CDATA иÑпользованы не будут.</p> + + + + Always use ampersand + Ð’Ñегда иÑпользовать амперÑанд + + + + Define XML namespace + Указать проÑтранÑтво имён XML + + + diff --git a/Plugins/XmlExport/translations/XmlExport_sk_SK.ts b/Plugins/XmlExport/translations/XmlExport_sk_SK.ts new file mode 100644 index 0000000..9932c36 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_sk_SK.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_sr_SP.ts b/Plugins/XmlExport/translations/XmlExport_sr_SP.ts new file mode 100644 index 0000000..5f955f7 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_sr_SP.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_sv_SE.ts b/Plugins/XmlExport/translations/XmlExport_sv_SE.ts new file mode 100644 index 0000000..6c1ce31 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_sv_SE.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_tr_TR.ts b/Plugins/XmlExport/translations/XmlExport_tr_TR.ts new file mode 100644 index 0000000..f417416 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_tr_TR.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Kullanılacak namespace adını girin (örneÄŸin: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Çıktı formatı + + + + Format document (new lines, indentation) + Dökümanı biçimi (yeni satırlar, boÅŸluk) + + + + Compress (everything in one line) + Sıkıştır (her ÅŸeyi bir satırda) + + + + Special characters escaping + Özel kaçış karakterleri + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + Ampersant <p> deÄŸerleri kısaltmak için kullanılacak ve CDATA da büyütmek için kullanılacaktır. Bu sadece kaçış karakterine ihtiyaç duyulduÄŸunda uygulanacaktır. DiÄŸer veriler olduÄŸu gibi çıkartılacaktır.</p> + + + + Use CDATA and ampersands + Ampersant için CDATA kullan + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Kaçış karakterine ihtiyaç duyan her deÄŸer CDATA ile kapatılacaktır.</p> + + + + Always use CDATA + Her zaman CDATA kullan + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Kaçış karakterine ihtiyaç duyan karakterler ampersant kaçış bloüuyla deÄŸiÅŸtirilecektir. CDATA bloÄŸu kullanılmayacaktır.</p> + + + + Always use ampersand + Her zaman ampersant kullan + + + + Define XML namespace + XML namespace tanımla + + + diff --git a/Plugins/XmlExport/translations/XmlExport_uk_UA.ts b/Plugins/XmlExport/translations/XmlExport_uk_UA.ts new file mode 100644 index 0000000..0d7020b --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_uk_UA.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_vi_VN.ts b/Plugins/XmlExport/translations/XmlExport_vi_VN.ts new file mode 100644 index 0000000..1d91ae0 --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_vi_VN.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + Enter the namespace to use (for example: http://my.namespace.org) + + + + XmlExportConfig + + + Output format + Output format + + + + Format document (new lines, indentation) + Format document (new lines, indentation) + + + + Compress (everything in one line) + Compress (everything in one line) + + + + Special characters escaping + Special characters escaping + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + + + + Use CDATA and ampersands + Use CDATA and ampersands + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + + + + Always use CDATA + Always use CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + + + + Always use ampersand + Always use ampersand + + + + Define XML namespace + Define XML namespace + + + diff --git a/Plugins/XmlExport/translations/XmlExport_zh_CN.ts b/Plugins/XmlExport/translations/XmlExport_zh_CN.ts new file mode 100644 index 0000000..ab72cee --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_zh_CN.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + 输入è¦ä½¿ç”¨çš„命å空间(例如:http://my.namespace.org) + + + + XmlExportConfig + + + Output format + è¾“å‡ºæ ¼å¼ + + + + Format document (new lines, indentation) + æ ¼å¼åŒ–的文本(新行,缩进) + + + + Compress (everything in one line) + 压缩(å•行文件) + + + + Special characters escaping + 特殊字符转义 + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>&& 符å·å°†ç”¨äºŽè¾ƒçŸ­çš„值,CDATA 将用于较长的值。这仅用于需è¦å­—符转义的值。其他值将按原样导出。</p> + + + + Use CDATA and ampersands + 使用 CDATA åŠ && ç¬¦å· + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>æ¯ä¸ªéœ€è¦å­—符转义的值都将包裹在 CDATA å—中。</p> + + + + Always use CDATA + 总是使用 CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>æ¯ä¸ªéœ€è¦è½¬ä¹‰çš„字符都将使用 && 符å·åŠåºåˆ—代替。ä¸ä¼šä½¿ç”¨ CDATA å—。</p> + + + + Always use ampersand + 总是使用 && ç¬¦å· + + + + Define XML namespace + 定义 XML 命å空间 + + + diff --git a/Plugins/XmlExport/translations/XmlExport_zh_TW.ts b/Plugins/XmlExport/translations/XmlExport_zh_TW.ts new file mode 100644 index 0000000..5c1deba --- /dev/null +++ b/Plugins/XmlExport/translations/XmlExport_zh_TW.ts @@ -0,0 +1,70 @@ + + + + + XmlExport + + + Enter the namespace to use (for example: http://my.namespace.org) + 輸入è¦ä½¿ç”¨çš„å稱空間 (例如:http://my.namespace.org) + + + + XmlExportConfig + + + Output format + è¼¸å‡ºæ ¼å¼ + + + + Format document (new lines, indentation) + æ ¼å¼åŒ–的文字 (新行,縮排) + + + + Compress (everything in one line) + 壓縮 (單行檔案) + + + + Special characters escaping + 特殊字元轉義 + + + + <p>Ampersands will be used for shorter values and CDATA will be used for larger values. This applies only to values that require character escaping. Other values will be exported as they are.</p> + <p>&& 符號將用於較短的值,CDATA 將用於較長的值。這僅用於需è¦å­—元轉義的值。其他值將按原樣匯出。</p> + + + + Use CDATA and ampersands + 使用 CDATA åŠ && 符號 + + + + <p>Every value requiring character escepe will be enclosed in CDATA block.</p> + <p>æ¯å€‹éœ€è¦å­—元轉義的值都將包裹在 CDATA 塊中。</p> + + + + Always use CDATA + 總是使用 CDATA + + + + <p>Every character that require esceping will be replaced with its ampersand escape sequence. No CDATA blocks will be used.</p> + <p>æ¯å€‹éœ€è¦è½‰ç¾©çš„字元都將使用 && 符號åŠåºåˆ—ä»£æ›¿ã€‚ä¸æœƒä½¿ç”¨ CDATA 塊。</p> + + + + Always use ampersand + 總是使用 && 符號 + + + + Define XML namespace + 定義 XML å稱空間 + + + diff --git a/Plugins/XmlExport/xmlexport.cpp b/Plugins/XmlExport/xmlexport.cpp index 9d55736..5085611 100644 --- a/Plugins/XmlExport/xmlexport.cpp +++ b/Plugins/XmlExport/xmlexport.cpp @@ -136,9 +136,12 @@ bool XmlExport::exportTable(const QString& database, const QString& table, const writeTagWithValue("database", database); writeTagWithValue("name", table); - if (!createTable->withOutRowId.isNull()) + if (createTable->withOutRowId) writeln("true"); + if (createTable->strict) + writeln("true"); + writeTagWithValue("ddl", ddl); writeln(""); @@ -478,11 +481,11 @@ QString XmlExport::toString(bool value) bool XmlExport::init() { - Q_INIT_RESOURCE(xmlexport); + SQLS_INIT_RESOURCE(xmlexport); return GenericExportPlugin::init(); } void XmlExport::deinit() { - Q_CLEANUP_RESOURCE(xmlexport); + SQLS_CLEANUP_RESOURCE(xmlexport); } diff --git a/Plugins/XmlExport/xmlexport.qrc b/Plugins/XmlExport/xmlexport.qrc index e070c5a..d6a77a6 100644 --- a/Plugins/XmlExport/xmlexport.qrc +++ b/Plugins/XmlExport/xmlexport.qrc @@ -2,19 +2,4 @@ XmlExport.ui - - XmlExport_ro_RO.qm - XmlExport_de.qm - - - XmlExport_pl.qm - XmlExport_ru.qm - XmlExport_fr.qm - XmlExport_sk.qm - XmlExport_zh_CN.qm - - - - - diff --git a/README.md b/README.md index 46d8f4b..10d2844 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,10 @@ A free, open source, multi-platform SQLite database manager written in C++, with https://github.com/pawelsalawa/sqlitestudio/wiki/How_can_I_contribute? +## Want to donate? +https://sqlitestudio.pl/donate/ + + ## Home page https://sqlitestudio.pl/ @@ -19,10 +23,6 @@ https://sqlitestudio.pl/ https://github.com/pawelsalawa/sqlitestudio/wiki/Compiling_application_from_sources -## Discussion forum -https://forum.sqlitestudio.pl/ - - ## Bugs & feature requests tracker https://github.com/pawelsalawa/sqlitestudio/issues diff --git a/SQLiteStudio-installer.xml b/SQLiteStudio-installer.xml new file mode 100644 index 0000000..609a38f --- /dev/null +++ b/SQLiteStudio-installer.xml @@ -0,0 +1,414 @@ + + SQLiteStudio + SQLiteStudio + 1 + ${src_prefix}/LICENSE + 0 + ${src_prefix}/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_installer.png + ${src_prefix}/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_48.png + + + default + Default Component + 1 + 1 + 1 + + + + sqlitestudio + + SQLiteStudio + + all + 0 + 0 + ${installdir}/SQLiteStudio.exe + + + + + + + + Program Files + ${installdir} + programfiles + all + + + Uninstall + ${installdir}/${uninstallerName} + + Uninstall ${product_fullname} + ${installdir} + all + 0 + 0 + ${installdir}/${uninstallerName}.exe + + + ${installdir} + + + + + Program Files + ${installdir} + programfileslinux64 + linux-x64 + + + 1 + ${bin_prefix}/* + + + + + Program Files + ${installdir} + programfileslinux32 + linux + + + 1 + ${bin_prefix}/* + + + + + Program Files + ${installdir} + programfileswindows64 + windows-x64 + + + 1 + ${bin_prefix}/* + + + + + Program Files + ${installdir} + programfileswindows32 + windows + + + 1 + ${bin_prefix}/* + + + + + Program Files + ${installdir} + programfilesosx + osx + + + 1 + ${bin_prefix}/SQLiteStudio.app + + + + + + + Uninstall ${product_fullname} + Uninstall ${product_fullname} + 0 + 0 + ${installdir}/${uninstallerName}.exe + + + ${installdir}/ + + + Run ${product_shortname} + ${product_shortname} + 0 + 0 + ${installdir}/${product_shortname}.exe + + ${installdir}/appicon.ico + + + + + + + + src_prefix + ${env(INSTALLER_SRC_PREFIX)} + + + + + + src_prefix + C:/projects/sqlitestudio + + + + + + bin_prefix + ${env(INSTALLER_BIN_PREFIX)} + + + + + + bin_prefix + C:/projects/sqlitestudio/output/SQLiteStudio + + + + + + + + desktop_prefix + 1 + /usr/share/applications + + + + + + + desktop_prefix + 1 + ~/.local/share/applications + + + + + + + running_user + logname + + + + + + + + File association + + + .db .db3 .sqlite .sqlite3 .sdb .s3db + SQLite + ${installdir}/appicon.ico + application/vnd.sqlite3 + SQLiteStudio.GUI.3 + + + 1 + ${installdir}\SQLiteStudio.exe + "%1" + Open + + + + + + + + Linux + + + utf-8 + ${desktop_prefix}/SQLiteStudio.desktop + +[Desktop Entry] +Version=1.0 +Encoding=UTF-8 +Name=SQLiteStudio +GenericName=SQLiteStudio +Comment=SQLiteStudio +Icon=${installdir}/assets/appicon.png +Exec=${installdir}/sqlitestudio +Terminal=false +Type=Application +Categories=Application;Development;Database +MimeType=application/vnd.sqlite3 + + + + xdg-mime + default SQLiteStudio.desktop application/vnd.sqlite3 + + + + + + + + + + + + + + + .db .db3 .sqlite .sqlite3 .sdb .s3db + application/vnd.sqlite3 + SQLiteStudio.GUI.3 + + + + + + ${desktop_prefix}/SQLiteStudio.desktop + + + + + + lzma-ultra + 1 + 1 + 1 + 1 + sqlitestudio.pl + ${src_prefix}/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_opt.ico + ${project.fullName} + ${installdir}\appicon.ico + + + ${installdir}/SQLiteStudio.exe + & + Launch ${project.fullName} now. + + + + + + ${installdir}/sqlitestudio + & + Launch ${project.fullName} now. + + + + + + + ${installdir}/sqlitestudio + & + ${running_user} + Launch ${project.fullName} now. + + + + + + + ${installdir}/${product_shortname}.app/Contents/MacOS/SQLiteStudio + & + Launch ${project.fullName} now. + + + + + + + + install_options + Installation options + + + + installdir + + + install_for_grp + Install the application for: + + + + + install_for + + + + + 0 + radiobuttons + default + 40 + + + + + + + + + + + + + + + + + + + + file_assoc + Associate SQLite file extensions with SQLiteStudio + + 1 + 1 + yes + + + + + + + + + + + installdir + Installer.Parameter.installdir.description + Installer.Parameter.installdir.explanation + + ${platform_install_prefix}/${product_shortname} + 0 + prefix + 1 + 0 + 40 + + + + + platform_install_prefix + /opt + + + installdir + /opt/${product_shortname} + + + + + + + + + + + + diff --git a/SQLiteStudio3/SQLiteStudio3.pro b/SQLiteStudio3/SQLiteStudio3.pro index e5ca357..1fd7959 100644 --- a/SQLiteStudio3/SQLiteStudio3.pro +++ b/SQLiteStudio3/SQLiteStudio3.pro @@ -37,8 +37,12 @@ contains(DEFINES, tests) { OUTPUT_DIR_NAME = output macx: { - bundle.commands = sh $$PWD/create_macosx_bundle.sh $$PWD/../$$OUTPUT_DIR_NAME $$QMAKE_QMAKE - dmg.commands = sh $$PWD/create_macosx_bundle.sh $$PWD/../$$OUTPUT_DIR_NAME $$QMAKE_QMAKE dmg - pkg.commands = sh $$PWD/create_macosx_bundle.sh $$PWD/../$$OUTPUT_DIR_NAME $$QMAKE_QMAKE dist + bundle.commands = sh \"$$PWD/create_macosx_bundle.sh\" \"$$PWD/../$$OUTPUT_DIR_NAME\" \"$$QMAKE_QMAKE\" + dmg.commands = sh \"$$PWD/create_macosx_bundle.sh\" \"$$PWD/../$$OUTPUT_DIR_NAME\" \"$$QMAKE_QMAKE\" dmg + pkg.commands = sh \"$$PWD/create_macosx_bundle.sh\" \"$$PWD/../$$OUTPUT_DIR_NAME\" \"$$QMAKE_QMAKE\" dist QMAKE_EXTRA_TARGETS += bundle dmg pkg } + +OTHER_FILES += \ + ../ChangeLog.md \ + ../SQLiteStudio-installer.xml diff --git a/SQLiteStudio3/Tests/CompletionHelperTest/CompletionHelperTest.pro b/SQLiteStudio3/Tests/CompletionHelperTest/CompletionHelperTest.pro index d4c1e99..76d4951 100644 --- a/SQLiteStudio3/Tests/CompletionHelperTest/CompletionHelperTest.pro +++ b/SQLiteStudio3/Tests/CompletionHelperTest/CompletionHelperTest.pro @@ -10,7 +10,7 @@ QT += testlib QT -= gui TARGET = tst_completionhelpertest -CONFIG += console +CONFIG += console testcase CONFIG -= app_bundle TEMPLATE = app diff --git a/SQLiteStudio3/Tests/LexerTest/tst_lexertest.cpp b/SQLiteStudio3/Tests/LexerTest/tst_lexertest.cpp index 0e44e7d..abdc76f 100644 --- a/SQLiteStudio3/Tests/LexerTest/tst_lexertest.cpp +++ b/SQLiteStudio3/Tests/LexerTest/tst_lexertest.cpp @@ -16,6 +16,7 @@ class LexerTest : public QObject void testHex1(); void testHex2(); void testBindParam1(); + void testBlobLiteral(); }; LexerTest::LexerTest() @@ -89,6 +90,16 @@ void LexerTest::testBindParam1() QVERIFY(bindTokens[4]->value == "@id"); } +void LexerTest::testBlobLiteral() +{ + QString sql = "SELECT X'010f0E'"; + + Lexer lex; + TokenList tokens = lex.tokenize(sql); + QCOMPARE(tokens.size(), 3); + QCOMPARE(tokens[2]->value, "X'010f0E'"); +} + QTEST_APPLESS_MAIN(LexerTest) #include "tst_lexertest.moc" diff --git a/SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp b/SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp index 6d537dc..eec1e39 100644 --- a/SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp +++ b/SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp @@ -2,7 +2,6 @@ #include "parser/ast/sqliteselect.h" #include "parser/ast/sqlitecreatetable.h" #include "parser/ast/sqliteinsert.h" -#include "parser/ast/sqlitewith.h" #include "parser/ast/sqliteupdate.h" #include "parser/keywords.h" #include "parser/lexer.h" @@ -58,9 +57,17 @@ class ParserTest : public QObject void testGetColumnTokensFromInsertUpsert(); void testGeneratedColumn(); void testWindowClause(); + void testWindowKwAsColumn(); void testFilterClause(); + void testFilterAsId(); void testUpdateFrom(); void testStringAsTableId(); + void testJsonPtrOp(); + void testUnfinishedSelectWithAliasForCompleter(); + void testUnfinishedSelectWithAliasStrict(); + void testBlobLiteral(); + void testBigDec(); + void testQuotedFunction(); }; ParserTest::ParserTest() @@ -559,6 +566,15 @@ void ParserTest::testWindowClause() verifyWindowClause(sql, select, ok); } +void ParserTest::testWindowKwAsColumn() +{ + QString sql = "SELECT window FROM test_table;"; + parser3->setLemonDebug(true); + bool res = parser3->parse(sql); + QVERIFY(res); + QVERIFY(parser3->getErrors().isEmpty()); +} + void ParserTest::verifyWindowClause(const QString& sql, SqliteSelectPtr& select, bool& ok) { bool res = parser3->parse(sql); @@ -646,6 +662,18 @@ void ParserTest::testFilterClause() QVERIFY(resCol->expr->filterOver->over->window->mode == SqliteWindowDefinition::Window::Mode::ORDER_BY); } +void ParserTest::testFilterAsId() +{ + QString sql = "CREATE TABLE aa2 (sdfsdfdf, filter)"; + bool res = parser3->parse(sql); + QVERIFY(res); + QVERIFY(parser3->getErrors().isEmpty()); + + SqliteCreateTablePtr create = parser3->getQueries().first().dynamicCast(); + QCOMPARE(create->columns.size(), 2); + QCOMPARE(create->columns[1]->name, "filter"); +} + void ParserTest::testUpdateFrom() { QString sql = "UPDATE inventory" @@ -674,6 +702,92 @@ void ParserTest::testStringAsTableId() QVERIFY(parser3->getErrors().isEmpty()); } +void ParserTest::testJsonPtrOp() +{ + QString sql = "SELECT '[\"a11\", \"a22\", {\"x\":\"a33\"}]' -> 2," + " '[\"a11\", \"a22\", {\"x\":\"a33\"}]' ->> 2"; + bool res = parser3->parse(sql); + QVERIFY(res); + QVERIFY(parser3->getErrors().isEmpty()); +} + +void ParserTest::testUnfinishedSelectWithAliasForCompleter() +{ + QString sql = "select * from a1 x where x."; + bool res = parser3->parse(sql, true); + QVERIFY(res); + QVERIFY(parser3->getErrors().isEmpty()); +} + +void ParserTest::testUnfinishedSelectWithAliasStrict() +{ + QString sql = "select * from a1 x where x."; + bool res = parser3->parse(sql); + QVERIFY(!res); + QVERIFY(!parser3->getErrors().isEmpty()); +} + +void ParserTest::testBlobLiteral() +{ + QString sql = "insert into tab1 values (X'010e0F', 'string''with''quotes')"; + bool res = parser3->parse(sql); + SqliteQueryPtr query = parser3->getQueries()[0]; + QVERIFY(res); + QVERIFY(parser3->getErrors().isEmpty()); + + SqliteInsertPtr insert = parser3->getQueries().first().dynamicCast(); + SqliteSelect::Core* core = insert->select->coreSelects[0]; + QCOMPARE(core->resultColumns.size(), 2); + + QCOMPARE(core->resultColumns[0]->expr->mode, SqliteExpr::Mode::LITERAL_VALUE); + QCOMPARE(core->resultColumns[0]->expr->literalValue.type(), QVariant::ByteArray); + QCOMPARE(core->resultColumns[0]->expr->literalValue.toByteArray().toHex(), "010e0f"); + QCOMPARE(core->resultColumns[1]->expr->mode, SqliteExpr::Mode::LITERAL_VALUE); + QCOMPARE(core->resultColumns[1]->expr->literalValue.type(), QVariant::String); + QCOMPARE(core->resultColumns[1]->expr->literalValue.toString(), "string''with''quotes"); + + core->resultColumns[0]->expr->rebuildTokens(); + QCOMPARE(core->resultColumns[0]->expr->tokens[0]->value, "X'010e0f'"); +} + +void ParserTest::testBigDec() +{ + QString sql = "select 9999999999999999999 + 9999999999999999999, 9999999999999999999.1 + 9999999999999999999.2, 9999.1 + 9999.2;"; + bool res = parser3->parse(sql); + SqliteQueryPtr query = parser3->getQueries()[0]; + QVERIFY(res); + QVERIFY(parser3->getErrors().isEmpty()); + SqliteSelectPtr select = parser3->getQueries().first().dynamicCast(); + SqliteSelect::Core* core = select->coreSelects[0]; + QCOMPARE(core->resultColumns[0]->expr->expr1->literalValue.type(), QVariant::Double); + QCOMPARE(core->resultColumns[1]->expr->expr1->literalValue.type(), QVariant::Double); + QCOMPARE(core->resultColumns[2]->expr->expr1->literalValue.type(), QVariant::Double); +} + +void ParserTest::testQuotedFunction() +{ + QString sql = "select \"abs\"(1)"; + bool res = parser3->parse(sql); + QVERIFY(res); + QVERIFY(parser3->getQueries().size() > 0); + + SqliteQueryPtr query = parser3->getQueries().first(); + QVERIFY(query); + + SqliteSelectPtr select = query.dynamicCast(); + QVERIFY(select); + QVERIFY(select->coreSelects.size() > 0); + QVERIFY(select->coreSelects.first()->resultColumns.size() > 0); + + SqliteSelect::Core::ResultColumn* rc = select->coreSelects.first()->resultColumns.first(); + SqliteExpr* e = rc->expr; + QVERIFY(e); + + QVERIFY(e->mode == SqliteExpr::Mode::FUNCTION); + QCOMPARE(e->function, "abs"); +} + + void ParserTest::initTestCase() { initKeywords(); diff --git a/SQLiteStudio3/Tests/SelectResolverTest/tst_selectresolvertest.cpp b/SQLiteStudio3/Tests/SelectResolverTest/tst_selectresolvertest.cpp index 78f0b53..bcb142e 100644 --- a/SQLiteStudio3/Tests/SelectResolverTest/tst_selectresolvertest.cpp +++ b/SQLiteStudio3/Tests/SelectResolverTest/tst_selectresolvertest.cpp @@ -29,10 +29,12 @@ class SelectResolverTest : public QObject void testWithCommonTableExpression(); void testWithCte2(); void testWithCte3(); + void testWithCte4(); void testStarWithJoinAndError(); void testTableFunction(); void testSubselect(); void testSubselectWithAlias(); + void testIssue4607(); }; SelectResolverTest::SelectResolverTest() @@ -214,7 +216,7 @@ void SelectResolverTest::testWithCte2() QList coreColumns = columns.first(); QVERIFY(coreColumns.size() == 2); - QCOMPARE(coreColumns[0].type, SelectResolver::Column::COLUMN); + QCOMPARE(coreColumns[0].type, SelectResolver::Column::OTHER); QCOMPARE(coreColumns[0].flags, SelectResolver::FROM_CTE_SELECT); QCOMPARE(coreColumns[0].database, "main"); QVERIFY(coreColumns[0].table.isNull()); @@ -222,7 +224,7 @@ void SelectResolverTest::testWithCte2() QVERIFY(coreColumns[0].alias.isNull()); QCOMPARE(coreColumns[0].displayName, "c1"); - QCOMPARE(coreColumns[1].type, SelectResolver::Column::COLUMN); + QCOMPARE(coreColumns[1].type, SelectResolver::Column::OTHER); QCOMPARE(coreColumns[1].flags, SelectResolver::FROM_CTE_SELECT); QCOMPARE(coreColumns[1].database, "main"); QVERIFY(coreColumns[1].table.isNull()); @@ -242,10 +244,11 @@ void SelectResolverTest::testWithCte3() QList coreColumns = columns.first(); QCOMPARE(coreColumns.size(), 3); - QCOMPARE(coreColumns[0].type, SelectResolver::Column::COLUMN); + QCOMPARE(coreColumns[0].type, SelectResolver::Column::OTHER); QCOMPARE(coreColumns[0].flags, SelectResolver::FROM_CTE_SELECT); QCOMPARE(coreColumns[0].database, "main"); QVERIFY(coreColumns[0].table.isNull()); + QCOMPARE(coreColumns[0].tableAlias, "t"); QCOMPARE(coreColumns[0].column, "x"); QVERIFY(coreColumns[0].alias.isNull()); QCOMPARE(coreColumns[0].displayName, "x"); @@ -254,19 +257,43 @@ void SelectResolverTest::testWithCte3() QCOMPARE(coreColumns[1].flags, SelectResolver::FROM_CTE_SELECT); QCOMPARE(coreColumns[1].database, "main"); QCOMPARE(coreColumns[1].table, "test2"); + QCOMPARE(coreColumns[1].tableAlias, "t"); QCOMPARE(coreColumns[1].column, "y"); QVERIFY(coreColumns[1].alias.isNull()); QCOMPARE(coreColumns[1].displayName, "y"); - QCOMPARE(coreColumns[2].type, SelectResolver::Column::COLUMN); + QCOMPARE(coreColumns[2].type, SelectResolver::Column::OTHER); QCOMPARE(coreColumns[2].flags, SelectResolver::FROM_CTE_SELECT); QCOMPARE(coreColumns[2].database, "main"); QVERIFY(coreColumns[2].table.isNull()); + QCOMPARE(coreColumns[2].tableAlias, "t"); QCOMPARE(coreColumns[2].column, "'z'"); QVERIFY(coreColumns[2].alias.isNull()); QCOMPARE(coreColumns[2].displayName, "'z'"); } +void SelectResolverTest::testWithCte4() +{ + QString sql = "WITH testcte AS (SELECT 3)" + "SELECT * FROM TESTCTE"; + + SelectResolver resolver(db, sql); + Parser parser; + QVERIFY(parser.parse(sql)); + + QList> columns = resolver.resolve(parser.getQueries().first().dynamicCast().data()); + QList coreColumns = columns.first(); + QVERIFY(coreColumns.size() == 1); + + QCOMPARE(coreColumns[0].type, SelectResolver::Column::OTHER); + QCOMPARE(coreColumns[0].flags, SelectResolver::FROM_CTE_SELECT); + QCOMPARE(coreColumns[0].database, "main"); + QVERIFY(coreColumns[0].table.isNull()); + QCOMPARE(coreColumns[0].column, "3"); + QVERIFY(coreColumns[0].alias.isNull()); + QCOMPARE(coreColumns[0].displayName, "3"); +} + void SelectResolverTest::testStarWithJoinAndError() { QString sql = "SELECT t1.*, t2.* FROM test t1 JOIN test2 USING (col1)"; @@ -335,6 +362,48 @@ void SelectResolverTest::testSubselectWithAlias() QVERIFY(coreColumns[2].oldTableAliases.first() == "secm"); } +void SelectResolverTest::testIssue4607() +{ + QString sql = "SELECT Trip.TripID AS [Trip Number]," + " P1.PlaceAlternates AS [Origin Alternates]," + " P2.PlaceAlternates AS [Destination Alternates]," + " Trip.ROWID AS ResCol_0" + " FROM Trip" + " INNER JOIN" + " (" + " SELECT 1 AS PlaceID," + " 2 AS PlaceAlternates" + " )" + " P1 ON Trip.TripID = P1.PlaceID" + " INNER JOIN" + " (" + " SELECT 1 AS PlaceID," + " 2 AS PlaceAlternates" + " GROUP BY PlaceID" + " )" + " P2 ON Trip.TripID = P2.PlaceID"; + + SelectResolver resolver(db, sql); + Parser parser; + QVERIFY(parser.parse(sql)); + + QList > columns = resolver.resolve(parser.getQueries().first().dynamicCast().data()); + QList coreColumns = columns.first(); + QVERIFY(coreColumns.size() == 4); + QVERIFY(coreColumns[0].table == "Trip"); + QVERIFY(coreColumns[0].tableAlias.isNull()); + QVERIFY(coreColumns[0].flags == 0); + QVERIFY(coreColumns[1].table.isNull()); + QVERIFY(coreColumns[1].tableAlias == "P1"); + QVERIFY(coreColumns[1].flags == 0); + QVERIFY(coreColumns[2].table.isNull()); + QVERIFY(coreColumns[2].tableAlias == "P2"); + QVERIFY(coreColumns[2].flags & SelectResolver::FROM_GROUPED_SELECT); + QVERIFY(coreColumns[3].table == "Trip"); + QVERIFY(coreColumns[3].tableAlias.isNull()); + QVERIFY(coreColumns[3].flags == 0); +} + void SelectResolverTest::initTestCase() { initKeywords(); @@ -346,6 +415,7 @@ void SelectResolverTest::initTestCase() db->exec("CREATE TABLE test (col1, col2, col3);"); db->exec("CREATE TABLE org (name TEXT PRIMARY KEY, boss TEXT REFERENCES org, height INT)"); db->exec("CREATE TABLE test2 (col1);"); + db->exec("CREATE TABLE Trip (TripID INTEGER PRIMARY KEY ASC);"); //SqlQueryPtr results = db->exec("SELECT name FROM sqlite_master"); } diff --git a/SQLiteStudio3/Tests/TestUtils/TestUtils.pro b/SQLiteStudio3/Tests/TestUtils/TestUtils.pro index 727fb74..3ea06e6 100644 --- a/SQLiteStudio3/Tests/TestUtils/TestUtils.pro +++ b/SQLiteStudio3/Tests/TestUtils/TestUtils.pro @@ -41,7 +41,7 @@ unix:!symbian { maemo5 { target.path = /opt/usr/lib } else { - target.path = /usr/lib + target.path = $$PREFIX/lib } INSTALLS += target } diff --git a/SQLiteStudio3/Tests/TestUtils/configmock.cpp b/SQLiteStudio3/Tests/TestUtils/configmock.cpp index c52fa1b..f5ce5c5 100644 --- a/SQLiteStudio3/Tests/TestUtils/configmock.cpp +++ b/SQLiteStudio3/Tests/TestUtils/configmock.cpp @@ -4,10 +4,6 @@ void ConfigMock::init() { } -void ConfigMock::cleanUp() -{ -} - const QString& ConfigMock::getConfigDir() { static const QString cfg; @@ -194,16 +190,6 @@ void ConfigMock::rollback() { } -bool ConfigMock::setCollations(const QList&) -{ - return true; -} - -QList ConfigMock::getCollations() const -{ - return QList(); -} - const QString &ConfigMock::getConfigDir() const { static QString s; diff --git a/SQLiteStudio3/Tests/TestUtils/configmock.h b/SQLiteStudio3/Tests/TestUtils/configmock.h index a8a5a2c..620548b 100644 --- a/SQLiteStudio3/Tests/TestUtils/configmock.h +++ b/SQLiteStudio3/Tests/TestUtils/configmock.h @@ -9,7 +9,6 @@ class ConfigMock : public Config { public: void init(); - void cleanUp(); const QString& getConfigDir(); void beginMassSave(); void commitMassSave(); @@ -51,8 +50,6 @@ class ConfigMock : public Config void begin(); void commit(); void rollback(); - bool setCollations(const QList&); - QList getCollations() const; const QString &getConfigDir() const; QString getConfigFilePath() const; bool isMassSaving() const; diff --git a/SQLiteStudio3/Tests/TestUtils/extensionmanagermock.cpp b/SQLiteStudio3/Tests/TestUtils/extensionmanagermock.cpp index dc4669b..d98fc42 100644 --- a/SQLiteStudio3/Tests/TestUtils/extensionmanagermock.cpp +++ b/SQLiteStudio3/Tests/TestUtils/extensionmanagermock.cpp @@ -17,3 +17,8 @@ QList ExtensionManagerMock::getExtensionFo { return QList(); } + +QStringList ExtensionManagerMock::getExtensionDirs() const +{ + return QStringList(); +} diff --git a/SQLiteStudio3/Tests/TestUtils/extensionmanagermock.h b/SQLiteStudio3/Tests/TestUtils/extensionmanagermock.h index 7a7a1f4..37bd634 100644 --- a/SQLiteStudio3/Tests/TestUtils/extensionmanagermock.h +++ b/SQLiteStudio3/Tests/TestUtils/extensionmanagermock.h @@ -12,6 +12,7 @@ class ExtensionManagerMock : public SqliteExtensionManager void setExtensions(const QList&); QList getAllExtensions() const; QList getExtensionForDatabase(const QString&) const; + QStringList getExtensionDirs() const; }; #endif // EXTENSIONMANAGERMOCK_H diff --git a/SQLiteStudio3/Tests/TestUtils/test_common.pri b/SQLiteStudio3/Tests/TestUtils/test_common.pri index 24d1479..4eb550b 100644 --- a/SQLiteStudio3/Tests/TestUtils/test_common.pri +++ b/SQLiteStudio3/Tests/TestUtils/test_common.pri @@ -1,5 +1,5 @@ -include($$PWD/../../dirs.pri) -include($$PWD/../testdirs.pri) +include($$PWD/../../common.pri) +include($$PWD/../testcommon.pri) CONFIG += c++17 diff --git a/SQLiteStudio3/Tests/testcommon.pri b/SQLiteStudio3/Tests/testcommon.pri new file mode 100644 index 0000000..131bd4d --- /dev/null +++ b/SQLiteStudio3/Tests/testcommon.pri @@ -0,0 +1,4 @@ +LIBS += -L$$PWD/../$$OUTPUT_DIR_NAME/SQLiteStudio + +INCLUDEPATH += $$PWD/TestUtils +DEPENDPATH += $$PWD/TestUtils diff --git a/SQLiteStudio3/Tests/testdirs.pri b/SQLiteStudio3/Tests/testdirs.pri deleted file mode 100644 index 131bd4d..0000000 --- a/SQLiteStudio3/Tests/testdirs.pri +++ /dev/null @@ -1,4 +0,0 @@ -LIBS += -L$$PWD/../$$OUTPUT_DIR_NAME/SQLiteStudio - -INCLUDEPATH += $$PWD/TestUtils -DEPENDPATH += $$PWD/TestUtils diff --git a/SQLiteStudio3/common.pri b/SQLiteStudio3/common.pri new file mode 100644 index 0000000..c3155a7 --- /dev/null +++ b/SQLiteStudio3/common.pri @@ -0,0 +1,62 @@ +OUTPUT_DIR_NAME = output +export(OUTPUT_DIR_NAME) + +DESTDIR = $$PWD/../$$OUTPUT_DIR_NAME/SQLiteStudio +OBJECTS_DIR = $$PWD/../$$OUTPUT_DIR_NAME/build +MOC_DIR = $$PWD/../$$OUTPUT_DIR_NAME/build +UI_DIR = $$PWD/../$$OUTPUT_DIR_NAME/build + +LIBS += -L$$DESTDIR + +macx: { + QMAKE_CXXFLAGS += -Wno-gnu-zero-variadic-macro-arguments -Wno-overloaded-virtual + INCLUDEPATH += $$PWD/../../include + LIBS += -L$$PWD/../../lib +} + +win32: { + INCLUDEPATH += $$PWD/../../include + LIBS += -L$$PWD/../../lib +} + +INCLUDEPATH += $$PWD/coreSQLiteStudio +DEPENDPATH += $$PWD/coreSQLiteStudio + +contains(QT, gui): { + INCLUDEPATH += $$PWD/guiSQLiteStudio $$PWD/../$$OUTPUT_DIR_NAME/build/guiSQLiteStudio + DEPENDPATH += $$PWD/guiSQLiteStudio +} + +win32|macx: { + CONFIG += portable +} + +portable { + QMAKE_LFLAGS += -Wl,-rpath,. + linux: { + LIBS += -L$$DESTDIR/lib + } +} + +unix: { + isEmpty(LIBDIR) { + LIBDIR = $$PREFIX/lib + } + export(LIBDIR) + isEmpty(BINDIR) { + BINDIR = $$PREFIX/bin + } + export(BINDIR) +} + +# Enable automatic translation files processing globally +QMAKE_RESOURCE_FLAGS += -name $${TARGET}_${QMAKE_FILE_BASE} +TRANSLATIONS += $$files($$_PRO_FILE_PWD_/translations/*.ts) +defined(TARGET, "var") { + DEFINES += "PROJECT_MODULE_NAME=$${TARGET}" +} +!isEmpty(TRANSLATIONS) { + CONFIG += lrelease embed_translations + QM_FILES_RESOURCE_PREFIX = /msg/translations +} + diff --git a/SQLiteStudio3/coreSQLiteStudio/Info.plist b/SQLiteStudio3/coreSQLiteStudio/Info.plist index 794e404..9b29626 100644 --- a/SQLiteStudio3/coreSQLiteStudio/Info.plist +++ b/SQLiteStudio3/coreSQLiteStudio/Info.plist @@ -46,4 +46,4 @@ - + \ No newline at end of file diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/LICENSE-chillout b/SQLiteStudio3/coreSQLiteStudio/chillout/LICENSE-chillout new file mode 100644 index 0000000..e0a4e10 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/LICENSE-chillout @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Taras Kushnir + +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. diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/README b/SQLiteStudio3/coreSQLiteStudio/chillout/README new file mode 100644 index 0000000..b01444c --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/README @@ -0,0 +1,4 @@ +Chillout crash handler +https://gitlab.com/ribtoks/chillout + +This is subset of the original code. Some parts were removed, some were changed to adjust it for compilation with Mingw under Windows and to remove parts unused in SQLiteStudio. \ No newline at end of file diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/chillout.cpp b/SQLiteStudio3/coreSQLiteStudio/chillout/chillout.cpp new file mode 100644 index 0000000..87f8c79 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/chillout.cpp @@ -0,0 +1,47 @@ +#include "chillout.h" + +#ifdef _WIN32 +#include "windows/windowscrashhandler.h" +#else +#include "posix/posixcrashhandler.h" +#endif + +namespace Debug { + void Chillout::init(const string_t &appName, const string_t &pathToDumpsDir) { + if (0 == m_InitCounter.fetch_add(1)) { +#ifdef _WIN32 + WindowsCrashHandler &handler = WindowsCrashHandler::getInstance(); +#else + PosixCrashHandler &handler = PosixCrashHandler::getInstance(); +#endif + handler.setup(appName, pathToDumpsDir); + } + } + + void Chillout::deinit() { +#ifdef _WIN32 + WindowsCrashHandler &handler = WindowsCrashHandler::getInstance(); +#else + PosixCrashHandler &handler = PosixCrashHandler::getInstance(); +#endif + handler.teardown(); + } + + void Chillout::setBacktraceCallback(const std::function &callback) { +#ifdef _WIN32 + WindowsCrashHandler &handler = WindowsCrashHandler::getInstance(); +#else + PosixCrashHandler &handler = PosixCrashHandler::getInstance(); +#endif + handler.setBacktraceCallback(callback); + } + + void Chillout::setCrashCallback(const std::function &callback) { +#ifdef _WIN32 + WindowsCrashHandler &handler = WindowsCrashHandler::getInstance(); +#else + PosixCrashHandler &handler = PosixCrashHandler::getInstance(); +#endif + handler.setCrashCallback(callback); + } +} diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/chillout.h b/SQLiteStudio3/coreSQLiteStudio/chillout/chillout.h new file mode 100644 index 0000000..2c8000a --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/chillout.h @@ -0,0 +1,42 @@ +#ifndef CHILLOUT_H +#define CHILLOUT_H + +#include +#include +#include +#include "common/common.h" + +namespace Debug { + class Chillout { + public: + static Chillout& getInstance() + { + static Chillout instance; // Guaranteed to be destroyed. + // Instantiated on first use. + return instance; + } + + public: +#ifdef _WIN32 + typedef std::wstring string_t; +#else + typedef std::string string_t; +#endif + + public: + void init(const string_t &appName, const string_t &pathToDumpsDir); + void deinit(); + void setBacktraceCallback(const std::function &callback); + void setCrashCallback(const std::function &callback); + + private: + Chillout(): m_InitCounter(0) {} + Chillout(Chillout const&); + void operator=(Chillout const&); + + private: + std::atomic_int m_InitCounter; + }; +} + +#endif // CHILLOUT_H diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/common/common.cpp b/SQLiteStudio3/coreSQLiteStudio/chillout/common/common.cpp new file mode 100644 index 0000000..2f51fc1 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/common/common.cpp @@ -0,0 +1,24 @@ +#include "common.h" +#include +#include +#include + +namespace Debug { + tm now() { + time_t now = time(0); + return *localtime(&now); + } + +#ifdef _WIN32 + std::wostream& formatDateTime(std::wostream& out, const tm& t, const wchar_t* fmt) { + const std::time_put& dateWriter = std::use_facet >(out.getloc()); + const size_t n = wcslen(fmt); +#else + std::ostream& formatDateTime(std::ostream& out, const tm& t, const char* fmt) { + const std::time_put& dateWriter = std::use_facet >(out.getloc()); + const size_t n = strlen(fmt); +#endif + dateWriter.put(out, out, ' ', &t, fmt, fmt + n); + return out; + } +} diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/common/common.h b/SQLiteStudio3/coreSQLiteStudio/chillout/common/common.h new file mode 100644 index 0000000..85b479a --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/common/common.h @@ -0,0 +1,24 @@ +#ifndef COMMON_H +#define COMMON_H + +#include +#include + +#define CHILLOUT_DATETIME "%Y%m%d_%H%M%S" + +namespace Debug { + tm now(); +#ifdef _WIN32 + std::wostream& formatDateTime(std::wostream& out, const tm& t, const wchar_t *fmt); +#else + std::ostream& formatDateTime(std::ostream& out, const tm& t, const char* fmt); +#endif + + enum CrashDumpSize { + CrashDumpSmall, + CrashDumpNormal, + CrashDumpFull + }; +} + +#endif // COMMON_H diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/defines.h b/SQLiteStudio3/coreSQLiteStudio/chillout/defines.h new file mode 100644 index 0000000..59f8db8 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/defines.h @@ -0,0 +1,16 @@ +#ifndef CHILLOUTDEFINES_H +#define CHILLOUTDEFINES_H + +#define CHILLOUT_EXIT_CODE 3 + +#define STRINGIZE_(x) #x +#define STRINGIZE(x) STRINGIZE_(x) + +#ifdef _WIN32 +#define WIDEN(quote) WIDEN2(quote) +#define WIDEN2(quote) L##quote +#else +#define WIDEN(quote) quote +#endif + +#endif // CHILLOUTDEFINES_H diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/posix/posixcrashhandler.cpp b/SQLiteStudio3/coreSQLiteStudio/chillout/posix/posixcrashhandler.cpp new file mode 100644 index 0000000..a9e95a6 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/posix/posixcrashhandler.cpp @@ -0,0 +1,146 @@ +#include "posixcrashhandler.h" + +#ifndef _WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../defines.h" +#include "../common/common.h" + +#define KILOBYTE 1024 +#define DEMANGLE_MEMORY_SIZE (10*(KILOBYTE)) +#define STACK_MEMORY_SIZE (90*(KILOBYTE)) + +namespace Debug { + struct FreeDeleter { + void operator()(void* ptr) const { + free(ptr); + } + }; + + char *fake_alloc(char **memory, size_t size) { + char *allocated = *memory; + char *last = allocated + size; + *last = '\0'; + *memory += size + 1; + return allocated; + } + + void chilltrace(const char * const stackEntry) { + if (stackEntry) { + fputs(stackEntry,stderr); + } + } + + void posixSignalHandler( int signum, siginfo_t* si, void* ucontext ) { + (void)si; + (void)ucontext; + + auto &handler = PosixCrashHandler::getInstance(); + handler.handleCrash(); + + // If you caught one of the above signals, it is likely you just + // want to quit your program right now. + //exit( signum ); + std::_Exit(CHILLOUT_EXIT_CODE); + } + + PosixCrashHandler::PosixCrashHandler(): + m_stackMemory(nullptr), + m_demangleMemory(nullptr) + { + m_stackMemory = (char*)malloc(STACK_MEMORY_SIZE); + memset(&m_stackMemory[0], 0, STACK_MEMORY_SIZE); + + m_demangleMemory = (char*)malloc(DEMANGLE_MEMORY_SIZE); + memset(&m_demangleMemory[0], 0, DEMANGLE_MEMORY_SIZE); + + m_backtraceCallback = chilltrace; + } + + PosixCrashHandler::~PosixCrashHandler() { + free(m_stackMemory); + free(m_demangleMemory); + } + + void PosixCrashHandler::setup(const std::string &appName, const std::string &crashDirPath) { + struct sigaction sa; + sa.sa_sigaction = posixSignalHandler; + sigemptyset( &sa.sa_mask ); + +#ifdef __APPLE__ + /* for some reason we backtrace() doesn't work on osx + when we use an alternate stack */ + sa.sa_flags = SA_SIGINFO; +#else + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; +#endif + + sigaction( SIGABRT, &sa, NULL ); + sigaction( SIGSEGV, &sa, NULL ); + sigaction( SIGBUS, &sa, NULL ); + sigaction( SIGILL, &sa, NULL ); + sigaction( SIGFPE, &sa, NULL ); + sigaction( SIGPIPE, &sa, NULL ); + sigaction( SIGTERM, &sa, NULL ); + + if (!crashDirPath.empty()) { + std::string path = crashDirPath; + while ((path.size() > 1) && + (path[path.size() - 1] == '/')) { + path.erase(path.size() - 1); + } + + std::stringstream s; + s << path << "/" << appName << "_"; + formatDateTime(s, now(), CHILLOUT_DATETIME); + s << ".bktr"; + m_backtraceFilePath = s.str(); + } + } + + void PosixCrashHandler::teardown() { + struct sigaction sa; + sigset_t mysigset; + + sigemptyset(&mysigset); + + sa.sa_handler = SIG_DFL; + sa.sa_mask = mysigset; + sa.sa_flags = 0; + + sigaction( SIGABRT, &sa, NULL ); + sigaction( SIGSEGV, &sa, NULL ); + sigaction( SIGBUS, &sa, NULL ); + sigaction( SIGILL, &sa, NULL ); + sigaction( SIGFPE, &sa, NULL ); + sigaction( SIGPIPE, &sa, NULL ); + sigaction( SIGTERM, &sa, NULL ); + } + + void PosixCrashHandler::handleCrash() { + if (m_crashCallback) { + m_crashCallback(); + } + } + + void PosixCrashHandler::setCrashCallback(const std::function &callback) { + m_crashCallback = callback; + } + + void PosixCrashHandler::setBacktraceCallback(const std::function &callback) { + m_backtraceCallback = callback; + } +} + +#endif diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/posix/posixcrashhandler.h b/SQLiteStudio3/coreSQLiteStudio/chillout/posix/posixcrashhandler.h new file mode 100644 index 0000000..f2036a7 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/posix/posixcrashhandler.h @@ -0,0 +1,45 @@ +#ifndef POSIXCRASHHANDLER_H +#define POSIXCRASHHANDLER_H + +#ifndef _WIN32 + +#include +#include + +namespace Debug { + class PosixCrashHandler { + public: + static PosixCrashHandler& getInstance() + { + static PosixCrashHandler instance; // Guaranteed to be destroyed. + // Instantiated on first use. + return instance; + } + + private: + PosixCrashHandler(); + ~PosixCrashHandler(); + + public: + void setup(const std::string &appName, const std::string &crashDirPath); + void teardown(); + void handleCrash(); + void setCrashCallback(const std::function &callback); + void setBacktraceCallback(const std::function &callback); + + private: + void walkStackTrace(char *memory, size_t memorySize, int maxFrames=128); + char *dlDemangle(void *addr, char *symbol, int frameIndex, char *stackMemory); + + private: + std::function m_crashCallback; + std::function m_backtraceCallback; + char *m_stackMemory; + char *m_demangleMemory; + std::string m_backtraceFilePath; + }; +} + +#endif // _WIN32 + +#endif // POSIXCRASHHANDLER_H diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/windows/StackWalker.cpp b/SQLiteStudio3/coreSQLiteStudio/chillout/windows/StackWalker.cpp new file mode 100644 index 0000000..91d6824 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/windows/StackWalker.cpp @@ -0,0 +1,1382 @@ +/********************************************************************** + * + * StackWalker.cpp + * http://stackwalker.codeplex.com/ + * + * + * History: + * 2005-07-27 v1 - First public release on http://www.codeproject.com/ + * http://www.codeproject.com/threads/StackWalker.asp + * 2005-07-28 v2 - Changed the params of the constructor and ShowCallstack + * (to simplify the usage) + * 2005-08-01 v3 - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL + * (should also be enough) + * - Changed to compile correctly with the PSDK of VC7.0 + * (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined: + * it uses LPSTR instead of LPCSTR as first paremeter) + * - Added declarations to support VC5/6 without using 'dbghelp.h' + * - Added a 'pUserData' member to the ShowCallstack function and the + * PReadProcessMemoryRoutine declaration (to pass some user-defined data, + * which can be used in the readMemoryFunction-callback) + * 2005-08-02 v4 - OnSymInit now also outputs the OS-Version by default + * - Added example for doing an exception-callstack-walking in main.cpp + * (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268) + * 2005-08-05 v5 - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse! + * 2008-08-04 v6 - Fixed Bug: Missing LEAK-end-tag + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2502890#xx2502890xx + * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN" + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx + * Fixed Bug: Compiling with "/Wall" + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx + * Fixed Bug: Now checking SymUseSymSrv + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1388979#xx1388979xx + * Fixed Bug: Support for recursive function calls + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1434538#xx1434538xx + * Fixed Bug: Missing FreeLibrary call in "GetModuleListTH32" + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1326923#xx1326923xx + * Fixed Bug: SymDia is number 7, not 9! + * 2008-09-11 v7 For some (undocumented) reason, dbhelp.h is needing a packing of 8! + * Thanks to Teajay which reported the bug... + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2718933#xx2718933xx + * 2008-11-27 v8 Debugging Tools for Windows are now stored in a different directory + * Thanks to Luiz Salamon which reported this "bug"... + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2822736#xx2822736xx + * 2009-04-10 v9 License slihtly corrected ( replaced) + * 2009-11-01 v10 Moved to http://stackwalker.codeplex.com/ + * 2009-11-02 v11 Now try to use IMAGEHLP_MODULE64_V3 if available + * 2010-04-15 v12 Added support for VS2010 RTM + * 2010-05-25 v13 Now using secure MyStrcCpy. Thanks to luke.simon: + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3477467#xx3477467xx + * 2013-01-07 v14 Runtime Check Error VS2010 Debug Builds fixed: + * http://stackwalker.codeplex.com/workitem/10511 + * + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2013, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ +#ifdef _WIN32 + +#include +#include +#include +#include +#include "StackWalker.h" + +#pragma GCC diagnostic ignored "-Wcast-function-type" +#pragma GCC diagnostic ignored "-Wformat=" + +// If VC7 and later, then use the shipped 'dbghelp.h'-file +#pragma pack(push,8) +#if _MSC_VER >= 1300 +#include +#else +// inline the important dbghelp.h-declarations... +typedef enum { + SymNone = 0, + SymCoff, + SymCv, + SymPdb, + SymExport, + SymDeferred, + SymSym, + SymDia, + SymVirtual, + NumSymTypes +} SYM_TYPE; +typedef struct _IMAGEHLP_LINE64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64) + PVOID Key; // internal + DWORD LineNumber; // line number in file + PCHAR FileName; // full filename + DWORD64 Address; // first instruction of line +} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64; +typedef struct _IMAGEHLP_MODULE64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name +} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64; +typedef struct _IMAGEHLP_SYMBOL64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64) + DWORD64 Address; // virtual address including dll base address + DWORD Size; // estimated size of symbol, can be zero + DWORD Flags; // info about the symbols, see the SYMF defines + DWORD MaxNameLength; // maximum size of symbol name in 'Name' + CHAR Name[1]; // symbol name (null terminated string) +} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64; +typedef enum { + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat +} ADDRESS_MODE; +typedef struct _tagADDRESS64 { + DWORD64 Offset; + WORD Segment; + ADDRESS_MODE Mode; +} ADDRESS64, *LPADDRESS64; +typedef struct _KDHELP64 { + DWORD64 Thread; + DWORD ThCallbackStack; + DWORD ThCallbackBStore; + DWORD NextCallback; + DWORD FramePointer; + DWORD64 KiCallUserMode; + DWORD64 KeUserCallbackDispatcher; + DWORD64 SystemRangeStart; + DWORD64 Reserved[8]; +} KDHELP64, *PKDHELP64; +typedef struct _tagSTACKFRAME64 { + ADDRESS64 AddrPC; // program counter + ADDRESS64 AddrReturn; // return address + ADDRESS64 AddrFrame; // frame pointer + ADDRESS64 AddrStack; // stack pointer + ADDRESS64 AddrBStore; // backing store pointer + PVOID FuncTableEntry; // pointer to pdata/fpo or NULL + DWORD64 Params[4]; // possible arguments to the function + BOOL Far; // WOW far call + BOOL Virtual; // is this a virtual frame? + DWORD64 Reserved[3]; + KDHELP64 KdHelp; +} STACKFRAME64, *LPSTACKFRAME64; +typedef +BOOL +(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead + ); +typedef +PVOID +(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( + HANDLE hProcess, + DWORD64 AddrBase + ); +typedef +DWORD64 +(__stdcall *PGET_MODULE_BASE_ROUTINE64)( + HANDLE hProcess, + DWORD64 Address + ); +typedef +DWORD64 +(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)( + HANDLE hProcess, + HANDLE hThread, + LPADDRESS64 lpaddr + ); +#define SYMOPT_CASE_INSENSITIVE 0x00000001 +#define SYMOPT_UNDNAME 0x00000002 +#define SYMOPT_DEFERRED_LOADS 0x00000004 +#define SYMOPT_NO_CPP 0x00000008 +#define SYMOPT_LOAD_LINES 0x00000010 +#define SYMOPT_OMAP_FIND_NEAREST 0x00000020 +#define SYMOPT_LOAD_ANYTHING 0x00000040 +#define SYMOPT_IGNORE_CVREC 0x00000080 +#define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 +#define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 +#define SYMOPT_EXACT_SYMBOLS 0x00000400 +#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 +#define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 +#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 +#define SYMOPT_PUBLICS_ONLY 0x00004000 +#define SYMOPT_NO_PUBLICS 0x00008000 +#define SYMOPT_AUTO_PUBLICS 0x00010000 +#define SYMOPT_NO_IMAGE_SEARCH 0x00020000 +#define SYMOPT_SECURE 0x00040000 +#define SYMOPT_DEBUG 0x80000000 +#define UNDNAME_COMPLETE (0x0000) // Enable full undecoration +#define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration; +#endif // _MSC_VER < 1300 +#pragma pack(pop) + +// Some missing defines (for VC5/6): +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#endif + + +// secure-CRT_functions are only available starting with VC8 +#ifdef _MSC_VER +#if _MSC_VER < 1400 +#define strcpy_s(dst, len, src) strcpy(dst, src) +#define strncpy_s(dst, len, src, maxLen) strncpy(dst, len, src) +#define strcat_s(dst, len, src) strcat(dst, src) +#define _snprintf_s _snprintf +#define _tcscat_s _tcscat +#endif +#endif + +#ifdef __MINGW32__ +#define strcpy_s(dst, len, src) strcpy(dst, src) +#define strncpy_s(dst, len, src, maxLen) strncpy(dst, src, len) +#define strcat_s(dst, len, src) strcat(dst, src) +#define _snprintf_s _snprintf +#endif + +static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc) +{ + if (nMaxDestSize <= 0) return; + strncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE); + szDest[nMaxDestSize-1] = 0; // INFO: _TRUNCATE will ensure that it is nul-terminated; but with older compilers (<1400) it uses "strncpy" and this does not!) +} // MyStrCpy + +// Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL') +#define USED_CONTEXT_FLAGS CONTEXT_FULL + + +class StackWalkerInternal +{ +public: + StackWalkerInternal(StackWalker *parent, HANDLE hProcess) + { + m_parent = parent; + m_hDbhHelp = NULL; + pSC = NULL; + m_hProcess = hProcess; + m_szSymPath = NULL; + pSFTA = NULL; + pSGLFA = NULL; + pSGMB = NULL; + pSGMI = NULL; + pSGO = NULL; + pSGSFA = NULL; + pSI = NULL; + pSLM = NULL; + pSSO = NULL; + pSW = NULL; + pUDSN = NULL; + pSGSP = NULL; + } + ~StackWalkerInternal() + { + if (pSC != NULL) + pSC(m_hProcess); // SymCleanup + if (m_hDbhHelp != NULL) + FreeLibrary(m_hDbhHelp); + m_hDbhHelp = NULL; + m_parent = NULL; + if(m_szSymPath != NULL) + free(m_szSymPath); + m_szSymPath = NULL; + } + BOOL Init(LPCSTR szSymPath) + { + if (m_parent == NULL) + return FALSE; + // Dynamically load the Entry-Points for dbghelp.dll: + // First try to load the newsest one from + TCHAR szTemp[4096]; + // But before wqe do this, we first check if the ".local" file exists + if (GetModuleFileName(NULL, szTemp, 4096) > 0) + { + _tcscat_s(szTemp, _T(".local")); + if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES) + { + // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows" + // Ok, first try the new path according to the archtitecture: +#ifdef _M_IX86 + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#elif _M_X64 + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#elif _M_IA64 + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#endif + // If still not found, try the old directories... + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#if defined _M_X64 || defined _M_IA64 + // Still not found? Then try to load the (old) 64-Bit version: + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll")); + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#endif + } + } + if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one + m_hDbhHelp = LoadLibrary( _T("dbghelp.dll") ); + if (m_hDbhHelp == NULL) + return FALSE; + pSI = (tSI) GetProcAddress(m_hDbhHelp, "SymInitialize" ); + pSC = (tSC) GetProcAddress(m_hDbhHelp, "SymCleanup" ); + + pSW = (tSW) GetProcAddress(m_hDbhHelp, "StackWalk64" ); + pSGO = (tSGO) GetProcAddress(m_hDbhHelp, "SymGetOptions" ); + pSSO = (tSSO) GetProcAddress(m_hDbhHelp, "SymSetOptions" ); + + pSFTA = (tSFTA) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" ); + pSGLFA = (tSGLFA) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" ); + pSGMB = (tSGMB) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" ); + pSGMI = (tSGMI) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" ); + pSGSFA = (tSGSFA) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" ); + pUDSN = (tUDSN) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" ); + pSLM = (tSLM) GetProcAddress(m_hDbhHelp, "SymLoadModule64" ); + pSGSP =(tSGSP) GetProcAddress(m_hDbhHelp, "SymGetSearchPath" ); + + if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || + pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL || + pSW == NULL || pUDSN == NULL || pSLM == NULL ) + { + FreeLibrary(m_hDbhHelp); + m_hDbhHelp = NULL; + pSC = NULL; + return FALSE; + } + + // SymInitialize + if (szSymPath != NULL) + m_szSymPath = _strdup(szSymPath); + if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE) + this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0); + + DWORD symOptions = this->pSGO(); // SymGetOptions + symOptions |= SYMOPT_LOAD_LINES; + symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; + //symOptions |= SYMOPT_NO_PROMPTS; + // SymSetOptions + symOptions = this->pSSO(symOptions); + + char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0}; + if (this->pSGSP != NULL) + { + if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE) + this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0); + } + char szUserName[1024] = {0}; + DWORD dwSize = 1024; + GetUserNameA(szUserName, &dwSize); + this->m_parent->OnSymInit(buf, symOptions, szUserName); + + return TRUE; + } + + StackWalker *m_parent; + + HMODULE m_hDbhHelp; + HANDLE m_hProcess; + LPSTR m_szSymPath; + +#pragma pack(push,8) +struct IMAGEHLP_MODULE64_V3 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name + // new elements: 07-Jun-2002 + CHAR LoadedPdbName[256]; // pdb file name + DWORD CVSig; // Signature of the CV record in the debug directories + CHAR CVData[MAX_PATH * 3]; // Contents of the CV record + DWORD PdbSig; // Signature of PDB + GUID PdbSig70; // Signature of PDB (VC 7 and up) + DWORD PdbAge; // DBI age of pdb + BOOL PdbUnmatched; // loaded an unmatched pdb + BOOL DbgUnmatched; // loaded an unmatched dbg + BOOL LineNumbers; // we have line number information + BOOL GlobalSymbols; // we have internal symbol information + BOOL TypeInfo; // we have type information + // new elements: 17-Dec-2003 + BOOL SourceIndexed; // pdb supports source server + BOOL Publics; // contains public symbols +}; + +struct IMAGEHLP_MODULE64_V2 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name +}; +#pragma pack(pop) + + + // SymCleanup() + typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess ); + tSC pSC; + + // SymFunctionTableAccess64() + typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase ); + tSFTA pSFTA; + + // SymGetLineFromAddr64() + typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, + OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line ); + tSGLFA pSGLFA; + + // SymGetModuleBase64() + typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr ); + tSGMB pSGMB; + + // SymGetModuleInfo64() + typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3 *ModuleInfo ); + tSGMI pSGMI; + + // SymGetOptions() + typedef DWORD (__stdcall *tSGO)( VOID ); + tSGO pSGO; + + // SymGetSymFromAddr64() + typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, + OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol ); + tSGSFA pSGSFA; + + // SymInitialize() + typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess ); + tSI pSI; + + // SymLoadModule64() + typedef DWORD64 (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile, + IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll ); + tSLM pSLM; + + // SymSetOptions() + typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions ); + tSSO pSSO; + + // StackWalk64() + typedef BOOL (__stdcall *tSW)( + DWORD MachineType, + HANDLE hProcess, + HANDLE hThread, + LPSTACKFRAME64 StackFrame, + PVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress ); + tSW pSW; + + // UnDecorateSymbolName() + typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName, + DWORD UndecoratedLength, DWORD Flags ); + tUDSN pUDSN; + + typedef BOOL (__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength); + tSGSP pSGSP; + + +private: + // **************************************** ToolHelp32 ************************ + #define MAX_MODULE_NAME32 255 + #define TH32CS_SNAPMODULE 0x00000008 + #pragma pack( push, 8 ) + typedef struct tagMODULEENTRY32 + { + DWORD dwSize; + DWORD th32ModuleID; // This module + DWORD th32ProcessID; // owning process + DWORD GlblcntUsage; // Global usage count on the module + DWORD ProccntUsage; // Module usage count in th32ProcessID's context + BYTE * modBaseAddr; // Base address of module in th32ProcessID's context + DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr + HMODULE hModule; // The hModule of this module in th32ProcessID's context + char szModule[MAX_MODULE_NAME32 + 1]; + char szExePath[MAX_PATH]; + } MODULEENTRY32; + typedef MODULEENTRY32 * PMODULEENTRY32; + typedef MODULEENTRY32 * LPMODULEENTRY32; + #pragma pack( pop ) + + BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid) + { + // CreateToolhelp32Snapshot() + typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID); + // Module32First() + typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + // Module32Next() + typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + + // try both dlls... + const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") }; + HINSTANCE hToolhelp = NULL; + tCT32S pCT32S = NULL; + tM32F pM32F = NULL; + tM32N pM32N = NULL; + + HANDLE hSnap; + MODULEENTRY32 me; + me.dwSize = sizeof(me); + BOOL keepGoing; + size_t i; + + for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ ) + { + hToolhelp = LoadLibrary( dllname[i] ); + if (hToolhelp == NULL) + continue; + pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot"); + pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First"); + pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next"); + if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) ) + break; // found the functions! + FreeLibrary(hToolhelp); + hToolhelp = NULL; + } + + if (hToolhelp == NULL) + return FALSE; + + hSnap = pCT32S( TH32CS_SNAPMODULE, pid ); + if (hSnap == (HANDLE) -1) + { + FreeLibrary(hToolhelp); + return FALSE; + } + + keepGoing = !!pM32F( hSnap, &me ); + int cnt = 0; + while (keepGoing) + { + this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize); + cnt++; + keepGoing = !!pM32N( hSnap, &me ); + } + CloseHandle(hSnap); + FreeLibrary(hToolhelp); + if (cnt <= 0) + return FALSE; + return TRUE; + } // GetModuleListTH32 + + // **************************************** PSAPI ************************ + typedef struct _MODULEINFO { + LPVOID lpBaseOfDll; + DWORD SizeOfImage; + LPVOID EntryPoint; + } MODULEINFO, *LPMODULEINFO; + + BOOL GetModuleListPSAPI(HANDLE hProcess) + { + // EnumProcessModules() + typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded ); + // GetModuleFileNameEx() + typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize ); + // GetModuleBaseName() + typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize ); + // GetModuleInformation() + typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize ); + + HINSTANCE hPsapi; + tEPM pEPM; + tGMFNE pGMFNE; + tGMBN pGMBN; + tGMI pGMI; + + DWORD i; + //ModuleEntry e; + DWORD cbNeeded; + MODULEINFO mi; + HMODULE *hMods = 0; + char *tt = NULL; + char *tt2 = NULL; + const SIZE_T TTBUFLEN = 8096; + int cnt = 0; + + hPsapi = LoadLibrary( _T("psapi.dll") ); + if (hPsapi == NULL) + return FALSE; + + pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" ); + pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" ); + pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" ); + pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" ); + if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) ) + { + // we couldn´t find all functions + FreeLibrary(hPsapi); + return FALSE; + } + + hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof(HMODULE))); + tt = (char*) malloc(sizeof(char) * TTBUFLEN); + tt2 = (char*) malloc(sizeof(char) * TTBUFLEN); + if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) ) + goto cleanup; + + if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) ) + { + //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle ); + goto cleanup; + } + + if ( cbNeeded > TTBUFLEN ) + { + //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) ); + goto cleanup; + } + + for ( i = 0; i < cbNeeded / sizeof(hMods[0]); i++ ) + { + // base address, size + pGMI(hProcess, hMods[i], &mi, sizeof(mi)); + // image file name + tt[0] = 0; + pGMFNE(hProcess, hMods[i], tt, TTBUFLEN ); + // module name + tt2[0] = 0; + pGMBN(hProcess, hMods[i], tt2, TTBUFLEN ); + + DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage); + if (dwRes != ERROR_SUCCESS) + this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0); + cnt++; + } + + cleanup: + if (hPsapi != NULL) FreeLibrary(hPsapi); + if (tt2 != NULL) free(tt2); + if (tt != NULL) free(tt); + if (hMods != NULL) free(hMods); + + return cnt != 0; + } // GetModuleListPSAPI + + DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) + { + CHAR *szImg = _strdup(img); + CHAR *szMod = _strdup(mod); + DWORD result = ERROR_SUCCESS; + if ( (szImg == NULL) || (szMod == NULL) ) + result = ERROR_NOT_ENOUGH_MEMORY; + else + { + if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0) + result = GetLastError(); + } + ULONGLONG fileVersion = 0; + if ( (m_parent != NULL) && (szImg != NULL) ) + { + // try to retrive the file-version: + if ( (this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0) + { + VS_FIXEDFILEINFO *fInfo = NULL; + DWORD dwHandle; + DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle); + if (dwSize > 0) + { + LPVOID vData = malloc(dwSize); + if (vData != NULL) + { + if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) + { + UINT len; + TCHAR szSubBlock[] = _T("\\"); + if (VerQueryValue(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0) + fInfo = NULL; + else + { + fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32); + } + } + free(vData); + } + } + } + + // Retrive some additional-infos about the module + IMAGEHLP_MODULE64_V3 Module; + const char *szSymType = "-unknown-"; + if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) + { + switch(Module.SymType) + { + case SymNone: + szSymType = "-nosymbols-"; + break; + case SymCoff: // 1 + szSymType = "COFF"; + break; + case SymCv: // 2 + szSymType = "CV"; + break; + case SymPdb: // 3 + szSymType = "PDB"; + break; + case SymExport: // 4 + szSymType = "-exported-"; + break; + case SymDeferred: // 5 + szSymType = "-deferred-"; + break; + case SymSym: // 6 + szSymType = "SYM"; + break; + case 7: // SymDia: + szSymType = "DIA"; + break; + case 8: //SymVirtual: + szSymType = "Virtual"; + break; + default: + break; + } + } + LPCSTR pdbName = Module.LoadedImageName; + if (Module.LoadedPdbName[0] != 0) + pdbName = Module.LoadedPdbName; + this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, pdbName, fileVersion); + } + if (szImg != NULL) free(szImg); + if (szMod != NULL) free(szMod); + return result; + } +public: + BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId) + { + // first try toolhelp32 + if (GetModuleListTH32(hProcess, dwProcessId)) + return true; + // then try psapi + return GetModuleListPSAPI(hProcess); + } + + + BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3 *pModuleInfo) + { + memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3)); + if(this->pSGMI == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + // First try to use the larger ModuleInfo-Structure + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3); + void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites... + if (pData == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V3)); + static bool s_useV3Version = true; + if (s_useV3Version) + { + if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*) pData) != FALSE) + { + // only copy as much memory as is reserved... + memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V3)); + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3); + free(pData); + return TRUE; + } + s_useV3Version = false; // to prevent unneccessarry calls with the larger struct... + } + + // could not retrive the bigger structure, try with the smaller one (as defined in VC7.1)... + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); + memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2)); + if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*) pData) != FALSE) + { + // only copy as much memory as is reserved... + memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2)); + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); + free(pData); + return TRUE; + } + free(pData); + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } +}; + +// ############################################################# +StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess) +{ + this->m_options = OptionsAll; + this->m_modulesLoaded = FALSE; + this->m_hProcess = hProcess; + this->m_sw = new StackWalkerInternal(this, this->m_hProcess); + this->m_dwProcessId = dwProcessId; + this->m_szSymPath = NULL; + this->m_MaxRecursionCount = 1000; +} +StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess) +{ + this->m_options = options; + this->m_modulesLoaded = FALSE; + this->m_hProcess = hProcess; + this->m_sw = new StackWalkerInternal(this, this->m_hProcess); + this->m_dwProcessId = dwProcessId; + if (szSymPath != NULL) + { + this->m_szSymPath = _strdup(szSymPath); + this->m_options |= SymBuildPath; + } + else + this->m_szSymPath = NULL; + this->m_MaxRecursionCount = 1000; +} + +StackWalker::~StackWalker() +{ + if (m_szSymPath != NULL) + free(m_szSymPath); + m_szSymPath = NULL; + if (this->m_sw != NULL) + delete this->m_sw; + this->m_sw = NULL; +} + +BOOL StackWalker::LoadModules() +{ + if (this->m_sw == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + if (m_modulesLoaded != FALSE) + return TRUE; + + // Build the sym-path: + char *szSymPath = NULL; + if ( (this->m_options & SymBuildPath) != 0) + { + const size_t nSymPathLen = 4096; + szSymPath = (char*) malloc(nSymPathLen); + if (szSymPath == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + szSymPath[0] = 0; + // Now first add the (optional) provided sympath: + if (this->m_szSymPath != NULL) + { + strcat_s(szSymPath, nSymPathLen, this->m_szSymPath); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + strcat_s(szSymPath, nSymPathLen, ".;"); + + const size_t nTempLen = 1024; + char szTemp[nTempLen]; + // Now add the current directory: + if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + // Now add the path for the main-module: + if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p) + { + // locate the rightmost path separator + if ( (*p == '\\') || (*p == '/') || (*p == ':') ) + { + *p = 0; + break; + } + } // for (search for path separator...) + if (strlen(szTemp) > 0) + { + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + } + if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + // also add the "system32"-directory: + strcat_s(szTemp, nTempLen, "\\system32"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + if ( (this->m_options & SymUseSymSrv) != 0) + { + if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, "SRV*"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, "\\websymbols"); + strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;"); + } + else + strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;"); + } + } // if SymBuildPath + + // First Init the whole stuff... + BOOL bRet = this->m_sw->Init(szSymPath); + if (szSymPath != NULL) + free(szSymPath); + + szSymPath = NULL; + if (bRet == FALSE) + { + this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0); + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId); + if (bRet != FALSE) + m_modulesLoaded = TRUE; + return bRet; +} + + +// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction +// This has to be done due to a problem with the "hProcess"-parameter in x64... +// Because this class is in no case multi-threading-enabled (because of the limitations +// of dbghelp.dll) it is "safe" to use a static-variable +static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL; +static LPVOID s_readMemoryFunction_UserData = NULL; + +BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData) +{ + CONTEXT c; + CallstackEntry csEntry; + IMAGEHLP_SYMBOL64 *pSym = NULL; + StackWalkerInternal::IMAGEHLP_MODULE64_V3 Module; + IMAGEHLP_LINE64 Line; + int frameNum; + bool bLastEntryCalled = true; + int curRecursionCount = 0; + + if (m_modulesLoaded == FALSE) + this->LoadModules(); // ignore the result... + + if (this->m_sw->m_hDbhHelp == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + s_readMemoryFunction = readMemoryFunction; + s_readMemoryFunction_UserData = pUserData; + + if (context == NULL) + { + // If no context is provided, capture the context + // See: https://stackwalker.codeplex.com/discussions/446958 +#if _WIN32_WINNT <= 0x0501 + // If we need to support XP, we need to use the "old way", because "GetThreadId" is not available! + if (hThread == GetCurrentThread()) +#else + if (GetThreadId(hThread) == GetCurrentThreadId()) +#endif + { + GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, USED_CONTEXT_FLAGS); + } + else + { + SuspendThread(hThread); + memset(&c, 0, sizeof(CONTEXT)); + c.ContextFlags = USED_CONTEXT_FLAGS; + + // TODO: Detect if you want to get a thread context of a different process, which is running a different processor architecture... + // This does only work if we are x64 and the target process is x64 or x86; + // It cannnot work, if this process is x64 and the target process is x64... this is not supported... + // See also: http://www.howzatt.demon.co.uk/articles/DebuggingInWin64.html + if (GetThreadContext(hThread, &c) == FALSE) + { + ResumeThread(hThread); + return FALSE; + } + } + } + else + c = *context; + + // init STACKFRAME for first call + STACKFRAME64 s; // in/out stackframe + memset(&s, 0, sizeof(s)); + DWORD imageType; +#ifdef _M_IX86 + // normally, call ImageNtHeader() and use machine info from PE header + imageType = IMAGE_FILE_MACHINE_I386; + s.AddrPC.Offset = c.Eip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Ebp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Esp; + s.AddrStack.Mode = AddrModeFlat; +#elif _M_X64 + imageType = IMAGE_FILE_MACHINE_AMD64; + s.AddrPC.Offset = c.Rip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Rsp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Rsp; + s.AddrStack.Mode = AddrModeFlat; +#elif _M_IA64 + imageType = IMAGE_FILE_MACHINE_IA64; + s.AddrPC.Offset = c.StIIP; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.IntSp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrBStore.Offset = c.RsBSP; + s.AddrBStore.Mode = AddrModeFlat; + s.AddrStack.Offset = c.IntSp; + s.AddrStack.Mode = AddrModeFlat; +#else +#error "Platform not supported!" +#endif + + pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + if (!pSym) goto cleanup; // not enough memory... + memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; + + memset(&Line, 0, sizeof(Line)); + Line.SizeOfStruct = sizeof(Line); + + memset(&Module, 0, sizeof(Module)); + Module.SizeOfStruct = sizeof(Module); + + for (frameNum = 0; ; ++frameNum ) + { + // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64()) + // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can + // assume that either you are done, or that the stack is so hosed that the next + // deeper frame could not be found. + // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386! + if ( ! this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL) ) + { + // INFO: "StackWalk64" does not set "GetLastError"... + this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset); + break; + } + + csEntry.offset = s.AddrPC.Offset; + csEntry.name[0] = 0; + csEntry.undName[0] = 0; + csEntry.undFullName[0] = 0; + csEntry.offsetFromSmybol = 0; + csEntry.offsetFromLine = 0; + csEntry.lineFileName[0] = 0; + csEntry.lineNumber = 0; + csEntry.loadedImageName[0] = 0; + csEntry.moduleName[0] = 0; + if (s.AddrPC.Offset == s.AddrReturn.Offset) + { + if ( (this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount) ) + { + this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset); + break; + } + curRecursionCount++; + } + else + curRecursionCount = 0; + if (s.AddrPC.Offset != 0) + { + // we seem to have a valid PC + // show procedure info (SymGetSymFromAddr64()) + if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE) + { + MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name); + // UnDecorateSymbolName() + this->m_sw->pUDSN( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY ); + this->m_sw->pUDSN( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE ); + } + else + { + this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset); + } + + // show line number info, NT5.0-method (SymGetLineFromAddr64()) + if (this->m_sw->pSGLFA != NULL ) + { // yes, we have SymGetLineFromAddr64() + if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE) + { + csEntry.lineNumber = Line.LineNumber; + MyStrCpy(csEntry.lineFileName, STACKWALK_MAX_NAMELEN, Line.FileName); + } + else + { + this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset); + } + } // yes, we have SymGetLineFromAddr64() + + // show module info (SymGetModuleInfo64()) + if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module ) != FALSE) + { // got module info OK + switch ( Module.SymType ) + { + case SymNone: + csEntry.symTypeString = "-nosymbols-"; + break; + case SymCoff: + csEntry.symTypeString = "COFF"; + break; + case SymCv: + csEntry.symTypeString = "CV"; + break; + case SymPdb: + csEntry.symTypeString = "PDB"; + break; + case SymExport: + csEntry.symTypeString = "-exported-"; + break; + case SymDeferred: + csEntry.symTypeString = "-deferred-"; + break; + case SymSym: + csEntry.symTypeString = "SYM"; + break; +#if API_VERSION_NUMBER >= 9 + case SymDia: + csEntry.symTypeString = "DIA"; + break; +#endif + case 8: //SymVirtual: + csEntry.symTypeString = "Virtual"; + break; + default: + //_snprintf( ty, sizeof(ty), "symtype=%ld", (long) Module.SymType ); + csEntry.symTypeString = NULL; + break; + } + + MyStrCpy(csEntry.moduleName, STACKWALK_MAX_NAMELEN, Module.ModuleName); + csEntry.baseOfImage = Module.BaseOfImage; + MyStrCpy(csEntry.loadedImageName, STACKWALK_MAX_NAMELEN, Module.LoadedImageName); + } // got module info OK + else + { + this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset); + } + } // we seem to have a valid PC + + CallstackEntryType et = nextEntry; + if (frameNum == 0) + et = firstEntry; + bLastEntryCalled = false; + this->OnCallstackEntry(et, csEntry); + + if (s.AddrReturn.Offset == 0) + { + bLastEntryCalled = true; + this->OnCallstackEntry(lastEntry, csEntry); + SetLastError(ERROR_SUCCESS); + break; + } + } // for ( frameNum ) + + cleanup: + if (pSym) free( pSym ); + + if (bLastEntryCalled == false) + this->OnCallstackEntry(lastEntry, csEntry); + + if (context == NULL) + ResumeThread(hThread); + + return TRUE; +} + +BOOL __stdcall StackWalker::myReadProcMem( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead + ) +{ + if (s_readMemoryFunction == NULL) + { + SIZE_T st; + BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st); + *lpNumberOfBytesRead = (DWORD) st; + //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet); + return bRet; + } + else + { + return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData); + } +} + +void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + if (fileVersion == 0) + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName); + else + { + DWORD v4 = (DWORD) (fileVersion & 0xFFFF); + DWORD v3 = (DWORD) ((fileVersion>>16) & 0xFFFF); + DWORD v2 = (DWORD) ((fileVersion>>32) & 0xFFFF); + DWORD v1 = (DWORD) ((fileVersion>>48) & 0xFFFF); + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4); + } + OnOutput(buffer); +} + +void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + if ( (eType != lastEntry) && (entry.offset != 0) ) + { + if (entry.name[0] == 0) + MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)"); + if (entry.undName[0] != 0) + MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName); + if (entry.undFullName[0] != 0) + MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName); + if (entry.lineFileName[0] == 0) + { + MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)"); + if (entry.moduleName[0] == 0) + MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)"); + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name); + } + else + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name); + buffer[STACKWALK_MAX_NAMELEN-1] = 0; + OnOutput(buffer); + } +} + +void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr); + OnOutput(buffer); +} + +void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName); + OnOutput(buffer); + // Also display the OS-version +#if _MSC_VER <= 1200 + OSVERSIONINFOA ver; + ZeroMemory(&ver, sizeof(OSVERSIONINFOA)); + ver.dwOSVersionInfoSize = sizeof(ver); + if (GetVersionExA(&ver) != FALSE) + { + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s)\n", + ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, + ver.szCSDVersion); + OnOutput(buffer); + } +#else + OSVERSIONINFOEXA ver; + ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA)); + ver.dwOSVersionInfoSize = sizeof(ver); +#if _MSC_VER >= 1900 +#pragma warning(push) +#pragma warning(disable: 4996) +#endif + if (GetVersionExA( (OSVERSIONINFOA*) &ver) != FALSE) + { + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", + ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, + ver.szCSDVersion, ver.wSuiteMask, ver.wProductType); + OnOutput(buffer); + } +#if _MSC_VER >= 1900 +#pragma warning(pop) +#endif +#endif +} + +void StackWalker::OnOutput(LPCSTR buffer) +{ + OutputDebugStringA(buffer); +} + +#endif diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/windows/StackWalker.h b/SQLiteStudio3/coreSQLiteStudio/chillout/windows/StackWalker.h new file mode 100644 index 0000000..cd1a982 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/windows/StackWalker.h @@ -0,0 +1,222 @@ +/********************************************************************** + * + * StackWalker.h + * + * + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2009, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * **********************************************************************/ +// #pragma once is supported starting with _MCS_VER 1000, +// so we need not to check the version (because we only support _MSC_VER >= 1100)! +#pragma once + +#ifdef _WIN32 + +#include + +#if _MSC_VER >= 1900 +#pragma warning(disable : 4091) +#endif + +// special defines for VC5/6 (if no actual PSDK is installed): +#if _MSC_VER < 1300 +typedef unsigned __int64 DWORD64, *PDWORD64; +#if defined(_WIN64) +typedef unsigned __int64 SIZE_T, *PSIZE_T; +#else +typedef unsigned long SIZE_T, *PSIZE_T; +#endif +#endif // _MSC_VER < 1300 + +class StackWalkerInternal; // forward +class StackWalker +{ +public: + typedef enum StackWalkOptions + { + // No addition info will be retrived + // (only the address is available) + RetrieveNone = 0, + + // Try to get the symbol-name + RetrieveSymbol = 1, + + // Try to get the line for this symbol + RetrieveLine = 2, + + // Try to retrieve the module-infos + RetrieveModuleInfo = 4, + + // Also retrieve the version for the DLL/EXE + RetrieveFileVersion = 8, + + // Contains all the abouve + RetrieveVerbose = 0xF, + + // Generate a "good" symbol-search-path + SymBuildPath = 0x10, + + // Also use the public Microsoft-Symbol-Server + SymUseSymSrv = 0x20, + + // Contains all the abouve "Sym"-options + SymAll = 0x30, + + // Contains all options (default) + OptionsAll = 0x3F + } StackWalkOptions; + + StackWalker( + int options = OptionsAll, // 'int' is by design, to combine the enum-flags + LPCSTR szSymPath = NULL, + DWORD dwProcessId = GetCurrentProcessId(), + HANDLE hProcess = GetCurrentProcess() + ); + StackWalker(DWORD dwProcessId, HANDLE hProcess); + virtual ~StackWalker(); + + typedef BOOL (__stdcall *PReadProcessMemoryRoutine)( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead, + LPVOID pUserData // optional data, which was passed in "ShowCallstack" + ); + + BOOL LoadModules(); + + BOOL ShowCallstack( + HANDLE hThread = GetCurrentThread(), + const CONTEXT *context = NULL, + PReadProcessMemoryRoutine readMemoryFunction = NULL, + LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback + ); + +#if _MSC_VER >= 1300 +// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" +// in older compilers in order to use it... starting with VC7 we can declare it as "protected" +protected: +#endif + enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols + +protected: + // Entry for each Callstack-Entry + typedef struct CallstackEntry + { + DWORD64 offset; // if 0, we have no valid entry + CHAR name[STACKWALK_MAX_NAMELEN]; + CHAR undName[STACKWALK_MAX_NAMELEN]; + CHAR undFullName[STACKWALK_MAX_NAMELEN]; + DWORD64 offsetFromSmybol; + DWORD offsetFromLine; + DWORD lineNumber; + CHAR lineFileName[STACKWALK_MAX_NAMELEN]; + DWORD symType; + LPCSTR symTypeString; + CHAR moduleName[STACKWALK_MAX_NAMELEN]; + DWORD64 baseOfImage; + CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; + } CallstackEntry; + + enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; + + virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); + virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); + virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); + virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); + virtual void OnOutput(LPCSTR szText); + + StackWalkerInternal *m_sw; + HANDLE m_hProcess; + DWORD m_dwProcessId; + BOOL m_modulesLoaded; + LPSTR m_szSymPath; + + int m_options; + int m_MaxRecursionCount; + + static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); + + friend StackWalkerInternal; +}; // class StackWalker + + +// The "ugly" assembler-implementation is needed for systems before XP +// If you have a new PSDK and you only compile for XP and later, then you can use +// the "RtlCaptureContext" +// Currently there is no define which determines the PSDK-Version... +// So we just use the compiler-version (and assumes that the PSDK is +// the one which was installed by the VS-IDE) + +// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later... +// But I currently use it in x64/IA64 environments... +#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400) + +//#if defined(_M_IX86) +#ifdef CURRENT_THREAD_VIA_EXCEPTION +// TODO: The following is not a "good" implementation, +// because the callstack is only valid in the "__except" block... +#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + EXCEPTION_POINTERS *pExp = NULL; \ + __try { \ + throw 0; \ + } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \ + if (pExp != NULL) \ + memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + } while(0); +#else +// The following should be enough for walking the callstack... +#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + __asm call x \ + __asm x: pop eax \ + __asm mov c.Eip, eax \ + __asm mov c.Ebp, ebp \ + __asm mov c.Esp, esp \ + } while(0); +#endif + +#else + +// The following is defined for x86 (XP and higher), x64 and IA64: +#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + RtlCaptureContext(&c); \ +} while(0); +#endif + +#endif diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/windows/windowscrashhandler.cpp b/SQLiteStudio3/coreSQLiteStudio/chillout/windows/windowscrashhandler.cpp new file mode 100644 index 0000000..aa9767c --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/windows/windowscrashhandler.cpp @@ -0,0 +1,671 @@ +#ifdef _WIN32 + +#include "windowscrashhandler.h" +#include "../defines.h" + +#include +#include +#include "StackWalker.h" + +// minidump +#include +#include +//#include +#include +//#include +#include +#include + +#include +#include +#include +#include +#include "../common/common.h" + +#ifdef _MSC_VER +#if _MSC_VER < 1400 +#define strcpy_s(dst, len, src) strcpy(dst, src) +#define strncpy_s(dst, len, src, maxLen) strncpy(dst, len, src) +#define strcat_s(dst, len, src) strcat(dst, src) +#define _snprintf_s _snprintf +#define _tcscat_s _tcscat +#endif +#endif + +#ifdef __MINGW32__ +#define strcpy_s(dst, len, src) strcpy(dst, src) +#define strncpy_s(dst, len, src, maxLen) strncpy(dst, src, len) +#define strcat_s(dst, len, src) strcat(dst, src) +#define _snprintf_s _snprintf +#endif + +#define CR_SEH_EXCEPTION 0 //!< SEH exception. +#define CR_CPP_TERMINATE_CALL 1 //!< C++ terminate() call. +#define CR_CPP_UNEXPECTED_CALL 2 //!< C++ unexpected() call. +#define CR_CPP_PURE_CALL 3 //!< C++ pure virtual function call (VS .NET and later). +#define CR_CPP_NEW_OPERATOR_ERROR 4 //!< C++ new operator fault (VS .NET and later). +#define CR_CPP_SECURITY_ERROR 5 //!< Buffer overrun error (VS .NET only). +#define CR_CPP_INVALID_PARAMETER 6 //!< Invalid parameter exception (VS 2005 and later). +#define CR_CPP_SIGABRT 7 //!< C++ SIGABRT signal (abort). +#define CR_CPP_SIGFPE 8 //!< C++ SIGFPE signal (flotating point exception). +#define CR_CPP_SIGILL 9 //!< C++ SIGILL signal (illegal instruction). +#define CR_CPP_SIGINT 10 //!< C++ SIGINT signal (CTRL+C). +#define CR_CPP_SIGSEGV 11 //!< C++ SIGSEGV signal (invalid storage access). +#define CR_CPP_SIGTERM 12 //!< C++ SIGTERM signal (termination request). + +#define STRINGIZE_(x) #x +#define STRINGIZE(x) STRINGIZE_(x) + +#ifndef _AddressOfReturnAddress + // Taken from: http://msdn.microsoft.com/en-us/library/s975zw7k(VS.71).aspx + #ifdef __cplusplus + #define EXTERNC extern "C" + #else + #define EXTERNC + #endif + + // _ReturnAddress and _AddressOfReturnAddress should be prototyped before use + EXTERNC void * _AddressOfReturnAddress(void); + EXTERNC void * _ReturnAddress(void); +#endif + +#pragma GCC diagnostic ignored "-Wcast-function-type" + +namespace Debug { + class StackWalkerWithCallback : public StackWalker + { + public: + StackWalkerWithCallback(const std::function &callback): + StackWalker(RetrieveVerbose | SymBuildPath), + m_callback(callback) + { } + + protected: + virtual void OnOutput(LPCSTR szText) override { + m_callback(szText); + } + + private: + std::function m_callback; + }; + + BOOL PreventSetUnhandledExceptionFilter() + { + HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll")); + if (hKernel32 == NULL) return FALSE; + void* pOrgEntry = reinterpret_cast(GetProcAddress(hKernel32, "SetUnhandledExceptionFilter")); + if (pOrgEntry == NULL) return FALSE; + +#ifdef _M_IX86 + // Code for x86: + // 33 C0 xor eax,eax + // C2 04 00 ret 4 + unsigned char szExecute[] = { 0x33, 0xC0, 0xC2, 0x04, 0x00 }; +#elif _M_X64 + // 33 C0 xor eax,eax + // C3 ret + unsigned char szExecute[] = { 0x33, 0xC0, 0xC3 }; +#else +#error "The following code only works for x86 and x64!" +#endif + + SIZE_T bytesWritten = 0; + BOOL bRet = WriteProcessMemory(GetCurrentProcess(), + pOrgEntry, szExecute, sizeof(szExecute), &bytesWritten); + return bRet; + } + + BOOL CALLBACK MyMiniDumpCallback( + PVOID pParam, + const PMINIDUMP_CALLBACK_INPUT pInput, + PMINIDUMP_CALLBACK_OUTPUT pOutput + ) + { + BOOL bRet = FALSE; + + // Check parameters + + if( pInput == 0 ) + return FALSE; + + if( pOutput == 0 ) + return FALSE; + + // Process the callbacks + WindowsCrashHandler *handler = (WindowsCrashHandler*)pParam; + + switch( pInput->CallbackType ) + { + case IncludeModuleCallback: + { + // Include the module into the dump + bRet = TRUE; + } + break; + + case IncludeThreadCallback: + { + // Include the thread into the dump + bRet = TRUE; + } + break; + + case ModuleCallback: + { + // Are data sections available for this module ? + if( pOutput->ModuleWriteFlags & ModuleWriteDataSeg ) + { + // Yes, they are, but do we need them? + + if( !handler->isDataSectionNeeded( pInput->Module.FullPath ) ) + { + pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg); + } + } + + if( !(pOutput->ModuleWriteFlags & ModuleReferencedByMemory) ) + { + // No, it does not - exclude it + pOutput->ModuleWriteFlags &= (~ModuleWriteModule); + } + + bRet = TRUE; + } + break; + + case ThreadCallback: + { + // Include all thread information into the minidump + bRet = TRUE; + } + break; + + case ThreadExCallback: + { + // Include this information + bRet = TRUE; + } + break; + + case MemoryCallback: + { + // We do not include any information here -> return FALSE + bRet = FALSE; + } + break; + + case CancelCallback: + break; + } + + return bRet; + + } + + void DoHandleCrash() { + WindowsCrashHandler &handler = WindowsCrashHandler::getInstance(); + handler.handleCrash(); + } + + // http://groups.google.com/group/crashrpt/browse_thread/thread/a1dbcc56acb58b27/fbd0151dd8e26daf?lnk=gst&q=stack+overflow#fbd0151dd8e26daf + // Thread procedure doing the dump for stack overflow. + DWORD WINAPI StackOverflowThreadFunction(LPVOID) { + DoHandleCrash(); + TerminateProcess(GetCurrentProcess(), CHILLOUT_EXIT_CODE); + return 0; + } + + static LONG WINAPI SehHandler(EXCEPTION_POINTERS*) { +#ifdef _DEBUG + fprintf(stderr, "Chillout SehHandler"); +#endif + + DoHandleCrash(); + + TerminateProcess(GetCurrentProcess(), CHILLOUT_EXIT_CODE); + + // unreachable + return EXCEPTION_EXECUTE_HANDLER; + } + + // The following code is intended to fix the issue with 32-bit applications in 64-bit environment. + // http://support.microsoft.com/kb/976038/en-us + // http://code.google.com/p/crashrpt/issues/detail?id=104 + void EnableCrashingOnCrashes() { + typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags); + typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags); + static const DWORD EXCEPTION_SWALLOWING = 0x1; + + const HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + const tGetPolicy pGetPolicy = reinterpret_cast(GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy")); + const tSetPolicy pSetPolicy = reinterpret_cast(GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy")); + if(pGetPolicy && pSetPolicy) + { + DWORD dwFlags; + if(pGetPolicy(&dwFlags)) + { + // Turn off the filter + pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING); + } + } + } + + WindowsCrashHandler::WindowsCrashHandler() { + m_oldSehHandler = NULL; + +#if _MSC_VER>=1300 + m_prevPurec = NULL; + m_prevNewHandler = NULL; +#endif + +#if _MSC_VER>=1300 && _MSC_VER<1400 + m_prevSec = NULL; +#endif + +#if _MSC_VER>=1400 + m_prevInvpar = NULL; +#endif + + m_prevSigABRT = NULL; + m_prevSigINT = NULL; + m_prevSigTERM = NULL; + } + + void WindowsCrashHandler::setup(const std::wstring &appName, const std::wstring &dumpsDir) { + m_appName = appName; + + if (!appName.empty() && !dumpsDir.empty()) { + std::wstring path = dumpsDir; + while ((path.size() > 1) && + (path[path.size() - 1] == L'\\')) { + path.erase(path.size() - 1); + } + } + + EnableCrashingOnCrashes(); + setProcessExceptionHandlers(); + setThreadExceptionHandlers(); + } + + void WindowsCrashHandler::teardown() { + unsetProcessExceptionHandlers(); + unsetThreadExceptionHandlers(); + } + + void WindowsCrashHandler::setCrashCallback(const std::function &callback) { + m_crashCallback = callback; + } + + void WindowsCrashHandler::setBacktraceCallback(const std::function &callback) { + m_backtraceCallback = callback; + } + + void WindowsCrashHandler::handleCrash() { + std::lock_guard guard(m_crashMutex); + (void)guard; + + if (m_crashCallback) { + m_crashCallback(); + } + + // Terminate process + TerminateProcess(GetCurrentProcess(), CHILLOUT_EXIT_CODE); + } + + bool WindowsCrashHandler::isDataSectionNeeded(const WCHAR* pModuleName) { + if( pModuleName == 0 ) { + _ASSERTE( _T("Parameter is null.") ); + return false; + } + + // Extract the module name + + WCHAR szFileName[_MAX_FNAME] = L""; + _wsplitpath( pModuleName, NULL, NULL, szFileName, NULL ); + + // Compare the name with the list of known names and decide + // if contains app name in its path + if( wcsstr( pModuleName, m_appName.c_str() ) != 0 ) { + return true; + } else if( _wcsicmp( szFileName, L"ntdll" ) == 0 ) { + return true; + } else if( wcsstr( szFileName, L"Qt5" ) != 0 ) { + return true; + } + + // Complete + return false; + } + + void WindowsCrashHandler::setProcessExceptionHandlers() { + //SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS); + m_oldSehHandler = SetUnhandledExceptionFilter(SehHandler); +#if defined _M_X64 || defined _M_IX86 + if (m_oldSehHandler) + PreventSetUnhandledExceptionFilter(); +#endif + +#if _MSC_VER>=1300 + // Catch pure virtual function calls. + // Because there is one _purecall_handler for the whole process, + // calling this function immediately impacts all threads. The last + // caller on any thread sets the handler. + // http://msdn.microsoft.com/en-us/library/t296ys27.aspx + m_prevPurec = _set_purecall_handler(PureCallHandler); + + // Catch new operator memory allocation exceptions + //_set_new_mode(1); // Force malloc() to call new handler too + m_prevNewHandler = _set_new_handler(NewHandler); +#endif + +#if _MSC_VER>=1400 + // Catch invalid parameter exceptions. + m_prevInvpar = _set_invalid_parameter_handler(InvalidParameterHandler); +#endif + +#if _MSC_VER>=1300 && _MSC_VER<1400 + // Catch buffer overrun exceptions + // The _set_security_error_handler is deprecated in VC8 C++ run time library + m_prevSec = _set_security_error_handler(SecurityHandler); +#endif + +#if _MSC_VER>=1400 + _set_abort_behavior(0, _WRITE_ABORT_MSG); + _set_abort_behavior(_CALL_REPORTFAULT, _CALL_REPORTFAULT); +#endif + +#if defined(_MSC_VER) + // Disable all of the possible ways Windows conspires to make automated + // testing impossible. + + // ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + // ::_set_error_mode(_OUT_TO_STDERR); + + // _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + // _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + // _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + // _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + // _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + + m_crtReportHook = _CrtSetReportHook(CrtReportHook); +#endif + + // Set up C++ signal handlers + SetConsoleCtrlHandler(ConsoleCtrlHandler, true); + + // Catch an abnormal program termination + m_prevSigABRT = signal(SIGABRT, SigabrtHandler); + + // Catch illegal instruction handler + m_prevSigINT = signal(SIGINT, SigintHandler); + + // Catch a termination request + m_prevSigINT = signal(SIGTERM, SigtermHandler); + } + + void WindowsCrashHandler::unsetProcessExceptionHandlers() { +#if _MSC_VER>=1300 + if(m_prevPurec!=NULL) { + _set_purecall_handler(m_prevPurec); + } + + if(m_prevNewHandler!=NULL) { + _set_new_handler(m_prevNewHandler); + } +#endif + +#if _MSC_VER>=1400 + if(m_prevInvpar!=NULL) { + _set_invalid_parameter_handler(m_prevInvpar); + } +#endif //_MSC_VER>=1400 + +#if _MSC_VER>=1300 && _MSC_VER<1400 + if(m_prevSec!=NULL) + _set_security_error_handler(m_prevSec); +#endif //_MSC_VER<1400 + + if(m_prevSigABRT!=NULL) { + signal(SIGABRT, m_prevSigABRT); + } + + if(m_prevSigINT!=NULL) { + signal(SIGINT, m_prevSigINT); + } + + if(m_prevSigTERM!=NULL) { + signal(SIGTERM, m_prevSigTERM); + } + + // Reset SEH exception filter + if (m_oldSehHandler) { + SetUnhandledExceptionFilter(m_oldSehHandler); + } + + m_oldSehHandler = NULL; + } + + int WindowsCrashHandler::setThreadExceptionHandlers() + { + DWORD dwThreadId = GetCurrentThreadId(); + + std::lock_guard guard(m_threadHandlersMutex); + + auto it = m_threadExceptionHandlers.find(dwThreadId); + if (it != m_threadExceptionHandlers.end()) { + // handlers are already set for the thread + return 1; // failed + } + + ThreadExceptionHandlers handlers; + + // Catch terminate() calls. + // In a multithreaded environment, terminate functions are maintained + // separately for each thread. Each new thread needs to install its own + // terminate function. Thus, each thread is in charge of its own termination handling. + // http://msdn.microsoft.com/en-us/library/t6fk7h29.aspx + handlers.m_prevTerm = std::set_terminate(TerminateHandler); + + // Catch unexpected() calls. + // In a multithreaded environment, unexpected functions are maintained + // separately for each thread. Each new thread needs to install its own + // unexpected function. Thus, each thread is in charge of its own unexpected handling. + // http://msdn.microsoft.com/en-us/library/h46t5b69.aspx + handlers.m_prevUnexp = std::set_unexpected(UnexpectedHandler); + + // Catch a floating point error + typedef void (*sigh)(int); + handlers.m_prevSigFPE = signal(SIGFPE, (sigh)SigfpeHandler); + + // Catch an illegal instruction + handlers.m_prevSigILL = signal(SIGILL, SigillHandler); + + // Catch illegal storage access errors + handlers.m_prevSigSEGV = signal(SIGSEGV, SigsegvHandler); + + m_threadExceptionHandlers[dwThreadId] = handlers; + + return 0; + } + + int WindowsCrashHandler::unsetThreadExceptionHandlers() { + DWORD dwThreadId = GetCurrentThreadId(); + std::lock_guard guard(m_threadHandlersMutex); + (void)guard; + + auto it = m_threadExceptionHandlers.find(dwThreadId); + if (it == m_threadExceptionHandlers.end()) { + return 1; + } + + ThreadExceptionHandlers* handlers = &(it->second); + + if(handlers->m_prevTerm!=NULL) { + std::set_terminate(handlers->m_prevTerm); + } + + if(handlers->m_prevUnexp!=NULL) { + std::set_unexpected(handlers->m_prevUnexp); + } + + if(handlers->m_prevSigFPE!=NULL) { + signal(SIGFPE, handlers->m_prevSigFPE); + } + + if(handlers->m_prevSigILL!=NULL) { + signal(SIGINT, handlers->m_prevSigILL); + } + + if(handlers->m_prevSigSEGV!=NULL) { + signal(SIGSEGV, handlers->m_prevSigSEGV); + } + + // Remove from the list + m_threadExceptionHandlers.erase(it); + + return 0; + } + + int __cdecl WindowsCrashHandler::CrtReportHook(int nReportType, char* szMsg, int* pnRet) { + (void)szMsg; + + switch (nReportType) { + case _CRT_WARN: + case _CRT_ERROR: + case _CRT_ASSERT: + // Put some debug code here + break; + } + + if (pnRet) { + *pnRet = 0; + } + + return TRUE; + } + + // CRT terminate() call handler + void __cdecl WindowsCrashHandler::TerminateHandler() { + // Abnormal program termination (terminate() function was called) + DoHandleCrash(); + } + + // CRT unexpected() call handler + void __cdecl WindowsCrashHandler::UnexpectedHandler() { + // Unexpected error (unexpected() function was called) + DoHandleCrash(); + } + + // CRT Pure virtual method call handler + void __cdecl WindowsCrashHandler::PureCallHandler() { + // Pure virtual function call + DoHandleCrash(); + } + + // CRT buffer overrun handler. Available in CRT 7.1 only +#if _MSC_VER>=1300 && _MSC_VER<1400 + void __cdecl WindowsCrashHandler::SecurityHandler(int code, void *x) + { + // Security error (buffer overrun). + + code; + x; + + EXCEPTION_POINTERS* pExceptionPtrs = (PEXCEPTION_POINTERS)_pxcptinfoptrs; + if (pExceptionPtrs == nullptr) { + GetExceptionPointers(CR_CPP_SECURITY_ERROR, &pExceptionPtrs); + } + + DoHandleCrash(pExceptionPtrs); + } +#endif + +#if _MSC_VER>=1400 + // CRT invalid parameter handler + void __cdecl WindowsCrashHandler::InvalidParameterHandler( + const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t pReserved) { + pReserved; + expression; + function; + file; + line; + + // fwprintf(stderr, L"Invalid parameter detected in function %s." + // L" File: %s Line: %d\n", function, file, line); + + // Retrieve exception information + EXCEPTION_POINTERS* pExceptionPtrs = NULL; + GetExceptionPointers(CR_CPP_INVALID_PARAMETER, &pExceptionPtrs); + + DoHandleCrash(pExceptionPtrs); + } +#endif + + // CRT new operator fault handler + int __cdecl WindowsCrashHandler::NewHandler(size_t) { + // 'new' operator memory allocation exception + DoHandleCrash(); + + // Unreacheable code + return 0; + } + + // CRT SIGABRT signal handler + void WindowsCrashHandler::SigabrtHandler(int) { + // Caught SIGABRT C++ signal + DoHandleCrash(); + } + + // CRT SIGFPE signal handler + void WindowsCrashHandler::SigfpeHandler(int /*code*/, int subcode) { + // Floating point exception (SIGFPE) + (void)subcode; + DoHandleCrash(); + } + + // CRT sigill signal handler + void WindowsCrashHandler::SigillHandler(int) { + // Illegal instruction (SIGILL) + DoHandleCrash(); + } + + // CRT sigint signal handler + void WindowsCrashHandler::SigintHandler(int) { + // Interruption (SIGINT) + DoHandleCrash(); + } + + // CRT SIGSEGV signal handler + void WindowsCrashHandler::SigsegvHandler(int) { + // Invalid storage access (SIGSEGV) + DoHandleCrash(); + } + + // CRT SIGTERM signal handler + void WindowsCrashHandler::SigtermHandler(int) { + // Termination request (SIGTERM) + DoHandleCrash(); + } + + // CRT SIGTERM signal handler + BOOL WINAPI WindowsCrashHandler::ConsoleCtrlHandler(DWORD type) { + // Termination request (CTRL_C_EVENT) + switch (type) + { + case CTRL_C_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + case CTRL_CLOSE_EVENT: + DoHandleCrash(); + break; + } + return false; + } +} + +#endif diff --git a/SQLiteStudio3/coreSQLiteStudio/chillout/windows/windowscrashhandler.h b/SQLiteStudio3/coreSQLiteStudio/chillout/windows/windowscrashhandler.h new file mode 100644 index 0000000..9d7e70f --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/chillout/windows/windowscrashhandler.h @@ -0,0 +1,147 @@ +#ifndef CRASHHANDLER_H +#define CRASHHANDLER_H + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../common/common.h" + +#ifdef __MINGW32__ + typedef int (__cdecl *_CRT_REPORT_HOOK)(int,char *,int *); +#endif + +namespace Debug { + struct ThreadExceptionHandlers + { + ThreadExceptionHandlers() + { + m_prevTerm = NULL; + m_prevUnexp = NULL; + m_prevSigFPE = NULL; + m_prevSigILL = NULL; + m_prevSigSEGV = NULL; + } + + std::terminate_handler m_prevTerm; // Previous terminate handler + std::unexpected_handler m_prevUnexp; // Previous unexpected handler + void (__cdecl *m_prevSigFPE)(int); // Previous FPE handler + void (__cdecl *m_prevSigILL)(int); // Previous SIGILL handler + void (__cdecl *m_prevSigSEGV)(int); // Previous illegal storage access handler + }; + + // code mostly from https://www.codeproject.com/articles/207464/WebControls/ + class WindowsCrashHandler + { + public: + static WindowsCrashHandler& getInstance() + { + static WindowsCrashHandler instance; // Guaranteed to be destroyed. + // Instantiated on first use. + return instance; + } + + private: + WindowsCrashHandler(); + + public: + void setup(const std::wstring &appName, const std::wstring &dumpsDir); + void teardown(); + void setCrashCallback(const std::function &callback); + void setBacktraceCallback(const std::function &callback); + void handleCrash(); + + public: + bool isDataSectionNeeded(const WCHAR* pModuleName); + + public: + // Sets exception handlers that work on per-process basis + void setProcessExceptionHandlers(); + void unsetProcessExceptionHandlers(); + + // Installs C++ exception handlers that function on per-thread basis + int setThreadExceptionHandlers(); + int unsetThreadExceptionHandlers(); + + /* Exception handler functions. */ + + static int __cdecl CrtReportHook(int nReportType, char* szMsg, int* pnRet); + + static void __cdecl TerminateHandler(); + static void __cdecl UnexpectedHandler(); + + static void __cdecl PureCallHandler(); +#if _MSC_VER>=1300 && _MSC_VER<1400 + // Buffer overrun handler (deprecated in newest versions of Visual C++). + // Since CRT 8.0, you can't intercept the buffer overrun errors in your code. When a buffer overrun is detected, CRT invokes Dr. Watson directly + static void __cdecl SecurityHandler(int code, void *x); +#endif + +#if _MSC_VER>=1400 + static void __cdecl InvalidParameterHandler(const wchar_t* expression, + const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved); +#endif + + static int __cdecl NewHandler(size_t); + + static void SigabrtHandler(int); + static void SigfpeHandler(int /*code*/, int subcode); + static void SigintHandler(int); + static void SigillHandler(int); + static void SigsegvHandler(int); + static void SigtermHandler(int); + static BOOL WINAPI ConsoleCtrlHandler(DWORD type); + + private: + std::function m_crashCallback; + std::function m_backtraceCallback; + std::mutex m_crashMutex; + std::wstring m_appName; + + std::map m_threadExceptionHandlers; + std::mutex m_threadHandlersMutex; + + // Previous SEH exception filter. + LPTOP_LEVEL_EXCEPTION_FILTER m_oldSehHandler; + +#if _MSC_VER>=1300 + _purecall_handler m_prevPurec; // Previous pure virtual call exception filter. + _PNH m_prevNewHandler; // Previous new operator exception filter. +#endif + +#if _MSC_VER>=1400 + _invalid_parameter_handler m_prevInvpar; // Previous invalid parameter exception filter. +#endif + +#if _MSC_VER>=1300 && _MSC_VER<1400 + _secerr_handler_func m_prevSec; // Previous security exception filter. +#endif + + void (__cdecl *m_prevSigABRT)(int); // Previous SIGABRT handler. + void (__cdecl *m_prevSigINT)(int); // Previous SIGINT handler. + void (__cdecl *m_prevSigTERM)(int); // Previous SIGTERM handler. + }; +} + +#endif + +#endif // CRASHHANDLER_H + diff --git a/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.cpp b/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.cpp index 5aac017..f2c1b2c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.cpp @@ -17,6 +17,15 @@ BiStrHash::BiStrHash(const BiStrHash& other) : hash(other.hash), inverted(other. { } +BiStrHash& BiStrHash::operator=(const BiStrHash& other) +{ + this->hash = other.hash; + this->inverted = other.inverted; + this->lowerHash = other.lowerHash; + this->lowerInverted = other.lowerInverted; + return *this; +} + void BiStrHash::insert(const QString& left, const QString& right) { if (lowerHash.contains(left.toLower())) @@ -163,6 +172,14 @@ QString BiStrHash::valueByLeft(const QString& left, Qt::CaseSensitivity cs) cons return hash.value(lowerHash.value(left.toLower())); } +QString BiStrHash::valueByLeft(const QString& left, const QString& defaultValue, Qt::CaseSensitivity cs) const +{ + if (containsLeft(left, cs)) + return valueByLeft(left, cs); + + return defaultValue; +} + QString BiStrHash::valueByRight(const QString& right, Qt::CaseSensitivity cs) const { if (cs == Qt::CaseSensitive) @@ -171,6 +188,14 @@ QString BiStrHash::valueByRight(const QString& right, Qt::CaseSensitivity cs) co return inverted.value(lowerInverted.value(right.toLower())); } +QString BiStrHash::valueByRight(const QString& right, const QString& defaultValue, Qt::CaseSensitivity cs) const +{ + if (containsRight(right, cs)) + return valueByRight(right, cs); + + return defaultValue; +} + QStringList BiStrHash::leftValues() const { return hash.keys(); diff --git a/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.h b/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.h index cec9b8a..0ad80ea 100644 --- a/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.h +++ b/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.h @@ -1,7 +1,6 @@ #ifndef BISTRHASH_H #define BISTRHASH_H -#include "bihash.h" #include "coreSQLiteStudio_global.h" #include #include @@ -44,6 +43,8 @@ class API_EXPORT BiStrHash */ BiStrHash(const BiStrHash& other); + BiStrHash& operator=(const BiStrHash& other); + /** * @brief Inserts entry into the hash. * @param left Left-side value to insert. @@ -122,6 +123,15 @@ class API_EXPORT BiStrHash */ QString valueByLeft(const QString& left, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + /** + * @brief Finds right-side value by matching the left-side value or returns defaultValue if not matched. + * @param left Left-side value to match. + * @param defaultValue Value to be returned if requested left key cannot be found. + * @param cs Case sensitivity flag. + * @return Right-side value, or provided default value if left-side value was not matched. + */ + QString valueByLeft(const QString& left, const QString& defaultValue, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + /** * @brief Finds left-side value by matching the right-side value. * @param right Right-side value to match. @@ -130,6 +140,15 @@ class API_EXPORT BiStrHash */ QString valueByRight(const QString& right, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + /** + * @brief Finds left-side value by matching the right-side value or returns defaultValue if not matched. + * @param right Right-side value to match. + * @param defaultValue Value to be returned if requested right key cannot be found. + * @param cs Case sensitivity flag. + * @return Left-side value, or provided default value if right-side value was not matched. + */ + QString valueByRight(const QString& right, const QString& defaultValue, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + /** * @brief Gives all left-side values. * @return List of values. diff --git a/SQLiteStudio3/coreSQLiteStudio/common/compatibility.cpp b/SQLiteStudio3/coreSQLiteStudio/common/compatibility.cpp index 9676105..c5918c4 100644 --- a/SQLiteStudio3/coreSQLiteStudio/common/compatibility.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/common/compatibility.cpp @@ -1,2 +1,9 @@ #include "compatibility.h" +void strSort(QStringList& collection, Qt::CaseSensitivity cs) +{ + std::stable_sort(collection.begin(), collection.end(), [cs](const QString& v1, const QString& v2) -> bool + { + return v1.compare(v2, cs) < 0; + }); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/common/compatibility.h b/SQLiteStudio3/coreSQLiteStudio/common/compatibility.h index 5ed5c8b..97637b0 100644 --- a/SQLiteStudio3/coreSQLiteStudio/common/compatibility.h +++ b/SQLiteStudio3/coreSQLiteStudio/common/compatibility.h @@ -1,6 +1,7 @@ #ifndef COMPATIBILITY_H #define COMPATIBILITY_H +#include "coreSQLiteStudio_global.h" #include #include #include @@ -37,4 +38,6 @@ void sSort(T& collection, C cmp) std::sort(collection.begin(), collection.end(), cmp); } +API_EXPORT void strSort(QStringList& collection, Qt::CaseSensitivity cs); + #endif // COMPATIBILITY_H diff --git a/SQLiteStudio3/coreSQLiteStudio/common/global.h b/SQLiteStudio3/coreSQLiteStudio/common/global.h index e09391a..c056abb 100644 --- a/SQLiteStudio3/coreSQLiteStudio/common/global.h +++ b/SQLiteStudio3/coreSQLiteStudio/common/global.h @@ -39,9 +39,7 @@ var = nullptr; \ } -#define parser_safe_delete(var) \ - if (var) \ - delete var +#define parser_safe_delete(var) delete var #define static_char static constexpr const char @@ -74,4 +72,19 @@ #define STRINGIFY(s) _STRINGIFY(s) #define _STRINGIFY(s) #s +#define __SQLS_INIT_RESOURCE(proj, name) Q_INIT_RESOURCE(proj ## _ ## name) +#define __SQLS_CLEANUP_RESOURCE(proj, name) Q_CLEANUP_RESOURCE(proj ## _ ## name) +#define _SQLS_INIT_RESOURCE(pname, name) __SQLS_INIT_RESOURCE(pname, name) +#define _SQLS_CLEANUP_RESOURCE(pname, name) __SQLS_CLEANUP_RESOURCE(pname, name) + +// These are replacements for Qt's macros to cover customized resource naming, +// which is used to avoid duplication of qmake_qmake_qm_files resource initialization function across different shared libraries. +#ifdef PROJECT_MODULE_NAME +# define SQLS_INIT_RESOURCE(name) _SQLS_INIT_RESOURCE(PROJECT_MODULE_NAME, name) +# define SQLS_CLEANUP_RESOURCE(name) _SQLS_CLEANUP_RESOURCE(PROJECT_MODULE_NAME, name) +#else +# define SQLS_INIT_RESOURCE(name) +# define SQLS_CLEANUP_RESOURCE(name) +#endif + #endif // GLOBAL_H diff --git a/SQLiteStudio3/coreSQLiteStudio/common/utils.cpp b/SQLiteStudio3/coreSQLiteStudio/common/utils.cpp index 0b95a85..a572330 100644 --- a/SQLiteStudio3/coreSQLiteStudio/common/utils.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/common/utils.cpp @@ -54,6 +54,7 @@ void initUtils() { qRegisterMetaType>("QList"); qRegisterMetaType("DbObjectType"); + qRegisterMetaType>>("QList>"); } bool isXDigit(const QChar& c) @@ -805,7 +806,7 @@ QString getOsString() DistributionType getDistributionType() { #if defined(Q_OS_OSX) - return DistributionType::OSX_BOUNDLE; + return DistributionType::OSX_BUNDLE; #elif defined(PORTABLE_CONFIG) return DistributionType::PORTABLE; #else @@ -966,12 +967,14 @@ QStringList concat(const QList& list) QString doubleToString(const QVariant& val) { QString str = val.toString(); - if (str.contains("e")) + if (str.contains("e") || str.midRef(str.indexOf('.') + 1).length() > 14) + { str = QString::number(val.toDouble(), 'f', 14).remove(QRegExp("0*$")); + if (str.endsWith(".")) + str += "0"; + } else if (!str.contains('.')) str += ".0"; - else if (str.mid(str.indexOf('.') + 1).length() > 14) - str = QString::number(val.toDouble(), 'f', 14).remove(QRegExp("0*$")); return str; } @@ -1097,3 +1100,31 @@ uint qHash(const QVariant& var) // could not generate a hash for the given variant return -2; } + +QString indentMultiline(const QString& str) +{ + QStringList lines = str.split("\n"); + for (QString& line : lines) + line = line.prepend(" "); + + return lines.join("\n"); +} + +QString toNativePath(const QString& path) +{ + return QDir::toNativeSeparators(path); +} + +QStringList sharedLibFileFilters() +{ + static QStringList filters({ +#ifdef Q_OS_WIN + "*.dll" +#elif defined Q_OS_MACOS + "*.dylib" +#elif defined Q_OS_LINUX || Q_OS_BSD + "*.so" +#endif + }); + return filters; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/common/utils.h b/SQLiteStudio3/coreSQLiteStudio/common/utils.h index f7317e2..0ceecc5 100644 --- a/SQLiteStudio3/coreSQLiteStudio/common/utils.h +++ b/SQLiteStudio3/coreSQLiteStudio/common/utils.h @@ -2,6 +2,7 @@ #define UTILS_H #include "coreSQLiteStudio_global.h" +#include #include #include #include @@ -66,6 +67,7 @@ API_EXPORT QStringList tokenizeArgs(const QString& str); API_EXPORT QStringList prefixEach(const QString& prefix, const QStringList& list); API_EXPORT QByteArray hashToBytes(const QHash& hash); API_EXPORT QHash bytesToHash(const QByteArray& bytes); +API_EXPORT QString indentMultiline(const QString& str); /** * @brief indexOf Extension to QStringList::indexOf(). * @@ -261,6 +263,7 @@ API_EXPORT QTextCodec* defaultCodec(); API_EXPORT QTextCodec* codecForName(const QString& name); API_EXPORT QStringList splitByLines(const QString& str); API_EXPORT QString joinLines(const QStringList& lines); +API_EXPORT QStringList sharedLibFileFilters(); API_EXPORT int sum(const QList& integers); API_EXPORT QString getOsString(); API_EXPORT bool validateEmail(const QString& email); @@ -289,7 +292,7 @@ API_EXPORT void sortWithReferenceList(QList& listToSort, const QList reverse(const QList& list) return result; } +template +QList map(const QList& list, std::function transformer) +{ + QList result; + for (const S& el : list) + result << transformer(el); + + return result; +} + +template +QHash toHash(const QList& list, std::function transformer) +{ + QHash result; + for (const S& el : list) + result[el] = transformer(el); + + return result; +} + +template +QSet map(const QSet& set, std::function transformer) +{ + QSet result; + for (const S& el : set) + result << transformer(el); + + return result; +} + template void removeDuplicates(QList& list) { @@ -328,6 +361,8 @@ API_EXPORT QVariant deserializeFromBytes(const QByteArray& bytes); API_EXPORT QString readFileContents(const QString& path, QString* err); +API_EXPORT QString toNativePath(const QString& path); + Q_DECLARE_METATYPE(QList) #endif // UTILS_H diff --git a/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp index c8d63e1..0122352 100644 --- a/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp @@ -4,14 +4,13 @@ #include "parser/token.h" #include "parser/lexer.h" #include "parser/keywords.h" -#include "log.h" #include #include #include #include #include -QString invalidIdCharacters = "[]()\"'@*.,+-=/%&|:; \t\n<>"; +QString invalidIdCharacters = "[](){}\"'@*.,+-=/#$%&|:; \t\n<>"; QHash> wrapperChars; QHash> wrapperEscapedEnding; QList sqlite3Wrappers; @@ -41,10 +40,15 @@ bool doesObjectNeedWrapping(const QString& str) if (str.isEmpty()) return true; - if (isObjWrapped(str)) - return false; + // It used to return false if object name looked to be wrapped already, + // but actually name [abc] is proper name that needs to be wrapped (i.e. "[abc]"). + // Bug reported for this was #4362 + //if (isObjWrapped(str)) + // return false; - if (isKeyword(str)) + // The "soft keyword" check added, as they don't require wrapping. + // For example: SELECT replace('abc', 'a', '1'); + if (isKeyword(str) && !isSoftKeyword(str)) return true; for (int i = 0; i < str.size(); i++) @@ -580,12 +584,80 @@ QString getQueryWithPosition(const QStringList& queries, int position, int* star return QString(); } +int getCursorFinalPositionForQueries(const QString& queries, int position) +{ + const static QSet whitespaceChars = {' ', '\t'}; + + int len = queries.length(); + if (position >= len || position < 1) + return position; + + if (!whitespaceChars.contains(queries[position]) && queries[position] != '\xa') + return position; + + // First checking characters at & after cursor - are they just whitespaces until end of line? + int newPos = position; + while (newPos < len) + { + QChar c = queries[newPos++]; + if (whitespaceChars.contains(c)) + continue; + else if (c == '\xa') + break; + else + return position; + } + + // Okay, only whitespaces after cursor, so let's check whats before cursor, + // until the ';' - is it just whitespaces too? + newPos = position; + while (newPos > 1 && whitespaceChars.contains(queries[newPos - 1])) + newPos--; + + if (queries[newPos - 1] == ';') + { + // Jackpot! Our cursor is just somewhere in whitespaces after the query. + // Let's consider that prior query as current. + return newPos - 1; + } + + return position; +} + QString getQueryWithPosition(const QString& queries, int position, int* startPos) { + position = getCursorFinalPositionForQueries(queries, position); QStringList queryList = splitQueries(queries); return getQueryWithPosition(queryList, position, startPos); } +QPair getQueryBoundriesForPosition(const QString& contents, int cursorPosition, bool fallBackToPreviousIfNecessary) +{ + int queryStartPos; + QString query = getQueryWithPosition(contents, cursorPosition, &queryStartPos); + TokenList tokens = Lexer::tokenize(query); + tokens.trim(); + tokens.trimRight(Token::OPERATOR, ";"); + + if (tokens.size() == 0 && fallBackToPreviousIfNecessary) + { + // Fallback + cursorPosition = contents.lastIndexOf(";", cursorPosition - 1); + if (cursorPosition > -1) + { + query = getQueryWithPosition(contents, cursorPosition, &queryStartPos); + tokens = Lexer::tokenize(query); + tokens.trim(); + tokens.trimRight(Token::OPERATOR, ";"); + } + } + + if (tokens.size() == 0) + return QPair(-1, -1); + + return QPair(tokens.first()->start + queryStartPos, tokens.last()->end + 1 + queryStartPos); +} + QString trimBindParamPrefix(const QString& param) { if (param == "?") @@ -822,3 +894,15 @@ SqliteDataType toSqliteDataType(const QString& typeStr) return SqliteDataType::UNKNOWN; } + +QByteArray blobFromLiteral(const QString& value) +{ + if (value.length() <= 3) + { + qCritical() << "Call to blobFromLiteral() with blob literal shorter or equal to 3 characters:" << value; + return QByteArray(); + } + + QString hex = value.mid(2, value.length() - 3); + return QByteArray::fromHex(hex.toLatin1()); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h index b9cfdcf..353faf3 100644 --- a/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h +++ b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h @@ -78,6 +78,7 @@ API_EXPORT QStringList splitQueries(const QString& sql, bool keepEmptyQueries = API_EXPORT QStringList quickSplitQueries(const QString& sql, bool keepEmptyQueries = true, bool removeComments = false); API_EXPORT QString getQueryWithPosition(const QStringList& queries, int position, int* startPos = nullptr); API_EXPORT QString getQueryWithPosition(const QString& queries, int position, int* startPos = nullptr); +API_EXPORT QPair getQueryBoundriesForPosition(const QString& contents, int cursorPosition, bool fallBackToPreviousIfNecessary); API_EXPORT QList getQueriesWithParamNames(const QString& query); API_EXPORT QList getQueriesWithParamCount(const QString& query); API_EXPORT QueryWithParamNames getQueryWithParamNames(const QString& query); @@ -88,6 +89,7 @@ API_EXPORT QString getBindTokenName(const TokenPtr& token); API_EXPORT QueryAccessMode getQueryAccessMode(const QString& query, bool* isSelect = nullptr); API_EXPORT QStringList valueListToSqlList(const QList& values); API_EXPORT QString trimQueryEnd(const QString& query); +API_EXPORT QByteArray blobFromLiteral(const QString& value); #endif // UTILS_SQL_H diff --git a/SQLiteStudio3/coreSQLiteStudio/completioncomparer.cpp b/SQLiteStudio3/coreSQLiteStudio/completioncomparer.cpp index 28f383d..cd6e2a5 100644 --- a/SQLiteStudio3/coreSQLiteStudio/completioncomparer.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/completioncomparer.cpp @@ -1,8 +1,6 @@ #include "completioncomparer.h" #include "completionhelper.h" #include "parser/ast/sqliteselect.h" -#include "db/db.h" -#include "parser/token.h" #include CompletionComparer::CompletionComparer(CompletionHelper *helper) @@ -82,7 +80,7 @@ bool CompletionComparer::initSelect() contextTables = helper->originalCurrentSelectCore->getContextTables(false); contextDatabases = helper->originalCurrentSelectCore->getContextDatabases(false); - for (SqliteSelect::Core* core : helper->parentSelectCores) + for (SqliteSelect::Core*& core : helper->parentSelectCores) { parentContextColumns += core->getContextColumns(false); parentContextTables += core->getContextTables(false); @@ -124,6 +122,11 @@ bool CompletionComparer::compareColumns(const ExpectedTokenPtr& token1, const Ex case CompletionHelper::Context::CREATE_TABLE: result = compareColumnsForCreateTable(token1, token2, &ok); break; + case CompletionHelper::Context::INSERT_RETURNING: + case CompletionHelper::Context::UPDATE_RETURNING: + case CompletionHelper::Context::DELETE_RETURNING: + result = compareColumnsForReturning(token1, token2, &ok); + break; default: return compareValues(token1, token2); } @@ -136,8 +139,8 @@ bool CompletionComparer::compareColumns(const ExpectedTokenPtr& token1, const Ex return result; // Context info of the column has a strong meaning when sorting (system tables are pushed to the end) - bool firstIsSystem = token1->contextInfo.toLower().startsWith("sqlite_"); - bool secondIsSystem = token2->contextInfo.toLower().startsWith("sqlite_"); + bool firstIsSystem = token1->contextInfo.startsWith("sqlite_", Qt::CaseInsensitive); + bool secondIsSystem = token2->contextInfo.startsWith("sqlite_", Qt::CaseInsensitive); if (firstIsSystem && !secondIsSystem) return false; @@ -152,8 +155,8 @@ bool CompletionComparer::compareColumnsForSelectResCol(const ExpectedTokenPtr &t *result = true; // Checking if columns are on list of columns available in FROM clause - bool token1available = isTokenOnAvailableList(token1); - bool token2available = isTokenOnAvailableList(token2); + bool token1available = isTokenOnAvailableColumnList(token1); + bool token2available = isTokenOnAvailableColumnList(token2); if (token1available && !token2available) return true; @@ -161,8 +164,8 @@ bool CompletionComparer::compareColumnsForSelectResCol(const ExpectedTokenPtr &t return false; // Checking if columns are on list of columns available in FROM clause of any parent SELECT core - bool token1parentAvailable = isTokenOnParentAvailableList(token1); - bool token2parentAvailable = isTokenOnParentAvailableList(token2); + bool token1parentAvailable = isTokenOnParentAvailableColumnList(token1); + bool token2parentAvailable = isTokenOnParentAvailableColumnList(token2); if (token1parentAvailable && !token2parentAvailable) return true; @@ -217,6 +220,23 @@ bool CompletionComparer::compareColumnsForCreateTable(const ExpectedTokenPtr& to return false; } +bool CompletionComparer::compareColumnsForReturning(const ExpectedTokenPtr& token1, const ExpectedTokenPtr& token2, bool* result) +{ + *result = true; + + // Checking if columns are on list of columns available in FROM clause + bool token1available = isTokenOnAvailableColumnList(token1); + bool token2available = isTokenOnAvailableColumnList(token2); + if (token1available && !token2available) + return true; + + if (!token1available && token2available) + return false; + + *result = false; + return false; +} + bool CompletionComparer::compareTables(const ExpectedTokenPtr& token1, const ExpectedTokenPtr& token2) { if (!helper->parsedQuery || helper->parsedQuery->queryType != SqliteQueryType::Select) @@ -380,12 +400,15 @@ bool CompletionComparer::compareByContextOnly(const QString &token1, const QStri return false; } -bool CompletionComparer::isTokenOnAvailableList(const ExpectedTokenPtr &token) +bool CompletionComparer::isTokenOnAvailableColumnList(const ExpectedTokenPtr &token) { - return isTokenOnColumnList(token, helper->selectAvailableColumns); + if (helper->parsedQuery->queryType == SqliteQueryType::Select) + return isTokenOnColumnList(token, helper->selectAvailableColumns); + else + return isTokenOnColumnList(token, helper->theFromAvailableColumns); } -bool CompletionComparer::isTokenOnParentAvailableList(const ExpectedTokenPtr &token) +bool CompletionComparer::isTokenOnParentAvailableColumnList(const ExpectedTokenPtr &token) { return isTokenOnColumnList(token, helper->parentSelectAvailableColumns); } diff --git a/SQLiteStudio3/coreSQLiteStudio/completioncomparer.h b/SQLiteStudio3/coreSQLiteStudio/completioncomparer.h index 4d5dd79..92401a3 100644 --- a/SQLiteStudio3/coreSQLiteStudio/completioncomparer.h +++ b/SQLiteStudio3/coreSQLiteStudio/completioncomparer.h @@ -46,6 +46,7 @@ class CompletionComparer bool compareColumnsForUpdateCol(const ExpectedTokenPtr& token1, const ExpectedTokenPtr& token2, bool* result); bool compareColumnsForDeleteCol(const ExpectedTokenPtr& token1, const ExpectedTokenPtr& token2, bool* result); bool compareColumnsForCreateTable(const ExpectedTokenPtr& token1, const ExpectedTokenPtr& token2, bool* result); + bool compareColumnsForReturning(const ExpectedTokenPtr& token1, const ExpectedTokenPtr& token2, bool* result); bool compareTables(const ExpectedTokenPtr& token1, const ExpectedTokenPtr& token2); bool compareIndexes(const ExpectedTokenPtr& token1, const ExpectedTokenPtr& token2); bool compareTriggers(const ExpectedTokenPtr& token1, const ExpectedTokenPtr& token2); @@ -63,8 +64,8 @@ class CompletionComparer const QList& contextValues, bool handleSystemNames, bool* ok = nullptr); bool compareByContextOnly(const QString &token1, const QString &token2, const QStringList& contextValues, bool handleSystemNames, bool* ok); - bool isTokenOnAvailableList(const ExpectedTokenPtr& token); - bool isTokenOnParentAvailableList(const ExpectedTokenPtr& token); + bool isTokenOnAvailableColumnList(const ExpectedTokenPtr& token); + bool isTokenOnParentAvailableColumnList(const ExpectedTokenPtr& token); bool isTokenOnResultColumns(const ExpectedTokenPtr& token); static bool isTokenOnColumnList(const ExpectedTokenPtr& token, const QList& columnList); }; diff --git a/SQLiteStudio3/coreSQLiteStudio/completionhelper.cpp b/SQLiteStudio3/coreSQLiteStudio/completionhelper.cpp index 675a6a6..b38e42d 100644 --- a/SQLiteStudio3/coreSQLiteStudio/completionhelper.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/completionhelper.cpp @@ -1,6 +1,9 @@ #include "completionhelper.h" #include "completioncomparer.h" #include "db/db.h" +#include "parser/ast/sqlitedelete.h" +#include "parser/ast/sqliteinsert.h" +#include "parser/ast/sqliteupdate.h" #include "parser/keywords.h" #include "parser/parser.h" #include "parser/lexer.h" @@ -147,6 +150,7 @@ QList CompletionHelper::getExpectedTokens(TokenPtr token) switch (token->type) { case Token::CTX_ROWID_KW: + case Token::CTX_STRICT_KW: results += getExpectedToken(ExpectedToken::KEYWORD, token->value); break; case Token::CTX_NEW_KW: @@ -497,6 +501,16 @@ QList CompletionHelper::getObjects(ExpectedToken::Type type, c QList CompletionHelper::getColumns() { QList results; + switch (context) { + case Context::UPDATE_RETURNING: + case Context::INSERT_RETURNING: + case Context::DELETE_RETURNING: + results += getExpectedToken(ExpectedToken::OPERATOR, "*", QString(), QString(), 1); + break; + default: + break; + } + if (previousId) { if (twoIdsBack) @@ -896,6 +910,7 @@ void CompletionHelper::filterOtherId(QList &resultsSoFar, cons case Token::CTX_FK_MATCH: case Token::CTX_PRAGMA: case Token::CTX_ROWID_KW: + case Token::CTX_STRICT_KW: case Token::CTX_NEW_KW: case Token::CTX_OLD_KW: case Token::CTX_ERROR_MESSAGE: @@ -1055,6 +1070,21 @@ void CompletionHelper::extractQueryAdditionalInfo() { context = Context::EXPR; } + else if (isInUpdateReturning()) + { + context = Context::UPDATE_RETURNING; + extractUpdateAvailableColumnsAndTables(); + } + else if (isInInsertReturning()) + { + context = Context::INSERT_RETURNING; + extractInsertAvailableColumnsAndTables(); + } + else if (isInDeleteReturning()) + { + context = Context::DELETE_RETURNING; + extractDeleteAvailableColumnsAndTables(); + } } void CompletionHelper::detectSelectContext() @@ -1134,6 +1164,21 @@ bool CompletionHelper::isInCreateTrigger() return true; } +bool CompletionHelper::isInUpdateReturning() +{ + return isIn(SqliteQueryType::Update, "returning", "RETURNING"); +} + +bool CompletionHelper::isInDeleteReturning() +{ + return isIn(SqliteQueryType::Delete, "returning", "RETURNING"); +} + +bool CompletionHelper::isInInsertReturning() +{ + return isIn(SqliteQueryType::Insert, "returning", "RETURNING"); +} + bool CompletionHelper::isIn(SqliteQueryType queryType, const QString &tokenMapKey, const QString &prefixKeyword) { if (!parsedQuery) @@ -1266,7 +1311,8 @@ void CompletionHelper::initFunctions(Db* db) << "lag(expr)" << "lag(expr, offset)" << "lag(expr, offset, default)" << "lead(expr)" << "lead(expr, offset)" << "lead(expr, offset, default)" << "first_value(expr)" << "last_value(expr)" << "nth_value(expr, N)" - << "substring(X,Y,Z)" << "substring(X,Y)"; + << "substring(X,Y,Z)" << "substring(X,Y)" << "unixepoch(mod,mod,...)" + << "printf(format,...)" << "format(format,...)"; if (!db->isOpen()) return; @@ -1274,7 +1320,7 @@ void CompletionHelper::initFunctions(Db* db) // Parse what we already have QSet handledSignatures; static_qstring(sigTpl, "%1_%2"); - for (const QString& fn : sqlite3Functions) + for (QString& fn : sqlite3Functions) { int argStart = fn.lastIndexOf("("); int argEnd = fn.lastIndexOf(")"); @@ -1405,9 +1451,42 @@ void CompletionHelper::extractSelectAvailableColumnsAndTables() } } +void CompletionHelper::extractInsertAvailableColumnsAndTables() +{ + auto insert = parsedQuery.dynamicCast(); + extractAvailableColumnsAndTables(insert->database, insert->table); +} + +void CompletionHelper::extractDeleteAvailableColumnsAndTables() +{ + auto del = parsedQuery.dynamicCast(); + extractAvailableColumnsAndTables(del->database, del->table); +} + +void CompletionHelper::extractUpdateAvailableColumnsAndTables() +{ + auto update = parsedQuery.dynamicCast(); + theFromAvailableColumns = selectResolver->resolveAvailableColumns(update->from); + theFromAvailableTables = selectResolver->resolveTables(update->from); +} + +void CompletionHelper::extractAvailableColumnsAndTables(const QString& database, const QString& table) +{ + QStringList columnNames = schemaResolver->getTableColumns(database, table); + for (QString& colName : columnNames) { + SelectResolver::Column column; + column.type = SelectResolver::Column::COLUMN; + column.database = database; + column.table = table; + column.column = colName; + theFromAvailableColumns << column; + theFromAvailableTables << column.getTable(); + } +} + void CompletionHelper::extractTableAliasMap() { - for (SelectResolver::Column column : selectAvailableColumns) + for (SelectResolver::Column& column : selectAvailableColumns) { if (column.type != SelectResolver::Column::COLUMN) continue; @@ -1423,7 +1502,7 @@ void CompletionHelper::extractTableAliasMap() // Given the above, we can extract table aliases in an order from deepest // to shallowest, skipping any duplicates, becase the deeper alias is mentioned, // the higher is its priority. - for (SelectResolver::Column column : parentSelectAvailableColumns) + for (SelectResolver::Column& column : parentSelectAvailableColumns) { if (column.type != SelectResolver::Column::COLUMN) continue; @@ -1445,7 +1524,7 @@ void CompletionHelper::extractCreateTableColumns() return; SqliteCreateTablePtr createTable = parsedQuery.dynamicCast(); - for (SqliteCreateTable::Column* col : createTable->columns) + for (SqliteCreateTable::Column*& col : createTable->columns) favoredColumnNames << col->name; } diff --git a/SQLiteStudio3/coreSQLiteStudio/completionhelper.h b/SQLiteStudio3/coreSQLiteStudio/completionhelper.h index 8df1912..3cff8ec 100644 --- a/SQLiteStudio3/coreSQLiteStudio/completionhelper.h +++ b/SQLiteStudio3/coreSQLiteStudio/completionhelper.h @@ -64,7 +64,10 @@ class API_EXPORT CompletionHelper : public QObject DELETE_WHERE, CREATE_TABLE, CREATE_TRIGGER, - EXPR + EXPR, + INSERT_RETURNING, + UPDATE_RETURNING, + DELETE_RETURNING }; static void initFunctions(Db* db); @@ -125,6 +128,10 @@ class API_EXPORT CompletionHelper : public QObject void extractPreviousIdTokens(const TokenList& parsedTokens); void extractQueryAdditionalInfo(); void extractSelectAvailableColumnsAndTables(); + void extractInsertAvailableColumnsAndTables(); + void extractDeleteAvailableColumnsAndTables(); + void extractUpdateAvailableColumnsAndTables(); + void extractAvailableColumnsAndTables(const QString& database, const QString& table); bool extractSelectCore(); SqliteSelect::Core* extractSelectCore(SqliteQueryPtr query); void extractTableAliasMap(); @@ -135,6 +142,9 @@ class API_EXPORT CompletionHelper : public QObject bool isInDeleteWhere(); bool isInCreateTable(); bool isInCreateTrigger(); + bool isInUpdateReturning(); + bool isInDeleteReturning(); + bool isInInsertReturning(); bool isIn(SqliteQueryType queryType, const QString& tokenMapKey, const QString &prefixKeyword); bool isInExpr(); bool testQueryToken(int tokenPosition, Token::Type type, const QString& value, Qt::CaseSensitivity cs = Qt::CaseInsensitive); @@ -230,6 +240,20 @@ class API_EXPORT CompletionHelper : public QObject */ QSet selectAvailableTables; + /** + * @brief theFromAvailableColumns + * Available columns are columns that can be selected basing on what tables are mentioned in FROM clause + * of queries other than SELECT (so INSERT, UPDATE, DELETE), in their RETURNING clause. + */ + QList theFromAvailableColumns; + + /** + * @brief theFromAvailableTables + * Availble tables are tables mentioned in FROM clause of queries other than SELECT (so INSERT, UPDATE, DELETE), + * in their RETURNING clause. + */ + QSet theFromAvailableTables; + /** * @brief parentSelectCores * List of all parent select core objects in order: from direct parent, to the oldest parent. diff --git a/SQLiteStudio3/coreSQLiteStudio/config_builder.h b/SQLiteStudio3/coreSQLiteStudio/config_builder.h index f4e030e..4f651e5 100644 --- a/SQLiteStudio3/coreSQLiteStudio/config_builder.h +++ b/SQLiteStudio3/coreSQLiteStudio/config_builder.h @@ -6,7 +6,7 @@ #include "config_builder/cfgentry.h" #include "config_builder/cfglazyinitializer.h" -#define CFG_CATEGORIES(Type,Body) _CFG_CATEGORIES_WITH_METANAME_AND_TITLE(Type,Body,"",QString()) +#define CFG_CATEGORIES(Type,Body) _CFG_CATEGORIES_WITH_METANAME_AND_TITLE(Type,Body,"",QString(),API_EXPORT) #define CFG_CATEGORY(Name,Body) \ _CFG_CATEGORY_WITH_TITLE(Name,Body,QString()) @@ -34,24 +34,30 @@ #define CFG_INSTANCE(Type) (*Cfg::get##Type##Instance()) +#define CFG_DELETE_INSTANCE(Type) \ + if (Cfg::cfgMainInstance##Type) \ + delete Cfg::cfgMainInstance##Type; \ + Cfg::cfgMainInstance##Type = nullptr; + + // Macros below are kind of private. You should not need to use them explicitly. // They are called from macros above. #define _CFG_CATEGORIES_WITH_METANAME(Type,Body,MetaName) \ - _CFG_CATEGORIES_WITH_METANAME_AND_TITLE(Type,Body,MetaName,QString()) + _CFG_CATEGORIES_WITH_METANAME_AND_TITLE(Type,Body,MetaName,QString(),API_EXPORT) #define _CFG_CATEGORIES_WITH_TITLE(Type,Body,Title) \ - _CFG_CATEGORIES_WITH_METANAME_AND_TITLE(Type,Body,"",Title) + _CFG_CATEGORIES_WITH_METANAME_AND_TITLE(Type,Body,"",Title,API_EXPORT) -#define _CFG_CATEGORIES_WITH_METANAME_AND_TITLE(Type,Body,MetaName,Title) \ +#define _CFG_CATEGORIES_WITH_METANAME_AND_TITLE(Type,Body,MetaName,Title,ExportType) \ namespace Cfg\ {\ - struct API_EXPORT Type : public CfgMain\ + struct ExportType Type : public CfgMain\ {\ Type(bool persistable) : CfgMain(#Type, persistable, MetaName, Title) {}\ Body\ };\ - API_EXPORT Type* get##Type##Instance();\ + ExportType Type* get##Type##Instance();\ } #define _CFG_DEFINE(Type, Persistant) \ diff --git a/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgcategory.cpp b/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgcategory.cpp index a5c661e..6f5451d 100644 --- a/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgcategory.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgcategory.cpp @@ -11,7 +11,7 @@ CfgCategory::CfgCategory(const CfgCategory &other) : lastCreatedCfgCategory = this; lastCreatedCfgMain->childs[name] = this; cfgParent = lastCreatedCfgMain; - for (CfgEntry* entry : childs) + for (CfgEntry*& entry : childs) entry->parent = this; } @@ -26,7 +26,10 @@ CfgCategory::CfgCategory(const QString &name, const QString &title) : CfgEntry *CfgCategory::getEntryByName(const QString& name) { - return childs[name]; + if (childs.contains(name)) + return childs[name]; + + return nullptr; } QString CfgCategory::toString() const @@ -44,32 +47,32 @@ void CfgCategory::translateTitle() // This needs to be "QObject::tr" and not just "tr", because this guarantees proper message context for retranslating // titles for objects initialized in global scope (as CfgCategories are). title = QObject::tr(title.toUtf8().constData()); - for (CfgEntry* entry : childs) + for (CfgEntry*& entry : childs) entry->translateTitle(); } void CfgCategory::reset() { - for (CfgEntry* entry : childs) + for (CfgEntry*& entry : childs) entry->reset(); } void CfgCategory::savepoint(bool transaction) { - for (CfgEntry* entry : childs) + for (CfgEntry*& entry : childs) entry->savepoint(transaction); } void CfgCategory::restore() { - for (CfgEntry* entry : childs) + for (CfgEntry*& entry : childs) entry->restore(); } void CfgCategory::release() { - for (CfgEntry* entry : childs) + for (CfgEntry*& entry : childs) entry->release(); } @@ -80,7 +83,8 @@ void CfgCategory::commit() void CfgCategory::rollback() { - rollback(); + for (CfgEntry*& entry : childs) + entry->rollback(); } void CfgCategory::begin() diff --git a/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgentry.cpp b/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgentry.cpp index 9a91ea8..bdbfd29 100644 --- a/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgentry.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgentry.cpp @@ -56,7 +56,7 @@ QVariant CfgEntry::get() const return cfgVal; } -QVariant CfgEntry::getDefultValue() const +QVariant CfgEntry::getDefaultValue() const { if (defValueFunc) return (*defValueFunc)(); @@ -99,6 +99,11 @@ QString CfgEntry::getTitle() const return title; } +QString CfgEntry::getName() const +{ + return name; +} + void CfgEntry::translateTitle() { // This needs to be "QObject::tr" and not just "tr". See CfgCategory::translateTitle() for details. @@ -107,7 +112,7 @@ void CfgEntry::translateTitle() void CfgEntry::reset() { - set(getDefultValue()); + set(getDefaultValue()); } bool CfgEntry::isPersistable() const diff --git a/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgentry.h b/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgentry.h index 4c1bbb0..1b5bdd9 100644 --- a/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgentry.h +++ b/SQLiteStudio3/coreSQLiteStudio/config_builder/cfgentry.h @@ -23,12 +23,13 @@ class API_EXPORT CfgEntry : public QObject virtual ~CfgEntry(); QVariant get() const; - QVariant getDefultValue() const; + QVariant getDefaultValue() const; void set(const QVariant& value); operator QString() const; void defineDefaultValueFunction(DefaultValueProviderFunc func); QString getFullKey() const; QString getTitle() const; + QString getName() const; void translateTitle(); void reset(); bool isPersistable() const; diff --git a/SQLiteStudio3/coreSQLiteStudio/config_builder/cfglazyinitializer.cpp b/SQLiteStudio3/coreSQLiteStudio/config_builder/cfglazyinitializer.cpp index 7e554a8..1f5fa5e 100644 --- a/SQLiteStudio3/coreSQLiteStudio/config_builder/cfglazyinitializer.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/config_builder/cfglazyinitializer.cpp @@ -18,7 +18,7 @@ void CfgLazyInitializer::init() if (!instances) instances = new QList(); - for (CfgLazyInitializer* initializer : *instances) + for (CfgLazyInitializer*& initializer : *instances) initializer->doInitialize(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.pro b/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.pro index bcd8148..ee78caa 100644 --- a/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.pro +++ b/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -include($$PWD/../dirs.pri) +include($$PWD/../common.pri) include($$PWD/../utils.pri) OBJECTS_DIR = $$OBJECTS_DIR/coreSQLiteStudio @@ -12,13 +12,13 @@ MOC_DIR = $$MOC_DIR/coreSQLiteStudio UI_DIR = $$UI_DIR/coreSQLiteStudio QT -= gui -QT += script network +QT += qml network TARGET = coreSQLiteStudio TEMPLATE = lib win32: { - LIBS += -lpsapi -limagehlp + LIBS += -lpsapi -limagehlp -lversion DEFINES += "SQLITE_API=\"__declspec(dllexport)\"" !debug: { @@ -26,7 +26,7 @@ win32: { THE_DEST = $${DESTDIR} THE_FILE ~= s,/,\\,g THE_DEST ~= s,/,\\,g - QMAKE_POST_LINK += $$QMAKE_COPY $$THE_FILE $$THE_DEST $$escape_expand(\\n\\t) + QMAKE_POST_LINK += "$$QMAKE_COPY $$THE_FILE $$THE_DEST $$escape_expand(\\n\\t);" } } @@ -39,10 +39,10 @@ linux: { macx: { out_file = $$DESTDIR/lib $$TARGET .dylib - QMAKE_POST_LINK += install_name_tool -change libsqlite3.dylib @loader_path/../Frameworks/libsqlite3.dylib $$join(out_file) - QMAKE_POST_LINK += $$QMAKE_MKDIR $$DESTDIR/SQLiteStudio.app - QMAKE_POST_LINK += ; $$QMAKE_MKDIR $$DESTDIR/SQLiteStudio.app/Contents - QMAKE_POST_LINK += ; $$QMAKE_COPY $$PWD/Info.plist $$DESTDIR/SQLiteStudio.app/Contents + QMAKE_POST_LINK += "install_name_tool -change libsqlite3.dylib @loader_path/../Frameworks/libsqlite3.dylib \"$$join(out_file)\"" + QMAKE_POST_LINK += "; $$QMAKE_MKDIR \"$$DESTDIR/SQLiteStudio.app\"" + QMAKE_POST_LINK += "; $$QMAKE_MKDIR \"$$DESTDIR/SQLiteStudio.app/Contents\"" + QMAKE_POST_LINK += "; $$QMAKE_COPY \"$$PWD/Info.plist\" \"$$DESTDIR/SQLiteStudio.app/Contents\"" LIBS += -L/usr/local/lib } @@ -57,24 +57,20 @@ portable { CONFIG += c++17 QMAKE_CXXFLAGS += -pedantic -TRANSLATIONS += translations/coreSQLiteStudio_ro_RO.ts \ - translations/coreSQLiteStudio_de.ts \ - translations/coreSQLiteStudio_it.ts \ - translations/coreSQLiteStudio_zh_CN.ts \ - translations/coreSQLiteStudio_sk.ts \ - translations/coreSQLiteStudio_ru.ts \ - translations/coreSQLiteStudio_pt_BR.ts \ - translations/coreSQLiteStudio_fr.ts \ - translations/coreSQLiteStudio_es.ts \ - translations/coreSQLiteStudio_pl.ts - SOURCES += sqlitestudio.cpp \ + chillout/chillout.cpp \ + chillout/common/common.cpp \ + chillout/posix/posixcrashhandler.cpp \ + chillout/windows/StackWalker.cpp \ + chillout/windows/windowscrashhandler.cpp \ common/compatibility.cpp \ db/queryexecutorsteps/queryexecutorcolumntype.cpp \ + db/queryexecutorsteps/queryexecutorfilter.cpp \ parser/ast/sqlitefilterover.cpp \ parser/ast/sqlitenulls.cpp \ parser/ast/sqlitewindowdefinition.cpp \ returncode.cpp \ + services/codesnippetmanager.cpp \ services/config.cpp \ common/nulldevice.cpp \ parser/lexer_low_lev.cpp \ @@ -112,7 +108,6 @@ SOURCES += sqlitestudio.cpp \ parser/ast/sqliteselect.cpp \ parser/ast/sqliteupdate.cpp \ parser/ast/sqlitevacuum.cpp \ - parser/ast/sqlitecopy.cpp \ parser/ast/sqliteemptyquery.cpp \ parser/parser_helper_stubs.cpp \ parser/ast/sqliteexpr.cpp \ @@ -147,7 +142,6 @@ SOURCES += sqlitestudio.cpp \ db/queryexecutorsteps/queryexecutoraddrowids.cpp \ db/queryexecutorsteps/queryexecutorlimit.cpp \ db/queryexecutorsteps/queryexecutorcolumns.cpp \ - db/queryexecutorsteps/queryexecutorcellsize.cpp \ db/queryexecutorsteps/queryexecutororder.cpp \ db/sqlerrorcodes.cpp \ common/readwritelocker.cpp \ @@ -156,6 +150,7 @@ SOURCES += sqlitestudio.cpp \ csvserializer.cpp \ db/queryexecutorsteps/queryexecutordatasources.cpp \ expectedtoken.cpp \ + sqlfileexecutor.cpp \ sqlhistorymodel.cpp \ db/queryexecutorsteps/queryexecutorexplainmode.cpp \ services/notifymanager.cpp \ @@ -240,14 +235,22 @@ SOURCES += sqlitestudio.cpp \ parser/ast/sqliteupsert.cpp HEADERS += sqlitestudio.h\ + chillout/chillout.h \ + chillout/common/common.h \ + chillout/defines.h \ + chillout/posix/posixcrashhandler.h \ + chillout/windows/StackWalker.h \ + chillout/windows/windowscrashhandler.h \ common/compatibility.h \ coreSQLiteStudio_global.h \ db/queryexecutorsteps/queryexecutorcolumntype.h \ + db/queryexecutorsteps/queryexecutorfilter.h \ db/sqlite3.h \ parser/ast/sqlitefilterover.h \ parser/ast/sqlitenulls.h \ parser/ast/sqlitewindowdefinition.h \ returncode.h \ + services/codesnippetmanager.h \ services/config.h \ common/nulldevice.h \ parser/lexer_low_lev.h \ @@ -285,7 +288,6 @@ HEADERS += sqlitestudio.h\ parser/ast/sqliteselect.h \ parser/ast/sqliteupdate.h \ parser/ast/sqlitevacuum.h \ - parser/ast/sqlitecopy.h \ parser/ast/sqlitequerytype.h \ parser/ast/sqliteemptyquery.h \ parser/parser_helper_stubs.h \ @@ -329,7 +331,6 @@ HEADERS += sqlitestudio.h\ db/queryexecutorsteps/queryexecutoraddrowids.h \ db/queryexecutorsteps/queryexecutorlimit.h \ db/queryexecutorsteps/queryexecutorcolumns.h \ - db/queryexecutorsteps/queryexecutorcellsize.h \ common/unused.h \ db/queryexecutorsteps/queryexecutororder.h \ common/readwritelocker.h \ @@ -337,6 +338,7 @@ HEADERS += sqlitestudio.h\ csvformat.h \ csvserializer.h \ db/queryexecutorsteps/queryexecutordatasources.h \ + sqlfileexecutor.h \ sqlhistorymodel.h \ db/queryexecutorsteps/queryexecutorexplainmode.h \ services/notifymanager.h \ @@ -475,4 +477,5 @@ RESOURCES += \ coreSQLiteStudio.qrc DISTFILES += \ + licenses/icu.txt \ licenses/mit.txt diff --git a/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.qrc b/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.qrc index f0bc89f..5ff8de8 100644 --- a/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.qrc +++ b/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.qrc @@ -18,14 +18,6 @@ licenses/diff_match.txt licenses/gpl.txt licenses/mit.txt - - - translations/coreSQLiteStudio_ro_RO.qm - translations/coreSQLiteStudio_pl.qm - translations/coreSQLiteStudio_ru.qm - translations/coreSQLiteStudio_fr.qm - translations/coreSQLiteStudio_sk.qm - translations/coreSQLiteStudio_zh_CN.qm - translations/coreSQLiteStudio_de.qm + licenses/icu.txt diff --git a/SQLiteStudio3/coreSQLiteStudio/csvformat.h b/SQLiteStudio3/coreSQLiteStudio/csvformat.h index 4b6df69..79807cb 100644 --- a/SQLiteStudio3/coreSQLiteStudio/csvformat.h +++ b/SQLiteStudio3/coreSQLiteStudio/csvformat.h @@ -24,6 +24,7 @@ struct API_EXPORT CsvFormat bool multipleColumnSeparators = false; int maxColumnSeparatorLength = 0; int maxRowSeparatorLength = 0; + bool quotationMark = true; static const CsvFormat DEFAULT; }; diff --git a/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp b/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp index aceba03..bc82265 100644 --- a/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp @@ -94,7 +94,7 @@ void typedDeserializeInternal(QTextStream& data, const CsvFormat& format, QList< data >> theChar; sepAsLast = false; - if (!quotes && theChar == '"' ) + if (format.quotationMark && !quotes && theChar == '"' ) { quotes = true; } diff --git a/SQLiteStudio3/coreSQLiteStudio/datatype.cpp b/SQLiteStudio3/coreSQLiteStudio/datatype.cpp index 4fbd7ef..36fe63c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/datatype.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/datatype.cpp @@ -36,6 +36,18 @@ const QStringList DataType::names = []() -> QStringList return list; }(); +QList DataType::valuesForUiDropdown = {BLOB, INTEGER, NUMERIC, REAL, TEXT}; +QList DataType::strictValues = {ANY, INT, INTEGER, REAL, TEXT, BLOB}; + +const QStringList DataType::strictNames = []() -> QStringList +{ + QStringList list; + for (DataType::Enum& type : strictValues) + list << DataType::toString(type); + + return list; +}(); + DataType::DataType() { setEmpty(); @@ -130,22 +142,27 @@ QString DataType::toFullTypeString() const return str; } -bool DataType::isNumeric() +bool DataType::isNumeric() const { return isNumeric(type); } -bool DataType::isBinary() +bool DataType::isBinary() const { return isBinary(typeStr); } -bool DataType::isNull() +bool DataType::isStrict() const +{ + return isStrict(type); +} + +bool DataType::isNull() const { return type == ::DataType::unknown; } -bool DataType::isEmpty() +bool DataType::isEmpty() const { return typeStr.isEmpty(); } @@ -155,7 +172,7 @@ DataType& DataType::operator=(const DataType& other) this->type = other.type; this->typeStr = other.typeStr; this->precision = other.precision; - this->scale = scale; + this->scale = other.scale; return *this; } @@ -206,10 +223,21 @@ bool DataType::isNumeric(DataType::Enum e) case TEXT: case TIME: case VARCHAR: + case ANY: case unknown: break; } - return false; + return false; +} + +bool DataType::isStrict(Enum e) +{ + return strictValues.contains(e); +} + +bool DataType::isStrict(const QString& type) +{ + return isStrict(fromString(type, Qt::CaseInsensitive)); } bool DataType::isBinary(const QString& type) @@ -223,6 +251,21 @@ QList DataType::getAllTypes() return values; } +QList DataType::getAllTypesForUiDropdown() +{ + return valuesForUiDropdown; +} + +QList DataType::getStrictValues() +{ + return strictValues; +} + +QStringList DataType::getStrictValueNames() +{ + return strictNames; +} + QStringList DataType::getAllNames() { return names; diff --git a/SQLiteStudio3/coreSQLiteStudio/datatype.h b/SQLiteStudio3/coreSQLiteStudio/datatype.h index 9d8ca4e..06b4169 100644 --- a/SQLiteStudio3/coreSQLiteStudio/datatype.h +++ b/SQLiteStudio3/coreSQLiteStudio/datatype.h @@ -7,12 +7,12 @@ class API_EXPORT DataType : public QObject { - Q_OBJECT - Q_ENUMS(Enum) + Q_OBJECT public: enum Enum { + ANY, BIGINT, BLOB, BOOLEAN, @@ -32,6 +32,7 @@ class API_EXPORT DataType : public QObject VARCHAR, unknown }; + Q_ENUM(Enum) DataType(); DataType(const QString& fullTypeString); @@ -46,18 +47,24 @@ class API_EXPORT DataType : public QObject QString toString() const; QString toFullTypeString() const; void setEmpty(); - bool isNumeric(); - bool isBinary(); - bool isNull(); - bool isEmpty(); + bool isNumeric() const; + bool isBinary() const; + bool isStrict() const; + bool isNull() const; + bool isEmpty() const; DataType& operator=(const DataType& other); static QString toString(Enum e); static Enum fromString(QString key, Qt::CaseSensitivity cs = Qt::CaseSensitive); static bool isNumeric(Enum e); + static bool isStrict(Enum e); + static bool isStrict(const QString& type); static bool isBinary(const QString& type); static QList getAllTypes(); static QStringList getAllNames(); + static QList getAllTypesForUiDropdown(); + static QList getStrictValues(); + static QStringList getStrictValueNames(); private: Enum type = unknown; @@ -67,6 +74,9 @@ class API_EXPORT DataType : public QObject static QList values; static const QStringList names; + static QList valuesForUiDropdown; + static QList strictValues; + static const QStringList strictNames; }; #endif // DATATYPE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp index 9bfb46f..b137c1f 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp @@ -1,15 +1,13 @@ #include "abstractdb.h" -#include "services/dbmanager.h" +#include "services/collationmanager.h" #include "common/utils.h" #include "asyncqueryrunner.h" #include "sqlresultsrow.h" #include "common/utils_sql.h" -#include "services/config.h" #include "sqlerrorresults.h" #include "sqlerrorcodes.h" #include "services/notifymanager.h" #include "services/sqliteextensionmanager.h" -#include "log.h" #include "parser/lexer.h" #include "common/compatibility.h" #include @@ -25,10 +23,12 @@ quint32 AbstractDb::asyncId = 1; AbstractDb::AbstractDb(const QString& name, const QString& path, const QHash& connOptions) : name(name), path(path), connOptions(connOptions) { + connect(SQLITESTUDIO, SIGNAL(aboutToQuit()), this, SLOT(appIsAboutToQuit())); } AbstractDb::~AbstractDb() { + disconnect(SQLITESTUDIO, SIGNAL(aboutToQuit()), this, SLOT(appIsAboutToQuit())); } bool AbstractDb::open() @@ -47,7 +47,11 @@ bool AbstractDb::close() if (deny) return false; - bool res = !isOpen() || closeQuiet(); + bool open = isOpen(); + if (open) + flushWal(); + + bool res = !open || closeQuiet(); if (res) emit disconnected(); @@ -71,7 +75,7 @@ bool AbstractDb::closeQuiet() registeredFunctions.clear(); registeredCollations.clear(); if (FUNCTIONS) // FUNCTIONS is already null when closing db while closing entire app - disconnect(FUNCTIONS, SIGNAL(functionListChanged()), this, SLOT(registerAllFunctions())); + disconnect(FUNCTIONS, SIGNAL(functionListChanged()), this, SLOT(registerUserFunctions())); return res; } @@ -89,40 +93,49 @@ bool AbstractDb::openForProbing() return res; } -void AbstractDb::registerAllFunctions() +void AbstractDb::registerUserFunctions() { - for (const RegisteredFunction& regFn : registeredFunctions) + QMutableSetIterator it(registeredFunctions); + while (it.hasNext()) { + const RegisteredFunction& regFn = it.next(); + if (regFn.builtIn) + continue; + if (!deregisterFunction(regFn.name, regFn.argCount)) qWarning() << "Failed to deregister custom SQL function:" << regFn.name; - } - registeredFunctions.clear(); + it.remove(); + } RegisteredFunction regFn; - for (FunctionManager::ScriptFunction* fnPtr : FUNCTIONS->getScriptFunctionsForDatabase(getName())) + for (FunctionManager::ScriptFunction*& fnPtr : FUNCTIONS->getScriptFunctionsForDatabase(getName())) { regFn.argCount = fnPtr->undefinedArgs ? -1 : fnPtr->arguments.count(); regFn.name = fnPtr->name; regFn.type = fnPtr->type; + regFn.deterministic = fnPtr->deterministic; registerFunction(regFn); } +} - for (FunctionManager::NativeFunction* fnPtr : FUNCTIONS->getAllNativeFunctions()) +void AbstractDb::registerBuiltInFunctions() +{ + RegisteredFunction regFn; + for (FunctionManager::NativeFunction*& fnPtr : FUNCTIONS->getAllNativeFunctions()) { regFn.argCount = fnPtr->undefinedArgs ? -1 : fnPtr->arguments.count(); regFn.name = fnPtr->name; regFn.type = fnPtr->type; + regFn.builtIn = true; + regFn.deterministic = fnPtr->deterministic; registerFunction(regFn); } - - disconnect(FUNCTIONS, SIGNAL(functionListChanged()), this, SLOT(registerAllFunctions())); - connect(FUNCTIONS, SIGNAL(functionListChanged()), this, SLOT(registerAllFunctions())); } -void AbstractDb::registerAllCollations() +void AbstractDb::registerUserCollations() { - for (const QString& name : registeredCollations) + for (QString& name : registeredCollations) { if (!deregisterCollation(name)) qWarning() << "Failed to deregister custom collation:" << name; @@ -130,16 +143,16 @@ void AbstractDb::registerAllCollations() registeredCollations.clear(); - for (const CollationManager::CollationPtr& collPtr : COLLATIONS->getCollationsForDatabase(getName())) + for (CollationManager::CollationPtr& collPtr : COLLATIONS->getCollationsForDatabase(getName())) registerCollation(collPtr->name); - disconnect(COLLATIONS, SIGNAL(collationListChanged()), this, SLOT(registerAllCollations())); - connect(COLLATIONS, SIGNAL(collationListChanged()), this, SLOT(registerAllCollations())); + disconnect(COLLATIONS, SIGNAL(collationListChanged()), this, SLOT(registerUserCollations())); + connect(COLLATIONS, SIGNAL(collationListChanged()), this, SLOT(registerUserCollations())); } void AbstractDb::loadExtensions() { - for (const SqliteExtensionManager::ExtensionPtr& extPtr : SQLITE_EXTENSIONS->getExtensionForDatabase(getName())) + for (SqliteExtensionManager::ExtensionPtr& extPtr : SQLITE_EXTENSIONS->getExtensionForDatabase(getName())) loadedExtensionCount += loadExtension(extPtr->filePath, extPtr->initFunc) ? 1 : 0; connect(SQLITE_EXTENSIONS, SIGNAL(extensionListChanged()), this, SLOT(reloadExtensions())); @@ -377,14 +390,20 @@ bool AbstractDb::openAndSetup() // Implementation specific initialization initAfterOpen(); + // Built-in SQL functions + registerBuiltInFunctions(); + // Load extension loadExtensions(); // Custom SQL functions - registerAllFunctions(); + registerUserFunctions(); // Custom collations - registerAllCollations(); + registerUserCollations(); + + disconnect(FUNCTIONS, SIGNAL(functionListChanged()), this, SLOT(registerUserFunctions())); + connect(FUNCTIONS, SIGNAL(functionListChanged()), this, SLOT(registerUserFunctions())); return result; } @@ -616,6 +635,12 @@ void AbstractDb::asyncQueryFinished(AsyncQueryRunner *runner) emit idle(); } +void AbstractDb::appIsAboutToQuit() +{ + if (isOpen()) + flushWal(); +} + QString AbstractDb::attach(Db* otherDb, bool silent) { QWriteLocker locker(&dbOperLock); @@ -716,7 +741,7 @@ QString AbstractDb::getUniqueNewObjectName(const QString &attachedDbName) QSet existingNames; SqlQueryPtr results = exec(QString("SELECT name FROM %1.sqlite_master").arg(dbName)); - for (SqlResultsRowPtr row : results->getAll()) + for (SqlResultsRowPtr& row : results->getAll()) existingNames << row->value(0).toString(); return randStrNotIn(16, existingNames, false); @@ -875,10 +900,10 @@ void AbstractDb::registerFunction(const AbstractDb::RegisteredFunction& function switch (function.type) { case FunctionManager::ScriptFunction::SCALAR: - successful = registerScalarFunction(function.name, function.argCount); + successful = registerScalarFunction(function.name, function.argCount, function.deterministic); break; case FunctionManager::ScriptFunction::AGGREGATE: - successful = registerAggregateFunction(function.name, function.argCount); + successful = registerAggregateFunction(function.name, function.argCount, function.deterministic); break; } @@ -888,6 +913,12 @@ void AbstractDb::registerFunction(const AbstractDb::RegisteredFunction& function qCritical() << "Could not register SQL function:" << function.name << function.argCount << function.type; } +void AbstractDb::flushWal() +{ + if (!flushWalInternal()) + notifyWarn(tr("Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2").arg(name, getErrorTextInternal())); +} + int qHash(const AbstractDb::RegisteredFunction& fn) { return qHash(fn.name) ^ fn.argCount ^ fn.type; diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.h b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.h index a465679..193f173 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.h @@ -203,6 +203,8 @@ class API_EXPORT AbstractDb : public Db virtual void initAfterOpen(); + virtual bool flushWalInternal() = 0; + void checkForDroppedObject(const QString& query); bool registerCollation(const QString& name); bool deregisterCollation(const QString& name); @@ -341,6 +343,16 @@ class API_EXPORT AbstractDb : public Db * @brief Function type. */ FunctionManager::ScriptFunction::Type type; + + /** + * @brief The deterministic flag used for function registration. + */ + bool deterministic; + + /** + * @brief Flag indicating if this function is SQLiteStudio's built-in function or user's custom function. + */ + bool builtIn = false; }; friend int qHash(const AbstractDb::RegisteredFunction& fn); @@ -414,6 +426,23 @@ class API_EXPORT AbstractDb : public Db */ void registerFunction(const RegisteredFunction& function); + /** + * @brief Flushes any pending WAL log files into the db file. + * + * It actually makes a 'PRAGMA wal_checkpoint(FULL)' call on the database. + * It's called automatically upon closing the database. + */ + void flushWal(); + + /** + * @brief Registers SQLiteStudio's built-in functions in the db. + * + * This function is called once during opening the db. + * + * @see FunctionManager + */ + void registerBuiltInFunctions(); + /** * @brief Connection state lock. * @@ -457,14 +486,16 @@ class API_EXPORT AbstractDb : public Db */ void asyncQueryFinished(AsyncQueryRunner* runner); + void appIsAboutToQuit(); + public slots: bool open(); bool close(); bool openQuiet(); bool closeQuiet(); bool openForProbing(); - void registerAllFunctions(); - void registerAllCollations(); + void registerUserFunctions(); + void registerUserCollations(); void reloadExtensions(); }; diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h index 72c1614..f8812e9 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h @@ -58,10 +58,11 @@ class AbstractDb3 : public AbstractDb bool initAfterCreated(); void initAfterOpen(); SqlQueryPtr prepare(const QString& query); - QString getTypeLabel(); + bool flushWalInternal(); + QString getTypeLabel() const; bool deregisterFunction(const QString& name, int argCount); - bool registerScalarFunction(const QString& name, int argCount); - bool registerAggregateFunction(const QString& name, int argCount); + bool registerScalarFunction(const QString& name, int argCount, bool deterministic); + bool registerAggregateFunction(const QString& name, int argCount, bool deterministic); bool registerCollationInternal(const QString& name); bool deregisterCollationInternal(const QString& name); @@ -121,6 +122,7 @@ class AbstractDb3 : public AbstractDb }; QString extractLastError(); + QString extractLastError(typename T::handle* handle); void cleanUp(); void resetError(); @@ -406,6 +408,22 @@ int AbstractDb3::getErrorCodeInternal() return dbErrorCode; } +template +bool AbstractDb3::flushWalInternal() +{ + resetError(); + if (!dbHandle) + return false; + + int res = T::wal_checkpoint_v2(dbHandle, nullptr, T::CHECKPOINT_FULL, nullptr, nullptr); + if (res != T::OK) + { + dbErrorMessage = QObject::tr("Could not run WAL checkpoint: %1").arg(extractLastError()); + dbErrorCode = res; + } + return res == T::OK; +} + template bool AbstractDb3::openInternal() { @@ -414,11 +432,11 @@ bool AbstractDb3::openInternal() int res = T::open_v2(path.toUtf8().constData(), &handle, T::OPEN_READWRITE|T::OPEN_CREATE, nullptr); if (res != T::OK) { + dbErrorMessage = QObject::tr("Could not open database: %1").arg(extractLastError(handle)); + dbErrorCode = res; if (handle) T::close(handle); - dbErrorMessage = QObject::tr("Could not open database: %1").arg(extractLastError()); - dbErrorCode = res; return false; } dbHandle = handle; @@ -469,7 +487,7 @@ SqlQueryPtr AbstractDb3::prepare(const QString& query) } template -QString AbstractDb3::getTypeLabel() +QString AbstractDb3::getTypeLabel() const { return T::label; } @@ -485,7 +503,7 @@ bool AbstractDb3::deregisterFunction(const QString& name, int argCount) } template -bool AbstractDb3::registerScalarFunction(const QString& name, int argCount) +bool AbstractDb3::registerScalarFunction(const QString& name, int argCount, bool deterministic) { if (!dbHandle) return false; @@ -495,7 +513,11 @@ bool AbstractDb3::registerScalarFunction(const QString& name, int argCount) userData->name = name; userData->argCount = argCount; - int res = T::create_function_v2(dbHandle, name.toUtf8().constData(), argCount, T::UTF8, userData, + int opts = T::UTF8; + if (deterministic) + opts |= T::DETERMINISTIC; + + int res = T::create_function_v2(dbHandle, name.toUtf8().constData(), argCount, opts, userData, &AbstractDb3::evaluateScalar, nullptr, nullptr, @@ -505,7 +527,7 @@ bool AbstractDb3::registerScalarFunction(const QString& name, int argCount) } template -bool AbstractDb3::registerAggregateFunction(const QString& name, int argCount) +bool AbstractDb3::registerAggregateFunction(const QString& name, int argCount, bool deterministic) { if (!dbHandle) return false; @@ -515,7 +537,11 @@ bool AbstractDb3::registerAggregateFunction(const QString& name, int argCount userData->name = name; userData->argCount = argCount; - int res = T::create_function_v2(dbHandle, name.toUtf8().constData(), argCount, T::UTF8, userData, + int opts = T::UTF8; + if (deterministic) + opts |= T::DETERMINISTIC; + + int res = T::create_function_v2(dbHandle, name.toUtf8().constData(), argCount, opts, userData, nullptr, &AbstractDb3::evaluateAggregateStep, &AbstractDb3::evaluateAggregateFinal, @@ -552,8 +578,14 @@ bool AbstractDb3::deregisterCollationInternal(const QString& name) template QString AbstractDb3::extractLastError() { - dbErrorCode = T::extended_errcode(dbHandle); - dbErrorMessage = QString::fromUtf8(T::errmsg(dbHandle)); + return extractLastError(dbHandle); +} + +template +QString AbstractDb3::extractLastError(typename T::handle* handle) +{ + dbErrorCode = T::extended_errcode(handle); + dbErrorMessage = QString::fromUtf8(T::errmsg(handle)); return dbErrorMessage; } @@ -799,11 +831,25 @@ void AbstractDb3::registerDefaultCollation(void* fnUserData, typename T::hand return; } + SqlQueryPtr results = db->exec("PRAGMA collation_list", Db::Flag::NO_LOCK|Db::Flag::SKIP_DROP_DETECTION); + if (results->isError()) + qWarning() << "Unable to query existing collations while registering needed collation" << collationName << ":" << db->getErrorText(); + + QStringList existingCollations = results->columnAsList("name"); + if (existingCollations.contains(collationName)) + { + qDebug() << "Requested collation" << collationName << "already exists. Probably different input encoding was expected," + << "but SQLite should deal with it. Skipping default collation registration."; + return; + } + int res = T::create_collation_v2(fnDbHandle, collationName, T::UTF8, nullptr, &AbstractDb3::evaluateDefaultCollation, nullptr); if (res != T::OK) qWarning() << "Could not register default collation in AbstractDb3::registerDefaultCollation()."; + else + qDebug() << "Registered default collation on demand, under name:" << collationName; } template diff --git a/SQLiteStudio3/coreSQLiteStudio/db/db.cpp b/SQLiteStudio3/coreSQLiteStudio/db/db.cpp index b3ad91b..11e47f2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/db.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/db.cpp @@ -46,6 +46,6 @@ QDataStream &operator >>(QDataStream &in, Db*& myObj) QDebug operator<<(QDebug dbg, const Db* db) { - dbg.nospace() << "getName() << ">"; + dbg.nospace() << "getName() : 0x0) << ">"; return dbg.space(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/db/db.h b/SQLiteStudio3/coreSQLiteStudio/db/db.h index d937fb7..1331fff 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/db.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/db.h @@ -2,9 +2,7 @@ #define DB_H #include -#include "returncode.h" -#include "services/functionmanager.h" -#include "common/readwritelocker.h" +#include "common/global.h" #include "coreSQLiteStudio_global.h" #include "db/attachguard.h" #include "interruptable.h" @@ -604,7 +602,16 @@ class API_EXPORT Db : public QObject, public Interruptable * This is usually the same as DbPlugin::getTitle(), but getTitle() is used in list of plugins in configuration dialog, * while getTypeLabel() is used on databases list. */ - virtual QString getTypeLabel() = 0; + virtual QString getTypeLabel() const = 0; + + /** + * @brief Gets C++ class name implementing this particular Db instance. + * @return Class name. + * + * It can be used to distinguish between different drivers of Db instances. While getTypeLabel() can theoretically return + * same labels for two different drivers, this method will always return distinct class name. + */ + virtual QString getTypeClassName() const = 0; /** * @brief Initializes resources once the all derived Db classes are constructed. @@ -631,6 +638,7 @@ class API_EXPORT Db : public QObject, public Interruptable * @brief Registers scalar custom SQL function. * @param name Name of the function. * @param argCount Number of arguments accepted by the function (-1 for undefined). + * @param deterministic The deterministic function flag used when registering the function. * @return true on success, false on failure. * * Scalar functions are evaluated for each row and their result is used in place of function invokation. @@ -643,12 +651,13 @@ class API_EXPORT Db : public QObject, public Interruptable * * @see FunctionManager */ - virtual bool registerScalarFunction(const QString& name, int argCount) = 0; + virtual bool registerScalarFunction(const QString& name, int argCount, bool deterministic) = 0; /** * @brief Registers aggregate custom SQL function. * @param name Name of the function. * @param argCount Number of arguments accepted by the function (-1 for undefined). + * @param deterministic The deterministic function flag used when registering the function. * @return true on success, false on failure. * * Aggregate functions are used to aggregate many rows into single row. They are common in queries with GROUP BY statements. @@ -666,7 +675,7 @@ class API_EXPORT Db : public QObject, public Interruptable * * @see FunctionManager */ - virtual bool registerAggregateFunction(const QString& name, int argCount) = 0; + virtual bool registerAggregateFunction(const QString& name, int argCount, bool deterministic) = 0; /** * @brief Registers a collation sequence implementation in the database. @@ -707,6 +716,15 @@ class API_EXPORT Db : public QObject, public Interruptable */ virtual bool loadExtension(const QString& filePath, const QString& initFunc = QString()) = 0; + /** + * @brief Creates instance of same (derived) class with same construction parameters passed. + * @return Created instance. + * + * This is useful when one needs to operate on this database out of DbManager context, + * so ownership, connection/disconnection, deletion, etc. all belongs to the caller of this method. + */ + virtual Db* clone() const = 0; + signals: /** * @brief Emitted when the connection to the database was established. @@ -825,22 +843,20 @@ class API_EXPORT Db : public QObject, public Interruptable virtual bool closeQuiet() = 0; /** - * @brief Deregisters all funtions registered in the database and registers new (possibly the same) functions. + * @brief Deregisters previously registered user-defined functions and registers their fresh definition in the db. * * This slot is called from openAndSetup() and then every time user modifies custom SQL functions and commits changes to them. - * It deregisters all functions registered before in this database and registers new functions, currently defined for - * this database. * * @see FunctionManager */ - virtual void registerAllFunctions() = 0; + virtual void registerUserFunctions() = 0; /** * @brief Deregisters all collations registered in the database and registers new (possibly the same) collations. * * This slot is called from openAndsetup() and then every time user modifies custom collations and commits changes to them. */ - virtual void registerAllCollations() = 0; + virtual void registerUserCollations() = 0; }; QDataStream &operator<<(QDataStream &out, const Db* myObj); diff --git a/SQLiteStudio3/coreSQLiteStudio/db/dbsqlite3.cpp b/SQLiteStudio3/coreSQLiteStudio/db/dbsqlite3.cpp index 4778bb2..76ddd20 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/dbsqlite3.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/dbsqlite3.cpp @@ -14,3 +14,13 @@ bool DbSqlite3::complete(const QString& sql) { return Sqlite3::complete(sql.toUtf8().constData()); } + +Db* DbSqlite3::clone() const +{ + return new DbSqlite3(name, path, connOptions); +} + +QString DbSqlite3::getTypeClassName() const +{ + return "DbSqlite3"; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/db/dbsqlite3.h b/SQLiteStudio3/coreSQLiteStudio/db/dbsqlite3.h index 1e7f6e2..54a4230 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/dbsqlite3.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/dbsqlite3.h @@ -2,7 +2,6 @@ #define DBSQLITE3_H #include "abstractdb3.h" -#include "common/global.h" #include "stdsqlite3driver.h" #include "db/sqlite3.h" @@ -30,6 +29,9 @@ class API_EXPORT DbSqlite3 : public AbstractDb3 DbSqlite3(const QString& name, const QString& path); static bool complete(const QString& sql); + + Db* clone() const; + QString getTypeClassName() const; }; #endif // DBSQLITE3_H diff --git a/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.cpp b/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.cpp index e7acb7b..e001017 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.cpp @@ -241,11 +241,16 @@ int InvalidDb::getErrorCode() return 0; } -QString InvalidDb::getTypeLabel() +QString InvalidDb::getTypeLabel() const { return QStringLiteral("INVALID"); } +QString InvalidDb::getTypeClassName() const +{ + return "InvalidDb"; +} + bool InvalidDb::initAfterCreated() { return false; @@ -258,17 +263,19 @@ bool InvalidDb::deregisterFunction(const QString& name, int argCount) return false; } -bool InvalidDb::registerScalarFunction(const QString& name, int argCount) +bool InvalidDb::registerScalarFunction(const QString& name, int argCount, bool deterministic) { UNUSED(name); UNUSED(argCount); + UNUSED(deterministic); return false; } -bool InvalidDb::registerAggregateFunction(const QString& name, int argCount) +bool InvalidDb::registerAggregateFunction(const QString& name, int argCount, bool deterministic) { UNUSED(name); UNUSED(argCount); + UNUSED(deterministic); return false; } @@ -309,11 +316,11 @@ bool InvalidDb::closeQuiet() return false; } -void InvalidDb::registerAllFunctions() +void InvalidDb::registerUserFunctions() { } -void InvalidDb::registerAllCollations() +void InvalidDb::registerUserCollations() { } QString InvalidDb::getError() const @@ -339,6 +346,11 @@ bool InvalidDb::isComplete(const QString& sql) const return false; } +Db* InvalidDb::clone() const +{ + return new InvalidDb(name, path, connOptions); +} + void InvalidDb::interrupt() { } diff --git a/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.h b/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.h index c56da2e..2505079 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/invaliddb.h @@ -48,11 +48,12 @@ class API_EXPORT InvalidDb : public Db QString getUniqueNewObjectName(const QString& attachedDbName); QString getErrorText(); int getErrorCode(); - QString getTypeLabel(); + QString getTypeLabel() const; + QString getTypeClassName() const; bool initAfterCreated(); bool deregisterFunction(const QString& name, int argCount); - bool registerScalarFunction(const QString& name, int argCount); - bool registerAggregateFunction(const QString& name, int argCount); + bool registerScalarFunction(const QString& name, int argCount, bool deterministic); + bool registerAggregateFunction(const QString& name, int argCount, bool deterministic); bool registerCollation(const QString& name); bool deregisterCollation(const QString& name); void interrupt(); @@ -61,6 +62,7 @@ class API_EXPORT InvalidDb : public Db void setError(const QString& value); bool loadExtension(const QString& filePath, const QString& initFunc); bool isComplete(const QString& sql) const; + Db* clone() const; public slots: bool open(); @@ -68,8 +70,8 @@ class API_EXPORT InvalidDb : public Db bool openQuiet(); bool openForProbing(); bool closeQuiet(); - void registerAllFunctions(); - void registerAllCollations(); + void registerUserFunctions(); + void registerUserCollations(); private: QString name; diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp index 83dae5d..022bf47 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.cpp @@ -1,5 +1,5 @@ #include "queryexecutor.h" -#include "sqlerrorresults.h" +#include "db/queryexecutorsteps/queryexecutorfilter.h" #include "sqlerrorcodes.h" #include "services/dbmanager.h" #include "db/sqlerrorcodes.h" @@ -10,7 +10,6 @@ #include "queryexecutorsteps/queryexecutorattaches.h" #include "queryexecutorsteps/queryexecutorcountresults.h" #include "queryexecutorsteps/queryexecutorexecute.h" -#include "queryexecutorsteps/queryexecutorcellsize.h" #include "queryexecutorsteps/queryexecutorlimit.h" #include "queryexecutorsteps/queryexecutororder.h" #include "queryexecutorsteps/queryexecutorwrapdistinctresults.h" @@ -49,7 +48,7 @@ QueryExecutor::QueryExecutor(Db* db, const QString& query, QObject *parent) : setAutoDelete(false); connect(this, SIGNAL(executionFailed(int,QString)), this, SLOT(cleanupAfterExecFailed(int,QString))); - connect(DBLIST, SIGNAL(dbAboutToBeUnloaded(Db*, DbPlugin*)), this, SLOT(cleanupBeforeDbDestroy(Db*))); + connect(DBLIST, SIGNAL(dbAboutToBeUnloaded(Db*,DbPlugin*)), this, SLOT(cleanupBeforeDbDestroy(Db*))); connect(DBLIST, SIGNAL(dbRemoved(Db*)), this, SLOT(cleanupBeforeDbDestroy(Db*))); connect(simpleExecutor, &ChainExecutor::finished, this, &QueryExecutor::simpleExecutionFinished, Qt::DirectConnection); } @@ -82,6 +81,12 @@ void QueryExecutor::setupExecutionChain() executionChain.append(additionalStatelessSteps[AFTER_REPLACED_VIEWS]); executionChain.append(createSteps(AFTER_REPLACED_VIEWS)); + executionChain << new QueryExecutorFilter() + << new QueryExecutorParseQuery("after Filter"); + + executionChain.append(additionalStatelessSteps[AFTER_COLUMN_FILTERS]); + executionChain.append(createSteps(AFTER_COLUMN_FILTERS)); + executionChain << new QueryExecutorAddRowIds() << new QueryExecutorParseQuery("after AddRowIds"); @@ -105,14 +110,8 @@ void QueryExecutor::setupExecutionChain() executionChain.append(additionalStatelessSteps[AFTER_DISTINCT_WRAP]); executionChain.append(createSteps(AFTER_DISTINCT_WRAP)); - executionChain << new QueryExecutorCellSize() - << new QueryExecutorCountResults() - << new QueryExecutorParseQuery("after CellSize"); - - executionChain.append(additionalStatelessSteps[AFTER_CELL_SIZE_LIMIT]); - executionChain.append(createSteps(AFTER_CELL_SIZE_LIMIT)); - - executionChain << new QueryExecutorColumnType() + executionChain << new QueryExecutorCountResults() + << new QueryExecutorColumnType() << new QueryExecutorParseQuery("after ColumnType"); executionChain.append(additionalStatelessSteps[AFTER_COLUMN_TYPES]); @@ -130,13 +129,13 @@ void QueryExecutor::setupExecutionChain() executionChain << new QueryExecutorExecute(); - for (QueryExecutorStep* step : executionChain) + for (QueryExecutorStep*& step : executionChain) step->init(this, context); } void QueryExecutor::clearChain() { - for (QueryExecutorStep* step : executionChain) + for (QueryExecutorStep*& step : executionChain) { if (!allAdditionalStatelsssSteps.contains(step)) delete step; @@ -149,7 +148,7 @@ void QueryExecutor::executeChain() { // Go through all remaining steps bool result; - for (QueryExecutorStep* currentStep : executionChain) + for (QueryExecutorStep*& currentStep : executionChain) { if (isInterrupted()) { @@ -189,7 +188,9 @@ void QueryExecutor::stepFailed(QueryExecutorStep* currentStep) if (isInterrupted()) { + executionMutex.lock(); executionInProgress = false; + executionMutex.unlock(); emit executionFailed(SqlErrorCode::INTERRUPTED, tr("Execution interrupted.")); return; } @@ -416,7 +417,7 @@ QList QueryExecutor::getRowIdResultColumns( int QueryExecutor::getMetaColumnCount() const { int count = 0; - for (ResultRowIdColumnPtr rowIdCol : context->rowIdColumns) + for (ResultRowIdColumnPtr& rowIdCol : context->rowIdColumns) count += rowIdCol->queryExecutorAliasToColumn.size(); return count; @@ -450,7 +451,9 @@ void QueryExecutor::executeSimpleMethod() if (queriesForSimpleExecution.isEmpty()) queriesForSimpleExecution = quickSplitQueries(originalQuery, false, true); - QStringList queriesWithPagination = applyLimitForSimpleMethod(queriesForSimpleExecution); + QStringList queriesWithPagination = applyFiltersAndLimitAndOrderForSimpleMethod(queriesForSimpleExecution); + if (isExecutorLoggingEnabled()) + qDebug() << "Simple Execution Method query:" << queriesWithPagination.join("; "); simpleExecutor->setQueries(queriesWithPagination); simpleExecutor->setDb(db); @@ -479,7 +482,7 @@ void QueryExecutor::simpleExecutionFinished(SqlQueryPtr results) ResultColumnPtr resCol; context->resultColumns.clear(); - for (const QString& colName : results->getColumnNames()) + for (QString& colName : results->getColumnNames()) { resCol = ResultColumnPtr::create(); resCol->displayName = colName; @@ -558,7 +561,7 @@ bool QueryExecutor::simpleExecIsSelect() void QueryExecutor::cleanup() { Db* attDb = nullptr; - for (const QString& attDbName : context->dbNameToAttach.leftValues()) + for (QString& attDbName : context->dbNameToAttach.leftValues()) { attDb = DBLIST->getByName(attDbName, Qt::CaseInsensitive); if (attDbName.isNull()) @@ -597,29 +600,65 @@ bool QueryExecutor::handleRowCountingResults(quint32 asyncId, SqlQueryPtr result return true; } -QStringList QueryExecutor::applyLimitForSimpleMethod(const QStringList &queries) +QStringList QueryExecutor::applyFiltersAndLimitAndOrderForSimpleMethod(const QStringList &queries) { + static_qstring(filtersTpl, "SELECT * FROM (%1) WHERE %2"); static_qstring(tpl, "SELECT * FROM (%1) LIMIT %2 OFFSET %3"); - if (page < 0) - return queries; // no paging requested + static_qstring(sortTpl, "SELECT * FROM (%1) ORDER BY %2"); + static_qstring(sortColTpl, "%1 %2"); + + if (page < 0 && sortOrder.isEmpty()) + return queries; QStringList result = queries; QString lastQuery = queries.last(); bool isSelect = false; getQueryAccessMode(lastQuery, &isSelect); - if (isSelect) + + // FILTERS + QString filters = getFilters(); + if (isSelect && !filters.isEmpty()) { - result.removeLast(); - result << tpl.arg(trimQueryEnd(lastQuery), QString::number(resultsPerPage), QString::number(page * resultsPerPage)); + lastQuery = filtersTpl.arg( + trimQueryEnd(lastQuery), + filters + ); } + + // ORDER BY + if (!sortOrder.isEmpty()) + { + QStringList cols; + for (QueryExecutor::Sort& sort : sortOrder) + { + cols << sortColTpl.arg( + QString::number(sort.column + 1), // in ORDER BY column indexes are 1-based + (sort.order == QueryExecutor::Sort::DESC) ? "DESC" : "ASC" + ); + } + lastQuery = sortTpl.arg(trimQueryEnd(lastQuery), cols.join(", ")); + } + + // LIMIT + if (page >= 0 && isSelect) + { + lastQuery = tpl.arg( + trimQueryEnd(lastQuery), + QString::number(resultsPerPage), + QString::number(page * resultsPerPage) + ); + } + + result.removeLast(); + result << lastQuery; return result; } QList QueryExecutor::createSteps(QueryExecutor::StepPosition position) { QList steps; - for (StepFactory* factory : additionalStatefulStepFactories[position]) + for (StepFactory*& factory : additionalStatefulStepFactories[position]) steps << factory->produceQueryExecutorStep(); return steps; @@ -714,7 +753,7 @@ void QueryExecutor::handleErrorsFromSmartAndSimpleMethods(SqlQueryPtr results) { QString match; QString replaceName; - for (const QString& attachName : context->dbNameToAttach.rightValues()) + for (QString& attachName : context->dbNameToAttach.rightValues()) { match = attachName + "."; replaceName = wrapObjIfNeeded(context->dbNameToAttach.valueByRight(attachName)) + "."; @@ -752,7 +791,7 @@ bool QueryExecutor::wasDataModifyingQuery() const QList QueryExecutor::resolveColumnTypes(Db* db, QList& columns, bool noDbLocking) { QSet tables; - for (ResultColumnPtr col : columns) + for (ResultColumnPtr& col : columns) tables << Table(col->database, col->table); SchemaResolver resolver(db); @@ -774,7 +813,7 @@ QList QueryExecutor::resolveColumnTypes(Db* db, QList datatypeList; Table t; SqliteCreateTable::Column* parsedCol = nullptr; - for (ResultColumnPtr col : columns) + for (ResultColumnPtr& col : columns) { t = Table(col->database, col->table); if (!parsedTables.contains(t)) @@ -969,3 +1008,13 @@ int qHash(QueryExecutor::SourceTable sourceTable) return qHash(sourceTable.database + "." + sourceTable.table + "/" + sourceTable.alias); } + +QString QueryExecutor::getFilters() const +{ + return filters; +} + +void QueryExecutor::setFilters(const QString& newFilters) +{ + filters = newFilters; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h index ffbcb7b..c8d0b00 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutor.h @@ -2,10 +2,10 @@ #define QUERYEXECUTOR_H #include "db/db.h" -#include "parser/token.h" -#include "selectresolver.h" #include "coreSQLiteStudio_global.h" #include "common/bistrhash.h" +#include "parser/ast/sqlitequery.h" +#include "parser/ast/sqlitequerytype.h" #include "datatype.h" #include #include @@ -15,7 +15,6 @@ /** @file */ class Parser; -class SqliteQuery; class QueryExecutorStep; class DbPlugin; class ChainExecutor; @@ -174,6 +173,10 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable * in COMPOUND_SELECT case. To learn about common table expression statement, * see http://sqlite.org/lang_with.html */ + VIEW_NOT_EXPANDED,/**< + * The data cell comes from a VIEW that was not expanded (because there were + * multi-level views), therefore it was impossible to get ROWID for the cell. + */ }; /** @@ -652,6 +655,14 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable * message from smart execution. */ QString errorMessageFromSmartExecution; + + /** + * @brief Flag indicating whether views were replaced/expanded. + * + * In other words, this flag tells whether the ReplaceViews step of query executor + * was executed, or skipped (due to many levels of views). False = skipped. + */ + bool viewsExpanded = false; }; /** @@ -679,8 +690,8 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable AFTER_REPLACED_COLUMNS, /**< After all columns have been explicitly listed in result list, together with unique alias names */ AFTER_ORDER, /**< After order clause was applied/modified */ AFTER_DISTINCT_WRAP, /**< After wrapping SELECT was added in case of DISTINCT or GROUP BY clauses were used */ - AFTER_CELL_SIZE_LIMIT, /**< After cell result size was limited to save memory usage */ AFTER_COLUMN_TYPES, /**< After typeof() result meta columns were added */ + AFTER_COLUMN_FILTERS, /**< After WHERE filters applied */ AFTER_ROW_LIMIT_AND_OFFSET, /**< After LIMIT and ORDER clauses were added/modified. This is the last possible moment, directly ahead of final query execution */ JUST_BEFORE_EXECUTION, /**< Same as AFTER_ROW_LIMIT_AND_OFFSET */ LAST /**< Same as AFTER_ROW_LIMIT_AND_OFFSET */ @@ -1077,6 +1088,9 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable bool getNoMetaColumns() const; void setNoMetaColumns(bool value); + void setFilters(const QString& newFilters); + QString getFilters() const; + void handleErrorsFromSmartAndSimpleMethods(SqlQueryPtr results); /** @@ -1222,7 +1236,7 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable */ bool handleRowCountingResults(quint32 asyncId, SqlQueryPtr results); - QStringList applyLimitForSimpleMethod(const QStringList &queries); + QStringList applyFiltersAndLimitAndOrderForSimpleMethod(const QStringList &queries); /** * @brief Creates instances of steps for all registered factories for given position. @@ -1347,6 +1361,13 @@ class API_EXPORT QueryExecutor : public QObject, public QRunnable */ int dataLengthLimit = -1; + /** + * @brief Optional filters to apply to the query. + * If not empty, it will be appended to the WHERE clause at the very end of execution chain, + * skipping complex result colum analysis, etc. + */ + QString filters; + /** * @brief Limit of queries, after which simple mode is used. * diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.cpp index a487623..d294b2c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.cpp @@ -1,7 +1,6 @@ #include "queryexecutoraddrowids.h" #include "parser/ast/sqliteselect.h" #include "selectresolver.h" -#include "common/utils_sql.h" #include "parser/ast/sqlitecreatetable.h" #include "schemaresolver.h" #include "common/compatibility.h" @@ -55,12 +54,14 @@ QHash> QueryExecutorAddRowIds::addR return rowIdColsMap; // Go trough subselects to add ROWID result columns there and collect rowId mapping to use here. - for (SqliteSelect* subSelect : getSubSelects(core)) + for (SqliteSelect*& subSelect : getSubSelects(core)) { unite(rowIdColsMap, addRowIdForTables(subSelect, ok, false)); if (!ok) return rowIdColsMap; + } + core->rebuildTokens(); // Getting all tables we need to get ROWID for SelectResolver resolver(db, select->tokens.detokenize(), context->dbNameToAttach); @@ -71,7 +72,11 @@ QHash> QueryExecutorAddRowIds::addR { if (table.flags & (SelectResolver::FROM_COMPOUND_SELECT | SelectResolver::FROM_DISTINCT_SELECT | SelectResolver::FROM_GROUPED_SELECT | SelectResolver::FROM_CTE_SELECT)) - continue; // we don't get ROWID from compound, distinct or aggregated subselects + continue; // we don't get ROWID from compound, distinct or aggregated subselects. + + // Tables from inside of view don't provide ROWID, if views were not expanded. + if (!context->viewsExpanded && table.flags & SelectResolver::FROM_VIEW) + continue; if (checkInWithClause(table, select->with)) continue; // we don't get ROWID from WITH clause, as it's likely to be recurrent and difficult. TODO: support columns from WITH clause @@ -94,7 +99,7 @@ QList QueryExecutorAddRowIds::getSubSelects(SqliteSelect::Core* c if (core->from->singleSource && core->from->singleSource->select) selects << core->from->singleSource->select; - for (SqliteSelect::Core::JoinSourceOther* otherSource : core->from->otherSources) + for (SqliteSelect::Core::JoinSourceOther*& otherSource : core->from->otherSources) { if (!otherSource->singleSource->select) continue; @@ -118,7 +123,7 @@ QHash QueryExecutorAddRowIds::getNextColNames(const SelectResol return colNames; } - if (createTable->withOutRowId.isNull()) + if (!createTable->withOutRowId) { // It's a regular ROWID table colNames[getNextColName()] = "ROWID"; @@ -142,7 +147,7 @@ QHash QueryExecutorAddRowIds::getNextColNames(const SelectResol SqliteCreateTable::Constraint* tableConstr = dynamic_cast(primaryKey); if (tableConstr) { - for (SqliteIndexedColumn* idxCol : tableConstr->indexedColumns) + for (SqliteIndexedColumn*& idxCol : tableConstr->indexedColumns) colNames[getNextColName()] = idxCol->name; return colNames; @@ -155,7 +160,11 @@ QHash QueryExecutorAddRowIds::getNextColNames(const SelectResol bool QueryExecutorAddRowIds::addResultColumns(SqliteSelect::Core* core, const SelectResolver::Table& table, QHash>& rowIdColsMap, bool isTopSelect) { - SelectResolver::Table keyTable = table; + SelectResolver::Table destilledTable = table; + if (destilledTable.database == "main" && destilledTable.originalDatabase.isNull()) + destilledTable.database = QString(); + + SelectResolver::Table keyTable = destilledTable; // If selecting from named subselect, where table in that subselect has no alias, we need to match // Table by table&database, but excluding alias. @@ -163,18 +172,20 @@ bool QueryExecutorAddRowIds::addResultColumns(SqliteSelect::Core* core, const Se { keyTable.tableAlias = QString(); if (!rowIdColsMap.contains(keyTable)) - keyTable = table; + { + keyTable = destilledTable; + } } // Aliased matching should be performed also against pushed (to old) aliases, due to multi-level subselects. if (!rowIdColsMap.contains(keyTable)) { - for (const SelectResolver::Table& rowIdColsMapTable : rowIdColsMap.keys()) + for (auto rowIdColsMapTable = rowIdColsMap.keyBegin(), end = rowIdColsMap.keyEnd(); rowIdColsMapTable != end; ++rowIdColsMapTable) { - if (!table.oldTableAliases.contains(rowIdColsMapTable.tableAlias, Qt::CaseInsensitive)) + if (!table.oldTableAliases.contains(rowIdColsMapTable->tableAlias, Qt::CaseInsensitive)) continue; - keyTable = rowIdColsMapTable; + keyTable = *rowIdColsMapTable; } } @@ -202,7 +213,7 @@ bool QueryExecutorAddRowIds::addResultColumns(SqliteSelect::Core* core, const Se while (it.hasNext()) { it.next(); - if (!addResultColumns(core, table, it.key(), it.value(), aliasOnlyAsSelectColumn)) + if (!addResultColumns(core, destilledTable, it.key(), it.value(), aliasOnlyAsSelectColumn)) return false; } @@ -228,7 +239,7 @@ bool QueryExecutorAddRowIds::checkInWithClause(const SelectResolver::Table &tabl SqliteWith::CommonTableExpression* cte = nullptr; QString nameToCompareWith = table.tableAlias.isNull() ? table.table : table.tableAlias; - for (SqliteWith::CommonTableExpression* cteItem : with->cteList) + for (SqliteWith::CommonTableExpression*& cteItem : with->cteList) { if (cteItem->table == nameToCompareWith) { diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.h b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.h index 1669542..bf4d263 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutoraddrowids.h @@ -2,7 +2,7 @@ #define QUERYEXECUTORADDROWIDS_H #include "queryexecutorstep.h" -#include "parser/token.h" +#include "selectresolver.h" /** * @brief Adds ROWID to result columns. diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.cpp deleted file mode 100644 index 352c74b..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "queryexecutorcellsize.h" -#include - -bool QueryExecutorCellSize::exec() -{ - if (queryExecutor->getDataLengthLimit() < 0) - return true; - - SqliteSelectPtr select = getSelect(); - if (!select || select->explain) - return true; - - for (SqliteSelect::Core* core : select->coreSelects) - { - if (!applyDataLimit(select.data(), core)) - return false; - } - - updateQueries(); - return true; -} - -bool QueryExecutorCellSize::applyDataLimit(SqliteSelect* select, SqliteSelect::Core* core) -{ - if (core->tokensMap["selcollist"].size() == 0) - { - qCritical() << "No 'selcollist' in Select::Core. Cannot apply cell size limits."; - return false; - } - - bool first = true; - TokenList tokens; - - for (const QueryExecutor::ResultColumnPtr& col : context->resultColumns) - { - if (!first) - tokens += getSeparatorTokens(); - - tokens += getLimitTokens(col); - first = false; - } - - for (const QueryExecutor::ResultRowIdColumnPtr& col : context->rowIdColumns) - { - if (!first) - tokens += getSeparatorTokens(); - - tokens += getNoLimitTokens(col); - first = false; - } - - // Wrapping original select with new select with limited columns - select->tokens = wrapSelect(select->tokens, tokens); - - return true; -} - -TokenList QueryExecutorCellSize::getLimitTokens(const QueryExecutor::ResultColumnPtr& resCol) -{ - // CASE WHEN typeof(alias) IN ('real', 'integer', 'numeric', 'null') THEN alias ELSE substr(alias, 1, limit) END - TokenList newTokens; - newTokens << TokenPtr::create(Token::KEYWORD, "CASE") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::KEYWORD, "WHEN") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::OTHER, "typeof") - << TokenPtr::create(Token::PAR_LEFT, "(") - << TokenPtr::create(Token::OTHER, resCol->queryExecutorAlias) - << TokenPtr::create(Token::PAR_RIGHT, ")") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::KEYWORD, "IN") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::PAR_LEFT, "(") - << TokenPtr::create(Token::STRING, "'real'") - << TokenPtr::create(Token::OPERATOR, ",") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::STRING, "'integer'") - << TokenPtr::create(Token::OPERATOR, ",") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::STRING, "'numeric'") - << TokenPtr::create(Token::OPERATOR, ",") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::STRING, "'null'") - << TokenPtr::create(Token::PAR_RIGHT, ")") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::KEYWORD, "THEN") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::OTHER, resCol->queryExecutorAlias) - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::KEYWORD, "ELSE") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::OTHER, "substr") - << TokenPtr::create(Token::PAR_LEFT, "(") - << TokenPtr::create(Token::OTHER, resCol->queryExecutorAlias) - << TokenPtr::create(Token::OPERATOR, ",") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::INTEGER, "1") - << TokenPtr::create(Token::OPERATOR, ",") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::INTEGER, QString::number(queryExecutor->getDataLengthLimit())) - << TokenPtr::create(Token::PAR_RIGHT, ")") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::KEYWORD, "END") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::KEYWORD, "AS") - << TokenPtr::create(Token::SPACE, " ") - << TokenPtr::create(Token::OTHER, resCol->queryExecutorAlias); - return newTokens; -} - -TokenList QueryExecutorCellSize::getNoLimitTokens(const QueryExecutor::ResultRowIdColumnPtr& resCol) -{ - TokenList newTokens; - bool first = true; - for (const QString& col : resCol->queryExecutorAliasToColumn.keys()) - { - if (!first) - newTokens += getSeparatorTokens(); - - newTokens << TokenPtr::create(Token::OTHER, col); - first = false; - } - return newTokens; -} - -TokenList QueryExecutorCellSize::getSeparatorTokens() -{ - TokenList newTokens; - newTokens << TokenPtr::create(Token::OPERATOR, ","); - newTokens << TokenPtr::create(Token::SPACE, " "); - return newTokens; -} diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.h b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.h deleted file mode 100644 index c174c69..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcellsize.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef QUERYEXECUTORCELLSIZE_H -#define QUERYEXECUTORCELLSIZE_H - -#include "queryexecutorstep.h" - -/** - * @brief Applies per-cell byte size limit to the query. - * - * Size of data extracted for each cell is limited in order to avoid huge memory use - * when the database contains column with like 500MB values per row and the query - * returns for example 100 rows. - * - * This is accomplished by wrapping all result columns (except ROWID columns) with substr() SQL function. - * - * SQLiteStudio limits each column to SqlQueryModel::cellDataLengthLimit when displaying - * data in SqlQueryView. - * - * This feature is disabled by default in QueryExecutor and has to be enabled by defining - * QueryExecutor::setDataLengthLimit(). - */ -class QueryExecutorCellSize : public QueryExecutorStep -{ - Q_OBJECT - - public: - bool exec(); - - private: - /** - * @brief Applies limit function to all result columns in given SELECT. - * @param select Select that we want to limit. - * @param core Select's core that we want to limit. - * @return true on success, false on failure. - * - * This method is called for each core in the \p select. - */ - bool applyDataLimit(SqliteSelect* select, SqliteSelect::Core* core); - - /** - * @brief Generates tokens that will return limited value of the result column. - * @param resCol Result column to wrap. - * @return List of tokens. - */ - TokenList getLimitTokens(const QueryExecutor::ResultColumnPtr& resCol); - - /** - * @brief Generates tokens that will return unlimited value of the ROWID result column. - * @param resCol ROWID result column. - * @return List of tokens. - */ - TokenList getNoLimitTokens(const QueryExecutor::ResultRowIdColumnPtr& resCol); - - /** - * @brief Generates tokens representing result columns separator. - * @return List of tokens. - * - * Result columns separator tokens are just a period and a space. - */ - TokenList getSeparatorTokens(); -}; - -#endif // QUERYEXECUTORCELLSIZE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp index 5ababcd..d3f2eea 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumns.cpp @@ -18,7 +18,7 @@ bool QueryExecutorColumns::exec() // Resolving result columns of the select SelectResolver resolver(db, queryExecutor->getOriginalQuery(), context->dbNameToAttach); resolver.resolveMultiCore = true; - QList columns = resolver.resolve(select.data()).first(); + QList columns = resolver.resolve(select->coreSelects.first()); if (resolver.hasErrors()) { @@ -34,22 +34,21 @@ bool QueryExecutorColumns::exec() // Deleting old result columns and defining new ones SqliteSelect::Core* core = select->coreSelects.first(); - for (SqliteSelect::Core::ResultColumn* resCol : core->resultColumns) + for (SqliteSelect::Core::ResultColumn*& resCol : core->resultColumns) delete resCol; core->resultColumns.clear(); // Count total rowId columns - for (const QueryExecutor::ResultRowIdColumnPtr& rowIdCol : context->rowIdColumns) + for (QueryExecutor::ResultRowIdColumnPtr& rowIdCol : context->rowIdColumns) rowIdColNames += rowIdCol->queryExecutorAliasToColumn.keys(); // Defining result columns QueryExecutor::ResultColumnPtr resultColumn; SqliteSelect::Core::ResultColumn* resultColumnForSelect = nullptr; bool rowIdColumn = false; - int i = 0; QSet usedAliases; - for (const SelectResolver::Column& col : columns) + for (SelectResolver::Column& col : columns) { // Convert column to QueryExecutor result column resultColumn = getResultColumn(col); @@ -68,8 +67,6 @@ bool QueryExecutorColumns::exec() if (!rowIdColumn) context->resultColumns << resultColumn; // store it in context for later usage by any step - - i++; } // qDebug() << "before: " << context->processedQuery; @@ -111,6 +108,9 @@ QueryExecutor::ResultColumnPtr QueryExecutorColumns::getResultColumn(const Selec if (resolvedColumn.flags & SelectResolver::FROM_CTE_SELECT) resultColumn->editionForbiddenReasons << QueryExecutor::ColumnEditionForbiddenReason::COMM_TAB_EXPR; + if (resolvedColumn.flags & SelectResolver::FROM_VIEW) + resultColumn->editionForbiddenReasons << QueryExecutor::ColumnEditionForbiddenReason::VIEW_NOT_EXPANDED; + resultColumn->database = resolvedColumn.originalDatabase; resultColumn->table = resolvedColumn.table; resultColumn->column = resolvedColumn.column; @@ -120,13 +120,10 @@ QueryExecutor::ResultColumnPtr QueryExecutorColumns::getResultColumn(const Selec } if (isRowIdColumnAlias(resultColumn->alias)) - { resultColumn->queryExecutorAlias = resultColumn->alias; - } else - { resultColumn->queryExecutorAlias = getNextColName(); - } + return resultColumn; } @@ -135,10 +132,10 @@ SqliteSelect::Core::ResultColumn* QueryExecutorColumns::getResultColumnForSelect SqliteSelect::Core::ResultColumn* selectResultColumn = new SqliteSelect::Core::ResultColumn(); QString colString = resultColumn->column; - if (col.aliasDefinedInSubQuery) // #2931 - colString = col.alias; + if (col.aliasDefinedInSubQuery) // #2819 (id from old tracker was 2931) + colString = wrapObjIfNeeded(col.alias); - if (!resultColumn->expression) + if (!resultColumn->expression && !col.aliasDefinedInSubQuery) // if alias defined in subquery, it's already wrapped colString = wrapObjIfNeeded(colString); Parser parser; @@ -149,6 +146,7 @@ SqliteSelect::Core::ResultColumn* QueryExecutorColumns::getResultColumnForSelect if (parser.getErrors().size() > 0) qWarning() << "The error was:" << parser.getErrors().first()->getFrom() << ":" << parser.getErrors().first()->getMessage(); + delete selectResultColumn; return nullptr; } @@ -189,7 +187,6 @@ SqliteSelect::Core::ResultColumn* QueryExecutorColumns::getResultColumnForSelect selectResultColumn->alias = aliasTpl.arg(aliasBase, QString::number(nextAliasCounter++)); usedAliases += selectResultColumn->alias; - selectResultColumn->alias = wrapObjIfNeeded(selectResultColumn->alias); return selectResultColumn; } @@ -204,7 +201,7 @@ QString QueryExecutorColumns::resolveAttachedDatabases(const QString &dbName) bool QueryExecutorColumns::isRowIdColumnAlias(const QString& alias) { - for (QueryExecutor::ResultRowIdColumnPtr rowIdColumn : context->rowIdColumns) + for (QueryExecutor::ResultRowIdColumnPtr& rowIdColumn : context->rowIdColumns) { if (rowIdColumn->queryExecutorAliasToColumn.keys().contains(alias)) return true; @@ -224,7 +221,7 @@ void QueryExecutorColumns::wrapWithAliasedColumns(SqliteSelect* select) QString baseColName; QString colName; static_qstring(colNameTpl, "%1:%2"); - for (const QueryExecutor::ResultColumnPtr& resCol : context->resultColumns) + for (QueryExecutor::ResultColumnPtr& resCol : context->resultColumns) { if (!first) outerColumns += sepTokens; @@ -253,9 +250,9 @@ void QueryExecutorColumns::wrapWithAliasedColumns(SqliteSelect* select) first = false; } - for (const QueryExecutor::ResultRowIdColumnPtr& rowIdColumn : context->rowIdColumns) + for (QueryExecutor::ResultRowIdColumnPtr& rowIdColumn : context->rowIdColumns) { - for (const QString& alias : rowIdColumn->queryExecutorAliasToColumn.keys()) + for (QString& alias : rowIdColumn->queryExecutorAliasToColumn.keys()) { if (!first) outerColumns += sepTokens; diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumntype.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumntype.cpp index fb788f4..d0f9387 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumntype.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumntype.cpp @@ -1,4 +1,7 @@ #include "queryexecutorcolumntype.h" +#include "parser/parser.h" +#include +#include bool QueryExecutorColumnType::exec() { @@ -9,7 +12,22 @@ bool QueryExecutorColumnType::exec() if (!select || select->explain) return true; - addTypeColumns(select.data()); + static_qstring(selectTpl, "SELECT *, %1 FROM (%2)"); + + QStringList columns = addTypeColumns(); + QString newSelect = selectTpl.arg(columns.join(", "), select->detokenize()); + + Parser parser; + if (!parser.parse(newSelect) || parser.getQueries().size() == 0) + { + qWarning() << "Could not parse SELECT after applying typeof(). Tried to parse query:\n" << newSelect; + return false; + } + + context->parsedQueries.removeLast(); + context->parsedQueries << parser.getQueries().first(); + + updateQueries(); select->rebuildTokens(); updateQueries(); @@ -17,31 +35,16 @@ bool QueryExecutorColumnType::exec() return true; } -void QueryExecutorColumnType::addTypeColumns(SqliteSelect* select) +QStringList QueryExecutorColumnType::addTypeColumns() { - for (const QueryExecutor::ResultColumnPtr& resCol : context->resultColumns) + static_qstring(typeOfColTpl, "typeof(%1) AS %2"); + QStringList typeColumns; + for (QueryExecutor::ResultColumnPtr& resCol : context->resultColumns) { QString nextCol = getNextColName(); QString targetCol = resCol->queryExecutorAlias; - - for (SqliteSelect::Core* core : select->coreSelects) - { - SqliteSelect::Core::ResultColumn* realResCol = createRealTypeOfResCol(targetCol, nextCol); - core->resultColumns << realResCol; - realResCol->setParent(core); - } - + typeColumns << typeOfColTpl.arg(targetCol, nextCol); context->typeColumnToResultColumnAlias[nextCol] = targetCol; } -} - -SqliteSelect::Core::ResultColumn* QueryExecutorColumnType::createRealTypeOfResCol(const QString& targetCol, const QString& alias) -{ - SqliteExpr* targetColExpr = new SqliteExpr(); - targetColExpr->initId(targetCol); - - SqliteExpr* expr = new SqliteExpr(); - expr->initFunction("typeof", false, {targetColExpr}); - - return new SqliteSelect::Core::ResultColumn(expr, true, alias); + return typeColumns; } diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumntype.h b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumntype.h index f492313..92c6324 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumntype.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorcolumntype.h @@ -2,7 +2,7 @@ #define QUERYEXECUTORCOLUMNTYPE_H #include "queryexecutorstep.h" -#include "parser/ast/sqliteselect.h" +//#include "parser/ast/sqliteselect.h" class QueryExecutorColumnType : public QueryExecutorStep { @@ -12,8 +12,8 @@ class QueryExecutorColumnType : public QueryExecutorStep bool exec(); private: - void addTypeColumns(SqliteSelect* select); - SqliteSelect::Core::ResultColumn* createRealTypeOfResCol(const QString& targetCol, const QString& alias); + QStringList addTypeColumns(); +// SqliteSelect::Core::ResultColumn* createRealTypeOfResCol(const QString& targetCol, const QString& alias); }; #endif // QUERYEXECUTORCOLUMNTYPE_H diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordatasources.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordatasources.cpp index be7a873..9ff8026 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordatasources.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutordatasources.cpp @@ -1,6 +1,7 @@ #include "queryexecutordatasources.h" #include "parser/ast/sqliteselect.h" #include "selectresolver.h" +#include bool QueryExecutorDataSources::exec() { @@ -19,7 +20,7 @@ bool QueryExecutorDataSources::exec() SqliteSelect::Core* core = select->coreSelects.first(); QSet tables = resolver.resolveTables(core); - for (SelectResolver::Table resolvedTable : tables) + for (const SelectResolver::Table& resolvedTable : tables) { if (resolvedTable.flags & SelectResolver::FROM_CTE_SELECT) continue; diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorfilter.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorfilter.cpp new file mode 100644 index 0000000..bcc9ce4 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorfilter.cpp @@ -0,0 +1,26 @@ +#include "queryexecutorfilter.h" +#include + +bool QueryExecutorFilter::exec() +{ +// qDebug() << "filters:" << queryExecutor->getFilters(); +// qDebug() << "q1:" << context->processedQuery; + if (queryExecutor->getFilters().trimmed().isEmpty()) + return true; + + SqliteSelectPtr select = getSelect(); + if (!select || select->explain) + return true; + + if (select->tokens.size() < 1) + return true; // shouldn't happen, but if happens, quit gracefully + + static_qstring(selectTpl, "SELECT * FROM (%1) WHERE %2"); + QString newSelect = selectTpl.arg(select->detokenize(), queryExecutor->getFilters()); + + int begin = select->tokens.first()->start; + int length = select->tokens.last()->end - select->tokens.first()->start + 1; + context->processedQuery = context->processedQuery.replace(begin, length, newSelect); +// qDebug() << "q2:" << context->processedQuery; + return true; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorfilter.h b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorfilter.h new file mode 100644 index 0000000..0bb8459 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorfilter.h @@ -0,0 +1,20 @@ +#ifndef QUERYEXECUTORFILTER_H +#define QUERYEXECUTORFILTER_H + +#include "queryexecutorstep.h" + +/** + * @brief Applies WHERE filtering to the query. + * + * This step is executed late in the execution chain. It is useful, when one wants to apply filtering + * without involving whole column/rowid analysis that is done in earlier executor steps. + */ +class QueryExecutorFilter : public QueryExecutorStep +{ + Q_OBJECT + + public: + bool exec(); +}; + +#endif // QUERYEXECUTORFILTER_H diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorlimit.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorlimit.cpp index af1d7a6..afa1297 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorlimit.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorlimit.cpp @@ -1,5 +1,4 @@ #include "queryexecutorlimit.h" -#include "parser/ast/sqlitelimit.h" #include bool QueryExecutorLimit::exec() diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.cpp index 8195322..b6db4b2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.cpp @@ -1,5 +1,6 @@ #include "queryexecutorreplaceviews.h" #include "parser/ast/sqlitecreateview.h" +#include "parser/ast/sqliteselect.h" #include "schemaresolver.h" #include @@ -68,39 +69,20 @@ SqliteCreateViewPtr QueryExecutorReplaceViews::getView(const QString& database, void QueryExecutorReplaceViews::replaceViews(SqliteSelect* select) { SqliteSelect::Core* core = select->coreSelects.first(); - - QStringList viewsInDatabase; - SqliteCreateViewPtr view; - QList sources = core->getAllTypedStatements(); - QList viewSources; - QSet parents; + typedef QPair SourceViewPair; + SqliteCreateViewPtr view; + QList sourceViewPairs; for (SqliteSelect::Core::SingleSource* src : sources) { if (src->table.isNull()) continue; - viewsInDatabase = getViews(src->database); + QStringList viewsInDatabase = getViews(src->database); if (!viewsInDatabase.contains(src->table, Qt::CaseInsensitive)) continue; - parents << src->parentStatement(); - viewSources << src; - } - - if (parents.size() > 1) - { - // Multi-level views (view selecting from view, selecting from view...). - // Such constructs build up easily to huge, non-optimized queries. - // For performance reasons, we won't expand such views. - qDebug() << "Multi-level views. Skipping view expanding feature of query executor. Some columns won't be editable due to that. Number of different view parents:" - << parents.size(); - return; - } - - for (SqliteSelect::Core::SingleSource* src : viewSources) - { view = getView(src->database, src->table); if (!view) { @@ -109,15 +91,50 @@ void QueryExecutorReplaceViews::replaceViews(SqliteSelect* select) continue; } - QString alias = src->alias.isNull() ? view->view : src->alias; + if (usesAnyView(view->select, viewsInDatabase)) + { + // Multi-level views (view selecting from view, selecting from view...). + // Such constructs build up easily to huge, non-optimized queries. + // For performance reasons, we won't expand such views. + qDebug() << "Multi-level views. Skipping view expanding feature of query executor. Some columns won't be editable due to that."; + return; + } + + sourceViewPairs << SourceViewPair(src, view); + } + + for (SourceViewPair& pair : sourceViewPairs) + { + view = pair.second; + + QString alias = pair.first->alias.isNull() ? view->view : pair.first->alias; - src->select = view->select; - src->alias = alias; - src->database = QString(); - src->table = QString(); + pair.first->select = view->select; + pair.first->alias = alias; + pair.first->database = QString(); + pair.first->table = QString(); - replaceViews(src->select); + // replaceViews(pair.first->select); // No recursion, as we avoid multi-level expanding. + } + + context->viewsExpanded = true; +} + +bool QueryExecutorReplaceViews::usesAnyView(SqliteSelect* select, const QStringList& viewsInDatabase) +{ + for (SqliteSelect::Core*& core : select->coreSelects) + { + QList sources = core->getAllTypedStatements(); + for (SqliteSelect::Core::SingleSource* src : sources) + { + if (src->table.isNull()) + continue; + + if (viewsInDatabase.contains(src->table, Qt::CaseInsensitive)) + return true; + } } + return false; } uint qHash(const QueryExecutorReplaceViews::View& view) diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.h b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.h index 633b0bc..345934d 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorreplaceviews.h @@ -4,6 +4,8 @@ #include "queryexecutorstep.h" #include "parser/ast/sqlitecreateview.h" +class SchemaResolver; + /** * @brief Replaces all references to views in query with SELECTs from those views. * @@ -85,6 +87,14 @@ class QueryExecutorReplaceViews : public QueryExecutorStep */ void replaceViews(SqliteSelect* select); + /** + * @brief Tells whether particular SELECT statement has any View as a data source. + * @param select Parsed SELECT statement. + * @param viewsInDatabase Prepared list of views existing in the database. + * @return true if the SELECT uses at least one existing View. + */ + bool usesAnyView(SqliteSelect* select, const QStringList& viewsInDatabase); + /** * @brief Used for caching view list per database. */ diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorstep.cpp b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorstep.cpp index 72dbeec..26b137a 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorstep.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorstep.cpp @@ -1,6 +1,5 @@ #include "queryexecutorstep.h" #include "db/queryexecutor.h" -#include "common/unused.h" QueryExecutorStep::~QueryExecutorStep() { @@ -17,7 +16,7 @@ void QueryExecutorStep::init(QueryExecutor *queryExecutor, QueryExecutor::Contex void QueryExecutorStep::updateQueries() { QString newQuery; - for (SqliteQueryPtr query : context->parsedQueries) + for (SqliteQueryPtr& query : context->parsedQueries) { newQuery += query->detokenize(); newQuery += "\n"; diff --git a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorstep.h b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorstep.h index fcc0cf6..cc89212 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorstep.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/queryexecutorsteps/queryexecutorstep.h @@ -1,7 +1,6 @@ #ifndef QUERYEXECUTORSTEP_H #define QUERYEXECUTORSTEP_H -#include "db/sqlquery.h" #include "parser/ast/sqliteselect.h" #include "db/queryexecutor.h" #include diff --git a/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.cpp b/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.cpp index bef3ed8..0880344 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.cpp @@ -1,6 +1,7 @@ #include "sqlquery.h" #include "db/sqlerrorcodes.h" #include "common/utils_sql.h" +#include "common/unused.h" SqlQuery::~SqlQuery() { @@ -141,3 +142,83 @@ QString RowIdConditionBuilder::build() { return conditions.join(" AND "); } + +/********************** SqlQueryError ************************/ + +class API_EXPORT SqlQueryError : public SqlQuery +{ + public: + SqlQueryError(const QString& errorText, int errorCode); + virtual ~SqlQueryError(); + + QString getErrorText(); + int getErrorCode(); + QStringList getColumnNames(); + int columnCount(); + + protected: + SqlResultsRowPtr nextInternal(); + bool hasNextInternal(); + bool execInternal(const QList& args); + bool execInternal(const QHash& args); + + private: + QString errorText; + int errorCode = 0; +}; + +SqlQueryPtr SqlQuery::error(const QString& errorText, int errorCode) +{ + return SqlQueryPtr(new SqlQueryError(errorText, errorCode)); +} + +SqlQueryError::SqlQueryError(const QString& errorText, int errorCode) : + errorText(errorText), errorCode(errorCode) +{ +} + +SqlQueryError::~SqlQueryError() +{ +} + +QString SqlQueryError::getErrorText() +{ + return errorText; +} + +int SqlQueryError::getErrorCode() +{ + return errorCode; +} + +QStringList SqlQueryError::getColumnNames() +{ + return QStringList(); +} + +int SqlQueryError::columnCount() +{ + return 0; +} + +SqlResultsRowPtr SqlQueryError::nextInternal() +{ + return SqlResultsRowPtr(); +} + +bool SqlQueryError::hasNextInternal() +{ + return false; +} + +bool SqlQueryError::execInternal(const QList& args) +{ + UNUSED(args); + return false; +} + +bool SqlQueryError::execInternal(const QHash& args) +{ + UNUSED(args); + return false; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.h b/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.h index 17bb464..1eea25c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/sqlquery.h @@ -65,6 +65,14 @@ typedef QHash RowId; class API_EXPORT SqlQuery { public: + /** + * @brief Produces empty, erronous result. + * @param errorText Error message returned with #getErrorText() of the returned object. + * @param errorCode Error code returned with #getErrorText() of the returned object. + * @return SqlQuery object shared pointer with no results, but with error details populated. + */ + static SqlQueryPtr error(const QString& errorText, int errorCode); + /** * @brief Releases result resources. */ diff --git a/SQLiteStudio3/coreSQLiteStudio/db/stdsqlite3driver.h b/SQLiteStudio3/coreSQLiteStudio/db/stdsqlite3driver.h index 01894c2..c8a3ba3 100644 --- a/SQLiteStudio3/coreSQLiteStudio/db/stdsqlite3driver.h +++ b/SQLiteStudio3/coreSQLiteStudio/db/stdsqlite3driver.h @@ -11,6 +11,7 @@ static const int OPEN_READWRITE = UppercasePrefix##SQLITE_OPEN_READWRITE; \ static const int OPEN_CREATE = UppercasePrefix##SQLITE_OPEN_CREATE; \ static const int UTF8 = UppercasePrefix##SQLITE_UTF8; \ + static const int DETERMINISTIC = UppercasePrefix##SQLITE_DETERMINISTIC; \ static const int INTEGER = UppercasePrefix##SQLITE_INTEGER; \ static const int FLOAT = UppercasePrefix##SQLITE_FLOAT; \ static const int NULL_TYPE = UppercasePrefix##SQLITE_NULL; \ @@ -19,6 +20,10 @@ static const int BUSY = UppercasePrefix##SQLITE_BUSY; \ static const int ROW = UppercasePrefix##SQLITE_ROW; \ static const int DONE = UppercasePrefix##SQLITE_DONE; \ + static const int CHECKPOINT_PASSIVE = UppercasePrefix##SQLITE_CHECKPOINT_PASSIVE; \ + static const int CHECKPOINT_FULL = UppercasePrefix##SQLITE_CHECKPOINT_FULL; \ + static const int CHECKPOINT_RESTART = UppercasePrefix##SQLITE_CHECKPOINT_RESTART; \ + static const int CHECKPOINT_TRUNCATE = UppercasePrefix##SQLITE_CHECKPOINT_TRUNCATE; \ \ typedef Prefix##sqlite3 handle; \ typedef Prefix##sqlite3_stmt stmt; \ @@ -74,6 +79,8 @@ static int reset(stmt* arg) {return Prefix##sqlite3_reset(arg);} \ static int close(handle* arg) {return Prefix##sqlite3_close(arg);} \ static void free(void* arg) {return Prefix##sqlite3_free(arg);} \ + static int wal_checkpoint(handle* arg1, const char* arg2) {return Prefix##sqlite3_wal_checkpoint(arg1, arg2);} \ + static int wal_checkpoint_v2(handle* a1, const char* a2, int a3, int* a4, int* a5) {return Prefix##sqlite3_wal_checkpoint_v2(a1, a2, a3, a4, a5);} \ static int enable_load_extension(handle* arg1, int arg2) {return Prefix##sqlite3_enable_load_extension(arg1, arg2);} \ static int load_extension(handle *arg1, const char *arg2, const char *arg3, char **arg4) {return Prefix##sqlite3_load_extension(arg1, arg2, arg3, arg4);} \ static void* user_data(context* arg) {return Prefix##sqlite3_user_data(arg);} \ diff --git a/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.cpp b/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.cpp index 99110a0..1c206f9 100644 --- a/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.cpp @@ -1,7 +1,6 @@ #include "dbobjectorganizer.h" #include "db/db.h" #include "common/utils_sql.h" -#include "datatype.h" #include "services/notifymanager.h" #include "db/attachguard.h" #include "common/compatibility.h" @@ -198,7 +197,7 @@ bool DbObjectOrganizer::processAll() // Attaching target db if needed AttachGuard attach; - if (!(referencedTables + srcTables).isEmpty()) + if (srcDb->getTypeClassName() == dstDb->getTypeClassName() && !(referencedTables + srcTables).isEmpty()) { attach = srcDb->guardedAttach(dstDb, true); attachName = attach->getName(); @@ -431,12 +430,16 @@ bool DbObjectOrganizer::copyTableToDb(const QString& table) bool DbObjectOrganizer::copyDataAsMiddleware(const QString& table) { - QStringList srcColumns = srcResolver->getTableColumns(srcTable); + static_qstring(selectTpl, "SELECT %1 FROM %2"); + static_qstring(insertTpl, "INSERT INTO %1 (%2) VALUES (%3)"); + + QStringList srcColumns = srcResolver->getTableColumns(srcTable, true); + QString srcColumnsStr = srcColumns.join(", "); QString wrappedSrcTable = wrapObjIfNeeded(srcTable); - SqlQueryPtr results = srcDb->prepare("SELECT * FROM " + wrappedSrcTable); + SqlQueryPtr results = srcDb->prepare(selectTpl.arg(srcColumnsStr, wrappedSrcTable)); if (!results->execute()) { - notifyError(tr("Error while copying data for table %1: %2").arg(table).arg(results->getErrorText())); + notifyError(tr("Error while copying data for table %1: %2").arg(table, results->getErrorText())); return false; } @@ -445,7 +448,7 @@ bool DbObjectOrganizer::copyDataAsMiddleware(const QString& table) argPlaceholderList << "?"; QString wrappedDstTable = wrapObjIfNeeded(table); - QString sql = "INSERT INTO " + wrappedDstTable + " VALUES (" + argPlaceholderList.join(", ") + ")"; + QString sql = insertTpl.arg(wrappedDstTable, srcColumnsStr, argPlaceholderList.join(", ")); SqlQueryPtr insertQuery = dstDb->prepare(sql); SqlResultsRowPtr row; @@ -455,14 +458,14 @@ bool DbObjectOrganizer::copyDataAsMiddleware(const QString& table) row = results->next(); if (!row) { - notifyError(tr("Error while copying data to table %1: %2").arg(table).arg(results->getErrorText())); + notifyError(tr("Error while copying data to table %1: %2").arg(table, results->getErrorText())); return false; } insertQuery->setArgs(row->valueList()); if (!insertQuery->execute()) { - notifyError(tr("Error while copying data to table %1: %2").arg(table).arg(insertQuery->getErrorText())); + notifyError(tr("Error while copying data to table %1: %2").arg(table, insertQuery->getErrorText())); return false; } @@ -480,12 +483,16 @@ bool DbObjectOrganizer::copyDataAsMiddleware(const QString& table) bool DbObjectOrganizer::copyDataUsingAttach(const QString& table) { + static_qstring(insertTpl, "INSERT INTO %1.%2 (%3) SELECT %3 FROM %4"); + QString wrappedSrcTable = wrapObjIfNeeded(srcTable); QString wrappedDstTable = wrapObjIfNeeded(table); - SqlQueryPtr results = srcDb->exec("INSERT INTO " + attachName + "." + wrappedDstTable + " SELECT * FROM " + wrappedSrcTable); + QStringList srcColumns = srcResolver->getTableColumns(srcTable, true); + QString srcColumnsStr = srcColumns.join(", "); + SqlQueryPtr results = srcDb->exec(insertTpl.arg(attachName, wrappedDstTable, srcColumnsStr, wrappedSrcTable)); if (results->isError()) { - notifyError(tr("Error while copying data to table %1: %2").arg(table).arg(results->getErrorText())); + notifyError(tr("Error while copying data to table %1: %2").arg(table, results->getErrorText())); return false; } return true; @@ -508,46 +515,36 @@ void DbObjectOrganizer::dropObject(const QString& name, const QString& type) if (results->isError()) { notifyWarn(tr("Error while dropping source view %1: %2\nTables, indexes, triggers and views copied to database %3 will remain.") - .arg(name).arg(results->getErrorText()).arg(dstDb->getName())); + .arg(name, results->getErrorText(), dstDb->getName())); } } bool DbObjectOrganizer::copyViewToDb(const QString& view) { - return copySimpleObjectToDb(view, tr("Error while creating view in target database: %1")); + return copySimpleObjectToDb(view, tr("Error while creating view in target database: %1"), SchemaResolver::VIEW); } bool DbObjectOrganizer::copyIndexToDb(const QString& index) { - return copySimpleObjectToDb(index, tr("Error while creating index in target database: %1")); + return copySimpleObjectToDb(index, tr("Error while creating index in target database: %1"),SchemaResolver::INDEX); } bool DbObjectOrganizer::copyTriggerToDb(const QString& trigger) { - return copySimpleObjectToDb(trigger, tr("Error while creating trigger in target database: %1")); + return copySimpleObjectToDb(trigger, tr("Error while creating trigger in target database: %1"), SchemaResolver::TRIGGER); } -bool DbObjectOrganizer::copySimpleObjectToDb(const QString& name, const QString& errorMessage) +bool DbObjectOrganizer::copySimpleObjectToDb(const QString& name, const QString& errorMessage, SchemaResolver::ObjectType objectType) { - QString ddl = srcResolver->getObjectDdl(name, SchemaResolver::ANY); + QString ddl = srcResolver->getObjectDdl(name, objectType); if (ddl.trimmed() == ";") // empty query, result of ignored errors in UI return true; - SqlQueryPtr result; - - if (!attachName.isNull()) - { - ddl = prefixSimpleObjectWithAttachName(name, ddl); - if (ddl.isNull()) - return false; - - result = srcDb->exec(ddl); - } - else - { - result = dstDb->exec(ddl); - } + ddl = processSimpleObjectAttachNameAndRename(name, ddl); + if (ddl.isNull()) + return false; + SqlQueryPtr result = srcDb->exec(ddl); if (result->isError()) { notifyError(errorMessage.arg(result->getErrorText())); @@ -572,7 +569,7 @@ void DbObjectOrganizer::collectReferencedTables(const QString& table, const StrH { QList parsedTables; SqliteCreateTablePtr parsedTable; - for (SqliteQueryPtr query : allParsedObjects.values()) + for (SqliteQueryPtr& query : allParsedObjects.values()) { parsedTable = query.dynamicCast(); if (parsedTable) @@ -670,8 +667,11 @@ bool DbObjectOrganizer::execConfirmFunctionInMainThread(const QStringList& table return res; } -QString DbObjectOrganizer::prefixSimpleObjectWithAttachName(const QString& objName, const QString& ddl) +QString DbObjectOrganizer::processSimpleObjectAttachNameAndRename(const QString& objName, const QString& ddl) { + if (attachName.isNull() && !renamed.contains(objName)) + return ddl; + Parser parser; if (!parser.parse(ddl)) { @@ -696,7 +696,12 @@ QString DbObjectOrganizer::prefixSimpleObjectWithAttachName(const QString& objNa return QString(); } - ddlWithDb->setTargetDatabase(attachName); + if (!attachName.isNull()) + ddlWithDb->setTargetDatabase(attachName); + + if (renamed.contains(objName)) + ddlWithDb->setObjectName(renamed[objName]); + query->rebuildTokens(); return query->tokens.detokenize(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.h b/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.h index c8a1f97..14a4b46 100644 --- a/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.h +++ b/SQLiteStudio3/coreSQLiteStudio/dbobjectorganizer.h @@ -84,7 +84,7 @@ class API_EXPORT DbObjectOrganizer : public QObject, public QRunnable, public In bool copyViewToDb(const QString& view); bool copyIndexToDb(const QString& index); bool copyTriggerToDb(const QString& trigger); - bool copySimpleObjectToDb(const QString& name, const QString& errorMessage); + bool copySimpleObjectToDb(const QString& name, const QString& errorMessage, SchemaResolver::ObjectType objectType); QSet resolveReferencedTables(const QString& table, const QList& parsedTables); void collectReferencedTables(const QString& table, const StrHash& allParsedObjects); void collectReferencedIndexes(const QString& table); @@ -105,7 +105,7 @@ class API_EXPORT DbObjectOrganizer : public QObject, public QRunnable, public In bool rollback(); void emitFinished(bool success); bool execConfirmFunctionInMainThread(const QStringList& tables); - QString prefixSimpleObjectWithAttachName(const QString& objName, const QString& ddl); + QString processSimpleObjectAttachNameAndRename(const QString& objName, const QString& ddl); ReferencedTablesConfimFunction confirmFunction; NameConflictResolveFunction nameConflictResolveFunction; diff --git a/SQLiteStudio3/coreSQLiteStudio/expectedtoken.cpp b/SQLiteStudio3/coreSQLiteStudio/expectedtoken.cpp index a02f8c9..d6c6f4b 100644 --- a/SQLiteStudio3/coreSQLiteStudio/expectedtoken.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/expectedtoken.cpp @@ -64,7 +64,7 @@ int operator==(const ExpectedTokenPtr& ptr1, const ExpectedTokenPtr& ptr2) int qHash(const ExpectedToken& token) { - return token.type ^ qHash(token.value + "/" + token.value + "/" + token.contextInfo + "/" + token.label + "/" + token.prefix); + return token.type ^ qHash(token.value + "/" + token.contextInfo + "/" + token.label + "/" + token.prefix); } int qHash(const ExpectedTokenPtr& ptr) diff --git a/SQLiteStudio3/coreSQLiteStudio/importworker.cpp b/SQLiteStudio3/coreSQLiteStudio/importworker.cpp index 19cbd07..1a949a9 100644 --- a/SQLiteStudio3/coreSQLiteStudio/importworker.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/importworker.cpp @@ -140,14 +140,17 @@ bool ImportWorker::prepareTable() bool ImportWorker::importData(int& rowCount) { - static const QString insertTemplate = QStringLiteral("INSERT INTO %1 VALUES (%2)"); + static const QString insertTemplate = QStringLiteral("INSERT INTO %1 (%2) VALUES (%3)"); int colCount = targetColumns.size(); QStringList valList; for (int i = 0; i < colCount; i++) valList << "?"; - QString theInsert = insertTemplate.arg(wrapObjIfNeeded(table), valList.join(", ")); + QString theInsert = insertTemplate.arg(wrapObjIfNeeded(table), + targetColumns.join(", "), + valList.join(", ")); + SqlQueryPtr query = db->prepare(theInsert); query->setFlags(Db::Flag::SKIP_DROP_DETECTION|Db::Flag::SKIP_PARAM_COUNTING|Db::Flag::NO_LOCK); diff --git a/SQLiteStudio3/coreSQLiteStudio/licenses/icu.txt b/SQLiteStudio3/coreSQLiteStudio/licenses/icu.txt new file mode 100644 index 0000000..22472dc --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/licenses/icu.txt @@ -0,0 +1,519 @@ +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +See Terms of Use +for definitions of Unicode Inc.’s Data Files and Software. + +NOTICE TO USER: Carefully read the following legal agreement. +BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S +DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), +YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. +IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE +THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2023 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in https://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. + +---------------------------------------------------------------------- + +Third-Party Software Licenses + +This section contains third-party software notices and/or additional +terms for licensed third-party software components included within ICU +libraries. + +---------------------------------------------------------------------- + +ICU License - ICU 1.8.1 to ICU 57.1 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1995-2016 International Business Machines Corporation and others +All rights reserved. + +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, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, provided that the above +copyright notice(s) and this permission notice appear in all copies of +the Software and that both the above copyright notice(s) and this +permission notice appear in supporting documentation. + +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 +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY +SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +of the copyright holder. + +All trademarks and registered trademarks mentioned herein are the +property of their respective owners. + +---------------------------------------------------------------------- + +Chinese/Japanese Word Break Dictionary Data (cjdict.txt) + + # The Google Chrome software developed by Google is licensed under + # the BSD license. Other software included in this distribution is + # provided under other licenses, as set forth below. + # + # The BSD License + # http://opensource.org/licenses/bsd-license.php + # Copyright (C) 2006-2008, Google Inc. + # + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # Redistributions in binary form must reproduce the above + # copyright notice, this list of conditions and the following + # disclaimer in the documentation and/or other materials provided with + # the distribution. + # Neither the name of Google Inc. nor the names of its + # contributors may be used to endorse or promote products derived from + # this software without specific prior written permission. + # + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # + # + # The word list in cjdict.txt are generated by combining three word lists + # listed below with further processing for compound word breaking. The + # frequency is generated with an iterative training against Google web + # corpora. + # + # * Libtabe (Chinese) + # - https://sourceforge.net/project/?group_id=1519 + # - Its license terms and conditions are shown below. + # + # * IPADIC (Japanese) + # - http://chasen.aist-nara.ac.jp/chasen/distribution.html + # - Its license terms and conditions are shown below. + # + # ---------COPYING.libtabe ---- BEGIN-------------------- + # + # /* + # * Copyright (c) 1999 TaBE Project. + # * Copyright (c) 1999 Pai-Hsiang Hsiao. + # * All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . Redistributions in binary form must reproduce the above copyright + # * notice, this list of conditions and the following disclaimer in + # * the documentation and/or other materials provided with the + # * distribution. + # * . Neither the name of the TaBE Project nor the names of its + # * contributors may be used to endorse or promote products derived + # * from this software without specific prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # * OF THE POSSIBILITY OF SUCH DAMAGE. + # */ + # + # /* + # * Copyright (c) 1999 Computer Systems and Communication Lab, + # * Institute of Information Science, Academia + # * Sinica. All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . Redistributions in binary form must reproduce the above copyright + # * notice, this list of conditions and the following disclaimer in + # * the documentation and/or other materials provided with the + # * distribution. + # * . Neither the name of the Computer Systems and Communication Lab + # * nor the names of its contributors may be used to endorse or + # * promote products derived from this software without specific + # * prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # * OF THE POSSIBILITY OF SUCH DAMAGE. + # */ + # + # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, + # University of Illinois + # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 + # + # ---------------COPYING.libtabe-----END-------------------------------- + # + # + # ---------------COPYING.ipadic-----BEGIN------------------------------- + # + # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science + # and Technology. All Rights Reserved. + # + # Use, reproduction, and distribution of this software is permitted. + # Any copy of this software, whether in its original form or modified, + # must include both the above copyright notice and the following + # paragraphs. + # + # Nara Institute of Science and Technology (NAIST), + # the copyright holders, disclaims all warranties with regard to this + # software, including all implied warranties of merchantability and + # fitness, in no event shall NAIST be liable for + # any special, indirect or consequential damages or any damages + # whatsoever resulting from loss of use, data or profits, whether in an + # action of contract, negligence or other tortuous action, arising out + # of or in connection with the use or performance of this software. + # + # A large portion of the dictionary entries + # originate from ICOT Free Software. The following conditions for ICOT + # Free Software applies to the current dictionary as well. + # + # Each User may also freely distribute the Program, whether in its + # original form or modified, to any third party or parties, PROVIDED + # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear + # on, or be attached to, the Program, which is distributed substantially + # in the same form as set out herein and that such intended + # distribution, if actually made, will neither violate or otherwise + # contravene any of the laws and regulations of the countries having + # jurisdiction over the User or the intended distribution itself. + # + # NO WARRANTY + # + # The program was produced on an experimental basis in the course of the + # research and development conducted during the project and is provided + # to users as so produced on an experimental basis. Accordingly, the + # program is provided without any warranty whatsoever, whether express, + # implied, statutory or otherwise. The term "warranty" used herein + # includes, but is not limited to, any warranty of the quality, + # performance, merchantability and fitness for a particular purpose of + # the program and the nonexistence of any infringement or violation of + # any right of any third party. + # + # Each user of the program will agree and understand, and be deemed to + # have agreed and understood, that there is no warranty whatsoever for + # the program and, accordingly, the entire risk arising from or + # otherwise connected with the program is assumed by the user. + # + # Therefore, neither ICOT, the copyright holder, or any other + # organization that participated in or was otherwise related to the + # development of the program and their respective officials, directors, + # officers and other employees shall be held liable for any and all + # damages, including, without limitation, general, special, incidental + # and consequential damages, arising out of or otherwise in connection + # with the use or inability to use the program or any product, material + # or result produced or otherwise obtained by using the program, + # regardless of whether they have been advised of, or otherwise had + # knowledge of, the possibility of such damages at any time during the + # project or thereafter. Each user will be deemed to have agreed to the + # foregoing by his or her commencement of use of the program. The term + # "use" as used herein includes, but is not limited to, the use, + # modification, copying and distribution of the program and the + # production of secondary products from the program. + # + # In the case where the program, whether in its original form or + # modified, was distributed or delivered to or received by a user from + # any person, organization or entity other than ICOT, unless it makes or + # grants independently of ICOT any specific warranty to the user in + # writing, such person, organization or entity, will also be exempted + # from and not be held liable to the user for any such damages as noted + # above as far as the program is concerned. + # + # ---------------COPYING.ipadic-----END---------------------------------- + +---------------------------------------------------------------------- + +Lao Word Break Dictionary Data (laodict.txt) + + # Copyright (C) 2016 and later: Unicode, Inc. and others. + # License & terms of use: http://www.unicode.org/copyright.html + # Copyright (c) 2015 International Business Machines Corporation + # and others. All Rights Reserved. + # + # Project: https://github.com/rober42539/lao-dictionary + # Dictionary: https://github.com/rober42539/lao-dictionary/laodict.txt + # License: https://github.com/rober42539/lao-dictionary/LICENSE.txt + # (copied below) + # + # This file is derived from the above dictionary version of Nov 22, 2020 + # ---------------------------------------------------------------------- + # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # Redistributions of source code must retain the above copyright notice, this + # list of conditions and the following disclaimer. Redistributions in binary + # form must reproduce the above copyright notice, this list of conditions and + # the following disclaimer in the documentation and/or other materials + # provided with the distribution. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # OF THE POSSIBILITY OF SUCH DAMAGE. + # -------------------------------------------------------------------------- + +---------------------------------------------------------------------- + +Burmese Word Break Dictionary Data (burmesedict.txt) + + # Copyright (c) 2014 International Business Machines Corporation + # and others. All Rights Reserved. + # + # This list is part of a project hosted at: + # github.com/kanyawtech/myanmar-karen-word-lists + # + # -------------------------------------------------------------------------- + # Copyright (c) 2013, LeRoy Benjamin Sharon + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: Redistributions of source code must retain the above + # copyright notice, this list of conditions and the following + # disclaimer. Redistributions in binary form must reproduce the + # above copyright notice, this list of conditions and the following + # disclaimer in the documentation and/or other materials provided + # with the distribution. + # + # Neither the name Myanmar Karen Word Lists, nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + # SUCH DAMAGE. + # -------------------------------------------------------------------------- + +---------------------------------------------------------------------- + +Time Zone Database + + ICU uses the public domain data and code derived from Time Zone +Database for its time zone support. The ownership of the TZ database +is explained in BCP 175: Procedure for Maintaining the Time Zone +Database section 7. + + # 7. Database Ownership + # + # The TZ database itself is not an IETF Contribution or an IETF + # document. Rather it is a pre-existing and regularly updated work + # that is in the public domain, and is intended to remain in the + # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do + # not apply to the TZ Database or contributions that individuals make + # to it. Should any claims be made and substantiated against the TZ + # Database, the organization that is providing the IANA + # Considerations defined in this RFC, under the memorandum of + # understanding with the IETF, currently ICANN, may act in accordance + # with all competent court orders. No ownership claims will be made + # by ICANN or the IETF Trust on the database or the code. Any person + # making a contribution to the database or code waives all rights to + # future claims in that contribution or in the TZ Database. + +---------------------------------------------------------------------- + +Google double-conversion + +Copyright 2006-2011, the V8 project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- + +File: aclocal.m4 (only for ICU4C) +Section: pkg.m4 - Macros to locate and utilise pkg-config. + + +Copyright © 2004 Scott James Remnant . +Copyright © 2012-2015 Dan Nicholson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. + +As a special exception to the GNU General Public License, if you +distribute this file as part of a program that contains a +configuration script generated by Autoconf, you may include it under +the same distribution terms that you use for the rest of that +program. + + +(The condition for the exception is fulfilled because +ICU4C includes a configuration script generated by Autoconf, +namely the `configure` script.) + +---------------------------------------------------------------------- + +File: config.guess (only for ICU4C) + + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . + +As a special exception to the GNU General Public License, if you +distribute this file as part of a program that contains a +configuration script generated by Autoconf, you may include it under +the same distribution terms that you use for the rest of that +program. This Exception is an additional permission under section 7 +of the GNU General Public License, version 3 ("GPLv3"). + + +(The condition for the exception is fulfilled because +ICU4C includes a configuration script generated by Autoconf, +namely the `configure` script.) + +---------------------------------------------------------------------- + +File: install-sh (only for ICU4C) + + +Copyright 1991 by the Massachusetts Institute of Technology + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of M.I.T. not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. M.I.T. makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. diff --git a/SQLiteStudio3/coreSQLiteStudio/log.cpp b/SQLiteStudio3/coreSQLiteStudio/log.cpp index 60a22b2..5f9d72f 100644 --- a/SQLiteStudio3/coreSQLiteStudio/log.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/log.cpp @@ -75,3 +75,8 @@ void logExecutorAfterStep(const QString& str) qDebug() << getLogDateTime() << str; } + +bool isExecutorLoggingEnabled() +{ + return EXECUTOR_DEBUG; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/log.h b/SQLiteStudio3/coreSQLiteStudio/log.h index 0167509..54691f5 100644 --- a/SQLiteStudio3/coreSQLiteStudio/log.h +++ b/SQLiteStudio3/coreSQLiteStudio/log.h @@ -15,6 +15,7 @@ API_EXPORT void logSql(Db* db, const QString& str, const QHash API_EXPORT void logSql(Db* db, const QString& str, const QList& args, Db::Flags flags); API_EXPORT void logExecutorStep(QueryExecutorStep* step); API_EXPORT void logExecutorAfterStep(const QString& str); +API_EXPORT bool isExecutorLoggingEnabled(); API_EXPORT void setSqlLoggingEnabled(bool enabled); API_EXPORT void setSqlLoggingFilter(const QString& filter); API_EXPORT void setExecutorLoggingEnabled(bool enabled); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp index 5ca593f..42cb59b 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.cpp @@ -8,7 +8,8 @@ SqliteAlterTable::SqliteAlterTable() } SqliteAlterTable::SqliteAlterTable(const SqliteAlterTable& other) - : SqliteQuery(other), command(other.command), newName(other.newName), database(other.database), table(other.table), columnKw(other.columnKw) + : SqliteQuery(other), command(other.command), newName(other.newName), database(other.database), table(other.table), + dropColumnName(other.dropColumnName), columnKw(other.columnKw) { DEEP_COPY_FIELD(SqliteCreateTable::Column, newColumn); } @@ -32,10 +33,17 @@ SqliteAlterTable::SqliteAlterTable(const QString& name1, const QString& name2, b column->setParent(this); } +SqliteAlterTable::SqliteAlterTable(const QString& name1, const QString& name2, bool columnKw, const QString& dropColumn) + : SqliteAlterTable() +{ + command = Command::DROP_COLUMN; + initName(name1, name2); + this->columnKw = columnKw; + this->dropColumnName = dropColumn; +} + SqliteAlterTable::~SqliteAlterTable() { -// if (newColumn) - // delete newColumn; } SqliteStatement* SqliteAlterTable::clone() @@ -43,6 +51,15 @@ SqliteStatement* SqliteAlterTable::clone() return new SqliteAlterTable(*this); } +QStringList SqliteAlterTable::getColumnsInStatement() +{ + QStringList list; + if (!dropColumnName.isNull()) + list << dropColumnName; + + return list; +} + QStringList SqliteAlterTable::getTablesInStatement() { QStringList list; @@ -60,6 +77,14 @@ QStringList SqliteAlterTable::getDatabasesInStatement() return getStrListFromValue(database); } +TokenList SqliteAlterTable::getColumnTokensInStatement() +{ + if (command == Command::DROP_COLUMN && tokensMap.contains("nm")) + return extractPrintableTokens(tokensMap["nm"]); + + return TokenList(); +} + TokenList SqliteAlterTable::getTableTokensInStatement() { return getObjectTokenListFromFullname(); @@ -110,17 +135,32 @@ TokenList SqliteAlterTable::rebuildTokensFromContents() builder.withOther(table).withSpace(); - if (newColumn) - { - builder.withKeyword("ADD").withSpace(); - if (columnKw) - builder.withKeyword("COLUMN").withSpace(); - - builder.withStatement(newColumn); - } - else if (!newName.isNull()) - { - builder.withKeyword("RENAME").withSpace().withKeyword("TO").withSpace().withOther(newName); + switch (command) { + case Command::RENAME: + { + builder.withKeyword("RENAME").withSpace().withKeyword("TO").withSpace().withOther(newName); + break; + } + case Command::ADD_COLUMN: + { + builder.withKeyword("ADD").withSpace(); + if (columnKw) + builder.withKeyword("COLUMN").withSpace(); + + builder.withStatement(newColumn); + break; + } + case Command::DROP_COLUMN: + { + builder.withKeyword("DROP").withSpace(); + if (columnKw) + builder.withKeyword("COLUMN").withSpace(); + + builder.withOther(dropColumnName); + break; + } + case Command::null: + break; } builder.withOperator(";"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h index fbac3fe..1733dfc 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitealtertable.h @@ -11,6 +11,7 @@ class API_EXPORT SqliteAlterTable : public SqliteQuery { RENAME, ADD_COLUMN, + DROP_COLUMN, null }; @@ -18,12 +19,15 @@ class API_EXPORT SqliteAlterTable : public SqliteQuery SqliteAlterTable(const SqliteAlterTable& other); SqliteAlterTable(const QString& name1, const QString& name2, const QString& newName); SqliteAlterTable(const QString& name1, const QString& name2, bool columnKw, SqliteCreateTable::Column* column); + SqliteAlterTable(const QString& name1, const QString& name2, bool columnKw, const QString& dropColumn); ~SqliteAlterTable(); SqliteStatement* clone(); protected: + QStringList getColumnsInStatement(); QStringList getTablesInStatement(); QStringList getDatabasesInStatement(); + TokenList getColumnTokensInStatement(); TokenList getTableTokensInStatement(); TokenList getDatabaseTokensInStatement(); QList getFullObjectsInStatement(); @@ -37,6 +41,7 @@ class API_EXPORT SqliteAlterTable : public SqliteQuery QString newName = QString(); QString database = QString(); QString table = QString(); + QString dropColumnName = QString(); bool columnKw = false; SqliteCreateTable::Column* newColumn = nullptr; }; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp deleted file mode 100644 index c3ff500..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "sqlitecopy.h" -#include "sqlitequerytype.h" - -#include - -SqliteCopy::SqliteCopy() -{ - queryType = SqliteQueryType::Copy; -} - -SqliteCopy::SqliteCopy(const SqliteCopy& other) : - SqliteQuery(other), onConflict(other.onConflict), database(other.database), table(other.table), file(other.file), delimiter(other.delimiter) -{ -} - -SqliteCopy::SqliteCopy(SqliteConflictAlgo onConflict, const QString &name1, const QString &name2, const QString &name3, const QString &delim) - : SqliteCopy() -{ - this->onConflict = onConflict; - - if (!name2.isNull()) - { - database = name1; - table = name2; - } - else - table = name1; - - file = name3; - delimiter = delim; -} - -SqliteStatement* SqliteCopy::clone() -{ - return new SqliteCopy(*this); -} - -QStringList SqliteCopy::getTablesInStatement() -{ - return getStrListFromValue(table); -} - -QStringList SqliteCopy::getDatabasesInStatement() -{ - return getStrListFromValue(database); -} - -TokenList SqliteCopy::getTableTokensInStatement() -{ - return getObjectTokenListFromNmDbnm(); -} - -TokenList SqliteCopy::getDatabaseTokensInStatement() -{ - return getDbTokenListFromNmDbnm(); -} - -QList SqliteCopy::getFullObjectsInStatement() -{ - QList result; - - FullObject fullObj = getFullObjectFromNmDbnm(FullObject::TABLE); - if (fullObj.isValid()) - result << fullObj; - - fullObj = getFirstDbFullObject(); - if (fullObj.isValid()) - result << fullObj; - - return result; -} - -TokenList SqliteCopy::rebuildTokensFromContents() -{ - StatementTokenBuilder builder; - builder.withTokens(SqliteQuery::rebuildTokensFromContents()); - builder.withKeyword("COPY").withSpace(); - if (onConflict != SqliteConflictAlgo::null) - builder.withKeyword("OR").withSpace().withKeyword(sqliteConflictAlgo(onConflict)).withSpace(); - - if (!database.isNull()) - builder.withOther(database).withSpace(); - - builder.withOther(table).withSpace().withKeyword("FROM").withSpace().withString(file); - - if (!delimiter.isNull()) - builder.withSpace().withKeyword("USING").withSpace().withKeyword("DELIMITERS").withSpace().withString(delimiter); - - builder.withOperator(";"); - - return builder.build(); -} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h deleted file mode 100644 index 1e4b5b7..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecopy.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SQLITECOPY_H -#define SQLITECOPY_H - -#include "sqlitequery.h" -#include "sqliteconflictalgo.h" - -class API_EXPORT SqliteCopy : public SqliteQuery -{ - public: - SqliteCopy(); - SqliteCopy(const SqliteCopy& other); - SqliteCopy(SqliteConflictAlgo onConflict, const QString& name1, const QString& name2, const QString& name3, const QString& delim = QString()); - SqliteStatement* clone(); - - SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; - QString database = QString(); - QString table = QString(); - QString file = QString(); - QString delimiter = QString(); - - protected: - QStringList getTablesInStatement(); - QStringList getDatabasesInStatement(); - TokenList getTableTokensInStatement(); - TokenList getDatabaseTokensInStatement(); - QList getFullObjectsInStatement(); - TokenList rebuildTokensFromContents(); -}; - -typedef QSharedPointer SqliteCopyPtr; - -#endif // SQLITECOPY_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp index b747c33..1749e9a 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.cpp @@ -172,3 +172,13 @@ void SqliteCreateIndex::setTargetDatabase(const QString& database) { this->database = database; } + +QString SqliteCreateIndex::getObjectName() const +{ + return index; +} + +void SqliteCreateIndex::setObjectName(const QString& name) +{ + index = name; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h index 870ed17..b8876a2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateindex.h @@ -29,6 +29,8 @@ class API_EXPORT SqliteCreateIndex : public SqliteQuery, public SqliteTableRelat QString getTargetTable() const; QString getTargetDatabase() const; void setTargetDatabase(const QString& database); + QString getObjectName() const; + void setObjectName(const QString& name); bool uniqueKw = false; bool ifNotExistsKw = false; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp index d82270e..fff4036 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp @@ -1,6 +1,6 @@ #include "sqlitecreatetable.h" +#include "parser/parser_helper_stubs.h" #include "parser/statementtokenbuilder.h" -#include "common/utils_sql.h" #include "common/global.h" const QRegExp SqliteCreateTable::Column::GENERATED_ALWAYS_REGEXP = QRegExp("GENERATED\\s+ALWAYS"); @@ -12,7 +12,7 @@ SqliteCreateTable::SqliteCreateTable() SqliteCreateTable::SqliteCreateTable(const SqliteCreateTable& other) : SqliteQuery(other), ifNotExistsKw(other.ifNotExistsKw), tempKw(other.tempKw), temporaryKw(other.temporaryKw), - database(other.database), table(other.table), withOutRowId(other.withOutRowId) + database(other.database), table(other.table), withOutRowId(other.withOutRowId), strict(other.strict) { DEEP_COPY_COLLECTION(Column, columns); DEEP_COPY_COLLECTION(Constraint, constraints); @@ -40,10 +40,11 @@ SqliteCreateTable::SqliteCreateTable(bool ifNotExistsKw, int temp, const QString } } -SqliteCreateTable::SqliteCreateTable(bool ifNotExistsKw, int temp, const QString& name1, const QString& name2, const QList& columns, const QList& constraints, const QString& withOutRowId) : +SqliteCreateTable::SqliteCreateTable(bool ifNotExistsKw, int temp, const QString& name1, const QString& name2, const QList& columns, const QList& constraints, const QList& options) : SqliteCreateTable(ifNotExistsKw, temp, name1, name2, columns, constraints) { - this->withOutRowId = withOutRowId; + this->withOutRowId = parserStubFindCreateTableOption(options, ParserStubCreateTableOption::WITHOUT_ROWID) != nullptr; + this->strict = parserStubFindCreateTableOption(options, ParserStubCreateTableOption::STRICT) != nullptr; } SqliteCreateTable::SqliteCreateTable(bool ifNotExistsKw, int temp, const QString &name1, const QString &name2, SqliteSelect *select) @@ -76,7 +77,7 @@ QList SqliteCreateTable::getConstraints(SqliteCr SqliteStatement* SqliteCreateTable::getPrimaryKey() const { - for (Constraint* constr : getConstraints(Constraint::PRIMARY_KEY)) + for (Constraint*& constr : getConstraints(Constraint::PRIMARY_KEY)) return constr; Column::Constraint* colConstr = nullptr; @@ -115,7 +116,7 @@ QStringList SqliteCreateTable::getPrimaryKeyColumns() const SqliteCreateTable::Column* SqliteCreateTable::getColumn(const QString& colName) { - for (Column* col : columns) + for (Column*& col : columns) { if (col->name.compare(colName, Qt::CaseInsensitive) == 0) return col; @@ -142,6 +143,18 @@ QList SqliteCreateTable::getColumnForeig return results; } +void SqliteCreateTable::removeColumnConstraint(Column::Constraint* constr) +{ + for (Column* col : columns) + { + if (col->constraints.contains(constr)) + { + col->constraints.removeOne(constr); + return; + } + } +} + QStringList SqliteCreateTable::getColumnNames() const { QStringList names; @@ -236,8 +249,21 @@ TokenList SqliteCreateTable::rebuildTokensFromContents() builder.withParRight(); - if (!withOutRowId.isNull()) + bool atLeastOneOption = false; + if (withOutRowId) + { builder.withSpace().withKeyword("WITHOUT").withSpace().withOther("ROWID"); + atLeastOneOption = true; + } + + if (strict) + { + if (atLeastOneOption) + builder.withOperator(","); + + builder.withSpace().withOther("STRICT"); + //atLeastOneOption = true; // to uncomment if there are further options down below + } } builder.withOperator(";"); @@ -564,7 +590,7 @@ bool SqliteCreateTable::Constraint::doesAffectColumn(const QString& columnName) int SqliteCreateTable::Constraint::getAffectedColumnIdx(const QString& columnName) { int i = 0; - for (SqliteIndexedColumn* idxCol : indexedColumns) + for (SqliteIndexedColumn*& idxCol : indexedColumns) { if (idxCol->name.compare(columnName, Qt::CaseInsensitive) == 0) return i; @@ -604,12 +630,12 @@ TokenList SqliteCreateTable::Constraint::rebuildTokensFromContents() { case SqliteCreateTable::Constraint::PRIMARY_KEY: { - builder.withKeyword("PRIMARY").withSpace().withKeyword("KEY").withSpace().withParLeft().withStatementList(indexedColumns).withParRight(); + builder.withKeyword("PRIMARY").withSpace().withKeyword("KEY").withSpace().withParLeft().withStatementList(indexedColumns); if (autoincrKw) builder.withSpace().withKeyword("AUTOINCREMENT"); - builder.withConflict(onConflict); + builder.withParRight().withConflict(onConflict); break; } case SqliteCreateTable::Constraint::UNIQUE: @@ -861,3 +887,13 @@ void SqliteCreateTable::setTargetDatabase(const QString& database) { this->database = database; } + +QString SqliteCreateTable::getObjectName() const +{ + return table; +} + +void SqliteCreateTable::setObjectName(const QString& name) +{ + table = name; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h index d34688c..5d797c1 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h @@ -15,6 +15,8 @@ #include #include +struct ParserStubCreateTableOption; + class API_EXPORT SqliteCreateTable : public SqliteQuery, public SqliteDdlWithDbContext { public: @@ -189,7 +191,7 @@ class API_EXPORT SqliteCreateTable : public SqliteQuery, public SqliteDdlWithDbC const QList& columns, const QList& constraints); SqliteCreateTable(bool ifNotExistsKw, int temp, const QString& name1, const QString& name2, const QList& columns, const QList& constraints, - const QString& withOutRowId); + const QList& options); SqliteCreateTable(bool ifNotExistsKw, int temp, const QString& name1, const QString& name2, SqliteSelect* select); ~SqliteCreateTable(); @@ -201,10 +203,13 @@ class API_EXPORT SqliteCreateTable : public SqliteQuery, public SqliteDdlWithDbC Column* getColumn(const QString& colName); QList getForeignKeysByTable(const QString& foreignTable) const; QList getColumnForeignKeysByTable(const QString& foreignTable) const; + void removeColumnConstraint(Column::Constraint* constr); QStringList getColumnNames() const; QHash getModifiedColumnsMap(bool lowercaseKeys = false, Qt::CaseSensitivity cs = Qt::CaseInsensitive) const; QString getTargetDatabase() const; void setTargetDatabase(const QString& database); + QString getObjectName() const; + void setObjectName(const QString& name); bool ifNotExistsKw = false; bool tempKw = false; @@ -213,8 +218,9 @@ class API_EXPORT SqliteCreateTable : public SqliteQuery, public SqliteDdlWithDbC QString table = QString(); QList columns; QList constraints; + bool withOutRowId = false; + bool strict = false; SqliteSelect* select = nullptr; - QString withOutRowId = QString(); protected: QStringList getTablesInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp index 3f2c4e3..597395f 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.cpp @@ -370,3 +370,13 @@ void SqliteCreateTrigger::setTargetDatabase(const QString& database) { this->database = database; } + +QString SqliteCreateTrigger::getObjectName() const +{ + return trigger; +} + +void SqliteCreateTrigger::setObjectName(const QString& name) +{ + trigger = name; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h index ac7b81f..44b953a 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetrigger.h @@ -66,6 +66,8 @@ class API_EXPORT SqliteCreateTrigger : public SqliteQuery, public SqliteTableRel QString getTargetTable() const; QString getTargetDatabase() const; void setTargetDatabase(const QString& database); + QString getObjectName() const; + void setObjectName(const QString& name); bool tempKw = false; bool temporaryKw = false; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp index f5b54e7..0e11619 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.cpp @@ -129,3 +129,13 @@ void SqliteCreateView::setTargetDatabase(const QString& database) { this->database = database; } + +QString SqliteCreateView::getObjectName() const +{ + return view; +} + +void SqliteCreateView::setObjectName(const QString& name) +{ + view = name; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h index 74f6b91..645532c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreateview.h @@ -21,6 +21,8 @@ class API_EXPORT SqliteCreateView : public SqliteQuery, public SqliteDdlWithDbCo SqliteStatement* clone(); QString getTargetDatabase() const; void setTargetDatabase(const QString& database); + QString getObjectName() const; + void setObjectName(const QString& name); bool tempKw = false; bool temporaryKw = false; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteddlwithdbcontext.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteddlwithdbcontext.h index cb64986..0daf378 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteddlwithdbcontext.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteddlwithdbcontext.h @@ -9,6 +9,9 @@ class API_EXPORT SqliteDdlWithDbContext public: virtual QString getTargetDatabase() const = 0; virtual void setTargetDatabase(const QString& database) = 0; + + virtual QString getObjectName() const = 0; + virtual void setObjectName(const QString& name) = 0; }; typedef QSharedPointer SqliteDdlWithDbContextPtr; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp index dd4d7cb..faf92a4 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.cpp @@ -16,20 +16,21 @@ SqliteDelete::SqliteDelete(const SqliteDelete& other) : { DEEP_COPY_FIELD(SqliteExpr, where); DEEP_COPY_FIELD(SqliteWith, with); + DEEP_COPY_COLLECTION(SqliteResultColumn, returning); } -SqliteDelete::SqliteDelete(const QString &name1, const QString &name2, const QString& indexedByName, SqliteExpr *where, SqliteWith* with) +SqliteDelete::SqliteDelete(const QString &name1, const QString &name2, const QString& indexedByName, SqliteExpr *where, SqliteWith* with, const QList& returning) : SqliteDelete() { - init(name1, name2, where, with); + init(name1, name2, where, with, returning); this->indexedBy = indexedByName; this->indexedByKw = true; } -SqliteDelete::SqliteDelete(const QString &name1, const QString &name2, bool notIndexedKw, SqliteExpr *where, SqliteWith* with) +SqliteDelete::SqliteDelete(const QString &name1, const QString &name2, bool notIndexedKw, SqliteExpr *where, SqliteWith* with, const QList& returning) : SqliteDelete() { - init(name1, name2, where, with); + init(name1, name2, where, with, returning); this->notIndexedKw = notIndexedKw; } @@ -37,7 +38,7 @@ SqliteDelete::~SqliteDelete() { } -SqliteStatement*SqliteDelete::clone() +SqliteStatement* SqliteDelete::clone() { return new SqliteDelete(*this); } @@ -91,7 +92,7 @@ QList SqliteDelete::getFullObjectsInStatement() return result; } -void SqliteDelete::init(const QString &name1, const QString &name2, SqliteExpr *where, SqliteWith* with) +void SqliteDelete::init(const QString &name1, const QString &name2, SqliteExpr *where, SqliteWith* with, const QList& returning) { this->where = where; if (where) @@ -108,6 +109,10 @@ void SqliteDelete::init(const QString &name1, const QString &name2, SqliteExpr * } else table = name1; + + this->returning = returning; + for (SqliteResultColumn*& retCol : this->returning) + retCol->setParent(this); } TokenList SqliteDelete::rebuildTokensFromContents() @@ -131,6 +136,13 @@ TokenList SqliteDelete::rebuildTokensFromContents() if (where) builder.withSpace().withKeyword("WHERE").withStatement(where); + if (!returning.isEmpty()) + { + builder.withKeyword("RETURNING"); + for (SqliteResultColumn*& retCol : returning) + builder.withSpace().withStatement(retCol); + } + builder.withOperator(";"); return builder.build(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h index 5d24619..faa38fe 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitedelete.h @@ -2,8 +2,9 @@ #define SQLITEDELETE_H #include "sqlitequery.h" - +#include "sqliteselect.h" #include +#include class SqliteExpr; class SqliteWith; @@ -13,8 +14,10 @@ class API_EXPORT SqliteDelete : public SqliteQuery public: SqliteDelete(); SqliteDelete(const SqliteDelete& other); - SqliteDelete(const QString& name1, const QString& name2, const QString& indexedByName, SqliteExpr* where, SqliteWith* with); - SqliteDelete(const QString& name1, const QString& name2, bool notIndexedKw, SqliteExpr* where, SqliteWith* with); + SqliteDelete(const QString& name1, const QString& name2, const QString& indexedByName, SqliteExpr* where, SqliteWith* with, + const QList& returning); + SqliteDelete(const QString& name1, const QString& name2, bool notIndexedKw, SqliteExpr* where, SqliteWith* with, + const QList& returning); ~SqliteDelete(); SqliteStatement* clone(); @@ -28,7 +31,8 @@ class API_EXPORT SqliteDelete : public SqliteQuery TokenList rebuildTokensFromContents(); private: - void init(const QString& name1, const QString& name2, SqliteExpr* where, SqliteWith* with); + void init(const QString& name1, const QString& name2, SqliteExpr* where, SqliteWith* with, + const QList& returning); public: QString database = QString(); @@ -38,6 +42,7 @@ class API_EXPORT SqliteDelete : public SqliteQuery QString indexedBy = QString(); SqliteExpr* where = nullptr; SqliteWith* with = nullptr; + QList returning; }; typedef QSharedPointer SqliteDeletePtr; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp index e2c79ea..89c6b9b 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp @@ -231,6 +231,19 @@ void SqliteExpr::initUnaryOp(SqliteExpr *expr, const QString& op) expr->setParent(this); } +void SqliteExpr::initPtrOp(SqliteExpr* expr1, const QString& op, SqliteExpr* expr2) +{ + mode = SqliteExpr::Mode::PTR_OP; + this->expr1 = expr1; + this->expr2 = expr2; + ptrOp = op; + if (expr1) + expr1->setParent(this); + + if (expr2) + expr2->setParent(this); +} + void SqliteExpr::initLike(SqliteExpr *expr1, bool notKw, LikeOp likeOp, SqliteExpr *expr2, SqliteExpr *expr3) { mode = SqliteExpr::Mode::LIKE; @@ -271,6 +284,19 @@ void SqliteExpr::initIs(SqliteExpr *expr1, bool notKw, SqliteExpr *expr2) expr2->setParent(this); } +void SqliteExpr::initDistinct(SqliteExpr* expr1, bool notKw, SqliteExpr* expr2) +{ + mode = SqliteExpr::Mode::DISTINCT; + this->expr1 = expr1; + this->notKw = notKw; + this->expr2 = expr2; + if (expr1) + expr1->setParent(this); + + if (expr2) + expr2->setParent(this); +} + void SqliteExpr::initBetween(SqliteExpr *expr1, bool notKw, SqliteExpr *expr2, SqliteExpr *expr3) { mode = SqliteExpr::Mode::BETWEEN; @@ -549,6 +575,9 @@ TokenList SqliteExpr::rebuildTokensFromContents() case SqliteExpr::Mode::BINARY_OP: builder.withStatement(expr1).withSpace().withOperator(binaryOp).withSpace().withStatement(expr2); break; + case SqliteExpr::Mode::PTR_OP: + builder.withStatement(expr1).withSpace().withOperator(ptrOp).withSpace().withStatement(expr2); + break; case SqliteExpr::Mode::FUNCTION: builder.withOther(function).withParLeft(); if (distinctKw) @@ -598,6 +627,9 @@ TokenList SqliteExpr::rebuildTokensFromContents() case SqliteExpr::Mode::IS: builder.withTokens(rebuildIs()); break; + case SqliteExpr::Mode::DISTINCT: + builder.withTokens(rebuildDistinct()); + break; case SqliteExpr::Mode::BETWEEN: builder.withTokens(rebuildBetween()); break; @@ -691,6 +723,17 @@ TokenList SqliteExpr::rebuildIs() return builder.build(); } +TokenList SqliteExpr::rebuildDistinct() +{ + StatementTokenBuilder builder; + builder.withStatement(expr1).withSpace().withKeyword("IS"); + if (notKw) + builder.withSpace().withKeyword("NOT"); + + builder.withSpace().withKeyword("DISTINCT").withSpace().withKeyword("FROM").withSpace().withStatement(expr2); + return builder.build(); +} + TokenList SqliteExpr::rebuildBetween() { StatementTokenBuilder builder; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h index ef4c7da..f5fc6c2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.h @@ -23,6 +23,7 @@ class API_EXPORT SqliteExpr : public SqliteStatement ID, UNARY_OP, BINARY_OP, + PTR_OP, FUNCTION, SUB_EXPR, ROW_VALUE, @@ -32,6 +33,7 @@ class API_EXPORT SqliteExpr : public SqliteStatement NULL_, NOTNULL, IS, + DISTINCT, BETWEEN, IN, EXISTS, @@ -85,9 +87,11 @@ class API_EXPORT SqliteExpr : public SqliteStatement void initWindowFunction(const QString& fnName, SqliteFilterOver* filterOver); void initBinOp(SqliteExpr* expr1, const QString& op, SqliteExpr* expr2); void initUnaryOp(SqliteExpr* expr, const QString& op); + void initPtrOp(SqliteExpr* expr1, const QString& op, SqliteExpr* expr2); void initLike(SqliteExpr* expr1, bool notKw, SqliteExpr::LikeOp likeOp, SqliteExpr* expr2, SqliteExpr* expr3 = nullptr); void initNull(SqliteExpr* expr, const QString& value); void initIs(SqliteExpr* expr1, bool notKw, SqliteExpr* expr2); + void initDistinct(SqliteExpr* expr1, bool notKw, SqliteExpr* expr2); void initBetween(SqliteExpr* expr1, bool notKw, SqliteExpr* expr2, SqliteExpr* expr3); void initIn(SqliteExpr* expr, bool notKw, const QList& exprList); void initIn(SqliteExpr* expr, bool notKw, SqliteSelect* select); @@ -108,6 +112,7 @@ class API_EXPORT SqliteExpr : public SqliteStatement QString column = QString(); QString unaryOp = QString(); QString binaryOp = QString(); + QString ptrOp = QString(); QString function = QString(); QString collation = QString(); QString ctime = QString(); @@ -144,6 +149,7 @@ class API_EXPORT SqliteExpr : public SqliteStatement TokenList rebuildLike(); TokenList rebuildNotNull(); TokenList rebuildIs(); + TokenList rebuildDistinct(); TokenList rebuildBetween(); TokenList rebuildIn(); TokenList rebuildCase(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp index 906b385..5242fa6 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.cpp @@ -20,14 +20,14 @@ SqliteInsert::SqliteInsert(const SqliteInsert& other) : DEEP_COPY_FIELD(SqliteSelect, select); DEEP_COPY_FIELD(SqliteWith, with); DEEP_COPY_FIELD(SqliteUpsert, upsert); + DEEP_COPY_COLLECTION(SqliteResultColumn, returning); } SqliteInsert::SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString &name1, const QString &name2, const QList &columns, - const QList &row, SqliteWith* with) : + const QList &row, SqliteWith* with, const QList& returning) : SqliteInsert() { - initName(name1, name2); - initMode(replace, onConflict); + init(name1, name2, replace, onConflict, returning); columnNames = columns; values = row; @@ -40,11 +40,10 @@ SqliteInsert::SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QS } SqliteInsert::SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString &name1, const QString &name2, const QList &columns, - SqliteSelect *select, SqliteWith* with, SqliteUpsert* upsert) : + SqliteSelect *select, SqliteWith* with, SqliteUpsert* upsert, const QList& returning) : SqliteInsert() { - initName(name1, name2); - initMode(replace, onConflict); + init(name1, name2, replace, onConflict, returning); this->with = with; if (with) @@ -61,11 +60,10 @@ SqliteInsert::SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QS } SqliteInsert::SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString &name1, const QString &name2, const QList &columns, - SqliteWith* with) : + SqliteWith* with, const QList& returning) : SqliteInsert() { - initName(name1, name2); - initMode(replace, onConflict); + init(name1, name2, replace, onConflict, returning); this->with = with; if (with) @@ -104,7 +102,7 @@ QStringList SqliteInsert::getDatabasesInStatement() TokenList SqliteInsert::getColumnTokensInStatement() { TokenList list; - for (TokenPtr token : getTokenListFromNamedKey("idlist_opt", -1)) + for (TokenPtr& token : getTokenListFromNamedKey("idlist_opt", -1)) { if (token->type != Token::OTHER && token->type != Token::KEYWORD) continue; @@ -153,7 +151,7 @@ QList SqliteInsert::getFullObjectsInStatement() return result; } -void SqliteInsert::initName(const QString& name1, const QString& name2) +void SqliteInsert::init(const QString& name1, const QString& name2, bool replace, SqliteConflictAlgo onConflict, const QList& returning) { if (!name2.isNull()) { @@ -162,12 +160,13 @@ void SqliteInsert::initName(const QString& name1, const QString& name2) } else table = name1; -} -void SqliteInsert::initMode(bool replace, SqliteConflictAlgo onConflict) -{ replaceKw = replace; this->onConflict = onConflict; + + this->returning = returning; + for (SqliteResultColumn*& retCol : this->returning) + retCol->setParent(this); } TokenList SqliteInsert::rebuildTokensFromContents() @@ -212,6 +211,13 @@ TokenList SqliteInsert::rebuildTokensFromContents() } } + if (!returning.isEmpty()) + { + builder.withKeyword("RETURNING"); + for (SqliteResultColumn*& retCol : returning) + builder.withSpace().withStatement(retCol); + } + builder.withOperator(";"); return builder.build(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h index 40581cd..b409abc 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteinsert.h @@ -3,10 +3,10 @@ #include "sqlitequery.h" #include "sqliteconflictalgo.h" +#include "sqliteselect.h" #include #include -class SqliteSelect; class SqliteExpr; class SqliteWith; class SqliteUpsert; @@ -18,11 +18,14 @@ class API_EXPORT SqliteInsert : public SqliteQuery SqliteInsert(const SqliteInsert& other); SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString& name1, const QString& name2, const QList& columns, - const QList& row, SqliteWith* with); + const QList& row, SqliteWith* with, + const QList& returning); SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString& name1, - const QString& name2, const QList& columns, SqliteSelect* select, SqliteWith* with, SqliteUpsert* upsert = nullptr); + const QString& name2, const QList& columns, SqliteSelect* select, SqliteWith* with, + SqliteUpsert* upsert, const QList& returning); SqliteInsert(bool replace, SqliteConflictAlgo onConflict, const QString& name1, - const QString& name2, const QList& columns, SqliteWith* with); + const QString& name2, const QList& columns, SqliteWith* with, + const QList& returning); ~SqliteInsert(); SqliteStatement* clone(); @@ -38,8 +41,8 @@ class API_EXPORT SqliteInsert : public SqliteQuery TokenList rebuildTokensFromContents(); private: - void initName(const QString& name1, const QString& name2); - void initMode(bool replace, SqliteConflictAlgo onConflict); + void init(const QString& name1, const QString& name2, bool replace, SqliteConflictAlgo onConflict, + const QList& returning); public: bool replaceKw = false; @@ -52,6 +55,7 @@ class API_EXPORT SqliteInsert : public SqliteQuery SqliteSelect* select = nullptr; SqliteWith* with = nullptr; SqliteUpsert* upsert = nullptr; + QList returning; }; typedef QSharedPointer SqliteInsertPtr; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp index 732c0a2..4db4b5d 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp @@ -88,18 +88,20 @@ void SqliteOrderBy::setColumnName(const QString& name) void SqliteOrderBy::setCollation(const QString& name) { - if (expr && expr->mode == SqliteExpr::Mode::COLLATE) + if (!expr) + return; + + if (expr->mode == SqliteExpr::Mode::COLLATE) { expr->collation = name; + return; } - else - { - SqliteExpr* theExpr = expr; - SqliteExpr* collationExpr = new SqliteExpr(); - collationExpr->initCollate(theExpr, name); - theExpr->setParent(collationExpr); - collationExpr->setParent(this); - } + + SqliteExpr* collationExpr = new SqliteExpr(); + collationExpr->initCollate(expr, name); + expr->setParent(collationExpr); + collationExpr->setParent(this); + expr = collationExpr; } TokenList SqliteOrderBy::rebuildTokensFromContents() diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp index 1c04ca6..b70778e 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitequerytype.cpp @@ -60,9 +60,8 @@ QString sqliteQueryTypeToString(const SqliteQueryType& type) return "Update"; case SqliteQueryType::Vacuum: return "Vacuum"; - default: - return QString(); } + return QString(); } bool isDataReturningQuery(const SqliteQueryType& type) @@ -98,7 +97,7 @@ bool isDataReturningQuery(const SqliteQueryType& type) case SqliteQueryType::Savepoint: case SqliteQueryType::Update: case SqliteQueryType::Vacuum: - default: return false; } + return false; } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp index e0439d2..501f7f3 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.cpp @@ -118,7 +118,7 @@ SqliteSelect::CompoundOperator SqliteSelect::compoundOperator(const QString& op) void SqliteSelect::reset() { - for (Core* core : coreSelects) + for (Core*& core : coreSelects) delete core; coreSelects.clear(); @@ -443,7 +443,7 @@ QStringList SqliteSelect::Core::JoinConstraint::getColumnsInStatement() TokenList SqliteSelect::Core::JoinConstraint::getColumnTokensInStatement() { TokenList list; - for (TokenPtr token : getTokenListFromNamedKey("idlist", -1)) + for (TokenPtr& token : getTokenListFromNamedKey("idlist", -1)) { if (token->type == Token::OPERATOR) // a COMMA continue; @@ -573,7 +573,7 @@ SqliteSelect::Core::JoinSource::JoinSource(SqliteSelect::Core::SingleSource *sin if (singleSource) singleSource->setParent(this); - for (JoinSourceOther* other : otherSources) + for (JoinSourceOther*& other : otherSources) other->setParent(this); } @@ -666,11 +666,6 @@ TokenList SqliteSelect::Core::SingleSource::rebuildTokensFromContents() } TokenList SqliteSelect::Core::JoinOp::rebuildTokensFromContents() -{ - return rebuildTokensForSqlite3(); -} - -TokenList SqliteSelect::Core::JoinOp::rebuildTokensForSqlite3() { StatementTokenBuilder builder; if (comma) @@ -682,9 +677,16 @@ TokenList SqliteSelect::Core::JoinOp::rebuildTokensForSqlite3() if (naturalKw) builder.withKeyword("NATURAL").withSpace(); - if (leftKw) + if (leftKw || fullKw || rightKw) { - builder.withKeyword("LEFT").withSpace(); + if (leftKw) + builder.withKeyword("LEFT"); + else if (fullKw) + builder.withKeyword("FULL"); + else if (rightKw) + builder.withKeyword("RIGHT"); + + builder.withSpace(); if (outerKw) builder.withKeyword("OUTER").withSpace(); } @@ -780,7 +782,7 @@ TokenList SqliteSelect::rebuildTokensFromContents() if (with) builder.withStatement(with); - for (SqliteSelect::Core* core : coreSelects) + for (SqliteSelect::Core*& core : coreSelects) { if (core->compoundOp == CompoundOperator::UNION_ALL) { diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h index 58babfe..dfed9c2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteselect.h @@ -123,9 +123,6 @@ class API_EXPORT SqliteSelect : public SqliteQuery protected: TokenList rebuildTokensFromContents(); - - private: - TokenList rebuildTokensForSqlite3(); }; class API_EXPORT JoinConstraint : public SqliteStatement @@ -232,5 +229,6 @@ class API_EXPORT SqliteSelect : public SqliteQuery }; typedef QSharedPointer SqliteSelectPtr; +typedef SqliteSelect::Core::ResultColumn SqliteResultColumn; #endif // SQLITESELECT_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h index 43a60ba..167321f 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitestatement.h @@ -227,6 +227,12 @@ class API_EXPORT SqliteStatement : public QObject void processPostParsing(); virtual SqliteStatement* clone() = 0; + template + T* typeClone() + { + return dynamic_cast(clone()); + } + template void attach(QList& listMemberForChild, T* childStatementToAttach) { diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp index 89c8195..55d8b34 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.cpp @@ -27,6 +27,7 @@ SqliteUpdate::SqliteUpdate(const SqliteUpdate& other) : DEEP_COPY_FIELD(SqliteExpr, where); DEEP_COPY_FIELD(SqliteWith, with); DEEP_COPY_FIELD(SqliteSelect::Core::JoinSource, from); + DEEP_COPY_COLLECTION(SqliteResultColumn, returning); } SqliteUpdate::~SqliteUpdate() @@ -34,7 +35,7 @@ SqliteUpdate::~SqliteUpdate() } SqliteUpdate::SqliteUpdate(SqliteConflictAlgo onConflict, const QString &name1, const QString &name2, bool notIndexedKw, const QString &indexedBy, - const QList& values, SqliteSelect::Core::JoinSource* from, SqliteExpr *where, SqliteWith* with) + const QList& values, SqliteSelect::Core::JoinSource* from, SqliteExpr *where, SqliteWith* with, const QList& returning) : SqliteUpdate() { this->onConflict = onConflict; @@ -64,8 +65,12 @@ SqliteUpdate::SqliteUpdate(SqliteConflictAlgo onConflict, const QString &name1, if (with) with->setParent(this); - for (const ColumnAndValue& keyValue : keyValueMap) + for (ColumnAndValue& keyValue : keyValueMap) keyValue.second->setParent(this); + + this->returning = returning; + for (SqliteResultColumn*& retCol : this->returning) + retCol->setParent(this); } SqliteStatement*SqliteUpdate::clone() @@ -75,7 +80,7 @@ SqliteStatement*SqliteUpdate::clone() SqliteExpr* SqliteUpdate::getValueForColumnSet(const QString& column) { - for (const ColumnAndValue& keyValue : keyValueMap) + for (ColumnAndValue& keyValue : keyValueMap) { if (keyValue.first == column) return keyValue.second; @@ -86,7 +91,7 @@ SqliteExpr* SqliteUpdate::getValueForColumnSet(const QString& column) QStringList SqliteUpdate::getColumnsInStatement() { QStringList columns; - for (const ColumnAndValue& keyValue : keyValueMap) + for (ColumnAndValue& keyValue : keyValueMap) { if (keyValue.first.type() == QVariant::StringList) columns += keyValue.first.toStringList(); @@ -120,7 +125,7 @@ TokenList SqliteUpdate::getColumnTokensInStatement() int end; int start = 0; SqliteExpr* expr = nullptr; - for (const ColumnAndValue& keyValue : keyValueMap) + for (ColumnAndValue& keyValue : keyValueMap) { expr = keyValue.second; end = setListTokens.indexOf(expr->tokens[0]); @@ -205,7 +210,7 @@ TokenList SqliteUpdate::rebuildTokensFromContents() builder.withKeyword("SET").withSpace(); bool first = true; - for (const ColumnAndValue& keyVal : keyValueMap) + for (ColumnAndValue& keyVal : keyValueMap) { if (!first) builder.withOperator(",").withSpace(); @@ -225,6 +230,13 @@ TokenList SqliteUpdate::rebuildTokensFromContents() if (where) builder.withSpace().withKeyword("WHERE").withStatement(where); + if (!returning.isEmpty()) + { + builder.withKeyword("RETURNING"); + for (SqliteResultColumn*& retCol : returning) + builder.withSpace().withStatement(retCol); + } + builder.withOperator(";"); return builder.build(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h index e843bcd..be121b2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteupdate.h @@ -21,7 +21,7 @@ class API_EXPORT SqliteUpdate : public SqliteQuery ~SqliteUpdate(); SqliteUpdate(SqliteConflictAlgo onConflict, const QString& name1, const QString& name2, bool notIndexedKw, const QString& indexedBy, const QList& values, - SqliteSelect::Core::JoinSource* from, SqliteExpr* where, SqliteWith* with); + SqliteSelect::Core::JoinSource* from, SqliteExpr* where, SqliteWith* with, const QList& returning); SqliteStatement* clone(); SqliteExpr* getValueForColumnSet(const QString& column); @@ -36,6 +36,7 @@ class API_EXPORT SqliteUpdate : public SqliteQuery SqliteSelect::Core::JoinSource* from = nullptr; SqliteExpr* where = nullptr; SqliteWith* with = nullptr; + QList returning; protected: QStringList getColumnsInStatement(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewindowdefinition.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewindowdefinition.cpp index adf135f..69138b6 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewindowdefinition.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewindowdefinition.cpp @@ -265,7 +265,7 @@ SqliteWindowDefinition::Window::Frame::Bound::Bound(SqliteExpr* expr, const QStr if (upper == "UNBOUNDED PRECEDING") type = Type::UNBOUNDED_PRECEDING; else if (expr && upper == "PRECEDING") - type = Type::EXPR_FOLLOWING; + type = Type::EXPR_PRECEDING; else if (upper == "UNBOUNDED FOLLOWING") type = Type::UNBOUNDED_FOLLOWING; else if (expr && upper == "FOLLOWING") diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp index 901ac56..c15b447 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.cpp @@ -36,19 +36,19 @@ SqliteWith::CommonTableExpression::CommonTableExpression() } SqliteWith::CommonTableExpression::CommonTableExpression(const SqliteWith::CommonTableExpression& other) : - SqliteStatement(other), table(other.table) + SqliteStatement(other), table(other.table), asMode(other.asMode) { DEEP_COPY_COLLECTION(SqliteIndexedColumn, indexedColumns); DEEP_COPY_FIELD(SqliteSelect, select); } -SqliteWith::CommonTableExpression::CommonTableExpression(const QString& tableName, const QList& indexedColumns, SqliteSelect* select) : - table(tableName), indexedColumns(indexedColumns), select(select) +SqliteWith::CommonTableExpression::CommonTableExpression(const QString& tableName, const QList& indexedColumns, SqliteSelect* select, AsMode asMode) : + table(tableName), indexedColumns(indexedColumns), select(select), asMode(asMode) { select->setParent(this); } -SqliteStatement*SqliteWith::CommonTableExpression::clone() +SqliteStatement* SqliteWith::CommonTableExpression::clone() { return new SqliteWith::CommonTableExpression(*this); } @@ -61,7 +61,19 @@ TokenList SqliteWith::CommonTableExpression::rebuildTokensFromContents() if (indexedColumns.size() > 0) builder.withSpace().withParLeft().withStatementList(indexedColumns).withParRight(); - builder.withSpace().withKeyword("AS").withSpace().withParLeft().withStatement(select).withParRight(); + builder.withSpace().withKeyword("AS"); + switch (asMode) { + case ANY: + break; + case MATERIALIZED: + builder.withKeyword("MATERIALIZED"); + break; + case NOT_MATERIALIZED: + builder.withKeyword("NOT").withSpace().withKeyword("MATERIALIZED"); + break; + } + + builder.withSpace().withParLeft().withStatement(select).withParRight(); return builder.build(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h index 30f8181..8429e53 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitewith.h @@ -12,15 +12,23 @@ class SqliteWith : public SqliteStatement class CommonTableExpression : public SqliteStatement { public: + enum AsMode { + ANY, + MATERIALIZED, + NOT_MATERIALIZED + }; + CommonTableExpression(); CommonTableExpression(const CommonTableExpression& other); - CommonTableExpression(const QString& tableName, const QList& indexedColumns, SqliteSelect* select); + CommonTableExpression(const QString& tableName, const QList& indexedColumns, SqliteSelect* select, + AsMode asMode); SqliteStatement* clone(); QString table; QList indexedColumns; SqliteSelect* select = nullptr; + AsMode asMode = ANY; protected: TokenList rebuildTokensFromContents(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/keywords.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/keywords.cpp index a5651db..3d877c8 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/keywords.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/keywords.cpp @@ -118,6 +118,7 @@ void initKeywords() keywords3["LIKE"] = TK3_LIKE_KW; keywords3["LIMIT"] = TK3_LIMIT; keywords3["MATCH"] = TK3_MATCH; + keywords3["MATERIALIZED"] = TK3_MATERIALIZED; keywords3["NATURAL"] = TK3_JOIN_KW; keywords3["NO"] = TK3_NO; keywords3["NOT"] = TK3_NOT; @@ -149,6 +150,7 @@ void initKeywords() keywords3["RENAME"] = TK3_RENAME; keywords3["REPLACE"] = TK3_REPLACE; keywords3["RESTRICT"] = TK3_RESTRICT; + keywords3["RETURNING"] = TK3_RETURNING; keywords3["RIGHT"] = TK3_JOIN_KW; keywords3["ROLLBACK"] = TK3_ROLLBACK; keywords3["ROW"] = TK3_ROW; @@ -192,11 +194,11 @@ void initKeywords() softKeywords3 << "ABORT" << "ACTION" << "AFTER" << "ALWAYS" << "ANALYZE" << "ASC" << "ATTACH" << "BEFORE" << "BEGIN" << "BY" << "CASCADE" << "CAST" << "COLUMNKW" << "CONFLICT" << "CURRENT" << "DATABASE" << "DEFERRED" << "DESC" << "DETACH" << "DO" << "EACH" << "END" << "EXCLUDE" << "EXCLUSIVE" << "EXPLAIN" << "FAIL" << "FIRST" << "FOLLOWING" << "FOR" << "GENERATED" << "GROUPS" - << "IGNORE" << "IMMEDIATE" << "INDEXED" << "INITIALLY" << "INSTEAD" << "LAST" << "LIKE_KW" << "MATCH" << "NO" << "NULLS" - << "OTHERS" << "PLAN" << "QUERY" << "KEY" << "OF" << "OFFSET" << "PARTITION" << "PRAGMA" << "PRECEDING" << "RAISE" << "RANGE" - << "RECURSIVE" << "RELEASE" << "REPLACE" << "RESTRICT" << "ROW" << "ROWS" << "ROLLBACK" << "SAVEPOINT" << "TEMP" << "TIES" - << "TRIGGER" << "UNBOUNDED" << "VACUUM" << "VIEW" << "VIRTUAL" << "WITH" << "WITHOUT" << "REINDEX" << "RENAME" << "IF" - << "CURRENT_DATE" << "CURRENT_TIME" << "CURRENT_TIMESTAMP"; + << "IGNORE" << "IMMEDIATE" << "INDEXED" << "INITIALLY" << "INSTEAD" << "LAST" << "LIKE_KW" << "MATCH" << "MATERIALIZED" << "NO" + << "NULLS" << "OTHERS" << "PLAN" << "QUERY" << "KEY" << "OF" << "OFFSET" << "PARTITION" << "PRAGMA" << "PRECEDING" << "RAISE" + << "RANGE" << "RECURSIVE" << "RELEASE" << "REPLACE" << "RESTRICT" << "ROW" << "ROWS" << "ROLLBACK" << "SAVEPOINT" << "TEMP" + << "TIES" << "TRIGGER" << "UNBOUNDED" << "VACUUM" << "VIEW" << "VIRTUAL" << "WITH" << "WITHOUT" << "REINDEX" << "RENAME" + << "IF" << "CURRENT_DATE" << "CURRENT_TIME" << "CURRENT_TIMESTAMP" << "WINDOW" << "OVER" << "FILTER"; } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/lempar.c b/SQLiteStudio3/coreSQLiteStudio/parser/lempar.c index 3b239dc..5bb7412 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/lempar.c +++ b/SQLiteStudio3/coreSQLiteStudio/parser/lempar.c @@ -782,7 +782,7 @@ static void yy_reduce( #endif { yy_shift(yypParser,yyact,yygoto,&yygotominor); - if (parserContext->setupTokens) + if (parserContext->setupTokens && yypParser->yyidx >= 0) { QList* tokensPtr = yypParser->yystack[yypParser->yyidx].tokens; *tokensPtr = allTokensWithAllInherited + *tokensPtr; @@ -1023,4 +1023,4 @@ void Parse( } }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); return; -} \ No newline at end of file +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/lexer.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/lexer.cpp index bf5185b..8660583 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/lexer.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/lexer.cpp @@ -1,6 +1,5 @@ #include "lexer.h" #include "keywords.h" -#include "log.h" #include "lexer_low_lev.h" #include "sqlite3_parse.h" #include "common/utils_sql.h" @@ -26,6 +25,7 @@ TokenList Lexer::process(const QString &sql) { TokenList resultList; int lgt; + TokenPtr prevToken; TokenPtr token; QString str = sql; @@ -37,7 +37,7 @@ TokenList Lexer::process(const QString &sql) else token = TokenPtr::create(); - lgt = lexerGetToken(str, token, 3, tolerant); + lgt = lexerGetToken(str, token, prevToken, 3, tolerant); if (lgt == 0) break; @@ -48,6 +48,8 @@ TokenList Lexer::process(const QString &sql) resultList << token; str = str.mid(lgt); pos += lgt; + if (!token->isWhitespace()) + prevToken = token; } return resultList; @@ -70,7 +72,7 @@ TokenPtr Lexer::getToken() else token = TokenPtr::create(); - int lgt = lexerGetToken(sqlToTokenize, token, 3, tolerant); + int lgt = lexerGetToken(sqlToTokenize, token, prevTokenProcessed, 3, tolerant); if (lgt == 0) return TokenPtr(); @@ -80,6 +82,8 @@ TokenPtr Lexer::getToken() sqlToTokenize = sqlToTokenize.mid(lgt); tokenPosition += lgt; + if (!token->isWhitespace()) + prevTokenProcessed = token; return token; } @@ -190,6 +194,7 @@ void Lexer::staticInit() createTokenType(TK3_ID_TRIG, Token::CTX_TRIGGER, ""); createTokenType(TK3_ID_TRIG_NEW, Token::CTX_TRIGGER_NEW, ""); createTokenType(TK3_CTX_ROWID_KW, Token::CTX_ROWID_KW, "ROWID"); + createTokenType(TK3_CTX_STRICT_KW, Token::CTX_STRICT_KW, "STRICT"); createTokenType(TK3_ID, Token::CTX_OLD_KW, "OLD"); createTokenType(TK3_ID, Token::CTX_NEW_KW, "NEW"); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/lexer.h b/SQLiteStudio3/coreSQLiteStudio/parser/lexer.h index 10db9ce..07b2d06 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/lexer.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/lexer.h @@ -201,6 +201,13 @@ class API_EXPORT Lexer */ QString sqlToTokenize; + /** + * @brief Token produced by the lexer previously. + * + * This is used only by the stateful lexer processing (i.e. with getToken()) + */ + TokenPtr prevTokenProcessed; + /** * @brief Current tokenizer position in the sqlToTokenize. * diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/lexer_low_lev.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/lexer_low_lev.cpp index 65f9222..6b76768 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/lexer_low_lev.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/lexer_low_lev.cpp @@ -4,6 +4,7 @@ #include "sqlite3_parse.h" #include "common/utils.h" #include "common/utils_sql.h" +#include "common/unused.h" #include #include @@ -13,19 +14,131 @@ // Low-level lexer routines based on tokenizer from SQLite 3.7.15.2 // +int lexerGetToken(const QString& z, TokenPtr& token, bool tolerant); + bool isIdChar(const QChar& c) { return c.isPrint() && !c.isSpace() && !doesObjectNeedWrapping(c); } -int lexerGetToken(const QString& z, TokenPtr token, int sqliteVersion, bool tolerant) +// From SQLite source code: +/* +** The following three functions are called immediately after the tokenizer +** reads the keywords WINDOW, OVER and FILTER, respectively, to determine +** whether the token should be treated as a keyword or an SQL identifier. +** This cannot be handled by the usual lemon %fallback method, due to +** the ambiguity in some constructions. e.g. +** +** SELECT sum(x) OVER ... +** +** In the above, "OVER" might be a keyword, or it might be an alias for the +** sum(x) expression. If a "%fallback ID OVER" directive were added to +** grammar, then SQLite would always treat "OVER" as an alias, making it +** impossible to call a window-function without a FILTER clause. +** +** WINDOW is treated as a keyword if: +** +** * the following token is an identifier, or a keyword that can fallback +** to being an identifier, and +** * the token after than one is TK_AS. +** +** OVER is a keyword if: +** +** * the previous token was TK_RP, and +** * the next token is either TK_LP or an identifier. +** +** FILTER is a keyword if: +** +** * the previous token was TK_RP, and +** * the next token is TK_LP. +*/ + +int sqlite3ParserFallback(int iToken); // defined in parse.y +int lexerWindowSpecificGetToken(const QString& z, TokenPtr& token, const TokenPtr& prevToken, bool tolerant) +{ + int lgt = 0; + do + lgt += lexerGetToken(z.mid(lgt), token, prevToken, tolerant); + while (token->lemonType == TK3_SPACE); + + if ( + token->lemonType == TK3_ID || + token->lemonType == TK3_STRING || + token->lemonType == TK3_JOIN_KW || + token->lemonType == TK3_WINDOW || + token->lemonType == TK3_OVER || + sqlite3ParserFallback(token->lemonType) == TK3_ID + ) { + token->lemonType = TK3_ID; + token->type = Token::OTHER; + } + + return lgt; +} + +void lexerHandleWindowKeyword(const QString& z, TokenPtr& token, const TokenPtr& prevToken, bool tolerant) { - if (sqliteVersion < 3 || sqliteVersion > 3) + UNUSED(prevToken); + TokenPtr firstAfter = TokenPtr::create(); + int lgt = lexerWindowSpecificGetToken(z, firstAfter, token, tolerant); + if (firstAfter->lemonType != TK3_ID) { - qCritical() << "lexerGetToken() called with invalid sqliteVersion:" << sqliteVersion; - return 0; + token->lemonType = TK3_ID; + token->type = Token::OTHER; + return; + } + + TokenPtr secondAfter = TokenPtr::create(); + lexerWindowSpecificGetToken(z.mid(lgt), secondAfter, firstAfter, tolerant); + if (secondAfter->lemonType != TK3_AS) + { + token->lemonType = TK3_ID; + token->type = Token::OTHER; + return; + } +} + +void lexerHandleOverKeyword(const QString& z, TokenPtr& token, const TokenPtr& prevToken, bool tolerant) +{ + if (prevToken && prevToken->lemonType == TK3_RP) { + TokenPtr firstAfter = TokenPtr::create(); + lexerWindowSpecificGetToken(z, firstAfter, token, tolerant); + if (firstAfter->lemonType == TK3_LP || firstAfter->lemonType == TK3_ID) + return; // remains OVER keyword + } + token->lemonType = TK3_ID; + token->type = Token::OTHER; +} + +void lexerHandleFilterKeyword(const QString& z, TokenPtr& token, const TokenPtr& prevToken, bool tolerant) +{ + if (prevToken && prevToken->lemonType == TK3_RP) { + TokenPtr firstAfter = TokenPtr::create(); + lexerWindowSpecificGetToken(z, firstAfter, token, tolerant); + if (firstAfter->lemonType == TK3_LP) + return; // remains FILTER keyword } + token->lemonType = TK3_ID; + token->type = Token::OTHER; +} + +int lexerGetToken(const QString& z, TokenPtr& token, const TokenPtr& prevToken, int sqliteVersion, bool tolerant) +{ + UNUSED(sqliteVersion); + int lgt = lexerGetToken(z, token, tolerant); + if (token->lemonType == TK3_WINDOW) + lexerHandleWindowKeyword(z.mid(lgt), token, prevToken, tolerant); + else if (token->lemonType == TK3_OVER) + lexerHandleOverKeyword(z.mid(lgt), token, prevToken, tolerant); + else if (token->lemonType == TK3_FILTER) + lexerHandleFilterKeyword(z.mid(lgt), token, prevToken, tolerant); + + return lgt; +} + +int lexerGetToken(const QString& z, TokenPtr& token, bool tolerant) +{ if (tolerant && !token.dynamicCast()) { qCritical() << "lexerGetToken() called with tolerant=true, but not a TolerantToken entity!"; @@ -54,6 +167,12 @@ int lexerGetToken(const QString& z, TokenPtr token, int sqliteVersion, bool tole token->type = Token::COMMENT; return i; } + else if (charAt(z, 1) == '>') + { + token->lemonType = TK3_PTR; + token->type = Token::OPERATOR; + return (charAt(z, 2) == '>') ? 3 : 2; + } token->lemonType = TK3_MINUS; token->type = Token::OPERATOR; return 1; @@ -453,7 +572,7 @@ int lexerGetToken(const QString& z, TokenPtr token, int sqliteVersion, bool tole for (i = 1; isIdChar(charAt(z, i)); i++) {} - token->lemonType = getKeywordId3(z.mid(0, i)); + token->lemonType = getKeywordId3(z.mid(0, i)); if (token->lemonType == TK3_ID) token->type = Token::OTHER; diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/lexer_low_lev.h b/SQLiteStudio3/coreSQLiteStudio/parser/lexer_low_lev.h index d642177..4f2cc35 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/lexer_low_lev.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/lexer_low_lev.h @@ -11,6 +11,7 @@ * @brief Low level tokenizer function used by the Lexer. * @param z Query to tokenize. * @param[out] token Token container to fill with values. Can be also a TolerantToken. + * @param prevToken Previous token returned from this function (if this is a subsequent call), or empty pointer othwewise. It's required for a contextual logic. * @param sqliteVersion SQLite version, for which the tokenizer should work (currently only 3). * Version affects the list of recognized keywords, a BLOB expression and an object name wrapper with the grave accent character (`). * @param tolerant If true, then all multi-line and unfinished tokens (strings, comments) @@ -23,6 +24,6 @@ * Most of the method code was taken from SQLite tokenizer code. It is modified to support both SQLite 3 gramma * and other SQLiteStudio specific features. */ -int lexerGetToken(const QString& z, TokenPtr token, int sqliteVersion, bool tolerant = false); +int lexerGetToken(const QString& z, TokenPtr& token, const TokenPtr& prevToken, int sqliteVersion, bool tolerant = false); #endif // LEXER_LOW_LEV_H diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/parser.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/parser.cpp index b54c786..b80d45a 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/parser.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/parser.cpp @@ -214,7 +214,7 @@ void Parser::expectedTokenLookup(void* pParser) Token::CTX_ALIAS, Token::CTX_TABLE_NEW, Token::CTX_INDEX_NEW, Token::CTX_TRIGGER_NEW, Token::CTX_VIEW_NEW, Token::CTX_COLUMN_NEW, Token::CTX_TRANSACTION, Token::CTX_CONSTRAINT, Token::CTX_COLUMN_TYPE, Token::CTX_OLD_KW, Token::CTX_NEW_KW, - Token::CTX_ROWID_KW, Token::INVALID + Token::CTX_ROWID_KW, Token::CTX_STRICT_KW, Token::INVALID }); for (TokenPtr token : tokenSet) diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/parser_helper_stubs.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/parser_helper_stubs.cpp index 8b14f07..b8534cc 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/parser_helper_stubs.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/parser_helper_stubs.cpp @@ -75,3 +75,13 @@ bool ParserTermOrLiteral::isLiteral() const { return !nameMode; } + +ParserStubCreateTableOption* parserStubFindCreateTableOption(const QList& options, ParserStubCreateTableOption::Type type) +{ + return findFirst(options, [type](auto opt) -> bool {return opt->type == type;}); +} + +ParserStubCreateTableOption::ParserStubCreateTableOption(Type type) : + type(type) +{ +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/parser_helper_stubs.h b/SQLiteStudio3/coreSQLiteStudio/parser/parser_helper_stubs.h index 5160b10..ec71f8b 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/parser_helper_stubs.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/parser_helper_stubs.h @@ -68,6 +68,21 @@ struct ParserStubTransDetails SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; }; +struct ParserStubCreateTableOption +{ + enum Type + { + WITHOUT_ROWID, + STRICT + }; + + ParserStubCreateTableOption(Type type); + + Type type; +}; + +ParserStubCreateTableOption* parserStubFindCreateTableOption(const QList& options, ParserStubCreateTableOption::Type type); + typedef QList ParserCreateTableColumnList; typedef QList ParserCreateTableConstraintList; typedef QList ParserCreateTableColumnConstraintList; @@ -83,6 +98,7 @@ typedef QList ParserSetValueList; typedef QList ParserIndexedColumnList; typedef QList ParserExprNestedList; typedef QList ParserWindowDefList; +typedef QList ParserCreateTableOptionList; /** * @brief Stores parameters for defferable foreign keys. diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/parsercontext.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/parsercontext.cpp index a9eae82..a275882 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/parsercontext.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/parsercontext.cpp @@ -167,9 +167,9 @@ const QList &ParserContext::getErrors() QVariant *ParserContext::handleNumberToken(const QString &tokenValue) { recentNumberIsCandidateForMaxNegative = false; + bool ok; if (tokenValue.startsWith("0x", Qt::CaseInsensitive)) { - bool ok; qint64 i64 = tokenValue.toLongLong(&ok, 16); if (!ok) { @@ -183,10 +183,12 @@ QVariant *ParserContext::handleNumberToken(const QString &tokenValue) recentNumberIsCandidateForMaxNegative = true; return new QVariant(static_cast(0L)); } - else - { - return new QVariant(QVariant(tokenValue).toLongLong()); - } + + QVariant varLong = QVariant(tokenValue).toLongLong(&ok); + if (!ok) + varLong = QVariant(tokenValue).toDouble(); + + return new QVariant(varLong); } bool ParserContext::isCandidateForMaxNegativeNumber() const @@ -196,7 +198,7 @@ bool ParserContext::isCandidateForMaxNegativeNumber() const void ParserContext::cleanUp() { - for (ParserError* err : errors) + for (ParserError*& err : errors) delete err; parsedQueries.clear(); diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.cpp index c980096..7be06fb 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.cpp @@ -22,7 +22,6 @@ #include "parser/ast/sqliteattach.h" #include "parser/ast/sqlitebegintrans.h" #include "parser/ast/sqlitecommittrans.h" -#include "parser/ast/sqlitecopy.h" #include "parser/ast/sqlitecreateindex.h" #include "parser/ast/sqlitecreatetable.h" #include "parser/ast/sqlitecreatetrigger.h" @@ -62,6 +61,7 @@ #define assert(X) Q_ASSERT(X) #define UNUSED_PARAMETER(X) (void)(X) #define DONT_INHERIT_TOKENS(X) noTokenInheritanceFields << X + /* Next is all token values, in a form suitable for use by makeheaders. ** This section will be null unless lemon is run with the -m switch. */ @@ -112,77 +112,80 @@ ** defined, then do no error processing. */ #define YYCODETYPE unsigned short int -#define YYNOCODE 321 +#define YYNOCODE 328 #define YYACTIONTYPE unsigned short int -#define YYWILDCARD 78 +#define YYWILDCARD 80 #define sqlite3_parseTOKENTYPE Token* typedef union { int yyinit; sqlite3_parseTOKENTYPE yy0; - SqliteWith* yy1; - SqliteLimit* yy4; - ParserDeferSubClause* yy9; - SqliteSelect::Core::JoinSource* yy31; - SqliteExpr::LikeOp* yy40; - SqliteWindowDefinition::Window::Frame* yy41; - ParserCreateTableColumnList* yy42; - ParserCreateTableColumnConstraintList* yy51; - ParserResultColumnList* yy53; - SqliteColumnType* yy57; - SqliteWindowDefinition::Window::Frame::Exclude* yy63; - ParserExprList* yy71; - SqliteCreateTrigger::Scope* yy83; - ParserStubTransDetails* yy84; - ParserStubExplain* yy91; - SqliteForeignKey::Condition::Reaction* yy104; - ParserQueryList* yy110; - SqliteCreateTable::Column* yy147; - SqliteCreateTrigger::Event* yy151; - ParserStubAlias* yy200; - ParserSetValueList* yy201; - SqliteSelect::Core::JoinOp* yy221; - ParserIndexedColumnList* yy223; - QVariant* yy229; - SqliteCreateTable::Constraint* yy246; - SqliteFilterOver* yy247; - SqliteFilterOver::Over* yy248; - SqliteWindowDefinition* yy266; - SqliteIndexedColumn* yy268; - SqliteSelect::Core::JoinConstraint* yy295; - ParserWindowDefList* yy299; - SqliteInitially* yy312; - SqliteSelect* yy313; + SqliteCreateTable::Column* yy3; + SqliteFilterOver::Over* yy11; + SqliteWith::CommonTableExpression::AsMode* yy21; + ParserResultColumnList* yy27; + SqliteSortOrder* yy35; + SqliteFilterOver::Filter* yy39; + SqliteQuery* yy41; + ParserDeferSubClause* yy53; + SqliteForeignKey::Condition::Reaction* yy106; + ParserOtherSourceList* yy107; + SqliteIndexedColumn* yy110; + ParserCreateTableConstraintList* yy115; + SqliteCreateTrigger::Time* yy120; + int* yy130; + SqliteConflictAlgo* yy136; + SqliteSelect::CompoundOperator* yy142; + SqliteWindowDefinition::Window::Frame::RangeOrRows* yy143; + SqliteWith::CommonTableExpression* yy146; + SqliteWindowDefinition::Window::Frame* yy149; + ParserFkConditionList* yy156; + SqliteWith* yy161; + SqliteWindowDefinition::Window* yy162; + ParserCteList* yy164; + QStringList* yy173; + SqliteFilterOver* yy181; + SqliteExpr* yy186; + SqliteForeignKey::Condition* yy205; + SqliteSelect::Core::JoinConstraint* yy215; + bool* yy225; + ParserOrderByList* yy226; + SqliteWindowDefinition::Window::Frame::Exclude* yy237; + ParserQueryList* yy240; + SqliteCreateTrigger::Event* yy259; + SqliteColumnType* yy267; + SqliteExpr::LikeOp* yy274; + SqliteWindowDefinition::Window::Frame::Bound* yy285; + SqliteSelect* yy297; + ParserIndexedBy* yy300; + ParserStubInsertOrReplace* yy308; + SqliteNulls* yy315; QString* yy319; - SqliteWindowDefinition::Window* yy334; - SqliteQuery* yy363; - SqliteSelect::CompoundOperator* yy382; - int* yy386; - SqliteFilterOver::Filter* yy397; - SqliteUpsert* yy400; - ParserOrderByList* yy403; - SqliteConflictAlgo* yy418; - SqliteWindowDefinition::Window::Frame::RangeOrRows* yy419; - ParserFullName* yy440; - SqliteSelect::Core::SingleSource* yy441; - SqliteWindowDefinition::Window::Frame::Bound* yy442; - SqliteWith::CommonTableExpression* yy446; - ParserOtherSourceList* yy451; - SqliteCreateTable::Column::Constraint* yy464; - SqliteSelect::Core* yy470; - ParserExprNestedList* yy486; - ParserCreateTableConstraintList* yy493; - ParserStubInsertOrReplace* yy504; - SqliteForeignKey::Condition* yy507; - SqliteExpr* yy512; - SqliteCreateTrigger::Time* yy532; - SqliteSortOrder* yy549; - QStringList* yy575; - SqliteNulls* yy579; - ParserFkConditionList* yy584; - ParserTermOrLiteral* yy590; - ParserIndexedBy* yy592; - ParserCteList* yy593; - bool* yy611; + ParserCreateTableColumnConstraintList* yy323; + SqliteUpsert* yy332; + SqliteLimit* yy360; + SqliteSelect::Core* yy378; + ParserTermOrLiteral* yy380; + ParserCreateTableColumnList* yy390; + QVariant* yy393; + ParserFullName* yy396; + SqliteCreateTable::Constraint* yy400; + SqliteCreateTable::Column::Constraint* yy448; + SqliteSelect::Core::JoinOp* yy449; + ParserCreateTableOptionList* yy455; + SqliteCreateTrigger::Scope* yy456; + ParserStubExplain* yy499; + ParserStubTransDetails* yy512; + ParserExprNestedList* yy522; + ParserWindowDefList* yy525; + SqliteSelect::Core::JoinSource* yy553; + SqliteWindowDefinition* yy562; + SqliteSelect::Core::SingleSource* yy595; + SqliteInitially* yy612; + ParserExprList* yy615; + ParserSetValueList* yy621; + ParserIndexedColumnList* yy627; + ParserStubAlias* yy628; + ParserStubCreateTableOption* yy629; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -191,8 +194,8 @@ typedef union { #define sqlite3_parseARG_PDECL ,ParserContext* parserContext #define sqlite3_parseARG_FETCH ParserContext* parserContext = yypParser->parserContext #define sqlite3_parseARG_STORE yypParser->parserContext = parserContext -#define YYNSTATE 844 -#define YYNRULE 472 +#define YYNSTATE 870 +#define YYNRULE 487 #define YYFALLBACK 1 #define YY_NO_ACTION (YYNSTATE+YYNRULE+2) #define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) @@ -264,806 +267,778 @@ static const YYMINORTYPE yyzerominor = { 0 }; ** shifting non-terminals after a reduce. ** yy_default[] Default action for each state. */ -#define YY_ACTTAB_COUNT (3037) +#define YY_ACTTAB_COUNT (2866) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 510, 625, 284, 836, 655, 288, 841, 64, 65, 509, - /* 10 */ 339, 626, 501, 795, 795, 62, 62, 63, 63, 63, - /* 20 */ 63, 351, 61, 61, 61, 61, 60, 60, 59, 59, - /* 30 */ 59, 58, 253, 485, 1085, 61, 61, 61, 61, 60, - /* 40 */ 60, 59, 59, 59, 58, 253, 835, 290, 794, 1085, - /* 50 */ 71, 506, 63, 63, 63, 63, 40, 61, 61, 61, - /* 60 */ 61, 60, 60, 59, 59, 59, 58, 253, 805, 635, - /* 70 */ 836, 347, 785, 836, 592, 70, 452, 54, 817, 88, - /* 80 */ 338, 835, 835, 59, 59, 59, 58, 253, 1085, 836, - /* 90 */ 806, 223, 836, 835, 51, 52, 693, 1085, 635, 508, - /* 100 */ 325, 53, 1012, 348, 261, 415, 622, 621, 88, 2, - /* 110 */ 1012, 835, 681, 117, 793, 286, 498, 1012, 324, 807, - /* 120 */ 833, 634, 834, 833, 510, 810, 157, 466, 453, 439, - /* 130 */ 436, 435, 793, 809, 808, 807, 501, 1012, 563, 682, - /* 140 */ 682, 611, 486, 746, 1078, 327, 677, 434, 366, 575, - /* 150 */ 1012, 254, 1012, 1012, 396, 403, 1012, 296, 425, 303, - /* 160 */ 749, 1012, 1012, 1012, 1012, 1012, 793, 274, 456, 793, - /* 170 */ 748, 783, 12, 356, 565, 506, 273, 586, 77, 69, - /* 180 */ 39, 138, 69, 1012, 701, 440, 22, 701, 768, 834, - /* 190 */ 833, 805, 834, 833, 14, 836, 785, 111, 1, 489, - /* 200 */ 798, 54, 702, 804, 835, 702, 587, 589, 834, 833, - /* 210 */ 588, 834, 833, 806, 223, 570, 659, 701, 51, 52, - /* 220 */ 701, 137, 508, 649, 706, 53, 1078, 836, 1029, 1029, - /* 230 */ 21, 412, 386, 2, 1078, 172, 117, 328, 793, 705, - /* 240 */ 498, 1078, 157, 807, 833, 439, 436, 435, 510, 810, - /* 250 */ 392, 75, 443, 188, 421, 250, 793, 809, 808, 807, - /* 260 */ 501, 1078, 784, 434, 1178, 275, 935, 835, 648, 56, - /* 270 */ 455, 366, 687, 686, 1078, 130, 1078, 1078, 174, 121, - /* 280 */ 326, 445, 321, 444, 159, 1078, 1078, 1078, 1078, 811, - /* 290 */ 793, 700, 812, 793, 162, 783, 12, 245, 319, 506, - /* 300 */ 60, 60, 59, 59, 59, 58, 253, 1078, 840, 526, - /* 310 */ 836, 650, 651, 513, 834, 833, 563, 805, 836, 798, - /* 320 */ 785, 717, 805, 23, 611, 54, 527, 526, 158, 257, - /* 330 */ 835, 797, 699, 695, 835, 835, 242, 241, 240, 806, - /* 340 */ 223, 523, 51, 52, 806, 208, 834, 833, 508, 53, - /* 350 */ 1063, 356, 564, 508, 384, 526, 171, 2, 1063, 523, - /* 360 */ 548, 77, 793, 786, 498, 1063, 431, 807, 833, 798, - /* 370 */ 578, 329, 836, 810, 723, 579, 466, 451, 729, 465, - /* 380 */ 793, 809, 808, 807, 835, 1063, 789, 523, 786, 547, - /* 390 */ 546, 388, 788, 545, 502, 475, 497, 366, 1063, 729, - /* 400 */ 1063, 1063, 754, 407, 798, 729, 692, 1160, 1160, 1063, - /* 410 */ 329, 1063, 1063, 76, 793, 635, 117, 793, 516, 783, - /* 420 */ 12, 786, 787, 835, 519, 88, 370, 108, 835, 834, - /* 430 */ 833, 1063, 319, 117, 464, 361, 479, 834, 833, 731, - /* 440 */ 836, 1163, 792, 836, 64, 65, 509, 339, 1160, 1160, - /* 450 */ 795, 795, 62, 62, 63, 63, 63, 63, 781, 61, - /* 460 */ 61, 61, 61, 60, 60, 59, 59, 59, 58, 253, - /* 470 */ 836, 807, 729, 722, 361, 55, 792, 810, 58, 253, - /* 480 */ 167, 73, 777, 776, 604, 809, 808, 807, 798, 1160, - /* 490 */ 1160, 834, 833, 729, 64, 65, 509, 339, 66, 729, - /* 500 */ 795, 795, 62, 62, 63, 63, 63, 63, 474, 61, - /* 510 */ 61, 61, 61, 60, 60, 59, 59, 59, 58, 253, - /* 520 */ 836, 770, 836, 798, 712, 771, 64, 65, 509, 339, - /* 530 */ 1160, 1160, 795, 795, 62, 62, 63, 63, 63, 63, - /* 540 */ 765, 61, 61, 61, 61, 60, 60, 59, 59, 59, - /* 550 */ 58, 253, 764, 729, 613, 836, 562, 562, 44, 834, - /* 560 */ 833, 191, 834, 833, 566, 805, 246, 685, 45, 454, - /* 570 */ 482, 365, 1160, 1160, 729, 772, 636, 252, 835, 662, - /* 580 */ 729, 1317, 184, 514, 3, 538, 613, 806, 208, 834, - /* 590 */ 833, 765, 1163, 313, 1163, 661, 508, 393, 835, 357, - /* 600 */ 1269, 742, 792, 764, 539, 540, 835, 1269, 130, 64, - /* 610 */ 65, 509, 339, 1160, 1160, 795, 795, 62, 62, 63, - /* 620 */ 63, 63, 63, 742, 61, 61, 61, 61, 60, 60, - /* 630 */ 59, 59, 59, 58, 253, 818, 792, 769, 158, 834, - /* 640 */ 833, 834, 833, 113, 566, 754, 836, 76, 64, 65, - /* 650 */ 509, 339, 1160, 1160, 795, 795, 62, 62, 63, 63, - /* 660 */ 63, 63, 488, 61, 61, 61, 61, 60, 60, 59, - /* 670 */ 59, 59, 58, 253, 834, 833, 541, 775, 836, 357, - /* 680 */ 1268, 330, 734, 473, 87, 814, 836, 1268, 836, 64, - /* 690 */ 65, 509, 339, 1160, 1160, 795, 795, 62, 62, 63, - /* 700 */ 63, 63, 63, 447, 61, 61, 61, 61, 60, 60, - /* 710 */ 59, 59, 59, 58, 253, 63, 63, 63, 63, 56, - /* 720 */ 61, 61, 61, 61, 60, 60, 59, 59, 59, 58, - /* 730 */ 253, 494, 64, 65, 509, 339, 370, 671, 795, 795, - /* 740 */ 62, 62, 63, 63, 63, 63, 813, 61, 61, 61, - /* 750 */ 61, 60, 60, 59, 59, 59, 58, 253, 811, 1086, - /* 760 */ 571, 812, 190, 488, 309, 834, 833, 312, 591, 64, - /* 770 */ 65, 509, 339, 469, 1086, 795, 795, 62, 62, 63, - /* 780 */ 63, 63, 63, 836, 61, 61, 61, 61, 60, 60, - /* 790 */ 59, 59, 59, 58, 253, 107, 1087, 834, 833, 836, - /* 800 */ 758, 798, 1164, 836, 485, 834, 833, 834, 833, 349, - /* 810 */ 802, 1087, 755, 1086, 78, 836, 468, 835, 64, 65, - /* 820 */ 509, 339, 1086, 571, 795, 795, 62, 62, 63, 63, - /* 830 */ 63, 63, 510, 61, 61, 61, 61, 60, 60, 59, - /* 840 */ 59, 59, 58, 253, 501, 1088, 271, 270, 1214, 836, - /* 850 */ 1087, 78, 603, 493, 791, 64, 65, 509, 339, 1087, - /* 860 */ 1088, 795, 795, 62, 62, 63, 63, 63, 63, 538, - /* 870 */ 61, 61, 61, 61, 60, 60, 59, 59, 59, 58, - /* 880 */ 253, 798, 835, 506, 1167, 777, 776, 20, 539, 398, - /* 890 */ 117, 675, 69, 1022, 580, 581, 409, 701, 337, 1088, - /* 900 */ 78, 753, 834, 833, 785, 391, 780, 779, 1088, 54, - /* 910 */ 8, 835, 167, 576, 745, 702, 805, 620, 834, 833, - /* 920 */ 798, 670, 834, 833, 327, 677, 51, 52, 44, 835, - /* 930 */ 701, 1277, 1277, 53, 834, 833, 390, 112, 806, 223, - /* 940 */ 1229, 2, 1170, 236, 235, 484, 793, 508, 498, 836, - /* 950 */ 282, 807, 833, 1164, 117, 1164, 712, 810, 68, 136, - /* 960 */ 399, 775, 782, 835, 793, 809, 808, 807, 807, 85, - /* 970 */ 714, 836, 835, 504, 810, 466, 467, 42, 576, 775, - /* 980 */ 1277, 1277, 809, 808, 807, 816, 759, 772, 499, 252, - /* 990 */ 86, 19, 767, 643, 442, 832, 366, 470, 793, 111, - /* 1000 */ 798, 793, 830, 783, 12, 27, 64, 65, 509, 339, - /* 1010 */ 836, 831, 795, 795, 62, 62, 63, 63, 63, 63, - /* 1020 */ 836, 61, 61, 61, 61, 60, 60, 59, 59, 59, - /* 1030 */ 58, 253, 50, 1229, 48, 64, 65, 509, 339, 74, - /* 1040 */ 544, 795, 795, 62, 62, 63, 63, 63, 63, 510, - /* 1050 */ 61, 61, 61, 61, 60, 60, 59, 59, 59, 58, - /* 1060 */ 253, 501, 1243, 532, 477, 136, 836, 329, 834, 833, - /* 1070 */ 483, 724, 4, 83, 836, 500, 780, 779, 835, 341, - /* 1080 */ 835, 335, 611, 798, 617, 617, 64, 65, 509, 339, - /* 1090 */ 834, 833, 795, 795, 62, 62, 63, 63, 63, 63, - /* 1100 */ 506, 61, 61, 61, 61, 60, 60, 59, 59, 59, - /* 1110 */ 58, 253, 480, 476, 798, 668, 720, 667, 586, 77, - /* 1120 */ 739, 785, 114, 168, 608, 1172, 54, 532, 6, 834, - /* 1130 */ 833, 265, 310, 756, 836, 744, 311, 481, 615, 834, - /* 1140 */ 833, 78, 608, 51, 52, 835, 1172, 587, 589, 775, - /* 1150 */ 53, 588, 1172, 374, 238, 510, 738, 805, 2, 736, - /* 1160 */ 271, 270, 836, 793, 689, 498, 836, 501, 807, 833, - /* 1170 */ 835, 56, 413, 247, 810, 117, 450, 724, 4, 806, - /* 1180 */ 223, 793, 809, 808, 807, 834, 833, 836, 508, 5, - /* 1190 */ 535, 836, 531, 834, 833, 717, 1172, 414, 836, 777, - /* 1200 */ 776, 729, 798, 537, 1172, 536, 506, 844, 835, 733, - /* 1210 */ 416, 1172, 805, 371, 761, 793, 466, 457, 793, 730, - /* 1220 */ 783, 12, 729, 78, 732, 835, 338, 785, 729, 805, - /* 1230 */ 18, 1172, 54, 798, 806, 118, 395, 366, 597, 835, - /* 1240 */ 718, 786, 835, 508, 1172, 641, 1172, 1172, 549, 51, - /* 1250 */ 52, 806, 118, 834, 833, 1172, 53, 1172, 1172, 737, - /* 1260 */ 508, 317, 17, 305, 2, 381, 786, 259, 111, 793, - /* 1270 */ 837, 498, 666, 713, 807, 833, 835, 1172, 843, 3, - /* 1280 */ 810, 834, 833, 487, 432, 834, 833, 793, 809, 808, - /* 1290 */ 807, 111, 754, 836, 529, 665, 713, 836, 406, 786, - /* 1300 */ 170, 602, 1165, 1277, 1277, 594, 834, 833, 110, 754, - /* 1310 */ 834, 833, 338, 842, 549, 640, 639, 834, 833, 602, - /* 1320 */ 707, 793, 529, 594, 793, 835, 783, 12, 631, 64, - /* 1330 */ 65, 509, 339, 492, 757, 795, 795, 62, 62, 63, - /* 1340 */ 63, 63, 63, 704, 61, 61, 61, 61, 60, 60, - /* 1350 */ 59, 59, 59, 58, 253, 64, 65, 509, 339, 298, - /* 1360 */ 16, 795, 795, 62, 62, 63, 63, 63, 63, 11, - /* 1370 */ 61, 61, 61, 61, 60, 60, 59, 59, 59, 58, - /* 1380 */ 253, 395, 64, 65, 509, 339, 743, 4, 795, 795, - /* 1390 */ 62, 62, 63, 63, 63, 63, 117, 61, 61, 61, - /* 1400 */ 61, 60, 60, 59, 59, 59, 58, 253, 422, 728, - /* 1410 */ 4, 559, 834, 833, 166, 168, 834, 833, 56, 496, - /* 1420 */ 757, 836, 727, 4, 64, 65, 509, 339, 49, 559, - /* 1430 */ 795, 795, 62, 62, 63, 63, 63, 63, 165, 61, - /* 1440 */ 61, 61, 61, 60, 60, 59, 59, 59, 58, 253, - /* 1450 */ 607, 836, 667, 709, 64, 65, 509, 339, 842, 676, - /* 1460 */ 795, 795, 62, 62, 63, 63, 63, 63, 708, 61, - /* 1470 */ 61, 61, 61, 60, 60, 59, 59, 59, 58, 253, - /* 1480 */ 164, 46, 725, 4, 64, 65, 509, 339, 163, 674, - /* 1490 */ 795, 795, 62, 62, 63, 63, 63, 63, 696, 61, - /* 1500 */ 61, 61, 61, 60, 60, 59, 59, 59, 58, 253, - /* 1510 */ 246, 685, 836, 454, 64, 65, 509, 339, 124, 657, - /* 1520 */ 795, 795, 62, 62, 63, 63, 63, 63, 460, 61, - /* 1530 */ 61, 61, 61, 60, 60, 59, 59, 59, 58, 253, - /* 1540 */ 834, 833, 419, 691, 64, 65, 509, 339, 458, 189, - /* 1550 */ 795, 795, 62, 62, 63, 63, 63, 63, 7, 61, - /* 1560 */ 61, 61, 61, 60, 60, 59, 59, 59, 58, 253, - /* 1570 */ 834, 833, 726, 4, 64, 65, 509, 339, 690, 316, - /* 1580 */ 795, 795, 62, 62, 63, 63, 63, 63, 15, 61, - /* 1590 */ 61, 61, 61, 60, 60, 59, 59, 59, 58, 253, - /* 1600 */ 612, 1193, 64, 65, 509, 339, 274, 456, 795, 795, - /* 1610 */ 62, 62, 63, 63, 63, 63, 825, 61, 61, 61, - /* 1620 */ 61, 60, 60, 59, 59, 59, 58, 253, 574, 688, - /* 1630 */ 836, 834, 833, 64, 65, 509, 339, 836, 28, 795, - /* 1640 */ 795, 62, 62, 63, 63, 63, 63, 684, 61, 61, - /* 1650 */ 61, 61, 60, 60, 59, 59, 59, 58, 253, 836, - /* 1660 */ 1244, 64, 65, 509, 339, 683, 836, 795, 795, 62, - /* 1670 */ 62, 63, 63, 63, 63, 510, 61, 61, 61, 61, - /* 1680 */ 60, 60, 59, 59, 59, 58, 253, 501, 1242, 420, - /* 1690 */ 477, 64, 65, 509, 339, 116, 168, 795, 795, 62, - /* 1700 */ 62, 63, 63, 63, 63, 798, 61, 61, 61, 61, - /* 1710 */ 60, 60, 59, 59, 59, 58, 253, 123, 600, 280, - /* 1720 */ 302, 255, 679, 805, 258, 168, 506, 421, 297, 401, - /* 1730 */ 337, 279, 115, 835, 353, 44, 835, 448, 337, 476, - /* 1740 */ 835, 823, 835, 835, 836, 806, 208, 785, 836, 834, - /* 1750 */ 833, 835, 54, 37, 508, 25, 834, 833, 293, 133, - /* 1760 */ 300, 127, 525, 821, 299, 44, 350, 836, 260, 51, - /* 1770 */ 52, 352, 178, 835, 263, 805, 53, 104, 834, 833, - /* 1780 */ 78, 510, 122, 441, 2, 834, 833, 333, 835, 793, - /* 1790 */ 525, 498, 805, 501, 807, 833, 477, 806, 223, 805, - /* 1800 */ 810, 713, 256, 754, 568, 835, 508, 793, 809, 808, - /* 1810 */ 807, 358, 835, 556, 806, 223, 167, 354, 759, 176, - /* 1820 */ 956, 806, 118, 508, 798, 78, 766, 623, 647, 836, - /* 1830 */ 508, 36, 506, 713, 267, 836, 638, 331, 438, 717, - /* 1840 */ 731, 793, 82, 247, 793, 478, 783, 12, 703, 836, - /* 1850 */ 318, 266, 835, 785, 433, 366, 836, 367, 54, 584, - /* 1860 */ 712, 836, 332, 834, 833, 81, 634, 834, 833, 24, - /* 1870 */ 169, 838, 366, 160, 449, 51, 52, 80, 826, 754, - /* 1880 */ 264, 517, 53, 824, 835, 289, 834, 833, 156, 561, - /* 1890 */ 2, 835, 324, 397, 717, 793, 835, 498, 835, 517, - /* 1900 */ 807, 833, 698, 616, 10, 510, 810, 835, 429, 380, - /* 1910 */ 187, 152, 822, 793, 809, 808, 807, 501, 247, 101, - /* 1920 */ 99, 64, 38, 509, 339, 835, 35, 795, 795, 62, - /* 1930 */ 62, 63, 63, 63, 63, 836, 61, 61, 61, 61, - /* 1940 */ 60, 60, 59, 59, 59, 58, 253, 793, 834, 833, - /* 1950 */ 793, 713, 783, 12, 834, 833, 506, 34, 33, 836, - /* 1960 */ 819, 805, 32, 555, 379, 98, 805, 644, 834, 833, - /* 1970 */ 552, 836, 805, 835, 835, 834, 833, 785, 717, 835, - /* 1980 */ 834, 833, 54, 806, 223, 835, 378, 697, 806, 223, - /* 1990 */ 836, 835, 508, 376, 806, 118, 555, 508, 694, 51, - /* 2000 */ 52, 97, 653, 508, 836, 805, 53, 595, 596, 590, - /* 2010 */ 585, 510, 410, 337, 2, 656, 552, 247, 835, 793, - /* 2020 */ 387, 498, 805, 501, 807, 833, 835, 806, 223, 836, - /* 2030 */ 810, 583, 105, 168, 805, 835, 508, 793, 809, 808, - /* 2040 */ 807, 366, 577, 377, 806, 118, 490, 835, 408, 336, - /* 2050 */ 147, 383, 754, 508, 834, 833, 806, 216, 295, 13, - /* 2060 */ 815, 292, 506, 181, 471, 508, 192, 624, 558, 92, - /* 2070 */ 534, 793, 619, 835, 793, 533, 783, 12, 834, 833, - /* 2080 */ 394, 337, 287, 785, 836, 366, 557, 803, 54, 139, - /* 2090 */ 834, 833, 515, 375, 835, 835, 90, 247, 790, 262, - /* 2100 */ 835, 759, 754, 582, 243, 51, 52, 109, 836, 834, - /* 2110 */ 833, 835, 53, 503, 754, 239, 369, 510, 368, 382, - /* 2120 */ 2, 315, 610, 834, 833, 793, 835, 498, 805, 501, - /* 2130 */ 807, 833, 839, 179, 805, 180, 810, 829, 827, 805, - /* 2140 */ 828, 835, 719, 793, 809, 808, 807, 835, 834, 833, - /* 2150 */ 806, 194, 835, 836, 177, 835, 806, 204, 836, 508, - /* 2160 */ 512, 806, 226, 285, 554, 508, 430, 553, 506, 759, - /* 2170 */ 508, 511, 715, 836, 805, 614, 835, 793, 836, 805, - /* 2180 */ 793, 72, 783, 12, 836, 835, 103, 835, 836, 785, - /* 2190 */ 175, 820, 835, 836, 54, 801, 806, 224, 800, 173, - /* 2200 */ 710, 806, 232, 834, 833, 508, 274, 836, 754, 836, - /* 2210 */ 508, 51, 52, 835, 754, 343, 606, 23, 53, 754, - /* 2220 */ 43, 569, 472, 510, 342, 805, 2, 834, 833, 67, - /* 2230 */ 836, 793, 186, 498, 253, 501, 807, 833, 835, 799, - /* 2240 */ 805, 836, 810, 752, 495, 805, 84, 806, 231, 793, - /* 2250 */ 809, 808, 807, 835, 754, 251, 508, 283, 835, 754, - /* 2260 */ 269, 550, 806, 233, 268, 47, 334, 806, 237, 57, - /* 2270 */ 835, 508, 834, 833, 506, 751, 508, 834, 833, 488, - /* 2280 */ 750, 161, 836, 793, 747, 601, 793, 389, 783, 12, - /* 2290 */ 1168, 741, 834, 833, 835, 785, 244, 834, 833, 134, - /* 2300 */ 54, 520, 463, 834, 833, 754, 756, 834, 833, 678, - /* 2310 */ 522, 735, 834, 833, 135, 835, 572, 51, 52, 459, - /* 2320 */ 754, 320, 835, 654, 53, 754, 834, 833, 834, 833, - /* 2330 */ 456, 805, 2, 1170, 835, 680, 835, 793, 551, 498, - /* 2340 */ 160, 560, 807, 833, 835, 663, 669, 510, 810, 834, - /* 2350 */ 833, 542, 664, 806, 272, 793, 809, 808, 807, 501, - /* 2360 */ 834, 833, 508, 446, 65, 509, 339, 385, 322, 795, - /* 2370 */ 795, 62, 62, 63, 63, 63, 63, 805, 61, 61, - /* 2380 */ 61, 61, 60, 60, 59, 59, 59, 58, 253, 793, - /* 2390 */ 835, 660, 793, 276, 783, 12, 760, 372, 506, 806, - /* 2400 */ 340, 834, 833, 632, 659, 805, 835, 716, 508, 314, - /* 2410 */ 307, 754, 805, 404, 658, 637, 835, 618, 835, 785, - /* 2420 */ 630, 360, 835, 835, 54, 835, 835, 806, 230, 155, - /* 2430 */ 629, 521, 628, 346, 806, 203, 508, 805, 507, 627, - /* 2440 */ 154, 51, 52, 508, 835, 308, 835, 359, 53, 30, - /* 2450 */ 835, 835, 102, 510, 428, 132, 2, 754, 835, 806, - /* 2460 */ 221, 793, 423, 498, 153, 501, 807, 833, 508, 249, - /* 2470 */ 294, 805, 810, 291, 281, 835, 805, 411, 277, 793, - /* 2480 */ 809, 808, 807, 835, 835, 754, 835, 835, 278, 835, - /* 2490 */ 306, 835, 754, 806, 225, 426, 31, 151, 806, 364, - /* 2500 */ 609, 835, 508, 131, 506, 100, 150, 508, 605, 149, - /* 2510 */ 185, 106, 418, 793, 599, 148, 793, 754, 783, 12, - /* 2520 */ 417, 593, 129, 128, 405, 785, 402, 146, 573, 96, - /* 2530 */ 54, 145, 400, 95, 144, 94, 143, 93, 142, 30, - /* 2540 */ 543, 141, 29, 530, 518, 26, 126, 51, 52, 373, - /* 2550 */ 528, 754, 524, 805, 53, 125, 754, 140, 805, 193, - /* 2560 */ 79, 778, 2, 344, 774, 763, 835, 793, 721, 498, - /* 2570 */ 491, 835, 834, 833, 711, 806, 363, 183, 810, 323, - /* 2580 */ 806, 362, 182, 655, 508, 793, 809, 808, 807, 508, - /* 2590 */ 437, 234, 598, 304, 424, 509, 339, 301, 355, 795, - /* 2600 */ 795, 62, 62, 63, 63, 63, 63, 9, 61, 61, - /* 2610 */ 61, 61, 60, 60, 59, 59, 59, 58, 253, 793, - /* 2620 */ 796, 773, 793, 762, 783, 12, 642, 805, 248, 345, - /* 2630 */ 427, 41, 505, 754, 805, 740, 652, 91, 754, 567, - /* 2640 */ 835, 805, 646, 78, 673, 645, 836, 835, 672, 806, - /* 2650 */ 220, 1318, 633, 805, 835, 1318, 806, 206, 508, 1318, - /* 2660 */ 805, 1318, 1318, 806, 219, 508, 835, 805, 1318, 1318, - /* 2670 */ 1318, 1318, 508, 835, 1318, 806, 218, 1318, 1318, 805, - /* 2680 */ 835, 805, 806, 205, 508, 1318, 1318, 1318, 1318, 806, - /* 2690 */ 202, 508, 835, 805, 835, 1318, 1318, 1318, 508, 1318, - /* 2700 */ 1318, 806, 119, 806, 201, 1318, 835, 754, 1318, 1318, - /* 2710 */ 508, 1318, 508, 1318, 754, 806, 199, 1318, 1318, 805, - /* 2720 */ 1318, 754, 1318, 1318, 508, 1318, 1318, 1318, 1318, 1318, - /* 2730 */ 1318, 1318, 835, 754, 1318, 1318, 1318, 1318, 1318, 1318, - /* 2740 */ 754, 806, 227, 1318, 1318, 1318, 1318, 754, 1318, 1318, - /* 2750 */ 508, 1318, 1318, 805, 1318, 1318, 805, 1318, 805, 754, - /* 2760 */ 1318, 754, 1318, 1318, 1318, 1318, 835, 1318, 1318, 835, - /* 2770 */ 1318, 835, 805, 754, 1318, 806, 229, 1318, 806, 222, - /* 2780 */ 806, 228, 1318, 1318, 508, 835, 1318, 508, 805, 508, - /* 2790 */ 1318, 1318, 1318, 1318, 806, 217, 1318, 1318, 1318, 754, - /* 2800 */ 805, 835, 1318, 508, 805, 1318, 1318, 1318, 1318, 1318, - /* 2810 */ 806, 214, 1318, 835, 1318, 1318, 1318, 835, 1318, 508, - /* 2820 */ 1318, 1318, 806, 198, 805, 1318, 806, 197, 1318, 1318, - /* 2830 */ 1318, 508, 1318, 754, 1318, 508, 754, 835, 754, 1318, - /* 2840 */ 805, 1318, 1318, 1318, 1318, 1318, 806, 196, 1318, 1318, - /* 2850 */ 1318, 1318, 754, 835, 805, 508, 805, 1318, 1318, 1318, - /* 2860 */ 805, 1318, 806, 195, 1318, 1318, 1318, 835, 754, 835, - /* 2870 */ 1318, 508, 1318, 835, 1318, 805, 806, 207, 806, 212, - /* 2880 */ 754, 1318, 806, 211, 754, 508, 1318, 508, 835, 1318, - /* 2890 */ 805, 508, 805, 1318, 805, 1318, 1318, 806, 120, 1318, - /* 2900 */ 1318, 1318, 1318, 835, 754, 835, 508, 835, 1318, 1318, - /* 2910 */ 1318, 1318, 806, 210, 806, 209, 806, 215, 1318, 805, - /* 2920 */ 754, 508, 1318, 508, 1318, 508, 1318, 1318, 1318, 1318, - /* 2930 */ 1318, 1318, 835, 1318, 754, 1318, 754, 1318, 1318, 1318, - /* 2940 */ 754, 806, 213, 1318, 1318, 1318, 1318, 1318, 1318, 1318, - /* 2950 */ 508, 1318, 1318, 805, 1318, 754, 805, 1318, 1318, 1318, - /* 2960 */ 1318, 1318, 1318, 1318, 1318, 1318, 835, 1318, 1318, 835, - /* 2970 */ 754, 1318, 754, 1318, 754, 806, 200, 1318, 806, 89, - /* 2980 */ 1318, 1318, 1318, 1318, 508, 1318, 1318, 462, 1318, 1318, - /* 2990 */ 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 754, - /* 3000 */ 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, - /* 3010 */ 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, - /* 3020 */ 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, - /* 3030 */ 1318, 1318, 1318, 754, 1318, 1318, 754, + /* 0 */ 530, 162, 431, 771, 456, 453, 452, 27, 67, 68, + /* 10 */ 529, 282, 521, 770, 818, 818, 65, 65, 66, 66, + /* 20 */ 66, 66, 451, 64, 64, 64, 64, 63, 63, 62, + /* 30 */ 62, 62, 61, 59, 263, 464, 1230, 807, 67, 68, + /* 40 */ 529, 282, 58, 116, 818, 818, 65, 65, 66, 66, + /* 50 */ 66, 66, 526, 64, 64, 64, 64, 63, 63, 62, + /* 60 */ 62, 62, 61, 59, 263, 681, 1280, 62, 62, 62, + /* 70 */ 61, 59, 263, 808, 432, 66, 66, 66, 66, 56, + /* 80 */ 64, 64, 64, 64, 63, 63, 62, 62, 62, 61, + /* 90 */ 59, 263, 834, 862, 14, 835, 53, 54, 715, 704, + /* 100 */ 704, 353, 828, 619, 55, 1042, 508, 633, 295, 781, + /* 110 */ 530, 299, 2, 1042, 343, 861, 861, 816, 1042, 354, + /* 120 */ 867, 518, 521, 830, 859, 829, 230, 672, 673, 833, + /* 130 */ 1108, 821, 324, 861, 528, 327, 816, 832, 831, 830, + /* 140 */ 406, 1042, 39, 143, 608, 80, 862, 128, 515, 291, + /* 150 */ 472, 265, 709, 708, 1042, 301, 1042, 1042, 828, 585, + /* 160 */ 1042, 290, 526, 366, 368, 1042, 1042, 1042, 1042, 1042, + /* 170 */ 1042, 816, 861, 609, 816, 611, 806, 12, 610, 364, + /* 180 */ 560, 829, 230, 808, 510, 506, 163, 351, 1042, 56, + /* 190 */ 528, 782, 132, 82, 861, 371, 587, 365, 21, 428, + /* 200 */ 561, 562, 367, 183, 805, 72, 53, 54, 109, 89, + /* 210 */ 723, 81, 497, 127, 55, 1108, 860, 859, 483, 468, + /* 220 */ 530, 560, 2, 1108, 862, 297, 448, 816, 1108, 724, + /* 230 */ 791, 518, 521, 830, 859, 861, 107, 809, 498, 833, + /* 240 */ 381, 561, 414, 8, 723, 172, 816, 832, 831, 830, + /* 250 */ 369, 1108, 181, 745, 821, 1059, 1059, 141, 72, 52, + /* 260 */ 758, 50, 809, 723, 1108, 502, 1108, 1108, 688, 860, + /* 270 */ 859, 861, 526, 563, 798, 1108, 1108, 1108, 1108, 861, + /* 280 */ 1108, 816, 724, 78, 816, 194, 806, 12, 386, 862, + /* 290 */ 734, 382, 687, 808, 334, 79, 809, 723, 1108, 56, + /* 300 */ 64, 64, 64, 64, 63, 63, 62, 62, 62, 61, + /* 310 */ 59, 263, 751, 858, 415, 798, 53, 54, 790, 614, + /* 320 */ 856, 722, 828, 116, 55, 1093, 742, 167, 413, 857, + /* 330 */ 469, 173, 2, 1093, 751, 42, 861, 816, 1093, 809, + /* 340 */ 751, 518, 821, 830, 859, 829, 230, 860, 859, 833, + /* 350 */ 1358, 190, 535, 3, 528, 487, 816, 832, 831, 830, + /* 360 */ 815, 1093, 711, 759, 809, 721, 717, 58, 122, 505, + /* 370 */ 522, 500, 746, 4, 1093, 817, 1093, 1093, 767, 828, + /* 380 */ 1197, 1197, 483, 470, 600, 1093, 828, 1093, 1093, 601, + /* 390 */ 1093, 816, 91, 861, 816, 815, 806, 12, 809, 44, + /* 400 */ 861, 113, 829, 215, 381, 307, 442, 317, 1093, 829, + /* 410 */ 123, 528, 860, 859, 1200, 284, 473, 423, 528, 67, + /* 420 */ 68, 529, 282, 1197, 1197, 818, 818, 65, 65, 66, + /* 430 */ 66, 66, 66, 74, 64, 64, 64, 64, 63, 63, + /* 440 */ 62, 62, 62, 61, 59, 263, 63, 63, 62, 62, + /* 450 */ 62, 61, 59, 263, 800, 799, 172, 332, 504, 73, + /* 460 */ 626, 775, 116, 1197, 1197, 821, 389, 247, 775, 67, + /* 470 */ 68, 529, 282, 69, 264, 818, 818, 65, 65, 66, + /* 480 */ 66, 66, 66, 412, 64, 64, 64, 64, 63, 63, + /* 490 */ 62, 62, 62, 61, 59, 263, 496, 58, 142, 753, + /* 500 */ 588, 734, 67, 68, 529, 282, 1197, 1197, 818, 818, + /* 510 */ 65, 65, 66, 66, 66, 66, 862, 64, 64, 64, + /* 520 */ 64, 63, 63, 62, 62, 62, 61, 59, 263, 788, + /* 530 */ 449, 703, 828, 830, 340, 116, 372, 1307, 177, 833, + /* 540 */ 657, 787, 1307, 45, 505, 363, 861, 832, 831, 830, + /* 550 */ 643, 92, 339, 254, 861, 829, 230, 407, 803, 802, + /* 560 */ 285, 1197, 1197, 380, 528, 342, 699, 795, 658, 261, + /* 570 */ 1200, 81, 1200, 66, 66, 66, 66, 40, 64, 64, + /* 580 */ 64, 64, 63, 63, 62, 62, 62, 61, 59, 263, + /* 590 */ 693, 862, 483, 484, 281, 280, 862, 1201, 286, 836, + /* 600 */ 67, 68, 529, 282, 1197, 1197, 818, 818, 65, 65, + /* 610 */ 66, 66, 66, 66, 381, 64, 64, 64, 64, 63, + /* 620 */ 63, 62, 62, 62, 61, 59, 263, 862, 1, 751, + /* 630 */ 430, 141, 798, 827, 812, 800, 799, 788, 647, 860, + /* 640 */ 859, 677, 328, 1197, 1197, 861, 409, 492, 648, 787, + /* 650 */ 87, 751, 67, 68, 529, 282, 861, 751, 818, 818, + /* 660 */ 65, 65, 66, 66, 66, 66, 862, 64, 64, 64, + /* 670 */ 64, 63, 63, 62, 62, 62, 61, 59, 263, 251, + /* 680 */ 250, 249, 67, 68, 529, 282, 1197, 1197, 818, 818, + /* 690 */ 65, 65, 66, 66, 66, 66, 176, 64, 64, 64, + /* 700 */ 64, 63, 63, 62, 62, 62, 61, 59, 263, 764, + /* 710 */ 502, 135, 777, 656, 860, 859, 657, 1266, 162, 860, + /* 720 */ 859, 456, 453, 452, 861, 811, 840, 92, 843, 357, + /* 730 */ 861, 350, 764, 255, 707, 531, 471, 841, 163, 451, + /* 740 */ 839, 684, 861, 584, 584, 467, 746, 4, 665, 459, + /* 750 */ 860, 859, 271, 1201, 644, 1201, 862, 683, 67, 68, + /* 760 */ 529, 282, 23, 491, 818, 818, 65, 65, 66, 66, + /* 770 */ 66, 66, 486, 64, 64, 64, 64, 63, 63, 62, + /* 780 */ 62, 62, 61, 59, 263, 67, 68, 529, 282, 860, + /* 790 */ 859, 818, 818, 65, 65, 66, 66, 66, 66, 751, + /* 800 */ 64, 64, 64, 64, 63, 63, 62, 62, 62, 61, + /* 810 */ 59, 263, 530, 1266, 866, 862, 636, 485, 344, 534, + /* 820 */ 457, 751, 503, 768, 521, 344, 180, 751, 597, 466, + /* 830 */ 633, 697, 861, 482, 419, 67, 68, 529, 282, 861, + /* 840 */ 386, 818, 818, 65, 65, 66, 66, 66, 66, 810, + /* 850 */ 64, 64, 64, 64, 63, 63, 62, 62, 62, 61, + /* 860 */ 59, 263, 821, 828, 526, 342, 699, 570, 80, 401, + /* 870 */ 353, 122, 119, 61, 59, 263, 842, 861, 585, 860, + /* 880 */ 859, 81, 744, 376, 861, 808, 829, 223, 692, 481, + /* 890 */ 376, 56, 135, 259, 804, 528, 569, 592, 568, 325, + /* 900 */ 283, 567, 633, 326, 965, 520, 803, 802, 53, 54, + /* 910 */ 22, 246, 245, 861, 371, 586, 55, 179, 126, 341, + /* 920 */ 462, 336, 461, 164, 2, 1207, 537, 71, 57, 816, + /* 930 */ 602, 603, 425, 518, 79, 830, 859, 334, 830, 608, + /* 940 */ 80, 833, 588, 165, 833, 775, 793, 1215, 816, 832, + /* 950 */ 831, 830, 832, 831, 830, 795, 519, 261, 197, 862, + /* 960 */ 789, 438, 1202, 354, 1315, 1315, 173, 690, 609, 689, + /* 970 */ 611, 255, 707, 610, 471, 613, 172, 861, 372, 1306, + /* 980 */ 798, 293, 821, 816, 1306, 821, 816, 792, 806, 12, + /* 990 */ 67, 68, 529, 282, 429, 861, 818, 818, 65, 65, + /* 1000 */ 66, 66, 66, 66, 435, 64, 64, 64, 64, 63, + /* 1010 */ 63, 62, 62, 62, 61, 59, 263, 67, 68, 529, + /* 1020 */ 282, 734, 81, 818, 818, 65, 65, 66, 66, 66, + /* 1030 */ 66, 780, 64, 64, 64, 64, 63, 63, 62, 62, + /* 1040 */ 62, 61, 59, 263, 67, 68, 529, 282, 662, 661, + /* 1050 */ 818, 818, 65, 65, 66, 66, 66, 66, 1251, 64, + /* 1060 */ 64, 64, 64, 63, 63, 62, 62, 62, 61, 59, + /* 1070 */ 263, 779, 512, 566, 778, 639, 639, 870, 67, 68, + /* 1080 */ 529, 282, 860, 859, 818, 818, 65, 65, 66, 66, + /* 1090 */ 66, 66, 51, 64, 64, 64, 64, 63, 63, 62, + /* 1100 */ 62, 62, 61, 59, 263, 821, 411, 794, 862, 67, + /* 1110 */ 68, 529, 282, 118, 698, 818, 818, 65, 65, 66, + /* 1120 */ 66, 66, 66, 437, 64, 64, 64, 64, 63, 63, + /* 1130 */ 62, 62, 62, 61, 59, 263, 834, 861, 862, 835, + /* 1140 */ 196, 67, 68, 529, 282, 696, 48, 818, 818, 65, + /* 1150 */ 65, 66, 66, 66, 66, 776, 64, 64, 64, 64, + /* 1160 */ 63, 63, 62, 62, 62, 61, 59, 263, 1204, 869, + /* 1170 */ 3, 436, 67, 68, 529, 282, 173, 679, 818, 818, + /* 1180 */ 65, 65, 66, 66, 66, 66, 868, 64, 64, 64, + /* 1190 */ 64, 63, 63, 62, 62, 62, 61, 59, 263, 774, + /* 1200 */ 267, 862, 622, 67, 68, 529, 282, 173, 195, 818, + /* 1210 */ 818, 65, 65, 66, 66, 66, 66, 735, 64, 64, + /* 1220 */ 64, 64, 63, 63, 62, 62, 62, 61, 59, 263, + /* 1230 */ 507, 860, 859, 309, 67, 68, 529, 282, 44, 331, + /* 1240 */ 818, 818, 65, 65, 66, 66, 66, 66, 862, 64, + /* 1250 */ 64, 64, 64, 63, 63, 62, 62, 62, 61, 59, + /* 1260 */ 263, 860, 859, 671, 729, 862, 67, 68, 529, 282, + /* 1270 */ 720, 28, 818, 818, 65, 65, 66, 66, 66, 66, + /* 1280 */ 735, 64, 64, 64, 64, 63, 63, 62, 62, 62, + /* 1290 */ 61, 59, 263, 460, 1281, 67, 68, 529, 282, 862, + /* 1300 */ 719, 818, 818, 65, 65, 66, 66, 66, 66, 670, + /* 1310 */ 64, 64, 64, 64, 63, 63, 62, 62, 62, 61, + /* 1320 */ 59, 263, 530, 1279, 860, 859, 304, 726, 320, 354, + /* 1330 */ 735, 44, 269, 81, 521, 837, 47, 494, 67, 68, + /* 1340 */ 529, 282, 861, 861, 818, 818, 65, 65, 66, 66, + /* 1350 */ 66, 66, 862, 64, 64, 64, 64, 63, 63, 62, + /* 1360 */ 62, 62, 61, 59, 263, 598, 46, 828, 530, 606, + /* 1370 */ 828, 860, 859, 590, 526, 509, 20, 716, 373, 517, + /* 1380 */ 521, 861, 554, 494, 861, 821, 862, 493, 860, 859, + /* 1390 */ 829, 230, 828, 829, 230, 808, 843, 256, 399, 528, + /* 1400 */ 501, 56, 528, 6, 862, 846, 861, 437, 44, 986, + /* 1410 */ 861, 112, 117, 122, 645, 829, 215, 122, 53, 54, + /* 1420 */ 526, 861, 860, 859, 528, 77, 55, 408, 417, 862, + /* 1430 */ 483, 474, 598, 493, 2, 751, 862, 766, 516, 816, + /* 1440 */ 778, 808, 861, 518, 513, 830, 859, 56, 554, 381, + /* 1450 */ 530, 833, 381, 499, 539, 828, 441, 751, 816, 832, + /* 1460 */ 831, 830, 521, 751, 53, 54, 22, 862, 861, 861, + /* 1470 */ 625, 629, 55, 689, 775, 860, 859, 862, 829, 215, + /* 1480 */ 2, 864, 122, 19, 266, 816, 739, 528, 551, 518, + /* 1490 */ 761, 830, 859, 816, 862, 861, 816, 833, 806, 12, + /* 1500 */ 861, 828, 526, 548, 816, 832, 831, 830, 821, 860, + /* 1510 */ 859, 345, 756, 642, 755, 861, 551, 828, 637, 81, + /* 1520 */ 820, 549, 548, 808, 829, 230, 5, 860, 859, 56, + /* 1530 */ 311, 861, 760, 528, 862, 765, 4, 775, 862, 816, + /* 1540 */ 829, 230, 816, 863, 806, 12, 53, 54, 754, 528, + /* 1550 */ 548, 122, 860, 859, 55, 752, 122, 403, 18, 860, + /* 1560 */ 859, 277, 2, 281, 280, 728, 821, 816, 122, 490, + /* 1570 */ 862, 518, 346, 830, 859, 753, 735, 276, 316, 833, + /* 1580 */ 727, 387, 268, 381, 750, 4, 816, 832, 831, 830, + /* 1590 */ 860, 859, 861, 17, 630, 557, 16, 256, 344, 381, + /* 1600 */ 860, 859, 1315, 1315, 800, 799, 821, 530, 559, 862, + /* 1610 */ 558, 593, 861, 630, 749, 4, 553, 860, 859, 521, + /* 1620 */ 411, 816, 494, 725, 816, 11, 806, 12, 67, 38, + /* 1630 */ 529, 282, 284, 473, 818, 818, 65, 65, 66, 66, + /* 1640 */ 66, 66, 821, 64, 64, 64, 64, 63, 63, 62, + /* 1650 */ 62, 62, 61, 59, 263, 828, 115, 860, 859, 526, + /* 1660 */ 348, 860, 859, 275, 747, 4, 862, 828, 422, 861, + /* 1670 */ 825, 353, 495, 735, 547, 58, 593, 851, 829, 123, + /* 1680 */ 808, 861, 828, 852, 828, 861, 56, 528, 171, 821, + /* 1690 */ 829, 123, 862, 860, 859, 353, 861, 861, 861, 528, + /* 1700 */ 868, 784, 547, 53, 54, 829, 230, 829, 123, 861, + /* 1710 */ 270, 55, 748, 4, 528, 730, 528, 256, 90, 2, + /* 1720 */ 274, 170, 347, 169, 816, 168, 635, 175, 518, 821, + /* 1730 */ 830, 859, 860, 859, 398, 862, 833, 775, 718, 174, + /* 1740 */ 129, 657, 402, 816, 832, 831, 830, 477, 828, 775, + /* 1750 */ 862, 541, 92, 313, 862, 861, 392, 312, 713, 635, + /* 1760 */ 850, 782, 861, 475, 381, 821, 775, 861, 739, 710, + /* 1770 */ 7, 829, 123, 849, 861, 712, 862, 663, 816, 256, + /* 1780 */ 528, 816, 861, 806, 12, 782, 68, 529, 282, 860, + /* 1790 */ 859, 818, 818, 65, 65, 66, 66, 66, 66, 847, + /* 1800 */ 64, 64, 64, 64, 63, 63, 62, 62, 62, 61, + /* 1810 */ 59, 263, 530, 15, 465, 860, 859, 706, 705, 81, + /* 1820 */ 390, 862, 121, 701, 521, 120, 273, 529, 282, 862, + /* 1830 */ 775, 818, 818, 65, 65, 66, 66, 66, 66, 396, + /* 1840 */ 64, 64, 64, 64, 63, 63, 62, 62, 62, 61, + /* 1850 */ 59, 263, 828, 138, 862, 848, 828, 862, 860, 859, + /* 1860 */ 37, 25, 72, 1052, 526, 669, 861, 723, 741, 861, + /* 1870 */ 861, 545, 862, 860, 859, 829, 230, 860, 859, 829, + /* 1880 */ 201, 845, 861, 353, 528, 808, 724, 458, 528, 862, + /* 1890 */ 545, 56, 660, 571, 36, 861, 862, 861, 455, 860, + /* 1900 */ 859, 723, 1315, 1315, 86, 739, 862, 333, 53, 54, + /* 1910 */ 524, 85, 488, 24, 530, 450, 55, 323, 545, 861, + /* 1920 */ 84, 862, 397, 828, 2, 678, 521, 714, 300, 816, + /* 1930 */ 161, 861, 583, 518, 381, 830, 859, 861, 775, 446, + /* 1940 */ 828, 833, 861, 739, 860, 859, 829, 211, 816, 832, + /* 1950 */ 831, 830, 860, 859, 861, 528, 339, 861, 638, 193, + /* 1960 */ 157, 571, 10, 829, 233, 815, 526, 105, 838, 745, + /* 1970 */ 862, 624, 528, 782, 574, 862, 395, 860, 859, 35, + /* 1980 */ 860, 859, 861, 816, 103, 828, 816, 808, 806, 12, + /* 1990 */ 624, 844, 814, 56, 34, 860, 859, 33, 862, 861, + /* 2000 */ 815, 828, 32, 102, 391, 775, 577, 393, 829, 231, + /* 2010 */ 53, 54, 860, 859, 394, 861, 530, 528, 55, 860, + /* 2020 */ 859, 574, 775, 616, 829, 241, 2, 330, 521, 860, + /* 2030 */ 859, 816, 828, 528, 828, 518, 101, 830, 859, 862, + /* 2040 */ 577, 618, 616, 833, 860, 859, 861, 828, 861, 828, + /* 2050 */ 816, 832, 831, 830, 862, 829, 240, 829, 242, 862, + /* 2060 */ 731, 861, 862, 861, 528, 308, 528, 775, 526, 612, + /* 2070 */ 829, 243, 829, 262, 607, 605, 862, 426, 828, 528, + /* 2080 */ 736, 528, 298, 775, 110, 816, 579, 581, 816, 808, + /* 2090 */ 806, 12, 861, 860, 859, 56, 861, 826, 860, 859, + /* 2100 */ 173, 829, 356, 296, 599, 306, 581, 575, 152, 739, + /* 2110 */ 528, 861, 53, 54, 775, 538, 775, 861, 530, 13, + /* 2120 */ 55, 860, 859, 861, 303, 862, 187, 828, 2, 775, + /* 2130 */ 521, 775, 580, 816, 538, 439, 198, 518, 556, 830, + /* 2140 */ 859, 861, 828, 96, 828, 833, 740, 555, 410, 861, + /* 2150 */ 829, 239, 816, 832, 831, 830, 861, 862, 861, 528, + /* 2160 */ 775, 862, 860, 859, 862, 829, 355, 829, 210, 144, + /* 2170 */ 526, 536, 94, 256, 528, 666, 528, 860, 859, 862, + /* 2180 */ 424, 248, 860, 859, 862, 860, 859, 816, 76, 828, + /* 2190 */ 816, 808, 806, 12, 1205, 604, 813, 56, 252, 860, + /* 2200 */ 859, 272, 862, 861, 828, 862, 114, 675, 385, 775, + /* 2210 */ 861, 185, 829, 228, 53, 54, 384, 865, 861, 294, + /* 2220 */ 530, 528, 55, 572, 775, 855, 775, 829, 232, 828, + /* 2230 */ 2, 1207, 521, 861, 854, 816, 528, 853, 634, 518, + /* 2240 */ 533, 830, 859, 861, 862, 184, 287, 833, 860, 859, + /* 2250 */ 383, 828, 829, 379, 816, 832, 831, 830, 862, 653, + /* 2260 */ 861, 528, 862, 523, 862, 861, 862, 182, 108, 447, + /* 2270 */ 532, 775, 526, 641, 829, 378, 737, 861, 75, 359, + /* 2280 */ 860, 859, 828, 528, 860, 859, 775, 860, 859, 816, + /* 2290 */ 861, 862, 816, 808, 806, 12, 861, 646, 178, 56, + /* 2300 */ 632, 824, 860, 859, 284, 829, 377, 860, 859, 358, + /* 2310 */ 823, 775, 862, 732, 528, 628, 53, 54, 23, 489, + /* 2320 */ 623, 591, 530, 192, 55, 860, 859, 861, 860, 859, + /* 2330 */ 43, 828, 2, 775, 521, 822, 70, 816, 166, 305, + /* 2340 */ 263, 518, 352, 830, 859, 861, 828, 88, 862, 833, + /* 2350 */ 862, 279, 861, 861, 829, 227, 816, 832, 831, 830, + /* 2360 */ 861, 480, 862, 528, 775, 700, 260, 860, 859, 829, + /* 2370 */ 213, 405, 278, 349, 526, 861, 49, 505, 528, 861, + /* 2380 */ 1209, 860, 859, 514, 596, 860, 859, 860, 859, 860, + /* 2390 */ 859, 816, 335, 828, 816, 808, 806, 12, 828, 773, + /* 2400 */ 594, 56, 1209, 772, 253, 404, 861, 861, 1209, 578, + /* 2410 */ 769, 763, 861, 775, 860, 859, 829, 226, 53, 54, + /* 2420 */ 862, 829, 225, 862, 676, 528, 55, 582, 775, 573, + /* 2430 */ 528, 139, 862, 828, 2, 860, 859, 757, 861, 816, + /* 2440 */ 140, 476, 473, 518, 702, 860, 859, 861, 165, 564, + /* 2450 */ 862, 833, 691, 686, 1209, 654, 829, 212, 816, 832, + /* 2460 */ 831, 830, 1209, 400, 463, 528, 542, 1209, 329, 861, + /* 2470 */ 685, 860, 859, 860, 859, 775, 576, 322, 828, 337, + /* 2480 */ 775, 420, 861, 681, 544, 860, 859, 682, 659, 680, + /* 2490 */ 1209, 861, 861, 816, 543, 861, 816, 828, 806, 12, + /* 2500 */ 375, 829, 238, 1209, 362, 1209, 1209, 652, 861, 828, + /* 2510 */ 528, 861, 651, 374, 1209, 775, 1209, 1209, 861, 1209, + /* 2520 */ 829, 237, 160, 861, 650, 527, 828, 649, 302, 528, + /* 2530 */ 106, 445, 829, 209, 30, 640, 777, 1209, 828, 861, + /* 2540 */ 861, 528, 861, 860, 859, 828, 860, 859, 159, 829, + /* 2550 */ 124, 158, 861, 292, 137, 860, 859, 288, 528, 861, + /* 2560 */ 775, 829, 208, 427, 828, 258, 289, 861, 829, 206, + /* 2570 */ 528, 861, 656, 860, 859, 31, 321, 528, 861, 775, + /* 2580 */ 861, 443, 156, 318, 199, 631, 104, 829, 234, 136, + /* 2590 */ 155, 775, 627, 314, 783, 154, 528, 191, 828, 111, + /* 2600 */ 828, 310, 828, 738, 434, 828, 621, 617, 775, 433, + /* 2610 */ 615, 134, 861, 153, 861, 421, 861, 828, 133, 861, + /* 2620 */ 775, 829, 236, 829, 229, 829, 235, 775, 829, 224, + /* 2630 */ 528, 861, 528, 595, 528, 151, 418, 528, 828, 100, + /* 2640 */ 829, 221, 416, 828, 150, 99, 775, 149, 98, 528, + /* 2650 */ 148, 97, 861, 147, 30, 565, 388, 861, 146, 29, + /* 2660 */ 552, 829, 205, 550, 131, 828, 829, 204, 546, 828, + /* 2670 */ 528, 828, 26, 130, 145, 528, 83, 540, 186, 861, + /* 2680 */ 775, 200, 775, 861, 775, 861, 360, 775, 829, 203, + /* 2690 */ 786, 801, 829, 202, 829, 214, 511, 528, 189, 775, + /* 2700 */ 828, 528, 188, 528, 60, 828, 797, 828, 743, 733, + /* 2710 */ 338, 677, 244, 315, 861, 454, 319, 440, 370, 861, + /* 2720 */ 775, 861, 620, 829, 219, 775, 819, 9, 829, 218, + /* 2730 */ 829, 125, 528, 796, 785, 41, 828, 528, 664, 528, + /* 2740 */ 444, 828, 361, 257, 525, 762, 674, 775, 668, 95, + /* 2750 */ 861, 775, 695, 775, 655, 861, 828, 667, 694, 829, + /* 2760 */ 217, 589, 828, 81, 829, 216, 862, 1359, 528, 1359, + /* 2770 */ 861, 1359, 1359, 528, 1359, 1359, 861, 1359, 1359, 829, + /* 2780 */ 222, 828, 775, 828, 1359, 829, 220, 775, 528, 775, + /* 2790 */ 1359, 1359, 1359, 1359, 528, 861, 1359, 861, 1359, 1359, + /* 2800 */ 1359, 1359, 1359, 1359, 829, 207, 829, 93, 1359, 1359, + /* 2810 */ 1359, 1359, 1359, 528, 1359, 479, 1359, 1359, 775, 1359, + /* 2820 */ 1359, 1359, 1359, 775, 1359, 1359, 1359, 1359, 1359, 1359, + /* 2830 */ 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 775, 1359, + /* 2840 */ 1359, 1359, 1359, 1359, 775, 1359, 1359, 1359, 1359, 1359, + /* 2850 */ 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, + /* 2860 */ 1359, 1359, 1359, 775, 1359, 775, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 4, 228, 67, 4, 231, 70, 106, 79, 80, 81, - /* 10 */ 82, 238, 16, 85, 86, 87, 88, 89, 90, 91, - /* 20 */ 92, 110, 94, 95, 96, 97, 98, 99, 100, 101, - /* 30 */ 102, 103, 104, 199, 106, 94, 95, 96, 97, 98, - /* 40 */ 99, 100, 101, 102, 103, 104, 212, 112, 129, 121, - /* 50 */ 88, 55, 89, 90, 91, 92, 93, 94, 95, 96, - /* 60 */ 97, 98, 99, 100, 101, 102, 103, 104, 199, 199, - /* 70 */ 4, 114, 76, 4, 59, 113, 207, 81, 208, 209, - /* 80 */ 199, 212, 212, 100, 101, 102, 103, 104, 160, 4, - /* 90 */ 221, 222, 4, 212, 98, 99, 100, 169, 199, 230, - /* 100 */ 129, 105, 106, 204, 234, 24, 236, 208, 209, 113, - /* 110 */ 114, 212, 285, 244, 118, 180, 120, 121, 147, 123, - /* 120 */ 124, 122, 123, 124, 4, 129, 127, 258, 259, 130, - /* 130 */ 131, 132, 136, 137, 138, 139, 16, 141, 78, 42, - /* 140 */ 43, 207, 308, 309, 24, 318, 319, 148, 279, 65, - /* 150 */ 154, 113, 156, 157, 75, 71, 160, 142, 143, 144, - /* 160 */ 31, 165, 166, 167, 168, 169, 170, 152, 153, 173, - /* 170 */ 41, 175, 176, 113, 114, 55, 250, 243, 244, 113, - /* 180 */ 83, 84, 113, 187, 118, 34, 260, 118, 307, 123, - /* 190 */ 124, 199, 123, 124, 113, 4, 76, 121, 113, 207, - /* 200 */ 4, 81, 136, 118, 212, 136, 272, 273, 123, 124, - /* 210 */ 276, 123, 124, 221, 222, 131, 140, 151, 98, 99, - /* 220 */ 151, 116, 230, 15, 136, 105, 106, 4, 162, 163, - /* 230 */ 296, 297, 81, 113, 114, 116, 244, 121, 118, 151, - /* 240 */ 120, 121, 127, 123, 124, 130, 131, 132, 4, 129, - /* 250 */ 258, 113, 44, 115, 199, 104, 136, 137, 138, 139, - /* 260 */ 16, 141, 114, 148, 180, 186, 115, 212, 60, 121, - /* 270 */ 154, 279, 156, 157, 154, 121, 156, 157, 127, 128, - /* 280 */ 129, 130, 131, 132, 133, 165, 166, 167, 168, 155, - /* 290 */ 170, 121, 158, 173, 124, 175, 176, 113, 147, 55, - /* 300 */ 98, 99, 100, 101, 102, 103, 104, 187, 191, 118, - /* 310 */ 4, 128, 129, 196, 123, 124, 78, 199, 4, 123, - /* 320 */ 76, 199, 199, 169, 207, 81, 135, 136, 43, 274, - /* 330 */ 212, 135, 162, 163, 212, 212, 142, 143, 144, 221, - /* 340 */ 222, 118, 98, 99, 221, 222, 123, 124, 230, 105, - /* 350 */ 106, 113, 114, 230, 146, 164, 121, 113, 114, 136, - /* 360 */ 243, 244, 118, 5, 120, 121, 81, 123, 124, 4, - /* 370 */ 7, 199, 4, 129, 121, 12, 258, 259, 35, 207, - /* 380 */ 136, 137, 138, 139, 212, 141, 114, 164, 30, 272, - /* 390 */ 273, 269, 114, 276, 36, 52, 207, 279, 154, 56, - /* 400 */ 156, 157, 279, 40, 4, 62, 100, 42, 43, 165, - /* 410 */ 199, 167, 168, 160, 170, 199, 244, 173, 301, 175, - /* 420 */ 176, 63, 114, 212, 208, 209, 141, 113, 212, 123, - /* 430 */ 124, 187, 147, 244, 262, 263, 313, 123, 124, 316, - /* 440 */ 4, 26, 136, 4, 79, 80, 81, 82, 83, 84, - /* 450 */ 85, 86, 87, 88, 89, 90, 91, 92, 114, 94, - /* 460 */ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - /* 470 */ 4, 123, 35, 262, 263, 113, 170, 129, 103, 104, - /* 480 */ 115, 113, 137, 138, 170, 137, 138, 139, 123, 42, - /* 490 */ 43, 123, 124, 56, 79, 80, 81, 82, 51, 62, - /* 500 */ 85, 86, 87, 88, 89, 90, 91, 92, 165, 94, - /* 510 */ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - /* 520 */ 4, 114, 4, 123, 159, 125, 79, 80, 81, 82, - /* 530 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - /* 540 */ 10, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 550 */ 103, 104, 22, 35, 118, 4, 117, 118, 121, 123, - /* 560 */ 124, 113, 123, 124, 78, 199, 245, 246, 121, 248, - /* 570 */ 52, 210, 42, 43, 56, 214, 215, 216, 212, 21, - /* 580 */ 62, 192, 193, 194, 195, 199, 150, 221, 222, 123, - /* 590 */ 124, 10, 177, 199, 179, 37, 230, 203, 212, 113, - /* 600 */ 114, 32, 136, 22, 218, 219, 212, 121, 121, 79, - /* 610 */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - /* 620 */ 90, 91, 92, 54, 94, 95, 96, 97, 98, 99, - /* 630 */ 100, 101, 102, 103, 104, 119, 170, 114, 43, 123, - /* 640 */ 124, 123, 124, 121, 78, 279, 4, 160, 79, 80, - /* 650 */ 81, 82, 42, 43, 85, 86, 87, 88, 89, 90, - /* 660 */ 91, 92, 165, 94, 95, 96, 97, 98, 99, 100, - /* 670 */ 101, 102, 103, 104, 123, 124, 290, 291, 4, 113, - /* 680 */ 114, 315, 316, 165, 187, 134, 4, 121, 4, 79, - /* 690 */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - /* 700 */ 90, 91, 92, 100, 94, 95, 96, 97, 98, 99, - /* 710 */ 100, 101, 102, 103, 104, 89, 90, 91, 92, 121, - /* 720 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, - /* 730 */ 104, 115, 79, 80, 81, 82, 141, 212, 85, 86, - /* 740 */ 87, 88, 89, 90, 91, 92, 221, 94, 95, 96, - /* 750 */ 97, 98, 99, 100, 101, 102, 103, 104, 155, 106, - /* 760 */ 118, 158, 113, 165, 67, 123, 124, 70, 26, 79, - /* 770 */ 80, 81, 82, 38, 121, 85, 86, 87, 88, 89, - /* 780 */ 90, 91, 92, 4, 94, 95, 96, 97, 98, 99, - /* 790 */ 100, 101, 102, 103, 104, 113, 106, 123, 124, 4, - /* 800 */ 114, 4, 26, 4, 199, 123, 124, 123, 124, 112, - /* 810 */ 136, 121, 26, 160, 72, 4, 81, 212, 79, 80, - /* 820 */ 81, 82, 169, 181, 85, 86, 87, 88, 89, 90, - /* 830 */ 91, 92, 4, 94, 95, 96, 97, 98, 99, 100, - /* 840 */ 101, 102, 103, 104, 16, 106, 98, 99, 106, 4, - /* 850 */ 160, 72, 170, 207, 170, 79, 80, 81, 82, 169, - /* 860 */ 121, 85, 86, 87, 88, 89, 90, 91, 92, 199, - /* 870 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, - /* 880 */ 104, 4, 212, 55, 177, 137, 138, 14, 218, 219, - /* 890 */ 244, 285, 113, 114, 142, 143, 144, 118, 199, 160, - /* 900 */ 72, 114, 123, 124, 76, 217, 218, 219, 169, 81, - /* 910 */ 113, 212, 115, 118, 309, 136, 199, 207, 123, 124, - /* 920 */ 123, 76, 123, 124, 318, 319, 98, 99, 121, 212, - /* 930 */ 151, 152, 153, 105, 123, 124, 237, 121, 221, 222, - /* 940 */ 13, 113, 114, 98, 99, 115, 118, 230, 120, 4, - /* 950 */ 199, 123, 124, 177, 244, 179, 159, 129, 113, 199, - /* 960 */ 290, 291, 114, 212, 136, 137, 138, 139, 123, 121, - /* 970 */ 159, 4, 212, 174, 129, 258, 259, 141, 183, 291, - /* 980 */ 152, 153, 137, 138, 139, 220, 287, 214, 215, 216, - /* 990 */ 113, 14, 114, 228, 229, 21, 279, 161, 170, 121, - /* 1000 */ 123, 173, 28, 175, 176, 49, 79, 80, 81, 82, - /* 1010 */ 4, 37, 85, 86, 87, 88, 89, 90, 91, 92, - /* 1020 */ 4, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 1030 */ 103, 104, 177, 106, 179, 79, 80, 81, 82, 113, - /* 1040 */ 289, 85, 86, 87, 88, 89, 90, 91, 92, 4, - /* 1050 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, - /* 1060 */ 104, 16, 106, 118, 19, 199, 4, 199, 123, 124, - /* 1070 */ 310, 311, 312, 57, 4, 217, 218, 219, 212, 255, - /* 1080 */ 212, 257, 207, 4, 117, 118, 79, 80, 81, 82, - /* 1090 */ 123, 124, 85, 86, 87, 88, 89, 90, 91, 92, - /* 1100 */ 55, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 1110 */ 103, 104, 19, 68, 4, 239, 114, 241, 243, 244, - /* 1120 */ 46, 76, 115, 121, 118, 35, 81, 182, 83, 123, - /* 1130 */ 124, 263, 199, 117, 4, 114, 203, 44, 207, 123, - /* 1140 */ 124, 72, 136, 98, 99, 212, 56, 272, 273, 291, - /* 1150 */ 105, 276, 62, 302, 303, 4, 61, 199, 113, 66, - /* 1160 */ 98, 99, 4, 118, 114, 120, 4, 16, 123, 124, - /* 1170 */ 212, 121, 297, 280, 129, 244, 310, 311, 312, 221, - /* 1180 */ 222, 136, 137, 138, 139, 123, 124, 4, 230, 80, - /* 1190 */ 128, 4, 299, 123, 124, 199, 106, 128, 4, 137, - /* 1200 */ 138, 35, 123, 141, 114, 143, 55, 0, 212, 32, - /* 1210 */ 144, 121, 199, 201, 135, 170, 258, 259, 173, 54, - /* 1220 */ 175, 176, 56, 72, 61, 212, 199, 76, 62, 199, - /* 1230 */ 14, 141, 81, 123, 221, 222, 29, 279, 172, 212, - /* 1240 */ 170, 5, 212, 230, 154, 135, 156, 157, 118, 98, - /* 1250 */ 99, 221, 222, 123, 124, 165, 105, 167, 168, 166, - /* 1260 */ 230, 114, 14, 199, 113, 269, 30, 203, 121, 118, - /* 1270 */ 108, 120, 36, 214, 123, 124, 212, 187, 194, 195, - /* 1280 */ 129, 123, 124, 270, 114, 123, 124, 136, 137, 138, - /* 1290 */ 139, 121, 279, 4, 136, 59, 214, 4, 286, 63, - /* 1300 */ 270, 118, 26, 152, 153, 118, 123, 124, 113, 279, - /* 1310 */ 123, 124, 199, 106, 184, 212, 213, 123, 124, 136, - /* 1320 */ 261, 170, 164, 136, 173, 212, 175, 176, 134, 79, - /* 1330 */ 80, 81, 82, 306, 307, 85, 86, 87, 88, 89, - /* 1340 */ 90, 91, 92, 261, 94, 95, 96, 97, 98, 99, - /* 1350 */ 100, 101, 102, 103, 104, 79, 80, 81, 82, 207, - /* 1360 */ 14, 85, 86, 87, 88, 89, 90, 91, 92, 14, - /* 1370 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, - /* 1380 */ 104, 29, 79, 80, 81, 82, 311, 312, 85, 86, - /* 1390 */ 87, 88, 89, 90, 91, 92, 244, 94, 95, 96, - /* 1400 */ 97, 98, 99, 100, 101, 102, 103, 104, 114, 311, - /* 1410 */ 312, 118, 123, 124, 14, 121, 123, 124, 121, 306, - /* 1420 */ 307, 4, 311, 312, 79, 80, 81, 82, 178, 136, - /* 1430 */ 85, 86, 87, 88, 89, 90, 91, 92, 114, 94, - /* 1440 */ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - /* 1450 */ 239, 4, 241, 164, 79, 80, 81, 82, 106, 114, - /* 1460 */ 85, 86, 87, 88, 89, 90, 91, 92, 38, 94, - /* 1470 */ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - /* 1480 */ 114, 178, 311, 312, 79, 80, 81, 82, 114, 114, - /* 1490 */ 85, 86, 87, 88, 89, 90, 91, 92, 162, 94, - /* 1500 */ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - /* 1510 */ 245, 246, 4, 248, 79, 80, 81, 82, 116, 114, - /* 1520 */ 85, 86, 87, 88, 89, 90, 91, 92, 116, 94, - /* 1530 */ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - /* 1540 */ 123, 124, 277, 100, 79, 80, 81, 82, 121, 114, - /* 1550 */ 85, 86, 87, 88, 89, 90, 91, 92, 113, 94, - /* 1560 */ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - /* 1570 */ 123, 124, 311, 312, 79, 80, 81, 82, 114, 114, - /* 1580 */ 85, 86, 87, 88, 89, 90, 91, 92, 113, 94, - /* 1590 */ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - /* 1600 */ 183, 106, 79, 80, 81, 82, 152, 153, 85, 86, - /* 1610 */ 87, 88, 89, 90, 91, 92, 108, 94, 95, 96, - /* 1620 */ 97, 98, 99, 100, 101, 102, 103, 104, 181, 155, - /* 1630 */ 4, 123, 124, 79, 80, 81, 82, 4, 115, 85, - /* 1640 */ 86, 87, 88, 89, 90, 91, 92, 114, 94, 95, - /* 1650 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 4, - /* 1660 */ 106, 79, 80, 81, 82, 114, 4, 85, 86, 87, - /* 1670 */ 88, 89, 90, 91, 92, 4, 94, 95, 96, 97, - /* 1680 */ 98, 99, 100, 101, 102, 103, 104, 16, 106, 114, - /* 1690 */ 19, 79, 80, 81, 82, 114, 121, 85, 86, 87, - /* 1700 */ 88, 89, 90, 91, 92, 4, 94, 95, 96, 97, - /* 1710 */ 98, 99, 100, 101, 102, 103, 104, 9, 114, 11, - /* 1720 */ 199, 13, 114, 199, 203, 121, 55, 199, 114, 199, - /* 1730 */ 199, 23, 114, 212, 26, 121, 212, 113, 199, 68, - /* 1740 */ 212, 108, 212, 212, 4, 221, 222, 76, 4, 123, - /* 1750 */ 124, 212, 81, 169, 230, 113, 123, 124, 114, 18, - /* 1760 */ 199, 53, 136, 108, 203, 121, 58, 4, 237, 98, - /* 1770 */ 99, 63, 64, 212, 49, 199, 105, 69, 123, 124, - /* 1780 */ 72, 4, 74, 115, 113, 123, 124, 224, 212, 118, - /* 1790 */ 164, 120, 199, 16, 123, 124, 19, 221, 222, 199, - /* 1800 */ 129, 214, 274, 279, 114, 212, 230, 136, 137, 138, - /* 1810 */ 139, 121, 212, 151, 221, 222, 115, 109, 287, 111, - /* 1820 */ 114, 221, 222, 230, 123, 72, 287, 121, 6, 4, - /* 1830 */ 230, 113, 55, 214, 258, 4, 8, 313, 49, 199, - /* 1840 */ 316, 170, 113, 280, 173, 68, 175, 176, 261, 4, - /* 1850 */ 114, 258, 212, 76, 49, 279, 4, 149, 81, 106, - /* 1860 */ 159, 4, 224, 123, 124, 113, 122, 123, 124, 113, - /* 1870 */ 270, 199, 279, 188, 189, 98, 99, 113, 199, 279, - /* 1880 */ 261, 118, 105, 199, 212, 199, 123, 124, 133, 203, - /* 1890 */ 113, 212, 147, 185, 199, 118, 212, 120, 212, 136, - /* 1900 */ 123, 124, 162, 120, 13, 4, 129, 212, 81, 269, - /* 1910 */ 115, 141, 199, 136, 137, 138, 139, 16, 280, 160, - /* 1920 */ 146, 79, 80, 81, 82, 212, 88, 85, 86, 87, - /* 1930 */ 88, 89, 90, 91, 92, 4, 94, 95, 96, 97, - /* 1940 */ 98, 99, 100, 101, 102, 103, 104, 170, 123, 124, - /* 1950 */ 173, 214, 175, 176, 123, 124, 55, 88, 88, 4, - /* 1960 */ 199, 199, 88, 118, 269, 171, 199, 136, 123, 124, - /* 1970 */ 118, 4, 199, 212, 212, 123, 124, 76, 199, 212, - /* 1980 */ 123, 124, 81, 221, 222, 212, 18, 162, 221, 222, - /* 1990 */ 4, 212, 230, 24, 221, 222, 151, 230, 261, 98, - /* 2000 */ 99, 146, 145, 230, 4, 199, 105, 153, 172, 106, - /* 2010 */ 106, 4, 25, 199, 113, 114, 164, 280, 212, 118, - /* 2020 */ 258, 120, 199, 16, 123, 124, 212, 221, 222, 4, - /* 2030 */ 129, 61, 50, 121, 199, 212, 230, 136, 137, 138, - /* 2040 */ 139, 279, 50, 270, 221, 222, 279, 212, 269, 282, - /* 2050 */ 141, 237, 279, 230, 123, 124, 221, 222, 180, 113, - /* 2060 */ 199, 112, 55, 161, 258, 230, 113, 136, 120, 112, - /* 2070 */ 114, 170, 117, 212, 173, 114, 175, 176, 123, 124, - /* 2080 */ 48, 199, 199, 76, 4, 279, 203, 199, 81, 110, - /* 2090 */ 123, 124, 47, 270, 212, 212, 211, 280, 199, 227, - /* 2100 */ 212, 287, 279, 136, 264, 98, 99, 227, 4, 123, - /* 2110 */ 124, 212, 105, 199, 279, 303, 305, 4, 305, 237, - /* 2120 */ 113, 114, 136, 123, 124, 118, 212, 120, 199, 16, - /* 2130 */ 123, 124, 198, 200, 199, 107, 129, 198, 64, 199, - /* 2140 */ 198, 212, 199, 136, 137, 138, 139, 212, 123, 124, - /* 2150 */ 221, 222, 212, 4, 200, 212, 221, 222, 4, 230, - /* 2160 */ 198, 221, 222, 199, 164, 230, 77, 203, 55, 287, - /* 2170 */ 230, 205, 199, 4, 199, 150, 212, 170, 4, 199, - /* 2180 */ 173, 121, 175, 176, 4, 212, 202, 212, 4, 76, - /* 2190 */ 73, 206, 212, 4, 81, 267, 221, 222, 246, 116, - /* 2200 */ 199, 221, 222, 123, 124, 230, 152, 4, 279, 4, - /* 2210 */ 230, 98, 99, 212, 279, 252, 136, 169, 105, 279, - /* 2220 */ 167, 117, 166, 4, 253, 199, 113, 123, 124, 168, - /* 2230 */ 4, 118, 254, 120, 104, 16, 123, 124, 212, 256, - /* 2240 */ 199, 4, 129, 81, 226, 199, 113, 221, 222, 136, - /* 2250 */ 137, 138, 139, 212, 279, 104, 230, 199, 212, 279, - /* 2260 */ 288, 203, 221, 222, 283, 177, 255, 221, 222, 280, - /* 2270 */ 212, 230, 123, 124, 55, 256, 230, 123, 124, 165, - /* 2280 */ 271, 199, 4, 170, 271, 136, 173, 45, 175, 176, - /* 2290 */ 177, 314, 123, 124, 212, 76, 27, 123, 124, 116, - /* 2300 */ 81, 117, 199, 123, 124, 279, 117, 123, 124, 199, - /* 2310 */ 136, 314, 123, 124, 267, 212, 136, 98, 99, 248, - /* 2320 */ 279, 199, 212, 199, 105, 279, 123, 124, 123, 124, - /* 2330 */ 153, 199, 113, 114, 212, 319, 212, 118, 184, 120, - /* 2340 */ 188, 136, 123, 124, 212, 233, 223, 4, 129, 123, - /* 2350 */ 124, 182, 223, 221, 222, 136, 137, 138, 139, 16, - /* 2360 */ 123, 124, 230, 141, 80, 81, 82, 39, 223, 85, - /* 2370 */ 86, 87, 88, 89, 90, 91, 92, 199, 94, 95, - /* 2380 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 170, - /* 2390 */ 212, 225, 173, 199, 175, 176, 170, 203, 55, 221, - /* 2400 */ 222, 123, 124, 199, 140, 199, 212, 170, 230, 199, - /* 2410 */ 199, 279, 199, 199, 223, 233, 212, 267, 212, 76, - /* 2420 */ 223, 226, 212, 212, 81, 212, 212, 221, 222, 116, - /* 2430 */ 223, 199, 223, 199, 221, 222, 230, 199, 199, 223, - /* 2440 */ 116, 98, 99, 230, 212, 199, 212, 226, 105, 171, - /* 2450 */ 212, 212, 202, 4, 226, 202, 113, 279, 212, 221, - /* 2460 */ 222, 118, 199, 120, 116, 16, 123, 124, 230, 267, - /* 2470 */ 199, 199, 129, 199, 199, 212, 199, 33, 199, 136, - /* 2480 */ 137, 138, 139, 212, 212, 279, 212, 212, 199, 212, - /* 2490 */ 294, 212, 279, 221, 222, 295, 177, 116, 221, 222, - /* 2500 */ 252, 212, 230, 79, 55, 240, 116, 230, 252, 116, - /* 2510 */ 275, 113, 141, 170, 278, 116, 173, 279, 175, 176, - /* 2520 */ 252, 240, 202, 202, 267, 76, 267, 116, 252, 242, - /* 2530 */ 81, 116, 77, 242, 116, 242, 116, 242, 116, 171, - /* 2540 */ 289, 116, 298, 20, 17, 298, 116, 98, 99, 267, - /* 2550 */ 267, 279, 267, 199, 105, 116, 279, 116, 199, 249, - /* 2560 */ 300, 291, 113, 251, 291, 224, 212, 118, 266, 120, - /* 2570 */ 284, 212, 123, 124, 268, 221, 222, 255, 129, 224, - /* 2580 */ 221, 222, 255, 231, 230, 136, 137, 138, 139, 230, - /* 2590 */ 225, 235, 252, 268, 268, 81, 82, 251, 197, 85, - /* 2600 */ 86, 87, 88, 89, 90, 91, 92, 249, 94, 95, - /* 2610 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 170, - /* 2620 */ 214, 214, 173, 214, 175, 176, 214, 199, 292, 247, - /* 2630 */ 293, 281, 241, 279, 199, 317, 232, 265, 279, 304, - /* 2640 */ 212, 199, 232, 72, 221, 232, 4, 212, 221, 221, - /* 2650 */ 222, 320, 236, 199, 212, 320, 221, 222, 230, 320, - /* 2660 */ 199, 320, 320, 221, 222, 230, 212, 199, 320, 320, - /* 2670 */ 320, 320, 230, 212, 320, 221, 222, 320, 320, 199, - /* 2680 */ 212, 199, 221, 222, 230, 320, 320, 320, 320, 221, - /* 2690 */ 222, 230, 212, 199, 212, 320, 320, 320, 230, 320, - /* 2700 */ 320, 221, 222, 221, 222, 320, 212, 279, 320, 320, - /* 2710 */ 230, 320, 230, 320, 279, 221, 222, 320, 320, 199, - /* 2720 */ 320, 279, 320, 320, 230, 320, 320, 320, 320, 320, - /* 2730 */ 320, 320, 212, 279, 320, 320, 320, 320, 320, 320, - /* 2740 */ 279, 221, 222, 320, 320, 320, 320, 279, 320, 320, - /* 2750 */ 230, 320, 320, 199, 320, 320, 199, 320, 199, 279, - /* 2760 */ 320, 279, 320, 320, 320, 320, 212, 320, 320, 212, - /* 2770 */ 320, 212, 199, 279, 320, 221, 222, 320, 221, 222, - /* 2780 */ 221, 222, 320, 320, 230, 212, 320, 230, 199, 230, - /* 2790 */ 320, 320, 320, 320, 221, 222, 320, 320, 320, 279, - /* 2800 */ 199, 212, 320, 230, 199, 320, 320, 320, 320, 320, - /* 2810 */ 221, 222, 320, 212, 320, 320, 320, 212, 320, 230, - /* 2820 */ 320, 320, 221, 222, 199, 320, 221, 222, 320, 320, - /* 2830 */ 320, 230, 320, 279, 320, 230, 279, 212, 279, 320, - /* 2840 */ 199, 320, 320, 320, 320, 320, 221, 222, 320, 320, - /* 2850 */ 320, 320, 279, 212, 199, 230, 199, 320, 320, 320, - /* 2860 */ 199, 320, 221, 222, 320, 320, 320, 212, 279, 212, - /* 2870 */ 320, 230, 320, 212, 320, 199, 221, 222, 221, 222, - /* 2880 */ 279, 320, 221, 222, 279, 230, 320, 230, 212, 320, - /* 2890 */ 199, 230, 199, 320, 199, 320, 320, 221, 222, 320, - /* 2900 */ 320, 320, 320, 212, 279, 212, 230, 212, 320, 320, - /* 2910 */ 320, 320, 221, 222, 221, 222, 221, 222, 320, 199, - /* 2920 */ 279, 230, 320, 230, 320, 230, 320, 320, 320, 320, - /* 2930 */ 320, 320, 212, 320, 279, 320, 279, 320, 320, 320, - /* 2940 */ 279, 221, 222, 320, 320, 320, 320, 320, 320, 320, - /* 2950 */ 230, 320, 320, 199, 320, 279, 199, 320, 320, 320, - /* 2960 */ 320, 320, 320, 320, 320, 320, 212, 320, 320, 212, - /* 2970 */ 279, 320, 279, 320, 279, 221, 222, 320, 221, 222, - /* 2980 */ 320, 320, 320, 320, 230, 320, 320, 230, 320, 320, - /* 2990 */ 320, 320, 320, 320, 320, 320, 320, 320, 320, 279, - /* 3000 */ 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, - /* 3010 */ 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, - /* 3020 */ 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, - /* 3030 */ 320, 320, 320, 279, 320, 320, 279, + /* 0 */ 4, 131, 24, 31, 134, 135, 136, 50, 81, 82, + /* 10 */ 83, 84, 16, 41, 87, 88, 89, 90, 91, 92, + /* 20 */ 93, 94, 152, 96, 97, 98, 99, 100, 101, 102, + /* 30 */ 103, 104, 105, 106, 107, 102, 109, 117, 81, 82, + /* 40 */ 83, 84, 122, 122, 87, 88, 89, 90, 91, 92, + /* 50 */ 93, 94, 56, 96, 97, 98, 99, 100, 101, 102, + /* 60 */ 103, 104, 105, 106, 107, 144, 109, 102, 103, 104, + /* 70 */ 105, 106, 107, 77, 148, 91, 92, 93, 94, 83, + /* 80 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 90 */ 106, 107, 159, 4, 116, 162, 100, 101, 102, 42, + /* 100 */ 43, 203, 203, 177, 108, 109, 83, 211, 68, 44, + /* 110 */ 4, 71, 116, 117, 122, 217, 217, 121, 122, 203, + /* 120 */ 109, 125, 16, 127, 128, 226, 227, 132, 133, 133, + /* 130 */ 24, 4, 68, 217, 235, 71, 140, 141, 142, 143, + /* 140 */ 242, 145, 85, 86, 248, 249, 4, 9, 83, 11, + /* 150 */ 158, 13, 160, 161, 158, 115, 160, 161, 203, 80, + /* 160 */ 164, 23, 56, 113, 26, 169, 170, 171, 172, 173, + /* 170 */ 174, 175, 217, 277, 178, 279, 180, 181, 282, 115, + /* 180 */ 203, 226, 227, 77, 285, 162, 43, 288, 192, 83, + /* 190 */ 235, 293, 54, 117, 217, 116, 117, 59, 302, 303, + /* 200 */ 223, 224, 64, 65, 117, 116, 100, 101, 70, 122, + /* 210 */ 121, 73, 19, 75, 108, 109, 127, 128, 263, 264, + /* 220 */ 4, 203, 116, 117, 4, 185, 83, 121, 122, 140, + /* 230 */ 314, 125, 16, 127, 128, 217, 122, 5, 45, 133, + /* 240 */ 285, 223, 224, 116, 155, 118, 140, 141, 142, 143, + /* 250 */ 112, 145, 114, 122, 127, 166, 167, 203, 116, 182, + /* 260 */ 67, 184, 30, 121, 158, 203, 160, 161, 36, 127, + /* 270 */ 128, 217, 56, 296, 297, 169, 170, 171, 172, 217, + /* 280 */ 174, 175, 140, 116, 178, 118, 180, 181, 145, 4, + /* 290 */ 163, 153, 60, 77, 151, 164, 64, 155, 192, 83, + /* 300 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 310 */ 106, 107, 35, 21, 296, 297, 100, 101, 117, 60, + /* 320 */ 28, 122, 203, 122, 108, 109, 117, 128, 190, 37, + /* 330 */ 211, 122, 116, 117, 57, 145, 217, 121, 122, 5, + /* 340 */ 63, 125, 4, 127, 128, 226, 227, 127, 128, 133, + /* 350 */ 196, 197, 198, 199, 235, 165, 140, 141, 142, 143, + /* 360 */ 140, 145, 117, 170, 30, 166, 167, 122, 249, 169, + /* 370 */ 36, 317, 318, 319, 158, 133, 160, 161, 316, 203, + /* 380 */ 42, 43, 263, 264, 7, 169, 203, 171, 172, 12, + /* 390 */ 174, 175, 192, 217, 178, 175, 180, 181, 64, 122, + /* 400 */ 217, 116, 226, 227, 285, 146, 147, 148, 192, 226, + /* 410 */ 227, 235, 127, 128, 26, 156, 157, 40, 235, 81, + /* 420 */ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + /* 430 */ 92, 93, 94, 90, 96, 97, 98, 99, 100, 101, + /* 440 */ 102, 103, 104, 105, 106, 107, 100, 101, 102, 103, + /* 450 */ 104, 105, 106, 107, 141, 142, 118, 117, 275, 116, + /* 460 */ 175, 285, 122, 42, 43, 127, 308, 309, 285, 81, + /* 470 */ 82, 83, 84, 52, 116, 87, 88, 89, 90, 91, + /* 480 */ 92, 93, 94, 76, 96, 97, 98, 99, 100, 101, + /* 490 */ 102, 103, 104, 105, 106, 107, 320, 122, 119, 323, + /* 500 */ 80, 163, 81, 82, 83, 84, 85, 86, 87, 88, + /* 510 */ 89, 90, 91, 92, 93, 94, 4, 96, 97, 98, + /* 520 */ 99, 100, 101, 102, 103, 104, 105, 106, 107, 10, + /* 530 */ 117, 291, 203, 127, 133, 122, 116, 117, 119, 133, + /* 540 */ 203, 22, 122, 122, 169, 208, 217, 141, 142, 143, + /* 550 */ 213, 214, 151, 116, 217, 226, 227, 222, 223, 224, + /* 560 */ 153, 42, 43, 215, 235, 325, 326, 219, 220, 221, + /* 570 */ 182, 73, 184, 91, 92, 93, 94, 95, 96, 97, + /* 580 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + /* 590 */ 217, 4, 263, 264, 100, 101, 4, 26, 191, 226, + /* 600 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* 610 */ 91, 92, 93, 94, 285, 96, 97, 98, 99, 100, + /* 620 */ 101, 102, 103, 104, 105, 106, 107, 4, 116, 35, + /* 630 */ 132, 203, 297, 121, 117, 141, 142, 10, 233, 127, + /* 640 */ 128, 236, 203, 42, 43, 217, 207, 53, 243, 22, + /* 650 */ 58, 57, 81, 82, 83, 84, 217, 63, 87, 88, + /* 660 */ 89, 90, 91, 92, 93, 94, 4, 96, 97, 98, + /* 670 */ 99, 100, 101, 102, 103, 104, 105, 106, 107, 146, + /* 680 */ 147, 148, 81, 82, 83, 84, 85, 86, 87, 88, + /* 690 */ 89, 90, 91, 92, 93, 94, 122, 96, 97, 98, + /* 700 */ 99, 100, 101, 102, 103, 104, 105, 106, 107, 32, + /* 710 */ 203, 122, 120, 126, 127, 128, 203, 13, 131, 127, + /* 720 */ 128, 134, 135, 136, 217, 117, 213, 214, 203, 260, + /* 730 */ 217, 262, 55, 250, 251, 210, 253, 212, 43, 152, + /* 740 */ 225, 21, 217, 120, 121, 317, 318, 319, 233, 234, + /* 750 */ 127, 128, 239, 182, 241, 184, 4, 37, 81, 82, + /* 760 */ 83, 84, 173, 169, 87, 88, 89, 90, 91, 92, + /* 770 */ 93, 94, 38, 96, 97, 98, 99, 100, 101, 102, + /* 780 */ 103, 104, 105, 106, 107, 81, 82, 83, 84, 127, + /* 790 */ 128, 87, 88, 89, 90, 91, 92, 93, 94, 35, + /* 800 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 810 */ 106, 107, 4, 109, 195, 4, 154, 83, 203, 200, + /* 820 */ 34, 57, 315, 316, 16, 203, 74, 63, 66, 79, + /* 830 */ 211, 291, 217, 211, 72, 81, 82, 83, 84, 217, + /* 840 */ 145, 87, 88, 89, 90, 91, 92, 93, 94, 117, + /* 850 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 860 */ 106, 107, 4, 203, 56, 325, 326, 248, 249, 83, + /* 870 */ 203, 249, 118, 105, 106, 107, 124, 217, 80, 127, + /* 880 */ 128, 73, 267, 268, 217, 77, 226, 227, 77, 267, + /* 890 */ 268, 83, 122, 107, 117, 235, 277, 135, 279, 203, + /* 900 */ 255, 282, 211, 207, 118, 222, 223, 224, 100, 101, + /* 910 */ 265, 100, 101, 217, 116, 117, 108, 131, 132, 133, + /* 920 */ 134, 135, 136, 137, 116, 117, 307, 116, 116, 121, + /* 930 */ 146, 147, 148, 125, 164, 127, 128, 151, 127, 248, + /* 940 */ 249, 133, 80, 193, 133, 285, 117, 185, 140, 141, + /* 950 */ 142, 143, 141, 142, 143, 219, 220, 221, 116, 4, + /* 960 */ 293, 117, 26, 203, 156, 157, 122, 244, 277, 246, + /* 970 */ 279, 250, 251, 282, 253, 26, 118, 217, 116, 117, + /* 980 */ 297, 203, 4, 175, 122, 127, 178, 117, 180, 181, + /* 990 */ 81, 82, 83, 84, 303, 217, 87, 88, 89, 90, + /* 1000 */ 91, 92, 93, 94, 283, 96, 97, 98, 99, 100, + /* 1010 */ 101, 102, 103, 104, 105, 106, 107, 81, 82, 83, + /* 1020 */ 84, 163, 73, 87, 88, 89, 90, 91, 92, 93, + /* 1030 */ 94, 44, 96, 97, 98, 99, 100, 101, 102, 103, + /* 1040 */ 104, 105, 106, 107, 81, 82, 83, 84, 217, 218, + /* 1050 */ 87, 88, 89, 90, 91, 92, 93, 94, 109, 96, + /* 1060 */ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + /* 1070 */ 107, 117, 312, 295, 314, 120, 121, 0, 81, 82, + /* 1080 */ 83, 84, 127, 128, 87, 88, 89, 90, 91, 92, + /* 1090 */ 93, 94, 183, 96, 97, 98, 99, 100, 101, 102, + /* 1100 */ 103, 104, 105, 106, 107, 127, 29, 129, 4, 81, + /* 1110 */ 82, 83, 84, 122, 117, 87, 88, 89, 90, 91, + /* 1120 */ 92, 93, 94, 203, 96, 97, 98, 99, 100, 101, + /* 1130 */ 102, 103, 104, 105, 106, 107, 159, 217, 4, 162, + /* 1140 */ 116, 81, 82, 83, 84, 117, 183, 87, 88, 89, + /* 1150 */ 90, 91, 92, 93, 94, 26, 96, 97, 98, 99, + /* 1160 */ 100, 101, 102, 103, 104, 105, 106, 107, 182, 198, + /* 1170 */ 199, 117, 81, 82, 83, 84, 122, 117, 87, 88, + /* 1180 */ 89, 90, 91, 92, 93, 94, 109, 96, 97, 98, + /* 1190 */ 99, 100, 101, 102, 103, 104, 105, 106, 107, 117, + /* 1200 */ 280, 4, 117, 81, 82, 83, 84, 122, 117, 87, + /* 1210 */ 88, 89, 90, 91, 92, 93, 94, 219, 96, 97, + /* 1220 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + /* 1230 */ 162, 127, 128, 117, 81, 82, 83, 84, 122, 117, + /* 1240 */ 87, 88, 89, 90, 91, 92, 93, 94, 4, 96, + /* 1250 */ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + /* 1260 */ 107, 127, 128, 15, 266, 4, 81, 82, 83, 84, + /* 1270 */ 166, 118, 87, 88, 89, 90, 91, 92, 93, 94, + /* 1280 */ 219, 96, 97, 98, 99, 100, 101, 102, 103, 104, + /* 1290 */ 105, 106, 107, 45, 109, 81, 82, 83, 84, 4, + /* 1300 */ 166, 87, 88, 89, 90, 91, 92, 93, 94, 61, + /* 1310 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 1320 */ 106, 107, 4, 109, 127, 128, 117, 266, 203, 203, + /* 1330 */ 219, 122, 207, 73, 16, 138, 164, 19, 81, 82, + /* 1340 */ 83, 84, 217, 217, 87, 88, 89, 90, 91, 92, + /* 1350 */ 93, 94, 4, 96, 97, 98, 99, 100, 101, 102, + /* 1360 */ 103, 104, 105, 106, 107, 121, 164, 203, 4, 109, + /* 1370 */ 203, 127, 128, 117, 56, 211, 14, 266, 122, 211, + /* 1380 */ 16, 217, 121, 19, 217, 4, 4, 69, 127, 128, + /* 1390 */ 226, 227, 203, 226, 227, 77, 203, 286, 150, 235, + /* 1400 */ 118, 83, 235, 85, 4, 212, 217, 203, 122, 117, + /* 1410 */ 217, 116, 122, 249, 122, 226, 227, 249, 100, 101, + /* 1420 */ 56, 217, 127, 128, 235, 116, 108, 263, 203, 4, + /* 1430 */ 263, 264, 188, 69, 116, 35, 4, 117, 312, 121, + /* 1440 */ 314, 77, 217, 125, 211, 127, 128, 83, 187, 285, + /* 1450 */ 4, 133, 285, 53, 203, 203, 255, 57, 140, 141, + /* 1460 */ 142, 143, 16, 63, 100, 101, 265, 4, 217, 217, + /* 1470 */ 175, 244, 108, 246, 285, 127, 128, 4, 226, 227, + /* 1480 */ 116, 203, 249, 14, 280, 121, 203, 235, 140, 125, + /* 1490 */ 47, 127, 128, 175, 4, 217, 178, 133, 180, 181, + /* 1500 */ 217, 203, 56, 121, 140, 141, 142, 143, 127, 127, + /* 1510 */ 128, 322, 323, 211, 32, 217, 168, 203, 211, 73, + /* 1520 */ 139, 139, 140, 77, 226, 227, 82, 127, 128, 83, + /* 1530 */ 211, 217, 62, 235, 4, 318, 319, 285, 4, 175, + /* 1540 */ 226, 227, 178, 111, 180, 181, 100, 101, 62, 235, + /* 1550 */ 168, 249, 127, 128, 108, 55, 249, 274, 14, 127, + /* 1560 */ 128, 263, 116, 100, 101, 140, 4, 121, 249, 169, + /* 1570 */ 4, 125, 320, 127, 128, 323, 219, 263, 203, 133, + /* 1580 */ 155, 205, 207, 285, 318, 319, 140, 141, 142, 143, + /* 1590 */ 127, 128, 217, 14, 121, 132, 14, 286, 203, 285, + /* 1600 */ 127, 128, 156, 157, 141, 142, 4, 4, 145, 4, + /* 1610 */ 147, 121, 217, 140, 318, 319, 305, 127, 128, 16, + /* 1620 */ 29, 175, 19, 266, 178, 14, 180, 181, 81, 82, + /* 1630 */ 83, 84, 156, 157, 87, 88, 89, 90, 91, 92, + /* 1640 */ 93, 94, 4, 96, 97, 98, 99, 100, 101, 102, + /* 1650 */ 103, 104, 105, 106, 107, 203, 116, 127, 128, 56, + /* 1660 */ 229, 127, 128, 268, 318, 319, 4, 203, 292, 217, + /* 1670 */ 140, 203, 69, 219, 140, 122, 186, 111, 226, 227, + /* 1680 */ 77, 217, 203, 203, 203, 217, 83, 235, 14, 127, + /* 1690 */ 226, 227, 4, 127, 128, 203, 217, 217, 217, 235, + /* 1700 */ 109, 139, 168, 100, 101, 226, 227, 226, 227, 217, + /* 1710 */ 242, 108, 318, 319, 235, 38, 235, 286, 116, 116, + /* 1720 */ 266, 117, 229, 117, 121, 117, 121, 275, 125, 127, + /* 1730 */ 127, 128, 127, 128, 242, 4, 133, 285, 166, 275, + /* 1740 */ 119, 203, 263, 140, 141, 142, 143, 119, 203, 285, + /* 1750 */ 4, 213, 214, 203, 4, 217, 275, 207, 102, 154, + /* 1760 */ 203, 293, 217, 122, 285, 127, 285, 217, 203, 159, + /* 1770 */ 116, 226, 227, 111, 217, 117, 4, 139, 175, 286, + /* 1780 */ 235, 178, 217, 180, 181, 293, 82, 83, 84, 127, + /* 1790 */ 128, 87, 88, 89, 90, 91, 92, 93, 94, 111, + /* 1800 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 1810 */ 106, 107, 4, 116, 116, 127, 128, 117, 117, 73, + /* 1820 */ 275, 4, 117, 117, 16, 117, 50, 83, 84, 4, + /* 1830 */ 285, 87, 88, 89, 90, 91, 92, 93, 94, 274, + /* 1840 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + /* 1850 */ 106, 107, 203, 18, 4, 203, 203, 4, 127, 128, + /* 1860 */ 173, 116, 116, 117, 56, 6, 217, 121, 203, 217, + /* 1870 */ 217, 121, 4, 127, 128, 226, 227, 127, 128, 226, + /* 1880 */ 227, 203, 217, 203, 235, 77, 140, 118, 235, 4, + /* 1890 */ 140, 83, 8, 121, 116, 217, 4, 217, 50, 127, + /* 1900 */ 128, 155, 156, 157, 116, 203, 4, 117, 100, 101, + /* 1910 */ 179, 116, 263, 116, 4, 50, 108, 203, 168, 217, + /* 1920 */ 116, 4, 242, 203, 116, 117, 16, 102, 203, 121, + /* 1930 */ 137, 217, 207, 125, 285, 127, 128, 217, 285, 83, + /* 1940 */ 203, 133, 217, 203, 127, 128, 226, 227, 140, 141, + /* 1950 */ 142, 143, 127, 128, 217, 235, 151, 217, 125, 118, + /* 1960 */ 145, 189, 13, 226, 227, 140, 56, 164, 203, 122, + /* 1970 */ 4, 121, 235, 293, 121, 4, 274, 127, 128, 90, + /* 1980 */ 127, 128, 217, 175, 150, 203, 178, 77, 180, 181, + /* 1990 */ 140, 123, 175, 83, 90, 127, 128, 90, 4, 217, + /* 2000 */ 175, 203, 90, 176, 24, 285, 121, 18, 226, 227, + /* 2010 */ 100, 101, 127, 128, 274, 217, 4, 235, 108, 127, + /* 2020 */ 128, 168, 285, 121, 226, 227, 116, 117, 16, 127, + /* 2030 */ 128, 121, 203, 235, 203, 125, 150, 127, 128, 4, + /* 2040 */ 155, 177, 140, 133, 127, 128, 217, 203, 217, 203, + /* 2050 */ 140, 141, 142, 143, 4, 226, 227, 226, 227, 4, + /* 2060 */ 168, 217, 4, 217, 235, 157, 235, 285, 56, 109, + /* 2070 */ 226, 227, 226, 227, 109, 62, 4, 25, 203, 235, + /* 2080 */ 163, 235, 203, 285, 51, 175, 207, 121, 178, 77, + /* 2090 */ 180, 181, 217, 127, 128, 83, 217, 203, 127, 128, + /* 2100 */ 122, 226, 227, 203, 51, 185, 140, 207, 145, 203, + /* 2110 */ 235, 217, 100, 101, 285, 121, 285, 217, 4, 116, + /* 2120 */ 108, 127, 128, 217, 115, 4, 165, 203, 116, 285, + /* 2130 */ 16, 285, 125, 121, 140, 203, 116, 125, 117, 127, + /* 2140 */ 128, 217, 203, 115, 203, 133, 175, 117, 49, 217, + /* 2150 */ 226, 227, 140, 141, 142, 143, 217, 4, 217, 235, + /* 2160 */ 285, 4, 127, 128, 4, 226, 227, 226, 227, 113, + /* 2170 */ 56, 48, 216, 286, 235, 140, 235, 127, 128, 4, + /* 2180 */ 274, 309, 127, 128, 4, 127, 128, 175, 116, 203, + /* 2190 */ 178, 77, 180, 181, 182, 140, 203, 83, 269, 127, + /* 2200 */ 128, 232, 4, 217, 203, 4, 232, 149, 311, 285, + /* 2210 */ 217, 110, 226, 227, 100, 101, 311, 202, 217, 203, + /* 2220 */ 4, 235, 108, 207, 285, 202, 285, 226, 227, 203, + /* 2230 */ 116, 117, 16, 217, 202, 121, 235, 65, 188, 125, + /* 2240 */ 202, 127, 128, 217, 4, 204, 203, 133, 127, 128, + /* 2250 */ 207, 203, 226, 227, 140, 141, 142, 143, 4, 138, + /* 2260 */ 217, 235, 4, 203, 4, 217, 4, 204, 206, 78, + /* 2270 */ 209, 285, 56, 120, 226, 227, 203, 217, 122, 257, + /* 2280 */ 127, 128, 203, 235, 127, 128, 285, 127, 128, 175, + /* 2290 */ 217, 4, 178, 77, 180, 181, 217, 140, 119, 83, + /* 2300 */ 140, 272, 127, 128, 156, 226, 227, 127, 128, 258, + /* 2310 */ 251, 285, 4, 203, 235, 140, 100, 101, 173, 170, + /* 2320 */ 140, 120, 4, 259, 108, 127, 128, 217, 127, 128, + /* 2330 */ 171, 203, 116, 285, 16, 261, 172, 121, 203, 203, + /* 2340 */ 107, 125, 231, 127, 128, 217, 203, 116, 4, 133, + /* 2350 */ 4, 294, 217, 217, 226, 227, 140, 141, 142, 143, + /* 2360 */ 217, 203, 4, 235, 285, 203, 107, 127, 128, 226, + /* 2370 */ 227, 118, 289, 260, 56, 217, 182, 169, 235, 217, + /* 2380 */ 35, 127, 128, 313, 186, 127, 128, 127, 128, 127, + /* 2390 */ 128, 175, 203, 203, 178, 77, 180, 181, 203, 261, + /* 2400 */ 140, 83, 57, 276, 27, 46, 217, 217, 63, 155, + /* 2410 */ 276, 321, 217, 285, 127, 128, 226, 227, 100, 101, + /* 2420 */ 4, 226, 227, 4, 203, 235, 108, 140, 285, 189, + /* 2430 */ 235, 119, 4, 203, 116, 127, 128, 321, 217, 121, + /* 2440 */ 272, 253, 157, 125, 326, 127, 128, 217, 193, 187, + /* 2450 */ 4, 133, 228, 228, 109, 203, 226, 227, 140, 141, + /* 2460 */ 142, 143, 117, 39, 145, 235, 120, 122, 203, 217, + /* 2470 */ 238, 127, 128, 127, 128, 285, 168, 203, 203, 228, + /* 2480 */ 285, 203, 217, 144, 140, 127, 128, 230, 238, 228, + /* 2490 */ 145, 217, 217, 175, 203, 217, 178, 203, 180, 181, + /* 2500 */ 231, 226, 227, 158, 203, 160, 161, 228, 217, 203, + /* 2510 */ 235, 217, 228, 231, 169, 285, 171, 172, 217, 174, + /* 2520 */ 226, 227, 119, 217, 228, 203, 203, 228, 203, 235, + /* 2530 */ 206, 231, 226, 227, 176, 272, 120, 192, 203, 217, + /* 2540 */ 217, 235, 217, 127, 128, 203, 127, 128, 119, 226, + /* 2550 */ 227, 119, 217, 203, 206, 127, 128, 203, 235, 217, + /* 2560 */ 285, 226, 227, 33, 203, 272, 203, 217, 226, 227, + /* 2570 */ 235, 217, 126, 127, 128, 182, 300, 235, 217, 285, + /* 2580 */ 217, 301, 119, 257, 174, 278, 245, 226, 227, 81, + /* 2590 */ 119, 285, 278, 257, 175, 119, 235, 281, 203, 116, + /* 2600 */ 203, 284, 203, 175, 145, 203, 278, 278, 285, 257, + /* 2610 */ 245, 206, 217, 119, 217, 272, 217, 203, 206, 217, + /* 2620 */ 285, 226, 227, 226, 227, 226, 227, 285, 226, 227, + /* 2630 */ 235, 217, 235, 257, 235, 119, 272, 235, 203, 247, + /* 2640 */ 226, 227, 78, 203, 119, 247, 285, 119, 247, 235, + /* 2650 */ 119, 247, 217, 119, 176, 295, 272, 217, 119, 304, + /* 2660 */ 20, 226, 227, 272, 119, 203, 226, 227, 272, 203, + /* 2670 */ 235, 203, 304, 119, 119, 235, 306, 17, 306, 217, + /* 2680 */ 285, 254, 285, 217, 285, 217, 256, 285, 226, 227, + /* 2690 */ 229, 297, 226, 227, 226, 227, 290, 235, 260, 285, + /* 2700 */ 203, 235, 260, 235, 286, 203, 297, 203, 271, 273, + /* 2710 */ 229, 236, 240, 256, 217, 230, 273, 273, 201, 217, + /* 2720 */ 285, 217, 257, 226, 227, 285, 219, 254, 226, 227, + /* 2730 */ 226, 227, 235, 219, 219, 287, 203, 235, 219, 235, + /* 2740 */ 299, 203, 252, 298, 246, 324, 237, 285, 237, 270, + /* 2750 */ 217, 285, 226, 285, 241, 217, 203, 237, 226, 226, + /* 2760 */ 227, 310, 203, 73, 226, 227, 4, 327, 235, 327, + /* 2770 */ 217, 327, 327, 235, 327, 327, 217, 327, 327, 226, + /* 2780 */ 227, 203, 285, 203, 327, 226, 227, 285, 235, 285, + /* 2790 */ 327, 327, 327, 327, 235, 217, 327, 217, 327, 327, + /* 2800 */ 327, 327, 327, 327, 226, 227, 226, 227, 327, 327, + /* 2810 */ 327, 327, 327, 235, 327, 235, 327, 327, 285, 327, + /* 2820 */ 327, 327, 327, 285, 327, 327, 327, 327, 327, 327, + /* 2830 */ 327, 327, 327, 327, 327, 327, 327, 327, 285, 327, + /* 2840 */ 327, 327, 327, 327, 285, 327, 327, 327, 327, 327, + /* 2850 */ 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + /* 2860 */ 327, 327, 327, 285, 327, 285, }; -#define YY_SHIFT_USE_DFLT (-101) -#define YY_SHIFT_COUNT (513) -#define YY_SHIFT_MIN (-100) -#define YY_SHIFT_MAX (2642) +#define YY_SHIFT_USE_DFLT (-131) +#define YY_SHIFT_COUNT (534) +#define YY_SHIFT_MIN (-130) +#define YY_SHIFT_MAX (2762) static const short yy_shift_ofst[] = { - /* 0 */ 1352, 828, 1151, 1708, 1045, 1777, 1671, 2219, 2219, 2219, - /* 10 */ 1753, 244, 2113, 2343, 2343, 2343, 2343, 2343, 2343, 2449, - /* 20 */ 2343, 742, -4, 120, 2007, 1901, 2343, 2343, 2343, 2343, - /* 30 */ 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, - /* 40 */ 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, - /* 50 */ 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, - /* 60 */ 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 779, - /* 70 */ 1062, 1062, -1, 518, 518, 1744, 66, 15, 1016, 1744, - /* 80 */ 2226, 2226, 2226, 2189, 2226, 748, 748, 2203, 400, 365, - /* 90 */ 151, 69, 1763, 1130, 1852, 1845, 1293, 682, 1187, 682, - /* 100 */ 1183, 1006, 436, 967, 2278, 2237, 2237, 2237, 2237, 285, - /* 110 */ 2237, 2226, 2203, 2203, 400, 1685, 1685, 1454, 530, 530, - /* 120 */ 956, 845, 191, 223, 306, 1626, 1158, 945, 439, 642, - /* 130 */ 314, 1236, 795, 1236, 88, 797, 343, 466, 85, 2184, - /* 140 */ 2174, 2169, 2154, 2000, 1662, 2205, 2104, 2180, 1447, 2149, - /* 150 */ 2080, 1986, 1967, 1417, 2025, 1955, 1931, 1194, 1857, 1831, - /* 160 */ 368, 1825, 1740, 1701, 1701, 1701, 1289, 811, 1070, 437, - /* 170 */ 437, 799, 684, 674, 551, 516, 84, 1655, 1633, 1508, - /* 180 */ 1162, 2203, 1166, 1166, 1207, 1069, 497, 2571, 2571, 2642, - /* 190 */ 2571, 2571, -101, -101, 447, 739, 690, 653, -72, 610, - /* 200 */ 610, 610, 610, 610, 610, 776, 415, 927, 569, 1582, - /* 210 */ 1554, 1523, 1495, 1465, 1435, 1405, 1375, 1345, 1303, 1276, - /* 220 */ 1250, 1007, 1842, 1612, 2284, 2514, 2514, -37, 626, 626, - /* 230 */ 626, 626, -59, 202, 115, 348, 348, -17, 566, 486, - /* 240 */ 208, 208, 208, 170, 1093, 358, 116, 97, 752, 363, - /* 250 */ 1110, 1079, 877, 196, 603, 974, 154, 487, 735, 735, - /* 260 */ 76, 1706, 595, 581, 735, 836, 598, 598, 855, 581, - /* 270 */ 345, 345, 375, 253, 134, 2527, 2441, 2439, 2430, 2523, - /* 280 */ 2523, 2425, 2368, 2422, 2455, 2420, 2455, 2418, 2455, 2415, - /* 290 */ 2455, 2411, 2089, 2048, 2399, 2089, 2424, 2048, 2371, 2398, - /* 300 */ 2393, 2048, 2390, 2424, 2048, 2381, 2319, 2444, 2348, 2089, - /* 310 */ 2324, 2133, 2089, 2313, 2133, 2222, 2222, 2222, 2222, 2328, - /* 320 */ 2133, 2222, 2264, 2222, 2328, 2222, 2222, 2152, 2177, 2183, - /* 330 */ 2269, 2269, 2242, 2242, 2061, 2114, 2088, 2151, 2133, 2162, - /* 340 */ 2130, 2061, 2053, 2056, 2048, 2054, 2083, 2117, 2060, 2089, - /* 350 */ 2074, 2074, 2028, 2028, 2028, 2028, -101, -101, -101, -101, - /* 360 */ -101, -101, -101, -101, -101, -101, 1090, -65, 238, 60, - /* 370 */ 194, 697, 79, -38, 1690, 1644, 1066, 1614, 81, 1604, - /* 380 */ 1575, 1294, 1170, 1147, 183, 558, -29, 1050, 1002, 129, - /* 390 */ 878, 848, 148, 138, 2045, 2032, 1979, 1957, 1961, 1956, - /* 400 */ 1948, 1953, 1902, 1949, 1946, 1909, 1878, 1992, 1912, 1982, - /* 410 */ 1970, 1987, 1904, 1903, 1854, 1836, 1855, 1969, 1968, 1794, - /* 420 */ 1874, 1870, 1869, 1838, 1774, 1759, 1891, 1770, 1795, 1783, - /* 430 */ 1827, 1745, 1755, 1764, 1805, 1756, 1752, 1736, 1729, 1789, - /* 440 */ 1828, 1718, 1668, 1822, 1642, 1725, 1741, 1618, 1584, 1624, - /* 450 */ 1608, 1581, 1551, 1533, 1427, 1474, 1475, 1464, 1445, 1427, - /* 460 */ 1443, 1412, 1402, 1336, 1374, 1366, 1297, 1324, 1430, 1400, - /* 470 */ 1195, 1297, 1355, 1346, 1248, 1216, 1165, 1163, 1177, 1109, - /* 480 */ 1095, 1074, 977, 1021, 926, 830, 816, 807, 873, 787, - /* 490 */ 707, 786, 522, 686, 649, 616, 522, 523, 448, 407, - /* 500 */ 344, 362, 308, 278, 272, 235, 184, 119, 105, -81, - /* 510 */ 38, -43, -89, -100, + /* 0 */ 1591, 808, 1446, 138, 1318, 1603, 1364, 2114, 2114, 2114, + /* 10 */ 1260, 216, 2012, 2216, 2318, 2216, 2216, 2216, 2216, 2216, + /* 20 */ 2216, 949, -4, 106, 1910, 1808, 2216, 2216, 2216, 2216, + /* 30 */ 2216, 2216, 2216, 2216, 2216, 2216, 2216, 2216, 2318, 2216, + /* 40 */ 2216, 2216, 2216, 2216, 2216, 2216, 2216, 2216, 2216, 2216, + /* 50 */ 2216, 2216, 2216, 2216, 2216, 2216, 2216, 2216, 2216, 2216, + /* 60 */ 2216, 2216, 2216, 2216, 2216, 2216, 2216, 2216, 2216, 2216, + /* 70 */ 2216, 2216, 1746, 1463, 1463, 587, 1400, 1400, 2446, 89, + /* 80 */ 259, 592, 752, 2446, 2419, 2419, 2419, 2416, 2419, 494, + /* 90 */ 494, 2258, 978, 338, 786, 142, 1994, 1772, 1853, 1885, + /* 100 */ 1966, 1295, 1902, 1295, 1850, 1473, 1605, 752, 955, 2358, + /* 110 */ 2428, 2428, 2428, 2428, 143, 2428, 2419, 2258, 2258, 978, + /* 120 */ 750, 750, 1476, 519, 519, -43, 811, 1382, 1750, 1825, + /* 130 */ 1534, 1348, 1261, 623, 1490, 285, 232, 1244, 232, 1425, + /* 140 */ 127, 594, 220, 512, 2346, 2344, 2262, 2240, 2308, 2254, + /* 150 */ 2287, 2201, 2260, 2198, 2180, 2175, 2160, 2055, 2050, 662, + /* 160 */ 2153, 2157, 2121, 2058, 2035, 2072, 1134, 1104, 858, 858, + /* 170 */ 858, 1892, 1917, 1971, 277, 277, 1731, 1817, 1530, 1197, + /* 180 */ 1868, 762, 1688, 1662, 1566, 1432, 2258, 2258, 764, 764, + /* 190 */ 1077, 498, 200, 2690, 2690, 2762, 2690, 2690, -131, -131, + /* 200 */ -131, 421, 601, 601, 601, 601, 601, 601, 601, 601, + /* 210 */ 601, 601, 571, 388, 704, 677, 1214, 1185, 1153, -73, + /* 220 */ 1122, 1091, 1060, 1028, 997, 963, 936, 909, 754, 1547, + /* 230 */ 1257, 1704, 1744, 1744, 482, -16, -16, -16, -16, -16, + /* 240 */ -16, 204, 346, -35, -130, 406, 406, 862, 420, 1248, + /* 250 */ 1248, 1248, 199, 193, 334, -8, 57, 784, 377, 1638, + /* 260 */ 1562, 1602, 768, 1381, -67, 292, 589, 770, 734, 734, + /* 270 */ -79, 1292, 695, 627, 734, 190, 375, 375, 77, 627, + /* 280 */ 313, 313, 23, 131, 977, 2660, 2660, 2555, 2554, 2545, + /* 290 */ 2640, 2640, 2539, 2478, 2534, 2564, 2531, 2564, 2528, 2564, + /* 300 */ 2525, 2564, 2516, 2191, 2145, 2494, 2191, 2508, 2410, 2145, + /* 310 */ 2410, 2459, 2483, 2476, 2410, 2145, 2471, 2508, 2410, 2145, + /* 320 */ 2463, 2393, 2530, 2432, 2191, 2429, 2231, 2191, 2403, 2231, + /* 330 */ 2319, 2319, 2319, 2319, 2424, 2231, 2319, 2339, 2319, 2424, + /* 340 */ 2319, 2319, 2255, 2285, 2312, 2377, 2377, 2359, 2359, 2164, + /* 350 */ 2208, 2194, 2253, 2259, 2231, 2233, 2233, 2164, 2159, 2149, + /* 360 */ 2145, 2148, 2179, 2156, 2191, 2172, 2172, 2101, 2101, 2101, + /* 370 */ 2101, -131, -131, -131, -131, -131, -131, -131, -131, -131, + /* 380 */ -131, 2345, 40, 407, 798, 79, 533, 64, 343, 1256, + /* 390 */ 1209, -74, 1116, -22, 1085, 1054, 844, 413, 340, -5, + /* 400 */ 720, 401, 245, 209, -28, 65, 201, 87, -80, 167, + /* 410 */ 2123, 2099, 2056, 2028, 2030, 2021, 2007, 2020, 1961, 2009, + /* 420 */ 2003, 1963, 1920, 2053, 1978, 2033, 2013, 2052, 1965, 1960, + /* 430 */ 1908, 1864, 1886, 1980, 1989, 1827, 1912, 1907, 1904, 1889, + /* 440 */ 1834, 1847, 1803, 1949, 1815, 1841, 1833, 1856, 1805, 1793, + /* 450 */ 1804, 1865, 1797, 1795, 1790, 1788, 1848, 1884, 1778, 1769, + /* 460 */ 1859, 1745, 1776, 1835, 1708, 1687, 1698, 1706, 1705, 1701, + /* 470 */ 1700, 1641, 1610, 1697, 1658, 1654, 1641, 1656, 1628, 1621, + /* 480 */ 1572, 1608, 1606, 1553, 1604, 1677, 1674, 1540, 1553, 1611, + /* 490 */ 1582, 1579, 1544, 1500, 1486, 1482, 1444, 1470, 1443, 1469, + /* 500 */ 1320, 1309, 1282, 1290, 1286, 1362, 1202, 1172, 1068, 1082, + /* 510 */ 986, 1129, 991, 954, 1024, 987, 991, 870, 842, 829, + /* 520 */ 777, 812, 732, 608, 517, 574, 437, 419, 379, 242, + /* 530 */ 358, 114, 76, 50, 11, }; -#define YY_REDUCE_USE_DFLT (-228) -#define YY_REDUCE_COUNT (365) -#define YY_REDUCE_MIN (-227) -#define YY_REDUCE_MAX (2757) +#define YY_REDUCE_USE_DFLT (-105) +#define YY_REDUCE_COUNT (380) +#define YY_REDUCE_MIN (-104) +#define YY_REDUCE_MAX (2580) static const short yy_reduce_ofst[] = { - /* 0 */ 389, -131, -8, 117, 1524, 366, 123, 958, 717, 118, - /* 10 */ -66, 1806, 1767, 1823, 1773, 1762, 1600, 1030, 1593, 1576, - /* 20 */ 1013, 875, 2757, 2754, 2720, 2695, 2693, 2691, 2676, 2661, - /* 30 */ 2657, 2655, 2641, 2625, 2605, 2601, 2589, 2573, 2559, 2557, - /* 40 */ 2554, 2520, 2494, 2482, 2480, 2468, 2461, 2454, 2442, 2435, - /* 50 */ 2428, 2359, 2354, 2277, 2272, 2238, 2213, 2206, 2178, 2132, - /* 60 */ 2046, 2041, 2026, 1980, 1975, 1940, 1935, 1929, 1835, 172, - /* 70 */ 670, 386, -130, 866, 760, -101, 211, 1265, 1113, 216, - /* 80 */ 1882, 1814, 1531, 1027, 699, 858, 688, -166, 361, 1737, - /* 90 */ 765, 868, 2194, 2058, 1964, 1883, 1686, 1528, 1561, 55, - /* 100 */ 1521, 1064, 933, 394, 751, 1779, 1695, 1640, 996, -227, - /* 110 */ 122, 1539, 605, -119, 773, 606, -173, 321, 1638, 1563, - /* 120 */ 893, 525, 2289, 2279, 2239, 1888, 1888, 2275, 2274, 2271, - /* 130 */ 2263, 1211, 2246, 876, 1888, 1619, 1261, 2239, 2234, 2232, - /* 140 */ 1888, 1888, 1888, 1888, 1888, 1888, 1888, 2214, 1888, 1888, - /* 150 */ 1888, 1888, 2211, 1888, 1888, 1888, 2210, 2204, 2124, 2122, - /* 160 */ 2110, 2103, 2082, 1587, 1082, 1059, 2001, 1973, 1943, 1171, - /* 170 */ 1111, 1914, 1899, 1888, 1861, 1761, 1012, 1713, 1684, 1679, - /* 180 */ 1672, 1530, 1098, 1075, 1084, 1152, 824, 931, 710, 1103, - /* 190 */ 646, 189, 851, -74, 1817, 1817, 1817, 1817, 1817, 1817, - /* 200 */ 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, - /* 210 */ 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, - /* 220 */ 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, - /* 230 */ 1817, 1817, 1817, 1817, 2416, 2427, 2423, 1817, 2335, 2335, - /* 240 */ 2413, 2410, 2404, 2372, 2318, 2391, 2382, 2350, 2337, 2336, - /* 250 */ 2412, 2409, 2407, 2406, 2358, 2401, 2340, 2346, 2326, 2325, - /* 260 */ 2365, 2356, 2352, 2355, 2306, 2302, 2327, 2322, 2286, 2341, - /* 270 */ 2273, 2270, 1817, 2312, 2310, 2260, 2150, 2285, 2283, 2247, - /* 280 */ 2244, 2282, 2251, 2150, 2295, 2150, 2293, 2150, 2291, 2150, - /* 290 */ 2287, 2259, 2321, 2276, 2257, 2320, 2281, 2268, 2236, 2235, - /* 300 */ 2150, 2256, 2150, 2265, 2248, 2150, 2200, 2196, 2202, 2253, - /* 310 */ 2150, 2228, 2250, 2150, 2221, 2216, 2209, 2207, 2197, 2182, - /* 320 */ 2195, 2191, 2166, 2145, 2112, 2129, 2123, 2016, 2071, 2047, - /* 330 */ 1997, 1977, 2013, 2009, 2019, 2011, 1981, 1972, 2018, 1989, - /* 340 */ 1817, 1983, 1978, 1971, 1963, 1952, 1928, 1985, 1966, 1984, - /* 350 */ 1954, 1933, 1962, 1942, 1939, 1934, 1813, 1811, 1812, 1880, - /* 360 */ 1872, 1840, 1817, 1817, 1817, 1885, + /* 0 */ 154, 119, 1164, 619, 1252, 1189, 176, 1167, 329, -45, + /* 10 */ -104, 1649, -101, 1545, 1481, 1479, 1464, 1452, 1314, 1298, + /* 20 */ 183, 691, 2580, 2578, 2559, 2553, 2538, 2533, 2504, 2502, + /* 30 */ 2497, 2468, 2466, 2462, 2440, 2435, 2414, 2402, 2399, 2397, + /* 40 */ 2395, 2361, 2342, 2335, 2323, 2306, 2294, 2275, 2230, 2195, + /* 50 */ 2190, 2143, 2128, 2079, 2048, 2026, 2001, 1986, 1941, 1939, + /* 60 */ 1924, 1875, 1846, 1844, 1831, 1829, 1798, 1782, 1737, 1720, + /* 70 */ 1653, 660, 622, 18, -23, 513, 428, 54, 337, 615, + /* 80 */ 721, 1126, 525, 1538, 1680, 1492, 1468, 760, -102, 683, + /* 90 */ 335, 507, 348, 1111, 515, 1395, 2043, 2016, 1900, 1879, + /* 100 */ 1725, 1204, 1550, 920, 1375, 1125, 696, 1193, 439, 778, + /* 110 */ 1906, 1740, 1702, 1565, 405, 1283, 667, 62, -84, 736, + /* 120 */ 540, 240, 483, 1493, 1431, 1311, 373, 2363, 2354, 2322, + /* 130 */ 1894, 1894, 2350, 2325, 2136, 1932, 1227, 1714, 723, 1894, + /* 140 */ 1454, 1394, 2322, 2301, 2291, 1894, 1894, 1894, 1894, 1894, + /* 150 */ 1894, 1894, 2278, 1894, 1894, 1894, 1894, 2274, 1894, 1894, + /* 160 */ 1894, 2265, 2252, 2221, 2189, 2162, 2158, 2135, 1357, 1061, + /* 170 */ 998, 2110, 2073, 1665, 1346, 1296, 2060, 1993, 1894, 1765, + /* 180 */ 1678, 1376, 1652, 1557, 1480, 1278, 1251, 1225, 1266, 1217, + /* 190 */ 971, 1319, 469, 1307, 1302, 831, 1233, 1168, 158, 1201, + /* 200 */ 645, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, + /* 210 */ 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, + /* 220 */ 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, + /* 230 */ 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, + /* 240 */ 1887, 1887, 1887, 1887, 2513, 2532, 2526, 2451, 2451, 2520, + /* 250 */ 2511, 2509, 2479, 2421, 2498, 2490, 2448, 2441, 2445, 2519, + /* 260 */ 2515, 2514, 1887, 2507, 2473, 2517, 2465, 2457, 2444, 2443, + /* 270 */ 2485, 2472, 2475, 2481, 2436, 2437, 2442, 2438, 2406, 2461, + /* 280 */ 2409, 2394, 2418, 2430, 2427, 2372, 2370, 2263, 2396, 2391, + /* 290 */ 2368, 2355, 2384, 2360, 2263, 2404, 2263, 2401, 2263, 2398, + /* 300 */ 2263, 2392, 2364, 2412, 2376, 2343, 2405, 2365, 2329, 2352, + /* 310 */ 2328, 2317, 2316, 2263, 2314, 2336, 2263, 2341, 2307, 2326, + /* 320 */ 2263, 2280, 2276, 2293, 2348, 2263, 2300, 2324, 2263, 2282, + /* 330 */ 2299, 2296, 2284, 2279, 2250, 2269, 2261, 2257, 2251, 2232, + /* 340 */ 2225, 2224, 2118, 2188, 2168, 2116, 2090, 2134, 2127, 2138, + /* 350 */ 2113, 2083, 2070, 2057, 2111, 1887, 1887, 2074, 2064, 2051, + /* 360 */ 2022, 2059, 2029, 2061, 2062, 2063, 2041, 2038, 2032, 2023, + /* 370 */ 2015, 1905, 1897, 1872, 1974, 1969, 1929, 1887, 1887, 1887, + /* 380 */ 1956, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 849, 1158, 1158, 1277, 1158, 1158, 1158, 1158, 1158, 1158, - /* 10 */ 1277, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, - /* 20 */ 1158, 1277, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, - /* 30 */ 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, - /* 40 */ 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, - /* 50 */ 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, - /* 60 */ 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1029, - /* 70 */ 1316, 1316, 1316, 1293, 1293, 1316, 1022, 1316, 1316, 1316, - /* 80 */ 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 896, 1018, - /* 90 */ 886, 1029, 1316, 1316, 1316, 1316, 1316, 1089, 1103, 1089, - /* 100 */ 1081, 1072, 1316, 1316, 1194, 1097, 1097, 1097, 1097, 969, - /* 110 */ 1097, 1316, 1316, 1316, 1316, 1129, 1128, 1316, 1057, 1057, - /* 120 */ 1160, 1316, 1247, 1252, 1316, 1316, 1316, 1316, 1316, 1316, - /* 130 */ 1090, 1316, 1316, 1316, 1030, 1018, 1293, 1316, 1316, 1316, - /* 140 */ 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1104, - /* 150 */ 1082, 1073, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, - /* 160 */ 1316, 1316, 1316, 1018, 1018, 1018, 1316, 1316, 1316, 1293, - /* 170 */ 1293, 1316, 1316, 1316, 1316, 1316, 883, 1316, 1316, 1316, - /* 180 */ 855, 1316, 1293, 1293, 849, 1277, 1051, 1277, 1277, 891, - /* 190 */ 1277, 1277, 1270, 1008, 1067, 1160, 1160, 1160, 1160, 1035, - /* 200 */ 1077, 1065, 1069, 1171, 1068, 1160, 1160, 1160, 1160, 1160, - /* 210 */ 1160, 1160, 1160, 1160, 1160, 1160, 1160, 1160, 1160, 1160, - /* 220 */ 1160, 1160, 1160, 1160, 1131, 1143, 1130, 1138, 1147, 1139, - /* 230 */ 1142, 1133, 1132, 1134, 1316, 1316, 1316, 1135, 1316, 1316, - /* 240 */ 1316, 1316, 1316, 1021, 1316, 1316, 991, 1316, 1316, 1221, - /* 250 */ 1316, 1316, 898, 1316, 1006, 858, 1076, 1019, 1047, 1047, - /* 260 */ 936, 960, 920, 1057, 1047, 1037, 1051, 1051, 1166, 1057, - /* 270 */ 1316, 1316, 1136, 1019, 1006, 1261, 1038, 1038, 1038, 1246, - /* 280 */ 1246, 1038, 1194, 1038, 982, 1038, 982, 1038, 982, 1038, - /* 290 */ 982, 1038, 880, 1076, 1038, 880, 973, 1076, 1109, 1093, - /* 300 */ 1038, 1076, 1038, 973, 1076, 1038, 1228, 1226, 1038, 880, - /* 310 */ 1038, 1179, 880, 1038, 1179, 971, 971, 971, 971, 952, - /* 320 */ 1179, 971, 936, 971, 952, 971, 971, 1312, 1316, 1038, - /* 330 */ 1303, 1303, 1060, 1060, 1066, 1051, 1316, 1185, 1179, 1160, - /* 340 */ 1137, 1066, 1064, 1061, 1076, 1316, 1038, 877, 955, 880, - /* 350 */ 866, 866, 854, 854, 854, 854, 1274, 1274, 1270, 938, - /* 360 */ 938, 1024, 1146, 1145, 1144, 907, 1159, 1316, 1316, 1316, - /* 370 */ 1316, 1316, 1316, 1195, 1316, 1316, 1316, 1316, 1316, 1316, - /* 380 */ 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, - /* 390 */ 1316, 1316, 1316, 1316, 1316, 850, 1316, 1316, 1316, 1316, - /* 400 */ 1316, 1264, 1316, 1316, 1316, 1316, 1316, 1316, 1225, 1224, - /* 410 */ 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, - /* 420 */ 1316, 1316, 1316, 1316, 1316, 1316, 1213, 1316, 1316, 1316, - /* 430 */ 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, - /* 440 */ 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, - /* 450 */ 1316, 1316, 1316, 1316, 994, 1000, 1316, 1316, 1316, 995, - /* 460 */ 1316, 1316, 1122, 1316, 1316, 1316, 1169, 1316, 1316, 1316, - /* 470 */ 1316, 1062, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, - /* 480 */ 1316, 1316, 1316, 1316, 1316, 1316, 1309, 1052, 1316, 1316, - /* 490 */ 1159, 1316, 1279, 1316, 1316, 1316, 1278, 1316, 1316, 1316, - /* 500 */ 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1123, 1122, 1161, - /* 510 */ 889, 1316, 864, 1316, 846, 851, 1263, 1260, 1262, 1257, - /* 520 */ 1258, 1256, 1259, 1255, 1253, 1254, 1251, 1249, 1248, 1250, - /* 530 */ 1245, 1241, 1201, 1199, 1197, 1206, 1205, 1204, 1203, 1202, - /* 540 */ 1198, 1196, 1200, 1192, 1191, 1100, 1079, 1070, 989, 1240, - /* 550 */ 1238, 1239, 1190, 1188, 1189, 988, 987, 986, 981, 980, - /* 560 */ 979, 978, 1267, 1276, 1275, 1273, 1272, 1271, 1265, 1266, - /* 570 */ 1177, 1176, 1174, 1173, 1175, 882, 1217, 1220, 1219, 1218, - /* 580 */ 1223, 1222, 1215, 1227, 1232, 1231, 1236, 1235, 1234, 1233, - /* 590 */ 1230, 1212, 1108, 1107, 1105, 1102, 1112, 1111, 1110, 1101, - /* 600 */ 1094, 1106, 1084, 1092, 1091, 1080, 1083, 974, 1075, 1071, - /* 610 */ 1074, 990, 1216, 985, 984, 983, 881, 876, 1040, 875, - /* 620 */ 874, 885, 958, 959, 967, 970, 965, 968, 964, 963, - /* 630 */ 962, 966, 961, 957, 888, 887, 897, 951, 934, 923, - /* 640 */ 890, 925, 922, 921, 926, 943, 942, 949, 948, 947, - /* 650 */ 946, 945, 941, 944, 940, 939, 927, 919, 918, 937, - /* 660 */ 917, 954, 953, 950, 916, 977, 976, 975, 972, 915, - /* 670 */ 914, 913, 912, 911, 910, 1157, 1315, 1311, 1314, 1313, - /* 680 */ 1310, 1156, 1162, 1150, 1148, 992, 1003, 1002, 1001, 998, - /* 690 */ 999, 1013, 1011, 1010, 1009, 1046, 1045, 1044, 1043, 1042, - /* 700 */ 1041, 1034, 1032, 1027, 1026, 1033, 1031, 1028, 1049, 1050, - /* 710 */ 1048, 1025, 1017, 1015, 1016, 1014, 1099, 1096, 1098, 1095, - /* 720 */ 1036, 1023, 1020, 1007, 1291, 1289, 1292, 1290, 1288, 1296, - /* 730 */ 1298, 1297, 1302, 1300, 1299, 1295, 1308, 1307, 1306, 1305, - /* 740 */ 1304, 1294, 1301, 1287, 1286, 1285, 1284, 1054, 1059, 1058, - /* 750 */ 1053, 997, 1161, 1149, 1159, 1153, 1282, 1280, 1283, 1182, - /* 760 */ 1184, 1187, 1186, 1183, 1056, 1055, 1181, 1180, 1281, 1152, - /* 770 */ 1127, 903, 901, 902, 1209, 1208, 1211, 1210, 1207, 905, - /* 780 */ 904, 900, 899, 1125, 1121, 1120, 1237, 1154, 1155, 1119, - /* 790 */ 1124, 1117, 1116, 1115, 1141, 1140, 1126, 1118, 892, 996, - /* 800 */ 993, 1151, 1114, 1039, 1113, 933, 932, 931, 930, 929, - /* 810 */ 928, 1005, 1004, 909, 924, 908, 906, 884, 879, 878, - /* 820 */ 873, 871, 868, 870, 867, 872, 869, 865, 863, 862, - /* 830 */ 861, 860, 859, 895, 894, 893, 889, 857, 856, 853, - /* 840 */ 852, 848, 847, 845, + /* 0 */ 875, 1195, 1195, 1315, 1195, 1195, 1195, 1195, 1195, 1195, + /* 10 */ 1315, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, + /* 20 */ 1195, 1315, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, + /* 30 */ 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, + /* 40 */ 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, + /* 50 */ 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, + /* 60 */ 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, + /* 70 */ 1195, 1195, 1059, 1357, 1357, 1357, 1334, 1334, 1357, 1052, + /* 80 */ 1357, 1357, 903, 1357, 1357, 1357, 1357, 1357, 1357, 1357, + /* 90 */ 1357, 1357, 926, 1048, 916, 1059, 1357, 1357, 1357, 1357, + /* 100 */ 1357, 1121, 1135, 1121, 1113, 1102, 1357, 1357, 1357, 1231, + /* 110 */ 1129, 1129, 1129, 1129, 999, 1129, 1357, 1357, 1357, 1357, + /* 120 */ 1163, 1162, 1357, 1087, 1087, 1197, 1357, 1284, 1289, 1156, + /* 130 */ 1357, 1357, 1357, 1357, 1357, 1122, 1357, 1357, 1357, 1060, + /* 140 */ 1048, 1334, 1156, 1357, 1357, 1357, 1357, 1357, 1357, 1357, + /* 150 */ 1357, 1357, 1357, 1357, 1136, 1114, 1103, 1357, 1357, 1357, + /* 160 */ 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1048, 1048, + /* 170 */ 1048, 1357, 1357, 1357, 1334, 1334, 1357, 1158, 1357, 1357, + /* 180 */ 1357, 913, 1357, 1357, 1357, 881, 1357, 1357, 1334, 1334, + /* 190 */ 875, 1315, 1081, 1315, 1315, 921, 1315, 1315, 1308, 1038, + /* 200 */ 1038, 1097, 1120, 1119, 1118, 1117, 1065, 1107, 1095, 1099, + /* 210 */ 1208, 1098, 1197, 1197, 1197, 1197, 1197, 1197, 1197, 1197, + /* 220 */ 1197, 1197, 1197, 1197, 1197, 1197, 1197, 1197, 1197, 1197, + /* 230 */ 1197, 1165, 1179, 1164, 1172, 1184, 1173, 1178, 1177, 1176, + /* 240 */ 1167, 1166, 1168, 1169, 1357, 1357, 1357, 1357, 1357, 1357, + /* 250 */ 1357, 1357, 1051, 1357, 1357, 1021, 1357, 1357, 1258, 1357, + /* 260 */ 1357, 928, 1170, 1357, 1036, 884, 1106, 1049, 1077, 1077, + /* 270 */ 966, 990, 950, 1087, 1077, 1067, 1081, 1081, 1203, 1087, + /* 280 */ 1357, 1357, 1197, 1049, 1036, 1299, 1299, 1068, 1068, 1068, + /* 290 */ 1283, 1283, 1068, 1231, 1068, 1012, 1068, 1012, 1068, 1012, + /* 300 */ 1068, 1012, 1068, 910, 1106, 1068, 910, 1003, 1109, 1106, + /* 310 */ 1109, 1141, 1125, 1068, 1109, 1106, 1068, 1003, 1109, 1106, + /* 320 */ 1068, 1265, 1263, 1068, 910, 1068, 1216, 910, 1068, 1216, + /* 330 */ 1001, 1001, 1001, 1001, 982, 1216, 1001, 966, 1001, 982, + /* 340 */ 1001, 1001, 1353, 1357, 1068, 1344, 1344, 1090, 1090, 1096, + /* 350 */ 1081, 1357, 1357, 1222, 1216, 1183, 1171, 1096, 1094, 1091, + /* 360 */ 1106, 1357, 1068, 985, 910, 892, 892, 880, 880, 880, + /* 370 */ 880, 1312, 1312, 1308, 968, 968, 1054, 1182, 1181, 1180, + /* 380 */ 937, 1196, 1357, 1357, 1357, 1357, 1357, 1357, 1232, 1357, + /* 390 */ 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, + /* 400 */ 1357, 1357, 1357, 1357, 1357, 1318, 1357, 1357, 1357, 1357, + /* 410 */ 1357, 876, 1357, 1357, 1357, 1357, 1357, 1302, 1357, 1357, + /* 420 */ 1357, 1357, 1357, 1357, 1262, 1261, 1357, 1357, 1357, 1357, + /* 430 */ 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, + /* 440 */ 1357, 1110, 1357, 1250, 1357, 1357, 1357, 1357, 1357, 1357, + /* 450 */ 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, + /* 460 */ 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, + /* 470 */ 1357, 1024, 1030, 1357, 1357, 1357, 1025, 1357, 1357, 1154, + /* 480 */ 1357, 1357, 1357, 1206, 1357, 1357, 1357, 1357, 1092, 1357, + /* 490 */ 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, + /* 500 */ 1357, 1357, 1357, 1350, 1082, 1357, 1357, 1357, 1198, 1357, + /* 510 */ 1196, 1357, 1317, 1357, 1357, 1357, 1316, 1357, 1357, 1357, + /* 520 */ 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1155, 1154, 1198, + /* 530 */ 919, 899, 1357, 890, 1357, 872, 877, 1301, 1298, 1295, + /* 540 */ 1300, 1294, 1296, 1293, 1297, 1292, 1290, 1291, 1288, 1286, + /* 550 */ 1285, 1287, 1282, 1278, 1238, 1236, 1234, 1243, 1242, 1241, + /* 560 */ 1240, 1239, 1235, 1233, 1237, 1229, 1228, 1132, 1111, 1100, + /* 570 */ 1019, 1277, 1275, 1276, 1227, 1225, 1226, 1018, 1017, 1016, + /* 580 */ 1011, 1010, 1009, 1008, 1305, 1314, 1313, 1311, 1310, 1309, + /* 590 */ 1303, 1304, 1214, 1213, 1211, 1210, 1212, 912, 1254, 1257, + /* 600 */ 1256, 1255, 1260, 1259, 1252, 1264, 1269, 1268, 1273, 1272, + /* 610 */ 1271, 1270, 1267, 1249, 1140, 1139, 1137, 1134, 1144, 1143, + /* 620 */ 1142, 1133, 1126, 1138, 1116, 1124, 1123, 1112, 1115, 1004, + /* 630 */ 1105, 1101, 1104, 1020, 1253, 1015, 1014, 1013, 911, 902, + /* 640 */ 1070, 901, 900, 915, 988, 989, 997, 1000, 995, 998, + /* 650 */ 994, 993, 992, 996, 991, 987, 918, 917, 927, 981, + /* 660 */ 964, 953, 920, 955, 952, 951, 956, 973, 972, 979, + /* 670 */ 978, 977, 976, 975, 971, 974, 970, 969, 957, 949, + /* 680 */ 948, 967, 947, 984, 983, 980, 946, 1007, 1006, 1005, + /* 690 */ 1002, 945, 944, 943, 942, 941, 940, 1194, 1356, 1352, + /* 700 */ 1355, 1354, 1351, 1193, 1199, 1187, 1185, 1022, 1033, 1032, + /* 710 */ 1031, 1028, 1029, 1043, 1041, 1040, 1039, 1076, 1075, 1074, + /* 720 */ 1073, 1072, 1071, 1064, 1062, 1057, 1056, 1063, 1061, 1058, + /* 730 */ 1079, 1080, 1078, 1055, 1047, 1045, 1046, 1044, 1131, 1128, + /* 740 */ 1130, 1127, 1066, 1053, 1050, 1037, 1332, 1330, 1333, 1331, + /* 750 */ 1329, 1337, 1339, 1338, 1343, 1341, 1340, 1336, 1349, 1348, + /* 760 */ 1347, 1346, 1345, 1335, 1342, 1328, 1327, 1326, 1325, 1084, + /* 770 */ 1089, 1088, 1083, 1027, 1186, 1196, 1190, 1323, 1321, 1324, + /* 780 */ 1320, 1319, 1219, 1221, 1224, 1223, 1220, 1086, 1085, 1218, + /* 790 */ 1217, 1322, 1189, 1161, 933, 931, 932, 1246, 1245, 1248, + /* 800 */ 1247, 1244, 935, 934, 930, 929, 1159, 1153, 1152, 1274, + /* 810 */ 1191, 1192, 1151, 1157, 1149, 1148, 1147, 1175, 1174, 1160, + /* 820 */ 1150, 922, 1026, 1023, 1188, 1146, 1069, 1145, 963, 962, + /* 830 */ 961, 960, 959, 958, 1035, 1034, 939, 954, 938, 936, + /* 840 */ 914, 904, 909, 907, 908, 906, 905, 897, 894, 896, + /* 850 */ 893, 898, 895, 891, 889, 888, 887, 886, 885, 925, + /* 860 */ 924, 923, 919, 883, 882, 879, 878, 874, 873, 871, }; /* The next table maps tokens into fallback tokens. If a construct @@ -1122,6 +1097,7 @@ static const YYCODETYPE yyFallback[] = { 4, /* LAST => ID */ 4, /* LIKE_KW => ID */ 4, /* MATCH => ID */ + 4, /* MATERIALIZED => ID */ 4, /* NO => ID */ 4, /* NULLS => ID */ 4, /* OTHERS => ID */ @@ -1156,6 +1132,7 @@ static const YYCODETYPE yyFallback[] = { 4, /* RENAME => ID */ 4, /* CTIME_KW => ID */ 4, /* IF => ID */ + 4, /* FILTER => ID */ }; #endif /* YYFALLBACK */ @@ -1316,26 +1293,27 @@ static const char *const yyTokenName[] = { "FOLLOWING", "FOR", "GENERATED", "GROUPS", "IGNORE", "IMMEDIATE", "INDEXED", "INITIALLY", "INSTEAD", "LAST", "LIKE_KW", "MATCH", - "NO", "NULLS", "OTHERS", "PLAN", - "QUERY", "KEY", "OF", "OFFSET", - "PARTITION", "PRAGMA", "PRECEDING", "RAISE", - "RANGE", "RECURSIVE", "RELEASE", "REPLACE", - "RESTRICT", "ROW", "ROWS", "ROLLBACK", - "SAVEPOINT", "TEMP", "TIES", "TRIGGER", - "UNBOUNDED", "VACUUM", "VIEW", "VIRTUAL", - "WITH", "WITHOUT", "REINDEX", "RENAME", - "CTIME_KW", "IF", "ANY", "OR", - "AND", "NOT", "IS", "BETWEEN", - "IN", "ISNULL", "NOTNULL", "NE", - "EQ", "GT", "LE", "LT", - "GE", "ESCAPE", "BITAND", "BITOR", - "LSHIFT", "RSHIFT", "PLUS", "MINUS", - "STAR", "SLASH", "REM", "CONCAT", - "COLLATE", "BITNOT", "SEMI", "TRANSACTION", - "ID_TRANS", "COMMIT", "TO", "CREATE", - "TABLE", "LP", "RP", "AS", - "DOT", "ID_TAB_NEW", "ID_DB", "CTX_ROWID_KW", - "EXISTS", "COMMA", "ID_COL_NEW", "STRING", + "MATERIALIZED", "NO", "NULLS", "OTHERS", + "PLAN", "QUERY", "KEY", "OF", + "OFFSET", "PARTITION", "PRAGMA", "PRECEDING", + "RAISE", "RANGE", "RECURSIVE", "RELEASE", + "REPLACE", "RESTRICT", "ROW", "ROWS", + "ROLLBACK", "SAVEPOINT", "TEMP", "TIES", + "TRIGGER", "UNBOUNDED", "VACUUM", "VIEW", + "VIRTUAL", "WITH", "WITHOUT", "REINDEX", + "RENAME", "CTIME_KW", "IF", "FILTER", + "ANY", "OR", "AND", "NOT", + "IS", "BETWEEN", "IN", "ISNULL", + "NOTNULL", "NE", "EQ", "GT", + "LE", "LT", "GE", "ESCAPE", + "BITAND", "BITOR", "LSHIFT", "RSHIFT", + "PLUS", "MINUS", "STAR", "SLASH", + "REM", "CONCAT", "PTR", "COLLATE", + "BITNOT", "SEMI", "TRANSACTION", "ID_TRANS", + "COMMIT", "TO", "CREATE", "TABLE", + "LP", "RP", "AS", "DOT", + "ID_TAB_NEW", "ID_DB", "COMMA", "CTX_ROWID_KW", + "CTX_STRICT_KW", "EXISTS", "ID_COL_NEW", "STRING", "JOIN_KW", "ID_COL_TYPE", "RIGHT_ASSOC", "CONSTRAINT", "DEFAULT", "NULL", "PRIMARY", "UNIQUE", "CHECK", "REFERENCES", "ID_CONSTR", "ID_COLLATE", @@ -1347,44 +1325,45 @@ static const char *const yyTokenName[] = { "EXCEPT", "INTERSECT", "DISTINCT", "ID_ALIAS", "FROM", "USING", "JOIN", "ID_JOIN_OPTS", "ID_IDX", "ORDER", "GROUP", "HAVING", - "LIMIT", "WHERE", "ID_COL", "INTO", - "NOTHING", "ID_FN", "ID_ERR_MSG", "VARIABLE", - "CASE", "WHEN", "THEN", "ELSE", - "INDEX", "ID_IDX_NEW", "ID_PRAGMA", "ID_TRIG_NEW", - "ID_TRIG", "ALTER", "ADD", "WINDOW", - "OVER", "FILTER", "error", "cmd", + "LIMIT", "WHERE", "RETURNING", "ID_COL", + "INTO", "NOTHING", "ID_FN", "ID_ERR_MSG", + "VARIABLE", "CASE", "WHEN", "THEN", + "ELSE", "INDEX", "ID_IDX_NEW", "ID_PRAGMA", + "ID_TRIG_NEW", "ID_TRIG", "ALTER", "ADD", + "WINDOW", "OVER", "error", "cmd", "input", "cmdlist", "ecmd", "explain", "cmdx", "transtype", "trans_opt", "nm", "savepoint_opt", "temp", "ifnotexists", "fullname", "columnlist", "conslist_opt", "table_options", "select", - "column", "columnid", "type", "carglist", - "id", "id_opt", "ids", "typetoken", - "typename", "signed", "plus_num", "minus_num", - "ccons", "term", "expr", "onconf", - "sortorder", "autoinc", "idxlist_opt", "refargs", - "defer_subclause", "gen_always", "tnm", "refarg", - "refact", "init_deferred_pred_opt", "conslist", "tconscomma", - "tcons", "idxlist", "defer_subclause_opt", "resolvetype", - "orconf", "raisetype", "ifexists", "select_stmt", - "with", "selectnowith", "oneselect", "multiselect_op", - "values", "distinct", "selcollist", "from", - "where_opt", "groupby_opt", "having_opt", "orderby_opt", - "limit_opt", "window_clause", "nexprlist", "exprlist", - "sclp", "as", "joinsrc", "singlesrc", - "seltablist", "joinop", "joinconstr_opt", "dbnm", - "indexed_opt", "idlist", "sortlist", "nulls", - "delete_stmt", "update_stmt", "setlist", "idlist_opt", - "insert_stmt", "insert_cmd", "upsert", "exprx", - "not_opt", "likeop", "case_operand", "case_exprlist", - "case_else", "filter_over", "uniqueflag", "idxlist_single", - "collate", "vinto", "nmnum", "number", - "trigger_time", "trigger_event", "foreach_clause", "when_clause", - "trigger_cmd_list", "trigger_cmd", "database_kw_opt", "key_opt", - "kwcolumn_opt", "create_vtab", "vtabarglist", "vtabarg", - "vtabargtoken", "anylist", "wqlist", "wqcte", - "windowdefn_list", "windowdefn", "window", "frame_opt", - "range_or_rows", "frame_bound_s", "frame_exclude_opt", "frame_bound_e", - "frame_bound", "frame_exclude", "filter_clause", "over_clause", + "table_option", "column", "columnid", "type", + "carglist", "id", "id_opt", "ids", + "typetoken", "typename", "signed", "plus_num", + "minus_num", "ccons", "term", "expr", + "onconf", "sortorder", "autoinc", "idxlist_opt", + "refargs", "defer_subclause", "gen_always", "tnm", + "refarg", "refact", "init_deferred_pred_opt", "conslist", + "tconscomma", "tcons", "idxlist", "defer_subclause_opt", + "resolvetype", "orconf", "raisetype", "ifexists", + "select_stmt", "with", "selectnowith", "oneselect", + "multiselect_op", "values", "distinct", "selcollist", + "from", "where_opt", "groupby_opt", "having_opt", + "orderby_opt", "limit_opt", "window_clause", "nexprlist", + "exprlist", "sclp", "as", "joinsrc", + "singlesrc", "seltablist", "joinop", "joinconstr_opt", + "dbnm", "indexed_opt", "idlist", "sortlist", + "nulls", "delete_stmt", "returning", "update_stmt", + "setlist", "idlist_opt", "insert_stmt", "insert_cmd", + "upsert", "exprx", "not_opt", "likeop", + "case_operand", "case_exprlist", "case_else", "filter_over", + "uniqueflag", "idxlist_single", "collate", "vinto", + "nmnum", "number", "trigger_time", "trigger_event", + "foreach_clause", "when_clause", "trigger_cmd_list", "trigger_cmd", + "database_kw_opt", "key_opt", "kwcolumn_opt", "create_vtab", + "vtabarglist", "vtabarg", "vtabargtoken", "anylist", + "wqlist", "wqas", "wqcte", "windowdefn_list", + "windowdefn", "window", "frame_opt", "range_or_rows", + "frame_bound_s", "frame_exclude_opt", "frame_bound_e", "frame_bound", + "frame_exclude", "filter_clause", "over_clause", }; #endif /* NDEBUG */ @@ -1426,444 +1405,459 @@ static const char *const yyRuleName[] = { /* 31 */ "cmd ::= CREATE temp TABLE ifnotexists nm DOT ID_TAB_NEW", /* 32 */ "cmd ::= CREATE temp TABLE ifnotexists ID_DB|ID_TAB_NEW", /* 33 */ "table_options ::=", - /* 34 */ "table_options ::= WITHOUT nm", - /* 35 */ "table_options ::= WITHOUT CTX_ROWID_KW", - /* 36 */ "ifnotexists ::=", - /* 37 */ "ifnotexists ::= IF NOT EXISTS", - /* 38 */ "temp ::= TEMP", - /* 39 */ "temp ::=", - /* 40 */ "columnlist ::= columnlist COMMA column", - /* 41 */ "columnlist ::= column", - /* 42 */ "column ::= columnid type carglist", - /* 43 */ "columnid ::= nm", - /* 44 */ "columnid ::= ID_COL_NEW", - /* 45 */ "id ::= ID", - /* 46 */ "id_opt ::= id", - /* 47 */ "id_opt ::=", - /* 48 */ "ids ::= ID|STRING", - /* 49 */ "nm ::= id", - /* 50 */ "nm ::= STRING", - /* 51 */ "nm ::= JOIN_KW", - /* 52 */ "type ::=", - /* 53 */ "type ::= typetoken", - /* 54 */ "typetoken ::= typename", - /* 55 */ "typetoken ::= typename LP signed RP", - /* 56 */ "typetoken ::= typename LP signed COMMA signed RP", - /* 57 */ "typename ::= ids", - /* 58 */ "typename ::= typename ids", - /* 59 */ "typename ::= ID_COL_TYPE", - /* 60 */ "signed ::= plus_num", - /* 61 */ "signed ::= minus_num", - /* 62 */ "carglist ::= carglist ccons", - /* 63 */ "carglist ::=", - /* 64 */ "ccons ::= CONSTRAINT nm", - /* 65 */ "ccons ::= DEFAULT term", - /* 66 */ "ccons ::= DEFAULT LP expr RP", - /* 67 */ "ccons ::= DEFAULT PLUS term", - /* 68 */ "ccons ::= DEFAULT MINUS term", - /* 69 */ "ccons ::= DEFAULT id", - /* 70 */ "ccons ::= DEFAULT CTIME_KW", - /* 71 */ "ccons ::= NULL onconf", - /* 72 */ "ccons ::= NOT NULL onconf", - /* 73 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", - /* 74 */ "ccons ::= UNIQUE onconf", - /* 75 */ "ccons ::= CHECK LP expr RP", - /* 76 */ "ccons ::= REFERENCES nm idxlist_opt refargs", - /* 77 */ "ccons ::= defer_subclause", - /* 78 */ "ccons ::= COLLATE ids", - /* 79 */ "ccons ::= gen_always AS LP expr RP id_opt", - /* 80 */ "ccons ::= CONSTRAINT ID_CONSTR", - /* 81 */ "ccons ::= COLLATE ID_COLLATE", - /* 82 */ "ccons ::= REFERENCES ID_TAB", - /* 83 */ "ccons ::= CHECK LP RP", - /* 84 */ "term ::= NULL", - /* 85 */ "term ::= INTEGER", - /* 86 */ "term ::= FLOAT", - /* 87 */ "term ::= STRING|BLOB", - /* 88 */ "tnm ::= term", - /* 89 */ "tnm ::= nm", - /* 90 */ "gen_always ::= GENERATED ALWAYS", - /* 91 */ "gen_always ::=", - /* 92 */ "autoinc ::=", - /* 93 */ "autoinc ::= AUTOINCR", - /* 94 */ "refargs ::=", - /* 95 */ "refargs ::= refargs refarg", - /* 96 */ "refarg ::= MATCH nm", - /* 97 */ "refarg ::= ON INSERT refact", - /* 98 */ "refarg ::= ON DELETE refact", - /* 99 */ "refarg ::= ON UPDATE refact", - /* 100 */ "refarg ::= MATCH ID_FK_MATCH", - /* 101 */ "refact ::= SET NULL", - /* 102 */ "refact ::= SET DEFAULT", - /* 103 */ "refact ::= CASCADE", - /* 104 */ "refact ::= RESTRICT", - /* 105 */ "refact ::= NO ACTION", - /* 106 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", - /* 107 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", - /* 108 */ "init_deferred_pred_opt ::=", - /* 109 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", - /* 110 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", - /* 111 */ "conslist_opt ::=", - /* 112 */ "conslist_opt ::= COMMA conslist", - /* 113 */ "conslist ::= conslist tconscomma tcons", - /* 114 */ "conslist ::= tcons", - /* 115 */ "tconscomma ::= COMMA", - /* 116 */ "tconscomma ::=", - /* 117 */ "tcons ::= CONSTRAINT nm", - /* 118 */ "tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf", - /* 119 */ "tcons ::= UNIQUE LP idxlist RP onconf", - /* 120 */ "tcons ::= CHECK LP expr RP onconf", - /* 121 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt", - /* 122 */ "tcons ::= CONSTRAINT ID_CONSTR", - /* 123 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES ID_TAB", - /* 124 */ "tcons ::= CHECK LP RP onconf", - /* 125 */ "defer_subclause_opt ::=", - /* 126 */ "defer_subclause_opt ::= defer_subclause", - /* 127 */ "onconf ::=", - /* 128 */ "onconf ::= ON CONFLICT resolvetype", - /* 129 */ "orconf ::=", - /* 130 */ "orconf ::= OR resolvetype", - /* 131 */ "resolvetype ::= raisetype", - /* 132 */ "resolvetype ::= IGNORE", - /* 133 */ "resolvetype ::= REPLACE", - /* 134 */ "cmd ::= DROP TABLE ifexists fullname", - /* 135 */ "cmd ::= DROP TABLE ifexists nm DOT ID_TAB", - /* 136 */ "cmd ::= DROP TABLE ifexists ID_DB|ID_TAB", - /* 137 */ "ifexists ::= IF EXISTS", - /* 138 */ "ifexists ::=", - /* 139 */ "cmd ::= CREATE temp VIEW ifnotexists fullname idxlist_opt AS select", - /* 140 */ "cmd ::= CREATE temp VIEW ifnotexists nm DOT ID_VIEW_NEW", - /* 141 */ "cmd ::= CREATE temp VIEW ifnotexists ID_DB|ID_VIEW_NEW", - /* 142 */ "cmd ::= DROP VIEW ifexists fullname", - /* 143 */ "cmd ::= DROP VIEW ifexists nm DOT ID_VIEW", - /* 144 */ "cmd ::= DROP VIEW ifexists ID_DB|ID_VIEW", - /* 145 */ "cmd ::= select_stmt", - /* 146 */ "select_stmt ::= select", - /* 147 */ "select ::= with selectnowith", - /* 148 */ "selectnowith ::= oneselect", - /* 149 */ "selectnowith ::= selectnowith multiselect_op oneselect", - /* 150 */ "selectnowith ::= values", - /* 151 */ "selectnowith ::= selectnowith COMMA values", - /* 152 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", - /* 153 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", - /* 154 */ "values ::= VALUES LP nexprlist RP", - /* 155 */ "values ::= values COMMA LP exprlist RP", - /* 156 */ "multiselect_op ::= UNION", - /* 157 */ "multiselect_op ::= UNION ALL", - /* 158 */ "multiselect_op ::= EXCEPT", - /* 159 */ "multiselect_op ::= INTERSECT", - /* 160 */ "distinct ::= DISTINCT", - /* 161 */ "distinct ::= ALL", - /* 162 */ "distinct ::=", - /* 163 */ "sclp ::= selcollist COMMA", - /* 164 */ "sclp ::=", - /* 165 */ "selcollist ::= sclp expr as", - /* 166 */ "selcollist ::= sclp STAR", - /* 167 */ "selcollist ::= sclp tnm DOT STAR", - /* 168 */ "selcollist ::= sclp", - /* 169 */ "selcollist ::= sclp ID_TAB DOT STAR", - /* 170 */ "as ::= AS nm", - /* 171 */ "as ::= ids", - /* 172 */ "as ::= AS ID_ALIAS", - /* 173 */ "as ::= ID_ALIAS", - /* 174 */ "as ::=", - /* 175 */ "from ::=", - /* 176 */ "from ::= FROM joinsrc", - /* 177 */ "joinsrc ::= singlesrc seltablist", - /* 178 */ "joinsrc ::=", - /* 179 */ "seltablist ::= seltablist joinop singlesrc joinconstr_opt", - /* 180 */ "seltablist ::=", - /* 181 */ "singlesrc ::= nm dbnm as indexed_opt", - /* 182 */ "singlesrc ::= LP select RP as", - /* 183 */ "singlesrc ::= LP joinsrc RP as", - /* 184 */ "singlesrc ::= nm dbnm LP exprlist RP as", - /* 185 */ "singlesrc ::=", - /* 186 */ "singlesrc ::= nm DOT", - /* 187 */ "singlesrc ::= nm DOT ID_TAB", - /* 188 */ "singlesrc ::= ID_DB|ID_TAB", - /* 189 */ "singlesrc ::= nm DOT ID_VIEW", - /* 190 */ "singlesrc ::= ID_DB|ID_VIEW", - /* 191 */ "joinconstr_opt ::= ON expr", - /* 192 */ "joinconstr_opt ::= USING LP idlist RP", - /* 193 */ "joinconstr_opt ::=", - /* 194 */ "dbnm ::=", - /* 195 */ "dbnm ::= DOT nm", - /* 196 */ "fullname ::= nm dbnm", - /* 197 */ "joinop ::= COMMA", - /* 198 */ "joinop ::= JOIN", - /* 199 */ "joinop ::= JOIN_KW JOIN", - /* 200 */ "joinop ::= JOIN_KW nm JOIN", - /* 201 */ "joinop ::= JOIN_KW nm nm JOIN", - /* 202 */ "joinop ::= ID_JOIN_OPTS", - /* 203 */ "indexed_opt ::=", - /* 204 */ "indexed_opt ::= INDEXED BY nm", - /* 205 */ "indexed_opt ::= NOT INDEXED", - /* 206 */ "indexed_opt ::= INDEXED BY ID_IDX", - /* 207 */ "orderby_opt ::=", - /* 208 */ "orderby_opt ::= ORDER BY sortlist", - /* 209 */ "sortlist ::= sortlist COMMA expr sortorder nulls", - /* 210 */ "sortlist ::= expr sortorder nulls", - /* 211 */ "sortorder ::= ASC", - /* 212 */ "sortorder ::= DESC", - /* 213 */ "sortorder ::=", - /* 214 */ "nulls ::= NULLS FIRST", - /* 215 */ "nulls ::= NULLS LAST", - /* 216 */ "nulls ::=", - /* 217 */ "groupby_opt ::=", - /* 218 */ "groupby_opt ::= GROUP BY nexprlist", - /* 219 */ "groupby_opt ::= GROUP BY", - /* 220 */ "having_opt ::=", - /* 221 */ "having_opt ::= HAVING expr", - /* 222 */ "limit_opt ::=", - /* 223 */ "limit_opt ::= LIMIT expr", - /* 224 */ "limit_opt ::= LIMIT expr OFFSET expr", - /* 225 */ "limit_opt ::= LIMIT expr COMMA expr", - /* 226 */ "cmd ::= delete_stmt", - /* 227 */ "delete_stmt ::= with DELETE FROM fullname indexed_opt where_opt", - /* 228 */ "delete_stmt ::= with DELETE FROM", - /* 229 */ "delete_stmt ::= with DELETE FROM nm DOT", - /* 230 */ "delete_stmt ::= with DELETE FROM nm DOT ID_TAB", - /* 231 */ "delete_stmt ::= with DELETE FROM ID_DB|ID_TAB", - /* 232 */ "where_opt ::=", - /* 233 */ "where_opt ::= WHERE expr", - /* 234 */ "where_opt ::= WHERE", - /* 235 */ "cmd ::= update_stmt", - /* 236 */ "update_stmt ::= with UPDATE orconf fullname indexed_opt SET setlist from where_opt", - /* 237 */ "update_stmt ::= with UPDATE orconf", - /* 238 */ "update_stmt ::= with UPDATE orconf nm DOT", - /* 239 */ "update_stmt ::= with UPDATE orconf nm DOT ID_TAB", - /* 240 */ "update_stmt ::= with UPDATE orconf ID_DB|ID_TAB", - /* 241 */ "setlist ::= setlist COMMA nm EQ expr", - /* 242 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", - /* 243 */ "setlist ::= nm EQ expr", - /* 244 */ "setlist ::= LP idlist RP EQ expr", - /* 245 */ "setlist ::=", - /* 246 */ "setlist ::= setlist COMMA", - /* 247 */ "setlist ::= setlist COMMA ID_COL", - /* 248 */ "setlist ::= ID_COL", - /* 249 */ "idlist_opt ::=", - /* 250 */ "idlist_opt ::= LP idlist RP", - /* 251 */ "idlist ::= idlist COMMA nm", - /* 252 */ "idlist ::= nm", - /* 253 */ "idlist ::=", - /* 254 */ "idlist ::= idlist COMMA ID_COL", - /* 255 */ "idlist ::= ID_COL", - /* 256 */ "cmd ::= insert_stmt", - /* 257 */ "insert_stmt ::= with insert_cmd INTO fullname idlist_opt select upsert", - /* 258 */ "insert_stmt ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES", - /* 259 */ "insert_stmt ::= with insert_cmd INTO", - /* 260 */ "insert_stmt ::= with insert_cmd INTO nm DOT", - /* 261 */ "insert_stmt ::= with insert_cmd INTO ID_DB|ID_TAB", - /* 262 */ "insert_stmt ::= with insert_cmd INTO nm DOT ID_TAB", - /* 263 */ "insert_cmd ::= INSERT orconf", - /* 264 */ "insert_cmd ::= REPLACE", - /* 265 */ "upsert ::=", - /* 266 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt", - /* 267 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING", - /* 268 */ "upsert ::= ON CONFLICT DO NOTHING", - /* 269 */ "exprx ::= expr not_opt IN ID_DB", - /* 270 */ "exprx ::= expr not_opt IN nm DOT ID_TAB", - /* 271 */ "exprx ::= ID_DB|ID_TAB|ID_COL|ID_FN", - /* 272 */ "exprx ::= tnm DOT ID_TAB|ID_COL", - /* 273 */ "exprx ::= tnm DOT nm DOT ID_COL", - /* 274 */ "exprx ::= expr COLLATE ID_COLLATE", - /* 275 */ "exprx ::= RAISE LP raisetype COMMA ID_ERR_MSG RP", - /* 276 */ "exprx ::= CTIME_KW", - /* 277 */ "exprx ::= LP nexprlist RP", - /* 278 */ "exprx ::= tnm", - /* 279 */ "exprx ::= tnm DOT nm", - /* 280 */ "exprx ::= tnm DOT nm DOT nm", - /* 281 */ "exprx ::= VARIABLE", - /* 282 */ "exprx ::= expr COLLATE ids", - /* 283 */ "exprx ::= CAST LP expr AS typetoken RP", - /* 284 */ "exprx ::= ID LP distinct exprlist RP", - /* 285 */ "exprx ::= ID LP STAR RP", - /* 286 */ "exprx ::= expr AND expr", - /* 287 */ "exprx ::= expr OR expr", - /* 288 */ "exprx ::= expr LT|GT|GE|LE expr", - /* 289 */ "exprx ::= expr EQ|NE expr", - /* 290 */ "exprx ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", - /* 291 */ "exprx ::= expr PLUS|MINUS expr", - /* 292 */ "exprx ::= expr STAR|SLASH|REM expr", - /* 293 */ "exprx ::= expr CONCAT expr", - /* 294 */ "exprx ::= expr not_opt likeop expr", - /* 295 */ "exprx ::= expr not_opt likeop expr ESCAPE expr", - /* 296 */ "exprx ::= expr ISNULL|NOTNULL", - /* 297 */ "exprx ::= expr NOT NULL", - /* 298 */ "exprx ::= expr IS not_opt expr", - /* 299 */ "exprx ::= NOT expr", - /* 300 */ "exprx ::= BITNOT expr", - /* 301 */ "exprx ::= MINUS expr", - /* 302 */ "exprx ::= PLUS expr", - /* 303 */ "exprx ::= expr not_opt BETWEEN expr AND expr", - /* 304 */ "exprx ::= expr not_opt IN LP exprlist RP", - /* 305 */ "exprx ::= LP select RP", - /* 306 */ "exprx ::= expr not_opt IN LP select RP", - /* 307 */ "exprx ::= expr not_opt IN nm dbnm", - /* 308 */ "exprx ::= EXISTS LP select RP", - /* 309 */ "exprx ::= CASE case_operand case_exprlist case_else END", - /* 310 */ "exprx ::= RAISE LP IGNORE RP", - /* 311 */ "exprx ::= RAISE LP raisetype COMMA nm RP", - /* 312 */ "exprx ::= ID LP distinct exprlist RP filter_over", - /* 313 */ "exprx ::= ID LP STAR RP filter_over", - /* 314 */ "expr ::=", - /* 315 */ "expr ::= exprx", - /* 316 */ "not_opt ::=", - /* 317 */ "not_opt ::= NOT", - /* 318 */ "likeop ::= LIKE_KW|MATCH", - /* 319 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", - /* 320 */ "case_exprlist ::= WHEN expr THEN expr", - /* 321 */ "case_else ::= ELSE expr", - /* 322 */ "case_else ::=", - /* 323 */ "case_operand ::= exprx", - /* 324 */ "case_operand ::=", - /* 325 */ "exprlist ::= nexprlist", - /* 326 */ "exprlist ::=", - /* 327 */ "nexprlist ::= nexprlist COMMA expr", - /* 328 */ "nexprlist ::= exprx", - /* 329 */ "cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", - /* 330 */ "cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON ID_TAB", - /* 331 */ "cmd ::= CREATE uniqueflag INDEX ifnotexists nm DOT ID_IDX_NEW", - /* 332 */ "cmd ::= CREATE uniqueflag INDEX ifnotexists ID_DB|ID_IDX_NEW", - /* 333 */ "uniqueflag ::= UNIQUE", - /* 334 */ "uniqueflag ::=", - /* 335 */ "idxlist_opt ::=", - /* 336 */ "idxlist_opt ::= LP idxlist RP", - /* 337 */ "idxlist ::= idxlist COMMA idxlist_single", - /* 338 */ "idxlist ::= idxlist_single", - /* 339 */ "idxlist_single ::= nm collate sortorder", - /* 340 */ "idxlist_single ::= ID_COL", - /* 341 */ "collate ::=", - /* 342 */ "collate ::= COLLATE ids", - /* 343 */ "collate ::= COLLATE ID_COLLATE", - /* 344 */ "cmd ::= DROP INDEX ifexists fullname", - /* 345 */ "cmd ::= DROP INDEX ifexists nm DOT ID_IDX", - /* 346 */ "cmd ::= DROP INDEX ifexists ID_DB|ID_IDX", - /* 347 */ "cmd ::= VACUUM vinto", - /* 348 */ "cmd ::= VACUUM nm vinto", - /* 349 */ "vinto ::= INTO expr", - /* 350 */ "vinto ::=", - /* 351 */ "cmd ::= PRAGMA nm dbnm", - /* 352 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", - /* 353 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", - /* 354 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 355 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", - /* 356 */ "cmd ::= PRAGMA nm DOT ID_PRAGMA", - /* 357 */ "cmd ::= PRAGMA ID_DB|ID_PRAGMA", - /* 358 */ "nmnum ::= plus_num", - /* 359 */ "nmnum ::= nm", - /* 360 */ "nmnum ::= ON", - /* 361 */ "nmnum ::= DELETE", - /* 362 */ "nmnum ::= DEFAULT", - /* 363 */ "plus_num ::= PLUS number", - /* 364 */ "plus_num ::= number", - /* 365 */ "minus_num ::= MINUS number", - /* 366 */ "number ::= INTEGER", - /* 367 */ "number ::= FLOAT", - /* 368 */ "cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause BEGIN trigger_cmd_list END", - /* 369 */ "cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause", - /* 370 */ "cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause BEGIN trigger_cmd_list", - /* 371 */ "cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON ID_TAB", - /* 372 */ "cmd ::= CREATE temp TRIGGER ifnotexists nm DOT ID_TRIG_NEW", - /* 373 */ "cmd ::= CREATE temp TRIGGER ifnotexists ID_DB|ID_TRIG_NEW", - /* 374 */ "trigger_time ::= BEFORE", - /* 375 */ "trigger_time ::= AFTER", - /* 376 */ "trigger_time ::= INSTEAD OF", - /* 377 */ "trigger_time ::=", - /* 378 */ "trigger_event ::= DELETE", - /* 379 */ "trigger_event ::= INSERT", - /* 380 */ "trigger_event ::= UPDATE", - /* 381 */ "trigger_event ::= UPDATE OF idlist", - /* 382 */ "foreach_clause ::=", - /* 383 */ "foreach_clause ::= FOR EACH ROW", - /* 384 */ "when_clause ::=", - /* 385 */ "when_clause ::= WHEN expr", - /* 386 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", - /* 387 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 388 */ "trigger_cmd_list ::= SEMI", - /* 389 */ "trigger_cmd ::= update_stmt", - /* 390 */ "trigger_cmd ::= insert_stmt", - /* 391 */ "trigger_cmd ::= delete_stmt", - /* 392 */ "trigger_cmd ::= select_stmt", - /* 393 */ "raisetype ::= ROLLBACK|ABORT|FAIL", - /* 394 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 395 */ "cmd ::= DROP TRIGGER ifexists nm DOT ID_TRIG", - /* 396 */ "cmd ::= DROP TRIGGER ifexists ID_DB|ID_TRIG", - /* 397 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 398 */ "cmd ::= DETACH database_kw_opt expr", - /* 399 */ "key_opt ::=", - /* 400 */ "key_opt ::= KEY expr", - /* 401 */ "database_kw_opt ::= DATABASE", - /* 402 */ "database_kw_opt ::=", - /* 403 */ "cmd ::= REINDEX", - /* 404 */ "cmd ::= REINDEX nm dbnm", - /* 405 */ "cmd ::= REINDEX ID_COLLATE", - /* 406 */ "cmd ::= REINDEX nm DOT ID_TAB|ID_IDX", - /* 407 */ "cmd ::= REINDEX ID_DB|ID_IDX|ID_TAB", - /* 408 */ "cmd ::= ANALYZE", - /* 409 */ "cmd ::= ANALYZE nm dbnm", - /* 410 */ "cmd ::= ANALYZE nm DOT ID_TAB|ID_IDX", - /* 411 */ "cmd ::= ANALYZE ID_DB|ID_IDX|ID_TAB", - /* 412 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 413 */ "cmd ::= ALTER TABLE fullname ADD kwcolumn_opt column", - /* 414 */ "cmd ::= ALTER TABLE fullname RENAME TO ID_TAB_NEW", - /* 415 */ "cmd ::= ALTER TABLE nm DOT ID_TAB", - /* 416 */ "cmd ::= ALTER TABLE ID_DB|ID_TAB", - /* 417 */ "kwcolumn_opt ::=", - /* 418 */ "kwcolumn_opt ::= COLUMNKW", - /* 419 */ "cmd ::= create_vtab", - /* 420 */ "create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm dbnm USING nm", - /* 421 */ "create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm dbnm USING nm LP vtabarglist RP", - /* 422 */ "create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm DOT ID_TAB_NEW", - /* 423 */ "create_vtab ::= CREATE VIRTUAL TABLE ifnotexists ID_DB|ID_TAB_NEW", - /* 424 */ "vtabarglist ::= vtabarg", - /* 425 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 426 */ "vtabarg ::=", - /* 427 */ "vtabarg ::= vtabarg vtabargtoken", - /* 428 */ "vtabargtoken ::= ANY", - /* 429 */ "vtabargtoken ::= LP anylist RP", - /* 430 */ "anylist ::=", - /* 431 */ "anylist ::= anylist LP anylist RP", - /* 432 */ "anylist ::= anylist ANY", - /* 433 */ "with ::=", - /* 434 */ "with ::= WITH wqlist", - /* 435 */ "with ::= WITH RECURSIVE wqlist", - /* 436 */ "wqlist ::= wqcte", - /* 437 */ "wqlist ::= wqlist COMMA wqcte", - /* 438 */ "wqlist ::= ID_TAB_NEW", - /* 439 */ "wqcte ::= nm idxlist_opt AS LP select RP", - /* 440 */ "windowdefn_list ::= windowdefn", - /* 441 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", - /* 442 */ "windowdefn ::= nm AS LP window RP", - /* 443 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", - /* 444 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", - /* 445 */ "window ::= ORDER BY sortlist frame_opt", - /* 446 */ "window ::= nm ORDER BY sortlist frame_opt", - /* 447 */ "window ::= frame_opt", - /* 448 */ "window ::= nm frame_opt", - /* 449 */ "frame_opt ::=", - /* 450 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", - /* 451 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", - /* 452 */ "range_or_rows ::= RANGE|ROWS|GROUPS", - /* 453 */ "frame_bound_s ::= frame_bound", - /* 454 */ "frame_bound_s ::= UNBOUNDED PRECEDING", - /* 455 */ "frame_bound_e ::= frame_bound", - /* 456 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", - /* 457 */ "frame_bound ::= expr PRECEDING|FOLLOWING", - /* 458 */ "frame_bound ::= CURRENT ROW", - /* 459 */ "frame_exclude_opt ::=", - /* 460 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", - /* 461 */ "frame_exclude ::= NO OTHERS", - /* 462 */ "frame_exclude ::= CURRENT ROW", - /* 463 */ "frame_exclude ::= GROUP", - /* 464 */ "frame_exclude ::= TIES", - /* 465 */ "window_clause ::= WINDOW windowdefn_list", - /* 466 */ "filter_over ::= filter_clause over_clause", - /* 467 */ "filter_over ::= over_clause", - /* 468 */ "filter_over ::= filter_clause", - /* 469 */ "over_clause ::= OVER LP window RP", - /* 470 */ "over_clause ::= OVER nm", - /* 471 */ "filter_clause ::= FILTER LP WHERE expr RP", + /* 34 */ "table_options ::= table_option", + /* 35 */ "table_options ::= table_options COMMA table_option", + /* 36 */ "table_option ::= WITHOUT nm", + /* 37 */ "table_option ::= nm", + /* 38 */ "table_option ::= WITHOUT CTX_ROWID_KW", + /* 39 */ "table_option ::= CTX_STRICT_KW", + /* 40 */ "ifnotexists ::=", + /* 41 */ "ifnotexists ::= IF NOT EXISTS", + /* 42 */ "temp ::= TEMP", + /* 43 */ "temp ::=", + /* 44 */ "columnlist ::= columnlist COMMA column", + /* 45 */ "columnlist ::= column", + /* 46 */ "column ::= columnid type carglist", + /* 47 */ "columnid ::= nm", + /* 48 */ "columnid ::= ID_COL_NEW", + /* 49 */ "id ::= ID", + /* 50 */ "id_opt ::= id", + /* 51 */ "id_opt ::=", + /* 52 */ "ids ::= ID|STRING", + /* 53 */ "nm ::= id", + /* 54 */ "nm ::= STRING", + /* 55 */ "nm ::= JOIN_KW", + /* 56 */ "type ::=", + /* 57 */ "type ::= typetoken", + /* 58 */ "typetoken ::= typename", + /* 59 */ "typetoken ::= typename LP signed RP", + /* 60 */ "typetoken ::= typename LP signed COMMA signed RP", + /* 61 */ "typename ::= ids", + /* 62 */ "typename ::= typename ids", + /* 63 */ "typename ::= ID_COL_TYPE", + /* 64 */ "signed ::= plus_num", + /* 65 */ "signed ::= minus_num", + /* 66 */ "carglist ::= carglist ccons", + /* 67 */ "carglist ::=", + /* 68 */ "ccons ::= CONSTRAINT nm", + /* 69 */ "ccons ::= DEFAULT term", + /* 70 */ "ccons ::= DEFAULT LP expr RP", + /* 71 */ "ccons ::= DEFAULT PLUS term", + /* 72 */ "ccons ::= DEFAULT MINUS term", + /* 73 */ "ccons ::= DEFAULT id", + /* 74 */ "ccons ::= DEFAULT CTIME_KW", + /* 75 */ "ccons ::= NULL onconf", + /* 76 */ "ccons ::= NOT NULL onconf", + /* 77 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", + /* 78 */ "ccons ::= UNIQUE onconf", + /* 79 */ "ccons ::= CHECK LP expr RP", + /* 80 */ "ccons ::= REFERENCES nm idxlist_opt refargs", + /* 81 */ "ccons ::= defer_subclause", + /* 82 */ "ccons ::= COLLATE ids", + /* 83 */ "ccons ::= gen_always AS LP expr RP id_opt", + /* 84 */ "ccons ::= CONSTRAINT ID_CONSTR", + /* 85 */ "ccons ::= COLLATE ID_COLLATE", + /* 86 */ "ccons ::= REFERENCES ID_TAB", + /* 87 */ "ccons ::= CHECK LP RP", + /* 88 */ "term ::= NULL", + /* 89 */ "term ::= INTEGER", + /* 90 */ "term ::= FLOAT", + /* 91 */ "term ::= STRING|BLOB", + /* 92 */ "tnm ::= term", + /* 93 */ "tnm ::= nm", + /* 94 */ "gen_always ::= GENERATED ALWAYS", + /* 95 */ "gen_always ::=", + /* 96 */ "autoinc ::=", + /* 97 */ "autoinc ::= AUTOINCR", + /* 98 */ "refargs ::=", + /* 99 */ "refargs ::= refargs refarg", + /* 100 */ "refarg ::= MATCH nm", + /* 101 */ "refarg ::= ON INSERT refact", + /* 102 */ "refarg ::= ON DELETE refact", + /* 103 */ "refarg ::= ON UPDATE refact", + /* 104 */ "refarg ::= MATCH ID_FK_MATCH", + /* 105 */ "refact ::= SET NULL", + /* 106 */ "refact ::= SET DEFAULT", + /* 107 */ "refact ::= CASCADE", + /* 108 */ "refact ::= RESTRICT", + /* 109 */ "refact ::= NO ACTION", + /* 110 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", + /* 111 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", + /* 112 */ "init_deferred_pred_opt ::=", + /* 113 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", + /* 114 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", + /* 115 */ "conslist_opt ::=", + /* 116 */ "conslist_opt ::= COMMA conslist", + /* 117 */ "conslist ::= conslist tconscomma tcons", + /* 118 */ "conslist ::= tcons", + /* 119 */ "tconscomma ::= COMMA", + /* 120 */ "tconscomma ::=", + /* 121 */ "tcons ::= CONSTRAINT nm", + /* 122 */ "tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf", + /* 123 */ "tcons ::= UNIQUE LP idxlist RP onconf", + /* 124 */ "tcons ::= CHECK LP expr RP onconf", + /* 125 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt", + /* 126 */ "tcons ::= CONSTRAINT ID_CONSTR", + /* 127 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES ID_TAB", + /* 128 */ "tcons ::= CHECK LP RP onconf", + /* 129 */ "defer_subclause_opt ::=", + /* 130 */ "defer_subclause_opt ::= defer_subclause", + /* 131 */ "onconf ::=", + /* 132 */ "onconf ::= ON CONFLICT resolvetype", + /* 133 */ "orconf ::=", + /* 134 */ "orconf ::= OR resolvetype", + /* 135 */ "resolvetype ::= raisetype", + /* 136 */ "resolvetype ::= IGNORE", + /* 137 */ "resolvetype ::= REPLACE", + /* 138 */ "cmd ::= DROP TABLE ifexists fullname", + /* 139 */ "cmd ::= DROP TABLE ifexists nm DOT ID_TAB", + /* 140 */ "cmd ::= DROP TABLE ifexists ID_DB|ID_TAB", + /* 141 */ "ifexists ::= IF EXISTS", + /* 142 */ "ifexists ::=", + /* 143 */ "cmd ::= CREATE temp VIEW ifnotexists fullname idxlist_opt AS select", + /* 144 */ "cmd ::= CREATE temp VIEW ifnotexists nm DOT ID_VIEW_NEW", + /* 145 */ "cmd ::= CREATE temp VIEW ifnotexists ID_DB|ID_VIEW_NEW", + /* 146 */ "cmd ::= DROP VIEW ifexists fullname", + /* 147 */ "cmd ::= DROP VIEW ifexists nm DOT ID_VIEW", + /* 148 */ "cmd ::= DROP VIEW ifexists ID_DB|ID_VIEW", + /* 149 */ "cmd ::= select_stmt", + /* 150 */ "select_stmt ::= select", + /* 151 */ "select ::= with selectnowith", + /* 152 */ "selectnowith ::= oneselect", + /* 153 */ "selectnowith ::= selectnowith multiselect_op oneselect", + /* 154 */ "selectnowith ::= values", + /* 155 */ "selectnowith ::= selectnowith COMMA values", + /* 156 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", + /* 157 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", + /* 158 */ "values ::= VALUES LP nexprlist RP", + /* 159 */ "values ::= values COMMA LP exprlist RP", + /* 160 */ "multiselect_op ::= UNION", + /* 161 */ "multiselect_op ::= UNION ALL", + /* 162 */ "multiselect_op ::= EXCEPT", + /* 163 */ "multiselect_op ::= INTERSECT", + /* 164 */ "distinct ::= DISTINCT", + /* 165 */ "distinct ::= ALL", + /* 166 */ "distinct ::=", + /* 167 */ "sclp ::= selcollist COMMA", + /* 168 */ "sclp ::=", + /* 169 */ "selcollist ::= sclp expr as", + /* 170 */ "selcollist ::= sclp STAR", + /* 171 */ "selcollist ::= sclp tnm DOT STAR", + /* 172 */ "selcollist ::= sclp", + /* 173 */ "selcollist ::= sclp ID_TAB DOT STAR", + /* 174 */ "as ::= AS nm", + /* 175 */ "as ::= ids", + /* 176 */ "as ::= AS ID_ALIAS", + /* 177 */ "as ::= ID_ALIAS", + /* 178 */ "as ::=", + /* 179 */ "from ::=", + /* 180 */ "from ::= FROM joinsrc", + /* 181 */ "joinsrc ::= singlesrc seltablist", + /* 182 */ "joinsrc ::=", + /* 183 */ "seltablist ::= seltablist joinop singlesrc joinconstr_opt", + /* 184 */ "seltablist ::=", + /* 185 */ "singlesrc ::= nm dbnm as indexed_opt", + /* 186 */ "singlesrc ::= LP select RP as", + /* 187 */ "singlesrc ::= LP joinsrc RP as", + /* 188 */ "singlesrc ::= nm dbnm LP exprlist RP as", + /* 189 */ "singlesrc ::=", + /* 190 */ "singlesrc ::= nm DOT", + /* 191 */ "singlesrc ::= nm DOT ID_TAB", + /* 192 */ "singlesrc ::= ID_DB|ID_TAB", + /* 193 */ "singlesrc ::= nm DOT ID_VIEW", + /* 194 */ "singlesrc ::= ID_DB|ID_VIEW", + /* 195 */ "joinconstr_opt ::= ON expr", + /* 196 */ "joinconstr_opt ::= USING LP idlist RP", + /* 197 */ "joinconstr_opt ::=", + /* 198 */ "dbnm ::=", + /* 199 */ "dbnm ::= DOT nm", + /* 200 */ "fullname ::= nm dbnm", + /* 201 */ "joinop ::= COMMA", + /* 202 */ "joinop ::= JOIN", + /* 203 */ "joinop ::= JOIN_KW JOIN", + /* 204 */ "joinop ::= JOIN_KW nm JOIN", + /* 205 */ "joinop ::= JOIN_KW nm nm JOIN", + /* 206 */ "joinop ::= ID_JOIN_OPTS", + /* 207 */ "indexed_opt ::=", + /* 208 */ "indexed_opt ::= INDEXED BY nm", + /* 209 */ "indexed_opt ::= NOT INDEXED", + /* 210 */ "indexed_opt ::= INDEXED BY ID_IDX", + /* 211 */ "orderby_opt ::=", + /* 212 */ "orderby_opt ::= ORDER BY sortlist", + /* 213 */ "sortlist ::= sortlist COMMA expr sortorder nulls", + /* 214 */ "sortlist ::= expr sortorder nulls", + /* 215 */ "sortorder ::= ASC", + /* 216 */ "sortorder ::= DESC", + /* 217 */ "sortorder ::=", + /* 218 */ "nulls ::= NULLS FIRST", + /* 219 */ "nulls ::= NULLS LAST", + /* 220 */ "nulls ::=", + /* 221 */ "groupby_opt ::=", + /* 222 */ "groupby_opt ::= GROUP BY nexprlist", + /* 223 */ "groupby_opt ::= GROUP BY", + /* 224 */ "having_opt ::=", + /* 225 */ "having_opt ::= HAVING expr", + /* 226 */ "limit_opt ::=", + /* 227 */ "limit_opt ::= LIMIT expr", + /* 228 */ "limit_opt ::= LIMIT expr OFFSET expr", + /* 229 */ "limit_opt ::= LIMIT expr COMMA expr", + /* 230 */ "cmd ::= delete_stmt", + /* 231 */ "delete_stmt ::= with DELETE FROM fullname indexed_opt where_opt returning", + /* 232 */ "delete_stmt ::= with DELETE FROM", + /* 233 */ "delete_stmt ::= with DELETE FROM nm DOT", + /* 234 */ "delete_stmt ::= with DELETE FROM nm DOT ID_TAB", + /* 235 */ "delete_stmt ::= with DELETE FROM ID_DB|ID_TAB", + /* 236 */ "where_opt ::=", + /* 237 */ "where_opt ::= WHERE expr", + /* 238 */ "where_opt ::= WHERE", + /* 239 */ "returning ::=", + /* 240 */ "returning ::= RETURNING selcollist", + /* 241 */ "cmd ::= update_stmt", + /* 242 */ "update_stmt ::= with UPDATE orconf fullname indexed_opt SET setlist from where_opt returning", + /* 243 */ "update_stmt ::= with UPDATE orconf", + /* 244 */ "update_stmt ::= with UPDATE orconf nm DOT", + /* 245 */ "update_stmt ::= with UPDATE orconf nm DOT ID_TAB", + /* 246 */ "update_stmt ::= with UPDATE orconf ID_DB|ID_TAB", + /* 247 */ "setlist ::= setlist COMMA nm EQ expr", + /* 248 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", + /* 249 */ "setlist ::= nm EQ expr", + /* 250 */ "setlist ::= LP idlist RP EQ expr", + /* 251 */ "setlist ::=", + /* 252 */ "setlist ::= setlist COMMA", + /* 253 */ "setlist ::= setlist COMMA ID_COL", + /* 254 */ "setlist ::= ID_COL", + /* 255 */ "idlist_opt ::=", + /* 256 */ "idlist_opt ::= LP idlist RP", + /* 257 */ "idlist ::= idlist COMMA nm", + /* 258 */ "idlist ::= nm", + /* 259 */ "idlist ::=", + /* 260 */ "idlist ::= idlist COMMA ID_COL", + /* 261 */ "idlist ::= ID_COL", + /* 262 */ "cmd ::= insert_stmt", + /* 263 */ "insert_stmt ::= with insert_cmd INTO fullname idlist_opt select upsert returning", + /* 264 */ "insert_stmt ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES returning", + /* 265 */ "insert_stmt ::= with insert_cmd INTO", + /* 266 */ "insert_stmt ::= with insert_cmd INTO nm DOT", + /* 267 */ "insert_stmt ::= with insert_cmd INTO ID_DB|ID_TAB", + /* 268 */ "insert_stmt ::= with insert_cmd INTO nm DOT ID_TAB", + /* 269 */ "insert_cmd ::= INSERT orconf", + /* 270 */ "insert_cmd ::= REPLACE", + /* 271 */ "upsert ::=", + /* 272 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt", + /* 273 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING", + /* 274 */ "upsert ::= ON CONFLICT DO NOTHING", + /* 275 */ "exprx ::= expr not_opt IN ID_DB", + /* 276 */ "exprx ::= expr not_opt IN nm DOT ID_TAB", + /* 277 */ "exprx ::= ID_DB|ID_TAB|ID_COL|ID_FN", + /* 278 */ "exprx ::= tnm DOT ID_TAB|ID_COL", + /* 279 */ "exprx ::= tnm DOT nm DOT ID_COL", + /* 280 */ "exprx ::= expr COLLATE ID_COLLATE", + /* 281 */ "exprx ::= RAISE LP raisetype COMMA ID_ERR_MSG RP", + /* 282 */ "exprx ::= CTIME_KW", + /* 283 */ "exprx ::= LP nexprlist RP", + /* 284 */ "exprx ::= tnm", + /* 285 */ "exprx ::= tnm DOT nm", + /* 286 */ "exprx ::= tnm DOT", + /* 287 */ "exprx ::= tnm DOT nm DOT nm", + /* 288 */ "exprx ::= tnm DOT nm DOT", + /* 289 */ "exprx ::= VARIABLE", + /* 290 */ "exprx ::= expr COLLATE ids", + /* 291 */ "exprx ::= CAST LP expr AS typetoken RP", + /* 292 */ "exprx ::= ID LP distinct exprlist RP", + /* 293 */ "exprx ::= ID LP STAR RP", + /* 294 */ "exprx ::= expr AND expr", + /* 295 */ "exprx ::= expr OR expr", + /* 296 */ "exprx ::= expr LT|GT|GE|LE expr", + /* 297 */ "exprx ::= expr EQ|NE expr", + /* 298 */ "exprx ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", + /* 299 */ "exprx ::= expr PLUS|MINUS expr", + /* 300 */ "exprx ::= expr STAR|SLASH|REM expr", + /* 301 */ "exprx ::= expr CONCAT expr", + /* 302 */ "exprx ::= expr not_opt likeop expr", + /* 303 */ "exprx ::= expr not_opt likeop expr ESCAPE expr", + /* 304 */ "exprx ::= expr ISNULL|NOTNULL", + /* 305 */ "exprx ::= expr NOT NULL", + /* 306 */ "exprx ::= expr IS not_opt expr", + /* 307 */ "exprx ::= expr IS NOT DISTINCT FROM expr", + /* 308 */ "exprx ::= expr IS DISTINCT FROM expr", + /* 309 */ "exprx ::= NOT expr", + /* 310 */ "exprx ::= BITNOT expr", + /* 311 */ "exprx ::= MINUS expr", + /* 312 */ "exprx ::= PLUS expr", + /* 313 */ "exprx ::= expr PTR expr", + /* 314 */ "exprx ::= expr not_opt BETWEEN expr AND expr", + /* 315 */ "exprx ::= expr not_opt IN LP exprlist RP", + /* 316 */ "exprx ::= LP select RP", + /* 317 */ "exprx ::= expr not_opt IN LP select RP", + /* 318 */ "exprx ::= expr not_opt IN nm dbnm", + /* 319 */ "exprx ::= EXISTS LP select RP", + /* 320 */ "exprx ::= CASE case_operand case_exprlist case_else END", + /* 321 */ "exprx ::= RAISE LP IGNORE RP", + /* 322 */ "exprx ::= RAISE LP raisetype COMMA nm RP", + /* 323 */ "exprx ::= ID LP distinct exprlist RP filter_over", + /* 324 */ "exprx ::= ID LP STAR RP filter_over", + /* 325 */ "expr ::=", + /* 326 */ "expr ::= exprx", + /* 327 */ "not_opt ::=", + /* 328 */ "not_opt ::= NOT", + /* 329 */ "likeop ::= LIKE_KW|MATCH", + /* 330 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 331 */ "case_exprlist ::= WHEN expr THEN expr", + /* 332 */ "case_else ::= ELSE expr", + /* 333 */ "case_else ::=", + /* 334 */ "case_operand ::= exprx", + /* 335 */ "case_operand ::=", + /* 336 */ "exprlist ::= nexprlist", + /* 337 */ "exprlist ::=", + /* 338 */ "nexprlist ::= nexprlist COMMA expr", + /* 339 */ "nexprlist ::= exprx", + /* 340 */ "cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", + /* 341 */ "cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON ID_TAB", + /* 342 */ "cmd ::= CREATE uniqueflag INDEX ifnotexists nm DOT ID_IDX_NEW", + /* 343 */ "cmd ::= CREATE uniqueflag INDEX ifnotexists ID_DB|ID_IDX_NEW", + /* 344 */ "uniqueflag ::= UNIQUE", + /* 345 */ "uniqueflag ::=", + /* 346 */ "idxlist_opt ::=", + /* 347 */ "idxlist_opt ::= LP idxlist RP", + /* 348 */ "idxlist ::= idxlist COMMA idxlist_single", + /* 349 */ "idxlist ::= idxlist_single", + /* 350 */ "idxlist_single ::= nm collate sortorder", + /* 351 */ "idxlist_single ::= ID_COL", + /* 352 */ "collate ::=", + /* 353 */ "collate ::= COLLATE ids", + /* 354 */ "collate ::= COLLATE ID_COLLATE", + /* 355 */ "cmd ::= DROP INDEX ifexists fullname", + /* 356 */ "cmd ::= DROP INDEX ifexists nm DOT ID_IDX", + /* 357 */ "cmd ::= DROP INDEX ifexists ID_DB|ID_IDX", + /* 358 */ "cmd ::= VACUUM vinto", + /* 359 */ "cmd ::= VACUUM nm vinto", + /* 360 */ "vinto ::= INTO expr", + /* 361 */ "vinto ::=", + /* 362 */ "cmd ::= PRAGMA nm dbnm", + /* 363 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 364 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 365 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 366 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 367 */ "cmd ::= PRAGMA nm DOT ID_PRAGMA", + /* 368 */ "cmd ::= PRAGMA ID_DB|ID_PRAGMA", + /* 369 */ "nmnum ::= plus_num", + /* 370 */ "nmnum ::= nm", + /* 371 */ "nmnum ::= ON", + /* 372 */ "nmnum ::= DELETE", + /* 373 */ "nmnum ::= DEFAULT", + /* 374 */ "plus_num ::= PLUS number", + /* 375 */ "plus_num ::= number", + /* 376 */ "minus_num ::= MINUS number", + /* 377 */ "number ::= INTEGER", + /* 378 */ "number ::= FLOAT", + /* 379 */ "cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause BEGIN trigger_cmd_list END", + /* 380 */ "cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause", + /* 381 */ "cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause BEGIN trigger_cmd_list", + /* 382 */ "cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON ID_TAB", + /* 383 */ "cmd ::= CREATE temp TRIGGER ifnotexists nm DOT ID_TRIG_NEW", + /* 384 */ "cmd ::= CREATE temp TRIGGER ifnotexists ID_DB|ID_TRIG_NEW", + /* 385 */ "trigger_time ::= BEFORE", + /* 386 */ "trigger_time ::= AFTER", + /* 387 */ "trigger_time ::= INSTEAD OF", + /* 388 */ "trigger_time ::=", + /* 389 */ "trigger_event ::= DELETE", + /* 390 */ "trigger_event ::= INSERT", + /* 391 */ "trigger_event ::= UPDATE", + /* 392 */ "trigger_event ::= UPDATE OF idlist", + /* 393 */ "foreach_clause ::=", + /* 394 */ "foreach_clause ::= FOR EACH ROW", + /* 395 */ "when_clause ::=", + /* 396 */ "when_clause ::= WHEN expr", + /* 397 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 398 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 399 */ "trigger_cmd_list ::= SEMI", + /* 400 */ "trigger_cmd ::= update_stmt", + /* 401 */ "trigger_cmd ::= insert_stmt", + /* 402 */ "trigger_cmd ::= delete_stmt", + /* 403 */ "trigger_cmd ::= select_stmt", + /* 404 */ "raisetype ::= ROLLBACK|ABORT|FAIL", + /* 405 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 406 */ "cmd ::= DROP TRIGGER ifexists nm DOT ID_TRIG", + /* 407 */ "cmd ::= DROP TRIGGER ifexists ID_DB|ID_TRIG", + /* 408 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 409 */ "cmd ::= DETACH database_kw_opt expr", + /* 410 */ "key_opt ::=", + /* 411 */ "key_opt ::= KEY expr", + /* 412 */ "database_kw_opt ::= DATABASE", + /* 413 */ "database_kw_opt ::=", + /* 414 */ "cmd ::= REINDEX", + /* 415 */ "cmd ::= REINDEX nm dbnm", + /* 416 */ "cmd ::= REINDEX ID_COLLATE", + /* 417 */ "cmd ::= REINDEX nm DOT ID_TAB|ID_IDX", + /* 418 */ "cmd ::= REINDEX ID_DB|ID_IDX|ID_TAB", + /* 419 */ "cmd ::= ANALYZE", + /* 420 */ "cmd ::= ANALYZE nm dbnm", + /* 421 */ "cmd ::= ANALYZE nm DOT ID_TAB|ID_IDX", + /* 422 */ "cmd ::= ANALYZE ID_DB|ID_IDX|ID_TAB", + /* 423 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 424 */ "cmd ::= ALTER TABLE fullname ADD kwcolumn_opt column", + /* 425 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", + /* 426 */ "cmd ::= ALTER TABLE fullname RENAME TO ID_TAB_NEW", + /* 427 */ "cmd ::= ALTER TABLE nm DOT ID_TAB", + /* 428 */ "cmd ::= ALTER TABLE ID_DB|ID_TAB", + /* 429 */ "kwcolumn_opt ::=", + /* 430 */ "kwcolumn_opt ::= COLUMNKW", + /* 431 */ "cmd ::= create_vtab", + /* 432 */ "create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 433 */ "create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm dbnm USING nm LP vtabarglist RP", + /* 434 */ "create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm DOT ID_TAB_NEW", + /* 435 */ "create_vtab ::= CREATE VIRTUAL TABLE ifnotexists ID_DB|ID_TAB_NEW", + /* 436 */ "vtabarglist ::= vtabarg", + /* 437 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 438 */ "vtabarg ::=", + /* 439 */ "vtabarg ::= vtabarg vtabargtoken", + /* 440 */ "vtabargtoken ::= ANY", + /* 441 */ "vtabargtoken ::= LP anylist RP", + /* 442 */ "anylist ::=", + /* 443 */ "anylist ::= anylist LP anylist RP", + /* 444 */ "anylist ::= anylist ANY", + /* 445 */ "with ::=", + /* 446 */ "with ::= WITH wqlist", + /* 447 */ "with ::= WITH RECURSIVE wqlist", + /* 448 */ "wqas ::= AS", + /* 449 */ "wqas ::= AS MATERIALIZED", + /* 450 */ "wqas ::= AS NOT MATERIALIZED", + /* 451 */ "wqlist ::= wqcte", + /* 452 */ "wqlist ::= wqlist COMMA wqcte", + /* 453 */ "wqlist ::= ID_TAB_NEW", + /* 454 */ "wqcte ::= nm idxlist_opt wqas LP select RP", + /* 455 */ "windowdefn_list ::= windowdefn", + /* 456 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 457 */ "windowdefn ::= nm AS LP window RP", + /* 458 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 459 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 460 */ "window ::= ORDER BY sortlist frame_opt", + /* 461 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 462 */ "window ::= frame_opt", + /* 463 */ "window ::= nm frame_opt", + /* 464 */ "frame_opt ::=", + /* 465 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", + /* 466 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", + /* 467 */ "range_or_rows ::= RANGE|ROWS|GROUPS", + /* 468 */ "frame_bound_s ::= frame_bound", + /* 469 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 470 */ "frame_bound_e ::= frame_bound", + /* 471 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 472 */ "frame_bound ::= expr PRECEDING|FOLLOWING", + /* 473 */ "frame_bound ::= CURRENT ROW", + /* 474 */ "frame_exclude_opt ::=", + /* 475 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", + /* 476 */ "frame_exclude ::= NO OTHERS", + /* 477 */ "frame_exclude ::= CURRENT ROW", + /* 478 */ "frame_exclude ::= GROUP", + /* 479 */ "frame_exclude ::= TIES", + /* 480 */ "window_clause ::= WINDOW windowdefn_list", + /* 481 */ "filter_over ::= filter_clause over_clause", + /* 482 */ "filter_over ::= over_clause", + /* 483 */ "filter_over ::= filter_clause", + /* 484 */ "over_clause ::= OVER LP window RP", + /* 485 */ "over_clause ::= OVER nm", + /* 486 */ "filter_clause ::= FILTER LP WHERE expr RP", }; #endif /* NDEBUG */ @@ -1944,384 +1938,399 @@ static void yy_destructor( ** which appear on the RHS of the rule, but which are not used ** inside the C code. */ - case 191: /* cmd */ - case 194: /* ecmd */ - case 196: /* cmdx */ - case 243: /* select_stmt */ - case 272: /* delete_stmt */ - case 273: /* update_stmt */ - case 276: /* insert_stmt */ - case 297: /* trigger_cmd */ - case 301: /* create_vtab */ -{ -parser_safe_delete((yypminor->yy363)); + case 195: /* cmd */ + case 198: /* ecmd */ + case 200: /* cmdx */ + case 248: /* select_stmt */ + case 277: /* delete_stmt */ + case 279: /* update_stmt */ + case 282: /* insert_stmt */ + case 303: /* trigger_cmd */ + case 307: /* create_vtab */ +{ +parser_safe_delete((yypminor->yy41)); } break; - case 195: /* explain */ + case 199: /* explain */ { -parser_safe_delete((yypminor->yy91)); +parser_safe_delete((yypminor->yy499)); } break; - case 197: /* transtype */ - case 198: /* trans_opt */ + case 201: /* transtype */ + case 202: /* trans_opt */ { -parser_safe_delete((yypminor->yy84)); +parser_safe_delete((yypminor->yy512)); } break; - case 199: /* nm */ - case 206: /* table_options */ - case 209: /* columnid */ - case 212: /* id */ - case 213: /* id_opt */ - case 214: /* ids */ - case 216: /* typename */ - case 267: /* dbnm */ - case 288: /* collate */ - case 303: /* vtabarg */ - case 304: /* vtabargtoken */ - case 305: /* anylist */ + case 203: /* nm */ + case 214: /* columnid */ + case 217: /* id */ + case 218: /* id_opt */ + case 219: /* ids */ + case 221: /* typename */ + case 272: /* dbnm */ + case 294: /* collate */ + case 309: /* vtabarg */ + case 310: /* vtabargtoken */ + case 311: /* anylist */ { parser_safe_delete((yypminor->yy319)); } break; - case 200: /* savepoint_opt */ - case 202: /* ifnotexists */ - case 225: /* autoinc */ - case 229: /* gen_always */ - case 235: /* tconscomma */ - case 242: /* ifexists */ - case 280: /* not_opt */ - case 286: /* uniqueflag */ - case 298: /* database_kw_opt */ - case 300: /* kwcolumn_opt */ -{ -parser_safe_delete((yypminor->yy611)); + case 204: /* savepoint_opt */ + case 206: /* ifnotexists */ + case 230: /* autoinc */ + case 234: /* gen_always */ + case 240: /* tconscomma */ + case 247: /* ifexists */ + case 286: /* not_opt */ + case 292: /* uniqueflag */ + case 304: /* database_kw_opt */ + case 306: /* kwcolumn_opt */ +{ +parser_safe_delete((yypminor->yy225)); } break; - case 201: /* temp */ - case 249: /* distinct */ + case 205: /* temp */ + case 254: /* distinct */ { -parser_safe_delete((yypminor->yy386)); +parser_safe_delete((yypminor->yy130)); } break; - case 203: /* fullname */ + case 207: /* fullname */ { -parser_safe_delete((yypminor->yy440)); +parser_safe_delete((yypminor->yy396)); } break; - case 204: /* columnlist */ + case 208: /* columnlist */ { -parser_safe_delete((yypminor->yy42)); +parser_safe_delete((yypminor->yy390)); } break; - case 205: /* conslist_opt */ - case 234: /* conslist */ + case 209: /* conslist_opt */ + case 239: /* conslist */ { -parser_safe_delete((yypminor->yy493)); +parser_safe_delete((yypminor->yy115)); } break; - case 207: /* select */ - case 245: /* selectnowith */ + case 210: /* table_options */ { -parser_safe_delete((yypminor->yy313)); +parser_safe_delete((yypminor->yy455)); } break; - case 208: /* column */ + case 211: /* select */ + case 250: /* selectnowith */ { -parser_safe_delete((yypminor->yy147)); +parser_safe_delete((yypminor->yy297)); } break; - case 210: /* type */ - case 215: /* typetoken */ + case 212: /* table_option */ { -parser_safe_delete((yypminor->yy57)); +parser_safe_delete((yypminor->yy629)); } break; - case 211: /* carglist */ + case 213: /* column */ { -parser_safe_delete((yypminor->yy51)); +parser_safe_delete((yypminor->yy3)); } break; - case 217: /* signed */ - case 218: /* plus_num */ - case 219: /* minus_num */ - case 221: /* term */ - case 290: /* nmnum */ - case 291: /* number */ -{ -parser_safe_delete((yypminor->yy229)); + case 215: /* type */ + case 220: /* typetoken */ +{ +parser_safe_delete((yypminor->yy267)); } break; - case 220: /* ccons */ + case 216: /* carglist */ { -parser_safe_delete((yypminor->yy464)); +parser_safe_delete((yypminor->yy323)); } break; - case 222: /* expr */ - case 252: /* where_opt */ - case 254: /* having_opt */ - case 279: /* exprx */ - case 282: /* case_operand */ - case 284: /* case_else */ - case 289: /* vinto */ - case 295: /* when_clause */ - case 299: /* key_opt */ + case 222: /* signed */ + case 223: /* plus_num */ + case 224: /* minus_num */ + case 226: /* term */ + case 296: /* nmnum */ + case 297: /* number */ +{ +parser_safe_delete((yypminor->yy393)); +} + break; + case 225: /* ccons */ { -parser_safe_delete((yypminor->yy512)); +parser_safe_delete((yypminor->yy448)); } break; - case 223: /* onconf */ - case 239: /* resolvetype */ - case 240: /* orconf */ + case 227: /* expr */ + case 257: /* where_opt */ + case 259: /* having_opt */ + case 285: /* exprx */ + case 288: /* case_operand */ + case 290: /* case_else */ + case 295: /* vinto */ + case 301: /* when_clause */ + case 305: /* key_opt */ +{ +parser_safe_delete((yypminor->yy186)); +} + break; + case 228: /* onconf */ + case 244: /* resolvetype */ + case 245: /* orconf */ { -parser_safe_delete((yypminor->yy418)); +parser_safe_delete((yypminor->yy136)); } break; - case 224: /* sortorder */ + case 229: /* sortorder */ { -parser_safe_delete((yypminor->yy549)); +parser_safe_delete((yypminor->yy35)); } break; - case 226: /* idxlist_opt */ - case 237: /* idxlist */ + case 231: /* idxlist_opt */ + case 242: /* idxlist */ { -parser_safe_delete((yypminor->yy223)); +parser_safe_delete((yypminor->yy627)); } break; - case 227: /* refargs */ + case 232: /* refargs */ { -parser_safe_delete((yypminor->yy584)); +parser_safe_delete((yypminor->yy156)); } break; - case 228: /* defer_subclause */ - case 238: /* defer_subclause_opt */ + case 233: /* defer_subclause */ + case 243: /* defer_subclause_opt */ { -parser_safe_delete((yypminor->yy9)); +parser_safe_delete((yypminor->yy53)); } break; - case 230: /* tnm */ + case 235: /* tnm */ { -parser_safe_delete((yypminor->yy590)); +parser_safe_delete((yypminor->yy380)); } break; - case 231: /* refarg */ + case 236: /* refarg */ { -parser_safe_delete((yypminor->yy507)); +parser_safe_delete((yypminor->yy205)); } break; - case 232: /* refact */ + case 237: /* refact */ { -parser_safe_delete((yypminor->yy104)); +parser_safe_delete((yypminor->yy106)); } break; - case 233: /* init_deferred_pred_opt */ + case 238: /* init_deferred_pred_opt */ { -parser_safe_delete((yypminor->yy312)); +parser_safe_delete((yypminor->yy612)); } break; - case 236: /* tcons */ + case 241: /* tcons */ { -parser_safe_delete((yypminor->yy246)); +parser_safe_delete((yypminor->yy400)); } break; - case 244: /* with */ + case 249: /* with */ { -parser_safe_delete((yypminor->yy1)); +parser_safe_delete((yypminor->yy161)); } break; - case 246: /* oneselect */ + case 251: /* oneselect */ { -parser_safe_delete((yypminor->yy470)); +parser_safe_delete((yypminor->yy378)); } break; - case 247: /* multiselect_op */ + case 252: /* multiselect_op */ { -parser_safe_delete((yypminor->yy382)); +parser_safe_delete((yypminor->yy142)); } break; - case 248: /* values */ + case 253: /* values */ { -parser_safe_delete((yypminor->yy486)); +parser_safe_delete((yypminor->yy522)); } break; - case 250: /* selcollist */ - case 260: /* sclp */ + case 255: /* selcollist */ + case 265: /* sclp */ + case 278: /* returning */ { -parser_safe_delete((yypminor->yy53)); +parser_safe_delete((yypminor->yy27)); } break; - case 251: /* from */ - case 262: /* joinsrc */ + case 256: /* from */ + case 267: /* joinsrc */ { -parser_safe_delete((yypminor->yy31)); +parser_safe_delete((yypminor->yy553)); } break; - case 253: /* groupby_opt */ - case 258: /* nexprlist */ - case 259: /* exprlist */ - case 283: /* case_exprlist */ + case 258: /* groupby_opt */ + case 263: /* nexprlist */ + case 264: /* exprlist */ + case 289: /* case_exprlist */ { -parser_safe_delete((yypminor->yy71)); +parser_safe_delete((yypminor->yy615)); } break; - case 255: /* orderby_opt */ - case 270: /* sortlist */ + case 260: /* orderby_opt */ + case 275: /* sortlist */ { -parser_safe_delete((yypminor->yy403)); +parser_safe_delete((yypminor->yy226)); } break; - case 256: /* limit_opt */ + case 261: /* limit_opt */ { -parser_safe_delete((yypminor->yy4)); +parser_safe_delete((yypminor->yy360)); } break; - case 257: /* window_clause */ - case 308: /* windowdefn_list */ + case 262: /* window_clause */ + case 315: /* windowdefn_list */ { -parser_safe_delete((yypminor->yy299)); +parser_safe_delete((yypminor->yy525)); } break; - case 261: /* as */ + case 266: /* as */ { -parser_safe_delete((yypminor->yy200)); +parser_safe_delete((yypminor->yy628)); } break; - case 263: /* singlesrc */ + case 268: /* singlesrc */ { -parser_safe_delete((yypminor->yy441)); +parser_safe_delete((yypminor->yy595)); } break; - case 264: /* seltablist */ + case 269: /* seltablist */ { -parser_safe_delete((yypminor->yy451)); +parser_safe_delete((yypminor->yy107)); } break; - case 265: /* joinop */ + case 270: /* joinop */ { -parser_safe_delete((yypminor->yy221)); +parser_safe_delete((yypminor->yy449)); } break; - case 266: /* joinconstr_opt */ + case 271: /* joinconstr_opt */ { -parser_safe_delete((yypminor->yy295)); +parser_safe_delete((yypminor->yy215)); } break; - case 268: /* indexed_opt */ + case 273: /* indexed_opt */ { -parser_safe_delete((yypminor->yy592)); +parser_safe_delete((yypminor->yy300)); } break; - case 269: /* idlist */ - case 275: /* idlist_opt */ - case 302: /* vtabarglist */ + case 274: /* idlist */ + case 281: /* idlist_opt */ + case 308: /* vtabarglist */ { -parser_safe_delete((yypminor->yy575)); +parser_safe_delete((yypminor->yy173)); } break; - case 271: /* nulls */ + case 276: /* nulls */ { -parser_safe_delete((yypminor->yy579)); +parser_safe_delete((yypminor->yy315)); } break; - case 274: /* setlist */ + case 280: /* setlist */ { -parser_safe_delete((yypminor->yy201)); +parser_safe_delete((yypminor->yy621)); } break; - case 277: /* insert_cmd */ + case 283: /* insert_cmd */ { -parser_safe_delete((yypminor->yy504)); +parser_safe_delete((yypminor->yy308)); } break; - case 278: /* upsert */ + case 284: /* upsert */ { -parser_safe_delete((yypminor->yy400)); +parser_safe_delete((yypminor->yy332)); } break; - case 281: /* likeop */ + case 287: /* likeop */ { -parser_safe_delete((yypminor->yy40)); +parser_safe_delete((yypminor->yy274)); } break; - case 285: /* filter_over */ + case 291: /* filter_over */ { -parser_safe_delete((yypminor->yy247)); +parser_safe_delete((yypminor->yy181)); } break; - case 287: /* idxlist_single */ + case 293: /* idxlist_single */ { -parser_safe_delete((yypminor->yy268)); +parser_safe_delete((yypminor->yy110)); } break; - case 292: /* trigger_time */ + case 298: /* trigger_time */ { -parser_safe_delete((yypminor->yy532)); +parser_safe_delete((yypminor->yy120)); } break; - case 293: /* trigger_event */ + case 299: /* trigger_event */ { -parser_safe_delete((yypminor->yy151)); +parser_safe_delete((yypminor->yy259)); } break; - case 294: /* foreach_clause */ + case 300: /* foreach_clause */ { -parser_safe_delete((yypminor->yy83)); +parser_safe_delete((yypminor->yy456)); } break; - case 296: /* trigger_cmd_list */ + case 302: /* trigger_cmd_list */ { -parser_safe_delete((yypminor->yy110)); +parser_safe_delete((yypminor->yy240)); } break; - case 306: /* wqlist */ + case 312: /* wqlist */ { -parser_safe_delete((yypminor->yy593)); +parser_safe_delete((yypminor->yy164)); } break; - case 307: /* wqcte */ + case 313: /* wqas */ { -parser_safe_delete((yypminor->yy446)); +parser_safe_delete((yypminor->yy21)); } break; - case 309: /* windowdefn */ + case 314: /* wqcte */ { -parser_safe_delete((yypminor->yy266)); +parser_safe_delete((yypminor->yy146)); } break; - case 310: /* window */ + case 316: /* windowdefn */ { -parser_safe_delete((yypminor->yy334)); +parser_safe_delete((yypminor->yy562)); } break; - case 311: /* frame_opt */ + case 317: /* window */ { -parser_safe_delete((yypminor->yy41)); +parser_safe_delete((yypminor->yy162)); } break; - case 312: /* range_or_rows */ + case 318: /* frame_opt */ { -parser_safe_delete((yypminor->yy419)); +parser_safe_delete((yypminor->yy149)); } break; - case 313: /* frame_bound_s */ - case 315: /* frame_bound_e */ + case 319: /* range_or_rows */ { -parser_safe_delete((yypminor->yy442)); +parser_safe_delete((yypminor->yy143)); } break; - case 316: /* frame_bound */ + case 320: /* frame_bound_s */ + case 322: /* frame_bound_e */ { -parser_safe_delete((yypminor->yy442));parser_safe_delete((yypminor->yy442));parser_safe_delete((yypminor->yy442)); +parser_safe_delete((yypminor->yy285)); } break; - case 318: /* filter_clause */ + case 323: /* frame_bound */ { -parser_safe_delete((yypminor->yy397)); +parser_safe_delete((yypminor->yy285));parser_safe_delete((yypminor->yy285));parser_safe_delete((yypminor->yy285)); } break; - case 319: /* over_clause */ + case 325: /* filter_clause */ { -parser_safe_delete((yypminor->yy248)); +parser_safe_delete((yypminor->yy39)); +} + break; + case 326: /* over_clause */ +{ +parser_safe_delete((yypminor->yy11)); } break; default: break; /* If no destructor action specified: do nothing */ @@ -2572,478 +2581,493 @@ static const struct { YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ unsigned char nrhs; /* Number of right-hand side symbols in the rule */ } yyRuleInfo[] = { - { 192, 1 }, - { 193, 2 }, - { 193, 1 }, - { 194, 1 }, - { 194, 3 }, - { 195, 0 }, - { 195, 1 }, - { 195, 3 }, { 196, 1 }, - { 191, 3 }, - { 198, 0 }, - { 198, 1 }, - { 198, 2 }, - { 198, 2 }, - { 197, 0 }, - { 197, 1 }, + { 197, 2 }, { 197, 1 }, - { 197, 1 }, - { 191, 2 }, - { 191, 2 }, - { 191, 2 }, + { 198, 1 }, + { 198, 3 }, + { 199, 0 }, + { 199, 1 }, + { 199, 3 }, { 200, 1 }, - { 200, 0 }, - { 191, 2 }, - { 191, 3 }, - { 191, 5 }, - { 191, 2 }, - { 191, 3 }, - { 191, 5 }, - { 191, 10 }, - { 191, 7 }, - { 191, 7 }, - { 191, 5 }, - { 206, 0 }, - { 206, 2 }, - { 206, 2 }, + { 195, 3 }, { 202, 0 }, - { 202, 3 }, - { 201, 1 }, + { 202, 1 }, + { 202, 2 }, + { 202, 2 }, { 201, 0 }, - { 204, 3 }, + { 201, 1 }, + { 201, 1 }, + { 201, 1 }, + { 195, 2 }, + { 195, 2 }, + { 195, 2 }, { 204, 1 }, - { 208, 3 }, - { 209, 1 }, - { 209, 1 }, - { 212, 1 }, - { 213, 1 }, - { 213, 0 }, - { 214, 1 }, - { 199, 1 }, - { 199, 1 }, - { 199, 1 }, + { 204, 0 }, + { 195, 2 }, + { 195, 3 }, + { 195, 5 }, + { 195, 2 }, + { 195, 3 }, + { 195, 5 }, + { 195, 10 }, + { 195, 7 }, + { 195, 7 }, + { 195, 5 }, { 210, 0 }, { 210, 1 }, - { 215, 1 }, - { 215, 4 }, - { 215, 6 }, - { 216, 1 }, - { 216, 2 }, - { 216, 1 }, - { 217, 1 }, + { 210, 3 }, + { 212, 2 }, + { 212, 1 }, + { 212, 2 }, + { 212, 1 }, + { 206, 0 }, + { 206, 3 }, + { 205, 1 }, + { 205, 0 }, + { 208, 3 }, + { 208, 1 }, + { 213, 3 }, + { 214, 1 }, + { 214, 1 }, { 217, 1 }, - { 211, 2 }, - { 211, 0 }, - { 220, 2 }, - { 220, 2 }, - { 220, 4 }, - { 220, 3 }, - { 220, 3 }, - { 220, 2 }, - { 220, 2 }, - { 220, 2 }, - { 220, 3 }, - { 220, 5 }, - { 220, 2 }, - { 220, 4 }, - { 220, 4 }, + { 218, 1 }, + { 218, 0 }, + { 219, 1 }, + { 203, 1 }, + { 203, 1 }, + { 203, 1 }, + { 215, 0 }, + { 215, 1 }, { 220, 1 }, - { 220, 2 }, + { 220, 4 }, { 220, 6 }, - { 220, 2 }, - { 220, 2 }, - { 220, 2 }, - { 220, 3 }, - { 221, 1 }, - { 221, 1 }, { 221, 1 }, + { 221, 2 }, { 221, 1 }, - { 230, 1 }, - { 230, 1 }, - { 229, 2 }, - { 229, 0 }, - { 225, 0 }, + { 222, 1 }, + { 222, 1 }, + { 216, 2 }, + { 216, 0 }, + { 225, 2 }, + { 225, 2 }, + { 225, 4 }, + { 225, 3 }, + { 225, 3 }, + { 225, 2 }, + { 225, 2 }, + { 225, 2 }, + { 225, 3 }, + { 225, 5 }, + { 225, 2 }, + { 225, 4 }, + { 225, 4 }, { 225, 1 }, - { 227, 0 }, - { 227, 2 }, - { 231, 2 }, - { 231, 3 }, - { 231, 3 }, - { 231, 3 }, - { 231, 2 }, - { 232, 2 }, - { 232, 2 }, - { 232, 1 }, - { 232, 1 }, - { 232, 2 }, - { 228, 3 }, - { 228, 2 }, - { 233, 0 }, - { 233, 2 }, - { 233, 2 }, - { 205, 0 }, - { 205, 2 }, - { 234, 3 }, - { 234, 1 }, + { 225, 2 }, + { 225, 6 }, + { 225, 2 }, + { 225, 2 }, + { 225, 2 }, + { 225, 3 }, + { 226, 1 }, + { 226, 1 }, + { 226, 1 }, + { 226, 1 }, { 235, 1 }, - { 235, 0 }, + { 235, 1 }, + { 234, 2 }, + { 234, 0 }, + { 230, 0 }, + { 230, 1 }, + { 232, 0 }, + { 232, 2 }, { 236, 2 }, - { 236, 7 }, - { 236, 5 }, - { 236, 5 }, - { 236, 10 }, + { 236, 3 }, + { 236, 3 }, + { 236, 3 }, { 236, 2 }, - { 236, 7 }, - { 236, 4 }, + { 237, 2 }, + { 237, 2 }, + { 237, 1 }, + { 237, 1 }, + { 237, 2 }, + { 233, 3 }, + { 233, 2 }, { 238, 0 }, - { 238, 1 }, - { 223, 0 }, - { 223, 3 }, - { 240, 0 }, - { 240, 2 }, - { 239, 1 }, + { 238, 2 }, + { 238, 2 }, + { 209, 0 }, + { 209, 2 }, + { 239, 3 }, { 239, 1 }, - { 239, 1 }, - { 191, 4 }, - { 191, 6 }, - { 191, 4 }, - { 242, 2 }, - { 242, 0 }, - { 191, 8 }, - { 191, 7 }, - { 191, 5 }, - { 191, 4 }, - { 191, 6 }, - { 191, 4 }, - { 191, 1 }, + { 240, 1 }, + { 240, 0 }, + { 241, 2 }, + { 241, 7 }, + { 241, 5 }, + { 241, 5 }, + { 241, 10 }, + { 241, 2 }, + { 241, 7 }, + { 241, 4 }, + { 243, 0 }, { 243, 1 }, - { 207, 2 }, - { 245, 1 }, - { 245, 3 }, - { 245, 1 }, - { 245, 3 }, - { 246, 9 }, - { 246, 10 }, - { 248, 4 }, - { 248, 5 }, - { 247, 1 }, + { 228, 0 }, + { 228, 3 }, + { 245, 0 }, + { 245, 2 }, + { 244, 1 }, + { 244, 1 }, + { 244, 1 }, + { 195, 4 }, + { 195, 6 }, + { 195, 4 }, { 247, 2 }, - { 247, 1 }, - { 247, 1 }, - { 249, 1 }, - { 249, 1 }, - { 249, 0 }, - { 260, 2 }, - { 260, 0 }, + { 247, 0 }, + { 195, 8 }, + { 195, 7 }, + { 195, 5 }, + { 195, 4 }, + { 195, 6 }, + { 195, 4 }, + { 195, 1 }, + { 248, 1 }, + { 211, 2 }, + { 250, 1 }, { 250, 3 }, - { 250, 2 }, - { 250, 4 }, { 250, 1 }, - { 250, 4 }, - { 261, 2 }, - { 261, 1 }, - { 261, 2 }, - { 261, 1 }, - { 261, 0 }, - { 251, 0 }, - { 251, 2 }, - { 262, 2 }, - { 262, 0 }, - { 264, 4 }, - { 264, 0 }, - { 263, 4 }, - { 263, 4 }, - { 263, 4 }, - { 263, 6 }, - { 263, 0 }, - { 263, 2 }, - { 263, 3 }, - { 263, 1 }, - { 263, 3 }, - { 263, 1 }, + { 250, 3 }, + { 251, 9 }, + { 251, 10 }, + { 253, 4 }, + { 253, 5 }, + { 252, 1 }, + { 252, 2 }, + { 252, 1 }, + { 252, 1 }, + { 254, 1 }, + { 254, 1 }, + { 254, 0 }, + { 265, 2 }, + { 265, 0 }, + { 255, 3 }, + { 255, 2 }, + { 255, 4 }, + { 255, 1 }, + { 255, 4 }, { 266, 2 }, - { 266, 4 }, + { 266, 1 }, + { 266, 2 }, + { 266, 1 }, { 266, 0 }, - { 267, 0 }, + { 256, 0 }, + { 256, 2 }, { 267, 2 }, - { 203, 2 }, - { 265, 1 }, - { 265, 1 }, - { 265, 2 }, - { 265, 3 }, - { 265, 4 }, - { 265, 1 }, + { 267, 0 }, + { 269, 4 }, + { 269, 0 }, + { 268, 4 }, + { 268, 4 }, + { 268, 4 }, + { 268, 6 }, { 268, 0 }, - { 268, 3 }, { 268, 2 }, { 268, 3 }, - { 255, 0 }, - { 255, 3 }, - { 270, 5 }, - { 270, 3 }, - { 224, 1 }, - { 224, 1 }, - { 224, 0 }, - { 271, 2 }, + { 268, 1 }, + { 268, 3 }, + { 268, 1 }, { 271, 2 }, + { 271, 4 }, { 271, 0 }, - { 253, 0 }, - { 253, 3 }, - { 253, 2 }, - { 254, 0 }, - { 254, 2 }, - { 256, 0 }, - { 256, 2 }, - { 256, 4 }, - { 256, 4 }, - { 191, 1 }, - { 272, 6 }, - { 272, 3 }, - { 272, 5 }, - { 272, 6 }, - { 272, 4 }, - { 252, 0 }, - { 252, 2 }, - { 252, 1 }, - { 191, 1 }, - { 273, 9 }, + { 272, 0 }, + { 272, 2 }, + { 207, 2 }, + { 270, 1 }, + { 270, 1 }, + { 270, 2 }, + { 270, 3 }, + { 270, 4 }, + { 270, 1 }, + { 273, 0 }, { 273, 3 }, - { 273, 5 }, - { 273, 6 }, - { 273, 4 }, - { 274, 5 }, - { 274, 7 }, - { 274, 3 }, - { 274, 5 }, - { 274, 0 }, - { 274, 2 }, - { 274, 3 }, - { 274, 1 }, - { 275, 0 }, + { 273, 2 }, + { 273, 3 }, + { 260, 0 }, + { 260, 3 }, + { 275, 5 }, { 275, 3 }, - { 269, 3 }, - { 269, 1 }, - { 269, 0 }, - { 269, 3 }, - { 269, 1 }, - { 191, 1 }, - { 276, 7 }, - { 276, 7 }, - { 276, 3 }, - { 276, 5 }, - { 276, 4 }, - { 276, 6 }, - { 277, 2 }, - { 277, 1 }, + { 229, 1 }, + { 229, 1 }, + { 229, 0 }, + { 276, 2 }, + { 276, 2 }, + { 276, 0 }, + { 258, 0 }, + { 258, 3 }, + { 258, 2 }, + { 259, 0 }, + { 259, 2 }, + { 261, 0 }, + { 261, 2 }, + { 261, 4 }, + { 261, 4 }, + { 195, 1 }, + { 277, 7 }, + { 277, 3 }, + { 277, 5 }, + { 277, 6 }, + { 277, 4 }, + { 257, 0 }, + { 257, 2 }, + { 257, 1 }, { 278, 0 }, - { 278, 11 }, - { 278, 8 }, - { 278, 4 }, - { 279, 4 }, - { 279, 6 }, - { 279, 1 }, - { 279, 3 }, - { 279, 5 }, - { 279, 3 }, - { 279, 6 }, - { 279, 1 }, - { 279, 3 }, - { 279, 1 }, + { 278, 2 }, + { 195, 1 }, + { 279, 10 }, { 279, 3 }, { 279, 5 }, - { 279, 1 }, - { 279, 3 }, { 279, 6 }, - { 279, 5 }, - { 279, 4 }, - { 279, 3 }, - { 279, 3 }, - { 279, 3 }, - { 279, 3 }, - { 279, 3 }, - { 279, 3 }, - { 279, 3 }, - { 279, 3 }, { 279, 4 }, - { 279, 6 }, - { 279, 2 }, - { 279, 3 }, - { 279, 4 }, - { 279, 2 }, - { 279, 2 }, - { 279, 2 }, - { 279, 2 }, - { 279, 6 }, - { 279, 6 }, - { 279, 3 }, - { 279, 6 }, - { 279, 5 }, - { 279, 4 }, - { 279, 5 }, - { 279, 4 }, - { 279, 6 }, - { 279, 6 }, - { 279, 5 }, - { 222, 0 }, - { 222, 1 }, + { 280, 5 }, + { 280, 7 }, + { 280, 3 }, + { 280, 5 }, { 280, 0 }, + { 280, 2 }, + { 280, 3 }, { 280, 1 }, - { 281, 1 }, - { 283, 5 }, - { 283, 4 }, - { 284, 2 }, + { 281, 0 }, + { 281, 3 }, + { 274, 3 }, + { 274, 1 }, + { 274, 0 }, + { 274, 3 }, + { 274, 1 }, + { 195, 1 }, + { 282, 8 }, + { 282, 8 }, + { 282, 3 }, + { 282, 5 }, + { 282, 4 }, + { 282, 6 }, + { 283, 2 }, + { 283, 1 }, { 284, 0 }, - { 282, 1 }, - { 282, 0 }, - { 259, 1 }, - { 259, 0 }, - { 258, 3 }, - { 258, 1 }, - { 191, 12 }, - { 191, 8 }, - { 191, 7 }, - { 191, 5 }, - { 286, 1 }, + { 284, 11 }, + { 284, 8 }, + { 284, 4 }, + { 285, 4 }, + { 285, 6 }, + { 285, 1 }, + { 285, 3 }, + { 285, 5 }, + { 285, 3 }, + { 285, 6 }, + { 285, 1 }, + { 285, 3 }, + { 285, 1 }, + { 285, 3 }, + { 285, 2 }, + { 285, 5 }, + { 285, 4 }, + { 285, 1 }, + { 285, 3 }, + { 285, 6 }, + { 285, 5 }, + { 285, 4 }, + { 285, 3 }, + { 285, 3 }, + { 285, 3 }, + { 285, 3 }, + { 285, 3 }, + { 285, 3 }, + { 285, 3 }, + { 285, 3 }, + { 285, 4 }, + { 285, 6 }, + { 285, 2 }, + { 285, 3 }, + { 285, 4 }, + { 285, 6 }, + { 285, 5 }, + { 285, 2 }, + { 285, 2 }, + { 285, 2 }, + { 285, 2 }, + { 285, 3 }, + { 285, 6 }, + { 285, 6 }, + { 285, 3 }, + { 285, 6 }, + { 285, 5 }, + { 285, 4 }, + { 285, 5 }, + { 285, 4 }, + { 285, 6 }, + { 285, 6 }, + { 285, 5 }, + { 227, 0 }, + { 227, 1 }, { 286, 0 }, - { 226, 0 }, - { 226, 3 }, - { 237, 3 }, - { 237, 1 }, - { 287, 3 }, + { 286, 1 }, { 287, 1 }, + { 289, 5 }, + { 289, 4 }, + { 290, 2 }, + { 290, 0 }, + { 288, 1 }, { 288, 0 }, - { 288, 2 }, - { 288, 2 }, - { 191, 4 }, - { 191, 6 }, - { 191, 4 }, - { 191, 2 }, - { 191, 3 }, - { 289, 2 }, - { 289, 0 }, - { 191, 3 }, - { 191, 5 }, - { 191, 6 }, - { 191, 5 }, - { 191, 6 }, - { 191, 4 }, - { 191, 2 }, - { 290, 1 }, - { 290, 1 }, - { 290, 1 }, - { 290, 1 }, - { 290, 1 }, - { 218, 2 }, - { 218, 1 }, - { 219, 2 }, - { 291, 1 }, - { 291, 1 }, - { 191, 15 }, - { 191, 12 }, - { 191, 14 }, - { 191, 10 }, - { 191, 7 }, - { 191, 5 }, - { 292, 1 }, + { 264, 1 }, + { 264, 0 }, + { 263, 3 }, + { 263, 1 }, + { 195, 12 }, + { 195, 8 }, + { 195, 7 }, + { 195, 5 }, { 292, 1 }, - { 292, 2 }, { 292, 0 }, - { 293, 1 }, - { 293, 1 }, - { 293, 1 }, + { 231, 0 }, + { 231, 3 }, + { 242, 3 }, + { 242, 1 }, { 293, 3 }, + { 293, 1 }, { 294, 0 }, - { 294, 3 }, - { 295, 0 }, + { 294, 2 }, + { 294, 2 }, + { 195, 4 }, + { 195, 6 }, + { 195, 4 }, + { 195, 2 }, + { 195, 3 }, { 295, 2 }, - { 296, 3 }, - { 296, 2 }, + { 295, 0 }, + { 195, 3 }, + { 195, 5 }, + { 195, 6 }, + { 195, 5 }, + { 195, 6 }, + { 195, 4 }, + { 195, 2 }, { 296, 1 }, + { 296, 1 }, + { 296, 1 }, + { 296, 1 }, + { 296, 1 }, + { 223, 2 }, + { 223, 1 }, + { 224, 2 }, { 297, 1 }, { 297, 1 }, - { 297, 1 }, - { 297, 1 }, - { 241, 1 }, - { 191, 4 }, - { 191, 6 }, - { 191, 4 }, - { 191, 6 }, - { 191, 3 }, - { 299, 0 }, - { 299, 2 }, + { 195, 15 }, + { 195, 12 }, + { 195, 14 }, + { 195, 10 }, + { 195, 7 }, + { 195, 5 }, { 298, 1 }, + { 298, 1 }, + { 298, 2 }, { 298, 0 }, - { 191, 1 }, - { 191, 3 }, - { 191, 2 }, - { 191, 4 }, - { 191, 2 }, - { 191, 1 }, - { 191, 3 }, - { 191, 4 }, - { 191, 2 }, - { 191, 6 }, - { 191, 6 }, - { 191, 6 }, - { 191, 5 }, - { 191, 3 }, + { 299, 1 }, + { 299, 1 }, + { 299, 1 }, + { 299, 3 }, { 300, 0 }, - { 300, 1 }, - { 191, 1 }, - { 301, 8 }, - { 301, 11 }, - { 301, 7 }, - { 301, 5 }, - { 302, 1 }, + { 300, 3 }, + { 301, 0 }, + { 301, 2 }, { 302, 3 }, - { 303, 0 }, - { 303, 2 }, - { 304, 1 }, - { 304, 3 }, + { 302, 2 }, + { 302, 1 }, + { 303, 1 }, + { 303, 1 }, + { 303, 1 }, + { 303, 1 }, + { 246, 1 }, + { 195, 4 }, + { 195, 6 }, + { 195, 4 }, + { 195, 6 }, + { 195, 3 }, { 305, 0 }, - { 305, 4 }, { 305, 2 }, - { 244, 0 }, - { 244, 2 }, - { 244, 3 }, - { 306, 1 }, - { 306, 3 }, + { 304, 1 }, + { 304, 0 }, + { 195, 1 }, + { 195, 3 }, + { 195, 2 }, + { 195, 4 }, + { 195, 2 }, + { 195, 1 }, + { 195, 3 }, + { 195, 4 }, + { 195, 2 }, + { 195, 6 }, + { 195, 6 }, + { 195, 6 }, + { 195, 6 }, + { 195, 5 }, + { 195, 3 }, + { 306, 0 }, { 306, 1 }, - { 307, 6 }, + { 195, 1 }, + { 307, 8 }, + { 307, 11 }, + { 307, 7 }, + { 307, 5 }, { 308, 1 }, { 308, 3 }, - { 309, 5 }, - { 310, 5 }, - { 310, 6 }, - { 310, 4 }, - { 310, 5 }, + { 309, 0 }, + { 309, 2 }, { 310, 1 }, - { 310, 2 }, + { 310, 3 }, { 311, 0 }, - { 311, 3 }, - { 311, 6 }, - { 312, 1 }, + { 311, 4 }, + { 311, 2 }, + { 249, 0 }, + { 249, 2 }, + { 249, 3 }, { 313, 1 }, { 313, 2 }, + { 313, 3 }, + { 312, 1 }, + { 312, 3 }, + { 312, 1 }, + { 314, 6 }, { 315, 1 }, - { 315, 2 }, - { 316, 2 }, - { 316, 2 }, - { 314, 0 }, - { 314, 2 }, - { 317, 2 }, - { 317, 2 }, - { 317, 1 }, + { 315, 3 }, + { 316, 5 }, + { 317, 5 }, + { 317, 6 }, + { 317, 4 }, + { 317, 5 }, { 317, 1 }, - { 257, 2 }, - { 285, 2 }, - { 285, 1 }, - { 285, 1 }, - { 319, 4 }, - { 319, 2 }, - { 318, 5 }, + { 317, 2 }, + { 318, 0 }, + { 318, 3 }, + { 318, 6 }, + { 319, 1 }, + { 320, 1 }, + { 320, 2 }, + { 322, 1 }, + { 322, 2 }, + { 323, 2 }, + { 323, 2 }, + { 321, 0 }, + { 321, 2 }, + { 324, 2 }, + { 324, 2 }, + { 324, 1 }, + { 324, 1 }, + { 262, 2 }, + { 291, 2 }, + { 291, 1 }, + { 291, 1 }, + { 326, 4 }, + { 326, 2 }, + { 325, 5 }, }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -3103,279 +3127,299 @@ static void yy_reduce( ** break; */ case 1: /* cmdlist ::= cmdlist ecmd */ -{parserContext->addQuery(yymsp[0].minor.yy363); DONT_INHERIT_TOKENS("cmdlist");} +{parserContext->addQuery(yymsp[0].minor.yy41); DONT_INHERIT_TOKENS("cmdlist");} break; case 2: /* cmdlist ::= ecmd */ -{parserContext->addQuery(yymsp[0].minor.yy363);} +{parserContext->addQuery(yymsp[0].minor.yy41);} break; case 3: /* ecmd ::= SEMI */ -{yygotominor.yy363 = new SqliteEmptyQuery();} +{yygotominor.yy41 = new SqliteEmptyQuery();} break; case 4: /* ecmd ::= explain cmdx SEMI */ { - yygotominor.yy363 = yymsp[-1].minor.yy363; - yygotominor.yy363->explain = yymsp[-2].minor.yy91->explain; - yygotominor.yy363->queryPlan = yymsp[-2].minor.yy91->queryPlan; - delete yymsp[-2].minor.yy91; - objectForTokens = yygotominor.yy363; + yygotominor.yy41 = yymsp[-1].minor.yy41; + yygotominor.yy41->explain = yymsp[-2].minor.yy499->explain; + yygotominor.yy41->queryPlan = yymsp[-2].minor.yy499->queryPlan; + delete yymsp[-2].minor.yy499; + objectForTokens = yygotominor.yy41; } break; case 5: /* explain ::= */ -{yygotominor.yy91 = new ParserStubExplain(false, false);} +{yygotominor.yy499 = new ParserStubExplain(false, false);} break; case 6: /* explain ::= EXPLAIN */ -{yygotominor.yy91 = new ParserStubExplain(true, false);} +{yygotominor.yy499 = new ParserStubExplain(true, false);} break; case 7: /* explain ::= EXPLAIN QUERY PLAN */ -{yygotominor.yy91 = new ParserStubExplain(true, true);} +{yygotominor.yy499 = new ParserStubExplain(true, true);} break; case 8: /* cmdx ::= cmd */ - case 389: /* trigger_cmd ::= update_stmt */ yytestcase(yyruleno==389); - case 390: /* trigger_cmd ::= insert_stmt */ yytestcase(yyruleno==390); - case 391: /* trigger_cmd ::= delete_stmt */ yytestcase(yyruleno==391); - case 392: /* trigger_cmd ::= select_stmt */ yytestcase(yyruleno==392); - case 419: /* cmd ::= create_vtab */ yytestcase(yyruleno==419); -{yygotominor.yy363 = yymsp[0].minor.yy363;} + case 400: /* trigger_cmd ::= update_stmt */ yytestcase(yyruleno==400); + case 401: /* trigger_cmd ::= insert_stmt */ yytestcase(yyruleno==401); + case 402: /* trigger_cmd ::= delete_stmt */ yytestcase(yyruleno==402); + case 403: /* trigger_cmd ::= select_stmt */ yytestcase(yyruleno==403); + case 431: /* cmd ::= create_vtab */ yytestcase(yyruleno==431); +{yygotominor.yy41 = yymsp[0].minor.yy41;} break; case 9: /* cmd ::= BEGIN transtype trans_opt */ { - yygotominor.yy363 = new SqliteBeginTrans( - yymsp[-1].minor.yy84->type, - yymsp[0].minor.yy84->transactionKw, - yymsp[0].minor.yy84->name + yygotominor.yy41 = new SqliteBeginTrans( + yymsp[-1].minor.yy512->type, + yymsp[0].minor.yy512->transactionKw, + yymsp[0].minor.yy512->name ); - delete yymsp[0].minor.yy84; - delete yymsp[-1].minor.yy84; - objectForTokens = yygotominor.yy363; + delete yymsp[0].minor.yy512; + delete yymsp[-1].minor.yy512; + objectForTokens = yygotominor.yy41; } break; case 10: /* trans_opt ::= */ case 14: /* transtype ::= */ yytestcase(yyruleno==14); -{yygotominor.yy84 = new ParserStubTransDetails();} +{yygotominor.yy512 = new ParserStubTransDetails();} break; case 11: /* trans_opt ::= TRANSACTION */ { - yygotominor.yy84 = new ParserStubTransDetails(); - yygotominor.yy84->transactionKw = true; + yygotominor.yy512 = new ParserStubTransDetails(); + yygotominor.yy512->transactionKw = true; } break; case 12: /* trans_opt ::= TRANSACTION nm */ case 13: /* trans_opt ::= TRANSACTION ID_TRANS */ yytestcase(yyruleno==13); { - yygotominor.yy84 = new ParserStubTransDetails(); - yygotominor.yy84->transactionKw = true; - yygotominor.yy84->name = *(yymsp[0].minor.yy319); + yygotominor.yy512 = new ParserStubTransDetails(); + yygotominor.yy512->transactionKw = true; + yygotominor.yy512->name = *(yymsp[0].minor.yy319); delete yymsp[0].minor.yy319; } break; case 15: /* transtype ::= DEFERRED */ { - yygotominor.yy84 = new ParserStubTransDetails(); - yygotominor.yy84->type = SqliteBeginTrans::Type::DEFERRED; + yygotominor.yy512 = new ParserStubTransDetails(); + yygotominor.yy512->type = SqliteBeginTrans::Type::DEFERRED; } break; case 16: /* transtype ::= IMMEDIATE */ { - yygotominor.yy84 = new ParserStubTransDetails(); - yygotominor.yy84->type = SqliteBeginTrans::Type::IMMEDIATE; + yygotominor.yy512 = new ParserStubTransDetails(); + yygotominor.yy512->type = SqliteBeginTrans::Type::IMMEDIATE; } break; case 17: /* transtype ::= EXCLUSIVE */ { - yygotominor.yy84 = new ParserStubTransDetails(); - yygotominor.yy84->type = SqliteBeginTrans::Type::EXCLUSIVE; + yygotominor.yy512 = new ParserStubTransDetails(); + yygotominor.yy512->type = SqliteBeginTrans::Type::EXCLUSIVE; } break; case 18: /* cmd ::= COMMIT trans_opt */ { - yygotominor.yy363 = new SqliteCommitTrans( - yymsp[0].minor.yy84->transactionKw, - yymsp[0].minor.yy84->name, + yygotominor.yy41 = new SqliteCommitTrans( + yymsp[0].minor.yy512->transactionKw, + yymsp[0].minor.yy512->name, false ); - delete yymsp[0].minor.yy84; - objectForTokens = yygotominor.yy363; + delete yymsp[0].minor.yy512; + objectForTokens = yygotominor.yy41; } break; case 19: /* cmd ::= END trans_opt */ { - yygotominor.yy363 = new SqliteCommitTrans( - yymsp[0].minor.yy84->transactionKw, - yymsp[0].minor.yy84->name, + yygotominor.yy41 = new SqliteCommitTrans( + yymsp[0].minor.yy512->transactionKw, + yymsp[0].minor.yy512->name, true ); - delete yymsp[0].minor.yy84; - objectForTokens = yygotominor.yy363; + delete yymsp[0].minor.yy512; + objectForTokens = yygotominor.yy41; } break; case 20: /* cmd ::= ROLLBACK trans_opt */ { - yygotominor.yy363 = new SqliteRollback( - yymsp[0].minor.yy84->transactionKw, - yymsp[0].minor.yy84->name + yygotominor.yy41 = new SqliteRollback( + yymsp[0].minor.yy512->transactionKw, + yymsp[0].minor.yy512->name ); - delete yymsp[0].minor.yy84; - objectForTokens = yygotominor.yy363; + delete yymsp[0].minor.yy512; + objectForTokens = yygotominor.yy41; } break; case 21: /* savepoint_opt ::= SAVEPOINT */ - case 37: /* ifnotexists ::= IF NOT EXISTS */ yytestcase(yyruleno==37); - case 90: /* gen_always ::= GENERATED ALWAYS */ yytestcase(yyruleno==90); - case 93: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==93); - case 115: /* tconscomma ::= COMMA */ yytestcase(yyruleno==115); - case 137: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==137); - case 317: /* not_opt ::= NOT */ yytestcase(yyruleno==317); - case 333: /* uniqueflag ::= UNIQUE */ yytestcase(yyruleno==333); - case 401: /* database_kw_opt ::= DATABASE */ yytestcase(yyruleno==401); - case 417: /* kwcolumn_opt ::= */ yytestcase(yyruleno==417); -{yygotominor.yy611 = new bool(true);} + case 41: /* ifnotexists ::= IF NOT EXISTS */ yytestcase(yyruleno==41); + case 94: /* gen_always ::= GENERATED ALWAYS */ yytestcase(yyruleno==94); + case 97: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==97); + case 119: /* tconscomma ::= COMMA */ yytestcase(yyruleno==119); + case 141: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==141); + case 328: /* not_opt ::= NOT */ yytestcase(yyruleno==328); + case 344: /* uniqueflag ::= UNIQUE */ yytestcase(yyruleno==344); + case 412: /* database_kw_opt ::= DATABASE */ yytestcase(yyruleno==412); + case 429: /* kwcolumn_opt ::= */ yytestcase(yyruleno==429); +{yygotominor.yy225 = new bool(true);} break; case 22: /* savepoint_opt ::= */ - case 36: /* ifnotexists ::= */ yytestcase(yyruleno==36); - case 91: /* gen_always ::= */ yytestcase(yyruleno==91); - case 92: /* autoinc ::= */ yytestcase(yyruleno==92); - case 116: /* tconscomma ::= */ yytestcase(yyruleno==116); - case 138: /* ifexists ::= */ yytestcase(yyruleno==138); - case 316: /* not_opt ::= */ yytestcase(yyruleno==316); - case 334: /* uniqueflag ::= */ yytestcase(yyruleno==334); - case 402: /* database_kw_opt ::= */ yytestcase(yyruleno==402); - case 418: /* kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==418); -{yygotominor.yy611 = new bool(false);} + case 40: /* ifnotexists ::= */ yytestcase(yyruleno==40); + case 95: /* gen_always ::= */ yytestcase(yyruleno==95); + case 96: /* autoinc ::= */ yytestcase(yyruleno==96); + case 120: /* tconscomma ::= */ yytestcase(yyruleno==120); + case 142: /* ifexists ::= */ yytestcase(yyruleno==142); + case 327: /* not_opt ::= */ yytestcase(yyruleno==327); + case 345: /* uniqueflag ::= */ yytestcase(yyruleno==345); + case 413: /* database_kw_opt ::= */ yytestcase(yyruleno==413); + case 430: /* kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==430); +{yygotominor.yy225 = new bool(false);} break; case 23: /* cmd ::= SAVEPOINT nm */ { - yygotominor.yy363 = new SqliteSavepoint(*(yymsp[0].minor.yy319)); + yygotominor.yy41 = new SqliteSavepoint(*(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; case 24: /* cmd ::= RELEASE savepoint_opt nm */ { - yygotominor.yy363 = new SqliteRelease(*(yymsp[-1].minor.yy611), *(yymsp[0].minor.yy319)); + yygotominor.yy41 = new SqliteRelease(*(yymsp[-1].minor.yy225), *(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; case 25: /* cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ case 26: /* cmd ::= SAVEPOINT ID_TRANS */ yytestcase(yyruleno==26); { - yygotominor.yy363 = new SqliteRollback( - yymsp[-3].minor.yy84->transactionKw, - *(yymsp[-1].minor.yy611), + yygotominor.yy41 = new SqliteRollback( + yymsp[-3].minor.yy512->transactionKw, + *(yymsp[-1].minor.yy225), *(yymsp[0].minor.yy319) ); - delete yymsp[-1].minor.yy611; - delete yymsp[-3].minor.yy84; - objectForTokens = yygotominor.yy363; + delete yymsp[-1].minor.yy225; + delete yymsp[-3].minor.yy512; + objectForTokens = yygotominor.yy41; } break; case 27: /* cmd ::= RELEASE savepoint_opt ID_TRANS */ case 28: /* cmd ::= ROLLBACK trans_opt TO savepoint_opt ID_TRANS */ yytestcase(yyruleno==28); -{ yy_destructor(yypParser,200,&yymsp[-1].minor); +{ yy_destructor(yypParser,204,&yymsp[-1].minor); } break; case 29: /* cmd ::= CREATE temp TABLE ifnotexists fullname LP columnlist conslist_opt RP table_options */ { - yygotominor.yy363 = new SqliteCreateTable( - *(yymsp[-6].minor.yy611), - *(yymsp[-8].minor.yy386), - yymsp[-5].minor.yy440->name1, - yymsp[-5].minor.yy440->name2, - *(yymsp[-3].minor.yy42), - *(yymsp[-2].minor.yy493), - *(yymsp[0].minor.yy319) + yygotominor.yy41 = new SqliteCreateTable( + *(yymsp[-6].minor.yy225), + *(yymsp[-8].minor.yy130), + yymsp[-5].minor.yy396->name1, + yymsp[-5].minor.yy396->name2, + *(yymsp[-3].minor.yy390), + *(yymsp[-2].minor.yy115), + *(yymsp[0].minor.yy455) ); - delete yymsp[-6].minor.yy611; - delete yymsp[-8].minor.yy386; - delete yymsp[-3].minor.yy42; - delete yymsp[-2].minor.yy493; - delete yymsp[-5].minor.yy440; - delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy363; + delete yymsp[-6].minor.yy225; + delete yymsp[-8].minor.yy130; + delete yymsp[-3].minor.yy390; + delete yymsp[-2].minor.yy115; + delete yymsp[-5].minor.yy396; + delete yymsp[0].minor.yy455; + objectForTokens = yygotominor.yy41; } break; case 30: /* cmd ::= CREATE temp TABLE ifnotexists fullname AS select */ { - yygotominor.yy363 = new SqliteCreateTable( - *(yymsp[-3].minor.yy611), - *(yymsp[-5].minor.yy386), - yymsp[-2].minor.yy440->name1, - yymsp[-2].minor.yy440->name2, - yymsp[0].minor.yy313 + yygotominor.yy41 = new SqliteCreateTable( + *(yymsp[-3].minor.yy225), + *(yymsp[-5].minor.yy130), + yymsp[-2].minor.yy396->name1, + yymsp[-2].minor.yy396->name2, + yymsp[0].minor.yy297 ); - delete yymsp[-3].minor.yy611; - delete yymsp[-5].minor.yy386; - delete yymsp[-2].minor.yy440; - objectForTokens = yygotominor.yy363; + delete yymsp[-3].minor.yy225; + delete yymsp[-5].minor.yy130; + delete yymsp[-2].minor.yy396; + objectForTokens = yygotominor.yy41; } break; case 31: /* cmd ::= CREATE temp TABLE ifnotexists nm DOT ID_TAB_NEW */ - case 140: /* cmd ::= CREATE temp VIEW ifnotexists nm DOT ID_VIEW_NEW */ yytestcase(yyruleno==140); - case 372: /* cmd ::= CREATE temp TRIGGER ifnotexists nm DOT ID_TRIG_NEW */ yytestcase(yyruleno==372); -{ yy_destructor(yypParser,201,&yymsp[-5].minor); - yy_destructor(yypParser,199,&yymsp[-2].minor); + case 144: /* cmd ::= CREATE temp VIEW ifnotexists nm DOT ID_VIEW_NEW */ yytestcase(yyruleno==144); + case 383: /* cmd ::= CREATE temp TRIGGER ifnotexists nm DOT ID_TRIG_NEW */ yytestcase(yyruleno==383); +{ yy_destructor(yypParser,205,&yymsp[-5].minor); + yy_destructor(yypParser,203,&yymsp[-2].minor); } break; case 32: /* cmd ::= CREATE temp TABLE ifnotexists ID_DB|ID_TAB_NEW */ - case 141: /* cmd ::= CREATE temp VIEW ifnotexists ID_DB|ID_VIEW_NEW */ yytestcase(yyruleno==141); - case 373: /* cmd ::= CREATE temp TRIGGER ifnotexists ID_DB|ID_TRIG_NEW */ yytestcase(yyruleno==373); -{ yy_destructor(yypParser,201,&yymsp[-3].minor); + case 145: /* cmd ::= CREATE temp VIEW ifnotexists ID_DB|ID_VIEW_NEW */ yytestcase(yyruleno==145); + case 384: /* cmd ::= CREATE temp TRIGGER ifnotexists ID_DB|ID_TRIG_NEW */ yytestcase(yyruleno==384); +{ yy_destructor(yypParser,205,&yymsp[-3].minor); } break; case 33: /* table_options ::= */ - case 194: /* dbnm ::= */ yytestcase(yyruleno==194); - case 341: /* collate ::= */ yytestcase(yyruleno==341); - case 426: /* vtabarg ::= */ yytestcase(yyruleno==426); - case 430: /* anylist ::= */ yytestcase(yyruleno==430); -{yygotominor.yy319 = new QString();} +{yygotominor.yy455 = new ParserCreateTableOptionList();} break; - case 34: /* table_options ::= WITHOUT nm */ - case 35: /* table_options ::= WITHOUT CTX_ROWID_KW */ yytestcase(yyruleno==35); + case 34: /* table_options ::= table_option */ +{ + yygotominor.yy455 = new ParserCreateTableOptionList(); + yygotominor.yy455->append(yymsp[0].minor.yy629); + } + break; + case 35: /* table_options ::= table_options COMMA table_option */ +{ + yymsp[-2].minor.yy455->append(yymsp[0].minor.yy629); + yygotominor.yy455 = yymsp[-2].minor.yy455; + DONT_INHERIT_TOKENS("table_options"); + } + break; + case 36: /* table_option ::= WITHOUT nm */ { if (yymsp[0].minor.yy319->toLower() != "rowid") parserContext->errorAtToken(QString("Invalid table option: %1").arg(*(yymsp[0].minor.yy319))); - yygotominor.yy319 = yymsp[0].minor.yy319; + yygotominor.yy629 = new ParserStubCreateTableOption(ParserStubCreateTableOption::WITHOUT_ROWID); + delete yymsp[0].minor.yy319; } break; - case 38: /* temp ::= TEMP */ -{yygotominor.yy386 = new int( (yymsp[0].minor.yy0->value.length() > 4) ? 2 : 1 );} + case 37: /* table_option ::= nm */ + case 38: /* table_option ::= WITHOUT CTX_ROWID_KW */ yytestcase(yyruleno==38); + case 39: /* table_option ::= CTX_STRICT_KW */ yytestcase(yyruleno==39); +{ + if (yymsp[0].minor.yy319->toLower() != "strict") + parserContext->errorAtToken(QString("Invalid table option: %1").arg(*(yymsp[0].minor.yy319))); + + yygotominor.yy629 = new ParserStubCreateTableOption(ParserStubCreateTableOption::STRICT); + delete yymsp[0].minor.yy319; + } + break; + case 42: /* temp ::= TEMP */ +{yygotominor.yy130 = new int( (yymsp[0].minor.yy0->value.length() > 4) ? 2 : 1 );} break; - case 39: /* temp ::= */ - case 162: /* distinct ::= */ yytestcase(yyruleno==162); -{yygotominor.yy386 = new int(0);} + case 43: /* temp ::= */ + case 166: /* distinct ::= */ yytestcase(yyruleno==166); +{yygotominor.yy130 = new int(0);} break; - case 40: /* columnlist ::= columnlist COMMA column */ + case 44: /* columnlist ::= columnlist COMMA column */ { - yymsp[-2].minor.yy42->append(yymsp[0].minor.yy147); - yygotominor.yy42 = yymsp[-2].minor.yy42; + yymsp[-2].minor.yy390->append(yymsp[0].minor.yy3); + yygotominor.yy390 = yymsp[-2].minor.yy390; DONT_INHERIT_TOKENS("columnlist"); } break; - case 41: /* columnlist ::= column */ + case 45: /* columnlist ::= column */ { - yygotominor.yy42 = new ParserCreateTableColumnList(); - yygotominor.yy42->append(yymsp[0].minor.yy147); + yygotominor.yy390 = new ParserCreateTableColumnList(); + yygotominor.yy390->append(yymsp[0].minor.yy3); } break; - case 42: /* column ::= columnid type carglist */ + case 46: /* column ::= columnid type carglist */ { - yygotominor.yy147 = new SqliteCreateTable::Column(*(yymsp[-2].minor.yy319), yymsp[-1].minor.yy57, *(yymsp[0].minor.yy51)); + yygotominor.yy3 = new SqliteCreateTable::Column(*(yymsp[-2].minor.yy319), yymsp[-1].minor.yy267, *(yymsp[0].minor.yy323)); delete yymsp[-2].minor.yy319; - delete yymsp[0].minor.yy51; - objectForTokens = yygotominor.yy147; + delete yymsp[0].minor.yy323; + objectForTokens = yygotominor.yy3; } break; - case 43: /* columnid ::= nm */ - case 44: /* columnid ::= ID_COL_NEW */ yytestcase(yyruleno==44); - case 49: /* nm ::= id */ yytestcase(yyruleno==49); - case 57: /* typename ::= ids */ yytestcase(yyruleno==57); - case 195: /* dbnm ::= DOT nm */ yytestcase(yyruleno==195); - case 342: /* collate ::= COLLATE ids */ yytestcase(yyruleno==342); - case 343: /* collate ::= COLLATE ID_COLLATE */ yytestcase(yyruleno==343); + case 47: /* columnid ::= nm */ + case 48: /* columnid ::= ID_COL_NEW */ yytestcase(yyruleno==48); + case 53: /* nm ::= id */ yytestcase(yyruleno==53); + case 61: /* typename ::= ids */ yytestcase(yyruleno==61); + case 199: /* dbnm ::= DOT nm */ yytestcase(yyruleno==199); + case 353: /* collate ::= COLLATE ids */ yytestcase(yyruleno==353); + case 354: /* collate ::= COLLATE ID_COLLATE */ yytestcase(yyruleno==354); {yygotominor.yy319 = yymsp[0].minor.yy319;} break; - case 45: /* id ::= ID */ + case 49: /* id ::= ID */ { yygotominor.yy319 = new QString( stripObjName( @@ -3384,2006 +3428,2096 @@ static void yy_reduce( ); } break; - case 46: /* id_opt ::= id */ + case 50: /* id_opt ::= id */ { yygotominor.yy319 = yymsp[0].minor.yy319; } break; - case 47: /* id_opt ::= */ + case 51: /* id_opt ::= */ { yygotominor.yy319 = new QString(); } break; - case 48: /* ids ::= ID|STRING */ - case 51: /* nm ::= JOIN_KW */ yytestcase(yyruleno==51); + case 52: /* ids ::= ID|STRING */ + case 55: /* nm ::= JOIN_KW */ yytestcase(yyruleno==55); {yygotominor.yy319 = new QString(yymsp[0].minor.yy0->value);} break; - case 50: /* nm ::= STRING */ + case 54: /* nm ::= STRING */ {yygotominor.yy319 = new QString(stripString(yymsp[0].minor.yy0->value));} break; - case 52: /* type ::= */ -{yygotominor.yy57 = nullptr;} + case 56: /* type ::= */ +{yygotominor.yy267 = nullptr;} break; - case 53: /* type ::= typetoken */ -{yygotominor.yy57 = yymsp[0].minor.yy57;} + case 57: /* type ::= typetoken */ +{yygotominor.yy267 = yymsp[0].minor.yy267;} break; - case 54: /* typetoken ::= typename */ + case 58: /* typetoken ::= typename */ { - yygotominor.yy57 = new SqliteColumnType(*(yymsp[0].minor.yy319)); + yygotominor.yy267 = new SqliteColumnType(*(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy57; + objectForTokens = yygotominor.yy267; } break; - case 55: /* typetoken ::= typename LP signed RP */ + case 59: /* typetoken ::= typename LP signed RP */ { - yygotominor.yy57 = new SqliteColumnType(*(yymsp[-3].minor.yy319), *(yymsp[-1].minor.yy229)); + yygotominor.yy267 = new SqliteColumnType(*(yymsp[-3].minor.yy319), *(yymsp[-1].minor.yy393)); delete yymsp[-3].minor.yy319; - delete yymsp[-1].minor.yy229; - objectForTokens = yygotominor.yy57; + delete yymsp[-1].minor.yy393; + objectForTokens = yygotominor.yy267; } break; - case 56: /* typetoken ::= typename LP signed COMMA signed RP */ + case 60: /* typetoken ::= typename LP signed COMMA signed RP */ { - yygotominor.yy57 = new SqliteColumnType(*(yymsp[-5].minor.yy319), *(yymsp[-3].minor.yy229), *(yymsp[-1].minor.yy229)); + yygotominor.yy267 = new SqliteColumnType(*(yymsp[-5].minor.yy319), *(yymsp[-3].minor.yy393), *(yymsp[-1].minor.yy393)); delete yymsp[-5].minor.yy319; - delete yymsp[-3].minor.yy229; - delete yymsp[-1].minor.yy229; - objectForTokens = yygotominor.yy57; + delete yymsp[-3].minor.yy393; + delete yymsp[-1].minor.yy393; + objectForTokens = yygotominor.yy267; } break; - case 58: /* typename ::= typename ids */ - case 59: /* typename ::= ID_COL_TYPE */ yytestcase(yyruleno==59); + case 62: /* typename ::= typename ids */ + case 63: /* typename ::= ID_COL_TYPE */ yytestcase(yyruleno==63); { yymsp[-1].minor.yy319->append(" " + *(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; yygotominor.yy319 = yymsp[-1].minor.yy319; } break; - case 60: /* signed ::= plus_num */ - case 61: /* signed ::= minus_num */ yytestcase(yyruleno==61); - case 358: /* nmnum ::= plus_num */ yytestcase(yyruleno==358); - case 363: /* plus_num ::= PLUS number */ yytestcase(yyruleno==363); - case 364: /* plus_num ::= number */ yytestcase(yyruleno==364); -{yygotominor.yy229 = yymsp[0].minor.yy229;} + case 64: /* signed ::= plus_num */ + case 65: /* signed ::= minus_num */ yytestcase(yyruleno==65); + case 369: /* nmnum ::= plus_num */ yytestcase(yyruleno==369); + case 374: /* plus_num ::= PLUS number */ yytestcase(yyruleno==374); + case 375: /* plus_num ::= number */ yytestcase(yyruleno==375); +{yygotominor.yy393 = yymsp[0].minor.yy393;} break; - case 62: /* carglist ::= carglist ccons */ + case 66: /* carglist ::= carglist ccons */ { - yymsp[-1].minor.yy51->append(yymsp[0].minor.yy464); - yygotominor.yy51 = yymsp[-1].minor.yy51; + yymsp[-1].minor.yy323->append(yymsp[0].minor.yy448); + yygotominor.yy323 = yymsp[-1].minor.yy323; DONT_INHERIT_TOKENS("carglist"); } break; - case 63: /* carglist ::= */ -{yygotominor.yy51 = new ParserCreateTableColumnConstraintList();} + case 67: /* carglist ::= */ +{yygotominor.yy323 = new ParserCreateTableColumnConstraintList();} break; - case 64: /* ccons ::= CONSTRAINT nm */ + case 68: /* ccons ::= CONSTRAINT nm */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initDefNameOnly(*(yymsp[0].minor.yy319)); + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initDefNameOnly(*(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy464; + objectForTokens = yygotominor.yy448; } break; - case 65: /* ccons ::= DEFAULT term */ + case 69: /* ccons ::= DEFAULT term */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initDefTerm(*(yymsp[0].minor.yy229)); - delete yymsp[0].minor.yy229; - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initDefTerm(*(yymsp[0].minor.yy393)); + delete yymsp[0].minor.yy393; + objectForTokens = yygotominor.yy448; } break; - case 66: /* ccons ::= DEFAULT LP expr RP */ + case 70: /* ccons ::= DEFAULT LP expr RP */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initDefExpr(yymsp[-1].minor.yy512); - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initDefExpr(yymsp[-1].minor.yy186); + objectForTokens = yygotominor.yy448; } break; - case 67: /* ccons ::= DEFAULT PLUS term */ + case 71: /* ccons ::= DEFAULT PLUS term */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initDefTerm(*(yymsp[0].minor.yy229), false); - delete yymsp[0].minor.yy229; - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initDefTerm(*(yymsp[0].minor.yy393), false); + delete yymsp[0].minor.yy393; + objectForTokens = yygotominor.yy448; } break; - case 68: /* ccons ::= DEFAULT MINUS term */ + case 72: /* ccons ::= DEFAULT MINUS term */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initDefTerm(*(yymsp[0].minor.yy229), true); - delete yymsp[0].minor.yy229; - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initDefTerm(*(yymsp[0].minor.yy393), true); + delete yymsp[0].minor.yy393; + objectForTokens = yygotominor.yy448; } break; - case 69: /* ccons ::= DEFAULT id */ + case 73: /* ccons ::= DEFAULT id */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initDefId(*(yymsp[0].minor.yy319)); + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initDefId(*(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy464; + objectForTokens = yygotominor.yy448; } break; - case 70: /* ccons ::= DEFAULT CTIME_KW */ + case 74: /* ccons ::= DEFAULT CTIME_KW */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initDefCTime(yymsp[0].minor.yy0->value); - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initDefCTime(yymsp[0].minor.yy0->value); + objectForTokens = yygotominor.yy448; } break; - case 71: /* ccons ::= NULL onconf */ + case 75: /* ccons ::= NULL onconf */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initNull(*(yymsp[0].minor.yy418)); - delete yymsp[0].minor.yy418; - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initNull(*(yymsp[0].minor.yy136)); + delete yymsp[0].minor.yy136; + objectForTokens = yygotominor.yy448; } break; - case 72: /* ccons ::= NOT NULL onconf */ + case 76: /* ccons ::= NOT NULL onconf */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initNotNull(*(yymsp[0].minor.yy418)); - delete yymsp[0].minor.yy418; - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initNotNull(*(yymsp[0].minor.yy136)); + delete yymsp[0].minor.yy136; + objectForTokens = yygotominor.yy448; } break; - case 73: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ + case 77: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initPk(*(yymsp[-2].minor.yy549), *(yymsp[-1].minor.yy418), *(yymsp[0].minor.yy611)); - delete yymsp[-2].minor.yy549; - delete yymsp[0].minor.yy611; - delete yymsp[-1].minor.yy418; - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initPk(*(yymsp[-2].minor.yy35), *(yymsp[-1].minor.yy136), *(yymsp[0].minor.yy225)); + delete yymsp[-2].minor.yy35; + delete yymsp[0].minor.yy225; + delete yymsp[-1].minor.yy136; + objectForTokens = yygotominor.yy448; } break; - case 74: /* ccons ::= UNIQUE onconf */ + case 78: /* ccons ::= UNIQUE onconf */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initUnique(*(yymsp[0].minor.yy418)); - delete yymsp[0].minor.yy418; - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initUnique(*(yymsp[0].minor.yy136)); + delete yymsp[0].minor.yy136; + objectForTokens = yygotominor.yy448; } break; - case 75: /* ccons ::= CHECK LP expr RP */ + case 79: /* ccons ::= CHECK LP expr RP */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initCheck(yymsp[-1].minor.yy512); - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initCheck(yymsp[-1].minor.yy186); + objectForTokens = yygotominor.yy448; } break; - case 76: /* ccons ::= REFERENCES nm idxlist_opt refargs */ + case 80: /* ccons ::= REFERENCES nm idxlist_opt refargs */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initFk(*(yymsp[-2].minor.yy319), *(yymsp[-1].minor.yy223), *(yymsp[0].minor.yy584)); + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initFk(*(yymsp[-2].minor.yy319), *(yymsp[-1].minor.yy627), *(yymsp[0].minor.yy156)); delete yymsp[-2].minor.yy319; - delete yymsp[0].minor.yy584; - delete yymsp[-1].minor.yy223; - objectForTokens = yygotominor.yy464; + delete yymsp[0].minor.yy156; + delete yymsp[-1].minor.yy627; + objectForTokens = yygotominor.yy448; } break; - case 77: /* ccons ::= defer_subclause */ + case 81: /* ccons ::= defer_subclause */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initDefer(yymsp[0].minor.yy9->initially, yymsp[0].minor.yy9->deferrable); - delete yymsp[0].minor.yy9; - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initDefer(yymsp[0].minor.yy53->initially, yymsp[0].minor.yy53->deferrable); + delete yymsp[0].minor.yy53; + objectForTokens = yygotominor.yy448; } break; - case 78: /* ccons ::= COLLATE ids */ + case 82: /* ccons ::= COLLATE ids */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initColl(*(yymsp[0].minor.yy319)); + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initColl(*(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy464; + objectForTokens = yygotominor.yy448; } break; - case 79: /* ccons ::= gen_always AS LP expr RP id_opt */ - case 80: /* ccons ::= CONSTRAINT ID_CONSTR */ yytestcase(yyruleno==80); - case 81: /* ccons ::= COLLATE ID_COLLATE */ yytestcase(yyruleno==81); - case 82: /* ccons ::= REFERENCES ID_TAB */ yytestcase(yyruleno==82); + case 83: /* ccons ::= gen_always AS LP expr RP id_opt */ + case 84: /* ccons ::= CONSTRAINT ID_CONSTR */ yytestcase(yyruleno==84); + case 85: /* ccons ::= COLLATE ID_COLLATE */ yytestcase(yyruleno==85); + case 86: /* ccons ::= REFERENCES ID_TAB */ yytestcase(yyruleno==86); { if (!yymsp[0].minor.yy319->isNull() && yymsp[0].minor.yy319->toLower() != "stored" && yymsp[0].minor.yy319->toLower() != "virtual") parserContext->errorAtToken(QString("Invalid generated column type: %1").arg(*(yymsp[0].minor.yy319))); - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initGeneratedAs(yymsp[-2].minor.yy512, *(yymsp[-5].minor.yy611), *(yymsp[0].minor.yy319)); - delete yymsp[-5].minor.yy611; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initGeneratedAs(yymsp[-2].minor.yy186, *(yymsp[-5].minor.yy225), *(yymsp[0].minor.yy319)); + delete yymsp[-5].minor.yy225; delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy464; + objectForTokens = yygotominor.yy448; } break; - case 83: /* ccons ::= CHECK LP RP */ + case 87: /* ccons ::= CHECK LP RP */ { - yygotominor.yy464 = new SqliteCreateTable::Column::Constraint(); - yygotominor.yy464->initCheck(); - objectForTokens = yygotominor.yy464; + yygotominor.yy448 = new SqliteCreateTable::Column::Constraint(); + yygotominor.yy448->initCheck(); + objectForTokens = yygotominor.yy448; parserContext->minorErrorAfterLastToken("Syntax error"); } break; - case 84: /* term ::= NULL */ -{yygotominor.yy229 = new QVariant();} + case 88: /* term ::= NULL */ +{yygotominor.yy393 = new QVariant();} break; - case 85: /* term ::= INTEGER */ - case 366: /* number ::= INTEGER */ yytestcase(yyruleno==366); -{yygotominor.yy229 = parserContext->handleNumberToken(yymsp[0].minor.yy0->value);} + case 89: /* term ::= INTEGER */ + case 377: /* number ::= INTEGER */ yytestcase(yyruleno==377); +{yygotominor.yy393 = parserContext->handleNumberToken(yymsp[0].minor.yy0->value);} break; - case 86: /* term ::= FLOAT */ - case 367: /* number ::= FLOAT */ yytestcase(yyruleno==367); -{yygotominor.yy229 = new QVariant(QVariant(yymsp[0].minor.yy0->value).toDouble());} + case 90: /* term ::= FLOAT */ + case 378: /* number ::= FLOAT */ yytestcase(yyruleno==378); +{yygotominor.yy393 = new QVariant(QVariant(yymsp[0].minor.yy0->value).toDouble());} break; - case 87: /* term ::= STRING|BLOB */ -{yygotominor.yy229 = new QVariant(stripString(yymsp[0].minor.yy0->value));} + case 91: /* term ::= STRING|BLOB */ +{ + if (yymsp[0].minor.yy0->value.length() >= 3 && yymsp[0].minor.yy0->value.startsWith("x'", Qt::CaseInsensitive)) + yygotominor.yy393 = new QVariant(blobFromLiteral(yymsp[0].minor.yy0->value)); + else + yygotominor.yy393 = new QVariant(stripString(yymsp[0].minor.yy0->value)); + } break; - case 88: /* tnm ::= term */ + case 92: /* tnm ::= term */ { - yygotominor.yy590 = new ParserTermOrLiteral(*(yymsp[0].minor.yy229)); - delete yymsp[0].minor.yy229; + yygotominor.yy380 = new ParserTermOrLiteral(*(yymsp[0].minor.yy393)); + delete yymsp[0].minor.yy393; } break; - case 89: /* tnm ::= nm */ + case 93: /* tnm ::= nm */ { - yygotominor.yy590 = new ParserTermOrLiteral(*(yymsp[0].minor.yy319)); + yygotominor.yy380 = new ParserTermOrLiteral(*(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; } break; - case 94: /* refargs ::= */ -{yygotominor.yy584 = new ParserFkConditionList();} + case 98: /* refargs ::= */ +{yygotominor.yy156 = new ParserFkConditionList();} break; - case 95: /* refargs ::= refargs refarg */ + case 99: /* refargs ::= refargs refarg */ { - yymsp[-1].minor.yy584->append(yymsp[0].minor.yy507); - yygotominor.yy584 = yymsp[-1].minor.yy584; + yymsp[-1].minor.yy156->append(yymsp[0].minor.yy205); + yygotominor.yy156 = yymsp[-1].minor.yy156; DONT_INHERIT_TOKENS("refargs"); } break; - case 96: /* refarg ::= MATCH nm */ + case 100: /* refarg ::= MATCH nm */ { - yygotominor.yy507 = new SqliteForeignKey::Condition(*(yymsp[0].minor.yy319)); + yygotominor.yy205 = new SqliteForeignKey::Condition(*(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; } break; - case 97: /* refarg ::= ON INSERT refact */ -{yygotominor.yy507 = new SqliteForeignKey::Condition(SqliteForeignKey::Condition::INSERT, *(yymsp[0].minor.yy104)); delete yymsp[0].minor.yy104;} + case 101: /* refarg ::= ON INSERT refact */ +{yygotominor.yy205 = new SqliteForeignKey::Condition(SqliteForeignKey::Condition::INSERT, *(yymsp[0].minor.yy106)); delete yymsp[0].minor.yy106;} break; - case 98: /* refarg ::= ON DELETE refact */ -{yygotominor.yy507 = new SqliteForeignKey::Condition(SqliteForeignKey::Condition::DELETE, *(yymsp[0].minor.yy104)); delete yymsp[0].minor.yy104;} + case 102: /* refarg ::= ON DELETE refact */ +{yygotominor.yy205 = new SqliteForeignKey::Condition(SqliteForeignKey::Condition::DELETE, *(yymsp[0].minor.yy106)); delete yymsp[0].minor.yy106;} break; - case 99: /* refarg ::= ON UPDATE refact */ - case 100: /* refarg ::= MATCH ID_FK_MATCH */ yytestcase(yyruleno==100); -{yygotominor.yy507 = new SqliteForeignKey::Condition(SqliteForeignKey::Condition::UPDATE, *(yymsp[0].minor.yy104)); delete yymsp[0].minor.yy104;} + case 103: /* refarg ::= ON UPDATE refact */ + case 104: /* refarg ::= MATCH ID_FK_MATCH */ yytestcase(yyruleno==104); +{yygotominor.yy205 = new SqliteForeignKey::Condition(SqliteForeignKey::Condition::UPDATE, *(yymsp[0].minor.yy106)); delete yymsp[0].minor.yy106;} break; - case 101: /* refact ::= SET NULL */ -{yygotominor.yy104 = new SqliteForeignKey::Condition::Reaction(SqliteForeignKey::Condition::SET_NULL);} + case 105: /* refact ::= SET NULL */ +{yygotominor.yy106 = new SqliteForeignKey::Condition::Reaction(SqliteForeignKey::Condition::SET_NULL);} break; - case 102: /* refact ::= SET DEFAULT */ -{yygotominor.yy104 = new SqliteForeignKey::Condition::Reaction(SqliteForeignKey::Condition::SET_DEFAULT);} + case 106: /* refact ::= SET DEFAULT */ +{yygotominor.yy106 = new SqliteForeignKey::Condition::Reaction(SqliteForeignKey::Condition::SET_DEFAULT);} break; - case 103: /* refact ::= CASCADE */ -{yygotominor.yy104 = new SqliteForeignKey::Condition::Reaction(SqliteForeignKey::Condition::CASCADE);} + case 107: /* refact ::= CASCADE */ +{yygotominor.yy106 = new SqliteForeignKey::Condition::Reaction(SqliteForeignKey::Condition::CASCADE);} break; - case 104: /* refact ::= RESTRICT */ -{yygotominor.yy104 = new SqliteForeignKey::Condition::Reaction(SqliteForeignKey::Condition::RESTRICT);} + case 108: /* refact ::= RESTRICT */ +{yygotominor.yy106 = new SqliteForeignKey::Condition::Reaction(SqliteForeignKey::Condition::RESTRICT);} break; - case 105: /* refact ::= NO ACTION */ -{yygotominor.yy104 = new SqliteForeignKey::Condition::Reaction(SqliteForeignKey::Condition::NO_ACTION);} + case 109: /* refact ::= NO ACTION */ +{yygotominor.yy106 = new SqliteForeignKey::Condition::Reaction(SqliteForeignKey::Condition::NO_ACTION);} break; - case 106: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + case 110: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ { - yygotominor.yy9 = new ParserDeferSubClause(SqliteDeferrable::NOT_DEFERRABLE, *(yymsp[0].minor.yy312)); - delete yymsp[0].minor.yy312; + yygotominor.yy53 = new ParserDeferSubClause(SqliteDeferrable::NOT_DEFERRABLE, *(yymsp[0].minor.yy612)); + delete yymsp[0].minor.yy612; } break; - case 107: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + case 111: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ { - yygotominor.yy9 = new ParserDeferSubClause(SqliteDeferrable::DEFERRABLE, *(yymsp[0].minor.yy312)); - delete yymsp[0].minor.yy312; + yygotominor.yy53 = new ParserDeferSubClause(SqliteDeferrable::DEFERRABLE, *(yymsp[0].minor.yy612)); + delete yymsp[0].minor.yy612; } break; - case 108: /* init_deferred_pred_opt ::= */ -{yygotominor.yy312 = new SqliteInitially(SqliteInitially::null);} + case 112: /* init_deferred_pred_opt ::= */ +{yygotominor.yy612 = new SqliteInitially(SqliteInitially::null);} break; - case 109: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ -{yygotominor.yy312 = new SqliteInitially(SqliteInitially::DEFERRED);} + case 113: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ +{yygotominor.yy612 = new SqliteInitially(SqliteInitially::DEFERRED);} break; - case 110: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -{yygotominor.yy312 = new SqliteInitially(SqliteInitially::IMMEDIATE);} + case 114: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ +{yygotominor.yy612 = new SqliteInitially(SqliteInitially::IMMEDIATE);} break; - case 111: /* conslist_opt ::= */ -{yygotominor.yy493 = new ParserCreateTableConstraintList();} + case 115: /* conslist_opt ::= */ +{yygotominor.yy115 = new ParserCreateTableConstraintList();} break; - case 112: /* conslist_opt ::= COMMA conslist */ -{yygotominor.yy493 = yymsp[0].minor.yy493;} + case 116: /* conslist_opt ::= COMMA conslist */ +{yygotominor.yy115 = yymsp[0].minor.yy115;} break; - case 113: /* conslist ::= conslist tconscomma tcons */ + case 117: /* conslist ::= conslist tconscomma tcons */ { - yymsp[0].minor.yy246->afterComma = *(yymsp[-1].minor.yy611); - yymsp[-2].minor.yy493->append(yymsp[0].minor.yy246); - yygotominor.yy493 = yymsp[-2].minor.yy493; - delete yymsp[-1].minor.yy611; + yymsp[0].minor.yy400->afterComma = *(yymsp[-1].minor.yy225); + yymsp[-2].minor.yy115->append(yymsp[0].minor.yy400); + yygotominor.yy115 = yymsp[-2].minor.yy115; + delete yymsp[-1].minor.yy225; DONT_INHERIT_TOKENS("conslist"); } break; - case 114: /* conslist ::= tcons */ + case 118: /* conslist ::= tcons */ { - yygotominor.yy493 = new ParserCreateTableConstraintList(); - yygotominor.yy493->append(yymsp[0].minor.yy246); + yygotominor.yy115 = new ParserCreateTableConstraintList(); + yygotominor.yy115->append(yymsp[0].minor.yy400); } break; - case 117: /* tcons ::= CONSTRAINT nm */ + case 121: /* tcons ::= CONSTRAINT nm */ { - yygotominor.yy246 = new SqliteCreateTable::Constraint(); - yygotominor.yy246->initNameOnly(*(yymsp[0].minor.yy319)); + yygotominor.yy400 = new SqliteCreateTable::Constraint(); + yygotominor.yy400->initNameOnly(*(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy246; + objectForTokens = yygotominor.yy400; } break; - case 118: /* tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf */ + case 122: /* tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf */ { - yygotominor.yy246 = new SqliteCreateTable::Constraint(); - yygotominor.yy246->initPk(*(yymsp[-3].minor.yy223), *(yymsp[-2].minor.yy611), *(yymsp[0].minor.yy418)); - delete yymsp[-2].minor.yy611; - delete yymsp[0].minor.yy418; - delete yymsp[-3].minor.yy223; - objectForTokens = yygotominor.yy246; + yygotominor.yy400 = new SqliteCreateTable::Constraint(); + yygotominor.yy400->initPk(*(yymsp[-3].minor.yy627), *(yymsp[-2].minor.yy225), *(yymsp[0].minor.yy136)); + delete yymsp[-2].minor.yy225; + delete yymsp[0].minor.yy136; + delete yymsp[-3].minor.yy627; + objectForTokens = yygotominor.yy400; } break; - case 119: /* tcons ::= UNIQUE LP idxlist RP onconf */ + case 123: /* tcons ::= UNIQUE LP idxlist RP onconf */ { - yygotominor.yy246 = new SqliteCreateTable::Constraint(); - yygotominor.yy246->initUnique(*(yymsp[-2].minor.yy223), *(yymsp[0].minor.yy418)); - delete yymsp[0].minor.yy418; - delete yymsp[-2].minor.yy223; - objectForTokens = yygotominor.yy246; + yygotominor.yy400 = new SqliteCreateTable::Constraint(); + yygotominor.yy400->initUnique(*(yymsp[-2].minor.yy627), *(yymsp[0].minor.yy136)); + delete yymsp[0].minor.yy136; + delete yymsp[-2].minor.yy627; + objectForTokens = yygotominor.yy400; } break; - case 120: /* tcons ::= CHECK LP expr RP onconf */ + case 124: /* tcons ::= CHECK LP expr RP onconf */ { - yygotominor.yy246 = new SqliteCreateTable::Constraint(); - yygotominor.yy246->initCheck(yymsp[-2].minor.yy512, *(yymsp[0].minor.yy418)); - objectForTokens = yygotominor.yy246; + yygotominor.yy400 = new SqliteCreateTable::Constraint(); + yygotominor.yy400->initCheck(yymsp[-2].minor.yy186, *(yymsp[0].minor.yy136)); + objectForTokens = yygotominor.yy400; } break; - case 121: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt */ - case 122: /* tcons ::= CONSTRAINT ID_CONSTR */ yytestcase(yyruleno==122); - case 123: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES ID_TAB */ yytestcase(yyruleno==123); + case 125: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt */ + case 126: /* tcons ::= CONSTRAINT ID_CONSTR */ yytestcase(yyruleno==126); + case 127: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES ID_TAB */ yytestcase(yyruleno==127); { - yygotominor.yy246 = new SqliteCreateTable::Constraint(); - yygotominor.yy246->initFk( - *(yymsp[-6].minor.yy223), + yygotominor.yy400 = new SqliteCreateTable::Constraint(); + yygotominor.yy400->initFk( + *(yymsp[-6].minor.yy627), *(yymsp[-3].minor.yy319), - *(yymsp[-2].minor.yy223), - *(yymsp[-1].minor.yy584), - yymsp[0].minor.yy9->initially, - yymsp[0].minor.yy9->deferrable + *(yymsp[-2].minor.yy627), + *(yymsp[-1].minor.yy156), + yymsp[0].minor.yy53->initially, + yymsp[0].minor.yy53->deferrable ); delete yymsp[-3].minor.yy319; - delete yymsp[-1].minor.yy584; - delete yymsp[0].minor.yy9; - delete yymsp[-2].minor.yy223; - delete yymsp[-6].minor.yy223; - objectForTokens = yygotominor.yy246; + delete yymsp[-1].minor.yy156; + delete yymsp[0].minor.yy53; + delete yymsp[-2].minor.yy627; + delete yymsp[-6].minor.yy627; + objectForTokens = yygotominor.yy400; } break; - case 124: /* tcons ::= CHECK LP RP onconf */ + case 128: /* tcons ::= CHECK LP RP onconf */ { - yygotominor.yy246 = new SqliteCreateTable::Constraint(); - yygotominor.yy246->initCheck(); - objectForTokens = yygotominor.yy246; + yygotominor.yy400 = new SqliteCreateTable::Constraint(); + yygotominor.yy400->initCheck(); + objectForTokens = yygotominor.yy400; parserContext->minorErrorAfterLastToken("Syntax error"); - yy_destructor(yypParser,223,&yymsp[0].minor); + yy_destructor(yypParser,228,&yymsp[0].minor); } break; - case 125: /* defer_subclause_opt ::= */ -{yygotominor.yy9 = new ParserDeferSubClause(SqliteDeferrable::null, SqliteInitially::null);} - break; - case 126: /* defer_subclause_opt ::= defer_subclause */ -{yygotominor.yy9 = yymsp[0].minor.yy9;} - break; - case 127: /* onconf ::= */ - case 129: /* orconf ::= */ yytestcase(yyruleno==129); -{yygotominor.yy418 = new SqliteConflictAlgo(SqliteConflictAlgo::null);} - break; - case 128: /* onconf ::= ON CONFLICT resolvetype */ - case 130: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==130); -{yygotominor.yy418 = yymsp[0].minor.yy418;} - break; - case 131: /* resolvetype ::= raisetype */ - case 132: /* resolvetype ::= IGNORE */ yytestcase(yyruleno==132); - case 133: /* resolvetype ::= REPLACE */ yytestcase(yyruleno==133); -{yygotominor.yy418 = new SqliteConflictAlgo(sqliteConflictAlgo(yymsp[0].minor.yy0->value));} - break; - case 134: /* cmd ::= DROP TABLE ifexists fullname */ -{ - yygotominor.yy363 = new SqliteDropTable(*(yymsp[-1].minor.yy611), yymsp[0].minor.yy440->name1, yymsp[0].minor.yy440->name2); - delete yymsp[-1].minor.yy611; - delete yymsp[0].minor.yy440; - objectForTokens = yygotominor.yy363; - } - break; - case 135: /* cmd ::= DROP TABLE ifexists nm DOT ID_TAB */ - case 136: /* cmd ::= DROP TABLE ifexists ID_DB|ID_TAB */ yytestcase(yyruleno==136); - case 143: /* cmd ::= DROP VIEW ifexists nm DOT ID_VIEW */ yytestcase(yyruleno==143); - case 144: /* cmd ::= DROP VIEW ifexists ID_DB|ID_VIEW */ yytestcase(yyruleno==144); - case 187: /* singlesrc ::= nm DOT ID_TAB */ yytestcase(yyruleno==187); - case 188: /* singlesrc ::= ID_DB|ID_TAB */ yytestcase(yyruleno==188); - case 189: /* singlesrc ::= nm DOT ID_VIEW */ yytestcase(yyruleno==189); - case 190: /* singlesrc ::= ID_DB|ID_VIEW */ yytestcase(yyruleno==190); - case 331: /* cmd ::= CREATE uniqueflag INDEX ifnotexists nm DOT ID_IDX_NEW */ yytestcase(yyruleno==331); - case 332: /* cmd ::= CREATE uniqueflag INDEX ifnotexists ID_DB|ID_IDX_NEW */ yytestcase(yyruleno==332); - case 345: /* cmd ::= DROP INDEX ifexists nm DOT ID_IDX */ yytestcase(yyruleno==345); - case 346: /* cmd ::= DROP INDEX ifexists ID_DB|ID_IDX */ yytestcase(yyruleno==346); - case 356: /* cmd ::= PRAGMA nm DOT ID_PRAGMA */ yytestcase(yyruleno==356); - case 357: /* cmd ::= PRAGMA ID_DB|ID_PRAGMA */ yytestcase(yyruleno==357); - case 395: /* cmd ::= DROP TRIGGER ifexists nm DOT ID_TRIG */ yytestcase(yyruleno==395); - case 396: /* cmd ::= DROP TRIGGER ifexists ID_DB|ID_TRIG */ yytestcase(yyruleno==396); - case 406: /* cmd ::= REINDEX nm DOT ID_TAB|ID_IDX */ yytestcase(yyruleno==406); - case 407: /* cmd ::= REINDEX ID_DB|ID_IDX|ID_TAB */ yytestcase(yyruleno==407); - case 410: /* cmd ::= ANALYZE nm DOT ID_TAB|ID_IDX */ yytestcase(yyruleno==410); - case 411: /* cmd ::= ANALYZE ID_DB|ID_IDX|ID_TAB */ yytestcase(yyruleno==411); - case 415: /* cmd ::= ALTER TABLE nm DOT ID_TAB */ yytestcase(yyruleno==415); - case 416: /* cmd ::= ALTER TABLE ID_DB|ID_TAB */ yytestcase(yyruleno==416); - case 422: /* create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm DOT ID_TAB_NEW */ yytestcase(yyruleno==422); - case 423: /* create_vtab ::= CREATE VIRTUAL TABLE ifnotexists ID_DB|ID_TAB_NEW */ yytestcase(yyruleno==423); -{ yy_destructor(yypParser,199,&yymsp[-2].minor); + case 129: /* defer_subclause_opt ::= */ +{yygotominor.yy53 = new ParserDeferSubClause(SqliteDeferrable::null, SqliteInitially::null);} + break; + case 130: /* defer_subclause_opt ::= defer_subclause */ +{yygotominor.yy53 = yymsp[0].minor.yy53;} + break; + case 131: /* onconf ::= */ + case 133: /* orconf ::= */ yytestcase(yyruleno==133); +{yygotominor.yy136 = new SqliteConflictAlgo(SqliteConflictAlgo::null);} + break; + case 132: /* onconf ::= ON CONFLICT resolvetype */ + case 134: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==134); +{yygotominor.yy136 = yymsp[0].minor.yy136;} + break; + case 135: /* resolvetype ::= raisetype */ + case 136: /* resolvetype ::= IGNORE */ yytestcase(yyruleno==136); + case 137: /* resolvetype ::= REPLACE */ yytestcase(yyruleno==137); +{yygotominor.yy136 = new SqliteConflictAlgo(sqliteConflictAlgo(yymsp[0].minor.yy0->value));} + break; + case 138: /* cmd ::= DROP TABLE ifexists fullname */ +{ + yygotominor.yy41 = new SqliteDropTable(*(yymsp[-1].minor.yy225), yymsp[0].minor.yy396->name1, yymsp[0].minor.yy396->name2); + delete yymsp[-1].minor.yy225; + delete yymsp[0].minor.yy396; + objectForTokens = yygotominor.yy41; + } + break; + case 139: /* cmd ::= DROP TABLE ifexists nm DOT ID_TAB */ + case 140: /* cmd ::= DROP TABLE ifexists ID_DB|ID_TAB */ yytestcase(yyruleno==140); + case 147: /* cmd ::= DROP VIEW ifexists nm DOT ID_VIEW */ yytestcase(yyruleno==147); + case 148: /* cmd ::= DROP VIEW ifexists ID_DB|ID_VIEW */ yytestcase(yyruleno==148); + case 191: /* singlesrc ::= nm DOT ID_TAB */ yytestcase(yyruleno==191); + case 192: /* singlesrc ::= ID_DB|ID_TAB */ yytestcase(yyruleno==192); + case 193: /* singlesrc ::= nm DOT ID_VIEW */ yytestcase(yyruleno==193); + case 194: /* singlesrc ::= ID_DB|ID_VIEW */ yytestcase(yyruleno==194); + case 342: /* cmd ::= CREATE uniqueflag INDEX ifnotexists nm DOT ID_IDX_NEW */ yytestcase(yyruleno==342); + case 343: /* cmd ::= CREATE uniqueflag INDEX ifnotexists ID_DB|ID_IDX_NEW */ yytestcase(yyruleno==343); + case 356: /* cmd ::= DROP INDEX ifexists nm DOT ID_IDX */ yytestcase(yyruleno==356); + case 357: /* cmd ::= DROP INDEX ifexists ID_DB|ID_IDX */ yytestcase(yyruleno==357); + case 367: /* cmd ::= PRAGMA nm DOT ID_PRAGMA */ yytestcase(yyruleno==367); + case 368: /* cmd ::= PRAGMA ID_DB|ID_PRAGMA */ yytestcase(yyruleno==368); + case 406: /* cmd ::= DROP TRIGGER ifexists nm DOT ID_TRIG */ yytestcase(yyruleno==406); + case 407: /* cmd ::= DROP TRIGGER ifexists ID_DB|ID_TRIG */ yytestcase(yyruleno==407); + case 417: /* cmd ::= REINDEX nm DOT ID_TAB|ID_IDX */ yytestcase(yyruleno==417); + case 418: /* cmd ::= REINDEX ID_DB|ID_IDX|ID_TAB */ yytestcase(yyruleno==418); + case 421: /* cmd ::= ANALYZE nm DOT ID_TAB|ID_IDX */ yytestcase(yyruleno==421); + case 422: /* cmd ::= ANALYZE ID_DB|ID_IDX|ID_TAB */ yytestcase(yyruleno==422); + case 427: /* cmd ::= ALTER TABLE nm DOT ID_TAB */ yytestcase(yyruleno==427); + case 428: /* cmd ::= ALTER TABLE ID_DB|ID_TAB */ yytestcase(yyruleno==428); + case 434: /* create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm DOT ID_TAB_NEW */ yytestcase(yyruleno==434); + case 435: /* create_vtab ::= CREATE VIRTUAL TABLE ifnotexists ID_DB|ID_TAB_NEW */ yytestcase(yyruleno==435); +{ yy_destructor(yypParser,203,&yymsp[-2].minor); } break; - case 139: /* cmd ::= CREATE temp VIEW ifnotexists fullname idxlist_opt AS select */ + case 143: /* cmd ::= CREATE temp VIEW ifnotexists fullname idxlist_opt AS select */ { - yygotominor.yy363 = new SqliteCreateView(*(yymsp[-6].minor.yy386), *(yymsp[-4].minor.yy611), yymsp[-3].minor.yy440->name1, yymsp[-3].minor.yy440->name2, yymsp[0].minor.yy313, *(yymsp[-2].minor.yy223)); - delete yymsp[-6].minor.yy386; - delete yymsp[-4].minor.yy611; - delete yymsp[-3].minor.yy440; - delete yymsp[-2].minor.yy223; - objectForTokens = yygotominor.yy363; + yygotominor.yy41 = new SqliteCreateView(*(yymsp[-6].minor.yy130), *(yymsp[-4].minor.yy225), yymsp[-3].minor.yy396->name1, yymsp[-3].minor.yy396->name2, yymsp[0].minor.yy297, *(yymsp[-2].minor.yy627)); + delete yymsp[-6].minor.yy130; + delete yymsp[-4].minor.yy225; + delete yymsp[-3].minor.yy396; + delete yymsp[-2].minor.yy627; + objectForTokens = yygotominor.yy41; } break; - case 142: /* cmd ::= DROP VIEW ifexists fullname */ + case 146: /* cmd ::= DROP VIEW ifexists fullname */ { - yygotominor.yy363 = new SqliteDropView(*(yymsp[-1].minor.yy611), yymsp[0].minor.yy440->name1, yymsp[0].minor.yy440->name2); - delete yymsp[-1].minor.yy611; - delete yymsp[0].minor.yy440; - objectForTokens = yygotominor.yy363; + yygotominor.yy41 = new SqliteDropView(*(yymsp[-1].minor.yy225), yymsp[0].minor.yy396->name1, yymsp[0].minor.yy396->name2); + delete yymsp[-1].minor.yy225; + delete yymsp[0].minor.yy396; + objectForTokens = yygotominor.yy41; } break; - case 145: /* cmd ::= select_stmt */ - case 226: /* cmd ::= delete_stmt */ yytestcase(yyruleno==226); - case 235: /* cmd ::= update_stmt */ yytestcase(yyruleno==235); - case 256: /* cmd ::= insert_stmt */ yytestcase(yyruleno==256); + case 149: /* cmd ::= select_stmt */ + case 230: /* cmd ::= delete_stmt */ yytestcase(yyruleno==230); + case 241: /* cmd ::= update_stmt */ yytestcase(yyruleno==241); + case 262: /* cmd ::= insert_stmt */ yytestcase(yyruleno==262); { - yygotominor.yy363 = yymsp[0].minor.yy363; - objectForTokens = yygotominor.yy363; + yygotominor.yy41 = yymsp[0].minor.yy41; + objectForTokens = yygotominor.yy41; } break; - case 146: /* select_stmt ::= select */ + case 150: /* select_stmt ::= select */ { - yygotominor.yy363 = yymsp[0].minor.yy313; + yygotominor.yy41 = yymsp[0].minor.yy297; // since it's used in trigger: - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; - case 147: /* select ::= with selectnowith */ + case 151: /* select ::= with selectnowith */ { - yygotominor.yy313 = yymsp[0].minor.yy313; - yymsp[0].minor.yy313->setWith(yymsp[-1].minor.yy1); - objectForTokens = yygotominor.yy313; + yygotominor.yy297 = yymsp[0].minor.yy297; + yymsp[0].minor.yy297->setWith(yymsp[-1].minor.yy161); + objectForTokens = yygotominor.yy297; } break; - case 148: /* selectnowith ::= oneselect */ + case 152: /* selectnowith ::= oneselect */ { - yygotominor.yy313 = SqliteSelect::append(yymsp[0].minor.yy470); - objectForTokens = yygotominor.yy313; + yygotominor.yy297 = SqliteSelect::append(yymsp[0].minor.yy378); + objectForTokens = yygotominor.yy297; } break; - case 149: /* selectnowith ::= selectnowith multiselect_op oneselect */ + case 153: /* selectnowith ::= selectnowith multiselect_op oneselect */ { - yygotominor.yy313 = SqliteSelect::append(yymsp[-2].minor.yy313, *(yymsp[-1].minor.yy382), yymsp[0].minor.yy470); - delete yymsp[-1].minor.yy382; - objectForTokens = yygotominor.yy313; + yygotominor.yy297 = SqliteSelect::append(yymsp[-2].minor.yy297, *(yymsp[-1].minor.yy142), yymsp[0].minor.yy378); + delete yymsp[-1].minor.yy142; + objectForTokens = yygotominor.yy297; } break; - case 150: /* selectnowith ::= values */ + case 154: /* selectnowith ::= values */ { - yygotominor.yy313 = SqliteSelect::append(*(yymsp[0].minor.yy486)); - delete yymsp[0].minor.yy486; - objectForTokens = yygotominor.yy313; + yygotominor.yy297 = SqliteSelect::append(*(yymsp[0].minor.yy522)); + delete yymsp[0].minor.yy522; + objectForTokens = yygotominor.yy297; } break; - case 151: /* selectnowith ::= selectnowith COMMA values */ + case 155: /* selectnowith ::= selectnowith COMMA values */ { - yygotominor.yy313 = SqliteSelect::append(yymsp[-2].minor.yy313, SqliteSelect::CompoundOperator::UNION_ALL, *(yymsp[0].minor.yy486)); - delete yymsp[0].minor.yy486; - objectForTokens = yygotominor.yy313; + yygotominor.yy297 = SqliteSelect::append(yymsp[-2].minor.yy297, SqliteSelect::CompoundOperator::UNION_ALL, *(yymsp[0].minor.yy522)); + delete yymsp[0].minor.yy522; + objectForTokens = yygotominor.yy297; } break; - case 152: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + case 156: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yygotominor.yy470 = new SqliteSelect::Core( - *(yymsp[-7].minor.yy386), - *(yymsp[-6].minor.yy53), - yymsp[-5].minor.yy31, - yymsp[-4].minor.yy512, - *(yymsp[-3].minor.yy71), - yymsp[-2].minor.yy512, - *(yymsp[-1].minor.yy403), - yymsp[0].minor.yy4 + yygotominor.yy378 = new SqliteSelect::Core( + *(yymsp[-7].minor.yy130), + *(yymsp[-6].minor.yy27), + yymsp[-5].minor.yy553, + yymsp[-4].minor.yy186, + *(yymsp[-3].minor.yy615), + yymsp[-2].minor.yy186, + *(yymsp[-1].minor.yy226), + yymsp[0].minor.yy360 ); - delete yymsp[-6].minor.yy53; - delete yymsp[-7].minor.yy386; - delete yymsp[-3].minor.yy71; - delete yymsp[-1].minor.yy403; - objectForTokens = yygotominor.yy470; - } - break; - case 153: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ -{ - yygotominor.yy470 = new SqliteSelect::Core( - *(yymsp[-8].minor.yy386), - *(yymsp[-7].minor.yy53), - yymsp[-6].minor.yy31, - yymsp[-5].minor.yy512, - *(yymsp[-4].minor.yy71), - yymsp[-3].minor.yy512, - *(yymsp[-2].minor.yy299), - *(yymsp[-1].minor.yy403), - yymsp[0].minor.yy4 + delete yymsp[-6].minor.yy27; + delete yymsp[-7].minor.yy130; + delete yymsp[-3].minor.yy615; + delete yymsp[-1].minor.yy226; + objectForTokens = yygotominor.yy378; + } + break; + case 157: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ +{ + yygotominor.yy378 = new SqliteSelect::Core( + *(yymsp[-8].minor.yy130), + *(yymsp[-7].minor.yy27), + yymsp[-6].minor.yy553, + yymsp[-5].minor.yy186, + *(yymsp[-4].minor.yy615), + yymsp[-3].minor.yy186, + *(yymsp[-2].minor.yy525), + *(yymsp[-1].minor.yy226), + yymsp[0].minor.yy360 ); - delete yymsp[-7].minor.yy53; - delete yymsp[-8].minor.yy386; - delete yymsp[-4].minor.yy71; - delete yymsp[-1].minor.yy403; - delete yymsp[-2].minor.yy299; - objectForTokens = yygotominor.yy470; + delete yymsp[-7].minor.yy27; + delete yymsp[-8].minor.yy130; + delete yymsp[-4].minor.yy615; + delete yymsp[-1].minor.yy226; + delete yymsp[-2].minor.yy525; + objectForTokens = yygotominor.yy378; } break; - case 154: /* values ::= VALUES LP nexprlist RP */ + case 158: /* values ::= VALUES LP nexprlist RP */ { - yygotominor.yy486 = new ParserExprNestedList(); - yygotominor.yy486->append(*(yymsp[-1].minor.yy71)); - delete yymsp[-1].minor.yy71; + yygotominor.yy522 = new ParserExprNestedList(); + yygotominor.yy522->append(*(yymsp[-1].minor.yy615)); + delete yymsp[-1].minor.yy615; } break; - case 155: /* values ::= values COMMA LP exprlist RP */ + case 159: /* values ::= values COMMA LP exprlist RP */ { - yymsp[-4].minor.yy486->append(*(yymsp[-1].minor.yy71)); - yygotominor.yy486 = yymsp[-4].minor.yy486; - delete yymsp[-1].minor.yy71; + yymsp[-4].minor.yy522->append(*(yymsp[-1].minor.yy615)); + yygotominor.yy522 = yymsp[-4].minor.yy522; + delete yymsp[-1].minor.yy615; DONT_INHERIT_TOKENS("values"); } break; - case 156: /* multiselect_op ::= UNION */ -{yygotominor.yy382 = new SqliteSelect::CompoundOperator(SqliteSelect::CompoundOperator::UNION);} + case 160: /* multiselect_op ::= UNION */ +{yygotominor.yy142 = new SqliteSelect::CompoundOperator(SqliteSelect::CompoundOperator::UNION);} break; - case 157: /* multiselect_op ::= UNION ALL */ -{yygotominor.yy382 = new SqliteSelect::CompoundOperator(SqliteSelect::CompoundOperator::UNION_ALL);} + case 161: /* multiselect_op ::= UNION ALL */ +{yygotominor.yy142 = new SqliteSelect::CompoundOperator(SqliteSelect::CompoundOperator::UNION_ALL);} break; - case 158: /* multiselect_op ::= EXCEPT */ -{yygotominor.yy382 = new SqliteSelect::CompoundOperator(SqliteSelect::CompoundOperator::EXCEPT);} + case 162: /* multiselect_op ::= EXCEPT */ +{yygotominor.yy142 = new SqliteSelect::CompoundOperator(SqliteSelect::CompoundOperator::EXCEPT);} break; - case 159: /* multiselect_op ::= INTERSECT */ -{yygotominor.yy382 = new SqliteSelect::CompoundOperator(SqliteSelect::CompoundOperator::INTERSECT);} + case 163: /* multiselect_op ::= INTERSECT */ +{yygotominor.yy142 = new SqliteSelect::CompoundOperator(SqliteSelect::CompoundOperator::INTERSECT);} break; - case 160: /* distinct ::= DISTINCT */ -{yygotominor.yy386 = new int(1);} + case 164: /* distinct ::= DISTINCT */ +{yygotominor.yy130 = new int(1);} break; - case 161: /* distinct ::= ALL */ -{yygotominor.yy386 = new int(2);} + case 165: /* distinct ::= ALL */ +{yygotominor.yy130 = new int(2);} break; - case 163: /* sclp ::= selcollist COMMA */ -{yygotominor.yy53 = yymsp[-1].minor.yy53;} + case 167: /* sclp ::= selcollist COMMA */ +{yygotominor.yy27 = yymsp[-1].minor.yy27;} break; - case 164: /* sclp ::= */ -{yygotominor.yy53 = new ParserResultColumnList();} + case 168: /* sclp ::= */ + case 239: /* returning ::= */ yytestcase(yyruleno==239); +{yygotominor.yy27 = new ParserResultColumnList();} break; - case 165: /* selcollist ::= sclp expr as */ + case 169: /* selcollist ::= sclp expr as */ { SqliteSelect::Core::ResultColumn* obj = new SqliteSelect::Core::ResultColumn( - yymsp[-1].minor.yy512, - yymsp[0].minor.yy200 ? yymsp[0].minor.yy200->asKw : false, - yymsp[0].minor.yy200 ? yymsp[0].minor.yy200->name : QString() + yymsp[-1].minor.yy186, + yymsp[0].minor.yy628 ? yymsp[0].minor.yy628->asKw : false, + yymsp[0].minor.yy628 ? yymsp[0].minor.yy628->name : QString() ); - yymsp[-2].minor.yy53->append(obj); - yygotominor.yy53 = yymsp[-2].minor.yy53; - delete yymsp[0].minor.yy200; + yymsp[-2].minor.yy27->append(obj); + yygotominor.yy27 = yymsp[-2].minor.yy27; + delete yymsp[0].minor.yy628; objectForTokens = obj; DONT_INHERIT_TOKENS("sclp"); } break; - case 166: /* selcollist ::= sclp STAR */ + case 170: /* selcollist ::= sclp STAR */ { SqliteSelect::Core::ResultColumn* obj = new SqliteSelect::Core::ResultColumn(true); - yymsp[-1].minor.yy53->append(obj); - yygotominor.yy53 = yymsp[-1].minor.yy53; + yymsp[-1].minor.yy27->append(obj); + yygotominor.yy27 = yymsp[-1].minor.yy27; objectForTokens = obj; DONT_INHERIT_TOKENS("sclp"); } break; - case 167: /* selcollist ::= sclp tnm DOT STAR */ + case 171: /* selcollist ::= sclp tnm DOT STAR */ { SqliteSelect::Core::ResultColumn* obj = new SqliteSelect::Core::ResultColumn( true, - yymsp[-2].minor.yy590->toName() + yymsp[-2].minor.yy380->toName() ); - if (!yymsp[-2].minor.yy590->isName()) + if (!yymsp[-2].minor.yy380->isName()) parserContext->errorAtToken("Syntax error ", -3); - yymsp[-3].minor.yy53->append(obj); - yygotominor.yy53 = yymsp[-3].minor.yy53; - delete yymsp[-2].minor.yy590; + yymsp[-3].minor.yy27->append(obj); + yygotominor.yy27 = yymsp[-3].minor.yy27; + delete yymsp[-2].minor.yy380; objectForTokens = obj; DONT_INHERIT_TOKENS("sclp"); } break; - case 168: /* selcollist ::= sclp */ - case 169: /* selcollist ::= sclp ID_TAB DOT STAR */ yytestcase(yyruleno==169); + case 172: /* selcollist ::= sclp */ + case 173: /* selcollist ::= sclp ID_TAB DOT STAR */ yytestcase(yyruleno==173); { parserContext->minorErrorBeforeNextToken("Syntax error"); - yygotominor.yy53 = yymsp[0].minor.yy53; + yygotominor.yy27 = yymsp[0].minor.yy27; } break; - case 170: /* as ::= AS nm */ + case 174: /* as ::= AS nm */ { - yygotominor.yy200 = new ParserStubAlias(*(yymsp[0].minor.yy319), true); + yygotominor.yy628 = new ParserStubAlias(*(yymsp[0].minor.yy319), true); delete yymsp[0].minor.yy319; } break; - case 171: /* as ::= ids */ - case 172: /* as ::= AS ID_ALIAS */ yytestcase(yyruleno==172); - case 173: /* as ::= ID_ALIAS */ yytestcase(yyruleno==173); + case 175: /* as ::= ids */ + case 176: /* as ::= AS ID_ALIAS */ yytestcase(yyruleno==176); + case 177: /* as ::= ID_ALIAS */ yytestcase(yyruleno==177); { - yygotominor.yy200 = new ParserStubAlias(*(yymsp[0].minor.yy319), false); + yygotominor.yy628 = new ParserStubAlias(*(yymsp[0].minor.yy319), false); delete yymsp[0].minor.yy319; } break; - case 174: /* as ::= */ -{yygotominor.yy200 = nullptr;} + case 178: /* as ::= */ +{yygotominor.yy628 = nullptr;} break; - case 175: /* from ::= */ -{yygotominor.yy31 = nullptr;} + case 179: /* from ::= */ +{yygotominor.yy553 = nullptr;} break; - case 176: /* from ::= FROM joinsrc */ -{yygotominor.yy31 = yymsp[0].minor.yy31;} + case 180: /* from ::= FROM joinsrc */ +{yygotominor.yy553 = yymsp[0].minor.yy553;} break; - case 177: /* joinsrc ::= singlesrc seltablist */ + case 181: /* joinsrc ::= singlesrc seltablist */ { - yygotominor.yy31 = new SqliteSelect::Core::JoinSource( - yymsp[-1].minor.yy441, - *(yymsp[0].minor.yy451) + yygotominor.yy553 = new SqliteSelect::Core::JoinSource( + yymsp[-1].minor.yy595, + *(yymsp[0].minor.yy107) ); - delete yymsp[0].minor.yy451; - objectForTokens = yygotominor.yy31; + delete yymsp[0].minor.yy107; + objectForTokens = yygotominor.yy553; } break; - case 178: /* joinsrc ::= */ + case 182: /* joinsrc ::= */ { parserContext->minorErrorBeforeNextToken("Syntax error"); - yygotominor.yy31 = new SqliteSelect::Core::JoinSource(); - objectForTokens = yygotominor.yy31; + yygotominor.yy553 = new SqliteSelect::Core::JoinSource(); + objectForTokens = yygotominor.yy553; } break; - case 179: /* seltablist ::= seltablist joinop singlesrc joinconstr_opt */ + case 183: /* seltablist ::= seltablist joinop singlesrc joinconstr_opt */ { SqliteSelect::Core::JoinSourceOther* src = - new SqliteSelect::Core::JoinSourceOther(yymsp[-2].minor.yy221, yymsp[-1].minor.yy441, yymsp[0].minor.yy295); + new SqliteSelect::Core::JoinSourceOther(yymsp[-2].minor.yy449, yymsp[-1].minor.yy595, yymsp[0].minor.yy215); - yymsp[-3].minor.yy451->append(src); - yygotominor.yy451 = yymsp[-3].minor.yy451; + yymsp[-3].minor.yy107->append(src); + yygotominor.yy107 = yymsp[-3].minor.yy107; objectForTokens = src; DONT_INHERIT_TOKENS("seltablist"); } break; - case 180: /* seltablist ::= */ + case 184: /* seltablist ::= */ { - yygotominor.yy451 = new ParserOtherSourceList(); + yygotominor.yy107 = new ParserOtherSourceList(); } break; - case 181: /* singlesrc ::= nm dbnm as indexed_opt */ + case 185: /* singlesrc ::= nm dbnm as indexed_opt */ { - yygotominor.yy441 = new SqliteSelect::Core::SingleSource( + yygotominor.yy595 = new SqliteSelect::Core::SingleSource( *(yymsp[-3].minor.yy319), *(yymsp[-2].minor.yy319), - yymsp[-1].minor.yy200 ? yymsp[-1].minor.yy200->asKw : false, - yymsp[-1].minor.yy200 ? yymsp[-1].minor.yy200->name : QString(), - yymsp[0].minor.yy592 ? yymsp[0].minor.yy592->notIndexedKw : false, - yymsp[0].minor.yy592 ? yymsp[0].minor.yy592->indexedBy : QString() + yymsp[-1].minor.yy628 ? yymsp[-1].minor.yy628->asKw : false, + yymsp[-1].minor.yy628 ? yymsp[-1].minor.yy628->name : QString(), + yymsp[0].minor.yy300 ? yymsp[0].minor.yy300->notIndexedKw : false, + yymsp[0].minor.yy300 ? yymsp[0].minor.yy300->indexedBy : QString() ); delete yymsp[-3].minor.yy319; delete yymsp[-2].minor.yy319; - delete yymsp[-1].minor.yy200; - if (yymsp[0].minor.yy592) - delete yymsp[0].minor.yy592; - objectForTokens = yygotominor.yy441; + delete yymsp[-1].minor.yy628; + if (yymsp[0].minor.yy300) + delete yymsp[0].minor.yy300; + objectForTokens = yygotominor.yy595; } break; - case 182: /* singlesrc ::= LP select RP as */ + case 186: /* singlesrc ::= LP select RP as */ { - yygotominor.yy441 = new SqliteSelect::Core::SingleSource( - yymsp[-2].minor.yy313, - yymsp[0].minor.yy200 ? yymsp[0].minor.yy200->asKw : false, - yymsp[0].minor.yy200 ? yymsp[0].minor.yy200->name : QString() + yygotominor.yy595 = new SqliteSelect::Core::SingleSource( + yymsp[-2].minor.yy297, + yymsp[0].minor.yy628 ? yymsp[0].minor.yy628->asKw : false, + yymsp[0].minor.yy628 ? yymsp[0].minor.yy628->name : QString() ); - delete yymsp[0].minor.yy200; - objectForTokens = yygotominor.yy441; + delete yymsp[0].minor.yy628; + objectForTokens = yygotominor.yy595; } break; - case 183: /* singlesrc ::= LP joinsrc RP as */ + case 187: /* singlesrc ::= LP joinsrc RP as */ { - yygotominor.yy441 = new SqliteSelect::Core::SingleSource( - yymsp[-2].minor.yy31, - yymsp[0].minor.yy200 ? yymsp[0].minor.yy200->asKw : false, - yymsp[0].minor.yy200 ? yymsp[0].minor.yy200->name : QString() + yygotominor.yy595 = new SqliteSelect::Core::SingleSource( + yymsp[-2].minor.yy553, + yymsp[0].minor.yy628 ? yymsp[0].minor.yy628->asKw : false, + yymsp[0].minor.yy628 ? yymsp[0].minor.yy628->name : QString() ); - delete yymsp[0].minor.yy200; - objectForTokens = yygotominor.yy441; + delete yymsp[0].minor.yy628; + objectForTokens = yygotominor.yy595; } break; - case 184: /* singlesrc ::= nm dbnm LP exprlist RP as */ + case 188: /* singlesrc ::= nm dbnm LP exprlist RP as */ { - yygotominor.yy441 = new SqliteSelect::Core::SingleSource( + yygotominor.yy595 = new SqliteSelect::Core::SingleSource( *(yymsp[-5].minor.yy319), *(yymsp[-4].minor.yy319), - yymsp[0].minor.yy200 ? yymsp[0].minor.yy200->asKw : false, - yymsp[0].minor.yy200 ? yymsp[0].minor.yy200->name : QString(), - *(yymsp[-2].minor.yy71) + yymsp[0].minor.yy628 ? yymsp[0].minor.yy628->asKw : false, + yymsp[0].minor.yy628 ? yymsp[0].minor.yy628->name : QString(), + *(yymsp[-2].minor.yy615) ); delete yymsp[-5].minor.yy319; delete yymsp[-4].minor.yy319; - delete yymsp[0].minor.yy200; - if (yymsp[-2].minor.yy71) - delete yymsp[-2].minor.yy71; + delete yymsp[0].minor.yy628; + if (yymsp[-2].minor.yy615) + delete yymsp[-2].minor.yy615; - objectForTokens = yygotominor.yy441; + objectForTokens = yygotominor.yy595; } break; - case 185: /* singlesrc ::= */ + case 189: /* singlesrc ::= */ { parserContext->minorErrorBeforeNextToken("Syntax error"); - yygotominor.yy441 = new SqliteSelect::Core::SingleSource(); - objectForTokens = yygotominor.yy441; + yygotominor.yy595 = new SqliteSelect::Core::SingleSource(); + objectForTokens = yygotominor.yy595; } break; - case 186: /* singlesrc ::= nm DOT */ + case 190: /* singlesrc ::= nm DOT */ { parserContext->minorErrorBeforeNextToken("Syntax error"); - yygotominor.yy441 = new SqliteSelect::Core::SingleSource(); - yygotominor.yy441->database = *(yymsp[-1].minor.yy319); + yygotominor.yy595 = new SqliteSelect::Core::SingleSource(); + yygotominor.yy595->database = *(yymsp[-1].minor.yy319); delete yymsp[-1].minor.yy319; - objectForTokens = yygotominor.yy441; + objectForTokens = yygotominor.yy595; } break; - case 191: /* joinconstr_opt ::= ON expr */ + case 195: /* joinconstr_opt ::= ON expr */ { - yygotominor.yy295 = new SqliteSelect::Core::JoinConstraint(yymsp[0].minor.yy512); - objectForTokens = yygotominor.yy295; + yygotominor.yy215 = new SqliteSelect::Core::JoinConstraint(yymsp[0].minor.yy186); + objectForTokens = yygotominor.yy215; } break; - case 192: /* joinconstr_opt ::= USING LP idlist RP */ + case 196: /* joinconstr_opt ::= USING LP idlist RP */ { - yygotominor.yy295 = new SqliteSelect::Core::JoinConstraint(*(yymsp[-1].minor.yy575)); - delete yymsp[-1].minor.yy575; - objectForTokens = yygotominor.yy295; + yygotominor.yy215 = new SqliteSelect::Core::JoinConstraint(*(yymsp[-1].minor.yy173)); + delete yymsp[-1].minor.yy173; + objectForTokens = yygotominor.yy215; } break; - case 193: /* joinconstr_opt ::= */ -{yygotominor.yy295 = nullptr;} + case 197: /* joinconstr_opt ::= */ +{yygotominor.yy215 = nullptr;} + break; + case 198: /* dbnm ::= */ + case 352: /* collate ::= */ yytestcase(yyruleno==352); + case 438: /* vtabarg ::= */ yytestcase(yyruleno==438); + case 442: /* anylist ::= */ yytestcase(yyruleno==442); +{yygotominor.yy319 = new QString();} break; - case 196: /* fullname ::= nm dbnm */ + case 200: /* fullname ::= nm dbnm */ { - yygotominor.yy440 = new ParserFullName(); - yygotominor.yy440->name1 = *(yymsp[-1].minor.yy319); - yygotominor.yy440->name2 = *(yymsp[0].minor.yy319); + yygotominor.yy396 = new ParserFullName(); + yygotominor.yy396->name1 = *(yymsp[-1].minor.yy319); + yygotominor.yy396->name2 = *(yymsp[0].minor.yy319); delete yymsp[-1].minor.yy319; delete yymsp[0].minor.yy319; } break; - case 197: /* joinop ::= COMMA */ + case 201: /* joinop ::= COMMA */ { - yygotominor.yy221 = new SqliteSelect::Core::JoinOp(true); - objectForTokens = yygotominor.yy221; + yygotominor.yy449 = new SqliteSelect::Core::JoinOp(true); + objectForTokens = yygotominor.yy449; } break; - case 198: /* joinop ::= JOIN */ + case 202: /* joinop ::= JOIN */ { - yygotominor.yy221 = new SqliteSelect::Core::JoinOp(false); - objectForTokens = yygotominor.yy221; + yygotominor.yy449 = new SqliteSelect::Core::JoinOp(false); + objectForTokens = yygotominor.yy449; } break; - case 199: /* joinop ::= JOIN_KW JOIN */ + case 203: /* joinop ::= JOIN_KW JOIN */ { - yygotominor.yy221 = new SqliteSelect::Core::JoinOp(yymsp[-1].minor.yy0->value); - objectForTokens = yygotominor.yy221; + yygotominor.yy449 = new SqliteSelect::Core::JoinOp(yymsp[-1].minor.yy0->value); + objectForTokens = yygotominor.yy449; } break; - case 200: /* joinop ::= JOIN_KW nm JOIN */ + case 204: /* joinop ::= JOIN_KW nm JOIN */ { - yygotominor.yy221 = new SqliteSelect::Core::JoinOp(yymsp[-2].minor.yy0->value, *(yymsp[-1].minor.yy319)); + yygotominor.yy449 = new SqliteSelect::Core::JoinOp(yymsp[-2].minor.yy0->value, *(yymsp[-1].minor.yy319)); delete yymsp[-1].minor.yy319; - objectForTokens = yygotominor.yy221; + objectForTokens = yygotominor.yy449; } break; - case 201: /* joinop ::= JOIN_KW nm nm JOIN */ - case 202: /* joinop ::= ID_JOIN_OPTS */ yytestcase(yyruleno==202); + case 205: /* joinop ::= JOIN_KW nm nm JOIN */ + case 206: /* joinop ::= ID_JOIN_OPTS */ yytestcase(yyruleno==206); { - yygotominor.yy221 = new SqliteSelect::Core::JoinOp(yymsp[-3].minor.yy0->value, *(yymsp[-2].minor.yy319), *(yymsp[-1].minor.yy319)); + yygotominor.yy449 = new SqliteSelect::Core::JoinOp(yymsp[-3].minor.yy0->value, *(yymsp[-2].minor.yy319), *(yymsp[-1].minor.yy319)); delete yymsp[-2].minor.yy319; - objectForTokens = yygotominor.yy221; + objectForTokens = yygotominor.yy449; } break; - case 203: /* indexed_opt ::= */ -{yygotominor.yy592 = nullptr;} + case 207: /* indexed_opt ::= */ +{yygotominor.yy300 = nullptr;} break; - case 204: /* indexed_opt ::= INDEXED BY nm */ + case 208: /* indexed_opt ::= INDEXED BY nm */ { - yygotominor.yy592 = new ParserIndexedBy(*(yymsp[0].minor.yy319)); + yygotominor.yy300 = new ParserIndexedBy(*(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; } break; - case 205: /* indexed_opt ::= NOT INDEXED */ - case 206: /* indexed_opt ::= INDEXED BY ID_IDX */ yytestcase(yyruleno==206); -{yygotominor.yy592 = new ParserIndexedBy(true);} + case 209: /* indexed_opt ::= NOT INDEXED */ + case 210: /* indexed_opt ::= INDEXED BY ID_IDX */ yytestcase(yyruleno==210); +{yygotominor.yy300 = new ParserIndexedBy(true);} break; - case 207: /* orderby_opt ::= */ -{yygotominor.yy403 = new ParserOrderByList();} + case 211: /* orderby_opt ::= */ +{yygotominor.yy226 = new ParserOrderByList();} break; - case 208: /* orderby_opt ::= ORDER BY sortlist */ -{yygotominor.yy403 = yymsp[0].minor.yy403;} + case 212: /* orderby_opt ::= ORDER BY sortlist */ +{yygotominor.yy226 = yymsp[0].minor.yy226;} break; - case 209: /* sortlist ::= sortlist COMMA expr sortorder nulls */ + case 213: /* sortlist ::= sortlist COMMA expr sortorder nulls */ { - SqliteOrderBy* obj = new SqliteOrderBy(yymsp[-2].minor.yy512, *(yymsp[-1].minor.yy549), *(yymsp[0].minor.yy579)); - yymsp[-4].minor.yy403->append(obj); - yygotominor.yy403 = yymsp[-4].minor.yy403; - delete yymsp[-1].minor.yy549; - delete yymsp[0].minor.yy579; + SqliteOrderBy* obj = new SqliteOrderBy(yymsp[-2].minor.yy186, *(yymsp[-1].minor.yy35), *(yymsp[0].minor.yy315)); + yymsp[-4].minor.yy226->append(obj); + yygotominor.yy226 = yymsp[-4].minor.yy226; + delete yymsp[-1].minor.yy35; + delete yymsp[0].minor.yy315; objectForTokens = obj; DONT_INHERIT_TOKENS("sortlist"); } break; - case 210: /* sortlist ::= expr sortorder nulls */ + case 214: /* sortlist ::= expr sortorder nulls */ { - SqliteOrderBy* obj = new SqliteOrderBy(yymsp[-2].minor.yy512, *(yymsp[-1].minor.yy549), *(yymsp[0].minor.yy579)); - yygotominor.yy403 = new ParserOrderByList(); - yygotominor.yy403->append(obj); - delete yymsp[-1].minor.yy549; - delete yymsp[0].minor.yy579; + SqliteOrderBy* obj = new SqliteOrderBy(yymsp[-2].minor.yy186, *(yymsp[-1].minor.yy35), *(yymsp[0].minor.yy315)); + yygotominor.yy226 = new ParserOrderByList(); + yygotominor.yy226->append(obj); + delete yymsp[-1].minor.yy35; + delete yymsp[0].minor.yy315; objectForTokens = obj; } break; - case 211: /* sortorder ::= ASC */ -{yygotominor.yy549 = new SqliteSortOrder(SqliteSortOrder::ASC);} + case 215: /* sortorder ::= ASC */ +{yygotominor.yy35 = new SqliteSortOrder(SqliteSortOrder::ASC);} break; - case 212: /* sortorder ::= DESC */ -{yygotominor.yy549 = new SqliteSortOrder(SqliteSortOrder::DESC);} + case 216: /* sortorder ::= DESC */ +{yygotominor.yy35 = new SqliteSortOrder(SqliteSortOrder::DESC);} break; - case 213: /* sortorder ::= */ -{yygotominor.yy549 = new SqliteSortOrder(SqliteSortOrder::null);} + case 217: /* sortorder ::= */ +{yygotominor.yy35 = new SqliteSortOrder(SqliteSortOrder::null);} break; - case 214: /* nulls ::= NULLS FIRST */ -{yygotominor.yy579 = new SqliteNulls(SqliteNulls::FIRST);} + case 218: /* nulls ::= NULLS FIRST */ +{yygotominor.yy315 = new SqliteNulls(SqliteNulls::FIRST);} break; - case 215: /* nulls ::= NULLS LAST */ -{yygotominor.yy579 = new SqliteNulls(SqliteNulls::LAST);} + case 219: /* nulls ::= NULLS LAST */ +{yygotominor.yy315 = new SqliteNulls(SqliteNulls::LAST);} break; - case 216: /* nulls ::= */ -{yygotominor.yy579 = new SqliteNulls(SqliteNulls::null);} + case 220: /* nulls ::= */ +{yygotominor.yy315 = new SqliteNulls(SqliteNulls::null);} break; - case 217: /* groupby_opt ::= */ - case 326: /* exprlist ::= */ yytestcase(yyruleno==326); -{yygotominor.yy71 = new ParserExprList();} + case 221: /* groupby_opt ::= */ + case 337: /* exprlist ::= */ yytestcase(yyruleno==337); +{yygotominor.yy615 = new ParserExprList();} break; - case 218: /* groupby_opt ::= GROUP BY nexprlist */ - case 325: /* exprlist ::= nexprlist */ yytestcase(yyruleno==325); -{yygotominor.yy71 = yymsp[0].minor.yy71;} + case 222: /* groupby_opt ::= GROUP BY nexprlist */ + case 336: /* exprlist ::= nexprlist */ yytestcase(yyruleno==336); +{yygotominor.yy615 = yymsp[0].minor.yy615;} break; - case 219: /* groupby_opt ::= GROUP BY */ + case 223: /* groupby_opt ::= GROUP BY */ { parserContext->minorErrorBeforeNextToken("Syntax error"); - yygotominor.yy71 = new ParserExprList(); + yygotominor.yy615 = new ParserExprList(); } break; - case 220: /* having_opt ::= */ - case 232: /* where_opt ::= */ yytestcase(yyruleno==232); - case 322: /* case_else ::= */ yytestcase(yyruleno==322); - case 324: /* case_operand ::= */ yytestcase(yyruleno==324); - case 350: /* vinto ::= */ yytestcase(yyruleno==350); - case 384: /* when_clause ::= */ yytestcase(yyruleno==384); - case 399: /* key_opt ::= */ yytestcase(yyruleno==399); -{yygotominor.yy512 = nullptr;} + case 224: /* having_opt ::= */ + case 236: /* where_opt ::= */ yytestcase(yyruleno==236); + case 333: /* case_else ::= */ yytestcase(yyruleno==333); + case 335: /* case_operand ::= */ yytestcase(yyruleno==335); + case 361: /* vinto ::= */ yytestcase(yyruleno==361); + case 395: /* when_clause ::= */ yytestcase(yyruleno==395); + case 410: /* key_opt ::= */ yytestcase(yyruleno==410); +{yygotominor.yy186 = nullptr;} break; - case 221: /* having_opt ::= HAVING expr */ - case 233: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==233); - case 315: /* expr ::= exprx */ yytestcase(yyruleno==315); - case 321: /* case_else ::= ELSE expr */ yytestcase(yyruleno==321); - case 323: /* case_operand ::= exprx */ yytestcase(yyruleno==323); - case 349: /* vinto ::= INTO expr */ yytestcase(yyruleno==349); - case 385: /* when_clause ::= WHEN expr */ yytestcase(yyruleno==385); - case 400: /* key_opt ::= KEY expr */ yytestcase(yyruleno==400); -{yygotominor.yy512 = yymsp[0].minor.yy512;} + case 225: /* having_opt ::= HAVING expr */ + case 237: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==237); + case 326: /* expr ::= exprx */ yytestcase(yyruleno==326); + case 332: /* case_else ::= ELSE expr */ yytestcase(yyruleno==332); + case 334: /* case_operand ::= exprx */ yytestcase(yyruleno==334); + case 360: /* vinto ::= INTO expr */ yytestcase(yyruleno==360); + case 396: /* when_clause ::= WHEN expr */ yytestcase(yyruleno==396); + case 411: /* key_opt ::= KEY expr */ yytestcase(yyruleno==411); +{yygotominor.yy186 = yymsp[0].minor.yy186;} break; - case 222: /* limit_opt ::= */ -{yygotominor.yy4 = nullptr;} + case 226: /* limit_opt ::= */ +{yygotominor.yy360 = nullptr;} break; - case 223: /* limit_opt ::= LIMIT expr */ + case 227: /* limit_opt ::= LIMIT expr */ { - yygotominor.yy4 = new SqliteLimit(yymsp[0].minor.yy512); - objectForTokens = yygotominor.yy4; + yygotominor.yy360 = new SqliteLimit(yymsp[0].minor.yy186); + objectForTokens = yygotominor.yy360; } break; - case 224: /* limit_opt ::= LIMIT expr OFFSET expr */ + case 228: /* limit_opt ::= LIMIT expr OFFSET expr */ { - yygotominor.yy4 = new SqliteLimit(yymsp[-2].minor.yy512, yymsp[0].minor.yy512, true); - objectForTokens = yygotominor.yy4; + yygotominor.yy360 = new SqliteLimit(yymsp[-2].minor.yy186, yymsp[0].minor.yy186, true); + objectForTokens = yygotominor.yy360; } break; - case 225: /* limit_opt ::= LIMIT expr COMMA expr */ + case 229: /* limit_opt ::= LIMIT expr COMMA expr */ { - yygotominor.yy4 = new SqliteLimit(yymsp[-2].minor.yy512, yymsp[0].minor.yy512, false); - objectForTokens = yygotominor.yy4; + yygotominor.yy360 = new SqliteLimit(yymsp[-2].minor.yy186, yymsp[0].minor.yy186, false); + objectForTokens = yygotominor.yy360; } break; - case 227: /* delete_stmt ::= with DELETE FROM fullname indexed_opt where_opt */ + case 231: /* delete_stmt ::= with DELETE FROM fullname indexed_opt where_opt returning */ { - if (yymsp[-1].minor.yy592) + if (yymsp[-2].minor.yy300) { - if (!yymsp[-1].minor.yy592->indexedBy.isNull()) + if (!yymsp[-2].minor.yy300->indexedBy.isNull()) { - yygotominor.yy363 = new SqliteDelete( - yymsp[-2].minor.yy440->name1, - yymsp[-2].minor.yy440->name2, - yymsp[-1].minor.yy592->indexedBy, - yymsp[0].minor.yy512, - yymsp[-5].minor.yy1 + yygotominor.yy41 = new SqliteDelete( + yymsp[-3].minor.yy396->name1, + yymsp[-3].minor.yy396->name2, + yymsp[-2].minor.yy300->indexedBy, + yymsp[-1].minor.yy186, + yymsp[-6].minor.yy161, + *(yymsp[0].minor.yy27) ); } else { - yygotominor.yy363 = new SqliteDelete( - yymsp[-2].minor.yy440->name1, - yymsp[-2].minor.yy440->name2, - yymsp[-1].minor.yy592->notIndexedKw, - yymsp[0].minor.yy512, - yymsp[-5].minor.yy1 + yygotominor.yy41 = new SqliteDelete( + yymsp[-3].minor.yy396->name1, + yymsp[-3].minor.yy396->name2, + yymsp[-2].minor.yy300->notIndexedKw, + yymsp[-1].minor.yy186, + yymsp[-6].minor.yy161, + *(yymsp[0].minor.yy27) ); } - delete yymsp[-1].minor.yy592; + delete yymsp[-2].minor.yy300; } else { - yygotominor.yy363 = new SqliteDelete( - yymsp[-2].minor.yy440->name1, - yymsp[-2].minor.yy440->name2, + yygotominor.yy41 = new SqliteDelete( + yymsp[-3].minor.yy396->name1, + yymsp[-3].minor.yy396->name2, false, - yymsp[0].minor.yy512, - yymsp[-5].minor.yy1 + yymsp[-1].minor.yy186, + yymsp[-6].minor.yy161, + *(yymsp[0].minor.yy27) ); } - delete yymsp[-2].minor.yy440; + delete yymsp[-3].minor.yy396; + delete yymsp[0].minor.yy27; // since it's used in trigger: - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; - case 228: /* delete_stmt ::= with DELETE FROM */ + case 232: /* delete_stmt ::= with DELETE FROM */ { parserContext->minorErrorBeforeNextToken("Syntax error"); SqliteDelete* q = new SqliteDelete(); - q->with = yymsp[-2].minor.yy1; - yygotominor.yy363 = q; - objectForTokens = yygotominor.yy363; + q->with = yymsp[-2].minor.yy161; + yygotominor.yy41 = q; + objectForTokens = yygotominor.yy41; } break; - case 229: /* delete_stmt ::= with DELETE FROM nm DOT */ + case 233: /* delete_stmt ::= with DELETE FROM nm DOT */ { parserContext->minorErrorBeforeNextToken("Syntax error"); SqliteDelete* q = new SqliteDelete(); - q->with = yymsp[-4].minor.yy1; + q->with = yymsp[-4].minor.yy161; q->database = *(yymsp[-1].minor.yy319); - yygotominor.yy363 = q; - objectForTokens = yygotominor.yy363; + yygotominor.yy41 = q; + objectForTokens = yygotominor.yy41; delete yymsp[-1].minor.yy319; } break; - case 230: /* delete_stmt ::= with DELETE FROM nm DOT ID_TAB */ - case 239: /* update_stmt ::= with UPDATE orconf nm DOT ID_TAB */ yytestcase(yyruleno==239); -{ yy_destructor(yypParser,244,&yymsp[-5].minor); - yy_destructor(yypParser,199,&yymsp[-2].minor); + case 234: /* delete_stmt ::= with DELETE FROM nm DOT ID_TAB */ + case 245: /* update_stmt ::= with UPDATE orconf nm DOT ID_TAB */ yytestcase(yyruleno==245); +{ yy_destructor(yypParser,249,&yymsp[-5].minor); + yy_destructor(yypParser,203,&yymsp[-2].minor); } break; - case 231: /* delete_stmt ::= with DELETE FROM ID_DB|ID_TAB */ - case 240: /* update_stmt ::= with UPDATE orconf ID_DB|ID_TAB */ yytestcase(yyruleno==240); -{ yy_destructor(yypParser,244,&yymsp[-3].minor); + case 235: /* delete_stmt ::= with DELETE FROM ID_DB|ID_TAB */ + case 246: /* update_stmt ::= with UPDATE orconf ID_DB|ID_TAB */ yytestcase(yyruleno==246); +{ yy_destructor(yypParser,249,&yymsp[-3].minor); } break; - case 234: /* where_opt ::= WHERE */ + case 238: /* where_opt ::= WHERE */ { parserContext->minorErrorBeforeNextToken("Syntax error"); - yygotominor.yy512 = new SqliteExpr(); + yygotominor.yy186 = new SqliteExpr(); } break; - case 236: /* update_stmt ::= with UPDATE orconf fullname indexed_opt SET setlist from where_opt */ + case 240: /* returning ::= RETURNING selcollist */ +{yygotominor.yy27 = yymsp[0].minor.yy27;} + break; + case 242: /* update_stmt ::= with UPDATE orconf fullname indexed_opt SET setlist from where_opt returning */ { - yygotominor.yy363 = new SqliteUpdate( - *(yymsp[-6].minor.yy418), - yymsp[-5].minor.yy440->name1, - yymsp[-5].minor.yy440->name2, - yymsp[-4].minor.yy592 ? yymsp[-4].minor.yy592->notIndexedKw : false, - yymsp[-4].minor.yy592 ? yymsp[-4].minor.yy592->indexedBy : QString(), - *(yymsp[-2].minor.yy201), - yymsp[-1].minor.yy31, - yymsp[0].minor.yy512, - yymsp[-8].minor.yy1 + yygotominor.yy41 = new SqliteUpdate( + *(yymsp[-7].minor.yy136), + yymsp[-6].minor.yy396->name1, + yymsp[-6].minor.yy396->name2, + yymsp[-5].minor.yy300 ? yymsp[-5].minor.yy300->notIndexedKw : false, + yymsp[-5].minor.yy300 ? yymsp[-5].minor.yy300->indexedBy : QString(), + *(yymsp[-3].minor.yy621), + yymsp[-2].minor.yy553, + yymsp[-1].minor.yy186, + yymsp[-9].minor.yy161, + *(yymsp[0].minor.yy27) ); - delete yymsp[-6].minor.yy418; - delete yymsp[-5].minor.yy440; - delete yymsp[-2].minor.yy201; - if (yymsp[-4].minor.yy592) - delete yymsp[-4].minor.yy592; + delete yymsp[-7].minor.yy136; + delete yymsp[-6].minor.yy396; + delete yymsp[-3].minor.yy621; + delete yymsp[0].minor.yy27; + if (yymsp[-5].minor.yy300) + delete yymsp[-5].minor.yy300; // since it's used in trigger: - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; - case 237: /* update_stmt ::= with UPDATE orconf */ + case 243: /* update_stmt ::= with UPDATE orconf */ { parserContext->minorErrorBeforeNextToken("Syntax error"); SqliteUpdate* q = new SqliteUpdate(); - q->with = yymsp[-2].minor.yy1; - yygotominor.yy363 = q; - objectForTokens = yygotominor.yy363; - delete yymsp[0].minor.yy418; + q->with = yymsp[-2].minor.yy161; + yygotominor.yy41 = q; + objectForTokens = yygotominor.yy41; + delete yymsp[0].minor.yy136; } break; - case 238: /* update_stmt ::= with UPDATE orconf nm DOT */ + case 244: /* update_stmt ::= with UPDATE orconf nm DOT */ { parserContext->minorErrorBeforeNextToken("Syntax error"); SqliteUpdate* q = new SqliteUpdate(); - q->with = yymsp[-4].minor.yy1; + q->with = yymsp[-4].minor.yy161; q->database = *(yymsp[-1].minor.yy319); - yygotominor.yy363 = q; - objectForTokens = yygotominor.yy363; - delete yymsp[-2].minor.yy418; + yygotominor.yy41 = q; + objectForTokens = yygotominor.yy41; + delete yymsp[-2].minor.yy136; delete yymsp[-1].minor.yy319; } break; - case 241: /* setlist ::= setlist COMMA nm EQ expr */ + case 247: /* setlist ::= setlist COMMA nm EQ expr */ { - yymsp[-4].minor.yy201->append(ParserSetValue(*(yymsp[-2].minor.yy319), yymsp[0].minor.yy512)); - yygotominor.yy201 = yymsp[-4].minor.yy201; + yymsp[-4].minor.yy621->append(ParserSetValue(*(yymsp[-2].minor.yy319), yymsp[0].minor.yy186)); + yygotominor.yy621 = yymsp[-4].minor.yy621; delete yymsp[-2].minor.yy319; } break; - case 242: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ + case 248: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ { - yymsp[-6].minor.yy201->append(ParserSetValue(*(yymsp[-3].minor.yy575), yymsp[0].minor.yy512)); - yygotominor.yy201 = yymsp[-6].minor.yy201; - delete yymsp[-3].minor.yy575; + yymsp[-6].minor.yy621->append(ParserSetValue(*(yymsp[-3].minor.yy173), yymsp[0].minor.yy186)); + yygotominor.yy621 = yymsp[-6].minor.yy621; + delete yymsp[-3].minor.yy173; } break; - case 243: /* setlist ::= nm EQ expr */ + case 249: /* setlist ::= nm EQ expr */ { - yygotominor.yy201 = new ParserSetValueList(); - yygotominor.yy201->append(ParserSetValue(*(yymsp[-2].minor.yy319), yymsp[0].minor.yy512)); + yygotominor.yy621 = new ParserSetValueList(); + yygotominor.yy621->append(ParserSetValue(*(yymsp[-2].minor.yy319), yymsp[0].minor.yy186)); delete yymsp[-2].minor.yy319; } break; - case 244: /* setlist ::= LP idlist RP EQ expr */ + case 250: /* setlist ::= LP idlist RP EQ expr */ { - yygotominor.yy201 = new ParserSetValueList(); - yygotominor.yy201->append(ParserSetValue(*(yymsp[-3].minor.yy575), yymsp[0].minor.yy512)); - delete yymsp[-3].minor.yy575; + yygotominor.yy621 = new ParserSetValueList(); + yygotominor.yy621->append(ParserSetValue(*(yymsp[-3].minor.yy173), yymsp[0].minor.yy186)); + delete yymsp[-3].minor.yy173; } break; - case 245: /* setlist ::= */ + case 251: /* setlist ::= */ { parserContext->minorErrorBeforeNextToken("Syntax error"); - yygotominor.yy201 = new ParserSetValueList(); + yygotominor.yy621 = new ParserSetValueList(); } break; - case 246: /* setlist ::= setlist COMMA */ + case 252: /* setlist ::= setlist COMMA */ { parserContext->minorErrorBeforeNextToken("Syntax error"); - yygotominor.yy201 = yymsp[-1].minor.yy201; + yygotominor.yy621 = yymsp[-1].minor.yy621; } break; - case 247: /* setlist ::= setlist COMMA ID_COL */ - case 248: /* setlist ::= ID_COL */ yytestcase(yyruleno==248); -{ yy_destructor(yypParser,274,&yymsp[-2].minor); + case 253: /* setlist ::= setlist COMMA ID_COL */ + case 254: /* setlist ::= ID_COL */ yytestcase(yyruleno==254); +{ yy_destructor(yypParser,280,&yymsp[-2].minor); } break; - case 249: /* idlist_opt ::= */ -{yygotominor.yy575 = new QStringList();} + case 255: /* idlist_opt ::= */ +{yygotominor.yy173 = new QStringList();} break; - case 250: /* idlist_opt ::= LP idlist RP */ -{yygotominor.yy575 = yymsp[-1].minor.yy575;} + case 256: /* idlist_opt ::= LP idlist RP */ +{yygotominor.yy173 = yymsp[-1].minor.yy173;} break; - case 251: /* idlist ::= idlist COMMA nm */ + case 257: /* idlist ::= idlist COMMA nm */ { - yygotominor.yy575 = yymsp[-2].minor.yy575; - *(yygotominor.yy575) << *(yymsp[0].minor.yy319); + yygotominor.yy173 = yymsp[-2].minor.yy173; + *(yygotominor.yy173) << *(yymsp[0].minor.yy319); delete yymsp[0].minor.yy319; } break; - case 252: /* idlist ::= nm */ + case 258: /* idlist ::= nm */ { - yygotominor.yy575 = new QStringList(); - *(yygotominor.yy575) << *(yymsp[0].minor.yy319); + yygotominor.yy173 = new QStringList(); + *(yygotominor.yy173) << *(yymsp[0].minor.yy319); delete yymsp[0].minor.yy319; } break; - case 253: /* idlist ::= */ + case 259: /* idlist ::= */ { parserContext->minorErrorBeforeNextToken("Syntax error"); - yygotominor.yy575 = new QStringList(); + yygotominor.yy173 = new QStringList(); } break; - case 254: /* idlist ::= idlist COMMA ID_COL */ - case 255: /* idlist ::= ID_COL */ yytestcase(yyruleno==255); -{ yy_destructor(yypParser,269,&yymsp[-2].minor); + case 260: /* idlist ::= idlist COMMA ID_COL */ + case 261: /* idlist ::= ID_COL */ yytestcase(yyruleno==261); +{ yy_destructor(yypParser,274,&yymsp[-2].minor); } break; - case 257: /* insert_stmt ::= with insert_cmd INTO fullname idlist_opt select upsert */ -{ - yygotominor.yy363 = new SqliteInsert( - yymsp[-5].minor.yy504->replace, - yymsp[-5].minor.yy504->orConflict, - yymsp[-3].minor.yy440->name1, - yymsp[-3].minor.yy440->name2, - *(yymsp[-2].minor.yy575), - yymsp[-1].minor.yy313, - yymsp[-6].minor.yy1, - yymsp[0].minor.yy400 + case 263: /* insert_stmt ::= with insert_cmd INTO fullname idlist_opt select upsert returning */ +{ + yygotominor.yy41 = new SqliteInsert( + yymsp[-6].minor.yy308->replace, + yymsp[-6].minor.yy308->orConflict, + yymsp[-4].minor.yy396->name1, + yymsp[-4].minor.yy396->name2, + *(yymsp[-3].minor.yy173), + yymsp[-2].minor.yy297, + yymsp[-7].minor.yy161, + yymsp[-1].minor.yy332, + *(yymsp[0].minor.yy27) ); - delete yymsp[-3].minor.yy440; - delete yymsp[-5].minor.yy504; - delete yymsp[-2].minor.yy575; + delete yymsp[-4].minor.yy396; + delete yymsp[-6].minor.yy308; + delete yymsp[-3].minor.yy173; + delete yymsp[0].minor.yy27; // since it's used in trigger: - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; - case 258: /* insert_stmt ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES */ + case 264: /* insert_stmt ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES returning */ { - yygotominor.yy363 = new SqliteInsert( - yymsp[-5].minor.yy504->replace, - yymsp[-5].minor.yy504->orConflict, - yymsp[-3].minor.yy440->name1, - yymsp[-3].minor.yy440->name2, - *(yymsp[-2].minor.yy575), - yymsp[-6].minor.yy1 + yygotominor.yy41 = new SqliteInsert( + yymsp[-6].minor.yy308->replace, + yymsp[-6].minor.yy308->orConflict, + yymsp[-4].minor.yy396->name1, + yymsp[-4].minor.yy396->name2, + *(yymsp[-3].minor.yy173), + yymsp[-7].minor.yy161, + *(yymsp[0].minor.yy27) ); - delete yymsp[-3].minor.yy440; - delete yymsp[-5].minor.yy504; - delete yymsp[-2].minor.yy575; + delete yymsp[-4].minor.yy396; + delete yymsp[-6].minor.yy308; + delete yymsp[-3].minor.yy173; + delete yymsp[0].minor.yy27; // since it's used in trigger: - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; - case 259: /* insert_stmt ::= with insert_cmd INTO */ + case 265: /* insert_stmt ::= with insert_cmd INTO */ { parserContext->minorErrorBeforeNextToken("Syntax error"); SqliteInsert* q = new SqliteInsert(); - q->replaceKw = yymsp[-1].minor.yy504->replace; - q->onConflict = yymsp[-1].minor.yy504->orConflict; - q->with = yymsp[-2].minor.yy1; - yygotominor.yy363 = q; - objectForTokens = yygotominor.yy363; - delete yymsp[-1].minor.yy504; + q->replaceKw = yymsp[-1].minor.yy308->replace; + q->onConflict = yymsp[-1].minor.yy308->orConflict; + q->with = yymsp[-2].minor.yy161; + yygotominor.yy41 = q; + objectForTokens = yygotominor.yy41; + delete yymsp[-1].minor.yy308; } break; - case 260: /* insert_stmt ::= with insert_cmd INTO nm DOT */ + case 266: /* insert_stmt ::= with insert_cmd INTO nm DOT */ { parserContext->minorErrorBeforeNextToken("Syntax error"); SqliteInsert* q = new SqliteInsert(); - q->replaceKw = yymsp[-3].minor.yy504->replace; - q->onConflict = yymsp[-3].minor.yy504->orConflict; - q->with = yymsp[-4].minor.yy1; + q->replaceKw = yymsp[-3].minor.yy308->replace; + q->onConflict = yymsp[-3].minor.yy308->orConflict; + q->with = yymsp[-4].minor.yy161; q->database = *(yymsp[-1].minor.yy319); - yygotominor.yy363 = q; - objectForTokens = yygotominor.yy363; - delete yymsp[-3].minor.yy504; + yygotominor.yy41 = q; + objectForTokens = yygotominor.yy41; + delete yymsp[-3].minor.yy308; delete yymsp[-1].minor.yy319; } break; - case 261: /* insert_stmt ::= with insert_cmd INTO ID_DB|ID_TAB */ -{ yy_destructor(yypParser,244,&yymsp[-3].minor); - yy_destructor(yypParser,277,&yymsp[-2].minor); + case 267: /* insert_stmt ::= with insert_cmd INTO ID_DB|ID_TAB */ +{ yy_destructor(yypParser,249,&yymsp[-3].minor); + yy_destructor(yypParser,283,&yymsp[-2].minor); } break; - case 262: /* insert_stmt ::= with insert_cmd INTO nm DOT ID_TAB */ -{ yy_destructor(yypParser,244,&yymsp[-5].minor); - yy_destructor(yypParser,277,&yymsp[-4].minor); - yy_destructor(yypParser,199,&yymsp[-2].minor); + case 268: /* insert_stmt ::= with insert_cmd INTO nm DOT ID_TAB */ +{ yy_destructor(yypParser,249,&yymsp[-5].minor); + yy_destructor(yypParser,283,&yymsp[-4].minor); + yy_destructor(yypParser,203,&yymsp[-2].minor); } break; - case 263: /* insert_cmd ::= INSERT orconf */ + case 269: /* insert_cmd ::= INSERT orconf */ { - yygotominor.yy504 = new ParserStubInsertOrReplace(false, *(yymsp[0].minor.yy418)); - delete yymsp[0].minor.yy418; + yygotominor.yy308 = new ParserStubInsertOrReplace(false, *(yymsp[0].minor.yy136)); + delete yymsp[0].minor.yy136; } break; - case 264: /* insert_cmd ::= REPLACE */ -{yygotominor.yy504 = new ParserStubInsertOrReplace(true);} + case 270: /* insert_cmd ::= REPLACE */ +{yygotominor.yy308 = new ParserStubInsertOrReplace(true);} break; - case 265: /* upsert ::= */ + case 271: /* upsert ::= */ { - yygotominor.yy400 = nullptr; + yygotominor.yy332 = nullptr; } break; - case 266: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ + case 272: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ { - yygotominor.yy400 = new SqliteUpsert(*(yymsp[-7].minor.yy403), yymsp[-5].minor.yy512, *(yymsp[-1].minor.yy201), yymsp[0].minor.yy512); - delete yymsp[-7].minor.yy403; - delete yymsp[-1].minor.yy201; - objectForTokens = yygotominor.yy400; + yygotominor.yy332 = new SqliteUpsert(*(yymsp[-7].minor.yy226), yymsp[-5].minor.yy186, *(yymsp[-1].minor.yy621), yymsp[0].minor.yy186); + delete yymsp[-7].minor.yy226; + delete yymsp[-1].minor.yy621; + objectForTokens = yygotominor.yy332; } break; - case 267: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ + case 273: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ { - yygotominor.yy400 = new SqliteUpsert(*(yymsp[-4].minor.yy403), yymsp[-2].minor.yy512); - delete yymsp[-4].minor.yy403; - objectForTokens = yygotominor.yy400; + yygotominor.yy332 = new SqliteUpsert(*(yymsp[-4].minor.yy226), yymsp[-2].minor.yy186); + delete yymsp[-4].minor.yy226; + objectForTokens = yygotominor.yy332; } break; - case 268: /* upsert ::= ON CONFLICT DO NOTHING */ + case 274: /* upsert ::= ON CONFLICT DO NOTHING */ { - yygotominor.yy400 = new SqliteUpsert(); - objectForTokens = yygotominor.yy400; + yygotominor.yy332 = new SqliteUpsert(); + objectForTokens = yygotominor.yy332; } break; - case 269: /* exprx ::= expr not_opt IN ID_DB */ -{ yy_destructor(yypParser,222,&yymsp[-3].minor); + case 275: /* exprx ::= expr not_opt IN ID_DB */ +{ yy_destructor(yypParser,227,&yymsp[-3].minor); } break; - case 270: /* exprx ::= expr not_opt IN nm DOT ID_TAB */ - case 271: /* exprx ::= ID_DB|ID_TAB|ID_COL|ID_FN */ yytestcase(yyruleno==271); -{ yy_destructor(yypParser,222,&yymsp[-5].minor); - yy_destructor(yypParser,199,&yymsp[-2].minor); + case 276: /* exprx ::= expr not_opt IN nm DOT ID_TAB */ + case 277: /* exprx ::= ID_DB|ID_TAB|ID_COL|ID_FN */ yytestcase(yyruleno==277); +{ yy_destructor(yypParser,227,&yymsp[-5].minor); + yy_destructor(yypParser,203,&yymsp[-2].minor); } break; - case 272: /* exprx ::= tnm DOT ID_TAB|ID_COL */ -{ yy_destructor(yypParser,230,&yymsp[-2].minor); + case 278: /* exprx ::= tnm DOT ID_TAB|ID_COL */ +{ yy_destructor(yypParser,235,&yymsp[-2].minor); } break; - case 273: /* exprx ::= tnm DOT nm DOT ID_COL */ -{ yy_destructor(yypParser,230,&yymsp[-4].minor); - yy_destructor(yypParser,199,&yymsp[-2].minor); + case 279: /* exprx ::= tnm DOT nm DOT ID_COL */ +{ yy_destructor(yypParser,235,&yymsp[-4].minor); + yy_destructor(yypParser,203,&yymsp[-2].minor); } break; - case 274: /* exprx ::= expr COLLATE ID_COLLATE */ - case 275: /* exprx ::= RAISE LP raisetype COMMA ID_ERR_MSG RP */ yytestcase(yyruleno==275); -{ yy_destructor(yypParser,222,&yymsp[-2].minor); + case 280: /* exprx ::= expr COLLATE ID_COLLATE */ + case 281: /* exprx ::= RAISE LP raisetype COMMA ID_ERR_MSG RP */ yytestcase(yyruleno==281); +{ yy_destructor(yypParser,227,&yymsp[-2].minor); } break; - case 276: /* exprx ::= CTIME_KW */ + case 282: /* exprx ::= CTIME_KW */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initCTime(yymsp[0].minor.yy0->value); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initCTime(yymsp[0].minor.yy0->value); + objectForTokens = yygotominor.yy186; } break; - case 277: /* exprx ::= LP nexprlist RP */ + case 283: /* exprx ::= LP nexprlist RP */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initRowValue(*(yymsp[-1].minor.yy71)); - delete yymsp[-1].minor.yy71; - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initRowValue(*(yymsp[-1].minor.yy615)); + delete yymsp[-1].minor.yy615; + objectForTokens = yygotominor.yy186; } break; - case 278: /* exprx ::= tnm */ + case 284: /* exprx ::= tnm */ { - yygotominor.yy512 = new SqliteExpr(); - if (yymsp[0].minor.yy590->isLiteral()) - yygotominor.yy512->initLiteral(yymsp[0].minor.yy590->toLiteral()); + yygotominor.yy186 = new SqliteExpr(); + if (yymsp[0].minor.yy380->isLiteral()) + yygotominor.yy186->initLiteral(yymsp[0].minor.yy380->toLiteral()); else - yygotominor.yy512->initId(yymsp[0].minor.yy590->toName()); + yygotominor.yy186->initId(yymsp[0].minor.yy380->toName()); //parserContext->errorBeforeLastToken("Syntax error "); - delete yymsp[0].minor.yy590; - objectForTokens = yygotominor.yy512; + delete yymsp[0].minor.yy380; + objectForTokens = yygotominor.yy186; } break; - case 279: /* exprx ::= tnm DOT nm */ + case 285: /* exprx ::= tnm DOT nm */ { - yygotominor.yy512 = new SqliteExpr(); - if (yymsp[-2].minor.yy590->isName()) - yygotominor.yy512->initId(yymsp[-2].minor.yy590->toName(), *(yymsp[0].minor.yy319)); + yygotominor.yy186 = new SqliteExpr(); + if (yymsp[-2].minor.yy380->isName()) + yygotominor.yy186->initId(yymsp[-2].minor.yy380->toName(), *(yymsp[0].minor.yy319)); else parserContext->errorAtToken("Syntax error ", -3); - delete yymsp[-2].minor.yy590; + delete yymsp[-2].minor.yy380; delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy512; + objectForTokens = yygotominor.yy186; } break; - case 280: /* exprx ::= tnm DOT nm DOT nm */ + case 286: /* exprx ::= tnm DOT */ { - yygotominor.yy512 = new SqliteExpr(); - if (yymsp[-4].minor.yy590->isName()) - yygotominor.yy512->initId(yymsp[-4].minor.yy590->toName(), *(yymsp[-2].minor.yy319), *(yymsp[0].minor.yy319)); + yygotominor.yy186 = new SqliteExpr(); + objectForTokens = yygotominor.yy186; + if (yymsp[-1].minor.yy380->isName()) + { + yygotominor.yy186->initId(yymsp[-1].minor.yy380->toName(), QString()); + parserContext->minorErrorAfterLastToken("Syntax error "); + } + else + parserContext->errorAtToken("Syntax error ", -3); + + delete yymsp[-1].minor.yy380; + } + break; + case 287: /* exprx ::= tnm DOT nm DOT nm */ +{ + yygotominor.yy186 = new SqliteExpr(); + if (yymsp[-4].minor.yy380->isName()) + yygotominor.yy186->initId(yymsp[-4].minor.yy380->toName(), *(yymsp[-2].minor.yy319), *(yymsp[0].minor.yy319)); else parserContext->errorAtToken("Syntax error ", -5); - delete yymsp[-4].minor.yy590; + delete yymsp[-4].minor.yy380; delete yymsp[-2].minor.yy319; delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy512; + objectForTokens = yygotominor.yy186; + } + break; + case 288: /* exprx ::= tnm DOT nm DOT */ +{ + yygotominor.yy186 = new SqliteExpr(); + objectForTokens = yygotominor.yy186; + if (yymsp[-3].minor.yy380->isName()) + { + yygotominor.yy186->initId(yymsp[-3].minor.yy380->toName(), *(yymsp[-1].minor.yy319), QString()); + parserContext->minorErrorAfterLastToken("Syntax error "); + } + else + parserContext->errorAtToken("Syntax error ", -5); + + delete yymsp[-3].minor.yy380; + delete yymsp[-1].minor.yy319; } break; - case 281: /* exprx ::= VARIABLE */ + case 289: /* exprx ::= VARIABLE */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initBindParam(yymsp[0].minor.yy0->value); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initBindParam(yymsp[0].minor.yy0->value); + objectForTokens = yygotominor.yy186; } break; - case 282: /* exprx ::= expr COLLATE ids */ + case 290: /* exprx ::= expr COLLATE ids */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initCollate(yymsp[-2].minor.yy512, *(yymsp[0].minor.yy319)); + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initCollate(yymsp[-2].minor.yy186, *(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy512; + objectForTokens = yygotominor.yy186; + } + break; + case 291: /* exprx ::= CAST LP expr AS typetoken RP */ +{ + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initCast(yymsp[-3].minor.yy186, yymsp[-1].minor.yy267); + objectForTokens = yygotominor.yy186; + } + break; + case 292: /* exprx ::= ID LP distinct exprlist RP */ +{ + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initFunction(stripObjName(yymsp[-4].minor.yy0->value), *(yymsp[-2].minor.yy130), *(yymsp[-1].minor.yy615)); + delete yymsp[-2].minor.yy130; + delete yymsp[-1].minor.yy615; + objectForTokens = yygotominor.yy186; } break; - case 283: /* exprx ::= CAST LP expr AS typetoken RP */ + case 293: /* exprx ::= ID LP STAR RP */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initCast(yymsp[-3].minor.yy512, yymsp[-1].minor.yy57); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initFunction(stripObjName(yymsp[-3].minor.yy0->value), true); + objectForTokens = yygotominor.yy186; } break; - case 284: /* exprx ::= ID LP distinct exprlist RP */ + case 294: /* exprx ::= expr AND expr */ + case 295: /* exprx ::= expr OR expr */ yytestcase(yyruleno==295); + case 296: /* exprx ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==296); + case 297: /* exprx ::= expr EQ|NE expr */ yytestcase(yyruleno==297); + case 298: /* exprx ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==298); + case 299: /* exprx ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==299); + case 300: /* exprx ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==300); + case 301: /* exprx ::= expr CONCAT expr */ yytestcase(yyruleno==301); { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initFunction(yymsp[-4].minor.yy0->value, *(yymsp[-2].minor.yy386), *(yymsp[-1].minor.yy71)); - delete yymsp[-2].minor.yy386; - delete yymsp[-1].minor.yy71; - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initBinOp(yymsp[-2].minor.yy186, yymsp[-1].minor.yy0->value, yymsp[0].minor.yy186); + objectForTokens = yygotominor.yy186; } break; - case 285: /* exprx ::= ID LP STAR RP */ + case 302: /* exprx ::= expr not_opt likeop expr */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initFunction(yymsp[-3].minor.yy0->value, true); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initLike(yymsp[-3].minor.yy186, *(yymsp[-2].minor.yy225), *(yymsp[-1].minor.yy274), yymsp[0].minor.yy186); + delete yymsp[-2].minor.yy225; + delete yymsp[-1].minor.yy274; + objectForTokens = yygotominor.yy186; } break; - case 286: /* exprx ::= expr AND expr */ - case 287: /* exprx ::= expr OR expr */ yytestcase(yyruleno==287); - case 288: /* exprx ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==288); - case 289: /* exprx ::= expr EQ|NE expr */ yytestcase(yyruleno==289); - case 290: /* exprx ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==290); - case 291: /* exprx ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==291); - case 292: /* exprx ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==292); - case 293: /* exprx ::= expr CONCAT expr */ yytestcase(yyruleno==293); + case 303: /* exprx ::= expr not_opt likeop expr ESCAPE expr */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initBinOp(yymsp[-2].minor.yy512, yymsp[-1].minor.yy0->value, yymsp[0].minor.yy512); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initLike(yymsp[-5].minor.yy186, *(yymsp[-4].minor.yy225), *(yymsp[-3].minor.yy274), yymsp[-2].minor.yy186, yymsp[0].minor.yy186); + delete yymsp[-4].minor.yy225; + delete yymsp[-3].minor.yy274; + objectForTokens = yygotominor.yy186; } break; - case 294: /* exprx ::= expr not_opt likeop expr */ + case 304: /* exprx ::= expr ISNULL|NOTNULL */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initLike(yymsp[-3].minor.yy512, *(yymsp[-2].minor.yy611), *(yymsp[-1].minor.yy40), yymsp[0].minor.yy512); - delete yymsp[-2].minor.yy611; - delete yymsp[-1].minor.yy40; - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initNull(yymsp[-1].minor.yy186, yymsp[0].minor.yy0->value); + objectForTokens = yygotominor.yy186; } break; - case 295: /* exprx ::= expr not_opt likeop expr ESCAPE expr */ + case 305: /* exprx ::= expr NOT NULL */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initLike(yymsp[-5].minor.yy512, *(yymsp[-4].minor.yy611), *(yymsp[-3].minor.yy40), yymsp[-2].minor.yy512, yymsp[0].minor.yy512); - delete yymsp[-4].minor.yy611; - delete yymsp[-3].minor.yy40; - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initNull(yymsp[-2].minor.yy186, "NOT NULL"); + objectForTokens = yygotominor.yy186; } break; - case 296: /* exprx ::= expr ISNULL|NOTNULL */ + case 306: /* exprx ::= expr IS not_opt expr */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initNull(yymsp[-1].minor.yy512, yymsp[0].minor.yy0->value); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initIs(yymsp[-3].minor.yy186, *(yymsp[-1].minor.yy225), yymsp[0].minor.yy186); + delete yymsp[-1].minor.yy225; + objectForTokens = yygotominor.yy186; } break; - case 297: /* exprx ::= expr NOT NULL */ + case 307: /* exprx ::= expr IS NOT DISTINCT FROM expr */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initNull(yymsp[-2].minor.yy512, "NOT NULL"); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initDistinct(yymsp[-5].minor.yy186, true, yymsp[0].minor.yy186); + objectForTokens = yygotominor.yy186; } break; - case 298: /* exprx ::= expr IS not_opt expr */ + case 308: /* exprx ::= expr IS DISTINCT FROM expr */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initIs(yymsp[-3].minor.yy512, *(yymsp[-1].minor.yy611), yymsp[0].minor.yy512); - delete yymsp[-1].minor.yy611; - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initDistinct(yymsp[-4].minor.yy186, false, yymsp[0].minor.yy186); + objectForTokens = yygotominor.yy186; } break; - case 299: /* exprx ::= NOT expr */ + case 309: /* exprx ::= NOT expr */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initUnaryOp(yymsp[0].minor.yy512, yymsp[-1].minor.yy0->value); + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initUnaryOp(yymsp[0].minor.yy186, yymsp[-1].minor.yy0->value); } break; - case 300: /* exprx ::= BITNOT expr */ - case 302: /* exprx ::= PLUS expr */ yytestcase(yyruleno==302); + case 310: /* exprx ::= BITNOT expr */ + case 312: /* exprx ::= PLUS expr */ yytestcase(yyruleno==312); { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initUnaryOp(yymsp[0].minor.yy512, yymsp[-1].minor.yy0->value); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initUnaryOp(yymsp[0].minor.yy186, yymsp[-1].minor.yy0->value); + objectForTokens = yygotominor.yy186; } break; - case 301: /* exprx ::= MINUS expr */ + case 311: /* exprx ::= MINUS expr */ { - yygotominor.yy512 = new SqliteExpr(); - if (yymsp[0].minor.yy512->mode == SqliteExpr::Mode::LITERAL_VALUE && + yygotominor.yy186 = new SqliteExpr(); + if (yymsp[0].minor.yy186->mode == SqliteExpr::Mode::LITERAL_VALUE && parserContext->isCandidateForMaxNegativeNumber() && - yymsp[0].minor.yy512->literalValue == static_cast(0L)) + yymsp[0].minor.yy186->literalValue == static_cast(0L)) { - yygotominor.yy512->initLiteral(std::numeric_limits::min()); - delete yymsp[0].minor.yy512; + yygotominor.yy186->initLiteral(std::numeric_limits::min()); + delete yymsp[0].minor.yy186; } else { - yygotominor.yy512->initUnaryOp(yymsp[0].minor.yy512, yymsp[-1].minor.yy0->value); + yygotominor.yy186->initUnaryOp(yymsp[0].minor.yy186, yymsp[-1].minor.yy0->value); } - objectForTokens = yygotominor.yy512; + objectForTokens = yygotominor.yy186; } break; - case 303: /* exprx ::= expr not_opt BETWEEN expr AND expr */ + case 313: /* exprx ::= expr PTR expr */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initBetween(yymsp[-5].minor.yy512, *(yymsp[-4].minor.yy611), yymsp[-2].minor.yy512, yymsp[0].minor.yy512); - delete yymsp[-4].minor.yy611; - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initPtrOp(yymsp[-2].minor.yy186, yymsp[-1].minor.yy0->value, yymsp[0].minor.yy186); + objectForTokens = yygotominor.yy186; } break; - case 304: /* exprx ::= expr not_opt IN LP exprlist RP */ + case 314: /* exprx ::= expr not_opt BETWEEN expr AND expr */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initIn(yymsp[-5].minor.yy512, *(yymsp[-4].minor.yy611), *(yymsp[-1].minor.yy71)); - delete yymsp[-4].minor.yy611; - delete yymsp[-1].minor.yy71; - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initBetween(yymsp[-5].minor.yy186, *(yymsp[-4].minor.yy225), yymsp[-2].minor.yy186, yymsp[0].minor.yy186); + delete yymsp[-4].minor.yy225; + objectForTokens = yygotominor.yy186; } break; - case 305: /* exprx ::= LP select RP */ + case 315: /* exprx ::= expr not_opt IN LP exprlist RP */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initSubSelect(yymsp[-1].minor.yy313); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initIn(yymsp[-5].minor.yy186, *(yymsp[-4].minor.yy225), *(yymsp[-1].minor.yy615)); + delete yymsp[-4].minor.yy225; + delete yymsp[-1].minor.yy615; + objectForTokens = yygotominor.yy186; } break; - case 306: /* exprx ::= expr not_opt IN LP select RP */ + case 316: /* exprx ::= LP select RP */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initIn(yymsp[-5].minor.yy512, *(yymsp[-4].minor.yy611), yymsp[-1].minor.yy313); - delete yymsp[-4].minor.yy611; - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initSubSelect(yymsp[-1].minor.yy297); + objectForTokens = yygotominor.yy186; } break; - case 307: /* exprx ::= expr not_opt IN nm dbnm */ + case 317: /* exprx ::= expr not_opt IN LP select RP */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initIn(yymsp[-4].minor.yy512, *(yymsp[-3].minor.yy611), *(yymsp[-1].minor.yy319), *(yymsp[0].minor.yy319)); - delete yymsp[-3].minor.yy611; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initIn(yymsp[-5].minor.yy186, *(yymsp[-4].minor.yy225), yymsp[-1].minor.yy297); + delete yymsp[-4].minor.yy225; + objectForTokens = yygotominor.yy186; + } + break; + case 318: /* exprx ::= expr not_opt IN nm dbnm */ +{ + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initIn(yymsp[-4].minor.yy186, *(yymsp[-3].minor.yy225), *(yymsp[-1].minor.yy319), *(yymsp[0].minor.yy319)); + delete yymsp[-3].minor.yy225; delete yymsp[-1].minor.yy319; - objectForTokens = yygotominor.yy512; + objectForTokens = yygotominor.yy186; } break; - case 308: /* exprx ::= EXISTS LP select RP */ + case 319: /* exprx ::= EXISTS LP select RP */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initExists(yymsp[-1].minor.yy313); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initExists(yymsp[-1].minor.yy297); + objectForTokens = yygotominor.yy186; } break; - case 309: /* exprx ::= CASE case_operand case_exprlist case_else END */ + case 320: /* exprx ::= CASE case_operand case_exprlist case_else END */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initCase(yymsp[-3].minor.yy512, *(yymsp[-2].minor.yy71), yymsp[-1].minor.yy512); - delete yymsp[-2].minor.yy71; - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initCase(yymsp[-3].minor.yy186, *(yymsp[-2].minor.yy615), yymsp[-1].minor.yy186); + delete yymsp[-2].minor.yy615; + objectForTokens = yygotominor.yy186; } break; - case 310: /* exprx ::= RAISE LP IGNORE RP */ + case 321: /* exprx ::= RAISE LP IGNORE RP */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initRaise(yymsp[-1].minor.yy0->value); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initRaise(yymsp[-1].minor.yy0->value); + objectForTokens = yygotominor.yy186; } break; - case 311: /* exprx ::= RAISE LP raisetype COMMA nm RP */ + case 322: /* exprx ::= RAISE LP raisetype COMMA nm RP */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initRaise(yymsp[-3].minor.yy0->value, *(yymsp[-1].minor.yy319)); + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initRaise(yymsp[-3].minor.yy0->value, *(yymsp[-1].minor.yy319)); delete yymsp[-1].minor.yy319; - objectForTokens = yygotominor.yy512; + objectForTokens = yygotominor.yy186; } break; - case 312: /* exprx ::= ID LP distinct exprlist RP filter_over */ + case 323: /* exprx ::= ID LP distinct exprlist RP filter_over */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initWindowFunction(yymsp[-5].minor.yy0->value, *(yymsp[-3].minor.yy386), *(yymsp[-2].minor.yy71), yymsp[0].minor.yy247); - delete yymsp[-3].minor.yy386; - delete yymsp[-2].minor.yy71; - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initWindowFunction(stripObjName(yymsp[-5].minor.yy0->value), *(yymsp[-3].minor.yy130), *(yymsp[-2].minor.yy615), yymsp[0].minor.yy181); + delete yymsp[-3].minor.yy130; + delete yymsp[-2].minor.yy615; + objectForTokens = yygotominor.yy186; } break; - case 313: /* exprx ::= ID LP STAR RP filter_over */ + case 324: /* exprx ::= ID LP STAR RP filter_over */ { - yygotominor.yy512 = new SqliteExpr(); - yygotominor.yy512->initWindowFunction(yymsp[-4].minor.yy0->value, yymsp[0].minor.yy247); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + yygotominor.yy186->initWindowFunction(stripObjName(yymsp[-4].minor.yy0->value), yymsp[0].minor.yy181); + objectForTokens = yygotominor.yy186; } break; - case 314: /* expr ::= */ + case 325: /* expr ::= */ { - yygotominor.yy512 = new SqliteExpr(); - objectForTokens = yygotominor.yy512; + yygotominor.yy186 = new SqliteExpr(); + objectForTokens = yygotominor.yy186; parserContext->minorErrorAfterLastToken("Syntax error "); } break; - case 318: /* likeop ::= LIKE_KW|MATCH */ -{yygotominor.yy40 = new SqliteExpr::LikeOp(SqliteExpr::likeOp(yymsp[0].minor.yy0->value));} + case 329: /* likeop ::= LIKE_KW|MATCH */ +{yygotominor.yy274 = new SqliteExpr::LikeOp(SqliteExpr::likeOp(yymsp[0].minor.yy0->value));} break; - case 319: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + case 330: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { - yymsp[-4].minor.yy71->append(yymsp[-2].minor.yy512); - yymsp[-4].minor.yy71->append(yymsp[0].minor.yy512); - yygotominor.yy71 = yymsp[-4].minor.yy71; + yymsp[-4].minor.yy615->append(yymsp[-2].minor.yy186); + yymsp[-4].minor.yy615->append(yymsp[0].minor.yy186); + yygotominor.yy615 = yymsp[-4].minor.yy615; } break; - case 320: /* case_exprlist ::= WHEN expr THEN expr */ + case 331: /* case_exprlist ::= WHEN expr THEN expr */ { - yygotominor.yy71 = new ParserExprList(); - yygotominor.yy71->append(yymsp[-2].minor.yy512); - yygotominor.yy71->append(yymsp[0].minor.yy512); + yygotominor.yy615 = new ParserExprList(); + yygotominor.yy615->append(yymsp[-2].minor.yy186); + yygotominor.yy615->append(yymsp[0].minor.yy186); } break; - case 327: /* nexprlist ::= nexprlist COMMA expr */ + case 338: /* nexprlist ::= nexprlist COMMA expr */ { - yymsp[-2].minor.yy71->append(yymsp[0].minor.yy512); - yygotominor.yy71 = yymsp[-2].minor.yy71; + yymsp[-2].minor.yy615->append(yymsp[0].minor.yy186); + yygotominor.yy615 = yymsp[-2].minor.yy615; DONT_INHERIT_TOKENS("nexprlist"); } break; - case 328: /* nexprlist ::= exprx */ + case 339: /* nexprlist ::= exprx */ { - yygotominor.yy71 = new ParserExprList(); - yygotominor.yy71->append(yymsp[0].minor.yy512); + yygotominor.yy615 = new ParserExprList(); + yygotominor.yy615->append(yymsp[0].minor.yy186); } break; - case 329: /* cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + case 340: /* cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ { - yygotominor.yy363 = new SqliteCreateIndex( - *(yymsp[-10].minor.yy611), - *(yymsp[-8].minor.yy611), + yygotominor.yy41 = new SqliteCreateIndex( + *(yymsp[-10].minor.yy225), + *(yymsp[-8].minor.yy225), *(yymsp[-7].minor.yy319), *(yymsp[-6].minor.yy319), *(yymsp[-4].minor.yy319), - *(yymsp[-2].minor.yy403), - yymsp[0].minor.yy512 + *(yymsp[-2].minor.yy226), + yymsp[0].minor.yy186 ); - delete yymsp[-8].minor.yy611; - delete yymsp[-10].minor.yy611; + delete yymsp[-8].minor.yy225; + delete yymsp[-10].minor.yy225; delete yymsp[-7].minor.yy319; delete yymsp[-6].minor.yy319; delete yymsp[-4].minor.yy319; - delete yymsp[-2].minor.yy403; - objectForTokens = yygotominor.yy363; + delete yymsp[-2].minor.yy226; + objectForTokens = yygotominor.yy41; } break; - case 330: /* cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON ID_TAB */ -{ yy_destructor(yypParser,199,&yymsp[-3].minor); + case 341: /* cmd ::= CREATE uniqueflag INDEX ifnotexists nm dbnm ON ID_TAB */ +{ yy_destructor(yypParser,203,&yymsp[-3].minor); } break; - case 335: /* idxlist_opt ::= */ -{yygotominor.yy223 = new ParserIndexedColumnList();} + case 346: /* idxlist_opt ::= */ +{yygotominor.yy627 = new ParserIndexedColumnList();} break; - case 336: /* idxlist_opt ::= LP idxlist RP */ -{yygotominor.yy223 = yymsp[-1].minor.yy223;} + case 347: /* idxlist_opt ::= LP idxlist RP */ +{yygotominor.yy627 = yymsp[-1].minor.yy627;} break; - case 337: /* idxlist ::= idxlist COMMA idxlist_single */ + case 348: /* idxlist ::= idxlist COMMA idxlist_single */ { - yymsp[-2].minor.yy223->append(yymsp[0].minor.yy268); - yygotominor.yy223 = yymsp[-2].minor.yy223; + yymsp[-2].minor.yy627->append(yymsp[0].minor.yy110); + yygotominor.yy627 = yymsp[-2].minor.yy627; DONT_INHERIT_TOKENS("idxlist"); } break; - case 338: /* idxlist ::= idxlist_single */ + case 349: /* idxlist ::= idxlist_single */ { - yygotominor.yy223 = new ParserIndexedColumnList(); - yygotominor.yy223->append(yymsp[0].minor.yy268); + yygotominor.yy627 = new ParserIndexedColumnList(); + yygotominor.yy627->append(yymsp[0].minor.yy110); } break; - case 339: /* idxlist_single ::= nm collate sortorder */ - case 340: /* idxlist_single ::= ID_COL */ yytestcase(yyruleno==340); + case 350: /* idxlist_single ::= nm collate sortorder */ + case 351: /* idxlist_single ::= ID_COL */ yytestcase(yyruleno==351); { SqliteIndexedColumn* obj = new SqliteIndexedColumn( *(yymsp[-2].minor.yy319), *(yymsp[-1].minor.yy319), - *(yymsp[0].minor.yy549) + *(yymsp[0].minor.yy35) ); - yygotominor.yy268 = obj; - delete yymsp[0].minor.yy549; + yygotominor.yy110 = obj; + delete yymsp[0].minor.yy35; delete yymsp[-2].minor.yy319; delete yymsp[-1].minor.yy319; - objectForTokens = yygotominor.yy268; + objectForTokens = yygotominor.yy110; } break; - case 344: /* cmd ::= DROP INDEX ifexists fullname */ + case 355: /* cmd ::= DROP INDEX ifexists fullname */ { - yygotominor.yy363 = new SqliteDropIndex(*(yymsp[-1].minor.yy611), yymsp[0].minor.yy440->name1, yymsp[0].minor.yy440->name2); - delete yymsp[-1].minor.yy611; - delete yymsp[0].minor.yy440; - objectForTokens = yygotominor.yy363; + yygotominor.yy41 = new SqliteDropIndex(*(yymsp[-1].minor.yy225), yymsp[0].minor.yy396->name1, yymsp[0].minor.yy396->name2); + delete yymsp[-1].minor.yy225; + delete yymsp[0].minor.yy396; + objectForTokens = yygotominor.yy41; } break; - case 347: /* cmd ::= VACUUM vinto */ + case 358: /* cmd ::= VACUUM vinto */ { - yygotominor.yy363 = new SqliteVacuum(yymsp[0].minor.yy512); - objectForTokens = yygotominor.yy363; + yygotominor.yy41 = new SqliteVacuum(yymsp[0].minor.yy186); + objectForTokens = yygotominor.yy41; } break; - case 348: /* cmd ::= VACUUM nm vinto */ + case 359: /* cmd ::= VACUUM nm vinto */ { - yygotominor.yy363 = new SqliteVacuum(*(yymsp[-1].minor.yy319), yymsp[0].minor.yy512); + yygotominor.yy41 = new SqliteVacuum(*(yymsp[-1].minor.yy319), yymsp[0].minor.yy186); delete yymsp[-1].minor.yy319; - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; - case 351: /* cmd ::= PRAGMA nm dbnm */ + case 362: /* cmd ::= PRAGMA nm dbnm */ { - yygotominor.yy363 = new SqlitePragma(*(yymsp[-1].minor.yy319), *(yymsp[0].minor.yy319)); + yygotominor.yy41 = new SqlitePragma(*(yymsp[-1].minor.yy319), *(yymsp[0].minor.yy319)); delete yymsp[-1].minor.yy319; delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; - case 352: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ - case 354: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ yytestcase(yyruleno==354); + case 363: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + case 365: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ yytestcase(yyruleno==365); { - yygotominor.yy363 = new SqlitePragma(*(yymsp[-3].minor.yy319), *(yymsp[-2].minor.yy319), *(yymsp[0].minor.yy229), true); + yygotominor.yy41 = new SqlitePragma(*(yymsp[-3].minor.yy319), *(yymsp[-2].minor.yy319), *(yymsp[0].minor.yy393), true); delete yymsp[-3].minor.yy319; delete yymsp[-2].minor.yy319; - delete yymsp[0].minor.yy229; - objectForTokens = yygotominor.yy363; + delete yymsp[0].minor.yy393; + objectForTokens = yygotominor.yy41; } break; - case 353: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ - case 355: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ yytestcase(yyruleno==355); + case 364: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + case 366: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ yytestcase(yyruleno==366); { - yygotominor.yy363 = new SqlitePragma(*(yymsp[-4].minor.yy319), *(yymsp[-3].minor.yy319), *(yymsp[-1].minor.yy229), false); + yygotominor.yy41 = new SqlitePragma(*(yymsp[-4].minor.yy319), *(yymsp[-3].minor.yy319), *(yymsp[-1].minor.yy393), false); delete yymsp[-4].minor.yy319; delete yymsp[-3].minor.yy319; - delete yymsp[-1].minor.yy229; - objectForTokens = yygotominor.yy363; + delete yymsp[-1].minor.yy393; + objectForTokens = yygotominor.yy41; } break; - case 359: /* nmnum ::= nm */ + case 370: /* nmnum ::= nm */ { - yygotominor.yy229 = new QVariant(*(yymsp[0].minor.yy319)); + yygotominor.yy393 = new QVariant(*(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; } break; - case 360: /* nmnum ::= ON */ - case 361: /* nmnum ::= DELETE */ yytestcase(yyruleno==361); - case 362: /* nmnum ::= DEFAULT */ yytestcase(yyruleno==362); -{yygotominor.yy229 = new QVariant(yymsp[0].minor.yy0->value);} + case 371: /* nmnum ::= ON */ + case 372: /* nmnum ::= DELETE */ yytestcase(yyruleno==372); + case 373: /* nmnum ::= DEFAULT */ yytestcase(yyruleno==373); +{yygotominor.yy393 = new QVariant(yymsp[0].minor.yy0->value);} break; - case 365: /* minus_num ::= MINUS number */ + case 376: /* minus_num ::= MINUS number */ { - if (yymsp[0].minor.yy229->type() == QVariant::Double) - *(yymsp[0].minor.yy229) = -(yymsp[0].minor.yy229->toDouble()); - else if (yymsp[0].minor.yy229->type() == QVariant::LongLong) + if (yymsp[0].minor.yy393->type() == QVariant::Double) + *(yymsp[0].minor.yy393) = -(yymsp[0].minor.yy393->toDouble()); + else if (yymsp[0].minor.yy393->type() == QVariant::LongLong) { if (parserContext->isCandidateForMaxNegativeNumber()) - *(yymsp[0].minor.yy229) = std::numeric_limits::min(); + *(yymsp[0].minor.yy393) = std::numeric_limits::min(); else - *(yymsp[0].minor.yy229) = -(yymsp[0].minor.yy229->toLongLong()); + *(yymsp[0].minor.yy393) = -(yymsp[0].minor.yy393->toLongLong()); } else Q_ASSERT_X(true, "producing minus number", "QVariant is neither of Double or LongLong."); - yygotominor.yy229 = yymsp[0].minor.yy229; + yygotominor.yy393 = yymsp[0].minor.yy393; } break; - case 368: /* cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause BEGIN trigger_cmd_list END */ + case 379: /* cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause BEGIN trigger_cmd_list END */ { - yygotominor.yy363 = new SqliteCreateTrigger( - *(yymsp[-13].minor.yy386), - *(yymsp[-11].minor.yy611), + yygotominor.yy41 = new SqliteCreateTrigger( + *(yymsp[-13].minor.yy130), + *(yymsp[-11].minor.yy225), *(yymsp[-10].minor.yy319), *(yymsp[-9].minor.yy319), *(yymsp[-5].minor.yy319), - *(yymsp[-8].minor.yy532), - yymsp[-7].minor.yy151, - *(yymsp[-4].minor.yy83), - yymsp[-3].minor.yy512, - *(yymsp[-1].minor.yy110), + *(yymsp[-8].minor.yy120), + yymsp[-7].minor.yy259, + *(yymsp[-4].minor.yy456), + yymsp[-3].minor.yy186, + *(yymsp[-1].minor.yy240), 3 ); - delete yymsp[-11].minor.yy611; - delete yymsp[-13].minor.yy386; - delete yymsp[-8].minor.yy532; - delete yymsp[-4].minor.yy83; + delete yymsp[-11].minor.yy225; + delete yymsp[-13].minor.yy130; + delete yymsp[-8].minor.yy120; + delete yymsp[-4].minor.yy456; delete yymsp[-10].minor.yy319; delete yymsp[-5].minor.yy319; delete yymsp[-9].minor.yy319; - delete yymsp[-1].minor.yy110; - objectForTokens = yygotominor.yy363; + delete yymsp[-1].minor.yy240; + objectForTokens = yygotominor.yy41; } break; - case 369: /* cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause */ + case 380: /* cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause */ { QList CL; - yygotominor.yy363 = new SqliteCreateTrigger( - *(yymsp[-10].minor.yy386), - *(yymsp[-8].minor.yy611), + yygotominor.yy41 = new SqliteCreateTrigger( + *(yymsp[-10].minor.yy130), + *(yymsp[-8].minor.yy225), *(yymsp[-7].minor.yy319), *(yymsp[-6].minor.yy319), *(yymsp[-2].minor.yy319), - *(yymsp[-5].minor.yy532), - yymsp[-4].minor.yy151, - *(yymsp[-1].minor.yy83), - yymsp[0].minor.yy512, + *(yymsp[-5].minor.yy120), + yymsp[-4].minor.yy259, + *(yymsp[-1].minor.yy456), + yymsp[0].minor.yy186, CL, 3 ); - delete yymsp[-8].minor.yy611; - delete yymsp[-10].minor.yy386; - delete yymsp[-5].minor.yy532; - delete yymsp[-1].minor.yy83; + delete yymsp[-8].minor.yy225; + delete yymsp[-10].minor.yy130; + delete yymsp[-5].minor.yy120; + delete yymsp[-1].minor.yy456; delete yymsp[-7].minor.yy319; delete yymsp[-2].minor.yy319; delete yymsp[-6].minor.yy319; - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; parserContext->minorErrorAfterLastToken("Syntax error"); } break; - case 370: /* cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause BEGIN trigger_cmd_list */ + case 381: /* cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON nm foreach_clause when_clause BEGIN trigger_cmd_list */ { - yygotominor.yy363 = new SqliteCreateTrigger( - *(yymsp[-12].minor.yy386), - *(yymsp[-10].minor.yy611), + yygotominor.yy41 = new SqliteCreateTrigger( + *(yymsp[-12].minor.yy130), + *(yymsp[-10].minor.yy225), *(yymsp[-9].minor.yy319), *(yymsp[-8].minor.yy319), *(yymsp[-4].minor.yy319), - *(yymsp[-7].minor.yy532), - yymsp[-6].minor.yy151, - *(yymsp[-3].minor.yy83), - yymsp[-2].minor.yy512, - *(yymsp[0].minor.yy110), + *(yymsp[-7].minor.yy120), + yymsp[-6].minor.yy259, + *(yymsp[-3].minor.yy456), + yymsp[-2].minor.yy186, + *(yymsp[0].minor.yy240), 3 ); - delete yymsp[-10].minor.yy611; - delete yymsp[-12].minor.yy386; - delete yymsp[-7].minor.yy532; - delete yymsp[-3].minor.yy83; + delete yymsp[-10].minor.yy225; + delete yymsp[-12].minor.yy130; + delete yymsp[-7].minor.yy120; + delete yymsp[-3].minor.yy456; delete yymsp[-9].minor.yy319; delete yymsp[-4].minor.yy319; delete yymsp[-8].minor.yy319; - delete yymsp[0].minor.yy110; - objectForTokens = yygotominor.yy363; + delete yymsp[0].minor.yy240; + objectForTokens = yygotominor.yy41; parserContext->minorErrorAfterLastToken("Syntax error"); } break; - case 371: /* cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON ID_TAB */ -{ yy_destructor(yypParser,201,&yymsp[-8].minor); - yy_destructor(yypParser,199,&yymsp[-5].minor); - yy_destructor(yypParser,292,&yymsp[-3].minor); - yy_destructor(yypParser,293,&yymsp[-2].minor); + case 382: /* cmd ::= CREATE temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON ID_TAB */ +{ yy_destructor(yypParser,205,&yymsp[-8].minor); + yy_destructor(yypParser,203,&yymsp[-5].minor); + yy_destructor(yypParser,298,&yymsp[-3].minor); + yy_destructor(yypParser,299,&yymsp[-2].minor); } break; - case 374: /* trigger_time ::= BEFORE */ -{yygotominor.yy532 = new SqliteCreateTrigger::Time(SqliteCreateTrigger::Time::BEFORE);} + case 385: /* trigger_time ::= BEFORE */ +{yygotominor.yy120 = new SqliteCreateTrigger::Time(SqliteCreateTrigger::Time::BEFORE);} break; - case 375: /* trigger_time ::= AFTER */ -{yygotominor.yy532 = new SqliteCreateTrigger::Time(SqliteCreateTrigger::Time::AFTER);} + case 386: /* trigger_time ::= AFTER */ +{yygotominor.yy120 = new SqliteCreateTrigger::Time(SqliteCreateTrigger::Time::AFTER);} break; - case 376: /* trigger_time ::= INSTEAD OF */ -{yygotominor.yy532 = new SqliteCreateTrigger::Time(SqliteCreateTrigger::Time::INSTEAD_OF);} + case 387: /* trigger_time ::= INSTEAD OF */ +{yygotominor.yy120 = new SqliteCreateTrigger::Time(SqliteCreateTrigger::Time::INSTEAD_OF);} break; - case 377: /* trigger_time ::= */ -{yygotominor.yy532 = new SqliteCreateTrigger::Time(SqliteCreateTrigger::Time::null);} + case 388: /* trigger_time ::= */ +{yygotominor.yy120 = new SqliteCreateTrigger::Time(SqliteCreateTrigger::Time::null);} break; - case 378: /* trigger_event ::= DELETE */ + case 389: /* trigger_event ::= DELETE */ { - yygotominor.yy151 = new SqliteCreateTrigger::Event(SqliteCreateTrigger::Event::DELETE); - objectForTokens = yygotominor.yy151; + yygotominor.yy259 = new SqliteCreateTrigger::Event(SqliteCreateTrigger::Event::DELETE); + objectForTokens = yygotominor.yy259; } break; - case 379: /* trigger_event ::= INSERT */ + case 390: /* trigger_event ::= INSERT */ { - yygotominor.yy151 = new SqliteCreateTrigger::Event(SqliteCreateTrigger::Event::INSERT); - objectForTokens = yygotominor.yy151; + yygotominor.yy259 = new SqliteCreateTrigger::Event(SqliteCreateTrigger::Event::INSERT); + objectForTokens = yygotominor.yy259; } break; - case 380: /* trigger_event ::= UPDATE */ + case 391: /* trigger_event ::= UPDATE */ { - yygotominor.yy151 = new SqliteCreateTrigger::Event(SqliteCreateTrigger::Event::UPDATE); - objectForTokens = yygotominor.yy151; + yygotominor.yy259 = new SqliteCreateTrigger::Event(SqliteCreateTrigger::Event::UPDATE); + objectForTokens = yygotominor.yy259; } break; - case 381: /* trigger_event ::= UPDATE OF idlist */ + case 392: /* trigger_event ::= UPDATE OF idlist */ { - yygotominor.yy151 = new SqliteCreateTrigger::Event(*(yymsp[0].minor.yy575)); - delete yymsp[0].minor.yy575; - objectForTokens = yygotominor.yy151; + yygotominor.yy259 = new SqliteCreateTrigger::Event(*(yymsp[0].minor.yy173)); + delete yymsp[0].minor.yy173; + objectForTokens = yygotominor.yy259; } break; - case 382: /* foreach_clause ::= */ -{yygotominor.yy83 = new SqliteCreateTrigger::Scope(SqliteCreateTrigger::Scope::null);} + case 393: /* foreach_clause ::= */ +{yygotominor.yy456 = new SqliteCreateTrigger::Scope(SqliteCreateTrigger::Scope::null);} break; - case 383: /* foreach_clause ::= FOR EACH ROW */ -{yygotominor.yy83 = new SqliteCreateTrigger::Scope(SqliteCreateTrigger::Scope::FOR_EACH_ROW);} + case 394: /* foreach_clause ::= FOR EACH ROW */ +{yygotominor.yy456 = new SqliteCreateTrigger::Scope(SqliteCreateTrigger::Scope::FOR_EACH_ROW);} break; - case 386: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 397: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { - yymsp[-2].minor.yy110->append(yymsp[-1].minor.yy363); - yygotominor.yy110 = yymsp[-2].minor.yy110; + yymsp[-2].minor.yy240->append(yymsp[-1].minor.yy41); + yygotominor.yy240 = yymsp[-2].minor.yy240; DONT_INHERIT_TOKENS("trigger_cmd_list"); } break; - case 387: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 398: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - yygotominor.yy110 = new ParserQueryList(); - yygotominor.yy110->append(yymsp[-1].minor.yy363); + yygotominor.yy240 = new ParserQueryList(); + yygotominor.yy240->append(yymsp[-1].minor.yy41); } break; - case 388: /* trigger_cmd_list ::= SEMI */ + case 399: /* trigger_cmd_list ::= SEMI */ { - yygotominor.yy110 = new ParserQueryList(); + yygotominor.yy240 = new ParserQueryList(); parserContext->minorErrorAfterLastToken("Syntax error"); } break; - case 393: /* raisetype ::= ROLLBACK|ABORT|FAIL */ + case 404: /* raisetype ::= ROLLBACK|ABORT|FAIL */ {yygotominor.yy0 = yymsp[0].minor.yy0;} break; - case 394: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 405: /* cmd ::= DROP TRIGGER ifexists fullname */ { - yygotominor.yy363 = new SqliteDropTrigger(*(yymsp[-1].minor.yy611), yymsp[0].minor.yy440->name1, yymsp[0].minor.yy440->name2); - delete yymsp[-1].minor.yy611; - delete yymsp[0].minor.yy440; - objectForTokens = yygotominor.yy363; + yygotominor.yy41 = new SqliteDropTrigger(*(yymsp[-1].minor.yy225), yymsp[0].minor.yy396->name1, yymsp[0].minor.yy396->name2); + delete yymsp[-1].minor.yy225; + delete yymsp[0].minor.yy396; + objectForTokens = yygotominor.yy41; } break; - case 397: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 408: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - yygotominor.yy363 = new SqliteAttach(*(yymsp[-4].minor.yy611), yymsp[-3].minor.yy512, yymsp[-1].minor.yy512, yymsp[0].minor.yy512); - delete yymsp[-4].minor.yy611; - objectForTokens = yygotominor.yy363; + yygotominor.yy41 = new SqliteAttach(*(yymsp[-4].minor.yy225), yymsp[-3].minor.yy186, yymsp[-1].minor.yy186, yymsp[0].minor.yy186); + delete yymsp[-4].minor.yy225; + objectForTokens = yygotominor.yy41; } break; - case 398: /* cmd ::= DETACH database_kw_opt expr */ + case 409: /* cmd ::= DETACH database_kw_opt expr */ { - yygotominor.yy363 = new SqliteDetach(*(yymsp[-1].minor.yy611), yymsp[0].minor.yy512); - delete yymsp[-1].minor.yy611; - objectForTokens = yygotominor.yy363; + yygotominor.yy41 = new SqliteDetach(*(yymsp[-1].minor.yy225), yymsp[0].minor.yy186); + delete yymsp[-1].minor.yy225; + objectForTokens = yygotominor.yy41; } break; - case 403: /* cmd ::= REINDEX */ -{yygotominor.yy363 = new SqliteReindex();} + case 414: /* cmd ::= REINDEX */ +{yygotominor.yy41 = new SqliteReindex();} break; - case 404: /* cmd ::= REINDEX nm dbnm */ - case 405: /* cmd ::= REINDEX ID_COLLATE */ yytestcase(yyruleno==405); + case 415: /* cmd ::= REINDEX nm dbnm */ + case 416: /* cmd ::= REINDEX ID_COLLATE */ yytestcase(yyruleno==416); { - yygotominor.yy363 = new SqliteReindex(*(yymsp[-1].minor.yy319), *(yymsp[0].minor.yy319)); + yygotominor.yy41 = new SqliteReindex(*(yymsp[-1].minor.yy319), *(yymsp[0].minor.yy319)); delete yymsp[-1].minor.yy319; delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; - case 408: /* cmd ::= ANALYZE */ + case 419: /* cmd ::= ANALYZE */ { - yygotominor.yy363 = new SqliteAnalyze(); - objectForTokens = yygotominor.yy363; + yygotominor.yy41 = new SqliteAnalyze(); + objectForTokens = yygotominor.yy41; } break; - case 409: /* cmd ::= ANALYZE nm dbnm */ + case 420: /* cmd ::= ANALYZE nm dbnm */ { - yygotominor.yy363 = new SqliteAnalyze(*(yymsp[-1].minor.yy319), *(yymsp[0].minor.yy319)); + yygotominor.yy41 = new SqliteAnalyze(*(yymsp[-1].minor.yy319), *(yymsp[0].minor.yy319)); delete yymsp[-1].minor.yy319; delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; - case 412: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 423: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - yygotominor.yy363 = new SqliteAlterTable( - yymsp[-3].minor.yy440->name1, - yymsp[-3].minor.yy440->name2, + yygotominor.yy41 = new SqliteAlterTable( + yymsp[-3].minor.yy396->name1, + yymsp[-3].minor.yy396->name2, *(yymsp[0].minor.yy319) ); delete yymsp[0].minor.yy319; - delete yymsp[-3].minor.yy440; - objectForTokens = yygotominor.yy363; + delete yymsp[-3].minor.yy396; + objectForTokens = yygotominor.yy41; } break; - case 413: /* cmd ::= ALTER TABLE fullname ADD kwcolumn_opt column */ + case 424: /* cmd ::= ALTER TABLE fullname ADD kwcolumn_opt column */ { - yygotominor.yy363 = new SqliteAlterTable( - yymsp[-3].minor.yy440->name1, - yymsp[-3].minor.yy440->name2, - *(yymsp[-1].minor.yy611), - yymsp[0].minor.yy147 + yygotominor.yy41 = new SqliteAlterTable( + yymsp[-3].minor.yy396->name1, + yymsp[-3].minor.yy396->name2, + *(yymsp[-1].minor.yy225), + yymsp[0].minor.yy3 ); - delete yymsp[-1].minor.yy611; - delete yymsp[-3].minor.yy440; - objectForTokens = yygotominor.yy363; + delete yymsp[-1].minor.yy225; + delete yymsp[-3].minor.yy396; + objectForTokens = yygotominor.yy41; } break; - case 414: /* cmd ::= ALTER TABLE fullname RENAME TO ID_TAB_NEW */ -{ yy_destructor(yypParser,203,&yymsp[-3].minor); + case 425: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ +{ + yygotominor.yy41 = new SqliteAlterTable( + yymsp[-3].minor.yy396->name1, + yymsp[-3].minor.yy396->name2, + *(yymsp[-1].minor.yy225), + *(yymsp[0].minor.yy319) + ); + delete yymsp[-1].minor.yy225; + delete yymsp[-3].minor.yy396; + delete yymsp[0].minor.yy319; + } + break; + case 426: /* cmd ::= ALTER TABLE fullname RENAME TO ID_TAB_NEW */ +{ yy_destructor(yypParser,207,&yymsp[-3].minor); } break; - case 420: /* create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + case 432: /* create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { - yygotominor.yy363 = new SqliteCreateVirtualTable( - *(yymsp[-4].minor.yy611), + yygotominor.yy41 = new SqliteCreateVirtualTable( + *(yymsp[-4].minor.yy225), *(yymsp[-3].minor.yy319), *(yymsp[-2].minor.yy319), *(yymsp[0].minor.yy319) ); - delete yymsp[-4].minor.yy611; + delete yymsp[-4].minor.yy225; delete yymsp[-3].minor.yy319; delete yymsp[-2].minor.yy319; delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy363; + objectForTokens = yygotominor.yy41; } break; - case 421: /* create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm dbnm USING nm LP vtabarglist RP */ + case 433: /* create_vtab ::= CREATE VIRTUAL TABLE ifnotexists nm dbnm USING nm LP vtabarglist RP */ { - yygotominor.yy363 = new SqliteCreateVirtualTable( - *(yymsp[-7].minor.yy611), + yygotominor.yy41 = new SqliteCreateVirtualTable( + *(yymsp[-7].minor.yy225), *(yymsp[-6].minor.yy319), *(yymsp[-5].minor.yy319), *(yymsp[-3].minor.yy319), - *(yymsp[-1].minor.yy575) + *(yymsp[-1].minor.yy173) ); delete yymsp[-6].minor.yy319; delete yymsp[-5].minor.yy319; delete yymsp[-3].minor.yy319; - delete yymsp[-7].minor.yy611; - delete yymsp[-1].minor.yy575; - objectForTokens = yygotominor.yy363; + delete yymsp[-7].minor.yy225; + delete yymsp[-1].minor.yy173; + objectForTokens = yygotominor.yy41; } break; - case 424: /* vtabarglist ::= vtabarg */ + case 436: /* vtabarglist ::= vtabarg */ { - yygotominor.yy575 = new QStringList(); - yygotominor.yy575->append((yymsp[0].minor.yy319)->mid(1)); // mid(1) to skip the first whitespace added in vtabarg + yygotominor.yy173 = new QStringList(); + yygotominor.yy173->append((yymsp[0].minor.yy319)->mid(1)); // mid(1) to skip the first whitespace added in vtabarg delete yymsp[0].minor.yy319; } break; - case 425: /* vtabarglist ::= vtabarglist COMMA vtabarg */ + case 437: /* vtabarglist ::= vtabarglist COMMA vtabarg */ { - yymsp[-2].minor.yy575->append((yymsp[0].minor.yy319)->mid(1)); // mid(1) to skip the first whitespace added in vtabarg - yygotominor.yy575 = yymsp[-2].minor.yy575; + yymsp[-2].minor.yy173->append((yymsp[0].minor.yy319)->mid(1)); // mid(1) to skip the first whitespace added in vtabarg + yygotominor.yy173 = yymsp[-2].minor.yy173; delete yymsp[0].minor.yy319; DONT_INHERIT_TOKENS("vtabarglist"); } break; - case 427: /* vtabarg ::= vtabarg vtabargtoken */ + case 439: /* vtabarg ::= vtabarg vtabargtoken */ { yymsp[-1].minor.yy319->append(" "+ *(yymsp[0].minor.yy319)); yygotominor.yy319 = yymsp[-1].minor.yy319; delete yymsp[0].minor.yy319; } break; - case 428: /* vtabargtoken ::= ANY */ + case 440: /* vtabargtoken ::= ANY */ { yygotominor.yy319 = new QString(yymsp[0].minor.yy0->value); } break; - case 429: /* vtabargtoken ::= LP anylist RP */ + case 441: /* vtabargtoken ::= LP anylist RP */ { yygotominor.yy319 = new QString("("); yygotominor.yy319->append(*(yymsp[-1].minor.yy319)); @@ -5391,7 +5525,7 @@ static void yy_reduce( delete yymsp[-1].minor.yy319; } break; - case 431: /* anylist ::= anylist LP anylist RP */ + case 443: /* anylist ::= anylist LP anylist RP */ { yygotominor.yy319 = yymsp[-3].minor.yy319; yygotominor.yy319->append("("); @@ -5401,258 +5535,268 @@ static void yy_reduce( DONT_INHERIT_TOKENS("anylist"); } break; - case 432: /* anylist ::= anylist ANY */ + case 444: /* anylist ::= anylist ANY */ { yygotominor.yy319 = yymsp[-1].minor.yy319; yygotominor.yy319->append(yymsp[0].minor.yy0->value); DONT_INHERIT_TOKENS("anylist"); } break; - case 433: /* with ::= */ -{yygotominor.yy1 = nullptr;} + case 445: /* with ::= */ +{yygotominor.yy161 = nullptr;} break; - case 434: /* with ::= WITH wqlist */ + case 446: /* with ::= WITH wqlist */ { - yygotominor.yy1 = new SqliteWith(); - yygotominor.yy1->cteList = *(yymsp[0].minor.yy593); - delete yymsp[0].minor.yy593; - objectForTokens = yygotominor.yy1; + yygotominor.yy161 = new SqliteWith(); + yygotominor.yy161->cteList = *(yymsp[0].minor.yy164); + delete yymsp[0].minor.yy164; + objectForTokens = yygotominor.yy161; } break; - case 435: /* with ::= WITH RECURSIVE wqlist */ + case 447: /* with ::= WITH RECURSIVE wqlist */ { - yygotominor.yy1 = new SqliteWith(); - yygotominor.yy1->cteList = *(yymsp[0].minor.yy593); - yygotominor.yy1->recursive = true; - delete yymsp[0].minor.yy593; - objectForTokens = yygotominor.yy1; + yygotominor.yy161 = new SqliteWith(); + yygotominor.yy161->cteList = *(yymsp[0].minor.yy164); + yygotominor.yy161->recursive = true; + delete yymsp[0].minor.yy164; + objectForTokens = yygotominor.yy161; } break; - case 436: /* wqlist ::= wqcte */ + case 448: /* wqas ::= AS */ +{yygotominor.yy21 = new SqliteWith::CommonTableExpression::AsMode(SqliteWith::CommonTableExpression::ANY);} + break; + case 449: /* wqas ::= AS MATERIALIZED */ +{yygotominor.yy21 = new SqliteWith::CommonTableExpression::AsMode(SqliteWith::CommonTableExpression::MATERIALIZED);} + break; + case 450: /* wqas ::= AS NOT MATERIALIZED */ +{yygotominor.yy21 = new SqliteWith::CommonTableExpression::AsMode(SqliteWith::CommonTableExpression::NOT_MATERIALIZED);} + break; + case 451: /* wqlist ::= wqcte */ { - yygotominor.yy593 = new ParserCteList(); - yygotominor.yy593->append(yymsp[0].minor.yy446); + yygotominor.yy164 = new ParserCteList(); + yygotominor.yy164->append(yymsp[0].minor.yy146); } break; - case 437: /* wqlist ::= wqlist COMMA wqcte */ + case 452: /* wqlist ::= wqlist COMMA wqcte */ { - yygotominor.yy593 = yymsp[-2].minor.yy593; - yygotominor.yy593->append(yymsp[0].minor.yy446); + yygotominor.yy164 = yymsp[-2].minor.yy164; + yygotominor.yy164->append(yymsp[0].minor.yy146); DONT_INHERIT_TOKENS("wqlist"); } break; - case 438: /* wqlist ::= ID_TAB_NEW */ + case 453: /* wqlist ::= ID_TAB_NEW */ { parserContext->minorErrorBeforeNextToken("Syntax error"); } break; - case 439: /* wqcte ::= nm idxlist_opt AS LP select RP */ + case 454: /* wqcte ::= nm idxlist_opt wqas LP select RP */ { - yygotominor.yy446 = new SqliteWith::CommonTableExpression(*(yymsp[-5].minor.yy319), *(yymsp[-4].minor.yy223), yymsp[-1].minor.yy313); + yygotominor.yy146 = new SqliteWith::CommonTableExpression(*(yymsp[-5].minor.yy319), *(yymsp[-4].minor.yy627), yymsp[-1].minor.yy297, *(yymsp[-3].minor.yy21)); delete yymsp[-5].minor.yy319; - delete yymsp[-4].minor.yy223; - objectForTokens = yygotominor.yy446; + delete yymsp[-4].minor.yy627; + delete yymsp[-3].minor.yy21; + objectForTokens = yygotominor.yy146; } break; - case 440: /* windowdefn_list ::= windowdefn */ + case 455: /* windowdefn_list ::= windowdefn */ { - yygotominor.yy299 = new ParserWindowDefList(); - yygotominor.yy299->append(yymsp[0].minor.yy266); + yygotominor.yy525 = new ParserWindowDefList(); + yygotominor.yy525->append(yymsp[0].minor.yy562); } break; - case 441: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 456: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { - yymsp[-2].minor.yy299->append(yymsp[0].minor.yy266); - yygotominor.yy299 = yymsp[-2].minor.yy299; + yymsp[-2].minor.yy525->append(yymsp[0].minor.yy562); + yygotominor.yy525 = yymsp[-2].minor.yy525; DONT_INHERIT_TOKENS("windowdefn_list"); } break; - case 442: /* windowdefn ::= nm AS LP window RP */ + case 457: /* windowdefn ::= nm AS LP window RP */ { - yygotominor.yy266 = new SqliteWindowDefinition(*(yymsp[-4].minor.yy319), yymsp[-1].minor.yy334); + yygotominor.yy562 = new SqliteWindowDefinition(*(yymsp[-4].minor.yy319), yymsp[-1].minor.yy162); delete yymsp[-4].minor.yy319; - objectForTokens = yygotominor.yy266; + objectForTokens = yygotominor.yy562; } break; - case 443: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + case 458: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ { - yygotominor.yy334 = new SqliteWindowDefinition::Window(); - yygotominor.yy334->initPartitionBy(QString(), *(yymsp[-2].minor.yy71), *(yymsp[-1].minor.yy403), yymsp[0].minor.yy41); - delete yymsp[-2].minor.yy71; - delete yymsp[-1].minor.yy403; - objectForTokens = yygotominor.yy334; + yygotominor.yy162 = new SqliteWindowDefinition::Window(); + yygotominor.yy162->initPartitionBy(QString(), *(yymsp[-2].minor.yy615), *(yymsp[-1].minor.yy226), yymsp[0].minor.yy149); + delete yymsp[-2].minor.yy615; + delete yymsp[-1].minor.yy226; + objectForTokens = yygotominor.yy162; } break; - case 444: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + case 459: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ { - yygotominor.yy334 = new SqliteWindowDefinition::Window(); - yygotominor.yy334->initPartitionBy(*(yymsp[-5].minor.yy319), *(yymsp[-2].minor.yy71), *(yymsp[-1].minor.yy403), yymsp[0].minor.yy41); - delete yymsp[-2].minor.yy71; + yygotominor.yy162 = new SqliteWindowDefinition::Window(); + yygotominor.yy162->initPartitionBy(*(yymsp[-5].minor.yy319), *(yymsp[-2].minor.yy615), *(yymsp[-1].minor.yy226), yymsp[0].minor.yy149); + delete yymsp[-2].minor.yy615; delete yymsp[-5].minor.yy319; - delete yymsp[-1].minor.yy403; - objectForTokens = yygotominor.yy334; + delete yymsp[-1].minor.yy226; + objectForTokens = yygotominor.yy162; } break; - case 445: /* window ::= ORDER BY sortlist frame_opt */ + case 460: /* window ::= ORDER BY sortlist frame_opt */ { - yygotominor.yy334 = new SqliteWindowDefinition::Window(); - yygotominor.yy334->initOrderBy(QString(), *(yymsp[-1].minor.yy403), yymsp[0].minor.yy41); - delete yymsp[-1].minor.yy403; - objectForTokens = yygotominor.yy334; + yygotominor.yy162 = new SqliteWindowDefinition::Window(); + yygotominor.yy162->initOrderBy(QString(), *(yymsp[-1].minor.yy226), yymsp[0].minor.yy149); + delete yymsp[-1].minor.yy226; + objectForTokens = yygotominor.yy162; } break; - case 446: /* window ::= nm ORDER BY sortlist frame_opt */ + case 461: /* window ::= nm ORDER BY sortlist frame_opt */ { - yygotominor.yy334 = new SqliteWindowDefinition::Window(); - yygotominor.yy334->initOrderBy(*(yymsp[-4].minor.yy319), *(yymsp[-1].minor.yy403), yymsp[0].minor.yy41); - delete yymsp[-1].minor.yy403; + yygotominor.yy162 = new SqliteWindowDefinition::Window(); + yygotominor.yy162->initOrderBy(*(yymsp[-4].minor.yy319), *(yymsp[-1].minor.yy226), yymsp[0].minor.yy149); + delete yymsp[-1].minor.yy226; delete yymsp[-4].minor.yy319; - objectForTokens = yygotominor.yy334; + objectForTokens = yygotominor.yy162; } break; - case 447: /* window ::= frame_opt */ + case 462: /* window ::= frame_opt */ { - yygotominor.yy334 = new SqliteWindowDefinition::Window(); - yygotominor.yy334->init(QString(), yymsp[0].minor.yy41); - objectForTokens = yygotominor.yy334; + yygotominor.yy162 = new SqliteWindowDefinition::Window(); + yygotominor.yy162->init(QString(), yymsp[0].minor.yy149); + objectForTokens = yygotominor.yy162; } break; - case 448: /* window ::= nm frame_opt */ + case 463: /* window ::= nm frame_opt */ { - yygotominor.yy334 = new SqliteWindowDefinition::Window(); - yygotominor.yy334->init(QString(), yymsp[0].minor.yy41); + yygotominor.yy162 = new SqliteWindowDefinition::Window(); + yygotominor.yy162->init(QString(), yymsp[0].minor.yy149); delete yymsp[-1].minor.yy319; - objectForTokens = yygotominor.yy334; + objectForTokens = yygotominor.yy162; } break; - case 449: /* frame_opt ::= */ -{yygotominor.yy41 = nullptr;} + case 464: /* frame_opt ::= */ +{yygotominor.yy149 = nullptr;} break; - case 450: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + case 465: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ { - yygotominor.yy41 = new SqliteWindowDefinition::Window::Frame(*(yymsp[-2].minor.yy419), yymsp[-1].minor.yy442, nullptr, *(yymsp[0].minor.yy63)); - delete yymsp[-2].minor.yy419; - delete yymsp[0].minor.yy63; - objectForTokens = yygotominor.yy41; + yygotominor.yy149 = new SqliteWindowDefinition::Window::Frame(*(yymsp[-2].minor.yy143), yymsp[-1].minor.yy285, nullptr, *(yymsp[0].minor.yy237)); + delete yymsp[-2].minor.yy143; + delete yymsp[0].minor.yy237; + objectForTokens = yygotominor.yy149; } break; - case 451: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + case 466: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ { - yygotominor.yy41 = new SqliteWindowDefinition::Window::Frame(*(yymsp[-5].minor.yy419), yymsp[-3].minor.yy442, yymsp[-1].minor.yy442, *(yymsp[0].minor.yy63)); - delete yymsp[-5].minor.yy419; - delete yymsp[0].minor.yy63; - objectForTokens = yygotominor.yy41; + yygotominor.yy149 = new SqliteWindowDefinition::Window::Frame(*(yymsp[-5].minor.yy143), yymsp[-3].minor.yy285, yymsp[-1].minor.yy285, *(yymsp[0].minor.yy237)); + delete yymsp[-5].minor.yy143; + delete yymsp[0].minor.yy237; + objectForTokens = yygotominor.yy149; } break; - case 452: /* range_or_rows ::= RANGE|ROWS|GROUPS */ + case 467: /* range_or_rows ::= RANGE|ROWS|GROUPS */ { - yygotominor.yy419 = new SqliteWindowDefinition::Window::Frame::RangeOrRows( + yygotominor.yy143 = new SqliteWindowDefinition::Window::Frame::RangeOrRows( SqliteWindowDefinition::Window::Frame::toRangeOrRows(yymsp[0].minor.yy0->value) ); } break; - case 453: /* frame_bound_s ::= frame_bound */ - case 455: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==455); + case 468: /* frame_bound_s ::= frame_bound */ + case 470: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==470); { - yygotominor.yy442 = yymsp[0].minor.yy442; - objectForTokens = yygotominor.yy442; + yygotominor.yy285 = yymsp[0].minor.yy285; + objectForTokens = yygotominor.yy285; } break; - case 454: /* frame_bound_s ::= UNBOUNDED PRECEDING */ - case 456: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==456); - case 458: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==458); + case 469: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 471: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==471); + case 473: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==473); { - yygotominor.yy442 = new SqliteWindowDefinition::Window::Frame::Bound(nullptr, yymsp[-1].minor.yy0->value + " " + yymsp[0].minor.yy0->value); - objectForTokens = yygotominor.yy442; + yygotominor.yy285 = new SqliteWindowDefinition::Window::Frame::Bound(nullptr, yymsp[-1].minor.yy0->value + " " + yymsp[0].minor.yy0->value); + objectForTokens = yygotominor.yy285; } break; - case 457: /* frame_bound ::= expr PRECEDING|FOLLOWING */ + case 472: /* frame_bound ::= expr PRECEDING|FOLLOWING */ { - yygotominor.yy442 = new SqliteWindowDefinition::Window::Frame::Bound(yymsp[-1].minor.yy512, yymsp[0].minor.yy0->value); - objectForTokens = yygotominor.yy442; + yygotominor.yy285 = new SqliteWindowDefinition::Window::Frame::Bound(yymsp[-1].minor.yy186, yymsp[0].minor.yy0->value); + objectForTokens = yygotominor.yy285; } break; - case 459: /* frame_exclude_opt ::= */ + case 474: /* frame_exclude_opt ::= */ { - yygotominor.yy63 = new SqliteWindowDefinition::Window::Frame::Exclude( + yygotominor.yy237 = new SqliteWindowDefinition::Window::Frame::Exclude( SqliteWindowDefinition::Window::Frame::Exclude::null ); } break; - case 460: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ + case 475: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ { - yygotominor.yy63 = yymsp[0].minor.yy63; + yygotominor.yy237 = yymsp[0].minor.yy237; } break; - case 461: /* frame_exclude ::= NO OTHERS */ + case 476: /* frame_exclude ::= NO OTHERS */ { - yygotominor.yy63 = new SqliteWindowDefinition::Window::Frame::Exclude( + yygotominor.yy237 = new SqliteWindowDefinition::Window::Frame::Exclude( SqliteWindowDefinition::Window::Frame::Exclude::NO_OTHERS ); } break; - case 462: /* frame_exclude ::= CURRENT ROW */ + case 477: /* frame_exclude ::= CURRENT ROW */ { - yygotominor.yy63 = new SqliteWindowDefinition::Window::Frame::Exclude( + yygotominor.yy237 = new SqliteWindowDefinition::Window::Frame::Exclude( SqliteWindowDefinition::Window::Frame::Exclude::CURRENT_ROW ); } break; - case 463: /* frame_exclude ::= GROUP */ + case 478: /* frame_exclude ::= GROUP */ { - yygotominor.yy63 = new SqliteWindowDefinition::Window::Frame::Exclude( + yygotominor.yy237 = new SqliteWindowDefinition::Window::Frame::Exclude( SqliteWindowDefinition::Window::Frame::Exclude::GROUP ); } break; - case 464: /* frame_exclude ::= TIES */ + case 479: /* frame_exclude ::= TIES */ { - yygotominor.yy63 = new SqliteWindowDefinition::Window::Frame::Exclude( + yygotominor.yy237 = new SqliteWindowDefinition::Window::Frame::Exclude( SqliteWindowDefinition::Window::Frame::Exclude::TIES ); } break; - case 465: /* window_clause ::= WINDOW windowdefn_list */ + case 480: /* window_clause ::= WINDOW windowdefn_list */ { - yygotominor.yy299 = yymsp[0].minor.yy299; + yygotominor.yy525 = yymsp[0].minor.yy525; } break; - case 466: /* filter_over ::= filter_clause over_clause */ + case 481: /* filter_over ::= filter_clause over_clause */ { - yygotominor.yy247 = new SqliteFilterOver(yymsp[-1].minor.yy397, yymsp[0].minor.yy248); - objectForTokens = yygotominor.yy247; + yygotominor.yy181 = new SqliteFilterOver(yymsp[-1].minor.yy39, yymsp[0].minor.yy11); + objectForTokens = yygotominor.yy181; } break; - case 467: /* filter_over ::= over_clause */ + case 482: /* filter_over ::= over_clause */ { - yygotominor.yy247 = new SqliteFilterOver(nullptr, yymsp[0].minor.yy248); - objectForTokens = yygotominor.yy247; + yygotominor.yy181 = new SqliteFilterOver(nullptr, yymsp[0].minor.yy11); + objectForTokens = yygotominor.yy181; } break; - case 468: /* filter_over ::= filter_clause */ + case 483: /* filter_over ::= filter_clause */ { - yygotominor.yy247 = new SqliteFilterOver(yymsp[0].minor.yy397, nullptr); - objectForTokens = yygotominor.yy247; + yygotominor.yy181 = new SqliteFilterOver(yymsp[0].minor.yy39, nullptr); + objectForTokens = yygotominor.yy181; } break; - case 469: /* over_clause ::= OVER LP window RP */ + case 484: /* over_clause ::= OVER LP window RP */ { - yygotominor.yy248 = new SqliteFilterOver::Over(yymsp[-1].minor.yy334); - objectForTokens = yygotominor.yy248; + yygotominor.yy11 = new SqliteFilterOver::Over(yymsp[-1].minor.yy162); + objectForTokens = yygotominor.yy11; } break; - case 470: /* over_clause ::= OVER nm */ + case 485: /* over_clause ::= OVER nm */ { - yygotominor.yy248 = new SqliteFilterOver::Over(*(yymsp[0].minor.yy319)); + yygotominor.yy11 = new SqliteFilterOver::Over(*(yymsp[0].minor.yy319)); delete yymsp[0].minor.yy319; - objectForTokens = yygotominor.yy248; + objectForTokens = yygotominor.yy11; } break; - case 471: /* filter_clause ::= FILTER LP WHERE expr RP */ + case 486: /* filter_clause ::= FILTER LP WHERE expr RP */ { - yygotominor.yy397 = new SqliteFilterOver::Filter(yymsp[-1].minor.yy512); - objectForTokens = yygotominor.yy397; - } + yygotominor.yy39 = new SqliteFilterOver::Filter(yymsp[-1].minor.yy186); + objectForTokens = yygotominor.yy39; + } break; default: /* (0) input ::= cmdlist */ yytestcase(yyruleno==0); @@ -5746,7 +5890,7 @@ static void yy_reduce( #endif { yy_shift(yypParser,yyact,yygoto,&yygotominor); - if (parserContext->setupTokens) + if (parserContext->setupTokens && yypParser->yyidx >= 0) { QList* tokensPtr = yypParser->yystack[yypParser->yyidx].tokens; *tokensPtr = allTokensWithAllInherited + *tokensPtr; @@ -5988,4 +6132,8 @@ void sqlite3_parse( } }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); return; -} \ No newline at end of file +} + +int sqlite3ParserFallback(int iToken) { + return yyFallback[iToken]; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.h b/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.h index 200b09c..6dedaad 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.h @@ -41,149 +41,153 @@ #define TK3_LAST 41 #define TK3_LIKE_KW 42 #define TK3_MATCH 43 -#define TK3_NO 44 -#define TK3_NULLS 45 -#define TK3_OTHERS 46 -#define TK3_PLAN 47 -#define TK3_QUERY 48 -#define TK3_KEY 49 -#define TK3_OF 50 -#define TK3_OFFSET 51 -#define TK3_PARTITION 52 -#define TK3_PRAGMA 53 -#define TK3_PRECEDING 54 -#define TK3_RAISE 55 -#define TK3_RANGE 56 -#define TK3_RECURSIVE 57 -#define TK3_RELEASE 58 -#define TK3_REPLACE 59 -#define TK3_RESTRICT 60 -#define TK3_ROW 61 -#define TK3_ROWS 62 -#define TK3_ROLLBACK 63 -#define TK3_SAVEPOINT 64 -#define TK3_TEMP 65 -#define TK3_TIES 66 -#define TK3_TRIGGER 67 -#define TK3_UNBOUNDED 68 -#define TK3_VACUUM 69 -#define TK3_VIEW 70 -#define TK3_VIRTUAL 71 -#define TK3_WITH 72 -#define TK3_WITHOUT 73 -#define TK3_REINDEX 74 -#define TK3_RENAME 75 -#define TK3_CTIME_KW 76 -#define TK3_IF 77 -#define TK3_ANY 78 -#define TK3_OR 79 -#define TK3_AND 80 -#define TK3_NOT 81 -#define TK3_IS 82 -#define TK3_BETWEEN 83 -#define TK3_IN 84 -#define TK3_ISNULL 85 -#define TK3_NOTNULL 86 -#define TK3_NE 87 -#define TK3_EQ 88 -#define TK3_GT 89 -#define TK3_LE 90 -#define TK3_LT 91 -#define TK3_GE 92 -#define TK3_ESCAPE 93 -#define TK3_BITAND 94 -#define TK3_BITOR 95 -#define TK3_LSHIFT 96 -#define TK3_RSHIFT 97 -#define TK3_PLUS 98 -#define TK3_MINUS 99 -#define TK3_STAR 100 -#define TK3_SLASH 101 -#define TK3_REM 102 -#define TK3_CONCAT 103 -#define TK3_COLLATE 104 -#define TK3_BITNOT 105 -#define TK3_SEMI 106 -#define TK3_TRANSACTION 107 -#define TK3_ID_TRANS 108 -#define TK3_COMMIT 109 -#define TK3_TO 110 -#define TK3_CREATE 111 -#define TK3_TABLE 112 -#define TK3_LP 113 -#define TK3_RP 114 -#define TK3_AS 115 -#define TK3_DOT 116 -#define TK3_ID_TAB_NEW 117 -#define TK3_ID_DB 118 -#define TK3_CTX_ROWID_KW 119 -#define TK3_EXISTS 120 -#define TK3_COMMA 121 -#define TK3_ID_COL_NEW 122 -#define TK3_STRING 123 -#define TK3_JOIN_KW 124 -#define TK3_ID_COL_TYPE 125 -#define TK3_RIGHT_ASSOC 126 -#define TK3_CONSTRAINT 127 -#define TK3_DEFAULT 128 -#define TK3_NULL 129 -#define TK3_PRIMARY 130 -#define TK3_UNIQUE 131 -#define TK3_CHECK 132 -#define TK3_REFERENCES 133 -#define TK3_ID_CONSTR 134 -#define TK3_ID_COLLATE 135 -#define TK3_ID_TAB 136 -#define TK3_INTEGER 137 -#define TK3_FLOAT 138 -#define TK3_BLOB 139 -#define TK3_AUTOINCR 140 -#define TK3_ON 141 -#define TK3_INSERT 142 -#define TK3_DELETE 143 -#define TK3_UPDATE 144 -#define TK3_ID_FK_MATCH 145 -#define TK3_SET 146 -#define TK3_DEFERRABLE 147 -#define TK3_FOREIGN 148 -#define TK3_DROP 149 -#define TK3_ID_VIEW_NEW 150 -#define TK3_ID_VIEW 151 -#define TK3_SELECT 152 -#define TK3_VALUES 153 -#define TK3_UNION 154 -#define TK3_ALL 155 -#define TK3_EXCEPT 156 -#define TK3_INTERSECT 157 -#define TK3_DISTINCT 158 -#define TK3_ID_ALIAS 159 -#define TK3_FROM 160 -#define TK3_USING 161 -#define TK3_JOIN 162 -#define TK3_ID_JOIN_OPTS 163 -#define TK3_ID_IDX 164 -#define TK3_ORDER 165 -#define TK3_GROUP 166 -#define TK3_HAVING 167 -#define TK3_LIMIT 168 -#define TK3_WHERE 169 -#define TK3_ID_COL 170 -#define TK3_INTO 171 -#define TK3_NOTHING 172 -#define TK3_ID_FN 173 -#define TK3_ID_ERR_MSG 174 -#define TK3_VARIABLE 175 -#define TK3_CASE 176 -#define TK3_WHEN 177 -#define TK3_THEN 178 -#define TK3_ELSE 179 -#define TK3_INDEX 180 -#define TK3_ID_IDX_NEW 181 -#define TK3_ID_PRAGMA 182 -#define TK3_ID_TRIG_NEW 183 -#define TK3_ID_TRIG 184 -#define TK3_ALTER 185 -#define TK3_ADD 186 -#define TK3_WINDOW 187 -#define TK3_OVER 188 -#define TK3_FILTER 189 +#define TK3_MATERIALIZED 44 +#define TK3_NO 45 +#define TK3_NULLS 46 +#define TK3_OTHERS 47 +#define TK3_PLAN 48 +#define TK3_QUERY 49 +#define TK3_KEY 50 +#define TK3_OF 51 +#define TK3_OFFSET 52 +#define TK3_PARTITION 53 +#define TK3_PRAGMA 54 +#define TK3_PRECEDING 55 +#define TK3_RAISE 56 +#define TK3_RANGE 57 +#define TK3_RECURSIVE 58 +#define TK3_RELEASE 59 +#define TK3_REPLACE 60 +#define TK3_RESTRICT 61 +#define TK3_ROW 62 +#define TK3_ROWS 63 +#define TK3_ROLLBACK 64 +#define TK3_SAVEPOINT 65 +#define TK3_TEMP 66 +#define TK3_TIES 67 +#define TK3_TRIGGER 68 +#define TK3_UNBOUNDED 69 +#define TK3_VACUUM 70 +#define TK3_VIEW 71 +#define TK3_VIRTUAL 72 +#define TK3_WITH 73 +#define TK3_WITHOUT 74 +#define TK3_REINDEX 75 +#define TK3_RENAME 76 +#define TK3_CTIME_KW 77 +#define TK3_IF 78 +#define TK3_FILTER 79 +#define TK3_ANY 80 +#define TK3_OR 81 +#define TK3_AND 82 +#define TK3_NOT 83 +#define TK3_IS 84 +#define TK3_BETWEEN 85 +#define TK3_IN 86 +#define TK3_ISNULL 87 +#define TK3_NOTNULL 88 +#define TK3_NE 89 +#define TK3_EQ 90 +#define TK3_GT 91 +#define TK3_LE 92 +#define TK3_LT 93 +#define TK3_GE 94 +#define TK3_ESCAPE 95 +#define TK3_BITAND 96 +#define TK3_BITOR 97 +#define TK3_LSHIFT 98 +#define TK3_RSHIFT 99 +#define TK3_PLUS 100 +#define TK3_MINUS 101 +#define TK3_STAR 102 +#define TK3_SLASH 103 +#define TK3_REM 104 +#define TK3_CONCAT 105 +#define TK3_PTR 106 +#define TK3_COLLATE 107 +#define TK3_BITNOT 108 +#define TK3_SEMI 109 +#define TK3_TRANSACTION 110 +#define TK3_ID_TRANS 111 +#define TK3_COMMIT 112 +#define TK3_TO 113 +#define TK3_CREATE 114 +#define TK3_TABLE 115 +#define TK3_LP 116 +#define TK3_RP 117 +#define TK3_AS 118 +#define TK3_DOT 119 +#define TK3_ID_TAB_NEW 120 +#define TK3_ID_DB 121 +#define TK3_COMMA 122 +#define TK3_CTX_ROWID_KW 123 +#define TK3_CTX_STRICT_KW 124 +#define TK3_EXISTS 125 +#define TK3_ID_COL_NEW 126 +#define TK3_STRING 127 +#define TK3_JOIN_KW 128 +#define TK3_ID_COL_TYPE 129 +#define TK3_RIGHT_ASSOC 130 +#define TK3_CONSTRAINT 131 +#define TK3_DEFAULT 132 +#define TK3_NULL 133 +#define TK3_PRIMARY 134 +#define TK3_UNIQUE 135 +#define TK3_CHECK 136 +#define TK3_REFERENCES 137 +#define TK3_ID_CONSTR 138 +#define TK3_ID_COLLATE 139 +#define TK3_ID_TAB 140 +#define TK3_INTEGER 141 +#define TK3_FLOAT 142 +#define TK3_BLOB 143 +#define TK3_AUTOINCR 144 +#define TK3_ON 145 +#define TK3_INSERT 146 +#define TK3_DELETE 147 +#define TK3_UPDATE 148 +#define TK3_ID_FK_MATCH 149 +#define TK3_SET 150 +#define TK3_DEFERRABLE 151 +#define TK3_FOREIGN 152 +#define TK3_DROP 153 +#define TK3_ID_VIEW_NEW 154 +#define TK3_ID_VIEW 155 +#define TK3_SELECT 156 +#define TK3_VALUES 157 +#define TK3_UNION 158 +#define TK3_ALL 159 +#define TK3_EXCEPT 160 +#define TK3_INTERSECT 161 +#define TK3_DISTINCT 162 +#define TK3_ID_ALIAS 163 +#define TK3_FROM 164 +#define TK3_USING 165 +#define TK3_JOIN 166 +#define TK3_ID_JOIN_OPTS 167 +#define TK3_ID_IDX 168 +#define TK3_ORDER 169 +#define TK3_GROUP 170 +#define TK3_HAVING 171 +#define TK3_LIMIT 172 +#define TK3_WHERE 173 +#define TK3_RETURNING 174 +#define TK3_ID_COL 175 +#define TK3_INTO 176 +#define TK3_NOTHING 177 +#define TK3_ID_FN 178 +#define TK3_ID_ERR_MSG 179 +#define TK3_VARIABLE 180 +#define TK3_CASE 181 +#define TK3_WHEN 182 +#define TK3_THEN 183 +#define TK3_ELSE 184 +#define TK3_INDEX 185 +#define TK3_ID_IDX_NEW 186 +#define TK3_ID_PRAGMA 187 +#define TK3_ID_TRIG_NEW 188 +#define TK3_ID_TRIG 189 +#define TK3_ALTER 190 +#define TK3_ADD 191 +#define TK3_WINDOW 192 +#define TK3_OVER 193 diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.y b/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.y index 1510c49..4b1dda4 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.y +++ b/SQLiteStudio3/coreSQLiteStudio/parser/sqlite3_parse.y @@ -27,7 +27,6 @@ #include "parser/ast/sqliteattach.h" #include "parser/ast/sqlitebegintrans.h" #include "parser/ast/sqlitecommittrans.h" -#include "parser/ast/sqlitecopy.h" #include "parser/ast/sqlitecreateindex.h" #include "parser/ast/sqlitecreatetable.h" #include "parser/ast/sqlitecreatetrigger.h" @@ -67,6 +66,13 @@ #define assert(X) Q_ASSERT(X) #define UNUSED_PARAMETER(X) (void)(X) #define DONT_INHERIT_TOKENS(X) noTokenInheritanceFields << X + +} + +%code { +int sqlite3ParserFallback(int iToken) { + return yyFallback[iToken]; +} } // These are extra tokens used by the lexer but never seen by the @@ -84,10 +90,10 @@ %fallback ID ABORT ACTION AFTER ALWAYS ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW CONFLICT CURRENT DATABASE DEFERRED DESC DETACH DO EACH END EXCLUDE EXCLUSIVE EXPLAIN FAIL FIRST FOLLOWING FOR - GENERATED GROUPS IGNORE IMMEDIATE INDEXED INITIALLY INSTEAD LAST LIKE_KW MATCH NO NULLS OTHERS PLAN + GENERATED GROUPS IGNORE IMMEDIATE INDEXED INITIALLY INSTEAD LAST LIKE_KW MATCH MATERIALIZED NO NULLS OTHERS PLAN QUERY KEY OF OFFSET PARTITION PRAGMA PRECEDING RAISE RANGE RECURSIVE RELEASE REPLACE RESTRICT ROW ROWS ROLLBACK SAVEPOINT TEMP TIES TRIGGER UNBOUNDED VACUUM VIEW VIRTUAL WITH WITHOUT - REINDEX RENAME CTIME_KW IF + REINDEX RENAME CTIME_KW IF FILTER . %wildcard ANY. @@ -110,7 +116,7 @@ %left BITAND BITOR LSHIFT RSHIFT. %left PLUS MINUS. %left STAR SLASH REM. -%left CONCAT. +%left CONCAT PTR. %left COLLATE. %right BITNOT. @@ -252,7 +258,7 @@ cmd(X) ::= CREATE temp(T) TABLE ifnotexists(E) fullname(N) LP columnlist(CL) conslist_opt(CS) RP - table_options(F). { + table_options(O). { X = new SqliteCreateTable( *(E), *(T), @@ -260,14 +266,14 @@ cmd(X) ::= CREATE temp(T) TABLE N->name2, *(CL), *(CS), - *(F) + *(O) ); delete E; delete T; delete CL; delete CS; delete N; - delete F; + delete O; objectForTokens = X; } cmd(X) ::= CREATE temp(T) TABLE @@ -290,16 +296,38 @@ cmd ::= CREATE temp TABLE ifnotexists cmd ::= CREATE temp TABLE ifnotexists ID_DB|ID_TAB_NEW. {} -%type table_options {QString*} +%type table_options {ParserCreateTableOptionList*} %destructor table_options {parser_safe_delete($$);} -table_options(X) ::= . {X = new QString();} -table_options(X) ::= WITHOUT nm(N). { +table_options(X) ::= . {X = new ParserCreateTableOptionList();} +table_options(X) ::= table_option(O). { + X = new ParserCreateTableOptionList(); + X->append(O); + } +table_options(X) ::= table_options(L) COMMA + table_option(O). { + L->append(O); + X = L; + DONT_INHERIT_TOKENS("table_options"); + } + +%type table_option {ParserStubCreateTableOption*} +%destructor table_option {parser_safe_delete($$);} +table_option(X) ::= WITHOUT nm(N). { if (N->toLower() != "rowid") parserContext->errorAtToken(QString("Invalid table option: %1").arg(*(N))); - X = N; + X = new ParserStubCreateTableOption(ParserStubCreateTableOption::WITHOUT_ROWID); + delete N; + } +table_option(X) ::= nm(N). { + if (N->toLower() != "strict") + parserContext->errorAtToken(QString("Invalid table option: %1").arg(*(N))); + + X = new ParserStubCreateTableOption(ParserStubCreateTableOption::STRICT); + delete N; } -table_options ::= WITHOUT CTX_ROWID_KW. {} +table_option ::= WITHOUT CTX_ROWID_KW. {} +table_option ::= CTX_STRICT_KW. {} %type ifnotexists {bool*} %destructor ifnotexists {parser_safe_delete($$);} @@ -568,7 +596,12 @@ ccons(X) ::= CHECK LP RP. { term(X) ::= NULL. {X = new QVariant();} term(X) ::= INTEGER(N). {X = parserContext->handleNumberToken(N->value);} term(X) ::= FLOAT(N). {X = new QVariant(QVariant(N->value).toDouble());} -term(X) ::= STRING|BLOB(S). {X = new QVariant(stripString(S->value));} +term(X) ::= STRING|BLOB(S). { + if (S->value.length() >= 3 && S->value.startsWith("x'", Qt::CaseInsensitive)) + X = new QVariant(blobFromLiteral(S->value)); + else + X = new QVariant(stripString(S->value)); + } // Term Literal or Name. String falls under Term, but can be falled back to Name if necessary in the context. // On the other hand - if name is ID, it cannot be falled back to Term, as ID is never a Literal value. @@ -1277,7 +1310,8 @@ cmd(X) ::= delete_stmt(S). { delete_stmt(X) ::= with(WI) DELETE FROM fullname(N) indexed_opt(I) - where_opt(W). { + where_opt(W) + returning(R). { if (I) { if (!I->indexedBy.isNull()) @@ -1287,7 +1321,8 @@ delete_stmt(X) ::= with(WI) DELETE FROM N->name2, I->indexedBy, W, - WI + WI, + *(R) ); } else @@ -1297,7 +1332,8 @@ delete_stmt(X) ::= with(WI) DELETE FROM N->name2, I->notIndexedKw, W, - WI + WI, + *(R) ); } delete I; @@ -1309,10 +1345,12 @@ delete_stmt(X) ::= with(WI) DELETE FROM N->name2, false, W, - WI + WI, + *(R) ); } delete N; + delete R; // since it's used in trigger: objectForTokens = X; } @@ -1349,6 +1387,11 @@ where_opt(X) ::= WHERE. { X = new SqliteExpr(); } +%type returning {ParserResultColumnList*} +%destructor returning {parser_safe_delete($$);} +returning(X) ::= . {X = new ParserResultColumnList();} +returning(X) ::= RETURNING selcollist(L). {X = L;} + ////////////////////////// The UPDATE command //////////////////////////////// cmd(X) ::= update_stmt(S). { @@ -1361,7 +1404,7 @@ cmd(X) ::= update_stmt(S). { update_stmt(X) ::= with(WI) UPDATE orconf(C) fullname(N) indexed_opt(I) SET setlist(L) from(F) - where_opt(W). { + where_opt(W) returning(R). { X = new SqliteUpdate( *(C), N->name1, @@ -1371,11 +1414,13 @@ update_stmt(X) ::= with(WI) UPDATE orconf(C) *(L), F, W, - WI + WI, + *(R) ); delete C; delete N; delete L; + delete R; if (I) delete I; // since it's used in trigger: @@ -1481,7 +1526,7 @@ cmd(X) ::= insert_stmt(S). { insert_stmt(X) ::= with(W) insert_cmd(C) INTO fullname(N) idlist_opt(I) select(S) - upsert(U). { + upsert(U) returning(R). { X = new SqliteInsert( C->replace, C->orConflict, @@ -1490,29 +1535,33 @@ insert_stmt(X) ::= with(W) insert_cmd(C) *(I), S, W, - U + U, + *(R) ); delete N; delete C; delete I; + delete R; // since it's used in trigger: objectForTokens = X; } insert_stmt(X) ::= with(W) insert_cmd(C) INTO fullname(N) idlist_opt(I) DEFAULT - VALUES. { + VALUES returning(R). { X = new SqliteInsert( C->replace, C->orConflict, N->name1, N->name2, *(I), - W + W, + *(R) ); delete N; delete C; delete I; + delete R; // since it's used in trigger: objectForTokens = X; } @@ -1564,8 +1613,7 @@ upsert(X) ::= . { upsert(X) ::= ON CONFLICT LP sortlist(C) RP where_opt(CW) DO UPDATE SET setlist(S) - where_opt(SW). - { + where_opt(SW). { X = new SqliteUpsert(*(C), CW, *(S), SW); delete C; delete S; @@ -1591,8 +1639,8 @@ exprx ::= expr not_opt IN ID_DB. [IN] {} exprx ::= expr not_opt IN nm DOT ID_TAB. [IN] {} exprx ::= ID_DB|ID_TAB|ID_COL|ID_FN. {} -exprx ::= tnm DOT ID_TAB|ID_COL. {} -exprx ::= tnm DOT nm DOT ID_COL. {} +exprx ::= tnm DOT ID_TAB|ID_COL. {} +exprx ::= tnm DOT nm DOT ID_COL. {} exprx ::= expr COLLATE ID_COLLATE. {} exprx ::= RAISE LP raisetype COMMA ID_ERR_MSG RP. {} @@ -1631,6 +1679,19 @@ exprx(X) ::= tnm(N1) DOT nm(N2). { delete N2; objectForTokens = X; } +exprx(X) ::= tnm(N1) DOT. { + X = new SqliteExpr(); + objectForTokens = X; + if (N1->isName()) + { + X->initId(N1->toName(), QString()); + parserContext->minorErrorAfterLastToken("Syntax error "); + } + else + parserContext->errorAtToken("Syntax error ", -3); + + delete N1; + } exprx(X) ::= tnm(N1) DOT nm(N2) DOT nm(N3). { X = new SqliteExpr(); if (N1->isName()) @@ -1643,6 +1704,20 @@ exprx(X) ::= tnm(N1) DOT nm(N2) DOT nm(N3). { delete N3; objectForTokens = X; } +exprx(X) ::= tnm(N1) DOT nm(N2) DOT. { + X = new SqliteExpr(); + objectForTokens = X; + if (N1->isName()) + { + X->initId(N1->toName(), *(N2), QString()); + parserContext->minorErrorAfterLastToken("Syntax error "); + } + else + parserContext->errorAtToken("Syntax error ", -5); + + delete N1; + delete N2; + } exprx(X) ::= VARIABLE(V). { X = new SqliteExpr(); X->initBindParam(V->value); @@ -1663,14 +1738,14 @@ exprx(X) ::= CAST LP expr(E) AS typetoken(T) exprx(X) ::= ID(I) LP distinct(D) exprlist(L) RP. { X = new SqliteExpr(); - X->initFunction(I->value, *(D), *(L)); + X->initFunction(stripObjName(I->value), *(D), *(L)); delete D; delete L; objectForTokens = X; } exprx(X) ::= ID(I) LP STAR RP. { X = new SqliteExpr(); - X->initFunction(I->value, true); + X->initFunction(stripObjName(I->value), true); objectForTokens = X; } exprx(X) ::= expr(E1) AND(O) expr(E2). { @@ -1752,6 +1827,18 @@ exprx(X) ::= expr(E1) IS not_opt(N) delete N; objectForTokens = X; } +exprx(X) ::= expr(E1) IS NOT DISTINCT FROM + expr(E2). { + X = new SqliteExpr(); + X->initDistinct(E1, true, E2); + objectForTokens = X; + } +exprx(X) ::= expr(E1) IS DISTINCT FROM + expr(E2). { + X = new SqliteExpr(); + X->initDistinct(E1, false, E2); + objectForTokens = X; + } exprx(X) ::= NOT(O) expr(E). { X = new SqliteExpr(); X->initUnaryOp(E, O->value); @@ -1781,6 +1868,11 @@ exprx(X) ::= PLUS(O) expr(E). [BITNOT] { X->initUnaryOp(E, O->value); objectForTokens = X; } +exprx(X) ::= expr(E1) PTR(O) expr(E2). { + X = new SqliteExpr(); + X->initPtrOp(E1, O->value, E2); + objectForTokens = X; + } exprx(X) ::= expr(E1) not_opt(N) BETWEEN expr(E2) AND expr(E3). [BETWEEN] { @@ -1846,7 +1938,7 @@ exprx(X) ::= RAISE LP raisetype(R) COMMA exprx(X) ::= ID(I) LP distinct(D) exprlist(E) RP filter_over(F). { X = new SqliteExpr(); - X->initWindowFunction(I->value, *(D), *(E), F); + X->initWindowFunction(stripObjName(I->value), *(D), *(E), F); delete D; delete E; objectForTokens = X; @@ -1854,7 +1946,7 @@ exprx(X) ::= ID(I) LP distinct(D) exprx(X) ::= ID(I) LP STAR RP filter_over(F). { X = new SqliteExpr(); - X->initWindowFunction(I->value, F); + X->initWindowFunction(stripObjName(I->value), F); objectForTokens = X; } @@ -2378,6 +2470,18 @@ cmd(X) ::= ALTER TABLE fullname(FN) ADD delete FN; objectForTokens = X; } +cmd(X) ::= ALTER TABLE fullname(FN) DROP + kwcolumn_opt(K) nm(N). { + X = new SqliteAlterTable( + FN->name1, + FN->name2, + *(K), + *(N) + ); + delete K; + delete FN; + delete N; + } cmd ::= ALTER TABLE fullname RENAME TO ID_TAB_NEW. {} @@ -2508,6 +2612,12 @@ with(X) ::= WITH RECURSIVE wqlist(W). { objectForTokens = X; } +%type wqas {SqliteWith::CommonTableExpression::AsMode*} +%destructor wqas {parser_safe_delete($$);} +wqas(X) ::= AS. {X = new SqliteWith::CommonTableExpression::AsMode(SqliteWith::CommonTableExpression::ANY);} +wqas(X) ::= AS MATERIALIZED. {X = new SqliteWith::CommonTableExpression::AsMode(SqliteWith::CommonTableExpression::MATERIALIZED);} +wqas(X) ::= AS NOT MATERIALIZED. {X = new SqliteWith::CommonTableExpression::AsMode(SqliteWith::CommonTableExpression::NOT_MATERIALIZED);} + %type wqlist {ParserCteList*} %destructor wqlist {parser_safe_delete($$);} @@ -2527,11 +2637,12 @@ wqlist ::= ID_TAB_NEW. { %type wqcte {SqliteWith::CommonTableExpression*} %destructor wqcte {parser_safe_delete($$);} -wqcte(X) ::= nm(N) idxlist_opt(IL) AS +wqcte(X) ::= nm(N) idxlist_opt(IL) wqas(A) LP select(S) RP. { - X = new SqliteWith::CommonTableExpression(*(N), *(IL), S); + X = new SqliteWith::CommonTableExpression(*(N), *(IL), S, *(A)); delete N; delete IL; + delete A; objectForTokens = X; } @@ -2764,4 +2875,4 @@ filter_clause(X) ::= FILTER LP WHERE expr(E) RP. { X = new SqliteFilterOver::Filter(E); objectForTokens = X; - } + } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/statementtokenbuilder.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/statementtokenbuilder.cpp index faf8c8e..abe2578 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/statementtokenbuilder.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/statementtokenbuilder.cpp @@ -1,6 +1,7 @@ #include "statementtokenbuilder.h" #include "parser/ast/sqlitestatement.h" #include "common/utils_sql.h" +#include "common/global.h" #include StatementTokenBuilder& StatementTokenBuilder::withKeyword(const QString& value) @@ -23,7 +24,7 @@ StatementTokenBuilder&StatementTokenBuilder::withStringPossiblyOther(const QStri if (value.contains("\"")) return withOther(wrapObjIfNeeded(value)); else - return withOther(wrapObjName(value, NameWrapper::DOUBLE_QUOTE)); + return withOther(wrapObjName(value, NameWrapper::DOUBLE_QUOTE), false); } StatementTokenBuilder& StatementTokenBuilder::withOtherList(const QList& value, const QString& separator) @@ -91,7 +92,7 @@ StatementTokenBuilder& StatementTokenBuilder::withBlob(const QString& value) StatementTokenBuilder& StatementTokenBuilder::withString(const QString& value) { - return with(Token::STRING, wrapStringIfNeeded(value)); + return with(Token::STRING, wrapString(value)); } StatementTokenBuilder& StatementTokenBuilder::withConflict(SqliteConflictAlgo onConflict) @@ -145,6 +146,13 @@ StatementTokenBuilder& StatementTokenBuilder::withLiteralValue(const QVariant& v return *this; } + if (value.userType() == QVariant::ByteArray) + { + static_qstring(blobLiteral, "X'%1'"); + withBlob(blobLiteral.arg(QString::fromLatin1(value.toByteArray().toHex()))); + return *this; + } + bool ok; if (value.userType() == QVariant::Double) { @@ -163,14 +171,7 @@ StatementTokenBuilder& StatementTokenBuilder::withLiteralValue(const QVariant& v return *this; } - QString str = value.toString(); - if (str.startsWith("x'", Qt::CaseInsensitive) && str.endsWith("'")) - { - withBlob(str); - return *this; - } - - withString(str); + withString(value.toString()); return *this; } diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/token.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/token.cpp index 41980b4..84f2023 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/token.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/parser/token.cpp @@ -58,6 +58,8 @@ const QString Token::typeToString(Token::Type type) { case Token::CTX_ROWID_KW: return "CTX_ROWID_KW"; + case Token::CTX_STRICT_KW: + return "CTX_STRICT_KW"; case Token::CTX_NEW_KW: return "CTX_NEW_KW"; case Token::CTX_OLD_KW: diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/token.h b/SQLiteStudio3/coreSQLiteStudio/parser/token.h index 1088d9e..d977963 100644 --- a/SQLiteStudio3/coreSQLiteStudio/parser/token.h +++ b/SQLiteStudio3/coreSQLiteStudio/parser/token.h @@ -101,7 +101,8 @@ struct API_EXPORT Token CTX_ROWID_KW = 0x0034, /**< ROWID keywords is valid at this token position (see isRowIdKeyword()). */ CTX_NEW_KW = 0x0035, /**< The NEW keyword is valid at this token position. */ CTX_OLD_KW = 0x0036, /**< The OLD keyword is valid at this token position. */ - CTX_ERROR_MESSAGE = 0x0037 /**< Error message string is valid at this token position. */ + CTX_ERROR_MESSAGE = 0x0037, /**< Error message string is valid at this token position. */ + CTX_STRICT_KW = 0x0038, /**< STRICT keyword is valid at this token position. */ }; /** diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp index 4a19489..854e7b4 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp @@ -10,6 +10,9 @@ Db* DbPluginSqlite3::getInstance(const QString& name, const QString& path, const if (!db->openForProbing()) { + if (errorMessage) + *errorMessage = db->getErrorText(); + delete db; return nullptr; } @@ -17,9 +20,13 @@ Db* DbPluginSqlite3::getInstance(const QString& name, const QString& path, const SqlQueryPtr results = db->exec("SELECT * FROM sqlite_master"); if (results->isError()) { + if (errorMessage) + *errorMessage = db->getErrorText(); + delete db; return nullptr; } + results.clear(); db->closeQuiet(); return db; diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h index 79b762a..417c93e 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h @@ -10,7 +10,7 @@ class DbPluginSqlite3 : public BuiltInPlugin, public DbPlugin SQLITESTUDIO_PLUGIN_TITLE("SQLite 3") SQLITESTUDIO_PLUGIN_DESC("SQLite 3 databases support.") - SQLITESTUDIO_PLUGIN_VERSION(10000) + SQLITESTUDIO_PLUGIN_VERSION(10001) SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl") public: diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginstdfilebase.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginstdfilebase.cpp index b890c26..5887eac 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginstdfilebase.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginstdfilebase.cpp @@ -11,6 +11,9 @@ Db *DbPluginStdFileBase::getInstance(const QString &name, const QString &path, c if (!db->openForProbing()) { + if (errorMessage) + *errorMessage = db->getErrorText(); + delete db; return nullptr; } @@ -18,6 +21,9 @@ Db *DbPluginStdFileBase::getInstance(const QString &name, const QString &path, c SqlQueryPtr results = db->exec("SELECT * FROM sqlite_master"); if (results->isError()) { + if (errorMessage) + *errorMessage = db->getErrorText(); + delete db; return nullptr; } diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp index 149e92b..e1d9f98 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp @@ -22,7 +22,7 @@ bool PopulateRandomEngine::beforePopulating(Db* db, const QString& table) { UNUSED(db); UNUSED(table); - QRandomGenerator::system()->seed(QDateTime::currentDateTime().toTime_t()); + randomGenerator = QRandomGenerator::securelySeeded(); range = cfg.PopulateRandom.MaxValue.get() - cfg.PopulateRandom.MinValue.get() + 1; return (range > 0); } @@ -30,7 +30,7 @@ bool PopulateRandomEngine::beforePopulating(Db* db, const QString& table) QVariant PopulateRandomEngine::nextValue(bool& nextValueError) { UNUSED(nextValueError); - QString randValue = QString::number((QRandomGenerator::system()->generate() % range) + cfg.PopulateRandom.MinValue.get()); + QString randValue = QString::number((randomGenerator.generate() % range) + cfg.PopulateRandom.MinValue.get()); return (cfg.PopulateRandom.Prefix.get() + randValue + cfg.PopulateRandom.Suffix.get()); } diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h index f4e9feb..29bb02b 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h @@ -4,6 +4,7 @@ #include "builtinplugin.h" #include "populateplugin.h" #include "config_builder.h" +#include CFG_CATEGORIES(PopulateRandomConfig, CFG_CATEGORY(PopulateRandom, @@ -43,5 +44,6 @@ class PopulateRandomEngine : public PopulateEngine private: CFG_LOCAL(PopulateRandomConfig, cfg) int range; + QRandomGenerator randomGenerator; }; #endif // POPULATERANDOM_H diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp index e2f8733..33ac1d6 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp @@ -23,7 +23,7 @@ bool PopulateRandomTextEngine::beforePopulating(Db* db, const QString& table) { UNUSED(db); UNUSED(table); - QRandomGenerator::system()->seed(QDateTime::currentDateTime().toTime_t()); + randomGenerator = QRandomGenerator::securelySeeded(); range = cfg.PopulateRandomText.MaxLength.get() - cfg.PopulateRandomText.MinLength.get() + 1; chars = ""; @@ -55,7 +55,7 @@ bool PopulateRandomTextEngine::beforePopulating(Db* db, const QString& table) QVariant PopulateRandomTextEngine::nextValue(bool& nextValueError) { UNUSED(nextValueError); - int lgt = (QRandomGenerator::system()->generate() % range) + cfg.PopulateRandomText.MinLength.get(); + int lgt = (randomGenerator.generate() % range) + cfg.PopulateRandomText.MinLength.get(); return randStr(lgt, chars); } diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h index 892b302..fdc5c43 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h @@ -4,6 +4,7 @@ #include "builtinplugin.h" #include "populateplugin.h" #include "config_builder.h" +#include CFG_CATEGORIES(PopulateRandomTextConfig, CFG_CATEGORY(PopulateRandomText, @@ -47,6 +48,7 @@ class PopulateRandomTextEngine : public PopulateEngine CFG_LOCAL(PopulateRandomTextConfig, cfg) int range; QString chars; + QRandomGenerator randomGenerator; }; #endif // POPULATERANDOMTEXT_H diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp index 79a8ac1..8c356f9 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp @@ -4,6 +4,27 @@ #include "services/pluginmanager.h" #include "services/notifymanager.h" +class PopulateFunctionInfoImpl : public ScriptingPlugin::FunctionInfo +{ + public: + PopulateFunctionInfoImpl(bool rowCount) + { + args = QStringList({"dbName", "tableName"}); + if (rowCount) + args << "rowCount"; + } + + QString getName() const {return QString();} + QStringList getArguments() const {return args;} + bool getUndefinedArgs() const {return false;} + + private: + QStringList args; +}; + +PopulateFunctionInfoImpl populateInitFunctionInfo(false); +PopulateFunctionInfoImpl populateNextFunctionInfo(true); + PopulateScript::PopulateScript() { } @@ -49,9 +70,9 @@ bool PopulateScriptEngine::beforePopulating(Db* db, const QString& table) if (!initCode.trimmed().isEmpty()) { if (dbAwarePlugin) - dbAwarePlugin->evaluate(context, initCode, evalArgs, db); + dbAwarePlugin->evaluate(context, initCode, populateInitFunctionInfo, evalArgs, db); else - scriptingPlugin->evaluate(context, initCode, evalArgs); + scriptingPlugin->evaluate(context, initCode, populateInitFunctionInfo, evalArgs); if (scriptingPlugin->hasError(context)) { @@ -71,9 +92,9 @@ QVariant PopulateScriptEngine::nextValue(bool& nextValueError) { QVariant result; if (dbAwarePlugin) - result = dbAwarePlugin->evaluate(context, cfg.PopulateScript.Code.get(), evalArgs, db); + result = dbAwarePlugin->evaluate(context, cfg.PopulateScript.Code.get(), populateNextFunctionInfo, evalArgs, db); else - result = scriptingPlugin->evaluate(context, cfg.PopulateScript.Code.get(), evalArgs); + result = scriptingPlugin->evaluate(context, cfg.PopulateScript.Code.get(), populateNextFunctionInfo, evalArgs); if (scriptingPlugin->hasError(context)) { diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h index d081073..80aa758 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h @@ -15,33 +15,45 @@ class ScriptingPlugin : virtual public Plugin virtual ~Context() {} }; + class FunctionInfo + { + public: + virtual QString getName() const = 0; + virtual QStringList getArguments() const = 0; + virtual bool getUndefinedArgs() const = 0; + }; + virtual QString getLanguage() const = 0; virtual Context* createContext() = 0; virtual void releaseContext(Context* context) = 0; virtual void resetContext(Context* context) = 0; virtual void setVariable(Context* context, const QString& name, const QVariant& value) = 0; virtual QVariant getVariable(Context* context, const QString& name) = 0; - virtual QVariant evaluate(Context* context, const QString& code, const QList& args = QList()) = 0; + virtual QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, + const QList& args = QList()) = 0; virtual bool hasError(Context* context) const = 0; virtual QString getErrorMessage(Context* context) const = 0; - virtual QVariant evaluate(const QString& code, const QList& args = QList(), QString* errorMessage = nullptr) = 0; + virtual QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, const QList& args = QList(), + QString* errorMessage = nullptr) = 0; virtual QString getIconPath() const = 0; }; class DbAwareScriptingPlugin : public ScriptingPlugin { public: - virtual QVariant evaluate(Context* context, const QString& code, const QList& args, Db* db, bool locking = false) = 0; - virtual QVariant evaluate(const QString& code, const QList& args, Db* db, bool locking = false, QString* errorMessage = nullptr) = 0; + virtual QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, const QList& args, + Db* db, bool locking = false) = 0; + virtual QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, + bool locking = false, QString* errorMessage = nullptr) = 0; - QVariant evaluate(Context* context, const QString& code, const QList& args) + QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, const QList& args) { - return evaluate(context, code, args, nullptr, true); + return evaluate(context, code, funcInfo, args, nullptr, true); } - QVariant evaluate(const QString& code, const QList& args, QString* errorMessage = nullptr) + QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, const QList& args, QString* errorMessage = nullptr) { - return evaluate(code, args, nullptr, true, errorMessage); + return evaluate(code, funcInfo, args, nullptr, true, errorMessage); } }; diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp index f88fa85..5305f24 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp @@ -1,42 +1,39 @@ #include "scriptingqt.h" -#include "common/unused.h" #include "common/global.h" #include "scriptingqtdbproxy.h" -#include +#include "services/notifymanager.h" +#include #include #include #include -static QScriptValue scriptingQtDebug(QScriptContext *context, QScriptEngine *engine) +ScriptingQt::ScriptingQt() { - UNUSED(engine); - QStringList args; - for (int i = 0; i < context->argumentCount(); i++) - args << context->argument(i).toString(); - - qDebug() << "[ScriptingQt]" << args; - return QScriptValue(); + managedMainContextsMutex = new QMutex(); } -ScriptingQt::ScriptingQt() +ScriptingQt::~ScriptingQt() { - mainEngineMutex = new QMutex(); + safe_delete(managedMainContextsMutex); } -ScriptingQt::~ScriptingQt() +QJSValueList ScriptingQt::toValueList(QJSEngine* engine, const QList& values) { - safe_delete(mainEngineMutex); + QJSValueList result; + for (const QVariant& value : values) + result << engine->toScriptValue(value); + + return result; } QString ScriptingQt::getLanguage() const { - return QStringLiteral("QtScript"); + return QStringLiteral("JavaScript"); } ScriptingPlugin::Context* ScriptingQt::createContext() { ContextQt* ctx = new ContextQt; - ctx->engine->pushContext(); contexts << ctx; return ctx; } @@ -56,60 +53,54 @@ void ScriptingQt::resetContext(ScriptingPlugin::Context* context) ContextQt* ctx = getContext(context); if (!ctx) return; - - ctx->engine->popContext(); - ctx->engine->pushContext(); } -QVariant ScriptingQt::evaluate(const QString& code, const QList& args, Db* db, bool locking, QString* errorMessage) +QVariant ScriptingQt::evaluate(const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking, QString* errorMessage) { - QMutexLocker locker(mainEngineMutex); - - // Enter a new context - QScriptContext* engineContext = mainContext->engine->pushContext(); - // Call the function - QVariant result = evaluate(mainContext, engineContext, code, args, db, locking); + ContextQt* context = getMainContext(); + QVariant result = evaluate(context, code, funcInfo, args, db, locking); // Handle errors - if (!mainContext->error.isEmpty()) - *errorMessage = mainContext->error; - - // Leave the context to reset "this". - mainContext->engine->popContext(); + if (!context->error.isEmpty()) + *errorMessage = context->error; return result; } -QVariant ScriptingQt::evaluate(ScriptingPlugin::Context* context, const QString& code, const QList& args, Db* db, bool locking) +QVariant ScriptingQt::evaluate(ScriptingPlugin::Context* context, const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking) { ContextQt* ctx = getContext(context); if (!ctx) return QVariant(); - return evaluate(ctx, ctx->engine->currentContext(), code, args, db, locking); + return evaluate(ctx, code, funcInfo, args, db, locking); } -QVariant ScriptingQt::evaluate(ContextQt* ctx, QScriptContext* engineContext, const QString& code, const QList& args, Db* db, bool locking) +QVariant ScriptingQt::evaluate(ContextQt* ctx, const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking) { // Define function to call - QScriptValue functionValue = getFunctionValue(ctx, code); + QJSValue functionValue = getFunctionValue(ctx, code, funcInfo); // Db for this evaluation ctx->dbProxy->setDb(db); ctx->dbProxy->setUseDbLocking(locking); // Call the function - QScriptValue result; + QJSValue result; if (args.size() > 0) - result = functionValue.call(engineContext->activationObject(), ctx->engine->toScriptValue(args)); + result = functionValue.call(toValueList(ctx->engine, args)); else - result = functionValue.call(engineContext->activationObject()); + result = functionValue.call(); // Handle errors ctx->error.clear(); - if (ctx->engine->hasUncaughtException()) - ctx->error = ctx->engine->uncaughtException().toString(); + if (result.isError()) + { + ctx->error = QString("Uncaught exception at line %1: %2").arg( + result.property("lineNumber").toString(), + result.toString()); + } ctx->dbProxy->setDb(nullptr); ctx->dbProxy->setUseDbLocking(false); @@ -117,6 +108,20 @@ QVariant ScriptingQt::evaluate(ContextQt* ctx, QScriptContext* engineContext, co return convertVariant(result.toVariant()); } +ScriptingQt::ContextQt* ScriptingQt::getMainContext() +{ + if (mainContext.hasLocalData()) + return mainContext.localData(); + + ContextQt* context = new ContextQt(); + mainContext.setLocalData(context); + + QMutexLocker locker(managedMainContextsMutex); + managedMainContexts << context; + + return context; +} + QVariant ScriptingQt::convertVariant(const QVariant& value, bool wrapStrings) { switch (value.type()) @@ -148,7 +153,7 @@ QVariant ScriptingQt::convertVariant(const QVariant& value, bool wrapStrings) case QVariant::List: { QStringList list; - for (const QVariant& var : value.toList()) + for (QVariant& var : value.toList()) list << convertVariant(var, true).toString(); return "[" + list.join(", ") + "]"; @@ -176,7 +181,7 @@ void ScriptingQt::setVariable(ScriptingPlugin::Context* context, const QString& if (!ctx) return; - ctx->engine->globalObject().setProperty(name, ctx->engine->newVariant(value)); + ctx->engine->globalObject().setProperty(name, ctx->engine->toScriptValue(value)); } QVariant ScriptingQt::getVariable(ScriptingPlugin::Context* context, const QString& name) @@ -185,7 +190,7 @@ QVariant ScriptingQt::getVariable(ScriptingPlugin::Context* context, const QStri if (!ctx) return QVariant(); - QScriptValue value = ctx->engine->globalObject().property(name); + QJSValue value = ctx->engine->globalObject().property(name); return convertVariant(value.toVariant()); } @@ -214,20 +219,28 @@ QString ScriptingQt::getIconPath() const bool ScriptingQt::init() { - QMutexLocker locker(mainEngineMutex); - mainContext = new ContextQt; return true; } void ScriptingQt::deinit() { - for (Context* ctx : contexts) + for (Context*& ctx : contexts) delete ctx; contexts.clear(); - QMutexLocker locker(mainEngineMutex); - safe_delete(mainContext); + QMutexLocker locker(managedMainContextsMutex); + for (ContextQt*& ctx : managedMainContexts) + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + ctx->engine->setInterrupted(true); +#else + // FIXME No way to cleanly interrupt QJSEngine before Qt 5.14 +#endif + delete ctx; + } + + managedMainContexts.clear(); } ScriptingQt::ContextQt* ScriptingQt::getContext(ScriptingPlugin::Context* context) const @@ -239,31 +252,30 @@ ScriptingQt::ContextQt* ScriptingQt::getContext(ScriptingPlugin::Context* contex return ctx; } -QScriptValue ScriptingQt::getFunctionValue(ContextQt* ctx, const QString& code) +QJSValue ScriptingQt::getFunctionValue(ContextQt* ctx, const QString& code, const FunctionInfo& funcInfo) { - static const QString fnDef = QStringLiteral("(function () {%1\n})"); + static const QString fnDef = QStringLiteral("(function (%1) {%2\n})"); - QScriptProgram* prog = nullptr; - if (!ctx->scriptCache.contains(code)) - { - prog = new QScriptProgram(fnDef.arg(code)); - ctx->scriptCache.insert(code, prog); - } - else - { - prog = ctx->scriptCache[code]; - } - return ctx->engine->evaluate(*prog); + QString fullCode = fnDef.arg(funcInfo.getArguments().join(", "), code); + QJSValue* func = ctx->scriptCache[fullCode]; + if (func) + return *func; + + func = new QJSValue(ctx->engine->evaluate(fullCode)); + ctx->scriptCache.insert(fullCode, func); + return *func; } ScriptingQt::ContextQt::ContextQt() { - engine = new QScriptEngine(); + engine = new QJSEngine(); + engine->installExtensions(QJSEngine::ConsoleExtension); - dbProxy = new ScriptingQtDbProxy(); - dbProxyScriptValue = engine->newQObject(dbProxy, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater); + dbProxy = new ScriptingQtDbProxy(engine); + dbProxyScriptValue = engine->newQObject(dbProxy); + console = new ScriptingQtConsole(engine); - engine->globalObject().setProperty("debug", engine->newFunction(scriptingQtDebug)); + engine->globalObject().setProperty("console", engine->newQObject(console)); engine->globalObject().setProperty("db", dbProxyScriptValue); scriptCache.setMaxCost(cacheSize); @@ -271,6 +283,19 @@ ScriptingQt::ContextQt::ContextQt() ScriptingQt::ContextQt::~ContextQt() { - safe_delete(engine); + safe_delete(console); safe_delete(dbProxy); + safe_delete(engine); +} + +ScriptingQtConsole::ScriptingQtConsole(QJSEngine* engine) : + QObject(), engine(engine) +{ +} + +QJSValue ScriptingQtConsole::log(const QJSValue& value) +{ + static_qstring(tpl, "[JS] %1"); + NOTIFY_MANAGER->info(tpl.arg(ScriptingQt::convertVariant(value.toVariant()).toString())); + return QJSValue(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h index 125789a..a7e156c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h @@ -6,33 +6,35 @@ #include #include #include -#include -#include +#include +#include -class QScriptEngine; class QMutex; -class QScriptContext; class ScriptingQtDbProxy; +class ScriptingQtConsole; class ScriptingQt : public BuiltInPlugin, public DbAwareScriptingPlugin { Q_OBJECT - SQLITESTUDIO_PLUGIN_TITLE("Qt scripting") - SQLITESTUDIO_PLUGIN_DESC("Qt scripting support.") - SQLITESTUDIO_PLUGIN_VERSION(10000) + SQLITESTUDIO_PLUGIN_TITLE("JavaScript scripting") + SQLITESTUDIO_PLUGIN_DESC("JavaScript scripting support.") + SQLITESTUDIO_PLUGIN_VERSION(10100) SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl") public: ScriptingQt(); ~ScriptingQt(); + static QJSValueList toValueList(QJSEngine* engine, const QList& values); + static QVariant convertVariant(const QVariant& value, bool wrapStrings = false); + QString getLanguage() const; Context* createContext(); void releaseContext(Context* context); void resetContext(Context* context); - QVariant evaluate(const QString& code, const QList& args, Db* db, bool locking = false, QString* errorMessage = nullptr); - QVariant evaluate(Context* context, const QString& code, const QList& args, Db* db, bool locking = false); + QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking = false, QString* errorMessage = nullptr); + QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking = false); void setVariable(Context* context, const QString& name, const QVariant& value); QVariant getVariable(Context* context, const QString& name); bool hasError(Context* context) const; @@ -50,23 +52,39 @@ class ScriptingQt : public BuiltInPlugin, public DbAwareScriptingPlugin ContextQt(); ~ContextQt(); - QScriptEngine* engine = nullptr; - QCache scriptCache; + QJSEngine* engine = nullptr; + QCache scriptCache; QString error; ScriptingQtDbProxy* dbProxy = nullptr; - QScriptValue dbProxyScriptValue; + ScriptingQtConsole* console = nullptr; + QJSValue dbProxyScriptValue; }; ContextQt* getContext(ScriptingPlugin::Context* context) const; - QScriptValue getFunctionValue(ContextQt* ctx, const QString& code); - QVariant evaluate(ContextQt* ctx, QScriptContext* engineContext, const QString& code, const QList& args, Db* db, bool locking); - QVariant convertVariant(const QVariant& value, bool wrapStrings = false); + QJSValue getFunctionValue(ContextQt* ctx, const QString& code, const FunctionInfo& funcInfo); + QVariant evaluate(ContextQt* ctx, const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking); + ContextQt* getMainContext(); static const constexpr int cacheSize = 5; - ContextQt* mainContext = nullptr; + QThreadStorage mainContext; QList contexts; - QMutex* mainEngineMutex = nullptr; + QList managedMainContexts; + QMutex* managedMainContextsMutex = nullptr; +}; + +class ScriptingQtConsole : public QObject +{ + Q_OBJECT + + public: + ScriptingQtConsole(QJSEngine* engine); + + private: + QJSEngine* engine = nullptr; + + public slots: + QJSValue log(const QJSValue& value); }; #endif // SCRIPTINGQT_H diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.png b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.png index 220ea27..ce8e60a 100644 Binary files a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.png and b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.png differ diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp index ff3c7ee..cc9fa2d 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp @@ -1,11 +1,11 @@ +#include "scriptingqt.h" #include "scriptingqtdbproxy.h" #include "db/db.h" #include "db/sqlquery.h" -#include -#include +#include -ScriptingQtDbProxy::ScriptingQtDbProxy(QObject *parent) : - QObject(parent) +ScriptingQtDbProxy::ScriptingQtDbProxy(QJSEngine* engine, QObject *parent) : + QObject(parent), engine(engine) { } Db* ScriptingQtDbProxy::getDb() const @@ -40,12 +40,12 @@ QHash ScriptingQtDbProxy::mapToHash(const QMap& listArgs, const QMap& mapArgs, - bool singleCell, const QScriptValue* funcPtr) + bool singleCell, const QJSValue* funcPtr) { if (!db) { QString funcName = singleCell ? QStringLiteral("db.onecolumn()") : QStringLiteral("db.eval()"); - context()->throwError(tr("No database available in current context, while called QtScript's %1 command.").arg(funcName)); + engine->throwError(tr("No database available in current context, while called JavaScript's %1 command.").arg(funcName)); return evalInternalErrorResult(singleCell); } @@ -62,7 +62,7 @@ QVariant ScriptingQtDbProxy::evalInternal(const QString& sql, const QListisError()) { QString funcName = singleCell ? QStringLiteral("db.onecolumn()") : QStringLiteral("db.eval()"); - context()->throwError(tr("Error from %1: %2").arg(funcName, results->getErrorText())); + engine->throwError(tr("Error from %1: %2").arg(funcName, results->getErrorText())); return evalInternalErrorResult(singleCell); } @@ -72,15 +72,15 @@ QVariant ScriptingQtDbProxy::evalInternal(const QString& sql, const QListhasNext()) { row = results->next(); - funcArgs = context()->engine()->toScriptValue(row->valueList()); - funcResult = func.call(context()->thisObject(), funcArgs); + funcArgs = ScriptingQt::toValueList(engine, row->valueList()); + funcResult = func.call(funcArgs); if (!funcResult.isUndefined()) break; } @@ -123,12 +123,12 @@ QVariant ScriptingQtDbProxy::eval(const QString& sql, const QMap(), args, false); } -QVariant ScriptingQtDbProxy::eval(const QString& sql, const QList& args, const QScriptValue& func) +QVariant ScriptingQtDbProxy::eval(const QString& sql, const QList& args, const QJSValue& func) { return evalInternal(sql, args, QMap(), false, &func); } -QVariant ScriptingQtDbProxy::eval(const QString& sql, const QMap& args, const QScriptValue& func) +QVariant ScriptingQtDbProxy::eval(const QString& sql, const QMap& args, const QJSValue& func) { return evalInternal(sql, QList(), args, false, &func); } diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h index add9540..9a86f6b 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h @@ -2,18 +2,18 @@ #define SCRIPTINGQTDBPROXY_H #include -#include #include #include #include +#include class Db; -class ScriptingQtDbProxy : public QObject, protected QScriptable +class ScriptingQtDbProxy : public QObject { Q_OBJECT public: - explicit ScriptingQtDbProxy(QObject *parent = 0); + explicit ScriptingQtDbProxy(QJSEngine* engine, QObject *parent = 0); Db* getDb() const; void setDb(Db* value); @@ -23,20 +23,21 @@ class ScriptingQtDbProxy : public QObject, protected QScriptable private: QVariant evalInternal(const QString& sql, const QList& listArgs, const QMap& mapArgs, bool singleCell, - const QScriptValue* funcPtr = nullptr); + const QJSValue* funcPtr = nullptr); QVariant evalInternalErrorResult(bool singleCell); static QHash mapToHash(const QMap& map); Db* db = nullptr; bool useDbLocking = false; + QJSEngine* engine = nullptr; public slots: QVariant eval(const QString& sql); QVariant eval(const QString& sql, const QList& args); QVariant eval(const QString& sql, const QMap& args); - QVariant eval(const QString& sql, const QList& args, const QScriptValue& func); - QVariant eval(const QString& sql, const QMap& args, const QScriptValue& func); + QVariant eval(const QString& sql, const QList& args, const QJSValue& func); + QVariant eval(const QString& sql, const QMap& args, const QJSValue& func); QVariant onecolumn(const QString& sql, const QList& args); QVariant onecolumn(const QString& sql, const QMap& args); }; diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp index 93a6d91..7edd7e7 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp @@ -38,7 +38,7 @@ void ScriptingSql::resetContext(ScriptingPlugin::Context* context) dynamic_cast(context)->errorText.clear(); } -QVariant ScriptingSql::evaluate(ScriptingPlugin::Context* context, const QString& code, const QList& args, Db* db, bool locking) +QVariant ScriptingSql::evaluate(ScriptingPlugin::Context* context, const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking) { SqlContext* ctx = dynamic_cast(context); ctx->errorText.clear(); @@ -58,14 +58,15 @@ QVariant ScriptingSql::evaluate(ScriptingPlugin::Context* context, const QString QString sql = code; if (ctx->variables.size() > 0) { - QString value; for (const QString& key : ctx->variables.keys()) { - value = "'" + ctx->variables[key].toString() + "'"; + QString value = "'" + ctx->variables[key].toString() + "'"; sql.replace(":" + key, value).replace("@" + key, value).replace("$" + key, value); } } + replaceNamedArgs(sql, funcInfo, args); + SqlQueryPtr result = theDb->exec(sql, args, execFlags); if (result->isError()) { @@ -76,7 +77,7 @@ QVariant ScriptingSql::evaluate(ScriptingPlugin::Context* context, const QString return result->getSingleCell(); } -QVariant ScriptingSql::evaluate(const QString& code, const QList& args, Db* db, bool locking, QString* errorMessage) +QVariant ScriptingSql::evaluate(const QString& code, const FunctionInfo& funcInfo, const QList& args, Db* db, bool locking, QString* errorMessage) { Db* theDb = nullptr; @@ -91,7 +92,10 @@ QVariant ScriptingSql::evaluate(const QString& code, const QList& args if (!locking) execFlags |= Db::Flag::NO_LOCK; - SqlQueryPtr result = theDb->exec(code, args, execFlags); + QString sql = code; + replaceNamedArgs(sql, funcInfo, args); + + SqlQueryPtr result = theDb->exec(sql, args, execFlags); if (result->isError()) { *errorMessage = result->getErrorText(); @@ -144,3 +148,18 @@ void ScriptingSql::deinit() safe_delete(memDb); } + +void ScriptingSql::replaceNamedArgs(QString& sql, const ScriptingPlugin::FunctionInfo& funcInfo, const QList& args) +{ + int i = 0; + for (const QString& key : funcInfo.getArguments()) + { + if (i >= args.size()) + break; + + QString value = "'" + args[i++].toString() + "'"; + sql.replace(":" + key, value) + .replace("@" + key, value) + .replace("$" + key, value); + } +} diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h index 7b8fd3b..8fa51db 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h @@ -28,8 +28,10 @@ class ScriptingSql : public BuiltInPlugin, public DbAwareScriptingPlugin Context* createContext(); void releaseContext(Context* context); void resetContext(Context* context); - QVariant evaluate(Context* context, const QString& code, const QList& args, Db* db, bool locking); - QVariant evaluate(const QString& code, const QList& args, Db* db, bool locking, QString* errorMessage); + QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, + const QList& args, Db* db, bool locking); + QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, + const QList& args, Db* db, bool locking, QString* errorMessage); void setVariable(Context* context, const QString& name, const QVariant& value); QVariant getVariable(Context* context, const QString& name); bool hasError(Context* context) const; @@ -39,6 +41,8 @@ class ScriptingSql : public BuiltInPlugin, public DbAwareScriptingPlugin void deinit(); private: + void replaceNamedArgs(QString& sql, const FunctionInfo& funcInfo, const QList& args); + QList contexts; Db* memDb = nullptr; }; diff --git a/SQLiteStudio3/coreSQLiteStudio/querygenerator.cpp b/SQLiteStudio3/coreSQLiteStudio/querygenerator.cpp index 3f01350..af201ba 100644 --- a/SQLiteStudio3/coreSQLiteStudio/querygenerator.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/querygenerator.cpp @@ -159,8 +159,7 @@ QString QueryGenerator::generateSelectFromSelect(Db* db, const QString& initialS static_qstring(tpl, "SELECT %1 FROM (%2)%3"); // Resolve all columns of the select - SelectResolver resolver(db, initialSelect, dbNameToAttach); - QList columns = resolver.resolveColumnsFromFirstCore(); + QList columns = SelectResolver::sqliteResolveColumns(db, initialSelect, dbNameToAttach); // Generate result columns QStringList resCols; diff --git a/SQLiteStudio3/coreSQLiteStudio/querymodel.cpp b/SQLiteStudio3/coreSQLiteStudio/querymodel.cpp index 8d20b70..5e57db6 100644 --- a/SQLiteStudio3/coreSQLiteStudio/querymodel.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/querymodel.cpp @@ -1,6 +1,7 @@ #include "querymodel.h" #include "db/db.h" #include "common/unused.h" +#include "db/sqlquery.h" QueryModel::QueryModel(Db* db, QObject *parent) : QAbstractTableModel(parent), db(db) @@ -15,7 +16,7 @@ void QueryModel::refresh() beginResetModel(); loadedRows.clear(); SqlQueryPtr results = db->exec(query); - for (SqlResultsRowPtr row : results->getAll()) + for (SqlResultsRowPtr& row : results->getAll()) loadedRows += row; columns = results->columnCount(); diff --git a/SQLiteStudio3/coreSQLiteStudio/querymodel.h b/SQLiteStudio3/coreSQLiteStudio/querymodel.h index 6c7e48a..9d4c1a2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/querymodel.h +++ b/SQLiteStudio3/coreSQLiteStudio/querymodel.h @@ -1,7 +1,6 @@ #ifndef SQLQUERYMODEL_H #define SQLQUERYMODEL_H -#include "db/sqlquery.h" #include "db/sqlresultsrow.h" #include #include diff --git a/SQLiteStudio3/coreSQLiteStudio/rsa/BigInt.cpp b/SQLiteStudio3/coreSQLiteStudio/rsa/BigInt.cpp index 3fbe83c..45dd2cd 100644 --- a/SQLiteStudio3/coreSQLiteStudio/rsa/BigInt.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/rsa/BigInt.cpp @@ -4,19 +4,6 @@ * * This file is part of rsa - the RSA implementation in C++. * - * rsa is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * rsa is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with rsa. If not, see . - * * BigInt.cpp * * Author: Nedim Srndic diff --git a/SQLiteStudio3/coreSQLiteStudio/rsa/BigInt.h b/SQLiteStudio3/coreSQLiteStudio/rsa/BigInt.h index 6d59c7a..d377247 100644 --- a/SQLiteStudio3/coreSQLiteStudio/rsa/BigInt.h +++ b/SQLiteStudio3/coreSQLiteStudio/rsa/BigInt.h @@ -4,19 +4,6 @@ * * This file is part of rsa - the RSA implementation in C++. * - * rsa is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * rsa is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with rsa. If not, see . - * * BigInt.h * * Author: Nedim Srndic diff --git a/SQLiteStudio3/coreSQLiteStudio/rsa/Key.cpp b/SQLiteStudio3/coreSQLiteStudio/rsa/Key.cpp index adc4a1f..27238bc 100644 --- a/SQLiteStudio3/coreSQLiteStudio/rsa/Key.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/rsa/Key.cpp @@ -4,19 +4,6 @@ * * This file is part of rsa - the RSA implementation in C++. * - * rsa is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * rsa is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with rsa. If not, see . - * * Key.cpp * * Author: Nedim Srndic diff --git a/SQLiteStudio3/coreSQLiteStudio/rsa/Key.h b/SQLiteStudio3/coreSQLiteStudio/rsa/Key.h index f1d2ee6..dfda7e0 100644 --- a/SQLiteStudio3/coreSQLiteStudio/rsa/Key.h +++ b/SQLiteStudio3/coreSQLiteStudio/rsa/Key.h @@ -4,19 +4,6 @@ * * This file is part of rsa - the RSA implementation in C++. * - * rsa is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * rsa is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with rsa. If not, see . - * * Key.h * * Author: Nedim Srndic diff --git a/SQLiteStudio3/coreSQLiteStudio/rsa/KeyPair.cpp b/SQLiteStudio3/coreSQLiteStudio/rsa/KeyPair.cpp index 7780288..e705db2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/rsa/KeyPair.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/rsa/KeyPair.cpp @@ -4,19 +4,6 @@ * * This file is part of rsa - the RSA implementation in C++. * - * rsa is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * rsa is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with rsa. If not, see . - * * KeyPair.cpp * * Author: Nedim Srndic diff --git a/SQLiteStudio3/coreSQLiteStudio/rsa/KeyPair.h b/SQLiteStudio3/coreSQLiteStudio/rsa/KeyPair.h index 153195a..a93e485 100644 --- a/SQLiteStudio3/coreSQLiteStudio/rsa/KeyPair.h +++ b/SQLiteStudio3/coreSQLiteStudio/rsa/KeyPair.h @@ -4,19 +4,6 @@ * * This file is part of rsa - the RSA implementation in C++. * - * rsa is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * rsa is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with rsa. If not, see . - * * KeyPair.h * * Author: Nedim Srndic diff --git a/SQLiteStudio3/coreSQLiteStudio/rsa/PrimeGenerator.cpp b/SQLiteStudio3/coreSQLiteStudio/rsa/PrimeGenerator.cpp index 7f38f55..f476a30 100644 --- a/SQLiteStudio3/coreSQLiteStudio/rsa/PrimeGenerator.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/rsa/PrimeGenerator.cpp @@ -4,19 +4,6 @@ * * This file is part of rsa - the RSA implementation in C++. * - * rsa is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * rsa is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with rsa. If not, see . - * * PrimeGenerator.cpp * * Author: Nedim Srndic diff --git a/SQLiteStudio3/coreSQLiteStudio/rsa/PrimeGenerator.h b/SQLiteStudio3/coreSQLiteStudio/rsa/PrimeGenerator.h index 64ee9f6..b8fda82 100644 --- a/SQLiteStudio3/coreSQLiteStudio/rsa/PrimeGenerator.h +++ b/SQLiteStudio3/coreSQLiteStudio/rsa/PrimeGenerator.h @@ -4,19 +4,6 @@ * * This file is part of rsa - the RSA implementation in C++. * - * rsa is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * rsa is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with rsa. If not, see . - * * PrimeGenerator.h * * A class used to generate large prime or random numbers. diff --git a/SQLiteStudio3/coreSQLiteStudio/rsa/RSA.cpp b/SQLiteStudio3/coreSQLiteStudio/rsa/RSA.cpp index 0d8e921..5021e59 100644 --- a/SQLiteStudio3/coreSQLiteStudio/rsa/RSA.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/rsa/RSA.cpp @@ -4,19 +4,6 @@ * * This file is part of rsa - the RSA implementation in C++. * - * rsa is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * rsa is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with rsa. If not, see . - * * RSA.cpp * * Author: Nedim Srndic diff --git a/SQLiteStudio3/coreSQLiteStudio/rsa/RSA.h b/SQLiteStudio3/coreSQLiteStudio/rsa/RSA.h index 501261e..b6343ea 100644 --- a/SQLiteStudio3/coreSQLiteStudio/rsa/RSA.h +++ b/SQLiteStudio3/coreSQLiteStudio/rsa/RSA.h @@ -4,19 +4,6 @@ * * This file is part of rsa - the RSA implementation in C++. * - * rsa is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * rsa is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with rsa. If not, see . - * * RSA.h * * Author: Nedim Srndic diff --git a/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp b/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp index 4d65461..129bb43 100644 --- a/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp @@ -98,14 +98,15 @@ StrHash SchemaResolver::getGroupedTriggers(const QString &database) StrHash SchemaResolver::getGroupedObjects(const QString &database, const QStringList &inputList, SqliteQueryType type) { QString strType = sqliteQueryTypeToString(type); + ObjectType objectType = objectTypeFromQueryType(type); StrHash groupedObjects; SqliteQueryPtr parsedQuery; SqliteTableRelatedDdlPtr tableRelatedDdl; - for (QString object : inputList) + for (const QString& object : inputList) { - parsedQuery = getParsedObject(database, object, ANY); + parsedQuery = getParsedObject(database, object, objectType); if (!parsedQuery) { qWarning() << "Could not get parsed object for " << strType << ":" << object; @@ -116,7 +117,8 @@ StrHash SchemaResolver::getGroupedObjects(const QString &database, if (!tableRelatedDdl) { qWarning() << "Parsed object is not of expected type. Expected" << strType - << ", but got" << sqliteQueryTypeToString(parsedQuery->queryType); + << ", but got" << sqliteQueryTypeToString(parsedQuery->queryType) + << "; Object db and name:" << database << object; continue; } @@ -145,12 +147,12 @@ QSet SchemaResolver::getDatabases() return db->getAllAttaches(); } -QStringList SchemaResolver::getTableColumns(const QString& table) +QStringList SchemaResolver::getTableColumns(const QString& table, bool onlyReal) { - return getTableColumns("main", table); + return getTableColumns("main", table, onlyReal); } -QStringList SchemaResolver::getTableColumns(const QString &database, const QString &table) +QStringList SchemaResolver::getTableColumns(const QString &database, const QString &table, bool onlyReal) { QStringList columns; // result @@ -178,7 +180,12 @@ QStringList SchemaResolver::getTableColumns(const QString &database, const QStri // Now we have a regular table, let's extract columns. for (SqliteCreateTable::Column* column : createTable->columns) + { + if (onlyReal && column->hasConstraint(SqliteCreateTable::Column::Constraint::GENERATED)) + continue; + columns << column->name; + } return columns; } @@ -272,12 +279,14 @@ QList SchemaResolver::getViewColumnObjects(const QString SqliteCreateTablePtr SchemaResolver::virtualTableAsRegularTable(const QString &database, const QString &table) { - QString strippedName = stripObjName(table); + // The "table" name here used to be stripped with stripObjName(), but actually [abc] is a proper table name, + // that should not be stripped. Any names passed to SchemaResolver cannot be wrapped. + QString dbName = getPrefixDb(database); // Create temp table to see columns. - QString newTable = db->getUniqueNewObjectName(strippedName); - QString origTable = wrapObjIfNeeded(strippedName); + QString newTable = db->getUniqueNewObjectName(table); + QString origTable = wrapObjIfNeeded(table); SqlQueryPtr tempTableRes = db->exec(QString("CREATE TEMP TABLE %1 AS SELECT * FROM %2.%3 LIMIT 0;").arg(newTable, dbName, origTable), dbFlags); if (tempTableRes->isError()) qWarning() << "Could not create temp table to identify virtual table columns of virtual table " << origTable << ". Error details:" << tempTableRes->getErrorText(); @@ -303,6 +312,9 @@ QString SchemaResolver::getObjectDdl(const QString& name, ObjectType type) QString SchemaResolver::getObjectDdl(const QString &database, const QString &name, ObjectType type) { + // The "name" here used to be stripped with stripObjName(), but actually [abc] is a proper object name, + // that should not be stripped. Any names passed to SchemaResolver cannot be wrapped. + if (name.isNull()) return QString(); @@ -310,13 +322,13 @@ QString SchemaResolver::getObjectDdl(const QString &database, const QString &nam QString dbName = getPrefixDb(database); // In case of sqlite_master or sqlite_temp_master we have static definitions - QString lowerName = stripObjName(name).toLower(); + QString lowerName = name.toLower(); if (lowerName == "sqlite_master") return getSqliteMasterDdl(false); else if (lowerName == "sqlite_temp_master") return getSqliteMasterDdl(true); else if (lowerName.startsWith("sqlite_autoindex_")) - return getSqliteAutoIndexDdl(dbName, stripObjName(name)); + return getSqliteAutoIndexDdl(dbName, name); // Standalone or temp table? QString targetTable = "sqlite_master"; @@ -648,7 +660,7 @@ QString SchemaResolver::getSqliteAutoIndexDdl(const QString& database, const QSt } // Check the unique flag of the index - static_qstring(idxUniqueQueryTpl, "SELECT unique FROM %1.pragma_index_list(%2) WHERE name = ?"); + static_qstring(idxUniqueQueryTpl, "SELECT [unique] FROM %1.pragma_index_list(%2) WHERE name = ?"); SqlQueryPtr uniqRes = db->exec(idxUniqueQueryTpl.arg(dbName, wrapString(table)), {index}, dbFlags); bool unique = uniqRes->getSingleCell().toInt() > 0; @@ -846,6 +858,47 @@ QStringList SchemaResolver::getFkReferencingTables(const QString& table, const Q return tables; } +SchemaResolver::ObjectType SchemaResolver::objectTypeFromQueryType(const SqliteQueryType& queryType) +{ + switch (queryType) + { + case SqliteQueryType::CreateIndex: + return INDEX; + case SqliteQueryType::CreateTrigger: + return TRIGGER; + case SqliteQueryType::CreateView: + return VIEW; + case SqliteQueryType::CreateTable: + case SqliteQueryType::CreateVirtualTable: + return TABLE; + case SqliteQueryType::Select: + case SqliteQueryType::Pragma: + case SqliteQueryType::UNDEFINED: + case SqliteQueryType::EMPTY: + case SqliteQueryType::AlterTable: + case SqliteQueryType::Analyze: + case SqliteQueryType::Attach: + case SqliteQueryType::BeginTrans: + case SqliteQueryType::CommitTrans: + case SqliteQueryType::Copy: + case SqliteQueryType::Delete: + case SqliteQueryType::Detach: + case SqliteQueryType::DropIndex: + case SqliteQueryType::DropTable: + case SqliteQueryType::DropTrigger: + case SqliteQueryType::DropView: + case SqliteQueryType::Insert: + case SqliteQueryType::Reindex: + case SqliteQueryType::Release: + case SqliteQueryType::Rollback: + case SqliteQueryType::Savepoint: + case SqliteQueryType::Update: + case SqliteQueryType::Vacuum: + return ANY; + } + return ANY; +} + QStringList SchemaResolver::getIndexesForTable(const QString& database, const QString& table) { static_qstring(idxForTableTpl, "SELECT name FROM %1.pragma_index_list(%2)"); @@ -1150,7 +1203,7 @@ bool SchemaResolver::isWithoutRowIdTable(const QString& database, const QString& if (!createTable) return false; - return !createTable->withOutRowId.isNull(); + return createTable->withOutRowId; } bool SchemaResolver::isVirtualTable(const QString& database, const QString& table) @@ -1195,7 +1248,7 @@ QStringList SchemaResolver::getWithoutRowIdTableColumns(const QString& database, if (!createTable) return columns; - if (createTable->withOutRowId.isNull()) + if (!createTable->withOutRowId) return columns; // it's not WITHOUT ROWID table return createTable->getPrimaryKeyColumns(); @@ -1252,6 +1305,31 @@ void SchemaResolver::setNoDbLocking(bool value) dbFlags ^= Db::Flag::NO_LOCK; } +QString SchemaResolver::normalizeCaseObjectName(const QString& name) +{ + static_qstring(sql, "SELECT name FROM main.sqlite_master WHERE lower(name) = lower(?);"); + return normalizeCaseObjectNameByQuery(sql, name); +} + +QString SchemaResolver::normalizeCaseObjectName(const QString& database, const QString& name) +{ + static_qstring(sql, "SELECT name FROM %1.sqlite_master WHERE lower(name) = lower(?);"); + QString query = sql.arg(wrapObjIfNeeded(database)); + return normalizeCaseObjectNameByQuery(query, name); +} + +QString SchemaResolver::normalizeCaseObjectNameByQuery(const QString& query, const QString& name) +{ + SqlQueryPtr results = db->exec(query, {name}); + if (results->isError()) + { + qCritical() << "Could not get object name normalized case. Object name:" << name << ", error:" + << results->getErrorText(); + return name; + } + + return results->getSingleCell().toString(); +} SchemaResolver::ObjectCacheKey::ObjectCacheKey(Type type, Db* db, const QString& value1, const QString& value2, const QString& value3) : type(type), db(db), value1(value1), value2(value2), value3(value3) diff --git a/SQLiteStudio3/coreSQLiteStudio/schemaresolver.h b/SQLiteStudio3/coreSQLiteStudio/schemaresolver.h index e7bf2c8..cfb5bd5 100644 --- a/SQLiteStudio3/coreSQLiteStudio/schemaresolver.h +++ b/SQLiteStudio3/coreSQLiteStudio/schemaresolver.h @@ -15,6 +15,7 @@ #include "db/db.h" #include "common/strhash.h" #include "common/expiringcache.h" +#include "parser/ast/sqlitequerytype.h" #include class SqliteCreateTable; @@ -110,6 +111,7 @@ class API_EXPORT SchemaResolver /** * @brief getTableColumns Get column names for a table. * @param table Table to query. + * @param onlyReal If true, the method will skip columns that are not real (like GENERATED columns). * @return List of column names of the table. * This does something similar to getting list of columns with * PRAGMA table_info(), but the pragma in Sqlite2 doesn't support @@ -117,16 +119,17 @@ class API_EXPORT SchemaResolver * to make this possible. It finds table's DDL and parses it, * then extracts list of columns from parsing results. */ - QStringList getTableColumns(const QString& table); + QStringList getTableColumns(const QString& table, bool onlyReal = false); /** * @brief getTableColumns Get column names for a table. * @param database Attached database name. * @param table Table to query. + * @param onlyReal If true, the method will skip columns that are not real (like GENERATED columns). * @return List of column names of the table. * @overload */ - QStringList getTableColumns(const QString& database, const QString& table); + QStringList getTableColumns(const QString& database, const QString& table, bool onlyReal = false); QList getTableColumnDataTypes(const QString& table, int expectedNumberOfTypes = -1); QList getTableColumnDataTypes(const QString& database, const QString& table, int expectedNumberOfTypes = -1); @@ -178,6 +181,7 @@ class API_EXPORT SchemaResolver QString getSqliteAutoIndexDdl(const QString& database, const QString& index); static QString getSqliteMasterDdl(bool temp = false); static QStringList getFkReferencingTables(const QString& table, const QList& allParsedTables); + static ObjectType objectTypeFromQueryType(const SqliteQueryType& queryType); QStringList getCollations(); @@ -187,6 +191,9 @@ class API_EXPORT SchemaResolver bool getNoDbLocking() const; void setNoDbLocking(bool value); + QString normalizeCaseObjectName(const QString& name); + QString normalizeCaseObjectName(const QString& database, const QString& name); + static QString objectTypeToString(ObjectType type); static ObjectType stringToObjectType(const QString& type); static void staticInit(); @@ -204,6 +211,7 @@ class API_EXPORT SchemaResolver QString getObjectDdlWithDifficultName(const QString& dbName, const QString& lowerName, QString targetTable, ObjectType type); QString getObjectDdlWithSimpleName(const QString& dbName, const QString& lowerName, QString targetTable, ObjectType type); StrHash getIndexesWithTables(const QString& database = QString()); + QString normalizeCaseObjectNameByQuery(const QString& query, const QString& name); template StrHash> getAllParsedObjectsForType(const QString& database, const QString& type); diff --git a/SQLiteStudio3/coreSQLiteStudio/selectresolver.cpp b/SQLiteStudio3/coreSQLiteStudio/selectresolver.cpp index 3db029d..8a3efa8 100644 --- a/SQLiteStudio3/coreSQLiteStudio/selectresolver.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/selectresolver.cpp @@ -1,9 +1,7 @@ #include "selectresolver.h" #include "parser/token.h" -#include "parser/lexer.h" #include "parser/keywords.h" #include "schemaresolver.h" -#include "parser/ast/sqlitecreateview.h" #include "common/global.h" #include #include @@ -55,8 +53,8 @@ QList> SelectResolver::resolve(SqliteSelect *selec { errors.clear(); extractCte(select); - QList > results; - for (SqliteSelect::Core* core : select->coreSelects) + QList> results; + for (SqliteSelect::Core*& core : select->coreSelects) { results << resolveCore(core); currentCoreResults.clear(); @@ -77,17 +75,39 @@ QList > SelectResolver::resolveAvailableColumns(Sq errors.clear(); extractCte(select); QList > results; - for (SqliteSelect::Core* core : select->coreSelects) + for (SqliteSelect::Core*& core : select->coreSelects) results << resolveAvailableCoreColumns(core); return results; } +QList SelectResolver::resolveAvailableColumns(SqliteSelect::Core::JoinSource* joinSrc) +{ + errors.clear(); + return resolveJoinSource(joinSrc); +} + QSet SelectResolver::resolveTables(SqliteSelect::Core *selectCore) { - QSet
    tables; extractCte(selectCore); - QList columns = resolveAvailableColumns(selectCore); + return resolveTablesFromCore(selectCore); +} + +QList> SelectResolver::resolveTables(SqliteSelect *select) +{ + extractCte(select); + QList> results; + for (SqliteSelect::Core*& core : select->coreSelects) + results << resolveTablesFromCore(core); + + return results; +} + +QSet SelectResolver::resolveTables(SqliteSelect::Core::JoinSource* joinSrc) +{ + errors.clear(); + QSet
    tables; + QList columns = resolveAvailableColumns(joinSrc); for (const Column& col : columns) { if (col.type != Column::Type::COLUMN) @@ -95,30 +115,21 @@ QSet SelectResolver::resolveTables(SqliteSelect::Core *se tables << col.getTable(); } - return tables; } -QList > SelectResolver::resolveTables(SqliteSelect *select) +QSet SelectResolver::resolveTablesFromCore(SqliteSelect::Core *selectCore) { - QList > results; - extractCte(select); - QList > columnLists = resolveAvailableColumns(select); - for (const QList& columns : columnLists) + QSet
    tables; + QList columns = resolveAvailableColumns(selectCore); + for (const Column& col : columns) { - QSet
    tables; - for (const Column& col : columns) - { - if (col.type != Column::Type::COLUMN) - continue; - - tables << col.getTable(); - } + if (col.type != Column::Type::COLUMN) + continue; - results << tables; + tables << col.getTable(); } - - return results; + return tables; } QList SelectResolver::translateToColumns(SqliteSelect* select, const TokenList& columnTokens) @@ -148,25 +159,21 @@ const QStringList& SelectResolver::getErrors() const return errors; } +QList SelectResolver::sqliteResolveColumns(const QString& query) +{ + return sqliteResolveColumns(db, query, dbNameToAttach); +} + QList SelectResolver::resolveCore(SqliteSelect::Core* selectCore) { if (selectCore->from) currentCoreSourceColumns = resolveJoinSource(selectCore->from); - for (SqliteSelect::Core::ResultColumn* resCol : selectCore->resultColumns) + for (SqliteSelect::Core::ResultColumn*& resCol : selectCore->resultColumns) resolve(resCol); - if (selectCore->distinctKw) - markDistinctColumns(); - - if (selectCore->groupBy.size() > 0) - markGroupedColumns(); - fixColumnNames(); - - SqliteSelect* select = dynamic_cast(selectCore->parentStatement()); - if (select && select->coreSelects.size() > 1) - markCompoundColumns(); + markFlagsBySelect(selectCore, currentCoreResults); return currentCoreResults; } @@ -177,15 +184,31 @@ QList SelectResolver::resolveAvailableCoreColumns(Sqlite if (selectCore->from) columns = resolveJoinSource(selectCore->from); + markFlagsBySelect(selectCore, columns); return columns; } +void SelectResolver::markFlagsBySelect(SqliteSelect::Core* core, QList& columns) +{ + if (core->distinctKw) + markDistinctColumns(&columns); + + if (core->groupBy.size() > 0) + markGroupedColumns(&columns); + + SqliteSelect* select = dynamic_cast(core->parentStatement()); + if (select && select->coreSelects.size() > 1) + markCompoundColumns(&columns); +} + SelectResolver::Column SelectResolver::translateTokenToColumn(SqliteSelect* select, TokenPtr token) { + QString strippedColName = stripObjName(token->value); + // Default result Column notTranslatedColumn; notTranslatedColumn.type = Column::OTHER; - notTranslatedColumn.column = token->value; + notTranslatedColumn.column = strippedColName; // Find containing statement SqliteStatement* parentStmt = select->findStatementWithToken(token); @@ -214,9 +237,9 @@ SelectResolver::Column SelectResolver::translateTokenToColumn(SqliteSelect* sele } // Search through available columns - for (const Column& availableColumn : resolveAvailableColumns(core)) + for (Column& availableColumn : resolveAvailableColumns(core)) { - if (availableColumn.type == Column::COLUMN && availableColumn.column.compare(token->value, Qt::CaseInsensitive) == 0) + if (availableColumn.type == Column::COLUMN && availableColumn.column.compare(strippedColName, Qt::CaseInsensitive) == 0) return availableColumn; } @@ -227,19 +250,19 @@ SelectResolver::Column SelectResolver::translateTokenToColumn(SqliteSelect* sele return notTranslatedColumn; } -void SelectResolver::markDistinctColumns() +void SelectResolver::markDistinctColumns(QList* columnList) { - markCurrentColumnsWithFlag(FROM_DISTINCT_SELECT); + markCurrentColumnsWithFlag(FROM_DISTINCT_SELECT, columnList); } -void SelectResolver::markCompoundColumns() +void SelectResolver::markCompoundColumns(QList* columnList) { - markCurrentColumnsWithFlag(FROM_COMPOUND_SELECT); + markCurrentColumnsWithFlag(FROM_COMPOUND_SELECT, columnList); } -void SelectResolver::markGroupedColumns() +void SelectResolver::markGroupedColumns(QList* columnList) { - markCurrentColumnsWithFlag(FROM_GROUPED_SELECT); + markCurrentColumnsWithFlag(FROM_GROUPED_SELECT, columnList); } void SelectResolver::fixColumnNames() @@ -296,7 +319,7 @@ void SelectResolver::resolve(SqliteSelect::Core::ResultColumn *resCol) void SelectResolver::resolveStar(SqliteSelect::Core::ResultColumn *resCol) { bool foundAtLeastOne = false; - for (SelectResolver::Column column : currentCoreSourceColumns) + for (SelectResolver::Column column : qAsConst(currentCoreSourceColumns)) { if (!resCol->table.isNull()) { @@ -338,7 +361,6 @@ void SelectResolver::resolveStar(SqliteSelect::Core::ResultColumn *resCol) else column.displayName = column.column; - column.originalColumn = resCol; currentCoreResults << column; foundAtLeastOne = true; } @@ -355,7 +377,6 @@ void SelectResolver::resolveExpr(SqliteSelect::Core::ResultColumn *resCol) // Not a simple column, but some expression SelectResolver::Column column; column.alias = resCol->alias; - column.originalColumn = resCol; column.column = getResColTokensWithoutAlias(resCol).detokenize().trimmed(); column.displayName = !resCol->alias.isNull() ? column.alias : column.column; @@ -378,7 +399,6 @@ void SelectResolver::resolveDbAndTable(SqliteSelect::Core::ResultColumn *resCol) Column col; col.alias = resCol->alias; col.column = expr->column; - col.originalColumn = resCol; col.type = Column::COLUMN; // Display name @@ -399,7 +419,7 @@ void SelectResolver::resolveDbAndTable(SqliteSelect::Core::ResultColumn *resCol) matched = resolveExplicitColumn(expr->column); - if (!matched.table.isNull()) + if (!matched.table.isNull() || !matched.tableAlias.isNull()) { col.database = matched.database; col.originalDatabase = resolveDatabase(matched.database); @@ -425,7 +445,7 @@ void SelectResolver::resolveDbAndTable(SqliteSelect::Core::ResultColumn *resCol) SelectResolver::Column SelectResolver::resolveRowIdColumn(SqliteExpr *expr) { // Looking for first source that can provide ROWID. - for (const Column& column : currentCoreSourceColumns) + for (Column& column : currentCoreSourceColumns) { if (column.table.isNull()) continue; // ROWID cannot be related to source with no table @@ -438,7 +458,7 @@ SelectResolver::Column SelectResolver::resolveRowIdColumn(SqliteExpr *expr) SelectResolver::Column SelectResolver::resolveExplicitColumn(const QString &columnName) { - for (const Column& column : currentCoreSourceColumns) + for (Column& column : currentCoreSourceColumns) { if (columnName.compare(column.column, Qt::CaseInsensitive) != 0 && columnName.compare(column.alias, Qt::CaseInsensitive) != 0) continue; @@ -450,7 +470,7 @@ SelectResolver::Column SelectResolver::resolveExplicitColumn(const QString &colu SelectResolver::Column SelectResolver::resolveExplicitColumn(const QString &table, const QString &columnName) { - for (const Column& column : currentCoreSourceColumns) + for (Column& column : currentCoreSourceColumns) { if (columnName.compare(column.column, Qt::CaseInsensitive) != 0 && columnName.compare(column.alias, Qt::CaseInsensitive) != 0) continue; @@ -465,7 +485,7 @@ SelectResolver::Column SelectResolver::resolveExplicitColumn(const QString &tabl SelectResolver::Column SelectResolver::resolveExplicitColumn(const QString &database, const QString &table, const QString &columnName) { - for (const Column& column : currentCoreSourceColumns) + for (Column& column : currentCoreSourceColumns) { if (columnName.compare(column.column, Qt::CaseInsensitive) != 0 && columnName.compare(column.alias, Qt::CaseInsensitive) != 0) continue; @@ -529,7 +549,7 @@ void SelectResolver::extractCte(SqliteSelect *select) if (!select->with) return; - for (SqliteWith::CommonTableExpression* cte : select->with->cteList) + for (SqliteWith::CommonTableExpression*& cte : select->with->cteList) cteList[cte->table] = cte; } @@ -545,7 +565,7 @@ QList SelectResolver::resolveJoinSource(SqliteSelect::Co { QList columnSources; columnSources += resolveSingleSource(joinSrc->singleSource); - for (SqliteSelect::Core::JoinSourceOther* otherSrc : joinSrc->otherSources) + for (SqliteSelect::Core::JoinSourceOther*& otherSrc : joinSrc->otherSources) columnSources += resolveOtherSource(otherSrc); return columnSources; @@ -566,16 +586,16 @@ QList SelectResolver::resolveSingleSource(SqliteSelect:: return resolveTableFunctionColumns(joinSrc); if (isView(joinSrc->database, joinSrc->table)) - return resolveView(joinSrc->database, joinSrc->table, joinSrc->alias); + return resolveView(joinSrc); - if (joinSrc->database.isNull() && cteList.contains(joinSrc->table)) + if (joinSrc->database.isNull() && cteList.contains(joinSrc->table, Qt::CaseInsensitive)) return resolveCteColumns(joinSrc); QList columnSources; QStringList columns = getTableColumns(joinSrc->database, joinSrc->table, joinSrc->alias); Column column; column.type = Column::COLUMN; - column.table = joinSrc->table;; + column.table = joinSrc->table; column.database = joinSrc->database; column.originalDatabase = resolveDatabase(joinSrc->database); if (!joinSrc->alias.isNull()) @@ -592,40 +612,25 @@ QList SelectResolver::resolveSingleSource(SqliteSelect:: QList SelectResolver::resolveCteColumns(SqliteSelect::Core::SingleSource* joinSrc) { - SqliteWith::CommonTableExpression* cte = cteList[joinSrc->table]; - - Column column; - column.type = Column::COLUMN; - column.flags |= FROM_CTE_SELECT; - column.tableAlias = cte->table; - - QList columnSources; - static_qstring(cteSelectTpl, "WITH %1 SELECT * FROM %2"); + + SqliteWith::CommonTableExpression* cte = cteList.value(joinSrc->table, Qt::CaseInsensitive); QString selectQuery = cte->detokenize(); - QString query = cteSelectTpl.arg(selectQuery, cte->table); - QList queryColumns = db->columnsForQuery(query); - if (queryColumns.isEmpty()) - { - qWarning() << "Could not detect query columns. Probably due to db error:" << db->getErrorText(); - return columnSources; - } + QString theQuery = cteSelectTpl.arg(selectQuery, cte->table); + QList columnSources = sqliteResolveColumns(theQuery); - for (const AliasedColumn& queryColumn : queryColumns) + for (Column& column : columnSources) { - if (!queryColumn.getDatabase().isNull()) - column.database = resolveDatabase(queryColumn.getDatabase()); - else - column.database = queryColumn.getDatabase(); - - column.table = queryColumn.getTable(); + column.flags |= FROM_CTE_SELECT; + column.tableAlias = cte->table; // From CTE perspective, however the column is received as "result column name" from SQLite API // is what we report back to user of the CTE as available column. No matter if it's actual alias, // or simply name of a column. - column.column = queryColumn.getAlias(); - column.displayName = queryColumn.getAlias(); - columnSources << column; + column.column = column.displayName; + + // Column aliases inside of CTE are disregarded, because they do not matter outside of CTE + column.alias = QString(); } return columnSources; @@ -680,16 +685,41 @@ QList SelectResolver::resolveOtherSource(SqliteSelect::C QList SelectResolver::resolveSubSelect(SqliteSelect *select) { - QList columnSources; Q_ASSERT(select->coreSelects.size() > 0); bool compound = (select->coreSelects.size() > 1); if (compound && !resolveMultiCore) - return columnSources; + return QList(); + + // SQLite API used for fully correct & precise resolution of source table, column and displayName. + QString coreQuery = select->detokenize(); + QList columnSources = sqliteResolveColumns(coreQuery); + // Internal, recursive SelectResolver used for fine tuning the tableAlias and oldTableAliases of columns. SelectResolver internalResolver(db, query); - columnSources += internalResolver.resolve(select->coreSelects[0]); + QList columnSourcesFromInternal = internalResolver.resolve(select->coreSelects[0]); + + if (columnSourcesFromInternal.size() == columnSources.size()) + { + // Copy tableAlias and oldTableAliases from internal resolver. + QMutableListIterator it(columnSources); + QMutableListIterator intIt(columnSourcesFromInternal); + while (it.hasNext() && intIt.hasNext()) + { + Column& col = it.next(); + Column& intCol = intIt.next(); + col.tableAlias = intCol.tableAlias; + col.oldTableAliases = intCol.oldTableAliases; + col.flags = intCol.flags; + } + } + else + { + qCritical() << "Number of columns resolved by internal SchemaResolver is different than resolved by SQLite API:" + << columnSourcesFromInternal.size() << "!=" << columnSources.size() + << ", therefore table alias may be identified incorrectly (from resolver, but not by SQLite API)"; + } if (compound) { @@ -701,27 +731,51 @@ QList SelectResolver::resolveSubSelect(SqliteSelect *sel return columnSources; } -QList SelectResolver::resolveView(const QString& database, const QString& name, const QString& alias) +QList SelectResolver::resolveView(SqliteSelect::Core::SingleSource *joinSrc) { - QList results; - SqliteQueryPtr query = schemaResolver->getParsedObject(database, name, SchemaResolver::VIEW); - if (!query) - { - qDebug() << "Could not get parsed CREATE VIEW in SelectResolver::resolveView()."; - return results; - } + static_qstring(columnSqlTpl, "SELECT * FROM %1 LIMIT 0"); + QList results = sqliteResolveColumns(columnSqlTpl.arg(joinSrc->detokenize())); + applySubSelectAlias(results, (!joinSrc->alias.isNull() ? joinSrc->alias : joinSrc->table)); + for (Column& column : results) + column.flags |= FROM_VIEW; - SqliteCreateViewPtr createView = query.dynamicCast(); - if (!createView) + return results; +} + +QList SelectResolver::sqliteResolveColumns(Db* db, const QString& query, const BiStrHash& dbNameToAttach) +{ + QList columnSources; + QList queryColumns = db->columnsForQuery(query); + if (queryColumns.isEmpty()) + qWarning() << "Could not detect query columns. Probably due to db error:" << db->getErrorText(); + + Column column; + for (const AliasedColumn& queryColumn : queryColumns) { - qDebug() << "Parsed object not a CREATE VIEW as expected, but instead it's:" << sqliteQueryTypeToString(query->queryType); - return results; - } + if (!queryColumn.getDatabase().isNull()) + column.database = dbNameToAttach.valueByRight(queryColumn.getDatabase(), queryColumn.getDatabase(), Qt::CaseInsensitive); + else + column.database = QString(); - results = resolveSubSelect(createView->select); - applySubSelectAlias(results, (!alias.isNull() ? alias : name)); + column.displayName = queryColumn.getAlias(); + if (queryColumn.getTable().isNull()) + { + column.type = Column::OTHER; + column.table = QString(); + column.column = wrapObjIfNeeded(queryColumn.getAlias()); + column.alias = queryColumn.getAlias(); + } + else + { + column.type = Column::COLUMN; + column.table = queryColumn.getTable(); + column.column = queryColumn.getColumn(); + column.alias = (queryColumn.getColumn() != queryColumn.getAlias()) ? queryColumn.getAlias() : QString(); + } + columnSources << column; + } - return results; + return columnSources; } bool SelectResolver::isView(const QString& database, const QString& name) @@ -773,10 +827,7 @@ void SelectResolver::applySubSelectAlias(QList& columns, QString SelectResolver::resolveDatabase(const QString& database) { - if (dbNameToAttach.containsRight(database, Qt::CaseInsensitive)) - return dbNameToAttach.valueByRight(database, Qt::CaseInsensitive); - - return database; + return dbNameToAttach.valueByRight(database, database, Qt::CaseInsensitive); } bool SelectResolver::parseOriginalQuery() @@ -802,16 +853,6 @@ bool SelectResolver::parseOriginalQuery() return true; } -SelectResolver::Table::Table() -{ -} - -SelectResolver::Table::Table(const SelectResolver::Table &other) : - database(other.database), originalDatabase(other.originalDatabase), table(other.table), - tableAlias(other.tableAlias), oldTableAliases(other.oldTableAliases), flags(other.flags) -{ -} - int SelectResolver::Table::operator ==(const SelectResolver::Table &other) { return ::operator==(*this, other); @@ -863,3 +904,24 @@ uint qHash(const SelectResolver::Column &column) return qHash(column.database.toLower() + "." + column.table.toLower() + "." + column.column.toLower() + "/" + column.tableAlias.toLower() + "/" + column.oldTableAliases.join(",")); } + +QDebug operator<<(QDebug debug, const SelectResolver::Column &c) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "Column " << c.displayName << "(" + << c.column << " = " << c.alias << ", " + << c.table << " = " << c.tableAlias << ", " + << c.database << " = " << c.originalDatabase + << ")"; + return debug; +} + +QDebug operator<<(QDebug debug, const SelectResolver::Table &c) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "Table (" + << c.table << " = " << c.tableAlias << ", " + << c.database << " = " << c.originalDatabase + << ")"; + return debug; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/selectresolver.h b/SQLiteStudio3/coreSQLiteStudio/selectresolver.h index cb93d65..a77ffe1 100644 --- a/SQLiteStudio3/coreSQLiteStudio/selectresolver.h +++ b/SQLiteStudio3/coreSQLiteStudio/selectresolver.h @@ -1,9 +1,9 @@ #ifndef SELECTRESOLVER_H #define SELECTRESOLVER_H +#include "common/strhash.h" #include "parser/ast/sqliteselect.h" #include "common/bistrhash.h" -#include "expectedtoken.h" #include "parser/ast/sqlitewith.h" #include #include @@ -63,7 +63,8 @@ class API_EXPORT SelectResolver FROM_ANONYMOUS_SELECT = 0x02, FROM_DISTINCT_SELECT = 0x04, FROM_GROUPED_SELECT = 0x08, - FROM_CTE_SELECT = 0x10 + FROM_CTE_SELECT = 0x10, + FROM_VIEW = 0x20 }; /** @@ -71,9 +72,6 @@ class API_EXPORT SelectResolver */ struct API_EXPORT Table { - Table(); - Table(const Table& other); - /** * @brief Database name. * @@ -112,7 +110,6 @@ class API_EXPORT SelectResolver QString alias; QString displayName; bool aliasDefinedInSubQuery = false; - SqliteSelect::Core::ResultColumn* originalColumn = nullptr; int operator==(const Column& other); Table getTable() const; @@ -123,16 +120,18 @@ class API_EXPORT SelectResolver ~SelectResolver(); QList resolveColumnsFromFirstCore(); - QList > resolveColumns(); + QList> resolveColumns(); QList resolve(SqliteSelect::Core* selectCore); - QList > resolve(SqliteSelect* select); + QList> resolve(SqliteSelect* select); QList resolveAvailableColumns(SqliteSelect::Core* selectCore); - QList > resolveAvailableColumns(SqliteSelect* select); + QList> resolveAvailableColumns(SqliteSelect* select); + QList resolveAvailableColumns(SqliteSelect::Core::JoinSource* joinSrc); QSet
    resolveTables(SqliteSelect::Core* selectCore); - QList > resolveTables(SqliteSelect* select); + QList> resolveTables(SqliteSelect* select); + QSet
    resolveTables(SqliteSelect::Core::JoinSource* joinSrc); /** * @brief Translates tokens representing column name in the SELECT into full column objects. @@ -179,6 +178,30 @@ class API_EXPORT SelectResolver */ const QStringList& getErrors() const; + /** + * @brief This is a convenient overload of method that uses database object passed to the constructor. + * @param query SQL query to analyze. + * @return Column with limited metadata (database, table name - if applicable, column name - if applicable, and displayName) + * + * See static overloaded method for more details. + */ + QList sqliteResolveColumns(const QString& query); + + /** + * @brief Does quick analysis of result columns for the query, using SQLite built-in API. + * @param db Database to analyze the query against. + * @param query SQL query to analyze. + * @param dbNameToAttach A mapping of database name to its attach name, used to resolve db name if SQLite API returns it as attach name. + * @return Column with limited metadata (database, table name - if applicable, column name - if applicable, and displayName) + * + * Instead of doing full scale anaysis of the query, it leverages SQLite API for a query metadata, + * but it lacks information about source table aliases, subquery aliases, + * flags (compund query, aggregate query, cte query, etc). + * It's convenient, but must be complemented by sophisticated logic of the SchemaResolver to provide full info. + * Yet it's good enough if these extended information are not needed, as it can be much faster, than full analysis. + */ + static QList sqliteResolveColumns(Db* db, const QString& query, const BiStrHash& dbNameToAttach = BiStrHash()); + /** * @brief resolveMultiCore * If true (by default), the multi-core subselects will be resolved using @@ -197,6 +220,8 @@ class API_EXPORT SelectResolver private: QList resolveCore(SqliteSelect::Core* selectCore); QList resolveAvailableCoreColumns(SqliteSelect::Core* selectCore); + QSet
    resolveTablesFromCore(SqliteSelect::Core* selectCore); + void markFlagsBySelect(SqliteSelect::Core* core, QList& columns); Column translateTokenToColumn(SqliteSelect* select, TokenPtr token); void resolve(SqliteSelect::Core::ResultColumn* resCol); void resolveStar(SqliteSelect::Core::ResultColumn* resCol); @@ -214,16 +239,16 @@ class API_EXPORT SelectResolver QList resolveSingleSourceSubSelect(SqliteSelect::Core::SingleSource* joinSrc); QList resolveOtherSource(SqliteSelect::Core::JoinSourceOther *otherSrc); QList resolveSubSelect(SqliteSelect* select); - QList resolveView(const QString& database, const QString& name, const QString &alias); + QList resolveView(SqliteSelect::Core::SingleSource *joinSrc); bool isView(const QString& database, const QString& name); QStringList getTableColumns(const QString& database, const QString& table, const QString &alias); void applySubSelectAlias(QList& columns, const QString& alias); QString resolveDatabase(const QString& database); bool parseOriginalQuery(); - void markDistinctColumns(); - void markCompoundColumns(); - void markGroupedColumns(); + void markDistinctColumns(QList* columnList = nullptr); + void markCompoundColumns(QList* columnList = nullptr); + void markGroupedColumns(QList* columnList = nullptr); void fixColumnNames(); void markCurrentColumnsWithFlag(Flag flag, QList* columnList = nullptr); bool matchTable(const Column& sourceColumn, const QString& table); @@ -234,7 +259,7 @@ class API_EXPORT SelectResolver Db* db = nullptr; QString query; SqliteSelectPtr originalQueryParsed; - QHash cteList; + StrHash cteList; /** * @brief Database name to attach name map. @@ -307,4 +332,7 @@ API_EXPORT uint qHash(const SelectResolver::Table& table); API_EXPORT int operator==(const SelectResolver::Column& c1, const SelectResolver::Column& c2); API_EXPORT uint qHash(const SelectResolver::Column& column); +API_EXPORT QDebug operator<<(QDebug debug, const SelectResolver::Column &c); +API_EXPORT QDebug operator<<(QDebug debug, const SelectResolver::Table &c); + #endif // SELECTRESOLVER_H diff --git a/SQLiteStudio3/coreSQLiteStudio/services/codesnippetmanager.cpp b/SQLiteStudio3/coreSQLiteStudio/services/codesnippetmanager.cpp new file mode 100644 index 0000000..b50e791 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/services/codesnippetmanager.cpp @@ -0,0 +1,119 @@ +#include "codesnippetmanager.h" +#include "common/utils.h" +#include "services/config.h" +#include +#include +#include + +CodeSnippetManager::CodeSnippetManager(Config* config) : + config(config) +{ + loadFromConfig(); + if (!CFG_CORE.Internal.DefaultSnippetsCreated.get()) + createDefaultSnippets(); +} + +void CodeSnippetManager::setSnippets(const QList& snippets) +{ + clearSnippets(); + allSnippets = snippets; + refreshNames(); + saveToConfig(); +} + +const QList& CodeSnippetManager::getSnippets() const +{ + return allSnippets; +} + +const QStringList& CodeSnippetManager::getNames() const +{ + return names; +} + +void CodeSnippetManager::saveToConfig() +{ + QVariantList list; + QHash snHash; + for (CodeSnippet*& snip : allSnippets) + { + snHash["name"] = snip->name; + snHash["code"] = snip->code; + snHash["hoteky"] = snip->hotkey; + list << snHash; + } + CFG_CORE.Internal.CodeSnippets.set(list); +} + +QString CodeSnippetManager::getCodeByName(const QString& name) const +{ + CodeSnippet* snippet = findFirst(allSnippets, [name](CodeSnippet* snippet) -> bool + { + return snippet->name == name; + }); + return snippet ? snippet->code : QString(); +} + +void CodeSnippetManager::loadFromConfig() +{ + clearSnippets(); + + QVariantList list = CFG_CORE.Internal.CodeSnippets.get(); + QHash snHash; + CodeSnippet* snip = nullptr; + for (const QVariant& var : list) + { + snHash = var.toHash(); + snip = new CodeSnippet(); + snip->name = snHash["name"].toString(); + snip->code = snHash["code"].toString(); + snip->hotkey = snHash["hoteky"].toString(); + allSnippets << snip; + } + refreshNames(); +} + +void CodeSnippetManager::refreshNames() +{ + names = map(allSnippets, [](CodeSnippet* snippet) -> QString + { + return snippet->name; + }); +} + +void CodeSnippetManager::clearSnippets() +{ + for (CodeSnippet*& snip : allSnippets) + delete snip; + + allSnippets.clear(); +} + +void CodeSnippetManager::createDefaultSnippets() +{ + CodeSnippet* snip = new CodeSnippet(); + snip->name = "Create Table"; + snip->code = "CREATE TABLE tableName (\n" + " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + " value TEXT,\n" + " image BLOB\n" + ");"; + snip->hotkey = "c"; + allSnippets << snip; + + snip = new CodeSnippet(); + snip->name = "With Recursive"; + snip->code = "WITH RECURSIVE\n" + " cnt(x) AS (\n" + " SELECT 1\n" + " UNION ALL\n" + " SELECT x+1 FROM cnt\n" + " LIMIT 1000000\n" + " )\n" + "SELECT x FROM cnt;"; + snip->hotkey = "w"; + allSnippets << snip; + + saveToConfig(); + CFG_CORE.Internal.DefaultSnippetsCreated.set(true); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/services/codesnippetmanager.h b/SQLiteStudio3/coreSQLiteStudio/services/codesnippetmanager.h new file mode 100644 index 0000000..ade14bd --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/services/codesnippetmanager.h @@ -0,0 +1,42 @@ +#ifndef CODESNIPPETMANAGER_H +#define CODESNIPPETMANAGER_H + +#include "coreSQLiteStudio_global.h" +#include + +class Config; + +class API_EXPORT CodeSnippetManager : public QObject +{ + Q_OBJECT + + public: + struct API_EXPORT CodeSnippet + { + QString name; + QString code; + QString hotkey; + }; + + CodeSnippetManager(Config* config); + + void setSnippets(const QList& snippets); + const QList& getSnippets() const; + const QStringList& getNames() const; + void saveToConfig(); + QString getCodeByName(const QString& name) const; + + private: + void loadFromConfig(); + void refreshNames(); + void clearSnippets(); + void createDefaultSnippets(); + + Config* config = nullptr; + QList allSnippets; + QStringList names; +}; + +#define CODESNIPPETS SQLITESTUDIO->getCodeSnippetManager() + +#endif // CODESNIPPETMANAGER_H diff --git a/SQLiteStudio3/coreSQLiteStudio/services/collationmanager.h b/SQLiteStudio3/coreSQLiteStudio/services/collationmanager.h index 5a05b0f..ef1f57a 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/collationmanager.h +++ b/SQLiteStudio3/coreSQLiteStudio/services/collationmanager.h @@ -2,7 +2,6 @@ #define COLLATIONMANAGER_H #include "coreSQLiteStudio_global.h" -#include "common/global.h" #include #include #include diff --git a/SQLiteStudio3/coreSQLiteStudio/services/config.cpp b/SQLiteStudio3/coreSQLiteStudio/services/config.cpp index c03847a..8abd762 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/config.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/services/config.cpp @@ -1,10 +1,14 @@ #include "services/config.h" +#include +#include +#include CFG_DEFINE(Core) static const QString DB_FILE_NAME = QStringLiteral("settings3"); static QString MASTER_CONFIG_FILE = QString(); Config::AskUserForConfigDirFunc Config::askUserForConfigDirFunc; +QSettings* globalSettingsInstance = nullptr; Config::~Config() { @@ -24,3 +28,58 @@ void Config::setAskUserForConfigDirFunc(const AskUserForConfigDirFunc& value) { askUserForConfigDirFunc = value; } + +QString Config::getPortableConfigPath() +{ + QStringList paths = QStringList({"./sqlitestudio-cfg", qApp->applicationDirPath() + "/sqlitestudio-cfg"}); + QSet pathSet; + QDir dir; + for (const QString& path : paths) + { + dir = QDir(path); + pathSet << dir.absolutePath(); + } + + QString potentialPath; + QFileInfo file; + for (const QString& path : pathSet) + { + dir = QDir(path); + file = QFileInfo(dir.absolutePath()); + if (!file.exists()) + { + if (potentialPath.isNull()) + potentialPath = dir.absolutePath(); + + continue; + } + + if (!file.isDir() || !file.isReadable() || !file.isWritable()) + continue; + + for (QFileInfo& entryFile : dir.entryInfoList()) + { + if (!entryFile.isReadable() || !entryFile.isWritable()) + continue; + } + + return dir.absolutePath(); + } + + return potentialPath; +} + +QSettings* Config::getSettings() +{ + if (globalSettingsInstance == nullptr) + { + QString portableConfigPath = Config::getPortableConfigPath(); + QFileInfo portableConfigInfo(portableConfigPath); + if (portableConfigInfo.exists() && portableConfigInfo.isDir() && portableConfigInfo.isReadable()) + globalSettingsInstance = new QSettings(portableConfigPath + "/settings.ini", QSettings::IniFormat); + else + globalSettingsInstance = new QSettings(); + } + + return globalSettingsInstance; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/services/config.h b/SQLiteStudio3/coreSQLiteStudio/services/config.h index 9d1e567..bd001c2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/config.h +++ b/SQLiteStudio3/coreSQLiteStudio/services/config.h @@ -3,8 +3,6 @@ #include "coreSQLiteStudio_global.h" #include "config_builder.h" -#include "services/functionmanager.h" -#include "collationmanager.h" #include "sqlitestudio.h" #include "common/utils.h" #include @@ -34,11 +32,16 @@ CFG_CATEGORIES(Core, CFG_ENTRY(QVariantList, Functions, QVariantList()) CFG_ENTRY(QVariantList, Collations, QVariantList()) CFG_ENTRY(QVariantList, Extensions, QVariantList()) + CFG_ENTRY(QVariantList, CodeSnippets, QVariantList()) CFG_ENTRY(QString, BugReportUser, QString()) CFG_ENTRY(QString, BugReportPassword, QString()) CFG_ENTRY(QString, BugReportRecentTitle, QString()) CFG_ENTRY(QString, BugReportRecentContents, QString()) CFG_ENTRY(bool, BugReportRecentError, false) + CFG_ENTRY(bool, DefaultSnippetsCreated, false) + ) + CFG_CATEGORY(CodeAssistant, + CFG_ENTRY(bool, AutoTrigger, true) ) ) @@ -46,6 +49,7 @@ CFG_CATEGORIES(Core, class QAbstractItemModel; class DdlHistoryModel; +class QSettings; class API_EXPORT Config : public QObject { @@ -113,9 +117,10 @@ class API_EXPORT Config : public QObject static void setMasterConfigFile(const QString& path); static QString getMasterConfigFile(); static void setAskUserForConfigDirFunc(const AskUserForConfigDirFunc& value); + static QString getPortableConfigPath(); + static QSettings* getSettings(); virtual void init() = 0; - virtual void cleanUp() = 0; virtual const QString& getConfigDir() const = 0; virtual QString getConfigFilePath() const = 0; virtual bool isInMemory() const = 0; diff --git a/SQLiteStudio3/coreSQLiteStudio/services/functionmanager.h b/SQLiteStudio3/coreSQLiteStudio/services/functionmanager.h index 5fb4908..4680b74 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/functionmanager.h +++ b/SQLiteStudio3/coreSQLiteStudio/services/functionmanager.h @@ -2,7 +2,6 @@ #define FUNCTIONMANAGER_H #include "coreSQLiteStudio_global.h" -#include "common/global.h" #include #include #include @@ -37,6 +36,7 @@ class API_EXPORT FunctionManager : public QObject QStringList arguments; Type type = SCALAR; bool undefinedArgs = true; + bool deterministic = false; }; struct API_EXPORT ScriptFunction : public FunctionBase diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.cpp index 7d24e47..1cdc59f 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.cpp @@ -6,6 +6,16 @@ #include "common/utils.h" #include +class CollationFunctionInfoImpl : public ScriptingPlugin::FunctionInfo +{ + public: + QString getName() const {return QString();} + QStringList getArguments() const {return {"first", "second"};} + bool getUndefinedArgs() const {return false;} +}; + +CollationFunctionInfoImpl collationFunctionInfo; + CollationManagerImpl::CollationManagerImpl() { init(); @@ -51,7 +61,7 @@ int CollationManagerImpl::evaluate(const QString& name, const QString& value1, c } QString err; - QVariant result = plugin->evaluate(collationsByKey[name]->code, {value1, value2}, &err); + QVariant result = plugin->evaluate(collationsByKey[name]->code, collationFunctionInfo, {value1, value2}, &err); if (!err.isNull()) { @@ -109,7 +119,7 @@ void CollationManagerImpl::loadFromConfig() collHash = var.toHash(); coll = CollationPtr::create(); coll->name = collHash["name"].toString(); - coll->lang = collHash["lang"].toString(); + coll->lang = updateScriptingQtLang(collHash["lang"].toString()); coll->code = collHash["code"].toString(); coll->databases = collHash["databases"].toStringList(); coll->allDatabases = collHash["allDatabases"].toBool(); @@ -123,3 +133,11 @@ void CollationManagerImpl::refreshCollationsByKey() for (CollationPtr collation : collations) collationsByKey[collation->name] = collation; } + +QString CollationManagerImpl::updateScriptingQtLang(const QString& lang) const +{ + if (lang == "QtScript") + return QStringLiteral("JavaScript"); + + return lang; +} diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.h b/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.h index f5a5937..27adffb 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.h +++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.h @@ -23,6 +23,7 @@ class API_EXPORT CollationManagerImpl : public CollationManager void storeInConfig(); void loadFromConfig(); void refreshCollationsByKey(); + QString updateScriptingQtLang(const QString& lang) const; QList collations; QHash collationsByKey; diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.cpp index 570395a..a7f8d2b 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.cpp @@ -610,55 +610,12 @@ QString ConfigImpl::getConfigPath() QString ConfigImpl::getLegacyConfigPath() { #ifdef Q_OS_WIN - if (QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) - return SQLITESTUDIO->getEnv("APPDATA")+"/sqlitestudio"; - else - return SQLITESTUDIO->getEnv("HOME")+"/sqlitestudio"; + return SQLITESTUDIO->getEnv("APPDATA")+"/sqlitestudio"; #else return SQLITESTUDIO->getEnv("HOME")+"/.config/sqlitestudio"; #endif } -QString ConfigImpl::getPortableConfigPath() -{ - QStringList paths = QStringList({"./sqlitestudio-cfg", qApp->applicationDirPath() + "/sqlitestudio-cfg"}); - QSet pathSet; - QDir dir; - for (const QString& path : paths) - { - dir = QDir(path); - pathSet << dir.absolutePath(); - } - - QString potentialPath; - QFileInfo file; - for (const QString& path : pathSet) - { - dir = QDir(path); - file = QFileInfo(dir.absolutePath()); - if (!file.exists()) - { - if (potentialPath.isNull()) - potentialPath = dir.absolutePath(); - - continue; - } - - if (!file.isDir() || !file.isReadable() || !file.isWritable()) - continue; - - for (const QFileInfo& entryFile : dir.entryInfoList()) - { - if (!entryFile.isReadable() || !entryFile.isWritable()) - continue; - } - - return dir.absolutePath(); - } - - return potentialPath; -} - void ConfigImpl::initTables() { SqlQueryPtr results = db->exec("SELECT lower(name) AS name FROM sqlite_master WHERE type = 'table'"); @@ -729,29 +686,18 @@ void ConfigImpl::initTables() void ConfigImpl::initDbFile() { - QList> paths; - - // Do we have path selected by user? (because app was unable to find writable location before) - QSettings sett; - QString customPath = sett.value(CONFIG_DIR_SETTING).toString(); - if (!customPath.isEmpty()) - { - paths << QPair(customPath + "/" + DB_FILE_NAME, false); - qDebug() << "Using custom configuration directory. The location is stored in" << sett.fileName(); - } - else - paths.append(getStdDbPaths()); + QList paths = getStdDbPaths(); // A fallback to in-memory db - paths << QPair(memoryDbName, false); + paths << ConfigDirCandidate{memoryDbName, false, false}; // Go through all candidates and pick one QDir dir; - for (const QPair& path : paths) + for (ConfigDirCandidate& path : paths) { - dir = QDir(path.first); - if (path.first != memoryDbName) - dir = QFileInfo(path.first).dir(); + dir = QDir(path.path); + if (path.path != memoryDbName) + dir = QFileInfo(path.path).dir(); if (tryInitDbFile(path)) { @@ -760,6 +706,19 @@ void ConfigImpl::initDbFile() } } + // If not one of std paths, look for the one from previously saved. + if (configDir == memoryDbName) + { + // Do we have path selected by user? (because app was unable to find writable location before) + QSettings* sett = getSettings(); + QString path = sett->value(CONFIG_DIR_SETTING).toString(); + if (!path.isEmpty()) + { + paths << ConfigDirCandidate{path + "/" + DB_FILE_NAME, false, false}; + qDebug() << "Using custom configuration directory. The location is stored in" << sett->fileName(); + } + } + // Failed to use any of predefined directories. Let's ask the user. while (configDir == memoryDbName) { @@ -768,12 +727,12 @@ void ConfigImpl::initDbFile() break; dir = QDir(path); - if (tryInitDbFile(QPair(path + "/" + DB_FILE_NAME, false))) + if (tryInitDbFile(ConfigDirCandidate{path + "/" + DB_FILE_NAME, false, false})) { configDir = dir.absolutePath(); - QSettings sett; - sett.setValue(CONFIG_DIR_SETTING, configDir); - qDebug() << "Using custom configuration directory. The location is stored in" << sett.fileName(); + QSettings* sett = getSettings(); + sett->setValue(CONFIG_DIR_SETTING, configDir); + qDebug() << "Using custom configuration directory. The location is stored in" << sett->fileName(); } } @@ -782,35 +741,35 @@ void ConfigImpl::initDbFile() { paths.removeLast(); QStringList pathStrings; - for (const QPair& path : paths) - pathStrings << path.first; + for (ConfigDirCandidate& path : paths) + pathStrings << path.path; notifyError(QObject::tr("Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart." " Unable to create a file at following locations: %1.").arg(pathStrings.join(", "))); } - qDebug() << "Using configuration directory:" << configDir; + qDebug().noquote() << "Using configuration directory:" << toNativePath(configDir); db->exec("PRAGMA foreign_keys = 1;"); } -QList> ConfigImpl::getStdDbPaths() +QList ConfigImpl::getStdDbPaths() { - QList> paths; + QList paths; // Portable dir location has always precedense - comes first QString portablePath = getPortableConfigPath(); if (!portablePath.isNull()) - paths << QPair(portablePath+"/"+DB_FILE_NAME, false); + paths << ConfigDirCandidate{portablePath+"/"+DB_FILE_NAME, false, true}; // Determinate global config location QString globalPath = getConfigPath(); - paths << QPair(globalPath, true); + paths << ConfigDirCandidate{globalPath, true, false}; // If needed, migrate configuration from legacy location (pre-3.3) to new location (3.3 and later) QString legacyGlobalPath = getLegacyConfigPath(); if (!legacyGlobalPath.isNull()) { - paths << QPair(legacyGlobalPath+"/"+DB_FILE_NAME, true); + paths << ConfigDirCandidate{legacyGlobalPath+"/"+DB_FILE_NAME, true, false}; if (!QFile::exists(globalPath)) tryToMigrateOldGlobalPath(legacyGlobalPath, globalPath); } @@ -818,17 +777,17 @@ QList> ConfigImpl::getStdDbPaths() return paths; } -bool ConfigImpl::tryInitDbFile(const QPair &dbPath) +bool ConfigImpl::tryInitDbFile(const ConfigDirCandidate& dbPath) { // Create global config directory if not existing - if (dbPath.second && !dbPath.first.isNull()) + if (dbPath.createIfNotExists && !dbPath.path.isNull()) { - QDir dir(dbPath.first.mid(0, dbPath.first.length() - DB_FILE_NAME.length() - 1)); + QDir dir(dbPath.path.mid(0, dbPath.path.length() - DB_FILE_NAME.length() - 1)); if (!dir.exists()) QDir::root().mkpath(dir.absolutePath()); } - db = new DbSqlite3("SQLiteStudio settings", dbPath.first, {{DB_PURE_INIT, true}}); + db = new DbSqlite3("SQLiteStudio settings", dbPath.path, {{DB_PURE_INIT, true}}); if (!db->open()) { safe_delete(db); @@ -1185,7 +1144,7 @@ bool ConfigImpl::tryToMigrateOldGlobalPath(const QString& oldPath, const QString if (!QFileInfo::exists(oldPath)) return false; - qDebug() << "Attempting to migrate legacy config location" << oldPath << "to new location" << newPath; + qDebug().noquote() << "Attempting to migrate legacy config location" << toNativePath(oldPath) << "to new location" << toNativePath(newPath); QDir dir = QFileInfo(newPath).dir(); if (!dir.exists()) QDir::root().mkpath(dir.absolutePath()); diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.h b/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.h index 710fbb3..9f0f36e 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.h +++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.h @@ -8,6 +8,7 @@ class AsyncConfigHandler; class SqlHistoryModel; +class QSettings; class API_EXPORT ConfigImpl : public Config { @@ -90,6 +91,13 @@ class API_EXPORT ConfigImpl : public Config void rollback(); private: + struct ConfigDirCandidate + { + QString path; + bool createIfNotExists; + bool isPortable; + }; + /** * @brief Stores error from query in class member. * @param query Query to get error from. @@ -102,11 +110,10 @@ class API_EXPORT ConfigImpl : public Config void storeGroup(const DbGroupPtr& group, qint64 parentId = -1); void readGroupRecursively(DbGroupPtr group); QString getConfigPath(); - QString getPortableConfigPath(); void initTables(); void initDbFile(); - QList > getStdDbPaths(); - bool tryInitDbFile(const QPair& dbPath); + QList getStdDbPaths(); + bool tryInitDbFile(const ConfigDirCandidate& dbPath); QVariant deserializeValue(const QVariant& value) const; void asyncAddSqlHistory(qint64 id, const QString& sql, const QString& dbName, int timeSpentMillis, int rowsAffected); diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp index 9086257..6258f71 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp @@ -24,7 +24,7 @@ DbManagerImpl::DbManagerImpl(QObject *parent) : DbManagerImpl::~DbManagerImpl() { // qDebug() << "DbManagerImpl::~DbManagerImpl()"; - for (Db* db : dbList) + for (Db*& db : dbList) { disconnect(db, SIGNAL(disconnected()), this, SLOT(dbDisconnectedSlot())); disconnect(db, SIGNAL(aboutToDisconnect(bool&)), this, SLOT(dbAboutToDisconnect(bool&))); @@ -61,7 +61,7 @@ bool DbManagerImpl::addDb(const QString &name, const QString &path, const QHash< Db* db = createDb(name, path, options, &errorMessage); if (!db) { - notifyError(tr("Could not add database %1: %2").arg(path).arg(errorMessage)); + notifyError(tr("Could not add database %1: %2").arg(path, errorMessage)); return false; } @@ -93,8 +93,6 @@ bool DbManagerImpl::updateDb(Db* db, const QString &name, const QString &path, c nameToDb.remove(db->getName(), Qt::CaseInsensitive); pathToDb.remove(db->getPath()); - bool pathDifferent = db->getPath() != normalizedPath; - QString oldName = db->getName(); db->setName(name); db->setPath(normalizedPath); @@ -114,7 +112,7 @@ bool DbManagerImpl::updateDb(Db* db, const QString &name, const QString &path, c InvalidDb* invalidDb = dynamic_cast(db); bool wasReloaded = false; Db* reloadedDb = db; - if (pathDifferent && invalidDb) + if (invalidDb) { reloadedDb = tryToLoadDb(invalidDb, false); if (reloadedDb) // we need to know that, so we can emit dbLoaded() signal later, out of the listLock @@ -136,7 +134,7 @@ bool DbManagerImpl::updateDb(Db* db, const QString &name, const QString &path, c if (result && reloadedDb) emit dbUpdated(oldName, db); else if (reloadedDb) // database reloaded correctly, but update failed - notifyError(tr("Database %1 could not be updated, because of an error: %2").arg(oldName).arg(CFG->getLastErrorString())); + notifyError(tr("Database %1 could not be updated, because of an error: %2").arg(oldName, CFG->getLastErrorString())); return result; } @@ -297,7 +295,7 @@ DbPlugin* DbManagerImpl::getPluginForDbFile(const QString& filePath) QHash options; Db* probeDb = nullptr; - for (DbPlugin* plugin : dbPlugins) + for (DbPlugin*& plugin : dbPlugins) { probeDb = plugin->getInstance("", filePath, options); if (probeDb) @@ -346,7 +344,7 @@ void DbManagerImpl::loadInitialDbList() { QUrl url; InvalidDb* db = nullptr; - for (const Config::CfgDbPtr& cfgDb : CFG->dbList()) + for (Config::CfgDbPtr& cfgDb : CFG->dbList()) { db = new InvalidDb(cfgDb->name, cfgDb->path, cfgDb->options); @@ -372,7 +370,7 @@ void DbManagerImpl::scanForNewDatabasesInConfig() QUrl url; InvalidDb* db = nullptr; - for (const Config::CfgDbPtr& cfgDb : cfgDbList) + for (Config::CfgDbPtr& cfgDb : cfgDbList) { if (getByName(cfgDb->name) || getByPath(cfgDb->path)) continue; @@ -402,13 +400,13 @@ void DbManagerImpl::rescanInvalidDatabasesForPlugin(DbPlugin* dbPlugin) QUrl url; QString errorMessages; - for (Db* invalidDb : getInvalidDatabases()) + for (Db*& invalidDb : getInvalidDatabases()) { if (invalidDb->getConnectionOptions().contains(DB_PLUGIN) && invalidDb->getConnectionOptions()[DB_PLUGIN].toString() != dbPlugin->getName()) continue; url = QUrl::fromUserInput(invalidDb->getPath()); - if (url.isLocalFile() && !QFile::exists(invalidDb->getPath())) + if (url.isLocalFile() && !QFile::exists(invalidDb->getPath()) && invalidDb->getPath() != ":memory:") continue; errorMessages = QString(); @@ -424,7 +422,7 @@ void DbManagerImpl::rescanInvalidDatabasesForPlugin(DbPlugin* dbPlugin) if (!dbPlugin->checkIfDbServedByPlugin(db)) { - qDebug() << "Managed to load database" << db->getPath() << " (" << db->getName() << ")" + qDebug().noquote() << "Managed to load database" << toNativePath(db->getPath()) << " (" << db->getName() << ")" << "but it doesn't use DbPlugin that was just loaded, so it will not be loaded to the db manager"; delete db; @@ -473,10 +471,6 @@ QList DbManagerImpl::getInvalidDatabases() const Db* DbManagerImpl::tryToLoadDb(InvalidDb* invalidDb, bool emitNotifySignal) { - QUrl url = QUrl::fromUserInput(invalidDb->getPath()); - if (url.isLocalFile() && !QFile::exists(invalidDb->getPath())) - return nullptr; - Db* db = createDb(invalidDb->getName(), invalidDb->getPath(), invalidDb->getConnectionOptions()); if (!db) return nullptr; @@ -588,7 +582,7 @@ void DbManagerImpl::aboutToUnload(Plugin* plugin, PluginType* type) DbPlugin* dbPlugin = dynamic_cast(plugin); dbPlugins.removeOne(dbPlugin); QList toRemove; - for (Db* db : dbList) + for (Db*& db : dbList) { if (!dbPlugin->checkIfDbServedByPlugin(db)) continue; @@ -596,7 +590,7 @@ void DbManagerImpl::aboutToUnload(Plugin* plugin, PluginType* type) toRemove << db; } - for (Db* db : toRemove) + for (Db*& db : toRemove) { emit dbAboutToBeUnloaded(db, dbPlugin); diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.cpp index 2fcc689..1b49f3b 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.cpp @@ -18,6 +18,50 @@ #include #include +class FunctionInfoImpl : public ScriptingPlugin::FunctionInfo +{ + public: + FunctionInfoImpl(FunctionManager::FunctionBase* fn); + FunctionInfoImpl(); + + QString getName() const; + QStringList getArguments() const; + bool getUndefinedArgs() const; + + private: + QString name; + QStringList arguments; + bool undefinedArgs = true; +}; + +FunctionInfoImpl::FunctionInfoImpl(FunctionManager::FunctionBase* fn) +{ + name = fn->name; + arguments = fn->arguments; + undefinedArgs = fn->undefinedArgs; +} + +FunctionInfoImpl::FunctionInfoImpl() +{ +} + +QString FunctionInfoImpl::getName() const +{ + return name; +} + +QStringList FunctionInfoImpl::getArguments() const +{ + return arguments; +} + +bool FunctionInfoImpl::getUndefinedArgs() const +{ + return undefinedArgs; +} + + + FunctionManagerImpl::FunctionManagerImpl() { init(); @@ -120,14 +164,15 @@ QVariant FunctionManagerImpl::evaluateScriptScalar(ScriptFunction* func, const Q return langUnsupportedError(name, argCount, func->lang); } DbAwareScriptingPlugin* dbAwarePlugin = dynamic_cast(plugin); + FunctionInfoImpl info(func); QString error; QVariant result; if (dbAwarePlugin) - result = dbAwarePlugin->evaluate(func->code, args, db, false, &error); + result = dbAwarePlugin->evaluate(func->code, info, args, db, false, &error); else - result = plugin->evaluate(func->code, args, &error); + result = plugin->evaluate(func->code, info, args, &error); if (!error.isEmpty()) { @@ -147,11 +192,12 @@ void FunctionManagerImpl::evaluateScriptAggregateInitial(ScriptFunction* func, D ScriptingPlugin::Context* ctx = plugin->createContext(); aggregateStorage["context"] = QVariant::fromValue(ctx); + FunctionInfoImpl info(func); if (dbAwarePlugin) - dbAwarePlugin->evaluate(ctx, func->initCode, {}, db, false); + dbAwarePlugin->evaluate(ctx, func->initCode, info, {}, db, false); else - plugin->evaluate(ctx, func->initCode, {}); + plugin->evaluate(ctx, func->initCode, info, {}); if (plugin->hasError(ctx)) { @@ -170,12 +216,13 @@ void FunctionManagerImpl::evaluateScriptAggregateStep(ScriptFunction* func, cons return; DbAwareScriptingPlugin* dbAwarePlugin = dynamic_cast(plugin); + FunctionInfoImpl info(func); ScriptingPlugin::Context* ctx = aggregateStorage["context"].value(); if (dbAwarePlugin) - dbAwarePlugin->evaluate(ctx, func->code, args, db, false); + dbAwarePlugin->evaluate(ctx, func->code, info, args, db, false); else - plugin->evaluate(ctx, func->code, args); + plugin->evaluate(ctx, func->code, info, args); if (plugin->hasError(ctx)) { @@ -203,11 +250,13 @@ QVariant FunctionManagerImpl::evaluateScriptAggregateFinal(ScriptFunction* func, DbAwareScriptingPlugin* dbAwarePlugin = dynamic_cast(plugin); + FunctionInfoImpl info(func); + QVariant result; if (dbAwarePlugin) - result = dbAwarePlugin->evaluate(ctx, func->finalCode, {}, db, false); + result = dbAwarePlugin->evaluate(ctx, func->finalCode, info, {}, db, false); else - result = plugin->evaluate(ctx, func->finalCode, {}); + result = plugin->evaluate(ctx, func->finalCode, info, {}); if (plugin->hasError(ctx)) { @@ -320,7 +369,7 @@ void FunctionManagerImpl::loadFromConfig() fnHash = var.toHash(); func = new ScriptFunction(); func->name = fnHash["name"].toString(); - func->lang = fnHash["lang"].toString(); + func->lang = updateScriptingQtLang(fnHash["lang"].toString()); func->code = fnHash["code"].toString(); func->initCode = fnHash["initCode"].toString(); func->finalCode = fnHash["finalCode"].toString(); @@ -335,7 +384,7 @@ void FunctionManagerImpl::loadFromConfig() void FunctionManagerImpl::clearFunctions() { - for (ScriptFunction* fn : functions) + for (ScriptFunction*& fn : functions) delete fn; functions.clear(); @@ -483,14 +532,15 @@ QVariant FunctionManagerImpl::nativeScript(const QList& args, Db* db, return tr("Unsupported scripting language: %1").arg(args[0].toString()); } DbAwareScriptingPlugin* dbAwarePlugin = dynamic_cast(plugin); + FunctionInfoImpl info; QString error; QVariant result; if (dbAwarePlugin) - result = dbAwarePlugin->evaluate(args[1].toString(), QList(), db, false, &error); + result = dbAwarePlugin->evaluate(args[1].toString(), info, QList(), db, false, &error); else - result = plugin->evaluate(args[1].toString(), QList(), &error); + result = plugin->evaluate(args[1].toString(), info, QList(), &error); if (!error.isEmpty()) { @@ -781,6 +831,14 @@ void FunctionManagerImpl::registerNativeFunction(const QString& name, const QStr nativeFunctions << nf; } +QString FunctionManagerImpl::updateScriptingQtLang(const QString& lang) const +{ + if (lang == "QtScript") + return QStringLiteral("JavaScript"); + + return lang; +} + int qHash(const FunctionManagerImpl::Key& key) { return qHash(key.name) ^ key.argCount ^ static_cast(key.type); diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.h b/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.h index 0e5e103..26f4ae2 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.h +++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.h @@ -56,6 +56,7 @@ class API_EXPORT FunctionManagerImpl : public FunctionManager QString cannotFindFunctionError(const QString& name, int argCount); QString langUnsupportedError(const QString& name, int argCount, const QString& lang); void registerNativeFunction(const QString& name, const QStringList& args, NativeFunction::ImplementationFunction funcPtr); + QString updateScriptingQtLang(const QString& lang) const; static QStringList getArgMarkers(int argCount); static QVariant nativeRegExp(const QList& args, Db* db, bool& ok); diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.cpp index 324f549..dc36980 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.cpp @@ -38,7 +38,7 @@ void PluginManagerImpl::init() #endif #ifdef Q_OS_MACX - pluginDirs += QCoreApplication::applicationDirPath()+"/../PlugIns"; + pluginDirs += QDir(QCoreApplication::applicationDirPath()+"/../PlugIns").absolutePath(); #endif scanPlugins(); @@ -50,7 +50,7 @@ void PluginManagerImpl::deinit() emit aboutToQuit(); // Plugin containers and their plugins - for (PluginContainer* container : pluginContainer.values()) + for (PluginContainer*& container : pluginContainer.values()) { if (container->builtIn) { @@ -61,13 +61,13 @@ void PluginManagerImpl::deinit() unload(container->name); } - for (PluginContainer* container : pluginContainer.values()) + for (PluginContainer*& container : pluginContainer.values()) delete container; pluginContainer.clear(); // Types - for (PluginType* type : registeredPluginTypes) + for (PluginType*& type : registeredPluginTypes) delete type; registeredPluginTypes.clear(); @@ -109,14 +109,11 @@ PluginType* PluginManagerImpl::getPluginType(Plugin* plugin) const void PluginManagerImpl::scanPlugins() { - QStringList nameFilters; - nameFilters << "*.so" << "*.dll" << "*.dylib"; - QPluginLoader* loader = nullptr; - for (QString pluginDirPath : pluginDirs) + for (QString& pluginDirPath : pluginDirs) { QDir pluginDir(pluginDirPath); - for (QString fileName : pluginDir.entryList(nameFilters, QDir::Files)) + for (QString& fileName : pluginDir.entryList(sharedLibFileFilters(), QDir::Files)) { fileName = pluginDir.absoluteFilePath(fileName); loader = new QPluginLoader(fileName); @@ -131,7 +128,7 @@ void PluginManagerImpl::scanPlugins() } QStringList names; - for (PluginContainer* container : pluginContainer.values()) + for (PluginContainer*& container : pluginContainer.values()) { if (!container->builtIn) names << container->name; @@ -143,7 +140,7 @@ void PluginManagerImpl::scanPlugins() void PluginManagerImpl::loadPlugins() { QStringList alreadyAttempted; - for (const QString& pluginName : pluginContainer.keys()) + for (QString& pluginName : pluginContainer.keys()) { if (shouldAutoLoad(pluginName)) load(pluginName, alreadyAttempted); @@ -158,7 +155,7 @@ bool PluginManagerImpl::initPlugin(QPluginLoader* loader, const QString& fileNam QJsonObject pluginMetaData = loader->metaData(); QString pluginTypeName = pluginMetaData.value("MetaData").toObject().value("type").toString(); PluginType* pluginType = nullptr; - for (PluginType* type : registeredPluginTypes) + for (PluginType*& type : registeredPluginTypes) { if (type->getName() == pluginTypeName) { @@ -453,12 +450,12 @@ void PluginManagerImpl::unload(const QString& pluginName) return; // Unloading depdendent plugins - for (PluginContainer* otherContainer : pluginContainer.values()) + for (PluginContainer*& otherContainer : pluginContainer.values()) { if (otherContainer == container) continue; - for (const PluginDependency& dep : otherContainer->dependencies) + for (PluginDependency& dep : otherContainer->dependencies) { if (dep.name == pluginName) { @@ -472,9 +469,9 @@ void PluginManagerImpl::unload(const QString& pluginName) removePluginFromCollections(container->plugin); // Deinitializing and unloading plugin + unloadTranslation(container->name); emit aboutToUnload(container->plugin, container->type); container->plugin->deinit(); - unloadTranslation(container->name); QPluginLoader* loader = container->loader; if (!loader->isLoaded()) @@ -491,7 +488,7 @@ void PluginManagerImpl::unload(const QString& pluginName) emit unloaded(container->name, container->type); - qDebug() << pluginName << "unloaded:" << container->filePath; + qDebug().noquote() << pluginName << "unloaded:" << toNativePath(container->filePath); } bool PluginManagerImpl::load(const QString& pluginName) @@ -596,7 +593,15 @@ void PluginManagerImpl::pluginLoaded(PluginManagerImpl::PluginContainer* contain { if (!container->builtIn) { - loadTranslation(container->name); + QString tsName = container->translationName.isEmpty() ? + ( + container->name.endsWith("Plugin") ? + container->name.left(container->name.length() - 6) : + container->name + ) : + container->translationName; + + loadTranslation(tsName); container->plugin = dynamic_cast(container->loader->instance()); container->loaded = true; } @@ -604,7 +609,7 @@ void PluginManagerImpl::pluginLoaded(PluginManagerImpl::PluginContainer* contain emit loaded(container->plugin, container->type); if (!container->builtIn) - qDebug() << container->name << "loaded:" << container->filePath; + qDebug().noquote() << container->name << "loaded:" << toNativePath(container->filePath); } void PluginManagerImpl::addPluginToCollections(Plugin* plugin) @@ -618,7 +623,7 @@ void PluginManagerImpl::removePluginFromCollections(Plugin* plugin) { ScriptingPlugin* scriptingPlugin = dynamic_cast(plugin); if (scriptingPlugin && scriptingPlugins.contains(scriptingPlugin->getLanguage())) - scriptingPlugins.remove(plugin->getName()); + scriptingPlugins.remove(scriptingPlugin->getLanguage()); } bool PluginManagerImpl::readMetaData(PluginManagerImpl::PluginContainer* container) @@ -633,6 +638,7 @@ bool PluginManagerImpl::readMetaData(PluginManagerImpl::PluginContainer* contain container->description = metaData["description"].toString(); container->title = metaData["title"].toString(); container->loadByDefault = metaData.contains("loadByDefault") ? metaData["loadByDefault"].toBool() : true; + container->translationName = metaData.contains("translationName") ? metaData["translationName"].toString() : QString(); } else if (container->plugin) { diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.h b/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.h index c9d56aa..7a8ba4f 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.h +++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.h @@ -156,6 +156,21 @@ class API_EXPORT PluginManagerImpl : public PluginManager * @brief Names of plugins that this plugin conflicts with. */ QStringList conflicts; + + /** + * @brief If not empty, contains Plugin's project name to be used for loading translation resource file. + * + * For typical SQLiteStudio plugin the auto-generated translation resource name is the same + * as the name of the plugin project. Typically, name of loaded plugin class is made of + * the name of the plugin project and the "Plugin" word suffix. Therefore SQLiteStudio + * by default just removes the "Plugin" suffix (if it has such) and attempts to load the translation + * named this way. + * + * If the main Plugin class does not follow this naming strategy (project name + Plugin suffix), + * then the translationName should be specified in plugin's metadata, + * giving actual name of translation resource (i.e. name of Plugin's source code project) to be loaded. + */ + QString translationName; }; /** diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.cpp index 63dbaf6..d5cc4f6 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.cpp @@ -1,6 +1,8 @@ #include "sqliteextensionmanagerimpl.h" -#include "services/notifymanager.h" #include "services/dbmanager.h" +#include +#include +#include SqliteExtensionManagerImpl::SqliteExtensionManagerImpl() { @@ -30,21 +32,66 @@ QList SqliteExtensionManagerImpl::getExten return results; } +QStringList SqliteExtensionManagerImpl::getExtensionDirs() const +{ + return extensionDirs; +} + void SqliteExtensionManagerImpl::init() { loadFromConfig(); + scanExtensionDirs(); +} + +void SqliteExtensionManagerImpl::scanExtensionDirs() +{ + extensionDirs += qApp->applicationDirPath() + "/extensions"; + extensionDirs += qApp->applicationDirPath() + "/ext"; + extensionDirs += QDir(CFG->getConfigDir()).absoluteFilePath("ext"); + extensionDirs += QDir(CFG->getConfigDir()).absoluteFilePath("extensions"); +#ifdef Q_OS_MACX + extensionDirs += QDir(QCoreApplication::applicationDirPath()+"/../extensions").absolutePath(); +#endif + + QString envDirs = SQLITESTUDIO->getEnv("SQLITESTUDIO_SQLITE_EXTENSIONS"); + if (!envDirs.isNull()) + extensionDirs += envDirs.split(PATH_LIST_SEPARATOR); + +#ifdef SQLITE_EXTENSIONS_DIR + extensionDirs += STRINGIFY(SQLITE_EXTENSIONS_DIR); +#endif + + for (QString& extDirPath : extensionDirs) + { + QDir extDir(extDirPath); + for (QString& fileName : extDir.entryList(sharedLibFileFilters(), QDir::Files)) + { + QString path = extDir.absoluteFilePath(fileName); + auto findIt = std::find_if(extensions.begin(), extensions.end(), [path](ExtensionPtr& ext) {return ext->filePath == path;}); + if (findIt != extensions.end()) + continue; // already on the list + + ExtensionPtr ext = ExtensionPtr::create(); + ext->filePath = path; + ext->initFunc = QString(); + ext->databases = QStringList(); + ext->allDatabases = false; + extensions << ext; + qDebug() << "SQLite extension:" << path; + } + } } void SqliteExtensionManagerImpl::storeInConfig() { QVariantList list; QHash extHash; - for (ExtensionPtr ext : extensions) + for (ExtensionPtr& ext : extensions) { extHash["filePath"] = ext->filePath; extHash["initFunc"] = ext->initFunc; extHash["allDatabases"] = ext->allDatabases; - extHash["databases"] =common(DBLIST->getDbNames(), ext->databases); + extHash["databases"] = common(DBLIST->getDbNames(), ext->databases); list << extHash; } CFG_CORE.Internal.Extensions.set(list); @@ -66,5 +113,6 @@ void SqliteExtensionManagerImpl::loadFromConfig() ext->databases = extHash["databases"].toStringList(); ext->allDatabases = extHash["allDatabases"].toBool(); extensions << ext; + qDebug() << "SQLite extension from config:" << ext->filePath; } } diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.h b/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.h index 6fd7f46..9414c0a 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.h +++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.h @@ -11,13 +11,16 @@ class SqliteExtensionManagerImpl : public SqliteExtensionManager void setExtensions(const QList& newExtensions); QList getAllExtensions() const; QList getExtensionForDatabase(const QString& dbName) const; + QStringList getExtensionDirs() const; private: void init(); + void scanExtensionDirs(); void storeInConfig(); void loadFromConfig(); QList extensions; + QStringList extensionDirs; }; #endif // SQLITEEXTENSIONMANAGERIMPL_H diff --git a/SQLiteStudio3/coreSQLiteStudio/services/notifymanager.cpp b/SQLiteStudio3/coreSQLiteStudio/services/notifymanager.cpp index f8b7f25..977897f 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/notifymanager.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/services/notifymanager.cpp @@ -36,7 +36,7 @@ void NotifyManager::deleted(Db* db, const QString& database, const QString& obje emit objectDeleted(db, database, object); } -void NotifyManager::createded(Db* db, const QString& database, const QString& object) +void NotifyManager::created(Db* db, const QString& database, const QString& object) { emit objectCreated(db, database, object); } diff --git a/SQLiteStudio3/coreSQLiteStudio/services/notifymanager.h b/SQLiteStudio3/coreSQLiteStudio/services/notifymanager.h index 5bb4571..517ecf4 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/notifymanager.h +++ b/SQLiteStudio3/coreSQLiteStudio/services/notifymanager.h @@ -36,7 +36,7 @@ class API_EXPORT NotifyManager : public QObject void modified(Db* db, const QString& database, const QString& object); void deleted(Db* db, const QString& database, const QString& object); - void createded(Db* db, const QString& database, const QString& object); + void created(Db* db, const QString& database, const QString& object); void renamed(Db* db, const QString& database, const QString& oldObject, const QString& newObject); private: diff --git a/SQLiteStudio3/coreSQLiteStudio/services/sqliteextensionmanager.h b/SQLiteStudio3/coreSQLiteStudio/services/sqliteextensionmanager.h index a135f3b..9d5cd1c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/services/sqliteextensionmanager.h +++ b/SQLiteStudio3/coreSQLiteStudio/services/sqliteextensionmanager.h @@ -24,6 +24,7 @@ class API_EXPORT SqliteExtensionManager : public QObject virtual void setExtensions(const QList& newExtensions) = 0; virtual QList getAllExtensions() const = 0; virtual QList getExtensionForDatabase(const QString& dbName) const = 0; + virtual QStringList getExtensionDirs() const = 0; signals: void extensionListChanged(); diff --git a/SQLiteStudio3/coreSQLiteStudio/sqlfileexecutor.cpp b/SQLiteStudio3/coreSQLiteStudio/sqlfileexecutor.cpp new file mode 100644 index 0000000..819d222 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/sqlfileexecutor.cpp @@ -0,0 +1,223 @@ +#include "sqlfileexecutor.h" +#include "db/db.h" +#include "db/sqlquery.h" +#include "services/notifymanager.h" + +#include +#include +#include + +SqlFileExecutor::SqlFileExecutor(QObject *parent) + : QObject{parent} +{ +} + +void SqlFileExecutor::execSqlFromFile(Db* db, const QString& filePath, bool ignoreErrors, QString codec, bool async) +{ + if (!db && !db->isOpen()) + { + emit execEnded(); + return; + } + + if (executionInProgress) + { + emit execEnded(); + return; + } + + fkWasEnabled = db->exec("PRAGMA foreign_keys")->getSingleCell().toBool(); + if (fkWasEnabled) + { + SqlQueryPtr res = db->exec("PRAGMA foreign_keys = 0"); + if (res->isError()) + { + qDebug() << "Failed to temporarily disable foreign keys enforcement:" << db->getErrorText(); + emit execEnded(); + return; + } + } + + // Exec file + executionInProgress = 1; + this->ignoreErrors = ignoreErrors; + this->codec = codec; + this->filePath = filePath; + this->db = db; + emit updateProgress(0); + if (!db->begin()) + { + notifyError(tr("Could not execute SQL, because application has failed to start transaction: %1").arg(db->getErrorText())); + emit execEnded(); + return; + } + + if (async) + QtConcurrent::run(this, &SqlFileExecutor::execInThread); + else + execInThread(); +} + +bool SqlFileExecutor::isExecuting() const +{ + return executionInProgress; +} + +void SqlFileExecutor::stopExecution() +{ + if (!executionInProgress) + { + emit execEnded(); + return; + } + + executionInProgress = 0; + + if (db) // should always be there, but just in case + { + db->interrupt(); + db->rollback(); + db = nullptr; + notifyWarn(tr("Execution from file cancelled. Any queries executed so far have been rolled back.")); + } + emit execEnded(); +} + +bool SqlFileExecutor::execQueryFromFile(Db* db, const QString& sql) +{ + return !db->exec(sql)->isError(); +} + +void SqlFileExecutor::execInThread() +{ + // Open file + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + notifyError(tr("Could not open file '%1' for reading: %2").arg(filePath, file.errorString())); + executionInProgress = 0; + emit execEnded(); + return; + } + + QTextStream stream(&file); + stream.setCodec(codec.toLatin1().constData()); + + qint64 fileSize = file.size(); + int attemptedExecutions = 0; + int executed = 0; + bool ok = true; + + QElapsedTimer timer; + timer.start(); + QList> errors = executeFromStream(stream, executed, attemptedExecutions, ok, fileSize); + int millis = timer.elapsed(); + + if (fkWasEnabled) + { + SqlQueryPtr res = db->exec("PRAGMA foreign_keys = 1"); + if (res->isError()) + qDebug() << "Failed to restore foreign keys enforcement after execution SQL from file:" << res->getErrorText(); + } + + if (executionInProgress.loadAcquire()) + { + handleExecutionResults(db, executed, attemptedExecutions, ok, ignoreErrors, millis); + if (!errors.isEmpty()) + emit execErrors(errors, !ok && !ignoreErrors); + } + + file.close(); + emit execEnded(); + executionInProgress = 0; +} + +void SqlFileExecutor::handleExecutionResults(Db* db, int executed, int attemptedExecutions, bool ok, bool ignoreErrors, int millis) +{ + bool doCommit = ok ? true : ignoreErrors; + if (doCommit) + { + if (!db->commit()) + { + notifyError(tr("Could not execute SQL, because application has failed to commit the transaction: %1").arg(db->getErrorText())); + db->rollback(); + } + else if (!ok) // committed with errors + { + notifyInfo(tr("Finished executing %1 queries in %2 seconds. %3 were not executed due to errors.") + .arg(QString::number(executed), QString::number(millis / 1000.0), QString::number(attemptedExecutions - executed))); + emit schemaNeedsRefreshing(db); + } + else + { + notifyInfo(tr("Finished executing %1 queries in %2 seconds.").arg(QString::number(executed), QString::number(millis / 1000.0))); + emit schemaNeedsRefreshing(db); + } + } + else + { + db->rollback(); + notifyError(tr("Could not execute SQL due to error.")); + } +} + +QList> SqlFileExecutor::executeFromStream(QTextStream& stream, int& executed, int& attemptedExecutions, bool& ok, qint64 fileSize) +{ + QList> errors; + qint64 pos = 0; + QChar c; + QString sql; + sql.reserve(10000); + SqlQueryPtr results; + while (!stream.atEnd() && executionInProgress.loadAcquire()) + { + while (!db->isComplete(sql) && !stream.atEnd()) + { + stream >> c; + sql.append(c); + while (c != ';' && !stream.atEnd()) + { + stream >> c; + sql.append(c); + } + } + + if (shouldSkipQuery(sql)) + { + sql.clear(); + continue; + } + + results = db->exec(sql); + attemptedExecutions++; + if (results->isError()) + { + ok = false; + errors << QPair(sql, results->getErrorText()); + if (!ignoreErrors) + break; + } + else + executed++; + + sql.clear(); + if (attemptedExecutions % 100 == 0) + { + pos = stream.device()->pos(); + emit updateProgress(static_cast(100 * pos / fileSize)); + } + } + return errors; +} + +bool SqlFileExecutor::shouldSkipQuery(const QString& sql) +{ + if (sql.trimmed().isEmpty() || !db->isComplete(sql)) + return true; + + QString upper = sql.toUpper().trimmed().split("\n").last().trimmed(); + return (upper.startsWith("BEGIN") || + upper.startsWith("COMMIT") || + upper.startsWith("ROLLBACK") || + upper.startsWith("END")); +} diff --git a/SQLiteStudio3/coreSQLiteStudio/sqlfileexecutor.h b/SQLiteStudio3/coreSQLiteStudio/sqlfileexecutor.h new file mode 100644 index 0000000..fbca301 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/sqlfileexecutor.h @@ -0,0 +1,43 @@ +#ifndef SQLFILEEXECUTOR_H +#define SQLFILEEXECUTOR_H + +#include "coreSQLiteStudio_global.h" +#include +#include + +class Db; + +class API_EXPORT SqlFileExecutor : public QObject +{ + Q_OBJECT + + public: + explicit SqlFileExecutor(QObject *parent = nullptr); + void execSqlFromFile(Db* db, const QString& filePath, bool ignoreErrors, QString codec, bool async = true); + bool isExecuting() const; + + private: + bool execQueryFromFile(Db* db, const QString& sql); + void execInThread(); + void handleExecutionResults(Db* db, int executed, int attemptedExecutions, bool ok, bool ignoreErrors, int millis); + QList> executeFromStream(QTextStream& stream, int& executed, int& attemptedExecutions, bool& ok, qint64 fileSize); + bool shouldSkipQuery(const QString& sql); + + QAtomicInt executionInProgress = 0; + Db* db = nullptr; + bool fkWasEnabled = true; + bool ignoreErrors = false; + QString codec; + QString filePath; + + public slots: + void stopExecution(); + + signals: + void schemaNeedsRefreshing(Db* db); + void updateProgress(int value); + void execEnded(); + void execErrors(const QList>& errors, bool rolledBack); +}; + +#endif // SQLFILEEXECUTOR_H diff --git a/SQLiteStudio3/coreSQLiteStudio/sqlhistorymodel.cpp b/SQLiteStudio3/coreSQLiteStudio/sqlhistorymodel.cpp index 26e2d41..6d7addd 100644 --- a/SQLiteStudio3/coreSQLiteStudio/sqlhistorymodel.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/sqlhistorymodel.cpp @@ -16,8 +16,6 @@ QVariant SqlHistoryModel::data(const QModelIndex& index, int role) const if (role == Qt::TextAlignmentRole && (index.column() == 2 || index.column() == 3)) return (int)(Qt::AlignRight|Qt::AlignVCenter); - QVariant d = QueryModel::data(index, role); - return QueryModel::data(index, role); } diff --git a/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.cpp b/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.cpp index c38f528..4e966fd 100644 --- a/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.cpp @@ -1,5 +1,6 @@ #include "sqlitestudio.h" #include "plugins/plugin.h" +#include "services/codesnippetmanager.h" #include "services/pluginmanager.h" #include "common/utils.h" #include "common/utils_sql.h" @@ -34,13 +35,14 @@ #include "services/extralicensemanager.h" #include "services/sqliteextensionmanager.h" #include "translations.h" +#include "chillout/chillout.h" #include #include #include DEFINE_SINGLETON(SQLiteStudio) -static const int sqlitestudioVersion = 30303; +static const int sqlitestudioVersion = 30404; SQLiteStudio::SQLiteStudio() { @@ -51,6 +53,25 @@ SQLiteStudio::SQLiteStudio() SQLiteStudio::~SQLiteStudio() { } + +void SQLiteStudio::setupCrashHandler() +{ + auto &chillout = Debug::Chillout::getInstance(); + +#ifdef _WIN32 + chillout.init(qApp->applicationName().toStdWString(), qApp->applicationDirPath().toStdWString()); +#else + chillout.init(qApp->applicationName().toStdString(), qApp->applicationDirPath().toStdString()); +#endif + + chillout.setBacktraceCallback([](const char * const) {}); + + chillout.setCrashCallback([this]() { + for (CrashHandler& hnd : crashHandlers) + hnd(); + }); +} + QStringList SQLiteStudio::getInitialTranslationFiles() const { return initialTranslationFiles; @@ -61,6 +82,11 @@ void SQLiteStudio::setInitialTranslationFiles(const QStringList& value) initialTranslationFiles = value; } +void SQLiteStudio::installCrashHandler(SQLiteStudio::CrashHandler handler) +{ + crashHandlers << handler; +} + QString SQLiteStudio::getCurrentLang() const { @@ -182,6 +208,16 @@ void SQLiteStudio::setExportManager(ExportManager* value) exportManager = value; } +CodeSnippetManager* SQLiteStudio::getCodeSnippetManager() const +{ + return codeSnippetManager; +} + +void SQLiteStudio::setCodeSnippetManager(CodeSnippetManager* newCodeSnippetManager) +{ + codeSnippetManager = newCodeSnippetManager; +} + int SQLiteStudio::getVersion() const { return sqlitestudioVersion; @@ -280,7 +316,7 @@ void SQLiteStudio::init(const QStringList& cmdListArguments, bool guiAvailable) QThreadPool::globalInstance()->setMaxThreadCount(10); - Q_INIT_RESOURCE(coreSQLiteStudio); + SQLS_INIT_RESOURCE(coreSQLiteStudio); CfgLazyInitializer::init(); @@ -345,6 +381,7 @@ void SQLiteStudio::init(const QStringList& cmdListArguments, bool guiAvailable) updateManager = new UpdateManager(); #endif extraLicenseManager = new ExtraLicenseManager(); + codeSnippetManager = new CodeSnippetManager(config); extraLicenseManager->addLicense("SQLiteStudio license (GPL v3)", ":/docs/licenses/sqlitestudio_license.txt"); extraLicenseManager->addLicense("Fugue icons", ":/docs/licenses/fugue_icons.txt"); @@ -352,6 +389,9 @@ void SQLiteStudio::init(const QStringList& cmdListArguments, bool guiAvailable) extraLicenseManager->addLicense("diff_match (Apache License v2.0)", ":/docs/licenses/diff_match.txt"); extraLicenseManager->addLicense("RSA library (GPL v3)", ":/docs/licenses/gpl.txt"); extraLicenseManager->addLicense("SingleApplication (The MIT License)", ":/docs/licenses/mit.txt"); + extraLicenseManager->addLicense("ICU (ICU License)", ":/docs/licenses/icu.txt"); + + setupCrashHandler(); } void SQLiteStudio::initPlugins() @@ -365,31 +405,43 @@ void SQLiteStudio::initPlugins() void SQLiteStudio::cleanUp() { + if (finalCleanupDone) + return; + + finalCleanupDone = true; emit aboutToQuit(); - disconnect(pluginManager, SIGNAL(aboutToUnload(Plugin*,PluginType*)), this, SLOT(pluginToBeUnloaded(Plugin*,PluginType*))); - disconnect(pluginManager, SIGNAL(unloaded(QString,PluginType*)), this, SLOT(pluginUnloaded(QString,PluginType*))); - if (!immediateQuit) - { - if (pluginManager) - pluginManager->deinit(); - - safe_delete(pluginManager); // PluginManager before DbManager, so Db objects are deleted while DbManager still exists -#ifdef PORTABLE_CONFIG - safe_delete(updateManager); -#endif - safe_delete(populateManager); - safe_delete(importManager); - safe_delete(exportManager); - safe_delete(functionManager); - safe_delete(extraLicenseManager); - safe_delete(dbManager); - safe_delete(config); - safe_delete(codeFormatter); - safe_delete(dbAttacherFactory); - safe_delete(env); - NotifyManager::destroy(); - } - Q_CLEANUP_RESOURCE(coreSQLiteStudio); + // Deleting all singletons contained in this object, alongside with plugin deinitialization & unloading + // causes QTranslator to crash randomly during shutdown, due to some issue in Qt itself, because it tries to refresh + // some internal translators state after the translator is uninstalled, but at the same time many message resources + // are being unloaded together with plugins and it somehow causes the crash (randomly). + // At the same time if hardly find any reason to execute proper deinitialization of all singletons, when the application stops. + // The session (UI) is saved anyway independently in the UI code. + // Explicit deletion of singletons does not really have any benefits. + // Leaving this code here for some time, just to understand it later if needed, but eventually it will be deleted. +// disconnect(pluginManager, SIGNAL(aboutToUnload(Plugin*,PluginType*)), this, SLOT(pluginToBeUnloaded(Plugin*,PluginType*))); +// disconnect(pluginManager, SIGNAL(unloaded(QString,PluginType*)), this, SLOT(pluginUnloaded(QString,PluginType*))); +// if (!immediateQuit) +// { +// if (pluginManager) +// pluginManager->deinit(); + +// safe_delete(pluginManager); // PluginManager before DbManager, so Db objects are deleted while DbManager still exists +//#ifdef PORTABLE_CONFIG +// safe_delete(updateManager); +//#endif +// safe_delete(populateManager); +// safe_delete(importManager); +// safe_delete(exportManager); +// safe_delete(functionManager); +// safe_delete(extraLicenseManager); +// safe_delete(dbManager); +// safe_delete(config); +// safe_delete(codeFormatter); +// safe_delete(dbAttacherFactory); +// safe_delete(env); +// NotifyManager::destroy(); +// } +// SQLS_CLEANUP_RESOURCE(coreSQLiteStudio); } void SQLiteStudio::updateCodeFormatter() diff --git a/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.h b/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.h index 0b07717..c284133 100644 --- a/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.h +++ b/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.h @@ -10,6 +10,7 @@ class DbManager; class Config; +class CollationManager; class QProcessEnvironment; class PluginManager; class QThreadPool; @@ -29,6 +30,8 @@ class UpdateManager; #endif class ExtraLicenseManager; class SqliteExtensionManager; +class Db; +class CodeSnippetManager; /** @file */ @@ -60,6 +63,8 @@ class API_EXPORT SQLiteStudio : public QObject DECLARE_SINGLETON(SQLiteStudio) public: + typedef std::function CrashHandler; + /** * @brief Initializes SQLiteStudio object. * @param cmdListArguments Command line arguments. @@ -123,6 +128,9 @@ class API_EXPORT SQLiteStudio : public QObject ExportManager* getExportManager() const; void setExportManager(ExportManager* value); + CodeSnippetManager* getCodeSnippetManager() const; + void setCodeSnippetManager(CodeSnippetManager* newCodeSnippetManager); + int getVersion() const; QString getVersionString() const; @@ -159,6 +167,8 @@ class API_EXPORT SQLiteStudio : public QObject QStringList getInitialTranslationFiles() const; void setInitialTranslationFiles(const QStringList& value); + void installCrashHandler(CrashHandler handler); + private: /** * @brief Creates singleton instance. @@ -175,6 +185,10 @@ class API_EXPORT SQLiteStudio : public QObject */ ~SQLiteStudio(); + void setupCrashHandler(); + + QList crashHandlers; + /** * @brief Code formatter service. */ @@ -207,26 +221,28 @@ class API_EXPORT SQLiteStudio : public QObject ExportManager* exportManager = nullptr; ImportManager* importManager = nullptr; PopulateManager* populateManager = nullptr; + CodeSnippetManager* codeSnippetManager = nullptr; #ifdef PORTABLE_CONFIG UpdateManager* updateManager = nullptr; #endif ExtraLicenseManager* extraLicenseManager = nullptr; QString currentLang; QStringList initialTranslationFiles; + bool finalCleanupDone = false; private slots: void pluginLoaded(Plugin* plugin,PluginType* pluginType); void pluginToBeUnloaded(Plugin* plugin,PluginType* pluginType); void pluginUnloaded(const QString& pluginName,PluginType* pluginType); + public slots: /** * @brief Cleans up all internal objects. * - * Deletes all internal objects. It's called from destructor. + * Deletes all internal objects. It's called from qApp signal or from UI window closing event. */ void cleanUp(); - public slots: /** * @brief Updates code formatter with available plugins. * diff --git a/SQLiteStudio3/coreSQLiteStudio/tablemodifier.cpp b/SQLiteStudio3/coreSQLiteStudio/tablemodifier.cpp index 59d978c..1a8abb6 100644 --- a/SQLiteStudio3/coreSQLiteStudio/tablemodifier.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/tablemodifier.cpp @@ -41,12 +41,14 @@ void TableModifier::alterTable(SqliteCreateTablePtr newCreateTable) handleFkConstrains(newCreateTable.data(), createTable->table, newName); QString tempTableName; + bool doCopyData = !getColumnsToCopyData(newCreateTable).isEmpty(); if (table.compare(newName, Qt::CaseInsensitive) == 0) - tempTableName = renameToTemp(); + tempTableName = renameToTemp(doCopyData); newCreateTable->rebuildTokens(); sqls << newCreateTable->detokenize(); - copyDataTo(newCreateTable); + if (doCopyData) + copyDataTo(newCreateTable); handleFks(); @@ -61,24 +63,24 @@ void TableModifier::alterTable(SqliteCreateTablePtr newCreateTable) sqls << "PRAGMA foreign_keys = 1;"; } -void TableModifier::renameTo(const QString& newName) +void TableModifier::renameTo(const QString& newName, bool doCopyData) { if (!createTable) return; // Using ALTER TABLE RENAME TO is not a good solution here, because it automatically renames all occurrences in REFERENCES, // which we don't want, because we rename a lot to temporary tables and drop them. - sqls << QString("CREATE TABLE %1 AS SELECT * FROM %2;").arg(wrapObjIfNeeded(newName), wrapObjIfNeeded(table)) + sqls << QString("CREATE TABLE %1 AS SELECT * FROM %2%3;").arg(wrapObjIfNeeded(newName), wrapObjIfNeeded(table), doCopyData ? "" : " LIMIT 0") << QString("DROP TABLE %1;").arg(wrapObjIfNeeded(table)); table = newName; createTable->table = newName; } -QString TableModifier::renameToTemp() +QString TableModifier::renameToTemp(bool doCopyData) { QString name = getTempTableName(); - renameTo(name); + renameTo(name, doCopyData); return name; } @@ -189,16 +191,30 @@ bool TableModifier::handleFks(SqliteForeignKey* fk, const QString& oldName, cons bool TableModifier::handleFkConstrains(SqliteCreateTable* stmt, const QString& oldName, const QString& theNewName) { bool modified = false; - for (SqliteCreateTable::Constraint* fk : stmt->getForeignKeysByTable(oldName)) + for (SqliteCreateTable::Constraint*& fk : stmt->getForeignKeysByTable(oldName)) { if (handleFks(fk->foreignKey, oldName, theNewName)) + { modified = true; + if (fk->foreignKey->indexedColumns.isEmpty()) + { + stmt->constraints.removeOne(fk); + delete fk; + } + } } - for (SqliteCreateTable::Column::Constraint* fk : stmt->getColumnForeignKeysByTable(oldName)) + for (SqliteCreateTable::Column::Constraint*& fk : stmt->getColumnForeignKeysByTable(oldName)) { if (handleFks(fk->foreignKey, oldName, theNewName)) + { modified = true; + if (fk->foreignKey->indexedColumns.isEmpty()) + { + stmt->removeColumnConstraint(fk); + delete fk; + } + } } return modified; } @@ -302,7 +318,6 @@ bool TableModifier::handleColumnTokens(TokenList& columnsToUpdate) bool TableModifier::handleUpdateColumns(SqliteUpdate* update) { bool modified = false; - QString lowerName; QVariant colName; QString newName; QStringList newNames; @@ -401,20 +416,29 @@ QStringList TableModifier::getModifiedTables() const return modifiedTables; } -void TableModifier::copyDataTo(SqliteCreateTablePtr newCreateTable) +QList TableModifier::getColumnsToCopyData(SqliteCreateTablePtr newCreateTable) { - QStringList existingColumns = createTable->getColumnNames(); - - QStringList srcCols; - QStringList dstCols; - for (SqliteCreateTable::Column* column : newCreateTable->columns) + QList resultColumns; + QStringList existingColumnsBefore = createTable->getColumnNames(); + for (SqliteCreateTable::Column*& column : newCreateTable->columns) { if (column->hasConstraint(SqliteCreateTable::Column::Constraint::GENERATED)) continue; - if (!existingColumns.contains(column->originalName)) + if (!existingColumnsBefore.contains(column->originalName)) continue; // not copying columns that didn't exist before + resultColumns << column; + } + return resultColumns; +} + +void TableModifier::copyDataTo(SqliteCreateTablePtr newCreateTable) +{ + QStringList srcCols; + QStringList dstCols; + for (SqliteCreateTable::Column*& column : getColumnsToCopyData(newCreateTable)) + { srcCols << wrapObjIfNeeded(column->originalName); dstCols << wrapObjIfNeeded(column->name); } @@ -427,7 +451,7 @@ void TableModifier::handleIndexes() SchemaResolver resolver(db); resolver.setIgnoreSystemObjects(true); QList parsedIndexesForTable = resolver.getParsedIndexesForTable(originalTable); - for (SqliteCreateIndexPtr index : parsedIndexesForTable) + for (SqliteCreateIndexPtr& index : parsedIndexesForTable) handleIndex(index); } @@ -454,7 +478,7 @@ void TableModifier::handleTriggers() SchemaResolver resolver(db); resolver.setIgnoreSystemObjects(true); QList parsedTriggersForTable = resolver.getParsedTriggersForTable(originalTable, true); - for (SqliteCreateTriggerPtr trig : parsedTriggersForTable) + for (SqliteCreateTriggerPtr& trig : parsedTriggersForTable) handleTrigger(trig); } @@ -527,7 +551,7 @@ void TableModifier::handleTriggerQueries(SqliteCreateTriggerPtr trigger) { SqliteQuery* newQuery = nullptr; QList newQueries; - for (SqliteQuery* query : trigger->queries) + for (SqliteQuery*& query : trigger->queries) { // The handleTriggerQuery() may delete the input query object. Don't refer to it later. newQuery = handleTriggerQuery(query, trigger->trigger, trigger->table); @@ -544,7 +568,7 @@ void TableModifier::handleViews() SchemaResolver resolver(db); resolver.setIgnoreSystemObjects(true); QList parsedViewsForTable = resolver.getParsedViewsForTable(originalTable); - for (SqliteCreateViewPtr view : parsedViewsForTable) + for (SqliteCreateViewPtr& view : parsedViewsForTable) handleView(view); } @@ -608,12 +632,12 @@ SqliteSelect* TableModifier::handleSelect(SqliteSelect* select, const QString& t QList selSources = select->getAllTypedStatements(); TokenList tableTokens; StrHash resolvedTables; - for (SqliteSelect::Core* core : select->coreSelects) + for (SqliteSelect::Core*& core : select->coreSelects) { resolvedTables = tablesAsNameHash(selectResolver.resolveTables(core)); tableTokens = core->getContextTableTokens(false); - for (TokenPtr token : tableTokens) + for (TokenPtr& token : tableTokens) { if (token->value.compare(originalTable, Qt::CaseInsensitive) != 0) continue; @@ -868,7 +892,7 @@ bool TableModifier::handleExpr(SqliteExpr* expr) if (!exprList.isEmpty()) { bool res = true; - for (SqliteExpr* e : exprList) + for (SqliteExpr*& e : exprList) { res &= handleExpr(e); if (!res) @@ -906,7 +930,7 @@ void TableModifier::simpleHandleIndexes() SchemaResolver resolver(db); resolver.setIgnoreSystemObjects(true); QList parsedIndexesForTable = resolver.getParsedIndexesForTable(originalTable); - for (SqliteCreateIndexPtr index : parsedIndexesForTable) + for (SqliteCreateIndexPtr& index : parsedIndexesForTable) sqls << index->detokenize(); } @@ -920,7 +944,7 @@ void TableModifier::simpleHandleTriggers(const QString& view) else parsedTriggers = resolver.getParsedTriggersForTable(originalTable); - for (SqliteCreateTriggerPtr trig : parsedTriggers) + for (SqliteCreateTriggerPtr& trig : parsedTriggers) sqls << trig->detokenize(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/tablemodifier.h b/SQLiteStudio3/coreSQLiteStudio/tablemodifier.h index 4c6af24..600a761 100644 --- a/SQLiteStudio3/coreSQLiteStudio/tablemodifier.h +++ b/SQLiteStudio3/coreSQLiteStudio/tablemodifier.h @@ -35,8 +35,8 @@ class API_EXPORT TableModifier void parseDdl(); QString getTempTableName(); void copyDataTo(const QString& targetTable, const QStringList& srcCols, const QStringList& dstCols); - void renameTo(const QString& newName); - QString renameToTemp(); + void renameTo(const QString& newName, bool doCopyData = true); + QString renameToTemp(bool doCopyData = true); void copyDataTo(const QString& table); void copyDataTo(SqliteCreateTablePtr newCreateTable); @@ -84,7 +84,7 @@ class API_EXPORT TableModifier bool handleUpdateColumns(SqliteUpdate* update); QStringList handleUpdateColumns(const QStringList& colNames, bool& modified); QString handleUpdateColumn(const QString& colName, bool& modified); - + QList getColumnsToCopyData(SqliteCreateTablePtr newCreateTable); template bool handleIndexedColumns(QList& columnsToUpdate) diff --git a/SQLiteStudio3/coreSQLiteStudio/translations.cpp b/SQLiteStudio3/coreSQLiteStudio/translations.cpp index dc53395..72883b7 100644 --- a/SQLiteStudio3/coreSQLiteStudio/translations.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/translations.cpp @@ -22,10 +22,10 @@ void loadTranslation(const QString& baseName) QStringList filters = QStringList({baseName+"_"+lang+".qm"}); QDir dir; - for (const QString& dirPath : SQLITESTUDIO_TRANSLATION_DIRS) + for (QString& dirPath : SQLITESTUDIO_TRANSLATION_DIRS) { dir.setPath(dirPath); - for (const QString& f : dir.entryList(filters)) + for (QString& f : dir.entryList(filters)) { res = translator->load(f, dirPath); if (res) @@ -40,7 +40,10 @@ void loadTranslation(const QString& baseName) } if (!res) + { + delete translator; return; + } qApp->installTranslator(translator); SQLITESTUDIO_TRANSLATIONS[baseName] = translator; @@ -66,15 +69,15 @@ void loadTranslations(const QStringList& baseNames) QStringList getAvailableTranslations() { + static QRegularExpression re("[^\\_]+\\_(\\w+)\\.qm"); QSet locales; - QRegularExpression re("[^\\_]+\\_(\\w+)\\.qm"); QRegularExpressionMatch match; QDir dir; QStringList filters = QStringList({"*_*.qm"}); - for (const QString& dirPath : SQLITESTUDIO_TRANSLATION_DIRS) + for (QString& dirPath : SQLITESTUDIO_TRANSLATION_DIRS) { dir.setPath(dirPath); - for (const QString& f : dir.entryList(filters)) + for (QString& f : dir.entryList(filters)) { match = re.match(f); if (!match.isValid()) @@ -85,6 +88,10 @@ QStringList getAvailableTranslations() } locales << "en"; + // #4278 - the en_us translation as explicit qm file is unnecessary, + // but produced by CrowdIn. The "en" is default and used for American English. + locales.remove("en_us"); + return locales.values(); } @@ -114,5 +121,5 @@ void setDefaultLanguage(const QString& lang) QString getConfigLanguageDefault() { - return CFG_CORE.General.Language.getDefultValue().toString(); + return CFG_CORE.General.Language.getDefaultValue().toString(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio.ts new file mode 100644 index 0000000..ba114ea --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio.ts @@ -0,0 +1,1099 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + + + + + Error attaching database %1: %2 + + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + + + + + The database for executing queries was not open. + chain executor + + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + + + + + Could not start a database transaction. Details: %1 + chain executor + + + + + Interrupted + chain executor + + + + + Could not commit a database transaction. Details: %1 + chain executor + + + + + CompletionHelper + + + New row reference + + + + + Old row reference + + + + + New table name + + + + + New index name + + + + + New view name + + + + + New trigger name + + + + + Table or column alias + + + + + transaction name + + + + + New column name + + + + + Column data type + + + + + Constraint name + + + + + Error message + + + + + Any word + + + + + Default database + + + + + Temporary objects database + + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + + DbManagerImpl + + + Could not add database %1: %2 + + + + + Database %1 could not be updated, because of an error: %2 + + + + + + Database file doesn't exist. + + + + + + + No supporting plugin loaded. + + + + + Database could not be initialized. + + + + + No suitable database driver plugin found. + + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + + + + + Could not parse table. + + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + + Error while copying data for table %1: %2 + + + + + + + Error while copying data to table %1: %2 + + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + + Error while creating view in target database: %1 + + + + + Error while creating index in target database: %1 + + + + + Error while creating trigger in target database: %1 + + + + + + + Could not parse object '%1' in order to move or copy it. + + + + + DdlHistoryModel + + + Database name + ddl history header + + + + + Database file + ddl history header + + + + + Date of execution + ddl history header + + + + + Changes + ddl history header + + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + + + + + Export plugin %1 doesn't support exporing tables. + + + + + Export plugin %1 doesn't support exporing databases. + + + + + Export format '%1' is not supported. Supported formats are: %2. + + + + + Export to the clipboard was successful. + + + + + Export to the file '%1' was successful. + + + + + Export was successful. + + + + + Could not export to file %1. File cannot be open for writting. + + + + + ExportWorker + + + Error while exporting query results: %1 + + + + + Error while counting data column width to export from query results: %1 + + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + + Error while reading data to export from table %1: %2 + + + + + Error while counting data to export from table %1: %2 + + + + + Error while counting data column width to export from table %1: %2 + + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + + No such function registered in SQLiteStudio: %1(%2) + + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + + Invalid regular expression pattern: %1 + + + + + + Could not open file %1 for reading: %2 + + + + + Could not open file %1 for writting: %2 + + + + + Error while writting to file %1: %2 + + + + + Unsupported scripting language: %1 + + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + + ImportWorker + + + No columns provided by the import plugin. + + + + + Could not start transaction in order to import a data: %1 + + + + + Could not commit transaction for imported data: %1 + + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + + Could not create table to import to: %1 + + + + + + + Error while importing data: %1 + + + + + + Interrupted. + import process status update + + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + + Cannot load plugin %1. Error details: %2 + + + + + Cannot load plugin %1 (error while initializing plugin). + + + + + min: %1 + plugin dependency version + + + + + max: %1 + plugin dependency version + + + + + PopulateConstant + + + Constant + populate constant plugin name + + + + + PopulateConstantConfig + + + Constant value: + + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + + + + + PopulateDictionaryConfig + + + Dictionary file + + + + + Pick dictionary file + + + + + Word separator + + + + + Whitespace + + + + + Line break + + + + + Method of using words + + + + + Ordered + + + + + Randomly + + + + + PopulateManager + + + Table '%1' populated successfully. + + + + + PopulateRandom + + + Random number + + + + + PopulateRandomConfig + + + Constant prefix + + + + + No prefix + + + + + Minimum value + + + + + Maximum value + + + + + Constant suffix + + + + + No suffix + + + + + PopulateRandomText + + + Random text + + + + + PopulateRandomTextConfig + + + Use characters from common sets: + + + + + Minimum length + + + + + Letters from a to z. + + + + + Alpha + + + + + Numbers from 0 to 9. + + + + + Numeric + + + + + A whitespace, a tab and a new line character. + + + + + Whitespace + + + + + Includes all above and all others. + + + + + Binary + + + + + Use characters from my custom set: + + + + + Maximum length + + + + + If you type some character multiple times, it's more likely to be used. + + + + + PopulateScript + + + Script + + + + + PopulateScriptConfig + + + Initialization code (optional) + + + + + Per step code + + + + + Language + + + + + Help + + + + + PopulateSequence + + + Sequence + + + + + PopulateSequenceConfig + + + Start value: + + + + + Step: + + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + + + + + Error while populating table: %1 + + + + + Could not commit transaction after table populating. Error details: %1 + + + + + QObject + + + Could not open file '%1' for reading: %2 + + + + + Could not open database: %1 + + + + + Result set expired or no row available. + + + + + + Could not load extension %1: %2 + + + + + Could not run WAL checkpoint: %1 + + + + + Could not close database: %1 + + + + + Could not attach database %1: %2 + + + + + + Incomplete query. + + + + + Parser stack overflow + + + + + Syntax error + + + + + Could not open dictionary file %1 for reading. + + + + + Dictionary file must exist and be readable. + + + + + Maximum value cannot be less than minimum value. + + + + + Maximum length cannot be less than minimum length. + + + + + Custom character set cannot be empty. + + + + + Could not find plugin to support scripting language: %1 + + + + + Error while executing populating initial code: %1 + + + + + Error while executing populating code: %1 + + + + + Select implementation language. + + + + + Implementation code cannot be empty. + + + + + Could not resolve data source for column: %1 + + + + + Could not resolve table for column '%1'. + + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + + General purpose + plugin category name + + + + + Database support + plugin category name + + + + + Code formatter + plugin category name + + + + + Scripting languages + plugin category name + + + + + Exporting + plugin category name + + + + + Importing + plugin category name + + + + + Table populating + plugin category name + + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + + Cannot not update trigger %1 according to table %2 modification. + + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + + Could not parse DDL of the view to be created. Details: %1 + + + + + Parsed query is not CREATE VIEW. It's: %1 + + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + + QueryExecutor + + + Execution interrupted. + + + + + Database is not open. + + + + + Only one query can be executed simultaneously. + + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + + + + + Error from %1: %2 + + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + + Could not open file '%1' for reading: %2 + + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + + Finished executing %1 queries in %2 seconds. + + + + + Could not execute SQL due to error. + + + + + SqlHistoryModel + + + Database + sql history header + + + + + Execution date + sql history header + + + + + Time spent + sql history header + + + + + Rows affected + sql history header + + + + + SQL + sql history header + + + + + UpdateManager + + + Could not check for updates (%1). + + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_af_ZA.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_af_ZA.ts new file mode 100644 index 0000000..2f9d9f2 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_af_ZA.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ar_SA.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ar_SA.ts new file mode 100644 index 0000000..d61f7f8 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ar_SA.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ca_ES.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ca_ES.ts new file mode 100644 index 0000000..71fc147 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ca_ES.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_cs_CZ.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_cs_CZ.ts new file mode 100644 index 0000000..8625bc8 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_cs_CZ.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_da_DK.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_da_DK.ts new file mode 100644 index 0000000..ce31614 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_da_DK.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de.qm b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de.qm deleted file mode 100644 index 29cc169..0000000 Binary files a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de.qm and /dev/null differ diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de.ts deleted file mode 100644 index 803f815..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de.ts +++ /dev/null @@ -1,1320 +0,0 @@ - - - - - AbstractDb - - - - Cannot execute query on closed database. - Die Abfrage kann nicht auf einer ungeöffneten Datenbank ausgeführt werden. - - - - Error attaching database %1: %2 - Fehler beim Anhängen der Datenbank %1: %2 - - - - BugReporter - - Invalid login or password - Ungültiger Nutzername oder Passwort - - - - ChainExecutor - - - The database for executing queries was not defined. - chain executor - Es wurde keine Datenbank zur Ausführung von Abfragen festgelegt. - - - - The database for executing queries was not open. - chain executor - Die Datenbank ist zur Ausführung von Abfragen nicht geöffnet worden. - - - - Could not disable foreign keys in the database. Details: %1 - chain executor - Fremdschlüssel konnten nicht deaktiviert werden für die Datenbank. Details: %1 - - - - Could not start a database transaction. Details: %1 - chain executor - Es kann keine Datenbanktransaktion gestartet werden. Details: %1 - - - - Interrupted - chain executor - Abgebrochen - - - - Could not commit a database transaction. Details: %1 - chain executor - Die Datenbanktransaktion kann nicht 'committet' werden. Details: %1 - - - - CompletionHelper - - - New row reference - Neue Zeilenreferenz - - - - Old row reference - Alte Zeilenreferenz - - - - New table name - Neuer Tabellenname - - - - New index name - Neuer Indizename - - - - New view name - Neuer Viewname - - - - New trigger name - Neuer Triggername - - - - Table or column alias - Tabellen- oder Spaltenalias - - - - transaction name - Transaktionsname - - - - New column name - Neuer Spaltenname - - - - Column data type - Spaltendatentyp - - - - Constraint name - Abhängigkeitsname - - - - Error message - Fehlermeldung - - - - Collation name - Hier weiß ich nicht wie man das sprechend übersetzen kann. - Kollationsname - - - - Any word - Beliebiges Wort - - - - Default database - Standarddatenbank - - - - Temporary objects database - Datenbank für temporäre Objekte - - - - ConfigImpl - - - Could not start database transaction for deleting SQL history, therefore it's not deleted. - - - - - Could not commit database transaction for deleting SQL history, therefore it's not deleted. - - - - - DbManagerImpl - - - Could not add database %1: %2 - Die Datenbank %1 kann nicht hinzugefügt werden: %2 - - - - Database %1 could not be updated, because of an error: %2 - Die Datenbank %1 kann nicht aktualisiert werden. Grund: %2 - - - - - Database file doesn't exist. - Die Datenbankdatei existiert nicht. - - - - - - No supporting plugin loaded. - Es wurde kein passendes plugin geladen. - - - - Database could not be initialized. - Die Datenbank kann nicht initialisiert werden. - - - - No suitable database driver plugin found. - Es wurde kein passender Datenbanktreiber (plugin) gefunden. - - - - DbObjectOrganizer - - - - Error while creating table in target database: %1 - Fehler beim Erstellen einer Tabelle in der Zieldatenbank %1 - - - - Could not parse table. - Die Tabelle konnte nicht verarbeitet werden. - - - - Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. - Datenbank %1 konnte nicht mit Datenbank %2 verbunden werden, daher werden die Daten der Tabelle %3 durch SQLiteStudio kopiert. Diese Methode kann bei großen Tabellen sehr lange dauern, bitte haben Sie Geduld. - - - - Error while copying data for table %1: %2 - Fehler beim Kopieren von Daten für Tabelle %1: %2 - - - - - - Error while copying data to table %1: %2 - Fehler beim Kopieren von Daten in die Tabelle %1: %2 - - - - Error while dropping source view %1: %2 -Tables, indexes, triggers and views copied to database %3 will remain. - Fehler beim Entfernen des Quellviews %1: %2 -Tabellen, Indizes, Trigger und Views die in Datenbank %3 kopiert wurden, werden auf der Quelldatenbank verbleiben. - - - - Error while creating view in target database: %1 - Fehler beim Erstellen eines Views in der Zieldatenbank %1 - - - - Error while creating index in target database: %1 - Fehler beim Erstellen eines Indizes in der Zieldatenbank %1 - - - - Error while creating trigger in target database: %1 - Fehler beim Erstellen eines Triggers in der Zieldatenbank %1 - - - - - - Could not parse object '%1' in order to move or copy it. - Objekt '%1' konnte analysieren, um es zu verschieben oder kopieren zu können. - - - - DbVersionConverter - - - Target file exists, but could not be overwritten. - Die Zieldatei existiert zwar, konnte aber nicht überschrieben werden. - - - - Could not find proper database plugin to create target database. - Es konnte kein geeignetes Datenbankplugin gefunden werden, um die Zieldatenbank zu erzeugen. - - - - Error while converting database: %1 - Fehler beim Konvertieren der Datenbank: %1 - - - - DdlHistoryModel - - - Database name - ddl history header - Datenbankname - - - - Database file - ddl history header - Datenbankdatei - - - - Date of execution - ddl history header - Datum der Ausführung - - - - Changes - ddl history header - Änderungen - - - - ExportManager - - - Export plugin %1 doesn't support exporing query results. - Das Exportplugin %1 unterstützt das Exportieren der Abfrageergebnisse nicht. - - - - Export plugin %1 doesn't support exporing tables. - Das Exportplugin %1 unterstützt das Exportieren von Tabellen nicht. - - - - Export plugin %1 doesn't support exporing databases. - Das Exportplugin %1 unterstützt das Exportieren von Datenbanken nicht. - - - - Export format '%1' is not supported. Supported formats are: %2. - Das Exportformat %1 wird nicht unterstützt. Unterstützte Formate sind: %2. - - - - Export to the clipboard was successful. - Der Export in die Zwischenablage war erfolgreich. - - - - Export to the file '%1' was successful. - Der Export in die Datei %1 war erfolgreich. - - - - Export was successful. - Der Export war erfolgreich. - - - - Could not export to file %1. File cannot be open for writting. - Es kann nicht in die Datei %1 exportiert werden. Die Datei lässt sich nicht für Schreibzugriffe öffnen. - - - - ExportWorker - - - Error while exporting query results: %1 - Fehler beim Exportieren der Abfrageergebnisse: %1 - - - - Error while counting data column width to export from query results: %1 - Fehler beim Ermitteln der Spaltenbreite für den Export der Abfrageergebnisse: %1 - - - - - Could not parse %1 in order to export it. It will be excluded from the export output. - %1 konnte zum Exportieren nicht korrekt verarbeitet werden. Diese Daten werden nicht exportiert. - - - - Error while reading data to export from table %1: %2 - Fehler beim Lesen der zu exportierenden Daten aus der Tabelle %1: %2 - - - - Error while counting data to export from table %1: %2 - Fehler beim Ermitteln der zu exportierenden Daten aus der Tabelle %1: %2 - - - - Error while counting data column width to export from table %1: %2 - Fehler beim Ermitteln der Spaltenbreite für den Export aus Tabelle %1: %2 - - - - FunctionManagerImpl - - - Invalid number of arguments to function '%1'. Expected %2, but got %3. - Falsche Anzahl an Parametern für Funktion '%1'. Erwartet wurden %2, angegeben wurden jedoch %3. - - - - No such function registered in SQLiteStudio: %1(%2) - Diese Funktion ist in SQLiteStudio nicht verfügbar: %1(%2) - - - - Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. - Auch hier ist sicherlich eine Überarbeitung nötig, wenn der Kontext der Meldung bekannt ist. - Die Funktion %1(%2) wurde für die Sprache %3 erstellt, jedoch ist das Plugin, welches diese Sprache unterstützt, derzeit nicht geladen. - - - - Invalid regular expression pattern: %1 - Ungültiges Muster für die regulären Ausdrücke: %1 - - - - - Could not open file %1 for reading: %2 - Datei %1 kann nicht für Lesezugriffe geöffnet werden: %2 - - - - Could not open file %1 for writting: %2 - Datei %1 kann nicht für Schreibzugriffe geöffnet werden: %2 - - - - Error while writting to file %1: %2 - Fehler beim Schreiben in Datei %1: %2 - - - - Unsupported scripting language: %1 - Nicht unterstützte Skriptsprache: %1 - - - - GenericExportPlugin - - - Could not initialize text codec for exporting. Using default codec: %1 - Der 'Export Textcodec' konnte nicht initialisiert werden. Es wird der Standardcodec genutzt: %1 - - - - ImportManager - - - Imported data to the table '%1' successfully. - Die Daten wurden erfolgreich in Tabelle %1 importiert. - - - - ImportWorker - - - No columns provided by the import plugin. - Dieses Importplugin stellt keine Spalten zur Verfügung. - - - - Could not start transaction in order to import a data: %1 - Es kann keine Transaktion zum Import der Daten gestartet werden: %1 - - - - Could not commit transaction for imported data: %1 - Die Transaktion für die importierten Daten kann nicht 'committet' werden: %1 - - - - Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. - Die Tabelle %1 hat weniger Spalten als die zu importierenden Daten liefern. Überschüssige Spalten werden daher ignoriert. - - - - Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. - Die Tabelle %1 hat mehr Spalten als die zu importierenden Daten liefern. Einige Tabellenspalten werden deshalb leer bleiben. - - - - Could not create table to import to: %1 - Die Tabelle, in die importiert werden soll, kann nicht erstellt werden: %1 - - - - - - Error while importing data: %1 - Fehler beim Import der Daten: %1 - - - - - Interrupted. - import process status update - Abgebrochen. - - - - Could not import data row number %1. The row was ignored. Problem details: %2 - Datenzeile %1 konnte nicht importiert werden. Die Zeile wurde ignoriert. Problembeschreibung: %2 - - - - PluginManagerImpl - - - Cannot load plugin %1, because it's in conflict with plugin %2. - Plugin %1 konnte nicht geladen werden, weil ein Konflikt besteht mit Plugin %2. - - - - Cannot load plugin %1, because its dependency was not loaded: %2. - Plugin %1 konnte nicht geladen werden, da dessen Abhängigkeiten nicht geladen worden sind: %2. - - - - Cannot load plugin %1. Error details: %2 - Plugin %1 konnte nicht geladen. Problembeschreibung: %2 - - - - Cannot load plugin %1 (error while initializing plugin). - Plugin %1 konnte wegen eines Fehlers bei der Initialisierung nicht geladen werden. - - - - min: %1 - plugin dependency version - Min: %1 - - - - max: %1 - plugin dependency version - Max: %1 - - - - PopulateConstant - - - Constant - populate constant plugin name - Hier bin ich mir absolut nicht sicher was genau gemeint ist. - konstanten - - - - PopulateConstantConfig - - - Constant value: - Konstanter Wert: - - - - PopulateDictionary - - - Dictionary - dictionary populating plugin name - Wörterbuch - - - - PopulateDictionaryConfig - - - Dictionary file - Wörterbuchdatei - - - - Pick dictionary file - Wähle Wörterbuchdatei aus - - - - Word separator - Wortseperator - - - - Whitespace - Leerzeichen - - - - Line break - Zeilenumbruch - - - - Method of using words - Methode der Wortverwendung - - - - Ordered - Sortiert - - - - Randomly - Zufällig - - - - PopulateManager - - - Table '%1' populated successfully. - Tabelle %1 wurde erfolgreich gefüllt. - - - - PopulateRandom - - - Random number - Zufällige Nummer - - - - PopulateRandomConfig - - - Constant prefix - Konstanter Präfix - - - - No prefix - Kein Präfix - - - - Minimum value - Kleinster Wert - - - - Maximum value - Größter Wert - - - - Constant suffix - Konstanter Suffix - - - - No suffix - Kein Suffix - - - - PopulateRandomText - - - Random text - Zufälliger Text - - - - PopulateRandomTextConfig - - - Use characters from common sets: - Benutze Zeichen von den gebräuchlichsten Sets: - - - - Minimum length - Mindestlänge - - - - Letters from a to z. - Buchstaben von a bis z. - - - - Alpha - Alphanumerisch - - - - Numbers from 0 to 9. - Ziffern von 0 bis 9. - - - - Numeric - Numerisch - - - - A whitespace, a tab and a new line character. - Ein Leerzeichen, ein Tab und ein Zeilenumbruchzeichen. - - - - Whitespace - Leerzeichen - - - - Includes all above and all others. - Alle obigen und auch andere. - - - - Binary - Binär - - - - Use characters from my custom set: - Benutze Zeichen von meinem benutzerdefinierten Set: - - - - Maximum length - Maximallänge - - - - If you type some character multiple times, it's more likely to be used. - Auch hier kann man besser übersetzen, wenn man den Kontext kennt. - Wenn Sie einige Buchstaben mehrmals eingeben, dann ist es wahrscheinlicher, dass sie genutzt werden. - - - - PopulateScript - - - Script - Skript - - - - PopulateScriptConfig - - - Initialization code (optional) - Initialisierungscode (optional) - - - - Per step code - Einzelschrittcode - - - - Language - Sprache - - - - Help - Hilfe - - - - PopulateSequence - - - Sequence - Sequenz - - - - PopulateSequenceConfig - - - Start value: - Startwert: - - - - Step: - Schritt: - - - - PopulateWorker - - - Could not start transaction in order to perform table populating. Error details: %1 - Es kann keine Transaktion zum Füllen der Tabelle gestartet werden. Problembeschreibung: %1 - - - - Error while populating table: %1 - Fehler beim Füllen der Tabelle: %1 - - - - Could not commit transaction after table populating. Error details: %1 - Die Transaktion zum Füllen der Tabelle kann nicht 'committet' werden. Problembeschreibung: %1 - - - - QObject - - - - Could not open database: %1 - Die Datenbank %1 kann nicht geöffnet werden. - - - - - Result set expired or no row available. - Das Abfrageergebniss ist ungültig oder es ist keine Datenzeile verfügbar. - - - - - Could not load extension %1: %2 - - - - - Could not close database: %1 - Die Datenbank %1 kann nicht geschlossen werden. - - - - - - - - - - SQLite %1 does not support '%2' statement. - SQLite %1 unterstützt keine '%2' Abfrage. - - - - SQLite %1 does not support '%2' statement, but the regular table can be created instead if you proceed. - SQLite %1 unterstützt keine '%2' Abfrage, aber die normale Tabelle kann stattdessen erzeugt werden, wenn Sie fortfahren. - - - - Could not parse statement: %1 -Error details: %2 - Die Abfrage kann nicht verarbeitet werden: %1 -Problembeschreibung: %2 - - - - - - - SQLite %1 does not support the '%2' clause. Cannot convert '%3' statement with that clause. - SQLite %1 unterstützt keine '%2' Klausel. Die %3 Abfrage kann mit dieser Klausel nicht konvertiert werden. - - - - SQLite %1 does not support the '%2' clause in the '%3' statement. - SQLite %1 unterstützt keine '%2' Klausel in der %3 Abfrage. - - - - SQLite %1 does not support current date or time clauses in expressions. - SQLite %1 unterstützt keine current date' oder 'time' Klauseln in Ausdrücken. - - - - SQLite %1 does not support row value clauses in expressions. - - - - - - - SQLite %1 does not support '%2' clause in expressions. - SQLite %1 unterstützt keine '%2' Klausel Ausdrücken. - - - - Could not attach database %1: %2 - Generell muss in den Übersetzungen das 'attached' genauer analysiert werden. - Die Datenbank %1 kann nicht angefügt werden: %2 - - - - - Incomplete query. - Unvollständige Abfrage. - - - - - Parser stack overflow - Stacküberlauf bei Verarbeitung - - - - - Syntax error - Syntaxfehler - - - - Could not open dictionary file %1 for reading. - Die Wörterbuchdatei %1 kann für den Lesezugriff nicht geöffnet werden. - - - - Dictionary file must exist and be readable. - Die Wörterbuchdatei muss vorhanden sein und Leserechte besitzen. - - - - Maximum value cannot be less than minimum value. - Der Maximalwert kann nicht kleiner sein als der Minimalwert. - - - - Maximum length cannot be less than minimum length. - Die Maximallänge kann nicht kleiner sein als die Minimallänge. - - - - Custom character set cannot be empty. - Ein benutzerdefiniertes Zeichenset darf nicht leer sein. - - - - Could not find plugin to support scripting language: %1 - Das Plugin zur Unterstützung der folgenden Skriptsprache kann nicht gefunden werden: %1 - - - - Error while executing populating initial code: %1 - Fehler beim Ausführen des Initialisierungsprozesses: %1 - - - - Error while executing populating code: %1 - Fehler beim Ausführen des Auffüllprozesses: %1 - - - - Select implementation language. - Wählen Sie die Implementationssprache. - - - - Implementation code cannot be empty. - Der Implementationscode darf nicht leer sein. - - - - Could not resolve data source for column: %1 - Die Datenquelle für Spalte %1 kann nicht aufgelöst werden. - - - - Could not resolve table for column '%1'. - - - - - Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Tried to initialize the file at following localizations: %1. - Ich hoffe, dass ich "to initialize" hier richtig mit "erstellt" übersetzt habe. - Die Konfigurationsdatei kann nicht erstellt werden. Alle Änderungen an der Konfiguration sowie die Abfragehistorie sind nach einem Programmneustart verloren. Es wurde versucht die Konfigurationsdatei in folgendem Verzeichnis zu erstellen: %1 - - - - General purpose - plugin category name - Allgemeine Verwendung - - - - Database support - plugin category name - Datenbankunterstützung - - - - Code formatter - plugin category name - Codeformatierer - - - - Scripting languages - plugin category name - Skriptsprachen - - - - Exporting - plugin category name - Exportieren - - - - Importing - plugin category name - Importieren - - - - Table populating - plugin category name - Tabellen füllen - - - - Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. - Tabelle %1 referenziert Tabelle %2, jedoch wird die 'foreign key'-Definition für die neue Tabellendefinition nicht aktualisiert, da es Probleme bei der DDL-Analyse von Tabelle %3 gibt. - - - - All columns indexed by the index %1 are gone. The index will not be recreated after table modification. - Alle Spalten, die von Index %1 indiziert wurden, sind verloren. Der Index wird nach der Tabellenmodifikation nicht neu erstellt. - - - - There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. - Es ist ein Problem bei der korrekten Verarbeitung des Triggers %1 aufgetreten. Er wird möglicherweise nicht vollständig aktualisert werden und sollte geprüft werden. - - - - Cannot not update trigger %1 according to table %2 modification. - Die Aktualisierung des Triggers %1, resultierend aus der Änderung der Tabelle %2, kann nicht ausgeführt werden. - - - - - - There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - Es ist ein Problem beim Aktualisieren einer %1 Abfrage innerhalb eines %2 Triggers aufgetreten. Eine der %1 Unterabfragen, welche möglicherweise die Tabelle %3 referenziert, kann nicht geändert werden. Eine manuelle Anpassung des Triggers wird nötig sein. - - - - All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. - Alle Spalten, die durch den Trigger %1 abgedeckt wurden, sind verloren. Der Trigger wird nach der Änderung nicht wiederhergestellt. - - - - Cannot not update view %1 according to table %2 modifications. -The view will remain as it is. - Die Aktualisierung des Views %1, resultierend aus der Änderung der Tabelle %2, kann nicht ausgeführt werden. Der View wird daher nicht geändert. - - - - Could not parse DDL of the view to be created. Details: %1 - DDL des zu erzeugenden Views kann nicht verarbeitet werden. Details: %1 - - - - Parsed query is not CREATE VIEW. It's: %1 - Die zu verarbeitende Abfrage ist nicht CREATE VIEW, es ist: %1 - - - - SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. - SQLiteStudio konnte die vom View zurückgegebenen Spalten nicht auflösen, daher kann nicht ermittelt werden, welcher Trigger beim Wiederherstelllungsprozess einen Fehler verusracht haben könnte. - - - - Could not open file '%1' for reading: %2 - - - - - QueryExecutor - - - Execution interrupted. - Ausführung abgebrochen. - - - - Database is not open. - Die Datenbank ist nicht geöffnet. - - - - Only one query can be executed simultaneously. - Es kann nur eine Abfrage gleichzeitig ausgeführt werden. - - - - - An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 - Hier muss ggf. noch das 'data paging' korrekt übersetzt werden. - Beim Ausführen der count(*) Abfrage ist ein Fehler aufgetreten, daher wird das data paging abgeschaltet. Problemdetails der Datenbank: %1 - - - - SQLiteStudio was unable to extract metadata from the query. Results won't be editable. - SQLiteStudio konnte keine Metadaten aus der Abfrage extrahieren. Die Ergebnismenge kann daher nicht editiert werden. - - - - ScriptingQtDbProxy - - - No database available in current context, while called QtScript's %1 command. - Beim Aufruf des QtSkript Kommandos %1 ist im zugehörigen Kontext keine Datenbank verfügbar. - - - - Error from %1: %2 - Fehler von %1: %2 - - - - SqlHistoryModel - - - Database - sql history header - Datenbank - - - - Execution date - sql history header - Ausführungsdatum - - - - Time spent - sql history header - Dauer - - - - Rows affected - sql history header - Anzahl Zeilen - - - - SQL - sql history header - SQL - - - - UpdateManager - - An error occurred while checking for updates: %1. - Beim Prüfen auf Updates trat folgender Fehler auf: %1. - - - Could not check available updates, because server responded with invalid message format. It is safe to ignore this warning. - Es konnte nicht auf neue Updates geprüft werden, da der Updateserver in einem ungültigen Nachrichtenformat antwortet. Diese Meldung kann gefahrlos ignoriert werden. - - - An error occurred while reading updates metadata: %1. - Beim Lesen der Update-Metadaten ist ein Fehler aufgetreten: %1. - - - Could not download updates, because server responded with invalid message format. You can try again later or download and install updates manually. See <a href="%1">User Manual</a> for details. - Das Update konnte nicht heruntergeladen werden, da der Updateserver in einem ungültigen Nachrichtenformat antwortet. Sie können es später noch einmal versuchen oder das Update und die Installation manuell ausführen. Weitere Infoamtionen hierzu finden Sie in der <a href="%1">Programmdokumentation</a>. - - - Could not create temporary directory for downloading the update. Updating aborted. - Das temporäre Verzeichnis zum Herunterladen des Updates konnte nicht erstellt werden. Der Updatevorgang wird abgebrochen. - - - There was no updates to download. Updating aborted. - Keine neuen Updates vorhanden. Der Updatevorgang wird beendet. - - - Downloading: %1 - Herunterladen von: %1 - - - Could not determinate file name from update URL: %1. Updating aborted. - Der Dateiname der Update-URL %1 konnte nicht ermittelt werden. Der Updatevorgang wird abgebrochen. - - - Failed to open file '%1' for writting: %2. Updating aborted. - Beim Schreiben in die Datei '%1' trat folgender Fehler auf: %2. Der Updatevorgang wird abgebrochen. - - - Installing updates. - Update wird installiert. - - - Could not copy current application directory into %1 directory. - Das aktuelle Programmverzeichnis konnte nicht in das Verzeichnis %1 kopiert werden. - - - Could not create directory %1. - Das Verzeichnis %1 konnte nicht erstellt werden. - - - Could not rename directory %1 to %2. -Details: %3 - Das Verzeichnis %1 konnte nicht in %2 umbenannt werden. -Details: %3 - - - Cannot not rename directory %1 to %2. -Details: %3 - Das Verzeichnis %1 kann nicht in %2 umbenannt werden. -Details: %3 - - - Could not move directory %1 to %2 and also failed to restore original directory, so the original SQLiteStudio directory is now located at: %3 - Das Verzeichnis %1 konnte nicht nach %2 verschoben werden. Ebenso schlug das Wiederherstellen des originalen Verzeichnissses fehlt, daher befindet sich das SQLiteStudio Verzeichnis nun hier: %3 - - - Could not rename directory %1 to %2. Rolled back to the original SQLiteStudio version. - Das Das Verzeichnis %1 konnte nicht nach %2 umbenannt werden.SQLiteStudio wird auf den Ursprungszustand zurückgesetzt. - - - Could not unpack component %1 into %2 directory. - Die Komponente %1 konnte nicht in das Verzeichnis %2 extrahiert werden. - - - Could not find permissions elevator application to run update as a root. Looked for: %1 - Die Rechteerweiterung zum Ausführen des Updates als 'root' konnte nicht gefunden werden. Es wurde gesucht nach: %1 - - - Could not execute final updating steps as root: %1 - Die abschließenden Aktualisierungsschritte konnten nicht als 'root' ausgeführt werden: %1 - - - Could not execute final updating steps as admin: %1 - Die abschließenden Aktualisierungsschritte konnten nicht als 'admin' ausgeführt werden: %1 - - - Cannot create temporary directory for updater. - Das temporäre Verzeichnis für den Updater konnte nicht erstellt werden. - - - Cannot create updater script file. - Die Skriptdatei für den Updater konnte nicht erstellt werden. - - - Updating canceled. - Updatevorgang abgebrochen. - - - Could not execute final updating steps as administrator. - Die abschließenden Aktualisierungsschritte konnten nicht als 'administrator' ausgeführt werden. - - - Could not execute final updating steps as administrator. Updater startup timed out. - Die abschließenden Aktualisierungsschritte konnten nicht als 'administrator' ausgeführt werden. Die Updatevorbereitungen liefen auf Zeitüberschreitung. - - - Could not execute final updating steps as administrator. Updater operation timed out. - Die abschließenden Aktualisierungsschritte konnten nicht als 'administrator' ausgeführt werden. Der Updatevorgang lief auf Zeitüberschreitung. - - - Could not clean up temporary directory %1. You can delete it manually at any time. - Das temporäre Verzeichnis %1 konnte nicht aufgeräumt werden. Sie können es später manuell löschen. - - - Could not run new version for continuing update. - Die neue Version zum Fortführen des Updates kann nicht gestartet werden. - - - Package not in tar.gz format, cannot install: %1 - Das Paket liegt nicht im tar.gz Format vor. %1 kann nicht installiert werden. - - - Package %1 cannot be installed, because cannot move it to directory: %2 - Das Paket %1 kann nicht installiert werden, weil es nicht in das Verzeichnis %2 verschoben werden kann. - - - Package %1 cannot be installed, because cannot unpack it: %2 - Das Paket %1 kann nicht installiert werden, weil es nicht extrahiert werden kann: %2 - - - Package not in zip format, cannot install: %1 - Das Paket liegt nicht im zip Format vor. %1 kann nicht installiert werden. - - - Package %1 cannot be installed, because cannot unzip it to directory %2: %3 - Das Paket %1 kann nicht installiert werden, weil es nicht in das Verzeichnis %2: %3 extrahiert werden kann. - - - Package %1 cannot be installed, because cannot unzip it to directory: %2 - Das Paket %1 kann nicht installiert werden, weil es nicht in das Verzeichnis %2 entzippt werden kann. - - - Could not rename directory %1 to %2. - Das Verzeichnis %1 konnte nicht in %2 umbenannt werden. - - - Could not delete directory %1. - Das Verzeichnis %1 konnte nicht gelöscht werden. - - - Error executing update command: %1 -Error message: %2 - Fehler beim Ausführen des Updatekommandos %1. -Fehlerbeschreibung: %2 - - - An error occurred while downloading updates: %1. Updating aborted. - Beim Herunterladen des Updates %1 ist ein fehelr aufgetreten. Der Updatevorgang wurde abgebrochen. - - - - Updates installer executable is missing. - - - - - - Unable to check for updates (%1) - - - - - details are unknown - - - - - Unable to run updater application (%1). Please report this. - - - - diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de_DE.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de_DE.ts new file mode 100644 index 0000000..8bb0a2c --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_de_DE.ts @@ -0,0 +1,1100 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Die Abfrage kann nicht auf einer ungeöffneten Datenbank ausgeführt werden. + + + + Error attaching database %1: %2 + Fehler beim Anhängen der Datenbank %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + Es wurde keine Datenbank zur Ausführung von Abfragen festgelegt. + + + + The database for executing queries was not open. + chain executor + Die Datenbank ist zur Ausführung von Abfragen nicht geöffnet worden. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Fremdschlüssel konnten nicht deaktiviert werden für die Datenbank. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Es kann keine Datenbanktransaktion gestartet werden. Details: %1 + + + + Interrupted + chain executor + Abgebrochen + + + + Could not commit a database transaction. Details: %1 + chain executor + Die Datenbanktransaktion kann nicht 'committet' werden. Details: %1 + + + + CompletionHelper + + + New row reference + Neue Zeilenreferenz + + + + Old row reference + Alte Zeilenreferenz + + + + New table name + Neuer Tabellenname + + + + New index name + Neuer Indizename + + + + New view name + Neuer Viewname + + + + New trigger name + Neuer Triggername + + + + Table or column alias + Tabellen- oder Spaltenalias + + + + transaction name + Transaktionsname + + + + New column name + Neuer Spaltenname + + + + Column data type + Spaltendatentyp + + + + Constraint name + Abhängigkeitsname + + + + Error message + Fehlermeldung + + + + Any word + Beliebiges Wort + + + + Default database + Standarddatenbank + + + + Temporary objects database + Datenbank für temporäre Objekte + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Die Datenbank %1 kann nicht hinzugefügt werden: %2 + + + + Database %1 could not be updated, because of an error: %2 + Die Datenbank %1 kann nicht aktualisiert werden. Grund: %2 + + + + + Database file doesn't exist. + Die Datenbankdatei existiert nicht. + + + + + + No supporting plugin loaded. + Es wurde kein passendes plugin geladen. + + + + Database could not be initialized. + Die Datenbank kann nicht initialisiert werden. + + + + No suitable database driver plugin found. + Es wurde kein passender Datenbanktreiber (plugin) gefunden. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Fehler beim Erstellen einer Tabelle in der Zieldatenbank %1 + + + + Could not parse table. + Die Tabelle konnte nicht verarbeitet werden. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Datenbank %1 konnte nicht mit Datenbank %2 verbunden werden, daher werden die Daten der Tabelle %3 durch SQLiteStudio kopiert. Diese Methode kann bei großen Tabellen sehr lange dauern, bitte haben Sie Geduld. + + + + Error while copying data for table %1: %2 + Fehler beim Kopieren von Daten für Tabelle %1: %2 + + + + + + Error while copying data to table %1: %2 + Fehler beim Kopieren von Daten in die Tabelle %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Fehler beim Entfernen des Quellviews %1: %2 +Tabellen, Indizes, Trigger und Views die in Datenbank %3 kopiert wurden, werden auf der Quelldatenbank verbleiben. + + + + Error while creating view in target database: %1 + Fehler beim Erstellen eines Views in der Zieldatenbank %1 + + + + Error while creating index in target database: %1 + Fehler beim Erstellen eines Indizes in der Zieldatenbank %1 + + + + Error while creating trigger in target database: %1 + Fehler beim Erstellen eines Triggers in der Zieldatenbank %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Objekt '%1' konnte analysieren, um es zu verschieben oder kopieren zu können. + + + + DdlHistoryModel + + + Database name + ddl history header + Datenbankname + + + + Database file + ddl history header + Datenbankdatei + + + + Date of execution + ddl history header + Datum der Ausführung + + + + Changes + ddl history header + Änderungen + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Das Exportplugin %1 unterstützt das Exportieren der Abfrageergebnisse nicht. + + + + Export plugin %1 doesn't support exporing tables. + Das Exportplugin %1 unterstützt das Exportieren von Tabellen nicht. + + + + Export plugin %1 doesn't support exporing databases. + Das Exportplugin %1 unterstützt das Exportieren von Datenbanken nicht. + + + + Export format '%1' is not supported. Supported formats are: %2. + Das Exportformat %1 wird nicht unterstützt. Unterstützte Formate sind: %2. + + + + Export to the clipboard was successful. + Der Export in die Zwischenablage war erfolgreich. + + + + Export to the file '%1' was successful. + Der Export in die Datei %1 war erfolgreich. + + + + Export was successful. + Der Export war erfolgreich. + + + + Could not export to file %1. File cannot be open for writting. + Es kann nicht in die Datei %1 exportiert werden. Die Datei lässt sich nicht für Schreibzugriffe öffnen. + + + + ExportWorker + + + Error while exporting query results: %1 + Fehler beim Exportieren der Abfrageergebnisse: %1 + + + + Error while counting data column width to export from query results: %1 + Fehler beim Ermitteln der Spaltenbreite für den Export der Abfrageergebnisse: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + %1 konnte zum Exportieren nicht korrekt verarbeitet werden. Diese Daten werden nicht exportiert. + + + + Error while reading data to export from table %1: %2 + Fehler beim Lesen der zu exportierenden Daten aus der Tabelle %1: %2 + + + + Error while counting data to export from table %1: %2 + Fehler beim Ermitteln der zu exportierenden Daten aus der Tabelle %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Fehler beim Ermitteln der Spaltenbreite für den Export aus Tabelle %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Falsche Anzahl an Parametern für Funktion '%1'. Erwartet wurden %2, angegeben wurden jedoch %3. + + + + No such function registered in SQLiteStudio: %1(%2) + Diese Funktion ist in SQLiteStudio nicht verfügbar: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Die Funktion %1(%2) wurde für die Sprache %3 erstellt, jedoch ist das Plugin, welches diese Sprache unterstützt, derzeit nicht geladen. + + + + Invalid regular expression pattern: %1 + Ungültiges Muster für die regulären Ausdrücke: %1 + + + + + Could not open file %1 for reading: %2 + Datei %1 kann nicht für Lesezugriffe geöffnet werden: %2 + + + + Could not open file %1 for writting: %2 + Datei %1 kann nicht für Schreibzugriffe geöffnet werden: %2 + + + + Error while writting to file %1: %2 + Fehler beim Schreiben in Datei %1: %2 + + + + Unsupported scripting language: %1 + Nicht unterstützte Skriptsprache: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Der 'Export Textcodec' konnte nicht initialisiert werden. Es wird der Standardcodec genutzt: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + Dieses Importplugin stellt keine Spalten zur Verfügung. + + + + Could not start transaction in order to import a data: %1 + Es kann keine Transaktion zum Import der Daten gestartet werden: %1 + + + + Could not commit transaction for imported data: %1 + Die Transaktion für die importierten Daten kann nicht 'committet' werden: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Die Tabelle %1 hat weniger Spalten als die zu importierenden Daten liefern. Überschüssige Spalten werden daher ignoriert. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Die Tabelle %1 hat mehr Spalten als die zu importierenden Daten liefern. Einige Tabellenspalten werden deshalb leer bleiben. + + + + Could not create table to import to: %1 + Die Tabelle, in die importiert werden soll, kann nicht erstellt werden: %1 + + + + + + Error while importing data: %1 + Fehler beim Import der Daten: %1 + + + + + Interrupted. + import process status update + Abgebrochen. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Datenzeile %1 konnte nicht importiert werden. Die Zeile wurde ignoriert. Problembeschreibung: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Plugin %1 konnte nicht geladen werden, weil ein Konflikt besteht mit Plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Plugin %1 konnte nicht geladen werden, da dessen Abhängigkeiten nicht geladen worden sind: %2. + + + + Cannot load plugin %1. Error details: %2 + Plugin %1 konnte nicht geladen. Problembeschreibung: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Plugin %1 konnte wegen eines Fehlers bei der Initialisierung nicht geladen werden. + + + + min: %1 + plugin dependency version + Min: %1 + + + + max: %1 + plugin dependency version + Max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + konstanten + + + + PopulateConstantConfig + + + Constant value: + Konstanter Wert: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Wörterbuch + + + + PopulateDictionaryConfig + + + Dictionary file + Wörterbuchdatei + + + + Pick dictionary file + Wähle Wörterbuchdatei aus + + + + Word separator + Wortseperator + + + + Whitespace + Leerzeichen + + + + Line break + Zeilenumbruch + + + + Method of using words + Methode der Wortverwendung + + + + Ordered + Sortiert + + + + Randomly + Zufällig + + + + PopulateManager + + + Table '%1' populated successfully. + Tabelle %1 wurde erfolgreich gefüllt. + + + + PopulateRandom + + + Random number + Zufällige Nummer + + + + PopulateRandomConfig + + + Constant prefix + Konstanter Präfix + + + + No prefix + Kein Präfix + + + + Minimum value + Kleinster Wert + + + + Maximum value + Größter Wert + + + + Constant suffix + Konstanter Suffix + + + + No suffix + Kein Suffix + + + + PopulateRandomText + + + Random text + Zufälliger Text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Benutze Zeichen von den gebräuchlichsten Sets: + + + + Minimum length + Mindestlänge + + + + Letters from a to z. + Buchstaben von a bis z. + + + + Alpha + Alphanumerisch + + + + Numbers from 0 to 9. + Ziffern von 0 bis 9. + + + + Numeric + Numerisch + + + + A whitespace, a tab and a new line character. + Ein Leerzeichen, ein Tab und ein Zeilenumbruchzeichen. + + + + Whitespace + Leerzeichen + + + + Includes all above and all others. + Alle obigen und auch andere. + + + + Binary + Binär + + + + Use characters from my custom set: + Benutze Zeichen von meinem benutzerdefinierten Set: + + + + Maximum length + Maximallänge + + + + If you type some character multiple times, it's more likely to be used. + Wenn Sie einige Buchstaben mehrmals eingeben, dann ist es wahrscheinlicher, dass sie genutzt werden. + + + + PopulateScript + + + Script + Skript + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialisierungscode (optional) + + + + Per step code + Einzelschrittcode + + + + Language + Sprache + + + + Help + Hilfe + + + + PopulateSequence + + + Sequence + Sequenz + + + + PopulateSequenceConfig + + + Start value: + Startwert: + + + + Step: + Schritt: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Es kann keine Transaktion zum Füllen der Tabelle gestartet werden. Problembeschreibung: %1 + + + + Error while populating table: %1 + Fehler beim Füllen der Tabelle: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Die Transaktion zum Füllen der Tabelle kann nicht 'committet' werden. Problembeschreibung: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Die Datenbank %1 kann nicht geöffnet werden. + + + + Result set expired or no row available. + Das Abfrageergebniss ist ungültig oder es ist keine Datenzeile verfügbar. + + + + + Could not load extension %1: %2 + Konnte die Erweiterung %1 nicht laden: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Die Datenbank %1 kann nicht geschlossen werden. + + + + Could not attach database %1: %2 + Die Datenbank %1 kann nicht angefügt werden: %2 + + + + + Incomplete query. + Unvollständige Abfrage. + + + + Parser stack overflow + Stacküberlauf bei Verarbeitung + + + + Syntax error + Syntaxfehler + + + + Could not open dictionary file %1 for reading. + Die Wörterbuchdatei %1 kann für den Lesezugriff nicht geöffnet werden. + + + + Dictionary file must exist and be readable. + Die Wörterbuchdatei muss vorhanden sein und Leserechte besitzen. + + + + Maximum value cannot be less than minimum value. + Der Maximalwert kann nicht kleiner sein als der Minimalwert. + + + + Maximum length cannot be less than minimum length. + Die Maximallänge kann nicht kleiner sein als die Minimallänge. + + + + Custom character set cannot be empty. + Ein benutzerdefiniertes Zeichenset darf nicht leer sein. + + + + Could not find plugin to support scripting language: %1 + Das Plugin zur Unterstützung der folgenden Skriptsprache kann nicht gefunden werden: %1 + + + + Error while executing populating initial code: %1 + Fehler beim Ausführen des Initialisierungsprozesses: %1 + + + + Error while executing populating code: %1 + Fehler beim Ausführen des Auffüllprozesses: %1 + + + + Select implementation language. + Wählen Sie die Implementationssprache. + + + + Implementation code cannot be empty. + Der Implementationscode darf nicht leer sein. + + + + Could not resolve data source for column: %1 + Die Datenquelle für Spalte %1 kann nicht aufgelöst werden. + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + Allgemeine Verwendung + + + + Database support + plugin category name + Datenbankunterstützung + + + + Code formatter + plugin category name + Codeformatierer + + + + Scripting languages + plugin category name + Skriptsprachen + + + + Exporting + plugin category name + Exportieren + + + + Importing + plugin category name + Importieren + + + + Table populating + plugin category name + Tabellen füllen + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Tabelle %1 referenziert Tabelle %2, jedoch wird die 'foreign key'-Definition für die neue Tabellendefinition nicht aktualisiert, da es Probleme bei der DDL-Analyse von Tabelle %3 gibt. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + Alle Spalten, die von Index %1 indiziert wurden, sind verloren. Der Index wird nach der Tabellenmodifikation nicht neu erstellt. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + Es ist ein Problem bei der korrekten Verarbeitung des Triggers %1 aufgetreten. Er wird möglicherweise nicht vollständig aktualisert werden und sollte geprüft werden. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + Alle Spalten, die durch den Trigger %1 abgedeckt wurden, sind verloren. Der Trigger wird nach der Änderung nicht wiederhergestellt. + + + + Cannot not update trigger %1 according to table %2 modification. + Die Aktualisierung des Triggers %1, resultierend aus der Änderung der Tabelle %2, kann nicht ausgeführt werden. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Die Aktualisierung des Views %1, resultierend aus der Änderung der Tabelle %2, kann nicht ausgeführt werden. Der View wird daher nicht geändert. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + Es ist ein Problem beim Aktualisieren einer %1 Abfrage innerhalb eines %2 Triggers aufgetreten. Eine der %1 Unterabfragen, welche möglicherweise die Tabelle %3 referenziert, kann nicht geändert werden. Eine manuelle Anpassung des Triggers wird nötig sein. + + + + Could not parse DDL of the view to be created. Details: %1 + DDL des zu erzeugenden Views kann nicht verarbeitet werden. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Die zu verarbeitende Abfrage ist nicht CREATE VIEW, es ist: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio konnte die vom View zurückgegebenen Spalten nicht auflösen, daher kann nicht ermittelt werden, welcher Trigger beim Wiederherstelllungsprozess einen Fehler verusracht haben könnte. + + + + QueryExecutor + + + Execution interrupted. + Ausführung abgebrochen. + + + + Database is not open. + Die Datenbank ist nicht geöffnet. + + + + Only one query can be executed simultaneously. + Es kann nur eine Abfrage gleichzeitig ausgeführt werden. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + Beim Ausführen der count(*) Abfrage ist ein Fehler aufgetreten, daher wird das data paging abgeschaltet. Problemdetails der Datenbank: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio konnte keine Metadaten aus der Abfrage extrahieren. Die Ergebnismenge kann daher nicht editiert werden. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Fehler von %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Datenbank + + + + Execution date + sql history header + Ausführungsdatum + + + + Time spent + sql history header + Dauer + + + + Rows affected + sql history header + Anzahl Zeilen + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Konnte nicht auf Updates prüfen (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_el_GR.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_el_GR.ts new file mode 100644 index 0000000..5795b18 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_el_GR.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_en_US.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_en_US.ts new file mode 100644 index 0000000..fc66680 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_en_US.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es.qm b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es.qm and /dev/null differ diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es.ts deleted file mode 100644 index f0c170b..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es.ts +++ /dev/null @@ -1,1146 +0,0 @@ - - - - - AbstractDb - - - - Cannot execute query on closed database. - - - - - Error attaching database %1: %2 - - - - - ChainExecutor - - - The database for executing queries was not defined. - chain executor - - - - - The database for executing queries was not open. - chain executor - - - - - Could not disable foreign keys in the database. Details: %1 - chain executor - - - - - Could not start a database transaction. Details: %1 - chain executor - - - - - Interrupted - chain executor - - - - - Could not commit a database transaction. Details: %1 - chain executor - - - - - CompletionHelper - - - New row reference - - - - - Old row reference - - - - - New table name - - - - - New index name - - - - - New view name - - - - - New trigger name - - - - - Table or column alias - - - - - transaction name - - - - - New column name - - - - - Column data type - - - - - Constraint name - - - - - Error message - - - - - Collation name - - - - - Any word - - - - - Default database - - - - - Temporary objects database - - - - - ConfigImpl - - - Could not start database transaction for deleting SQL history, therefore it's not deleted. - - - - - Could not commit database transaction for deleting SQL history, therefore it's not deleted. - - - - - DbManagerImpl - - - Could not add database %1: %2 - - - - - Database %1 could not be updated, because of an error: %2 - - - - - - Database file doesn't exist. - - - - - - - No supporting plugin loaded. - - - - - Database could not be initialized. - - - - - No suitable database driver plugin found. - - - - - DbObjectOrganizer - - - - Error while creating table in target database: %1 - - - - - Could not parse table. - - - - - Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. - - - - - Error while copying data for table %1: %2 - - - - - - - Error while copying data to table %1: %2 - - - - - Error while dropping source view %1: %2 -Tables, indexes, triggers and views copied to database %3 will remain. - - - - - Error while creating view in target database: %1 - - - - - Error while creating index in target database: %1 - - - - - Error while creating trigger in target database: %1 - - - - - - - Could not parse object '%1' in order to move or copy it. - - - - - DbVersionConverter - - - Target file exists, but could not be overwritten. - - - - - Could not find proper database plugin to create target database. - - - - - Error while converting database: %1 - - - - - DdlHistoryModel - - - Database name - ddl history header - - - - - Database file - ddl history header - - - - - Date of execution - ddl history header - - - - - Changes - ddl history header - - - - - ExportManager - - - Export plugin %1 doesn't support exporing query results. - - - - - Export plugin %1 doesn't support exporing tables. - - - - - Export plugin %1 doesn't support exporing databases. - - - - - Export format '%1' is not supported. Supported formats are: %2. - - - - - Export to the clipboard was successful. - - - - - Export to the file '%1' was successful. - - - - - Export was successful. - - - - - Could not export to file %1. File cannot be open for writting. - - - - - ExportWorker - - - Error while exporting query results: %1 - - - - - Error while counting data column width to export from query results: %1 - - - - - - Could not parse %1 in order to export it. It will be excluded from the export output. - - - - - Error while reading data to export from table %1: %2 - - - - - Error while counting data to export from table %1: %2 - - - - - Error while counting data column width to export from table %1: %2 - - - - - FunctionManagerImpl - - - Invalid number of arguments to function '%1'. Expected %2, but got %3. - - - - - No such function registered in SQLiteStudio: %1(%2) - - - - - Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. - - - - - Invalid regular expression pattern: %1 - - - - - - Could not open file %1 for reading: %2 - - - - - Could not open file %1 for writting: %2 - - - - - Error while writting to file %1: %2 - - - - - Unsupported scripting language: %1 - - - - - GenericExportPlugin - - - Could not initialize text codec for exporting. Using default codec: %1 - - - - - ImportManager - - - Imported data to the table '%1' successfully. - - - - - ImportWorker - - - No columns provided by the import plugin. - - - - - Could not start transaction in order to import a data: %1 - - - - - Could not commit transaction for imported data: %1 - - - - - Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. - - - - - Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. - - - - - Could not create table to import to: %1 - - - - - - - Error while importing data: %1 - - - - - - Interrupted. - import process status update - - - - - Could not import data row number %1. The row was ignored. Problem details: %2 - - - - - PluginManagerImpl - - - Cannot load plugin %1, because it's in conflict with plugin %2. - - - - - Cannot load plugin %1, because its dependency was not loaded: %2. - - - - - Cannot load plugin %1. Error details: %2 - - - - - Cannot load plugin %1 (error while initializing plugin). - - - - - min: %1 - plugin dependency version - - - - - max: %1 - plugin dependency version - - - - - PopulateConstant - - - Constant - populate constant plugin name - - - - - PopulateConstantConfig - - - Constant value: - - - - - PopulateDictionary - - - Dictionary - dictionary populating plugin name - - - - - PopulateDictionaryConfig - - - Dictionary file - - - - - Pick dictionary file - - - - - Word separator - - - - - Whitespace - - - - - Line break - - - - - Method of using words - - - - - Ordered - - - - - Randomly - - - - - PopulateManager - - - Table '%1' populated successfully. - - - - - PopulateRandom - - - Random number - - - - - PopulateRandomConfig - - - Constant prefix - - - - - No prefix - - - - - Minimum value - - - - - Maximum value - - - - - Constant suffix - - - - - No suffix - - - - - PopulateRandomText - - - Random text - - - - - PopulateRandomTextConfig - - - Use characters from common sets: - - - - - Minimum length - - - - - Letters from a to z. - - - - - Alpha - - - - - Numbers from 0 to 9. - - - - - Numeric - - - - - A whitespace, a tab and a new line character. - - - - - Whitespace - - - - - Includes all above and all others. - - - - - Binary - - - - - Use characters from my custom set: - - - - - Maximum length - - - - - If you type some character multiple times, it's more likely to be used. - - - - - PopulateScript - - - Script - - - - - PopulateScriptConfig - - - Initialization code (optional) - - - - - Per step code - - - - - Language - - - - - Help - - - - - PopulateSequence - - - Sequence - - - - - PopulateSequenceConfig - - - Start value: - - - - - Step: - - - - - PopulateWorker - - - Could not start transaction in order to perform table populating. Error details: %1 - - - - - Error while populating table: %1 - - - - - Could not commit transaction after table populating. Error details: %1 - - - - - QObject - - - - Could not open database: %1 - - - - - - Result set expired or no row available. - - - - - - Could not load extension %1: %2 - - - - - Could not close database: %1 - - - - - - - - - - - SQLite %1 does not support '%2' statement. - - - - - SQLite %1 does not support '%2' statement, but the regular table can be created instead if you proceed. - - - - - Could not parse statement: %1 -Error details: %2 - - - - - - - - SQLite %1 does not support the '%2' clause. Cannot convert '%3' statement with that clause. - - - - - SQLite %1 does not support the '%2' clause in the '%3' statement. - - - - - SQLite %1 does not support current date or time clauses in expressions. - - - - - SQLite %1 does not support row value clauses in expressions. - - - - - - - SQLite %1 does not support '%2' clause in expressions. - - - - - Could not attach database %1: %2 - - - - - - Incomplete query. - - - - - - Parser stack overflow - - - - - - Syntax error - - - - - Could not open dictionary file %1 for reading. - - - - - Dictionary file must exist and be readable. - - - - - Maximum value cannot be less than minimum value. - - - - - Maximum length cannot be less than minimum length. - - - - - Custom character set cannot be empty. - - - - - Could not find plugin to support scripting language: %1 - - - - - Error while executing populating initial code: %1 - - - - - Error while executing populating code: %1 - - - - - Select implementation language. - - - - - Implementation code cannot be empty. - - - - - Could not resolve data source for column: %1 - - - - - Could not resolve table for column '%1'. - - - - - Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Tried to initialize the file at following localizations: %1. - - - - - General purpose - plugin category name - - - - - Database support - plugin category name - - - - - Code formatter - plugin category name - - - - - Scripting languages - plugin category name - - - - - Exporting - plugin category name - - - - - Importing - plugin category name - - - - - Table populating - plugin category name - - - - - Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. - - - - - All columns indexed by the index %1 are gone. The index will not be recreated after table modification. - - - - - There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. - - - - - Cannot not update trigger %1 according to table %2 modification. - - - - - - - There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - - - - - All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. - - - - - Cannot not update view %1 according to table %2 modifications. -The view will remain as it is. - - - - - Could not parse DDL of the view to be created. Details: %1 - - - - - Parsed query is not CREATE VIEW. It's: %1 - - - - - SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. - - - - - Could not open file '%1' for reading: %2 - - - - - QueryExecutor - - - Execution interrupted. - - - - - Database is not open. - - - - - Only one query can be executed simultaneously. - - - - - - An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 - - - - - SQLiteStudio was unable to extract metadata from the query. Results won't be editable. - - - - - ScriptingQtDbProxy - - - No database available in current context, while called QtScript's %1 command. - - - - - Error from %1: %2 - - - - - SqlHistoryModel - - - Database - sql history header - - - - - Execution date - sql history header - - - - - Time spent - sql history header - - - - - Rows affected - sql history header - - - - - SQL - sql history header - - - - - UpdateManager - - - Updates installer executable is missing. - - - - - - Unable to check for updates (%1) - - - - - details are unknown - - - - - Unable to run updater application (%1). Please report this. - - - - diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es_ES.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es_ES.ts new file mode 100644 index 0000000..588b8ea --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_es_ES.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + No se puede ejecutar la consulta en una base de datos desconectada. + + + + Error attaching database %1: %2 + Error al adjuntar la base de datos %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + No se definió una base de datos sobre la cual ejecutar las consultas. + + + + The database for executing queries was not open. + chain executor + La base de datos usada para ejecutar las consultas no estaba abierta. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + No se pudieron desactivar las claves foráneas en la base de datos. Detalles: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + No se pudo iniciar una transacción en la base de datos. Detalles: %1 + + + + Interrupted + chain executor + Interrumpido + + + + Could not commit a database transaction. Details: %1 + chain executor + No se pudo confirmar una transacción a la base de datos. Detalles: %1 + + + + CompletionHelper + + + New row reference + Referencia a nueva fila + + + + Old row reference + Referencia a antigua fila + + + + New table name + Nuevo nombre de tabla + + + + New index name + Nuevo nombre de índice + + + + New view name + Nuevo nombre de vista + + + + New trigger name + Nuevo nombre de disparador + + + + Table or column alias + Alias de tabla o columna + + + + transaction name + nombre de transacción + + + + New column name + Nuevo nombre de columna + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fa_IR.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fa_IR.ts new file mode 100644 index 0000000..47432f8 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fa_IR.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fi_FI.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fi_FI.ts new file mode 100644 index 0000000..7908672 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fi_FI.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr.qm b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr.qm deleted file mode 100644 index 552d0cf..0000000 Binary files a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr.qm and /dev/null differ diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr.ts deleted file mode 100644 index 15f6cf7..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr.ts +++ /dev/null @@ -1,1319 +0,0 @@ - - - - - AbstractDb - - - - Cannot execute query on closed database. - Impossible d’exécuter la requête sur une base de données fermée. - - - - Error attaching database %1: %2 - Erreur base de données attachée %1 : %2 - - - - BugReporter - - Invalid login or password - Identifiant ou mot de passe incorrect - - - - ChainExecutor - - - The database for executing queries was not defined. - chain executor - La base de données pour exécuter des requêtes n’était pas définie. - - - - The database for executing queries was not open. - chain executor - La base de données pour exécuter des requêtes n’est pas ouverte. - - - - Could not disable foreign keys in the database. Details: %1 - chain executor - - - - - Could not start a database transaction. Details: %1 - chain executor - Impossible d’initialiser une transaction de la base de données : %1 - - - - Interrupted - chain executor - Interruption - - - - Could not commit a database transaction. Details: %1 - chain executor - Impossible d’enregistrer la transaction de la base de données : %1 - - - - CompletionHelper - - - New row reference - Nouvelle référence de ligne - - - - Old row reference - Anncienne référence de ligne - - - - New table name - Nouveau nom de table - - - - New index name - Nouveau nom d’index - - - - New view name - Nouveau nom de vue - - - - New trigger name - Nouveau nom de déclencheur - - - - Table or column alias - Table ou alias colonne - - - - transaction name - Nom de transaction - - - - New column name - Nouveau nom de colonne - - - - Column data type - Type de données de la colonne - - - - Constraint name - Nom de la contrainte - - - - Error message - Message d’erreur - - - - Collation name - Nom de la collation - - - - Any word - N’importe quel mot - - - - Default database - Base de données par défaut - - - - Temporary objects database - Objets temporaires de base de données - - - - ConfigImpl - - - Could not start database transaction for deleting SQL history, therefore it's not deleted. - - - - - Could not commit database transaction for deleting SQL history, therefore it's not deleted. - - - - - DbManagerImpl - - - Could not add database %1: %2 - Impossible d’ajouter une base de données %1 : %2 - - - - Database %1 could not be updated, because of an error: %2 - La base de données %1 ne peut ëtre mise à jour à cause de l’erreur : %2 - - - - - Database file doesn't exist. - Le fichier de la base de données n’existe pas. - - - - - - No supporting plugin loaded. - Aucun plugin supporté chargé. - - - - Database could not be initialized. - La base de données ne peut être initialisée. - - - - No suitable database driver plugin found. - Aucun pilote de base de données approprié trouvé. - - - - DbObjectOrganizer - - - - Error while creating table in target database: %1 - Erreur lors de la creation de la table de la base de données : %1 - - - - Could not parse table. - Impossible d’analyser la table. - - - - Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. - La base de données %1 ne peut être attachée à la base de données %2, aussi les données de la table %3 seront copiées avec SQLiteStudio comme interpréteur. Cette méthode est lente pour des tables importantes, SVP soyer patient. - - - - Error while copying data for table %1: %2 - Erreur lors de la copie des données vers la table %1 : %2 - - - - - - Error while copying data to table %1: %2 - Erreur lors de la copie des données de la table %1 : %2 - - - - Error while dropping source view %1: %2 -Tables, indexes, triggers and views copied to database %3 will remain. - Erreur à la création de la vue %1 : %2 -Tables, index, déclencheurs et vues copiés de la base de données %3 seront maintenus. - - - - Error while creating view in target database: %1 - Erreur lors de la création de la vue de la base de données : %1 - - - - Error while creating index in target database: %1 - Erreur lors de la création de l’index de la base de données : %1 - - - - Error while creating trigger in target database: %1 - Erreur lors de la création du déclencheur de la base de données : %1 - - - - - - Could not parse object '%1' in order to move or copy it. - - - - - DbVersionConverter - - - Target file exists, but could not be overwritten. - Le fichier cible existe, mais ne peut être remplacé. - - - - Could not find proper database plugin to create target database. - Impossible de trouver le plugin correct pour créer la base de données cible. - - - - Error while converting database: %1 - Erreur lors de la conversion de la base de données : %1 - - - - DdlHistoryModel - - - Database name - ddl history header - Nom de la base - - - - Database file - ddl history header - Fichier de la base - - - - Date of execution - ddl history header - Date d’exécution - - - - Changes - ddl history header - Modifications - - - - ExportManager - - - Export plugin %1 doesn't support exporing query results. - Plugin d’export %1 ne supporte pas l’exportation de la requête. - - - - Export plugin %1 doesn't support exporing tables. - Plugin d’export %1 ne supporte pas l’exportation de la table. - - - - Export plugin %1 doesn't support exporing databases. - Plugin d’export %1 ne supporte pas l’exportation de la base de données. - - - - Export format '%1' is not supported. Supported formats are: %2. - Format d’export « %1 » n’est pas supporté. Les formats supportés sont : %2. - - - - Export to the clipboard was successful. - Export vers le presse-papier avec succés. - - - - Export to the file '%1' was successful. - Export vers le fichier « %1 » avec succès. - - - - Export was successful. - Export avec succès. - - - - Could not export to file %1. File cannot be open for writting. - Impossible d’exporter vers le fichier %1. Le fichier ne peut être ouvert en écriture. - - - - ExportWorker - - - Error while exporting query results: %1 - Erreur lors de l’exportation des résultats de la requête : %1 - - - - Error while counting data column width to export from query results: %1 - Erreur lors de la totalisation des données de colonne issu de la requête : %1 - - - - - Could not parse %1 in order to export it. It will be excluded from the export output. - Impossible d’analyser %1 afin de l’exporter. Celle-ci sera excluse de l’exportation. - - - - Error while reading data to export from table %1: %2 - Erreur lors de la lecture des données à exporter de la table %1 : %2 - - - - Error while counting data to export from table %1: %2 - Erreur lors du comptage des données à exporter de la table %1 : %2 - - - - Error while counting data column width to export from table %1: %2 - Erreur lors de la totalisation des données à exporter de la table %1 : %2 - - - - FunctionManagerImpl - - - Invalid number of arguments to function '%1'. Expected %2, but got %3. - Nombre de paramètres invalide de la fonction « %1 ». Attendu %2, obtenu %3. - - - - No such function registered in SQLiteStudio: %1(%2) - Fonction inconnue avec SQLiteStudio : %1(%2) - - - - Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. - La fonction %1(%2) est référencée avec le langage %3, mais le plugin supportant ce langage n’est actuellement pas chargé. - - - - Invalid regular expression pattern: %1 - Expression invalide : %1 - - - - - Could not open file %1 for reading: %2 - Impossible d’ouvrir en lecture le ficher %1 : %2 - - - - Could not open file %1 for writting: %2 - Impossible d’ouvrir en écriture le ficher %1 : %2 - - - - Error while writting to file %1: %2 - Erreur lors de l’écriture du fichier %1 : %2 - - - - Unsupported scripting language: %1 - Langage script non supporté : %1 - - - - GenericExportPlugin - - - Could not initialize text codec for exporting. Using default codec: %1 - Impossible d’initialiser le paramètre texte por l’export. Utulisation du paramètre par défaut : %1 - - - - ImportManager - - - Imported data to the table '%1' successfully. - Importation des données de la table « %1 » réussie. - - - - ImportWorker - - - No columns provided by the import plugin. - Aucune colonne fournie par le plugin d’importation. - - - - Could not start transaction in order to import a data: %1 - Impossible d’initialiser la transaction d’import de données : %1 - - - - Could not commit transaction for imported data: %1 - Impossible d’enregistrer la transaction d’import de données : %1 - - - - Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. - La table « %1 » a moins de colonnes que de données à importer. Les colonnes supplèmentaires seront ignorées. - - - - Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. - La table « %1 » a plus de colonnes que de colonnes de données à importer. Certaines colonnes de la table seront vides. - - - - Could not create table to import to: %1 - Impossible de créer la table d’import : %1 - - - - - - Error while importing data: %1 - Erreur lors de l’import des données : %1 - - - - - Interrupted. - import process status update - Transaction interrompue. - - - - Could not import data row number %1. The row was ignored. Problem details: %2 - - - - - PluginManagerImpl - - - Cannot load plugin %1, because it's in conflict with plugin %2. - Chargement impossible du plugin %1 celui-ci est en conflit avec le plugin %2. - - - - Cannot load plugin %1, because its dependency was not loaded: %2. - Chargement impossible du plugin %1, les dépendances n’ont pa été chargées : %2. - - - - Cannot load plugin %1. Error details: %2 - Chargement impossible du plugin %1, Détails de l’erreur : %2 - - - - Cannot load plugin %1 (error while initializing plugin). - Chargement impossible du plugin %1 (erreur à initialisation du plugin). - - - - min: %1 - plugin dependency version - min : %1 - - - - max: %1 - plugin dependency version - max : %1 - - - - PopulateConstant - - - Constant - populate constant plugin name - Constante - - - - PopulateConstantConfig - - - Constant value: - Valeur constante : - - - - PopulateDictionary - - - Dictionary - dictionary populating plugin name - Dictionnaire - - - - PopulateDictionaryConfig - - - Dictionary file - Fichier du dictionnaire - - - - Pick dictionary file - Extrait du dictionnaire - - - - Word separator - Mot séparateur - - - - Whitespace - Espace - - - - Line break - Fin de ligne - - - - Method of using words - Méthode d’utilisation des mots - - - - Ordered - Trié - - - - Randomly - Aléatoire - - - - PopulateManager - - - Table '%1' populated successfully. - Table « %1 » remplie avec succès. - - - - PopulateRandom - - - Random number - Nombre aléatoire - - - - PopulateRandomConfig - - - Constant prefix - Préfixe constant - - - - No prefix - Aucun préfixe - - - - Minimum value - Valeur minimale - - - - Maximum value - Valeur maximale - - - - Constant suffix - Suffixe constant - - - - No suffix - Aucun suffixe - - - - PopulateRandomText - - - Random text - Text aléatoire - - - - PopulateRandomTextConfig - - - Use characters from common sets: - Utilisez des caractères de la table standard : - - - - Minimum length - Longueur minimun - - - - Letters from a to z. - Caractères de a à z. - - - - Alpha - Caractère alpha - - - - Numbers from 0 to 9. - Carctères numériques de 0 à 9. - - - - Numeric - Numérique - - - - A whitespace, a tab and a new line character. - Espace, tabulation et retour chariot. - - - - Whitespace - Espace - - - - Includes all above and all others. - Inclus ci-dessus et tous les autres. - - - - Binary - Binaire - - - - Use characters from my custom set: - Utilisez les catactères de ma table personalisée : - - - - Maximum length - Longueur maximum - - - - If you type some character multiple times, it's more likely to be used. - Si vous saisissez plusieurs fois le même caractère, la sécurité est faible. - - - - PopulateScript - - - Script - Script - - - - PopulateScriptConfig - - - Initialization code (optional) - Code d’initialisation (optionel) - - - - Per step code - Code par étape - - - - Language - Langage - - - - Help - Aide - - - - PopulateSequence - - - Sequence - Séquence - - - - PopulateSequenceConfig - - - Start value: - Valeur de départ : - - - - Step: - Incrément : - - - - PopulateWorker - - - Could not start transaction in order to perform table populating. Error details: %1 - Impossible d’initialiser la transaction pour remplir la table.Détails de l’erreur %1 - - - - Error while populating table: %1 - Erreur lors du remplissage de la table : %1 - - - - Could not commit transaction after table populating. Error details: %1 - Impossible d’enregistrer la transaction après le remplissage de la table. Erreur %1 - - - - QObject - - - - Could not open database: %1 - Impossible d’ouvrir la base de données : %1 - - - - - Result set expired or no row available. - Terminé ou aucune ligne valide. - - - - - Could not load extension %1: %2 - - - - - Could not close database: %1 - Impossible de clore la base de bonnées : %1 - - - - - - - - - - SQLite %1 does not support '%2' statement. - SQLite %1 ne supporte pas l’instruction « %2 ». - - - - SQLite %1 does not support '%2' statement, but the regular table can be created instead if you proceed. - SQLite %1 ne supporte pas l’instruction « %2 », mais la table normale peut être créée à la place si vous confirmez. - - - - Could not parse statement: %1 -Error details: %2 - Impossible d’analyser l’instruction : %1 -Détails erreur: %2 - - - - - - - SQLite %1 does not support the '%2' clause. Cannot convert '%3' statement with that clause. - SQLite %1 ne supporte pas la clause « %2 ». Impossible de convertir l’instruction « %3 » avec cette clause. - - - - SQLite %1 does not support the '%2' clause in the '%3' statement. - SQLite %1 ne supporte pas la clause « %2 » de l’instruction « %3 ». - - - - SQLite %1 does not support current date or time clauses in expressions. - SQLite %1 ne supporte pas la clause date ou l’heure actuelle dans l’expression. - - - - SQLite %1 does not support row value clauses in expressions. - - - - - - - SQLite %1 does not support '%2' clause in expressions. - SQLite %1 ne supporte pas la clause « %2 » dans l’expression. - - - - Could not attach database %1: %2 - Impossible d’attacher la base de données %1 : %2 - - - - - Incomplete query. - Requête imcomplète. - - - - - Parser stack overflow - Analyse dépassement pile - - - - - Syntax error - Erreur de syntaxe - - - - Could not open dictionary file %1 for reading. - Impossible d’ouvrir en lecture le fichier dictionnaire %1. - - - - Dictionary file must exist and be readable. - Le fichier dictionnaire doit exister et être en lecture. - - - - Maximum value cannot be less than minimum value. - La valeur maximum ne peut être inférieure à la valeur minimum. - - - - Maximum length cannot be less than minimum length. - Longueur maximum ne peut être inférieure à la longueur minimum. - - - - Custom character set cannot be empty. - La table des caractères personalisés ne peut être vide. - - - - Could not find plugin to support scripting language: %1 - Impossible de trouver le plugin supportant le script : %1 - - - - Error while executing populating initial code: %1 - Erreur à l’initialisation du code de remplissage : %1 - - - - Error while executing populating code: %1 - Erreur à l’exécution du code de remplissage : %1 - - - - Select implementation language. - Sélectionnez l’application langage. - - - - Implementation code cannot be empty. - L’application de code ne peut être vide. - - - - Could not resolve data source for column: %1 - Impossible de résoudre la source de données pour la colonnes : %1 - - - - Could not resolve table for column '%1'. - - - - - Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Tried to initialize the file at following localizations: %1. - Impossible d’initialiser le fichier de configuration. - Aucune modification et les requêtes seront perdues après redémarrage. - Essayez d’initialiser le fichier avec cette localisation : %1. - - - - General purpose - plugin category name - Objectif général - - - - Database support - plugin category name - Support base de données - - - - Code formatter - plugin category name - Format code - - - - Scripting languages - plugin category name - Langages script - - - - Exporting - plugin category name - Export - - - - Importing - plugin category name - Import - - - - Table populating - plugin category name - Peuplement de la table - - - - Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. - La table %1 référence la table %2, mais la clef étrangère ne pourra être mise à jour pour la nouvelle table à cause de problèmes lors de l’analyse DDL de la table %3. - - - - All columns indexed by the index %1 are gone. The index will not be recreated after table modification. - Toutes les colonnes indéxées par l’index %1 sont traitées. L’index ne sera pas recréé après la modification de la table. - - - - There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. - - - - - Cannot not update trigger %1 according to table %2 modification. - Impossible de mettre à jour le déclencheur %1 selon la modification de la table %2. - - - - - - There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - - - - - All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. - Toutes les colonnes couvertes par le déclencheur %1 sont faites. Le déclencheur ne sera pas recréé après la modification de la table. - - - - Cannot not update view %1 according to table %2 modifications. -The view will remain as it is. - Impossible de mettre à jour les modifications de la vue %1 issue de la table %2 -La vue restera telque. - - - There is a problem with updating an %1 statement within %2 trigger. One of the SELECT substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - Il y a un problème à la mise à jour l’instruction %1 avec le déclencheur %2. Une partie de l’instruction SELECT référençant la table %3 ne ppermet pas sa modification. La mise à jour manuelle du déclencheur est nécessaire. - - - - Could not parse DDL of the view to be created. Details: %1 - Impossible d’analyser le DDL de création de vue. Détails : %1 - - - - Parsed query is not CREATE VIEW. It's: %1 - L’expression CREATE VIEW est fausse. C’est : %1 - - - - SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. - SQLiteStudio ne peut résoudre les colonnes résultant de la nouvelle vue, d’où le déclencheur en cause ne pourra être indiqué pendant le process. - - - - Could not open file '%1' for reading: %2 - - - - - QueryExecutor - - - Execution interrupted. - Exécution interrompue. - - - - Database is not open. - La base de données n’est ouverte. - - - - Only one query can be executed simultaneously. - Une seule requête peut être exécutée à la fois. - - - - - An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 - Une erreur s’est produite à l’exécution de la requête count(*), la recherche des données est arrêtée. Erreur de la base de données : %1 - - - - SQLiteStudio was unable to extract metadata from the query. Results won't be editable. - SQLiteStudio ne peut extraire des métadonnées d’une requête. Les résultats ne peut être affichés. - - - - ScriptingQtDbProxy - - - No database available in current context, while called QtScript's %1 command. - Aucune base de données validedans le context actuel, plutôtutiliser la commande QtScript %1. - - - - Error from %1: %2 - Erreur de %1 : %2 - - - - SqlHistoryModel - - - Database - sql history header - Base de données - - - - Execution date - sql history header - Date d’exécution - - - - Time spent - sql history header - Temps passé - - - - Rows affected - sql history header - Lignes affectées - - - - SQL - sql history header - SQL - - - - UpdateManager - - An error occurred while checking for updates: %1. - Une erreur est apparue lors du contrôle pour la mise à jour : %1. - - - - Could not check available updates, because server responded with invalid message format. It is safe to ignore this warning. - Impossible de vérifier la mise à jour, car le serveur a répondu avec un message invalide. Il est possible d’ignorer le warning. - - - An error occurred while reading updates metadata: %1. - Erreur lors de la lecture de mise des méta données : %1. - - - Could not download updates, because server responded with invalid message format. You can try again later or download and install updates manually. See <a href="%1">User Manual</a> for details. - Impossibles de télécharger les mises à jour, car le serveur répond avec un format de message invalide. Vous pover essayer plus tard ou télécharger et mettre à jour manuellement. Voir <a href="%1">User Manual</a> for details. - - - Could not create temporary directory for downloading the update. Updating aborted. - Impossible de créer un répertoire temporaire pour télécharger la mise à jour. Mise à jour abandonnée. - - - There was no updates to download. Updating aborted. - Il n’y a aucune mise à jour à télécharger. Mise à jour abandonnée. - - - Downloading: %1 - Téléchargement : %1 - - - Could not determinate file name from update URL: %1. Updating aborted. - Impossible de déterminer le fichier de mise à jour URL : %1.Mise à jour abandonnée. - - - Failed to open file '%1' for writting: %2. Updating aborted. - Erreur à l’ouverture du fichier « %1 » pour l’écriture : %2. Mise à jour abandonnée. - - - Installing updates. - Installation des mises jour. - - - Could not copy current application directory into %1 directory. - Impossible de copier le répertoire de l’application courante dans %1. - - - Could not create directory %1. - Impossible de créer le répertoire : %1. - - - Could not rename directory %1 to %2. -Details: %3 - Impossible de renommer le répertoire %1 en %2. Détails : %3 - - - Cannot not rename directory %1 to %2. -Details: %3 - Impossible de renommer le répertoire %1 en %2.Détails : %3 - - - Could not move directory %1 to %2 and also failed to restore original directory, so the original SQLiteStudio directory is now located at: %3 - Impossible de déplacer le répertoire %1 vers %2 d’où l’impossibilité de restaurer le répertoire original. SQLiteStudio est maintenant localisé : %3 - - - Could not rename directory %1 to %2. Rolled back to the original SQLiteStudio version. - Impossible de renommer le répertoire %1 en %2, retour vers la version originale SQLiteStudio. - - - Could not unpack component %1 into %2 directory. - Impossible d’extraire le composant %1 dans le répertoire %2. - - - Could not find permissions elevator application to run update as a root. Looked for: %1 - Impossible d’élever les autorisations pour lancer la mise à jour en tantque root. Bloqué : %1 - - - Could not execute final updating steps as root: %1 - Impossible de finaliser la mis à jour en tant que root : %1 - - - Could not execute final updating steps as admin: %1 - Impossible de finaliser la mis à jour en tant que admin : %1 - - - Cannot create temporary directory for updater. - Impossible de créer un répertoire temporaire pour la mise à jour. - - - Cannot create updater script file. - impossible de créer le fichier du scripte de mise à jour. - - - Updating canceled. - Mise à jour suspendue. - - - Could not execute final updating steps as administrator. - Impossible de finaliser la mis à jour en tant qu’administrateur. - - - Could not execute final updating steps as administrator. Updater startup timed out. - Impossible de finaliser la mis à jour en tant qu’administrateur. Délai d’attente de lancement dépassé. - - - Could not execute final updating steps as administrator. Updater operation timed out. - Impossible de finaliser la mis à jour en tant qu’administrateur. Délai d’attente d’opération dépassé. - - - Could not clean up temporary directory %1. You can delete it manually at any time. - Impossible de nettoyer le répertoire temporaire %1. Vous pouver le supprimer manuellement plutard. - - - Could not run new version for continuing update. - Impossible de lancer la nouvelle version afin de continuer la mise à jour. - - - Package not in tar.gz format, cannot install: %1 - Installation impossible un paquet n’est pas au format tar.zg : %1 - - - Package %1 cannot be installed, because cannot move it to directory: %2 - Le paquet %1 ne peut être installé, celui-ci ne pouvant déplacé dans le répertoire : %2 - - - Package %1 cannot be installed, because cannot unpack it: %2 - Le paquet %1 ne peut être installé, celui-ci ne pouvant décompressé : %2 - - - Package not in zip format, cannot install: %1 - Installation impossible, un paquet est manquant : %1 - - - Package %1 cannot be installed, because cannot unzip it to directory %2: %3 - Le paquet %1 ne peut être installé, celui-ci ne pouvant décompressé dans le répertoire %2 : %3 - - - Package %1 cannot be installed, because cannot unzip it to directory: %2 - Le paquet %1 ne peut être installé, celui-ci ne pouvant décompressé dans le répertoire : %2 - - - Could not rename directory %1 to %2. - Impossible de renommer le répertoire %1 en %2. - - - Could not delete directory %1. - Impossible de supprimer le répertoire %1. - - - Error executing update command: %1 -Error message: %2 - Erreur d’exécution de la commande de mise à jour : %1 -Message d’erreur : %2 - - - An error occurred while downloading updates: %1. Updating aborted. - Erreur lors du téléchargement de la mise à jour : %1. Mise à jour abandonnée. - - - - Updates installer executable is missing. - - - - - - Unable to check for updates (%1) - - - - - details are unknown - - - - - Unable to run updater application (%1). Please report this. - - - - diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr_FR.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr_FR.ts new file mode 100644 index 0000000..da17636 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_fr_FR.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Impossible d’exécuter la requête sur une base de données fermée. + + + + Error attaching database %1: %2 + Erreur base de données attachée %1 : %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Impossible de faire le point de vérification complet des journaux de transactions sur la base de données '%1'. Erreur retournée par le moteur SQLite : %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + La base de données pour exécuter des requêtes n’était pas définie. + + + + The database for executing queries was not open. + chain executor + La base de données pour exécuter des requêtes n’est pas ouverte. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Impossible de désactiver les clés étrangères dans la base de données. Détails : %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Impossible d’initialiser une transaction de la base de données : %1 + + + + Interrupted + chain executor + Interruption + + + + Could not commit a database transaction. Details: %1 + chain executor + Impossible d’enregistrer la transaction de la base de données : %1 + + + + CompletionHelper + + + New row reference + Nouvelle référence de ligne + + + + Old row reference + Anncienne référence de ligne + + + + New table name + Nouveau nom de tableau + + + + New index name + Nouveau nom d’index + + + + New view name + Nouveau nom de vue + + + + New trigger name + Nouveau nom de déclencheur + + + + Table or column alias + Alias de tableau ou de colonne + + + + transaction name + Nom de transaction + + + + New column name + Nouveau nom de colonne + + + + Column data type + Type de données de la colonne + + + + Constraint name + Nom de la contrainte + + + + Error message + Message d’erreur + + + + Any word + N’importe quel mot + + + + Default database + Base de données par défaut + + + + Temporary objects database + Objets temporaires de base de données + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Impossible de démarrer la transaction de la base de données pour la suppression de l'historique SQL, donc il n'a pas été supprimé. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Impossible de valider la transaction de la base de données pour la suppression de l'historique SQL, donc il n'a pas été supprimé. + + + + DbManagerImpl + + + Could not add database %1: %2 + Impossible d’ajouter une base de données %1 : %2 + + + + Database %1 could not be updated, because of an error: %2 + La base de données %1 ne peut ëtre mise à jour à cause de l’erreur : %2 + + + + + Database file doesn't exist. + Le fichier de la base de données n'existe pas. + + + + + + No supporting plugin loaded. + Aucun plugin supporté chargé. + + + + Database could not be initialized. + La base de données ne peut être initialisée. + + + + No suitable database driver plugin found. + Aucun pilote de base de données approprié trouvé. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Erreur lors de la création du tableau dans la base de données cible : %1 + + + + Could not parse table. + Impossible d'analyser le tableau. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + La base de données %1 n'a pas pu être attachée à la base de données %2, donc les données de la table %3 seront copiées avec SQLiteStudio en tant que médiateur. Cette méthode peut être lente pour les grands tableaux, donc soyez patient s'il vous plaît. + + + + Error while copying data for table %1: %2 + Erreur lors de la copie des données pour le tableau %1: %2 + + + + + + Error while copying data to table %1: %2 + Erreur lors de la copie des données vers le tableau %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Erreur lors de la suppression de la vue source %1: %2 +Tableaux, indexes, déclencheurs et vues copiés dans la base de données %3 resteront. + + + + Error while creating view in target database: %1 + Erreur lors de la création de la vue de la base de données : %1 + + + + Error while creating index in target database: %1 + Erreur lors de la création de l’index de la base de données : %1 + + + + Error while creating trigger in target database: %1 + Erreur lors de la création du déclencheur de la base de données : %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Impossible d'analyser l'objet '%1' pour le déplacer ou le copier. + + + + DdlHistoryModel + + + Database name + ddl history header + Nom de la base + + + + Database file + ddl history header + Fichier de la base + + + + Date of execution + ddl history header + Date d’exécution + + + + Changes + ddl history header + Modifications + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Plugin d’export %1 ne supporte pas l’exportation de la requête. + + + + Export plugin %1 doesn't support exporing tables. + Exporter le plugin %1 ne prend pas en charge l'exportation des tableaux. + + + + Export plugin %1 doesn't support exporing databases. + Plugin d’export %1 ne supporte pas l’exportation de la base de données. + + + + Export format '%1' is not supported. Supported formats are: %2. + Format d’export « %1 » n’est pas supporté. Les formats supportés sont : %2. + + + + Export to the clipboard was successful. + Export vers le presse-papier avec succés. + + + + Export to the file '%1' was successful. + Export vers le fichier « %1 » avec succès. + + + + Export was successful. + Export avec succès. + + + + Could not export to file %1. File cannot be open for writting. + Impossible d’exporter vers le fichier %1. Le fichier ne peut être ouvert en écriture. + + + + ExportWorker + + + Error while exporting query results: %1 + Erreur lors de l’exportation des résultats de la requête : %1 + + + + Error while counting data column width to export from query results: %1 + Erreur lors de la totalisation des données de colonne issu de la requête : %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Impossible d’analyser %1 afin de l’exporter. Celle-ci sera excluse de l’exportation. + + + + Error while reading data to export from table %1: %2 + Erreur lors de la lecture des données à exporter depuis le tableau %1 : %2 + + + + Error while counting data to export from table %1: %2 + Erreur lors du comptage des données à exporter à partir du tableau %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Erreur lors du comptage de la largeur de la colonne de données à exporter à partir du tableau %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Nombre de paramètres invalide de la fonction « %1 ». Attendu %2, obtenu %3. + + + + No such function registered in SQLiteStudio: %1(%2) + Fonction inconnue avec SQLiteStudio : %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + La fonction %1(%2) est référencée avec le langage %3, mais le plugin supportant ce langage n’est actuellement pas chargé. + + + + Invalid regular expression pattern: %1 + Expression invalide : %1 + + + + + Could not open file %1 for reading: %2 + Impossible d’ouvrir en lecture le ficher %1 : %2 + + + + Could not open file %1 for writting: %2 + Impossible d’ouvrir en écriture le ficher %1 : %2 + + + + Error while writting to file %1: %2 + Erreur lors de l’écriture du fichier %1 : %2 + + + + Unsupported scripting language: %1 + Langage script non supporté : %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Impossible d’initialiser le paramètre texte por l’export. Utulisation du paramètre par défaut : %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Données importées dans le tableau '%1' avec succès. Nombre de lignes importées : %2 + + + + ImportWorker + + + No columns provided by the import plugin. + Aucune colonne fournie par le plugin d’importation. + + + + Could not start transaction in order to import a data: %1 + Impossible d’initialiser la transaction d’import de données : %1 + + + + Could not commit transaction for imported data: %1 + Impossible d’enregistrer la transaction d’import de données : %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Le tableau '%1' a moins de colonnes qu'il y en a dans les données à importer. Les colonnes de données excessives seront ignorées. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Le tableau '%1' a plus de colonnes qu'il y en a dans les données à importer. Certaines colonnes de la table seront laissées vides. + + + + Could not create table to import to: %1 + Impossible de créer un tableau où importer : %1 + + + + + + Error while importing data: %1 + Erreur lors de l’import des données : %1 + + + + + Interrupted. + import process status update + Transaction interrompue. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Impossible d'importer les données de la ligne numéro %1. La ligne a été ignorée. Détails du problème : %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Chargement impossible du plugin %1 celui-ci est en conflit avec le plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Chargement impossible du plugin %1, les dépendances n’ont pa été chargées : %2. + + + + Cannot load plugin %1. Error details: %2 + Chargement impossible du plugin %1, Détails de l’erreur : %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Chargement impossible du plugin %1 (erreur à initialisation du plugin). + + + + min: %1 + plugin dependency version + min : %1 + + + + max: %1 + plugin dependency version + max : %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constante + + + + PopulateConstantConfig + + + Constant value: + Valeur constante : + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionnaire + + + + PopulateDictionaryConfig + + + Dictionary file + Fichier du dictionnaire + + + + Pick dictionary file + Extrait du dictionnaire + + + + Word separator + Mot séparateur + + + + Whitespace + Espace + + + + Line break + Fin de ligne + + + + Method of using words + Méthode d’utilisation des mots + + + + Ordered + Trié + + + + Randomly + Aléatoire + + + + PopulateManager + + + Table '%1' populated successfully. + Table « %1 » remplie avec succès. + + + + PopulateRandom + + + Random number + Nombre aléatoire + + + + PopulateRandomConfig + + + Constant prefix + Préfixe constant + + + + No prefix + Aucun préfixe + + + + Minimum value + Valeur minimale + + + + Maximum value + Valeur maximale + + + + Constant suffix + Suffixe constant + + + + No suffix + Aucun suffixe + + + + PopulateRandomText + + + Random text + Text aléatoire + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Utilisez des caractères de la table standard : + + + + Minimum length + Longueur minimun + + + + Letters from a to z. + Caractères de a à z. + + + + Alpha + Caractère alpha + + + + Numbers from 0 to 9. + Carctères numériques de 0 à 9. + + + + Numeric + Numérique + + + + A whitespace, a tab and a new line character. + Espace, tabulation et retour chariot. + + + + Whitespace + Espace + + + + Includes all above and all others. + Inclus ci-dessus et tous les autres. + + + + Binary + Binaire + + + + Use characters from my custom set: + Utilisez les catactères de ma table personalisée : + + + + Maximum length + Longueur maximum + + + + If you type some character multiple times, it's more likely to be used. + Si vous saisissez plusieurs fois le même caractère, la sécurité est faible. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Code d’initialisation (optionel) + + + + Per step code + Code par étape + + + + Language + Langage + + + + Help + Aide + + + + PopulateSequence + + + Sequence + Séquence + + + + PopulateSequenceConfig + + + Start value: + Valeur de départ : + + + + Step: + Incrément : + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Impossible d’initialiser la transaction pour remplir la table.Détails de l’erreur %1 + + + + Error while populating table: %1 + Erreur lors du remplissage de la table : %1 + + + + Could not commit transaction after table populating. Error details: %1 + Impossible d’enregistrer la transaction après le remplissage de la table. Erreur %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Impossible d'ouvrir le fichier '%1' pour la lecture : %2 + + + + Could not open database: %1 + Impossible d’ouvrir la base de données : %1 + + + + Result set expired or no row available. + Terminé ou aucune ligne valide. + + + + + Could not load extension %1: %2 + Impossible de charger l'extension %1 : %2 + + + + Could not run WAL checkpoint: %1 + Impossible d’exécuter le point de contrôle WAL : %1 + + + + Could not close database: %1 + Impossible de clore la base de bonnées : %1 + + + + Could not attach database %1: %2 + Impossible d’attacher la base de données %1 : %2 + + + + + Incomplete query. + Requête imcomplète. + + + + Parser stack overflow + Analyse dépassement pile + + + + Syntax error + Erreur de syntaxe + + + + Could not open dictionary file %1 for reading. + Impossible d’ouvrir en lecture le fichier dictionnaire %1. + + + + Dictionary file must exist and be readable. + Le fichier dictionnaire doit exister et être en lecture. + + + + Maximum value cannot be less than minimum value. + La valeur maximum ne peut être inférieure à la valeur minimum. + + + + Maximum length cannot be less than minimum length. + Longueur maximum ne peut être inférieure à la longueur minimum. + + + + Custom character set cannot be empty. + La table des caractères personalisés ne peut être vide. + + + + Could not find plugin to support scripting language: %1 + Impossible de trouver le plugin supportant le script : %1 + + + + Error while executing populating initial code: %1 + Erreur à l’initialisation du code de remplissage : %1 + + + + Error while executing populating code: %1 + Erreur à l’exécution du code de remplissage : %1 + + + + Select implementation language. + Sélectionnez l’application langage. + + + + Implementation code cannot be empty. + L’application de code ne peut être vide. + + + + Could not resolve data source for column: %1 + Impossible de résoudre la source de données pour la colonnes : %1 + + + + Could not resolve table for column '%1'. + Impossible de résoudre le tableau pour la colonne '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + Objectif général + + + + Database support + plugin category name + Support base de données + + + + Code formatter + plugin category name + Format code + + + + Scripting languages + plugin category name + Langages script + + + + Exporting + plugin category name + Export + + + + Importing + plugin category name + Import + + + + Table populating + plugin category name + Peuplement de la table + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Le tableau %1 fait référence au tableau %2, mais la définition de la clé étrangère ne sera pas mise à jour pour une nouvelle définition d'un tableau en raison de problèmes lors de l'analyse de DDL du tableau %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + Toutes les colonnes indexées par l'index %1 ont disparu. L'index ne sera pas recréé après modification du tableau. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + Il y a un problème avec le déclencheur de traitement %1. Il ne sera peut-être pas complètement mis à jour plus tard et nécessitera votre attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + Toutes les colonnes couvertes par le déclencheur %1 ont disparu. Le déclencheur ne sera pas recréé après la modification du tableau. + + + + Cannot not update trigger %1 according to table %2 modification. + Impossible de mettre à jour le déclencheur %1 selon la modification du tableau %2. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Impossible de mettre à jour la vue %1 selon les modifications de la table %2. +La vue restera telle quelle. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Impossible d’analyser le DDL de création de vue. Détails : %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + L’expression CREATE VIEW est fausse. C’est : %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio ne peut résoudre les colonnes résultant de la nouvelle vue, d’où le déclencheur en cause ne pourra être indiqué pendant le process. + + + + QueryExecutor + + + Execution interrupted. + Exécution interrompue. + + + + Database is not open. + La base de données n’est ouverte. + + + + Only one query can be executed simultaneously. + Une seule requête peut être exécutée à la fois. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + Une erreur s’est produite à l’exécution de la requête count(*), la recherche des données est arrêtée. Erreur de la base de données : %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio ne peut extraire des métadonnées d’une requête. Les résultats ne peut être affichés. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + Aucune base de données n'est disponible dans le contexte actuel, durant l'utilisation de la commande JavaScript %1. + + + + Error from %1: %2 + Erreur de %1 : %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Impossible d'exécuter SQL, car l'application n'a pas pu démarrer la transaction : %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Exécution depuis le fichier annulé. Toutes les requêtes exécutées jusqu'à présent ont été restaurées. + + + + Could not open file '%1' for reading: %2 + Impossible d'ouvrir le fichier '%1' pour la lecture : %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Impossible d'exécuter SQL, car l'application n'a pas pu valider la transaction : %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Fin de l'exécution de %1 requêtes en %2 secondes. %3 n'ont pas été exécutées en raison d'erreurs. + + + + Finished executing %1 queries in %2 seconds. + Fin de l'exécution de %1 requêtes en %2 secondes. + + + + Could not execute SQL due to error. + Impossible d'exécuter SQL en raison d'une erreur. + + + + SqlHistoryModel + + + Database + sql history header + Base de données + + + + Execution date + sql history header + Date d’exécution + + + + Time spent + sql history header + Temps passé + + + + Rows affected + sql history header + Lignes affectées + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Impossible de vérifier les mises à jour (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_he_IL.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_he_IL.ts new file mode 100644 index 0000000..5538f60 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_he_IL.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + ×œ× × ×™×ª×Ÿ ל×תחל תנועת מסד נתוני×. פרטי×:%1 + + + + Interrupted + chain executor + פסיקה + + + + Could not commit a database transaction. Details: %1 + chain executor + ×œ× × ×™×ª×Ÿ ×”×™×” לקבע תנועת מסד נתוני×. פרטי×:%1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + ×©× ×˜×‘×œ×” חדשה + + + + New index name + ×©× ×ž×¤×ª×— חדש + + + + New view name + ×©× ×ž×¦×’ חדש + + + + New trigger name + ×©× ×ž×–× ×§ חדש + + + + Table or column alias + כינוי טבלה ×ו עמודות + + + + transaction name + ×©× ×ª× ×•×¢×” + + + + New column name + ×©× ×¢×ž×•×“×” חדשה + + + + Column data type + סוג עמודת מידע + + + + Constraint name + ×©× ×ילוץ + + + + Error message + הודעת שגי××” + + + + Any word + מילה כלשהי + + + + Default database + מסד × ×ª×•× ×™× ×‘×¨×™×¨×ª מחדל + + + + Temporary objects database + מסד נתוני ×¢×¦×ž×™× ×–×ž× ×™×™× + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + ×œ× × ×™×ª×Ÿ ל×תחל תנועת מסד × ×ª×•× ×™× ×œ×ž×—×™×§×ª היסטוריית SQL, לכן ×”×™× ×œ× × ×ž×—×§×”. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + ×œ× × ×™×ª×Ÿ ×”×™×” לקבע תנועת מסד × ×ª×•× ×™× ×œ×ž×—×™×§×ª היסטוריית SQL, לכן ×”×™× ×œ× × ×ž×—×§×”. + + + + DbManagerImpl + + + Could not add database %1: %2 + ×œ× × ×™×ª×Ÿ להוסיף מסד נתוני×%1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + ×œ× × ×™×ª×Ÿ ×”×™×” ל×תחל ×ת מסד הנתוני×.â€. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + ×œ× × ×™×ª×Ÿ לפרוס טבלה. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + ×©× ×ž×¡×“ ×”× ×ª×•× ×™× + + + + Database file + ddl history header + קובץ מסד × ×ª×•× ×™× + + + + Date of execution + ddl history header + ת×ריך ביצוע + + + + Changes + ddl history header + ×©×™× ×•×™×™× + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + ×”×™×™×¦×•× ×¦×œ×—. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + שגי××” בעת ×™×¦×•× ×ª×•×¦×ות ש×ילתה: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + תבנית ביטוי־רגיל ×œ× ×ª×§×™× ×”: %1 + + + + + Could not open file %1 for reading: %2 + ×œ× × ×™×ª×Ÿ לפתוח קובץ%1 לקרי××”: %2 + + + + Could not open file %1 for writting: %2 + ×œ× × ×™×ª×Ÿ לפתוח קובץ%1 לכתיבה: %2 + + + + Error while writting to file %1: %2 + שגי××” בעת כתיבה לקובץ %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + ×œ× × ×™×ª×Ÿ לקבע תנועת ×œ× ×ª×•× ×™× ×”×ž×™×•×‘××™×: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + שגי××” בעת ×™×™×‘×•× × ×ª×•× ×™×: %1 + + + + + Interrupted. + import process status update + פסיקה. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + מזערי: %1 + + + + max: %1 + plugin dependency version + מירבי: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + קבוע + + + + PopulateConstantConfig + + + Constant value: + ערך קבוע: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + מילון + + + + PopulateDictionaryConfig + + + Dictionary file + קובץ מילון + + + + Pick dictionary file + בחירת קובץ מילון + + + + Word separator + מפריד ×ž×™×œ×™× + + + + Whitespace + מרווח לבן + + + + Line break + מעבר שורה + + + + Method of using words + שיטת שימוש ×‘×ž×™×œ×™× + + + + Ordered + ממוין + + + + Randomly + ×קר××™ + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + מספר ×קרעי + + + + PopulateRandomConfig + + + Constant prefix + תחילית קבועה + + + + No prefix + ×ין תחלית + + + + Minimum value + ערך מזערי + + + + Maximum value + ערך מרבי + + + + Constant suffix + סופית קבועה + + + + No suffix + ×œ×œ× ×¡×•×¤×™×ª + + + + PopulateRandomText + + + Random text + מלל ×קר××™ + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + ×ורך מזערי + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + ××œ×¤× + + + + Numbers from 0 to 9. + ספרתי בין 0 ל 9. + + + + Numeric + ספרתי + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + מרווח לבן + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + בינ×רי + + + + Use characters from my custom set: + שימוש ×‘×ª×•×•×™× ×ž×”×¡×“×¨×” המות×מת שלי: + + + + Maximum length + ×ורך מירבי + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + תסריט + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + שפה + + + + Help + עזרה + + + + PopulateSequence + + + Sequence + רצף + + + + PopulateSequenceConfig + + + Start value: + הערך ההתחלתי: + + + + Step: + שלב: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + ×œ× × ×™×ª×Ÿ לקבע תנועות ל×חר ×כלוס טבלה. פרטי שגי××”: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + ש×ילתה חלקית. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + שגי×ת תחביר + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + בחירת שפת יישו×. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + מטרה כללית + + + + Database support + plugin category name + תמיכת מסד × ×ª×•× ×™× + + + + Code formatter + plugin category name + מתבנת קוד + + + + Scripting languages + plugin category name + שפת תסריט + + + + Exporting + plugin category name + ×ž×™×™×¦× + + + + Importing + plugin category name + ×ž×™×™×‘× + + + + Table populating + plugin category name + ×כלוס טבלה + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + הביצוע הופסק. + + + + Database is not open. + מספד ×”× ×ª×•× ×™× ×ינו פתוח. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + שגי××” מ %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + מסד × ×ª×•× ×™× + + + + Execution date + sql history header + ת×ריך ביצוע + + + + Time spent + sql history header + ×—×•×ª× ×–×ž×Ÿ + + + + Rows affected + sql history header + שורות הושפעו + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_hu_HU.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_hu_HU.ts new file mode 100644 index 0000000..09d8517 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_hu_HU.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it.qm b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it.qm and /dev/null differ diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it.ts deleted file mode 100644 index 461461f..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it.ts +++ /dev/null @@ -1,1146 +0,0 @@ - - - - - AbstractDb - - - - Cannot execute query on closed database. - - - - - Error attaching database %1: %2 - - - - - ChainExecutor - - - The database for executing queries was not defined. - chain executor - - - - - The database for executing queries was not open. - chain executor - - - - - Could not disable foreign keys in the database. Details: %1 - chain executor - - - - - Could not start a database transaction. Details: %1 - chain executor - - - - - Interrupted - chain executor - - - - - Could not commit a database transaction. Details: %1 - chain executor - - - - - CompletionHelper - - - New row reference - - - - - Old row reference - - - - - New table name - - - - - New index name - - - - - New view name - - - - - New trigger name - - - - - Table or column alias - - - - - transaction name - - - - - New column name - - - - - Column data type - - - - - Constraint name - - - - - Error message - - - - - Collation name - - - - - Any word - - - - - Default database - - - - - Temporary objects database - - - - - ConfigImpl - - - Could not start database transaction for deleting SQL history, therefore it's not deleted. - - - - - Could not commit database transaction for deleting SQL history, therefore it's not deleted. - - - - - DbManagerImpl - - - Could not add database %1: %2 - - - - - Database %1 could not be updated, because of an error: %2 - - - - - - Database file doesn't exist. - - - - - - - No supporting plugin loaded. - - - - - Database could not be initialized. - - - - - No suitable database driver plugin found. - - - - - DbObjectOrganizer - - - - Error while creating table in target database: %1 - - - - - Could not parse table. - - - - - Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. - - - - - Error while copying data for table %1: %2 - - - - - - - Error while copying data to table %1: %2 - - - - - Error while dropping source view %1: %2 -Tables, indexes, triggers and views copied to database %3 will remain. - - - - - Error while creating view in target database: %1 - - - - - Error while creating index in target database: %1 - - - - - Error while creating trigger in target database: %1 - - - - - - - Could not parse object '%1' in order to move or copy it. - - - - - DbVersionConverter - - - Target file exists, but could not be overwritten. - - - - - Could not find proper database plugin to create target database. - - - - - Error while converting database: %1 - - - - - DdlHistoryModel - - - Database name - ddl history header - - - - - Database file - ddl history header - - - - - Date of execution - ddl history header - - - - - Changes - ddl history header - - - - - ExportManager - - - Export plugin %1 doesn't support exporing query results. - - - - - Export plugin %1 doesn't support exporing tables. - - - - - Export plugin %1 doesn't support exporing databases. - - - - - Export format '%1' is not supported. Supported formats are: %2. - - - - - Export to the clipboard was successful. - - - - - Export to the file '%1' was successful. - - - - - Export was successful. - - - - - Could not export to file %1. File cannot be open for writting. - - - - - ExportWorker - - - Error while exporting query results: %1 - - - - - Error while counting data column width to export from query results: %1 - - - - - - Could not parse %1 in order to export it. It will be excluded from the export output. - - - - - Error while reading data to export from table %1: %2 - - - - - Error while counting data to export from table %1: %2 - - - - - Error while counting data column width to export from table %1: %2 - - - - - FunctionManagerImpl - - - Invalid number of arguments to function '%1'. Expected %2, but got %3. - - - - - No such function registered in SQLiteStudio: %1(%2) - - - - - Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. - - - - - Invalid regular expression pattern: %1 - - - - - - Could not open file %1 for reading: %2 - - - - - Could not open file %1 for writting: %2 - - - - - Error while writting to file %1: %2 - - - - - Unsupported scripting language: %1 - - - - - GenericExportPlugin - - - Could not initialize text codec for exporting. Using default codec: %1 - - - - - ImportManager - - - Imported data to the table '%1' successfully. - - - - - ImportWorker - - - No columns provided by the import plugin. - - - - - Could not start transaction in order to import a data: %1 - - - - - Could not commit transaction for imported data: %1 - - - - - Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. - - - - - Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. - - - - - Could not create table to import to: %1 - - - - - - - Error while importing data: %1 - - - - - - Interrupted. - import process status update - - - - - Could not import data row number %1. The row was ignored. Problem details: %2 - - - - - PluginManagerImpl - - - Cannot load plugin %1, because it's in conflict with plugin %2. - - - - - Cannot load plugin %1, because its dependency was not loaded: %2. - - - - - Cannot load plugin %1. Error details: %2 - - - - - Cannot load plugin %1 (error while initializing plugin). - - - - - min: %1 - plugin dependency version - - - - - max: %1 - plugin dependency version - - - - - PopulateConstant - - - Constant - populate constant plugin name - - - - - PopulateConstantConfig - - - Constant value: - - - - - PopulateDictionary - - - Dictionary - dictionary populating plugin name - - - - - PopulateDictionaryConfig - - - Dictionary file - - - - - Pick dictionary file - - - - - Word separator - - - - - Whitespace - - - - - Line break - - - - - Method of using words - - - - - Ordered - - - - - Randomly - - - - - PopulateManager - - - Table '%1' populated successfully. - - - - - PopulateRandom - - - Random number - - - - - PopulateRandomConfig - - - Constant prefix - - - - - No prefix - - - - - Minimum value - - - - - Maximum value - - - - - Constant suffix - - - - - No suffix - - - - - PopulateRandomText - - - Random text - - - - - PopulateRandomTextConfig - - - Use characters from common sets: - - - - - Minimum length - - - - - Letters from a to z. - - - - - Alpha - - - - - Numbers from 0 to 9. - - - - - Numeric - - - - - A whitespace, a tab and a new line character. - - - - - Whitespace - - - - - Includes all above and all others. - - - - - Binary - - - - - Use characters from my custom set: - - - - - Maximum length - - - - - If you type some character multiple times, it's more likely to be used. - - - - - PopulateScript - - - Script - - - - - PopulateScriptConfig - - - Initialization code (optional) - - - - - Per step code - - - - - Language - - - - - Help - - - - - PopulateSequence - - - Sequence - - - - - PopulateSequenceConfig - - - Start value: - - - - - Step: - - - - - PopulateWorker - - - Could not start transaction in order to perform table populating. Error details: %1 - - - - - Error while populating table: %1 - - - - - Could not commit transaction after table populating. Error details: %1 - - - - - QObject - - - - Could not open database: %1 - - - - - - Result set expired or no row available. - - - - - - Could not load extension %1: %2 - - - - - Could not close database: %1 - - - - - - - - - - - SQLite %1 does not support '%2' statement. - - - - - SQLite %1 does not support '%2' statement, but the regular table can be created instead if you proceed. - - - - - Could not parse statement: %1 -Error details: %2 - - - - - - - - SQLite %1 does not support the '%2' clause. Cannot convert '%3' statement with that clause. - - - - - SQLite %1 does not support the '%2' clause in the '%3' statement. - - - - - SQLite %1 does not support current date or time clauses in expressions. - - - - - SQLite %1 does not support row value clauses in expressions. - - - - - - - SQLite %1 does not support '%2' clause in expressions. - - - - - Could not attach database %1: %2 - - - - - - Incomplete query. - - - - - - Parser stack overflow - - - - - - Syntax error - - - - - Could not open dictionary file %1 for reading. - - - - - Dictionary file must exist and be readable. - - - - - Maximum value cannot be less than minimum value. - - - - - Maximum length cannot be less than minimum length. - - - - - Custom character set cannot be empty. - - - - - Could not find plugin to support scripting language: %1 - - - - - Error while executing populating initial code: %1 - - - - - Error while executing populating code: %1 - - - - - Select implementation language. - - - - - Implementation code cannot be empty. - - - - - Could not resolve data source for column: %1 - - - - - Could not resolve table for column '%1'. - - - - - Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Tried to initialize the file at following localizations: %1. - - - - - General purpose - plugin category name - - - - - Database support - plugin category name - - - - - Code formatter - plugin category name - - - - - Scripting languages - plugin category name - - - - - Exporting - plugin category name - - - - - Importing - plugin category name - - - - - Table populating - plugin category name - - - - - Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. - - - - - All columns indexed by the index %1 are gone. The index will not be recreated after table modification. - - - - - There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. - - - - - All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. - - - - - Cannot not update trigger %1 according to table %2 modification. - - - - - Cannot not update view %1 according to table %2 modifications. -The view will remain as it is. - - - - - - - There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - - - - - Could not parse DDL of the view to be created. Details: %1 - - - - - Parsed query is not CREATE VIEW. It's: %1 - - - - - SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. - - - - - Could not open file '%1' for reading: %2 - - - - - QueryExecutor - - - Execution interrupted. - - - - - Database is not open. - - - - - Only one query can be executed simultaneously. - - - - - - An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 - - - - - SQLiteStudio was unable to extract metadata from the query. Results won't be editable. - - - - - ScriptingQtDbProxy - - - No database available in current context, while called QtScript's %1 command. - - - - - Error from %1: %2 - - - - - SqlHistoryModel - - - Database - sql history header - - - - - Execution date - sql history header - - - - - Time spent - sql history header - - - - - Rows affected - sql history header - - - - - SQL - sql history header - - - - - UpdateManager - - - Updates installer executable is missing. - - - - - - Unable to check for updates (%1) - - - - - details are unknown - - - - - Unable to run updater application (%1). Please report this. - - - - diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it_IT.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it_IT.ts new file mode 100644 index 0000000..975635e --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_it_IT.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Impossibile eseguire la query su un database chiuso. + + + + Error attaching database %1: %2 + Errore nell'allegare il database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Impossibile creare il checkpoint WAL completo sul database '%1'. Errore restituito dal motore SQLite: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + Il database per l'esecuzione delle query non è stato definito. + + + + The database for executing queries was not open. + chain executor + Il database per l'esecuzione delle query non è stato aperto. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Impossibile disabilitare le chiavi esterne nel database. Dettagli: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Impossibile avviare una transazione nel database. Dettagli: %1 + + + + Interrupted + chain executor + Interrotto + + + + Could not commit a database transaction. Details: %1 + chain executor + Impossibile effettuare il commit di una transazione nel database. Dettagli: %1 + + + + CompletionHelper + + + New row reference + Nuovo riferimento riga + + + + Old row reference + Vecchio riferimento riga + + + + New table name + Nuovo nome della tabella + + + + New index name + Nuovo nome indice + + + + New view name + Nuovo nome vista + + + + New trigger name + Nuovo nome del trigger + + + + Table or column alias + Alias tabella o colonna + + + + transaction name + nome della transazione + + + + New column name + Nuovo nome colonna + + + + Column data type + Tipo di dati colonna + + + + Constraint name + Nome del constraint + + + + Error message + Messaggio di errore + + + + Any word + Qualsiasi parola + + + + Default database + Database predefinito + + + + Temporary objects database + Database oggetti temporanei + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Impossibile avviare la transazione del database per eliminare la cronologia SQL, quindi non è stata eliminata. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Impossibile avviare la transazione del database per eliminare la cronologia SQL, quindi non è stata eliminata. + + + + DbManagerImpl + + + Could not add database %1: %2 + Impossibile aggiungere il database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Il database %1 non può essere aggiornato, a causa di un errore: %2 + + + + + Database file doesn't exist. + Il file del database non esiste. + + + + + + No supporting plugin loaded. + Nessun plugin di supporto caricato. + + + + Database could not be initialized. + Impossibile inizializzare il database. + + + + No suitable database driver plugin found. + Non è stato trovato alcun plugin per il driver del database. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Errore durante la creazione della tabella nel database di destinazione: %1 + + + + Could not parse table. + Impossibile analizzare la tabella. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Il database %1 non può essere allegato al database %2, così i dati della tabella %3 verranno copiati con SQLiteStudio come mediatore. Questo metodo può essere lento per le tabelle enormi, quindi sii paziente. + + + + Error while copying data for table %1: %2 + Errore durante la copia dei dati per la tabella %1: %2 + + + + + + Error while copying data to table %1: %2 + Errore durante la copia dei dati nella tabella %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Errore durante il rilascio della vista sorgente %1: %2 +Le tabelle, gli indici, i trigger e le viste copiate nel database %3 rimarranno. + + + + Error while creating view in target database: %1 + Errore durante la creazione della vista nel database di destinazione: %1 + + + + Error while creating index in target database: %1 + Errore durante la creazione dell'indice nel database di destinazione: %1 + + + + Error while creating trigger in target database: %1 + Errore durante la creazione del trigger nel database di destinazione: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Impossibile analizzare l'oggetto '%1' per spostarlo o copiarlo. + + + + DdlHistoryModel + + + Database name + ddl history header + Nome database + + + + Database file + ddl history header + File database + + + + Date of execution + ddl history header + Data di esecuzione + + + + Changes + ddl history header + Modifiche + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Il plugin di esportazione %1 non supporta l'esplorazione dei risultati delle interrogazioni. + + + + Export plugin %1 doesn't support exporing tables. + Il plugin di esportazione %1 non supporta l'esportazione di tabelle. + + + + Export plugin %1 doesn't support exporing databases. + Il plugin di esportazione %1 non supporta l'esportazione di databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Il formato di esportazione '%1' non è supportato. I formati supportati sono: %2. + + + + Export to the clipboard was successful. + Esportazione negli appunti riuscita. + + + + Export to the file '%1' was successful. + Esportazione nel file '%1' riuscita. + + + + Export was successful. + Esportazione riuscita. + + + + Could not export to file %1. File cannot be open for writting. + Impossibile esportare nel file %1. Il file non può essere aperto in scrittura. + + + + ExportWorker + + + Error while exporting query results: %1 + Errore durante l'esportazione dei risultati della query: %1 + + + + Error while counting data column width to export from query results: %1 + Errore durante il conteggio della larghezza della colonna dei dati da esportare dalla tabella %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Impossibile analizzare %1 per esportarlo. Sarà escluso dall'output di esportazione. + + + + Error while reading data to export from table %1: %2 + Errore durante la lettura dei dati da esportare dalla tabella %1: %2 + + + + Error while counting data to export from table %1: %2 + Errore durante il conteggio dei dati da esportare dalla tabella %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Errore durante il conteggio della larghezza della colonna dei dati da esportare dalla tabella %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Numero di argomenti non valido per la funzione '%1'. Atteso %2, ma ottenuto %3. + + + + No such function registered in SQLiteStudio: %1(%2) + Funzione inesistente non registrata in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + La funzione %1(%2) è stata registrata con la lingua %3, ma il plugin che supporta tale lingua non è attualmente caricato. + + + + Invalid regular expression pattern: %1 + Modello di espressione regolare non valido: %1 + + + + + Could not open file %1 for reading: %2 + Apertura del file %1 in lettura fallita: %2 + + + + Could not open file %1 for writting: %2 + Apertura del file %1 in scrittura fallita: %2 + + + + Error while writting to file %1: %2 + Errore durante la scrittura sul file %1: %2 + + + + Unsupported scripting language: %1 + Linguaggio di scripting non supportato: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Impossibile inizializzare il codec di testo per l'esportazione. Utilizzando il codec predefinito: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Dati importati nella tabella '%1' con successo. Numero di righe importate: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + Nessuna colonna fornita dal plugin di importazione. + + + + Could not start transaction in order to import a data: %1 + Impossibile avviare la transazione per importare i dati: %1 + + + + Could not commit transaction for imported data: %1 + Impossibile effettuare il commit della transazione per i dati importati: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + La tabella '%1' ha meno colonne di quelle che ci sono colonne nei dati da importare. Le colonne di dati eccessive verranno ignorate. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + La tabella '%1' ha più colonne di quelle che ci sono nei dati da importare. Alcune colonne nella tabella saranno lasciate vuote. + + + + Could not create table to import to: %1 + Impossibile creare la tabella da importare in: %1 + + + + + + Error while importing data: %1 + Errore durante l'importazione dei dati: %1 + + + + + Interrupted. + import process status update + Interrotto. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Impossibile importare riga numero %1. La riga è stata ignorata. Dettagli problema: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Impossibile caricare il plugin %1 perché và in conflitto con il plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Impossibile caricare il plugin %1, perché non è stata caricata la sua dipendenza: %2. + + + + Cannot load plugin %1. Error details: %2 + Impossibile caricare il plugin %1. Dettagli errore: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Impossibile caricare il plugin %1 (errore durante l'inizializzazione del plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Costante + + + + PopulateConstantConfig + + + Constant value: + Valore costante: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dizionario + + + + PopulateDictionaryConfig + + + Dictionary file + File dizionario + + + + Pick dictionary file + Scegli il file di dizionario + + + + Word separator + Separatore parole + + + + Whitespace + Spazio bianco + + + + Line break + Interruzione di linea + + + + Method of using words + Metodo di utilizzo delle parole + + + + Ordered + Ordinato + + + + Randomly + Casuale + + + + PopulateManager + + + Table '%1' populated successfully. + Tabella '%1' popolata con successo. + + + + PopulateRandom + + + Random number + Numeri casuali + + + + PopulateRandomConfig + + + Constant prefix + Prefisso costante + + + + No prefix + Nessun prefisso + + + + Minimum value + Valore minimo + + + + Maximum value + Valore massimo + + + + Constant suffix + Suffisso costante + + + + No suffix + Nessun suffisso + + + + PopulateRandomText + + + Random text + Testo casuale + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Usa i caratteri dai set comuni: + + + + Minimum length + Lunghezza minima + + + + Letters from a to z. + Lettere da a a z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numeri da 0 a 9. + + + + Numeric + Numerico + + + + A whitespace, a tab and a new line character. + Uno spazio bianco, un tab ed un carattere di ritorno a capo. + + + + Whitespace + Spazio bianco + + + + Includes all above and all others. + Include tutto sopra e tutti gli altri. + + + + Binary + Binario + + + + Use characters from my custom set: + Usa i caratteri del mio set personalizzato: + + + + Maximum length + Lunghezza massima + + + + If you type some character multiple times, it's more likely to be used. + Se si digita un carattere più volte, esso ha più probabilità di essere utilizzato. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Codice di inizializzazione (facoltativo) + + + + Per step code + Per passo di codice + + + + Language + Lingua + + + + Help + Aiuto + + + + PopulateSequence + + + Sequence + Sequenza + + + + PopulateSequenceConfig + + + Start value: + Valore iniziale: + + + + Step: + Passo: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Impossibile avviare la transazione per eseguire il popolamento della tabella. Dettagli di errore: %1 + + + + Error while populating table: %1 + Errore durante il popolamento della tabella: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Impossibile effettuare il commit della transazione dopo il popolamento della tabella. Dettagli errore: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Impossibile aprire il file '%1' in lettura: %2 + + + + Could not open database: %1 + Impossibile aprire il database: %1 + + + + Result set expired or no row available. + Risultato impostato scaduto o nessuna riga disponibile. + + + + + Could not load extension %1: %2 + Impossibile caricare l'estensione %1: %2 + + + + Could not run WAL checkpoint: %1 + Impossibile eseguire il punto di controllo WAL: %1 + + + + Could not close database: %1 + Impossibile chiudere il database: %1 + + + + Could not attach database %1: %2 + Impossibile allegare il database %1: %2 + + + + + Incomplete query. + Query incompleta. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Errore di sintassi + + + + Could not open dictionary file %1 for reading. + Impossibile aprire il file dizionario %1 in lettura. + + + + Dictionary file must exist and be readable. + Il file del dizionario deve esistere ed essere leggibile. + + + + Maximum value cannot be less than minimum value. + Il valore massimo non può essere inferiore al valore minimo. + + + + Maximum length cannot be less than minimum length. + La lunghezza massima non può essere minore della lunghezza minima. + + + + Custom character set cannot be empty. + Il set di caratteri personalizzati non può essere vuoto. + + + + Could not find plugin to support scripting language: %1 + Impossibile trovare il plugin per supportare la lingua dello script: %1 + + + + Error while executing populating initial code: %1 + Errore durante il popolamento del codice iniziale: %1 + + + + Error while executing populating code: %1 + Errore durante il popolamento del codice: %1 + + + + Select implementation language. + Seleziona lingua di implementazione. + + + + Implementation code cannot be empty. + Il codice di implementazione non può essere vuoto. + + + + Could not resolve data source for column: %1 + Impossibile risolvere la sorgente dati per la colonna: %1 + + + + Could not resolve table for column '%1'. + Impossibile risolvere la tabella per la colonna '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Impossibile inizializzare il file di configurazione. Qualsiasi modifica di configurazione e cronologia delle interrogazioni verrà persa dopo il riavvio dell'applicazione. Impossibile creare un file nelle seguenti posizioni: %1. + + + + General purpose + plugin category name + Scopo generale + + + + Database support + plugin category name + Supporto database + + + + Code formatter + plugin category name + Formattatore codice + + + + Scripting languages + plugin category name + Linguaggi di scripting + + + + Exporting + plugin category name + Sto esportando ... + + + + Importing + plugin category name + Sto importando ... + + + + Table populating + plugin category name + Popolamento tabella + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + La tabella %1 fa riferimento alla tabella %2, ma la definizione della chiave esterna non sarà aggiornata per la nuova definizione della tabella a causa di problemi durante l'analisi DDL della tabella %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + Tutte le colonne indicizzate dall'indice %1 non esistono più. L'indice non verrà ricreato dopo la modifica della tabella. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + C'è un problema con il corretto processo trigger %1. Potrebbe non essere completamente aggiornato in seguito e avrà bisogno della vostra attenzione. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + Tutte le colonne indicizzate dall'indice %1 non esistono più. L'indice non verrà ricreato dopo la modifica della tabella. + + + + Cannot not update trigger %1 according to table %2 modification. + Impossibile aggiornare il trigger %1 in accordo alla modifica della tabella %2. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Impossibile aggiornare la vista %1 in base alle modifiche della tabella %2. +La vista rimarrà così com'è. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + C'è un problema con l'aggiornamento di un'istruzione %1 entro %2 trigger. Una delle %1 sotto-istruzioni che potrebbero riferirsi alla tabella %3 non può essere modificata correttamente. Può essere necessario un aggiornamento manuale del trigger. + + + + Could not parse DDL of the view to be created. Details: %1 + Impossibile analizzare il DDL della vista da creare. Dettagli: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + La query analizzata non è una CREATE VIEW. Vedi: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio non è riuscito a risolvere le colonne restituite dalla nuova vista, quindi non potrà essere in grado di riportare quali trigger potrebbero fallire durante il processo di ricreazione. + + + + QueryExecutor + + + Execution interrupted. + Esecuzione interrotta. + + + + Database is not open. + Il database non è aperto. + + + + Only one query can be executed simultaneously. + Solo una query può essere eseguita contemporaneamente. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + Si è verificato un errore durante l'esecuzione della query count(*), quindi la pagina dei dati sarà disabilitata. Dettagli di errore dal database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio non è stato in grado di estrarre i metadati dalla query. I risultati non sono modificabili. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + Nessun database disponibile nel contesto attuale, mentre si richiamava il comando JavaScript %1. + + + + Error from %1: %2 + Errore da %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Impossibile eseguire SQL perché l'applicazione non è riuscita ad avviare la transazione: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Esecuzione dal file annullata. Tutte le query eseguite finora sono state ripristinate. + + + + Could not open file '%1' for reading: %2 + Impossibile aprire il file '%1' in lettura: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Impossibile eseguire SQL perché l'applicazione non è riuscita a effettuare il commit della transazione: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Completata l'esecuzione di %1 query in %2 secondi. %3 non è stata eseguita a causa di errori. + + + + Finished executing %1 queries in %2 seconds. + Terminata l'esecuzione di %1 query in %2 secondi. + + + + Could not execute SQL due to error. + Impossibile eseguire SQL a causa di errore. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Data di esecuzione + + + + Time spent + sql history header + Tempo trascorso + + + + Rows affected + sql history header + Righe interessate + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Impossibile controllare gli aggiornamenti: (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ja_JP.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ja_JP.ts new file mode 100644 index 0000000..79c7923 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ja_JP.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_kaa.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_kaa.ts new file mode 100644 index 0000000..f3ecb43 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_kaa.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Qádem: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ko_KR.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ko_KR.ts new file mode 100644 index 0000000..ee8f7d5 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ko_KR.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + ì—°ê²°ë˜ì§€ ì•Šì€ ë°ì´í„°ë² ì´ìФì—서 쿼리를 실행할 수 없습니다 + + + + Error attaching database %1: %2 + ë°ì´í„°ë² ì´ìФ ë“±ë¡ ì¤‘ 오류 %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + 오류 메시지 + + + + Any word + Any word + + + + Default database + 기본 ë°ì´í„°ë² ì´ìФ + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + ë°ì´í„°ë² ì´ìФ ì´ë¦„ + + + + Database file + ddl history header + ë°ì´í„°ë² ì´ìФ íŒŒì¼ + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_nl_NL.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_nl_NL.ts new file mode 100644 index 0000000..2034b82 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_nl_NL.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_no_NO.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_no_NO.ts new file mode 100644 index 0000000..e2d7736 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_no_NO.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl.qm b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl.qm deleted file mode 100644 index d8a2202..0000000 Binary files a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl.qm and /dev/null differ diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl.ts deleted file mode 100644 index 6a1e7aa..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl.ts +++ /dev/null @@ -1,1337 +0,0 @@ - - - - - AbstractDb - - - - Cannot execute query on closed database. - Nie można wykonać zapytania na zamkniÄ™tej bazie danych. - - - - Error attaching database %1: %2 - Błąd podczas dołączania bazy danych %1: %2 - - - - BugReporter - - Invalid login or password - Niepoprawny login lub hasÅ‚o - - - - ChainExecutor - - - The database for executing queries was not defined. - chain executor - Nie zdefiniowano bazy danych do wykonywania zapytaÅ„. - - - - The database for executing queries was not open. - chain executor - Baza danych do wykonywania zapytaÅ„ nie jest otwarta. - - - - Could not disable foreign keys in the database. Details: %1 - chain executor - Nie udaÅ‚o siÄ™ wyłączyć kluczy obcych w bazie. Szczegóły: %1 - - - - Could not start a database transaction. Details: %1 - chain executor - Nie udaÅ‚o siÄ™ rozpocząć transakcji bazy danych. Szczegóły: %1 - - - - Interrupted - chain executor - Przerwane - - - - Could not commit a database transaction. Details: %1 - chain executor - Nie udaÅ‚o siÄ™ zatwierdzić transakcji bazy danych. Szczegóły: %1 - - - - CompletionHelper - - - New row reference - OdnoÅ›nik do nowego wiersza - - - - Old row reference - OdnoÅ›nik do starego wiersza - - - - New table name - Nazwa nowej tabeli - - - - New index name - Nazwa nowego indeksu - - - - New view name - Nazwa nowego widoku - - - - New trigger name - Nazwa nowego wyzwalacza - - - - Table or column alias - Alias tabeli lub kolumny - - - - transaction name - Nazwa transakcji - - - - New column name - Nazwa nowej kolumny - - - - Column data type - Typ danych kolumny - - - - Constraint name - Nazwa ograniczenia - - - - Error message - Treść błędu - - - - Collation name - Nazwa zestawienia - - - - Any word - Dowolne sÅ‚owo - - - - Default database - DomyÅ›lna baza danych - - - - Temporary objects database - Baza danych obiektów tymczasowych - - - - ConfigImpl - - - Could not start database transaction for deleting SQL history, therefore it's not deleted. - Nie można rozpocząć transakcji dla usuwania historii SQL, wiÄ™c nie można usunąć historii. - - - - Could not commit database transaction for deleting SQL history, therefore it's not deleted. - Nie można zatwierdzić transakcji dla usuwania historii SQL, wiÄ™c nie można usunąć historii. - - - - DbManagerImpl - - - Could not add database %1: %2 - Nie udaÅ‚o siÄ™ dodać bazÄ™ danych %1: %2 - - - - Database %1 could not be updated, because of an error: %2 - Nie udaÅ‚o siÄ™ zaktualizować baza danych %1 z powodu błędu: %2 - - - - - Database file doesn't exist. - Plik bazy danych nie istnieje. - - - - - - No supporting plugin loaded. - Nie zaÅ‚adowano obsÅ‚ugujÄ…cej wtyczki. - - - - Database could not be initialized. - Nie udaÅ‚o siÄ™ zainicjalizować bazy danych. - - - - No suitable database driver plugin found. - Nie znaleziono odpowiedniej wtyczki sterownika. - - - - DbObjectOrganizer - - - - Error while creating table in target database: %1 - Błąd podczas tworzenia tabeli w docelowej bazie danych: %1 - - - - Could not parse table. - Nie udaÅ‚o siÄ™ przeanalizować tabeli. - - - - Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. - Nie udaÅ‚o siÄ™ dołączyć bazy danych %1 do bazy danych %2, wiÄ™c dane tabeli %3 bÄ™dÄ… skopiowane przez SQLiteStudio jako poÅ›rednika. Ta metoda może być powolna dla dużych tabel, wiÄ™c proszÄ™ o cierpliwość. - - - - Error while copying data for table %1: %2 - Błąd podczas copiowania danych tabeli %1: %2 - - - - - - Error while copying data to table %1: %2 - Błąd podczas kopiowania danych do tabeli %1: %2 - - - - Error while dropping source view %1: %2 -Tables, indexes, triggers and views copied to database %3 will remain. - Błąd podczas upuszczania widoku źródÅ‚owego %1: %2 -Tabele, indeksy, wyzwalacze i widoki skopiowane do bazy danych %3 pozostanÄ… na miejscu. - - - - Error while creating view in target database: %1 - Błąd podczas tworzenia widoku w docelowej bazie danych: %1 - - - - Error while creating index in target database: %1 - Błąd podczas tworzenia indeksu w docelowej bazie danych: %1 - - - - Error while creating trigger in target database: %1 - Błąd podczas tworzenia wyzwalacza w docelowej bazie danych: %1 - - - - - - Could not parse object '%1' in order to move or copy it. - Nie można zanalizować obiektu '%1' w celu przeniesienia lub skopiowania go. - - - - DbVersionConverter - - - Target file exists, but could not be overwritten. - Plik docelowy istnieje, ale nie może być nadpisany. - - - - Could not find proper database plugin to create target database. - Nie znaleziono odpowiedniej wtyczki bazy danych, aby utworzyć docelowÄ… bazÄ™ danych. - - - - Error while converting database: %1 - Błąd podczas konwersji bazy danych: %1 - - - - DdlHistoryModel - - - Database name - ddl history header - Nazwa bazy danych - - - - Database file - ddl history header - Plik bazy danych - - - - Date of execution - ddl history header - Data wykonania - - - - Changes - ddl history header - Zmiany - - - - ExportManager - - - Export plugin %1 doesn't support exporing query results. - Wtyczka eksportu %1 nie obsÅ‚uguje exportowania wyników zapytania. - - - - Export plugin %1 doesn't support exporing tables. - Wtyczka exportu %1 nie obsÅ‚uguje eksportowania tabel. - - - - Export plugin %1 doesn't support exporing databases. - Wtyczka exportu %1 nie obsÅ‚uguje eksportowania baz danych. - - - - Export format '%1' is not supported. Supported formats are: %2. - Format eksportu %1 nie jest obsÅ‚ugiwany. ObsÅ‚ugiwane formaty to: %2 - - - - Export to the clipboard was successful. - Eksport do schowka przebiegÅ‚ pomyÅ›lnie. - - - - Export to the file '%1' was successful. - Eksport do pliku '%1' przebiegÅ‚ pomyÅ›lnie. - - - - Export was successful. - Export przebiegÅ‚ pomyÅ›lnie. - - - - Could not export to file %1. File cannot be open for writting. - Eksport do pliku %1 nie powiódÅ‚ siÄ™. Plik nie może być otwarty do zapisu. - - - Export to was successful. - Eksport do przebiegÅ‚ pomyÅ›lnie. - - - - ExportWorker - - - Error while exporting query results: %1 - Błąd podczas eksportowania wyników zapytania: %1 - - - Error while counting data column width to export from query results: %2 - Błąd podczas liczenia szerokoÅ›ci kolumn danych do eksportu wyników zapytania: %2 - - - - Error while counting data column width to export from query results: %1 - Błąd podczas liczenia szerokoÅ›ci kolumn danych do eksportu wyników zapytania: %1 - - - - - Could not parse %1 in order to export it. It will be excluded from the export output. - Nie udaÅ‚o siÄ™ przeanalizować %1 w celu wyeksportowania. Element ten zostanie pominiÄ™ty w wynikach eksportu. - - - - Error while reading data to export from table %1: %2 - Błąd podczas odczytu danych do eksportu z tabeli %1: %2 - - - - Error while counting data to export from table %1: %2 - Błąd podczas liczenia danych do eksportu z tabeli %1: %2 - - - - Error while counting data column width to export from table %1: %2 - Błąd podczas obliczania szerokoÅ›ci kolumn danych do eksportu z tabeli %1: %2 - - - - FunctionManagerImpl - - - Invalid number of arguments to function '%1'. Expected %2, but got %3. - Niepoprawna liczba argumentów do funkcji '%1'. Oczekiwano %2, a jest %3. - - - - No such function registered in SQLiteStudio: %1(%2) - Nie znaleziono funkcji zarejestrowanej w SQLiteStudio: %1 (%2) - - - - Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. - Funkcja %1 (%2) zostaÅ‚a zarejestrowana dla jÄ™zyka %3, ale wtyczka obsÅ‚ugujÄ…ca ten jÄ™zyk nie jest aktualnie zaÅ‚adowana. - - - - Invalid regular expression pattern: %1 - Niepoprawne wyrażenie regularne: %1 - - - - - Could not open file %1 for reading: %2 - Nie udaÅ‚o siÄ™ otworzyć pliku %1 do odczytu: %2 - - - - Could not open file %1 for writting: %2 - Nie udaÅ‚o siÄ™ otworzyć pliku %2 do zapisu: %2 - - - - Error while writting to file %1: %2 - Błąd podczas zapisu do pliku %1: %2 - - - - Unsupported scripting language: %1 - NieobsÅ‚ugiwany jÄ™zyk skryptowy: %1 - - - - GenericExportPlugin - - - Could not initialize text codec for exporting. Using default codec: %1 - Nie udaÅ‚o siÄ™ zainicjalizować kodeka do exportu. Użyty bÄ™dzie domyÅ›lny kodek: %1 - - - - ImportManager - - - Imported data to the table '%1' successfully. - PomyÅ›lnie zaimportowano dane do tabeli '%1'. - - - - ImportWorker - - - No columns provided by the import plugin. - Wtyczka importu nie dostarczyÅ‚a żadnych kolumn. - - - - Could not start transaction in order to import a data: %1 - Nie udaÅ‚o siÄ™ wystartować transakcji w celu zaimportowania danych: %1 - - - - Could not commit transaction for imported data: %1 - Nie udaÅ‚o siÄ™ zatwierdzić transakcji w celu zaimportowania danych: %1 - - - - Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. - Tabela '%1' ma mniej kolumn, niż jest kolumn w danych do importu. Nadmiarowe kolumny zostanÄ… zignorowane. - - - - Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. - Tabela '%1' ma wiÄ™cej kolumn, niż jest kolumn w danych do importu. Część kolumn w tabeli bÄ™dzie pozostawiona pusta. - - - - Could not create table to import to: %1 - Nie udaÅ‚o siÄ™ stworzyć tabeli do zaimportowania: %1 - - - - - - Error while importing data: %1 - Błąd podczas importowania danych: %1 - - - - - Interrupted. - import process status update - Przerwano. - - - - Could not import data row number %1. The row was ignored. Problem details: %2 - Nie udaÅ‚o siÄ™ zaimportować wiersza danych numer %1. Wiersz ten zostaÅ‚ zignorowany. Szczegóły problemu: %2 - - - - PluginManagerImpl - - - Cannot load plugin %1, because it's in conflict with plugin %2. - Nie udaÅ‚o siÄ™ zaÅ‚adować wtyczki %1, ponieważ jest ona w konflikcie z wtyczkÄ… %2. - - - - Cannot load plugin %1, because its dependency was not loaded: %2. - Nie udaÅ‚o siÄ™ zaÅ‚adować wtyczki %1, ponieważ jej zależność nie zostaÅ‚a zaÅ‚adowana: %2 - - - - Cannot load plugin %1. Error details: %2 - Nie udaÅ‚o siÄ™ zaÅ‚adować wtyczki %1. Szczegóły błędu: %2 - - - - Cannot load plugin %1 (error while initializing plugin). - Nie udaÅ‚o siÄ™ zaÅ‚adować wtyczki %1 (błąd podczas inicjalizacji wtyczki). - - - - min: %1 - plugin dependency version - min: %1 - - - - max: %1 - plugin dependency version - maks: %1 - - - - PopulateConstant - - - Constant - populate constant plugin name - StaÅ‚a - - - - PopulateConstantConfig - - - Constant value: - StaÅ‚a wartość: - - - - PopulateDictionary - - - Dictionary - dictionary populating plugin name - SÅ‚ownik - - - - PopulateDictionaryConfig - - - Dictionary file - Plik sÅ‚ownika - - - - Pick dictionary file - Wybierz plik sÅ‚ownika - - - - Word separator - Separator sÅ‚owa - - - - Whitespace - BiaÅ‚y znak - - - - Line break - Nowa linia - - - - Method of using words - Metoda używania słów - - - - Ordered - UporzÄ…dkowana - - - - Randomly - Losowa - - - - PopulateManager - - - Table '%1' populated successfully. - Zaludnianie tabeli '%1' przebiegÅ‚o pomyÅ›lnie. - - - - PopulateRandom - - - Random number - Losowa liczba - - - - PopulateRandomConfig - - - Constant prefix - StaÅ‚y przedrostek - - - - No prefix - Bez predrostka - - - - Minimum value - Wartość minimalna - - - - Maximum value - Wartość maksymalna - - - - Constant suffix - StaÅ‚y przyrostek - - - - No suffix - Brak przyrostka - - - - PopulateRandomText - - - Random text - Losowy tekst - - - - PopulateRandomTextConfig - - - Use characters from common sets: - Użyj znaków z zestawów: - - - - Minimum length - DÅ‚ugość minimalna - - - - Letters from a to z. - Litery od a do z. - - - - Alpha - Litery - - - - Numbers from 0 to 9. - Liczby od 0 do 9. - - - - Numeric - Liczby - - - - A whitespace, a tab and a new line character. - Znak biaÅ‚y, znak tabulacji, znak nowej linii. - - - - Whitespace - Znak biaÅ‚y - - - - Includes all above and all others. - Zawiera wszystkie powyższe, oraz wszystkie pozostaÅ‚e. - - - - Binary - Binarne - - - - Use characters from my custom set: - Użyj znaków z mojego zestawu: - - - - Maximum length - DÅ‚ugość maksymalna - - - - If you type some character multiple times, it's more likely to be used. - JeÅ›li wpiszesz dany znak kilka razy, bÄ™dzie on miaÅ‚ wiÄ™kszÄ… szansÄ™ na wylosowanie. - - - - PopulateScript - - - Script - Skrypt - - - - PopulateScriptConfig - - - Initialization code (optional) - Kod inicjalizujÄ…cy (opcjonalny) - - - - Per step code - Kod dla każdego kroku - - - - Language - JÄ™zyk - - - - Help - Pomoc - - - - PopulateSequence - - - Sequence - Sekwencja - - - - PopulateSequenceConfig - - - Start value: - Wartość poczÄ…tkowa: - - - - Step: - Krok: - - - - PopulateWorker - - - Could not start transaction in order to perform table populating. Error details: %1 - Nie udaÅ‚o siÄ™ rozpocząć transakcji w celu zaludnienia tabeli. Szczegóły błędu: %1 - - - - Error while populating table: %1 - Błąd podczas zaludniania tabeli: %2 - - - - Could not commit transaction after table populating. Error details: %1 - Nie udaÅ‚o siÄ™ zatwierdzić transakcji po zaludnieniu tabeli. Szczegóły błędy: %1 - - - - QObject - - - - - - - - - SQLite %1 does not support '%2' statement. - SQLite %1 nie obsÅ‚uguje zapytania '%2'. - - - - SQLite %1 does not support '%2' statement, but the regular table can be created instead if you proceed. - SQLite %1 nie obsÅ‚uguje zapytania '%2', ale stworzona zostanie zwykÅ‚a tabela, jeÅ›li bÄ™dziesz kontynuować. - - - - Could not parse statement: %1 -Error details: %2 - Nie udaÅ‚o siÄ™ przeanalizować zapytania: %1 -Szczegóły błędu: %2 - - - - - - - SQLite %1 does not support the '%2' clause. Cannot convert '%3' statement with that clause. - SQLite %1 nie obsÅ‚uguje klauzuli '%2'. Nie można przekonwertować zapytania '%3' z tÄ… klauzulÄ…. - - - - SQLite %1 does not support the '%2' clause in the '%3' statement. - SQLite %1 nie obsÅ‚uguje klauzuli '%2' w zapytaniu '%3'. - - - SQLite %1 does not support the '%2' clause. Cannot convert '%1' statement with that clause. - SQLite %1 nie obsÅ‚uguje klauzuli '%2'. Nie można przekonwertować zapytania '%3' z tÄ… klauzulÄ…. {1 ?} {2'?} {1'?} - - - - SQLite %1 does not support current date or time clauses in expressions. - SQLite %1 nie obsÅ‚uguje aktualnej daty lub klauzul czasowu w wyrażeniach. - - - - SQLite %1 does not support row value clauses in expressions. - SQLite %1 nie obsÅ‚uguje klauzuli wartoÅ›ci wierszowej w wyrażeniach. - - - - - - SQLite %1 does not support '%2' clause in expressions. - SQLite %1 nie obsÅ‚uguje klauzuli '%2' w wyrażeniach. - - - - Could not attach database %1: %2 - Nie udaÅ‚o siÄ™ dołączyć bazy danych %1: %2 - - - - - Incomplete query. - Niekompletne zapytanie. - - - - - Parser stack overflow - Przeciążenie stosu analizatora. - - - - - Syntax error - Błąd skÅ‚adni - - - - Could not open dictionary file %1 for reading. - Nie udaÅ‚o siÄ™ otworzyć pliku sÅ‚ownika %1 do odczytu. - - - - Dictionary file must exist and be readable. - Plik sÅ‚ownika musi istnieć i musisz mieć prawa do jego odczytu. - - - - Maximum value cannot be less than minimum value. - Wartość maksymalna nie może być mniejsza niż wartość minimalna. - - - - Maximum length cannot be less than minimum length. - DÅ‚ugość maksymalna nie może być mniejsza niż dÅ‚ugość minimalna. - - - - Custom character set cannot be empty. - Zestaw wÅ‚asnych znaków nie może być pusty. - - - - Could not find plugin to support scripting language: %1 - Nie udaÅ‚o siÄ™ znaleźć wtyczki obsÅ‚ugujÄ…cej jÄ™zyk skryptowy: %1 - - - - Error while executing populating initial code: %1 - Błąd podczas wykonywania kodu inicjalizujÄ…cego zaludnianie: %1 - - - - Error while executing populating code: %1 - Błąd podczas wykonywania kodu zaludniania: %1 - - - - Select implementation language. - Wybierz jÄ™zyk implementacji. - - - - Implementation code cannot be empty. - Kod implementacji nie może być pusty. - - - - Could not resolve data source for column: %1 - Nie znaleziono źródÅ‚a danych dla kolumny: %1 - - - - Could not resolve table for column '%1'. - Nie można ustalić tabeli lub kolumny '%1'. - - - - Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Tried to initialize the file at following localizations: %1. - Nie udaÅ‚o siÄ™ zainicjalizować pliku konfiguracyjnego. Jakiekolwiek zmiany w konfiguracji i historia zapytaÅ„ bÄ™dÄ… utracone po zrestartowaniu aplikacji. Próbowano zainicjalizować plik konfiguracyjny w nastÄ™pujÄ…cych lokalizacjach: %1. - - - - General purpose - plugin category name - Ogólne - - - - Database support - plugin category name - Wsparcie baz danych - - - - Code formatter - plugin category name - Formatowanie kodu - - - - Scripting languages - plugin category name - JÄ™zyki skryptowe - - - - Exporting - plugin category name - Eksportowanie - - - - Importing - plugin category name - Importowanie - - - - Table populating - plugin category name - Zaludnianie tabel - - - - Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. - Tabela %1 odwoÅ‚uje siÄ™ do tabeli %2, ale definicja klucza obcego nie zostanie zaktualizowane dla definicji nowej tabeli w zwiÄ…zku z problemami przy analizowaniu DDL tabeli %3. - - - - All columns indexed by the index %1 are gone. The index will not be recreated after table modification. - Wszystkie kolumny indeksowane przez indeks %1 już nie istniejÄ…. Indeks ten nie bÄ™dzie odtworzony po modyfikacji tabeli. - - - - There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. - WystÄ…piÅ‚ problem z poprawnym przetworzeniem wyzwalacza %1. Może on zostać zaktualizowany tylko częściowo i bÄ™dzie wymagaÅ‚ twojej uwagi. - - - - Cannot not update trigger %1 according to table %2 modification. - Nie można zaktualizować wyzwalacza %1 zgodnie z modyfikacjami tabeli %2. - - - - - - There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - Jest problem ze zaktualizowaniem zapytania %1 w wyzwalaczu %2. Jedeno z podzapytaÅ„ %1, które może odwoÅ‚ywać siÄ™ do tabeli %3 nie może być poprawnie zmodyfikowane. RÄ™czna aktualizacja tego wyzwalacza może być niezbÄ™dna. - - - - All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. - Wszystkie kolumny obsÅ‚ugiwane przez wyzwalacz %1 już nie istniejÄ…. Wyzwalacz ten nie bÄ™dzie odtworzony po modyfikacji tabeli. - - - - Cannot not update view %1 according to table %2 modifications. -The view will remain as it is. - Nie można zaktualizować widoku %1 w zwiÄ…zku z modyfikacjami tabeli %2. -Widok pozostanie nienaruszony. - - - There is a problem with updating an %1 statement within %2 trigger. One of the SELECT substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - Jest problem ze zaktualizowaniem zapytania %1 w wyzwalaczu %2. Jedeno z podzapytaÅ„ SELECT, które może odwoÅ‚ywać siÄ™ do tabeli %3 nie może być poprawnie zmodyfikowane. RÄ™czna aktualizacja tego wyzwalacza może być niezbÄ™dna. - - - - Could not parse DDL of the view to be created. Details: %1 - Nie udaÅ‚o siÄ™ przeanalizować DDL widoku do stworzenia. Szczegóły: %1 - - - - Parsed query is not CREATE VIEW. It's: %1 - Przeanalizowane zapytanie to nie CREATE VIEW, ale: %1 - - - - SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. - SQLiteStudio nie byÅ‚o w stanie okreÅ›lić kolumn zwracanych przez nowy widok, w zwiÄ…zku z czym nie może okreÅ›lić które wyzwalacze mogÄ… siÄ™ nie powieść podczas procesu odtwarzania. - - - - - Could not open database: %1 - Nie udaÅ‚o siÄ™ otworzyć bazy danych: %1 - - - - - Could not load extension %1: %2 - Nie udaÅ‚o siÄ™ zaÅ‚adować rozszerzenia %1: %2 - - - - Could not close database: %1 - Nie udaÅ‚o siÄ™ zamknąć bazy danych: %1 - - - - - Result set expired or no row available. - Wyniki zapytania sÄ… nieaktualne, lub nie ma dostÄ™pnych wierszy. - - - - Could not open file '%1' for reading: %2 - Nie można otworzyż pliku '%1' do odczytu: %2 - - - - Query - - Result set expired or no row available. - Wyniki zapytania sÄ… nieaktualne, lub nie ma dostÄ™pnych wierszy. - - - - QueryExecutor - - - Execution interrupted. - Wykonywanie przerwane. - - - - Database is not open. - Baza danych nie jest otwarta. - - - - Only one query can be executed simultaneously. - Tylko jedno zapytanie może być wykonywane w danym momencie. - - - - - An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 - WystÄ…piÅ‚ błąd podczas wykonywania zapytania count(*), przez co stronicowanie danych bÄ™dzie wyłączone. Szczegóły błędy z bazy danych: %1 - - - - SQLiteStudio was unable to extract metadata from the query. Results won't be editable. - SQLiteStudio nie mogÅ‚o uzyskać metadanych z zapytania. Nie bÄ™dzie można edytować wyników zapytania. - - - - ScriptingQtDbProxy - - - No database available in current context, while called QtScript's %1 command. - Brak dostÄ™pnej bazy danych w bieżącym kontekÅ›cie, podczas wywoÅ‚ywania komendy QtScript: %1. - - - - Error from %1: %2 - Błąd z %1: %2 - - - - SqlHistoryModel - - - Database - sql history header - Baza danych - - - - Execution date - sql history header - Data wykonania - - - - Time spent - sql history header - Czas trwania - - - - Rows affected - sql history header - Liczba wierszy - - - - SQL - sql history header - SQL - - - - UpdateManager - - An error occurred while checking for updates: %1. - WystÄ…piÅ‚ błąd podczas sprawdzania aktualizacji: %1 - - - Could not check available updates, because server responded with invalid message format. It is safe to ignore this warning. - Nie udaÅ‚o siÄ™ sprawdzić aktualizacji, ponieważ serwer odpowiedziaÅ‚ wiadomoÅ›ciÄ… w niepoprawnym formacie. Możesz spokojnie zignorować tÄ… informacjÄ™. - - - An error occurred while reading updates metadata: %1. - WystÄ…piÅ‚ błąd podczas odczytu metadanych aktualizacji: %1 - - - Could not download updates, because server responded with invalid message format. You can try again later or download and install updates manually. See <a href="%1">User Manual</a> for details. - Nie udaÅ‚o siÄ™ Å›ciÄ…gnąć aktualizacji, ponieważ serwer odpowiedziaÅ‚ wiadomoÅ›ciÄ… w niepoprawnym formacie. Możesz spróbować jeszcze raz później, lub Å›ciÄ…gnąć i stainstalować aktualizacjÄ™ rÄ™cznie. Szczegóły: <a href="%1">PodrÄ™cznik użytkownika</a>. - - - Could not create temporary directory for downloading the update. Updating aborted. - Nie udaÅ‚o siÄ™ stworzyć katalogu tymczasowego w celu pobrania aktualizacji. Aktualizacja zostaÅ‚a przerwana. - - - There was no updates to download. Updating aborted. - Nie znaleziono aktualizacji do pobrania. Aktualizacja przerwana. - - - Downloading: %1 - Pobieranie: %1 - - - Could not determinate file name from update URL: %1. Updating aborted. - Nie udaÅ‚o siÄ™ okreÅ›lić nazwy pliku z URL aktualizacji: %1. Aktualizacja przerwana. - - - Failed to open file '%1' for writting: %2. Updating aborted. - Nie udaÅ‚o siÄ™ otworzyć pliku '%1' do zapisu: %2. Aktualizacja przerwana. - - - Installing updates. - Instalowanie aktualizacji. - - - Could not copy current application directory into %1 directory. - Nie udaÅ‚o siÄ™ skopiować bieżącego katalogu aplikacji do katalogu %1. - - - Could not create directory %1. - Nie udaÅ‚o siÄ™ stworzyć katalogu %1. - - - Could not rename directory %1 to %2. -Details: %3 - Nie udaÅ‚o siÄ™ zmienić nazwy katalogu %1 na %2. -Szczegóły: %3 - - - Cannot not rename directory %1 to %2. -Details: %3 - Nie można zmienić nazwy katalogu %1 na %2. -Szczegóły: %3 - - - Could not move directory %1 to %2 and also failed to restore original directory, so the original SQLiteStudio directory is now located at: %3 - Nie udaÅ‚o siÄ™ przenieść katalogu %1 do %2, oraz nie udaÅ‚o siÄ™ przywrócić originalnego katalog, wiÄ™c originalny katalog SQLiteStudio jest mieÅ›ci siÄ™ teraz w: %3 - - - Could not rename directory %1 to %2. Rolled back to the original SQLiteStudio version. - Nie udaÅ‚o siÄ™ zmienić nazwy katalogu %1 na %2. Przywrócono originalnÄ… wersjÄ™ SQLiteStudio. - - - Could not unpack component %1 into %2 directory. - Nie udaÅ‚o siÄ™ rozpakować komponentu %1 do katalogu %2. - - - Could not find permissions elevator application to run update as a root. Looked for: %1 - Nie udaÅ‚o siÄ™ znaleźć narzÄ™dzia do podnoszenia uprawnieÅ„ aplikacji, aby uruchomić aktualizacjÄ™ jako administrator. Szukano nastÄ™pujÄ…cych: %1 - - - Could not execute final updating steps as root: %1 - Nie udaÅ‚o siÄ™ wykonać ostatnich kroków jako administrator: %1 - - - Could not execute final updating steps as admin: %1 - Nie udaÅ‚o siÄ™ wykonać ostatnich kroków jako administrator: %1 - - - Cannot create temporary directory for updater. - Nie można stworzyć tymczasowego katalogu dla aktualizacji. - - - Cannot create updater script file. - Nie można utworzyć skryptu aktualizacji. - - - Updating canceled. - Aktualizacja wycofana. - - - Could not execute final updating steps as administrator. - Nie udaÅ‚o siÄ™ wykonać ostatich kroków aktualizacji jako administrator. - - - Could not execute final updating steps as administrator. Updater startup timed out. - Nie udaÅ‚o siÄ™ wykonać ostatich kroków aktualizacji jako administrator. Przekroczono limit czasu oczekiwania. - - - Could not execute final updating steps as administrator. Updater operation timed out. - Nie udaÅ‚o siÄ™ wykonać ostatich kroków aktualizacji jako administrator. Przekroczono limit czasu oczekiwania. - - - Could not clean up temporary directory %1. You can delete it manually at any time. - Nie udaÅ‚o siÄ™ wyczyÅ›cić katalogu tymczasowego %1. Możesz go usunąć rÄ™cznie w dowolnym momencie. - - - Could not run new version for continuing update. - Nie udaÅ‚o siÄ™ uruchomić nowej wersji w celu kontynuowania aktualizacji. - - - Package not in tar.gz format, cannot install: %1 - Paczka nie jest w formacie tar.gz, nie można zainstalować: %1 - - - Package %1 cannot be installed, because cannot move it to directory: %2 - Paczka %1 nie może być zainstalowana, ponieważ nie można przenieść jej do katalogu: %2 - - - Package %1 cannot be installed, because cannot unpack it: %2 - Paczka %1 nie może być zainstalowana, ponieważ nie można jej rozpakować: %2 - - - Package not in zip format, cannot install: %1 - Paczka nie jest w formacie zip, nie można zainstalować: %1 - - - Package %1 cannot be installed, because cannot unzip it to directory %2: %3 - Paczka %1 nie może być zainstalowana, ponieważ nie można jej rozpakować do katalogu %2: %3 - - - Package %1 cannot be installed, because cannot unzip it to directory: %2 - Paczka %1 nie może być zainstalowana, ponieważ nie można jej rozpakować do katalogu %2 - - - Could not rename directory %1 to %2. - Nie udaÅ‚o siÄ™ zmienić nazwy katalogu %1 na %2. - - - Could not delete directory %1. - Nie udaÅ‚o siÄ™ skasować katalogu %1. - - - Error executing update command: %1 -Error message: %2 - Błąd podczas wykonywania polecenia aktualizacji: %1 -Treść błędu: %2 - - - An error occurred while downloading updates: %1. Updating aborted. - WystÄ…piÅ‚ błąd podczas pobierania aktualizacji: %1. Aktualizacja przerwana. - - - - Updates installer executable is missing. - Nie można znaleźć pliku wykonywalnego instalatora aktualizacji. - - - - - Unable to check for updates (%1) - Nie można sprawdzić dostÄ™pnych aktualizacji (%1) - - - - details are unknown - szczegóły nieznane - - - - Unable to run updater application (%1). Please report this. - Nie można uruchomić aplikacji aktualizujÄ…cej (%1). ProszÄ™ to zgÅ‚osić. - - - diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl_PL.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl_PL.ts new file mode 100644 index 0000000..3d30cbc --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pl_PL.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Nie można wykonać zapytania na zamkniÄ™tej bazie danych. + + + + Error attaching database %1: %2 + Błąd podczas dołączania bazy danych %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Nie udaÅ‚o siÄ™ utworzyć peÅ‚nego punktu kontrolnego WAL w bazie danych '%1'. Błąd zwrócony z silnika SQLite: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + Nie zdefiniowano bazy danych do wykonywania zapytaÅ„. + + + + The database for executing queries was not open. + chain executor + Baza danych do wykonywania zapytaÅ„ nie jest otwarta. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Nie udaÅ‚o siÄ™ wyłączyć kluczy obcych w bazie. Szczegóły: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Nie udaÅ‚o siÄ™ rozpocząć transakcji bazy danych. Szczegóły: %1 + + + + Interrupted + chain executor + Przerwane + + + + Could not commit a database transaction. Details: %1 + chain executor + Nie udaÅ‚o siÄ™ zatwierdzić transakcji bazy danych. Szczegóły: %1 + + + + CompletionHelper + + + New row reference + OdnoÅ›nik do nowego wiersza + + + + Old row reference + OdnoÅ›nik do starego wiersza + + + + New table name + Nazwa nowej tabeli + + + + New index name + Nazwa nowego indeksu + + + + New view name + Nazwa nowego widoku + + + + New trigger name + Nazwa nowego wyzwalacza + + + + Table or column alias + Alias tabeli lub kolumny + + + + transaction name + Nazwa transakcji + + + + New column name + Nazwa nowej kolumny + + + + Column data type + Typ danych kolumny + + + + Constraint name + Nazwa ograniczenia + + + + Error message + Treść błędu + + + + Any word + Dowolne sÅ‚owo + + + + Default database + DomyÅ›lna baza danych + + + + Temporary objects database + Baza danych obiektów tymczasowych + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Nie można rozpocząć transakcji dla usuwania historii SQL, wiÄ™c nie można usunąć historii. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Nie można zatwierdzić transakcji dla usuwania historii SQL, wiÄ™c nie można usunąć historii. + + + + DbManagerImpl + + + Could not add database %1: %2 + Nie udaÅ‚o siÄ™ dodać bazÄ™ danych %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Nie udaÅ‚o siÄ™ zaktualizować baza danych %1 z powodu błędu: %2 + + + + + Database file doesn't exist. + Plik bazy danych nie istnieje. + + + + + + No supporting plugin loaded. + Nie zaÅ‚adowano obsÅ‚ugujÄ…cej wtyczki. + + + + Database could not be initialized. + Nie udaÅ‚o siÄ™ zainicjalizować bazy danych. + + + + No suitable database driver plugin found. + Nie znaleziono odpowiedniej wtyczki sterownika. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Błąd podczas tworzenia tabeli w docelowej bazie danych: %1 + + + + Could not parse table. + Nie udaÅ‚o siÄ™ przeanalizować tabeli. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Nie udaÅ‚o siÄ™ dołączyć bazy danych %1 do bazy danych %2, wiÄ™c dane tabeli %3 bÄ™dÄ… skopiowane przez SQLiteStudio jako poÅ›rednika. Ta metoda może być powolna dla dużych tabel, wiÄ™c proszÄ™ o cierpliwość. + + + + Error while copying data for table %1: %2 + Błąd podczas copiowania danych tabeli %1: %2 + + + + + + Error while copying data to table %1: %2 + Błąd podczas kopiowania danych do tabeli %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Błąd podczas upuszczania widoku źródÅ‚owego %1: %2 +Tabele, indeksy, wyzwalacze i widoki skopiowane do bazy danych %3 pozostanÄ… na miejscu. + + + + Error while creating view in target database: %1 + Błąd podczas tworzenia widoku w docelowej bazie danych: %1 + + + + Error while creating index in target database: %1 + Błąd podczas tworzenia indeksu w docelowej bazie danych: %1 + + + + Error while creating trigger in target database: %1 + Błąd podczas tworzenia wyzwalacza w docelowej bazie danych: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Nie można zanalizować obiektu '%1' w celu przeniesienia lub skopiowania go. + + + + DdlHistoryModel + + + Database name + ddl history header + Nazwa bazy danych + + + + Database file + ddl history header + Plik bazy danych + + + + Date of execution + ddl history header + Data wykonania + + + + Changes + ddl history header + Zmiany + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Wtyczka eksportu %1 nie obsÅ‚uguje exportowania wyników zapytania. + + + + Export plugin %1 doesn't support exporing tables. + Wtyczka exportu %1 nie obsÅ‚uguje eksportowania tabel. + + + + Export plugin %1 doesn't support exporing databases. + Wtyczka exportu %1 nie obsÅ‚uguje eksportowania baz danych. + + + + Export format '%1' is not supported. Supported formats are: %2. + Format eksportu %1 nie jest obsÅ‚ugiwany. ObsÅ‚ugiwane formaty to: %2 + + + + Export to the clipboard was successful. + Eksport do schowka przebiegÅ‚ pomyÅ›lnie. + + + + Export to the file '%1' was successful. + Eksport do pliku '%1' przebiegÅ‚ pomyÅ›lnie. + + + + Export was successful. + Export przebiegÅ‚ pomyÅ›lnie. + + + + Could not export to file %1. File cannot be open for writting. + Eksport do pliku %1 nie powiódÅ‚ siÄ™. Plik nie może być otwarty do zapisu. + + + + ExportWorker + + + Error while exporting query results: %1 + Błąd podczas eksportowania wyników zapytania: %1 + + + + Error while counting data column width to export from query results: %1 + Błąd podczas liczenia szerokoÅ›ci kolumn danych do eksportu wyników zapytania: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Nie udaÅ‚o siÄ™ przeanalizować %1 w celu wyeksportowania. Element ten zostanie pominiÄ™ty w wynikach eksportu. + + + + Error while reading data to export from table %1: %2 + Błąd podczas odczytu danych do eksportu z tabeli %1: %2 + + + + Error while counting data to export from table %1: %2 + Błąd podczas liczenia danych do eksportu z tabeli %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Błąd podczas obliczania szerokoÅ›ci kolumn danych do eksportu z tabeli %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Niepoprawna liczba argumentów do funkcji '%1'. Oczekiwano %2, a jest %3. + + + + No such function registered in SQLiteStudio: %1(%2) + Nie znaleziono funkcji zarejestrowanej w SQLiteStudio: %1 (%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Funkcja %1 (%2) zostaÅ‚a zarejestrowana dla jÄ™zyka %3, ale wtyczka obsÅ‚ugujÄ…ca ten jÄ™zyk nie jest aktualnie zaÅ‚adowana. + + + + Invalid regular expression pattern: %1 + Niepoprawne wyrażenie regularne: %1 + + + + + Could not open file %1 for reading: %2 + Nie udaÅ‚o siÄ™ otworzyć pliku %1 do odczytu: %2 + + + + Could not open file %1 for writting: %2 + Nie udaÅ‚o siÄ™ otworzyć pliku %2 do zapisu: %2 + + + + Error while writting to file %1: %2 + Błąd podczas zapisu do pliku %1: %2 + + + + Unsupported scripting language: %1 + NieobsÅ‚ugiwany jÄ™zyk skryptowy: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Nie udaÅ‚o siÄ™ zainicjalizować kodeka do exportu. Użyty bÄ™dzie domyÅ›lny kodek: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + PomyÅ›lnie zaimportowano dane do tabeli '%1'. Liczba zaimportowanych wierszy: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + Wtyczka importu nie dostarczyÅ‚a żadnych kolumn. + + + + Could not start transaction in order to import a data: %1 + Nie udaÅ‚o siÄ™ wystartować transakcji w celu zaimportowania danych: %1 + + + + Could not commit transaction for imported data: %1 + Nie udaÅ‚o siÄ™ zatwierdzić transakcji w celu zaimportowania danych: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Tabela '%1' ma mniej kolumn, niż jest kolumn w danych do importu. Nadmiarowe kolumny zostanÄ… zignorowane. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Tabela '%1' ma wiÄ™cej kolumn, niż jest kolumn w danych do importu. Część kolumn w tabeli bÄ™dzie pozostawiona pusta. + + + + Could not create table to import to: %1 + Nie udaÅ‚o siÄ™ stworzyć tabeli do zaimportowania: %1 + + + + + + Error while importing data: %1 + Błąd podczas importowania danych: %1 + + + + + Interrupted. + import process status update + Przerwano. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Nie udaÅ‚o siÄ™ zaimportować wiersza danych numer %1. Wiersz ten zostaÅ‚ zignorowany. Szczegóły problemu: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Nie udaÅ‚o siÄ™ zaÅ‚adować wtyczki %1, ponieważ jest ona w konflikcie z wtyczkÄ… %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Nie udaÅ‚o siÄ™ zaÅ‚adować wtyczki %1, ponieważ jej zależność nie zostaÅ‚a zaÅ‚adowana: %2 + + + + Cannot load plugin %1. Error details: %2 + Nie udaÅ‚o siÄ™ zaÅ‚adować wtyczki %1. Szczegóły błędu: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Nie udaÅ‚o siÄ™ zaÅ‚adować wtyczki %1 (błąd podczas inicjalizacji wtyczki). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + maks: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + StaÅ‚a + + + + PopulateConstantConfig + + + Constant value: + StaÅ‚a wartość: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + SÅ‚ownik + + + + PopulateDictionaryConfig + + + Dictionary file + Plik sÅ‚ownika + + + + Pick dictionary file + Wybierz plik sÅ‚ownika + + + + Word separator + Separator sÅ‚owa + + + + Whitespace + BiaÅ‚y znak + + + + Line break + Nowa linia + + + + Method of using words + Metoda używania słów + + + + Ordered + UporzÄ…dkowana + + + + Randomly + Losowa + + + + PopulateManager + + + Table '%1' populated successfully. + Zaludnianie tabeli '%1' przebiegÅ‚o pomyÅ›lnie. + + + + PopulateRandom + + + Random number + Losowa liczba + + + + PopulateRandomConfig + + + Constant prefix + StaÅ‚y przedrostek + + + + No prefix + Bez predrostka + + + + Minimum value + Wartość minimalna + + + + Maximum value + Wartość maksymalna + + + + Constant suffix + StaÅ‚y przyrostek + + + + No suffix + Brak przyrostka + + + + PopulateRandomText + + + Random text + Losowy tekst + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Użyj znaków z zestawów: + + + + Minimum length + DÅ‚ugość minimalna + + + + Letters from a to z. + Litery od a do z. + + + + Alpha + Litery + + + + Numbers from 0 to 9. + Liczby od 0 do 9. + + + + Numeric + Liczby + + + + A whitespace, a tab and a new line character. + Znak biaÅ‚y, znak tabulacji, znak nowej linii. + + + + Whitespace + Znak biaÅ‚y + + + + Includes all above and all others. + Zawiera wszystkie powyższe, oraz wszystkie pozostaÅ‚e. + + + + Binary + Binarne + + + + Use characters from my custom set: + Użyj znaków z mojego zestawu: + + + + Maximum length + DÅ‚ugość maksymalna + + + + If you type some character multiple times, it's more likely to be used. + JeÅ›li wpiszesz dany znak kilka razy, bÄ™dzie on miaÅ‚ wiÄ™kszÄ… szansÄ™ na wylosowanie. + + + + PopulateScript + + + Script + Skrypt + + + + PopulateScriptConfig + + + Initialization code (optional) + Kod inicjalizujÄ…cy (opcjonalny) + + + + Per step code + Kod dla każdego kroku + + + + Language + JÄ™zyk + + + + Help + Pomoc + + + + PopulateSequence + + + Sequence + Sekwencja + + + + PopulateSequenceConfig + + + Start value: + Wartość poczÄ…tkowa: + + + + Step: + Krok: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Nie udaÅ‚o siÄ™ rozpocząć transakcji w celu zaludnienia tabeli. Szczegóły błędu: %1 + + + + Error while populating table: %1 + Błąd podczas zaludniania tabeli: %2 + + + + Could not commit transaction after table populating. Error details: %1 + Nie udaÅ‚o siÄ™ zatwierdzić transakcji po zaludnieniu tabeli. Szczegóły błędy: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Nie można otworzyż pliku '%1' do odczytu: %2 + + + + Could not open database: %1 + Nie udaÅ‚o siÄ™ otworzyć bazy danych: %1 + + + + Result set expired or no row available. + Wyniki zapytania sÄ… nieaktualne, lub nie ma dostÄ™pnych wierszy. + + + + + Could not load extension %1: %2 + Nie udaÅ‚o siÄ™ zaÅ‚adować rozszerzenia %1: %2 + + + + Could not run WAL checkpoint: %1 + Nie można uruchomić punktu kontrolnego WAL: %1 + + + + Could not close database: %1 + Nie udaÅ‚o siÄ™ zamknąć bazy danych: %1 + + + + Could not attach database %1: %2 + Nie udaÅ‚o siÄ™ dołączyć bazy danych %1: %2 + + + + + Incomplete query. + Niekompletne zapytanie. + + + + Parser stack overflow + Przeciążenie stosu analizatora. + + + + Syntax error + Błąd skÅ‚adni + + + + Could not open dictionary file %1 for reading. + Nie udaÅ‚o siÄ™ otworzyć pliku sÅ‚ownika %1 do odczytu. + + + + Dictionary file must exist and be readable. + Plik sÅ‚ownika musi istnieć i musisz mieć prawa do jego odczytu. + + + + Maximum value cannot be less than minimum value. + Wartość maksymalna nie może być mniejsza niż wartość minimalna. + + + + Maximum length cannot be less than minimum length. + DÅ‚ugość maksymalna nie może być mniejsza niż dÅ‚ugość minimalna. + + + + Custom character set cannot be empty. + Zestaw wÅ‚asnych znaków nie może być pusty. + + + + Could not find plugin to support scripting language: %1 + Nie udaÅ‚o siÄ™ znaleźć wtyczki obsÅ‚ugujÄ…cej jÄ™zyk skryptowy: %1 + + + + Error while executing populating initial code: %1 + Błąd podczas wykonywania kodu inicjalizujÄ…cego zaludnianie: %1 + + + + Error while executing populating code: %1 + Błąd podczas wykonywania kodu zaludniania: %1 + + + + Select implementation language. + Wybierz jÄ™zyk implementacji. + + + + Implementation code cannot be empty. + Kod implementacji nie może być pusty. + + + + Could not resolve data source for column: %1 + Nie znaleziono źródÅ‚a danych dla kolumny: %1 + + + + Could not resolve table for column '%1'. + Nie można ustalić tabeli lub kolumny '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Nie udaÅ‚o siÄ™ zainicjalizować pliku konfiguracyjnego. Wszelkie zmiany w konfiguracji i historia zapytaÅ„ zostanÄ… utracone po restarcie aplikacji. Nie udaÅ‚o siÄ™ utworzyć pliku w lokalizacji: %1. + + + + General purpose + plugin category name + Ogólne + + + + Database support + plugin category name + Wsparcie baz danych + + + + Code formatter + plugin category name + Formatowanie kodu + + + + Scripting languages + plugin category name + JÄ™zyki skryptowe + + + + Exporting + plugin category name + Eksportowanie + + + + Importing + plugin category name + Importowanie + + + + Table populating + plugin category name + Zaludnianie tabel + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Tabela %1 odwoÅ‚uje siÄ™ do tabeli %2, ale definicja klucza obcego nie zostanie zaktualizowane dla definicji nowej tabeli w zwiÄ…zku z problemami przy analizowaniu DDL tabeli %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + Wszystkie kolumny indeksowane przez indeks %1 już nie istniejÄ…. Indeks ten nie bÄ™dzie odtworzony po modyfikacji tabeli. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + WystÄ…piÅ‚ problem z poprawnym przetworzeniem wyzwalacza %1. Może on zostać zaktualizowany tylko częściowo i bÄ™dzie wymagaÅ‚ twojej uwagi. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + Wszystkie kolumny obsÅ‚ugiwane przez wyzwalacz %1 już nie istniejÄ…. Wyzwalacz ten nie bÄ™dzie odtworzony po modyfikacji tabeli. + + + + Cannot not update trigger %1 according to table %2 modification. + Nie można zaktualizować wyzwalacza %1 zgodnie z modyfikacjami tabeli %2. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Nie można zaktualizować widoku %1 w zwiÄ…zku z modyfikacjami tabeli %2. +Widok pozostanie nienaruszony. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + Jest problem ze zaktualizowaniem zapytania %1 w wyzwalaczu %2. Jedeno z podzapytaÅ„ %1, które może odwoÅ‚ywać siÄ™ do tabeli %3 nie może być poprawnie zmodyfikowane. RÄ™czna aktualizacja tego wyzwalacza może być niezbÄ™dna. + + + + Could not parse DDL of the view to be created. Details: %1 + Nie udaÅ‚o siÄ™ przeanalizować DDL widoku do stworzenia. Szczegóły: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Przeanalizowane zapytanie to nie CREATE VIEW, ale: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio nie byÅ‚o w stanie okreÅ›lić kolumn zwracanych przez nowy widok, w zwiÄ…zku z czym nie może okreÅ›lić które wyzwalacze mogÄ… siÄ™ nie powieść podczas procesu odtwarzania. + + + + QueryExecutor + + + Execution interrupted. + Wykonywanie przerwane. + + + + Database is not open. + Baza danych nie jest otwarta. + + + + Only one query can be executed simultaneously. + Tylko jedno zapytanie może być wykonywane w danym momencie. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + WystÄ…piÅ‚ błąd podczas wykonywania zapytania count(*), przez co stronicowanie danych bÄ™dzie wyłączone. Szczegóły błędy z bazy danych: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio nie mogÅ‚o uzyskać metadanych z zapytania. Nie bÄ™dzie można edytować wyników zapytania. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + Brak dostÄ™pnej bazy danych w bieżącym kontekÅ›cie, podczas wywoÅ‚ywania komendy JavaScript: %1. + + + + Error from %1: %2 + Błąd z %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Nie można wykonać SQLa, ponieważ aplikacja nie mogÅ‚a rozpocząć transakcji: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Wykonywanie z pliku przerwane. Jakiekolwiek wykonane zapytania zostaÅ‚y wycofane. + + + + Could not open file '%1' for reading: %2 + Nie można otworzyż pliku '%1' do odczytu: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Nie można wykonać SQLa, ponieważ aplikacja nie mogÅ‚a zatwierdzić transakcji: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + ZakoÅ„czono wykonywanie %1 zapytaÅ„ w %2 sekund(y). %3 nie zostaÅ‚ wykonany z powodu błędów. + + + + Finished executing %1 queries in %2 seconds. + ZakoÅ„czono wykonywanie %1 zapytaÅ„ w %2 sekund(y). + + + + Could not execute SQL due to error. + Nie można wykonać SQL z powodu błędu. + + + + SqlHistoryModel + + + Database + sql history header + Baza danych + + + + Execution date + sql history header + Data wykonania + + + + Time spent + sql history header + Czas trwania + + + + Rows affected + sql history header + Liczba wierszy + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Nie można sprawdzić aktualizacji (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_BR.qm b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_BR.qm deleted file mode 100644 index 95d8136..0000000 Binary files a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_BR.qm and /dev/null differ diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_BR.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_BR.ts index 8f3404e..78c2853 100644 --- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_BR.ts +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_BR.ts @@ -1,1153 +1,1101 @@ - - + + AbstractDb - - - Cannot execute query on closed database. - Não é possível executar query em banco de dados fechado. + + + Cannot execute query on closed database. + Não é possível executar a consulta com o banco de dados fechado. - - Error attaching database %1: %2 - + + Error attaching database %1: %2 + Erro ao anexar banco de dados %1: %2 - - - BugReporter - Invalid login or password - login ou senha inválido + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Falha ao fazer checkpoint WAL cheio no banco de dados '%1'. Erro retornado do mecanismo SQLite: %2 - - + + ChainExecutor - - The database for executing queries was not defined. - chain executor - Não foi especificado banco de dados para execução das consultas. + + The database for executing queries was not defined. + chain executor + Não foi especificado banco de dados para execução das consultas. - - The database for executing queries was not open. - chain executor - O banco de dados para execução das consultas não foi aberto. + + The database for executing queries was not open. + chain executor + O banco de dados para execução das consultas não foi aberto. - - Could not disable foreign keys in the database. Details: %1 - chain executor - Não foi possível desativar as chaves estrangeiras do banco de dados. Detalhes: %1 + + Could not disable foreign keys in the database. Details: %1 + chain executor + Não foi possível desativar as chaves estrangeiras do banco de dados. Detalhes: %1 - - Could not start a database transaction. Details: %1 - chain executor - Não foi possível iniciar a transação do banco de dados. Detalhes: %1 + + Could not start a database transaction. Details: %1 + chain executor + Não foi possível iniciar a transação do banco de dados. Detalhes: %1 - - Interrupted - chain executor - Interrompido + + Interrupted + chain executor + Interrompido - - Could not commit a database transaction. Details: %1 - chain executor - Não foi possível efetuar commit no banco de dados. Detalhes: %1 + + Could not commit a database transaction. Details: %1 + chain executor + Não foi possível efetuar commit no banco de dados. Detalhes: %1 - - + + CompletionHelper - - New row reference - + + New row reference + Nova referência de linha - - Old row reference - + + Old row reference + Referência de linha antiga - - New table name - + + New table name + Nome da nova tabela - - New index name - + + New index name + Nome do novo índice - - New view name - + + New view name + Nome da nova visualização - - New trigger name - + + New trigger name + Nome da nova trigger - - Table or column alias - + + Table or column alias + Apelido da tabela ou coluna - - transaction name - + + transaction name + nome da transação - - New column name - + + New column name + Nome da nova coluna - - Column data type - + + Column data type + Tipo de dados de coluna - - Constraint name - + + Constraint name + Nome da constraint - - Error message - + + Error message + Mensagem de Erro - - Collation name - + + Any word + Qualquer palavra - - Any word - + + Default database + Banco de dados padrão - - Default database - + + Temporary objects database + Banco de dados de objetos temporários - - - Temporary objects database - - - - + + ConfigImpl - - Could not start database transaction for deleting SQL history, therefore it's not deleted. - + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Não foi possível iniciar a transação do banco de dados para excluir o histórico do SQL. Portanto, ela não será excluída. - - Could not commit database transaction for deleting SQL history, therefore it's not deleted. - + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Não foi possível submeter a transação do banco de dados para excluir o histórico do SQL. Portanto, não será excluída. - - + + DbManagerImpl - - Could not add database %1: %2 - + + Could not add database %1: %2 + Não foi possível adicionar o banco de dados %1: %2 - - Database %1 could not be updated, because of an error: %2 - + + Database %1 could not be updated, because of an error: %2 + Banco de dados %1 não pode ser atualizado, devido a um erro: %2 - - - Database file doesn't exist. - + + + Database file doesn't exist. + Arquivo de banco de dados não existe. - - - - No supporting plugin loaded. - + + + + No supporting plugin loaded. + Nenhum plugin de suporte carregado. - - Database could not be initialized. - + + Database could not be initialized. + Banco de dados não pode ser inicializado. - - No suitable database driver plugin found. - + + No suitable database driver plugin found. + Nenhum plugin de driver de base de dados adequado encontrado. - - + + DbObjectOrganizer - - - Error while creating table in target database: %1 - Erro ao criar tabela no banco de dados: %1 + + + Error while creating table in target database: %1 + Erro ao criar tabela no banco de dados: %1 - - Could not parse table. - Não foi possível analisar a tabela. + + Could not parse table. + Não foi possível analisar a tabela. - - Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. - Banco de dados %1 não pôde ser ligado ao banco de dados %2, de modo que os dados da tabela %3 vão ser copiados com o SQLiteStudio como um mediador. Este metodo pode ser lento para grande tabelas, por favor seja paciente. + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Banco de dados %1 não pôde ser ligado ao banco de dados %2, de modo que os dados da tabela %3 vão ser copiados com o SQLiteStudio como um mediador. Este método pode ser lento para grande tabelas, por favor seja paciente. - - Error while copying data for table %1: %2 - Erro ao copiar data para tabela %1:%2 + + Error while copying data for table %1: %2 + Erro ao copiar dados da tabela %1:%2 - - - - Error while copying data to table %1: %2 - Erro ao copiar data para tabela %1:%2 + + + + Error while copying data to table %1: %2 + Erro ao copiar dados para a tabela %1:%2 - - Error while dropping source view %1: %2 + + Error while dropping source view %1: %2 Tables, indexes, triggers and views copied to database %3 will remain. - - - - - Error while creating view in target database: %1 - - - - - Error while creating index in target database: %1 - - - - - Error while creating trigger in target database: %1 - + Erro ao soltar a visão da fonte %1: %2 +Tabelas, índices, trigger e visualizações copiadas para o banco de dados %3 permanecerão. - - - - Could not parse object '%1' in order to move or copy it. - + + Error while creating view in target database: %1 + Erro ao criar a visualização no banco de dados destino: %1 - - - DbVersionConverter - - Target file exists, but could not be overwritten. - + + Error while creating index in target database: %1 + Erro ao criar o índice no banco de dados destino: %1 - - Could not find proper database plugin to create target database. - + + Error while creating trigger in target database: %1 + Erro ao criar gatilho no banco de dados de destino: %1 - - Error while converting database: %1 - + + + + Could not parse object '%1' in order to move or copy it. + Não foi possível analisar o objeto '%1' para mover ou copiar. - - + + DdlHistoryModel - - Database name - ddl history header - + + Database name + ddl history header + Nome do banco de dados - - Database file - ddl history header - + + Database file + ddl history header + Arquivo de banco de dados - - Date of execution - ddl history header - + + Date of execution + ddl history header + Data de execução - - Changes - ddl history header - + + Changes + ddl history header + Alterações - - + + ExportManager - - Export plugin %1 doesn't support exporing query results. - + + Export plugin %1 doesn't support exporing query results. + Plugin de exportação %1 não suporta resultados de consulta suprimindo. - - Export plugin %1 doesn't support exporing tables. - + + Export plugin %1 doesn't support exporing tables. + O plugin %1 de exportação não suporta'tabelas de exportação. - - Export plugin %1 doesn't support exporing databases. - + + Export plugin %1 doesn't support exporing databases. + Plugin de exportação %1 não suporta resultados de consulta suprimindo. - - Export format '%1' is not supported. Supported formats are: %2. - + + Export format '%1' is not supported. Supported formats are: %2. + Formato de exportação '%1' não é suportado. Formatos suportados são: %2. - - Export to the clipboard was successful. - + + Export to the clipboard was successful. + Exportação para área de transferência com sucesso. - - Export to the file '%1' was successful. - + + Export to the file '%1' was successful. + Exportação para o arquivo '%1' foi bem sucedida. - - Export was successful. - + + Export was successful. + Exportação bem-sucedida. - - Could not export to file %1. File cannot be open for writting. - + + Could not export to file %1. File cannot be open for writting. + Não foi possível exportar para o arquivo %1. O arquivo não pode ser aberto para escrita. - - + + ExportWorker - - Error while exporting query results: %1 - + + Error while exporting query results: %1 + Erro ao exportar os resultados da consulta: %1 - - Error while counting data column width to export from query results: %1 - + + Error while counting data column width to export from query results: %1 + Erro ao contar a largura da coluna de dados para exportar dos resultados da consulta: %1 - - - Could not parse %1 in order to export it. It will be excluded from the export output. - + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Não foi possível analisar %1 para exportá-lo. Ele será excluído da saída de exportação. - - Error while reading data to export from table %1: %2 - + + Error while reading data to export from table %1: %2 + Erro ao ler os dados para exportar da tabela %1: %2 - - Error while counting data to export from table %1: %2 - + + Error while counting data to export from table %1: %2 + Erro ao contar os dados a serem exportados da tabela %1: %2 - - Error while counting data column width to export from table %1: %2 - + + Error while counting data column width to export from table %1: %2 + Erro ao contar a largura da coluna de dados para exportar da tabela %1: %2 - - + + FunctionManagerImpl - - Invalid number of arguments to function '%1'. Expected %2, but got %3. - + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Número inválido de argumentos para a função '%1'. Esperado %2, mas tem %3. - - No such function registered in SQLiteStudio: %1(%2) - + + No such function registered in SQLiteStudio: %1(%2) + Nenhuma função registrada no SQLiteStudio: %1(%2) - - Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. - + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + A função %1(%2) foi registrada no idioma %3, mas o plugin que suporta essa linguagem não está atualmente carregado. - - Invalid regular expression pattern: %1 - + + Invalid regular expression pattern: %1 + Expressão regular inválida: %1 - - - Could not open file %1 for reading: %2 - + + + Could not open file %1 for reading: %2 + Não foi possível abrir o arquivo %1 para leitura: %2 - - Could not open file %1 for writting: %2 - + + Could not open file %1 for writting: %2 + Não foi possível abrir o arquivo %1 para escrita: %2 - - Error while writting to file %1: %2 - + + Error while writting to file %1: %2 + Erro ao gravar o arquivo %1: %2 - - Unsupported scripting language: %1 - + + Unsupported scripting language: %1 + Idioma do script não suportado: %1 - - + + GenericExportPlugin - - Could not initialize text codec for exporting. Using default codec: %1 - + + Could not initialize text codec for exporting. Using default codec: %1 + Não foi possível inicializar o codec de texto para exportação. Usando o codec padrão: %1 - - + + ImportManager - - Imported data to the table '%1' successfully. - + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Dados importados para a tabela '%1' com sucesso. Número de linhas importadas: %2 - - + + ImportWorker - - No columns provided by the import plugin. - + + No columns provided by the import plugin. + Nenhuma coluna fornecida pelo plugin de importação. - - Could not start transaction in order to import a data: %1 - + + Could not start transaction in order to import a data: %1 + Não foi possível iniciar a transação para importar os dados: %1 - - Could not commit transaction for imported data: %1 - + + Could not commit transaction for imported data: %1 + Não foi possível submeter a transação para dados importados: %1 - - Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. - + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Tabela '%1' tem menos colunas que há nos dados a serem importados. Colunas de dados excessivas serão ignoradas. - - Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. - + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Tabela '%1' tem mais colunas que há colunas nos dados a serem importados. Algumas colunas na tabela serão deixadas vazias. - - Could not create table to import to: %1 - + + Could not create table to import to: %1 + Não foi possível criar a tabela para importar para: %1 - - - - Error while importing data: %1 - + + + + Error while importing data: %1 + Erro ao importar dados: %1 - - - Interrupted. - import process status update - + + + Interrupted. + import process status update + Interrompido. - - Could not import data row number %1. The row was ignored. Problem details: %2 - + + Could not import data row number %1. The row was ignored. Problem details: %2 + Não foi possível importar a linha de dados número %1. A linha foi ignorada. Detalhes do problema: %2 - - + + PluginManagerImpl - - Cannot load plugin %1, because it's in conflict with plugin %2. - + + Cannot load plugin %1, because it's in conflict with plugin %2. + Não foi possível carregar o plugin %1, porque está em conflito com o plugin %2. - - Cannot load plugin %1, because its dependency was not loaded: %2. - + + Cannot load plugin %1, because its dependency was not loaded: %2. + Não foi possível carregar o plugin %1, porque sua dependência não foi carregada: %2. - - Cannot load plugin %1. Error details: %2 - + + Cannot load plugin %1. Error details: %2 + Não foi possível carregar o plugin %1. Detalhes do erro: %2 - - Cannot load plugin %1 (error while initializing plugin). - + + Cannot load plugin %1 (error while initializing plugin). + Não é possível carregar o plugin %1 (erro durante a inicialização do plugin). - - min: %1 - plugin dependency version - + + min: %1 + plugin dependency version + min: %1 - - max: %1 - plugin dependency version - + + max: %1 + plugin dependency version + max: %1 - - + + PopulateConstant - - Constant - populate constant plugin name - + + Constant + populate constant plugin name + Constante - - + + PopulateConstantConfig - - Constant value: - + + Constant value: + Valor constante: - - + + PopulateDictionary - - Dictionary - dictionary populating plugin name - + + Dictionary + dictionary populating plugin name + Dicionário - - + + PopulateDictionaryConfig - - Dictionary file - + + Dictionary file + Arquivo de dicionário - - Pick dictionary file - + + Pick dictionary file + Escolher arquivo do dicionário - - Word separator - + + Word separator + Separador de palavras - - Whitespace - + + Whitespace + Espaço em branco - - Line break - + + Line break + Quebra de linha - - Method of using words - + + Method of using words + Método de uso palavras - - Ordered - + + Ordered + Ordenado - - Randomly - + + Randomly + Aleatoriamente - - + + PopulateManager - - Table '%1' populated successfully. - + + Table '%1' populated successfully. + Tabela '%1' preenchida com sucesso. - - + + PopulateRandom - - Random number - + + Random number + Número aleatório - - + + PopulateRandomConfig - - Constant prefix - + + Constant prefix + Prefixo da constante - - No prefix - + + No prefix + Nenhum prefixo - - Minimum value - + + Minimum value + Valor mínimo - - Maximum value - + + Maximum value + Valor máximo - - Constant suffix - + + Constant suffix + Sufixo da constante - - No suffix - + + No suffix + Nenhum sufixo - - + + PopulateRandomText - - Random text - + + Random text + Texto aleatório - - + + PopulateRandomTextConfig - - Use characters from common sets: - + + Use characters from common sets: + Usar caracteres de conjuntos comuns: - - Minimum length - + + Minimum length + Comprimento mínimo - - Letters from a to z. - + + Letters from a to z. + Letras de a a z. - - Alpha - + + Alpha + Alfa - - Numbers from 0 to 9. - + + Numbers from 0 to 9. + Números de 0 a 9. - - Numeric - + + Numeric + Numérico - - A whitespace, a tab and a new line character. - + + A whitespace, a tab and a new line character. + Um espaço em branco, uma aba e um novo caractere de linha. - - Whitespace - + + Whitespace + Espaço em branco - - Includes all above and all others. - + + Includes all above and all others. + Inclui todos acima e todos os outros. - - Binary - + + Binary + Binário - - Use characters from my custom set: - + + Use characters from my custom set: + Usar caracteres do meu grupo personalizado: - - Maximum length - + + Maximum length + Comprimento máximo - - If you type some character multiple times, it's more likely to be used. - + + If you type some character multiple times, it's more likely to be used. + Se você digitar um caractere várias vezes, é mais provável que seja usado. - - + + PopulateScript - - Script - + + Script + Script - - + + PopulateScriptConfig - - Initialization code (optional) - + + Initialization code (optional) + Código de inicialização (opcional) - - Per step code - + + Per step code + Código detalhado - - Language - + + Language + Idioma - - Help - + + Help + Ajuda - - + + PopulateSequence - - Sequence - + + Sequence + Sequência - - + + PopulateSequenceConfig - - Start value: - + + Start value: + Valor inicial: - - Step: - + + Step: + Etapas: - - + + PopulateWorker - - Could not start transaction in order to perform table populating. Error details: %1 - + + Could not start transaction in order to perform table populating. Error details: %1 + Não foi possível iniciar a transação para realizar o preenchimento da tabela. Detalhes do erro: %1 - - Error while populating table: %1 - + + Error while populating table: %1 + Erro ao preencher a tabela: %1 - - Could not commit transaction after table populating. Error details: %1 - + + Could not commit transaction after table populating. Error details: %1 + Não foi possível submeter a transação após o preenchimento da tabela. Detalhes de erro: %1 - - + + QObject - - - Could not open database: %1 - + + Could not open file '%1' for reading: %2 + Não foi possível abrir o arquivo '%1' para leitura: %2 - - - Result set expired or no row available. - + + Could not open database: %1 + Não foi possível abrir o banco de dados: %1 - - - Could not load extension %1: %2 - + + Result set expired or no row available. + Conjunto de resultados expirado ou nenhuma linha disponível. - - Could not close database: %1 - + + + Could not load extension %1: %2 + Não foi possível carregar a extensão %1: %2 - - - - - - - - SQLite %1 does not support '%2' statement. - + + Could not run WAL checkpoint: %1 + Não foi possível executar o ponto de verificação do WAL: %1 - - SQLite %1 does not support '%2' statement, but the regular table can be created instead if you proceed. - + + Could not close database: %1 + Não foi possível fechar o banco de dados: %1 - - Could not parse statement: %1 -Error details: %2 - + + Could not attach database %1: %2 + Não foi possível anexar o banco de dados %1: %2 - - - - - SQLite %1 does not support the '%2' clause. Cannot convert '%3' statement with that clause. - + + + Incomplete query. + Consulta incompleta. - - SQLite %1 does not support the '%2' clause in the '%3' statement. - + + Parser stack overflow + Parser stack overflow (estourado) - - SQLite %1 does not support current date or time clauses in expressions. - + + Syntax error + Erro de sintaxe - - SQLite %1 does not support row value clauses in expressions. - + + Could not open dictionary file %1 for reading. + Não foi possível abrir o arquivo de dicionário %1 para leitura. - - - - SQLite %1 does not support '%2' clause in expressions. - + + Dictionary file must exist and be readable. + Arquivo de dicionário deve existir e estar legível. - - Could not attach database %1: %2 - + + Maximum value cannot be less than minimum value. + O valor máximo não pode ser menor que o valor mínimo. - - - Incomplete query. - + + Maximum length cannot be less than minimum length. + O comprimento máximo não pode ser inferior ao comprimento mínimo. - - - Parser stack overflow - + + Custom character set cannot be empty. + O conjunto de caracteres personalizado não pode estar vazio. - - - Syntax error - + + Could not find plugin to support scripting language: %1 + Não foi possível encontrar o plugin para suportar o idioma do script: %1 - - Could not open dictionary file %1 for reading. - + + Error while executing populating initial code: %1 + Erro ao executar o preenchimento do código inicial: %1 - - Dictionary file must exist and be readable. - + + Error while executing populating code: %1 + Erro ao executar o código de execução: %1 - - Maximum value cannot be less than minimum value. - + + Select implementation language. + Selecionar idioma de implementação. - - Maximum length cannot be less than minimum length. - + + Implementation code cannot be empty. + Código de implementação não pode ser vazio. - - Custom character set cannot be empty. - + + Could not resolve data source for column: %1 + Não foi possível resolver a fonte de dados para a coluna: %1 - - Could not find plugin to support scripting language: %1 - + + Could not resolve table for column '%1'. + Não foi possível resolver a tabela para a coluna '%1'. - - Error while executing populating initial code: %1 - + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Não foi possível inicializar o arquivo de configuração. Quaisquer alterações de configuração e histórico de consultas serão perdidos após a reinicialização do aplicativo. Não foi possível criar um arquivo nos seguintes locais: %1. - - Error while executing populating code: %1 - + + General purpose + plugin category name + Objetivo geral - - Select implementation language. - + + Database support + plugin category name + Suporte do banco de dados - - Implementation code cannot be empty. - + + Code formatter + plugin category name + Formatador de código - - Could not resolve data source for column: %1 - + + Scripting languages + plugin category name + Linguagens dos scripts - - Could not resolve table for column '%1'. - + + Exporting + plugin category name + Exportando - - Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Tried to initialize the file at following localizations: %1. - + + Importing + plugin category name + Importando - - General purpose - plugin category name - + + Table populating + plugin category name + Preencher a tabela - - Database support - plugin category name - + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Tabela %1 é tabela de referência %2, mas a definição de chave estrangeira não será atualizada para uma nova definição de tabela devido a problemas ao analisar DDL da tabela %3. - - Code formatter - plugin category name - + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + Todas as colunas indexadas pelo índice %1 desapareceram. O índice não será recriado após a modificação da tabela. - - Scripting languages - plugin category name - + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + Há um problema com a trigger %1. Ela pode não ser totalmente atualizada e precisará de sua atenção. - - Exporting - plugin category name - + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + Todas as colunas cobertas pela trigger %1 desapareceram. A trigger não será recriada após a modificação da tabela. - - Importing - plugin category name - + + Cannot not update trigger %1 according to table %2 modification. + Não é possível atualizar trigger %1 de acordo com modificação da tabela %2. - - Table populating - plugin category name - + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Não é possível atualizar a exibição %1 de acordo com as modificações da tabela %2 . +A visualização permanecerá como é. - - Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. - + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + Ocorreu um problema ao atualizar uma instrução %1 dentro do gatilho %2 . Uma das %1 substâncias que poderiam referir-se à tabela %3 não pode ser devidamente modificada. A atualização manual da trigger pode ser necessária. - - All columns indexed by the index %1 are gone. The index will not be recreated after table modification. - + + Could not parse DDL of the view to be created. Details: %1 + Não foi possível analisar DDL da view a ser criada. Detalhes: %1 - - There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. - + + Parsed query is not CREATE VIEW. It's: %1 + A consulta analisada não é CREATE VIEW. É: %1 - - Cannot not update trigger %1 according to table %2 modification. - + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + O SQLiteStudio não conseguiu resolver colunas retornadas pela nova visualização Portanto, ele não é capaz de dizer quais trigger podem falhar durante o processo de recriação. + + + QueryExecutor - - - - There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - + + Execution interrupted. + Execução interrompida. - - All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. - + + Database is not open. + Banco de dados não está aberto. - - Cannot not update view %1 according to table %2 modifications. -The view will remain as it is. - + + Only one query can be executed simultaneously. + Apenas uma consulta pode ser executada simultaneamente. - - Could not parse DDL of the view to be created. Details: %1 - + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + Ocorreu um erro ao executar a função count(*), desta forma a paginação de dados será desabilitada. Detalhes de erro do banco de dados: %1 - - Parsed query is not CREATE VIEW. It's: %1 - + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio não pôde extrair os metadados da consulta. Os resultados obtidos não serão editáveis. + + + ScriptingQtDbProxy - - SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. - + + No database available in current context, while called JavaScript's %1 command. + Nenhum banco de dados disponível, enquanto rodando JavaScript %1. - - Could not open file '%1' for reading: %2 - + + Error from %1: %2 + Erro de %1: %2 - - - QueryExecutor + + + SqlFileExecutor - - Execution interrupted. - + + Could not execute SQL, because application has failed to start transaction: %1 + Não foi possível executar SQL, porque a aplicação falhou ao iniciar a transação: %1 - - Database is not open. - + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execução do arquivo cancelada. Quaisquer consultas executadas até agora foram desfeitas. - - Only one query can be executed simultaneously. - + + Could not open file '%1' for reading: %2 + Não foi possível abrir o arquivo '%1' para leitura: %2 - - - An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 - + + Could not execute SQL, because application has failed to commit the transaction: %1 + Não foi possível executar SQL, porque o aplicativo falhou ao confirmar a transação: %1 - - SQLiteStudio was unable to extract metadata from the query. Results won't be editable. - + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Concluiu a execução de consultas %1 em %2 segundos. %3 não executado devido a erros. - - - ScriptingQtDbProxy - - No database available in current context, while called QtScript's %1 command. - + + Finished executing %1 queries in %2 seconds. + Terminou a consulta %1 em %2 segundos. - - Error from %1: %2 - + + Could not execute SQL due to error. + Não foi possível executar SQL devido a um erro. - - + + SqlHistoryModel - - Database - sql history header - + + Database + sql history header + Banco de dados - - Execution date - sql history header - + + Execution date + sql history header + Data de execução - - Time spent - sql history header - + + Time spent + sql history header + Tempo gasto - - Rows affected - sql history header - + + Rows affected + sql history header + Linhas afetadas - - SQL - sql history header - + + SQL + sql history header + SQL - - + + UpdateManager - - Updates installer executable is missing. - - - - - - Unable to check for updates (%1) - - - - - details are unknown - - - - - Unable to run updater application (%1). Please report this. - + + Could not check for updates (%1). + Não foi possível verificar se há atualizações (%1). - + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_PT.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_PT.ts new file mode 100644 index 0000000..bdc50f6 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_pt_PT.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ro_RO.qm b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ro_RO.qm and /dev/null differ diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ro_RO.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ro_RO.ts index 744cb0c..5a793f5 100644 --- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ro_RO.ts +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ro_RO.ts @@ -1,1146 +1,1101 @@ - - + + AbstractDb - - - Cannot execute query on closed database. - + + + Cannot execute query on closed database. + Cannot execute query on closed database. - - Error attaching database %1: %2 - + + Error attaching database %1: %2 + Error attaching database %1: %2 - - - ChainExecutor - - The database for executing queries was not defined. - chain executor - + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + ChainExecutor - - The database for executing queries was not open. - chain executor - + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. - - Could not disable foreign keys in the database. Details: %1 - chain executor - + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. - - Could not start a database transaction. Details: %1 - chain executor - + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 - - Interrupted - chain executor - + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 - - Could not commit a database transaction. Details: %1 - chain executor - + + Interrupted + chain executor + Interrupted - - - CompletionHelper - - New row reference - + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + CompletionHelper - - Old row reference - + + New row reference + New row reference - - New table name - + + Old row reference + Old row reference - - New index name - + + New table name + New table name - - New view name - + + New index name + New index name - - New trigger name - + + New view name + New view name - - Table or column alias - + + New trigger name + New trigger name - - transaction name - + + Table or column alias + Table or column alias - - New column name - + + transaction name + transaction name - - Column data type - + + New column name + New column name - - Constraint name - + + Column data type + Column data type - - Error message - + + Constraint name + Constraint name - - Collation name - + + Error message + Error message - - Any word - + + Any word + Any word - - Default database - + + Default database + Default database - - Temporary objects database - + + Temporary objects database + Temporary objects database - - + + ConfigImpl - - Could not start database transaction for deleting SQL history, therefore it's not deleted. - + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. - - Could not commit database transaction for deleting SQL history, therefore it's not deleted. - + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. - - + + DbManagerImpl - - Could not add database %1: %2 - + + Could not add database %1: %2 + Could not add database %1: %2 - - Database %1 could not be updated, because of an error: %2 - + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 - - - Database file doesn't exist. - + + + Database file doesn't exist. + Database file doesn't exist. - - - - No supporting plugin loaded. - + + + + No supporting plugin loaded. + No supporting plugin loaded. - - Database could not be initialized. - + + Database could not be initialized. + Database could not be initialized. - - No suitable database driver plugin found. - + + No suitable database driver plugin found. + No suitable database driver plugin found. - - + + DbObjectOrganizer - - - Error while creating table in target database: %1 - + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 - - Could not parse table. - + + Could not parse table. + Could not parse table. - - Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. - + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. - - Error while copying data for table %1: %2 - + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 - - - - Error while copying data to table %1: %2 - + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 - - Error while dropping source view %1: %2 + + Error while dropping source view %1: %2 Tables, indexes, triggers and views copied to database %3 will remain. - - - - - Error while creating view in target database: %1 - + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. - - Error while creating index in target database: %1 - + + Error while creating view in target database: %1 + Error while creating view in target database: %1 - - Error while creating trigger in target database: %1 - + + Error while creating index in target database: %1 + Error while creating index in target database: %1 - - - - Could not parse object '%1' in order to move or copy it. - + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 - - - DbVersionConverter - - Target file exists, but could not be overwritten. - + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. - - - Could not find proper database plugin to create target database. - - - - - Error while converting database: %1 - - - - + + DdlHistoryModel - - Database name - ddl history header - + + Database name + ddl history header + Database name - - Database file - ddl history header - + + Database file + ddl history header + Database file - - Date of execution - ddl history header - + + Date of execution + ddl history header + Date of execution - - Changes - ddl history header - + + Changes + ddl history header + Changes - - + + ExportManager - - Export plugin %1 doesn't support exporing query results. - + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. - - Export plugin %1 doesn't support exporing tables. - + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. - - Export plugin %1 doesn't support exporing databases. - + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. - - Export format '%1' is not supported. Supported formats are: %2. - + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. - - Export to the clipboard was successful. - + + Export to the clipboard was successful. + Export to the clipboard was successful. - - Export to the file '%1' was successful. - + + Export to the file '%1' was successful. + Export to the file '%1' was successful. - - Export was successful. - + + Export was successful. + Export was successful. - - Could not export to file %1. File cannot be open for writting. - + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. - - + + ExportWorker - - Error while exporting query results: %1 - + + Error while exporting query results: %1 + Error while exporting query results: %1 - - Error while counting data column width to export from query results: %1 - + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 - - - Could not parse %1 in order to export it. It will be excluded from the export output. - + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. - - Error while reading data to export from table %1: %2 - + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 - - Error while counting data to export from table %1: %2 - + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 - - Error while counting data column width to export from table %1: %2 - + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 - - + + FunctionManagerImpl - - Invalid number of arguments to function '%1'. Expected %2, but got %3. - + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. - - No such function registered in SQLiteStudio: %1(%2) - + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) - - Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. - + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. - - Invalid regular expression pattern: %1 - + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 - - - Could not open file %1 for reading: %2 - + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 - - Could not open file %1 for writting: %2 - + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 - - Error while writting to file %1: %2 - + + Error while writting to file %1: %2 + Error while writting to file %1: %2 - - Unsupported scripting language: %1 - + + Unsupported scripting language: %1 + Unsupported scripting language: %1 - - + + GenericExportPlugin - - Could not initialize text codec for exporting. Using default codec: %1 - + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 - - + + ImportManager - - Imported data to the table '%1' successfully. - + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 - - + + ImportWorker - - No columns provided by the import plugin. - + + No columns provided by the import plugin. + No columns provided by the import plugin. - - Could not start transaction in order to import a data: %1 - + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 - - Could not commit transaction for imported data: %1 - + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 - - Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. - + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. - - Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. - + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. - - Could not create table to import to: %1 - + + Could not create table to import to: %1 + Could not create table to import to: %1 - - - - Error while importing data: %1 - + + + + Error while importing data: %1 + Error while importing data: %1 - - - Interrupted. - import process status update - + + + Interrupted. + import process status update + Interrupted. - - Could not import data row number %1. The row was ignored. Problem details: %2 - + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 - - + + PluginManagerImpl - - Cannot load plugin %1, because it's in conflict with plugin %2. - + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. - - Cannot load plugin %1, because its dependency was not loaded: %2. - + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. - - Cannot load plugin %1. Error details: %2 - + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 - - Cannot load plugin %1 (error while initializing plugin). - + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). - - min: %1 - plugin dependency version - + + min: %1 + plugin dependency version + min: %1 - - max: %1 - plugin dependency version - + + max: %1 + plugin dependency version + max: %1 - - + + PopulateConstant - - Constant - populate constant plugin name - + + Constant + populate constant plugin name + Constant - - + + PopulateConstantConfig - - Constant value: - + + Constant value: + Constant value: - - + + PopulateDictionary - - Dictionary - dictionary populating plugin name - + + Dictionary + dictionary populating plugin name + Dictionary - - + + PopulateDictionaryConfig - - Dictionary file - + + Dictionary file + Dictionary file - - Pick dictionary file - + + Pick dictionary file + Pick dictionary file - - Word separator - + + Word separator + Word separator - - Whitespace - + + Whitespace + Whitespace - - Line break - + + Line break + Line break - - Method of using words - + + Method of using words + Method of using words - - Ordered - + + Ordered + Ordered - - Randomly - + + Randomly + Randomly - - + + PopulateManager - - Table '%1' populated successfully. - + + Table '%1' populated successfully. + Table '%1' populated successfully. - - + + PopulateRandom - - Random number - + + Random number + Random number - - + + PopulateRandomConfig - - Constant prefix - + + Constant prefix + Constant prefix - - No prefix - + + No prefix + No prefix - - Minimum value - + + Minimum value + Minimum value - - Maximum value - + + Maximum value + Maximum value - - Constant suffix - + + Constant suffix + Constant suffix - - No suffix - + + No suffix + No suffix - - + + PopulateRandomText - - Random text - + + Random text + Random text - - + + PopulateRandomTextConfig - - Use characters from common sets: - + + Use characters from common sets: + Use characters from common sets: - - Minimum length - + + Minimum length + Minimum length - - Letters from a to z. - + + Letters from a to z. + Letters from a to z. - - Alpha - + + Alpha + Alpha - - Numbers from 0 to 9. - + + Numbers from 0 to 9. + Numbers from 0 to 9. - - Numeric - + + Numeric + Numeric - - A whitespace, a tab and a new line character. - + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. - - Whitespace - + + Whitespace + Whitespace - - Includes all above and all others. - + + Includes all above and all others. + Includes all above and all others. - - Binary - + + Binary + Binary - - Use characters from my custom set: - + + Use characters from my custom set: + Use characters from my custom set: - - Maximum length - + + Maximum length + Maximum length - - If you type some character multiple times, it's more likely to be used. - + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. - - + + PopulateScript - - Script - + + Script + Script - - + + PopulateScriptConfig - - Initialization code (optional) - + + Initialization code (optional) + Initialization code (optional) - - Per step code - + + Per step code + Per step code - - Language - + + Language + Language - - Help - + + Help + Help - - + + PopulateSequence - - Sequence - + + Sequence + Sequence - - + + PopulateSequenceConfig - - Start value: - + + Start value: + Start value: - - Step: - + + Step: + Step: - - + + PopulateWorker - - Could not start transaction in order to perform table populating. Error details: %1 - + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 - - Error while populating table: %1 - + + Error while populating table: %1 + Error while populating table: %1 - - Could not commit transaction after table populating. Error details: %1 - + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 - - + + QObject - - - Could not open database: %1 - + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 - - - Result set expired or no row available. - + + Could not open database: %1 + Could not open database: %1 - - - Could not load extension %1: %2 - + + Result set expired or no row available. + Result set expired or no row available. - - Could not close database: %1 - + + + Could not load extension %1: %2 + Could not load extension %1: %2 - - - - - - - - SQLite %1 does not support '%2' statement. - + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 - - SQLite %1 does not support '%2' statement, but the regular table can be created instead if you proceed. - + + Could not close database: %1 + Could not close database: %1 - - Could not parse statement: %1 -Error details: %2 - + + Could not attach database %1: %2 + Could not attach database %1: %2 - - - - - SQLite %1 does not support the '%2' clause. Cannot convert '%3' statement with that clause. - + + + Incomplete query. + Incomplete query. - - SQLite %1 does not support the '%2' clause in the '%3' statement. - + + Parser stack overflow + Parser stack overflow - - SQLite %1 does not support current date or time clauses in expressions. - + + Syntax error + Syntax error - - SQLite %1 does not support row value clauses in expressions. - + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. - - - - SQLite %1 does not support '%2' clause in expressions. - + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. - - Could not attach database %1: %2 - + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. - - - Incomplete query. - + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. - - - Parser stack overflow - + + Custom character set cannot be empty. + Custom character set cannot be empty. - - - Syntax error - + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 - - Could not open dictionary file %1 for reading. - + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 - - Dictionary file must exist and be readable. - + + Error while executing populating code: %1 + Error while executing populating code: %1 - - Maximum value cannot be less than minimum value. - + + Select implementation language. + Select implementation language. - - Maximum length cannot be less than minimum length. - + + Implementation code cannot be empty. + Implementation code cannot be empty. - - Custom character set cannot be empty. - + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 - - Could not find plugin to support scripting language: %1 - + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. - - Error while executing populating initial code: %1 - + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. - - Error while executing populating code: %1 - + + General purpose + plugin category name + General purpose - - Select implementation language. - + + Database support + plugin category name + Database support - - Implementation code cannot be empty. - + + Code formatter + plugin category name + Code formatter - - Could not resolve data source for column: %1 - + + Scripting languages + plugin category name + Scripting languages - - Could not resolve table for column '%1'. - + + Exporting + plugin category name + Exporting - - Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Tried to initialize the file at following localizations: %1. - + + Importing + plugin category name + Importing - - General purpose - plugin category name - + + Table populating + plugin category name + Table populating - - Database support - plugin category name - + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. - - Code formatter - plugin category name - + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. - - Scripting languages - plugin category name - + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. - - Exporting - plugin category name - + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. - - Importing - plugin category name - + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. - - Table populating - plugin category name - + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. - - Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. - + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - - All columns indexed by the index %1 are gone. The index will not be recreated after table modification. - + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 - - There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. - + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 - - All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. - + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + QueryExecutor - - Cannot not update trigger %1 according to table %2 modification. - + + Execution interrupted. + Execution interrupted. - - Cannot not update view %1 according to table %2 modifications. -The view will remain as it is. - + + Database is not open. + Database is not open. - - - - There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. - - Could not parse DDL of the view to be created. Details: %1 - + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 - - Parsed query is not CREATE VIEW. It's: %1 - + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + ScriptingQtDbProxy - - SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. - + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. - - Could not open file '%1' for reading: %2 - + + Error from %1: %2 + Error from %1: %2 - - - QueryExecutor + + + SqlFileExecutor - - Execution interrupted. - + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 - - Database is not open. - + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. - - Only one query can be executed simultaneously. - + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 - - - An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 - + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 - - SQLiteStudio was unable to extract metadata from the query. Results won't be editable. - + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - - - ScriptingQtDbProxy - - No database available in current context, while called QtScript's %1 command. - + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. - - Error from %1: %2 - + + Could not execute SQL due to error. + Could not execute SQL due to error. - - + + SqlHistoryModel - - Database - sql history header - + + Database + sql history header + Database - - Execution date - sql history header - + + Execution date + sql history header + Execution date - - Time spent - sql history header - + + Time spent + sql history header + Time spent - - Rows affected - sql history header - + + Rows affected + sql history header + Rows affected - - SQL - sql history header - + + SQL + sql history header + SQL - - + + UpdateManager - - Updates installer executable is missing. - - - - - - Unable to check for updates (%1) - - - - - details are unknown - - - - - Unable to run updater application (%1). Please report this. - + + Could not check for updates (%1). + Could not check for updates (%1). - + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru.qm b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru.qm deleted file mode 100644 index 55d59ab..0000000 Binary files a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru.qm and /dev/null differ diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru.ts deleted file mode 100644 index 956c7f0..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru.ts +++ /dev/null @@ -1,1318 +0,0 @@ - - - - - AbstractDb - - - - Cannot execute query on closed database. - Ðевозможно выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ñ€Ð¸ закрытой базе данных. - - - - Error attaching database %1: %2 - Ошибка во Ð²Ñ€ÐµÐ¼Ñ Ð¿Ñ€Ð¸ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных %1: %2 - - - - BugReporter - - Invalid login or password - Ðеправильный логин или пароль - - - - ChainExecutor - - - The database for executing queries was not defined. - chain executor - Ðе указана база данных Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов. - - - - The database for executing queries was not open. - chain executor - Ðе открыта база данных Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов. - - - - Could not disable foreign keys in the database. Details: %1 - chain executor - Ðевозможно отключить внешние ключи в базе данных. ПодробноÑти: %1 - - - - Could not start a database transaction. Details: %1 - chain executor - Ðевозможно начать транзакцию. ПодробноÑти: %1 - - - - Interrupted - chain executor - Прервано - - - - Could not commit a database transaction. Details: %1 - chain executor - Ðевозможно завершить транзакцию. ПодробноÑти: %1 - - - - CompletionHelper - - - New row reference - ÐÐ¾Ð²Ð°Ñ ÑÑылка на Ñтроку - - - - Old row reference - Ð¡Ñ‚Ð°Ñ€Ð°Ñ ÑÑылка на Ñтроку - - - - New table name - Ðовое Ð¸Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ - - - - New index name - Ðовое Ð¸Ð¼Ñ Ð¸Ð½Ð´ÐµÐºÑа - - - - New view name - - - - - New trigger name - Ðовое Ð¸Ð¼Ñ Ñ‚Ñ€Ð¸Ð³Ð³ÐµÑ€Ð° - - - - Table or column alias - ПÑевдоним таблицы или Ñтолбца - - - - transaction name - Ð¸Ð¼Ñ Ñ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ð¸ - - - - New column name - Ðовое Ð¸Ð¼Ñ Ñтолбца - - - - Column data type - Тип данных Ñтолбца - - - - Constraint name - Ð˜Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ - - - - Error message - Сообщение об ошибке - - - - Collation name - Ð˜Ð¼Ñ ÑÑ€Ð°Ð²Ð½ÐµÐ½Ð¸Ñ - - - - Any word - Любое Ñлово - - - - Default database - База данных по умолчанию - - - - Temporary objects database - База данных временных объектов - - - - ConfigImpl - - - Could not start database transaction for deleting SQL history, therefore it's not deleted. - Ðевозможно начать транзакцию Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¸Ñтории SQL, поÑтому она не удалена. - - - - Could not commit database transaction for deleting SQL history, therefore it's not deleted. - Ðевозможно завершить транзакцию Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¸Ñтории SQL, поÑтому она не удалена. - - - - DbManagerImpl - - - Could not add database %1: %2 - Ðе удалоÑÑŒ добавить базу данных %1: %2 - - - - Database %1 could not be updated, because of an error: %2 - Ðевозможно обновить базу данных %1 из-за ошибки: %2 - - - - - Database file doesn't exist. - Файл базы данных не ÑущеÑтвует. - - - - - - No supporting plugin loaded. - Unclear error string. Checking the source didn't help. - Модуль поддержки не загружен. - - - - Database could not be initialized. - Ðевозможно инициализировать базу данных. - - - - No suitable database driver plugin found. - Ðе найден подходÑщий драйвер базы данных. - - - - DbObjectOrganizer - - - - Error while creating table in target database: %1 - Ошибка при Ñоздании таблицы в целевой базе данных: %1 - - - - Could not parse table. - Ðевозможно проанализировать Ñтруктуру таблицы. - - - - Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. - Ðевозможно приÑоединить базу данных %1 к базе данных %2, поÑтому данные таблицы %3 будут Ñкопированы при поÑредничеÑтве SQLiteStudio. Этот метод может быть медленным Ð´Ð»Ñ Ð±Ð¾Ð»ÑŒÑˆÐ¸Ñ… таблиц, так что наберитеÑÑŒ терпениÑ. - - - - Error while copying data for table %1: %2 - Ошибка при копировании данных из таблицы %1: %2 - - - - - - Error while copying data to table %1: %2 - Ошибка при копировании данных в таблицу %1: %2 - - - - Error while dropping source view %1: %2 -Tables, indexes, triggers and views copied to database %3 will remain. - Ошибка при удалении иÑходного предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ %1: %2 -Таблицы, индекÑÑ‹, триггеры и предÑтавлениÑ, Ñкопированные в базу данных %3, ÑохранÑÑ‚ÑÑ. - - - - Error while creating view in target database: %1 - Ошибка при Ñоздании предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð² целевой базе данных: %1 - - - - Error while creating index in target database: %1 - Ошибка при Ñоздании индекÑа в целевой базе данных: %1 - - - - Error while creating trigger in target database: %1 - Ошибка при Ñоздании триггера в целевой базе данных: %1 - - - - - - Could not parse object '%1' in order to move or copy it. - Ðевозможно проанализировать объект '%1' Ð´Ð»Ñ ÐµÐ³Ð¾ Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸Ñ Ð»Ð¸Ð±Ð¾ копированиÑ. - - - - DbVersionConverter - - - Target file exists, but could not be overwritten. - Целевой файл ÑущеÑтвует, но не может быть перезапиÑан. - - - - Could not find proper database plugin to create target database. - Ðевозможно найти подходÑщий модуль Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñ†ÐµÐ»ÐµÐ²Ð¾Ð¹ базы данных. - - - - Error while converting database: %1 - Ошибка при конвертации базы данных: %1 - - - - DdlHistoryModel - - - Database name - ddl history header - Ð˜Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных - - - - Database file - ddl history header - Файл базы данных - - - - Date of execution - ddl history header - Дата Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ - - - - Changes - ddl history header - Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ - - - - ExportManager - - - Export plugin %1 doesn't support exporing query results. - Модуль ÑкÑпорта %1 не поддерживает ÑкÑпорт результатов запроÑа. - - - - Export plugin %1 doesn't support exporing tables. - Модуль ÑкÑпорта %1 не поддерживает ÑкÑпорт таблиц. - - - - Export plugin %1 doesn't support exporing databases. - Модуль ÑкÑпорта %1 не поддерживает ÑкÑпорт баз данных. - - - - Export format '%1' is not supported. Supported formats are: %2. - Формат ÑкÑпорта %1 не поддерживаетÑÑ. Поддерживаемые форматы: %2 - - - - Export to the clipboard was successful. - ЭкÑпорт в буфер обмена уÑпешно выполнен. - - - - Export to the file '%1' was successful. - ЭкÑпорт в файл %1 уÑпешно выполнен. - - - - Export was successful. - ЭкÑпорт уÑпешно выполнен. - - - - Could not export to file %1. File cannot be open for writting. - Ðевозможно выполнить ÑкÑпорт в файл %1. Ðе удалоÑÑŒ открыть файл Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи. - - - - ExportWorker - - - Error while exporting query results: %1 - Ошибка при ÑкÑпорте результатов запроÑа: %1 - - - - Error while counting data column width to export from query results: %1 - Ошибка при подÑчёте ширины Ñтолбца данных Ð´Ð»Ñ ÑкÑпорта результатов запроÑа: %1 - - - - - Could not parse %1 in order to export it. It will be excluded from the export output. - Ðевозможно проанализировать Ñтруктуру %1. Данный объект будет иÑключён при выполнении ÑкÑпорта. - - - - Error while reading data to export from table %1: %2 - Ошибка при Ñчитывании данных Ð´Ð»Ñ ÑкÑпорта из таблицы %1: %2 - - - - Error while counting data to export from table %1: %2 - Ошибка при подÑчёте количеÑтва данных Ð´Ð»Ñ ÑкÑпорта из таблицы %1: %2 - - - - Error while counting data column width to export from table %1: %2 - Ошибка при подÑчёте ширины Ñтолбца данных Ð´Ð»Ñ ÑкÑпорта из таблицы %1: %2 - - - - FunctionManagerImpl - - - Invalid number of arguments to function '%1'. Expected %2, but got %3. - Ðеверное количеÑтво аргументов Ð´Ð»Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ '%1'. Ожидаемое количеÑтво: %2, передано: %3. - - - - No such function registered in SQLiteStudio: %1(%2) - Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð½Ðµ зарегиÑтрирована в SQLiteStudio: %1(%2) - - - - Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. - Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ %1(%2) зарегиÑтрирована Ð´Ð»Ñ Ñзыка %3, однако модуль поддержки Ñтого Ñзыка на данный момент не загружен. - - - - Invalid regular expression pattern: %1 - Ðеверный шаблон регулÑрного выражениÑ: %1 - - - - - Could not open file %1 for reading: %2 - Ðевозможно открыть файл %1 Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ: %2 - - - - Could not open file %1 for writting: %2 - Ðевозможно открыть файл %1 Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи: %2 - - - - Error while writting to file %1: %2 - Ошибка при запиÑи в файл %1: %2 - - - - Unsupported scripting language: %1 - Ðеподдерживаемый Ñкриптовый Ñзык: %1 - - - - GenericExportPlugin - - - Could not initialize text codec for exporting. Using default codec: %1 - Ðевозможно инициализировать текÑтовый кодек Ð´Ð»Ñ ÑкÑпорта. ИÑпользуетÑÑ ÐºÐ¾Ð´ÐµÐº по умолчанию: %1 - - - - ImportManager - - - Imported data to the table '%1' successfully. - Импорт данных в таблицу '%1' выполнен уÑпешно. - - - - ImportWorker - - - No columns provided by the import plugin. - Модуль импорта не обнаружил ни одного Ñтолбца. - - - - Could not start transaction in order to import a data: %1 - Ðевозможно начать транзакцию Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° данных: %1 - - - - Could not commit transaction for imported data: %1 - Ðевозможно завершить транзакцию Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ñ‹Ñ… данных: %1 - - - - Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. - Ð’ таблице '%1' Ñтолбцов меньше, чем в импортируемых данных. Лишние Ñтолбцы будут проигнорированы. - - - - Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. - Ð’ таблице '%1' Ñтолбцов больше, чем в импортируемых данных. ÐедоÑтающие Ñтолбцы будут оÑтавлены пуÑтыми. - - - - Could not create table to import to: %1 - Ðевозможно Ñоздать таблицу Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð°: %1 - - - - - - Error while importing data: %1 - Ошибка при импорте данных: %1 - - - - - Interrupted. - import process status update - Прервано. - - - - Could not import data row number %1. The row was ignored. Problem details: %2 - Ðевозможно импортировать Ñтроку данных â„– %1. Строка пропущена. ПодробноÑти проблемы: %2 - - - - PluginManagerImpl - - - Cannot load plugin %1, because it's in conflict with plugin %2. - Ðевозможно загрузить модуль %1, так как он конфликтует Ñ Ð¼Ð¾Ð´ÑƒÐ»ÐµÐ¼ %2. - - - - Cannot load plugin %1, because its dependency was not loaded: %2. - Ðевозможно загрузить модуль %1, так как не загружен необходимый ему модуль: %2. - - - - Cannot load plugin %1. Error details: %2 - Ðевозможно загрузить модуль %1. ПодробноÑти ошибки: %2 - - - - Cannot load plugin %1 (error while initializing plugin). - Ðевозможно загрузить модуль %1 (ошибка при инициализации модулÑ). - - - - min: %1 - plugin dependency version - минимальнаÑ: %1 - - - - max: %1 - plugin dependency version - макÑимальнаÑ: %1 - - - - PopulateConstant - - - Constant - populate constant plugin name - КонÑтанта - - - - PopulateConstantConfig - - - Constant value: - Значение конÑтанты: - - - - PopulateDictionary - - - Dictionary - dictionary populating plugin name - Словарь - - - - PopulateDictionaryConfig - - - Dictionary file - Файл ÑÐ»Ð¾Ð²Ð°Ñ€Ñ - - - - Pick dictionary file - Выберите файл ÑÐ»Ð¾Ð²Ð°Ñ€Ñ - - - - Word separator - Разделитель Ñлов - - - - Whitespace - Пробел - - - - Line break - ÐŸÐµÑ€ÐµÐ½Ð¾Ñ Ñтроки - - - - Method of using words - СпоÑоб иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñлов - - - - Ordered - По порÑдку - - - - Randomly - Случайным образом - - - - PopulateManager - - - Table '%1' populated successfully. - Таблица '%1' уÑпешно заполнена. - - - - PopulateRandom - - - Random number - Случайное чиÑло - - - - PopulateRandomConfig - - - Constant prefix - ÐŸÑ€ÐµÑ„Ð¸ÐºÑ ÐºÐ¾Ð½Ñтанты - - - - No prefix - Без префикÑа - - - - Minimum value - Минимальное значение - - - - Maximum value - МакÑимальное значение - - - - Constant suffix - Ð¡ÑƒÑ„Ñ„Ð¸ÐºÑ ÐºÐ¾Ð½Ñтанты - - - - No suffix - Без ÑуффикÑа - - - - PopulateRandomText - - - Random text - Случайный текÑÑ‚ - - - - PopulateRandomTextConfig - - - Use characters from common sets: - ИÑпользовать Ñимволы из Ñтандартных наборов: - - - - Minimum length - ÐœÐ¸Ð½Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° - - - - Letters from a to z. - Буквы от a до z. - - - - Alpha - Буквенный - - - - Numbers from 0 to 9. - Цифры от 0 до 9. - - - - Numeric - Цифровой - - - - A whitespace, a tab and a new line character. - Пробел, табулÑÑ†Ð¸Ñ Ð¸ Ñимвол переноÑа Ñтроки. - - - - Whitespace - Ðепечатаемые Ñимволы - - - - Includes all above and all others. - Включает вÑе вышеперечиÑленные и вÑе оÑтальные. - - - - Binary - Бинарный - - - - Use characters from my custom set: - ИÑпользовать Ñимволы из моего набора: - - - - Maximum length - МакÑÐ¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° - - - - If you type some character multiple times, it's more likely to be used. - При указании одного Ñимвола неÑколько раз, вероÑтноÑть его иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÑƒÐ²ÐµÐ»Ð¸Ñ‡Ð¸Ð²Ð°ÐµÑ‚ÑÑ. - - - - PopulateScript - - - Script - Скрипт - - - - PopulateScriptConfig - - - Initialization code (optional) - Инициализирующий код (необÑзательно) - - - - Per step code - Код Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ шага - - - - Language - Язык - - - - Help - Помощь - - - - PopulateSequence - - - Sequence - ПоÑледовательноÑть - - - - PopulateSequenceConfig - - - Start value: - Ðачальное значение: - - - - Step: - Шаг: - - - - PopulateWorker - - - Could not start transaction in order to perform table populating. Error details: %1 - Ðевозможно начать транзакцию Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹. ПодробноÑти ошибки: %1 - - - - Error while populating table: %1 - Ошибка при заполнении таблицы: %1 - - - - Could not commit transaction after table populating. Error details: %1 - Ðевозможно завершить транзакцию поÑле Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹. ПодробноÑти ошибки: %1 - - - - QObject - - - - Could not open database: %1 - Ðевозможно открыть базу данных: %1 - - - - - Result set expired or no row available. - Ð ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð¸Ñ€ÑƒÑŽÑ‰Ð°Ñ Ð²Ñ‹Ð±Ð¾Ñ€ÐºÐ° уÑтарела или ни одна Ñтрока не доÑтупна. - - - - - Could not load extension %1: %2 - Ðевозможно загрузить раÑширение %1: %2 - - - - Could not close database: %1 - Ðевозможно закрыть базу данных: %1 - - - - - - - - - - SQLite %1 does not support '%2' statement. - SQLite %1 не поддерживает конÑтрукцию '%2'. - - - - SQLite %1 does not support '%2' statement, but the regular table can be created instead if you proceed. - SQLite %1 не поддерживает конÑтрукцию '%2', однако можно Ñоздать обычную таблицу, еÑли вы продолжите. - - - - Could not parse statement: %1 -Error details: %2 - Ðевозможно проанализировать Ñтруктуру конÑтрукции: %1 ПодробноÑти ошибки: %2 - - - - - - - SQLite %1 does not support the '%2' clause. Cannot convert '%3' statement with that clause. - SQLite %1 не поддерживает оператор '%2'. Ðевозможно Ñконвертировать конÑтрукцию '%3' Ñ Ñтим оператором. - - - - SQLite %1 does not support the '%2' clause in the '%3' statement. - SQLite %1 не поддерживает оператор '%2' в конÑтрукции '%3'. - - - - SQLite %1 does not support current date or time clauses in expressions. - SQLite %1 не поддерживает операторы текущей даты и текущего времени в выражениÑÑ…. - - - - SQLite %1 does not support row value clauses in expressions. - SQLite %1 не поддерживает операции Ñо значениÑми Ñтрок в выражениÑÑ…. - - - - - - SQLite %1 does not support '%2' clause in expressions. - SQLite %1 не поддерживает оператор '%2' в выражениÑÑ…. - - - - Could not attach database %1: %2 - Ðе удалоÑÑŒ приÑоединить базу данных %1: %2 - - - - - Incomplete query. - Ðезавершённый запроÑ. - - - - - Parser stack overflow - Переполнение Ñтека анализатора - - - - - Syntax error - СинтакÑичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° - - - - Could not open dictionary file %1 for reading. - Ðевозможно открыть файл ÑÐ»Ð¾Ð²Ð°Ñ€Ñ %1 Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ. - - - - Dictionary file must exist and be readable. - Файл ÑÐ»Ð¾Ð²Ð°Ñ€Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ ÑущеÑтвовать и быть доÑтупным Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ. - - - - Maximum value cannot be less than minimum value. - МакÑимальное значение не может быть меньше минимального. - - - - Maximum length cannot be less than minimum length. - МакÑÐ¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° не может быть меньше минимальной. - - - - Custom character set cannot be empty. - Произвольный набор Ñимволов не может быть пуÑтым. - - - - Could not find plugin to support scripting language: %1 - Ðевозможно найти модуль поддержки Ñкриптового Ñзыка: %1 - - - - Error while executing populating initial code: %1 - Ошибка при выполнении инициализирующего кода заполнениÑ: %1 - - - - Error while executing populating code: %1 - Ошибка при выполнении кода заполнениÑ: %1 - - - - Select implementation language. - Выберите Ñзык реализации. - - - - Implementation code cannot be empty. - ЗаполнÑющий код не может быть пуÑтым. - - - - Could not resolve data source for column: %1 - Ðевозможно определить иÑточник данных Ð´Ð»Ñ Ñтолбца: %1 - - - - Could not resolve table for column '%1'. - Ðевозможно определить таблицу Ð´Ð»Ñ Ñтолбца '%1'. - - - - Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Tried to initialize the file at following localizations: %1. - Ðевозможно инициализировать файл конфигурации. Любые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¸ и иÑÑ‚Ð¾Ñ€Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов будут утерÑны поÑле перезапуÑка приложениÑ. Попытки инициализации файла предпринималиÑÑŒ в Ñледующих меÑтах: %1. - - - - General purpose - plugin category name - Общего Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ - - - - Database support - plugin category name - Поддержка баз данных - - - - Code formatter - plugin category name - Форматирование кода - - - - Scripting languages - plugin category name - Скриптовые Ñзыки - - - - Exporting - plugin category name - ЭкÑпорт - - - - Importing - plugin category name - Импорт - - - - Table populating - plugin category name - Заполнение таблиц - - - - Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. - Таблица %1 ÑÑылаетÑÑ Ð½Ð° таблицу %2, но опиÑание внешнего ключа не будет обновлено Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð¹ таблицы из-за проблем Ñ Ð°Ð½Ð°Ð»Ð¸Ð·Ð¾Ð¼ DDL таблицы %3. - - - - All columns indexed by the index %1 are gone. The index will not be recreated after table modification. - Ð’Ñе Ñтолбцы, проиндекÑированные индекÑом %1, удалены. Ð˜Ð½Ð´ÐµÐºÑ Ð½Ðµ будет воÑÑоздан поÑле модификации таблицы. - - - - There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. - Возникла проблема при обработке триггера %1. ВпоÑледÑтвии он не будет полноÑтью обновлён и потребует вашего вниманиÑ. - - - - Cannot not update trigger %1 according to table %2 modification. - Ðевозможно обновить триггер %1 в ÑоответÑтвии Ñ Ð¼Ð¾Ð´Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸ÐµÐ¹ таблицы %2. - - - - - - There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - Возникла проблема при обновлении конÑтрукции %1 внутри триггера %2. Одна из вложенных конÑтрукций %1, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ ÑÑылаетÑÑ Ð½Ð° таблицу %3, не может быть корректно модифицирована. Возможно необходима Ñ€ÑƒÑ‡Ð½Ð°Ñ Ð¿Ñ€Ð°Ð²ÐºÐ° триггера. - - - - All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. - Ð’Ñе Ñтолбцы, затронутые в триггере %1, удалены. Триггер не будет воÑÑоздан поÑле модификации таблицы. - - - - Cannot not update view %1 according to table %2 modifications. -The view will remain as it is. - Ðевозможно обновить предÑтавление %1 в ÑоответÑтвии Ñ Ð¼Ð¾Ð´Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñми таблицы %2. -ПредÑтавление оÑтанетÑÑ ÐºÐ°Ðº еÑть. - - - There is a problem with updating an %1 statement within %2 trigger. One of the SELECT substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - Возникла проблема при обновлении конÑтрукции %1 внутри триггера %2. Одна из вложенных конÑтрукций SELECT, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ ÑÑылаетÑÑ Ð½Ð° таблицу %3, не может быть корректно модифицирована. Возможно необходима Ñ€ÑƒÑ‡Ð½Ð°Ñ Ð¿Ñ€Ð°Ð²ÐºÐ° триггера. - - - - Could not parse DDL of the view to be created. Details: %1 - Ðевозможно проанализировать DDL Ñоздаваемого предÑтавлениÑ. ПодробноÑти: %1 - - - - Parsed query is not CREATE VIEW. It's: %1 - Проанализированный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ðµ ÑвлÑетÑÑ Ð·Ð°Ð¿Ñ€Ð¾Ñом CREATE VIEW. Тип запроÑа: %1 - - - - SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. - SQLiteStudio не удалоÑÑŒ определить Ñтолбцы, возвращаемые новым предÑтавлением, поÑтому невозможно указать, какие триггеры могут ÑломатьÑÑ Ð² процеÑÑе воÑÑозданиÑ. - - - - Could not open file '%1' for reading: %2 - Ðевозможно открыть файл '%1' Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ: %2 - - - - QueryExecutor - - - Execution interrupted. - Выполнение прервано. - - - - Database is not open. - База данных не открыта. - - - - Only one query can be executed simultaneously. - Одновременно может быть выполнен только один запроÑ. - - - - - An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 - Возникла ошибка при выполнении запроÑа count(*), поÑтому разбивка данных по Ñтраницам отключена. Детали ошибки из базы данных: %1 - - - - SQLiteStudio was unable to extract metadata from the query. Results won't be editable. - SQLiteStudio не удалоÑÑŒ извлечь метаданные из запроÑа. Результаты Ð½ÐµÐ»ÑŒÐ·Ñ Ð±ÑƒÐ´ÐµÑ‚ редактировать. - - - - ScriptingQtDbProxy - - - No database available in current context, while called QtScript's %1 command. - При вызове команды QtScript %1 в текущем контекÑте нет доÑтупных баз данных. - - - - Error from %1: %2 - Ошибка в команде %1: %2 - - - - SqlHistoryModel - - - Database - sql history header - База данных - - - - Execution date - sql history header - Дата Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ - - - - Time spent - sql history header - Затраченное Ð²Ñ€ÐµÐ¼Ñ - - - - Rows affected - sql history header - Затронуто Ñтрок - - - - SQL - sql history header - SQL - - - - UpdateManager - - An error occurred while checking for updates: %1. - При проверке обновлений возникла ошибка: %1 - - - Could not check available updates, because server responded with invalid message format. It is safe to ignore this warning. - Ðевозможно проверить наличие обновлений, так как ответ Ñервера имеет некорректный формат. Это предупреждение можно проигнорировать. - - - An error occurred while reading updates metadata: %1. - При чтении метаданных об обновлениÑÑ… возникла ошибка: %1 - - - Could not download updates, because server responded with invalid message format. You can try again later or download and install updates manually. See <a href="%1">User Manual</a> for details. - Ðевозможно загрузить обновлениÑ, так как ответ Ñервера имеет некорректный формат. Ð’Ñ‹ можете попробовать Ñнова позже или Ñкачать и уÑтановить Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð²Ñ€ÑƒÑ‡Ð½ÑƒÑŽ. ПодробноÑти Ñмотрите в <a href="%1">РуководÑтве пользователÑ</a>. - - - Could not create temporary directory for downloading the update. Updating aborted. - Ðевозможно Ñоздать временный каталог Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ обновлениÑ. Обновление прервано. - - - There was no updates to download. Updating aborted. - Ðет обновлений Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸. Обновление прервано. - - - Downloading: %1 - Загрузка: %1 - - - Could not determinate file name from update URL: %1. Updating aborted. - Ðевозможно определить Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° из URL обновлениÑ. Обновление прервано. - - - Failed to open file '%1' for writting: %2. Updating aborted. - Ðе удалоÑÑŒ открыть файл %1 Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи: %2. Обновление прервано. - - - Installing updates. - УÑтановка обновлений. - - - Could not copy current application directory into %1 directory. - Ðевозможно Ñкопировать текущий каталог Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð² каталог %1. - - - Could not create directory %1. - Ðевозможно Ñоздать каталог %1. - - - Could not rename directory %1 to %2. -Details: %3 - Ðевозможно переименовать каталог %1 в %2. -ПодробноÑти: %3 - - - Cannot not rename directory %1 to %2. -Details: %3 - Ðевозможно переименовать каталог %1 в %2. -ПодробноÑти: %3 - - - Could not move directory %1 to %2 and also failed to restore original directory, so the original SQLiteStudio directory is now located at: %3 - Ðевозможно перемеÑтить каталог %1 в %2, а также не удалоÑÑŒ воÑÑтановить оригинальный каталог, поÑтому оригинальный каталог SQLiteStudio теперь раÑположен в: %3 - - - Could not rename directory %1 to %2. Rolled back to the original SQLiteStudio version. - Ðевозможно переименовать каталог %1 в %2. ВоÑÑтановлена Ð¸Ð·Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ SQLiteStudio. - - - Could not unpack component %1 into %2 directory. - Ðевозможно раÑпаковать компонент %1 в каталог %2. - - - Could not find permissions elevator application to run update as a root. Looked for: %1 - Ðевозможно найти приложение Ð¿Ð¾Ð²Ñ‹ÑˆÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸Ð²Ð¸Ð»ÐµÐ³Ð¸Ð¹ Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ root. Были иÑпробованы: %1 - - - Could not execute final updating steps as root: %1 - Ðевозможно выполнить финальные шаги Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ root: %1 - - - Could not execute final updating steps as admin: %1 - Ðевозможно выполнить финальные шаги Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ админиÑтратора: %1 - - - Cannot create temporary directory for updater. - Ðевозможно Ñоздать временный каталог Ð´Ð»Ñ ÑƒÑтановщика обновлений. - - - Cannot create updater script file. - Ðевозможно Ñоздать файл Ñкрипта обновлениÑ. - - - Updating canceled. - Обновление отменено. - - - Could not execute final updating steps as administrator. - Ðевозможно выполнить финальные шаги Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ админиÑтратора. - - - Could not execute final updating steps as administrator. Updater startup timed out. - Ðевозможно выполнить финальные шаги Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ админиÑтратора. Превышен тайм-аут запуÑка программы обновлениÑ. - - - Could not execute final updating steps as administrator. Updater operation timed out. - Ðевозможно выполнить финальные шаги Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ админиÑтратора. Превышен тайм-аут операции программы обновлениÑ. - - - Could not clean up temporary directory %1. You can delete it manually at any time. - Ðевозможно очиÑтить временный каталог %1. Ð’Ñ‹ можете удалить его вручную в любое времÑ. - - - Could not run new version for continuing update. - Ðевозможно запуÑтить новую верÑию Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ. - - - Package not in tar.gz format, cannot install: %1 - Пакет не в формате tar.gz, уÑтановка невозможна: %1 - - - Package %1 cannot be installed, because cannot move it to directory: %2 - Пакет %1 не может быть уÑтановлен, так как невозможно перенеÑти его в каталог: %2 - - - Package %1 cannot be installed, because cannot unpack it: %2 - Пакет %1 не может быть уÑтановлен, так как его невозможно раÑпаковать: %2 - - - Package not in zip format, cannot install: %1 - Пакет не в формате zip, уÑтановка невозможна: %1 - - - Package %1 cannot be installed, because cannot unzip it to directory %2: %3 - Пакет %1 не может быть уÑтановлен, так как его невозможно раÑпаковать в каталог %2: %3 - - - Package %1 cannot be installed, because cannot unzip it to directory: %2 - Пакет %1 не может быть уÑтановлен, так как его невозможно раÑпаковать в каталог: %2 - - - Could not rename directory %1 to %2. - Ðевозможно переименовать каталог %1 в %2. - - - Could not delete directory %1. - Ðевозможно удалить каталог %1. - - - Error executing update command: %1 -Error message: %2 - Ошибка при выполнении команды обновлениÑ: %1 -Сообщение об ошибке: %2 - - - An error occurred while downloading updates: %1. Updating aborted. - При загрузке обновлений произошла ошибка: %1. Обновление прервано. - - - - Updates installer executable is missing. - ОтÑутÑтвует иÑполнÑемый файл уÑтановщика обновлений. - - - - - Unable to check for updates (%1) - Ðевозможно проверить Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ (%1) - - - - details are unknown - подробноÑти неизвеÑтны - - - - Unable to run updater application (%1). Please report this. - Ðевозможно запуÑтить уÑтановщик обновлений (%1). СÑообщите пожалуйÑта об Ñтом разработчику. - - - diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru_RU.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru_RU.ts new file mode 100644 index 0000000..1dc362d --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_ru_RU.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Ðевозможно выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ñ€Ð¸ закрытой базе данных. + + + + Error attaching database %1: %2 + Ошибка во Ð²Ñ€ÐµÐ¼Ñ Ð¿Ñ€Ð¸ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Ðе удалоÑÑŒ Ñоздать полную контрольную точку WAL в базе данных '%1'. Ошибка из движка SQLite: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + Ðе указана база данных Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов. + + + + The database for executing queries was not open. + chain executor + Ðе открыта база данных Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Ðевозможно отключить внешние ключи в базе данных. ПодробноÑти: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Ðевозможно начать транзакцию. ПодробноÑти: %1 + + + + Interrupted + chain executor + Прервано + + + + Could not commit a database transaction. Details: %1 + chain executor + Ðевозможно завершить транзакцию. ПодробноÑти: %1 + + + + CompletionHelper + + + New row reference + ÐÐ¾Ð²Ð°Ñ ÑÑылка на Ñтроку + + + + Old row reference + Ð¡Ñ‚Ð°Ñ€Ð°Ñ ÑÑылка на Ñтроку + + + + New table name + Ðовое Ð¸Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ + + + + New index name + Ðовое Ð¸Ð¼Ñ Ð¸Ð½Ð´ÐµÐºÑа + + + + New view name + Ðовое Ð¸Ð¼Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ + + + + New trigger name + Ðовое Ð¸Ð¼Ñ Ñ‚Ñ€Ð¸Ð³Ð³ÐµÑ€Ð° + + + + Table or column alias + ПÑевдоним таблицы или Ñтолбца + + + + transaction name + Ð¸Ð¼Ñ Ñ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ð¸ + + + + New column name + Ðовое Ð¸Ð¼Ñ Ñтолбца + + + + Column data type + Тип данных Ñтолбца + + + + Constraint name + Ð˜Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ + + + + Error message + Сообщение об ошибке + + + + Any word + Любое Ñлово + + + + Default database + База данных по умолчанию + + + + Temporary objects database + База данных временных объектов + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Ðевозможно начать транзакцию Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¸Ñтории SQL, поÑтому она не удалена. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Ðевозможно завершить транзакцию Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¸Ñтории SQL, поÑтому она не удалена. + + + + DbManagerImpl + + + Could not add database %1: %2 + Ðе удалоÑÑŒ добавить базу данных %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Ðевозможно обновить базу данных %1 из-за ошибки: %2 + + + + + Database file doesn't exist. + Файл базы данных не ÑущеÑтвует. + + + + + + No supporting plugin loaded. + Модуль поддержки не загружен. + + + + Database could not be initialized. + Ðевозможно инициализировать базу данных. + + + + No suitable database driver plugin found. + Ðе найден подходÑщий драйвер базы данных. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Ошибка при Ñоздании таблицы в целевой базе данных: %1 + + + + Could not parse table. + Ðевозможно проанализировать Ñтруктуру таблицы. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Ðевозможно приÑоединить базу данных %1 к базе данных %2, поÑтому данные таблицы %3 будут Ñкопированы при поÑредничеÑтве SQLiteStudio. Этот метод может быть медленным Ð´Ð»Ñ Ð±Ð¾Ð»ÑŒÑˆÐ¸Ñ… таблиц, так что наберитеÑÑŒ терпениÑ. + + + + Error while copying data for table %1: %2 + Ошибка при копировании данных из таблицы %1: %2 + + + + + + Error while copying data to table %1: %2 + Ошибка при копировании данных в таблицу %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Ошибка при удалении иÑходного предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ %1: %2 +Таблицы, индекÑÑ‹, триггеры и предÑтавлениÑ, Ñкопированные в базу данных %3, ÑохранÑÑ‚ÑÑ. + + + + Error while creating view in target database: %1 + Ошибка при Ñоздании предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð² целевой базе данных: %1 + + + + Error while creating index in target database: %1 + Ошибка при Ñоздании индекÑа в целевой базе данных: %1 + + + + Error while creating trigger in target database: %1 + Ошибка при Ñоздании триггера в целевой базе данных: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Ðевозможно проанализировать объект '%1' Ð´Ð»Ñ ÐµÐ³Ð¾ Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸Ñ Ð»Ð¸Ð±Ð¾ копированиÑ. + + + + DdlHistoryModel + + + Database name + ddl history header + Ð˜Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных + + + + Database file + ddl history header + Файл базы данных + + + + Date of execution + ddl history header + Дата Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ + + + + Changes + ddl history header + Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Модуль ÑкÑпорта %1 не поддерживает ÑкÑпорт результатов запроÑа. + + + + Export plugin %1 doesn't support exporing tables. + Модуль ÑкÑпорта %1 не поддерживает ÑкÑпорт таблиц. + + + + Export plugin %1 doesn't support exporing databases. + Модуль ÑкÑпорта %1 не поддерживает ÑкÑпорт баз данных. + + + + Export format '%1' is not supported. Supported formats are: %2. + Формат ÑкÑпорта %1 не поддерживаетÑÑ. Поддерживаемые форматы: %2. + + + + Export to the clipboard was successful. + ЭкÑпорт в буфер обмена уÑпешно выполнен. + + + + Export to the file '%1' was successful. + ЭкÑпорт в файл %1 уÑпешно выполнен. + + + + Export was successful. + ЭкÑпорт уÑпешно выполнен. + + + + Could not export to file %1. File cannot be open for writting. + Ðевозможно выполнить ÑкÑпорт в файл %1. Ðе удалоÑÑŒ открыть файл Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи. + + + + ExportWorker + + + Error while exporting query results: %1 + Ошибка при ÑкÑпорте результатов запроÑа: %1 + + + + Error while counting data column width to export from query results: %1 + Ошибка при подÑчёте ширины Ñтолбца данных Ð´Ð»Ñ ÑкÑпорта результатов запроÑа: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Ðевозможно проанализировать Ñтруктуру %1. Данный объект будет иÑключён при выполнении ÑкÑпорта. + + + + Error while reading data to export from table %1: %2 + Ошибка при Ñчитывании данных Ð´Ð»Ñ ÑкÑпорта из таблицы %1: %2 + + + + Error while counting data to export from table %1: %2 + Ошибка при подÑчёте количеÑтва данных Ð´Ð»Ñ ÑкÑпорта из таблицы %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Ошибка при подÑчёте ширины Ñтолбца данных Ð´Ð»Ñ ÑкÑпорта из таблицы %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Ðеверное количеÑтво аргументов Ð´Ð»Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ '%1'. Ожидаемое количеÑтво: %2, передано: %3. + + + + No such function registered in SQLiteStudio: %1(%2) + Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð½Ðµ зарегиÑтрирована в SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ %1(%2) зарегиÑтрирована Ð´Ð»Ñ Ñзыка %3, однако модуль поддержки Ñтого Ñзыка на данный момент не загружен. + + + + Invalid regular expression pattern: %1 + Ðеверный шаблон регулÑрного выражениÑ: %1 + + + + + Could not open file %1 for reading: %2 + Ðевозможно открыть файл %1 Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ: %2 + + + + Could not open file %1 for writting: %2 + Ðевозможно открыть файл %1 Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи: %2 + + + + Error while writting to file %1: %2 + Ошибка при запиÑи в файл %1: %2 + + + + Unsupported scripting language: %1 + Ðеподдерживаемый Ñкриптовый Ñзык: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Ðевозможно инициализировать текÑтовый кодек Ð´Ð»Ñ ÑкÑпорта. ИÑпользуетÑÑ ÐºÐ¾Ð´ÐµÐº по умолчанию: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Импорт данных в таблицу '%1' выполнен уÑпешно. КоличеÑтво импортированных Ñтрок: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + Модуль импорта не обнаружил ни одного Ñтолбца. + + + + Could not start transaction in order to import a data: %1 + Ðевозможно начать транзакцию Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° данных: %1 + + + + Could not commit transaction for imported data: %1 + Ðевозможно завершить транзакцию Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ñ‹Ñ… данных: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Ð’ таблице '%1' Ñтолбцов меньше, чем в импортируемых данных. Лишние Ñтолбцы будут проигнорированы. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Ð’ таблице '%1' Ñтолбцов больше, чем в импортируемых данных. ÐедоÑтающие Ñтолбцы будут оÑтавлены пуÑтыми. + + + + Could not create table to import to: %1 + Ðевозможно Ñоздать таблицу Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð°: %1 + + + + + + Error while importing data: %1 + Ошибка при импорте данных: %1 + + + + + Interrupted. + import process status update + Прервано. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Ðевозможно импортировать Ñтроку данных â„– %1. Строка пропущена. ПодробноÑти проблемы: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Ðевозможно загрузить модуль %1, так как он конфликтует Ñ Ð¼Ð¾Ð´ÑƒÐ»ÐµÐ¼ %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Ðевозможно загрузить модуль %1, так как не загружен необходимый ему модуль: %2. + + + + Cannot load plugin %1. Error details: %2 + Ðевозможно загрузить модуль %1. ПодробноÑти ошибки: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Ðевозможно загрузить модуль %1 (ошибка при инициализации модулÑ). + + + + min: %1 + plugin dependency version + минимальнаÑ: %1 + + + + max: %1 + plugin dependency version + макÑимальнаÑ: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + КонÑтанта + + + + PopulateConstantConfig + + + Constant value: + Значение конÑтанты: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Словарь + + + + PopulateDictionaryConfig + + + Dictionary file + Файл ÑÐ»Ð¾Ð²Ð°Ñ€Ñ + + + + Pick dictionary file + Выберите файл ÑÐ»Ð¾Ð²Ð°Ñ€Ñ + + + + Word separator + Разделитель Ñлов + + + + Whitespace + Пробел + + + + Line break + ÐŸÐµÑ€ÐµÐ½Ð¾Ñ Ñтроки + + + + Method of using words + СпоÑоб иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñлов + + + + Ordered + По порÑдку + + + + Randomly + Случайным образом + + + + PopulateManager + + + Table '%1' populated successfully. + Таблица '%1' уÑпешно заполнена. + + + + PopulateRandom + + + Random number + Случайное чиÑло + + + + PopulateRandomConfig + + + Constant prefix + ÐŸÑ€ÐµÑ„Ð¸ÐºÑ ÐºÐ¾Ð½Ñтанты + + + + No prefix + Без префикÑа + + + + Minimum value + Минимальное значение + + + + Maximum value + МакÑимальное значение + + + + Constant suffix + Ð¡ÑƒÑ„Ñ„Ð¸ÐºÑ ÐºÐ¾Ð½Ñтанты + + + + No suffix + Без ÑуффикÑа + + + + PopulateRandomText + + + Random text + Случайный текÑÑ‚ + + + + PopulateRandomTextConfig + + + Use characters from common sets: + ИÑпользовать Ñимволы из Ñтандартных наборов: + + + + Minimum length + ÐœÐ¸Ð½Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° + + + + Letters from a to z. + Буквы от a до z. + + + + Alpha + Буквенный + + + + Numbers from 0 to 9. + Цифры от 0 до 9. + + + + Numeric + Цифровой + + + + A whitespace, a tab and a new line character. + Пробел, табулÑÑ†Ð¸Ñ Ð¸ Ñимвол переноÑа Ñтроки. + + + + Whitespace + Ðепечатаемые Ñимволы + + + + Includes all above and all others. + Включает вÑе вышеперечиÑленные и вÑе оÑтальные. + + + + Binary + Бинарный + + + + Use characters from my custom set: + ИÑпользовать Ñимволы из моего набора: + + + + Maximum length + МакÑÐ¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° + + + + If you type some character multiple times, it's more likely to be used. + При указании одного Ñимвола неÑколько раз, вероÑтноÑть его иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÑƒÐ²ÐµÐ»Ð¸Ñ‡Ð¸Ð²Ð°ÐµÑ‚ÑÑ. + + + + PopulateScript + + + Script + Скрипт + + + + PopulateScriptConfig + + + Initialization code (optional) + Инициализирующий код (необÑзательно) + + + + Per step code + Код Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ шага + + + + Language + Язык + + + + Help + Помощь + + + + PopulateSequence + + + Sequence + ПоÑледовательноÑть + + + + PopulateSequenceConfig + + + Start value: + Ðачальное значение: + + + + Step: + Шаг: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Ðевозможно начать транзакцию Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹. ПодробноÑти ошибки: %1 + + + + Error while populating table: %1 + Ошибка при заполнении таблицы: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Ðевозможно завершить транзакцию поÑле Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹. ПодробноÑти ошибки: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Ðевозможно открыть файл '%1' Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ: %2 + + + + Could not open database: %1 + Ðевозможно открыть базу данных: %1 + + + + Result set expired or no row available. + Ð ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð¸Ñ€ÑƒÑŽÑ‰Ð°Ñ Ð²Ñ‹Ð±Ð¾Ñ€ÐºÐ° уÑтарела или ни одна Ñтрока не доÑтупна. + + + + + Could not load extension %1: %2 + Ðевозможно загрузить раÑширение %1: %2 + + + + Could not run WAL checkpoint: %1 + Ðе удалоÑÑŒ запуÑтить контрольную точку WAL: %1 + + + + Could not close database: %1 + Ðевозможно закрыть базу данных: %1 + + + + Could not attach database %1: %2 + Ðе удалоÑÑŒ приÑоединить базу данных %1: %2 + + + + + Incomplete query. + Ðезавершённый запроÑ. + + + + Parser stack overflow + Переполнение Ñтека анализатора + + + + Syntax error + СинтакÑичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° + + + + Could not open dictionary file %1 for reading. + Ðевозможно открыть файл ÑÐ»Ð¾Ð²Ð°Ñ€Ñ %1 Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ. + + + + Dictionary file must exist and be readable. + Файл ÑÐ»Ð¾Ð²Ð°Ñ€Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ ÑущеÑтвовать и быть доÑтупным Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ. + + + + Maximum value cannot be less than minimum value. + МакÑимальное значение не может быть меньше минимального. + + + + Maximum length cannot be less than minimum length. + МакÑÐ¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° не может быть меньше минимальной. + + + + Custom character set cannot be empty. + Произвольный набор Ñимволов не может быть пуÑтым. + + + + Could not find plugin to support scripting language: %1 + Ðевозможно найти модуль поддержки Ñкриптового Ñзыка: %1 + + + + Error while executing populating initial code: %1 + Ошибка при выполнении инициализирующего кода заполнениÑ: %1 + + + + Error while executing populating code: %1 + Ошибка при выполнении кода заполнениÑ: %1 + + + + Select implementation language. + Выберите Ñзык реализации. + + + + Implementation code cannot be empty. + ЗаполнÑющий код не может быть пуÑтым. + + + + Could not resolve data source for column: %1 + Ðевозможно определить иÑточник данных Ð´Ð»Ñ Ñтолбца: %1 + + + + Could not resolve table for column '%1'. + Ðевозможно определить таблицу Ð´Ð»Ñ Ñтолбца '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Ðе удалоÑÑŒ инициализировать конфигурационный файл. Любые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¸ и иÑÑ‚Ð¾Ñ€Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов будут потерÑны поÑле перезагрузки приложениÑ. Ðевозможно Ñоздать файл в Ñледующих меÑтах: %1. + + + + General purpose + plugin category name + Общего Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ + + + + Database support + plugin category name + Поддержка баз данных + + + + Code formatter + plugin category name + Форматирование кода + + + + Scripting languages + plugin category name + Скриптовые Ñзыки + + + + Exporting + plugin category name + ЭкÑпорт + + + + Importing + plugin category name + Импорт + + + + Table populating + plugin category name + Заполнение таблиц + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Таблица %1 ÑÑылаетÑÑ Ð½Ð° таблицу %2, но опиÑание внешнего ключа не будет обновлено Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð¹ таблицы из-за проблем Ñ Ð°Ð½Ð°Ð»Ð¸Ð·Ð¾Ð¼ DDL таблицы %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + Ð’Ñе Ñтолбцы, проиндекÑированные индекÑом %1, удалены. Ð˜Ð½Ð´ÐµÐºÑ Ð½Ðµ будет воÑÑоздан поÑле модификации таблицы. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + Возникла проблема при обработке триггера %1. ВпоÑледÑтвии он не будет полноÑтью обновлён и потребует вашего вниманиÑ. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + Ð’Ñе Ñтолбцы, затронутые в триггере %1, удалены. Триггер не будет воÑÑоздан поÑле модификации таблицы. + + + + Cannot not update trigger %1 according to table %2 modification. + Ðевозможно обновить триггер %1 в ÑоответÑтвии Ñ Ð¼Ð¾Ð´Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸ÐµÐ¹ таблицы %2. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Ðевозможно обновить предÑтавление %1 в ÑоответÑтвии Ñ Ð¼Ð¾Ð´Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñми таблицы %2. +ПредÑтавление оÑтанетÑÑ ÐºÐ°Ðº еÑть. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + Возникла проблема при обновлении конÑтрукции %1 внутри триггера %2. Одна из вложенных конÑтрукций %1, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ ÑÑылаетÑÑ Ð½Ð° таблицу %3, не может быть корректно модифицирована. Возможно необходима Ñ€ÑƒÑ‡Ð½Ð°Ñ Ð¿Ñ€Ð°Ð²ÐºÐ° триггера. + + + + Could not parse DDL of the view to be created. Details: %1 + Ðевозможно проанализировать DDL Ñоздаваемого предÑтавлениÑ. ПодробноÑти: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Проанализированный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ðµ ÑвлÑетÑÑ Ð·Ð°Ð¿Ñ€Ð¾Ñом CREATE VIEW. Тип запроÑа: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio не удалоÑÑŒ определить Ñтолбцы, возвращаемые новым предÑтавлением, поÑтому невозможно указать, какие триггеры могут ÑломатьÑÑ Ð² процеÑÑе воÑÑозданиÑ. + + + + QueryExecutor + + + Execution interrupted. + Выполнение прервано. + + + + Database is not open. + База данных не открыта. + + + + Only one query can be executed simultaneously. + Одновременно может быть выполнен только один запроÑ. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + Возникла ошибка при выполнении запроÑа count(*), поÑтому разбивка данных по Ñтраницам отключена. Детали ошибки из базы данных: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio не удалоÑÑŒ извлечь метаданные из запроÑа. Результаты Ð½ÐµÐ»ÑŒÐ·Ñ Ð±ÑƒÐ´ÐµÑ‚ редактировать. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + При вызове команды JavaScript %1, в текущем контекÑте нет доÑтупных баз данных. + + + + Error from %1: %2 + Ошибка в команде %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Ðевозможно выполнить SQL-запроÑ, так как приложению не удалоÑÑŒ начать транзакцию: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Выполнение запроÑов из файла отменено. Ð’Ñе выполненные ранее из него запроÑÑ‹ откачены. + + + + Could not open file '%1' for reading: %2 + Ðевозможно открыть файл '%1' Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Ðевозможно выполнить SQL-запроÑ, так как приложению не удалоÑÑŒ завершить транзакцию: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Завершено выполнение %1 запроÑов за %2 Ñекунд. %3 запроÑов не было выполнено из-за ошибок. + + + + Finished executing %1 queries in %2 seconds. + Завершено выполнение %1 запроÑов за %2 Ñекунд. + + + + Could not execute SQL due to error. + Ðевозможно выполнить SQL-Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸Ð·-за ошибки. + + + + SqlHistoryModel + + + Database + sql history header + База данных + + + + Execution date + sql history header + Дата Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ + + + + Time spent + sql history header + Затраченное Ð²Ñ€ÐµÐ¼Ñ + + + + Rows affected + sql history header + Затронуто Ñтрок + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Ðевозможно проверить наличие обновлений (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.qm b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.qm deleted file mode 100644 index de604e9..0000000 Binary files a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.qm and /dev/null differ diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.ts deleted file mode 100644 index 8db8efb..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.ts +++ /dev/null @@ -1,1178 +0,0 @@ - - - - - AbstractDb - - - - Cannot execute query on closed database. - Nemôžem spustiÅ¥ dotaz na uzatvorenej databáze. - - - - Error attaching database %1: %2 - Chyba pri pripájaní databázy %1: %2 - - - - BugReporter - - Invalid login or password - Neplatné meno alebo heslo - - - - ChainExecutor - - - The database for executing queries was not defined. - chain executor - - - - - The database for executing queries was not open. - chain executor - - - - - Could not disable foreign keys in the database. Details: %1 - chain executor - - - - - Could not start a database transaction. Details: %1 - chain executor - - - - - Interrupted - chain executor - - - - - Could not commit a database transaction. Details: %1 - chain executor - - - - - CompletionHelper - - - New row reference - - - - - Old row reference - - - - - New table name - - - - - New index name - - - - - New view name - - - - - New trigger name - - - - - Table or column alias - - - - - transaction name - - - - - New column name - - - - - Column data type - - - - - Constraint name - - - - - Error message - - - - - Collation name - - - - - Any word - - - - - Default database - - - - - Temporary objects database - - - - - ConfigImpl - - - Could not start database transaction for deleting SQL history, therefore it's not deleted. - - - - - Could not commit database transaction for deleting SQL history, therefore it's not deleted. - - - - - DbManagerImpl - - - Could not add database %1: %2 - Nemôžem pridaÅ¥ databázu %1: %2 - - - - Database %1 could not be updated, because of an error: %2 - Databáza %1 nemôže byÅ¥ aktualizovaná kvôli chybe: %2 - - - - - Database file doesn't exist. - Databázový súbor neexistuje. - - - - - - No supporting plugin loaded. - - - - - Database could not be initialized. - - - - - No suitable database driver plugin found. - - - - - DbObjectOrganizer - - - - Error while creating table in target database: %1 - Vyskytla sa chyba poÄas vytvárania tabuľky v cieľovej databáze: %1 - - - - Could not parse table. - - - - - Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. - - - - - Error while copying data for table %1: %2 - - - - - - - Error while copying data to table %1: %2 - - - - - Error while dropping source view %1: %2 -Tables, indexes, triggers and views copied to database %3 will remain. - - - - - Error while creating view in target database: %1 - - - - - Error while creating index in target database: %1 - - - - - Error while creating trigger in target database: %1 - - - - - - - Could not parse object '%1' in order to move or copy it. - - - - - DbVersionConverter - - - Target file exists, but could not be overwritten. - Cieľový súbor existuje ale nemôže byÅ¥ prepísaný. - - - - Could not find proper database plugin to create target database. - Nieje možné nájsÅ¥ správny databázový plugin pre vytvorenie cieľovej databázy. - - - - Error while converting database: %1 - Vyskytla sa chyba poÄas konvertovania databázy: %1 - - - - DdlHistoryModel - - - Database name - ddl history header - - - - - Database file - ddl history header - - - - - Date of execution - ddl history header - - - - - Changes - ddl history header - - - - - ExportManager - - - Export plugin %1 doesn't support exporing query results. - - - - - Export plugin %1 doesn't support exporing tables. - - - - - Export plugin %1 doesn't support exporing databases. - - - - - Export format '%1' is not supported. Supported formats are: %2. - - - - - Export to the clipboard was successful. - Export do schránky bol úspeÅ¡ný. - - - - Export to the file '%1' was successful. - Export do súboru '%1' bol úspeÅ¡ný. - - - - Export was successful. - Export bol úspeÅ¡ný. - - - - Could not export to file %1. File cannot be open for writting. - Nemôžem exportovaÅ¥ do súboru %1.Súbor nieje možné otvoriÅ¥ pre zápis. - - - - ExportWorker - - - Error while exporting query results: %1 - - - - - Error while counting data column width to export from query results: %1 - - - - - - Could not parse %1 in order to export it. It will be excluded from the export output. - - - - - Error while reading data to export from table %1: %2 - - - - - Error while counting data to export from table %1: %2 - - - - - Error while counting data column width to export from table %1: %2 - - - - - FunctionManagerImpl - - - Invalid number of arguments to function '%1'. Expected %2, but got %3. - - - - - No such function registered in SQLiteStudio: %1(%2) - - - - - Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. - - - - - Invalid regular expression pattern: %1 - - - - - - Could not open file %1 for reading: %2 - - - - - Could not open file %1 for writting: %2 - - - - - Error while writting to file %1: %2 - - - - - Unsupported scripting language: %1 - - - - - GenericExportPlugin - - - Could not initialize text codec for exporting. Using default codec: %1 - - - - - ImportManager - - - Imported data to the table '%1' successfully. - - - - - ImportWorker - - - No columns provided by the import plugin. - - - - - Could not start transaction in order to import a data: %1 - - - - - Could not commit transaction for imported data: %1 - - - - - Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. - - - - - Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. - - - - - Could not create table to import to: %1 - - - - - - - Error while importing data: %1 - Vyskytla sa chyba poÄas importu dát: %1 - - - - - Interrupted. - import process status update - - - - - Could not import data row number %1. The row was ignored. Problem details: %2 - - - - - PluginManagerImpl - - - Cannot load plugin %1, because it's in conflict with plugin %2. - Nemôžem naÄítaÅ¥ plugin %1, pretože je v konflikte s pluginom %2. - - - - Cannot load plugin %1, because its dependency was not loaded: %2. - Nemôžem naÄítaÅ¥ plugin %1, pretože neboli naÄítané jeho závislosti %2. - - - - Cannot load plugin %1. Error details: %2 - Nemôžem naÄítaÅ¥ plugin %1. Detaily chyby %2 - - - - Cannot load plugin %1 (error while initializing plugin). - Nemôžem naÄítaÅ¥ plugin %1 (nastala chyba pri jeho inicializácii). - - - - min: %1 - plugin dependency version - - - - - max: %1 - plugin dependency version - - - - - PopulateConstant - - - Constant - populate constant plugin name - KonÅ¡tanta - - - - PopulateConstantConfig - - - Constant value: - Hodnota konÅ¡tanty: - - - - PopulateDictionary - - - Dictionary - dictionary populating plugin name - Slovník - - - - PopulateDictionaryConfig - - - Dictionary file - - - - - Pick dictionary file - - - - - Word separator - - - - - Whitespace - - - - - Line break - - - - - Method of using words - - - - - Ordered - - - - - Randomly - - - - - PopulateManager - - - Table '%1' populated successfully. - Tabuľka %1 úspeÅ¡ne naplnená. - - - - PopulateRandom - - - Random number - Náhodné Äíslo - - - - PopulateRandomConfig - - - Constant prefix - - - - - No prefix - - - - - Minimum value - - - - - Maximum value - - - - - Constant suffix - - - - - No suffix - - - - - PopulateRandomText - - - Random text - Náhodný text - - - - PopulateRandomTextConfig - - - Use characters from common sets: - - - - - Minimum length - - - - - Letters from a to z. - - - - - Alpha - - - - - Numbers from 0 to 9. - - - - - Numeric - - - - - A whitespace, a tab and a new line character. - - - - - Whitespace - - - - - Includes all above and all others. - - - - - Binary - - - - - Use characters from my custom set: - - - - - Maximum length - - - - - If you type some character multiple times, it's more likely to be used. - - - - - PopulateScript - - - Script - Skript - - - - PopulateScriptConfig - - - Initialization code (optional) - - - - - Per step code - - - - - Language - - - - - Help - - - - - PopulateSequence - - - Sequence - Sekvencia - - - - PopulateSequenceConfig - - - Start value: - - - - - Step: - - - - - PopulateWorker - - - Could not start transaction in order to perform table populating. Error details: %1 - - - - - Error while populating table: %1 - Vyskytla sa chyba poÄas napĺňania tabuľky: %1 - - - - Could not commit transaction after table populating. Error details: %1 - - - - - QObject - - - - Could not open database: %1 - - - - - - Result set expired or no row available. - - - - - - Could not load extension %1: %2 - - - - - Could not close database: %1 - - - - - - - - - - - SQLite %1 does not support '%2' statement. - - - - - SQLite %1 does not support '%2' statement, but the regular table can be created instead if you proceed. - - - - - Could not parse statement: %1 -Error details: %2 - - - - - - - - SQLite %1 does not support the '%2' clause. Cannot convert '%3' statement with that clause. - - - - - SQLite %1 does not support the '%2' clause in the '%3' statement. - - - - - SQLite %1 does not support current date or time clauses in expressions. - - - - - SQLite %1 does not support row value clauses in expressions. - - - - - - - SQLite %1 does not support '%2' clause in expressions. - - - - - Could not attach database %1: %2 - - - - - - Incomplete query. - - - - - - Parser stack overflow - - - - - - Syntax error - Chyba syntaxe - - - - Could not open dictionary file %1 for reading. - - - - - Dictionary file must exist and be readable. - - - - - Maximum value cannot be less than minimum value. - - - - - Maximum length cannot be less than minimum length. - - - - - Custom character set cannot be empty. - - - - - Could not find plugin to support scripting language: %1 - - - - - Error while executing populating initial code: %1 - - - - - Error while executing populating code: %1 - - - - - Select implementation language. - - - - - Implementation code cannot be empty. - - - - - Could not resolve data source for column: %1 - - - - - Could not resolve table for column '%1'. - - - - - Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Tried to initialize the file at following localizations: %1. - - - - - General purpose - plugin category name - - - - - Database support - plugin category name - - - - - Code formatter - plugin category name - - - - - Scripting languages - plugin category name - Skriptovacie jazyky - - - - Exporting - plugin category name - - - - - Importing - plugin category name - - - - - Table populating - plugin category name - - - - - Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. - - - - - All columns indexed by the index %1 are gone. The index will not be recreated after table modification. - - - - - There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. - - - - - Cannot not update trigger %1 according to table %2 modification. - - - - - - - There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - - - - - All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. - - - - - Cannot not update view %1 according to table %2 modifications. -The view will remain as it is. - - - - - Could not parse DDL of the view to be created. Details: %1 - - - - - Parsed query is not CREATE VIEW. It's: %1 - - - - - SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. - - - - - Could not open file '%1' for reading: %2 - - - - - QueryExecutor - - - Execution interrupted. - - - - - Database is not open. - - - - - Only one query can be executed simultaneously. - - - - - - An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 - Vyskytla sa chyba poÄas vykonávania dotazu count(*), dôsledkom Äoho bolo zablokované stránkovanie. Detail chyby: %1 - - - - SQLiteStudio was unable to extract metadata from the query. Results won't be editable. - - - - - ScriptingQtDbProxy - - - No database available in current context, while called QtScript's %1 command. - - - - - Error from %1: %2 - - - - - SqlHistoryModel - - - Database - sql history header - Dátum spustenia - Databáza - - - - Execution date - sql history header - Dátum spustenia - - - - Time spent - sql history header - Trvanie dotazu - - - - Rows affected - sql history header - PoÄet riadkov - - - - SQL - sql history header - SQL - - - - UpdateManager - - An error occurred while checking for updates: %1. - Vyskytla sa chyba poÄas kontroly aktualizácii: %1. - - - Downloading: %1 - SÅ¥ahujem: %1 - - - Installing updates. - InÅ¡talujem aktualizácie. - - - Could not rename directory %1 to %2. - Nemôžem premenovaÅ¥ adresár %1na %2. - - - Could not delete directory %1. - Nemôžem vymazaÅ¥ adresár %1. - - - An error occurred while downloading updates: %1. Updating aborted. - Vyskytla sa chyba poÄas sÅ¥ahovani aktualizácií:%1. Aktualizácia zruÅ¡ená. - - - - Updates installer executable is missing. - - - - - - Unable to check for updates (%1) - - - - - details are unknown - - - - - Unable to run updater application (%1). Please report this. - - - - diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk_SK.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk_SK.ts new file mode 100644 index 0000000..3496a22 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk_SK.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Nemôžem spustiÅ¥ dotaz na uzatvorenej databáze. + + + + Error attaching database %1: %2 + Chyba pri pripájaní databázy %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Nemôžem pridaÅ¥ databázu %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Databáza %1 nemôže byÅ¥ aktualizovaná kvôli chybe: %2 + + + + + Database file doesn't exist. + Databázový súbor neexistuje. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Vyskytla sa chyba poÄas vytvárania tabuľky v cieľovej databáze: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export do schránky bol úspeÅ¡ný. + + + + Export to the file '%1' was successful. + Export do súboru '%1' bol úspeÅ¡ný. + + + + Export was successful. + Export bol úspeÅ¡ný. + + + + Could not export to file %1. File cannot be open for writting. + Nemôžem exportovaÅ¥ do súboru %1.Súbor nieje možné otvoriÅ¥ pre zápis. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Vyskytla sa chyba poÄas importu dát: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Nemôžem naÄítaÅ¥ plugin %1, pretože je v konflikte s pluginom %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Nemôžem naÄítaÅ¥ plugin %1, pretože neboli naÄítané jeho závislosti %2. + + + + Cannot load plugin %1. Error details: %2 + Nemôžem naÄítaÅ¥ plugin %1. Detaily chyby %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Nemôžem naÄítaÅ¥ plugin %1 (nastala chyba pri jeho inicializácii). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + KonÅ¡tanta + + + + PopulateConstantConfig + + + Constant value: + Hodnota konÅ¡tanty: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Slovník + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Tabuľka %1 úspeÅ¡ne naplnená. + + + + PopulateRandom + + + Random number + Náhodné Äíslo + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Náhodný text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Skript + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sekvencia + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Vyskytla sa chyba poÄas napĺňania tabuľky: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Chyba syntaxe + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Skriptovacie jazyky + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + Vyskytla sa chyba poÄas vykonávania dotazu count(*), dôsledkom Äoho bolo zablokované stránkovanie. Detail chyby: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Databáza + + + + Execution date + sql history header + Dátum spustenia + + + + Time spent + sql history header + Trvanie dotazu + + + + Rows affected + sql history header + PoÄet riadkov + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sr_SP.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sr_SP.ts new file mode 100644 index 0000000..48b9e7a --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sr_SP.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sv_SE.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sv_SE.ts new file mode 100644 index 0000000..7943cb5 --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sv_SE.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_tr_TR.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_tr_TR.ts new file mode 100644 index 0000000..4f3173c --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_tr_TR.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Kapalı veritabanında sorgu çalıştırılamaz. + + + + Error attaching database %1: %2 + Veritabanına eklenirken hata %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + Sorgu çalıştırılacak veritabanı belirtilmemiÅŸ. + + + + The database for executing queries was not open. + chain executor + Sorgu çalıştırılacak veritabanı açılmamış. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Veritabanındaki ikincil anahtar devredışı bırakılamadı. Detaylar: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Veritabanı iÅŸlemi baÅŸlatılamadı. Detaylar: %1 + + + + Interrupted + chain executor + Yarıda kesildi + + + + Could not commit a database transaction. Details: %1 + chain executor + Veritabanı iÅŸlemi baÅŸarılı bitirilemedi. Detaylar: %1 + + + + CompletionHelper + + + New row reference + Yeni satır referansı + + + + Old row reference + Eski satır referansı + + + + New table name + Yeni tablo adı + + + + New index name + Yeni indeks + + + + New view name + Yeni view adı + + + + New trigger name + Yeni trigger adı + + + + Table or column alias + Tablo ya da kolon takma adı + + + + transaction name + iÅŸlem adı + + + + New column name + Yeni kolon adı + + + + Column data type + Kolon veri tipi + + + + Constraint name + Constraint adı + + + + Error message + Hata mesajı + + + + Any word + Herhangi bir kelime + + + + Default database + Varsayılan veritabanı + + + + Temporary objects database + Geçici nesneler veritabanı + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + SQL geçmiÅŸini silme iÅŸlemi baÅŸlatılamadı, bu sebeple silinemedi. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + SQL geçmiÅŸini silme iÅŸlemi commitlenemedi, bu sebeple silinmedi. + + + + DbManagerImpl + + + Could not add database %1: %2 + %1 veritabanı eklenemedi: %2 + + + + Database %1 could not be updated, because of an error: %2 + %2 hatası sebebiyle %1 veritabanı güncellenemedi + + + + + Database file doesn't exist. + Veritabanı dosyası bulunamadı. + + + + + + No supporting plugin loaded. + Hiçbir destekleyen eklenti yüklenmedi. + + + + Database could not be initialized. + Veritabanı açılamadı. + + + + No suitable database driver plugin found. + Hiçbir veritabanı sürücü eklentisi bulunamadı. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Hedef veritabanında tablo yaratırken hata: %1 + + + + Could not parse table. + Tablo ayrıştırılamadı. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Veritabanı dosyası + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + DeÄŸiÅŸiklikler + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Yarıda kesildi. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + en az: %1 + + + + max: %1 + plugin dependency version + en fazla: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Sabit + + + + PopulateConstantConfig + + + Constant value: + Sabit deÄŸer: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Sözlük + + + + PopulateDictionaryConfig + + + Dictionary file + Kütüphane dosyası + + + + Pick dictionary file + Sözlük dosyası seç + + + + Word separator + Kelime ayracı + + + + Whitespace + BoÅŸluk + + + + Line break + Satır sonu + + + + Method of using words + Method of using words + + + + Ordered + Sıralandı + + + + Randomly + Rastgele + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Rasgele Numara + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + Ön Ek Yok + + + + Minimum value + En düşük deÄŸer + + + + Maximum value + En yüksek deÄŸer + + + + Constant suffix + Constant suffix + + + + No suffix + Son ek yok + + + + PopulateRandomText + + + Random text + Rastgele metin + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + En az uzunluk + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Åžefaflık + + + + Numbers from 0 to 9. + 0'dan 9'a kadar sayılar. + + + + Numeric + Sayısal + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + BoÅŸluk + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + İkili (Binary) + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + En yüksek uzunluk + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Komut dizini + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Dil + + + + Help + Yardım + + + + PopulateSequence + + + Sequence + Sıra + + + + PopulateSequenceConfig + + + Start value: + BaÅŸlangıç deÄŸeri: + + + + Step: + Adım: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Veritabanı açılamadı: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Sözdizimi hatası + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + Genel amaçlı + + + + Database support + plugin category name + Veritabanı desteÄŸi + + + + Code formatter + plugin category name + Kod biçimleyici + + + + Scripting languages + plugin category name + Betik dilleri + + + + Exporting + plugin category name + Dışa aktarılıyor + + + + Importing + plugin category name + İçeri aktarılıyor + + + + Table populating + plugin category name + Tablo doldurma + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Veritabanı açık deÄŸil. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Hata %1 den %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Veritabanı + + + + Execution date + sql history header + Yürütme tarihi + + + + Time spent + sql history header + Harcanan zaman + + + + Rows affected + sql history header + Etkilenen satırlar + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + GüncelleÅŸtirmeler kontrol edilemedi (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_uk_UA.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_uk_UA.ts new file mode 100644 index 0000000..5abb81e --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_uk_UA.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Ðеможливо виконати запит в закритій базі даних. + + + + Error attaching database %1: %2 + Помилка Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð±Ð°Ð·Ð¸ даних %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + Ðе вказана база даних Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð². + + + + The database for executing queries was not open. + chain executor + Ðе відкрита база даних Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð². + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ð¸ зовнішні ключі в базі даних. Подробиці: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Ðеможливо розпочати транзакцію. Подробиці: %1 + + + + Interrupted + chain executor + Перервано + + + + Could not commit a database transaction. Details: %1 + chain executor + Ðеможливо завершити транзакцію. Подробиці: %1 + + + + CompletionHelper + + + New row reference + Ðове поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° Ñ€Ñдок + + + + Old row reference + Старе поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° Ñ€Ñдок + + + + New table name + Ðазва нової таблиці + + + + New index name + Ðазва нового індекÑу + + + + New view name + Ðова назва розрізу даних (view) + + + + New trigger name + Ðазва нової тригера + + + + Table or column alias + ПÑевдонім таблиці або ÑÑ‚Ð¾Ð²Ð¿Ñ†Ñ + + + + transaction name + назва транзакції + + + + New column name + Ðове ім'Ñ ÑÑ‚Ð¾Ð²Ð¿Ñ†Ñ + + + + Column data type + Тип даних ÑÑ‚Ð¾Ð²Ð¿Ñ†Ñ + + + + Constraint name + Ðазва Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ + + + + Error message + ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ помилку + + + + Any word + Будь-Ñке Ñлово + + + + Default database + База даних за замовчуваннÑм + + + + Temporary objects database + База даних тимчаÑових об'єктів + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Ðе вдалоÑÑ Ð¿Ð¾Ñ‡Ð°Ñ‚Ð¸ транзакцію бази даних Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ SQL Ñ–Ñторії, тому вона не видалена. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Ðе вдалоÑÑ Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ñ‚Ð¸ транзакцію бази даних Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ SQL Ñ–Ñторії, тому вона не видалена. + + + + DbManagerImpl + + + Could not add database %1: %2 + Ðе вдалоÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ базу даних %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + База даних %1 не може бути оновлена через помилку: %2 + + + + + Database file doesn't exist. + Файл бази даних не Ñ–Ñнує. + + + + + + No supporting plugin loaded. + Модуль підтримки не завантажений. + + + + Database could not be initialized. + Ðеможливо ініціалізувати базу даних. + + + + No suitable database driver plugin found. + Ðе знайдено відповідного драйвера бази даних. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– в цільовій базі даних: %1 + + + + Could not parse table. + Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ð°Ð½Ð°Ð»Ñ–Ð·ÑƒÐ²Ð°Ñ‚Ð¸ Ñтруктуру таблиці. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Ðеможливо приєднати базу даних %1 до бази даних %2, тому дані таблиці %3 будуть Ñкопійовані за допомогою SQLiteStudio. Цей метод може бути повільним Ð´Ð»Ñ Ð²ÐµÐ»Ð¸ÐºÐ¸Ñ… таблиць, так що наберітьÑÑ Ñ‚ÐµÑ€Ð¿Ñ–Ð½Ð½Ñ. + + + + Error while copying data for table %1: %2 + Помилка при копіюванні даних з таблиці %1: %2 + + + + + + Error while copying data to table %1: %2 + Помилка при копіюванні даних в таблицю %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Помилка при видаленні розрізу даних (view) %1: %2 +Таблиці, індекÑи, тригери та переглÑди, що Ñкопійовані в базу %3, залишатьÑÑ. + + + + Error while creating view in target database: %1 + Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– в цільовій базі даних: %1 + + + + Error while creating index in target database: %1 + Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ–Ð½Ð´ÐµÐºÑу в цільовій базі даних: %1 + + + + Error while creating trigger in target database: %1 + Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‚Ñ€Ð¸Ð³ÐµÑ€Ð° в цільовій базі даних: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ð°Ð½Ð°Ð»Ñ–Ð·ÑƒÐ²Ð°Ñ‚Ð¸ об'єкт '%1' Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб переміÑтити або Ñкопіювати його. + + + + DdlHistoryModel + + + Database name + ddl history header + Ðазва бази даних + + + + Database file + ddl history header + Файл бази даних + + + + Date of execution + ddl history header + Дата Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ + + + + Changes + ddl history header + Зміни + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Модуль екÑпорту %1 не підтримує екÑпорт результатів запиту. + + + + Export plugin %1 doesn't support exporing tables. + Модуль екÑпорту %1 не підтримує екÑпорт таблиць. + + + + Export plugin %1 doesn't support exporing databases. + Модуль екÑпорту %1 не підтримує екÑпорт бази даних. + + + + Export format '%1' is not supported. Supported formats are: %2. + Формат екÑпорту '%1' не підтримуєтьÑÑ. ПідтримуютьÑÑ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸: %2. + + + + Export to the clipboard was successful. + ЕкÑпорт до буфера обміну пройшов уÑпішно. + + + + Export to the file '%1' was successful. + ЕкÑпорт у файл '%1' уÑпішно здійÑнено. + + + + Export was successful. + Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð¹ÑˆÐ»Ð¾ уÑпішно. + + + + Could not export to file %1. File cannot be open for writting. + Ðе вдалоÑÑ ÐµÐºÑпортувати до файлу %1. Файл не може бути відкритий Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñу. + + + + ExportWorker + + + Error while exporting query results: %1 + Помилка під Ñ‡Ð°Ñ ÐµÐºÑпорту результатів запиту: %1 + + + + Error while counting data column width to export from query results: %1 + Помилка під Ñ‡Ð°Ñ Ð¿Ñ–Ð´Ñ€Ð°Ñ…ÑƒÐ½ÐºÑƒ ширини ÑÑ‚Ð¾Ð²Ð¿Ñ†Ñ Ð´Ð°Ð½Ð¸Ñ… Ð´Ð»Ñ ÐµÐºÑпорту з результатів запиту: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Ðеможливо проаналізувати Ñтруктуру %1. Даний об'єкт буде виключений при виконанні екÑпорту. + + + + Error while reading data to export from table %1: %2 + Помилка при читанні даних Ð´Ð»Ñ ÐµÐºÑпорту з таблиці %1: %2 + + + + Error while counting data to export from table %1: %2 + Помилка при підрахунку кількоÑті даних Ð´Ð»Ñ ÐµÐºÑпорту з таблиці %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Помилка при підрахунку ширини Ñтовпчика даних Ð´Ð»Ñ ÐµÐºÑпорту з таблиці %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Ðекоректна кількіÑть аргументів Ð´Ð»Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ— '%1'. ОчікувалоÑÑŒ %2, але отримано %3. + + + + No such function registered in SQLiteStudio: %1(%2) + Ðемає такої функції, зареєÑтрованої в SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Ð¤ÑƒÐ½ÐºÑ†Ñ–Ñ %1(%2) була зареєÑтрована на мові %3, але плагін, що підтримує мову, наразі не завантажений. + + + + Invalid regular expression pattern: %1 + Ðевірний шаблон регулÑрного виразу: %1 + + + + + Could not open file %1 for reading: %2 + Ðеможливо відкрити файл %1 Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ: %2 + + + + Could not open file %1 for writting: %2 + Ðеможливо відкрити файл %1 Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñу: %2 + + + + Error while writting to file %1: %2 + Помилка при запиÑу в файл %1: %2 + + + + Unsupported scripting language: %1 + Ðепідтримуваний Ñкриптова мова: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Ðеможливо ініціалізувати текÑтовий кодек Ð´Ð»Ñ ÐµÐºÑпорту. ВикориÑтовуєтьÑÑ ÐºÐ¾Ð´ÐµÐº за замовчуваннÑм: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Дані в таблицю '%1' імпортовані уÑпішно. КількіÑть імпортованих Ñ€Ñдків: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + Модуль імпорту не виÑвив жодного Ñтовпчика. + + + + Could not start transaction in order to import a data: %1 + Ðе вдалоÑÑ Ð¿Ð¾Ñ‡Ð°Ñ‚Ð¸ транзакцію Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ…: %1 + + + + Could not commit transaction for imported data: %1 + Ðе вдалоÑÑ Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ñ‚Ð¸ транзакцію Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ð¾Ð²Ð°Ð½Ð¸Ñ… даних: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%1' має менше Ñтовпців, ніж в імпортованих даних. Зайві Ñтовпці даних будуть проігноровані. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + У таблиці '%1' Ñтовпців більше, ніж в імпортованих даних. ВідÑутні Ñтовпці будуть залишені порожніми. + + + + Could not create table to import to: %1 + Ðе вдалоÑÑ Ñтворити таблицю Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ: %1 + + + + + + Error while importing data: %1 + Помилка при імпорті даних: %1 + + + + + Interrupted. + import process status update + Перервано. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Ðе вдалоÑÑ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ñ‚Ð¸ дані у Ñ€Ñдку %1. РÑдок було проігноровано. Подробиці проблеми: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ плагін %1, тому що він конфліктує з плагіном %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Ðеможливо завантажити модуль%1, тому що не завантажений необхідний йому модуль: %2. + + + + Cannot load plugin %1. Error details: %2 + Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ плагін %1. Подробиці помилки: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ плагін %1 (помилка при ініціалізації плагіна). + + + + min: %1 + plugin dependency version + мінімум: %1 + + + + max: %1 + plugin dependency version + макÑимум: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + КонÑтанта + + + + PopulateConstantConfig + + + Constant value: + Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñтанти: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Словник + + + + PopulateDictionaryConfig + + + Dictionary file + Файл Ñловника + + + + Pick dictionary file + Вибрати файл Ñловника + + + + Word separator + Розділювач Ñлів + + + + Whitespace + Пробіл + + + + Line break + ПеренеÑÐµÐ½Ð½Ñ Ñ€Ñдка + + + + Method of using words + СпоÑіб викориÑÑ‚Ð°Ð½Ð½Ñ Ñлів + + + + Ordered + По порÑдку + + + + Randomly + Випадковим чином + + + + PopulateManager + + + Table '%1' populated successfully. + Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%1' уÑпішно заповнена. + + + + PopulateRandom + + + Random number + Випадкове чиÑло + + + + PopulateRandomConfig + + + Constant prefix + ÐŸÑ€ÐµÑ„Ñ–ÐºÑ ÐºÐ¾Ð½Ñтанти + + + + No prefix + Без префікÑа + + + + Minimum value + Мінімальне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ + + + + Maximum value + МакÑимальне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ + + + + Constant suffix + Ð¡ÑƒÑ„Ñ–ÐºÑ ÐºÐ¾Ð½Ñтанти + + + + No suffix + Без ÑуфікÑа + + + + PopulateRandomText + + + Random text + Випадковий текÑÑ‚ + + + + PopulateRandomTextConfig + + + Use characters from common sets: + ВикориÑтовувати Ñимволи з Ñтандартного набору: + + + + Minimum length + Мінімальна довжина + + + + Letters from a to z. + Букви від a до z. + + + + Alpha + Буквений + + + + Numbers from 0 to 9. + ЧиÑла від 0 до 9. + + + + Numeric + Цифровий + + + + A whitespace, a tab and a new line character. + Пробіл, табулÑÑ†Ñ–Ñ Ñ‚Ð° Ñимвол нового Ñ€Ñдка. + + + + Whitespace + Пробіл + + + + Includes all above and all others. + Включає вÑÑ– перераховані вище Ñ– вÑÑ– інші. + + + + Binary + Бінарний + + + + Use characters from my custom set: + ВикориÑтовувати Ñимволи з мого набору: + + + + Maximum length + МакÑимальна довжина + + + + If you type some character multiple times, it's more likely to be used. + При вказівці одного Ñимволу кілька разів, ймовірніÑть його викориÑÑ‚Ð°Ð½Ð½Ñ Ð·Ð±Ñ–Ð»ÑŒÑˆÑƒÑ”Ñ‚ÑŒÑÑ. + + + + PopulateScript + + + Script + Скрипт + + + + PopulateScriptConfig + + + Initialization code (optional) + Ініціалізаційний код (необов'Ñзково) + + + + Per step code + Код Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ кроку + + + + Language + Мова + + + + Help + Допомога + + + + PopulateSequence + + + Sequence + ПоÑлідовніÑть + + + + PopulateSequenceConfig + + + Start value: + Початкове значеннÑ: + + + + Step: + Крок: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Ðе вдалоÑÑ Ð¿Ð¾Ñ‡Ð°Ñ‚Ð¸ транзакцію Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ–. Подробиці помилки: %1 + + + + Error while populating table: %1 + Помилка при заповненні таблиці: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Ðе вдалоÑÑ Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ñ‚Ð¸ транзакцію піÑÐ»Ñ Ð·Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ–. Подробиці помилки: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл '%1' Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ: %2 + + + + Could not open database: %1 + Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ базу даних: %1 + + + + Result set expired or no row available. + Результуюча вибірка заÑтаріла або жоден Ñ€Ñдок не доÑтупний. + + + + + Could not load extension %1: %2 + Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Ðе вдалоÑÑ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¸ базу даних: %1 + + + + Could not attach database %1: %2 + Ðе вдалоÑÑ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ð¸ базу даних %1: %2 + + + + + Incomplete query. + Ðезавершений запит. + + + + Parser stack overflow + ÐŸÐµÑ€ÐµÐ¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ñтека аналізатора + + + + Syntax error + СинтакÑична помилка + + + + Could not open dictionary file %1 for reading. + Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл Ñловника %1 Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ. + + + + Dictionary file must exist and be readable. + Файл Ñловника має Ñ–Ñнувати та бути читабельним. + + + + Maximum value cannot be less than minimum value. + МакÑимальне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð½Ðµ може бути менше мінімального значеннÑ. + + + + Maximum length cannot be less than minimum length. + МакÑимальна довжина не може бути меншою мінімальної довжини. + + + + Custom character set cannot be empty. + Довільний набір Ñимволів не може бути пуÑтим. + + + + Could not find plugin to support scripting language: %1 + Ðеможливо знайти модуль підтримки Ñкриптового мови: %1 + + + + Error while executing populating initial code: %1 + Помилка при виконанні ініціалізації коду заповненнÑ: %1 + + + + Error while executing populating code: %1 + Помилка при виконанні коду заповненнÑ: %1 + + + + Select implementation language. + Виберіть мову реалізації. + + + + Implementation code cannot be empty. + Заповнюючий код не може бути порожнім. + + + + Could not resolve data source for column: %1 + Ðеможливо визначити джерело даних Ð´Ð»Ñ ÑтовпцÑ: %1 + + + + Could not resolve table for column '%1'. + Ðеможливо визначити таблицю Ð´Ð»Ñ ÑÑ‚Ð¾Ð²Ð¿Ñ†Ñ '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Ðеможливо ініціалізувати конфігураційний файл. Будь-Ñкі зміни конфігурації Ñ– Ñ–ÑÑ‚Ð¾Ñ€Ñ–Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² будуть втрачені піÑÐ»Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸. Ðеможливо Ñтворити файл в наÑтупних міÑцÑÑ…: %1. + + + + General purpose + plugin category name + Загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ + + + + Database support + plugin category name + Підтримка баз даних + + + + Code formatter + plugin category name + Ð¤Ð¾Ñ€Ð¼Ð°Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ + + + + Scripting languages + plugin category name + Скриптові мови + + + + Exporting + plugin category name + ЕкÑпорт + + + + Importing + plugin category name + Імпорт + + + + Table populating + plugin category name + Ð—Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†ÑŒ + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ %1 поÑилаєтьÑÑ Ð½Ð° таблицю %2, але Ð¾Ð¿Ð¸Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ð³Ð¾ ключа не буде оновлено Ð´Ð»Ñ Ð¾Ð¿Ð¸Ñу нової таблиці через проблеми з аналізом DDL таблиці %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + Ð’ÑÑ– Ñтовпчики, проіндекÑовані індекÑом %1, видалені. Ð†Ð½Ð´ÐµÐºÑ Ð½Ðµ буде відтворений піÑÐ»Ñ Ð¼Ð¾Ð´Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— таблиці. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + Виникла проблема при обробці тригера %1. Згодом він не буде повніÑтю оновлений Ñ– вимагає вашої уваги. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + Ð’ÑÑ– Ñтовпчики, зачеплені в тригері %1, видалені. Тригер буде відтворений піÑÐ»Ñ Ð¼Ð¾Ð´Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— таблиці. + + + + Cannot not update trigger %1 according to table %2 modification. + Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ тригер %1 відповідно до зміни таблиці %2. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Ðеможливо оновити розріз даних (view) %1 у відповідноÑті зі зміною таблиці %2. +Вид залишитьÑÑ Ñ‚Ð°ÐºÐ¸Ð¼, Ñк Ñ”. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + Виникла проблема при оновленні конÑтрукції %1 вÑередині тригера %2. Одна з вкладених конÑтрукцій %1, Ñка можливо поÑилаєтьÑÑ Ð½Ð° таблицю %3, не може бути коректно модифікована. Можливо необхідне ручна правка тригера. + + + + Could not parse DDL of the view to be created. Details: %1 + Ðеможливо проаналізувати DDL Ñтворюваного розрізу даних (view). Подробиці: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Проаналізований запит не Ñ” запитом CREATE VIEW. Тип запиту: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio не вдалоÑÑ Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð¸Ñ‚Ð¸ Ñтовпці, Ñкі повертаютьÑÑ Ð½Ð¾Ð²Ð¸Ð¼ розрізом даних (view), тому неможливо вказати, Ñкі тригери можуть зламатиÑÑ Ð² процеÑÑ– відтвореннÑ. + + + + QueryExecutor + + + Execution interrupted. + Ð’Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÑ€Ð²Ð°Ð½Ð¾. + + + + Database is not open. + Базу даних не відкрито. + + + + Only one query can be executed simultaneously. + ОдночаÑно може бути виконаний тільки один запит. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ count(*) запиту, тому розбивка даних по Ñторінках буде вимкнено. Деталі помилки з бази даних: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio не вдалоÑÑ Ð²Ð¸Ñ‚Ñгти метадані із запиту. Результат не зможе бути редагованим. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + Ðемає доÑтупної бази даних в поточному контекÑті, під Ñ‡Ð°Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÑƒ JavaScript's %1 команди. + + + + Error from %1: %2 + Помилка в команді %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл '%1' Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + База даних + + + + Execution date + sql history header + Дата Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ + + + + Time spent + sql history header + Витрачено чаÑу + + + + Rows affected + sql history header + ТоркнулоÑÑ Ñ€Ñдків + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Ðе вдаєтьÑÑ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ð¸Ñ‚Ð¸ наÑвніÑть оновлень (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_vi_VN.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_vi_VN.ts new file mode 100644 index 0000000..173c03e --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_vi_VN.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + Cannot execute query on closed database. + + + + Error attaching database %1: %2 + Error attaching database %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + The database for executing queries was not defined. + + + + The database for executing queries was not open. + chain executor + The database for executing queries was not open. + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + Could not disable foreign keys in the database. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + Could not start a database transaction. Details: %1 + + + + Interrupted + chain executor + Interrupted + + + + Could not commit a database transaction. Details: %1 + chain executor + Could not commit a database transaction. Details: %1 + + + + CompletionHelper + + + New row reference + New row reference + + + + Old row reference + Old row reference + + + + New table name + New table name + + + + New index name + New index name + + + + New view name + New view name + + + + New trigger name + New trigger name + + + + Table or column alias + Table or column alias + + + + transaction name + transaction name + + + + New column name + New column name + + + + Column data type + Column data type + + + + Constraint name + Constraint name + + + + Error message + Error message + + + + Any word + Any word + + + + Default database + Default database + + + + Temporary objects database + Temporary objects database + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + Could not start database transaction for deleting SQL history, therefore it's not deleted. + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + + + + DbManagerImpl + + + Could not add database %1: %2 + Could not add database %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + Database %1 could not be updated, because of an error: %2 + + + + + Database file doesn't exist. + Database file doesn't exist. + + + + + + No supporting plugin loaded. + No supporting plugin loaded. + + + + Database could not be initialized. + Database could not be initialized. + + + + No suitable database driver plugin found. + No suitable database driver plugin found. + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + Error while creating table in target database: %1 + + + + Could not parse table. + Could not parse table. + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + + + + Error while copying data for table %1: %2 + Error while copying data for table %1: %2 + + + + + + Error while copying data to table %1: %2 + Error while copying data to table %1: %2 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + Error while creating view in target database: %1 + + + + Error while creating index in target database: %1 + Error while creating index in target database: %1 + + + + Error while creating trigger in target database: %1 + Error while creating trigger in target database: %1 + + + + + + Could not parse object '%1' in order to move or copy it. + Could not parse object '%1' in order to move or copy it. + + + + DdlHistoryModel + + + Database name + ddl history header + Database name + + + + Database file + ddl history header + Database file + + + + Date of execution + ddl history header + Date of execution + + + + Changes + ddl history header + Changes + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + Export to the clipboard was successful. + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + Export was successful. + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + Error while exporting query results: %1 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + No such function registered in SQLiteStudio: %1(%2) + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + Invalid regular expression pattern: %1 + + + + + Could not open file %1 for reading: %2 + Could not open file %1 for reading: %2 + + + + Could not open file %1 for writting: %2 + Could not open file %1 for writting: %2 + + + + Error while writting to file %1: %2 + Error while writting to file %1: %2 + + + + Unsupported scripting language: %1 + Unsupported scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + Error while importing data: %1 + + + + + Interrupted. + import process status update + Interrupted. + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + min: %1 + + + + max: %1 + plugin dependency version + max: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + Constant + + + + PopulateConstantConfig + + + Constant value: + Constant value: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + Dictionary + + + + PopulateDictionaryConfig + + + Dictionary file + Dictionary file + + + + Pick dictionary file + Pick dictionary file + + + + Word separator + Word separator + + + + Whitespace + Whitespace + + + + Line break + Line break + + + + Method of using words + Method of using words + + + + Ordered + Ordered + + + + Randomly + Randomly + + + + PopulateManager + + + Table '%1' populated successfully. + Table '%1' populated successfully. + + + + PopulateRandom + + + Random number + Random number + + + + PopulateRandomConfig + + + Constant prefix + Constant prefix + + + + No prefix + No prefix + + + + Minimum value + Minimum value + + + + Maximum value + Maximum value + + + + Constant suffix + Constant suffix + + + + No suffix + No suffix + + + + PopulateRandomText + + + Random text + Random text + + + + PopulateRandomTextConfig + + + Use characters from common sets: + Use characters from common sets: + + + + Minimum length + Minimum length + + + + Letters from a to z. + Letters from a to z. + + + + Alpha + Alpha + + + + Numbers from 0 to 9. + Numbers from 0 to 9. + + + + Numeric + Numeric + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + Whitespace + + + + Includes all above and all others. + Includes all above and all others. + + + + Binary + Binary + + + + Use characters from my custom set: + Use characters from my custom set: + + + + Maximum length + Maximum length + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + Script + + + + PopulateScriptConfig + + + Initialization code (optional) + Initialization code (optional) + + + + Per step code + Per step code + + + + Language + Language + + + + Help + Help + + + + PopulateSequence + + + Sequence + Sequence + + + + PopulateSequenceConfig + + + Start value: + Start value: + + + + Step: + Step: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + Could not start transaction in order to perform table populating. Error details: %1 + + + + Error while populating table: %1 + Error while populating table: %1 + + + + Could not commit transaction after table populating. Error details: %1 + Could not commit transaction after table populating. Error details: %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not open database: %1 + Could not open database: %1 + + + + Result set expired or no row available. + Result set expired or no row available. + + + + + Could not load extension %1: %2 + Could not load extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + Could not close database: %1 + + + + Could not attach database %1: %2 + Could not attach database %1: %2 + + + + + Incomplete query. + Incomplete query. + + + + Parser stack overflow + Parser stack overflow + + + + Syntax error + Syntax error + + + + Could not open dictionary file %1 for reading. + Could not open dictionary file %1 for reading. + + + + Dictionary file must exist and be readable. + Dictionary file must exist and be readable. + + + + Maximum value cannot be less than minimum value. + Maximum value cannot be less than minimum value. + + + + Maximum length cannot be less than minimum length. + Maximum length cannot be less than minimum length. + + + + Custom character set cannot be empty. + Custom character set cannot be empty. + + + + Could not find plugin to support scripting language: %1 + Could not find plugin to support scripting language: %1 + + + + Error while executing populating initial code: %1 + Error while executing populating initial code: %1 + + + + Error while executing populating code: %1 + Error while executing populating code: %1 + + + + Select implementation language. + Select implementation language. + + + + Implementation code cannot be empty. + Implementation code cannot be empty. + + + + Could not resolve data source for column: %1 + Could not resolve data source for column: %1 + + + + Could not resolve table for column '%1'. + Could not resolve table for column '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + + + + General purpose + plugin category name + General purpose + + + + Database support + plugin category name + Database support + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + Exporting + + + + Importing + plugin category name + Importing + + + + Table populating + plugin category name + Table populating + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + + + + Cannot not update trigger %1 according to table %2 modification. + Cannot not update trigger %1 according to table %2 modification. + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + + + + Could not parse DDL of the view to be created. Details: %1 + Could not parse DDL of the view to be created. Details: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + Parsed query is not CREATE VIEW. It's: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + + + + QueryExecutor + + + Execution interrupted. + Execution interrupted. + + + + Database is not open. + Database is not open. + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + No database available in current context, while called JavaScript's %1 command. + + + + Error from %1: %2 + Error from %1: %2 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + Database + + + + Execution date + sql history header + Execution date + + + + Time spent + sql history header + Time spent + + + + Rows affected + sql history header + Rows affected + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + Could not check for updates (%1). + + + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_CN.qm b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_CN.qm deleted file mode 100644 index be651ee..0000000 --- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_CN.qm +++ /dev/null @@ -1 +0,0 @@ -<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_CN.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_CN.ts index 1d68aaa..b0ced46 100644 --- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_CN.ts +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_CN.ts @@ -1,1153 +1,1101 @@ - - + + AbstractDb - - - Cannot execute query on closed database. - 无法在关闭的数æ®åº“上执行查询。 + + + Cannot execute query on closed database. + 无法在关闭的数æ®åº“上执行查询。 - - Error attaching database %1: %2 - 附加数æ®åº“ %1 æ—¶å‘生错误:%2 + + Error attaching database %1: %2 + 附加数æ®åº“ %1 æ—¶å‘生错误:%2 - - - BugReporter - Invalid login or password - ç”¨æˆ·åæˆ–密ç é”™è¯¯ + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 - - + + ChainExecutor - - The database for executing queries was not defined. - chain executor - 用于执行查询的数æ®åº“没有被定义。 + + The database for executing queries was not defined. + chain executor + 没有定义执行查询的数æ®åº“。 - - The database for executing queries was not open. - chain executor - 用于执行查询的数æ®åº“没有打开。 + + The database for executing queries was not open. + chain executor + 没有打开执行查询的数æ®åº“。 - - Could not disable foreign keys in the database. Details: %1 - chain executor - 无法在数æ®åº“中ç¦ç”¨å¤–键。详情:%1 + + Could not disable foreign keys in the database. Details: %1 + chain executor + 未能ç¦ç”¨è¯¥æ•°æ®åº“中的外键。详情:%1 - - Could not start a database transaction. Details: %1 - chain executor - 无法开始数æ®åº“事务。详情:%1 + + Could not start a database transaction. Details: %1 + chain executor + 未能å¯åŠ¨æ•°æ®åº“事务。详情:%1 - - Interrupted - chain executor - 中断 + + Interrupted + chain executor + 中断 - - Could not commit a database transaction. Details: %1 - chain executor - 无法æäº¤æ•°æ®åº“事务。详情:%1 + + Could not commit a database transaction. Details: %1 + chain executor + 未能æäº¤æ•°æ®åº“事务。详情:%1 - - + + CompletionHelper - - New row reference - 新的行引用 + + New row reference + 新的行引用 - - Old row reference - 旧的行引用 + + Old row reference + 旧的行引用 - - New table name - 新的表å + + New table name + 新的表å - - New index name - 新的索引å + + New index name + 新的索引å - - New view name - 新的视图å + + New view name + 新的视图å - - New trigger name - 新的触å‘器å + + New trigger name + 新的触å‘器å - - Table or column alias - 表或字段别å + + Table or column alias + 表或列别å - - transaction name - 事务å + + transaction name + 事务å - - New column name - 新字段å + + New column name + 新列å - - Column data type - 字段数æ®ç±»åž‹ + + Column data type + 列数æ®ç±»åž‹ - - Constraint name - 约æŸå + + Constraint name + 约æŸå - - Error message - é”™è¯¯ä¿¡æ¯ + + Error message + é”™è¯¯ä¿¡æ¯ - - Collation name - 排åºè§„则å + + Any word + ä»»æ„å•è¯ - - Any word - ä»»æ„å•è¯ + + Default database + 默认数æ®åº“ - - Default database - 默认数æ®åº“ + + Temporary objects database + 临时对象数æ®åº“ - - - Temporary objects database - 临时对象数æ®åº“ - - - + + ConfigImpl - - Could not start database transaction for deleting SQL history, therefore it's not deleted. - 无法为删除 SQL 历å²å¼€å§‹æ•°æ®åº“事务,因此它没有被删除。 + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + 删除 SQL 历å²çš„æ•°æ®åº“事务å¯åŠ¨å¤±è´¥ï¼Œå› æ­¤æœªåˆ é™¤ã€‚ - - Could not commit database transaction for deleting SQL history, therefore it's not deleted. - 无法为删除 SQL åŽ†å²æäº¤æ•°æ®åº“事务,因此它没有被删除。 + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + 删除 SQL 历å²çš„æ•°æ®åº“事务æäº¤å¤±è´¥ï¼Œå› æ­¤æœªåˆ é™¤ã€‚ - - + + DbManagerImpl - - Could not add database %1: %2 - 无法添加数æ®åº“ %1:%2 + + Could not add database %1: %2 + 无法添加数æ®åº“ %1:%2 - - Database %1 could not be updated, because of an error: %2 - æ•°æ®åº“ %1 没有被更新,由于错误:%2 + + Database %1 could not be updated, because of an error: %2 + æ•°æ®åº“ %1 没有被更新,由于错误:%2 - - - Database file doesn't exist. - æ•°æ®åº“文件ä¸å­˜åœ¨ + + + Database file doesn't exist. + æ•°æ®åº“文件ä¸å­˜åœ¨ã€‚ - - - - No supporting plugin loaded. - + + + + No supporting plugin loaded. + 没有加载所需的æ’件。 - - Database could not be initialized. - æ•°æ®åº“无法被åˆå§‹åŒ–。 + + Database could not be initialized. + 无法åˆå§‹åŒ–æ•°æ®åº“。 - - No suitable database driver plugin found. - 没有找到åˆé€‚的数æ®åº“驱动æ’ä»¶ + + No suitable database driver plugin found. + 没有找到åˆé€‚的数æ®åº“驱动æ’件。 - - + + DbObjectOrganizer - - - Error while creating table in target database: %1 - 在目标数æ®åº“中创建表时å‘生错误:%1 + + + Error while creating table in target database: %1 + 在目标数æ®åº“中创建表时å‘生错误:%1 - - Could not parse table. - 无法解æžè¡¨ã€‚ + + Could not parse table. + 无法解æžè¡¨ã€‚ - - Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. - + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + æ•°æ®åº“ %1 无法附加到数æ®åº“ %2,因此将使用 SQLiteStudio åšä¸­é—´äººæ¥å¤åˆ¶è¡¨ %3 的数æ®ã€‚此方法用于大型表格å¯èƒ½ä¼šå¾ˆæ…¢ï¼Œè¯·è€å¿ƒç­‰å¾…。 - - Error while copying data for table %1: %2 - 在从表 %1 中å¤åˆ¶æ•°æ®æ—¶å‘生错误:%2 + + Error while copying data for table %1: %2 + 在从表 %1 中å¤åˆ¶æ•°æ®æ—¶å‘生错误:%2 - - - - Error while copying data to table %1: %2 - 在å‘表 %1 中å¤åˆ¶æ•°æ®æ—¶å‘生错误:%2 + + + + Error while copying data to table %1: %2 + 在å‘表 %1 中å¤åˆ¶æ•°æ®æ—¶å‘生错误:%2 - - Error while dropping source view %1: %2 + + Error while dropping source view %1: %2 Tables, indexes, triggers and views copied to database %3 will remain. - - - - - Error while creating view in target database: %1 - 在目标数æ®åº“中创建视图时å‘生错误:%1 - - - - Error while creating index in target database: %1 - 在目标数æ®åº“中创建索引时å‘生错误:%1 - - - - Error while creating trigger in target database: %1 - 在目标数æ®åº“中创建触å‘器时å‘生错误:%1 + 在丢弃æºè§†å›¾ %1:%2 时出错 +已拷è´è‡³æ•°æ®åº“ %3 的表格ã€ç´¢å¼•ã€è§¦å‘器和视图将被ä¿ç•™ã€‚ - - - - Could not parse object '%1' in order to move or copy it. - + + Error while creating view in target database: %1 + 在目标数æ®åº“中创建视图时å‘生错误:%1 - - - DbVersionConverter - - Target file exists, but could not be overwritten. - 目标文件存在,但无法被覆写。 + + Error while creating index in target database: %1 + 在目标数æ®åº“中创建索引时å‘生错误:%1 - - Could not find proper database plugin to create target database. - 无法找到åˆé€‚的数æ®åº“æ’件用以创建目标数æ®åº“。 + + Error while creating trigger in target database: %1 + 在目标数æ®åº“中创建触å‘器时å‘生错误:%1 - - Error while converting database: %1 - è½¬æ¢æ•°æ®åº“æ—¶å‘生错误:%1 + + + + Could not parse object '%1' in order to move or copy it. + 无法解æžå¯¹è±¡ '%1' 用于移动或å¤åˆ¶ã€‚ - - + + DdlHistoryModel - - Database name - ddl history header - æ•°æ®åº“å + + Database name + ddl history header + æ•°æ®åº“å - - Database file - ddl history header - æ•°æ®åº“文件 + + Database file + ddl history header + æ•°æ®åº“文件 - - Date of execution - ddl history header - 执行日期 + + Date of execution + ddl history header + 执行日期 - - Changes - ddl history header - å½±å“ + + Changes + ddl history header + å½±å“ - - + + ExportManager - - Export plugin %1 doesn't support exporing query results. - 导出æ’ä»¶ %1 䏿”¯æŒå¯¼å‡ºæŸ¥è¯¢ç»“果。 + + Export plugin %1 doesn't support exporing query results. + 导出æ’ä»¶ %1 䏿”¯æŒå¯¼å‡ºæŸ¥è¯¢ç»“果。 - - Export plugin %1 doesn't support exporing tables. - 导出æ’ä»¶ %1 䏿”¯æŒå¯¼å‡ºè¡¨ã€‚ + + Export plugin %1 doesn't support exporing tables. + 导出æ’ä»¶ %1 䏿”¯æŒå¯¼å‡ºè¡¨ã€‚ - - Export plugin %1 doesn't support exporing databases. - 导出æ’ä»¶ %1 䏿”¯æŒå¯¼å‡ºæ•°æ®åº“。 + + Export plugin %1 doesn't support exporing databases. + 导出æ’ä»¶ %1 䏿”¯æŒå¯¼å‡ºæ•°æ®åº“。 - - Export format '%1' is not supported. Supported formats are: %2. - å¯¼å‡ºæ ¼å¼ '%1' ä¸å—支æŒï¼Œå—支æŒçš„æ ¼å¼æ˜¯ï¼š%2。 + + Export format '%1' is not supported. Supported formats are: %2. + å¯¼å‡ºæ ¼å¼ '%1' ä¸å—支æŒï¼Œå—支æŒçš„æ ¼å¼æ˜¯ï¼š%2。 - - Export to the clipboard was successful. - æˆåŠŸå¯¼å‡ºåˆ°å‰ªè´´æ¿ã€‚ + + Export to the clipboard was successful. + æˆåŠŸå¯¼å‡ºåˆ°å‰ªè´´æ¿ã€‚ - - Export to the file '%1' was successful. - æˆåŠŸå¯¼å‡ºåˆ°æ–‡ä»¶ '%1'。 + + Export to the file '%1' was successful. + æˆåŠŸå¯¼å‡ºåˆ°æ–‡ä»¶ '%1'。 - - Export was successful. - 导出æˆåŠŸã€‚ + + Export was successful. + 导出æˆåŠŸã€‚ - - Could not export to file %1. File cannot be open for writting. - 无法导出到文件 %1ã€‚æ–‡ä»¶æ— æ³•ä»¥å†™æ¨¡å¼æ‰“开。 + + Could not export to file %1. File cannot be open for writting. + 无法导出到文件 %1ã€‚æ–‡ä»¶æ— æ³•ä»¥å†™æ¨¡å¼æ‰“开。 - - + + ExportWorker - - Error while exporting query results: %1 - 导出查询结果时å‘生错误:%1 + + Error while exporting query results: %1 + 导出查询结果时å‘生错误:%1 - - Error while counting data column width to export from query results: %1 - + + Error while counting data column width to export from query results: %1 + 计算从查询结果æ¥å¯¼å‡ºæ•°æ®çš„列宽度时出错:%1 - - - Could not parse %1 in order to export it. It will be excluded from the export output. - æ— æ³•ä¸ºå¯¼å‡ºè§£æž %1。因此它将ä¸ä¼šè¢«åŒ…å«åœ¨å¯¼å‡ºè¾“出中。 + + + Could not parse %1 in order to export it. It will be excluded from the export output. + æ— æ³•ä¸ºå¯¼å‡ºè§£æž %1。因此它将ä¸ä¼šè¢«åŒ…å«åœ¨å¯¼å‡ºè¾“出中。 - - Error while reading data to export from table %1: %2 - + + Error while reading data to export from table %1: %2 + 从表 %1è¯»å–æ•°æ®å¹¶å¯¼å‡ºæ—¶å‡ºé”™ï¼š%2 - - Error while counting data to export from table %1: %2 - + + Error while counting data to export from table %1: %2 + 计算从 %1: %2 表导出数æ®çš„æ•°é‡æ—¶å‡ºé”™ - - Error while counting data column width to export from table %1: %2 - + + Error while counting data column width to export from table %1: %2 + 计算从 %1: %2 表导出数æ®çš„列宽度时出错 - - + + FunctionManagerImpl - - Invalid number of arguments to function '%1'. Expected %2, but got %3. - æ— æ•ˆçš„å‚æ•°ä¸ªæ•°ã€‚对于函数 %1ï¼Œéœ€è¦ %2 个,但得到 %3 个。 + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + æ— æ•ˆçš„å‚æ•°ä¸ªæ•°ã€‚对于函数 %1ï¼Œéœ€è¦ %2 个,但得到 %3 个。 - - No such function registered in SQLiteStudio: %1(%2) - 没有这样的一个在 SQLiteStudio 中注册的函数:%1(%2) + + No such function registered in SQLiteStudio: %1(%2) + SQLiteStudio 中没有注册该函数:%1(%2) - - Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. - 函数 %1(%2) 以 %3 语言被注册。但是支æŒè¯¥è¯­è¨€çš„æ’ä»¶æ²¡æœ‰è¢«æ­£ç¡®åŠ è½½ã€‚ + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + 函数 %1(%2) 以 %3 语言注册,但是支æŒè¯¥è¯­è¨€çš„æ’ä»¶æ²¡æœ‰è¢«æ­£ç¡®åŠ è½½ã€‚ - - Invalid regular expression pattern: %1 - 无效的正则表达å¼ï¼š%1 + + Invalid regular expression pattern: %1 + æ— æ•ˆçš„æ­£åˆ™è¡¨è¾¾å¼æ¨¡å¼ï¼š%1 - - - Could not open file %1 for reading: %2 - æ— æ³•ä»¥è¯»æ¨¡å¼æ‰“开文件 %1:%2 + + + Could not open file %1 for reading: %2 + æ— æ³•ä»¥è¯»æ¨¡å¼æ‰“开文件 %1:%2 - - Could not open file %1 for writting: %2 - æ— æ³•ä»¥å†™æ¨¡å¼æ‰“开文件 %1:%2 + + Could not open file %1 for writting: %2 + æ— æ³•ä»¥å†™æ¨¡å¼æ‰“开文件 %1:%2 - - Error while writting to file %1: %2 - 写入文件 %1 æ—¶å‘生错误:%2 + + Error while writting to file %1: %2 + 写入文件 %1 æ—¶å‘生错误:%2 - - Unsupported scripting language: %1 - 䏿”¯æŒçš„脚本语言:%1 + + Unsupported scripting language: %1 + 䏿”¯æŒçš„脚本语言:%1 - - + + GenericExportPlugin - - Could not initialize text codec for exporting. Using default codec: %1 - 无法为导出åˆå§‹åŒ–文本编解ç å™¨ã€‚使用默认的编解ç å™¨ï¼š%1 + + Could not initialize text codec for exporting. Using default codec: %1 + 无法为导出åˆå§‹åŒ–文本编解ç å™¨ã€‚使用默认的编解ç å™¨ï¼š%1 - - + + ImportManager - - Imported data to the table '%1' successfully. - æˆåŠŸå¯¼å…¥æ•°æ®è‡³è¡¨ %1 + + Imported data to the table '%1' successfully. Number of imported rows: %2 + æˆåŠŸå‘表 '%1' 导入数æ®ã€‚å½±å“行数:%2 - - + + ImportWorker - - No columns provided by the import plugin. - + + No columns provided by the import plugin. + 导入æ’件没有æä¾›åˆ—。 - - Could not start transaction in order to import a data: %1 - 无法为导入数æ®å¼€å§‹äº‹åŠ¡ï¼š%1 + + Could not start transaction in order to import a data: %1 + 无法为导入数æ®å¼€å§‹äº‹åŠ¡ï¼š%1 - - Could not commit transaction for imported data: %1 - æ— æ³•ä¸ºå¯¼å…¥æ•°æ®æäº¤äº‹åŠ¡ï¼š%1 + + Could not commit transaction for imported data: %1 + æ— æ³•ä¸ºå¯¼å…¥æ•°æ®æäº¤äº‹åŠ¡ï¼š%1 - - Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. - 表 %1 的字段数少于å³å°†å¯¼å…¥æ•°æ®çš„字段数。过多的字段将会被忽略。 + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + 表 '%1' 的列少于å³å°†å¯¼å…¥æ•°æ®çš„列数。过多的数æ®åˆ—将被忽略。 - - Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. - 表 %1 的字段数多于å³å°†å¯¼å…¥æ•°æ®çš„字段数。一些字段将会被置为空值。 + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + 表 '%1' 的列多于å³å°†å¯¼å…¥æ•°æ®çš„列数。一些字段将被留空。 - - Could not create table to import to: %1 - + + Could not create table to import to: %1 + 未能创建导入所用的表:%1 - - - - Error while importing data: %1 - å¯¼å…¥æ•°æ®æ—¶å‘生错误:%1 + + + + Error while importing data: %1 + å¯¼å…¥æ•°æ®æ—¶å‘生错误:%1 - - - Interrupted. - import process status update - 中断。 + + + Interrupted. + import process status update + 中断。 - - Could not import data row number %1. The row was ignored. Problem details: %2 - 无法导入行数为 %1 的数æ®ï¼Œè¯¥è¡Œå°†ä¼šè¢«å¿½ç•¥ã€‚问题详情:%2 + + Could not import data row number %1. The row was ignored. Problem details: %2 + æ— æ³•å¯¼å…¥è¡Œå· %1 的数æ®ã€‚该行将被忽略。问题详情:%2 - - + + PluginManagerImpl - - Cannot load plugin %1, because it's in conflict with plugin %2. - 无法加载æ’ä»¶ %1,因为它与这些æ’件冲çªï¼š%2。 + + Cannot load plugin %1, because it's in conflict with plugin %2. + 无法加载æ’ä»¶ %1,因为它与这些æ’件冲çªï¼š%2。 - - Cannot load plugin %1, because its dependency was not loaded: %2. - 无法加载æ’ä»¶ %1,因为它ä¾èµ–的这些æ’件没有被加载:%2。 + + Cannot load plugin %1, because its dependency was not loaded: %2. + 无法加载æ’ä»¶ %1,因为它ä¾èµ–的这些æ’件没有被加载:%2。 - - Cannot load plugin %1. Error details: %2 - 无法加载æ’ä»¶ %1。错误详情:52 + + Cannot load plugin %1. Error details: %2 + 无法加载æ’ä»¶ %1。错误详情:%2 - - Cannot load plugin %1 (error while initializing plugin). - 无法加载æ’ä»¶ %1(åˆå§‹åŒ–æ’ä»¶æ—¶å‘生错误)。 + + Cannot load plugin %1 (error while initializing plugin). + 无法加载æ’ä»¶ %1(åˆå§‹åŒ–æ’ä»¶æ—¶å‘生错误)。 - - min: %1 - plugin dependency version - 最å°ï¼š%1 + + min: %1 + plugin dependency version + 最å°ï¼š%1 - - max: %1 - plugin dependency version - 最大:%1 + + max: %1 + plugin dependency version + 最大:%1 - - + + PopulateConstant - - Constant - populate constant plugin name - å¸¸é‡ + + Constant + populate constant plugin name + å¸¸é‡ - - + + PopulateConstantConfig - - Constant value: - 常é‡å€¼ï¼š + + Constant value: + 常é‡å€¼ï¼š - - + + PopulateDictionary - - Dictionary - dictionary populating plugin name - å­—å…¸ + + Dictionary + dictionary populating plugin name + å­—å…¸ - - + + PopulateDictionaryConfig - - Dictionary file - 字典文件 + + Dictionary file + 字典文件 - - Pick dictionary file - 选择字典文件 + + Pick dictionary file + 选择字典文件 - - Word separator - 文本分隔符 + + Word separator + å•è¯åˆ†éš”符 - - Whitespace - 空白 + + Whitespace + 空白 - - Line break - æ¢è¡Œ + + Line break + æ¢è¡Œ - - Method of using words - + + Method of using words + å•è¯ç”¨æ³• - - Ordered - 有åºçš„ + + Ordered + æœ‰åº - - Randomly - éšæœºåœ° + + Randomly + éšæœº - - + + PopulateManager - - Table '%1' populated successfully. - + + Table '%1' populated successfully. + 表 '%1' å¡«å……æˆåŠŸã€‚ - - + + PopulateRandom - - Random number - éšæœºæ•° + + Random number + éšæœºæ•° - - + + PopulateRandomConfig - - Constant prefix - 常é‡å‰ç¼€ + + Constant prefix + 固定å‰ç¼€ - - No prefix - æ— å‰ç¼€ + + No prefix + æ— å‰ç¼€ - - Minimum value - 最å°å€¼ + + Minimum value + 最å°å€¼ - - Maximum value - 最大值 + + Maximum value + 最大值 - - Constant suffix - 常é‡åŽç¼€ + + Constant suffix + 固定åŽç¼€ - - No suffix - æ— åŽç¼€ + + No suffix + æ— åŽç¼€ - - + + PopulateRandomText - - Random text - éšæœºæ–‡æœ¬ + + Random text + éšæœºæ–‡æœ¬ - - + + PopulateRandomTextConfig - - Use characters from common sets: - + + Use characters from common sets: + 使用常用字符集åˆï¼š - - Minimum length - 最å°é•¿åº¦ + + Minimum length + 最å°é•¿åº¦ - - Letters from a to z. - å­—æ¯a到z。 + + Letters from a to z. + å­—æ¯ a 到 z。 - - Alpha - + + Alpha + è‹±æ–‡å­—æ¯ - - Numbers from 0 to 9. - æ•°å­—0到9 + + Numbers from 0 to 9. + æ•°å­— 0 到 9。 - - Numeric - æ•°å­— + + Numeric + æ•°å­— - - A whitespace, a tab and a new line character. - + + A whitespace, a tab and a new line character. + 空格ã€åˆ¶è¡¨ç¬¦å’Œæ¢è¡Œç¬¦ã€‚ - - Whitespace - 空白 + + Whitespace + 空白字符 - - Includes all above and all others. - + + Includes all above and all others. + 包括上述åŠå…¶ä»–所有。 - - Binary - 二进制 + + Binary + 二进制 - - Use characters from my custom set: - + + Use characters from my custom set: + 使用自定义字符集åˆï¼š - - Maximum length - 最大长度 + + Maximum length + 最大长度 - - If you type some character multiple times, it's more likely to be used. - + + If you type some character multiple times, it's more likely to be used. + 字符被输入的次数越多,被使用的概率越大。 - - + + PopulateScript - - Script - 脚本 + + Script + 脚本 - - + + PopulateScriptConfig - - Initialization code (optional) - åˆå§‹åŒ–代ç ï¼ˆå¯é€‰ï¼‰ + + Initialization code (optional) + åˆå§‹åŒ–代ç ï¼ˆå¯é€‰ï¼‰ - - Per step code - æ¯ä¸€æ­¥ä»£ç  + + Per step code + æ­¥è¿›ä»£ç  - - Language - 语言 + + Language + 语言 - - Help - 帮助 + + Help + 帮助 - - + + PopulateSequence - - Sequence - åºåˆ— + + Sequence + åºåˆ— - - + + PopulateSequenceConfig - - Start value: - 开始值: + + Start value: + 起始值: - - Step: - 步数: + + Step: + 步进: - - + + PopulateWorker - - Could not start transaction in order to perform table populating. Error details: %1 - + + Could not start transaction in order to perform table populating. Error details: %1 + 未能å¯åŠ¨æ‰§è¡Œè¡¨å¡«å……çš„äº‹åŠ¡ã€‚é”™è¯¯ç»†èŠ‚ï¼š%1 - - Error while populating table: %1 - + + Error while populating table: %1 + 表填充出错:%1 - - Could not commit transaction after table populating. Error details: %1 - + + Could not commit transaction after table populating. Error details: %1 + 未能æäº¤è¡¨å¡«å……事务。错误细节:%1 - - + + QObject - - - Could not open database: %1 - 无法打开数æ®åº“:%1 + + Could not open file '%1' for reading: %2 + æ— æ³•ä»¥è¯»æ¨¡å¼æ‰“开文件 '%1':%2 - - - Result set expired or no row available. - + + Could not open database: %1 + 无法打开数æ®åº“:%1 - - - Could not load extension %1: %2 - 无法加载扩展 %1:%2 + + Result set expired or no row available. + 结果集过期或者无å¯ç”¨çš„行。 - - Could not close database: %1 - 无法关闭数æ®åº“:%1 + + + Could not load extension %1: %2 + 无法加载扩展 %1:%2 - - - - - - - - SQLite %1 does not support '%2' statement. - + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 - - SQLite %1 does not support '%2' statement, but the regular table can be created instead if you proceed. - + + Could not close database: %1 + 无法关闭数æ®åº“:%1 - - Could not parse statement: %1 -Error details: %2 - + + Could not attach database %1: %2 + 无法附加数æ®åº“ %1:%2 - - - - - SQLite %1 does not support the '%2' clause. Cannot convert '%3' statement with that clause. - + + + Incomplete query. + ä¸å®Œæ•´çš„æŸ¥è¯¢ã€‚ - - SQLite %1 does not support the '%2' clause in the '%3' statement. - + + Parser stack overflow + è§£æžå †æ ˆæº¢å‡º - - SQLite %1 does not support current date or time clauses in expressions. - + + Syntax error + 语法错误 - - SQLite %1 does not support row value clauses in expressions. - + + Could not open dictionary file %1 for reading. + æ— æ³•ä»¥è¯»æ¨¡å¼æ‰“开字典文件 %1。 - - - - SQLite %1 does not support '%2' clause in expressions. - + + Dictionary file must exist and be readable. + 字典文件必须存在且å¯è¯»ã€‚ - - Could not attach database %1: %2 - 无法附加数æ®åº“ %1:%2 + + Maximum value cannot be less than minimum value. + 最大值ä¸èƒ½å°äºŽæœ€å°å€¼ã€‚ - - - Incomplete query. - + + Maximum length cannot be less than minimum length. + 最大长度ä¸èƒ½å°äºŽæœ€å°é•¿åº¦ã€‚ - - - Parser stack overflow - è§£æžå †æ ˆæº¢å‡º + + Custom character set cannot be empty. + 自定义字符集åˆä¸èƒ½ä¸ºç©ºã€‚ - - - Syntax error - 语法错误 + + Could not find plugin to support scripting language: %1 + 无法找到æä¾›è„šæœ¬è¯­è¨€æ”¯æŒçš„æ’ä»¶ï¼š%1 - - Could not open dictionary file %1 for reading. - æ— æ³•ä»¥è¯»æ¨¡å¼æ‰“开字典文件 %1。 + + Error while executing populating initial code: %1 + 执行填充åˆå§‹åŒ–ä»£ç æ—¶å‡ºé”™ï¼š%1 - - Dictionary file must exist and be readable. - 字典文件必须存在且å¯è¯»ã€‚ + + Error while executing populating code: %1 + æ‰§è¡Œå¡«å……ä»£ç æ—¶å‡ºé”™ï¼š%1 - - Maximum value cannot be less than minimum value. - 最大值ä¸é¥¿èƒ½å°äºŽæœ€å°å€¼ã€‚ + + Select implementation language. + 选择实现语言。 - - Maximum length cannot be less than minimum length. - 最大长度ä¸èƒ½å°äºŽæœ€å°é•¿åº¦ã€‚ + + Implementation code cannot be empty. + 实现代ç ä¸å¾—为空。 - - Custom character set cannot be empty. - + + Could not resolve data source for column: %1 + 无法解æžè¯¥åˆ—çš„æ•°æ®æºï¼š%1 - - Could not find plugin to support scripting language: %1 - 无法找到æ’件去支æŒè„šæœ¬è¯­è¨€ï¼š%1 + + Could not resolve table for column '%1'. + 无法解æžè¡¨çš„列 '%1'。 - - Error while executing populating initial code: %1 - + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + 无法åˆå§‹åŒ–é…置文件。所有的é…置更改和查询历å²éƒ½å°†åœ¨åº”用程åºé‡å¯æ—¶ä¸¢å¤±ã€‚无法在下列ä½ç½®åˆ›å»ºæ–‡ä»¶ï¼š%1。 - - Error while executing populating code: %1 - + + General purpose + plugin category name + 一般用途 - - Select implementation language. - 选择实现语言。 + + Database support + plugin category name + æ•°æ®åº“æ”¯æŒ - - Implementation code cannot be empty. - 实现代ç ä¸å¾—为空。 + + Code formatter + plugin category name + ä»£ç æ ¼å¼åŒ– - - Could not resolve data source for column: %1 - + + Scripting languages + plugin category name + 脚本语言 - - Could not resolve table for column '%1'. - + + Exporting + plugin category name + 导出 - - Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Tried to initialize the file at following localizations: %1. - + + Importing + plugin category name + 导入 - - General purpose - plugin category name - + + Table populating + plugin category name + æ•°æ®è¡¨å¡«å…… - - Database support - plugin category name - æ•°æ®åº“æ”¯æŒ + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + 表 %1 引用了表 %2,但由于解æžè¡¨ %3 çš„ DDL 出现问题,ä¸ä¼šä¸ºæ–°çš„表定义更新该外键。 - - Code formatter - plugin category name - + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + 索引 %1 涵盖的所有列索引已消失。表修改åŽè¯¥ç´¢å¼•也ä¸ä¼šè¢«é‡æ–°è§¦å‘。 - - Scripting languages - plugin category name - 脚本语言 + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + 处ç†è§¦å‘器 %1 时出现问题。它å¯èƒ½æ²¡æœ‰è¢«å……åˆ†æ›´æ–°ï¼Œè¿™éœ€è¦æ‚¨çš„æ³¨æ„。 - - Exporting - plugin category name - 导出 + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + 触å‘器 %1 涵盖的所有列已消失。表修改åŽè¯¥è§¦å‘器也ä¸ä¼šè¢«é‡æ–°è§¦å‘。 - - Importing - plugin category name - 导入 + + Cannot not update trigger %1 according to table %2 modification. + 无法根æ®è¡¨ %2 的修改更新触å‘器 %1。 - - Table populating - plugin category name - + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + 无法根æ®è¡¨ %2 的修改更新视图 %1。 +è§†å›¾å°†ä¿æŒåŽŸæ ·ä¸å˜ã€‚ - - Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. - + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + æ›´æ–° %2 触å‘器内的一个 %1 è¯­å¥æ—¶å‡ºçŽ°é—®é¢˜ã€‚%1 å­å¥ä¸­å¼•用的表 %3 å¯èƒ½æ— æ³•è¢«æ­£ç¡®ä¿®æ”¹ã€‚å¿…è¦æ—¶éœ€è¦æ‰‹åŠ¨æ›´æ–°è§¦å‘器。 - - All columns indexed by the index %1 are gone. The index will not be recreated after table modification. - + + Could not parse DDL of the view to be created. Details: %1 + 无法解æžè¦åˆ›å»ºçš„视图的 DDL。详情:%1 - - There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. - + + Parsed query is not CREATE VIEW. It's: %1 + è§£æžçš„æŸ¥è¯¢ä¸æ˜¯ CREATE VIEW。它是:%1 - - Cannot not update trigger %1 according to table %2 modification. - 无法根æ®è¡¨ %2 的修改去更新触å‘器 %1。 + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio æ— æ³•è§£æžæ–°å»ºè§†å›¾æ—¶è¿”回的列,因此无法告知哪些触å‘器å¯èƒ½åœ¨è§¦å‘过程中失败。 + + + QueryExecutor - - - - There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. - + + Execution interrupted. + 执行被中断。 - - All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. - + + Database is not open. + æ•°æ®åº“没有打开。 - - Cannot not update view %1 according to table %2 modifications. -The view will remain as it is. - + + Only one query can be executed simultaneously. + åªèƒ½åŒæ—¶æ‰§è¡Œä¸€ä¸ªæŸ¥è¯¢ã€‚ - - Could not parse DDL of the view to be created. Details: %1 - + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + 执行 count(*) 查询时出错,因此将ç¦ç”¨æ•°æ®åˆ†é¡µã€‚æ•°æ®åº“错误详情:%1 - - Parsed query is not CREATE VIEW. It's: %1 - + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio 无法从查询中æå–元数æ®ã€‚结果将ä¸å¯ç¼–辑。 + + + ScriptingQtDbProxy - - SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. - + + No database available in current context, while called JavaScript's %1 command. + 调用 JavaScript çš„ %1 命令期间,当å‰ä¸Šä¸‹æ–‡æ²¡æœ‰å¯ç”¨çš„æ•°æ®åº“。 - - Could not open file '%1' for reading: %2 - æ— æ³•ä»¥è¯»æ¨¡å¼æ‰“开文件 '%1':%2 + + Error from %1: %2 + æ¥è‡ª %1 的错误:%2 - - - QueryExecutor + + + SqlFileExecutor - - Execution interrupted. - 执行被中断。 + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 - - Database is not open. - æ•°æ®åº“没有打开。 + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. - - Only one query can be executed simultaneously. - åªèƒ½åŒæ—¶æ‰§è¡Œä¸€ä¸ªæŸ¥è¯¢ã€‚ + + Could not open file '%1' for reading: %2 + æ— æ³•ä»¥è¯»æ¨¡å¼æ‰“开文件 '%1':%2 - - - An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 - + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 - - SQLiteStudio was unable to extract metadata from the query. Results won't be editable. - SQLiteStudio 无法从查询中æå–元数æ®ã€‚结果将ä¸å¯ç¼–辑。 + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - - - ScriptingQtDbProxy - - No database available in current context, while called QtScript's %1 command. - + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. - - Error from %1: %2 - æ¥è‡ª %1 的错误:%2 + + Could not execute SQL due to error. + Could not execute SQL due to error. - - + + SqlHistoryModel - - Database - sql history header - æ•°æ®åº“ + + Database + sql history header + æ•°æ®åº“ - - Execution date - sql history header - æ‰§è¡Œä»£ç  + + Execution date + sql history header + 执行日期 - - Time spent - sql history header - 耗时 + + Time spent + sql history header + 用时 - - Rows affected - sql history header - å½±å“的行数 + + Rows affected + sql history header + å½±å“行数 - - SQL - sql history header - SQL + + SQL + sql history header + SQL - - + + UpdateManager - - Updates installer executable is missing. - 缺少更新安装程åºå¯æ‰§è¡Œæ–‡ä»¶ã€‚ - - - - - Unable to check for updates (%1) - 无法检查更新(%1) - - - - details are unknown - 无法得知详情 - - - - Unable to run updater application (%1). Please report this. - 无法è¿è¡Œæ›´æ–°å®‰è£…器(%1)。请报告这个问题。 + + Could not check for updates (%1). + 检查更新失败 (%1)。 - + diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_TW.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_TW.ts new file mode 100644 index 0000000..7bcf7ed --- /dev/null +++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_zh_TW.ts @@ -0,0 +1,1101 @@ + + + + + AbstractDb + + + + Cannot execute query on closed database. + 在已關閉的資料庫中, 無法執行查詢 + + + + Error attaching database %1: %2 + 無法附加資料庫 %1: %2 + + + + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + Failed to make full WAL checkpoint on database '%1'. Error returned from SQLite engine: %2 + + + + ChainExecutor + + + The database for executing queries was not defined. + chain executor + 此資料庫無法執行ä¸è¢«å®šç¾©çš„æŸ¥è©¢ + + + + The database for executing queries was not open. + chain executor + 此資料庫無法執行沒有被開啟的查詢 + + + + Could not disable foreign keys in the database. Details: %1 + chain executor + 無法disable foreign keys. Details: %1 + + + + Could not start a database transaction. Details: %1 + chain executor + 無法啟動資料庫交易. 請見詳細資料說明: %1 + + + + Interrupted + chain executor + 已中斷 + + + + Could not commit a database transaction. Details: %1 + chain executor + 無法啟動資料庫交易. 請見詳細資料說明: %1 + + + + CompletionHelper + + + New row reference + æ–°çš„ row åƒè€ƒ + + + + Old row reference + 舊的 row åƒè€ƒ + + + + New table name + æ–°çš„table å稱 %1 + + + + New index name + æ–°çš„indexå稱 + + + + New view name + æ–°çš„viewå稱 + + + + New trigger name + æ–°çš„triggerå稱 + + + + Table or column alias + Table 或 Column 別å + + + + transaction name + transaction å稱 + + + + New column name + 新欄ä½å稱: + + + + Column data type + 欄ä½åž‹åˆ¥ + + + + Constraint name + Constraintå稱 + + + + Error message + éŒ¯èª¤è¨Šæ¯ + + + + Any word + 任何字 + + + + Default database + é è¨­è³‡æ–™åº« + + + + Temporary objects database + 暫時的物件資料庫 + + + + ConfigImpl + + + Could not start database transaction for deleting SQL history, therefore it's not deleted. + 因無法啟動資料庫交易, 造æˆåˆªé™¤SQL æ­·å²å¤±æ•— + + + + Could not commit database transaction for deleting SQL history, therefore it's not deleted. + 因無法commit資料庫交易, 造æˆåˆªé™¤SQL æ­·å²å¤±æ•— + + + + DbManagerImpl + + + Could not add database %1: %2 + 無法新增資料庫 %1: %2 + + + + Database %1 could not be updated, because of an error: %2 + 資料庫 %1 無法被更新,因為 error: %2 + + + + + Database file doesn't exist. + 資料庫檔案ä¸å­˜åœ¨ + + + + + + No supporting plugin loaded. + ç„¡æ³•è¼‰å…¥å¯æ”¯æ´çš„ plugin + + + + Database could not be initialized. + 無法開啟資料庫。 + + + + No suitable database driver plugin found. + 沒有é©åˆçš„è³‡æ–™åº«è¶¨å‹•ç¨‹å¼ plugin + + + + DbObjectOrganizer + + + + Error while creating table in target database: %1 + 在資料庫 %1 建立 table時發生錯誤 + + + + Could not parse table. + ç„¡æ³•è§£æž table + + + + Database %1 could not be attached to database %2, so the data of table %3 will be copied with SQLiteStudio as a mediator. This method can be slow for huge tables, so please be patient. + 資料庫 %1 無法被附加至資料庫 %2, 所以 table %3 的資料將會被SQLiteStudio複製一份作為mediator. æ­¤æ–¹æ³•å°æ–¼å¤§table會很慢,請è€å¿ƒç­‰å¾…. + + + + Error while copying data for table %1: %2 + 當複製table %1: %2 時發生錯誤 + + + + + + Error while copying data to table %1: %2 + 當複製table %1: %2 時發生錯誤 + + + + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + Error while dropping source view %1: %2 +Tables, indexes, triggers and views copied to database %3 will remain. + + + + Error while creating view in target database: %1 + 在資料庫 %1 建立 table時發生錯誤 + + + + Error while creating index in target database: %1 + 在資料庫 %1 建立 index時發生錯誤 + + + + Error while creating trigger in target database: %1 + 在資料庫 %1 建立 trigger 時發生錯誤 + + + + + + Could not parse object '%1' in order to move or copy it. + 無法ä¾åºè§£æž object '%1' 是è¦ç§»å‹• (move) 或是複製 (copy) + + + + DdlHistoryModel + + + Database name + ddl history header + 資料庫å稱 + + + + Database file + ddl history header + 資料庫檔案 + + + + Date of execution + ddl history header + 執行日期 + + + + Changes + ddl history header + 變更 + + + + ExportManager + + + Export plugin %1 doesn't support exporing query results. + Export plugin %1 doesn't support exporing query results. + + + + Export plugin %1 doesn't support exporing tables. + Export plugin %1 doesn't support exporing tables. + + + + Export plugin %1 doesn't support exporing databases. + Export plugin %1 doesn't support exporing databases. + + + + Export format '%1' is not supported. Supported formats are: %2. + Export format '%1' is not supported. Supported formats are: %2. + + + + Export to the clipboard was successful. + å·²æˆåŠŸå°‡è³‡æ–™åŒ¯å‡ºè‡³å‰ªè²¼ç°¿ + + + + Export to the file '%1' was successful. + Export to the file '%1' was successful. + + + + Export was successful. + 匯出æˆåŠŸã€‚ + + + + Could not export to file %1. File cannot be open for writting. + Could not export to file %1. File cannot be open for writting. + + + + ExportWorker + + + Error while exporting query results: %1 + ç•¶è¼‰å…¥æŸ¥è©¢çµæžœ %1 時發生錯誤 + + + + Error while counting data column width to export from query results: %1 + Error while counting data column width to export from query results: %1 + + + + + Could not parse %1 in order to export it. It will be excluded from the export output. + Could not parse %1 in order to export it. It will be excluded from the export output. + + + + Error while reading data to export from table %1: %2 + Error while reading data to export from table %1: %2 + + + + Error while counting data to export from table %1: %2 + Error while counting data to export from table %1: %2 + + + + Error while counting data column width to export from table %1: %2 + Error while counting data column width to export from table %1: %2 + + + + FunctionManagerImpl + + + Invalid number of arguments to function '%1'. Expected %2, but got %3. + Invalid number of arguments to function '%1'. Expected %2, but got %3. + + + + No such function registered in SQLiteStudio: %1(%2) + æ–¼ SQLiteStudio: %1(%2) ,找ä¸åˆ°æ­¤function 被註冊的紀錄 + + + + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + Function %1(%2) was registered with language %3, but the plugin supporting that language is not currently loaded. + + + + Invalid regular expression pattern: %1 + 無效的正è¦è¡¨ç¤ºå¼ï¼š%1 + + + + + Could not open file %1 for reading: %2 + 無法開啟檔案 %1 以讀å–:%2 + + + + Could not open file %1 for writting: %2 + 無法開啟檔案 %1 以寫入:%2 + + + + Error while writting to file %1: %2 + 當寫入檔案 %1:%2時發生錯誤。 + + + + Unsupported scripting language: %1 + 䏿”¯æ´çš„scripting language: %1 + + + + GenericExportPlugin + + + Could not initialize text codec for exporting. Using default codec: %1 + Could not initialize text codec for exporting. Using default codec: %1 + + + + ImportManager + + + Imported data to the table '%1' successfully. Number of imported rows: %2 + Imported data to the table '%1' successfully. Number of imported rows: %2 + + + + ImportWorker + + + No columns provided by the import plugin. + No columns provided by the import plugin. + + + + Could not start transaction in order to import a data: %1 + Could not start transaction in order to import a data: %1 + + + + Could not commit transaction for imported data: %1 + Could not commit transaction for imported data: %1 + + + + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + Table '%1' has less columns than there are columns in the data to be imported. Excessive data columns will be ignored. + + + + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + Table '%1' has more columns than there are columns in the data to be imported. Some columns in the table will be left empty. + + + + Could not create table to import to: %1 + Could not create table to import to: %1 + + + + + + Error while importing data: %1 + 載入資料 %1 時發生錯誤。 + + + + + Interrupted. + import process status update + 已中斷 + + + + Could not import data row number %1. The row was ignored. Problem details: %2 + Could not import data row number %1. The row was ignored. Problem details: %2 + + + + PluginManagerImpl + + + Cannot load plugin %1, because it's in conflict with plugin %2. + Cannot load plugin %1, because it's in conflict with plugin %2. + + + + Cannot load plugin %1, because its dependency was not loaded: %2. + Cannot load plugin %1, because its dependency was not loaded: %2. + + + + Cannot load plugin %1. Error details: %2 + Cannot load plugin %1. Error details: %2 + + + + Cannot load plugin %1 (error while initializing plugin). + Cannot load plugin %1 (error while initializing plugin). + + + + min: %1 + plugin dependency version + 最å°å€¼: %1 + + + + max: %1 + plugin dependency version + 最大值: %1 + + + + PopulateConstant + + + Constant + populate constant plugin name + 常數 + + + + PopulateConstantConfig + + + Constant value: + 常數值: + + + + PopulateDictionary + + + Dictionary + dictionary populating plugin name + å­—å…¸ + + + + PopulateDictionaryConfig + + + Dictionary file + 字典檔案 + + + + Pick dictionary file + 鏿“‡å­—典檔 + + + + Word separator + 文字分隔字元 + + + + Whitespace + 空格 + + + + Line break + æ›è¡Œ + + + + Method of using words + 使用字元的方法 + + + + Ordered + 排列 + + + + Randomly + 隨機 + + + + PopulateManager + + + Table '%1' populated successfully. + 表格 '%1' å·²æˆåŠŸæ“´å±• + + + + PopulateRandom + + + Random number + 亂數 + + + + PopulateRandomConfig + + + Constant prefix + 常數的字首文字 + + + + No prefix + 無字首文字 + + + + Minimum value + 最å°å€¼ + + + + Maximum value + 最大值 + + + + Constant suffix + 常數的字尾文字 + + + + No suffix + 無字尾文字 + + + + PopulateRandomText + + + Random text + 亂數文字 + + + + PopulateRandomTextConfig + + + Use characters from common sets: + 使用通用集åˆä¸­çš„å­—å…ƒ + + + + Minimum length + 最å°é•·åº¦ + + + + Letters from a to z. + 從a到z的文字 + + + + Alpha + 逿˜Žåº¦ + + + + Numbers from 0 to 9. + 從0到9的數字 + + + + Numeric + 數值 + + + + A whitespace, a tab and a new line character. + A whitespace, a tab and a new line character. + + + + Whitespace + 空格 + + + + Includes all above and all others. + 包å«ä»¥ä¸‹åŠæ‰€æœ‰. + + + + Binary + 二進制(Binary) + + + + Use characters from my custom set: + 使用自訂集åˆä¸­çš„å­—å…ƒ + + + + Maximum length + 最大長度 + + + + If you type some character multiple times, it's more likely to be used. + If you type some character multiple times, it's more likely to be used. + + + + PopulateScript + + + Script + 腳本 Script + + + + PopulateScriptConfig + + + Initialization code (optional) + åˆå§‹åŒ–的程å¼ç‰‡æ®µ (optional) + + + + Per step code + Per step code + + + + Language + 語言設定 + + + + Help + 幫助 + + + + PopulateSequence + + + Sequence + åºåˆ— + + + + PopulateSequenceConfig + + + Start value: + 起始值: + + + + Step: + 步骤: + + + + PopulateWorker + + + Could not start transaction in order to perform table populating. Error details: %1 + 當匯出table後無法ä¾åºå•Ÿå‹•交易. éŒ¯èª¤è¨Šæ¯ %1 + + + + Error while populating table: %1 + 當產生 table %1 時發生錯誤 + + + + Could not commit transaction after table populating. Error details: %1 + 當滙出table後無法commit transaction. éŒ¯èª¤è¨Šæ¯ %1 + + + + QObject + + + Could not open file '%1' for reading: %2 + 無法開啟檔案 '%1' 為了讀å–: %2 + + + + Could not open database: %1 + 無法開啟資料庫 %1。 + + + + Result set expired or no row available. + çµæžœé›†å·²éŽæœŸæˆ–是無有效的資料 + + + + + Could not load extension %1: %2 + 無法載入extension %1: %2 + + + + Could not run WAL checkpoint: %1 + Could not run WAL checkpoint: %1 + + + + Could not close database: %1 + 無法關閉資料庫 %1 + + + + Could not attach database %1: %2 + 無法附加資料庫 %1: %2 + + + + + Incomplete query. + ä¸å®Œæ•´çš„æŸ¥è©¢ + + + + Parser stack overflow + Parser æº¢ä½ + + + + Syntax error + 語法錯誤 + + + + Could not open dictionary file %1 for reading. + 無法開啟檔案 %1 以讀å–。 + + + + Dictionary file must exist and be readable. + 字典檔必須存在並且å¯è¢«è®€å– + + + + Maximum value cannot be less than minimum value. + 最大值ä¸èƒ½å°æ–¼æœ€å°å€¼ + + + + Maximum length cannot be less than minimum length. + 最大值ä¸èƒ½å°æ–¼æœ€å°å€¼ + + + + Custom character set cannot be empty. + 自訂字元集ä¸èƒ½ç‚ºç©ºç™½ + + + + Could not find plugin to support scripting language: %1 + 無法找到plugin以支æ´scripting language: %1 + + + + Error while executing populating initial code: %1 + 當產生initial code: %1 時發生錯誤 + + + + Error while executing populating code: %1 + 當產生code: %1 時發生錯誤 + + + + Select implementation language. + è«‹é¸æ“‡å¯¦ä½œçš„語系 + + + + Implementation code cannot be empty. + Implementation code ä¸èƒ½æ˜¯ç©ºçš„ + + + + Could not resolve data source for column: %1 + 無法解æžè³‡æ–™ä¾†æº, Column: %1 + + + + Could not resolve table for column '%1'. + 無法解æžtable, Column: '%1'. + + + + Could not initialize configuration file. Any configuration changes and queries history will be lost after application restart. Unable to create a file at following locations: %1. + 無法åˆå§‹åŒ–設定檔. 在應用程å¼é‡å•Ÿæ™‚ï¼Œä»»ä½•è¨­å®šæª”çš„ç•°å‹•åŠæŸ¥è©¢æ­·å²å°‡éºå¤±. 無法建立檔案在以下ä½ç½®: %1 + + + + General purpose + plugin category name + 通用目的說明 + + + + Database support + plugin category name + è³‡æ–™åº«æ”¯æ´ + + + + Code formatter + plugin category name + Code formatter + + + + Scripting languages + plugin category name + Scripting languages + + + + Exporting + plugin category name + 匯出中 + + + + Importing + plugin category name + 匯入中... + + + + Table populating + plugin category name + Table 產生中 + + + + Table %1 is referencing table %2, but the foreign key definition will not be updated for new table definition due to problems while parsing DDL of the table %3. + ç•¶è§£æž table %3 çš„DDL時候發生了錯誤,因為Table %1 åƒç…§ Table %2 ,影響 table 中的foreign key 無法æˆåŠŸæ›´æ–°ï¼Œ + + + + All columns indexed by the index %1 are gone. The index will not be recreated after table modification. + All columns indexed by the index %1 å·²éºå¤±. 在 table 修改後, æ­¤ index 䏦䏿œƒå»ºç«‹. + + + + There is problem with proper processing trigger %1. It may be not fully updated afterwards and will need your attention. + 當處ç†trigger %1時發生了å•é¡Œã€‚è«‹æ³¨æ„æ­¤ trigger å¯èƒ½å°šæœªæ›´æ–°å®Œæˆã€‚ + + + + All columns covered by the trigger %1 are gone. The trigger will not be recreated after table modification. + 被trigger %1 包å«çš„æ‰€æœ‰æ¬„ä½ç•°å‹•å‡å¤±æ•ˆ. 在 table 修改後, æ­¤ Trigger 亦無法被建立. + + + + Cannot not update trigger %1 according to table %2 modification. + 根據 table %2 的修改內容,導致無法更新 trigger %1 + + + + Cannot not update view %1 according to table %2 modifications. +The view will remain as it is. + å›  table %2 的異動導致無法更新 view %1 +view å°‡ä¿ç•™åŽŸå§‹å…§å®¹ + + + + + + There is a problem with updating an %1 statement within %2 trigger. One of the %1 substatements which might be referring to table %3 cannot be properly modified. Manual update of the trigger may be necessary. + æ›´æ–° trigger %2 中的SQL %1 時發生了錯誤,有å¯èƒ½æ˜¯SQL %1 中åƒè€ƒäº†æŸå¼µä¸èƒ½è¢«ä¿®æ”¹çš„ table %3. 若有需è¦çš„話,如有需è¦è«‹æ‰‹å‹•æ›´æ–°æ­¤trigger。 + + + + Could not parse DDL of the view to be created. Details: %1 + ç„¡æ³•è§£æžæ­¤viewçš„DDL, 詳細資料請見: %1 + + + + Parsed query is not CREATE VIEW. It's: %1 + è§£æžå¾Œçš„æŸ¥è©¢ä¸¦éžCREATE VIEW, 它是: %1 + + + + SQLiteStudio was unable to resolve columns returned by the new view, therefore it won't be able to tell which triggers might fail during the recreation process. + SQLiteStudio 無法從回傳的新view中解æžcolumns, 亦無法確èªtrigger 在é‡å»ºç¨‹åºä¸­å°‡å°Žè‡´å¤±æ•—的狀æ³. + + + + QueryExecutor + + + Execution interrupted. + 執行中斷 + + + + Database is not open. + 資料庫未開啟 + + + + Only one query can be executed simultaneously. + 儘有單一查詢å¯è¢«å†‹æ™‚執行 + + + + + An error occured while executing the count(*) query, thus data paging will be disabled. Error details from the database: %1 + 當執行count(*) 查詢時發生錯誤, 資料分é åŠŸèƒ½äº¦è¢«åœç”¨. 詳細錯誤資訊來自於資料庫: %1 + + + + SQLiteStudio was unable to extract metadata from the query. Results won't be editable. + SQLiteStudio無法從此查詢解æžmetadata. çµæžœäº¦ç„¡æ³•編輯 + + + + ScriptingQtDbProxy + + + No database available in current context, while called JavaScript's %1 command. + 當呼å«JavaScript %1 時 ,無法確èªè³‡æ–™åº«æ˜¯å¦æœ‰æ•ˆã€‚ + + + + Error from %1: %2 + 從 %1: %2 引發的錯誤 + + + + SqlFileExecutor + + + Could not execute SQL, because application has failed to start transaction: %1 + Could not execute SQL, because application has failed to start transaction: %1 + + + + Execution from file cancelled. Any queries executed so far have been rolled back. + Execution from file cancelled. Any queries executed so far have been rolled back. + + + + Could not open file '%1' for reading: %2 + 無法開啟檔案 '%1' 為了讀å–: %2 + + + + Could not execute SQL, because application has failed to commit the transaction: %1 + Could not execute SQL, because application has failed to commit the transaction: %1 + + + + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. + + + + Finished executing %1 queries in %2 seconds. + Finished executing %1 queries in %2 seconds. + + + + Could not execute SQL due to error. + Could not execute SQL due to error. + + + + SqlHistoryModel + + + Database + sql history header + 資料庫 + + + + Execution date + sql history header + 執行時間 + + + + Time spent + sql history header + 所花費的時間 + + + + Rows affected + sql history header + 被影響的列數 + + + + SQL + sql history header + SQL + + + + UpdateManager + + + Could not check for updates (%1). + ä¸èƒ½æª¢æŸ¥ for updates (%1). + + + diff --git a/SQLiteStudio3/create_macosx_bundle.sh b/SQLiteStudio3/create_macosx_bundle.sh index b6cdd3a..e1a8683 100755 --- a/SQLiteStudio3/create_macosx_bundle.sh +++ b/SQLiteStudio3/create_macosx_bundle.sh @@ -21,7 +21,7 @@ if [ "$?" -ne 0 ]; then exit 1 fi -cd $1/SQLiteStudio +cd "$1/SQLiteStudio" rm -rf SQLiteStudio.app/Contents/Frameworks rm -rf SQLiteStudio.app/Contents/PlugIns @@ -39,7 +39,7 @@ cp -RP styles/* SQLiteStudio.app/Contents/PlugIns/styles cp -RP lib*SQLiteStudio*.dylib SQLiteStudio.app/Contents/Frameworks # CLI paths -qtcore_path=`otool -L sqlitestudiocli | grep QtCore | awk '{print $1;}'` +qtcore_path=`otool -L sqlitestudiocli | awk '/QtCore/ {print $1;}'` new_qtcore_path="@rpath/QtCore.framework/Versions/5/QtCore" cp -P sqlitestudiocli SQLiteStudio.app/Contents/MacOS @@ -57,7 +57,7 @@ install_name_tool -change libsqlite3.0.dylib "@rpath/libsqlite3.0.dylib" SQLiteS cdir=`pwd` cd ../../../lib/ libdir=`pwd` -cd $cdir +cd "$cdir" echo "lib:" ls -l ../../../lib/ @@ -75,16 +75,16 @@ ls -l SQLiteStudio.app/Contents/Frameworks # Plugin paths function fixPluginPaths() { - for f in `ls $1` + for f in `ls "$1"` do - PLUGIN_FILE=$1/$f - if [ -f $PLUGIN_FILE ]; then + PLUGIN_FILE="$1/$f" + if [ -f "$PLUGIN_FILE" ]; then echo "Fixing paths for plugin $PLUGIN_FILE" - install_name_tool -change libcoreSQLiteStudio.1.dylib "@rpath/libcoreSQLiteStudio.1.dylib" $PLUGIN_FILE - install_name_tool -change libguiSQLiteStudio.1.dylib "@rpath/libguiSQLiteStudio.1.dylib" $PLUGIN_FILE + install_name_tool -change libcoreSQLiteStudio.1.dylib "@rpath/libcoreSQLiteStudio.1.dylib" "$PLUGIN_FILE" + install_name_tool -change libguiSQLiteStudio.1.dylib "@rpath/libguiSQLiteStudio.1.dylib" "$PLUGIN_FILE" fi - if [ -d $PLUGIN_FILE ]; then - fixPluginPaths $PLUGIN_FILE + if [ -d "$PLUGIN_FILE" ]; then + fixPluginPaths "$PLUGIN_FILE" fi done } @@ -93,7 +93,7 @@ fixPluginPaths SQLiteStudio.app/Contents/PlugIns function replaceInfo() { cdir=`pwd` echo Replacing Info.plist - cd $1/SQLiteStudio + cd "$1/SQLiteStudio" VERSION=`SQLiteStudio.app/Contents/MacOS/sqlitestudiocli -v | awk '{print $2}'` YEAR=`date '+%Y'` @@ -102,32 +102,43 @@ function replaceInfo() { echo "New plist:" cat Info.plist.new mv Info.plist.new Info.plist - cd $cdir + cd "$cdir" } if [ "$3" == "dmg" ]; then - replaceInfo $1 - $qt_deploy_bin SQLiteStudio.app -dmg + replaceInfo "$1" + "$qt_deploy_bin" SQLiteStudio.app -dmg elif [ "$3" == "dist" ]; then - replaceInfo $1 + replaceInfo "$1" - $qt_deploy_bin SQLiteStudio.app -dmg -executable=SQLiteStudio.app/Contents/MacOS/SQLiteStudio -always-overwrite -verbose=3 + "$qt_deploy_bin" SQLiteStudio.app -dmg -executable=SQLiteStudio.app/Contents/MacOS/SQLiteStudio -always-overwrite -verbose=3 - cd $1/SQLiteStudio + cd "$1/SQLiteStudio" VERSION=`SQLiteStudio.app/Contents/MacOS/sqlitestudiocli -v | awk '{print $2}'` mv SQLiteStudio.dmg sqlitestudio-$VERSION.dmg hdiutil attach sqlitestudio-$VERSION.dmg hdiutil detach /Volumes/SQLiteStudio - + + # Convert image to RW and attach hdiutil convert sqlitestudio-$VERSION.dmg -format UDRW -o sqlitestudio-rw-$VERSION.dmg hdiutil attach -readwrite sqlitestudio-rw-$VERSION.dmg - cp -RPf $libdir/libsqlite3.0.dylib /Volumes/SQLiteStudio/SQLiteStudio.app/Contents/Frameworks/ + + # Fix sqlite3 file in the image + cp -RPf "$libdir/libsqlite3.0.dylib" /Volumes/SQLiteStudio/SQLiteStudio.app/Contents/Frameworks/ + + # Fix python dependencies in the image + rm -f /Volumes/SQLiteStudio/SQLiteStudio.app/Contents/Frameworks/libpython* + rm -f /Volumes/SQLiteStudio/SQLiteStudio.app/Contents/Frameworks/libint* + install_name_tool -change "@loader_path/../Frameworks/libpython3.9.dylib" libpython3.9.dylib /Volumes/SQLiteStudio/SQLiteStudio.app/Contents/PlugIns/libScriptingPython.dylib + + # Detach RW image hdiutil detach /Volumes/SQLiteStudio hdiutil compact sqlitestudio-rw-$VERSION.dmg + # Convert image back to RO and compressed rm -f sqlitestudio-$VERSION.dmg hdiutil convert sqlitestudio-rw-$VERSION.dmg -format UDZO -o sqlitestudio-$VERSION.dmg rm -f sqlitestudio-rw-$VERSION.dmg @@ -139,6 +150,6 @@ elif [ "$3" == "dist" ]; then echo "Done." else - replaceInfo $1 - $qt_deploy_bin SQLiteStudio.app + "$qt_deploy_bin" SQLiteStudio.app + replaceInfo "$1" fi diff --git a/SQLiteStudio3/create_source_dist.sh b/SQLiteStudio3/create_source_dist.sh index 8fb9d4b..e631d3a 100755 --- a/SQLiteStudio3/create_source_dist.sh +++ b/SQLiteStudio3/create_source_dist.sh @@ -24,7 +24,7 @@ gzip -9 ../sqlitestudio-$VERSION.tar zip -r ../sqlitestudio-$VERSION.zip SQLiteStudio3 Plugins -cd $OLDDIR +cd "$OLDDIR" mv $TEMP/sqlitestudio-$VERSION.zip ../output mv $TEMP/sqlitestudio-$VERSION.tar.gz ../output @@ -35,4 +35,4 @@ rm -rf $TEMP echo "Source packages stored in `pwd`" -cd $OLDDIR +cd "$OLDDIR" diff --git a/SQLiteStudio3/dirs.pri b/SQLiteStudio3/dirs.pri deleted file mode 100644 index 777c640..0000000 --- a/SQLiteStudio3/dirs.pri +++ /dev/null @@ -1,50 +0,0 @@ -OUTPUT_DIR_NAME = output -export(OUTPUT_DIR_NAME) - -DESTDIR = $$PWD/../$$OUTPUT_DIR_NAME/SQLiteStudio -OBJECTS_DIR = $$PWD/../$$OUTPUT_DIR_NAME/build -MOC_DIR = $$PWD/../$$OUTPUT_DIR_NAME/build -UI_DIR = $$PWD/../$$OUTPUT_DIR_NAME/build - -LIBS += -L$$DESTDIR - -macx: { - QMAKE_CXXFLAGS += -Wno-gnu-zero-variadic-macro-arguments -Wno-overloaded-virtual - INCLUDEPATH += $$PWD/../../include - LIBS += -L$$PWD/../../lib -} - -win32: { - INCLUDEPATH += $$PWD/../../include $$PWD/../../include/quazip - LIBS += -L$$PWD/../../lib -} - -INCLUDEPATH += $$PWD/coreSQLiteStudio -DEPENDPATH += $$PWD/coreSQLiteStudio - -contains(QT, gui): { - INCLUDEPATH += $$PWD/guiSQLiteStudio $$PWD/../$$OUTPUT_DIR_NAME/build/guiSQLiteStudio - DEPENDPATH += $$PWD/guiSQLiteStudio -} - -win32|macx: { - CONFIG += portable -} - -portable { - QMAKE_LFLAGS += -Wl,-rpath,. - linux: { - LIBS += -L$$DESTDIR/lib - } -} - -unix: { - isEmpty(LIBDIR) { - LIBDIR = /usr/lib - } - export(LIBDIR) - isEmpty(BINDIR) { - BINDIR = /usr/bin - } - export(BINDIR) -} diff --git a/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.h b/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.h index 8ee4636..2174f54 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.h +++ b/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.h @@ -27,8 +27,8 @@ class DataWidgetMapper : public QObject private: struct MappingEntry { - QWidget* widget = nullptr; - int columnIndex = 0; + QWidget* widget = nullptr; + int columnIndex = 0; QString propertyName; }; diff --git a/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.cpp b/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.cpp index 303a37a..273aab3 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.cpp @@ -8,6 +8,7 @@ DbComboBox::DbComboBox(QWidget* parent) : QComboBox(parent) dbComboModel->setCombo(this); setModel(dbComboModel); setEditable(false); + connect(dbComboModel, SIGNAL(listCleared()), this, SLOT(handleListCleared())); } DbListModel* DbComboBox::getModel() const @@ -24,3 +25,8 @@ Db* DbComboBox::currentDb() const { return dbComboModel->getDb(currentIndex()); } + +void DbComboBox::handleListCleared() +{ + emit currentTextChanged(QString()); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.h b/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.h index 39814a6..602c20e 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.h +++ b/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.h @@ -9,6 +9,8 @@ class Db; class DbComboBox : public QComboBox { + Q_OBJECT + public: explicit DbComboBox(QWidget* parent = nullptr); @@ -18,6 +20,9 @@ class DbComboBox : public QComboBox private: DbListModel* dbComboModel = nullptr; + + private slots: + void handleListCleared(); }; #endif // DBCOMBOBOX_H diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp b/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp index 1203582..bb49985 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp @@ -129,7 +129,7 @@ void ExtActionContainer::createAction(int action, QAction* qAction, const QObjec qAction->setParent(owner); actionMap[action] = qAction; - QObject::connect(qAction, SIGNAL(triggered()), receiver, slot); + QObject::connect(qAction, SIGNAL(triggered(bool)), receiver, slot); container->addAction(qAction); } diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.h b/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.h index 159d4e5..dbec385 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.h +++ b/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.h @@ -1,7 +1,6 @@ #ifndef extactionCONTAINER_H #define extactionCONTAINER_H -#include "iconmanager.h" #include "config_builder.h" #include "extactionprototype.h" #include @@ -18,6 +17,7 @@ class QActionGroup; class QToolBar; class QSignalMapper; class QMenu; +class Icon; #define CFG_SHORTCUTS_METANAME "Shortcuts" @@ -58,7 +58,21 @@ class QMenu; } \ } -#define GET_SHORTCUTS(Type) ExtActionContainer::getAllShortcutSequences(Cfg::getShortcuts##Type##Instance()->ShortcutsCategory##Type) +/** + * @def Finds shortcut config category instance. + * Finds CfgCategory containing CfgEntry instances of all shortcuts defined for class \arg Type. + * For example: GET_SHORTCUTS_CATEGORY(EditorWindow)->getTitle() + * @return CfgCategory instance of a shortcuts configuration used for specified class. + */ +#define GET_SHORTCUTS_CATEGORY(Type) Cfg::getShortcuts##Type##Instance()->ShortcutsCategory##Type + +/** + * @def Finds shortcut config entry instance. + * Finds CfgEntry used to store shortcut for enumerated action with \arg ActionName in the class \arg Type. + * For example: GET_SHORTCUT_ENTRY(EditorWindow, EXEC_QUERY)->get().toString() + * @return CfgEntry instance of a shortcut config entry. + */ +#define GET_SHORTCUT_ENTRY(Type, ActionName) Cfg::getShortcuts##Type##Instance()->ShortcutsCategory##Type.getEntryByName(#ActionName) class GUI_API_EXPORT ExtActionContainer { @@ -224,7 +238,7 @@ void ExtActionContainer::removeAction(ExtActionPrototype* action, int toolbar) return; ActionDetails* dets = nullptr; - for (ActionDetails* d : extraActions[clsName][toolbar]) + for (ActionDetails*& d : extraActions[clsName][toolbar]) { if (d->action == action) { @@ -248,7 +262,7 @@ QList ExtActionContainer::getInstances() { QList typedInstances; T* typedInstance = nullptr; - for (ExtActionContainer* instance : instances) + for (ExtActionContainer*& instance : instances) { typedInstance = dynamic_cast(instance); if (typedInstance) diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp b/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp index 2ac6531..325e93c 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp @@ -56,15 +56,21 @@ void ExtLineEdit::setClearButtonEnabled(bool enable) return; } connect(clearAction, SIGNAL(triggered()), this, SLOT(checkForValueErased())); + connect(this, SIGNAL(textEdited(QString)), this, SLOT(checkForValueErased(QString))); } } void ExtLineEdit::checkForValueErased() { - if (text().isEmpty()) - return; + nextClearingIsFromButton = true; +} + +void ExtLineEdit::checkForValueErased(const QString& text) +{ + if (nextClearingIsFromButton && text.isEmpty()) + emit valueErased(); - emit valueErased(); + nextClearingIsFromButton = false; } bool ExtLineEdit::getExpanding() const diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.h b/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.h index df2eaf6..81f189a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.h +++ b/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.h @@ -34,10 +34,12 @@ class GUI_API_EXPORT ExtLineEdit : public QLineEdit bool expanding = false; int expandingMinWidth = 0; int expandingMaxWidth = -1; + bool nextClearingIsFromButton = false; private slots: void handleTextChanged(); void checkForValueErased(); + void checkForValueErased(const QString &text); signals: void valueErased(); diff --git a/SQLiteStudio3/guiSQLiteStudio/common/immediatetooltip.cpp b/SQLiteStudio3/guiSQLiteStudio/common/immediatetooltip.cpp new file mode 100644 index 0000000..45531be --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/common/immediatetooltip.cpp @@ -0,0 +1,43 @@ +#include "immediatetooltip.h" +#include "common/unused.h" +#include +#include +#include +#include + +ImmediateTooltip::ImmediateTooltip(QWidget* parent) + : QObject(parent) +{ + parent->setMouseTracking(true); + parent->installEventFilter(this); +} + +bool ImmediateTooltip::eventFilter(QObject* obj, QEvent* event) +{ + UNUSED(obj); + switch (event->type()) + { + case QEvent::Enter: + { + QEnterEvent* e = dynamic_cast(event); + QToolTip::showText(e->globalPos(), toolTip); + break; + } + case QEvent::Leave: + QToolTip::hideText(); + break; + default: + break; + } + return false; +} + +const QString& ImmediateTooltip::getToolTip() const +{ + return toolTip; +} + +void ImmediateTooltip::setToolTip(const QString& newToolTip) +{ + toolTip = newToolTip; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/common/immediatetooltip.h b/SQLiteStudio3/guiSQLiteStudio/common/immediatetooltip.h new file mode 100644 index 0000000..27ffacc --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/common/immediatetooltip.h @@ -0,0 +1,22 @@ +#ifndef IMMEDIATETOOLTIP_H +#define IMMEDIATETOOLTIP_H + +#include + +class ImmediateTooltip : public QObject +{ + Q_OBJECT + public: + explicit ImmediateTooltip(QWidget *parent = nullptr); + + const QString& getToolTip() const; + void setToolTip(const QString& newToolTip); + + protected: + bool eventFilter(QObject *obj, QEvent *event) override; + + private: + QString toolTip; +}; + +#endif // IMMEDIATETOOLTIP_H diff --git a/SQLiteStudio3/guiSQLiteStudio/common/mouseshortcut.cpp b/SQLiteStudio3/guiSQLiteStudio/common/mouseshortcut.cpp new file mode 100644 index 0000000..51fb524 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/common/mouseshortcut.cpp @@ -0,0 +1,77 @@ +#include "mouseshortcut.h" +#include +#include +#include + +MouseShortcut::MouseShortcut(ClickType type, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QObject* parent) + : QObject(parent), type(type), buttons(buttons), modifiers(modifiers) +{ +} + +MouseShortcut* MouseShortcut::forButton(ClickType type, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QObject* receiver, const char* slot, QObject* parent) +{ + MouseShortcut* instance = new MouseShortcut(type, buttons, modifiers, parent); + connect(instance, SIGNAL(activated(QPoint)), receiver, slot); + parent->installEventFilter(instance); + return instance; +} + +MouseShortcut* MouseShortcut::forWheel(Qt::KeyboardModifiers modifiers, QObject* parent) +{ + return new MouseShortcut(Wheel, Qt::MouseButtons(), modifiers, parent); +} + +MouseShortcut* MouseShortcut::forWheel(Qt::KeyboardModifiers modifiers, QObject* parent, const char* slot) +{ + MouseShortcut* instance = new MouseShortcut(Wheel, Qt::MouseButtons(), modifiers, parent); + connect(instance, SIGNAL(wheelActivated(int)), parent, slot); + parent->installEventFilter(instance); + return instance; +} + +MouseShortcut* MouseShortcut::forWheel(Qt::KeyboardModifiers modifiers, QObject* receiver, const char* slot, QObject* parent) +{ + MouseShortcut* instance = new MouseShortcut(Wheel, Qt::MouseButtons(), modifiers, parent); + connect(instance, SIGNAL(wheelActivated(int)), receiver, slot); + parent->installEventFilter(instance); + return instance; +} + +void MouseShortcut::enableDebug() +{ + debug = true; +} + +bool MouseShortcut::eventFilter(QObject* object, QEvent* event) +{ + if (debug && event->type() != QEvent::Paint) + qDebug() << event; + + if ((event->type() == QEvent::MouseButtonPress && type == SingleClick && attributesMatch(event)) || + (event->type() == QEvent::MouseButtonDblClick && type == DoubleClick && attributesMatch(event))) + { + emit activated(dynamic_cast(event)->globalPos()); + return true; + } + + if (event->type() == QEvent::Wheel && type == Wheel) + { + QWheelEvent* wheelEvent = dynamic_cast(event); + if (modifiers == wheelEvent->modifiers()) + { + emit wheelActivated(wheelEvent->angleDelta().y()); + return true; + } + } + + return QObject::eventFilter(object, event); +} + +bool MouseShortcut::attributesMatch(QEvent* event) +{ + QMouseEvent* mouseEvent = dynamic_cast(event); + if (!mouseEvent) + return false; + + return (buttons.testFlag(mouseEvent->button())) && (modifiers == mouseEvent->modifiers()); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/common/mouseshortcut.h b/SQLiteStudio3/guiSQLiteStudio/common/mouseshortcut.h new file mode 100644 index 0000000..e400a24 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/common/mouseshortcut.h @@ -0,0 +1,59 @@ +#ifndef MOUSESHORTCUT_H +#define MOUSESHORTCUT_H + +#include + +class MouseShortcut : public QObject +{ + Q_OBJECT + + public: + enum ClickType + { + SingleClick, + DoubleClick, + Wheel + }; + + static MouseShortcut* forButton(MouseShortcut::ClickType type, + Qt::MouseButtons buttons, + Qt::KeyboardModifiers modifiers, + QObject* receiver, + const char* slot, + QObject *parent); + + static MouseShortcut* forWheel(Qt::KeyboardModifiers modifiers, + QObject *parent = 0); + + static MouseShortcut* forWheel(Qt::KeyboardModifiers modifiers, + QObject *parent, + const char *slot); + static MouseShortcut* forWheel(Qt::KeyboardModifiers modifiers, + QObject* receiver, + const char* slot, + QObject* parent); + + void enableDebug(); + + protected: + MouseShortcut(MouseShortcut::ClickType type, + Qt::MouseButtons buttons, + Qt::KeyboardModifiers modifiers, + QObject *parent = 0); + + bool eventFilter(QObject *object, QEvent *event); + + private: + bool attributesMatch(QEvent* event); + + MouseShortcut::ClickType type; + Qt::MouseButtons buttons; + Qt::KeyboardModifiers modifiers; + bool debug = false; + + signals: + void activated(const QPoint& pos); + void wheelActivated(int delta); +}; + +#endif // MOUSESHORTCUT_H diff --git a/SQLiteStudio3/guiSQLiteStudio/completer/completerview.cpp b/SQLiteStudio3/guiSQLiteStudio/completer/completerview.cpp index c154b14..a209c42 100644 --- a/SQLiteStudio3/guiSQLiteStudio/completer/completerview.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/completer/completerview.cpp @@ -1,6 +1,9 @@ #include "completerview.h" #include "completeritemdelegate.h" +#include "sqleditor.h" +#include #include +#include CompleterView::CompleterView(QWidget *parent) : QListView(parent) @@ -46,6 +49,11 @@ void CompleterView::focusOutEvent(QFocusEvent* e) void CompleterView::keyPressEvent(QKeyEvent* e) { + QKeySequence hotkey = GET_SHORTCUTS_CATEGORY(SqlEditor).COMPLETE.get(); + QKeySequence theKey = QKeySequence(e->key() | e->modifiers()); + if (hotkey == theKey) + return; + QString txt = e->text(); if (!txt.isEmpty() && txt[0].isPrint()) { diff --git a/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.cpp index 89a0848..68ca58d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.cpp @@ -4,16 +4,20 @@ #include "common/unused.h" #include "sqleditor.h" #include "common/utils_sql.h" +#include "services/codesnippetmanager.h" +#include "sqlitestudio.h" #include +#include #include +#include #include +#include CompleterWindow::CompleterWindow(SqlEditor *parent) : QDialog(parent, Qt::FramelessWindowHint), ui(new Ui::CompleterWindow), sqlEditor(parent) { - ui->setupUi(this); init(); } @@ -24,6 +28,11 @@ CompleterWindow::~CompleterWindow() void CompleterWindow::init() { + ui->setupUi(this); + + modeChangeShortcut = new QShortcut(GET_SHORTCUTS_CATEGORY(SqlEditor).COMPLETE.get(), this); + snippetSignalMapper = new QSignalMapper(this); + model = new CompleterModel(this); ui->list->setModel(model); model->setCompleterView(ui->list); @@ -36,9 +45,42 @@ void CompleterWindow::init() connect(ui->list, SIGNAL(backspace()), this, SIGNAL(backspacePressed())); connect(ui->list, SIGNAL(left()), this, SIGNAL(leftPressed())); connect(ui->list, SIGNAL(right()), this, SIGNAL(rightPressed())); + connect(modeChangeShortcut, SIGNAL(activated()), this, SLOT(modeChangeRequested())); + connect(ui->snippets, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(snippetDoubleClicked(QListWidgetItem*))); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + connect(snippetSignalMapper, SIGNAL(mappedInt(int)), this, SLOT(snippetHotkeyPressed(int))); +#else + connect(snippetSignalMapper, SIGNAL(mapped(int)), this, SLOT(snippetHotkeyPressed(int))); +#endif reset(); } +void CompleterWindow::refreshSnippets() +{ + ui->snippets->clear(); + for (QShortcut*& sc : snippetShortcuts) + delete sc; + + snippetShortcuts.clear(); + + int i = 0; + for (CodeSnippetManager::CodeSnippet* snip : CODESNIPPETS->getSnippets()) + { + ui->snippets->addItem(snip->name); + if (!snip->hotkey.isEmpty()) + { + QShortcut* shortcut = new QShortcut(snip->hotkey, ui->snippets); + snippetShortcuts << shortcut; + snippetSignalMapper->setMapping(shortcut, i); + connect(shortcut, SIGNAL(activated()), snippetSignalMapper, SLOT(map())); + } + i++; + } + + if (ui->snippets->count() > 0) + ui->snippets->setCurrentRow(0); +} + void CompleterWindow::reset() { model->clear(); @@ -110,7 +152,17 @@ bool CompleterWindow::immediateResolution() return false; } -ExpectedTokenPtr CompleterWindow::getSelected() +CompleterWindow::Mode CompleterWindow::getMode() const +{ + return static_cast(ui->modeStack->currentIndex()); +} + +QString CompleterWindow::getSnippetName() const +{ + return ui->snippets->currentItem()->text(); +} + +ExpectedTokenPtr CompleterWindow::getSelected() const { QModelIndex current = ui->list->currentIndex(); if (!current.isValid()) @@ -228,10 +280,42 @@ void CompleterWindow::currentRowChanged(const QModelIndex& current, const QModel ui->status->showMessage(getStatusMsg(current)); } +void CompleterWindow::modeChangeRequested() +{ + ui->modeStack->setCurrentIndex((ui->modeStack->currentIndex() + 1) % ui->modeStack->count()); + + switch (ui->modeStack->currentIndex()) + { + case SNIPPETS: + ui->status->showMessage(tr("Insert a code snippet")); + refreshSnippets(); + break; + case CODE: + ui->status->showMessage(getStatusMsg(ui->list->currentIndex())); + break; + } +} + +void CompleterWindow::snippetHotkeyPressed(int index) +{ + ui->snippets->setCurrentRow(index); + accept(); +} + +void CompleterWindow::snippetDoubleClicked(QListWidgetItem* item) +{ + UNUSED(item); + accept(); +} + void CompleterWindow::showEvent(QShowEvent*e) { + ui->modeStack->setCurrentIndex(0); QDialog::showEvent(e); // A hack for Gnome3 to give this widget a focus. Harmless for others. ui->list->activateWindow(); + + // Refresh hotkey if changed + modeChangeShortcut->setKey(GET_SHORTCUTS_CATEGORY(SqlEditor).COMPLETE.get()); } diff --git a/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.h b/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.h index ec7256c..6e1f087 100644 --- a/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.h @@ -10,27 +10,37 @@ namespace Ui { class CompleterWindow; } - +class QListWidgetItem; +class QSignalMapper; class CompleterModel; class QSizeGrip; class SqlEditor; +class QShortcut; class GUI_API_EXPORT CompleterWindow : public QDialog { Q_OBJECT public: + enum Mode + { + CODE, + SNIPPETS + }; + explicit CompleterWindow(SqlEditor* parent = 0); ~CompleterWindow(); void reset(); void setData(const CompletionHelper::Results& completionResults); void setDb(Db* db); - ExpectedTokenPtr getSelected(); + ExpectedTokenPtr getSelected() const; int getNumberOfCharsToRemove(); void shringFilterBy(int chars); void extendFilterBy(const QString& text); bool immediateResolution(); + Mode getMode() const; + QString getSnippetName() const; protected: void changeEvent(QEvent *e); @@ -42,6 +52,7 @@ class GUI_API_EXPORT CompleterWindow : public QDialog QString getStatusMsg(const QModelIndex& index); void updateFilter(); void init(); + void refreshSnippets(); Ui::CompleterWindow *ui = nullptr; CompleterModel* model = nullptr; @@ -49,11 +60,17 @@ class GUI_API_EXPORT CompleterWindow : public QDialog QString filter; Db* db = nullptr; bool wrappedFilter = false; + QShortcut* modeChangeShortcut = nullptr; + QList snippetShortcuts; + QSignalMapper* snippetSignalMapper = nullptr; private slots: void focusOut(); void doubleClicked(const QModelIndex& index); void currentRowChanged(const QModelIndex& current, const QModelIndex& previous); + void modeChangeRequested(); + void snippetHotkeyPressed(int index); + void snippetDoubleClicked(QListWidgetItem* item); signals: void textTyped(const QString& text); diff --git a/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.ui b/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.ui index d5ce987..8944640 100644 --- a/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.ui +++ b/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.ui @@ -16,18 +16,72 @@ true - + 0 - + + 0 + + + 0 + + + 0 + + 0 - - - true - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/configmapper.cpp b/SQLiteStudio3/guiSQLiteStudio/configmapper.cpp index b36d985..704104b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/configmapper.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/configmapper.cpp @@ -293,14 +293,14 @@ void ConfigMapper::applyConfigToWidget(QWidget* widget, const QHashgetFullKey()]; if (!configValue.isValid()) - configValue = cfgEntry->getDefultValue(); + configValue = cfgEntry->getDefaultValue(); } else if (cfgEntry->isPersistable()) { // In case this is a persistable config, we should have everything in the config hash, which is just one, initial database query. // If we don't, than we don't want to call get(), because it will cause one more query to the database for it. // We go with the default value. - configValue = cfgEntry->getDefultValue(); + configValue = cfgEntry->getDefaultValue(); } else { @@ -329,18 +329,36 @@ void ConfigMapper::applyConfigToWidget(QWidget* widget, CfgEntry* cfgEntry, cons void ConfigMapper::applyConfigDefaultValueToWidget(QWidget* widget) { - QString keyStr = widget->property(CFG_MODEL_PROPERTY).toString(); + CfgEntry* key = getConfigForWidget(widget); + if (!key) + { + qWarning() << "Asked to apply config value to widget" << widget + << "but it's config entry key was not found."; + return; + } + + applyConfigToWidget(widget, key, key->getDefaultValue()); +} + +CfgEntry* ConfigMapper::getConfigForWidget(QWidget* widget) +{ + QString keyStr = getConfigFullKeyForWidget(widget); QHash allConfigEntries = getAllConfigEntries(); if (!allConfigEntries.contains(keyStr)) { - qWarning() << "Asked to apply config value to widget" << widget << "but it's config entry key was not found:" << keyStr; - return; + qWarning() << "Config entry with key not found:" << keyStr; + return nullptr; } - CfgEntry* key = allConfigEntries[keyStr]; - applyConfigToWidget(widget, key, key->getDefultValue()); + return allConfigEntries[keyStr]; } +QString ConfigMapper::getConfigFullKeyForWidget(QWidget* widget) +{ + return widget->property(CFG_MODEL_PROPERTY).toString(); +} + + void ConfigMapper::handleSpecialWidgets(QWidget* widget, const QHash& allConfigEntries) { handleConfigComboBox(widget, allConfigEntries); @@ -372,7 +390,7 @@ bool ConfigMapper::applyCustomConfigToWidget(CfgEntry* key, QWidget* widget, con handlers += internalCustomConfigWidgets; handlers += PLUGINS->getLoadedPlugins(); - for (CustomConfigWidgetPlugin* handler : handlers) + for (CustomConfigWidgetPlugin*& handler : handlers) { if (handler->isConfigForWidget(key, widget)) { @@ -389,11 +407,11 @@ bool ConfigMapper::connectCustomNotifierToWidget(QWidget* widget, CfgEntry* cfgE handlers += internalCustomConfigWidgets; handlers += PLUGINS->getLoadedPlugins(); - for (CustomConfigWidgetPlugin* handler : handlers) + for (CustomConfigWidgetPlugin*& handler : handlers) { if (handler->isConfigForWidget(cfgEntry, widget)) { - connect(widget, handler->getModifiedNotifier(), this, SIGNAL(modified())); + connect(widget, handler->getModifiedNotifier(), this, SLOT(handleCustomModified())); if (widget->property(CFG_NOTIFY_PROPERTY).isValid() && widget->property(CFG_NOTIFY_PROPERTY).toBool()) connect(widget, handler->getModifiedNotifier(), this, SLOT(uiConfigEntryChanged())); @@ -430,7 +448,7 @@ bool ConfigMapper::saveCustomConfigFromWidget(QWidget* widget, CfgEntry* key) handlers += internalCustomConfigWidgets; handlers += PLUGINS->getLoadedPlugins(); - for (CustomConfigWidgetPlugin* plugin : handlers) + for (CustomConfigWidgetPlugin*& plugin : handlers) { if (plugin->isConfigForWidget(key, widget)) { @@ -470,7 +488,7 @@ QHash ConfigMapper::getAllConfigEntries() if (allEntries.isEmpty()) { QString key; - for (CfgMain* cfgMain : cfgMainList) + for (CfgMain*& cfgMain : cfgMainList) { QHashIterator catIt(cfgMain->getCategories()); while (catIt.hasNext()) @@ -627,7 +645,13 @@ void ConfigMapper::handleModified() handleDependencyChange(widget); - emit modified(); + emit modified(widget); +} + +void ConfigMapper::handleCustomModified() +{ + QWidget* widget = dynamic_cast(sender()); + emit modified(widget); } void ConfigMapper::entryChanged(const QVariant& newValue) diff --git a/SQLiteStudio3/guiSQLiteStudio/configmapper.h b/SQLiteStudio3/guiSQLiteStudio/configmapper.h index ca70918..64b17ec 100644 --- a/SQLiteStudio3/guiSQLiteStudio/configmapper.h +++ b/SQLiteStudio3/guiSQLiteStudio/configmapper.h @@ -24,6 +24,9 @@ class GUI_API_EXPORT ConfigMapper : public QObject void saveFromWidget(QWidget* widget, bool noTransaction = false); void setInternalCustomConfigWidgets(const QList& value); bool isPersistant() const; + void applyConfigDefaultValueToWidget(QWidget *widget); + QList getAllConfigWidgets(QWidget* parent); + CfgEntry* getConfigForWidget(QWidget* widget); /** * @brief Scans widget and binds widgets to proper config objects. @@ -74,11 +77,11 @@ class GUI_API_EXPORT ConfigMapper : public QObject void clearExtraWidgets(); void ignoreWidget(QWidget* w); void removeIgnoredWidget(QWidget* w); + void saveFromWidget(QWidget* widget, CfgEntry* cfgEntry); private: void applyConfigToWidget(QWidget *widget, const QHash& allConfigEntries, const QHash &config); void applyConfigToWidget(QWidget *widget, CfgEntry* cfgEntry, const QVariant& configValue); - void applyConfigDefaultValueToWidget(QWidget *widget); void handleSpecialWidgets(QWidget *widget, const QHash& allConfigEntries); void handleConfigComboBox(QWidget *widget, const QHash& allConfigEntries); void connectCommonNotifierToWidget(QWidget *widget, CfgEntry* key); @@ -86,7 +89,6 @@ class GUI_API_EXPORT ConfigMapper : public QObject void applyCommonConfigToWidget(QWidget *widget, const QVariant& value, CfgEntry* cfgEntry); bool applyCustomConfigToWidget(CfgEntry* key, QWidget *widget, const QVariant& value); void saveWidget(QWidget* widget, const QHash& allConfigEntries); - void saveFromWidget(QWidget* widget, CfgEntry* cfgEntry); void saveCommonConfigFromWidget(QWidget *widget, CfgEntry* key); bool saveCustomConfigFromWidget(QWidget *widget, CfgEntry* key); QVariant getCommonConfigValueFromWidget(QWidget *widget, CfgEntry* key, bool& ok); @@ -96,11 +98,11 @@ class GUI_API_EXPORT ConfigMapper : public QObject CfgEntry* getConfigEntry(QWidget* widget, const QHash& allConfigEntries); CfgEntry* getEntryForProperty(QWidget* widget, const char* propertyName, const QHash& allConfigEntries); QHash getAllConfigEntries(); - QList getAllConfigWidgets(QWidget* parent); void handleDependencySettings(QWidget* widget); void handleBoolDependencySettings(const QString& boolDependency, QWidget* widget); void handleDependencyChange(QWidget* widget); bool handleBoolDependencyChange(QWidget* widget); + QString getConfigFullKeyForWidget(QWidget* widget); QWidget* configDialogWidget = nullptr; QList cfgMainList; @@ -122,13 +124,14 @@ class GUI_API_EXPORT ConfigMapper : public QObject private slots: void handleModified(); + void handleCustomModified(); void entryChanged(const QVariant& newValue); void uiConfigEntryChanged(); void updateConfigComboModel(const QVariant& value); void notifiableConfigKeyChanged(); signals: - void modified(); + void modified(QWidget* widget); void notifyEnabledWidgetModified(QWidget* widget, CfgEntry* key, const QVariant& value); }; diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/fkcombobox.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/fkcombobox.cpp new file mode 100644 index 0000000..b9ca7b7 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/fkcombobox.cpp @@ -0,0 +1,345 @@ +#include "fkcombobox.h" +#include "common/unused.h" +#include "datagrid/sqlqueryitem.h" +#include "schemaresolver.h" +#include "services/notifymanager.h" +#include "sqlquerymodel.h" +#include "sqlqueryview.h" +#include "uiconfig.h" +#include +#include +#include +#include + +FkComboBox::FkComboBox(QWidget* parent, int dropDownViewMinWidth) + : QComboBox(parent), dropDownViewMinWidth(dropDownViewMinWidth) +{ + init(); +} + +QString FkComboBox::getSqlForFkEditor(Db* db, SqlQueryModelColumn* columnModel, const QVariant& currentValue) +{ + static_qstring(sql, "SELECT %4, %1 FROM %2%3"); + static_qstring(currValueTpl, "(%1 == %2) AS %3"); + static_qstring(currNullValueTpl, "(%1 IS NULL) AS %2"); + static_qstring(dbColTpl, "%1 AS %2"); + static_qstring(conditionTpl, "%1.%2 = %3.%4"); + static_qstring(conditionPrefixTpl, " WHERE %1"); + + QStringList selectedCols; + QStringList fkConditionTables; + QStringList fkConditionCols; + QStringList srcCols; + SchemaResolver resolver(db); + + QList fkList = columnModel->getFkConstraints(); + int i = 0; + QString src; + QString fullSrcCol; + QString col; + QString firstSrcCol; + QStringList usedNames; + for (SqlQueryModelColumn::ConstraintFk*& fk : fkList) + { + col = wrapObjIfNeeded(fk->foreignColumn); + src = wrapObjIfNeeded(fk->foreignTable); + if (i == 0) + { + firstSrcCol = col; + fullSrcCol = src + "." + col; + selectedCols << dbColTpl.arg(fullSrcCol, wrapObjIfNeeded(columnModel->column)); + } + + if (fkConditionTables.contains(src, Qt::CaseInsensitive)) + continue; + + srcCols = resolver.getTableColumns(src); + for (QString& srcCol : srcCols) + { + if (fk->foreignColumn.compare(srcCol, Qt::CaseInsensitive) == 0) + continue; // Exclude matching column. We don't want the same column several times. + + fullSrcCol = src + "." + wrapObjIfNeeded(srcCol); + selectedCols << fullSrcCol; + usedNames << srcCol; + } + + fkConditionCols << col; + fkConditionTables << src; + + i++; + } + + QStringList conditions; + QString firstSrc = fkConditionTables.first(); + QString firstCol = fkConditionCols.first(); + for (i = 1; i < fkConditionTables.size(); i++) + { + src = fkConditionTables[i]; + col = fkConditionCols[i]; + conditions << conditionTpl.arg(firstSrc, firstCol, src, col); + } + + QString conditionsStr; + if (!conditions.isEmpty()) { + conditionsStr = conditionPrefixTpl.arg(conditions.join(", ")); + } + + // Current value column (will be 1 for row which matches current cell value) + QString currValueColName = generateUniqueName("curr", usedNames); + QString currValueExpr = currentValue.isNull() ? + currNullValueTpl.arg(firstSrcCol, currValueColName) : + currValueTpl.arg(firstSrcCol, wrapValueIfNeeded(currentValue), currValueColName); + + return sql.arg( + selectedCols.join(", "), + fkConditionTables.join(", "), + conditionsStr, + currValueExpr + ); +} + +void FkComboBox::init(Db* db, SqlQueryModelColumn* columnModel) +{ + this->columnModel = columnModel; + comboModel->setDb(db); +} + +void FkComboBox::setValue(const QVariant& value) +{ + bool doExecQuery = (sourceValue != value || comboModel->getQuery().isNull()); + sourceValue = value; + setCurrentText(value.toString()); + if (!value.isNull() && isEditable()) + lineEdit()->selectAll(); + + if (doExecQuery) + { + comboModel->setQuery(getSql()); + if (!comboModel->getQuery().isNull()) + comboModel->executeQuery(); + } +} + +QVariant FkComboBox::getValue(bool* manualValueUsed, bool* ok) const +{ + manualValueUsed && (*manualValueUsed = false); + ok && (*ok = true); + SqlQueryModel* cbModel = dynamic_cast(model()); + if (cbModel->isExecutionInProgress() || !cbModel->isAllDataLoaded()) + { + ok && (*ok = false); + return QVariant(); + } + + int idx = currentIndex(); + QModelIndex cbCol0Index = cbModel->index(idx, 0); + QString cbText = currentText(); + bool customValue = false; + + if (currentIndex() > -1 && !cbModel->itemFromIndex(cbCol0Index)) + { + // Inserted QStandardItem by QComboBox, meaning custom value (out of dropdown model) + // With Qt 5.15 (maybe earlier) QComboBox started inserting QStandardItems and setting them as currentIndex. + // Here we're extracting this inserted value and remembering this is the custom value. + cbText = cbModel->data(cbCol0Index).toString(); + customValue = true; + } + + // Regardless if its preselected value or custom value, we need to honor empty=null setting + if (CFG_UI.General.KeepNullWhenEmptyValue.get() && sourceValue.isNull() && cbText.isEmpty()) + { + ok && (*ok = false); + return QVariant(); + } + + // Out of index? So it's custom value. Set it and it's done. + // If we deal with custom value inserted as item, we also just set it and that's it. + if (idx < 0 || idx >= cbModel->rowCount() || customValue) + { + manualValueUsed && (*manualValueUsed = true); + return cbText; + } + + // Otherwise we will have at least 2 columns. 1st column is hidden and is meta-column holding 1/0 (1 for value matching current cell value) + QList row = cbModel->getRow(idx); + if (row.size() < 2 || !row[1]) + { + // This happens when inexisting value is confirmed with "Enter" key, + // and rowCount() is apparently incremented, but items not yet. + // Very likely this was addressed in recent Qt versions (5.15 or a bit earlier) + // which resulted in value insertion and the "customValue" flag above in this method. + qCritical() << "Confirmed FK edition, but there is no combo item in the row for index" << idx << ", CB row count is" << cbModel->rowCount(); + manualValueUsed && (*manualValueUsed = true); + return cbText; + } + + return row[1]->getValue(); +} + +void FkComboBox::init() +{ + setEditable(true); + + // Prepare combo dropdown view. + comboView = new SqlQueryView(); + comboView->setSimpleBrowserMode(true); + comboView->setMaximumWidth(QGuiApplication::primaryScreen()->size().width()); + + connect(comboView->horizontalHeader(), &QHeaderView::sectionResized, this, [this/*, comboView, model*/](int, int, int) + { + // This line is supposed to check if the source SqlQueryModel has already finished loading, + // before updating combo geometry, but I'm not sure at the moment, why & how would that ever need to be checked. + // Leaving it here for now, in case some bug appears related to this. +// if (!model->isAllDataLoaded()) +// return; + + updateComboViewGeometry(false); + }); + + comboModel = new SqlQueryModel(comboView); + comboModel->setView(comboView); + + // When execution is done, update combo. + connect(comboModel, SIGNAL(aboutToLoadResults()), this, SLOT(fkDataAboutToLoad())); + connect(comboModel, SIGNAL(executionSuccessful()), this, SLOT(fkDataReady())); + connect(comboModel, SIGNAL(executionFailed(QString)), this, SLOT(fkDataFailed(QString))); + connect(this, SIGNAL(currentTextChanged(QString)), this, SLOT(notifyValueModified())); + + // Setup combo, model, etc. + setModel(comboModel); + setView(comboView); + setModelColumn(1); + view()->viewport()->installEventFilter(new FkComboShowFilter(this)); + view()->verticalScrollBar()->installEventFilter(new FkComboShowFilter(this)); + + comboModel->setHardRowLimit(MAX_ROWS_FOR_FK); + comboModel->setCellDataLengthLimit(FK_CELL_LENGTH_LIMIT); + comboModel->setAsyncMode(true); + + comboView->verticalHeader()->setVisible(false); + comboView->horizontalHeader()->setVisible(true); + comboView->setSelectionMode(QAbstractItemView::SingleSelection); + comboView->setSelectionBehavior(QAbstractItemView::SelectRows); +} + +void FkComboBox::updateComboViewGeometry(bool initial) const +{ + int wd = getFkViewHeaderWidth(true); + comboView->setMinimumWidth(qMin(qMax(dropDownViewMinWidth, wd), comboView->maximumWidth())); + + if (initial && wd < comboView->minimumWidth()) + { + // First time, upon showing up + int currentSize = comboView->horizontalHeader()->sectionSize(1); + int gap = comboView->minimumWidth() - wd; + comboView->horizontalHeader()->resizeSection(1, currentSize + gap); + } + + QWidget* container = comboView->parentWidget(); + if (container->width() > comboView->minimumWidth()) + { + container->setMaximumWidth(comboView->minimumWidth()); + container->resize(comboView->minimumWidth(), container->height()); + } +} + +int FkComboBox::getFkViewHeaderWidth(bool includeScrollBar) const +{ + int wd = comboView->horizontalHeader()->length(); + if (includeScrollBar && comboView->verticalScrollBar()->isVisible()) + wd += comboView->verticalScrollBar()->width(); + + return wd; +} + +void FkComboBox::fkDataAboutToLoad() +{ + beforeLoadValue = currentText(); + + // Not editable combo needs to keep track of initially pre-loading value by using source value + if (!isEditable() && beforeLoadValue.isNull() && !sourceValue.isNull()) + beforeLoadValue = sourceValue.toString(); +} + +void FkComboBox::fkDataReady() +{ + disableValueChangeNotifications = true; + + comboView->horizontalHeader()->setSectionHidden(0, true); + comboView->resizeColumnsToContents(); + comboView->resizeRowsToContents(); + + updateComboViewGeometry(true); + + if (comboModel->rowCount() > 0) + { + QModelIndex startIdx = comboModel->index(0, modelColumn()); + QModelIndex endIdx = comboModel->index(comboModel->rowCount() - 1, modelColumn()); + QModelIndexList idxList = comboModel->findIndexes(startIdx, endIdx, SqlQueryItem::DataRole::VALUE, beforeLoadValue, 1, true); + + if (idxList.size() > 0) + { + setCurrentIndex(idxList.first().row()); + } + else + { + setCurrentIndex(-1); + setEditText(beforeLoadValue); + } + } + else + setEditText(beforeLoadValue); + + disableValueChangeNotifications = false; +} + +void FkComboBox::fkDataFailed(const QString& errorText) +{ + notifyWarn(tr("Cannot edit this cell. Details: %1").arg(errorText)); +} + +void FkComboBox::notifyValueModified() +{ + if (disableValueChangeNotifications || !comboModel->isAllDataLoaded()) + return; + + oldValue = currentText(); + emit valueModified(); +} + +FkComboBox::FkComboShowFilter::FkComboShowFilter(FkComboBox* parentCombo) + : QObject(parentCombo) +{ +} + +bool FkComboBox::FkComboShowFilter::eventFilter(QObject* obj, QEvent* event) +{ + UNUSED(obj); + if (event->type() == QEvent::Show) + dynamic_cast(parent())->updateComboViewGeometry(true); + + return false; +} + +QString FkComboBox::getSql() const +{ + if (columnModel == nullptr) { + qWarning() << "Called FkComboBox::getSqlForFkEditor() without column model defined. Tried to setValue() before init()?"; + return QString(); + } + + return getSqlForFkEditor(comboModel->getDb(), columnModel, sourceValue); +} + +qlonglong FkComboBox::getRowCountForFkEditor(Db* db, const QString& query, bool* isError) +{ + static_qstring(tpl, "SELECT count(*) FROM (%1)"); + + QString sql = tpl.arg(query); + SqlQueryPtr result = db->exec(sql); + if (isError) + *isError = result->isError(); + + return result->getSingleCell().toLongLong(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/fkcombobox.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/fkcombobox.h new file mode 100644 index 0000000..8cb22a5 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/fkcombobox.h @@ -0,0 +1,60 @@ +#ifndef FKCOMBOBOX_H +#define FKCOMBOBOX_H + +#include + +class SqlQueryModelColumn; +class SqlQueryModel; +class SqlQueryView; +class Db; + +class FkComboBox : public QComboBox +{ + Q_OBJECT + + public: + FkComboBox(QWidget *parent, int dropDownViewMinWidth = -1); + + static QString getSqlForFkEditor(Db* db, SqlQueryModelColumn* columnModel, const QVariant& currentValue); + static qlonglong getRowCountForFkEditor(Db* db, const QString& query, bool* isError); + + static const qlonglong MAX_ROWS_FOR_FK = 10000L; + static const int FK_CELL_LENGTH_LIMIT = 30; + + void init(Db* db, SqlQueryModelColumn* columnModel); + void setValue(const QVariant& value); + QVariant getValue(bool* manualValueUsed = nullptr, bool* ok = nullptr) const; + + private: + class FkComboShowFilter : public QObject + { + public: + explicit FkComboShowFilter(FkComboBox* parentCombo); + bool eventFilter(QObject *obj, QEvent *event); + }; + + void init(); + void updateComboViewGeometry(bool initial) const; + int getFkViewHeaderWidth(bool includeScrollBar) const; + QString getSql() const; + + int dropDownViewMinWidth; + SqlQueryView* comboView = nullptr; + SqlQueryModel* comboModel = nullptr; + SqlQueryModelColumn* columnModel = nullptr; + QString beforeLoadValue; + QVariant sourceValue; + bool disableValueChangeNotifications = false; + QString oldValue; + + private slots: + void fkDataAboutToLoad(); + void fkDataReady(); + void fkDataFailed(const QString& errorText); + void notifyValueModified(); + + signals: + void valueModified(); +}; + +#endif // FKCOMBOBOX_H diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.cpp new file mode 100644 index 0000000..a1a6cee --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.cpp @@ -0,0 +1,156 @@ +#include "sqldatasourcequerymodel.h" +#include "common/utils_sql.h" + +SqlDataSourceQueryModel::SqlDataSourceQueryModel(QObject *parent) : + SqlQueryModel(parent) +{ +} + +QString SqlDataSourceQueryModel::getDatabase() const +{ + return database; +} + +SqlQueryModel::Features SqlDataSourceQueryModel::features() const +{ + return FILTERING; +} + +void SqlDataSourceQueryModel::updateTablesInUse(const QString& inUse) +{ + QString dbName = database; + if (database.toLower() == "main" || database.isEmpty()) + dbName = QString(); + + tablesInUse.clear(); + tablesInUse << DbAndTable(db, dbName, inUse); +} + +void SqlDataSourceQueryModel::applyFilter(const QString& value, FilterValueProcessor valueProc) +{ +// static_qstring(sql, "SELECT * FROM %1 WHERE %2"); + if (value.isEmpty()) + { + resetFilter(); + return; + } + + QStringList conditions; + for (SqlQueryModelColumnPtr& column : columns) + conditions << wrapObjIfNeeded(column->getAliasedName())+" "+valueProc(value); + +// setQuery(sql.arg(getDataSource(), conditions.join(" OR "))); + queryExecutor->setFilters(conditions.join(" OR ")); + executeQuery(); +} + +void SqlDataSourceQueryModel::applyFilter(const QStringList& values, FilterValueProcessor valueProc) +{ +// static_qstring(sql, "SELECT * FROM %1 WHERE %2"); + if (values.isEmpty()) + { + resetFilter(); + return; + } + + if (values.size() != columns.size()) + { + qCritical() << "Asked to per-column filter, but number columns" + << columns.size() << "is different than number of values" << values.size(); + return; + } + + QStringList conditions; + for (int i = 0, total = columns.size(); i < total; ++i) + { + if (values[i].isEmpty()) + continue; + + conditions << wrapObjIfNeeded(columns[i]->getAliasedName())+" "+valueProc(values[i]); + } + +// setQuery(sql.arg(getDataSource(), conditions.join(" AND "))); + queryExecutor->setFilters(conditions.join(" AND ")); + executeQuery(); +} + +QString SqlDataSourceQueryModel::stringFilterValueProcessor(const QString& value) +{ + static_qstring(pattern, "LIKE '%%1%'"); + return pattern.arg(escapeString(value)); +} + +QString SqlDataSourceQueryModel::strictFilterValueProcessor(const QString& value) +{ + static_qstring(pattern, "= '%1'"); + return pattern.arg(escapeString(value)); +} + +QString SqlDataSourceQueryModel::regExpFilterValueProcessor(const QString& value) +{ + static_qstring(pattern, "REGEXP '%1'"); + return pattern.arg(escapeString(value)); +} + +void SqlDataSourceQueryModel::applySqlFilter(const QString& value) +{ + if (value.isEmpty()) + { + resetFilter(); + return; + } + +// setQuery("SELECT * FROM "+getDataSource()+" WHERE "+value); + queryExecutor->setFilters(value); + executeQuery(); +} + +void SqlDataSourceQueryModel::applyStringFilter(const QString& value) +{ + applyFilter(value, &stringFilterValueProcessor); +} + +void SqlDataSourceQueryModel::applyStringFilter(const QStringList& values) +{ + applyFilter(values, &stringFilterValueProcessor); +} + +void SqlDataSourceQueryModel::applyRegExpFilter(const QString& value) +{ + applyFilter(value, ®ExpFilterValueProcessor); +} + +void SqlDataSourceQueryModel::applyRegExpFilter(const QStringList& values) +{ + applyFilter(values, ®ExpFilterValueProcessor); +} + +void SqlDataSourceQueryModel::applyStrictFilter(const QString& value) +{ + applyFilter(value, &strictFilterValueProcessor); +} + +void SqlDataSourceQueryModel::applyStrictFilter(const QStringList& values) +{ + applyFilter(values, &strictFilterValueProcessor); +} + +void SqlDataSourceQueryModel::resetFilter() +{ +// setQuery("SELECT * FROM "+getDataSource()); + queryExecutor->setFilters(QString()); + executeQuery(); +} + +QString SqlDataSourceQueryModel::getDatabasePrefix() +{ + if (database.isNull()) + return ""; // not "main.", because the "main." doesn't work for TEMP tables, such as sqlite_temp_master + + return wrapObjIfNeeded(database) + "."; +} + +QString SqlDataSourceQueryModel::getDataSource() +{ + return QString(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.h new file mode 100644 index 0000000..6f8f696 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.h @@ -0,0 +1,46 @@ +#ifndef SQLDATASOURCEQUERYMODEL_H +#define SQLDATASOURCEQUERYMODEL_H + +#include "sqlquerymodel.h" + +class SqlDataSourceQueryModel : public SqlQueryModel +{ + public: + explicit SqlDataSourceQueryModel(QObject *parent = 0); + + QString getDatabase() const; + Features features() const; + void updateTablesInUse(const QString& inUse); + + void applySqlFilter(const QString& value); + void applyStringFilter(const QString& value); + void applyStringFilter(const QStringList& values); + void applyRegExpFilter(const QString& value); + void applyRegExpFilter(const QStringList& values); + void applyStrictFilter(const QString& value); + void applyStrictFilter(const QStringList& values); + void resetFilter(); + + protected: + typedef std::function FilterValueProcessor; + + static QString stringFilterValueProcessor(const QString& value); + static QString strictFilterValueProcessor(const QString& value); + static QString regExpFilterValueProcessor(const QString& value); + + void applyFilter(const QString& value, FilterValueProcessor valueProc); + void applyFilter(const QStringList& values, FilterValueProcessor valueProc); + + QString getDatabasePrefix(); + + /** + * @brief Get the data source for this object. + * Default implementation returns an empty string. Working implementation + * (i.e. for a table) should return the data source string. + */ + virtual QString getDataSource(); + + QString database; +}; + +#endif // SQLDATASOURCEQUERYMODEL_H diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.cpp index ebca434..771e890 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.cpp @@ -55,7 +55,7 @@ void SqlQueryItem::setUncommitted(bool uncommitted) void SqlQueryItem::rollback() { - setValue(getOldValue(), getOldValueLimited(), true); + setValue(getOldValue(), true); setUncommitted(false); setDeletedRow(false); } @@ -126,7 +126,7 @@ QVariant SqlQueryItem::getValue() const return QStandardItem::data(DataRole::VALUE); } -void SqlQueryItem::setValue(const QVariant &value, bool limited, bool loadedFromDb) +void SqlQueryItem::setValue(const QVariant &value, bool loadedFromDb) { if (!valueSettingLock.tryLock()) { @@ -160,24 +160,14 @@ void SqlQueryItem::setValue(const QVariant &value, bool limited, bool loadedFrom QStandardItem::setData("x", DataRole::VALUE); QStandardItem::setData(newValue, DataRole::VALUE); - setLimitedValue(limited); setUncommitted(modified); - // Value for display (in a cell) will always be limited, for performance reasons - setValueForDisplay("x"); // the same trick as with the DataRole::VALUE - setValueForDisplay(newValue); - if (modified && getModel()) getModel()->itemValueEdited(this); valueSettingLock.unlock(); } -bool SqlQueryItem::isLimitedValue() const -{ - return QStandardItem::data(DataRole::LIMITED_VALUE).toBool(); -} - QVariant SqlQueryItem::getOldValue() const { return QStandardItem::data(DataRole::OLD_VALUE); @@ -188,31 +178,6 @@ void SqlQueryItem::setOldValue(const QVariant& value) QStandardItem::setData(value, DataRole::OLD_VALUE); } -bool SqlQueryItem::getOldValueLimited() const -{ - return QStandardItem::data(DataRole::OLD_VALUE_LIMITED).toBool(); -} - -void SqlQueryItem::setOldValueLimited(bool value) -{ - QStandardItem::setData(value, DataRole::OLD_VALUE_LIMITED); -} - -QVariant SqlQueryItem::getValueForDisplay() const -{ - return QStandardItem::data(DataRole::VALUE_FOR_DISPLAY); -} - -void SqlQueryItem::setValueForDisplay(const QVariant &value) -{ - QStandardItem::setData(value, DataRole::VALUE_FOR_DISPLAY); -} - -void SqlQueryItem::setLimitedValue(bool limited) -{ - QStandardItem::setData(QVariant(limited), DataRole::LIMITED_VALUE); -} - QVariant SqlQueryItem::adjustVariantType(const QVariant& value) { QVariant newValue; @@ -304,13 +269,11 @@ QString SqlQueryItem::getToolTip() const void SqlQueryItem::rememberOldValue() { setOldValue(getValue()); - setOldValueLimited(isLimitedValue()); } void SqlQueryItem::clearOldValue() { setOldValue(QVariant()); - setOldValueLimited(false); } SqlQueryModelColumn* SqlQueryItem::getColumn() const @@ -340,7 +303,7 @@ void SqlQueryItem::setData(const QVariant &value, int role) // -1 column is used by Qt for header items (ie. when setHeaderData() is called) // and we want this to mean that the value was loaded from db, because it forces // the value to be interpreted as not modified. - setValue(value, false, (column() == -1)); + setValue(value, (column() == -1)); return; } } @@ -364,7 +327,7 @@ QVariant SqlQueryItem::data(int role) const if (isDeletedRow()) return ""; - QVariant value = getValueForDisplay(); + QVariant value = getValue(); if (value.isNull()) return "NULL"; @@ -414,72 +377,3 @@ QVariant SqlQueryItem::data(int role) const return QStandardItem::data(role); } - -QString SqlQueryItem::loadFullData() -{ - SqlQueryModelColumn* col = getColumn(); - SqlQueryModel *model = getModel(); - Db* db = model->getDb(); - if (!db->isOpen()) - { - qWarning() << "Tried to load the data for a cell that refers to the already closed database."; - return tr("Cannot load the data for a cell that refers to the already closed database."); - } - - // Query - QString query; - QHash queryArgs; - if (col->editionForbiddenReason.size() > 0) - { - static_qstring(tpl, "SELECT %1 FROM (%2) LIMIT 1 OFFSET %3"); - - // The query - QString colName = !col->alias.isNull() ? col->alias : col->column; - if (colName.isNull()) - colName = col->displayName; - - QString column = wrapObjIfNeeded(colName); - query = tpl.arg(column, model->getQuery(), QString::number(index().row())); - } - else - { - static_qstring(tpl, "SELECT %1 FROM %2 WHERE %3"); - - // Column - QString column = wrapObjIfNeeded(col->column); - - // Db and table - QString source = wrapObjIfNeeded(col->table); - if (!col->database.isNull()) - source.prepend(wrapObjIfNeeded(col->database)+"."); - - // ROWID - RowIdConditionBuilder rowIdBuilder; - rowIdBuilder.setRowId(getRowId()); - QString rowId = rowIdBuilder.build(); - queryArgs = rowIdBuilder.getQueryArgs(); - - // The query - query = tpl.arg(column, source, rowId); - } - - // Get the data - SqlQueryPtr results = db->exec(query, queryArgs); - if (results->isError()) - return results->getErrorText(); - - setValue(results->getSingleCell(), false, true); - return QString(); -} - -QVariant SqlQueryItem::getFullValue() -{ - if (!isLimitedValue()) - return getValue(); - - QVariant originalValue = getValue(); - loadFullData(); - QVariant result = getValue(); - setValue(originalValue, true, !isUncommitted()); - return result; -} diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.h index 6ffddfc..0de01c1 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.h @@ -19,17 +19,14 @@ class GUI_API_EXPORT SqlQueryItem : public QObject, public QStandardItem { ROWID = 1001, VALUE = 1002, - LIMITED_VALUE = 1003, - COLUMN = 1004, - UNCOMMITTED = 1005, - COMMITTING_ERROR = 1006, - NEW_ROW = 1007, - DELETED = 1008, - OLD_VALUE = 1009, - JUST_INSERTED_WITHOUT_ROWID = 1010, - VALUE_FOR_DISPLAY = 1011, - COMMITTING_ERROR_MESSAGE = 1012, - OLD_VALUE_LIMITED = 1013 + COLUMN = 1003, + UNCOMMITTED = 1004, + COMMITTING_ERROR = 1005, + NEW_ROW = 1006, + DELETED = 1007, + OLD_VALUE = 1008, + JUST_INSERTED_WITHOUT_ROWID = 1009, + COMMITTING_ERROR_MESSAGE = 1010 }; }; @@ -62,32 +59,11 @@ class GUI_API_EXPORT SqlQueryItem : public QObject, public QStandardItem void setDeletedRow(bool isDeleted); QVariant getValue() const; - void setValue(const QVariant& value, bool limited = false, bool loadedFromDb = false); - bool isLimitedValue() const; + void setValue(const QVariant& value, bool loadedFromDb = false); QVariant getOldValue() const; void setOldValue(const QVariant& value); - bool getOldValueLimited() const; - void setOldValueLimited(bool value); - - QVariant getValueForDisplay() const; - void setValueForDisplay(const QVariant& value); - - /** - * @brief loadFullData Reloads entire value of the cell from database. - * @return QString() on sucess, or error string on failure. - */ - QString loadFullData(); - - /** - * @brief getFullValue Loads and returns full value from database, but keeps the original value. - * @return Full value, reloaded from database. - * Calls loadFullData(), then getValue() for the result, - * but just before returning - restores initial, limited value. - */ - QVariant getFullValue(); - SqlQueryModelColumn* getColumn() const; void setColumn(SqlQueryModelColumn* column); @@ -97,7 +73,6 @@ class GUI_API_EXPORT SqlQueryItem : public QObject, public QStandardItem QVariant data(int role = Qt::UserRole + 1) const; private: - void setLimitedValue(bool limited); QVariant adjustVariantType(const QVariant& value); QString getToolTip() const; void rememberOldValue(); diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.cpp index 1ebf06f..7b01084 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.cpp @@ -6,6 +6,7 @@ #include "sqlqueryview.h" #include "uiconfig.h" #include "common/utils_sql.h" +#include "fkcombobox.h" #include "schemaresolver.h" #include #include @@ -28,9 +29,6 @@ bool SqlQueryItemDelegate::warnedAboutHugeContents = false; SqlQueryItemDelegate::SqlQueryItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { - fullValueButtonOption.icon = ICONS.LOAD_FULL_VALUE; - fullValueButtonOption.iconSize = QSize(LOAD_FULL_VALUE_ICON_SIZE, LOAD_FULL_VALUE_ICON_SIZE); - fullValueButtonOption.state = QStyle::State_Enabled; } void SqlQueryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const @@ -44,111 +42,6 @@ void SqlQueryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & painter->setBrush(Qt::NoBrush); painter->drawRect(option.rect.x(), option.rect.y(), option.rect.width()-1, option.rect.height()-1); } - - if (item->isLimitedValue()) - { - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - QString text = displayText(item->getValue(), opt.locale); - int textWidth = opt.fontMetrics.horizontalAdvance(text); - int margin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, opt.widget) + 1; // from QCommonStyle source code - if (opt.rect.width() >= (textWidth + LOAD_FULL_VALUE_BUTTON_SIZE + margin)) - { - QStyleOptionButton button = fullValueButtonOption; - button.rect = getLoadFullValueButtonRegion(opt.rect); - button.state = QStyle::State_Enabled | QStyle::State_MouseOver; - if (lmbPressedOnButton) - button.state |= QStyle::State_Sunken | QStyle::State_Active; - - QApplication::style()->drawControl( - (mouseOverFullDataButton == index) ? QStyle::CE_PushButton : QStyle::CE_PushButtonLabel, - &button, painter); - } - } -} - -bool SqlQueryItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) -{ - switch (event->type()) - { - case QEvent::MouseButtonDblClick: - if (isOverFullValueButton(option.rect, dynamic_cast(event)) && isLimited(index)) - return true; - - break; - case QEvent::MouseButtonPress: - { - if (isOverFullValueButton(option.rect, dynamic_cast(event)) && isLimited(index)) - { - lmbPressedOnButton = true; - return true; - } - - break; - } - case QEvent::MouseButtonRelease: - if (lmbPressedOnButton && isOverFullValueButton(option.rect, dynamic_cast(event)) && isLimited(index)) - { - lmbPressedOnButton = false; - getItem(index)->loadFullData(); - return true; - } - lmbPressedOnButton = false; - break; - case QEvent::MouseMove: - { - bool isOverButton = isOverFullValueButton(option.rect, dynamic_cast(event)); - if (mouseOverFullDataButton.isValid() != isOverButton) - { - mouseOverFullDataButton = isOverButton ? index : QModelIndex(); - dynamic_cast(model)->getView()->update(index); - } - - if (!isOverButton && showingFullButtonTooltip) - { - QToolTip::hideText(); - showingFullButtonTooltip = false; - } - break; - } - default: - break; - } - - return QStyledItemDelegate::editorEvent(event, model, option, index); -} - -bool SqlQueryItemDelegate::shouldLoadFullData(const QRect& rect, QMouseEvent* event, const QModelIndex& index) -{ - return shouldLoadFullData(rect, event->x(), event->y(), index); -} - -bool SqlQueryItemDelegate::shouldLoadFullData(const QRect& rect, int x, int y, const QModelIndex& index) -{ - return isOverFullValueButton(rect, x, y) && isLimited(index); -} - -void SqlQueryItemDelegate::mouseLeftIndex(const QModelIndex& index) -{ - if (mouseOverFullDataButton == index) - mouseOverFullDataButton = QModelIndex(); -} - -bool SqlQueryItemDelegate::isLimited(const QModelIndex& index) -{ - return index.data(SqlQueryItem::DataRole::LIMITED_VALUE).toBool(); -} - -bool SqlQueryItemDelegate::helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index) -{ - if (shouldLoadFullData(option.rect, event->x(), event->y(), index)) - { - QToolTip::showText(view->mapToGlobal(event->pos() - QPoint(0, 15)), tr("Load remaining part of the value")); - showingFullButtonTooltip = true; - return true; - } - - return QStyledItemDelegate::helpEvent(event, view, option, index); } QWidget* SqlQueryItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const @@ -172,7 +65,7 @@ QWidget* SqlQueryItemDelegate::createEditor(QWidget* parent, const QStyleOptionV return nullptr; } - if (item->isLimitedValue() && !item->loadFullData().isNull() && model->isStructureOutOfDate()) + if (model->isStructureOutOfDate()) { notifyWarn(tr("Cannot edit this cell. Details: %1").arg(tr("Structure of this table has changed since last data was loaded. Reload the data to proceed."))); return nullptr; @@ -189,7 +82,12 @@ QString SqlQueryItemDelegate::displayText(const QVariant& value, const QLocale& UNUSED(locale); if (value.type() == QVariant::Double) - return doubleToString(value); + { + if (CFG_UI.General.UseSciFormatForDoubles.get()) + return value.toString(); + else + return doubleToString(value); + } return QStyledItemDelegate::displayText(value, locale); } @@ -220,7 +118,7 @@ void SqlQueryItemDelegate::setEditorDataForFk(QComboBox* cb, const QModelIndex& void SqlQueryItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { - QComboBox* cb = dynamic_cast(editor); + FkComboBox* cb = dynamic_cast(editor); QLineEdit* le = dynamic_cast(editor); if (cb) { setModelDataForFk(cb, model, index); @@ -234,29 +132,11 @@ void SqlQueryItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* mod queryModel->notifyItemEditionEnded(index); } -void SqlQueryItemDelegate::setModelDataForFk(QComboBox* cb, QAbstractItemModel* model, const QModelIndex& index) const +void SqlQueryItemDelegate::setModelDataForFk(FkComboBox* cb, QAbstractItemModel* model, const QModelIndex& index) const { - SqlQueryModel* cbModel = dynamic_cast(cb->model()); - if (cbModel->isExecutionInProgress() || !cbModel->isAllDataLoaded()) - return; - - int idx = cb->currentIndex(); - QModelIndex cbCol0Index = cbModel->index(idx, 0); - QString cbText = cb->currentText(); - bool customValue = false; - - if (cb->currentIndex() > -1 && !cbModel->itemFromIndex(cbCol0Index)) - { - // Inserted QStandardItem by QComboBox, meaning custom value (out of dropdown model) - // With Qt 5.15 (maybe earlier) QComboBox started inserting QStandardItems and setting them as currentIndex. - // Here we're extracting this inserted value and remembering this is the custom value. - cbText = cbModel->data(cbCol0Index).toString(); - customValue = true; - } - - // Regardless if its preselected value or custom value, we need to honor empty=null setting - if (CFG_UI.General.KeepNullWhenEmptyValue.get() && model->data(index, Qt::EditRole).isNull() && cbText.isEmpty()) - return; + bool manualValue = false; + bool ok = false; + QVariant value = cb->getValue(&manualValue, &ok); SqlQueryModel* dataModel = dynamic_cast(model); SqlQueryItem* theItem = dataModel->itemFromIndex(index); @@ -266,33 +146,19 @@ void SqlQueryItemDelegate::setModelDataForFk(QComboBox* cb, QAbstractItemModel* if (!theItem) { qCritical() << "Confirmed FK edition, but there is no SqlQueryItem for which this was triggered!" << index; - model->setData(index, cbText, Qt::EditRole); + model->setData(index, value, Qt::EditRole); return; } // Out of index? So it's custom value. Set it and it's done. // If we deal with custom value inserted as item, we also just set it and that's it. - if (idx < 0 || idx >= cbModel->rowCount() || customValue) - { - theItem->setValue(cbText); - return; - } - - // Otherwise we will have at least 2 columns. 1st column is hidden and is meta-column holding 1/0 (1 for value matching current cell value) - QList row = cbModel->getRow(idx); - if (row.size() < 2 || !row[1]) + if (manualValue) { - // This happens when inexisting value is confirmed with "Enter" key, - // and rowCount() is apparently incremented, but items not yet. - // Very likely this was addressed in recent Qt versions (5.15 or a bit earlier) - // which resulted in value insertion and the "customValue" flag above in this method. - qCritical() << "Confirmed FK edition, but there is no combo item in the row for index" << idx << ", the item is" << theItem << ", CB row count is" << cbModel->rowCount(); - theItem->setValue(cbText); + theItem->setValue(value); return; } - QVariant comboData = row[1]->getFullValue(); - theItem->setValue(comboData); + theItem->setValue(value); } void SqlQueryItemDelegate::setModelDataForLineEdit(QLineEdit* editor, QAbstractItemModel* model, const QModelIndex& index) const @@ -328,7 +194,7 @@ void SqlQueryItemDelegate::setModelDataForLineEdit(QLineEdit* editor, QAbstractI void SqlQueryItemDelegate::setEditorDataForLineEdit(QLineEdit* le, const QModelIndex& index) const { QVariant value = index.data(Qt::EditRole); - if (value.userType() == QVariant::Double) + if (value.userType() == QVariant::Double && !CFG_UI.General.UseSciFormatForDoubles.get()) { le->setText(doubleToString(value)); return; @@ -382,7 +248,7 @@ QString SqlQueryItemDelegate::getSqlForFkEditor(SqlQueryItem* item) const QString col; QString firstSrcCol; QStringList usedNames; - for (SqlQueryModelColumn::ConstraintFk* fk : fkList) + for (SqlQueryModelColumn::ConstraintFk*& fk : fkList) { col = wrapObjIfNeeded(fk->foreignColumn); src = wrapObjIfNeeded(fk->foreignTable); @@ -397,7 +263,7 @@ QString SqlQueryItemDelegate::getSqlForFkEditor(SqlQueryItem* item) const continue; srcCols = resolver.getTableColumns(src); - for (const QString& srcCol : srcCols) + for (QString& srcCol : srcCols) { if (fk->foreignColumn.compare(srcCol, Qt::CaseInsensitive) == 0) continue; // Exclude matching column. We don't want the same column several times. @@ -429,11 +295,11 @@ QString SqlQueryItemDelegate::getSqlForFkEditor(SqlQueryItem* item) const } // Current value column (will be 1 for row which matches current cell value) - QVariant fullValue = item->getFullValue(); + QVariant value = item->getValue(); QString currValueColName = generateUniqueName("curr", usedNames); - QString currValueExpr = fullValue.isNull() ? + QString currValueExpr = value.isNull() ? currNullValueTpl.arg(firstSrcCol, currValueColName) : - currValueTpl.arg(firstSrcCol, wrapValueIfNeeded(fullValue), currValueColName); + currValueTpl.arg(firstSrcCol, wrapValueIfNeeded(value), currValueColName); return sql.arg( selectedCols.join(", "), @@ -443,56 +309,16 @@ QString SqlQueryItemDelegate::getSqlForFkEditor(SqlQueryItem* item) const ); } -qlonglong SqlQueryItemDelegate::getRowCountForFkEditor(Db* db, const QString& query, bool* isError) const -{ - static_qstring(tpl, "SELECT count(*) FROM (%1)"); - - QString sql = tpl.arg(query); - SqlQueryPtr result = db->exec(sql); - if (isError) - *isError = result->isError(); - - return result->getSingleCell().toLongLong(); -} - -QRect SqlQueryItemDelegate::getLoadFullValueButtonRegion(const QRect& cell) -{ - int x = cell.left() + cell.width() - LOAD_FULL_VALUE_BUTTON_SIZE - LOAD_FULL_VALUE_BUTTON_SIDE_MARGIN; - int y = cell.top() + (cell.height() / 2) - (LOAD_FULL_VALUE_BUTTON_SIZE / 2); - return QRect(x, y, LOAD_FULL_VALUE_BUTTON_SIZE, LOAD_FULL_VALUE_BUTTON_SIZE); -} - -bool SqlQueryItemDelegate::isOverFullValueButton(const QRect& cell, QMouseEvent* event) -{ - return isOverFullValueButton(cell, event->x(), event->y()); -} - -bool SqlQueryItemDelegate::isOverFullValueButton(const QRect& cell, int x, int y) -{ - QRect buttonRect = getLoadFullValueButtonRegion(cell); - return buttonRect.contains(x, y); -} - -int SqlQueryItemDelegate::getFkViewHeaderWidth(SqlQueryView* fkView, bool includeScrollBar) const -{ - int wd = fkView->horizontalHeader()->length(); - if (includeScrollBar && fkView->verticalScrollBar()->isVisible()) - wd += fkView->verticalScrollBar()->width(); - - return wd; -} - QWidget* SqlQueryItemDelegate::getFkEditor(SqlQueryItem* item, QWidget* parent, const SqlQueryModel* model) const { - QString sql = getSqlForFkEditor(item); - Db* db = model->getDb(); bool countingError = false; - qlonglong rowCount = getRowCountForFkEditor(db, sql, &countingError); - if (rowCount > MAX_ROWS_FOR_FK) + QString sql = FkComboBox::getSqlForFkEditor(db, item->getColumn(), item->getValue()); + qlonglong rowCount = FkComboBox::getRowCountForFkEditor(db, sql, &countingError); + if (rowCount > FkComboBox::MAX_ROWS_FOR_FK) { notifyWarn(tr("Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually.") - .arg(MAX_ROWS_FOR_FK).arg(item->getColumn()->column)); + .arg(FkComboBox::MAX_ROWS_FOR_FK).arg(item->getColumn()->column)); return getEditor(item->getValue().userType(), parent); } @@ -503,168 +329,9 @@ QWidget* SqlQueryItemDelegate::getFkEditor(SqlQueryItem* item, QWidget* parent, return nullptr; } - QComboBox *cb = new QComboBox(parent); - cb->setEditable(true); - - // Prepare combo dropdown view. - SqlQueryView* comboView = new SqlQueryView(); - comboView->setSimpleBrowserMode(true); - comboView->setMaximumWidth(QGuiApplication::primaryScreen()->size().width()); - - fkViewParentItemSize = model->getView()->horizontalHeader()->sectionSize(item->index().column()); - connect(comboView->horizontalHeader(), &QHeaderView::sectionResized, [this, comboView, model](int, int, int) - { - if (!model->isAllDataLoaded()) - return; - - updateComboViewGeometry(comboView, false); - }); - - SqlQueryModel* comboModel = new SqlQueryModel(comboView); - comboModel->setView(comboView); - - // Mapping of model to cb, so we can update combo when data arrives. - modelToFkInitialValue[comboModel] = item->getFullValue(); - modelToFkCombo[comboModel] = cb; - connect(cb, &QComboBox::destroyed, [this, comboModel](QObject*) - { - modelToFkCombo.remove(comboModel); - modelToFkInitialValue.remove(comboModel); - }); - - connect(cb, QOverload::of(&QComboBox::currentIndexChanged), [this, comboModel](int idx) - { - if (idx > -1 && idx < comboModel->getTotalRowsReturned() && comboModel->isAllDataLoaded()) - comboModel->itemFromIndex(idx, 1)->loadFullData(); - }); - - // When execution is done, update combo. - connect(comboModel, SIGNAL(executionSuccessful()), this, SLOT(fkDataReady())); - connect(comboModel, SIGNAL(executionFailed(QString)), this, SLOT(fkDataFailed(QString))); - - // Setup combo, model, etc. - cb->setModel(comboModel); - cb->setView(comboView); - cb->setModelColumn(1); - cb->view()->viewport()->installEventFilter(new FkComboFilter(this, comboView, cb)); - cb->view()->viewport()->installEventFilter(new FkComboShowFilter(this, comboView, cb)); - cb->view()->verticalScrollBar()->installEventFilter(new FkComboShowFilter(this, comboView, cb)); - - comboModel->setHardRowLimit(MAX_ROWS_FOR_FK); - comboModel->setCellDataLengthLimit(FK_CELL_LENGTH_LIMIT); - comboModel->setDb(db); - comboModel->setQuery(sql); - comboModel->setAsyncMode(false); - comboModel->executeQuery(); - - comboView->verticalHeader()->setVisible(false); - comboView->horizontalHeader()->setVisible(true); - comboView->setSelectionMode(QAbstractItemView::SingleSelection); - comboView->setSelectionBehavior(QAbstractItemView::SelectRows); - + int dropDownViewMinWidth = model->getView()->horizontalHeader()->sectionSize(item->index().column()); + FkComboBox* cb = new FkComboBox(parent, dropDownViewMinWidth); + cb->init(db, item->getColumn()); + cb->setValue(item->getValue()); return cb; } - -void SqlQueryItemDelegate::fkDataReady() -{ - SqlQueryModel* model = dynamic_cast(sender()); - SqlQueryView* queryView = model->getView(); - - queryView->horizontalHeader()->setSectionHidden(0, true); - queryView->resizeColumnsToContents(); - queryView->resizeRowsToContents(); - - updateComboViewGeometry(queryView, true); - - // Set selected combo value to initial value from the cell - QComboBox* cb = modelToFkCombo[model]; - QVariant valueFromQueryModel = modelToFkInitialValue[model]; - - if (model->rowCount() > 0) - { - QModelIndex startIdx = model->index(0, 0); - QModelIndex endIdx = model->index(model->rowCount() - 1, 0); - QModelIndexList idxList = model->findIndexes(startIdx, endIdx, SqlQueryItem::DataRole::VALUE, 1, 1); - - if (idxList.size() > 0) - { - model->itemFromIndex(idxList.first().row(), 1)->loadFullData(); - cb->setCurrentIndex(idxList.first().row()); - } - else - { - cb->setCurrentIndex(-1); - cb->setEditText(valueFromQueryModel.toString()); - } - } - else - cb->setEditText(valueFromQueryModel.toString()); - - cb->lineEdit()->selectAll(); -} - -void SqlQueryItemDelegate::fkDataFailed(const QString &errorText) -{ - notifyWarn(tr("Cannot edit this cell. Details: %1").arg(errorText)); -} - -void SqlQueryItemDelegate::updateComboViewGeometry(SqlQueryView* comboView, bool initial) const -{ - int wd = getFkViewHeaderWidth(comboView, true); - comboView->setMinimumWidth(qMin(qMax(fkViewParentItemSize, wd), comboView->maximumWidth())); - - if (initial && wd < comboView->minimumWidth()) - { - // First time, upon showing up - int currentSize = comboView->horizontalHeader()->sectionSize(1); - int gap = comboView->minimumWidth() - wd; - comboView->horizontalHeader()->resizeSection(1, currentSize + gap); - } - - QWidget* container = comboView->parentWidget(); - if (container->width() > comboView->minimumWidth()) - { - container->setMaximumWidth(comboView->minimumWidth()); - container->resize(comboView->minimumWidth(), container->height()); - } -} - - -SqlQueryItemDelegate::FkComboFilter::FkComboFilter(const SqlQueryItemDelegate* delegate, SqlQueryView* comboView, QObject* parent) - : QObject(parent), delegate(delegate), comboView(comboView) -{ -} - -bool SqlQueryItemDelegate::FkComboFilter::eventFilter(QObject* obj, QEvent* event) -{ - UNUSED(obj); - if (event->type() == QEvent::MouseButtonRelease) - { - QMouseEvent* e = dynamic_cast(event); - QModelIndex idx = comboView->indexAt(QPoint(e->pos())); - if (!idx.isValid()) - return false; - - SqlQueryItem* item = comboView->getModel()->itemFromIndex(idx); - if (shouldLoadFullData(comboView->visualRect(idx), e, idx)) - { - item->loadFullData(); - return true; - } - } - return false; -} - -SqlQueryItemDelegate::FkComboShowFilter::FkComboShowFilter(const SqlQueryItemDelegate* delegate, SqlQueryView* comboView, QObject* parent) - : QObject(parent), delegate(delegate), comboView(comboView) -{ -} - -bool SqlQueryItemDelegate::FkComboShowFilter::eventFilter(QObject* obj, QEvent* event) -{ - UNUSED(obj); - if (event->type() == QEvent::Show) - delegate->updateComboViewGeometry(comboView, true); - - return false; -} diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.h index dbd2192..655eae2 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.h @@ -6,6 +6,7 @@ #include #include +class FkComboBox; class SqlQueryItem; class QComboBox; class QStandardItemModel; @@ -19,8 +20,6 @@ class GUI_API_EXPORT SqlQueryItemDelegate : public QStyledItemDelegate explicit SqlQueryItemDelegate(QObject *parent = 0); void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; - bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); - bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index); QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; QString displayText(const QVariant & value, const QLocale & locale) const; void setEditorData(QWidget * editor, const QModelIndex & index) const; @@ -28,17 +27,6 @@ class GUI_API_EXPORT SqlQueryItemDelegate : public QStyledItemDelegate void mouseLeftIndex(const QModelIndex& index); private: - class FkComboFilter : public QObject - { - public: - explicit FkComboFilter(const SqlQueryItemDelegate* delegate, SqlQueryView* comboView, QObject* parent = 0); - bool eventFilter(QObject *obj, QEvent *event); - - private: - const SqlQueryItemDelegate* delegate = nullptr; - SqlQueryView* comboView = nullptr; - }; - class FkComboShowFilter : public QObject { public: @@ -50,45 +38,21 @@ class GUI_API_EXPORT SqlQueryItemDelegate : public QStyledItemDelegate SqlQueryView* comboView = nullptr; }; - static QRect getLoadFullValueButtonRegion(const QRect& cell); - static bool isOverFullValueButton(const QRect& cell, QMouseEvent* event); - static bool isOverFullValueButton(const QRect& cell, int x, int y); - static bool shouldLoadFullData(const QRect& rect, QMouseEvent* event, const QModelIndex& index); - static bool shouldLoadFullData(const QRect& rect, int x, int y, const QModelIndex& index); - static bool isLimited(const QModelIndex &index); - SqlQueryItem* getItem(const QModelIndex &index) const; QWidget* getEditor(int type, QWidget* parent) const; QWidget* getFkEditor(SqlQueryItem* item, QWidget* parent, const SqlQueryModel *model) const; void setEditorDataForLineEdit(QLineEdit* le, const QModelIndex& index) const; void setEditorDataForFk(QComboBox* cb, const QModelIndex& index) const; - void setModelDataForFk(QComboBox* editor, QAbstractItemModel* model, const QModelIndex& index) const; + void setModelDataForFk(FkComboBox* editor, QAbstractItemModel* model, const QModelIndex& index) const; void setModelDataForLineEdit(QLineEdit* editor, QAbstractItemModel* model, const QModelIndex& index) const; QString getSqlForFkEditor(SqlQueryItem* item) const; qlonglong getRowCountForFkEditor(Db* db, const QString& query, bool *isError) const; int getFkViewHeaderWidth(SqlQueryView* fkView, bool includeScrollBar) const; - void updateComboViewGeometry(SqlQueryView* comboView, bool initial) const; - QStyleOptionButton fullValueButtonOption; QSet editorsWithAsyncExecution; - QModelIndex mouseOverFullDataButton; - bool showingFullButtonTooltip = false; - bool lmbPressedOnButton = false; - mutable int fkViewParentItemSize = 0; - mutable QHash modelToFkCombo; - mutable QHash modelToFkInitialValue; static bool warnedAboutHugeContents; - static const int LOAD_FULL_VALUE_BUTTON_SIZE = 18; - static const int LOAD_FULL_VALUE_BUTTON_SIDE_MARGIN = 2; - static const int LOAD_FULL_VALUE_ICON_SIZE = 12; - static const qlonglong MAX_ROWS_FOR_FK = 10000L; - static const int FK_CELL_LENGTH_LIMIT = 30; static const int HUGE_CONTENTS_WARNING_LIMIT = 32767; // pow(2, 16) / 2 - 1 - - private slots: - void fkDataReady(); - void fkDataFailed(const QString& errorText); }; #endif // SQLQUERYITEMDELEGATE_H diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp index dd3f655..2982415 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp @@ -9,12 +9,12 @@ #include "parser/ast/sqlitecreatetable.h" #include "uiconfig.h" #include "datagrid/sqlqueryview.h" -#include "datagrid/sqlqueryrownummodel.h" #include "services/dbmanager.h" #include "querygenerator.h" #include "parser/lexer.h" #include "common/compatibility.h" #include "mainwindow.h" +#include "iconmanager.h" #include #include #include @@ -89,7 +89,6 @@ void SqlQueryModel::executeQuery() return; } - sortOrder.clear(); queryExecutor->setSkipRowCounting(false); queryExecutor->setSortOrder(sortOrder); queryExecutor->setPage(0); @@ -222,7 +221,7 @@ QModelIndexList SqlQueryModel::findIndexes(int role, const QVariant& value, int return findIndexes(startIdx, endIdx, role, value, hits); } -QModelIndexList SqlQueryModel::findIndexes(const QModelIndex& start, const QModelIndex& end, int role, const QVariant& value, int hits) const +QModelIndexList SqlQueryModel::findIndexes(const QModelIndex& start, const QModelIndex& end, int role, const QVariant& value, int hits, bool stringApproximation) const { QModelIndexList results; bool allHits = hits < 0; @@ -231,6 +230,7 @@ QModelIndexList SqlQueryModel::findIndexes(const QModelIndex& start, const QMode int toRow = end.row(); int fromCol = start.column(); int toCol = end.column(); + QString stringVal = value.toString(); for (int row = fromRow; row <= toRow && (allHits || results.count() < hits); row++) { @@ -241,7 +241,7 @@ QModelIndexList SqlQueryModel::findIndexes(const QModelIndex& start, const QMode continue; QVariant cellVal = data(idx, role); - if (value != cellVal) + if (value != cellVal && !(stringApproximation && stringVal == cellVal.toString())) continue; results.append(idx); @@ -312,9 +312,9 @@ QHash > SqlQueryModel::groupItemsByTable(cons { if (item->getColumn()) { - table.setDatabase(item->getColumn()->database.toLower()); - table.setTable(item->getColumn()->table.toLower()); - table.setTableAlias(item->getColumn()->tableAlias.toLower()); + table.setDatabase(item->getColumn()->database); + table.setTable(item->getColumn()->table); + table.setTableAlias(item->getColumn()->tableAlias); itemsByTable[table] << item; } else @@ -410,7 +410,7 @@ void SqlQueryModel::refreshGeneratedColumns(const QList& items) for (auto resultIt = resultValues.begin(); resultIt != resultValues.end(); resultIt++) { SqlQueryItem* item = resultIt.key(); - item->setValue(resultIt.value(), false, true); + item->setValue(resultIt.value(), true); item->setTextAlignment(findValueAlignment(resultIt.value(), item->getColumn())); } } @@ -1087,13 +1087,8 @@ void SqlQueryModel::updateItem(SqlQueryItem* item, const QVariant& value, int co void SqlQueryModel::updateItem(SqlQueryItem* item, const QVariant& value, int columnIndex, const RowId& rowId, Qt::Alignment alignment) { SqlQueryModelColumnPtr column = columns[columnIndex]; - - // This should be equal at most, unless we have UTF-8 string, than there might be more bytes. - // If less, than it's not limited. - bool limited = value.toByteArray().size() >= cellDataLengthLimit; - item->setJustInsertedWithOutRowId(false); - item->setValue(value, limited, true); + item->setValue(value, true); item->setColumn(column.data()); item->setTextAlignment(alignment); item->setRowId(rowId); @@ -1132,6 +1127,14 @@ RowId SqlQueryModel::getNewRowId(const RowId& currentRowId, const QListgetColumn(); + QStringList tableRowIdColumns = tableToRowIdColumn[col->getAliasedTable()].values(); + if (tableRowIdColumns.contains(col->column, Qt::CaseInsensitive)) + { + RowId newRowId; + newRowId[col->column] = item->getValue(); + return newRowId; + } + if (isRowIdKeyword(col->column) || col->isRowIdPk()) { RowId newRowId; @@ -1170,7 +1173,7 @@ QHash SqlQueryModel::toValuesGroupedByColumns(const QList { QHash values; for (SqlQueryItem* item : items) - values[item->getColumn()->displayName] << item->getFullValue(); + values[item->getColumn()->displayName] << item->getValue(); return values; } @@ -1187,7 +1190,6 @@ bool SqlQueryModel::readColumns() tablesInUse.clear(); // Reading column mapping for ROWID columns - int totalRowIdCols = 0; AliasedTable aliasedTable; DbAndTable dbAndTable; for (const QueryExecutor::ResultRowIdColumnPtr& resCol : queryExecutor->getRowIdResultColumns()) @@ -1205,7 +1207,6 @@ bool SqlQueryModel::readColumns() aliasedTable.setTable(resCol->table); aliasedTable.setTableAlias(resCol->tableAlias); tableToRowIdColumn[aliasedTable] = resCol->queryExecutorAliasToColumn; - totalRowIdCols += resCol->queryExecutorAliasToColumn.size(); } // Reading column details (datatype, constraints) @@ -1406,6 +1407,7 @@ void SqlQueryModel::handleExecFinished(SqlQueryPtr results) return; } + emit aboutToLoadResults(); storeStep1NumbersFromExecution(); if (!loadData(results)) return; @@ -1488,33 +1490,57 @@ void SqlQueryModel::itemValueEdited(SqlQueryItem* item) emit commitStatusChanged(getUncommittedItems().size() > 0); } +void SqlQueryModel::repaintAllItems() +{ + QModelIndex startIdx = index(0, 0); + if (!startIdx.isValid()) + return; + + QModelIndex endIdx = index(rowCount() - 1, columnCount() - 1); + emit dataChanged(startIdx, endIdx, QVector({Qt::DisplayRole, Qt::EditRole})); +} + void SqlQueryModel::changeSorting(int logicalIndex, Qt::SortOrder order) { if (!reloadAvailable) return; + QueryExecutor::SortList sortList = QueryExecutor::SortList(); + if (logicalIndex > -1) + sortList = {QueryExecutor::Sort(order, logicalIndex)}; + queryExecutor->setSkipRowCounting(true); - queryExecutor->setSortOrder({QueryExecutor::Sort(order, logicalIndex)}); + queryExecutor->setSortOrder(sortList); reloadInternal(); } void SqlQueryModel::changeSorting(int logicalIndex) { Qt::SortOrder newOrder = Qt::AscendingOrder; - if (sortOrder.size() == 1) + if (sortOrder.size() != 1) { - switch (sortOrder.first().order) - { - case QueryExecutor::Sort::ASC: - newOrder = Qt::DescendingOrder; - break; - case QueryExecutor::Sort::DESC: - newOrder = Qt::AscendingOrder; - break; - case QueryExecutor::Sort::NONE: - newOrder = Qt::AscendingOrder; - break; - } + changeSorting(logicalIndex, newOrder); + return; + } + + QueryExecutor::Sort singleOrder = sortOrder.first(); + if (singleOrder.column != logicalIndex) + { + changeSorting(logicalIndex, newOrder); + return; + } + + switch (singleOrder.order) + { + case QueryExecutor::Sort::ASC: + newOrder = Qt::DescendingOrder; + break; + case QueryExecutor::Sort::DESC: + logicalIndex = -1; + break; + case QueryExecutor::Sort::NONE: + newOrder = Qt::AscendingOrder; + break; } changeSorting(logicalIndex, newOrder); } @@ -2014,6 +2040,12 @@ void SqlQueryModel::applyStringFilter(const QString& value) // For custom query this is not supported. } +void SqlQueryModel::applyStrictFilter(const QString& value) +{ + UNUSED(value); + // For custom query this is not supported. +} + void SqlQueryModel::applyRegExpFilter(const QString& value) { UNUSED(value); @@ -2026,6 +2058,12 @@ void SqlQueryModel::applyStringFilter(const QStringList& values) // For custom query this is not supported. } +void SqlQueryModel::applyStrictFilter(const QStringList& values) +{ + UNUSED(values); + // For custom query this is not supported. +} + void SqlQueryModel::applyRegExpFilter(const QStringList& values) { UNUSED(values); @@ -2087,56 +2125,6 @@ bool SqlQueryModel::isExecutionInProgress() const return queryExecutor->isExecutionInProgress(); } -void SqlQueryModel::loadFullDataForEntireRow(int row) -{ - int colCnt = columns.size(); - SqlQueryItem *item = nullptr; - for (int col = 0; col < colCnt; col++) - { - item = itemFromIndex(row, col); - if (!item) - continue; - - if (!item->isLimitedValue()) - continue; - - item->loadFullData(); - } -} - -void SqlQueryModel::loadFullDataForEntireColumn(int column) -{ - int rowCnt = rowCount(); - SqlQueryItem *item = nullptr; - for (int row = 0; row < rowCnt; row++) - { - item = itemFromIndex(row, column); - if (!item) - continue; - - if (!item->isLimitedValue()) - continue; - - item->loadFullData(); - } -} - -bool SqlQueryModel::doesColumnHaveLimitedValues(int column) const -{ - int rowCnt = rowCount(); - SqlQueryItem *item = nullptr; - for (int row = 0; row < rowCnt; row++) - { - item = itemFromIndex(row, column); - if (!item) - continue; - - if (item->isLimitedValue()) - return true; - } - return false; -} - void SqlQueryModel::CommitUpdateQueryBuilder::clear() { database.clear(); @@ -2180,7 +2168,7 @@ QString SqlQueryModel::CommitUpdateQueryBuilder::build() int argIndex = 0; QString arg; QStringList assignments; - for (const QString& col : columns) + for (QString& col : columns) { arg = ":value_" + QString::number(argIndex++); assignmentArgs << arg; diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h index 9a89205..43222f1 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h @@ -54,7 +54,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel SqlQueryItem* itemFromIndex(const QModelIndex& index) const; SqlQueryItem* itemFromIndex(int row, int column) const; QModelIndexList findIndexes(int role, const QVariant &value, int hits = -1) const; - QModelIndexList findIndexes(const QModelIndex &start, const QModelIndex& end, int role, const QVariant &value, int hits = -1) const; + QModelIndexList findIndexes(const QModelIndex &start, const QModelIndex& end, int role, const QVariant &value, int hits = -1, bool stringApproximation = false) const; QList findItems(int role, const QVariant &value, int hits = -1) const; QList findItems(const QModelIndex &start, const QModelIndex& end, int role, const QVariant &value, int hits = -1) const; SqlQueryItem* findAnyInColumn(int column, int role, const QVariant &value) const; @@ -63,9 +63,6 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; bool isExecutionInProgress() const; - void loadFullDataForEntireRow(int row); - void loadFullDataForEntireColumn(int column); - bool doesColumnHaveLimitedValues(int column) const; StrHash attachDependencyTables(); void detachDependencyTables(); @@ -94,6 +91,15 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel */ virtual void applySqlFilter(const QString& value); + /** + * @brief Request for applying an "equals" filtering on a dataset. + * @param value Filter expression. + * Default implementation does nothing. Working implementation (i.e. for a table) + * should set the query to temporary value which respects given filter and reload the data. + * Filter passed to this method is meant to be treated as strict value to be compared against. + */ + virtual void applyStrictFilter(const QString& value); + /** * @brief Request for applying "LIKE" filtering on a dataset. * @param value Filter expression. @@ -120,6 +126,14 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel */ virtual void applyStringFilter(const QStringList& values); + /** + * @brief Request for applying an "equals" filtering on a dataset. + * @param values Filter expressions per column. + * This is the same as applyStrictFilter(const QString&), but is used for per-column filtering, + * when user enters filtering expressions for each column sparately. + */ + virtual void applyStrictFilter(const QStringList& values); + /** * @brief Request for applying Regular Expression filtering on a dataset. * @param values Filter expressions per column. @@ -501,6 +515,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel public slots: void itemValueEdited(SqlQueryItem* item); + void repaintAllItems(); void changeSorting(int logicalIndex, Qt::SortOrder order); void changeSorting(int logicalIndex); void firstPage(); @@ -536,6 +551,12 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel */ void executionSuccessful(); + /** + * @brief Execution is finished and data is about to be loaded to model. + * Emitted every query execution, every data reloading and every page change. + */ + void aboutToLoadResults(); + /** * @brief executionFailed * @param errorText diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.cpp index 8171154..68287bd 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.cpp @@ -56,6 +56,8 @@ SqlQueryModelColumn::EditionForbiddenReason SqlQueryModelColumn::convert(QueryEx return EditionForbiddenReason::DISTINCT_RESULTS; case QueryExecutor::ColumnEditionForbiddenReason::COMM_TAB_EXPR: return EditionForbiddenReason::COMMON_TABLE_EXPRESSION; + case QueryExecutor::ColumnEditionForbiddenReason::VIEW_NOT_EXPANDED: + return EditionForbiddenReason::VIEW_NOT_EXPANDED; } return static_cast(-1); } @@ -83,6 +85,8 @@ QString SqlQueryModelColumn::resolveMessage(SqlQueryModelColumn::EditionForbidde return QObject::tr("Cannot edit columns that are result of common table expression statement (%1).").arg("WITH ... SELECT ..."); case EditionForbiddenReason::GENERATED_COLUMN: return QObject::tr("Cannot edit table generated columns."); + case EditionForbiddenReason::VIEW_NOT_EXPANDED: + return QObject::tr("Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view)."); } qCritical() << "Reached null text message for SqlQueryModel::EditionForbiddenReason. This should not happen!"; return QString(); @@ -176,6 +180,11 @@ bool SqlQueryModelColumn::isGenerated() const return getConstraints().size() > 0; } +QString SqlQueryModelColumn::getAliasedName() const +{ + return this->alias.isEmpty() ? this->column : this->alias; +} + QList SqlQueryModelColumn::getFkConstraints() const { return getConstraints(); @@ -190,6 +199,11 @@ SqlQueryModelColumn::ConstraintDefault* SqlQueryModelColumn::getDefaultConstrain return list[0]; } +AliasedTable SqlQueryModelColumn::getAliasedTable() const +{ + return AliasedTable(database, table, tableAlias); +} + int qHash(SqlQueryModelColumn::EditionForbiddenReason reason) { return static_cast(reason); @@ -211,6 +225,11 @@ QDataStream&operator >>(QDataStream& in, SqlQueryModelColumn*& col) SqlQueryModelColumn::Constraint* SqlQueryModelColumn::Constraint::create(const QString& column, SqliteCreateTable::ConstraintPtr tableConstraint) +{ + return create(column, tableConstraint.data()); +} + +SqlQueryModelColumn::Constraint* SqlQueryModelColumn::Constraint::create(const QString& column, SqliteCreateTable::Constraint* tableConstraint) { Constraint* constr = nullptr; switch (tableConstraint->type) @@ -222,10 +241,18 @@ SqlQueryModelColumn::Constraint* SqlQueryModelColumn::Constraint::create(const Q constr = new ConstraintPk(); constr->type = Type::PRIMARY_KEY; + dynamic_cast(constr)->multiColumns = + map(tableConstraint->indexedColumns, [](SqliteIndexedColumn* idxCol) -> QString + { + return idxCol->detokenize().trimmed(); + }); break; } case SqliteCreateTable::Constraint::UNIQUE: { + if (!tableConstraint->doesAffectColumn(column)) + return nullptr; + constr = new ConstraintUnique(); constr->type = Type::UNIQUE; break; @@ -268,6 +295,11 @@ SqlQueryModelColumn::Constraint* SqlQueryModelColumn::Constraint::create(const Q } SqlQueryModelColumn::Constraint* SqlQueryModelColumn::Constraint::create(SqliteCreateTable::Column::ConstraintPtr columnConstraint) +{ + return create(columnConstraint.data()); +} + +SqlQueryModelColumn::Constraint* SqlQueryModelColumn::Constraint::create(SqliteCreateTable::Column::Constraint* columnConstraint) { Constraint* constr = nullptr; switch (columnConstraint->type) @@ -328,6 +360,7 @@ SqlQueryModelColumn::Constraint* SqlQueryModelColumn::Constraint::create(SqliteC { ConstraintGenerated* generate = new ConstraintGenerated(); generate->generatedType = columnConstraint->generatedType; + generate->expr = columnConstraint->expr ? columnConstraint->expr->detokenize() : QString(); constr = generate; constr->type = Type::GENERATED; break; @@ -377,6 +410,9 @@ QString SqlQueryModelColumn::ConstraintPk::getTypeString() const QString SqlQueryModelColumn::ConstraintPk::getDetails() const { QStringList detailList; + if (!multiColumns.isEmpty()) + detailList << "("+multiColumns.join(", ")+")"; + if (autoIncrement) detailList << "AUTOINCREMENT"; @@ -384,7 +420,12 @@ QString SqlQueryModelColumn::ConstraintPk::getDetails() const detailList << QObject::tr("on conflict: %1", "data view tooltip").arg(sqliteConflictAlgo(onConflict)); if (detailList.size() > 0) - return "("+detailList.join(", ")+")"; + { + if (detailList.size() > 1) + return "("+detailList.join(", ")+")"; + else + return detailList.join(", "); + } return ""; } @@ -503,8 +544,7 @@ QString SqlQueryModelColumn::ConstraintGenerated::getTypeString() const QString SqlQueryModelColumn::ConstraintGenerated::getDetails() const { - return "("+QObject::tr("generated column type: %1", "data view tooltip") - .arg(SqliteCreateTable::Column::Constraint::toString(generatedType))+")"; + return QString("(%1) %2").arg(expr, SqliteCreateTable::Column::Constraint::toString(generatedType)).trimmed(); } Icon* SqlQueryModelColumn::ConstraintGenerated::getIcon() const diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.h index 8e05fd5..1a347e5 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.h @@ -29,7 +29,8 @@ class GUI_API_EXPORT SqlQueryModelColumn SMART_EXECUTION_FAILED, DISTINCT_RESULTS, COMMON_TABLE_EXPRESSION, - GENERATED_COLUMN + GENERATED_COLUMN, + VIEW_NOT_EXPANDED }; struct Constraint @@ -55,8 +56,10 @@ class GUI_API_EXPORT SqlQueryModelColumn virtual ~Constraint() {} + static Constraint* create(const QString& column, SqliteCreateTable::Constraint* tableConstraint); static Constraint* create(const QString& column, SqliteCreateTable::ConstraintPtr tableConstraint); static Constraint* create(SqliteCreateTable::Column::ConstraintPtr columnConstraint); + static Constraint* create(SqliteCreateTable::Column::Constraint* columnConstraint); virtual QString getTypeString() const = 0; virtual QString getDetails() const = 0; @@ -74,6 +77,7 @@ class GUI_API_EXPORT SqlQueryModelColumn Icon* getIcon() const; bool autoIncrement; + QStringList multiColumns; SqliteConflictAlgo onConflict = SqliteConflictAlgo::null; }; @@ -139,6 +143,7 @@ class GUI_API_EXPORT SqlQueryModelColumn QString getDetails() const; Icon* getIcon() const; + QString expr; SqliteCreateTable::Column::Constraint::GeneratedType generatedType = SqliteCreateTable::Column::Constraint::GeneratedType::null; }; @@ -163,8 +168,10 @@ class GUI_API_EXPORT SqlQueryModelColumn bool isDefault() const; bool isCollate() const; bool isGenerated() const; + QString getAliasedName() const; QList getFkConstraints() const; ConstraintDefault* getDefaultConstraint() const; + AliasedTable getAliasedTable() const; QString displayName; QString column; diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.cpp index 9d3ea96..d9695d7 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.cpp @@ -7,7 +7,6 @@ #include "iconmanager.h" #include "common/unused.h" #include "common/extaction.h" -#include "multieditor/multieditor.h" #include "multieditor/multieditordialog.h" #include "uiconfig.h" #include "dialogs/sortdialog.h" @@ -15,8 +14,7 @@ #include "windows/editorwindow.h" #include "mainwindow.h" #include "common/utils_sql.h" -#include "querygenerator.h" -#include "services/codeformatter.h" +#include "common/mouseshortcut.h" #include #include #include @@ -61,13 +59,17 @@ void SqlQueryView::init() connect(this, &QWidget::customContextMenuRequested, this, &SqlQueryView::customContextMenuRequested); connect(CFG_UI.Fonts.DataView, SIGNAL(changed(QVariant)), this, SLOT(updateFont())); connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(itemActivated(QModelIndex))); - connect(horizontalHeader(), &QHeaderView::sectionResized, [this](int section, int, int newSize) + connect(horizontalHeader(), &QHeaderView::sectionResized, this, [this](int section, int, int newSize) { if (ignoreColumnWidthChanges) return; getModel()->setDesiredColumnWidth(section, newSize); }); + connect(verticalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(adjustRowToContents(int))); + MouseShortcut::forWheel(Qt::ControlModifier, + this, SLOT(fontSizeChangeRequested(int)), + viewport()); horizontalHeader()->setSortIndicatorShown(false); horizontalHeader()->setSectionsClickable(true); @@ -98,6 +100,7 @@ void SqlQueryView::createActions() createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback"), this, SLOT(rollback()), this); createAction(SELECTIVE_COMMIT, ICONS.COMMIT, tr("Commit selected cells"), this, SLOT(selectiveCommit()), this); createAction(SELECTIVE_ROLLBACK, ICONS.ROLLBACK, tr("Rollback selected cells"), this, SLOT(selectiveRollback()), this); + createAction(EDIT_CURRENT, tr("Edit current cell inline"), this, SLOT(editCurrent()), this); createAction(GENERATE_SELECT, "SELECT", this, SLOT(generateSelect()), this); createAction(GENERATE_INSERT, "INSERT", this, SLOT(generateInsert()), this); createAction(GENERATE_UPDATE, "UPDATE", this, SLOT(generateUpdate()), this); @@ -107,15 +110,19 @@ void SqlQueryView::createActions() createAction(INSERT_ROW, ICONS.INSERT_ROW, tr("Insert row"), this, SIGNAL(requestForRowInsert()), this); createAction(INSERT_MULTIPLE_ROWS, ICONS.INSERT_ROWS, tr("Insert multiple rows"), this, SIGNAL(requestForMultipleRowInsert()), this); createAction(DELETE_ROW, ICONS.DELETE_ROW, tr("Delete selected row"), this, SIGNAL(requestForRowDelete()), this); - createAction(LOAD_FULL_VALUES, ICONS.LOAD_FULL_VALUES, tr("Load full values"), this, SLOT(loadFullValuesForColumn()), this); - + createAction(ADJUST_ROWS_SIZE, tr("Adjust height of rows"), this, SLOT(toggleRowsHeightAdjustment(bool)), this); + actionMap[ADJUST_ROWS_SIZE]->setCheckable(true); + actionMap[ADJUST_ROWS_SIZE]->setChecked(false); actionMap[RESET_SORTING]->setEnabled(false); + createAction(INCR_FONT_SIZE, tr("Increase font size", "data view"), this, SLOT(incrFontSize()), this); + createAction(DECR_FONT_SIZE, tr("Decrease font size", "data view"), this, SLOT(decrFontSize()), this); + createAction(INVERT_SELECTION, ICONS.SELECTION_INVERT, tr("Invert selection", "data view"), this, SLOT(invertSelection()), this); } void SqlQueryView::setupDefShortcuts() { setShortcutContext({ROLLBACK, SET_NULL, ERASE, OPEN_VALUE_EDITOR, COMMIT, COPY, COPY_AS, - PASTE, PASTE_AS}, Qt::WidgetWithChildrenShortcut); + PASTE, PASTE_AS, ADJUST_ROWS_SIZE, INCR_FONT_SIZE, DECR_FONT_SIZE}, Qt::WidgetWithChildrenShortcut); BIND_SHORTCUTS(SqlQueryView, Action); } @@ -186,7 +193,6 @@ void SqlQueryView::setupActionsForMenu(SqlQueryItem* currentItem, const QListaddAction(actionMap[GENERATE_DELETE]); } - contextMenu->addSeparator(); contextMenu->addAction(actionMap[COPY]); contextMenu->addAction(actionMap[COPY_WITH_HEADER]); @@ -194,10 +200,13 @@ void SqlQueryView::setupActionsForMenu(SqlQueryItem* currentItem, const QListaddAction(actionMap[PASTE]); //contextMenu->addAction(actionMap[PASTE_AS]); // TODO uncomment when implemented } + contextMenu->addSeparator(); + contextMenu->addAction(actionMap[INVERT_SELECTION]); + contextMenu->addAction(actionMap[ADJUST_ROWS_SIZE]); if (additionalActions.size() > 0) { contextMenu->addSeparator(); - for (QAction* action : additionalActions) + for (QAction*& action : additionalActions) contextMenu->addAction(action); } } @@ -209,8 +218,6 @@ void SqlQueryView::setupHeaderMenu() headerContextMenu = new QMenu(horizontalHeader()); headerContextMenu->addAction(actionMap[SORT_DIALOG]); headerContextMenu->addAction(actionMap[RESET_SORTING]); - headerContextMenu->addSeparator(); - headerContextMenu->addAction(actionMap[LOAD_FULL_VALUES]); } QList SqlQueryView::getSelectedItems() @@ -251,8 +258,13 @@ void SqlQueryView::setModel(QAbstractItemModel* model) QTableView::setModel(model); SqlQueryModel* m = getModel(); connect(widgetCover, SIGNAL(cancelClicked()), m, SLOT(interrupt())); - connect(getModel(), &SqlQueryModel::commitStatusChanged, this, &SqlQueryView::updateCommitRollbackActions); - connect(getModel(), &SqlQueryModel::sortingUpdated, this, &SqlQueryView::sortingUpdated); + connect(m, &SqlQueryModel::commitStatusChanged, this, &SqlQueryView::updateCommitRollbackActions); + connect(m, &SqlQueryModel::sortingUpdated, this, &SqlQueryView::sortingUpdated); + connect(m, &SqlQueryModel::executionSuccessful, this, [this]() + { + if (actionMap[ADJUST_ROWS_SIZE]->isChecked()) + verticalHeader()->resizeSections(QHeaderView::ResizeToContents); + }); } SqlQueryItem* SqlQueryView::itemAt(const QPoint& pos) @@ -319,9 +331,67 @@ void SqlQueryView::generateDelete() MAINWINDOW->openSqlEditor(getModel()->getDb(), sql); } -void SqlQueryView::loadFullValuesForColumn() +void SqlQueryView::editCurrent() +{ + QModelIndex idx = getCurrentIndex(); + if (idx.isValid()) + edit(idx); +} + +void SqlQueryView::toggleRowsHeightAdjustment(bool enabled) +{ + QHeaderView* hdr = verticalHeader(); + if (enabled) + { + hdr->resizeSections(QHeaderView::ResizeToContents); + } + else + { + hdr->setSectionResizeMode(QHeaderView::Interactive); + hdr->resizeSections(QHeaderView::Interactive); + int height = hdr->defaultSectionSize(); + int rows = getModel()->rowCount(); + for (int row = 0; row < rows; row++) + hdr->resizeSection(row, height); + } +} + +void SqlQueryView::adjustRowToContents(int section) +{ + verticalHeader()->setSectionResizeMode(section, QHeaderView::ResizeToContents); +} + +void SqlQueryView::fontSizeChangeRequested(int delta) +{ + changeFontSize(delta >= 0 ? 1 : -1); +} + +void SqlQueryView::incrFontSize() +{ + changeFontSize(1); +} + +void SqlQueryView::decrFontSize() +{ + changeFontSize(-1); +} + +void SqlQueryView::invertSelection() { - getModel()->loadFullDataForEntireColumn(headerContextMenuSection); + SqlQueryModel* model = getModel(); + int rows = model->rowCount(); + int cols = model->columnCount(); + QItemSelectionModel* selection = selectionModel(); + for (int r = 0; r < rows; r++) + for (int c = 0; c < cols; c++) + selection->select(model->index(r, c), QItemSelectionModel::Toggle); + + if (!selection->isSelected(currentIndex())) + { + QModelIndexList idxList = selection->selectedIndexes(); + if (!idxList.isEmpty()) + selection->setCurrentIndex(selection->selectedIndexes().first(), QItemSelectionModel::NoUpdate); + } } bool SqlQueryView::editInEditorIfNecessary(SqlQueryItem* item) @@ -362,7 +432,7 @@ void SqlQueryView::paste(const QList >& data) if (!validatePasting(warnedColumns, warnedRowDeletion, item)) continue; - item->setValue(theValue, false, false); + item->setValue(theValue, false); } return; @@ -402,7 +472,7 @@ void SqlQueryView::paste(const QList >& data) if (!validatePasting(warnedColumns, warnedRowDeletion, item)) continue; - item->setValue(cell, false, false); + item->setValue(cell, false); } // Go to next row, first column @@ -429,7 +499,7 @@ bool SqlQueryView::validatePasting(QSet& warnedColumns, bool& warnedRow if (!warnedColumns.contains(colName)) { warnedColumns << colName; - notifyWarn(tr("Cannot paste to column %1. Details: %2").arg(colName).arg(item->getColumn()->getEditionForbiddenReason())); + notifyWarn(tr("Cannot paste to column %1. Details: %2").arg(colName, item->getColumn()->getEditionForbiddenReason())); } return false; } @@ -509,7 +579,7 @@ void SqlQueryView::copy(bool withHeader) if (withHeader) { int leftMostColumn = groupedItems.first().first()->column(); - for (SqlQueryModelColumnPtr col : getModel()->getColumns().mid(leftMostColumn, groupedItems.first().size())) + for (SqlQueryModelColumnPtr& col : getModel()->getColumns().mid(leftMostColumn, groupedItems.first().size())) { theDataRow << col->displayName; cells << col->displayName; @@ -527,7 +597,7 @@ void SqlQueryView::copy(bool withHeader) { for (SqlQueryItem* item : itemsInRows) { - itemValue = item->getFullValue(); + itemValue = item->getValue(); if (itemValue.userType() == QVariant::Double) cells << doubleToString(itemValue); else @@ -559,6 +629,13 @@ void SqlQueryView::copy(bool withHeader) qApp->clipboard()->setMimeData(mimeData); } +void SqlQueryView::changeFontSize(int factor) +{ + auto f = CFG_UI.Fonts.DataView.get(); + f.setPointSize(f.pointSize() + factor); + CFG_UI.Fonts.DataView.set(f); +} + bool SqlQueryView::getSimpleBrowserMode() const { return simpleBrowserMode; @@ -585,20 +662,6 @@ void SqlQueryView::scrollContentsBy(int dx, int dy) emit scrolledBy(dx, dy); } -void SqlQueryView::mouseMoveEvent(QMouseEvent* event) -{ - QAbstractItemView::mouseMoveEvent(event); - - QModelIndex idx = indexAt(QPoint(event->x(), event->y())); - if (idx != indexUnderCursor) - { - if (indexUnderCursor.isValid()) - itemDelegate->mouseLeftIndex(indexUnderCursor); - - indexUnderCursor = idx; - } -} - void SqlQueryView::updateCommitRollbackActions(bool enabled) { actionMap[COMMIT]->setEnabled(enabled); @@ -629,11 +692,6 @@ void SqlQueryView::headerContextMenuRequested(const QPoint& pos) if (simpleBrowserMode) return; - headerContextMenuSection = horizontalHeader()->visualIndexAt(pos.x()); - - bool hasLimitedValues = getModel()->doesColumnHaveLimitedValues(headerContextMenuSection); - actionMap[LOAD_FULL_VALUES]->setEnabled(hasLimitedValues); - headerContextMenu->popup(horizontalHeader()->mapToGlobal(pos)); } @@ -667,6 +725,8 @@ void SqlQueryView::updateFont() QFont f = CFG_UI.Fonts.DataView.get(); QFontMetrics fm(f); verticalHeader()->setDefaultSectionSize(fm.height() + 4); + if (getModel()) + getModel()->repaintAllItems(); } void SqlQueryView::executionStarted() @@ -727,6 +787,8 @@ void SqlQueryView::paste() QList deserializedRows = TsvSerializer::deserialize(mimeData->text()); bool trimOnPaste = false; bool trimOnPasteAsked = false; + bool pasteAsNull = false; + bool pasteAsNullAsked = false; QList dataRow; QList> dataToPaste; @@ -740,14 +802,30 @@ void SqlQueryView::paste() if ((cell.at(0).isSpace() || cell.at(cell.size() - 1).isSpace()) && !trimOnPasteAsked) #endif { - QMessageBox::StandardButton choice; - choice = QMessageBox::question(this, tr("Trim pasted text?"), + QMessageBox::StandardButton trimChoice; + trimChoice = QMessageBox::question(this, tr("Trim pasted text?"), tr("The pasted text contains leading or trailing white space. Trim it automatically?")); trimOnPasteAsked = true; - trimOnPaste = (choice == QMessageBox::Yes); + trimOnPaste = (trimChoice == QMessageBox::Yes); } - dataRow << (trimOnPaste ? cell.trimmed() : cell); + if (cell=="NULL" && !pasteAsNullAsked) + { + QMessageBox::StandardButton nullChoice; + nullChoice = QMessageBox::question(this, tr("Paste \"NULL\" as null value?"), + tr("The pasted text contains \"NULL\" literals. Do you want to consider them as NULL values?")); + pasteAsNullAsked = true; + pasteAsNull = (nullChoice == QMessageBox::Yes); + } + + if (cell=="NULL" && pasteAsNull) + { + dataRow << QVariant(); + } + else + { + dataRow << (trimOnPaste ? cell.trimmed() : cell); + } } dataToPaste << dataRow; @@ -776,7 +854,7 @@ void SqlQueryView::setNull() if (selItem->getColumn()->editionForbiddenReason.size() > 0) continue; - selItem->setValue(QVariant(QString()), false, false); + selItem->setValue(QVariant(QString()), false); } } @@ -789,7 +867,7 @@ void SqlQueryView::erase() if (selItem->getColumn()->editionForbiddenReason.size() > 0) continue; - selItem->setValue("", false, false); + selItem->setValue("", false); } } @@ -836,11 +914,17 @@ void SqlQueryView::openValueEditor(SqlQueryItem* item) return; } + SqlQueryModelColumn* column = item->getColumn(); + MultiEditorDialog editor(this); + if (!column->getFkConstraints().isEmpty()) + editor.enableFk(getModel()->getDb(), column); + + editor.setDataType(column->dataType); editor.setWindowTitle(tr("Edit value")); - editor.setDataType(item->getColumn()->dataType); - editor.setValue(item->getFullValue()); - editor.setReadOnly(!item->getColumn()->canEdit()); + editor.setValue(item->getValue()); + editor.setReadOnly(!column->canEdit()); + if (editor.exec() == QDialog::Rejected) return; @@ -867,7 +951,7 @@ QSize SqlQueryView::Header::sectionSizeFromContents(int section) const { QSize originalSize = QHeaderView::sectionSizeFromContents(section); int colCount = dynamic_cast(parent())->getModel()->columnCount(); - if (colCount <= 5) + if (colCount <= 5 || CFG_UI.General.ColumnWidthForName.get()) return originalSize; int wd = minHeaderWidth; diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.h index 5e50ed1..6cd528d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.h @@ -5,7 +5,6 @@ #include "common/extactioncontainer.h" #include "db/queryexecutor.h" #include "guiSQLiteStudio_global.h" -#include "common/table.h" #include #include @@ -19,24 +18,27 @@ class QProgressBar; class QMenu; CFG_KEY_LIST(SqlQueryView, QObject::tr("Data grid view"), - CFG_KEY_ENTRY(COPY, Qt::CTRL + Qt::Key_C, QObject::tr("Copy cell(s) contents to clipboard")) - CFG_KEY_ENTRY(COPY_WITH_HEADER, Qt::CTRL + Qt::SHIFT + Qt::Key_C, QObject::tr("Copy cell(s) contents together with header to clipboard")) -// CFG_KEY_ENTRY(COPY_AS, Qt::CTRL + Qt::ALT + Qt::Key_C, QObject::tr("")) - CFG_KEY_ENTRY(PASTE, Qt::CTRL + Qt::Key_V, QObject::tr("Paste cell(s) contents from clipboard")) -// CFG_KEY_ENTRY(PASTE_AS, Qt::CTRL + Qt::ALT + Qt::Key_V, QObject::tr("")) - CFG_KEY_ENTRY(ERASE, Qt::ALT + Qt::Key_Backspace, QObject::tr("Set empty value to selected cell(s)")) - CFG_KEY_ENTRY(SET_NULL, Qt::Key_Backspace, QObject::tr("Set NULL value to selected cell(s)")) - CFG_KEY_ENTRY(COMMIT, Qt::CTRL + Qt::Key_Return, QObject::tr("Commit changes to cell(s) contents")) - CFG_KEY_ENTRY(ROLLBACK, Qt::CTRL + Qt::Key_Backspace, QObject::tr("Rollback changes to cell(s) contents")) - CFG_KEY_ENTRY(DELETE_ROW, Qt::Key_Delete, QObject::tr("Delete selected data row")) - CFG_KEY_ENTRY(INSERT_ROW, Qt::Key_Insert, QObject::tr("Insert new data row")) - CFG_KEY_ENTRY(OPEN_VALUE_EDITOR, Qt::ALT + Qt::Key_Return, QObject::tr("Open contents of selected cell in a separate editor")) + CFG_KEY_ENTRY(EDIT_CURRENT, Qt::Key_F2, QObject::tr("Edit current cell inline")) + CFG_KEY_ENTRY(COPY, Qt::CTRL + Qt::Key_C, QObject::tr("Copy cell(s) contents to clipboard")) + CFG_KEY_ENTRY(COPY_WITH_HEADER, Qt::CTRL + Qt::SHIFT + Qt::Key_C, QObject::tr("Copy cell(s) contents together with header to clipboard")) +// CFG_KEY_ENTRY(COPY_AS, Qt::CTRL + Qt::ALT + Qt::Key_C, QObject::tr("")) + CFG_KEY_ENTRY(PASTE, Qt::CTRL + Qt::Key_V, QObject::tr("Paste cell(s) contents from clipboard")) +// CFG_KEY_ENTRY(PASTE_AS, Qt::CTRL + Qt::ALT + Qt::Key_V, QObject::tr("")) + CFG_KEY_ENTRY(ERASE, Qt::ALT + Qt::Key_Backspace, QObject::tr("Set empty value to selected cell(s)")) + CFG_KEY_ENTRY(SET_NULL, Qt::Key_Backspace, QObject::tr("Set NULL value to selected cell(s)")) + CFG_KEY_ENTRY(COMMIT, Qt::CTRL + Qt::Key_Return, QObject::tr("Commit changes to cell(s) contents")) + CFG_KEY_ENTRY(ROLLBACK, Qt::ALT + Qt::SHIFT + Qt::Key_Backspace, QObject::tr("Rollback changes to cell(s) contents")) + CFG_KEY_ENTRY(DELETE_ROW, Qt::Key_Delete, QObject::tr("Delete selected data row")) + CFG_KEY_ENTRY(INSERT_ROW, Qt::Key_Insert, QObject::tr("Insert new data row")) + CFG_KEY_ENTRY(OPEN_VALUE_EDITOR, Qt::ALT + Qt::Key_Return, QObject::tr("Open contents of selected cell in a separate editor")) + CFG_KEY_ENTRY(ADJUST_ROWS_SIZE, Qt::ALT + Qt::Key_H, QObject::tr("Toggle the height adjustment of rows")) + CFG_KEY_ENTRY(INCR_FONT_SIZE, Qt::CTRL + Qt::Key_Plus, QObject::tr("Increase font size", "data view")) + CFG_KEY_ENTRY(DECR_FONT_SIZE, Qt::CTRL + Qt::Key_Minus, QObject::tr("Decrease font size", "data view")) ) class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer { - Q_OBJECT - Q_ENUMS(Action) + Q_OBJECT public: enum Action @@ -55,15 +57,20 @@ class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer DELETE_ROW, SELECTIVE_COMMIT, SELECTIVE_ROLLBACK, + EDIT_CURRENT, OPEN_VALUE_EDITOR, SORT_DIALOG, RESET_SORTING, - LOAD_FULL_VALUES, GENERATE_SELECT, GENERATE_INSERT, GENERATE_UPDATE, - GENERATE_DELETE + GENERATE_DELETE, + INVERT_SELECTION, + ADJUST_ROWS_SIZE, + INCR_FONT_SIZE, + DECR_FONT_SIZE }; + Q_ENUM(Action) enum ToolBar { @@ -86,7 +93,6 @@ class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer protected: void scrollContentsBy(int dx, int dy); - void mouseMoveEvent(QMouseEvent *event); private: class Header : public QHeaderView @@ -110,6 +116,7 @@ class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer void addFkActionsToContextMenu(SqlQueryItem* currentItem); void goToReferencedRow(const QString& table, const QString& column, const QVariant& value); void copy(bool withHeaders); + void changeFontSize(int factor); constexpr static const char* mimeDataId = "application/x-sqlitestudio-data-view-data"; constexpr static const int minHeaderWidth = 15; @@ -125,8 +132,6 @@ class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer bool simpleBrowserMode = false; bool ignoreColumnWidthChanges = false; int beforeExecutionHorizontalPosition = -1; - int headerContextMenuSection = -1; - QModelIndex indexUnderCursor; private slots: void updateCommitRollbackActions(bool enabled); @@ -141,7 +146,13 @@ class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer void generateInsert(); void generateUpdate(); void generateDelete(); - void loadFullValuesForColumn(); + void editCurrent(); + void toggleRowsHeightAdjustment(bool enabled); + void adjustRowToContents(int section); + void fontSizeChangeRequested(int delta); + void incrFontSize(); + void decrFontSize(); + void invertSelection(); public slots: void executionStarted(); diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.cpp index 50db973..50b0c5b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.cpp @@ -10,15 +10,10 @@ #include SqlTableModel::SqlTableModel(QObject *parent) : - SqlQueryModel(parent) + SqlDataSourceQueryModel(parent) { } -QString SqlTableModel::getDatabase() const -{ - return database; -} - QString SqlTableModel::getTable() const { return table; @@ -29,13 +24,7 @@ void SqlTableModel::setDatabaseAndTable(const QString& database, const QString& this->database = database; this->table = table; setQuery("SELECT * FROM "+getDataSource()); - - QString dbName = database; - if (database.toLower() == "main" || database.isEmpty()) - dbName = QString(); - - tablesInUse.clear(); - tablesInUse << DbAndTable(db, dbName, table); + updateTablesInUse(table); SchemaResolver resolver(db); isWithOutRowIdTable = resolver.isWithoutRowIdTable(database, table); @@ -156,109 +145,11 @@ bool SqlTableModel::commitDeletedRow(const QList& itemsInRow, QLi return true; } -void SqlTableModel::applyFilter(const QString& value, FilterValueProcessor valueProc) -{ - static_qstring(sql, "SELECT * FROM %1 WHERE %2"); - - if (value.isEmpty()) - { - resetFilter(); - return; - } - - QStringList conditions; - for (SqlQueryModelColumnPtr column : columns) - conditions << wrapObjIfNeeded(column->column)+" "+valueProc(value); - - setQuery(sql.arg(getDataSource(), conditions.join(" OR "))); - executeQuery(); -} - -void SqlTableModel::applyFilter(const QStringList& values, FilterValueProcessor valueProc) -{ - static_qstring(sql, "SELECT * FROM %1 WHERE %2"); - if (values.isEmpty()) - { - resetFilter(); - return; - } - - if (values.size() != columns.size()) - { - qCritical() << "Asked to per-column filter, but number columns" - << columns.size() << "is different than number of values" << values.size(); - return; - } - - QStringList conditions; - for (int i = 0, total = columns.size(); i < total; ++i) - { - if (values[i].isEmpty()) - continue; - - conditions << wrapObjIfNeeded(columns[i]->column)+" "+valueProc(values[i]); - } - - setQuery(sql.arg(getDataSource(), conditions.join(" AND "))); - executeQuery(); -} - -QString SqlTableModel::stringFilterValueProcessor(const QString& value) -{ - static_qstring(pattern, "LIKE '%%1%'"); - return pattern.arg(escapeString(value)); -} - -QString SqlTableModel::regExpFilterValueProcessor(const QString& value) -{ - static_qstring(pattern, "REGEXP '%1'"); - return pattern.arg(escapeString(value)); -} - bool SqlTableModel::supportsModifyingQueriesInMenu() const { return true; } -void SqlTableModel::applySqlFilter(const QString& value) -{ - if (value.isEmpty()) - { - resetFilter(); - return; - } - - setQuery("SELECT * FROM "+getDataSource()+" WHERE "+value); - executeQuery(); -} - -void SqlTableModel::applyStringFilter(const QString& value) -{ - applyFilter(value, &stringFilterValueProcessor); -} - -void SqlTableModel::applyStringFilter(const QStringList& values) -{ - applyFilter(values, &stringFilterValueProcessor); -} - -void SqlTableModel::applyRegExpFilter(const QString& value) -{ - applyFilter(value, ®ExpFilterValueProcessor); -} - -void SqlTableModel::applyRegExpFilter(const QStringList& values) -{ - applyFilter(values, ®ExpFilterValueProcessor); -} - -void SqlTableModel::resetFilter() -{ - setQuery("SELECT * FROM "+getDataSource()); - //reload(); - executeQuery(); -} - QString SqlTableModel::generateSelectQueryForItems(const QList& items) { QHash values = toValuesGroupedByColumns(items); @@ -435,14 +326,6 @@ void SqlTableModel::processDefaultValueAfterInsert(QHashvalue(colIdx++); } -QString SqlTableModel::getDatabasePrefix() -{ - if (database.isNull()) - return ""; // not "main.", because the "main." doesn't work for TEMP tables, such as sqlite_temp_master - - return wrapObjIfNeeded(database) + "."; -} - QString SqlTableModel::getDataSource() { return getDatabasePrefix() + wrapObjIfNeeded(table); @@ -490,7 +373,7 @@ void SqlTableModel::updateColumnsAndValues(const QList& itemsInRo colNameList << wrapObjIfNeeded(modelColumn->column); sqlValues << ":arg" + QString::number(i); - args << item->getFullValue(); + args << item->getValue(); } } diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.h index 3a1ab36..665e014 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.h @@ -2,25 +2,18 @@ #define SQLTABLEMODEL_H #include "guiSQLiteStudio_global.h" -#include "sqlquerymodel.h" +#include "sqldatasourcequerymodel.h" -class GUI_API_EXPORT SqlTableModel : public SqlQueryModel +class GUI_API_EXPORT SqlTableModel : public SqlDataSourceQueryModel { Q_OBJECT public: explicit SqlTableModel(QObject *parent = 0); - QString getDatabase() const; QString getTable() const; void setDatabaseAndTable(const QString& database, const QString& table); Features features() const; - void applySqlFilter(const QString& value); - void applyStringFilter(const QString& value); - void applyStringFilter(const QStringList& values); - void applyRegExpFilter(const QString& value); - void applyRegExpFilter(const QStringList& values); - void resetFilter(); QString generateSelectQueryForItems(const QList& items); QString generateInsertQueryForItems(const QList& items); QString generateUpdateQueryForItems(const QList& items); @@ -31,6 +24,8 @@ class GUI_API_EXPORT SqlTableModel : public SqlQueryModel bool commitAddedRow(const QList& itemsInRow, QList& successfulCommitHandlers); bool commitDeletedRow(const QList& itemsInRow, QList& successfulCommitHandlers); + QString getDataSource(); + private: class CommitDeleteQueryBuilder : public CommitUpdateQueryBuilder { @@ -45,13 +40,6 @@ class GUI_API_EXPORT SqlTableModel : public SqlQueryModel void addColumn(const QString& col); }; - typedef std::function FilterValueProcessor; - - static QString stringFilterValueProcessor(const QString& value); - static QString regExpFilterValueProcessor(const QString& value); - - void applyFilter(const QString& value, FilterValueProcessor valueProc); - void applyFilter(const QStringList& values, FilterValueProcessor valueProc); void updateColumnsAndValuesWithDefaultValues(const QList& modelColumns, QStringList& colNameList, QStringList& sqlValues, QList& args); void updateColumnsAndValues(const QList& itemsInRow, const QList& modelColumns, @@ -65,11 +53,7 @@ class GUI_API_EXPORT SqlTableModel : public SqlQueryModel void processDefaultValueAfterInsert(QHash& columnsToReadFromDb, QHash& values, RowId rowId); - QString getDatabasePrefix(); - QString getDataSource(); - QString table; - QString database; bool isWithOutRowIdTable = false; }; diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlviewmodel.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlviewmodel.cpp index 653e4b5..1f8422c 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlviewmodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlviewmodel.cpp @@ -2,7 +2,7 @@ #include "querygenerator.h" SqlViewModel::SqlViewModel(QObject *parent) : - SqlQueryModel(parent) + SqlDataSourceQueryModel(parent) { } @@ -14,7 +14,17 @@ QString SqlViewModel::generateSelectQueryForItems(const QList& it return generator.generateSelectFromView(db, view, values); } -void SqlViewModel::setView(const QString& view) +void SqlViewModel::setDatabaseAndView(const QString& database, const QString& view) { + this->database = database; this->view = view; + //setQuery("SELECT * FROM "+getDataSource()); + updateTablesInUse(view); + + SchemaResolver resolver(db); +} + +QString SqlViewModel::getDataSource() +{ + return getDatabasePrefix() + wrapObjIfNeeded(view); } diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlviewmodel.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlviewmodel.h index 19efbce..e93eff5 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlviewmodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlviewmodel.h @@ -1,15 +1,20 @@ #ifndef SQLVIEWMODEL_H #define SQLVIEWMODEL_H -#include "sqlquerymodel.h" +#include "sqldatasourcequerymodel.h" -class SqlViewModel : public SqlQueryModel +class SqlViewModel : public SqlDataSourceQueryModel { public: - SqlViewModel(QObject *parent = 0); + explicit SqlViewModel(QObject *parent = 0); + + QString getView() const; QString generateSelectQueryForItems(const QList& items); - void setView(const QString& view); + void setDatabaseAndView(const QString& database, const QString& view); + + protected: + QString getDataSource(); private: QString view; diff --git a/SQLiteStudio3/guiSQLiteStudio/dataview.cpp b/SQLiteStudio3/guiSQLiteStudio/dataview.cpp index 568954c..e7ae9b5 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dataview.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dataview.cpp @@ -1,11 +1,9 @@ #include "dataview.h" -#include "datagrid/sqltablemodel.h" #include "datagrid/sqlquerymodel.h" #include "datagrid/sqlqueryview.h" #include "formview.h" #include "common/extlineedit.h" #include "mainwindow.h" -#include "statusfield.h" #include "common/intvalidator.h" #include "common/extaction.h" #include "iconmanager.h" @@ -77,7 +75,7 @@ void DataView::initSlots() connect(model, SIGNAL(executionStarted()), gridView, SLOT(executionStarted())); connect(model, SIGNAL(loadingEnded(bool)), gridView, SLOT(executionEnded())); connect(model, SIGNAL(totalRowsAndPagesAvailable()), this, SLOT(totalRowsAndPagesAvailable())); - connect(gridView->horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(columnsHeaderClicked(int))); + connect(gridView->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(columnsHeaderDoubleClicked(int))); connect(this, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); connect(model, SIGNAL(itemEditionEnded(SqlQueryItem*)), this, SLOT(adjustColumnWidth(SqlQueryItem*))); connect(gridView, SIGNAL(scrolledBy(int, int)), this, SLOT(syncFilterScrollPosition())); @@ -235,8 +233,8 @@ void DataView::createActions() createAction(SELECTIVE_COMMIT, ICONS.COMMIT, tr("Commit changes for selected cells", "data view"), this, SLOT(selectiveCommitGrid()), this); createAction(SELECTIVE_ROLLBACK, ICONS.ROLLBACK, tr("Rollback changes for selected cells", "data view"), this, SLOT(selectiveRollbackGrid()), this); - createAction(SHOW_GRID_VIEW, tr("Show grid view of results", "sql editor"), this, SLOT(showGridView()), this); - createAction(SHOW_FORM_VIEW, tr("Show form view of results", "sql editor"), this, SLOT(showFormView()), this); + createAction(SHOW_GRID_VIEW, tr("Show grid view of results", "data view"), this, SLOT(showGridView()), this); + createAction(SHOW_FORM_VIEW, tr("Show form view of results", "data view"), this, SLOT(showFormView()), this); connect(gridView, SIGNAL(requestForRowInsert()), this, SLOT(insertRow())); connect(gridView, SIGNAL(requestForMultipleRowInsert()), this, SLOT(insertMultipleRows())); @@ -310,14 +308,11 @@ void DataView::resizeColumnsInitiallyToContents() continue; } + int headerMinSize = qMax(gridView->horizontalHeader()->sizeHintForColumn(i), 60); if (wd > CFG_UI.General.MaxInitialColumnWith.get()) - { gridView->setColumnWidth(i, CFG_UI.General.MaxInitialColumnWith.get()); - } - else if (wd < 60) - { - gridView->setColumnWidth(i, 60); - } + else if (wd < headerMinSize) + gridView->setColumnWidth(i, headerMinSize); } gridView->setIgnoreColumnWidthChanges(false); } @@ -427,7 +422,6 @@ void DataView::goToFormRow(IndexModifier idxMod) return; gridView->setCurrentIndex(newRowIdx); - model->loadFullDataForEntireRow(row); formView->updateFromGrid(); updateCurrentFormViewRow(); } @@ -551,6 +545,9 @@ void DataView::adjustColumnWidth(SqlQueryItem* item) if (model->getDesiredColumnWidth(col) > -1) return; + if (!CFG_UI.General.EnlargeColumnForValue.get()) + return; + gridView->resizeColumnToContents(col); if (gridView->columnWidth(col) > CFG_UI.General.MaxInitialColumnWith.get()) gridView->setColumnWidth(col, CFG_UI.General.MaxInitialColumnWith.get()); @@ -564,6 +561,9 @@ void DataView::syncFilterScrollPosition() void DataView::resizeFilter(int section, int oldSize, int newSize) { UNUSED(oldSize); + if (!model->features().testFlag(SqlQueryModel::FILTERING)) + return; + if (filterInputs.isEmpty()) return; @@ -841,6 +841,9 @@ void DataView::applyFilter() case DataView::FilterMode::REGEXP: model->applyRegExpFilter(filterValues); break; + case FilterMode::EXACT: + model->applyStrictFilter(filterValues); + break; } } else @@ -857,6 +860,9 @@ void DataView::applyFilter() case DataView::FilterMode::REGEXP: model->applyRegExpFilter(value); break; + case DataView::FilterMode::EXACT: + model->applyStrictFilter(value); + break; } } } @@ -938,19 +944,22 @@ void DataView::formViewFocusFirstEditor() void DataView::recreateFilterInputs() { + if (!model->features().testFlag(SqlQueryModel::FILTERING)) + return; + qApp->processEvents(); - for (QLineEdit* edit : filterInputs) + for (ExtLineEdit*& edit : filterInputs) delete edit; filterInputs.clear(); filterLeftSpacer->setFixedSize(gridView->verticalHeader()->width() + 1, 1); - QLineEdit* edit = nullptr; + ExtLineEdit* edit = nullptr; for (int i = 0, total = gridView->horizontalHeader()->count(); i < total; ++i) { - edit = new QLineEdit(perColumnWidget); + edit = new ExtLineEdit(perColumnWidget); edit->setPlaceholderText(tr("Filter")); edit->setClearButtonEnabled(true); edit->setFixedWidth(gridView->columnWidth(i)); @@ -982,20 +991,24 @@ void DataView::recreateFilterInputs() void DataView::createFilteringActions() { - createAction(FILTER_STRING, ICONS.APPLY_FILTER_TXT, tr("Filter by text", "data view"), this, SLOT(filterModeSelected()), this); + createAction(FILTER_STRING, ICONS.APPLY_FILTER_TXT, tr("Filter by text (if contains)", "data view"), this, SLOT(filterModeSelected()), this); + createAction(FILTER_EXACT, ICONS.APPLY_FILTER_TXT_STRICT, tr("Filter strictly by text (if equals)", "data view"), this, SLOT(filterModeSelected()), this); createAction(FILTER_REGEXP, ICONS.APPLY_FILTER_RE, tr("Filter by the Regular Expression", "data view"), this, SLOT(filterModeSelected()), this); createAction(FILTER_SQL, ICONS.APPLY_FILTER_SQL, tr("Filter by SQL expression", "data view"), this, SLOT(filterModeSelected()), this); actionMap[FILTER_STRING]->setProperty(DATA_VIEW_FILTER_PROP, static_cast(FilterMode::STRING)); + actionMap[FILTER_EXACT]->setProperty(DATA_VIEW_FILTER_PROP, static_cast(FilterMode::EXACT)); actionMap[FILTER_REGEXP]->setProperty(DATA_VIEW_FILTER_PROP, static_cast(FilterMode::REGEXP)); actionMap[FILTER_SQL]->setProperty(DATA_VIEW_FILTER_PROP, static_cast(FilterMode::SQL)); QActionGroup* filterGroup = new QActionGroup(gridToolBar); filterGroup->addAction(actionMap[FILTER_STRING]); + filterGroup->addAction(actionMap[FILTER_EXACT]); filterGroup->addAction(actionMap[FILTER_SQL]); filterGroup->addAction(actionMap[FILTER_REGEXP]); actionMap[FILTER_STRING]->setCheckable(true); + actionMap[FILTER_EXACT]->setCheckable(true); actionMap[FILTER_REGEXP]->setCheckable(true); actionMap[FILTER_SQL]->setCheckable(true); actionMap[FILTER_STRING]->setChecked(true); @@ -1006,6 +1019,7 @@ void DataView::createFilteringActions() actionMap[FILTER_VALUE] = gridToolBar->addWidget(filterEdit); createAction(FILTER, tr("Apply filter", "data view"), this, SLOT(applyFilter()), gridToolBar); attachActionInMenu(FILTER, actionMap[FILTER_STRING], gridToolBar); + attachActionInMenu(FILTER, actionMap[FILTER_EXACT], gridToolBar); attachActionInMenu(FILTER, actionMap[FILTER_REGEXP], gridToolBar); attachActionInMenu(FILTER, actionMap[FILTER_SQL], gridToolBar); addSeparatorInMenu(FILTER, gridToolBar); @@ -1018,7 +1032,8 @@ void DataView::createFilteringActions() gridView->getHeaderContextMenu()->addAction(actionMap[FILTER_PER_COLUMN]); } -void DataView::columnsHeaderClicked(int columnIdx) + +void DataView::columnsHeaderDoubleClicked(int columnIdx) { model->changeSorting(columnIdx); } @@ -1038,8 +1053,6 @@ void DataView::tabChanged(int newIndex) if (!gridView->getCurrentIndex().isValid() && model->rowCount() > 0) gridView->setCurrentRow(0); - int row = gridView->getCurrentIndex().row(); - model->loadFullDataForEntireRow(row); formView->updateFromGrid(); updateCurrentFormViewRow(); break; diff --git a/SQLiteStudio3/guiSQLiteStudio/dataview.h b/SQLiteStudio3/guiSQLiteStudio/dataview.h index 55c7895..d8f71dc 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dataview.h +++ b/SQLiteStudio3/guiSQLiteStudio/dataview.h @@ -26,8 +26,7 @@ CFG_KEY_LIST(DataView, QObject::tr("Data view (both grid and form)"), class GUI_API_EXPORT DataView : public QTabWidget, public ExtActionContainer { - Q_OBJECT - Q_ENUMS(Action) + Q_OBJECT public: enum Action @@ -48,6 +47,7 @@ class GUI_API_EXPORT DataView : public QTabWidget, public ExtActionContainer FILTER_STRING, FILTER_SQL, FILTER_REGEXP, + FILTER_EXACT, FILTER_PER_COLUMN, GRID_TOTAL_ROWS, SELECTIVE_COMMIT, @@ -59,6 +59,7 @@ class GUI_API_EXPORT DataView : public QTabWidget, public ExtActionContainer FORM_TOTAL_ROWS, FORM_CURRENT_ROW }; + Q_ENUM(Action) enum class ActionGroup { @@ -113,7 +114,8 @@ class GUI_API_EXPORT DataView : public QTabWidget, public ExtActionContainer { STRING, SQL, - REGEXP + REGEXP, + EXACT }; static void createStaticActions(); @@ -169,7 +171,7 @@ class GUI_API_EXPORT DataView : public QTabWidget, public ExtActionContainer bool uncommittedGrid = false; bool uncommittedForm = false; WidgetCover* widgetCover = nullptr; - QList filterInputs; + QList filterInputs; QStringList filterValues; QWidget* filterLeftSpacer = nullptr; QWidget* filterRightSpacer = nullptr; @@ -203,7 +205,7 @@ class GUI_API_EXPORT DataView : public QTabWidget, public ExtActionContainer void prevRow(); void nextRow(); void lastRow(); - void columnsHeaderClicked(int columnIdx); + void columnsHeaderDoubleClicked(int columnIdx); void tabChanged(int newIndex); void updateFormNavigationState(); void updateFormCommitRollbackActions(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dblistmodel.cpp b/SQLiteStudio3/guiSQLiteStudio/dblistmodel.cpp index a03675f..61bca90 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dblistmodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dblistmodel.cpp @@ -172,6 +172,9 @@ void DbListModel::dbDisconnected(Db* db) comboBox->setCurrentText(current); else if (newIdx > -1) comboBox->setCurrentIndex(newIdx); + + if (dbList.isEmpty()) + emit listCleared(); } DbListModel::DbTreeComparer::DbTreeComparer() diff --git a/SQLiteStudio3/guiSQLiteStudio/dblistmodel.h b/SQLiteStudio3/guiSQLiteStudio/dblistmodel.h index c375674..1bff305 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dblistmodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/dblistmodel.h @@ -68,6 +68,9 @@ class GUI_API_EXPORT DbListModel : public QAbstractListModel private slots: void dbConnected(Db* db); void dbDisconnected(Db* db); + + signals: + void listCleared(); }; #endif // DBLISTMODEL_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.cpp b/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.cpp index 05b614b..42f2841 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.cpp @@ -4,6 +4,7 @@ #include "dialogs/triggerdialog.h" #include "common/utils_sql.h" #include "dbtree/dbtree.h" +#include "schemaresolver.h" #include "services/notifymanager.h" #include "mdiarea.h" #include "mdiwindow.h" @@ -121,27 +122,31 @@ ViewWindow* DbObjectDialogs::editView(const QString& database, const QString& vi return win; } -void DbObjectDialogs::editObject(const QString& name) +void DbObjectDialogs::editObject(Type type, const QString& name) { - editObject("main", name); + editObject(type, "main", name); } -void DbObjectDialogs::editObject(const QString& database, const QString& name) +void DbObjectDialogs::editObject(Type type, const QString& database, const QString& name) { - Type type = getObjectType(database, name); + SchemaResolver schemaResolver(db); + QString normalizedName = schemaResolver.normalizeCaseObjectName(database, name); + if (type == Type::UNKNOWN) + type = getObjectType(database, normalizedName); + switch (type) { case Type::TABLE: - editTable(database, name); + editTable(database, normalizedName); break; case Type::INDEX: - editIndex(name); + editIndex(normalizedName); break; case Type::TRIGGER: - editTrigger(name); + editTrigger(normalizedName); break; case Type::VIEW: - editView(database, name); + editView(database, normalizedName); break; default: { @@ -151,19 +156,20 @@ void DbObjectDialogs::editObject(const QString& database, const QString& name) } } -bool DbObjectDialogs::dropObject(const QString& name) +bool DbObjectDialogs::dropObject(Type type, const QString& name) { - return dropObject("main", name); + return dropObject(type, "main", name); } -bool DbObjectDialogs::dropObject(const QString& database, const QString& name) +bool DbObjectDialogs::dropObject(Type type, const QString& database, const QString& name) { - static const QString dropSql2 = "DROP %1 %2;"; static const QString dropSql3 = "DROP %1 %2.%3;"; QString dbName = wrapObjIfNeeded(database); - Type type = getObjectType(database, name); + if (type == Type::UNKNOWN) + type = getObjectType(database, name); + QString title; QString message; QString typeForSql; @@ -210,7 +216,7 @@ bool DbObjectDialogs::dropObject(const QString& database, const QString& name) results = db->exec(finalSql); if (results->isError()) { - notifyError(tr("Error while dropping %1: %2").arg(name).arg(results->getErrorText())); + notifyError(tr("Error while dropping %1: %2").arg(name, results->getErrorText())); qCritical() << "Error while dropping object " << database << "." << name << ":" << results->getErrorText(); return false; } @@ -366,7 +372,7 @@ void DbObjectDialogs::setNoConfirmation(bool value) TableWindow* DbObjectDialogs::editTable(const QString& database, const QString& table) { TableWindow* win = nullptr; - for (MdiWindow* mdiWin : mdiArea->getWindows()) + for (MdiWindow*& mdiWin : mdiArea->getWindows()) { win = dynamic_cast(mdiWin->getMdiChild()); if (!win) diff --git a/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.h b/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.h index 5be09cc..7003356 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.h +++ b/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.h @@ -17,6 +17,15 @@ class GUI_API_EXPORT DbObjectDialogs : public QObject Q_OBJECT public: + enum class Type + { + TABLE = 0, + INDEX = 1, + TRIGGER = 2, + VIEW = 3, + UNKNOWN = -1 + }; + explicit DbObjectDialogs(Db* db); DbObjectDialogs(Db* db, QWidget* parentWidget); @@ -35,10 +44,10 @@ class GUI_API_EXPORT DbObjectDialogs : public QObject ViewWindow* addView(const QString& initialSelect = QString()); ViewWindow* editView(const QString& database, const QString& view); - void editObject(const QString& name); - void editObject(const QString& database, const QString& name); - bool dropObject(const QString& name); - bool dropObject(const QString& database, const QString& name); + void editObject(Type type, const QString& name); + void editObject(Type type, const QString& database, const QString& name); + bool dropObject(Type type, const QString& name); + bool dropObject(Type type, const QString& database, const QString& name); bool dropObjects(const QStringList& names); bool dropObjects(const QHash& objects); @@ -49,15 +58,6 @@ class GUI_API_EXPORT DbObjectDialogs : public QObject void setNoSchemaRefreshing(bool value); private: - enum class Type - { - TABLE = 0, - INDEX = 1, - TRIGGER = 2, - VIEW = 3, - UNKNOWN = -1 - }; - Type getObjectType(const QString& database, const QString& name); QHash > groupObjects(const QHash& objects); diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp index 85a7047..0540aae 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp @@ -3,7 +3,6 @@ #include "ui_dbtree.h" #include "actionentry.h" #include "common/utils_sql.h" -#include "common/utils.h" #include "dbtreemodel.h" #include "dialogs/dbdialog.h" #include "services/dbmanager.h" @@ -11,14 +10,11 @@ #include "common/global.h" #include "services/notifymanager.h" #include "mainwindow.h" -#include "mdiarea.h" #include "common/unused.h" #include "dbobjectdialogs.h" #include "common/userinputfilter.h" #include "common/widgetcover.h" #include "windows/tablewindow.h" -#include "dialogs/indexdialog.h" -#include "dialogs/triggerdialog.h" #include "dialogs/exportdialog.h" #include "dialogs/importdialog.h" #include "dialogs/populatedialog.h" @@ -30,6 +26,8 @@ #include "dialogs/execfromfiledialog.h" #include "dialogs/fileexecerrorsdialog.h" #include "common/compatibility.h" +#include "sqlfileexecutor.h" +#include "common/mouseshortcut.h" #include #include #include @@ -76,6 +74,8 @@ void DbTree::init() THEME_TUNER->manageCompactLayout(widget()); + fileExecutor = new SqlFileExecutor(this); + ui->nameFilter->setClearButtonEnabled(true); treeRefreshWidgetCover = new WidgetCover(this); @@ -87,25 +87,12 @@ void DbTree::init() fileExecWidgetCover->initWithInterruptContainer(); fileExecWidgetCover->displayProgress(100); fileExecWidgetCover->hide(); - connect(fileExecWidgetCover, &WidgetCover::cancelClicked, [this]() - { - if (!this->executingQueriesFromFile) - return; - - this->executingQueriesFromFile = 0; - - if (this->executingQueriesFromFileDb) // should always be there, but just in case - { - this->executingQueriesFromFileDb->interrupt(); - this->executingQueriesFromFileDb->rollback(); - this->executingQueriesFromFileDb = nullptr; - notifyWarn(tr("Execution from file cancelled. Any queries executed so far have been rolled back.")); - } - }); - connect(this, &DbTree::updateFileExecProgress, this, &DbTree::setFileExecProgress, Qt::QueuedConnection); - connect(this, &DbTree::fileExecCoverToBeClosed, this, &DbTree::hideFileExecCover, Qt::QueuedConnection); - connect(this, &DbTree::fileExecErrors, this, &DbTree::showFileExecErrors, Qt::QueuedConnection); - connect(this, SIGNAL(schemaNeedsRefreshing(Db*)), this, SLOT(refreshSchema(Db*)), Qt::QueuedConnection); + connect(fileExecWidgetCover, &WidgetCover::cancelClicked, fileExecutor, &SqlFileExecutor::stopExecution); + connect(fileExecutor, SIGNAL(updateProgress(int)), this, SLOT(setFileExecProgress(int)), Qt::QueuedConnection); + connect(fileExecutor, SIGNAL(execEnded()), this, SLOT(hideFileExecCover()), Qt::QueuedConnection); + connect(fileExecutor, SIGNAL(execErrors(QList>, bool)), this, SLOT(showFileExecErrors(QList>, bool)), + Qt::QueuedConnection); + connect(fileExecutor, SIGNAL(schemaNeedsRefreshing(Db*)), this, SLOT(refreshSchema(Db*)), Qt::QueuedConnection); treeModel = new DbTreeModel(); treeModel->setTreeView(ui->treeView); @@ -127,6 +114,13 @@ void DbTree::init() connect(IMPORT_MANAGER, SIGNAL(schemaModified(Db*)), this, SLOT(refreshSchema(Db*))); connect(CFG_UI.Fonts.DbTree, SIGNAL(changed(QVariant)), this, SLOT(refreshFont())); + MouseShortcut::forWheel(Qt::ControlModifier, this, SLOT(fontSizeChangeRequested(int)), ui->treeView->viewport()); + + connect(treeModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(sessionValueChanged())); + connect(treeModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SIGNAL(sessionValueChanged())); + connect(treeModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(sessionValueChanged())); + connect(ui->treeView, SIGNAL(expanded(QModelIndex)), this, SIGNAL(sessionValueChanged())); + connect(ui->treeView, SIGNAL(collapsed(QModelIndex)), this, SIGNAL(sessionValueChanged())); updateActionsForCurrent(); } @@ -179,6 +173,8 @@ void DbTree::createActions() createAction(GENERATE_DELETE, "DELETE", this, SLOT(generateDeleteForTable()), this); createAction(OPEN_DB_DIRECTORY, ICONS.DIRECTORY_OPEN_WITH_DB, tr("Open file's directory"), this, SLOT(openDbDirectory()), this); createAction(EXEC_SQL_FROM_FILE, ICONS.EXEC_SQL_FROM_FILE, tr("Execute SQL from file"), this, SLOT(execSqlFromFile()), this); + createAction(INCR_FONT_SIZE, tr("Increase font size", "database list"), this, SLOT(incrFontSize()), this); + createAction(DECR_FONT_SIZE, tr("Decrease font size", "database list"), this, SLOT(decrFontSize()), this); } void DbTree::updateActionStates(const QStandardItem *item) @@ -255,7 +251,7 @@ void DbTree::updateActionStates(const QStandardItem *item) break; case DbTreeItem::Type::TRIGGERS: { - if (parentItem->getType() == DbTreeItem::Type::TABLE) + if (parentItem && parentItem->getType() == DbTreeItem::Type::TABLE) { enabled << EDIT_TABLE << DEL_TABLE; enabled << ADD_INDEX << ADD_TRIGGER; @@ -271,7 +267,7 @@ void DbTree::updateActionStates(const QStandardItem *item) } case DbTreeItem::Type::TRIGGER: { - if (grandParentItem->getType() == DbTreeItem::Type::TABLE) + if (grandParentItem && grandParentItem->getType() == DbTreeItem::Type::TABLE) { enabled << EDIT_TABLE << DEL_TABLE; enabled << ADD_INDEX << ADD_TRIGGER; @@ -490,7 +486,7 @@ void DbTree::setupActionsForMenu(DbTreeItem* currItem, QMenu* contextMenu) { actions += ActionEntry(ADD_TRIGGER); actions += ActionEntry(_separator); - if (parentItem->getType() == DbTreeItem::Type::TABLE) + if (parentItem && parentItem->getType() == DbTreeItem::Type::TABLE) { actions += ActionEntry(ADD_TABLE); actions += ActionEntry(EDIT_TABLE); @@ -512,7 +508,7 @@ void DbTree::setupActionsForMenu(DbTreeItem* currItem, QMenu* contextMenu) actions += ActionEntry(EDIT_TRIGGER); actions += ActionEntry(DEL_TRIGGER); actions += ActionEntry(_separator); - if (grandParentItem->getType() == DbTreeItem::Type::TABLE) + if (grandParentItem && grandParentItem->getType() == DbTreeItem::Type::TABLE) { actions += ActionEntry(ADD_TABLE); actions += ActionEntry(EDIT_TABLE); @@ -707,7 +703,7 @@ bool DbTree::areDbTreeItemsValidForItem(QList srcItems, const DbTre srcDbs << srcItem->getDb(); } - for (DbTreeItem::Type srcType : srcTypes) + for (DbTreeItem::Type& srcType : srcTypes) { if (!allowedTypesInside[dstType].contains(srcType)) return false; @@ -854,27 +850,26 @@ void DbTree::editTrigger(DbTreeItem* item) dialogs.editTrigger(item->text()); } -void DbTree::delSelectedObject() -{ - Db* db = getSelectedOpenDb(); - if (!db) - return; +//void DbTree::delSelectedObject() +//{ +// Db* db = getSelectedOpenDb(); +// if (!db) +// return; - DbTreeItem* item = ui->treeView->currentItem(); - if (!item) - return; +// DbTreeItem* item = ui->treeView->currentItem(); +// if (!item) +// return; - DbObjectDialogs dialogs(db); - dialogs.dropObject(item->text()); // TODO add database prefix when supported -} +// DbObjectDialogs dialogs(db); +// dialogs.dropObject(item->text()); // TODO add database prefix when supported +//} void DbTree::filterUndeletableItems(QList& items) { QMutableListIterator it(items); - DbTreeItem::Type type; while (it.hasNext()) { - type = it.next()->getType(); + DbTreeItem::Type type = it.next()->getType(); switch (type) { case DbTreeItem::Type::TABLES: @@ -918,27 +913,28 @@ void DbTree::filterItemsWithParentInList(QList& items) void DbTree::deleteItem(DbTreeItem* item) { + DbObjectDialogs::Type objType = DbObjectDialogs::Type::UNKNOWN; switch (item->getType()) { case DbTreeItem::Type::DIR: treeModel->deleteGroup(item); - break; + return; case DbTreeItem::Type::DB: DBLIST->removeDb(item->getDb()); - break; + return; case DbTreeItem::Type::TABLE: case DbTreeItem::Type::VIRTUAL_TABLE: + objType = DbObjectDialogs::Type::TABLE; + break; case DbTreeItem::Type::INDEX: + objType = DbObjectDialogs::Type::INDEX; + break; case DbTreeItem::Type::TRIGGER: + objType = DbObjectDialogs::Type::TRIGGER; + break; case DbTreeItem::Type::VIEW: - { - Db* db = item->getDb(); - DbObjectDialogs dialogs(db); - dialogs.setNoConfirmation(true); // confirmation is done in deleteSelected() - dialogs.setNoSchemaRefreshing(true); // we will refresh after all items are deleted - dialogs.dropObject(item->text()); // TODO database name when supported + objType = DbObjectDialogs::Type::VIEW; break; - } case DbTreeItem::Type::TABLES: case DbTreeItem::Type::INDEXES: case DbTreeItem::Type::TRIGGERS: @@ -946,7 +942,16 @@ void DbTree::deleteItem(DbTreeItem* item) case DbTreeItem::Type::COLUMNS: case DbTreeItem::Type::COLUMN: case DbTreeItem::Type::ITEM_PROTOTYPE: - break; + return; + } + + if (objType != DbObjectDialogs::Type::UNKNOWN) + { + Db* db = item->getDb(); + DbObjectDialogs dialogs(db); + dialogs.setNoConfirmation(true); // confirmation is done in deleteSelected() + dialogs.setNoSchemaRefreshing(true); // we will refresh after all items are deleted + dialogs.dropObject(objType, item->text()); // TODO database name when supported } } @@ -1034,6 +1039,19 @@ void DbTree::refreshSchema(Db* db) treeModel->refreshSchema(db); updateActionsForCurrent(); + + for (MdiChild*& mdi : MAINWINDOW->getMdiArea()->getMdiChilds()) + { + EditorWindow* editor = dynamic_cast(mdi); + if (!editor) + continue; + + Db* editorDb = editor->getCurrentDb(); + if (!editorDb || editorDb != db) + continue; + + editor->refreshValidDbObjects(); + } } void DbTree::copy() @@ -1209,6 +1227,9 @@ void DbTree::disconnectFromDb() if (!db->isOpen()) return; + DbTreeItem* dbItem = ui->treeView->currentDbItem(); + ui->treeView->setCurrentIndex(dbItem->index()); + db->close(); } @@ -1667,6 +1688,17 @@ QList DbTree::getSelectedItems(DbTree::ItemFilterFunc filterFunc) return items; } +void DbTree::changeFontSize(int factor) +{ + auto f = CFG_UI.Fonts.DbTree.get(); + f.setPointSize(f.pointSize() + factor); + CFG_UI.Fonts.DbTree.set(f); + + f = CFG_UI.Fonts.DbTreeLabel.get(); + f.setPointSize(f.pointSize() + factor); + CFG_UI.Fonts.DbTreeLabel.set(f); +} + void DbTree::deleteItems(const QList& itemsToDelete) { QList items = itemsToDelete; @@ -1729,6 +1761,8 @@ void DbTree::deleteItems(const QList& itemsToDelete) refreshSchema(dbToRefresh); } + + emit sessionValueChanged(); } void DbTree::refreshSchemas() @@ -1771,16 +1805,33 @@ void DbTree::showFileExecErrors(const QList >& errors, b dialog.exec(); } +void DbTree::fontSizeChangeRequested(int delta) +{ + changeFontSize(delta >= 0 ? 1 : -1); +} + +void DbTree::incrFontSize() +{ + changeFontSize(1); +} + +void DbTree::decrFontSize() +{ + changeFontSize(-1); +} + void DbTree::dbConnected(Db* db) { updateActionsForCurrent(); updateDbIcon(db); + emit sessionValueChanged(); } void DbTree::dbDisconnected(Db* db) { updateActionsForCurrent(); updateDbIcon(db); + emit sessionValueChanged(); } void DbTree::updateDbIcon(Db* db) @@ -1861,156 +1912,11 @@ void DbTree::execSqlFromFile() if (res != QDialog::Accepted) return; - if (executingQueriesFromFile) + if (fileExecutor->isExecuting()) return; - // Exec file - executingQueriesFromFile = 1; - executingQueriesFromFileDb = db; - fileExecWidgetCover->setProgress(0); fileExecWidgetCover->show(); - if (!db->begin()) - { - notifyError(tr("Could not execute SQL, because application has failed to start transaction: %1").arg(db->getErrorText())); - fileExecWidgetCover->hide(); - return; - } - - QtConcurrent::run(this, &DbTree::execFromFileAsync, dialog.filePath(), db, dialog.ignoreErrors(), dialog.codec()); -} - -void DbTree::execFromFileAsync(const QString& path, Db* db, bool ignoreErrors, const QString& codec) -{ - // Open file - QFile file(path); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) - { - notifyError(tr("Could not open file '%1' for reading: %2").arg(path).arg(file.errorString())); - executingQueriesFromFile = 0; - emit fileExecCoverToBeClosed(); - return; - } - - - QTextStream stream(&file); - stream.setCodec(codec.toLatin1().constData()); - - qint64 fileSize = file.size(); - int attemptedExecutions = 0; - int executed = 0; - bool ok = true; - - QElapsedTimer timer; - timer.start(); - QList> errors = executeFileQueries(db, stream, executed, attemptedExecutions, ok, ignoreErrors, fileSize); - int millis = timer.elapsed(); - if (executingQueriesFromFile.loadAcquire()) - { - handleFileQueryExecution(db, executed, attemptedExecutions, ok, ignoreErrors, millis); - if (!errors.isEmpty()) - emit fileExecErrors(errors, !ok && !ignoreErrors); - } - - file.close(); - emit fileExecCoverToBeClosed(); - executingQueriesFromFile = 0; -} - -QList> DbTree::executeFileQueries(Db* db, QTextStream& stream, int& executed, int& attemptedExecutions, bool& ok, bool ignoreErrors, qint64 fileSize) -{ - QList> errors; - qint64 pos = 0; - QChar c; - QString sql; - sql.reserve(10000); - SqlQueryPtr results; - while (!stream.atEnd() && executingQueriesFromFile.loadAcquire()) - { - while (!db->isComplete(sql) && !stream.atEnd()) - { - stream >> c; - sql.append(c); - while (c != ';' && !stream.atEnd()) - { - stream >> c; - sql.append(c); - } - } - - if (shouldSkipQueryFromFileExecution(sql)) - { - sql.clear();; - continue; - } - - results = db->exec(sql); - attemptedExecutions++; - if (results->isError()) - { - ok = false; - errors << QPair(sql, results->getErrorText()); - - if (!ignoreErrors) - break; - } - else - executed++; - - sql.clear(); - if (attemptedExecutions % 100 == 0) - { - pos = stream.device()->pos(); - emit updateFileExecProgress(static_cast(100 * pos / fileSize)); - } - } - return errors; -} - -bool DbTree::shouldSkipQueryFromFileExecution(const QString& sql) -{ - if (sql.trimmed().isEmpty()) - return true; - - QString upper = sql.toUpper().trimmed().split("\n").last().trimmed(); - return (upper.startsWith("BEGIN") || - upper.startsWith("COMMIT") || - upper.startsWith("ROLLBACK") || - upper.startsWith("END")); -} - -void DbTree::handleFileQueryExecution(Db* db, int executed, int attemptedExecutions, bool ok, bool ignoreErrors, int millis) -{ - bool doCommit = ok ? true : ignoreErrors; - if (doCommit) - { - if (!db->commit()) - { - notifyError(tr("Could not execute SQL, because application has failed to commit the transaction: %1").arg(db->getErrorText())); - db->rollback(); - } - else if (!ok) // committed with errors - { - notifyInfo(tr("Finished executing %1 queries in %2 seconds. %3 were not executed due to errors.") - .arg(executed).arg(millis / 1000.0).arg(attemptedExecutions - executed)); - - emit schemaNeedsRefreshing(db); - } - else - { - notifyInfo(tr("Finished executing %1 queries in %2 seconds.").arg(executed).arg(millis / 1000.0)); - emit schemaNeedsRefreshing(db); - } - } - else - { - db->rollback(); - notifyError(tr("Could not execute SQL due to error.")); - } -} - -bool DbTree::execQueryFromFile(Db* db, const QString& sql) -{ - return !db->exec(sql)->isError(); + fileExecutor->execSqlFromFile(db, dialog.filePath(), dialog.ignoreErrors(), dialog.codec()); } void DbTree::setupDefShortcuts() diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h index 62ef0df..e424a41 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h +++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h @@ -21,25 +21,28 @@ class ViewWindow; class UserInputFilter; class DbTreeView; + +class SqlFileExecutor; namespace Ui { class DbTree; } CFG_KEY_LIST(DbTree, QObject::tr("Database list"), - CFG_KEY_ENTRY(DEL_SELECTED, Qt::Key_Delete, QObject::tr("Delete selected item")) - CFG_KEY_ENTRY(CLEAR_FILTER, Qt::Key_Escape, QObject::tr("Clear filter contents")) - CFG_KEY_ENTRY(REFRESH_SCHEMA, Qt::Key_F5, QObject::tr("Refresh schema")) - CFG_KEY_ENTRY(REFRESH_SCHEMAS, Qt::SHIFT + Qt::Key_F5, QObject::tr("Refresh all schemas")) - CFG_KEY_ENTRY(ADD_DB, Qt::CTRL + Qt::Key_O, QObject::tr("Add database")) - CFG_KEY_ENTRY(SELECT_ALL, Qt::CTRL + Qt::Key_A, QObject::tr("Select all items")) - CFG_KEY_ENTRY(COPY, Qt::CTRL + Qt::Key_C, QObject::tr("Copy selected item(s)")) - CFG_KEY_ENTRY(PASTE, Qt::CTRL + Qt::Key_V, QObject::tr("Paste from clipboard")) + CFG_KEY_ENTRY(DEL_SELECTED, Qt::Key_Delete, QObject::tr("Delete selected item")) + CFG_KEY_ENTRY(CLEAR_FILTER, Qt::Key_Escape, QObject::tr("Clear filter contents")) + CFG_KEY_ENTRY(REFRESH_SCHEMA, Qt::Key_F5, QObject::tr("Refresh schema")) + CFG_KEY_ENTRY(REFRESH_SCHEMAS, Qt::SHIFT + Qt::Key_F5, QObject::tr("Refresh all schemas")) + CFG_KEY_ENTRY(ADD_DB, Qt::CTRL + Qt::Key_O, QObject::tr("Add database")) + CFG_KEY_ENTRY(SELECT_ALL, Qt::CTRL + Qt::Key_A, QObject::tr("Select all items")) + CFG_KEY_ENTRY(COPY, Qt::CTRL + Qt::Key_C, QObject::tr("Copy selected item(s)")) + CFG_KEY_ENTRY(PASTE, Qt::CTRL + Qt::Key_V, QObject::tr("Paste from clipboard")) + CFG_KEY_ENTRY(INCR_FONT_SIZE, Qt::CTRL + Qt::Key_Plus, QObject::tr("Increase font size", "database list")) + CFG_KEY_ENTRY(DECR_FONT_SIZE, Qt::CTRL + Qt::Key_Minus, QObject::tr("Decrease font size", "database list")) ) class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer { - Q_OBJECT - Q_ENUMS(Action) + Q_OBJECT public: friend class DbTreeView; @@ -92,8 +95,11 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer GENERATE_DELETE, OPEN_DB_DIRECTORY, EXEC_SQL_FROM_FILE, + INCR_FONT_SIZE, + DECR_FONT_SIZE, _separator // Never use it directly, it's just for menu setup }; + Q_ENUM(Action) enum ToolBar { @@ -138,7 +144,7 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer ViewWindow* newView(Db* db); void editIndex(DbTreeItem* item); void editTrigger(DbTreeItem* item); - void delSelectedObject(); +// void delSelectedObject(); void filterUndeletableItems(QList& items); void filterItemsWithParentInList(QList& items); void deleteItem(DbTreeItem* item); @@ -151,11 +157,7 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer QString getSelectedViewName() const; QList getSelectedItems(DbTreeItem::Type itemType); QList getSelectedItems(ItemFilterFunc filterFunc = nullptr); - void execFromFileAsync(const QString& path, Db* db, bool ignoreErrors, const QString& codec); - bool execQueryFromFile(Db* db, const QString& sql); - void handleFileQueryExecution(Db* db, int executed, int attemptedExecutions, bool ok, bool ignoreErrors, int millis); - QList> executeFileQueries(Db* db, QTextStream& stream, int& executed, int& attemptedExecutions, bool& ok, bool ignoreErrors, qint64 fileSize); - bool shouldSkipQueryFromFileExecution(const QString& sql); + void changeFontSize(int factor); static bool areDbTreeItemsValidForItem(QList srcItems, const DbTreeItem* dstItem, bool forPasting = false); static bool areUrlsValidForItem(const QList& srcUrls, const DbTreeItem* dstItem); @@ -165,8 +167,7 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer DbTreeModel* treeModel = nullptr; WidgetCover* treeRefreshWidgetCover = nullptr; WidgetCover* fileExecWidgetCover = nullptr; - QAtomicInt executingQueriesFromFile = 0; - Db* executingQueriesFromFileDb = nullptr; + SqlFileExecutor* fileExecutor = nullptr; static QHash> allowedTypesInside; static QSet draggableTypes; @@ -235,12 +236,15 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer void setFileExecProgress(int newValue); void hideFileExecCover(); void showFileExecErrors(const QList>& errors, bool rolledBack); + void fontSizeChangeRequested(int delta); + void incrFontSize(); + void decrFontSize(); signals: void updateFileExecProgress(int value); void fileExecCoverToBeClosed(); void fileExecErrors(const QList>& errors, bool rolledBack); - void schemaNeedsRefreshing(Db* db); + void sessionValueChanged(); }; int qHash(DbTree::Action action); diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp index 436ab50..6620290 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp @@ -420,7 +420,7 @@ QString DbTreeModel::getDbToolTip(DbTreeItem* item) const fileSize = QFile(db->getPath()).size(); rows << toolTipHdrRowTmp.arg(iconPath).arg(tr("Database: %1", "dbtree tooltip").arg(db->getName())); - rows << toolTipRowTmp.arg("URI:").arg(db->getPath()); + rows << toolTipRowTmp.arg(tr("URI:", "dbtree tooltip")).arg(toNativePath(db->getPath())); if (db->isValid()) { @@ -714,6 +714,7 @@ void DbTreeModel::dbConnected(Db* db, bool expandItem) if (CFG_UI.General.ExpandViews.get()) treeView->expand(item->model()->index(1, 0, item->index())); // also expand views } + treeView->setCurrentIndex(item->index()); } void DbTreeModel::dbDisconnected(Db* db) diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeview.cpp b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeview.cpp index 9382d7c..797652a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeview.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeview.cpp @@ -45,6 +45,15 @@ DbTreeItem *DbTreeView::currentItem() return dynamic_cast(model()->itemFromIndex(currentIndex())); } +DbTreeItem* DbTreeView::currentDbItem() +{ + DbTreeItem* item = currentItem(); + if (item->getType() == DbTreeItem::Type::DB) + return item; + + return item->findParentItem(DbTreeItem::Type::DB); +} + DbTreeItem *DbTreeView::itemAt(const QPoint &pos) { return dynamic_cast(model()->itemFromIndex(indexAt(pos))); diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeview.h b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeview.h index 3ec33b4..3fa0255 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeview.h +++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeview.h @@ -22,6 +22,7 @@ class GUI_API_EXPORT DbTreeView : public QTreeView DbTree* getDbTree() const; DbTreeItem *currentItem(); + DbTreeItem *currentDbItem(); DbTreeItem *itemAt(const QPoint& pos); QList selectionItems(); DbTreeModel *model() const; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp index 7e084fb..26a9f52 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp @@ -1,16 +1,18 @@ #include "aboutdialog.h" #include "ui_aboutdialog.h" #include "common/utils.h" -#include "sqlitestudio.h" #include "iconmanager.h" #include "services/extralicensemanager.h" #include "services/pluginmanager.h" +#include "services/sqliteextensionmanager.h" #include "formmanager.h" #include "iconmanager.h" +#include "mainwindow.h" #include #include #include #include +#include AboutDialog::AboutDialog(InitialMode initialMode, QWidget *parent) : QDialog(parent), @@ -38,8 +40,8 @@ void AboutDialog::init(InitialMode initialMode) case DistributionType::PORTABLE: distName = tr("Portable distribution."); break; - case DistributionType::OSX_BOUNDLE: - distName = tr("MacOS X application boundle distribution."); + case DistributionType::OSX_BUNDLE: + distName = tr("MacOS X application bundle distribution."); break; case DistributionType::OS_MANAGED: distName = tr("Operating system managed distribution."); @@ -75,28 +77,21 @@ void AboutDialog::init(InitialMode initialMode) licenseContents.clear(); // Environment - ui->appDirEdit->setText(qApp->applicationDirPath()); - ui->cfgDirEdit->setText(CFG->getConfigDir()); - ui->pluginDirList->addItems(filterResourcePaths(PLUGINS->getPluginDirs())); - ui->iconDirList->addItems(filterResourcePaths(ICONMANAGER->getIconDirs())); - ui->formDirList->addItems(filterResourcePaths(FORMS->getFormDirs())); + ui->appDirEdit->setText(toNativePath(qApp->applicationDirPath())); + ui->cfgDirEdit->setText(toNativePath(CFG->getConfigDir())); + ui->pluginDirList->setPlainText(filterResourcePaths(PLUGINS->getPluginDirs()).join("\n")); + ui->iconDirList->setPlainText(filterResourcePaths(ICONMANAGER->getIconDirs()).join("\n")); + ui->formDirList->setPlainText(filterResourcePaths(FORMS->getFormDirs()).join("\n")); + ui->extensionDirList->setPlainText(filterResourcePaths(SQLITE_EXTENSIONS->getExtensionDirs()).join("\n")); ui->qtVerEdit->setText(QT_VERSION_STR); ui->sqlite3Edit->setText(CFG->getSqlite3Version()); - - QAction* copyAct; - for (QListWidget* w : {ui->pluginDirList, ui->iconDirList, ui->formDirList}) - { - copyAct = new QAction(tr("Copy"), w); - w->addAction(copyAct); - connect(copyAct, SIGNAL(triggered()), this, SLOT(copy())); - } } void AboutDialog::buildIndex() { static const QString entryTpl = QStringLiteral("
  • %1
  • "); QStringList entries; - for (const QString& idx : indexContents) + for (QString& idx : indexContents) entries += entryTpl.arg(idx); licenseContents.prepend(tr("

    Table of contents:

      %2
    ").arg(entries.join(""))); @@ -135,24 +130,8 @@ QStringList AboutDialog::filterResourcePaths(const QStringList& paths) if (path.startsWith(":")) continue; - output << path; + QString newPath = toNativePath(path); + output << newPath; } return output; } - -void AboutDialog::copy() -{ - QListWidget* list = dynamic_cast(sender()->parent()); - if (!list) - return; - - QList items = list->selectedItems(); - if (items.size() == 0) - return; - - QStringList lines; - for (QListWidgetItem* item : items) - lines << item->text(); - - QApplication::clipboard()->setText(lines.join("\n")); -} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h index a43daec..73d891d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h @@ -34,9 +34,6 @@ class GUI_API_EXPORT AboutDialog : public QDialog Ui::AboutDialog *ui = nullptr; QStringList indexContents; QString licenseContents; - - private slots: - void copy(); }; #endif // ABOUTDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui index 16a6e1f..fda2099 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui @@ -7,7 +7,7 @@ 0 0 741 - 447 + 500
    @@ -17,7 +17,7 @@ - 0 + 2 @@ -55,70 +55,51 @@ Environment - - - - Icon directories - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + true - - - - Qt::ActionsContextMenu - - - QAbstractItemView::ExtendedSelection + + + + - - + + - Form directories + Configuration directory Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - Qt::ActionsContextMenu - - - QAbstractItemView::ExtendedSelection - - - - - + + - Plugin directories + Qt version: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - Configuration directory - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + true - - + + - Application directory + Icon directories Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -126,12 +107,16 @@ - - - Qt::ActionsContextMenu + + + true - - QAbstractItemView::ExtendedSelection + + + + + + true @@ -142,31 +127,34 @@ - - + + - Qt version: + + + + + + + + Plugin directories Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + - + Application directory - - - - - - true + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + SQLite 3 version: @@ -176,10 +164,30 @@ - - + + - + Form directories + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + SQLite extension directories + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.cpp index 1d7ba66..90f816f 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.cpp @@ -44,20 +44,21 @@ void BindParamsDialog::init() void BindParamsDialog::initEditors() { QStringList paramNames; - for (BindParam* param : bindParams) + for (BindParam*& param : bindParams) paramNames << param->originalName; MultiEditor* firstEditor = nullptr; MultiEditor* multiEditor = nullptr; QVector> paramHistory = CFG->getBindParamHistory(paramNames); - for (BindParam* param : bindParams) + for (BindParam*& param : bindParams) { multiEditor = initEditor(param, paramHistory.size() > param->position ? paramHistory[param->position].second : QVariant()); if (firstEditor == nullptr) firstEditor = multiEditor; } - firstEditor->focusThisEditor(); + if (firstEditor) + firstEditor->focusThisEditor(); } MultiEditor* BindParamsDialog::initEditor(BindParam* param, const QVariant& cachedValue) diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.h index 666ffc4..99adca6 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/bindparamsdialog.h @@ -27,7 +27,7 @@ class BindParamsDialog : public QDialog static const int margins = 2; static const int spacing = 2; - static const int minimumFieldHeight = 80; + static const int minimumFieldHeight = 120; Ui::BindParamsDialog *ui; QVector bindParams; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp index ebf9253..a217da6 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp @@ -1,5 +1,6 @@ #include "columndialog.h" #include "common/unused.h" +#include "services/notifymanager.h" #include "ui_columndialog.h" #include "columndialogconstraintsmodel.h" #include "iconmanager.h" @@ -36,10 +37,6 @@ void ColumnDialog::init() ui->scale->setStrict(true, true); ui->precision->setStrict(true, true); - ui->typeCombo->addItem(""); - for (DataType::Enum type : DataType::getAllTypes()) - ui->typeCombo->addItem(DataType::toString(type)); - connect(ui->typeCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateDataType())); constraintsModel = new ColumnDialogConstraintsModel(); @@ -54,7 +51,7 @@ void ColumnDialog::init() connect(ui->constraintsView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(editConstraint(QModelIndex))); connect(constraintsModel, SIGNAL(constraintsChanged()), this, SLOT(updateValidations())); connect(constraintsModel, SIGNAL(constraintsChanged()), this, SLOT(updateState())); - connect(ui->typeCombo, SIGNAL(currentTextChanged(const QString&)), this, SLOT(updateValidations())); + connect(ui->typeCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateValidations())); connect(ui->scale, SIGNAL(modified()), this, SLOT(updateValidations())); connect(ui->precision, SIGNAL(modified()), this, SLOT(updateValidations())); @@ -617,7 +614,7 @@ void ColumnDialog::updateValidations() setValidState(tb, true); } - for (SqliteCreateTable::Column::Constraint* constr : column->constraints) + for (SqliteCreateTable::Column::Constraint*& constr : column->constraints) updateConstraint(constr); updateTypeValidations(); @@ -640,11 +637,40 @@ void ColumnDialog::setColumn(SqliteCreateTable::Column* value) constraintsModel->setColumn(column.data()); ui->name->setText(value->name); - if (value->type) + + SqliteCreateTable* createTable = dynamic_cast(value->parentStatement()); + if (createTable->strict) + { + ui->typeCombo->setEditable(false); + for (DataType::Enum& type : DataType::getStrictValues()) + ui->typeCombo->addItem(DataType::toString(type)); + + ui->scale->setVisible(false); + ui->precision->setVisible(false); + ui->sizeLabel->setVisible(false); + ui->sizeCommaLabel->setVisible(false); + + if (value->type) + { + int idx = ui->typeCombo->findText(value->type->name, Qt::MatchFixedString); + if (idx > -1) + ui->typeCombo->setCurrentIndex(idx); + else + notifyError(tr("Could not match valid STRICT table datatype from declared type: %1.").arg(value->type->name)); + } + } + else { - ui->typeCombo->setEditText(value->type->name); - ui->scale->setValue(value->type->scale, false); - ui->precision->setValue(value->type->precision, false); + ui->typeCombo->addItem(""); + for (DataType::Enum& type : DataType::getAllTypesForUiDropdown()) + ui->typeCombo->addItem(DataType::toString(type)); + + if (value->type) + { + ui->typeCombo->setEditText(value->type->name); + ui->scale->setValue(value->type->scale, false); + ui->precision->setValue(value->type->precision, false); + } } updateValidations(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui index 6094ab1..8ff75fd 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui @@ -7,7 +7,7 @@ 0 0 467 - 393 + 431 @@ -37,7 +37,7 @@ - + , diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp index be45873..8c80f6d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp @@ -1,6 +1,5 @@ #include "configdialog.h" #include "ui_configdialog.h" -#include "services/config.h" #include "uiconfig.h" #include "customconfigwidgetplugin.h" #include "services/pluginmanager.h" @@ -17,14 +16,18 @@ #include "plugins/confignotifiableplugin.h" #include "mainwindow.h" #include "common/unused.h" -#include "sqlitestudio.h" #include "configmapper.h" #include "datatype.h" #include "uiutils.h" +#include "common/utils.h" #include "translations.h" #include "plugins/uiconfiguredplugin.h" #include "dbtree/dbtree.h" #include "common/compatibility.h" +#include "windows/editorwindow.h" +#include "syntaxhighlighterplugin.h" +#include "sqleditor.h" +#include "style.h" #include #include #include @@ -39,7 +42,9 @@ #include #include #include +#include #include +#include #define GET_FILTER_STRING(Widget, WidgetType, Method) \ if (qobject_cast(Widget))\ @@ -50,6 +55,18 @@ if (w##WidgetType)\ return getFilterString(w##WidgetType) + " " + Widget->toolTip(); +class ConfigDialogItemDelegate : public QItemDelegate +{ + public: + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const + { + QVariant userData = index.data(Qt::UserRole); + bool isCategory = userData.isValid() && userData.toBool(); + QSize size = QItemDelegate::sizeHint(option, index); + return isCategory ? QSize(size.width(), size.height() * 1.7) : size; + } +}; + ConfigDialog::ConfigDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ConfigDialog) @@ -59,12 +76,14 @@ ConfigDialog::ConfigDialog(QWidget *parent) : ConfigDialog::~ConfigDialog() { + rollbackColorsConfig(); + // Cancel transaction on CfgMain objects from plugins rollbackPluginConfigs(); // Notify plugins about dialog being closed UiConfiguredPlugin* cfgPlugin = nullptr; - for (Plugin* plugin : PLUGINS->getLoadedPlugins()) + for (Plugin*& plugin : PLUGINS->getLoadedPlugins()) { cfgPlugin = dynamic_cast(plugin); if (!cfgPlugin) @@ -74,14 +93,14 @@ ConfigDialog::~ConfigDialog() } // Delete UI and other resources + qDeleteAll(colorPreviewEditors); delete ui; safe_delete(configMapper); - for (ConfigMapper* mapper : pluginConfigMappers.values()) + for (ConfigMapper*& mapper : pluginConfigMappers) delete mapper; pluginConfigMappers.clear(); - } void ConfigDialog::configureDataEditors(const QString& dataTypeString) @@ -161,6 +180,23 @@ QString ConfigDialog::getFilterString(QTableWidget *widget) return strList.join(" "); } +void ConfigDialog::showEvent(QShowEvent* event) +{ + UNUSED(event); + ui->categoriesTree->resizeColumnToContents(0); + int adjustedColumnWidth = ui->categoriesTree->columnWidth(0) + 4; + if (adjustedColumnWidth > ui->categoriesTree->width()) { + if (adjustedColumnWidth > (width() / 2)) + adjustedColumnWidth = width() / 2; + + QList sizes = ui->splitter->sizes(); + int rightPartSize = sizes[0] + sizes[1] - adjustedColumnWidth; + sizes[0] = adjustedColumnWidth; + sizes[1] = rightPartSize; + ui->splitter->setSizes(sizes); + } +} + void ConfigDialog::init() { ui->setupUi(this); @@ -184,12 +220,15 @@ void ConfigDialog::init() initDataEditors(); initShortcuts(); initLangs(); + initTooltips(); + initColors(); connect(ui->categoriesTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(switchPage(QTreeWidgetItem*))); connect(ui->previewTabs, SIGNAL(currentChanged(int)), this, SLOT(updateStylePreview())); connect(ui->activeStyleCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateStylePreview())); connect(ui->buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(apply())); connect(ui->hideBuiltInPluginsCheck, SIGNAL(toggled(bool)), this, SLOT(updateBuiltInPluginsVisibility())); + connect(ui->codeColorsResetBtn, SIGNAL(pressed()), this, SLOT(resetCodeSyntaxColors())); QList entries; entries << CFG_UI.General.SortObjects @@ -199,12 +238,13 @@ void ConfigDialog::init() << CFG_UI.General.ShowSystemObjects << CFG_UI.General.ShowVirtualTableLabels; - for (CfgEntry* cfg : entries) + for (CfgEntry*& cfg : entries) connect(cfg, SIGNAL(changed(QVariant)), this, SLOT(markRequiresSchemasRefresh())); QStringList styles = QStyleFactory::keys(); styles.sort(Qt::CaseInsensitive); ui->activeStyleCombo->addItems(styles); + ui->activeStyleCombo->setCurrentText(STYLE->name()); connect(ui->stackedWidget, SIGNAL(currentChanged(int)), this, SLOT(pageSwitched())); @@ -214,8 +254,14 @@ void ConfigDialog::init() ui->updatesGroup->setVisible(false); #endif + resettingColors = true; load(); + resettingColors = false; + colorChanged(); updateStylePreview(); + updateColorsAfterLoad(); + + ui->categoriesTree->expandAll(); } void ConfigDialog::load() @@ -229,14 +275,28 @@ void ConfigDialog::load() void ConfigDialog::save() { if (MainWindow::getInstance()->currentStyle().compare(ui->activeStyleCombo->currentText(), Qt::CaseInsensitive) != 0) + { + QList unmodifiedColors = prepareCodeSyntaxColorsForStyle(); + bool wasDark = STYLE->isDark(); + MainWindow::getInstance()->setStyle(ui->activeStyleCombo->currentText()); + if (STYLE->isDark() != wasDark) + { + resettingColors = true; // to avoid mass events of color change on syntax page + adjustSyntaxColorsForStyle(unmodifiedColors); + resettingColors = false; + colorChanged(); + } + } + QString loadedPlugins = collectLoadedPlugins(); storeSelectedFormatters(); CFG->beginMassSave(); CFG_CORE.General.LoadedPlugins.set(loadedPlugins); configMapper->saveFromWidget(ui->stackedWidget, true); commitPluginConfigs(); + commitColorsConfig(); CFG->commitMassSave(); if (requiresSchemasRefresh) @@ -302,7 +362,7 @@ void ConfigDialog::applyFilter(const QString &filter) QColor disabledColor = ui->categoriesTree->palette().color(QPalette::Disabled, QPalette::WindowText); if (filter.isEmpty()) { - for (QTreeWidgetItem* item : getAllCategoryItems()) + for (QTreeWidgetItem*& item : getAllCategoryItems()) item->setForeground(0, normalColor); return; @@ -318,10 +378,11 @@ void ConfigDialog::applyFilter(const QString &filter) QHash pageToCategoryItem = buildPageToCategoryItemMap(); QSet matchedCategories; - for (QWidget* page : pageToCategoryItem.keys()) + for (auto it = pageToCategoryItem.keyBegin(), end = pageToCategoryItem.keyEnd(); it != end; ++it) { - for (QWidget* matched : matchedWidgets) + for (QWidget* matched : qAsConst(matchedWidgets)) { + QWidget* page = *it; if (page->isAncestorOf(matched)) { if (!pageToCategoryItem.contains(page)) @@ -336,10 +397,10 @@ void ConfigDialog::applyFilter(const QString &filter) } } - for (QTreeWidgetItem* item : getAllCategoryItems()) + for (QTreeWidgetItem*& item : getAllCategoryItems()) item->setForeground(0, disabledColor); - for (QTreeWidgetItem* item : matchedCategories) + for (QTreeWidgetItem* item : qAsConst(matchedCategories)) { item->setForeground(0, normalColor); while ((item = item->parent()) != nullptr) @@ -350,7 +411,7 @@ void ConfigDialog::applyFilter(const QString &filter) QHash ConfigDialog::buildPageToCategoryItemMap() const { QHash pageNameToCategoryItem; - for (QTreeWidgetItem* item : getAllCategoryItems()) + for (QTreeWidgetItem*& item : getAllCategoryItems()) pageNameToCategoryItem[item->statusTip(0)] = item; QWidget* page = nullptr; @@ -393,7 +454,7 @@ QList ConfigDialog::getDefaultEditorsForType(DataType: }); QList results; - for (const PluginWithPriority& p: sortedPlugins) + for (PluginWithPriority& p: sortedPlugins) results << p.second; return results; @@ -427,7 +488,7 @@ void ConfigDialog::updateDataTypeEditors() ui->dataEditorsAvailableList->sortItems(); - for (MultiEditorWidgetPlugin* plugin : sortedPlugins) + for (MultiEditorWidgetPlugin*& plugin : sortedPlugins) addDataTypeEditor(plugin); } @@ -557,7 +618,7 @@ void ConfigDialog::addDataType(const QString& typeStr) void ConfigDialog::rollbackPluginConfigs() { CfgMain* mainCfg = nullptr; - for (UiConfiguredPlugin* plugin : pluginConfigMappers.keys()) + for (UiConfiguredPlugin*& plugin : pluginConfigMappers.keys()) { mainCfg = plugin->getMainUiConfig(); if (mainCfg) @@ -565,24 +626,68 @@ void ConfigDialog::rollbackPluginConfigs() } } +void ConfigDialog::rollbackColorsConfig() +{ + CFG_UI.Colors.rollback(); +} + void ConfigDialog::commitPluginConfigs() { CfgMain* mainCfg = nullptr; - for (UiConfiguredPlugin* plugin : pluginConfigMappers.keys()) + for (auto it = pluginConfigMappers.keyBegin(), end = pluginConfigMappers.keyEnd(); it != end; ++it) { - mainCfg = plugin->getMainUiConfig(); + mainCfg = (*it)->getMainUiConfig(); if (mainCfg) { mainCfg->commit(); mainCfg->begin(); // be prepared for further changes after "Apply" } } + + auto it = highlightingPluginForPreviewEditor.iterator(); + while (it.hasNext()) + { + auto item = it.next(); + if (item.value()->getLanguageName() == "SQL") + continue; + + item.value()->refreshFormats(); + } +} + +void ConfigDialog::commitColorsConfig() +{ + CFG_UI.Colors.commit(); + CFG_UI.Colors.begin(); } void ConfigDialog::connectMapperSignals(ConfigMapper* mapper) { - connect(mapper, SIGNAL(modified()), this, SLOT(markModified())); - connect(mapper, SIGNAL(notifyEnabledWidgetModified(QWidget*, CfgEntry*, const QVariant&)), this, SLOT(notifyPluginsAboutModification(QWidget*, CfgEntry*, const QVariant&))); + connect(mapper, SIGNAL(modified(QWidget*)), this, SLOT(markModified())); + connect(mapper, SIGNAL(notifyEnabledWidgetModified(QWidget*,CfgEntry*,QVariant)), this, SLOT(notifyPluginsAboutModification(QWidget*,CfgEntry*,QVariant))); +} + +QList ConfigDialog::getShortcutsCfgMains() const +{ + static const QString metaName = CFG_SHORTCUTS_METANAME; + QList mains; + for (CfgMain*& cfgMain : CfgMain::getInstances()) + { + if (cfgMain->getMetaName() != metaName) + continue; + + mains << cfgMain; + } + return mains; +} + +QList ConfigDialog::getShortcutsCfgCategories() const +{ + QList categories; + for (CfgMain*& cfgMain : getShortcutsCfgMains()) + categories.append(cfgMain->getCategories().values()); + + return categories; } void ConfigDialog::updateDataTypeListState() @@ -792,23 +897,23 @@ void ConfigDialog::detailsClicked(const QString& pluginName) // Rows QStringList rows; - rows << row.arg(tr("Description:", "plugin details")).arg(PLUGINS->getDescription(pluginName)); - rows << row.arg(tr("Category:", "plugin details")).arg(type->getTitle()); - rows << row.arg(tr("Version:", "plugin details")).arg(PLUGINS->getPrintableVersion(pluginName)); - rows << row.arg(tr("Author:", "plugin details")).arg(PLUGINS->getAuthor(pluginName)); + rows << row.arg(tr("Description:", "plugin details"), PLUGINS->getDescription(pluginName)); + rows << row.arg(tr("Category:", "plugin details"), type->getTitle()); + rows << row.arg(tr("Version:", "plugin details"), PLUGINS->getPrintableVersion(pluginName)); + rows << row.arg(tr("Author:", "plugin details"), PLUGINS->getAuthor(pluginName)); rows << hline; - rows << row.arg(tr("Internal name:", "plugin details")).arg(pluginName); - rows << row.arg(tr("Dependencies:", "plugin details")).arg(PLUGINS->getDependencies(pluginName).join(", ")); - rows << row.arg(tr("Conflicts:", "plugin details")).arg(PLUGINS->getConflicts(pluginName).join(", ")); + rows << row.arg(tr("Internal name:", "plugin details"), pluginName); + rows << row.arg(tr("Dependencies:", "plugin details"), PLUGINS->getDependencies(pluginName).join(", ")); + rows << row.arg(tr("Conflicts:", "plugin details"), PLUGINS->getConflicts(pluginName).join(", ")); // Message - QString pluginDetails = details.arg(PLUGINS->getTitle(pluginName)).arg(rows.join("")); + QString pluginDetails = details.arg(PLUGINS->getTitle(pluginName), rows.join("")); QMessageBox::information(this, tr("Plugin details"), pluginDetails); } void ConfigDialog::failedToLoadPlugin(const QString& pluginName) { - QTreeWidgetItem* theItem = itemToPluginNameMap.valueByRight(pluginName); + QTreeWidgetItem* theItem = pluginListItemToPluginNameMap.valueByRight(pluginName); if (!theItem) { qWarning() << "Plugin" << pluginName << "failed to load, but it could not be found on the plugins list in ConfigDialog."; @@ -833,7 +938,7 @@ void ConfigDialog::loadUnloadPlugin(QTreeWidgetItem* item, int column) if (column != 0) return; - QString pluginName = itemToPluginNameMap.valueByLeft(item); + QString pluginName = pluginListItemToPluginNameMap.valueByLeft(item); if (PLUGINS->isBuiltIn(pluginName)) return; @@ -866,6 +971,10 @@ void ConfigDialog::pluginAboutToUnload(Plugin* plugin, PluginType* type) if (notifiablePlugin && notifiablePlugins.contains(notifiablePlugin)) notifiablePlugins.removeOne(notifiablePlugin); + // Update code colors page + if (type->isForPluginType()) + highlighterPluginUnloaded(dynamic_cast(plugin)); + // Deinit page deinitPluginPage(plugin); @@ -879,6 +988,14 @@ void ConfigDialog::pluginLoaded(Plugin* plugin, PluginType* type, bool skipConfi if (type->isForPluginType()) codeFormatterLoaded(); + // Update code colors page + if (type->isForPluginType()) + highlighterPluginLoaded(dynamic_cast(plugin)); + + QTreeWidgetItem* listItem = pluginListItemToPluginNameMap.valueByRight(plugin->getName()); + if (listItem && listItem->checkState(0) == Qt::Unchecked) + listItem->setCheckState(0, Qt::Checked); + // Init page if (!initPluginPage(plugin, skipConfigLoading)) return; @@ -901,7 +1018,9 @@ void ConfigDialog::pluginLoaded(Plugin* plugin, PluginType* type, bool skipConfi void ConfigDialog::pluginUnloaded(const QString& pluginName, PluginType* type) { - UNUSED(pluginName); + QTreeWidgetItem* item = pluginListItemToPluginNameMap.valueByRight(pluginName); + if (item && item->checkState(0) == Qt::Checked) + item->setCheckState(0, Qt::Unchecked); // Update formatters page if (type->isForPluginType()) @@ -918,7 +1037,7 @@ void ConfigDialog::updatePluginCategoriesVisibility() void ConfigDialog::updateBuiltInPluginsVisibility() { bool hideBuiltIn = ui->hideBuiltInPluginsCheck->isChecked(); - QHashIterator it = itemToPluginNameMap.iterator(); + QHashIterator it = pluginListItemToPluginNameMap.iterator(); while (it.hasNext()) { it.next(); @@ -929,6 +1048,13 @@ void ConfigDialog::updateBuiltInPluginsVisibility() } } +void ConfigDialog::refreshColorsInSyntaxHighlighters() +{ + auto it = highlightingPluginForPreviewEditor.iterator(); + while (it.hasNext()) + it.next().value()->refreshFormats(); +} + void ConfigDialog::applyShortcutsFilter(const QString &filter) { QTreeWidgetItem* categoryItem = nullptr; @@ -964,10 +1090,144 @@ void ConfigDialog::markRequiresSchemasRefresh() void ConfigDialog::notifyPluginsAboutModification(QWidget*, CfgEntry* key, const QVariant& value) { - for (ConfigNotifiablePlugin* plugin : notifiablePlugins) + for (ConfigNotifiablePlugin*& plugin : notifiablePlugins) plugin->configModified(key, value); } +void ConfigDialog::resetCodeSyntaxColors() +{ + resettingColors = true; + for (QWidget*& widget : configMapper->getAllConfigWidgets(ui->commonCodeColorsGroup)) + configMapper->applyConfigDefaultValueToWidget(widget); + + resettingColors = false; + colorChanged(); +} + +void ConfigDialog::colorChanged() +{ + refreshColorsInSyntaxHighlighters(); + for (QSyntaxHighlighter*& highligter : colorPreviewHighlighters) + highligter->rehighlight(); + + if (codePreviewSqlEditor) + codePreviewSqlEditor->colorsConfigChanged(); +} + +void ConfigDialog::adjustSyntaxColorsForStyle(QList& unmodifiedColors) +{ + for (QWidget*& w : unmodifiedColors) + configMapper->applyConfigDefaultValueToWidget(w); +} + +void ConfigDialog::highlighterPluginLoaded(SyntaxHighlighterPlugin* plugin) +{ + QPlainTextEdit* editor = nullptr; + if (plugin->getLanguageName() == "SQL") + { + // For SQL there is assumed just one, built-in plugin + codePreviewSqlEditor = new SqlEditor(ui->codeColorsPreviewTabWidget); + codePreviewSqlEditor->setShowLineNumbers(false); + codePreviewSqlEditor->setCurrentQueryHighlighting(true); + editor = codePreviewSqlEditor; + } + else + { + editor = new QPlainTextEdit(ui->codeColorsPreviewTabWidget); + editor->setFont(CFG_UI.Fonts.SqlEditor.get()); + colorPreviewHighlighters << plugin->createSyntaxHighlighter(editor); + } + + editor->setPlainText(plugin->previewSampleCode()); + editor->setReadOnly(true); + colorPreviewEditors << editor; + highlightingPluginForPreviewEditor.insert(editor, plugin); + ui->codeColorsPreviewTabWidget->addTab(editor, plugin->getLanguageName()); +} + +void ConfigDialog::highlighterPluginUnloaded(SyntaxHighlighterPlugin* plugin) +{ + QPlainTextEdit* editor = highlightingPluginForPreviewEditor.valueByRight(plugin); + if (!editor) + { + qCritical() << "Highlighter plugin unloaded for which there is no preview editor! Application crash is possible. Plugin:" << plugin->getName(); + return; + } + + QTextDocument* document = editor->document(); + QSyntaxHighlighter* highlighter = findFirst(colorPreviewHighlighters, [document](auto highlighter) {return highlighter->document() == document;}); + + ui->codeColorsPreviewTabWidget->removeTab(ui->codeColorsPreviewTabWidget->indexOf(editor)); + colorPreviewHighlighters.removeOne(highlighter); + colorPreviewEditors.removeOne(editor); + delete editor; + + highlightingPluginForPreviewEditor.removeRight(plugin); +} + +QList ConfigDialog::prepareCodeSyntaxColorsForStyle() +{ + QList unmodified; + for (QWidget*& w : configMapper->getAllConfigWidgets(ui->commonCodeColorsGroup)) + { + CfgEntry* entry = configMapper->getConfigForWidget(w); + if (entry->getDefaultValue() == entry->get()) + unmodified << w; + } + return unmodified; +} + +void ConfigDialog::initColors() +{ + CFG_UI.Colors.begin(); + connect(configMapper, &ConfigMapper::modified, this, + [this](QWidget* widget) + { + CfgEntry* key = configMapper->getBindConfigForWidget(widget); + if (!key) + { + qCritical() << "Missing CfgEntry in Colors configuration for widget" << widget->objectName(); + return; + } + + if (key->getCategory() != CFG_UI.Colors) + return; + + configMapper->saveFromWidget(widget, key); + if (key->getName().endsWith("Custom")) + toggleColorButtonState(key); + + if (resettingColors) + return; + + colorChanged(); + }); +} + +void ConfigDialog::updateColorsAfterLoad() +{ + QHash entries = CFG_UI.Colors.getEntries(); + auto it = entries.begin(); + while (it != entries.end()) + { + if (it.key().endsWith("Custom")) + toggleColorButtonState(it.value()); + + it++; + } +} + +void ConfigDialog::toggleColorButtonState(CfgEntry* colorCheckEntry) +{ + CfgEntry* colorKey = colorCheckEntry->getCategory()->getEntryByName(colorCheckEntry->getName().chopped(6)); + if (colorKey) + { + QWidget* button = configMapper->getBindWidgetForConfig(colorKey); + if (button) + button->setEnabled(colorCheckEntry->get().toBool()); + } +} + void ConfigDialog::updatePluginCategoriesVisibility(QTreeWidgetItem* categoryItem) { categoryItem->setHidden(categoryItem->childCount() == 0); @@ -976,7 +1236,7 @@ void ConfigDialog::updatePluginCategoriesVisibility(QTreeWidgetItem* categoryIte QString ConfigDialog::collectLoadedPlugins() const { QStringList loaded; - QHashIterator it = itemToPluginNameMap.iterator(); + QHashIterator it = pluginListItemToPluginNameMap.iterator(); while (it.hasNext()) { it.next(); @@ -1147,8 +1407,7 @@ QTreeWidgetItem* ConfigDialog::createPluginsTypeItem(const QString& widgetName, if (item->statusTip(0) == widgetName) return item; } - return nullptr; - + return new QTreeWidgetItem({title}); } QTreeWidgetItem* ConfigDialog::getItemByTitle(const QString& title) const @@ -1195,23 +1454,24 @@ void ConfigDialog::initPlugins() // Recreate QTreeWidgetItem *typeItem = nullptr; - for (PluginType* pluginType : PLUGINS->getPluginTypes()) + for (PluginType*& pluginType : PLUGINS->getPluginTypes()) { typeItem = createPluginsTypeItem(pluginType->getConfigUiForm(), pluginType->getTitle()); - if (!typeItem) - continue; item->addChild(typeItem); pluginTypeToItemMap[pluginType] = typeItem; - for (Plugin* plugin : pluginType->getLoadedPlugins()) + for (Plugin*& plugin : pluginType->getLoadedPlugins()) pluginLoaded(plugin, pluginType, true); } updatePluginCategoriesVisibility(); + // Using old connect() syntax, becase the pointer syntax does not work correctly with signals declared in an interface class. + // At least that's the case with Qt 5.15.2. connect(PLUGINS, SIGNAL(loaded(Plugin*,PluginType*)), this, SLOT(pluginLoaded(Plugin*,PluginType*))); connect(PLUGINS, SIGNAL(aboutToUnload(Plugin*,PluginType*)), this, SLOT(pluginAboutToUnload(Plugin*,PluginType*))); + connect(PLUGINS, SIGNAL(unloaded(QString,PluginType*)), this, SLOT(pluginUnloaded(QString,PluginType*))); } void ConfigDialog::initPluginsPage() @@ -1255,7 +1515,7 @@ void ConfigDialog::initPluginsPage() categoryRow = 0; QList pluginTypes = PLUGINS->getPluginTypes(); sSort(pluginTypes, PluginType::nameLessThan); - for (PluginType* pluginType : pluginTypes) + for (PluginType*& pluginType : pluginTypes) { category = new QTreeWidgetItem({pluginType->getTitle()}); font.setItalic(false); @@ -1275,7 +1535,7 @@ void ConfigDialog::initPluginsPage() itemRow = 0; pluginNames = pluginType->getAllPluginNames(); sSort(pluginNames); - for (const QString& pluginName : pluginNames) + for (QString& pluginName : pluginNames) { builtIn = PLUGINS->isBuiltIn(pluginName); title = PLUGINS->getTitle(pluginName); @@ -1290,10 +1550,10 @@ void ConfigDialog::initPluginsPage() category->addChild(item); - itemToPluginNameMap.insert(item, pluginName); + pluginListItemToPluginNameMap.insert(item, pluginName); // Details button - detailsLabel = new QLabel(QString("%2 ").arg(pluginName).arg(tr("Details")), ui->pluginsList); + detailsLabel = new QLabel(QString("%2 ").arg(pluginName, tr("Details")), ui->pluginsList); detailsLabel->setAlignment(Qt::AlignRight); itemIndex = ui->pluginsList->model()->index(itemRow, 1, categoryIndex); ui->pluginsList->setIndexWidget(itemIndex, detailsLabel); @@ -1322,10 +1582,10 @@ void ConfigDialog::initPluginsPage() bool ConfigDialog::initPluginPage(Plugin* plugin, bool skipConfigLoading) { - if (!dynamic_cast(plugin)) + UiConfiguredPlugin* cfgPlugin = dynamic_cast(plugin); + if (!cfgPlugin) return false; - UiConfiguredPlugin* cfgPlugin = dynamic_cast(plugin); QString pluginName = plugin->getName(); QString formName = cfgPlugin->getConfigUiForm(); QWidget* widget = FORMS->createWidget(formName); @@ -1398,7 +1658,7 @@ void ConfigDialog::initDataEditors() sSort(dataTypeList); QListWidgetItem* item = nullptr; - for (const QString& type : dataTypeList) + for (QString& type : dataTypeList) { item = new QListWidgetItem(type); if (!DataType::getAllNames().contains(type)) @@ -1441,49 +1701,30 @@ void ConfigDialog::initShortcuts() ui->shortcutsTable->header()->setSectionResizeMode(2, QHeaderView::Fixed); ui->shortcutsTable->header()->resizeSection(1, 150); ui->shortcutsTable->header()->resizeSection(2, 26); + ui->shortcutsTable->header()->resizeSection(3, 26); ui->shortcutsFilterEdit->setClearButtonEnabled(true); new UserInputFilter(ui->shortcutsFilterEdit, this, SLOT(applyShortcutsFilter(QString))); - static const QString metaName = CFG_SHORTCUTS_METANAME; - QList categories; - for (CfgMain* cfgMain : CfgMain::getInstances()) - { - if (cfgMain->getMetaName() != metaName) - continue; - - for (CfgCategory* cat : cfgMain->getCategories().values()) - categories << cat; - } + QList categories = getShortcutsCfgCategories(); + ui->shortcutsTable->setItemDelegate(new ConfigDialogItemDelegate()); sSort(categories, [](CfgCategory* cat1, CfgCategory* cat2) -> bool { return cat1->getTitle().compare(cat2->getTitle()) < 0; }); - for (CfgCategory* cat : categories) + for (CfgCategory*& cat : categories) initShortcuts(cat); } void ConfigDialog::initShortcuts(CfgCategory *cfgCategory) { - QTreeWidgetItem* item = nullptr; - QFont font; - QModelIndex categoryIndex; - QModelIndex itemIndex; - QKeySequenceEdit *sequenceEdit = nullptr; - QToolButton* clearButton = nullptr; - QString title; - QSize itemSize; - // Font and metrics - item = new QTreeWidgetItem({""}); - font = item->font(0); - - QFontMetrics fm(font); - itemSize = QSize(-1, (fm.ascent() + fm.descent() + 4)); - - delete item; + QTreeWidgetItem item({""}); + QFont font = item.font(0); +// QFontMetrics fm(font); +// QSize itemSize = QSize(-1, -1); // Creating... QBrush categoryBg = ui->shortcutsTable->palette().button(); @@ -1493,47 +1734,63 @@ void ConfigDialog::initShortcuts(CfgCategory *cfgCategory) font.setItalic(false); font.setBold(true); category->setFont(0, font); - for (int i = 0; i < 3; i++) + for (int i = 0; i < 4; i++) { + category->setData(i, Qt::UserRole, true); category->setBackground(i, categoryBg); category->setForeground(i, categoryFg); } - category->setSizeHint(0, itemSize); +// category->setSizeHint(0, itemSize); category->setFlags(category->flags() ^ Qt::ItemIsSelectable); ui->shortcutsTable->addTopLevelItem(category); int categoryRow = ui->shortcutsTable->topLevelItemCount() - 1; - categoryIndex = ui->shortcutsTable->model()->index(categoryRow, 0); + QModelIndex categoryIndex = ui->shortcutsTable->model()->index(categoryRow, 0); int itemRow = 0; QStringList entryNames = cfgCategory->getEntries().keys(); sSort(entryNames); - for (const QString& entryName : entryNames) + for (QString& entryName : entryNames) { + CfgEntry* cfgEntry = cfgCategory->getEntries()[entryName]; + // Title - title = cfgCategory->getEntries()[entryName]->getTitle(); - item = new QTreeWidgetItem(category, {title}); + QString title = cfgEntry->getTitle(); + new QTreeWidgetItem(category, {title}); // Key edit - sequenceEdit = new QKeySequenceEdit(ui->shortcutsTable); + QKeySequenceEdit* sequenceEdit = new QKeySequenceEdit(ui->shortcutsTable); sequenceEdit->setFixedWidth(150); - sequenceEdit->setProperty("cfg", cfgCategory->getEntries()[entryName]->getFullKey()); - itemIndex = ui->shortcutsTable->model()->index(itemRow, 1, categoryIndex); + sequenceEdit->setProperty("cfg", cfgEntry->getFullKey()); + QModelIndex itemIndex = ui->shortcutsTable->model()->index(itemRow, 1, categoryIndex); ui->shortcutsTable->setIndexWidget(itemIndex, sequenceEdit); configMapper->addExtraWidget(sequenceEdit); // Clear button - clearButton = new QToolButton(ui->shortcutsTable); + QToolButton* clearButton = new QToolButton(ui->shortcutsTable); clearButton->setIcon(ICONS.CLEAR_LINEEDIT); - connect(clearButton, &QToolButton::clicked, [this, sequenceEdit]() + clearButton->setToolTip(tr("Clear hotkey for this action")); + connect(clearButton, &QToolButton::clicked, this, [this, sequenceEdit]() { sequenceEdit->clear(); this->markModified(); - }); itemIndex = ui->shortcutsTable->model()->index(itemRow, 2, categoryIndex); ui->shortcutsTable->setIndexWidget(itemIndex, clearButton); + // Restore default value button + QToolButton* defaultButton = new QToolButton(ui->shortcutsTable); + defaultButton->setIcon(ICONS.RESTORE_DEFAULT); + defaultButton->setToolTip(tr("Restore original hotkey for this action")); + connect(defaultButton, &QToolButton::clicked, this, [this, sequenceEdit, cfgEntry]() + { + cfgEntry->reset(); + sequenceEdit->setKeySequence(QKeySequence::fromString(cfgEntry->get().toString())); + this->markModified(); + }); + itemIndex = ui->shortcutsTable->model()->index(itemRow, 3, categoryIndex); + ui->shortcutsTable->setIndexWidget(itemIndex, defaultButton); + itemRow++; } @@ -1545,7 +1802,9 @@ void ConfigDialog::initLangs() QMap langs = getAvailableLanguages(); int idx = 0; int selected = -1; - for (const QString& lang : langs.keys()) + QStringList langList = langs.keys(); + strSort(langList, Qt::CaseInsensitive); + for (QString& lang : langList) { ui->langCombo->addItem(lang, langs[lang]); if (langs[lang] == SQLITESTUDIO->getCurrentLang()) @@ -1557,6 +1816,21 @@ void ConfigDialog::initLangs() ui->langCombo->setCurrentIndex(selected); } +void ConfigDialog::initTooltips() +{ + ui->execQueryUnderCursorCheck->setToolTip(ui->execQueryUnderCursorCheck->toolTip().arg( + GET_SHORTCUT_ENTRY(EditorWindow, EXEC_ONE_QUERY)->get().toString(), + GET_SHORTCUT_ENTRY(EditorWindow, EXEC_ALL_QUERIES)->get().toString() + )); + + setValidStateTooltip(ui->commonCodeColorsGroup, + tr("Here you can configure colors for code syntax highlighting. " + "They are shared across different languages - not only for SQL, but also JavaScript and others. " + "By default a theme-based color is used. To define your own color, enable a custom color " + "by selecting a checkbox next to a particular color.")); + +} + bool ConfigDialog::isPluginCategoryItem(QTreeWidgetItem *item) const { return item->parent() && item->parent()->parent() && item->parent()->parent() == getPluginsCategoryItem(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h index f0ab1de..7f5abb3 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h @@ -26,6 +26,13 @@ class ConfigMapper; class MultiEditorWidgetPlugin; class ConfigNotifiablePlugin; class UiConfiguredPlugin; +class SyntaxHighlighterPlugin; +class QPlainTextEdit; +class QSyntaxHighlighter; +class SqlEditor; + +#define CFG_UI_NAME "Ui" +#define CFG_COLORS_NAME "Colors" class GUI_API_EXPORT ConfigDialog : public QDialog { @@ -43,6 +50,9 @@ class GUI_API_EXPORT ConfigDialog : public QDialog static QString getFilterString(QListWidget* widget); static QString getFilterString(QTableWidget* widget); + protected: + void showEvent(QShowEvent* event); + private: void init(); void load(); @@ -57,6 +67,10 @@ class GUI_API_EXPORT ConfigDialog : public QDialog void initShortcuts(); void initShortcuts(CfgCategory* cfgCategory); void initLangs(); + void initTooltips(); + void initColors(); + void updateColorsAfterLoad(); + void toggleColorButtonState(CfgEntry* colorCheckEntry); void applyStyle(QWidget* widget, QStyle* style); QTreeWidgetItem* getPluginsCategoryItem() const; QTreeWidgetItem* getPluginsCategoryItem(PluginType* type) const; @@ -83,13 +97,23 @@ class GUI_API_EXPORT ConfigDialog : public QDialog void setPluginNamesForDataTypeItem(QListWidgetItem* typeItem, const QStringList& pluginNames); void addDataType(const QString& typeStr); void rollbackPluginConfigs(); + void rollbackColorsConfig(); void commitPluginConfigs(); + void commitColorsConfig(); void connectMapperSignals(ConfigMapper* mapper); + QList getShortcutsCfgMains() const; + QList getShortcutsCfgCategories() const; + void refreshColorsInSyntaxHighlighters(); + void colorChanged(); + QList prepareCodeSyntaxColorsForStyle(); + void adjustSyntaxColorsForStyle(QList& unmodifiedColors); + void highlighterPluginLoaded(SyntaxHighlighterPlugin* plugin); + void highlighterPluginUnloaded(SyntaxHighlighterPlugin* plugin); Ui::ConfigDialog *ui = nullptr; QStyle* previewStyle = nullptr; QHash nameToPage; - BiHash itemToPluginNameMap; + BiHash pluginListItemToPluginNameMap; QHash pluginTypeToItemMap; QHash pluginToItemMap; QHash formatterLangToPluginComboMap; @@ -102,6 +126,11 @@ class GUI_API_EXPORT ConfigDialog : public QDialog bool modifiedFlag = false; QList notifiablePlugins; bool requiresSchemasRefresh = false; + QList colorPreviewEditors; + SqlEditor* codePreviewSqlEditor = nullptr; + QList colorPreviewHighlighters; + BiHash highlightingPluginForPreviewEditor; + bool resettingColors = false; private slots: void refreshFormattersPage(); @@ -138,6 +167,7 @@ class GUI_API_EXPORT ConfigDialog : public QDialog void applyShortcutsFilter(const QString& filter); void markRequiresSchemasRefresh(); void notifyPluginsAboutModification(QWidget*, CfgEntry* key, const QVariant& value); + void resetCodeSyntaxColors(); public slots: void accept(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui index 4452f63..475d7b0 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui @@ -6,8 +6,8 @@ 0 0 - 770 - 539 + 866 + 580 @@ -148,6 +148,18 @@ :/icons/img/config_font.png:/icons/img/config_font.png + + + Code colors + + + codeColorsPage + + + + :/icons/img/config_colors.png:/icons/img/config_colors.png + + @@ -161,6 +173,18 @@ :/icons/img/database_online.png:/icons/img/database_online.png + + + Code assistant + + + codeAssistantPage + + + + :/icons/img/code_assistant.png:/icons/img/code_assistant.png + + Data browsing @@ -217,7 +241,7 @@ - 1 + 4 @@ -395,8 +419,8 @@ 0 0 - 564 - 540 + 433 + 718 @@ -406,41 +430,16 @@ Data browsing and editing - - - - - 150 - 16777215 - - - - 1 - - - 99999 - - - General.NumberOfRowsPerPage - - - - + - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - - - 10 + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - 99999 - - - 600 + 999999 - General.MaxInitialColumnWith + General.PopulateHistorySize @@ -452,16 +451,42 @@ - + - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + Number of memorized table populating configurations + + + + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> - Limit initial data column width to (in pixels): + Limit number of rows for in case of dozens of columns + + + General.LimitRowsForManyColumns + + + + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + Show column and row details tooltip in data view + + + General.ShowDataViewTooltips - + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> @@ -474,65 +499,118 @@ - - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + + 150 + 16777215 + + + + 1 - 999999 + 99999 - General.PopulateHistorySize + General.NumberOfRowsPerPage - - + + - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - Number of memorized table populating configurations + Keep NULL value when entering empty value + + + General.KeepNullWhenEmptyValue - - + + - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> - Show column and row details tooltip in data view + Use scientific notation for real numbers in the grid view - General.ShowDataViewTooltips + General.UseSciFormatForDoubles - - + + + + + + + Data column width + + + + - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> - Keep NULL value when entering empty value + Enlarge column when entering value longer than current width - General.KeepNullWhenEmptyValue + General.EnlargeColumnForValue - - + + + + + 150 + 16777215 + + - <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + 10 + + + 99999 + + + 600 + + + General.MaxInitialColumnWith + + + + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> - Limit number of rows for in case of dozens of columns + Limit automatic data column width to (in pixels): + + + + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + Keep at least the width to show complete column name - General.LimitRowsForManyColumns + General.ColumnWidthForName @@ -851,13 +929,13 @@ - - + + - Number of queries kept in the history. + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - History size: + Number of memorized query parameters @@ -877,7 +955,7 @@ - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> Execute only the query under the cursor @@ -887,13 +965,39 @@ - - + + - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + Number of queries kept in the history. - Number of memorized query parameters + History size: + + + + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + Wrap lines in SQL editor + + + General.SqlEditorWrapWords + + + + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + Highlight current query + + + General.SqlEditorCurrQueryHighlight @@ -941,7 +1045,7 @@ Allow multiple instances of the application at the same time - General.AllowMultipleSessions + General.AllowMultipleSessions @@ -985,6 +1089,387 @@ + + + + 15 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Code syntax colors + + + + + + Keyword foreground + + + Colors.SyntaxKeywordFgCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxForeground + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxBlobFg + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxKeywordFg + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxValidObject + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxCommentFg + + + + + + + Regular foreground + + + Colors.SyntaxForegroundCustom + + + + + + + Comment foreground + + + Colors.SyntaxCommentFgCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxNumberFg + + + + + + + Number foreground + + + Colors.SyntaxNumberFgCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxParenthesisBg + + + + + + + Valid objects foreground + + + Colors.SyntaxValidObjectCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxStringFg + + + + + + + + 50 + 16777215 + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + + + Colors.SyntaxCurrentQueryBg + + + + + + + BLOB value foreground + + + Colors.SyntaxBlobFgCustom + + + + + + + String foreground + + + Colors.SyntaxStringFgCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxCurrentLineBg + + + + + + + + + + Colors.SyntaxParenthesisFg + + + + + + + Bind parameter foreground + + + Colors.SyntaxBindParamFgCustom + + + + + + + + 50 + 16777215 + + + + + + + Colors.SyntaxBindParamFg + + + + + + + Current line background + + + Colors.SyntaxCurrentLineBgCustom + + + + + + + Current query background + + + Colors.SyntaxCurrentQueryBgCustom + + + + + + + Matched parenthesis background + + + Colors.SyntaxParenthesisBgCustom + + + + + + + Matched parenthesis foreground + + + Colors.SyntaxParenthesisFgCustom + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Reset to defaults + + + + + + + + @@ -1053,7 +1538,12 @@
    - + + + + + + @@ -1084,8 +1574,8 @@ 0 0 - 339 - 264 + 364 + 283 @@ -1503,10 +1993,13 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Abcdefgh</span></p></body></html> +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Abcdefgh</span></p></body></html> @@ -1596,8 +2089,8 @@ p, li { white-space: pre-wrap; } 0 0 - 206 - 298 + 222 + 313 @@ -1700,6 +2193,45 @@ p, li { white-space: pre-wrap; } + + + + + + Code assistant settings + + + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + Automatically trigger the assistant after a dot is typed after an object name + + + CodeAssistant.AutoTrigger + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + @@ -1717,19 +2249,13 @@ p, li { white-space: pre-wrap; } - - - - 0 - 0 - 100 - 30 - - - - + + ConfigRadioButton + QRadioButton +
    common/configradiobutton.h
    +
    FontEdit QWidget @@ -1737,9 +2263,9 @@ p, li { white-space: pre-wrap; } 1 - ConfigRadioButton - QRadioButton -
    common/configradiobutton.h
    + ColorButton + QPushButton +
    common/colorbutton.h
    diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp index 1e56258..e6a53db 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp @@ -1,4 +1,6 @@ #include "dbdialog.h" +#include "common/immediatetooltip.h" +#include "services/notifymanager.h" #include "ui_dbdialog.h" #include "services/pluginmanager.h" #include "plugins/dbplugin.h" @@ -18,6 +20,8 @@ #include #include #include +#include +#include DbDialog::DbDialog(Mode mode, QWidget *parent) : QDialog(parent), @@ -42,14 +46,32 @@ void DbDialog::setPermanent(bool perm) ui->permamentCheckBox->setChecked(perm); } + +void DbDialog::dragEnterEvent(QDragEnterEvent* e) +{ + if (e->mimeData()->hasUrls()) + e->acceptProposedAction(); +} + +void DbDialog::dropEvent(QDropEvent* e) +{ + if (!e->isAccepted() && e->mimeData()->hasUrls()) + { + setPath(e->mimeData()->urls().first().toLocalFile()); + e->accept(); + } +} + QString DbDialog::getPath() { - return ui->fileEdit->text(); + QString newPath = QDir::fromNativeSeparators(ui->fileEdit->text()); + return newPath; } void DbDialog::setPath(const QString& path) { - ui->fileEdit->setText(path); + QString newPath = QDir::toNativeSeparators(path); + ui->fileEdit->setText(newPath); } QString DbDialog::getName() @@ -82,7 +104,7 @@ void DbDialog::showEvent(QShowEvent *e) int idx = ui->typeCombo->findText(db->getTypeLabel()); ui->typeCombo->setCurrentIndex(idx); - ui->fileEdit->setText(db->getPath()); + setPath(db->getPath()); ui->nameEdit->setText(db->getName()); disableTypeAutodetection = false; } @@ -111,8 +133,7 @@ void DbDialog::showEvent(QShowEvent *e) void DbDialog::init() { ui->setupUi(this); - - ui->browseCreateButton->setIcon(ICONS.PLUS); + connIconTooltip = new ImmediateTooltip(ui->testConnIcon); for (DbPlugin* dbPlugin : PLUGINS->getLoadedPlugins()) dbPlugins[dbPlugin->getLabel()] = dbPlugin; @@ -122,12 +143,10 @@ void DbDialog::init() typeLabels.sort(Qt::CaseInsensitive); ui->typeCombo->addItems(typeLabels); - ui->browseCreateButton->setVisible(true); ui->testConnIcon->setVisible(false); connect(ui->fileEdit, SIGNAL(textChanged(QString)), this, SLOT(fileChanged(QString))); connect(ui->nameEdit, SIGNAL(textEdited(QString)), this, SLOT(nameModified(QString))); - connect(ui->browseCreateButton, SIGNAL(clicked()), this, SLOT(browseClicked())); connect(ui->browseOpenButton, SIGNAL(clicked()), this, SLOT(browseClicked())); connect(ui->testConnButton, SIGNAL(clicked()), this, SLOT(testConnectionClicked())); connect(ui->typeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(dbTypeChanged(int))); @@ -145,7 +164,7 @@ void DbDialog::updateOptions() setUpdatesEnabled(false); // Remove olds - for (QWidget* w : optionWidgets) + for (QWidget*& w : optionWidgets) { ui->optionsGrid->removeWidget(w); delete w; @@ -153,8 +172,7 @@ void DbDialog::updateOptions() customBrowseHandler = nullptr; ui->pathGroup->setTitle(tr("File")); - ui->browseOpenButton->setToolTip(tr("Browse for existing database file on local computer")); - ui->browseCreateButton->setVisible(true); + ui->browseOpenButton->setToolTip(tr("Select new or existing file on local computer")); optionWidgets.clear(); optionKeyToWidget.clear(); @@ -191,7 +209,6 @@ void DbDialog::addOption(const DbPluginOption& option, int& row) // This option does not add any editor, but has it's own label for path edit. row--; ui->pathGroup->setTitle(option.label); - ui->browseCreateButton->setVisible(false); if (!option.toolTip.isEmpty()) ui->browseOpenButton->setToolTip(option.toolTip); @@ -450,7 +467,7 @@ void DbDialog::updateType() if (disableTypeAutodetection) return; - DbPlugin* validPlugin = SQLITESTUDIO->getDbManager()->getPluginForDbFile(ui->fileEdit->text()); + DbPlugin* validPlugin = SQLITESTUDIO->getDbManager()->getPluginForDbFile(getPath()); if (!validPlugin || validPlugin->getLabel() == ui->typeCombo->currentText()) return; @@ -476,14 +493,20 @@ QHash DbDialog::collectOptions() return options; } -bool DbDialog::testDatabase() +bool DbDialog::testDatabase(QString& errorMsg) { if (ui->typeCombo->currentIndex() < 0) + { + errorMsg = tr("Database type not selected."); return false; + } - QString path = ui->fileEdit->text(); + QString path = getPath(); if (path.isEmpty()) + { + errorMsg = tr("Database path not specified."); return false; + } QUrl url(path); if (url.scheme().isEmpty()) @@ -491,7 +514,7 @@ bool DbDialog::testDatabase() QHash options = collectOptions(); DbPlugin* plugin = dbPlugins[ui->typeCombo->currentText()]; - Db* testDb = plugin->getInstance("", path, options); + Db* testDb = plugin->getInstance("", path, options, &errorMsg); bool res = false; if (testDb) @@ -499,6 +522,7 @@ bool DbDialog::testDatabase() if (testDb->openForProbing()) { res = !testDb->exec("SELECT sqlite_version();")->getSingleCell().toString().isEmpty(); + errorMsg = testDb->getErrorText(); testDb->closeQuiet(); } delete testDb; @@ -546,7 +570,7 @@ bool DbDialog::validate() if (fileState) { - registeredDb = DBLIST->getByPath(ui->fileEdit->text()); + registeredDb = DBLIST->getByPath(getPath()); if (registeredDb && (mode == Mode::ADD || registeredDb != db)) { setValidState(ui->fileEdit, false, tr("This database is already on the list under name: %1").arg(registeredDb->getName())); @@ -601,6 +625,7 @@ void DbDialog::typeChanged(int index) { UNUSED(index); updateOptions(); + updateState(); } void DbDialog::valueForNameGenerationChanged() @@ -612,10 +637,9 @@ void DbDialog::valueForNameGenerationChanged() QString generatedName; DbPlugin* plugin = dbPlugins.count() > 0 ? dbPlugins[ui->typeCombo->currentText()] : nullptr; if (plugin) - generatedName = DBLIST->generateUniqueDbName(plugin, ui->fileEdit->text()); + generatedName = DBLIST->generateUniqueDbName(plugin, getPath()); else - generatedName = DBLIST->generateUniqueDbName(ui->fileEdit->text()); - + generatedName = DBLIST->generateUniqueDbName(getPath()); ui->nameEdit->setText(generatedName); } @@ -624,7 +648,7 @@ void DbDialog::browseForFile() { QString dir = getFileDialogInitPath(); QString path = QFileDialog::getOpenFileName(0, QString(), dir); - if (path.isNull()) + if (path.isEmpty()) return; QString key = helperToKey[dynamic_cast(sender())]; @@ -645,18 +669,16 @@ void DbDialog::browseClicked() { if (customBrowseHandler) { - QString newUrl = customBrowseHandler(ui->fileEdit->text()); + QString newUrl = customBrowseHandler(getPath()); if (!newUrl.isNull()) { - ui->fileEdit->setText(newUrl); + setPath(newUrl); updateState(); } return; } - bool createMode = (sender() == ui->browseCreateButton); - - QFileInfo fileInfo(ui->fileEdit->text()); + QFileInfo fileInfo(getPath()); QString dir; if (ui->fileEdit->text().isEmpty()) dir = getFileDialogInitPath(); @@ -667,7 +689,7 @@ void DbDialog::browseClicked() else dir = getFileDialogInitPath(); - QString path = getDbPath(createMode, dir); + QString path = getDbPath(dir); if (path.isNull()) return; @@ -679,8 +701,19 @@ void DbDialog::browseClicked() void DbDialog::testConnectionClicked() { - ui->testConnIcon->setPixmap(testDatabase() ? ICONS.TEST_CONN_OK : ICONS.TEST_CONN_ERROR); + QString errorMsg; + bool ok = testDatabase(errorMsg); + ui->testConnIcon->setPixmap(ok ? ICONS.TEST_CONN_OK : ICONS.TEST_CONN_ERROR); + connIconTooltip->setToolTip(ok ? QString() : errorMsg); ui->testConnIcon->setVisible(true); + if (!ok) + { + QString path = getPath(); + if (!path.isEmpty()) + notifyWarn(QString("%1: %2").arg(getPath(), errorMsg)); + else + notifyWarn(errorMsg); + } } void DbDialog::dbTypeChanged(int index) diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h index 3d1a9da..76cd3ec 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h @@ -12,6 +12,7 @@ class DbPlugin; class QGridLayout; struct DbPluginOption; +class ImmediateTooltip; namespace Ui { class DbDialog; @@ -44,6 +45,8 @@ class GUI_API_EXPORT DbDialog : public QDialog protected: void changeEvent(QEvent *e); void showEvent(QShowEvent* e); + void dragEnterEvent(QDragEnterEvent* e); + void dropEvent(QDropEvent*e); private: void init(); @@ -53,7 +56,7 @@ class GUI_API_EXPORT DbDialog : public QDialog QVariant getValueFrom(DbPluginOption::Type type, QWidget* editor); void setValueFor(DbPluginOption::Type type, QWidget* editor, const QVariant& value); void updateType(); - bool testDatabase(); + bool testDatabase(QString& errorMsg); bool validate(); void updateState(); @@ -71,6 +74,7 @@ class GUI_API_EXPORT DbDialog : public QDialog bool disableTypeAutodetection = false; bool doAutoTest = false; bool nameManuallyEdited = false; + ImmediateTooltip* connIconTooltip = nullptr; static const constexpr int ADDITIONAL_ROWS_BEGIN_INDEX = 1; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui index 9878bec..f0ebe2a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui @@ -16,6 +16,9 @@ 0 + + true + Database @@ -45,20 +48,6 @@ - - - - Create new database file - - - - - - - :/icons/img/plus.png:/icons/img/plus.png - - - diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp index 7dda03e..135bc9d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp @@ -187,6 +187,7 @@ void ExportDialog::initQueryPage() return dbOk && queryOk; }); + ui->queryEdit->setAlwaysEnforceErrorsChecking(true); connect(ui->queryEdit, SIGNAL(errorsChecked(bool)), ui->queryPage, SIGNAL(completeChanged())); connect(ui->queryEdit, SIGNAL(textChanged()), ui->queryPage, SIGNAL(completeChanged())); @@ -619,7 +620,7 @@ void ExportDialog::updatePluginOptions(ExportPlugin* plugin, int& optionsRow) configMapper = new ConfigMapper(cfgMain); configMapper->bindToConfig(pluginOptionsWidget); - connect(configMapper, SIGNAL(modified()), this, SLOT(updateValidation())); + connect(configMapper, SIGNAL(modified(QWidget*)), this, SLOT(updateValidation())); plugin->validateOptions(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp index 565feb4..f443f18 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp @@ -1,12 +1,9 @@ #include "importdialog.h" #include "dblistmodel.h" #include "dbobjlistmodel.h" -#include "common/widgetstateindicator.h" #include "uiutils.h" #include "common/widgetcover.h" #include "services/dbmanager.h" -#include "services/pluginmanager.h" -#include "sqlitestudio.h" #include "plugins/importplugin.h" #include "ui_importdialog.h" #include "configmapper.h" @@ -14,6 +11,8 @@ #include "common/utils.h" #include "uiconfig.h" #include "themetuner.h" +#include "iconmanager.h" +#include "mainwindow.h" #include #include #include @@ -203,6 +202,7 @@ void ImportDialog::initDataSourcePage() void ImportDialog::removeOldOptions() { + pluginConfigOk.clear(); safe_delete(configMapper); safe_delete(pluginOptionsWidget); } @@ -284,7 +284,7 @@ void ImportDialog::updatePluginOptions(int& rows) configMapper = new ConfigMapper(cfgMain); configMapper->bindToConfig(pluginOptionsWidget); - connect(configMapper, SIGNAL(modified()), this, SLOT(updateValidation())); + connect(configMapper, SIGNAL(modified(QWidget*)), this, SLOT(updateValidation())); updateValidation(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp index ebf9beb..d5249d0 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp @@ -13,7 +13,6 @@ #include "indexexprcolumndialog.h" #include "windows/editorwindow.h" #include "services/codeformatter.h" -#include "common/compatibility.h" #include #include #include @@ -37,6 +36,10 @@ IndexDialog::IndexDialog(Db* db, const QString& index, QWidget* parent) : { existingIndex = true; init(); + + bool sysIdx = isSystemIndex(index); + ui->indexTab->setDisabled(sysIdx); + ui->ddlTab->setDisabled(sysIdx); } IndexDialog::~IndexDialog() @@ -709,15 +712,32 @@ void IndexDialog::preReject() preRejected = true; } +QString IndexDialog::getOriginalDdl() const +{ + SqliteCreateIndex* initialCreateIndex = originalCreateIndex->typeClone(); + initialCreateIndex->rebuildTokens(); + QString initialDdl = initialCreateIndex->detokenize(); + delete initialCreateIndex; + return initialDdl; +} + void IndexDialog::accept() { + QString initialDdl = getOriginalDdl(); rebuildCreateIndex(); + QString ddl = createIndex->detokenize(); + if (initialDdl == ddl) + { + // Nothing changed. Just close. + QDialog::accept(); + return; + } QStringList sqls; if (existingIndex) sqls << QString("DROP INDEX %1").arg(wrapObjIfNeeded(originalCreateIndex->index)); - sqls << createIndex->detokenize(); + sqls << ddl; if (!CFG_UI.General.DontShowDdlPreview.get()) { diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h index 2e586d4..dd68137 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h @@ -123,6 +123,7 @@ class GUI_API_EXPORT IndexDialog : public QDialog QStringList getExistingColumnExprs(const QString& exceptThis = QString()) const; QStringList getTableColumns() const; void preReject(); + QString getOriginalDdl() const; bool existingIndex = false; Db* db = nullptr; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.cpp index d9f05ff..b68ce10 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.cpp @@ -1,3 +1,4 @@ +#include "iconmanager.h" #include "languagedialog.h" #include "ui_languagedialog.h" #include "uiconfig.h" @@ -43,3 +44,8 @@ void LanguageDialog::askedForDefaultLanguage() { CFG_UI.General.LanguageAsked.set(true); } + +void LanguageDialog::showEvent(QShowEvent*) +{ + setWindowIcon(ICONS.SQLITESTUDIO_APP); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.h index d5fbed4..a11fc8a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/languagedialog.h @@ -13,6 +13,9 @@ class GUI_API_EXPORT LanguageDialog : public QDialog { Q_OBJECT + protected: + void showEvent(QShowEvent*); + public: explicit LanguageDialog(QWidget *parent = 0); ~LanguageDialog(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui index 12794ce..045c245 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui @@ -18,7 +18,6 @@ - 75 true @@ -35,12 +34,11 @@ 16 - 75 true - 0.0.0 + 0.0.0 Qt::AlignCenter diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp index 5dc506f..f4d93a4 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp @@ -2,9 +2,9 @@ #include "ui_populateconfigdialog.h" #include "plugins/populateplugin.h" #include "services/populatemanager.h" -#include "sqlitestudio.h" #include "formmanager.h" #include "configmapper.h" +#include "mainwindow.h" #include "uiutils.h" #include #include @@ -55,7 +55,7 @@ void PopulateConfigDialog::init() ui->headerLabel->setText(headerString ); configMapper = new ConfigMapper(engine->getConfig()); - connect(configMapper, SIGNAL(modified()), this, SLOT(validateEngine())); + connect(configMapper, SIGNAL(modified(QWidget*)), this, SLOT(validateEngine())); connect(POPULATE_MANAGER, SIGNAL(validationResultFromPlugin(bool,CfgEntry*,QString)), this, SLOT(validationResultFromPlugin(bool,CfgEntry*,QString))); connect(POPULATE_MANAGER, SIGNAL(stateUpdateRequestFromPlugin(CfgEntry*,bool,bool)), this, SLOT(stateUpdateRequestFromPlugin(CfgEntry*,bool,bool))); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp index aae0d58..89fff04 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp @@ -32,8 +32,13 @@ PopulateDialog::~PopulateDialog() void PopulateDialog::setDbAndTable(Db* db, const QString& table) { + QString oldTable = ui->tableCombo->currentText(); ui->databaseCombo->setCurrentText(db->getName()); ui->tableCombo->setCurrentText(table); + + // #4177 + if (oldTable == table) + refreshColumns(); } void PopulateDialog::init() @@ -48,7 +53,7 @@ void PopulateDialog::init() return p1->getTitle().compare(p2->getTitle()) < 0; }); - for (PopulatePlugin* plugin : plugins) + for (PopulatePlugin*& plugin : plugins) { pluginByName[plugin->getName()] = plugin; pluginTitles << plugin->getTitle(); @@ -103,7 +108,7 @@ void PopulateDialog::rebuildEngines(const QHashtableCombo->currentText().isNull(); bool colCountOk = false; - for (const ColumnEntry& entry : columnEntries) + for (ColumnEntry& entry : columnEntries) { if (entry.check->isChecked()) { diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp index 7890b3c..896ef39 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp @@ -1,10 +1,13 @@ #include "triggercolumnsdialog.h" #include "ui_triggercolumnsdialog.h" -#include "uiutils.h" #include TriggerColumnsDialog::TriggerColumnsDialog(QWidget *parent, int globalX, int globalY) : +#ifdef Q_OS_OSX + QDialog(parent), +#else QDialog(parent, Qt::Popup), +#endif globalX(globalX), globalY(globalY), ui(new Ui::TriggerColumnsDialog) diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp index 86862e2..5091613 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp @@ -1,12 +1,10 @@ #include "triggerdialog.h" #include "ui_triggerdialog.h" -#include "parser/ast/sqliteselect.h" #include "services/notifymanager.h" #include "parser/ast/sqliteexpr.h" #include "triggercolumnsdialog.h" #include "common/utils_sql.h" #include "schemaresolver.h" -#include "parser/parser.h" #include "iconmanager.h" #include "db/chainexecutor.h" #include "dbtree/dbtree.h" @@ -208,11 +206,14 @@ void TriggerDialog::readTrigger() if (createTrigger->queries.size() > 0) { QStringList sqls; - for (SqliteQuery* query : createTrigger->queries) + for (SqliteQuery*& query : createTrigger->queries) sqls << query->detokenize(); ui->codeEdit->setPlainText(sqls.join(";\n")+";"); } + + rebuildTrigger(); + originalDdl = ddl; } void TriggerDialog::setupVirtualSqls() @@ -299,7 +300,7 @@ void TriggerDialog::rebuildTrigger() if (actionType == SqliteCreateTrigger::Event::UPDATE_OF) { QStringList colNames; - for (const QString& colName : selectedColumns) + for (QString& colName : selectedColumns) colNames << wrapObjIfNeeded(colName); columns = " "+colNames.join(", "); @@ -321,7 +322,7 @@ void TriggerDialog::rebuildTrigger() if (!scope.isNull()) scope.prepend(" "); - ddl = tempDdl.arg(trigName).arg(when).arg(action).arg(columns).arg(target).arg(scope).arg(precondition).arg(queries); + ddl = tempDdl.arg(trigName, when, action, columns, target, scope, precondition, queries); } void TriggerDialog::updateState() @@ -385,6 +386,12 @@ void TriggerDialog::tableChanged(const QString& newValue) void TriggerDialog::accept() { rebuildTrigger(); + if (originalDdl == ddl) + { + // Nothing changed. Just close. + QDialog::accept(); + return; + } QStringList sqls; if (existingTrigger) diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h index 712ea5e..1eb7064 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h @@ -50,6 +50,7 @@ class GUI_API_EXPORT TriggerDialog : public QDialog QStringList targetColumns; QStringList selectedColumns; QString ddl; + QString originalDdl; SqliteCreateTriggerPtr createTrigger; Ui::TriggerDialog *ui = nullptr; diff --git a/SQLiteStudio3/guiSQLiteStudio/extendedpalette.cpp b/SQLiteStudio3/guiSQLiteStudio/extendedpalette.cpp index 8357810..eba2f4c 100644 --- a/SQLiteStudio3/guiSQLiteStudio/extendedpalette.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/extendedpalette.cpp @@ -1,10 +1,10 @@ #include "extendedpalette.h" +#include "common/unused.h" #include #include ExtendedPalette::ExtendedPalette() { - } QBrush ExtendedPalette::editorString() const @@ -27,26 +27,44 @@ void ExtendedPalette::setEditorLineBase(const QBrush &value) editorLineBaseBrush = value; } -void ExtendedPalette::styleChanged(QStyle *style, const QString &themeName) +bool ExtendedPalette::styleChanged(QStyle *style, const QString &themeName) { + UNUSED(themeName); QPalette stdPalette = style->standardPalette(); + if (stdPalette == lastPalette) + return false; + + lastPalette = stdPalette; bool isDark = stdPalette.base().color().lightness() < 128; - static const QColor stdAltColor = QColor(Qt::green); + static const QColor stdStrColor = QColor(Qt::green); + static const QColor stdExprColor = QColor(Qt::magenta); if (stdPalette.text().color().lightness() >= 128) - editorStringBrush = QBrush(stdAltColor.lighter()); + editorStringBrush = stdStrColor.lighter(); else - editorStringBrush = QBrush(stdAltColor.darker()); + editorStringBrush = stdStrColor.darker(); - if (themeName.toLower() == "macintosh" && isDark) - editorLineBaseBrush = QBrush(stdPalette.alternateBase().color().darker(300)); + if (stdPalette.text().color().lightness() >= 128) + editorExpressionBrush = stdExprColor.lighter(); else - editorLineBaseBrush = stdPalette.alternateBase(); + editorExpressionBrush = stdExprColor; if (isDark) + { mdiAreaBaseBrush = stdPalette.alternateBase(); + editorLineNumberBaseBrush = stdPalette.base().color().lighter(130); + editorLineBaseBrush = stdPalette.base().color().lighter(130); + editorCurrentQueryBrush = stdPalette.base().color().lighter(120); + } else - mdiAreaBaseBrush = QBrush("#8a8a8a"); + { + editorLineNumberBaseBrush = stdPalette.base().color().darker(120); + editorLineBaseBrush = stdPalette.base().color().darker(120); + editorCurrentQueryBrush = stdPalette.base().color().darker(110); + mdiAreaBaseBrush = QColor(138, 138, 138); + } + + return true; } QBrush ExtendedPalette::mdiAreaBase() const @@ -58,3 +76,33 @@ void ExtendedPalette::setMdiAreaBase(const QBrush& value) { mdiAreaBaseBrush = value; } + +QBrush ExtendedPalette::editorExpression() const +{ + return editorExpressionBrush; +} + +void ExtendedPalette::setEditorExpression(const QBrush& value) +{ + editorExpressionBrush = value; +} + +const QBrush& ExtendedPalette::editorCurrentQueryBase() const +{ + return editorCurrentQueryBrush; +} + +void ExtendedPalette::setEditorCurrentQueryBase(const QBrush& value) +{ + editorCurrentQueryBrush = value; +} + +const QBrush& ExtendedPalette::editorLineNumberBase() const +{ + return editorLineNumberBaseBrush; +} + +void ExtendedPalette::setEditorLineNumberBaseBrush(const QBrush& newEditorLineNumberBaseBrush) +{ + editorLineNumberBaseBrush = newEditorLineNumberBaseBrush; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/extendedpalette.h b/SQLiteStudio3/guiSQLiteStudio/extendedpalette.h index e6e5aef..6afaeba 100644 --- a/SQLiteStudio3/guiSQLiteStudio/extendedpalette.h +++ b/SQLiteStudio3/guiSQLiteStudio/extendedpalette.h @@ -2,6 +2,7 @@ #define EXTENDEDPALETTE_H #include +#include class QStyle; @@ -16,15 +17,29 @@ class ExtendedPalette QBrush editorLineBase() const; void setEditorLineBase(const QBrush &value); - void styleChanged(QStyle* style, const QString& themeName); + bool styleChanged(QStyle* style, const QString& themeName); QBrush mdiAreaBase() const; void setMdiAreaBase(const QBrush& value); + QBrush editorExpression() const; + void setEditorExpression(const QBrush& value); + + const QBrush& editorCurrentQueryBase() const; + void setEditorCurrentQueryBase(const QBrush& newEditorCurrentQueryBrush); + + const QBrush& editorLineNumberBase() const; + void setEditorLineNumberBaseBrush(const QBrush& newEditorLineNumberBaseBrush); + private: QBrush editorStringBrush; + QBrush editorExpressionBrush; QBrush editorLineBaseBrush; + QBrush editorLineNumberBaseBrush; + QBrush editorCurrentQueryBrush; QBrush mdiAreaBaseBrush; + + QPalette lastPalette; }; #endif // EXTENDEDPALETTE_H diff --git a/SQLiteStudio3/guiSQLiteStudio/formmanager.cpp b/SQLiteStudio3/guiSQLiteStudio/formmanager.cpp index bd5f6c0..55b0038 100644 --- a/SQLiteStudio3/guiSQLiteStudio/formmanager.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/formmanager.cpp @@ -1,7 +1,5 @@ #include "formmanager.h" -#include "services/config.h" #include "services/pluginmanager.h" -#include "sqlitestudio.h" #include "uiloader.h" #include "common/unused.h" #include "common/global.h" @@ -143,7 +141,7 @@ void FormManager::loadRecurently(const QString& path, const QString& prefix) continue; } - qDebug() << "Loading form file:" << fullPath; + qDebug().noquote() << "Loading form file:" << toNativePath(fullPath); widgetName = getWidgetName(fullPath); if (widgetName.isNull()) diff --git a/SQLiteStudio3/guiSQLiteStudio/formmanager.h b/SQLiteStudio3/guiSQLiteStudio/formmanager.h index fbffd7e..466dbd2 100644 --- a/SQLiteStudio3/guiSQLiteStudio/formmanager.h +++ b/SQLiteStudio3/guiSQLiteStudio/formmanager.h @@ -2,11 +2,13 @@ #define FORMMANAGER_H #include "guiSQLiteStudio_global.h" -#include "mainwindow.h" #include #include +#include class UiLoader; +class Plugin; +class PluginType; class GUI_API_EXPORT FormManager : public QObject { diff --git a/SQLiteStudio3/guiSQLiteStudio/formview.cpp b/SQLiteStudio3/guiSQLiteStudio/formview.cpp index 575c2de..84f9091 100644 --- a/SQLiteStudio3/guiSQLiteStudio/formview.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/formview.cpp @@ -1,11 +1,13 @@ #include "formview.h" #include "common/unused.h" +#include "datagrid/fkcombobox.h" #include "datagrid/sqlquerymodel.h" #include "datagrid/sqlqueryview.h" #include "widgetresizer.h" #include "datagrid/sqlqueryitem.h" #include "uiconfig.h" #include "common/datawidgetmapper.h" +#include "iconmanager.h" #include #include #include @@ -62,15 +64,15 @@ void FormView::setModel(SqlQueryModel* value) void FormView::load() { - reloadInternal(); - dataMapper->toFirst(); + shouldReload = true; + indexForReload = 0; } void FormView::reload() { - int idx = dataMapper->getCurrentIndex(); + shouldReload = true; + indexForReload = dataMapper->getCurrentIndex(); reloadInternal(); - dataMapper->setCurrentIndex(idx); } void FormView::focusFirstEditor() @@ -83,9 +85,14 @@ void FormView::focusFirstEditor() void FormView::reloadInternal() { + if (!shouldReload) + return; + + shouldReload = false; + // Cleanup dataMapper->clearMapping(); - for (QWidget* widget : widgets) + for (QWidget*& widget : widgets) { contents->layout()->removeWidget(widget); delete widget; @@ -97,8 +104,8 @@ void FormView::reloadInternal() // Recreate dataMapper->setModel(model.data()); int i = 0; - for (SqlQueryModelColumnPtr column : model->getColumns()) - addColumn(i++, column->displayName, column->dataType, (column->editionForbiddenReason.size() > 0)); + for (SqlQueryModelColumnPtr& column : model->getColumns()) + addColumn(i++, column.data()); } bool FormView::isModified() const @@ -106,12 +113,14 @@ bool FormView::isModified() const return valueModified; } -void FormView::addColumn(int colIdx, const QString& name, const DataType& dataType, bool readOnly) +MultiEditor* FormView::addColumn(int colIdx, SqlQueryModelColumn* column) { + bool readOnly = (column->editionForbiddenReason.size() > 0); + // Group with label - QString groupLabel = name; - if (!dataType.toString().isEmpty()) - groupLabel += " (" + dataType.toString() + ")"; + QString groupLabel = column->displayName; + if (!column->dataType.toString().isEmpty()) + groupLabel += " (" + column->dataType.toString() + ")"; // MultiEditor MultiEditor* multiEditor = new MultiEditor(); @@ -126,7 +135,24 @@ void FormView::addColumn(int colIdx, const QString& name, const DataType& dataTy connect(multiEditor, SIGNAL(modified()), this, SLOT(editorValueModified())); // MultiEditor editors - multiEditor->setDataType(dataType); + if (!column->getFkConstraints().isEmpty()) + { + Db* db = model->getDb(); + QString sql = FkComboBox::getSqlForFkEditor(db, column, QVariant()); + bool countingError = false; + qlonglong rowCount = FkComboBox::getRowCountForFkEditor(db, sql, &countingError); + if (!countingError && rowCount <= FkComboBox::MAX_ROWS_FOR_FK) + multiEditor->enableFk(db, column); + else + { + qDebug() << "FkCombo excluded from FormView for column" << column->column << "due to" + << (countingError ? + "error with row counting query" : + "too many rows in the FK table: " + QString::number(rowCount)); + } + } + + multiEditor->setDataType(column->dataType); // Resizer WidgetResizer* resizer = new WidgetResizer(Qt::Vertical); @@ -134,6 +160,8 @@ void FormView::addColumn(int colIdx, const QString& name, const DataType& dataTy resizer->setWidgetMinimumSize(0, minimumFieldHeight); widgets << resizer; contents->layout()->addWidget(resizer); + + return multiEditor; } bool FormView::isCurrentRowModifiedInGrid() @@ -266,3 +294,9 @@ QToolBar* FormView::getToolBar(int toolbar) const UNUSED(toolbar); return nullptr; } + +void FormView::showEvent(QShowEvent* event) +{ + UNUSED(event); + reloadInternal(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/formview.h b/SQLiteStudio3/guiSQLiteStudio/formview.h index 7f84e4f..c8f757a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/formview.h +++ b/SQLiteStudio3/guiSQLiteStudio/formview.h @@ -2,32 +2,30 @@ #define FORMVIEW_H #include "guiSQLiteStudio_global.h" -#include "datagrid/sqlquerymodelcolumn.h" #include "multieditor/multieditor.h" +#include "common/extactioncontainer.h" #include #include #include -#include class SqlQueryModel; class SqlQueryView; class DataWidgetMapper; CFG_KEY_LIST(FormView, QObject::tr("Data form view"), - CFG_KEY_ENTRY(COMMIT, Qt::CTRL + Qt::Key_Return, QObject::tr("Commit changes for current row")) - CFG_KEY_ENTRY(ROLLBACK, Qt::CTRL + Qt::Key_Backspace, QObject::tr("Rollback changes for current row")) - CFG_KEY_ENTRY(FIRST_ROW, Qt::CTRL + Qt::ALT + Qt::Key_PageUp, QObject::tr("Go to first row on current page")) - CFG_KEY_ENTRY(NEXT_ROW, Qt::CTRL + Qt::ALT + Qt::Key_Right, QObject::tr("Go to next row")) - CFG_KEY_ENTRY(PREV_ROW, Qt::CTRL + Qt::ALT + Qt::Key_Left, QObject::tr("Go to previous row")) - CFG_KEY_ENTRY(LAST_ROW, Qt::CTRL + Qt::ALT + Qt::Key_PageDown, QObject::tr("Go to last row on current page")) - CFG_KEY_ENTRY(INSERT_ROW, Qt::Key_Insert, QObject::tr("Insert new row")) - CFG_KEY_ENTRY(DELETE_ROW, Qt::CTRL + Qt::Key_Delete, QObject::tr("Delete current row")) + CFG_KEY_ENTRY(COMMIT, Qt::CTRL + Qt::Key_Return, QObject::tr("Commit changes for current row")) + CFG_KEY_ENTRY(ROLLBACK, Qt::ALT + Qt::SHIFT + Qt::Key_Backspace, QObject::tr("Rollback changes for current row")) + CFG_KEY_ENTRY(FIRST_ROW, Qt::CTRL + Qt::ALT + Qt::Key_PageUp, QObject::tr("Go to first row on current page")) + CFG_KEY_ENTRY(NEXT_ROW, Qt::CTRL + Qt::ALT + Qt::Key_Right, QObject::tr("Go to next row")) + CFG_KEY_ENTRY(PREV_ROW, Qt::CTRL + Qt::ALT + Qt::Key_Left, QObject::tr("Go to previous row")) + CFG_KEY_ENTRY(LAST_ROW, Qt::CTRL + Qt::ALT + Qt::Key_PageDown, QObject::tr("Go to last row on current page")) + CFG_KEY_ENTRY(INSERT_ROW, Qt::Key_Insert, QObject::tr("Insert new row")) + CFG_KEY_ENTRY(DELETE_ROW, Qt::CTRL + Qt::Key_Delete, QObject::tr("Delete current row")) ) class GUI_API_EXPORT FormView : public QScrollArea, public ExtActionContainer { Q_OBJECT - Q_ENUMS(Action) public: enum Action @@ -41,6 +39,7 @@ class GUI_API_EXPORT FormView : public QScrollArea, public ExtActionContainer INSERT_ROW, DELETE_ROW }; + Q_ENUM(Action) enum ToolBar { @@ -64,10 +63,11 @@ class GUI_API_EXPORT FormView : public QScrollArea, public ExtActionContainer void createActions(); void setupDefShortcuts(); QToolBar* getToolBar(int toolbar) const; + void showEvent(QShowEvent* event); private: void reloadInternal(); - void addColumn(int colIdx, const QString& name, const DataType& dataType, bool readOnly); + MultiEditor* addColumn(int colIdx, SqlQueryModelColumn* column); bool isCurrentRowModifiedInGrid(); void updateDeletedState(); @@ -84,6 +84,8 @@ class GUI_API_EXPORT FormView : public QScrollArea, public ExtActionContainer QList readOnly; bool valueModified = false; bool currentIndexUpdating = false; + bool shouldReload = false; + int indexForReload = 0; private slots: void dataLoaded(bool successful); diff --git a/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.pro b/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.pro index 59a56d0..9a436de 100644 --- a/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.pro +++ b/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.pro @@ -6,7 +6,7 @@ QT += core gui uitools widgets xml svg -include($$PWD/../dirs.pri) +include($$PWD/../common.pri) include($$PWD/../utils.pri) OBJECTS_DIR = $$OBJECTS_DIR/guiSQLiteStudio @@ -31,20 +31,12 @@ QMAKE_CXXFLAGS += -pedantic DEFINES += GUISQLITESTUDIO_LIBRARY -TRANSLATIONS += translations/guiSQLiteStudio_ro_RO.ts \ - translations/guiSQLiteStudio_de.ts \ - translations/guiSQLiteStudio_it.ts \ - translations/guiSQLiteStudio_zh_CN.ts \ - translations/guiSQLiteStudio_sk.ts \ - translations/guiSQLiteStudio_ru.ts \ - translations/guiSQLiteStudio_pt_BR.ts \ - translations/guiSQLiteStudio_fr.ts \ - translations/guiSQLiteStudio_es.ts \ - translations/guiSQLiteStudio_pl.ts - SOURCES +=\ common/dbcombobox.cpp \ + common/immediatetooltip.cpp \ + common/mouseshortcut.cpp \ constraints/columngeneratedpanel.cpp \ + datagrid/fkcombobox.cpp \ extendedpalette.cpp \ mainwindow.cpp \ iconmanager.cpp \ @@ -53,12 +45,14 @@ SOURCES +=\ dbtree/dbtree.cpp \ dbtree/dbtreeview.cpp \ actionentry.cpp \ + multieditor/multieditorfk.cpp \ style.cpp \ uiutils.cpp \ dbtree/dbtreeitemdelegate.cpp \ dbtree/dbtreeitemfactory.cpp \ sqleditor.cpp \ datagrid/sqlquerymodel.cpp \ + datagrid/sqldatasourcequerymodel.cpp \ dblistmodel.cpp \ mdiarea.cpp \ statusfield.cpp \ @@ -96,6 +90,8 @@ SOURCES +=\ completer/completerview.cpp \ dialogs/searchtextdialog.cpp \ searchtextlocator.cpp \ + windows/codesnippeteditor.cpp \ + windows/codesnippeteditormodel.cpp \ windows/tablewindow.cpp \ windows/editorwindow.cpp \ datagrid/sqltablemodel.cpp \ @@ -197,7 +193,10 @@ SOURCES +=\ HEADERS += mainwindow.h \ common/dbcombobox.h \ + common/immediatetooltip.h \ + common/mouseshortcut.h \ constraints/columngeneratedpanel.h \ + datagrid/fkcombobox.h \ extendedpalette.h \ iconmanager.h \ dbtree/dbtreemodel.h \ @@ -205,12 +204,14 @@ HEADERS += mainwindow.h \ dbtree/dbtree.h \ dbtree/dbtreeview.h \ actionentry.h \ + multieditor/multieditorfk.h \ style.h \ uiutils.h \ dbtree/dbtreeitemdelegate.h \ dbtree/dbtreeitemfactory.h \ sqleditor.h \ datagrid/sqlquerymodel.h \ + datagrid/sqldatasourcequerymodel.h \ dblistmodel.h \ mdiarea.h \ statusfield.h \ @@ -249,6 +250,8 @@ HEADERS += mainwindow.h \ completer/completerview.h \ dialogs/searchtextdialog.h \ searchtextlocator.h \ + windows/codesnippeteditor.h \ + windows/codesnippeteditormodel.h \ windows/tablewindow.h \ windows/editorwindow.h \ datagrid/sqltablemodel.h \ @@ -359,6 +362,7 @@ FORMS += mainwindow.ui \ statusfield.ui \ completer/completerwindow.ui \ dialogs/searchtextdialog.ui \ + windows/codesnippeteditor.ui \ windows/tablewindow.ui \ windows/editorwindow.ui \ dialogs/columndialog.ui \ @@ -428,6 +432,17 @@ DISTFILES += \ + + + + + + + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.qrc b/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.qrc index 56db5ac..07ef5dd 100644 --- a/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.qrc +++ b/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.qrc @@ -1,18 +1,5 @@ - - translations/guiSQLiteStudio_ro_RO.qm - translations/guiSQLiteStudio_pl.qm - translations/guiSQLiteStudio_ru.qm - translations/guiSQLiteStudio_fr.qm - translations/guiSQLiteStudio_sk.qm - translations/guiSQLiteStudio_zh_CN.qm - translations/guiSQLiteStudio_de.qm - general.css - - - - diff --git a/SQLiteStudio3/guiSQLiteStudio/iconmanager.cpp b/SQLiteStudio3/guiSQLiteStudio/iconmanager.cpp index 8610dbf..3ab4341 100644 --- a/SQLiteStudio3/guiSQLiteStudio/iconmanager.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/iconmanager.cpp @@ -48,7 +48,7 @@ void IconManager::init() iconFileExtensions << "*.png" << "*.PNG" << "*.jpg" << "*.JPG" << "*.svg" << "*.SVG"; movieFileExtensions << "*.gif" << "*.GIF" << "*.mng" << "*.MNG"; - for (QString dirPath : iconDirs) + for (QString& dirPath : iconDirs) { loadRecurently(dirPath, "", false); loadRecurently(dirPath, "", true); diff --git a/SQLiteStudio3/guiSQLiteStudio/iconmanager.h b/SQLiteStudio3/guiSQLiteStudio/iconmanager.h index 6bff0e0..d698a2b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/iconmanager.h +++ b/SQLiteStudio3/guiSQLiteStudio/iconmanager.h @@ -32,11 +32,14 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICON(APPLY_FILTER_RE, "apply_filter_re") DEF_ICON(APPLY_FILTER_SQL, "apply_filter_sql") DEF_ICON(APPLY_FILTER_TXT, "apply_filter_txt") + DEF_ICON(APPLY_FILTER_TXT_STRICT, "apply_filter_txt_strict") DEF_ICON(BUG, "bug") DEF_ICON(BUG_LIST, "bug_list") DEF_ICON(CLEAR_HISTORY, "clear_history") DEF_ICON(CLEAR_LINEEDIT, "clear_lineedit") DEF_ICON(CLOSE, "close") + DEF_ICON(CODE_ASSISTANT, "code_assistant") + DEF_ICON(CODE_SNIPPET, "code_snippet") DEF_ICON(COLUMN, "column") DEF_ICON(COLUMN_CONSTRAINT, "column_constraint") DEF_ICO2(COLUMN_CONSTRAINT_ADD, COLUMN_CONSTRAINT, PLUS) @@ -142,8 +145,6 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICON(KEYWORD, "keyword") DEF_ICON(LICENSES, "licenses") DEF_ICON(LOADING, "loading") - DEF_ICON(LOAD_FULL_VALUE, "load_full_value") - DEF_ICON(LOAD_FULL_VALUES, "load_full_values") DEF_ICON(MOVE_DOWN, "move_down") DEF_ICON(MOVE_UP, "move_up") DEF_ICO3(NEW_COLLATION, INSERT_ROW) @@ -164,6 +165,7 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICON(RENAME_FN_ARG, "rename_fn_arg") DEF_ICO3(RENAME_DATATYPE, RENAME_FN_ARG) DEF_ICON(RESET_AUTOINCREMENT, "reset_autoincrement") + DEF_ICON(RESTORE_DEFAULT, "restore_default") DEF_ICON(RESULTS_BELOW, "results_below") DEF_ICON(RESULTS_IN_TAB, "results_in_tab") DEF_ICON(ROLLBACK, "rollback") @@ -171,6 +173,7 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICON(SAVE_SQL_FILE, "save_sql_file") DEF_ICON(SEARCH, "search") DEF_ICON(SEARCH_AND_REPLACE, "search_and_replace") + DEF_ICON(SELECTION_INVERT, "selection_invert") DEF_ICON(SET_NULL, "set_null") DEF_ICON(SORT_COLUMNS, "sort_columns") DEF_ICON(SORT_COUNT_01, "sort_cnt_01") @@ -241,6 +244,8 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICON(WIN_TILE_VERTICAL, "win_tile_vertical") DEF_ICON(WIN_CLOSE, "window_close") DEF_ICON(WIN_CLOSE_ALL, "window_close_all") + DEF_ICON(WIN_CLOSE_ALL_LEFT, "window_close_all_left") + DEF_ICON(WIN_CLOSE_ALL_RIGHT, "window_close_all_right") DEF_ICON(WIN_CLOSE_OTHER, "window_close_other") DEF_ICON(WIN_RESTORE, "window_restore") DEF_ICON(WIN_RENAME, "window_rename") diff --git a/SQLiteStudio3/guiSQLiteStudio/icons.qrc b/SQLiteStudio3/guiSQLiteStudio/icons.qrc index 67c9351..cdb1f02 100644 --- a/SQLiteStudio3/guiSQLiteStudio/icons.qrc +++ b/SQLiteStudio3/guiSQLiteStudio/icons.qrc @@ -205,5 +205,12 @@ img/load_full_value.png img/load_full_values.png img/quit.png + img/window_close_all_left.png + img/window_close_all_right.png + img/code_assistant.png + img/code_snippet.png + img/selection_invert.png + img/apply_filter_txt_strict.png + img/restore_default.png diff --git a/SQLiteStudio3/guiSQLiteStudio/img/apply_filter_txt_strict.png b/SQLiteStudio3/guiSQLiteStudio/img/apply_filter_txt_strict.png new file mode 100644 index 0000000..593c5e2 Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/apply_filter_txt_strict.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/code_assistant.png b/SQLiteStudio3/guiSQLiteStudio/img/code_assistant.png new file mode 100644 index 0000000..5b47c67 Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/code_assistant.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/code_snippet.png b/SQLiteStudio3/guiSQLiteStudio/img/code_snippet.png new file mode 100644 index 0000000..d398622 Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/code_snippet.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/restore_default.png b/SQLiteStudio3/guiSQLiteStudio/img/restore_default.png new file mode 100644 index 0000000..d39f06d Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/restore_default.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/selection_invert.png b/SQLiteStudio3/guiSQLiteStudio/img/selection_invert.png new file mode 100644 index 0000000..889cb10 Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/selection_invert.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_256.png b/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_256.png new file mode 100644 index 0000000..5b99669 Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_256.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_48.png b/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_48.png new file mode 100644 index 0000000..1d7da73 Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_48.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_installer.png b/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_installer.png new file mode 100644 index 0000000..198b93f Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_installer.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_opt.ico b/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_opt.ico new file mode 100644 index 0000000..79cad9f Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_opt.ico differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/window_close_all_left.png b/SQLiteStudio3/guiSQLiteStudio/img/window_close_all_left.png new file mode 100644 index 0000000..6c6f3a8 Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/window_close_all_left.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/window_close_all_right.png b/SQLiteStudio3/guiSQLiteStudio/img/window_close_all_right.png new file mode 100644 index 0000000..0b21b17 Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/window_close_all_right.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/mainwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/mainwindow.cpp index 57c7d35..b1f1f42 100644 --- a/SQLiteStudio3/guiSQLiteStudio/mainwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/mainwindow.cpp @@ -4,8 +4,6 @@ #include "dbtree/dbtreemodel.h" #include "iconmanager.h" #include "windows/editorwindow.h" -#include "windows/tablewindow.h" -#include "windows/viewwindow.h" #include "windows/functionseditor.h" #include "windows/collationseditor.h" #include "windows/ddlhistorywindow.h" @@ -13,8 +11,6 @@ #include "mdiarea.h" #include "statusfield.h" #include "uiconfig.h" -#include "common/extaction.h" -#include "dbobjectdialogs.h" #include "services/notifymanager.h" #include "dialogs/configdialog.h" #include "services/pluginmanager.h" @@ -35,12 +31,13 @@ #include "dialogs/aboutdialog.h" #include "dialogs/newversiondialog.h" #include "dialogs/quitconfirmdialog.h" -#include "common/widgetcover.h" #include "dialogs/cssdebugdialog.h" #include "themetuner.h" #include "style.h" #include "services/codeformatter.h" #include "common/compatibility.h" +#include "windows/codesnippeteditor.h" +#include "uiutils.h" #include #include #include @@ -51,6 +48,8 @@ #include #include #include +#include +#include CFG_KEYS_DEFINE(MainWindow) MainWindow* MainWindow::instance = nullptr; @@ -139,6 +138,30 @@ void MainWindow::init() updateMultipleSessionsSetting(); fixFonts(); + fixToolbars(); + observeSessionChanges(); + + SQLITESTUDIO->installCrashHandler([this]() + { + saveSession(); + }); +} + +void MainWindow::observeSessionChanges() +{ + saveSessionTimer = new QTimer(this); + saveSessionTimer->setSingleShot(true); + connect(saveSessionTimer, SIGNAL(timeout()), this, SLOT(saveSession())); + + for (QDockWidget* dock : QList({dbTree, statusField})) + { + connect(dock, SIGNAL(topLevelChanged(bool)), this, SLOT(scheduleSessionSave())); + connect(dock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(scheduleSessionSave())); + connect(dock, SIGNAL(visibilityChanged(bool)), this, SLOT(scheduleSessionSave())); + } + connect(dbTree, SIGNAL(sessionValueChanged()), this, SLOT(scheduleSessionSave())); + connect(getMdiArea(), SIGNAL(sessionValueChanged()), this, SLOT(scheduleSessionSave())); + connect(this, SIGNAL(sessionValueChanged()), this, SLOT(scheduleSessionSave())); } void MainWindow::cleanUp() @@ -186,6 +209,8 @@ void MainWindow::updateWindowActions() actionMap[CLOSE_WINDOW]->setEnabled(hasActiveTask); actionMap[CLOSE_OTHER_WINDOWS]->setEnabled(hasActiveTask); actionMap[CLOSE_ALL_WINDOWS]->setEnabled(hasActiveTask); + actionMap[CLOSE_ALL_WINDOWS_LEFT]->setEnabled(hasActiveTask); + actionMap[CLOSE_ALL_WINDOWS_RIGHT]->setEnabled(hasActiveTask); actionMap[RENAME_WINDOW]->setEnabled(hasActiveTask); actionMap[RESTORE_WINDOW]->setEnabled(hasClosedWindowToRestore()); } @@ -225,11 +250,13 @@ void MainWindow::closeEvent(QCloseEvent* event) return; } + saveSessionTimer->stop(); + safe_delete(saveSessionTimer); + closingApp = true; closeNonSessionWindows(); - MdiWindow* currWindow = ui->mdiArea->getCurrentWindow(); - hide(); - saveSession(currWindow); + saveSession(true); + SQLITESTUDIO->cleanUp(); QMainWindow::closeEvent(event); } @@ -238,6 +265,7 @@ void MainWindow::createActions() createAction(OPEN_SQL_EDITOR, ICONS.OPEN_SQL_EDITOR, tr("Open SQL &editor"), this, SLOT(openSqlEditorSlot()), ui->mainToolBar); createAction(OPEN_DDL_HISTORY, ICONS.DDL_HISTORY, tr("Open DDL &history"), this, SLOT(openDdlHistorySlot()), ui->mainToolBar); createAction(OPEN_FUNCTION_EDITOR, ICONS.FUNCTION, tr("Open SQL &functions editor"), this, SLOT(openFunctionEditorSlot()), ui->mainToolBar); + createAction(OPEN_SNIPPETS_EDITOR, ICONS.CODE_SNIPPET, tr("Open code &snippets editor"), this, SLOT(openCodeSnippetsEditorSlot()), ui->mainToolBar); createAction(OPEN_COLLATION_EDITOR, ICONS.CONSTRAINT_COLLATION, tr("Open &collations editor"), this, SLOT(openCollationEditorSlot()), ui->mainToolBar); createAction(OPEN_EXTENSION_MANAGER, ICONS.EXTENSION, tr("Open ex&tension manager"), this, SLOT(openExtensionManagerSlot()), ui->mainToolBar); createAction(IMPORT, ICONS.IMPORT, tr("&Import"), this, SLOT(importAnything()), ui->mainToolBar); @@ -253,11 +281,13 @@ void MainWindow::createActions() createAction(PREV_TASK, tr("Previous window"), ui->taskBar, SLOT(prevTask()), this); createAction(HIDE_STATUS_FIELD, tr("Hide status field"), this, SLOT(hideStatusField()), this); - createAction(CLOSE_WINDOW, ICONS.WIN_CLOSE, tr("Close selected &window"), this, SLOT(closeSelectedWindow()), this); - createAction(CLOSE_OTHER_WINDOWS, ICONS.WIN_CLOSE_OTHER, tr("Close all windows &but selected"), this, SLOT(closeAllWindowsButSelected()), this); + createAction(CLOSE_WINDOW, ICONS.WIN_CLOSE, tr("Close current &window"), this, SLOT(closeSelectedWindow()), this); + createAction(CLOSE_OTHER_WINDOWS, ICONS.WIN_CLOSE_OTHER, tr("Close &other windows"), this, SLOT(closeAllWindowsButSelected()), this); createAction(CLOSE_ALL_WINDOWS, ICONS.WIN_CLOSE_ALL, tr("Close &all windows"), this, SLOT(closeAllWindows()), this); + createAction(CLOSE_ALL_WINDOWS_LEFT, ICONS.WIN_CLOSE_ALL_LEFT, tr("Close windows on the &left"), this, SLOT(closeAllLeftWindows()), this); + createAction(CLOSE_ALL_WINDOWS_RIGHT, ICONS.WIN_CLOSE_ALL_RIGHT, tr("Close windows on the &right"), this, SLOT(closeAllRightWindows()), this); createAction(RESTORE_WINDOW, ICONS.WIN_RESTORE, tr("Re&store recently closed window"), this, SLOT(restoreLastClosedWindow()), this); - createAction(RENAME_WINDOW, ICONS.WIN_RENAME, tr("&Rename selected window"), this, SLOT(renameWindow()), this); + createAction(RENAME_WINDOW, ICONS.WIN_RENAME, tr("Re&name selected window"), this, SLOT(renameWindow()), this); createAction(OPEN_DEBUG_CONSOLE, tr("Open Debug Console"), this, SLOT(openDebugConsole()), this); createAction(OPEN_CSS_CONSOLE, tr("Open CSS Console"), this, SLOT(openCssConsole()), this); @@ -352,8 +382,10 @@ void MainWindow::initMenuBar() viewMenu->addAction(actionMap[MDI_CASCADE]); viewMenu->addSeparator(); viewMenu->addAction(actionMap[CLOSE_WINDOW]); - viewMenu->addAction(actionMap[CLOSE_OTHER_WINDOWS]); viewMenu->addAction(actionMap[CLOSE_ALL_WINDOWS]); + viewMenu->addAction(actionMap[CLOSE_OTHER_WINDOWS]); + viewMenu->addAction(actionMap[CLOSE_ALL_WINDOWS_LEFT]); + viewMenu->addAction(actionMap[CLOSE_ALL_WINDOWS_RIGHT]); viewMenu->addSeparator(); viewMenu->addAction(actionMap[RESTORE_WINDOW]); viewMenu->addAction(actionMap[RENAME_WINDOW]); @@ -369,6 +401,7 @@ void MainWindow::initMenuBar() toolsMenu->addAction(actionMap[OPEN_SQL_EDITOR]); toolsMenu->addAction(actionMap[OPEN_DDL_HISTORY]); toolsMenu->addAction(actionMap[OPEN_FUNCTION_EDITOR]); + toolsMenu->addAction(actionMap[OPEN_SNIPPETS_EDITOR]); toolsMenu->addAction(actionMap[OPEN_COLLATION_EDITOR]); toolsMenu->addAction(actionMap[OPEN_EXTENSION_MANAGER]); toolsMenu->addAction(actionMap[IMPORT]); @@ -448,10 +481,16 @@ void MainWindow::restoreSession() } if (sessionValue.contains("style")) - setStyle(sessionValue["style"].toString()); + { + QString styleName = sessionValue["style"].toString(); + setStyle(styleName); + } else THEME_TUNER->tuneCurrentTheme(); + QString styleName = currentStyle(); + CFG_UI.General.Style.set(styleName); + if (sessionValue.contains("geometry")) restoreGeometry(sessionValue["geometry"].toByteArray()); @@ -533,22 +572,24 @@ MdiWindow* MainWindow::restoreWindowSession(const QVariant &windowSessions) { window->setCloseWithoutSessionSaving(true); delete window; + return nullptr; } return window; } -void MainWindow::setStyle(const QString& styleName) +bool MainWindow::setStyle(const QString& styleName) { QStyle* style = QStyleFactory::create(styleName); if (!style) { notifyWarn(tr("Could not set style: %1", "main window").arg(styleName)); - return; + return false; } STYLE->setStyle(style, styleName); statusField->refreshColors(); + return true; } QString MainWindow::currentStyle() const @@ -570,12 +611,33 @@ EditorWindow* MainWindow::openSqlEditor(Db* dbToSet, const QString& sql) return win; } +void MainWindow::saveSession(bool hide) +{ + MdiWindow* currWindow = ui->mdiArea->getCurrentWindow(); + if (hide) + this->hide(); + + saveSession(currWindow); +} + +void MainWindow::saveSession() +{ + saveSession(false); +} + +void MainWindow::scheduleSessionSave() +{ + if (saveSessionTimer) + saveSessionTimer->start(saveSessionDelayMs); +} + void MainWindow::closeNonSessionWindows() { for (MdiWindow* window : ui->mdiArea->getWindows()) if (!window->restoreSessionNextTime()) window->close(); } + FormManager* MainWindow::getFormManager() const { return formManager; @@ -607,6 +669,7 @@ void MainWindow::refreshMdiWindows() for (const QString& name : actionNames) mdiMenu->addAction(nameToAction[name]); + fixToolbarTooltips(ui->viewToolbar); updateWindowActions(); } @@ -632,6 +695,11 @@ void MainWindow::openFunctionEditorSlot() openFunctionEditor(); } +void MainWindow::openCodeSnippetsEditorSlot() +{ + openCodeSnippetEditor(); +} + void MainWindow::openCollationEditorSlot() { openCollationEditor(); @@ -679,6 +747,16 @@ void MainWindow::closeAllWindows() ui->mdiArea->closeAllSubWindows(); } +void MainWindow::closeAllLeftWindows() +{ + ui->mdiArea->closeAllLeftToActive(); +} + +void MainWindow::closeAllRightWindows() +{ + ui->mdiArea->closeAllRightToActive(); +} + void MainWindow::closeAllWindowsButSelected() { ui->mdiArea->closeAllButActive(); @@ -784,14 +862,12 @@ void MainWindow::quit() void MainWindow::updateMultipleSessionsSetting(const QVariant& newValue) { - QSettings sett; - sett.setValue(ALLOW_MULTIPLE_SESSIONS_SETTING, newValue); + Config::getSettings()->setValue(ALLOW_MULTIPLE_SESSIONS_SETTING, newValue); } void MainWindow::updateMultipleSessionsSetting() { - QSettings sett; - sett.setValue(ALLOW_MULTIPLE_SESSIONS_SETTING, CFG_UI.General.AllowMultipleSessions.get()); + Config::getSettings()->setValue(ALLOW_MULTIPLE_SESSIONS_SETTING, CFG_UI.General.AllowMultipleSessions.get()); } #ifdef PORTABLE_CONFIG @@ -859,6 +935,11 @@ FunctionsEditor* MainWindow::openFunctionEditor() return openMdiWindow(); } +CodeSnippetEditor* MainWindow::openCodeSnippetEditor() +{ + return openMdiWindow(); +} + CollationsEditor* MainWindow::openCollationEditor() { return openMdiWindow(); @@ -876,10 +957,18 @@ void MainWindow::fixFonts() { typed = dynamic_cast*>(cfg); if (typed->get().pointSize() == 0) - cfg->set(cfg->getDefultValue()); + cfg->set(cfg->getDefaultValue()); } } +void MainWindow::fixToolbars() +{ + fixToolbarTooltips(ui->viewToolbar); + fixToolbarTooltips(ui->mainToolBar); + fixToolbarTooltips(ui->structureToolbar); + fixToolbarTooltips(ui->dbToolbar); +} + bool MainWindow::confirmQuit(const QList& instances) { QuitConfirmDialog dialog(MAINWINDOW); diff --git a/SQLiteStudio3/guiSQLiteStudio/mainwindow.h b/SQLiteStudio3/guiSQLiteStudio/mainwindow.h index cedbc20..01457b0 100644 --- a/SQLiteStudio3/guiSQLiteStudio/mainwindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/mainwindow.h @@ -5,7 +5,6 @@ #include "db/db.h" #include "ui_mainwindow.h" #include "mdiwindow.h" -#include "services/updatemanager.h" #include "guiSQLiteStudio_global.h" #include #include @@ -30,8 +29,10 @@ class Committable; class WidgetCover; class QProgressBar; class QLabel; +class QTimer; class ThemeTuner; class SqliteExtensionEditor; +class CodeSnippetEditor; #ifdef Q_OS_MACX #define PREV_TASK_KEY_SEQ Qt::CTRL + Qt::ALT + Qt::Key_Left @@ -42,20 +43,26 @@ class SqliteExtensionEditor; #endif CFG_KEY_LIST(MainWindow, QObject::tr("Main window"), - CFG_KEY_ENTRY(OPEN_SQL_EDITOR, Qt::ALT + Qt::Key_E, QObject::tr("Open SQL editor")) - CFG_KEY_ENTRY(PREV_TASK, PREV_TASK_KEY_SEQ, QObject::tr("Previous window")) - CFG_KEY_ENTRY(NEXT_TASK, NEXT_TASK_KEY_SEQ, QObject::tr("Next window")) - CFG_KEY_ENTRY(HIDE_STATUS_FIELD, Qt::Key_Escape, QObject::tr("Hide status area")) - CFG_KEY_ENTRY(OPEN_CONFIG, Qt::Key_F2, QObject::tr("Open configuration dialog")) - CFG_KEY_ENTRY(OPEN_DEBUG_CONSOLE, Qt::Key_F12, QObject::tr("Open Debug Console")) - CFG_KEY_ENTRY(OPEN_CSS_CONSOLE, Qt::Key_F11, QObject::tr("Open CSS Console")) - CFG_KEY_ENTRY(QUIT, Qt::CTRL + Qt::Key_Q, QObject::tr("Quit the application")) + CFG_KEY_ENTRY(OPEN_SQL_EDITOR, Qt::ALT + Qt::Key_E, QObject::tr("Open SQL editor")) + CFG_KEY_ENTRY(OPEN_DDL_HISTORY, Qt::CTRL + Qt::Key_H, QObject::tr("Open DDL history window")) + CFG_KEY_ENTRY(OPEN_SNIPPETS_EDITOR, Qt::CTRL + Qt::SHIFT + Qt::Key_P, QObject::tr("Open snippets editor window")) + CFG_KEY_ENTRY(OPEN_FUNCTION_EDITOR, Qt::CTRL + Qt::SHIFT + Qt::Key_F, QObject::tr("Open function editor window")) + CFG_KEY_ENTRY(OPEN_COLLATION_EDITOR, Qt::CTRL + Qt::SHIFT + Qt::Key_C, QObject::tr("Open collation editor window")) + CFG_KEY_ENTRY(OPEN_EXTENSION_MANAGER, Qt::CTRL + Qt::SHIFT + Qt::Key_E, QObject::tr("Open extension manager window")) + CFG_KEY_ENTRY(PREV_TASK, PREV_TASK_KEY_SEQ, QObject::tr("Previous window")) + CFG_KEY_ENTRY(NEXT_TASK, NEXT_TASK_KEY_SEQ, QObject::tr("Next window")) + CFG_KEY_ENTRY(HIDE_STATUS_FIELD, Qt::Key_Escape, QObject::tr("Hide status area")) + CFG_KEY_ENTRY(USER_MANUAL, Qt::Key_F1, QObject::tr("Open user manual")) + CFG_KEY_ENTRY(OPEN_CONFIG, Qt::Key_F10, QObject::tr("Open configuration dialog")) + CFG_KEY_ENTRY(OPEN_DEBUG_CONSOLE, Qt::Key_F12, QObject::tr("Open Debug Console")) + CFG_KEY_ENTRY(OPEN_CSS_CONSOLE, Qt::Key_F11, QObject::tr("Open CSS Console")) + CFG_KEY_ENTRY(ABOUT, Qt::SHIFT + Qt::Key_F1, QObject::tr("Open the About dialog")) + CFG_KEY_ENTRY(QUIT, Qt::CTRL + Qt::Key_Q, QObject::tr("Quit the application")) ) class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer { - Q_OBJECT - Q_ENUMS(Action) + Q_OBJECT public: enum Action @@ -70,6 +77,7 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer HIDE_STATUS_FIELD, OPEN_CONFIG, OPEN_DDL_HISTORY, + OPEN_SNIPPETS_EDITOR, OPEN_FUNCTION_EDITOR, OPEN_COLLATION_EDITOR, OPEN_EXTENSION_MANAGER, @@ -77,6 +85,8 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer IMPORT, CLOSE_WINDOW, CLOSE_ALL_WINDOWS, + CLOSE_ALL_WINDOWS_LEFT, + CLOSE_ALL_WINDOWS_RIGHT, CLOSE_OTHER_WINDOWS, RESTORE_WINDOW, RENAME_WINDOW, @@ -94,6 +104,7 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer CHECK_FOR_UPDATES, QUIT }; + Q_ENUM(Action) enum ToolBar { @@ -109,7 +120,7 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer DbTree* getDbTree() const; StatusField* getStatusField() const; void restoreSession(); - void setStyle(const QString& styleName); + bool setStyle(const QString& styleName); FormManager* getFormManager() const; bool eventFilter(QObject* obj, QEvent* e); void pushClosedWindowSessionValue(const QVariant& value); @@ -136,18 +147,22 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer ~MainWindow(); void init(); + void observeSessionChanges(); void createActions(); void setupDefShortcuts(); void initMenuBar(); void saveSession(MdiWindow* currWindow); + void saveSession(bool hide); void restoreWindowSessions(const QList& windowSessions); MdiWindow *restoreWindowSession(const QVariant& windowSessions); void closeNonSessionWindows(); DdlHistoryWindow* openDdlHistory(); FunctionsEditor* openFunctionEditor(); + CodeSnippetEditor* openCodeSnippetEditor(); CollationsEditor* openCollationEditor(); SqliteExtensionEditor* openExtensionManager(); void fixFonts(); + void fixToolbars(); template T* openMdiWindow(); @@ -157,6 +172,7 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer static MainWindow* instance; static constexpr int closedWindowsStackSize = 20; static_char* openUpdatesUrl = "open_updates://"; + static constexpr int saveSessionDelayMs = 500; Ui::MainWindow *ui = nullptr; DbTree* dbTree = nullptr; @@ -175,6 +191,7 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer #endif WidgetCover* widgetCover = nullptr; bool manualUpdatesChecking = false; + QTimer* saveSessionTimer = nullptr; public slots: EditorWindow* openSqlEditor(); @@ -198,11 +215,14 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer void openConfig(); void openDdlHistorySlot(); void openFunctionEditorSlot(); + void openCodeSnippetsEditorSlot(); void openCollationEditorSlot(); void openExtensionManagerSlot(); void exportAnything(); void importAnything(); void closeAllWindows(); + void closeAllLeftWindows(); + void closeAllRightWindows(); void closeAllWindowsButSelected(); void closeSelectedWindow(); void restoreLastClosedWindow(); @@ -221,6 +241,11 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer void quit(); void updateMultipleSessionsSetting(); void updateMultipleSessionsSetting(const QVariant& newValue); + void saveSession(); + void scheduleSessionSave(); + + signals: + void sessionValueChanged(); }; template diff --git a/SQLiteStudio3/guiSQLiteStudio/mdiarea.cpp b/SQLiteStudio3/guiSQLiteStudio/mdiarea.cpp index 8f8521d..d73c506 100644 --- a/SQLiteStudio3/guiSQLiteStudio/mdiarea.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/mdiarea.cpp @@ -42,7 +42,10 @@ MdiWindow *MdiArea::addSubWindow(MdiChild *mdiChild) mdiWin->setWindowState(mdiWin->windowState()|Qt::WindowMaximized); } + connect(mdiChild, SIGNAL(sessionValueChanged()), this, SIGNAL(sessionValueChanged())); + emit windowListChanged(); + emit sessionValueChanged(); return mdiWin; } @@ -87,6 +90,16 @@ QList MdiArea::getMdiChilds() const return childs; } +void MdiArea::enforceTaskSelectionAfterWindowClose(QAction* task) +{ + taskToSelectAfterWindowClose = task; +} + +void MdiArea::enforceCurrentTaskSelectionAfterWindowClose() +{ + enforceTaskSelectionAfterWindowClose(getTaskBar()->getActiveTask()); +} + QList MdiArea::getWindowsToTile() const { QList list; @@ -110,6 +123,7 @@ void MdiArea::taskActivated() } setActiveSubWindow(actionToWinMap[action]); + emit sessionValueChanged(); } void MdiArea::windowDestroyed(MdiWindow* window) @@ -121,7 +135,11 @@ void MdiArea::windowDestroyed(MdiWindow* window) QAction* taskToSelect = nullptr; if (!MAINWINDOW->isClosingApp()) { - taskToSelect = taskBar->getNextTask(action); + taskToSelect = taskToSelectAfterWindowClose; + taskToSelectAfterWindowClose = nullptr; + if (!taskToSelect || action == taskToSelect || !taskBar->getTasks().contains(taskToSelect)) + taskToSelect = taskBar->getNextTask(action); + if (!taskToSelect) taskToSelect = taskBar->getPrevTask(action); } @@ -132,6 +150,7 @@ void MdiArea::windowDestroyed(MdiWindow* window) delete action; emit windowListChanged(); + emit sessionValueChanged(); if (taskToSelect) taskBar->setActiveTask(taskToSelect); @@ -151,6 +170,7 @@ void MdiArea::windowActivated() QAction* action = winToActionMap[subWin]; action->setChecked(true); + emit sessionValueChanged(); } void MdiArea::tileHorizontally() @@ -212,16 +232,36 @@ void MdiArea::closeAllButActive() QList allButActive = subWindowList(); allButActive.removeOne(activeSubWindow()); - for (QMdiSubWindow *window : allButActive) + for (QMdiSubWindow*& window : allButActive) window->close(); } +void MdiArea::closeAllLeftToActive() +{ + QList tasks = taskBar->getTasks(); + QAction* activeTask = taskBar->getActiveTask(); + int activeIdx = tasks.indexOf(activeTask); + + for (QAction*& task : tasks.mid(0, activeIdx)) + actionToWinMap[task]->close(); +} + +void MdiArea::closeAllRightToActive() +{ + QList tasks = taskBar->getTasks(); + QAction* activeTask = taskBar->getActiveTask(); + int activeIdx = tasks.indexOf(activeTask); + + for (QAction*& task : tasks.mid(activeIdx + 1)) + actionToWinMap[task]->close(); +} + MdiWindow* MdiArea::getWindowByChild(MdiChild *child) { if (!child) return nullptr; - for (QMdiSubWindow *window : subWindowList()) + for (QMdiSubWindow*& window : subWindowList()) if (window->widget() == child) return dynamic_cast(window); diff --git a/SQLiteStudio3/guiSQLiteStudio/mdiarea.h b/SQLiteStudio3/guiSQLiteStudio/mdiarea.h index f34434e..2cb488a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/mdiarea.h +++ b/SQLiteStudio3/guiSQLiteStudio/mdiarea.h @@ -28,6 +28,8 @@ class GUI_API_EXPORT MdiArea : public QMdiArea QAction* getTaskByWindow(MdiWindow* window); QList getWindows() const; QList getMdiChilds() const; + void enforceTaskSelectionAfterWindowClose(QAction* task); + void enforceCurrentTaskSelectionAfterWindowClose(); template QList getMdiChilds() const; @@ -38,9 +40,11 @@ class GUI_API_EXPORT MdiArea : public QMdiArea TaskBar* taskBar = nullptr; QHash actionToWinMap; QHash winToActionMap; + QAction* taskToSelectAfterWindowClose = nullptr; signals: void windowListChanged(); + void sessionValueChanged(); private slots: void taskActivated(); @@ -51,6 +55,8 @@ class GUI_API_EXPORT MdiArea : public QMdiArea void tileHorizontally(); void tileVertically(); void closeAllButActive(); + void closeAllLeftToActive(); + void closeAllRightToActive(); }; template diff --git a/SQLiteStudio3/guiSQLiteStudio/mdichild.h b/SQLiteStudio3/guiSQLiteStudio/mdichild.h index 5ca20e5..fa0c3e5 100644 --- a/SQLiteStudio3/guiSQLiteStudio/mdichild.h +++ b/SQLiteStudio3/guiSQLiteStudio/mdichild.h @@ -41,6 +41,9 @@ class GUI_API_EXPORT MdiChild : public QWidget, public ExtActionContainer, publi private: MdiWindow* mdiWindow = nullptr; + + signals: + void sessionValueChanged(); }; diff --git a/SQLiteStudio3/guiSQLiteStudio/mdiwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/mdiwindow.cpp index 685e608..f6072c3 100644 --- a/SQLiteStudio3/guiSQLiteStudio/mdiwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/mdiwindow.cpp @@ -1,7 +1,7 @@ #include "mdiwindow.h" #include "mdichild.h" -#include "common/unused.h" #include "mdiarea.h" +#include "iconmanager.h" #include "mainwindow.h" #include "services/dbmanager.h" #include "db/db.h" @@ -170,7 +170,7 @@ void MdiWindow::closeEvent(QCloseEvent* e) void MdiWindow::dbAboutToBeDisconnected(Db* db, bool& deny) { - if (!db || getMdiChild()->getAssociatedDb() != db) + if (!isAssociatedWithDb(db)) return; if (MAINWINDOW->isClosingApp()) @@ -184,14 +184,13 @@ void MdiWindow::dbAboutToBeDisconnected(Db* db, bool& deny) void MdiWindow::dbDisconnected(Db* db) { - if (!db || getMdiChild()->getAssociatedDb() != db) + if (!isAssociatedWithDb(db)) return; if (MAINWINDOW->isClosingApp()) return; - getMdiChild()->dbClosedFinalCleanup(); - close(); + closeWindow(); } bool MdiWindow::confirmClose() @@ -208,6 +207,22 @@ bool MdiWindow::confirmClose() return (msgBox.exec() == QMessageBox::Yes); } + +void MdiWindow::closeWindow() +{ + getMdiChild()->dbClosedFinalCleanup(); + MDIAREA->enforceCurrentTaskSelectionAfterWindowClose(); + close(); +} + +bool MdiWindow::isAssociatedWithDb(Db* db) +{ + if (dbBeingClosed) + return true; // it was already confirmed by dbAboutToBeDisconnected() and any changes to the "db" member afterwards should not inflict decision change here + + return db && getMdiChild()->getAssociatedDb() == db; +} + bool MdiWindow::getCloseWithoutSessionSaving() const { return closeWithoutSessionSaving; diff --git a/SQLiteStudio3/guiSQLiteStudio/mdiwindow.h b/SQLiteStudio3/guiSQLiteStudio/mdiwindow.h index 9a7be2b..18dafa2 100644 --- a/SQLiteStudio3/guiSQLiteStudio/mdiwindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/mdiwindow.h @@ -33,6 +33,8 @@ class GUI_API_EXPORT MdiWindow : public QMdiSubWindow private: bool confirmClose(); + void closeWindow(); + bool isAssociatedWithDb(Db* db); QPointer lastFocusedWidget; MdiArea* mdiArea = nullptr; diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.cpp b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.cpp index 0f965cd..b3ee1ee 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.cpp @@ -1,4 +1,5 @@ #include "multieditor.h" +#include "multieditor/multieditorfk.h" #include "multieditortext.h" #include "multieditornumeric.h" #include "multieditordatetime.h" @@ -7,15 +8,15 @@ #include "multieditorbool.h" #include "multieditorhex.h" #include "mainwindow.h" -#include "common/unused.h" +#include "iconmanager.h" #include "services/notifymanager.h" #include "services/pluginmanager.h" #include "multieditorwidgetplugin.h" #include "uiconfig.h" #include "dialogs/configdialog.h" -#include "formview.h" #include "themetuner.h" #include "common/compatibility.h" +#include "datagrid/sqlquerymodelcolumn.h" #include #include #include @@ -191,7 +192,6 @@ void MultiEditor::addEditor(MultiEditorWidget* editorWidget) connect(editorWidget, SIGNAL(valueModified()), this, SLOT(invalidateValue())); editors << editorWidget; tabs->addTab(editorWidget, editorWidget->getTabLabel().replace("&", "&&")); - THEME_TUNER->manageCompactLayout(editorWidget); editorWidget->installEventFilter(this); connect(editorWidget, &MultiEditorWidget::aboutToBeDeleted, [this, editorWidget]() @@ -280,7 +280,7 @@ void MultiEditor::setDataType(const DataType& dataType) { this->dataType = dataType; - for (MultiEditorWidget* editorWidget : getEditorTypes(dataType)) + for (MultiEditorWidget*& editorWidget : getEditorTypes(dataType)) addEditor(editorWidget); showTab(0); @@ -288,6 +288,14 @@ void MultiEditor::setDataType(const DataType& dataType) configBtn->setEnabled(true); } +void MultiEditor::enableFk(Db* db, SqlQueryModelColumn* column) +{ + MultiEditorFk* fkEditor = new MultiEditorFk(); + fkEditor->initFkCombo(db, column); + fkEditor->setTabLabel(tr("Foreign Key")); + addEditor(fkEditor); +} + void MultiEditor::focusThisEditor() { MultiEditorWidget* w = dynamic_cast(tabs->currentWidget()); @@ -324,14 +332,15 @@ QList MultiEditor::getEditorTypes(const DataType& dataType) if (editorsOrder.contains(typeStr)) { MultiEditorWidgetPlugin* plugin = nullptr; - for (const QString& editorPluginName : editorsOrder[typeStr].toStringList()) + for (QString& editorPluginName : editorsOrder[typeStr].toStringList()) { plugin = dynamic_cast(PLUGINS->getLoadedPlugin(editorPluginName)); if (!plugin) { if (!missingEditorPluginsAlreadyWarned.contains(editorPluginName)) { - notifyWarn(tr("Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type.")); + notifyWarn(tr("Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type.") + .arg(editorPluginName, typeStr)); missingEditorPluginsAlreadyWarned[editorPluginName] = true; } continue; @@ -371,7 +380,7 @@ QList MultiEditor::getEditorTypes(const DataType& dataType) return ed1.first < ed2.first; }); - for (const EditorWithPriority& e : sortedEditors) + for (EditorWithPriority& e : sortedEditors) editors << e.second; return editors; @@ -439,7 +448,7 @@ QVariant MultiEditor::getValueOmmitNull() const void MultiEditor::initAddTabMenu() { addTabMenu = new QMenu(addTabBtn); - for (MultiEditorWidgetPlugin* plugin : PLUGINS->getLoadedPlugins()) + for (MultiEditorWidgetPlugin*& plugin : PLUGINS->getLoadedPlugins()) addPluginToMenu(plugin); sortAddTabMenu(); diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.h b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.h index 55d7f2e..25b09de 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.h +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.h @@ -1,11 +1,13 @@ #ifndef MULTIEDITOR_H #define MULTIEDITOR_H +#include "datatype.h" #include "guiSQLiteStudio_global.h" -#include "datagrid/sqlquerymodelcolumn.h" #include #include +class Db; +class SqlQueryModelColumn; class QCheckBox; class QTabWidget; class MultiEditorWidget; @@ -51,9 +53,23 @@ class GUI_API_EXPORT MultiEditor : public QWidget void setReadOnly(bool value); void setDeletedRow(bool value); void setDataType(const DataType& dataType); + void enableFk(Db* db, SqlQueryModelColumn* column); void focusThisEditor(); void setCornerLabel(const QString& label); + template + T* getEditorWidget() const + { + QListIterator it(editors); + while (it.hasNext()) + { + T* casted = dynamic_cast(it.next()); + if (casted) + return casted; + } + return nullptr; + } + static void loadBuiltInEditors(); private: diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorbool.cpp b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorbool.cpp index 1882335..a466e81 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorbool.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorbool.cpp @@ -175,6 +175,7 @@ bool MultiEditorBoolPlugin::validFor(const DataType& dataType) case DataType::DATE: case DataType::DATETIME: case DataType::TIME: + case DataType::ANY: case DataType::unknown: break; } @@ -188,6 +189,7 @@ int MultiEditorBoolPlugin::getPriority(const DataType& dataType) case DataType::BOOLEAN: return 1; case DataType::BLOB: + case DataType::ANY: case DataType::BIGINT: case DataType::DECIMAL: case DataType::DOUBLE: diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordate.cpp b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordate.cpp index 4059f96..2377a7d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordate.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordate.cpp @@ -45,6 +45,7 @@ bool MultiEditorDatePlugin::validFor(const DataType& dataType) case DataType::VARCHAR: case DataType::DATETIME: case DataType::TIME: + case DataType::ANY: case DataType::unknown: break; case DataType::DATE: @@ -59,6 +60,7 @@ int MultiEditorDatePlugin::getPriority(const DataType& dataType) { case DataType::BLOB: case DataType::BOOLEAN: + case DataType::ANY: case DataType::BIGINT: case DataType::DECIMAL: case DataType::DOUBLE: diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordatetime.cpp b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordatetime.cpp index 99053b0..d876378 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordatetime.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordatetime.cpp @@ -231,6 +231,7 @@ bool MultiEditorDateTimePlugin::validFor(const DataType& dataType) case DataType::CHAR: case DataType::VARCHAR: case DataType::TIME: + case DataType::ANY: case DataType::unknown: break; case DataType::DATE: @@ -259,6 +260,7 @@ int MultiEditorDateTimePlugin::getPriority(const DataType& dataType) case DataType::CHAR: case DataType::VARCHAR: case DataType::TIME: + case DataType::ANY: case DataType::unknown: break; case DataType::DATE: diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordialog.cpp b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordialog.cpp index 5e3985c..75ebb07 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordialog.cpp @@ -47,3 +47,8 @@ void MultiEditorDialog::setReadOnly(bool readOnly) { multiEditor->setReadOnly(readOnly); } + +void MultiEditorDialog::enableFk(Db* db, SqlQueryModelColumn* column) +{ + multiEditor->enableFk(db, column); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordialog.h b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordialog.h index ffbbd9c..57fb4f2 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditordialog.h @@ -20,6 +20,7 @@ class GUI_API_EXPORT MultiEditorDialog : public QDialog void setDataType(const DataType& dataType); void setReadOnly(bool readOnly); + void enableFk(Db* db, SqlQueryModelColumn* column); private: MultiEditor* multiEditor = nullptr; diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorfk.cpp b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorfk.cpp new file mode 100644 index 0000000..e0ec5e1 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorfk.cpp @@ -0,0 +1,49 @@ +#include "multieditorfk.h" +#include "datagrid/fkcombobox.h" +#include +#include + +MultiEditorFk::MultiEditorFk(QWidget* parent) + : MultiEditorWidget(parent) +{ + setLayout(new QVBoxLayout()); + comboBox = new FkComboBox(this); + comboBox->setEditable(false); + layout()->addWidget(comboBox); + + connect(comboBox, SIGNAL(valueModified()), this, SIGNAL(valueModified())); + + setFocusProxy(comboBox); +} + +void MultiEditorFk::initFkCombo(Db* db, SqlQueryModelColumn* columnModel) +{ + comboBox->init(db, columnModel); +} + +void MultiEditorFk::setValue(const QVariant& value) +{ + comboBox->setValue(value); +} + +QVariant MultiEditorFk::getValue() +{ + return comboBox->getValue(); +} + +void MultiEditorFk::setReadOnly(bool value) +{ + comboBox->setDisabled(value); +} + +void MultiEditorFk::focusThisWidget() +{ + comboBox->setFocus(); +} + +QList MultiEditorFk::getNoScrollWidgets() +{ + QList list; + list << comboBox; + return list; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorfk.h b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorfk.h new file mode 100644 index 0000000..5854ffc --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorfk.h @@ -0,0 +1,29 @@ +#ifndef MULTIEDITORFK_H +#define MULTIEDITORFK_H + +#include "multieditorwidget.h" + +class Db; +class SqlQueryModelColumn; +class FkComboBox; + +class MultiEditorFk : public MultiEditorWidget +{ + Q_OBJECT + + public: + explicit MultiEditorFk(QWidget *parent = nullptr); + + void initFkCombo(Db* db, SqlQueryModelColumn* columnModel); + void setValue(const QVariant& value); + QVariant getValue(); + void setReadOnly(bool value); + void focusThisWidget(); + + QList getNoScrollWidgets(); + + private: + FkComboBox* comboBox = nullptr; +}; + +#endif // MULTIEDITORFK_H diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorhex.cpp b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorhex.cpp index a959e53..3f92330 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorhex.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditorhex.cpp @@ -65,6 +65,7 @@ int MultiEditorHexPlugin::getPriority(const DataType& dataType) case DataType::BLOB: return 1; case DataType::BIGINT: + case DataType::ANY: case DataType::DECIMAL: case DataType::DOUBLE: case DataType::INTEGER: diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditornumeric.cpp b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditornumeric.cpp index 6b1dd09..322c94f 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditornumeric.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditornumeric.cpp @@ -70,6 +70,7 @@ bool MultiEditorNumericPlugin::validFor(const DataType& dataType) case DataType::DATE: case DataType::DATETIME: case DataType::TIME: + case DataType::ANY: case DataType::unknown: break; } @@ -98,6 +99,7 @@ int MultiEditorNumericPlugin::getPriority(const DataType& dataType) case DataType::DATE: case DataType::DATETIME: case DataType::TIME: + case DataType::ANY: case DataType::unknown: break; } diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortext.cpp b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortext.cpp index 0d963a5..9222f40 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortext.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortext.cpp @@ -1,5 +1,6 @@ #include "multieditortext.h" #include "common/unused.h" +#include "iconmanager.h" #include #include #include @@ -173,6 +174,7 @@ int MultiEditorTextPlugin::getPriority(const DataType& dataType) case DataType::TEXT: case DataType::CHAR: case DataType::VARCHAR: + case DataType::ANY: case DataType::unknown: break; } diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortext.h b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortext.h index 59d2a44..369dada 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortext.h +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortext.h @@ -20,8 +20,7 @@ CFG_KEY_LIST(MultiEditorText, QObject::tr("Cell text value editor"), class GUI_API_EXPORT MultiEditorText : public MultiEditorWidget, public ExtActionContainer { - Q_OBJECT - Q_ENUMS(Action) + Q_OBJECT public: enum Action @@ -34,6 +33,7 @@ class GUI_API_EXPORT MultiEditorText : public MultiEditorWidget, public ExtActio UNDO, REDO }; + Q_ENUM(Action) enum ToolBar { diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortime.cpp b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortime.cpp index 35800ce..1acd606 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortime.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditortime.cpp @@ -48,6 +48,7 @@ bool MultiEditorTimePlugin::validFor(const DataType& dataType) case DataType::VARCHAR: case DataType::DATE: case DataType::DATETIME: + case DataType::ANY: case DataType::unknown: break; case DataType::TIME: @@ -76,6 +77,7 @@ int MultiEditorTimePlugin::getPriority(const DataType& dataType) case DataType::VARCHAR: case DataType::DATE: case DataType::DATETIME: + case DataType::ANY: case DataType::unknown: break; case DataType::TIME: diff --git a/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.cpp b/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.cpp index 58e4315..8304f9c 100644 --- a/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.cpp @@ -1,196 +1,219 @@ #include "qtscriptsyntaxhighlighter.h" #include "style.h" +#include "uiconfig.h" +#include "common/global.h" #include #include #include +#include +#include + +/** + * @brief The JavaScript highlighter + * + * This class is mostly copied from Ofi Labs X2 project. It has been slightly modified for SQLiteStudio needs. + * See the source code for the full license disclaimer. + */ +class GUI_API_EXPORT JavaScriptSyntaxHighlighter : public QSyntaxHighlighter +{ + public: + explicit JavaScriptSyntaxHighlighter(QTextDocument *parent, const QHash* formats); + + protected: + void highlightBlock(const QString &text); + + private: + void highlightTemplateExpressions(const QString &text, int strStart, int strEnd); + + QSet keywords; + QSet knownIds; + const QHash* formats = nullptr; +}; -JavaScriptSyntaxHighlighter::JavaScriptSyntaxHighlighter(QTextDocument *parent) - : QSyntaxHighlighter(parent) - , m_markCaseSensitivity(Qt::CaseInsensitive) +JavaScriptSyntaxHighlighter::JavaScriptSyntaxHighlighter(QTextDocument *parent, const QHash* formats) + : QSyntaxHighlighter(parent), formats(formats) { // https://developer.mozilla.org/en/JavaScript/Reference/Reserved_Words - m_keywords << "break"; - m_keywords << "case"; - m_keywords << "catch"; - m_keywords << "continue"; - m_keywords << "default"; - m_keywords << "delete"; - m_keywords << "do"; - m_keywords << "else"; - m_keywords << "finally"; - m_keywords << "for"; - m_keywords << "function"; - m_keywords << "if"; - m_keywords << "in"; - m_keywords << "instanceof"; - m_keywords << "new"; - m_keywords << "return"; - m_keywords << "switch"; - m_keywords << "this"; - m_keywords << "throw"; - m_keywords << "try"; - m_keywords << "typeof"; - m_keywords << "var"; - m_keywords << "void"; - m_keywords << "while"; - m_keywords << "with"; - - m_keywords << "true"; - m_keywords << "false"; - m_keywords << "null"; + keywords << "break"; + keywords << "case"; + keywords << "catch"; + keywords << "continue"; + keywords << "default"; + keywords << "delete"; + keywords << "do"; + keywords << "else"; + keywords << "finally"; + keywords << "for"; + keywords << "function"; + keywords << "if"; + keywords << "in"; + keywords << "instanceof"; + keywords << "new"; + keywords << "return"; + keywords << "switch"; + keywords << "this"; + keywords << "throw"; + keywords << "try"; + keywords << "typeof"; + keywords << "var"; + keywords << "void"; + keywords << "while"; + keywords << "with"; + + keywords << "true"; + keywords << "false"; + keywords << "null"; // built-in and other popular objects + properties - m_knownIds << "Object"; - m_knownIds << "prototype"; - m_knownIds << "create"; - m_knownIds << "defineProperty"; - m_knownIds << "defineProperties"; - m_knownIds << "getOwnPropertyDescriptor"; - m_knownIds << "keys"; - m_knownIds << "getOwnPropertyNames"; - m_knownIds << "constructor"; - m_knownIds << "__parent__"; - m_knownIds << "__proto__"; - m_knownIds << "__defineGetter__"; - m_knownIds << "__defineSetter__"; - m_knownIds << "eval"; - m_knownIds << "hasOwnProperty"; - m_knownIds << "isPrototypeOf"; - m_knownIds << "__lookupGetter__"; - m_knownIds << "__lookupSetter__"; - m_knownIds << "__noSuchMethod__"; - m_knownIds << "propertyIsEnumerable"; - m_knownIds << "toSource"; - m_knownIds << "toLocaleString"; - m_knownIds << "toString"; - m_knownIds << "unwatch"; - m_knownIds << "valueOf"; - m_knownIds << "watch"; - - m_knownIds << "Function"; - m_knownIds << "arguments"; - m_knownIds << "arity"; - m_knownIds << "caller"; - m_knownIds << "constructor"; - m_knownIds << "length"; - m_knownIds << "name"; - m_knownIds << "apply"; - m_knownIds << "bind"; - m_knownIds << "call"; - - m_knownIds << "String"; - m_knownIds << "fromCharCode"; - m_knownIds << "length"; - m_knownIds << "charAt"; - m_knownIds << "charCodeAt"; - m_knownIds << "concat"; - m_knownIds << "indexOf"; - m_knownIds << "lastIndexOf"; - m_knownIds << "localCompare"; - m_knownIds << "match"; - m_knownIds << "quote"; - m_knownIds << "replace"; - m_knownIds << "search"; - m_knownIds << "slice"; - m_knownIds << "split"; - m_knownIds << "substr"; - m_knownIds << "substring"; - m_knownIds << "toLocaleLowerCase"; - m_knownIds << "toLocaleUpperCase"; - m_knownIds << "toLowerCase"; - m_knownIds << "toUpperCase"; - m_knownIds << "trim"; - m_knownIds << "trimLeft"; - m_knownIds << "trimRight"; - - m_knownIds << "Array"; - m_knownIds << "isArray"; - m_knownIds << "index"; - m_knownIds << "input"; - m_knownIds << "pop"; - m_knownIds << "push"; - m_knownIds << "reverse"; - m_knownIds << "shift"; - m_knownIds << "sort"; - m_knownIds << "splice"; - m_knownIds << "unshift"; - m_knownIds << "concat"; - m_knownIds << "join"; - m_knownIds << "filter"; - m_knownIds << "forEach"; - m_knownIds << "every"; - m_knownIds << "map"; - m_knownIds << "some"; - m_knownIds << "reduce"; - m_knownIds << "reduceRight"; - - m_knownIds << "RegExp"; - m_knownIds << "global"; - m_knownIds << "ignoreCase"; - m_knownIds << "lastIndex"; - m_knownIds << "multiline"; - m_knownIds << "source"; - m_knownIds << "exec"; - m_knownIds << "test"; - - m_knownIds << "JSON"; - m_knownIds << "parse"; - m_knownIds << "stringify"; - - m_knownIds << "decodeURI"; - m_knownIds << "decodeURIComponent"; - m_knownIds << "encodeURI"; - m_knownIds << "encodeURIComponent"; - m_knownIds << "eval"; - m_knownIds << "isFinite"; - m_knownIds << "isNaN"; - m_knownIds << "parseFloat"; - m_knownIds << "parseInt"; - m_knownIds << "Infinity"; - m_knownIds << "NaN"; - m_knownIds << "undefined"; - - m_knownIds << "Math"; - m_knownIds << "E"; - m_knownIds << "LN2"; - m_knownIds << "LN10"; - m_knownIds << "LOG2E"; - m_knownIds << "LOG10E"; - m_knownIds << "PI"; - m_knownIds << "SQRT1_2"; - m_knownIds << "SQRT2"; - m_knownIds << "abs"; - m_knownIds << "acos"; - m_knownIds << "asin"; - m_knownIds << "atan"; - m_knownIds << "atan2"; - m_knownIds << "ceil"; - m_knownIds << "cos"; - m_knownIds << "exp"; - m_knownIds << "floor"; - m_knownIds << "log"; - m_knownIds << "max"; - m_knownIds << "min"; - m_knownIds << "pow"; - m_knownIds << "random"; - m_knownIds << "round"; - m_knownIds << "sin"; - m_knownIds << "sqrt"; - m_knownIds << "tan"; - - m_knownIds << "document"; - m_knownIds << "window"; - m_knownIds << "navigator"; - m_knownIds << "userAgent"; - - keywordsFormat.setFontWeight(QFont::Bold); - commentFormat.setFontItalic(true); + knownIds << "Object"; + knownIds << "prototype"; + knownIds << "create"; + knownIds << "defineProperty"; + knownIds << "defineProperties"; + knownIds << "getOwnPropertyDescriptor"; + knownIds << "keys"; + knownIds << "getOwnPropertyNames"; + knownIds << "constructor"; + knownIds << "__parent__"; + knownIds << "__proto__"; + knownIds << "__defineGetter__"; + knownIds << "__defineSetter__"; + knownIds << "eval"; + knownIds << "hasOwnProperty"; + knownIds << "isPrototypeOf"; + knownIds << "__lookupGetter__"; + knownIds << "__lookupSetter__"; + knownIds << "__noSuchMethod__"; + knownIds << "propertyIsEnumerable"; + knownIds << "toSource"; + knownIds << "toLocaleString"; + knownIds << "toString"; + knownIds << "unwatch"; + knownIds << "valueOf"; + knownIds << "watch"; + + knownIds << "Function"; + knownIds << "arguments"; + knownIds << "arity"; + knownIds << "caller"; + knownIds << "constructor"; + knownIds << "length"; + knownIds << "name"; + knownIds << "apply"; + knownIds << "bind"; + knownIds << "call"; + + knownIds << "String"; + knownIds << "fromCharCode"; + knownIds << "length"; + knownIds << "charAt"; + knownIds << "charCodeAt"; + knownIds << "concat"; + knownIds << "indexOf"; + knownIds << "lastIndexOf"; + knownIds << "localCompare"; + knownIds << "match"; + knownIds << "quote"; + knownIds << "replace"; + knownIds << "search"; + knownIds << "slice"; + knownIds << "split"; + knownIds << "substr"; + knownIds << "substring"; + knownIds << "toLocaleLowerCase"; + knownIds << "toLocaleUpperCase"; + knownIds << "toLowerCase"; + knownIds << "toUpperCase"; + knownIds << "trim"; + knownIds << "trimLeft"; + knownIds << "trimRight"; + + knownIds << "Array"; + knownIds << "isArray"; + knownIds << "index"; + knownIds << "input"; + knownIds << "pop"; + knownIds << "push"; + knownIds << "reverse"; + knownIds << "shift"; + knownIds << "sort"; + knownIds << "splice"; + knownIds << "unshift"; + knownIds << "concat"; + knownIds << "join"; + knownIds << "filter"; + knownIds << "forEach"; + knownIds << "every"; + knownIds << "map"; + knownIds << "some"; + knownIds << "reduce"; + knownIds << "reduceRight"; + + knownIds << "RegExp"; + knownIds << "global"; + knownIds << "ignoreCase"; + knownIds << "lastIndex"; + knownIds << "multiline"; + knownIds << "source"; + knownIds << "exec"; + knownIds << "test"; + + knownIds << "JSON"; + knownIds << "parse"; + knownIds << "stringify"; + + knownIds << "decodeURI"; + knownIds << "decodeURIComponent"; + knownIds << "encodeURI"; + knownIds << "encodeURIComponent"; + knownIds << "eval"; + knownIds << "isFinite"; + knownIds << "isNaN"; + knownIds << "parseFloat"; + knownIds << "parseInt"; + knownIds << "Infinity"; + knownIds << "NaN"; + knownIds << "undefined"; + + knownIds << "Math"; + knownIds << "E"; + knownIds << "LN2"; + knownIds << "LN10"; + knownIds << "LOG2E"; + knownIds << "LOG10E"; + knownIds << "PI"; + knownIds << "SQRT1_2"; + knownIds << "SQRT2"; + knownIds << "abs"; + knownIds << "acos"; + knownIds << "asin"; + knownIds << "atan"; + knownIds << "atan2"; + knownIds << "ceil"; + knownIds << "cos"; + knownIds << "exp"; + knownIds << "floor"; + knownIds << "log"; + knownIds << "max"; + knownIds << "min"; + knownIds << "pow"; + knownIds << "random"; + knownIds << "round"; + knownIds << "sin"; + knownIds << "sqrt"; + knownIds << "tan"; + + knownIds << "document"; + knownIds << "window"; + knownIds << "navigator"; + knownIds << "userAgent"; } void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) { // parsing state - enum { + enum + { Start = -1, Number = 1, Identifier = 2, @@ -199,167 +222,256 @@ void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) Regex = 5 }; - commentFormat.setForeground(QApplication::style()->standardPalette().dark()); - keywordsFormat.setForeground(QApplication::style()->standardPalette().windowText()); - keywordsFormat.setFontWeight(QFont::Bold); - normalFormat.setForeground(QApplication::style()->standardPalette().text()); - stringFormat.setForeground(STYLE->extendedPalette().editorString()); - int state = previousBlockState(); + setFormat(0, text.length(), formats->value(JavaScriptHighlighterPlugin::NORMAL)); int start = 0; int i = 0; - while (i <= text.length()) { + while (i <= text.length()) + { QChar ch = (i < text.length()) ? text.at(i) : QChar(); QChar next = (i < text.length() - 1) ? text.at(i + 1) : QChar(); - switch (state) { - - case Start: - start = i; - if (ch.isSpace()) { - ++i; - } else if (ch.isDigit()) { - ++i; - state = Number; - } else if (ch.isLetter() || ch == '_') { - ++i; - state = Identifier; - } else if (ch == '\'' || ch == '\"') { - ++i; - state = String; - } else if (ch == '/' && next == '*') { - ++i; - ++i; - state = Comment; - } else if (ch == '/' && next == '/') { - i = text.length(); - setFormat(start, text.length(), commentFormat); - } else if (ch == '/' && next != '*') { - ++i; - state = Regex; - } else { - if (!QString("(){}[]").contains(ch)) - setFormat(start, 1, normalFormat); - ++i; - state = Start; - } - break; + switch (state) + { + case Start: + start = i; + if (ch.isSpace()) + { + ++i; + } + else if (ch.isDigit()) + { + ++i; + state = Number; + } + else if (ch.isLetter() || ch == '_') + { + ++i; + state = Identifier; + } + else if (ch == '\'' || ch == '\"' || ch == '`') + { + ++i; + state = String; + } + else if (ch == '/' && next == '*') + { + ++i; + ++i; + state = Comment; + } + else if (ch == '/' && next == '/') + { + i = text.length(); + setFormat(start, text.length(), formats->value(JavaScriptHighlighterPlugin::COMMENT)); + } + else if (ch == '/' && next != '*') + { + ++i; + state = Regex; + } + else + { + if (!QString("(){}[]").contains(ch)) + setFormat(start, 1, formats->value(JavaScriptHighlighterPlugin::NORMAL)); + ++i; + state = Start; + } + break; - case Number: - if (ch.isSpace() || !ch.isDigit()) { - setFormat(start, i - start, normalFormat); - state = Start; - } else { - ++i; - } - break; + case Number: + if (ch.isSpace() || !ch.isDigit()) + { + setFormat(start, i - start, formats->value(JavaScriptHighlighterPlugin::NUMBER)); + state = Start; + } + else + ++i; - case Identifier: - if (ch.isSpace() || !(ch.isDigit() || ch.isLetter() || ch == '_')) { - QString token = text.mid(start, i - start).trimmed(); - if (m_keywords.contains(token)) - setFormat(start, i - start, keywordsFormat); - else if (m_knownIds.contains(token)) - setFormat(start, i - start, normalFormat); + break; - state = Start; - } else { - ++i; - } - break; + case Identifier: + if (ch.isSpace() || !(ch.isDigit() || ch.isLetter() || ch == '_')) + { + QString token = text.mid(start, i - start).trimmed(); + if (keywords.contains(token)) + setFormat(start, i - start, formats->value(JavaScriptHighlighterPlugin::KEYWORDS)); + else if (knownIds.contains(token)) + setFormat(start, i - start, formats->value(JavaScriptHighlighterPlugin::NORMAL)); - case String: - if (ch == text.at(start)) { - QChar prev = (i > 0) ? text.at(i - 1) : QChar(); - if (prev != '\\') { - ++i; - setFormat(start, i - start, stringFormat); state = Start; - } else { + } + else ++i; + + break; + + case String: + if (ch == text.at(start)) + { + QChar prev = (i > 0) ? text.at(i - 1) : QChar(); + if (prev != '\\') + { + ++i; + setFormat(start, i - start, formats->value(JavaScriptHighlighterPlugin::STRING)); + if (ch == '`') + highlightTemplateExpressions(text, start, i); + + state = Start; + } + else + ++i; } - } else { - ++i; - } - break; + else + ++i; - case Comment: - if (ch == '*' && next == '/') { - ++i; - ++i; - setFormat(start, i - start, commentFormat); - state = Start; - } else { - ++i; - } - break; + break; - case Regex: - if (ch == '/') { - QChar prev = (i > 0) ? text.at(i - 1) : QChar(); - if (prev != '\\') { + case Comment: + if (ch == '*' && next == '/') + { + ++i; ++i; - setFormat(start, i - start, normalFormat); + setFormat(start, i - start, formats->value(JavaScriptHighlighterPlugin::COMMENT)); state = Start; - } else { + } + else ++i; + + break; + + case Regex: + if (ch == '/') + { + QChar prev = (i > 0) ? text.at(i - 1) : QChar(); + if (prev != '\\') + { + ++i; + setFormat(start, i - start, formats->value(JavaScriptHighlighterPlugin::EXPRESSION)); + state = Start; + } + else + ++i; } - } else { - ++i; - } - break; + else + ++i; - default: - state = Start; - break; + break; + + default: + state = Start; + break; } } if (state == Comment) - setFormat(start, text.length(), commentFormat); + setFormat(start, text.length(), formats->value(JavaScriptHighlighterPlugin::COMMENT)); else state = Start; - if (!m_markString.isEmpty()) { - int pos = 0; - int len = m_markString.length(); - QTextCharFormat markerFormat; - markerFormat.setBackground(QApplication::style()->standardPalette().alternateBase()); - markerFormat.setForeground(QApplication::style()->standardPalette().text()); - for (;;) { - pos = text.indexOf(m_markString, pos, m_markCaseSensitivity); - if (pos < 0) - break; - setFormat(pos, len, markerFormat); - ++pos; - } - } - setCurrentBlockState(state); } -void JavaScriptSyntaxHighlighter::mark(const QString &str, Qt::CaseSensitivity caseSensitivity) +void JavaScriptSyntaxHighlighter::highlightTemplateExpressions(const QString& text, int strStart, int strEnd) { - m_markString = str; - m_markCaseSensitivity = caseSensitivity; - rehighlight(); + bool expr = false; + int i = strStart; + int start = i; + while (i <= strEnd) + { + QChar ch = text.at(i); + QChar next = (i < strEnd - 1) ? text.at(i + 1) : QChar(); + if (expr) + { + if (ch == '}') + { + QChar prev = (i > 0) ? text.at(i - 1) : QChar(); + if (prev != '\\') + { + ++i; + setFormat(start, i - start, formats->value(JavaScriptHighlighterPlugin::EXPRESSION)); + expr = false; + } + else + ++i; + } + else + ++i; + + } + else if (ch == '$' && next == '{') + { + start = i; + expr = true; + ++i; + ++i; + } + else + { + ++i; + } + } } +bool JavaScriptHighlighterPlugin::init() +{ + refreshFormats(); + return true; +} QString JavaScriptHighlighterPlugin::getLanguageName() const { - return QStringLiteral("QtScript"); + return QStringLiteral("JavaScript"); } QSyntaxHighlighter* JavaScriptHighlighterPlugin::createSyntaxHighlighter(QWidget* textEdit) const { QPlainTextEdit* plainEdit = dynamic_cast(textEdit); if (plainEdit) - return new JavaScriptSyntaxHighlighter(plainEdit->document()); + return new JavaScriptSyntaxHighlighter(plainEdit->document(), &formats); QTextEdit* edit = dynamic_cast(textEdit); if (edit) - return new JavaScriptSyntaxHighlighter(edit->document()); + return new JavaScriptSyntaxHighlighter(edit->document(), &formats); return nullptr; } + +void JavaScriptHighlighterPlugin::refreshFormats() +{ + QTextCharFormat format; + + format.setForeground(Cfg::getSyntaxForeground()); + formats[NORMAL] = format; + + format.setForeground(Cfg::getSyntaxNumberFg()); + formats[NUMBER] = format; + + format.setForeground(Cfg::getSyntaxKeywordFg()); + format.setFontWeight(QFont::Bold); + formats[KEYWORDS] = format; + + format.setFontItalic(true); + format.setFontWeight(QFont::Normal); + format.setForeground(Cfg::getSyntaxCommentFg()); + formats[COMMENT] = format; + + format.setFontItalic(false); + format.setForeground(Cfg::getSyntaxStringFg()); + formats[STRING] = format; + + format.setForeground(Cfg::getSyntaxNumberFg()); + formats[EXPRESSION] = format; +} + +QString JavaScriptHighlighterPlugin::previewSampleCode() const +{ + static_qstring(code, + "function myFunction() { // Declare a function\n" + " return \"Hello World!\";\n" + "}\n" + "\n" + "myFunction(); // Call the function" + ); + return code; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.h b/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.h index 3d701de..3d5e6bd 100644 --- a/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.h +++ b/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.h @@ -33,46 +33,37 @@ #include "guiSQLiteStudio_global.h" #include "plugins/builtinplugin.h" #include "syntaxhighlighterplugin.h" -#include -#include -/** - * @brief The JavaScript (also QtScript) highlighter - * - * This class is mostly copied from Ofi Labs X2 project. It has been slightly modified for SQLiteStudio needs. - * See the source code for the full license disclaimer. - */ -class GUI_API_EXPORT JavaScriptSyntaxHighlighter : public QSyntaxHighlighter -{ - public: - explicit JavaScriptSyntaxHighlighter(QTextDocument *parent = 0); - void mark(const QString &str, Qt::CaseSensitivity caseSensitivity); - - protected: - void highlightBlock(const QString &text); - - private: - QSet m_keywords; - QSet m_knownIds; - QString m_markString; - Qt::CaseSensitivity m_markCaseSensitivity; - QTextCharFormat normalFormat; - QTextCharFormat keywordsFormat; - QTextCharFormat commentFormat; - QTextCharFormat stringFormat; -}; +#include class GUI_API_EXPORT JavaScriptHighlighterPlugin : public BuiltInPlugin, public SyntaxHighlighterPlugin { Q_OBJECT - SQLITESTUDIO_PLUGIN_TITLE("QtScript highlighter") - SQLITESTUDIO_PLUGIN_DESC("QtScript (JavaScript) syntax highlighter.") - SQLITESTUDIO_PLUGIN_VERSION(10000) + SQLITESTUDIO_PLUGIN_TITLE("JavaScript highlighter") + SQLITESTUDIO_PLUGIN_DESC("JavaScript syntax highlighter.") + SQLITESTUDIO_PLUGIN_VERSION(10200) SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl") public: + enum State + { + NORMAL, + NUMBER, + KEYWORDS, + COMMENT, + STRING, + EXPRESSION + }; + + bool init(); QString getLanguageName() const; QSyntaxHighlighter* createSyntaxHighlighter(QWidget* textEdit) const; + void refreshFormats(); + QString previewSampleCode() const; + + private: + QHash formats; + }; #endif // JAVASCRIPTSYNTAXHIGHLIGHTER_H diff --git a/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.cpp b/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.cpp index 7e5c2ec..ac93028 100644 --- a/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.cpp @@ -204,8 +204,45 @@ bool SearchTextLocator::replaceAndFind() void SearchTextLocator::replaceAll() { - while (replaceAndFind()) - continue; + QString origContents = document->toPlainText(); + QString contents = origContents; + Qt::CaseSensitivity cs = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; + int replLen = replaceString.length(); + int diff = 0; + if (regularExpression) + { + QRegExp re(lookupString, cs); + contents.replace(re, replaceString); + + int pos = 0; + while ((pos = re.indexIn(origContents, pos)) != -1 && pos < startPosition) + { + int len = re.matchedLength(); + pos += len; + diff += (replLen - len); + } + } + else + { + contents.replace(lookupString, replaceString, cs); + + int len = lookupString.length(); + int singleDiff = (replLen - len); + int pos = 0; + while ((pos = origContents.indexOf(lookupString, pos, cs)) != -1 && pos < startPosition) + { + pos += len; + diff += singleDiff; + } + } + int newPos = startPosition + diff; // calculated before replacing contents to use original startPosition + + QTextCursor cursor(document); + cursor.setPosition(0); + cursor.setPosition(origContents.length(), QTextCursor::KeepAnchor); + cursor.insertText(contents); + + emit newCursorPositionAfterAllReplaced(newPos); } void SearchTextLocator::cursorMoved() diff --git a/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.h b/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.h index 534da51..687821b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.h +++ b/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.h @@ -71,6 +71,7 @@ class GUI_API_EXPORT SearchTextLocator : public QObject void found(int start, int end); void reachedEnd(); void replaceAvailable(bool available); + void newCursorPositionAfterAllReplaced(int newPos); }; #endif // SEARCHTEXTLOCATOR_H diff --git a/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp b/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp index 50189d4..d1f1a47 100644 --- a/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp @@ -1,8 +1,10 @@ #include "sqleditor.h" -#include "log.h" +#include "common/mouseshortcut.h" +#include "sqlitesyntaxhighlighter.h" +#include "db/db.h" #include "uiconfig.h" #include "uiutils.h" -#include "services/config.h" +#include "services/codesnippetmanager.h" #include "iconmanager.h" #include "completer/completerwindow.h" #include "completionhelper.h" @@ -16,12 +18,13 @@ #include "dbobjectdialogs.h" #include "searchtextlocator.h" #include "services/codeformatter.h" -#include "sqlitestudio.h" #include "style.h" #include "dbtree/dbtreeitem.h" #include "dbtree/dbtree.h" #include "dbtree/dbtreemodel.h" +#include "dbtree/dbtreeview.h" #include "common/lazytrigger.h" +#include "common/extaction.h" #include #include #include @@ -36,6 +39,28 @@ CFG_KEYS_DEFINE(SqlEditor) +QHash SqlEditor::staticActions; +bool SqlEditor::wrapWords = false; + +void SqlEditor::createStaticActions() +{ + staticActions[WORD_WRAP] = new ExtAction(tr("Wrap words", "sql editor"), MainWindow::getInstance()); + + staticActions[WORD_WRAP]->setCheckable(true); + staticActions[WORD_WRAP]->setChecked(wrapWords); + connect(staticActions[WORD_WRAP], &QAction::toggled, [=](bool value) + { + wrapWords = value; + CFG_UI.General.SqlEditorWrapWords.set(value); + }); +} + +void SqlEditor::staticInit() +{ + wrapWords = CFG_UI.General.SqlEditorWrapWords.get(); + createStaticActions(); +} + SqlEditor::SqlEditor(QWidget *parent) : QPlainTextEdit(parent) { @@ -44,8 +69,8 @@ SqlEditor::SqlEditor(QWidget *parent) : SqlEditor::~SqlEditor() { - if (objectsInNamedDbFuture.isRunning()) - objectsInNamedDbFuture.waitForFinished(); + if (objectsInNamedDbWatcher->isRunning()) + objectsInNamedDbWatcher->waitForFinished(); if (queryParser) { @@ -57,20 +82,25 @@ SqlEditor::~SqlEditor() void SqlEditor::init() { highlighter = new SqliteSyntaxHighlighter(document()); - setFont(CFG_UI.Fonts.SqlEditor.get()); initActions(); setupMenu(); + objectsInNamedDbWatcher = new QFutureWatcher>(this); + connect(objectsInNamedDbWatcher, SIGNAL(finished()), this, SLOT(scheduleQueryParserForSchemaRefresh())); + textLocator = new SearchTextLocator(document(), this); connect(textLocator, SIGNAL(found(int,int)), this, SLOT(found(int,int))); connect(textLocator, SIGNAL(reachedEnd()), this, SLOT(reachedEnd())); + connect(textLocator, SIGNAL(newCursorPositionAfterAllReplaced(int)), this, SLOT(moveCursorTo(int))); lineNumberArea = new LineNumberArea(this); + changeFont(CFG_UI.Fonts.SqlEditor.get()); connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth())); connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); connect(this, SIGNAL(textChanged()), this, SLOT(checkContentSize())); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(cursorMoved())); + MouseShortcut::forWheel(Qt::ControlModifier, this, SLOT(fontSizeChangeRequested(int)), viewport()); updateLineNumberAreaWidth(); highlightCurrentCursorContext(); @@ -97,6 +127,7 @@ void SqlEditor::init() connect(this, &QWidget::customContextMenuRequested, this, &SqlEditor::customContextMenuRequested); connect(CFG_UI.Fonts.SqlEditor, SIGNAL(changed(QVariant)), this, SLOT(changeFont(QVariant))); connect(CFG, SIGNAL(massSaveCommitted()), this, SLOT(configModified())); + connect(STYLE, SIGNAL(paletteChanged()), this, SLOT(colorsConfigChanged())); } void SqlEditor::removeErrorMarkers() @@ -143,6 +174,8 @@ void SqlEditor::createActions() createAction(FIND_PREV, tr("Find previous", "sql editor"), this, SLOT(findPrevious()), this); createAction(REPLACE, ICONS.SEARCH_AND_REPLACE, tr("Replace", "sql editor"), this, SLOT(replace()), this); createAction(TOGGLE_COMMENT, tr("Toggle comment", "sql editor"), this, SLOT(toggleComment()), this); + createAction(INCR_FONT_SIZE, tr("Increase font size", "sql editor"), this, SLOT(incrFontSize()), this); + createAction(DECR_FONT_SIZE, tr("Decrease font size", "sql editor"), this, SLOT(decrFontSize()), this); actionMap[CUT]->setEnabled(false); actionMap[COPY]->setEnabled(false); @@ -153,12 +186,14 @@ void SqlEditor::createActions() connect(this, &QPlainTextEdit::undoAvailable, this, &SqlEditor::updateUndoAction); connect(this, &QPlainTextEdit::redoAvailable, this, &SqlEditor::updateRedoAction); connect(this, &QPlainTextEdit::copyAvailable, this, &SqlEditor::updateCopyAction); + + connect(CFG_UI.General.SqlEditorWrapWords, SIGNAL(changed(QVariant)), this, SLOT(wordWrappingChanged(QVariant))); } void SqlEditor::setupDefShortcuts() { setShortcutContext({CUT, COPY, PASTE, DELETE, SELECT_ALL, UNDO, REDO, COMPLETE, FORMAT_SQL, SAVE_SQL_FILE, OPEN_SQL_FILE, - DELETE_LINE}, Qt::WidgetWithChildrenShortcut); + DELETE_LINE, INCR_FONT_SIZE, DECR_FONT_SIZE}, Qt::WidgetWithChildrenShortcut); BIND_SHORTCUTS(SqlEditor, Action); } @@ -167,6 +202,7 @@ void SqlEditor::setupMenu() { contextMenu = new QMenu(this); contextMenu->addAction(actionMap[FORMAT_SQL]); + contextMenu->addAction(staticActions[WORD_WRAP]); contextMenu->addSeparator(); contextMenu->addAction(actionMap[SAVE_SQL_FILE]); contextMenu->addAction(actionMap[OPEN_SQL_FILE]); @@ -194,7 +230,7 @@ void SqlEditor::setDb(Db* value) { db = value; refreshValidObjects(); - scheduleQueryParser(true); + scheduleQueryParser(true, true); } void SqlEditor::setAutoCompletion(bool enabled) @@ -243,10 +279,13 @@ bool SqlEditor::handleValidObjectContextMenu(const QPoint& pos) void SqlEditor::saveToFile(const QString &fileName) { + if (!openSaveActionsEnabled) + return; + QFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - notifyError(tr("Could not open file '%1' for writing: %2").arg(fileName).arg(file.errorString())); + notifyError(tr("Could not open file '%1' for writing: %2").arg(fileName, file.errorString())); return; } @@ -271,7 +310,34 @@ void SqlEditor::toggleLineCommentForLine(const QTextBlock& block) } else cur.insertText("--"); +} +bool SqlEditor::getAlwaysEnforceErrorsChecking() const +{ + return alwaysEnforceErrorsChecking; +} + +void SqlEditor::setAlwaysEnforceErrorsChecking(bool newAlwaysEnforceErrorsChecking) +{ + alwaysEnforceErrorsChecking = newAlwaysEnforceErrorsChecking; +} + +bool SqlEditor::getHighlightingSyntax() const +{ + return highlightingSyntax; +} + +void SqlEditor::setOpenSaveActionsEnabled(bool value) +{ + openSaveActionsEnabled = value; + if (value) + { + noConfigShortcutActions.remove(SAVE_SQL_FILE); + noConfigShortcutActions.remove(SAVE_AS_SQL_FILE); + noConfigShortcutActions.remove(OPEN_SQL_FILE); + } + else + noConfigShortcutActions << SAVE_SQL_FILE << SAVE_AS_SQL_FILE << OPEN_SQL_FILE; } void SqlEditor::updateUndoAction(bool enabled) @@ -487,6 +553,12 @@ void SqlEditor::updateCompleterPosition() void SqlEditor::completeSelected() { + if (completer->getMode() == CompleterWindow::SNIPPETS) + { + insertPlainText(CODESNIPPETS->getCodeByName(completer->getSnippetName())); + return; + } + deletePreviousChars(completer->getNumberOfCharsToRemove()); ExpectedTokenPtr token = completer->getSelected(); @@ -505,7 +577,7 @@ void SqlEditor::completeSelected() void SqlEditor::checkForAutoCompletion() { - if (!db || !autoCompletion || deletionKeyPressed || !richFeaturesEnabled) + if (!db || !autoCompletion || deletionKeyPressed || !richFeaturesEnabled || !CFG_CORE.CodeAssistant.AutoTrigger.get()) return; Lexer lexer; @@ -529,21 +601,25 @@ void SqlEditor::refreshValidObjects() if (!db || !db->isValid()) return; - objectsInNamedDbFuture = QtConcurrent::run([this]() + Db* dbClone = db->clone(); + QFuture> objectsInNamedDbFuture = QtConcurrent::run([dbClone]() { - QMutexLocker lock(&objectsInNamedDbMutex); - objectsInNamedDb.clear(); - - SchemaResolver resolver(db); + dbClone->openQuiet(); + QHash objectsByDbName; + SchemaResolver resolver(dbClone); QSet databases = resolver.getDatabases(); databases << "main"; QStringList objects; - for (const QString& dbName : databases) + for (const QString& dbName : qAsConst(databases)) { objects = resolver.getAllObjects(dbName); - objectsInNamedDb[dbName] << objects; + objectsByDbName[dbName] << objects; } + dbClone->closeQuiet(); + delete dbClone; + return objectsByDbName; }); + objectsInNamedDbWatcher->setFuture(objectsInNamedDbFuture); } void SqlEditor::setObjectLinks(bool enabled) @@ -551,7 +627,7 @@ void SqlEditor::setObjectLinks(bool enabled) objectLinksEnabled = enabled; setMouseTracking(enabled); highlighter->setObjectLinksEnabled(enabled); - highlighter->rehighlight(); + highlightSyntax(); if (enabled) handleValidObjectCursor(mapFromGlobal(QCursor::pos())); @@ -574,7 +650,7 @@ void SqlEditor::clearDbObjects() void SqlEditor::lineNumberAreaPaintEvent(QPaintEvent* event) { QPainter painter(lineNumberArea); - painter.fillRect(event->rect(), STYLE->extendedPalette().editorLineBase()); + painter.fillRect(event->rect(), STYLE->extendedPalette().editorLineNumberBase()); QTextBlock block = firstVisibleBlock(); int blockNumber = block.blockNumber(); int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); @@ -649,9 +725,32 @@ void SqlEditor::highlightParenthesis(QList& selection markMatchedParenthesis(thePar->position, matchedPar->position, selections); } -void SqlEditor::highlightCurrentCursorContext() +void SqlEditor::highlightCurrentQuery(QList& selections) +{ + QTextCursor cursor = textCursor(); + int curPos = cursor.position(); + QString contents = cursor.document()->toPlainText(); + QPair boundries = getQueryBoundriesForPosition(contents, curPos, true); + if (boundries.second < 0) + return; + + QTextEdit::ExtraSelection selection; + selection.format.setBackground(STYLE->extendedPalette().editorCurrentQueryBase()); + + cursor.setPosition(boundries.first); + cursor.setPosition(boundries.second, QTextCursor::KeepAnchor); + selection.cursor = cursor; + selections.append(selection); +} + +void SqlEditor::highlightCurrentCursorContext(bool delayedCall) { QList selections; + if (delayedCall) + highlightCurrentQuery(selections); + else if (currentQueryTimer) + currentQueryTimer->start(); + highlightCurrentLine(selections); highlightParenthesis(selections); setExtraSelections(selections); @@ -661,8 +760,8 @@ void SqlEditor::markMatchedParenthesis(int pos1, int pos2, QListstandardPalette().windowText()); - selection.format.setForeground(style()->standardPalette().window()); + selection.format.setBackground(Cfg::getSyntaxParenthesisBg()); + selection.format.setForeground(Cfg::getSyntaxParenthesisFg()); QTextCursor cursor = textCursor(); @@ -829,7 +928,7 @@ void SqlEditor::completerRightPressed() void SqlEditor::parseContents() { - if (!richFeaturesEnabled) + if (!richFeaturesEnabled && !alwaysEnforceErrorsChecking) return; QString sql = toPlainText(); @@ -841,13 +940,20 @@ void SqlEditor::parseContents() sql = virtualSqlExpression.arg(sql); } + queryParser->parse(sql); if (richFeaturesEnabled) - { - queryParser->parse(sql); checkForValidObjects(); - checkForSyntaxErrors(); - highlighter->rehighlight(); - } + + checkForSyntaxErrors(); + + if (richFeaturesEnabled) + highlightSyntax(); +} + +void SqlEditor::scheduleQueryParserForSchemaRefresh() +{ + objectsInNamedDb = objectsInNamedDbWatcher->future().result(); + scheduleQueryParser(true, true); } void SqlEditor::checkForSyntaxErrors() @@ -858,9 +964,9 @@ void SqlEditor::checkForSyntaxErrors() // Marking invalid tokens, like in "SELECT * from test] t" - the "]" token is invalid. // Such tokens don't cause parser to fail. - for (SqliteQueryPtr query : queryParser->getQueries()) + for (const SqliteQueryPtr& query : queryParser->getQueries()) { - for (TokenPtr token : query->tokens) + for (TokenPtr& token : query->tokens) { if (token->type == Token::INVALID) markErrorAt(token->start, token->end, true); @@ -886,16 +992,15 @@ void SqlEditor::checkForValidObjects() if (!db || !db->isValid()) return; - QMutexLocker lock(&objectsInNamedDbMutex); QList fullObjects; QString dbName; - for (SqliteQueryPtr query : queryParser->getQueries()) + for (const SqliteQueryPtr& query : queryParser->getQueries()) { fullObjects = query->getContextFullObjects(); - for (const SqliteStatement::FullObject& fullObj : fullObjects) + for (SqliteStatement::FullObject& fullObj : fullObjects) { dbName = fullObj.database ? stripObjName(fullObj.database->value) : "main"; - if (!objectsInNamedDb.contains(dbName)) + if (!objectsInNamedDb.contains(dbName, Qt::CaseInsensitive)) continue; if (fullObj.type == SqliteStatement::FullObject::DATABASE) @@ -905,7 +1010,7 @@ void SqlEditor::checkForValidObjects() continue; } - if (!objectsInNamedDb[dbName].contains(stripObjName(fullObj.object->value))) + if (!objectsInNamedDb[dbName].contains(stripObjName(fullObj.object->value), Qt::CaseInsensitive)) continue; // Valid object name @@ -914,7 +1019,7 @@ void SqlEditor::checkForValidObjects() } } -void SqlEditor::scheduleQueryParser(bool force) +void SqlEditor::scheduleQueryParser(bool force, bool skipCompleter) { if (!document()->isModified() && !force) return; @@ -923,7 +1028,8 @@ void SqlEditor::scheduleQueryParser(bool force) document()->setModified(false); queryParserTrigger->schedule(); - autoCompleteTrigger->schedule(); + if (!skipCompleter) + autoCompleteTrigger->schedule(); } int SqlEditor::sqlIndex(int idx) @@ -968,7 +1074,14 @@ QString SqlEditor::getSelectedText() const void SqlEditor::openObject(const QString& database, const QString& name) { DbObjectDialogs dialogs(db); - dialogs.editObject(database, name); + dialogs.editObject(DbObjectDialogs::Type::UNKNOWN, database, name); +} + +void SqlEditor::highlightSyntax() +{ + highlightingSyntax = true; + highlighter->rehighlight(); + highlightingSyntax = false; } void SqlEditor::updateLineNumberAreaWidth() @@ -987,7 +1100,6 @@ void SqlEditor::highlightCurrentLine(QList& selection if (!isReadOnly() && isEnabled()) { QTextEdit::ExtraSelection selection; - selection.format.setBackground(STYLE->extendedPalette().editorLineBase()); selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor = textCursor(); @@ -1059,6 +1171,9 @@ void SqlEditor::saveToFile() void SqlEditor::saveAsToFile() { + if (!openSaveActionsEnabled) + return; + QString dir = getFileDialogInitPath(); QString fName = QFileDialog::getSaveFileName(this, tr("Save to file"), dir); if (fName.isNull()) @@ -1071,6 +1186,9 @@ void SqlEditor::saveAsToFile() void SqlEditor::loadFromFile() { + if (!openSaveActionsEnabled) + return; + QString dir = getFileDialogInitPath(); QString filters = tr("SQL scripts (*.sql);;All files (*)"); QString fName = QFileDialog::getOpenFileName(this, tr("Open file"), dir, filters); @@ -1083,7 +1201,7 @@ void SqlEditor::loadFromFile() QString sql = readFileContents(fName, &err); if (sql.isNull() && !err.isNull()) { - notifyError(tr("Could not open file '%1' for reading: %2").arg(fName).arg(err)); + notifyError(tr("Could not open file '%1' for reading: %2").arg(fName, err)); return; } @@ -1293,13 +1411,14 @@ void SqlEditor::reachedEnd() void SqlEditor::changeFont(const QVariant& font) { - setFont(font.value()); + auto f = font.value(); + setFont(f); + lineNumberArea->setFont(f); } void SqlEditor::configModified() { - highlighter->rehighlight(); - highlightCurrentCursorContext(); + colorsConfigChanged(); } void SqlEditor::toggleComment() @@ -1387,6 +1506,51 @@ void SqlEditor::toggleComment() setTextCursor(cur); } +void SqlEditor::wordWrappingChanged(const QVariant& value) +{ + setLineWrapMode(value.toBool() ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap); +} + +void SqlEditor::currentCursorContextDelayedHighlight() +{ + highlightCurrentCursorContext(true); +} + +void SqlEditor::fontSizeChangeRequested(int delta) +{ + changeFontSize(delta >= 0 ? 1 : -1); +} + +void SqlEditor::incrFontSize() +{ + changeFontSize(1); +} + +void SqlEditor::decrFontSize() +{ + changeFontSize(-1); +} + +void SqlEditor::moveCursorTo(int pos) +{ + QTextCursor cur = textCursor(); + cur.setPosition(pos); + setTextCursor(cur); +} + +void SqlEditor::changeFontSize(int factor) +{ + auto f = font(); + f.setPointSize(f.pointSize() + factor); + CFG_UI.Fonts.SqlEditor.set(f); +} + +void SqlEditor::colorsConfigChanged() +{ + highlightSyntax(); + highlightCurrentCursorContext(); +} + void SqlEditor::keyPressEvent(QKeyEvent* e) { switch (e->key()) @@ -1563,6 +1727,21 @@ QToolBar* SqlEditor::getToolBar(int toolbar) const return nullptr; } +void SqlEditor::setCurrentQueryHighlighting(bool enabled) +{ + if (enabled && !currentQueryTimer) + { + currentQueryTimer = new QTimer(this); + currentQueryTimer->setInterval(300); + currentQueryTimer->setSingleShot(true); + connect(currentQueryTimer, SIGNAL(timeout()), this, SLOT(currentCursorContextDelayedHighlight())); + } + else if (!enabled && currentQueryTimer) + { + safe_delete(currentQueryTimer); + } +} + QString SqlEditor::getVirtualSqlExpression() const { return virtualSqlExpression; @@ -1600,7 +1779,7 @@ const SqlEditor::DbObject* SqlEditor::getValidObjectForPosition(const QPoint& po const SqlEditor::DbObject* SqlEditor::getValidObjectForPosition(int position, bool movedLeft) { - for (const DbObject& obj : validDbObjects) + for (DbObject& obj : validDbObjects) { if ((!movedLeft && position > obj.from && position-1 <= obj.to) || (movedLeft && position >= obj.from && position <= obj.to)) @@ -1641,3 +1820,16 @@ void SqlEditor::changeEvent(QEvent* e) QPlainTextEdit::changeEvent(e); } + +void SqlEditor::showEvent(QShowEvent* event) +{ + UNUSED(event); + setLineWrapMode(wrapWords ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap); +} + +void SqlEditor::dropEvent(QDropEvent* e) +{ + QPlainTextEdit::dropEvent(e); + if (MAINWINDOW->getDbTree()->getModel()->hasDbTreeItem(e->mimeData())) + e->ignore(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/sqleditor.h b/SQLiteStudio3/guiSQLiteStudio/sqleditor.h index ca79a1c..4af89c1 100644 --- a/SQLiteStudio3/guiSQLiteStudio/sqleditor.h +++ b/SQLiteStudio3/guiSQLiteStudio/sqleditor.h @@ -1,9 +1,9 @@ #ifndef SQLEDITOR_H #define SQLEDITOR_H +#include "common/strhash.h" #include "guiSQLiteStudio_global.h" #include "common/extactioncontainer.h" -#include "db/db.h" #include "sqlitesyntaxhighlighter.h" #include #include @@ -18,6 +18,8 @@ class SqlEditor; class SearchTextDialog; class SearchTextLocator; class LazyTrigger; +class Db; +class QTimer; #ifdef Q_OS_OSX # define COMPLETE_REQ_KEY Qt::META @@ -40,19 +42,20 @@ CFG_KEY_LIST(SqlEditor, QObject::tr("SQL editor input field"), CFG_KEY_ENTRY(FIND_PREV, QKeySequence::FindPrevious, QObject::tr("Find previous")) CFG_KEY_ENTRY(REPLACE, QKeySequence::Replace, QObject::tr("Replace in text")) CFG_KEY_ENTRY(DELETE_LINE, Qt::CTRL + Qt::Key_D, QObject::tr("Delete current line")) - CFG_KEY_ENTRY(COMPLETE, COMPLETE_REQ_KEY + Qt::Key_Space, QObject::tr("Request code assistant")) + CFG_KEY_ENTRY(COMPLETE, COMPLETE_REQ_KEY + Qt::Key_Space, QObject::tr("Request code assistant")) CFG_KEY_ENTRY(FORMAT_SQL, Qt::CTRL + Qt::Key_T, QObject::tr("Format contents")) CFG_KEY_ENTRY(MOVE_BLOCK_DOWN, Qt::ALT + Qt::Key_Down, QObject::tr("Move selected block of text one line down")) CFG_KEY_ENTRY(MOVE_BLOCK_UP, Qt::ALT + Qt::Key_Up, QObject::tr("Move selected block of text one line up")) CFG_KEY_ENTRY(COPY_BLOCK_DOWN, Qt::ALT + Qt::CTRL + Qt::Key_Down, QObject::tr("Copy selected block of text and paste it a line below")) CFG_KEY_ENTRY(COPY_BLOCK_UP, Qt::ALT + Qt::CTRL + Qt::Key_Up, QObject::tr("Copy selected block of text and paste it a line above")) CFG_KEY_ENTRY(TOGGLE_COMMENT, Qt::CTRL + Qt::Key_Slash, QObject::tr("Toggle comment")) + CFG_KEY_ENTRY(INCR_FONT_SIZE, Qt::CTRL + Qt::Key_Plus, QObject::tr("Increase font size", "sql editor")) + CFG_KEY_ENTRY(DECR_FONT_SIZE, Qt::CTRL + Qt::Key_Minus, QObject::tr("Decrease font size", "sql editor")) ) class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContainer { - Q_OBJECT - Q_ENUMS(Action) + Q_OBJECT public: enum Action @@ -78,13 +81,20 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine FIND_NEXT, FIND_PREV, REPLACE, - TOGGLE_COMMENT + TOGGLE_COMMENT, + WORD_WRAP, + INCR_FONT_SIZE, + DECR_FONT_SIZE }; + Q_ENUM(Action) enum ToolBar { }; + static void createStaticActions(); + static void staticInit(); + explicit SqlEditor(QWidget *parent = 0); ~SqlEditor(); @@ -102,9 +112,17 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine void saveSelection(); void restoreSelection(); QToolBar* getToolBar(int toolbar) const; - + void setCurrentQueryHighlighting(bool enabled); bool getVirtualSqlCompleteSemicolon() const; void setVirtualSqlCompleteSemicolon(bool value); + bool getHighlightingSyntax() const; + void setOpenSaveActionsEnabled(bool value); + + static QHash staticActions; + static bool wrapWords; + + bool getAlwaysEnforceErrorsChecking() const; + void setAlwaysEnforceErrorsChecking(bool newAlwaysEnforceErrorsChecking); protected: void setupDefShortcuts(); @@ -116,7 +134,9 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine void mouseMoveEvent(QMouseEvent* e); void mousePressEvent(QMouseEvent* e); void resizeEvent(QResizeEvent *e); - void changeEvent(QEvent*e); + void changeEvent(QEvent* e); + void showEvent(QShowEvent* event); + void dropEvent(QDropEvent* e); private: class LineNumberArea : public QWidget @@ -162,7 +182,6 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine */ void markErrorAt(int start, int end, bool limitedDamage = false); void deletePreviousChars(int length = 1); - void refreshValidObjects(); void checkForSyntaxErrors(); void checkForValidObjects(); void setObjectLinks(bool enabled); @@ -171,8 +190,9 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine void lineNumberAreaPaintEvent(QPaintEvent* event); int lineNumberAreaWidth(); void highlightParenthesis(QList& selections); + void highlightCurrentQuery(QList& selections); void highlightCurrentLine(QList& selections); - void highlightCurrentCursorContext(); + void highlightCurrentCursorContext(bool delayedCall = false); const TextBlockData::Parenthesis* matchParenthesis(QList parList, const TextBlockData::Parenthesis* thePar); void markMatchedParenthesis(int pos1, int pos2, QList& selections); void doBackspace(int repeats = 1); @@ -211,8 +231,7 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine bool deletionKeyPressed = false; LazyTrigger* queryParserTrigger = nullptr; Parser* queryParser = nullptr; - QHash objectsInNamedDb; - QMutex objectsInNamedDbMutex; + StrHash objectsInNamedDb; bool objectLinksEnabled = false; QList validDbObjects; QWidget* lineNumberArea = nullptr; @@ -224,6 +243,11 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine int storedSelectionStart = 0; int storedSelectionEnd = 0; bool richFeaturesEnabled = true; + bool alwaysEnforceErrorsChecking = false; + bool highlightingSyntax = true; + QBrush currentQueryBrush; + QTimer* currentQueryTimer = nullptr; + bool openSaveActionsEnabled = true; /** * @brief virtualSqlExpression @@ -245,12 +269,14 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine bool virtualSqlCompleteSemicolon = false; QString createTriggerTable; QString loadedFile; - QFuture objectsInNamedDbFuture; + QFutureWatcher>* objectsInNamedDbWatcher = nullptr; + void changeFontSize(int factor); static const int autoCompleterDelay = 300; static const int queryParserDelay = 500; private slots: + void highlightSyntax(); void customContextMenuRequested(const QPoint& pos); void updateUndoAction(bool enabled); void updateRedoAction(bool enabled); @@ -268,7 +294,8 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine void completerLeftPressed(); void completerRightPressed(); void parseContents(); - void scheduleQueryParser(bool force = false); + void scheduleQueryParserForSchemaRefresh(); + void scheduleQueryParser(bool force = false, bool skipCompleter = false); void updateLineNumberAreaWidth(); void updateLineNumberArea(const QRect&rect, int dy); void cursorMoved(); @@ -291,6 +318,16 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine void changeFont(const QVariant& font); void configModified(); void toggleComment(); + void wordWrappingChanged(const QVariant& value); + void currentCursorContextDelayedHighlight(); + void fontSizeChangeRequested(int delta); + void incrFontSize(); + void decrFontSize(); + void moveCursorTo(int pos); + + public slots: + void colorsConfigChanged(); + void refreshValidObjects(); signals: void errorsChecked(bool haveErrors); diff --git a/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.cpp b/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.cpp index 92679e2..8ebdb7b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.cpp @@ -3,80 +3,32 @@ #include "services/config.h" #include "style.h" #include "parser/keywords.h" +#include "uiconfig.h" +#include "services/pluginmanager.h" #include #include #include #include #include -SqliteSyntaxHighlighter::SqliteSyntaxHighlighter(QTextDocument *parent) : +SqliteSyntaxHighlighter::SqliteSyntaxHighlighter(QTextDocument *parent, const QHash* formats) : QSyntaxHighlighter(parent) { - setupFormats(); - setupMapping(); - setCurrentBlockState(regulartTextBlockState); - connect(CFG, SIGNAL(massSaveCommitted()), this, SLOT(setupFormats())); -} - -void SqliteSyntaxHighlighter::setFormat(SqliteSyntaxHighlighter::State state, QTextCharFormat format) -{ - formats[state] = format; + init(formats); } -QTextCharFormat SqliteSyntaxHighlighter::getFormat(SqliteSyntaxHighlighter::State state) const +SqliteSyntaxHighlighter::SqliteSyntaxHighlighter(QTextDocument* parent) : + QSyntaxHighlighter(parent) { - return formats[state]; + SqliteHighlighterPlugin* plugin = dynamic_cast(PLUGINS->getLoadedPlugin("SqliteHighlighterPlugin")); + init(plugin->getFormats()); } -void SqliteSyntaxHighlighter::setupFormats() +void SqliteSyntaxHighlighter::init(const QHash* formats) { - QTextCharFormat format; - - // Standard - format.setForeground(QApplication::style()->standardPalette().text()); - format.setFontWeight(QFont::Normal); - format.setFontItalic(false); - formats[State::STANDARD] = format; - - // Parenthesis - format.setForeground(QApplication::style()->standardPalette().text()); - formats[State::PARENTHESIS] = format; - - // String - format.setForeground(STYLE->extendedPalette().editorString()); - format.setFontWeight(QFont::Normal); - format.setFontItalic(true); - formats[State::STRING] = format; - - // Keyword - format.setForeground(QApplication::style()->standardPalette().windowText()); - format.setFontWeight(QFont::ExtraBold); - format.setFontItalic(false); - formats[State::KEYWORD] = format; - - // BindParam - format.setForeground(QApplication::style()->standardPalette().linkVisited()); - format.setFontWeight(QFont::Normal); - format.setFontItalic(false); - formats[State::BIND_PARAM] = format; - - // Blob - format.setForeground(QApplication::style()->standardPalette().text()); - format.setFontWeight(QFont::Normal); - format.setFontItalic(false); - formats[State::BLOB] = format; - - // Comment - format.setForeground(QApplication::style()->standardPalette().dark()); - format.setFontWeight(QFont::Normal); - format.setFontItalic(true); - formats[State::COMMENT] = format; - - // Number - format.setForeground(QApplication::style()->standardPalette().text()); - format.setFontWeight(QFont::Normal); - format.setFontItalic(false); - formats[State::NUMBER] = format; + this->formats = formats; + setupMapping(); + setCurrentBlockState(regulartTextBlockState); } void SqliteSyntaxHighlighter::setupMapping() @@ -127,7 +79,7 @@ void SqliteSyntaxHighlighter::highlightBlock(const QString &text) return; // Reset to default - QSyntaxHighlighter::setFormat(0, text.length(), formats[State::STANDARD]); + QSyntaxHighlighter::setFormat(0, text.length(), formats->value(State::STANDARD)); qint32 idxModifier = 0; QString statePrefix = ""; @@ -207,14 +159,14 @@ bool SqliteSyntaxHighlighter::handleToken(TokenPtr token, TokenPtr aheadToken, q ); bool fatalError = (error && !limitedDamage) || wasError; - QTextCharFormat format = formats[State::STANDARD]; + QTextCharFormat format = formats->value(State::STANDARD); // Applying valid object format. applyValidObjectFormat(format, valid, error, wasError); // Get format for token type (if any) if (tokenTypeMapping.contains(token->type)) - format = formats[tokenTypeMapping[token->type]]; + format = formats->value(tokenTypeMapping[token->type]); // Merge with error format (if this is an error). applyErrorFormat(format, error, wasError, token->type); @@ -283,7 +235,7 @@ bool SqliteSyntaxHighlighter::isError(int start, int lgt, bool* limitedDamage) { start += currentBlock().position(); int end = start + lgt - 1; - for (const Error& error : errors) + for (Error& error : errors) { if (error.from <= start && error.to >= end) { @@ -298,7 +250,7 @@ bool SqliteSyntaxHighlighter::isValid(int start, int lgt) { start += currentBlock().position(); int end = start + lgt - 1; - for (const DbObject& obj : dbObjects) + for (DbObject& obj : dbObjects) { if (obj.from <= start && obj.to >= end) return true; @@ -383,7 +335,7 @@ SqliteSyntaxHighlighter::DbObject::DbObject(int from, int to) : QList TextBlockData::parentheses() { QList list; - for (const TextBlockData::Parenthesis& par : parData) + for (TextBlockData::Parenthesis& par : parData) list << ∥ return list; @@ -399,7 +351,7 @@ void TextBlockData::insertParenthesis(int pos, char c) const TextBlockData::Parenthesis* TextBlockData::parenthesisForPosision(int pos) { - for (const Parenthesis& par : parData) + for (Parenthesis& par : parData) { if (par.position == pos) return ∥ @@ -431,6 +383,12 @@ int TextBlockData::Parenthesis::operator==(const TextBlockData::Parenthesis& oth return other.position == position && other.character == character; } +bool SqliteHighlighterPlugin::init() +{ + refreshFormats(); + return true; +} + QString SqliteHighlighterPlugin::getLanguageName() const { return "SQL"; @@ -440,11 +398,80 @@ QSyntaxHighlighter* SqliteHighlighterPlugin::createSyntaxHighlighter(QWidget* te { QPlainTextEdit* plainEdit = dynamic_cast(textEdit); if (plainEdit) - return new SqliteSyntaxHighlighter(plainEdit->document()); + return new SqliteSyntaxHighlighter(plainEdit->document(), &formats); QTextEdit* edit = dynamic_cast(textEdit); if (edit) - return new SqliteSyntaxHighlighter(edit->document()); + return new SqliteSyntaxHighlighter(edit->document(), &formats); return nullptr; } + +void SqliteHighlighterPlugin::refreshFormats() +{ + QTextCharFormat format; + + // Standard + format.setForeground(Cfg::getSyntaxForeground()); + format.setFontWeight(QFont::Normal); + format.setFontItalic(false); + formats[SqliteSyntaxHighlighter::State::STANDARD] = format; + + // Parenthesis + format.setForeground(Cfg::getSyntaxForeground()); + formats[SqliteSyntaxHighlighter::State::PARENTHESIS] = format; + + // String + format.setForeground(Cfg::getSyntaxStringFg()); + format.setFontWeight(QFont::Normal); + format.setFontItalic(true); + formats[SqliteSyntaxHighlighter::State::STRING] = format; + + // Keyword + format.setForeground(Cfg::getSyntaxKeywordFg()); + format.setFontWeight(QFont::ExtraBold); + format.setFontItalic(false); + formats[SqliteSyntaxHighlighter::State::KEYWORD] = format; + + // BindParam + format.setForeground(Cfg::getSyntaxBindParamFg()); + format.setFontWeight(QFont::Normal); + format.setFontItalic(false); + formats[SqliteSyntaxHighlighter::State::BIND_PARAM] = format; + + // Blob + format.setForeground(Cfg::getSyntaxBlobFg()); + format.setFontWeight(QFont::Normal); + format.setFontItalic(false); + formats[SqliteSyntaxHighlighter::State::BLOB] = format; + + // Comment + format.setForeground(Cfg::getSyntaxCommentFg()); + format.setFontWeight(QFont::Normal); + format.setFontItalic(true); + formats[SqliteSyntaxHighlighter::State::COMMENT] = format; + + // Number + format.setForeground(Cfg::getSyntaxNumberFg()); + format.setFontWeight(QFont::Normal); + format.setFontItalic(false); + formats[SqliteSyntaxHighlighter::State::NUMBER] = format; +} + +QString SqliteHighlighterPlugin::previewSampleCode() const +{ + static_qstring(code, + "SELECT my_column\n" + " FROM my_table;\n" + "\n" + "SELECT my_column, 'sample string', x'afff'\n" + " FROM my_table -- sample comment\n" + " WHERE (col1 + col2) > @input_param;" + ); + return code; +} + +const QHash* SqliteHighlighterPlugin::getFormats() const +{ + return &formats; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.h b/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.h index b17c45f..c11ab7d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.h +++ b/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.h @@ -52,11 +52,9 @@ class GUI_API_EXPORT SqliteSyntaxHighlighter : public QSyntaxHighlighter NUMBER }; + SqliteSyntaxHighlighter(QTextDocument *parent, const QHash* formats); explicit SqliteSyntaxHighlighter(QTextDocument *parent); - void setFormat(State state, QTextCharFormat format); - QTextCharFormat getFormat(State state) const; - void addError(int from, int to, bool limitedDamage = false); void clearErrors(); bool haveErrors(); @@ -107,6 +105,8 @@ class GUI_API_EXPORT SqliteSyntaxHighlighter : public QSyntaxHighlighter int to; }; + void init(const QHash* formats); + void setupMapping(); /** @@ -155,15 +155,13 @@ class GUI_API_EXPORT SqliteSyntaxHighlighter : public QSyntaxHighlighter void handleParenthesis(TokenPtr token, TextBlockData* data); static const int regulartTextBlockState = static_cast(TextBlockState::REGULAR); - QHash formats; + QHash tokenTypeMapping; QList errors; QList dbObjects; bool objectLinksEnabled = false; bool createTriggerContext = false; - - private slots: - void setupFormats(); + const QHash* formats = nullptr; }; class GUI_API_EXPORT SqliteHighlighterPlugin : public BuiltInPlugin, public SyntaxHighlighterPlugin @@ -172,12 +170,19 @@ class GUI_API_EXPORT SqliteHighlighterPlugin : public BuiltInPlugin, public Synt SQLITESTUDIO_PLUGIN_TITLE("SQL highlighter") SQLITESTUDIO_PLUGIN_DESC("SQL (SQLite) syntax highlighter.") - SQLITESTUDIO_PLUGIN_VERSION(10000) + SQLITESTUDIO_PLUGIN_VERSION(10100) SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl") public: + bool init(); QString getLanguageName() const; QSyntaxHighlighter* createSyntaxHighlighter(QWidget* textEdit) const; + void refreshFormats(); + QString previewSampleCode() const; + const QHash* getFormats() const; + + private: + QHash formats; }; GUI_API_EXPORT int qHash(SqliteSyntaxHighlighter::State state); diff --git a/SQLiteStudio3/guiSQLiteStudio/statusfield.cpp b/SQLiteStudio3/guiSQLiteStudio/statusfield.cpp index 11eed59..6b4d040 100644 --- a/SQLiteStudio3/guiSQLiteStudio/statusfield.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/statusfield.cpp @@ -1,11 +1,11 @@ #include "statusfield.h" #include "ui_statusfield.h" -#include "mainwindow.h" #include "uiconfig.h" #include "iconmanager.h" #include "themetuner.h" #include "common/tablewidget.h" #include "services/notifymanager.h" +#include "common/mouseshortcut.h" #include #include #include @@ -29,6 +29,7 @@ StatusField::StatusField(QWidget *parent) : connect(nm, SIGNAL(notifyError(QString)), this, SLOT(error(QString))); connect(nm, SIGNAL(notifyWarning(QString)), this, SLOT(warn(QString))); connect(CFG_UI.Fonts.StatusField, SIGNAL(changed(QVariant)), this, SLOT(fontChanged(QVariant))); + MouseShortcut::forWheel(Qt::ControlModifier, this, SLOT(fontSizeChangeRequested(int)), ui->tableWidget->viewport()); THEME_TUNER->manageCompactLayout(widget()); @@ -206,7 +207,7 @@ void StatusField::customContextMenuRequested(const QPoint &pos) void StatusField::reset() { - for (QAbstractAnimation* anim : itemAnimations) + for (QAbstractAnimation*& anim : itemAnimations) anim->stop(); itemAnimations.clear(); @@ -226,3 +227,15 @@ void StatusField::fontChanged(const QVariant& variant) ui->tableWidget->item(row, col)->setFont(font); } } + +void StatusField::changeFontSize(int factor) +{ + auto f = CFG_UI.Fonts.StatusField.get(); + f.setPointSize(f.pointSize() + factor); + CFG_UI.Fonts.StatusField.set(f); +} + +void StatusField::fontSizeChangeRequested(int delta) +{ + changeFontSize(delta >= 0 ? 1 : -1); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/statusfield.h b/SQLiteStudio3/guiSQLiteStudio/statusfield.h index 6f9ad22..5e9b59e 100644 --- a/SQLiteStudio3/guiSQLiteStudio/statusfield.h +++ b/SQLiteStudio3/guiSQLiteStudio/statusfield.h @@ -38,6 +38,7 @@ class GUI_API_EXPORT StatusField : public QDockWidget void flashItems(const QList& items, const QColor& color); void setupMenu(); void readRecentMessages(); + void changeFontSize(int factor); Ui::StatusField *ui = nullptr; QMenu* menu = nullptr; @@ -58,6 +59,7 @@ class GUI_API_EXPORT StatusField : public QDockWidget void error(const QString& text); void reset(); void fontChanged(const QVariant& variant); + void fontSizeChangeRequested(int delta); public slots: void refreshColors(); diff --git a/SQLiteStudio3/guiSQLiteStudio/style.cpp b/SQLiteStudio3/guiSQLiteStudio/style.cpp index 525a832..6946443 100644 --- a/SQLiteStudio3/guiSQLiteStudio/style.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/style.cpp @@ -1,9 +1,12 @@ #include "style.h" #include "themetuner.h" -#include "common/global.h" #include "mainwindow.h" +#include "common/unused.h" +#include "syntaxhighlighterplugin.h" +#include "services/pluginmanager.h" #include #include +#include Style* Style::instance = nullptr; @@ -15,6 +18,12 @@ Style* Style::getInstance() return instance; } + +bool Style::isDark(const QStyle* style) +{ + return style->standardPalette().text().color().value() >= 80; +} + const ExtendedPalette& Style::extendedPalette() const { return extPalette; @@ -24,15 +33,21 @@ void Style::setStyle(QStyle *style, const QString &styleName) { setBaseStyle(style); - QApplication::setPalette(initialPalette); // reset palette, cause styles don't provide - // full palette when changed in runtime (i.e. windowsvista) - QApplication::setStyle(this); - QApplication::setPalette(standardPalette()); - THEME_TUNER->tuneTheme(styleName); - QToolTip::setPalette(standardPalette()); + QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); - extPalette.styleChanged(this, styleName); + if (styleName != "qt5ct-style") + { + QApplication::setPalette(initialPalette); // reset palette, cause styles don't provide + // full palette when changed in runtime (i.e. windowsvista) + } + QApplication::setStyle(this); + if (styleName != "qt5ct-style") + { + QApplication::setPalette(standardPalette()); + QToolTip::setPalette(standardPalette()); + } + THEME_TUNER->tuneTheme(styleName); MAINWINDOW->getMdiArea()->setBackground(extPalette.mdiAreaBase()); } @@ -41,8 +56,36 @@ QString Style::name() const return baseStyle()->objectName(); } +bool Style::isDark() const +{ + return isDark(this); +} + +bool Style::eventFilter(QObject *obj, QEvent *ev) +{ + UNUSED(obj); + if (ev->type() == QEvent::PaletteChange) + { + if (extPalette.styleChanged(this, name())) + { + QList plugins = PLUGINS->getLoadedPlugins(); + auto it = plugins.begin(); + while (it != plugins.end()) + { + (*it)->refreshFormats(); + it++; + } + emit paletteChanged(); + } + } + + return false; +} + Style::Style(QStyle *style) : QProxyStyle(style) { initialPalette = style->standardPalette(); + extPalette.styleChanged(this, name()); + qApp->installEventFilter(this); } diff --git a/SQLiteStudio3/guiSQLiteStudio/style.h b/SQLiteStudio3/guiSQLiteStudio/style.h index ccb5e9e..82d79bc 100644 --- a/SQLiteStudio3/guiSQLiteStudio/style.h +++ b/SQLiteStudio3/guiSQLiteStudio/style.h @@ -5,15 +5,23 @@ #include #include +class CfgEntry; class Style : public QProxyStyle { + Q_OBJECT + public: static Style* getInstance(); + static bool isDark(const QStyle* style); const ExtendedPalette &extendedPalette() const; void setStyle(QStyle* style, const QString& styleName); QString name() const; + bool isDark() const; + + protected: + bool eventFilter(QObject *obj, QEvent *ev) override; private: static Style* instance; @@ -22,6 +30,9 @@ class Style : public QProxyStyle ExtendedPalette extPalette; QPalette initialPalette; + + signals: + void paletteChanged(); }; #define STYLE Style::getInstance() diff --git a/SQLiteStudio3/guiSQLiteStudio/syntaxhighlighterplugin.h b/SQLiteStudio3/guiSQLiteStudio/syntaxhighlighterplugin.h index 15b6c2c..8a33265 100644 --- a/SQLiteStudio3/guiSQLiteStudio/syntaxhighlighterplugin.h +++ b/SQLiteStudio3/guiSQLiteStudio/syntaxhighlighterplugin.h @@ -12,6 +12,8 @@ class GUI_API_EXPORT SyntaxHighlighterPlugin : virtual public Plugin public: virtual QString getLanguageName() const = 0; virtual QSyntaxHighlighter* createSyntaxHighlighter(QWidget* textEdit) const = 0; + virtual void refreshFormats() = 0; + virtual QString previewSampleCode() const = 0; }; #endif // SYNTAXHIGHLIGHTERPLUGIN_H diff --git a/SQLiteStudio3/guiSQLiteStudio/taskbar.cpp b/SQLiteStudio3/guiSQLiteStudio/taskbar.cpp index b209bbc..2e6a93a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/taskbar.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/taskbar.cpp @@ -125,8 +125,10 @@ void TaskBar::initContextMenu(ExtActionContainer* mainWin) // because that macro causes MainWindow initialization and this caused endless loop. taskMenu = new QMenu(this); taskMenu->addAction(mainWin->getAction(MainWindow::CLOSE_WINDOW)); - taskMenu->addAction(mainWin->getAction(MainWindow::CLOSE_OTHER_WINDOWS)); taskMenu->addAction(mainWin->getAction(MainWindow::CLOSE_ALL_WINDOWS)); + taskMenu->addAction(mainWin->getAction(MainWindow::CLOSE_OTHER_WINDOWS)); + taskMenu->addAction(mainWin->getAction(MainWindow::CLOSE_ALL_WINDOWS_LEFT)); + taskMenu->addAction(mainWin->getAction(MainWindow::CLOSE_ALL_WINDOWS_RIGHT)); taskMenu->addSeparator(); taskMenu->addAction(mainWin->getAction(MainWindow::RESTORE_WINDOW)); taskMenu->addAction(mainWin->getAction(MainWindow::RENAME_WINDOW)); diff --git a/SQLiteStudio3/guiSQLiteStudio/themetuner.cpp b/SQLiteStudio3/guiSQLiteStudio/themetuner.cpp index 89092d6..ee8a0b9 100644 --- a/SQLiteStudio3/guiSQLiteStudio/themetuner.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/themetuner.cpp @@ -35,7 +35,7 @@ void ThemeTuner::manageCompactLayout(QWidget* w) void ThemeTuner::manageCompactLayout(QList wList) { widgetsForCompactLayout += wList; - for (QWidget* w : wList) + for (QWidget*& w : wList) connect(w, SIGNAL(destroyed()), this, SLOT(handleWidgetDestroyed())); handleCompactLayoutChange(CFG_UI.General.CompactLayout.get()); @@ -59,11 +59,27 @@ void ThemeTuner::darkThemeFix(QWizard* wizard) } void ThemeTuner::registerQWizardThemeTuneRequired(const QString& styleName) +{ + if (MAINWINDOW->isClosingApp()) + return; + + getInstance()->registerQWizardThemeTuneRequiredInternal(styleName); +} + +void ThemeTuner::registerQWizardThemeTuneRequiredInternal(const QString& styleName) { qwizardThemeTuneRequired << styleName; } void ThemeTuner::deregisterQWizardThemeTuneRequired(const QString& styleName) +{ + if (MAINWINDOW->isClosingApp()) + return; + + getInstance()->deregisterQWizardThemeTuneRequiredInternal(styleName); +} + +void ThemeTuner::deregisterQWizardThemeTuneRequiredInternal(const QString& styleName) { qwizardThemeTuneRequired.removeOne(styleName); } @@ -126,7 +142,7 @@ void ThemeTuner::handleCompactLayoutChange(const QVariant& newValue) { if (newValue.toBool()) { - for (QWidget* w : widgetsForCompactLayout) + for (QWidget*& w : widgetsForCompactLayout) { w->layout()->setContentsMargins(0, 0, 0, 0); w->layout()->setSpacing(0); @@ -134,7 +150,7 @@ void ThemeTuner::handleCompactLayoutChange(const QVariant& newValue) } else { - for (QWidget* w : widgetsForCompactLayout) + for (QWidget*& w : widgetsForCompactLayout) { w->layout()->setContentsMargins(-1, -1, -1, -1); w->layout()->setSpacing(-1); diff --git a/SQLiteStudio3/guiSQLiteStudio/themetuner.h b/SQLiteStudio3/guiSQLiteStudio/themetuner.h index 805ca94..5607fcd 100644 --- a/SQLiteStudio3/guiSQLiteStudio/themetuner.h +++ b/SQLiteStudio3/guiSQLiteStudio/themetuner.h @@ -22,14 +22,16 @@ class GUI_API_EXPORT ThemeTuner : public QObject void manageCompactLayout(QList wList); QString getDefaultCss(const QString& themeName = QString()) const; void darkThemeFix(QWizard* wizard); - void registerQWizardThemeTuneRequired(const QString& styleName); - void deregisterQWizardThemeTuneRequired(const QString& styleName); + static void registerQWizardThemeTuneRequired(const QString& styleName); + static void deregisterQWizardThemeTuneRequired(const QString& styleName); static ThemeTuner* getInstance(); static void cleanUp(); private: ThemeTuner(QObject* parent = 0); + void registerQWizardThemeTuneRequiredInternal(const QString& styleName); + void deregisterQWizardThemeTuneRequiredInternal(const QString& styleName); void init(); void tuneCss(const QString& themeName); diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio.ts new file mode 100644 index 0000000..a796b76 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio.ts @@ -0,0 +1,7087 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + + + + + About + + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + + Licenses + + + + + Environment + + + + + Icon directories + + + + + Form directories + + + + + SQLite extension directories + + + + + Plugin directories + + + + + Configuration directory + + + + + Application directory + + + + + Qt version: + + + + + SQLite 3 version: + + + + + Portable distribution. + + + + + MacOS X application bundle distribution. + + + + + Operating system managed distribution. + + + + + <h3>Table of contents:</h3><ol>%2</ol> + + + + + BindParamsDialog + + + Query parameters + + + + + Please provide values for query parameters + + + + + CodeSnippetEditor + + + Filter snippets + + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + + Snippet name + + + + + Code assistant shortcut + + + + + Snippet code + + + + + Code Snippets editor window has uncommitted modifications. + + + + + Code Snippets editor + + + + + Commit all snippet changes + + + + + Rollback all snippet changes + + + + + Create new snippet + + + + + Delete selected snippet + + + + + Move the snippet up + + + + + Move the snippet down + + + + + Code snippets manual + + + + + Enter a non-empty, unique name of the snippet. + + + + + Enter a non-empty snippet content. + + + + + This hotkey is not unique in context of a code assistant. + + + + + CollationsEditor + + + Filter collations + + + + + Databases + + + + + Register in all databases + + + + + Register in following databases: + + + + + Implementation code: + + + + + Collation name: + + + + + Implementation language: + + + + + Collations editor + + + + + Commit all collation changes + + + + + Rollback all collation changes + + + + + Create new collation + + + + + Delete selected collation + + + + + Editing collations manual + + + + + Enter a non-empty, unique name of the collation. + + + + + Pick the implementation language. + + + + + Enter a non-empty implementation code. + + + + + Collations editor window has uncommitted modifications. + + + + + ColorButton + + + Pick a color + + + + + ColumnCollatePanel + + + Collation name: + + + + + Named constraint: + + + + + Enter a name of the constraint. + + + + + Enter a collation name. + + + + + ColumnDefaultPanel + + + Default value: + + + + + Named constraint: + + + + + Enter a default value expression. + + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + + Enter a name of the constraint. + + + + + ColumnDialog + + + Column + + + + + Name and type + + + + + Scale + + + + + Precision + + + + + Data type: + + + + + Column name: + + + + + Size: + + + + + Constraints + + + + + Generated value + + + + + Unique + + + + + + + + + + + + Configure + + + + + Foreign Key + + + + + Collate + + + + + Not NULL + + + + + Check condition + + + + + Primary Key + + + + + Default + + + + + Advanced mode + + + + + Add constraint + column dialog + + + + + Edit constraint + column dialog + + + + + + Delete constraint + column dialog + + + + + Move constraint up + column dialog + + + + + Move constraint down + column dialog + + + + + Add a primary key + column dialog + + + + + Add a foreign key + column dialog + + + + + Add an unique constraint + column dialog + + + + + Add a check constraint + column dialog + + + + + Add a not null constraint + column dialog + + + + + Add a collate constraint + column dialog + + + + + Add a generated value constraint + column dialog + + + + + Add a default constraint + column dialog + + + + + Are you sure you want to delete constraint '%1'? + column dialog + + + + + Correct the constraint's configuration. + + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + + Precision cannot be defined without the scale. + + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + + Could not match valid STRICT table datatype from declared type: %1. + + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + + + + + Name + column dialog constraints + + + + + Details + column dialog constraints + + + + + ColumnForeignKeyPanel + + + Foreign table: + + + + + Foreign column: + + + + + Reactions + + + + + Deferred foreign key + + + + + Named constraint + + + + + Constraint name + + + + + Pick the foreign table. + + + + + Pick the foreign column. + + + + + Enter a name of the constraint. + + + + + ColumnGeneratedPanel + + + Generating code: + + + + + Explicit type: + + + + + Use "GENERATED ALWAYS" keywords + + + + + Named constraint: + + + + + Enter the column value generating expression. + + + + + Invalid value generating expression: %1. + + + + + Invalid value generating expression. + + + + + Enter a name of the constraint. + + + + + ColumnPrimaryKeyPanel + + + Autoincrement + + + + + Sort order: + + + + + Named constraint: + + + + + On conflict: + + + + + Enter a name of the constraint. + + + + + Descending order is not allowed with AUTOINCREMENT. + + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + + + + + On conflict: + + + + + Enter a name of the constraint. + + + + + CompleterWindow + + + Column: %1 + completer statusbar + + + + + Table: %1 + completer statusbar + + + + + Index: %1 + completer statusbar + + + + + Trigger: %1 + completer statusbar + + + + + View: %1 + completer statusbar + + + + + Database: %1 + completer statusbar + + + + + Keyword: %1 + completer statusbar + + + + + Function: %1 + completer statusbar + + + + + Operator: %1 + completer statusbar + + + + + String + completer statusbar + + + + + Number + completer statusbar + + + + + Binary data + completer statusbar + + + + + Collation: %1 + completer statusbar + + + + + Pragma function: %1 + completer statusbar + + + + + Insert a code snippet + + + + + ConfigDialog + + + + Configuration + + + + + Search + + + + + General + + + + + Keyboard shortcuts + + + + + Look & feel + + + + + Style + + + + + Fonts + + + + + Code colors + + + + + + Database list + + + + + Code assistant + + + + + Data browsing + + + + + Data editors + + + + + Plugins + + + + + Code formatters + + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + + Sort table columns alphabetically + + + + + Expand tables node when connected to a database + + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + + Display additional labels on the list + + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + + Display labels for regular tables + + + + + Virtual tables will be marked with a 'virtual' label. + + + + + Display labels for virtual tables + + + + + Expand views node when connected to a database + + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + + Sort objects (tables, indexes, triggers and views) alphabetically + + + + + Display system tables and indexes on the list + + + + + Database dialog window + + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + + Do not mark database to be "permanent" by default + + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + + Try to bypass dialog completly when dropping database file onto the list + + + + + Data browsing and editing + + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + + Number of memorized table populating configurations + + + + + Data column width + + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + + Enlarge column when entering value longer than current width + + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + + Number of data rows per page: + + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + + Show column and row details tooltip in data view + + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + + Keep NULL value when entering empty value + + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + + Use DEFAULT value (if defined), when committing NULL value + + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + + Limit number of rows for in case of dozens of columns + + + + + Inserting new row in data grid + + + + + Before currently selected row + + + + + After currently selected row + + + + + At the end of data view + + + + + Table windows + + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + + Open Table Windows with the data tab for start + + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + + Place data tab as first tab in a Table Window + + + + + View windows + + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + + Open View Windows with the data tab for start + + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + + Place data tab as first tab in a View Window + + + + + Data types + + + + + Available editors: + + + + + Editors selected for this data type: + + + + + Schema editing + + + + + Number of DDL changes kept in history. + + + + + DDL history size: + + + + + Don't show DDL preview dialog when committing schema changes + + + + + SQL queries + + + + + + Number of queries kept in the history. + + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + + History size: + + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + + Execute only the query under the cursor + + + + + Number of memorized query parameters + + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + + Use scientific notation for real numbers in the grid view + + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + + Limit automatic data column width to (in pixels): + + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + + Keep at least the width to show complete column name + + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + + Wrap lines in SQL editor + + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + + Highlight current query + + + + + Updates + + + + + Automatically check for updates at startup + + + + + Session + + + + + Restore last session (active MDI windows) after startup + + + + + Allow multiple instances of the application at the same time + + + + + Status Field + + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + + Always open Status panel when new message is printed + + + + + Code syntax colors + + + + + Keyword foreground + + + + + Regular foreground + + + + + String foreground + + + + + Comment foreground + + + + + Valid objects foreground + + + + + Current query background + + + + + Bind parameter foreground + + + + + Current line background + + + + + Matched parenthesis background + + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + + Number foreground + + + + + BLOB value foreground + + + + + Matched parenthesis foreground + + + + + Reset to defaults + + + + + Filter shortcuts by name or key combination + + + + + Action + + + + + Key combination + + + + + + Language + + + + + Changing language requires application restart to take effect. + + + + + Compact layout + + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + + Use compact layout + + + + + Main window dock areas + + + + + Left and right areas occupy corners + + + + + Top and bottom areas occupy corners + + + + + Hide built-in plugins + + + + + Current style: + + + + + Preview + + + + + Enabled + + + + + Disabled + + + + + Active formatter plugin + + + + + SQL editor font + + + + + Database list font + + + + + Database list additional label font + + + + + Data view font + + + + + Status field font + + + + + Code assistant settings + + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + + Automatically trigger the assistant after a dot is typed after an object name + + + + + Description: + plugin details + + + + + Category: + plugin details + + + + + Version: + plugin details + + + + + Author: + plugin details + + + + + Internal name: + plugin details + + + + + Dependencies: + plugin details + + + + + Conflicts: + plugin details + + + + + Plugin details + + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + + %1 (built-in) + plugins manager in configuration dialog + + + + + Details + + + + + No plugins in this category. + + + + + Add new data type + + + + + Rename selected data type + + + + + Delete selected data type + + + + + Help for configuring data type editors + + + + + Clear hotkey for this action + + + + + Restore original hotkey for this action + + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + + ConstraintCheckPanel + + + The condition + + + + + Named constraint: + + + + + On conflict + + + + + Enter a valid condition. + + + + + Enter a name of the constraint. + + + + + ConstraintDialog + + + New constraint + constraint dialog + + + + + Create + constraint dialog + + + + + Edit constraint + dialog window + + + + + Apply + constraint dialog + + + + + Primary key + table constraints + + + + + Foreign key + table constraints + + + + + Unique + table constraints + + + + + Not NULL + table constraints + + + + + Check + table constraints + + + + + Generated + table constraints + + + + + Collate + table constraints + + + + + Default + table constraints + + + + + ConstraintTabModel + + + Table + table constraints + + + + + Column (%1) + table constraints + + + + + Scope + table constraints + + + + + Type + table constraints + + + + + Details + table constraints + + + + + Name + table constraints + + + + + CssDebugDialog + + + SQLiteStudio CSS console + + + + + DataView + + + Filter data + data view + + + + + Grid view + + + + + Form view + + + + + Refresh table data + data view + + + + + First page + data view + + + + + Previous page + data view + + + + + Next page + data view + + + + + Last page + data view + + + + + Commit changes for selected cells + data view + + + + + Rollback changes for selected cells + data view + + + + + Show grid view of results + data view + + + + + Show form view of results + data view + + + + + Filter by text (if contains) + data view + + + + + Filter strictly by text (if equals) + data view + + + + + Tabs on top + data view + + + + + Tabs at bottom + data view + + + + + Place new rows above selected row + data view + + + + + Place new rows below selected row + data view + + + + + Place new rows at the end of the data view + data view + + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + + Row: %1 + + + + + Filter + + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + + Filter by the Regular Expression + data view + + + + + Filter by SQL expression + data view + + + + + Show filter inputs per column + data view + + + + + Apply filter + data view + + + + + DbDialog + + + Database + + + + + Database type + + + + + Database driver + + + + + + File + + + + + Name (on the list) + + + + + Options + + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + + + + + Permanent (keep it in configuration) + + + + + Test connection + + + + + Select new or existing file on local computer + + + + + Browse + + + + + Database type not selected. + + + + + Database path not specified. + + + + + Enter an unique database name. + + + + + This name is already in use. Please enter unique name. + + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + + Enter a database file path. + + + + + This database is already on the list under name: %1 + + + + + Select a database type. + + + + + DbObjectDialogs + + + Delete table + + + + + Are you sure you want to delete table %1? + + + + + Delete index + + + + + Are you sure you want to delete index %1? + + + + + Delete trigger + + + + + Are you sure you want to delete trigger %1? + + + + + Delete view + + + + + Are you sure you want to delete view %1? + + + + + + Error while dropping %1: %2 + + + + + Delete objects + + + + + Are you sure you want to delete following objects: +%1 + + + + + Cannot start transaction. Details: %1 + + + + + Cannot commit transaction. Details: %1 + + + + + DbTree + + + Databases + + + + + Filter by name + + + + + Copy + + + + + Paste + + + + + Select all + + + + + Create a group + + + + + Delete the group + + + + + Rename the group + + + + + &Add a database + + + + + &Edit the database + + + + + &Remove the database + + + + + &Connect to the database + + + + + &Disconnect from the database + + + + + Import + + + + + &Export the database + + + + + Vac&uum + + + + + &Integrity check + + + + + Create a &table + + + + + Edit the t&able + + + + + Delete the ta&ble + + + + + Export the table + + + + + Import into the table + + + + + Populate table + + + + + Create similar table + + + + + Reset autoincrement sequence + + + + + Create an &index + + + + + Edit the i&ndex + + + + + Delete the in&dex + + + + + Create a trig&ger + + + + + Edit the trigg&er + + + + + Delete the trigge&r + + + + + Create a &view + + + + + Edit the v&iew + + + + + Delete the vi&ew + + + + + Add a column + + + + + Edit the column + + + + + Delete the column + + + + + Delete selected items + + + + + Clear filter + + + + + &Refresh all database schemas + + + + + Re&fresh selected database schema + + + + + + Erase table data + + + + + Open file's directory + + + + + Execute SQL from file + + + + + Increase font size + database list + + + + + Decrease font size + database list + + + + + + Database + + + + + Grouping + + + + + Generate query for table + + + + + + Create group + + + + + Group name + + + + + Entry with name %1 already exists in group %2. + + + + + Delete group + + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + + Are you sure you want to remove database '%1' from the list? + + + + + Are you sure you want to remove following databases from the list: +%1 + + + + + Remove database + + + + + + Cannot import, because no import plugin is loaded. + + + + + + Cannot export, because no export plugin is loaded. + + + + + Vacuum (%1) + + + + + Integrity check (%1) + + + + + Reset autoincrement + + + + + Are you sure you want to reset autoincrement value for table '%1'? + + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + + Autoincrement value for table '%1' has been reset successfully. + + + + + Are you sure you want to delete all data from table(s): %1? + + + + + An error occurred while trying to delete data from table '%1': %2 + + + + + All data has been deleted for table '%1'. + + + + + Following objects will be deleted: %1. + + + + + Following databases will be removed from list: %1. + + + + + Remainig objects from deleted group will be moved in place where the group used to be. + + + + + %1<br><br>Are you sure you want to continue? + + + + + Delete objects + + + + + DbTreeItemDelegate + + + error + dbtree labels + + + + + (system table) + database tree label + + + + + (virtual) + virtual table label + + + + + (system index) + database tree label + + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + + + + + URI: + dbtree tooltip + + + + + Version: + dbtree tooltip + + + + + File size: + dbtree tooltip + + + + + Encoding: + dbtree tooltip + + + + + Error: + dbtree tooltip + + + + + Table : %1 + dbtree tooltip + + + + + Columns (%1): + dbtree tooltip + + + + + Indexes (%1): + dbtree tooltip + + + + + Triggers (%1): + dbtree tooltip + + + + + Copy + + + + + Move + + + + + Include data + + + + + Include indexes + + + + + Include triggers + + + + + Abort + + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + + Referenced tables + + + + + Do you want to include following referenced tables as well: +%1 + + + + + Name conflict + + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + + SQL statements conversion + + + + + Following error occurred while converting SQL statements to the target SQLite version: + + + + + Would you like to ignore those errors and proceed? + + + + + DdlHistoryWindow + + + Filter by database: + + + + + Clear entire history + + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + + Clear history + + + + + Are you sure you want to erase entire DDL history? + + + + + DDL history + + + + + DdlPreviewDialog + + + Queries to be executed + + + + + Don't show again + + + + + DebugConsole + + + SQLiteStudio Debug Console + + + + + EditorWindow + + + SQL editor + + + + + Query + + + + + History + + + + + Results in the separate tab + + + + + Results below the query + + + + + + SQL editor %1 + + + + + + Results + + + + + Execute query + + + + + Explain query + + + + + Clear execution history + sql editor + + + + + Export results + sql editor + + + + + Create view from query + sql editor + + + + + Previous database + + + + + Next database + + + + + Show next tab + sql editor + + + + + Show previous tab + sql editor + + + + + Focus results below + sql editor + + + + + Focus SQL editor above + sql editor + + + + + Delete selected SQL history entries + sql editor + + + + + Execute single query under cursor + + + + + Execute all queries in editor + + + + + Active database (%1/%2) + + + + + Query finished in %1 second(s). Rows affected: %2 + + + + + Query finished in %1 second(s). + + + + + Clear execution history + + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + + Cannot export, because no export plugin is loaded. + + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + + Editor window "%1" has uncommitted data. + + + + + ErrorsConfirmDialog + + + Errors + + + + + Following errors occured: + + + + + Would you like to proceed? + + + + + ExecFromFileDialog + + + Execute SQL from file + + + + + Input file + + + + + Path to file + + + + + Browse for file + + + + + Options + + + + + File encoding + + + + + Skip failing SQL statements + + + + + SQL scripts (*.sql);;All files (*) + + + + + Execute SQL file + + + + + Please provide file to be executed. + + + + + Provided file does not exist or cannot be read. + + + + + ExportDialog + + + Export + + + + + What do you want to export? + + + + + A database + + + + + A single table + + + + + Query results + + + + + Table to export + + + + + Database + + + + + Table + + + + + Options + + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + + Export table data + + + + + Export table indexes + + + + + Export table triggers + + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + + Select database objects to export + + + + + Export data from tables + + + + + Select all + + + + + Deselect all + + + + + + Database: + + + + + Query to export results for + + + + + Query to be executed for results: + + + + + Export format and options + + + + + Export format + + + + + Output + + + + + Exported file path + + + + + Clipboard + + + + + File + + + + + Exported text encoding: + + + + + Export format options + + + + + Cancel + + + + + + + Select database to export. + + + + + Select table to export. + + + + + Enter valid query to export. + + + + + Select at least one object to export. + + + + + You must provide a file name to export to. + + + + + Path you provided is an existing directory. You cannot overwrite it. + + + + + The directory '%1' does not exist. + + + + + The file '%1' exists and will be overwritten. + + + + + All files (*) + + + + + Pick file to export to + + + + + Internal error during export. This is a bug. Please report it. + + + + + FileExecErrorsDialog + + + Execution errors + + + + + Following errors were encountered during execution of SQL statements from the file: + + + + + SQL + + + + + Error + + + + + Statements that were executed successfully were commited. + + + + + Statements that were executed successfully were rolled back. + + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + + + + + FontEdit + + + Choose font + font configuration + + + + + Form + + + Active SQL formatter plugin + + + + + FormView + + + Commit row + form view + + + + + Rollback row + form view + + + + + First row + form view + + + + + Previous row + form view + + + + + Next row + form view + + + + + Last row + form view + + + + + Insert new row + form view + + + + + Delete current row + form view + + + + + FunctionsEditor + + + Filter functions + + + + + Input arguments + + + + + Undefined + + + + + Databases + + + + + Register in all databases + + + + + Register in following databases: + + + + + Type: + + + + + Function name: + + + + + Implementation language: + + + + + Deterministic + + + + + Initialization code: + + + + + + Function implementation code: + + + + + Final step implementation code: + + + + + SQL functions editor + + + + + Commit all function changes + + + + + Rollback all function changes + + + + + Create new function + + + + + Delete selected function + + + + + Custom SQL functions manual + + + + + Add function argument + + + + + Rename function argument + + + + + Delete function argument + + + + + Move function argument up + + + + + Move function argument down + + + + + Scalar + + + + + Aggregate + + + + + Enter a non-empty, unique name of the function. + + + + + Pick the implementation language. + + + + + Per step code: + + + + + Enter a non-empty implementation code. + + + + + argument + new function argument name in function editor window + + + + + Functions editor window has uncommitted modifications. + + + + + ImportDialog + + + Import data + + + + + Table to import to + + + + + Table + + + + + Database + + + + + Data source to import from + + + + + Data source type + + + + + Options + + + + + Text encoding: + + + + + Input file: + + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + + Ignore errors + + + + + Data source options + + + + + Cancel + + + + + If you type table name that doesn't exist, it will be created. + + + + + Enter the table name + + + + + Select import plugin. + + + + + You must provide a file to import from. + + + + + The file '%1' does not exist. + + + + + Path you provided is a directory. A regular file is required. + + + + + Pick file to import from + + + + + IndexDialog + + + + Index + + + + + Column + + + + + Sort + + + + + Collation + + + + + On table: + + + + + Delete selected indexed expression + + + + + Moves selected index column up in the order, making it more significant in the index. + + + + + Moves selected index column down in the order, making it less significant in the index. + + + + + Partial index condition + + + + + Unique index + + + + + Index name: + + + + + Edit selected indexed expression + + + + + Add indexed expression + + + + + DDL + + + + + Tried to open index dialog for closed or inexisting database. + + + + + Could not process index %1 correctly. Unable to open an index dialog. + + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + + Pick the table for the index. + + + + + Select at least one column. + + + + + Enter a valid condition. + + + + + default + index dialog + + + + + Sort order + table constraints + + + + + + Error + index dialog + + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + + An error occurred while executing SQL statements: +%1 + + + + + IndexExprColumnDialog + + + Indexed expression + + + + + Expression to index + + + + + This expression is already indexed by the index. + + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + + Enter an indexed expression. + + + + + Invalid expression. + + + + + LanguageDialog + + + Language + + + + + Please choose language: + + + + + MainWindow + + + Database toolbar + + + + + Structure toolbar + + + + + Tools + + + + + Window list + + + + + View toolbar + + + + + Configuration widgets + + + + + Syntax highlighting engines + + + + + Data editors + + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + + Running in debug mode. Debug messages are printed to the standard output. + + + + + You need to restart application to make the language change take effect. + + + + + Open SQL &editor + + + + + Open DDL &history + + + + + Open SQL &functions editor + + + + + Open code &snippets editor + + + + + Open &collations editor + + + + + Open ex&tension manager + + + + + &Import + + + + + E&xport + + + + + Open confi&guration dialog + + + + + &Tile windows + + + + + Tile windows &horizontally + + + + + Tile windows &vertically + + + + + &Cascade windows + + + + + Next window + + + + + Previous window + + + + + Hide status field + + + + + Close &all windows + + + + + Re&store recently closed window + + + + + Close current &window + + + + + Close &other windows + + + + + Close windows on the &left + + + + + Close windows on the &right + + + + + Re&name selected window + + + + + Open Debug Console + + + + + Open CSS Console + + + + + Report a &bug + + + + + D&onate + + + + + Propose a new &feature + + + + + &About + + + + + &Licenses + + + + + Open home &page + + + + + User &Manual + + + + + SQLite &documentation + + + + + Bugs and feature &requests + + + + + Quit + + + + + Check for &updates + + + + + &Database + menubar + + + + + &Structure + menubar + + + + + &View + menubar + + + + + Window list + menubar view menu + + + + + &Tools + menubar + + + + + &Help + + + + + Could not set style: %1 + main window + + + + + Cannot export, because no export plugin is loaded. + + + + + Cannot import, because no import plugin is loaded. + + + + + Rename window + + + + + Enter new name for the window: + + + + + New updates are available. <a href="%1">Click here for details</a>. + + + + + You're running the most recent version. No updates are available. + + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + + Could not add database %1 to list. + + + + + MdiWindow + + + Uncommitted changes + + + + + Close anyway + + + + + Don't close + + + + + MultiEditor + + + Null value + multieditor + + + + + Configure editors for this data type + + + + + Open another tab + + + + + Foreign Key + + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + + + + + Deleted + multieditor + + + + + Read only + multieditor + + + + + MultiEditorBoolPlugin + + + Boolean + + + + + MultiEditorDatePlugin + + + Date + + + + + MultiEditorDateTimePlugin + + + Date & time + + + + + MultiEditorHexPlugin + + + Hex + + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + + + + + MultiEditorText + + + Tab changes focus + + + + + Cut + + + + + Copy + + + + + Paste + + + + + Delete + + + + + Undo + + + + + Redo + + + + + MultiEditorTextPlugin + + + Text + + + + + MultiEditorTimePlugin + + + Time + + + + + NewConstraintDialog + + + New constraint + + + + + + Primary Key + new constraint dialog + + + + + + Foreign Key + new constraint dialog + + + + + + Unique + new constraint dialog + + + + + + Check + new constraint dialog + + + + + Not NULL + new constraint dialog + + + + + Collate + new constraint dialog + + + + + Generated + new constraint dialog + + + + + Default + new constraint dialog + + + + + NewVersionDialog + + + SQLiteStudio updates + + + + + New version is available! + + + + + Download new version! + + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + + Open SQLiteStudio home page. + + + + + Read release notes && download package yourself. + + + + + Just close this window. + + + + + Check for updates on startup + + + + + Not now. + + + + + PopulateConfigDialog + + + Populating configuration + + + + + Configuring <b>%1</b> for column <b>%2</b> + + + + + PopulateDialog + + + Populate table + + + + + Database + + + + + Table + + + + + Columns + + + + + Number of rows to populate: + + + + + Populate + populate dialog button + + + + + Abort + + + + + Configure + + + + + Populating configuration for this column is invalid or incomplete. + + + + + Select database with table to populate + + + + + Select table to populate + + + + + You have to select at least one column. + + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + + Cannot edit results of query other than %1. + + + + + Cannot edit columns that are result of aggregated %1 statements. + + + + + Cannot edit columns that are result of %1 statement. + + + + + Cannot edit columns that are result of common table expression statement (%1). + + + + + Cannot edit table generated columns. + + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + + on conflict: %1 + data view tooltip + + + + + references table %1, column %2 + data view tooltip + + + + + condition: %1 + data view tooltip + + + + + collation name: %1 + data view tooltip + + + + + Data grid view + + + + + Edit current cell inline + + + + + Copy cell(s) contents to clipboard + + + + + Copy cell(s) contents together with header to clipboard + + + + + Paste cell(s) contents from clipboard + + + + + Set empty value to selected cell(s) + + + + + Set NULL value to selected cell(s) + + + + + Commit changes to cell(s) contents + + + + + Rollback changes to cell(s) contents + + + + + Delete selected data row + + + + + Insert new data row + + + + + Open contents of selected cell in a separate editor + + + + + Toggle the height adjustment of rows + + + + + Increase font size + data view + + + + + Decrease font size + data view + + + + + Total pages available: %1 + + + + + Total rows loaded: %1 + + + + + Data view (both grid and form) + + + + + Refresh data + + + + + Switch to grid view of the data + + + + + Switch to form view of the data + + + + + Database list + + + + + Delete selected item + + + + + Clear filter contents + + + + + Refresh schema + + + + + Refresh all schemas + + + + + Add database + + + + + Select all items + + + + + Copy selected item(s) + + + + + + + Paste from clipboard + + + + + Increase font size + database list + + + + + Decrease font size + database list + + + + + Tables + + + + + Indexes + + + + + Triggers + + + + + Views + + + + + Columns + + + + + Data form view + + + + + Commit changes for current row + + + + + Rollback changes for current row + + + + + Go to first row on current page + + + + + Go to next row + + + + + Go to previous row + + + + + Go to last row on current page + + + + + Insert new row + + + + + Delete current row + + + + + Main window + + + + + Open SQL editor + + + + + Open DDL history window + + + + + Open snippets editor window + + + + + Open function editor window + + + + + Open collation editor window + + + + + Open extension manager window + + + + + Previous window + + + + + Next window + + + + + Hide status area + + + + + Open user manual + + + + + Open configuration dialog + + + + + Open Debug Console + + + + + Open CSS Console + + + + + Open the About dialog + + + + + Quit the application + + + + + Cell text value editor + + + + + + Cut selected text + + + + + + Copy selected text + + + + + + Delete selected text + + + + + + Undo + + + + + + Redo + + + + + SQL editor input field + + + + + Select whole editor contents + + + + + Save contents into a file + + + + + Load contents from a file + + + + + Find in text + + + + + Find next + + + + + Find previous + + + + + Replace in text + + + + + Delete current line + + + + + Request code assistant + + + + + Format contents + + + + + Move selected block of text one line down + + + + + Move selected block of text one line up + + + + + Copy selected block of text and paste it a line below + + + + + Copy selected block of text and paste it a line above + + + + + Toggle comment + + + + + Increase font size + sql editor + + + + + Decrease font size + sql editor + + + + + All SQLite databases + + + + + All files + + + + + Select database file + + + + + Select + + + + + File type + + + + + SQL editor window + + + + + Execute query + + + + + Execute single query under cursor + + + + + Execute all queries in editor + + + + + Execute "%1" query + + + + + Switch current working database to previous on the list + + + + + Switch current working database to next on the list + + + + + Go to next editor tab + + + + + Go to previous editor tab + + + + + Move keyboard input focus to the results view below + + + + + Move keyboard input focus to the SQL editor above + + + + + Delete selected SQL history entries + + + + + Table window + + + + + Commit the table structure + + + + + Rollback pending changes in the table structure + + + + + Refresh table structure + + + + + Add new column + + + + + Edit selected column + + + + + Delete selected column + + + + + Export table data + + + + + Import data to the table + + + + + Add new table constraint + + + + + Edit selected table constraint + + + + + Delete selected table constraint + + + + + Refresh table index list + + + + + Add new index + + + + + Edit selected index + + + + + Delete selected index + + + + + Refresh table trigger list + + + + + + Add new trigger + + + + + + Edit selected trigger + + + + + + Delete selected trigger + + + + + + Go to next tab + + + + + + Go to previous tab + + + + + A view window + + + + + Commit the view's query + + + + + Rollback pending changes in the view's query + + + + + Refresh view trigger list + + + + + Execute the view's query + + + + + A code snippets editor window + + + + + + + + Commit the pending changes + + + + + + + + Rollback the pending changes + + + + + A collation editor window + + + + + A function editor window + + + + + A SQLite extension editor window + + + + + QuitConfirmDialog + + + Uncommitted changes + + + + + Are you sure you want to quit the application? + +Following items are pending: + + + + + SearchTextDialog + + + Find or replace + + + + + Find: + + + + + Case sensitive + + + + + Search backwards + + + + + Regular expression matching + + + + + Replace && +find next + + + + + Replace with: + + + + + Replace all + + + + + Find + + + + + SortDialog + + + Sort by columns + + + + + + Column + + + + + + Order + + + + + Sort by: %1 + + + + + Move column up + + + + + Move column down + + + + + SqlEditor + + + Wrap words + sql editor + + + + + Cut + sql editor + + + + + Copy + sql editor + + + + + Paste + sql editor + + + + + Delete + sql editor + + + + + Select all + sql editor + + + + + Undo + sql editor + + + + + Redo + sql editor + + + + + Complete + sql editor + + + + + Format SQL + sql editor + + + + + Save SQL to file + sql editor + + + + + Select file to save SQL + sql editor + + + + + Load SQL from file + sql editor + + + + + Delete line + sql editor + + + + + Move block down + sql editor + + + + + Move block up + sql editor + + + + + Copy block down + sql editor + + + + + Copy up down + sql editor + + + + + Find + sql editor + + + + + Find next + sql editor + + + + + Find previous + sql editor + + + + + Replace + sql editor + + + + + Toggle comment + sql editor + + + + + Increase font size + sql editor + + + + + Decrease font size + sql editor + + + + + Could not open file '%1' for writing: %2 + + + + + Saved SQL contents to file: %1 + + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + + Save to file + + + + + SQL scripts (*.sql);;All files (*) + + + + + Open file + + + + + Could not open file '%1' for reading: %2 + + + + + Reached the end of document. Hit the find again to restart the search. + + + + + SqlQueryItem + + + Committing error: + data view tooltip + + + + + Column: + data view tooltip + + + + + Data type: + data view + + + + + Table: + data view tooltip + + + + + Constraints: + data view tooltip + + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + + + + + The row is marked for deletion. + + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + + + + + Cannot execute query on undefined or invalid database. + + + + + Cannot execute empty query. + + + + + Uncommitted data + + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + + Cannot commit the data for a cell that refers to the already closed database. + + + + + Could not begin transaction on the database. Details: %1 + + + + + An error occurred while committing the transaction: %1 + + + + + An error occurred while rolling back the transaction: %1 + + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + + An error occurred while committing the data: %1 + + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + + Error while executing SQL query on database '%1': %2 + + + + + Error while loading query results: %1 + + + + + Insert multiple rows + + + + + Number of rows to insert: + + + + + Delete rows + + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + + SqlQueryView + + + Go to referenced row in... + + + + + Copy + + + + + Copy with headers + + + + + Copy as... + + + + + Paste + + + + + Paste as... + + + + + Set NULL values + + + + + Erase values + + + + + Commit + + + + + Rollback + + + + + Commit selected cells + + + + + Rollback selected cells + + + + + Edit current cell inline + + + + + Define columns to sort by + + + + + Remove custom sorting + + + + + Insert row + + + + + Insert multiple rows + + + + + Delete selected row + + + + + Adjust height of rows + + + + + Increase font size + data view + + + + + Decrease font size + data view + + + + + Invert selection + data view + + + + + Edit value in editor + + + + + Show value in a viewer + + + + + Generate query for selected cells + + + + + No items selected to paste clipboard contents to. + + + + + Cannot paste data. Details: %1 + + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + + Cannot paste to a cell. Details: %1 + + + + + The row is marked for deletion. + + + + + Cannot paste to column %1. Details: %2 + + + + + Go to referenced row in table '%1' + + + + + table '%1' + + + + + Referenced row (%1) + + + + + Trim pasted text? + + + + + The pasted text contains leading or trailing white space. Trim it automatically? + + + + + Paste "NULL" as null value? + + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + + Edit value + + + + + SqlTableModel + + + Error while committing new row: %1 + + + + + Error while deleting row from table %1: %2 + + + + + SqliteExtensionEditor + + + Filter extensions + + + + + Leave empty to use default function + + + + + Extension file + + + + + Initialization function + + + + + Databases + + + + + Register in all databases + + + + + Register in following databases: + + + + + Extension manager window has uncommitted modifications. + + + + + Extension manager + + + + + Commit all extension changes + + + + + Rollback all extension changes + + + + + Add new extension + + + + + Remove selected extension + + + + + Editing extensions manual + + + + + File with given path does not exist or is not readable. + + + + + Unable to load extension: %1 + + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + + Dynamic link libraries (*.dll);;All files (*) + + + + + Shared objects (*.so);;All files (*) + + + + + Dynamic libraries (*.dylib);;All files (*) + + + + + All files (*) + + + + + Open file + + + + + StatusField + + + Status + + + + + Copy + + + + + Clear + + + + + TableConstraintsModel + + + Type + table constraints + + + + + Details + table constraints + + + + + Name + table constraints + + + + + TableForeignKeyPanel + + + Foreign table: + + + + + Columns + + + + + Local column + + + + + Foreign column + + + + + Reactions + + + + + Deferred foreign key + + + + + Named constraint + + + + + Constraint name + + + + + Pick the foreign column. + + + + + Pick the foreign table. + + + + + Select at least one foreign column. + + + + + Enter a name of the constraint. + + + + + Foreign column + table constraints + + + + + TablePrimaryKeyAndUniquePanel + + + Columns + + + + + Column + + + + + Collation + + + + + Sort + + + + + Valid only for a single column with INTEGER data type + + + + + Autoincrement + + + + + Named constraint + + + + + Constraint name + + + + + On conflict + + + + + Collate + table constraints + + + + + Sort order + table constraints + + + + + Select at least one column. + + + + + Enter a name of the constraint. + + + + + TableStructureModel + + + Name + table structure columns + + + + + Data type + table structure columns + + + + + Primary +Key + table structure columns + + + + + Foreign +Key + table structure columns + + + + + Unique + table structure columns + + + + + Check + table structure columns + + + + + Not +NULL + table structure columns + + + + + Collate + table structure columns + + + + + Generated + table structure columns + + + + + Default value + table structure columns + + + + + TableWindow + + + Structure + + + + + Table name: + + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + + Data + + + + + Constraints + + + + + Indexes + + + + + Triggers + + + + + DDL + + + + + Export table + table window + + + + + Import data to table + table window + + + + + Populate table + table window + + + + + Refresh structure + table window + + + + + Commit structure changes + table window + + + + + Rollback structure changes + table window + + + + + Add column + table window + + + + + Edit column + table window + + + + + + Delete column + table window + + + + + Move column up + table window + + + + + Move column down + table window + + + + + Create similar table + table window + + + + + Reset autoincrement value + table window + + + + + Add table constraint + table window + + + + + Edit table constraint + table window + + + + + Delete table constraint + table window + + + + + Move table constraint up + table window + + + + + Move table constraint down + table window + + + + + Add table primary key + table window + + + + + Add table foreign key + table window + + + + + Add table unique constraint + table window + + + + + Add table check constraint + table window + + + + + Refresh index list + table window + + + + + + Create index + table window + + + + + Edit index + table window + + + + + Delete index + table window + + + + + Refresh trigger list + table window + + + + + + Create trigger + table window + + + + + Edit trigger + table window + + + + + Delete trigger + table window + + + + + Are you sure you want to delete column '%1'? + table window + + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + + + + + Table modification + table window + + + + + Could not load data for table %1. Error details: %2 + + + + + Could not process the %1 table correctly. Unable to open a table window. + + + + + Database + + + + + Could not restore window %1, because no database or table was stored in session for this window. + + + + + Could not restore window '%1', because no database or table was stored in session for this window. + + + + + Could not restore window '%1', because database %2 could not be resolved. + + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + + New table %1 + + + + + Committed changes for table '%1' successfully. + + + + + Committed changes for table '%1' (named before '%2') successfully. + + + + + Could not commit table structure. Error message: %1 + table window + + + + + Reset autoincrement + + + + + Are you sure you want to reset autoincrement value for table '%1'? + + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + + Autoincrement value for table '%1' has been reset successfully. + + + + + Empty name + + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + + Cannot create a table without at least one column. + + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + + Are you sure you want to delete table constraint '%1'? + table window + + + + + Delete constraint + table window + + + + + Cannot export, because no export plugin is loaded. + + + + + Cannot import, because no import plugin is loaded. + + + + + Uncommitted changes + + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + + Go back to structure tab + + + + + Commit modifications and browse data. + + + + + Name + table window indexes + + + + + Unique + table window indexes + + + + + Columns + table window indexes + + + + + Partial index condition + table window indexes + + + + + Name + table window triggers + + + + + Event + table window triggers + + + + + Condition + table window triggers + + + + + Details + table window triggers + + + + + Table window "%1" has uncommitted structure modifications and data. + + + + + Table window "%1" has uncommitted data. + + + + + Table window "%1" has uncommitted structure modifications. + + + + + TriggerColumnsDialog + + + Trigger columns + + + + + Triggering columns: + + + + + Select all + + + + + Deselect all + + + + + TriggerDialog + + + + Trigger + + + + + On table: + + + + + Action: + + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + + Pre-condition: + + + + + The scope is still not fully supported by the SQLite database. + + + + + Trigger name: + + + + + When: + + + + + List of columns for UPDATE OF action. + + + + + Scope: + + + + + Code: + + + + + Trigger statements to be executed. + + + + + DDL + + + + + On view: + + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + + Enter a valid condition. + + + + + Enter a valid trigger code. + + + + + Error + trigger dialog + + + + + An error occurred while executing SQL statements: +%1 + + + + + VersionConvertSummaryDialog + + + Database version convert + + + + + Following changes to the SQL statements will be made: + + + + + Before + + + + + After + + + + + ViewWindow + + + Query + + + + + View name: + + + + + Output column names + + + + + + Data + + + + + Triggers + + + + + DDL + + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + + + + + Could not restore window '%1', because database %2 could not be resolved. + + + + + Could not restore window '%1', because database %2 could not be open. + + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + + New view %1 + + + + + Database + + + + + Refresh the view + view window + + + + + Commit the view changes + view window + + + + + Rollback the view changes + view window + + + + + Explicit column names + + + + + Generate output column names automatically basing on result columns of the view. + + + + + Add column + view window + + + + + Edit column + view window + + + + + Delete column + view window + + + + + Move column up + view window + + + + + Move column down + view window + + + + + Refresh trigger list + view window + + + + + Create new trigger + view window + + + + + Edit selected trigger + view window + + + + + Delete selected trigger + view window + + + + + View window "%1" has uncommitted structure modifications and data. + + + + + View window "%1" has uncommitted data. + + + + + View window "%1" has uncommitted structure modifications. + + + + + Could not load data for view %1. Error details: %2 + + + + + Uncommitted changes + + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + + Go back to structure tab + + + + + Commit modifications and browse data. + + + + + View '%1' was committed successfully. + + + + + Committed changes for view '%1' successfully. + + + + + Committed changes for view '%1' (named before '%2') successfully. + + + + + Could not commit view changes. Error message: %1 + view window + + + + + Override columns + + + + + Currently defined columns will be overriden. Do you want to continue? + + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + + Name + view window triggers + + + + + Instead of + view window triggers + + + + + Condition + view window triggers + + + + + Details + table window triggers + + + + + Could not process the %1 view correctly. Unable to open a view window. + + + + + Empty name + + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + + + + + View modification + view window + + + + + WidgetCover + + + Interrupt + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_af_ZA.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_af_ZA.ts new file mode 100644 index 0000000..cadd4bc --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_af_ZA.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ar_SA.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ar_SA.ts new file mode 100644 index 0000000..e2e9763 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ar_SA.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ca_ES.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ca_ES.ts new file mode 100644 index 0000000..5d58b1f --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ca_ES.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_cs_CZ.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_cs_CZ.ts new file mode 100644 index 0000000..17bed2c --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_cs_CZ.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_da_DK.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_da_DK.ts new file mode 100644 index 0000000..26fca17 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_da_DK.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de.qm b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de.qm deleted file mode 100644 index 3f118d1..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de.qm and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de.ts deleted file mode 100644 index 0047f6c..0000000 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de.ts +++ /dev/null @@ -1,7261 +0,0 @@ - - - - - AboutDialog - - - About SQLiteStudio and licenses - Über SQLiteStudio und deren Lizenzen - - - - About - Über SQLiteStudio - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Freier, open-source, multiplattformfähiger SQLite Datenbankmanager.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Autor und aktiver Verantwortlicher:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - Licenses - Lizenzen - - - - Environment - Programmumgebung - - - - Icon directories - Icon Verzeichnisse - - - - Form directories - Formular Verzeichnisse - - - - Plugin directories - Plugin Verzeichnisse - - - - Application directory - Programmverzeichnis - - - - SQLite 3 version: - SQLite 3 Version: - - - - Configuration directory - Konfigurationsverzeichnis - - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - - Qt version: - Qt Version: - - - - Portable distribution. - Sollte hier vermutlich "Portable Version" heißen? - Portable Version. - - - - MacOS X application boundle distribution. - Das müsste mal genauer übersetzt werden. - MacOS X Programmbundle-Version. - - - - Operating system managed distribution. - Betriebssystemverwaltete Version. - - - - Copy - Kopie - - - - <h3>Table of contents:</h3><ol>%2</ol> - <h3>Inhaltsverzeichnis:</h3><ol>%2</ol> - - - - BindParamsDialog - - - Query parameters - - - - - Please provide values for query parameters - - - - - BugDialog - - Bugs and ideas - Fehler und Anregungen - - - Reporter - Gemeldet von - - - E-mail address - Ihre E-mail Adresse oder Ihr 'bugtracker' Login - - - Log in - Anmelden - - - Short description - Kurzbeschreibung - - - Detailed description - Ausführliche Fehlerbeschreibung - - - Show more details - Mehr Details - - - SQLiteStudio version - SQLiteStudio Version - - - Operating system - Betriebssystem - - - Loaded plugins - Geladene Plugins - - - Send - Absenden - - - You can see all your reported bugs and ideas by selecting menu '%1' and then '%2'. - Sie können Ihre gemeldeten Fehler und Anregungen sehen, wenn Sie im Menü '%1' den Eintrag '%2' auswählen. - - - A bug report sent successfully. - Ihr Fehlerbericht wurde erfolgreich versendet. - - - An error occurred while sending a bug report: %1 -%2 - Beim Absenden des Fehlerberichts ist ein Fehler aufgetreten: %1 %2 - - - You can retry sending. The contents will be restored when you open a report dialog after an error like this. - Sie können versuchen den Bericht erneut abzusenden. Ihr eingegebener Text wird nach einem Fehler wie diesem wieder hergestellt. - - - An idea proposal sent successfully. - Ihre Anregung wurde erfolgreich versendet. - - - An error occurred while sending an idea proposal: %1 -%2 - Beim Absenden der Anregung ist ein Fehler aufgetreten: %1 %2 - - - A bug report - Fehlerbericht erfassen - - - Describe problem in few words - Beschreiben Sie das Problem mit wenigen Worten - - - Describe problem and how to reproduce it - Beschreiben Sie das Problem hier genauer und die Schritte, um es zu reproduzieren - - - A new feature idea - Anregung zu einer neuen Funktion erfassen - - - A title for your idea - Ein kurzer Titel für ihre Anregung - - - Describe your idea in more details - Beschreiben Sie hier Ihre Anregung ausführlich - - - Reporting as an unregistered user, using e-mail address. - Versenden als nicht registrierter Benutzer mittels E-mail Adresse. - - - Reporting as a registered user. - Versenden als registrierter Benutzer. - - - Log out - Abmelden - - - Providing true email address will make it possible to contact you regarding your report. To learn more, press 'help' button on the right side. - Die Angabe Ihrer echten E-mail Adresse ermöglicht es uns Sie bzgl. Ihres Berichts zu kontaktieren. Erfahren Sie mehr dazu und klicken Sie den 'Hilfe' Knopf auf der rechtehn Seite. - - - Enter vaild e-mail address, or log in. - Geben Sie Ihre gültige E-mail Adresse oder Ihre Anmeldedaten an. - - - Short description requires at least 10 characters, but not more than 100. Longer description can be entered in the field below. - Eine Kurzbeschreibung benötigt mindestens 10 Zeichen, maximal jedoch 100 Zeichen. Eine ausführlichere Beschreibung kann in dem Feld unten erfasst werden. - - - Long description requires at least 30 characters. - Eine ausführliche Beschreibung benötigt mindestens 30 Zeichen. - - - - BugReportHistoryWindow - - Title - Titel - - - Reported at - Gemeldet am - - - URL - URL - - - Reports history - Berichtsverlauf - - - Clear reports history - Lösche Berichtsverlauf - - - Delete selected entry - Gewählten Eintrag löschen - - - Invalid response from server. - Ungültige Antwort vom Server. - - - - BugReportLoginDialog - - Log in - Anmelden - - - Credentials - Hier fehlt mir der Kontext!!! - Überprüfung - - - Login: - Login: - - - Password: - Passwort: - - - Validation - Überprüfung - - - Validate - Überprüfe - - - Validation result message - Ergebnis der Überprüfung - - - Abort - Abbrechen - - - A login must be at least 2 characters long. - Ein Login Kürzel muss mindestens 2 Zeichen lang sein. - - - A password must be at least 5 characters long. - Ein Passwort muss mindestens 5 Zeichen lang sein. - - - Valid - Gültig - - - - CollationsEditor - - - Filter collations - Kollationen filtern - - - - Collation name: - Kollationsname: - - - - Implementation language: - Sprache: - - - - Databases - Datenbanken - - - - Register in all databases - In allen Datenbanken registrieren - - - - Register in following databases: - In den folgenden Datenbanken registrieren: - - - - Implementation code: - Anweisungen: - - - - Collations editor - Editor für Kollationen - - - - Commit all collation changes - Speichern aller Änderungen an Kollationen - - - - Rollback all collation changes - Zurücknehmen aller Änderungen an Kollationen - - - - Create new collation - Neue Kollation erstellen - - - - Delete selected collation - Markierte Kollationen löschen - - - - Editing collations manual - Kollationen manuell editieren - - - - Enter a non-empty, unique name of the collation. - Geben Sie einen eindeutigen Namen für die Kollation ein. - - - - Pick the implementation language. - Wählen Sie die Sprache aus. - - - - Enter a non-empty implementation code. - Geben Sie eine eindeutige Vergleichsoperatorendefinition ein. - - - - Collations editor window has uncommitted modifications. - - - - Collations editor window has uncommited modifications. - Der Editorfür Kollationen enthält nicht gespeicherte Änderungen. - - - - ColorButton - - - Pick a color - Wählen Sie eine Farbe aus - - - - ColumnCollatePanel - - - Collation name: - Name der Kollation: - - - - Named constraint: - Name der Bedingung: - - - - Enter a name of the constraint. - Geben Sie einen Namen für die Bedingung ein. - - - - Enter a collation name. - Geben Sie einen Namen für die Kollation ein. - - - - ColumnDefaultPanel - - - Default value: - Standardwert: - - - - Named constraint: - Benannte Bedingung: - - - - Enter a default value expression. - Geben Sie einen Standardwert an. - - - - Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - - - - - Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - - - - Invalid default value expression: %1 - Ungültiger Standardwert für Ausdruck: %1 - - - - Enter a name of the constraint. - Geben Sie einen Namen für die Bedingung ein. - - - - ColumnDialog - - - Column - Spalte - - - - Name and type - Name und Typ - - - - Scale - Skalierung - - - - Precision - Präzision - - - - Data type: - Datentyp: - - - - Column name: - Spaltenname: - - - - Size: - Größe: - - - - Constraints - Bedingungen - - - - Unique - Eindeutigkeit - - - - - - - - - - Configure - Konfigurieren - - - - Foreign Key - Fremdschlüssel - - - - Collate - Kollationieren - - - - Not NULL - Nicht NULL - - - - Check condition - Zustandsprüfung - - - - Primary Key - Primärer Schlüssel - - - - Default - Standard - - - - Advanced mode - Erweiterter Modus - - - - Add constraint - column dialog - Bedingung hinzufügen - - - - Edit constraint - column dialog - Bedingung editieren - - - - - Delete constraint - column dialog - Bedingung löschen - - - - Move constraint up - column dialog - Bedingung nach oben verschieben - - - - Move constraint down - column dialog - Bedingung nach unten verschieben - - - - Add a primary key - column dialog - Primärschlüssel zufügen - - - - Add a foreign key - column dialog - Fremdschlüssel zufügen - - - - Add an unique constraint - column dialog - Eindeutige Bedingung hinzufügen - - - - Add a check constraint - column dialog - Prüfungsbedingung hinzufügen - - - - Add a not null constraint - column dialog - Nicht-NULL Bedingung hinzufügen - - - - Add a collate constraint - column dialog - Kollationsbedingung hinzufügen - - - - Add a default constraint - column dialog - Standardbedingung hinzufügen - - - - Are you sure you want to delete constraint '%1'? - column dialog - Sind Sie sicher, dass Sie die folgende Bedingung löschen wollen: '%1'? - - - - Correct the constraint's configuration. - Korrigiert die Konfiguration der Bedingung. - - - - This constraint is not officially supported by SQLite 2, -but it's okay to use it. - Diese Bedingung wird von SQLite 2 offiziell nicht unterstützt, aber sie kann dennoch benutzt werden. - - - - Scale is not allowed for INTEGER PRIMARY KEY columns. - Für INTEGER PRIMARY KEY ist eine Skalierung nicht erlaubt. - - - - Precision cannot be defined without the scale. - Die Präzision kann ohne Skalierung nicht definiert werden. - - - - Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - - - - - INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - - - - - Precision is not allowed for INTEGER PRIMARY KEY columns. - Für INTEGER PRIMARY KEY ist eine Präzision nicht erlaubt. - - - - ColumnDialogConstraintsModel - - - Type - column dialog constraints - Typ - - - - Name - column dialog constraints - Name - - - - Details - column dialog constraints - Details - - - - ColumnForeignKeyPanel - - - Foreign table: - Fremde Tabelle: - - - - Foreign column: - Fremde Spalte: - - - - Reactions - Reaktionen - - - - Deferred foreign key - Verzögerter Fremdschlüssel - - - - Named constraint - Benannte Bedingung - - - - Constraint name - Name der Bedingung - - - - Pick the foreign table. - Wählen Sie die Fremdtabelle aus. - - - - Pick the foreign column. - Wählen Sie die Fremdspalte aus. - - - - Enter a name of the constraint. - Geben Sie einen Namen für die Bedingung ein. - - - - ColumnPrimaryKeyPanel - - - Autoincrement - Automatisch hochzählend - - - - Sort order: - Sortierfolge: - - - - Named constraint: - Benannte Bedingung: - - - - On conflict: - Bei Konflikt: - - - - Enter a name of the constraint. - Geben Sie einen Namen für die Bedingung ein. - - - Autoincrement (only for %1 type columns) - column primary key - Automatische Zählung (nur für %1 Spaltentypen) - - - - ColumnUniqueAndNotNullPanel - - - Named constraint: - Benannte Bedingung: - - - - On conflict: - Bei Konflikt: - - - - Enter a name of the constraint. - Geben Sie einen Namen für die Bedingung ein. - - - - CompleterWindow - - - Column: %1 - completer statusbar - Spalte: %1 - - - - Table: %1 - completer statusbar - Tabelle: %1 - - - - Index: %1 - completer statusbar - Index: %1 - - - - Trigger: %1 - completer statusbar - Trigger: %1 - - - - View: %1 - completer statusbar - View: %1 - - - - Database: %1 - completer statusbar - Datenbank: %1 - - - - Keyword: %1 - completer statusbar - Schlüsselwort: %1 - - - - Function: %1 - completer statusbar - Funktion: %1 - - - - Operator: %1 - completer statusbar - Operator: %1 - - - - String - completer statusbar - Zeichenkette - - - - Number - completer statusbar - Nummer - - - - Binary data - completer statusbar - Binäre Daten - - - - Collation: %1 - completer statusbar - Kollation: %1 - - - - Pragma function: %1 - completer statusbar - Pragma Funktion: %1 - - - - ConfigDialog - - - - Configuration - Konfiguration - - - - Search - Suchen - - - - General - Allgemein - - - - Keyboard shortcuts - Tastaturkürzel - - - - Look & feel - Kurz und knackig - Layout - - - - Style - Stil - - - - Fonts - Schriftarten - - - - Colors - Farben - - - - Plugins - Plugins - - - - Code formatters - Codeformatierer - - - - Data browsing - Datenbearbeitung - - - - Data editors - Dateneditoren - - - - Database dialog window - Dialogfenster der Datenbank - - - - <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> - - - - - Do not mark database to be "permanent" by default - - - - - <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> - - - - - Try to bypass dialog completly when dropping database file onto the list - - - - - Data browsing and editing - Datenbearbeitung - - - - Number of data rows per page: - Anzahl an Datenzeilen pro Seite: - - - - - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - <p>Wenn Daten in das Ergebnisfenster eingelesen werden, dann wird die Breite der Spalten dabei automatisch angepasst. Dieser Wert begrenzt maximale Breite für die automatische Breitenanpassung. Der Anwender kann die Spaltenbreite jedoch manuell über dieses Limit verbreitern.</p> - - - - Limit initial data column width to (in pixels): - Begrenze die initiale Spaltenbreite im Ergebnisfenster auf (Pixel): - - - - Keep NULL value when entering empty value - - - - - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> - - - - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - - - - - Number of memorized table populating configurations - - - - - Show column and row details tooltip in data view - - - - - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - - - - - <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> - - - - - Use DEFAULT value (if defined), when committing NULL value - - - - - Inserting new row in data grid - Neue Zeile im Gitternetz des Datenfensters hinzufügen - - - - Before currently selected row - Vor der derzeitig ausgewählten Zeile - - - - After currently selected row - Nach der derzeitig ausgewählten Zeile - - - - At the end of data view - Am Ende der Datenfensters - - - - <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - <p>Wenn aktiviert, wird der Reiter "Daten" anstelle des Reiters "Struktur" angezeigt beim öffnen eines Tabellenfensters angezeigt.</p> - - - - <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - <p>Wenn aktiviert, wird der Reiter "Daten" als erster Reiter angezeigt für jedes Tabellenfenster, anstelle an zweiter Stelle.</p> - - - - Place data tab as first tab in a Table Window - Den Reiter Daten als ersten Reiter im Tabellenfenster anzeigen - - - - <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - - - - - <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - - - - - Place data tab as first tab in a View Window - Den Reiter Daten als ersten Reiter im View-Fenster anzeigen - - - - Data types - Datentypen - - - - Available editors: - Verfügbare Editoren: - - - - Editors selected for this data type: - Für diesen Datentyp ausgewählte Editoren: - - - - Schema editing - Schema - - - - Number of DDL changes kept in history. - Maximale Anzahl an DDL Änderungen im Verlauf. - - - - DDL history size: - DDL Verlaufsgröße: - - - Don't show DDL preview dialog when commiting schema changes - Zeige keine DDL Vorschau, wenn Schemaänderungen committed werden - - - - SQL queries - SQL Abfragen - - - - - Number of queries kept in the history. - Maximale Anzahl an SQL Abfragen im Verlauf. - - - - History size: - Verlaufsgröße: - - - - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> - <p>Wenn diese Option aktiviert ist und sich mehrere SQL Abfragen im Editorfenster befinden, dann wird nur die SQL Abfrage ausgeführt, in der sich der Cursor befindet. Ist diese Option nicht gesetzt, dann werden alle SQL Abfragen ausgeführt. Sie können die auszuführenden SQL Abfragen selbst bestimmen, indem Sie diese vor der Ausführung mit der Maus oder Tastatur markieren.</p> - - - - Execute only the query under the cursor - Führt nur die Abfrage unter dem Cursor aus - - - - Updates - Updates - - - - Automatically check for updates at startup - Prüfe vor dem Start automatisch auf Updates - - - - Session - Sitzung - - - - Restore last session (active MDI windows) after startup - Stelle letzte Sitzung nach dem Start wieder her (aktive MDI Fenster) - - - - Status Field - Statusfeld - - - - <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> - - - - - Always open Status panel when new message is printed - Den Panel Status immer öffnen, wenn eine neue Meldung ausgegeben wird - - - - Filter shortcuts by name or key combination - Filtere Tastaturkürzel nach Name oder Tastenkombination - - - - Action - Aktion - - - - Key combination - Tastenkombination - - - - - Language - Sprache - - - - Changing language requires application restart to take effect. - Die Änderung der Sprache erfordert einen Neustart des Programms. - - - - Compact layout - Kompaktes Layout - - - - <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - <p>Das kompakte Layout reduziert alle Lücken und Abstände der Oberfläche auf ein Minimum, um mehr Platz für die Darstellung der Daten zu schaffen. Die Oberfläche sieht dann zwar nicht mehr sehr ästhetisch aus, aber man hat mehr Daten im Überblick.</p> - - - - Use compact layout - Benutze kompaktes Layout - - - General.CompactLayout - Standard.KompaktesLayout - - - - - Database list - Liste der Datenbanken - - - - If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. - Wenn die Option deaktiviert ist, werden die Spalten in der Reihenfolge sortiert in der sie im CREATE TABLE Statement angegeben wurden. - - - - Sort table columns alphabetically - Tabellenspalten alphabetisch sortieren - - - - Expand tables node when connected to a database - Tabellenknoten aufklappen, wenn eine Datenbank verbunden ist - - - - <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> - <p>Zusätzliche Bezeichnungen sind jene, die neben den Namen der Datenbankliste angezeigt werden (sie sind normalerweise blau gefärbt, es sei denn dies wurde umkonfiguriert). Ist diese Option aktiviert, dann werden diese Bezeichnungen angezeigt für Datenbanken, ungültige Datenbanken und zusammengefasste Knoten (Spalten-, Index- und Triggergruppen). Für mehr Details siehe die folgenden optionen.<p> - - - - Display additional labels on the list - Zeige zusätzliche Bezeichnungen in der Liste an - - - - For regular tables labels will show number of columns, indexes and triggers for each of tables. - Für normale Tabellen enthält die Bezeichnung die Anzahl der Spalten, Indizes und Trigger einer jeden Tabelle. - - - - Display labels for regular tables - Zeigt Bezeichnungen für normale Tabellen an - - - - Virtual tables will be marked with a 'virtual' label. - Virtuelle Tabellen werden mit einem 'virtuell' Kürzel versehen. - - - - Display labels for virtual tables - Zeige Bezeichnungen für virtuelle Tabellen - - - - Expand views node when connected to a database - Knoten aufklappen, wenn eine Datenbank verbunden ist - - - - If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) - Wenn die Option deaktiviert ist, werden die Objekte in der Reihenfolge sortiert in der sie in der sqlite_master Tabelle angezeigt werden (entspricht der Reihenfolge in der sie angelegt worden sind) - - - - Sort objects (tables, indexes, triggers and views) alphabetically - Objekte alphabetisch sortieren - - - - Display system tables and indexes on the list - Zeige Systemtabellen und Indizes in der Liste an - - - - Table windows - Tabellenfenster - - - When enabled, Table Windows will show up with the data tab, instead of the structure tab. - Wenn die Option aktiviert ist, dann wird im Tabellenfenster der Reiter "Daten" angezeigt statt "Strukturen". - - - - Open Table Windows with the data tab for start - Öffnet das Tabellenfenster mit dem Reiter "Daten" im Vordergrund - - - - View windows - Viewfenster - - - When enabled, View Windows will show up with the data tab, instead of the structure tab. - Wenn die Option aktiviert ist, dann wird im Viewfenster der Reiter "Daten" angezeigt statt "Strukturen". - - - - Open View Windows with the data tab for start - Öffnet das Viewfenster mit dem Reiter "Daten" im Vordergrund - - - - Don't show DDL preview dialog when committing schema changes - - - - - - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - - - - - Number of memorized query parameters - - - - - Main window dock areas - Dockingbereiche des Hauptfensters - - - - Left and right areas occupy corners - Linke und rechte Bereiche belegen die Ecken - - - - Top and bottom areas occupy corners - Obere und untere Bereiche belegen die Ecken - - - - Hide built-in plugins - Verberge eingebaute Plugins - - - - Current style: - Aktueller Stil: - - - - Preview - Vorschau - - - - Enabled - Aktiviert - - - - Disabled - Deaktiviert - - - - Active formatter plugin - Aktives Formatierungsplugin - - - - SQL editor font - Schriftart des SQL Editors - - - - Database list font - Schriftart der Datenbankliste - - - - Database list additional label font - Zusätzliche Bezeichnungen in der Datenbankliste - - - - Data view font - Schriftart der Ergebnisansicht - - - - Status field font - Schriftart des Statusfelds - - - - SQL editor colors - Farben des SQL Editors - - - - Current line background - Hintergrundfarbe der aktuellen Zeile - - - - <p>SQL strings are enclosed with single quote characters.</p> - <p>SQL Zeichenketten sind mit einfachen Anführungszeichen umschlossen.</p> - - - - String foreground - Vordergrundfarbe von Zeichenketten - - - - <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - <p>Bind Parameter sind Platzhalter für Werte, die der Anwender eingibt. Sie haben dabei eine der folgenden Formen:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - - - - Bind parameter foreground - Vordergrundfarbe von Bind Parametern - - - - Highlighted parenthesis background - Hintergrundfarbe von hervorgehobener Klammern - - - - <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - <p>BLOB Werte sind hexadezimale Werte wie z.B.:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - - - - BLOB value foreground - Vordergrundfarbe von BLOB Werten - - - - Regular foreground - Reguläre Vordergrundfarbe - - - - Line numbers area background - Hintergrundfarbe der Zeilennummernleiste - - - - Keyword foreground - Vordergrundfarbe von Schlüsselwörtern - - - - Number foreground - Vordergrundfarbe von Ziffern - - - - Comment foreground - Vordergrundfarbe von Kommentaren - - - - <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> - <p>Gültige Objekte sind Namen von Tabellen, Indizes, Triggern oder Views die in der SQLite Datenbank existieren.</p> - - - - Valid objects foreground - Vordergrundfarbe von gültigen Objekten - - - - Data view colors - Farben der Ergebnisansicht - - - - <p>Any data changes will be outlined with this color, until they're committed to the database.</p> - - - - - Uncommitted data outline color - - - - - <p>In case of error while committing data changes, the problematic cell will be outlined with this color.</p> - - - - <p>Any data changes will be outlined with this color, until they're commited to the database.</p> - <p>Jede Datenänderung wird mit dieser Farbe kenntlich gemacht, bis die geänderten Daten in die Datenbank zurückgeschrieben worden sind.</p> - - - Uncommited data outline color - Rahmenfarbe von nicht gespeicherten Daten - - - <p>In case of error while commiting data changes, the problematic cell will be outlined with this color.</p> - <p>Tritt beim Speichern einer Änderung ein Problem auf, dann wird die problematische Zelle mit dieser Farbe markiert.</p> - - - - Commit error outline color - Rahmenfarbe für fehlerhafte Daten - - - - NULL value foreground - Vordergrundfarbe für NULL Werte - - - - Deleted row background - Hintergrundfarbe von gelöschten Zeilen - - - - Database list colors - Farben der Datenbankliste - - - - <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> - <p>Zusätzliche Bezeichnungen sind solche, die z.B. die SQLite Version oder die Anzahl an Einträgen in einer Baumliste usw. anzeigen.</p> - - - - Additional labels foreground - Vordergrundfarbe für zusätzliche Bezeichnungen - - - - Status field colors - Farben des Statusfelds - - - - Information message foreground - Vordergrundfarbe für Infomeldungen - - - - Warning message foreground - Vordergrundfarbe für Warnmeldungen - - - - Error message foreground - Vordergrundfarbe für Fehlermeldungen - - - - Description: - plugin details - Bezeichnung: - - - - Category: - plugin details - Kategorie: - - - - Version: - plugin details - Version: - - - - Author: - plugin details - Autor: - - - - Internal name: - plugin details - Interner Name: - - - - Dependencies: - plugin details - Abhängigkeiten: - - - - Conflicts: - plugin details - Konflikte: - - - - Plugin details - Plugin Details - - - - Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. - Plugins werden direkt beim Aktivieren/Deaktivieren geladen bzw. entfernt, die modifizierte Pluginliste wird jedoch erst beim Bestätigen und Schließen des Konfigurationsfensters gespeichert. - - - - %1 (built-in) - plugins manager in configuration dialog - %1 (eingebaut) - - - - Details - Details - - - - No plugins in this category. - Keine Plugins in dieser Kategorie. - - - - Add new data type - Neuen Datentypen zufügen - - - - Rename selected data type - Markierten Datentypen umbenennen - - - - Delete selected data type - Markierten Datentypen löschen - - - - Help for configuring data type editors - Hilfe zur Konfiguration des Datentypen Editors - - - - ConstraintCheckPanel - - - The condition - Der Zustand: - - - - Named constraint: - Benannte Bedingung: - - - - On conflict - Bei Konflikt - - - - Enter a valid condition. - Geben Sie einen gültigen Zustand ein. - - - - Enter a name of the constraint. - Geben Sie einen Namen für die Bedingung ein. - - - - ConstraintDialog - - - New constraint - constraint dialog - Neue Bedingung - - - - Create - constraint dialog - Erstellen - - - - Edit constraint - dialog window - Bedingung editieren - - - - Apply - constraint dialog - Übernehmen - - - - Primary key - table constraints - Primärer Schlüssel - - - - Foreign key - table constraints - Fremdschlüssel - - - - Unique - table constraints - Eindeutigkeit - - - - Not NULL - table constraints - Nicht NULL - - - - Check - table constraints - Prüfung - - - - Collate - table constraints - Kollation - - - - Default - table constraints - Standard - - - - ConstraintTabModel - - - Table - table constraints - Tabelle - - - - Column (%1) - table constraints - Spalte (%1) - - - - Scope - table constraints - Bereich - - - - Type - table constraints - Typ - - - - Details - table constraints - Details - - - - Name - table constraints - Name - - - - CssDebugDialog - - - SQLiteStudio CSS console - SQLiteStudio CSS Konsole - - - - DataView - - - Filter data - data view - Daten filtern - - - - Grid view - Gitteransicht - - - - Form view - Formularansicht - - - - Refresh table data - data view - Aktualisiere Tabellendaten - - - - First page - data view - Erste Seite - - - - Previous page - data view - Vorherige Seite - - - - Next page - data view - Nächste Seite - - - - Last page - data view - Letzte Seite - - - - Filter - - - - - Hit Enter key or press "Apply filter" button on toolbar to apply new value. - - - - - Show filter inputs per column - data view - - - - - Apply filter - data view - Filter anwenden - - - - Commit changes for selected cells - data view - Änderungen für die selektierten Zellen speichern - - - - Rollback changes for selected cells - data view - Änderungen für die selektierten Zellen zurücknehmen - - - - Show grid view of results - sql editor - Zeige Ergebnismenge in der Gitteransicht - - - - Show form view of results - sql editor - Zeige Ergebnismenge in der Formularansicht - - - - Filter by text - data view - Nach Text filtern - - - - Filter by the Regular Expression - data view - Nach regulärem Ausdruck filtern - - - - Filter by SQL expression - data view - Nach einem SQL Ausdruck filtern - - - - Tabs on top - data view - Reiterleiste oben - - - - Tabs at bottom - data view - Reiterleiste unten - - - - Place new rows above selected row - data view - Neue Zeilen über der ausgewählten Zeile einfügen - - - - Place new rows below selected row - data view - Neue Zeilen nach der ausgewählten Zeile einfügen - - - - Place new rows at the end of the data view - data view - Neue Zeilen am Ende des Datenfensters einfügen - - - - Total number of rows is being counted. -Browsing other pages will be possible after the row counting is done. - Gesamtanzahl der Zeilen wird ermittelt. -Das Aufrufen anderer Seiten ist erst nach Abschluss der Zählung möglich. - - - - Row: %1 - Zeile: %1 - - - - DbConverterDialog - - - Convert database - Konvertiere Datenbank - - - - Source database - Quelldatenbank - - - - Source database version: - Version der Quelldatenbank: - - - - Target database - Zieldatenbank - - - - Target version: - Version der Zieldatenbank: - - - - This is the file that will be created as a result of the conversion. - Dies ist die Datei, die durch die Konvertierung erzeut werden wird. - - - - Target file: - Zieldatei: - - - - Name of the new database: - Name der neuen Datenbank: - - - - This is the name that the converted database will be added to SQLiteStudio with. - Mit diesem Namen wird die konvertierte Datenbank SQLiteStudio zugefügt werden. - - - - Select source database - Quelldatenbank auswählen - - - - Enter valid and writable file path. - Geben Sie einen gültigen und beschreibbaren Dateipfad ein. - - - - Entered file exists and will be overwritten. - Die angegebene Datei existiert bereits und wird überschrieben werden. - - - - Enter a not empty, unique name (as in the list of databases on the left). - Geben Sie einen eindeutigen Namen an (so wie links in der Datenbankliste). - - - - No valid target dialect available. Conversion not possible. - Es ist kein gültiger Zieldialekt verfügbar. Die Konvertierung kann nicht durchgeführt werden. - - - - Select valid target dialect. - Wählen Sie einen gültigen Zieldialekt aus. - - - - Database %1 has been successfully converted and now is available under new name: %2 - Datenbank %1 wurde erfolgreich konvertiert und ist verfügbar unter dem neuen Namen: %2 - - - - SQL statements conversion - SQL Statements Konvertierung - - - - Following error occurred while converting SQL statements to the target SQLite version: - Folgender Fehler ist aufgetreten, während der Konvertierung von SQL Statements in die Ziel-SQLite Version: - - - - Would you like to ignore those errors and proceed? - Möchten Sie diese Fehler ignorieren und fortfahren? - - - - DbDialog - - - Database - Datenbank - - - - Database type - Datenbanktyp - - - - Database driver - Datenbanktreiber - - - Generate automatically - Automatisch generieren - - - - Options - Optionen - - - - Permanent (keep it in configuration) - Permanent (in der Konfiguration behalten) - - - - Test connection - Verbindung testen - - - - Create new database file - Neue Datenbank erzeugen - - - - - File - Datei - - - - Name (on the list) - Name (in der Liste) - - - Generate name basing on file path - Leitet den Namen vom Dateipfad ab - - - - <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> - aasfd - <p>Wenn diese Option aktiviert ist, wird die Datenbank in der Konfiguration gespeichert und bei jedem Start von SQLiteStudio wieder hergestellt.</p> - - - - Browse for existing database file on local computer - Lokalen Computer nach Datenbankdateien durchsuchen - - - - Browse - Durchsuchen - - - - Enter an unique database name. - Geben Sie einen eindeutigen Datenbanknamen ein. - - - - This name is already in use. Please enter unique name. - Der Name wird bereits benutzt, bitte geben Sie einen freien, eindeutigen Namen ein. - - - - <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - - - - - Enter a database file path. - Geben Sie einen Dateipfad für die Datenbank ein. - - - - This database is already on the list under name: %1 - Die Datenbank ist bereits unter folgendem Namen in der Liste enthalten: %1 - - - - Select a database type. - Wählen Sie einen Datebanktypen aus. - - - Auto-generated - Automatisch generiert - - - Type the name - Geben Sie den Namen ein - - - - DbObjectDialogs - - - Delete table - Tabelle löschen - - - - Are you sure you want to delete table %1? - Sind Sie sicher, dass Sie die Tabelle %1 löschen möchten? - - - - Delete index - Index löschen - - - - Are you sure you want to delete index %1? - Sind Sie sicher, dass Sie den Index %1 löschen möchten? - - - - Delete trigger - Trigger löschen - - - - Are you sure you want to delete trigger %1? - Sind Sie sicher, dass Sie den Trigger %1 löschen möchten? - - - - Delete view - View löschen - - - - Are you sure you want to delete view %1? - Sind Sie sicher, dass Sie den View %1 löschen möchten? - - - - - Error while dropping %1: %2 - Fehler beim Löschen: %1 %2 - - - - Delete objects - Objekte löschen - - - - Are you sure you want to delete following objects: -%1 - Sind Sie sicher, dass Sie die folgenden Objekte löschen möchten: -%1 - - - - Cannot start transaction. Details: %1 - Kann Transaktion nicht starten. Details: %1 - - - - Cannot commit transaction. Details: %1 - Kann Transaktion nicht ausführen. Details: %1 - - - - DbTree - - - Databases - Datenbanken - - - - Filter by name - Nach Name filtern - - - - Copy - Kopieren - - - - Paste - Einfügen - - - - Select all - Alles auswählen - - - - Create a group - Gruppe erstellen - - - - Delete the group - Diese Gruppe löschen - - - - Rename the group - Gruppe umbenennen - - - Add a database - Datenbank hinzufügen - - - Edit the database - Datenbank editieren - - - Remove the database - Datenbank entfernen - - - Connect to the database - Mit der Datenbank verbinden - - - Disconnect from the database - Verbindung zur Datenbank trennen - - - - Import - Import - - - Export the database - Datenbank exportieren - - - Convert database type - Datenbanktyp konvertieren - - - Vacuum - ??? - Vakuum - - - Integrity check - Integritätsprüfung - - - Create a table - Tabelle erstellen - - - Edit the table - Tabelle editieren - - - Delete the table - Tabelle löschen - - - - Export the table - Tabelle exportieren - - - - Import into the table - In die Tabelle importieren - - - - Populate table - Tabelle füllen - - - - Create similar table - Erzeuge identische Tabelle - - - - Reset autoincrement sequence - Automatischen Zähler zurücksetzen - - - Create an index - Index erstellen - - - Edit the index - Index editieren - - - Delete the index - Index löschen - - - Create a trigger - Trigger erstellen - - - Edit the trigger - Trigger editieren - - - Delete the trigger - Trigger löschen - - - Create a view - View erstellen - - - Edit the view - View editieren - - - Delete the view - View löschen - - - - Add a column - Spalte zufügen - - - - Edit the column - Spalte editieren - - - - Delete the column - Spalte löschen - - - - Delete selected items - Gewählte Einträge löschen - - - - Clear filter - Filter zurücksetzen - - - Refresh all database schemas - Alle Datenbankschemen aktualisieren - - - Refresh selected database schema - Alle markierten Datenbankschemen aktualisieren - - - - Execution from file cancelled. Any queries executed so far have been rolled back. - - - - - &Add a database - - - - - &Edit the database - - - - - &Remove the database - - - - - &Connect to the database - - - - - &Disconnect from the database - - - - - &Export the database - - - - - Con&vert database type - - - - - Vac&uum - - - - - &Integrity check - - - - - Create a &table - - - - - Edit the t&able - - - - - Delete the ta&ble - - - - - Create an &index - - - - - Edit the i&ndex - - - - - Delete the in&dex - - - - - Create a trig&ger - - - - - Edit the trigg&er - - - - - Delete the trigge&r - - - - - Create a &view - - - - - Edit the v&iew - - - - - Delete the vi&ew - - - - - &Refresh all database schemas - - - - - Re&fresh selected database schema - - - - - - Erase table data - Tabellendaten löschen - - - - Open file's directory - - - - - Execute SQL from file - - - - - - Database - Datenbank - - - - Grouping - Gruppieren - - - - Generate query for table - Abfrage für Tabelle generieren - - - - - Create group - Gruppe erstellen - - - - Group name - Gruppenname - - - - Entry with name %1 already exists in group %2. - Der Eintrag mit Namen %1 existiert bereits in der Gruppe %2. - - - - Delete group - Gruppe löschen - - - - Are you sure you want to delete group %1? -All objects from this group will be moved to parent group. - Sind Sie sicher, dass Sie die Gruppe %1 löschen möchten? -Alle Objekte in dieser Gruppe werden in die übergeordnete Gruppe verschoben. - - - - Are you sure you want to remove database '%1' from the list? - Sind Sie sicher, dass Sie die Datenbank '%1' aus der Liste entfernen möchten? - - - - Are you sure you want to remove following databases from the list: -%1 - Sind Sie sicher, dass Sie folgende Datenbanken aus der Liste entfernen möchten: -%1 - - - - Remove database - Datenbank entfernen - - - - Vacuum (%1) - Vacuum (%1) - - - - Autoincrement value for table '%1' has been reset successfully. - - - - - Are you sure you want to delete all data from table(s): %1? - - - - - Could not execute SQL, because application has failed to start transaction: %1 - - - - - Could not open file '%1' for reading: %2 - Die Datei '%1' kann nicht für Lesezugriffe geöffnet werden: %2 - - - - Could not execute SQL, because application has failed to commit the transaction: %1 - - - - - Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - - - - - Finished executing %1 queries in %2 seconds. - - - - - Could not execute SQL due to error. - - - - Delete database - Datenbank löschen - - - Are you sure you want to delete database '%1'? - http://bugs.sqlitestudio.pl/?id=3066 changed according to this - Sind Sie sicher, dass Sie die Datenbank '%1' entfernen möchten? - - - - - Cannot import, because no import plugin is loaded. - Der Import kann nicht durchgeführt werden, da kein Import Plugin geladen ist. - - - - - Cannot export, because no export plugin is loaded. - Export fehlgeschlagen, da kein Export Plugins geladen sind. - - - Error while executing VACUUM on the database %1: %2 - Fehler beim Ausführen des VACUUM-Befehls auf die Datenbank %1: %2 - - - VACUUM execution finished successfully. - VACUUM erfolgreich abgeschlossen. - - - - Integrity check (%1) - Integritätsprüfung (%1) - - - - Reset autoincrement - Autoincrement zurücksetzen - - - - Are you sure you want to reset autoincrement value for table '%1'? - Sind Sie sicher, dass Sie den Autoincrement Wert für die Tabelle '%1' zurücksetzen möchten? - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - Ein Fehler ist aufgetreten beim Zurücksetzen des Autoincrementwertes für die Tabelle '%1': %2 - - - Autoincrement value for table '%1' has been reset successfly. - Autoincrementwert für die Tabelle '%1' wurde erfolgreich zurückgesetzt. - - - Are you sure you want to delete all data from table '%1'? - Sind Sie sicher, dass Sie alle Daten der Tabelle '%1' löschen möchten? - - - - An error occurred while trying to delete data from table '%1': %2 - Beim Löschen der Daten aus Tabelle '%1' ist folgender Fehler aufgetreten: %2 - - - - All data has been deleted for table '%1'. - Es wurden alle Daten aus Tabelle '%1' gelöscht. - - - - Following objects will be deleted: %1. - Folgende Objekte werden gelöscht: %1. - - - - Following databases will be removed from list: %1. - Folgende Datenbanken werden aus der Liste entfernt: %1. - - - - Remainig objects from deleted group will be moved in place where the group used to be. - Die aus der gelöschten Gruppe verbleibenden Objekte werden an die Position der gelöschten Gruppe verschoben. - - - - %1<br><br>Are you sure you want to continue? - %1<br><br>Sind Sie sicher, dass Sie fortfahren möchten? - - - - Delete objects - Objekte löschen - - - - DbTreeItemDelegate - - - error - dbtree labels - Fehler - - - - (system table) - database tree label - (Systemtabelle) - - - - (virtual) - virtual table label - (Virtual) - - - - (system index) - database tree label - (Systemindex) - - - - DbTreeModel - - - Database: %1 - dbtree tooltip - Datenbank: %1 - - - - Version: - dbtree tooltip - Version: - - - - File size: - dbtree tooltip - Dateigröße: - - - - Encoding: - dbtree tooltip - Kodierung: - - - - Error: - dbtree tooltip - Fehlerbeschreibung: - - - - Table : %1 - dbtree tooltip - Tabelle: %1 - - - - Columns (%1): - dbtree tooltip - Spalten (%1): - - - - Indexes (%1): - dbtree tooltip - Indizes (%1): - - - - Triggers (%1): - dbtree tooltip - Trigger (%1): - - - - Copy - Kopieren - - - - Move - Verschieben - - - - Include data - Inklusive Daten - - - - Include indexes - Inklusive Indizes - - - - Include triggers - Inklusive Trigger - - - - Abort - Abbrechen - - - - Could not add dropped database file '%1' automatically. Manual setup is necessary. - - - - - Referenced tables - Referenzierte Tabellen - - - - Do you want to include following referenced tables as well: -%1 - Möchten Sie die folgenden referenzierten Tabellen mit einbeziehen? %1 - - - - Name conflict - Namenskonflikt - - - - Following object already exists in the target database. -Please enter new, unique name, or press '%1' to abort the operation: - Folgende Objekte existieren bereits in der Datenbank. -Bitte geben Sie einen neuen, eindeutigen Namen an oder drücken Sie %1, um den Vorgang abzubrechen: - - - - SQL statements conversion - SQL Statement Konvertierung - - - - Following error occurred while converting SQL statements to the target SQLite version: - Folgender Fehler trat auf bei der Konvertierung von SQL Statements in die SQLite Zielversion: - - - - Would you like to ignore those errors and proceed? - Möchten Sie diese Fehler ignorieren und fortfahren? - - - - DdlHistoryWindow - - - Filter by database: - Nach Datenbank filtern: - - - - -- Queries executed on database %1 (%2) --- Date and time of execution: %3 -%4 - -- Abfragen werden ausgeführt auf Datenbank %1 (%2) --- Datum und Ausführungszeitpunkt: %3 -%4 - - - - DDL history - DDL Verlauf - - - - DdlPreviewDialog - - - Queries to be executed - Auszuführende Abfragen - - - - Don't show again - Nicht wieder anzeigen - - - - DebugConsole - - - SQLiteStudio Debug Console - SQLiteStudio Debug Konsole - - - - EditorWindow - - - Query - Abfrage - - - - History - Verlauf - - - - Results in the separate tab - Ergebnisse in separatem Reiter - - - - Results below the query - Ergebnisse unter der Abfrage - - - - - SQL editor %1 - SQL Editor %1 - - - - Results - Ergebnisse - - - - Execute query - Abfrage ausführen - - - - Explain query - Abfrage ausführen (explain) - - - - Clear execution history - sql editor - Ausführungsverlauf löschen - - - - Export results - sql editor - Ergebnisse exportieren - - - - Create view from query - sql editor - View aus der Abfrage erstellen - - - - Previous database - Vorherige Datenbank - - - - Next database - Nächste Datenbank - - - - Show next tab - sql editor - Nächsten Reiter zeigen - - - - Show previous tab - sql editor - Vorherigen Reiter zeigen - - - - Focus results below - sql editor - Fokus auf die Ergebnisse unten - - - - Focus SQL editor above - sql editor - Fokus auf den SQL Editor oben - - - - Delete selected SQL history entries - sql editor - - - - - Active database (%1/%2) - Aktive Datenbank (%1/%2) - - - - Query finished in %1 second(s). Rows affected: %2 - Abfrage in %1 Sekunde(n) abgeschlossen. %2 Zeile(n) betroffen - - - - Query finished in %1 second(s). - Abfrage in %1 Sekunde(n) abgeschlossen. - - - - Clear execution history - Lösche Ausführungsverlauf - - - - Are you sure you want to erase the entire SQL execution history? This cannot be undone. - Sind Sie sicher, dass Sie den gesamten SQL Ausführungsverlauf löschen möchten? Dieser Vorgang kann nicht rückgängig gemacht werden. - - - - Cannot export, because no export plugin is loaded. - Es kann nicht exportiert werden, da kein Export Plugin geladen ist. - - - - No database selected in the SQL editor. Cannot create a view for unknown database. - Es ist keine Datenbank im SQL Editor selektiert. Für eine unbekannte Datenbank kann kein View erzeugt werden. - - - - Editor window "%1" has uncommitted data. - - - - Editor window "%1" has uncommited data. - Das Editorfenster "%1" hat ungespeicherte Daten. - - - - ErrorsConfirmDialog - - - Errors - Fehler - - - - Following errors occured: - Folgende Fehler sind aufgetreten: - - - - Would you like to proceed? - Möchten Sie fortsetzen? - - - - ExecFromFileDialog - - - Execute SQL from file - - - - - Input file - - - - - Path to file - - - - - Browse for file - - - - - Options - Optionen - - - - File encoding - - - - - Skip failing SQL statements - - - - - SQL scripts (*.sql);;All files (*) - SQL Skripte (*.sql);;Alle Dateien (*) - - - - Execute SQL file - - - - - Please provide file to be executed. - - - - - Provided file does not exist or cannot be read. - - - - - ExportDialog - - - Export - Exportieren - - - - What do you want to export? - Was möchten Sie exportieren? - - - - A database - Eine Datenbank - - - - A single table - Eine einzelne Tabelle - - - - Query results - Abfrageergebnisse - - - - Table to export - Zu exportierende Tabelle - - - - Database - Datenbank - - - - Table - Tabelle - - - - Options - Optionen - - - - When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. - Wenn die Option deaktiviert ist, dann wird nur das Tabellen DDL (CREATE TABLE Statement) exportiert. - - - - Export table data - Tabellendaten exportieren - - - - Export table indexes - Tabellenindizes exportieren - - - - Export table triggers - Tabellentrigger exportieren - - - - Note, that exporting table indexes and triggers may be unsupported by some output formats. - Hinweis: Das Exportieren von Tabellen, Indizes und Triggern könnte von einigen Ausgabeformaten nicht unterstützt werden. - - - - Select database objects to export - Wählen Sie die zu exportierenden Datebankobjekte aus - - - - Export data from tables - Daten aus Tabellen exportieren - - - - Select all - Alles auswählen - - - - Deselect all - Auswahl aufheben - - - - - Database: - Datenbank: - - - - Query to export results for - Abfrage deren Ergebnisse exportiert werden sollen - - - - Query to be executed for results: - ??? - Auszuführende Abfrage... : - - - - Export format and options - Exportformat und Optionen - - - - Export format - Exportformat - - - - Output - Ausgabe - - - - Exported file path - ??? - Exportverzeichnis - - - - Clipboard - Zwischenablage - - - - File - Datei - - - - Exported text encoding: - Exportierte Textkodierung: - - - - Export format options - Optionen des Exportformats - - - - Cancel - Abbrechen - - - - - - Select database to export. - Wählen Sie die zu exportierenden Datebank aus. - - - - Select table to export. - Wählen Sie die zu exportierenden Tabellen aus. - - - - Enter valid query to export. - ??? - Geben Sie eine gültige Abfrage für den Export an. - - - - Select at least one object to export. - Wählen Sie ein zu exportierendes Datebankobjekt aus. - - - - You must provide a file name to export to. - Sie müssen einen Namen für die Exportdatei angeben. - - - - Path you provided is an existing directory. You cannot overwrite it. - Das von Ihnen angegebene Verzeichnis existiert bereits. Es kann nicht überschrieben werden. - - - - The directory '%1' does not exist. - Das Verzeichnis '%1' existiert nicht. - - - - The file '%1' exists and will be overwritten. - Die Datei '%1' existiert bereits und wird überschrieben werden. - - - - All files (*) - Alle Dateien (*) - - - - Pick file to export to - Wählen Sie eine Datei aus in die exportiert werden soll - - - - Internal error during export. This is a bug. Please report it. - Es trat ein interner Fehler während des Exportvorgangs auf. Dies ist ein Fehler, bitte melden Sie ihn dem Programmautor. - - - - FileExecErrorsDialog - - - Execution errors - - - - - Following errors were encountered during execution of SQL statements from the file: - - - - - SQL - - - - - Error - Fehler - - - - Statements that were executed successfully were commited. - - - - - Statements that were executed successfully were rolled back. - - - - - FontEdit - - - Choose font - font configuration - Schriftart auswählen - - - - Form - - - Active SQL formatter plugin - Aktives SQL Formatierungsplugin - - - - FormView - - - Commit row - form view - Zeile speichern (Commit) - - - - Rollback row - form view - Zeile rückgängig (Rollback) - - - - First row - form view - Erste Zeile - - - - Previous row - form view - Vorherige Zeile - - - - Next row - form view - Nächste Zeile - - - - Last row - form view - Letzte Zeile - - - - Insert new row - form view - Neue Zeile einfügen - - - - Delete current row - form view - Aktuelle Zeile löschen - - - - FunctionsEditor - - - Filter funtions - Filterfunktionen - - - - Function name: - Funktionsname: - - - - Implementation language: - Implementationssprache: - - - - Type: - Typ: - - - - Input arguments - Eingabeargumente - - - - Undefined - Undefiniert - - - - Databases - Datenbanken - - - - Register in all databases - In allen Datenbanken registrieren - - - - Register in following databases: - In den folgenden Datenbanken registrieren: - - - - Initialization code: - Initialisierungsanweisungen: - - - - - Function implementation code: - Funktionsanweisungen: - - - - Final step implementation code: - Abschlussanweisungen: - - - - SQL function editor - SQL Funktionseditor - - - - Commit all function changes - Speichern aller Funktionsänderungen - - - - Rollback all function changes - Zurücknehmen aller Funktionsänderungen - - - - Create new function - Neue Funktion erstellen - - - - Delete selected function - Ausgewählte Funktion löschen - - - - Custom SQL functions manual - Anleitung zu 'Benutzerdefinierte SQL Funktionen' - - - - Add function argument - Funktionsargument zufügen - - - - Rename function argument - Funktionsargument umbenennen - - - - Delete function argument - Funktionsargument löschen - - - - Move function argument up - Funktionsargument hochschieben - - - - Move function argument down - Funktionsargument runterschieben - - - - Scalar - Skalar - - - - Aggregate - Aggregat - - - - Enter a non-empty, unique name of the function. - Geben Sie einen eindeutigen Namen für die Funktion ein. - - - - Pick the implementation language. - Wählen Sie die Sprache aus. - - - - Per step code: - evtl. Einzelschrittanweisung??? (Artur: hört sich gut an) - Einzelschrittanweisung: - - - - Enter a non-empty implementation code. - Geben Sie die Anweisungen ein. - - - - argument - new function argument name in function editor window - Argument - - - - Functions editor window has uncommitted modifications. - - - - Functions editor window has uncommited modifications. - Der Editorfür Funktionen enthält nicht gespeicherte Änderungen. - - - - ImportDialog - - - Import data - Daten importieren - - - - Table to import to - Tabelle in die importiert werden soll - - - - Table - Tabelle - - - - Database - Datenbank - - - - Data source to import from - Datenquelle von der aus importiert werden soll - - - - Data source type - Datenquellentyp - - - - Options - Optionen - - - - Input file: - Eingabedatei: - - - - Text encoding: - Textkodierung: - - - - <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> - <p>Wenn diese Option aktiviert ist, wird jede Verletzung von Bedingungen oder ein ungültiges Datenformat (falsche Anzahl an Spalten) oder jedes andere Problem, das während des Imports auftritt, ignoriert und der Import wird fortgesetzt.</p> - - - - Ignore errors - Fehler ignorieren - - - - Data source options - Datenquellenoptionen - - - - Cancel - Abbrechen - - - - If you type table name that doesn't exist, it will be created. - Wenn Sie einen Tabellenname eingeben, der noch nicht existiert, dann wird diese neue Tabelle erzeugt werden. - - - - Enter the table name - Datenbankname eingeben - - - - Select import plugin. - Importplugin auswählen - - - - You must provide a file to import from. - Sie müssen den Namen der Importdatei angeben. - - - - The file '%1' does not exist. - Die Datei '%1' existiert nicht. - - - - Path you provided is a directory. A regular file is required. - Der von Ihnen angegebene Pfad ist ein Verzeichnis. Es wird jedoch eine Datei benötigt. - - - - Pick file to import from - Wählen Sie eine Datei aus von der importiert werden soll. - - - - IndexDialog - - - - Index - Index - - - - On table: - Auf Tabelle: - - - - Index name: - Indexname: - - - - Partial index condition - Partieller Indexzustand - - - - Unique index - Einzigartiger Index - - - - Column - Spalte - - - - Collation - Kollation - - - - Sort - Sortierung - - - - Delete selected indexed expression - - - - - Moves selected index column up in the order, making it more significant in the index. - - - - - Moves selected index column down in the order, making it less significant in the index. - - - - - Edit selected indexed expression - - - - - Add indexed expression - - - - - DDL - DDL - - - - Tried to open index dialog for closed or inexisting database. - Es wurde versucht den Index-Dialog für eine geschlossene oder nicht existente Datenbank zu öffnen. - - - - Could not process index %1 correctly. Unable to open an index dialog. - Der Index %1 kann nicht vollständig bearbeitet werden, da der Index-Dialog nicht geöffnet werden kann. - - - - Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. - - - - - Pick the table for the index. - Tabelle für den Index auswählen. - - - - Select at least one column. - Mindestens eine Spalte auswählen. - - - - Enter a valid condition. - Geben Sie einen gültigen Zustand ein. - - - - default - index dialog - Standard - - - - Sort order - table constraints - Sortierung - - - - - Error - index dialog - Fehler - - - - Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? - Der eindeutige Index kann nicht erzeigt werden, da Werte in den selektierten Spalten nicht eundeutig sind. Möchten Sie die zugehörige SELECT Abfrage ausführen, um die uneindeutigen Werte zu sehen? - - - - An error occurred while executing SQL statements: -%1 - Fehler beim Ausführen des folgenden SQL Statments: -%1 - - - - IndexExprColumnDialog - - - Indexed expression - - - - - Expression to index - - - - - This expression is already indexed by the index. - - - - - Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. - - - - - Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. - - - - - It's forbidden to use 'SELECT' statements in indexed expressions. - - - - - Enter an indexed expression. - - - - - Invalid expression. - - - - - LanguageDialog - - - Language - Sprache - - - - Please choose language: - Bitte Sprache auswählen: - - - - MainWindow - - - Database toolbar - Datenbankleiste - - - - Structure toolbar - Bearbeitungsleiste - - - - Tools - Werkzeuge - - - - Window list - Fensterliste - - - - View toolbar - Ansichtenleiste - - - - Configuration widgets - Konfigurationshelfer - - - - Syntax highlighting engines - Syntaxhervorhebungen - - - - Data editors - Dateneditoren - - - - Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - Ablauf im Debugmodus. Zum Öffnen der Debugkonsole drücken Sie %1 oder wählen Menü 'Hilfe' den Eintrag 'Debugkonsole öffnen' aus. - - - - Running in debug mode. Debug messages are printed to the standard output. - Ablauf im Debugmodus. Debugmeldungen werden in der Standardausgabe angezeigt.. - - - - You need to restart application to make the language change take effect. - Das Programm muss neu gestartet werden, damit die Änderung der Sprache wirksam wird. - - - Open SQL editor - SQL Editor öffnen - - - Open DDL history - DDL Verlauf öffnen - - - Open collations editor - Editor für Kollationen öffnen - - - Import - Importieren - - - Export - Exportieren - - - Open configuration dialog - Einstellungen - - - Tile windows - Alle Fenster aufteilen - - - Tile windows horizontally - Alle Fenster horizontal aufteilen - - - Tile windows vertically - Alle Fenster vertikal aufteilen - - - Cascade windows - Alle Fenster kaskadiert aufteilen - - - - Next window - Nächstes Fenster - - - - Previous window - Vorheriges Fenster - - - - Hide status field - Statusfeld verbergen - - - Close selected window - Ausgewähltes Fenster schließen - - - Close all windows but selected - Alle anderen Fenster schließen - - - Close all windows - Alle Fenster schließen - - - Restore recently closed window - Zuletzt geöffnetes Fenster wiederherstellen - - - Rename selected window - Ausgewähltes Fenster umbenennen - - - - Open Debug Console - Debug Konsole öffnen - - - - Open CSS Console - CSS Konsole öffnen - - - Report a bug - Fehler melden - - - Propose a new feature - Eine neue Programmfunktion vorschlagen - - - About - Über SQLiteStudio - - - Licenses - Lizenzen - - - Open home page - Homepage aufrufen - - - Open forum page - Forum aufrufen - - - User Manual - Bedienungsanleitung - - - SQLite documentation - SQLite Dokumentation - - - Report history - Verlauf gemeldeter Fehler - - - Check for updates - Auf Updates prüfen - - - Database - menubar - Datenbank - - - Structure - menubar - Struktur - - - View - menubar - Ansicht - - - - Window list - menubar view menu - Fensterliste - - - Tools - menubar - Werkzeuge - - - Help - Hilfe - - - - Open SQL &editor - - - - - Open DDL &history - - - - - Open SQL &functions editor - - - - - Open &collations editor - - - - - Open ex&tension manager - - - - - &Import - - - - - E&xport - - - - - Open confi&guration dialog - - - - - &Tile windows - - - - - Tile windows &horizontally - - - - - Tile windows &vertically - - - - - &Cascade windows - - - - - Close selected &window - - - - - Close all windows &but selected - - - - - Close &all windows - - - - - Re&store recently closed window - - - - - &Rename selected window - - - - - Report a &bug - - - - - Propose a new &feature - - - - - &About - - - - - &Licenses - - - - - Open home &page - - - - - Open fo&rum page - - - - - User &Manual - - - - - SQLite &documentation - - - - - Bugs and feature &requests - - - - - Check for &updates - - - - - &Database - menubar - - - - - &Structure - menubar - - - - - &View - menubar - - - - - &Tools - menubar - - - - - &Help - - - - - Could not set style: %1 - main window - Der folgende Stil kann nicht gesetzt werden: %1 - - - - Cannot export, because no export plugin is loaded. - Es kann nicht exportiert werden, da kein Export Plugin geladen ist. - - - - Cannot import, because no import plugin is loaded. - Es kann nicht importiert werden, da kein Import Plugin geladen ist. - - - - Rename window - Fenster umbenennen - - - - Enter new name for the window: - Neuen Namen für das Fenster eingeben: - - - - New updates are available. <a href="%1">Click here for details</a>. - Neues Update verfügbar. <a href="%1">Weitere Details</a>. - - - - You're running the most recent version. No updates are available. - Sie haben bereits die aktuellste Version. Keine Update verfügbar. - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - Die Datenbank, die mittels Programmparameter übergeben wurde (%1), war bereits in der Liste unter dem Namen %2 vorhanden. - - - - Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 - Die Datenbank, die mittels Programmparameter übergeben wurde (%1), wurde in der Liste termporär unter dem Namen %2 zugefügt. - - - - Could not add database %1 to list. - Die Datenbank %1 konnte nicht hinzugefügt werden. - - - - MdiWindow - - Uncommited changes - Nicht gespeicherte Änderungen - - - - Uncommitted changes - - - - - Close anyway - Trotzdem schließen - - - - Don't close - Nicht schließen - - - - MultiEditor - - - Null value - multieditor - NULL Wert - - - - Configure editors for this data type - Konfigurationseditoren für diesen Datentyp - - - - Open another tab - - - - - Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. - Das Dateneditor Plugin '%1' ist nicht geladen, obwohl es für den '%1' Datentypen als Editor definiert ist. - - - - Deleted - multieditor - Gelöscht - - - - Read only - multieditor - Nur lesend - - - - MultiEditorBool - - Boolean - Boolean - - - - MultiEditorBoolPlugin - - - Boolean - Boolean - - - - MultiEditorDate - - Date - Datum - - - - MultiEditorDatePlugin - - - Date - Datum - - - - MultiEditorDateTime - - Date & time - Datum & Zeit - - - - MultiEditorDateTimePlugin - - - Date & time - Datum & Zeit - - - - MultiEditorHex - - Hex - Hexadezimal - - - - MultiEditorHexPlugin - - - Hex - Hexadezimal - - - - MultiEditorNumeric - - Number - numeric multi editor tab name - Nummer - - - - MultiEditorNumericPlugin - - - Number - numeric multi editor tab name - Nummer - - - - MultiEditorText - - Text - Text - - - - Tab changes focus - Hier fehlt mir der Kontext... Nacharbeiten nötig. - Reiter Änderungen Fokus - - - - Cut - Ausschneiden - - - - Copy - Kopieren - - - - Paste - Einfügen - - - - Delete - Löschen - - - - Undo - Rückgängig - - - - Redo - Wiederholen - - - - MultiEditorTextPlugin - - - Text - Text - - - - MultiEditorTime - - Time - Zeit - - - - MultiEditorTimePlugin - - - Time - Zeit - - - - NewConstraintDialog - - - New constraint - Neue Bedingung - - - - - Primary Key - new constraint dialog - Primärschlüssel - - - - - Foreign Key - new constraint dialog - Fremdschlüssel - - - - - Unique - new constraint dialog - Einzigartig - - - - - Check - new constraint dialog - Prüfung - - - - Not NULL - new constraint dialog - Nicht NULL - - - - Collate - new constraint dialog - Kollation - - - - Default - new constraint dialog - Standard - - - - NewVersionDialog - - - SQLiteStudio updates - SQLiteStudio Updates - - - - New updates are available! - Neues Update verfügbar! - - - - Component - Komponente - - - - This application will be closed and the update installer will start to download and install all the updates. - - - - Current version - Derzeitige Version - - - - Update version - Neue Version - - - - Check for updates on startup - Beim Programmstart auf Updates prüfen - - - - Update to new version! - Auf neue Version aktualisieren! - - - The update will be automatically downloaded and installed. This will also restart application at the end. - Das Update wird automatisch heruntergeladen und installiert. Die Anwendung wird daraufhin neugestartet. - - - - Not now. - Nicht jetzt. - - - - Don't install the update and close this window. - Update nicht installieren und Fenster schließen. - - - - PopulateConfigDialog - - - Populating configuration - Konfiguration auffüllen - - - - Configuring <b>%1</b> for column <b>%2</b> - Konfiguriere <b>%1</b> für Spalte <b>%2</b> - - - - PopulateDialog - - - Populate table - Tabelle füllen - - - - Database - Datenbank - - - - Table - Tabelle - - - - Columns - Spalten - - - - Number of rows to populate: - Anzahl an Datenzeilen zum Auffüllen: - - - - Populate - populate dialog button - Füllen - - - - Abort - Abbrechen - - - - Configure - Konfigurieren - - - - Populating configuration for this column is invalid or incomplete. - Die Konfigurationsauffüllung für diese Spalte ist ungültig oder unvollständig. - - - - Select database with table to populate - Wählen Sie die Datebank und Tabelle zum Auffüllen aus - - - - Select table to populate - Wählen Sie die Tabelle zum Auffüllen aus - - - - You have to select at least one column. - Sie müssen mindestens eine Spalte auswählen. - - - - QObject - - - Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). - Spalten, die das Ergebnis von verbundenen %1 Abfragen sind (solche, die %2, %3 oder %4 Schlüsselwörter enthalten), können nicht editiert werden. - - - - The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. - Der Ausführungsmechanismus hat Probleme die ROWID korrekt zu extrahieren. Dies könnte ein Programmfehler sein, den Sie evtl. melden möchten. - - - - Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. - Die betreffende Spalte ist das Ergebnis eines SQL-Ausdrucks statt einer einfachen Spaltenselektion. Solche Spalten können nicht editiert werden. - - - - Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. - Die betreffende Spalte gehört zu einer eingeschränkten SQLite Tabelle. Solche Tabellen können nicht direkt editiert werden. - - - - Cannot edit results of query other than %1. - Es können keine Ergebnisse von einer von %1 abweichenden Abfrage editiert werden. - - - - Cannot edit columns that are result of aggregated %1 statements. - Es können keine Spalten editiert werden, die das Ergebnis einer aggregierten %1 Abfrage sind. - - - - Cannot edit columns that are result of %1 statement. - Es können keine Spalten editiert werden, die das Ergebnis von %1 Abfragen sind. - - - - Cannot edit columns that are result of common table expression statement (%1). - Es können keine Spalten editiert werden, die das Ergebnis von allgemeinen Tabellenausdrücken sind (%1). - - - - - - - on conflict: %1 - data view tooltip - Bei Konfikt: %1 - - - - references table %1, column %2 - data view tooltip - Referenztabelle %1, Zeile %2 - - - - condition: %1 - data view tooltip - Zustand: %1 - - - - collation name: %1 - data view tooltip - Name der Kollation: %1 - - - - Data grid view - Ergebnisansicht - - - - Copy cell(s) contents to clipboard - Kopiert Zelleninhalt(e) in die Zwischenablage - - - - Copy cell(s) contents together with header to clipboard - - - - - Paste cell(s) contents from clipboard - Fügt Zelleninhalt(e) von der Zwischenablage ein - - - - Set empty value to selected cell(s) - Fügt einen leeren Wert in die selektierte(n) Zelle(n) ein - - - - Set NULL value to selected cell(s) - Fügt den NULL Wert in die selektierte(n) Zelle(n) ein - - - - Commit changes to cell(s) contents - Änderungen der Zellenninhalte speichern - - - - Rollback changes to cell(s) contents - Änderungen der Zelleninhalte zurücknehmen - - - - Delete selected data row - Markierte Datenzeile löschen - - - - Insert new data row - Neue Datenzeile einfügen - - - - Open contents of selected cell in a separate editor - Inhalt der markierten Zelle im separaten Editor öffnen - - - - Total pages available: %1 - Verfügbare Gesamtseiten: %1 - - - - Total rows loaded: %1 - Insgesamt geladene Zeilen: %1 - - - - Data view (both grid and form) - Ergebnisansicht (tabellarisch und Formular) - - - - Refresh data - Daten aktualisieren - - - - Switch to grid view of the data - Zur tabellarischen Ergebnisansicht wechseln - - - - Switch to form view of the data - Zur Formularansicht wechseln - - - - Database list - Liste der Datenbanken - - - - Delete selected item - Gewählten Eintrag löschen - - - - Clear filter contents - Filter zurücksetzen - - - - Refresh schema - Schema aktualisieren - - - - Refresh all schemas - Alle Schemas aktualisieren - - - - Add database - Datenbank hinzufügen - - - - Select all items - Alles auswählen - - - - Copy selected item(s) - Gewählte Einträge kopieren - - - - - - Paste from clipboard - Von der Zwischenablage einfügen - - - - Tables - Tabellen - - - - Indexes - Indizes - - - - Triggers - Trigger - - - - Views - Views - - - - Columns - Spalten - - - - Data form view - Formularansicht der Ergebnisse - - - - Commit changes for current row - Änderungen der aktuellen Zeile speichern - - - - Rollback changes for current row - Änderungen der aktuellen Zeile zurücknehmen - - - - Go to first row on current page - Springe zur ersten Zeile dieser Seite - - - - Go to next row - Springe zur nächsten Zeile - - - - Go to previous row - Springe zur vorherigen Zeile - - - - Go to last row on current page - Springe zur letzten Zeile dieser Seite - - - - Insert new row - Neue Zeile einfügen - - - - Delete current row - Derzeitige Zeile löschen - - - - Main window - Hauptfenster - - - - Open SQL editor - SQL Editor öffnen - - - - Previous window - Vorheriges Fenster - - - - Next window - Nächstes Fenster - - - - Hide status area - Statusfeld verbergen - - - - Open configuration dialog - Konfigurationsdialog öffnen - - - - Open Debug Console - Debug Konsole öffnen - - - - Open CSS Console - CSS Konsole öffnen - - - - Cell text value editor - Editor für Textwerte in Zellen - - - - - Cut selected text - Gewählten Text ausschneiden - - - - - Copy selected text - Gewählten Text kopieren - - - - - Delete selected text - Gewählten Text löschen - - - - - Undo - Rückgängig - - - - - Redo - Wiederholen - - - - SQL editor input field - SQL Editor Eingabefeld - - - - Select whole editor contents - Gesamten Editorinhalt auswählen - - - - Save contents into a file - Inhalte in eine Datei speichern - - - - Load contents from a file - Inhalte aus einer Datei laden - - - - Find in text - Suche im Text - - - - Find next - Nächster Fund - - - - Find previous - Vorheriger Fund - - - - Replace in text - Ersetze im Text - - - - Delete current line - Aktuelle Zeile löschen - - - - Request code assistant - Code-Assistenten anfordern - - - - Format contents - Format-Inhalte - - - - Move selected block of text one line down - Selektierten Textblock eine Zeile nach unten verschieben - - - - Move selected block of text one line up - Selektierten Textblock eine Zeile nach oben verschieben - - - - Copy selected block of text and paste it a line below - Selektierten Textblock kopieren und unterhalb einfügen - - - - Copy selected block of text and paste it a line above - Selektierten Textblock kopieren und oberhalb einfügen - - - - Toggle comment - Kommentar umschalten - - - - All SQLite databases - Alle SQLite Datenbanken - - - - All files - Alle Dateien - - - - - Database file - Datenbankdatei - - - Reports history window - Diese Übersetzung muss noch einmal geprüft werden, wenn ich den Kontext dazu kenne. - Report-Verlaufsfenster - - - Delete selected entry - Gewählten Eintrag löschen - - - - SQL editor window - SQL Editor-Fenster - - - - Execute query - Abfrage ausführen - - - - Execute "%1" query - Abfrage "%1" ausführen - - - - Switch current working database to previous on the list - Wechsel von der aktuellen Datenbank zur vorherigen in der Liste - - - - Switch current working database to next on the list - Wechsel von der aktuellen Datenbank zur nächsten in der Liste - - - - Go to next editor tab - Gehe zum nächsten Editor-Reiter - - - - Go to previous editor tab - Gehe zum vorherigen Editor-Reiter - - - - Move keyboard input focus to the results view below - Tastatureingabe-Fokus in das untere Ergebnisfenster setzen - - - - Move keyboard input focus to the SQL editor above - Tastatureingabe-Fokus in das obere SQL Editorfenster setzen - - - - Delete selected SQL history entries - - - - - Table window - Tabellenfenster - - - - Refresh table structure - Aktualisiere Tabellenstruktur - - - - Add new column - Neue Spalte zufügen - - - - Edit selected column - Gewählte Spalte bearbeiten - - - - Delete selected column - Gewählte Spalte löschen - - - - Export table data - Tabellendaten exportieren - - - - Import data to the table - Daten in die Tabelle importieren - - - - Add new table constraint - Neue Tabellenbedingung zufügen - - - - Edit selected table constraint - Markierte Tabellenbedingung bearbeiten - - - - Delete selected table constraint - Markierte Tabellenbedingung löschen - - - - Refresh table index list - Aktualisiere Tabellenindexliste - - - - Add new index - Neuen Index zufügen - - - - Edit selected index - Gewählten Index bearbeiten - - - - Delete selected index - Gewählten Index löschen - - - - Refresh table trigger list - Aktualisiere Tabellentriggerliste - - - - - Add new trigger - Neuen Trigger zufügen - - - - - Edit selected trigger - Gewählten Trigger bearbeiten - - - - - Delete selected trigger - Gewählten Trigger löschen - - - - - Go to next tab - Springe zum nächsten Reiter - - - - - Go to previous tab - Springe zum vorherigen Reiter - - - - A view window - Neues Fenster zufügen - - - - Refresh view trigger list - Ggf. View mit Ansicht übersetzen, muss im Kontext geklärt werden - Aktualisiere View Triggerliste - - - - QuitConfirmDialog - - Uncommited changes - Nicht gespeicherte Änderungen - - - - Uncommitted changes - - - - - Are you sure you want to quit the application? - -Following items are pending: - Sind Sie sicher, dass Sie das Programm beenden wollen? - -Folgende Punkte sind unerledigt: - - - - SearchTextDialog - - - Find or replace - Ggf. Suchen statt Finden? - Finden oder Ersetzen - - - - Find: - Finden: - - - - Case sensitive - Groß- und Kleinschreibung beachten - - - - Search backwards - Suche rückwärts - - - - Regular expression matching - Prüfung nach regulärem Ausdruck - - - - Replace && -find next - Ersetzen && weitersuchen - - - - Replace with: - Ersetzen mit: - - - - Replace all - Alles ersetzen - - - - Find - Finden - - - - SortDialog - - - Sort by columns - Nach Spalten sortiert - - - - - Column - Spalte - - - - - Order - Sortierung - - - - Sort by: %1 - Sortiert nach: %1 - - - - Move column up - Spalte nach oben verschieben - - - - Move column down - Spalte nach unten verschieben - - - - SqlEditor - - - Cut - sql editor - Ausschneiden - - - - Copy - sql editor - Kopieren - - - - Paste - sql editor - Einfügen - - - - Delete - sql editor - Löschen - - - - Select all - sql editor - Alles auswählen - - - - Undo - sql editor - Rückgängig - - - - Redo - sql editor - Wiederholen - - - - Complete - sql editor - Komplett - - - - Format SQL - sql editor - SQL formatieren - - - - Save SQL to file - sql editor - SQL in Datei speichern - - - - Select file to save SQL - sql editor - SQL aus Datei laden - - - - Load SQL from file - sql editor - Zeile löschen - - - - Delete line - sql editor - Zeile löschen - - - - Move block down - sql editor - Block nach unten verschieben - - - - Move block up - sql editor - Block nach oben verschieben - - - - Copy block down - sql editor - Block nach unten kopieren - - - - Copy up down - sql editor - "up down" ??? Muss geklärt werden! - Kopiere auf ab - - - - Find - sql editor - Finden - - - - Find next - sql editor - Nächster Fund - - - - Find previous - sql editor - Vorheriger Fund - - - - Replace - sql editor - Ersetzen - - - - Toggle comment - sql editor - Kommentar umschalten - - - - Saved SQL contents to file: %1 - SQL Inhalte in Datei speichern: %1 - - - - Syntax completion can be used only when a valid database is set for the SQL editor. - Die Funktion Autovervollständigung kann nur genutzt werden, wenn eine gültige Datenbank für den SQL Editor gewählt wurde. - - - - Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. - Der Text im SQL Editor ist sehr groß, daher wurde die Syntaxkontrolle und die farbliche Hervorhebung von Objekten vorübergehend deaktiviert. - - - - Save to file - In Datei speichern - - - - Could not open file '%1' for writing: %2 - Die Datei '%1' kann nicht für Schreibzugriffe geöffnet werden: %2 - - - - SQL scripts (*.sql);;All files (*) - SQL Skripte (*.sql);;Alle Dateien (*) - - - - Open file - Datei öffnen - - - - Could not open file '%1' for reading: %2 - Die Datei '%1' kann nicht für Lesezugriffe geöffnet werden: %2 - - - - Reached the end of document. Hit the find again to restart the search. - Das Dokumentenende wurde erreicht. Drücken Sie 'Nächster Fund', um die Suche am Dokumentenanfang fortzusetzen. - - - - SqlQueryItem - - - Column: - data view tooltip - Spalte: - - - - Data type: - data view - Datentyp: - - - - Table: - data view tooltip - Tabelle: - - - - Constraints: - data view tooltip - Bedingungen: - - - This cell is not editable, because: %1 - Diese Zelle kann nicht editiert werden, weil: %1 - - - - Cannot load the data for a cell that refers to the already closed database. - Es können keine Daten für eine Zelle dargestellt werden, die eine bereits geschlossene Datenbank referenziert. - - - - SqlQueryItemDelegate - - Cannot edit this cell. Details: %2 - Die Zelle kann nicht editiert. Details: %2 - - - - The row is marked for deletion. - Diese Zeile ist zum Löschen markiert. - - - - - - - - Cannot edit this cell. Details: %1 - Die Zelle kann nicht editiert. Details: %1 - - - - - Structure of this table has changed since last data was loaded. Reload the data to proceed. - - - - - Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). - - - - - Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. - - - - - SqlQueryModel - - - - Only one query can be executed simultaneously. - Es kann nur eine Abfrage gleichzeitig ausgeführt werden. - - - Uncommited data - Nicht gespeicherte Daten - - - There are uncommited data changes. Do you want to proceed anyway? All uncommited changes will be lost. - Es gibt ungespeicherte Änderungen. Möchten Sie wirklich fortfahren? Alle Änderungen werden dann verloren gehen. - - - - Cannot commit the data for a cell that refers to the already closed database. - Es können keine Daten für eine Zelle gespeichert werden, die eine bereits geschlossene Datenbank referenziert. - - - - Could not begin transaction on the database. Details: %1 - Es kann keine Transaktion auf der Datenbank gestartet werden. Details: %1 - - - An error occurred while commiting the transaction: %1 - Fehler beim Committen der Transaktion: %1 - - - - An error occurred while rolling back the transaction: %1 - Fehler beim Rollback der Transaktion: %1 - - - - Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. - Es wurde versucht eine nicht editierbare Zelle zu committen (derzeit modifiziert und auf das Commit wartend)! Dies ist ein Fehler den Sie melden sollten. - - - An error occurred while commiting the data: %1 - Fehler beim Committen der Daten: %1 - - - - Uncommitted data - - - - - There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. - - - - - An error occurred while committing the transaction: %1 - - - - - An error occurred while committing the data: %1 - - - - - Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - - - - - - Error while executing SQL query on database '%1': %2 - Fehler beim Ausführen der SQL-Abfrage auf der Datenbank '%1': %2 - - - - Error while loading query results: %1 - Fehler beim Laden der Abfrageergebnisse: %1 - - - - Insert multiple rows - Mehrere Zeilen einfügen - - - - Number of rows to insert: - Anzahl an Zeilen zum Einfügen: - - - - SqlQueryView - - - Go to referenced row in... - - - - - Copy - Kopieren - - - - Copy as... - Kopieren als... - - - - Paste - Einfügen - - - - Paste as... - Einfügen als... - - - - Set NULL values - NULL Wert setzen - - - - Erase values - Werte löschen - - - - Edit value in editor - Wert im Editor bearbeiten - - - - Commit - Commit - - - - Copy with headers - - - - - Rollback - Rollback - - - - Commit selected cells - Gewählte Zellen speichern - - - - Rollback selected cells - Gewählte Zellen wiederherstellen - - - - Define columns to sort by - Sortierspalten definieren - - - - Remove custom sorting - Benutzerdefinierte Sortierung entfernen - - - - Insert row - Zeile einfügen - - - - Insert multiple rows - Mehrere Zeilen einfügen - - - - Delete selected row - Gewählte Zeile löschen - - - - Show value in a viewer - - - - - Generate query for selected cells - - - - - No items selected to paste clipboard contents to. - Es sind keine Elemente selektiert in die der Inhalt der Zwischenablage eingefügt werden könnte. - - - - Go to referenced row in table '%1' - - - - - table '%1' - - - - - Referenced row (%1) - - - - - Trim pasted text? - - - - - The pasted text contains leading or trailing white space. Trim it automatically? - - - - - Edit value - Werte editieren - - - - SqlTableModel - - Error while commiting new row: %1 - Fehler beim Committen der neuen Zeile: %1 - - - - Error while committing new row: %1 - - - - - Error while deleting row from table %1: %2 - Fehler beim Löschen der Zeile aus Tabelle %1: %2 - - - - SqliteExtensionEditor - - - Filter extensions - - - - - Leave empty to use default function - - - - - Extension file - - - - - Initialization function - - - - - Databases - Datenbanken - - - - Register in all databases - In allen Datenbanken registrieren - - - - Register in following databases: - In den folgenden Datenbanken registrieren: - - - - Extension manager window has uncommitted modifications. - - - - - Extension manager - - - - - Commit all extension changes - - - - - Rollback all extension changes - - - - - Add new extension - - - - - Remove selected extension - - - - - Editing extensions manual - - - - - File with given path does not exist or is not readable. - - - - - Unable to load extension: %1 - - - - - Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - - - - - Dynamic link libraries (*.dll);;All files (*) - - - - - Shared objects (*.so);;All files (*) - - - - - Dynamic libraries (*.dylib);;All files (*) - - - - - All files (*) - Alle Dateien (*) - - - - Open file - Datei öffnen - - - - StatusField - - - Status - Status - - - - Copy - Kopieren - - - - Clear - Leeren - - - - TableConstraintsModel - - - Type - table constraints - Typ - - - - Details - table constraints - Details - - - - Name - table constraints - Name - - - - TableForeignKeyPanel - - - Foreign table: - Fremde Tabelle: - - - - SQLite 2 does not support foreign keys officially, -but it's okay to use them anyway. - - - - - Columns - Spalten - - - - Local column - - - - - Foreign column - - - - - Reactions - Reaktionen - - - - Deferred foreign key - Verzögerter Fremdschlüssel - - - - Named constraint - Benannte Bedingung - - - - Constraint name - Name der Bedingung - - - - Pick the foreign column. - Wählen Sie die Fremdspalte aus. - - - - Pick the foreign table. - Wählen Sie die Fremdtabelle aus. - - - - Select at least one foreign column. - - - - - Enter a name of the constraint. - Geben Sie einen Namen für die Bedingung ein. - - - - Foreign column - table constraints - - - - - TablePrimaryKeyAndUniquePanel - - - Columns - Spalten - - - - Column - Spalte - - - - Collation - Kollation - - - - Sort - Sortierung - - - - Valid only for a single column with INTEGER data type - - - - - Autoincrement - Automatisch hochzählend - - - - Named constraint - Benannte Bedingung - - - - Constraint name - Name der Bedingung - - - - On conflict - Bei Konflikt - - - - Collate - table constraints - - - - - Sort order - table constraints - Sortierung - - - - Select at least one column. - Mindestens eine Spalte auswählen. - - - - Enter a name of the constraint. - Geben Sie einen Namen für die Bedingung ein. - - - - TableStructureModel - - - Name - table structure columns - Name - - - - Data type - table structure columns - Datentyp - - - - Primary -Key - table structure columns - - - - - Foreign -Key - table structure columns - - - - - Unique - table structure columns - - - - - Check - table structure columns - Prüfung - - - - Not -NULL - table structure columns - - - - - Collate - table structure columns - - - - - Default value - table structure columns - Standardwert - - - - TableWindow - - - Structure - Struktur - - - - Table name: - - - - - - Data - - - - - Constraints - Bedingungen - - - - Indexes - Indizes - - - - Triggers - Trigger - - - - DDL - DDL - - - - Export table - table window - - - - - Import data to table - table window - - - - - Populate table - table window - Tabelle füllen - - - - Refresh structure - table window - - - - - Commit structure changes - table window - - - - - Rollback structure changes - table window - - - - - Add column - table window - - - - - Edit column - table window - - - - - - Delete column - table window - - - - - Move column up - table window - Spalte nach oben verschieben - - - - Move column down - table window - Spalte nach unten verschieben - - - - Create similar table - table window - Erzeuge identische Tabelle - - - - Reset autoincrement value - table window - - - - - Add table constraint - table window - - - - - Edit table constraint - table window - - - - - Delete table constraint - table window - - - - - Move table constraint up - table window - - - - - Move table constraint down - table window - - - - - Add table primary key - table window - - - - - Add table foreign key - table window - - - - - Add table unique constraint - table window - - - - - Add table check constraint - table window - - - - - Refresh index list - table window - - - - - Create index - table window - - - - - Edit index - table window - - - - - Delete index - table window - Index löschen - - - - Refresh trigger list - table window - Trigger Liste aktualisieren - - - - Create trigger - table window - - - - - Edit trigger - table window - - - - - Delete trigger - table window - Trigger löschen - - - - Are you sure you want to delete column '%1'? - table window - - - - - Following problems will take place while modifying the table. -Would you like to proceed? - table window - - - - - Table modification - table window - - - - - Could not load data for table %1. Error details: %2 - - - - - Could not process the %1 table correctly. Unable to open a table window. - - - - - Could not restore window %1, because no database or table was stored in session for this window. - - - - - Could not restore window '%1', because no database or table was stored in session for this window. - - - - - Could not restore window '%1', because database %2 could not be resolved. - - - - - Could not restore window '%1'', because the table %2 doesn't exist in the database %3. - - - - - - New table %1 - - - - - Committed changes for table '%1' successfully. - - - - - Committed changes for table '%1' (named before '%2') successfully. - - - - - Autoincrement value for table '%1' has been reset successfully. - - - - - Uncommitted changes - - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - - - - - Table window "%1" has uncommitted structure modifications and data. - - - - - Table window "%1" has uncommitted data. - - - - - Table window "%1" has uncommitted structure modifications. - - - - - Could not commit table structure. Error message: %1 - table window - - - - - Reset autoincrement - Autoincrement zurücksetzen - - - - Are you sure you want to reset autoincrement value for table '%1'? - Sind Sie sicher, dass Sie den Autoincrement Wert für die Tabelle '%1' zurücksetzen möchten? - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - Ein Fehler ist aufgetreten beim Zurücksetzen des Autoincrementwertes für die Tabelle '%1': %2 - - - Autoincrement value for table '%1' has been reset successfly. - Autoincrementwert für die Tabelle '%1' wurde erfolgreich zurückgesetzt. - - - - Empty name - - - - - A blank name for the table is allowed in SQLite, but it is not recommended. -Are you sure you want to create a table with blank name? - - - - - Cannot create a table without at least one column. - - - - - Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. - - - - - Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. - - - - - Are you sure you want to delete table constraint '%1'? - table window - - - - - Delete constraint - table window - Bedingung löschen - - - - Cannot export, because no export plugin is loaded. - - - - - Cannot import, because no import plugin is loaded. - - - - Uncommited changes - Nicht gespeicherte Änderungen - - - - Go back to structure tab - - - - - Commit modifications and browse data. - - - - - Name - table window indexes - Name - - - - Unique - table window indexes - - - - - Columns - table window indexes - Spalten - - - - Partial index condition - table window indexes - Partieller Indexzustand - - - - Name - table window triggers - Name - - - - Event - table window triggers - - - - - Condition - table window triggers - - - - - Details - table window triggers - Details - - - - TriggerColumnsDialog - - - Trigger columns - - - - - Triggering columns: - - - - - Select all - Alles auswählen - - - - Deselect all - Auswahl aufheben - - - - TriggerDialog - - - - Trigger - - - - - On table: - Auf Tabelle: - - - - Action: - - - - - - <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> - - - - - Pre-condition: - - - - - The scope is still not fully supported by the SQLite database. - - - - - Trigger name: - - - - - When: - - - - - List of columns for UPDATE OF action. - - - - - Scope: - - - - - Code: - - - - - Trigger statements to be executed. - - - - - DDL - DDL - - - - On view: - - - - - Could not process trigger %1 correctly. Unable to open a trigger dialog. - - - - - Enter a valid condition. - Geben Sie einen gültigen Zustand ein. - - - - Enter a valid trigger code. - - - - - Error - trigger dialog - Fehler - - - - An error occurred while executing SQL statements: -%1 - Fehler beim Ausführen des folgenden SQL Statments: -%1 - - - - VersionConvertSummaryDialog - - - Database version convert - - - - - Following changes to the SQL statements will be made: - - - - - Before - - - - - After - - - - - ViewWindow - - - Query - Abfrage - - - - View name: - - - - - Output column names - - - - - - Data - - - - - Triggers - Trigger - - - - DDL - DDL - - - - - Could not restore window '%1', because no database or view was stored in session for this window. - - - - - Could not restore window '%1', because database %2 could not be resolved. - - - - - Could not restore window '%1', because database %2 could not be open. - - - - - Could not restore window '%1', because the view %2 doesn't exist in the database %3. - - - - - - New view %1 - - - - - Refresh the view - view window - - - - - Commit the view changes - view window - - - - - Rollback the view changes - view window - - - - - Explicit column names - - - - - Generate output column names automatically basing on result columns of the view. - - - - - Add column - view window - - - - - Edit column - view window - - - - - Delete column - view window - - - - - Move column up - view window - Spalte nach oben verschieben - - - - Move column down - view window - Spalte nach unten verschieben - - - - Refresh trigger list - view window - Trigger Liste aktualisieren - - - - Create new trigger - view window - Trigger erstellen - - - - Edit selected trigger - view window - Trigger editieren - - - - Delete selected trigger - view window - Trigger löschen - - - - View window "%1" has uncommitted structure modifications and data. - - - - - View window "%1" has uncommitted data. - - - - - View window "%1" has uncommitted structure modifications. - - - - - Uncommitted changes - - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - - - - - Committed changes for view '%1' successfully. - - - - - Committed changes for view '%1' (named before '%2') successfully. - - - - - Could not load data for view %1. Error details: %2 - - - - Uncommited changes - Nicht gespeicherte Änderungen - - - - Go back to structure tab - - - - - Commit modifications and browse data. - - - - - Could not commit view changes. Error message: %1 - view window - - - - - Override columns - - - - - Currently defined columns will be overriden. Do you want to continue? - - - - - Could not determinate columns returned from the view. The query is problably incomplete or contains errors. - - - - - Name - view window triggers - Name - - - - Instead of - view window triggers - - - - - Condition - view window triggers - - - - - Details - table window triggers - Details - - - - Could not process the %1 view correctly. Unable to open a view window. - - - - - Empty name - - - - - A blank name for the view is allowed in SQLite, but it is not recommended. -Are you sure you want to create a view with blank name? - - - - - The SELECT statement could not be parsed. Please correct the query and retry. -Details: %1 - - - - - The view could not be modified due to internal SQLiteStudio error. Please report this! - - - - - The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. - - - - - Following problems will take place while modifying the view. -Would you like to proceed? - view window - - - - - View modification - view window - - - - - WidgetCover - - - Interrupt - - - - diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de_DE.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de_DE.ts new file mode 100644 index 0000000..de05638 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_de_DE.ts @@ -0,0 +1,7107 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + Über SQLiteStudio und deren Lizenzen + + + + About + Über SQLiteStudio + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Lizenzen + + + + Environment + Programmumgebung + + + + Icon directories + Icon Verzeichnisse + + + + Form directories + Formular Verzeichnisse + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin Verzeichnisse + + + + Configuration directory + Konfigurationsverzeichnis + + + + Application directory + Programmverzeichnis + + + + Qt version: + Qt Version: + + + + SQLite 3 version: + SQLite 3 Version: + + + + Portable distribution. + Portable Version. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Betriebssystemverwaltete Version. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Inhaltsverzeichnis:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Abfrageparameter + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Kollationen filtern + + + + Databases + Datenbanken + + + + Register in all databases + In allen Datenbanken registrieren + + + + Register in following databases: + In den folgenden Datenbanken registrieren: + + + + Implementation code: + Anweisungen: + + + + Collation name: + Kollationsname: + + + + Implementation language: + Sprache: + + + + Collations editor + Editor für Kollationen + + + + Commit all collation changes + Speichern aller Änderungen an Kollationen + + + + Rollback all collation changes + Zurücknehmen aller Änderungen an Kollationen + + + + Create new collation + Neue Kollation erstellen + + + + Delete selected collation + Markierte Kollationen löschen + + + + Editing collations manual + Kollationen manuell editieren + + + + Enter a non-empty, unique name of the collation. + Geben Sie einen eindeutigen Namen für die Kollation ein. + + + + Pick the implementation language. + Wählen Sie die Sprache aus. + + + + Enter a non-empty implementation code. + Geben Sie eine eindeutige Vergleichsoperatorendefinition ein. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Wählen Sie eine Farbe aus + + + + ColumnCollatePanel + + + Collation name: + Name der Kollation: + + + + Named constraint: + Name der Bedingung: + + + + Enter a name of the constraint. + Geben Sie einen Namen für die Bedingung ein. + + + + Enter a collation name. + Geben Sie einen Namen für die Kollation ein. + + + + ColumnDefaultPanel + + + Default value: + Standardwert: + + + + Named constraint: + Benannte Bedingung: + + + + Enter a default value expression. + Geben Sie einen Standardwert an. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Geben Sie einen Namen für die Bedingung ein. + + + + ColumnDialog + + + Column + Spalte + + + + Name and type + Name und Typ + + + + Scale + Skalierung + + + + Precision + Präzision + + + + Data type: + Datentyp: + + + + Column name: + Spaltenname: + + + + Size: + Größe: + + + + Constraints + Bedingungen + + + + Generated value + Generierter Wert + + + + Unique + Eindeutigkeit + + + + + + + + + + + Configure + Konfigurieren + + + + Foreign Key + Fremdschlüssel + + + + Collate + Kollationieren + + + + Not NULL + Nicht NULL + + + + Check condition + Zustandsprüfung + + + + Primary Key + Primärschlüssel + + + + Default + Standard + + + + Advanced mode + Erweiterter Modus + + + + Add constraint + column dialog + Bedingung hinzufügen + + + + Edit constraint + column dialog + Bedingung editieren + + + + + Delete constraint + column dialog + Bedingung löschen + + + + Move constraint up + column dialog + Bedingung nach oben verschieben + + + + Move constraint down + column dialog + Bedingung nach unten verschieben + + + + Add a primary key + column dialog + Primärschlüssel zufügen + + + + Add a foreign key + column dialog + Fremdschlüssel zufügen + + + + Add an unique constraint + column dialog + Eindeutige Bedingung hinzufügen + + + + Add a check constraint + column dialog + Prüfungsbedingung hinzufügen + + + + Add a not null constraint + column dialog + Nicht-NULL Bedingung hinzufügen + + + + Add a collate constraint + column dialog + Kollationsbedingung hinzufügen + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Standardbedingung hinzufügen + + + + Are you sure you want to delete constraint '%1'? + column dialog + Sind Sie sicher, dass Sie die folgende Bedingung löschen wollen: '%1'? + + + + Correct the constraint's configuration. + Korrigiert die Konfiguration der Bedingung. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Für INTEGER PRIMARY KEY ist eine Skalierung nicht erlaubt. + + + + Precision cannot be defined without the scale. + Die Präzision kann ohne Skalierung nicht definiert werden. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Für INTEGER PRIMARY KEY ist eine Präzision nicht erlaubt. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Typ + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Fremde Tabelle: + + + + Foreign column: + Fremde Spalte: + + + + Reactions + Reaktionen + + + + Deferred foreign key + Verzögerter Fremdschlüssel + + + + Named constraint + Benannte Bedingung + + + + Constraint name + Name der Bedingung + + + + Pick the foreign table. + Wählen Sie die Fremdtabelle aus. + + + + Pick the foreign column. + Wählen Sie die Fremdspalte aus. + + + + Enter a name of the constraint. + Geben Sie einen Namen für die Bedingung ein. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Geben Sie einen Namen für die Bedingung ein. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Automatisch hochzählend + + + + Sort order: + Sortierfolge: + + + + Named constraint: + Benannte Bedingung: + + + + On conflict: + Bei Konflikt: + + + + Enter a name of the constraint. + Geben Sie einen Namen für die Bedingung ein. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Benannte Bedingung: + + + + On conflict: + Bei Konflikt: + + + + Enter a name of the constraint. + Geben Sie einen Namen für die Bedingung ein. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Spalte: %1 + + + + Table: %1 + completer statusbar + Tabelle: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Datenbank: %1 + + + + Keyword: %1 + completer statusbar + Schlüsselwort: %1 + + + + Function: %1 + completer statusbar + Funktion: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + Zeichenkette + + + + Number + completer statusbar + Nummer + + + + Binary data + completer statusbar + Binäre Daten + + + + Collation: %1 + completer statusbar + Kollation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma Funktion: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Konfiguration + + + + Search + Suchen + + + + General + Allgemein + + + + Keyboard shortcuts + Tastaturkürzel + + + + Look & feel + Layout + + + + Style + Stil + + + + Fonts + Schriftarten + + + + Code colors + Code colors + + + + + Database list + Liste der Datenbanken + + + + Code assistant + Code assistant + + + + Data browsing + Datenbearbeitung + + + + Data editors + Dateneditoren + + + + Plugins + Erweiterungen + + + + Code formatters + Codeformatierer + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + Wenn die Option deaktiviert ist, werden die Spalten in der Reihenfolge sortiert in der sie im CREATE TABLE Statement angegeben wurden. + + + + Sort table columns alphabetically + Tabellenspalten alphabetisch sortieren + + + + Expand tables node when connected to a database + Tabellenknoten aufklappen, wenn eine Datenbank verbunden ist + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Zusätzliche Bezeichnungen sind jene, die neben den Namen der Datenbankliste angezeigt werden (sie sind normalerweise blau gefärbt, es sei denn dies wurde umkonfiguriert). Ist diese Option aktiviert, dann werden diese Bezeichnungen angezeigt für Datenbanken, ungültige Datenbanken und zusammengefasste Knoten (Spalten-, Index- und Triggergruppen). Für mehr Details siehe die folgenden optionen.<p> + + + + Display additional labels on the list + Zeige zusätzliche Bezeichnungen in der Liste an + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + Für normale Tabellen enthält die Bezeichnung die Anzahl der Spalten, Indizes und Trigger einer jeden Tabelle. + + + + Display labels for regular tables + Zeigt Bezeichnungen für normale Tabellen an + + + + Virtual tables will be marked with a 'virtual' label. + Virtuelle Tabellen werden mit einem 'virtuell' Kürzel versehen. + + + + Display labels for virtual tables + Zeige Bezeichnungen für virtuelle Tabellen + + + + Expand views node when connected to a database + Knoten aufklappen, wenn eine Datenbank verbunden ist + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + Wenn die Option deaktiviert ist, werden die Objekte in der Reihenfolge sortiert in der sie in der sqlite_master Tabelle angezeigt werden (entspricht der Reihenfolge in der sie angelegt worden sind) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Objekte alphabetisch sortieren + + + + Display system tables and indexes on the list + Zeige Systemtabellen und Indizes in der Liste an + + + + Database dialog window + Dialogfenster der Datenbank + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Datenbearbeitung + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>Wenn Daten in das Ergebnisfenster eingelesen werden, dann wird die Breite der Spalten dabei automatisch angepasst. Dieser Wert begrenzt maximale Breite für die automatische Breitenanpassung. Der Anwender kann die Spaltenbreite jedoch manuell über dieses Limit verbreitern.</p> + + + + Number of data rows per page: + Anzahl an Datenzeilen pro Seite: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Neue Zeile im Gitternetz des Datenfensters hinzufügen + + + + Before currently selected row + Vor der derzeitig ausgewählten Zeile + + + + After currently selected row + Nach der derzeitig ausgewählten Zeile + + + + At the end of data view + Am Ende der Datenfensters + + + + Table windows + Tabellenfenster + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>Wenn aktiviert, wird der Reiter "Daten" anstelle des Reiters "Struktur" angezeigt beim öffnen eines Tabellenfensters angezeigt.</p> + + + + Open Table Windows with the data tab for start + Öffnet das Tabellenfenster mit dem Reiter "Daten" im Vordergrund + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>Wenn aktiviert, wird der Reiter "Daten" als erster Reiter angezeigt für jedes Tabellenfenster, anstelle an zweiter Stelle.</p> + + + + Place data tab as first tab in a Table Window + Den Reiter Daten als ersten Reiter im Tabellenfenster anzeigen + + + + View windows + Viewfenster + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Öffnet das Viewfenster mit dem Reiter "Daten" im Vordergrund + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Den Reiter Daten als ersten Reiter im View-Fenster anzeigen + + + + Data types + Datentypen + + + + Available editors: + Verfügbare Editoren: + + + + Editors selected for this data type: + Für diesen Datentyp ausgewählte Editoren: + + + + Schema editing + Schema + + + + Number of DDL changes kept in history. + Maximale Anzahl an DDL Änderungen im Verlauf. + + + + DDL history size: + DDL Verlaufsgröße: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL Abfragen + + + + + Number of queries kept in the history. + Maximale Anzahl an SQL Abfragen im Verlauf. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + Verlaufsgröße: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Führt nur die Abfrage unter dem Cursor aus + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Prüfe vor dem Start automatisch auf Updates + + + + Session + Sitzung + + + + Restore last session (active MDI windows) after startup + Stelle letzte Sitzung nach dem Start wieder her (aktive MDI Fenster) + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Statusfeld + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Den Panel Status immer öffnen, wenn eine neue Meldung ausgegeben wird + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filtere Tastaturkürzel nach Name oder Tastenkombination + + + + Action + Aktion + + + + Key combination + Tastenkombination + + + + + Language + Sprache + + + + Changing language requires application restart to take effect. + Die Änderung der Sprache erfordert einen Neustart des Programms. + + + + Compact layout + Kompaktes Layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Das kompakte Layout reduziert alle Lücken und Abstände der Oberfläche auf ein Minimum, um mehr Platz für die Darstellung der Daten zu schaffen. Die Oberfläche sieht dann zwar nicht mehr sehr ästhetisch aus, aber man hat mehr Daten im Überblick.</p> + + + + Use compact layout + Benutze kompaktes Layout + + + + Main window dock areas + Dockingbereiche des Hauptfensters + + + + Left and right areas occupy corners + Linke und rechte Bereiche belegen die Ecken + + + + Top and bottom areas occupy corners + Obere und untere Bereiche belegen die Ecken + + + + Hide built-in plugins + Verberge eingebaute Plugins + + + + Current style: + Aktueller Stil: + + + + Preview + Vorschau + + + + Enabled + Aktiviert + + + + Disabled + Deaktiviert + + + + Active formatter plugin + Aktives Formatierungsplugin + + + + SQL editor font + Schriftart des SQL Editors + + + + Database list font + Schriftart der Datenbankliste + + + + Database list additional label font + Zusätzliche Bezeichnungen in der Datenbankliste + + + + Data view font + Schriftart der Ergebnisansicht + + + + Status field font + Schriftart des Statusfelds + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Bezeichnung: + + + + Category: + plugin details + Kategorie: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Autor: + + + + Internal name: + plugin details + Interner Name: + + + + Dependencies: + plugin details + Abhängigkeiten: + + + + Conflicts: + plugin details + Konflikte: + + + + Plugin details + Plugin Details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins werden direkt beim Aktivieren/Deaktivieren geladen bzw. entfernt, die modifizierte Pluginliste wird jedoch erst beim Bestätigen und Schließen des Konfigurationsfensters gespeichert. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (eingebaut) + + + + Details + Details + + + + No plugins in this category. + Keine Plugins in dieser Kategorie. + + + + Add new data type + Neuen Datentypen zufügen + + + + Rename selected data type + Markierten Datentypen umbenennen + + + + Delete selected data type + Markierten Datentypen löschen + + + + Help for configuring data type editors + Hilfe zur Konfiguration des Datentypen Editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + Der Zustand: + + + + Named constraint: + Benannte Bedingung: + + + + On conflict + Bei Konflikt + + + + Enter a valid condition. + Geben Sie einen gültigen Zustand ein. + + + + Enter a name of the constraint. + Geben Sie einen Namen für die Bedingung ein. + + + + ConstraintDialog + + + New constraint + constraint dialog + Neue Bedingung + + + + Create + constraint dialog + Erstellen + + + + Edit constraint + dialog window + Bedingung editieren + + + + Apply + constraint dialog + Übernehmen + + + + Primary key + table constraints + Primärer Schlüssel + + + + Foreign key + table constraints + Fremdschlüssel + + + + Unique + table constraints + Eindeutigkeit + + + + Not NULL + table constraints + Nicht NULL + + + + Check + table constraints + Prüfung + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Kollation + + + + Default + table constraints + Standard + + + + ConstraintTabModel + + + Table + table constraints + Tabelle + + + + Column (%1) + table constraints + Spalte (%1) + + + + Scope + table constraints + Bereich + + + + Type + table constraints + Typ + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS Konsole + + + + DataView + + + Filter data + data view + Daten filtern + + + + Grid view + Gitteransicht + + + + Form view + Formularansicht + + + + Refresh table data + data view + Aktualisiere Tabellendaten + + + + First page + data view + Erste Seite + + + + Previous page + data view + Vorherige Seite + + + + Next page + data view + Nächste Seite + + + + Last page + data view + Letzte Seite + + + + Commit changes for selected cells + data view + Änderungen für die selektierten Zellen speichern + + + + Rollback changes for selected cells + data view + Änderungen für die selektierten Zellen zurücknehmen + + + + Show grid view of results + data view + Zeige Ergebnismenge in der Gitteransicht + + + + Show form view of results + data view + Zeige Ergebnismenge in der Formularansicht + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Reiterleiste oben + + + + Tabs at bottom + data view + Reiterleiste unten + + + + Place new rows above selected row + data view + Neue Zeilen über der ausgewählten Zeile einfügen + + + + Place new rows below selected row + data view + Neue Zeilen nach der ausgewählten Zeile einfügen + + + + Place new rows at the end of the data view + data view + Neue Zeilen am Ende des Datenfensters einfügen + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Gesamtanzahl der Zeilen wird ermittelt. +Das Aufrufen anderer Seiten ist erst nach Abschluss der Zählung möglich. + + + + Row: %1 + Zeile: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Nach regulärem Ausdruck filtern + + + + Filter by SQL expression + data view + Nach einem SQL Ausdruck filtern + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Filter anwenden + + + + DbDialog + + + Database + Datenbank + + + + Database type + Datenbanktyp + + + + Database driver + Datenbanktreiber + + + + + File + Datei + + + + Name (on the list) + Name (in der Liste) + + + + Options + Optionen + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Wenn diese Option aktiviert ist, wird die Datenbank in der Konfiguration gespeichert und bei jedem Start von SQLiteStudio wieder hergestellt.</p> + + + + Permanent (keep it in configuration) + Permanent (in der Konfiguration behalten) + + + + Test connection + Verbindung testen + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Durchsuchen + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Geben Sie einen eindeutigen Datenbanknamen ein. + + + + This name is already in use. Please enter unique name. + Der Name wird bereits benutzt, bitte geben Sie einen freien, eindeutigen Namen ein. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Geben Sie einen Dateipfad für die Datenbank ein. + + + + This database is already on the list under name: %1 + Die Datenbank ist bereits unter folgendem Namen in der Liste enthalten: %1 + + + + Select a database type. + Wählen Sie einen Datebanktypen aus. + + + + DbObjectDialogs + + + Delete table + Tabelle löschen + + + + Are you sure you want to delete table %1? + Sind Sie sicher, dass Sie die Tabelle %1 löschen möchten? + + + + Delete index + Index löschen + + + + Are you sure you want to delete index %1? + Sind Sie sicher, dass Sie den Index %1 löschen möchten? + + + + Delete trigger + Trigger löschen + + + + Are you sure you want to delete trigger %1? + Sind Sie sicher, dass Sie den Trigger %1 löschen möchten? + + + + Delete view + View löschen + + + + Are you sure you want to delete view %1? + Sind Sie sicher, dass Sie den View %1 löschen möchten? + + + + + Error while dropping %1: %2 + Fehler beim Löschen: %1 %2 + + + + Delete objects + Objekte löschen + + + + Are you sure you want to delete following objects: +%1 + Sind Sie sicher, dass Sie die folgenden Objekte löschen möchten: +%1 + + + + Cannot start transaction. Details: %1 + Kann Transaktion nicht starten. Details: %1 + + + + Cannot commit transaction. Details: %1 + Kann Transaktion nicht ausführen. Details: %1 + + + + DbTree + + + Databases + Datenbanken + + + + Filter by name + Nach Name filtern + + + + Copy + Kopieren + + + + Paste + Einfügen + + + + Select all + Alles auswählen + + + + Create a group + Gruppe erstellen + + + + Delete the group + Diese Gruppe löschen + + + + Rename the group + Gruppe umbenennen + + + + &Add a database + &Datenbank hinzufügen + + + + &Edit the database + Datenbank bearbeiten + + + + &Remove the database + &Datenbank entfernen + + + + &Connect to the database + Verbindung mit der Datenbank + + + + &Disconnect from the database + Verbindung zur &Datenbank trennen + + + + Import + Import + + + + &Export the database + Datenbank &exportieren + + + + Vac&uum + Aufrä&umen + + + + &Integrity check + &Integritätsprüfung + + + + Create a &table + Erstellt eine Tabelle + + + + Edit the t&able + &Table bearbeiten + + + + Delete the ta&ble + Tabelle löschen + + + + Export the table + Tabelle exportieren + + + + Import into the table + In die Tabelle importieren + + + + Populate table + Tabelle füllen + + + + Create similar table + Erzeuge identische Tabelle + + + + Reset autoincrement sequence + Automatischen Zähler zurücksetzen + + + + Create an &index + Neuen Index anlegen + + + + Edit the i&ndex + Bearbeite den I&ndex + + + + Delete the in&dex + Index löschen + + + + Create a trig&ger + Trigger erstellen + + + + Edit the trigg&er + Trigg&er bearbeiten + + + + Delete the trigge&r + Trigger löschen + + + + Create a &view + Ansicht erstellen + + + + Edit the v&iew + V&iew bearbeiten + + + + Delete the vi&ew + Vi&ew löschen + + + + Add a column + Spalte zufügen + + + + Edit the column + Spalte editieren + + + + Delete the column + Spalte löschen + + + + Delete selected items + Gewählte Einträge löschen + + + + Clear filter + Filter zurücksetzen + + + + &Refresh all database schemas + &Alle Datenbankschemen aktualisieren + + + + Re&fresh selected database schema + Alle Datenbankschemen aktualisieren + + + + + Erase table data + Tabellendaten löschen + + + + Open file's directory + Verzeichnis der Datei öffnen + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Datenbank + + + + Grouping + Gruppieren + + + + Generate query for table + Abfrage für Tabelle generieren + + + + + Create group + Gruppe erstellen + + + + Group name + Gruppenname + + + + Entry with name %1 already exists in group %2. + Der Eintrag mit Namen %1 existiert bereits in der Gruppe %2. + + + + Delete group + Gruppe löschen + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Sind Sie sicher, dass Sie die Gruppe %1 löschen möchten? +Alle Objekte in dieser Gruppe werden in die übergeordnete Gruppe verschoben. + + + + Are you sure you want to remove database '%1' from the list? + Sind Sie sicher, dass Sie die Datenbank '%1' aus der Liste entfernen möchten? + + + + Are you sure you want to remove following databases from the list: +%1 + Sind Sie sicher, dass Sie folgende Datenbanken aus der Liste entfernen möchten: +%1 + + + + Remove database + Datenbank entfernen + + + + + Cannot import, because no import plugin is loaded. + Der Import kann nicht durchgeführt werden, da kein Import Plugin geladen ist. + + + + + Cannot export, because no export plugin is loaded. + Export fehlgeschlagen, da kein Export Plugins geladen sind. + + + + Vacuum (%1) + Aufräumen (%1) + + + + Integrity check (%1) + Integritätsprüfung (%1) + + + + Reset autoincrement + Autoincrement zurücksetzen + + + + Are you sure you want to reset autoincrement value for table '%1'? + Sind Sie sicher, dass Sie den Autoincrement Wert für die Tabelle '%1' zurücksetzen möchten? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + Ein Fehler ist aufgetreten beim Zurücksetzen des Autoincrementwertes für die Tabelle '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + Beim Löschen der Daten aus Tabelle '%1' ist folgender Fehler aufgetreten: %2 + + + + All data has been deleted for table '%1'. + Es wurden alle Daten aus Tabelle '%1' gelöscht. + + + + Following objects will be deleted: %1. + Folgende Objekte werden gelöscht: %1. + + + + Following databases will be removed from list: %1. + Folgende Datenbanken werden aus der Liste entfernt: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Die aus der gelöschten Gruppe verbleibenden Objekte werden an die Position der gelöschten Gruppe verschoben. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Sind Sie sicher, dass Sie fortfahren möchten? + + + + Delete objects + Objekte löschen + + + + DbTreeItemDelegate + + + error + dbtree labels + Fehler + + + + (system table) + database tree label + (Systemtabelle) + + + + (virtual) + virtual table label + (Virtual) + + + + (system index) + database tree label + (Systemindex) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Datenbank: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + Dateigröße: + + + + Encoding: + dbtree tooltip + Kodierung: + + + + Error: + dbtree tooltip + Fehlerbeschreibung: + + + + Table : %1 + dbtree tooltip + Tabelle: %1 + + + + Columns (%1): + dbtree tooltip + Spalten (%1): + + + + Indexes (%1): + dbtree tooltip + Indizes (%1): + + + + Triggers (%1): + dbtree tooltip + Trigger (%1): + + + + Copy + Kopieren + + + + Move + Verschieben + + + + Include data + Inklusive Daten + + + + Include indexes + Inklusive Indizes + + + + Include triggers + Inklusive Trigger + + + + Abort + Abbrechen + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenzierte Tabellen + + + + Do you want to include following referenced tables as well: +%1 + Möchten Sie die folgenden referenzierten Tabellen mit einbeziehen? %1 + + + + Name conflict + Namenskonflikt + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Folgende Objekte existieren bereits in der Datenbank. +Bitte geben Sie einen neuen, eindeutigen Namen an oder drücken Sie %1, um den Vorgang abzubrechen: + + + + SQL statements conversion + SQL Statement Konvertierung + + + + Following error occurred while converting SQL statements to the target SQLite version: + Folgender Fehler trat auf bei der Konvertierung von SQL Statements in die SQLite Zielversion: + + + + Would you like to ignore those errors and proceed? + Möchten Sie diese Fehler ignorieren und fortfahren? + + + + DdlHistoryWindow + + + Filter by database: + Nach Datenbank filtern: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Abfragen werden ausgeführt auf Datenbank %1 (%2) +-- Datum und Ausführungszeitpunkt: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL Verlauf + + + + DdlPreviewDialog + + + Queries to be executed + Auszuführende Abfragen + + + + Don't show again + Nicht wieder anzeigen + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Konsole + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Abfrage + + + + History + Verlauf + + + + Results in the separate tab + Ergebnisse in separatem Reiter + + + + Results below the query + Ergebnisse unter der Abfrage + + + + + SQL editor %1 + SQL Editor %1 + + + + + Results + Ergebnisse + + + + Execute query + Abfrage ausführen + + + + Explain query + Abfrage ausführen (explain) + + + + Clear execution history + sql editor + Ausführungsverlauf löschen + + + + Export results + sql editor + Ergebnisse exportieren + + + + Create view from query + sql editor + View aus der Abfrage erstellen + + + + Previous database + Vorherige Datenbank + + + + Next database + Nächste Datenbank + + + + Show next tab + sql editor + Nächsten Reiter zeigen + + + + Show previous tab + sql editor + Vorherigen Reiter zeigen + + + + Focus results below + sql editor + Fokus auf die Ergebnisse unten + + + + Focus SQL editor above + sql editor + Fokus auf den SQL Editor oben + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Aktive Datenbank (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Abfrage in %1 Sekunde(n) abgeschlossen. %2 Zeile(n) betroffen + + + + Query finished in %1 second(s). + Abfrage in %1 Sekunde(n) abgeschlossen. + + + + Clear execution history + Lösche Ausführungsverlauf + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Sind Sie sicher, dass Sie den gesamten SQL Ausführungsverlauf löschen möchten? Dieser Vorgang kann nicht rückgängig gemacht werden. + + + + Cannot export, because no export plugin is loaded. + Es kann nicht exportiert werden, da kein Export Plugin geladen ist. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + Es ist keine Datenbank im SQL Editor selektiert. Für eine unbekannte Datenbank kann kein View erzeugt werden. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Fehler + + + + Following errors occured: + Folgende Fehler sind aufgetreten: + + + + Would you like to proceed? + Möchten Sie fortsetzen? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Eingabedatei + + + + Path to file + Dateipfad + + + + Browse for file + Datei auswählen + + + + Options + Optionen + + + + File encoding + Dateikodierung + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL Skripte (*.sql);;Alle Dateien (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Exportieren + + + + What do you want to export? + Was möchten Sie exportieren? + + + + A database + Eine Datenbank + + + + A single table + Eine einzelne Tabelle + + + + Query results + Abfrageergebnisse + + + + Table to export + Zu exportierende Tabelle + + + + Database + Datenbank + + + + Table + Tabelle + + + + Options + Optionen + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + Wenn die Option deaktiviert ist, dann wird nur das Tabellen DDL (CREATE TABLE Statement) exportiert. + + + + Export table data + Tabellendaten exportieren + + + + Export table indexes + Tabellenindizes exportieren + + + + Export table triggers + Tabellentrigger exportieren + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Hinweis: Das Exportieren von Tabellen, Indizes und Triggern könnte von einigen Ausgabeformaten nicht unterstützt werden. + + + + Select database objects to export + Wählen Sie die zu exportierenden Datebankobjekte aus + + + + Export data from tables + Daten aus Tabellen exportieren + + + + Select all + Alles auswählen + + + + Deselect all + Auswahl aufheben + + + + + Database: + Datenbank: + + + + Query to export results for + Abfrage deren Ergebnisse exportiert werden sollen + + + + Query to be executed for results: + Auszuführende Abfrage... : + + + + Export format and options + Exportformat und Optionen + + + + Export format + Exportformat + + + + Output + Ausgabe + + + + Exported file path + Exportverzeichnis + + + + Clipboard + Zwischenablage + + + + File + Datei + + + + Exported text encoding: + Exportierte Textkodierung: + + + + Export format options + Optionen des Exportformats + + + + Cancel + Abbrechen + + + + + + Select database to export. + Wählen Sie die zu exportierenden Datebank aus. + + + + Select table to export. + Wählen Sie die zu exportierenden Tabellen aus. + + + + Enter valid query to export. + Geben Sie eine gültige Abfrage für den Export an. + + + + Select at least one object to export. + Wählen Sie ein zu exportierendes Datebankobjekt aus. + + + + You must provide a file name to export to. + Sie müssen einen Namen für die Exportdatei angeben. + + + + Path you provided is an existing directory. You cannot overwrite it. + Das von Ihnen angegebene Verzeichnis existiert bereits. Es kann nicht überschrieben werden. + + + + The directory '%1' does not exist. + Das Verzeichnis '%1' existiert nicht. + + + + The file '%1' exists and will be overwritten. + Die Datei '%1' existiert bereits und wird überschrieben werden. + + + + All files (*) + Alle Dateien (*) + + + + Pick file to export to + Wählen Sie eine Datei aus in die exportiert werden soll + + + + Internal error during export. This is a bug. Please report it. + Es trat ein interner Fehler während des Exportvorgangs auf. Dies ist ein Fehler, bitte melden Sie ihn dem Programmautor. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Fehler + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Die Zelle kann nicht editiert. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Schriftart auswählen + + + + Form + + + Active SQL formatter plugin + Aktives SQL Formatierungsplugin + + + + FormView + + + Commit row + form view + Zeile speichern (Commit) + + + + Rollback row + form view + Zeile rückgängig (Rollback) + + + + First row + form view + Erste Zeile + + + + Previous row + form view + Vorherige Zeile + + + + Next row + form view + Nächste Zeile + + + + Last row + form view + Letzte Zeile + + + + Insert new row + form view + Neue Zeile einfügen + + + + Delete current row + form view + Aktuelle Zeile löschen + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Eingabeargumente + + + + Undefined + Undefiniert + + + + Databases + Datenbanken + + + + Register in all databases + In allen Datenbanken registrieren + + + + Register in following databases: + In den folgenden Datenbanken registrieren: + + + + Type: + Typ: + + + + Function name: + Funktionsname: + + + + Implementation language: + Implementationssprache: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialisierungsanweisungen: + + + + + Function implementation code: + Funktionsanweisungen: + + + + Final step implementation code: + Abschlussanweisungen: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Speichern aller Funktionsänderungen + + + + Rollback all function changes + Zurücknehmen aller Funktionsänderungen + + + + Create new function + Neue Funktion erstellen + + + + Delete selected function + Ausgewählte Funktion löschen + + + + Custom SQL functions manual + Anleitung zu 'Benutzerdefinierte SQL Funktionen' + + + + Add function argument + Funktionsargument zufügen + + + + Rename function argument + Funktionsargument umbenennen + + + + Delete function argument + Funktionsargument löschen + + + + Move function argument up + Funktionsargument hochschieben + + + + Move function argument down + Funktionsargument runterschieben + + + + Scalar + Skalar + + + + Aggregate + Aggregat + + + + Enter a non-empty, unique name of the function. + Geben Sie einen eindeutigen Namen für die Funktion ein. + + + + Pick the implementation language. + Wählen Sie die Sprache aus. + + + + Per step code: + Einzelschrittanweisung: + + + + Enter a non-empty implementation code. + Geben Sie die Anweisungen ein. + + + + argument + new function argument name in function editor window + Argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Daten importieren + + + + Table to import to + Tabelle in die importiert werden soll + + + + Table + Tabelle + + + + Database + Datenbank + + + + Data source to import from + Datenquelle von der aus importiert werden soll + + + + Data source type + Datenquellentyp + + + + Options + Optionen + + + + Text encoding: + Textkodierung: + + + + Input file: + Eingabedatei: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>Wenn diese Option aktiviert ist, wird jede Verletzung von Bedingungen oder ein ungültiges Datenformat (falsche Anzahl an Spalten) oder jedes andere Problem, das während des Imports auftritt, ignoriert und der Import wird fortgesetzt.</p> + + + + Ignore errors + Fehler ignorieren + + + + Data source options + Datenquellenoptionen + + + + Cancel + Abbrechen + + + + If you type table name that doesn't exist, it will be created. + Wenn Sie einen Tabellenname eingeben, der noch nicht existiert, dann wird diese neue Tabelle erzeugt werden. + + + + Enter the table name + Datenbankname eingeben + + + + Select import plugin. + Importplugin auswählen + + + + You must provide a file to import from. + Sie müssen den Namen der Importdatei angeben. + + + + The file '%1' does not exist. + Die Datei '%1' existiert nicht. + + + + Path you provided is a directory. A regular file is required. + Der von Ihnen angegebene Pfad ist ein Verzeichnis. Es wird jedoch eine Datei benötigt. + + + + Pick file to import from + Wählen Sie eine Datei aus von der importiert werden soll. + + + + IndexDialog + + + + Index + Index + + + + Column + Spalte + + + + Sort + Sortierung + + + + Collation + Kollation + + + + On table: + Auf Tabelle: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partieller Indexzustand + + + + Unique index + Einzigartiger Index + + + + Index name: + Indexname: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Es wurde versucht den Index-Dialog für eine geschlossene oder nicht existente Datenbank zu öffnen. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Der Index %1 kann nicht vollständig bearbeitet werden, da der Index-Dialog nicht geöffnet werden kann. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Tabelle für den Index auswählen. + + + + Select at least one column. + Mindestens eine Spalte auswählen. + + + + Enter a valid condition. + Geben Sie einen gültigen Zustand ein. + + + + default + index dialog + Standard + + + + Sort order + table constraints + Sortierung + + + + + Error + index dialog + Fehler + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Der eindeutige Index kann nicht erzeigt werden, da Werte in den selektierten Spalten nicht eundeutig sind. Möchten Sie die zugehörige SELECT Abfrage ausführen, um die uneindeutigen Werte zu sehen? + + + + An error occurred while executing SQL statements: +%1 + Fehler beim Ausführen des folgenden SQL Statments: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Sprache + + + + Please choose language: + Bitte Sprache auswählen: + + + + MainWindow + + + Database toolbar + Datenbankleiste + + + + Structure toolbar + Bearbeitungsleiste + + + + Tools + Werkzeuge + + + + Window list + Fensterliste + + + + View toolbar + Ansichtenleiste + + + + Configuration widgets + Konfigurationshelfer + + + + Syntax highlighting engines + Syntaxhervorhebungen + + + + Data editors + Dateneditoren + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Ablauf im Debugmodus. Zum Öffnen der Debugkonsole drücken Sie %1 oder wählen Menü 'Hilfe' den Eintrag 'Debugkonsole öffnen' aus. + + + + Running in debug mode. Debug messages are printed to the standard output. + Ablauf im Debugmodus. Debugmeldungen werden in der Standardausgabe angezeigt.. + + + + You need to restart application to make the language change take effect. + Das Programm muss neu gestartet werden, damit die Änderung der Sprache wirksam wird. + + + + Open SQL &editor + SQL &Editor öffnen + + + + Open DDL &history + DDL &Verlauf öffnen + + + + Open SQL &functions editor + SQL &Funktionseditor öffnen + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Erwei&terungsmanager öffnen + + + + &Import + &Importieren + + + + E&xport + E&xportieren + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Nächstes Fenster + + + + Previous window + Vorheriges Fenster + + + + Hide status field + Statusfeld verbergen + + + + Close &all windows + &Alle Fenster schließen + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Debug Konsole öffnen + + + + Open CSS Console + CSS Konsole öffnen + + + + Report a &bug + Report a &bug + + + + D&onate + Spen&den + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Nach Akt&ualisierungen suchen + + + + &Database + menubar + &Datenbank + + + + &Structure + menubar + &Struktur + + + + &View + menubar + &Ansicht + + + + Window list + menubar view menu + Fensterliste + + + + &Tools + menubar + &Werkzeuge + + + + &Help + &Hilfe + + + + Could not set style: %1 + main window + Der folgende Stil kann nicht gesetzt werden: %1 + + + + Cannot export, because no export plugin is loaded. + Es kann nicht exportiert werden, da kein Export Plugin geladen ist. + + + + Cannot import, because no import plugin is loaded. + Es kann nicht importiert werden, da kein Import Plugin geladen ist. + + + + Rename window + Fenster umbenennen + + + + Enter new name for the window: + Neuen Namen für das Fenster eingeben: + + + + New updates are available. <a href="%1">Click here for details</a>. + Neues Update verfügbar. <a href="%1">Weitere Details</a>. + + + + You're running the most recent version. No updates are available. + Sie haben bereits die aktuellste Version. Keine Update verfügbar. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Die Datenbank, die mittels Programmparameter übergeben wurde (%1), war bereits in der Liste unter dem Namen %2 vorhanden. + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Die Datenbank, die mittels Programmparameter übergeben wurde (%1), wurde in der Liste termporär unter dem Namen %2 zugefügt. + + + + Could not add database %1 to list. + Die Datenbank %1 konnte nicht hinzugefügt werden. + + + + MdiWindow + + + Uncommitted changes + Nicht übertragene Änderungen + + + + Close anyway + Trotzdem schließen + + + + Don't close + Nicht schließen + + + + MultiEditor + + + Null value + multieditor + NULL Wert + + + + Configure editors for this data type + Konfigurationseditoren für diesen Datentyp + + + + Open another tab + Weiteren Tab öffnen + + + + Foreign Key + Fremdschlüssel + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Das Dateneditor Plugin '%1' ist nicht geladen, obwohl es für den '%1' Datentypen als Editor definiert ist. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Gelöscht + + + + Read only + multieditor + Nur lesend + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Datum + + + + MultiEditorDateTimePlugin + + + Date & time + Datum & Zeit + + + + MultiEditorHexPlugin + + + Hex + Hexadezimal + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Nummer + + + + MultiEditorText + + + Tab changes focus + Reiter Änderungen Fokus + + + + Cut + Ausschneiden + + + + Copy + Kopieren + + + + Paste + Einfügen + + + + Delete + Löschen + + + + Undo + Rückgängig + + + + Redo + Wiederholen + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Zeit + + + + NewConstraintDialog + + + New constraint + Neue Bedingung + + + + + Primary Key + new constraint dialog + Primärschlüssel + + + + + Foreign Key + new constraint dialog + Fremdschlüssel + + + + + Unique + new constraint dialog + Einzigartig + + + + + Check + new constraint dialog + Prüfung + + + + Not NULL + new constraint dialog + Nicht NULL + + + + Collate + new constraint dialog + Kollation + + + + Generated + new constraint dialog + Generiert + + + + Default + new constraint dialog + Standard + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio Updates + + + + New version is available! + Eine neue Version ist verfügbar! + + + + Download new version! + Neue Version herunterladen! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + Das neue Versionspaket wird heruntergeladen. Es liegt an Ihnen, es zu installieren, wenn Sie bereit sind. + + + + Open SQLiteStudio home page. + SQLiteStudio Startseite öffnen. + + + + Read release notes && download package yourself. + Versionshinweise lesen && das Paket selbst herunterladen. + + + + Just close this window. + Nur dieses Fenster schließen. + + + + Check for updates on startup + Beim Programmstart auf Updates prüfen + + + + Not now. + Nicht jetzt. + + + + PopulateConfigDialog + + + Populating configuration + Konfiguration auffüllen + + + + Configuring <b>%1</b> for column <b>%2</b> + Konfiguriere <b>%1</b> für Spalte <b>%2</b> + + + + PopulateDialog + + + Populate table + Tabelle füllen + + + + Database + Datenbank + + + + Table + Tabelle + + + + Columns + Spalten + + + + Number of rows to populate: + Anzahl an Datenzeilen zum Auffüllen: + + + + Populate + populate dialog button + Füllen + + + + Abort + Abbrechen + + + + Configure + Konfigurieren + + + + Populating configuration for this column is invalid or incomplete. + Die Konfigurationsauffüllung für diese Spalte ist ungültig oder unvollständig. + + + + Select database with table to populate + Wählen Sie die Datebank und Tabelle zum Auffüllen aus + + + + Select table to populate + Wählen Sie die Tabelle zum Auffüllen aus + + + + You have to select at least one column. + Sie müssen mindestens eine Spalte auswählen. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Spalten, die das Ergebnis von verbundenen %1 Abfragen sind (solche, die %2, %3 oder %4 Schlüsselwörter enthalten), können nicht editiert werden. + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + Der Ausführungsmechanismus hat Probleme die ROWID korrekt zu extrahieren. Dies könnte ein Programmfehler sein, den Sie evtl. melden möchten. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Die betreffende Spalte ist das Ergebnis eines SQL-Ausdrucks statt einer einfachen Spaltenselektion. Solche Spalten können nicht editiert werden. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Die betreffende Spalte gehört zu einer eingeschränkten SQLite Tabelle. Solche Tabellen können nicht direkt editiert werden. + + + + Cannot edit results of query other than %1. + Es können keine Ergebnisse von einer von %1 abweichenden Abfrage editiert werden. + + + + Cannot edit columns that are result of aggregated %1 statements. + Es können keine Spalten editiert werden, die das Ergebnis einer aggregierten %1 Abfrage sind. + + + + Cannot edit columns that are result of %1 statement. + Es können keine Spalten editiert werden, die das Ergebnis von %1 Abfragen sind. + + + + Cannot edit columns that are result of common table expression statement (%1). + Es können keine Spalten editiert werden, die das Ergebnis von allgemeinen Tabellenausdrücken sind (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + Bei Konfikt: %1 + + + + references table %1, column %2 + data view tooltip + Referenztabelle %1, Zeile %2 + + + + condition: %1 + data view tooltip + Zustand: %1 + + + + collation name: %1 + data view tooltip + Name der Kollation: %1 + + + + Data grid view + Ergebnisansicht + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Kopiert Zelleninhalt(e) in die Zwischenablage + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Fügt Zelleninhalt(e) von der Zwischenablage ein + + + + Set empty value to selected cell(s) + Fügt einen leeren Wert in die selektierte(n) Zelle(n) ein + + + + Set NULL value to selected cell(s) + Fügt den NULL Wert in die selektierte(n) Zelle(n) ein + + + + Commit changes to cell(s) contents + Änderungen der Zellenninhalte speichern + + + + Rollback changes to cell(s) contents + Änderungen der Zelleninhalte zurücknehmen + + + + Delete selected data row + Markierte Datenzeile löschen + + + + Insert new data row + Neue Datenzeile einfügen + + + + Open contents of selected cell in a separate editor + Inhalt der markierten Zelle im separaten Editor öffnen + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Verfügbare Gesamtseiten: %1 + + + + Total rows loaded: %1 + Insgesamt geladene Zeilen: %1 + + + + Data view (both grid and form) + Ergebnisansicht (tabellarisch und Formular) + + + + Refresh data + Daten aktualisieren + + + + Switch to grid view of the data + Zur tabellarischen Ergebnisansicht wechseln + + + + Switch to form view of the data + Zur Formularansicht wechseln + + + + Database list + Liste der Datenbanken + + + + Delete selected item + Gewählten Eintrag löschen + + + + Clear filter contents + Filter zurücksetzen + + + + Refresh schema + Schema aktualisieren + + + + Refresh all schemas + Alle Schemas aktualisieren + + + + Add database + Datenbank hinzufügen + + + + Select all items + Alles auswählen + + + + Copy selected item(s) + Gewählte Einträge kopieren + + + + + + Paste from clipboard + Von der Zwischenablage einfügen + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tabellen + + + + Indexes + Indizes + + + + Triggers + Trigger + + + + Views + Views + + + + Columns + Spalten + + + + Data form view + Formularansicht der Ergebnisse + + + + Commit changes for current row + Änderungen der aktuellen Zeile speichern + + + + Rollback changes for current row + Änderungen der aktuellen Zeile zurücknehmen + + + + Go to first row on current page + Springe zur ersten Zeile dieser Seite + + + + Go to next row + Springe zur nächsten Zeile + + + + Go to previous row + Springe zur vorherigen Zeile + + + + Go to last row on current page + Springe zur letzten Zeile dieser Seite + + + + Insert new row + Neue Zeile einfügen + + + + Delete current row + Derzeitige Zeile löschen + + + + Main window + Hauptfenster + + + + Open SQL editor + SQL Editor öffnen + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Vorheriges Fenster + + + + Next window + Nächstes Fenster + + + + Hide status area + Statusfeld verbergen + + + + Open user manual + Open user manual + + + + Open configuration dialog + Konfigurationsdialog öffnen + + + + Open Debug Console + Debug Konsole öffnen + + + + Open CSS Console + CSS Konsole öffnen + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Editor für Textwerte in Zellen + + + + + Cut selected text + Gewählten Text ausschneiden + + + + + Copy selected text + Gewählten Text kopieren + + + + + Delete selected text + Gewählten Text löschen + + + + + Undo + Rückgängig + + + + + Redo + Wiederholen + + + + SQL editor input field + SQL Editor Eingabefeld + + + + Select whole editor contents + Gesamten Editorinhalt auswählen + + + + Save contents into a file + Inhalte in eine Datei speichern + + + + Load contents from a file + Inhalte aus einer Datei laden + + + + Find in text + Suche im Text + + + + Find next + Nächster Fund + + + + Find previous + Vorheriger Fund + + + + Replace in text + Ersetze im Text + + + + Delete current line + Aktuelle Zeile löschen + + + + Request code assistant + Code-Assistenten anfordern + + + + Format contents + Format-Inhalte + + + + Move selected block of text one line down + Selektierten Textblock eine Zeile nach unten verschieben + + + + Move selected block of text one line up + Selektierten Textblock eine Zeile nach oben verschieben + + + + Copy selected block of text and paste it a line below + Selektierten Textblock kopieren und unterhalb einfügen + + + + Copy selected block of text and paste it a line above + Selektierten Textblock kopieren und oberhalb einfügen + + + + Toggle comment + Kommentar umschalten + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + Alle SQLite Datenbanken + + + + All files + Alle Dateien + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL Editor-Fenster + + + + Execute query + Abfrage ausführen + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Abfrage "%1" ausführen + + + + Switch current working database to previous on the list + Wechsel von der aktuellen Datenbank zur vorherigen in der Liste + + + + Switch current working database to next on the list + Wechsel von der aktuellen Datenbank zur nächsten in der Liste + + + + Go to next editor tab + Gehe zum nächsten Editor-Reiter + + + + Go to previous editor tab + Gehe zum vorherigen Editor-Reiter + + + + Move keyboard input focus to the results view below + Tastatureingabe-Fokus in das untere Ergebnisfenster setzen + + + + Move keyboard input focus to the SQL editor above + Tastatureingabe-Fokus in das obere SQL Editorfenster setzen + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Tabellenfenster + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Aktualisiere Tabellenstruktur + + + + Add new column + Neue Spalte zufügen + + + + Edit selected column + Gewählte Spalte bearbeiten + + + + Delete selected column + Gewählte Spalte löschen + + + + Export table data + Tabellendaten exportieren + + + + Import data to the table + Daten in die Tabelle importieren + + + + Add new table constraint + Neue Tabellenbedingung zufügen + + + + Edit selected table constraint + Markierte Tabellenbedingung bearbeiten + + + + Delete selected table constraint + Markierte Tabellenbedingung löschen + + + + Refresh table index list + Aktualisiere Tabellenindexliste + + + + Add new index + Neuen Index zufügen + + + + Edit selected index + Gewählten Index bearbeiten + + + + Delete selected index + Gewählten Index löschen + + + + Refresh table trigger list + Aktualisiere Tabellentriggerliste + + + + + Add new trigger + Neuen Trigger zufügen + + + + + Edit selected trigger + Gewählten Trigger bearbeiten + + + + + Delete selected trigger + Gewählten Trigger löschen + + + + + Go to next tab + Springe zum nächsten Reiter + + + + + Go to previous tab + Springe zum vorherigen Reiter + + + + A view window + Neues Fenster zufügen + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Aktualisiere View Triggerliste + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Sind Sie sicher, dass Sie das Programm beenden wollen? + +Folgende Punkte sind unerledigt: + + + + SearchTextDialog + + + Find or replace + Finden oder Ersetzen + + + + Find: + Finden: + + + + Case sensitive + Groß- und Kleinschreibung beachten + + + + Search backwards + Suche rückwärts + + + + Regular expression matching + Prüfung nach regulärem Ausdruck + + + + Replace && +find next + Ersetzen && weitersuchen + + + + Replace with: + Ersetzen mit: + + + + Replace all + Alles ersetzen + + + + Find + Finden + + + + SortDialog + + + Sort by columns + Nach Spalten sortiert + + + + + Column + Spalte + + + + + Order + Sortierung + + + + Sort by: %1 + Sortiert nach: %1 + + + + Move column up + Spalte nach oben verschieben + + + + Move column down + Spalte nach unten verschieben + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Ausschneiden + + + + Copy + sql editor + Kopieren + + + + Paste + sql editor + Einfügen + + + + Delete + sql editor + Löschen + + + + Select all + sql editor + Alles auswählen + + + + Undo + sql editor + Rückgängig + + + + Redo + sql editor + Wiederholen + + + + Complete + sql editor + Komplett + + + + Format SQL + sql editor + SQL formatieren + + + + Save SQL to file + sql editor + SQL in Datei speichern + + + + Select file to save SQL + sql editor + SQL aus Datei laden + + + + Load SQL from file + sql editor + Zeile löschen + + + + Delete line + sql editor + Zeile löschen + + + + Move block down + sql editor + Block nach unten verschieben + + + + Move block up + sql editor + Block nach oben verschieben + + + + Copy block down + sql editor + Block nach unten kopieren + + + + Copy up down + sql editor + Kopiere auf ab + + + + Find + sql editor + Finden + + + + Find next + sql editor + Nächster Fund + + + + Find previous + sql editor + Vorheriger Fund + + + + Replace + sql editor + Ersetzen + + + + Toggle comment + sql editor + Kommentar umschalten + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Die Datei '%1' kann nicht für Schreibzugriffe geöffnet werden: %2 + + + + Saved SQL contents to file: %1 + SQL Inhalte in Datei speichern: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Die Funktion Autovervollständigung kann nur genutzt werden, wenn eine gültige Datenbank für den SQL Editor gewählt wurde. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Der Text im SQL Editor ist sehr groß, daher wurde die Syntaxkontrolle und die farbliche Hervorhebung von Objekten vorübergehend deaktiviert. + + + + Save to file + In Datei speichern + + + + SQL scripts (*.sql);;All files (*) + SQL Skripte (*.sql);;Alle Dateien (*) + + + + Open file + Datei öffnen + + + + Could not open file '%1' for reading: %2 + Die Datei '%1' kann nicht für Lesezugriffe geöffnet werden: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Das Dokumentenende wurde erreicht. Drücken Sie 'Nächster Fund', um die Suche am Dokumentenanfang fortzusetzen. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Spalte: + + + + Data type: + data view + Datentyp: + + + + Table: + data view tooltip + Tabelle: + + + + Constraints: + data view tooltip + Bedingungen: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Die Zelle kann nicht editiert. Details: %1 + + + + The row is marked for deletion. + Diese Zeile ist zum Löschen markiert. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Es kann nur eine Abfrage gleichzeitig ausgeführt werden. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Es können keine Daten für eine Zelle gespeichert werden, die eine bereits geschlossene Datenbank referenziert. + + + + Could not begin transaction on the database. Details: %1 + Es kann keine Transaktion auf der Datenbank gestartet werden. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + Fehler beim Rollback der Transaktion: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Es wurde versucht eine nicht editierbare Zelle zu committen (derzeit modifiziert und auf das Commit wartend)! Dies ist ein Fehler den Sie melden sollten. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Fehler beim Ausführen der SQL-Abfrage auf der Datenbank '%1': %2 + + + + Error while loading query results: %1 + Fehler beim Laden der Abfrageergebnisse: %1 + + + + Insert multiple rows + Mehrere Zeilen einfügen + + + + Number of rows to insert: + Anzahl an Zeilen zum Einfügen: + + + + Delete rows + Zeilen löschen + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Kopieren + + + + Copy with headers + Copy with headers + + + + Copy as... + Kopieren als... + + + + Paste + Einfügen + + + + Paste as... + Einfügen als... + + + + Set NULL values + NULL Wert setzen + + + + Erase values + Werte löschen + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Gewählte Zellen speichern + + + + Rollback selected cells + Gewählte Zellen wiederherstellen + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Sortierspalten definieren + + + + Remove custom sorting + Benutzerdefinierte Sortierung entfernen + + + + Insert row + Zeile einfügen + + + + Insert multiple rows + Mehrere Zeilen einfügen + + + + Delete selected row + Gewählte Zeile löschen + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Wert im Editor bearbeiten + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + Es sind keine Elemente selektiert in die der Inhalt der Zwischenablage eingefügt werden könnte. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + Diese Zeile ist zum Löschen markiert. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Werte editieren + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Fehler beim Löschen der Zeile aus Tabelle %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Datenbanken + + + + Register in all databases + In allen Datenbanken registrieren + + + + Register in following databases: + In den folgenden Datenbanken registrieren: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + Alle Dateien (*) + + + + Open file + Datei öffnen + + + + StatusField + + + Status + Status + + + + Copy + Kopieren + + + + Clear + Leeren + + + + TableConstraintsModel + + + Type + table constraints + Typ + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Fremde Tabelle: + + + + Columns + Spalten + + + + Local column + Lokale Spalte + + + + Foreign column + Fremdspalte + + + + Reactions + Reaktionen + + + + Deferred foreign key + Verzögerter Fremdschlüssel + + + + Named constraint + Benannte Bedingung + + + + Constraint name + Name der Bedingung + + + + Pick the foreign column. + Wählen Sie die Fremdspalte aus. + + + + Pick the foreign table. + Wählen Sie die Fremdtabelle aus. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Geben Sie einen Namen für die Bedingung ein. + + + + Foreign column + table constraints + Fremdspalte + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Spalten + + + + Column + Spalte + + + + Collation + Kollation + + + + Sort + Sortierung + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Automatisch hochzählend + + + + Named constraint + Benannte Bedingung + + + + Constraint name + Name der Bedingung + + + + On conflict + Bei Konflikt + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sortierung + + + + Select at least one column. + Mindestens eine Spalte auswählen. + + + + Enter a name of the constraint. + Geben Sie einen Namen für die Bedingung ein. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Datentyp + + + + Primary +Key + table structure columns + Primärschlüssel + + + + Foreign +Key + table structure columns + Fremdschlüssel + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Prüfung + + + + Not +NULL + table structure columns + Nicht +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generiert + + + + Default value + table structure columns + Standardwert + + + + TableWindow + + + Structure + Struktur + + + + Table name: + Tabellenname: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Daten + + + + Constraints + Bedingungen + + + + Indexes + Indizes + + + + Triggers + Trigger + + + + DDL + DDL + + + + Export table + table window + Tabelle exportieren + + + + Import data to table + table window + Daten in Tabelle importieren + + + + Populate table + table window + Tabelle füllen + + + + Refresh structure + table window + Struktur aktualisieren + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Spalte hinzufügen + + + + Edit column + table window + Spalte bearbeiten + + + + + Delete column + table window + Spalte löschen + + + + Move column up + table window + Spalte nach oben verschieben + + + + Move column down + table window + Spalte nach unten verschieben + + + + Create similar table + table window + Erzeuge identische Tabelle + + + + Reset autoincrement value + table window + Autoincrement-Wert zurücksetzen + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Primärschlüssel hinzufügen + + + + Add table foreign key + table window + Fremdschlüssel hinzufügen + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Index erstellen + + + + Edit index + table window + Index bearbeiten + + + + Delete index + table window + Index löschen + + + + Refresh trigger list + table window + Trigger Liste aktualisieren + + + + + Create trigger + table window + Trigger erstellen + + + + Edit trigger + table window + Trigger bearbeiten + + + + Delete trigger + table window + Trigger löschen + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Datenbank + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Autoincrement zurücksetzen + + + + Are you sure you want to reset autoincrement value for table '%1'? + Sind Sie sicher, dass Sie den Autoincrement Wert für die Tabelle '%1' zurücksetzen möchten? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + Ein Fehler ist aufgetreten beim Zurücksetzen des Autoincrementwertes für die Tabelle '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Bedingung löschen + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Spalten + + + + Partial index condition + table window indexes + Partieller Indexzustand + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Alles auswählen + + + + Deselect all + Auswahl aufheben + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + Auf Tabelle: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Geben Sie einen gültigen Zustand ein. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Fehler + + + + An error occurred while executing SQL statements: +%1 + Fehler beim Ausführen des folgenden SQL Statments: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Abfrage + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Daten + + + + Triggers + Trigger + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Datenbank + + + + Refresh the view + view window + Ansicht aktualisieren + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Spalte hinzufügen + + + + Edit column + view window + Spalte bearbeiten + + + + Delete column + view window + Spalte löschen + + + + Move column up + view window + Spalte nach oben verschieben + + + + Move column down + view window + Spalte nach unten verschieben + + + + Refresh trigger list + view window + Trigger Liste aktualisieren + + + + Create new trigger + view window + Trigger erstellen + + + + Edit selected trigger + view window + Trigger editieren + + + + Delete selected trigger + view window + Trigger löschen + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Nicht übertragene Änderungen + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_el_GR.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_el_GR.ts new file mode 100644 index 0000000..9b21de9 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_el_GR.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_en_US.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_en_US.ts new file mode 100644 index 0000000..389b2b6 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_en_US.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es.qm b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es.qm and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es.ts deleted file mode 100644 index fc3f218..0000000 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es.ts +++ /dev/null @@ -1,6612 +0,0 @@ - - - - - AboutDialog - - - About SQLiteStudio and licenses - - - - - About - - - - - Licenses - - - - - Environment - - - - - Icon directories - - - - - Form directories - - - - - Plugin directories - - - - - Application directory - - - - - SQLite 3 version: - - - - - Configuration directory - - - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - - Qt version: - - - - - Portable distribution. - - - - - MacOS X application boundle distribution. - - - - - Operating system managed distribution. - - - - - Copy - - - - - <h3>Table of contents:</h3><ol>%2</ol> - - - - - BindParamsDialog - - - Query parameters - - - - - Please provide values for query parameters - - - - - CollationsEditor - - - Filter collations - - - - - Collation name: - - - - - Implementation language: - - - - - Databases - - - - - Register in all databases - - - - - Register in following databases: - - - - - Implementation code: - - - - - Collations editor - - - - - Commit all collation changes - - - - - Rollback all collation changes - - - - - Create new collation - - - - - Delete selected collation - - - - - Editing collations manual - - - - - Enter a non-empty, unique name of the collation. - - - - - Pick the implementation language. - - - - - Enter a non-empty implementation code. - - - - - Collations editor window has uncommitted modifications. - - - - - ColorButton - - - Pick a color - - - - - ColumnCollatePanel - - - Collation name: - - - - - Named constraint: - - - - - Enter a name of the constraint. - - - - - Enter a collation name. - - - - - ColumnDefaultPanel - - - Default value: - - - - - Named constraint: - - - - - Enter a default value expression. - - - - - Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - - - - - Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - - - - - Enter a name of the constraint. - - - - - ColumnDialog - - - Column - - - - - Name and type - - - - - Scale - - - - - Precision - - - - - Data type: - - - - - Column name: - - - - - Size: - - - - - Constraints - - - - - Unique - - - - - - - - - - - Configure - - - - - Foreign Key - - - - - Collate - - - - - Not NULL - - - - - Check condition - - - - - Primary Key - - - - - Default - - - - - Advanced mode - - - - - Add constraint - column dialog - - - - - Edit constraint - column dialog - - - - - - Delete constraint - column dialog - - - - - Move constraint up - column dialog - - - - - Move constraint down - column dialog - - - - - Add a primary key - column dialog - - - - - Add a foreign key - column dialog - - - - - Add an unique constraint - column dialog - - - - - Add a check constraint - column dialog - - - - - Add a not null constraint - column dialog - - - - - Add a collate constraint - column dialog - - - - - Add a default constraint - column dialog - - - - - Are you sure you want to delete constraint '%1'? - column dialog - - - - - Correct the constraint's configuration. - - - - - This constraint is not officially supported by SQLite 2, -but it's okay to use it. - - - - - Scale is not allowed for INTEGER PRIMARY KEY columns. - - - - - Precision cannot be defined without the scale. - - - - - Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - - - - - INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - - - - - Precision is not allowed for INTEGER PRIMARY KEY columns. - - - - - ColumnDialogConstraintsModel - - - Type - column dialog constraints - - - - - Name - column dialog constraints - - - - - Details - column dialog constraints - - - - - ColumnForeignKeyPanel - - - Foreign table: - - - - - Foreign column: - - - - - Reactions - - - - - Deferred foreign key - - - - - Named constraint - - - - - Constraint name - - - - - Pick the foreign table. - - - - - Pick the foreign column. - - - - - Enter a name of the constraint. - - - - - ColumnPrimaryKeyPanel - - - Autoincrement - - - - - Sort order: - - - - - Named constraint: - - - - - On conflict: - - - - - Enter a name of the constraint. - - - - - ColumnUniqueAndNotNullPanel - - - Named constraint: - - - - - On conflict: - - - - - Enter a name of the constraint. - - - - - CompleterWindow - - - Column: %1 - completer statusbar - - - - - Table: %1 - completer statusbar - - - - - Index: %1 - completer statusbar - - - - - Trigger: %1 - completer statusbar - - - - - View: %1 - completer statusbar - - - - - Database: %1 - completer statusbar - - - - - Keyword: %1 - completer statusbar - - - - - Function: %1 - completer statusbar - - - - - Operator: %1 - completer statusbar - - - - - String - completer statusbar - - - - - Number - completer statusbar - - - - - Binary data - completer statusbar - - - - - Collation: %1 - completer statusbar - - - - - Pragma function: %1 - completer statusbar - - - - - ConfigDialog - - - - Configuration - - - - - Search - - - - - General - - - - - Keyboard shortcuts - - - - - Look & feel - - - - - Style - - - - - Fonts - - - - - Colors - - - - - Plugins - - - - - Code formatters - - - - - Data browsing - - - - - Data editors - - - - - Database dialog window - - - - - <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> - - - - - Do not mark database to be "permanent" by default - - - - - <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> - - - - - Try to bypass dialog completly when dropping database file onto the list - - - - - Data browsing and editing - - - - - Number of data rows per page: - - - - - - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - - - - - Limit initial data column width to (in pixels): - - - - - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> - - - - - Show column and row details tooltip in data view - - - - - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - - - - - Inserting new row in data grid - - - - - Before currently selected row - - - - - After currently selected row - - - - - At the end of data view - - - - - <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - - - - - <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - - - - - Place data tab as first tab in a Table Window - - - - - <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - - - - - <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - - - - - Place data tab as first tab in a View Window - - - - - Data types - - - - - Available editors: - - - - - Editors selected for this data type: - - - - - Schema editing - - - - - Number of DDL changes kept in history. - - - - - DDL history size: - - - - - SQL queries - - - - - - Number of queries kept in the history. - - - - - History size: - - - - - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> - - - - - Execute only the query under the cursor - - - - - Updates - - - - - Automatically check for updates at startup - - - - - Session - - - - - Restore last session (active MDI windows) after startup - - - - - Status Field - - - - - <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> - - - - - Always open Status panel when new message is printed - - - - - Filter shortcuts by name or key combination - - - - - Action - - - - - Key combination - - - - - - Language - - - - - Changing language requires application restart to take effect. - - - - - Compact layout - - - - - <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - - - - - Use compact layout - - - - - - Database list - - - - - If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. - - - - - Sort table columns alphabetically - - - - - Expand tables node when connected to a database - - - - - <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> - - - - - Display additional labels on the list - - - - - For regular tables labels will show number of columns, indexes and triggers for each of tables. - - - - - Display labels for regular tables - - - - - Virtual tables will be marked with a 'virtual' label. - - - - - Display labels for virtual tables - - - - - Expand views node when connected to a database - - - - - If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) - - - - - Sort objects (tables, indexes, triggers and views) alphabetically - - - - - Display system tables and indexes on the list - - - - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - - - - - Number of memorized table populating configurations - - - - - Keep NULL value when entering empty value - - - - - <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> - - - - - Use DEFAULT value (if defined), when committing NULL value - - - - - Table windows - - - - - Open Table Windows with the data tab for start - - - - - View windows - - - - - Open View Windows with the data tab for start - - - - - Don't show DDL preview dialog when committing schema changes - - - - - - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - - - - - Number of memorized query parameters - - - - - Main window dock areas - - - - - Left and right areas occupy corners - - - - - Top and bottom areas occupy corners - - - - - Hide built-in plugins - - - - - Current style: - - - - - Preview - - - - - Enabled - - - - - Disabled - - - - - Active formatter plugin - - - - - SQL editor font - - - - - Database list font - - - - - Database list additional label font - - - - - Data view font - - - - - Status field font - - - - - SQL editor colors - - - - - Current line background - - - - - <p>SQL strings are enclosed with single quote characters.</p> - - - - - String foreground - - - - - <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - - - - - Bind parameter foreground - - - - - Highlighted parenthesis background - - - - - <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - - - - - BLOB value foreground - - - - - Regular foreground - - - - - Line numbers area background - - - - - Keyword foreground - - - - - Number foreground - - - - - Comment foreground - - - - - <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> - - - - - Valid objects foreground - - - - - Data view colors - - - - - <p>Any data changes will be outlined with this color, until they're committed to the database.</p> - - - - - Uncommitted data outline color - - - - - <p>In case of error while committing data changes, the problematic cell will be outlined with this color.</p> - - - - - Commit error outline color - - - - - NULL value foreground - - - - - Deleted row background - - - - - Database list colors - - - - - <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> - - - - - Additional labels foreground - - - - - Status field colors - - - - - Information message foreground - - - - - Warning message foreground - - - - - Error message foreground - - - - - Description: - plugin details - - - - - Category: - plugin details - - - - - Version: - plugin details - - - - - Author: - plugin details - - - - - Internal name: - plugin details - - - - - Dependencies: - plugin details - - - - - Conflicts: - plugin details - - - - - Plugin details - - - - - Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. - - - - - %1 (built-in) - plugins manager in configuration dialog - - - - - Details - - - - - No plugins in this category. - - - - - Add new data type - - - - - Rename selected data type - - - - - Delete selected data type - - - - - Help for configuring data type editors - - - - - ConstraintCheckPanel - - - The condition - - - - - Named constraint: - - - - - On conflict - - - - - Enter a valid condition. - - - - - Enter a name of the constraint. - - - - - ConstraintDialog - - - New constraint - constraint dialog - - - - - Create - constraint dialog - - - - - Edit constraint - dialog window - - - - - Apply - constraint dialog - - - - - Primary key - table constraints - - - - - Foreign key - table constraints - - - - - Unique - table constraints - - - - - Not NULL - table constraints - - - - - Check - table constraints - - - - - Collate - table constraints - - - - - Default - table constraints - - - - - ConstraintTabModel - - - Table - table constraints - - - - - Column (%1) - table constraints - - - - - Scope - table constraints - - - - - Type - table constraints - - - - - Details - table constraints - - - - - Name - table constraints - - - - - CssDebugDialog - - - SQLiteStudio CSS console - - - - - DataView - - - Filter data - data view - - - - - Grid view - - - - - Form view - - - - - Refresh table data - data view - - - - - First page - data view - - - - - Previous page - data view - - - - - Next page - data view - - - - - Last page - data view - - - - - Filter - - - - - Hit Enter key or press "Apply filter" button on toolbar to apply new value. - - - - - Show filter inputs per column - data view - - - - - Apply filter - data view - - - - - Commit changes for selected cells - data view - - - - - Rollback changes for selected cells - data view - - - - - Show grid view of results - sql editor - - - - - Show form view of results - sql editor - - - - - Filter by text - data view - - - - - Filter by the Regular Expression - data view - - - - - Filter by SQL expression - data view - - - - - Tabs on top - data view - - - - - Tabs at bottom - data view - - - - - Place new rows above selected row - data view - - - - - Place new rows below selected row - data view - - - - - Place new rows at the end of the data view - data view - - - - - Total number of rows is being counted. -Browsing other pages will be possible after the row counting is done. - - - - - Row: %1 - - - - - DbConverterDialog - - - Convert database - - - - - Source database - - - - - Source database version: - - - - - Target database - - - - - Target version: - - - - - This is the file that will be created as a result of the conversion. - - - - - Target file: - - - - - Name of the new database: - - - - - This is the name that the converted database will be added to SQLiteStudio with. - - - - - Select source database - - - - - Enter valid and writable file path. - - - - - Entered file exists and will be overwritten. - - - - - Enter a not empty, unique name (as in the list of databases on the left). - - - - - No valid target dialect available. Conversion not possible. - - - - - Select valid target dialect. - - - - - Database %1 has been successfully converted and now is available under new name: %2 - - - - - SQL statements conversion - - - - - Following error occurred while converting SQL statements to the target SQLite version: - - - - - Would you like to ignore those errors and proceed? - - - - - DbDialog - - - Database - - - - - Database type - - - - - Database driver - - - - - Options - - - - - Permanent (keep it in configuration) - - - - - Test connection - - - - - Create new database file - - - - - - File - - - - - Name (on the list) - - - - - <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> - aasfd - - - - - Browse for existing database file on local computer - - - - - Browse - - - - - Enter an unique database name. - - - - - This name is already in use. Please enter unique name. - - - - - <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - - - - - Enter a database file path. - - - - - This database is already on the list under name: %1 - - - - - Select a database type. - - - - - DbObjectDialogs - - - Delete table - - - - - Are you sure you want to delete table %1? - - - - - Delete index - - - - - Are you sure you want to delete index %1? - - - - - Delete trigger - - - - - Are you sure you want to delete trigger %1? - - - - - Delete view - - - - - Are you sure you want to delete view %1? - - - - - - Error while dropping %1: %2 - - - - - Delete objects - - - - - Are you sure you want to delete following objects: -%1 - - - - - Cannot start transaction. Details: %1 - - - - - Cannot commit transaction. Details: %1 - - - - - DbTree - - - Databases - - - - - Filter by name - - - - - Copy - - - - - Paste - - - - - Select all - - - - - Create a group - - - - - Delete the group - - - - - Rename the group - - - - - Import - - - - - Export the table - - - - - Import into the table - - - - - Populate table - - - - - Create similar table - - - - - Reset autoincrement sequence - - - - - Add a column - - - - - Edit the column - - - - - Delete the column - - - - - Delete selected items - - - - - Clear filter - - - - - - Erase table data - - - - - - Database - - - - - Grouping - - - - - Generate query for table - - - - - - Create group - - - - - Group name - - - - - Entry with name %1 already exists in group %2. - - - - - Delete group - - - - - Are you sure you want to delete group %1? -All objects from this group will be moved to parent group. - - - - - Are you sure you want to remove database '%1' from the list? - - - - - Are you sure you want to remove following databases from the list: -%1 - - - - - Remove database - - - - - Vacuum (%1) - - - - - Autoincrement value for table '%1' has been reset successfully. - - - - - Are you sure you want to delete all data from table(s): %1? - - - - - - Cannot import, because no import plugin is loaded. - - - - - Execution from file cancelled. Any queries executed so far have been rolled back. - - - - - &Add a database - - - - - &Edit the database - - - - - &Remove the database - - - - - &Connect to the database - - - - - &Disconnect from the database - - - - - &Export the database - - - - - Con&vert database type - - - - - Vac&uum - - - - - &Integrity check - - - - - Create a &table - - - - - Edit the t&able - - - - - Delete the ta&ble - - - - - Create an &index - - - - - Edit the i&ndex - - - - - Delete the in&dex - - - - - Create a trig&ger - - - - - Edit the trigg&er - - - - - Delete the trigge&r - - - - - Create a &view - - - - - Edit the v&iew - - - - - Delete the vi&ew - - - - - &Refresh all database schemas - - - - - Re&fresh selected database schema - - - - - Open file's directory - - - - - Execute SQL from file - - - - - - Cannot export, because no export plugin is loaded. - - - - - Integrity check (%1) - - - - - Reset autoincrement - - - - - Are you sure you want to reset autoincrement value for table '%1'? - - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - - - - - An error occurred while trying to delete data from table '%1': %2 - - - - - All data has been deleted for table '%1'. - - - - - Following objects will be deleted: %1. - - - - - Following databases will be removed from list: %1. - - - - - Remainig objects from deleted group will be moved in place where the group used to be. - - - - - %1<br><br>Are you sure you want to continue? - - - - - Delete objects - - - - - Could not execute SQL, because application has failed to start transaction: %1 - - - - - Could not open file '%1' for reading: %2 - - - - - Could not execute SQL, because application has failed to commit the transaction: %1 - - - - - Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - - - - - Finished executing %1 queries in %2 seconds. - - - - - Could not execute SQL due to error. - - - - - DbTreeItemDelegate - - - error - dbtree labels - - - - - (system table) - database tree label - - - - - (virtual) - virtual table label - - - - - (system index) - database tree label - - - - - DbTreeModel - - - Database: %1 - dbtree tooltip - - - - - Version: - dbtree tooltip - - - - - File size: - dbtree tooltip - - - - - Encoding: - dbtree tooltip - - - - - Error: - dbtree tooltip - - - - - Table : %1 - dbtree tooltip - - - - - Columns (%1): - dbtree tooltip - - - - - Indexes (%1): - dbtree tooltip - - - - - Triggers (%1): - dbtree tooltip - - - - - Copy - - - - - Move - - - - - Include data - - - - - Include indexes - - - - - Include triggers - - - - - Abort - - - - - Could not add dropped database file '%1' automatically. Manual setup is necessary. - - - - - Referenced tables - - - - - Do you want to include following referenced tables as well: -%1 - - - - - Name conflict - - - - - Following object already exists in the target database. -Please enter new, unique name, or press '%1' to abort the operation: - - - - - SQL statements conversion - - - - - Following error occurred while converting SQL statements to the target SQLite version: - - - - - Would you like to ignore those errors and proceed? - - - - - DdlHistoryWindow - - - Filter by database: - - - - - -- Queries executed on database %1 (%2) --- Date and time of execution: %3 -%4 - - - - - DDL history - - - - - DdlPreviewDialog - - - Queries to be executed - - - - - Don't show again - - - - - DebugConsole - - - SQLiteStudio Debug Console - - - - - EditorWindow - - - Query - - - - - History - - - - - Results in the separate tab - - - - - Results below the query - - - - - - SQL editor %1 - - - - - Results - - - - - Execute query - - - - - Explain query - - - - - Clear execution history - sql editor - - - - - Export results - sql editor - - - - - Create view from query - sql editor - - - - - Previous database - - - - - Next database - - - - - Show next tab - sql editor - - - - - Show previous tab - sql editor - - - - - Focus results below - sql editor - - - - - Focus SQL editor above - sql editor - - - - - Delete selected SQL history entries - sql editor - - - - - Active database (%1/%2) - - - - - Query finished in %1 second(s). Rows affected: %2 - - - - - Query finished in %1 second(s). - - - - - Clear execution history - - - - - Are you sure you want to erase the entire SQL execution history? This cannot be undone. - - - - - Cannot export, because no export plugin is loaded. - - - - - No database selected in the SQL editor. Cannot create a view for unknown database. - - - - - Editor window "%1" has uncommitted data. - - - - - ErrorsConfirmDialog - - - Errors - - - - - Following errors occured: - - - - - Would you like to proceed? - - - - - ExecFromFileDialog - - - Execute SQL from file - - - - - Input file - - - - - Path to file - - - - - Browse for file - - - - - Options - - - - - File encoding - - - - - Skip failing SQL statements - - - - - SQL scripts (*.sql);;All files (*) - - - - - Execute SQL file - - - - - Please provide file to be executed. - - - - - Provided file does not exist or cannot be read. - - - - - ExportDialog - - - Export - - - - - What do you want to export? - - - - - A database - - - - - A single table - - - - - Query results - - - - - Table to export - - - - - Database - - - - - Table - - - - - Options - - - - - When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. - - - - - Export table data - - - - - Export table indexes - - - - - Export table triggers - - - - - Note, that exporting table indexes and triggers may be unsupported by some output formats. - - - - - Select database objects to export - - - - - Export data from tables - - - - - Select all - - - - - Deselect all - - - - - - Database: - - - - - Query to export results for - - - - - Query to be executed for results: - - - - - Export format and options - - - - - Export format - - - - - Output - - - - - Exported file path - - - - - Clipboard - - - - - File - - - - - Exported text encoding: - - - - - Export format options - - - - - Cancel - - - - - - - Select database to export. - - - - - Select table to export. - - - - - Enter valid query to export. - - - - - Select at least one object to export. - - - - - You must provide a file name to export to. - - - - - Path you provided is an existing directory. You cannot overwrite it. - - - - - The directory '%1' does not exist. - - - - - The file '%1' exists and will be overwritten. - - - - - All files (*) - - - - - Pick file to export to - - - - - Internal error during export. This is a bug. Please report it. - - - - - FileExecErrorsDialog - - - Execution errors - - - - - Following errors were encountered during execution of SQL statements from the file: - - - - - SQL - - - - - Error - - - - - Statements that were executed successfully were commited. - - - - - Statements that were executed successfully were rolled back. - - - - - FontEdit - - - Choose font - font configuration - - - - - Form - - - Active SQL formatter plugin - - - - - FormView - - - Commit row - form view - - - - - Rollback row - form view - - - - - First row - form view - - - - - Previous row - form view - - - - - Next row - form view - - - - - Last row - form view - - - - - Insert new row - form view - - - - - Delete current row - form view - - - - - FunctionsEditor - - - Filter funtions - - - - - Function name: - - - - - Implementation language: - - - - - Type: - - - - - Input arguments - - - - - Undefined - - - - - Databases - - - - - Register in all databases - - - - - Register in following databases: - - - - - Initialization code: - - - - - - Function implementation code: - - - - - Final step implementation code: - - - - - SQL function editor - - - - - Commit all function changes - - - - - Rollback all function changes - - - - - Create new function - - - - - Delete selected function - - - - - Custom SQL functions manual - - - - - Add function argument - - - - - Rename function argument - - - - - Delete function argument - - - - - Move function argument up - - - - - Move function argument down - - - - - Scalar - - - - - Aggregate - - - - - Enter a non-empty, unique name of the function. - - - - - Pick the implementation language. - - - - - Per step code: - - - - - Enter a non-empty implementation code. - - - - - argument - new function argument name in function editor window - - - - - Functions editor window has uncommitted modifications. - - - - - ImportDialog - - - Import data - - - - - Table to import to - - - - - Table - - - - - Database - - - - - Data source to import from - - - - - Data source type - - - - - Options - - - - - Input file: - - - - - Text encoding: - - - - - <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> - - - - - Ignore errors - - - - - Data source options - - - - - Cancel - - - - - If you type table name that doesn't exist, it will be created. - - - - - Enter the table name - - - - - Select import plugin. - - - - - You must provide a file to import from. - - - - - The file '%1' does not exist. - - - - - Path you provided is a directory. A regular file is required. - - - - - Pick file to import from - - - - - IndexDialog - - - - Index - - - - - On table: - - - - - Index name: - - - - - Partial index condition - - - - - Unique index - - - - - Column - - - - - Collation - - - - - Sort - - - - - Delete selected indexed expression - - - - - Moves selected index column up in the order, making it more significant in the index. - - - - - Moves selected index column down in the order, making it less significant in the index. - - - - - Edit selected indexed expression - - - - - Add indexed expression - - - - - DDL - - - - - Tried to open index dialog for closed or inexisting database. - - - - - Could not process index %1 correctly. Unable to open an index dialog. - - - - - Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. - - - - - Pick the table for the index. - - - - - Select at least one column. - - - - - Enter a valid condition. - - - - - default - index dialog - - - - - Sort order - table constraints - - - - - - Error - index dialog - - - - - Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? - - - - - An error occurred while executing SQL statements: -%1 - - - - - IndexExprColumnDialog - - - Indexed expression - - - - - Expression to index - - - - - This expression is already indexed by the index. - - - - - Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. - - - - - Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. - - - - - It's forbidden to use 'SELECT' statements in indexed expressions. - - - - - Enter an indexed expression. - - - - - Invalid expression. - - - - - LanguageDialog - - - Language - - - - - Please choose language: - - - - - MainWindow - - - Database toolbar - - - - - Structure toolbar - - - - - Tools - - - - - Window list - - - - - View toolbar - - - - - Configuration widgets - - - - - Syntax highlighting engines - - - - - Data editors - - - - - Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - - - - - Running in debug mode. Debug messages are printed to the standard output. - - - - - You need to restart application to make the language change take effect. - - - - - Next window - - - - - Previous window - - - - - Hide status field - - - - - Open Debug Console - - - - - Open CSS Console - - - - - Bugs and feature &requests - - - - - Window list - menubar view menu - - - - - Open SQL &editor - - - - - Open DDL &history - - - - - Open SQL &functions editor - - - - - Open &collations editor - - - - - Open ex&tension manager - - - - - &Import - - - - - E&xport - - - - - Open confi&guration dialog - - - - - &Tile windows - - - - - Tile windows &horizontally - - - - - Tile windows &vertically - - - - - &Cascade windows - - - - - Close selected &window - - - - - Close all windows &but selected - - - - - Close &all windows - - - - - Re&store recently closed window - - - - - &Rename selected window - - - - - Report a &bug - - - - - Propose a new &feature - - - - - &About - - - - - &Licenses - - - - - Open home &page - - - - - Open fo&rum page - - - - - User &Manual - - - - - SQLite &documentation - - - - - Check for &updates - - - - - &Database - menubar - - - - - &Structure - menubar - - - - - &View - menubar - - - - - &Tools - menubar - - - - - &Help - - - - - Could not set style: %1 - main window - - - - - Cannot export, because no export plugin is loaded. - - - - - Cannot import, because no import plugin is loaded. - - - - - Rename window - - - - - Enter new name for the window: - - - - - New updates are available. <a href="%1">Click here for details</a>. - - - - - You're running the most recent version. No updates are available. - - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - - - - - Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 - - - - - Could not add database %1 to list. - - - - - MdiWindow - - - Uncommitted changes - - - - - Close anyway - - - - - Don't close - - - - - MultiEditor - - - Null value - multieditor - - - - - Configure editors for this data type - - - - - Open another tab - - - - - Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. - - - - - Deleted - multieditor - - - - - Read only - multieditor - - - - - MultiEditorBoolPlugin - - - Boolean - - - - - MultiEditorDatePlugin - - - Date - - - - - MultiEditorDateTimePlugin - - - Date & time - - - - - MultiEditorHexPlugin - - - Hex - - - - - MultiEditorNumericPlugin - - - Number - numeric multi editor tab name - - - - - MultiEditorText - - - Tab changes focus - - - - - Cut - - - - - Copy - - - - - Paste - - - - - Delete - - - - - Undo - - - - - Redo - - - - - MultiEditorTextPlugin - - - Text - - - - - MultiEditorTimePlugin - - - Time - - - - - NewConstraintDialog - - - New constraint - - - - - - Primary Key - new constraint dialog - - - - - - Foreign Key - new constraint dialog - - - - - - Unique - new constraint dialog - - - - - - Check - new constraint dialog - - - - - Not NULL - new constraint dialog - - - - - Collate - new constraint dialog - - - - - Default - new constraint dialog - - - - - NewVersionDialog - - - SQLiteStudio updates - - - - - New updates are available! - - - - - Component - - - - - This application will be closed and the update installer will start to download and install all the updates. - - - - - Update version - - - - - Check for updates on startup - - - - - Update to new version! - - - - - Not now. - - - - - Don't install the update and close this window. - - - - - PopulateConfigDialog - - - Populating configuration - - - - - Configuring <b>%1</b> for column <b>%2</b> - - - - - PopulateDialog - - - Populate table - - - - - Database - - - - - Table - - - - - Columns - - - - - Number of rows to populate: - - - - - Populate - populate dialog button - - - - - Abort - - - - - Configure - - - - - Populating configuration for this column is invalid or incomplete. - - - - - Select database with table to populate - - - - - Select table to populate - - - - - You have to select at least one column. - - - - - QObject - - - Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). - - - - - The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. - - - - - Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. - - - - - Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. - - - - - Cannot edit results of query other than %1. - - - - - Cannot edit columns that are result of aggregated %1 statements. - - - - - Cannot edit columns that are result of %1 statement. - - - - - Cannot edit columns that are result of common table expression statement (%1). - - - - - - - - on conflict: %1 - data view tooltip - - - - - references table %1, column %2 - data view tooltip - - - - - condition: %1 - data view tooltip - - - - - collation name: %1 - data view tooltip - - - - - Data grid view - - - - - Copy cell(s) contents to clipboard - - - - - Copy cell(s) contents together with header to clipboard - - - - - Paste cell(s) contents from clipboard - - - - - Set empty value to selected cell(s) - - - - - Set NULL value to selected cell(s) - - - - - Commit changes to cell(s) contents - - - - - Rollback changes to cell(s) contents - - - - - Delete selected data row - - - - - Insert new data row - - - - - Open contents of selected cell in a separate editor - - - - - Total pages available: %1 - - - - - Total rows loaded: %1 - - - - - Data view (both grid and form) - - - - - Refresh data - - - - - Switch to grid view of the data - - - - - Switch to form view of the data - - - - - Database list - - - - - Delete selected item - - - - - Clear filter contents - - - - - Refresh schema - - - - - Refresh all schemas - - - - - Add database - - - - - Select all items - - - - - Copy selected item(s) - - - - - - - Paste from clipboard - - - - - Tables - - - - - Indexes - - - - - Triggers - - - - - Views - - - - - Columns - - - - - Data form view - - - - - Commit changes for current row - - - - - Rollback changes for current row - - - - - Go to first row on current page - - - - - Go to next row - - - - - Go to previous row - - - - - Go to last row on current page - - - - - Insert new row - - - - - Delete current row - - - - - Main window - - - - - Open SQL editor - - - - - Previous window - - - - - Next window - - - - - Hide status area - - - - - Open configuration dialog - - - - - Open Debug Console - - - - - Open CSS Console - - - - - Cell text value editor - - - - - - Cut selected text - - - - - - Copy selected text - - - - - - Delete selected text - - - - - - Undo - - - - - - Redo - - - - - SQL editor input field - - - - - Select whole editor contents - - - - - Save contents into a file - - - - - Load contents from a file - - - - - Find in text - - - - - Find next - - - - - Find previous - - - - - Replace in text - - - - - Delete current line - - - - - Request code assistant - - - - - Format contents - - - - - Move selected block of text one line down - - - - - Move selected block of text one line up - - - - - Copy selected block of text and paste it a line below - - - - - Copy selected block of text and paste it a line above - - - - - Toggle comment - - - - - All SQLite databases - - - - - All files - - - - - - Database file - - - - - SQL editor window - - - - - Execute query - - - - - Execute "%1" query - - - - - Switch current working database to previous on the list - - - - - Switch current working database to next on the list - - - - - Go to next editor tab - - - - - Go to previous editor tab - - - - - Move keyboard input focus to the results view below - - - - - Move keyboard input focus to the SQL editor above - - - - - Delete selected SQL history entries - - - - - Table window - - - - - Refresh table structure - - - - - Add new column - - - - - Edit selected column - - - - - Delete selected column - - - - - Export table data - - - - - Import data to the table - - - - - Add new table constraint - - - - - Edit selected table constraint - - - - - Delete selected table constraint - - - - - Refresh table index list - - - - - Add new index - - - - - Edit selected index - - - - - Delete selected index - - - - - Refresh table trigger list - - - - - - Add new trigger - - - - - - Edit selected trigger - - - - - - Delete selected trigger - - - - - - Go to next tab - - - - - - Go to previous tab - - - - - A view window - - - - - Refresh view trigger list - - - - - QuitConfirmDialog - - - Uncommitted changes - - - - - Are you sure you want to quit the application? - -Following items are pending: - - - - - SearchTextDialog - - - Find or replace - - - - - Find: - - - - - Case sensitive - - - - - Search backwards - - - - - Regular expression matching - - - - - Replace && -find next - - - - - Replace with: - - - - - Replace all - - - - - Find - - - - - SortDialog - - - Sort by columns - - - - - - Column - - - - - - Order - - - - - Sort by: %1 - - - - - Move column up - - - - - Move column down - - - - - SqlEditor - - - Cut - sql editor - - - - - Copy - sql editor - - - - - Paste - sql editor - - - - - Delete - sql editor - - - - - Select all - sql editor - - - - - Undo - sql editor - - - - - Redo - sql editor - - - - - Complete - sql editor - - - - - Format SQL - sql editor - - - - - Save SQL to file - sql editor - - - - - Select file to save SQL - sql editor - - - - - Load SQL from file - sql editor - - - - - Delete line - sql editor - - - - - Move block down - sql editor - - - - - Move block up - sql editor - - - - - Copy block down - sql editor - - - - - Copy up down - sql editor - - - - - Find - sql editor - - - - - Find next - sql editor - - - - - Find previous - sql editor - - - - - Replace - sql editor - - - - - Toggle comment - sql editor - - - - - Saved SQL contents to file: %1 - - - - - Syntax completion can be used only when a valid database is set for the SQL editor. - - - - - Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. - - - - - Save to file - - - - - Could not open file '%1' for writing: %2 - - - - - SQL scripts (*.sql);;All files (*) - - - - - Open file - - - - - Could not open file '%1' for reading: %2 - - - - - Reached the end of document. Hit the find again to restart the search. - - - - - SqlQueryItem - - - Column: - data view tooltip - - - - - Data type: - data view - - - - - Table: - data view tooltip - - - - - Constraints: - data view tooltip - - - - - Cannot load the data for a cell that refers to the already closed database. - - - - - SqlQueryItemDelegate - - - The row is marked for deletion. - - - - - - - - - Cannot edit this cell. Details: %1 - - - - - - Structure of this table has changed since last data was loaded. Reload the data to proceed. - - - - - Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). - - - - - Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. - - - - - SqlQueryModel - - - - Only one query can be executed simultaneously. - - - - - Cannot commit the data for a cell that refers to the already closed database. - - - - - Could not begin transaction on the database. Details: %1 - - - - - An error occurred while rolling back the transaction: %1 - - - - - Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. - - - - - Uncommitted data - - - - - There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. - - - - - An error occurred while committing the transaction: %1 - - - - - An error occurred while committing the data: %1 - - - - - Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - - - - - - Error while executing SQL query on database '%1': %2 - - - - - Error while loading query results: %1 - - - - - Insert multiple rows - - - - - Number of rows to insert: - - - - - SqlQueryView - - - Go to referenced row in... - - - - - Copy - - - - - Copy as... - - - - - Paste - - - - - Paste as... - - - - - Set NULL values - - - - - Erase values - - - - - Edit value in editor - - - - - Commit - - - - - Copy with headers - - - - - Rollback - - - - - Commit selected cells - - - - - Rollback selected cells - - - - - Define columns to sort by - - - - - Remove custom sorting - - - - - Insert row - - - - - Insert multiple rows - - - - - Delete selected row - - - - - Show value in a viewer - - - - - Generate query for selected cells - - - - - No items selected to paste clipboard contents to. - - - - - Go to referenced row in table '%1' - - - - - table '%1' - - - - - Referenced row (%1) - - - - - Trim pasted text? - - - - - The pasted text contains leading or trailing white space. Trim it automatically? - - - - - Edit value - - - - - SqlTableModel - - - Error while committing new row: %1 - - - - - Error while deleting row from table %1: %2 - - - - - SqliteExtensionEditor - - - Filter extensions - - - - - Leave empty to use default function - - - - - Extension file - - - - - Initialization function - - - - - Databases - - - - - Register in all databases - - - - - Register in following databases: - - - - - Extension manager window has uncommitted modifications. - - - - - Extension manager - - - - - Commit all extension changes - - - - - Rollback all extension changes - - - - - Add new extension - - - - - Remove selected extension - - - - - Editing extensions manual - - - - - File with given path does not exist or is not readable. - - - - - Unable to load extension: %1 - - - - - Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - - - - - Dynamic link libraries (*.dll);;All files (*) - - - - - Shared objects (*.so);;All files (*) - - - - - Dynamic libraries (*.dylib);;All files (*) - - - - - All files (*) - - - - - Open file - - - - - StatusField - - - Status - - - - - Copy - - - - - Clear - - - - - TableConstraintsModel - - - Type - table constraints - - - - - Details - table constraints - - - - - Name - table constraints - - - - - TableForeignKeyPanel - - - Foreign table: - - - - - SQLite 2 does not support foreign keys officially, -but it's okay to use them anyway. - - - - - Columns - - - - - Local column - - - - - Foreign column - - - - - Reactions - - - - - Deferred foreign key - - - - - Named constraint - - - - - Constraint name - - - - - Pick the foreign column. - - - - - Pick the foreign table. - - - - - Select at least one foreign column. - - - - - Enter a name of the constraint. - - - - - Foreign column - table constraints - - - - - TablePrimaryKeyAndUniquePanel - - - Columns - - - - - Column - - - - - Collation - - - - - Sort - - - - - Valid only for a single column with INTEGER data type - - - - - Autoincrement - - - - - Named constraint - - - - - Constraint name - - - - - On conflict - - - - - Collate - table constraints - - - - - Sort order - table constraints - - - - - Select at least one column. - - - - - Enter a name of the constraint. - - - - - TableStructureModel - - - Name - table structure columns - - - - - Data type - table structure columns - - - - - Primary -Key - table structure columns - - - - - Foreign -Key - table structure columns - - - - - Unique - table structure columns - - - - - Check - table structure columns - - - - - Not -NULL - table structure columns - - - - - Collate - table structure columns - - - - - Default value - table structure columns - - - - - TableWindow - - - Structure - - - - - Table name: - - - - - - Data - - - - - Constraints - - - - - Indexes - - - - - Triggers - - - - - DDL - - - - - Export table - table window - - - - - Import data to table - table window - - - - - Populate table - table window - - - - - Refresh structure - table window - - - - - Commit structure changes - table window - - - - - Rollback structure changes - table window - - - - - Add column - table window - - - - - Edit column - table window - - - - - - Delete column - table window - - - - - Move column up - table window - - - - - Move column down - table window - - - - - Create similar table - table window - - - - - Reset autoincrement value - table window - - - - - Add table constraint - table window - - - - - Edit table constraint - table window - - - - - Delete table constraint - table window - - - - - Move table constraint up - table window - - - - - Move table constraint down - table window - - - - - Add table primary key - table window - - - - - Add table foreign key - table window - - - - - Add table unique constraint - table window - - - - - Add table check constraint - table window - - - - - Refresh index list - table window - - - - - Create index - table window - - - - - Edit index - table window - - - - - Delete index - table window - - - - - Refresh trigger list - table window - - - - - Create trigger - table window - - - - - Edit trigger - table window - - - - - Delete trigger - table window - - - - - Are you sure you want to delete column '%1'? - table window - - - - - Following problems will take place while modifying the table. -Would you like to proceed? - table window - - - - - Table modification - table window - - - - - Could not load data for table %1. Error details: %2 - - - - - Could not process the %1 table correctly. Unable to open a table window. - - - - - Could not restore window %1, because no database or table was stored in session for this window. - - - - - Could not restore window '%1', because no database or table was stored in session for this window. - - - - - Could not restore window '%1', because database %2 could not be resolved. - - - - - Could not restore window '%1'', because the table %2 doesn't exist in the database %3. - - - - - - New table %1 - - - - - Committed changes for table '%1' successfully. - - - - - Committed changes for table '%1' (named before '%2') successfully. - - - - - Autoincrement value for table '%1' has been reset successfully. - - - - - Uncommitted changes - - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - - - - - Table window "%1" has uncommitted structure modifications and data. - - - - - Table window "%1" has uncommitted data. - - - - - Table window "%1" has uncommitted structure modifications. - - - - - Could not commit table structure. Error message: %1 - table window - - - - - Reset autoincrement - - - - - Are you sure you want to reset autoincrement value for table '%1'? - - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - - - - - Empty name - - - - - A blank name for the table is allowed in SQLite, but it is not recommended. -Are you sure you want to create a table with blank name? - - - - - Cannot create a table without at least one column. - - - - - Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. - - - - - Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. - - - - - Are you sure you want to delete table constraint '%1'? - table window - - - - - Delete constraint - table window - - - - - Cannot export, because no export plugin is loaded. - - - - - Cannot import, because no import plugin is loaded. - - - - - Go back to structure tab - - - - - Commit modifications and browse data. - - - - - Name - table window indexes - - - - - Unique - table window indexes - - - - - Columns - table window indexes - - - - - Partial index condition - table window indexes - - - - - Name - table window triggers - - - - - Event - table window triggers - - - - - Condition - table window triggers - - - - - Details - table window triggers - - - - - TriggerColumnsDialog - - - Trigger columns - - - - - Triggering columns: - - - - - Select all - - - - - Deselect all - - - - - TriggerDialog - - - - Trigger - - - - - On table: - - - - - Action: - - - - - - <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> - - - - - Pre-condition: - - - - - The scope is still not fully supported by the SQLite database. - - - - - Trigger name: - - - - - When: - - - - - List of columns for UPDATE OF action. - - - - - Scope: - - - - - Code: - - - - - Trigger statements to be executed. - - - - - DDL - - - - - On view: - - - - - Could not process trigger %1 correctly. Unable to open a trigger dialog. - - - - - Enter a valid condition. - - - - - Enter a valid trigger code. - - - - - Error - trigger dialog - - - - - An error occurred while executing SQL statements: -%1 - - - - - VersionConvertSummaryDialog - - - Database version convert - - - - - Following changes to the SQL statements will be made: - - - - - Before - - - - - After - - - - - ViewWindow - - - Query - - - - - View name: - - - - - Output column names - - - - - - Data - - - - - Triggers - - - - - DDL - - - - - - Could not restore window '%1', because no database or view was stored in session for this window. - - - - - Could not restore window '%1', because database %2 could not be resolved. - - - - - Could not restore window '%1', because database %2 could not be open. - - - - - Could not restore window '%1', because the view %2 doesn't exist in the database %3. - - - - - - New view %1 - - - - - Refresh the view - view window - - - - - Commit the view changes - view window - - - - - Rollback the view changes - view window - - - - - Explicit column names - - - - - Generate output column names automatically basing on result columns of the view. - - - - - Add column - view window - - - - - Edit column - view window - - - - - Delete column - view window - - - - - Move column up - view window - - - - - Move column down - view window - - - - - Refresh trigger list - view window - - - - - Create new trigger - view window - - - - - Edit selected trigger - view window - - - - - Delete selected trigger - view window - - - - - View window "%1" has uncommitted structure modifications and data. - - - - - View window "%1" has uncommitted data. - - - - - View window "%1" has uncommitted structure modifications. - - - - - Uncommitted changes - - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - - - - - Committed changes for view '%1' successfully. - - - - - Committed changes for view '%1' (named before '%2') successfully. - - - - - Could not load data for view %1. Error details: %2 - - - - - Go back to structure tab - - - - - Commit modifications and browse data. - - - - - Could not commit view changes. Error message: %1 - view window - - - - - Override columns - - - - - Currently defined columns will be overriden. Do you want to continue? - - - - - Could not determinate columns returned from the view. The query is problably incomplete or contains errors. - - - - - Name - view window triggers - - - - - Instead of - view window triggers - - - - - Condition - view window triggers - - - - - Details - table window triggers - - - - - Could not process the %1 view correctly. Unable to open a view window. - - - - - Empty name - - - - - A blank name for the view is allowed in SQLite, but it is not recommended. -Are you sure you want to create a view with blank name? - - - - - The SELECT statement could not be parsed. Please correct the query and retry. -Details: %1 - - - - - The view could not be modified due to internal SQLiteStudio error. Please report this! - - - - - The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. - - - - - Following problems will take place while modifying the view. -Would you like to proceed? - view window - - - - - View modification - view window - - - - - WidgetCover - - - Interrupt - - - - diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es_ES.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es_ES.ts new file mode 100644 index 0000000..8e80956 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_es_ES.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + Acerca de SQLiteStudio y las licencias + + + + About + Acerca de + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Gestor de bases de datos SQLite gratuito, open-source y multi-plataforma.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Autor y mantenedor activo:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licencias + + + + Environment + Entorno + + + + Icon directories + Directorios de los iconos + + + + Form directories + Directorios de los formularios + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Directorios de los plugins + + + + Configuration directory + Directorio de configuración + + + + Application directory + Directorio de la aplicación + + + + Qt version: + Versión de Qt: + + + + SQLite 3 version: + Versión de SQLite 3: + + + + Portable distribution. + Distribución portable. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + El sistema operativo gestionó la distribución. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Tabla de contenidos:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Parámetros de la consulta + + + + Please provide values for query parameters + Por favor, proporciona los valores de los parámetros de la consulta + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filtrar colaciones + + + + Databases + Bases de datos + + + + Register in all databases + Registrar en todas las bases de datos + + + + Register in following databases: + Registrar en las siguientes bases de datos: + + + + Implementation code: + Código de implementación: + + + + Collation name: + Nombre de la colación: + + + + Implementation language: + Lenguaje de implementación: + + + + Collations editor + Editor de colaciones + + + + Commit all collation changes + Confirmar todos los cambios en las colaciones + + + + Rollback all collation changes + Revertir todos los cambios en las colaciones + + + + Create new collation + Crear nueva colación + + + + Delete selected collation + Borrar colación seleccionada + + + + Editing collations manual + Manual para editar colaciones + + + + Enter a non-empty, unique name of the collation. + Ingresa un nombre único y que no esté vacío para la colación. + + + + Pick the implementation language. + Elige el idioma de implementación. + + + + Enter a non-empty implementation code. + Ingresa código de implementación que no esté vacío. + + + + Collations editor window has uncommitted modifications. + La ventana del editor de colaciones tiene cambios sin confirmar. + + + + ColorButton + + + Pick a color + Elige un color + + + + ColumnCollatePanel + + + Collation name: + Nombre de la colación: + + + + Named constraint: + Nombre de la restricción: + + + + Enter a name of the constraint. + Ingresa el nombre de la restricción. + + + + Enter a collation name. + Ingresa un nombre para la colación. + + + + ColumnDefaultPanel + + + Default value: + Valor predeterminado: + + + + Named constraint: + Nombre de la restricción: + + + + Enter a default value expression. + Ingresa una expresión de valor predeterminada. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Valor de expresión predeterminado inválido: %1. Si quieres usar una cadena simple como valor, recuerda encerrarla con caracteres de comillas. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Valor de expresión predeterminada inválida. Si quieres usar cadenas simples como valores, recuerda encerrarlas con los caracteres de comillas. + + + + Enter a name of the constraint. + Ingresa el nombre de la restricción. + + + + ColumnDialog + + + Column + Columna + + + + Name and type + Nombre y tipo + + + + Scale + Escala + + + + Precision + Precisión + + + + Data type: + Tipo de dato: + + + + Column name: + Nombre de columna: + + + + Size: + Tamaño: + + + + Constraints + Restricciones + + + + Generated value + Valor generado + + + + Unique + Único + + + + + + + + + + + Configure + Configurar + + + + Foreign Key + Clave Foránea + + + + Collate + Cotejar + + + + Not NULL + No NULO + + + + Check condition + Verificar condición + + + + Primary Key + Clave Primaria + + + + Default + Predeterminado + + + + Advanced mode + Modo avanzado + + + + Add constraint + column dialog + Añadir restricción + + + + Edit constraint + column dialog + Editar restricción + + + + + Delete constraint + column dialog + Borrar restricción + + + + Move constraint up + column dialog + Mover esta restricción arriba + + + + Move constraint down + column dialog + Mover esta restricción abajo + + + + Add a primary key + column dialog + Añadir una clave primaria + + + + Add a foreign key + column dialog + Añadir una clave foránea + + + + Add an unique constraint + column dialog + Añadir una restricción única + + + + Add a check constraint + column dialog + Añadir una restricción de verificación + + + + Add a not null constraint + column dialog + Añadir una restricción no nula + + + + Add a collate constraint + column dialog + Añadir una restricción de verificación + + + + Add a generated value constraint + column dialog + Añadir una restricción de valor generado + + + + Add a default constraint + column dialog + Añadir una restricción predeterminada + + + + Are you sure you want to delete constraint '%1'? + column dialog + ¿Estás seguro de querer borrar la restricción '%1'? + + + + Correct the constraint's configuration. + Corregir la configuración de la restricción. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + No se permite usar escala para las columnas ENTERAS DE CLAVE PRIMARIA. + + + + Precision cannot be defined without the scale. + No se puede definir la precisión sin la escala. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + No se puede usar otro tipo que no sea INTEGER si el AUTOINCREMENTO está activo en la CLAVE PRIMARIA. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + Se forzó el uso del tipo ENTERO debido a que la CLAVE PRIMARIA tiene activo el AUTOINCREMENTO. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Las columnas INTEGER PRIMARY KEY no permiten precisión. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Tipo + + + + Name + column dialog constraints + Nombre + + + + Details + column dialog constraints + Detalles + + + + ColumnForeignKeyPanel + + + Foreign table: + Tabla foránea: + + + + Foreign column: + Columna foránea: + + + + Reactions + Reacciones + + + + Deferred foreign key + Clave foránea diferida + + + + Named constraint + Nombre de la restricción + + + + Constraint name + Nombre de la restricción + + + + Pick the foreign table. + Elige la tabla externa. + + + + Pick the foreign column. + Elige la columna externa. + + + + Enter a name of the constraint. + Ingresa el nombre de la restricción. + + + + ColumnGeneratedPanel + + + Generating code: + Código generador: + + + + Explicit type: + Tipo explícito: + + + + Use "GENERATED ALWAYS" keywords + Usar palabras clave "GENERATED ALWAYS" + + + + Named constraint: + Nombre de la restricción: + + + + Enter the column value generating expression. + Ingresa la expresión para generar el valor de la columna. + + + + Invalid value generating expression: %1. + Valor de generador de expresión no válido: %1. + + + + Invalid value generating expression. + Valor de generador de expresión no válido. + + + + Enter a name of the constraint. + Ingresa el nombre de la restricción. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrementar + + + + Sort order: + Ordenar por: + + + + Named constraint: + Nombre de la restricción: + + + + On conflict: + En un conflicto: + + + + Enter a name of the constraint. + Ingresa el nombre de la restricción. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Nombre de la restricción: + + + + On conflict: + En un conflicto: + + + + Enter a name of the constraint. + Ingresa el nombre de la restricción. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Columna: %1 + + + + Table: %1 + completer statusbar + Tabla: %1 + + + + Index: %1 + completer statusbar + Ãndice: %1 + + + + Trigger: %1 + completer statusbar + Disparador: %1 + + + + View: %1 + completer statusbar + Vista: %1 + + + + Database: %1 + completer statusbar + Base de datos: %1 + + + + Keyword: %1 + completer statusbar + Palabra clave: %1 + + + + Function: %1 + completer statusbar + Función: %1 + + + + Operator: %1 + completer statusbar + Operador: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Número + + + + Binary data + completer statusbar + Datos binarios + + + + Collation: %1 + completer statusbar + Colación: %1 + + + + Pragma function: %1 + completer statusbar + Función pragma: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuración + + + + Search + Buscar + + + + General + General + + + + Keyboard shortcuts + Atajos de teclado + + + + Look & feel + Aspecto visual + + + + Style + Estilo + + + + Fonts + Fuentes + + + + Code colors + Code colors + + + + + Database list + Lista de bases de datos + + + + Code assistant + Code assistant + + + + Data browsing + Explorar datos + + + + Data editors + Editores de datos + + + + Plugins + Plugins + + + + Code formatters + Formateadores de código + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + Si se desactiva, las columnas se ordenarán según el orden en que se escriben en la declaración CREATE TABLE. + + + + Sort table columns alphabetically + Ordenar columnas de la tabla alfabéticamente + + + + Expand tables node when connected to a database + Expandir el nodo de tablas al conectarse a una base de datos + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Las etiquetas extras son las que se muestran al lado de los nombres en la lista de bases de datos (son azules, a menos que se configure otro color). Activar esta opción mostrará etiquetas para las bases de datos, bases de datos inválidas y nodos agregados (grupo de columnas, grupo de índices, grupo de disparadores). Para más etiquetas ve las opciones debajo.<p> + + + + Display additional labels on the list + Mostrar etiquetas adicionales en la lista + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + Para las tablas comunes, las etiquetas mostrarán el número de columnas, índices y disparadores para cada tabla. + + + + Display labels for regular tables + Mostrar etiquetas para tablas estándares + + + + Virtual tables will be marked with a 'virtual' label. + Las tablas virtuales se marcarán con la etiqueta 'virtual'. + + + + Display labels for virtual tables + Mostrar etiquetas para tablas virtuales + + + + Expand views node when connected to a database + Expandir la vista de nodos cuando se conecte a una base de datos + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + Si se desactiva esta opción, los objetos se ordenarán en el orden que aparezcan en la tabla sqlite_master (ordenados según se fueron creando) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Ordenar objetos (tablas, índices, disparadores y vistas) alfabéticamente + + + + Display system tables and indexes on the list + Mostrar tablas e índices del sistema en la lista + + + + Database dialog window + Ventana de diálogo de la base de datos + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>Al agregar una nueva base de datos, esta se añade por defecto como "permanente" (almacenado en la configuración). Marcar esta opción hace que cada nueva base de datos ya NO se añada como "permanente" por defecto.</p> + + + + Do not mark database to be "permanent" by default + No marcar las bases de datos como "permanentes" por defecto + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>Cuando esta opción está activada, los archivos agregados desde el gestor de archivos a la lista de bases de datos se añadirán automáticamente a la lista, saltándose el diálogo de base de datos estándar. Si por varios motivos el proceso automático falla, entonces se presentará el diálogo estándar al usuario.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Intentar saltarse completamente el diálogo al soltar un archivo de base de datos a la lista + + + + Data browsing and editing + Exploración y edición de datos + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Número máximo de configuraciones del diálogo de Llenar Tabla almacenados en la configuración. El valor de 100 debería bastar.</p> + + + + Number of memorized table populating configurations + Número de configuraciones de llenado de tabla memorizados + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>Cuando se cargan los datos en la vista de grilla, se ajusta el ancho de las columnas automáticamente. Este valor limita el ancho inicial para el ajuste, pero el usuario puede redimensionar manualmente la columna por encima de este límite.</p> + + + + Number of data rows per page: + Número de filas de datos por página: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>Cuando esto está activado y el usuario deja el puntero del mouse encima de una celda en cualquier vista de datos (resultados de la consulta, una tabla de datos, una vista de datos) un globo de texto aparecerá con detalles sobre la celda - incluye detalles como el tipo de datos de la columna, restricciones, ROWID y otros.</p> + + + + Show column and row details tooltip in data view + Mostrar globo de texto con detalles para filas y columnas en la vista de datos + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>Al editar una celda que solía contener un valor NULL, ingresando una string vacía como valor nuevo, con esta opción se determina si el nuevo valor debería quedarse como NULL (si esta opción está activada), o debería reemplazarse con un valor de string vacío (si esta opción está desactivada).</p> + + + + Keep NULL value when entering empty value + Mantener valor NULL al ingresar un valor vacío + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Activar esto para forzar siempre el uso del valor DEFAULT al confirmar un valor NULL para una columna con el valor DEFAULT definido, aún así que la columna esté permitida de almacenar valores NULL.</p><p>Desactiva esta opción para usar el valor DEFAULT exclusivamente cuando un valor NULL se confirme a una columna con la restricción NOT NULL.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Usar valor DEFAULT (si se ha definido) al confirmar un valor NULL + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Insertar una nueva fila en la cuadrícula de datos + + + + Before currently selected row + Antes de la fila actualmente seleccionada + + + + After currently selected row + Después de la fila actualmente seleccionada + + + + At the end of data view + Al final de la vista de datos + + + + Table windows + Ventanas de tabla + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>Si está activado, Ventanas de Tabla se mostrará con la pestaña datos, en vez de la pestaña estructura.</p> + + + + Open Table Windows with the data tab for start + Abrir Ventanas de Tabla con la pestaña de datos para iniciar + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>Cuando esté activado, la pestaña "Datos" se posicionará como la primera pestaña en cada Ventana Tabla, en vez de estar en el segundo lugar.</p> + + + + Place data tab as first tab in a Table Window + Ubicar la pestaña de datos como la primera pestaña en una Ventana Tabla + + + + View windows + Ver ventanas + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>Cuando está activado, Ver Ventanas se mostrará en la pestaña de Datos, en vez de la de Estructura.</p> + + + + Open View Windows with the data tab for start + Abrir Ver Ventanas con la pestaña de datos al iniciar + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>Cuando esté activado, la pestaña "Datos" se posicionará como la primera pestaña en cada Ventana Tabla, en vez de estar en el segundo lugar.</p> + + + + Place data tab as first tab in a View Window + Ubicar la pestaña de datos como la primera pestaña en una Ventana Ver + + + + Data types + Tipos de datos + + + + Available editors: + Editores disponibles: + + + + Editors selected for this data type: + Editores seleccionados para este tipo de dato: + + + + Schema editing + Edición de esquema + + + + Number of DDL changes kept in history. + Número de cambios de DDL a mantener en el historial. + + + + DDL history size: + Tamaño del historial DDL: + + + + Don't show DDL preview dialog when committing schema changes + No mostrar el diálogo de vista previa del DDL al confirmar cambios del esquema + + + + SQL queries + Consultas SQL + + + + + Number of queries kept in the history. + Número de consultas a mantener en el historial. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + Tamaño del historial: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Número máximo de parámetros en la consulta (:param, @param, $param, ?) almacenados en el historial. Cuando reusas un parámetro con el mismo nombre/posición, SQLiteStudio lo pre-inicializará con el valor más reciente almacenado en memoria (aún podrás cambiarlo). Un valor de 1000 debería bastar.</p> + + + + Execute only the query under the cursor + Ejecutar sólo la consulta bajo el cursor + + + + Number of memorized query parameters + Número de parámetros de consulta en memoria + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Actualizaciones + + + + Automatically check for updates at startup + Comprobar actualizaciones automáticamente al inicio + + + + Session + Sesión + + + + Restore last session (active MDI windows) after startup + Restaurar la última sesión (ventana MDI activa) luego de iniciar + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Campo de Estado + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>Cuando el usuario cierra manualmente el panel de Estado, esta opción se asegura que cualquier nuevo mensaje enviado al panel de Estado se muestre, mostrando de nuevo ese panel. Si se desactiva, el usuario tiene que abrir manualmente el panel de Estado desde el menú "Vista".</p> + + + + Always open Status panel when new message is printed + Abrir siempre el panel de Estado cuando un nuevo mensaje se muestre + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filtrar atajos por nombre o combinación de teclas + + + + Action + Acción + + + + Key combination + Combinación de teclas + + + + + Language + Idioma + + + + Changing language requires application restart to take effect. + Cambiar el idioma requiere reiniciar la aplicación para que los cambios surtan efecto. + + + + Compact layout + Diseño compacto + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compactar diseño reduce todos los márgenes y espaciados de la IU al mínimo, haciendo espacio para mostrar más datos. Le quita algo de belleza a la interfaz, pero permite mostrar más datos.</p> + + + + Use compact layout + Usar diseño compacto + + + + Main window dock areas + Ãreas de acoplamiento de la ventana principal + + + + Left and right areas occupy corners + Las áreas izquierda y derecha ocupan las esquinas + + + + Top and bottom areas occupy corners + Las áreas superior e inferior ocupan las esquinas + + + + Hide built-in plugins + Ocultar plugins integrados + + + + Current style: + Estilo actual: + + + + Preview + Vista previa + + + + Enabled + Activado + + + + Disabled + Desactivado + + + + Active formatter plugin + Plugin de formato activo + + + + SQL editor font + Fuente del editor SQL + + + + Database list font + Fuente de la lista de bases de datos + + + + Database list additional label font + Fuente para la etiqueta adicional de lista de bases de datos + + + + Data view font + Fuente del visor de datos + + + + Status field font + Fuente del campo de Estado + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Descripción: + + + + Category: + plugin details + Categoría: + + + + Version: + plugin details + Versión: + + + + Author: + plugin details + Autor: + + + + Internal name: + plugin details + Nombre interno: + + + + Dependencies: + plugin details + Dependencias: + + + + Conflicts: + plugin details + Conflictos: + + + + Plugin details + Detalles del plugin + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Los plugins son cargados/liberados inmediatamente el mismo momento que los seleccionas/deseleccionas, pero la lista modificada de plugins a cargar al iniciar el programa no se guarda hasta que Aceptes los cambios de este diálogo de configuración. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (integrado) + + + + Details + Detalles + + + + No plugins in this category. + No hay plugins en esta categoría. + + + + Add new data type + Añadir un nuevo tipo de dato + + + + Rename selected data type + Renombrar el tipo de dato seleccionado + + + + Delete selected data type + Borrar el tipo de dato seleccionado + + + + Help for configuring data type editors + Ayuda para configurar los editores de tipo de dato + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + La condición + + + + Named constraint: + Nombre de la restricción: + + + + On conflict + En un conflicto + + + + Enter a valid condition. + Ingresa una condición válida. + + + + Enter a name of the constraint. + Ingresa el nombre de la restricción. + + + + ConstraintDialog + + + New constraint + constraint dialog + Nueva restricción + + + + Create + constraint dialog + Crear + + + + Edit constraint + dialog window + Editar restricción + + + + Apply + constraint dialog + Aplicar + + + + Primary key + table constraints + Clave primaria + + + + Foreign key + table constraints + Clave foránea + + + + Unique + table constraints + Único + + + + Not NULL + table constraints + No es NULL + + + + Check + table constraints + Comprobar + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Predeterminado + + + + ConstraintTabModel + + + Table + table constraints + Tabla + + + + Column (%1) + table constraints + Columna (%1) + + + + Scope + table constraints + Alcance + + + + Type + table constraints + Tipo + + + + Details + table constraints + Detalles + + + + Name + table constraints + Nombre + + + + CssDebugDialog + + + SQLiteStudio CSS console + Consola CSS de SQLIteStudio + + + + DataView + + + Filter data + data view + Filtrar datos + + + + Grid view + Vista de rejilla + + + + Form view + Vista de formulario + + + + Refresh table data + data view + Actualizar los datos de la tabla + + + + First page + data view + Primera página + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Tipo de base de datos + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copiar + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Añadir una base de datos + + + + &Edit the database + &Editar la base de datos + + + + &Remove the database + &Quitar la base de datos + + + + &Connect to the database + &Conectar a la base de datos + + + + &Disconnect from the database + &Desconectar de la base de datos + + + + Import + Import + + + + &Export the database + &Exportar la base de datos + + + + Vac&uum + Ejecutar Vac&uum + + + + &Integrity check + Comprobación de &integridad + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Poblar tabla + + + + Create similar table + Crear tabla similar + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + A&ctualizar todos los esquemas de bases de datos + + + + Re&fresh selected database schema + Ac&tualizar el esquema de base de datos seleccionado + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Comprobación de integridad (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copiar + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filtrar por base de datos: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Consultas ejecutadas en la base de datos %1 (%2) +-- Fecha y hora de la ejecución: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + Historial DDL + + + + DdlPreviewDialog + + + Queries to be executed + Consultas a ejecutar + + + + Don't show again + No mostrar de nuevo + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + Historial + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Limpiar historial de ejecución + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Borrar entradas SQL seleccionadas del historial + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Consulta finalizada en %1 segundo(s). Filas afectadas: %2 + + + + Query finished in %1 second(s). + Consulta finalizada en %1 segundo(s). + + + + Clear execution history + Limpiar historial de ejecución + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + ¿Estás seguro de que quieres borrar todo el historial de ejecución SQL? Esto no se puede deshacer. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Tabla + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancelar + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancelar + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Ingresa una condición válida. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Abrir &historial DDL + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Salir + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Base de datos + + + + &Structure + menubar + &Estructura + + + + &View + menubar + &Ver + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Herramientas + + + + &Help + &Ayuda + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Cambios sin confirmar + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Clave Foránea + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copiar + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Clave Primaria + + + + + Foreign Key + new constraint dialog + Clave Foránea + + + + + Unique + new constraint dialog + Único + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + No es NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Predeterminado + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Poblar tabla + + + + Database + Database + + + + Table + Tabla + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Seleccionar base de datos con la tabla a poblar + + + + Select table to populate + Selecciona la tabla a poblar + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copiar contenido de la(s) celda(s) al portapapeles + + + + Copy cell(s) contents together with header to clipboard + Copiar contenido de la(s) celda(s) junto con la cabecera al portapapeles + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copiar elemento(s) seleccionado(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Salir de la aplicación + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copiar texto seleccionado + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copiar bloque de texto seleccionado y pegarlo una línea debajo + + + + Copy selected block of text and paste it a line above + Copiar bloque de texto seleccionado y pegarlo una línea arriba + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Borrar entradas seleccionadas del historial SQL + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Cambios sin confirmar + + + + Are you sure you want to quit the application? + +Following items are pending: + ¿Estás seguro de querer salir de la aplicación? + +Estos elementos están pendientes: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Subir columna + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copiar + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copiar bloque abajo + + + + Copy up down + sql editor + Copiar arriba abajo + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Tipo de dato: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Restricciones: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + Hay cambios de datos no confirmados. ¿Quieres continuar aún así? Todos los cambios sin confirmar se perderán. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copiar + + + + Copy with headers + Copiar con las cabeceras + + + + Copy as... + Copiar como... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copiar + + + + Clear + Limpiar + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Elige la columna externa. + + + + Pick the foreign table. + Elige la tabla externa. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Escribe el nombre para la restricción. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Ingresa el nombre de la restricción. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Clave +Primaria + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Único + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + No es +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Restricciones + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Exportar tabla + + + + Import data to table + table window + Importar datos a la tabla + + + + Populate table + table window + Poblar tabla + + + + Refresh structure + table window + Actualizar estructura + + + + Commit structure changes + table window + Confirmar cambios de estructura + + + + Rollback structure changes + table window + Revertir cambios de estructura + + + + Add column + table window + Añadir columna + + + + Edit column + table window + Editar columna + + + + + Delete column + table window + Eliminar columna + + + + Move column up + table window + Subir columna + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Crear tabla similar + + + + Reset autoincrement value + table window + Reiniciar valor del autoincremento + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Crear índice + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Crear disparador + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + ¿Estás seguro que quieres eliminar la columna '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Se confirmaron los cambios a la tabla '%1' con éxito. + + + + Committed changes for table '%1' (named before '%2') successfully. + Se confirmaron los cambios a la tabla '%1' (anteriormente llamada '%2') con éxito. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + SQLite permite usar tablas sin nombre, pero no es lo recomendado. +¿Estás seguro de que quieres crear una tabla sin nombre? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Cambios sin confirmar + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + La ventana de la tabla "%1" tiene cambios sin confirmar en su estructura y datos. + + + + Table window "%1" has uncommitted data. + La ventana de la tabla "%1" tiene datos sin confirmar. + + + + Table window "%1" has uncommitted structure modifications. + La ventana de la tabla "%1" tiene cambios de estructura sin confirmar. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Añadir columna + + + + Edit column + view window + Editar columna + + + + Delete column + view window + Eliminar columna + + + + Move column up + view window + Subir columna + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Crear nuevo disparador + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Cambios sin confirmar + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Se confirmaron los cambios a la vista '%1' con éxito. + + + + Committed changes for view '%1' (named before '%2') successfully. + Se confirmaron los cambios a la vista '%1' (anteriormente llamada '%2') con éxito. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrumpir + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fa_IR.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fa_IR.ts new file mode 100644 index 0000000..a797ccf --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fa_IR.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fi_FI.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fi_FI.ts new file mode 100644 index 0000000..9df74b6 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fi_FI.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr.qm b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr.qm deleted file mode 100644 index 0f02e29..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr.qm and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr.ts deleted file mode 100644 index aec2cac..0000000 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr.ts +++ /dev/null @@ -1,7133 +0,0 @@ - - - - - AboutDialog - - - About SQLiteStudio and licenses - À propos de SQLiteStudio et des licences - - - - About - À propos de… - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Libre, open-source, cross-platform SQLite database manager.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Auteur et maintenance:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - - Licenses - Licences - - - - Environment - Environnement - - - - Icon directories - Répertoires des images - - - - Form directories - Répertoires des formulaires - - - - Plugin directories - Répertoires des plugins - - - - Application directory - Répertoire de l’application - - - - SQLite 3 version: - Version de SQLite 3 : - - - SQLite 3 version : - Version SQLite : - - - - Configuration directory - Répertoire de configuration - - - - Qt version: - Version Qt : - - - - Portable distribution. - Version portable. - - - - MacOS X application boundle distribution. - MacOS X application boundle distribution - - - - Operating system managed distribution. - Distribution gérée par le système d'exploitation. - - - - Copy - Copie - - - - <h3>Table of contents:</h3><ol>%2</ol> - <h3>Table des matières : </h3><ol>%2</ol> - - - - BindParamsDialog - - - Query parameters - - - - - Please provide values for query parameters - - - - - BugDialog - - Bugs and ideas - Bugs et idées - - - Reporter - Rapport - - - E-mail address - Adresse électronique - - - Log in - S’identifier - - - Short description - Description brève - - - Detailed description - Description détaillée - - - Show more details - Montrer plus de détails - - - SQLiteStudio version - Version de SQLiteStudio - - - Operating system - Système d’exploitation - - - Loaded plugins - Plugins chargés - - - Send - Envoyer - - - You can see all your reported bugs and ideas by selecting menu '%1' and then '%2'. - Vous pouvez voir tous bugs et idées que vous avez rapportées en sélectionnant le menu « %1 » puis « %2 ». - - - A bug report sent successfully. - Rapport de bogue envoyé avec succès - - - An error occurred while sending a bug report: %1 -%2 - Une erreur est survenue lors de l’envoi du rapport de bogue : %1 -%2 - - - You can retry sending. The contents will be restored when you open a report dialog after an error like this. - Vous pouvez essayer à nouveau. Le contenu sera restauré lorsque vous ouvrirez le dialogue du rapport après une telle erreur. - - - An idea proposal sent successfully. - L’idée proposée à été envoyée avec succès. - - - An error occurred while sending an idea proposal: %1 -%2 - Une erreeur est survenu lors de l’envoi de l’idée proposée : %1 %2 - - - A bug report - Rapport de bug - - - Describe problem in few words - Décrivez le problème en queques mots - - - Describe problem and how to reproduce it - Décrivez le problème et comment le reproduire - - - A new feature idea - Une nouvelle idée de fonctionalité - - - A title for your idea - Un titre pour votre idée - - - Describe your idea in more details - Décrivez votre idée avec plus de détails - - - Reporting as an unregistered user, using e-mail address. - Envoyer le rapport comme nouvel utilisateur, avec une adresse mail. - - - Reporting as a registered user. - Envoyer le rapport comme utilisateur enregistré. - - - Log out - Déconnexion - - - Providing true email address will make it possible to contact you regarding your report. To learn more, press 'help' button on the right side. - En fournissant un mail existant il sera possible de vous contacter au sujet du rapport. Pour en savoir plus, clic sur le bouton « help » sur le coté droit. - - - Enter vaild e-mail address, or log in. - Entrez un email valide ou connectez-vous. - - - Short description requires at least 10 characters, but not more than 100. Longer description can be entered in the field below. - Une description courte nécessite au moins 10 caractères, mais pas plus de 100. La longueur de la description ne peut être contenue dans ce champ. - - - Long description requires at least 30 characters. - Une descption longue requiert au moins 30 caractères. - - - - BugReportHistoryWindow - - Title - Titre - - - Reported at - Rapport envoyé à - - - URL - URL - - - Reports history - Historique de rapports - - - Clear reports history - Vider l’historique des rapports - - - Delete selected entry - Supprimer l’entrée sélectionnée - - - Invalid response from server. - Réponse invalide du serveur. - - - - BugReportLoginDialog - - Log in - Connexion - - - Credentials - Identités - - - Login: - Identification : - - - Password: - Mot de passe : - - - Validation - Validation - - - Validate - Valider - - - Validation result message - Message de validation - - - Abort - Abandonner - - - A login must be at least 2 characters long. - Un identifiant doit avoir au moins 2 caractères. - - - A password must be at least 5 characters long. - Un mot de passe doit avoir au moins 5 caractères. - - - Valid - Valide - - - - CollationsEditor - - - Filter collations - Filtre de collation - - - - Collation name: - Nom de collation : - - - - Implementation language: - Language d’implémentation : - - - - Databases - Base de données - - - - Register in all databases - Inscrire dans toutes les bases de données - - - - Register in following databases: - Inscrire dans les bases de données suivantes : - - - - Implementation code: - Code d’implémentation : - - - - Collations editor - Éditeur de collation - - - - Commit all collation changes - Enregistrer les motifications de collation - - - - Rollback all collation changes - Annuler toutes les modifications de collation - - - - Create new collation - Création de collation - - - - Delete selected collation - Supprimer la collation sélectionnée - - - - Editing collations manual - Manuel pour l'édition de collations - - - - Enter a non-empty, unique name of the collation. - Saisissez un nom unique, non vide, de regroupement. - - - - Pick the implementation language. - Choisir le language d’implémentation. - - - - Enter a non-empty implementation code. - Saisissez un nom, non vide, de language d’implémentation. - - - - Collations editor window has uncommitted modifications. - L’éditeur de collations a des modifications non enregistrées. - - - - ColorButton - - - Pick a color - Choisir une couleur - - - - ColumnCollatePanel - - - Collation name: - Nom de la collation : - - - - Named constraint: - Contrainte nommée : - - - - Enter a name of the constraint. - Saisir le nom de la contrainte. - - - - Enter a collation name. - Saisir le nom de la collation. - - - - ColumnDefaultPanel - - - Default value: - Valeur par défaut : - - - - Named constraint: - Contrainte nommée : - - - - Enter a default value expression. - Saisissez l’expression d’une valeur par défaut. - - - - Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - - - - - Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - - - - Invalid default value expression: %1 - Expression invalide pour une valeur par défaut : %1 - - - - Enter a name of the constraint. - Saisir un nom de contrainte. - - - - ColumnDialog - - - Column - Colonne - - - - Name and type - Nom et type - - - - Scale - - - - - Precision - - - - - Data type: - Type de données : - - - - Column name: - Nom de colonne : - - - - Size: - Taille : - - - - Constraints - Contraintes - - - - Unique - Unique - - - - - - - - - - Configure - Configurer - - - - Foreign Key - Clef étrangère - - - - Collate - Collation - - - - Not NULL - Non NULL - - - - Check condition - Vérifier la condition - - - - Primary Key - Clef primaire - - - - Default - Défaut - - - - Advanced mode - Mode avancé - - - - Add constraint - column dialog - Ajouter une contrainte - - - - Edit constraint - column dialog - Editer la contrainte - - - - - Delete constraint - column dialog - Supprimer la contrainte - - - - Move constraint up - column dialog - Monter la contrainte - - - - Move constraint down - column dialog - Descendre la contrainte - - - - Add a primary key - column dialog - Ajouter une clef primaire - - - - Add a foreign key - column dialog - Ajouter une clef étrangère - - - - Add an unique constraint - column dialog - Ajouter une contrainte d'unicité - - - - Add a check constraint - column dialog - Ajouter une contrainte de contrôle - - - - Add a not null constraint - column dialog - Ajouter une contrainte non NULL - - - - Add a collate constraint - column dialog - Ajouter un commentaire à la contrainte - - - - Add a default constraint - column dialog - Ajouter une contrainte par défaut - - - - Are you sure you want to delete constraint '%1'? - column dialog - Êtes-vous sûr de vouloir supprimer la contrainte « %1 » ? - - - - Correct the constraint's configuration. - Corrigez la configuration de la contrainte. - - - - This constraint is not officially supported by SQLite 2, -but it's okay to use it. - Cette contrainte n’est pas supportée officiellement par SQLite 2, -mais c’est OK pour l’utiliser. - - - - Scale is not allowed for INTEGER PRIMARY KEY columns. - - - - - Precision cannot be defined without the scale. - - - - - Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - - - - - INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - - - - - Precision is not allowed for INTEGER PRIMARY KEY columns. - - - - - ColumnDialogConstraintsModel - - - Type - column dialog constraints - Type - - - - Name - column dialog constraints - Nom - - - - Details - column dialog constraints - Détails - - - - ColumnForeignKeyPanel - - - Foreign table: - Table étrangère : - - - - Foreign column: - Colonne étrangère : - - - - Reactions - Réactions - - - - Deferred foreign key - Clef étrangère refusée - - - - Named constraint - Contrainte nommée - - - - Constraint name - Nom de contrainte - - - - Pick the foreign table. - Sélectionner la table étrangère. - - - - Pick the foreign column. - Sélectionner la colonne étrangère. - - - - Enter a name of the constraint. - Saisir un nom de contraite. - - - - ColumnPrimaryKeyPanel - - - Autoincrement - Auto-incrémentation - - - - Sort order: - Ordre de tri : - - - - Named constraint: - Contrainte nommée : - - - - On conflict: - En cas de conflit : - - - - Enter a name of the constraint. - Saisissez le nom d’une contrainte. - - - Autoincrement (only for %1 type columns) - column primary key - Auto-incrémentation (seulement pour %1 colonne type) - - - - ColumnUniqueAndNotNullPanel - - - Named constraint: - Contrainte nommée : - - - - On conflict: - En cas de conflit : - - - - Enter a name of the constraint. - Saisissez un nom de contrainte. - - - - CompleterWindow - - - Column: %1 - completer statusbar - Colonne : %1 - - - - Table: %1 - completer statusbar - Table : %1 - - - - Index: %1 - completer statusbar - Index : %1 - - - - Trigger: %1 - completer statusbar - Déclencheur : %1 - - - - View: %1 - completer statusbar - Vue : %1 - - - - Database: %1 - completer statusbar - Base de données : %1 - - - - Keyword: %1 - completer statusbar - Mot-clef : %1 - - - - Function: %1 - completer statusbar - Fonction : %1 - - - - Operator: %1 - completer statusbar - Opérateur : %1 - - - - String - completer statusbar - Chaîne de caractères - - - - Number - completer statusbar - Nombre - - - - Binary data - completer statusbar - Données binaires - - - - Collation: %1 - completer statusbar - Collation : %1 - - - - Pragma function: %1 - completer statusbar - Fonction Pragma : %1 - - - - ConfigDialog - - - - Configuration - Configuration - - - - Search - Recherche - - - - General - Général - - - - Keyboard shortcuts - Raccourcis clavier - - - - Look & feel - Apparence - - - - Style - Style - - - - Fonts - Polices - - - - Colors - Couleurs - - - - Plugins - Plugins - - - - Code formatters - Formateurs de code - - - - Data browsing - Navigation de données - - - - Data editors - Éditeurs de données - - - - Data browsing and editing - Navigateur et éditeur de données - - - - Number of data rows per page: - Nombre de lignes de données par page : - - - - - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - <p>Lorsque les données sont lues dans le tableau, la largeur est automatiquement ajustée. Cette valeur limite la largeur initiale pour l’ajustement, mais l’utilisateur peut recadrer les colonnes manuellement au-dessus de cette limite.</p> - - - - Limit initial data column width to (in pixels): - Limite initiale de la largeur de la colonne de données (en pixel) : - - - - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - - - - - <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> - - - - - Use DEFAULT value (if defined), when committing NULL value - - - - - Inserting new row in data grid - Insertion d'une nouvelle ligne dans la grille de données - - - - Before currently selected row - Avant la ligne courante - - - - After currently selected row - Après la ligne courante - - - - At the end of data view - À la fin de la vue de données - - - - <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - - - - - <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - - - - - Place data tab as first tab in a Table Window - Placer l'onglet Données en premier dans les fenêtres de tables - - - - <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - - - - - Data types - Types de données - - - - Available editors: - Éditeurs disponibles : - - - - Editors selected for this data type: - Éditeur sélectionné pour ce type de données : - - - - Schema editing - Edition de schéma - - - - Number of DDL changes kept in history. - Nombre de DDL modifiés gardés dans l’historique. - - - - DDL history size: - Dimension de l’historique DDL : - - - - SQL queries - Requêtes SQL - - - - - Number of queries kept in the history. - Nombre de requêtes gardées dans l’historique. - - - - History size: - Dimension de l’historique : - - - - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> - <p>S’il y a plus d’une requête dans l’éditeur SQL, alors (si cette option est permise) seulement une seule requête sera exécutée -cellesous le curseur d’insertion. Autrement toutes les requêtes seront exécutées. Vous pouvez limiter le nombre de requêtes devant être exécutées en sélectionnant ces requêtes avant leur exécution.</p> - - - - Execute only the query under the cursor - Exécuter seulement la requête sous le curseur - - - - Updates - Mises à jour - - - - Automatically check for updates at startup - Contrôle automatique des mises à jour au lancement - - - - Session - Session - - - - Restore last session (active MDI windows) after startup - Restaurer la dernière session (Fenêtre MDI active) après lancement - - - - Filter shortcuts by name or key combination - Filtre par nom raccourci ou combinaison de touches - - - - Action - Action - - - - Key combination - Combinaison de touches - - - - - Language - Langage - - - - Changing language requires application restart to take effect. - Le changement de langage requiert le redémarrage de l’application pour prendre effet. - - - - Compact layout - Présentation compacte - - - - <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - - - - - Use compact layout - Utiliser la présentation compacte - - - - - Database list - Liste de base de données - - - - If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. - Sur off, les colonnes seront triées dans l’ordre de saisie de l’instruction CREATE TABLE. - - - - Sort table columns alphabetically - Ordre de tri alpha de la colonne - - - - Expand tables node when connected to a database - Développer le nÅ“ud des tables lors de la connexion de la base de données - - - - <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> - <p>Les labels supplémentaires sont ceux montrés à côté des noms dans la liste de bases de données ( bleus,sauf autre configaration). Permettre cette option aboutira aux lablels pour des bases de données, des bases de données invalides et des noeuds (colonnes, index, déclancheur). Pour plus de labels voir des options ci-dessous.<p> - - - - Display additional labels on the list - Afficher des labels supplémentaires dans la liste - - - - For regular tables labels will show number of columns, indexes and triggers for each of tables. - Pour des tables courantes les labels montrerons le nombre der colonnes, index et déclencheurs pour chaque tables. - - - - Display labels for regular tables - Afficher les labels pour les tables courantes - - - - Virtual tables will be marked with a 'virtual' label. - Les tables vituelles seront marquées avec un label « virtuel ». - - - - Display labels for virtual tables - Afficher les labels pour les tables virtuelles - - - - Expand views node when connected to a database - Développer le nÅ“ud des vues lorsque la base de données est connectée - - - - If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) - Si cette option est désactivée, les objets seront triés pour qu’ ils apparaissent dans la table sqlite_master (dans l’ordre de création) - - - - Sort objects (tables, indexes, triggers and views) alphabetically - Tri d’objets (tables, index, déclancheurs et vues) en alpha - - - - Display system tables and indexes on the list - Afficher les tables système et index dans la liste - - - - Database dialog window - - - - - <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> - - - - - Do not mark database to be "permanent" by default - - - - - <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> - - - - - Try to bypass dialog completly when dropping database file onto the list - - - - - Keep NULL value when entering empty value - - - - - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> - - - - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - - - - - Number of memorized table populating configurations - - - - - Show column and row details tooltip in data view - - - - - Table windows - Fenêtres de tables - - - - Open Table Windows with the data tab for start - Sélectionner l'onglet de données lors de l'ouverture d'une fenêtre de table - - - - View windows - Fenêtre de vue - - - - Open View Windows with the data tab for start - Sélectionner l'onglet de données lors de l'ouverture d'une fenêtre de vue - - - - <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - - - - - Place data tab as first tab in a View Window - - - - - Don't show DDL preview dialog when committing schema changes - - - - - - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - - - - - Number of memorized query parameters - - - - - Status Field - - - - - <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> - - - - - Always open Status panel when new message is printed - - - - - Main window dock areas - - - - - Left and right areas occupy corners - - - - - Top and bottom areas occupy corners - - - - - Hide built-in plugins - Cacher des plugins incorporés - - - - Current style: - Style actuel : - - - - Preview - Aperçu - - - - Enabled - En service - - - - Disabled - Hors service - - - - Active formatter plugin - Plugin de formattage actif - - - - SQL editor font - Police de caractères de l’éditeur SQL - - - - Database list font - Liste des polices de caractères de base de données - - - - Database list additional label font - Police de caractères additionelle de la liste des bases de données - - - - Data view font - Police de caractères des données de vue - - - - Status field font - Police de caractères du champ d’état - - - - SQL editor colors - Couleurs de l’éditeur SQL - - - - Current line background - Arrière plan pour la ligne courante - - - - <p>SQL strings are enclosed with single quote characters.</p> - <p>Les chaines SQL sont encadrées avec de caractères simple quote.</p> - - - - String foreground - Avant plan chaine - - - - <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - <p>Les paramètres fournis par l’utilisateur sont passés par valeur. Ils ont l’une de ces formes : </p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - - - - Bind parameter foreground - Premier plan pour les paramètres de lien - - - - Highlighted parenthesis background - Parenthèses surlignées - - - - <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - <p>les valeurs BLOB sont binaire représentés comme nombres hexadécimaux, comme : </p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - - - - BLOB value foreground - Premier plan pour les valeurs BLOB - - - - Regular foreground - Avant plan par défaut - - - - Line numbers area background - Arrière plan pour la zone des numéros de ligne - - - - Keyword foreground - Premier plan pour les mots-cléfs - - - - Number foreground - Premier plan pour les nombres - - - - Comment foreground - Premier plan pour les commentaires - - - - <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> - <p>Les objets valides sont les nom de tables, index, déclencheurs, ou vues qui existent dans la base de données SQLite.</p> - - - - Valid objects foreground - Premier plan pour les objets valides - - - - Data view colors - Couleurs de vue de données - - - - <p>Any data changes will be outlined with this color, until they're committed to the database.</p> - - - - - Uncommitted data outline color - - - - - <p>In case of error while committing data changes, the problematic cell will be outlined with this color.</p> - - - - - Commit error outline color - Surlignage pour les erreurs de commit - - - - NULL value foreground - Premier plan pour la valeur NULL - - - - Deleted row background - Arrière-plan pour une ligne supprimée - - - - Database list colors - Couleurs pour la liste des bases de données - - - - <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> - <p>Des labels supplémentaires indique la version SQLITE, le nombre d’objets au nievau inférieur, etc.</p> - - - - Additional labels foreground - Premier plan pour les labels additionnels - - - - Status field colors - Couleurs du champ d’état - - - - Information message foreground - Premier plan pour les messages d’information - - - - Warning message foreground - Premier plan pour les avertissements - - - - Error message foreground - Premier plan pour les erreurs - - - - Description: - plugin details - Description : - - - - Category: - plugin details - Catégorie : - - - - Version: - plugin details - Version : - - - - Author: - plugin details - Auteur : - - - - Internal name: - plugin details - Nom interne : - - - - Dependencies: - plugin details - Dépendances : - - - - Conflicts: - plugin details - Conflits : - - - - Plugin details - Détails du plugin - - - - Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. - Les plugins sont chargés/déchargés immédiatement avec vérifié/non vérifié, mais les modifications de la liste de plugins à charger au lancement ne sont pas enregistrées avant l’enregistrement de la configuration entière. - - - - %1 (built-in) - plugins manager in configuration dialog - %1 (intégré) - - - - Details - Détails - - - - No plugins in this category. - Pas de plugins dans cette catégorie. - - - - Add new data type - Ajouter un nouveau type de données - - - - Rename selected data type - Renommer le type de données sélectionné - - - - Delete selected data type - Supprimer le type de données sélectionnées - - - - Help for configuring data type editors - Aide à la configuration des éditeurs de type de données - - - - ConstraintCheckPanel - - - The condition - Condition - - - - Named constraint: - Contrainte nommée : - - - - On conflict - Sur conflit - - - - Enter a valid condition. - Saississez une condition valide. - - - - Enter a name of the constraint. - Saississez un nom de contrainte valide. - - - - ConstraintDialog - - - New constraint - constraint dialog - Nouvelle contrainte - - - - Create - constraint dialog - Créer - - - - Edit constraint - dialog window - Modifier la contrainte - - - - Apply - constraint dialog - Appliquer - - - - Primary key - table constraints - Clef primaire - - - - Foreign key - table constraints - Clef étrangère - - - - Unique - table constraints - Unique - - - - Not NULL - table constraints - Non NULL - - - - Check - table constraints - Contrôle - - - - Collate - table constraints - Regroupe - - - - Default - table constraints - Défaut - - - - ConstraintTabModel - - - Table - table constraints - Table - - - - Column (%1) - table constraints - Colonne (%1) - - - - Scope - table constraints - Portée - - - - Type - table constraints - Type - - - - Details - table constraints - Details - - - - Name - table constraints - Nom - - - - CssDebugDialog - - - SQLiteStudio CSS console - - - - - DataView - - - Filter data - data view - Filtre de données - - - - Grid view - Table - - - - Form view - Formulaire - - - - Refresh table data - data view - Actualiser les données de la table - - - - First page - data view - Première page - - - - Previous page - data view - Page précédente - - - - Next page - data view - Page suivante - - - - Last page - data view - Dernière page - - - - Filter - - - - - Hit Enter key or press "Apply filter" button on toolbar to apply new value. - - - - - Show filter inputs per column - data view - - - - - Apply filter - data view - Appliquer le filtre - - - - Commit changes for selected cells - data view - Enregistrer les modifications des cellules sélectionnées - - - - Rollback changes for selected cells - data view - Annuler les modifications des celulles sélectionnées - - - - Show grid view of results - sql editor - Affichage des résultats en tableau - - - - Show form view of results - sql editor - Affichage des résultat en formulaire - - - - Filter by text - data view - Filtrer par texte - - - - Filter by the Regular Expression - data view - Filtrer par une expression standard - - - - Filter by SQL expression - data view - Filtrer par une expression SQL - - - - Tabs on top - data view - Onglets en haut - - - - Tabs at bottom - data view - Onglet en bas - - - - Place new rows above selected row - data view - - - - - Place new rows below selected row - data view - - - - - Place new rows at the end of the data view - data view - - - - - Total number of rows is being counted. -Browsing other pages will be possible after the row counting is done. - Le total des lignes en cours de comptage. La navigation d’autres pages à la fin du comptage. - - - - Row: %1 - Lignes : %1 - - - - DbConverterDialog - - - Convert database - Base de données convertie - - - - Source database - Base de données source - - - - Source database version: - Version de la base de données source : - - - - Target database - Base de données cible - - - - Target version: - Version cible : - - - - This is the file that will be created as a result of the conversion. - Voici le fichier qui sera créé pour lles résultats de la conversion. - - - - Target file: - Fichier cible : - - - - Name of the new database: - Nom de la nouvelle base de données : - - - - This is the name that the converted database will be added to SQLiteStudio with. - Voici le nom de la base de données convertie qui sera ajoutée à SQLiteStudio. - - - - Select source database - Sélectionnez la base de données source - - - - Enter valid and writable file path. - Saississez le chemin d’un fichier valide et en écriture. - - - - Entered file exists and will be overwritten. - Le fichier remplacera l’existant. - - - - Enter a not empty, unique name (as in the list of databases on the left). - Saississez un nom unique, non vide(comme dans la liste des bases de données à gauche). - - - - No valid target dialect available. Conversion not possible. - Nom de la cible non valide. conversion impossible. - - - - Select valid target dialect. - Sélectionnez un nom cie cible valide. - - - - Database %1 has been successfully converted and now is available under new name: %2 - La base de données %1 a été convertie correctement et disponible sous le nom : %2 - - - - SQL statements conversion - Conversion des déclacrations SQL - - - - Following error occurred while converting SQL statements to the target SQLite version: - L’erreur suivante est survenue lors de la conversion des déclarations SQL dans cible version SQLite : - - - - Would you like to ignore those errors and proceed? - Souhaitez-vous ignorer ces erreurs et continuer ? - - - - DbDialog - - - Database - Base de données - - - - Database type - Type de base de données - - - - Database driver - Pilote de base de données - - - - Options - Options - - - - Permanent (keep it in configuration) - Permanent (conserver dans la configuration) - - - - Test connection - Tester la connexion - - - Browse for database file on local computer - Navigation de la base de données en local - - - - Create new database file - Créer un nouveau fichier de base de données - - - - - File - Fichier - - - - Name (on the list) - Nom (dans la liste) - - - - <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> - aasfd - <p>Autorisez-ceci si vous voulez que la base de données soit stockée dans le fichier de configuration et restauré chaque fois SQLiteStudio est lancé.</p> - - - - Browse for existing database file on local computer - - - - - Browse - Navigateur - - - - Enter an unique database name. - - - - - This name is already in use. Please enter unique name. - - - - - <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - - - - - Enter a database file path. - - - - - This database is already on the list under name: %1 - - - - - Select a database type. - - - - - DbObjectDialogs - - - Delete table - Table supprimée - - - - Are you sure you want to delete table %1? - Confirmez la suppression de la table %1 ? - - - - Delete index - Index supprimé - - - - Are you sure you want to delete index %1? - Confirmez la suppression de l’index %1 ? - - - - Delete trigger - Déclencheur supprimé - - - - Are you sure you want to delete trigger %1? - Confirmez la suppression du déclencheur %1 ? - - - - Delete view - Vue supprimée - - - - Are you sure you want to delete view %1? - Confirmez la suppression de la vue %1 ? - - - - - Error while dropping %1: %2 - Erreur à l’abandon %1 : %2 - - - - Delete objects - Objets supprimés - - - - Are you sure you want to delete following objects: -%1 - - - - - Cannot start transaction. Details: %1 - - - - - Cannot commit transaction. Details: %1 - - - - - DbTree - - - Databases - Base de données - - - - Filter by name - Filtre par nom - - - - Copy - Copier - - - - Paste - Coller - - - - Select all - Tout sélectionner - - - - Create a group - Créer un groupe - - - - Delete the group - Supprimer le groupe - - - - Rename the group - Renommer le groupe - - - Add a database - Attacher une base de données - - - Edit the database - Modifier la base de données - - - Remove the database - Déatcher la base de données - - - Connect to the database - Connecter une base de données - - - Disconnect from the database - Déconnecter la base de données - - - - Import - Importer - - - Export the database - Exporter la base de données - - - Convert database type - Convertir le format de base de données - - - Vacuum - Vaccum - - - Integrity check - Contrôler l’intégrité - - - Create a table - Créer une table - - - Edit the table - Modifier la table - - - Delete the table - Supprimer la table - - - - Export the table - Exporter la table - - - - Import into the table - Importer dans la table - - - - Populate table - Peupler une table - - - - Create similar table - Créer une table identique - - - - Reset autoincrement sequence - Réinitialise l’auto-incrémentation - - - Create an index - Créer un index - - - Edit the index - Modifier l’index - - - Delete the index - Supprimer l’index - - - Create a trigger - Créer un déclencheur - - - Edit the trigger - Modifier le déclencheur - - - Delete the trigger - Supprimer le déclencheur - - - Create a view - Créer une vue - - - Edit the view - Modier la vue - - - Delete the view - Supprimer la vue - - - - Add a column - Ajouter une colonne - - - - Edit the column - Modifier la colonne - - - - Delete the column - Supprimer la colonne - - - - Delete selected items - Supprimer les objets sélectionnés - - - - Clear filter - Vider le filtre - - - Refresh all database schemas - Actualiser tous les schémas de base de données - - - Refresh selected database schema - Actualiser le schéma de base de données sélectionné - - - - - Erase table data - - - - - - Database - Base de données - - - - Grouping - Groupement - - - - Generate query for table - - - - - - Create group - Créer un groupe - - - - Group name - Nom du groupe - - - - Entry with name %1 already exists in group %2. - L’entrée nommée %1 existe déjà dans le groupe %2. - - - - Delete group - Supprimer le groupe - - - - Are you sure you want to delete group %1? -All objects from this group will be moved to parent group. - Êtes-vous certain de supprimer le groupe %1 ? -Tous les objets de ce groupe seront déplacés dans le groupe parent. - - - - Are you sure you want to remove database '%1' from the list? - - - - - Are you sure you want to remove following databases from the list: -%1 - - - - - Remove database - - - - - Vacuum (%1) - - - - - Autoincrement value for table '%1' has been reset successfully. - - - - - Are you sure you want to delete all data from table(s): %1? - - - - - - Cannot import, because no import plugin is loaded. - Import impossible, car aucun plugin d’import n’est chargé. - - - - Execution from file cancelled. Any queries executed so far have been rolled back. - - - - - &Add a database - - - - - &Edit the database - - - - - &Remove the database - - - - - &Connect to the database - - - - - &Disconnect from the database - - - - - &Export the database - - - - - Con&vert database type - - - - - Vac&uum - - - - - &Integrity check - - - - - Create a &table - - - - - Edit the t&able - - - - - Delete the ta&ble - - - - - Create an &index - - - - - Edit the i&ndex - - - - - Delete the in&dex - - - - - Create a trig&ger - - - - - Edit the trigg&er - - - - - Delete the trigge&r - - - - - Create a &view - - - - - Edit the v&iew - - - - - Delete the vi&ew - - - - - &Refresh all database schemas - - - - - Re&fresh selected database schema - - - - - Open file's directory - - - - - Execute SQL from file - - - - - - Cannot export, because no export plugin is loaded. - Export impossible, car aucun plugin d’import n’est chargé. - - - - Integrity check (%1) - Contrôle d’intégrité (%1) - - - - Reset autoincrement - Remise à zéro de l’auto-incrément - - - - Are you sure you want to reset autoincrement value for table '%1'? - Êtes-vous certain de vouloir réinitialiser l’auto-incrémentation de la table « %1 » ? - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - Une erreur est survenue pendant la réinitialisation de la valeur de l’auto-incrémentation de la table « %1 » : %2 - - - - An error occurred while trying to delete data from table '%1': %2 - - - - - All data has been deleted for table '%1'. - - - - - Following objects will be deleted: %1. - Les objets suivant vont être supprimés : %1. - - - - Following databases will be removed from list: %1. - Les bases de données suivantes seront enlevées de la liste : %1. - - - - Remainig objects from deleted group will be moved in place where the group used to be. - Les objets restants du groupe supprimé seront déplacés où le groupe a eu l’habitude d’être. - - - - %1<br><br>Are you sure you want to continue? - %1<br><br>Êtes-vous certain de vouloir continuer ? - - - - Delete objects - Objets supprimés - - - - Could not execute SQL, because application has failed to start transaction: %1 - - - - - Could not open file '%1' for reading: %2 - Impossible d’ouvrir en lecture le fichier « %1 » : %2 - - - - Could not execute SQL, because application has failed to commit the transaction: %1 - - - - - Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - - - - - Finished executing %1 queries in %2 seconds. - - - - - Could not execute SQL due to error. - - - - - DbTreeItemDelegate - - - error - dbtree labels - erreur - - - - (system table) - database tree label - (Table système) - - - - (virtual) - virtual table label - (virtuel) - - - - (system index) - database tree label - (index système) - - - - DbTreeModel - - - Database: %1 - dbtree tooltip - Base de données : %1 - - - - Version: - dbtree tooltip - Version : - - - - File size: - dbtree tooltip - Taille fichier : - - - - Encoding: - dbtree tooltip - Codage : - - - - Error: - dbtree tooltip - Erreur : - - - - Table : %1 - dbtree tooltip - Table : %1 - - - - Columns (%1): - dbtree tooltip - Colonnes (%1): - - - - Indexes (%1): - dbtree tooltip - Indexs (%1) : - - - - Triggers (%1): - dbtree tooltip - Déclencheurs (%1) : - - - - Copy - Copier - - - - Move - Déplacer - - - - Include data - Données incluses - - - - Include indexes - Index inclus - - - - Include triggers - Déclencheurs inclus - - - - Abort - Abandonner - - - - Could not add dropped database file '%1' automatically. Manual setup is necessary. - - - - - Referenced tables - Tables référencées - - - - Do you want to include following referenced tables as well: -%1 - Vous voulez inclure des tables référencées suivantes aussi : -%1 - - - - Name conflict - Conflit de nom - - - - Following object already exists in the target database. -Please enter new, unique name, or press '%1' to abort the operation: - L’objet suivant existe déjà dans la base de données cible. -Entrez SVP un nouveau nom, unique, ou cliquez « %1 » pour d’interrompre l’opération : - - - - SQL statements conversion - Conversion des déclarations SQL - - - - Following error occurred while converting SQL statements to the target SQLite version: - L’erreur suivante est survenue en convertissant des déclarations de SQL de la version cible SQLite : - - - - Would you like to ignore those errors and proceed? - Voulez-vous ignorer ces erreurs et procéder ? - - - - DdlHistoryWindow - - - Filter by database: - Filtre par base de données : - - - - -- Queries executed on database %1 (%2) --- Date and time of execution: %3 -%4 - -- Requête éxécutée sur la base de données %1 (%2) --- Date et heure d’exécution : %3 -%4 - - - - DDL history - Historique DDL - - - - DdlPreviewDialog - - - Queries to be executed - Requêtes à exécuter - - - - Don't show again - Ne plus afficher - - - - DebugConsole - - - SQLiteStudio Debug Console - Console SQLiteStudio de débogage - - - - EditorWindow - - - Query - Requête - - - - History - Historique - - - - Results in the separate tab - Résultats dans un onglet séparé - - - - Results below the query - Résultats après la requête - - - - - SQL editor %1 - Éditeur SQL %1 - - - - Results - Résultats - - - - Execute query - Exécuter la requête - - - - Explain query - Explication de la requête - - - - Clear execution history - sql editor - Vider l’historique d’exécution - - - - Export results - sql editor - Exporter résultats - - - - Create view from query - sql editor - Créer une vue à partir d’une requête - - - - Previous database - Base de données précédente - - - - Next database - Base de données suivante - - - - Show next tab - sql editor - Afficher l’onglet suivant - - - - Show previous tab - sql editor - Afficher l’onget précédent - - - - Focus results below - sql editor - - - - - Focus SQL editor above - sql editor - Focus sur l’éditeur SQL ci-dessus - - - - Delete selected SQL history entries - sql editor - - - - - Active database (%1/%2) - Base de données active (%1/%2) - - - - Query finished in %1 second(s). Rows affected: %2 - Requête terminée en %1 secondes. Nombre de lignes : %2 - - - - Query finished in %1 second(s). - Requête terminée en %1 seconde(s). - - - - Clear execution history - Supprimer l’historique d’exécution - - - - Are you sure you want to erase the entire SQL execution history? This cannot be undone. - Êtes vous certain de vouloir supprimer la totalité de l’historique d’exécution SQL ? Aucun retour possible. - - - - Cannot export, because no export plugin is loaded. - Impossible d’exporter, car aucun plugin d’expertation n’est chargés. - - - - No database selected in the SQL editor. Cannot create a view for unknown database. - Aucune base de données den sélectionnée dans l’éditeur SQL. Impossible de créer une vue sur une base de données inconnue. - - - - Editor window "%1" has uncommitted data. - - - - - ErrorsConfirmDialog - - - Errors - Erreurs - - - - Following errors occured: - Les erreurs suivantes sont arrivées : - - - - Would you like to proceed? - Désirez-vous traiter ? - - - - ExecFromFileDialog - - - Execute SQL from file - - - - - Input file - - - - - Path to file - - - - - Browse for file - - - - - Options - Options - - - - File encoding - - - - - Skip failing SQL statements - - - - - SQL scripts (*.sql);;All files (*) - Scripts SQL (*.sql);;Tous les fichiers (*) - - - - Execute SQL file - - - - - Please provide file to be executed. - - - - - Provided file does not exist or cannot be read. - - - - - ExportDialog - - - Export - Exporter - - - - What do you want to export? - Que voulez-vous exporter ? - - - - A database - Une base de données - - - - A single table - Une table - - - - Query results - Résultats d’une requête - - - - Table to export - Table à exporter - - - - Database - Base de données - - - - Table - Table - - - - Options - Options - - - - When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. - Lorsque cette option n’est contrôlée, alors seulement le DDL de la table (CREATE TABLE...) est exporté. - - - - Export table data - Exporter les données de la table - - - - Export table indexes - Exporter les index de la table - - - - Export table triggers - Exporter les déclencheurs de la table - - - - Note, that exporting table indexes and triggers may be unsupported by some output formats. - NB : les index de la table d’exportation et les déclencheurs peuvent ne pas être pris en charge par certains formats d’export. - - - - Select database objects to export - Sélectionnez les objets de la base de données à exporter - - - - Export data from tables - Exporter les données des tables - - - - Select all - Tout sélectionner - - - - Deselect all - Tout désélectionner - - - - - Database: - Base de données : - - - - Query to export results for - Résultats de la requête - - - - Query to be executed for results: - Requête à exécuter : - - - - Export format and options - Exporter formatset options - - - - Export format - Format d’exportation - - - - Output - Sortie - - - - Exported file path - Chemin du fichier d’exportation - - - - Clipboard - presse-papier - - - - File - Fichier - - - - Exported text encoding: - Texte encodé exporté : - - - - Export format options - Exporter options de format - - - - Cancel - Annuler - - - - - - Select database to export. - Sélecctionnez la base de données à exporter. - - - - Select table to export. - Sélectionnez la table à exporter. - - - - Enter valid query to export. - Saississez une requête valide à exporter. - - - - Select at least one object to export. - Sélectionnez au moins un objet à exporter. - - - - You must provide a file name to export to. - Vous devez fournir le nom d’un fichier à exporter. - - - - Path you provided is an existing directory. You cannot overwrite it. - Le chemin fourni est un répertoire existant. Vous ne pouvez pas l’écraser. - - - - The directory '%1' does not exist. - Le répertoire « %1 » n’existe pas. - - - - The file '%1' exists and will be overwritten. - Le fichier « %1 » existe et sera écrasé. - - - - All files (*) - Tous les fichiers(*) - - - - Pick file to export to - Sélectionnez un fichier à exporter - - - - Internal error during export. This is a bug. Please report it. - Erreur interne pendant l’exportation. c’est un bug. SVP veuillez le reporter. - - - - FileExecErrorsDialog - - - Execution errors - - - - - Following errors were encountered during execution of SQL statements from the file: - - - - - SQL - - - - - Error - Erreur - - - - Statements that were executed successfully were commited. - - - - - Statements that were executed successfully were rolled back. - - - - - FontEdit - - - Choose font - font configuration - Choisir la police - - - - Form - - - Active SQL formatter plugin - Activer le plugin de formattage SQL - - - - FormView - - - Commit row - form view - Enregistrer ligne - - - - Rollback row - form view - Annuler ligne - - - - First row - form view - Première ligne - - - - Previous row - form view - Ligne précédente - - - - Next row - form view - Ligne suivante - - - - Last row - form view - Dernière ligne - - - - Insert new row - form view - Insérer une nouvelle ligne - - - - Delete current row - form view - Supprimer la ligne courante - - - - FunctionsEditor - - - Filter funtions - Fonctions de filtrations - - - - Function name: - Nom fonction : - - - - Implementation language: - Langage : - - - - Type: - Type : - - - - Input arguments - Entrez arguments - - - - Undefined - Non défini - - - - Databases - Bases de données - - - - Register in all databases - Enregistre toutes les bases de données - - - - Register in following databases: - Enregistre les bases de données suivantes : - - - - Initialization code: - Code d’initialisation : - - - - - Function implementation code: - Fonction de code d’implémentation : - - - - Final step implementation code: - Etape finale de code d’implémentaion : - - - - SQL function editor - Fonction éditeur SQL - - - - Commit all function changes - Enregistre toutes les fonctions modifiées - - - - Rollback all function changes - Annule toutes les fonctions modifiées - - - - Create new function - Crée une nouvelle fonction - - - - Delete selected function - Supprime une fonction sélectionnée - - - - Custom SQL functions manual - Personalisation des fonctions SQL - - - - Add function argument - Ajoute un argument à la fonction - - - - Rename function argument - Renomme l’argument de la fonction - - - - Delete function argument - Supprime l’argument de la fonction - - - - Move function argument up - Monte l’argument de la fonction - - - - Move function argument down - Descend l’argument de la fonction - - - - Scalar - Scalaire - - - - Aggregate - Agregate - - - - Enter a non-empty, unique name of the function. - Saississez un nom unique de fonction. - - - - Pick the implementation language. - Choississez un langage. - - - - Per step code: - Code par étape : - - - - Enter a non-empty implementation code. - Saississez un code d’implémentation non vide. - - - - argument - new function argument name in function editor window - argument - - - - Functions editor window has uncommitted modifications. - - - - - ImportDialog - - - Import data - Import données - - - - Table to import to - Table à importer vers - - - - Table - Table - - - - Database - Base de données - - - - Data source to import from - Source de données à importer de - - - - Data source type - Type de données source - - - - Options - Options - - - - Input file: - Fichier : - - - - Text encoding: - Texte codé : - - - - <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> - - - - - Ignore errors - - - - - Data source options - Optrions de source de données - - - - Cancel - Annuler - - - - If you type table name that doesn't exist, it will be created. - Si vous saississez un nom de table inexistant, celle-ci sera créée. - - - - Enter the table name - Saississez un nom de table - - - - Select import plugin. - Sélectionnez un plugin d’importation. - - - - You must provide a file to import from. - Vous devez fournir un fichier à importer. - - - - The file '%1' does not exist. - Le fichier « %1 » n’existe pas. - - - - Path you provided is a directory. A regular file is required. - Le chemin indiqué est un répertoire. Un fichier est requis. - - - - Pick file to import from - Sélectionnez le fichier d’importation - - - - IndexDialog - - - - Index - Index - - - - On table: - De la table : - - - - Index name: - Nom index : - - - - Partial index condition - Condition partielle d’index - - - - Unique index - Index unique - - - - Column - Colonne - - - - Collation - Regroupement - - - - Sort - Tri - - - - Delete selected indexed expression - - - - - Moves selected index column up in the order, making it more significant in the index. - - - - - Moves selected index column down in the order, making it less significant in the index. - - - - - Edit selected indexed expression - - - - - Add indexed expression - - - - - DDL - DDL - - - - Tried to open index dialog for closed or inexisting database. - Vous tentez d’ouvrir le dialogue de l’index d’une base de données fermée ou inexistante. - - - - Could not process index %1 correctly. Unable to open an index dialog. - Impossible de définir l’index %1 correctement. Ouvrir un dialogue d’index valide. - - - - Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. - - - - - Pick the table for the index. - Sélectionnez la table pour l’index. - - - - Select at least one column. - Selectionnez au moins une colonne. - - - - Enter a valid condition. - Saississez une condition valide. - - - - default - index dialog - defaut - - - - Sort order - table constraints - Ordre de tri - - - - - Error - index dialog - Erreur - - - - Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? - Impossible de créer un index, car les valeurs des colonnes sélectionnées ne sont pas uniques. Voulez-vous exécuter une requête SELECT pour voir les valeurs problématiques ? - - - - An error occurred while executing SQL statements: -%1 - Une erreur survenue à l’exécution de l’SQL : -%1 - - - - IndexExprColumnDialog - - - Indexed expression - - - - - Expression to index - - - - - This expression is already indexed by the index. - - - - - Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. - - - - - Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. - - - - - It's forbidden to use 'SELECT' statements in indexed expressions. - - - - - Enter an indexed expression. - - - - - Invalid expression. - - - - - LanguageDialog - - - Language - Langage - - - - Please choose language: - SVP choississez un langage : - - - - MainWindow - - - Database toolbar - Barre d’outils de base de données - - - - Structure toolbar - Barre d’outils de structure - - - - Tools - Barre d’outils des éditeurs - - - - Window list - Liste des fenêtres ouvertes - - - - View toolbar - Barre d’outils de fenêtrage - - - - Configuration widgets - Configuration widgets - - - - Syntax highlighting engines - Syntaxe surlignée des moteurs - - - - Data editors - Éditeurs de données - - - - Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - Passage en mode débogue. Cliquez %1 ou utilisez l’entrée du menu « l’Aide / Ouvrir la console de débogage ». - - - - Running in debug mode. Debug messages are printed to the standard output. - Passage en mode débogue. Les messages de débogage sont imprimés dans la sortie standard. - - - - You need to restart application to make the language change take effect. - Vous devez relancer l’application pour que le langage prenne effet. - - - Open SQL editor - Ouvrir l’éditeur SQL - - - Open DDL history - Ouvrir l’historique DDL - - - Open SQL functions editor - Éditeur de fonctions SQL - - - Open collations editor - Ouvrir l’éditeur de collections - - - Import - Importer - - - Export - Exporter - - - Open configuration dialog - Préférences - - - Tile windows - Organisation des fenêtres en grille - - - Tile windows horizontally - Organisation horizontale des fenêtres - - - Tile windows vertically - Organisation verticale des fenêtres - - - Cascade windows - Organisation des fenêtres en cascade - - - - Next window - Fenêtre suivante - - - - Previous window - Fenêtre précédante - - - - Hide status field - Cacher le champ d’état - - - Close selected window - Fermer la fenêtre sélectionnée - - - Close all windows but selected - Fermer toutes les fenêtres sélectionnées - - - Close all windows - Fermer toutes les fenêtres - - - Restore recently closed window - Restaurer la dernière fenêtre fermée - - - Rename selected window - Renommer la fenêtre sélectionnée - - - - Open Debug Console - Ouvrir la console de debogage - - - - Open CSS Console - Ouvrir la console CSS - - - Report a bug - Rapporter un bogue - - - Propose a new feature - Proposer une fonctionnalité - - - About - À propos de… - - - Licenses - Licences - - - Open home page - Site web de l’application - - - Open forum page - Forum d’aide - - - User Manual - Manuel utilisateur en ligne - - - SQLite documentation - Documentation en ligne de SQLite - - - Report history - Historique - - - Check for updates - Vérifier les mises à jour - - - Database - menubar - Base de données - - - Structure - menubar - Structure - - - View - menubar - Vue - - - - Window list - menubar view menu - Liste des fenêtres - - - Tools - menubar - Outils - - - Help - Aide - - - - Open SQL &editor - - - - - Open DDL &history - - - - - Open SQL &functions editor - - - - - Open &collations editor - - - - - Open ex&tension manager - - - - - &Import - - - - - E&xport - - - - - Open confi&guration dialog - - - - - &Tile windows - - - - - Tile windows &horizontally - - - - - Tile windows &vertically - - - - - &Cascade windows - - - - - Close selected &window - - - - - Close all windows &but selected - - - - - Close &all windows - - - - - Re&store recently closed window - - - - - &Rename selected window - - - - - Report a &bug - - - - - Propose a new &feature - - - - - &About - - - - - &Licenses - - - - - Open home &page - - - - - Open fo&rum page - - - - - User &Manual - - - - - SQLite &documentation - - - - - Bugs and feature &requests - - - - - Check for &updates - - - - - &Database - menubar - - - - - &Structure - menubar - - - - - &View - menubar - - - - - &Tools - menubar - - - - - &Help - - - - - Could not set style: %1 - main window - Impossible de positionner le style : %1 - - - - Cannot export, because no export plugin is loaded. - Exportation impossible, aucun plugin d’exportation n’est chargé. - - - - Cannot import, because no import plugin is loaded. - Importation impossible, aucun plugin d’importation n’est chargé. - - - - Rename window - Renommer la fenêtre - - - - Enter new name for the window: - Saississez un nouveau nom de fenêtre : - - - - New updates are available. <a href="%1">Click here for details</a>. - Une nouvelle mise à jour est disponible. <a href="%1"> cliquez ici pour détails</a>. - - - - You're running the most recent version. No updates are available. - Vous utilisez la dernière version. Aucune mise à jour de disponible. - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - - - - - Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 - La base de données passée en paramètre dans la ligne de commande (%1)a été temporaire ajoutée à la liste sous le nom : %2 - - - - Could not add database %1 to list. - Impossible d’ajouter la base de données %1 à la liste. - - - - MdiWindow - - - Uncommitted changes - - - - - Close anyway - Fermer - - - - Don't close - Ne pas fermer - - - - MultiEditor - - - Null value - multieditor - Valeur NULL - - - - Configure editors for this data type - Configurer l’éditeur pour ce type de données - - - - Open another tab - - - - - Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. - Plugin d"éditeur de données « %1 » non chargé, ausii il n’ai pas défini pour le type de données « %1 ». - - - - Deleted - multieditor - Suppression - - - - Read only - multieditor - Lecture seule - - - - MultiEditorBool - - Boolean - booleen - - - - MultiEditorBoolPlugin - - - Boolean - booleen - - - - MultiEditorDate - - Date - Date - - - - MultiEditorDatePlugin - - - Date - Date - - - - MultiEditorDateTime - - Date & time - date & heure - - - - MultiEditorDateTimePlugin - - - Date & time - date & heure - - - - MultiEditorHex - - Hex - Hex - - - - MultiEditorHexPlugin - - - Hex - Hex - - - - MultiEditorNumeric - - Number - numeric multi editor tab name - Nombre - - - - MultiEditorNumericPlugin - - - Number - numeric multi editor tab name - Nombre - - - - MultiEditorText - - Text - Texte - - - - Tab changes focus - Tab modifie le focus - - - - Cut - Couper - - - - Copy - Copier - - - - Paste - Coller - - - - Delete - Supprimer - - - - Undo - Annuler - - - - Redo - Rétablir - - - - MultiEditorTextPlugin - - - Text - Texte - - - - MultiEditorTime - - Time - Heure - - - - MultiEditorTimePlugin - - - Time - Heure - - - - NewConstraintDialog - - - New constraint - Nouvelle contrainte - - - - - Primary Key - new constraint dialog - Clef primaire - - - - - Foreign Key - new constraint dialog - Clef étrangère - - - - - Unique - new constraint dialog - Unique - - - - - Check - new constraint dialog - Contrôle - - - - Not NULL - new constraint dialog - Non NULL - - - - Collate - new constraint dialog - Regrouper - - - - Default - new constraint dialog - Défault - - - - NewVersionDialog - - - SQLiteStudio updates - Mises à jour SQLiteStudio - - - - New updates are available! - Les nouvelles mises à jours sont valides ! - - - - Component - Composant - - - - This application will be closed and the update installer will start to download and install all the updates. - - - - Current version - Version courante - - - - Update version - Version de mise à jour - - - - Check for updates on startup - Contrôle de nouvelles version au lancement - - - - Update to new version! - Mettre à jour la nouvelle version ! - - - The update will be automatically downloaded and installed. This will also restart application at the end. - La mise à jour sera automatiquement téléchargée et installée. Un redémarrage de l’application sera aussi effectué à la fin. - - - - Not now. - Paas maintenant. - - - - Don't install the update and close this window. - Ne pas installer la mise à jour maintenant et fermer cette fenêtre. - - - - PopulateConfigDialog - - - Populating configuration - Remplir la configuration - - - - Configuring <b>%1</b> for column <b>%2</b> - Configuration <b>%1</b> pour colonne <b>%2</b> - - - - PopulateDialog - - - Populate table - Peupler la table - - - - Database - Base de données - - - - Table - Table - - - - Columns - Colonnes - - - - Number of rows to populate: - Nombre de lignes à peupler : - - - - Populate - populate dialog button - Peupler - - - - Abort - Abandonner - - - - Configure - Configurer - - - - Populating configuration for this column is invalid or incomplete. - La configuration du peuplement pour cette colonne est invalide ou incomplète. - - - - Select database with table to populate - Sélectionner la base de données avec la table à peupler - - - - Select table to populate - Sélectionner la table à peupler - - - - You have to select at least one column. - Vous devez sélectionner au moins une colonne. - - - - QObject - - - Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). - impossible d’éditer les colonnes qui ont le résultat composé des déclarations %1 (inclus %2, %3 ou %4 mots-clefs). - - - - The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. - Le mécanisme d’exécution de la requête a eu des problèmes avec l’extraction du ROWID’S. Ceci pourrait être un bogue de l’application. Vous pouvez le rapporter. - - - - Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. - La colonne demandée est un résultat d’expression de SQL, au lieu d’une sélection de colonne simple. De telles colonnes ne peuvent pas être éditées. - - - - Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. - La colonne demandée appartient à une table limitée SQLite. Ces tables ne peuvent pas être éditées directement. - - - - Cannot edit results of query other than %1. - Impossible d’éditer les résultats de la requëte autrement que %1. - - - - Cannot edit columns that are result of aggregated %1 statements. - Impossible d’éditer les colonnes qui sont le résultat de déclarations agrégées %1. - - - - Cannot edit columns that are result of %1 statement. - Impossible d’éditer les colonnesqui sont le résultat de déclaration %1. - - - - Cannot edit columns that are result of common table expression statement (%1). - Impossible d’éditer les colonnes qui sont le résultat de table commune de déclaration (%1). - - - - - - - on conflict: %1 - data view tooltip - Sur conflit : %1 - - - - references table %1, column %2 - data view tooltip - Références table %1, colonne %2 - - - - condition: %1 - data view tooltip - Condition : %1 - - - - collation name: %1 - data view tooltip - Nom de collation : %1 - - - - Data grid view - Vue de tableau de données - - - - Copy cell(s) contents to clipboard - Copie le contenu de cellule(s) dans le presse-papier - - - - Copy cell(s) contents together with header to clipboard - - - - - Paste cell(s) contents from clipboard - Colle - - - - Set empty value to selected cell(s) - Efface le contenu de cellule(s) - - - - Set NULL value to selected cell(s) - Met à NULL les cellules séléctionnées - - - - Commit changes to cell(s) contents - Enregistre les modifications de cellule(s) - - - - Rollback changes to cell(s) contents - Annule les modifications de cellule(s) - - - - Delete selected data row - Supprime les données de la ligne sélectionnée - - - - Insert new data row - Insére une nouvelle ligne de données - - - - Open contents of selected cell in a separate editor - Contenu ouvert de cellule choisie dans un éditeur séparé - - - - Total pages available: %1 - Nombre de pages disponibles : %1 - - - - Total rows loaded: %1 - Nombre de lignes chargées : %1 - - - - Data view (both grid and form) - Vue des données (tableau et formulaire) - - - - Refresh data - Actualisation des données - - - - Switch to grid view of the data - Basculer sur la vue des données en table - - - - Switch to form view of the data - Basculer sur la vue des données en formulaire - - - - Database list - Liste de bases de données - - - - Delete selected item - Suppression de l’item sélectionné - - - - Clear filter contents - Effacer le contenu du filtre - - - - Refresh schema - Actualiser le schéma - - - - Refresh all schemas - Actualiser tous les schémas - - - - Add database - Ajouter une base de données - - - - Select all items - Séléctionner tous les éléments - - - - Copy selected item(s) - Copie d’item(s) sélectionné(s) - - - - - - Paste from clipboard - Collé dans le presse-papier - - - - Tables - Tables - - - - Indexes - Index - - - - Triggers - Déclencheurs - - - - Views - Vues - - - - Columns - Colonnes - - - - Data form view - Formulaire vue de données - - - - Commit changes for current row - Enregistrement de la ligne courante - - - - Rollback changes for current row - Annulation de la ligne courante - - - - Go to first row on current page - Aller à la première ligne de la page courante - - - - Go to next row - Aller à la ligne suivante - - - - Go to previous row - Aller à la ligne précédente - - - - Go to last row on current page - Aller à la dernière ligne de la page courante - - - - Insert new row - Insérer une nouvelle ligne - - - - Delete current row - Supprimer la ligne courante - - - - Main window - Fenêtre principale - - - - Open SQL editor - Ouvrir l’éditeur SQL - - - - Previous window - Fenêtre précédente - - - - Next window - Fenêtre suivante - - - - Hide status area - Cacher la barre d’état - - - - Open configuration dialog - Préférences - - - - Open Debug Console - Ouvrir la console de débogage - - - - Open CSS Console - Ouvrir la console CSS - - - - Cell text value editor - Éditeur de cellule - - - - - Cut selected text - Couper le texte sélectionné - - - - - Copy selected text - Copie du texte sélectionné - - - - - Delete selected text - Suppression du texte sélectionné - - - - - Undo - Annuler - - - - - Redo - Rétablir - - - - SQL editor input field - Éditeur SQL saisie de champ - - - - Select whole editor contents - Sélectionnez le contenu entier de l’éditeur - - - - Save contents into a file - Sauver le contenu dans un fichier - - - - Load contents from a file - Charger le contenu d’un fichier - - - - Find in text - Rechercher un texte - - - - Find next - Occurence suivante - - - - Find previous - Occurence précédente - - - - Replace in text - Remplacer dans le texte - - - - Delete current line - Supprimer la ligne courante - - - - Request code assistant - Assistant de code nécessaire - - - - Format contents - Format de contenu - - - - Move selected block of text one line down - Déplacer le bloc de texte sélectionné à la ligne inférieure - - - - Move selected block of text one line up - Déplacer le bloc de texte sélectionné à la ligne supérieure - - - - Copy selected block of text and paste it a line below - Copier le bloc de texte sélectionné à la ligne au-dessus - - - - Copy selected block of text and paste it a line above - Copier le bloc de texte sélectionné à la ligne au-dessous - - - - Toggle comment - - - - - All SQLite databases - Toutes les bases de données SQLite - - - - All files - Tous les fichiers - - - - - Database file - Fichier base de données - - - Reports history window - Fenêtre d’historique - - - Delete selected entry - Effacer l’entrée sélectionnée - - - - SQL editor window - Fenêtre de l’éditeur SQL - - - - Execute query - Exécution de la requête - - - - Execute "%1" query - Exécution de la requête %1 - - - - Switch current working database to previous on the list - Basculer de la base de données actuelle à la précédente de la liste - - - - Switch current working database to next on the list - Basculer de la base de données actuelle à la suivante de la liste - - - - Go to next editor tab - Aller à l’onglet d’éditeur suivant - - - - Go to previous editor tab - Aller à l’onglet d’éditeur précédent - - - - Move keyboard input focus to the results view below - Déplacement au-dessus du focus des résultats de vue par les touches - - - - Move keyboard input focus to the SQL editor above - Déplacement au-dessous du focus des résultats de vue par les touches - - - - Delete selected SQL history entries - - - - - Table window - Fenêtre de table - - - - Refresh table structure - Actualiser la structure de la table - - - - Add new column - Ajouter une nouvelle colonne - - - - Edit selected column - Modifier la colonne sélectionnée - - - - Delete selected column - Supprime la colonne sélectionnée - - - - Export table data - Exporte les données de table - - - - Import data to the table - Importe les données de table - - - - Add new table constraint - Ajoute une nouvelle contrainte à la table - - - - Edit selected table constraint - Modifie la contrainte de la table sélectionnée - - - - Delete selected table constraint - Supprime la contrainte de la table sélectionnée - - - - Refresh table index list - Actualise la liste des index de la table - - - - Add new index - Ajoute un nouvel index - - - - Edit selected index - Modifie l’index sélectionné - - - - Delete selected index - Supprime l’index sélectionné - - - - Refresh table trigger list - Actualise la liste des déclencheurs de la table - - - - - Add new trigger - Ajoute un nouveau déclencheur - - - - - Edit selected trigger - Modifie le déclencheur sélectionné - - - - - Delete selected trigger - Supprime le déclencheur sélectionné - - - - - Go to next tab - Aller à l’onglet suivant - - - - - Go to previous tab - Aller à l’onglet précédent - - - - A view window - Fenêtre de vue - - - - Refresh view trigger list - Actualise l’affichage de la liste des déclencheurs - - - - QuitConfirmDialog - - - Uncommitted changes - - - - - Are you sure you want to quit the application? - -Following items are pending: - Confirmez la fermeture de l’application ? - - - - SearchTextDialog - - - Find or replace - Chercher et remplacer - - - - Find: - Trouvé : - - - - Case sensitive - Sensible à la casse - - - - Search backwards - Recherches en arrière - - - - Regular expression matching - Correspondance d’expression régulière - - - - Replace && -find next - Remplace && -recherche suivant - - - - Replace with: - Remplacer par : - - - - Replace all - Remplacer tout - - - - Find - Rechercher - - - - SortDialog - - - Sort by columns - Tri par colonnes - - - - - Column - Colonne - - - - - Order - Ordre - - - - Sort by: %1 - Tri par %1 - - - - Move column up - Monter colonne - - - - Move column down - Descendre colonne - - - - SqlEditor - - - Cut - sql editor - Couper - - - - Copy - sql editor - Copier - - - - Paste - sql editor - Coller - - - - Delete - sql editor - Supprimer - - - - Select all - sql editor - Tout sélectionner - - - - Undo - sql editor - Annuler - - - - Redo - sql editor - Rétablir - - - - Complete - sql editor - Complet - - - - Format SQL - sql editor - Format SQL - - - - Save SQL to file - sql editor - Enregistrer le SQL - - - - Select file to save SQL - sql editor - - - - - Load SQL from file - sql editor - Charger le SQL - - - - Delete line - sql editor - Ligne suppimée - - - - Move block down - sql editor - Descendre le bloc - - - - Move block up - sql editor - Monter le bloc - - - - Copy block down - sql editor - Copier bloc au-dessus - - - - Copy up down - sql editor - Copier bloc au-dessous - - - - Find - sql editor - Chercher - - - - Find next - sql editor - Chercher suivant - - - - Find previous - sql editor - Chercher précédent - - - - Replace - sql editor - Remplacer - - - - Toggle comment - sql editor - - - - - Saved SQL contents to file: %1 - - - - - Syntax completion can be used only when a valid database is set for the SQL editor. - L’achèvement de syntaxe peut être utilisé seulement quand une base de données valable est utilisée dans l’éditeur SQL. - - - - Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. - Le contenu l’éditeur SQL est important, aussi la détectiond’objets en erreur est temporairement mise hors de service. - - - - Save to file - Sauvegarder - - - - Could not open file '%1' for writing: %2 - Impossible d’ouvrir en écriture le fichier « %1 » : %2 - - - - SQL scripts (*.sql);;All files (*) - Scripts SQL (*.sql);;Tous les fichiers (*) - - - - Open file - Fichier ouvert - - - - Could not open file '%1' for reading: %2 - Impossible d’ouvrir en lecture le fichier « %1 » : %2 - - - - Reached the end of document. Hit the find again to restart the search. - Fin de document atteint. Saississez de nouveau la recherche pour relancer la recherche. - - - - SqlQueryItem - - - Column: - data view tooltip - Colonne : - - - - Data type: - data view - Type de données : - - - - Table: - data view tooltip - Table : - - - - Constraints: - data view tooltip - Contrainte : - - - - Cannot load the data for a cell that refers to the already closed database. - Impossible de charger les données pour une cellule référant à une base de données fermée. - - - - SqlQueryItemDelegate - - - The row is marked for deletion. - La ligne est marquée pour effacement. - - - - - - - - Cannot edit this cell. Details: %1 - Impossible de modifier cette cellule. Détails : %1 - - - - - Structure of this table has changed since last data was loaded. Reload the data to proceed. - - - - - Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). - - - - - Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. - - - - - SqlQueryModel - - - - Only one query can be executed simultaneously. - Uniquement une seule requête peut être exécutée à la fois. - - - - Cannot commit the data for a cell that refers to the already closed database. - Impossible d’enregistrer les données pour la celle qui référe à une base de données déjà fermée. - - - - Could not begin transaction on the database. Details: %1 - Impossible de lancer la transaction sur la base de données. Détails : %1 - - - - An error occurred while rolling back the transaction: %1 - Une erreur est survenuelors de l’annulation de la transaction : %1 - - - - Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. - Tentative d’enregistrement d’une une cellule qui n’est pas modifiable ! Ceci est un bogue. Rapportez-le SVP. - - - - Uncommitted data - - - - - There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. - - - - - An error occurred while committing the transaction: %1 - - - - - An error occurred while committing the data: %1 - - - - - Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - - - - - - Error while executing SQL query on database '%1': %2 - Erreur pendant l’exécution de la requête sur la base de données « %1 » : %2 - - - - Error while loading query results: %1 - Erreur lors du chargement des résultats de la requête : %1 - - - - Insert multiple rows - Insérer plusieurs lignes - - - - Number of rows to insert: - Nombre de lignes à inserer : - - - - SqlQueryView - - - Go to referenced row in... - - - - - Copy - Copier - - - - Copy as... - Copier comme… - - - - Paste - Coller - - - - Paste as... - Coller comme… - - - - Set NULL values - Valeurs NULL positionnées - - - - Erase values - valeurs écrasées - - - - Edit value in editor - Valeur modifiée par l’éditeur - - - - Commit - Enregistrer - - - - Copy with headers - - - - - Rollback - Annuler - - - - Commit selected cells - Enregistrer les cellules sélectionnées - - - - Rollback selected cells - Annuler les modifications des cellules sélectionnées - - - - Define columns to sort by - Définit les colonnes triées par - - - - Remove custom sorting - Enléve le tri personnalisé - - - - Insert row - Insérer une ligne - - - - Insert multiple rows - Insérer plusieurs lignes - - - - Delete selected row - Supprimer les lignes sélectionnées - - - - Show value in a viewer - - - - - Generate query for selected cells - - - - - No items selected to paste clipboard contents to. - - - - - Go to referenced row in table '%1' - - - - - table '%1' - - - - - Referenced row (%1) - - - - - Trim pasted text? - - - - - The pasted text contains leading or trailing white space. Trim it automatically? - - - - - Edit value - Modifier la valeur - - - - SqlTableModel - - - Error while committing new row: %1 - - - - - Error while deleting row from table %1: %2 - Erreur à la suppression d’une ligne de la table %1 : %2 - - - - SqliteExtensionEditor - - - Filter extensions - - - - - Leave empty to use default function - - - - - Extension file - - - - - Initialization function - - - - - Databases - - - - - Register in all databases - - - - - Register in following databases: - - - - - Extension manager window has uncommitted modifications. - - - - - Extension manager - - - - - Commit all extension changes - - - - - Rollback all extension changes - - - - - Add new extension - - - - - Remove selected extension - - - - - Editing extensions manual - - - - - File with given path does not exist or is not readable. - - - - - Unable to load extension: %1 - - - - - Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - - - - - Dynamic link libraries (*.dll);;All files (*) - - - - - Shared objects (*.so);;All files (*) - - - - - Dynamic libraries (*.dylib);;All files (*) - - - - - All files (*) - Tous les fichiers(*) - - - - Open file - Fichier ouvert - - - - StatusField - - - Status - Barre d’état - - - - Copy - Copier - - - - Clear - Vider - - - - TableConstraintsModel - - - Type - table constraints - Type - - - - Details - table constraints - Détails - - - - Name - table constraints - Nom - - - - TableForeignKeyPanel - - - Foreign table: - Table étrangère : - - - - SQLite 2 does not support foreign keys officially, -but it's okay to use them anyway. - SQLite 2 ne supporte pas officielement les clefs étrangères, mais vous pouvez les utilisées tout de même. - - - - Columns - Colonnes - - - - Local column - Colonne locale - - - - Foreign column - Colonne étrangère - - - - Reactions - Réactions - - - - Deferred foreign key - - - - - Named constraint - Contrainte nommée - - - - Constraint name - Nom de la contrainte - - - - Pick the foreign column. - Choisir la colonne étrangère. - - - - Pick the foreign table. - Choisir la table étrangère. - - - - Select at least one foreign column. - Sélectionner au moins une colonne étrangère. - - - - Enter a name of the constraint. - Saisissez un nom de contrainte. - - - - Foreign column - table constraints - Colonne étrangère - - - - TablePrimaryKeyAndUniquePanel - - - Columns - Colonnes - - - - Column - Colonne - - - - Collation - Collation - - - - Sort - Tri - - - - Valid only for a single column with INTEGER data type - Valide seulement pour une simple colonne avecun type de données INTEGER - - - - Autoincrement - Auto-incrémentation - - - - Named constraint - Contrainte nommée - - - - Constraint name - Nom de la contrainte - - - - On conflict - Sur conflit - - - - Collate - table constraints - Collationne - - - - Sort order - table constraints - Ordre de tri - - - - Select at least one column. - Sélectionnez au moins une colonne. - - - - Enter a name of the constraint. - Saisissez le nom de la contrainte. - - - - TableStructureModel - - - Name - table structure columns - Nom - - - - Data type - table structure columns - Type de données - - - - Primary -Key - table structure columns - - - - - Foreign -Key - table structure columns - - - - - Unique - table structure columns - Unique - - - - Check - table structure columns - Contrôle - - - - Not -NULL - table structure columns - - - - - Collate - table structure columns - - - - - Default value - table structure columns - Valeur par défaut - - - - TableWindow - - - Structure - Structure - - - - Table name: - Nom de la table : - - - - - Data - Données - - - - Constraints - Contraintes - - - - Indexes - Index - - - - Triggers - Déclencheurs - - - - DDL - DDL - - - - Export table - table window - Exporter une table - - - - Import data to table - table window - Importer les données d’une table - - - - Populate table - table window - Peupler une table - - - - Refresh structure - table window - Actualiser la structure - - - - Commit structure changes - table window - Enregistrer les modifications de la structure - - - - Rollback structure changes - table window - Annuler les modifications de la structure - - - - Add column - table window - Ajouter une colonne - - - - Edit column - table window - Modifier une colonne - - - - - Delete column - table window - Supprimer une colonne - - - - Move column up - table window - Monter la colonne - - - - Move column down - table window - Descendre la colonne - - - - Create similar table - table window - Créer une table identique - - - - Reset autoincrement value - table window - Réinitialisation de l’incrémentation - - - - Add table constraint - table window - Ajouter une contrainte de table - - - - Edit table constraint - table window - Modifier la contrainte de table - - - - Delete table constraint - table window - Supprimer la contrainte de table - - - - Move table constraint up - table window - Monter la contrainte de table - - - - Move table constraint down - table window - Descendre la contrainte de table - - - - Add table primary key - table window - Ajouter une clef primaire à la table - - - - Add table foreign key - table window - Ajouter une clef étrangère à la table - - - - Add table unique constraint - table window - Ajouter une contrainte clef unique à la table - - - - Add table check constraint - table window - Ajouter une contrainte de contrôle à la table - - - - Refresh index list - table window - Actualiser la liste des index - - - - Create index - table window - Créer un index - - - - Edit index - table window - Modifier un index - - - - Delete index - table window - Supprimer un index - - - - Refresh trigger list - table window - Actualiser la liste des déclencheurs - - - - Create trigger - table window - Créer un déclencheur - - - - Edit trigger - table window - Modifier un déclencheur - - - - Delete trigger - table window - Supprimer un déclencheur - - - - Are you sure you want to delete column '%1'? - table window - Êtes-vous certain de vouloir supprimer la colonne : « %1 » ? - - - - Following problems will take place while modifying the table. -Would you like to proceed? - table window - Des problèmes suivants auront lieu en modifiant la table. -Voudriez-vous procéder ? - - - - Table modification - table window - Modification de la table - - - - Could not load data for table %1. Error details: %2 - Impossible de charger les données de table %1. Détails d’ erreur : %2 - - - - Could not process the %1 table correctly. Unable to open a table window. - Impossible de lancer correctement la table %1. Impossible d’ouvrir la fenêtre de table. - - - - Could not restore window %1, because no database or table was stored in session for this window. - - - - - Could not restore window '%1', because no database or table was stored in session for this window. - - - - - Could not restore window '%1', because database %2 could not be resolved. - - - - - Could not restore window '%1'', because the table %2 doesn't exist in the database %3. - - - - - Committed changes for table '%1' successfully. - - - - - Committed changes for table '%1' (named before '%2') successfully. - - - - - Autoincrement value for table '%1' has been reset successfully. - - - - - Uncommitted changes - - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - - - - - Table window "%1" has uncommitted structure modifications and data. - - - - - Table window "%1" has uncommitted data. - - - - - Table window "%1" has uncommitted structure modifications. - - - - - - New table %1 - Nouvelle table %1 - - - - Could not commit table structure. Error message: %1 - table window - Impossible d’enregistrer la structure de table. Message d’erreur : %1 - - - - Reset autoincrement - Réinitialisation de l’incrémentation - - - - Are you sure you want to reset autoincrement value for table '%1'? - Êtes-vous certain de vouloir réinitialiser l’auto-incrémentation de la table %1 ? - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - Une erreur est survenue pendant la réinitialisation de la valeur de l’auto-incrémentation de la table « %1 » : %2 - - - - Empty name - Nom vide - - - - A blank name for the table is allowed in SQLite, but it is not recommended. -Are you sure you want to create a table with blank name? - Un nom vide pour la vue dans SQLITE est admis, mais on ne le recommande pas. -Êtes-vous sûrs que vous voulez créer une vue avec le nom vide ? - - - - Cannot create a table without at least one column. - Impossible de créer une table sans au moins une colonne. - - - - Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. - Impossible de créer la table %1, s’il n’y a pas de clef primaire de définie. Toutefois ne pas contrôler %2 ou définir une clef primaire. - - - - Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. - Impossible d’utiliser l’auto-incrémentation pour une clef primaire quand la clause %1 est utilisée. Toutefois ne pas contrôler %2, ou utiliser l’auto-incrémentation sur une clef primaire. - - - - Are you sure you want to delete table constraint '%1'? - table window - Êtes-vous sûr de vouloir supprimer la contrainte « %1 » ? - - - - Delete constraint - table window - Supprimer la contrainte - - - - Cannot export, because no export plugin is loaded. - Export impossible, car aucun plugin d’import n’est chargé. - - - - Cannot import, because no import plugin is loaded. - Import impossible, car aucun plugin d’import n’est chargé. - - - - Go back to structure tab - Retour à l’onglet de structure - - - - Commit modifications and browse data. - Enregistrer les modifications et continuer - - - - Name - table window indexes - Nom - - - - Unique - table window indexes - Unique - - - - Columns - table window indexes - Colonnes - - - - Partial index condition - table window indexes - Condition partielle d’index - - - - Name - table window triggers - Nom - - - - Event - table window triggers - Événement - - - - Condition - table window triggers - Condition - - - - Details - table window triggers - Details - - - - TriggerColumnsDialog - - - Trigger columns - Colonnes de déclencheur - - - - Triggering columns: - Colonnes avec déclencheurs : - - - - Select all - Tout sélectionner - - - - Deselect all - Tout désélectionner - - - - TriggerDialog - - - - Trigger - Déclencheur - - - - On table: - Sur table : - - - - Action: - Action : - - - - - <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> - <p> La condition SQL sera évaluée avant le code du déclencheur réel. Dans le cas où le retour de condition est faux, le déclencheur ne sera pas utilisé pour cette ligne.</p > - - - - Pre-condition: - Précondition : - - - - The scope is still not fully supported by the SQLite database. - La portée n’est toujours pas entièrement supportée par la base de données SQLITE. - - - - Trigger name: - Nom du déclencheur : - - - - When: - Quand : - - - - List of columns for UPDATE OF action. - Liste des colonnes pour l’action UPDATE OF. - - - - Scope: - Portée : - - - - Code: - Code : - - - - Trigger statements to be executed. - Déclaration du déclencheur devant être exécutée. - - - - DDL - DDL - - - - On view: - Sur vue : - - - - Could not process trigger %1 correctly. Unable to open a trigger dialog. - Impossible d’exécuter correctement le déclencheur %1. Ouverture invalide du dialogue de déclencheur. - - - - Enter a valid condition. - Saisissez une condition valide. - - - - Enter a valid trigger code. - Saisissez un code de déclencheur valide. - - - - Error - trigger dialog - Erreur - - - - An error occurred while executing SQL statements: -%1 - Une erreur survenue lors de l’exécution de l’intruction SQL : %1 - - - - VersionConvertSummaryDialog - - - Database version convert - Version de convertion de la base de données - - - - Following changes to the SQL statements will be made: - Des modifications suivantes aux déclarations SQL seront faits : - - - - Before - Avant - - - - After - Après - - - - ViewWindow - - - Query - Requête - - - - View name: - Nom de la vue : - - - - Output column names - - - - - - Data - Données - - - - Triggers - Déclencheur - - - - DDL - DDL - - - - - Could not restore window '%1', because no database or view was stored in session for this window. - - - - - Could not restore window '%1', because database %2 could not be resolved. - - - - - Could not restore window '%1', because database %2 could not be open. - - - - - Could not restore window '%1', because the view %2 doesn't exist in the database %3. - - - - - - New view %1 - Nouvelle vue %1 - - - - Refresh the view - view window - Actualisation de la vue - - - - Commit the view changes - view window - Enregistrement des changements dans la vue - - - - Rollback the view changes - view window - Annulation des changements dans la vue - - - - Explicit column names - - - - - Generate output column names automatically basing on result columns of the view. - - - - - Add column - view window - Ajouter une colonne - - - - Edit column - view window - Modifier une colonne - - - - Delete column - view window - Supprimer une colonne - - - - Move column up - view window - - - - - Move column down - view window - - - - - Refresh trigger list - view window - Actualisation de la liste des déclencheurs - - - - Create new trigger - view window - Création d’un nouveau déclencheur - - - - Edit selected trigger - view window - Modification du déclencheur sélectionné - - - - Delete selected trigger - view window - Suppression du déclencheur sélectionné - - - - View window "%1" has uncommitted structure modifications and data. - - - - - View window "%1" has uncommitted data. - - - - - View window "%1" has uncommitted structure modifications. - - - - - Uncommitted changes - - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - - - - - Committed changes for view '%1' successfully. - - - - - Committed changes for view '%1' (named before '%2') successfully. - - - - - Could not load data for view %1. Error details: %2 - Impossible de charher les données de vue %1. Détails d’ erreur : %2 - - - - Go back to structure tab - Retour à l’onlet de structure - - - - Commit modifications and browse data. - Enregistrement des modifications et navigation des données. - - - - Could not commit view changes. Error message: %1 - view window - Impossible d’enregistrer les modifications de vue. Message d’erreur : %1 - - - - Override columns - - - - - Currently defined columns will be overriden. Do you want to continue? - - - - - Could not determinate columns returned from the view. The query is problably incomplete or contains errors. - - - - - Name - view window triggers - Nom - - - - Instead of - view window triggers - À la place de - - - - Condition - view window triggers - Condition - - - - Details - table window triggers - Détails - - - - Could not process the %1 view correctly. Unable to open a view window. - Impossible de lancer correctement la vue %1. Impossible d’ouvrir la fenêtre de vue. - - - - Empty name - Nom absent - - - - A blank name for the view is allowed in SQLite, but it is not recommended. -Are you sure you want to create a view with blank name? - Un nom vide pour la vue dans SQLITE est admis, mais on ne le recommande pas. -Êtes-vous sûrs que vous voulez créer une vue avec le nom vide ? - - - - The SELECT statement could not be parsed. Please correct the query and retry. -Details: %1 - La déclaration SELECT ne peut être analysé. Veuillez corriger la requête et réessayer. -Details : %1 - - - - The view could not be modified due to internal SQLiteStudio error. Please report this! - La vue ne peut être modifiée a cause d’une erreur interne de SQLiteStudio. SVP repportez l’erreur ! - - - - The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. - - - - - Following problems will take place while modifying the view. -Would you like to proceed? - view window - Des problèmes suivants auront lieu en modifiant la vue. -Veulez-vous continuer ? - - - - View modification - view window - Fenêtre vue - - - - WidgetCover - - - Interrupt - Interruption - - - diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr_FR.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr_FR.ts new file mode 100644 index 0000000..e52792a --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_fr_FR.ts @@ -0,0 +1,7106 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + À propos de SQLiteStudio et des licences + + + + About + À propos de… + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licences + + + + Environment + Environnement + + + + Icon directories + Répertoires des images + + + + Form directories + Répertoires des formulaires + + + + SQLite extension directories + Répertoire d'extensions SQLite + + + + Plugin directories + Répertoires des plugins + + + + Configuration directory + Répertoire de configuration + + + + Application directory + Répertoire de l’application + + + + Qt version: + Version Qt : + + + + SQLite 3 version: + Version de SQLite 3 : + + + + Portable distribution. + Version portable. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Distribution gérée par le système d'exploitation. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table des matières : </h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Paramètres de requête + + + + Please provide values for query parameters + Veuillez fournir des valeurs pour les paramètres de la requête + + + + CodeSnippetEditor + + + Filter snippets + Filtrer les extraits + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Raccourci facultatif, qui ne fonctionnera que dans le contexte de la fenêtre de l'assistant de code actif. Il permet à l'utilisateur d'utiliser des combinaisons de touches, qui autrement seraient en conflit avec d'autres raccourcis. Avoir la fenêtre d'assistant de code comme contexte requis rend le choix des touches plus polyvalent.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Nom de l'extrait + + + + Code assistant shortcut + Raccourci de l'assistant de code + + + + Snippet code + Extrait de code + + + + Code Snippets editor window has uncommitted modifications. + La fenêtre de l'éditeur d'extraits de code a des modifications invalidées. + + + + Code Snippets editor + Éditeur d'extraits de code + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Déplacer l'extrait vers le haut + + + + Move the snippet down + Déplacer l'extrait vers le bas + + + + Code snippets manual + Manuel d'extraits de code + + + + Enter a non-empty, unique name of the snippet. + Entrez un nom unique et non vide de l'extrait. + + + + Enter a non-empty snippet content. + Entrez un extrait de contenu non vide. + + + + This hotkey is not unique in context of a code assistant. + Ce raccourci n'est pas unique dans le contexte d'un assistant de code. + + + + CollationsEditor + + + Filter collations + Filtre de collation + + + + Databases + Base de données + + + + Register in all databases + Inscrire dans toutes les bases de données + + + + Register in following databases: + Inscrire dans les bases de données suivantes : + + + + Implementation code: + Code d’implémentation : + + + + Collation name: + Nom de collation : + + + + Implementation language: + Language d’implémentation : + + + + Collations editor + Éditeur de collation + + + + Commit all collation changes + Valider toutes les modifications de classement + + + + Rollback all collation changes + Annuler toutes les modifications de collation + + + + Create new collation + Création de collation + + + + Delete selected collation + Supprimer la collation sélectionnée + + + + Editing collations manual + Manuel pour l'édition de collations + + + + Enter a non-empty, unique name of the collation. + Saisissez un nom unique, non vide, de regroupement. + + + + Pick the implementation language. + Choisir le language d’implémentation. + + + + Enter a non-empty implementation code. + Saisissez un nom, non vide, de language d’implémentation. + + + + Collations editor window has uncommitted modifications. + L’éditeur de collations a des modifications non enregistrées. + + + + ColorButton + + + Pick a color + Choisir une couleur + + + + ColumnCollatePanel + + + Collation name: + Nom de la collation : + + + + Named constraint: + Contrainte nommée : + + + + Enter a name of the constraint. + Saisir le nom de la contrainte. + + + + Enter a collation name. + Saisir le nom de la collation. + + + + ColumnDefaultPanel + + + Default value: + Valeur par défaut : + + + + Named constraint: + Contrainte nommée : + + + + Enter a default value expression. + Saisissez l’expression d’une valeur par défaut. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Expression de valeur par défaut invalide : %1. Si vous souhaitez utiliser une chaîne de caractère simple comme valeur, n'oubliez pas de l'entourer de guillemets. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Expression de valeur par défaut invalide. Si vous voulez utiliser une chaîne de caractère simple comme valeur, n'oubliez pas de l'entourer de guillemets. + + + + Enter a name of the constraint. + Saisir un nom de contrainte. + + + + ColumnDialog + + + Column + Colonne + + + + Name and type + Nom et type + + + + Scale + Échelle + + + + Precision + Précision + + + + Data type: + Type de données : + + + + Column name: + Nom de colonne : + + + + Size: + Taille : + + + + Constraints + Contraintes + + + + Generated value + Valeur générée + + + + Unique + Unique + + + + + + + + + + + Configure + Configurer + + + + Foreign Key + Clef étrangère + + + + Collate + Collation + + + + Not NULL + Non NULL + + + + Check condition + Vérifier la condition + + + + Primary Key + Clef primaire + + + + Default + Défaut + + + + Advanced mode + Mode avancé + + + + Add constraint + column dialog + Ajouter une contrainte + + + + Edit constraint + column dialog + Editer la contrainte + + + + + Delete constraint + column dialog + Supprimer la contrainte + + + + Move constraint up + column dialog + Monter la contrainte + + + + Move constraint down + column dialog + Descendre la contrainte + + + + Add a primary key + column dialog + Ajouter une clef primaire + + + + Add a foreign key + column dialog + Ajouter une clef étrangère + + + + Add an unique constraint + column dialog + Ajouter une contrainte d'unicité + + + + Add a check constraint + column dialog + Ajouter une contrainte de contrôle + + + + Add a not null constraint + column dialog + Ajouter une contrainte non NULL + + + + Add a collate constraint + column dialog + Ajouter un commentaire à la contrainte + + + + Add a generated value constraint + column dialog + Ajouter une contrainte de valeur générée + + + + Add a default constraint + column dialog + Ajouter une contrainte par défaut + + + + Are you sure you want to delete constraint '%1'? + column dialog + Êtes-vous sûr de vouloir supprimer la contrainte « %1 » ? + + + + Correct the constraint's configuration. + Corrigez la configuration de la contrainte. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + L'échelle n'est pas autorisée pour les colonnes CLÉ PRIMAIRE DE NOMBRE ENTIER. + + + + Precision cannot be defined without the scale. + La précision ne peut pas être définie sans l'échelle. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Impossible d'utiliser un autre type que NOMBRE ENTIER si AUTOINCRÉMENTATION est activé dans la CLÉ PRIMAIRE. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + Le type NOMBRE ENTIER a été appliqué en raison de l'activation de AUTOINCRÉMENTATION dans la CLÉ PRIMAIRE. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + La précision n'est pas autorisée pour les colonnes CLÉ PRIMAIRE DE NOMBRE ENTIER. + + + + Could not match valid STRICT table datatype from declared type: %1. + Impossible de faire correspondre le type de données, tableau STRICT valide du type déclaré : %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Nom + + + + Details + column dialog constraints + Détails + + + + ColumnForeignKeyPanel + + + Foreign table: + Table étrangère : + + + + Foreign column: + Colonne étrangère : + + + + Reactions + Réactions + + + + Deferred foreign key + Clef étrangère refusée + + + + Named constraint + Contrainte nommée + + + + Constraint name + Nom de contrainte + + + + Pick the foreign table. + Sélectionner la table étrangère. + + + + Pick the foreign column. + Sélectionner la colonne étrangère. + + + + Enter a name of the constraint. + Saisir un nom de contraite. + + + + ColumnGeneratedPanel + + + Generating code: + Génération du code : + + + + Explicit type: + Type explicite : + + + + Use "GENERATED ALWAYS" keywords + Utilisez des mots-clés "TOUOURS GÉNÉRÉ" + + + + Named constraint: + Contrainte nommée : + + + + Enter the column value generating expression. + Entrez l'expression de génération de la valeur de la colonne. + + + + Invalid value generating expression: %1. + Expression de génération de valeur non valide : %1. + + + + Invalid value generating expression. + Expression de génération de valeur invalide. + + + + Enter a name of the constraint. + Écrivez le nom de la contrainte. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Auto-incrémentation + + + + Sort order: + Ordre de tri : + + + + Named constraint: + Contrainte nommée : + + + + On conflict: + En cas de conflit : + + + + Enter a name of the constraint. + Saisissez le nom d’une contrainte. + + + + Descending order is not allowed with AUTOINCREMENT. + Ordre décroissant n'est pas autorisé avec AUTOINCRÉMENTATION. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Contrainte nommée : + + + + On conflict: + En cas de conflit : + + + + Enter a name of the constraint. + Saisissez un nom de contrainte. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Colonne : %1 + + + + Table: %1 + completer statusbar + Table : %1 + + + + Index: %1 + completer statusbar + Index : %1 + + + + Trigger: %1 + completer statusbar + Déclencheur : %1 + + + + View: %1 + completer statusbar + Vue : %1 + + + + Database: %1 + completer statusbar + Base de données : %1 + + + + Keyword: %1 + completer statusbar + Mot-clef : %1 + + + + Function: %1 + completer statusbar + Fonction : %1 + + + + Operator: %1 + completer statusbar + Opérateur : %1 + + + + String + completer statusbar + Chaîne de caractères + + + + Number + completer statusbar + Nombre + + + + Binary data + completer statusbar + Données binaires + + + + Collation: %1 + completer statusbar + Collation : %1 + + + + Pragma function: %1 + completer statusbar + Fonction Pragma : %1 + + + + Insert a code snippet + Insérer un extrait de code + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Recherche + + + + General + Général + + + + Keyboard shortcuts + Raccourcis clavier + + + + Look & feel + Apparence + + + + Style + Style + + + + Fonts + Polices + + + + Code colors + Couleurs du code + + + + + Database list + Liste de base de données + + + + Code assistant + Assistant de code + + + + Data browsing + Navigation de données + + + + Data editors + Éditeurs de données + + + + Plugins + Plugins + + + + Code formatters + Formateurs de code + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + Sur off, les colonnes seront triées dans l’ordre de saisie de l’instruction CREATE TABLE. + + + + Sort table columns alphabetically + Ordre de tri alpha de la colonne + + + + Expand tables node when connected to a database + Développer le nÅ“ud des tables lors de la connexion de la base de données + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Les labels supplémentaires sont ceux montrés à côté des noms dans la liste de bases de données ( bleus,sauf autre configaration). Permettre cette option aboutira aux lablels pour des bases de données, des bases de données invalides et des noeuds (colonnes, index, déclancheur). Pour plus de labels voir des options ci-dessous.<p> + + + + Display additional labels on the list + Afficher des labels supplémentaires dans la liste + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + Pour des tables courantes les labels montrerons le nombre der colonnes, index et déclencheurs pour chaque tables. + + + + Display labels for regular tables + Afficher les labels pour les tables courantes + + + + Virtual tables will be marked with a 'virtual' label. + Les tables vituelles seront marquées avec un label « virtuel ». + + + + Display labels for virtual tables + Afficher les labels pour les tables virtuelles + + + + Expand views node when connected to a database + Développer le nÅ“ud des vues lorsque la base de données est connectée + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + Si cette option est désactivée, les objets seront triés pour qu’ ils apparaissent dans la table sqlite_master (dans l’ordre de création) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Tri d’objets (tables, index, déclancheurs et vues) en alpha + + + + Display system tables and indexes on the list + Afficher les tables système et index dans la liste + + + + Database dialog window + Fenêtre de dialogue de la base de données + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Ne pas marquer la base de données comme étant "permanente" par défaut + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Essayez de contourner complètement la boîte de dialogue lors du dépôt du fichier de la base de données dans la liste + + + + Data browsing and editing + Navigateur et éditeur de données + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Nombre maximum de configurations de Tableau de Populations de boîte de dialogue stockées dans la configuration. La valeur 100 doit être suffisante.</p> + + + + Number of memorized table populating configurations + Nombre de tableaux mémorisés remplissant les configurations + + + + Data column width + Largeur de colonne de données + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Agrandir la colonne en entrant une valeur plus longue que la largeur actuelle + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>Lorsque les données sont lues dans le tableau, la largeur est automatiquement ajustée. Cette valeur limite la largeur initiale pour l’ajustement, mais l’utilisateur peut recadrer les colonnes manuellement au-dessus de cette limite.</p> + + + + Number of data rows per page: + Nombre de lignes de données par page : + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Afficher l'infobulle des détails des colonnes et des lignes dans la vue des données + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Conserver la valeur NULL en entrant une valeur vide + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Utiliser la valeur DEFAULT (si définie), lors de la validation de la valeur NULL + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limiter le nombre de lignes en cas de dizaines de colonnes + + + + Inserting new row in data grid + Insertion d'une nouvelle ligne dans la grille de données + + + + Before currently selected row + Avant la ligne courante + + + + After currently selected row + Après la ligne courante + + + + At the end of data view + À la fin de la vue de données + + + + Table windows + Fenêtres de tables + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>Lorsque cette option est activée, les Fenêtres des Tableaux s'afficheront avec l'onglet de données, au lieu de l'onglet structure.</p> + + + + Open Table Windows with the data tab for start + Sélectionner l'onglet de données lors de l'ouverture d'une fenêtre de table + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>Lorsque cette option est activée, l'onglet "Données" sera placé en tant que premier onglet dans chaque Fenêtre de Tableaux, au lieu d'être à la deuxième place.</p> + + + + Place data tab as first tab in a Table Window + Placer l'onglet Données en premier dans les fenêtres de tables + + + + View windows + Fenêtre de vue + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>Lorsque cette option est activée, les Fenêtres de Vue s'afficheront avec l'onglet de données, au lieu de l'onglet structure.</p> + + + + Open View Windows with the data tab for start + Sélectionner l'onglet de données lors de l'ouverture d'une fenêtre de vue + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>Lorsque cette option est activée, l'onglet "Données" sera placé en tant que premier onglet dans chaque Fenêtre de Vue, au lieu d'être à la deuxième place.</p> + + + + Place data tab as first tab in a View Window + Placer l'onglet de données comme premier onglet dans une fenêtre de vue + + + + Data types + Types de données + + + + Available editors: + Éditeurs disponibles : + + + + Editors selected for this data type: + Éditeur sélectionné pour ce type de données : + + + + Schema editing + Edition de schéma + + + + Number of DDL changes kept in history. + Nombre de DDL modifiés gardés dans l’historique. + + + + DDL history size: + Dimension de l’historique DDL : + + + + Don't show DDL preview dialog when committing schema changes + Cacher la boîte de dialogue d'aperçu des DDL lors de la validation des modifications de schéma + + + + SQL queries + Requêtes SQL + + + + + Number of queries kept in the history. + Nombre de requêtes gardées dans l’historique. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>S'il y a plus d'une requête dans la fenêtre de l'éditeur SQL, alors (si cette option est activée), seulement une requête sera exécutée - celle sous le curseur d'insertion du clavier. Sinon, toutes les requêtes seront exécutées. Vous pouvez toujours limiter les requêtes à exécuter en sélectionnant ces requêtes avant d'appeler à l'exécution. Vous pouvez également utiliser des raccourcis dédiés pour l'exécution dans un mode ou dans l'autre (actuellement configuré à %1 pour l'exécution d'une requête unique et %2 pour l'exécution de toutes les requêtes).</p></body></html> + + + + History size: + Dimension de l’historique : + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Exécuter seulement la requête sous le curseur + + + + Number of memorized query parameters + Nombre de paramètres de requête mémorisés + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Utiliser la notation scientifique pour les nombres réels dans la vue de grille + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limiter la largeur de la colonne de données automatique à (en pixels) : + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Garder au moins la largeur pour afficher le nom complet de la colonne + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Envelopper les lignes dans l'éditeur SQL + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Surligne la requête entière qui est actuellement sous le curseur d'insertion. Ce sera la même requête qui sera exécutée lorsque vous appuierez sur &quot;Exécuter la requête&quot; raccourci ou le bouton (sauf si configuré autrement).</p></body></html> + + + + Highlight current query + Surligner la requête actuelle + + + + Updates + Mises à jour + + + + Automatically check for updates at startup + Contrôle automatique des mises à jour au lancement + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restaurer la dernière session (Fenêtre MDI active) après lancement + + + + Allow multiple instances of the application at the same time + Autoriser plusieurs instances de l'application en même temps + + + + Status Field + Champ de statut + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Toujours ouvrir le panneau de Statut quand un nouveau message est écrit + + + + Code syntax colors + Couleurs de syntaxe du code + + + + Keyword foreground + Premier plan du mot-clé + + + + Regular foreground + Premier plan par défaut + + + + String foreground + Premier plan de la chaîne de caractère + + + + Comment foreground + Premier plan du commentaire + + + + Valid objects foreground + Premier plan des objets valides + + + + Current query background + Arrière-plan de la requête actuelle + + + + Bind parameter foreground + Lier le paramètre au premier plan + + + + Current line background + Arrière-plan de la ligne actuelle + + + + Matched parenthesis background + Arrière-plan de parenthèse correspondante + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>Vous pouvez désactiver le surlignage de la requête actuelle sur la page des paramètres généraux.</p></body></html> + + + + Number foreground + Premier plan du nombre + + + + BLOB value foreground + Valeur du BLOB au premier plan + + + + Matched parenthesis foreground + Premier plan de parenthèse correspondante + + + + Reset to defaults + Rétablir les valeurs par défaut + + + + Filter shortcuts by name or key combination + Filtre par nom raccourci ou combinaison de touches + + + + Action + Action + + + + Key combination + Combinaison de touches + + + + + Language + Langage + + + + Changing language requires application restart to take effect. + Le changement de langage requiert le redémarrage de l’application pour prendre effet. + + + + Compact layout + Présentation compacte + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Utiliser la présentation compacte + + + + Main window dock areas + Zones de dock de la fenêtre principale + + + + Left and right areas occupy corners + Les zones de gauche et de droite occupent des coins + + + + Top and bottom areas occupy corners + Les zones supérieures et inférieures occupent des coins + + + + Hide built-in plugins + Cacher des plugins incorporés + + + + Current style: + Style actuel : + + + + Preview + Aperçu + + + + Enabled + En service + + + + Disabled + Hors service + + + + Active formatter plugin + Plugin de formattage actif + + + + SQL editor font + Police de caractères de l’éditeur SQL + + + + Database list font + Liste des polices de caractères de base de données + + + + Database list additional label font + Police de caractères additionelle de la liste des bases de données + + + + Data view font + Police de caractères des données de vue + + + + Status field font + Police de caractères du champ d’état + + + + Code assistant settings + Paramètres de l'assistant de code + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>Si cette option est activée, l'assistant de code sera déclenché dans certains cas, par exemple lorsque l'utilisateur écris <span style=" font-weight:700;">NomdeTableau.</span> pour proposer certaines colonnes du tableau. Si l'option est désactivée, l'utilisateur devra appuyer explicitement sur la touche de raccourci assistant.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Déclenche automatiquement l'assistant après qu'un point soit écris après le nom d'un objet + + + + Description: + plugin details + Description : + + + + Category: + plugin details + Catégorie : + + + + Version: + plugin details + Version : + + + + Author: + plugin details + Auteur : + + + + Internal name: + plugin details + Nom interne : + + + + Dependencies: + plugin details + Dépendances : + + + + Conflicts: + plugin details + Conflits : + + + + Plugin details + Détails du plugin + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Les plugins sont chargés/déchargés immédiatement avec vérifié/non vérifié, mais les modifications de la liste de plugins à charger au lancement ne sont pas enregistrées avant l’enregistrement de la configuration entière. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (intégré) + + + + Details + Détails + + + + No plugins in this category. + Pas de plugins dans cette catégorie. + + + + Add new data type + Ajouter un nouveau type de données + + + + Rename selected data type + Renommer le type de données sélectionné + + + + Delete selected data type + Supprimer le type de données sélectionnées + + + + Help for configuring data type editors + Aide à la configuration des éditeurs de type de données + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + Condition + + + + Named constraint: + Contrainte nommée : + + + + On conflict + Sur conflit + + + + Enter a valid condition. + Saississez une condition valide. + + + + Enter a name of the constraint. + Saississez un nom de contrainte valide. + + + + ConstraintDialog + + + New constraint + constraint dialog + Nouvelle contrainte + + + + Create + constraint dialog + Créer + + + + Edit constraint + dialog window + Modifier la contrainte + + + + Apply + constraint dialog + Appliquer + + + + Primary key + table constraints + Clef primaire + + + + Foreign key + table constraints + Clef étrangère + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Non NULL + + + + Check + table constraints + Contrôle + + + + Generated + table constraints + Généré + + + + Collate + table constraints + Regroupe + + + + Default + table constraints + Défaut + + + + ConstraintTabModel + + + Table + table constraints + Tableau + + + + Column (%1) + table constraints + Colonne (%1) + + + + Scope + table constraints + Portée + + + + Type + table constraints + Type + + + + Details + table constraints + Détails + + + + Name + table constraints + Nom + + + + CssDebugDialog + + + SQLiteStudio CSS console + Console CSS SQLiteStudio + + + + DataView + + + Filter data + data view + Filtre de données + + + + Grid view + Table + + + + Form view + Formulaire + + + + Refresh table data + data view + Actualiser les données de la table + + + + First page + data view + Première page + + + + Previous page + data view + Page précédente + + + + Next page + data view + Page suivante + + + + Last page + data view + Dernière page + + + + Commit changes for selected cells + data view + Valider les modifications pour les cellules sélectionnées + + + + Rollback changes for selected cells + data view + Annuler les modifications des celulles sélectionnées + + + + Show grid view of results + data view + Affichage des résultats en tableau + + + + Show form view of results + data view + Affichage des résultat en formulaire + + + + Filter by text (if contains) + data view + Filtrer par texte (si contenu) + + + + Filter strictly by text (if equals) + data view + Filtrer strictement par texte (si égal) + + + + Tabs on top + data view + Onglets en haut + + + + Tabs at bottom + data view + Onglet en bas + + + + Place new rows above selected row + data view + Placer les nouvelles lignes au-dessus de la ligne sélectionnée + + + + Place new rows below selected row + data view + Placer les nouvelles lignes sous la ligne sélectionnée + + + + Place new rows at the end of the data view + data view + Placer les nouvelles lignes à la fin de la vue des données + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Le total des lignes en cours de comptage. La navigation d’autres pages à la fin du comptage. + + + + Row: %1 + Lignes : %1 + + + + Filter + Filtre + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Appuyez sur la touche Entrée ou appuyez sur "Appliquer le filtre" dans la barre d'outils pour appliquer une nouvelle valeur. + + + + Filter by the Regular Expression + data view + Filtrer par une expression standard + + + + Filter by SQL expression + data view + Filtrer par une expression SQL + + + + Show filter inputs per column + data view + Afficher les entrées de filtre par colonne + + + + Apply filter + data view + Appliquer le filtre + + + + DbDialog + + + Database + Base de données + + + + Database type + Type de base de données + + + + Database driver + Pilote de base de données + + + + + File + Fichier + + + + Name (on the list) + Nom (dans la liste) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Autorisez-ceci si vous voulez que la base de données soit stockée dans le fichier de configuration et restauré chaque fois SQLiteStudio est lancé.</p> + + + + Permanent (keep it in configuration) + Permanent (conserver dans la configuration) + + + + Test connection + Tester la connexion + + + + Select new or existing file on local computer + Sélectionnez un nouveau ou un existant fichier sur l'ordinateur local + + + + Browse + Navigateur + + + + Database type not selected. + Type de base de données non sélectionné. + + + + Database path not specified. + Chemin de la base de données non spécifié. + + + + Enter an unique database name. + Entrez un nom de base de données unique. + + + + This name is already in use. Please enter unique name. + Ce nom est déjà utilisé. Veuillez entrer un nom unique. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>La génération automatique de noms a été désactivée, car le nom a été modifié manuellement. Pour restaurer la génération automatique, veuillez effacer le nom dans le champ correspondant.</p> + + + + Enter a database file path. + Entrez le chemin du fichier de la base de données. + + + + This database is already on the list under name: %1 + Cette base de données est déjà dans la liste sous le nom de : %1 + + + + Select a database type. + Sélectionnez un type de base de données. + + + + DbObjectDialogs + + + Delete table + Table supprimée + + + + Are you sure you want to delete table %1? + Confirmez la suppression de la table %1 ? + + + + Delete index + Index supprimé + + + + Are you sure you want to delete index %1? + Confirmez la suppression de l’index %1 ? + + + + Delete trigger + Déclencheur supprimé + + + + Are you sure you want to delete trigger %1? + Confirmez la suppression du déclencheur %1 ? + + + + Delete view + Vue supprimée + + + + Are you sure you want to delete view %1? + Confirmez la suppression de la vue %1 ? + + + + + Error while dropping %1: %2 + Erreur à l’abandon %1 : %2 + + + + Delete objects + Objets supprimés + + + + Are you sure you want to delete following objects: +%1 + Êtes-vous sûr de vouloir supprimer les objets suivants : +%1 + + + + Cannot start transaction. Details: %1 + Impossible de démarrer la transaction. Détails : %1 + + + + Cannot commit transaction. Details: %1 + Impossible de valider la transaction. Détails : %1 + + + + DbTree + + + Databases + Base de données + + + + Filter by name + Filtre par nom + + + + Copy + Copier + + + + Paste + Coller + + + + Select all + Tout sélectionner + + + + Create a group + Créer un groupe + + + + Delete the group + Supprimer le groupe + + + + Rename the group + Renommer le groupe + + + + &Add a database + &Ajouter une base de données + + + + &Edit the database + &Éditer la base de données + + + + &Remove the database + &Supprimer la base de données + + + + &Connect to the database + &Se connecter à la base de données + + + + &Disconnect from the database + &Se déconnecter de la base de données + + + + Import + Importer + + + + &Export the database + &Exporter la base de données + + + + Vac&uum + Vi&der + + + + &Integrity check + &Contrôle d'intégrité + + + + Create a &table + Créer un &tableau + + + + Edit the t&able + Éditer le t&ableau + + + + Delete the ta&ble + Supprimer le ta&bleau + + + + Export the table + Exporter la table + + + + Import into the table + Importer dans la table + + + + Populate table + Peupler une table + + + + Create similar table + Créer une table identique + + + + Reset autoincrement sequence + Réinitialise l’auto-incrémentation + + + + Create an &index + Créer un &indice + + + + Edit the i&ndex + Modifier l'i&ndice + + + + Delete the in&dex + Supprimer l'in&dex + + + + Create a trig&ger + Créer un décl&encheur + + + + Edit the trigg&er + Éditer le décl&encheur + + + + Delete the trigge&r + Supprimer le déclencheu&r + + + + Create a &view + Créer une &vue + + + + Edit the v&iew + Editer la v&ue + + + + Delete the vi&ew + Supprimer la vu&e + + + + Add a column + Ajouter une colonne + + + + Edit the column + Modifier la colonne + + + + Delete the column + Supprimer la colonne + + + + Delete selected items + Supprimer les objets sélectionnés + + + + Clear filter + Vider le filtre + + + + &Refresh all database schemas + &Rafraîchir tous les schémas de base de données + + + + Re&fresh selected database schema + Ra&fraîchir le schéma de la base de données sélectionnée + + + + + Erase table data + Effacer les données du tableau + + + + Open file's directory + Ouvrir le répertoire du fichier + + + + Execute SQL from file + Exécuter SQL depuis un fichier + + + + Increase font size + database list + Augmenter la taille de la police de caractère + + + + Decrease font size + database list + Réduire la taille de la police de caractère + + + + + Database + Base de données + + + + Grouping + Groupement + + + + Generate query for table + Générer une requête pour le tableau + + + + + Create group + Créer un groupe + + + + Group name + Nom du groupe + + + + Entry with name %1 already exists in group %2. + L’entrée nommée %1 existe déjà dans le groupe %2. + + + + Delete group + Supprimer le groupe + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Êtes-vous certain de supprimer le groupe %1 ? +Tous les objets de ce groupe seront déplacés dans le groupe parent. + + + + Are you sure you want to remove database '%1' from the list? + Êtes-vous sûr de vouloir supprimer la base de données '%1' de la liste ? + + + + Are you sure you want to remove following databases from the list: +%1 + Êtes-vous sûr de vouloir supprimer les bases de données suivantes de la liste : +%1 + + + + Remove database + Supprimer la base de données + + + + + Cannot import, because no import plugin is loaded. + Import impossible, car aucun plugin d’import n’est chargé. + + + + + Cannot export, because no export plugin is loaded. + Export impossible, car aucun plugin d’import n’est chargé. + + + + Vacuum (%1) + Vider (%1) + + + + Integrity check (%1) + Contrôle d’intégrité (%1) + + + + Reset autoincrement + Remise à zéro de l’auto-incrément + + + + Are you sure you want to reset autoincrement value for table '%1'? + Êtes-vous certain de vouloir réinitialiser l’auto-incrémentation de la table « %1 » ? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + Une erreur est survenue pendant la réinitialisation de la valeur de l’auto-incrémentation de la table « %1 » : %2 + + + + Autoincrement value for table '%1' has been reset successfully. + La valeur d'auto-incrémentation pour le tableau '%1' a été réinitialisée avec succès. + + + + Are you sure you want to delete all data from table(s): %1? + Êtes-vous sûr de vouloir supprimer toutes les données de(s) tableau(x) : %1? + + + + An error occurred while trying to delete data from table '%1': %2 + Une erreur est survenue lors de la suppression de données du tableau '%1': %2 + + + + All data has been deleted for table '%1'. + Toutes les données ont été supprimées pour le tableau '%1'. + + + + Following objects will be deleted: %1. + Les objets suivant vont être supprimés : %1. + + + + Following databases will be removed from list: %1. + Les bases de données suivantes seront enlevées de la liste : %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Les objets restants du groupe supprimé seront déplacés où le groupe a eu l’habitude d’être. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Êtes-vous certain de vouloir continuer ? + + + + Delete objects + Objets supprimés + + + + DbTreeItemDelegate + + + error + dbtree labels + erreur + + + + (system table) + database tree label + (Table système) + + + + (virtual) + virtual table label + (virtuel) + + + + (system index) + database tree label + (index système) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Base de données : %1 + + + + URI: + dbtree tooltip + URI : + + + + Version: + dbtree tooltip + Version : + + + + File size: + dbtree tooltip + Taille fichier : + + + + Encoding: + dbtree tooltip + Codage : + + + + Error: + dbtree tooltip + Erreur : + + + + Table : %1 + dbtree tooltip + Tableau : %1 + + + + Columns (%1): + dbtree tooltip + Colonnes (%1): + + + + Indexes (%1): + dbtree tooltip + Indexs (%1) : + + + + Triggers (%1): + dbtree tooltip + Déclencheurs (%1) : + + + + Copy + Copier + + + + Move + Déplacer + + + + Include data + Données incluses + + + + Include indexes + Index inclus + + + + Include triggers + Déclencheurs inclus + + + + Abort + Abandonner + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Impossible d'ajouter le fichier de base de données abandonné '%1' automatiquement. La configuration manuelle est nécessaire. + + + + Referenced tables + Tables référencées + + + + Do you want to include following referenced tables as well: +%1 + Vous voulez inclure des tables référencées suivantes aussi : +%1 + + + + Name conflict + Conflit de nom + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + L’objet suivant existe déjà dans la base de données cible. +Entrez SVP un nouveau nom, unique, ou cliquez « %1 » pour d’interrompre l’opération : + + + + SQL statements conversion + Conversion des déclarations SQL + + + + Following error occurred while converting SQL statements to the target SQLite version: + L’erreur suivante est survenue en convertissant des déclarations de SQL de la version cible SQLite : + + + + Would you like to ignore those errors and proceed? + Voulez-vous ignorer ces erreurs et procéder ? + + + + DdlHistoryWindow + + + Filter by database: + Filtre par base de données : + + + + Clear entire history + Effacer tout l'historique + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Requête éxécutée sur la base de données %1 (%2) +-- Date et heure d’exécution : %3 +%4 + + + + Clear history + Effacer l'historique + + + + Are you sure you want to erase entire DDL history? + Êtes-vous sûr de vouloir effacer l'historique complet des DDL ? + + + + DDL history + Historique DDL + + + + DdlPreviewDialog + + + Queries to be executed + Requêtes à exécuter + + + + Don't show again + Ne plus afficher + + + + DebugConsole + + + SQLiteStudio Debug Console + Console SQLiteStudio de débogage + + + + EditorWindow + + + SQL editor + Éditeur SQL + + + + Query + Requête + + + + History + Historique + + + + Results in the separate tab + Résultats dans un onglet séparé + + + + Results below the query + Résultats après la requête + + + + + SQL editor %1 + Éditeur SQL %1 + + + + + Results + Résultats + + + + Execute query + Exécuter la requête + + + + Explain query + Explication de la requête + + + + Clear execution history + sql editor + Vider l’historique d’exécution + + + + Export results + sql editor + Exporter résultats + + + + Create view from query + sql editor + Créer une vue à partir d’une requête + + + + Previous database + Base de données précédente + + + + Next database + Base de données suivante + + + + Show next tab + sql editor + Afficher l’onglet suivant + + + + Show previous tab + sql editor + Afficher l’onget précédent + + + + Focus results below + sql editor + Focus sur les résultats ci-dessous + + + + Focus SQL editor above + sql editor + Focus sur l’éditeur SQL ci-dessus + + + + Delete selected SQL history entries + sql editor + Supprimer les entrées de l'historique SQL sélectionnées + + + + Execute single query under cursor + Exécuter une requête sous le curseur + + + + Execute all queries in editor + Exécuter toutes les requêtes dans l'éditeur + + + + Active database (%1/%2) + Base de données active (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Requête terminée en %1 secondes. Nombre de lignes : %2 + + + + Query finished in %1 second(s). + Requête terminée en %1 seconde(s). + + + + Clear execution history + Supprimer l’historique d’exécution + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Êtes vous certain de vouloir supprimer la totalité de l’historique d’exécution SQL ? Aucun retour possible. + + + + Cannot export, because no export plugin is loaded. + Impossible d’exporter, car aucun plugin d’expertation n’est chargés. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + Aucune base de données den sélectionnée dans l’éditeur SQL. Impossible de créer une vue sur une base de données inconnue. + + + + Editor window "%1" has uncommitted data. + La fenêtre de l'éditeur "%1" a des données invalidées. + + + + ErrorsConfirmDialog + + + Errors + Erreurs + + + + Following errors occured: + Les erreurs suivantes sont arrivées : + + + + Would you like to proceed? + Désirez-vous traiter ? + + + + ExecFromFileDialog + + + Execute SQL from file + Exécuter SQL depuis un fichier + + + + Input file + Fichier de saisie + + + + Path to file + Chemin vers le fichier + + + + Browse for file + Parcourir les fichiers + + + + Options + Options + + + + File encoding + Encodage du fichier + + + + Skip failing SQL statements + Ignorer les requêtes SQL échouées + + + + SQL scripts (*.sql);;All files (*) + Scripts SQL (*.sql);;Tous les fichiers (*) + + + + Execute SQL file + Exécuter le fichier SQL + + + + Please provide file to be executed. + Veuillez fournir le fichier à exécuter. + + + + Provided file does not exist or cannot be read. + Le fichier fourni n'existe pas ou ne peut pas être lu. + + + + ExportDialog + + + Export + Exporter + + + + What do you want to export? + Que voulez-vous exporter ? + + + + A database + Une base de données + + + + A single table + Une table + + + + Query results + Résultats d’une requête + + + + Table to export + Table à exporter + + + + Database + Base de données + + + + Table + Tableau + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + Lorsque cette option n’est contrôlée, alors seulement le DDL de la table (CREATE TABLE...) est exporté. + + + + Export table data + Exporter les données de la table + + + + Export table indexes + Exporter les index de la table + + + + Export table triggers + Exporter les déclencheurs de la table + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + NB : les index de la table d’exportation et les déclencheurs peuvent ne pas être pris en charge par certains formats d’export. + + + + Select database objects to export + Sélectionnez les objets de la base de données à exporter + + + + Export data from tables + Exporter les données des tables + + + + Select all + Tout sélectionner + + + + Deselect all + Tout désélectionner + + + + + Database: + Base de données : + + + + Query to export results for + Résultats de la requête + + + + Query to be executed for results: + Requête à exécuter : + + + + Export format and options + Exporter formatset options + + + + Export format + Format d’exportation + + + + Output + Sortie + + + + Exported file path + Chemin du fichier d’exportation + + + + Clipboard + presse-papier + + + + File + Fichier + + + + Exported text encoding: + Texte encodé exporté : + + + + Export format options + Exporter options de format + + + + Cancel + Annuler + + + + + + Select database to export. + Sélecctionnez la base de données à exporter. + + + + Select table to export. + Sélectionnez la table à exporter. + + + + Enter valid query to export. + Saississez une requête valide à exporter. + + + + Select at least one object to export. + Sélectionnez au moins un objet à exporter. + + + + You must provide a file name to export to. + Vous devez fournir le nom d’un fichier à exporter. + + + + Path you provided is an existing directory. You cannot overwrite it. + Le chemin fourni est un répertoire existant. Vous ne pouvez pas l’écraser. + + + + The directory '%1' does not exist. + Le répertoire « %1 » n’existe pas. + + + + The file '%1' exists and will be overwritten. + Le fichier « %1 » existe et sera écrasé. + + + + All files (*) + Tous les fichiers(*) + + + + Pick file to export to + Sélectionnez un fichier à exporter + + + + Internal error during export. This is a bug. Please report it. + Erreur interne pendant l’exportation. c’est un bug. SVP veuillez le reporter. + + + + FileExecErrorsDialog + + + Execution errors + Erreurs d'exécution + + + + Following errors were encountered during execution of SQL statements from the file: + Les erreurs suivantes ont été rencontrées lors de l'exécution d'instructions SQL depuis le fichier : + + + + SQL + SQL + + + + Error + Erreur + + + + Statements that were executed successfully were commited. + Les instructions qui ont été exécutées avec succès ont été validés. + + + + Statements that were executed successfully were rolled back. + Les instructions qui ont été exécutées avec succès ont été restaurées. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Impossible de modifier cette cellule. Détails : %1 + + + + FontEdit + + + Choose font + font configuration + Choisir la police + + + + Form + + + Active SQL formatter plugin + Activer le plugin de formattage SQL + + + + FormView + + + Commit row + form view + Valider la ligne + + + + Rollback row + form view + Annuler ligne + + + + First row + form view + Première ligne + + + + Previous row + form view + Ligne précédente + + + + Next row + form view + Ligne suivante + + + + Last row + form view + Dernière ligne + + + + Insert new row + form view + Insérer une nouvelle ligne + + + + Delete current row + form view + Supprimer la ligne courante + + + + FunctionsEditor + + + Filter functions + Fonctions de filtrage + + + + Input arguments + Entrez arguments + + + + Undefined + Non défini + + + + Databases + Bases de données + + + + Register in all databases + Enregistre toutes les bases de données + + + + Register in following databases: + Enregistre les bases de données suivantes : + + + + Type: + Type : + + + + Function name: + Nom fonction : + + + + Implementation language: + Langage : + + + + Deterministic + Déterministe + + + + Initialization code: + Code d’initialisation : + + + + + Function implementation code: + Fonction de code d’implémentation : + + + + Final step implementation code: + Etape finale de code d’implémentaion : + + + + SQL functions editor + Éditeur de fonctions SQL + + + + Commit all function changes + Valider toutes les modifications de fonction + + + + Rollback all function changes + Annule toutes les fonctions modifiées + + + + Create new function + Crée une nouvelle fonction + + + + Delete selected function + Supprime une fonction sélectionnée + + + + Custom SQL functions manual + Personalisation des fonctions SQL + + + + Add function argument + Ajoute un argument à la fonction + + + + Rename function argument + Renomme l’argument de la fonction + + + + Delete function argument + Supprime l’argument de la fonction + + + + Move function argument up + Monte l’argument de la fonction + + + + Move function argument down + Descend l’argument de la fonction + + + + Scalar + Scalaire + + + + Aggregate + Agregate + + + + Enter a non-empty, unique name of the function. + Saississez un nom unique de fonction. + + + + Pick the implementation language. + Choississez un langage. + + + + Per step code: + Code par étape : + + + + Enter a non-empty implementation code. + Saississez un code d’implémentation non vide. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + La fenêtre de l'éditeur de fonctions contient des modifications invalidées. + + + + ImportDialog + + + Import data + Import données + + + + Table to import to + Table à importer vers + + + + Table + Tableau + + + + Database + Base de données + + + + Data source to import from + Source de données à importer de + + + + Data source type + Type de données source + + + + Options + Options + + + + Text encoding: + Texte codé : + + + + Input file: + Fichier : + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignorer les erreurs + + + + Data source options + Optrions de source de données + + + + Cancel + Annuler + + + + If you type table name that doesn't exist, it will be created. + Si vous saississez un nom de table inexistant, celle-ci sera créée. + + + + Enter the table name + Saississez un nom de table + + + + Select import plugin. + Sélectionnez un plugin d’importation. + + + + You must provide a file to import from. + Vous devez fournir un fichier à importer. + + + + The file '%1' does not exist. + Le fichier « %1 » n’existe pas. + + + + Path you provided is a directory. A regular file is required. + Le chemin indiqué est un répertoire. Un fichier est requis. + + + + Pick file to import from + Sélectionnez le fichier d’importation + + + + IndexDialog + + + + Index + Indice + + + + Column + Colonne + + + + Sort + Tri + + + + Collation + Regroupement + + + + On table: + De la table : + + + + Delete selected indexed expression + Supprimer l'expression indexée sélectionnée + + + + Moves selected index column up in the order, making it more significant in the index. + Déplace la colonne d'index sélectionnée vers le haut dans l'ordre, la rendant plus significative dans l'index. + + + + Moves selected index column down in the order, making it less significant in the index. + Déplace la colonne d'index sélectionnée vers le bas dans l'ordre, la rendant moins significative dans l'index. + + + + Partial index condition + Condition partielle d’index + + + + Unique index + Index unique + + + + Index name: + Nom index : + + + + Edit selected indexed expression + Éditer l'expression indexée sélectionnée + + + + Add indexed expression + Ajouter une expression indexée + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Vous tentez d’ouvrir le dialogue de l’index d’une base de données fermée ou inexistante. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Impossible de définir l’index %1 correctement. Ouvrir un dialogue d’index valide. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Un index unique ne peut pas avoir d'expressions indexées. Supprimez les expressions de la liste ci-dessous ou décochez cette option. + + + + Pick the table for the index. + Sélectionnez la table pour l’index. + + + + Select at least one column. + Selectionnez au moins une colonne. + + + + Enter a valid condition. + Saississez une condition valide. + + + + default + index dialog + defaut + + + + Sort order + table constraints + Ordre de tri + + + + + Error + index dialog + Erreur + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Impossible de créer un index, car les valeurs des colonnes sélectionnées ne sont pas uniques. Voulez-vous exécuter une requête SELECT pour voir les valeurs problématiques ? + + + + An error occurred while executing SQL statements: +%1 + Une erreur survenue à l’exécution de l’SQL : +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Expression indexée + + + + Expression to index + Expression à indexer + + + + This expression is already indexed by the index. + Cette expression est déjà indexée par l'index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + La colonne '%1' n'appartient pas au tableau couvert par cet index. Les expressions indexées ne peuvent se référer qu'aux colonnes de la table indexée. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + Il est interdit d'utiliser l'instruction 'SELECT' dans les expressions indexées. + + + + Enter an indexed expression. + Entrez une expression indexée. + + + + Invalid expression. + Expression invalide. + + + + LanguageDialog + + + Language + Langage + + + + Please choose language: + SVP choississez un langage : + + + + MainWindow + + + Database toolbar + Barre d’outils de base de données + + + + Structure toolbar + Barre d’outils de structure + + + + Tools + Barre d’outils des éditeurs + + + + Window list + Liste des fenêtres ouvertes + + + + View toolbar + Barre d’outils de fenêtrage + + + + Configuration widgets + Configurer les widgets + + + + Syntax highlighting engines + Syntaxe surlignée des moteurs + + + + Data editors + Éditeurs de données + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Passage en mode débogue. Cliquez %1 ou utilisez l’entrée du menu « l’Aide / Ouvrir la console de débogage ». + + + + Running in debug mode. Debug messages are printed to the standard output. + Passage en mode débogue. Les messages de débogage sont imprimés dans la sortie standard. + + + + You need to restart application to make the language change take effect. + Vous devez relancer l’application pour que le langage prenne effet. + + + + Open SQL &editor + Ouvrir l'&éditeur SQL + + + + Open DDL &history + Ouvrir l'&historique des DDL + + + + Open SQL &functions editor + Ouvrir l'éditeur de &fonctions SQL + + + + Open code &snippets editor + Ouvrir l'éditeur des &extraits de code + + + + Open &collations editor + Ouvrir l'éditeur de &regroupements + + + + Open ex&tension manager + Ouvrir le gestionnaire d'ex&tension + + + + &Import + &Importer + + + + E&xport + E&xporter + + + + Open confi&guration dialog + Ouvrir la boîte de dialogue de confi&guration + + + + &Tile windows + &Fenêtres mosaïques + + + + Tile windows &horizontally + Mosaïque &horizontale des fenêtres + + + + Tile windows &vertically + Mosaïque &verticale des fenêtres + + + + &Cascade windows + &Fenêtres en cascade + + + + Next window + Fenêtre suivante + + + + Previous window + Fenêtre précédante + + + + Hide status field + Cacher le champ d’état + + + + Close &all windows + Fermer &toutes les fenêtres + + + + Re&store recently closed window + Re&staurer la fenêtre récemment fermée + + + + Close current &window + Fermer la &fenêtre actuelle + + + + Close &other windows + Fermer les &autres fenêtres + + + + Close windows on the &left + Fermer les fenêtres à &gauche + + + + Close windows on the &right + Fermer les fenêtres à &droite + + + + Re&name selected window + Re&nommer la fenêtre sélectionnée + + + + Open Debug Console + Ouvrir la console de debogage + + + + Open CSS Console + Ouvrir la console CSS + + + + Report a &bug + Signaler un &bug + + + + D&onate + F&aites un don + + + + Propose a new &feature + Proposer une nouvelle &fonctionnalité + + + + &About + &À propos + + + + &Licenses + &Licences + + + + Open home &page + Ouvrir la &page d'accueil + + + + User &Manual + &Manuel de l'utilisateur + + + + SQLite &documentation + &Documentation SQLite + + + + Bugs and feature &requests + Bugs et &demandes de fonctionnalités + + + + Quit + Quitter + + + + Check for &updates + Rechercher des &mises à jour + + + + &Database + menubar + &Base de données + + + + &Structure + menubar + &Structure + + + + &View + menubar + &Affichage + + + + Window list + menubar view menu + Liste des fenêtres + + + + &Tools + menubar + &Outils + + + + &Help + &Aide + + + + Could not set style: %1 + main window + Impossible de positionner le style : %1 + + + + Cannot export, because no export plugin is loaded. + Exportation impossible, aucun plugin d’exportation n’est chargé. + + + + Cannot import, because no import plugin is loaded. + Importation impossible, aucun plugin d’importation n’est chargé. + + + + Rename window + Renommer la fenêtre + + + + Enter new name for the window: + Saississez un nouveau nom de fenêtre : + + + + New updates are available. <a href="%1">Click here for details</a>. + Une nouvelle mise à jour est disponible. <a href="%1"> cliquez ici pour détails</a>. + + + + You're running the most recent version. No updates are available. + Vous utilisez déjà la version la plus récente. Aucune mise à jour n'est disponible. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + La base de données passée dans les paramètres de la ligne de commande (%1) était déjà dans la liste sous le nom : %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + La base de données passée en paramètre dans la ligne de commande (%1)a été temporaire ajoutée à la liste sous le nom : %2 + + + + Could not add database %1 to list. + Impossible d’ajouter la base de données %1 à la liste. + + + + MdiWindow + + + Uncommitted changes + Modifications invalidées + + + + Close anyway + Fermer + + + + Don't close + Ne pas fermer + + + + MultiEditor + + + Null value + multieditor + Valeur NULL + + + + Configure editors for this data type + Configurer l’éditeur pour ce type de données + + + + Open another tab + Ouvrir un autre onglet + + + + Foreign Key + Clef étrangère + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Donnée du plugin dans l'éditeur '%1' ne c'est pas chargé, alors qu'il est défini pour l'édition du type de données '%2'. + + + + Deleted + multieditor + Suppression + + + + Read only + multieditor + Lecture seule + + + + MultiEditorBoolPlugin + + + Boolean + booleen + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + date & heure + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Nombre + + + + MultiEditorText + + + Tab changes focus + Tab modifie le focus + + + + Cut + Couper + + + + Copy + Copier + + + + Paste + Coller + + + + Delete + Supprimer + + + + Undo + Annuler + + + + Redo + Rétablir + + + + MultiEditorTextPlugin + + + Text + Texte + + + + MultiEditorTimePlugin + + + Time + Heure + + + + NewConstraintDialog + + + New constraint + Nouvelle contrainte + + + + + Primary Key + new constraint dialog + Clef primaire + + + + + Foreign Key + new constraint dialog + Clef étrangère + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Contrôle + + + + Not NULL + new constraint dialog + Non NULL + + + + Collate + new constraint dialog + Regrouper + + + + Generated + new constraint dialog + Généré + + + + Default + new constraint dialog + Défault + + + + NewVersionDialog + + + SQLiteStudio updates + Mises à jour SQLiteStudio + + + + New version is available! + Une nouvelle version est disponible ! + + + + Download new version! + Télécharger la nouvelle version ! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + Le nouveau paquet de version sera téléchargé. Il vous appartiendra de l'installer dès lors que vous serez prêt. + + + + Open SQLiteStudio home page. + Ouvrir la page d'accueil de SQLiteStudio. + + + + Read release notes && download package yourself. + Lisez les notes de version && téléchargez le paquet vous-même. + + + + Just close this window. + Fermez seulement cette fenêtre. + + + + Check for updates on startup + Contrôle de nouvelles version au lancement + + + + Not now. + Paas maintenant. + + + + PopulateConfigDialog + + + Populating configuration + Remplir la configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuration <b>%1</b> pour colonne <b>%2</b> + + + + PopulateDialog + + + Populate table + Peupler la table + + + + Database + Base de données + + + + Table + Tableau + + + + Columns + Colonnes + + + + Number of rows to populate: + Nombre de lignes à peupler : + + + + Populate + populate dialog button + Peupler + + + + Abort + Abandonner + + + + Configure + Configurer + + + + Populating configuration for this column is invalid or incomplete. + La configuration du peuplement pour cette colonne est invalide ou incomplète. + + + + Select database with table to populate + Sélectionner la base de données avec la table à peupler + + + + Select table to populate + Sélectionner la table à peupler + + + + You have to select at least one column. + Vous devez sélectionner au moins une colonne. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + impossible d’éditer les colonnes qui ont le résultat composé des déclarations %1 (inclus %2, %3 ou %4 mots-clefs). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + Le mécanisme d'exécution de requête a eu des problèmes avec l'extraction de ROWID. Ceci peut être un bug dans l'application. Vous pouvez le signaler. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + La colonne demandée est un résultat d’expression de SQL, au lieu d’une sélection de colonne simple. De telles colonnes ne peuvent pas être éditées. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + La colonne demandée appartient à une table limitée SQLite. Ces tables ne peuvent pas être éditées directement. + + + + Cannot edit results of query other than %1. + Impossible d’éditer les résultats de la requëte autrement que %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Impossible d’éditer les colonnes qui sont le résultat de déclarations agrégées %1. + + + + Cannot edit columns that are result of %1 statement. + Impossible d’éditer les colonnesqui sont le résultat de déclaration %1. + + + + Cannot edit columns that are result of common table expression statement (%1). + Impossible d’éditer les colonnes qui sont le résultat de table commune de déclaration (%1). + + + + Cannot edit table generated columns. + Impossible de modifier les colonnes générées par le tableau. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Impossible de modifier les colonnes qui sont le résultat d'une vue si la requête exécutée lit à partir d'une vue à plusieurs niveaux (c.-à-d. une vue qui interroge une autre vue). + + + + + + + on conflict: %1 + data view tooltip + Sur conflit : %1 + + + + references table %1, column %2 + data view tooltip + Références table %1, colonne %2 + + + + condition: %1 + data view tooltip + Condition : %1 + + + + collation name: %1 + data view tooltip + Nom de collation : %1 + + + + Data grid view + Vue de tableau de données + + + + Edit current cell inline + Éditer la cellule actuelle intégrer + + + + Copy cell(s) contents to clipboard + Copie le contenu de cellule(s) dans le presse-papier + + + + Copy cell(s) contents together with header to clipboard + Copier le contenu de(s) cellule(s) avec l'en-tête dans le presse-papiers + + + + Paste cell(s) contents from clipboard + Colle + + + + Set empty value to selected cell(s) + Efface le contenu de cellule(s) + + + + Set NULL value to selected cell(s) + Met à NULL les cellules séléctionnées + + + + Commit changes to cell(s) contents + Valider les modifications sur le contenu de(s) cellule(s) + + + + Rollback changes to cell(s) contents + Annule les modifications de cellule(s) + + + + Delete selected data row + Supprime les données de la ligne sélectionnée + + + + Insert new data row + Insére une nouvelle ligne de données + + + + Open contents of selected cell in a separate editor + Contenu ouvert de cellule choisie dans un éditeur séparé + + + + Toggle the height adjustment of rows + Activer/désactiver le réglage de la hauteur des lignes + + + + Increase font size + data view + Augmenter la taille de la police de caractère + + + + Decrease font size + data view + Réduire la taille de la police de caractère + + + + Total pages available: %1 + Nombre de pages disponibles : %1 + + + + Total rows loaded: %1 + Nombre de lignes chargées : %1 + + + + Data view (both grid and form) + Vue des données (tableau et formulaire) + + + + Refresh data + Actualisation des données + + + + Switch to grid view of the data + Basculer sur la vue des données en table + + + + Switch to form view of the data + Basculer sur la vue des données en formulaire + + + + Database list + Liste de bases de données + + + + Delete selected item + Suppression de l’item sélectionné + + + + Clear filter contents + Effacer le contenu du filtre + + + + Refresh schema + Actualiser le schéma + + + + Refresh all schemas + Actualiser tous les schémas + + + + Add database + Ajouter une base de données + + + + Select all items + Séléctionner tous les éléments + + + + Copy selected item(s) + Copie d’item(s) sélectionné(s) + + + + + + Paste from clipboard + Collé dans le presse-papier + + + + Increase font size + database list + Augmenter la taille de la police de caractère + + + + Decrease font size + database list + Réduire la taille de la police de caractère + + + + Tables + Tableaux + + + + Indexes + Index + + + + Triggers + Déclencheurs + + + + Views + Vues + + + + Columns + Colonnes + + + + Data form view + Formulaire vue de données + + + + Commit changes for current row + Valider les modifications pour la ligne actuelle + + + + Rollback changes for current row + Annulation de la ligne courante + + + + Go to first row on current page + Aller à la première ligne de la page courante + + + + Go to next row + Aller à la ligne suivante + + + + Go to previous row + Aller à la ligne précédente + + + + Go to last row on current page + Aller à la dernière ligne de la page courante + + + + Insert new row + Insérer une nouvelle ligne + + + + Delete current row + Supprimer la ligne courante + + + + Main window + Fenêtre principale + + + + Open SQL editor + Ouvrir l’éditeur SQL + + + + Open DDL history window + Ouvrir la fenêtre de l'historique des DDL + + + + Open snippets editor window + Ouvrir la fenêtre de l'éditeur d'extraits de texte + + + + Open function editor window + Ouvrir la fenêtre de l'éditeur de fonction + + + + Open collation editor window + Ouvrir la fenêtre de l'éditeur de regroupement + + + + Open extension manager window + Ouvrir la fenêtre du gestionnaire d'extension + + + + Previous window + Fenêtre précédente + + + + Next window + Fenêtre suivante + + + + Hide status area + Cacher la barre d’état + + + + Open user manual + Ouvrir le manuel utilisateur + + + + Open configuration dialog + Préférences + + + + Open Debug Console + Ouvrir la console de débogage + + + + Open CSS Console + Ouvrir la console CSS + + + + Open the About dialog + Ouvrir la boîte de dialogue À propos + + + + Quit the application + Quitter l'application + + + + Cell text value editor + Éditeur de cellule + + + + + Cut selected text + Couper le texte sélectionné + + + + + Copy selected text + Copie du texte sélectionné + + + + + Delete selected text + Suppression du texte sélectionné + + + + + Undo + Annuler + + + + + Redo + Rétablir + + + + SQL editor input field + Éditeur SQL saisie de champ + + + + Select whole editor contents + Sélectionnez le contenu entier de l’éditeur + + + + Save contents into a file + Sauver le contenu dans un fichier + + + + Load contents from a file + Charger le contenu d’un fichier + + + + Find in text + Rechercher un texte + + + + Find next + Occurence suivante + + + + Find previous + Occurence précédente + + + + Replace in text + Remplacer dans le texte + + + + Delete current line + Supprimer la ligne courante + + + + Request code assistant + Assistant de code nécessaire + + + + Format contents + Format de contenu + + + + Move selected block of text one line down + Déplacer le bloc de texte sélectionné à la ligne inférieure + + + + Move selected block of text one line up + Déplacer le bloc de texte sélectionné à la ligne supérieure + + + + Copy selected block of text and paste it a line below + Copier le bloc de texte sélectionné à la ligne au-dessus + + + + Copy selected block of text and paste it a line above + Copier le bloc de texte sélectionné à la ligne au-dessous + + + + Toggle comment + Basculer le commentaire + + + + Increase font size + sql editor + Augmenter la taille de la police de caractère + + + + Decrease font size + sql editor + Réduire la taille de la police de caractère + + + + All SQLite databases + Toutes les bases de données SQLite + + + + All files + Tous les fichiers + + + + Select database file + Sélectionner le fichier de la base de données + + + + Select + Sélectionner + + + + File type + Type de fichier + + + + SQL editor window + Fenêtre de l’éditeur SQL + + + + Execute query + Exécution de la requête + + + + Execute single query under cursor + Exécuter une requête sous le curseur + + + + Execute all queries in editor + Exécuter toutes les requêtes dans l'éditeur + + + + Execute "%1" query + Exécution de la requête %1 + + + + Switch current working database to previous on the list + Basculer de la base de données actuelle à la précédente de la liste + + + + Switch current working database to next on the list + Basculer de la base de données actuelle à la suivante de la liste + + + + Go to next editor tab + Aller à l’onglet d’éditeur suivant + + + + Go to previous editor tab + Aller à l’onglet d’éditeur précédent + + + + Move keyboard input focus to the results view below + Déplacement au-dessus du focus des résultats de vue par les touches + + + + Move keyboard input focus to the SQL editor above + Déplacement au-dessous du focus des résultats de vue par les touches + + + + Delete selected SQL history entries + Supprimer les entrées de l'historique SQL sélectionnées + + + + Table window + Fenêtre de table + + + + Commit the table structure + Valider la structure du tableau + + + + Rollback pending changes in the table structure + Restaurer les modifications en attente dans la structure du tableau + + + + Refresh table structure + Actualiser la structure de la table + + + + Add new column + Ajouter une nouvelle colonne + + + + Edit selected column + Modifier la colonne sélectionnée + + + + Delete selected column + Supprime la colonne sélectionnée + + + + Export table data + Exporte les données de table + + + + Import data to the table + Importe les données de table + + + + Add new table constraint + Ajoute une nouvelle contrainte à la table + + + + Edit selected table constraint + Modifie la contrainte de la table sélectionnée + + + + Delete selected table constraint + Supprime la contrainte de la table sélectionnée + + + + Refresh table index list + Actualise la liste des index de la table + + + + Add new index + Ajoute un nouvel index + + + + Edit selected index + Modifie l’index sélectionné + + + + Delete selected index + Supprime l’index sélectionné + + + + Refresh table trigger list + Actualise la liste des déclencheurs de la table + + + + + Add new trigger + Ajoute un nouveau déclencheur + + + + + Edit selected trigger + Modifie le déclencheur sélectionné + + + + + Delete selected trigger + Supprime le déclencheur sélectionné + + + + + Go to next tab + Aller à l’onglet suivant + + + + + Go to previous tab + Aller à l’onglet précédent + + + + A view window + Fenêtre de vue + + + + Commit the view's query + Valider les requêtes vues + + + + Rollback pending changes in the view's query + Restaurer les modifications en attente parmi les requêtes vues + + + + Refresh view trigger list + Actualise l’affichage de la liste des déclencheurs + + + + Execute the view's query + Exécuter les requêtes vues + + + + A code snippets editor window + Une fenêtre d'éditeur d'extraits de code + + + + + + + Commit the pending changes + Valider les modifications en attente + + + + + + + Rollback the pending changes + Restaurer les modifications en attente + + + + A collation editor window + Une fenêtre d'éditeur de regroupement + + + + A function editor window + Une fenêtre d'éditeur de fonction + + + + A SQLite extension editor window + Une fenêtre d'éditeur d'extension SQLite + + + + QuitConfirmDialog + + + Uncommitted changes + Modifications invalidées + + + + Are you sure you want to quit the application? + +Following items are pending: + Confirmez la fermeture de l’application ? + + + + SearchTextDialog + + + Find or replace + Chercher et remplacer + + + + Find: + Trouvé : + + + + Case sensitive + Sensible à la casse + + + + Search backwards + Recherches en arrière + + + + Regular expression matching + Correspondance d’expression régulière + + + + Replace && +find next + Remplace && +recherche suivant + + + + Replace with: + Remplacer par : + + + + Replace all + Remplacer tout + + + + Find + Rechercher + + + + SortDialog + + + Sort by columns + Tri par colonnes + + + + + Column + Colonne + + + + + Order + Ordre + + + + Sort by: %1 + Tri par %1 + + + + Move column up + Monter colonne + + + + Move column down + Descendre colonne + + + + SqlEditor + + + Wrap words + sql editor + Envelopper les mots + + + + Cut + sql editor + Couper + + + + Copy + sql editor + Copier + + + + Paste + sql editor + Coller + + + + Delete + sql editor + Supprimer + + + + Select all + sql editor + Tout sélectionner + + + + Undo + sql editor + Annuler + + + + Redo + sql editor + Rétablir + + + + Complete + sql editor + Complet + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Enregistrer le SQL + + + + Select file to save SQL + sql editor + Sélectionnez le fichier pour enregistrer le SQL + + + + Load SQL from file + sql editor + Charger le SQL + + + + Delete line + sql editor + Ligne suppimée + + + + Move block down + sql editor + Descendre le bloc + + + + Move block up + sql editor + Monter le bloc + + + + Copy block down + sql editor + Copier bloc au-dessus + + + + Copy up down + sql editor + Copier bloc au-dessous + + + + Find + sql editor + Chercher + + + + Find next + sql editor + Chercher suivant + + + + Find previous + sql editor + Chercher précédent + + + + Replace + sql editor + Remplacer + + + + Toggle comment + sql editor + Basculer le commentaire + + + + Increase font size + sql editor + Augmenter la taille de la police de caractère + + + + Decrease font size + sql editor + Réduire la taille de la police de caractère + + + + Could not open file '%1' for writing: %2 + Impossible d’ouvrir en écriture le fichier « %1 » : %2 + + + + Saved SQL contents to file: %1 + Contenu SQL sauvegardé dans le fichier : %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + L’achèvement de syntaxe peut être utilisé seulement quand une base de données valable est utilisée dans l’éditeur SQL. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Le contenu l’éditeur SQL est important, aussi la détectiond’objets en erreur est temporairement mise hors de service. + + + + Save to file + Sauvegarder + + + + SQL scripts (*.sql);;All files (*) + Scripts SQL (*.sql);;Tous les fichiers (*) + + + + Open file + Fichier ouvert + + + + Could not open file '%1' for reading: %2 + Impossible d’ouvrir en lecture le fichier « %1 » : %2 + + + + Reached the end of document. Hit the find again to restart the search. + Fin de document atteint. Saississez de nouveau la recherche pour relancer la recherche. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Erreur de validation : + + + + Column: + data view tooltip + Colonne : + + + + Data type: + data view + Type de données : + + + + Table: + data view tooltip + Table : + + + + Constraints: + data view tooltip + Contrainte : + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Impossible de modifier cette cellule. Détails : %1 + + + + The row is marked for deletion. + La ligne est marquée pour effacement. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + La structure de ce tableau a changé depuis que les dernières données ont été chargés. Rechargez les données pour continuer. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + La clé étrangère de la colonne %2 a plus de %1 valeurs possibles. C'est trop pour être affiché dans la liste déroulante. Vous devez modifier la valeur manuellement. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Uniquement une seule requête peut être exécutée à la fois. + + + + Cannot execute query on undefined or invalid database. + Impossible d'exécuter la requête sur une base de données indéfinie ou invalide. + + + + Cannot execute empty query. + Impossible d'exécuter une requête vide. + + + + Uncommitted data + Données invalidées + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + Il y a des modifications de données invalidées. Voulez-vous quand même continuer ? Toutes les modifications non validées seront perdues. + + + + Cannot commit the data for a cell that refers to the already closed database. + Impossible de valider les données pour une cellule qui fait référence à une base de données déjà fermée. + + + + Could not begin transaction on the database. Details: %1 + Impossible de lancer la transaction sur la base de données. Détails : %1 + + + + An error occurred while committing the transaction: %1 + Une erreur s'est produite lors de la validation de la transaction : %1 + + + + An error occurred while rolling back the transaction: %1 + Une erreur est survenuelors de l’annulation de la transaction : %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tentative de validation d'une cellule qui n'est pas modifiable (encore modifiée et en attente de validation) ! Ceci est un bug. Merci de le signaler. + + + + An error occurred while committing the data: %1 + Une erreur s'est produite lors de la validation des données : %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Le nombre de lignes par page a été réduit à %1 en raison du nombre de colonnes (%2) dans la vue des données. + + + + + Error while executing SQL query on database '%1': %2 + Erreur pendant l’exécution de la requête sur la base de données « %1 » : %2 + + + + Error while loading query results: %1 + Erreur lors du chargement des résultats de la requête : %1 + + + + Insert multiple rows + Insérer plusieurs lignes + + + + Number of rows to insert: + Nombre de lignes à inserer : + + + + Delete rows + Supprimer les lignes + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + Vous vous apprêtez à supprimer des lignes nouvellement insérées qui ne sont pas encore validées. Numéros de ligne : %1 +Cette suppression sera permanente. Êtes-vous sûr de vouloir les supprimer ? + + + + SqlQueryView + + + Go to referenced row in... + Aller à la ligne référencée dans... + + + + Copy + Copier + + + + Copy with headers + Copier avec les en-têtes + + + + Copy as... + Copier comme… + + + + Paste + Coller + + + + Paste as... + Coller comme… + + + + Set NULL values + Valeurs NULL positionnées + + + + Erase values + valeurs écrasées + + + + Commit + Valider + + + + Rollback + Annuler + + + + Commit selected cells + Valider les cellules sélectionnées + + + + Rollback selected cells + Annuler les modifications des cellules sélectionnées + + + + Edit current cell inline + Éditer la cellule actuelle intégrer + + + + Define columns to sort by + Définit les colonnes triées par + + + + Remove custom sorting + Enléve le tri personnalisé + + + + Insert row + Insérer une ligne + + + + Insert multiple rows + Insérer plusieurs lignes + + + + Delete selected row + Supprimer les lignes sélectionnées + + + + Adjust height of rows + Ajuster la hauteur des lignes + + + + Increase font size + data view + Augmenter la taille de la police de caractère + + + + Decrease font size + data view + Réduire la taille de la police de caractère + + + + Invert selection + data view + Inverser la sélection + + + + Edit value in editor + Valeur modifiée par l’éditeur + + + + Show value in a viewer + Afficher la valeur dans une visionneuse + + + + Generate query for selected cells + Générer une requête pour les cellules sélectionnées + + + + No items selected to paste clipboard contents to. + Aucun élément sélectionné pour coller le contenu du presse-papiers. + + + + Cannot paste data. Details: %1 + Impossible de coller les données. Détails : %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + La structure d'au moins un tableau utilisé a changé depuis le chargement des dernières données. Rechargez les données pour continuer. + + + + Cannot paste to a cell. Details: %1 + Impossible de coller dans une cellule. Détails : %1 + + + + The row is marked for deletion. + La ligne est marquée pour effacement. + + + + Cannot paste to column %1. Details: %2 + Impossible de coller dans la colonne %1. Détails : %2 + + + + Go to referenced row in table '%1' + Aller à la ligne référencée dans le tableau '%1' + + + + table '%1' + tableau '%1' + + + + Referenced row (%1) + Ligne référencée (%1) + + + + Trim pasted text? + Réduire le texte collé ? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + Le texte collé contient de l'espace blanc en début ou en fin de page. Voulez-vous le supprimer automatiquement ? + + + + Paste "NULL" as null value? + Coller "NULL" en tant que valeur nulle ? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + Le texte collé contient des "NULL" littéraux. Voulez-vous les considérer comme des valeurs NULL ? + + + + Edit value + Modifier la valeur + + + + SqlTableModel + + + Error while committing new row: %1 + Erreur lors de la validation de la nouvelle ligne : %1 + + + + Error while deleting row from table %1: %2 + Erreur à la suppression d’une ligne de la table %1 : %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filtrer les extensions + + + + Leave empty to use default function + Laisser vide pour utiliser la fonction par défaut + + + + Extension file + Fichier d'extension + + + + Initialization function + Fonction d'initialisation + + + + Databases + Bases de données + + + + Register in all databases + Enregistre dans toutes les bases de données + + + + Register in following databases: + Inscrire dans les bases de données suivantes : + + + + Extension manager window has uncommitted modifications. + La fenêtre du gestionnaire d'extension contient des modifications invalidées. + + + + Extension manager + Gestionnaire d'extensions + + + + Commit all extension changes + Valider tous les changements d'extension + + + + Rollback all extension changes + Restaurer tous les changements d'extension + + + + Add new extension + Ajouter une nouvelle extension + + + + Remove selected extension + Supprimer l'extension sélectionnée + + + + Editing extensions manual + Éditer le manuel des extensions + + + + File with given path does not exist or is not readable. + Le fichier avec le chemin spécifié n'existe pas ou n'est pas lisible. + + + + Unable to load extension: %1 + Impossible de charger l'extension : %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Nom de la fonction d'initialisation invalide. Le nom de la fonction ne peut contenir que des caractères alphanumériques et des tirets bas. + + + + Dynamic link libraries (*.dll);;All files (*) + Librairies de liens dynamiques (*.dll);Tous les fichiers (*) + + + + Shared objects (*.so);;All files (*) + Objets partagés (*.so);Tous les fichiers (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Bibliothèques dynamiques (*.dylib);;Tous les fichiers (*) + + + + All files (*) + Tous les fichiers(*) + + + + Open file + Fichier ouvert + + + + StatusField + + + Status + Barre d’état + + + + Copy + Copier + + + + Clear + Vider + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Détails + + + + Name + table constraints + Nom + + + + TableForeignKeyPanel + + + Foreign table: + Table étrangère : + + + + Columns + Colonnes + + + + Local column + Colonne locale + + + + Foreign column + Colonne étrangère + + + + Reactions + Réactions + + + + Deferred foreign key + Clé étrangère différée + + + + Named constraint + Contrainte nommée + + + + Constraint name + Nom de la contrainte + + + + Pick the foreign column. + Choisir la colonne étrangère. + + + + Pick the foreign table. + Choisir la table étrangère. + + + + Select at least one foreign column. + Sélectionner au moins une colonne étrangère. + + + + Enter a name of the constraint. + Saisissez un nom de contrainte. + + + + Foreign column + table constraints + Colonne étrangère + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Colonnes + + + + Column + Colonne + + + + Collation + Classement + + + + Sort + Tri + + + + Valid only for a single column with INTEGER data type + Valide seulement pour une simple colonne avecun type de données INTEGER + + + + Autoincrement + Auto-incrémentation + + + + Named constraint + Contrainte nommée + + + + Constraint name + Nom de la contrainte + + + + On conflict + Sur conflit + + + + Collate + table constraints + Collationne + + + + Sort order + table constraints + Ordre de tri + + + + Select at least one column. + Sélectionnez au moins une colonne. + + + + Enter a name of the constraint. + Saisissez le nom de la contrainte. + + + + TableStructureModel + + + Name + table structure columns + Nom + + + + Data type + table structure columns + Type de données + + + + Primary +Key + table structure columns + Clé primaire + + + + Foreign +Key + table structure columns + Clé +étrangère + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Contrôle + + + + Not +NULL + table structure columns + Non +NULL + + + + Collate + table structure columns + Collecter + + + + Generated + table structure columns + Généré + + + + Default value + table structure columns + Valeur par défaut + + + + TableWindow + + + Structure + Structure + + + + Table name: + Nom de la table : + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Active/désactive la clause SANS ID DE LIGNE du tableau. Un tel tableau n'aura plus le &quot;idligne&quot; la colonne cachée. Pour un tel tableau, une colonne CLÉ PRIMAIRE explicite est obligatoire. Vous pouvez lire plus de détails à ce sujet dans la documentation officielle de SQLite.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Données + + + + Constraints + Contraintes + + + + Indexes + Index + + + + Triggers + Déclencheurs + + + + DDL + DDL + + + + Export table + table window + Exporter une table + + + + Import data to table + table window + Importer les données d’une table + + + + Populate table + table window + Peupler une table + + + + Refresh structure + table window + Actualiser la structure + + + + Commit structure changes + table window + Enregistrer les modifications de la structure + + + + Rollback structure changes + table window + Annuler les modifications de la structure + + + + Add column + table window + Ajouter une colonne + + + + Edit column + table window + Modifier une colonne + + + + + Delete column + table window + Supprimer une colonne + + + + Move column up + table window + Monter la colonne + + + + Move column down + table window + Descendre la colonne + + + + Create similar table + table window + Créer une table identique + + + + Reset autoincrement value + table window + Réinitialisation de l’incrémentation + + + + Add table constraint + table window + Ajouter une contrainte de table + + + + Edit table constraint + table window + Modifier la contrainte de table + + + + Delete table constraint + table window + Supprimer la contrainte de table + + + + Move table constraint up + table window + Monter la contrainte de table + + + + Move table constraint down + table window + Descendre la contrainte de table + + + + Add table primary key + table window + Ajouter une clef primaire à la table + + + + Add table foreign key + table window + Ajouter une clef étrangère à la table + + + + Add table unique constraint + table window + Ajouter une contrainte clef unique à la table + + + + Add table check constraint + table window + Ajouter une contrainte de vérification de tableau + + + + Refresh index list + table window + Actualiser la liste des index + + + + + Create index + table window + Créer un index + + + + Edit index + table window + Modifier un index + + + + Delete index + table window + Supprimer un index + + + + Refresh trigger list + table window + Actualiser la liste des déclencheurs + + + + + Create trigger + table window + Créer un déclencheur + + + + Edit trigger + table window + Modifier un déclencheur + + + + Delete trigger + table window + Supprimer un déclencheur + + + + Are you sure you want to delete column '%1'? + table window + Êtes-vous sûr de vouloir supprimer la colonne '%1' ? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Des problèmes suivants auront lieu en modifiant la table. +Voudriez-vous procéder ? + + + + Table modification + table window + Modification de la table + + + + Could not load data for table %1. Error details: %2 + Impossible de charger les données de table %1. Détails d’ erreur : %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Impossible de lancer correctement la table %1. Impossible d’ouvrir la fenêtre de table. + + + + Database + Base de données + + + + Could not restore window %1, because no database or table was stored in session for this window. + Impossible de restaurer la fenêtre %1, car aucune base de données ou tableau n'a été stockée dans la session pour cette fenêtre. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Impossible de restaurer la fenêtre '%1', car aucune base de données ou tableau n'a été stockée dans la session pour cette fenêtre. + + + + Could not restore window '%1', because database %2 could not be resolved. + Impossible de restaurer la fenêtre '%1', car la base de données %2 n'a pas pu être résolue. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Impossible de restaurer la fenêtre '%1', car le tableau %2 n'existe pas dans la base de données %3. + + + + + New table %1 + Nouvelle table %1 + + + + Committed changes for table '%1' successfully. + Modifications validées pour le tableau '%1' avec succès. + + + + Committed changes for table '%1' (named before '%2') successfully. + Modifications validées pour le tableau '%1' (précédemment nommés '%2') avec succès. + + + + Could not commit table structure. Error message: %1 + table window + Impossible d’enregistrer la structure de table. Message d’erreur : %1 + + + + Reset autoincrement + Réinitialisation de l’incrémentation + + + + Are you sure you want to reset autoincrement value for table '%1'? + Êtes-vous sûr de vouloir réinitialiser la valeur de l'auto-incrémentation pour le tableau '%1' ? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + Une erreur est survenue pendant la réinitialisation de la valeur de l’auto-incrémentation de la table « %1 » : %2 + + + + Autoincrement value for table '%1' has been reset successfully. + La valeur d'auto-incrémentation pour le tableau '%1' a été réinitialisée avec succès. + + + + Empty name + Nom vide + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + Un nom vide pour la vue dans SQLITE est admis, mais on ne le recommande pas. +Êtes-vous sûrs que vous voulez créer une vue avec le nom vide ? + + + + Cannot create a table without at least one column. + Impossible de créer une table sans au moins une colonne. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Impossible de créer la table %1, s’il n’y a pas de clef primaire de définie. Toutefois ne pas contrôler %2 ou définir une clef primaire. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Impossible d’utiliser l’auto-incrémentation pour une clef primaire quand la clause %1 est utilisée. Toutefois ne pas contrôler %2, ou utiliser l’auto-incrémentation sur une clef primaire. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Les colonnes suivantes ont un type de données non strictes : %1. Désactive le mode strict du tableau ou fixe les types de données des colonnes. Les types de données strictes valides sont : %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Êtes-vous sûr de vouloir supprimer la contrainte « %1 » ? + + + + Delete constraint + table window + Supprimer la contrainte + + + + Cannot export, because no export plugin is loaded. + Export impossible, car aucun plugin d’import n’est chargé. + + + + Cannot import, because no import plugin is loaded. + Import impossible, car aucun plugin d’import n’est chargé. + + + + Uncommitted changes + Changements invalidés + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Retour à l’onglet de structure + + + + Commit modifications and browse data. + Enregistrer les modifications et continuer + + + + Name + table window indexes + Nom + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Colonnes + + + + Partial index condition + table window indexes + Condition partielle d’index + + + + Name + table window triggers + Nom + + + + Event + table window triggers + Événement + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Détails + + + + Table window "%1" has uncommitted structure modifications and data. + La fenêtre du tableau "%1" a des modifications de structure et des données invalidées. + + + + Table window "%1" has uncommitted data. + La fenêtre du tableau "%1" a des données invalidées. + + + + Table window "%1" has uncommitted structure modifications. + La fenêtre du tableau "%1" a des modifications de structure invalidées. + + + + TriggerColumnsDialog + + + Trigger columns + Colonnes de déclencheur + + + + Triggering columns: + Colonnes avec déclencheurs : + + + + Select all + Tout sélectionner + + + + Deselect all + Tout désélectionner + + + + TriggerDialog + + + + Trigger + Déclencheur + + + + On table: + Sur table : + + + + Action: + Action : + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p> La condition SQL sera évaluée avant le code du déclencheur réel. Dans le cas où le retour de condition est faux, le déclencheur ne sera pas utilisé pour cette ligne.</p > + + + + Pre-condition: + Précondition : + + + + The scope is still not fully supported by the SQLite database. + La portée n’est toujours pas entièrement supportée par la base de données SQLITE. + + + + Trigger name: + Nom du déclencheur : + + + + When: + Quand : + + + + List of columns for UPDATE OF action. + Liste des colonnes pour l’action UPDATE OF. + + + + Scope: + Portée : + + + + Code: + Code : + + + + Trigger statements to be executed. + Déclaration du déclencheur devant être exécutée. + + + + DDL + DDL + + + + On view: + Sur vue : + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Impossible d’exécuter correctement le déclencheur %1. Ouverture invalide du dialogue de déclencheur. + + + + Enter a valid condition. + Saisissez une condition valide. + + + + Enter a valid trigger code. + Saisissez un code de déclencheur valide. + + + + Error + trigger dialog + Erreur + + + + An error occurred while executing SQL statements: +%1 + Une erreur survenue lors de l’exécution de l’intruction SQL : %1 + + + + VersionConvertSummaryDialog + + + Database version convert + Version de convertion de la base de données + + + + Following changes to the SQL statements will be made: + Des modifications suivantes aux déclarations SQL seront faits : + + + + Before + Avant + + + + After + Après + + + + ViewWindow + + + Query + Requête + + + + View name: + Nom de la vue : + + + + Output column names + Noms des colonnes de sortie + + + + + Data + Données + + + + Triggers + Déclencheur + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Impossible de restaurer la fenêtre '%1', car aucune base de données ou vue n'a été stockée dans la session pour cette fenêtre. + + + + Could not restore window '%1', because database %2 could not be resolved. + Impossible de restaurer la fenêtre '%1', car la base de données %2 n'a pas pu être résolue. + + + + Could not restore window '%1', because database %2 could not be open. + Impossible de restaurer la fenêtre '%1', car la base de données %2 n'a pas pu être ouverte. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Impossible de restaurer la fenêtre '%1', car la vue %2 n'existe pas dans la base de données %3. + + + + + New view %1 + Nouvelle vue %1 + + + + Database + Base de données + + + + Refresh the view + view window + Actualisation de la vue + + + + Commit the view changes + view window + Enregistrement des changements dans la vue + + + + Rollback the view changes + view window + Annulation des changements dans la vue + + + + Explicit column names + Noms de colonnes explicites + + + + Generate output column names automatically basing on result columns of the view. + Générer automatiquement les noms des colonnes de sortie en se basant sur les colonnes de résultat de la vue. + + + + Add column + view window + Ajouter une colonne + + + + Edit column + view window + Modifier une colonne + + + + Delete column + view window + Supprimer une colonne + + + + Move column up + view window + Monter la colonne + + + + Move column down + view window + Déplacer la colonne vers le bas + + + + Refresh trigger list + view window + Actualisation de la liste des déclencheurs + + + + Create new trigger + view window + Création d’un nouveau déclencheur + + + + Edit selected trigger + view window + Modification du déclencheur sélectionné + + + + Delete selected trigger + view window + Suppression du déclencheur sélectionné + + + + View window "%1" has uncommitted structure modifications and data. + Fenêtre de visualisation "%1" a des modifications de structure et des données invalidées. + + + + View window "%1" has uncommitted data. + Fenêtre de visualisation "%1" a des données non invalidées. + + + + View window "%1" has uncommitted structure modifications. + Fenêtre de visualisation "%1" a des modifications de structure invalidées. + + + + Could not load data for view %1. Error details: %2 + Impossible de charher les données de vue %1. Détails d’ erreur : %2 + + + + Uncommitted changes + Changements non envoyés + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Retour à l’onlet de structure + + + + Commit modifications and browse data. + Enregistrement des modifications et navigation des données. + + + + View '%1' was committed successfully. + La vue '%1' a été validée avec succès. + + + + Committed changes for view '%1' successfully. + Modifications validées pour la vue '%1' avec succès. + + + + Committed changes for view '%1' (named before '%2') successfully. + Modifications validées pour la vue '%1' (précédemment nommés '%2') avec succès. + + + + Could not commit view changes. Error message: %1 + view window + Impossible de valider les modifications de vue. Message d'erreur : %1 + + + + Override columns + Remplacer les colonnes + + + + Currently defined columns will be overriden. Do you want to continue? + Les colonnes actuellement définies seront remplacer. Voulez-vous continuer ? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Impossible de déterminer les colonnes renvoyées depuis la vue. La requête est sûrement incomplète ou contient des erreurs. + + + + Name + view window triggers + Nom + + + + Instead of + view window triggers + À la place de + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Détails + + + + Could not process the %1 view correctly. Unable to open a view window. + Impossible de lancer correctement la vue %1. Impossible d’ouvrir la fenêtre de vue. + + + + Empty name + Nom absent + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + Un nom vide pour la vue dans SQLITE est admis, mais on ne le recommande pas. +Êtes-vous sûrs que vous voulez créer une vue avec le nom vide ? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + La déclaration SELECT ne peut être analysé. Veuillez corriger la requête et réessayer. +Details : %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + La vue ne peut être modifiée a cause d’une erreur interne de SQLiteStudio. SVP repportez l’erreur ! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + Le code vu n'a pas pu être analysé correctement pour l'exécution. Ceci est un bug de SQLiteStudio. Veuillez le signaler. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Des problèmes suivants auront lieu en modifiant la vue. +Veulez-vous continuer ? + + + + View modification + view window + Fenêtre vue + + + + WidgetCover + + + Interrupt + Interruption + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_he_IL.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_he_IL.ts new file mode 100644 index 0000000..bc7da76 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_he_IL.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + ×ודות QLiteStudio ורישיונות + + + + About + ×ודות + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">מנהל מסדי × ×ª×•× ×™× SQLite חינמי בקוד פתוח, חוצה מסדות.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">מחבר ומתחזק פעיל:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + רישיון + + + + Environment + סביבה + + + + Icon directories + מחיצת צלמיות + + + + Form directories + מחיצת ×˜×¤×¡×™× + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + מחיצת ×ª×•×¡×¤×™× + + + + Configuration directory + מחיצת תצור + + + + Application directory + מחיצת ×”×™×™×©×•× + + + + Qt version: + גרסת Qt: + + + + SQLite 3 version: + גירסת SQLite 3: + + + + Portable distribution. + הפצה ניידת. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + הפצה מנוהלת מערכת הפעלה. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + גורמי ש×ילתה + + + + Please provide values for query parameters + × × ×œ×¡×¤×§ ×¢×¨×›×™× ×œ×ž×©×ª× ×™ הש×ילתה + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + סינון ××•×¨×’× ×™× + + + + Databases + מסד × ×ª×•× ×™× + + + + Register in all databases + ×¨×™×©×•× ×œ×›×œ מסדי ×”× ×ª×•× ×™× + + + + Register in following databases: + ×¨×™×©×•× ×œ×ž×¡×“×™ ×”× ×ª×•× ×™× ×”×‘××™×: + + + + Implementation code: + קוד יישו×: + + + + Collation name: + ×©× ×רגון: + + + + Implementation language: + שפת יישו×: + + + + Collations editor + עורך ×סופות, + + + + Commit all collation changes + קִבּוּעַ כל שינויי ×”×סופה + + + + Rollback all collation changes + הסגת כל שינויי ×”×סופה + + + + Create new collation + יצירת ×סופה חדשה + + + + Delete selected collation + מחיקת עמודות שנבחרו + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + בחירת שפת היישו×. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + בחירת צבע + + + + ColumnCollatePanel + + + Collation name: + ×©× ×וסף: + + + + Named constraint: + ×ילוץ ש×: + + + + Enter a name of the constraint. + הזנת ×©× ×œ×ילוץ. + + + + Enter a collation name. + הזנת ×©× ×יסוף. + + + + ColumnDefaultPanel + + + Default value: + ערך ברירת המחדל: + + + + Named constraint: + ×ילוץ בעל ש×: + + + + Enter a default value expression. + הזנת ביטוי ערך ברירת מחדל. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + הזנת ×©× ×œ×ילוץ. + + + + ColumnDialog + + + Column + עמודה + + + + Name and type + ×©× ×•×¡×•×’ + + + + Scale + ×§× ×” מידה + + + + Precision + דיוק + + + + Data type: + סוג מידע: + + + + Column name: + ×©× ×¢×ž×•×“×”: + + + + Size: + גודל: + + + + Constraints + ××™×œ×•×¦×™× + + + + Generated value + ערכך מחולל + + + + Unique + יחיד××™ + + + + + + + + + + + Configure + תצור + + + + Foreign Key + מפתח זר + + + + Collate + ×יסוף + + + + Not NULL + ×œ× NULL + + + + Check condition + תנ××™ בקרה + + + + Primary Key + מפתח ר×שי + + + + Default + ברירת מחדל + + + + Advanced mode + מצב ×ž×ª×§×“× + + + + Add constraint + column dialog + הוספת ×ילוץ + + + + Edit constraint + column dialog + עריכת ×ילוץ + + + + + Delete constraint + column dialog + מחיקת ×ילוץ + + + + Move constraint up + column dialog + העברת ×ילוץ מעלה + + + + Move constraint down + column dialog + העברת ×ילוץ מטה + + + + Add a primary key + column dialog + הוספת מפתח ר×שי + + + + Add a foreign key + column dialog + הוספת מפתח משני + + + + Add an unique constraint + column dialog + הוספת ×ילוץ יחוד××™ + + + + Add a check constraint + column dialog + הוספת ×ילוץ בקרה + + + + Add a not null constraint + column dialog + הוספת ×ילוץ ×œ× Null + + + + Add a collate constraint + column dialog + הוספת ×ילוץ ×יסוף + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + הוספת ×ילוץ ברירת מחדל + + + + Are you sure you want to delete constraint '%1'? + column dialog + ×”×× ×œ×ž×—×•×§ ×ילוץ '%1'? + + + + Correct the constraint's configuration. + × × ×œ×ª×§×Ÿ ×ת תצור ×”×ילוץ'. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + סוג + + + + Name + column dialog constraints + ×©× + + + + Details + column dialog constraints + ×¤×¨×˜×™× + + + + ColumnForeignKeyPanel + + + Foreign table: + טבלה זרה: + + + + Foreign column: + עמודה זרה: + + + + Reactions + תגובות + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + ×ילוץ בעל ×©× + + + + Constraint name + ×©× ×ילוץ + + + + Pick the foreign table. + בחירת הטבלה הזרה. + + + + Pick the foreign column. + בחירת העמודה הזרה. + + + + Enter a name of the constraint. + הזנת ×©× ×œ×ילוץ. + + + + ColumnGeneratedPanel + + + Generating code: + חילול קוד: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + ×ילוץ בש×: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + הזנת ×©× ×œ×ילוץ. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + סדר מיון: + + + + Named constraint: + ×©× ×ילוץ: + + + + On conflict: + ×ין התנגשות: + + + + Enter a name of the constraint. + הזנת ×©× ×œ×ילוץ. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + ×ילוץ בעל ש×: + + + + On conflict: + בהתנגשות: + + + + Enter a name of the constraint. + הזנת ×©× ×œ×ילוץ. + + + + CompleterWindow + + + Column: %1 + completer statusbar + עמודה: %1 + + + + Table: %1 + completer statusbar + טבלה: %1 + + + + Index: %1 + completer statusbar + מִפְתֵּחַ: %1 + + + + Trigger: %1 + completer statusbar + מַזְנֵק: %1 + + + + View: %1 + completer statusbar + מצג: %1 + + + + Database: %1 + completer statusbar + מסד נתוני×: %1 + + + + Keyword: %1 + completer statusbar + מילת מפתח: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + מחרוזת + + + + Number + completer statusbar + מספר + + + + Binary data + completer statusbar + × ×ª×•× ×™× ×‘×™× ××¨×™×™× + + + + Collation: %1 + completer statusbar + ×רגון: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + הגדרות + + + + Search + חיפוש + + + + General + כללי + + + + Keyboard shortcuts + קיצורי מקלדת + + + + Look & feel + מר××” ותחושה + + + + Style + סגנון + + + + Fonts + ×’×•×¤× ×™× + + + + Code colors + Code colors + + + + + Database list + רשימת מסדי × ×ª×•× ×™× + + + + Code assistant + Code assistant + + + + Data browsing + סיור ×‘× ×ª×•× ×™× + + + + Data editors + עורכי × ×ª×•× ×™× + + + + Plugins + ×ž×ª×§×¢×™× + + + + Code formatters + מתבנתי קוד + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + הצגת תוויות לטבל×ות רגילות + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + הצגת תוויות של טבל×ות מדומות + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + מיון ×¢×¦×ž×™× (טבל×ות, מפתחי×, ×”×“×§×™× ×•×ž×¦×’×™×) + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + חלון דו-שיח מסד × ×ª×•× ×™× + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + סיור ×‘× ×ª×•× ×™× ×•×¢×¨×™×›×” + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>×›×שר ×פשרות זו מופעלת והמשתמש מחזיק מצביע עכבר מעל ×ª× ×‘×ž×¦×’ × ×ª×•× ×™× ×›×œ×©×”×• (תוצ×ות ש×ילתה, נתוני טבלה, נתוני תצוגה) יופיע כלי עצה ×¢× ×¤×¨×˜×™× ×ודות ×”×ª× - שיכלול ×¤×¨×˜×™× ×ודות סוג נתוני העמודה, ×ילוצי×, ROWID ועוד</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + סוגי מידע + + + + Available editors: + ×¢×•×¨×›×™× ×–×ž×™× ×™×: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + ש×ילתות SQL + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + גודל היסטוריה: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + ×¢×“×›×•× ×™× + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + ×ž×•×¤×¢×™× + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + שדה מצב + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + פעולה + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + תצוגה מקדימה + + + + Enabled + מופעל + + + + Disabled + מושבת + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + גופן עורך SQL + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + ×©× ×ילוץ: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + הזנת ×©× ×œ×ילוץ. + + + + ConstraintDialog + + + New constraint + constraint dialog + ×ילוץ חדש + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + עריכת ×ילוץ + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + ×יסוף + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + קיבוע ×©×™× ×•×™×™× ×œ×ª××™× ×©× ×‘×—×¨×• + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + פתיחת מחיצת ×§×‘×¦×™× + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + ש×ילתה + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + הנתיב שסופק מפנה למחיצה קיימת, ×œ× × ×™×ª×Ÿ לדרוס ×ותה. + + + + The directory '%1' does not exist. + מחיצה „%1â€ ×œ× ×§×™×™×ž×ª. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + לקבע כעת + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + הנתיב שסופק מפנה למחיצה קיימת, נדרש קובץ רגיל. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + ×ילוץ חדש + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + ×יסוף + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + הוספת ×ילוץ טבלה חדש + + + + Edit selected table constraint + עריכת ×ילוץ טבלה + + + + Delete selected table constraint + מחיקת ×ילוץ טבלה שנבחר + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + שגי×ת קיבוע: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + ×ילוצי×: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + קיבוע + + + + Rollback + Rollback + + + + Commit selected cells + קיבוע הת××™× ×©× ×‘×—×¨×• + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + ×ילוץ בעל ×©× + + + + Constraint name + ×©× ×ילוץ + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + הזנת ×©× ×œ×ילוץ. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + ×ילוץ בעל ×©× + + + + Constraint name + ×©× ×ילוץ + + + + On conflict + On conflict + + + + Collate + table constraints + ×יסוף + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + הזנת ×©× ×œ×ילוץ. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + ×יסוף + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + ××™×œ×•×¦×™× + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + הוספת ×ילוץ טבלה + + + + Edit table constraint + table window + הוספת ×ילוץ טבלה + + + + Delete table constraint + table window + מחיקת ×ילוץ טבלה + + + + Move table constraint up + table window + העברת ×ילוץ מעלה + + + + Move table constraint down + table window + העברת ×ילוץ מטה + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + הוספת ×ילוץ טבלה יחוד××™ + + + + Add table check constraint + table window + הוספת ×ילוץ בקרת טבלה + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + ×”×× ×œ×ž×—×•×§ ×ילוץ '%1'? + + + + Delete constraint + table window + מחיקת ×ילוץ + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + ×©× ×•×™×™× ×œ× ×ž×§×•×‘×¢×™× + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_hu_HU.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_hu_HU.ts new file mode 100644 index 0000000..a63da83 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_hu_HU.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it.qm b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it.qm and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it.ts deleted file mode 100644 index e1651a8..0000000 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it.ts +++ /dev/null @@ -1,6612 +0,0 @@ - - - - - AboutDialog - - - About SQLiteStudio and licenses - - - - - About - - - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - - Licenses - - - - - Environment - - - - - Icon directories - - - - - Form directories - - - - - Plugin directories - - - - - Configuration directory - - - - - Application directory - - - - - Qt version: - - - - - SQLite 3 version: - - - - - Portable distribution. - - - - - MacOS X application boundle distribution. - - - - - Operating system managed distribution. - - - - - Copy - - - - - <h3>Table of contents:</h3><ol>%2</ol> - - - - - BindParamsDialog - - - Query parameters - - - - - Please provide values for query parameters - - - - - CollationsEditor - - - Filter collations - - - - - Databases - - - - - Register in all databases - - - - - Register in following databases: - - - - - Implementation code: - - - - - Collation name: - - - - - Implementation language: - - - - - Collations editor - - - - - Commit all collation changes - - - - - Rollback all collation changes - - - - - Create new collation - - - - - Delete selected collation - - - - - Editing collations manual - - - - - Enter a non-empty, unique name of the collation. - - - - - Pick the implementation language. - - - - - Enter a non-empty implementation code. - - - - - Collations editor window has uncommitted modifications. - - - - - ColorButton - - - Pick a color - - - - - ColumnCollatePanel - - - Collation name: - - - - - Named constraint: - - - - - Enter a name of the constraint. - - - - - Enter a collation name. - - - - - ColumnDefaultPanel - - - Default value: - - - - - Named constraint: - - - - - Enter a default value expression. - - - - - Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - - - - - Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - - - - - Enter a name of the constraint. - - - - - ColumnDialog - - - Column - - - - - Name and type - - - - - Scale - - - - - Precision - - - - - Data type: - - - - - Column name: - - - - - Size: - - - - - Constraints - - - - - Unique - - - - - - - - - - - Configure - - - - - Foreign Key - - - - - Collate - - - - - Not NULL - - - - - Check condition - - - - - Primary Key - - - - - Default - - - - - Advanced mode - - - - - Add constraint - column dialog - - - - - Edit constraint - column dialog - - - - - - Delete constraint - column dialog - - - - - Move constraint up - column dialog - - - - - Move constraint down - column dialog - - - - - Add a primary key - column dialog - - - - - Add a foreign key - column dialog - - - - - Add an unique constraint - column dialog - - - - - Add a check constraint - column dialog - - - - - Add a not null constraint - column dialog - - - - - Add a collate constraint - column dialog - - - - - Add a default constraint - column dialog - - - - - Are you sure you want to delete constraint '%1'? - column dialog - - - - - Correct the constraint's configuration. - - - - - This constraint is not officially supported by SQLite 2, -but it's okay to use it. - - - - - Scale is not allowed for INTEGER PRIMARY KEY columns. - - - - - Precision cannot be defined without the scale. - - - - - Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - - - - - INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - - - - - Precision is not allowed for INTEGER PRIMARY KEY columns. - - - - - ColumnDialogConstraintsModel - - - Type - column dialog constraints - - - - - Name - column dialog constraints - - - - - Details - column dialog constraints - - - - - ColumnForeignKeyPanel - - - Foreign table: - - - - - Foreign column: - - - - - Reactions - - - - - Deferred foreign key - - - - - Named constraint - - - - - Constraint name - - - - - Pick the foreign table. - - - - - Pick the foreign column. - - - - - Enter a name of the constraint. - - - - - ColumnPrimaryKeyPanel - - - Autoincrement - - - - - Sort order: - - - - - Named constraint: - - - - - On conflict: - - - - - Enter a name of the constraint. - - - - - ColumnUniqueAndNotNullPanel - - - Named constraint: - - - - - On conflict: - - - - - Enter a name of the constraint. - - - - - CompleterWindow - - - Column: %1 - completer statusbar - - - - - Table: %1 - completer statusbar - - - - - Index: %1 - completer statusbar - - - - - Trigger: %1 - completer statusbar - - - - - View: %1 - completer statusbar - - - - - Database: %1 - completer statusbar - - - - - Keyword: %1 - completer statusbar - - - - - Function: %1 - completer statusbar - - - - - Operator: %1 - completer statusbar - - - - - String - completer statusbar - - - - - Number - completer statusbar - - - - - Binary data - completer statusbar - - - - - Collation: %1 - completer statusbar - - - - - Pragma function: %1 - completer statusbar - - - - - ConfigDialog - - - - Configuration - - - - - Search - - - - - General - - - - - Keyboard shortcuts - - - - - Look & feel - - - - - Style - - - - - Fonts - - - - - Colors - - - - - Plugins - - - - - Code formatters - - - - - Data browsing - - - - - Data editors - - - - - Database dialog window - - - - - <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> - - - - - Do not mark database to be "permanent" by default - - - - - <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> - - - - - Try to bypass dialog completly when dropping database file onto the list - - - - - Data browsing and editing - - - - - Number of data rows per page: - - - - - - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - - - - - Limit initial data column width to (in pixels): - - - - - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> - - - - - Show column and row details tooltip in data view - - - - - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - - - - - Inserting new row in data grid - - - - - Before currently selected row - - - - - After currently selected row - - - - - At the end of data view - - - - - <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - - - - - <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - - - - - Place data tab as first tab in a Table Window - - - - - <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - - - - - <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - - - - - Place data tab as first tab in a View Window - - - - - Data types - - - - - Available editors: - - - - - Editors selected for this data type: - - - - - Schema editing - - - - - Number of DDL changes kept in history. - - - - - DDL history size: - - - - - SQL queries - - - - - - Number of queries kept in the history. - - - - - History size: - - - - - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> - - - - - Execute only the query under the cursor - - - - - Updates - - - - - Automatically check for updates at startup - - - - - Session - - - - - Restore last session (active MDI windows) after startup - - - - - Status Field - - - - - <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> - - - - - Always open Status panel when new message is printed - - - - - Filter shortcuts by name or key combination - - - - - Action - - - - - Key combination - - - - - - Language - - - - - Changing language requires application restart to take effect. - - - - - Compact layout - - - - - <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - - - - - Use compact layout - - - - - - Database list - - - - - If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. - - - - - Sort table columns alphabetically - - - - - Expand tables node when connected to a database - - - - - <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> - - - - - Display additional labels on the list - - - - - For regular tables labels will show number of columns, indexes and triggers for each of tables. - - - - - Display labels for regular tables - - - - - Virtual tables will be marked with a 'virtual' label. - - - - - Display labels for virtual tables - - - - - Expand views node when connected to a database - - - - - If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) - - - - - Sort objects (tables, indexes, triggers and views) alphabetically - - - - - Display system tables and indexes on the list - - - - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - - - - - Number of memorized table populating configurations - - - - - Keep NULL value when entering empty value - - - - - <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> - - - - - Use DEFAULT value (if defined), when committing NULL value - - - - - Table windows - - - - - Open Table Windows with the data tab for start - - - - - View windows - - - - - Open View Windows with the data tab for start - - - - - Don't show DDL preview dialog when committing schema changes - - - - - - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - - - - - Number of memorized query parameters - - - - - Main window dock areas - - - - - Left and right areas occupy corners - - - - - Top and bottom areas occupy corners - - - - - Hide built-in plugins - - - - - Current style: - - - - - Preview - - - - - Enabled - - - - - Disabled - - - - - Active formatter plugin - - - - - SQL editor font - - - - - Database list font - - - - - Database list additional label font - - - - - Data view font - - - - - Status field font - - - - - SQL editor colors - - - - - Current line background - - - - - <p>SQL strings are enclosed with single quote characters.</p> - - - - - String foreground - - - - - <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - - - - - Bind parameter foreground - - - - - Highlighted parenthesis background - - - - - <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - - - - - BLOB value foreground - - - - - Regular foreground - - - - - Line numbers area background - - - - - Keyword foreground - - - - - Number foreground - - - - - Comment foreground - - - - - <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> - - - - - Valid objects foreground - - - - - Data view colors - - - - - <p>Any data changes will be outlined with this color, until they're committed to the database.</p> - - - - - Uncommitted data outline color - - - - - <p>In case of error while committing data changes, the problematic cell will be outlined with this color.</p> - - - - - Commit error outline color - - - - - NULL value foreground - - - - - Deleted row background - - - - - Database list colors - - - - - <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> - - - - - Additional labels foreground - - - - - Status field colors - - - - - Information message foreground - - - - - Warning message foreground - - - - - Error message foreground - - - - - Description: - plugin details - - - - - Category: - plugin details - - - - - Version: - plugin details - - - - - Author: - plugin details - - - - - Internal name: - plugin details - - - - - Dependencies: - plugin details - - - - - Conflicts: - plugin details - - - - - Plugin details - - - - - Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. - - - - - %1 (built-in) - plugins manager in configuration dialog - - - - - Details - - - - - No plugins in this category. - - - - - Add new data type - - - - - Rename selected data type - - - - - Delete selected data type - - - - - Help for configuring data type editors - - - - - ConstraintCheckPanel - - - The condition - - - - - Named constraint: - - - - - On conflict - - - - - Enter a valid condition. - - - - - Enter a name of the constraint. - - - - - ConstraintDialog - - - New constraint - constraint dialog - - - - - Create - constraint dialog - - - - - Edit constraint - dialog window - - - - - Apply - constraint dialog - - - - - Primary key - table constraints - - - - - Foreign key - table constraints - - - - - Unique - table constraints - - - - - Not NULL - table constraints - - - - - Check - table constraints - - - - - Collate - table constraints - - - - - Default - table constraints - - - - - ConstraintTabModel - - - Table - table constraints - - - - - Column (%1) - table constraints - - - - - Scope - table constraints - - - - - Type - table constraints - - - - - Details - table constraints - - - - - Name - table constraints - - - - - CssDebugDialog - - - SQLiteStudio CSS console - - - - - DataView - - - Filter data - data view - - - - - Grid view - - - - - Form view - - - - - Refresh table data - data view - - - - - First page - data view - - - - - Previous page - data view - - - - - Next page - data view - - - - - Last page - data view - - - - - Filter - - - - - Hit Enter key or press "Apply filter" button on toolbar to apply new value. - - - - - Show filter inputs per column - data view - - - - - Apply filter - data view - - - - - Commit changes for selected cells - data view - - - - - Rollback changes for selected cells - data view - - - - - Show grid view of results - sql editor - - - - - Show form view of results - sql editor - - - - - Filter by text - data view - - - - - Filter by the Regular Expression - data view - - - - - Filter by SQL expression - data view - - - - - Tabs on top - data view - - - - - Tabs at bottom - data view - - - - - Place new rows above selected row - data view - - - - - Place new rows below selected row - data view - - - - - Place new rows at the end of the data view - data view - - - - - Total number of rows is being counted. -Browsing other pages will be possible after the row counting is done. - - - - - Row: %1 - - - - - DbConverterDialog - - - Convert database - - - - - Source database - - - - - Source database version: - - - - - Target database - - - - - Target version: - - - - - This is the file that will be created as a result of the conversion. - - - - - Target file: - - - - - Name of the new database: - - - - - This is the name that the converted database will be added to SQLiteStudio with. - - - - - Select source database - - - - - Enter valid and writable file path. - - - - - Entered file exists and will be overwritten. - - - - - Enter a not empty, unique name (as in the list of databases on the left). - - - - - No valid target dialect available. Conversion not possible. - - - - - Select valid target dialect. - - - - - Database %1 has been successfully converted and now is available under new name: %2 - - - - - SQL statements conversion - - - - - Following error occurred while converting SQL statements to the target SQLite version: - - - - - Would you like to ignore those errors and proceed? - - - - - DbDialog - - - Database - - - - - Database type - - - - - Database driver - - - - - - File - - - - - Create new database file - - - - - Name (on the list) - - - - - Options - - - - - <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> - aasfd - - - - - Permanent (keep it in configuration) - - - - - Test connection - - - - - Browse for existing database file on local computer - - - - - Browse - - - - - Enter an unique database name. - - - - - This name is already in use. Please enter unique name. - - - - - <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - - - - - Enter a database file path. - - - - - This database is already on the list under name: %1 - - - - - Select a database type. - - - - - DbObjectDialogs - - - Delete table - - - - - Are you sure you want to delete table %1? - - - - - Delete index - - - - - Are you sure you want to delete index %1? - - - - - Delete trigger - - - - - Are you sure you want to delete trigger %1? - - - - - Delete view - - - - - Are you sure you want to delete view %1? - - - - - - Error while dropping %1: %2 - - - - - Delete objects - - - - - Are you sure you want to delete following objects: -%1 - - - - - Cannot start transaction. Details: %1 - - - - - Cannot commit transaction. Details: %1 - - - - - DbTree - - - Databases - - - - - Filter by name - - - - - Copy - - - - - Paste - - - - - Select all - - - - - Create a group - - - - - Delete the group - - - - - Rename the group - - - - - Import - - - - - Export the table - - - - - Import into the table - - - - - Populate table - - - - - Create similar table - - - - - Reset autoincrement sequence - - - - - Add a column - - - - - Edit the column - - - - - Delete the column - - - - - Delete selected items - - - - - Clear filter - - - - - - Erase table data - - - - - - Database - - - - - Grouping - - - - - Generate query for table - - - - - - Create group - - - - - Group name - - - - - Entry with name %1 already exists in group %2. - - - - - Delete group - - - - - Are you sure you want to delete group %1? -All objects from this group will be moved to parent group. - - - - - Are you sure you want to remove database '%1' from the list? - - - - - Are you sure you want to remove following databases from the list: -%1 - - - - - Remove database - - - - - Vacuum (%1) - - - - - Autoincrement value for table '%1' has been reset successfully. - - - - - Are you sure you want to delete all data from table(s): %1? - - - - - - Cannot import, because no import plugin is loaded. - - - - - Execution from file cancelled. Any queries executed so far have been rolled back. - - - - - &Add a database - - - - - &Edit the database - - - - - &Remove the database - - - - - &Connect to the database - - - - - &Disconnect from the database - - - - - &Export the database - - - - - Con&vert database type - - - - - Vac&uum - - - - - &Integrity check - - - - - Create a &table - - - - - Edit the t&able - - - - - Delete the ta&ble - - - - - Create an &index - - - - - Edit the i&ndex - - - - - Delete the in&dex - - - - - Create a trig&ger - - - - - Edit the trigg&er - - - - - Delete the trigge&r - - - - - Create a &view - - - - - Edit the v&iew - - - - - Delete the vi&ew - - - - - &Refresh all database schemas - - - - - Re&fresh selected database schema - - - - - Open file's directory - - - - - Execute SQL from file - - - - - - Cannot export, because no export plugin is loaded. - - - - - Integrity check (%1) - - - - - Reset autoincrement - - - - - Are you sure you want to reset autoincrement value for table '%1'? - - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - - - - - An error occurred while trying to delete data from table '%1': %2 - - - - - All data has been deleted for table '%1'. - - - - - Following objects will be deleted: %1. - - - - - Following databases will be removed from list: %1. - - - - - Remainig objects from deleted group will be moved in place where the group used to be. - - - - - %1<br><br>Are you sure you want to continue? - - - - - Delete objects - - - - - Could not execute SQL, because application has failed to start transaction: %1 - - - - - Could not open file '%1' for reading: %2 - - - - - Could not execute SQL, because application has failed to commit the transaction: %1 - - - - - Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - - - - - Finished executing %1 queries in %2 seconds. - - - - - Could not execute SQL due to error. - - - - - DbTreeItemDelegate - - - error - dbtree labels - - - - - (system table) - database tree label - - - - - (virtual) - virtual table label - - - - - (system index) - database tree label - - - - - DbTreeModel - - - Database: %1 - dbtree tooltip - - - - - Version: - dbtree tooltip - - - - - File size: - dbtree tooltip - - - - - Encoding: - dbtree tooltip - - - - - Error: - dbtree tooltip - - - - - Table : %1 - dbtree tooltip - - - - - Columns (%1): - dbtree tooltip - - - - - Indexes (%1): - dbtree tooltip - - - - - Triggers (%1): - dbtree tooltip - - - - - Copy - - - - - Move - - - - - Include data - - - - - Include indexes - - - - - Include triggers - - - - - Abort - - - - - Could not add dropped database file '%1' automatically. Manual setup is necessary. - - - - - Referenced tables - - - - - Do you want to include following referenced tables as well: -%1 - - - - - Name conflict - - - - - Following object already exists in the target database. -Please enter new, unique name, or press '%1' to abort the operation: - - - - - SQL statements conversion - - - - - Following error occurred while converting SQL statements to the target SQLite version: - - - - - Would you like to ignore those errors and proceed? - - - - - DdlHistoryWindow - - - Filter by database: - - - - - -- Queries executed on database %1 (%2) --- Date and time of execution: %3 -%4 - - - - - DDL history - - - - - DdlPreviewDialog - - - Queries to be executed - - - - - Don't show again - - - - - DebugConsole - - - SQLiteStudio Debug Console - - - - - EditorWindow - - - Query - - - - - History - - - - - Results in the separate tab - - - - - Results below the query - - - - - - SQL editor %1 - - - - - Results - - - - - Execute query - - - - - Explain query - - - - - Clear execution history - sql editor - - - - - Export results - sql editor - - - - - Create view from query - sql editor - - - - - Previous database - - - - - Next database - - - - - Show next tab - sql editor - - - - - Show previous tab - sql editor - - - - - Focus results below - sql editor - - - - - Focus SQL editor above - sql editor - - - - - Delete selected SQL history entries - sql editor - - - - - Active database (%1/%2) - - - - - Query finished in %1 second(s). Rows affected: %2 - - - - - Query finished in %1 second(s). - - - - - Clear execution history - - - - - Are you sure you want to erase the entire SQL execution history? This cannot be undone. - - - - - Cannot export, because no export plugin is loaded. - - - - - No database selected in the SQL editor. Cannot create a view for unknown database. - - - - - Editor window "%1" has uncommitted data. - - - - - ErrorsConfirmDialog - - - Errors - - - - - Following errors occured: - - - - - Would you like to proceed? - - - - - ExecFromFileDialog - - - Execute SQL from file - - - - - Input file - - - - - Path to file - - - - - Browse for file - - - - - Options - - - - - File encoding - - - - - Skip failing SQL statements - - - - - SQL scripts (*.sql);;All files (*) - - - - - Execute SQL file - - - - - Please provide file to be executed. - - - - - Provided file does not exist or cannot be read. - - - - - ExportDialog - - - Export - - - - - What do you want to export? - - - - - A database - - - - - A single table - - - - - Query results - - - - - Table to export - - - - - Database - - - - - Table - - - - - Options - - - - - When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. - - - - - Export table data - - - - - Export table indexes - - - - - Export table triggers - - - - - Note, that exporting table indexes and triggers may be unsupported by some output formats. - - - - - Select database objects to export - - - - - Export data from tables - - - - - Select all - - - - - Deselect all - - - - - - Database: - - - - - Query to export results for - - - - - Query to be executed for results: - - - - - Export format and options - - - - - Export format - - - - - Output - - - - - Exported file path - - - - - Clipboard - - - - - File - - - - - Exported text encoding: - - - - - Export format options - - - - - Cancel - - - - - - - Select database to export. - - - - - Select table to export. - - - - - Enter valid query to export. - - - - - Select at least one object to export. - - - - - You must provide a file name to export to. - - - - - Path you provided is an existing directory. You cannot overwrite it. - - - - - The directory '%1' does not exist. - - - - - The file '%1' exists and will be overwritten. - - - - - All files (*) - - - - - Pick file to export to - - - - - Internal error during export. This is a bug. Please report it. - - - - - FileExecErrorsDialog - - - Execution errors - - - - - Following errors were encountered during execution of SQL statements from the file: - - - - - SQL - - - - - Error - - - - - Statements that were executed successfully were commited. - - - - - Statements that were executed successfully were rolled back. - - - - - FontEdit - - - Choose font - font configuration - - - - - Form - - - Active SQL formatter plugin - - - - - FormView - - - Commit row - form view - - - - - Rollback row - form view - - - - - First row - form view - - - - - Previous row - form view - - - - - Next row - form view - - - - - Last row - form view - - - - - Insert new row - form view - - - - - Delete current row - form view - - - - - FunctionsEditor - - - Filter funtions - - - - - Input arguments - - - - - Undefined - - - - - Databases - - - - - Register in all databases - - - - - Register in following databases: - - - - - Type: - - - - - Function name: - - - - - Implementation language: - - - - - Initialization code: - - - - - - Function implementation code: - - - - - Final step implementation code: - - - - - SQL function editor - - - - - Commit all function changes - - - - - Rollback all function changes - - - - - Create new function - - - - - Delete selected function - - - - - Custom SQL functions manual - - - - - Add function argument - - - - - Rename function argument - - - - - Delete function argument - - - - - Move function argument up - - - - - Move function argument down - - - - - Scalar - - - - - Aggregate - - - - - Enter a non-empty, unique name of the function. - - - - - Pick the implementation language. - - - - - Per step code: - - - - - Enter a non-empty implementation code. - - - - - argument - new function argument name in function editor window - - - - - Functions editor window has uncommitted modifications. - - - - - ImportDialog - - - Import data - - - - - Table to import to - - - - - Table - - - - - Database - - - - - Data source to import from - - - - - Data source type - - - - - Options - - - - - Text encoding: - - - - - Input file: - - - - - <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> - - - - - Ignore errors - - - - - Data source options - - - - - Cancel - - - - - If you type table name that doesn't exist, it will be created. - - - - - Enter the table name - - - - - Select import plugin. - - - - - You must provide a file to import from. - - - - - The file '%1' does not exist. - - - - - Path you provided is a directory. A regular file is required. - - - - - Pick file to import from - - - - - IndexDialog - - - - Index - - - - - On table: - - - - - Index name: - - - - - Partial index condition - - - - - Unique index - - - - - Column - - - - - Collation - - - - - Sort - - - - - Delete selected indexed expression - - - - - Moves selected index column up in the order, making it more significant in the index. - - - - - Moves selected index column down in the order, making it less significant in the index. - - - - - Edit selected indexed expression - - - - - Add indexed expression - - - - - DDL - - - - - Tried to open index dialog for closed or inexisting database. - - - - - Could not process index %1 correctly. Unable to open an index dialog. - - - - - Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. - - - - - Pick the table for the index. - - - - - Select at least one column. - - - - - Enter a valid condition. - - - - - default - index dialog - - - - - Sort order - table constraints - - - - - - Error - index dialog - - - - - Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? - - - - - An error occurred while executing SQL statements: -%1 - - - - - IndexExprColumnDialog - - - Indexed expression - - - - - Expression to index - - - - - This expression is already indexed by the index. - - - - - Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. - - - - - Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. - - - - - It's forbidden to use 'SELECT' statements in indexed expressions. - - - - - Enter an indexed expression. - - - - - Invalid expression. - - - - - LanguageDialog - - - Language - - - - - Please choose language: - - - - - MainWindow - - - Database toolbar - - - - - Structure toolbar - - - - - Tools - - - - - Window list - - - - - View toolbar - - - - - Configuration widgets - - - - - Syntax highlighting engines - - - - - Data editors - - - - - Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - - - - - Running in debug mode. Debug messages are printed to the standard output. - - - - - You need to restart application to make the language change take effect. - - - - - Next window - - - - - Previous window - - - - - Hide status field - - - - - Open Debug Console - - - - - Open CSS Console - - - - - Bugs and feature &requests - - - - - Window list - menubar view menu - - - - - Open SQL &editor - - - - - Open DDL &history - - - - - Open SQL &functions editor - - - - - Open &collations editor - - - - - Open ex&tension manager - - - - - &Import - - - - - E&xport - - - - - Open confi&guration dialog - - - - - &Tile windows - - - - - Tile windows &horizontally - - - - - Tile windows &vertically - - - - - &Cascade windows - - - - - Close selected &window - - - - - Close all windows &but selected - - - - - Close &all windows - - - - - Re&store recently closed window - - - - - &Rename selected window - - - - - Report a &bug - - - - - Propose a new &feature - - - - - &About - - - - - &Licenses - - - - - Open home &page - - - - - Open fo&rum page - - - - - User &Manual - - - - - SQLite &documentation - - - - - Check for &updates - - - - - &Database - menubar - - - - - &Structure - menubar - - - - - &View - menubar - - - - - &Tools - menubar - - - - - &Help - - - - - Could not set style: %1 - main window - - - - - Cannot export, because no export plugin is loaded. - - - - - Cannot import, because no import plugin is loaded. - - - - - Rename window - - - - - Enter new name for the window: - - - - - New updates are available. <a href="%1">Click here for details</a>. - - - - - You're running the most recent version. No updates are available. - - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - - - - - Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 - - - - - Could not add database %1 to list. - - - - - MdiWindow - - - Uncommitted changes - - - - - Close anyway - - - - - Don't close - - - - - MultiEditor - - - Null value - multieditor - - - - - Configure editors for this data type - - - - - Open another tab - - - - - Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. - - - - - Deleted - multieditor - - - - - Read only - multieditor - - - - - MultiEditorBoolPlugin - - - Boolean - - - - - MultiEditorDatePlugin - - - Date - - - - - MultiEditorDateTimePlugin - - - Date & time - - - - - MultiEditorHexPlugin - - - Hex - - - - - MultiEditorNumericPlugin - - - Number - numeric multi editor tab name - - - - - MultiEditorText - - - Tab changes focus - - - - - Cut - - - - - Copy - - - - - Paste - - - - - Delete - - - - - Undo - - - - - Redo - - - - - MultiEditorTextPlugin - - - Text - - - - - MultiEditorTimePlugin - - - Time - - - - - NewConstraintDialog - - - New constraint - - - - - - Primary Key - new constraint dialog - - - - - - Foreign Key - new constraint dialog - - - - - - Unique - new constraint dialog - - - - - - Check - new constraint dialog - - - - - Not NULL - new constraint dialog - - - - - Collate - new constraint dialog - - - - - Default - new constraint dialog - - - - - NewVersionDialog - - - SQLiteStudio updates - - - - - New updates are available! - - - - - Component - - - - - This application will be closed and the update installer will start to download and install all the updates. - - - - - Update version - - - - - Check for updates on startup - - - - - Update to new version! - - - - - Not now. - - - - - Don't install the update and close this window. - - - - - PopulateConfigDialog - - - Populating configuration - - - - - Configuring <b>%1</b> for column <b>%2</b> - - - - - PopulateDialog - - - Populate table - - - - - Database - - - - - Table - - - - - Columns - - - - - Number of rows to populate: - - - - - Populate - populate dialog button - - - - - Abort - - - - - Configure - - - - - Populating configuration for this column is invalid or incomplete. - - - - - Select database with table to populate - - - - - Select table to populate - - - - - You have to select at least one column. - - - - - QObject - - - Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). - - - - - The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. - - - - - Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. - - - - - Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. - - - - - Cannot edit results of query other than %1. - - - - - Cannot edit columns that are result of aggregated %1 statements. - - - - - Cannot edit columns that are result of %1 statement. - - - - - Cannot edit columns that are result of common table expression statement (%1). - - - - - - - - on conflict: %1 - data view tooltip - - - - - references table %1, column %2 - data view tooltip - - - - - condition: %1 - data view tooltip - - - - - collation name: %1 - data view tooltip - - - - - Data grid view - - - - - Copy cell(s) contents to clipboard - - - - - Copy cell(s) contents together with header to clipboard - - - - - Paste cell(s) contents from clipboard - - - - - Set empty value to selected cell(s) - - - - - Set NULL value to selected cell(s) - - - - - Commit changes to cell(s) contents - - - - - Rollback changes to cell(s) contents - - - - - Delete selected data row - - - - - Insert new data row - - - - - Open contents of selected cell in a separate editor - - - - - Total pages available: %1 - - - - - Total rows loaded: %1 - - - - - Data view (both grid and form) - - - - - Refresh data - - - - - Switch to grid view of the data - - - - - Switch to form view of the data - - - - - Database list - - - - - Delete selected item - - - - - Clear filter contents - - - - - Refresh schema - - - - - Refresh all schemas - - - - - Add database - - - - - Select all items - - - - - Copy selected item(s) - - - - - - - Paste from clipboard - - - - - Tables - - - - - Indexes - - - - - Triggers - - - - - Views - - - - - Columns - - - - - Data form view - - - - - Commit changes for current row - - - - - Rollback changes for current row - - - - - Go to first row on current page - - - - - Go to next row - - - - - Go to previous row - - - - - Go to last row on current page - - - - - Insert new row - - - - - Delete current row - - - - - Main window - - - - - Open SQL editor - - - - - Previous window - - - - - Next window - - - - - Hide status area - - - - - Open configuration dialog - - - - - Open Debug Console - - - - - Open CSS Console - - - - - Cell text value editor - - - - - - Cut selected text - - - - - - Copy selected text - - - - - - Delete selected text - - - - - - Undo - - - - - - Redo - - - - - SQL editor input field - - - - - Select whole editor contents - - - - - Save contents into a file - - - - - Load contents from a file - - - - - Find in text - - - - - Find next - - - - - Find previous - - - - - Replace in text - - - - - Delete current line - - - - - Request code assistant - - - - - Format contents - - - - - Move selected block of text one line down - - - - - Move selected block of text one line up - - - - - Copy selected block of text and paste it a line below - - - - - Copy selected block of text and paste it a line above - - - - - Toggle comment - - - - - All SQLite databases - - - - - All files - - - - - - Database file - - - - - SQL editor window - - - - - Execute query - - - - - Execute "%1" query - - - - - Switch current working database to previous on the list - - - - - Switch current working database to next on the list - - - - - Go to next editor tab - - - - - Go to previous editor tab - - - - - Move keyboard input focus to the results view below - - - - - Move keyboard input focus to the SQL editor above - - - - - Delete selected SQL history entries - - - - - Table window - - - - - Refresh table structure - - - - - Add new column - - - - - Edit selected column - - - - - Delete selected column - - - - - Export table data - - - - - Import data to the table - - - - - Add new table constraint - - - - - Edit selected table constraint - - - - - Delete selected table constraint - - - - - Refresh table index list - - - - - Add new index - - - - - Edit selected index - - - - - Delete selected index - - - - - Refresh table trigger list - - - - - - Add new trigger - - - - - - Edit selected trigger - - - - - - Delete selected trigger - - - - - - Go to next tab - - - - - - Go to previous tab - - - - - A view window - - - - - Refresh view trigger list - - - - - QuitConfirmDialog - - - Uncommitted changes - - - - - Are you sure you want to quit the application? - -Following items are pending: - - - - - SearchTextDialog - - - Find or replace - - - - - Find: - - - - - Case sensitive - - - - - Search backwards - - - - - Regular expression matching - - - - - Replace && -find next - - - - - Replace with: - - - - - Replace all - - - - - Find - - - - - SortDialog - - - Sort by columns - - - - - - Column - - - - - - Order - - - - - Sort by: %1 - - - - - Move column up - - - - - Move column down - - - - - SqlEditor - - - Cut - sql editor - - - - - Copy - sql editor - - - - - Paste - sql editor - - - - - Delete - sql editor - - - - - Select all - sql editor - - - - - Undo - sql editor - - - - - Redo - sql editor - - - - - Complete - sql editor - - - - - Format SQL - sql editor - - - - - Save SQL to file - sql editor - - - - - Select file to save SQL - sql editor - - - - - Load SQL from file - sql editor - - - - - Delete line - sql editor - - - - - Move block down - sql editor - - - - - Move block up - sql editor - - - - - Copy block down - sql editor - - - - - Copy up down - sql editor - - - - - Find - sql editor - - - - - Find next - sql editor - - - - - Find previous - sql editor - - - - - Replace - sql editor - - - - - Toggle comment - sql editor - - - - - Could not open file '%1' for writing: %2 - - - - - Saved SQL contents to file: %1 - - - - - Syntax completion can be used only when a valid database is set for the SQL editor. - - - - - Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. - - - - - Save to file - - - - - SQL scripts (*.sql);;All files (*) - - - - - Open file - - - - - Could not open file '%1' for reading: %2 - - - - - Reached the end of document. Hit the find again to restart the search. - - - - - SqlQueryItem - - - Column: - data view tooltip - - - - - Data type: - data view - - - - - Table: - data view tooltip - - - - - Constraints: - data view tooltip - - - - - Cannot load the data for a cell that refers to the already closed database. - - - - - SqlQueryItemDelegate - - - The row is marked for deletion. - - - - - - - - - Cannot edit this cell. Details: %1 - - - - - - Structure of this table has changed since last data was loaded. Reload the data to proceed. - - - - - Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). - - - - - Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. - - - - - SqlQueryModel - - - - Only one query can be executed simultaneously. - - - - - Cannot commit the data for a cell that refers to the already closed database. - - - - - Could not begin transaction on the database. Details: %1 - - - - - An error occurred while rolling back the transaction: %1 - - - - - Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. - - - - - Uncommitted data - - - - - There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. - - - - - An error occurred while committing the transaction: %1 - - - - - An error occurred while committing the data: %1 - - - - - Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - - - - - - Error while executing SQL query on database '%1': %2 - - - - - Error while loading query results: %1 - - - - - Insert multiple rows - - - - - Number of rows to insert: - - - - - SqlQueryView - - - Go to referenced row in... - - - - - Copy - - - - - Copy as... - - - - - Paste - - - - - Paste as... - - - - - Set NULL values - - - - - Erase values - - - - - Edit value in editor - - - - - Commit - - - - - Copy with headers - - - - - Rollback - - - - - Commit selected cells - - - - - Rollback selected cells - - - - - Define columns to sort by - - - - - Remove custom sorting - - - - - Insert row - - - - - Insert multiple rows - - - - - Delete selected row - - - - - Show value in a viewer - - - - - Generate query for selected cells - - - - - No items selected to paste clipboard contents to. - - - - - Go to referenced row in table '%1' - - - - - table '%1' - - - - - Referenced row (%1) - - - - - Trim pasted text? - - - - - The pasted text contains leading or trailing white space. Trim it automatically? - - - - - Edit value - - - - - SqlTableModel - - - Error while committing new row: %1 - - - - - Error while deleting row from table %1: %2 - - - - - SqliteExtensionEditor - - - Filter extensions - - - - - Leave empty to use default function - - - - - Extension file - - - - - Initialization function - - - - - Databases - - - - - Register in all databases - - - - - Register in following databases: - - - - - Extension manager window has uncommitted modifications. - - - - - Extension manager - - - - - Commit all extension changes - - - - - Rollback all extension changes - - - - - Add new extension - - - - - Remove selected extension - - - - - Editing extensions manual - - - - - File with given path does not exist or is not readable. - - - - - Unable to load extension: %1 - - - - - Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - - - - - Dynamic link libraries (*.dll);;All files (*) - - - - - Shared objects (*.so);;All files (*) - - - - - Dynamic libraries (*.dylib);;All files (*) - - - - - All files (*) - - - - - Open file - - - - - StatusField - - - Status - - - - - Copy - - - - - Clear - - - - - TableConstraintsModel - - - Type - table constraints - - - - - Details - table constraints - - - - - Name - table constraints - - - - - TableForeignKeyPanel - - - Foreign table: - - - - - SQLite 2 does not support foreign keys officially, -but it's okay to use them anyway. - - - - - Columns - - - - - Local column - - - - - Foreign column - - - - - Reactions - - - - - Deferred foreign key - - - - - Named constraint - - - - - Constraint name - - - - - Pick the foreign column. - - - - - Pick the foreign table. - - - - - Select at least one foreign column. - - - - - Enter a name of the constraint. - - - - - Foreign column - table constraints - - - - - TablePrimaryKeyAndUniquePanel - - - Columns - - - - - Column - - - - - Collation - - - - - Sort - - - - - Valid only for a single column with INTEGER data type - - - - - Autoincrement - - - - - Named constraint - - - - - Constraint name - - - - - On conflict - - - - - Collate - table constraints - - - - - Sort order - table constraints - - - - - Select at least one column. - - - - - Enter a name of the constraint. - - - - - TableStructureModel - - - Name - table structure columns - - - - - Data type - table structure columns - - - - - Primary -Key - table structure columns - - - - - Foreign -Key - table structure columns - - - - - Unique - table structure columns - - - - - Check - table structure columns - - - - - Not -NULL - table structure columns - - - - - Collate - table structure columns - - - - - Default value - table structure columns - - - - - TableWindow - - - Structure - - - - - Table name: - - - - - - Data - - - - - Constraints - - - - - Indexes - - - - - Triggers - - - - - DDL - - - - - Export table - table window - - - - - Import data to table - table window - - - - - Populate table - table window - - - - - Refresh structure - table window - - - - - Commit structure changes - table window - - - - - Rollback structure changes - table window - - - - - Add column - table window - - - - - Edit column - table window - - - - - - Delete column - table window - - - - - Move column up - table window - - - - - Move column down - table window - - - - - Create similar table - table window - - - - - Reset autoincrement value - table window - - - - - Add table constraint - table window - - - - - Edit table constraint - table window - - - - - Delete table constraint - table window - - - - - Move table constraint up - table window - - - - - Move table constraint down - table window - - - - - Add table primary key - table window - - - - - Add table foreign key - table window - - - - - Add table unique constraint - table window - - - - - Add table check constraint - table window - - - - - Refresh index list - table window - - - - - Create index - table window - - - - - Edit index - table window - - - - - Delete index - table window - - - - - Refresh trigger list - table window - - - - - Create trigger - table window - - - - - Edit trigger - table window - - - - - Delete trigger - table window - - - - - Are you sure you want to delete column '%1'? - table window - - - - - Following problems will take place while modifying the table. -Would you like to proceed? - table window - - - - - Table modification - table window - - - - - Could not load data for table %1. Error details: %2 - - - - - Could not process the %1 table correctly. Unable to open a table window. - - - - - Could not restore window %1, because no database or table was stored in session for this window. - - - - - Could not restore window '%1', because no database or table was stored in session for this window. - - - - - Could not restore window '%1', because database %2 could not be resolved. - - - - - Could not restore window '%1'', because the table %2 doesn't exist in the database %3. - - - - - - New table %1 - - - - - Committed changes for table '%1' successfully. - - - - - Committed changes for table '%1' (named before '%2') successfully. - - - - - Autoincrement value for table '%1' has been reset successfully. - - - - - Uncommitted changes - - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - - - - - Table window "%1" has uncommitted structure modifications and data. - - - - - Table window "%1" has uncommitted data. - - - - - Table window "%1" has uncommitted structure modifications. - - - - - Could not commit table structure. Error message: %1 - table window - - - - - Reset autoincrement - - - - - Are you sure you want to reset autoincrement value for table '%1'? - - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - - - - - Empty name - - - - - A blank name for the table is allowed in SQLite, but it is not recommended. -Are you sure you want to create a table with blank name? - - - - - Cannot create a table without at least one column. - - - - - Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. - - - - - Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. - - - - - Are you sure you want to delete table constraint '%1'? - table window - - - - - Delete constraint - table window - - - - - Cannot export, because no export plugin is loaded. - - - - - Cannot import, because no import plugin is loaded. - - - - - Go back to structure tab - - - - - Commit modifications and browse data. - - - - - Name - table window indexes - - - - - Unique - table window indexes - - - - - Columns - table window indexes - - - - - Partial index condition - table window indexes - - - - - Name - table window triggers - - - - - Event - table window triggers - - - - - Condition - table window triggers - - - - - Details - table window triggers - - - - - TriggerColumnsDialog - - - Trigger columns - - - - - Triggering columns: - - - - - Select all - - - - - Deselect all - - - - - TriggerDialog - - - - Trigger - - - - - On table: - - - - - Action: - - - - - - <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> - - - - - Pre-condition: - - - - - The scope is still not fully supported by the SQLite database. - - - - - Trigger name: - - - - - When: - - - - - List of columns for UPDATE OF action. - - - - - Scope: - - - - - Code: - - - - - Trigger statements to be executed. - - - - - DDL - - - - - On view: - - - - - Could not process trigger %1 correctly. Unable to open a trigger dialog. - - - - - Enter a valid condition. - - - - - Enter a valid trigger code. - - - - - Error - trigger dialog - - - - - An error occurred while executing SQL statements: -%1 - - - - - VersionConvertSummaryDialog - - - Database version convert - - - - - Following changes to the SQL statements will be made: - - - - - Before - - - - - After - - - - - ViewWindow - - - Query - - - - - View name: - - - - - Output column names - - - - - - Data - - - - - Triggers - - - - - DDL - - - - - - Could not restore window '%1', because no database or view was stored in session for this window. - - - - - Could not restore window '%1', because database %2 could not be resolved. - - - - - Could not restore window '%1', because database %2 could not be open. - - - - - Could not restore window '%1', because the view %2 doesn't exist in the database %3. - - - - - - New view %1 - - - - - Refresh the view - view window - - - - - Commit the view changes - view window - - - - - Rollback the view changes - view window - - - - - Explicit column names - - - - - Generate output column names automatically basing on result columns of the view. - - - - - Add column - view window - - - - - Edit column - view window - - - - - Delete column - view window - - - - - Move column up - view window - - - - - Move column down - view window - - - - - Refresh trigger list - view window - - - - - Create new trigger - view window - - - - - Edit selected trigger - view window - - - - - Delete selected trigger - view window - - - - - View window "%1" has uncommitted structure modifications and data. - - - - - View window "%1" has uncommitted data. - - - - - View window "%1" has uncommitted structure modifications. - - - - - Uncommitted changes - - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - - - - - Committed changes for view '%1' successfully. - - - - - Committed changes for view '%1' (named before '%2') successfully. - - - - - Could not load data for view %1. Error details: %2 - - - - - Go back to structure tab - - - - - Commit modifications and browse data. - - - - - Could not commit view changes. Error message: %1 - view window - - - - - Override columns - - - - - Currently defined columns will be overriden. Do you want to continue? - - - - - Could not determinate columns returned from the view. The query is problably incomplete or contains errors. - - - - - Name - view window triggers - - - - - Instead of - view window triggers - - - - - Condition - view window triggers - - - - - Details - table window triggers - - - - - Could not process the %1 view correctly. Unable to open a view window. - - - - - Empty name - - - - - A blank name for the view is allowed in SQLite, but it is not recommended. -Are you sure you want to create a view with blank name? - - - - - The SELECT statement could not be parsed. Please correct the query and retry. -Details: %1 - - - - - The view could not be modified due to internal SQLiteStudio error. Please report this! - - - - - The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. - - - - - Following problems will take place while modifying the view. -Would you like to proceed? - view window - - - - - View modification - view window - - - - - WidgetCover - - - Interrupt - - - - diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it_IT.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it_IT.ts new file mode 100644 index 0000000..987cdaf --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_it_IT.ts @@ -0,0 +1,7108 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + Informazioni su SQLiteStudio e licenze + + + + About + Circa + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Gestore di database SQLite gratuito, open-source e cross-platform.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Autore e manutentore attivo:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenze + + + + Environment + Ambiente + + + + Icon directories + Cartelle delle icone + + + + Form directories + Cartelle dei moduli + + + + SQLite extension directories + Cartella estensioni SQLite + + + + Plugin directories + Cartella dei plugin + + + + Configuration directory + Cartella di configurazione + + + + Application directory + Cartella dell'applicazione + + + + Qt version: + Versione Qt: + + + + SQLite 3 version: + Versione SQLite 3: + + + + Portable distribution. + Distribuzione portabile. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Sistema operativo di distribuzione gestita. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Tabella dei contenuti:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Parametri query + + + + Please provide values for query parameters + Si prega di fornire valori per i parametri di query + + + + CodeSnippetEditor + + + Filter snippets + Filtro snippet + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Scorciatoia opzionale, che funzionerà solo nel contesto della finestra assistente di codice attiva. Consente all'utente di utilizzare combinazioni di tasti, che altrimenti sarebbero in conflitto con altre scorciatoie. Avere la finestra assistente di codice come contesto necessario rende la scelta delle chiavi più versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>Il nome dello snippet verrà visualizzato nell'assistente di codice. Per accedere alla lista degli snippet l'utente deve premere la scorciatoia del assistente di codice due volte.</p></body></html> + + + + Snippet name + Nome Snippet + + + + Code assistant shortcut + Scorciatoia dell'assistente del codice + + + + Snippet code + Codice Snippet + + + + Code Snippets editor window has uncommitted modifications. + La finestra dell'editor di Snippet di codice contiene modifiche non salvate. + + + + Code Snippets editor + Editor Snippet di codice + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Sposta lo snippet in alto + + + + Move the snippet down + Sposta lo snippet in basso + + + + Code snippets manual + Manuale snippet di codice + + + + Enter a non-empty, unique name of the snippet. + Inserisci un nome non vuoto ed univoco dello snippet. + + + + Enter a non-empty snippet content. + Inserisci un contenuto di snippet non vuoto. + + + + This hotkey is not unique in context of a code assistant. + Questo tasto di scelta rapida non è univoco nel contesto di un assistente di codice. + + + + CollationsEditor + + + Filter collations + Filtro collations + + + + Databases + Database + + + + Register in all databases + Registra in tutti i database + + + + Register in following databases: + Registra nei seguenti database: + + + + Implementation code: + Codice di implementazione: + + + + Collation name: + Nome collation: + + + + Implementation language: + Linguaggio di implementazione: + + + + Collations editor + Editor collations + + + + Commit all collation changes + Esegui tutte le modifiche sulla collazione + + + + Rollback all collation changes + Ripristina tutte le modiche della collation + + + + Create new collation + Crea nuova collation + + + + Delete selected collation + Cancella collation selezionata + + + + Editing collations manual + Modifica manuale delle collations + + + + Enter a non-empty, unique name of the collation. + Inserisci un nome non vuoto e univoco della collation. + + + + Pick the implementation language. + Scegli il linguaggio di implementazione. + + + + Enter a non-empty implementation code. + Inserisci un codice di implementazione non vuoto. + + + + Collations editor window has uncommitted modifications. + La finestra dell'editor collations ha modifiche non registrate. + + + + ColorButton + + + Pick a color + Scegli un colore + + + + ColumnCollatePanel + + + Collation name: + Nome della collation: + + + + Named constraint: + Vincolo nominato: + + + + Enter a name of the constraint. + Inserire un nome per il vincolo. + + + + Enter a collation name. + Inserisci un nome di collazione. + + + + ColumnDefaultPanel + + + Default value: + Valore predefinito: + + + + Named constraint: + Vincolo nominato: + + + + Enter a default value expression. + Inserisci un'espressione di valore predefinita. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Espressione valore predefinito non valida: %1. Se si desidera utilizzare la stringa semplice come valore, ricordarsi di circondarla con caratteri di quotazione. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Espressione del valore predefinito non valida. Se vuoi usare la stringa semplice come valore, ricorda di circondarla con caratteri di quotazione. + + + + Enter a name of the constraint. + Inserisci un nome per il vincolo. + + + + ColumnDialog + + + Column + Colonna + + + + Name and type + Nome e tipo + + + + Scale + Scala + + + + Precision + Precisione + + + + Data type: + Tipo di dati: + + + + Column name: + Nome colonna: + + + + Size: + Dimensione: + + + + Constraints + Vincoli + + + + Generated value + Valore generato + + + + Unique + Unico + + + + + + + + + + + Configure + Configura + + + + Foreign Key + Chiave Esterna + + + + Collate + Raccogli + + + + Not NULL + Non NULL + + + + Check condition + Verifica condizione + + + + Primary Key + Chiave Primaria + + + + Default + Predefinito + + + + Advanced mode + Modalità avanzata + + + + Add constraint + column dialog + Aggiungi vincolo + + + + Edit constraint + column dialog + Modifica vincolo + + + + + Delete constraint + column dialog + Elimina vincolo + + + + Move constraint up + column dialog + Sposta vincolo su + + + + Move constraint down + column dialog + Sposta vincolo giù + + + + Add a primary key + column dialog + Aggiungi una chiave primaria + + + + Add a foreign key + column dialog + Aggiungi una chiave esterna + + + + Add an unique constraint + column dialog + Aggiungi un vincolo univoco + + + + Add a check constraint + column dialog + Aggiungi un vincolo di restrizione + + + + Add a not null constraint + column dialog + Aggiungi un vincolo non nullo + + + + Add a collate constraint + column dialog + Aggiungi un vincolo di ordinamento + + + + Add a generated value constraint + column dialog + Aggiungi un vincolo di valore generato + + + + Add a default constraint + column dialog + Aggiungi un vincolo predefinito + + + + Are you sure you want to delete constraint '%1'? + column dialog + Sei sicuro di voler eliminare il vincolo '%1'? + + + + Correct the constraint's configuration. + Correggi la configurazione del vincolo. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + La scala non è consentita per le colonne INTEGER PRIMARY KEY. + + + + Precision cannot be defined without the scale. + La precisione non può essere definita senza la scala. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Impossibile usare il tipo diverso da INTEGER se AUTOINCREMENT è abilitato in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + Il tipo di INTEGER è stato imposto a causa di AUTOINCREMENT abilitato in PRIMARIO CHIAVE. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + La precisione non è consentita per le colonne PRIMARIE INTEGER. + + + + Could not match valid STRICT table datatype from declared type: %1. + Impossibile abbinare il tipo di dati della tabella STRICT valido dal tipo dichiarato: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Tipo + + + + Name + column dialog constraints + Nome + + + + Details + column dialog constraints + Dettagli + + + + ColumnForeignKeyPanel + + + Foreign table: + Tabella esterna: + + + + Foreign column: + Colonna esterna: + + + + Reactions + Reazioni + + + + Deferred foreign key + Chiave esterna differita + + + + Named constraint + Vincolo nominato + + + + Constraint name + Nome del vincolo + + + + Pick the foreign table. + Scegli la tabella esterna. + + + + Pick the foreign column. + Scegli la colonna esterna. + + + + Enter a name of the constraint. + Inserisci un nome del vincolo. + + + + ColumnGeneratedPanel + + + Generating code: + Generazione codice: + + + + Explicit type: + Tipo esplicito: + + + + Use "GENERATED ALWAYS" keywords + Usa parole chiave "GENERATE ALWAYS" + + + + Named constraint: + Vincolo nominato: + + + + Enter the column value generating expression. + Inserisci il valore della colonna che genera l'espressione. + + + + Invalid value generating expression: %1. + Espressione generatrice del valore non valida: %1. + + + + Invalid value generating expression. + Espressione generatrice del valore non valida. + + + + Enter a name of the constraint. + Inserisci un nome del vincolo. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincremento + + + + Sort order: + Ordina per: + + + + Named constraint: + Vincolo nominato: + + + + On conflict: + Sul conflitto: + + + + Enter a name of the constraint. + Inserisci un nome del vincolo. + + + + Descending order is not allowed with AUTOINCREMENT. + L'ordine decrescente non è consentito con AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Vincolo nominato: + + + + On conflict: + Su conflitto: + + + + Enter a name of the constraint. + Inserisci un nome del vincolo. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Colonna: %1 + + + + Table: %1 + completer statusbar + Tabella: %1 + + + + Index: %1 + completer statusbar + Indice: %1 + + + + Trigger: %1 + completer statusbar + Attivatore: %1 + + + + View: %1 + completer statusbar + Vista: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Parola chiave: %1 + + + + Function: %1 + completer statusbar + Funzione: %1 + + + + Operator: %1 + completer statusbar + Operatore: %1 + + + + String + completer statusbar + Stringa + + + + Number + completer statusbar + Numero + + + + Binary data + completer statusbar + Dati binari + + + + Collation: %1 + completer statusbar + Collazione: %1 + + + + Pragma function: %1 + completer statusbar + Funzione Pragma: %1 + + + + Insert a code snippet + Inserisci un snippet di codice + + + + ConfigDialog + + + + Configuration + Configurazione + + + + Search + Cerca + + + + General + Generale + + + + Keyboard shortcuts + Scorciatoie da tastiera + + + + Look & feel + Aspetto + + + + Style + Stile + + + + Fonts + Caratteri + + + + Code colors + Colori del codice + + + + + Database list + Lista database + + + + Code assistant + Assistente codice + + + + Data browsing + Navigazione dati + + + + Data editors + Modificatori di dati + + + + Plugins + Plugin + + + + Code formatters + Formatori di codice + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + Se disattivato, le colonne saranno ordinate nell'ordine in cui vengono digitate nell'istruzione CREATE TABLE. + + + + Sort table columns alphabetically + Ordina le colonne della tabella alfabeticamente + + + + Expand tables node when connected to a database + Espandi il nodo delle tabelle quando connesso a un database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Ulteriori etichette sono quelle visualizzate accanto ai nomi nella lista dei database (sono blu, a meno che non siano in altro modo configurate). Abilitando questa opzione si otterranno etichette per database, database non validi e nodi aggregati (gruppo di colonne, gruppo di indici, gruppo di attivazione). Per ulteriori etichette vedere le opzioni qui sotto.<p> + + + + Display additional labels on the list + Mostra etichette aggiuntive nella lista + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + Per le tabelle regolari, le etichette mostreranno il numero di colonne, indici e trigger per ogni tabella. + + + + Display labels for regular tables + Visualizza le etichette per le tabelle regolari + + + + Virtual tables will be marked with a 'virtual' label. + Le tabelle virtuali saranno contrassegnate con un'etichetta 'virtuale'. + + + + Display labels for virtual tables + Visualizza le etichette per le tabelle virtuali + + + + Expand views node when connected to a database + Espandi il nodo delle viste quando connesso a un database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + Se questa opzione è disattivata, allora gli oggetti saranno ordinati nell'ordine in cui appaiono nella tabella sqlite_master (che è nell'ordine in cui sono stati creati) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Ordina gli oggetti (tabelle, indici, trigger e viste) alfabeticamente + + + + Display system tables and indexes on the list + Visualizza tabelle di sistema e indici nell'elenco + + + + Database dialog window + Finestra di dialogo database + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>Quando si aggiunge un nuovo database è contrassegnato come impostazione predefinita per essere "permanente" (memorizzato in configurazione). Selezionando questa opzione, ogni nuovo database NON sarà "permanente" per impostazione predefinita.</p> + + + + Do not mark database to be "permanent" by default + Non contrassegnare il database per essere "permanente" come impostazione predefinita + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>Quando questa opzione è abilitata, i file lasciati dal file manager sull'elenco dei database verranno automaticamente aggiunti all'elenco, bypassando la finestra di dialogo standard dei database. Se per vari motivi il processo automatico fallisce, verrà presentata all'utente la finestra standard.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Cerca di ignorare completamente la finestra di dialogo quando si rilascia il file del database nell'elenco + + + + Data browsing and editing + Navigazione e modifica dei dati + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Numero massimo di configurazioni della finestra di dialogo Popolazione tabella memorizzate in configurazione. Il valore di 100 dovrebbe essere sufficiente.</p> + + + + Number of memorized table populating configurations + Numero di configurazioni di popolamento delle tabelle memorizzate + + + + Data column width + Larghezza colonna dati + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>Quando l'utente inserisce nuovo valore nella colonna e il valore è più grande della larghezza corrente della colonna, l'applicazione allargherà la colonna per adattarsi al nuovo valore, ma non in modo più ampio del limite definito nell'opzione precedente.</p></body></html> + + + + Enlarge column when entering value longer than current width + Ingrandisci la colonna quando si inserisce un valore più lungo della larghezza corrente + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>Quando i dati vengono letti nella griglia la larghezza delle colonne della vista griglia viene automaticamente regolata. Questo valore limita la larghezza iniziale delle colonne a causa dell'aggiustamento, ma l'utente può ancora ridimensionare manualmente la colonna oltre questo limite.</p> + + + + Number of data rows per page: + Numero di righe di dati per pagina: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>Quando questa opzione è abilitata e l'utente tiene il puntatore del mouse su una cella in qualsiasi vista dati (risultati delle interrogazioni, dati di una tabella, una vista dati) apparirà un suggerimento con dettagli circa la cella - includendo dettagli come tipo di colonna di dati, vincoli, ROWID e altri.</p> + + + + Show column and row details tooltip in data view + Mostra i dettagli colonna e riga nella vista dati + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>Quando si modifica una cella che ha un valore NULL e si inserisce una stringa vuota come nuovo valore, allora questa opzione determina se il nuovo valore debba rimanere NULL (con questa opzione abilitata), o dovrebbe essere sovrascritto con valore di stringa vuota (con questa opzione disabilitata).</p> + + + + Keep NULL value when entering empty value + Mantiene il valore NULL quando si inserisce un valore vuoto + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Abilitare questo per imporre sempre il valore DEFAULT quando si memorizza un valore NULL per una colonna che ha definito il valore di DEFAULT, anche se la colonna può contenere valori NULL.</p><p>Disabilitare questa opzione per utilizzare il valore DEFAULT esclusivamente quando il valore NULL è memorizzato per la colonna con un vincolo NON NULL.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Usa il valore DEFAULT (se definito), quando si memorizza un valore NULL + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>Se i risultati della query contengono dozzine (o centinaia) di colonne, è più probabile che esaurirà la memoria libera del Tuo computer caricando parecchi gigabyte di dati contemporaneamente. SQLiteStudio può cercare di limitare il numero di risultati visualizzati su una pagina in tali casi per proteggere il computer. Se sei conscio che non lavori con grandi quantità di record nel database, puoi disabilitare questo limite e vedrai sempre tante righe per pagina così come sono state definite.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limita il numero di righe in caso di dozzine di colonne + + + + Inserting new row in data grid + Inserimento di una nuova riga nella griglia dei dati + + + + Before currently selected row + Prima della riga attualmente selezionata + + + + After currently selected row + Dopo la riga attualmente selezionata + + + + At the end of data view + Alla fine della vista dati + + + + Table windows + Finestra tabelle + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>Se abilitata, le finestre della tabella verranno visualizzate con la scheda dati, invece della scheda struttura.</p> + + + + Open Table Windows with the data tab for start + Apri Finestre Tabella all'avvio con la scheda dati + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>Quando abilitata, la scheda "Dati" sarà posizionata come prima scheda in ogni Finestra Tabella, invece di essere al secondo posto.</p> + + + + Place data tab as first tab in a Table Window + Posiziona la scheda dati come prima scheda in una Finestra Tabella + + + + View windows + Finestre Vista + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>Quando abilitata, Finestra Vista apparirà con la scheda dati, invece della scheda struttura.</p> + + + + Open View Windows with the data tab for start + Apri Finestre Vista all' avvio con la scheda dati + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>Quando abilitata, la scheda "Dati" sarà posizionata come prima scheda in ogni Finestra Vista, invece di essere al secondo posto.</p> + + + + Place data tab as first tab in a View Window + Posiziona la scheda dati come prima scheda in una Finestra Vista + + + + Data types + Tipi di dati + + + + Available editors: + Editor disponibili: + + + + Editors selected for this data type: + Editor selezionati per questo tipo di dati: + + + + Schema editing + Modifica dello schema + + + + Number of DDL changes kept in history. + Numero di modifiche DDL conservate nella cronologia. + + + + DDL history size: + Dimensione cronologia DDL: + + + + Don't show DDL preview dialog when committing schema changes + Non mostrare la finestra di anteprima DDL quando si salvano le modifiche allo schema + + + + SQL queries + Query SQL + + + + + Number of queries kept in the history. + Numero di query conservate nella cronologia. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>Se c'è più di una query nella finestra dell'editor SQL, allora (se questa opzione è abilitata) verrà eseguita solo una singola query - quella sotto il cursore dell'inserimento della tastiera. Altrimenti tutte le query verranno eseguite. È sempre possibile limitare le query da eseguire selezionando quelle query prima di richiamarne l'esecuzione. È inoltre possibile utilizzare scorciatoie dedicate per l'esecuzione in una modalità o nell'altra (attualmente configurate a %1 per l'esecuzione di una singola interrogazione e %2 per l'esecuzione di tutte le query).</p></body></html> + + + + History size: + Dimensione cronologia: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Numero massimo di parametri di interrogazione (:param, @param, $param, ?) memorizzati nella cronologia. Quando riutilizzi il parametro con lo stesso nome/posizione, SQLiteStudio lo preinizializzerà con il valore memorizzato più recente (sarà ancora possibile comunque cambiarlo). Il valore di 1000 dovrebbe essere sufficiente.</p> + + + + Execute only the query under the cursor + Esegue solo la query sotto il cursore + + + + Number of memorized query parameters + Numero di parametri di interrogazione memorizzati + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>Per impostazione predefinita (quando questa opzione è disabilitata) viene visualizzato un numero reale nel formato dei decimali con punto decimale. In alcuni casi, quando il numero è davvero piccolo (diverse posizioni dopo il punto decimale), la rappresentazione predefinita può apparire imprecisa. In tal caso potresti voler abilitare questa opzione per utilizzare la notazione scientifica (i.. <span style=" font-style:italic;">5,3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Usa la notazione scientifica per i numeri reali nella vista griglia + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>Quando i dati vengono letti la larghezza delle colonne della vista griglia viene automaticamente regolata. Questo valore limita la larghezza iniziale per la regolazione, ma l'utente può ancora ridimensionare manualmente la colonna oltre questo limite. Questo valore viene utilizzato anche quando si allarga la colonna su un nuovo valore più lungo inserito dall'utente (vedi l'opzione sottostante).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limita la larghezza automatica della colonna di dati (in pixel): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>La larghezza iniziale delle colonne di dati sarà impostata almeno per mostrare il nome completo della colonna nell'intestazione. Questo può ancora essere sovrascritto dal limite iniziale di larghezza della colonna specificato in pixel (l'impostazione sopra).</p></body></html> + + + + Keep at least the width to show complete column name + Mantieni la larghezza al fine di mostrare almeno il nome completo della colonna + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>Se abilitato, saranno inserite righe più lunghe della larghezza dell'editor, quindi lo scorrimento orizzontale non sarà necessario.</p></body></html> + + + + Wrap lines in SQL editor + A capo automatico di righe nell'editor SQL + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Evidenzia l'intera query che è attualmente sotto il cursore di inserimento. Essa è la query che verrà eseguita quando si preme il tasto di scelta rapida o il pulsante &quot;Esegui query&quot; (a meno che non sia configurato differentemente).</p></body></html> + + + + Highlight current query + Evidenzia la query corrente + + + + Updates + Aggiornamenti + + + + Automatically check for updates at startup + Controlla automaticamente gli aggiornamenti all'avvio + + + + Session + Sessione + + + + Restore last session (active MDI windows) after startup + Ripristina l'ultima sessione (finestre MDI attive) dopo l'avvio + + + + Allow multiple instances of the application at the same time + Consenti più istanze dell'applicazione nello stesso tempo + + + + Status Field + Campo Stato + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>Quando l'utente chiude manualmente il pannello di Stato, questa opzione fa in modo che se un nuovo messaggio viene stampato nel pannello di stato esso venga riaperto. Se è disabilitata, allora il pannello di stato può essere aperto manualmente dall'utente solo dal menu "Visualizza"</p> + + + + Always open Status panel when new message is printed + Apri sempre il pannello di stato quando viene stampato un nuovo messaggio + + + + Code syntax colors + Colori sintassi codice + + + + Keyword foreground + Parola chiave primo piano + + + + Regular foreground + Primo piano normale + + + + String foreground + Stringa in primo piano + + + + Comment foreground + Commento in primo piano + + + + Valid objects foreground + Oggetti validi in primo piano + + + + Current query background + Sfondo query corrente + + + + Bind parameter foreground + Associazione parametro primo piano + + + + Current line background + Sfondo riga corrente + + + + Matched parenthesis background + Corrispondenza parentesi background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>Puoi disabilitare l'evidenziazione della query corrente nella pagina Impostazioni generali.</p></body></html> + + + + Number foreground + Numero primo piano + + + + BLOB value foreground + Valore BLOB in primo piano + + + + Matched parenthesis foreground + Corrispondenza parentesi primo piano + + + + Reset to defaults + Ripristina impostazioni predefinite + + + + Filter shortcuts by name or key combination + Filtra le scorciatoie per nome o combinazione di tasti + + + + Action + Azione + + + + Key combination + Combinazione di tasti + + + + + Language + Lingua + + + + Changing language requires application restart to take effect. + La modifica della lingua richiede il riavvio dell'applicazione per avere effetto. + + + + Compact layout + Layout compatto + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>La disposizione compatta riduce al minimo tutti i margini e la spaziatura dell'interfaccia utente, rendendo lo spazio per visualizzare più dati. Rende l'interfaccia un po' meno estetica, ma permette di visualizzare più dati contemporaneamente.</p> + + + + Use compact layout + Usa layout compatto + + + + Main window dock areas + Aree di aggancio della finestra principale + + + + Left and right areas occupy corners + Le aree sinistra e destra occupano gli angoli + + + + Top and bottom areas occupy corners + Le aree superiore e inferiore occupano gli angoli + + + + Hide built-in plugins + Nascondi plugin integrati + + + + Current style: + Stile attuale: + + + + Preview + Anteprima + + + + Enabled + Abilitato + + + + Disabled + Disabilitato + + + + Active formatter plugin + Plugin formattatore attivo + + + + SQL editor font + Carattere editor SQL + + + + Database list font + Carattere elenco database + + + + Database list additional label font + Database list carattere etichetta aggiuntivo + + + + Data view font + Carattere vista dati + + + + Status field font + Carattere campo di stato + + + + Code assistant settings + Impostazioni dell'assistente codice + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>Se questa opzione è abilitata, l'assistente del codice verrà attivato quando l'utente preme per esempio <span style=" font-weight:700;">tableName.</span> per proporre colonne della tabella. Se l'opzione è disabilitata, l'utente dovrà premere esplicitamente il tasto di scelta rapida dell'assistente.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Attiva automaticamente l'assistente dopo aver digitato un punto dopo il nome di un oggetto + + + + Description: + plugin details + Descrizione: + + + + Category: + plugin details + Categoria: + + + + Version: + plugin details + Versione: + + + + Author: + plugin details + Autore: + + + + Internal name: + plugin details + Nome interno: + + + + Dependencies: + plugin details + Dipendenze: + + + + Conflicts: + plugin details + Conflitti: + + + + Plugin details + Dettagli plugin + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + I plugin vengono caricati/scaricati immediatamente quando selezionati/non selezionati, ma l'elenco modificato dei plugin da caricare all'avvio non viene salvato fino a quando non si effettua il commit dell'intera finestra di configurazione. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (integrato) + + + + Details + Dettagli + + + + No plugins in this category. + Nessun plugin in questa categoria. + + + + Add new data type + Aggiungi un nuovo tipo di file + + + + Rename selected data type + Rinomina il tipo di dati selezionato + + + + Delete selected data type + Elimina il tipo di dati selezionato + + + + Help for configuring data type editors + Aiuto per la configurazione degli editor di tipi di dati + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + La condizione + + + + Named constraint: + Vincolo nominato: + + + + On conflict + Su conflitto + + + + Enter a valid condition. + Inserisci una condizione valida. + + + + Enter a name of the constraint. + Inserisci un nome del vincolo. + + + + ConstraintDialog + + + New constraint + constraint dialog + Nuovo vincolo + + + + Create + constraint dialog + Crea + + + + Edit constraint + dialog window + Modifica vincolo + + + + Apply + constraint dialog + Applica + + + + Primary key + table constraints + Chiave primaria + + + + Foreign key + table constraints + Chiave Esterna + + + + Unique + table constraints + Unico + + + + Not NULL + table constraints + Non NULL + + + + Check + table constraints + Controlla + + + + Generated + table constraints + Generato + + + + Collate + table constraints + Raccogli + + + + Default + table constraints + Predefinito + + + + ConstraintTabModel + + + Table + table constraints + Tabella + + + + Column (%1) + table constraints + Colonna (%1) + + + + Scope + table constraints + Ambito + + + + Type + table constraints + Tipo + + + + Details + table constraints + Dettagli + + + + Name + table constraints + Nome + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filtra dati + + + + Grid view + Visualizzazione griglia + + + + Form view + Vista modulo + + + + Refresh table data + data view + Aggiorna dati tabella + + + + First page + data view + Prima pagina + + + + Previous page + data view + Pagina precedente + + + + Next page + data view + Pagina successiva + + + + Last page + data view + Ultima pagina + + + + Commit changes for selected cells + data view + Esegue il commit delle modifiche per le celle selezionate + + + + Rollback changes for selected cells + data view + Modifiche all'indietro per le celle selezionate + + + + Show grid view of results + data view + Mostra la visualizzazione della griglia dei risultati + + + + Show form view of results + data view + Mostra la visualizzazione del modulo dei risultati + + + + Filter by text (if contains) + data view + Filtra testualmente (se contiene) + + + + Filter strictly by text (if equals) + data view + Filtra rigorosamente testualmente (se uguale) + + + + Tabs on top + data view + Schede in alto + + + + Tabs at bottom + data view + Schede in basso + + + + Place new rows above selected row + data view + Posiziona nuove righe sopra la riga selezionata + + + + Place new rows below selected row + data view + Posiziona le nuove righe sotto la riga selezionata + + + + Place new rows at the end of the data view + data view + Posiziona nuove righe alla fine della vista dati + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Il numero totale di righe è in fase di conteggio. +L'esplorazione di altre pagine sarà possibile dopo il conteggio delle righe. + + + + Row: %1 + Riga: %1 + + + + Filter + Filtro + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Premi entrare o premi "Applica filtro" pulsante sulla barra degli strumenti per applicare un nuovo valore. + + + + Filter by the Regular Expression + data view + Filtra per espressione regolare + + + + Filter by SQL expression + data view + Filtra per espressione SQL + + + + Show filter inputs per column + data view + Mostra gli input del filtro per colonna + + + + Apply filter + data view + Applica filtro + + + + DbDialog + + + Database + Database + + + + Database type + Tipo di database + + + + Database driver + Driver del database + + + + + File + File + + + + Name (on the list) + Nome (nella lista) + + + + Options + Opzioni + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Abilita questa opzione se vuoi che il database sia memorizzato nel file di configurazione e ripristinato ogni volta che SQLiteStudio viene avviato.</p> + + + + Permanent (keep it in configuration) + Permanente (mantenerlo nella configurazione) + + + + Test connection + Verifica connessione + + + + Select new or existing file on local computer + Seleziona un file nuovo o esistente sul computer locale + + + + Browse + Sfoglia + + + + Database type not selected. + Tipo di database non selezionato. + + + + Database path not specified. + Percorso del database non specificato. + + + + Enter an unique database name. + Inserisci un nome di database univoco. + + + + This name is already in use. Please enter unique name. + Questo nome è già in uso. Inserisci un nome univoco. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>La generazione automatica del nome è stata disabilitata, perché il nome è stato modificato manualmente. Per ripristinare la generazione automatica si prega di cancellare il contenuto del campo nome.</p> + + + + Enter a database file path. + Inserire un percorso di un file database. + + + + This database is already on the list under name: %1 + Questo database è già nella lista sotto il nome: %1 + + + + Select a database type. + Selezionare un tipo di database. + + + + DbObjectDialogs + + + Delete table + Elimina tabella + + + + Are you sure you want to delete table %1? + Sei sicuro di voler eliminare la tabella %1? + + + + Delete index + Elimina indice + + + + Are you sure you want to delete index %1? + Sei sicuro di voler eliminare l'indice %1? + + + + Delete trigger + Elimina trigger + + + + Are you sure you want to delete trigger %1? + Sei sicuro di voler eliminare il trigger %1? + + + + Delete view + Elimina vista + + + + Are you sure you want to delete view %1? + Sei sicuro di voler eliminare la vista %1? + + + + + Error while dropping %1: %2 + Errore nel cancellare %1: %2 + + + + Delete objects + Elimina oggetti + + + + Are you sure you want to delete following objects: +%1 + Sei sicuro di voler eliminare i seguenti oggetti: +%1 + + + + Cannot start transaction. Details: %1 + Impossibile avviare la transazione. Dettagli: %1 + + + + Cannot commit transaction. Details: %1 + Impossibile eseguire il commit della transazione. Dettagli: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filtra per nome + + + + Copy + Copia + + + + Paste + Incolla + + + + Select all + Seleziona tutto + + + + Create a group + Crea un gruppo + + + + Delete the group + Cancella il gruppo + + + + Rename the group + Rinomina il gruppo + + + + &Add a database + &Aggiungi database + + + + &Edit the database + Modifica il databas&e + + + + &Remove the database + &Rimuovi il database + + + + &Connect to the database + &Connetti al database + + + + &Disconnect from the database + &Disconnetti dal database + + + + Import + Importa + + + + &Export the database + &Esporta il database + + + + Vac&uum + Vac&uum + + + + &Integrity check + Controllo &integrità + + + + Create a &table + Crea una &tabella + + + + Edit the t&able + Modifica la t&abella + + + + Delete the ta&ble + Cancella la ta&bella + + + + Export the table + Esporta la tabella + + + + Import into the table + Importa nella tabella + + + + Populate table + Popola tabella + + + + Create similar table + Crea una tabella simile + + + + Reset autoincrement sequence + Reimposta sequenza autoincrementale + + + + Create an &index + Crea un &indice + + + + Edit the i&ndex + Modifica 'i&ndice + + + + Delete the in&dex + Cancella l'in&dice + + + + Create a trig&ger + Crea un trig&ger + + + + Edit the trigg&er + Modifica il trigg&er + + + + Delete the trigge&r + Elimina il trigge&r + + + + Create a &view + Crea una &vista + + + + Edit the v&iew + Modifica la vista + + + + Delete the vi&ew + &Elimina la vista + + + + Add a column + Aggiungi una colonna + + + + Edit the column + Modifica la colonna + + + + Delete the column + Elimina la colonna + + + + Delete selected items + Elimina elementi selezionati + + + + Clear filter + Cancella filtro + + + + &Refresh all database schemas + Aggio&rna tutti gli schemi del database + + + + Re&fresh selected database schema + Aggiorna lo schema del database selezionato + + + + + Erase table data + Cancellare i dati della tabella + + + + Open file's directory + Apri cartella dei file + + + + Execute SQL from file + Esegui SQL da file + + + + Increase font size + database list + Aumenta dimensione carattere + + + + Decrease font size + database list + Riduci dimensione carattere + + + + + Database + Database + + + + Grouping + Raggruppamento + + + + Generate query for table + Genera query per tabella + + + + + Create group + Crea gruppo + + + + Group name + Nome del gruppo + + + + Entry with name %1 already exists in group %2. + La voce con il nome %1 esiste già nel gruppo %2. + + + + Delete group + Elimina gruppo + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Sei sicuro di voler eliminare il gruppo %1? +Tutti gli oggetti da questo gruppo verranno spostati nel gruppo principale. + + + + Are you sure you want to remove database '%1' from the list? + Sei sicuro di voler rimuovere il database '%1' dall'elenco? + + + + Are you sure you want to remove following databases from the list: +%1 + Sei sicuro di voler rimuovere i seguenti database dall'elenco: +%1 + + + + Remove database + Rimuovi database + + + + + Cannot import, because no import plugin is loaded. + Impossibile importare, perché non è stato caricato alcun plugin di importazione. + + + + + Cannot export, because no export plugin is loaded. + Impossibile esportare, perché non è stato caricato alcun plugin di esportazione. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Controllo integrità (%1) + + + + Reset autoincrement + Reimposta l'autoincremento + + + + Are you sure you want to reset autoincrement value for table '%1'? + Sei sicuro di voler ripristinare il valore dell'incremento automatico per la tabella '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + Si è verificato un errore durante il tentativo di ripristinare il valore dell'incremento automatico per la tabella '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Il valore dell'incremento automatico per la tabella '%1' è stato ripristinato con successo. + + + + Are you sure you want to delete all data from table(s): %1? + Sei sicuro di voler eliminare tutti i dati dalle tabelle: %1? + + + + An error occurred while trying to delete data from table '%1': %2 + Si è verificato un errore durante il tentativo di eliminare i dati dalla tabella '%1': %2 + + + + All data has been deleted for table '%1'. + Tutti i dati sono stati eliminati per la tabella '%1'. + + + + Following objects will be deleted: %1. + I seguenti oggetti verranno eliminati: %1. + + + + Following databases will be removed from list: %1. + I seguenti database saranno rimossi dall'elenco: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Gli oggetti rimanenti dal gruppo eliminato verranno spostati nella posizione in cui il gruppo era usato. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Sei sicuro di voler continuare? + + + + Delete objects + Elimina oggetti + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (tabella di sistema) + + + + (virtual) + virtual table label + (virtuale) + + + + (system index) + database tree label + (indice di sistema) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Versione: + + + + File size: + dbtree tooltip + Dimensione file: + + + + Encoding: + dbtree tooltip + Codifica: + + + + Error: + dbtree tooltip + Errore: + + + + Table : %1 + dbtree tooltip + Tabella: %1 + + + + Columns (%1): + dbtree tooltip + Colonne (%1): + + + + Indexes (%1): + dbtree tooltip + Indici (%1): + + + + Triggers (%1): + dbtree tooltip + Trigger (%1): + + + + Copy + Copia + + + + Move + Sposta + + + + Include data + Includi dati + + + + Include indexes + Includi indici + + + + Include triggers + Includi trigger + + + + Abort + Interrompi + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Impossibile aggiungere automaticamente il file del database eliminato '%1'. È necessaria la configurazione manuale. + + + + Referenced tables + Tabelle referenziate + + + + Do you want to include following referenced tables as well: +%1 + Vuoi includere anche le seguenti tabelle referenziate: +%1 + + + + Name conflict + Conflitto di nomi + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + L'oggetto seguente esiste già nel database di destinazione. +Inserisci un nuovo nome univoco o premi '%1' per interrompere l'operazione: + + + + SQL statements conversion + Conversione di istruzioni SQL + + + + Following error occurred while converting SQL statements to the target SQLite version: + Si è verificato un errore seguente durante la conversione di istruzioni SQL nella versione SQLite di destinazione: + + + + Would you like to ignore those errors and proceed? + Vuoi ignorare questi errori e procedere? + + + + DdlHistoryWindow + + + Filter by database: + Filtra per database: + + + + Clear entire history + Cancella tutta la cronologia + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Query eseguite nel database %1 (%2) +-- Data e ora di esecuzione: %3 +%4 + + + + Clear history + Cancella cronologia + + + + Are you sure you want to erase entire DDL history? + Sei sicuro di voler cancellare l'intera cronologia DDL? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Query da eseguire + + + + Don't show again + Non mostrare di nuovo + + + + DebugConsole + + + SQLiteStudio Debug Console + Console Debug SQLiteStudio + + + + EditorWindow + + + SQL editor + Editor SQL + + + + Query + Query + + + + History + Cronologia + + + + Results in the separate tab + Risultati nella scheda separata + + + + Results below the query + Risultati sotto la query + + + + + SQL editor %1 + Editor SQL %1 + + + + + Results + Risultati + + + + Execute query + Esegui query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Cancella cronologia esecuzione + + + + Export results + sql editor + Esporta risultati + + + + Create view from query + sql editor + Crea vista dalla query + + + + Previous database + Database precedente + + + + Next database + Prossimo database + + + + Show next tab + sql editor + Mostra la scheda successiva + + + + Show previous tab + sql editor + Mostra scheda precedente + + + + Focus results below + sql editor + Focus risultati sotto + + + + Focus SQL editor above + sql editor + Focus editor SQL sopra + + + + Delete selected SQL history entries + sql editor + Elimina le voci selezionate nella cronologia SQL + + + + Execute single query under cursor + Esegue la singola query sotto il cursore + + + + Execute all queries in editor + Esegue tutte le query dell'editor + + + + Active database (%1/%2) + Database attivo (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Interrogazione terminata in %1 secondi. Righe interessate: %2 + + + + Query finished in %1 second(s). + Interrogazione terminata in %1 secondi. + + + + Clear execution history + Cancella cronologia esecuzione + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Sei sicuro di voler cancellare l'intera cronologia di esecuzione SQL? Questo non può essere annullato. + + + + Cannot export, because no export plugin is loaded. + Impossibile esportare, perché non è stato caricato alcun plugin di esportazione. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + Nessun database selezionato nell'editor SQL. Impossibile creare una vista per un database sconosciuto. + + + + Editor window "%1" has uncommitted data. + La finestra dell'editor "%1" ha dati senza commit. + + + + ErrorsConfirmDialog + + + Errors + Errori + + + + Following errors occured: + Si sono verificati i seguenti errori: + + + + Would you like to proceed? + Ti piacerebbe procedere? + + + + ExecFromFileDialog + + + Execute SQL from file + Esegui SQL da file + + + + Input file + File in ingresso + + + + Path to file + Percorso del file + + + + Browse for file + Sfoglia per il file + + + + Options + Opzioni + + + + File encoding + Codifica file + + + + Skip failing SQL statements + Salta le istruzioni SQL fallite + + + + SQL scripts (*.sql);;All files (*) + Script SQL (*.sql);;Tutti i file (*) + + + + Execute SQL file + Esegui file SQL + + + + Please provide file to be executed. + Fornisci il file da eseguire. + + + + Provided file does not exist or cannot be read. + Il file fornito non esiste o non può essere letto. + + + + ExportDialog + + + Export + Esporta + + + + What do you want to export? + Cosa desideri esportare? + + + + A database + Un database + + + + A single table + Una singola tabella + + + + Query results + Risultati di interrogazione + + + + Table to export + Tabella da esportare + + + + Database + Database + + + + Table + Tabella + + + + Options + Opzioni + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + Quando questa opzione è deselezionata, viene esportata solo la tabella DDL (dichiarazione CREATE TABLE). + + + + Export table data + Esporta dati tabella + + + + Export table indexes + Esporta indici tabella + + + + Export table triggers + Esporta trigger tabella + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Nota, che l'esportazione di indici di tabella e trigger può non essere supportata da alcuni formati di output. + + + + Select database objects to export + Seleziona gli oggetti del database da esportare + + + + Export data from tables + Esporta dati dalle tabelle + + + + Select all + Seleziona tutto + + + + Deselect all + Deseleziona tutto + + + + + Database: + Database: + + + + Query to export results for + Interrogazione per esportare i risultati per + + + + Query to be executed for results: + Interrogazione da eseguire per i risultati: + + + + Export format and options + Formato di esportazione e opzioni + + + + Export format + Formato di esportazione + + + + Output + Output + + + + Exported file path + Percorso file esportato + + + + Clipboard + Appunti + + + + File + File + + + + Exported text encoding: + Codifica del testo esportato: + + + + Export format options + Opzioni del formato di esportazione + + + + Cancel + Annulla + + + + + + Select database to export. + Selezionare il database da esportare. + + + + Select table to export. + Selezionare la tabella da esportare. + + + + Enter valid query to export. + Inserisci una query valida per esportare. + + + + Select at least one object to export. + Selezionare almeno un oggetto da esportare. + + + + You must provide a file name to export to. + È necessario fornire un nome file su cui esportare. + + + + Path you provided is an existing directory. You cannot overwrite it. + Il percorso fornito è una directory esistente. Non puoi sovrascriverlo. + + + + The directory '%1' does not exist. + La directory '%1' non esiste. + + + + The file '%1' exists and will be overwritten. + Il file '%1' esiste e verrà sovrascritto. + + + + All files (*) + Tutti i file (*) + + + + Pick file to export to + Scegliere il file in cui esportare + + + + Internal error during export. This is a bug. Please report it. + Errore interno durante l'esportazione. Questo è un bug. Si prega di segnalarlo. + + + + FileExecErrorsDialog + + + Execution errors + Errori di esecuzione + + + + Following errors were encountered during execution of SQL statements from the file: + I seguenti errori sono stati riscontrati durante l'esecuzione di istruzioni SQL dal file: + + + + SQL + SQL + + + + Error + Errore + + + + Statements that were executed successfully were commited. + I comandi che sono stati eseguiti con successo sono stati memorizzati. + + + + Statements that were executed successfully were rolled back. + I comandi che sono stati eseguiti con successo sono stati annullati. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Impossibile modificare questa cella. Dettagli: %1 + + + + FontEdit + + + Choose font + font configuration + Scegli carattere + + + + Form + + + Active SQL formatter plugin + Plugin per formattare SQL attivo + + + + FormView + + + Commit row + form view + Committa riga + + + + Rollback row + form view + Ripristina riga + + + + First row + form view + Prima riga + + + + Previous row + form view + Riga precedente + + + + Next row + form view + Riga successiva + + + + Last row + form view + Ultima riga + + + + Insert new row + form view + Inserisci nuova riga + + + + Delete current row + form view + Elimina riga corrente + + + + FunctionsEditor + + + Filter functions + Funzioni Filtro + + + + Input arguments + Argomenti in input + + + + Undefined + Indefinito + + + + Databases + Database + + + + Register in all databases + Registra in tutti i database + + + + Register in following databases: + Registra nelle seguenti banche dati: + + + + Type: + Tipo: + + + + Function name: + Nome funzione: + + + + Implementation language: + Linguaggio di implementazione: + + + + Deterministic + Deterministico + + + + Initialization code: + Codice d'inizializzazione: + + + + + Function implementation code: + Codice implementazione funzione: + + + + Final step implementation code: + Codice di implementazione della fase finale: + + + + SQL functions editor + Editor delle funzioni SQL + + + + Commit all function changes + Commit tutte le modifiche alla funzione + + + + Rollback all function changes + Rollback tutte le modifiche della funzione + + + + Create new function + Crea nuova funzione + + + + Delete selected function + Elimina la funzione selezionata + + + + Custom SQL functions manual + Manuale personalizzato delle funzioni SQL + + + + Add function argument + Aggiungi argomento funzione + + + + Rename function argument + Rinomina argomento funzione + + + + Delete function argument + Elimina argomento funzione + + + + Move function argument up + Sposta argomento funzione in alto + + + + Move function argument down + Sposta argomento funzione in basso + + + + Scalar + Scalare + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Inserisci un nome non vuoto e univoco della funzione. + + + + Pick the implementation language. + Scegli il linguaggio di implementazione. + + + + Per step code: + Per passo di codice: + + + + Enter a non-empty implementation code. + Inserisci un codice di implementazione non vuoto. + + + + argument + new function argument name in function editor window + argomento + + + + Functions editor window has uncommitted modifications. + La finestra dell'editor Collations ha modifiche senza conferma. + + + + ImportDialog + + + Import data + Importa dati + + + + Table to import to + Tabella da importare a + + + + Table + Tabella + + + + Database + Database + + + + Data source to import from + Fonte dei dati da cui importare + + + + Data source type + Tipo di sorgente dati + + + + Options + Opzioni + + + + Text encoding: + Codifica del testo: + + + + Input file: + File di ingresso: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>Se abilitato, qualsiasi violazione del vincolo, o formato di dati non valido (numero di colonne errato), o qualsiasi altro problema riscontrato durante l'importazione verrà ignorato e l'importazione verrà continuata.</p> + + + + Ignore errors + Ignora errori + + + + Data source options + Opzioni sorgente dati + + + + Cancel + Annulla + + + + If you type table name that doesn't exist, it will be created. + Se digiti il nome di una tabella che non esiste, essa verrà creata. + + + + Enter the table name + Inserisci il nome della tabella + + + + Select import plugin. + Selezionare il plugin di importazione. + + + + You must provide a file to import from. + Devi fornire un file da cui importare. + + + + The file '%1' does not exist. + Il file '%1' non esiste. + + + + Path you provided is a directory. A regular file is required. + Il percorso fornito è una directory. È richiesto un file. + + + + Pick file to import from + Scegli il file da cui importare + + + + IndexDialog + + + + Index + Indice + + + + Column + Colonna + + + + Sort + Ordinamento + + + + Collation + Collation + + + + On table: + Su tabella: + + + + Delete selected indexed expression + Elimina l'espressione indicizzata selezionata + + + + Moves selected index column up in the order, making it more significant in the index. + Sposta la colonna dell'indice selezionata nell'ordinamento, rendendola nel'indice più significativa. + + + + Moves selected index column down in the order, making it less significant in the index. + Sposta la colonna dell'indice selezionata nell'ordinamento, rendendola nel'indice meno significativa. + + + + Partial index condition + Condizione indice parziale + + + + Unique index + Indice univoco + + + + Index name: + Nome indice: + + + + Edit selected indexed expression + Modifica l'espressione indicice selezionata + + + + Add indexed expression + Aggiungi espressione indice + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tentativo di aprire la finestra di dialogo dell'indice per un database chiuso o inesistente. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Impossibile elaborare correttamente l'indice %1. Impossibile aprire una finestra di dialogo indice. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + L'indice univoco non può avere espressioni indicizzate. Rimuovere le espressioni dalla lista sotto, o deselezionare questa opzione. + + + + Pick the table for the index. + Scegliere la tabella per l'indice. + + + + Select at least one column. + Seleziona almeno una colonna. + + + + Enter a valid condition. + Inserisci una condizione valida. + + + + default + index dialog + default + + + + Sort order + table constraints + Criterio di ordinamento + + + + + Error + index dialog + Errore + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Impossibile creare un indice univoco, perché i valori nelle colonne selezionate non sono univoci. Vuoi eseguire la query SELECT per vedere valori problematici? + + + + An error occurred while executing SQL statements: +%1 + Si è verificato un errore durante l'esecuzione dei comandi SQL: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Espressione indicizzata + + + + Expression to index + Espressione per l'indice + + + + This expression is already indexed by the index. + Questa espressione è già indicizzata dall'indice. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + La colonna deve essere indicizzata direttamente, non per espressione. Estendi questa espressione per contenere qualcosa di più del solo nome di colonna, oppure interrompi e seleziona direttamente questa colonna nella finestra di dialogo indice. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + La colonna '%1' non appartiene alla tabella coperta da questo indice. Le espressioni indicizzate possono riferirsi solo alle colonne della tabella indicizzata. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + È proibito usare istruzioni 'SELECT' in espressioni indicizzate. + + + + Enter an indexed expression. + Inserisci un'espressione indice. + + + + Invalid expression. + Espressione non valida. + + + + LanguageDialog + + + Language + Lingua + + + + Please choose language: + Scegli la lingua: + + + + MainWindow + + + Database toolbar + Barra degli strumenti del database + + + + Structure toolbar + Barra degli strumenti + + + + Tools + Strumenti + + + + Window list + Lista delle finestre + + + + View toolbar + Visualizza barra strumenti + + + + Configuration widgets + Widget di configurazione + + + + Syntax highlighting engines + Motori di evidenziazione della sintassi + + + + Data editors + Editor di dati + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Esecuzione in modalità debug. Premi %1 o usa 'Aiuto / Apri la console di debug' per aprire la console di debug. + + + + Running in debug mode. Debug messages are printed to the standard output. + Esecuzione in modalità debug. I messaggi di debug vengono stampati sullo standard output. + + + + You need to restart application to make the language change take effect. + È necessario riavviare l'applicazione per rendere effettivo il cambiamento della lingua. + + + + Open SQL &editor + Apri &editor SQL + + + + Open DDL &history + Apri &Cronologia DDL + + + + Open SQL &functions editor + Apri editor di &funzioni SQL + + + + Open code &snippets editor + Apri l'editor di &snippet di codice + + + + Open &collations editor + Apri editor &collations + + + + Open ex&tension manager + Apri gestore es&tensioni + + + + &Import + &Importa + + + + E&xport + E&sporta + + + + Open confi&guration dialog + Apri finestra di configurazione + + + + &Tile windows + Affianca fines&tre + + + + Tile windows &horizontally + Affianca finestre &orizzontalmente + + + + Tile windows &vertically + Affianca finestre &verticalmente + + + + &Cascade windows + Finestre in &cascata + + + + Next window + Finestra successiva + + + + Previous window + Finestra precedente + + + + Hide status field + Nascondi campo stato + + + + Close &all windows + Chiudi tutte le finestre + + + + Re&store recently closed window + Ripristina la finestra chiusa di recente + + + + Close current &window + Chiudi la finestra corrente + + + + Close &other windows + Chiudi &altre finestre + + + + Close windows on the &left + Chiudi le finestre a &sinistra + + + + Close windows on the &right + Chiudi le finestre a &destra + + + + Re&name selected window + Rinomina la finestra selezionata + + + + Open Debug Console + Apri Console di Debug + + + + Open CSS Console + Apri Console CSS + + + + Report a &bug + Segnala un &bug + + + + D&onate + D&ona + + + + Propose a new &feature + Proponi una nuova &funzionalità + + + + &About + &Informazioni + + + + &Licenses + &Licenze + + + + Open home &page + Apri home &page + + + + User &Manual + Manuale Utente + + + + SQLite &documentation + Documentazione SQLite + + + + Bugs and feature &requests + &Segnalazione di bug e richieste di funzionalità + + + + Quit + Esci + + + + Check for &updates + Controlla aggiornamenti + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Struttura + + + + &View + menubar + &Visualizza + + + + Window list + menubar view menu + Lista finestre + + + + &Tools + menubar + &Strumenti + + + + &Help + &Aiuto + + + + Could not set style: %1 + main window + Impossibile impostare lo stile: %1 + + + + Cannot export, because no export plugin is loaded. + Impossibile esportare, perché non è stato caricato alcun plugin di esportazione. + + + + Cannot import, because no import plugin is loaded. + Impossibile importare, perché non è stato caricato alcun plugin di importazione. + + + + Rename window + Rinomina finestra + + + + Enter new name for the window: + Inserisci un nuovo nome per la finestra: + + + + New updates are available. <a href="%1">Click here for details</a>. + Sono disponibili nuovi aggiornamenti. <a href="%1">Clicca qui per maggiori dettagli</a>. + + + + You're running the most recent version. No updates are available. + Stai usando la versione più recente. Non sono disponibili aggiornamenti. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Il database passato nei parametri della riga di comando (%1) era già nell'elenco con il nome: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Il database passato nei parametri della riga di comando (%1) è stato temporaneamente aggiunto all'elenco con nome: %2 + + + + Could not add database %1 to list. + Impossibile aggiungere il database %1 all'elenco. + + + + MdiWindow + + + Uncommitted changes + Modifiche non memorizzate + + + + Close anyway + Chiudi comunque + + + + Don't close + Non chiudere + + + + MultiEditor + + + Null value + multieditor + Valore nullo + + + + Configure editors for this data type + Configura gli editor per questo tipo di dati + + + + Open another tab + Apri un'altra scheda + + + + Foreign Key + Chiave Esterna + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Il plugin editor dati '%1' dell'editor di dati non viene caricato, mentre è definito per la modifica del tipo di dati '%1'. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Eliminato + + + + Read only + multieditor + Sola lettura + + + + MultiEditorBoolPlugin + + + Boolean + Booleano + + + + MultiEditorDatePlugin + + + Date + Data + + + + MultiEditorDateTimePlugin + + + Date & time + Data e ora + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Numero + + + + MultiEditorText + + + Tab changes focus + Cambio focus scheda + + + + Cut + Taglia + + + + Copy + Copia + + + + Paste + Incolla + + + + Delete + Elimina + + + + Undo + Annulla + + + + Redo + Ripeti + + + + MultiEditorTextPlugin + + + Text + Testo + + + + MultiEditorTimePlugin + + + Time + Tempo + + + + NewConstraintDialog + + + New constraint + Nuovo vincolo + + + + + Primary Key + new constraint dialog + Chiave Principale + + + + + Foreign Key + new constraint dialog + Chiave Esterna + + + + + Unique + new constraint dialog + Esclusivo + + + + + Check + new constraint dialog + Controllo + + + + Not NULL + new constraint dialog + Non NULL + + + + Collate + new constraint dialog + Ordinamento + + + + Generated + new constraint dialog + Generato + + + + Default + new constraint dialog + Predefinito + + + + NewVersionDialog + + + SQLiteStudio updates + Aggiornamenti di SQLiteStudio + + + + New version is available! + È disponibile una nuova versione! + + + + Download new version! + Scarica la nuova versione! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + Il pacchetto della nuova versione verrà scaricato. Starà a voi installarlo quando sarete pronti. + + + + Open SQLiteStudio home page. + Apri la home page SQLiteStudio. + + + + Read release notes && download package yourself. + Leggi le note di rilascio && scarica tu stesso il pacchetto. + + + + Just close this window. + Chiudi solo questa finestra. + + + + Check for updates on startup + Controlla gli aggiornamenti all'avvio + + + + Not now. + Non ora. + + + + PopulateConfigDialog + + + Populating configuration + Configurazione del popolamento + + + + Configuring <b>%1</b> for column <b>%2</b> + Configurazione di <b>%1</b> per la colonna <b>%2</b> + + + + PopulateDialog + + + Populate table + Popola tabella + + + + Database + Database + + + + Table + Tabella + + + + Columns + Colonne + + + + Number of rows to populate: + Numero di righe da popolare: + + + + Populate + populate dialog button + Popola + + + + Abort + Interrompi + + + + Configure + Configura + + + + Populating configuration for this column is invalid or incomplete. + La configurazione di popolamento per questa colonna non è valida o incompleta. + + + + Select database with table to populate + Seleziona il database con la tabella da popolare + + + + Select table to populate + Seleziona la tabella da popolare + + + + You have to select at least one column. + Devi selezionare almeno una colonna. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Impossibile modificare le colonne che sono il risultato di %1 istruzioni composte (una che include %2, %3 o %4 parole chiave). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + Il meccanismo di esecuzione della query ha avuto problemi con l'estrazione corretta dei ROWID. Questo potrebbe essere un bug nell'applicazione. Potresti volerlo segnalare. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + La colonna richiesta è il risultato di un'espressione SQL invece di una semplice selezione di colonne. Tali colonne non possono essere modificate. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + La colonna richiesta appartiene alla tabella SQLite limitata. Queste tabelle non possono essere modificate direttamente. + + + + Cannot edit results of query other than %1. + Impossibile modificare i risultati di query diversi da %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Impossibile modificare le colonne che sono il risultato di %1 istruzioni aggregate. + + + + Cannot edit columns that are result of %1 statement. + Impossibile modificare le colonne che sono il risultato dell'istruzione %1. + + + + Cannot edit columns that are result of common table expression statement (%1). + Impossibile modificare le colonne che sono il risultato di un'espressione di tabella comune (%1). + + + + Cannot edit table generated columns. + Impossibile modificare le colonne generate dalla tabella. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Impossibile modificare le colonne che sono il risultato di una vista se la query eseguita legge da qualsiasi vista multilivello (cioè una vista che interroga un'altra vista). + + + + + + + on conflict: %1 + data view tooltip + su conflitto: %1 + + + + references table %1, column %2 + data view tooltip + riferimento tabella %1, colonna %2 + + + + condition: %1 + data view tooltip + condizione: %1 + + + + collation name: %1 + data view tooltip + nome dell'ordinamento: %1 + + + + Data grid view + Visualizzazione griglia dati + + + + Edit current cell inline + Modifica la cella attuale inline + + + + Copy cell(s) contents to clipboard + Copia il contenuto delle celle negli appunti + + + + Copy cell(s) contents together with header to clipboard + Copia i contenuti delle celle insieme all'intestazione negli appunti + + + + Paste cell(s) contents from clipboard + Incolla il contenuto delle celle dagli appunti + + + + Set empty value to selected cell(s) + Imposta valore vuoto per le celle selezionate + + + + Set NULL value to selected cell(s) + Imposta valore NULL per le celle selezionate + + + + Commit changes to cell(s) contents + Esegue il commit delle modifiche ai contenuti delle celle + + + + Rollback changes to cell(s) contents + Annulla e ripristina le modifiche ai contenuti delle celle + + + + Delete selected data row + Elimina riga di dati selezionata + + + + Insert new data row + Inserisci nuova riga di dati + + + + Open contents of selected cell in a separate editor + Apre il contenuto della cella selezionata in un editor separato + + + + Toggle the height adjustment of rows + Attiva/disattiva la regolazione dell'altezza delle righe + + + + Increase font size + data view + Aumenta dimensione carattere + + + + Decrease font size + data view + Riduci dimensione carattere + + + + Total pages available: %1 + Pagine totali disponibili: %1 + + + + Total rows loaded: %1 + Righe totali caricate: %1 + + + + Data view (both grid and form) + Visualizzazione dati (sia griglia che form) + + + + Refresh data + Aggiorna dati + + + + Switch to grid view of the data + Passa alla visualizzazione a griglia dei dati + + + + Switch to form view of the data + Passa alla visualizzazione del modulo dei dati + + + + Database list + Lista database + + + + Delete selected item + Elimina elemento selezionato + + + + Clear filter contents + Cancella contenuto filtro + + + + Refresh schema + Aggiorna schema + + + + Refresh all schemas + Aggiorna tutti gli schemi + + + + Add database + Aggiungi database + + + + Select all items + Seleziona tutti gli elementi + + + + Copy selected item(s) + Copia gli elementi selezionati + + + + + + Paste from clipboard + Incolla dagli appunti + + + + Increase font size + database list + Aumenta dimensione carattere + + + + Decrease font size + database list + Riduci dimensione carattere + + + + Tables + Tabelle + + + + Indexes + Indici + + + + Triggers + Triggers + + + + Views + Viste + + + + Columns + Colonne + + + + Data form view + Vista modulo dati + + + + Commit changes for current row + Commit delle modifiche per la riga corrente + + + + Rollback changes for current row + Annulla cambiamenti per la riga corrente + + + + Go to first row on current page + Va alla prima riga della pagina corrente + + + + Go to next row + Vai alla riga successiva + + + + Go to previous row + Va alla riga precedente + + + + Go to last row on current page + Va all'ultima riga nella pagina corrente + + + + Insert new row + Inserisci nuova riga + + + + Delete current row + Elimina riga corrente + + + + Main window + Finestra principale + + + + Open SQL editor + Apri editor SQL + + + + Open DDL history window + Apri finestra cronologia DDL + + + + Open snippets editor window + Apri finestra di modifica snippet + + + + Open function editor window + Apri finestra editor funzione + + + + Open collation editor window + Apre la finestra dell'editor di ordinamento + + + + Open extension manager window + Apri finestra gestore estensioni + + + + Previous window + Finestra precedente + + + + Next window + Finestra successiva + + + + Hide status area + Nascondi area di stato + + + + Open user manual + Apri manuale utente + + + + Open configuration dialog + Apri finestra di configurazione + + + + Open Debug Console + Apri Console di Debug + + + + Open CSS Console + Apri Console CSS + + + + Open the About dialog + Apri la finestra Informazioni + + + + Quit the application + Esce dall'applicazione + + + + Cell text value editor + Editor dei valori testuali della cella + + + + + Cut selected text + Taglia il testo selezionato + + + + + Copy selected text + Copia il testo selezionato + + + + + Delete selected text + Elimina il testo selezionato + + + + + Undo + Annulla + + + + + Redo + Ripeti + + + + SQL editor input field + Campo d'immissione dell'editor SQL + + + + Select whole editor contents + Seleziona tutto il contenuto dell'editor + + + + Save contents into a file + Salva i contenuti in un file + + + + Load contents from a file + Carica contenuti da un file + + + + Find in text + Trova nel testo + + + + Find next + Trova successivo + + + + Find previous + Trova precedente + + + + Replace in text + Sostituisci nel testo + + + + Delete current line + Elimina la riga corrente + + + + Request code assistant + Richiedi assistente codice + + + + Format contents + Formato contenuti + + + + Move selected block of text one line down + Sposta il blocco di testo selezionato una riga verso il basso + + + + Move selected block of text one line up + Sposta il blocco di testo selezionato di una riga in alto + + + + Copy selected block of text and paste it a line below + Copia il blocco di testo selezionato e incollalo una riga sotto + + + + Copy selected block of text and paste it a line above + Copia il blocco di testo selezionato e incollalo una riga sopra + + + + Toggle comment + Attiva/Disattiva commento + + + + Increase font size + sql editor + Aumenta dimensione carattere + + + + Decrease font size + sql editor + Riduci dimensione carattere + + + + All SQLite databases + Tutti i database SQLite + + + + All files + Tutti i file + + + + Select database file + Seleziona file database + + + + Select + Seleziona + + + + File type + Tipo di file + + + + SQL editor window + Finestra editor SQL + + + + Execute query + Esegui query + + + + Execute single query under cursor + Esegue una singola interrogazione sotto il cursore + + + + Execute all queries in editor + Esegue tutte le query dell'editor + + + + Execute "%1" query + Esegui "%1" query + + + + Switch current working database to previous on the list + Passa il database corrente di lavoro a quello precedente nella lista + + + + Switch current working database to next on the list + Passa il database corrente di lavoro a quello successivo nella lista + + + + Go to next editor tab + Vai alla scheda successiva dell'editor + + + + Go to previous editor tab + Vai alla scheda precedente dell'editor + + + + Move keyboard input focus to the results view below + Sposta il focus sull'input della tastiera nella vista dei risultati qui sotto + + + + Move keyboard input focus to the SQL editor above + Sposta il focus sull'input della tastiera nell'editor SQL sopra + + + + Delete selected SQL history entries + Elimina le voci selezionate nella cronologia SQL + + + + Table window + Finestra tabella + + + + Commit the table structure + Commit della struttura della tabella + + + + Rollback pending changes in the table structure + Non memorizzare le modifiche nella struttura della tabella + + + + Refresh table structure + Aggiorna struttura tabella + + + + Add new column + Aggiungi nuova colonna + + + + Edit selected column + Modifica la colonna selezionata + + + + Delete selected column + Elimina la colonna selezionata + + + + Export table data + Esporta dati tabella + + + + Import data to the table + Importa dati nella tabella + + + + Add new table constraint + Aggiungi nuovo vincolo di tabella + + + + Edit selected table constraint + Modifica il vincolo di tabella selezionato + + + + Delete selected table constraint + Elimina il vincolo di tabella selezionato + + + + Refresh table index list + Aggiorna elenco indici tabella + + + + Add new index + Aggiungi un nuovo indice + + + + Edit selected index + Modifica l'indice selezionato + + + + Delete selected index + Elimina l'indice selezionato + + + + Refresh table trigger list + Aggiorna elenco trigger tabella + + + + + Add new trigger + Aggiungi nuovo trigger + + + + + Edit selected trigger + Modifica il trigger selezionato + + + + + Delete selected trigger + Elimina il trigger selezionato + + + + + Go to next tab + Vai alla scheda successiva + + + + + Go to previous tab + Vai alla scheda precedente + + + + A view window + Una finestra di visualizzazione + + + + Commit the view's query + Salva le query della vista + + + + Rollback pending changes in the view's query + Annulla le modifiche effettuate nella vista query + + + + Refresh view trigger list + Aggiorna vista elenco trigger + + + + Execute the view's query + Esegue la query della vista + + + + A code snippets editor window + Una finestra di modifica degli snippet di codice + + + + + + + Commit the pending changes + Esegue il commit delle modifiche in sospeso + + + + + + + Rollback the pending changes + Annulla le modifiche in sospeso + + + + A collation editor window + Una finestra di modifica dell'ordinamento + + + + A function editor window + Una finestra di modifica delle funzioni + + + + A SQLite extension editor window + Una finestra di modifica delle estensioni SQLite + + + + QuitConfirmDialog + + + Uncommitted changes + Modifiche senza commit + + + + Are you sure you want to quit the application? + +Following items are pending: + Sei sicuro di voler uscire dall'applicazione? + +I seguenti elementi sono in sospeso: + + + + SearchTextDialog + + + Find or replace + Trova o sostituisci + + + + Find: + Trova: + + + + Case sensitive + Distingui maiuscole + + + + Search backwards + Cerca all'indietro + + + + Regular expression matching + Espressione regolare corrispondente + + + + Replace && +find next + Sostituisci && +trova il prossimo + + + + Replace with: + Sostituisci con: + + + + Replace all + Sostituisci tutti + + + + Find + Trova + + + + SortDialog + + + Sort by columns + Ordina per colonne + + + + + Column + Colonna + + + + + Order + Ordine + + + + Sort by: %1 + Ordina per: %1 + + + + Move column up + Sposta colonna in alto + + + + Move column down + Sposta colonna in basso + + + + SqlEditor + + + Wrap words + sql editor + A capo automatico + + + + Cut + sql editor + Taglia + + + + Copy + sql editor + Copia + + + + Paste + sql editor + Incolla + + + + Delete + sql editor + Elimina + + + + Select all + sql editor + Seleziona tutto + + + + Undo + sql editor + Annulla + + + + Redo + sql editor + Ripeti + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Formato SQL + + + + Save SQL to file + sql editor + Salva SQL su file + + + + Select file to save SQL + sql editor + Seleziona file per salvare l'SQL + + + + Load SQL from file + sql editor + Carica SQL da file + + + + Delete line + sql editor + Elimina riga + + + + Move block down + sql editor + Sposta blocco giù + + + + Move block up + sql editor + Sposta blocco su + + + + Copy block down + sql editor + Copia blocco giù + + + + Copy up down + sql editor + Copia su giù + + + + Find + sql editor + Trova + + + + Find next + sql editor + Trova successivo + + + + Find previous + sql editor + Trova precedente + + + + Replace + sql editor + Sostituisci + + + + Toggle comment + sql editor + Attiva/disattiva commento + + + + Increase font size + sql editor + Aumenta dimensione carattere + + + + Decrease font size + sql editor + Riduci dimensione carattere + + + + Could not open file '%1' for writing: %2 + Impossibile aprire il file '%1' in scrittura: %2 + + + + Saved SQL contents to file: %1 + Contenuti SQL salvati nel file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Il completamento della sintassi può essere usato solo quando è impostato un database valido per l'editor SQL. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + I contenuti dell'editor SQL sono enormi, quindi gli errori di rilevamento e gli oggetti esistenti di evidenziazione sono temporaneamente disabilitati. + + + + Save to file + Salva su file + + + + SQL scripts (*.sql);;All files (*) + Script SQL (*.sql);;Tutti i file (*) + + + + Open file + Apri file + + + + Could not open file '%1' for reading: %2 + Impossibile aprire il file '%1' in lettura: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Raggiunta la fine del documento. Premi nuovamente la ricerca per riavviare la ricerca. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Errore di commit: + + + + Column: + data view tooltip + Colonna: + + + + Data type: + data view + Tipo di dati: + + + + Table: + data view tooltip + Tabella: + + + + Constraints: + data view tooltip + Vincoli: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Impossibile modificare questa cella. Dettagli: %1 + + + + The row is marked for deletion. + La riga è contrassegnata per l'eliminazione. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + La struttura di questa tabella è cambiata da quando sono stati caricati gli ultimi dati. Ricarica i dati per procedere. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Modificare un ampio contenuto in un editor di celle in linea non è una buona idea. Può diventare lento e scomodo. È meglio modificare tali grandi contenuti in una vista modulo, o in un editor popup (disponibile sotto il menu tasto-destro). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + La chiave esterna per la colonna %2 ha più di %1 valori possibili. Sono troppi per essere visualizzati nell'elenco a discesa. È necessario modificare il valore manualmente. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Solo una query può essere eseguita contemporaneamente. + + + + Cannot execute query on undefined or invalid database. + Impossibile eseguire la query su database non definito o non valido. + + + + Cannot execute empty query. + Impossibile eseguire l'interrogazione vuota. + + + + Uncommitted data + Dati non salvati + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + Ci sono modifiche dei dati senza commit. Vuoi procedere comunque? Tutte le modifiche senza commit andranno perse. + + + + Cannot commit the data for a cell that refers to the already closed database. + Impossibile eseguire il commit dei dati per una cella che si riferisce al database già chiuso. + + + + Could not begin transaction on the database. Details: %1 + Impossibile avviare la transazione nel database. Dettagli: %1 + + + + An error occurred while committing the transaction: %1 + Si è verificato un errore durante il commit della transazione: %1 + + + + An error occurred while rolling back the transaction: %1 + Si è verificato un errore durante il ripristino della transazione: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Hai provato a salvare una cella che non è modificabile (ancora modificata e in attesa di commit)! Questo è un bug. Si prega di segnalarlo. + + + + An error occurred while committing the data: %1 + Si è verificato un errore durante il commit dei dati: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Il numero di righe per pagina è decrementato a %1 a causa del numero di colonne (%2) nella vista dati. + + + + + Error while executing SQL query on database '%1': %2 + Errore durante l'esecuzione della query SQL nel database '%1': %2 + + + + Error while loading query results: %1 + Errore durante il caricamento dei risultati della query: %1 + + + + Insert multiple rows + Inserisci righe multiple + + + + Number of rows to insert: + Numero di righe da inserire: + + + + Delete rows + Elimina righe + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + Stai per cancellare le righe appena inserite che non sono ancora committate. Numeri di riga: %1 +Tale cancellazione sarà permanente. Sei sicuro di volerle cancellare? + + + + SqlQueryView + + + Go to referenced row in... + Vai alla riga di riferimento in... + + + + Copy + Copia + + + + Copy with headers + Copia con intestazioni + + + + Copy as... + Copia come... + + + + Paste + Incolla + + + + Paste as... + Incolla come... + + + + Set NULL values + Imposta valori NULL + + + + Erase values + Cancella valori + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Committa celle selezionate + + + + Rollback selected cells + Ripristina celle selezionate + + + + Edit current cell inline + Modifica la cella attuale inline + + + + Define columns to sort by + Definire le colonne da ordinare per + + + + Remove custom sorting + Rimuovi ordinamento personalizzato + + + + Insert row + Inserisci riga + + + + Insert multiple rows + Inserisci righe multiple + + + + Delete selected row + Elimina riga selezionata + + + + Adjust height of rows + Regola l'altezza delle righe + + + + Increase font size + data view + Aumenta dimensione carattere + + + + Decrease font size + data view + Riduci dimensione carattere + + + + Invert selection + data view + Inverti selezione + + + + Edit value in editor + Modifica valore nell'editor + + + + Show value in a viewer + Mostra valore in un visualizzatore + + + + Generate query for selected cells + Genera query per le celle selezionate + + + + No items selected to paste clipboard contents to. + Nessun elemento selezionato in cui incollare il contenuto degli appunti. + + + + Cannot paste data. Details: %1 + Impossibile incollare i dati. Dettagli: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + La struttura di almeno una tabella utilizzata è cambiata dall'ultimo caricamento dei dati. Ricarica i dati per procedere. + + + + Cannot paste to a cell. Details: %1 + Impossibile incollare in una cella. Dettagli: %1 + + + + The row is marked for deletion. + La riga è contrassegnata per l'eliminazione. + + + + Cannot paste to column %1. Details: %2 + Impossibile incollare nella colonna %1. Dettagli: %2 + + + + Go to referenced row in table '%1' + Vai alla riga referenziata nella tabella '%1' + + + + table '%1' + tabella '%1' + + + + Referenced row (%1) + Riga referenziata (%1) + + + + Trim pasted text? + Tagliare il testo incollato? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + Il testo incollato contiene lo spazio bianco iniziale o finale. Taglio automaticamente? + + + + Paste "NULL" as null value? + Incollare "NULL" come valore nullo? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + Il testo incollato contiene "NULL" letterali. Vuoi considerarli come valori NULL? + + + + Edit value + Modifica valore + + + + SqlTableModel + + + Error while committing new row: %1 + Errore durante il commit della nuova riga: %1 + + + + Error while deleting row from table %1: %2 + Errore durante l'eliminazione della riga dalla tabella %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Estensioni filtro + + + + Leave empty to use default function + Lascia vuoto per usare la funzione predefinita + + + + Extension file + File di estensione + + + + Initialization function + Funzione di inizializzazione + + + + Databases + Database + + + + Register in all databases + Registra in tutti i database + + + + Register in following databases: + Registra nei seguenti database: + + + + Extension manager window has uncommitted modifications. + La finestra del gestore di estensioni ha modifiche senza commit. + + + + Extension manager + Gestore estensioni + + + + Commit all extension changes + Esegue il commit di tutte le modifiche all'estensione + + + + Rollback all extension changes + Annulla tutte le modifiche all'estensione + + + + Add new extension + Aggiungi una nuova estensione + + + + Remove selected extension + Rimuovi l'estensione selezionata + + + + Editing extensions manual + Modifica le estensioni manualmente + + + + File with given path does not exist or is not readable. + Il file con percorso fornito non esiste o non è leggibile. + + + + Unable to load extension: %1 + Impossibile caricare l'estensione: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Nome della funzione di inizializzazione non valido. Il nome della funzione può contenere solo caratteri alfanumerici e underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Librerie di collegamento dinamico (*.dll);;Tutti i file (*) + + + + Shared objects (*.so);;All files (*) + Oggetti condivisi (*.so);;Tutti i file (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Librerie dinamiche (*.dylib);;Tutti i file (*) + + + + All files (*) + Tutti i file (*) + + + + Open file + Apri file + + + + StatusField + + + Status + Stato + + + + Copy + Copia + + + + Clear + Pulisci + + + + TableConstraintsModel + + + Type + table constraints + Tipo + + + + Details + table constraints + Dettagli + + + + Name + table constraints + Nome + + + + TableForeignKeyPanel + + + Foreign table: + Tabella esterna: + + + + Columns + Colonne + + + + Local column + Colonna locale + + + + Foreign column + Colonna esterna + + + + Reactions + Reazioni + + + + Deferred foreign key + Chiave esterna differita + + + + Named constraint + Vincolo nominato + + + + Constraint name + Nome del vincolo + + + + Pick the foreign column. + Scegli la colonna esterna. + + + + Pick the foreign table. + Scegli la tabella esterna. + + + + Select at least one foreign column. + Seleziona almeno una colonna esterna. + + + + Enter a name of the constraint. + Inserisci un nome del vincolo. + + + + Foreign column + table constraints + Colonna esterna + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Colonne + + + + Column + Colonna + + + + Collation + Collation + + + + Sort + Ordinamento + + + + Valid only for a single column with INTEGER data type + Valido solo per una singola colonna con tipo di dati INTEGER + + + + Autoincrement + Autoincremento + + + + Named constraint + Vincolo nominato + + + + Constraint name + Nome del vincolo + + + + On conflict + Su conflitto + + + + Collate + table constraints + Ordinamento + + + + Sort order + table constraints + Criterio di ordinamento + + + + Select at least one column. + Seleziona almeno una colonna. + + + + Enter a name of the constraint. + Inserisci un nome del vincolo. + + + + TableStructureModel + + + Name + table structure columns + Nome + + + + Data type + table structure columns + Tipo di dati + + + + Primary +Key + table structure columns + Chiave primaria + + + + Foreign +Key + table structure columns + Foreign key + + + + Unique + table structure columns + Univoco + + + + Check + table structure columns + Controllo + + + + Not +NULL + table structure columns + Non NULL + + + + Collate + table structure columns + Ordinamento + + + + Generated + table structure columns + Generato + + + + Default value + table structure columns + Valore predefinito + + + + TableWindow + + + Structure + Struttura + + + + Table name: + Nome tabella: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Abilita/disabilitata la clausola WITHOUT ROWID sulla tabella. Tale tabella non avrà più la colonna &quot;rowid&quot; nascosta. Per tale tabella è obbligatoria una colonna PRIMARY KEY esplicita. Puoi leggere maggiori dettagli sull'argomento nella documentazione ufficiale SQLite.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Abilita/disabilita la clausola STRICT per la tabella. Tale tabella effettua una verifica rigorosa dei dati memorizzati in colonne rispetto ai tipi di dati dichiarati per queste colonne. Ciò è simile a come i tipi di dati sono di solito memorizzati nella maggior parte degli altri motori di database. Mantenerlo disabilitato per utilizzare il metodo classico SQLite (es.. nessuna forzatura di tipo). Puoi trovare maggiori dettagli nella documentazione ufficiale SQLite.</p></body></html> + + + + + Data + Dati + + + + Constraints + Vincoli + + + + Indexes + Indici + + + + Triggers + Trigger + + + + DDL + DDL + + + + Export table + table window + Esporta tabella + + + + Import data to table + table window + Importa dati nella tabella + + + + Populate table + table window + Popola tabella + + + + Refresh structure + table window + Aggiorna struttura + + + + Commit structure changes + table window + Registra modifiche struttura + + + + Rollback structure changes + table window + Ripristina modifiche struttura + + + + Add column + table window + Aggiungi colonna + + + + Edit column + table window + Modifica colonna + + + + + Delete column + table window + Elimina colonna + + + + Move column up + table window + Sposta colonna in alto + + + + Move column down + table window + Sposta colonna in basso + + + + Create similar table + table window + Crea una tabella simile + + + + Reset autoincrement value + table window + Ripristina valore autoincremento + + + + Add table constraint + table window + Aggiungi vincolo di tabella + + + + Edit table constraint + table window + Modifica vincolo di tabella + + + + Delete table constraint + table window + Elimina vincolo di tabella + + + + Move table constraint up + table window + Sposta vincolo di tabella in su + + + + Move table constraint down + table window + Sposta vincolo tabella in giù + + + + Add table primary key + table window + Aggiungi chiave primaria tabella + + + + Add table foreign key + table window + Aggiungi la chiave esterna della tabella + + + + Add table unique constraint + table window + Aggiungi un vincolo univocità di tabella + + + + Add table check constraint + table window + Aggiungi vincolo di controllo di tabella + + + + Refresh index list + table window + Aggiorna elenco indici + + + + + Create index + table window + Crea indice + + + + Edit index + table window + Modifica indice + + + + Delete index + table window + Elimina indice + + + + Refresh trigger list + table window + Aggiorna elenco trigger + + + + + Create trigger + table window + Crea trigger + + + + Edit trigger + table window + Modifica trigger + + + + Delete trigger + table window + Elimina trigger + + + + Are you sure you want to delete column '%1'? + table window + Sei sicuro di voler eliminare la colonna '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + I seguenti problemi si verificheranno durante la modifica della tabella. +Vuoi procedere? + + + + Table modification + table window + Modifica tabella + + + + Could not load data for table %1. Error details: %2 + Impossibile caricare i dati per la tabella %1. Dettagli errore: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Impossibile elaborare correttamente la tabella %1. Impossibile aprire una finestra della tabella. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Impossibile ripristinare la finestra %1, perché nessun database o tabella è stato memorizzato in sessione per questa finestra. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Impossibile ripristinare la finestra '%1', perché nessun database o tabella è stato memorizzato nella sessione per questa finestra. + + + + Could not restore window '%1', because database %2 could not be resolved. + Impossibile ripristinare la finestra '%1', perché il database %2 non può essere risolto. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Impossibile ripristinare la finestra '%1', perché la tabella %2 non esiste nel database %3. + + + + + New table %1 + Nuova tabella %1 + + + + Committed changes for table '%1' successfully. + Modifiche per la tabella '%1' memorizzate con successo. + + + + Committed changes for table '%1' (named before '%2') successfully. + Modifiche memorizzate per la tabella '%1' (nominate prima del '%2') con successo. + + + + Could not commit table structure. Error message: %1 + table window + Impossibile eseguire il commit della struttura della tabella. Messaggio di errore: %1 + + + + Reset autoincrement + Reimposta l'autoincremento + + + + Are you sure you want to reset autoincrement value for table '%1'? + Sei sicuro di voler ripristinare il valore dell'incremento automatico per la tabella '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + Si è verificato un errore durante il tentativo di ripristinare il valore dell'incremento automatico per la tabella '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Il valore dell'incremento automatico per la tabella '%1' è stato ripristinato con successo. + + + + Empty name + Nome vuoto + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + Un nome vuoto per la tabella è consentito in SQLite, ma non è raccomandato. +Sei sicuro di voler creare una tabella con il nome vuoto? + + + + Cannot create a table without at least one column. + Impossibile creare una tabella senza almeno una colonna. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Impossibile creare la tabella %1, se non ha una chiave primaria definita. Deselezionare la %2 o definire una chiave primaria. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Impossibile usare l'auto incremento per la chiave primaria quando viene utilizzata la clausola %1. Deseleziona la casella %2 o l'auto incremento in una chiave primaria. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Le colonne seguenti hanno un tipo di dati non rigoroso: %1. Disabilita la modalità rigorosa della tabella o correggi i tipi di dati delle colonne. I tipi di dati rigorosi validi sono: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Sei sicuro di voler eliminare il vincolo della tabella '%1'? + + + + Delete constraint + table window + Elimina vincolo + + + + Cannot export, because no export plugin is loaded. + Impossibile esportare, perché non è stato caricato nessun plugin di esportazione. + + + + Cannot import, because no import plugin is loaded. + Impossibile importare, perché non è stato caricato alcun plugin di importazione. + + + + Uncommitted changes + Modifiche senza commit + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + Ci sono modifiche non committate alla struttura. Non puoi sfogliare o modificare i dati finché non hai sistemato la struttura della tabella. +Vuoi committare la struttura o vuoi tornare alla scheda della struttura? + + + + Go back to structure tab + Torna alla scheda struttura + + + + Commit modifications and browse data. + Salva le modifiche e sfoglia i dati. + + + + Name + table window indexes + Nome + + + + Unique + table window indexes + Unico + + + + Columns + table window indexes + Colonne + + + + Partial index condition + table window indexes + Stato parziale dell'indice + + + + Name + table window triggers + Nome + + + + Event + table window triggers + Evento + + + + Condition + table window triggers + Condizione + + + + Details + table window triggers + Dettagli + + + + Table window "%1" has uncommitted structure modifications and data. + La finestra della tabella "%1" ha modifiche e dati della struttura senza commit. + + + + Table window "%1" has uncommitted data. + La finestra della tabella "%1" ha dati senza commit. + + + + Table window "%1" has uncommitted structure modifications. + La finestra della tabella "%1" ha modifiche della struttura senza commit. + + + + TriggerColumnsDialog + + + Trigger columns + Colonne trigger + + + + Triggering columns: + Colonne di triggering: + + + + Select all + Seleziona tutto + + + + Deselect all + Deseleziona tutto + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + Su tabella: + + + + Action: + Azione: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>Condizione SQL che sarà valutata prima del codice di trigger attuale. Nel caso in cui la condizione restituisca false, il trigger non verrà eseguito per quella riga.</p> + + + + Pre-condition: + Pre-condizione: + + + + The scope is still not fully supported by the SQLite database. + Il campo di applicazione non è ancora completamente supportato dal database SQLite. + + + + Trigger name: + Nome del trigger: + + + + When: + Quando: + + + + List of columns for UPDATE OF action. + Elenco delle colonne per l'azione UPDATE OF. + + + + Scope: + Ambito: + + + + Code: + Codice: + + + + Trigger statements to be executed. + Dichiarazioni di trigger da eseguire. + + + + DDL + DDL + + + + On view: + Su vista: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Impossibile elaborare correttamente il trigger %1. Impossibile aprire una finestra di trigger. + + + + Enter a valid condition. + Inserisci una condizione valida. + + + + Enter a valid trigger code. + Inserisci un codice di trigger valido. + + + + Error + trigger dialog + Errore + + + + An error occurred while executing SQL statements: +%1 + Si è verificato un errore durante l'esecuzione dei comandi SQL: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Converti versione database + + + + Following changes to the SQL statements will be made: + Le seguenti modifiche alle istruzioni SQL saranno effettuate: + + + + Before + Prima + + + + After + Dopo + + + + ViewWindow + + + Query + Query + + + + View name: + Nome della vista: + + + + Output column names + Nomi colonne in output + + + + + Data + Dati + + + + Triggers + Trigger + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Impossibile ripristinare la finestra '%1', perché non è stato memorizzato alcun database o vista nella sessione per questa finestra. + + + + Could not restore window '%1', because database %2 could not be resolved. + Impossibile ripristinare la finestra '%1', perché il database %2 non può essere risolto. + + + + Could not restore window '%1', because database %2 could not be open. + Impossibile ripristinare la finestra '%1', perché il database %2 non può essere aperto. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Impossibile ripristinare la finestra '%1', perché la vista %2 non esiste't nel database %3. + + + + + New view %1 + Nuova vista %1 + + + + Database + Database + + + + Refresh the view + view window + Aggiorna la vista + + + + Commit the view changes + view window + Commit delle modifiche alla vista + + + + Rollback the view changes + view window + Ripristina le modifiche della vista + + + + Explicit column names + Nomi espliciti delle colonne + + + + Generate output column names automatically basing on result columns of the view. + Genera i nomi delle colonne di output automaticamente basandosi sulle colonne di risultato della vista. + + + + Add column + view window + Aggiungi colonna + + + + Edit column + view window + Modifica colonna + + + + Delete column + view window + Cancellare Colonna + + + + Move column up + view window + Sposta colonna su + + + + Move column down + view window + Sposta colonna giù + + + + Refresh trigger list + view window + Aggiorna elenco trigger + + + + Create new trigger + view window + Crea nuovo trigger + + + + Edit selected trigger + view window + Modifica il trigger selezionato + + + + Delete selected trigger + view window + Elimina il trigger selezionato + + + + View window "%1" has uncommitted structure modifications and data. + Visualizza la finestra "%1" ha modifiche e dati della struttura senza commit. + + + + View window "%1" has uncommitted data. + Visualizza la finestra "%1" ha dati senza commit. + + + + View window "%1" has uncommitted structure modifications. + Visualizza la finestra "%1" ha modifiche della struttura senza commit. + + + + Could not load data for view %1. Error details: %2 + Impossibile caricare i dati per la vista %1. Dettagli errore: %2 + + + + Uncommitted changes + Modifiche senza commit + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + Ci sono modifiche della struttura senza commit. Non è possibile sfogliare o modificare i dati fino a quando non si dispone di una struttura di visualizzazione regolata. +Vuoi eseguire il commit della struttura, o vuoi tornare alla scheda Struttura? + + + + Go back to structure tab + Torna alla scheda struttura + + + + Commit modifications and browse data. + Commit delle modifiche e sfoglia i dati. + + + + View '%1' was committed successfully. + Vista '%1' commit effettuato con successo. + + + + Committed changes for view '%1' successfully. + Modifiche per la vista '%1' memorizzate con successo. + + + + Committed changes for view '%1' (named before '%2') successfully. + Modifiche per la vista '%1' (nominate prima del '%2') memorizzate con successo. + + + + Could not commit view changes. Error message: %1 + view window + Impossibile effettuare il commit delle modifiche alla visualizzazione. Messaggio di errore: %1 + + + + Override columns + Sovrascrivi colonne + + + + Currently defined columns will be overriden. Do you want to continue? + Le colonne attualmente definite saranno sovrascritte. Vuoi continuare? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Impossibile determinare le colonne restituite dalla vista. La query è <unk> abilmente incompleta o contiene errori. + + + + Name + view window triggers + Nome + + + + Instead of + view window triggers + Invece di + + + + Condition + view window triggers + Condizione + + + + Details + table window triggers + Dettagli + + + + Could not process the %1 view correctly. Unable to open a view window. + Impossibile elaborare correttamente la vista « %1 ». Impossibile aprire una finestra di visualizzazione. + + + + Empty name + Nome vuoto + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + Un nome vuoto per la vista è consentito in SQLite, ma non è raccomandato. +Sei sicuro di voler creare una vista con il nome vuoto? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + L'istruzione SELECT non può essere analizzata. Correggi la richiesta e riprova. +Dettagli: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + La vista non può essere modificata a causa di un errore interno di SQLiteStudio. Si prega di segnalarlo! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + Il codice di visualizzazione non può essere analizzato correttamente per l'esecuzione. Questo è un bug di SQLiteStudio's. Si prega di segnalarlo. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + I seguenti problemi si verificheranno durante la modifica della vista. +Vuoi procedere? + + + + View modification + view window + Visualizza modifica + + + + WidgetCover + + + Interrupt + Interrompi + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ja_JP.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ja_JP.ts new file mode 100644 index 0000000..1c378fb --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ja_JP.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + SQLiteStudio ã¨ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã«ã¤ã„㦠+ + + + About + SQLiteStudioã«ã¤ã„㦠+ + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">ç„¡æ–™ã®ã‚ªãƒ¼ãƒ—ンソースã®ã‚¯ãƒ­ã‚¹ãƒ—ラットフォームSQLiteデータベースマãƒãƒ¼ã‚¸ãƒ£ãƒ¼ã€‚<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">作æˆè€…ãŠã‚ˆã³ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãªãƒ¡ãƒ³ãƒ†ãƒŠ<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + ライセンス + + + + Environment + 環境 + + + + Icon directories + アイコンディレクトリ + + + + Form directories + フォームディレクトリ + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + プラグインディレクトリ + + + + Configuration directory + 設定ディレクトリ + + + + Application directory + アプリケーション ディレクトリ + + + + Qt version: + Qt ãƒãƒ¼ã‚¸ãƒ§ãƒ³: + + + + SQLite 3 version: + SQLite3 ãƒãƒ¼ã‚¸ãƒ§ãƒ³: + + + + Portable distribution. + ãƒãƒ¼ã‚¿ãƒ–ル版 + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + OSマãƒãƒ¼ã‚¸ç‰ˆ + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>目次:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + クエリパラメータ + + + + Please provide values for query parameters + クエリパラメータã®å€¤ã‚’入力ã—ã¦ãã ã•ã„ + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + ç…§åˆé †åºã‚’絞り込む + + + + Databases + データベース + + + + Register in all databases + ã™ã¹ã¦ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«ç™»éŒ²ã™ã‚‹ + + + + Register in following databases: + 次ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«ç™»éŒ²: + + + + Implementation code: + 実装コード: + + + + Collation name: + ç…§åˆé †åºå: + + + + Implementation language: + 実装言語: + + + + Collations editor + ç…§åˆé †åºã‚¨ãƒ‡ã‚£ã‚¿ + + + + Commit all collation changes + ã™ã¹ã¦ã®ç…§åˆé †åºã®å¤‰æ›´ã‚’コミット + + + + Rollback all collation changes + ã™ã¹ã¦ã®ç…§åˆé †åºã®å¤‰æ›´ã‚’ロールãƒãƒƒã‚¯ + + + + Create new collation + æ–°ã—ã„ç…§åˆé †åºã‚’ä½œæˆ + + + + Delete selected collation + é¸æŠžã—ãŸç…§åˆé †åºã‚’削除 + + + + Editing collations manual + ç…§åˆé †åºãƒžãƒ‹ãƒ¥ã‚¢ãƒ«ã®ç·¨é›† + + + + Enter a non-empty, unique name of the collation. + ç…§åˆé †åºã®ç©ºã§ãªã„一æ„ã®åå‰ã‚’入力ã—ã¾ã™ã€‚ + + + + Pick the implementation language. + å®Ÿè£…è¨€èªžã‚’é¸æŠžã—ã¾ã™ã€‚ + + + + Enter a non-empty implementation code. + 空ã§ãªã„実装コードを入力ã—ã¾ã™ã€‚ + + + + Collations editor window has uncommitted modifications. + ç…§åˆé †åºã‚¨ãƒ‡ã‚£ã‚¿ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã«ã¯ã‚³ãƒŸãƒƒãƒˆã•れã¦ã„ãªã„変更ãŒã‚りã¾ã™ã€‚ + + + + ColorButton + + + Pick a color + è‰²ã‚’é¸æŠž + + + + ColumnCollatePanel + + + Collation name: + ç…§åˆé †åºå: + + + + Named constraint: + åå‰ä»˜ã制約: + + + + Enter a name of the constraint. + 制約ã®åå‰ã‚’入力ã—ã¾ã™ã€‚ + + + + Enter a collation name. + ç…§åˆé †åºåを入力ã—ã¾ã™ã€‚ + + + + ColumnDefaultPanel + + + Default value: + デフォルト値: + + + + Named constraint: + åå‰ä»˜ã制約: + + + + Enter a default value expression. + デフォルト値ã®å¼ã‚’入力ã—ã¾ã™ã€‚ + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + デフォルト値ã®å¼%1 ã¯ç„¡åйã§ã™. å˜ç´”ãªæ–‡å­—列を値ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹å ´åˆã¯ã€å¼•用符ã§å›²ã‚“ã§ãã ã•ã„。 + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + 無効ãªãƒ‡ãƒ•ォルト値ã§ã™ã€‚å˜ç´”ãªæ–‡å­—列を値ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹å ´åˆã¯ã€å¼•用符ã§å›²ã‚“ã§ãã ã•ã„。 + + + + Enter a name of the constraint. + 制約ã®åå‰ã‚’入力ã—ã¾ã™ã€‚ + + + + ColumnDialog + + + Column + 列 + + + + Name and type + åå‰ã¨ç¨®é¡ž + + + + Scale + スケール + + + + Precision + 精度 + + + + Data type: + データ型: + + + + Column name: + 列å: + + + + Size: + サイズ: + + + + Constraints + 制約 + + + + Generated value + 生æˆã•れãŸå€¤ + + + + Unique + ä¸€æ„ + + + + + + + + + + + Configure + 設定 + + + + Foreign Key +  å¤–部キー + + + + Collate + ç…§åˆé †åº + + + + Not NULL + NULLéžè¨±å®¹ + + + + Check condition + æ¡ä»¶ã‚’ãƒã‚§ãƒƒã‚¯ + + + + Primary Key + 主キー + + + + Default + デフォルト + + + + Advanced mode + 上級者å‘ã‘モード + + + + Add constraint + column dialog + 制約ã®è¿½åŠ  + + + + Edit constraint + column dialog + 制約を編集 + + + + + Delete constraint + column dialog + 制約を削除 + + + + Move constraint up + column dialog + 制約を上ã¸ç§»å‹• + + + + Move constraint down + column dialog + 制約を下ã«ç§»å‹• + + + + Add a primary key + column dialog + 主キーを追加 + + + + Add a foreign key + column dialog + 外部キーを追加 + + + + Add an unique constraint + column dialog + 一æ„制約を追加 + + + + Add a check constraint + column dialog + ãƒã‚§ãƒƒã‚¯åˆ¶ç´„を追加 + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_kaa.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_kaa.ts new file mode 100644 index 0000000..1f97506 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_kaa.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + Baǵdarlama haqqında + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Atı + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ko_KR.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ko_KR.ts new file mode 100644 index 0000000..10a65d5 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ko_KR.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_nl_NL.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_nl_NL.ts new file mode 100644 index 0000000..671687f --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_nl_NL.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_no_NO.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_no_NO.ts new file mode 100644 index 0000000..d6bf3b3 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_no_NO.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl.qm b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl.qm deleted file mode 100644 index 5740bcd..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl.qm and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl.ts deleted file mode 100644 index e144d8b..0000000 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl.ts +++ /dev/null @@ -1,7399 +0,0 @@ - - - - - AboutDialog - - - About SQLiteStudio and licenses - O SQLiteStudio i licencje - - - - About - O programie - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Darmowy, otwartoźródÅ‚owy, wieloplatformowy menadżer baz danych SQLite.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Autor i aktywny opiekun:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - Licenses - Licencje - - - - Environment - Åšrodowisko - - - - Icon directories - Katalogi ikon - - - - Form directories - Katalogi formularzy - - - - Plugin directories - Katalogi wtyczek - - - - Application directory - Katalog aplikacji - - - - SQLite 3 version: - Wersja SQLite 3: - - - - Configuration directory - Katalog konfiguracji - - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Darmowy, otwartoźródÅ‚owy, wieloplatformowy menadżer baz danych SQLite.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Autor i aktywny opiekun:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - Qt version: - Wersja Qt: - - - - Portable distribution. - Dystrybucja przenoÅ›na. - - - - MacOS X application boundle distribution. - Dytrybucja aplikacji MacOS X. - - - - Operating system managed distribution. - Dystrybucja zarzÄ…dzana przez system operacyjny. - - - - Copy - Kopiuj - - - - <h3>Table of contents:</h3><ol>%2</ol> - <h3>Zawartość:</h3><ol>%2</ol> - - - - BindParamsDialog - - - Query parameters - Parametry zapytania - - - - Please provide values for query parameters - ProszÄ™ podać wartoÅ›ci dla parametrów zapytania - - - - BugDialog - - Bugs and ideas - Błędy i pomysÅ‚y - - - Reporter - ZgÅ‚aszajÄ…cy - - - E-mail address - Adres e-mail - - - Log in - Zaloguj - - - Short description - Krótki opis - - - Detailed description - Opis szczegółowy - - - Show more details - Pokaż wiÄ™cej szczegółów - - - SQLiteStudio version - Wersja SQLiteStudio - - - Operating system - System operacyjny - - - Loaded plugins - ZaÅ‚adowane wtyczki - - - Send - WyÅ›lij - - - You can see all your reported bugs and ideas by selecting menu '%1' and then '%2'. - Możesz zobaczyć wszystkie błędy i pomysÅ‚y zgÅ‚oszone przez ciebie wybierajÄ…c menu '%1' i dalej '%2'. - - - A bug report sent successfully. - Błąd zostaÅ‚ zgÅ‚oszony pomyÅ›lnie. - - - An error occurred while sending a bug report: %1 -%2 - WystÄ…piÅ‚ błąd podczas zgÅ‚aszania błędu: %1 -%2 - - - You can retry sending. The contents will be restored when you open a report dialog after an error like this. - Możesz spróbować powtórzyć wysyÅ‚kÄ™. Zawartość bÄ™dzie przywrócona, kiedy otworzysz okno zgÅ‚aszania po błędzie takim jak ten. - - - An idea proposal sent successfully. - PomysÅ‚ zostaÅ‚ zgÅ‚oszony pomyÅ›lnie. - - - An error occurred while sending an idea proposal: %1 -%2 - WystÄ…piÅ‚ błąd podczas zgÅ‚aszania pomysÅ‚u: %1 -%2 - - - A bug report - ZgÅ‚oÅ› błąd - - - Describe problem in few words - Opisz problem w kilku sÅ‚owach - - - Describe problem and how to reproduce it - Opisz problem, oraz jak go powtórzyć - - - A new feature idea - ZgÅ‚oÅ› pomysÅ‚ - - - A title for your idea - TytuÅ‚ twojego pomysÅ‚u - - - Describe your idea in more details - Opisz twój pomysÅ‚ szerzej - - - Reporting as an unregistered user, using e-mail address. - ZgÅ‚aszanie jako niezarejestrowany użytkownik, używajÄ…c adresu e-mail. - - - Reporting as a registered user. - ZgÅ‚aszanie jako zarejestrowany użytkownik. - - - Log out - Wyloguj - - - Providing true email address will make it possible to contact you regarding your report. To learn more, press 'help' button on the right side. - Podanie prawdziwego adresu e-mail pozwoli na skontaktowanie siÄ™ z tobÄ… w zwiÄ…zku z twoim zgÅ‚oszeniem. Aby dowiedzieć siÄ™ wiÄ™cej, kliknij przycisk 'pomoc' po prawej stronie. - - - Enter vaild e-mail address, or log in. - Wpisz poprawny adres e-mail, lub zaloguj siÄ™. - - - Short description requires at least 10 characters, but not more than 100. Longer description can be entered in the field below. - Krótki opis wymaga przynajmniej 10 znaków, ale nie wiÄ™cej niż 100. DÅ‚uższy opis może być wpisany w polu poniżej. - - - Long description requires at least 30 characters. - DÅ‚ugi opis wymaga przynajmniej 30 znaków. - - - - BugReportHistoryWindow - - Title - TytuÅ‚ - - - Reported at - ZgÅ‚oszony dnia - - - URL - URL - - - Reports history - Historia zgÅ‚oszeÅ„ - - - Clear reports history - Wyczyść historiÄ™ zgÅ‚oszeÅ„ - - - Delete selected entry - UsuÅ„ wybranÄ… pozycjÄ™ - - - Invalid response from server. - Niepoprawna odpowiedź z serwera. - - - - BugReportLoginDialog - - Log in - Zaloguj - - - Credentials - Dane do logowania - - - Login: - Login: - - - Password: - HasÅ‚o: - - - Validation - Walidacja - - - Validate - Sprawdź - - - Validation result message - Treść wyniku walidacji - - - Abort - Przerwij - - - A login must be at least 2 characters long. - Login musi mieć przynajmniej 2 znaki. - - - A password must be at least 5 characters long. - HasÅ‚o musi mieć przynajmniej 5 znaków. - - - Valid - Poprawne - - - - CollationsEditor - - - Filter collations - Filtruj zestawienia - - - - Collation name: - Nazwa zestawienia: - - - - Implementation language: - JÄ™zyk implementacji: - - - - Databases - Bazy danych - - - - Register in all databases - Zarejestruj we wszystkich bazach danych - - - - Register in following databases: - Zarejestruj w nastÄ™pujÄ…cych bazach danych: - - - - Implementation code: - Kod implementacji: - - - - Collations editor - Edytor zestawieÅ„ - - - - Commit all collation changes - Zatwierdź wszystkie zmiany w zestawieniach - - - - Rollback all collation changes - Wycofaj wszystkie zmiany w zestawieniach - - - - Create new collation - Utwórz nowe zestawienie - - - - Delete selected collation - UsuÅ„ wybrane zestawienie - - - - Editing collations manual - PodrÄ™cznik edycji zestawieÅ„ - - - - Enter a non-empty, unique name of the collation. - Podaj niepustÄ…, unikalnÄ… nazwÄ™ zestawienia. - - - - Pick the implementation language. - Wybierz jÄ™zyk implementacji. - - - - Enter a non-empty implementation code. - Wprowadź niepusty kod implementacji. - - - - Collations editor window has uncommitted modifications. - Okno edytora zestawieÅ„ ma niezatwierdzone zmiany. - - - Collations editor window has uncommited modifications. - Okno edytora zestawieÅ„ ma niezatwierdzone zmiany. - - - - ColorButton - - - Pick a color - Wybierz kolor - - - - ColumnCollatePanel - - - Collation name: - Nazwa zestawienia: - - - - Named constraint: - Ograniczenie nazwane: - - - - Enter a name of the constraint. - Wprowadź nazwÄ™ ograniczenia. - - - - Enter a collation name. - Wprowadź nazwÄ™ zestawienia. - - - - ColumnDefaultPanel - - - Default value: - DomyÅ›lna wartość: - - - - Named constraint: - Ograniczenie nazwane: - - - - Enter a default value expression. - Wprowadź wyrażenie wartoÅ›ci domyÅ›lnej. - - - - Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - Niepoprawne wyrażenie wartoÅ›ci domyÅ›lnej: %1. JeÅ›li chcesz użyć zwykÅ‚ego tekstu jako wartość, pamiÄ™taj o zamkniÄ™ciu go w znakach apostrofu. - - - - Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - Niepoprawne wyrażenie wartoÅ›ci domyÅ›lnej. JeÅ›li chcesz użyć zwykÅ‚ego tekstu jako wartość, pamiÄ™taj o zamkniÄ™ciu go w znakach apostrofu. - - - Invalid default value expression: %1 - Niepoprawna wartość wyrażenia domyÅ›lnego: %1 - - - - Enter a name of the constraint. - Wprowadź nazwÄ™ ograniczenia. - - - - ColumnDialog - - - Column - Kolumna - - - - Name and type - Nazwa i typ - - - - Scale - Skala - - - - Precision - Precyzja - - - - Data type: - Typ danych: - - - - Column name: - Nazwa kolumny: - - - - Size: - Rozmiar: - - - - Constraints - Ograniczenia - - - - Unique - WartoÅ›ci unikalne - - - - - - - - - - Configure - Konfiguruj - - - - Foreign Key - Klucz obcy - - - - Collate - Zestawienie - - - - Not NULL - Niepuste - - - - Check condition - Sprawdzaj warunek - - - - Primary Key - Klucz główny - - - - Default - Wartość domyÅ›lna - - - - Advanced mode - Tryb zaawansowany - - - - Add constraint - column dialog - Dodaj ograniczenie - - - - Edit constraint - column dialog - Edytuj ograniczenie - - - - - Delete constraint - column dialog - UsuÅ„ ograniczenie - - - - Move constraint up - column dialog - PrzenieÅ› ograniczenie w górÄ™ - - - - Move constraint down - column dialog - PrzenieÅ› ograniczenie w dół - - - - Add a primary key - column dialog - Dodaj klucz główny - - - - Add a foreign key - column dialog - Dodaj klucz obcy - - - - Add an unique constraint - column dialog - Dodaj ograniczenie wartoÅ›ci unikalnych - - - - Add a check constraint - column dialog - Dodaj ograniczenie sprawdzania wartoÅ›ci - - - - Add a not null constraint - column dialog - Dodaj ograniczenie niepustych wartoÅ›ci - - - - Add a collate constraint - column dialog - Dodaj ograniczenie zestawienia - - - - Add a default constraint - column dialog - Dodaj ograniczenie wartoÅ›ci domyÅ›lnej - - - - Are you sure you want to delete constraint '%1'? - column dialog - Czy na pewno chcesz usunąć ograniczenie '%1'? - - - - Correct the constraint's configuration. - Popraw konfiguracjÄ™ ograniczenia. - - - - This constraint is not officially supported by SQLite 2, -but it's okay to use it. - To ograniczenie nie jest oficjalnie wspireane przez SQLite 2, -ale można go używać. - - - - Scale is not allowed for INTEGER PRIMARY KEY columns. - Skala nie jest dozwolona dla kolumn INTEGER PRIMARY KEY. - - - - Precision cannot be defined without the scale. - Precyzja nie może być zdefiniowana bez skali. - - - - Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - Nie można użyć innego typu niż INTEGER, jeÅ›li opcja AUTOINCREMENT jest wybrana w PRIMARY KEY. - - - - INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - Typ INTEGER zostaÅ‚ wymuszony w zwiÄ…zku z wybranÄ… opcjÄ… AUTOINCREMENT w PRIMARY KEY. - - - - Precision is not allowed for INTEGER PRIMARY KEY columns. - Precyzja nie jest dozwolona dla kolumn INTEGER PRIMARY KEY. - - - - ColumnDialogConstraintsModel - - - Type - column dialog constraints - Typ - - - - Name - column dialog constraints - Nazwa - - - - Details - column dialog constraints - Szczegóły - - - - ColumnForeignKeyPanel - - - Foreign table: - Tabela obca: - - - - Foreign column: - Kolumn obca: - - - - Reactions - Reakcje - - - - Deferred foreign key - Klucz obcy odroczony - - - - Named constraint - Nazwane ograniczenie - - - - Constraint name - Nazwa ograniczenia - - - - Pick the foreign table. - Wybierz tabelÄ™ obcÄ… - - - - Pick the foreign column. - Wybierz kolumnÄ™ obcÄ… - - - - Enter a name of the constraint. - Wprowadź nazwÄ™ ograniczenia. - - - - ColumnPrimaryKeyPanel - - - Autoincrement - Autoinkrementacja - - - - Sort order: - Kierunek sortowania: - - - - Named constraint: - Ograniczenie nazwane: - - - - On conflict: - W razie konfliktu: - - - - Enter a name of the constraint. - Wprowadź nazwÄ™ ograniczenia. - - - Autoincrement (only for %1 type columns) - column primary key - Autoinkrementacja (tylko dla kolumn o typie %1) - - - - ColumnUniqueAndNotNullPanel - - - Named constraint: - Ograniczenie nazwane: - - - - On conflict: - W razie konfliktu: - - - - Enter a name of the constraint. - Wprowadź nazwÄ™ ograniczenia. - - - - CompleterWindow - - - Column: %1 - completer statusbar - Kolumna: %1 - - - - Table: %1 - completer statusbar - Tabela: %1 - - - - Index: %1 - completer statusbar - Indeks: %1 - - - - Trigger: %1 - completer statusbar - Wyzwalacz: %1 - - - - View: %1 - completer statusbar - Widok: %1 - - - - Database: %1 - completer statusbar - Baza danych: %1 - - - - Keyword: %1 - completer statusbar - SÅ‚owo kluczowe: %1 - - - - Function: %1 - completer statusbar - Funkcja: %1 - - - - Operator: %1 - completer statusbar - Operator: %1 - - - - String - completer statusbar - Tekst - - - - Number - completer statusbar - Liczba - - - - Binary data - completer statusbar - Dane binarne - - - - Collation: %1 - completer statusbar - Zestawienie: %1 - - - - Pragma function: %1 - completer statusbar - Funkcja pragma: %1 - - - - ConfigDialog - - - - Configuration - Konfiguracja - - - - Search - Szukaj - - - - General - Ogólne - - - - Keyboard shortcuts - Skróty klawiszowe - - - - Look & feel - WyglÄ…d i zachowanie - - - - Style - Style - - - - Fonts - Czcionki - - - - Colors - Kolory - - - - Plugins - Wtyczki - - - - Code formatters - Formatery kodu - - - - Data browsing - PrzeglÄ…danie danych - - - - Data editors - Edytory danych - - - - Data browsing and editing - PrzeglÄ…danie i edycja danych - - - - Number of data rows per page: - Liczba wierszy danych na stronie: - - - - - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - <p>Kiedy dane sÄ… wczytane do widoku siatki, szerokość kolumn jest automatycznie dostosowywana. Ta wartość ogranicza poczÄ…tkowÄ… szerokość tego dostosowywania, ale użytkownik nadal może rozszerzać kolumnÄ™ rÄ™cznie poza ten limit.</p> - - - - Limit initial data column width to (in pixels): - Ogranicz poczÄ…tkowÄ… szerokość kolumn danych (w pikselach): - - - - Inserting new row in data grid - Wstawianie nowego wiersza w widoku siatki danych. - - - - Before currently selected row - Przed aktualnie wybranym wierszem - - - - After currently selected row - Po aktualnie wybranym wierszu. - - - - At the end of data view - Na koÅ„cu widoku siatki danych - - - - Data types - Type danych - - - - Available editors: - DostÄ™pne edytory: - - - - Editors selected for this data type: - Edytory wybrane dla tego typu danych: - - - - Schema editing - Edycja schematu - - - - Number of DDL changes kept in history. - Liczba zmian DDL trzymanych w historii. - - - - DDL history size: - Rozmiar historii DDL: - - - Don't show DDL preview dialog when commiting schema changes - Nie pokazuj okna podglÄ…du DDL podczas zatwierdzania zmian schematu - - - - SQL queries - Zapytania SQL - - - - - Number of queries kept in the history. - Liczba zapytaÅ„ trzymana w historii. - - - - History size: - Rozmiar historii: - - - - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> - <p>JeÅ›li w oknie edytora SQL jest wiÄ™cej niż jedno zapytanie, to (jeÅ›li ta opcja jest włączona) tylko jedno zapytanie bÄ™dzie wykonana - to, które znajduje siÄ™ pod kursorem pisania. W przeciwnym wypadku wszystkie zapytania bÄ™dÄ… wykonywane. Zawsze możesz ograniczyć zapytania do wywoÅ‚ania przez zaznaczenie tych zapytaÅ„, które chcesz wywoÅ‚ać.</p> - - - - Execute only the query under the cursor - Wykonuj tylko zapytania bÄ™dÄ…ce pod kursorem - - - - Updates - Aktualizacje - - - - Automatically check for updates at startup - Sprawdzaj aktualizacje automatycznie przy starcie - - - - Session - Sesje - - - - Restore last session (active MDI windows) after startup - Przywróć ostatniÄ… sesjÄ™ (aktywne okna MDI) po starcie - - - - Filter shortcuts by name or key combination - Filtruj skróty po nazwie, lub kombinacji klawiszy - - - - Action - Akcja - - - - Key combination - Kombinacja klawiszy - - - - Changing language requires application restart to take effect. - Zmiana jÄ™zyka wymaga restartu aplikacji, aby zadziaÅ‚ać. - - - - Compact layout - UkÅ‚ad kompaktowy - - - - <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - <p>UkÅ‚ad kompaktowy zmniejsza wszystkie marginesy i odstÄ™py na interfejsie do minimum, robiÄ…c wiÄ™cej miejsca na wyÅ›wietlanie danych. Powoduje to, że interfejs jest nieco mniej estetyczny, ale pozwala to na prezentacjÄ™ wiÄ™kszej iloÅ›ci danych naraz.</p> - - - - Use compact layout - Użyj ukÅ‚adu kompaktowego - - - - - Database list - Lista baz - - - - If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. - Gdy wyłączone, to kolumny bÄ™dÄ… uÅ‚ożone w takiej kolejnoÅ›ci, w jakiej wystÄ…piÅ‚y w zapytaniu CREATE TABLE. - - - - Sort table columns alphabetically - Sortuj kolumny tabel alfabetycznie. - - - - Expand tables node when connected to a database - RozwiÅ„ listÄ™ tabel po połączeniu z bazÄ… danych - - - - <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> - <p>Dodatkowe etykiety, to te wyÅ›wietlane obok nazw na liÅ›cie baz danych (sÄ… niebieskie, chyba że skonfigurowano je inaczej). Włączenie tej opcji spowoduje wyÅ›wietlenie etykiet dla baz danych, niepoprawnych baz danych, oraz dla wÄ™złów agregujÄ…cych (grupa kolumn, grupa indeksów, grupa wyzwalaczy). WiÄ™cej etykiet jest dostÄ™pne niżej.</p> - - - - Display additional labels on the list - WyÅ›wietlaj dodatkowe etykiety na liÅ›cie - - - - For regular tables labels will show number of columns, indexes and triggers for each of tables. - Dla zwykÅ‚ych tabel etykiety bÄ™dÄ… pokazywać liczbÄ™ kolumn, inseksów, oraz wyzwalaczy dla tych tabel. - - - - Display labels for regular tables - WyÅ›wietlaj etykiety dla zwykÅ‚ych tabel - - - - Virtual tables will be marked with a 'virtual' label. - Tabele wirtualne bÄ™dÄ… oznaczone etykietÄ… 'wirtualna'. - - - - Display labels for virtual tables - WyÅ›wietlaj etykiety dla tabel wirtualnych - - - - Expand views node when connected to a database - RozwiÅ„ listÄ™ widoków po połączeniu z bazÄ…. - - - - If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) - Gdy ta opcja jest wyłączona, to wszystkie obiekty bÄ™dÄ… uÅ‚ożone w takiej kolejnoÅ›ci, w jakiej wystÄ™pujÄ… w tabeli sqlite_master (czyli w takiej, w jakiej zostaÅ‚y stworzone) - - - - Sort objects (tables, indexes, triggers and views) alphabetically - Sortuj obiekty (tabele, indeksy, wyzwalacze i widoki) alfabetycznie - - - - Display system tables and indexes on the list - WyÅ›wietlaj tabele i indeksy systemowe na liÅ›cie - - - - Table windows - Okna tabel - - - When enabled, Table Windows will show up with the data tab, instead of the structure tab. - Gdy włączone, Okna Tabel bÄ™dÄ… siÄ™ pokazywać z zakÅ‚adkÄ… danych, zamiast z zakÅ‚adkÄ… struktury. - - - - Open Table Windows with the data tab for start - Otwieraj Okna Tabeli z zakÅ‚adkÄ… danych na poczÄ…tek - - - - View windows - Okna Widoków - - - When enabled, View Windows will show up with the data tab, instead of the structure tab. - Gdy włączone, Okna Widoków bÄ™dÄ… siÄ™ pokazywać z zakÅ‚adkÄ… danych, zamiast z zakÅ‚adkÄ… struktury. - - - - Open View Windows with the data tab for start - Otwieraj Okna Widoku z zakÅ‚adkÄ… danych na poczÄ…tek - - - - Main window dock areas - Strefy dokowania głównego okna - - - - Left and right areas occupy corners - Lewa i prawa strefa zajmujÄ… rogi - - - - Top and bottom areas occupy corners - Górna i dolna strefa zajmujÄ… rogi - - - - Hide built-in plugins - Ukryj wtyczki wbudowane - - - - Current style: - Aktualny styl: - - - - Preview - PodglÄ…d - - - - Enabled - Włączone - - - Column - Kolumna - - - - Disabled - Wyłączone - - - - - Language - JÄ™zyk - - - - Database dialog window - Okno dialogowe bazy danych - - - - <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> - <p>Podczas dodawania nowej bazy danych jest ona domyÅ›lnie zaznaczana jako "trwaÅ‚a" (zapisywana w konfiguracji). Włączenie tej opcji powoduje, że każda nowa baza danych NIE bÄ™dzie zaznaczana domyÅ›lnie jako "trwaÅ‚a".</p> - - - - Do not mark database to be "permanent" by default - Nie zaznaczaj bazy danych domyÅ›lnie jako "trwaÅ‚a" - - - - <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> - <p>Gdy ta opcja jest włączona, to pliki upuszczone z menadżera plików na listÄ™ baz danych bÄ™dÄ… automatycznie dodawane do list, pomijajÄ…c standardowe okno bazy danych. JeÅ›li z różnych powodów automatyczny proces siÄ™ nie powiedzie, to użytkownikowi ukaże siÄ™ standardowe okno.</p> - - - - Try to bypass dialog completly when dropping database file onto the list - Próbuj caÅ‚kowicie pomijać dialog podczas upuszczania pliku bazy na listÄ™ - - - - Keep NULL value when entering empty value - Zachowaj wartość NULL gdy wstawiania jest pusta wartość - - - - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> - <p>Gdy to jest włączone i użytkownik zatrzyma kursor myszy nad komórkÄ… w widoku siatki danych (wyniki zapytania, dane tabeli, dane widoku), to pojawi siÄ™ podpowiedź ze szczegółami odnoÅ›nie komórki - zawiera ona szczegóły , jak typ danych kolumny, ograniczenia, ROWID i inne.</p> - - - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - <p>Maksymalna liczba konfiguracji w oknie dialogowym zaludniania tabeli, która ma być trzymana w konfiguracji. Wartość 100 powinna być wystarczajÄ…ca.</p> - - - - Number of memorized table populating configurations - Liczba zapamiÄ™tanych konfiguracji zaludniania tabeli - - - - Show column and row details tooltip in data view - Pokazuj podpowiedź ze szczegółami o kolumnie i wierszu w widoku siatki danych - - - - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - <p>Kiedy edytowana jest komórka, która miaÅ‚a wartość NULL, a nowa wartość wprowadzona jest pusta, to ta opcja decyduje o tym, czy wartość powinna pozostać NULL (gdy ta opcja jest włączona), czy powinna być nadpisana pustym Å‚aÅ„cuchem znaków (gdy ta opcja wyłączona).</p> - - - - <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> - <html><head/><body><p>Włącz to, aby wymusić wartość DEFAULT podczas zatwierdzania wartoÅ›ci NULL dla kolumn, które majÄ… zdefiniowanÄ… wartość DEFAULT, nawet jeÅ›li kolumna dopuszcza wartoÅ›ci NULL.</p><p>Wyłącz tÄ… opcjÄ™ aby używać wartoÅ›ci DEFAULT tylko i wyłącznie, gdy wartość NULL jest zatwierdzana dla kolumny z ograniczeniem NOT NULL.</p></body></html> - - - - Use DEFAULT value (if defined), when committing NULL value - Używaj wartoÅ›ci DEFAULT (jeÅ›li zdefiniowana), gdy zatwierdzana jest wartość NULL - - - - <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - <p>Gdy włączone, Okna Tabeli bÄ™dÄ… siÄ™ otwierać na zakÅ‚adce danych, zamiast na zakÅ‚adce struktury.</p> - - - - <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - <p>Gdy włączone, to zakÅ‚adka "Dane" bÄ™dzie umieszczona jako pierwsza w każdym Oknie Tabeli, zamiast jako druga.</p> - - - - Place data tab as first tab in a Table Window - Ustaw zakÅ‚adkÄ™ danych jako pierwszÄ… w Oknie Tabeli - - - - <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - <p>Gdy włączone, Okna Widoku bÄ™dÄ… siÄ™ otwierać na zakÅ‚adce danych, zamiast na zakÅ‚adce struktury.</p> - - - - <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - <p>Gdy włączone, to zakÅ‚adka "Dane" bÄ™dzie umieszczona jako pierwsza w każdym Oknie Widoku, zamiast jako druga.</p> - - - - Place data tab as first tab in a View Window - Ustaw zakÅ‚adkÄ™ danych jako pierwszÄ… w Oknie Widoku - - - - Don't show DDL preview dialog when committing schema changes - Nie pokazuj okna podglÄ…du DDL podczas zatwierdzania zmian struktury - - - - - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - <p>Maksymalna liczba parametrów zapytania (:param, @param, $param, ?) trzymanych w historii. Kiedy ponownie użyjesz parametru o tej samej nazwie/pozycji, SQLiteStudio wstÄ™pnie uzupeÅ‚ni go używajÄ…c ostatniej zapamiÄ™tanej wartoÅ›ci (nadal bÄ™dzie można jÄ… zmienić). Wartość 100 powinna być wystarczajÄ…ca.</p> - - - - Number of memorized query parameters - Liczba zapamiÄ™tanych parametrów zapytania - - - - Status Field - Pole Statusu - - - - <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> - <p>Kiedy użytkownik rÄ™cznie zamyka panel Statusu, ta opcja zapewnia, że zostanie ono otwarte ponownie, gdy jest wyÅ›wietlona nowa wiadomość. JeÅ›li jest ona wyłączona, to panel Statusu może być otwarte tylko rÄ™cznie z menu "Widok".</p> - - - - Always open Status panel when new message is printed - Zawsze otwieraj panel Statusu, gdy wyÅ›wietlona jest nowa wiadomość - - - - Active formatter plugin - Aktywna wtyczka formatera - - - - SQL editor font - Czcionka edytora SQL - - - - Database list font - Czcionka listy baz danych - - - - Database list additional label font - Czcionka dodatkowych etykiety listy baz danych - - - - Data view font - Czcionka widoku danych - - - - Status field font - Czcionka pola statusu - - - - SQL editor colors - Kolory edytora SQL - - - - Current line background - TÅ‚o bieżącej linii - - - - <p>SQL strings are enclosed with single quote characters.</p> - <p>ÅaÅ„cuchy znaków SQL sÄ… zamkniÄ™te pomiÄ™dzy znakami apostrofu.</p> - - - - String foreground - Czcionka Å‚aÅ„cucha znaków - - - - <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - <b>Parametry wiążące to wyrażenia zastÄ™pcze dla wartoÅ›ci, które majÄ… być dopiero dostarczone przez użytkownika. MajÄ… one jednÄ… z form: </p><ul><li>:nazwa_parametru</li><li>$nazwa_parametru</li><li>@nazwa_parametru</li><li>?</li></ul> - - - - Bind parameter foreground - Czcionka parametru wiążącego - - - - Highlighted parenthesis background - TÅ‚o podÅ›wietlonych nawiasów - - - - <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - <p>WartoÅ›ci BLOB sÄ… wartoÅ›ciami binarnymi, reprezentowanymi jako liczby heksadecymalne, jak np:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - - - - BLOB value foreground - - - - - Regular foreground - Standardowa czcionka - - - - Line numbers area background - TÅ‚o obszaru numerów linii - - - - Keyword foreground - Czcionka sÅ‚owa kluczowego - - - - Number foreground - Czcionka liczby - - - - Comment foreground - Czcionka komentarza - - - - <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> - <p>Poprawne obiekty to nazwy tabel, indekstów, wyzwalaczy i widoków, które istniejÄ… w basie SQLite.</p> - - - - Valid objects foreground - Czcionka poprawnych obiektów - - - - Data view colors - Kolory widoku danych - - - - <p>Any data changes will be outlined with this color, until they're committed to the database.</p> - <p>Jakiekolwiek zmiany danych bÄ™dÄ… otoczone ramkÄ… w tym kolorze, dopóki nie zostanÄ… zatwierdzone do bazy.</p> - - - - Uncommitted data outline color - Kolor obramowania niezatwierdzonych danych - - - - <p>In case of error while committing data changes, the problematic cell will be outlined with this color.</p> - <p>W razie błędu podczas zatwierdzania zmian w danych, komórki sprawiajÄ…ce problem bÄ™dÄ… obramowane tym kolorem.</p> - - - <p>Any data changes will be outlined with this color, until they're commited to the database.</p> - <p>Jakakolwiek zmiana danych bÄ™dzie obrysowana tym kolorem, dopóki nie zostanie zatwierdzona do bazy danych.</p> - - - Uncommited data outline color - Kolor obrysu niezatwierdzonych danych - - - <p>In case of error while commiting data changes, the problematic cell will be outlined with this color.</p> - <p>W przypadku błędu podczas zatwierdzania zmian danych, komórka bÄ™dÄ…ca przyczynÄ… problemu zostanie obrysowana tym kolorem.</p> - - - - Commit error outline color - Kolor obrysu błędu zatwierdzania - - - - NULL value foreground - Kolor czcionki wartoÅ›ci NULL - - - - Deleted row background - TÅ‚o wiersza usuniÄ™tego - - - - Database list colors - Kolory listy baz danych - - - - <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> - <p>Dodatkowe etykiety to te, które mówiÄ… o wersji SQLite, liczbie obiektów w głębszych częściach drzewa, itp.</p> - - - - Additional labels foreground - Czcionka dodatkowych etykiet - - - - Status field colors - Kolory pola statusu - - - - Information message foreground - Czcionka wiadomoÅ›ci informujÄ…cej - - - - Warning message foreground - Czcionka wiadomoÅ›ci ostrzegajÄ…cej - - - - Error message foreground - Czcionka wiadomoÅ›ci błędu - - - - Description: - plugin details - Opis: - - - - Category: - plugin details - Kategoria: - - - - Version: - plugin details - Wersja: - - - - Author: - plugin details - Autor: - - - - Internal name: - plugin details - Nazwa wewnÄ™trzna: - - - - Dependencies: - plugin details - ZależnoÅ›ci: - - - - Conflicts: - plugin details - Konflikty: - - - - Plugin details - Szczegóły wtyczki - - - - Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. - Wtyczki sÄ… Å‚adowane/wyÅ‚adowywane natychmiast po zaznaczeniu/odznaczeniu, ale zmodyfikowana lista wtyczek, które należy zaÅ‚adować przy starcie nie jest zapisana, dopóki nie zatwierdzisz caÅ‚ego okna configuracji. - - - - %1 (built-in) - plugins manager in configuration dialog - %1 (wbudowany) - - - - Details - Szczegóły - - - - No plugins in this category. - Brak wtyczek w tej kategorii. - - - - Add new data type - Dodaj nowy typ danych - - - - Rename selected data type - ZmieÅ„ nazwÄ™ wybranego typu danych - - - - Delete selected data type - UsuÅ„ wybrany typ danych - - - - Help for configuring data type editors - Pomoc w konfiguracji edytorów typów danych - - - - ConstraintCheckPanel - - - The condition - Warunek - - - - Named constraint: - Ograniczenie nazwane: - - - - On conflict - W razie konfliktu - - - - Enter a valid condition. - Wprowadź poprawny warunek. - - - - Enter a name of the constraint. - Wprowadź nazwÄ™ ograniczenia. - - - - ConstraintDialog - - - New constraint - constraint dialog - Nowe ograniczenie - - - - Create - constraint dialog - Utwórz - - - - Edit constraint - dialog window - Edytuj ograniczenie - - - - Apply - constraint dialog - Zastosuj - - - - Primary key - table constraints - Klucz główny - - - - Foreign key - table constraints - Klucz obcy - - - - Unique - table constraints - WartoÅ›ci unikalne - - - - Not NULL - table constraints - Niepuste - - - - Check - table constraints - Warunek - - - - Collate - table constraints - Zestawienie - - - - Default - table constraints - Wartość domyÅ›lna - - - - ConstraintTabModel - - - Table - table constraints - Tabela - - - - Column (%1) - table constraints - Kolumna (%1) - - - - Scope - table constraints - Zakres - - - - Type - table constraints - Typ - - - - Details - table constraints - Szczegóły - - - - Name - table constraints - Nazwa - - - - CssDebugDialog - - - SQLiteStudio CSS console - Konsola CSS SQLiteStudio - - - - DataView - - - Filter data - data view - Filtruj dane - - - - Grid view - Widok siatki - - - - Form view - Widok formularza - - - - Refresh table data - data view - OdÅ›wież dane tabeli - - - - First page - data view - Pierwsza strona - - - - Previous page - data view - Poprzednia strona - - - - Next page - data view - NastÄ™pna strona - - - - Last page - data view - Ostatnia strona - - - - Filter - Filtruj - - - - Hit Enter key or press "Apply filter" button on toolbar to apply new value. - WciÅ›nij Enter lub naciÅ›nij przycisk "Zastosuj filtr", aby zastosować nowÄ… wartość. - - - - Show filter inputs per column - data view - Pokaż filtr dla każdej kolumny - - - - Apply filter - data view - Zastosuj filtr - - - - Commit changes for selected cells - data view - Zatwierdź zmiany dla wybranych komórek - - - - Rollback changes for selected cells - data view - Wycofaj zmiany dla wybranych komórek - - - - Show grid view of results - sql editor - Pokaż widok siatki dla wyników - - - - Show form view of results - sql editor - Pokaż widok formularza dla wyników - - - - Filter by text - data view - Filtruj po tekÅ›cie - - - - Filter by the Regular Expression - data view - Filtruj używajÄ…c WyrażeÅ„ Regularnych - - - - Filter by SQL expression - data view - Filtruj używajÄ…c wyrażenia SQL - - - - Tabs on top - data view - Karty na górze - - - - Tabs at bottom - data view - Karty na dole - - - - Place new rows above selected row - data view - Wstawiaj nowe wiersze nad aktualnie wybranym wierszem - - - - Place new rows below selected row - data view - Wstawiaj nowe wiersze pod aktualnie wybranym wierszem - - - - Place new rows at the end of the data view - data view - Wstawiaj nowe wiersze na koÅ„cu widoku siatki danych - - - - Total number of rows is being counted. -Browsing other pages will be possible after the row counting is done. - CaÅ‚kowita liczba wierszy jest liczona. -PrzeglÄ…danie pozostaÅ‚ych stron bÄ™dzie możliwe kiedy liczenie wierszy zostanie zakoÅ„czone. - - - - Row: %1 - Wiersz: %1 - - - - DbConverterDialog - - - Source database - ŹródÅ‚owa baza danych - - - - Source database version: - Wersja źródÅ‚owej bazy: - - - - Target database - Docelowa baza danych - - - - Target version: - Wersja docelowej bazy: - - - - This is the file that will be created as a result of the conversion. - To jest plik, który bÄ™dzie stworzony jako wynik konwersji. - - - - Target file: - Docelowy plik: - - - - Name of the new database: - Nazwa nowej bazy: - - - - This is the name that the converted database will be added to SQLiteStudio with. - To jest nazwa z jakÄ… skonwertowana baza bÄ™dzie dodana do SQLiteStudio. - - - - Convert database - Konwertuj bazÄ™ danych - - - - Select source database - Wybierz źródÅ‚owÄ… bazÄ™ danych - - - - Enter valid and writable file path. - Wprowadź poprawnÄ… Å›cieżkÄ™ do pliku, do której masz prawo zapisywać. - - - - Entered file exists and will be overwritten. - Podany plik istnieje i zostanie nadpisany. - - - - Enter a not empty, unique name (as in the list of databases on the left). - Wprowadź niepustÄ…, unikalnÄ… nazwÄ™ (w kontekÅ›cie listy baz danych po lewej). - - - - No valid target dialect available. Conversion not possible. - Nie ma dostÄ™pnego poprawnego docelowego dialektu. Konwersja nie jest możliwa. - - - - Select valid target dialect. - Wybierz poprawny docelowy dialekt. - - - - Database %1 has been successfully converted and now is available under new name: %2 - Baza danych %1 zostaÅ‚a przekonwertowana pomyÅ›lnie i jest teraz dostÄ™pna pod nazwÄ…: %2 - - - - SQL statements conversion - Konwersja zapytaÅ„ SQL - - - - Following error occurred while converting SQL statements to the target SQLite version: - NastÄ™pujÄ…ce błędy wystÄ…piÅ‚y podczas konwersji zapytaÅ„ SQL do docelowej wersji SQLite: - - - - Would you like to ignore those errors and proceed? - Czy chcesz zignorować te błędy i kontynuować? - - - - DbDialog - - - Database - Baza danych - - - - Database type - Typ bazy danych - - - - Database driver - Sterownik bazy danych - - - Generate automatically - Generuj automatycznie - - - - Options - Opcje - - - - Permanent (keep it in configuration) - TrwaÅ‚a (trzymaj w konfiguracji) - - - - Test connection - Testuj połączenie - - - Name - Nazwa - - - Type - Typ - - - Browse for database file on local computer - PrzeglÄ…daj w poszukiwaniu pliku bazy danych na lokalnym komputerze - - - - Create new database file - Utwórz nowÄ… bazÄ™ - - - - - File - Plik - - - - Name (on the list) - Nazwa (na liÅ›cie) - - - Generate name basing on file path - Generuj nazwÄ™ bazujÄ…c na Å›cieżce do pliku - - - Permanent - TrwaÅ‚a - - - - <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> - aasfd - <p>Włącz to, jeÅ›li chcesz aby baza danych byÅ‚a przechowywana w pliku konfiguracji i przywracana za każdym razem, gdy startuje SQLiteStudio.</p> - - - Test database connection - Testuj połączenie z bazÄ… - - - - Browse for existing database file on local computer - PrzeglÄ…daj lokalny komputer w poszukiwaniu istniejÄ…cej bazy - - - - Browse - PrzeglÄ…daj - - - - Enter an unique database name. - Wprowadź unikalnÄ… nazwÄ™ bazy danych. - - - - This name is already in use. Please enter unique name. - Ta nazwa jest już w użyciu. ProszÄ™ wprowadzić unikalnÄ… nazwÄ™. - - - - <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - <p>Automatyczne generowanie nazwy zostaÅ‚o wyłączone, ponieważ nazwa byÅ‚a edytowana rÄ™cznie. Aby przywrócić automatyczne generowanie, proszÄ™ wyczyÅ›cić pole nazwy.</p> - - - <p>Automatic name generation was disabled, becuase the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - <p>Automatyczne generowanie nazwy zostaÅ‚o wyłączone, ponieważ nazwa byÅ‚a edytowana rÄ™cznie. Aby przywrócić automatyczne generowanie, proszÄ™ wyczyÅ›cić pole nazwy.</p> - - - - Enter a database file path. - Wprowadź Å›cieżkÄ™ do pliku bazy danych. - - - - This database is already on the list under name: %1 - Ta baza jest już na liÅ›cie pod nazwÄ…: %1 - - - - Select a database type. - Wybierz typ bazy danych. - - - Auto-generated - Auto-generowana - - - The name will be auto-generated - Nazwa bÄ™dzie generowana automatycznie - - - Type the name - Wprowadź nazwÄ™ - - - - DbObjectDialogs - - - Delete table - UsuÅ„ tabelÄ™ - - - - Are you sure you want to delete table %1? - Czy na pewno chcesz usunąć tabelÄ™ %1? - - - - Delete index - UsuÅ„ indeks - - - - Are you sure you want to delete index %1? - Czy na pewno chcesz usunąć indeks %1? - - - - Delete trigger - UsuÅ„ wyzwalacz - - - - Are you sure you want to delete trigger %1? - Czy na pewno chcesz usunąć wyzwalacz %1? - - - - Delete view - UsuÅ„ widok - - - - Are you sure you want to delete view %1? - Czy na pewno chcesz usunąć widok %1? - - - - - Error while dropping %1: %2 - Błąd podczas porzucania %1: %2 - - - - Delete objects - UsuÅ„ obiekty - - - - Are you sure you want to delete following objects: -%1 - Czy na pewno chcesz usunąć nastÄ™pujÄ…ce obiekty: -%1 - - - - Cannot start transaction. Details: %1 - Nie można wystartować transakcji. Szczegóły: %1 - - - - Cannot commit transaction. Details: %1 - Nie można zatwierdzić transakcji. Szczegóły: %1 - - - - DbTree - - - Databases - Bazy danych - - - - Filter by name - Filtruj po nazwie - - - - Copy - Kopiuj - - - - Paste - Wklej - - - - Select all - Zaznacz wszystko - - - - Create a group - Utwórz grupÄ™ - - - - Delete the group - UsuÅ„ grupÄ™ - - - - Rename the group - ZmieÅ„ nazwÄ™ grupy - - - Add a database - Dodaj bazÄ™ danych - - - Edit the database - Edytuj bazÄ™ danych - - - Remove the database - UsuÅ„ bazÄ™ danych - - - Connect to the database - Połącz z bazÄ… danych - - - Disconnect from the database - Rozłącz siÄ™ z bazÄ… danych - - - - Import - Importuj - - - Export the database - Eksportuj bazÄ™ danych - - - Convert database type - Konwertuj typ bazy danych - - - Vacuum - Odkurz - - - Integrity check - Sprawdź spójność - - - Create a table - Utwórz tabelÄ™ - - - Edit the table - Edytuj tabelÄ™ - - - - Execution from file cancelled. Any queries executed so far have been rolled back. - Wykonywanie z pliku przerwane. Jakiekolwiek wykonane zapytania zostaÅ‚y wycofane. - - - - &Add a database - Dod&aj bazÄ™ danych - - - - &Edit the database - &Edytuj bazÄ™ danych - - - - &Remove the database - U&suÅ„ bazÄ™ danych - - - - &Connect to the database - &Połącz z bazÄ… danych - - - - &Disconnect from the database - &Rozłącz siÄ™ z bazÄ… danych - - - - &Export the database - &Eksportuj bazÄ™ danych - - - - Con&vert database type - Kon&wertuj typ bazy danych - - - - Vac&uum - Odk&urz - - - - &Integrity check - Sprawdź spó&jność - - - - Create a &table - Utwórz &tabelÄ™ - - - - Edit the t&able - Edytuj t&abelÄ™ - - - - Delete the ta&ble - UsuÅ„ ta&belÄ™ - - - - Create an &index - Utwórz &indeks - - - - Edit the i&ndex - Edytuj i&ndeks - - - - Delete the in&dex - UsuÅ„ in&deks - - - - Create a trig&ger - Utwórz wyz&walacz - - - - Edit the trigg&er - Edytuj wyzw&alacz - - - - Delete the trigge&r - UsuÅ„ wyzwa&lacz - - - - Create a &view - Utwórz &widok - - - - Edit the v&iew - Edytuj w&idok - - - - Delete the vi&ew - UsuÅ„ wi&dok - - - - &Refresh all database schemas - &OdÅ›wież schematy wszystkich baz danych - - - - Re&fresh selected database schema - OdÅ›wież schemat wy&branej bazy danych - - - - Open file's directory - Otwórz katalog pliku - - - - Execute SQL from file - Wykonaj SQL z pliku - - - - Generate query for table - Generuj zapytanie dla tabeli - - - - Entry with name %1 already exists in group %2. - Pozycja o nazwie %1 istnieje już w grupie %2. - - - - Are you sure you want to remove database '%1' from the list? - Czy napewno chcesz wycofać bazÄ™ '%1' z listy? - - - - Are you sure you want to remove following databases from the list: -%1 - Czy na pewno chcesz wycofać nastÄ™pujÄ…ce bazy z listy: -%1 - - - - Remove database - Wycofaj bazÄ™ - - - - Vacuum (%1) - Odkurz (%1) - - - - Autoincrement value for table '%1' has been reset successfully. - Wartość automatycznej inkrementacji dla tabeli '%1' zostaÅ‚a zresetowana. - - - - Are you sure you want to delete all data from table(s): %1? - Czy na pewno chcesz usunąć wszystkie dane z tabel(i): %1? - - - - Could not execute SQL, because application has failed to start transaction: %1 - Nie można wykonać SQLa, ponieważ aplikacja nie mogÅ‚a rozpocząć transakcji: %1 - - - - Could not open file '%1' for reading: %2 - Nie udaÅ‚o siÄ™ otworzyć pliku '%1' do odczytu: %2 - - - - Could not execute SQL, because application has failed to commit the transaction: %1 - Nie można wykonać SQLa, ponieważ aplikacja nie mogÅ‚a zatwierdzić transakcji: %1 - - - - Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - ZakoÅ„czono wykonywanie %1 zapytaÅ„ w %2 sekund(y). %3 nie zostaÅ‚y wykonane w zwiążku z błędami. - - - - Finished executing %1 queries in %2 seconds. - ZakoÅ„czono wykonywanie %1 zapytaÅ„ w %2 sekund(y). - - - - Could not execute SQL due to error. - Nie można wykonać SQL w zwiÄ…zku z błędem. - - - Drop the table - Porzuć tabelÄ™ - - - - Export the table - Eksportuj tabelÄ™ - - - - Import into the table - Importuj do tabeli - - - - Populate table - Zaludnij tabelÄ™ - - - - Create similar table - Utwórz podobnÄ… tabelÄ™ - - - Create an index - Utwórz indeks - - - Edit the index - Edytuj indeks - - - Drop the index - Porzuć indeks - - - Create a trigger - Utwórz wyzwalacz - - - Edit the trigger - Edytuj wyzwalacz - - - Drop the trigger - Porzuć wyzwalacz - - - Create a view - Utwórz widok - - - Edit the view - Edytuj widok - - - Drop the view - Porzuć widok - - - - Add a column - Dodaj kolumnÄ™ - - - - Edit the column - Edytuj kolumnÄ™ - - - - Delete the column - UsuÅ„ kolumnÄ™ - - - - Delete selected items - UsuÅ„ wybrane elementy - - - - Clear filter - Wyczyść filtr - - - Refresh all database schemas - OdÅ›wież schematy wszystkich baz danych - - - Refresh selected database schema - OdÅ›wież schemat wybranej bazy danych - - - Delete the table - UsuÅ„ tabelÄ™ - - - - Reset autoincrement sequence - Wyzeruj sekwencjÄ™ autoinkrementacji - - - Delete the index - UsuÅ„ indeks - - - Delete the trigger - UsuÅ„ wyzwalacz - - - Delete the view - UsuÅ„ widok - - - - - Erase table data - Wymaż dane tabeli - - - - - Database - Baza danych - - - - Grouping - Grupowanie - - - - - Create group - Utwórz grupÄ™ - - - - Group name - Nazwa grupy - - - - Delete group - UsuÅ„ grupÄ™ - - - - Are you sure you want to delete group %1? -All objects from this group will be moved to parent group. - Czy na pewno chcesz usunąć grupÄ™ %1? -Wszystkie obiekty z tej grupy zostanÄ… przeniesione do nadrzÄ™dnej grupy. - - - Delete database - UsuÅ„ bazÄ™ danych - - - Are you sure you want to delete database '%1'? - Czy na pewno chcesz usunąć bazÄ™ danych '%1'? - - - - - Cannot import, because no import plugin is loaded. - Nie można zaimportować, ponieważ żadna wtyczka importu nie zostaÅ‚a zaÅ‚adowana. - - - - - Cannot export, because no export plugin is loaded. - Nie można wyeksportować, ponieważ żadna wtyczka eksportu nie zostaÅ‚a zaÅ‚adowana. - - - Error while executing VACUUM on the database %1: %2 - Błąd podczas wykonywania VACUUM na bazie danych %1: %2 - - - VACUUM execution finished successfully. - Wykonanie VACUUM przebiegÅ‚o pomyÅ›lnie. - - - - Integrity check (%1) - Sprawdzanie spójnoÅ›ci (%1) - - - - Reset autoincrement - Wyzeruj autoinkrementacjÄ™ - - - - Are you sure you want to reset autoincrement value for table '%1'? - Czy na pewno chcesz wyzerować wartość autoinkrementacji dla tabeli '%1'? - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - WystÄ…piÅ‚ błąd podczas próby wyzerowania wartoÅ›ci autoinkrementacji dla tabeli '%1': %2 - - - Autoincrement value for table '%1' has been reset successfly. - Wartość autoinkrementacji dla tabeli '%1' zostaÅ‚a pomyÅ›lnie wyzerowana. - - - Are you sure you want to delete all data from table '%1'? - Czy na pewno chcesz usunąć wszystkie dane z tabeli '%1'? - - - - An error occurred while trying to delete data from table '%1': %2 - WystÄ…piÅ‚ błąd podczas próby usuniÄ™cia danych z tabeli '%1': %2 - - - - All data has been deleted for table '%1'. - Wszystkie dane z tabeli '%1' zostaÅ‚y usuniÄ™te. - - - - Following objects will be deleted: %1. - NastÄ™pujÄ…ce obiekty zostanÄ… usuniÄ™te: %1 - - - - Following databases will be removed from list: %1. - NastÄ™pujÄ…ce bazy danych zostanÄ… usuniÄ™te z listy: %1 - - - - Remainig objects from deleted group will be moved in place where the group used to be. - PozostaÅ‚e obiekty z usuniÄ™tej grupy bÄ™dÄ… przeniesione w miejsce, gdzie dotychczas byÅ‚a ta grupa. - - - - %1<br><br>Are you sure you want to continue? - %1<br><br>Czy na pewno chcesz kontynuować? - - - - Delete objects - UsuÅ„ obiekty - - - - DbTreeItemDelegate - - - error - dbtree labels - błąd - - - - (system table) - database tree label - (tabela systemowa) - - - - (virtual) - virtual table label - (wirtualna) - - - - (system index) - database tree label - (indeks systemowy) - - - - DbTreeModel - - - Database: %1 - dbtree tooltip - Baza danych: %1 - - - - Version: - dbtree tooltip - Wersja: - - - - File size: - dbtree tooltip - Rozmiar pliku: - - - - Encoding: - dbtree tooltip - Kodowanie: - - - Error details: - dbtree tooltip - Szczegóły błędu: - - - - Error: - dbtree tooltip - Błąd: - - - - Table : %1 - dbtree tooltip - Tablela: : %1 - - - - Columns (%1): - dbtree tooltip - Kolumny (%1): - - - - Indexes (%1): - dbtree tooltip - Indeksy (%1): - - - - Triggers (%1): - dbtree tooltip - Wyzwalacze (%1): - - - - Copy - Kopiuj - - - - Move - PrzenieÅ› - - - - Include data - Również dane - - - - Include indexes - Również indeksy - - - - Include triggers - Również wyzwalacze - - - - Abort - Przerwij - - - - Could not add dropped database file '%1' automatically. Manual setup is necessary. - Nie udaÅ‚o siÄ™ automatycznie dodać upuszczonego pliku bazy '%1'. NiezbÄ™dna rÄ™czna ingerencja. - - - - Referenced tables - Tabele powiÄ…zane - - - - Do you want to include following referenced tables as well: -%1 - Czy chcesz zawrzeć również powiÄ…zane tabele: -%1 - - - - Name conflict - Konflikt nazwy - - - - Following object already exists in the target database. -Please enter new, unique name, or press '%1' to abort the operation: - NastÄ™pujÄ…y obiekt istnieje już w docelowej bazie danych. -ProszÄ™ podać nowÄ…, unikalnÄ… nazwÄ™, lub nacisnąć '%1', aby przerwać operacjÄ™. - - - - SQL statements conversion - Konwersja zapytaÅ„ SQL - - - - Following error occurred while converting SQL statements to the target SQLite version: - NastÄ™pujÄ…ce błędy wystÄ…piÅ‚y podczas konwersji zapytaÅ„ SQL do docelowej wersji SQLite: - - - - Would you like to ignore those errors and proceed? - Czy chcesz zignorować te błędy i kontynuować? - - - - DdlHistoryWindow - - - Filter by database: - Filtruj po bazie danych: - - - - -- Queries executed on database %1 (%2) --- Date and time of execution: %3 -%4 - -- Zapytania wykonane na bazie %1 (%2) --- Data i godzina wykonania: %3 -%4 - - - - DDL history - Historia DDL - - - - DdlPreviewDialog - - - Queries to be executed - Zapytania do wykonania - - - - Don't show again - Nie pokazuj wiÄ™cej - - - - DebugConsole - - - SQLiteStudio Debug Console - Konsola Debugowania SQLiteStudio - - - - EditorWindow - - - Query - Zapytanie - - - - History - Historia - - - - Results in the separate tab - Wyniki w osobnej karcie - - - - Results below the query - Wyniki pod zapytaniem - - - - - SQL editor %1 - Edytor SQL %1 - - - - Results - Wyniki - - - - Execute query - Wykonaj zapytanie - - - - Explain query - WytÅ‚umacz zapytanie - - - - Clear execution history - sql editor - Wymaż historiÄ™ zapytaÅ„ - - - - Export results - sql editor - Wyeksportuj wyniki - - - - Create view from query - sql editor - Utwórz widok z zapytania - - - - Previous database - Poprzednia baza danych - - - - Next database - NastÄ™pna baza danych - - - - Show next tab - sql editor - Pokaż nastÄ™pnÄ… kartÄ™ - - - - Show previous tab - sql editor - Pokaż poprzedniÄ… kartÄ™ - - - - Focus results below - sql editor - Aktywuj wyniki poniżej - - - - Focus SQL editor above - sql editor - Aktywuj edytor SQL powyżej - - - - Delete selected SQL history entries - sql editor - UsuÅ„ wybrane wpisy z historii SQL - - - - Active database (%1/%2) - Aktywna baza danych (%1/%2) - - - - Query finished in %1 second(s). Rows affected: %2 - Zapytanie ukoÅ„czone w %1 sekund(y). Liczba przetworzonych wierszy: %2 - - - - Query finished in %1 second(s). - Zapytanie ukoÅ„czone w %1 sekund(y). - - - - Editor window "%1" has uncommitted data. - Okno edytora "%1" ma niezatwierdzone dane. - - - Query finished in %2 second(s). - Zapytanie ukoÅ„czone w %2 sekund(y). - - - - Clear execution history - Wymaż historiÄ™ zapytaÅ„ - - - - Are you sure you want to erase the entire SQL execution history? This cannot be undone. - Czy na pewno chcesz wymazać całą historiÄ™ zapytaÅ„ SQL? Tego nie można odwrócić. - - - - Cannot export, because no export plugin is loaded. - Nie można wyeksportować, ponieważ żadna wtyczka eksportu nie zostaÅ‚a zaÅ‚adowana. - - - - No database selected in the SQL editor. Cannot create a view for unknown database. - Nie wybrano bazdy danych w edytorze SQL. Nie można utworzyć widoku dla nieznanej bazy. - - - Editor window "%1" has uncommited data. - Okno edytora "%1" ma niezatwierdzone dane. - - - - ErrorsConfirmDialog - - - Errors - Błędy - - - - Following errors occured: - WystÄ…piÅ‚y nastÄ™pujÄ…ce błędy: - - - - Would you like to proceed? - Czy chcesz kontynuować? - - - - ExecFromFileDialog - - - Execute SQL from file - Wykonaj SQL z pliku - - - - Input file - Plik wejÅ›ciowy - - - - Path to file - Åšcieżka do pliku - - - - Browse for file - PrzeglÄ…daj pliki - - - - Options - Opcje - - - - File encoding - Kodowanie pliku - - - - Skip failing SQL statements - PomiÅ„ zapytania z błędami - - - - SQL scripts (*.sql);;All files (*) - Skrypty SQL (*.sql);;Wszystkie pliki (*) - - - - Execute SQL file - Wykonaj plik SQL - - - - Please provide file to be executed. - ProszÄ™ podać plik do wykonania - - - - Provided file does not exist or cannot be read. - Podany plik nie istnieje, lub nie można go odczytać. - - - - ExportDialog - - - Export - Eksportuj - - - - What do you want to export? - Co chcesz eksportować? - - - - A database - BazÄ™ danych - - - - A single table - PojedynczÄ… tabelÄ™ - - - - Query results - Wyniki zapytania - - - - Table to export - Tabela do wyeksportowania - - - - Database - Baza danych - - - - Table - Tabela - - - - Options - Opcje - - - - When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. - Gdy ta opcja jest odznaczona, to tylko DDL tabeli (zapytanie CREATE TABLE) jest eksportowane. - - - - Export table data - Eksportuj dane tabeli - - - - Export table indexes - Eksportuj indeksy tabeli - - - - Export table triggers - Eksportuj wyzwalacze tabeli - - - - Note, that exporting table indexes and triggers may be unsupported by some output formats. - Zwróć uwagÄ™, że eksportowanie indeksów i wyzwalaczy tabeli może nie być obsÅ‚ugiwane przez niektóre formaty wyjÅ›ciowe. - - - - Select database objects to export - Wybierz obiekty bazy danych do eksportu - - - - Export data from tables - Eksportuj dane z tabel - - - - Select all - Zaznacz wszystko - - - - Deselect all - Odznacz wszystko - - - - - Database: - Baza danych: - - - - Query to export results for - Zapytanie dla wyników do eksportu - - - - Query to be executed for results: - Zapytanie, które należy wykonać dla wyników: - - - - Export format and options - Format eksportu i opcje - - - - Export format - Format eksportu - - - - Output - WyjÅ›cie - - - - Exported file path - Åšcieżka do wyeksportowanego pliku - - - - Clipboard - Schowek - - - - File - Plik - - - - Exported text encoding: - Kodowanie wyeksportowanego tekstu: - - - - Export format options - Opcje formatu eksportowania - - - - Cancel - Anuluj - - - - - - Select database to export. - Wybierz bazÄ™ do eksportu. - - - - Select table to export. - Wybierz tabelÄ™ do eksportu. - - - - Enter valid query to export. - Wprowadź poprawne zapytanie do eksportu. - - - - Select at least one object to export. - Wybierz przynajmniej jeden obiekt do eksportu. - - - - You must provide a file name to export to. - Musisz podać nazwÄ™ pliku do którego należy wyeksportować. - - - - Path you provided is an existing directory. You cannot overwrite it. - Åšcieżka którÄ… podaÅ‚eÅ› jest istniejÄ…cym katalogiem. Nie można go nadpisać. - - - - The directory '%1' does not exist. - Katalog '%1' nie istnieje. - - - - The file '%1' exists and will be overwritten. - Plik '%1' istnieje i zostanie nadpisany. - - - - All files (*) - Wszystkie pliki (*) - - - - Pick file to export to - Wybierz plik do eksportu - - - - Internal error during export. This is a bug. Please report it. - WystÄ…piÅ‚ wewnÄ™trzny błąd podczas eksportu. To jest błąd programu. ProszÄ™ to zgÅ‚osić. - - - - FileExecErrorsDialog - - - Execution errors - Błędy wykonywania - - - - Following errors were encountered during execution of SQL statements from the file: - NastÄ™pujÄ…ce błędy wystÄ…piÅ‚y podczas wykonywania zapytaÅ„ SQL z pliku: - - - - SQL - SQL - - - - Error - Błąd - - - - Statements that were executed successfully were commited. - Wyniki zapytaÅ„, które zostaÅ‚y wykonane, zostaÅ‚y zatwierdzone. - - - - Statements that were executed successfully were rolled back. - Wyniki zapytaÅ„, które zostaÅ‚y wykonane zostaÅ‚y wycofane. - - - - FontEdit - - - Choose font - font configuration - Wybierz czcionkÄ™ - - - - Form - - - Active SQL formatter plugin - Aktywna wtyczka formatera SQL - - - - FormView - - - Commit row - form view - Zatwierdź wiersz - - - - Rollback row - form view - Wycofaj wiersz - - - - First row - form view - Pierwszy wiersz - - - - Previous row - form view - Poprzedni wiersz - - - - Next row - form view - NastÄ™pny wiersz - - - - Last row - form view - Ostatni wiersz - - - - Insert new row - form view - Wstaw nowy wiersz - - - - Delete current row - form view - UsuÅ„ bieżący wiersz - - - - FunctionsEditor - - - Filter funtions - Filtruj funkcje - - - - Function name: - Nazwa funkcji: - - - - Implementation language: - JÄ™zyk implementacji: - - - - Type: - Typ: - - - - Input arguments - Argumenty wejÅ›ciowe - - - - Undefined - Niezdefiniowane - - - - Databases - Bazy danych - - - - Register in all databases - Zarejestruj we wszystkich bazach danych - - - - Register in following databases: - Zarejestruj w nastÄ™pujÄ…cych bazach danych: - - - - Initialization code: - Kod inicjalizacji: - - - - - Function implementation code: - Kod implementacji funkcji: - - - - Final step implementation code: - Kod implementacji ostatniego kroku: - - - - SQL function editor - Edytor funkcji SQL - - - - Commit all function changes - Zatwierdź zmiany we wszystkich funkcjach - - - - Rollback all function changes - Wycofaj zmiany we wszystkich funkcjach - - - - Create new function - Utwórz nowÄ… funkcjÄ™ - - - - Delete selected function - UsuÅ„ wybranÄ… funkcjÄ™ - - - - Custom SQL functions manual - PodrÄ™cznik wÅ‚asnych funkcji SQL - - - - Add function argument - Dodaj argument funkcji - - - - Rename function argument - ZmieÅ„ nazwÄ™ argumentu funkcji - - - - Delete function argument - UsuÅ„ argument funkcji - - - - Move function argument up - PrzesuÅ„ argument funkcji w górÄ™ - - - - Move function argument down - PrzesuÅ„ argument funkcji w dół - - - - Scalar - Skalarna - - - - Aggregate - Agregacyjna - - - - Enter a non-empty, unique name of the function. - Wprowadź niepustÄ…, unikalnÄ… nazwÄ™ funkcji - - - - Pick the implementation language. - Wybierz jÄ™zyk implementacji. - - - - Per step code: - Kod pojedynczego kroku: - - - - Enter a non-empty implementation code. - Wprowadź niepusty kod implementacji. - - - - argument - new function argument name in function editor window - argument - - - - Functions editor window has uncommitted modifications. - Okno edytora funkcji ma niezatwierdzone modyfikacje. - - - Functions editor window has uncommited modifications. - Okno edytora funkcji ma niezatwierdzone zmiany. - - - - ImportDialog - - - Import data - Importuj dane - - - - Table to import to - Tabela do której należy importować - - - - Table - Tabela - - - - Database - Baza danych - - - - Data source to import from - ŹródÅ‚o danych z którego należy importować - - - - Data source type - Typ źródÅ‚a danych - - - - Options - Opcje - - - - Input file: - Plik wejÅ›ciowy: - - - - Text encoding: - Kodowanie tekstu: - - - - <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> - <p>JeÅ›li włączone, to jakiekolwiek naruszenia ograniczeÅ„, lub niepoprawny format danych (niepoprawna liczba kolumn), lub jakikolwiek inny problem, który wystÄ…pi podczas importu zostanie zignorowany i importowanie bÄ™dzie kontynuowane.</p> - - - - Ignore errors - Ignoruj błędy - - - - Data source options - Opcje źródÅ‚a danych - - - - Cancel - Anuluj - - - - If you type table name that doesn't exist, it will be created. - JeÅ›li wpiszesz nazwÄ™ tabeli, która nie istnieje, to zostanie ona stworzona. - - - - Enter the table name - Wprowadź nazwÄ™ tabeli - - - - Select import plugin. - Wybierz wtyczkÄ™ importu - - - - You must provide a file to import from. - Musisz podać plik z którego należy zaimportować. - - - - The file '%1' does not exist. - Plik '%1' nie istnieje. - - - - Path you provided is a directory. A regular file is required. - Åšcieżka którÄ… podaÅ‚eÅ› jest katalogiem. Wymagany jest zwykÅ‚y plik. - - - - Pick file to import from - Wybierz plik do importu - - - - IndexDialog - - - - Index - Indeks - - - - On table: - Na tabeli: - - - - Index name: - Nazwa indeksu: - - - - Partial index condition - Warunek indeksu częściowego: - - - - Unique index - Indeks unikalny - - - - Column - Kolumna - - - - Collation - Zestawienie - - - - Sort - Sortowanie - - - - Delete selected indexed expression - UsuÅ„ wybrane wyrażenie indeksowane. - - - - Moves selected index column up in the order, making it more significant in the index. - Przenosi wybranÄ… indeksowanÄ… kolumnÄ™ wyżej w kolejnoÅ›ci, czyniÄ…c jÄ… ważniejszÄ… w indeksie. - - - - Moves selected index column down in the order, making it less significant in the index. - Przenosi wybranÄ… indeksowanÄ… kolumnÄ™ niżej w kolejnoÅ›ci, czyniÄ…c jÄ… mniej ważnÄ… w indeksie. - - - - Edit selected indexed expression - Edytuj wybrane wyrażenie indeksowane - - - - Add indexed expression - Dodaj wyrażenie indeksowane - - - - DDL - DDL - - - - Tried to open index dialog for closed or inexisting database. - Próbowano otworzyć okno indeksu dla zamkniÄ™tej lub nieistniejÄ…cej bazy. - - - - Could not process index %1 correctly. Unable to open an index dialog. - Nie udaÅ‚o siÄ™ przetworzyć poprawnie indeksu %1. Nie można otworzyć okna indeksu. - - - - Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. - Unikalny indeks nie może zawierać wyrażeÅ„ indeksowanych. Albo usuÅ„ wyrażenia z poniższej listy, albo odznacz tÄ™ opcjÄ™. - - - - Pick the table for the index. - Wybierz tabelÄ™ dla indeksu. - - - - Select at least one column. - Zaznacz przynajmniej jednÄ… kolumnÄ™. - - - - Enter a valid condition. - Wprowadź poprawny warunek. - - - - default - index dialog - domyÅ›lne - - - - Sort order - table constraints - Kierunek sortowania - - - - - Error - index dialog - Błąd - - - - Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? - Nie można utworzyć indeksu, ponieważ wartoÅ›ci w wybranych kolumnach nie sÄ… unikalne. Czy chcesz wykonać zapytanie SELECT, aby zobaczyć wartoÅ›ci stwarzajÄ…ce problem? - - - - An error occurred while executing SQL statements: -%1 - WystÄ…piÅ‚ błąd podczas wykonywania zapytaÅ„ SQL: -%1 - - - - IndexExprColumnDialog - - - Indexed expression - Wyrażenie indeksowane - - - - Expression to index - Wyrażenie do indeksowania - - - - This expression is already indexed by the index. - To wyrażenie jest już indeksowane przez indeks. - - - - Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. - Kolumna powinna być indeksowana bezpoÅ›rednio, nie przez wyrażenie. Albo rozszerz to wyrażenie, aby zwieraÅ‚o coÅ› wiÄ™cej niż tylko nazwÄ™ kolumny, albo przerwij i wybierz tÄ… kolumnÄ™ bezpoÅ›rednio w oknie dialogowym indeksu. - - - - Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. - Kolumna '%1' nie należy do tabeli objÄ™tej tym indeksem. Wyrażenia indeksowane mogÄ… odnosić siÄ™ jedynie do kolumn z indeksowanej tabeli. - - - - It's forbidden to use 'SELECT' statements in indexed expressions. - Nie można używać zapytaÅ„ 'SELECT' w wyrażeniach indeksowanych. - - - - Enter an indexed expression. - Wprowadź wyrażenie indeksowane. - - - - Invalid expression. - Niepoprawne wyrażenie. - - - - LanguageDialog - - - Language - JÄ™zyk - - - - Please choose language: - ProszÄ™ wybrać jÄ™zyk: - - - - MainWindow - - - Database toolbar - Pasek narzÄ™dzi baz danych - - - - Structure toolbar - Pasek narzÄ™dzi struktury - - - - Tools - NarzÄ™dzia - - - - Window list - Lista okien - - - - View toolbar - Pasek narzÄ™dzi widoku - - - - Configuration widgets - Kontrolki konfiguracji - - - - Syntax highlighting engines - Silniki podÅ›wietlania skÅ‚adni - - - - Data editors - Edytory danych - - - - Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - Uruchomiono tryb debugowania. WciÅ›nij %1 lub wybierz menu 'Pomoc / Otwórz konsolÄ™ debugowania' aby otworzyć konsolÄ™ debugowania. - - - - Running in debug mode. Debug messages are printed to the standard output. - Uruchomiono tryb debugowania. WiadomoÅ›ci debugujÄ…ce sÄ… wyÅ›wietlane na standardowym wyjÅ›ciu. - - - - You need to restart application to make the language change take effect. - Należy zrestartować aplikacjÄ™, aby nastÄ…piÅ‚a zmiana jÄ™zyka. - - - Open SQL editor - Otwórz edytor SQL - - - Open DDL history - Otwórz historiÄ™ DDL - - - Open SQL functions editor - Otwórz edytor funkcji SQL - - - Open collations editor - Otwórz edytor zestawieÅ„ - - - Import - Importuj - - - Export - Eksportuj - - - Open configuration dialog - Otwórz okno konfiguracji - - - Tile windows - Ustaw okna w pÅ‚ytki - - - Tile windows horizontally - Ustaw okno poziomo - - - Tile windows vertically - Ustaw okna pionowo - - - Cascade windows - Ustaw okna kaskadowo - - - - Next window - NastÄ™pne okno - - - - Previous window - Poprzednie okno - - - - Hide status field - Ukryj pole statusu - - - Close selected window - Zamknij wybrane okno - - - Close all windows but selected - Zamknij wszystkie okna, oprócz wybranego - - - Close all windows - Zamknij wszystkie okna - - - Restore recently closed window - Przywróć ostatnio zamkniÄ™te okno - - - Rename selected window - ZmieÅ„ nazwÄ™ wybranego okna - - - - Open Debug Console - Otwórz KonsolÄ™ Debugowania - - - - Open CSS Console - Otwórz konsolÄ™ CSS - - - Report a bug - ZgÅ‚oÅ› błąd - - - Propose a new feature - ZgÅ‚oÅ› pomysÅ‚ - - - About - O programie - - - Licenses - Licencje - - - Open home page - Otwórz stronÄ™ domowÄ… - - - Open forum page - Otwórz stronÄ™ forum - - - User Manual - PodrÄ™cznik Użytkownika - - - SQLite documentation - Dokumentacja SQLite - - - Report history - Historia zgÅ‚oszeÅ„ - - - Check for updates - Sprawdź aktualizacje - - - Database - menubar - Baza danych - - - Structure - menubar - Struktura - - - View - menubar - Widok - - - - Window list - menubar view menu - Lista okien - - - Tools - menubar - NarzÄ™dzia - - - Help - Pomoc - - - - Open SQL &editor - Otwórz &edytor SQL - - - - Open DDL &history - Otwórz &historiÄ™ DDL - - - - Open SQL &functions editor - Otwórz edytor &funkcji SQL - - - - Open &collations editor - Otwórz edytor &zestawieÅ„ - - - - Open ex&tension manager - O&twórzy menadżera rozszerzeÅ„ - - - - &Import - &Importuj - - - - E&xport - E&ksportuj - - - - Open confi&guration dialog - Otwórz okno konfi&guracji - - - - &Tile windows - Ustaw okna w pÅ‚y&tki - - - - Tile windows &horizontally - Ustaw okno po&ziomo - - - - Tile windows &vertically - Ustaw okna pio&nowo - - - - &Cascade windows - Ustaw okna &kaskadowo - - - - Close selected &window - Zamknij &wybrane okno - - - - Close all windows &but selected - Zamknij wszystkie okna, &oprócz wybranego - - - - Close &all windows - Z&amknij wszystkie okna - - - - Re&store recently closed window - Przywróć o&statnio zamkniÄ™te okno - - - - &Rename selected window - ZmieÅ„ nazwÄ™ wyb&ranego okna - - - - Report a &bug - ZgÅ‚oÅ› &błąd - - - - Propose a new &feature - ZgÅ‚oÅ› &pomysÅ‚ - - - - &About - O progra&mie - - - - &Licenses - &Licencje - - - - Open home &page - Otwórz stronÄ™ &domowÄ… - - - - Open fo&rum page - Otwórz stronÄ™ &forum - - - - User &Manual - &PodrÄ™cznik Użytkownika - - - - SQLite &documentation - &Dokumentacja SQLite - - - - Bugs and feature &requests - Błęd&y i pomysÅ‚y - - - - Check for &updates - Sprawdź akt&ualizacje - - - - &Database - menubar - Bazy &danych - - - - &Structure - menubar - &Struktura - - - - &View - menubar - &Widoki - - - - &Tools - menubar - &NarzÄ™dzia - - - - &Help - &Pomoc - - - - Could not set style: %1 - main window - Nie udaÅ‚o siÄ™ ustawić stylu: %1 - - - - Cannot export, because no export plugin is loaded. - Nie można wyeksportować, ponieważ żadna wtyczka eksportu nie zostaÅ‚a zaÅ‚adowana. - - - - Cannot import, because no import plugin is loaded. - Nie można zaimportować, ponieważ żadna wtyczka importu nie zostaÅ‚a zaÅ‚adowana. - - - - Rename window - ZmieÅ„ nazwÄ™ okna - - - - Enter new name for the window: - Wprowadź nowÄ… nazwÄ™ dla okna: - - - - New updates are available. <a href="%1">Click here for details</a>. - Nowe aktualizacje sÄ… dostÄ™pne: <a href="%1">Kliknij aby poznać szczegóły</a>. - - - - You're running the most recent version. No updates are available. - Uruchomiona jest najnowsza wersja. Nie ma dostÄ™pnych aktualizacji. - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - Baza danych podana w parametrach linii poleceÅ„ (%1) byÅ‚a już na liÅ›cie pod nazwÄ…: %2 - - - - Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 - Baza danych podana w linii poleceÅ„ (%1) jest tymczasowo dodana do listy pod nazwÄ…: %2 - - - - Could not add database %1 to list. - Nie udaÅ‚o siÄ™ dodać bazy danych %1 do listy. - - - - MdiWindow - - Uncommited changes - Niezatwierdzone dane - - - - Uncommitted changes - Niezatwierdzone zmiany - - - - Close anyway - Zamknij mimo to - - - - Don't close - Nie zamykaj - - - - MultiEditor - - - Null value - multieditor - Wartość null - - - - Configure editors for this data type - Skonfiguruj edytory dla tego typu danych - - - - Open another tab - Otwórz kolejnÄ… zakÅ‚adkÄ™ - - - - Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. - Wtyczka edytora danych '%1' nie jest zaÅ‚adowana, podczas gdy jest ona zdefiniowana do edycji typu danych '%1'. - - - - Deleted - multieditor - UsuniÄ™to - - - - Read only - multieditor - Tylko do odczytu - - - - MultiEditorBool - - Boolean - Logiczna - - - - MultiEditorBoolPlugin - - - Boolean - Logiczna - - - - MultiEditorDate - - Date - Data - - - - MultiEditorDatePlugin - - - Date - Data - - - - MultiEditorDateTime - - Date & time - Data i czas - - - - MultiEditorDateTimePlugin - - - Date & time - Data i czas - - - - MultiEditorHex - - Hex - Heks - - - - MultiEditorHexPlugin - - - Hex - Heks - - - - MultiEditorNumeric - - Number - Liczba - - - Number - numeric multi editor tab name - Liczba - - - - MultiEditorNumericPlugin - - - Number - numeric multi editor tab name - Liczba - - - - MultiEditorText - - Text - Tekst - - - - Tab changes focus - Tabulator zmienia aktywność - - - - Cut - Wytnij - - - - Copy - Kopiuj - - - - Paste - Wklej - - - - Delete - UsuÅ„ - - - - Undo - Cofnij - - - - Redo - Przywróć - - - - MultiEditorTextPlugin - - - Text - Tekst - - - - MultiEditorTime - - Time - Czas - - - - MultiEditorTimePlugin - - - Time - Czas - - - - NewConstraintDialog - - - New constraint - Nowe ograniczenie - - - - - Primary Key - new constraint dialog - Klucz główny - - - - - Foreign Key - new constraint dialog - Klucz obcy - - - - - Unique - new constraint dialog - WartoÅ›ci unikalne - - - - - Check - new constraint dialog - Warunek - - - - Not NULL - new constraint dialog - Niepuste - - - - Collate - new constraint dialog - Zestawienie - - - - Default - new constraint dialog - Wartość domyÅ›lna - - - - NewVersionDialog - - - SQLiteStudio updates - Aktualizacje SQLiteStudio - - - - New updates are available! - DostÄ™pne sÄ… nowe aktualizacje! - - - - Component - Komponent - - - - This application will be closed and the update installer will start to download and install all the updates. - Aplikacja zostanie zamkniÄ™ta i uruchomiony zostanie instalator aktualizacji, który Å›ciÄ…gnie i zainstaluje wszystkie aktualizacje. - - - Current version - Obecna wersja - - - - Update version - Wersja aktualizacji - - - - Check for updates on startup - Sprawdzaj aktualizacje na starcie - - - - Update to new version! - Aktualizuj do nowej wersji! - - - The update will be automatically downloaded and installed. This will also restart application at the end. - Aktualizacja bÄ™dzie pobrana i zainstalowana automatycznie. Spowoduje to również na koÅ„cu restart aplikacji. - - - - Not now. - Nie teraz. - - - - Don't install the update and close this window. - Nie instaluj aktualizacji i zamknij to okno. - - - - PopulateConfigDialog - - - Populating configuration - Konfiguracja zaludniania - - - - Configuring <b>%1</b> for column <b>%2</b> - Konfigurowanie <b>%1</b> dla kolumny <b>%2</b> - - - - PopulateDialog - - - Populate table - Zaludnij tabelÄ™ - - - - Database - Baza danych - - - - Table - Tabela - - - - Columns - Kolumny - - - - Number of rows to populate: - Liczba wierszy do zaludnienia: - - - - Populate - populate dialog button - Zaludnij - - - - Abort - Przerwij - - - - Configure - Konfiguruj - - - - Populating configuration for this column is invalid or incomplete. - Konfiguracja zaludniania dla tej kolumny jest niepoprawna lub niekompletna. - - - - Select database with table to populate - Wybierz bazÄ™ danych z tabelÄ… do zaludnienia - - - - Select table to populate - Wybierz tabelÄ™ do zaludnienia - - - - You have to select at least one column. - Musisz zaznaczyć przynajmniej jednÄ… kolumnÄ™. - - - - QObject - - - The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. - Mechanizm wykonywania zapytaÅ„ miaÅ‚ problemy z wyciÄ…gniÄ™ciem wÅ‚asnoÅ›ci ROWID. To może być błąd aplikacji. Możesz to zgÅ‚osić. - - - - Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. - Ta kolumna jest wynikiem wyrażenia SQL, a nie zwykÅ‚ej selekcji kolumny. Takie kolumny nie mogÄ… być edytowane. - - - - Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. - Ta kolumna należy do systemowej tabeli SQLite. Te tabele nie mogÄ… być edytowane bezpoÅ›rednio. - - - - Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). - Nie można edytować kolumn, które sÄ… wynikiem zÅ‚ożonego zapytania %1 (tego, które zawiera sÅ‚owo kluczowe %2, %3, lub %4). - - - - Cannot edit results of query other than %1. - Nie można edytować wyników zapytania innego niż %1. - - - - Cannot edit columns that are result of aggregated %1 statements. - Nie można edytować kolumn, które sÄ… wynikiem zapytania agregacyjnego %1. - - - - Cannot edit columns that are result of %1 statement. - Nie można edytować kolumn, które sÄ… wynikiem zapytania %1. - - - - Cannot edit columns that are result of common table expression statement (%1). - Nie można edytować kolumn, które sÄ… wynikiem zapytania ze wspólnym wyrażeniem tabeli (%1). - - - - - - - on conflict: %1 - data view tooltip - w razie konfliktu: %1 - - - - references table %1, column %2 - data view tooltip - odwoÅ‚uje siÄ™ do tabeli %1, kolumny %2 - - - - condition: %1 - data view tooltip - warunek: %1 - - - - collation name: %1 - data view tooltip - nazwa zestawienia: %1 - - - - Data grid view - Widok siatki danych - - - - Copy cell(s) contents to clipboard - Skopiuj zawartość komórek do schowka. - - - - Copy cell(s) contents together with header to clipboard - Skopiuj zawartość komórek z z nagłówkiem do schowka - - - - Paste cell(s) contents from clipboard - Wklej zawartość komórkek ze schowka. - - - - Set empty value to selected cell(s) - Ustaw pustÄ… wartość dla wybranych komórek - - - - Set NULL value to selected cell(s) - Ustaw wartość NULL dla wybranych komórek - - - - Commit changes to cell(s) contents - Zatwierdź zmiany dla zawartoÅ›ci komórek - - - - Rollback changes to cell(s) contents - Wycofaj zmiany dla zawartoÅ›ci komórek - - - - Delete selected data row - UsuÅ„ wybrane wiersze danych - - - - Insert new data row - Wstaw nowy wiersz danych - - - - Open contents of selected cell in a separate editor - Otwórz zawartość wybranej komórki w osobnym edytorze - - - - Total pages available: %1 - Liczba dostÄ™pnych stron: %1 - - - - Total rows loaded: %1 - Liczba zaÅ‚adowanych wierszy: %1 - - - - Data view (both grid and form) - Widok danych (zarówno siatki i formularza) - - - - Refresh data - OdÅ›wież dane - - - - Switch to grid view of the data - Przełącz do widoku siatki danych - - - - Switch to form view of the data - Przełącz do widoku formularza danych - - - - Database list - Lista baz - - - - Delete selected item - UsuÅ„ zaznaczony element - - - - Clear filter contents - Wyczyść zawartość filtra - - - - Refresh schema - OdÅ›wież schemat - - - - Refresh all schemas - OdÅ›wież wszystkie schematy - - - - Add database - Dodaj bazÄ™ danych - - - - Select all items - Zaznacz wszystkie elementy - - - - Copy selected item(s) - Kopiuj zaznaczone elementy - - - - - - Paste from clipboard - Wklej ze schowka - - - - Tables - Tabele - - - - Indexes - Indeksy - - - - Triggers - Wyzwalacze - - - - Views - Widoki - - - - Columns - Kolumny - - - - Data form view - Widok formularza danych - - - - Commit changes for current row - Zatawierdź zmiany dla bieżącego wiersza - - - - Rollback changes for current row - Wycofaj zmiany dla bieżącego wiersza - - - - Go to first row on current page - Przejdź do pierwszego wiersza na bieżącej stronie - - - - Go to next row - Przejdź do nastÄ™pnego wiersza - - - - Go to previous row - Przejdź do poprzedniego wiersza - - - - Go to last row on current page - Przejdź do ostatniego wiersza na bieżącej stronie - - - - Insert new row - Wstaw nowy wiersz - - - - Delete current row - UsuÅ„ bieżący wiersz - - - - Main window - Okno główne - - - - Open SQL editor - Otwórz edytor SQL - - - - Previous window - Poprzednie okno - - - - Next window - NastÄ™pne okno - - - - Hide status area - Ukryj pole statusu - - - - Open configuration dialog - Otwórz okno konfiguracji - - - - Open Debug Console - Otwórz KonsolÄ™ Debugowania - - - - Open CSS Console - Otwórz konsolÄ™ CSS - - - - Cell text value editor - Edytor tekstowy wartoÅ›ci komórki - - - - - Cut selected text - Wytnij wybrany tekst - - - - - Copy selected text - Skopiuj wybrany tekst - - - - - Delete selected text - UsuÅ„ wybrany tekst - - - - - Undo - Cofnij - - - - - Redo - Przywróć - - - - SQL editor input field - Pole wprowadzania edytora SQL - - - - Select whole editor contents - Zaznacz całą zawartość edytora - - - - Save contents into a file - Zapisz zawartość do pliku - - - - Load contents from a file - Wczytaj zawartość z pliku - - - - Find in text - Znajdź w tekÅ›cie - - - - Find next - Znajdź nastÄ™pny - - - - Find previous - Znajdź poprzedni - - - - Replace in text - ZmieÅ„ w tekÅ›cie - - - - Delete current line - UsuÅ„ bieżącÄ… liniÄ™ - - - - Request code assistant - WywoÅ‚aj asystenta kodu - - - - Format contents - Formatuj zawartość - - - - Move selected block of text one line down - PrzenieÅ› wybrany blok tekstu o jednÄ… liniÄ™ w dół - - - - Move selected block of text one line up - PrzenieÅ› wybrany blok tekstu o jednÄ… liniÄ™ w górÄ™ - - - - Copy selected block of text and paste it a line below - Skopiuj wybrany blok tekstu i wklej go poniżej - - - - Copy selected block of text and paste it a line above - Skopiuj wybrany blok tekstu i wklej go powyżej - - - - Toggle comment - Przełącz komentarz - - - - All SQLite databases - Wszystkie bazy danych SQLite - - - - All files - Wszystkie pliki - - - - - Database file - Plik bazy danych - - - Reports history window - Okno history zgÅ‚oszeÅ„ - - - Delete selected entry - UsuÅ„ wybranÄ… pozycjÄ™ - - - - SQL editor window - Okno edytora SQL - - - - Execute query - Wykonaj zapytanie - - - - Execute "%1" query - Wykonaj zapytanie "%1" - - - - Switch current working database to previous on the list - ZmieÅ„ roboczÄ… bazÄ™ danych na poprzedniÄ… z listy - - - - Switch current working database to next on the list - ZmieÅ„ roboczÄ… bazÄ™ danych na nastÄ™pnÄ… z listy - - - - Go to next editor tab - Przejdź do nastÄ™pnej karty edytora - - - - Go to previous editor tab - Przejdź do poprzedniej karty edytora - - - - Move keyboard input focus to the results view below - PrzenieÅ› aktywność klawiatury do widoku wyników poniżej - - - - Move keyboard input focus to the SQL editor above - PrzenieÅ› aktywność klawiatury do edytora SQL powyżej - - - - Delete selected SQL history entries - UsuÅ„ wybrane wpisy z historii SQL - - - - Table window - Okno tabeli - - - - Refresh table structure - OdÅ›wież strukturÄ™ tabeli - - - - Add new column - Dodaj nowÄ… kolumnÄ™ - - - - Edit selected column - Edytuj wybranÄ… kolumnÄ™ - - - - Delete selected column - UsuÅ„ wybranÄ… kolumnÄ™ - - - - Export table data - Eksportuj dane tabeli - - - - Import data to the table - Importuj dane do tabeli - - - - Add new table constraint - Dodaj nowe ograniczenie tabeli - - - - Edit selected table constraint - Edytuj wybrane ograniczenie tabeli - - - - Delete selected table constraint - UsuÅ„ wybrane ograniczenie tabeli - - - - Refresh table index list - OdÅ›wież listÄ™ indeksów tabeli - - - - Add new index - Dodaj nowy indeks - - - - Edit selected index - Edytuj wybrany indeks - - - - Delete selected index - UsuÅ„ wybrany indeks - - - - Refresh table trigger list - OdÅ›wież listÄ™ wyzwalaczy tabeli - - - - - Add new trigger - Dodaj nowy wyzwalacz - - - - - Edit selected trigger - Edytuj wybrany wyzwalacz - - - - - Delete selected trigger - UsuÅ„ wybrany wyzwalacz - - - - - Go to next tab - Przejdź do nastÄ™pnej karty - - - - - Go to previous tab - Przejdź do poprzedniej karty - - - - A view window - Okno widoku - - - - Refresh view trigger list - OdÅ›wież listÄ™ wizwalaczy widoku - - - - QuitConfirmDialog - - Uncommited changes - Niezatwierdzone dane - - - - Uncommitted changes - Niezatwierdzone zmiany - - - - Are you sure you want to quit the application? - -Following items are pending: - Czy na pewno chcesz zamknąć aplikacjÄ™? - -NastÄ™pujÄ…ce elementy sÄ… w toku: - - - - SearchTextDialog - - - Find or replace - Znajdź lub zastÄ…p - - - - Find: - Znajdź: - - - - Case sensitive - UwzglÄ™dniaj wielkość liter - - - - Search backwards - Szukaj wstecz - - - - Regular expression matching - Dopasowywanie wyrażeniem regularnym - - - - Replace && -find next - ZastÄ…p i -znajdź nastÄ™pny - - - - Replace with: - ZastÄ…p: - - - - Replace all - ZastÄ…p wszystkie - - - - Find - Znajdź - - - - SortDialog - - - Sort by columns - Sortuj wg. kolumn - - - - - Column - Kolumna - - - - - Order - Kierunek - - - - Sort by: %1 - Sortuj po: %1 - - - - Move column up - PrzesuÅ„ kolumnÄ™ w górÄ™ - - - - Move column down - PrzesuÅ„ kolumnÄ™ w dół - - - - SqlEditor - - - Cut - sql editor - Wytnij - - - - Copy - sql editor - Kopiuj - - - - Paste - sql editor - Wklej - - - - Delete - sql editor - UsuÅ„ - - - - Select all - sql editor - Zaznacz wszystko - - - - Undo - sql editor - Cofnij - - - - Redo - sql editor - Przywróć - - - - Complete - sql editor - DopeÅ‚nij - - - - Format SQL - sql editor - Formatuj SQL - - - - Save SQL to file - sql editor - Zapisz SQL do pliku - - - - Select file to save SQL - sql editor - Wybierz plik do zapisu SQL - - - - Load SQL from file - sql editor - Wczytaj SQL z pliku - - - - Delete line - sql editor - UsuÅ„ liniÄ™ - - - - Move block down - sql editor - PrzesuÅ„ blok w dół - - - - Move block up - sql editor - PrzesuÅ„ blok w górÄ™ - - - - Copy block down - sql editor - Skopiuj blok w dół - - - - Copy up down - sql editor - Skopiuj blok w górÄ™ - - - - Find - sql editor - Znajdź - - - - Find next - sql editor - Znajdź nastÄ™pny - - - - Find previous - sql editor - Znajdź poprzedni - - - - Replace - sql editor - ZastÄ…p - - - - Toggle comment - sql editor - Przełącz komentarz - - - - Saved SQL contents to file: %1 - Zapisano zawartość SQL do pliku: %1 - - - - Syntax completion can be used only when a valid database is set for the SQL editor. - DopeÅ‚nianie skÅ‚adni może być użyte tylko wtedy, gdy poprawna baza danych jest ustawiona w edytorze SQL. - - - - Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. - Zawartość edytora SQL jest ogromna, wiÄ™c sprawdzanie błędów i podÅ›wietlanie istniejÄ…cych obiektów zostaÅ‚o tymczasowo wyłączone. - - - - Save to file - Zapisz do pliku - - - - Could not open file '%1' for writing: %2 - Nie udaÅ‚o siÄ™ otworzyć pliku '%1' do zapisu: %2 - - - - SQL scripts (*.sql);;All files (*) - Skrypty SQL (*.sql);;Wszystkie pliki (*) - - - - Open file - Otwórz plik - - - - Could not open file '%1' for reading: %2 - Nie udaÅ‚o siÄ™ otworzyć pliku '%1' do odczytu: %2 - - - - Reached the end of document. Hit the find again to restart the search. - OsiÄ…gniÄ™to koniec dokumentu. WciÅ›nij szukanie ponownie, aby zrestartować szukanie. - - - - SqlQueryItem - - - Column: - data view tooltip - Kolumna: - - - - Data type: - data view - Typ danych: - - - - Table: - data view tooltip - Tabela: - - - - Constraints: - data view tooltip - Ograniczenie: - - - This cell is not editable, because: %1 - Tej komórki nie można edytować, ponieważ: %1 - - - - Cannot load the data for a cell that refers to the already closed database. - Nie można zaÅ‚adować danych dla komórki, która odwoÅ‚uje siÄ™ do zamkniÄ™tej już bazy danych. - - - - SqlQueryItemDelegate - - Cannot edit this cell. Details: %2 - Nie można edytować tej komórki. Szczegóły: %2 - - - - The row is marked for deletion. - Wiersz jest zaznaczony do usuniÄ™cia. - - - - - - - - Cannot edit this cell. Details: %1 - Nie można edytować tej komórki. Szczegóły: %1 - - - - - Structure of this table has changed since last data was loaded. Reload the data to proceed. - Struktura tej tabeli zmieniÅ‚a siÄ™ od ostatniego Å‚adowania danych. PrzeÅ‚aduj dane, aby kontynuować. - - - - Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). - Edytowanie ogromnych iloÅ›ci danych w podrÄ™cznym edytorze nie jest dobrym pomysÅ‚em. Może być on powolny i nieporÄ™czny. Lepiej edytować takie duże iloÅ›ci danych w Widoku Formularza, lub w osobnym oknie edytora (dostÄ™pnym w menu prawego klikniÄ™cia myszy). - - - - Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. - Klucz obcy dla kolumny %2 ma wiÄ™cej niż %1 możliwych wartoÅ›ci. To zbyt wiele, by wyÅ›wietlić w liÅ›cie rozwijanej. Musisz edytować wartość rÄ™cznie. - - - - SqlQueryModel - - - - Only one query can be executed simultaneously. - Tylko jedno zapytanie może być wykonywane w danym momencie. - - - Uncommited data - Niezatwierdzone dane - - - There are uncommited data changes. Do you want to proceed anyway? All uncommited changes will be lost. - Niektóre zmiany w danych nie zostaÅ‚y zatwierdzone. Czy na pewno chcesz kontynuować? Wszystkie niezatwierdzone zmiany zostanÄ… utracone. - - - - Cannot commit the data for a cell that refers to the already closed database. - Nie można zatwierdzić danych dla komórki, która odnosi siÄ™ do zamkniÄ™tej już bazy danych. - - - - Could not begin transaction on the database. Details: %1 - Nie udaÅ‚o siÄ™ rozpocząć transakcji na bazie danych. Szczegóły: %1 - - - An error occurred while commiting the transaction: %1 - WystÄ…piÅ‚ błąd podczas zatwierdzania transakcji: %1 - - - - An error occurred while rolling back the transaction: %1 - WystÄ…piÅ‚ błąd podczas wycofywania transakcji: %1 - - - - Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. - Próbowano zatwierdzić komórkÄ™, której nie można edytować (a mimo to zostaÅ‚a zmodyfikowana i czeka na zatwierdzenie)! To jest błąd. ProszÄ™ to zgÅ‚osić. - - - An error occurred while commiting the data: %1 - WystÄ…piÅ‚ błąd podczas zatwierdzania danych: %1 - - - - - Error while executing SQL query on database '%1': %2 - Błąd podczas wykonywania zapytania SQL na bazie '%1': %2 - - - Error while executing SQL query: %1 - Błąd podczas wykonywania zapytania SQL: %1 - - - - Uncommitted data - Niezatwierdzone dane - - - - There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. - Niektóre zmiany w danych nie zostaÅ‚y zatwierdzone. Czy na pewno chcesz kontynuować? Wszystkie niezatwierdzone zmiany zostanÄ… utracone. - - - - An error occurred while committing the transaction: %1 - WystÄ…piÅ‚ błąd podczas zatwierdzania transakcji: %1 - - - - An error occurred while committing the data: %1 - WystÄ…piÅ‚ błąd podczas zatwierdzania danych: %1 - - - - Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - Liczba wierszy na stronÄ™ zostaÅ‚a zmniejszona do %1, w zwiÄ…zku z liczbÄ… kolumn (%2) w widoku danych. - - - - Error while loading query results: %1 - Błąd podczas wczytywania wyników zapytania: %1 - - - - Insert multiple rows - Wstaw wiele wierszy - - - - Number of rows to insert: - Liczba wierszy do wstawienia: - - - - SqlQueryView - - - Go to referenced row in... - Idź do powiÄ…zanego wiersza w... - - - - Copy - Kopiuj - - - - Copy as... - Kopiuj jako... - - - - Paste - Wklej - - - - Paste as... - Wklej jako... - - - - Set NULL values - Ustaw wartoÅ›ci NULL - - - - Erase values - Wymaż wartoÅ›ci - - - - Edit value in editor - Edytuj wartość w edytorze - - - - Commit - Zatwierdź - - - - Copy with headers - Kopiuj z nagłówkami - - - - Rollback - Wycofaj - - - - Commit selected cells - Zatwierdź zaznaczone komórki - - - - Rollback selected cells - Wycofaj zaznaczone komórki - - - - Define columns to sort by - Zdefiniuj kolumny po których sortować - - - - Remove custom sorting - Wycofaj wÅ‚asne sortowanie - - - - Insert row - Wstaw wiersz - - - - Insert multiple rows - Wstaw wiele wierszy - - - - Delete selected row - UsuÅ„ zaznaczony wiersz - - - - Show value in a viewer - Pokaż wartość w przeglÄ…darce - - - - Generate query for selected cells - Generuj zapytanie dla wybranych komórek - - - - No items selected to paste clipboard contents to. - Nie wybrano elementów do których należy wkleić zawartość schowka. - - - - Go to referenced row in table '%1' - Idź do powiÄ…zanego wiersza w tabeli '%1' - - - - table '%1' - tabela '%1' - - - - Referenced row (%1) - PowiÄ…zany wiersz (%1) - - - - Trim pasted text? - Przyciąć wklejany tekst? - - - - The pasted text contains leading or trailing white space. Trim it automatically? - Wklejany tekst zawiera spacje na poczÄ…tku lub koÅ„cu. Czy przyciąć go automatycznie? - - - - Edit value - Edytuj wartość - - - - SqlTableModel - - Error while commiting new row: %1 - Błąd podczas zatwierdzania nowego wiersza: %1 - - - - Error while committing new row: %1 - Błąd podczas zatwierdzania nowego wiersza: %1 - - - - Error while deleting row from table %1: %2 - Błąd podczas usuwania wiersza z tabeli %1: %2 - - - - SqliteExtensionEditor - - - Filter extensions - Filtruj rozszerzenia - - - - Leave empty to use default function - Pozostaw puste, aby użyć domyÅ›lnej funkcji - - - - Extension file - Plik rozszerzenia - - - - Initialization function - Funkcja inicjalizujÄ…ca - - - - Databases - Bazy danych - - - - Register in all databases - Zarejestruj we wszystkich bazach danych - - - - Register in following databases: - Zarejestruj w nastÄ™pujÄ…cych bazach danych: - - - - Extension manager window has uncommitted modifications. - Okno menadżera rozszerzeÅ„ ma niezatwierdzone modyfikacje. - - - - Extension manager - Menadżer rozszerzeÅ„ - - - - Commit all extension changes - Zatwierdź wszystkie zmiany w rozszerzeniach - - - - Rollback all extension changes - Wycofaj wszystkie zmiany w rozszerzeniach - - - - Add new extension - Dodaj nowe rozszerzenie - - - - Remove selected extension - UsuÅ„ wybrane rozszerzenie - - - - Editing extensions manual - PodrÄ™cznik edytowania rozszerzeÅ„ - - - - File with given path does not exist or is not readable. - Plik o podanej Å›cieżce nie istnieje lub nie można go odczytać. - - - - Unable to load extension: %1 - Nie można zaÅ‚adować rozszerzenia: %1 - - - - Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - Niepoprawna nazwa funkcji inicjalizujÄ…cej. Nazwa funkcji może zawierać jedynie znaki alfanumeryczne i znak podkreÅ›lenia. - - - - Dynamic link libraries (*.dll);;All files (*) - Biblioteki linkowania dynamicznego (*.dll);;Wszystkie pliki (*) - - - - Shared objects (*.so);;All files (*) - Obiekty wspóldzielone (*.so);;Wszystkie pliki (*) - - - - Dynamic libraries (*.dylib);;All files (*) - Biblioteki dynamiczne (*.dylib);;Wszystkie pliki (*) - - - - All files (*) - Wszystkie pliki (*) - - - - Open file - Otwórz plik - - - - StatusField - - - Status - Status - - - - Copy - Kopiuj - - - - Clear - Wyczyść - - - - TableConstraintsModel - - - Type - table constraints - Typ - - - - Details - table constraints - Szczegóły - - - - Name - table constraints - Nazwa - - - - TableForeignKeyPanel - - - Foreign table: - Tabela obca: - - - - SQLite 2 does not support foreign keys officially, -but it's okay to use them anyway. - SQLite 2 oficjalnie nie obsÅ‚uguje kluczy obcych, -ale można ich używać. - - - - Columns - Kolumny - - - - Local column - Kolumna lokalna - - - - Foreign column - Kolumna obca - - - - Reactions - Reakcje - - - - Deferred foreign key - Klucz obcy odroczony - - - - Named constraint - Nazwane ograniczenie - - - - Constraint name - Nazwa ograniczenia - - - - Pick the foreign column. - Wybierz kolumnÄ™ obcÄ… - - - - Pick the foreign table. - Wybierz tabelÄ™ obcÄ… - - - - Select at least one foreign column. - Wybierz przynajmnie jednÄ… kolumnÄ™ obcÄ…. - - - - Enter a name of the constraint. - Wprowadź nazwÄ™ ograniczenia. - - - - Foreign column - table constraints - Kolumna obca - - - - TablePrimaryKeyAndUniquePanel - - - Columns - Kolumny - - - - Column - Kolumna - - - - Collation - Zestawienie - - - - Sort - Sortowanie - - - - Valid only for a single column with INTEGER data type - Dozwolone tylko dla jednej kolumny o typie danych INTEGER - - - - Autoincrement - Autoinkrementacja - - - - Named constraint - Nazwane ograniczenie - - - - Constraint name - Nazwa ograniczenia - - - - On conflict - W razie konfliktu - - - - Collate - table constraints - Zestawienie - - - - Sort order - table constraints - Kierunek sortowania - - - - Select at least one column. - Zaznacz przynajmniej jednÄ… kolumnÄ™. - - - - Enter a name of the constraint. - Wprowadź nazwÄ™ ograniczenia. - - - - TableStructureModel - - - Name - table structure columns - Nazwa - - - - Data type - table structure columns - Typ danych - - - - Primary -Key - table structure columns - Klucz -Główny - - - - Foreign -Key - table structure columns - Klucz -Obcy - - - - Unique - table structure columns - WartoÅ›ci -unikalne - - - - Check - table structure columns - Warunek - - - - Not -NULL - table structure columns - Niepuste - - - - Collate - table structure columns - Zestawienie - - - - Default value - table structure columns - DomyÅ›lna wartość - - - - TableWindow - - - Structure - Struktura - - - - Table name: - Nazwa tabeli: - - - - - Data - Dane - - - - Constraints - Ograniczenia - - - - Indexes - Indeksy - - - - Triggers - Wyzwalacze - - - - DDL - DDL - - - - Export table - table window - Eksportuj tabelÄ™ - - - - Import data to table - table window - Importuj do tabeli - - - - Populate table - table window - Zaludnij tabelÄ™ - - - - Refresh structure - table window - OdÅ›wież strukturÄ™ - - - - Commit structure changes - table window - Zatwierdź zmiany w strukturze - - - - Rollback structure changes - table window - Wycofaj zmiany w strukturze - - - - Add column - table window - Dodaj kolumnÄ™ - - - - Edit column - table window - Edytuj kolumnÄ™ - - - - - Delete column - table window - UsuÅ„ kolumnÄ™ - - - - Move column up - table window - PrzesuÅ„ kolumnÄ™ w górÄ™ - - - - Move column down - table window - PrzesuÅ„ kolumnÄ™ w dół - - - - Create similar table - table window - Utwórz podobnÄ… tabelÄ™ - - - - Reset autoincrement value - table window - Wyzeruj wartość autoinkrementacji - - - - Add table constraint - table window - Dodaj ograniczenie tabeli - - - - Edit table constraint - table window - Edytuj ograniczenie tabeli - - - - Delete table constraint - table window - UsuÅ„ ograniczenie tabeli - - - - Move table constraint up - table window - PrzesuÅ„ ograniczenie tabeli w górÄ™ - - - - Move table constraint down - table window - PrzesuÅ„ ograniczenie tabeli w dół - - - - Add table primary key - table window - Dodaj klucz główny tabeli - - - - Add table foreign key - table window - Dodaj klucz obcy tabeli - - - - Add table unique constraint - table window - Dodaj ograniczenie unikalnych wartoÅ›ci tabeli - - - - Add table check constraint - table window - Dodaj ograniczenie warunkiem tabeli - - - - Refresh index list - table window - OdÅ›wież listÄ™ indeksów - - - - Create index - table window - Utwórz indeks - - - - Edit index - table window - Edytuj indeks - - - - Delete index - table window - UsuÅ„ indeks - - - - Refresh trigger list - table window - OdÅ›wież listÄ™ wyzwalaczy - - - - Create trigger - table window - Utwórz wyzwalacz - - - - Edit trigger - table window - Edytuj wyzwalacz - - - - Delete trigger - table window - UsuÅ„ wyzwalacz - - - - Are you sure you want to delete column '%1'? - table window - Czy na pewno chcesz usunąć kolumnÄ™ '%1'? - - - - Following problems will take place while modifying the table. -Would you like to proceed? - table window - NastÄ™pujÄ…ce problemy wystÄ…piÄ… podczas modyfikacji tabeli. -Czy chcesz kontynuować? - - - - Table modification - table window - Modyfikacja tabeli - - - - Could not load data for table %1. Error details: %2 - Nie udaÅ‚o siÄ™ zaÅ‚adować danych dla tabeli %1. Szczegóły błędu: %2 - - - - Could not process the %1 table correctly. Unable to open a table window. - Nie udaÅ‚o siÄ™ przetworzyć poprawnie tabeli %1. Nie można otworzyć okna tabeli. - - - - Could not restore window %1, because no database or table was stored in session for this window. - Nie można przywrócić okna %1, ponieważ nie ma bazy danych lub tabeli zachowanej w sesji dla tego okna. - - - - Could not restore window '%1', because no database or table was stored in session for this window. - Nie można przywrócić okna %1, ponieważ nie ma bazy danych lub tabeli zachowanej w sesji dla tego okna. - - - - Could not restore window '%1', because database %2 could not be resolved. - Nie udaÅ‚o siÄ™ przywrócić okna '%1', ponieważ nie udaÅ‚o siÄ™ ustalić bazy danych %2. - - - - Could not restore window '%1'', because the table %2 doesn't exist in the database %3. - Nie można przywrócić okna '%1', ponieważ tabela %2 już nie jestnieje w bazie danych %3. - - - - Committed changes for table '%1' successfully. - PomyÅ›lnie zatwierdzono zmiany dla tabeli '%1'. - - - - Committed changes for table '%1' (named before '%2') successfully. - PomyÅ›lnie zatwierdzono zmiany dla tabeli '%1' (nazwanej wczeÅ›niej '%2'). - - - - Autoincrement value for table '%1' has been reset successfully. - Wartość automatycznej inkrementacji dla tabeli '%1' zostaÅ‚a zresetowana. - - - - Uncommitted changes - Niezatwierdzone zmiany - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - Zmiany w strukturze nie zostaÅ‚y zatwierdzone. Nie można przeglÄ…dać, ani edytować danych, dopóki struktura tabeli nie zostanie ustalona. -Czy chcesz zatwierdzić strukturÄ™, czy jednak chcesz wrócić do karty struktury? - - - - Table window "%1" has uncommitted structure modifications and data. - Okno tabeli "%1" ma niezatwierdzone modyfikacje struktury i danych. - - - - Table window "%1" has uncommitted data. - Okno tabeli "%1" ma niezatwierdzone dane. - - - - Table window "%1" has uncommitted structure modifications. - Okno tabeli "%1" ma niezatwierdzone modyfikacje struktury. - - - Could not restore window, because database %1 could not be resolved. - Nie można przywrócić okna, ponieważ nie znaleziono bazy danych %1. - - - Could not restore window, because the table %1 doesn't exist in the database %2. - Nie można przywrócić okna, ponieważ tabela %1 już nie jestnieje w bazie danych %2. - - - - - New table %1 - Nowa tabela %1 - - - Commited changes for table '%1' successfly. - PomyÅ›lnie zatwierdzono zmiany dla tabeli '%1'. - - - Commited changes for table '%1' (named before '%2') successfly. - PomyÅ›lnie zatwierdzono zmiany dla tabeli '%1' (nazwanej wczeÅ›niej '%2'). - - - - Could not commit table structure. Error message: %1 - table window - Nie udaÅ‚o siÄ™ zatwierdzić struktury tabeli. Treść błędu: %1 - - - - Reset autoincrement - Wyzeruj autoinkrementacjÄ™ - - - - Are you sure you want to reset autoincrement value for table '%1'? - Czy na pewno chcesz wyzerować wartość autoinkrementacji dla tabeli '%1'? - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - WystÄ…piÅ‚ błąd podczas próby wyzerowania wartoÅ›ci autoinkrementacji dla tabeli '%1': %2 - - - Autoincrement value for table '%1' has been reset successfly. - Wartość autoinkrementacji dla tabeli '%1' zostaÅ‚a pomyÅ›lnie wyzerowana. - - - - Empty name - Pusta nazwa - - - - A blank name for the table is allowed in SQLite, but it is not recommended. -Are you sure you want to create a table with blank name? - Pusta nazwa dla tabeli jest dozwolona w SQLite, ale nie jest zalecana. -Czy na pewno chcesz utworzyć tabelÄ™ o pustej nazwie? - - - - Cannot create a table without at least one column. - Nie można utworzyć tabeli bez przynajmniej jednej kolumny. - - - - Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. - Nie można utworzyć tabeli %1, jeÅ›li nie ma zdefiniowanego klucza głównego. Albo udznacz %2, albo zdefiniuj klucz główny. - - - - Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. - Nie można użyć autoinkrementacji dla klucza głównego, kiedy klauzula %1 jest użyta. Albo odnacz %2, albo autonkrementacjÄ™ w kluczu głównym. - - - - Are you sure you want to delete table constraint '%1'? - table window - Czy na pewno chcesz usunąć ograniczenie tabeli '%1'? - - - - Delete constraint - table window - UsuÅ„ ograniczenie - - - - Cannot export, because no export plugin is loaded. - Nie można wyeksportować, ponieważ żadna wtyczka eksportu nie zostaÅ‚a zaÅ‚adowana. - - - - Cannot import, because no import plugin is loaded. - Nie można zaimportować, ponieważ żadna wtyczka importu nie zostaÅ‚a zaÅ‚adowana. - - - Uncommited changes - Niezatwierdzone dane - - - There are uncommited structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - Zmiany w strukturze nie zostaÅ‚y zatwierdzone. Nie można przeglÄ…dać, ani edytować danych, dopóki struktura tabeli nie zostanie ustalona. -Czy chcesz zatwierdzić strukturÄ™, czy jednak chcesz wrócić do karty struktury? - - - - Go back to structure tab - Wróć do karty struktury - - - - Commit modifications and browse data. - Zatwierdź modyfikacje i przeglÄ…daj dane. - - - - Name - table window indexes - Nazwa - - - - Unique - table window indexes - WartoÅ›ci unikalne - - - - Columns - table window indexes - Kolumny - - - - Partial index condition - table window indexes - Warunek indeksu częściowego: - - - - Name - table window triggers - Nazwa - - - - Event - table window triggers - Zdarzenie - - - - Condition - table window triggers - Warunek - - - - Details - table window triggers - Szczegóły - - - Table window "%1" has uncommited structure modifications and data. - Okno tabeli "%1" ma niezatwierdzone modyfikacje struktury i danych. - - - Table window "%1" has uncommited data. - Okno tabeli "%1" ma niezatwierdzone dane. - - - Table window "%1" has uncommited structure modifications. - Okno tabeli "%1" ma niezatwierdzone modyfikacje struktury. - - - - TriggerColumnsDialog - - - Trigger columns - Kolumny wyzwalacza - - - - Triggering columns: - Kolumny wyzwalajÄ…ce: - - - - Select all - Zaznacz wszystko - - - - Deselect all - Odznacz wszystko - - - - TriggerDialog - - - - Trigger - Wyzwalacz - - - - On table: - Na tabeli: - - - - Action: - Akcja: - - - - - <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> - <p>Warunek SQL, który bÄ™dzie wykonany przed wÅ‚aÅ›ciwym kodem wyzwalacza. W przypadku gdy warunek zwróci faÅ‚sz, wyzwalacz nie zostanie uruchomiony dla tego wiersza.</p> - - - - Pre-condition: - Warunek wstÄ™pny: - - - - The scope is still not fully supported by the SQLite database. - Zakres wciąż nie jest w peÅ‚ni obsÅ‚ugiwany przez bazy danych SQLite. - - - - Trigger name: - Nazwa wyzwalacza: - - - - When: - Kiedy: - - - - List of columns for UPDATE OF action. - Lista kolumn dla akcji UPDATE OF. - - - - Scope: - Zakres: - - - - Code: - Kod: - - - - Trigger statements to be executed. - Zapytania wyzwalacz do wykonania. - - - - DDL - DDL - - - - On view: - Na widoku: - - - - Could not process trigger %1 correctly. Unable to open a trigger dialog. - Nie udaÅ‚o siÄ™ przetworzyć poprawnie wyzwalacza %1. Nie można otworzyć okna wyzwalacza. - - - - Enter a valid condition. - Wprowadź poprawny warunek. - - - - Enter a valid trigger code. - Wprowadź poprawny kod wyzwalacza. - - - - Error - trigger dialog - Błąd - - - - An error occurred while executing SQL statements: -%1 - WystÄ…piÅ‚ błąd podczas wykonywania zapytaÅ„ SQL: -%1 - - - - VersionConvertSummaryDialog - - - Database version convert - Konwersja bazy danych - - - - Following changes to the SQL statements will be made: - Dokonane bÄ™dÄ… nastÄ™pujÄ…ce zmiany w zapytaniach SQL: - - - - Before - Przed - - - - After - Po - - - - ViewWindow - - - Query - Zapytanie - - - - View name: - Nazwa widoku: - - - - Output column names - Nazwy kolumn wyjÅ›ciowych - - - - - Data - Dane - - - - Triggers - Wyzwalacze - - - - DDL - DDL - - - Could not restore window, because database %1 could not be resolved. - Nie można przywrócić okna, ponieważ nie znaleziono bazy danych %1. - - - Could not restore window, because database %1 could not be open. - Nie można przywrócić okna, ponieważ nie udaÅ‚o siÄ™ otworzyć bazy danych %1. - - - Could not restore window, because the view %1 doesn't exist in the database %2. - Nie można przywrócić okna, ponieważ widok %1 już nie jestnieje w bazie danych %2. - - - - - Could not restore window '%1', because no database or view was stored in session for this window. - Nie można przywrócić okna %1, ponieważ nie ma bazy danych lub tabeli zachowanej w sesji dla tego okna. - - - - Could not restore window '%1', because database %2 could not be resolved. - Nie udaÅ‚o siÄ™ przywrócić okna '%1', ponieważ nie udaÅ‚o siÄ™ ustalić bazy danych %2. - - - - Could not restore window '%1', because database %2 could not be open. - Nie można przywrócić okna '%1', ponieważ nie można byÅ‚o otworzyć bazy danych %2. - - - - Could not restore window '%1', because the view %2 doesn't exist in the database %3. - Nie można przywrócić okna '%1', ponieważ widok %2 już nie jestnieje w bazie danych %3. - - - - - New view %1 - Nowy widok %1 - - - - Refresh the view - view window - OdÅ›wież widok - - - - Commit the view changes - view window - Zatwierdź zmiany w widoku - - - - Rollback the view changes - view window - Wycofaj zmiany w widoku - - - - Explicit column names - Jawne nazwy kolumn - - - - Generate output column names automatically basing on result columns of the view. - Generuj automatycznie nazwy kolumn wyjÅ›ciowych bazujÄ…c na kolumnach wynikowych widoku. - - - - Add column - view window - Dodaj kolumnÄ™ - - - - Edit column - view window - Edytuj kolumnÄ™ - - - - Delete column - view window - UsuÅ„ kolumnÄ™ - - - - Move column up - view window - PrzesuÅ„ kolumnÄ™ w górÄ™ - - - - Move column down - view window - PrzesuÅ„ kolumnÄ™ w dół - - - - Refresh trigger list - view window - OdÅ›wież listÄ™ wyzwalaczy - - - - Create new trigger - view window - Utwórz nowy wyzwalacz - - - - Edit selected trigger - view window - Edytuj wybrany wyzwalacz - - - - Delete selected trigger - view window - UsuÅ„ wybrany wyzwalacz - - - - View window "%1" has uncommitted structure modifications and data. - Okno widoku "%1" ma niezatwierdzone modyfikacje struktury i danych. - - - - View window "%1" has uncommitted data. - Okno widoku "%1" ma niezatwierdzone dane. - - - - View window "%1" has uncommitted structure modifications. - Okno widoku "%1" ma niezatwierdzone modyfikacje struktury. - - - - Uncommitted changes - Niezatwierdzone zmiany - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - Zmiany w strukturze nie zostaÅ‚y zatwierdzone. Nie można przeglÄ…dać, ani edytować danych, dopóki struktura widoku nie zostanie ustalona. -Czy chcesz zatwierdzić strukturÄ™, czy jednak chcesz wrócić do karty struktury? - - - - Committed changes for view '%1' successfully. - PomyÅ›lnie zatwierdzono zmiany dla widoku '%1'. - - - - Committed changes for view '%1' (named before '%2') successfully. - PomyÅ›lnie zatwierdzono zmiany dla tabeli '%1' (nazwanej wczeÅ›niej '%2'). - - - View window "%1" has uncommited structure modifications and data. - Okno widoku "%1" ma niezatwierdzone modyfikacje struktury i danych. - - - View window "%1" has uncommited data. - Okno widoku "%1" ma niezatwierdzone dane. - - - View window "%1" has uncommited structure modifications. - Okno widoku "%1" ma niezatwierdzone modyfikacje struktury. - - - - Could not load data for view %1. Error details: %2 - Nie udaÅ‚o siÄ™ zaÅ‚adować danych dla widoku %1. Szczegóły błędu: %2 - - - Uncommited changes - Niezatwierdzone dane - - - There are uncommited structure modifications. You cannot browse or edit data until you have the view structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - Zmiany w strukturze nie zostaÅ‚y zatwierdzone. Nie można przeglÄ…dać, ani edytować danych, dopóki struktura widoku nie zostanie ustalona. -Czy chcesz zatwierdzić strukturÄ™, czy jednak chcesz wrócić do karty struktury? - - - - Go back to structure tab - Wróć do karty struktury - - - - Commit modifications and browse data. - Zatwierdź modyfikacje i przeglÄ…daj dane. - - - Commited changes for view '%1' successfly. - PomyÅ›lnie zatwierdzono zmiany dla widoku '%1'. - - - Commited changes for view '%1' (named before '%2') successfly. - PomyÅ›lnie zatwierdzono zmiany dla widoku '%1' (nazwanego wczeÅ›niej '%2'). - - - - Could not commit view changes. Error message: %1 - view window - Nie udaÅ‚o siÄ™ zatwierdzić widoku. Treść błędu: %1 - - - - Override columns - Nadpisz kolumny - - - - Currently defined columns will be overriden. Do you want to continue? - Aktualnie zdefiniowane kolumny zostanÄ… nadpisane. Czy chcesz kontynuować? - - - - Could not determinate columns returned from the view. The query is problably incomplete or contains errors. - Nie udaÅ‚o siÄ™ ustalić kolumn zwracanych z widoku. Zapytanie jest prawdopodobnie niekompletne lub zawiera błędy. - - - - Name - view window triggers - Nazwa - - - - Instead of - view window triggers - Zamiast - - - - Condition - view window triggers - Warunek - - - - Details - table window triggers - Szczegóły - - - - Could not process the %1 view correctly. Unable to open a view window. - Nie udaÅ‚o siÄ™ przetworzyć poprawnie widoku %1. Nie można otworzyć okna widoku. - - - - Empty name - Pusta nazwa - - - - A blank name for the view is allowed in SQLite, but it is not recommended. -Are you sure you want to create a view with blank name? - Pusta nazwa dla widoku jest dozwolona w SQLite, ale nie jest zalecana. -Czy na pewno chcesz utworzyć widok o pustej nazwie? - - - - The SELECT statement could not be parsed. Please correct the query and retry. -Details: %1 - Zapytanie SELECT nie mogÅ‚o być poprawnie przeanalizowane. ProszÄ™ poprawić zapytanie i spróbować ponownie. -Szczegóły: %1 - - - - The view could not be modified due to internal SQLiteStudio error. Please report this! - Widok nie mógÅ‚ być zmodyfikowany w zwiÄ…zku z wewnÄ™trznym błędem SQLiteStudio. ProszÄ™ to zgÅ‚osić! - - - - The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. - Kod widok nie mógÅ‚ być poprawnie przeanalizowany. To jest błąd SQLiteStudio ProszÄ™ to zgÅ‚osić! - - - - Following problems will take place while modifying the view. -Would you like to proceed? - view window - NastÄ™pujÄ…ce problemy wystÄ…piÄ… podczas modyfikacji widoku. -Czy chcesz kontynuować? - - - - View modification - view window - Modyfikacja widoku - - - - WidgetCover - - - Interrupt - Przerwij - - - diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl_PL.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl_PL.ts new file mode 100644 index 0000000..ab4476f --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pl_PL.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + O SQLiteStudio i licencje + + + + About + O programie + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Darmowy, otwartoźródÅ‚owy, wieloplatformowy menadżer baz danych SQLite.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Autor i aktywny opiekun:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licencje + + + + Environment + Åšrodowisko + + + + Icon directories + Katalogi ikon + + + + Form directories + Katalogi formularzy + + + + SQLite extension directories + Katalogi rozszerzeÅ„ SQLite + + + + Plugin directories + Katalogi wtyczek + + + + Configuration directory + Katalog konfiguracji + + + + Application directory + Katalog aplikacji + + + + Qt version: + Wersja Qt: + + + + SQLite 3 version: + Wersja SQLite 3: + + + + Portable distribution. + Dystrybucja przenoÅ›na. + + + + MacOS X application bundle distribution. + Dystrybucja aplikacji MacOS X. + + + + Operating system managed distribution. + Dystrybucja zarzÄ…dzana przez system operacyjny. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Zawartość:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Parametry zapytania + + + + Please provide values for query parameters + ProszÄ™ podać wartoÅ›ci dla parametrów zapytania + + + + CodeSnippetEditor + + + Filter snippets + Filtruj fragmenty + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Opcjonalny skrót, który bÄ™dzie dziaÅ‚ać tylko w kontekÅ›cie aktywnego okna asystenta kodu. Pozwala użytkownikowi na używanie kombinacji klawiszy, które w przeciwnym razie byÅ‚yby sprzeczne z innymi skrótami. Posiadanie okna asystenta kodu w wymaganym kontekÅ›cie sprawia, że wybór klawiszy jest bardziej elastyczny.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>Nazwa fragmentu bÄ™dzie wyÅ›wietlana w asystencie kodu. Aby uzyskać dostÄ™p do listy fragmentów użytkownik musi dwukrotnie uruchomić skrót asystenta kodu.</p></body></html> + + + + Snippet name + Nazwa fragmentu + + + + Code assistant shortcut + Skrót asystenta kodu + + + + Snippet code + Kod fragmentu + + + + Code Snippets editor window has uncommitted modifications. + Okno edytora fragmentu kodu ma niezatwierdzone modyfikacje. + + + + Code Snippets editor + Edytor fragmentów kodu + + + + Commit all snippet changes + Zatwierdź wszystkie zmiany fragmentów + + + + Rollback all snippet changes + Wycofaj zmiany we wszystkich fragmentach + + + + Create new snippet + Utwórz nowy fragment kodu + + + + Delete selected snippet + UsuÅ„ wybrany fragment kodu + + + + Move the snippet up + PrzesuÅ„ fragment w górÄ™ + + + + Move the snippet down + PrzesuÅ„ fragment w dół + + + + Code snippets manual + Instrukcja fragmentów kodu + + + + Enter a non-empty, unique name of the snippet. + Wprowadź niepustÄ…, unikalnÄ… nazwÄ™ fragmentu. + + + + Enter a non-empty snippet content. + Wprowadź niepustÄ… zawartość fragmentu. + + + + This hotkey is not unique in context of a code assistant. + Ten skrót klawiszowy nie jest unikalny w kontekÅ›cie asystenta kodu. + + + + CollationsEditor + + + Filter collations + Filtruj zestawienia + + + + Databases + Bazy danych + + + + Register in all databases + Zarejestruj we wszystkich bazach danych + + + + Register in following databases: + Zarejestruj w nastÄ™pujÄ…cych bazach danych: + + + + Implementation code: + Kod implementacji: + + + + Collation name: + Nazwa zestawienia: + + + + Implementation language: + JÄ™zyk implementacji: + + + + Collations editor + Edytor zestawieÅ„ + + + + Commit all collation changes + Zatwierdź wszystkie zmiany w zestawieniach + + + + Rollback all collation changes + Wycofaj wszystkie zmiany w zestawieniach + + + + Create new collation + Utwórz nowe zestawienie + + + + Delete selected collation + UsuÅ„ wybrane zestawienie + + + + Editing collations manual + PodrÄ™cznik edycji zestawieÅ„ + + + + Enter a non-empty, unique name of the collation. + Podaj niepustÄ…, unikalnÄ… nazwÄ™ zestawienia. + + + + Pick the implementation language. + Wybierz jÄ™zyk implementacji. + + + + Enter a non-empty implementation code. + Wprowadź niepusty kod implementacji. + + + + Collations editor window has uncommitted modifications. + Okno edytora zestawieÅ„ ma niezatwierdzone zmiany. + + + + ColorButton + + + Pick a color + Wybierz kolor + + + + ColumnCollatePanel + + + Collation name: + Nazwa zestawienia: + + + + Named constraint: + Ograniczenie nazwane: + + + + Enter a name of the constraint. + Wprowadź nazwÄ™ ograniczenia. + + + + Enter a collation name. + Wprowadź nazwÄ™ zestawienia. + + + + ColumnDefaultPanel + + + Default value: + DomyÅ›lna wartość: + + + + Named constraint: + Ograniczenie nazwane: + + + + Enter a default value expression. + Wprowadź wyrażenie wartoÅ›ci domyÅ›lnej. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Niepoprawne wyrażenie wartoÅ›ci domyÅ›lnej: %1. JeÅ›li chcesz użyć zwykÅ‚ego tekstu jako wartość, pamiÄ™taj o zamkniÄ™ciu go w znakach apostrofu. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Niepoprawne wyrażenie wartoÅ›ci domyÅ›lnej. JeÅ›li chcesz użyć zwykÅ‚ego tekstu jako wartość, pamiÄ™taj o zamkniÄ™ciu go w znakach apostrofu. + + + + Enter a name of the constraint. + Wprowadź nazwÄ™ ograniczenia. + + + + ColumnDialog + + + Column + Kolumna + + + + Name and type + Nazwa i typ + + + + Scale + Skala + + + + Precision + Precyzja + + + + Data type: + Typ danych: + + + + Column name: + Nazwa kolumny: + + + + Size: + Rozmiar: + + + + Constraints + Ograniczenia + + + + Generated value + Wygenerowana wartość + + + + Unique + WartoÅ›ci unikalne + + + + + + + + + + + Configure + Konfiguruj + + + + Foreign Key + Klucz obcy + + + + Collate + Zestawienie + + + + Not NULL + Niepuste + + + + Check condition + Sprawdzaj warunek + + + + Primary Key + Klucz główny + + + + Default + Wartość domyÅ›lna + + + + Advanced mode + Tryb zaawansowany + + + + Add constraint + column dialog + Dodaj ograniczenie + + + + Edit constraint + column dialog + Edytuj ograniczenie + + + + + Delete constraint + column dialog + UsuÅ„ ograniczenie + + + + Move constraint up + column dialog + PrzenieÅ› ograniczenie w górÄ™ + + + + Move constraint down + column dialog + PrzenieÅ› ograniczenie w dół + + + + Add a primary key + column dialog + Dodaj klucz główny + + + + Add a foreign key + column dialog + Dodaj klucz obcy + + + + Add an unique constraint + column dialog + Dodaj ograniczenie wartoÅ›ci unikalnych + + + + Add a check constraint + column dialog + Dodaj ograniczenie sprawdzania wartoÅ›ci + + + + Add a not null constraint + column dialog + Dodaj ograniczenie niepustych wartoÅ›ci + + + + Add a collate constraint + column dialog + Dodaj ograniczenie zestawienia + + + + Add a generated value constraint + column dialog + Dodaj ograniczenie wygenerowanej wartoÅ›ci + + + + Add a default constraint + column dialog + Dodaj ograniczenie wartoÅ›ci domyÅ›lnej + + + + Are you sure you want to delete constraint '%1'? + column dialog + Czy na pewno chcesz usunąć ograniczenie '%1'? + + + + Correct the constraint's configuration. + Popraw konfiguracjÄ™ ograniczenia. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Skala nie jest dozwolona dla kolumn INTEGER PRIMARY KEY. + + + + Precision cannot be defined without the scale. + Precyzja nie może być zdefiniowana bez skali. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Nie można użyć innego typu niż INTEGER, jeÅ›li opcja AUTOINCREMENT jest wybrana w PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + Typ INTEGER zostaÅ‚ wymuszony w zwiÄ…zku z wybranÄ… opcjÄ… AUTOINCREMENT w PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precyzja nie jest dozwolona dla kolumn INTEGER PRIMARY KEY. + + + + Could not match valid STRICT table datatype from declared type: %1. + Nie można dopasować prawidÅ‚owego typu danych dla tablicy STRICT wg zadeklarowanego typu: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Typ + + + + Name + column dialog constraints + Nazwa + + + + Details + column dialog constraints + Szczegóły + + + + ColumnForeignKeyPanel + + + Foreign table: + Tabela obca: + + + + Foreign column: + Kolumn obca: + + + + Reactions + Reakcje + + + + Deferred foreign key + Klucz obcy odroczony + + + + Named constraint + Nazwane ograniczenie + + + + Constraint name + Nazwa ograniczenia + + + + Pick the foreign table. + Wybierz tabelÄ™ obcÄ… + + + + Pick the foreign column. + Wybierz kolumnÄ™ obcÄ… + + + + Enter a name of the constraint. + Wprowadź nazwÄ™ ograniczenia. + + + + ColumnGeneratedPanel + + + Generating code: + Kod generujÄ…cy: + + + + Explicit type: + Sprecyzowany typ: + + + + Use "GENERATED ALWAYS" keywords + Użyj słów kluczowych "GENEROWANYCH ALWAYS" + + + + Named constraint: + Ograniczenie nazwane: + + + + Enter the column value generating expression. + Wprowadź wyrażenie generujÄ…ce wartość kolumny. + + + + Invalid value generating expression: %1. + NieprawidÅ‚owe wyrażenie generujÄ…ce wartość: %1. + + + + Invalid value generating expression. + NieprawidÅ‚owe wyrażenie generujÄ…ce wartość. + + + + Enter a name of the constraint. + Wprowadź nazwÄ™ ograniczenia. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoinkrementacja + + + + Sort order: + Kierunek sortowania: + + + + Named constraint: + Ograniczenie nazwane: + + + + On conflict: + W razie konfliktu: + + + + Enter a name of the constraint. + Wprowadź nazwÄ™ ograniczenia. + + + + Descending order is not allowed with AUTOINCREMENT. + Kolejność malejÄ…ca jest niedozwolona z AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Ograniczenie nazwane: + + + + On conflict: + W razie konfliktu: + + + + Enter a name of the constraint. + Wprowadź nazwÄ™ ograniczenia. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Kolumna: %1 + + + + Table: %1 + completer statusbar + Tabela: %1 + + + + Index: %1 + completer statusbar + Indeks: %1 + + + + Trigger: %1 + completer statusbar + Wyzwalacz: %1 + + + + View: %1 + completer statusbar + Widok: %1 + + + + Database: %1 + completer statusbar + Baza danych: %1 + + + + Keyword: %1 + completer statusbar + SÅ‚owo kluczowe: %1 + + + + Function: %1 + completer statusbar + Funkcja: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + Tekst + + + + Number + completer statusbar + Liczba + + + + Binary data + completer statusbar + Dane binarne + + + + Collation: %1 + completer statusbar + Zestawienie: %1 + + + + Pragma function: %1 + completer statusbar + Funkcja pragma: %1 + + + + Insert a code snippet + Wstaw fragment kodu + + + + ConfigDialog + + + + Configuration + Konfiguracja + + + + Search + Szukaj + + + + General + Ogólne + + + + Keyboard shortcuts + Skróty klawiszowe + + + + Look & feel + WyglÄ…d i zachowanie + + + + Style + Styl + + + + Fonts + Czcionki + + + + Code colors + Kolory kodu + + + + + Database list + Lista baz + + + + Code assistant + Asystent kodu + + + + Data browsing + PrzeglÄ…danie danych + + + + Data editors + Edytory danych + + + + Plugins + Wtyczki + + + + Code formatters + Formatery kodu + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + Gdy wyłączone, to kolumny bÄ™dÄ… uÅ‚ożone w takiej kolejnoÅ›ci, w jakiej wystÄ…piÅ‚y w zapytaniu CREATE TABLE. + + + + Sort table columns alphabetically + Sortuj kolumny tabel alfabetycznie. + + + + Expand tables node when connected to a database + RozwiÅ„ listÄ™ tabel po połączeniu z bazÄ… danych + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Dodatkowe etykiety, to te wyÅ›wietlane obok nazw na liÅ›cie baz danych (sÄ… niebieskie, chyba że skonfigurowano je inaczej). Włączenie tej opcji spowoduje wyÅ›wietlenie etykiet dla baz danych, niepoprawnych baz danych, oraz dla wÄ™złów agregujÄ…cych (grupa kolumn, grupa indeksów, grupa wyzwalaczy). WiÄ™cej etykiet jest dostÄ™pne niżej.</p> + + + + Display additional labels on the list + WyÅ›wietlaj dodatkowe etykiety na liÅ›cie + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + Dla zwykÅ‚ych tabel etykiety bÄ™dÄ… pokazywać liczbÄ™ kolumn, inseksów, oraz wyzwalaczy dla tych tabel. + + + + Display labels for regular tables + WyÅ›wietlaj etykiety dla zwykÅ‚ych tabel + + + + Virtual tables will be marked with a 'virtual' label. + Tabele wirtualne bÄ™dÄ… oznaczone etykietÄ… 'wirtualna'. + + + + Display labels for virtual tables + WyÅ›wietlaj etykiety dla tabel wirtualnych + + + + Expand views node when connected to a database + RozwiÅ„ listÄ™ widoków po połączeniu z bazÄ…. + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + Gdy ta opcja jest wyłączona, to wszystkie obiekty bÄ™dÄ… uÅ‚ożone w takiej kolejnoÅ›ci, w jakiej wystÄ™pujÄ… w tabeli sqlite_master (czyli w takiej, w jakiej zostaÅ‚y stworzone) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sortuj obiekty (tabele, indeksy, wyzwalacze i widoki) alfabetycznie + + + + Display system tables and indexes on the list + WyÅ›wietlaj tabele i indeksy systemowe na liÅ›cie + + + + Database dialog window + Okno dialogowe bazy danych + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>Podczas dodawania nowej bazy danych jest ona domyÅ›lnie zaznaczana jako "trwaÅ‚a" (zapisywana w konfiguracji). Włączenie tej opcji powoduje, że każda nowa baza danych NIE bÄ™dzie zaznaczana domyÅ›lnie jako "trwaÅ‚a".</p> + + + + Do not mark database to be "permanent" by default + Nie zaznaczaj bazy danych domyÅ›lnie jako "trwaÅ‚a" + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>Gdy ta opcja jest włączona, to pliki upuszczone z menadżera plików na listÄ™ baz danych bÄ™dÄ… automatycznie dodawane do list, pomijajÄ…c standardowe okno bazy danych. JeÅ›li z różnych powodów automatyczny proces siÄ™ nie powiedzie, to użytkownikowi ukaże siÄ™ standardowe okno.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Próbuj caÅ‚kowicie pomijać dialog podczas upuszczania pliku bazy na listÄ™ + + + + Data browsing and editing + PrzeglÄ…danie i edycja danych + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maksymalna liczba konfiguracji w oknie dialogowym zaludniania tabeli, która ma być trzymana w konfiguracji. Wartość 100 powinna być wystarczajÄ…ca.</p> + + + + Number of memorized table populating configurations + Liczba zapamiÄ™tanych konfiguracji zaludniania tabeli + + + + Data column width + Szerokość kolumny danych + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>Kiedy użytkownik wprowadzi nowÄ… wartość do kolumny, a wartość jest wiÄ™ksza niż obecna szerokość kolumny, aplikacja powiÄ™kszy kolumnÄ™ w celu dostosowania jej do nowej wartoÅ›ci, ale nie wiÄ™cej niż limit okreÅ›lony w opcji powyżej.</p></body></html> + + + + Enlarge column when entering value longer than current width + PowiÄ™ksz kolumnÄ™ podczas wprowadzania wartoÅ›ci dÅ‚uższej niż obecna szerokość + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>Kiedy dane sÄ… wczytane do widoku siatki, szerokość kolumn jest automatycznie dostosowywana. Ta wartość ogranicza poczÄ…tkowÄ… szerokość tego dostosowywania, ale użytkownik nadal może rozszerzać kolumnÄ™ rÄ™cznie poza ten limit.</p> + + + + Number of data rows per page: + Liczba wierszy danych na stronie: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>Gdy to jest włączone i użytkownik zatrzyma kursor myszy nad komórkÄ… w widoku siatki danych (wyniki zapytania, dane tabeli, dane widoku), to pojawi siÄ™ podpowiedź ze szczegółami odnoÅ›nie komórki - zawiera ona szczegóły , jak typ danych kolumny, ograniczenia, ROWID i inne.</p> + + + + Show column and row details tooltip in data view + Pokazuj podpowiedź ze szczegółami o kolumnie i wierszu w widoku siatki danych + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>Kiedy edytowana jest komórka, która miaÅ‚a wartość NULL, a nowa wartość wprowadzona jest pusta, to ta opcja decyduje o tym, czy wartość powinna pozostać NULL (gdy ta opcja jest włączona), czy powinna być nadpisana pustym Å‚aÅ„cuchem znaków (gdy ta opcja wyłączona).</p> + + + + Keep NULL value when entering empty value + Zachowaj wartość NULL gdy wstawiania jest pusta wartość + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Włącz to, aby wymusić wartość DEFAULT podczas zatwierdzania wartoÅ›ci NULL dla kolumn, które majÄ… zdefiniowanÄ… wartość DEFAULT, nawet jeÅ›li kolumna dopuszcza wartoÅ›ci NULL.</p><p>Wyłącz tÄ… opcjÄ™ aby używać wartoÅ›ci DEFAULT tylko i wyłącznie, gdy wartość NULL jest zatwierdzana dla kolumny z ograniczeniem NOT NULL.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Używaj wartoÅ›ci DEFAULT (jeÅ›li zdefiniowana), gdy zatwierdzana jest wartość NULL + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>JeÅ›li wyniki zapytania zawierajÄ… dziesiÄ…tki (lub setki) kolumn, bardziej prawdopodobne jest, że wyczerpie pamięć komputera Å‚adujÄ…c kilka gigabajtów danych jednoczeÅ›nie. SQLiteStudio może próbować ograniczyć liczbÄ™ wyników wyÅ›wietlanych na jednej stronie w celu ochrony komputera. JeÅ›li wiesz, że nie pracujesz z dużymi wartoÅ›ciami w bazie danych, możesz wyłączyć ten limit i zawsze zobaczysz tyle wierszy, ile jest zdefiniowanych na stronie.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Ogranicz liczbÄ™ wierszy w przypadku dziesiÄ…tek kolumn + + + + Inserting new row in data grid + Wstawianie nowego wiersza w widoku siatki danych. + + + + Before currently selected row + Przed aktualnie wybranym wierszem + + + + After currently selected row + Po aktualnie wybranym wierszu. + + + + At the end of data view + Na koÅ„cu widoku siatki danych + + + + Table windows + Okna tabel + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>Gdy włączone, Okna Tabeli bÄ™dÄ… siÄ™ otwierać na zakÅ‚adce danych, zamiast na zakÅ‚adce struktury.</p> + + + + Open Table Windows with the data tab for start + Otwieraj Okna Tabeli z zakÅ‚adkÄ… danych na poczÄ…tek + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>Gdy włączone, to zakÅ‚adka "Dane" bÄ™dzie umieszczona jako pierwsza w każdym Oknie Tabeli, zamiast jako druga.</p> + + + + Place data tab as first tab in a Table Window + Ustaw zakÅ‚adkÄ™ danych jako pierwszÄ… w Oknie Tabeli + + + + View windows + Okna Widoków + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>Gdy włączone, Okna Widoku bÄ™dÄ… siÄ™ otwierać na zakÅ‚adce danych, zamiast na zakÅ‚adce struktury.</p> + + + + Open View Windows with the data tab for start + Otwieraj Okna Widoku z zakÅ‚adkÄ… danych na poczÄ…tek + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>Gdy włączone, to zakÅ‚adka "Dane" bÄ™dzie umieszczona jako pierwsza w każdym Oknie Widoku, zamiast jako druga.</p> + + + + Place data tab as first tab in a View Window + Ustaw zakÅ‚adkÄ™ danych jako pierwszÄ… w Oknie Widoku + + + + Data types + Type danych + + + + Available editors: + DostÄ™pne edytory: + + + + Editors selected for this data type: + Edytory wybrane dla tego typu danych: + + + + Schema editing + Edycja schematu + + + + Number of DDL changes kept in history. + Liczba zmian DDL trzymanych w historii. + + + + DDL history size: + Rozmiar historii DDL: + + + + Don't show DDL preview dialog when committing schema changes + Nie pokazuj okna podglÄ…du DDL podczas zatwierdzania zmian struktury + + + + SQL queries + Zapytania SQL + + + + + Number of queries kept in the history. + Liczba zapytaÅ„ trzymana w historii. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>JeÅ›li w oknie edytora SQL jest wiÄ™cej niż jedno zapytanie, wtedy (jeÅ›li ta opcja jest włączona) zostanie wykonana tylko jedno zapytanie - to pod kursorem wstawiania klawiatury. W przeciwnym razie wszystkie zapytania zostanÄ… wykonane. Zawsze możesz ograniczyć zapytania do wykonania poprzez zaznaczenie tych zapytaÅ„ przed ich wykonaniem. Możesz również użyć dedykowanych skrótów do wykonywania w jednym lub drugim trybie (obecnie skonfigurowanych pod %1 dla wykonania pojedynczego zapytania i %2 dla wykonania wszystkich zapytaÅ„).</p></body></html> + + + + History size: + Rozmiar historii: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maksymalna liczba parametrów zapytania (:param, @param, $param, ?) trzymanych w historii. Kiedy ponownie użyjesz parametru o tej samej nazwie/pozycji, SQLiteStudio wstÄ™pnie uzupeÅ‚ni go używajÄ…c ostatniej zapamiÄ™tanej wartoÅ›ci (nadal bÄ™dzie można jÄ… zmienić). Wartość 100 powinna być wystarczajÄ…ca.</p> + + + + Execute only the query under the cursor + Wykonuj tylko zapytania bÄ™dÄ…ce pod kursorem + + + + Number of memorized query parameters + Liczba zapamiÄ™tanych parametrów zapytania + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>DomyÅ›lnie (gdy ta opcja jest wyłączona) rzeczywista liczba jest wyÅ›wietlana w formacie dziesiÄ™tnym z przecinkiem. W niektórych przypadkach, gdy liczba jest naprawdÄ™ maÅ‚a (kilka miejsc po przecinku), domyÅ›lna reprezentacja może wydawać siÄ™ niedokÅ‚adna. W takim przypadku możesz włączyć tÄ™ opcjÄ™ do użycia notacji naukowej (np. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Użyj notacji naukowej dla liczb rzeczywistych w widoku siatki + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>Gdy dane sÄ… odczytywane w widoku tabeli szerokość kolumn jest automatycznie dostosowywana. Ta wartość ogranicza wstÄ™pnie dostosowanÄ… szerokość, ale użytkownik może zmieniać rozmiar kolumny rÄ™cznie powyżej tego limitu. Ta wartość jest również używana podczas powiÄ™kszania kolumny w przypadku nowej, dÅ‚uższej wartoÅ›ci wprowadzonej przez użytkownika (patrz opcja poniżej).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Ogranicz automatycznÄ… szerokość kolumny danych do (w pikselach): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>PoczÄ…tkowa szerokość kolumn danych zostanie ustawiona co najmniej tak, aby pokazać peÅ‚ne nazwy kolumn w nagłówku. To może być nadpisane przez wstÄ™pny limit szerokoÅ›ci kolumny okreÅ›lony w pikselach (ustawienie powyżej).</p></body></html> + + + + Keep at least the width to show complete column name + Zachowaj co najmniej szerokość wystarczajÄ…cÄ…, aby wyÅ›wietlić peÅ‚nÄ… nazwÄ™ kolumny + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>JeÅ›li włączone, linie dÅ‚uższe niż szerokość edytora bÄ™dÄ… zawijane, wiÄ™c przewijanie poziome nie bÄ™dzie potrzebne.</p></body></html> + + + + Wrap lines in SQL editor + Zawijaj linie w edytorze SQL + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>PodÅ›wietla caÅ‚e zapytanie, które jest obecnie pod kursorem wstawiania. To jest to samo zapytanie, które zostanie wykonane po naciÅ›niÄ™ciu klawisza skrótu lub przycisku &quot;Wykonaj zapytanie&quot; (chyba że skonfigurowano inaczej).</p></body></html> + + + + Highlight current query + PodÅ›wietl bieżące zapytanie + + + + Updates + Aktualizacje + + + + Automatically check for updates at startup + Sprawdzaj aktualizacje automatycznie przy starcie + + + + Session + Sesje + + + + Restore last session (active MDI windows) after startup + Przywróć ostatniÄ… sesjÄ™ (aktywne okna MDI) po starcie + + + + Allow multiple instances of the application at the same time + Zezwalaj na wiele instancji aplikacji w tym samym czasie + + + + Status Field + Pole Statusu + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>Kiedy użytkownik rÄ™cznie zamyka panel Statusu, ta opcja zapewnia, że zostanie ono otwarte ponownie, gdy jest wyÅ›wietlona nowa wiadomość. JeÅ›li jest ona wyłączona, to panel Statusu może być otwarte tylko rÄ™cznie z menu "Widok".</p> + + + + Always open Status panel when new message is printed + Zawsze otwieraj panel Statusu, gdy wyÅ›wietlona jest nowa wiadomość + + + + Code syntax colors + Kolory skÅ‚adni kodu + + + + Keyword foreground + SÅ‚owo kluczowe + + + + Regular foreground + Standardowy tekst + + + + String foreground + ÅaÅ„cuch znaków + + + + Comment foreground + Komentarz + + + + Valid objects foreground + PrawidÅ‚owe obiekty + + + + Current query background + Bieżące tÅ‚o zapytania + + + + Bind parameter foreground + Parametr wiążący + + + + Current line background + TÅ‚o bieżącej linii + + + + Matched parenthesis background + TÅ‚o dopasowanych nawiasów + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>Możesz caÅ‚kowicie wyłączyć podÅ›wietlanie bieżącego zapytania na stronie ustawieÅ„ ogólnych.</p></body></html> + + + + Number foreground + Liczba + + + + BLOB value foreground + Kolor zawartoÅ›ci BLOB + + + + Matched parenthesis foreground + Dopasowane nawiasy + + + + Reset to defaults + Przywróć ustawienia domyÅ›lne + + + + Filter shortcuts by name or key combination + Filtruj skróty po nazwie, lub kombinacji klawiszy + + + + Action + Akcja + + + + Key combination + Kombinacja klawiszy + + + + + Language + JÄ™zyk + + + + Changing language requires application restart to take effect. + Zmiana jÄ™zyka wymaga restartu aplikacji, aby zadziaÅ‚ać. + + + + Compact layout + UkÅ‚ad kompaktowy + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>UkÅ‚ad kompaktowy zmniejsza wszystkie marginesy i odstÄ™py na interfejsie do minimum, robiÄ…c wiÄ™cej miejsca na wyÅ›wietlanie danych. Powoduje to, że interfejs jest nieco mniej estetyczny, ale pozwala to na prezentacjÄ™ wiÄ™kszej iloÅ›ci danych naraz.</p> + + + + Use compact layout + Użyj ukÅ‚adu kompaktowego + + + + Main window dock areas + Strefy dokowania głównego okna + + + + Left and right areas occupy corners + Lewa i prawa strefa zajmujÄ… rogi + + + + Top and bottom areas occupy corners + Górna i dolna strefa zajmujÄ… rogi + + + + Hide built-in plugins + Ukryj wtyczki wbudowane + + + + Current style: + Aktualny styl: + + + + Preview + PodglÄ…d + + + + Enabled + Włączone + + + + Disabled + Wyłączone + + + + Active formatter plugin + Aktywna wtyczka formatera + + + + SQL editor font + Czcionka edytora SQL + + + + Database list font + Czcionka listy baz danych + + + + Database list additional label font + Czcionka dodatkowych etykiety listy baz danych + + + + Data view font + Czcionka widoku danych + + + + Status field font + Czcionka pola statusu + + + + Code assistant settings + Ustawienia asystenta kodu + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>JeÅ›li ta opcja jest włączona, asystent kodu zostanie uruchomiony w przypadkach, gdy użytkownik wpisze na przykÅ‚ad <span style=" font-weight:700;">nazwaTabeli.</span>, aby zaproponować nazwy kolumn tabeli. JeÅ›li opcja jest wyłączona, użytkownik bÄ™dzie musiaÅ‚ użyć skrót klawiszowy asystenta.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatycznie wyzwalaj asystenta po wpisaniu kropki po nazwie obiektu + + + + Description: + plugin details + Opis: + + + + Category: + plugin details + Kategoria: + + + + Version: + plugin details + Wersja: + + + + Author: + plugin details + Autor: + + + + Internal name: + plugin details + Nazwa wewnÄ™trzna: + + + + Dependencies: + plugin details + ZależnoÅ›ci: + + + + Conflicts: + plugin details + Konflikty: + + + + Plugin details + Szczegóły wtyczki + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Wtyczki sÄ… Å‚adowane/wyÅ‚adowywane natychmiast po zaznaczeniu/odznaczeniu, ale zmodyfikowana lista wtyczek, które należy zaÅ‚adować przy starcie nie jest zapisana, dopóki nie zatwierdzisz caÅ‚ego okna configuracji. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (wbudowany) + + + + Details + Szczegóły + + + + No plugins in this category. + Brak wtyczek w tej kategorii. + + + + Add new data type + Dodaj nowy typ danych + + + + Rename selected data type + ZmieÅ„ nazwÄ™ wybranego typu danych + + + + Delete selected data type + UsuÅ„ wybrany typ danych + + + + Help for configuring data type editors + Pomoc w konfiguracji edytorów typów danych + + + + Clear hotkey for this action + Wyczyść klawisz skrótu dla tej akcji + + + + Restore original hotkey for this action + Przywróć oryginalny skrót klawiszowy dla tej akcji + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Tutaj możesz skonfigurować kolory dla podÅ›wietlenia skÅ‚adni kodu. SÄ… one współdzielone w różnych jÄ™zykach - nie tylko dla SQL, ale również dla JavaScript i innych. DomyÅ›lnie używany jest kolor zależny od motywu. Aby zdefiniować swój wÅ‚asny kolor, włącz kolor niestandardowy, zaznaczajÄ…c pole wyboru obok konkretnego koloru. + + + + ConstraintCheckPanel + + + The condition + Warunek + + + + Named constraint: + Ograniczenie nazwane: + + + + On conflict + W razie konfliktu + + + + Enter a valid condition. + Wprowadź poprawny warunek. + + + + Enter a name of the constraint. + Wprowadź nazwÄ™ ograniczenia. + + + + ConstraintDialog + + + New constraint + constraint dialog + Nowe ograniczenie + + + + Create + constraint dialog + Utwórz + + + + Edit constraint + dialog window + Edytuj ograniczenie + + + + Apply + constraint dialog + Zastosuj + + + + Primary key + table constraints + Klucz główny + + + + Foreign key + table constraints + Klucz obcy + + + + Unique + table constraints + WartoÅ›ci unikalne + + + + Not NULL + table constraints + Niepuste + + + + Check + table constraints + Warunek + + + + Generated + table constraints + Generowane + + + + Collate + table constraints + Zestawienie + + + + Default + table constraints + Wartość domyÅ›lna + + + + ConstraintTabModel + + + Table + table constraints + Tabela + + + + Column (%1) + table constraints + Kolumna (%1) + + + + Scope + table constraints + Zakres + + + + Type + table constraints + Typ + + + + Details + table constraints + Szczegóły + + + + Name + table constraints + Nazwa + + + + CssDebugDialog + + + SQLiteStudio CSS console + Konsola CSS SQLiteStudio + + + + DataView + + + Filter data + data view + Filtruj dane + + + + Grid view + Widok siatki + + + + Form view + Widok formularza + + + + Refresh table data + data view + OdÅ›wież dane tabeli + + + + First page + data view + Pierwsza strona + + + + Previous page + data view + Poprzednia strona + + + + Next page + data view + NastÄ™pna strona + + + + Last page + data view + Ostatnia strona + + + + Commit changes for selected cells + data view + Zatwierdź zmiany dla wybranych komórek + + + + Rollback changes for selected cells + data view + Wycofaj zmiany dla wybranych komórek + + + + Show grid view of results + data view + Pokaż widok siatki dla wyników + + + + Show form view of results + data view + Pokaż widok formularza dla wyników + + + + Filter by text (if contains) + data view + Filtruj wedÅ‚ug tekstu (jeÅ›li zawiera) + + + + Filter strictly by text (if equals) + data view + Filtruj Å›ciÅ›le wedÅ‚ug tekstu (jeÅ›li jest równy) + + + + Tabs on top + data view + Karty na górze + + + + Tabs at bottom + data view + Karty na dole + + + + Place new rows above selected row + data view + Wstawiaj nowe wiersze nad aktualnie wybranym wierszem + + + + Place new rows below selected row + data view + Wstawiaj nowe wiersze pod aktualnie wybranym wierszem + + + + Place new rows at the end of the data view + data view + Wstawiaj nowe wiersze na koÅ„cu widoku siatki danych + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + CaÅ‚kowita liczba wierszy jest liczona. +PrzeglÄ…danie pozostaÅ‚ych stron bÄ™dzie możliwe kiedy liczenie wierszy zostanie zakoÅ„czone. + + + + Row: %1 + Wiersz: %1 + + + + Filter + Filtruj + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + WciÅ›nij Enter lub naciÅ›nij przycisk "Zastosuj filtr", aby zastosować nowÄ… wartość. + + + + Filter by the Regular Expression + data view + Filtruj używajÄ…c WyrażeÅ„ Regularnych + + + + Filter by SQL expression + data view + Filtruj używajÄ…c wyrażenia SQL + + + + Show filter inputs per column + data view + Pokaż filtr dla każdej kolumny + + + + Apply filter + data view + Zastosuj filtr + + + + DbDialog + + + Database + Baza danych + + + + Database type + Typ bazy danych + + + + Database driver + Sterownik bazy danych + + + + + File + Plik + + + + Name (on the list) + Nazwa (na liÅ›cie) + + + + Options + Opcje + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Włącz to, jeÅ›li chcesz aby baza danych byÅ‚a przechowywana w pliku konfiguracji i przywracana za każdym razem, gdy startuje SQLiteStudio.</p> + + + + Permanent (keep it in configuration) + TrwaÅ‚a (trzymaj w konfiguracji) + + + + Test connection + Testuj połączenie + + + + Select new or existing file on local computer + Wybierz nowy lub istniejÄ…cy plik na komputerze lokalnym + + + + Browse + PrzeglÄ…daj + + + + Database type not selected. + Typ bazy danych nie zostaÅ‚ wybrany. + + + + Database path not specified. + Åšcieżka bazy danych nie zostaÅ‚a okreÅ›lona. + + + + Enter an unique database name. + Wprowadź unikalnÄ… nazwÄ™ bazy danych. + + + + This name is already in use. Please enter unique name. + Ta nazwa jest już w użyciu. ProszÄ™ wprowadzić unikalnÄ… nazwÄ™. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatyczne generowanie nazwy zostaÅ‚o wyłączone, ponieważ nazwa byÅ‚a edytowana rÄ™cznie. Aby przywrócić automatyczne generowanie, proszÄ™ wyczyÅ›cić pole nazwy.</p> + + + + Enter a database file path. + Wprowadź Å›cieżkÄ™ do pliku bazy danych. + + + + This database is already on the list under name: %1 + Ta baza jest już na liÅ›cie pod nazwÄ…: %1 + + + + Select a database type. + Wybierz typ bazy danych. + + + + DbObjectDialogs + + + Delete table + UsuÅ„ tabelÄ™ + + + + Are you sure you want to delete table %1? + Czy na pewno chcesz usunąć tabelÄ™ %1? + + + + Delete index + UsuÅ„ indeks + + + + Are you sure you want to delete index %1? + Czy na pewno chcesz usunąć indeks %1? + + + + Delete trigger + UsuÅ„ wyzwalacz + + + + Are you sure you want to delete trigger %1? + Czy na pewno chcesz usunąć wyzwalacz %1? + + + + Delete view + UsuÅ„ widok + + + + Are you sure you want to delete view %1? + Czy na pewno chcesz usunąć widok %1? + + + + + Error while dropping %1: %2 + Błąd podczas porzucania %1: %2 + + + + Delete objects + UsuÅ„ obiekty + + + + Are you sure you want to delete following objects: +%1 + Czy na pewno chcesz usunąć nastÄ™pujÄ…ce obiekty: +%1 + + + + Cannot start transaction. Details: %1 + Nie można wystartować transakcji. Szczegóły: %1 + + + + Cannot commit transaction. Details: %1 + Nie można zatwierdzić transakcji. Szczegóły: %1 + + + + DbTree + + + Databases + Bazy danych + + + + Filter by name + Filtruj po nazwie + + + + Copy + Kopiuj + + + + Paste + Wklej + + + + Select all + Zaznacz wszystko + + + + Create a group + Utwórz grupÄ™ + + + + Delete the group + UsuÅ„ grupÄ™ + + + + Rename the group + ZmieÅ„ nazwÄ™ grupy + + + + &Add a database + Dod&aj bazÄ™ danych + + + + &Edit the database + &Edytuj bazÄ™ danych + + + + &Remove the database + U&suÅ„ bazÄ™ danych + + + + &Connect to the database + &Połącz z bazÄ… danych + + + + &Disconnect from the database + &Rozłącz siÄ™ z bazÄ… danych + + + + Import + Importuj + + + + &Export the database + &Eksportuj bazÄ™ danych + + + + Vac&uum + Odk&urz + + + + &Integrity check + Sprawdź spó&jność + + + + Create a &table + Utwórz &tabelÄ™ + + + + Edit the t&able + Edytuj t&abelÄ™ + + + + Delete the ta&ble + UsuÅ„ ta&belÄ™ + + + + Export the table + Eksportuj tabelÄ™ + + + + Import into the table + Importuj do tabeli + + + + Populate table + Zaludnij tabelÄ™ + + + + Create similar table + Utwórz podobnÄ… tabelÄ™ + + + + Reset autoincrement sequence + Wyzeruj sekwencjÄ™ autoinkrementacji + + + + Create an &index + Utwórz &indeks + + + + Edit the i&ndex + Edytuj i&ndeks + + + + Delete the in&dex + UsuÅ„ in&deks + + + + Create a trig&ger + Utwórz wyz&walacz + + + + Edit the trigg&er + Edytuj wyzw&alacz + + + + Delete the trigge&r + UsuÅ„ wyzwa&lacz + + + + Create a &view + Utwórz &widok + + + + Edit the v&iew + Edytuj w&idok + + + + Delete the vi&ew + UsuÅ„ wi&dok + + + + Add a column + Dodaj kolumnÄ™ + + + + Edit the column + Edytuj kolumnÄ™ + + + + Delete the column + UsuÅ„ kolumnÄ™ + + + + Delete selected items + UsuÅ„ wybrane elementy + + + + Clear filter + Wyczyść filtr + + + + &Refresh all database schemas + &OdÅ›wież schematy wszystkich baz danych + + + + Re&fresh selected database schema + OdÅ›wież schemat wy&branej bazy danych + + + + + Erase table data + Wymaż dane tabeli + + + + Open file's directory + Otwórz katalog pliku + + + + Execute SQL from file + Wykonaj SQL z pliku + + + + Increase font size + database list + ZwiÄ™ksz rozmiar czcionki + + + + Decrease font size + database list + Zmniejsz rozmiar czcionki + + + + + Database + Baza danych + + + + Grouping + Grupowanie + + + + Generate query for table + Generuj zapytanie dla tabeli + + + + + Create group + Utwórz grupÄ™ + + + + Group name + Nazwa grupy + + + + Entry with name %1 already exists in group %2. + Pozycja o nazwie %1 istnieje już w grupie %2. + + + + Delete group + UsuÅ„ grupÄ™ + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Czy na pewno chcesz usunąć grupÄ™ %1? +Wszystkie obiekty z tej grupy zostanÄ… przeniesione do nadrzÄ™dnej grupy. + + + + Are you sure you want to remove database '%1' from the list? + Czy napewno chcesz wycofać bazÄ™ '%1' z listy? + + + + Are you sure you want to remove following databases from the list: +%1 + Czy na pewno chcesz wycofać nastÄ™pujÄ…ce bazy z listy: +%1 + + + + Remove database + Wycofaj bazÄ™ + + + + + Cannot import, because no import plugin is loaded. + Nie można zaimportować, ponieważ żadna wtyczka importu nie zostaÅ‚a zaÅ‚adowana. + + + + + Cannot export, because no export plugin is loaded. + Nie można wyeksportować, ponieważ żadna wtyczka eksportu nie zostaÅ‚a zaÅ‚adowana. + + + + Vacuum (%1) + Odkurz (%1) + + + + Integrity check (%1) + Sprawdzanie spójnoÅ›ci (%1) + + + + Reset autoincrement + Wyzeruj autoinkrementacjÄ™ + + + + Are you sure you want to reset autoincrement value for table '%1'? + Czy na pewno chcesz wyzerować wartość autoinkrementacji dla tabeli '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + WystÄ…piÅ‚ błąd podczas próby wyzerowania wartoÅ›ci autoinkrementacji dla tabeli '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Wartość automatycznej inkrementacji dla tabeli '%1' zostaÅ‚a zresetowana. + + + + Are you sure you want to delete all data from table(s): %1? + Czy na pewno chcesz usunąć wszystkie dane z tabel(i): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + WystÄ…piÅ‚ błąd podczas próby usuniÄ™cia danych z tabeli '%1': %2 + + + + All data has been deleted for table '%1'. + Wszystkie dane z tabeli '%1' zostaÅ‚y usuniÄ™te. + + + + Following objects will be deleted: %1. + NastÄ™pujÄ…ce obiekty zostanÄ… usuniÄ™te: %1 + + + + Following databases will be removed from list: %1. + NastÄ™pujÄ…ce bazy danych zostanÄ… usuniÄ™te z listy: %1 + + + + Remainig objects from deleted group will be moved in place where the group used to be. + PozostaÅ‚e obiekty z usuniÄ™tej grupy bÄ™dÄ… przeniesione w miejsce, gdzie dotychczas byÅ‚a ta grupa. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Czy na pewno chcesz kontynuować? + + + + Delete objects + UsuÅ„ obiekty + + + + DbTreeItemDelegate + + + error + dbtree labels + błąd + + + + (system table) + database tree label + (tabela systemowa) + + + + (virtual) + virtual table label + (wirtualna) + + + + (system index) + database tree label + (indeks systemowy) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Baza danych: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Wersja: + + + + File size: + dbtree tooltip + Rozmiar pliku: + + + + Encoding: + dbtree tooltip + Kodowanie: + + + + Error: + dbtree tooltip + Błąd: + + + + Table : %1 + dbtree tooltip + Tablela: : %1 + + + + Columns (%1): + dbtree tooltip + Kolumny (%1): + + + + Indexes (%1): + dbtree tooltip + Indeksy (%1): + + + + Triggers (%1): + dbtree tooltip + Wyzwalacze (%1): + + + + Copy + Kopiuj + + + + Move + PrzenieÅ› + + + + Include data + Również dane + + + + Include indexes + Również indeksy + + + + Include triggers + Również wyzwalacze + + + + Abort + Przerwij + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Nie udaÅ‚o siÄ™ automatycznie dodać upuszczonego pliku bazy '%1'. NiezbÄ™dna rÄ™czna ingerencja. + + + + Referenced tables + Tabele powiÄ…zane + + + + Do you want to include following referenced tables as well: +%1 + Czy chcesz zawrzeć również powiÄ…zane tabele: +%1 + + + + Name conflict + Konflikt nazwy + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + NastÄ™pujÄ…y obiekt istnieje już w docelowej bazie danych. +ProszÄ™ podać nowÄ…, unikalnÄ… nazwÄ™, lub nacisnąć '%1', aby przerwać operacjÄ™. + + + + SQL statements conversion + Konwersja zapytaÅ„ SQL + + + + Following error occurred while converting SQL statements to the target SQLite version: + NastÄ™pujÄ…ce błędy wystÄ…piÅ‚y podczas konwersji zapytaÅ„ SQL do docelowej wersji SQLite: + + + + Would you like to ignore those errors and proceed? + Czy chcesz zignorować te błędy i kontynuować? + + + + DdlHistoryWindow + + + Filter by database: + Filtruj po bazie danych: + + + + Clear entire history + Wyczyść całą historiÄ™ + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Zapytania wykonane na bazie %1 (%2) +-- Data i godzina wykonania: %3 +%4 + + + + Clear history + Wyczyść historiÄ™ + + + + Are you sure you want to erase entire DDL history? + Czy na pewno chcesz usunąć całą historiÄ™ DDL? + + + + DDL history + Historia DDL + + + + DdlPreviewDialog + + + Queries to be executed + Zapytania do wykonania + + + + Don't show again + Nie pokazuj wiÄ™cej + + + + DebugConsole + + + SQLiteStudio Debug Console + Konsola Debugowania SQLiteStudio + + + + EditorWindow + + + SQL editor + Edytor SQL + + + + Query + Zapytanie + + + + History + Historia + + + + Results in the separate tab + Wyniki w osobnej karcie + + + + Results below the query + Wyniki pod zapytaniem + + + + + SQL editor %1 + Edytor SQL %1 + + + + + Results + Wyniki + + + + Execute query + Wykonaj zapytanie + + + + Explain query + WytÅ‚umacz zapytanie + + + + Clear execution history + sql editor + Wymaż historiÄ™ zapytaÅ„ + + + + Export results + sql editor + Wyeksportuj wyniki + + + + Create view from query + sql editor + Utwórz widok z zapytania + + + + Previous database + Poprzednia baza danych + + + + Next database + NastÄ™pna baza danych + + + + Show next tab + sql editor + Pokaż nastÄ™pnÄ… kartÄ™ + + + + Show previous tab + sql editor + Pokaż poprzedniÄ… kartÄ™ + + + + Focus results below + sql editor + Aktywuj wyniki poniżej + + + + Focus SQL editor above + sql editor + Aktywuj edytor SQL powyżej + + + + Delete selected SQL history entries + sql editor + UsuÅ„ wybrane wpisy z historii SQL + + + + Execute single query under cursor + Wykonaj pojedyncze zapytanie pod kursorem + + + + Execute all queries in editor + Wykonaj wszystkie zapytania w edytorze + + + + Active database (%1/%2) + Aktywna baza danych (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Zapytanie ukoÅ„czone w %1 sekund(y). Liczba przetworzonych wierszy: %2 + + + + Query finished in %1 second(s). + Zapytanie ukoÅ„czone w %1 sekund(y). + + + + Clear execution history + Wymaż historiÄ™ zapytaÅ„ + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Czy na pewno chcesz wymazać całą historiÄ™ zapytaÅ„ SQL? Tego nie można odwrócić. + + + + Cannot export, because no export plugin is loaded. + Nie można wyeksportować, ponieważ żadna wtyczka eksportu nie zostaÅ‚a zaÅ‚adowana. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + Nie wybrano bazdy danych w edytorze SQL. Nie można utworzyć widoku dla nieznanej bazy. + + + + Editor window "%1" has uncommitted data. + Okno edytora "%1" ma niezatwierdzone dane. + + + + ErrorsConfirmDialog + + + Errors + Błędy + + + + Following errors occured: + WystÄ…piÅ‚y nastÄ™pujÄ…ce błędy: + + + + Would you like to proceed? + Czy chcesz kontynuować? + + + + ExecFromFileDialog + + + Execute SQL from file + Wykonaj SQL z pliku + + + + Input file + Plik wejÅ›ciowy + + + + Path to file + Åšcieżka do pliku + + + + Browse for file + PrzeglÄ…daj pliki + + + + Options + Opcje + + + + File encoding + Kodowanie pliku + + + + Skip failing SQL statements + PomiÅ„ zapytania z błędami + + + + SQL scripts (*.sql);;All files (*) + Skrypty SQL (*.sql);;Wszystkie pliki (*) + + + + Execute SQL file + Wykonaj plik SQL + + + + Please provide file to be executed. + ProszÄ™ podać plik do wykonania + + + + Provided file does not exist or cannot be read. + Podany plik nie istnieje, lub nie można go odczytać. + + + + ExportDialog + + + Export + Eksportuj + + + + What do you want to export? + Co chcesz eksportować? + + + + A database + BazÄ™ danych + + + + A single table + PojedynczÄ… tabelÄ™ + + + + Query results + Wyniki zapytania + + + + Table to export + Tabela do wyeksportowania + + + + Database + Baza danych + + + + Table + Tabela + + + + Options + Opcje + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + Gdy ta opcja jest odznaczona, to tylko DDL tabeli (zapytanie CREATE TABLE) jest eksportowane. + + + + Export table data + Eksportuj dane tabeli + + + + Export table indexes + Eksportuj indeksy tabeli + + + + Export table triggers + Eksportuj wyzwalacze tabeli + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Zwróć uwagÄ™, że eksportowanie indeksów i wyzwalaczy tabeli może nie być obsÅ‚ugiwane przez niektóre formaty wyjÅ›ciowe. + + + + Select database objects to export + Wybierz obiekty bazy danych do eksportu + + + + Export data from tables + Eksportuj dane z tabel + + + + Select all + Zaznacz wszystko + + + + Deselect all + Odznacz wszystko + + + + + Database: + Baza danych: + + + + Query to export results for + Zapytanie dla wyników do eksportu + + + + Query to be executed for results: + Zapytanie, które należy wykonać dla wyników: + + + + Export format and options + Format eksportu i opcje + + + + Export format + Format eksportu + + + + Output + WyjÅ›cie + + + + Exported file path + Åšcieżka do wyeksportowanego pliku + + + + Clipboard + Schowek + + + + File + Plik + + + + Exported text encoding: + Kodowanie wyeksportowanego tekstu: + + + + Export format options + Opcje formatu eksportowania + + + + Cancel + Anuluj + + + + + + Select database to export. + Wybierz bazÄ™ do eksportu. + + + + Select table to export. + Wybierz tabelÄ™ do eksportu. + + + + Enter valid query to export. + Wprowadź poprawne zapytanie do eksportu. + + + + Select at least one object to export. + Wybierz przynajmniej jeden obiekt do eksportu. + + + + You must provide a file name to export to. + Musisz podać nazwÄ™ pliku do którego należy wyeksportować. + + + + Path you provided is an existing directory. You cannot overwrite it. + Åšcieżka którÄ… podaÅ‚eÅ› jest istniejÄ…cym katalogiem. Nie można go nadpisać. + + + + The directory '%1' does not exist. + Katalog '%1' nie istnieje. + + + + The file '%1' exists and will be overwritten. + Plik '%1' istnieje i zostanie nadpisany. + + + + All files (*) + Wszystkie pliki (*) + + + + Pick file to export to + Wybierz plik do eksportu + + + + Internal error during export. This is a bug. Please report it. + WystÄ…piÅ‚ wewnÄ™trzny błąd podczas eksportu. To jest błąd programu. ProszÄ™ to zgÅ‚osić. + + + + FileExecErrorsDialog + + + Execution errors + Błędy wykonywania + + + + Following errors were encountered during execution of SQL statements from the file: + NastÄ™pujÄ…ce błędy wystÄ…piÅ‚y podczas wykonywania zapytaÅ„ SQL z pliku: + + + + SQL + SQL + + + + Error + Błąd + + + + Statements that were executed successfully were commited. + Wyniki zapytaÅ„, które zostaÅ‚y wykonane, zostaÅ‚y zatwierdzone. + + + + Statements that were executed successfully were rolled back. + Wyniki zapytaÅ„, które zostaÅ‚y wykonane zostaÅ‚y wycofane. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Nie można edytować tej komórki. Szczegóły: %1 + + + + FontEdit + + + Choose font + font configuration + Wybierz czcionkÄ™ + + + + Form + + + Active SQL formatter plugin + Aktywna wtyczka formatera SQL + + + + FormView + + + Commit row + form view + Zatwierdź wiersz + + + + Rollback row + form view + Wycofaj wiersz + + + + First row + form view + Pierwszy wiersz + + + + Previous row + form view + Poprzedni wiersz + + + + Next row + form view + NastÄ™pny wiersz + + + + Last row + form view + Ostatni wiersz + + + + Insert new row + form view + Wstaw nowy wiersz + + + + Delete current row + form view + UsuÅ„ bieżący wiersz + + + + FunctionsEditor + + + Filter functions + Filtruj funkcje + + + + Input arguments + Argumenty wejÅ›ciowe + + + + Undefined + Niezdefiniowane + + + + Databases + Bazy danych + + + + Register in all databases + Zarejestruj we wszystkich bazach danych + + + + Register in following databases: + Zarejestruj w nastÄ™pujÄ…cych bazach danych: + + + + Type: + Typ: + + + + Function name: + Nazwa funkcji: + + + + Implementation language: + JÄ™zyk implementacji: + + + + Deterministic + Deterministyczna + + + + Initialization code: + Kod inicjalizacji: + + + + + Function implementation code: + Kod implementacji funkcji: + + + + Final step implementation code: + Kod implementacji ostatniego kroku: + + + + SQL functions editor + Edytor funkcji SQL + + + + Commit all function changes + Zatwierdź zmiany we wszystkich funkcjach + + + + Rollback all function changes + Wycofaj zmiany we wszystkich funkcjach + + + + Create new function + Utwórz nowÄ… funkcjÄ™ + + + + Delete selected function + UsuÅ„ wybranÄ… funkcjÄ™ + + + + Custom SQL functions manual + PodrÄ™cznik wÅ‚asnych funkcji SQL + + + + Add function argument + Dodaj argument funkcji + + + + Rename function argument + ZmieÅ„ nazwÄ™ argumentu funkcji + + + + Delete function argument + UsuÅ„ argument funkcji + + + + Move function argument up + PrzesuÅ„ argument funkcji w górÄ™ + + + + Move function argument down + PrzesuÅ„ argument funkcji w dół + + + + Scalar + Skalarna + + + + Aggregate + Agregacyjna + + + + Enter a non-empty, unique name of the function. + Wprowadź niepustÄ…, unikalnÄ… nazwÄ™ funkcji + + + + Pick the implementation language. + Wybierz jÄ™zyk implementacji. + + + + Per step code: + Kod pojedynczego kroku: + + + + Enter a non-empty implementation code. + Wprowadź niepusty kod implementacji. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Okno edytora funkcji ma niezatwierdzone modyfikacje. + + + + ImportDialog + + + Import data + Importuj dane + + + + Table to import to + Tabela do której należy importować + + + + Table + Tabela + + + + Database + Baza danych + + + + Data source to import from + ŹródÅ‚o danych z którego należy importować + + + + Data source type + Typ źródÅ‚a danych + + + + Options + Opcje + + + + Text encoding: + Kodowanie tekstu: + + + + Input file: + Plik wejÅ›ciowy: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>JeÅ›li włączone, to jakiekolwiek naruszenia ograniczeÅ„, lub niepoprawny format danych (niepoprawna liczba kolumn), lub jakikolwiek inny problem, który wystÄ…pi podczas importu zostanie zignorowany i importowanie bÄ™dzie kontynuowane.</p> + + + + Ignore errors + Ignoruj błędy + + + + Data source options + Opcje źródÅ‚a danych + + + + Cancel + Anuluj + + + + If you type table name that doesn't exist, it will be created. + JeÅ›li wpiszesz nazwÄ™ tabeli, która nie istnieje, to zostanie ona stworzona. + + + + Enter the table name + Wprowadź nazwÄ™ tabeli + + + + Select import plugin. + Wybierz wtyczkÄ™ importu + + + + You must provide a file to import from. + Musisz podać plik z którego należy zaimportować. + + + + The file '%1' does not exist. + Plik '%1' nie istnieje. + + + + Path you provided is a directory. A regular file is required. + Åšcieżka którÄ… podaÅ‚eÅ› jest katalogiem. Wymagany jest zwykÅ‚y plik. + + + + Pick file to import from + Wybierz plik do importu + + + + IndexDialog + + + + Index + Indeks + + + + Column + Kolumna + + + + Sort + Sortowanie + + + + Collation + Zestawienie + + + + On table: + Na tabeli: + + + + Delete selected indexed expression + UsuÅ„ wybrane wyrażenie indeksowane. + + + + Moves selected index column up in the order, making it more significant in the index. + Przenosi wybranÄ… indeksowanÄ… kolumnÄ™ wyżej w kolejnoÅ›ci, czyniÄ…c jÄ… ważniejszÄ… w indeksie. + + + + Moves selected index column down in the order, making it less significant in the index. + Przenosi wybranÄ… indeksowanÄ… kolumnÄ™ niżej w kolejnoÅ›ci, czyniÄ…c jÄ… mniej ważnÄ… w indeksie. + + + + Partial index condition + Warunek indeksu częściowego: + + + + Unique index + Indeks unikalny + + + + Index name: + Nazwa indeksu: + + + + Edit selected indexed expression + Edytuj wybrane wyrażenie indeksowane + + + + Add indexed expression + Dodaj wyrażenie indeksowane + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Próbowano otworzyć okno indeksu dla zamkniÄ™tej lub nieistniejÄ…cej bazy. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Nie udaÅ‚o siÄ™ przetworzyć poprawnie indeksu %1. Nie można otworzyć okna indeksu. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unikalny indeks nie może zawierać wyrażeÅ„ indeksowanych. Albo usuÅ„ wyrażenia z poniższej listy, albo odznacz tÄ™ opcjÄ™. + + + + Pick the table for the index. + Wybierz tabelÄ™ dla indeksu. + + + + Select at least one column. + Zaznacz przynajmniej jednÄ… kolumnÄ™. + + + + Enter a valid condition. + Wprowadź poprawny warunek. + + + + default + index dialog + domyÅ›lne + + + + Sort order + table constraints + Kierunek sortowania + + + + + Error + index dialog + Błąd + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Nie można utworzyć indeksu, ponieważ wartoÅ›ci w wybranych kolumnach nie sÄ… unikalne. Czy chcesz wykonać zapytanie SELECT, aby zobaczyć wartoÅ›ci stwarzajÄ…ce problem? + + + + An error occurred while executing SQL statements: +%1 + WystÄ…piÅ‚ błąd podczas wykonywania zapytaÅ„ SQL: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Wyrażenie indeksowane + + + + Expression to index + Wyrażenie do indeksowania + + + + This expression is already indexed by the index. + To wyrażenie jest już indeksowane przez indeks. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Kolumna powinna być indeksowana bezpoÅ›rednio, nie przez wyrażenie. Albo rozszerz to wyrażenie, aby zwieraÅ‚o coÅ› wiÄ™cej niż tylko nazwÄ™ kolumny, albo przerwij i wybierz tÄ… kolumnÄ™ bezpoÅ›rednio w oknie dialogowym indeksu. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Kolumna '%1' nie należy do tabeli objÄ™tej tym indeksem. Wyrażenia indeksowane mogÄ… odnosić siÄ™ jedynie do kolumn z indeksowanej tabeli. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + Nie można używać zapytaÅ„ 'SELECT' w wyrażeniach indeksowanych. + + + + Enter an indexed expression. + Wprowadź wyrażenie indeksowane. + + + + Invalid expression. + Niepoprawne wyrażenie. + + + + LanguageDialog + + + Language + JÄ™zyk + + + + Please choose language: + ProszÄ™ wybrać jÄ™zyk: + + + + MainWindow + + + Database toolbar + Pasek narzÄ™dzi baz danych + + + + Structure toolbar + Pasek narzÄ™dzi struktury + + + + Tools + NarzÄ™dzia + + + + Window list + Lista okien + + + + View toolbar + Pasek narzÄ™dzi widoku + + + + Configuration widgets + Kontrolki konfiguracji + + + + Syntax highlighting engines + Silniki podÅ›wietlania skÅ‚adni + + + + Data editors + Edytory danych + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Uruchomiono tryb debugowania. WciÅ›nij %1 lub wybierz menu 'Pomoc / Otwórz konsolÄ™ debugowania' aby otworzyć konsolÄ™ debugowania. + + + + Running in debug mode. Debug messages are printed to the standard output. + Uruchomiono tryb debugowania. WiadomoÅ›ci debugujÄ…ce sÄ… wyÅ›wietlane na standardowym wyjÅ›ciu. + + + + You need to restart application to make the language change take effect. + Należy zrestartować aplikacjÄ™, aby nastÄ…piÅ‚a zmiana jÄ™zyka. + + + + Open SQL &editor + Otwórz &edytor SQL + + + + Open DDL &history + Otwórz &historiÄ™ DDL + + + + Open SQL &functions editor + Otwórz edytor &funkcji SQL + + + + Open code &snippets editor + Otwórz edytor kodu f&ragmentów + + + + Open &collations editor + Otwórz edytor &zestawieÅ„ + + + + Open ex&tension manager + O&twórzy menadżera rozszerzeÅ„ + + + + &Import + &Importuj + + + + E&xport + E&ksportuj + + + + Open confi&guration dialog + Otwórz okno konfi&guracji + + + + &Tile windows + Ustaw okna w pÅ‚y&tki + + + + Tile windows &horizontally + Ustaw okno po&ziomo + + + + Tile windows &vertically + Ustaw okna pio&nowo + + + + &Cascade windows + Ustaw okna &kaskadowo + + + + Next window + NastÄ™pne okno + + + + Previous window + Poprzednie okno + + + + Hide status field + Ukryj pole statusu + + + + Close &all windows + Z&amknij wszystkie okna + + + + Re&store recently closed window + Przywróć o&statnio zamkniÄ™te okno + + + + Close current &window + Zamknij &bieżące okno + + + + Close &other windows + Zamknij &inne okna + + + + Close windows on the &left + Zamknij okna po &lewej + + + + Close windows on the &right + Zamknij okna po &prawej + + + + Re&name selected window + ZmieÅ„ &nazwÄ™ wybranego okna + + + + Open Debug Console + Otwórz KonsolÄ™ Debugowania + + + + Open CSS Console + Otwórz konsolÄ™ CSS + + + + Report a &bug + ZgÅ‚oÅ› &błąd + + + + D&onate + Wesprzyj d&otacjÄ… + + + + Propose a new &feature + ZgÅ‚oÅ› &pomysÅ‚ + + + + &About + O progra&mie + + + + &Licenses + &Licencje + + + + Open home &page + Otwórz stronÄ™ &domowÄ… + + + + User &Manual + &PodrÄ™cznik Użytkownika + + + + SQLite &documentation + &Dokumentacja SQLite + + + + Bugs and feature &requests + Błęd&y i pomysÅ‚y + + + + Quit + Wyjdź + + + + Check for &updates + Sprawdź akt&ualizacje + + + + &Database + menubar + Bazy &danych + + + + &Structure + menubar + &Struktura + + + + &View + menubar + &Widoki + + + + Window list + menubar view menu + Lista okien + + + + &Tools + menubar + &NarzÄ™dzia + + + + &Help + &Pomoc + + + + Could not set style: %1 + main window + Nie udaÅ‚o siÄ™ ustawić stylu: %1 + + + + Cannot export, because no export plugin is loaded. + Nie można wyeksportować, ponieważ żadna wtyczka eksportu nie zostaÅ‚a zaÅ‚adowana. + + + + Cannot import, because no import plugin is loaded. + Nie można zaimportować, ponieważ żadna wtyczka importu nie zostaÅ‚a zaÅ‚adowana. + + + + Rename window + ZmieÅ„ nazwÄ™ okna + + + + Enter new name for the window: + Wprowadź nowÄ… nazwÄ™ dla okna: + + + + New updates are available. <a href="%1">Click here for details</a>. + Nowe aktualizacje sÄ… dostÄ™pne: <a href="%1">Kliknij aby poznać szczegóły</a>. + + + + You're running the most recent version. No updates are available. + Uruchomiona jest najnowsza wersja. Nie ma dostÄ™pnych aktualizacji. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Baza danych podana w parametrach linii poleceÅ„ (%1) byÅ‚a już na liÅ›cie pod nazwÄ…: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Baza danych podana w linii poleceÅ„ (%1) jest tymczasowo dodana do listy pod nazwÄ…: %2 + + + + Could not add database %1 to list. + Nie udaÅ‚o siÄ™ dodać bazy danych %1 do listy. + + + + MdiWindow + + + Uncommitted changes + Niezatwierdzone zmiany + + + + Close anyway + Zamknij mimo to + + + + Don't close + Nie zamykaj + + + + MultiEditor + + + Null value + multieditor + Wartość null + + + + Configure editors for this data type + Skonfiguruj edytory dla tego typu danych + + + + Open another tab + Otwórz kolejnÄ… zakÅ‚adkÄ™ + + + + Foreign Key + Klucz obcy + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Wtyczka edytora danych '%1' nie jest zaÅ‚adowana, podczas gdy jest ona zdefiniowana do edycji typu danych '%1'. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + UsuniÄ™to + + + + Read only + multieditor + Tylko do odczytu + + + + MultiEditorBoolPlugin + + + Boolean + Logiczna + + + + MultiEditorDatePlugin + + + Date + Data + + + + MultiEditorDateTimePlugin + + + Date & time + Data i czas + + + + MultiEditorHexPlugin + + + Hex + Heks + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Liczba + + + + MultiEditorText + + + Tab changes focus + Tabulator zmienia aktywność + + + + Cut + Wytnij + + + + Copy + Kopiuj + + + + Paste + Wklej + + + + Delete + UsuÅ„ + + + + Undo + Cofnij + + + + Redo + Przywróć + + + + MultiEditorTextPlugin + + + Text + Tekst + + + + MultiEditorTimePlugin + + + Time + Czas + + + + NewConstraintDialog + + + New constraint + Nowe ograniczenie + + + + + Primary Key + new constraint dialog + Klucz główny + + + + + Foreign Key + new constraint dialog + Klucz obcy + + + + + Unique + new constraint dialog + WartoÅ›ci unikalne + + + + + Check + new constraint dialog + Warunek + + + + Not NULL + new constraint dialog + Niepuste + + + + Collate + new constraint dialog + Zestawienie + + + + Generated + new constraint dialog + Generowane + + + + Default + new constraint dialog + Wartość domyÅ›lna + + + + NewVersionDialog + + + SQLiteStudio updates + Aktualizacje SQLiteStudio + + + + New version is available! + DostÄ™pna jest nowa wersja! + + + + Download new version! + Pobierz nowÄ… wersjÄ™! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + Paczka z nowÄ… wersjÄ… zostanie pobrana. Do ciebie bÄ™dzie należaÅ‚o zainstalowanie jej, gdy uznasz to za stosowne. + + + + Open SQLiteStudio home page. + Otwórz stronÄ™ głównÄ… SQLiteStudio. + + + + Read release notes && download package yourself. + Przeczytaj notatki o wydaniu i pobierz pakiet samodzielnie. + + + + Just close this window. + Po prostu zamknij to okno. + + + + Check for updates on startup + Sprawdzaj aktualizacje na starcie + + + + Not now. + Nie teraz. + + + + PopulateConfigDialog + + + Populating configuration + Konfiguracja zaludniania + + + + Configuring <b>%1</b> for column <b>%2</b> + Konfigurowanie <b>%1</b> dla kolumny <b>%2</b> + + + + PopulateDialog + + + Populate table + Zaludnij tabelÄ™ + + + + Database + Baza danych + + + + Table + Tabela + + + + Columns + Kolumny + + + + Number of rows to populate: + Liczba wierszy do zaludnienia: + + + + Populate + populate dialog button + Zaludnij + + + + Abort + Przerwij + + + + Configure + Konfiguruj + + + + Populating configuration for this column is invalid or incomplete. + Konfiguracja zaludniania dla tej kolumny jest niepoprawna lub niekompletna. + + + + Select database with table to populate + Wybierz bazÄ™ danych z tabelÄ… do zaludnienia + + + + Select table to populate + Wybierz tabelÄ™ do zaludnienia + + + + You have to select at least one column. + Musisz zaznaczyć przynajmniej jednÄ… kolumnÄ™. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Nie można edytować kolumn, które sÄ… wynikiem zÅ‚ożonego zapytania %1 (tego, które zawiera sÅ‚owo kluczowe %2, %3, lub %4). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + Mechanizm wykonywania zapytaÅ„ miaÅ‚ problemy z wyciÄ…gniÄ™ciem wÅ‚asnoÅ›ci ROWID. To może być błąd aplikacji. Możesz to zgÅ‚osić. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Ta kolumna jest wynikiem wyrażenia SQL, a nie zwykÅ‚ej selekcji kolumny. Takie kolumny nie mogÄ… być edytowane. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Ta kolumna należy do systemowej tabeli SQLite. Te tabele nie mogÄ… być edytowane bezpoÅ›rednio. + + + + Cannot edit results of query other than %1. + Nie można edytować wyników zapytania innego niż %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Nie można edytować kolumn, które sÄ… wynikiem zapytania agregacyjnego %1. + + + + Cannot edit columns that are result of %1 statement. + Nie można edytować kolumn, które sÄ… wynikiem zapytania %1. + + + + Cannot edit columns that are result of common table expression statement (%1). + Nie można edytować kolumn, które sÄ… wynikiem zapytania ze wspólnym wyrażeniem tabeli (%1). + + + + Cannot edit table generated columns. + Nie można edytować generowanych kolumn tabeli. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Nie można edytować kolumn, które sÄ… wynikiem widoku, jeÅ›li wykonane zapytanie odczytuje z dowolnego widoku wielopoziomowego (tj. widoku który zapytaÅ‚ inny widok). + + + + + + + on conflict: %1 + data view tooltip + w razie konfliktu: %1 + + + + references table %1, column %2 + data view tooltip + odwoÅ‚uje siÄ™ do tabeli %1, kolumny %2 + + + + condition: %1 + data view tooltip + warunek: %1 + + + + collation name: %1 + data view tooltip + nazwa zestawienia: %1 + + + + Data grid view + Widok siatki danych + + + + Edit current cell inline + Edytuj bieżącÄ… komórkÄ™ na miejscu + + + + Copy cell(s) contents to clipboard + Skopiuj zawartość komórek do schowka. + + + + Copy cell(s) contents together with header to clipboard + Skopiuj zawartość komórek z z nagłówkiem do schowka + + + + Paste cell(s) contents from clipboard + Wklej zawartość komórkek ze schowka. + + + + Set empty value to selected cell(s) + Ustaw pustÄ… wartość dla wybranych komórek + + + + Set NULL value to selected cell(s) + Ustaw wartość NULL dla wybranych komórek + + + + Commit changes to cell(s) contents + Zatwierdź zmiany dla zawartoÅ›ci komórek + + + + Rollback changes to cell(s) contents + Wycofaj zmiany dla zawartoÅ›ci komórek + + + + Delete selected data row + UsuÅ„ wybrane wiersze danych + + + + Insert new data row + Wstaw nowy wiersz danych + + + + Open contents of selected cell in a separate editor + Otwórz zawartość wybranej komórki w osobnym edytorze + + + + Toggle the height adjustment of rows + Przełącz regulacjÄ™ wysokoÅ›ci wierszy + + + + Increase font size + data view + ZwiÄ™ksz rozmiar czcionki + + + + Decrease font size + data view + Zmniejsz rozmiar czcionki + + + + Total pages available: %1 + Liczba dostÄ™pnych stron: %1 + + + + Total rows loaded: %1 + Liczba zaÅ‚adowanych wierszy: %1 + + + + Data view (both grid and form) + Widok danych (zarówno siatki i formularza) + + + + Refresh data + OdÅ›wież dane + + + + Switch to grid view of the data + Przełącz do widoku siatki danych + + + + Switch to form view of the data + Przełącz do widoku formularza danych + + + + Database list + Lista baz + + + + Delete selected item + UsuÅ„ zaznaczony element + + + + Clear filter contents + Wyczyść zawartość filtra + + + + Refresh schema + OdÅ›wież schemat + + + + Refresh all schemas + OdÅ›wież wszystkie schematy + + + + Add database + Dodaj bazÄ™ danych + + + + Select all items + Zaznacz wszystkie elementy + + + + Copy selected item(s) + Kopiuj zaznaczone elementy + + + + + + Paste from clipboard + Wklej ze schowka + + + + Increase font size + database list + ZwiÄ™ksz rozmiar czcionki + + + + Decrease font size + database list + Zmniejsz rozmiar czcionki + + + + Tables + Tabele + + + + Indexes + Indeksy + + + + Triggers + Wyzwalacze + + + + Views + Widoki + + + + Columns + Kolumny + + + + Data form view + Widok formularza danych + + + + Commit changes for current row + Zatawierdź zmiany dla bieżącego wiersza + + + + Rollback changes for current row + Wycofaj zmiany dla bieżącego wiersza + + + + Go to first row on current page + Przejdź do pierwszego wiersza na bieżącej stronie + + + + Go to next row + Przejdź do nastÄ™pnego wiersza + + + + Go to previous row + Przejdź do poprzedniego wiersza + + + + Go to last row on current page + Przejdź do ostatniego wiersza na bieżącej stronie + + + + Insert new row + Wstaw nowy wiersz + + + + Delete current row + UsuÅ„ bieżący wiersz + + + + Main window + Okno główne + + + + Open SQL editor + Otwórz edytor SQL + + + + Open DDL history window + Otwórz okno historii DDL + + + + Open snippets editor window + Otwórz okno edytora fragmentów + + + + Open function editor window + Otwórz okno edytora funkcji + + + + Open collation editor window + Otwórz okno edytora zestawieÅ„ + + + + Open extension manager window + Otwórz okno menedżera rozszerzeÅ„ + + + + Previous window + Poprzednie okno + + + + Next window + NastÄ™pne okno + + + + Hide status area + Ukryj pole statusu + + + + Open user manual + Otwórz podrÄ™cznik użytkownika + + + + Open configuration dialog + Otwórz okno konfiguracji + + + + Open Debug Console + Otwórz KonsolÄ™ Debugowania + + + + Open CSS Console + Otwórz konsolÄ™ CSS + + + + Open the About dialog + Otwórz okno O Programie + + + + Quit the application + Wyjdź z aplikacji + + + + Cell text value editor + Edytor tekstowy wartoÅ›ci komórki + + + + + Cut selected text + Wytnij wybrany tekst + + + + + Copy selected text + Skopiuj wybrany tekst + + + + + Delete selected text + UsuÅ„ wybrany tekst + + + + + Undo + Cofnij + + + + + Redo + Przywróć + + + + SQL editor input field + Pole wprowadzania edytora SQL + + + + Select whole editor contents + Zaznacz całą zawartość edytora + + + + Save contents into a file + Zapisz zawartość do pliku + + + + Load contents from a file + Wczytaj zawartość z pliku + + + + Find in text + Znajdź w tekÅ›cie + + + + Find next + Znajdź nastÄ™pny + + + + Find previous + Znajdź poprzedni + + + + Replace in text + ZmieÅ„ w tekÅ›cie + + + + Delete current line + UsuÅ„ bieżącÄ… liniÄ™ + + + + Request code assistant + WywoÅ‚aj asystenta kodu + + + + Format contents + Formatuj zawartość + + + + Move selected block of text one line down + PrzenieÅ› wybrany blok tekstu o jednÄ… liniÄ™ w dół + + + + Move selected block of text one line up + PrzenieÅ› wybrany blok tekstu o jednÄ… liniÄ™ w górÄ™ + + + + Copy selected block of text and paste it a line below + Skopiuj wybrany blok tekstu i wklej go poniżej + + + + Copy selected block of text and paste it a line above + Skopiuj wybrany blok tekstu i wklej go powyżej + + + + Toggle comment + Przełącz komentarz + + + + Increase font size + sql editor + ZwiÄ™ksz rozmiar czcionki + + + + Decrease font size + sql editor + Zmniejsz rozmiar czcionki + + + + All SQLite databases + Wszystkie bazy danych SQLite + + + + All files + Wszystkie pliki + + + + Select database file + Wybierz plik bazy danych + + + + Select + Wybierz + + + + File type + Typ pliku + + + + SQL editor window + Okno edytora SQL + + + + Execute query + Wykonaj zapytanie + + + + Execute single query under cursor + Wykonaj pojedyncze zapytanie pod kursorem + + + + Execute all queries in editor + Wykonaj wszystkie zapytania w edytorze + + + + Execute "%1" query + Wykonaj zapytanie "%1" + + + + Switch current working database to previous on the list + ZmieÅ„ roboczÄ… bazÄ™ danych na poprzedniÄ… z listy + + + + Switch current working database to next on the list + ZmieÅ„ roboczÄ… bazÄ™ danych na nastÄ™pnÄ… z listy + + + + Go to next editor tab + Przejdź do nastÄ™pnej karty edytora + + + + Go to previous editor tab + Przejdź do poprzedniej karty edytora + + + + Move keyboard input focus to the results view below + PrzenieÅ› aktywność klawiatury do widoku wyników poniżej + + + + Move keyboard input focus to the SQL editor above + PrzenieÅ› aktywność klawiatury do edytora SQL powyżej + + + + Delete selected SQL history entries + UsuÅ„ wybrane wpisy z historii SQL + + + + Table window + Okno tabeli + + + + Commit the table structure + Zatwierdź strukturÄ™ tabeli + + + + Rollback pending changes in the table structure + Wycofaj oczekujÄ…ce zmiany w strukturze tabeli + + + + Refresh table structure + OdÅ›wież strukturÄ™ tabeli + + + + Add new column + Dodaj nowÄ… kolumnÄ™ + + + + Edit selected column + Edytuj wybranÄ… kolumnÄ™ + + + + Delete selected column + UsuÅ„ wybranÄ… kolumnÄ™ + + + + Export table data + Eksportuj dane tabeli + + + + Import data to the table + Importuj dane do tabeli + + + + Add new table constraint + Dodaj nowe ograniczenie tabeli + + + + Edit selected table constraint + Edytuj wybrane ograniczenie tabeli + + + + Delete selected table constraint + UsuÅ„ wybrane ograniczenie tabeli + + + + Refresh table index list + OdÅ›wież listÄ™ indeksów tabeli + + + + Add new index + Dodaj nowy indeks + + + + Edit selected index + Edytuj wybrany indeks + + + + Delete selected index + UsuÅ„ wybrany indeks + + + + Refresh table trigger list + OdÅ›wież listÄ™ wyzwalaczy tabeli + + + + + Add new trigger + Dodaj nowy wyzwalacz + + + + + Edit selected trigger + Edytuj wybrany wyzwalacz + + + + + Delete selected trigger + UsuÅ„ wybrany wyzwalacz + + + + + Go to next tab + Przejdź do nastÄ™pnej karty + + + + + Go to previous tab + Przejdź do poprzedniej karty + + + + A view window + Okno widoku + + + + Commit the view's query + Zatwierdź zapytanie widoku + + + + Rollback pending changes in the view's query + Wycofaj oczekujÄ…ce zmiany w widoku zapytania + + + + Refresh view trigger list + OdÅ›wież listÄ™ wizwalaczy widoku + + + + Execute the view's query + Wykonaj zapytanie widoku + + + + A code snippets editor window + Okno edytora fragmentów kodu + + + + + + + Commit the pending changes + Zatwierdź oczekujÄ…ce zmiany + + + + + + + Rollback the pending changes + Wycofaj oczekujÄ…ce zmiany + + + + A collation editor window + Okno edytora zestawieÅ„ + + + + A function editor window + Okno edytora funkcji + + + + A SQLite extension editor window + Okno edytora rozszerzeÅ„ SQLite + + + + QuitConfirmDialog + + + Uncommitted changes + Niezatwierdzone zmiany + + + + Are you sure you want to quit the application? + +Following items are pending: + Czy na pewno chcesz zamknąć aplikacjÄ™? + +NastÄ™pujÄ…ce elementy sÄ… w toku: + + + + SearchTextDialog + + + Find or replace + Znajdź lub zastÄ…p + + + + Find: + Znajdź: + + + + Case sensitive + UwzglÄ™dniaj wielkość liter + + + + Search backwards + Szukaj wstecz + + + + Regular expression matching + Dopasowywanie wyrażeniem regularnym + + + + Replace && +find next + ZastÄ…p i +znajdź nastÄ™pny + + + + Replace with: + ZastÄ…p: + + + + Replace all + ZastÄ…p wszystkie + + + + Find + Znajdź + + + + SortDialog + + + Sort by columns + Sortuj wg. kolumn + + + + + Column + Kolumna + + + + + Order + Kierunek + + + + Sort by: %1 + Sortuj po: %1 + + + + Move column up + PrzesuÅ„ kolumnÄ™ w górÄ™ + + + + Move column down + PrzesuÅ„ kolumnÄ™ w dół + + + + SqlEditor + + + Wrap words + sql editor + Zawijaj sÅ‚owa + + + + Cut + sql editor + Wytnij + + + + Copy + sql editor + Kopiuj + + + + Paste + sql editor + Wklej + + + + Delete + sql editor + UsuÅ„ + + + + Select all + sql editor + Zaznacz wszystko + + + + Undo + sql editor + Cofnij + + + + Redo + sql editor + Przywróć + + + + Complete + sql editor + DopeÅ‚nij + + + + Format SQL + sql editor + Formatuj SQL + + + + Save SQL to file + sql editor + Zapisz SQL do pliku + + + + Select file to save SQL + sql editor + Wybierz plik do zapisu SQL + + + + Load SQL from file + sql editor + Wczytaj SQL z pliku + + + + Delete line + sql editor + UsuÅ„ liniÄ™ + + + + Move block down + sql editor + PrzesuÅ„ blok w dół + + + + Move block up + sql editor + PrzesuÅ„ blok w górÄ™ + + + + Copy block down + sql editor + Skopiuj blok w dół + + + + Copy up down + sql editor + Skopiuj blok w górÄ™ + + + + Find + sql editor + Znajdź + + + + Find next + sql editor + Znajdź nastÄ™pny + + + + Find previous + sql editor + Znajdź poprzedni + + + + Replace + sql editor + ZastÄ…p + + + + Toggle comment + sql editor + Przełącz komentarz + + + + Increase font size + sql editor + ZwiÄ™ksz rozmiar czcionki + + + + Decrease font size + sql editor + Zmniejsz rozmiar czcionki + + + + Could not open file '%1' for writing: %2 + Nie udaÅ‚o siÄ™ otworzyć pliku '%1' do zapisu: %2 + + + + Saved SQL contents to file: %1 + Zapisano zawartość SQL do pliku: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + DopeÅ‚nianie skÅ‚adni może być użyte tylko wtedy, gdy poprawna baza danych jest ustawiona w edytorze SQL. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Zawartość edytora SQL jest ogromna, wiÄ™c sprawdzanie błędów i podÅ›wietlanie istniejÄ…cych obiektów zostaÅ‚o tymczasowo wyłączone. + + + + Save to file + Zapisz do pliku + + + + SQL scripts (*.sql);;All files (*) + Skrypty SQL (*.sql);;Wszystkie pliki (*) + + + + Open file + Otwórz plik + + + + Could not open file '%1' for reading: %2 + Nie udaÅ‚o siÄ™ otworzyć pliku '%1' do odczytu: %2 + + + + Reached the end of document. Hit the find again to restart the search. + OsiÄ…gniÄ™to koniec dokumentu. WciÅ›nij szukanie ponownie, aby zrestartować szukanie. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Błąd zatwierdzenia: + + + + Column: + data view tooltip + Kolumna: + + + + Data type: + data view + Typ danych: + + + + Table: + data view tooltip + Tabela: + + + + Constraints: + data view tooltip + Ograniczenie: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Nie można edytować tej komórki. Szczegóły: %1 + + + + The row is marked for deletion. + Wiersz jest zaznaczony do usuniÄ™cia. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Struktura tej tabeli zmieniÅ‚a siÄ™ od ostatniego Å‚adowania danych. PrzeÅ‚aduj dane, aby kontynuować. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Edytowanie ogromnych iloÅ›ci danych w podrÄ™cznym edytorze nie jest dobrym pomysÅ‚em. Może być on powolny i nieporÄ™czny. Lepiej edytować takie duże iloÅ›ci danych w Widoku Formularza, lub w osobnym oknie edytora (dostÄ™pnym w menu prawego klikniÄ™cia myszy). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Klucz obcy dla kolumny %2 ma wiÄ™cej niż %1 możliwych wartoÅ›ci. To zbyt wiele, by wyÅ›wietlić w liÅ›cie rozwijanej. Musisz edytować wartość rÄ™cznie. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Tylko jedno zapytanie może być wykonywane w danym momencie. + + + + Cannot execute query on undefined or invalid database. + Nie można wykonać zapytania na niezdefiniowanej lub nieprawidÅ‚owej bazie danych. + + + + Cannot execute empty query. + Nie można wykonać pustego zapytania. + + + + Uncommitted data + Niezatwierdzone dane + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + Niektóre zmiany w danych nie zostaÅ‚y zatwierdzone. Czy na pewno chcesz kontynuować? Wszystkie niezatwierdzone zmiany zostanÄ… utracone. + + + + Cannot commit the data for a cell that refers to the already closed database. + Nie można zatwierdzić danych dla komórki, która odnosi siÄ™ do zamkniÄ™tej już bazy danych. + + + + Could not begin transaction on the database. Details: %1 + Nie udaÅ‚o siÄ™ rozpocząć transakcji na bazie danych. Szczegóły: %1 + + + + An error occurred while committing the transaction: %1 + WystÄ…piÅ‚ błąd podczas zatwierdzania transakcji: %1 + + + + An error occurred while rolling back the transaction: %1 + WystÄ…piÅ‚ błąd podczas wycofywania transakcji: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Próbowano zatwierdzić komórkÄ™, której nie można edytować (a mimo to zostaÅ‚a zmodyfikowana i czeka na zatwierdzenie)! To jest błąd. ProszÄ™ to zgÅ‚osić. + + + + An error occurred while committing the data: %1 + WystÄ…piÅ‚ błąd podczas zatwierdzania danych: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Liczba wierszy na stronÄ™ zostaÅ‚a zmniejszona do %1, w zwiÄ…zku z liczbÄ… kolumn (%2) w widoku danych. + + + + + Error while executing SQL query on database '%1': %2 + Błąd podczas wykonywania zapytania SQL na bazie '%1': %2 + + + + Error while loading query results: %1 + Błąd podczas wczytywania wyników zapytania: %1 + + + + Insert multiple rows + Wstaw wiele wierszy + + + + Number of rows to insert: + Liczba wierszy do wstawienia: + + + + Delete rows + UsuÅ„ wiersze + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + Chcesz usunąć nowo wstawione wiersze, które nie sÄ… jeszcze zatwierdzone. Numery wierszy: %1 +Takie usuniÄ™cie bÄ™dzie trwaÅ‚e. Czy na pewno chcesz je usunąć? + + + + SqlQueryView + + + Go to referenced row in... + Idź do powiÄ…zanego wiersza w... + + + + Copy + Kopiuj + + + + Copy with headers + Kopiuj z nagłówkami + + + + Copy as... + Kopiuj jako... + + + + Paste + Wklej + + + + Paste as... + Wklej jako... + + + + Set NULL values + Ustaw wartoÅ›ci NULL + + + + Erase values + Wymaż wartoÅ›ci + + + + Commit + Zatwierdź + + + + Rollback + Wycofaj + + + + Commit selected cells + Zatwierdź zaznaczone komórki + + + + Rollback selected cells + Wycofaj zaznaczone komórki + + + + Edit current cell inline + Edytuj bieżącÄ… komórkÄ™ na miejscu + + + + Define columns to sort by + Zdefiniuj kolumny po których sortować + + + + Remove custom sorting + Wycofaj wÅ‚asne sortowanie + + + + Insert row + Wstaw wiersz + + + + Insert multiple rows + Wstaw wiele wierszy + + + + Delete selected row + UsuÅ„ zaznaczony wiersz + + + + Adjust height of rows + Dostosuj wysokość wierszy + + + + Increase font size + data view + ZwiÄ™ksz rozmiar czcionki + + + + Decrease font size + data view + Zmniejsz rozmiar czcionki + + + + Invert selection + data view + Odwróć zaznaczenie + + + + Edit value in editor + Edytuj wartość w edytorze + + + + Show value in a viewer + Pokaż wartość w przeglÄ…darce + + + + Generate query for selected cells + Generuj zapytanie dla wybranych komórek + + + + No items selected to paste clipboard contents to. + Nie wybrano elementów do których należy wkleić zawartość schowka. + + + + Cannot paste data. Details: %1 + Nie można wkleić danych. Szczegóły: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + ZmieniÅ‚a siÄ™ struktura przynajmniej jednej tabeli od czasu ostatniego wczytania danych. OdÅ›wież dane, aby kontynuować. + + + + Cannot paste to a cell. Details: %1 + Nie można wkleić do komórki. Szczegóły: %1 + + + + The row is marked for deletion. + Wiersz jest zaznaczony do usuniÄ™cia. + + + + Cannot paste to column %1. Details: %2 + Nie można wkleić do kolumny %1. Szczegóły: %2 + + + + Go to referenced row in table '%1' + Idź do powiÄ…zanego wiersza w tabeli '%1' + + + + table '%1' + tabela '%1' + + + + Referenced row (%1) + PowiÄ…zany wiersz (%1) + + + + Trim pasted text? + Przyciąć wklejany tekst? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + Wklejany tekst zawiera spacje na poczÄ…tku lub koÅ„cu. Czy przyciąć go automatycznie? + + + + Paste "NULL" as null value? + Wkleić "NULL" jako wartość zerowÄ…? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + Wklejony tekst zawiera literaÅ‚y "NULL". Czy chcesz je uznać za wartoÅ›ci NULL? + + + + Edit value + Edytuj wartość + + + + SqlTableModel + + + Error while committing new row: %1 + Błąd podczas zatwierdzania nowego wiersza: %1 + + + + Error while deleting row from table %1: %2 + Błąd podczas usuwania wiersza z tabeli %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filtruj rozszerzenia + + + + Leave empty to use default function + Pozostaw puste, aby użyć domyÅ›lnej funkcji + + + + Extension file + Plik rozszerzenia + + + + Initialization function + Funkcja inicjalizujÄ…ca + + + + Databases + Bazy danych + + + + Register in all databases + Zarejestruj we wszystkich bazach danych + + + + Register in following databases: + Zarejestruj w nastÄ™pujÄ…cych bazach danych: + + + + Extension manager window has uncommitted modifications. + Okno menadżera rozszerzeÅ„ ma niezatwierdzone modyfikacje. + + + + Extension manager + Menadżer rozszerzeÅ„ + + + + Commit all extension changes + Zatwierdź wszystkie zmiany w rozszerzeniach + + + + Rollback all extension changes + Wycofaj wszystkie zmiany w rozszerzeniach + + + + Add new extension + Dodaj nowe rozszerzenie + + + + Remove selected extension + UsuÅ„ wybrane rozszerzenie + + + + Editing extensions manual + PodrÄ™cznik edytowania rozszerzeÅ„ + + + + File with given path does not exist or is not readable. + Plik o podanej Å›cieżce nie istnieje lub nie można go odczytać. + + + + Unable to load extension: %1 + Nie można zaÅ‚adować rozszerzenia: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Niepoprawna nazwa funkcji inicjalizujÄ…cej. Nazwa funkcji może zawierać jedynie znaki alfanumeryczne i znak podkreÅ›lenia. + + + + Dynamic link libraries (*.dll);;All files (*) + Biblioteki linkowania dynamicznego (*.dll);;Wszystkie pliki (*) + + + + Shared objects (*.so);;All files (*) + Obiekty wspóldzielone (*.so);;Wszystkie pliki (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Biblioteki dynamiczne (*.dylib);;Wszystkie pliki (*) + + + + All files (*) + Wszystkie pliki (*) + + + + Open file + Otwórz plik + + + + StatusField + + + Status + Status + + + + Copy + Kopiuj + + + + Clear + Wyczyść + + + + TableConstraintsModel + + + Type + table constraints + Typ + + + + Details + table constraints + Szczegóły + + + + Name + table constraints + Nazwa + + + + TableForeignKeyPanel + + + Foreign table: + Tabela obca: + + + + Columns + Kolumny + + + + Local column + Kolumna lokalna + + + + Foreign column + Kolumna obca + + + + Reactions + Reakcje + + + + Deferred foreign key + Klucz obcy odroczony + + + + Named constraint + Nazwane ograniczenie + + + + Constraint name + Nazwa ograniczenia + + + + Pick the foreign column. + Wybierz kolumnÄ™ obcÄ… + + + + Pick the foreign table. + Wybierz tabelÄ™ obcÄ… + + + + Select at least one foreign column. + Wybierz przynajmnie jednÄ… kolumnÄ™ obcÄ…. + + + + Enter a name of the constraint. + Wprowadź nazwÄ™ ograniczenia. + + + + Foreign column + table constraints + Kolumna obca + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Kolumny + + + + Column + Kolumna + + + + Collation + Zestawienie + + + + Sort + Sortowanie + + + + Valid only for a single column with INTEGER data type + Dozwolone tylko dla jednej kolumny o typie danych INTEGER + + + + Autoincrement + Autoinkrementacja + + + + Named constraint + Nazwane ograniczenie + + + + Constraint name + Nazwa ograniczenia + + + + On conflict + W razie konfliktu + + + + Collate + table constraints + Zestawienie + + + + Sort order + table constraints + Kierunek sortowania + + + + Select at least one column. + Zaznacz przynajmniej jednÄ… kolumnÄ™. + + + + Enter a name of the constraint. + Wprowadź nazwÄ™ ograniczenia. + + + + TableStructureModel + + + Name + table structure columns + Nazwa + + + + Data type + table structure columns + Typ danych + + + + Primary +Key + table structure columns + Klucz +Główny + + + + Foreign +Key + table structure columns + Klucz +Obcy + + + + Unique + table structure columns + WartoÅ›ci +unikalne + + + + Check + table structure columns + Warunek + + + + Not +NULL + table structure columns + Niepuste + + + + Collate + table structure columns + Zestawienie + + + + Generated + table structure columns + Generowane + + + + Default value + table structure columns + DomyÅ›lna wartość + + + + TableWindow + + + Structure + Struktura + + + + Table name: + Nazwa tabeli: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Włącza/wyłącza klauzulÄ™ WITHOUT ROWID na tabeli. Taka tabela nie bÄ™dzie już miaÅ‚a ukrytej kolumny &quot;rowid&quot;. Dla takiej tabeli obowiÄ…zkowa jest jawna kolumna PRIMARY KEY. WiÄ™cej szczegółów na ten temat można przeczytać w oficjalnej dokumentacji SQLite.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Włącza/wyłącza klauzulÄ™ STRICT dla tabeli. W tabeli takiej dokonuje siÄ™ Å›cisÅ‚ej weryfikacji danych przechowywanych w kolumnach w odniesieniu do deklarowanych typów danych dla tych kolumn. Jest to podobne do tego, w jaki sposób typy danych sÄ… zazwyczaj wymuszane w wiÄ™kszoÅ›ci innych baz danych. Pozostaw wyłączone, aby używać klasycznego zachowania SQLite (tj. brak wymuszania typu danych). WiÄ™cej szczegółów znajdziesz w oficjalnej dokumentacji SQLite.</p></body></html> + + + + + Data + Dane + + + + Constraints + Ograniczenia + + + + Indexes + Indeksy + + + + Triggers + Wyzwalacze + + + + DDL + DDL + + + + Export table + table window + Eksportuj tabelÄ™ + + + + Import data to table + table window + Importuj do tabeli + + + + Populate table + table window + Zaludnij tabelÄ™ + + + + Refresh structure + table window + OdÅ›wież strukturÄ™ + + + + Commit structure changes + table window + Zatwierdź zmiany w strukturze + + + + Rollback structure changes + table window + Wycofaj zmiany w strukturze + + + + Add column + table window + Dodaj kolumnÄ™ + + + + Edit column + table window + Edytuj kolumnÄ™ + + + + + Delete column + table window + UsuÅ„ kolumnÄ™ + + + + Move column up + table window + PrzesuÅ„ kolumnÄ™ w górÄ™ + + + + Move column down + table window + PrzesuÅ„ kolumnÄ™ w dół + + + + Create similar table + table window + Utwórz podobnÄ… tabelÄ™ + + + + Reset autoincrement value + table window + Wyzeruj wartość autoinkrementacji + + + + Add table constraint + table window + Dodaj ograniczenie tabeli + + + + Edit table constraint + table window + Edytuj ograniczenie tabeli + + + + Delete table constraint + table window + UsuÅ„ ograniczenie tabeli + + + + Move table constraint up + table window + PrzesuÅ„ ograniczenie tabeli w górÄ™ + + + + Move table constraint down + table window + PrzesuÅ„ ograniczenie tabeli w dół + + + + Add table primary key + table window + Dodaj klucz główny tabeli + + + + Add table foreign key + table window + Dodaj klucz obcy tabeli + + + + Add table unique constraint + table window + Dodaj ograniczenie unikalnych wartoÅ›ci tabeli + + + + Add table check constraint + table window + Dodaj ograniczenie warunkiem tabeli + + + + Refresh index list + table window + OdÅ›wież listÄ™ indeksów + + + + + Create index + table window + Utwórz indeks + + + + Edit index + table window + Edytuj indeks + + + + Delete index + table window + UsuÅ„ indeks + + + + Refresh trigger list + table window + OdÅ›wież listÄ™ wyzwalaczy + + + + + Create trigger + table window + Utwórz wyzwalacz + + + + Edit trigger + table window + Edytuj wyzwalacz + + + + Delete trigger + table window + UsuÅ„ wyzwalacz + + + + Are you sure you want to delete column '%1'? + table window + Czy na pewno chcesz usunąć kolumnÄ™ '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + NastÄ™pujÄ…ce problemy wystÄ…piÄ… podczas modyfikacji tabeli. +Czy chcesz kontynuować? + + + + Table modification + table window + Modyfikacja tabeli + + + + Could not load data for table %1. Error details: %2 + Nie udaÅ‚o siÄ™ zaÅ‚adować danych dla tabeli %1. Szczegóły błędu: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Nie udaÅ‚o siÄ™ przetworzyć poprawnie tabeli %1. Nie można otworzyć okna tabeli. + + + + Database + Baza danych + + + + Could not restore window %1, because no database or table was stored in session for this window. + Nie można przywrócić okna %1, ponieważ nie ma bazy danych lub tabeli zachowanej w sesji dla tego okna. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Nie można przywrócić okna %1, ponieważ nie ma bazy danych lub tabeli zachowanej w sesji dla tego okna. + + + + Could not restore window '%1', because database %2 could not be resolved. + Nie udaÅ‚o siÄ™ przywrócić okna '%1', ponieważ nie udaÅ‚o siÄ™ ustalić bazy danych %2. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Nie można przywrócić okna '%1', ponieważ tabela %2 nie istnieje w bazie danych %3. + + + + + New table %1 + Nowa tabela %1 + + + + Committed changes for table '%1' successfully. + PomyÅ›lnie zatwierdzono zmiany dla tabeli '%1'. + + + + Committed changes for table '%1' (named before '%2') successfully. + PomyÅ›lnie zatwierdzono zmiany dla tabeli '%1' (nazwanej wczeÅ›niej '%2'). + + + + Could not commit table structure. Error message: %1 + table window + Nie udaÅ‚o siÄ™ zatwierdzić struktury tabeli. Treść błędu: %1 + + + + Reset autoincrement + Wyzeruj autoinkrementacjÄ™ + + + + Are you sure you want to reset autoincrement value for table '%1'? + Czy na pewno chcesz wyzerować wartość autoinkrementacji dla tabeli '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + WystÄ…piÅ‚ błąd podczas próby wyzerowania wartoÅ›ci autoinkrementacji dla tabeli '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Wartość automatycznej inkrementacji dla tabeli '%1' zostaÅ‚a zresetowana. + + + + Empty name + Pusta nazwa + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + Pusta nazwa dla tabeli jest dozwolona w SQLite, ale nie jest zalecana. +Czy na pewno chcesz utworzyć tabelÄ™ o pustej nazwie? + + + + Cannot create a table without at least one column. + Nie można utworzyć tabeli bez przynajmniej jednej kolumny. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Nie można utworzyć tabeli %1, jeÅ›li nie ma zdefiniowanego klucza głównego. Albo udznacz %2, albo zdefiniuj klucz główny. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Nie można użyć autoinkrementacji dla klucza głównego, kiedy klauzula %1 jest użyta. Albo odnacz %2, albo autonkrementacjÄ™ w kluczu głównym. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + NastÄ™pujÄ…ce kolumny majÄ… nieÅ›cisÅ‚y typ danych: %1. Wyłącz tryb Å›cisÅ‚y tabeli lub napraw typy danych kolumn. PrawidÅ‚owe Å›cisÅ‚e typy danych to: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Czy na pewno chcesz usunąć ograniczenie tabeli '%1'? + + + + Delete constraint + table window + UsuÅ„ ograniczenie + + + + Cannot export, because no export plugin is loaded. + Nie można wyeksportować, ponieważ żadna wtyczka eksportu nie zostaÅ‚a zaÅ‚adowana. + + + + Cannot import, because no import plugin is loaded. + Nie można zaimportować, ponieważ żadna wtyczka importu nie zostaÅ‚a zaÅ‚adowana. + + + + Uncommitted changes + Niezatwierdzone zmiany + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + Zmiany w strukturze nie zostaÅ‚y zatwierdzone. Nie można przeglÄ…dać, ani edytować danych, dopóki struktura tabeli nie zostanie ustalona. +Czy chcesz zatwierdzić strukturÄ™, czy jednak chcesz wrócić do karty struktury? + + + + Go back to structure tab + Wróć do karty struktury + + + + Commit modifications and browse data. + Zatwierdź modyfikacje i przeglÄ…daj dane. + + + + Name + table window indexes + Nazwa + + + + Unique + table window indexes + WartoÅ›ci unikalne + + + + Columns + table window indexes + Kolumny + + + + Partial index condition + table window indexes + Warunek indeksu częściowego: + + + + Name + table window triggers + Nazwa + + + + Event + table window triggers + Zdarzenie + + + + Condition + table window triggers + Warunek + + + + Details + table window triggers + Szczegóły + + + + Table window "%1" has uncommitted structure modifications and data. + Okno tabeli "%1" ma niezatwierdzone modyfikacje struktury i danych. + + + + Table window "%1" has uncommitted data. + Okno tabeli "%1" ma niezatwierdzone dane. + + + + Table window "%1" has uncommitted structure modifications. + Okno tabeli "%1" ma niezatwierdzone modyfikacje struktury. + + + + TriggerColumnsDialog + + + Trigger columns + Kolumny wyzwalacza + + + + Triggering columns: + Kolumny wyzwalajÄ…ce: + + + + Select all + Zaznacz wszystko + + + + Deselect all + Odznacz wszystko + + + + TriggerDialog + + + + Trigger + Wyzwalacz + + + + On table: + Na tabeli: + + + + Action: + Akcja: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>Warunek SQL, który bÄ™dzie wykonany przed wÅ‚aÅ›ciwym kodem wyzwalacza. W przypadku gdy warunek zwróci faÅ‚sz, wyzwalacz nie zostanie uruchomiony dla tego wiersza.</p> + + + + Pre-condition: + Warunek wstÄ™pny: + + + + The scope is still not fully supported by the SQLite database. + Zakres wciąż nie jest w peÅ‚ni obsÅ‚ugiwany przez bazy danych SQLite. + + + + Trigger name: + Nazwa wyzwalacza: + + + + When: + Kiedy: + + + + List of columns for UPDATE OF action. + Lista kolumn dla akcji UPDATE OF. + + + + Scope: + Zakres: + + + + Code: + Kod: + + + + Trigger statements to be executed. + Zapytania wyzwalacz do wykonania. + + + + DDL + DDL + + + + On view: + Na widoku: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Nie udaÅ‚o siÄ™ przetworzyć poprawnie wyzwalacza %1. Nie można otworzyć okna wyzwalacza. + + + + Enter a valid condition. + Wprowadź poprawny warunek. + + + + Enter a valid trigger code. + Wprowadź poprawny kod wyzwalacza. + + + + Error + trigger dialog + Błąd + + + + An error occurred while executing SQL statements: +%1 + WystÄ…piÅ‚ błąd podczas wykonywania zapytaÅ„ SQL: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Konwersja bazy danych + + + + Following changes to the SQL statements will be made: + Dokonane bÄ™dÄ… nastÄ™pujÄ…ce zmiany w zapytaniach SQL: + + + + Before + Przed + + + + After + Po + + + + ViewWindow + + + Query + Zapytanie + + + + View name: + Nazwa widoku: + + + + Output column names + Nazwy kolumn wyjÅ›ciowych + + + + + Data + Dane + + + + Triggers + Wyzwalacze + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Nie można przywrócić okna %1, ponieważ nie ma bazy danych lub tabeli zachowanej w sesji dla tego okna. + + + + Could not restore window '%1', because database %2 could not be resolved. + Nie udaÅ‚o siÄ™ przywrócić okna '%1', ponieważ nie udaÅ‚o siÄ™ ustalić bazy danych %2. + + + + Could not restore window '%1', because database %2 could not be open. + Nie można przywrócić okna '%1', ponieważ nie można byÅ‚o otworzyć bazy danych %2. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Nie można przywrócić okna '%1', ponieważ widok %2 już nie jestnieje w bazie danych %3. + + + + + New view %1 + Nowy widok %1 + + + + Database + Baza danych + + + + Refresh the view + view window + OdÅ›wież widok + + + + Commit the view changes + view window + Zatwierdź zmiany w widoku + + + + Rollback the view changes + view window + Wycofaj zmiany w widoku + + + + Explicit column names + Jawne nazwy kolumn + + + + Generate output column names automatically basing on result columns of the view. + Generuj automatycznie nazwy kolumn wyjÅ›ciowych bazujÄ…c na kolumnach wynikowych widoku. + + + + Add column + view window + Dodaj kolumnÄ™ + + + + Edit column + view window + Edytuj kolumnÄ™ + + + + Delete column + view window + UsuÅ„ kolumnÄ™ + + + + Move column up + view window + PrzesuÅ„ kolumnÄ™ w górÄ™ + + + + Move column down + view window + PrzesuÅ„ kolumnÄ™ w dół + + + + Refresh trigger list + view window + OdÅ›wież listÄ™ wyzwalaczy + + + + Create new trigger + view window + Utwórz nowy wyzwalacz + + + + Edit selected trigger + view window + Edytuj wybrany wyzwalacz + + + + Delete selected trigger + view window + UsuÅ„ wybrany wyzwalacz + + + + View window "%1" has uncommitted structure modifications and data. + Okno widoku "%1" ma niezatwierdzone modyfikacje struktury i danych. + + + + View window "%1" has uncommitted data. + Okno widoku "%1" ma niezatwierdzone dane. + + + + View window "%1" has uncommitted structure modifications. + Okno widoku "%1" ma niezatwierdzone modyfikacje struktury. + + + + Could not load data for view %1. Error details: %2 + Nie udaÅ‚o siÄ™ zaÅ‚adować danych dla widoku %1. Szczegóły błędu: %2 + + + + Uncommitted changes + Niezatwierdzone zmiany + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + Zmiany w strukturze nie zostaÅ‚y zatwierdzone. Nie można przeglÄ…dać, ani edytować danych, dopóki struktura widoku nie zostanie ustalona. +Czy chcesz zatwierdzić strukturÄ™, czy jednak chcesz wrócić do karty struktury? + + + + Go back to structure tab + Wróć do karty struktury + + + + Commit modifications and browse data. + Zatwierdź modyfikacje i przeglÄ…daj dane. + + + + View '%1' was committed successfully. + Widok '%1' zostaÅ‚ pomyÅ›lnie zatwierdzony. + + + + Committed changes for view '%1' successfully. + PomyÅ›lnie zatwierdzono zmiany dla widoku '%1'. + + + + Committed changes for view '%1' (named before '%2') successfully. + PomyÅ›lnie zatwierdzono zmiany dla tabeli '%1' (nazwanej wczeÅ›niej '%2'). + + + + Could not commit view changes. Error message: %1 + view window + Nie udaÅ‚o siÄ™ zatwierdzić widoku. Treść błędu: %1 + + + + Override columns + Nadpisz kolumny + + + + Currently defined columns will be overriden. Do you want to continue? + Aktualnie zdefiniowane kolumny zostanÄ… nadpisane. Czy chcesz kontynuować? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Nie udaÅ‚o siÄ™ ustalić kolumn zwracanych z widoku. Zapytanie jest prawdopodobnie niekompletne lub zawiera błędy. + + + + Name + view window triggers + Nazwa + + + + Instead of + view window triggers + Zamiast + + + + Condition + view window triggers + Warunek + + + + Details + table window triggers + Szczegóły + + + + Could not process the %1 view correctly. Unable to open a view window. + Nie udaÅ‚o siÄ™ przetworzyć poprawnie widoku %1. Nie można otworzyć okna widoku. + + + + Empty name + Pusta nazwa + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + Pusta nazwa dla widoku jest dozwolona w SQLite, ale nie jest zalecana. +Czy na pewno chcesz utworzyć widok o pustej nazwie? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + Zapytanie SELECT nie mogÅ‚o być poprawnie przeanalizowane. ProszÄ™ poprawić zapytanie i spróbować ponownie. +Szczegóły: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + Widok nie mógÅ‚ być zmodyfikowany w zwiÄ…zku z wewnÄ™trznym błędem SQLiteStudio. ProszÄ™ to zgÅ‚osić! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + Kod widok nie mógÅ‚ być poprawnie przeanalizowany. To jest błąd SQLiteStudio ProszÄ™ to zgÅ‚osić! + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + NastÄ™pujÄ…ce problemy wystÄ…piÄ… podczas modyfikacji widoku. +Czy chcesz kontynuować? + + + + View modification + view window + Modyfikacja widoku + + + + WidgetCover + + + Interrupt + Przerwij + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_BR.qm b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_BR.qm and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_BR.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_BR.ts index 0d15aa6..2ba5919 100644 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_BR.ts +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_BR.ts @@ -1,6612 +1,7110 @@ - - + + AboutDialog - - About SQLiteStudio and licenses - + + About SQLiteStudio and licenses + Sobre SQLiteStudio e licenças - - About - + + About + Sobre - - Licenses - + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Gerenciador de banco de dados grátis, open-source, multi-plataforma.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio. l</span></a><br/></p><p align="center">%2<br/></p><p align="center">Autor e mantenedor ativo:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft. um.pl</span></a>)<br/></p></body></html> - - Environment - + + Licenses + Licenças - - Icon directories - + + Environment + Ambiente - - Form directories - + + Icon directories + Diretórios de ícones - - Plugin directories - + + Form directories + Diretório de formulários - - Application directory - + + SQLite extension directories + Diretórios de extensões SQLite - - SQLite 3 version: - + + Plugin directories + Diretório de plugins - - Configuration directory - + + Configuration directory + Configuração do diretório - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - + + Application directory + Diretório da aplicação - - Qt version: - + + Qt version: + Qt versão - - Portable distribution. - + + SQLite 3 version: + SQLite 3 versão - - MacOS X application boundle distribution. - + + Portable distribution. + Distribuição portátil. - - Operating system managed distribution. - + + MacOS X application bundle distribution. + MacOS X application bundle distribution. - - Copy - + + Operating system managed distribution. + Distribuição gerenciada pelo sistema operacional. - - <h3>Table of contents:</h3><ol>%2</ol> - + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Conteúdo:</h3><ol>%2</ol> - - + + BindParamsDialog - - Query parameters - + + Query parameters + Parâmetros de consultas - - Please provide values for query parameters - + + Please provide values for query parameters + Forneça valores para os parâmetros de consulta - - + + + CodeSnippetEditor + + + Filter snippets + Filtrar trechos + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Atalho opcional, que funcionará apenas no contexto da janela ativa de assistente de código. Ele permite ao usuário usar combinações de teclas que de outra forma entrariam em conflito com outros atalhos. Ter a janela do assistente de código conforme o contexto necessário torna a escolha das chaves mais versáteis.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>O nome do snippet será exibido no assistente de código. Para acessar a lista de snippets o usuário precisa clicar duas vezes no atalho assistente de código.</p></body></html> + + + + Snippet name + Nome do Snippet + + + + Code assistant shortcut + Atalho do assistente de código + + + + Snippet code + Nome do Snippet + + + + Code Snippets editor window has uncommitted modifications. + A janela do editor de colagens possui modificações não autorizadas. + + + + Code Snippets editor + Editor de snippet + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Mova o trecho para cima + + + + Move the snippet down + Move o trecho para baixo + + + + Code snippets manual + Snippets de código manuais + + + + Enter a non-empty, unique name of the snippet. + Insira um nome único e completo da função. + + + + Enter a non-empty snippet content. + Digite um conteúdo de snippet não-vazio. + + + + This hotkey is not unique in context of a code assistant. + Este atalho não é exclusivo no contexto de um assistente de código. + + + CollationsEditor - - Filter collations - + + Filter collations + Filtrar agrupamentos - - Collation name: - + + Databases + Banco de dados - - Implementation language: - + + Register in all databases + Inscrever todos os bancos de dados - - Databases - + + Register in following databases: + Inscrever e seguir os banco de dados - - Register in all databases - + + Implementation code: + Código de implementação: - - Register in following databases: - + + Collation name: + Nome do agrupamento: - - Implementation code: - + + Implementation language: + Idioma de implementação: - - Collations editor - + + Collations editor + Editor de ordenações - - Commit all collation changes - + + Commit all collation changes + Aplicar todas as alterações de agrupamento - - Rollback all collation changes - + + Rollback all collation changes + Reverter todas as alterações de agrupamento - - Create new collation - + + Create new collation + Criar novo agrupamento - - Delete selected collation - + + Delete selected collation + Apagar o agrupamento selecionado - - Editing collations manual - + + Editing collations manual + Editar ordenamento manual - - Enter a non-empty, unique name of the collation. - + + Enter a non-empty, unique name of the collation. + Insira um nome único e vazio para a coleção. - - Pick the implementation language. - + + Pick the implementation language. + Escolha a linguagem de implementação. - - Enter a non-empty implementation code. - + + Enter a non-empty implementation code. + Digite um código de implementação não vazio. - - Collations editor window has uncommitted modifications. - + + Collations editor window has uncommitted modifications. + A janela do editor de colagens possui modificações não autorizadas. - - + + ColorButton - - Pick a color - + + Pick a color + Escolha uma cor - - + + ColumnCollatePanel - - Collation name: - + + Collation name: + Nome do agrupamento: - - Named constraint: - + + Named constraint: + Restrição nomeada: - - Enter a name of the constraint. - + + Enter a name of the constraint. + Digite um nome da restrição - - Enter a collation name. - + + Enter a collation name. + Insira um nome de agrupamento. - - + + ColumnDefaultPanel - - Default value: - + + Default value: + Valor padrão: - - Named constraint: - + + Named constraint: + Restrição nomeada: - - Enter a default value expression. - + + Enter a default value expression. + Informe uma expressão de valor padrão. - - Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Expressão de valor padrão inválido: %1. Se você quiser usar uma string simples como valor, lembre-se de envolvê-lo com caracteres de cotação. - - Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Expressão de valor padrão inválida. Se você quiser usar uma string simples como valor, lembre-se de colocá-la em cima com caracteres de cotação. - - Enter a name of the constraint. - + + Enter a name of the constraint. + Digite um nome da restrição - - + + ColumnDialog - - Column - + + Column + Coluna - - Name and type - + + Name and type + Nome e tipo - - Scale - + + Scale + Escala - - Precision - + + Precision + Precisão - - Data type: - + + Data type: + Tipo de dado: - - Column name: - + + Column name: + Nome da coluna: - - Size: - + + Size: + Tamanho: - - Constraints - + + Constraints + Constraints - - Unique - + + Generated value + Valor gerado - - - - - - - - Configure - + + Unique + Unique - - Foreign Key - + + + + + + + + + Configure + Configurar - - Collate - + + Foreign Key + Foreign +Key - - Not NULL - + + Collate + Collate - - Check condition - + + Not NULL + Not NULL - - Primary Key - + + Check condition + Verifique o estado - - Default - + + Primary Key + Primary Key - - Advanced mode - + + Default + Padrão - - Add constraint - column dialog - + + Advanced mode + Modo avançado - - Edit constraint - column dialog - + + Add constraint + column dialog + Adicionar constraint - - - Delete constraint - column dialog - + + Edit constraint + column dialog + Editar constraint - - Move constraint up - column dialog - + + + Delete constraint + column dialog + Excluir constraint - - Move constraint down - column dialog - + + Move constraint up + column dialog + Mover constraint para cima - - Add a primary key - column dialog - + + Move constraint down + column dialog + Mover constraint para baixo - - Add a foreign key - column dialog - + + Add a primary key + column dialog + Adicionar uma chave primária - - Add an unique constraint - column dialog - + + Add a foreign key + column dialog + Adicionar uma chave estrangeira - - Add a check constraint - column dialog - + + Add an unique constraint + column dialog + Adicionar unique constraint - - Add a not null constraint - column dialog - + + Add a check constraint + column dialog + Adicionar uma check constraint - - Add a collate constraint - column dialog - + + Add a not null constraint + column dialog + Adicionar uma not null constraint - - Add a default constraint - column dialog - + + Add a collate constraint + column dialog + Adicionar uma collate constraint - - Are you sure you want to delete constraint '%1'? - column dialog - + + Add a generated value constraint + column dialog + Adicionar constraint de valor gerado - - Correct the constraint's configuration. - + + Add a default constraint + column dialog + Adicionar uma constraint padrão - - This constraint is not officially supported by SQLite 2, -but it's okay to use it. - + + Are you sure you want to delete constraint '%1'? + column dialog + Tem certeza que deseja excluir a constraint '%1'? - - Scale is not allowed for INTEGER PRIMARY KEY columns. - + + Correct the constraint's configuration. + Corrija a configuração da constraint - - Precision cannot be defined without the scale. - + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Escala não é permitida para colunas INTEGER PRIMARY KEY. - - Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - + + Precision cannot be defined without the scale. + A precisão não pode ser definida sem a escala. - - INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Não é possível usar outro tipo que não seja INTEGER se AUTOINCREMENT estiver habilitado no PRIMÃRIO KEY. - - Precision is not allowed for INTEGER PRIMARY KEY columns. - + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + O tipo INTEGER foi imposto devido à ativação do AUTOINCREMENT na PRIMARY KEY. - - + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precisão não é permitida para colunas INTEGER PRIMARY KEY. + + + + Could not match valid STRICT table datatype from declared type: %1. + Não foi possível corresponder a datatype de tabela STRICT válido do tipo declarado: %1. + + + ColumnDialogConstraintsModel - - Type - column dialog constraints - + + Type + column dialog constraints + Tipo - - Name - column dialog constraints - + + Name + column dialog constraints + Nome - - Details - column dialog constraints - + + Details + column dialog constraints + Detalhes - - + + ColumnForeignKeyPanel - - Foreign table: - + + Foreign table: + Tabela externa: - - Foreign column: - + + Foreign column: + Coluna estrangeira: - - Reactions - + + Reactions + Reações - - Deferred foreign key - + + Deferred foreign key + Chave estrangeira definida - - Named constraint - + + Named constraint + Constraint nomeada - - Constraint name - + + Constraint name + Nome da constraint - - Pick the foreign table. - + + Pick the foreign table. + Pegue a tabela externa. - - Pick the foreign column. - + + Pick the foreign column. + Selecione a coluna estrangeira. - - Enter a name of the constraint. - + + Enter a name of the constraint. + Digite um nome da constraint. - - - ColumnPrimaryKeyPanel + + + ColumnGeneratedPanel - - Autoincrement - + + Generating code: + Gerando código: - - Sort order: - + + Explicit type: + Tipo explícito: - - Named constraint: - + + Use "GENERATED ALWAYS" keywords + Usar palavras-chave "GENERATED ALWAYS" - - On conflict: - + + Named constraint: + Restrição nomeada: - - Enter a name of the constraint. - + + Enter the column value generating expression. + Informe o valor da coluna que gera expressão. - - - ColumnUniqueAndNotNullPanel - - Named constraint: - + + Invalid value generating expression: %1. + Valor que gera a expressão inválido: %1. - - On conflict: - + + Invalid value generating expression. + Expressão de geração de valor inválida. - - Enter a name of the constraint. - + + Enter a name of the constraint. + Digite um nome da restrição. - - - CompleterWindow + + + ColumnPrimaryKeyPanel - - Column: %1 - completer statusbar - + + Autoincrement + Auto-incremento - - Table: %1 - completer statusbar - + + Sort order: + Ordem de classificação: - - Index: %1 - completer statusbar - + + Named constraint: + Restrição nomeada: - - Trigger: %1 - completer statusbar - + + On conflict: + Conflito: - - View: %1 - completer statusbar - + + Enter a name of the constraint. + Digite um nome da restrição - - Database: %1 - completer statusbar - + + Descending order is not allowed with AUTOINCREMENT. + A ordem decrescente não é permitida com AUTOINCREMENT. + + + ColumnUniqueAndNotNullPanel - - Keyword: %1 - completer statusbar - + + Named constraint: + Restrição nomeada: - - Function: %1 - completer statusbar - + + On conflict: + Conflito: - - Operator: %1 - completer statusbar - + + Enter a name of the constraint. + Digite um nome da restrição + + + CompleterWindow - - String - completer statusbar - + + Column: %1 + completer statusbar + Coluna: %1 - - Number - completer statusbar - + + Table: %1 + completer statusbar + Tabela: %1 - - Binary data - completer statusbar - + + Index: %1 + completer statusbar + Ãndice: %1 - - Collation: %1 - completer statusbar - + + Trigger: %1 + completer statusbar + Desencadear: %1 - - Pragma function: %1 - completer statusbar - + + View: %1 + completer statusbar + Visualizar: %1 - - - ConfigDialog - - - Configuration - + + Database: %1 + completer statusbar + Banco de dados: %1 - - Search - + + Keyword: %1 + completer statusbar + Tecla: %1 - - General - + + Function: %1 + completer statusbar + Função: %1 - - Keyboard shortcuts - + + Operator: %1 + completer statusbar + Operador: %1 - - Look & feel - + + String + completer statusbar + String - - Style - + + Number + completer statusbar + Número - - Fonts - + + Binary data + completer statusbar + Dados binários - - Colors - + + Collation: %1 + completer statusbar + Agrupamento: %1 - - Plugins - + + Pragma function: %1 + completer statusbar + Pragma função: %1 - - Code formatters - + + Insert a code snippet + Inserir trecho de código + + + ConfigDialog - - Data browsing - + + + Configuration + Configuração - - Data editors - + + Search + Pesquisa - - Database dialog window - + + General + Geral - - <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> - + + Keyboard shortcuts + Atalhos de teclado - - Do not mark database to be "permanent" by default - + + Look & feel + Aparência e funcionamento - - <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> - + + Style + Estilo - - Try to bypass dialog completly when dropping database file onto the list - + + Fonts + Fontes - - Data browsing and editing - + + Code colors + Cores de código - - Number of data rows per page: - + + + Database list + Lista de banco de dados - - - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - + + Code assistant + Assistente de código - - Limit initial data column width to (in pixels): - + + Data browsing + Navegação de dados - - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> - + + Data editors + Editores de dados - - Show column and row details tooltip in data view - + + Plugins + Complementos - - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - + + Code formatters + Formatadores de código - - Inserting new row in data grid - + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + Se desativado, então as colunas serão classificadas na ordem em que são digitadas na instrução CREATE TABLE. - - Before currently selected row - + + Sort table columns alphabetically + Ordenar colunas das tabelas alfabeticamente - - After currently selected row - + + Expand tables node when connected to a database + Expandir tabelas nó quando conectado a um banco de dados - - At the end of data view - + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Rótulos adicionais são exibidos ao lado dos nomes na lista de bancos de dados (eles são azuis, a menos que configurados de outra forma). Ativar esta opção resultará em rótulos para bancos de dados, bancos de dados inválidos e nós agregados (grupo de colunas, grupo de indexações, grupo de ativação). Para mais rótulos veja as opções abaixo.<p> - - <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - + + Display additional labels on the list + Exibir rótulos adicionais na lista - - <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + Para tabelas regulares, os rótulos mostrarão o número de colunas, índices e ativadores para cada tabela. - - Place data tab as first tab in a Table Window - + + Display labels for regular tables + Exibir rótulos para tabelas regulares - - <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - + + Virtual tables will be marked with a 'virtual' label. + Tabelas virtuais serão marcadas com um 'virtual' rótulo. - - <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - + + Display labels for virtual tables + Exibir rótulos para tabelas virtuais - - Place data tab as first tab in a View Window - + + Expand views node when connected to a database + Expandir visualizações enquanto conectado a um banco de dados - - Data types - + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + Se esta opção estiver desligada, os objetos serão classificados na ordem em que aparecem na tabela mestre sqlite_master (que é em ordem que foram criados) - - Available editors: - + + Sort objects (tables, indexes, triggers and views) alphabetically + Ordenar objetos (tabelas, índices, gatilhos e visualizações) em ordem alfabética - - Editors selected for this data type: - + + Display system tables and indexes on the list + Exibir tabelas e índices do sistema em lista - - Schema editing - + + Database dialog window + Janela de diálogo do banco de dados - - Number of DDL changes kept in history. - + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>Ao adicionar um novo banco de dados, está marcado como "permanente" (armazenado na configuração) por padrão. Marcar esta opção faz com que cada novo banco de dados para NÃO ser "permanente" por padrão.</p> - - DDL history size: - + + Do not mark database to be "permanent" by default + Não marcar banco de dados para ser "permanente" por padrão - - SQL queries - + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>Quando esta opção está habilitada, em seguida, os arquivos descartados do gerenciador de arquivos na lista de banco de dados serão automaticamente adicionados à lista, ignorando o diálogo padrão de banco de dados. Se por várias razões o processo automático falhar, então a caixa de diálogo padrão será apresentada ao usuário.</p> - - - Number of queries kept in the history. - + + Try to bypass dialog completly when dropping database file onto the list + Tente ignorar completamente a caixa de diálogo ao soltar o arquivo de banco de dados na lista - - History size: - + + Data browsing and editing + Navegação e edição de dados - - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> - + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>O número máximo de configurações da caixa de diálogo Populate Table armazenada na configuração. O valor de 100 deve ser suficiente.</p> - - Execute only the query under the cursor - + + Number of memorized table populating configurations + Número de tabelas memorizadas que preenchem configurações - - Updates - + + Data column width + Largura da coluna de dados - - Automatically check for updates at startup - + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>Quando o usuário insere novo valor na coluna e o valor é maior do que a largura da coluna atual, o aplicativo irá ampliar a coluna para caber no novo valor, mas não mais do que o limite definido na opção acima.</p></body></html> - - Session - + + Enlarge column when entering value longer than current width + Ampliar a coluna ao inserir um valor maior que a largura atual - - Restore last session (active MDI windows) after startup - + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>Quando os dados são lidos na exibição das colunas de grade a largura é automaticamente ajustada. Este valor limita a largura inicial para o ajuste, mas o usuário ainda pode redimensionar a coluna manualmente acima deste limite.</p> - - Status Field - + + Number of data rows per page: + Número de linhas de dados por página: - - <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> - + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>Quando isso está habilitado e o usuário mantém o ponteiro do mouse sobre uma célula em qualquer exibição de dados (resultados de consulta, dados de uma tabela, uma visualização de dados), uma dica será exibida com detalhes sobre a célula - ela inclui detalhes como o tipo de dados da coluna, restrições, ROWID e outros.</p> - - Always open Status panel when new message is printed - + + Show column and row details tooltip in data view + Mostrar dica de detalhes da coluna e linha na visualização de dados - - Filter shortcuts by name or key combination - + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>Ao editar uma célula que usava como novo valor NULL e a entrada de seqüência de caracteres vazios, então esta opção determina se o novo valor deve permanecer NULO (tenha esta opção ativada), ou deve ser substituído pelo valor vazio da string (tenha esta opção desativada).</p> - - Action - + + Keep NULL value when entering empty value + Manter valor NULL ao inserir um valor vazio - - Key combination - + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Habilite isso para sempre aplicar o valor DEFAULT ao confirmar um valor NULL para uma coluna que tenha o valor padrão definido, embora a coluna possa conter valores NULL.</p><p>Desativar esta opção para usar valor PADRÃO exclusivamente quando o valor NULL é comprometido para a coluna com restrição NÃO NULL.</p></body></html> - - - Language - + + Use DEFAULT value (if defined), when committing NULL value + Usar valor PADRÃO (se definido), ao confirmar o valor NULL - - Changing language requires application restart to take effect. - + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>Se consultas de resultados contêm dezenas (ou centenas) de colunas, é mais provável que ele esgote a memória livre do seu computador carregando vários gigabytes de dados de uma só vez. SQLiteStudio pode tentar limitar o número de resultados exibidos em uma página para proteger o seu computador. Se você sabe que você don't trabalha com grandes valores no banco de dados, você pode desativar esse limite e sempre verá quantas linhas forem definidas por página.</p></body></html> - - Compact layout - + + Limit number of rows for in case of dozens of columns + Limitar o número de linhas no caso de dezenas de colunas - - <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - + + Inserting new row in data grid + Inserindo nova linha na grade de dados - - Use compact layout - + + Before currently selected row + Antes da linha selecionada - - - Database list - + + After currently selected row + Depois da linha selecionada - - If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. - + + At the end of data view + No final da visualização de dados - - Sort table columns alphabetically - + + Table windows + Janelas da tabela - - Expand tables node when connected to a database - + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>Quando ativado, as janelas de tabela serão exibidas na aba de dados, em vez da aba estrutura.</p> - - <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> - + + Open Table Windows with the data tab for start + Janelas em tabela aberta com a guia de dados para iniciar - - Display additional labels on the list - + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>Quando ativado a guia "Dados" será colocada como a primeira guia em cada janela de tabela, em vez de estar no segundo lugar.</p> - - For regular tables labels will show number of columns, indexes and triggers for each of tables. - + + Place data tab as first tab in a Table Window + Colocar guia de dados como primeira aba em uma janela de tabela - - Display labels for regular tables - + + View windows + Ver janelas - - Virtual tables will be marked with a 'virtual' label. - + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>Quando ativado, exibir janelas irá aparecer na aba de dados, em vez da aba estrutura.</p> - - Display labels for virtual tables - + + Open View Windows with the data tab for start + Abra a visualização de janelas com a guia de dados para iniciar - - Expand views node when connected to a database - + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>Quando ativado a guia "Dados" será colocada como a primeira guia em cada exibição de janela, em vez de ser no segundo lugar.</p> - - If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) - + + Place data tab as first tab in a View Window + Colocar guia de dados como primeira aba em uma janela de visualização - - Sort objects (tables, indexes, triggers and views) alphabetically - + + Data types + Tipo de dados - - Display system tables and indexes on the list - + + Available editors: + Editores disponíveis: - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - + + Editors selected for this data type: + Editores selecionados para este tipo de dado: - - Number of memorized table populating configurations - + + Schema editing + Edição do schema - - Keep NULL value when entering empty value - + + Number of DDL changes kept in history. + Número de alterações DDL mantidas no histórico. - - <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> - + + DDL history size: + Tamanho do histórico DDL: - - Use DEFAULT value (if defined), when committing NULL value - + + Don't show DDL preview dialog when committing schema changes + Não mostrar a janela de pré-visualização do DDL ao confirmar alterações de esquema - - Table windows - + + SQL queries + Consultas SQL - - Open Table Windows with the data tab for start - + + + Number of queries kept in the history. + Número de consultas mantidas no histórico. - - View windows - + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>Se houver mais de uma consulta na janela do editor de SQL, então (se essa opção estiver ativada) apenas uma consulta será executada - a que está sob o cursor de inserção do teclado. Caso contrário, todas as consultas serão executadas. Você pode limitar as consultas a serem executadas selecionando essas consultas antes de executar uma chamada. Você também pode usar atalhos dedicados para executar em um modo ou outro (atualmente configurado para %1 para única consulta de execução e %2 para todas as consultas).</p></body></html> - - Open View Windows with the data tab for start - + + History size: + Tamanho do histórico: - - Don't show DDL preview dialog when committing schema changes - + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Número máximo de parâmetros de consulta (:param, @param, $param, ?) armazenado na história. Quando você usar novamente o parâmetro com o mesmo nome/posição, SQLiteStudio irá pré-inicializá-lo com o valor mais recente memorizado (você ainda poderá alterá-lo). O valor de 1000 deve ser suficiente.</p> - - - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - + + Execute only the query under the cursor + Executar a consulta em que o cursor está posicionado - - Number of memorized query parameters - + + Number of memorized query parameters + Número de parâmetros de consulta memorizados - - Main window dock areas - + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> - - Left and right areas occupy corners - + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view - - Top and bottom areas occupy corners - + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>Quando os dados são lidos na largura de exibição de colunas da grade é automaticamente ajustada. Este valor limita a largura inicial para o ajuste, mas o usuário ainda pode redimensionar a coluna manualmente acima deste limite. Esse valor também é usado quando se amplia a coluna para um novo valor mais longo inserido pelo usuário (veja a opção abaixo).</p></body></html> - - Hide built-in plugins - + + Limit automatic data column width to (in pixels): + Limitar a largura da coluna de dados inicial em (em pixels): - - Current style: - + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>A largura inicial das colunas de dados será definida para pelo menos mostrar o nome completo da coluna no cabeçalho. Isso ainda pode ser substituído pelo limite inicial de largura de coluna especificada em pixels (configuração acima).</p></body></html> - - Preview - + + Keep at least the width to show complete column name + Mantenha pelo menos a largura para mostrar o nome completo da coluna - - Enabled - + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>Se ativado, linhas maiores que a largura do editor serão encapsuladas, então a rolagem horizontal não será necessária.</p></body></html> - - Disabled - + + Wrap lines in SQL editor + Quebrar linhas no editor SQL - - Active formatter plugin - + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Destaca a consulta inteira que está atualmente sob o cursor de inserção.'a mesma consulta que será executada quando você clicar em &quot;Executar consulta&quot; atalho ou botão (a menos que configurado de outra forma).</p></body></html> - - SQL editor font - + + Highlight current query + Destacar consulta atual - - Database list font - + + Updates + Atualizações - - Database list additional label font - + + Automatically check for updates at startup + Verificar atualizações automaticamente ao iniciar - - Data view font - + + Session + Sessão - - Status field font - + + Restore last session (active MDI windows) after startup + Restaurar última sessão (janelas MDI ativas) após a inicialização - - SQL editor colors - + + Allow multiple instances of the application at the same time + Permitir várias instâncias do aplicativo ao mesmo tempo - - Current line background - + + Status Field + Campo Status - - <p>SQL strings are enclosed with single quote characters.</p> - + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + Quando o usuário fecha manualmente o painel de Status, esta opção garante que, se alguma nova mensagem for impressa no painel de Status, ela será reaberta. Se desativado, o painel de status só pode ser aberto manualmente pelo usuário a partir do menu - - String foreground - + + Always open Status panel when new message is printed + Sempre abrir painel de Status quando uma nova mensagem é impressa - - <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - + + Code syntax colors + Cores de sintaxe - - Bind parameter foreground - + + Keyword foreground + Palavra-chave em primeiro plano - - Highlighted parenthesis background - + + Regular foreground + Normal primeiro plano - - <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - + + String foreground + Iniciar em primeiro plano - - BLOB value foreground - + + Comment foreground + Comentar primeiro plano - - Regular foreground - + + Valid objects foreground + Objetos válidos em primeiro plano - - Line numbers area background - + + Current query background + Fundo da consulta atual - - Keyword foreground - + + Bind parameter foreground + Vincular parâmetro em primeiro plano - - Number foreground - + + Current line background + Fundo da linha atual - - Comment foreground - + + Matched parenthesis background + Fundo de parênteses correspondente - - <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> - + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>É possível desativar o destaque das consultas inteiramente na página de configurações gerais.</p></body></html> - - Valid objects foreground - + + Number foreground + Número primeiro plano - - Data view colors - + + BLOB value foreground + Valor BLOB em primeiro plano - - <p>Any data changes will be outlined with this color, until they're committed to the database.</p> - + + Matched parenthesis foreground + Primeiro plano de parênteses correspondentes - - Uncommitted data outline color - + + Reset to defaults + Redefinir padrões - - <p>In case of error while committing data changes, the problematic cell will be outlined with this color.</p> - + + Filter shortcuts by name or key combination + Filtrar atalhos por nome ou combinação de teclas - - Commit error outline color - + + Action + Acão - - NULL value foreground - + + Key combination + Combinação de teclas - - Deleted row background - + + + Language + Idioma - - Database list colors - + + Changing language requires application restart to take effect. + Para mudar o idioma é necessário reiniciar o aplicativo para ter efeito. - - <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> - + + Compact layout + Layout compacto - - Additional labels foreground - + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>O layout compacto reduz ao mínimo todas as margens e espaços na interface do usuário, deixando espaço para exibir mais dados. A interface fica um pouco menos estética, mas permite exibir mais dados de uma só vez.</p> - - Status field colors - + + Use compact layout + Usar layout compacto - - Information message foreground - + + Main window dock areas + Ãreas de encaixe da janela principal - - Warning message foreground - + + Left and right areas occupy corners + Ãreas esquerda e direita ocupam cantos - - Error message foreground - + + Top and bottom areas occupy corners + Ãreas superior e inferior ocupam cantos - - Description: - plugin details - + + Hide built-in plugins + Ocultar plugins integrados - - Category: - plugin details - + + Current style: + Estilo atual: - - Version: - plugin details - + + Preview + Pré-visualizar - - Author: - plugin details - + + Enabled + Ativado - - Internal name: - plugin details - + + Disabled + Desabilitado - - Dependencies: - plugin details - + + Active formatter plugin + Plugin formatador ativo - - Conflicts: - plugin details - + + SQL editor font + Fonte do editor SQL - - Plugin details - + + Database list font + Fonte da lista do banco de dados - - Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. - + + Database list additional label font + Fonte de rótulo adicional da lista de banco de dados - - %1 (built-in) - plugins manager in configuration dialog - + + Data view font + Fonte para visualização de dados - - Details - + + Status field font + Fonte do campo status - - No plugins in this category. - + + Code assistant settings + Configurações code assistente - - Add new data type - + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>Se esta opção estiver habilitada, o assistente de código será acionado nos casos, quando tipos de usuário, por exemplo <span style=" font-weight:700;">tableName.</span> para propor colunas da tabela. Se a opção estiver desativada, o usuário terá de apertar explicitamente a chave de atalho assistente.</p></body></html> - - Rename selected data type - + + Automatically trigger the assistant after a dot is typed after an object name + Acionar automaticamente o assistente após um ponto ser digitado após um nome de objeto - - Delete selected data type - + + Description: + plugin details + Descrição: - - Help for configuring data type editors - + + Category: + plugin details + Categoria: - - - ConstraintCheckPanel - - The condition - + + Version: + plugin details + Versão: - - Named constraint: - + + Author: + plugin details + Autor: - - On conflict - + + Internal name: + plugin details + Nome interno: - - Enter a valid condition. - + + Dependencies: + plugin details + Dependências: - - Enter a name of the constraint. - + + Conflicts: + plugin details + Conflitos - - - ConstraintDialog - - New constraint - constraint dialog - + + Plugin details + Detalhes do plugin - - Create - constraint dialog - + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins são carregados/descarregados imediatamente quando marcados/desmarcados, mas a lista de plugins modificados para carregar na inicialização não é salva até que você confirme toda a caixa de diálogo de configuração. - - Edit constraint - dialog window - + + %1 (built-in) + plugins manager in configuration dialog + %1 (embutido) - - Apply - constraint dialog - + + Details + Detalhes - - Primary key - table constraints - + + No plugins in this category. + Sem plugins nesta categoria. - - Foreign key - table constraints - + + Add new data type + Adicionar novo tipo de dado - - Unique - table constraints - + + Rename selected data type + Renomear tipo de dados selecionados - - Not NULL - table constraints - + + Delete selected data type + Excluir tipo de dado selecionado - - Check - table constraints - + + Help for configuring data type editors + Ajuda para configurar editores de tipos de dados - - Collate - table constraints - + + Clear hotkey for this action + Clear hotkey for this action - - Default - table constraints - + + Restore original hotkey for this action + Restore original hotkey for this action - - - ConstraintTabModel - - Table - table constraints - + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + ConstraintCheckPanel - - Column (%1) - table constraints - + + The condition + A condição - - Scope - table constraints - + + Named constraint: + Constraint nomeada: - - Type - table constraints - + + On conflict + Em conflito - - Details - table constraints - + + Enter a valid condition. + Informe uma condição válida. - - Name - table constraints - + + Enter a name of the constraint. + Digite um nome da constraint. - - - CssDebugDialog + + + ConstraintDialog - - SQLiteStudio CSS console - + + New constraint + constraint dialog + Nova constraint - - - DataView - - Filter data - data view - + + Create + constraint dialog + Criar - - Grid view - + + Edit constraint + dialog window + Editar constraint - - Form view - + + Apply + constraint dialog + Aplicar - - Refresh table data - data view - + + Primary key + table constraints + Chave primária - - First page - data view - + + Foreign key + table constraints + Foreign Key - - Previous page - data view - + + Unique + table constraints + Unique - - Next page - data view - + + Not NULL + table constraints + Not NULL - - Last page - data view - + + Check + table constraints + Check - - Filter - + + Generated + table constraints + Generated - - Hit Enter key or press "Apply filter" button on toolbar to apply new value. - + + Collate + table constraints + Collate - - Show filter inputs per column - data view - + + Default + table constraints + Padrão + + + ConstraintTabModel - - Apply filter - data view - + + Table + table constraints + Tabela - - Commit changes for selected cells - data view - + + Column (%1) + table constraints + Coluna (%1) - - Rollback changes for selected cells - data view - + + Scope + table constraints + Escopo - - Show grid view of results - sql editor - + + Type + table constraints + Tipo - - Show form view of results - sql editor - + + Details + table constraints + Detalhes - - Filter by text - data view - + + Name + table constraints + Nome + + + CssDebugDialog - - Filter by the Regular Expression - data view - + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + DataView - - Filter by SQL expression - data view - + + Filter data + data view + Filtrar dados - - Tabs on top - data view - + + Grid view + Exibição em grade - - Tabs at bottom - data view - + + Form view + Visualização do formulário - - Place new rows above selected row - data view - + + Refresh table data + data view + Atualizar dados das tabelas - - Place new rows below selected row - data view - + + First page + data view + Primeira página - - Place new rows at the end of the data view - data view - + + Previous page + data view + Página anterior - - Total number of rows is being counted. -Browsing other pages will be possible after the row counting is done. - + + Next page + data view + Próxima página - - Row: %1 - + + Last page + data view + Última página - - - DbConverterDialog - - Convert database - + + Commit changes for selected cells + data view + Enviar mudanças para as células selecionadas - - Source database - + + Rollback changes for selected cells + data view + Reverter mudanças para as células selecionadas - - Source database version: - + + Show grid view of results + data view + Mostrar exibição de grid de resultados - - Target database - + + Show form view of results + data view + Mostrar formulário de visualização de resultados - - Target version: - + + Filter by text (if contains) + data view + Filtrar por texto (se conter) - - This is the file that will be created as a result of the conversion. - + + Filter strictly by text (if equals) + data view + Filtrar estritamente por texto (se igual) - - Target file: - + + Tabs on top + data view + Abas no topo - - Name of the new database: - + + Tabs at bottom + data view + Abas na parte inferior - - This is the name that the converted database will be added to SQLiteStudio with. - + + Place new rows above selected row + data view + Colocar novas linhas acima da linha selecionada - - Select source database - + + Place new rows below selected row + data view + Colocar novas linhas abaixo da linha selecionada - - Enter valid and writable file path. - + + Place new rows at the end of the data view + data view + Coloque novas linhas no final da exibição de dados - - Entered file exists and will be overwritten. - + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + O número total de linhas está sendo contado. +Navegar por outras páginas será possível após a contagem de linhas ser finalizada. - - Enter a not empty, unique name (as in the list of databases on the left). - + + Row: %1 + Linha: %1 - - No valid target dialect available. Conversion not possible. - + + Filter + Filtro - - Select valid target dialect. - + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Pressione a tecla Enter ou pressione "Aplicar filtro" botão na barra de ferramentas para aplicar o novo valor. - - Database %1 has been successfully converted and now is available under new name: %2 - + + Filter by the Regular Expression + data view + Filtrar pela Expressão Regular - - SQL statements conversion - + + Filter by SQL expression + data view + Filtrar por expressão SQL - - Following error occurred while converting SQL statements to the target SQLite version: - + + Show filter inputs per column + data view + Mostrar entradas de filtro por coluna - - Would you like to ignore those errors and proceed? - + + Apply filter + data view + Aplicar filtro - - + + DbDialog - - Database - + + Database + Banco de dados + + + + Database type + Tipo de banco de dados - - Database type - + + Database driver + Driver de banco de dados - - Database driver - + + + File + Arquivo - - Options - + + Name (on the list) + Nome (na lista) - - Permanent (keep it in configuration) - + + Options + Opções - - Test connection - + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Ative isso se quiser que o banco de dados seja armazenado no arquivo de configuração e restaurado toda vez que o SQLiteStudio é iniciado.</p> - - Create new database file - + + Permanent (keep it in configuration) + Permanente (mantenha na configuração) - - - File - + + Test connection + Testar conexão - - Name (on the list) - + + Select new or existing file on local computer + Selecionar arquivo novo ou existente no computador local - - <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> - aasfd - + + Browse + Navegar - - Browse for existing database file on local computer - + + Database type not selected. + Tipo de banco de dados não selecionado. - - Browse - + + Database path not specified. + Caminho da base de dados não especificado. - - Enter an unique database name. - + + Enter an unique database name. + Informe um nome único para o banco de dados. - - This name is already in use. Please enter unique name. - + + This name is already in use. Please enter unique name. + Este nome já está em uso. Por favor, insira um nome único. - - <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>A geração automática de nome foi desabilitada, porque o nome foi editado manualmente. Para restaurar a geração automática, apague o conteúdo do campo nome.</p> - - Enter a database file path. - + + Enter a database file path. + Digite o caminho do arquivo de banco de dados. - - This database is already on the list under name: %1 - + + This database is already on the list under name: %1 + Este banco de dados já está na lista sob o nome: %1 - - Select a database type. - + + Select a database type. + Selecione um tipo de banco de dados. - - + + DbObjectDialogs - - Delete table - + + Delete table + Apagar a tabela - - Are you sure you want to delete table %1? - + + Are you sure you want to delete table %1? + Tem certeza que deseja apagar a tabela %1? - - Delete index - + + Delete index + Excluir índice - - Are you sure you want to delete index %1? - + + Are you sure you want to delete index %1? + Tem certeza que deseja excluir o índice %1? - - Delete trigger - + + Delete trigger + Excluir trigger - - Are you sure you want to delete trigger %1? - + + Are you sure you want to delete trigger %1? + Deseja mesmo excluir trigger %1? - - Delete view - + + Delete view + Excluir visualização - - Are you sure you want to delete view %1? - + + Are you sure you want to delete view %1? + Tem certeza que deseja excluir a visualização %1? - - - Error while dropping %1: %2 - + + + Error while dropping %1: %2 + Erro ao derrubar %1: %2 - - Delete objects - + + Delete objects + Excluir objetos - - Are you sure you want to delete following objects: + + Are you sure you want to delete following objects: %1 - + Tem certeza que deseja excluir os seguintes objetos: +%1 - - Cannot start transaction. Details: %1 - + + Cannot start transaction. Details: %1 + Não é possível iniciar a transação. Detalhes: %1 - - Cannot commit transaction. Details: %1 - + + Cannot commit transaction. Details: %1 + Não é possível realizar a transação. Detalhes: %1 - - + + DbTree - - Databases - + + Databases + Banco de dados - - Filter by name - + + Filter by name + Filtrar por nome - - Copy - + + Copy + Copiar - - Paste - + + Paste + Colar - - Select all - + + Select all + Selecionar tudo - - Create a group - + + Create a group + Criar um grupo - - Delete the group - + + Delete the group + Excluir grupo - - Rename the group - + + Rename the group + Renomear o grupo - - Import - + + &Add a database + &Adicionar um banco de dados - - Export the table - + + &Edit the database + &Editar o banco de dados - - Import into the table - + + &Remove the database + &Remover o banco de dados - - Populate table - + + &Connect to the database + &Conectar ao banco de dados - - Create similar table - + + &Disconnect from the database + &Desconectar do banco de dados - - Reset autoincrement sequence - + + Import + Importar - - Add a column - + + &Export the database + &Exportar banco de dados - - Edit the column - + + Vac&uum + Vacuum - - Delete the column - + + &Integrity check + Verificar Integridade - - Delete selected items - + + Create a &table + Criar uma tabela - - Clear filter - + + Edit the t&able + Edita uma tabela - - - Erase table data - + + Delete the ta&ble + Apagar a tabela - - - Database - + + Export the table + Exportar a tabela - - Grouping - + + Import into the table + Importar para a tabela - - Generate query for table - + + Populate table + Preencher a tabela - - - Create group - + + Create similar table + Criar tabela semelhante - - Group name - + + Reset autoincrement sequence + Redefinir sequência de auto-incremento - - Entry with name %1 already exists in group %2. - + + Create an &index + Criar um índice - - Delete group - + + Edit the i&ndex + Editar o índice - - Are you sure you want to delete group %1? -All objects from this group will be moved to parent group. - + + Delete the in&dex + Apagar o índice - - Are you sure you want to remove database '%1' from the list? - + + Create a trig&ger + Criar um gatilho - - Are you sure you want to remove following databases from the list: -%1 - + + Edit the trigg&er + Editar gatilho - - Remove database - + + Delete the trigge&r + Excluir gatilho - - Vacuum (%1) - + + Create a &view + Criar uma visualização - - Autoincrement value for table '%1' has been reset successfully. - + + Edit the v&iew + Editar uma visualização - - Are you sure you want to delete all data from table(s): %1? - + + Delete the vi&ew + Apagar uma visualização - - - Cannot import, because no import plugin is loaded. - + + Add a column + Adicionar uma coluna - - Execution from file cancelled. Any queries executed so far have been rolled back. - + + Edit the column + Editar a coluna - - &Add a database - + + Delete the column + Excluir a coluna - - &Edit the database - + + Delete selected items + Excluir itens selecionados - - &Remove the database - + + Clear filter + Limpar filtro - - &Connect to the database - + + &Refresh all database schemas + &Atualizar todos os esquemas de banco de dados - - &Disconnect from the database - + + Re&fresh selected database schema + Atualizar &esquema de banco de dados selecionado - - &Export the database - + + + Erase table data + Apagar dados da tabela - - Con&vert database type - + + Open file's directory + Abrir arquivos diretório - - Vac&uum - + + Execute SQL from file + Executar SQL de um arquivo - - &Integrity check - + + Increase font size + database list + Aumentar o tamanho da fonte - - Create a &table - + + Decrease font size + database list + Diminuir o tamanho da fonte - - Edit the t&able - + + + Database + Base de dados - - Delete the ta&ble - + + Grouping + Agrupamento - - Create an &index - + + Generate query for table + Gerar consulta para tabela - - Edit the i&ndex - + + + Create group + Criar grupo - - Delete the in&dex - + + Group name + Nome do grupo - - Create a trig&ger - + + Entry with name %1 already exists in group %2. + A referência com o nome %1 já existe no grupo %2. - - Edit the trigg&er - + + Delete group + Apagar grupo - - Delete the trigge&r - + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Tem certeza que deseja excluir o grupo %1? +Todos os objetos deste grupo serão movidos para o grupo pai. - - Create a &view - + + Are you sure you want to remove database '%1' from the list? + Tem certeza de que deseja remover o banco de dados da lista? - - Edit the v&iew - + + Are you sure you want to remove following databases from the list: +%1 + Tem certeza de que deseja remover os seguintes bancos de dados da lista: +%1 - - Delete the vi&ew - + + Remove database + Apagar banco de dados - - &Refresh all database schemas - + + + Cannot import, because no import plugin is loaded. + Não é possível importar porque nenhum plugin de importação está carregado. - - Re&fresh selected database schema - + + + Cannot export, because no export plugin is loaded. + Não é possível exportar, porque nenhum plugin de exportação está carregado. - - Open file's directory - + + Vacuum (%1) + Vacuum (%1) - - Execute SQL from file - + + Integrity check (%1) + Verificação de integridade (%1) - - - Cannot export, because no export plugin is loaded. - + + Reset autoincrement + Redefinir auto-incremento - - Integrity check (%1) - + + Are you sure you want to reset autoincrement value for table '%1'? + Tem certeza de que deseja redefinir o valor de auto-incremento para a tabela? - - Reset autoincrement - + + An error occurred while trying to reset autoincrement value for table '%1': %2 + Ocorreu um erro ao tentar redefinir o valor de auto-incremento da tabela: %2 - - Are you sure you want to reset autoincrement value for table '%1'? - + + Autoincrement value for table '%1' has been reset successfully. + O valor do auto-incremento para a tabela foi redefinido com sucesso. - - An error occurred while trying to reset autoincrement value for table '%1': %2 - + + Are you sure you want to delete all data from table(s): %1? + Tem certeza que deseja excluir todos os dados da tabela: %1? - - An error occurred while trying to delete data from table '%1': %2 - + + An error occurred while trying to delete data from table '%1': %2 + Ocorreu um erro ao tentar excluir os dados da tabela: %2 - - All data has been deleted for table '%1'. - + + All data has been deleted for table '%1'. + Todos os dados da tabela %1 foram apagados. - - Following objects will be deleted: %1. - + + Following objects will be deleted: %1. + Os seguintes objetos serão deletados: %1. - - Following databases will be removed from list: %1. - + + Following databases will be removed from list: %1. + Os seguintes bancos de dados serão removidos da lista: %1. - - Remainig objects from deleted group will be moved in place where the group used to be. - + + Remainig objects from deleted group will be moved in place where the group used to be. + Os objetos restantes do grupo excluído serão movidos no lugar onde o grupo costumava estar. - - %1<br><br>Are you sure you want to continue? - + + %1<br><br>Are you sure you want to continue? + %1<br><br>Você tem certeza que quer continuar? - - Delete objects - + + Delete objects + Excluir objetos + + + DbTreeItemDelegate - - Could not execute SQL, because application has failed to start transaction: %1 - + + error + dbtree labels + erro - - Could not open file '%1' for reading: %2 - + + (system table) + database tree label + (tabela de sistema) - - Could not execute SQL, because application has failed to commit the transaction: %1 - + + (virtual) + virtual table label + (virtual) - - Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - + + (system index) + database tree label + (índice do sistema) + + + DbTreeModel - - Finished executing %1 queries in %2 seconds. - + + Database: %1 + dbtree tooltip + Banco de dados: %1 - - Could not execute SQL due to error. - + + URI: + dbtree tooltip + URI: - - - DbTreeItemDelegate - - error - dbtree labels - + + Version: + dbtree tooltip + Versão: - - (system table) - database tree label - + + File size: + dbtree tooltip + Tamanho do arquivo: - - (virtual) - virtual table label - + + Encoding: + dbtree tooltip + Codificação: - - (system index) - database tree label - + + Error: + dbtree tooltip + Erro: - - - DbTreeModel - - Database: %1 - dbtree tooltip - + + Table : %1 + dbtree tooltip + Tabela: %1 - - Version: - dbtree tooltip - + + Columns (%1): + dbtree tooltip + Colunas (%1): - - File size: - dbtree tooltip - + + Indexes (%1): + dbtree tooltip + Ãndices (%1): - - Encoding: - dbtree tooltip - + + Triggers (%1): + dbtree tooltip + Triggers (%1): - - Error: - dbtree tooltip - + + Copy + Cópia - - Table : %1 - dbtree tooltip - + + Move + Mover - - Columns (%1): - dbtree tooltip - + + Include data + Incluir dados - - Indexes (%1): - dbtree tooltip - + + Include indexes + Incluir índices - - Triggers (%1): - dbtree tooltip - + + Include triggers + Incluir triggers - - Copy - + + Abort + Interromper - - Move - + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Não foi possível adicionar o arquivo de banco de dados '%1' automaticamente. A configuração manual é necessária. - - Include data - + + Referenced tables + Tabelas referenciadas - - Include indexes - + + Do you want to include following referenced tables as well: +%1 + Você também deseja incluir as seguintes tabelas referenciadas: +%1 - - Include triggers - + + Name conflict + Conflito no nome - - Abort - + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Seguindo o objeto já existe no banco de dados de destino. +Digite um nome novo, único ou pressione '%1' para abortar a operação: - - Could not add dropped database file '%1' automatically. Manual setup is necessary. - + + SQL statements conversion + Conversão de comandos SQL - - Referenced tables - + + Following error occurred while converting SQL statements to the target SQLite version: + Ocorreu um erro ao converter as instruções SQL para a versão de SQLite de destino: - - Do you want to include following referenced tables as well: -%1 - + + Would you like to ignore those errors and proceed? + Gostaria de ignorar esses erros e prosseguir? + + + DdlHistoryWindow - - Name conflict - + + Filter by database: + Filtrar por banco de dados: - - Following object already exists in the target database. -Please enter new, unique name, or press '%1' to abort the operation: - + + Clear entire history + Limpar o histórico de navegação - - SQL statements conversion - + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Consultas executadas no banco de dados %1 (%2) +-- Data e horário de execução: %3 +%4 - - Following error occurred while converting SQL statements to the target SQLite version: - + + Clear history + Limpar histórico - - Would you like to ignore those errors and proceed? - + + Are you sure you want to erase entire DDL history? + Tem certeza de que deseja excluir todo o histórico? - - - DdlHistoryWindow - - Filter by database: - + + DDL history + Histórico DDL + + + DdlPreviewDialog - - -- Queries executed on database %1 (%2) --- Date and time of execution: %3 -%4 - + + Queries to be executed + Consultas a serem executadas - - DDL history - + + Don't show again + Não mostrar novamente - - - DdlPreviewDialog + + + DebugConsole - - Queries to be executed - + + SQLiteStudio Debug Console + Console de Debug SQLiteStudio + + + EditorWindow - - Don't show again - + + SQL editor + Editor SQL - - - DebugConsole - - SQLiteStudio Debug Console - + + Query + Consulta - - - EditorWindow - - Query - + + History + Histórico - - History - + + Results in the separate tab + Resultados em uma aba separada - - Results in the separate tab - + + Results below the query + Resultados abaixo da consulta - - Results below the query - + + + SQL editor %1 + Editor SQL %1 - - - SQL editor %1 - + + + Results + Resultados - - Results - + + Execute query + Executar consulta - - Execute query - + + Explain query + Expandir consulta - - Explain query - + + Clear execution history + sql editor + Limpar histórico de execução - - Clear execution history - sql editor - + + Export results + sql editor + Exportar resultados - - Export results - sql editor - + + Create view from query + sql editor + Criar visualização a partir da consulta - - Create view from query - sql editor - + + Previous database + Banco de dados anterior - - Previous database - + + Next database + Próximo banco de dados - - Next database - + + Show next tab + sql editor + Mostrar próxima aba - - Show next tab - sql editor - + + Show previous tab + sql editor + Mostrar aba anterior - - Show previous tab - sql editor - + + Focus results below + sql editor + Focar resultados abaixo - - Focus results below - sql editor - + + Focus SQL editor above + sql editor + Focar resultados acima - - Focus SQL editor above - sql editor - + + Delete selected SQL history entries + sql editor + Excluir as entradas do histórico SQL selecionadas - - Delete selected SQL history entries - sql editor - + + Execute single query under cursor + Executar a consulta em que o cursor está posicionado - - Active database (%1/%2) - + + Execute all queries in editor + Executar todas as consultas no editor - - Query finished in %1 second(s). Rows affected: %2 - + + Active database (%1/%2) + Banco de dados ativo (%1/%2) - - Query finished in %1 second(s). - + + Query finished in %1 second(s). Rows affected: %2 + Consulta finalizada em %1 segundo(s). Linhas afetadas: %2 - - Clear execution history - + + Query finished in %1 second(s). + Consulta finalizada em %1 segundo(s). - - Are you sure you want to erase the entire SQL execution history? This cannot be undone. - + + Clear execution history + Limpar histórico de execução - - Cannot export, because no export plugin is loaded. - + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Tem certeza que deseja apagar todo o histórico de execução de SQL? Isso não pode ser desfeito. - - No database selected in the SQL editor. Cannot create a view for unknown database. - + + Cannot export, because no export plugin is loaded. + Não é possível exportar, porque nenhum plugin de exportação está carregado. - - Editor window "%1" has uncommitted data. - + + No database selected in the SQL editor. Cannot create a view for unknown database. + Nenhum banco de dados selecionado no editor SQL. Não é possível criar uma view para um banco de dados desconhecido. - - + + + Editor window "%1" has uncommitted data. + Janela do editor "%1" possui dados não confirmados. + + + ErrorsConfirmDialog - - Errors - + + Errors + Erros - - Following errors occured: - + + Following errors occured: + Ocorreu o seguinte erro: - - Would you like to proceed? - + + Would you like to proceed? + Deseja continuar? - - + + ExecFromFileDialog - - Execute SQL from file - + + Execute SQL from file + Executar SQL de um arquivo - - Input file - + + Input file + Arquivo de entrada - - Path to file - + + Path to file + Caminho para o arquivo - - Browse for file - + + Browse for file + Procurar por arquivo - - Options - + + Options + Opções - - File encoding - + + File encoding + Codificação de arquivo - - Skip failing SQL statements - + + Skip failing SQL statements + Pular comandos SQL falhados - - SQL scripts (*.sql);;All files (*) - + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) - - Execute SQL file - + + Execute SQL file + Executar o arquivo SQL - - Please provide file to be executed. - + + Please provide file to be executed. + Por favor, forneça um arquivo para ser executado. - - Provided file does not exist or cannot be read. - + + Provided file does not exist or cannot be read. + O arquivo fornecido não existe ou não pode ser lido. - - + + ExportDialog - - Export - + + Export + Exportar - - What do you want to export? - + + What do you want to export? + O que você deseja exportar? - - A database - + + A database + Um banco de dados - - A single table - + + A single table + Uma única tabela - - Query results - + + Query results + Resultados da consulta - - Table to export - + + Table to export + Tabela a exportar - - Database - + + Database + Banco de dados - - Table - + + Table + Tabela - - Options - + + Options + Opções - - When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. - + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + Quando esta opção está desmarcada, então apenas o DDL da tabela (CREATE TABLE) é exportado. - - Export table data - + + Export table data + Exportar dados da tabela - - Export table indexes - + + Export table indexes + Exportar índices de tabelas - - Export table triggers - + + Export table triggers + Exportar gatilhos de tabelas - - Note, that exporting table indexes and triggers may be unsupported by some output formats. - + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Observe que a exportação de índices e gatilhos de tabela podem não ser suportados por alguns formatos de saída. - - Select database objects to export - + + Select database objects to export + Selecionar objetos de banco de dados para exportar - - Export data from tables - + + Export data from tables + Exportar dados das tabelas - - Select all - + + Select all + Selecionar todos - - Deselect all - + + Deselect all + Desmarcar todos - - - Database: - + + + Database: + Banco de dados: - - Query to export results for - + + Query to export results for + Consulta para exportar resultados para - - Query to be executed for results: - + + Query to be executed for results: + Consulta a ser executada para resultados: - - Export format and options - + + Export format and options + Formato e opções de exportação - - Export format - + + Export format + Formato de exportação - - Output - + + Output + Saída - - Exported file path - + + Exported file path + Caminho do arquivo exportado - - Clipboard - + + Clipboard + Ãrea de transferência - - File - + + File + Arquivo - - Exported text encoding: - + + Exported text encoding: + Codificação de texto exportado: - - Export format options - + + Export format options + Formato e opções de exportação - - Cancel - + + Cancel + Cancelar - - - - Select database to export. - + + + + Select database to export. + Selecionar banco de dados para exportar. - - Select table to export. - + + Select table to export. + Selecione a tabela a ser exportada. - - Enter valid query to export. - + + Enter valid query to export. + Digite uma consulta válida para exportar. - - Select at least one object to export. - + + Select at least one object to export. + Selecione pelo menos um objeto para exportar. - - You must provide a file name to export to. - + + You must provide a file name to export to. + Você deve fornecer um nome de arquivo para exportar. - - Path you provided is an existing directory. You cannot overwrite it. - + + Path you provided is an existing directory. You cannot overwrite it. + O caminho fornecido é um diretório existente. Você não pode substituí-lo. - - The directory '%1' does not exist. - + + The directory '%1' does not exist. + O diretório %1 não existe. - - The file '%1' exists and will be overwritten. - + + The file '%1' exists and will be overwritten. + O arquivo %1 existe e será sobrescrito. - - All files (*) - + + All files (*) + Todos os arquivos (*) - - Pick file to export to - + + Pick file to export to + Exportar arquivo para - - Internal error during export. This is a bug. Please report it. - + + Internal error during export. This is a bug. Please report it. + Erro interno durante a exportação. Isto é um bug. Por favor, reporte-o. - - + + FileExecErrorsDialog - - Execution errors - + + Execution errors + Erros de execução + + + + Following errors were encountered during execution of SQL statements from the file: + Os seguintes erros foram encontrados durante a execução das instruções SQL do arquivo: - - Following errors were encountered during execution of SQL statements from the file: - + + SQL + SQL - - SQL - + + Error + Erro - - Error - + + Statements that were executed successfully were commited. + Declarações executadas com sucesso foram enviadas. - - Statements that were executed successfully were commited. - + + Statements that were executed successfully were rolled back. + Declarações executadas com sucesso foram rebaixadas. + + + FkComboBox - - Statements that were executed successfully were rolled back. - + + Cannot edit this cell. Details: %1 + Não é possível editar esta célula. Detalhes: %1 - - + + FontEdit - - Choose font - font configuration - + + Choose font + font configuration + Escolha a fonte - - + + Form - - Active SQL formatter plugin - + + Active SQL formatter plugin + Formatador SQL ativo do plugin - - + + FormView - - Commit row - form view - + + Commit row + form view + Submeter linha - - Rollback row - form view - + + Rollback row + form view + Reverter linha - - First row - form view - + + First row + form view + Primeira linha - - Previous row - form view - + + Previous row + form view + Linha anterior - - Next row - form view - + + Next row + form view + Próxima linha - - Last row - form view - + + Last row + form view + Última linha - - Insert new row - form view - + + Insert new row + form view + Inserir nova linha - - Delete current row - form view - + + Delete current row + form view + Excluir linha atual - - + + FunctionsEditor - - Filter funtions - + + Filter functions + Filter functions - - Function name: - + + Input arguments + Imputar argumentos - - Implementation language: - + + Undefined + Indefinido - - Type: - + + Databases + Banco de dados - - Input arguments - + + Register in all databases + Registrar em todos os bancos de dados - - Undefined - + + Register in following databases: + Registrar nos seguintes bancos de dados: - - Databases - + + Type: + Tipo: - - Register in all databases - + + Function name: + Nome da função: - - Register in following databases: - + + Implementation language: + Idioma de implementação: - - Initialization code: - + + Deterministic + Determinístico - - - Function implementation code: - + + Initialization code: + Código de inicialização: - - Final step implementation code: - + + + Function implementation code: + Código de implementação da função: - - SQL function editor - + + Final step implementation code: + Código de implementação da etapa final: - - Commit all function changes - + + SQL functions editor + Editor de funções SQL - - Rollback all function changes - + + Commit all function changes + Enviar todas as mudanças de função - - Create new function - + + Rollback all function changes + Restaurar todas as alterações de função - - Delete selected function - + + Create new function + Criar nova função - - Custom SQL functions manual - + + Delete selected function + Excluir função selecionada - - Add function argument - + + Custom SQL functions manual + Personalizar funções SQL manualmente - - Rename function argument - + + Add function argument + Adicionar argumento da função - - Delete function argument - + + Rename function argument + Renomear argumento da função - - Move function argument up - + + Delete function argument + Excluir argumento da função - - Move function argument down - + + Move function argument up + Move o argumento da função para cima - - Scalar - + + Move function argument down + Move o argumento da função para baixo - - Aggregate - + + Scalar + Escalar - - Enter a non-empty, unique name of the function. - + + Aggregate + Agregar - - Pick the implementation language. - + + Enter a non-empty, unique name of the function. + Insira um nome único e completo da função. - - Per step code: - + + Pick the implementation language. + Escolha a linguagem de implementação. - - Enter a non-empty implementation code. - + + Per step code: + Código de cada passo: - - argument - new function argument name in function editor window - + + Enter a non-empty implementation code. + Digite um código de implementação não vazio. - - Functions editor window has uncommitted modifications. - + + argument + new function argument name in function editor window + argumento - - + + + Functions editor window has uncommitted modifications. + Janela do editor de funções tem modificações não autorizadas. + + + ImportDialog - - Import data - + + Import data + Importar dados - - Table to import to - + + Table to import to + Importar tabela para - - Table - + + Table + Tabela - - Database - + + Database + Banco de dados - - Data source to import from - + + Data source to import from + Dados para importar - - Data source type - + + Data source type + Tipo de fonte de dados - - Options - + + Options + Opções - - Input file: - + + Text encoding: + Codificação de texto: - - Text encoding: - + + Input file: + Arquivo de entrada: - - <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> - + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>Se ativado, qualquer violação de restrição ou formato de dados inválido (contagem de coluna errada), ou qualquer outro problema encontrado durante a importação será ignorado e a importação será continuada.</p> - - Ignore errors - + + Ignore errors + Ignorar erros - - Data source options - + + Data source options + Opções de fonte de dados - - Cancel - + + Cancel + Cancelar - - If you type table name that doesn't exist, it will be created. - + + If you type table name that doesn't exist, it will be created. + Se você digitar o nome da tabela que não existe, ela será criada. - - Enter the table name - + + Enter the table name + Digite o nome da tabela - - Select import plugin. - + + Select import plugin. + Selecione plugin de importação. - - You must provide a file to import from. - + + You must provide a file to import from. + Você deve fornecer um arquivo para importar. - - The file '%1' does not exist. - + + The file '%1' does not exist. + O arquivo '%1' não existe. - - Path you provided is a directory. A regular file is required. - + + Path you provided is a directory. A regular file is required. + Caminho fornecido é um diretório. Um arquivo regular é necessário. - - Pick file to import from - + + Pick file to import from + Escolher arquivo de onde importar - - + + IndexDialog - - - Index - + + + Index + Ãndice - - On table: - + + Column + Coluna - - Index name: - + + Sort + Ordenar - - Partial index condition - + + Collation + Collation - - Unique index - + + On table: + Na tabela: - - Column - + + Delete selected indexed expression + Excluir expressão indexada selecionada - - Collation - + + Moves selected index column up in the order, making it more significant in the index. + Move a coluna de índice selecionada para cima, tornando-a mais significativa no índice. - - Sort - + + Moves selected index column down in the order, making it less significant in the index. + Move a coluna de índice selecionada para baixo na ordem, tornando-a menos significativa no índice. - - Delete selected indexed expression - + + Partial index condition + Condição do índice parcial - - Moves selected index column up in the order, making it more significant in the index. - + + Unique index + Ãndice exclusivo - - Moves selected index column down in the order, making it less significant in the index. - + + Index name: + Nome do índice: - - Edit selected indexed expression - + + Edit selected indexed expression + Editar a expressão indexada selecionada - - Add indexed expression - + + Add indexed expression + Adicionar expressão indexada - - DDL - + + DDL + DDL - - Tried to open index dialog for closed or inexisting database. - + + Tried to open index dialog for closed or inexisting database. + Tentou abrir diálogo de índice para banco de dados fechado ou inexistente. - - Could not process index %1 correctly. Unable to open an index dialog. - + + Could not process index %1 correctly. Unable to open an index dialog. + Não foi possível processar o índice %1 corretamente. Não foi possível abrir um diálogo de índice. - - Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. - + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Ãndice exclusivo não pode ter expressões indexadas. Remova expressões da lista abaixo, ou desmarque esta opção. - - Pick the table for the index. - + + Pick the table for the index. + Selecione a tabela para o índice. - - Select at least one column. - + + Select at least one column. + Selecione pelo menos uma coluna. - - Enter a valid condition. - + + Enter a valid condition. + Informe uma condição válida. - - default - index dialog - + + default + index dialog + padrão - - Sort order - table constraints - + + Sort order + table constraints + Ordem de classificação - - - Error - index dialog - + + + Error + index dialog + Erro - - Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? - + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Não é possível criar um índice único, porque os valores nas colunas selecionadas não são únicos. Gostaria de executar uma consulta SELECT para ver os valores problemáticos? - - An error occurred while executing SQL statements: + + An error occurred while executing SQL statements: %1 - + Ocorreu um erro ao executar instruções SQL: +%1 - - + + IndexExprColumnDialog - - Indexed expression - + + Indexed expression + Expressão indexada - - Expression to index - + + Expression to index + Expressão para indexar - - This expression is already indexed by the index. - + + This expression is already indexed by the index. + Esta expressão já está indexada pelo índice. - - Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. - + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + A coluna deve ser indexada diretamente, não por expressão. Ou amplie esta expressão para conter algo mais do que apenas o nome da coluna, ou aborte e selecione esta coluna no diálogo de índice diretamente. - - Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. - + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + A coluna '%1' não pertence à tabela coberta por este índice. Expressões indexadas podem se referir apenas a colunas da tabela indexada. - - It's forbidden to use 'SELECT' statements in indexed expressions. - + + It's forbidden to use 'SELECT' statements in indexed expressions. + Proibido usar instruções SELECT em expressões indexadas. - - Enter an indexed expression. - + + Enter an indexed expression. + Informe uma expressão indexada. - - Invalid expression. - + + Invalid expression. + Expressão inválida. - - + + LanguageDialog - - Language - + + Language + Idioma - - Please choose language: - + + Please choose language: + Por favor, escolha o idioma: - - + + MainWindow - - Database toolbar - + + Database toolbar + Barra de ferramentas do banco de dados + + + + Structure toolbar + Barra de ferramentas da estrutura - - Structure toolbar - + + Tools + Ferramentas - - Tools - + + Window list + Lista de janelas - - Window list - + + View toolbar + Visualizar barra de ferramentas - - View toolbar - + + Configuration widgets + Configurar widgets - - Configuration widgets - + + Syntax highlighting engines + Realce do tema da sintaxe - - Syntax highlighting engines - + + Data editors + Editores de dados - - Data editors - + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Executando no modo de depuração. Pressione %1 ou use 'Ajuda / Abrir console de depuração' entrada de menu para abrir o console de depuração. - - Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - + + Running in debug mode. Debug messages are printed to the standard output. + Executando em modo de depuração. Mensagens de depuração são impressas na saída padrão. - - Running in debug mode. Debug messages are printed to the standard output. - + + You need to restart application to make the language change take effect. + Você precisa reiniciar o aplicativo para que a alteração de idioma tenha efeito. - - You need to restart application to make the language change take effect. - + + Open SQL &editor + Abrir editor de SQL - - Next window - + + Open DDL &history + Abrir histórico DDL - - Previous window - + + Open SQL &functions editor + Abrir editor de funções SQL - - Hide status field - + + Open code &snippets editor + Abrir editor de &snippets - - Open Debug Console - + + Open &collations editor + Abrir editor de ordenações - - Open CSS Console - + + Open ex&tension manager + Abrir gerenciador de extensão - - Bugs and feature &requests - + + &Import + Importar - - Window list - menubar view menu - + + E&xport + Exportar - - Open SQL &editor - + + Open confi&guration dialog + Configurações - - Open DDL &history - + + &Tile windows + Dividir janelas - - Open SQL &functions editor - + + Tile windows &horizontally + Dividir janela horizontalmente - - Open &collations editor - + + Tile windows &vertically + Dividir janela verticalmente - - Open ex&tension manager - + + &Cascade windows + Janelas em cascata - - &Import - + + Next window + Próxima janela - - E&xport - + + Previous window + Janela anterior - - Open confi&guration dialog - + + Hide status field + Ocultar a barra de status - - &Tile windows - + + Close &all windows + Fechar todas as janelas - - Tile windows &horizontally - + + Re&store recently closed window + Restaurar a janela fechada recentemente - - Tile windows &vertically - + + Close current &window + Fechar janela atual - - &Cascade windows - + + Close &other windows + Fechar janela - - Close selected &window - + + Close windows on the &left + Fechar janelas na &esquerda - - Close all windows &but selected - + + Close windows on the &right + Fechar janelas na &direita - - Close &all windows - + + Re&name selected window + Renomear janela selecionada - - Re&store recently closed window - + + Open Debug Console + Abrir Debug Console - - &Rename selected window - + + Open CSS Console + Abrir CSS Console - - Report a &bug - + + Report a &bug + Relatar um erro - - Propose a new &feature - + + D&onate + Fazer uma doação - - &About - + + Propose a new &feature + Proponha novas funcionalidades - - &Licenses - + + &About + Sobre - - Open home &page - + + &Licenses + Licenças - - Open fo&rum page - + + Open home &page + Abrir página inicial - - User &Manual - + + User &Manual + Manual do Usuário - - SQLite &documentation - + + SQLite &documentation + Documentação do SQLite - - Check for &updates - + + Bugs and feature &requests + Bugs e solicitações de recursos - - &Database - menubar - + + Quit + Sair - - &Structure - menubar - + + Check for &updates + Verificar atualizações - - &View - menubar - + + &Database + menubar + Banco de dados - - &Tools - menubar - + + &Structure + menubar + Estrutura - - &Help - + + &View + menubar + Visualizar - - Could not set style: %1 - main window - + + Window list + menubar view menu + Lista de janelas - - Cannot export, because no export plugin is loaded. - + + &Tools + menubar + Ferramentas - - Cannot import, because no import plugin is loaded. - + + &Help + Ajuda - - Rename window - + + Could not set style: %1 + main window + Não foi possível definir o estilo: %1 - - Enter new name for the window: - + + Cannot export, because no export plugin is loaded. + Não é possível exportar, porque nenhum plugin de exportação está carregado. - - New updates are available. <a href="%1">Click here for details</a>. - + + Cannot import, because no import plugin is loaded. + Não é possível importar porque nenhum plugin de importação está carregado. - - You're running the most recent version. No updates are available. - + + Rename window + Renomear janela - - Database passed in command line parameters (%1) was already on the list under name: %2 - + + Enter new name for the window: + Digite um novo nome para a janela: - - Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 - + + New updates are available. <a href="%1">Click here for details</a>. + Novas atualizações estão disponíveis. <a href="%1">Clique aqui para mais detalhes</a>. - - Could not add database %1 to list. - + + You're running the most recent version. No updates are available. + Você está executando a versão mais recente. Não há atualizações disponíveis. - - + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Banco de dados passado nos parâmetros da linha de comando (%1) já estava na lista com o nome: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Banco de dados passado nos parâmetros da linha de comando (%1) já estava na lista com o nome: %2 + + + + Could not add database %1 to list. + Não foi possível adicionar o banco de dados %1 à lista. + + + MdiWindow - - Uncommitted changes - + + Uncommitted changes + Alterações não realizadas - - Close anyway - + + Close anyway + Fechar mesmo assim - - Don't close - + + Don't close + Não fechar - - + + MultiEditor - - Null value - multieditor - + + Null value + multieditor + Valor nulo + + + + Configure editors for this data type + Configurar os editores para este tipo de dados - - Configure editors for this data type - + + Open another tab + Abrir outra aba - - Open another tab - + + Foreign Key + Chave estrangeira - - Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. - + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Plugin do editor de dados '%1' não carregado, enquanto estiver definido para edição '%1' tipo de dados. {1'?} {2'?} {1' or 2'?} - - Deleted - multieditor - + + Deleted + multieditor + Excluído - - Read only - multieditor - + + Read only + multieditor + Somente leitura - - + + MultiEditorBoolPlugin - - Boolean - + + Boolean + Booleano - - + + MultiEditorDatePlugin - - Date - + + Date + Data - - + + MultiEditorDateTimePlugin - - Date & time - + + Date & time + Data e hora - - + + MultiEditorHexPlugin - - Hex - + + Hex + Hexadecimal - - + + MultiEditorNumericPlugin - - Number - numeric multi editor tab name - + + Number + numeric multi editor tab name + Número - - + + MultiEditorText - - Tab changes focus - + + Tab changes focus + Mudança de foco da guia - - Cut - + + Cut + Recortar - - Copy - + + Copy + Copiar - - Paste - + + Paste + Colar - - Delete - + + Delete + Deletar - - Undo - + + Undo + Desfazer - - Redo - + + Redo + Refazer - - + + MultiEditorTextPlugin - - Text - + + Text + Texto - - + + MultiEditorTimePlugin - - Time - + + Time + Tempo - - + + NewConstraintDialog - - New constraint - + + New constraint + Nova constraint + + + + + Primary Key + new constraint dialog + Chave Primária - - - Primary Key - new constraint dialog - + + + Foreign Key + new constraint dialog + Foreign Key - - - Foreign Key - new constraint dialog - + + + Unique + new constraint dialog + Unique - - - Unique - new constraint dialog - + + + Check + new constraint dialog + Verificar - - - Check - new constraint dialog - + + Not NULL + new constraint dialog + Not NULL - - Not NULL - new constraint dialog - + + Collate + new constraint dialog + Collate - - Collate - new constraint dialog - + + Generated + new constraint dialog + Gerado - - Default - new constraint dialog - + + Default + new constraint dialog + Padrão - - + + NewVersionDialog - - SQLiteStudio updates - + + SQLiteStudio updates + Atualizações do SQLiteStudio - - New updates are available! - + + New version is available! + Uma nova versão está disponível! - - Component - + + Download new version! + Baixe a nova versão! - - This application will be closed and the update installer will start to download and install all the updates. - + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + O novo pacote de versão será baixado. Caberá a você instalá-lo sempre que você estiver pronto. - - Update version - + + Open SQLiteStudio home page. + Abrir página inicial do SQLiteStudio. - - Check for updates on startup - + + Read release notes && download package yourself. + Leia as notas do lançamento e baixe o pacote você mesmo. - - Update to new version! - + + Just close this window. + Apenas feche essa janela. - - Not now. - + + Check for updates on startup + Verificar se há atualizações na inicialização - - Don't install the update and close this window. - + + Not now. + Agora não. - - + + PopulateConfigDialog - - Populating configuration - + + Populating configuration + Preenchendo configuração - - Configuring <b>%1</b> for column <b>%2</b> - + + Configuring <b>%1</b> for column <b>%2</b> + Configurando <b>%1</b> para a coluna <b>%2</b> - - + + PopulateDialog - - Populate table - + + Populate table + Preencher tabela - - Database - + + Database + Banco de dados - - Table - + + Table + Tabela - - Columns - + + Columns + Colunas - - Number of rows to populate: - + + Number of rows to populate: + Número de linhas para preencher: - - Populate - populate dialog button - + + Populate + populate dialog button + Preencher - - Abort - + + Abort + Interromper - - Configure - + + Configure + Configurar - - Populating configuration for this column is invalid or incomplete. - + + Populating configuration for this column is invalid or incomplete. + Preencher a configuração para esta coluna é inválido ou incompleto. - - Select database with table to populate - + + Select database with table to populate + Selecionar a tabela do banco de dados para preencher - - Select table to populate - + + Select table to populate + Selecione a tabela para preencher - - You have to select at least one column. - + + You have to select at least one column. + Você deve selecionar pelo menos uma coluna. - - + + QObject - - Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). - + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Não é possível editar colunas que são resultado das declarações compostas %1 (uma que inclui %2, %3 ou %4 palavras-chave). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + O mecanismo de execução da consulta teve problemas com a extração de ROWID corretamente. Este pode ser um erro na aplicação. Você pode querer reportar isto. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + A coluna solicitada é um resultado de expressão SQL, ao invés de uma seleção de coluna simples. Essas colunas não podem ser editadas. - - The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. - + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Coluna requisitada pertence à tabela SQLite restrita. Essas tabelas não podem ser editadas diretamente. - - Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. - + + Cannot edit results of query other than %1. + Não é possível editar resultados da consulta diferente de %1. - - Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. - + + Cannot edit columns that are result of aggregated %1 statements. + Não é possível editar colunas que são resultado de declarações %1 agregadas. - - Cannot edit results of query other than %1. - + + Cannot edit columns that are result of %1 statement. + Não é possível editar colunas que são resultado de %1 demonstração. - - Cannot edit columns that are result of aggregated %1 statements. - + + Cannot edit columns that are result of common table expression statement (%1). + Não é possível editar colunas que são resultado da declaração de expressão comum da tabela (%1). - - Cannot edit columns that are result of %1 statement. - + + Cannot edit table generated columns. + Não é possível editar colunas geradas pela tabela. - - Cannot edit columns that are result of common table expression statement (%1). - + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). - - - - - on conflict: %1 - data view tooltip - + + + + + on conflict: %1 + data view tooltip + Conflito: %1 - - references table %1, column %2 - data view tooltip - + + references table %1, column %2 + data view tooltip + tabela de referências %1, coluna %2 - - condition: %1 - data view tooltip - + + condition: %1 + data view tooltip + condição: %1 - - collation name: %1 - data view tooltip - + + collation name: %1 + data view tooltip + nome da ordenação: %1 - - Data grid view - + + Data grid view + Exibição dos dados em grade - - Copy cell(s) contents to clipboard - + + Edit current cell inline + Editar célula atual em linha - - Copy cell(s) contents together with header to clipboard - + + Copy cell(s) contents to clipboard + Copiar conteúdo de células para área de transferência - - Paste cell(s) contents from clipboard - + + Copy cell(s) contents together with header to clipboard + Copiar conteúdo de células junto com o cabeçalho para área de transferência - - Set empty value to selected cell(s) - + + Paste cell(s) contents from clipboard + Colar o conteúdo da área de transferência - - Set NULL value to selected cell(s) - + + Set empty value to selected cell(s) + Definir valor vazio para a(s) célula(s) selecionada(s) - - Commit changes to cell(s) contents - + + Set NULL value to selected cell(s) + Definir valor NULL para as células selecionadas - - Rollback changes to cell(s) contents - + + Commit changes to cell(s) contents + Enviar alterações no conteúdo de célula(s) - - Delete selected data row - + + Rollback changes to cell(s) contents + Restaurar alterações para conteúdo de célula(s) - - Insert new data row - + + Delete selected data row + Excluir linha de dados selecionada - - Open contents of selected cell in a separate editor - + + Insert new data row + Inserir nova linha de dados - - Total pages available: %1 - + + Open contents of selected cell in a separate editor + Abrir o conteúdo da célula selecionada em um editor separado - - Total rows loaded: %1 - + + Toggle the height adjustment of rows + Alternar o ajuste de altura das linhas - - Data view (both grid and form) - + + Increase font size + data view + Aumentar o tamanho da fonte - - Refresh data - + + Decrease font size + data view + Diminuir o tamanho da fonte - - Switch to grid view of the data - + + Total pages available: %1 + Total de páginas disponíveis: %1 - - Switch to form view of the data - + + Total rows loaded: %1 + Total de linhas carregadas: %1 - - Database list - + + Data view (both grid and form) + Visualização de dados (grade e formulário) - - Delete selected item - + + Refresh data + Atualizar dados - - Clear filter contents - + + Switch to grid view of the data + Alternar para exibição em grade dos dados - - Refresh schema - + + Switch to form view of the data + Alternar para exibição de formulário dos dados - - Refresh all schemas - + + Database list + Lista de banco de dados - - Add database - + + Delete selected item + Apagar item selecionado - - Select all items - + + Clear filter contents + Limpar conteúdo do filtro - - Copy selected item(s) - + + Refresh schema + Atualizar esquema - - - - Paste from clipboard - + + Refresh all schemas + Atualizar todos os esquemas - - Tables - + + Add database + Adicionar banco de dados - - Indexes - + + Select all items + Selecionar todos os itens - - Triggers - + + Copy selected item(s) + Copiar itens selecionados - - Views - + + + + Paste from clipboard + Colar da área de transferência - - Columns - + + Increase font size + database list + Aumentar o tamanho da fonte - - Data form view - + + Decrease font size + database list + Diminuir o tamanho da fonte - - Commit changes for current row - + + Tables + Tabelas - - Rollback changes for current row - + + Indexes + Ãndices - - Go to first row on current page - + + Triggers + Triggers - - Go to next row - + + Views + Visualizações - - Go to previous row - + + Columns + Colunas - - Go to last row on current page - + + Data form view + Visualização de dados em formulário - - Insert new row - + + Commit changes for current row + Enviar alterações para a linha atual - - Delete current row - + + Rollback changes for current row + Reverter alterações da linha atual - - Main window - + + Go to first row on current page + Ir para a primeira linha na página atual - - Open SQL editor - + + Go to next row + Ir para a próxima linha - - Previous window - + + Go to previous row + Ir para a linha anterior - - Next window - + + Go to last row on current page + Ir para a última linha na página atual - - Hide status area - + + Insert new row + Inserir nova linha - - Open configuration dialog - + + Delete current row + Excluir linha atual - - Open Debug Console - + + Main window + Janela principal - - Open CSS Console - + + Open SQL editor + Abrir editor de SQL - - Cell text value editor - + + Open DDL history window + Abrir janela de histórico - - - Cut selected text - + + Open snippets editor window + Uma janela de editor de snippets - - - Copy selected text - + + Open function editor window + Uma janela de editor de função - - - Delete selected text - + + Open collation editor window + Uma janela de editor de ordenação - - - Undo - + + Open extension manager window + Abrir gerenciador de extensão - - - Redo - + + Previous window + Janela anterior - - SQL editor input field - + + Next window + Próxima janela - - Select whole editor contents - + + Hide status area + Ocultar área de status - - Save contents into a file - + + Open user manual + Abrir manual do usuário - - Load contents from a file - + + Open configuration dialog + Configurações - - Find in text - + + Open Debug Console + Abrir Console de Depuração - - Find next - + + Open CSS Console + Abrir Console CSS - - Find previous - + + Open the About dialog + Abrir o diálogo Sobre - - Replace in text - + + Quit the application + Sair do aplicativo - - Delete current line - + + Cell text value editor + Editor de texto de célula - - Request code assistant - + + + Cut selected text + Recortar texto selecionado - - Format contents - + + + Copy selected text + Copiar texto selecionado - - Move selected block of text one line down - + + + Delete selected text + Excluir texto selecionado - - Move selected block of text one line up - + + + Undo + Desfazer - - Copy selected block of text and paste it a line below - + + + Redo + Refazer - - Copy selected block of text and paste it a line above - + + SQL editor input field + Campo de entrada do editor SQL - - Toggle comment - + + Select whole editor contents + Selecionar todo o conteúdo do editor - - All SQLite databases - + + Save contents into a file + Salvar conteúdo em um arquivo - - All files - + + Load contents from a file + Carregar conteúdo de um arquivo - - - Database file - + + Find in text + Localizar no texto - - SQL editor window - + + Find next + Localizar próximo - - Execute query - + + Find previous + Localizar anterior - - Execute "%1" query - + + Replace in text + Substituir no texto - - Switch current working database to previous on the list - + + Delete current line + Excluir linha atual - - Switch current working database to next on the list - + + Request code assistant + Solicitar código assistente - - Go to next editor tab - + + Format contents + Formatar conteúdo - - Go to previous editor tab - + + Move selected block of text one line down + Mover o bloco selecionado de texto com uma linha para baixo - - Move keyboard input focus to the results view below - + + Move selected block of text one line up + Mover o bloco selecionado de uma linha para cima - - Move keyboard input focus to the SQL editor above - + + Copy selected block of text and paste it a line below + Copie o bloco de texto selecionado e cole uma linha abaixo - - Delete selected SQL history entries - + + Copy selected block of text and paste it a line above + Copie o bloco de texto selecionado e cole uma linha acima - - Table window - + + Toggle comment + Alternar comentário - - Refresh table structure - + + Increase font size + sql editor + Aumentar o tamanho da fonte - - Add new column - + + Decrease font size + sql editor + Diminuir o tamanho da fonte - - Edit selected column - + + All SQLite databases + Todos os bancos de dados - - Delete selected column - + + All files + Todos os arquivos - - Export table data - + + Select database file + Selecionar banco de dados - - Import data to the table - + + Select + Selecionar - - Add new table constraint - + + File type + Tipo de arquivo - - Edit selected table constraint - + + SQL editor window + Editor gráfico SQL - - Delete selected table constraint - + + Execute query + Executar consulta - - Refresh table index list - + + Execute single query under cursor + Executar a consulta em que o cursor está posicionado - - Add new index - + + Execute all queries in editor + Executar todas as consultas no editor - - Edit selected index - + + Execute "%1" query + Executar "%1" consulta - - Delete selected index - + + Switch current working database to previous on the list + Alternar a atual base de dados de trabalho para a anterior na lista - - Refresh table trigger list - + + Switch current working database to next on the list + Alternar a atual base de dados de trabalho para a próxima lista - - - Add new trigger - + + Go to next editor tab + Ir para a aba do próximo editor - - - Edit selected trigger - + + Go to previous editor tab + Ir para aba editor anterior - - - Delete selected trigger - + + Move keyboard input focus to the results view below + Mover o foco de entrada do teclado para a exibição de resultados abaixo - - - Go to next tab - + + Move keyboard input focus to the SQL editor above + Mover o foco de entrada do teclado para o editor SQL acima - - - Go to previous tab - + + Delete selected SQL history entries + Excluir as entradas do histórico SQL selecionadas - - A view window - + + Table window + Janela da tabela - - Refresh view trigger list - + + Commit the table structure + Commit the table structure - - + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Atualizar estrutura da tabela + + + + Add new column + Adicionar nova coluna + + + + Edit selected column + Editar coluna selecionada + + + + Delete selected column + Excluir coluna selecionada + + + + Export table data + Exportar dados da tabela + + + + Import data to the table + Importar dados para a tabela + + + + Add new table constraint + Adicionar nova restrição de tabela + + + + Edit selected table constraint + Editar restrição de tabela selecionada + + + + Delete selected table constraint + Excluir restrição de tabela selecionada + + + + Refresh table index list + Atualizar lista de índices das tabelas + + + + Add new index + Adicionar novo índice + + + + Edit selected index + Editar índice selecionado + + + + Delete selected index + Excluir índice selecionado + + + + Refresh table trigger list + Atualizar lista de gatilhos da tabela + + + + + Add new trigger + Adicionar novo gatilho + + + + + Edit selected trigger + Editar gatilho selecionado + + + + + Delete selected trigger + Excluir gatilho selecionado + + + + + Go to next tab + Ir para a próxima aba + + + + + Go to previous tab + Ir para a aba anterior + + + + A view window + Uma janela de visualização + + + + Commit the view's query + Executar a view após a consulta + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Atualizar lista de gatilho de visão + + + + Execute the view's query + Executar a view após a consulta + + + + A code snippets editor window + Uma janela de editor de snippets + + + + + + + Commit the pending changes + Faça commit da alteração. + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + Uma janela de editor de ordenação + + + + A function editor window + Uma janela de editor de função + + + + A SQLite extension editor window + Uma janela de editor de extensões SQLite + + + QuitConfirmDialog - - Uncommitted changes - + + Uncommitted changes + Alterações não realizadas - - Are you sure you want to quit the application? + + Are you sure you want to quit the application? Following items are pending: - + Tem certeza que quer sair do aplicativo? + +Os seguintes itens estão pendentes: - - + + SearchTextDialog - - Find or replace - + + Find or replace + Localizar ou substituir - - Find: - + + Find: + Localizar: - - Case sensitive - + + Case sensitive + Distinção entre maiúsculas e minúsculas - - Search backwards - + + Search backwards + Pesquisar para trás - - Regular expression matching - + + Regular expression matching + Expressão regular correspondente - - Replace && + + Replace && find next - + Substituir && +encontrar próximo - - Replace with: - + + Replace with: + Substituir por: - - Replace all - + + Replace all + Substituir todos - - Find - + + Find + Procurar - - + + SortDialog - - Sort by columns - + + Sort by columns + Ordenar por colunas - - - Column - + + + Column + Coluna - - - Order - + + + Order + Ordenar - - Sort by: %1 - + + Sort by: %1 + Classificar por: %1 - - Move column up - + + Move column up + Mover coluna para cima - - Move column down - + + Move column down + Mover coluna para baixo - - + + SqlEditor - - Cut - sql editor - + + Wrap words + sql editor + Ajustar palavras + + + + Cut + sql editor + Recortar + + + + Copy + sql editor + Copiar + + + + Paste + sql editor + Colar - - Copy - sql editor - + + Delete + sql editor + Deletar - - Paste - sql editor - + + Select all + sql editor + Selecionar tudo - - Delete - sql editor - + + Undo + sql editor + Desfazer - - Select all - sql editor - + + Redo + sql editor + Refazer - - Undo - sql editor - + + Complete + sql editor + Completado - - Redo - sql editor - + + Format SQL + sql editor + Formatar SQL - - Complete - sql editor - + + Save SQL to file + sql editor + Salvar SQL no arquivo - - Format SQL - sql editor - + + Select file to save SQL + sql editor + Selecione o arquivo para salvar SQL - - Save SQL to file - sql editor - + + Load SQL from file + sql editor + Carregar SQL do arquivo - - Select file to save SQL - sql editor - + + Delete line + sql editor + Excluir linha - - Load SQL from file - sql editor - + + Move block down + sql editor + Mover bloco para baixo - - Delete line - sql editor - + + Move block up + sql editor + Mover bloco para cima - - Move block down - sql editor - + + Copy block down + sql editor + Copiar bloco para baixo - - Move block up - sql editor - + + Copy up down + sql editor + Copie para baixo - - Copy block down - sql editor - + + Find + sql editor + Localizar - - Copy up down - sql editor - + + Find next + sql editor + Encontrar próximo - - Find - sql editor - + + Find previous + sql editor + Localizar anterior - - Find next - sql editor - + + Replace + sql editor + Substituir - - Find previous - sql editor - + + Toggle comment + sql editor + Alternar comentário - - Replace - sql editor - + + Increase font size + sql editor + Aumentar o tamanho da fonte - - Toggle comment - sql editor - + + Decrease font size + sql editor + Reduzir Tamanho da Fonte - - Saved SQL contents to file: %1 - + + Could not open file '%1' for writing: %2 + Não foi possível abrir o arquivo '%1' para escrever: %2 - - Syntax completion can be used only when a valid database is set for the SQL editor. - + + Saved SQL contents to file: %1 + Conteúdo SQL salvo no arquivo: %1 - - Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. - + + Syntax completion can be used only when a valid database is set for the SQL editor. + A conclusão da sintaxe só pode ser usada quando um banco de dados válido é definido para o editor SQL. - - Save to file - + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + O conteúdo do editor SQL é enorme, portanto os erros detectando e o realce de objetos existentes estão temporariamente desabilitados. - - Could not open file '%1' for writing: %2 - + + Save to file + Salvar para arquivo - - SQL scripts (*.sql);;All files (*) - + + SQL scripts (*.sql);;All files (*) + Scripts SQL (*.sql);;Todos os arquivos (*) - - Open file - + + Open file + Abrir arquivo - - Could not open file '%1' for reading: %2 - + + Could not open file '%1' for reading: %2 + Não foi possível abrir o arquivo '%1' para leitura: %2 - - Reached the end of document. Hit the find again to restart the search. - + + Reached the end of document. Hit the find again to restart the search. + Alcançou o fim do documento. Clique em encontrar novamente para reiniciar a pesquisa. - - + + SqlQueryItem - - Column: - data view tooltip - + + Committing error: + data view tooltip + Erro ao enviar: - - Data type: - data view - + + Column: + data view tooltip + Coluna: - - Table: - data view tooltip - + + Data type: + data view + Tipo de dado: - - Constraints: - data view tooltip - + + Table: + data view tooltip + Tabela: - - Cannot load the data for a cell that refers to the already closed database. - + + Constraints: + data view tooltip + Constraints: - - + + SqlQueryItemDelegate - - The row is marked for deletion. - + + + + + Cannot edit this cell. Details: %1 + Não é possível editar esta célula. Detalhes: %1 - - - - - - Cannot edit this cell. Details: %1 - + + The row is marked for deletion. + A linha está marcada para exclusão. - - - Structure of this table has changed since last data was loaded. Reload the data to proceed. - + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + A estrutura desta tabela mudou desde a última data foi carregada. Recarregue os dados para continuar. - - Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). - + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editar um conteúdo enorme em um editor de celular não é uma boa ideia. Pode se tornar lento e inconveniente. É melhor editar conteúdos tão grandes em um Form View ou em um editor pop-up (disponível no menu rick-click). - - Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. - + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Chave estrangeira para a coluna %2 possui mais de %1 valores possíveis.'é muito para ser exibido na lista de seleção. Você precisa editar o valor manualmente. - - + + SqlQueryModel - - - Only one query can be executed simultaneously. - + + + Only one query can be executed simultaneously. + Apenas uma consulta pode ser executada simultaneamente. + + + + Cannot execute query on undefined or invalid database. + Não é possível executar a consulta num banco de dados indefinido ou inválido. - - Cannot commit the data for a cell that refers to the already closed database. - + + Cannot execute empty query. + Não é possível executar consulta vazia. - - Could not begin transaction on the database. Details: %1 - + + Uncommitted data + Dados não confirmados - - An error occurred while rolling back the transaction: %1 - + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + Há alterações de dados não confirmadas. Você deseja prosseguir mesmo assim? Todas as alterações não confirmadas serão perdidas. - - Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. - + + Cannot commit the data for a cell that refers to the already closed database. + Não é possível confirmar os dados para uma célula que se refere ao banco de dados já fechado. - - Uncommitted data - + + Could not begin transaction on the database. Details: %1 + Não foi possível iniciar a transação no banco de dados. Detalhes: %1 - - There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. - + + An error occurred while committing the transaction: %1 + Ocorreu um erro ao confirmar a transação: %1 - - An error occurred while committing the transaction: %1 - + + An error occurred while rolling back the transaction: %1 + Ocorreu um erro ao reverter a transação: %1 - - An error occurred while committing the data: %1 - + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tentou comprometer uma célula que não é editável (ainda é modificada e aguardando commit)! Isso é um bug. Por favor, reporte isso. - - Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - + + An error occurred while committing the data: %1 + Ocorreu um erro ao confirmar os dados: %1 - - - Error while executing SQL query on database '%1': %2 - + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + O número de linhas por página foi reduzido para %1 devido ao número de colunas (%2) na visualização de dados. - - Error while loading query results: %1 - + + + Error while executing SQL query on database '%1': %2 + Erro ao executar consulta SQL no banco de dados '%1': %2 - - Insert multiple rows - + + Error while loading query results: %1 + Erro ao carregar os resultados da consulta: %1 - - Number of rows to insert: - + + Insert multiple rows + Inserir múltiplas linhas - - + + + Number of rows to insert: + Número de linhas a inserir: + + + + Delete rows + Excluir linha + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + Você está prestes a excluir linhas recém-inseridas que ainda não foram confirmadas. Números da linha: %1 +Essa exclusão será permanente. Tem certeza que deseja excluí-la? + + + SqlQueryView - - Go to referenced row in... - + + Go to referenced row in... + Ir para a linha referenciada em... + + + + Copy + Copiar + + + + Copy with headers + Copiar com cabeçalhos + + + + Copy as... + Copiar como... + + + + Paste + Colar + + + + Paste as... + Colar como... + + + + Set NULL values + Definir valores NULL + + + + Erase values + Apagar valores + + + + Commit + Commit - - Copy - + + Rollback + Rollback - - Copy as... - + + Commit selected cells + Submeter células selecionadas - - Paste - + + Rollback selected cells + Restaurar células selecionadas - - Paste as... - + + Edit current cell inline + Editar célula atual - - Set NULL values - + + Define columns to sort by + Definir colunas para classificar por - - Erase values - + + Remove custom sorting + Remover classificação personalizada - - Edit value in editor - + + Insert row + Inserir linha - - Commit - + + Insert multiple rows + Inserir múltiplas linhas - - Copy with headers - + + Delete selected row + Excluir linha selecionada - - Rollback - + + Adjust height of rows + Ajustar altura das linhas - - Commit selected cells - + + Increase font size + data view + Aumentar o tamanho da fonte - - Rollback selected cells - + + Decrease font size + data view + Diminuir o tamanho da fonte - - Define columns to sort by - + + Invert selection + data view + Inverter seleção - - Remove custom sorting - + + Edit value in editor + Editar valor no editor - - Insert row - + + Show value in a viewer + Mostrar valor em um visualizador - - Insert multiple rows - + + Generate query for selected cells + Gerar consulta para células selecionadas - - Delete selected row - + + No items selected to paste clipboard contents to. + Nenhum item selecionado para colar conteúdo da área de transferência. - - Show value in a viewer - + + Cannot paste data. Details: %1 + Não é possível colar dados. Detalhes: %1 - - Generate query for selected cells - + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + A estrutura de pelo menos uma tabela usada mudou desde a última data foi carregada. Recarregue os dados para prosseguir. - - No items selected to paste clipboard contents to. - + + Cannot paste to a cell. Details: %1 + Não é possível colar para uma célula. Detalhes: %1 - - Go to referenced row in table '%1' - + + The row is marked for deletion. + A linha está marcada para exclusão. - - table '%1' - + + Cannot paste to column %1. Details: %2 + Não é possível colar na coluna %1. Detalhes: %2 - - Referenced row (%1) - + + Go to referenced row in table '%1' + Ir para a linha referenciada na tabela '%1' - - Trim pasted text? - + + table '%1' + tabela '%1' - - The pasted text contains leading or trailing white space. Trim it automatically? - + + Referenced row (%1) + Linha referenciada (%1) - - Edit value - + + Trim pasted text? + Aparar texto colado? - - + + + The pasted text contains leading or trailing white space. Trim it automatically? + O texto colado contém espaço branco à esquerda ou à direita. Aparar automaticamente? + + + + Paste "NULL" as null value? + Colar "NULO" como valor nulo? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + O texto colado contém "NULL" literais. Deseja considerá-los como valores NULL? + + + + Edit value + Editar valor + + + SqlTableModel - - Error while committing new row: %1 - + + Error while committing new row: %1 + Erro ao confirmar nova linha: %1 - - Error while deleting row from table %1: %2 - + + Error while deleting row from table %1: %2 + Erro ao excluir linha da tabela %1: %2 - - + + SqliteExtensionEditor - - Filter extensions - + + Filter extensions + Filtrar extensões - - Leave empty to use default function - + + Leave empty to use default function + Deixe em branco para usar a função padrão - - Extension file - + + Extension file + Arquivo de extensão - - Initialization function - + + Initialization function + Função de inicialização - - Databases - + + Databases + Banco de dados - - Register in all databases - + + Register in all databases + Registrar em todos os bancos de dados - - Register in following databases: - + + Register in following databases: + Registrar nos seguintes bancos de dados: - - Extension manager window has uncommitted modifications. - + + Extension manager window has uncommitted modifications. + A janela do gerenciador de extensões tem modificações não realizadas. - - Extension manager - + + Extension manager + Gerenciador de extensões - - Commit all extension changes - + + Commit all extension changes + Enviar todas as alterações de extensão - - Rollback all extension changes - + + Rollback all extension changes + Restaurar todas as alterações de extensão - - Add new extension - + + Add new extension + Adicionar nova extensão - - Remove selected extension - + + Remove selected extension + Remover extensão selecionada - - Editing extensions manual - + + Editing extensions manual + Edição manual de extensões - - File with given path does not exist or is not readable. - + + File with given path does not exist or is not readable. + O arquivo com o caminho informado não existe ou não é legível. - - Unable to load extension: %1 - + + Unable to load extension: %1 + Não foi possível carregar a extensão: %1 - - Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Nome de função de inicialização inválido. Nome de função só pode conter caracteres alfa-numéricos e sublinhado. - - Dynamic link libraries (*.dll);;All files (*) - + + Dynamic link libraries (*.dll);;All files (*) + Bibliotecas dinâmicas de links (*.dll);;Todos os arquivos (*) - - Shared objects (*.so);;All files (*) - + + Shared objects (*.so);;All files (*) + Objetos compartilhados (*.so);;Todos os arquivos (*) - - Dynamic libraries (*.dylib);;All files (*) - + + Dynamic libraries (*.dylib);;All files (*) + Bibliotecas dinâmicas (*.dylib);;Todos os arquivos (*) - - All files (*) - + + All files (*) + Todos os arquivos (*) - - Open file - + + Open file + Abrir arquivo - - + + StatusField - - Status - + + Status + Status - - Copy - + + Copy + Copiar - - Clear - + + Clear + Limpar - - + + TableConstraintsModel - - Type - table constraints - + + Type + table constraints + Tipo - - Details - table constraints - + + Details + table constraints + Detalhes - - Name - table constraints - + + Name + table constraints + Nome - - + + TableForeignKeyPanel - - Foreign table: - - - - - SQLite 2 does not support foreign keys officially, -but it's okay to use them anyway. - + + Foreign table: + Tabela externa: - - Columns - + + Columns + Colunas - - Local column - + + Local column + Coluna local - - Foreign column - + + Foreign column + Foreign column - - Reactions - + + Reactions + Reações - - Deferred foreign key - + + Deferred foreign key + Chave estrangeira diferida - - Named constraint - + + Named constraint + Restrição nomeada - - Constraint name - + + Constraint name + Nome da restrição - - Pick the foreign column. - + + Pick the foreign column. + Selecione a coluna estrangeira. - - Pick the foreign table. - + + Pick the foreign table. + Pegue a tabela externa. - - Select at least one foreign column. - + + Select at least one foreign column. + Selecione pelo menos uma coluna estrangeira. - - Enter a name of the constraint. - + + Enter a name of the constraint. + Digite um nome da constraint. - - Foreign column - table constraints - + + Foreign column + table constraints + Coluna estrangeira - - + + TablePrimaryKeyAndUniquePanel - - Columns - + + Columns + Colunas - - Column - + + Column + Coluna - - Collation - + + Collation + Collation - - Sort - + + Sort + Ordenar - - Valid only for a single column with INTEGER data type - + + Valid only for a single column with INTEGER data type + Válido somente para uma única coluna com tipo de dados INTEGER - - Autoincrement - + + Autoincrement + Auto-incremento - - Named constraint - + + Named constraint + Restrição nomeada - - Constraint name - + + Constraint name + Nome da constraint - - On conflict - + + On conflict + Conflito - - Collate - table constraints - + + Collate + table constraints + Collate - - Sort order - table constraints - + + Sort order + table constraints + Ordem de classificação - - Select at least one column. - + + Select at least one column. + Selecione pelo menos uma coluna. - - Enter a name of the constraint. - + + Enter a name of the constraint. + Digite um nome da restrição - - + + TableStructureModel - - Name - table structure columns - + + Name + table structure columns + Nome - - Data type - table structure columns - + + Data type + table structure columns + Tipo de dado - - Primary + + Primary Key - table structure columns - + table structure columns + Primary Key - - Foreign + + Foreign Key - table structure columns - + table structure columns + Foreign +Key - - Unique - table structure columns - + + Unique + table structure columns + Unique - - Check - table structure columns - + + Check + table structure columns + Verificar - - Not + + Not NULL - table structure columns - + table structure columns + Not NULL - - Collate - table structure columns - + + Collate + table structure columns + Collate - - Default value - table structure columns - + + Generated + table structure columns + Gerado - - + + + Default value + table structure columns + Valor padrão + + + TableWindow - - Structure - + + Structure + Estrutura + + + + Table name: + Nome da tabela: - - Table name: - + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled a cláusula SEM ROWID na tabela. Tal tabela não terá mais o &quot;rowid&quot; coluna oculta. Para tal tabela, uma coluna explícita PRIMARY KEY é obrigatória. Você pode ler mais detalhes sobre isso na documentação oficial do SQLite.</p></body></html> - - - Data - + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/desabilita a cláusula STRICT para a tabela. Tal tabela faz uma verificação rigorosa dos dados armazenados em colunas contra tipos de dados declarados para essas colunas. Isto é semelhante à forma como os tipos de dados geralmente são aplicados na maioria dos outros motores de banco de dados. Mantenha-o desabilitado para usar o comportamento clássico de SQLite (i. . nenhuma aplicação do tipo de dados). Você pode encontrar mais detalhes na documentação oficial de SQLite.</p></body></html> - - Constraints - + + + Data + Dados - - Indexes - + + Constraints + Constraints - - Triggers - + + Indexes + Ãndices - - DDL - + + Triggers + Triggers - - Export table - table window - + + DDL + DDL - - Import data to table - table window - + + Export table + table window + Exportar a tabela - - Populate table - table window - + + Import data to table + table window + Importar para a tabela - - Refresh structure - table window - + + Populate table + table window + Preencher a tabela - - Commit structure changes - table window - + + Refresh structure + table window + Atualizar estrutura - - Rollback structure changes - table window - + + Commit structure changes + table window + Confirmar alterações de estrutura - - Add column - table window - + + Rollback structure changes + table window + Restaurar alterações de estrutura - - Edit column - table window - + + Add column + table window + Adicionar coluna - - - Delete column - table window - + + Edit column + table window + Editar coluna - - Move column up - table window - + + + Delete column + table window + Excluir coluna - - Move column down - table window - + + Move column up + table window + Mover a coluna para cima - - Create similar table - table window - + + Move column down + table window + Mover a coluna para baixo - - Reset autoincrement value - table window - + + Create similar table + table window + Criar tabela semelhante - - Add table constraint - table window - + + Reset autoincrement value + table window + Redefinir valor de auto-incremento - - Edit table constraint - table window - + + Add table constraint + table window + Adicionar restrição de tabela - - Delete table constraint - table window - + + Edit table constraint + table window + Editar restrição de tabela - - Move table constraint up - table window - + + Delete table constraint + table window + Apagar restrição de tabela - - Move table constraint down - table window - + + Move table constraint up + table window + Mover restrição de tabela para cima - - Add table primary key - table window - + + Move table constraint down + table window + Mover restrição de tabela para baixo - - Add table foreign key - table window - + + Add table primary key + table window + Adicionar chave primária da tabela - - Add table unique constraint - table window - + + Add table foreign key + table window + Adicionar chave estrangeira de tabela - - Add table check constraint - table window - + + Add table unique constraint + table window + Adicionar restrição de unicidade de tabela - - Refresh index list - table window - + + Add table check constraint + table window + Adicionar uma check constraint - - Create index - table window - + + Refresh index list + table window + Atualizar lista de índices - - Edit index - table window - + + + Create index + table window + Criar índice - - Delete index - table window - + + Edit index + table window + Editar índice - - Refresh trigger list - table window - + + Delete index + table window + Excluir índice - - Create trigger - table window - + + Refresh trigger list + table window + Atualizar lista de disparos - - Edit trigger - table window - + + + Create trigger + table window + Criar gatilho - - Delete trigger - table window - + + Edit trigger + table window + Editar gatilho - - Are you sure you want to delete column '%1'? - table window - + + Delete trigger + table window + Excluir gatilho - - Following problems will take place while modifying the table. + + Are you sure you want to delete column '%1'? + table window + Tem certeza que deseja excluir a coluna '%1'? + + + + Following problems will take place while modifying the table. Would you like to proceed? - table window - + table window + Os seguintes problemas ocorrerão durante a modificação da tabela. +Gostaria de continuar? - - Table modification - table window - + + Table modification + table window + Modificação da tabela - - Could not load data for table %1. Error details: %2 - + + Could not load data for table %1. Error details: %2 + Não foi possível carregar os dados da tabela %1. Detalhes do erro: %2 - - Could not process the %1 table correctly. Unable to open a table window. - + + Could not process the %1 table correctly. Unable to open a table window. + Não foi possível processar a tabela %1 corretamente. Não foi possível abrir uma janela da tabela. - - Could not restore window %1, because no database or table was stored in session for this window. - + + Database + Banco de dados - - Could not restore window '%1', because no database or table was stored in session for this window. - + + Could not restore window %1, because no database or table was stored in session for this window. + Não foi possível restaurar a janela %1, porque nenhum banco de dados ou tabela foi armazenada na sessão desta janela. - - Could not restore window '%1', because database %2 could not be resolved. - + + Could not restore window '%1', because no database or table was stored in session for this window. + Não foi possível restaurar a janela '%1', porque nenhum banco de dados ou tabela foi armazenada na sessão desta janela. - - Could not restore window '%1'', because the table %2 doesn't exist in the database %3. - + + Could not restore window '%1', because database %2 could not be resolved. + Não foi possível restaurar a janela '%1', porque o banco de dados %2 não pôde ser resolvido. - - - New table %1 - + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Não foi possível restaurar a janela %1'', porque a tabela %2 não existe no banco de dados %3. - - Committed changes for table '%1' successfully. - + + + New table %1 + Nova tabela %1 - - Committed changes for table '%1' (named before '%2') successfully. - + + Committed changes for table '%1' successfully. + Alterações comprometidas para a tabela '%1' com sucesso. - - Autoincrement value for table '%1' has been reset successfully. - + + Committed changes for table '%1' (named before '%2') successfully. + Alterações comprometidas para a tabela '%1' (nomeado antes '%2') com sucesso. - - Uncommitted changes - + + Could not commit table structure. Error message: %1 + table window + Não foi possível criar a estrutura da tabela. Mensagem de erro: %1 - - There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - + + Reset autoincrement + Redefinir auto-incremento - - Table window "%1" has uncommitted structure modifications and data. - + + Are you sure you want to reset autoincrement value for table '%1'? + Tem certeza de que deseja redefinir o valor de auto-incremento para a tabela '%1'? - - Table window "%1" has uncommitted data. - + + An error occurred while trying to reset autoincrement value for table '%1': %2 + Ocorreu um erro ao tentar redefinir o valor de auto-incremento da tabela '%1': %2 - - Table window "%1" has uncommitted structure modifications. - + + Autoincrement value for table '%1' has been reset successfully. + O valor do auto-incremento para a tabela '%1' foi redefinido com sucesso. - - Could not commit table structure. Error message: %1 - table window - + + Empty name + Nome vazio - - Reset autoincrement - + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + Um nome em branco para a tabela é permitido no SQLite, mas não é recomendado. +Tem certeza de que deseja criar uma tabela com nome em branco? - - Are you sure you want to reset autoincrement value for table '%1'? - + + Cannot create a table without at least one column. + Não é possível criar uma tabela sem pelo menos uma coluna. - - An error occurred while trying to reset autoincrement value for table '%1': %2 - + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Não é possível criar tabela %1, se não tem chave primária definida. Desmarque a chave %2ou defina uma chave primária. - - Empty name - + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Não é possível usar o autoincremento para a chave primária quando a cláusula %1 é usada. Desmarque o %2, ou o autoincremento em uma chave primária. - - A blank name for the table is allowed in SQLite, but it is not recommended. -Are you sure you want to create a table with blank name? - + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + As seguintes colunas têm um tipo de dado não estrito: %1. Ou desative o modo rigoroso da tabela, ou corrija tipos de dados de colunas. Tipos de dados válidos estritos são: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Tem certeza que deseja excluir a restrição de tabela '%1'? - - Cannot create a table without at least one column. - + + Delete constraint + table window + Apagar restrição - - Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. - + + Cannot export, because no export plugin is loaded. + Não é possível exportar, porque nenhum plugin de exportação está carregado. - - Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. - + + Cannot import, because no import plugin is loaded. + Não é possível importar porque nenhum plugin de importação está carregado. - - Are you sure you want to delete table constraint '%1'? - table window - + + Uncommitted changes + Alterações não realizadas - - Delete constraint - table window - + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + Há modificações de estrutura não autorizadas. Você não pode navegar ou editar dados até que sua estrutura de tabela seja estabelecida. +Você deseja comprometer a estrutura, ou deseja voltar para a guia estrutura? + + + + Go back to structure tab + Voltar à aba de estrutura - - Cannot export, because no export plugin is loaded. - + + Commit modifications and browse data. + Conclua modificações e navegue dados. - - Cannot import, because no import plugin is loaded. - + + Name + table window indexes + Nome - - Go back to structure tab - + + Unique + table window indexes + Unique - - Commit modifications and browse data. - + + Columns + table window indexes + Colunas - - Name - table window indexes - + + Partial index condition + table window indexes + Condição do índice parcial - - Unique - table window indexes - + + Name + table window triggers + Nome - - Columns - table window indexes - + + Event + table window triggers + Evento - - Partial index condition - table window indexes - + + Condition + table window triggers + Condição - - Name - table window triggers - + + Details + table window triggers + Detalhes - - Event - table window triggers - + + Table window "%1" has uncommitted structure modifications and data. + Janela de tabela "%1" possui dados e modificações de estrutura não autorizadas. - - Condition - table window triggers - + + Table window "%1" has uncommitted data. + Janela de tabela "%1" tem dados não confirmados. - - Details - table window triggers - + + Table window "%1" has uncommitted structure modifications. + Janela de tabela "%1" tem modificações de estrutura não autorizadas. - - + + TriggerColumnsDialog - - Trigger columns - + + Trigger columns + Colunas de disparo - - Triggering columns: - + + Triggering columns: + Disparar colunas: - - Select all - + + Select all + Selecionar tudo - - Deselect all - + + Deselect all + Desmarcar tudo - - + + TriggerDialog - - - Trigger - + + + Trigger + Trigger - - On table: - + + On table: + Na tabela: - - Action: - + + Action: + Ação: - - - <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> - + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>A condição de SQL será avaliada antes do atual código de ativação. Caso a condição retorne falso, o gatilho não será disparado por essa linha.</p> - - Pre-condition: - + + Pre-condition: + Pré-condição: - - The scope is still not fully supported by the SQLite database. - + + The scope is still not fully supported by the SQLite database. + O âmbito ainda não é totalmente suportado pelo banco de dados SQLite. - - Trigger name: - + + Trigger name: + Nome do gatilho: - - When: - + + When: + Quando: - - List of columns for UPDATE OF action. - + + List of columns for UPDATE OF action. + Lista de colunas para ATUALIZAR A ação. - - Scope: - + + Scope: + Escopo: - - Code: - + + Code: + Código: - - Trigger statements to be executed. - + + Trigger statements to be executed. + Instruções de disparo a serem executadas. - - DDL - + + DDL + DDL - - On view: - + + On view: + Na visualização: - - Could not process trigger %1 correctly. Unable to open a trigger dialog. - + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Não foi possível processar o gatilho %1 corretamente. Não foi possível abrir um diálogo de gatilho. - - Enter a valid condition. - + + Enter a valid condition. + Informe uma condição válida. - - Enter a valid trigger code. - + + Enter a valid trigger code. + Digite um código acionador válido. - - Error - trigger dialog - + + Error + trigger dialog + Erro - - An error occurred while executing SQL statements: + + An error occurred while executing SQL statements: %1 - + Ocorreu um erro ao executar instruções SQL: +%1 - - + + VersionConvertSummaryDialog - - Database version convert - + + Database version convert + Conversão do banco de dados - - Following changes to the SQL statements will be made: - + + Following changes to the SQL statements will be made: + As seguintes mudanças para as instruções SQL serão feitas: - - Before - + + Before + Antes - - After - + + After + Depois - - + + ViewWindow - - Query - + + Query + Consulta + + + + View name: + Ver nome: + + + + Output column names + Nomes das colunas de saída - - View name: - + + + Data + Dado - - Output column names - + + Triggers + Triggers - - - Data - + + DDL + DDL - - Triggers - + + + Could not restore window '%1', because no database or view was stored in session for this window. + Não foi possível restaurar a janela '%1', porque nenhum banco de dados ou visualização foi armazenado na sessão desta janela. - - DDL - + + Could not restore window '%1', because database %2 could not be resolved. + Não foi possível restaurar a janela '%1', porque o banco de dados %2 não pôde ser resolvido. - - - Could not restore window '%1', because no database or view was stored in session for this window. - + + Could not restore window '%1', because database %2 could not be open. + Não foi possível restaurar a janela '%1', porque o banco de dados %2 não pôde estar aberto. - - Could not restore window '%1', because database %2 could not be resolved. - + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Não foi possível restaurar a janela '%1', porque a visualização %2't existe no banco de dados %3. - - Could not restore window '%1', because database %2 could not be open. - + + + New view %1 + Nova visualização %1 - - Could not restore window '%1', because the view %2 doesn't exist in the database %3. - + + Database + Banco de dados - - - New view %1 - + + Refresh the view + view window + Atualizar a visualização - - Refresh the view - view window - + + Commit the view changes + view window + Faça commit da alteração. - - Commit the view changes - view window - + + Rollback the view changes + view window + Restaurar as alterações de exibição - - Rollback the view changes - view window - + + Explicit column names + Nomes de coluna explícitas - - Explicit column names - + + Generate output column names automatically basing on result columns of the view. + Gerar nomes de colunas de saída baseando-se automaticamente em colunas de resultado da exibição. - - Generate output column names automatically basing on result columns of the view. - + + Add column + view window + Adicionar coluna - - Add column - view window - + + Edit column + view window + Editar coluna - - Edit column - view window - + + Delete column + view window + Excluir coluna - - Delete column - view window - + + Move column up + view window + Mover a coluna para cima - - Move column up - view window - + + Move column down + view window + Mover a coluna para baixo - - Move column down - view window - + + Refresh trigger list + view window + Atualizar lista de disparos - - Refresh trigger list - view window - + + Create new trigger + view window + Criar novo gatilho - - Create new trigger - view window - + + Edit selected trigger + view window + Editar gatilho selecionado - - Edit selected trigger - view window - + + Delete selected trigger + view window + Excluir gatilho selecionado - - Delete selected trigger - view window - + + View window "%1" has uncommitted structure modifications and data. + Exibir janela "%1" possui dados e modificações de estrutura não autorizadas. - - View window "%1" has uncommitted structure modifications and data. - + + View window "%1" has uncommitted data. + Ver janela "%1" possui dados não confirmados. - - View window "%1" has uncommitted data. - + + View window "%1" has uncommitted structure modifications. + Exibir janela "%1" tem modificações de estrutura não autorizadas. - - View window "%1" has uncommitted structure modifications. - + + Could not load data for view %1. Error details: %2 + Não foi possível carregar os dados para a visualização %1. Detalhes do erro: %2 - - Uncommitted changes - + + Uncommitted changes + Alterações não realizadas - - There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. Do you want to commit the structure, or do you want to go back to the structure tab? - + Há modificações de estrutura não autorizadas. Você não pode navegar ou editar dados até que tenha a estrutura da visualização configurada. +Você deseja comprometer a estrutura, ou deseja voltar para a guia estrutura? - - Committed changes for view '%1' successfully. - + + Go back to structure tab + Voltar à aba de estrutura - - Committed changes for view '%1' (named before '%2') successfully. - + + Commit modifications and browse data. + Modificações e buscas por dados. - - Could not load data for view %1. Error details: %2 - + + View '%1' was committed successfully. + A visualização '%1' foi confirmada com sucesso. - - Go back to structure tab - + + Committed changes for view '%1' successfully. + Alterações comprometidas para visualização '%1' com sucesso. - - Commit modifications and browse data. - + + Committed changes for view '%1' (named before '%2') successfully. + Alterações comprometidas para visualização '%1' (nomeado antes '%2') com sucesso. - - Could not commit view changes. Error message: %1 - view window - + + Could not commit view changes. Error message: %1 + view window + Não foi possível enviar alterações de visualização. Mensagem de erro: %1 - - Override columns - + + Override columns + Substituir colunas - - Currently defined columns will be overriden. Do you want to continue? - + + Currently defined columns will be overriden. Do you want to continue? + Colunas atualmente definidas serão substituídas. Deseja continuar? - - Could not determinate columns returned from the view. The query is problably incomplete or contains errors. - + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Não foi possível determinar as colunas retornadas da visualização. A consulta está provavelmente incompleta ou contém erros. - - Name - view window triggers - + + Name + view window triggers + Nome - - Instead of - view window triggers - + + Instead of + view window triggers + Em vez de - - Condition - view window triggers - + + Condition + view window triggers + Condição - - Details - table window triggers - + + Details + table window triggers + Detalhes - - Could not process the %1 view correctly. Unable to open a view window. - + + Could not process the %1 view correctly. Unable to open a view window. + Não foi possível processar a visualização de %1 corretamente. Não é possível abrir uma janela de visualização. - - Empty name - + + Empty name + Nome vazio - - A blank name for the view is allowed in SQLite, but it is not recommended. + + A blank name for the view is allowed in SQLite, but it is not recommended. Are you sure you want to create a view with blank name? - + Um nome em branco para a visualização é permitido no SQLite, mas não é recomendado. +Tem certeza de que deseja criar uma view com nome em branco? - - The SELECT statement could not be parsed. Please correct the query and retry. + + The SELECT statement could not be parsed. Please correct the query and retry. Details: %1 - + A declaração SELECT não pôde ser analisada. Por favor, corrija a consulta e tente novamente. +Detalhes: %1 - - The view could not be modified due to internal SQLiteStudio error. Please report this! - + + The view could not be modified due to internal SQLiteStudio error. Please report this! + A vista não pôde ser modificada devido a um erro interno do SQLiteStudio. Por favor reporte isto! - - The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. - + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + A visualização do código não pôde ser analisada corretamente para execução. Este é um bug SQLiteStudio's. Por favor, reporte-o. - - Following problems will take place while modifying the view. + + Following problems will take place while modifying the view. Would you like to proceed? - view window - + view window + Os seguintes problemas ocorrerão durante a modificação da exibição. +Gostaria de continuar? - - View modification - view window - + + View modification + view window + Visualizar modificação - - + + WidgetCover - - Interrupt - + + Interrupt + Interromper - + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_PT.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_PT.ts new file mode 100644 index 0000000..52ac9cc --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_pt_PT.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ro_RO.qm b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ro_RO.qm and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ro_RO.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ro_RO.ts index 5df742d..81a96ec 100644 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ro_RO.ts +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ro_RO.ts @@ -1,6612 +1,7111 @@ - - + + AboutDialog - - About SQLiteStudio and licenses - + + About SQLiteStudio and licenses + About SQLiteStudio and licenses - - About - + + About + About - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - - Licenses - + + Licenses + Licenses - - Environment - + + Environment + Environment - - Icon directories - + + Icon directories + Icon directories - - Form directories - + + Form directories + Form directories - - Plugin directories - + + SQLite extension directories + SQLite extension directories - - Configuration directory - + + Plugin directories + Plugin directories - - Application directory - + + Configuration directory + Configuration directory - - Qt version: - + + Application directory + Application directory - - SQLite 3 version: - + + Qt version: + Qt version: - - Portable distribution. - + + SQLite 3 version: + SQLite 3 version: - - MacOS X application boundle distribution. - + + Portable distribution. + Portable distribution. - - Operating system managed distribution. - + + MacOS X application bundle distribution. + MacOS X application bundle distribution. - - Copy - + + Operating system managed distribution. + Operating system managed distribution. - - <h3>Table of contents:</h3><ol>%2</ol> - + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> - - + + BindParamsDialog - - Query parameters - + + Query parameters + Query parameters - - Please provide values for query parameters - + + Please provide values for query parameters + Please provide values for query parameters - - + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + CollationsEditor - - Filter collations - + + Filter collations + Filter collations - - Databases - + + Databases + Databases - - Register in all databases - + + Register in all databases + Register in all databases - - Register in following databases: - + + Register in following databases: + Register in following databases: - - Implementation code: - + + Implementation code: + Implementation code: - - Collation name: - + + Collation name: + Collation name: - - Implementation language: - + + Implementation language: + Implementation language: - - Collations editor - + + Collations editor + Collations editor - - Commit all collation changes - + + Commit all collation changes + Commit all collation changes - - Rollback all collation changes - + + Rollback all collation changes + Rollback all collation changes - - Create new collation - + + Create new collation + Create new collation - - Delete selected collation - + + Delete selected collation + Delete selected collation - - Editing collations manual - + + Editing collations manual + Editing collations manual - - Enter a non-empty, unique name of the collation. - + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. - - Pick the implementation language. - + + Pick the implementation language. + Pick the implementation language. - - Enter a non-empty implementation code. - + + Enter a non-empty implementation code. + Enter a non-empty implementation code. - - Collations editor window has uncommitted modifications. - + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. - - + + ColorButton - - Pick a color - + + Pick a color + Pick a color - - + + ColumnCollatePanel - - Collation name: - + + Collation name: + Collation name: - - Named constraint: - + + Named constraint: + Named constraint: - - Enter a name of the constraint. - + + Enter a name of the constraint. + Enter a name of the constraint. - - Enter a collation name. - + + Enter a collation name. + Enter a collation name. - - + + ColumnDefaultPanel - - Default value: - + + Default value: + Default value: - - Named constraint: - + + Named constraint: + Named constraint: - - Enter a default value expression. - + + Enter a default value expression. + Enter a default value expression. - - Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - - Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - - Enter a name of the constraint. - + + Enter a name of the constraint. + Enter a name of the constraint. - - + + ColumnDialog - - Column - + + Column + Column + + + + Name and type + Name and type - - Name and type - + + Scale + Scale - - Scale - + + Precision + Precision - - Precision - + + Data type: + Data type: - - Data type: - + + Column name: + Column name: - - Column name: - + + Size: + Size: - - Size: - + + Constraints + Constraints - - Constraints - + + Generated value + Generated value - - Unique - + + Unique + Unique - - - - - - - - Configure - + + + + + + + + + Configure + Configure - - Foreign Key - + + Foreign Key + Foreign Key - - Collate - + + Collate + Collate - - Not NULL - + + Not NULL + Not NULL - - Check condition - + + Check condition + Check condition - - Primary Key - + + Primary Key + Primary Key - - Default - + + Default + Default - - Advanced mode - + + Advanced mode + Advanced mode - - Add constraint - column dialog - + + Add constraint + column dialog + Add constraint - - Edit constraint - column dialog - + + Edit constraint + column dialog + Edit constraint - - - Delete constraint - column dialog - + + + Delete constraint + column dialog + Delete constraint - - Move constraint up - column dialog - + + Move constraint up + column dialog + Move constraint up - - Move constraint down - column dialog - + + Move constraint down + column dialog + Move constraint down - - Add a primary key - column dialog - + + Add a primary key + column dialog + Add a primary key - - Add a foreign key - column dialog - + + Add a foreign key + column dialog + Add a foreign key - - Add an unique constraint - column dialog - + + Add an unique constraint + column dialog + Add an unique constraint - - Add a check constraint - column dialog - + + Add a check constraint + column dialog + Add a check constraint - - Add a not null constraint - column dialog - + + Add a not null constraint + column dialog + Add a not null constraint - - Add a collate constraint - column dialog - + + Add a collate constraint + column dialog + Add a collate constraint - - Add a default constraint - column dialog - + + Add a generated value constraint + column dialog + Add a generated value constraint - - Are you sure you want to delete constraint '%1'? - column dialog - + + Add a default constraint + column dialog + Add a default constraint - - Correct the constraint's configuration. - + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? - - This constraint is not officially supported by SQLite 2, -but it's okay to use it. - + + Correct the constraint's configuration. + Correct the constraint's configuration. - - Scale is not allowed for INTEGER PRIMARY KEY columns. - + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. - - Precision cannot be defined without the scale. - + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. - - Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - - INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - - Precision is not allowed for INTEGER PRIMARY KEY columns. - + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. - - + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + ColumnDialogConstraintsModel - - Type - column dialog constraints - + + Type + column dialog constraints + Type - - Name - column dialog constraints - + + Name + column dialog constraints + Name - - Details - column dialog constraints - + + Details + column dialog constraints + Details - - + + ColumnForeignKeyPanel - - Foreign table: - + + Foreign table: + Foreign table: - - Foreign column: - + + Foreign column: + Foreign column: - - Reactions - + + Reactions + Reactions - - Deferred foreign key - + + Deferred foreign key + Deferred foreign key - - Named constraint - + + Named constraint + Named constraint - - Constraint name - + + Constraint name + Constraint name - - Pick the foreign table. - + + Pick the foreign table. + Pick the foreign table. - - Pick the foreign column. - + + Pick the foreign column. + Pick the foreign column. - - Enter a name of the constraint. - + + Enter a name of the constraint. + Enter a name of the constraint. - - - ColumnPrimaryKeyPanel + + + ColumnGeneratedPanel - - Autoincrement - + + Generating code: + Generating code: - - Sort order: - + + Explicit type: + Explicit type: - - Named constraint: - + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords - - On conflict: - + + Named constraint: + Named constraint: - - Enter a name of the constraint. - + + Enter the column value generating expression. + Enter the column value generating expression. - - - ColumnUniqueAndNotNullPanel - - Named constraint: - + + Invalid value generating expression: %1. + Invalid value generating expression: %1. - - On conflict: - + + Invalid value generating expression. + Invalid value generating expression. - - Enter a name of the constraint. - + + Enter a name of the constraint. + Enter a name of the constraint. - - - CompleterWindow + + + ColumnPrimaryKeyPanel - - Column: %1 - completer statusbar - + + Autoincrement + Autoincrement - - Table: %1 - completer statusbar - + + Sort order: + Sort order: - - Index: %1 - completer statusbar - + + Named constraint: + Named constraint: - - Trigger: %1 - completer statusbar - + + On conflict: + On conflict: - - View: %1 - completer statusbar - + + Enter a name of the constraint. + Enter a name of the constraint. - - Database: %1 - completer statusbar - + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + ColumnUniqueAndNotNullPanel - - Keyword: %1 - completer statusbar - + + Named constraint: + Named constraint: - - Function: %1 - completer statusbar - + + On conflict: + On conflict: - - Operator: %1 - completer statusbar - + + Enter a name of the constraint. + Enter a name of the constraint. + + + CompleterWindow - - String - completer statusbar - + + Column: %1 + completer statusbar + Column: %1 - - Number - completer statusbar - + + Table: %1 + completer statusbar + Table: %1 - - Binary data - completer statusbar - + + Index: %1 + completer statusbar + Index: %1 - - Collation: %1 - completer statusbar - + + Trigger: %1 + completer statusbar + Trigger: %1 - - Pragma function: %1 - completer statusbar - + + View: %1 + completer statusbar + View: %1 - - - ConfigDialog - - - Configuration - + + Database: %1 + completer statusbar + Database: %1 - - Search - + + Keyword: %1 + completer statusbar + Keyword: %1 - - General - + + Function: %1 + completer statusbar + Function: %1 - - Keyboard shortcuts - + + Operator: %1 + completer statusbar + Operator: %1 - - Look & feel - + + String + completer statusbar + String - - Style - + + Number + completer statusbar + Number - - Fonts - + + Binary data + completer statusbar + Binary data - - Colors - + + Collation: %1 + completer statusbar + Collation: %1 - - - Database list - + + Pragma function: %1 + completer statusbar + Pragma function: %1 - - Data browsing - + + Insert a code snippet + Insert a code snippet + + + ConfigDialog - - Data editors - + + + Configuration + Configuration - - Plugins - + + Search + Search - - Code formatters - + + General + General - - If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. - + + Keyboard shortcuts + Keyboard shortcuts - - Sort table columns alphabetically - + + Look & feel + Look & feel - - Expand tables node when connected to a database - + + Style + Style - - <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> - + + Fonts + Fonts - - Display additional labels on the list - + + Code colors + Code colors - - For regular tables labels will show number of columns, indexes and triggers for each of tables. - + + + Database list + Database list - - Display labels for regular tables - + + Code assistant + Code assistant - - Virtual tables will be marked with a 'virtual' label. - + + Data browsing + Data browsing - - Display labels for virtual tables - + + Data editors + Data editors - - Expand views node when connected to a database - + + Plugins + Plugins - - If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) - + + Code formatters + Code formatters - - Sort objects (tables, indexes, triggers and views) alphabetically - + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. - - Display system tables and indexes on the list - + + Sort table columns alphabetically + Sort table columns alphabetically - - Database dialog window - + + Expand tables node when connected to a database + Expand tables node when connected to a database - - <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> - + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> - - Do not mark database to be "permanent" by default - + + Display additional labels on the list + Display additional labels on the list - - <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> - + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. - - Try to bypass dialog completly when dropping database file onto the list - + + Display labels for regular tables + Display labels for regular tables - - Data browsing and editing - + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. - - - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - + + Display labels for virtual tables + Display labels for virtual tables - - Limit initial data column width to (in pixels): - + + Expand views node when connected to a database + Expand views node when connected to a database - - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> - + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) - - Show column and row details tooltip in data view - + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically - - Number of data rows per page: - + + Display system tables and indexes on the list + Display system tables and indexes on the list - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - + + Database dialog window + Database dialog window - - Number of memorized table populating configurations - + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> - - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default - - Keep NULL value when entering empty value - + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> - - <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> - + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list - - Use DEFAULT value (if defined), when committing NULL value - + + Data browsing and editing + Data browsing and editing - - Inserting new row in data grid - + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - - Before currently selected row - + + Number of memorized table populating configurations + Number of memorized table populating configurations - - After currently selected row - + + Data column width + Data column width - - At the end of data view - + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> - - Table windows - + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width - - <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - - Open Table Windows with the data tab for start - + + Number of data rows per page: + Number of data rows per page: - - <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> - - Place data tab as first tab in a Table Window - + + Show column and row details tooltip in data view + Show column and row details tooltip in data view - - View windows - + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - - <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - + + Keep NULL value when entering empty value + Keep NULL value when entering empty value - - Open View Windows with the data tab for start - + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> - - <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value - - Place data tab as first tab in a View Window - + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> - - Data types - + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns - - Available editors: - + + Inserting new row in data grid + Inserting new row in data grid - - Editors selected for this data type: - + + Before currently selected row + Before currently selected row - - Schema editing - + + After currently selected row + After currently selected row - - Number of DDL changes kept in history. - + + At the end of data view + At the end of data view - - DDL history size: - + + Table windows + Table windows - - Don't show DDL preview dialog when committing schema changes - + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - - SQL queries - + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start - - - Number of queries kept in the history. - + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - - History size: - + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window - - - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - + + View windows + View windows - - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> - + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - - Execute only the query under the cursor - + + Open View Windows with the data tab for start + Open View Windows with the data tab for start - - Number of memorized query parameters - + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - - Updates - + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window - - Automatically check for updates at startup - + + Data types + Data types - - Session - + + Available editors: + Available editors: - - Restore last session (active MDI windows) after startup - + + Editors selected for this data type: + Editors selected for this data type: - - Status Field - + + Schema editing + Schema editing - - <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> - + + Number of DDL changes kept in history. + Number of DDL changes kept in history. - - Always open Status panel when new message is printed - + + DDL history size: + DDL history size: - - Filter shortcuts by name or key combination - + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes - - Action - + + SQL queries + SQL queries - - Key combination - + + + Number of queries kept in the history. + Number of queries kept in the history. - - - Language - + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> - - Changing language requires application restart to take effect. - + + History size: + History size: - - Compact layout - + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - - <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - + + Execute only the query under the cursor + Execute only the query under the cursor - - Use compact layout - + + Number of memorized query parameters + Number of memorized query parameters - - Main window dock areas - + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> - - Left and right areas occupy corners - + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view - - Top and bottom areas occupy corners - + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> - - Hide built-in plugins - + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): - - Current style: - + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> - - Preview - + + Keep at least the width to show complete column name + Keep at least the width to show complete column name - - Enabled - + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> - - Disabled - + + Wrap lines in SQL editor + Wrap lines in SQL editor - - Active formatter plugin - + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> - - SQL editor font - + + Highlight current query + Highlight current query - - Database list font - + + Updates + Updates - - Database list additional label font - + + Automatically check for updates at startup + Automatically check for updates at startup - - Data view font - + + Session + Session - - Status field font - + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup - - SQL editor colors - + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time - - Current line background - + + Status Field + Status Field - - <p>SQL strings are enclosed with single quote characters.</p> - + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> - - String foreground - + + Always open Status panel when new message is printed + Always open Status panel when new message is printed - - <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - + + Code syntax colors + Code syntax colors - - Bind parameter foreground - + + Keyword foreground + Keyword foreground - - Highlighted parenthesis background - + + Regular foreground + Regular foreground - - <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - + + String foreground + String foreground - - BLOB value foreground - + + Comment foreground + Comment foreground - - Regular foreground - + + Valid objects foreground + Valid objects foreground - - Line numbers area background - + + Current query background + Current query background - - Keyword foreground - + + Bind parameter foreground + Bind parameter foreground - - Number foreground - + + Current line background + Current line background - - Comment foreground - + + Matched parenthesis background + Matched parenthesis background - - <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> - + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> - - Valid objects foreground - + + Number foreground + Number foreground - - Data view colors - + + BLOB value foreground + BLOB value foreground - - <p>Any data changes will be outlined with this color, until they're committed to the database.</p> - + + Matched parenthesis foreground + Matched parenthesis foreground - - Uncommitted data outline color - + + Reset to defaults + Reset to defaults - - <p>In case of error while committing data changes, the problematic cell will be outlined with this color.</p> - + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination - - Commit error outline color - + + Action + Action - - NULL value foreground - + + Key combination + Key combination - - Deleted row background - + + + Language + Language - - Database list colors - + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. - - <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> - + + Compact layout + Compact layout - - Additional labels foreground - + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - - Status field colors - + + Use compact layout + Use compact layout - - Information message foreground - + + Main window dock areas + Main window dock areas - - Warning message foreground - + + Left and right areas occupy corners + Left and right areas occupy corners - - Error message foreground - + + Top and bottom areas occupy corners + Top and bottom areas occupy corners - - Description: - plugin details - + + Hide built-in plugins + Hide built-in plugins - - Category: - plugin details - + + Current style: + Current style: - - Version: - plugin details - + + Preview + Preview - - Author: - plugin details - + + Enabled + Enabled - - Internal name: - plugin details - + + Disabled + Disabled - - Dependencies: - plugin details - + + Active formatter plugin + Active formatter plugin - - Conflicts: - plugin details - + + SQL editor font + SQL editor font - - Plugin details - + + Database list font + Database list font - - Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. - + + Database list additional label font + Database list additional label font - - %1 (built-in) - plugins manager in configuration dialog - + + Data view font + Data view font - - Details - + + Status field font + Status field font - - No plugins in this category. - + + Code assistant settings + Code assistant settings - - Add new data type - + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> - - Rename selected data type - + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name - - Delete selected data type - + + Description: + plugin details + Description: - - Help for configuring data type editors - + + Category: + plugin details + Category: - - - ConstraintCheckPanel - - The condition - + + Version: + plugin details + Version: - - Named constraint: - + + Author: + plugin details + Author: - - On conflict - + + Internal name: + plugin details + Internal name: - - Enter a valid condition. - + + Dependencies: + plugin details + Dependencies: - - Enter a name of the constraint. - + + Conflicts: + plugin details + Conflicts: - - - ConstraintDialog - - New constraint - constraint dialog - + + Plugin details + Plugin details - - Create - constraint dialog - + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. - - Edit constraint - dialog window - + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) - - Apply - constraint dialog - + + Details + Details - - Primary key - table constraints - + + No plugins in this category. + No plugins in this category. - - Foreign key - table constraints - + + Add new data type + Add new data type - - Unique - table constraints - + + Rename selected data type + Rename selected data type - - Not NULL - table constraints - + + Delete selected data type + Delete selected data type - - Check - table constraints - + + Help for configuring data type editors + Help for configuring data type editors - - Collate - table constraints - + + Clear hotkey for this action + Clear hotkey for this action - - Default - table constraints - + + Restore original hotkey for this action + Restore original hotkey for this action - - - ConstraintTabModel - - Table - table constraints - + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + ConstraintCheckPanel - - Column (%1) - table constraints - + + The condition + The condition - - Scope - table constraints - + + Named constraint: + Named constraint: - - Type - table constraints - + + On conflict + On conflict - - Details - table constraints - + + Enter a valid condition. + Enter a valid condition. - - Name - table constraints - + + Enter a name of the constraint. + Enter a name of the constraint. - - - CssDebugDialog + + + ConstraintDialog - - SQLiteStudio CSS console - + + New constraint + constraint dialog + New constraint - - - DataView - - Filter data - data view - + + Create + constraint dialog + Create - - Grid view - + + Edit constraint + dialog window + Edit constraint - - Form view - + + Apply + constraint dialog + Apply - - Refresh table data - data view - + + Primary key + table constraints + Primary key - - First page - data view - + + Foreign key + table constraints + Foreign key - - Previous page - data view - + + Unique + table constraints + Unique - - Next page - data view - + + Not NULL + table constraints + Not NULL - - Last page - data view - + + Check + table constraints + Check - - Filter - + + Generated + table constraints + Generated - - Hit Enter key or press "Apply filter" button on toolbar to apply new value. - + + Collate + table constraints + Collate - - Show filter inputs per column - data view - + + Default + table constraints + Default + + + ConstraintTabModel - - Apply filter - data view - + + Table + table constraints + Table - - Commit changes for selected cells - data view - + + Column (%1) + table constraints + Column (%1) - - Rollback changes for selected cells - data view - + + Scope + table constraints + Scope - - Show grid view of results - sql editor - + + Type + table constraints + Type - - Show form view of results - sql editor - + + Details + table constraints + Details - - Filter by text - data view - + + Name + table constraints + Name + + + CssDebugDialog - - Filter by the Regular Expression - data view - + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + DataView - - Filter by SQL expression - data view - + + Filter data + data view + Filter data - - Tabs on top - data view - + + Grid view + Grid view - - Tabs at bottom - data view - + + Form view + Form view - - Place new rows above selected row - data view - + + Refresh table data + data view + Refresh table data - - Place new rows below selected row - data view - + + First page + data view + First page - - Place new rows at the end of the data view - data view - + + Previous page + data view + Previous page - - Total number of rows is being counted. -Browsing other pages will be possible after the row counting is done. - + + Next page + data view + Next page - - Row: %1 - + + Last page + data view + Last page - - - DbConverterDialog - - Convert database - + + Commit changes for selected cells + data view + Commit changes for selected cells - - Source database - + + Rollback changes for selected cells + data view + Rollback changes for selected cells - - Source database version: - + + Show grid view of results + data view + Show grid view of results - - Target database - + + Show form view of results + data view + Show form view of results - - Target version: - + + Filter by text (if contains) + data view + Filter by text (if contains) - - This is the file that will be created as a result of the conversion. - + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) - - Target file: - + + Tabs on top + data view + Tabs on top - - Name of the new database: - + + Tabs at bottom + data view + Tabs at bottom - - This is the name that the converted database will be added to SQLiteStudio with. - + + Place new rows above selected row + data view + Place new rows above selected row - - Select source database - + + Place new rows below selected row + data view + Place new rows below selected row - - Enter valid and writable file path. - + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view - - Entered file exists and will be overwritten. - + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. - - Enter a not empty, unique name (as in the list of databases on the left). - + + Row: %1 + Row: %1 - - No valid target dialect available. Conversion not possible. - + + Filter + Filter - - Select valid target dialect. - + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. - - Database %1 has been successfully converted and now is available under new name: %2 - + + Filter by the Regular Expression + data view + Filter by the Regular Expression - - SQL statements conversion - + + Filter by SQL expression + data view + Filter by SQL expression - - Following error occurred while converting SQL statements to the target SQLite version: - + + Show filter inputs per column + data view + Show filter inputs per column - - Would you like to ignore those errors and proceed? - + + Apply filter + data view + Apply filter - - + + DbDialog - - Database - + + Database + Database - - Database type - + + Database type + Database type - - Database driver - + + Database driver + Database driver - - - File - + + + File + File - - Create new database file - + + Name (on the list) + Name (on the list) - - Name (on the list) - + + Options + Options - - Options - + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> - - <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> - aasfd - + + Permanent (keep it in configuration) + Permanent (keep it in configuration) - - Permanent (keep it in configuration) - + + Test connection + Test connection - - Test connection - + + Select new or existing file on local computer + Select new or existing file on local computer - - Browse for existing database file on local computer - + + Browse + Browse - - Browse - + + Database type not selected. + Database type not selected. - - Enter an unique database name. - + + Database path not specified. + Database path not specified. - - This name is already in use. Please enter unique name. - + + Enter an unique database name. + Enter an unique database name. - - <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. - - Enter a database file path. - + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - - This database is already on the list under name: %1 - + + Enter a database file path. + Enter a database file path. - - Select a database type. - + + This database is already on the list under name: %1 + This database is already on the list under name: %1 - - + + + Select a database type. + Select a database type. + + + DbObjectDialogs - - Delete table - + + Delete table + Delete table - - Are you sure you want to delete table %1? - + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? - - Delete index - + + Delete index + Delete index - - Are you sure you want to delete index %1? - + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? - - Delete trigger - + + Delete trigger + Delete trigger - - Are you sure you want to delete trigger %1? - + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? - - Delete view - + + Delete view + Delete view - - Are you sure you want to delete view %1? - + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? - - - Error while dropping %1: %2 - + + + Error while dropping %1: %2 + Error while dropping %1: %2 - - Delete objects - + + Delete objects + Delete objects - - Are you sure you want to delete following objects: + + Are you sure you want to delete following objects: %1 - + Are you sure you want to delete following objects: +%1 - - Cannot start transaction. Details: %1 - + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 - - Cannot commit transaction. Details: %1 - + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 - - + + DbTree - - Databases - + + Databases + Databases - - Filter by name - + + Filter by name + Filter by name - - Copy - + + Copy + Copy - - Paste - + + Paste + Paste - - Select all - + + Select all + Select all - - Create a group - + + Create a group + Create a group - - Delete the group - + + Delete the group + Delete the group - - Rename the group - + + Rename the group + Rename the group - - Import - + + &Add a database + &Add a database - - Export the table - + + &Edit the database + &Edit the database - - Import into the table - + + &Remove the database + &Remove the database - - Populate table - + + &Connect to the database + &Connect to the database - - Create similar table - + + &Disconnect from the database + &Disconnect from the database - - Reset autoincrement sequence - + + Import + Import - - Add a column - + + &Export the database + &Export the database - - Edit the column - + + Vac&uum + Vac&uum - - Delete the column - + + &Integrity check + &Integrity check - - Delete selected items - + + Create a &table + Create a &table - - Clear filter - + + Edit the t&able + Edit the t&able - - &Add a database - + + Delete the ta&ble + Delete the ta&ble - - Execution from file cancelled. Any queries executed so far have been rolled back. - + + Export the table + Export the table - - &Edit the database - + + Import into the table + Import into the table - - &Remove the database - + + Populate table + Populate table - - &Connect to the database - + + Create similar table + Create similar table - - &Disconnect from the database - + + Reset autoincrement sequence + Reset autoincrement sequence - - &Export the database - + + Create an &index + Create an &index - - Con&vert database type - + + Edit the i&ndex + Edit the i&ndex - - Vac&uum - + + Delete the in&dex + Delete the in&dex - - &Integrity check - + + Create a trig&ger + Create a trig&ger - - Create a &table - + + Edit the trigg&er + Edit the trigg&er - - Edit the t&able - + + Delete the trigge&r + Delete the trigge&r - - Delete the ta&ble - + + Create a &view + Create a &view - - Create an &index - + + Edit the v&iew + Edit the v&iew - - Edit the i&ndex - + + Delete the vi&ew + Delete the vi&ew - - Delete the in&dex - + + Add a column + Add a column - - Create a trig&ger - + + Edit the column + Edit the column - - Edit the trigg&er - + + Delete the column + Delete the column - - Delete the trigge&r - + + Delete selected items + Delete selected items - - Create a &view - + + Clear filter + Clear filter - - Edit the v&iew - + + &Refresh all database schemas + &Refresh all database schemas - - Delete the vi&ew - + + Re&fresh selected database schema + Re&fresh selected database schema - - &Refresh all database schemas - + + + Erase table data + Erase table data - - Re&fresh selected database schema - + + Open file's directory + Open file's directory - - - Erase table data - + + Execute SQL from file + Execute SQL from file - - Open file's directory - + + Increase font size + database list + Increase font size - - Execute SQL from file - + + Decrease font size + database list + Decrease font size - - - Database - + + + Database + Database - - Grouping - + + Grouping + Grouping - - Generate query for table - + + Generate query for table + Generate query for table - - - Create group - + + + Create group + Create group - - Group name - + + Group name + Group name - - Entry with name %1 already exists in group %2. - + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. - - Delete group - + + Delete group + Delete group - - Are you sure you want to delete group %1? + + Are you sure you want to delete group %1? All objects from this group will be moved to parent group. - + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. - - Are you sure you want to remove database '%1' from the list? - + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? - - Are you sure you want to remove following databases from the list: + + Are you sure you want to remove following databases from the list: %1 - + Are you sure you want to remove following databases from the list: +%1 - - Remove database - + + Remove database + Remove database - - - Cannot import, because no import plugin is loaded. - + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. - - - Cannot export, because no export plugin is loaded. - + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. - - Vacuum (%1) - + + Vacuum (%1) + Vacuum (%1) - - Integrity check (%1) - + + Integrity check (%1) + Integrity check (%1) - - Reset autoincrement - + + Reset autoincrement + Reset autoincrement - - Are you sure you want to reset autoincrement value for table '%1'? - + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? - - An error occurred while trying to reset autoincrement value for table '%1': %2 - + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 - - Autoincrement value for table '%1' has been reset successfully. - + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. - - Are you sure you want to delete all data from table(s): %1? - + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? - - An error occurred while trying to delete data from table '%1': %2 - + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 - - All data has been deleted for table '%1'. - + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. - - Following objects will be deleted: %1. - + + Following objects will be deleted: %1. + Following objects will be deleted: %1. - - Following databases will be removed from list: %1. - + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. - - Remainig objects from deleted group will be moved in place where the group used to be. - + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. - - %1<br><br>Are you sure you want to continue? - + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? - - Delete objects - + + Delete objects + Delete objects + + + DbTreeItemDelegate - - Could not execute SQL, because application has failed to start transaction: %1 - + + error + dbtree labels + error - - Could not open file '%1' for reading: %2 - + + (system table) + database tree label + (system table) - - Could not execute SQL, because application has failed to commit the transaction: %1 - + + (virtual) + virtual table label + (virtual) - - Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - + + (system index) + database tree label + (system index) + + + DbTreeModel - - Finished executing %1 queries in %2 seconds. - + + Database: %1 + dbtree tooltip + Database: %1 - - Could not execute SQL due to error. - + + URI: + dbtree tooltip + URI: - - - DbTreeItemDelegate - - error - dbtree labels - + + Version: + dbtree tooltip + Version: - - (system table) - database tree label - + + File size: + dbtree tooltip + File size: - - (virtual) - virtual table label - + + Encoding: + dbtree tooltip + Encoding: - - (system index) - database tree label - + + Error: + dbtree tooltip + Error: - - - DbTreeModel - - Database: %1 - dbtree tooltip - + + Table : %1 + dbtree tooltip + Table : %1 - - Version: - dbtree tooltip - + + Columns (%1): + dbtree tooltip + Columns (%1): - - File size: - dbtree tooltip - + + Indexes (%1): + dbtree tooltip + Indexes (%1): - - Encoding: - dbtree tooltip - + + Triggers (%1): + dbtree tooltip + Triggers (%1): - - Error: - dbtree tooltip - + + Copy + Copy - - Table : %1 - dbtree tooltip - + + Move + Move - - Columns (%1): - dbtree tooltip - + + Include data + Include data - - Indexes (%1): - dbtree tooltip - + + Include indexes + Include indexes - - Triggers (%1): - dbtree tooltip - + + Include triggers + Include triggers - - Copy - + + Abort + Abort - - Move - + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. - - Include data - + + Referenced tables + Referenced tables - - Include indexes - + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 - - Include triggers - + + Name conflict + Name conflict - - Abort - + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: - - Could not add dropped database file '%1' automatically. Manual setup is necessary. - + + SQL statements conversion + SQL statements conversion - - Referenced tables - + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: - - Do you want to include following referenced tables as well: -%1 - + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + DdlHistoryWindow - - Name conflict - + + Filter by database: + Filter by database: - - Following object already exists in the target database. -Please enter new, unique name, or press '%1' to abort the operation: - + + Clear entire history + Clear entire history - - SQL statements conversion - + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 - - Following error occurred while converting SQL statements to the target SQLite version: - + + Clear history + Clear history - - Would you like to ignore those errors and proceed? - + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? - - - DdlHistoryWindow - - Filter by database: - + + DDL history + DDL history + + + DdlPreviewDialog - - -- Queries executed on database %1 (%2) --- Date and time of execution: %3 -%4 - + + Queries to be executed + Queries to be executed - - DDL history - + + Don't show again + Don't show again - - - DdlPreviewDialog + + + DebugConsole - - Queries to be executed - + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + EditorWindow - - Don't show again - + + SQL editor + SQL editor - - - DebugConsole - - SQLiteStudio Debug Console - + + Query + Query - - - EditorWindow - - Query - + + History + History - - History - + + Results in the separate tab + Results in the separate tab - - Results in the separate tab - + + Results below the query + Results below the query - - Results below the query - + + + SQL editor %1 + SQL editor %1 - - - SQL editor %1 - + + + Results + Results - - Results - + + Execute query + Execute query - - Execute query - + + Explain query + Explain query - - Explain query - + + Clear execution history + sql editor + Clear execution history - - Clear execution history - sql editor - + + Export results + sql editor + Export results - - Export results - sql editor - + + Create view from query + sql editor + Create view from query - - Create view from query - sql editor - + + Previous database + Previous database - - Previous database - + + Next database + Next database - - Next database - + + Show next tab + sql editor + Show next tab - - Show next tab - sql editor - + + Show previous tab + sql editor + Show previous tab - - Show previous tab - sql editor - + + Focus results below + sql editor + Focus results below - - Focus results below - sql editor - + + Focus SQL editor above + sql editor + Focus SQL editor above - - Focus SQL editor above - sql editor - + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries - - Delete selected SQL history entries - sql editor - + + Execute single query under cursor + Execute single query under cursor - - Active database (%1/%2) - + + Execute all queries in editor + Execute all queries in editor - - Query finished in %1 second(s). Rows affected: %2 - + + Active database (%1/%2) + Active database (%1/%2) - - Query finished in %1 second(s). - + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 - - Clear execution history - + + Query finished in %1 second(s). + Query finished in %1 second(s). - - Are you sure you want to erase the entire SQL execution history? This cannot be undone. - + + Clear execution history + Clear execution history - - Cannot export, because no export plugin is loaded. - + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. - - No database selected in the SQL editor. Cannot create a view for unknown database. - + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. - - Editor window "%1" has uncommitted data. - + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. - - + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + ErrorsConfirmDialog - - Errors - + + Errors + Errors - - Following errors occured: - + + Following errors occured: + Following errors occured: - - Would you like to proceed? - + + Would you like to proceed? + Would you like to proceed? - - + + ExecFromFileDialog - - Execute SQL from file - + + Execute SQL from file + Execute SQL from file - - Input file - + + Input file + Input file - - Path to file - + + Path to file + Path to file - - Browse for file - + + Browse for file + Browse for file - - Options - + + Options + Options - - File encoding - + + File encoding + File encoding - - Skip failing SQL statements - + + Skip failing SQL statements + Skip failing SQL statements - - SQL scripts (*.sql);;All files (*) - + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) - - Execute SQL file - + + Execute SQL file + Execute SQL file - - Please provide file to be executed. - + + Please provide file to be executed. + Please provide file to be executed. - - Provided file does not exist or cannot be read. - + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. - - + + ExportDialog - - Export - + + Export + Export - - What do you want to export? - + + What do you want to export? + What do you want to export? - - A database - + + A database + A database - - A single table - + + A single table + A single table - - Query results - + + Query results + Query results - - Table to export - + + Table to export + Table to export - - Database - + + Database + Database - - Table - + + Table + Table - - Options - + + Options + Options - - When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. - + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. - - Export table data - + + Export table data + Export table data - - Export table indexes - + + Export table indexes + Export table indexes - - Export table triggers - + + Export table triggers + Export table triggers - - Note, that exporting table indexes and triggers may be unsupported by some output formats. - + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. - - Select database objects to export - + + Select database objects to export + Select database objects to export - - Export data from tables - + + Export data from tables + Export data from tables - - Select all - + + Select all + Select all - - Deselect all - + + Deselect all + Deselect all - - - Database: - + + + Database: + Database: - - Query to export results for - + + Query to export results for + Query to export results for - - Query to be executed for results: - + + Query to be executed for results: + Query to be executed for results: - - Export format and options - + + Export format and options + Export format and options - - Export format - + + Export format + Export format - - Output - + + Output + Output - - Exported file path - + + Exported file path + Exported file path - - Clipboard - + + Clipboard + Clipboard - - File - + + File + File - - Exported text encoding: - + + Exported text encoding: + Exported text encoding: - - Export format options - + + Export format options + Export format options - - Cancel - + + Cancel + Cancel - - - - Select database to export. - + + + + Select database to export. + Select database to export. - - Select table to export. - + + Select table to export. + Select table to export. - - Enter valid query to export. - + + Enter valid query to export. + Enter valid query to export. - - Select at least one object to export. - + + Select at least one object to export. + Select at least one object to export. - - You must provide a file name to export to. - + + You must provide a file name to export to. + You must provide a file name to export to. - - Path you provided is an existing directory. You cannot overwrite it. - + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. - - The directory '%1' does not exist. - + + The directory '%1' does not exist. + The directory '%1' does not exist. - - The file '%1' exists and will be overwritten. - + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. - - All files (*) - + + All files (*) + All files (*) - - Pick file to export to - + + Pick file to export to + Pick file to export to - - Internal error during export. This is a bug. Please report it. - + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. - - + + FileExecErrorsDialog - - Execution errors - + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: - - Following errors were encountered during execution of SQL statements from the file: - + + SQL + SQL - - SQL - + + Error + Error - - Error - + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. - - Statements that were executed successfully were commited. - + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + FkComboBox - - Statements that were executed successfully were rolled back. - + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 - - + + FontEdit - - Choose font - font configuration - + + Choose font + font configuration + Choose font - - + + Form - - Active SQL formatter plugin - + + Active SQL formatter plugin + Active SQL formatter plugin - - + + FormView - - Commit row - form view - + + Commit row + form view + Commit row - - Rollback row - form view - + + Rollback row + form view + Rollback row - - First row - form view - + + First row + form view + First row - - Previous row - form view - + + Previous row + form view + Previous row - - Next row - form view - + + Next row + form view + Next row - - Last row - form view - + + Last row + form view + Last row - - Insert new row - form view - + + Insert new row + form view + Insert new row - - Delete current row - form view - + + Delete current row + form view + Delete current row - - + + FunctionsEditor - - Filter funtions - + + Filter functions + Filter functions - - Input arguments - + + Input arguments + Input arguments - - Undefined - + + Undefined + Undefined - - Databases - + + Databases + Databases - - Register in all databases - + + Register in all databases + Register in all databases - - Register in following databases: - + + Register in following databases: + Register in following databases: - - Type: - + + Type: + Type: - - Function name: - + + Function name: + Function name: - - Implementation language: - + + Implementation language: + Implementation language: - - Initialization code: - + + Deterministic + Deterministic - - - Function implementation code: - + + Initialization code: + Initialization code: - - Final step implementation code: - + + + Function implementation code: + Function implementation code: - - SQL function editor - + + Final step implementation code: + Final step implementation code: - - Commit all function changes - + + SQL functions editor + SQL functions editor - - Rollback all function changes - + + Commit all function changes + Commit all function changes - - Create new function - + + Rollback all function changes + Rollback all function changes - - Delete selected function - + + Create new function + Create new function - - Custom SQL functions manual - + + Delete selected function + Delete selected function - - Add function argument - + + Custom SQL functions manual + Custom SQL functions manual - - Rename function argument - + + Add function argument + Add function argument - - Delete function argument - + + Rename function argument + Rename function argument - - Move function argument up - + + Delete function argument + Delete function argument - - Move function argument down - + + Move function argument up + Move function argument up - - Scalar - + + Move function argument down + Move function argument down - - Aggregate - + + Scalar + Scalar - - Enter a non-empty, unique name of the function. - + + Aggregate + Aggregate - - Pick the implementation language. - + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. - - Per step code: - + + Pick the implementation language. + Pick the implementation language. - - Enter a non-empty implementation code. - + + Per step code: + Per step code: - - argument - new function argument name in function editor window - + + Enter a non-empty implementation code. + Enter a non-empty implementation code. - - Functions editor window has uncommitted modifications. - + + argument + new function argument name in function editor window + argument - - + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + ImportDialog - - Import data - + + Import data + Import data - - Table to import to - + + Table to import to + Table to import to - - Table - + + Table + Table - - Database - + + Database + Database - - Data source to import from - + + Data source to import from + Data source to import from - - Data source type - + + Data source type + Data source type - - Options - + + Options + Options - - Text encoding: - + + Text encoding: + Text encoding: - - Input file: - + + Input file: + Input file: - - <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> - + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> - - Ignore errors - + + Ignore errors + Ignore errors - - Data source options - + + Data source options + Data source options - - Cancel - + + Cancel + Cancel - - If you type table name that doesn't exist, it will be created. - + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. - - Enter the table name - + + Enter the table name + Enter the table name - - Select import plugin. - + + Select import plugin. + Select import plugin. - - You must provide a file to import from. - + + You must provide a file to import from. + You must provide a file to import from. - - The file '%1' does not exist. - + + The file '%1' does not exist. + The file '%1' does not exist. - - Path you provided is a directory. A regular file is required. - + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. - - Pick file to import from - + + Pick file to import from + Pick file to import from - - + + IndexDialog - - - Index - + + + Index + Index - - Column - + + Column + Column - - Collation - + + Sort + Sort - - Sort - + + Collation + Collation - - On table: - + + On table: + On table: - - Delete selected indexed expression - + + Delete selected indexed expression + Delete selected indexed expression - - Moves selected index column up in the order, making it more significant in the index. - + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. - - Moves selected index column down in the order, making it less significant in the index. - + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. - - Partial index condition - + + Partial index condition + Partial index condition - - Unique index - + + Unique index + Unique index - - Index name: - + + Index name: + Index name: - - Edit selected indexed expression - + + Edit selected indexed expression + Edit selected indexed expression - - Add indexed expression - + + Add indexed expression + Add indexed expression - - DDL - + + DDL + DDL - - Tried to open index dialog for closed or inexisting database. - + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. - - Could not process index %1 correctly. Unable to open an index dialog. - + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. - - Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. - + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. - - Pick the table for the index. - + + Pick the table for the index. + Pick the table for the index. - - Select at least one column. - + + Select at least one column. + Select at least one column. - - Enter a valid condition. - + + Enter a valid condition. + Enter a valid condition. - - default - index dialog - + + default + index dialog + default - - Sort order - table constraints - + + Sort order + table constraints + Sort order - - - Error - index dialog - + + + Error + index dialog + Error - - Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? - + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? - - An error occurred while executing SQL statements: + + An error occurred while executing SQL statements: %1 - + An error occurred while executing SQL statements: +%1 - - + + IndexExprColumnDialog - - Indexed expression - + + Indexed expression + Indexed expression - - Expression to index - + + Expression to index + Expression to index - - This expression is already indexed by the index. - + + This expression is already indexed by the index. + This expression is already indexed by the index. - - Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. - + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. - - Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. - + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. - - It's forbidden to use 'SELECT' statements in indexed expressions. - + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. - - Enter an indexed expression. - + + Enter an indexed expression. + Enter an indexed expression. - - Invalid expression. - + + Invalid expression. + Invalid expression. - - + + LanguageDialog - - Language - + + Language + Language - - Please choose language: - + + Please choose language: + Please choose language: - - + + MainWindow - - Database toolbar - + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar - - Structure toolbar - + + Tools + Tools - - Tools - + + Window list + Window list - - Window list - + + View toolbar + View toolbar - - View toolbar - + + Configuration widgets + Configuration widgets - - Configuration widgets - + + Syntax highlighting engines + Syntax highlighting engines - - Syntax highlighting engines - + + Data editors + Data editors - - Data editors - + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - - Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. - - Running in debug mode. Debug messages are printed to the standard output. - + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. - - You need to restart application to make the language change take effect. - + + Open SQL &editor + Open SQL &editor - - Next window - + + Open DDL &history + Open DDL &history - - Previous window - + + Open SQL &functions editor + Open SQL &functions editor - - Hide status field - + + Open code &snippets editor + Open code &snippets editor - - Open Debug Console - + + Open &collations editor + Open &collations editor - - Open CSS Console - + + Open ex&tension manager + Open ex&tension manager - - Bugs and feature &requests - + + &Import + &Import - - Window list - menubar view menu - + + E&xport + E&xport - - Open SQL &editor - + + Open confi&guration dialog + Open confi&guration dialog - - Open DDL &history - + + &Tile windows + &Tile windows - - Open SQL &functions editor - + + Tile windows &horizontally + Tile windows &horizontally - - Open &collations editor - + + Tile windows &vertically + Tile windows &vertically - - Open ex&tension manager - + + &Cascade windows + &Cascade windows - - &Import - + + Next window + Next window - - E&xport - + + Previous window + Previous window - - Open confi&guration dialog - + + Hide status field + Hide status field - - &Tile windows - + + Close &all windows + Close &all windows - - Tile windows &horizontally - + + Re&store recently closed window + Re&store recently closed window - - Tile windows &vertically - + + Close current &window + Close current &window - - &Cascade windows - + + Close &other windows + Close &other windows - - Close selected &window - + + Close windows on the &left + Close windows on the &left - - Close all windows &but selected - + + Close windows on the &right + Close windows on the &right - - Close &all windows - + + Re&name selected window + Re&name selected window - - Re&store recently closed window - + + Open Debug Console + Open Debug Console - - &Rename selected window - + + Open CSS Console + Open CSS Console - - Report a &bug - + + Report a &bug + Report a &bug - - Propose a new &feature - + + D&onate + D&onate - - &About - + + Propose a new &feature + Propose a new &feature - - &Licenses - + + &About + &About - - Open home &page - + + &Licenses + &Licenses - - Open fo&rum page - + + Open home &page + Open home &page - - User &Manual - + + User &Manual + User &Manual - - SQLite &documentation - + + SQLite &documentation + SQLite &documentation - - Check for &updates - + + Bugs and feature &requests + Bugs and feature &requests - - &Database - menubar - + + Quit + Quit - - &Structure - menubar - + + Check for &updates + Check for &updates - - &View - menubar - + + &Database + menubar + &Database - - &Tools - menubar - + + &Structure + menubar + &Structure - - &Help - + + &View + menubar + &View - - Could not set style: %1 - main window - + + Window list + menubar view menu + Window list - - Cannot export, because no export plugin is loaded. - + + &Tools + menubar + &Tools - - Cannot import, because no import plugin is loaded. - + + &Help + &Help - - Rename window - + + Could not set style: %1 + main window + Could not set style: %1 - - Enter new name for the window: - + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. - - New updates are available. <a href="%1">Click here for details</a>. - + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. - - You're running the most recent version. No updates are available. - + + Rename window + Rename window - - Database passed in command line parameters (%1) was already on the list under name: %2 - + + Enter new name for the window: + Enter new name for the window: - - Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 - + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. - - Could not add database %1 to list. - + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. - - + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + MdiWindow - - Uncommitted changes - + + Uncommitted changes + Uncommitted changes - - Close anyway - + + Close anyway + Close anyway - - Don't close - + + Don't close + Don't close - - + + MultiEditor - - Null value - multieditor - + + Null value + multieditor + Null value - - Configure editors for this data type - + + Configure editors for this data type + Configure editors for this data type - - Open another tab - + + Open another tab + Open another tab - - Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. - + + Foreign Key + Foreign Key - - Deleted - multieditor - + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} - - Read only - multieditor - + + Deleted + multieditor + Deleted - - + + + Read only + multieditor + Read only + + + MultiEditorBoolPlugin - - Boolean - + + Boolean + Boolean - - + + MultiEditorDatePlugin - - Date - + + Date + Date - - + + MultiEditorDateTimePlugin - - Date & time - + + Date & time + Date & time - - + + MultiEditorHexPlugin - - Hex - + + Hex + Hex - - + + MultiEditorNumericPlugin - - Number - numeric multi editor tab name - + + Number + numeric multi editor tab name + Number - - + + MultiEditorText - - Tab changes focus - + + Tab changes focus + Tab changes focus - - Cut - + + Cut + Cut - - Copy - + + Copy + Copy - - Paste - + + Paste + Paste - - Delete - + + Delete + Delete - - Undo - + + Undo + Undo - - Redo - + + Redo + Redo - - + + MultiEditorTextPlugin - - Text - + + Text + Text - - + + MultiEditorTimePlugin - - Time - + + Time + Time - - + + NewConstraintDialog - - New constraint - + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key - - - Primary Key - new constraint dialog - + + + Foreign Key + new constraint dialog + Foreign Key - - - Foreign Key - new constraint dialog - + + + Unique + new constraint dialog + Unique - - - Unique - new constraint dialog - + + + Check + new constraint dialog + Check - - - Check - new constraint dialog - + + Not NULL + new constraint dialog + Not NULL - - Not NULL - new constraint dialog - + + Collate + new constraint dialog + Collate - - Collate - new constraint dialog - + + Generated + new constraint dialog + Generated - - Default - new constraint dialog - + + Default + new constraint dialog + Default - - + + NewVersionDialog - - SQLiteStudio updates - + + SQLiteStudio updates + SQLiteStudio updates - - New updates are available! - + + New version is available! + New version is available! - - Component - + + Download new version! + Download new version! - - This application will be closed and the update installer will start to download and install all the updates. - + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. - - Update version - + + Open SQLiteStudio home page. + Open SQLiteStudio home page. - - Check for updates on startup - + + Read release notes && download package yourself. + Read release notes && download package yourself. - - Update to new version! - + + Just close this window. + Just close this window. - - Not now. - + + Check for updates on startup + Check for updates on startup - - Don't install the update and close this window. - + + Not now. + Not now. - - + + PopulateConfigDialog - - Populating configuration - + + Populating configuration + Populating configuration - - Configuring <b>%1</b> for column <b>%2</b> - + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> - - + + PopulateDialog - - Populate table - + + Populate table + Populate table - - Database - + + Database + Database - - Table - + + Table + Table - - Columns - + + Columns + Columns - - Number of rows to populate: - + + Number of rows to populate: + Number of rows to populate: - - Populate - populate dialog button - + + Populate + populate dialog button + Populate - - Abort - + + Abort + Abort - - Configure - + + Configure + Configure - - Populating configuration for this column is invalid or incomplete. - + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. - - Select database with table to populate - + + Select database with table to populate + Select database with table to populate - - Select table to populate - + + Select table to populate + Select table to populate - - You have to select at least one column. - + + You have to select at least one column. + You have to select at least one column. - - + + QObject - - Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). - + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. - - The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. - + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. - - Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. - + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). - - Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. - + + Cannot edit table generated columns. + Cannot edit table generated columns. - - Cannot edit results of query other than %1. - + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). - - Cannot edit columns that are result of aggregated %1 statements. - + + + + + on conflict: %1 + data view tooltip + on conflict: %1 - - Cannot edit columns that are result of %1 statement. - + + references table %1, column %2 + data view tooltip + references table %1, column %2 - - Cannot edit columns that are result of common table expression statement (%1). - + + condition: %1 + data view tooltip + condition: %1 - - - - - on conflict: %1 - data view tooltip - + + collation name: %1 + data view tooltip + collation name: %1 - - references table %1, column %2 - data view tooltip - + + Data grid view + Data grid view - - condition: %1 - data view tooltip - + + Edit current cell inline + Edit current cell inline - - collation name: %1 - data view tooltip - + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard - - Data grid view - + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard - - Copy cell(s) contents to clipboard - + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard - - Copy cell(s) contents together with header to clipboard - + + Set empty value to selected cell(s) + Set empty value to selected cell(s) - - Paste cell(s) contents from clipboard - + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) - - Set empty value to selected cell(s) - + + Commit changes to cell(s) contents + Commit changes to cell(s) contents - - Set NULL value to selected cell(s) - + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents - - Commit changes to cell(s) contents - + + Delete selected data row + Delete selected data row - - Rollback changes to cell(s) contents - + + Insert new data row + Insert new data row - - Delete selected data row - + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor - - Insert new data row - + + Toggle the height adjustment of rows + Toggle the height adjustment of rows - - Open contents of selected cell in a separate editor - + + Increase font size + data view + Increase font size - - Total pages available: %1 - + + Decrease font size + data view + Decrease font size - - Total rows loaded: %1 - + + Total pages available: %1 + Total pages available: %1 - - Data view (both grid and form) - + + Total rows loaded: %1 + Total rows loaded: %1 - - Refresh data - + + Data view (both grid and form) + Data view (both grid and form) - - Switch to grid view of the data - + + Refresh data + Refresh data - - Switch to form view of the data - + + Switch to grid view of the data + Switch to grid view of the data - - Database list - + + Switch to form view of the data + Switch to form view of the data - - Delete selected item - + + Database list + Database list - - Clear filter contents - + + Delete selected item + Delete selected item - - Refresh schema - + + Clear filter contents + Clear filter contents - - Refresh all schemas - + + Refresh schema + Refresh schema - - Add database - + + Refresh all schemas + Refresh all schemas - - Select all items - + + Add database + Add database - - Copy selected item(s) - + + Select all items + Select all items - - - - Paste from clipboard - + + Copy selected item(s) + Copy selected item(s) - - Tables - + + + + Paste from clipboard + Paste from clipboard - - Indexes - + + Increase font size + database list + Increase font size - - Triggers - + + Decrease font size + database list + Decrease font size - - Views - + + Tables + Tables - - Columns - + + Indexes + Indexes - - Data form view - + + Triggers + Triggers - - Commit changes for current row - + + Views + Views - - Rollback changes for current row - + + Columns + Columns - - Go to first row on current page - + + Data form view + Data form view - - Go to next row - + + Commit changes for current row + Commit changes for current row - - Go to previous row - + + Rollback changes for current row + Rollback changes for current row - - Go to last row on current page - + + Go to first row on current page + Go to first row on current page - - Insert new row - + + Go to next row + Go to next row - - Delete current row - + + Go to previous row + Go to previous row - - Main window - + + Go to last row on current page + Go to last row on current page - - Open SQL editor - + + Insert new row + Insert new row - - Previous window - + + Delete current row + Delete current row - - Next window - + + Main window + Main window - - Hide status area - + + Open SQL editor + Open SQL editor - - Open configuration dialog - + + Open DDL history window + Open DDL history window - - Open Debug Console - + + Open snippets editor window + Open snippets editor window - - Open CSS Console - + + Open function editor window + Open function editor window - - Cell text value editor - + + Open collation editor window + Open collation editor window - - - Cut selected text - + + Open extension manager window + Open extension manager window - - - Copy selected text - + + Previous window + Previous window - - - Delete selected text - + + Next window + Next window - - - Undo - + + Hide status area + Hide status area - - - Redo - + + Open user manual + Open user manual - - SQL editor input field - + + Open configuration dialog + Open configuration dialog - - Select whole editor contents - + + Open Debug Console + Open Debug Console - - Save contents into a file - + + Open CSS Console + Open CSS Console - - Load contents from a file - + + Open the About dialog + Open the About dialog - - Find in text - + + Quit the application + Quit the application - - Find next - + + Cell text value editor + Cell text value editor - - Find previous - + + + Cut selected text + Cut selected text - - Replace in text - + + + Copy selected text + Copy selected text - - Delete current line - + + + Delete selected text + Delete selected text - - Request code assistant - + + + Undo + Undo - - Format contents - + + + Redo + Redo - - Move selected block of text one line down - + + SQL editor input field + SQL editor input field - - Move selected block of text one line up - + + Select whole editor contents + Select whole editor contents - - Copy selected block of text and paste it a line below - + + Save contents into a file + Save contents into a file - - Copy selected block of text and paste it a line above - + + Load contents from a file + Load contents from a file - - Toggle comment - + + Find in text + Find in text - - All SQLite databases - + + Find next + Find next - - All files - + + Find previous + Find previous - - - Database file - + + Replace in text + Replace in text - - SQL editor window - + + Delete current line + Delete current line - - Execute query - + + Request code assistant + Request code assistant - - Execute "%1" query - + + Format contents + Format contents - - Switch current working database to previous on the list - + + Move selected block of text one line down + Move selected block of text one line down - - Switch current working database to next on the list - + + Move selected block of text one line up + Move selected block of text one line up - - Go to next editor tab - + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below - - Go to previous editor tab - + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above - - Move keyboard input focus to the results view below - + + Toggle comment + Toggle comment - - Move keyboard input focus to the SQL editor above - + + Increase font size + sql editor + Increase font size - - Delete selected SQL history entries - + + Decrease font size + sql editor + Decrease font size - - Table window - + + All SQLite databases + All SQLite databases - - Refresh table structure - + + All files + All files - - Add new column - + + Select database file + Select database file - - Edit selected column - + + Select + Select - - Delete selected column - + + File type + File type - - Export table data - + + SQL editor window + SQL editor window - - Import data to the table - + + Execute query + Execute query - - Add new table constraint - + + Execute single query under cursor + Execute single query under cursor - - Edit selected table constraint - + + Execute all queries in editor + Execute all queries in editor - - Delete selected table constraint - + + Execute "%1" query + Execute "%1" query - - Refresh table index list - + + Switch current working database to previous on the list + Switch current working database to previous on the list - - Add new index - + + Switch current working database to next on the list + Switch current working database to next on the list - - Edit selected index - + + Go to next editor tab + Go to next editor tab - - Delete selected index - + + Go to previous editor tab + Go to previous editor tab - - Refresh table trigger list - + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below - - - Add new trigger - + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above - - - Edit selected trigger - + + Delete selected SQL history entries + Delete selected SQL history entries - - - Delete selected trigger - + + Table window + Table window - - - Go to next tab - + + Commit the table structure + Commit the table structure - - - Go to previous tab - + + Rollback pending changes in the table structure + Rollback pending changes in the table structure - - A view window - + + Refresh table structure + Refresh table structure - - Refresh view trigger list - + + Add new column + Add new column - - + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + QuitConfirmDialog - - Uncommitted changes - + + Uncommitted changes + Uncommitted changes - - Are you sure you want to quit the application? + + Are you sure you want to quit the application? Following items are pending: - + Are you sure you want to quit the application? + +Following items are pending: - - + + SearchTextDialog - - Find or replace - + + Find or replace + Find or replace - - Find: - + + Find: + Find: - - Case sensitive - + + Case sensitive + Case sensitive - - Search backwards - + + Search backwards + Search backwards - - Regular expression matching - + + Regular expression matching + Regular expression matching - - Replace && + + Replace && find next - + Replace && +find next - - Replace with: - + + Replace with: + Replace with: - - Replace all - + + Replace all + Replace all - - Find - + + Find + Find - - + + SortDialog - - Sort by columns - + + Sort by columns + Sort by columns - - - Column - + + + Column + Column - - - Order - + + + Order + Order - - Sort by: %1 - + + Sort by: %1 + Sort by: %1 - - Move column up - + + Move column up + Move column up - - Move column down - + + Move column down + Move column down - - + + SqlEditor - - Cut - sql editor - + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste - - Copy - sql editor - + + Delete + sql editor + Delete - - Paste - sql editor - + + Select all + sql editor + Select all - - Delete - sql editor - + + Undo + sql editor + Undo - - Select all - sql editor - + + Redo + sql editor + Redo - - Undo - sql editor - + + Complete + sql editor + Complete - - Redo - sql editor - + + Format SQL + sql editor + Format SQL - - Complete - sql editor - + + Save SQL to file + sql editor + Save SQL to file - - Format SQL - sql editor - + + Select file to save SQL + sql editor + Select file to save SQL - - Save SQL to file - sql editor - + + Load SQL from file + sql editor + Load SQL from file - - Select file to save SQL - sql editor - + + Delete line + sql editor + Delete line - - Load SQL from file - sql editor - + + Move block down + sql editor + Move block down - - Delete line - sql editor - + + Move block up + sql editor + Move block up - - Move block down - sql editor - + + Copy block down + sql editor + Copy block down - - Move block up - sql editor - + + Copy up down + sql editor + Copy up down - - Copy block down - sql editor - + + Find + sql editor + Find - - Copy up down - sql editor - + + Find next + sql editor + Find next - - Find - sql editor - + + Find previous + sql editor + Find previous - - Find next - sql editor - + + Replace + sql editor + Replace - - Find previous - sql editor - + + Toggle comment + sql editor + Toggle comment - - Replace - sql editor - + + Increase font size + sql editor + Increase font size - - Toggle comment - sql editor - + + Decrease font size + sql editor + Decrease font size - - Could not open file '%1' for writing: %2 - + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 - - Saved SQL contents to file: %1 - + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 - - Syntax completion can be used only when a valid database is set for the SQL editor. - + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. - - Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. - + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. - - Save to file - + + Save to file + Save to file - - SQL scripts (*.sql);;All files (*) - + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) - - Open file - + + Open file + Open file - - Could not open file '%1' for reading: %2 - + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 - - Reached the end of document. Hit the find again to restart the search. - + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. - - + + SqlQueryItem - - Column: - data view tooltip - + + Committing error: + data view tooltip + Committing error: - - Data type: - data view - + + Column: + data view tooltip + Column: - - Table: - data view tooltip - + + Data type: + data view + Data type: - - Constraints: - data view tooltip - + + Table: + data view tooltip + Table: - - Cannot load the data for a cell that refers to the already closed database. - + + Constraints: + data view tooltip + Constraints: - - + + SqlQueryItemDelegate - - - - - - Cannot edit this cell. Details: %1 - + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 - - The row is marked for deletion. - + + The row is marked for deletion. + The row is marked for deletion. - - - Structure of this table has changed since last data was loaded. Reload the data to proceed. - + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. - - Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). - + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). - - Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. - + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. - - + + SqlQueryModel - - - Only one query can be executed simultaneously. - + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. - - Uncommitted data - + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. - - There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. - + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 - - Cannot commit the data for a cell that refers to the already closed database. - + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 - - Could not begin transaction on the database. Details: %1 - + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 - - An error occurred while committing the transaction: %1 - + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. - - An error occurred while rolling back the transaction: %1 - + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 - - Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. - + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - - An error occurred while committing the data: %1 - + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 - - Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - + + Error while loading query results: %1 + Error while loading query results: %1 - - - Error while executing SQL query on database '%1': %2 - + + Insert multiple rows + Insert multiple rows - - Error while loading query results: %1 - + + Number of rows to insert: + Number of rows to insert: - - Insert multiple rows - + + Delete rows + Delete rows - - Number of rows to insert: - + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? - - + + SqlQueryView - - Go to referenced row in... - + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... - - Copy - + + Paste + Paste - - Copy as... - + + Paste as... + Paste as... - - Paste - + + Set NULL values + Set NULL values - - Paste as... - + + Erase values + Erase values - - Set NULL values - + + Commit + Commit - - Erase values - + + Rollback + Rollback - - Edit value in editor - + + Commit selected cells + Commit selected cells - - Commit - + + Rollback selected cells + Rollback selected cells - - Copy with headers - + + Edit current cell inline + Edit current cell inline - - Rollback - + + Define columns to sort by + Define columns to sort by - - Commit selected cells - + + Remove custom sorting + Remove custom sorting - - Rollback selected cells - + + Insert row + Insert row - - Define columns to sort by - + + Insert multiple rows + Insert multiple rows - - Remove custom sorting - + + Delete selected row + Delete selected row - - Insert row - + + Adjust height of rows + Adjust height of rows - - Insert multiple rows - + + Increase font size + data view + Increase font size - - Delete selected row - + + Decrease font size + data view + Decrease font size - - Show value in a viewer - + + Invert selection + data view + Invert selection - - Generate query for selected cells - + + Edit value in editor + Edit value in editor - - No items selected to paste clipboard contents to. - + + Show value in a viewer + Show value in a viewer - - Go to referenced row in table '%1' - + + Generate query for selected cells + Generate query for selected cells - - table '%1' - + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. - - Referenced row (%1) - + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 - - Trim pasted text? - + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. - - The pasted text contains leading or trailing white space. Trim it automatically? - + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 - - Edit value - + + The row is marked for deletion. + The row is marked for deletion. - - + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + SqlTableModel - - Error while committing new row: %1 - + + Error while committing new row: %1 + Error while committing new row: %1 - - Error while deleting row from table %1: %2 - + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 - - + + SqliteExtensionEditor - - Filter extensions - + + Filter extensions + Filter extensions - - Leave empty to use default function - + + Leave empty to use default function + Leave empty to use default function - - Extension file - + + Extension file + Extension file - - Initialization function - + + Initialization function + Initialization function - - Databases - + + Databases + Databases - - Register in all databases - + + Register in all databases + Register in all databases - - Register in following databases: - + + Register in following databases: + Register in following databases: - - Extension manager window has uncommitted modifications. - + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. - - Extension manager - + + Extension manager + Extension manager - - Commit all extension changes - + + Commit all extension changes + Commit all extension changes - - Rollback all extension changes - + + Rollback all extension changes + Rollback all extension changes - - Add new extension - + + Add new extension + Add new extension - - Remove selected extension - + + Remove selected extension + Remove selected extension - - Editing extensions manual - + + Editing extensions manual + Editing extensions manual - - File with given path does not exist or is not readable. - + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. - - Unable to load extension: %1 - + + Unable to load extension: %1 + Unable to load extension: %1 - - Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - - Dynamic link libraries (*.dll);;All files (*) - + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) - - Shared objects (*.so);;All files (*) - + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) - - Dynamic libraries (*.dylib);;All files (*) - + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) - - All files (*) - + + All files (*) + All files (*) - - Open file - + + Open file + Open file - - + + StatusField - - Status - + + Status + Status - - Copy - + + Copy + Copy - - Clear - + + Clear + Clear - - + + TableConstraintsModel - - Type - table constraints - + + Type + table constraints + Type - - Details - table constraints - + + Details + table constraints + Details - - Name - table constraints - + + Name + table constraints + Name - - + + TableForeignKeyPanel - - Foreign table: - + + Foreign table: + Foreign table: - - SQLite 2 does not support foreign keys officially, -but it's okay to use them anyway. - + + Columns + Columns - - Columns - + + Local column + Local column - - Local column - + + Foreign column + Foreign column - - Foreign column - + + Reactions + Reactions - - Reactions - + + Deferred foreign key + Deferred foreign key - - Deferred foreign key - + + Named constraint + Named constraint - - Named constraint - + + Constraint name + Constraint name - - Constraint name - + + Pick the foreign column. + Pick the foreign column. - - Pick the foreign column. - + + Pick the foreign table. + Pick the foreign table. - - Pick the foreign table. - + + Select at least one foreign column. + Select at least one foreign column. - - Select at least one foreign column. - + + Enter a name of the constraint. + Enter a name of the constraint. - - Enter a name of the constraint. - + + Foreign column + table constraints + Foreign column - - - Foreign column - table constraints - - - - + + TablePrimaryKeyAndUniquePanel - - Columns - + + Columns + Columns - - Column - + + Column + Column - - Collation - + + Collation + Collation - - Sort - + + Sort + Sort - - Valid only for a single column with INTEGER data type - + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type - - Autoincrement - + + Autoincrement + Autoincrement - - Named constraint - + + Named constraint + Named constraint - - Constraint name - + + Constraint name + Constraint name - - On conflict - + + On conflict + On conflict - - Collate - table constraints - + + Collate + table constraints + Collate - - Sort order - table constraints - + + Sort order + table constraints + Sort order - - Select at least one column. - + + Select at least one column. + Select at least one column. - - Enter a name of the constraint. - + + Enter a name of the constraint. + Enter a name of the constraint. - - + + TableStructureModel - - Name - table structure columns - + + Name + table structure columns + Name - - Data type - table structure columns - + + Data type + table structure columns + Data type - - Primary + + Primary Key - table structure columns - + table structure columns + Primary +Key - - Foreign + + Foreign Key - table structure columns - + table structure columns + Foreign +Key - - Unique - table structure columns - + + Unique + table structure columns + Unique - - Check - table structure columns - + + Check + table structure columns + Check - - Not + + Not NULL - table structure columns - + table structure columns + Not +NULL - - Collate - table structure columns - + + Collate + table structure columns + Collate - - Default value - table structure columns - + + Generated + table structure columns + Generated - - + + + Default value + table structure columns + Default value + + + TableWindow - - Structure - + + Structure + Structure + + + + Table name: + Table name: - - Table name: - + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> - - - Data - + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> - - Constraints - + + + Data + Data - - Indexes - + + Constraints + Constraints - - Triggers - + + Indexes + Indexes - - DDL - + + Triggers + Triggers - - Export table - table window - + + DDL + DDL - - Import data to table - table window - + + Export table + table window + Export table - - Populate table - table window - + + Import data to table + table window + Import data to table - - Refresh structure - table window - + + Populate table + table window + Populate table - - Commit structure changes - table window - + + Refresh structure + table window + Refresh structure - - Rollback structure changes - table window - + + Commit structure changes + table window + Commit structure changes - - Add column - table window - + + Rollback structure changes + table window + Rollback structure changes - - Edit column - table window - + + Add column + table window + Add column - - - Delete column - table window - + + Edit column + table window + Edit column - - Move column up - table window - + + + Delete column + table window + Delete column - - Move column down - table window - + + Move column up + table window + Move column up - - Create similar table - table window - + + Move column down + table window + Move column down - - Reset autoincrement value - table window - + + Create similar table + table window + Create similar table - - Add table constraint - table window - + + Reset autoincrement value + table window + Reset autoincrement value - - Edit table constraint - table window - + + Add table constraint + table window + Add table constraint - - Delete table constraint - table window - + + Edit table constraint + table window + Edit table constraint - - Move table constraint up - table window - + + Delete table constraint + table window + Delete table constraint - - Move table constraint down - table window - + + Move table constraint up + table window + Move table constraint up - - Add table primary key - table window - + + Move table constraint down + table window + Move table constraint down - - Add table foreign key - table window - + + Add table primary key + table window + Add table primary key - - Add table unique constraint - table window - + + Add table foreign key + table window + Add table foreign key - - Add table check constraint - table window - + + Add table unique constraint + table window + Add table unique constraint - - Refresh index list - table window - + + Add table check constraint + table window + Add table check constraint - - Create index - table window - + + Refresh index list + table window + Refresh index list - - Edit index - table window - + + + Create index + table window + Create index - - Delete index - table window - + + Edit index + table window + Edit index - - Refresh trigger list - table window - + + Delete index + table window + Delete index - - Create trigger - table window - + + Refresh trigger list + table window + Refresh trigger list - - Edit trigger - table window - + + + Create trigger + table window + Create trigger - - Delete trigger - table window - + + Edit trigger + table window + Edit trigger - - Are you sure you want to delete column '%1'? - table window - + + Delete trigger + table window + Delete trigger - - Following problems will take place while modifying the table. + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. Would you like to proceed? - table window - + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification - - Table modification - table window - + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 - - Could not load data for table %1. Error details: %2 - + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. - - Could not process the %1 table correctly. Unable to open a table window. - + + Database + Database - - Could not restore window %1, because no database or table was stored in session for this window. - + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. - - Could not restore window '%1', because no database or table was stored in session for this window. - + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. - - Could not restore window '%1', because database %2 could not be resolved. - + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. - - Could not restore window '%1'', because the table %2 doesn't exist in the database %3. - + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. - - - New table %1 - + + + New table %1 + New table %1 - - Committed changes for table '%1' successfully. - + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. - - Committed changes for table '%1' (named before '%2') successfully. - + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. - - Could not commit table structure. Error message: %1 - table window - + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 - - Reset autoincrement - + + Reset autoincrement + Reset autoincrement - - Are you sure you want to reset autoincrement value for table '%1'? - + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? - - An error occurred while trying to reset autoincrement value for table '%1': %2 - + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 - - Autoincrement value for table '%1' has been reset successfully. - + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. - - Empty name - + + Empty name + Empty name - - A blank name for the table is allowed in SQLite, but it is not recommended. + + A blank name for the table is allowed in SQLite, but it is not recommended. Are you sure you want to create a table with blank name? - + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? - - Cannot create a table without at least one column. - + + Cannot create a table without at least one column. + Cannot create a table without at least one column. - - Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. - + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. - - Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. - + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. - - Are you sure you want to delete table constraint '%1'? - table window - + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 - - Delete constraint - table window - + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? - - Cannot export, because no export plugin is loaded. - + + Delete constraint + table window + Delete constraint - - Cannot import, because no import plugin is loaded. - + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. - - Uncommitted changes - + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. - - There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. Do you want to commit the structure, or do you want to go back to the structure tab? - + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? - - Go back to structure tab - + + Go back to structure tab + Go back to structure tab - - Commit modifications and browse data. - + + Commit modifications and browse data. + Commit modifications and browse data. - - Name - table window indexes - + + Name + table window indexes + Name - - Unique - table window indexes - + + Unique + table window indexes + Unique - - Columns - table window indexes - + + Columns + table window indexes + Columns - - Partial index condition - table window indexes - + + Partial index condition + table window indexes + Partial index condition - - Name - table window triggers - + + Name + table window triggers + Name - - Event - table window triggers - + + Event + table window triggers + Event - - Condition - table window triggers - + + Condition + table window triggers + Condition - - Details - table window triggers - + + Details + table window triggers + Details - - Table window "%1" has uncommitted structure modifications and data. - + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. - - Table window "%1" has uncommitted data. - + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. - - Table window "%1" has uncommitted structure modifications. - + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. - - + + TriggerColumnsDialog - - Trigger columns - + + Trigger columns + Trigger columns - - Triggering columns: - + + Triggering columns: + Triggering columns: - - Select all - + + Select all + Select all - - Deselect all - + + Deselect all + Deselect all - - + + TriggerDialog - - - Trigger - + + + Trigger + Trigger - - On table: - + + On table: + On table: - - Action: - + + Action: + Action: - - - <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> - + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> - - Pre-condition: - + + Pre-condition: + Pre-condition: - - The scope is still not fully supported by the SQLite database. - + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. - - Trigger name: - + + Trigger name: + Trigger name: - - When: - + + When: + When: - - List of columns for UPDATE OF action. - + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. - - Scope: - + + Scope: + Scope: - - Code: - + + Code: + Code: - - Trigger statements to be executed. - + + Trigger statements to be executed. + Trigger statements to be executed. - - DDL - + + DDL + DDL - - On view: - + + On view: + On view: - - Could not process trigger %1 correctly. Unable to open a trigger dialog. - + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. - - Enter a valid condition. - + + Enter a valid condition. + Enter a valid condition. - - Enter a valid trigger code. - + + Enter a valid trigger code. + Enter a valid trigger code. - - Error - trigger dialog - + + Error + trigger dialog + Error - - An error occurred while executing SQL statements: + + An error occurred while executing SQL statements: %1 - + An error occurred while executing SQL statements: +%1 - - + + VersionConvertSummaryDialog - - Database version convert - + + Database version convert + Database version convert - - Following changes to the SQL statements will be made: - + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: - - Before - + + Before + Before - - After - + + After + After - - + + ViewWindow - - Query - + + Query + Query + + + + View name: + View name: - - View name: - + + Output column names + Output column names - - Output column names - + + + Data + Data - - - Data - + + Triggers + Triggers - - Triggers - + + DDL + DDL - - DDL - + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. - - - Could not restore window '%1', because no database or view was stored in session for this window. - + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. - - Could not restore window '%1', because database %2 could not be resolved. - + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. - - Could not restore window '%1', because database %2 could not be open. - + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. - - Could not restore window '%1', because the view %2 doesn't exist in the database %3. - + + + New view %1 + New view %1 - - - New view %1 - + + Database + Database - - Refresh the view - view window - + + Refresh the view + view window + Refresh the view - - Commit the view changes - view window - + + Commit the view changes + view window + Commit the view changes - - Rollback the view changes - view window - + + Rollback the view changes + view window + Rollback the view changes - - Explicit column names - + + Explicit column names + Explicit column names - - Generate output column names automatically basing on result columns of the view. - + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. - - Add column - view window - + + Add column + view window + Add column - - Edit column - view window - + + Edit column + view window + Edit column - - Delete column - view window - + + Delete column + view window + Delete column - - Move column up - view window - + + Move column up + view window + Move column up - - Move column down - view window - + + Move column down + view window + Move column down - - Refresh trigger list - view window - + + Refresh trigger list + view window + Refresh trigger list - - Create new trigger - view window - + + Create new trigger + view window + Create new trigger - - Edit selected trigger - view window - + + Edit selected trigger + view window + Edit selected trigger - - Delete selected trigger - view window - + + Delete selected trigger + view window + Delete selected trigger - - View window "%1" has uncommitted structure modifications and data. - + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. - - View window "%1" has uncommitted data. - + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. - - View window "%1" has uncommitted structure modifications. - + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. - - Could not load data for view %1. Error details: %2 - + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 - - Uncommitted changes - + + Uncommitted changes + Uncommitted changes - - There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. Do you want to commit the structure, or do you want to go back to the structure tab? - + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab - - Go back to structure tab - + + Commit modifications and browse data. + Commit modifications and browse data. - - Commit modifications and browse data. - + + View '%1' was committed successfully. + View '%1' was committed successfully. - - Committed changes for view '%1' successfully. - + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. - - Committed changes for view '%1' (named before '%2') successfully. - + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. - - Could not commit view changes. Error message: %1 - view window - + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 - - Override columns - + + Override columns + Override columns - - Currently defined columns will be overriden. Do you want to continue? - + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? - - Could not determinate columns returned from the view. The query is problably incomplete or contains errors. - + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. - - Name - view window triggers - + + Name + view window triggers + Name - - Instead of - view window triggers - + + Instead of + view window triggers + Instead of - - Condition - view window triggers - + + Condition + view window triggers + Condition - - Details - table window triggers - + + Details + table window triggers + Details - - Could not process the %1 view correctly. Unable to open a view window. - + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. - - Empty name - + + Empty name + Empty name - - A blank name for the view is allowed in SQLite, but it is not recommended. + + A blank name for the view is allowed in SQLite, but it is not recommended. Are you sure you want to create a view with blank name? - + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? - - The SELECT statement could not be parsed. Please correct the query and retry. + + The SELECT statement could not be parsed. Please correct the query and retry. Details: %1 - + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 - - The view could not be modified due to internal SQLiteStudio error. Please report this! - + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! - - The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. - + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. - - Following problems will take place while modifying the view. + + Following problems will take place while modifying the view. Would you like to proceed? - view window - + view window + Following problems will take place while modifying the view. +Would you like to proceed? - - View modification - view window - + + View modification + view window + View modification - - + + WidgetCover - - Interrupt - + + Interrupt + Interrupt - + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.qm b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.qm deleted file mode 100644 index 4a142a7..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.qm and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.ts deleted file mode 100644 index 3962775..0000000 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.ts +++ /dev/null @@ -1,7379 +0,0 @@ - - - - - AboutDialog - - - About SQLiteStudio and licenses - О программе SQLiteStudio и лицензиÑÑ… - - - - About - О программе - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">БеÑплатный кроÑÑплатформенный менеджер баз данных SQLite Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¼ иÑходным кодом.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Ðвтор и активный разработчик:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - Licenses - Лицензии - - - - Environment - СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ñреда - - - - Icon directories - Каталоги иконок - - - - Form directories - Каталоги форм - - - - Plugin directories - Каталоги модулей - - - - Application directory - Каталог программы - - - - SQLite 3 version: - ВерÑÐ¸Ñ SQLite 3: - - - - Configuration directory - Каталог конфигурации - - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio вер.%1</span></p><p align="center">БеÑплатный кроÑÑплатформенный менеджер баз данных SQLite Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¼ иÑходным кодом.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Ðвтор и активный разработчик:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - Qt version: - ВерÑÐ¸Ñ Qt: - - - - Portable distribution. - ÐŸÐ¾Ñ€Ñ‚Ð°Ð±ÐµÐ»ÑŒÐ½Ð°Ñ Ð²ÐµÑ€ÑиÑ. - - - - MacOS X application boundle distribution. - Пакет Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ MacOS X. - - - - Operating system managed distribution. - ВерÑиÑ, управлÑÐµÐ¼Ð°Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹ ÑиÑтемой. - - - - Copy - Копировать - - - - <h3>Table of contents:</h3><ol>%2</ol> - <h3>Содержание:</h3><ol>%2</ol> - - - - BindParamsDialog - - - Query parameters - Параметры запроÑа - - - - Please provide values for query parameters - ПожалуйÑта укажите Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð² запроÑа - - - - BugDialog - - Bugs and ideas - Ошибки и Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ - - - Reporter - Отправитель - - - E-mail address - ÐÐ´Ñ€ÐµÑ e-mail - - - Log in - Вход - - - Short description - Краткое опиÑание - - - Detailed description - Подробное опиÑание - - - Show more details - Показать дополнительную информацию - - - SQLiteStudio version - ВерÑÐ¸Ñ SQLiteStudio - - - Operating system - ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ ÑиÑтема - - - Loaded plugins - Загруженные модули - - - Send - Отправить - - - You can see all your reported bugs and ideas by selecting menu '%1' and then '%2'. - Ð’Ñ‹ можете проÑмотреть вÑе отправленные вами отчёты об ошибках и предложениÑ, выбрав в меню '%1' пункт '%2'. - - - A bug report sent successfully. - Отчёт об ошибке уÑпешно отправлен. - - - An error occurred while sending a bug report: %1 -%2 - При отправке отчёта об ошибке возникла проблема: %1 -%2 - - - You can retry sending. The contents will be restored when you open a report dialog after an error like this. - Ð’Ñ‹ можете повторить отправку. ПоÑле такой ошибки Ñодержимое полей окна отправки отчёта будет воÑÑтановлено при повторном открытии. - - - An idea proposal sent successfully. - Предложение по улучшению было уÑпешно отправлено. - - - An error occurred while sending an idea proposal: %1 -%2 - При отправке Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾ улучшению возникла проблема: %1 -%2 - - - A bug report - Отчёт об ошибке - - - Describe problem in few words - Опишите проблему в неÑкольких Ñловах - - - Describe problem and how to reproduce it - Опишите проблему и шаги Ð´Ð»Ñ ÐµÑ‘ воÑÐ¿Ñ€Ð¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ - - - A new feature idea - Предложение по улучшению функционала - - - A title for your idea - Ðазвание Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ - - - Describe your idea in more details - Опишите ваше предложение более подробно - - - Reporting as an unregistered user, using e-mail address. - Отправка от незарегиÑтрированного пользователÑ, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð°Ð´Ñ€ÐµÑ e-mail - - - Reporting as a registered user. - Отправка от зарегиÑтрированного Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ - - - Log out - Выход - - - Providing true email address will make it possible to contact you regarding your report. To learn more, press 'help' button on the right side. - Указание дейÑтвительного адреÑа e-mail поможет ÑвÑзатьÑÑ Ñ Ð²Ð°Ð¼Ð¸ каÑательно вашего отчёта. Ð”Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ð¹ информации нажмите кнопку Помощь Ñправа. - - - Enter vaild e-mail address, or log in. - Введите дейÑтвительный Ð°Ð´Ñ€ÐµÑ e-mail либо выполните вход. - - - Short description requires at least 10 characters, but not more than 100. Longer description can be entered in the field below. - Краткое опиÑание должно Ñодержать от 10 до 100 Ñимволов. Более подробное опиÑание можно ввеÑти в поле ниже. - - - Long description requires at least 30 characters. - Детальное опиÑание должно Ñодержать как минимум 30 Ñимволов. - - - - BugReportHistoryWindow - - Title - Заголовок - - - Reported at - Дата отправки - - - URL - URL - - - Reports history - ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¾Ñ‚Ñ‡Ñ‘Ñ‚Ð¾Ð² - - - Clear reports history - ОчиÑтить иÑторию отчётов - - - Delete selected entry - Удалить выбранную запиÑÑŒ - - - Invalid response from server. - Ðекорректный ответ Ñервера. - - - - BugReportLoginDialog - - Log in - Вход - - - Credentials - Данные Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ð° - - - Login: - Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ: - - - Password: - Пароль: - - - Validation - Проверка - - - Validate - Проверить - - - Validation result message - Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸ - - - Abort - Прервать - - - A login must be at least 2 characters long. - Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾ ÑоÑтоÑть как минимум из двух Ñимволов. - - - A password must be at least 5 characters long. - Пароль должен ÑоÑтоÑть как минимум из пÑти Ñимволов. - - - Valid - Верно - - - - CollationsEditor - - - Filter collations - Отфильтровать ÑÑ€Ð°Ð²Ð½ÐµÐ½Ð¸Ñ - - - - Collation name: - Ð˜Ð¼Ñ ÑравнениÑ: - - - - Implementation language: - Язык реализации: - - - - Databases - Базы данных - - - - Register in all databases - ЗарегиÑтрировать во вÑех базах данных - - - - Register in following databases: - ЗарегиÑтрировать в Ñледующих базах данных: - - - - Implementation code: - Код реализации: - - - - Collations editor - Редактор Ñравнений - - - - Commit all collation changes - Подтвердить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñравнений - - - - Rollback all collation changes - Откатить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñравнений - - - - Create new collation - Создать новое Ñравнение - - - - Delete selected collation - Удалить выбранное Ñравнение - - - - Editing collations manual - РуководÑтво по редактированию Ñравнений - - - - Enter a non-empty, unique name of the collation. - Введите непуÑтое уникальное Ð¸Ð¼Ñ ÑравнениÑ. - - - - Pick the implementation language. - Выберите Ñзык реализации. - - - - Enter a non-empty implementation code. - Введите непуÑтой код реализации. - - - - Collations editor window has uncommitted modifications. - Ð’ редакторе Ñравнений имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ изменениÑ. - - - Collations editor window has uncommited modifications. - Ð’ редакторе Ñравнений имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ изменениÑ. - - - - ColorButton - - - Pick a color - Выберите цвет - - - - ColumnCollatePanel - - - Collation name: - Ð˜Ð¼Ñ ÑравнениÑ: - - - - Named constraint: - Именованное ограничение: - - - - Enter a name of the constraint. - Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. - - - - Enter a collation name. - Введите Ð¸Ð¼Ñ ÑравнениÑ. - - - - ColumnDefaultPanel - - - Default value: - Значение по умолчанию: - - - - Named constraint: - Именованное ограничение: - - - - Enter a default value expression. - Введите выражение Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию. - - - - Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - Ðекорректное выражение Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию: %1. ЕÑли необходимо иÑпользовать проÑтую Ñтроку как значение, не забудьте помеÑтить её в кавычки. - - - - Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - Ðекорректное выражение Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию. ЕÑли необходимо иÑпользовать проÑтую Ñтроку как значение, не забудьте помеÑтить её в кавычки. - - - Invalid default value expression: %1 - Ðекорректное выражение Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию: %1 - - - - Enter a name of the constraint. - Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. - - - - ColumnDialog - - - Column - Столбец - - - - Name and type - Ð˜Ð¼Ñ Ð¸ тип - - - - Scale - Размер - - - - Precision - ТочноÑть - - - - Data type: - Тип данных: - - - - Column name: - Ð˜Ð¼Ñ Ñтолбца: - - - - Size: - Размер: - - - - Constraints - ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ - - - - Unique - УникальноÑть - - - - - - - - - - Configure - ÐаÑтроить - - - - Foreign Key - Внешний ключ - - - - Collate - Сравнение - - - - Not NULL - Ðе NULL - - - - Check condition - Проверка уÑÐ»Ð¾Ð²Ð¸Ñ - - - - Primary Key - Первичный ключ - - - - Default - - - - - Advanced mode - РаÑширенный режим - - - - Add constraint - column dialog - Добавить ограничение - - - - Edit constraint - column dialog - Редактировать ограничение - - - - - Delete constraint - column dialog - - - - - Move constraint up - column dialog - ПеремеÑтить ограничение вверх - - - - Move constraint down - column dialog - ПеремеÑтить ограничение вниз - - - - Add a primary key - column dialog - Добавить первичный ключ - - - - Add a foreign key - column dialog - Добавить внешний ключ - - - - Add an unique constraint - column dialog - Добавить ограничение на уникальноÑть - - - - Add a check constraint - column dialog - Добавить проверочное ограничение - - - - Add a not null constraint - column dialog - Добавить ограничение на не null - - - - Add a collate constraint - column dialog - Добавить ограничение на Ñравнение - - - - Add a default constraint - column dialog - Добавить ограничение на значение по умочанию - - - - Are you sure you want to delete constraint '%1'? - column dialog - Ð’Ñ‹ дейÑтвительно хотите удалить ограничение '%1'? - - - - Correct the constraint's configuration. - ИÑправьте конфигурацию ограничениÑ. - - - - This constraint is not officially supported by SQLite 2, -but it's okay to use it. - Это ограничение официально не поддерживаетÑÑ SQLite 2, но его иÑпользование допуÑтимо. - - - - Scale is not allowed for INTEGER PRIMARY KEY columns. - Указание размера данных недопуÑтимо Ð´Ð»Ñ Ñтолбцов Ñ Ñ‚Ð¸Ð¿Ð¾Ð¼ INTEGER PRIMARY KEY. - - - - Precision cannot be defined without the scale. - ТочноÑть не может быть задана без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ñ€Ð°Ð·Ð¼ÐµÑ€Ð° данных. - - - - Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - Ðевозможно иÑпользовать тип данных, отличный от INTEGER, еÑли в первичном ключе уÑтановлен автоинкремент. - - - - INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - Ð’ качеÑтве типа данных был принудительно выбран INTEGER, так как в первичном ключе уÑтановлен автоинкремент. - - - - Precision is not allowed for INTEGER PRIMARY KEY columns. - Указание точноÑти недопуÑтимо Ð´Ð»Ñ Ñтолбцов Ñ Ñ‚Ð¸Ð¿Ð¾Ð¼ INTEGER PRIMARY KEY. - - - - ColumnDialogConstraintsModel - - - Type - column dialog constraints - Тип - - - - Name - column dialog constraints - Ð˜Ð¼Ñ - - - - Details - column dialog constraints - ПодробноÑти - - - - ColumnForeignKeyPanel - - - Foreign table: - ВнешнÑÑ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°: - - - - Foreign column: - Внешний Ñтолбец: - - - - Reactions - ДейÑÑ‚Ð²Ð¸Ñ - - - - Deferred foreign key - Отложенный внешний ключ - - - - Named constraint - Именованное ограничение - - - - Constraint name - Ð˜Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ - - - - Pick the foreign table. - Выберите внешнюю таблицу. - - - - Pick the foreign column. - Выберите внешний Ñтолбец. - - - - Enter a name of the constraint. - Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. - - - - ColumnPrimaryKeyPanel - - - Autoincrement - Ðвтоинкремент - - - - Sort order: - ПорÑдок Ñортировки: - - - - Named constraint: - Именованное ограничение: - - - - On conflict: - При конфликте: - - - - Enter a name of the constraint. - Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. - - - Autoincrement (only for %1 type columns) - column primary key - Ðвтоинкремент (только Ð´Ð»Ñ Ñтолбцов типа %1) - - - - ColumnUniqueAndNotNullPanel - - - Named constraint: - Именованное ограничение: - - - - On conflict: - При конфликте: - - - - Enter a name of the constraint. - Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. - - - - CompleterWindow - - - Column: %1 - completer statusbar - Столбец: %1 - - - - Table: %1 - completer statusbar - Таблица: %1 - - - - Index: %1 - completer statusbar - ИндекÑ: %1 - - - - Trigger: %1 - completer statusbar - Триггер: %1 - - - - View: %1 - completer statusbar - ПредÑтавление: %1 - - - - Database: %1 - completer statusbar - База данных: %1 - - - - Keyword: %1 - completer statusbar - Ключевое Ñлово: %1 - - - - Function: %1 - completer statusbar - ФункциÑ: %1 - - - - Operator: %1 - completer statusbar - Оператор: %1 - - - - String - completer statusbar - Строка - - - - Number - completer statusbar - ЧиÑло - - - - Binary data - completer statusbar - Двоичные данные - - - - Collation: %1 - completer statusbar - Сравнение: %1 - - - - Pragma function: %1 - completer statusbar - Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ pragma: %1 - - - - ConfigDialog - - - - Configuration - ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ - - - - Search - ПоиÑк - - - - General - Общие - - - - Keyboard shortcuts - ГорÑчие клавиши - - - - Look & feel - Внешний вид - - - - Style - Стиль - - - - Fonts - Шрифты - - - - Colors - Цвета - - - - Plugins - Модули - - - - Code formatters - СредÑтва Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð´Ð° - - - - Data browsing - ПроÑмотр данных - - - - Data editors - Редакторы данных - - - - Database dialog window - Диалоговое окно Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных - - - - <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> - <p>По умолчанию при добавлении базы данных она отмечаетÑÑ ÐºÐ°Ðº "поÑтоÑннаÑ" (Ñ‚.е. ÑохранÑетÑÑ Ð² конфигурацию). При уÑтановке данной опции вÑе добавлÑемые базы данных по умолчанию ÐЕ будут отмечены как "поÑтоÑнные".</p> - - - - Do not mark database to be "permanent" by default - Ðе отмечать базу данных как "поÑтоÑнную" по умолчанию - - - - <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> - <p>При выборе данной опции вÑе файлы, перетÑнутые в ÑпиÑок баз данных из файлового менеджера, будут автоматичеÑки добавлены в ÑпиÑок без Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¸Ð°Ð»Ð¾Ð³Ð° Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных. ЕÑли по каким-либо причинам автоматичеÑкое добавление не получитÑÑ, пользователю будет показан Ñтандартный диалог Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных.</p> - - - - Try to bypass dialog completly when dropping database file onto the list - Ðе показывать диалог при перетÑгивании файла базы данных в ÑпиÑок - - - - Data browsing and editing - ПроÑмотр и редактирование данных - - - - Number of data rows per page: - КоличеÑтво Ñтрок данных на Ñтранице: - - - - - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - <p>При загрузке даных в табличный вид ширина Ñтолбцов автоматичеÑки подÑтраиваетÑÑ. Этот параметр ограничивает начальную ширину Ð´Ð»Ñ Ð¿Ð¾Ð´Ñтройки, при Ñтом пользователь может вручную изменить ширину Ñтолбца Ñверх данного лимита.</p> - - - - Limit initial data column width to (in pixels): - Ограничить начальную ширину Ñтолбца данных (в пикÑелÑÑ…): - - - <html><head/><body><p>&lt;p&gt;When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).&lt;/p&gt;</p></body></html> - <html><head/><body><p>&lt;p&gt;ЕÑли редактируетÑÑ Ñчейка, ÑÐ¾Ð´ÐµÑ€Ð¶Ð°Ñ‰Ð°Ñ NULL, и вводитÑÑ Ð¿ÑƒÑÑ‚Ð°Ñ Ñтрока в качеÑтве значениÑ, то Ñта Ð¾Ð¿Ñ†Ð¸Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»Ñет, оÑтанетÑÑ Ð»Ð¸ в качеÑтве Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñчейки NULL (еÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°), или значение будет заменено на пуÑтую Ñтроку (еÑли Ñта Ð¾Ð¿Ñ†Ð¸Ñ Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°).&lt;/p&gt;</p></body></html> - - - - Keep NULL value when entering empty value - СохранÑть значение NULL при вводе пуÑтой Ñ‚Ñроки - - - General.KeepNullWhenEmptyValue - General.KeepNullWhenEmptyValue - - - - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> - <p>ЕÑли Ð´Ð°Ð½Ð½Ð°Ñ Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð° и пользователь наводит указатель мыши на Ñчейку в любом режиме проÑмотра данных (результаты запроÑа, данные таблицы, данные предÑтавлениÑ), то будет отображена вÑÐ¿Ð»Ñ‹Ð²Ð°ÑŽÑ‰Ð°Ñ Ð¿Ð¾Ð´Ñказка Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸ÐµÐ¹ о Ñчейке, в том чиÑле Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ типе данных Ñтолбца, ограничениÑÑ…, значение ROWID и прочее.</p> - - - - Show column and row details tooltip in data view - Показывать вÑплывающую подÑказку Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸ÐµÐ¹ о Ñтолбце и Ñтроке при проÑмотре данных - - - - Inserting new row in data grid - Ð’Ñтавка новой Ñтроки в таблице данных - - - - Before currently selected row - Перед текущей выделенной Ñтрокой - - - General.InsertRowPlacement - General.InsertRowPlacement - - - - After currently selected row - ПоÑле текущей выделенной Ñтроки - - - - At the end of data view - Ð’ конец облаÑти проÑмотра данных - - - - <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - <p>ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, окна таблиц будут открыватьÑÑ Ð½Ð° вкладке данных вмеÑто вкладки Ñо Ñтруктурой.</p> - - - - <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - <p>ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, вкладка "Данные" в окнах таблиц будет первой, а не второй по порÑдку.</p> - - - - Place data tab as first tab in a Table Window - Помещать вкладку данных в окнах таблиц первой - - - - <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - <p>ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, окна предÑтавлений будут открыватьÑÑ Ð½Ð° вкладке данных вмеÑто вкладки Ñо Ñтруктурой.</p> - - - - <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - <p>ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, вкладка "Данные" в окнах предÑтавлений будет первой, а не второй по порÑдку.</p> - - - - Place data tab as first tab in a View Window - Помещать вкладку данных в окнах предÑтавлений первой - - - - Data types - Типы данных - - - - Available editors: - ДоÑтупные редакторы: - - - - Editors selected for this data type: - Выбранные редакторы Ð´Ð»Ñ Ñтого типа данных: - - - - Schema editing - Редактирование Ñхемы - - - - Number of DDL changes kept in history. - КоличеÑтво ÑохранÑемых в иÑтории изменений DDL. - - - - DDL history size: - Размер иÑтории DDL: - - - Don't show DDL preview dialog when commiting schema changes - Ðе показывать диалог предпроÑмотра DDL при подтверждении изменений Ñхемы - - - - SQL queries - SQL запроÑÑ‹ - - - - - Number of queries kept in the history. - КоличеÑтво ÑохранÑемых в иÑтории запроÑов. - - - - History size: - Размер иÑтории: - - - - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> - <p>ЕÑли в окне редактора SQL введено более одного запроÑа, то (еÑли Ð´Ð°Ð½Ð½Ð°Ñ Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°) будет выполнен лишь один Ð·Ð°Ð¿Ñ€Ð¾Ñ - тот, который находитÑÑ Ð¿Ð¾Ð´ текÑтовым курÑором. Ð’ противном Ñлучае будут иÑполнены вÑе запроÑÑ‹. Ð’Ñ‹ можете ограничить выполнÑемые запроÑÑ‹, выделив их перед вызовом выполнениÑ.</p> - - - - Execute only the query under the cursor - ВыполнÑть только Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ð´ курÑором - - - - Updates - ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ - - - - Automatically check for updates at startup - ÐвтоматичеÑки проверÑть Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ запуÑке - - - - Session - СеÑÑÐ¸Ñ - - - - Restore last session (active MDI windows) after startup - ВоÑÑтановить предыдущую ÑеÑÑию (активные MDI окна) поÑле запуÑка - - - - Status Field - Окно ÑтатуÑа - - - - <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> - <p>ЕÑли пользователь вручную закрыл окно ÑтатуÑа, включение данной опции гарантирует, что при поÑвлении новых Ñообщений окно ÑтатуÑа будет автоматичеÑки открыто. ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°, окно ÑтатуÑа может быть заново открыто только пользователем вручную через меню "Вид".</p> - - - - Always open Status panel when new message is printed - Ð’Ñегда открывать окно ÑтатуÑа при поÑвлении нового ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ - - - - Filter shortcuts by name or key combination - Фильтруйте горÑчие клавиши по имени или комбинации клавиш - - - - Action - ДейÑтвие - - - - Key combination - ÐšÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ð²Ð¸Ñˆ - - - - - Language - Язык - - - - Changing language requires application restart to take effect. - Ð”Ð»Ñ Ñмены Ñзыка потребуетÑÑ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑтить приложение. - - - - Compact layout - Компактный режим - - - - <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - <p>Ð’ компактном режиме вÑе Ð¿Ð¾Ð»Ñ Ð¸ отÑтупы в интерфейÑе минимизированы Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð±Ð¾Ð»ÑŒÑˆÐµÐ³Ð¾ количеÑтва данных. Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ñтанет чуть менее ÑÑтетичным, однако Ñто позволит умеÑтить больше данных на Ñкране.</p> - - - - Use compact layout - Включить компактный режим - - - General.CompactLayout - General.CompactLayout - - - - - Database list - СпиÑок баз данных - - - - If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. - ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, Ñтолбцы будут отÑортированы в том порÑдке, в котором они были указаны в конÑтрукции CREATE TABLE. - - - - Sort table columns alphabetically - Сортировать Ñтолбцы таблицы в алфавитном порÑдке - - - - Expand tables node when connected to a database - Развернуть ÑпиÑок таблиц поÑле Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº базе данных - - - - <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> - <p>Дополнительные метки находÑÑ‚ÑÑ Ñправа от имён в ÑпиÑке баз данных (они отображаютÑÑ Ñиним цветом, еÑли не выбран иной). При активации Ñтой опции будут отображены метки у баз данных, некорректных баз данных и у групповых узлов (группа Ñтолбцов, группа индекÑов, группа триггеров). Ð”Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ñ‹Ñ… меток воÑпользуйтеÑÑŒ опциÑми ниже.<p> - - - - Display additional labels on the list - Отображать дополнительные метки в ÑпиÑке - - - - For regular tables labels will show number of columns, indexes and triggers for each of tables. - Ð”Ð»Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ñ… таблиц метки будут показывать количеÑтво Ñтолбцов, индекÑов и триггеров у каждой таблицы. - - - - Display labels for regular tables - Отображать метки у обычных таблиц - - - - Virtual tables will be marked with a 'virtual' label. - Виртуальные таблицы будут помечены как 'вирутальные'. - - - - Display labels for virtual tables - Отображать метки у виртуальных таблиц - - - - Expand views node when connected to a database - Развернуть ÑпиÑок предÑтавлений поÑле Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº базе данных - - - - If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) - ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, объекты будут отÑортированы в том порÑдке, в котором они указаны в таблице sqlite_master (Ñ‚.е. в порÑдке ÑозданиÑ) - - - - Sort objects (tables, indexes, triggers and views) alphabetically - Сортировать объекты (таблицы, индекÑÑ‹, триггеры и предÑтавлениÑ) в алфавитном порÑдке - - - - Display system tables and indexes on the list - Отображать в ÑпиÑке ÑиÑтемные таблицы и индекÑÑ‹ - - - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - <p>МакиÑмальное количеÑтво конфигураций окна Ð—Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹, ÑохранÑемых в конфигурации программы. 100 конфигураций должно хватить.</p> - - - - Number of memorized table populating configurations - КоличеÑтво запоминаемых конфигураций Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ - - - - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - <p>ЕÑли редактируетÑÑ Ñчейка, ÑÐ¾Ð´ÐµÑ€Ð¶Ð°Ñ‰Ð°Ñ NULL, и вводитÑÑ Ð¿ÑƒÑÑ‚Ð°Ñ Ñтрока в качеÑтве значениÑ, то Ñта Ð¾Ð¿Ñ†Ð¸Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»Ñет, оÑтанетÑÑ Ð»Ð¸ в качеÑтве Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñчейки NULL (еÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°), или значение будет заменено на пуÑтую Ñтроку (еÑли Ñта Ð¾Ð¿Ñ†Ð¸Ñ Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°).</p> - - - - <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> - <html><head/><body><p>Ðктивируйте Ñту опцию, чтобы вÑегда подÑтавлÑть значение DEFAULT (значение по умолчанию) при запиÑи NULL в Ñтолбец, у которого определено значение DEFAULT, даже еÑли Ñтолбец может Ñодержать NULL.</p><p>Отключите Ñту опцию Ð´Ð»Ñ Ð¿Ð¾Ð´Ñтановки Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ DEFAULT только в Ñлучае запиÑи NULL в Ñтолбец Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸ÐµÐ¼ NOT NULL.</p></body></html> - - - - Use DEFAULT value (if defined), when committing NULL value - ИÑпользовать значение DEFAULT (еÑли оно определено) при запиÑи NULL - - - - Table windows - Окна таблиц - - - When enabled, Table Windows will show up with the data tab, instead of the structure tab. - ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, окно таблицы будет открыто на вкладке данных вмеÑто вкладки Ñтруктуры. - - - - Open Table Windows with the data tab for start - Открывать окна таблиц на вкладке данных - - - - View windows - Окна предÑтавлений - - - When enabled, View Windows will show up with the data tab, instead of the structure tab. - ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, окно предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ открыто на вкладке данных вмеÑто вкладки Ñтруктуры. - - - - Open View Windows with the data tab for start - Открывать окна предÑтавлений на вкладке данных - - - - Don't show DDL preview dialog when committing schema changes - Ðе показывать диалог предпроÑмотра DDL при подтверждении изменений Ñхемы - - - - - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - <p>МакÑимальное количеÑтво параметров запроÑа (:param, @param, $param, ?), ÑохранÑемых в иÑтории. Когда вы повторно иÑпользуете параметр Ñ Ñ‚ÐµÐ¼ же именем/раÑположением, SQLiteStudio преварительно инициализирует его поÑледним запомненным значением (которое затем можно изменить). 1000 параметров должно хватить.</p> - - - - Number of memorized query parameters - КоличеÑтво запоминаемых параметров запроÑа - - - - Main window dock areas - ОблаÑти Ð¿Ñ€Ð¸ÐºÑ€ÐµÐ¿Ð»ÐµÐ½Ð¸Ñ Ð²Ð¾ÐºÑ€ÑƒÐ³ главного окна - - - - Left and right areas occupy corners - Углы занимают Ð¿Ñ€Ð°Ð²Ð°Ñ Ð¸ Ð»ÐµÐ²Ð°Ñ Ð¾Ð±Ð»Ð°Ñти - - - - Top and bottom areas occupy corners - Углы занимают верхнÑÑ Ð¸ нижнÑÑ Ð¾Ð±Ð»Ð°Ñти - - - - Hide built-in plugins - Скрыть вÑтроенные модули - - - - Current style: - Текущий Ñтиль: - - - - Preview - ПредпроÑмотр - - - - Enabled - Ðктивно - - - - Disabled - Ðеактивно - - - - Active formatter plugin - Ðктивный модуль Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ - - - - SQL editor font - Шрифт редактора SQL - - - - Database list font - Шрифт ÑпиÑка баз данных - - - - Database list additional label font - Шрифт дополнительных меток в ÑпиÑке баз данных - - - - Data view font - Шрифт проÑмотра данных - - - - Status field font - Шрифт окна ÑтатуÑа - - - - SQL editor colors - Цвета редактора SQL - - - - Current line background - Фон текущей Ñтроки - - - - <p>SQL strings are enclosed with single quote characters.</p> - <p>Строки SQL обрамлÑÑŽÑ‚ÑÑ Ð² одинарные кавычки.</p> - - - - String foreground - Цвет Ñтроки - - - - <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - <p>ПодÑтановочные параметры предназначены Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹, которые будут в дальнейшем указаны пользователем. Они определÑÑŽÑ‚ÑÑ Ð¾Ð´Ð½Ð¸Ð¼ из Ñледующих ÑпоÑобов:</p><ul><li>:имÑ_параметра</li><li>$имÑ_параметра</li><li>@имÑ_параметра</li><li>?</li></ul> - - - - Bind parameter foreground - Цвет подÑтановочных параметров - - - - Highlighted parenthesis background - Фон подÑвечиваемых Ñкобок - - - - <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - <p>Данные типа BLOB — Ñто бинарные данные, предÑтавлÑемые в виде шеÑтнадцатеричных чиÑел, например:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - - - - BLOB value foreground - Цвет данных типа BLOB - - - - Regular foreground - Стандартный цвет - - - - Line numbers area background - Фон облаÑти нумерации Ñтрок - - - - Keyword foreground - Цвет ключевого Ñлова - - - - Number foreground - Цвет чиÑла - - - - Comment foreground - Цвет ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ð¸Ñ - - - - <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> - <p>РаÑпознаваемыми объектами ÑвлÑÑŽÑ‚ÑÑ Ð¸Ð¼ÐµÐ½Ð° талиц, индекÑов, триггеров и предÑтавлений, ÑущеÑтвующих в базе данных SQLite.</p> - - - - Valid objects foreground - Цвет раÑпознанных объектов - - - - Data view colors - Цвета в окне проÑмотра данных - - - - <p>Any data changes will be outlined with this color, until they're committed to the database.</p> - <p>Ð’Ñе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… будут обрамлены Ñтим цветом, пока не будут запиÑаны в базу данных.</p> - - - - Uncommitted data outline color - Цвет Ð¾Ð±Ñ€Ð°Ð¼Ð»ÐµÐ½Ð¸Ñ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ñ… изменений - - - - <p>In case of error while committing data changes, the problematic cell will be outlined with this color.</p> - <p>Ð’ Ñлучае ошибки при подтверждении изменений данных, Ñтим цветом будут обрамлены проблемные Ñчейки.</p> - - - <p>Any data changes will be outlined with this color, until they're commited to the database.</p> - <p>Ð’Ñе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… будут обрамлены Ñтим цветом, пока не будут запиÑаны в базу данных.</p> - - - Uncommited data outline color - Цвет Ð¾Ð±Ñ€Ð°Ð¼Ð»ÐµÐ½Ð¸Ñ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ñ… изменений - - - <p>In case of error while commiting data changes, the problematic cell will be outlined with this color.</p> - <p>Ð’ Ñлучае ошибки при подтверждении изменений данных, Ñтим цветом будут обрамлены проблемные Ñчейки.</p> - - - - Commit error outline color - Цвет Ð¾Ð±Ñ€Ð°Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾ÑˆÐ¸Ð±Ð¾Ñ‡Ð½Ñ‹Ñ… Ñчеек - - - - NULL value foreground - Цвет значений NULL - - - - Deleted row background - Фон удалённых Ñтрок - - - - Database list colors - Цвета ÑпиÑка баз данных - - - - <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> - <p>Дополнительные метки Ñодержат информацию о верÑии SQLite, о количеÑтве объектов в глубине дерева и Ñ‚.д.</p> - - - - Additional labels foreground - Цвет дополнительных меток - - - - Status field colors - Цвета в окне СтатуÑа - - - - Information message foreground - Цвет информационного ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ - - - - Warning message foreground - Цвет Ð¿Ñ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ñ - - - - Error message foreground - Цвет ошибки - - - - Description: - plugin details - ОпиÑание: - - - - Category: - plugin details - КатегориÑ: - - - - Version: - plugin details - ВерÑиÑ: - - - - Author: - plugin details - Ðвтор: - - - - Internal name: - plugin details - Внутреннее имÑ: - - - - Dependencies: - plugin details - ЗавиÑимоÑти: - - - - Conflicts: - plugin details - Конфликты: - - - - Plugin details - Ð¡Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ модуле - - - - Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. - Модули загружаютÑÑ Ð¸ выгружаютÑÑ Ñразу поÑле активации/деактивации, однако Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² ÑпиÑке загружаемых при Ñтарте модулей не будут Ñохранены пока вы не примените Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² окне конфигурации. - - - - %1 (built-in) - plugins manager in configuration dialog - %1 (вÑтроенный) - - - - Details - Ð¡Ð²ÐµÐ´ÐµÐ½Ð¸Ñ - - - - No plugins in this category. - Ð’ Ñтой категории модулей нет. - - - - Add new data type - Добавить новый тип данных - - - - Rename selected data type - Переименовать выбранный тип данных - - - - Delete selected data type - Удалить выбранный тип данных - - - - Help for configuring data type editors - Справка по наÑтройке редакторов типов данных - - - - ConstraintCheckPanel - - - The condition - УÑловие - - - - Named constraint: - Именованное ограничение: - - - - On conflict - При конфликте - - - - Enter a valid condition. - Введите корректное уÑловие - - - - Enter a name of the constraint. - Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. - - - - ConstraintDialog - - - New constraint - constraint dialog - Ðовое ограничение - - - - Create - constraint dialog - Создать - - - - Edit constraint - dialog window - Редактировать ограничение - - - - Apply - constraint dialog - Применить - - - - Primary key - table constraints - Первичный ключ - - - - Foreign key - table constraints - Внешний ключ - - - - Unique - table constraints - УникальноÑть - - - - Not NULL - table constraints - Ðе NULL - - - - Check - table constraints - Проверка - - - - Collate - table constraints - Сравнение - - - - Default - table constraints - Значение по умолчанию - - - - ConstraintTabModel - - - Table - table constraints - Таблица - - - - Column (%1) - table constraints - Столбец (%1) - - - - Scope - table constraints - ОблаÑть Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ - - - - Type - table constraints - Тип - - - - Details - table constraints - ПодробноÑти - - - - Name - table constraints - Ð˜Ð¼Ñ - - - - CssDebugDialog - - - SQLiteStudio CSS console - КонÑоль CSS SQLiteStudio - - - - DataView - - - Filter data - data view - Отфильтровать данные - - - - Grid view - Табличный вид - - - - Form view - Форма - - - - Refresh table data - data view - Обновить данные таблицы - - - - First page - data view - ÐŸÐµÑ€Ð²Ð°Ñ Ñтраница - - - - Previous page - data view - ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ñтраница - - - - Next page - data view - Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ñтраница - - - - Last page - data view - ПоÑледнÑÑ Ñтраница - - - - Filter - Фильтр - - - - Hit Enter key or press "Apply filter" button on toolbar to apply new value. - Ðажмите Enter или кнопку "Применить фильтр" на панели инÑтрументов чтобы применить новое значение - - - - Show filter inputs per column - data view - Показывать поле ввода Ð´Ð»Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð° в каждом Ñтолбце - - - - Apply filter - data view - Применить фильтр - - - - Commit changes for selected cells - data view - Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ñчеек - - - - Rollback changes for selected cells - data view - Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ñчеек - - - - Show grid view of results - sql editor - Показать результаты в виде таблицы - - - - Show form view of results - sql editor - Показать результаты в виде формы - - - - Filter by text - data view - ТекÑтовый фильтр - - - - Filter by the Regular Expression - data view - Фильтр по регулÑрному выражению - - - - Filter by SQL expression - data view - Фильтр по выражению SQL - - - - Tabs on top - data view - Вкладки Ñверху - - - - Tabs at bottom - data view - Вкладки Ñнизу - - - - Place new rows above selected row - data view - ПомеÑтить новые Ñтроки перед выделенной Ñтрокой - - - - Place new rows below selected row - data view - ПомеÑтить новые Ñтроки поÑле выделенной Ñтроки - - - - Place new rows at the end of the data view - data view - ПомеÑтить новые Ñтроки в конец облаÑти проÑмотра данных - - - - Total number of rows is being counted. -Browsing other pages will be possible after the row counting is done. - Идёт подÑчёт общего чиÑла Ñтрок. -Переключение на другие Ñтраницы Ñтанет возможным поÑле Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ Ð¿Ð¾Ð´Ñчёта. - - - - Row: %1 - Строка: %1 - - - - DbConverterDialog - - - Convert database - Преобразовать базу данных - - - - Source database - ИÑÑ…Ð¾Ð´Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных - - - - Source database version: - ВерÑÐ¸Ñ Ð¸Ñходной базы данных: - - - - Target database - Ð¦ÐµÐ»ÐµÐ²Ð°Ñ Ð±Ð°Ð·Ð° данных - - - - Target version: - Ð¦ÐµÐ»ÐµÐ²Ð°Ñ Ð²ÐµÑ€ÑиÑ: - - - - This is the file that will be created as a result of the conversion. - Это файл, который будет Ñоздан в результате конвертации. - - - - Target file: - Целевой файл: - - - - Name of the new database: - Ð˜Ð¼Ñ Ð½Ð¾Ð²Ð¾Ð¹ базы данных: - - - - This is the name that the converted database will be added to SQLiteStudio with. - Это имÑ, под которым ÑÐºÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных будет добавлена в SQLiteStudio. - - - - Select source database - Выберите иÑходную базу данных - - - - Enter valid and writable file path. - Введите корректный путь к файлу, доÑтупному Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи. - - - - Entered file exists and will be overwritten. - Указанный файл ÑущеÑтвует и будет перезапиÑан. - - - - Enter a not empty, unique name (as in the list of databases on the left). - Введите непуÑтое уникальное Ð¸Ð¼Ñ (как в ÑпиÑке баз данных Ñлева). - - - - No valid target dialect available. Conversion not possible. - ОтÑутÑтвует корректный целевой диалект. ÐšÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð°Ñ†Ð¸Ñ Ð½ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð°. - - - - Select valid target dialect. - Выберите корректный целевой диалект. - - - - Database %1 has been successfully converted and now is available under new name: %2 - База данных %1 была уÑпешно Ñконвертирована и теперь доÑтупна под новым именем: %2 - - - - SQL statements conversion - ÐšÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð°Ñ†Ð¸Ñ ÐºÐ¾Ð½Ñтрукций SQL - - - - Following error occurred while converting SQL statements to the target SQLite version: - При конвертации конÑтрукций SQL в новую верÑию SQLite произошла ошибка: - - - - Would you like to ignore those errors and proceed? - Ð’Ñ‹ хотите проигнорировать Ñти ошибки и продолжить? - - - - DbDialog - - - Database - База данных - - - - Database type - Тип базы данных - - - - Database driver - Драйвер базы данных - - - Generate automatically - Сгенерировать автоматичеÑки - - - - Options - Опции - - - - Permanent (keep it in configuration) - ПоÑтоÑÐ½Ð½Ð°Ñ (Ñохранить базу в конфигурационном файле) - - - - Test connection - ТеÑÑ‚ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ - - - Name - Ð˜Ð¼Ñ - - - Type - Тип - - - Browse for database file on local computer - Указать файл базы данных на локальном компьютере - - - - Create new database file - Создать новый файл базы данных - - - - - File - Файл - - - - Name (on the list) - Ð˜Ð¼Ñ (в ÑпиÑке) - - - Generate name basing on file path - Генерировать Ð¸Ð¼Ñ Ð½Ð° оÑнове пути к файлу - - - Permanent - Запомнить - - - - <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> - aasfd - <p>Ðктивируйте Ñту опцию Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных в конфигурационном файле и Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÐµÑ‘ в ÑпиÑок при каждом запуÑке SQLiteStudio.</p> - - - Test database connection - ТеÑÑ‚ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ð±Ð°Ð·Ð¾Ð¹ данных - - - - Browse for existing database file on local computer - Указать ÑущеÑтвующий файл базы данных на локальном компьютере - - - - Browse - Обзор - - - - Enter an unique database name. - Введите уникальное Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных. - - - - This name is already in use. Please enter unique name. - Данное Ð¸Ð¼Ñ ÑƒÐ¶Ðµ иÑпользуетÑÑ. ПожалуйÑта, укажите уникальное имÑ. - - - - <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - <p>ÐвтоматичеÑÐºÐ°Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð¸ отключена, так как Ð¸Ð¼Ñ Ð±Ñ‹Ð»Ð¾ задано вручную. Ð”Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкой генерации необходимо удалить Ñодержимое из Ð¿Ð¾Ð»Ñ Ð¸Ð¼ÐµÐ½Ð¸.</p> - - - <p>Automatic name generation was disabled, becuase the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - <p>ÐвтоматичеÑÐºÐ°Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð¸ отключена, так как Ð¸Ð¼Ñ Ð±Ñ‹Ð»Ð¾ задано вручную. Ð”Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкой генерации необходимо удалить Ñодержимое из Ð¿Ð¾Ð»Ñ Ð¸Ð¼ÐµÐ½Ð¸.</p> - - - - Enter a database file path. - Введите путь к базе данных. - - - - This database is already on the list under name: %1 - Ð£ÐºÐ°Ð·Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных уже находитÑÑ Ð² ÑпиÑке под именем %1 - - - - Select a database type. - Выберите тип базы данных. - - - Auto-generated - ÐвтоматичеÑки Ñгенерировано - - - The name will be auto-generated - Ð˜Ð¼Ñ Ð±ÑƒÐ´ÐµÑ‚ Ñгенерировано автоматичеÑки - - - Type the name - Введите Ð¸Ð¼Ñ - - - - DbObjectDialogs - - - Delete table - Удалить таблицу - - - - Are you sure you want to delete table %1? - Ð’Ñ‹ дейÑтвительно хотите удалить таблицу %1? - - - - Delete index - Удалить Ð¸Ð½Ð´ÐµÐºÑ - - - - Are you sure you want to delete index %1? - Ð’Ñ‹ дейÑтвительно хотите удалить Ð¸Ð½Ð´ÐµÐºÑ %1? - - - - Delete trigger - Удалить триггер - - - - Are you sure you want to delete trigger %1? - Ð’Ñ‹ дейÑтвительно хотите удалить триггер %1? - - - - Delete view - Удалить предÑтавление - - - - Are you sure you want to delete view %1? - Ð’Ñ‹ дейÑтвительно хотите удалить предÑтавление %1? - - - - - Error while dropping %1: %2 - Ошибка при удалении %1: %2 - - - - Delete objects - Удалить объекты - - - - Are you sure you want to delete following objects: -%1 - Ð’Ñ‹ дейÑтвительно хотите удалить Ñледующие объекты: -%1 - - - - Cannot start transaction. Details: %1 - Ðевозможно начать транзакцию. ПодробноÑти: %1 - - - - Cannot commit transaction. Details: %1 - Ðевозможно подтвердить транзакцию. ПодробноÑти: %1 - - - - DbTree - - - Databases - Базы данных - - - - Filter by name - Фильтр по имени - - - - Copy - Копировать - - - - Paste - Ð’Ñтавить - - - - Select all - Выделить вÑÑ‘ - - - - Create a group - Создать группу - - - - Delete the group - Удалить группу - - - - Rename the group - Переименовать группу - - - Add a database - Добавить базу данных - - - Edit the database - Редактировать базу данных - - - Remove the database - Удалить базу данных - - - Connect to the database - ПодключитьÑÑ Ðº базе данных - - - Disconnect from the database - ОтключитьÑÑ Ð¾Ñ‚ базы данных - - - - Import - Импорт - - - Export the database - ЭкÑпортировать базу данных - - - Convert database type - Измениить тип базы данных - - - Vacuum - Выполнить VACUUM - - - Integrity check - Проверить целоÑтноÑть - - - Create a table - Создать таблицу - - - Edit the table - Редактировать таблицу - - - Delete the table - Удалить таблицу - - - - Export the table - ЭкÑпортировать таблицу - - - - Import into the table - Импортировать данные в таблицу - - - - Populate table - Заполнить таблицу - - - - Create similar table - Создать подобную таблицу - - - - Reset autoincrement sequence - СброÑить Ñчётчик автоинкремента - - - Create an index - Создать Ð¸Ð½Ð´ÐµÐºÑ - - - Edit the index - Редактировать Ð¸Ð½Ð´ÐµÐºÑ - - - Delete the index - Удалить Ð¸Ð½Ð´ÐµÐºÑ - - - Create a trigger - Создать триггер - - - Edit the trigger - Редактировать триггер - - - Delete the trigger - Удалить триггер - - - Create a view - Создать предÑтавление - - - Edit the view - Редактировать предÑтавление - - - Delete the view - Удалить предÑтавление - - - - Add a column - Добавить Ñтолбец - - - - Edit the column - Редактировать Ñтолбец - - - - Delete the column - Удалить Ñтолбец - - - - Delete selected items - Удалить выбранные Ñлементы - - - - Clear filter - СброÑить фильтр - - - Refresh all database schemas - Обновить Ñтруктуры вÑех баз данных - - - Refresh selected database schema - Обновить Ñтруктуру выбранной базы данных - - - - Execution from file cancelled. Any queries executed so far have been rolled back. - Выполнение запроÑов из файла отменено. Ð’Ñе выполненные ранее из него запроÑÑ‹ откачены. - - - - &Add a database - &Добавить базу данных - - - - &Edit the database - &Редактировать базу данных - - - - &Remove the database - &Удалить базу данных - - - - &Connect to the database - &ПодключитьÑÑ Ðº базе данных - - - - &Disconnect from the database - &ОтключитьÑÑ Ð¾Ñ‚ базы данных - - - - &Export the database - &ЭкÑпортировать базу данных - - - - Con&vert database type - И&зменить тип базы данных - - - - Vac&uum - Оп&ÐµÑ€Ð°Ñ†Ð¸Ñ VACUUM - - - - &Integrity check - Проверить &целоÑтноÑть - - - - Create a &table - Создать &таблицу - - - - Edit the t&able - Редактировать Ñ‚&аблицу - - - - Delete the ta&ble - Удалить та&блицу - - - - Create an &index - Создать &Ð¸Ð½Ð´ÐµÐºÑ - - - - Edit the i&ndex - Редактировать и&Ð½Ð´ÐµÐºÑ - - - - Delete the in&dex - Удалить инде&ÐºÑ - - - - Create a trig&ger - Создать три&ггер - - - - Edit the trigg&er - Редактиро&вать триггер - - - - Delete the trigge&r - Уда&лить триггер - - - - Create a &view - &Создать предÑтавление - - - - Edit the v&iew - Редактироват&ÑŒ предÑтавление - - - - Delete the vi&ew - Удалить &предÑтавление - - - - &Refresh all database schemas - Обновить Ñтруктуры вÑех баз данн&ых - - - - Re&fresh selected database schema - Обновить Ñтруктуру выбранной базы данны&Ñ… - - - - - Erase table data - Удалить данные из таблицы - - - - Open file's directory - Открыть папку Ñ Ñтим файлом - - - - Execute SQL from file - Выполнить SQL-запроÑÑ‹ из файла - - - - - Database - База данных - - - - Grouping - Группировка - - - - Generate query for table - Сгенерировать Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ Ñтой таблицы - - - - - Create group - Создать группу - - - - Group name - Ð˜Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ - - - - Entry with name %1 already exists in group %2. - Элемент Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ %1 уже входит в группу %2. - - - - Delete group - Удалить группу - - - - Are you sure you want to delete group %1? -All objects from this group will be moved to parent group. - Ð’Ñ‹ дейÑтвительно хотите удалить группу %1? Ð’Ñе объекты из данной группы будут перемещены в родительÑкую группу. - - - - Are you sure you want to remove database '%1' from the list? - Ð’Ñ‹ дейÑтвительно хотите удалить базу данных '%1' из ÑпиÑка? - - - - Are you sure you want to remove following databases from the list: -%1 - Ð’Ñ‹ дейÑтвительно хотите удалить Ñледующие базы данных из ÑпиÑка: %1 - - - - Remove database - Удалить базу данных - - - - Vacuum (%1) - ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ VACUUM (%1) - - - - Autoincrement value for table '%1' has been reset successfully. - Ð¡Ð±Ñ€Ð¾Ñ Ñчётчика автоинкремента у таблицы '%1' уÑпешно выполнен. - - - - Are you sure you want to delete all data from table(s): %1? - Ð’Ñ‹ дейÑтвительно хотите удалить вÑе данные из таблицы (таблиц): '%1'? - - - - Could not execute SQL, because application has failed to start transaction: %1 - Ðевозможно выполнить SQL-запроÑ, так как приложению не удалоÑÑŒ начать транзакцию: %1 - - - - Could not open file '%1' for reading: %2 - Ðевозможно открыть файл '%1' Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ: %2 - - - - Could not execute SQL, because application has failed to commit the transaction: %1 - Ðевозможно выполнить SQL-запроÑ, так как приложению не удалоÑÑŒ завершить транзакцию: %1 - - - - Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - Завершено выполнение %1 запроÑов за %2 Ñекунд. %3 запроÑов не было выполнено из-за ошибок. - - - - Finished executing %1 queries in %2 seconds. - Завершено выполнение %1 запроÑов за %2 Ñекунд. - - - - Could not execute SQL due to error. - Ðевозможно выполнить SQL-Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸Ð·-за ошибки. - - - Delete database - Удалить базу данных - - - Are you sure you want to delete database '%1'? - Ð’Ñ‹ дейÑтвительно хотите удалить базу данных '%1'? - - - - - Cannot import, because no import plugin is loaded. - Ðевозможно произвеÑти импорт, Ñ‚.к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð°. - - - - - Cannot export, because no export plugin is loaded. - Ðевозможно произвеÑти ÑкÑпорт, Ñ‚.к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ ÑкÑпорта. - - - Error while executing VACUUM on the database %1: %2 - Ошибка при выполнении команды VACUUM Ð´Ð»Ñ Ð±Ð°Ð·Ñ‹ данных %1: %2 - - - VACUUM execution finished successfully. - Выполнение команды VACUUM уÑпешно завершено. - - - - Integrity check (%1) - Проверка целоÑтноÑти (%1) - - - - Reset autoincrement - Ð¡Ð±Ñ€Ð¾Ñ Ñчётчика автоинкремента - - - - Are you sure you want to reset autoincrement value for table '%1'? - Ð’Ñ‹ дейÑтвительно хотите ÑброÑить Ñчётчик автоинкремента у таблицы '%1'? - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - При попытке ÑброÑа Ñчётчика автоинкремента у таблицы '%1' произошла ошибка: %2 - - - Autoincrement value for table '%1' has been reset successfly. - Ð¡Ð±Ñ€Ð¾Ñ Ñчётчика автоинкремента у таблицы '%1' уÑпешно выполнен. - - - Are you sure you want to delete all data from table '%1'? - Ð’Ñ‹ дейÑтвительно хотите удалить вÑе данные из таблицы '%1'? - - - - An error occurred while trying to delete data from table '%1': %2 - При попытке ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… из таблицы '%1' произошла ошибка: %2 - - - - All data has been deleted for table '%1'. - Из таблицы '%1' были удалены вÑе данные. - - - - Following objects will be deleted: %1. - Будут удалены Ñледующие объекты: %1. - - - - Following databases will be removed from list: %1. - Из ÑпиÑка будут удалены Ñледующие базы данных: %1. - - - - Remainig objects from deleted group will be moved in place where the group used to be. - ОÑтавшиеÑÑ Ð¿Ð¾Ñле ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ объекты будут перемещены туда, где ранее раÑполагалаÑÑŒ группа. - - - - %1<br><br>Are you sure you want to continue? - %11<br><br>Ð’Ñ‹ дейÑтвительно хотите продолжить? - - - - Delete objects - Удалить объекты - - - - DbTreeItemDelegate - - - error - dbtree labels - ошибка - - - - (system table) - database tree label - (ÑиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°) - - - - (virtual) - virtual table label - (виртуальнаÑ) - - - - (system index) - database tree label - (ÑиÑтемный индекÑ) - - - - DbTreeModel - - - Database: %1 - dbtree tooltip - База данных: %1 - - - - Version: - dbtree tooltip - ВерÑиÑ: - - - - File size: - dbtree tooltip - Размер файла: - - - - Encoding: - dbtree tooltip - Кодировка: - - - Error details: - dbtree tooltip - ПодробноÑти ошибки: - - - - Error: - dbtree tooltip - Ошибка: - - - - Table : %1 - dbtree tooltip - Таблица: %1 - - - - Columns (%1): - dbtree tooltip - Столбцы (%1): - - - - Indexes (%1): - dbtree tooltip - ИндекÑÑ‹ (%1): - - - - Triggers (%1): - dbtree tooltip - Триггеры (%1): - - - - Copy - Копировать - - - - Move - ПеремеÑтить - - - - Include data - Ð’ÐºÐ»ÑŽÑ‡Ð°Ñ Ð´Ð°Ð½Ð½Ñ‹Ðµ - - - - Include indexes - Ð’ÐºÐ»ÑŽÑ‡Ð°Ñ Ð¸Ð½Ð´ÐµÐºÑÑ‹ - - - - Include triggers - Ð’ÐºÐ»ÑŽÑ‡Ð°Ñ Ñ‚Ñ€Ð¸Ð³Ð³ÐµÑ€Ñ‹ - - - - Abort - Прервать - - - - Could not add dropped database file '%1' automatically. Manual setup is necessary. - Ðевозможно автоматичеÑки добавить перетÑнутый файл базы данных '%1'. Ðеобходима Ñ€ÑƒÑ‡Ð½Ð°Ñ Ð½Ð°Ñтройка. - - - - Referenced tables - СвÑзанные таблицы - - - - Do you want to include following referenced tables as well: -%1 - Ð’Ñ‹ хотите также включить Ñледующие ÑвÑзанные таблицы: -%1 - - - - Name conflict - Конфликт имён - - - - Following object already exists in the target database. -Please enter new, unique name, or press '%1' to abort the operation: - Данный объект уже ÑущеÑтвует в целевой базе данных. -ПожалуйÑта введите новое уникальное Ð¸Ð¼Ñ Ð¸Ð»Ð¸ нажмите %1 Ð´Ð»Ñ Ð¿Ñ€ÐµÑ€Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¸: - - - - SQL statements conversion - ÐšÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð°Ñ†Ð¸Ñ ÐºÐ¾Ð½Ñтрукций SQL - - - - Following error occurred while converting SQL statements to the target SQLite version: - При конвертации конÑтрукций SQL в новую верÑию SQLite произошла ошибка: - - - - Would you like to ignore those errors and proceed? - Ð’Ñ‹ хотите проигнорировать Ñти ошибки и продолжить? - - - - DdlHistoryWindow - - - Filter by database: - Фильтр по базе данных: - - - - -- Queries executed on database %1 (%2) --- Date and time of execution: %3 -%4 - -- ЗапроÑÑ‹, выполненные к базе данных %1 (%2) --- Дата и Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ: %3 -%4 - - - - DDL history - ИÑÑ‚Ð¾Ñ€Ð¸Ñ DDL - - - - DdlPreviewDialog - - - Queries to be executed - ЗапроÑÑ‹, которые будут выполнены - - - - Don't show again - Больше не показывать - - - - DebugConsole - - - SQLiteStudio Debug Console - ÐžÑ‚Ð»Ð°Ð´Ð¾Ñ‡Ð½Ð°Ñ ÐºÐ¾Ð½Ñоль SQLiteStudio - - - - EditorWindow - - - Query - Ð—Ð°Ð¿Ñ€Ð¾Ñ - - - - History - ИÑÑ‚Ð¾Ñ€Ð¸Ñ - - - - Results in the separate tab - Результаты в отдельной вкладке - - - - Results below the query - Результаты под запроÑом - - - - - SQL editor %1 - Редактор SQL %1 - - - - Results - Результаты - - - - Execute query - Выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ - - - - Explain query - План запроÑа - - - - Clear execution history - sql editor - ОчиÑтить иÑторию запроÑов - - - - Export results - sql editor - ЭкÑпортировать результаты - - - - Create view from query - sql editor - Создать предÑтавление из запроÑа - - - - Previous database - ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных - - - - Next database - Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных - - - - Show next tab - sql editor - Открыть Ñледующую вкладку - - - - Show previous tab - sql editor - Открыть предыдущую вкладку - - - - Focus results below - sql editor - Ð¤Ð¾ÐºÑƒÑ Ð½Ð° результатах внизу - - - - Focus SQL editor above - sql editor - Ð¤Ð¾ÐºÑƒÑ Ð½Ð° редакторе SQL Ñверху - - - - Delete selected SQL history entries - sql editor - Удалить выбранные запиÑи из иÑтории SQL-запроÑов - - - - Active database (%1/%2) - Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных (%1/%2) - - - - Query finished in %1 second(s). Rows affected: %2 - Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½ за %1 Ñекунд. Затронуто Ñтрок: %2 - - - - Query finished in %1 second(s). - Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½ за %1 Ñекунд. - - - - Clear execution history - ОчиÑтка иÑтории запроÑов - - - - Are you sure you want to erase the entire SQL execution history? This cannot be undone. - Ð’Ñ‹ дейÑтвительно хотите удалить вÑÑŽ иÑторию Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ SQL запроÑов? Операцию невозможно отменить. - - - - Cannot export, because no export plugin is loaded. - Ðевозможно произвеÑти ÑкÑпорт, Ñ‚.к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ ÑкÑпорта. - - - - No database selected in the SQL editor. Cannot create a view for unknown database. - Ð’ редакторе SQL не выбрана база данных. Ðевозможно Ñоздать предÑтавление в неизвеÑтной базе данных. - - - - Editor window "%1" has uncommitted data. - Ð’ окне редактора "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ данные. - - - Editor window "%1" has uncommited data. - Ð’ окне редактора "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ данные. - - - - ErrorsConfirmDialog - - - Errors - Ошибки - - - - Following errors occured: - Возникли Ñледующие ошибки: - - - - Would you like to proceed? - Ð’Ñ‹ хотите продолжить? - - - - ExecFromFileDialog - - - Execute SQL from file - Выполнение SQL-запроÑов из файла - - - - Input file - Файл-иÑточник - - - - Path to file - Путь к файлу - - - - Browse for file - Выбрать файл - - - - Options - Опции - - - - File encoding - Кодировка файла - - - - Skip failing SQL statements - ПропуÑк неудавшихÑÑ SQL-запроÑов - - - - SQL scripts (*.sql);;All files (*) - Скрипты SQL (*.sql);;Ð’Ñе файлы (*) - - - - Execute SQL file - Выполнить SQL-запроÑÑ‹ из файла - - - - Please provide file to be executed. - ПожалуйÑта укажите файл Ñ SQL-запроÑами - - - - Provided file does not exist or cannot be read. - Указанный файл не ÑущеÑтвует или не может быть прочитан. - - - - ExportDialog - - - Export - ЭкÑпорт - - - - What do you want to export? - Что вы хотите ÑкÑпортировать? - - - - A database - Базу данных - - - - A single table - Одну таблицу - - - - Query results - Результаты запроÑа - - - - Table to export - ЭкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€ÑƒÐµÐ¼Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° - - - - Database - База данных - - - - Table - Таблица - - - - Options - Опции - - - - When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. - ЕÑли Ð´Ð°Ð½Ð½Ð°Ñ Ð¾Ð¿Ñ†Ð¸Ñ Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, будет ÑкÑпортирован только DDL таблицы (конÑÑ‚Ñ€ÑƒÐºÑ†Ð¸Ñ CREATE TABLE). - - - - Export table data - ЭкÑпортировать данные таблицы - - - - Export table indexes - ЭкÑпортировать индекÑÑ‹ таблицы - - - - Export table triggers - ЭкÑпортировать триггеры таблицы - - - - Note, that exporting table indexes and triggers may be unsupported by some output formats. - Учтите, что ÑкÑпорт индекÑов и триггеров таблицы может не поддерживатьÑÑ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¼Ð¸ выходными форматами. - - - - Select database objects to export - Выберите объекты базы данных Ð´Ð»Ñ ÑкÑпорта - - - - Export data from tables - ЭкÑпортировать данные таблиц - - - - Select all - Выбрать вÑÑ‘ - - - - Deselect all - СнÑть выделение - - - - - Database: - База данных: - - - - Query to export results for - Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ ÑкÑпорта результатов - - - - Query to be executed for results: - ВыполнÑемый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… - - - - Export format and options - Формат ÑкÑпорта и опции - - - - Export format - Формат ÑкÑпорта - - - - Output - Вывод - - - - Exported file path - Путь к результирующему файлу - - - - Clipboard - Буфер обмена - - - - File - Файл - - - - Exported text encoding: - Кодировка ÑкÑпорта - - - - Export format options - Опции формата ÑкÑпорта - - - - Cancel - Отмена - - - - - - Select database to export. - Выберите базу данных Ð´Ð»Ñ ÑкÑпорта. - - - - Select table to export. - Выберите таблицу Ð´Ð»Ñ ÑкÑпорта. - - - - Enter valid query to export. - Введи корректный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ ÑкÑпорта. - - - - Select at least one object to export. - Выберите Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один объект Ð´Ð»Ñ ÑкÑпорта. - - - - You must provide a file name to export to. - Ðеобходимо указать Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°, в который будет произведён ÑкÑпорт. - - - - Path you provided is an existing directory. You cannot overwrite it. - Указанный путь ÑвлÑетÑÑ ÑущеÑтвующим каталогом. Его невозможно перезапиÑать. - - - - The directory '%1' does not exist. - Каталог '%1' не ÑущеÑтвует. - - - - The file '%1' exists and will be overwritten. - Файл '%1' ÑущеÑтвует и будет перезапиÑан. - - - - All files (*) - Ð’Ñе файлы (*) - - - - Pick file to export to - Выберите файл Ð´Ð»Ñ ÑкÑпорта - - - - Internal error during export. This is a bug. Please report it. - ВнутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° во Ð²Ñ€ÐµÐ¼Ñ ÑкÑпорта. ПожалуйÑта, вышлите отчёт об Ñтой ошибке. - - - - FileExecErrorsDialog - - - Execution errors - Ошибки Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ - - - - Following errors were encountered during execution of SQL statements from the file: - При выполнении SQL-запроÑов из файла возникли Ñледующие ошибки: - - - - SQL - SQL-Ð·Ð°Ð¿Ñ€Ð¾Ñ - - - - Error - Ошибка - - - - Statements that were executed successfully were commited. - УÑпешно выполненные запроÑÑ‹ были запиÑаны в базу. - - - - Statements that were executed successfully were rolled back. - УÑпешно выполненные запроÑÑ‹ были откачены. - - - - FontEdit - - - Choose font - font configuration - Выберите шрифт - - - - Form - - - Active SQL formatter plugin - Ðктивный модуль Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ SQL - - - - FormView - - - Commit row - form view - Подтвердить Ñтроку - - - - Rollback row - form view - Откатить Ñтроку - - - - First row - form view - ÐŸÐµÑ€Ð²Ð°Ñ Ñтрока - - - - Previous row - form view - ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ñтрока - - - - Next row - form view - Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ñтрока - - - - Last row - form view - ПоÑледнÑÑ Ñтрока - - - - Insert new row - form view - Ð’Ñтавить новую Ñтроку - - - - Delete current row - form view - Удалить текущую Ñтроку - - - - FunctionsEditor - - - Filter funtions - Отфильтровать функции - - - - Function name: - Ð˜Ð¼Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸: - - - - Implementation language: - Язык реализации: - - - - Type: - Тип: - - - - Input arguments - Передаваемые аргументы - - - - Undefined - Ðе определено - - - - Databases - Базы данных - - - - Register in all databases - ЗарегиÑтрировать во вÑех базах данных - - - - Register in following databases: - ЗарегиÑтрировать в Ñледующих базах данных: - - - - Initialization code: - Код инициализации: - - - - - Function implementation code: - Код реализации функции: - - - - Final step implementation code: - Код реализации поÑледнего шага: - - - - SQL function editor - Редактор функций SQL - - - - Commit all function changes - Подтвердить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¹ - - - - Rollback all function changes - Откатить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¹ - - - - Create new function - Создать новую функцию - - - - Delete selected function - Удалить выбранную функцию - - - - Custom SQL functions manual - РуководÑтво по Ñозданию произвольных функций SQL - - - - Add function argument - Добавить аргумент функции - - - - Rename function argument - Переименовать аргумент функции - - - - Delete function argument - Удалить аргумент функции - - - - Move function argument up - ПеремеÑтить аргумент функции вверх - - - - Move function argument down - ПеремеÑтить аргумент функции вниз - - - - Scalar - СкалÑÑ€Ð½Ð°Ñ - - - - Aggregate - ÐÐ³Ñ€ÐµÐ³Ð¸Ñ€ÑƒÑŽÑ‰Ð°Ñ - - - - Enter a non-empty, unique name of the function. - Введите непуÑтое уникальное Ð¸Ð¼Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸. - - - - Pick the implementation language. - Выберите Ñзык реализации. - - - - Per step code: - Код на каждом шаге: - - - - Enter a non-empty implementation code. - Введите непуÑтой код реализации. - - - - argument - new function argument name in function editor window - аргумент - - - - Functions editor window has uncommitted modifications. - Ð’ окне редактора функций имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ изменениÑ. - - - Functions editor window has uncommited modifications. - Ð’ окне редактора функций имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ изменениÑ. - - - - ImportDialog - - - Import data - Импорт данных - - - - Table to import to - Таблица Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° - - - - Table - Таблица - - - - Database - База данных - - - - Data source to import from - ИÑточник данных Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° - - - - Data source type - Тип иÑточника данных - - - - Options - Опции - - - - Input file: - Файл-иÑточник: - - - - Text encoding: - Кодировка текÑта: - - - - <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> - <p>ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, вÑе Ð½Ð°Ñ€ÑƒÑˆÐµÐ½Ð¸Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ð¹, неправильный формат данных (неверное количеÑтво Ñтолбцов) и любые другие проблемы, возникшие при оÑущеÑтвлении импорта, будут проигнорированы и импорт будет продолжен.</p> - - - - Ignore errors - Игнорировать ошибки - - - - Data source options - Опции иÑточника данных - - - - Cancel - Отмена - - - - If you type table name that doesn't exist, it will be created. - ЕÑли вы введёте неÑущеÑтвующее Ð¸Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹, она будет Ñоздана. - - - - Enter the table name - Введите Ð¸Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ - - - - Select import plugin. - Выберите модуль импорта. - - - - You must provide a file to import from. - Ðеобходимо указать файл, из которого оÑущеÑтвлÑетÑÑ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚. - - - - The file '%1' does not exist. - Файл '%1' не ÑущеÑтвует. - - - - Path you provided is a directory. A regular file is required. - Указанный путь ÑвлÑетÑÑ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð¾Ð¼. Ðеобходимо указать файл. - - - - Pick file to import from - Выберите файл Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° - - - - IndexDialog - - - - Index - Ð˜Ð½Ð´ÐµÐºÑ - - - - On table: - Ð”Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹: - - - - Index name: - Ð˜Ð¼Ñ Ð¸Ð½Ð´ÐµÐºÑа: - - - - Partial index condition - УÑловие Ð´Ð»Ñ Ñ‡Ð°Ñтичного индекÑа - - - - Unique index - Уникальный Ð¸Ð½Ð´ÐµÐºÑ - - - - Column - Столбец - - - - Collation - Сравнение - - - - Sort - Сортировка - - - - Delete selected indexed expression - Удалить выбранное индекÑируемое выражение - - - - Moves selected index column up in the order, making it more significant in the index. - ПеремеÑтить индекÑируемый Ñтолбец выше по ÑпиÑку, ÑƒÐ²ÐµÐ»Ð¸Ñ‡Ð¸Ð²Ð°Ñ ÐµÐ³Ð¾ значимоÑть в индекÑе. - - - - Moves selected index column down in the order, making it less significant in the index. - ПеремеÑтить индекÑируемый Ñтолбец ниже по ÑпиÑку, ÑÐ½Ð¸Ð¶Ð°Ñ ÐµÐ³Ð¾ значимоÑть в индекÑе. - - - - Edit selected indexed expression - Редактировать выбранное индекÑируемое выражение - - - - Add indexed expression - Добавить индекÑируемое выражение - - - - DDL - DDL - - - - Tried to open index dialog for closed or inexisting database. - Попытка вызвать диалог ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑа Ð´Ð»Ñ Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¾Ð¹ или неÑущеÑтвующей базы данных. - - - - Could not process index %1 correctly. Unable to open an index dialog. - Ðе удалоÑÑŒ корректно обработать Ð¸Ð½Ð´ÐµÐºÑ %1. Ðевозможно открыть окно индекÑа. - - - - Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. - Уникальный Ð¸Ð½Ð´ÐµÐºÑ Ð½Ðµ может Ñодержать индекÑируемые выражениÑ. Либо удалите Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¸Ð· ÑпиÑка ниже, либо отключите Ñту опцию. - - - - Pick the table for the index. - Выберите таблицу Ð´Ð»Ñ Ð¸Ð½Ð´ÐµÐºÑа. - - - - Select at least one column. - Выберите Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один Ñтолбец. - - - - Enter a valid condition. - Введите корректное уÑловие. - - - - default - index dialog - по умолчанию - - - - Sort order - table constraints - ПорÑдок Ñортировки - - - - - Error - index dialog - Ошибка - - - - Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? - Ðевозможно Ñоздать уникальный индекÑ, Ñ‚.к. данные в выбранных Ñтолбцах неуникальны. Ð’Ñ‹ хотите выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ SELECT Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра проблемных данных? - - - - An error occurred while executing SQL statements: -%1 - При выполнении конÑтрукций SQL произошла ошибка: -%1 - - - - IndexExprColumnDialog - - - Indexed expression - ИндекÑируемое выражение - - - - Expression to index - Выражение Ð´Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð² Ð¸Ð½Ð´ÐµÐºÑ - - - - This expression is already indexed by the index. - Такое выражение уже приÑутÑтвует в индекÑе. - - - - Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. - Столбец необходимо индекÑировать напрÑмую, а не выражением. Либо добавьте в выражение что-либо кроме имени Ñтолбца, либо отмените Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸ отметьте Ñтолбец непоÑредÑтвенно в окне индекÑа. - - - - Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. - Столбец '%1' не принадлежит к индекÑируемой таблице. ИндекÑируемые Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ ÑÑылатьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ на Ñтолбцы индекÑируемой таблицы. - - - - It's forbidden to use 'SELECT' statements in indexed expressions. - Запрещено иÑпользовать конÑтрукции SELECT в индекÑируемых выражениÑÑ…. - - - - Enter an indexed expression. - Введите индекÑируемое выражение. - - - - Invalid expression. - Ðекорректное выражение. - - - - LanguageDialog - - - Language - Язык - - - - Please choose language: - ПожалйуÑта, выберите Ñзык: - - - - MainWindow - - - Database toolbar - Панель базы данных - - - - Structure toolbar - Панель Ñтруктуры - - - - Tools - ИнÑтрументы - - - - Window list - СпиÑок окон - - - - View toolbar - Панель Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ - - - - Configuration widgets - Виджеты конфигурации - - - - Syntax highlighting engines - Движки ÑинтакÑичеÑкой подÑветки - - - - Data editors - Редакторы данных - - - - Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - Отладочный режим. Ðажмите %1 или воÑпользуйтеÑÑŒ пунктом меню 'Справка / Открыть отладочную конÑоль' Ð´Ð»Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð¸Ñ Ð¾Ñ‚Ð»Ð°Ð´Ð¾Ñ‡Ð½Ð¾Ð¹ конÑоли. - - - - Running in debug mode. Debug messages are printed to the standard output. - Отладочный режим. Отладочные ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð²Ñ‹Ð²Ð¾Ð´ÑÑ‚ÑÑ Ð² Ñтандартный выходной поток. - - - - You need to restart application to make the language change take effect. - Ð”Ð»Ñ Ñмены Ñзыка необходимо перезапуÑтить приложение. - - - Open SQL editor - Открыть редактор SQL - - - Open DDL history - Открыть иÑторию DDL - - - Open SQL functions editor - Открыть редактор функций SQL - - - Open collations editor - Открыть редактор Ñравнений - - - Import - Импорт - - - Export - ЭкÑпорт - - - Open configuration dialog - Открыть диалог конфигурации - - - Tile windows - РаÑположить окна плиткой - - - Tile windows horizontally - РаÑположить окна по горизонтали - - - Tile windows vertically - РаÑположить окна по вертикали - - - Cascade windows - РаÑположить окна каÑкадом - - - - Next window - Следующее окно - - - - Previous window - Предыдущее окно - - - - Hide status field - Скрыть окно ÑтатуÑа - - - Close selected window - Закрыть выбранное окно - - - Close all windows but selected - Закрыть вÑе окна, кроме выбранного - - - Close all windows - Закрыть вÑе окна - - - Restore recently closed window - ВоÑÑтановить недавно закрытые окна - - - Rename selected window - Переименовать выбранное окно - - - - Open Debug Console - Открыть отладочную конÑоль - - - - Open CSS Console - Открыть конÑоль CSS - - - Report a bug - Сообщить об ошибке - - - Propose a new feature - Предложить новый функционал - - - About - О программе - - - Licenses - Лицензии - - - Open home page - Открыть домашнюю Ñтраницу - - - Open forum page - Открыть Ñтраницу форума - - - User Manual - РуководÑтво Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ - - - SQLite documentation - Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ Ð¿Ð¾ SQLite - - - Report history - ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¾Ñ‚Ñ‡Ñ‘Ñ‚Ð¾Ð² - - - Check for updates - Проверить Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ - - - Database - menubar - База данных - - - Structure - menubar - Структура - - - View - menubar - Вид - - - - Window list - menubar view menu - Окна - - - Tools - menubar - ИнÑтрументы - - - Help - Справка - - - - Open SQL &editor - &Открыть редактор SQL - - - - Open DDL &history - О&ткрыть иÑторию DDL - - - - Open SQL &functions editor - От&крыть редактор функций SQL - - - - Open &collations editor - Отк&рыть редактор Ñравнений - - - - Open ex&tension manager - Откр&ыть менеджер раÑширений - - - - &Import - &Импорт - - - - E&xport - &ЭкÑпорт - - - - Open confi&guration dialog - Открыт&ÑŒ диалог конфигурации - - - - &Tile windows - Р&аÑположить окна плиткой - - - - Tile windows &horizontally - РаÑпо&ложить окна по горизонтали - - - - Tile windows &vertically - РаÑполо&жить окна по вертикали - - - - &Cascade windows - Ра&Ñположить окна каÑкадом - - - - Close selected &window - &Закрыть выбранное окно - - - - Close all windows &but selected - Закрыть &вÑе окна, кроме выбранного - - - - Close &all windows - Закрыть вÑ&е окна - - - - Re&store recently closed window - ВоÑÑта&новить поÑледнее закрытое окно - - - - &Rename selected window - Переи&меновать выбранное окно - - - - Report a &bug - Сообщить об о&шибке - - - - Propose a new &feature - Предложить новую &функцию - - - - &About - О про&грамме - - - - &Licenses - Ли&цензии - - - - Open home &page - Открыть домашн&ÑŽÑŽ Ñтраницу - - - - Open fo&rum page - Открыть Ñтраниц&у форума - - - - User &Manual - РуководÑтво пользовател&Ñ - - - - SQLite &documentation - &Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ Ð¿Ð¾ SQLite - - - - Bugs and feature &requests - Оши&бки и Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ - - - - Check for &updates - &Проверить Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ - - - - &Database - menubar - &База данных - - - - &Structure - menubar - &Структура - - - - &View - menubar - &Вид - - - - &Tools - menubar - &ИнÑтрументы - - - - &Help - С&правка - - - - Could not set style: %1 - main window - Ðевозможно применить Ñтиль: %1 - - - - Cannot export, because no export plugin is loaded. - Ðевозможно произвеÑти ÑкÑпорт, Ñ‚.к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ ÑкÑпорта. - - - - Cannot import, because no import plugin is loaded. - Ðевозможно произвеÑти импорт, Ñ‚.к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð°. - - - - Rename window - Переименовать окно - - - - Enter new name for the window: - Введите новое Ð¸Ð¼Ñ Ð´Ð»Ñ Ð¾ÐºÐ½Ð°: - - - - New updates are available. <a href="%1">Click here for details</a>. - ДоÑтупны обновлениÑ. <a href="%1">Ðажмите здеÑÑŒ Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñтей</a>. - - - - You're running the most recent version. No updates are available. - УÑтановлена поÑледнÑÑ Ð²ÐµÑ€ÑиÑ. Обновлений нет. - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - База данных, Ð¿ÐµÑ€ÐµÐ´Ð°Ð½Ð½Ð°Ñ Ñ‡ÐµÑ€ÐµÐ· аргументы командной Ñтроки (%1), уже находитÑÑ Ð² ÑпиÑке под именем %2 - - - - Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 - База данных, Ð¿ÐµÑ€ÐµÐ´Ð°Ð½Ð½Ð°Ñ Ñ‡ÐµÑ€ÐµÐ· аргументы командной Ñтроки (%1), была временно добавлена в ÑпиÑок под именем %2 - - - - Could not add database %1 to list. - Ðевозможно добавить базу данных %1 в ÑпиÑок. - - - - MdiWindow - - Uncommited changes - Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ - - - - Uncommitted changes - Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ - - - - Close anyway - Ð’ÑÑ‘ равно закрыть - - - - Don't close - Ðе закрывать - - - - MultiEditor - - - Null value - multieditor - Значение Null - - - - Configure editors for this data type - ÐаÑтройте редакторы Ð´Ð»Ñ Ñтого типа данных - - - - Open another tab - Открыть дополнительную вкладку - - - - Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. - Модуль редактора данных '%1' не загружен, Ñ…Ð¾Ñ‚Ñ ÑƒÐºÐ°Ð·Ð°Ð½ Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ‚Ð¸Ð¿Ð° данных '%1' - - - - Deleted - multieditor - Удалено - - - - Read only - multieditor - Только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ - - - - MultiEditorBool - - Boolean - ЛогичеÑкое - - - - MultiEditorBoolPlugin - - - Boolean - ЛогичеÑкое - - - - MultiEditorDate - - Date - Дата - - - - MultiEditorDatePlugin - - - Date - Дата - - - - MultiEditorDateTime - - Date & time - Дата и Ð²Ñ€ÐµÐ¼Ñ - - - - MultiEditorDateTimePlugin - - - Date & time - Дата и Ð²Ñ€ÐµÐ¼Ñ - - - - MultiEditorHex - - Hex - ШеÑтнадцатеричное - - - - MultiEditorHexPlugin - - - Hex - ШеÑтнадцатеричное - - - - MultiEditorNumeric - - Number - numeric multi editor tab name - ЧиÑло - - - - MultiEditorNumericPlugin - - - Number - numeric multi editor tab name - ЧиÑло - - - - MultiEditorText - - Text - ТекÑÑ‚ - - - - Tab changes focus - Tab перемещает Ñ„Ð¾ÐºÑƒÑ - - - - Cut - Вырезать - - - - Copy - Копировать - - - - Paste - Ð’Ñтавить - - - - Delete - Удалить - - - - Undo - Отменить - - - - Redo - Повторить - - - - MultiEditorTextPlugin - - - Text - ТекÑÑ‚ - - - - MultiEditorTime - - Time - Ð’Ñ€ÐµÐ¼Ñ - - - - MultiEditorTimePlugin - - - Time - Ð’Ñ€ÐµÐ¼Ñ - - - - NewConstraintDialog - - - New constraint - Ðовое ограничение - - - - - Primary Key - new constraint dialog - Первичный ключ - - - - - Foreign Key - new constraint dialog - Внешний ключ - - - - - Unique - new constraint dialog - УникальноÑть - - - - - Check - new constraint dialog - Проверка - - - - Not NULL - new constraint dialog - Ðе NULL - - - - Collate - new constraint dialog - Сравнение - - - - Default - new constraint dialog - Значение по умолчанию - - - - NewVersionDialog - - - SQLiteStudio updates - ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ SQLiteStudio - - - - New updates are available! - ДоÑтупны обновлениÑ! - - - - Component - Компонент - - - - This application will be closed and the update installer will start to download and install all the updates. - Приложение будет закрыто, и уÑтановщик обновлений начнёт загрузку и уÑтановку обновлений. - - - Current version - Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ - - - - Update version - ÐÐ¾Ð²Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ - - - - Check for updates on startup - ПроверÑть Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ запуÑке - - - - Update to new version! - Обновить до новой верÑии! - - - The update will be automatically downloaded and installed. This will also restart application at the end. - Обновление будет автоматичеÑки загружено и уÑтановлено. Ð’ конце процеÑÑа приложение будет перезапущено. - - - - Not now. - Ðе ÑейчаÑ. - - - - Don't install the update and close this window. - Ðе уÑтанавливать обновление и закрыть данное окно. - - - - PopulateConfigDialog - - - Populating configuration - ÐаÑтройка Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ - - - - Configuring <b>%1</b> for column <b>%2</b> - ÐаÑтройка <b>%1</b> Ð´Ð»Ñ Ñтолбца <b>%2</b> - - - - PopulateDialog - - - Populate table - Заполнить таблицу - - - - Database - База данных - - - - Table - Таблица - - - - Columns - Столбцы - - - - Number of rows to populate: - КоличеÑтво заполнÑемых Ñтрок: - - - - Populate - populate dialog button - Заполнить - - - - Abort - Прервать - - - - Configure - ÐаÑтроить - - - - Populating configuration for this column is invalid or incomplete. - ÐаÑтройка Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ Ñтолбца некорректна или незавершена. - - - - Select database with table to populate - Выберите базу данных Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†ÐµÐ¹ Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ - - - - Select table to populate - Выберите таблицу Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ - - - - You have to select at least one column. - Ðеобходимо выбрать Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один Ñтолбец. - - - - QObject - - - Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). - Ðевозможно редактировать Ñтолцбы, ÑвлÑющиеÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð¼ ÑоÑтавных конÑтрукций %1 (те, которые включают ключевые Ñлова %2, %3 и %4). - - - - The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. - Ð’ механизме Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов возникли проблемы при корректном извлечении значений ROWID. Предположительно Ñто ошибка в приложении. Возможно Ñтоит отправить отчёт об ошибке. - - - - Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. - Запрошенный Ñтолбец ÑвлÑетÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð¼ SQL выражениÑ, а не проÑто выбором Ñтолбца. Такие Ñтолбцы не могут быть отредактированы. - - - - Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. - Запрошенный Ñтолбец принадлежит Ñлужебной таблице SQLite. Эти таблицы Ð½ÐµÐ»ÑŒÐ·Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ напрÑмую. - - - - Cannot edit results of query other than %1. - Ðевозможно редактировать результаты запроÑов, отличных от %1. - - - - Cannot edit columns that are result of aggregated %1 statements. - Ðевозможно редактировать Ñтолцбы, ÑвлÑющиеÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð¼ агрегирующих конÑтрукций %1. - - - - Cannot edit columns that are result of %1 statement. - Ðевозможно редактировать Ñтолцбы, ÑвлÑющиеÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð¼ конÑтрукции %1. - - - - Cannot edit columns that are result of common table expression statement (%1). - Ðевозможно редактировать Ñтолцбы, ÑвлÑющиеÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð¼ обобщённого табличного Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ (%1). - - - - - - - on conflict: %1 - data view tooltip - при конфликте: %1 - - - - references table %1, column %2 - data view tooltip - ÑÑылаетÑÑ Ð½Ð° таблицу %1, Ñтолбец %2 - - - - condition: %1 - data view tooltip - уÑловие: %1 - - - - collation name: %1 - data view tooltip - Ð¸Ð¼Ñ ÑравнениÑ: %1 - - - - Data grid view - Табличный вид данных - - - - Copy cell(s) contents to clipboard - Копировать Ñодержимое Ñчеек в буфер обмена - - - - Copy cell(s) contents together with header to clipboard - Копировать Ð¸Ð¼Ñ Ñтолбца и Ñодержимое Ñчеек в буфер обмена - - - - Paste cell(s) contents from clipboard - Ð’Ñтавить Ñодержимое Ñчеек из буфера обмена - - - - Set empty value to selected cell(s) - УÑтановить пуÑтое значение Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ñчеек - - - - Set NULL value to selected cell(s) - УÑтановить Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ñчеек значение NULL - - - - Commit changes to cell(s) contents - Подтвердить изменение Ñодержимого Ñчеек - - - - Rollback changes to cell(s) contents - Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñодержимого Ñчеек - - - - Delete selected data row - Удалить выбранную Ñтроку данных - - - - Insert new data row - Ð’Ñтавить новую Ñтроку данных - - - - Open contents of selected cell in a separate editor - Открыть Ñодержимое выбранной Ñчейки в отдельном редакторе - - - - Total pages available: %1 - Ð’Ñего доÑтупно Ñтраниц: %1 - - - - Total rows loaded: %1 - Ð’Ñего загружено Ñтрок: %1 - - - - Data view (both grid and form) - Окно данных (и табличный вид, и форма) - - - - Refresh data - Обновить данные - - - - Switch to grid view of the data - ПереключитьÑÑ Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ‡Ð½Ð¾Ð³Ð¾ вида на форму - - - - Switch to form view of the data - ПереключитьÑÑ Ð¸Ð· формы на табличный вид - - - - Database list - СпиÑок баз данных - - - - Delete selected item - Удалить выбранный Ñлемент - - - - Clear filter contents - СброÑить Ñодержимое фильтра - - - - Refresh schema - Обновить Ñтруктуру - - - - Refresh all schemas - Обновить Ñтруктуры вÑех баз данных - - - - Add database - Добавить базу данных - - - - Select all items - Выделить вÑе Ñлементы - - - - Copy selected item(s) - Копировать выбранные Ñлементы - - - - - - Paste from clipboard - Ð’Ñтавить из буфера обмена - - - - Tables - Таблицы - - - - Indexes - ИндекÑÑ‹ - - - - Triggers - Триггеры - - - - Views - ПредÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ - - - - Columns - Столбцы - - - - Data form view - Форма - - - - Commit changes for current row - Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ Ñтроки - - - - Rollback changes for current row - Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ Ñтроки - - - - Go to first row on current page - Перейти к первой Ñтроке текущей Ñтраницы - - - - Go to next row - Перейти к Ñледующей Ñтроке - - - - Go to previous row - Перейти к предыдущей Ñтроке - - - - Go to last row on current page - Перейти к поÑледней Ñтроке текущей Ñтраницы - - - - Insert new row - Ð’Ñтавить новую Ñтроку - - - - Delete current row - Удалить текущую Ñтроку - - - - Main window - Главное окно - - - - Open SQL editor - Открыть редактор SQL - - - - Previous window - Предыдущее окно - - - - Next window - Следующее окно - - - - Hide status area - Скрыть облаÑть ÑтатуÑа - - - - Open configuration dialog - Открыть диалог конфигурации - - - - Open Debug Console - Открыть отладочную конÑоль - - - - Open CSS Console - Открыть конÑоль CSS - - - - Cell text value editor - Редактор текÑтового Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ‡ÐµÐ¹ÐºÐ¸ - - - - - Cut selected text - Вырезать выбранный текÑÑ‚ - - - - - Copy selected text - Копировать выбранный текÑÑ‚ - - - - - Delete selected text - Удалить выбранный текÑÑ‚ - - - - - Undo - Отменить - - - - - Redo - Повторить - - - - SQL editor input field - Поле ввода редактора SQL - - - - Select whole editor contents - Выбрать вÑÑ‘ Ñодержимое редактора - - - - Save contents into a file - Сохранить Ñодержимое в файл - - - - Load contents from a file - Загрузить Ñодержимое из файла - - - - Find in text - Ðайти в текÑте - - - - Find next - Ðайти далее - - - - Find previous - Ðайти предыдущее - - - - Replace in text - Замена в текÑте - - - - Delete current line - Удалить текущую Ñтрочку - - - - Request code assistant - Вызвать автодополнение - - - - Format contents - Форматировать Ñодержимое - - - - Move selected block of text one line down - ПеремеÑтить выбранный блок текÑта на Ñтрочку вниз - - - - Move selected block of text one line up - ПеремеÑтить выбранный блок текÑта на Ñтрочку вверх - - - - Copy selected block of text and paste it a line below - Скопировать блок текÑта и вÑтавить его Ñтрочкой ниже - - - - Copy selected block of text and paste it a line above - Скопировать блок текÑта и вÑтавить его Ñтрочкой выше - - - - Toggle comment - Комментировать/раÑкомментировать - - - - All SQLite databases - Ð’Ñе базы данных SQLite - - - - All files - Ð’Ñе файлы - - - - - Database file - Файл баз данных - - - Reports history window - Окно иÑтории отчётов - - - Delete selected entry - Удалить выбранную запиÑÑŒ - - - - SQL editor window - Окно редактора SQL - - - - Execute query - Выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ - - - - Execute "%1" query - Выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ "%1" - - - - Switch current working database to previous on the list - Переключить текущую базу данных на предыдущую в ÑпиÑке - - - - Switch current working database to next on the list - Переключить текущую базу данных на Ñледующую в ÑпиÑке - - - - Go to next editor tab - Перейти на Ñледующую вкладку редактора - - - - Go to previous editor tab - Перейти на предыдущую вкладку редактора - - - - Move keyboard input focus to the results view below - ПеремеÑтить Ñ„Ð¾ÐºÑƒÑ Ð²Ð²Ð¾Ð´Ð° в окно результатов внизу - - - - Move keyboard input focus to the SQL editor above - ПеремеÑтить Ñ„Ð¾ÐºÑƒÑ Ð²Ð²Ð¾Ð´Ð° в окно редактора SQL вверху - - - - Delete selected SQL history entries - Удалить выбранные запиÑи из иÑтории SQL-запроÑов - - - - Table window - Окно таблицы - - - - Refresh table structure - Обновить Ñтруктуру таблицы - - - - Add new column - Добавить новый Ñтолбец - - - - Edit selected column - Редактировать выбранный Ñтолбец - - - - Delete selected column - Удалить выбранный Ñтолбец - - - - Export table data - ЭкÑпортировать данные таблицы - - - - Import data to the table - Импортировать данные в таблицу - - - - Add new table constraint - Добавить новое ограничение на таблицу - - - - Edit selected table constraint - Редактировать выбранное ограничение на таблицу - - - - Delete selected table constraint - Удалить выбранное ограничение на таблицу - - - - Refresh table index list - Обновить ÑпиÑок индекÑов таблицы - - - - Add new index - Добавить новый Ð¸Ð½Ð´ÐµÐºÑ - - - - Edit selected index - Редактировать выбранный Ð¸Ð½Ð´ÐµÐºÑ - - - - Delete selected index - Удалить выбранный Ð¸Ð½Ð´ÐµÐºÑ - - - - Refresh table trigger list - Обновить ÑпиÑок триггеров таблицы - - - - - Add new trigger - Добавить новый триггер - - - - - Edit selected trigger - Редактировать выбранный триггер - - - - - Delete selected trigger - Удалить выбранный триггер - - - - - Go to next tab - Перейти к Ñледующей вкладке - - - - - Go to previous tab - Перейти к предыдущей вкладке - - - - A view window - Окно предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ - - - - Refresh view trigger list - Обновить ÑпиÑок индекÑов предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ - - - - QuitConfirmDialog - - Uncommited changes - Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ - - - - Uncommitted changes - Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ - - - - Are you sure you want to quit the application? - -Following items are pending: - Ð’Ñ‹ дейÑтвительно хотите выйти из приложениÑ? - -Следующие Ñлементы ожидают подтверждениÑ: - - - - SearchTextDialog - - - Find or replace - Ðайти и заменить - - - - Find: - Ðайти: - - - - Case sensitive - Учитывать региÑтр - - - - Search backwards - ПоиÑк в обратном направлении - - - - Regular expression matching - ИÑпользовать регулÑрное выражение - - - - Replace && -find next - Заменить и найти далее - - - - Replace with: - Заменить на: - - - - Replace all - Заменить вÑÑ‘ - - - - Find - Ðайти - - - - SortDialog - - - Sort by columns - Сортировка по Ñтолбцам - - - - - Column - Столбец - - - - - Order - ПорÑдок - - - - Sort by: %1 - Сортировка по: %1 - - - - Move column up - ПеремеÑтить Ñтолбец вверх - - - - Move column down - ПеремеÑтить Ñтолбец вниз - - - - SqlEditor - - - Cut - sql editor - Вырезать - - - - Copy - sql editor - Копировать - - - - Paste - sql editor - Ð’Ñтавить - - - - Delete - sql editor - Удалить - - - - Select all - sql editor - Выделить вÑÑ‘ - - - - Undo - sql editor - Отменить - - - - Redo - sql editor - Повторить - - - - Complete - sql editor - Завершить - - - - Format SQL - sql editor - Форматировать SQL - - - - Save SQL to file - sql editor - Сохранить SQL в файл - - - - Select file to save SQL - sql editor - Выбрать файл Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ SQL - - - - Load SQL from file - sql editor - Загрузить SQL из файла - - - - Delete line - sql editor - Удалить Ñтрочку - - - - Move block down - sql editor - ПеремеÑтить блок вниз - - - - Move block up - sql editor - ПеремеÑтить блок вверх - - - - Copy block down - sql editor - Копировать блок вниз - - - - Copy up down - sql editor - Копировать блок вверх - - - - Find - sql editor - Ðайти - - - - Find next - sql editor - Ðайти далее - - - - Find previous - sql editor - Ðайти предыдущее - - - - Replace - sql editor - Заменить - - - - Toggle comment - sql editor - Комментировать/раÑкомментировать - - - - Saved SQL contents to file: %1 - SQL-код Ñохранён в файле %1 - - - - Syntax completion can be used only when a valid database is set for the SQL editor. - Дополнение ÑинтакÑиÑа может быть иÑпользовано только поÑле Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð¹ базы данных редактору SQL. - - - - Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. - Размер Ñодержимого редактора SQL Ñлишком велико, поÑтому обнаружение ошибок и подÑветка ÑущеÑтвующих объектов временно отключена. - - - - Save to file - Сохранить в файл - - - - Could not open file '%1' for writing: %2 - Ðевозможно открыть файл '%1' Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи: %2 - - - - SQL scripts (*.sql);;All files (*) - Скрипты SQL (*.sql);;Ð’Ñе файлы (*) - - - - Open file - Открыть файл - - - - Could not open file '%1' for reading: %2 - Ðевозможно открыть файл '%1' Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ: %2 - - - - Reached the end of document. Hit the find again to restart the search. - ДоÑтигнут конец документа. Ðажмите Ðайти Ñнова Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑка поиÑка. - - - - SqlQueryItem - - - Column: - data view tooltip - Столбец: - - - - Data type: - data view - Тип данных: - - - - Table: - data view tooltip - Таблица: - - - - Constraints: - data view tooltip - ОграничениÑ: - - - This cell is not editable, because: %1 - Эта Ñчейка нередактируема, причина: %1 - - - - Cannot load the data for a cell that refers to the already closed database. - Ðевозможно загрузить данные Ñчейки, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÑÑылаетÑÑ Ð½Ð° уже закрытую базу данных. - - - - SqlQueryItemDelegate - - Cannot edit this cell. Details: %2 - Ðевозможно редактировать данную Ñчейку. ПодробноÑти: %2 - - - - The row is marked for deletion. - Строка помечена Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ. - - - - - - - - Cannot edit this cell. Details: %1 - Ðевозможно редактировать данную Ñчейку. ПодробноÑти: %1 - - - - - Structure of this table has changed since last data was loaded. Reload the data to proceed. - Структура Ñтой таблицы изменилаÑÑŒ Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° поÑледней загрузки данных. Перезагрузите данные Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ. - - - - Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). - Редактирование значений большой длины непоÑредÑтвенно в табличном режиме не рекомендуетÑÑ. Возможны проблемы Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñтью и удобÑтвом работы. Ð”Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ Ñ‚Ð°ÐºÐ¸Ð¼Ð¸ большими значениÑми рекомендуетÑÑ Ð¸Ñпользовать режим формы либо отдельное окно Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ (доÑтупно в контекÑтном меню по щелчку правой кнопкой мыши). - - - - Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. - Внешний ключ Ð´Ð»Ñ Ñтолбца %2 имеет более чем %1 возможных значений. Это Ñлишком много Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð² выпадающем ÑпиÑке. Вам необходимо ввеÑти значение вручную. - - - - SqlQueryModel - - - - Only one query can be executed simultaneously. - Одновременно может быть выполнен только один запроÑ. - - - Uncommited data - Ðеподтверждённые данные - - - There are uncommited data changes. Do you want to proceed anyway? All uncommited changes will be lost. - ИмеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…. Ð’Ñ‹ дейÑтвительно хотите продолжить? Ð’Ñе неподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÑƒÑ‚ утерÑны. - - - - Cannot commit the data for a cell that refers to the already closed database. - Ðевозможно подтвердить данные Ð´Ð»Ñ Ñчейки, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÑÑылаетÑÑ Ð½Ð° уже закрытую базу данных. - - - - Could not begin transaction on the database. Details: %1 - Ðевозможно начать транзакцию в базе данных. ПодробноÑти: %1 - - - An error occurred while commiting the transaction: %1 - При завершении транзакции возникла ошибка: %1 - - - - An error occurred while rolling back the transaction: %1 - При отмене транзакции возникла ошибка: %1 - - - - Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. - Попытка Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… Ð´Ð»Ñ Ð½ÐµÑ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€ÑƒÐµÐ¼Ð¾Ð¹ Ñчейки (ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ñ‚ÐµÐ¼ не менее была изменена и ожидает подтверждениÑ). Это Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. ПожалуйÑта, отправьте о ней отчёт. - - - An error occurred while commiting the data: %1 - При подтверждении данных произошла ошибка: %1 - - - - Uncommitted data - Ðеподтверждённые данные - - - - There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. - ИмеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…. Ð’Ñ‹ дейÑтвительно хотите продолжить? Ð’Ñе неподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÑƒÑ‚ утерÑны. - - - - An error occurred while committing the transaction: %1 - При завершении транзакции возникла ошибка: %1 - - - - An error occurred while committing the data: %1 - При подтверждении данных произошла ошибка: %1 - - - - Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - КоличеÑтво Ñтрок на Ñтранице было уменьшено до %1 из-за большого количеÑтва Ñтолбцов (%2) в окне данных. - - - - - Error while executing SQL query on database '%1': %2 - Ошибка при выполнении SQL запроÑа к базе данных '%1': %2 - - - - Error while loading query results: %1 - Ошибка при загрузке результатов запроÑа: %1 - - - - Insert multiple rows - Ð’Ñтавить неÑколько Ñтрок - - - - Number of rows to insert: - КоличеÑтво вÑтавлÑемых Ñтрок: - - - - SqlQueryView - - - Go to referenced row in... - Перейти к ÑвÑзанной Ñтроке в... - - - - Copy - Копировать - - - - Copy as... - Копировать как... - - - - Paste - Ð’Ñтавить - - - - Paste as... - Ð’Ñтавить как... - - - - Set NULL values - УÑтановить Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð½Ð° NULL - - - - Erase values - Стереть Ñодержимое - - - - Edit value in editor - Править Ñодержимое в редакторе - - - - Commit - Подтвердить - - - - Copy with headers - Копировать Ñ Ð¸Ð¼ÐµÐ½Ð°Ð¼Ð¸ Ñтолбцов - - - - Rollback - Откатить - - - - Commit selected cells - Подтвердить выбранные Ñчейки - - - - Rollback selected cells - Откатить выбранные Ñчейки - - - - Define columns to sort by - Определить Ñтолбцы Ð´Ð»Ñ Ñортировки - - - - Remove custom sorting - СброÑить указанную Ñортировку - - - - Insert row - Ð’Ñтавить Ñтроку - - - - Insert multiple rows - Ð’Ñтавить неÑколько Ñтрок - - - - Delete selected row - Удалить выбранную Ñтроку - - - - Show value in a viewer - Показать значение в проÑмотрщике - - - - Generate query for selected cells - Сгенерировать Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ñчеек - - - - No items selected to paste clipboard contents to. - Ðе выбраны Ñлементы Ð´Ð»Ñ Ð²Ñтавки в них Ñодержимого буфера обмена. - - - - Go to referenced row in table '%1' - Перейти к ÑвÑзанной Ñтроке в таблице '%1' - - - - table '%1' - таблица '%1' - - - - Referenced row (%1) - СвÑÐ·Ð°Ð½Ð½Ð°Ñ Ñтрока (%1) - - - - Trim pasted text? - Обрезать вÑтавленный текÑÑ‚? - - - - The pasted text contains leading or trailing white space. Trim it automatically? - Ð’ начале либо конце вÑтавленного текÑта находÑÑ‚ÑÑ Ð½ÐµÐ¿ÐµÑ‡Ð°Ñ‚Ð°ÐµÐ¼Ñ‹Ðµ Ñимволы. Обрезать их автоматичеÑки? - - - - Edit value - Править Ñодержимое - - - - SqlTableModel - - Error while commiting new row: %1 - Ошибка при подтверждении новой Ñтроки: %1 - - - - Error while committing new row: %1 - Ошибка при подтверждении новой Ñтроки: %1 - - - - Error while deleting row from table %1: %2 - Ошибка при удалении Ñтроки из таблицы %1: %2 - - - - SqliteExtensionEditor - - - Filter extensions - Фильтр раÑширений - - - - Leave empty to use default function - ОÑтавьте пуÑтым Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ по умолчанию - - - - Extension file - Файл раÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ - - - - Initialization function - Ð˜Ð½Ð¸Ñ†Ð¸Ð°Ð»Ð¸Ð·Ð¸Ñ€ÑƒÑŽÑ‰Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ - - - - Databases - Базы данных - - - - Register in all databases - ЗарегиÑтрировать во вÑех базах данных - - - - Register in following databases: - ЗарегиÑтрировать в Ñледующих базах данных: - - - - Extension manager window has uncommitted modifications. - Ð’ менеджере раÑширений имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ изменениÑ. - - - - Extension manager - Менеджер раÑширений - - - - Commit all extension changes - Подтвердить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ€Ð°Ñширений - - - - Rollback all extension changes - Откатить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ€Ð°Ñширений - - - - Add new extension - Добавить новое раÑширение - - - - Remove selected extension - Удалить выбранное раÑширение - - - - Editing extensions manual - РуководÑтво по редактированию раÑширений - - - - File with given path does not exist or is not readable. - Файл по указанному пути не ÑущеÑтвует или не читаетÑÑ. - - - - Unable to load extension: %1 - Ðевозможно загрузить раÑширение: %1 - - - - Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - Ðекорректное Ð¸Ð¼Ñ Ð¸Ð½Ð¸Ñ†Ð¸Ð°Ð»Ð¸Ð·Ð¸Ñ€ÑƒÑŽÑ‰ÐµÐ¹ функции. Ð˜Ð¼Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ может ÑоÑтоÑть только из английÑких букв, цифр и подчёркиваниÑ. - - - - Dynamic link libraries (*.dll);;All files (*) - ДинамичеÑки подключаемые библиотеки (*.dll);;Ð’Ñе файлы (*) - - - - Shared objects (*.so);;All files (*) - Общие объекты (*.so);;Ð’Ñе файлы (*) - - - - Dynamic libraries (*.dylib);;All files (*) - ДинамичеÑкие библиотеки (*.dylib);;Ð’Ñе файлы (*) - - - - All files (*) - Ð’Ñе файлы (*) - - - - Open file - Открыть файл - - - - StatusField - - - Status - Ð¡Ñ‚Ð°Ñ‚ÑƒÑ - - - - Copy - Копировать - - - - Clear - ОчиÑтить - - - - TableConstraintsModel - - - Type - table constraints - Тип - - - - Details - table constraints - ПодробноÑти - - - - Name - table constraints - Ð˜Ð¼Ñ - - - - TableForeignKeyPanel - - - Foreign table: - ВнешнÑÑ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°: - - - - SQLite 2 does not support foreign keys officially, -but it's okay to use them anyway. - SQLite 2 официально не поддерживает внешние ключи, -но тем не менее их можно иÑпользовать. - - - - Columns - Столбцы - - - - Local column - Локальный Ñтолбец - - - - Foreign column - Внешний Ñтолбец - - - - Reactions - ДейÑÑ‚Ð²Ð¸Ñ - - - - Deferred foreign key - Отложенный внешний ключ - - - - Named constraint - Именованное ограничение - - - - Constraint name - Ð˜Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ - - - - Pick the foreign column. - Выберите внешний Ñтолбец. - - - - Pick the foreign table. - Выберите внешнюю таблицу. - - - - Select at least one foreign column. - Выберите Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один внешний Ñтолбец. - - - - Enter a name of the constraint. - Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. - - - - Foreign column - table constraints - Внешний Ñтолбец - - - - TablePrimaryKeyAndUniquePanel - - - Columns - Столбцы - - - - Column - Столбец - - - - Collation - Сравнение - - - - Sort - Сортировка - - - - Valid only for a single column with INTEGER data type - Применимо только к одному Ñтолбцу типа INTEGER - - - - Autoincrement - Ðвтоинкремент - - - - Named constraint - Именованное ограничение - - - - Constraint name - Ð˜Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ - - - - On conflict - При конфликте - - - - Collate - table constraints - Сравнение - - - - Sort order - table constraints - ПорÑдок Ñортировки - - - - Select at least one column. - Выберите Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один Ñтолбец. - - - - Enter a name of the constraint. - Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. - - - - TableStructureModel - - - Name - table structure columns - Ð˜Ð¼Ñ - - - - Data type - table structure columns - Тип данных - - - - Primary -Key - table structure columns - Первичный -ключ - - - - Foreign -Key - table structure columns - Внешний -ключ - - - - Unique - table structure columns - УникальноÑть - - - - Check - table structure columns - Проверка - - - - Not -NULL - table structure columns - Ðе -NULL - - - - Collate - table structure columns - Сравнение - - - - Default value - table structure columns - Значение по умолчанию - - - - TableWindow - - - Structure - Структура - - - - Table name: - Ð˜Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹: - - - - - Data - Данные - - - - Constraints - ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ - - - - Indexes - ИндекÑÑ‹ - - - - Triggers - Триггеры - - - - DDL - DDL - - - - Export table - table window - ЭкÑпортировать таблицу - - - - Import data to table - table window - Импортировать данные в таблицу - - - - Populate table - table window - Заполнить таблицу - - - - Refresh structure - table window - Обновить Ñтруктуру - - - - Commit structure changes - table window - Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры - - - - Rollback structure changes - table window - Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры - - - - Add column - table window - Добавить Ñтолбец - - - - Edit column - table window - Редактировать Ñтолбец - - - - - Delete column - table window - Удалить Ñтолбец - - - - Move column up - table window - ПеремеÑтить Ñтолбец вверх - - - - Move column down - table window - ПеремеÑтить Ñтолбец вниз - - - - Create similar table - table window - Создать подобную таблицу - - - - Reset autoincrement value - table window - СброÑить Ñчётчик автоинкремента - - - - Add table constraint - table window - Добавить ограничение на таблицу - - - - Edit table constraint - table window - Редактировать ограничение на таблицу - - - - Delete table constraint - table window - Удалить ограничение на таблицу - - - - Move table constraint up - table window - ПеремеÑтить ограничение на таблицу вверх - - - - Move table constraint down - table window - ПеремеÑтить ограничение на таблицу вниз - - - - Add table primary key - table window - Добавить первичный ключ таблицы - - - - Add table foreign key - table window - Добавить внешний ключ таблицы - - - - Add table unique constraint - table window - Добавить табличное ограничение на уникальноÑть - - - - Add table check constraint - table window - Добавить проверочное ограничение на таблицу - - - - Refresh index list - table window - Обновить ÑпиÑок индекÑов - - - - Create index - table window - Создать Ð¸Ð½Ð´ÐµÐºÑ - - - - Edit index - table window - Редактировать Ð¸Ð½Ð´ÐµÐºÑ - - - - Delete index - table window - Удалить Ð¸Ð½Ð´ÐµÐºÑ - - - - Refresh trigger list - table window - Обновить ÑпиÑок триггеров - - - - Create trigger - table window - Создать триггер - - - - Edit trigger - table window - Редактировать триггер - - - - Delete trigger - table window - Удалить триггер - - - - Are you sure you want to delete column '%1'? - table window - Ð’Ñ‹ дейÑтвительно хотите удалить Ñтолбец '%1'? - - - - Following problems will take place while modifying the table. -Would you like to proceed? - table window - При изменении таблицы возникнут нижеуказанные проблемы. -Ð’Ñ‹ хотите продолжить? - - - - Table modification - table window - Изменение таблицы - - - - Could not load data for table %1. Error details: %2 - Ðевозможно загрузить данные таблицы %1. ПодробноÑти ошибки: %2 - - - - Could not process the %1 table correctly. Unable to open a table window. - Ðе удалоÑÑŒ корректно обработать таблицу %1. Ðевозможно открыть окно таблицы. - - - - Could not restore window %1, because no database or table was stored in session for this window. - Ðевозможно воÑÑтановить окно %1, так как в ÑеÑÑии Ð´Ð»Ñ Ñтого окна не была Ñохранена база данных или таблица. - - - - Could not restore window '%1', because no database or table was stored in session for this window. - Ðевозможно воÑÑтановить окно '%1', так как в ÑеÑÑии Ð´Ð»Ñ Ñтого окна не была Ñохранена база данных или таблица. - - - - Could not restore window '%1', because database %2 could not be resolved. - Ðевозможно воÑÑтановить окно '%1', так как невозможно определить базу данных %2 - - - - Could not restore window '%1'', because the table %2 doesn't exist in the database %3. - Ðевозможно воÑÑтановить окно '%1', так как таблица %2 не ÑущеÑтвует в базе данных %3. - - - - Committed changes for table '%1' successfully. - Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² таблицу '%1' внеÑены уÑпешно. - - - - Committed changes for table '%1' (named before '%2') successfully. - Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² таблицу '%1' (предыдущее название '%2') внеÑены уÑпешно. - - - - Autoincrement value for table '%1' has been reset successfully. - Ð¡Ð±Ñ€Ð¾Ñ Ñчётчика автоинкремента у таблицы '%1' уÑпешно выполнен. - - - - Uncommitted changes - Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - ИмеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. Ðевозможно проÑматривать или редактировать данные, пока Ñтруктура таблицы не подтверждена. -Подтвердить Ñтруктуру таблицы или вернутьÑÑ Ð½Ð° вкладку Ñтруктуры? - - - - Table window "%1" has uncommitted structure modifications and data. - Ð’ окне таблицы "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры и данных. - - - - Table window "%1" has uncommitted data. - Ð’ окне таблицы "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…. - - - - Table window "%1" has uncommitted structure modifications. - Ð’ окне таблицы "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. - - - Could not restore window, because database %1 could not be resolved. - Ðевозможно воÑÑтановить окно, так как невозможно определить базу данных %1 - - - Could not restore window, because the table %1 doesn't exist in the database %2. - Ðевозможно воÑÑтановить окно, так как таблица %1 не ÑущеÑтвует в базе данных %2. - - - - - New table %1 - ÐÐ¾Ð²Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° %1 - - - Commited changes for table '%1' successfly. - Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² таблицу '%1' внеÑены уÑпешно. - - - Commited changes for table '%1' (named before '%2') successfly. - Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² таблицу '%1' (предыдущее название '%2') внеÑены уÑпешно. - - - - Could not commit table structure. Error message: %1 - table window - Ðевозможно подтвердить Ñтруктуру таблицы. Сообщение об ошибке: %1 - - - - Reset autoincrement - Ð¡Ð±Ñ€Ð¾Ñ Ñчётчика автоинкремента - - - - Are you sure you want to reset autoincrement value for table '%1'? - Ð’Ñ‹ дейÑтвительно хотите ÑброÑить Ñчётчик автоинкремента у таблицы '%1'? - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - При попытке ÑброÑа Ñчётчика автоинкремента у таблицы '%1' произошла ошибка: %2 - - - Autoincrement value for table '%1' has been reset successfly. - Ð¡Ð±Ñ€Ð¾Ñ Ñчётчика автоинкремента у таблицы '%1' уÑпешно выполнен. - - - - Empty name - ПуÑтое Ð¸Ð¼Ñ - - - - A blank name for the table is allowed in SQLite, but it is not recommended. -Are you sure you want to create a table with blank name? - SQLite позволÑет таблице иметь пуÑтое имÑ, Ñ…Ð¾Ñ‚Ñ Ñто не рекомендуетÑÑ. -Ð’Ñ‹ дейÑтвительно хотите Ñоздать таблицу Ñ Ð¿ÑƒÑтым именем? - - - - Cannot create a table without at least one column. - Ðевозможно Ñоздать таблицу без Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ одного Ñтолбца. - - - - Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. - Ðевозможно Ñоздать таблицу %1, еÑли не определён первичный ключ. Отключите %2, либо определите первичный ключ. - - - - Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. - Ðевозможно иÑпользовать автоинкремент первичного ключа при иÑпользовании оператора %1. Отключите либо %2, либо автоинкремент первичного ключа. - - - - Are you sure you want to delete table constraint '%1'? - table window - Ð’Ñ‹ дейÑтвительно хотите удалить ограничение на таблицу '%1'? - - - - Delete constraint - table window - Удалить ограничение - - - - Cannot export, because no export plugin is loaded. - Ðевозможно произвеÑти ÑкÑпорт, Ñ‚.к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ ÑкÑпорта. - - - - Cannot import, because no import plugin is loaded. - Ðевозможно произвеÑти импорт, Ñ‚.к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð°. - - - Uncommited changes - Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ - - - There are uncommited structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - ИмеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. Ðевозможно проÑматривать или редактировать данные, пока Ñтруктура таблицы не подтверждена. -Подтвердить Ñтруктуру таблицы или вернутьÑÑ Ð½Ð° вкладку Ñтруктуры? - - - - Go back to structure tab - ВернутьÑÑ Ð½Ð° вкладку Ñтруктуры - - - - Commit modifications and browse data. - Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸ перейти к данным. - - - - Name - table window indexes - Ð˜Ð¼Ñ - - - - Unique - table window indexes - УникальноÑть - - - - Columns - table window indexes - Столбцы - - - - Partial index condition - table window indexes - УÑловие Ð´Ð»Ñ Ñ‡Ð°Ñтичного индекÑа - - - - Name - table window triggers - Ð˜Ð¼Ñ - - - - Event - table window triggers - Событие - - - - Condition - table window triggers - УÑловие - - - - Details - table window triggers - ПодробноÑти - - - Table window "%1" has uncommited structure modifications and data. - Ð’ окне таблицы "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры и данных. - - - Table window "%1" has uncommited data. - Ð’ окне таблицы "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…. - - - Table window "%1" has uncommited structure modifications. - Ð’ окне таблицы "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. - - - - TriggerColumnsDialog - - - Trigger columns - Столбцы триггера - - - - Triggering columns: - Столбцы, вызывающие триггер: - - - - Select all - Выделить вÑÑ‘ - - - - Deselect all - СнÑть выделение - - - - TriggerDialog - - - - Trigger - Триггер - - - - On table: - Ð”Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹: - - - - Action: - ДейÑтвие: - - - - - <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> - <p>SQL-уÑловие Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸ перед запуÑком кода триггера. ЕÑли уÑловие не выполнено, Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ Ñтроки триггер вызван не будет.</p> - - - - Pre-condition: - Предварительное уÑловие: - - - - The scope is still not fully supported by the SQLite database. - База данных SQLite пока не полноÑтью поддерживает облаÑть дейÑтвиÑ. - - - - Trigger name: - Ð˜Ð¼Ñ Ñ‚Ñ€Ð¸Ð³Ð³ÐµÑ€Ð°: - - - - When: - Когда: - - - - List of columns for UPDATE OF action. - СпиÑок Ñтолбцов Ð´Ð»Ñ Ð´ÐµÐ¹ÑÑ‚Ð²Ð¸Ñ UPDATE OF. - - - - Scope: - ОблаÑть дейÑтвиÑ: - - - - Code: - Код: - - - - Trigger statements to be executed. - ВыполнÑемые конÑтрукции триггера. - - - - DDL - DDL - - - - On view: - Ð”Ð»Ñ Ð¿Ñ€ÐµÐ´ÑтавлениÑ: - - - - Could not process trigger %1 correctly. Unable to open a trigger dialog. - Ðе удалоÑÑŒ корректно обработать триггер %1. Ðевозможно открыть окно триггера. - - - - Enter a valid condition. - Введите корректное уÑловие. - - - - Enter a valid trigger code. - Введите корректный код триггера. - - - - Error - trigger dialog - Ошибка - - - - An error occurred while executing SQL statements: -%1 - При выполнении конÑтрукций SQL произошла ошибка: -%1 - - - - VersionConvertSummaryDialog - - - Database version convert - ÐšÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð°Ñ†Ð¸Ñ Ð²ÐµÑ€Ñии базы данных - - - - Following changes to the SQL statements will be made: - Ð’ конÑтрукции SQL будут внеÑены Ñледующие изменениÑ: - - - - Before - До - - - - After - ПоÑле - - - - ViewWindow - - - Query - Ð—Ð°Ð¿Ñ€Ð¾Ñ - - - - View name: - Ð˜Ð¼Ñ Ð¿Ñ€ÐµÐ´ÑтавлениÑ: - - - - Output column names - Отображаемые имена Ñтолбцов - - - - - Data - Данные - - - - Triggers - Триггеры - - - - DDL - DDL - - - Could not restore window, because database %1 could not be resolved. - Ðевозможно воÑÑтановить окно, так как невозможно определить базу данных %1. - - - Could not restore window, because database %1 could not be open. - Ðевозможно воÑÑтановить окно, так как невозможно открыть базу данных %1. - - - Could not restore window, because the view %1 doesn't exist in the database %2. - Ðевозможно воÑÑтановить окно, так как предÑтавление %1 не ÑущеÑтвует в базе данных %2. - - - - - Could not restore window '%1', because no database or view was stored in session for this window. - Ðевозможно воÑÑтановить окно '%1', так как в ÑеÑÑии Ð´Ð»Ñ Ñтого окна не была Ñохранена база данных или предÑтавление. - - - - Could not restore window '%1', because database %2 could not be resolved. - Ðевозможно воÑÑтановить окно '%1', так как невозможно определить базу данных %2. - - - - Could not restore window '%1', because database %2 could not be open. - Ðевозможно воÑÑтановить окно '%1', так как невозможно открыть базу данных %2. - - - - Could not restore window '%1', because the view %2 doesn't exist in the database %3. - Ðевозможно воÑÑтановить окно '%1', так как предÑтавление %2 не ÑущеÑтвует в базе данных %3. - - - - - New view %1 - Ðовое предÑтавление %1 - - - - Refresh the view - view window - Обновить предÑтавление - - - - Commit the view changes - view window - Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ - - - - Rollback the view changes - view window - Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ - - - - Explicit column names - Явные имена Ñтолбцов - - - - Generate output column names automatically basing on result columns of the view. - Сгенерировать отображаемые имена Ñтолбцов на оÑнове результирующих Ñтолбцов предÑтавлениÑ. - - - - Add column - view window - Добавить Ñтолбец - - - - Edit column - view window - Редактировать Ñтолбец - - - - Delete column - view window - Удалить Ñтолбец - - - - Move column up - view window - ПеремеÑтить Ñтолбец вверх - - - - Move column down - view window - ПеремеÑтить Ñтолбец вниз - - - - Refresh trigger list - view window - Обновить ÑпиÑок триггеров - - - - Create new trigger - view window - Создать новый триггер - - - - Edit selected trigger - view window - Редактировать выбранный триггер - - - - Delete selected trigger - view window - Удалить выбранный триггер - - - - View window "%1" has uncommitted structure modifications and data. - Ð’ окне предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры и данных. - - - - View window "%1" has uncommitted data. - Ð’ окне предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…. - - - - View window "%1" has uncommitted structure modifications. - Ð’ окне предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. - - - - Uncommitted changes - Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - ИмеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. Ðевозможно проÑматривать или редактировать данные, пока Ñтруктура предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð½Ðµ подтверждена. -Подтвердить Ñтруктуру предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ вернутьÑÑ Ð½Ð° вкладку Ñтруктуры? - - - - Committed changes for view '%1' successfully. - Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² предÑтавление '%1' внеÑены уÑпешно. - - - - Committed changes for view '%1' (named before '%2') successfully. - Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² таблицу '%1' (предыдущее название '%2') внеÑены уÑпешно. - - - View window "%1" has uncommited structure modifications and data. - Ð’ окне предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры и данных. - - - View window "%1" has uncommited data. - Ð’ окне предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…. - - - View window "%1" has uncommited structure modifications. - Ð’ окне предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. - - - - Could not load data for view %1. Error details: %2 - Ðевозможно загрузить данные предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ %1. ПодробноÑти ошибки: %2 - - - Uncommited changes - Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ - - - There are uncommited structure modifications. You cannot browse or edit data until you have the view structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - ИмеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. Ðевозможно проÑматривать или редактировать данные, пока Ñтруктура предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð½Ðµ подтверждена. -Подтвердить Ñтруктуру предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ вернутьÑÑ Ð½Ð° вкладку Ñтруктуры? - - - - Go back to structure tab - ВернутьÑÑ Ð½Ð° вкладку Ñтруктуры - - - - Commit modifications and browse data. - Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸ перейти к данным. - - - Commited changes for view '%1' successfly. - Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² предÑтавление '%1' внеÑены уÑпешно. - - - Commited changes for view '%1' (named before '%2') successfly. - Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² предÑтавление '%1' (предыдущее название '%2') внеÑены уÑпешно. - - - - Could not commit view changes. Error message: %1 - view window - Ðевозможно подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ñ€ÐµÐ´ÑтавлениÑ. Сообщение об ошибке: %1 - - - - Override columns - ПерезапиÑÑŒ Ñтолбцов - - - - Currently defined columns will be overriden. Do you want to continue? - Заданные Ñтолбцы будут перезапиÑаны. Ð’Ñ‹ хотите продолжить? - - - - Could not determinate columns returned from the view. The query is problably incomplete or contains errors. - Ðевозможно определить Ñтолбцы, возвращаемые предÑтавлением. ВероÑтно Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½ÐµÐ¿Ð¾Ð»Ð¾Ð½ или Ñодержит ошибки. - - - - Name - view window triggers - Ð˜Ð¼Ñ - - - - Instead of - view window triggers - ВмеÑто - - - - Condition - view window triggers - УÑловие - - - - Details - table window triggers - ПодробноÑти - - - - Could not process the %1 view correctly. Unable to open a view window. - Ðе удалоÑÑŒ корректно обработать предÑтавление %1. Ðевозможно открыть окно предÑтавлениÑ. - - - - Empty name - ПуÑтое Ð¸Ð¼Ñ - - - - A blank name for the view is allowed in SQLite, but it is not recommended. -Are you sure you want to create a view with blank name? - SQLite позволÑет предÑтавлению иметь пуÑтое имÑ, Ñ…Ð¾Ñ‚Ñ Ñто не рекомендуетÑÑ. -Ð’Ñ‹ дейÑтвительно хотите Ñоздать предÑтавление Ñ Ð¿ÑƒÑтым именем? - - - - The SELECT statement could not be parsed. Please correct the query and retry. -Details: %1 - Ðевозможно проанализировать Ñтруктуру конÑтрукции SELECT. ПожалуйÑта, иÑправьте Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸ повторите попытку. -ПодробноÑти: %1 - - - - The view could not be modified due to internal SQLiteStudio error. Please report this! - Ðевозможно изменить предÑтавление из-за внутренней ошибки SQLiteStudio. ПожалуйÑта, Ñообщите о ней! - - - - The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. - Ðевозможно корректно проанализировать Ñтруктуру предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ. Это ошибка SQLiteStudio. ПожалуйÑта, Ñообщите о ней. - - - - Following problems will take place while modifying the view. -Would you like to proceed? - view window - При изменении предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð²Ð¾Ð·Ð½Ð¸ÐºÐ½ÑƒÑ‚ нижеуказанные проблемы. -Ð’Ñ‹ хотите продолжить? - - - - View modification - view window - Изменение предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ - - - - WidgetCover - - - Interrupt - Прервать - - - diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru_RU.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru_RU.ts new file mode 100644 index 0000000..1917ba5 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru_RU.ts @@ -0,0 +1,7108 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + О программе SQLiteStudio и лицензиÑÑ… + + + + About + О программе + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio вер.%1</span></p><p align="center">БеÑплатный кроÑÑплатформенный менеджер баз данных SQLite Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¼ иÑходным кодом.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Ðвтор и активный разработчик:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Лицензии + + + + Environment + СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ñреда + + + + Icon directories + Каталоги иконок + + + + Form directories + Каталоги форм + + + + SQLite extension directories + Каталоги раÑширений SQLite + + + + Plugin directories + Каталоги модулей + + + + Configuration directory + Каталог конфигурации + + + + Application directory + Каталог программы + + + + Qt version: + ВерÑÐ¸Ñ Qt: + + + + SQLite 3 version: + ВерÑÐ¸Ñ SQLite 3: + + + + Portable distribution. + ÐŸÐ¾Ñ€Ñ‚Ð°Ð±ÐµÐ»ÑŒÐ½Ð°Ñ Ð²ÐµÑ€ÑиÑ. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + ВерÑиÑ, управлÑÐµÐ¼Ð°Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹ ÑиÑтемой. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Содержание:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Параметры запроÑа + + + + Please provide values for query parameters + ПожалуйÑта укажите Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð² запроÑа + + + + CodeSnippetEditor + + + Filter snippets + Фильтр Ñниппетов + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Опциональное Ñочетание клавиш, которое будет работать только в контекÑте активного окна автодополнениÑ. ПозволÑет пользователю иÑпользовать комбинации клавиш, которые иначе конфликтовали бы Ñ Ð¾Ð±Ñ‰Ð¸Ð¼Ð¸ горÑчими клавишами. Ðаличие окна Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð² качеÑтве требуемого контекÑта делает выбор ÑÐ¾Ñ‡ÐµÑ‚Ð°Ð½Ð¸Ñ ÐºÐ»Ð°Ð²Ð¸Ñˆ более универÑальным.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>Ðазвание Ñниппета будет отображено в окне автодополнениÑ. Ð”Ð»Ñ Ð´Ð¾Ñтупа к ÑпиÑку Ñниппетов пользователь должен нажать Ñочетание клавиш Ð´Ð»Ñ Ð²Ñ‹Ð·Ð¾Ð²Ð° Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð´Ð²Ð°Ð¶Ð´Ñ‹.</p></body></html> + + + + Snippet name + Ðазвание Ñниппета + + + + Code assistant shortcut + Сочетание клавиш в окне Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ + + + + Snippet code + Код Ñниппета + + + + Code Snippets editor window has uncommitted modifications. + Ð’ окне редактора Ñниппетов имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ изменениÑ. + + + + Code Snippets editor + Редактор Ñниппетов + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + ПеремеÑтить Ñниппет вверх + + + + Move the snippet down + ПеремеÑтить Ñниппет вниз + + + + Code snippets manual + РуководÑтво по иÑпользованию Ñниппетов + + + + Enter a non-empty, unique name of the snippet. + Введите непуÑтое уникальное название Ñниппета. + + + + Enter a non-empty snippet content. + Введите непуÑтое Ñодержимое Ñниппета. + + + + This hotkey is not unique in context of a code assistant. + Это Ñочетание клавиш уже иÑпользуетÑÑ Ð² контекÑте автодополнениÑ. + + + + CollationsEditor + + + Filter collations + Отфильтровать ÑÑ€Ð°Ð²Ð½ÐµÐ½Ð¸Ñ + + + + Databases + Базы данных + + + + Register in all databases + ЗарегиÑтрировать во вÑех базах данных + + + + Register in following databases: + ЗарегиÑтрировать в Ñледующих базах данных: + + + + Implementation code: + Код реализации: + + + + Collation name: + Ð˜Ð¼Ñ ÑравнениÑ: + + + + Implementation language: + Язык реализации: + + + + Collations editor + Редактор Ñравнений + + + + Commit all collation changes + Подтвердить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñравнений + + + + Rollback all collation changes + Откатить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñравнений + + + + Create new collation + Создать новое Ñравнение + + + + Delete selected collation + Удалить выбранное Ñравнение + + + + Editing collations manual + РуководÑтво по редактированию Ñравнений + + + + Enter a non-empty, unique name of the collation. + Введите непуÑтое уникальное Ð¸Ð¼Ñ ÑравнениÑ. + + + + Pick the implementation language. + Выберите Ñзык реализации. + + + + Enter a non-empty implementation code. + Введите непуÑтой код реализации. + + + + Collations editor window has uncommitted modifications. + Ð’ редакторе Ñравнений имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ изменениÑ. + + + + ColorButton + + + Pick a color + Выберите цвет + + + + ColumnCollatePanel + + + Collation name: + Ð˜Ð¼Ñ ÑравнениÑ: + + + + Named constraint: + Именованное ограничение: + + + + Enter a name of the constraint. + Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. + + + + Enter a collation name. + Введите Ð¸Ð¼Ñ ÑравнениÑ. + + + + ColumnDefaultPanel + + + Default value: + Значение по умолчанию: + + + + Named constraint: + Именованное ограничение: + + + + Enter a default value expression. + Введите выражение Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Ðекорректное выражение Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию: %1. ЕÑли необходимо иÑпользовать проÑтую Ñтроку как значение, не забудьте помеÑтить её в кавычки. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Ðекорректное выражение Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию. ЕÑли необходимо иÑпользовать проÑтую Ñтроку как значение, не забудьте помеÑтить её в кавычки. + + + + Enter a name of the constraint. + Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. + + + + ColumnDialog + + + Column + Столбец + + + + Name and type + Ð˜Ð¼Ñ Ð¸ тип + + + + Scale + Размер + + + + Precision + ТочноÑть + + + + Data type: + Тип данных: + + + + Column name: + Ð˜Ð¼Ñ Ñтолбца: + + + + Size: + Размер: + + + + Constraints + ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ + + + + Generated value + Сгенерированное значение + + + + Unique + УникальноÑть + + + + + + + + + + + Configure + ÐаÑтроить + + + + Foreign Key + Внешний ключ + + + + Collate + Сравнение + + + + Not NULL + Ðе NULL + + + + Check condition + Проверка уÑÐ»Ð¾Ð²Ð¸Ñ + + + + Primary Key + Первичный ключ + + + + Default + По умолчанию + + + + Advanced mode + РаÑширенный режим + + + + Add constraint + column dialog + Добавить ограничение + + + + Edit constraint + column dialog + Редактировать ограничение + + + + + Delete constraint + column dialog + Удалить ограничение + + + + Move constraint up + column dialog + ПеремеÑтить ограничение вверх + + + + Move constraint down + column dialog + ПеремеÑтить ограничение вниз + + + + Add a primary key + column dialog + Добавить первичный ключ + + + + Add a foreign key + column dialog + Добавить внешний ключ + + + + Add an unique constraint + column dialog + Добавить ограничение на уникальноÑть + + + + Add a check constraint + column dialog + Добавить проверочное ограничение + + + + Add a not null constraint + column dialog + Добавить ограничение на не null + + + + Add a collate constraint + column dialog + Добавить ограничение на Ñравнение + + + + Add a generated value constraint + column dialog + Добавить ограничение на Ñгенерированное значение + + + + Add a default constraint + column dialog + Добавить ограничение на значение по умолчанию + + + + Are you sure you want to delete constraint '%1'? + column dialog + Ð’Ñ‹ дейÑтвительно хотите удалить ограничение '%1'? + + + + Correct the constraint's configuration. + ИÑправьте конфигурацию ограничениÑ. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Указание размера данных недопуÑтимо Ð´Ð»Ñ Ñтолбцов Ñ Ñ‚Ð¸Ð¿Ð¾Ð¼ INTEGER PRIMARY KEY. + + + + Precision cannot be defined without the scale. + ТочноÑть не может быть задана без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ñ€Ð°Ð·Ð¼ÐµÑ€Ð° данных. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Ðевозможно иÑпользовать тип данных, отличный от INTEGER, еÑли в первичном ключе уÑтановлен автоинкремент. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + Ð’ качеÑтве типа данных был принудительно выбран INTEGER, так как в первичном ключе уÑтановлен автоинкремент. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Указание точноÑти недопуÑтимо Ð´Ð»Ñ Ñтолбцов Ñ Ñ‚Ð¸Ð¿Ð¾Ð¼ INTEGER PRIMARY KEY. + + + + Could not match valid STRICT table datatype from declared type: %1. + Ðе удалоÑÑŒ найти допуÑтимый Ð´Ð»Ñ STRICT-таблиц тип данных, ÑоответÑтвующий указанному типу: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Тип + + + + Name + column dialog constraints + Ð˜Ð¼Ñ + + + + Details + column dialog constraints + ПодробноÑти + + + + ColumnForeignKeyPanel + + + Foreign table: + ВнешнÑÑ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°: + + + + Foreign column: + Внешний Ñтолбец: + + + + Reactions + ДейÑÑ‚Ð²Ð¸Ñ + + + + Deferred foreign key + Отложенный внешний ключ + + + + Named constraint + Именованное ограничение + + + + Constraint name + Ð˜Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ + + + + Pick the foreign table. + Выберите внешнюю таблицу. + + + + Pick the foreign column. + Выберите внешний Ñтолбец. + + + + Enter a name of the constraint. + Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. + + + + ColumnGeneratedPanel + + + Generating code: + Генерирующий код: + + + + Explicit type: + Заданный тип: + + + + Use "GENERATED ALWAYS" keywords + ИÑпользовать ключевые Ñлова "GENERATED ALWAYS" + + + + Named constraint: + Именованное ограничение: + + + + Enter the column value generating expression. + Введите выражение Ð´Ð»Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ значений Ñтолбца. + + + + Invalid value generating expression: %1. + Ðекорректное выражение Ð´Ð»Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ значений: %1. + + + + Invalid value generating expression. + Ðекорректное выражение Ð´Ð»Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ значений. + + + + Enter a name of the constraint. + Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Ðвтоинкремент + + + + Sort order: + ПорÑдок Ñортировки: + + + + Named constraint: + Именованное ограничение: + + + + On conflict: + При конфликте: + + + + Enter a name of the constraint. + Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. + + + + Descending order is not allowed with AUTOINCREMENT. + Сортировка по убыванию недопуÑтима при иÑпользовании AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Именованное ограничение: + + + + On conflict: + При конфликте: + + + + Enter a name of the constraint. + Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Столбец: %1 + + + + Table: %1 + completer statusbar + Таблица: %1 + + + + Index: %1 + completer statusbar + ИндекÑ: %1 + + + + Trigger: %1 + completer statusbar + Триггер: %1 + + + + View: %1 + completer statusbar + ПредÑтавление: %1 + + + + Database: %1 + completer statusbar + База данных: %1 + + + + Keyword: %1 + completer statusbar + Ключевое Ñлово: %1 + + + + Function: %1 + completer statusbar + ФункциÑ: %1 + + + + Operator: %1 + completer statusbar + Оператор: %1 + + + + String + completer statusbar + Строка + + + + Number + completer statusbar + ЧиÑло + + + + Binary data + completer statusbar + Двоичные данные + + + + Collation: %1 + completer statusbar + Сравнение: %1 + + + + Pragma function: %1 + completer statusbar + Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ pragma: %1 + + + + Insert a code snippet + Ð’Ñтавить Ñниппет + + + + ConfigDialog + + + + Configuration + ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ + + + + Search + ПоиÑк + + + + General + Общие + + + + Keyboard shortcuts + ГорÑчие клавиши + + + + Look & feel + Внешний вид + + + + Style + Стиль + + + + Fonts + Шрифты + + + + Code colors + Цвета кода + + + + + Database list + СпиÑок баз данных + + + + Code assistant + Ðвтодополнение + + + + Data browsing + ПроÑмотр данных + + + + Data editors + Редакторы данных + + + + Plugins + Модули + + + + Code formatters + СредÑтва Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð´Ð° + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, Ñтолбцы будут отÑортированы в том порÑдке, в котором они были указаны в конÑтрукции CREATE TABLE. + + + + Sort table columns alphabetically + Сортировать Ñтолбцы таблицы в алфавитном порÑдке + + + + Expand tables node when connected to a database + Развернуть ÑпиÑок таблиц поÑле Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº базе данных + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Дополнительные метки находÑÑ‚ÑÑ Ñправа от имён в ÑпиÑке баз данных (они отображаютÑÑ Ñиним цветом, еÑли не выбран иной). При активации Ñтой опции будут отображены метки у баз данных, некорректных баз данных и у групповых узлов (группа Ñтолбцов, группа индекÑов, группа триггеров). Ð”Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ñ‹Ñ… меток воÑпользуйтеÑÑŒ опциÑми ниже.<p> + + + + Display additional labels on the list + Отображать дополнительные метки в ÑпиÑке + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + Ð”Ð»Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ñ… таблиц метки будут показывать количеÑтво Ñтолбцов, индекÑов и триггеров у каждой таблицы. + + + + Display labels for regular tables + Отображать метки у обычных таблиц + + + + Virtual tables will be marked with a 'virtual' label. + Виртуальные таблицы будут помечены как 'виртуальные'. + + + + Display labels for virtual tables + Отображать метки у виртуальных таблиц + + + + Expand views node when connected to a database + Развернуть ÑпиÑок предÑтавлений поÑле Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº базе данных + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, объекты будут отÑортированы в том порÑдке, в котором они указаны в таблице sqlite_master (Ñ‚. е. в порÑдке ÑозданиÑ) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Сортировать объекты (таблицы, индекÑÑ‹, триггеры и предÑтавлениÑ) в алфавитном порÑдке + + + + Display system tables and indexes on the list + Отображать в ÑпиÑке ÑиÑтемные таблицы и индекÑÑ‹ + + + + Database dialog window + Диалоговое окно Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>По умолчанию при добавлении базы данных она отмечаетÑÑ ÐºÐ°Ðº "поÑтоÑннаÑ" (Ñ‚.е. ÑохранÑетÑÑ Ð² конфигурацию). При уÑтановке данной опции вÑе добавлÑемые базы данных по умолчанию ÐЕ будут отмечены как "поÑтоÑнные".</p> + + + + Do not mark database to be "permanent" by default + Ðе отмечать базу данных как "поÑтоÑнную" по умолчанию + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>При выборе данной опции вÑе файлы, перетÑнутые в ÑпиÑок баз данных из файлового менеджера, будут автоматичеÑки добавлены в ÑпиÑок без Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¸Ð°Ð»Ð¾Ð³Ð° Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных. ЕÑли по каким-либо причинам автоматичеÑкое добавление не получитÑÑ, пользователю будет показан Ñтандартный диалог Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Ðе показывать диалог при перетÑгивании файла базы данных в ÑпиÑок + + + + Data browsing and editing + ПроÑмотр и редактирование данных + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>МакÑимальное количеÑтво конфигураций окна Ð—Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹, ÑохранÑемых в конфигурации программы. 100 конфигураций должно хватить.</p> + + + + Number of memorized table populating configurations + КоличеÑтво запоминаемых конфигураций Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ + + + + Data column width + Ширина Ñтолбца данных + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>Когда пользователь вводит новое значение в Ñтолбец, и оно не умещаетÑÑ Ð² Ñчейке при текущей ширине Ñтолбца, приложение раÑширит Ñтолбец, чтобы умеÑтить новое значение, но не Ð¿Ñ€ÐµÐ²Ñ‹ÑˆÐ°Ñ Ð»Ð¸Ð¼Ð¸Ñ‚, указанный в опции выше.</p></body></html> + + + + Enlarge column when entering value longer than current width + РаÑширить Ñтолбец при вводе Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ð¸Ð½Ð½ÐµÐµ текущей ширины + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>При загрузке данных в табличный вид ширина Ñтолбцов автоматичеÑки подÑтраиваетÑÑ. Этот параметр ограничивает начальную ширину Ð´Ð»Ñ Ð¿Ð¾Ð´Ñтройки, при Ñтом пользователь может вручную изменить ширину Ñтолбца Ñверх данного лимита.</p> + + + + Number of data rows per page: + КоличеÑтво Ñтрок данных на Ñтранице: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>ЕÑли Ð´Ð°Ð½Ð½Ð°Ñ Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð° и пользователь наводит указатель мыши на Ñчейку в любом режиме проÑмотра данных (результаты запроÑа, данные таблицы, данные предÑтавлениÑ), то будет отображена вÑÐ¿Ð»Ñ‹Ð²Ð°ÑŽÑ‰Ð°Ñ Ð¿Ð¾Ð´Ñказка Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸ÐµÐ¹ о Ñчейке, в том чиÑле Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ типе данных Ñтолбца, ограничениÑÑ…, значение ROWID и прочее.</p> + + + + Show column and row details tooltip in data view + Показывать вÑплывающую подÑказку Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸ÐµÐ¹ о Ñтолбце и Ñтроке при проÑмотре данных + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>ЕÑли редактируетÑÑ Ñчейка, ÑÐ¾Ð´ÐµÑ€Ð¶Ð°Ñ‰Ð°Ñ NULL, и вводитÑÑ Ð¿ÑƒÑÑ‚Ð°Ñ Ñтрока в качеÑтве значениÑ, то Ñта Ð¾Ð¿Ñ†Ð¸Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»Ñет, оÑтанетÑÑ Ð»Ð¸ в качеÑтве Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñчейки NULL (еÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°), или значение будет заменено на пуÑтую Ñтроку (еÑли Ñта Ð¾Ð¿Ñ†Ð¸Ñ Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°).</p> + + + + Keep NULL value when entering empty value + СохранÑть значение NULL при вводе пуÑтой Ñтроки + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Ðктивируйте Ñту опцию, чтобы вÑегда подÑтавлÑть значение DEFAULT (значение по умолчанию) при запиÑи NULL в Ñтолбец, у которого определено значение DEFAULT, даже еÑли Ñтолбец может Ñодержать NULL.</p><p>Отключите Ñту опцию Ð´Ð»Ñ Ð¿Ð¾Ð´Ñтановки Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ DEFAULT только в Ñлучае запиÑи NULL в Ñтолбец Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸ÐµÐ¼ NOT NULL.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + ИÑпользовать значение DEFAULT (еÑли оно определено) при запиÑи NULL + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>ЕÑли результаты запроÑа Ñодержат деÑÑтки (или Ñотни) Ñтолбцов, они вполне вероÑтно могут иÑчерпать вÑÑŽ Ñвободную памÑть компьютера, одномоментно загрузив неÑколько гигабайт данных. Ð’ таких ÑлучаÑÑ…, Ð´Ð»Ñ Ð·Ð°Ñ‰Ð¸Ñ‚Ñ‹ компьютера SQLiteStudio может попытатьÑÑ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡Ð¸Ñ‚ÑŒ количеÑтво результатов, отображаемых на одной Ñтранице. ЕÑли вы уверены, что не работаете Ñ Ð±Ð¾Ð»ÑŒÑˆÐ¸Ð¼Ð¸ объемами в базе данных, то можете отключить Ñто ограничение и вÑегда видеть заданное в наÑтройках количеÑтво Ñтрок на Ñтранице.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Ограничить чиÑло Ñтрок в Ñлучае деÑÑтков Ñтолбцов + + + + Inserting new row in data grid + Ð’Ñтавка новой Ñтроки в таблице данных + + + + Before currently selected row + Перед текущей выделенной Ñтрокой + + + + After currently selected row + ПоÑле текущей выделенной Ñтроки + + + + At the end of data view + Ð’ конец облаÑти проÑмотра данных + + + + Table windows + Окна таблиц + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, окна таблиц будут открыватьÑÑ Ð½Ð° вкладке данных вмеÑто вкладки Ñо Ñтруктурой.</p> + + + + Open Table Windows with the data tab for start + Открывать окна таблиц на вкладке данных + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, вкладка "Данные" в окнах таблиц будет первой, а не второй по порÑдку.</p> + + + + Place data tab as first tab in a Table Window + Помещать вкладку данных в окнах таблиц первой + + + + View windows + Окна предÑтавлений + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, окна предÑтавлений будут открыватьÑÑ Ð½Ð° вкладке данных вмеÑто вкладки Ñо Ñтруктурой.</p> + + + + Open View Windows with the data tab for start + Открывать окна предÑтавлений на вкладке данных + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, вкладка "Данные" в окнах предÑтавлений будет первой, а не второй по порÑдку.</p> + + + + Place data tab as first tab in a View Window + Помещать вкладку данных в окнах предÑтавлений первой + + + + Data types + Типы данных + + + + Available editors: + ДоÑтупные редакторы: + + + + Editors selected for this data type: + Выбранные редакторы Ð´Ð»Ñ Ñтого типа данных: + + + + Schema editing + Редактирование Ñхемы + + + + Number of DDL changes kept in history. + КоличеÑтво ÑохранÑемых в иÑтории изменений DDL. + + + + DDL history size: + Размер иÑтории DDL: + + + + Don't show DDL preview dialog when committing schema changes + Ðе показывать диалог предпроÑмотра DDL при подтверждении изменений Ñхемы + + + + SQL queries + SQL запроÑÑ‹ + + + + + Number of queries kept in the history. + КоличеÑтво ÑохранÑемых в иÑтории запроÑов. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>ЕÑли в окне редактора SQL более одного запроÑа, то (еÑли Ñта Ð¾Ð¿Ñ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°) будет выполнÑтьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ один Ð·Ð°Ð¿Ñ€Ð¾Ñ - тот, на котором Ñтоит курÑор клавиатуры. Ð’ противном Ñлучае будут выполнены вÑе запроÑÑ‹. Ð’Ñ‹ вÑегда можете указать запроÑÑ‹, подлежащие выполнению, выделив Ñти запроÑÑ‹ перед запуÑком. Ð’Ñ‹ также можете иÑпользовать Ñпециальные ÑÐ¾Ñ‡ÐµÑ‚Ð°Ð½Ð¸Ñ ÐºÐ»Ð°Ð²Ð¸Ñˆ Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов в том или ином режиме (ÑÐµÐ¹Ñ‡Ð°Ñ Ð½Ð°Ñтроено на %1 Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ запроÑа и %2 Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð²Ñех запроÑов).</p></body></html> + + + + History size: + Размер иÑтории: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>МакÑимальное количеÑтво параметров запроÑа (:param, @param, $param, ?), ÑохранÑемых в иÑтории. Когда вы повторно иÑпользуете параметр Ñ Ñ‚ÐµÐ¼ же именем/раÑположением, SQLiteStudio предварительно инициализирует его поÑледним запомненным значением (которое затем можно изменить). 1000 параметров должно хватить.</p> + + + + Execute only the query under the cursor + ВыполнÑть только Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ð´ курÑором + + + + Number of memorized query parameters + КоличеÑтво запоминаемых параметров запроÑа + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>По умолчанию (когда Ñта Ð¾Ð¿Ñ†Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°) вещеÑтвенное чиÑло отображаетÑÑ Ð² виде деÑÑтичных цифр Ñ Ñ€Ð°Ð·Ð´ÐµÐ»Ð¸Ñ‚ÐµÐ»ÐµÐ¼ дробной чаÑти. Ð’ некоторых ÑлучаÑÑ…, когда чиÑло очень маленькое (неÑколько нулей поÑле разделителÑ), такое предÑтавление может привеÑти к неточноÑти его отображениÑ. Ð’ таком Ñлучае вы можете включить Ñту опцию Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÑкÑпоненциальной запиÑи чиÑел (например <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + ИÑпользовать ÑкÑпоненциальную запиÑÑŒ вещеÑтвенных чиÑел в табличном виде + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>При загрузке данных в табличном виде ширина Ñтолбцов автоматичеÑки подÑтраиваетÑÑ. Это значение ограничивает ширину Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¿Ð¾Ð´Ñтройки, но пользователь может вручную раÑширить Ñтолбец Ñверх Ñтого лимита. Этот лимит также иÑпользуетÑÑ Ð¿Ñ€Ð¸ раÑширении Ñтолбца поÑле ввода нового, более длинного Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÐµÐ¼ (Ñм. опцию ниже).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Ограничить автоматичеÑкую ширину Ñтолбца данных (в пикÑелÑÑ…): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>ИÑÑ…Ð¾Ð´Ð½Ð°Ñ ÑˆÐ¸Ñ€Ð¸Ð½Ð° Ñтолбцов будет уÑтановлена так, чтобы как минимум умеÑтилоÑÑŒ полное Ð¸Ð¼Ñ Ñтолбца в заголовке. При Ñтом учитываетÑÑ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ðµ на автоматичеÑкую ширину Ñтолбца, указанное в пикÑелÑÑ… (Ð¾Ð¿Ñ†Ð¸Ñ Ð²Ñ‹ÑˆÐµ).</p></body></html> + + + + Keep at least the width to show complete column name + Как минимум умеÑтить полное Ð¸Ð¼Ñ Ñтолбца в ширину + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°, Ñтроки длиннее ширины окна редактора будут перенеÑены и Ð³Ð¾Ñ€Ð¸Ð·Ð¾Ð½Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¿Ñ€Ð¾ÐºÑ€ÑƒÑ‚ÐºÐ° не потребуетÑÑ.</p></body></html> + + + + Wrap lines in SQL editor + ПереноÑить Ñтроки в редакторе SQL + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>ПодÑвечивать веÑÑŒ запроÑ, на котором Ñтоит курÑор клавиатуры. Это запроÑ, который будет выполнен при нажатии кнопки или ÑÐ¾Ñ‡ÐµÑ‚Ð°Ð½Ð¸Ñ ÐºÐ»Ð°Ð²Ð¸Ñˆ &quot;Выполнить запроÑ&quot; (еÑли не наÑтроено иначе).</p></body></html> + + + + Highlight current query + ПодÑвечивать текущий Ð·Ð°Ð¿Ñ€Ð¾Ñ + + + + Updates + ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ + + + + Automatically check for updates at startup + ÐвтоматичеÑки проверÑть Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ запуÑке + + + + Session + СеÑÑÐ¸Ñ + + + + Restore last session (active MDI windows) after startup + ВоÑÑтановить предыдущую ÑеÑÑию (активные MDI окна) поÑле запуÑка + + + + Allow multiple instances of the application at the same time + Разрешить одновременную работу неÑкольких ÑкземплÑров Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ + + + + Status Field + Окно ÑтатуÑа + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>ЕÑли пользователь вручную закрыл окно ÑтатуÑа, включение данной опции гарантирует, что при поÑвлении новых Ñообщений окно ÑтатуÑа будет автоматичеÑки открыто. ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°, окно ÑтатуÑа может быть заново открыто только пользователем вручную через меню "Вид".</p> + + + + Always open Status panel when new message is printed + Ð’Ñегда открывать окно ÑтатуÑа при поÑвлении нового ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ + + + + Code syntax colors + СинтакÑичеÑÐºÐ°Ñ Ñ€Ð°Ñцветка кода + + + + Keyword foreground + Цвет ключевого Ñлова + + + + Regular foreground + Стандартный цвет + + + + String foreground + Цвет Ñтроки + + + + Comment foreground + Цвет ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ð¸Ñ + + + + Valid objects foreground + Цвет раÑпознанных объектов + + + + Current query background + Фон текущего запроÑа + + + + Bind parameter foreground + Цвет подÑтановочных параметров + + + + Current line background + Фон текущей Ñтроки + + + + Matched parenthesis background + Фон парных Ñкобок + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>Ð’Ñ‹ можете полноÑтью отключить подÑветку текущего запроÑа на Ñтранице Общих наÑтроек.</p></body></html> + + + + Number foreground + Цвет чиÑла + + + + BLOB value foreground + Цвет данных типа BLOB + + + + Matched parenthesis foreground + Цвет парных Ñкобок + + + + Reset to defaults + СброÑить к иÑходным + + + + Filter shortcuts by name or key combination + Фильтр горÑчих клавиш по имени или комбинации клавиш + + + + Action + ДейÑтвие + + + + Key combination + ÐšÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ð²Ð¸Ñˆ + + + + + Language + Язык + + + + Changing language requires application restart to take effect. + Ð”Ð»Ñ Ñмены Ñзыка потребуетÑÑ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑтить приложение. + + + + Compact layout + Компактный режим + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Ð’ компактном режиме вÑе Ð¿Ð¾Ð»Ñ Ð¸ отÑтупы в интерфейÑе минимизированы Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð±Ð¾Ð»ÑŒÑˆÐµÐ³Ð¾ количеÑтва данных. Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ñтанет чуть менее ÑÑтетичным, однако Ñто позволит умеÑтить больше данных на Ñкране.</p> + + + + Use compact layout + Включить компактный режим + + + + Main window dock areas + ОблаÑти Ð¿Ñ€Ð¸ÐºÑ€ÐµÐ¿Ð»ÐµÐ½Ð¸Ñ Ð²Ð¾ÐºÑ€ÑƒÐ³ главного окна + + + + Left and right areas occupy corners + Углы занимают Ð¿Ñ€Ð°Ð²Ð°Ñ Ð¸ Ð»ÐµÐ²Ð°Ñ Ð¾Ð±Ð»Ð°Ñти + + + + Top and bottom areas occupy corners + Углы занимают верхнÑÑ Ð¸ нижнÑÑ Ð¾Ð±Ð»Ð°Ñти + + + + Hide built-in plugins + Скрыть вÑтроенные модули + + + + Current style: + Текущий Ñтиль: + + + + Preview + ПредпроÑмотр + + + + Enabled + Ðктивно + + + + Disabled + Ðеактивно + + + + Active formatter plugin + Ðктивный модуль Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ + + + + SQL editor font + Шрифт редактора SQL + + + + Database list font + Шрифт ÑпиÑка баз данных + + + + Database list additional label font + Шрифт дополнительных меток в ÑпиÑке баз данных + + + + Data view font + Шрифт проÑмотра данных + + + + Status field font + Шрифт окна ÑтатуÑа + + + + Code assistant settings + ÐаÑтройки Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>ЕÑли Ñта Ð¾Ð¿Ñ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°, окно Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ вызыватьÑÑ Ð² Ñлучае, когда пользователь например набирает <span style=" font-weight:700;">tableName.</span> чтобы предложить вÑтавить Ð¸Ð¼Ñ Ñтолбца. ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°, пользователь должен нажать комбинацию клавиш Ð´Ð»Ñ Ð²Ñ‹Ð·Ð¾Ð²Ð° автодополнениÑ.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + ÐвтоматичеÑки показывать окно Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð³Ð´Ð° вводитÑÑ Ñ‚Ð¾Ñ‡ÐºÐ° поÑле имени объекта + + + + Description: + plugin details + ОпиÑание: + + + + Category: + plugin details + КатегориÑ: + + + + Version: + plugin details + ВерÑиÑ: + + + + Author: + plugin details + Ðвтор: + + + + Internal name: + plugin details + Внутреннее имÑ: + + + + Dependencies: + plugin details + ЗавиÑимоÑти: + + + + Conflicts: + plugin details + Конфликты: + + + + Plugin details + Ð¡Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ модуле + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Модули загружаютÑÑ Ð¸ выгружаютÑÑ Ñразу поÑле активации/деактивации, однако Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² ÑпиÑке загружаемых при Ñтарте модулей не будут Ñохранены пока вы не примените Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² окне конфигурации. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (вÑтроенный) + + + + Details + Ð¡Ð²ÐµÐ´ÐµÐ½Ð¸Ñ + + + + No plugins in this category. + Ð’ Ñтой категории модулей нет. + + + + Add new data type + Добавить новый тип данных + + + + Rename selected data type + Переименовать выбранный тип данных + + + + Delete selected data type + Удалить выбранный тип данных + + + + Help for configuring data type editors + Справка по наÑтройке редакторов типов данных + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + УÑловие + + + + Named constraint: + Именованное ограничение: + + + + On conflict + При конфликте + + + + Enter a valid condition. + Введите корректное уÑловие. + + + + Enter a name of the constraint. + Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. + + + + ConstraintDialog + + + New constraint + constraint dialog + Ðовое ограничение + + + + Create + constraint dialog + Создать + + + + Edit constraint + dialog window + Редактировать ограничение + + + + Apply + constraint dialog + Применить + + + + Primary key + table constraints + Первичный ключ + + + + Foreign key + table constraints + Внешний ключ + + + + Unique + table constraints + УникальноÑть + + + + Not NULL + table constraints + Ðе NULL + + + + Check + table constraints + Проверка + + + + Generated + table constraints + Сгенерированное + + + + Collate + table constraints + Сравнение + + + + Default + table constraints + Значение по умолчанию + + + + ConstraintTabModel + + + Table + table constraints + Таблица + + + + Column (%1) + table constraints + Столбец (%1) + + + + Scope + table constraints + ОблаÑть Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ + + + + Type + table constraints + Тип + + + + Details + table constraints + ПодробноÑти + + + + Name + table constraints + Ð˜Ð¼Ñ + + + + CssDebugDialog + + + SQLiteStudio CSS console + КонÑоль CSS SQLiteStudio + + + + DataView + + + Filter data + data view + Отфильтровать данные + + + + Grid view + Табличный вид + + + + Form view + Форма + + + + Refresh table data + data view + Обновить данные таблицы + + + + First page + data view + ÐŸÐµÑ€Ð²Ð°Ñ Ñтраница + + + + Previous page + data view + ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ñтраница + + + + Next page + data view + Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ñтраница + + + + Last page + data view + ПоÑледнÑÑ Ñтраница + + + + Commit changes for selected cells + data view + Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ñчеек + + + + Rollback changes for selected cells + data view + Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ñчеек + + + + Show grid view of results + data view + Показать результаты в виде таблицы + + + + Show form view of results + data view + Показать результаты в виде формы + + + + Filter by text (if contains) + data view + ТекÑтовый фильтр (еÑли Ñодержит) + + + + Filter strictly by text (if equals) + data view + Строгий текÑтовый фильтр (еÑли равно) + + + + Tabs on top + data view + Вкладки Ñверху + + + + Tabs at bottom + data view + Вкладки Ñнизу + + + + Place new rows above selected row + data view + ПомеÑтить новые Ñтроки перед выделенной Ñтрокой + + + + Place new rows below selected row + data view + ПомеÑтить новые Ñтроки поÑле выделенной Ñтроки + + + + Place new rows at the end of the data view + data view + ПомеÑтить новые Ñтроки в конец облаÑти проÑмотра данных + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Идёт подÑчёт общего чиÑла Ñтрок. +Переключение на другие Ñтраницы Ñтанет возможным поÑле Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ Ð¿Ð¾Ð´Ñчёта. + + + + Row: %1 + Строка: %1 + + + + Filter + Фильтр + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Ðажмите Enter или кнопку "Применить фильтр" на панели инÑтрументов чтобы применить новое значение. + + + + Filter by the Regular Expression + data view + Фильтр по регулÑрному выражению + + + + Filter by SQL expression + data view + Фильтр по выражению SQL + + + + Show filter inputs per column + data view + Показывать поле ввода Ð´Ð»Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð° в каждом Ñтолбце + + + + Apply filter + data view + Применить фильтр + + + + DbDialog + + + Database + База данных + + + + Database type + Тип базы данных + + + + Database driver + Драйвер базы данных + + + + + File + Файл + + + + Name (on the list) + Ð˜Ð¼Ñ (в ÑпиÑке) + + + + Options + Опции + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Ðктивируйте Ñту опцию Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных в конфигурационном файле и Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÐµÑ‘ в ÑпиÑок при каждом запуÑке SQLiteStudio.</p> + + + + Permanent (keep it in configuration) + ПоÑтоÑÐ½Ð½Ð°Ñ (Ñохранить базу в конфигурационном файле) + + + + Test connection + ТеÑÑ‚ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ + + + + Select new or existing file on local computer + Выберите новый или ÑущеÑтвующий файл на локальном компьютере + + + + Browse + Обзор + + + + Database type not selected. + Тип базы данных не выбран. + + + + Database path not specified. + Путь к базе данных не указан. + + + + Enter an unique database name. + Введите уникальное Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных. + + + + This name is already in use. Please enter unique name. + Данное Ð¸Ð¼Ñ ÑƒÐ¶Ðµ иÑпользуетÑÑ. ПожалуйÑта, укажите уникальное имÑ. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>ÐвтоматичеÑÐºÐ°Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð¸ отключена, так как Ð¸Ð¼Ñ Ð±Ñ‹Ð»Ð¾ задано вручную. Ð”Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкой генерации необходимо удалить Ñодержимое из Ð¿Ð¾Ð»Ñ Ð¸Ð¼ÐµÐ½Ð¸.</p> + + + + Enter a database file path. + Введите путь к базе данных. + + + + This database is already on the list under name: %1 + Ð£ÐºÐ°Ð·Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных уже находитÑÑ Ð² ÑпиÑке под именем %1 + + + + Select a database type. + Выберите тип базы данных. + + + + DbObjectDialogs + + + Delete table + Удалить таблицу + + + + Are you sure you want to delete table %1? + Ð’Ñ‹ дейÑтвительно хотите удалить таблицу %1? + + + + Delete index + Удалить Ð¸Ð½Ð´ÐµÐºÑ + + + + Are you sure you want to delete index %1? + Ð’Ñ‹ дейÑтвительно хотите удалить Ð¸Ð½Ð´ÐµÐºÑ %1? + + + + Delete trigger + Удалить триггер + + + + Are you sure you want to delete trigger %1? + Ð’Ñ‹ дейÑтвительно хотите удалить триггер %1? + + + + Delete view + Удалить предÑтавление + + + + Are you sure you want to delete view %1? + Ð’Ñ‹ дейÑтвительно хотите удалить предÑтавление %1? + + + + + Error while dropping %1: %2 + Ошибка при удалении %1: %2 + + + + Delete objects + Удалить объекты + + + + Are you sure you want to delete following objects: +%1 + Ð’Ñ‹ дейÑтвительно хотите удалить Ñледующие объекты: +%1 + + + + Cannot start transaction. Details: %1 + Ðевозможно начать транзакцию. ПодробноÑти: %1 + + + + Cannot commit transaction. Details: %1 + Ðевозможно подтвердить транзакцию. ПодробноÑти: %1 + + + + DbTree + + + Databases + Базы данных + + + + Filter by name + Фильтр по имени + + + + Copy + Копировать + + + + Paste + Ð’Ñтавить + + + + Select all + Выделить вÑÑ‘ + + + + Create a group + Создать группу + + + + Delete the group + Удалить группу + + + + Rename the group + Переименовать группу + + + + &Add a database + &Добавить базу данных + + + + &Edit the database + &Редактировать базу данных + + + + &Remove the database + &Удалить базу данных + + + + &Connect to the database + &ПодключитьÑÑ Ðº базе данных + + + + &Disconnect from the database + &ОтключитьÑÑ Ð¾Ñ‚ базы данных + + + + Import + Импорт + + + + &Export the database + &ЭкÑпортировать базу данных + + + + Vac&uum + Оп&ÐµÑ€Ð°Ñ†Ð¸Ñ VACUUM + + + + &Integrity check + Проверить &целоÑтноÑть + + + + Create a &table + Создать &таблицу + + + + Edit the t&able + Редактировать Ñ‚&аблицу + + + + Delete the ta&ble + Удалить та&блицу + + + + Export the table + ЭкÑпортировать таблицу + + + + Import into the table + Импортировать данные в таблицу + + + + Populate table + Заполнить таблицу + + + + Create similar table + Создать подобную таблицу + + + + Reset autoincrement sequence + СброÑить Ñчётчик автоинкремента + + + + Create an &index + Создать &Ð¸Ð½Ð´ÐµÐºÑ + + + + Edit the i&ndex + Редактировать и&Ð½Ð´ÐµÐºÑ + + + + Delete the in&dex + Удалить инде&ÐºÑ + + + + Create a trig&ger + Создать три&ггер + + + + Edit the trigg&er + Редактиро&вать триггер + + + + Delete the trigge&r + Уда&лить триггер + + + + Create a &view + &Создать предÑтавление + + + + Edit the v&iew + Редактироват&ÑŒ предÑтавление + + + + Delete the vi&ew + Удалить &предÑтавление + + + + Add a column + Добавить Ñтолбец + + + + Edit the column + Редактировать Ñтолбец + + + + Delete the column + Удалить Ñтолбец + + + + Delete selected items + Удалить выбранные Ñлементы + + + + Clear filter + СброÑить фильтр + + + + &Refresh all database schemas + Обновить Ñтруктуры вÑех баз данн&ых + + + + Re&fresh selected database schema + Обновить Ñтруктуру выбранной базы данны&Ñ… + + + + + Erase table data + Удалить данные из таблицы + + + + Open file's directory + Открыть папку Ñ Ñтим файлом + + + + Execute SQL from file + Выполнить SQL-запроÑÑ‹ из файла + + + + Increase font size + database list + Увеличить размер шрифта + + + + Decrease font size + database list + Уменьшить размер шрифта + + + + + Database + База данных + + + + Grouping + Группировка + + + + Generate query for table + Сгенерировать Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ Ñтой таблицы + + + + + Create group + Создать группу + + + + Group name + Ð˜Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ + + + + Entry with name %1 already exists in group %2. + Элемент Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ %1 уже входит в группу %2. + + + + Delete group + Удалить группу + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Ð’Ñ‹ дейÑтвительно хотите удалить группу %1? Ð’Ñе объекты из данной группы будут перемещены в родительÑкую группу. + + + + Are you sure you want to remove database '%1' from the list? + Ð’Ñ‹ дейÑтвительно хотите удалить базу данных '%1' из ÑпиÑка? + + + + Are you sure you want to remove following databases from the list: +%1 + Ð’Ñ‹ дейÑтвительно хотите удалить Ñледующие базы данных из ÑпиÑка: %1 + + + + Remove database + Удалить базу данных + + + + + Cannot import, because no import plugin is loaded. + Ðевозможно произвеÑти импорт, Ñ‚. к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð°. + + + + + Cannot export, because no export plugin is loaded. + Ðевозможно произвеÑти ÑкÑпорт, Ñ‚.к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ ÑкÑпорта. + + + + Vacuum (%1) + ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ VACUUM (%1) + + + + Integrity check (%1) + Проверка целоÑтноÑти (%1) + + + + Reset autoincrement + Ð¡Ð±Ñ€Ð¾Ñ Ñчётчика автоинкремента + + + + Are you sure you want to reset autoincrement value for table '%1'? + Ð’Ñ‹ дейÑтвительно хотите ÑброÑить Ñчётчик автоинкремента у таблицы '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + При попытке ÑброÑа Ñчётчика автоинкремента у таблицы '%1' произошла ошибка: %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Ð¡Ð±Ñ€Ð¾Ñ Ñчётчика автоинкремента у таблицы '%1' уÑпешно выполнен. + + + + Are you sure you want to delete all data from table(s): %1? + Ð’Ñ‹ дейÑтвительно хотите удалить вÑе данные из таблицы (таблиц): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + При попытке ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… из таблицы '%1' произошла ошибка: %2 + + + + All data has been deleted for table '%1'. + Из таблицы '%1' были удалены вÑе данные. + + + + Following objects will be deleted: %1. + Будут удалены Ñледующие объекты: %1. + + + + Following databases will be removed from list: %1. + Из ÑпиÑка будут удалены Ñледующие базы данных: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + ОÑтавшиеÑÑ Ð¿Ð¾Ñле ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ объекты будут перемещены туда, где ранее раÑполагалаÑÑŒ группа. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Ð’Ñ‹ дейÑтвительно хотите продолжить? + + + + Delete objects + Удалить объекты + + + + DbTreeItemDelegate + + + error + dbtree labels + ошибка + + + + (system table) + database tree label + (ÑиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°) + + + + (virtual) + virtual table label + (виртуальнаÑ) + + + + (system index) + database tree label + (ÑиÑтемный индекÑ) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + База данных: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + ВерÑиÑ: + + + + File size: + dbtree tooltip + Размер файла: + + + + Encoding: + dbtree tooltip + Кодировка: + + + + Error: + dbtree tooltip + Ошибка: + + + + Table : %1 + dbtree tooltip + Таблица: %1 + + + + Columns (%1): + dbtree tooltip + Столбцы (%1): + + + + Indexes (%1): + dbtree tooltip + ИндекÑÑ‹ (%1): + + + + Triggers (%1): + dbtree tooltip + Триггеры (%1): + + + + Copy + Копировать + + + + Move + ПеремеÑтить + + + + Include data + Ð’ÐºÐ»ÑŽÑ‡Ð°Ñ Ð´Ð°Ð½Ð½Ñ‹Ðµ + + + + Include indexes + Ð’ÐºÐ»ÑŽÑ‡Ð°Ñ Ð¸Ð½Ð´ÐµÐºÑÑ‹ + + + + Include triggers + Ð’ÐºÐ»ÑŽÑ‡Ð°Ñ Ñ‚Ñ€Ð¸Ð³Ð³ÐµÑ€Ñ‹ + + + + Abort + Прервать + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Ðевозможно автоматичеÑки добавить перетÑнутый файл базы данных '%1'. Ðеобходима Ñ€ÑƒÑ‡Ð½Ð°Ñ Ð½Ð°Ñтройка. + + + + Referenced tables + СвÑзанные таблицы + + + + Do you want to include following referenced tables as well: +%1 + Ð’Ñ‹ хотите также включить Ñледующие ÑвÑзанные таблицы: +%1 + + + + Name conflict + Конфликт имён + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Данный объект уже ÑущеÑтвует в целевой базе данных. +ПожалуйÑта введите новое уникальное Ð¸Ð¼Ñ Ð¸Ð»Ð¸ нажмите '%1' Ð´Ð»Ñ Ð¿Ñ€ÐµÑ€Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¸: + + + + SQL statements conversion + ÐšÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð°Ñ†Ð¸Ñ ÐºÐ¾Ð½Ñтрукций SQL + + + + Following error occurred while converting SQL statements to the target SQLite version: + При конвертации конÑтрукций SQL в новую верÑию SQLite произошла ошибка: + + + + Would you like to ignore those errors and proceed? + Ð’Ñ‹ хотите проигнорировать Ñти ошибки и продолжить? + + + + DdlHistoryWindow + + + Filter by database: + Фильтр по базе данных: + + + + Clear entire history + ОчиÑтить вÑÑŽ иÑторию + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- ЗапроÑÑ‹, выполненные к базе данных %1 (%2) +-- Дата и Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ: %3 +%4 + + + + Clear history + ОчиÑтить иÑторию + + + + Are you sure you want to erase entire DDL history? + Ð’Ñ‹ уверены, что хотите Ñтереть вÑÑŽ иÑторию DDL? + + + + DDL history + ИÑÑ‚Ð¾Ñ€Ð¸Ñ DDL + + + + DdlPreviewDialog + + + Queries to be executed + ЗапроÑÑ‹, которые будут выполнены + + + + Don't show again + Больше не показывать + + + + DebugConsole + + + SQLiteStudio Debug Console + ÐžÑ‚Ð»Ð°Ð´Ð¾Ñ‡Ð½Ð°Ñ ÐºÐ¾Ð½Ñоль SQLiteStudio + + + + EditorWindow + + + SQL editor + Редактор SQL + + + + Query + Ð—Ð°Ð¿Ñ€Ð¾Ñ + + + + History + ИÑÑ‚Ð¾Ñ€Ð¸Ñ + + + + Results in the separate tab + Результаты в отдельной вкладке + + + + Results below the query + Результаты под запроÑом + + + + + SQL editor %1 + Редактор SQL %1 + + + + + Results + Результаты + + + + Execute query + Выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ + + + + Explain query + План запроÑа + + + + Clear execution history + sql editor + ОчиÑтить иÑторию запроÑов + + + + Export results + sql editor + ЭкÑпортировать результаты + + + + Create view from query + sql editor + Создать предÑтавление из запроÑа + + + + Previous database + ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных + + + + Next database + Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных + + + + Show next tab + sql editor + Открыть Ñледующую вкладку + + + + Show previous tab + sql editor + Открыть предыдущую вкладку + + + + Focus results below + sql editor + Ð¤Ð¾ÐºÑƒÑ Ð½Ð° результатах внизу + + + + Focus SQL editor above + sql editor + Ð¤Ð¾ÐºÑƒÑ Ð½Ð° редакторе SQL Ñверху + + + + Delete selected SQL history entries + sql editor + Удалить выбранные запиÑи из иÑтории SQL-запроÑов + + + + Execute single query under cursor + Выполнить одиночный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ð´ курÑором + + + + Execute all queries in editor + Выполнить вÑе запроÑÑ‹ в редакторе + + + + Active database (%1/%2) + Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½ за %1 Ñекунд. Затронуто Ñтрок: %2 + + + + Query finished in %1 second(s). + Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½ за %1 Ñекунд. + + + + Clear execution history + ОчиÑтка иÑтории запроÑов + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Ð’Ñ‹ дейÑтвительно хотите удалить вÑÑŽ иÑторию Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ SQL запроÑов? Операцию невозможно отменить. + + + + Cannot export, because no export plugin is loaded. + Ðевозможно произвеÑти ÑкÑпорт, Ñ‚. к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ ÑкÑпорта. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + Ð’ редакторе SQL не выбрана база данных. Ðевозможно Ñоздать предÑтавление в неизвеÑтной базе данных. + + + + Editor window "%1" has uncommitted data. + Ð’ окне редактора "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ данные. + + + + ErrorsConfirmDialog + + + Errors + Ошибки + + + + Following errors occured: + Возникли Ñледующие ошибки: + + + + Would you like to proceed? + Ð’Ñ‹ хотите продолжить? + + + + ExecFromFileDialog + + + Execute SQL from file + Выполнение SQL-запроÑов из файла + + + + Input file + Файл-иÑточник + + + + Path to file + Путь к файлу + + + + Browse for file + Выбрать файл + + + + Options + Опции + + + + File encoding + Кодировка файла + + + + Skip failing SQL statements + ПропуÑк неудавшихÑÑ SQL-запроÑов + + + + SQL scripts (*.sql);;All files (*) + Скрипты SQL (*.sql);;Ð’Ñе файлы (*) + + + + Execute SQL file + Выполнить SQL-запроÑÑ‹ из файла + + + + Please provide file to be executed. + ПожалуйÑта укажите файл Ñ SQL-запроÑами. + + + + Provided file does not exist or cannot be read. + Указанный файл не ÑущеÑтвует или не может быть прочитан. + + + + ExportDialog + + + Export + ЭкÑпорт + + + + What do you want to export? + Что вы хотите ÑкÑпортировать? + + + + A database + Базу данных + + + + A single table + Одну таблицу + + + + Query results + Результаты запроÑа + + + + Table to export + ЭкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€ÑƒÐµÐ¼Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° + + + + Database + База данных + + + + Table + Таблица + + + + Options + Опции + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + ЕÑли Ð´Ð°Ð½Ð½Ð°Ñ Ð¾Ð¿Ñ†Ð¸Ñ Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, будет ÑкÑпортирован только DDL таблицы (конÑÑ‚Ñ€ÑƒÐºÑ†Ð¸Ñ CREATE TABLE). + + + + Export table data + ЭкÑпортировать данные таблицы + + + + Export table indexes + ЭкÑпортировать индекÑÑ‹ таблицы + + + + Export table triggers + ЭкÑпортировать триггеры таблицы + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Учтите, что ÑкÑпорт индекÑов и триггеров таблицы может не поддерживатьÑÑ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¼Ð¸ выходными форматами. + + + + Select database objects to export + Выберите объекты базы данных Ð´Ð»Ñ ÑкÑпорта + + + + Export data from tables + ЭкÑпортировать данные таблиц + + + + Select all + Выбрать вÑÑ‘ + + + + Deselect all + СнÑть выделение + + + + + Database: + База данных: + + + + Query to export results for + Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ ÑкÑпорта результатов + + + + Query to be executed for results: + ВыполнÑемый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…: + + + + Export format and options + Формат ÑкÑпорта и опции + + + + Export format + Формат ÑкÑпорта + + + + Output + Вывод + + + + Exported file path + Путь к результирующему файлу + + + + Clipboard + Буфер обмена + + + + File + Файл + + + + Exported text encoding: + Кодировка ÑкÑпорта: + + + + Export format options + Опции формата ÑкÑпорта + + + + Cancel + Отмена + + + + + + Select database to export. + Выберите базу данных Ð´Ð»Ñ ÑкÑпорта. + + + + Select table to export. + Выберите таблицу Ð´Ð»Ñ ÑкÑпорта. + + + + Enter valid query to export. + Введи корректный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ ÑкÑпорта. + + + + Select at least one object to export. + Выберите Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один объект Ð´Ð»Ñ ÑкÑпорта. + + + + You must provide a file name to export to. + Ðеобходимо указать Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°, в который будет произведён ÑкÑпорт. + + + + Path you provided is an existing directory. You cannot overwrite it. + Указанный путь ÑвлÑетÑÑ ÑущеÑтвующим каталогом. Его невозможно перезапиÑать. + + + + The directory '%1' does not exist. + Каталог '%1' не ÑущеÑтвует. + + + + The file '%1' exists and will be overwritten. + Файл '%1' ÑущеÑтвует и будет перезапиÑан. + + + + All files (*) + Ð’Ñе файлы (*) + + + + Pick file to export to + Выберите файл Ð´Ð»Ñ ÑкÑпорта + + + + Internal error during export. This is a bug. Please report it. + ВнутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° во Ð²Ñ€ÐµÐ¼Ñ ÑкÑпорта. ПожалуйÑта, вышлите отчёт об Ñтой ошибке. + + + + FileExecErrorsDialog + + + Execution errors + Ошибки Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ + + + + Following errors were encountered during execution of SQL statements from the file: + При выполнении SQL-запроÑов из файла возникли Ñледующие ошибки: + + + + SQL + SQL-Ð·Ð°Ð¿Ñ€Ð¾Ñ + + + + Error + Ошибка + + + + Statements that were executed successfully were commited. + УÑпешно выполненные запроÑÑ‹ были запиÑаны в базу. + + + + Statements that were executed successfully were rolled back. + УÑпешно выполненные запроÑÑ‹ были откачены. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Ðевозможно редактировать данную Ñчейку. ПодробноÑти: %1 + + + + FontEdit + + + Choose font + font configuration + Выберите шрифт + + + + Form + + + Active SQL formatter plugin + Ðктивный модуль Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ SQL + + + + FormView + + + Commit row + form view + Подтвердить Ñтроку + + + + Rollback row + form view + Откатить Ñтроку + + + + First row + form view + ÐŸÐµÑ€Ð²Ð°Ñ Ñтрока + + + + Previous row + form view + ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ñтрока + + + + Next row + form view + Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ñтрока + + + + Last row + form view + ПоÑледнÑÑ Ñтрока + + + + Insert new row + form view + Ð’Ñтавить новую Ñтроку + + + + Delete current row + form view + Удалить текущую Ñтроку + + + + FunctionsEditor + + + Filter functions + Фильтр функций + + + + Input arguments + Передаваемые аргументы + + + + Undefined + Ðе определено + + + + Databases + Базы данных + + + + Register in all databases + ЗарегиÑтрировать во вÑех базах данных + + + + Register in following databases: + ЗарегиÑтрировать в Ñледующих базах данных: + + + + Type: + Тип: + + + + Function name: + Ð˜Ð¼Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸: + + + + Implementation language: + Язык реализации: + + + + Deterministic + Ð”ÐµÑ‚ÐµÑ€Ð¼Ð¸Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ + + + + Initialization code: + Код инициализации: + + + + + Function implementation code: + Код реализации функции: + + + + Final step implementation code: + Код реализации поÑледнего шага: + + + + SQL functions editor + Редактор функций SQL + + + + Commit all function changes + Подтвердить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¹ + + + + Rollback all function changes + Откатить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¹ + + + + Create new function + Создать новую функцию + + + + Delete selected function + Удалить выбранную функцию + + + + Custom SQL functions manual + РуководÑтво по Ñозданию произвольных функций SQL + + + + Add function argument + Добавить аргумент функции + + + + Rename function argument + Переименовать аргумент функции + + + + Delete function argument + Удалить аргумент функции + + + + Move function argument up + ПеремеÑтить аргумент функции вверх + + + + Move function argument down + ПеремеÑтить аргумент функции вниз + + + + Scalar + СкалÑÑ€Ð½Ð°Ñ + + + + Aggregate + ÐÐ³Ñ€ÐµÐ³Ð¸Ñ€ÑƒÑŽÑ‰Ð°Ñ + + + + Enter a non-empty, unique name of the function. + Введите непуÑтое уникальное Ð¸Ð¼Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸. + + + + Pick the implementation language. + Выберите Ñзык реализации. + + + + Per step code: + Код на каждом шаге: + + + + Enter a non-empty implementation code. + Введите непуÑтой код реализации. + + + + argument + new function argument name in function editor window + аргумент + + + + Functions editor window has uncommitted modifications. + Ð’ окне редактора функций имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ изменениÑ. + + + + ImportDialog + + + Import data + Импорт данных + + + + Table to import to + Таблица Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° + + + + Table + Таблица + + + + Database + База данных + + + + Data source to import from + ИÑточник данных Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° + + + + Data source type + Тип иÑточника данных + + + + Options + Опции + + + + Text encoding: + Кодировка текÑта: + + + + Input file: + Файл-иÑточник: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>ЕÑли Ð¾Ð¿Ñ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°, вÑе Ð½Ð°Ñ€ÑƒÑˆÐµÐ½Ð¸Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ð¹, неправильный формат данных (неверное количеÑтво Ñтолбцов) и любые другие проблемы, возникшие при оÑущеÑтвлении импорта, будут проигнорированы и импорт будет продолжен.</p> + + + + Ignore errors + Игнорировать ошибки + + + + Data source options + Опции иÑточника данных + + + + Cancel + Отмена + + + + If you type table name that doesn't exist, it will be created. + ЕÑли вы введёте неÑущеÑтвующее Ð¸Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹, она будет Ñоздана. + + + + Enter the table name + Введите Ð¸Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ + + + + Select import plugin. + Выберите модуль импорта. + + + + You must provide a file to import from. + Ðеобходимо указать файл, из которого оÑущеÑтвлÑетÑÑ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚. + + + + The file '%1' does not exist. + Файл '%1' не ÑущеÑтвует. + + + + Path you provided is a directory. A regular file is required. + Указанный путь ÑвлÑетÑÑ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð¾Ð¼. Ðеобходимо указать файл. + + + + Pick file to import from + Выберите файл Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° + + + + IndexDialog + + + + Index + Ð˜Ð½Ð´ÐµÐºÑ + + + + Column + Столбец + + + + Sort + Сортировка + + + + Collation + Сравнение + + + + On table: + Ð”Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹: + + + + Delete selected indexed expression + Удалить выбранное индекÑируемое выражение + + + + Moves selected index column up in the order, making it more significant in the index. + ПеремеÑтить индекÑируемый Ñтолбец выше по ÑпиÑку, ÑƒÐ²ÐµÐ»Ð¸Ñ‡Ð¸Ð²Ð°Ñ ÐµÐ³Ð¾ значимоÑть в индекÑе. + + + + Moves selected index column down in the order, making it less significant in the index. + ПеремеÑтить индекÑируемый Ñтолбец ниже по ÑпиÑку, ÑÐ½Ð¸Ð¶Ð°Ñ ÐµÐ³Ð¾ значимоÑть в индекÑе. + + + + Partial index condition + УÑловие Ð´Ð»Ñ Ñ‡Ð°Ñтичного индекÑа + + + + Unique index + Уникальный Ð¸Ð½Ð´ÐµÐºÑ + + + + Index name: + Ð˜Ð¼Ñ Ð¸Ð½Ð´ÐµÐºÑа: + + + + Edit selected indexed expression + Редактировать выбранное индекÑируемое выражение + + + + Add indexed expression + Добавить индекÑируемое выражение + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Попытка вызвать диалог ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑа Ð´Ð»Ñ Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¾Ð¹ или неÑущеÑтвующей базы данных. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Ðе удалоÑÑŒ корректно обработать Ð¸Ð½Ð´ÐµÐºÑ %1. Ðевозможно открыть окно индекÑа. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Уникальный Ð¸Ð½Ð´ÐµÐºÑ Ð½Ðµ может Ñодержать индекÑируемые выражениÑ. Либо удалите Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¸Ð· ÑпиÑка ниже, либо отключите Ñту опцию. + + + + Pick the table for the index. + Выберите таблицу Ð´Ð»Ñ Ð¸Ð½Ð´ÐµÐºÑа. + + + + Select at least one column. + Выберите Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один Ñтолбец. + + + + Enter a valid condition. + Введите корректное уÑловие. + + + + default + index dialog + по умолчанию + + + + Sort order + table constraints + ПорÑдок Ñортировки + + + + + Error + index dialog + Ошибка + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Ðевозможно Ñоздать уникальный индекÑ, Ñ‚. к. данные в выбранных Ñтолбцах неуникальны. Ð’Ñ‹ хотите выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ SELECT Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра проблемных данных? + + + + An error occurred while executing SQL statements: +%1 + При выполнении конÑтрукций SQL произошла ошибка: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + ИндекÑируемое выражение + + + + Expression to index + Выражение Ð´Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð² Ð¸Ð½Ð´ÐµÐºÑ + + + + This expression is already indexed by the index. + Такое выражение уже приÑутÑтвует в индекÑе. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Столбец необходимо индекÑировать напрÑмую, а не выражением. Либо добавьте в выражение что-либо кроме имени Ñтолбца, либо отмените Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸ отметьте Ñтолбец непоÑредÑтвенно в окне индекÑа. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Столбец '%1' не принадлежит к индекÑируемой таблице. ИндекÑируемые Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ ÑÑылатьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ на Ñтолбцы индекÑируемой таблицы. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + Запрещено иÑпользовать конÑтрукции SELECT в индекÑируемых выражениÑÑ…. + + + + Enter an indexed expression. + Введите индекÑируемое выражение. + + + + Invalid expression. + Ðекорректное выражение. + + + + LanguageDialog + + + Language + Язык + + + + Please choose language: + ПожалуйÑта, выберите Ñзык: + + + + MainWindow + + + Database toolbar + Панель базы данных + + + + Structure toolbar + Панель Ñтруктуры + + + + Tools + ИнÑтрументы + + + + Window list + СпиÑок окон + + + + View toolbar + Панель Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ + + + + Configuration widgets + Виджеты конфигурации + + + + Syntax highlighting engines + Движки ÑинтакÑичеÑкой подÑветки + + + + Data editors + Редакторы данных + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Отладочный режим. Ðажмите %1 или воÑпользуйтеÑÑŒ пунктом меню 'Справка / Открыть отладочную конÑоль' Ð´Ð»Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð¸Ñ Ð¾Ñ‚Ð»Ð°Ð´Ð¾Ñ‡Ð½Ð¾Ð¹ конÑоли. + + + + Running in debug mode. Debug messages are printed to the standard output. + Отладочный режим. Отладочные ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð²Ñ‹Ð²Ð¾Ð´ÑÑ‚ÑÑ Ð² Ñтандартный выходной поток. + + + + You need to restart application to make the language change take effect. + Ð”Ð»Ñ Ñмены Ñзыка необходимо перезапуÑтить приложение. + + + + Open SQL &editor + &Открыть редактор SQL + + + + Open DDL &history + О&ткрыть иÑторию DDL + + + + Open SQL &functions editor + От&крыть редактор функций SQL + + + + Open code &snippets editor + Открыть редактор &Ñниппетов + + + + Open &collations editor + Отк&рыть редактор Ñравнений + + + + Open ex&tension manager + Откр&ыть менеджер раÑширений + + + + &Import + &Импорт + + + + E&xport + &ЭкÑпорт + + + + Open confi&guration dialog + Открыт&ÑŒ диалог конфигурации + + + + &Tile windows + Р&аÑположить окна плиткой + + + + Tile windows &horizontally + РаÑпо&ложить окна по горизонтали + + + + Tile windows &vertically + РаÑполо&жить окна по вертикали + + + + &Cascade windows + Ра&Ñположить окна каÑкадом + + + + Next window + Следующее окно + + + + Previous window + Предыдущее окно + + + + Hide status field + Скрыть окно ÑтатуÑа + + + + Close &all windows + Закрыть вÑ&е окна + + + + Re&store recently closed window + ВоÑÑта&новить поÑледнее закрытое окно + + + + Close current &window + Закрыть текущее &окно + + + + Close &other windows + Закрыть &другие окна + + + + Close windows on the &left + Закрыть окна Ñ&лева + + + + Close windows on the &right + Закрыть окна Ñ&права + + + + Re&name selected window + Переи&меновать выбранное окно + + + + Open Debug Console + Открыть отладочную конÑоль + + + + Open CSS Console + Открыть конÑоль CSS + + + + Report a &bug + Сообщить об о&шибке + + + + D&onate + По&жертвование + + + + Propose a new &feature + Предложить новую &функцию + + + + &About + О про&грамме + + + + &Licenses + Ли&цензии + + + + Open home &page + Открыть домашн&ÑŽÑŽ Ñтраницу + + + + User &Manual + РуководÑтво пользовател&Ñ + + + + SQLite &documentation + &Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ Ð¿Ð¾ SQLite + + + + Bugs and feature &requests + Оши&бки и Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ + + + + Quit + Выход + + + + Check for &updates + &Проверить Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ + + + + &Database + menubar + &База данных + + + + &Structure + menubar + &Структура + + + + &View + menubar + &Вид + + + + Window list + menubar view menu + Окна + + + + &Tools + menubar + &ИнÑтрументы + + + + &Help + С&правка + + + + Could not set style: %1 + main window + Ðевозможно применить Ñтиль: %1 + + + + Cannot export, because no export plugin is loaded. + Ðевозможно произвеÑти ÑкÑпорт, Ñ‚. к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ ÑкÑпорта. + + + + Cannot import, because no import plugin is loaded. + Ðевозможно произвеÑти импорт, Ñ‚. к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð°. + + + + Rename window + Переименовать окно + + + + Enter new name for the window: + Введите новое Ð¸Ð¼Ñ Ð´Ð»Ñ Ð¾ÐºÐ½Ð°: + + + + New updates are available. <a href="%1">Click here for details</a>. + ДоÑтупны обновлениÑ. <a href="%1">Ðажмите здеÑÑŒ Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñтей</a>. + + + + You're running the most recent version. No updates are available. + УÑтановлена поÑледнÑÑ Ð²ÐµÑ€ÑиÑ. Обновлений нет. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + База данных, Ð¿ÐµÑ€ÐµÐ´Ð°Ð½Ð½Ð°Ñ Ñ‡ÐµÑ€ÐµÐ· аргументы командной Ñтроки (%1), уже находитÑÑ Ð² ÑпиÑке под именем %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + База данных, Ð¿ÐµÑ€ÐµÐ´Ð°Ð½Ð½Ð°Ñ Ñ‡ÐµÑ€ÐµÐ· аргументы командной Ñтроки (%1), была временно добавлена в ÑпиÑок под именем %2 + + + + Could not add database %1 to list. + Ðевозможно добавить базу данных %1 в ÑпиÑок. + + + + MdiWindow + + + Uncommitted changes + Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ + + + + Close anyway + Ð’ÑÑ‘ равно закрыть + + + + Don't close + Ðе закрывать + + + + MultiEditor + + + Null value + multieditor + Значение Null + + + + Configure editors for this data type + ÐаÑтройте редакторы Ð´Ð»Ñ Ñтого типа данных + + + + Open another tab + Открыть дополнительную вкладку + + + + Foreign Key + Внешний ключ + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Модуль редактора данных '%1' не загружен, Ñ…Ð¾Ñ‚Ñ ÑƒÐºÐ°Ð·Ð°Ð½ Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ‚Ð¸Ð¿Ð° данных '%1'. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Удалено + + + + Read only + multieditor + Только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ + + + + MultiEditorBoolPlugin + + + Boolean + ЛогичеÑкое + + + + MultiEditorDatePlugin + + + Date + Дата + + + + MultiEditorDateTimePlugin + + + Date & time + Дата & Ð²Ñ€ÐµÐ¼Ñ + + + + MultiEditorHexPlugin + + + Hex + ШеÑтнадцатеричное + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + ЧиÑло + + + + MultiEditorText + + + Tab changes focus + Tab перемещает Ñ„Ð¾ÐºÑƒÑ + + + + Cut + Вырезать + + + + Copy + Копировать + + + + Paste + Ð’Ñтавить + + + + Delete + Удалить + + + + Undo + Отменить + + + + Redo + Повторить + + + + MultiEditorTextPlugin + + + Text + ТекÑÑ‚ + + + + MultiEditorTimePlugin + + + Time + Ð’Ñ€ÐµÐ¼Ñ + + + + NewConstraintDialog + + + New constraint + Ðовое ограничение + + + + + Primary Key + new constraint dialog + Первичный ключ + + + + + Foreign Key + new constraint dialog + Внешний ключ + + + + + Unique + new constraint dialog + УникальноÑть + + + + + Check + new constraint dialog + Проверка + + + + Not NULL + new constraint dialog + Ðе NULL + + + + Collate + new constraint dialog + Сравнение + + + + Generated + new constraint dialog + Сгенерированное + + + + Default + new constraint dialog + Значение по умолчанию + + + + NewVersionDialog + + + SQLiteStudio updates + ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ SQLiteStudio + + + + New version is available! + ДоÑтупна Ð½Ð¾Ð²Ð°Ñ Ð²ÐµÑ€ÑиÑ! + + + + Download new version! + Загрузить новую верÑию! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + Будет загружен уÑтановщик новой верÑии. Ð’Ñ‹ Ñможете начать уÑтановку когда будете готовы. + + + + Open SQLiteStudio home page. + ПоÑетить домашнюю Ñтраницу SQLiteStudio. + + + + Read release notes && download package yourself. + Прочитать Ð¿Ñ€Ð¸Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ðº выпуÑку и загрузить уÑтановщик ÑамоÑтоÑтельно. + + + + Just close this window. + ПроÑто закрыть Ñто окно. + + + + Check for updates on startup + ПроверÑть Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ запуÑке + + + + Not now. + Ðе ÑейчаÑ. + + + + PopulateConfigDialog + + + Populating configuration + ÐаÑтройка Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ + + + + Configuring <b>%1</b> for column <b>%2</b> + ÐаÑтройка <b>%1</b> Ð´Ð»Ñ Ñтолбца <b>%2</b> + + + + PopulateDialog + + + Populate table + Заполнить таблицу + + + + Database + База данных + + + + Table + Таблица + + + + Columns + Столбцы + + + + Number of rows to populate: + КоличеÑтво заполнÑемых Ñтрок: + + + + Populate + populate dialog button + Заполнить + + + + Abort + Прервать + + + + Configure + ÐаÑтроить + + + + Populating configuration for this column is invalid or incomplete. + ÐаÑтройка Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ Ñтолбца некорректна или не завершена. + + + + Select database with table to populate + Выберите базу данных Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†ÐµÐ¹ Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ + + + + Select table to populate + Выберите таблицу Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ + + + + You have to select at least one column. + Ðеобходимо выбрать Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один Ñтолбец. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Ðевозможно редактировать Ñтолбцы, ÑвлÑющиеÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð¼ ÑоÑтавных конÑтрукций %1 (те, которые включают ключевые Ñлова %2, %3 и %4). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + Ð’ механизме Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов возникли проблемы при корректном извлечении значений ROWID. Предположительно Ñто ошибка в приложении. Возможно Ñтоит отправить отчёт об ошибке. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Запрошенный Ñтолбец ÑвлÑетÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð¼ SQL выражениÑ, а не проÑто выбором Ñтолбца. Такие Ñтолбцы не могут быть отредактированы. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Запрошенный Ñтолбец принадлежит Ñлужебной таблице SQLite. Эти таблицы Ð½ÐµÐ»ÑŒÐ·Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ напрÑмую. + + + + Cannot edit results of query other than %1. + Ðевозможно редактировать результаты запроÑов, отличных от %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Ðевозможно редактировать Ñтолбцы, ÑвлÑющиеÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð¼ агрегирующих конÑтрукций %1. + + + + Cannot edit columns that are result of %1 statement. + Ðевозможно редактировать Ñтолбцы, ÑвлÑющиеÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð¼ конÑтрукции %1. + + + + Cannot edit columns that are result of common table expression statement (%1). + Ðевозможно редактировать Ñтолбцы, ÑвлÑющиеÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð¼ обобщённого табличного Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ (%1). + + + + Cannot edit table generated columns. + Ðевозможно редактировать Ñгенерированные таблицей Ñтолбцы. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Ðевозможно редактировать Ñтолбцы в результатах предÑтавлениÑ, еÑли выполнÑемый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð²Ñ‹Ð±Ð¸Ñ€Ð°ÐµÑ‚ данные из любых многоуровневых предÑтавлений (Ñ‚.е. предÑтавлений, в которых запрашиваютÑÑ Ð´Ñ€ÑƒÐ³Ð¸Ðµ предÑтавлениÑ). + + + + + + + on conflict: %1 + data view tooltip + при конфликте: %1 + + + + references table %1, column %2 + data view tooltip + ÑÑылаетÑÑ Ð½Ð° таблицу %1, Ñтолбец %2 + + + + condition: %1 + data view tooltip + уÑловие: %1 + + + + collation name: %1 + data view tooltip + Ð¸Ð¼Ñ ÑравнениÑ: %1 + + + + Data grid view + Табличный вид данных + + + + Edit current cell inline + Редактировать текущую Ñчейку + + + + Copy cell(s) contents to clipboard + Копировать Ñодержимое Ñчеек в буфер обмена + + + + Copy cell(s) contents together with header to clipboard + Копировать Ð¸Ð¼Ñ Ñтолбца и Ñодержимое Ñчеек в буфер обмена + + + + Paste cell(s) contents from clipboard + Ð’Ñтавить Ñодержимое Ñчеек из буфера обмена + + + + Set empty value to selected cell(s) + УÑтановить пуÑтое значение Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ñчеек + + + + Set NULL value to selected cell(s) + УÑтановить Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ñчеек значение NULL + + + + Commit changes to cell(s) contents + Подтвердить изменение Ñодержимого Ñчеек + + + + Rollback changes to cell(s) contents + Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñодержимого Ñчеек + + + + Delete selected data row + Удалить выбранную Ñтроку данных + + + + Insert new data row + Ð’Ñтавить новую Ñтроку данных + + + + Open contents of selected cell in a separate editor + Открыть Ñодержимое выбранной Ñчейки в отдельном редакторе + + + + Toggle the height adjustment of rows + Переключить регулировку выÑоты Ñтрок + + + + Increase font size + data view + Увеличить размер шрифта + + + + Decrease font size + data view + Уменьшить размер шрифта + + + + Total pages available: %1 + Ð’Ñего доÑтупно Ñтраниц: %1 + + + + Total rows loaded: %1 + Ð’Ñего загружено Ñтрок: %1 + + + + Data view (both grid and form) + Окно данных (и табличный вид, и форма) + + + + Refresh data + Обновить данные + + + + Switch to grid view of the data + ПереключитьÑÑ Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ‡Ð½Ð¾Ð³Ð¾ вида на форму + + + + Switch to form view of the data + ПереключитьÑÑ Ð¸Ð· формы на табличный вид + + + + Database list + СпиÑок баз данных + + + + Delete selected item + Удалить выбранный Ñлемент + + + + Clear filter contents + СброÑить Ñодержимое фильтра + + + + Refresh schema + Обновить Ñтруктуру + + + + Refresh all schemas + Обновить Ñтруктуры вÑех баз данных + + + + Add database + Добавить базу данных + + + + Select all items + Выделить вÑе Ñлементы + + + + Copy selected item(s) + Копировать выбранные Ñлементы + + + + + + Paste from clipboard + Ð’Ñтавить из буфера обмена + + + + Increase font size + database list + Увеличить размер шрифта + + + + Decrease font size + database list + Уменьшить размер шрифта + + + + Tables + Таблицы + + + + Indexes + ИндекÑÑ‹ + + + + Triggers + Триггеры + + + + Views + ПредÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ + + + + Columns + Столбцы + + + + Data form view + Форма + + + + Commit changes for current row + Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ Ñтроки + + + + Rollback changes for current row + Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ Ñтроки + + + + Go to first row on current page + Перейти к первой Ñтроке текущей Ñтраницы + + + + Go to next row + Перейти к Ñледующей Ñтроке + + + + Go to previous row + Перейти к предыдущей Ñтроке + + + + Go to last row on current page + Перейти к поÑледней Ñтроке текущей Ñтраницы + + + + Insert new row + Ð’Ñтавить новую Ñтроку + + + + Delete current row + Удалить текущую Ñтроку + + + + Main window + Главное окно + + + + Open SQL editor + Открыть редактор SQL + + + + Open DDL history window + Открыть окно иÑтории DDL + + + + Open snippets editor window + Открыть окно редактора Ñниппетов + + + + Open function editor window + Открыть окно редактора функций + + + + Open collation editor window + Открыть окно редактора Ñравнений + + + + Open extension manager window + Открыть окно менеджера раÑширений + + + + Previous window + Предыдущее окно + + + + Next window + Следующее окно + + + + Hide status area + Скрыть облаÑть ÑтатуÑа + + + + Open user manual + Открыть руководÑтво Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ + + + + Open configuration dialog + Открыть диалог конфигурации + + + + Open Debug Console + Открыть отладочную конÑоль + + + + Open CSS Console + Открыть конÑоль CSS + + + + Open the About dialog + Открыть диалог "О программе" + + + + Quit the application + Выход из Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ + + + + Cell text value editor + Редактор текÑтового Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñчейки + + + + + Cut selected text + Вырезать выбранный текÑÑ‚ + + + + + Copy selected text + Копировать выбранный текÑÑ‚ + + + + + Delete selected text + Удалить выбранный текÑÑ‚ + + + + + Undo + Отменить + + + + + Redo + Повторить + + + + SQL editor input field + Поле ввода редактора SQL + + + + Select whole editor contents + Выбрать вÑÑ‘ Ñодержимое редактора + + + + Save contents into a file + Сохранить Ñодержимое в файл + + + + Load contents from a file + Загрузить Ñодержимое из файла + + + + Find in text + Ðайти в текÑте + + + + Find next + Ðайти далее + + + + Find previous + Ðайти предыдущее + + + + Replace in text + Замена в текÑте + + + + Delete current line + Удалить текущую Ñтрочку + + + + Request code assistant + Вызвать автодополнение + + + + Format contents + Форматировать Ñодержимое + + + + Move selected block of text one line down + ПеремеÑтить выбранный блок текÑта на Ñтрочку вниз + + + + Move selected block of text one line up + ПеремеÑтить выбранный блок текÑта на Ñтрочку вверх + + + + Copy selected block of text and paste it a line below + Скопировать блок текÑта и вÑтавить его Ñтрочкой ниже + + + + Copy selected block of text and paste it a line above + Скопировать блок текÑта и вÑтавить его Ñтрочкой выше + + + + Toggle comment + Комментировать/раÑкомментировать + + + + Increase font size + sql editor + Увеличить размер шрифта + + + + Decrease font size + sql editor + Уменьшить размер шрифта + + + + All SQLite databases + Ð’Ñе базы данных SQLite + + + + All files + Ð’Ñе файлы + + + + Select database file + Выберите файл базы данных + + + + Select + Выбрать + + + + File type + Тип файла + + + + SQL editor window + Окно редактора SQL + + + + Execute query + Выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ + + + + Execute single query under cursor + Выполнить одиночный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ð´ курÑором + + + + Execute all queries in editor + Выполнить вÑе запроÑÑ‹ в редакторе + + + + Execute "%1" query + Выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ "%1" + + + + Switch current working database to previous on the list + Переключить текущую базу данных на предыдущую в ÑпиÑке + + + + Switch current working database to next on the list + Переключить текущую базу данных на Ñледующую в ÑпиÑке + + + + Go to next editor tab + Перейти на Ñледующую вкладку редактора + + + + Go to previous editor tab + Перейти на предыдущую вкладку редактора + + + + Move keyboard input focus to the results view below + ПеремеÑтить Ñ„Ð¾ÐºÑƒÑ Ð²Ð²Ð¾Ð´Ð° в окно результатов внизу + + + + Move keyboard input focus to the SQL editor above + ПеремеÑтить Ñ„Ð¾ÐºÑƒÑ Ð²Ð²Ð¾Ð´Ð° в окно редактора SQL вверху + + + + Delete selected SQL history entries + Удалить выбранные запиÑи из иÑтории SQL-запроÑов + + + + Table window + Окно таблицы + + + + Commit the table structure + Подтвердить изменение Ñтруктуры таблицы + + + + Rollback pending changes in the table structure + Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² Ñтруктуре таблицы + + + + Refresh table structure + Обновить Ñтруктуру таблицы + + + + Add new column + Добавить новый Ñтолбец + + + + Edit selected column + Редактировать выбранный Ñтолбец + + + + Delete selected column + Удалить выбранный Ñтолбец + + + + Export table data + ЭкÑпортировать данные таблицы + + + + Import data to the table + Импортировать данные в таблицу + + + + Add new table constraint + Добавить новое ограничение на таблицу + + + + Edit selected table constraint + Редактировать выбранное ограничение на таблицу + + + + Delete selected table constraint + Удалить выбранное ограничение на таблицу + + + + Refresh table index list + Обновить ÑпиÑок индекÑов таблицы + + + + Add new index + Добавить новый Ð¸Ð½Ð´ÐµÐºÑ + + + + Edit selected index + Редактировать выбранный Ð¸Ð½Ð´ÐµÐºÑ + + + + Delete selected index + Удалить выбранный Ð¸Ð½Ð´ÐµÐºÑ + + + + Refresh table trigger list + Обновить ÑпиÑок триггеров таблицы + + + + + Add new trigger + Добавить новый триггер + + + + + Edit selected trigger + Редактировать выбранный триггер + + + + + Delete selected trigger + Удалить выбранный триггер + + + + + Go to next tab + Перейти к Ñледующей вкладке + + + + + Go to previous tab + Перейти к предыдущей вкладке + + + + A view window + Окно предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ + + + + Commit the view's query + Подтвердить изменение запроÑа в предÑтавлении + + + + Rollback pending changes in the view's query + Откатить изменение запроÑа в предÑтавлении + + + + Refresh view trigger list + Обновить ÑпиÑок индекÑов предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ + + + + Execute the view's query + Выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð² предÑтавлении + + + + A code snippets editor window + Окно редактора Ñниппетов + + + + + + + Commit the pending changes + Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ + + + + + + + Rollback the pending changes + Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ + + + + A collation editor window + Окно редактора Ñравнений + + + + A function editor window + Окно редактора функций + + + + A SQLite extension editor window + Окно редактора раÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ SQLite + + + + QuitConfirmDialog + + + Uncommitted changes + Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ + + + + Are you sure you want to quit the application? + +Following items are pending: + Ð’Ñ‹ дейÑтвительно хотите выйти из приложениÑ? + +Следующие Ñлементы ожидают подтверждениÑ: + + + + SearchTextDialog + + + Find or replace + Ðайти и заменить + + + + Find: + Ðайти: + + + + Case sensitive + Учитывать региÑтр + + + + Search backwards + ПоиÑк в обратном направлении + + + + Regular expression matching + ИÑпользовать регулÑрное выражение + + + + Replace && +find next + Заменить и найти далее + + + + Replace with: + Заменить на: + + + + Replace all + Заменить вÑÑ‘ + + + + Find + Ðайти + + + + SortDialog + + + Sort by columns + Сортировка по Ñтолбцам + + + + + Column + Столбец + + + + + Order + ПорÑдок + + + + Sort by: %1 + Сортировка по: %1 + + + + Move column up + ПеремеÑтить Ñтолбец вверх + + + + Move column down + ПеремеÑтить Ñтолбец вниз + + + + SqlEditor + + + Wrap words + sql editor + ÐŸÐµÑ€ÐµÐ½Ð¾Ñ Ð¿Ð¾ Ñловам + + + + Cut + sql editor + Вырезать + + + + Copy + sql editor + Копировать + + + + Paste + sql editor + Ð’Ñтавить + + + + Delete + sql editor + Удалить + + + + Select all + sql editor + Выделить вÑÑ‘ + + + + Undo + sql editor + Отменить + + + + Redo + sql editor + Повторить + + + + Complete + sql editor + Завершить + + + + Format SQL + sql editor + Форматировать SQL + + + + Save SQL to file + sql editor + Сохранить SQL в файл + + + + Select file to save SQL + sql editor + Выбрать файл Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ SQL + + + + Load SQL from file + sql editor + Загрузить SQL из файла + + + + Delete line + sql editor + Удалить Ñтрочку + + + + Move block down + sql editor + ПеремеÑтить блок вниз + + + + Move block up + sql editor + ПеремеÑтить блок вверх + + + + Copy block down + sql editor + Копировать блок вниз + + + + Copy up down + sql editor + Копировать блок вверх + + + + Find + sql editor + Ðайти + + + + Find next + sql editor + Ðайти далее + + + + Find previous + sql editor + Ðайти предыдущее + + + + Replace + sql editor + Заменить + + + + Toggle comment + sql editor + Комментировать/раÑкомментировать + + + + Increase font size + sql editor + Увеличить размер шрифта + + + + Decrease font size + sql editor + Уменьшить размер шрифта + + + + Could not open file '%1' for writing: %2 + Ðевозможно открыть файл '%1' Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи: %2 + + + + Saved SQL contents to file: %1 + SQL-код Ñохранён в файле %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Дополнение ÑинтакÑиÑа может быть иÑпользовано только поÑле Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð¹ базы данных редактору SQL. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Размер Ñодержимого редактора SQL Ñлишком велико, поÑтому обнаружение ошибок и подÑветка ÑущеÑтвующих объектов временно отключена. + + + + Save to file + Сохранить в файл + + + + SQL scripts (*.sql);;All files (*) + Скрипты SQL (*.sql);;Ð’Ñе файлы (*) + + + + Open file + Открыть файл + + + + Could not open file '%1' for reading: %2 + Ðевозможно открыть файл '%1' Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ: %2 + + + + Reached the end of document. Hit the find again to restart the search. + ДоÑтигнут конец документа. Ðажмите Ðайти Ñнова Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑка поиÑка. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Ошибка при подтверждении изменений: + + + + Column: + data view tooltip + Столбец: + + + + Data type: + data view + Тип данных: + + + + Table: + data view tooltip + Таблица: + + + + Constraints: + data view tooltip + ОграничениÑ: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Ðевозможно редактировать данную Ñчейку. ПодробноÑти: %1 + + + + The row is marked for deletion. + Строка помечена Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Структура Ñтой таблицы изменилаÑÑŒ Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° поÑледней загрузки данных. Перезагрузите данные Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Редактирование значений большой длины непоÑредÑтвенно в табличном режиме не рекомендуетÑÑ. Возможны проблемы Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñтью и удобÑтвом работы. Ð”Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ Ñ‚Ð°ÐºÐ¸Ð¼Ð¸ большими значениÑми рекомендуетÑÑ Ð¸Ñпользовать режим формы либо отдельное окно Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ (доÑтупно в контекÑтном меню по щелчку правой кнопкой мыши). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Внешний ключ Ð´Ð»Ñ Ñтолбца %2 имеет более чем %1 возможных значений. Это Ñлишком много Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð² выпадающем ÑпиÑке. Вам необходимо ввеÑти значение вручную. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Одновременно может быть выполнен только один запроÑ. + + + + Cannot execute query on undefined or invalid database. + Ðевозможно выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ðº неопознанной или некорректной базе данных. + + + + Cannot execute empty query. + Ðевозможно выполнить пуÑтой запроÑ. + + + + Uncommitted data + Ðеподтверждённые данные + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + ИмеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…. Ð’Ñ‹ дейÑтвительно хотите продолжить? Ð’Ñе неподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÑƒÑ‚ утерÑны. + + + + Cannot commit the data for a cell that refers to the already closed database. + Ðевозможно подтвердить данные Ð´Ð»Ñ Ñчейки, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÑÑылаетÑÑ Ð½Ð° уже закрытую базу данных. + + + + Could not begin transaction on the database. Details: %1 + Ðевозможно начать транзакцию в базе данных. ПодробноÑти: %1 + + + + An error occurred while committing the transaction: %1 + При завершении транзакции возникла ошибка: %1 + + + + An error occurred while rolling back the transaction: %1 + При отмене транзакции возникла ошибка: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Попытка Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… Ð´Ð»Ñ Ð½ÐµÑ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€ÑƒÐµÐ¼Ð¾Ð¹ Ñчейки (ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ñ‚ÐµÐ¼ не менее была изменена и ожидает подтверждениÑ). Это Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. ПожалуйÑта, отправьте о ней отчёт. + + + + An error occurred while committing the data: %1 + При подтверждении данных произошла ошибка: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + КоличеÑтво Ñтрок на Ñтранице было уменьшено до %1 из-за большого количеÑтва Ñтолбцов (%2) в окне данных. + + + + + Error while executing SQL query on database '%1': %2 + Ошибка при выполнении SQL запроÑа к базе данных '%1': %2 + + + + Error while loading query results: %1 + Ошибка при загрузке результатов запроÑа: %1 + + + + Insert multiple rows + Ð’Ñтавить неÑколько Ñтрок + + + + Number of rows to insert: + КоличеÑтво вÑтавлÑемых Ñтрок: + + + + Delete rows + Удалить Ñтроки + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + Ð’Ñ‹ ÑобираетеÑÑŒ удалить недавно вÑтавленные Ñтроки, которые ещё не были подтверждены. Ðомера Ñтрок: %1 +Такое удаление необратимо. Ð’Ñ‹ дейÑтвительно хотите удалить их? + + + + SqlQueryView + + + Go to referenced row in... + Перейти к ÑвÑзанной Ñтроке в... + + + + Copy + Копировать + + + + Copy with headers + Копировать Ñ Ð¸Ð¼ÐµÐ½Ð°Ð¼Ð¸ Ñтолбцов + + + + Copy as... + Копировать как... + + + + Paste + Ð’Ñтавить + + + + Paste as... + Ð’Ñтавить как... + + + + Set NULL values + УÑтановить Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð½Ð° NULL + + + + Erase values + Стереть Ñодержимое + + + + Commit + Подтвердить + + + + Rollback + Откатить + + + + Commit selected cells + Подтвердить выбранные Ñчейки + + + + Rollback selected cells + Откатить выбранные Ñчейки + + + + Edit current cell inline + Редактировать текущую Ñчейку + + + + Define columns to sort by + Определить Ñтолбцы Ð´Ð»Ñ Ñортировки + + + + Remove custom sorting + СброÑить указанную Ñортировку + + + + Insert row + Ð’Ñтавить Ñтроку + + + + Insert multiple rows + Ð’Ñтавить неÑколько Ñтрок + + + + Delete selected row + Удалить выбранную Ñтроку + + + + Adjust height of rows + ПодÑтроить выÑоту Ñтрок + + + + Increase font size + data view + Увеличить размер шрифта + + + + Decrease font size + data view + Уменьшить размер шрифта + + + + Invert selection + data view + Обратить выделение + + + + Edit value in editor + Править Ñодержимое в редакторе + + + + Show value in a viewer + Показать значение в проÑмотрщике + + + + Generate query for selected cells + Сгенерировать Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ñчеек + + + + No items selected to paste clipboard contents to. + Ðе выбраны Ñлементы Ð´Ð»Ñ Ð²Ñтавки в них Ñодержимого буфера обмена. + + + + Cannot paste data. Details: %1 + Ðевозможно вÑтавить данные. ПодробноÑти: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Структура по крайней мере одной иÑпользуемой таблицы изменилаÑÑŒ Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° поÑледней загрузки данных. Перезагрузите данные, чтобы продолжить работу. + + + + Cannot paste to a cell. Details: %1 + Ðевозможно вÑтавить в Ñчейку. ПодробноÑти: %1 + + + + The row is marked for deletion. + Строка помечена Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ. + + + + Cannot paste to column %1. Details: %2 + Ðевозможно вÑтавить в Ñтолбец %1. ПодробноÑти: %2 + + + + Go to referenced row in table '%1' + Перейти к ÑвÑзанной Ñтроке в таблице '%1' + + + + table '%1' + таблица '%1' + + + + Referenced row (%1) + СвÑÐ·Ð°Ð½Ð½Ð°Ñ Ñтрока (%1) + + + + Trim pasted text? + Обрезать вÑтавленный текÑÑ‚? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + Ð’ начале либо конце вÑтавленного текÑта находÑÑ‚ÑÑ Ð½ÐµÐ¿ÐµÑ‡Ð°Ñ‚Ð°ÐµÐ¼Ñ‹Ðµ Ñимволы. Обрезать их автоматичеÑки? + + + + Paste "NULL" as null value? + Ð’Ñтавить "NULL" в качеÑтве Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + Ð’Ñтавленный текÑÑ‚ Ñодержит Ñтроки "NULL". Ð’Ñ‹ хотите чтобы они были вÑтавлены как Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL? + + + + Edit value + Править Ñодержимое + + + + SqlTableModel + + + Error while committing new row: %1 + Ошибка при подтверждении новой Ñтроки: %1 + + + + Error while deleting row from table %1: %2 + Ошибка при удалении Ñтроки из таблицы %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Фильтр раÑширений + + + + Leave empty to use default function + ОÑтавьте пуÑтым Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ по умолчанию + + + + Extension file + Файл раÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ + + + + Initialization function + Ð˜Ð½Ð¸Ñ†Ð¸Ð°Ð»Ð¸Ð·Ð¸Ñ€ÑƒÑŽÑ‰Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ + + + + Databases + Базы данных + + + + Register in all databases + ЗарегиÑтрировать во вÑех базах данных + + + + Register in following databases: + ЗарегиÑтрировать в Ñледующих базах данных: + + + + Extension manager window has uncommitted modifications. + Ð’ менеджере раÑширений имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ изменениÑ. + + + + Extension manager + Менеджер раÑширений + + + + Commit all extension changes + Подтвердить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ€Ð°Ñширений + + + + Rollback all extension changes + Откатить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ€Ð°Ñширений + + + + Add new extension + Добавить новое раÑширение + + + + Remove selected extension + Удалить выбранное раÑширение + + + + Editing extensions manual + РуководÑтво по редактированию раÑширений + + + + File with given path does not exist or is not readable. + Файл по указанному пути не ÑущеÑтвует или не читаетÑÑ. + + + + Unable to load extension: %1 + Ðевозможно загрузить раÑширение: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Ðекорректное Ð¸Ð¼Ñ Ð¸Ð½Ð¸Ñ†Ð¸Ð°Ð»Ð¸Ð·Ð¸Ñ€ÑƒÑŽÑ‰ÐµÐ¹ функции. Ð˜Ð¼Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ может ÑоÑтоÑть только из английÑких букв, цифр и подчёркиваниÑ. + + + + Dynamic link libraries (*.dll);;All files (*) + ДинамичеÑки подключаемые библиотеки (*.dll);;Ð’Ñе файлы (*) + + + + Shared objects (*.so);;All files (*) + Общие объекты (*.so);;Ð’Ñе файлы (*) + + + + Dynamic libraries (*.dylib);;All files (*) + ДинамичеÑкие библиотеки (*.dylib);;Ð’Ñе файлы (*) + + + + All files (*) + Ð’Ñе файлы (*) + + + + Open file + Открыть файл + + + + StatusField + + + Status + Ð¡Ñ‚Ð°Ñ‚ÑƒÑ + + + + Copy + Копировать + + + + Clear + ОчиÑтить + + + + TableConstraintsModel + + + Type + table constraints + Тип + + + + Details + table constraints + ПодробноÑти + + + + Name + table constraints + Ð˜Ð¼Ñ + + + + TableForeignKeyPanel + + + Foreign table: + ВнешнÑÑ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°: + + + + Columns + Столбцы + + + + Local column + Локальный Ñтолбец + + + + Foreign column + Внешний Ñтолбец + + + + Reactions + ДейÑÑ‚Ð²Ð¸Ñ + + + + Deferred foreign key + Отложенный внешний ключ + + + + Named constraint + Именованное ограничение + + + + Constraint name + Ð˜Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ + + + + Pick the foreign column. + Выберите внешний Ñтолбец. + + + + Pick the foreign table. + Выберите внешнюю таблицу. + + + + Select at least one foreign column. + Выберите Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один внешний Ñтолбец. + + + + Enter a name of the constraint. + Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. + + + + Foreign column + table constraints + Внешний Ñтолбец + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Столбцы + + + + Column + Столбец + + + + Collation + Сравнение + + + + Sort + Сортировка + + + + Valid only for a single column with INTEGER data type + Применимо только к одному Ñтолбцу типа INTEGER + + + + Autoincrement + Ðвтоинкремент + + + + Named constraint + Именованное ограничение + + + + Constraint name + Ð˜Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ + + + + On conflict + При конфликте + + + + Collate + table constraints + Сравнение + + + + Sort order + table constraints + ПорÑдок Ñортировки + + + + Select at least one column. + Выберите Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один Ñтолбец. + + + + Enter a name of the constraint. + Введите Ð¸Ð¼Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ. + + + + TableStructureModel + + + Name + table structure columns + Ð˜Ð¼Ñ + + + + Data type + table structure columns + Тип данных + + + + Primary +Key + table structure columns + Первичный +ключ + + + + Foreign +Key + table structure columns + Внешний +ключ + + + + Unique + table structure columns + УникальноÑть + + + + Check + table structure columns + Проверка + + + + Not +NULL + table structure columns + Ðе +NULL + + + + Collate + table structure columns + Сравнение + + + + Generated + table structure columns + Сгенерированное + + + + Default value + table structure columns + Значение по умолчанию + + + + TableWindow + + + Structure + Структура + + + + Table name: + Ð˜Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>ДобавлÑет/удалÑет признак WITHOUT ROWID у таблицы. Ð¢Ð°ÐºÐ°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° больше не будет Ñодержать Ñкрытый Ñтолбец &quot;rowid&quot;. Ð”Ð»Ñ Ñ‚Ð°ÐºÐ¾Ð¹ таблицы обÑзательно наличие Ñтолбца Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°ÐºÐ¾Ð¼ PRIMARY KEY. ПодробноÑти можно узнать в официальной документации по SQLite.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>ДобавлÑет/удалÑет признак STRICT у таблицы. Ð’ такой таблице Ñтрого ÑоблюдаетÑÑ ÑоответÑтвие типа данных, хранимых в Ñтолбцах, и указанного Ð´Ð»Ñ Ñтих Ñтолбцов типа. Такой подход обычно применÑетÑÑ Ð² большинÑтве других движков баз данных. Отключите Ñтот признак Ð´Ð»Ñ ÐºÐ»Ð°ÑÑичеÑкого Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ñ SQLite (Ñ‚.е. без Ñтрогой типизации данных). ПодробноÑти можно узнать в официальной документации по SQLite.</p></body></html> + + + + + Data + Данные + + + + Constraints + ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ + + + + Indexes + ИндекÑÑ‹ + + + + Triggers + Триггеры + + + + DDL + DDL + + + + Export table + table window + ЭкÑпортировать таблицу + + + + Import data to table + table window + Импортировать данные в таблицу + + + + Populate table + table window + Заполнить таблицу + + + + Refresh structure + table window + Обновить Ñтруктуру + + + + Commit structure changes + table window + Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры + + + + Rollback structure changes + table window + Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры + + + + Add column + table window + Добавить Ñтолбец + + + + Edit column + table window + Редактировать Ñтолбец + + + + + Delete column + table window + Удалить Ñтолбец + + + + Move column up + table window + ПеремеÑтить Ñтолбец вверх + + + + Move column down + table window + ПеремеÑтить Ñтолбец вниз + + + + Create similar table + table window + Создать подобную таблицу + + + + Reset autoincrement value + table window + СброÑить Ñчётчик автоинкремента + + + + Add table constraint + table window + Добавить ограничение на таблицу + + + + Edit table constraint + table window + Редактировать ограничение на таблицу + + + + Delete table constraint + table window + Удалить ограничение на таблицу + + + + Move table constraint up + table window + ПеремеÑтить ограничение на таблицу вверх + + + + Move table constraint down + table window + ПеремеÑтить ограничение на таблицу вниз + + + + Add table primary key + table window + Добавить первичный ключ таблицы + + + + Add table foreign key + table window + Добавить внешний ключ таблицы + + + + Add table unique constraint + table window + Добавить табличное ограничение на уникальноÑть + + + + Add table check constraint + table window + Добавить проверочное ограничение на таблицу + + + + Refresh index list + table window + Обновить ÑпиÑок индекÑов + + + + + Create index + table window + Создать Ð¸Ð½Ð´ÐµÐºÑ + + + + Edit index + table window + Редактировать Ð¸Ð½Ð´ÐµÐºÑ + + + + Delete index + table window + Удалить Ð¸Ð½Ð´ÐµÐºÑ + + + + Refresh trigger list + table window + Обновить ÑпиÑок триггеров + + + + + Create trigger + table window + Создать триггер + + + + Edit trigger + table window + Редактировать триггер + + + + Delete trigger + table window + Удалить триггер + + + + Are you sure you want to delete column '%1'? + table window + Ð’Ñ‹ дейÑтвительно хотите удалить Ñтолбец '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + При изменении таблицы возникнут нижеуказанные проблемы. +Ð’Ñ‹ хотите продолжить? + + + + Table modification + table window + Изменение таблицы + + + + Could not load data for table %1. Error details: %2 + Ðевозможно загрузить данные таблицы %1. ПодробноÑти ошибки: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Ðе удалоÑÑŒ корректно обработать таблицу %1. Ðевозможно открыть окно таблицы. + + + + Database + База данных + + + + Could not restore window %1, because no database or table was stored in session for this window. + Ðевозможно воÑÑтановить окно %1, так как в ÑеÑÑии Ð´Ð»Ñ Ñтого окна не была Ñохранена база данных или таблица. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Ðевозможно воÑÑтановить окно '%1', так как в ÑеÑÑии Ð´Ð»Ñ Ñтого окна не была Ñохранена база данных или таблица. + + + + Could not restore window '%1', because database %2 could not be resolved. + Ðевозможно воÑÑтановить окно '%1', так как невозможно определить базу данных %2. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Ðевозможно воÑÑтановить окно '%1', так как таблица %2 не ÑущеÑтвует в базе данных %3. + + + + + New table %1 + ÐÐ¾Ð²Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° %1 + + + + Committed changes for table '%1' successfully. + Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² таблицу '%1' внеÑены уÑпешно. + + + + Committed changes for table '%1' (named before '%2') successfully. + Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² таблицу '%1' (предыдущее название '%2') внеÑены уÑпешно. + + + + Could not commit table structure. Error message: %1 + table window + Ðевозможно подтвердить Ñтруктуру таблицы. Сообщение об ошибке: %1 + + + + Reset autoincrement + Ð¡Ð±Ñ€Ð¾Ñ Ñчётчика автоинкремента + + + + Are you sure you want to reset autoincrement value for table '%1'? + Ð’Ñ‹ дейÑтвительно хотите ÑброÑить Ñчётчик автоинкремента у таблицы '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + При попытке ÑброÑа Ñчётчика автоинкремента у таблицы '%1' произошла ошибка: %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Ð¡Ð±Ñ€Ð¾Ñ Ñчётчика автоинкремента у таблицы '%1' уÑпешно выполнен. + + + + Empty name + ПуÑтое Ð¸Ð¼Ñ + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + SQLite позволÑет таблице иметь пуÑтое имÑ, Ñ…Ð¾Ñ‚Ñ Ñто не рекомендуетÑÑ. +Ð’Ñ‹ дейÑтвительно хотите Ñоздать таблицу Ñ Ð¿ÑƒÑтым именем? + + + + Cannot create a table without at least one column. + Ðевозможно Ñоздать таблицу без Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ одного Ñтолбца. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Ðевозможно Ñоздать таблицу %1, еÑли не определён первичный ключ. Отключите %2, либо определите первичный ключ. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Ðевозможно иÑпользовать автоинкремент первичного ключа при иÑпользовании оператора %1. Отключите либо %2, либо автоинкремент первичного ключа. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Следующие Ñтолбцы имеют неÑтрогий тип данных: %1. Либо отключите Ñтрогий режим у таблицы, либо иÑправьте типы данных Ñтолбцов. ДопуÑтимы Ñледующие Ñтрогие типы данных: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Ð’Ñ‹ дейÑтвительно хотите удалить ограничение на таблицу '%1'? + + + + Delete constraint + table window + Удалить ограничение + + + + Cannot export, because no export plugin is loaded. + Ðевозможно произвеÑти ÑкÑпорт, Ñ‚. к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ ÑкÑпорта. + + + + Cannot import, because no import plugin is loaded. + Ðевозможно произвеÑти импорт, Ñ‚. к. не загружено ни одного Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð°. + + + + Uncommitted changes + Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + ИмеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. Ðевозможно проÑматривать или редактировать данные, пока Ñтруктура таблицы не подтверждена. +Подтвердить Ñтруктуру таблицы или вернутьÑÑ Ð½Ð° вкладку Ñтруктуры? + + + + Go back to structure tab + ВернутьÑÑ Ð½Ð° вкладку Ñтруктуры + + + + Commit modifications and browse data. + Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸ перейти к данным. + + + + Name + table window indexes + Ð˜Ð¼Ñ + + + + Unique + table window indexes + УникальноÑть + + + + Columns + table window indexes + Столбцы + + + + Partial index condition + table window indexes + УÑловие Ð´Ð»Ñ Ñ‡Ð°Ñтичного индекÑа + + + + Name + table window triggers + Ð˜Ð¼Ñ + + + + Event + table window triggers + Событие + + + + Condition + table window triggers + УÑловие + + + + Details + table window triggers + ПодробноÑти + + + + Table window "%1" has uncommitted structure modifications and data. + Ð’ окне таблицы "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры и данных. + + + + Table window "%1" has uncommitted data. + Ð’ окне таблицы "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…. + + + + Table window "%1" has uncommitted structure modifications. + Ð’ окне таблицы "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. + + + + TriggerColumnsDialog + + + Trigger columns + Столбцы триггера + + + + Triggering columns: + Столбцы, вызывающие триггер: + + + + Select all + Выделить вÑÑ‘ + + + + Deselect all + СнÑть выделение + + + + TriggerDialog + + + + Trigger + Триггер + + + + On table: + Ð”Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹: + + + + Action: + ДейÑтвие: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL-уÑловие Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸ перед запуÑком кода триггера. ЕÑли уÑловие не выполнено, Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ Ñтроки триггер вызван не будет.</p> + + + + Pre-condition: + Предварительное уÑловие: + + + + The scope is still not fully supported by the SQLite database. + База данных SQLite пока не полноÑтью поддерживает облаÑть дейÑтвиÑ. + + + + Trigger name: + Ð˜Ð¼Ñ Ñ‚Ñ€Ð¸Ð³Ð³ÐµÑ€Ð°: + + + + When: + Когда: + + + + List of columns for UPDATE OF action. + СпиÑок Ñтолбцов Ð´Ð»Ñ Ð´ÐµÐ¹ÑÑ‚Ð²Ð¸Ñ UPDATE OF. + + + + Scope: + ОблаÑть дейÑтвиÑ: + + + + Code: + Код: + + + + Trigger statements to be executed. + ВыполнÑемые конÑтрукции триггера. + + + + DDL + DDL + + + + On view: + Ð”Ð»Ñ Ð¿Ñ€ÐµÐ´ÑтавлениÑ: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Ðе удалоÑÑŒ корректно обработать триггер %1. Ðевозможно открыть окно триггера. + + + + Enter a valid condition. + Введите корректное уÑловие. + + + + Enter a valid trigger code. + Введите корректный код триггера. + + + + Error + trigger dialog + Ошибка + + + + An error occurred while executing SQL statements: +%1 + При выполнении конÑтрукций SQL произошла ошибка: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + ÐšÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð°Ñ†Ð¸Ñ Ð²ÐµÑ€Ñии базы данных + + + + Following changes to the SQL statements will be made: + Ð’ конÑтрукции SQL будут внеÑены Ñледующие изменениÑ: + + + + Before + До + + + + After + ПоÑле + + + + ViewWindow + + + Query + Ð—Ð°Ð¿Ñ€Ð¾Ñ + + + + View name: + Ð˜Ð¼Ñ Ð¿Ñ€ÐµÐ´ÑтавлениÑ: + + + + Output column names + Отображаемые имена Ñтолбцов + + + + + Data + Данные + + + + Triggers + Триггеры + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Ðевозможно воÑÑтановить окно '%1', так как в ÑеÑÑии Ð´Ð»Ñ Ñтого окна не была Ñохранена база данных или предÑтавление. + + + + Could not restore window '%1', because database %2 could not be resolved. + Ðевозможно воÑÑтановить окно '%1', так как невозможно определить базу данных %2. + + + + Could not restore window '%1', because database %2 could not be open. + Ðевозможно воÑÑтановить окно '%1', так как невозможно открыть базу данных %2. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Ðевозможно воÑÑтановить окно '%1', так как предÑтавление %2 не ÑущеÑтвует в базе данных %3. + + + + + New view %1 + Ðовое предÑтавление %1 + + + + Database + База данных + + + + Refresh the view + view window + Обновить предÑтавление + + + + Commit the view changes + view window + Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ + + + + Rollback the view changes + view window + Откатить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ + + + + Explicit column names + Явные имена Ñтолбцов + + + + Generate output column names automatically basing on result columns of the view. + Сгенерировать отображаемые имена Ñтолбцов на оÑнове результирующих Ñтолбцов предÑтавлениÑ. + + + + Add column + view window + Добавить Ñтолбец + + + + Edit column + view window + Редактировать Ñтолбец + + + + Delete column + view window + Удалить Ñтолбец + + + + Move column up + view window + ПеремеÑтить Ñтолбец вверх + + + + Move column down + view window + ПеремеÑтить Ñтолбец вниз + + + + Refresh trigger list + view window + Обновить ÑпиÑок триггеров + + + + Create new trigger + view window + Создать новый триггер + + + + Edit selected trigger + view window + Редактировать выбранный триггер + + + + Delete selected trigger + view window + Удалить выбранный триггер + + + + View window "%1" has uncommitted structure modifications and data. + Ð’ окне предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры и данных. + + + + View window "%1" has uncommitted data. + Ð’ окне предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…. + + + + View window "%1" has uncommitted structure modifications. + Ð’ окне предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ "%1" имеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. + + + + Could not load data for view %1. Error details: %2 + Ðевозможно загрузить данные предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ %1. ПодробноÑти ошибки: %2 + + + + Uncommitted changes + Ðеподтверждённые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + ИмеютÑÑ Ð½ÐµÐ¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ñ‘Ð½Ð½Ñ‹Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтруктуры. Ðевозможно проÑматривать или редактировать данные, пока Ñтруктура предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð½Ðµ подтверждена. +Подтвердить Ñтруктуру предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ вернутьÑÑ Ð½Ð° вкладку Ñтруктуры? + + + + Go back to structure tab + ВернутьÑÑ Ð½Ð° вкладку Ñтруктуры + + + + Commit modifications and browse data. + Подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸ перейти к данным. + + + + View '%1' was committed successfully. + ПредÑтавление '%1' уÑпешно подтверждено. + + + + Committed changes for view '%1' successfully. + Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² предÑтавление '%1' внеÑены уÑпешно. + + + + Committed changes for view '%1' (named before '%2') successfully. + Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² таблицу '%1' (предыдущее название '%2') внеÑены уÑпешно. + + + + Could not commit view changes. Error message: %1 + view window + Ðевозможно подтвердить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ñ€ÐµÐ´ÑтавлениÑ. Сообщение об ошибке: %1 + + + + Override columns + ПерезапиÑÑŒ Ñтолбцов + + + + Currently defined columns will be overriden. Do you want to continue? + Заданные Ñтолбцы будут перезапиÑаны. Ð’Ñ‹ хотите продолжить? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Ðевозможно определить Ñтолбцы, возвращаемые предÑтавлением. ВероÑтно Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½ÐµÐ¿Ð¾Ð»Ð¾Ð½ или Ñодержит ошибки. + + + + Name + view window triggers + Ð˜Ð¼Ñ + + + + Instead of + view window triggers + ВмеÑто + + + + Condition + view window triggers + УÑловие + + + + Details + table window triggers + ПодробноÑти + + + + Could not process the %1 view correctly. Unable to open a view window. + Ðе удалоÑÑŒ корректно обработать предÑтавление %1. Ðевозможно открыть окно предÑтавлениÑ. + + + + Empty name + ПуÑтое Ð¸Ð¼Ñ + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + SQLite позволÑет предÑтавлению иметь пуÑтое имÑ, Ñ…Ð¾Ñ‚Ñ Ñто не рекомендуетÑÑ. +Ð’Ñ‹ дейÑтвительно хотите Ñоздать предÑтавление Ñ Ð¿ÑƒÑтым именем? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + Ðевозможно проанализировать Ñтруктуру конÑтрукции SELECT. ПожалуйÑта, иÑправьте Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸ повторите попытку. +ПодробноÑти: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + Ðевозможно изменить предÑтавление из-за внутренней ошибки SQLiteStudio. ПожалуйÑта, Ñообщите о ней! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + Ðевозможно корректно проанализировать Ñтруктуру предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ. Это ошибка SQLiteStudio. ПожалуйÑта, Ñообщите о ней. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + При изменении предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð²Ð¾Ð·Ð½Ð¸ÐºÐ½ÑƒÑ‚ нижеуказанные проблемы. +Ð’Ñ‹ хотите продолжить? + + + + View modification + view window + Изменение предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ + + + + WidgetCover + + + Interrupt + Прервать + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.qm b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.qm deleted file mode 100644 index c3c6d5f..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.qm and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.ts deleted file mode 100644 index aeb372d..0000000 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.ts +++ /dev/null @@ -1,7262 +0,0 @@ - - - - - AboutDialog - - - About SQLiteStudio and licenses - O programe SQLiteStudio a licenciách - - - - About - O programe - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Slobodný, open-source, multi-platformový SQLite databázový manažér.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Autor a aktívny správca:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - Licenses - Licencie - - - - Environment - Prostredie - - - - Icon directories - Adresáre s ikonami - - - - Form directories - Adresáre so Å¡týlmi - - - - Plugin directories - Adresáre s pluginmi - - - - Application directory - Adresár aplikácie - - - - SQLite 3 version: - Verzia SQLite3: - - - - Configuration directory - Adresár s konfiguráciou - - - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - - - - - Qt version: - Verzia Qt: - - - - Portable distribution. - Prenosná distribúcia. - - - - MacOS X application boundle distribution. - MacOS X aplikaÄná distribúcia. - - - - Operating system managed distribution. - Distribúcia spravovaná operaÄným systémom. - - - - Copy - KopírovaÅ¥ - - - - <h3>Table of contents:</h3><ol>%2</ol> - <h3>Tabuľka obsahu:</h3><ol>%2</ol> - - - - BindParamsDialog - - - Query parameters - - - - - Please provide values for query parameters - - - - - BugDialog - - Bugs and ideas - Chyby a nápady - - - Reporter - Reportér - - - E-mail address - emailová adresa - - - Log in - Prihlásenie - - - Short description - Krátky popis - - - Detailed description - Detailný popis - - - Show more details - ZobraziÅ¥ viacej detailov - - - SQLiteStudio version - Verzia SQLiteStudio - - - Operating system - OperaÄný systém - - - Loaded plugins - NaÄítané pluginy - - - Send - OdoslaÅ¥ - - - You can see all your reported bugs and ideas by selecting menu '%1' and then '%2'. - VÅ¡etky svoje nahlásené chyby a nápady môžte vidieÅ¥ výberom '%1' a následne '%2'. - - - A bug report sent successfully. - Správa o chybe bola úspeÅ¡ne odoslaná. - - - An error occurred while sending a bug report: %1 -%2 - Vyskytol sa problém pri posielaní správy o chybe: %1 -%2 - - - You can retry sending. The contents will be restored when you open a report dialog after an error like this. - Môžte zopakovaÅ¥ odoslanie. KeÄ otvoríte nahlasovacie okno po takejto chybe, obsah bude obnovený. - - - An idea proposal sent successfully. - Nahlásenie prebehlo úspeÅ¡ne. - - - An error occurred while sending an idea proposal: %1 -%2 - Vyskytla sa chyba pri odoslieaní: %1 -%2 - - - A bug report - Nahlásenie chyby - - - Describe problem in few words - Popíšte problém niekoľkými slovami - - - Describe problem and how to reproduce it - Popíšte problém a spôsob ako ho reprodukovaÅ¥ - - - A new feature idea - Nová funkcia - - - A title for your idea - Titulok novej funkcie - - - Describe your idea in more details - DetailnejÅ¡ie popíšte svoj návrh na funkciu - - - Reporting as an unregistered user, using e-mail address. - Nahlásenie ako neregistrovaný používateľ pomocou emailovej adresy. - - - Reporting as a registered user. - Nahlásenie ako registrovaný používateľ. - - - Log out - OdhlásiÅ¥ sa - - - Providing true email address will make it possible to contact you regarding your report. To learn more, press 'help' button on the right side. - Zadaním reálnej emailovej adresy umožníte kontaktovaÅ¥ vás ohľadom vášho nahlásenia. Pre zistenie bližších detailov, kliknite na tlaÄitko 'i' na pravej strane okna. - - - Enter vaild e-mail address, or log in. - Zadajte platný email alebo sa prihláste. - - - Short description requires at least 10 characters, but not more than 100. Longer description can be entered in the field below. - Krátky popis by mal obsahovaÅ¥ minimálne 10 znakov ale nie viacej ako 100. Dlhší popis môžte zadaÅ¥ v poli nižšie. - - - Long description requires at least 30 characters. - Dlhší popis by mal obsahovaÅ¥ minimálne 30 znakov. - - - - BugReportHistoryWindow - - Title - Titulok - - - Reported at - Nahlásené - - - URL - URL - - - Reports history - História hlásení - - - Clear reports history - VymazaÅ¥ históriu hlásení - - - Delete selected entry - VymazaÅ¥ vybranú položku - - - Invalid response from server. - Neplatná odpoveÄ zo servera. - - - - BugReportLoginDialog - - Log in - Prihlásenie - - - Credentials - Prihlasovacie údaje - - - Login: - Meno: - - - Password: - Heslo: - - - Validation - Overenie - - - Validate - OveriÅ¥ - - - Validation result message - Výsledok overenia - - - Abort - ZruÅ¡iÅ¥ - - - A login must be at least 2 characters long. - meno musí maÅ¥ dĺžku minimálne 2 znaky. - - - A password must be at least 5 characters long. - Heslo musí maÅ¥ dĺžku minimálne 5 znakov. - - - Valid - ÚspeÅ¡né overenie - - - - CollationsEditor - - - Filter collations - FiltrovaÅ¥ porovnávania - - - - Collation name: - Názov porovnánavania: - - - - Implementation language: - ImplementaÄný jazyk: - - - - Databases - Databázy - - - - Register in all databases - RegistrovaÅ¥ vo vÅ¡etkých databázach - - - - Register in following databases: - RegistrovaÅ¥ v nasledujúcich databázach: - - - - Implementation code: - ImplementaÄný kód: - - - - Collations editor - Editor porovnávaní - - - - Commit all collation changes - PotvrdiÅ¥ vÅ¡etky zmeny v porovnávaní - - - - Rollback all collation changes - VrátiÅ¥ späť vÅ¡etky zmeny v porovnávaní - - - - Create new collation - VytvoriÅ¥ nové porovnávanie - - - - Delete selected collation - VymazaÅ¥ vybrané porovnávanie - - - - Editing collations manual - Manuál úpravy porovnávaní - - - - Enter a non-empty, unique name of the collation. - Zadajte jedineÄný názov porovnávania. - - - - Pick the implementation language. - Vyberte implementaÄný jazyk. - - - - Enter a non-empty implementation code. - Zadajte implementaÄný kód. - - - - Collations editor window has uncommitted modifications. - - - - Collations editor window has uncommited modifications. - Okno editora porovnávaní obsahuje nepotvrdené zmeny. - - - - ColorButton - - - Pick a color - Vyberte farbu - - - - ColumnCollatePanel - - - Collation name: - Názov porovnánavania: - - - - Named constraint: - Pomenovanie obmedzenia: - - - - Enter a name of the constraint. - Zadajte názov obmedzenia. - - - - Enter a collation name. - Zadajte názov porovnávania. - - - - ColumnDefaultPanel - - - Default value: - Prednastavená hodnota: - - - - Named constraint: - Pomenovanie obmedzenia: - - - - Enter a default value expression. - - - - - Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - - - - - Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - - - - - Enter a name of the constraint. - Zadajte názov obmedzenia. - - - - ColumnDialog - - - Column - Stĺpec - - - - Name and type - Názov a typ - - - - Scale - Rozsah - - - - Precision - PresnosÅ¥ - - - - Data type: - Datový typ: - - - - Column name: - Názov stĺpca: - - - - Size: - VeľkosÅ¥: - - - - Constraints - Obmedzenia - - - - Unique - JedineÄný - - - - - - - - - - Configure - KonfigurovaÅ¥ - - - - Foreign Key - Cudzí klÃºÄ - - - - Collate - Porovnanie - - - - Not NULL - Nie NULL - - - - Check condition - - - - - Primary Key - Primárny klÃºÄ - - - - Default - Prednastavená hodnota - - - - Advanced mode - Rozšírený mód - - - - Add constraint - column dialog - PridaÅ¥ obmedzenie - - - - Edit constraint - column dialog - UpraviÅ¥ obmedzenie - - - - - Delete constraint - column dialog - VymazaÅ¥ obmedzenie - - - - Move constraint up - column dialog - Posunúť obmedzenie hore - - - - Move constraint down - column dialog - Posunúť obmedzenie dole - - - - Add a primary key - column dialog - PridaÅ¥ primárny kÄ¾ÃºÄ - - - - Add a foreign key - column dialog - PridaÅ¥ cudzí kÄ¾ÃºÄ - - - - Add an unique constraint - column dialog - PridaÅ¥ jedineÄné obmedzenie - - - - Add a check constraint - column dialog - - - - - Add a not null constraint - column dialog - - - - - Add a collate constraint - column dialog - - - - - Add a default constraint - column dialog - - - - - Are you sure you want to delete constraint '%1'? - column dialog - Ste si istý, že chcete vymazaÅ¥ obmedzenie '%1'? - - - - Correct the constraint's configuration. - - - - - This constraint is not officially supported by SQLite 2, -but it's okay to use it. - - - - - Scale is not allowed for INTEGER PRIMARY KEY columns. - - - - - Precision cannot be defined without the scale. - - - - - Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - - - - - INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - - - - - Precision is not allowed for INTEGER PRIMARY KEY columns. - - - - - ColumnDialogConstraintsModel - - - Type - column dialog constraints - Typ - - - - Name - column dialog constraints - Názov - - - - Details - column dialog constraints - Detaily - - - - ColumnForeignKeyPanel - - - Foreign table: - Cudzia tabuľka: - - - - Foreign column: - Cudzí stĺpec: - - - - Reactions - Reakcie - - - - Deferred foreign key - - - - - Named constraint - Pomenovanie obmedzenia - - - - Constraint name - Názov obmedzenia - - - - Pick the foreign table. - Vyberte cudziu tabuľku. - - - - Pick the foreign column. - Vyberte cudzí stĺpec. - - - - Enter a name of the constraint. - Zadajte názov obmedzenia. - - - - ColumnPrimaryKeyPanel - - - Autoincrement - Autoikrement - - - - Sort order: - ZoradiÅ¥: - - - - Named constraint: - Pomenovanie obmedzenia: - - - - On conflict: - Pri konflikte: - - - - Enter a name of the constraint. - Zadajte názov obmedzenia. - - - Autoincrement (only for %1 type columns) - column primary key - Autoinkrement (iba pre stĺpec typu %1) - - - - ColumnUniqueAndNotNullPanel - - - Named constraint: - Pomenovanie obmedzenia: - - - - On conflict: - Pri konflikte: - - - - Enter a name of the constraint. - Zadajte názov obmedzenia. - - - - CompleterWindow - - - Column: %1 - completer statusbar - Stĺpec: %1 - - - - Table: %1 - completer statusbar - Tabuľka: %1 - - - - Index: %1 - completer statusbar - Index: %1 - - - - Trigger: %1 - completer statusbar - SpúšťaÄ: %1 - - - - View: %1 - completer statusbar - Pohľad: %1 - - - - Database: %1 - completer statusbar - Databáza: %1 - - - - Keyword: %1 - completer statusbar - KľúÄové slovo: %1 - - - - Function: %1 - completer statusbar - Funkcia: %1 - - - - Operator: %1 - completer statusbar - Operátor: %1 - - - - String - completer statusbar - ReÅ¥azec - - - - Number - completer statusbar - Číslo - - - - Binary data - completer statusbar - Binárne dáta - - - - Collation: %1 - completer statusbar - Porovnávanie: %1 - - - - Pragma function: %1 - completer statusbar - Pragma funkcia : %1 - - - - ConfigDialog - - - - Configuration - Konfigurácia - - - - Search - HľadaÅ¥ - - - - General - VÅ¡eobecné - - - - Keyboard shortcuts - Klávesové skratky - - - - Look & feel - Vzhľad - - - - Style - Å týl - - - - Fonts - Fonty - - - - Colors - Farby - - - - Plugins - Pluginy - - - - Code formatters - Formát kódu - - - - Data browsing - Prezeranie dát - - - - Data editors - Editory dát - - - - Database dialog window - Databázové dialógové okno - - - - <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> - <p>OznaÄením tejto možnosti sa vypne voľba "ZapamätaÅ¥ si databázu" v okne pridávania novej databázy</p> - - - - Do not mark database to be "permanent" by default - NepamätaÅ¥ si databázu - - - - <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> - - - - - Try to bypass dialog completly when dropping database file onto the list - - - - - Data browsing and editing - Prezeranie a úprava dát - - - - Number of data rows per page: - PoÄet data riadkov na stranu: - - - - - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - - - - - Limit initial data column width to (in pixels): - - - - - Keep NULL value when entering empty value - - - - - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> - - - - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - - - - - Number of memorized table populating configurations - - - - - Show column and row details tooltip in data view - - - - - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - - - - - <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> - - - - - Use DEFAULT value (if defined), when committing NULL value - - - - - Inserting new row in data grid - - - - - Before currently selected row - - - - - After currently selected row - - - - - At the end of data view - - - - - <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - - - - - <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - - - - - Place data tab as first tab in a Table Window - - - - - <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - - - - - <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - - - - - Place data tab as first tab in a View Window - - - - - Data types - Datové typy - - - - Available editors: - Dostupné editory: - - - - Editors selected for this data type: - - - - - Schema editing - Úprava schémy - - - - Number of DDL changes kept in history. - PoÄet DDL zmien uchovávaných v histórii. - - - - DDL history size: - VeľkosÅ¥ DDL histórie: - - - Don't show DDL preview dialog when commiting schema changes - NezobrazovaÅ¥ náhľad DDL pri potvrdzovaní zmien v schéme - - - - SQL queries - SQL dotazy - - - - - Number of queries kept in the history. - PoÄet dotazov uchovávaných v histórii. - - - - History size: - VeľkosÅ¥ SQL histórie: - - - - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> - <p>Ak je v SQL editore viacej ako jeden dotaz, potom(ak je táto voľba zapnutá) bude vykonaný iba jeden dotaz - ten, na ktorom je kurzor. InÃ¡Ä budú vykonané vÅ¡etky dotazy. Vždy si viete vybraÅ¥ ktoré dotazy budú vykonané a to ich výberom\oznaÄením.</p> - - - - Execute only the query under the cursor - VykonaÅ¥ len dotaz, na ktorom stojí kurzor - - - - Updates - Aktualizácie - - - - Automatically check for updates at startup - KontrolovaÅ¥ aktualizácie pri Å¡tarte - - - - Session - Sedenie - - - - Restore last session (active MDI windows) after startup - ObnoviÅ¥ posledné sedenie (aktívne okná) pri Å¡tarte - - - - Status Field - - - - - <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> - - - - - Always open Status panel when new message is printed - - - - - Filter shortcuts by name or key combination - FiltrovaÅ¥ podľa názvu alebo klávesovej skratky - - - - Action - Akcia - - - - Key combination - Klávesová skratka - - - - - Language - Jazyk - - - - Changing language requires application restart to take effect. - Je potrebné reÅ¡tartovaÅ¥ aplikáciu aby sa zmena jazyka prejavila. - - - - Compact layout - - - - - <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - - - - - Use compact layout - - - - - - Database list - Zoznam databáz - - - - If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. - - - - - Sort table columns alphabetically - ZoradiÅ¥ stĺpce tabuľky abecedne - - - - Expand tables node when connected to a database - RozbaliÅ¥ zoznam tabuliek po pripojení k databáze - - - - <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> - - - - - Display additional labels on the list - ZobraziÅ¥ doplnkové popisky v zozname - - - - For regular tables labels will show number of columns, indexes and triggers for each of tables. - - - - - Display labels for regular tables - ZobraziÅ¥ popisky pre regulárne tabuľky - - - - Virtual tables will be marked with a 'virtual' label. - - - - - Display labels for virtual tables - ZobraziÅ¥ popisky pre virtuálne tabuľky - - - - Expand views node when connected to a database - RozbaliÅ¥ zoznam pohľadov po pripojení k databáze - - - - If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) - - - - - Sort objects (tables, indexes, triggers and views) alphabetically - ZoradiÅ¥ objekty (tabuľky, indexy, spúšťaÄe a pohľady) abecedne - - - - Display system tables and indexes on the list - ZobraziÅ¥ systémové tabuľky a indexy v zozname - - - - Table windows - Okná tabuľky - - - When enabled, Table Windows will show up with the data tab, instead of the structure tab. - Ak je táto možnosÅ¥ zaÅ¡krtnutá, tak sa v okne zobrazia dáta a nie Å¡truktúra tabuľky. - - - - Open Table Windows with the data tab for start - ZobraziÅ¥ dáta po otvorení tabuľky - - - - View windows - Okná pohľadov - - - When enabled, View Windows will show up with the data tab, instead of the structure tab. - Ak je táto možnosÅ¥ zaÅ¡krtnutá, tak sa v okne zobrazia dáta a nie SQL dotaz. - - - - Open View Windows with the data tab for start - ZobraziÅ¥ dáta po otvorení pohľadu - - - - Don't show DDL preview dialog when committing schema changes - - - - - - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - - - - - Number of memorized query parameters - - - - - Main window dock areas - - - - - Left and right areas occupy corners - - - - - Top and bottom areas occupy corners - - - - - Hide built-in plugins - NezobrazovaÅ¥ interné pluginy - - - - Current style: - Aktuálny Å¡týl: - - - - Preview - Náhľad - - - - Enabled - Zapnutý - - - - Disabled - Vypnutý - - - - Active formatter plugin - Aktívny formátovací plugin - - - - SQL editor font - Písmo SQL editora - - - - Database list font - Font zoznamu databáz - - - - Database list additional label font - Font doplnkového popisku - - - - Data view font - Font dát - - - - Status field font - Font status okna - - - - SQL editor colors - Farby SQL editora - - - - Current line background - - - - - <p>SQL strings are enclosed with single quote characters.</p> - - - - - String foreground - - - - - <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - - - - - Bind parameter foreground - - - - - Highlighted parenthesis background - - - - - <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - - - - - BLOB value foreground - Farba BLOB hodnoty - - - - Regular foreground - - - - - Line numbers area background - - - - - Keyword foreground - - - - - Number foreground - - - - - Comment foreground - - - - - <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> - - - - - Valid objects foreground - - - - - Data view colors - Farby dát - - - - <p>Any data changes will be outlined with this color, until they're committed to the database.</p> - - - - - Uncommitted data outline color - - - - - <p>In case of error while committing data changes, the problematic cell will be outlined with this color.</p> - - - - <p>Any data changes will be outlined with this color, until they're commited to the database.</p> - <p>VÅ¡etky zmeny dát budú ohraniÄené touto farbou, dokiaľ nebudú potvrdené.</p> - - - Uncommited data outline color - Farba rámÄeka nepotvrdených dát - - - <p>In case of error while commiting data changes, the problematic cell will be outlined with this color.</p> - <p>V prípade chyby pri potvrdzovaní zmien dát, budú problematické bunky ohraniÄené touto farbou.</p> - - - - Commit error outline color - Farba rámÄeka s chybou potvrdenia dát - - - - NULL value foreground - Farba NULL hodnoty - - - - Deleted row background - Pozadie vymazaného riadka - - - - Database list colors - Farby zoznamu databáz - - - - <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> - - - - - Additional labels foreground - Farba doplnkového popisku - - - - Status field colors - Farby status okna - - - - Information message foreground - Farba informaÄnej správy - - - - Warning message foreground - Farba upozornenia - - - - Error message foreground - Farba chybovej správy - - - - Description: - plugin details - Popis: - - - - Category: - plugin details - Kategória: - - - - Version: - plugin details - Verzia: - - - - Author: - plugin details - Autor: - - - - Internal name: - plugin details - Interný názov: - - - - Dependencies: - plugin details - Závislosti: - - - - Conflicts: - plugin details - Konflikty: - - - - Plugin details - Detaily pluginu - - - - Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. - Pluginy sú naÄítané/uvoľnené okamžite pri zaÅ¡krtnutí/odÅ¡krtnutí ale midifikovaný zoznam pluginov naÄítaných pri Å¡tarte nieje uložený až kým nepotvrdíte celý konfiguraÄný dialóg. - - - - %1 (built-in) - plugins manager in configuration dialog - %1 (interný) - - - - Details - Detaily - - - - No plugins in this category. - - - - - Add new data type - PridaÅ¥ nový datový typ - - - - Rename selected data type - PremenovaÅ¥ vybraný datový typ - - - - Delete selected data type - VymazaÅ¥ vybraný datový typ - - - - Help for configuring data type editors - - - - - ConstraintCheckPanel - - - The condition - Podmienka - - - - Named constraint: - Pomenovanie obmedzenia: - - - - On conflict - Pri konflikte - - - - Enter a valid condition. - Zadajte platnú podmienku. - - - - Enter a name of the constraint. - Zadajte názov obmedzenia. - - - - ConstraintDialog - - - New constraint - constraint dialog - Nové obmedzenie - - - - Create - constraint dialog - VytvoriÅ¥ - - - - Edit constraint - dialog window - UpraviÅ¥ obmedzenie - - - - Apply - constraint dialog - AplikovaÅ¥ - - - - Primary key - table constraints - Primárny klÃºÄ - - - - Foreign key - table constraints - Cudzí klÃºÄ - - - - Unique - table constraints - JedineÄnosÅ¥ - - - - Not NULL - table constraints - Nie NULL - - - - Check - table constraints - - - - - Collate - table constraints - Porovnanie - - - - Default - table constraints - Prednastavená hodnota - - - - ConstraintTabModel - - - Table - table constraints - Tabuľka - - - - Column (%1) - table constraints - Stĺpec (%1) - - - - Scope - table constraints - - - - - Type - table constraints - Typ - - - - Details - table constraints - Detaily - - - - Name - table constraints - Názov - - - - CssDebugDialog - - - SQLiteStudio CSS console - - - - - DataView - - - Filter data - data view - FiltrovaÅ¥ - - - - Grid view - Tabuľkové zobrazenie - - - - Form view - Formulárové zobrazenie - - - - Refresh table data - data view - ObnoviÅ¥ dáta v tabuľke - - - - First page - data view - Prvá strana - - - - Previous page - data view - Predchádzajúca strana - - - - Next page - data view - Nasledujúca strana - - - - Last page - data view - Posledná strana - - - - Filter - - - - - Hit Enter key or press "Apply filter" button on toolbar to apply new value. - - - - - Show filter inputs per column - data view - - - - - Apply filter - data view - AplikovaÅ¥ filter - - - - Commit changes for selected cells - data view - PotvrdiÅ¥ zmeny pre vybrané bunky - - - - Rollback changes for selected cells - data view - VrátiÅ¥ späť zmeny pre vybrané bunky - - - - Show grid view of results - sql editor - Výsledky zobraziÅ¥ v tabuľke - - - - Show form view of results - sql editor - Výsledky zobraziÅ¥ vo formulári - - - - Filter by text - data view - FiltrovaÅ¥ pomocou textu - - - - Filter by the Regular Expression - data view - FiltrovaÅ¥ pomocou regulárneho výrazu - - - - Filter by SQL expression - data view - FiltrovaÅ¥ pomocou SQL výrazu - - - - Tabs on top - data view - Záložky hore - - - - Tabs at bottom - data view - Záložky dole - - - - Place new rows above selected row - data view - - - - - Place new rows below selected row - data view - - - - - Place new rows at the end of the data view - data view - - - - - Total number of rows is being counted. -Browsing other pages will be possible after the row counting is done. - SpoÄítava sa celkový poÄet riadkov. -Prezeranie Äalších strán bude možné až po dokonÄení spoÄítavania. - - - - Row: %1 - Riadok:%1 - - - - DbConverterDialog - - - Convert database - KonvertovaÅ¥ databázu - - - - Source database - Zdrojová databáza - - - - Source database version: - Verzia zdrojovej databázy: - - - - Target database - Cieľová databáza - - - - Target version: - Cieľová databáza: - - - - This is the file that will be created as a result of the conversion. - - - - - Target file: - Cieľový súbor: - - - - Name of the new database: - Názov novej databázy: - - - - This is the name that the converted database will be added to SQLiteStudio with. - - - - - Select source database - - - - - Enter valid and writable file path. - - - - - Entered file exists and will be overwritten. - - - - - Enter a not empty, unique name (as in the list of databases on the left). - Zadajte jedineÄný názov. - - - - No valid target dialect available. Conversion not possible. - - - - - Select valid target dialect. - - - - - Database %1 has been successfully converted and now is available under new name: %2 - - - - - SQL statements conversion - - - - - Following error occurred while converting SQL statements to the target SQLite version: - - - - - Would you like to ignore those errors and proceed? - - - - - DbDialog - - - Database - Databáza - - - - Database type - Typ databázy - - - - Database driver - Databázový ovládaÄ - - - Generate automatically - GenerovaÅ¥ automaticky - - - - Options - Voľby - - - - Permanent (keep it in configuration) - ZapamätaÅ¥ si databázu - - - - Test connection - Test spojenia - - - Name - Názov - - - Type - Typ - - - Browse for database file on local computer - HľadaÅ¥ databázový súbor na lokálnom poÄítaÄi - - - - Create new database file - VytvoriÅ¥ nový databázový súbor - - - - - File - Súbor - - - - Name (on the list) - Názov (v zozname) - - - Generate name basing on file path - GenerovaÅ¥ názov na základe cesty k súboru - - - Permanent - UložiÅ¥ natrvalo - - - - <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> - aasfd - <p>Databáza sa uloží do konfiguraÄného súboru a bude obnovená pri každom spustení SQLiteStudia.</p> - - - Test database connection - OtestovaÅ¥ spojenie s databázou - - - - Browse for existing database file on local computer - HľadaÅ¥ databázový súbor na lokálnom poÄítaÄi - - - - Browse - PrehľadávaÅ¥ - - - - Enter an unique database name. - Zadajte názov databázy. - - - - This name is already in use. Please enter unique name. - Tento názov už existuje. Prosím zadajte iný názov. - - - - <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - - - - - Enter a database file path. - Zadajte cestu k databázovému súboru. - - - - This database is already on the list under name: %1 - Táto databáza už je v zozname pod názvom: %1 - - - - Select a database type. - Vyberte typ databázy. - - - Auto-generated - Automaticky vygenerovaný - - - The name will be auto-generated - Názov bude vygenerovaný automaticky - - - Type the name - Zadajte meno - - - - DbObjectDialogs - - - Delete table - VymazaÅ¥ tabuľku - - - - Are you sure you want to delete table %1? - Ste si istý, že chcete vymazaÅ¥ tabuľku %1? - - - - Delete index - VymazaÅ¥ index - - - - Are you sure you want to delete index %1? - Ste si istý, že chcete vymazaÅ¥ index %1? - - - - Delete trigger - VymazaÅ¥ spúšťaÄ - - - - Are you sure you want to delete trigger %1? - Ste si istý, že chcete vymazaÅ¥ spúšťaÄ %1? - - - - Delete view - VymazaÅ¥ pohľad - - - - Are you sure you want to delete view %1? - Ste si istý, že chcete vymazaÅ¥ pohľad %1? - - - - - - Error while dropping %1: %2 - Vyskystla sa chyba poÄas mazania %1: %2 - - - - Delete objects - Odstránenie objektov - - - - Are you sure you want to delete following objects: -%1 - - - - - Cannot start transaction. Details: %1 - - - - - Cannot commit transaction. Details: %1 - - - - - DbTree - - - Databases - Databázy - - - - Filter by name - FiltrovaÅ¥ podľa názvu - - - - Copy - KopírovaÅ¥ - - - - Paste - VložiÅ¥ - - - - Select all - VybraÅ¥ vÅ¡etko - - - - Create a group - VytvoriÅ¥ skupinu - - - - Delete the group - VymazaÅ¥ skupinu - - - - Rename the group - PremenovaÅ¥ skupinu - - - Add a database - PridaÅ¥ databázu - - - Edit the database - UpraviÅ¥ databázu - - - Remove the database - OdstrániÅ¥ databázu - - - Connect to the database - PripojiÅ¥ sa k databáze - - - Disconnect from the database - OdpojiÅ¥ sa od databázy - - - - Import - ImportovaÅ¥ - - - Export the database - ExportovaÅ¥ databázu - - - Convert database type - KonvertovaÅ¥ databázu - - - Vacuum - Vacuum - - - Integrity check - Kontrola integrity - - - Create a table - VytvoriÅ¥ tabuľku - - - Edit the table - UpraviÅ¥ tabuľku - - - Delete the table - VymazaÅ¥ tabuľku - - - - Export the table - ExportovaÅ¥ tabuľku - - - - Import into the table - ImportovaÅ¥ do tabuľky - - - - Populate table - NaplniÅ¥ tabuľku - - - - Create similar table - VytvoriÅ¥ rovnakú tabuľku - - - - Reset autoincrement sequence - ResetovaÅ¥ sekvenciu autoinkrementu - - - Create an index - VytvoriÅ¥ index - - - Edit the index - UpraviÅ¥ index - - - Delete the index - VymazaÅ¥ index - - - Create a trigger - VytvoriÅ¥ spúšťaÄ - - - Edit the trigger - UpraviÅ¥ spúšťaÄ - - - Delete the trigger - VymazaÅ¥ spúšťaÄ - - - Create a view - VytvoriÅ¥ pohľad - - - Edit the view - UpraviÅ¥ pohľad - - - Delete the view - VymazaÅ¥ pohľad - - - - Add a column - PridaÅ¥ stĺpec - - - - Edit the column - UpraviÅ¥ stĺpec - - - - Delete the column - VymazaÅ¥ stĺpec - - - - Delete selected items - VymazaÅ¥ vybrané položky - - - - Clear filter - ZruÅ¡iÅ¥ filter - - - Refresh all database schemas - ObnoviÅ¥ vÅ¡etky databázové schémy - - - Refresh selected database schema - ObnoviÅ¥ vybranú databázovú schému - - - - Execution from file cancelled. Any queries executed so far have been rolled back. - - - - - &Add a database - - - - - &Edit the database - - - - - &Remove the database - - - - - &Connect to the database - - - - - &Disconnect from the database - - - - - &Export the database - - - - - Con&vert database type - - - - - Vac&uum - - - - - &Integrity check - - - - - Create a &table - - - - - Edit the t&able - - - - - Delete the ta&ble - - - - - Create an &index - - - - - Edit the i&ndex - - - - - Delete the in&dex - - - - - Create a trig&ger - - - - - Edit the trigg&er - - - - - Delete the trigge&r - - - - - Create a &view - - - - - Edit the v&iew - - - - - Delete the vi&ew - - - - - &Refresh all database schemas - - - - - Re&fresh selected database schema - - - - - - Erase table data - VymazaÅ¥ dáta z tabuľky - - - - Open file's directory - - - - - Execute SQL from file - - - - - - Database - Databáza - - - - Grouping - Zoskupovanie - - - - Generate query for table - - - - - - Create group - VytvoriÅ¥ skupinu - - - - Group name - Názov skupiny - - - - Entry with name %1 already exists in group %2. - Položka s názvom %1 už existuje v skupine %2. - - - - Delete group - VymazaÅ¥ skupinu - - - - Are you sure you want to delete group %1? -All objects from this group will be moved to parent group. - Ste si istý, že chcete vymazaÅ¥ skupinu %1? -VÅ¡etky objekty z tejto skupiny budú presunuté do nadradenej skupiny. - - - - Are you sure you want to remove database '%1' from the list? - - - - - Are you sure you want to remove following databases from the list: -%1 - - - - - Remove database - - - - - Vacuum (%1) - - - - - Autoincrement value for table '%1' has been reset successfully. - - - - - Are you sure you want to delete all data from table(s): %1? - - - - - Could not execute SQL, because application has failed to start transaction: %1 - - - - - Could not open file '%1' for reading: %2 - Nemôžem otvoriÅ¥ súbor '%1' na Äítanie: %2 - - - - Could not execute SQL, because application has failed to commit the transaction: %1 - - - - - Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - - - - - Finished executing %1 queries in %2 seconds. - - - - - Could not execute SQL due to error. - - - - Delete database - OdstrániÅ¥ databázu - - - Are you sure you want to delete database '%1'? - Ste si istý, že chcete odstrániÅ¥ databázu '%1'? - - - - - Cannot import, because no import plugin is loaded. - Nemôžem importovaÅ¥, lebo nebol naÄítaný žiaden plugin na import. - - - - - Cannot export, because no export plugin is loaded. - Nemôžem exportovaÅ¥, lebo nebol naÄítaný žiaden plugin na export. - - - Error while executing VACUUM on the database %1: %2 - Vyskytla sa chyba poÄas vykonávania príkazu VACUUM na databáze %1: %2 - - - VACUUM execution finished successfully. - VACUUM úspeÅ¡ne skonÄilo. - - - - Integrity check (%1) - Kontrola integrity (%1) - - - - Reset autoincrement - ResetovaÅ¥ autoinkrement - - - - Are you sure you want to reset autoincrement value for table '%1'? - Ste si istý, že chcete zresetovaÅ¥ hodnotu autoinkrementu pre tabuľku %1 ? - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - Vyskytla sa chyba pri pokuse o zresetovanie hodnoty autoinkrementu pre tebuľku '%1': %2 - - - Are you sure you want to delete all data from table '%1'? - Ste si istý, že chcete vymazaÅ¥ vÅ¡etky dáta z tabuľky '%1'? - - - - An error occurred while trying to delete data from table '%1': %2 - Vyskytla sa chyba pri pokuse vymazaÅ¥ dáta z tabuľky '%1': %2 - - - - All data has been deleted for table '%1'. - VÅ¡etky dáta z tabuľky '%1' boli vymazané. - - - - Following objects will be deleted: %1. - Nasledujúce objekty budú odstránené: %1. - - - - Following databases will be removed from list: %1. - Nasledujúce databázy budú odstránené zo zoznamu: %1. - - - - Remainig objects from deleted group will be moved in place where the group used to be. - - - - - %1<br><br>Are you sure you want to continue? - %1<br><br>Ste si istý, že chcete pokraÄovaÅ¥? - - - - Delete objects - Odstránenie objektov - - - - DbTreeItemDelegate - - - error - dbtree labels - - - - - (system table) - database tree label - - - - - (virtual) - virtual table label - - - - - (system index) - database tree label - - - - - DbTreeModel - - - Database: %1 - dbtree tooltip - Databáza: %1 - - - - Version: - dbtree tooltip - Verzia: - - - - File size: - dbtree tooltip - VeľkosÅ¥ súboru: - - - - Encoding: - dbtree tooltip - Kódovanie: - - - Error details: - dbtree tooltip - Detaily chyby: - - - - Error: - dbtree tooltip - - - - - Table : %1 - dbtree tooltip - Tabuľka : %1 - - - - Columns (%1): - dbtree tooltip - Stĺpce (%1): - - - - Indexes (%1): - dbtree tooltip - Indexy (%1): - - - - Triggers (%1): - dbtree tooltip - SpúšťaÄe (%1): - - - - Copy - KopírovaÅ¥ - - - - Move - - - - - Include data - - - - - Include indexes - - - - - Include triggers - - - - - Abort - ZruÅ¡iÅ¥ - - - - Could not add dropped database file '%1' automatically. Manual setup is necessary. - - - - - Referenced tables - - - - - Do you want to include following referenced tables as well: -%1 - - - - - Name conflict - - - - - Following object already exists in the target database. -Please enter new, unique name, or press '%1' to abort the operation: - - - - - SQL statements conversion - - - - - Following error occurred while converting SQL statements to the target SQLite version: - - - - - Would you like to ignore those errors and proceed? - - - - - DdlHistoryWindow - - - Filter by database: - - - - - -- Queries executed on database %1 (%2) --- Date and time of execution: %3 -%4 - - - - - DDL history - DDL história - - - - DdlPreviewDialog - - - Queries to be executed - Dotazy, ktoré budú vykonané - - - - Don't show again - Znovu nezobrazovaÅ¥ - - - - DebugConsole - - - SQLiteStudio Debug Console - - - - - EditorWindow - - - Query - Dotaz - - - - History - História - - - - Results in the separate tab - Výsledky zobraziÅ¥ v samostatnej záložke - - - - Results below the query - Výsledky zobraziÅ¥ pod dotaz - - - - - SQL editor %1 - SQL editor %1 - - - - Results - Výsledky - - - - Execute query - VykonaÅ¥ dotaz - - - - Explain query - VysvetliÅ¥ dotaz - - - - Clear execution history - sql editor - VymazaÅ¥ históriu dotazov - - - - Export results - sql editor - VyexportovaÅ¥ výsledky - - - - Create view from query - sql editor - VytvoriÅ¥ pohľad z dotazu - - - - Previous database - Predchádzajúca databáza - - - - Next database - Nasledujúca databáza - - - - Show next tab - sql editor - ZobraziÅ¥ nasledujúcu záložku - - - - Show previous tab - sql editor - ZobraziÅ¥ predchádzajúcu záložku - - - - Focus results below - sql editor - - - - - Focus SQL editor above - sql editor - - - - - Delete selected SQL history entries - sql editor - - - - - Active database (%1/%2) - Aktívna databáza (%1/%2) - - - - Query finished in %1 second(s). Rows affected: %2 - Dotaz trval %1 sekúnd. PoÄet dotknutých riadkov: %2 - - - - Query finished in %1 second(s). - Dotaz trval %1 sekúnd. - - - - Clear execution history - VymazaÅ¥ históriu dotazov - - - - Are you sure you want to erase the entire SQL execution history? This cannot be undone. - Ste si istý, že chete vymazaÅ¥ celú históriu SQL dotazov? Túto operáciu nieje možné vrátiÅ¥ späť. - - - - Cannot export, because no export plugin is loaded. - Nemôžem exportovaÅ¥, lebo nebol naÄítaný žiaden plugin na export. - - - - No database selected in the SQL editor. Cannot create a view for unknown database. - Nebola vybraná žiadna databáza v SQL editore. Nemôžem vytvoriÅ¥ view pre neznámu databázu. - - - - Editor window "%1" has uncommitted data. - - - - Editor window "%1" has uncommited data. - Okno editora "%1" obsahuje nepotrdené dáta. - - - - ErrorsConfirmDialog - - - Errors - - - - - Following errors occured: - - - - - Would you like to proceed? - - - - - ExecFromFileDialog - - - Execute SQL from file - - - - - Input file - - - - - Path to file - - - - - Browse for file - - - - - Options - Voľby - - - - File encoding - - - - - Skip failing SQL statements - - - - - SQL scripts (*.sql);;All files (*) - - - - - Execute SQL file - - - - - Please provide file to be executed. - - - - - Provided file does not exist or cannot be read. - - - - - ExportDialog - - - Export - Export - - - - What do you want to export? - ÄŒo chcete exportovaÅ¥? - - - - A database - Databáza - - - - A single table - Jednotlivá tabuľka - - - - Query results - Výsledky dotazu - - - - Table to export - Tabuľka na export - - - - Database - Databáza - - - - Table - Tabuľka - - - - Options - Voľby - - - - When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. - Ak táto voľba nebude oznaÄená, tak sa vyexportuje len DDL tabuľky(príkaz CREATE TABLE). - - - - Export table data - ExportovaÅ¥ dáta tabuľky - - - - Export table indexes - ExportovaÅ¥ indexy tabuľky - - - - Export table triggers - ExportovaÅ¥ spúšťaÄe tabuľky - - - - Note, that exporting table indexes and triggers may be unsupported by some output formats. - Poznámka. Niektoré exportné formáty nemusia podporovaÅ¥ export indexov a spúšťaÄov. - - - - Select database objects to export - Vyberte databázové objekty, ktoré chcete exportovaÅ¥ - - - - Export data from tables - ExportovaÅ¥ dáta z tabuliek - - - - Select all - VybraÅ¥ vÅ¡etko - - - - Deselect all - ZruÅ¡iÅ¥ výber - - - - - Database: - Databáza: - - - - Query to export results for - Dotaz pre export výsledkov pre - - - - Query to be executed for results: - Dotaz, ktorý bude vykonaný pre získanie dát: - - - - Export format and options - Formát exportu a možnosti - - - - Export format - Formát exportu - - - - Output - Výstup - - - - Exported file path - Cesta k výstupnému súboru - - - - Clipboard - Schránka - - - - File - Súbor - - - - Exported text encoding: - Kódovanie vyexportovaného textu: - - - - Export format options - Možnosti formátu exportu - - - - Cancel - ZruÅ¡iÅ¥ - - - - - - Select database to export. - Vyberte databázu, ktorú chcete exportovaÅ¥. - - - - Select table to export. - Vyberte tabuľku, ktorú chcete exportovaÅ¥. - - - - Enter valid query to export. - Zadajte platný dotaz pre export. - - - - Select at least one object to export. - Vyberte aspoň jeden objekt pre export. - - - - You must provide a file name to export to. - Musíte zadaÅ¥ názov súboru, do ktorého sa budú exportovaÅ¥ dáta. - - - - Path you provided is an existing directory. You cannot overwrite it. - Cesta, ktorú ste zadali je existujúci adresár. Nemôžte ho prepísaÅ¥. - - - - The directory '%1' does not exist. - Adresár %1 neexistuje. - - - - The file '%1' exists and will be overwritten. - Súbor %1 už existuje a bude prepísaný. - - - - All files (*) - VÅ¡etky súbory (*) - - - - Pick file to export to - Výber súboru do ktorého sa budú exportovaÅ¥ dáta - - - - Internal error during export. This is a bug. Please report it. - PoÄas exportu sa vyskytla interná chyba. Toto je chyba v programe. Prosím nahláste ju. - - - - FileExecErrorsDialog - - - Execution errors - - - - - Following errors were encountered during execution of SQL statements from the file: - - - - - SQL - - - - - Error - Chyba - - - - Statements that were executed successfully were commited. - - - - - Statements that were executed successfully were rolled back. - - - - - FontEdit - - - Choose font - font configuration - - - - - Form - - - Active SQL formatter plugin - - - - - FormView - - - Commit row - form view - PotvrdiÅ¥ riadok - - - - Rollback row - form view - VrátiÅ¥ späť riadok - - - - First row - form view - Prvý riadok - - - - Previous row - form view - Predchádzajúci riadok - - - - Next row - form view - Nasledujúci riadok - - - - Last row - form view - Posledný riadok - - - - Insert new row - form view - VložiÅ¥ nový riadok - - - - Delete current row - form view - VymazaÅ¥ aktuálny riadok - - - - FunctionsEditor - - - Filter funtions - - - - - Function name: - - - - - Implementation language: - ImplementaÄný jazyk: - - - - Type: - Typ: - - - - Input arguments - - - - - Undefined - - - - - Databases - Databázy - - - - Register in all databases - RegistrovaÅ¥ vo vÅ¡etkých databázach - - - - Register in following databases: - RegistrovaÅ¥ v nasledujúcich databázach: - - - - Initialization code: - - - - - - Function implementation code: - - - - - Final step implementation code: - - - - - SQL function editor - - - - - Commit all function changes - - - - - Rollback all function changes - - - - - Create new function - - - - - Delete selected function - - - - - Custom SQL functions manual - - - - - Add function argument - - - - - Rename function argument - - - - - Delete function argument - - - - - Move function argument up - - - - - Move function argument down - - - - - Scalar - - - - - Aggregate - - - - - Enter a non-empty, unique name of the function. - - - - - Pick the implementation language. - Vyberte implementaÄný jazyk. - - - - Per step code: - - - - - Enter a non-empty implementation code. - Zadajte implementaÄný kód. - - - - argument - new function argument name in function editor window - - - - - Functions editor window has uncommitted modifications. - - - - - ImportDialog - - - Import data - Import dát - - - - Table to import to - Tabuľka na import - - - - Table - Tabuľka - - - - Database - Databáza - - - - Data source to import from - Dátový zdroj na import - - - - Data source type - Typ dátového zdroja - - - - Options - Voľby - - - - Input file: - Vstupný súbor: - - - - Text encoding: - Kódovanie textu: - - - - <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> - - - - - Ignore errors - IgnorovaÅ¥ chyby - - - - Data source options - Nastavenia dátového zdroja - - - - Cancel - ZruÅ¡iÅ¥ - - - - If you type table name that doesn't exist, it will be created. - Ak zadáte názov neexistujúcej tabuľky, tak bude vytvorená. - - - - Enter the table name - Zadajte názov tabuľky - - - - Select import plugin. - Vyberte importný plugin. - - - - You must provide a file to import from. - Musíte zadaÅ¥ súbor, z ktorého sa budú importovaÅ¥ dáta. - - - - The file '%1' does not exist. - Súbor %1 neexistuje. - - - - Path you provided is a directory. A regular file is required. - Cesta, ktorú ste zadali je adresár. Prosím zadajte celú cestu. - - - - Pick file to import from - Výber súboru, z ktorého sa budú importovaÅ¥ dáta - - - - IndexDialog - - - - Index - Index - - - - On table: - Na tabuľke: - - - - Index name: - Názov indexu: - - - - Partial index condition - - - - - Unique index - JedineÄný index - - - - Column - Stĺpec - - - - Collation - Porovnávanie - - - - Sort - ZoradiÅ¥ - - - - Delete selected indexed expression - - - - - Moves selected index column up in the order, making it more significant in the index. - - - - - Moves selected index column down in the order, making it less significant in the index. - - - - - Edit selected indexed expression - - - - - Add indexed expression - - - - - DDL - DDL - - - - Tried to open index dialog for closed or inexisting database. - - - - - Could not process index %1 correctly. Unable to open an index dialog. - - - - - Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. - - - - - Pick the table for the index. - - - - - Select at least one column. - Vyberte minimálne jeden stĺpec. - - - - Enter a valid condition. - Zadajte platnú podmienku. - - - - default - index dialog - - - - - Sort order - table constraints - ZoradiÅ¥ - - - - - Error - index dialog - Chyba - - - - Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? - Nemôžem vytvoriÅ¥ jedineÄný index, pretože hodnoty vo vybraných stĺpcoch nie sú jedineÄné. Chcete spustiÅ¥ dotaz SELECT na zobrazenie problematických hodnôt? - - - - An error occurred while executing SQL statements: -%1 - - - - - IndexExprColumnDialog - - - Indexed expression - - - - - Expression to index - - - - - This expression is already indexed by the index. - - - - - Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. - - - - - Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. - - - - - It's forbidden to use 'SELECT' statements in indexed expressions. - - - - - Enter an indexed expression. - - - - - Invalid expression. - - - - - LanguageDialog - - - Language - Jazyk - - - - Please choose language: - Prosím vyberte si jazyk: - - - - MainWindow - - - Database toolbar - Databázová liÅ¡ta - - - - Structure toolbar - LiÅ¡ta Å¡truktúr - - - - Tools - Nástroje - - - - Window list - LiÅ¡ta okien - - - - View toolbar - LiÅ¡ta pohľadov - - - - Configuration widgets - - - - - Syntax highlighting engines - - - - - Data editors - Editory dát - - - - Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - - - - - Running in debug mode. Debug messages are printed to the standard output. - Beží v ladiacom móde. Ladiace správy sú vypisované na Å¡tandardný výstup. - - - - You need to restart application to make the language change take effect. - Je potrebné reÅ¡tartovaÅ¥ aplikáciu aby sa zmena jazyka prejavila. - - - Open SQL editor - OtvoriÅ¥ SQL editor - - - Open DDL history - OtvoriÅ¥ DDL históriu - - - Open SQL functions editor - OtvoriÅ¥ editor SQL funkcií - - - Open collations editor - OtvoriÅ¥ editor porovnávaní - - - Import - Import - - - Export - Export - - - Open configuration dialog - Konfigurácia - - - Tile windows - OddeliÅ¥ okná - - - Tile windows horizontally - OddeliÅ¥ okná horizontálne - - - Tile windows vertically - OddeliÅ¥ okná vertikálne - - - Cascade windows - Okná kaskádovito - - - - Next window - Nasledujúce okno - - - - Previous window - Predchádzajúce okno - - - - Hide status field - - - - Close selected window - ZatvoriÅ¥ vybrané okno - - - Close all windows but selected - ZatvoriÅ¥ vÅ¡etky okná okrem vybraného - - - Close all windows - ZatvoriÅ¥ vÅ¡etky okná - - - Restore recently closed window - ObnoviÅ¥ posledné zatvorené okno - - - Rename selected window - PremenovaÅ¥ vybrané okno - - - - Open Debug Console - OtvoriÅ¥ ladiacu konzolu - - - - Open CSS Console - OtvoriÅ¥ CSS konzolu - - - Report a bug - NahlásiÅ¥ chybu - - - Propose a new feature - Navrhnúť novú funkciu - - - About - O programe - - - Licenses - Licencie - - - Open home page - OtvoriÅ¥ domovskú stránku - - - Open forum page - OtvoriÅ¥ fórum - - - User Manual - Používateľský manuál - - - SQLite documentation - Dokumentácia SQLite - - - Report history - História hlásení - - - Check for updates - SkontrolovaÅ¥ akutalizácie - - - Database - menubar - Databázy - - - Structure - menubar - Å truktúry - - - View - menubar - Zobrazenie - - - - Window list - menubar view menu - LiÅ¡ta okien - - - Tools - menubar - Nástroje - - - Help - Pomoc - - - - Open SQL &editor - - - - - Open DDL &history - - - - - Open SQL &functions editor - - - - - Open &collations editor - - - - - Open ex&tension manager - - - - - &Import - - - - - E&xport - - - - - Open confi&guration dialog - - - - - &Tile windows - - - - - Tile windows &horizontally - - - - - Tile windows &vertically - - - - - &Cascade windows - - - - - Close selected &window - - - - - Close all windows &but selected - - - - - Close &all windows - - - - - Re&store recently closed window - - - - - &Rename selected window - - - - - Report a &bug - - - - - Propose a new &feature - - - - - &About - - - - - &Licenses - - - - - Open home &page - - - - - Open fo&rum page - - - - - User &Manual - - - - - SQLite &documentation - - - - - Bugs and feature &requests - - - - - Check for &updates - - - - - &Database - menubar - - - - - &Structure - menubar - - - - - &View - menubar - - - - - &Tools - menubar - - - - - &Help - - - - - Could not set style: %1 - main window - Nemôžem nastaviÅ¥ Å¡týl: %1 - - - - Cannot export, because no export plugin is loaded. - Nemôžem exportovaÅ¥, lebo nebol naÄítaný žiaden plugin na export. - - - - Cannot import, because no import plugin is loaded. - Nemôžem importovaÅ¥, lebo nebol naÄítaný žiaden plugin na import. - - - - Rename window - PremenovaÅ¥ okno - - - - Enter new name for the window: - Zadajte nový názov pre okno: - - - - New updates are available. <a href="%1">Click here for details</a>. - Nové aktualizácie sú dostupné. <a href="%1">Kliknite sem pre zobrazenie detailov</a>. - - - - You're running the most recent version. No updates are available. - Niesú dostupné žiadne aktualizácie. Používate aktuálnu verziu. - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - Databáza prebratá z príkazového riadka (%1) už je v zozname pod názvom: %2 - - - - Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 - Databáza prebratá z príkazového riadka (%1) bola doÄasne pridaná do zoznamu pod názvom: %2 - - - - Could not add database %1 to list. - Nemôžem pridaÅ¥ databázu %1 do zoznamu. - - - - MdiWindow - - Uncommited changes - Nepotvrdené zmeny - - - - Uncommitted changes - - - - - Close anyway - ZatvoriÅ¥ aj napriek tomu - - - - Don't close - NezatváraÅ¥ - - - - MultiEditor - - - Null value - multieditor - Hodnota null - - - - Configure editors for this data type - - - - - Open another tab - - - - - Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. - - - - - Deleted - multieditor - - - - - Read only - multieditor - Iba na Äítanie - - - - MultiEditorBoolPlugin - - - Boolean - - - - - MultiEditorDate - - Date - Dátum - - - - MultiEditorDatePlugin - - - Date - Dátum - - - - MultiEditorDateTime - - Date & time - Dátum a Äas - - - - MultiEditorDateTimePlugin - - - Date & time - Dátum a Äas - - - - MultiEditorHexPlugin - - - Hex - - - - - MultiEditorNumeric - - Number - numeric multi editor tab name - Číslo - - - - MultiEditorNumericPlugin - - - Number - numeric multi editor tab name - Číslo - - - - MultiEditorText - - - Tab changes focus - - - - - Cut - Vystrihnúť - - - - Copy - KopírovaÅ¥ - - - - Paste - VložiÅ¥ - - - - Delete - VymazaÅ¥ - - - - Undo - - - - - Redo - - - - - MultiEditorTextPlugin - - - Text - - - - - MultiEditorTimePlugin - - - Time - - - - - NewConstraintDialog - - - New constraint - Nové obmedzenie - - - - - Primary Key - new constraint dialog - Primárny klÃºÄ - - - - - Foreign Key - new constraint dialog - Cudzí klÃºÄ - - - - - Unique - new constraint dialog - JedineÄnosÅ¥ - - - - - Check - new constraint dialog - - - - - Not NULL - new constraint dialog - Nie NULL - - - - Collate - new constraint dialog - Porovnanie - - - - Default - new constraint dialog - Prednastavená hodnota - - - - NewVersionDialog - - - SQLiteStudio updates - Aktualizácie SQLiteStudia - - - - New updates are available! - Nové aktualizácie sú dostupné! - - - - Component - Komponenta - - - - This application will be closed and the update installer will start to download and install all the updates. - - - - Current version - NainÅ¡talovaná verzia - - - - Update version - Dostupná verzia - - - - Check for updates on startup - KontrolovaÅ¥ aktualizácie pri Å¡tarte - - - - Update to new version! - AktualizovaÅ¥ na novú verziu! - - - The update will be automatically downloaded and installed. This will also restart application at the end. - Aktualizácie budú automaticky stiahnuté a nainÅ¡talované. Na konci sa aplikácia reÅ¡tartuje. - - - - Not now. - Nie teraz. - - - - Don't install the update and close this window. - NeinÅ¡talovaÅ¥ aktializácie a zatvoriÅ¥ toto okno. - - - - PopulateConfigDialog - - - Populating configuration - - - - - Configuring <b>%1</b> for column <b>%2</b> - - - - - PopulateDialog - - - Populate table - NaplniÅ¥ tabuľku - - - - Database - Databáza - - - - Table - Tabuľka - - - - Columns - Stĺpce - - - - Number of rows to populate: - PoÄet riadkov na naplnenie: - - - - Populate - populate dialog button - NaplniÅ¥ - - - - Abort - ZruÅ¡iÅ¥ - - - - Configure - KonfigurovaÅ¥ - - - - Populating configuration for this column is invalid or incomplete. - - - - - Select database with table to populate - Vyberte databázu s tabuľkou na naplnenie - - - - Select table to populate - Vyberte tabuľku na naplnenie - - - - You have to select at least one column. - Musíte vybraÅ¥ minimálne jeden stĺpec. - - - - QObject - - - Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). - - - - - The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. - - - - - Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. - - - - - Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. - - - - - Cannot edit results of query other than %1. - - - - - Cannot edit columns that are result of aggregated %1 statements. - - - - - Cannot edit columns that are result of %1 statement. - - - - - Cannot edit columns that are result of common table expression statement (%1). - - - - - - - - on conflict: %1 - data view tooltip - Pri konflikte: %1 - - - - references table %1, column %2 - data view tooltip - - - - - condition: %1 - data view tooltip - - - - - collation name: %1 - data view tooltip - Názov porovnánavania: %1 - - - - Data grid view - Tabuľkové zobrazenie dát - - - - Copy cell(s) contents to clipboard - KopírovaÅ¥ obsah buniek do schránky - - - - Copy cell(s) contents together with header to clipboard - - - - - Paste cell(s) contents from clipboard - VložiÅ¥ obsah buniek zo schránky - - - - Set empty value to selected cell(s) - VymazaÅ¥ hodnoty z vybraných buniek - - - - Set NULL value to selected cell(s) - NastaviÅ¥ NULL hodnotu vo vybraných bunkách - - - - Commit changes to cell(s) contents - PotvrdiÅ¥ zmeny v bunkách - - - - Rollback changes to cell(s) contents - VrátiÅ¥ späť zmeny v bunkách - - - - Delete selected data row - VymazaÅ¥ vybraný riadok - - - - Insert new data row - VložiÅ¥ nový riadok - - - - Open contents of selected cell in a separate editor - OtvoriÅ¥ obsah vybranej bunky v samostatnom editore - - - - Total pages available: %1 - Celkový poÄet strán: %1 - - - - Total rows loaded: %1 - Celkový poÄet riadkov: %1 - - - - Data view (both grid and form) - Zobrazenie dát (tabuľka a formulár) - - - - Refresh data - ObnoviÅ¥ dáta - - - - Switch to grid view of the data - Prepnúť na tabuľkové zobrazenie dát - - - - Switch to form view of the data - Prepnúť na formulárové zobrazenie dát - - - - Database list - Zoznam databáz - - - - Delete selected item - VymazaÅ¥ vybranú položku - - - - Clear filter contents - VymazaÅ¥ filter - - - - Refresh schema - ObnoviÅ¥ schému - - - - Refresh all schemas - ObnoviÅ¥ vÅ¡etky schémy - - - - Add database - PridaÅ¥ databázu - - - - Select all items - VybraÅ¥ vÅ¡etky položky - - - - Copy selected item(s) - KopírovaÅ¥ vybrané položky - - - - - - Paste from clipboard - VložiÅ¥ zo schránky - - - - Tables - Tabuľky - - - - Indexes - Indexy - - - - Triggers - SpúšťaÄe - - - - Views - Pohľady - - - - Columns - Stĺpce - - - - Data form view - Formulárové zobrazenie dát - - - - Commit changes for current row - PotvrdiÅ¥ zmeny pre aktuálny riadok - - - - Rollback changes for current row - VrátiÅ¥ späť zmeny na aktuálnom riadku - - - - Go to first row on current page - PrejsÅ¥ na prvý riadok na aktuálnej strane - - - - Go to next row - PrejsÅ¥ na nasledujúci riadok - - - - Go to previous row - PrejsÅ¥ na predchádzajúci riadok - - - - Go to last row on current page - PrejsÅ¥ na posledný riadok na aktuálnej strane - - - - Insert new row - VložiÅ¥ nový riadok - - - - Delete current row - VymazaÅ¥ aktuálny riadok - - - - Main window - Hlavné okno - - - - Open SQL editor - OtvoriÅ¥ SQL editor - - - - Previous window - Predchádzajúce okno - - - - Next window - Nasledujúce okno - - - - Hide status area - SkryÅ¥ status okno - - - - Open configuration dialog - OtvoriÅ¥ konfiguraÄné okno - - - - Open Debug Console - OtvoriÅ¥ ladiacu konzolu - - - - Open CSS Console - OtvoriÅ¥ CSS konzolu - - - - Cell text value editor - Úprava hodnôt v bunkách - - - - - Cut selected text - Vystrihnúť vybraný text - - - - - Copy selected text - KopírovaÅ¥ vybraný text - - - - - Delete selected text - VymazaÅ¥ vybraný text - - - - - Undo - - - - - - Redo - - - - - SQL editor input field - - - - - Select whole editor contents - OznaÄiÅ¥ vÅ¡etko - - - - Save contents into a file - UložiÅ¥ SQL do súboru - - - - Load contents from a file - NaÄítaÅ¥ SQL zo súboru - - - - Find in text - NájsÅ¥ v SQL - - - - Find next - NájsÅ¥ Äalší - - - - Find previous - NájsÅ¥ predchádzajúci - - - - Replace in text - NahradiÅ¥ v SQL - - - - Delete current line - VymazaÅ¥ aktuálny riadok - - - - Request code assistant - OtvoriÅ¥ SQL pomocníka - - - - Format contents - FormátovaÅ¥ SQL - - - - Move selected block of text one line down - Presunúť blok kódu o riadok nižšie - - - - Move selected block of text one line up - Presunúť blok kódu o riadok vyššie - - - - Copy selected block of text and paste it a line below - KopírovaÅ¥ blok kódu a vložiÅ¥ ho na riadok nižšie - - - - Copy selected block of text and paste it a line above - KopírovaÅ¥ blok kódu a vložiÅ¥ ho na riadok vyššie - - - - Toggle comment - - - - - All SQLite databases - VÅ¡etky SQLite databázy - - - - All files - VÅ¡etky súbory - - - - - Database file - Databázový súbor - - - Reports history window - Okno histórie hlásení - - - Delete selected entry - VymazaÅ¥ vybranú položku - - - - SQL editor window - Okno SQL editora - - - - Execute query - VykonaÅ¥ dotaz - - - - Execute "%1" query - VykonaÅ¥ "%1" dotaz - - - - Switch current working database to previous on the list - Prepnúť sa na predchádzajúcu databázu v zozname - - - - Switch current working database to next on the list - Prepnúť sa na nasledujúcu databázu v zozname - - - - Go to next editor tab - Prechod na nasledujúcu záložku editora - - - - Go to previous editor tab - Prechod na predchádzajúcu záložku editora - - - - Move keyboard input focus to the results view below - Prepnúť kurzor na výsledky - - - - Move keyboard input focus to the SQL editor above - Prepnúť kurzor do editora - - - - Delete selected SQL history entries - - - - - Table window - Okno tabuľky - - - - Refresh table structure - ObnoviÅ¥ Å¡truktúru tabuľky - - - - Add new column - PridaÅ¥ nový stĺpec - - - - Edit selected column - UpraviÅ¥ vybraný stĺpec - - - - Delete selected column - VymazaÅ¥ vybraný stĺpec - - - - Export table data - ExportovaÅ¥ dáta z tabuľky - - - - Import data to the table - ImportovaÅ¥ dáta do tabuľky - - - - Add new table constraint - PridaÅ¥ nové obmedzenie - - - - Edit selected table constraint - UpraviÅ¥ vybrané obmedzenie - - - - Delete selected table constraint - VymazaÅ¥ vybrané obmedzenie - - - - Refresh table index list - ObnoviÅ¥ zoznam indexov - - - - Add new index - PridaÅ¥ nový index - - - - Edit selected index - UpraviÅ¥ vybraný index - - - - Delete selected index - VymazaÅ¥ vybraný index - - - - Refresh table trigger list - ObnoviÅ¥ zoznam spúšťaÄov - - - - - Add new trigger - PridaÅ¥ nový spúšťaÄ - - - - - Edit selected trigger - UpraviÅ¥ vybraný spúšťaÄ - - - - - Delete selected trigger - VymazaÅ¥ vybraný spúšťaÄ - - - - - Go to next tab - Prechod na nasledujúcu záložku - - - - - Go to previous tab - Prechod na predchádzajúcu záložku - - - - A view window - Okno pohľadu - - - - Refresh view trigger list - - - - - QuitConfirmDialog - - Uncommited changes - Nepotvrdené zmeny - - - - Uncommitted changes - - - - - Are you sure you want to quit the application? - -Following items are pending: - - - - - SearchTextDialog - - - Find or replace - NájsÅ¥ alebo nahradiÅ¥ - - - - Find: - NájsÅ¥: - - - - Case sensitive - RozliÅ¡ovaÅ¥ veľké písmená - - - - Search backwards - HľadaÅ¥ spätne - - - - Regular expression matching - - - - - Replace && -find next - NahradiÅ¥ a -nájsÅ¥ Äalší - - - - Replace with: - NahradiÅ¥ s: - - - - Replace all - NahradiÅ¥ vÅ¡etko - - - - Find - NájsÅ¥ - - - - SortDialog - - - Sort by columns - ZoradiÅ¥ podľa stĺpcov - - - - - Column - Stĺpec - - - - - Order - Usporiadanie - - - - Sort by: %1 - ZoradiÅ¥ podľa : %1 - - - - Move column up - Posunúť stĺpec hore - - - - Move column down - Posunúť stĺpec dole - - - - SqlEditor - - - Cut - sql editor - Vystrihnúť - - - - Copy - sql editor - KopírovaÅ¥ - - - - Paste - sql editor - VložiÅ¥ - - - - Delete - sql editor - VymazaÅ¥ - - - - Select all - sql editor - VybraÅ¥ vÅ¡etko - - - - Undo - sql editor - - - - - Redo - sql editor - - - - - Complete - sql editor - - - - - Format SQL - sql editor - FormátovaÅ¥ SQL - - - - Save SQL to file - sql editor - UložiÅ¥ SQL do súboru - - - - Select file to save SQL - sql editor - - - - - Load SQL from file - sql editor - NaÄítaÅ¥ SQL zo súboru - - - - Delete line - sql editor - VymazaÅ¥ riadok - - - - Move block down - sql editor - - - - - Move block up - sql editor - - - - - Copy block down - sql editor - - - - - Copy up down - sql editor - - - - - Find - sql editor - NájsÅ¥ - - - - Find next - sql editor - NájsÅ¥ Äalší - - - - Find previous - sql editor - NájsÅ¥ predchádzajúci - - - - Replace - sql editor - NahradiÅ¥ - - - - Toggle comment - sql editor - - - - - Saved SQL contents to file: %1 - - - - - Syntax completion can be used only when a valid database is set for the SQL editor. - - - - - Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. - - - - - Save to file - UložiÅ¥ do súboru - - - - Could not open file '%1' for writing: %2 - Nemôžem otvoriÅ¥ súbor '%1' pre zápis: %2 - - - - SQL scripts (*.sql);;All files (*) - - - - - Open file - OtvoriÅ¥ súbor - - - - Could not open file '%1' for reading: %2 - Nemôžem otvoriÅ¥ súbor '%1' na Äítanie: %2 - - - - Reached the end of document. Hit the find again to restart the search. - Dosiahnutý koniec súboru. Kliknite na tlaÄidlo NájsÅ¥ pre hľadanie od zaÄiatku súboru. - - - - SqlQueryItem - - - Column: - data view tooltip - Stĺpec: - - - - Data type: - data view - Datový typ: - - - - Table: - data view tooltip - Tabuľka: - - - - Constraints: - data view tooltip - Obmedzenia: - - - This cell is not editable, because: %1 - Táto bunka nieje editovateľná, pretože: %1 - - - - Cannot load the data for a cell that refers to the already closed database. - - - - - SqlQueryItemDelegate - - - The row is marked for deletion. - - - - - - - - - Cannot edit this cell. Details: %1 - - - - - - Structure of this table has changed since last data was loaded. Reload the data to proceed. - - - - - Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). - - - - - Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. - - - - - SqlQueryModel - - - - Only one query can be executed simultaneously. - Nemôže byÅ¥ spustených viacero dotazov súÄasne. - - - Uncommited data - Nepotvrdené dáta - - - There are uncommited data changes. Do you want to proceed anyway? All uncommited changes will be lost. - Sú tu nepotvrdené zmeny. Chcete aj napriek tomu pokraÄovaÅ¥? VÅ¡etky nepotvrdené zmeny budú stratené. - - - - Cannot commit the data for a cell that refers to the already closed database. - Nemôžem potrdiÅ¥ dáta bunky, ktorá odkazuje na už uzatvorenú databázu. - - - - Could not begin transaction on the database. Details: %1 - Nemôžem zaÄaÅ¥ tranzakciu na databáze. Detaily: %1 - - - An error occurred while commiting the transaction: %1 - Vyskytla sa chyba poÄas potvrdzovania tranzakcie: %1 - - - - An error occurred while rolling back the transaction: %1 - Vyskytla sa chyba poÄas vracania späť tranzakcie: %1 - - - - Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. - Nastal pokus o potvrdenie zmien v bunke, ktorú nieje možné upravovaÅ¥ (napriek tomu bola upravená a Äaká na potvrdenie)! Toto je chyba. Prosím nahláste ju. - - - An error occurred while commiting the data: %1 - Vyskytla sa chyba poÄas potvrdzovania dát: %1 - - - - Uncommitted data - - - - - There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. - - - - - An error occurred while committing the transaction: %1 - - - - - An error occurred while committing the data: %1 - - - - - Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - - - - - - Error while executing SQL query on database '%1': %2 - Vyskytla sa chyba poÄas vykonávania SQL dotazu na databáze '%1': %2 - - - - Error while loading query results: %1 - Vyskytla sa chyba poÄas naÄítavania výsledkov dotazu: %1 - - - - Insert multiple rows - VložiÅ¥ viacero riadkov - - - - Number of rows to insert: - PoÄet vkládaných riadkov: - - - - SqlQueryView - - - Go to referenced row in... - - - - - Copy - KopírovaÅ¥ - - - - Copy as... - KopírovaÅ¥ ako... - - - - Paste - VložiÅ¥ - - - - Paste as... - VložiÅ¥ ako... - - - - Set NULL values - NastaviÅ¥ null hodnoty - - - - Erase values - VymazaÅ¥ hodnoty - - - - Edit value in editor - UpraviÅ¥ hodnotu v editory - - - - Commit - PotvrdiÅ¥ - - - - Copy with headers - - - - - Rollback - VrátiÅ¥ späť - - - - Commit selected cells - PotvrdiÅ¥ vybrané bunky - - - - Rollback selected cells - VrátiÅ¥ späť vybrané bunky - - - - Define columns to sort by - VybraÅ¥ stĺpce na zoradenie podľa - - - - Remove custom sorting - OdstrániÅ¥ užívateľské triedenie - - - - Insert row - VložiÅ¥ riadok - - - - Insert multiple rows - VložiÅ¥ viacero riadkov - - - - Delete selected row - VymazaÅ¥ viacero riadkov - - - - Show value in a viewer - - - - - Generate query for selected cells - - - - - No items selected to paste clipboard contents to. - Neboli vybrané žiadne položky na vloženie obsahu schránky. - - - - Go to referenced row in table '%1' - - - - - table '%1' - - - - - Referenced row (%1) - - - - - Trim pasted text? - - - - - The pasted text contains leading or trailing white space. Trim it automatically? - - - - - Edit value - UpraviÅ¥ hodnotu - - - - SqlTableModel - - Error while commiting new row: %1 - Vyskytla sa chyba poÄas potvrdzovania nového riadka: %1 - - - - Error while committing new row: %1 - - - - - Error while deleting row from table %1: %2 - Vyskytla sa chyba poÄas mazania riadka z tabuľky %1: %2 - - - - SqliteExtensionEditor - - - Filter extensions - - - - - Leave empty to use default function - - - - - Extension file - - - - - Initialization function - - - - - Databases - Databázy - - - - Register in all databases - RegistrovaÅ¥ vo vÅ¡etkých databázach - - - - Register in following databases: - RegistrovaÅ¥ v nasledujúcich databázach: - - - - Extension manager window has uncommitted modifications. - - - - - Extension manager - - - - - Commit all extension changes - - - - - Rollback all extension changes - - - - - Add new extension - - - - - Remove selected extension - - - - - Editing extensions manual - - - - - File with given path does not exist or is not readable. - - - - - Unable to load extension: %1 - - - - - Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - - - - - Dynamic link libraries (*.dll);;All files (*) - - - - - Shared objects (*.so);;All files (*) - - - - - Dynamic libraries (*.dylib);;All files (*) - - - - - All files (*) - VÅ¡etky súbory (*) - - - - Open file - OtvoriÅ¥ súbor - - - - StatusField - - - Status - Status - - - - Copy - KopírovaÅ¥ - - - - Clear - VymazaÅ¥ - - - - TableConstraintsModel - - - Type - table constraints - Typ - - - - Details - table constraints - Detaily - - - - Name - table constraints - Názov - - - - TableForeignKeyPanel - - - Foreign table: - Cudzia tabuľka: - - - - SQLite 2 does not support foreign keys officially, -but it's okay to use them anyway. - - - - - Columns - Stĺpce - - - - Local column - Lokálny stĺpec - - - - Foreign column - Cudzí stĺpec - - - - Reactions - Reakcie - - - - Deferred foreign key - - - - - Named constraint - Pomenovanie obmedzenia - - - - Constraint name - Názov obmedzenia - - - - Pick the foreign column. - Vyberte cudzí stĺpec. - - - - Pick the foreign table. - Vyberte cudziu tabuľku. - - - - Select at least one foreign column. - Vyberte aspoň jeden cudzí stĺpec. - - - - Enter a name of the constraint. - Zadajte názov obmedzenia. - - - - Foreign column - table constraints - Cudzí stĺpec - - - - TablePrimaryKeyAndUniquePanel - - - Columns - Stĺpce - - - - Column - Stĺpec - - - - Collation - Porovnávanie - - - - Sort - ZoradiÅ¥ - - - - Valid only for a single column with INTEGER data type - Platné len pre jeden stĺpec s datovým typom INTEGER - - - - Autoincrement - Autoinkrement - - - - Named constraint - Pomenovanie obmedzenia - - - - Constraint name - Názov obmedzenia - - - - On conflict - Pri konflikte - - - - Collate - table constraints - PorovnaÅ¥ - - - - Sort order - table constraints - ZoradiÅ¥ - - - - Select at least one column. - Vyberte minimálne jeden stĺpec. - - - - Enter a name of the constraint. - Zadajte názov obmedzenia. - - - - TableStructureModel - - - Name - table structure columns - Názov - - - - Data type - table structure columns - Datový typ - - - - Primary -Key - table structure columns - - - - - Foreign -Key - table structure columns - - - - - Unique - table structure columns - - - - - Check - table structure columns - - - - - Not -NULL - table structure columns - - - - - Collate - table structure columns - - - - - Default value - table structure columns - Prednastavená hodnota - - - - TableWindow - - - Structure - Å truktúra - - - - Table name: - Názov tabuľky: - - - - - Data - Dáta - - - - Constraints - ObmedzovaÄe - - - - Indexes - Indexy - - - - Triggers - SpúšťaÄe - - - - DDL - DDL - - - - Export table - table window - ExportovaÅ¥ tabuľku - - - - Import data to table - table window - ImportovaÅ¥ dáta do tabuľky - - - - Populate table - table window - NaplniÅ¥ tabuľku - - - - Refresh structure - table window - ObnoviÅ¥ Å¡truktúru - - - - Commit structure changes - table window - PotvrdiÅ¥ zmeny Å¡truktúr - - - - Rollback structure changes - table window - VrátiÅ¥ späť zmeny Å¡truktúr - - - - Add column - table window - PridaÅ¥ stĺpec - - - - Edit column - table window - UpraviÅ¥ stĺpec - - - - - Delete column - table window - VymazaÅ¥ stĺpec - - - - Move column up - table window - Posunúť stĺpec hore - - - - Move column down - table window - Posunúť stĺpec dole - - - - Create similar table - table window - VytvoriÅ¥ rovnakú tabuľku - - - - Reset autoincrement value - table window - ResetovaÅ¥ hodnotu autoinkrementu - - - - Add table constraint - table window - PridaÅ¥ obmedzenie - - - - Edit table constraint - table window - UpraviÅ¥ obmedzenie - - - - Delete table constraint - table window - VymazaÅ¥ obmedzenie - - - - Move table constraint up - table window - Posunúť obmedzenie hore - - - - Move table constraint down - table window - Posunúť obmedzenie dole - - - - Add table primary key - table window - PridaÅ¥ primárny kÄ¾ÃºÄ - - - - Add table foreign key - table window - PridaÅ¥ cudzí kÄ¾ÃºÄ - - - - Add table unique constraint - table window - PridaÅ¥ jedineÄné obmedzenie - - - - Add table check constraint - table window - - - - - Refresh index list - table window - ObnoviÅ¥ zoznam indexov - - - - Create index - table window - VytvoriÅ¥ index - - - - Edit index - table window - UpraviÅ¥ index - - - - Delete index - table window - VymazaÅ¥ index - - - - Refresh trigger list - table window - ObnoviÅ¥ zoznam spúšťaÄov - - - - Create trigger - table window - VytvoriÅ¥ spúšťaÄ - - - - Edit trigger - table window - UpraviÅ¥ spúšťaÄ - - - - Delete trigger - table window - VymazaÅ¥ spúšťaÄ - - - - Are you sure you want to delete column '%1'? - table window - Ste si istý, že chcete vymazaÅ¥ stĺpec '%1' ? - - - - Following problems will take place while modifying the table. -Would you like to proceed? - table window - - - - - Table modification - table window - - - - - Could not load data for table %1. Error details: %2 - - - - - Could not process the %1 table correctly. Unable to open a table window. - - - - - Could not restore window %1, because no database or table was stored in session for this window. - - - - - Could not restore window '%1', because no database or table was stored in session for this window. - - - - - Could not restore window '%1', because database %2 could not be resolved. - - - - - Could not restore window '%1'', because the table %2 doesn't exist in the database %3. - - - - - - New table %1 - - - - - Committed changes for table '%1' successfully. - - - - - Committed changes for table '%1' (named before '%2') successfully. - - - - - Autoincrement value for table '%1' has been reset successfully. - - - - - Uncommitted changes - - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - - - - - Table window "%1" has uncommitted structure modifications and data. - - - - - Table window "%1" has uncommitted data. - - - - - Table window "%1" has uncommitted structure modifications. - - - - - Could not commit table structure. Error message: %1 - table window - - - - - Reset autoincrement - ResetovaÅ¥ autoinkrement - - - - Are you sure you want to reset autoincrement value for table '%1'? - Ste si istý, že chcete zresetovaÅ¥ hodnotu autoinkrementu pre tabuľku %1 ? - - - - An error occurred while trying to reset autoincrement value for table '%1': %2 - Vyskytla sa chyba pri pokuse o zresetovanie hodnoty autoinkrementu pre tebuľku '%1': %2 - - - - Empty name - - - - - A blank name for the table is allowed in SQLite, but it is not recommended. -Are you sure you want to create a table with blank name? - - - - - Cannot create a table without at least one column. - - - - - Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. - - - - - Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. - - - - - Are you sure you want to delete table constraint '%1'? - table window - Ste si istý, že chcete vymazaÅ¥ obmedzenie '%1'? - - - - Delete constraint - table window - VymazaÅ¥ obmedzenie - - - - Cannot export, because no export plugin is loaded. - Nemôžem exportovaÅ¥, lebo nebol naÄítaný žiaden plugin na export. - - - - Cannot import, because no import plugin is loaded. - Nemôžem importovaÅ¥, lebo nebol naÄítaný žiaden plugin na import. - - - Uncommited changes - Nepotvrdené zmeny - - - There are uncommited structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - Sú tu nepotvrdené zmeny Å¡truktúr. Nemôžte prezeraÅ¥ alebo editovaÅ¥ dáta dokiaľ nebude Å¡truktúra tabuľky jasná. -Chcete potvrdiÅ¥ Å¡truktúru alebo sa chcete vrátiÅ¥ do záložky Å¡truktúr? - - - - Go back to structure tab - ChoÄ späť na záložku Å¡truktúr - - - - Commit modifications and browse data. - PotvrdiÅ¥ zmeny a prezeraÅ¥ dáta. - - - - Name - table window indexes - Názov - - - - Unique - table window indexes - JedineÄný - - - - Columns - table window indexes - Stĺpce - - - - Partial index condition - table window indexes - - - - - Name - table window triggers - Názov - - - - Event - table window triggers - UdalosÅ¥ - - - - Condition - table window triggers - Podmienka - - - - Details - table window triggers - Detaily - - - Table window "%1" has uncommited structure modifications and data. - V okne tabuľky %1 sú nepotvrdené zmeny Å¡truktúry a dáta. - - - Table window "%1" has uncommited data. - V okne tabuľky %1 sú nepotvrdené dáta. - - - Table window "%1" has uncommited structure modifications. - V okne tabuľky %1 sú nepotvrdené zmeny Å¡truktúry. - - - - TriggerColumnsDialog - - - Trigger columns - - - - - Triggering columns: - - - - - Select all - VybraÅ¥ vÅ¡etko - - - - Deselect all - ZruÅ¡iÅ¥ výber - - - - TriggerDialog - - - - Trigger - SpúšťaÄ - - - - On table: - Na tabuľke: - - - - Action: - Akcia: - - - - - <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> - - - - - Pre-condition: - - - - - The scope is still not fully supported by the SQLite database. - - - - - Trigger name: - Názov spúšťaÄa: - - - - When: - Kedy: - - - - List of columns for UPDATE OF action. - - - - - Scope: - - - - - Code: - Kód: - - - - Trigger statements to be executed. - - - - - DDL - DDL - - - - On view: - - - - - Could not process trigger %1 correctly. Unable to open a trigger dialog. - - - - - Enter a valid condition. - Zadajte platnú podmienku. - - - - Enter a valid trigger code. - Zadajte validný kód spúšťaÄa. - - - - Error - trigger dialog - Chyba - - - - An error occurred while executing SQL statements: -%1 - - - - - VersionConvertSummaryDialog - - - Database version convert - - - - - Following changes to the SQL statements will be made: - - - - - Before - - - - - After - - - - - ViewWindow - - - Query - Dotaz - - - - View name: - Názov pohľadu: - - - - Output column names - - - - - - Data - Dáta - - - - Triggers - SpúšťaÄe - - - - DDL - DDL - - - - - Could not restore window '%1', because no database or view was stored in session for this window. - - - - - Could not restore window '%1', because database %2 could not be resolved. - - - - - Could not restore window '%1', because database %2 could not be open. - - - - - Could not restore window '%1', because the view %2 doesn't exist in the database %3. - - - - - - New view %1 - Nový pohľad %1 - - - - Refresh the view - view window - ObnoviÅ¥ pohľad - - - - Commit the view changes - view window - PotvrdiÅ¥ zmeny v pohľade - - - - Rollback the view changes - view window - VrátiÅ¥ späť zmeny v pohľade - - - - Explicit column names - - - - - Generate output column names automatically basing on result columns of the view. - - - - - Add column - view window - PridaÅ¥ stĺpec - - - - Edit column - view window - UpraviÅ¥ stĺpec - - - - Delete column - view window - VymazaÅ¥ stĺpec - - - - Move column up - view window - Posunúť stĺpec hore - - - - Move column down - view window - Posunúť stĺpec dole - - - - Refresh trigger list - view window - ObnoviÅ¥ zoznam spúšťaÄov - - - - Create new trigger - view window - VytvoriÅ¥ nový spúšťaÄ - - - - Edit selected trigger - view window - UpraviÅ¥ vybraný spúšťaÄ - - - - Delete selected trigger - view window - VymazaÅ¥ vybraný spúšťaÄ - - - - View window "%1" has uncommitted structure modifications and data. - - - - - View window "%1" has uncommitted data. - - - - - View window "%1" has uncommitted structure modifications. - - - - - Uncommitted changes - - - - - There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - - - - - Committed changes for view '%1' successfully. - - - - - Committed changes for view '%1' (named before '%2') successfully. - - - - View window "%1" has uncommited structure modifications and data. - Okno pohľadu "%1" obsahuje nepotrdené zmeny Å¡truktúr a dát. - - - View window "%1" has uncommited data. - Okno pohľadu "%1" obsahuje nepotrdené dáta. - - - View window "%1" has uncommited structure modifications. - Okno pohľadu "%1" obsahuje nepotrdené zmeny Å¡truktúr. - - - - Could not load data for view %1. Error details: %2 - Nemôžem naÄítaÅ¥ dáta z pohľadu %1. Detaily chyby: %2 - - - Uncommited changes - Nepotvrdené zmeny - - - There are uncommited structure modifications. You cannot browse or edit data until you have the view structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - Sú tu nepotvrdené zmeny Å¡truktúr. Nemôžte prezeraÅ¥ alebo editovaÅ¥ dáta dokiaľ nebude Å¡truktúra tabuľky jasná. -Chcete potvrdiÅ¥ Å¡truktúru alebo sa chcete vrátiÅ¥ do záložky Å¡truktúr? - - - - Go back to structure tab - ChoÄ späť na záložku Å¡truktúr - - - - Commit modifications and browse data. - PotvrdiÅ¥ zmeny a prezeraÅ¥ dáta. - - - - Could not commit view changes. Error message: %1 - view window - Nemôžem potvrdiÅ¥ zmeny v pohľade. Chyba: %1 - - - - Override columns - - - - - Currently defined columns will be overriden. Do you want to continue? - - - - - Could not determinate columns returned from the view. The query is problably incomplete or contains errors. - - - - - Name - view window triggers - Názov - - - - Instead of - view window triggers - - - - - Condition - view window triggers - Podmienka - - - - Details - table window triggers - Detaily - - - - Could not process the %1 view correctly. Unable to open a view window. - - - - - Empty name - - - - - A blank name for the view is allowed in SQLite, but it is not recommended. -Are you sure you want to create a view with blank name? - - - - - The SELECT statement could not be parsed. Please correct the query and retry. -Details: %1 - SELECT nemôže byÅ¥ analyzovaný. Prosím opravte dotaz a skúste to znovu. -Detaily: %1 - - - - The view could not be modified due to internal SQLiteStudio error. Please report this! - - - - - The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. - - - - - Following problems will take place while modifying the view. -Would you like to proceed? - view window - - - - - View modification - view window - - - - - WidgetCover - - - Interrupt - - - - diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk_SK.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk_SK.ts new file mode 100644 index 0000000..e7800f4 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk_SK.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + O programe SQLiteStudio a licenciách + + + + About + O programe + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licencie + + + + Environment + Prostredie + + + + Icon directories + Adresáre s ikonami + + + + Form directories + Adresáre so Å¡týlmi + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Adresáre s pluginmi + + + + Configuration directory + Adresár s konfiguráciou + + + + Application directory + Adresár aplikácie + + + + Qt version: + Verzia Qt: + + + + SQLite 3 version: + Verzia SQLite3: + + + + Portable distribution. + Prenosná distribúcia. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Distribúcia spravovaná operaÄným systémom. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Tabuľka obsahu:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + FiltrovaÅ¥ porovnávania + + + + Databases + Databázy + + + + Register in all databases + RegistrovaÅ¥ vo vÅ¡etkých databázach + + + + Register in following databases: + RegistrovaÅ¥ v nasledujúcich databázach: + + + + Implementation code: + ImplementaÄný kód: + + + + Collation name: + Názov porovnánavania: + + + + Implementation language: + ImplementaÄný jazyk: + + + + Collations editor + Editor porovnávaní + + + + Commit all collation changes + PotvrdiÅ¥ vÅ¡etky zmeny v porovnávaní + + + + Rollback all collation changes + VrátiÅ¥ späť vÅ¡etky zmeny v porovnávaní + + + + Create new collation + VytvoriÅ¥ nové porovnávanie + + + + Delete selected collation + VymazaÅ¥ vybrané porovnávanie + + + + Editing collations manual + Manuál úpravy porovnávaní + + + + Enter a non-empty, unique name of the collation. + Zadajte jedineÄný názov porovnávania. + + + + Pick the implementation language. + Vyberte implementaÄný jazyk. + + + + Enter a non-empty implementation code. + Zadajte implementaÄný kód. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Vyberte farbu + + + + ColumnCollatePanel + + + Collation name: + Názov porovnánavania: + + + + Named constraint: + Pomenovanie obmedzenia: + + + + Enter a name of the constraint. + Zadajte názov obmedzenia. + + + + Enter a collation name. + Zadajte názov porovnávania. + + + + ColumnDefaultPanel + + + Default value: + Prednastavená hodnota: + + + + Named constraint: + Pomenovanie obmedzenia: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Zadajte názov obmedzenia. + + + + ColumnDialog + + + Column + Stĺpec + + + + Name and type + Názov a typ + + + + Scale + Rozsah + + + + Precision + PresnosÅ¥ + + + + Data type: + Datový typ: + + + + Column name: + Názov stĺpca: + + + + Size: + VeľkosÅ¥: + + + + Constraints + Obmedzenia + + + + Generated value + Generated value + + + + Unique + JedineÄný + + + + + + + + + + + Configure + KonfigurovaÅ¥ + + + + Foreign Key + Cudzí klÃºÄ + + + + Collate + Porovnanie + + + + Not NULL + Nie NULL + + + + Check condition + Check condition + + + + Primary Key + Primárny klÃºÄ + + + + Default + Prednastavená hodnota + + + + Advanced mode + Rozšírený mód + + + + Add constraint + column dialog + PridaÅ¥ obmedzenie + + + + Edit constraint + column dialog + UpraviÅ¥ obmedzenie + + + + + Delete constraint + column dialog + VymazaÅ¥ obmedzenie + + + + Move constraint up + column dialog + Posunúť obmedzenie hore + + + + Move constraint down + column dialog + Posunúť obmedzenie dole + + + + Add a primary key + column dialog + PridaÅ¥ primárny kÄ¾ÃºÄ + + + + Add a foreign key + column dialog + PridaÅ¥ cudzí kÄ¾ÃºÄ + + + + Add an unique constraint + column dialog + PridaÅ¥ jedineÄné obmedzenie + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Ste si istý, že chcete vymazaÅ¥ obmedzenie '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Typ + + + + Name + column dialog constraints + Názov + + + + Details + column dialog constraints + Detaily + + + + ColumnForeignKeyPanel + + + Foreign table: + Cudzia tabuľka: + + + + Foreign column: + Cudzí stĺpec: + + + + Reactions + Reakcie + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Pomenovanie obmedzenia + + + + Constraint name + Názov obmedzenia + + + + Pick the foreign table. + Vyberte cudziu tabuľku. + + + + Pick the foreign column. + Vyberte cudzí stĺpec. + + + + Enter a name of the constraint. + Zadajte názov obmedzenia. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoikrement + + + + Sort order: + ZoradiÅ¥: + + + + Named constraint: + Pomenovanie obmedzenia: + + + + On conflict: + Pri konflikte: + + + + Enter a name of the constraint. + Zadajte názov obmedzenia. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Pomenovanie obmedzenia: + + + + On conflict: + Pri konflikte: + + + + Enter a name of the constraint. + Zadajte názov obmedzenia. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Stĺpec: %1 + + + + Table: %1 + completer statusbar + Tabuľka: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + SpúšťaÄ: %1 + + + + View: %1 + completer statusbar + Pohľad: %1 + + + + Database: %1 + completer statusbar + Databáza: %1 + + + + Keyword: %1 + completer statusbar + KľúÄové slovo: %1 + + + + Function: %1 + completer statusbar + Funkcia: %1 + + + + Operator: %1 + completer statusbar + Operátor: %1 + + + + String + completer statusbar + ReÅ¥azec + + + + Number + completer statusbar + Číslo + + + + Binary data + completer statusbar + Binárne dáta + + + + Collation: %1 + completer statusbar + Porovnávanie: %1 + + + + Pragma function: %1 + completer statusbar + Pragma funkcia : %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Konfigurácia + + + + Search + HľadaÅ¥ + + + + General + VÅ¡eobecné + + + + Keyboard shortcuts + Klávesové skratky + + + + Look & feel + Vzhľad + + + + Style + Å týl + + + + Fonts + Fonty + + + + Code colors + Code colors + + + + + Database list + Zoznam databáz + + + + Code assistant + Code assistant + + + + Data browsing + Prezeranie dát + + + + Data editors + Editory dát + + + + Plugins + Pluginy + + + + Code formatters + Formát kódu + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + ZoradiÅ¥ stĺpce tabuľky abecedne + + + + Expand tables node when connected to a database + RozbaliÅ¥ zoznam tabuliek po pripojení k databáze + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + ZobraziÅ¥ doplnkové popisky v zozname + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + ZobraziÅ¥ popisky pre regulárne tabuľky + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + ZobraziÅ¥ popisky pre virtuálne tabuľky + + + + Expand views node when connected to a database + RozbaliÅ¥ zoznam pohľadov po pripojení k databáze + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + ZoradiÅ¥ objekty (tabuľky, indexy, spúšťaÄe a pohľady) abecedne + + + + Display system tables and indexes on the list + ZobraziÅ¥ systémové tabuľky a indexy v zozname + + + + Database dialog window + Databázové dialógové okno + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>OznaÄením tejto možnosti sa vypne voľba "ZapamätaÅ¥ si databázu" v okne pridávania novej databázy</p> + + + + Do not mark database to be "permanent" by default + NepamätaÅ¥ si databázu + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Prezeranie a úprava dát + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + PoÄet data riadkov na stranu: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Okná tabuľky + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + ZobraziÅ¥ dáta po otvorení tabuľky + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + Okná pohľadov + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + ZobraziÅ¥ dáta po otvorení pohľadu + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Datové typy + + + + Available editors: + Dostupné editory: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Úprava schémy + + + + Number of DDL changes kept in history. + PoÄet DDL zmien uchovávaných v histórii. + + + + DDL history size: + VeľkosÅ¥ DDL histórie: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL dotazy + + + + + Number of queries kept in the history. + PoÄet dotazov uchovávaných v histórii. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + VeľkosÅ¥ SQL histórie: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + VykonaÅ¥ len dotaz, na ktorom stojí kurzor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Aktualizácie + + + + Automatically check for updates at startup + KontrolovaÅ¥ aktualizácie pri Å¡tarte + + + + Session + Sedenie + + + + Restore last session (active MDI windows) after startup + ObnoviÅ¥ posledné sedenie (aktívne okná) pri Å¡tarte + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + FiltrovaÅ¥ podľa názvu alebo klávesovej skratky + + + + Action + Akcia + + + + Key combination + Klávesová skratka + + + + + Language + Jazyk + + + + Changing language requires application restart to take effect. + Je potrebné reÅ¡tartovaÅ¥ aplikáciu aby sa zmena jazyka prejavila. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + NezobrazovaÅ¥ interné pluginy + + + + Current style: + Aktuálny Å¡týl: + + + + Preview + Náhľad + + + + Enabled + Zapnutý + + + + Disabled + Vypnutý + + + + Active formatter plugin + Aktívny formátovací plugin + + + + SQL editor font + Písmo SQL editora + + + + Database list font + Font zoznamu databáz + + + + Database list additional label font + Font doplnkového popisku + + + + Data view font + Font dát + + + + Status field font + Font status okna + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Popis: + + + + Category: + plugin details + Kategória: + + + + Version: + plugin details + Verzia: + + + + Author: + plugin details + Autor: + + + + Internal name: + plugin details + Interný názov: + + + + Dependencies: + plugin details + Závislosti: + + + + Conflicts: + plugin details + Konflikty: + + + + Plugin details + Detaily pluginu + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Pluginy sú naÄítané/uvoľnené okamžite pri zaÅ¡krtnutí/odÅ¡krtnutí ale midifikovaný zoznam pluginov naÄítaných pri Å¡tarte nieje uložený až kým nepotvrdíte celý konfiguraÄný dialóg. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (interný) + + + + Details + Detaily + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + PridaÅ¥ nový datový typ + + + + Rename selected data type + PremenovaÅ¥ vybraný datový typ + + + + Delete selected data type + VymazaÅ¥ vybraný datový typ + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + Podmienka + + + + Named constraint: + Pomenovanie obmedzenia: + + + + On conflict + Pri konflikte + + + + Enter a valid condition. + Zadajte platnú podmienku. + + + + Enter a name of the constraint. + Zadajte názov obmedzenia. + + + + ConstraintDialog + + + New constraint + constraint dialog + Nové obmedzenie + + + + Create + constraint dialog + VytvoriÅ¥ + + + + Edit constraint + dialog window + UpraviÅ¥ obmedzenie + + + + Apply + constraint dialog + AplikovaÅ¥ + + + + Primary key + table constraints + Primárny klÃºÄ + + + + Foreign key + table constraints + Cudzí klÃºÄ + + + + Unique + table constraints + JedineÄnosÅ¥ + + + + Not NULL + table constraints + Nie NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Porovnanie + + + + Default + table constraints + Prednastavená hodnota + + + + ConstraintTabModel + + + Table + table constraints + Tabuľka + + + + Column (%1) + table constraints + Stĺpec (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Typ + + + + Details + table constraints + Detaily + + + + Name + table constraints + Názov + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + FiltrovaÅ¥ + + + + Grid view + Tabuľkové zobrazenie + + + + Form view + Formulárové zobrazenie + + + + Refresh table data + data view + ObnoviÅ¥ dáta v tabuľke + + + + First page + data view + Prvá strana + + + + Previous page + data view + Predchádzajúca strana + + + + Next page + data view + Nasledujúca strana + + + + Last page + data view + Posledná strana + + + + Commit changes for selected cells + data view + PotvrdiÅ¥ zmeny pre vybrané bunky + + + + Rollback changes for selected cells + data view + VrátiÅ¥ späť zmeny pre vybrané bunky + + + + Show grid view of results + data view + Výsledky zobraziÅ¥ v tabuľke + + + + Show form view of results + data view + Výsledky zobraziÅ¥ vo formulári + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Záložky hore + + + + Tabs at bottom + data view + Záložky dole + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + SpoÄítava sa celkový poÄet riadkov. +Prezeranie Äalších strán bude možné až po dokonÄení spoÄítavania. + + + + Row: %1 + Riadok:%1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + FiltrovaÅ¥ pomocou regulárneho výrazu + + + + Filter by SQL expression + data view + FiltrovaÅ¥ pomocou SQL výrazu + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + AplikovaÅ¥ filter + + + + DbDialog + + + Database + Databáza + + + + Database type + Typ databázy + + + + Database driver + Databázový ovládaÄ + + + + + File + Súbor + + + + Name (on the list) + Názov (v zozname) + + + + Options + Voľby + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Databáza sa uloží do konfiguraÄného súboru a bude obnovená pri každom spustení SQLiteStudia.</p> + + + + Permanent (keep it in configuration) + ZapamätaÅ¥ si databázu + + + + Test connection + Test spojenia + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + PrehľadávaÅ¥ + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Zadajte názov databázy. + + + + This name is already in use. Please enter unique name. + Tento názov už existuje. Prosím zadajte iný názov. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Zadajte cestu k databázovému súboru. + + + + This database is already on the list under name: %1 + Táto databáza už je v zozname pod názvom: %1 + + + + Select a database type. + Vyberte typ databázy. + + + + DbObjectDialogs + + + Delete table + VymazaÅ¥ tabuľku + + + + Are you sure you want to delete table %1? + Ste si istý, že chcete vymazaÅ¥ tabuľku %1? + + + + Delete index + VymazaÅ¥ index + + + + Are you sure you want to delete index %1? + Ste si istý, že chcete vymazaÅ¥ index %1? + + + + Delete trigger + VymazaÅ¥ spúšťaÄ + + + + Are you sure you want to delete trigger %1? + Ste si istý, že chcete vymazaÅ¥ spúšťaÄ %1? + + + + Delete view + VymazaÅ¥ pohľad + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Vyskystla sa chyba poÄas mazania %1: %2 + + + + Delete objects + Odstránenie objektov + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databázy + + + + Filter by name + FiltrovaÅ¥ podľa názvu + + + + Copy + KopírovaÅ¥ + + + + Paste + VložiÅ¥ + + + + Select all + VybraÅ¥ vÅ¡etko + + + + Create a group + VytvoriÅ¥ skupinu + + + + Delete the group + VymazaÅ¥ skupinu + + + + Rename the group + PremenovaÅ¥ skupinu + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + ImportovaÅ¥ + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + ExportovaÅ¥ tabuľku + + + + Import into the table + ImportovaÅ¥ do tabuľky + + + + Populate table + NaplniÅ¥ tabuľku + + + + Create similar table + VytvoriÅ¥ rovnakú tabuľku + + + + Reset autoincrement sequence + ResetovaÅ¥ sekvenciu autoinkrementu + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + PridaÅ¥ stĺpec + + + + Edit the column + UpraviÅ¥ stĺpec + + + + Delete the column + VymazaÅ¥ stĺpec + + + + Delete selected items + VymazaÅ¥ vybrané položky + + + + Clear filter + ZruÅ¡iÅ¥ filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + VymazaÅ¥ dáta z tabuľky + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Databáza + + + + Grouping + Zoskupovanie + + + + Generate query for table + Generate query for table + + + + + Create group + VytvoriÅ¥ skupinu + + + + Group name + Názov skupiny + + + + Entry with name %1 already exists in group %2. + Položka s názvom %1 už existuje v skupine %2. + + + + Delete group + VymazaÅ¥ skupinu + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Ste si istý, že chcete vymazaÅ¥ skupinu %1? +VÅ¡etky objekty z tejto skupiny budú presunuté do nadradenej skupiny. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Nemôžem importovaÅ¥, lebo nebol naÄítaný žiaden plugin na import. + + + + + Cannot export, because no export plugin is loaded. + Nemôžem exportovaÅ¥, lebo nebol naÄítaný žiaden plugin na export. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Kontrola integrity (%1) + + + + Reset autoincrement + ResetovaÅ¥ autoinkrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Ste si istý, že chcete zresetovaÅ¥ hodnotu autoinkrementu pre tabuľku %1 ? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + Vyskytla sa chyba pri pokuse o zresetovanie hodnoty autoinkrementu pre tebuľku '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + Vyskytla sa chyba pri pokuse vymazaÅ¥ dáta z tabuľky '%1': %2 + + + + All data has been deleted for table '%1'. + VÅ¡etky dáta z tabuľky '%1' boli vymazané. + + + + Following objects will be deleted: %1. + Nasledujúce objekty budú odstránené: %1. + + + + Following databases will be removed from list: %1. + Nasledujúce databázy budú odstránené zo zoznamu: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Ste si istý, že chcete pokraÄovaÅ¥? + + + + Delete objects + Odstránenie objektov + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Databáza: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Verzia: + + + + File size: + dbtree tooltip + VeľkosÅ¥ súboru: + + + + Encoding: + dbtree tooltip + Kódovanie: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Tabuľka : %1 + + + + Columns (%1): + dbtree tooltip + Stĺpce (%1): + + + + Indexes (%1): + dbtree tooltip + Indexy (%1): + + + + Triggers (%1): + dbtree tooltip + SpúšťaÄe (%1): + + + + Copy + KopírovaÅ¥ + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + ZruÅ¡iÅ¥ + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL história + + + + DdlPreviewDialog + + + Queries to be executed + Dotazy, ktoré budú vykonané + + + + Don't show again + Znovu nezobrazovaÅ¥ + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Dotaz + + + + History + História + + + + Results in the separate tab + Výsledky zobraziÅ¥ v samostatnej záložke + + + + Results below the query + Výsledky zobraziÅ¥ pod dotaz + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Výsledky + + + + Execute query + VykonaÅ¥ dotaz + + + + Explain query + VysvetliÅ¥ dotaz + + + + Clear execution history + sql editor + VymazaÅ¥ históriu dotazov + + + + Export results + sql editor + VyexportovaÅ¥ výsledky + + + + Create view from query + sql editor + VytvoriÅ¥ pohľad z dotazu + + + + Previous database + Predchádzajúca databáza + + + + Next database + Nasledujúca databáza + + + + Show next tab + sql editor + ZobraziÅ¥ nasledujúcu záložku + + + + Show previous tab + sql editor + ZobraziÅ¥ predchádzajúcu záložku + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Aktívna databáza (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Dotaz trval %1 sekúnd. PoÄet dotknutých riadkov: %2 + + + + Query finished in %1 second(s). + Dotaz trval %1 sekúnd. + + + + Clear execution history + VymazaÅ¥ históriu dotazov + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Ste si istý, že chete vymazaÅ¥ celú históriu SQL dotazov? Túto operáciu nieje možné vrátiÅ¥ späť. + + + + Cannot export, because no export plugin is loaded. + Nemôžem exportovaÅ¥, lebo nebol naÄítaný žiaden plugin na export. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + Nebola vybraná žiadna databáza v SQL editore. Nemôžem vytvoriÅ¥ view pre neznámu databázu. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Voľby + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + ÄŒo chcete exportovaÅ¥? + + + + A database + Databáza + + + + A single table + Jednotlivá tabuľka + + + + Query results + Výsledky dotazu + + + + Table to export + Tabuľka na export + + + + Database + Databáza + + + + Table + Tabuľka + + + + Options + Voľby + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + Ak táto voľba nebude oznaÄená, tak sa vyexportuje len DDL tabuľky(príkaz CREATE TABLE). + + + + Export table data + ExportovaÅ¥ dáta tabuľky + + + + Export table indexes + ExportovaÅ¥ indexy tabuľky + + + + Export table triggers + ExportovaÅ¥ spúšťaÄe tabuľky + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Poznámka. Niektoré exportné formáty nemusia podporovaÅ¥ export indexov a spúšťaÄov. + + + + Select database objects to export + Vyberte databázové objekty, ktoré chcete exportovaÅ¥ + + + + Export data from tables + ExportovaÅ¥ dáta z tabuliek + + + + Select all + VybraÅ¥ vÅ¡etko + + + + Deselect all + ZruÅ¡iÅ¥ výber + + + + + Database: + Databáza: + + + + Query to export results for + Dotaz pre export výsledkov pre + + + + Query to be executed for results: + Dotaz, ktorý bude vykonaný pre získanie dát: + + + + Export format and options + Formát exportu a možnosti + + + + Export format + Formát exportu + + + + Output + Výstup + + + + Exported file path + Cesta k výstupnému súboru + + + + Clipboard + Schránka + + + + File + Súbor + + + + Exported text encoding: + Kódovanie vyexportovaného textu: + + + + Export format options + Možnosti formátu exportu + + + + Cancel + ZruÅ¡iÅ¥ + + + + + + Select database to export. + Vyberte databázu, ktorú chcete exportovaÅ¥. + + + + Select table to export. + Vyberte tabuľku, ktorú chcete exportovaÅ¥. + + + + Enter valid query to export. + Zadajte platný dotaz pre export. + + + + Select at least one object to export. + Vyberte aspoň jeden objekt pre export. + + + + You must provide a file name to export to. + Musíte zadaÅ¥ názov súboru, do ktorého sa budú exportovaÅ¥ dáta. + + + + Path you provided is an existing directory. You cannot overwrite it. + Cesta, ktorú ste zadali je existujúci adresár. Nemôžte ho prepísaÅ¥. + + + + The directory '%1' does not exist. + Adresár %1 neexistuje. + + + + The file '%1' exists and will be overwritten. + Súbor %1 už existuje a bude prepísaný. + + + + All files (*) + VÅ¡etky súbory (*) + + + + Pick file to export to + Výber súboru do ktorého sa budú exportovaÅ¥ dáta + + + + Internal error during export. This is a bug. Please report it. + PoÄas exportu sa vyskytla interná chyba. Toto je chyba v programe. Prosím nahláste ju. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Chyba + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + PotvrdiÅ¥ riadok + + + + Rollback row + form view + VrátiÅ¥ späť riadok + + + + First row + form view + Prvý riadok + + + + Previous row + form view + Predchádzajúci riadok + + + + Next row + form view + Nasledujúci riadok + + + + Last row + form view + Posledný riadok + + + + Insert new row + form view + VložiÅ¥ nový riadok + + + + Delete current row + form view + VymazaÅ¥ aktuálny riadok + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databázy + + + + Register in all databases + RegistrovaÅ¥ vo vÅ¡etkých databázach + + + + Register in following databases: + RegistrovaÅ¥ v nasledujúcich databázach: + + + + Type: + Typ: + + + + Function name: + Function name: + + + + Implementation language: + ImplementaÄný jazyk: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Vyberte implementaÄný jazyk. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Zadajte implementaÄný kód. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import dát + + + + Table to import to + Tabuľka na import + + + + Table + Tabuľka + + + + Database + Databáza + + + + Data source to import from + Dátový zdroj na import + + + + Data source type + Typ dátového zdroja + + + + Options + Voľby + + + + Text encoding: + Kódovanie textu: + + + + Input file: + Vstupný súbor: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + IgnorovaÅ¥ chyby + + + + Data source options + Nastavenia dátového zdroja + + + + Cancel + ZruÅ¡iÅ¥ + + + + If you type table name that doesn't exist, it will be created. + Ak zadáte názov neexistujúcej tabuľky, tak bude vytvorená. + + + + Enter the table name + Zadajte názov tabuľky + + + + Select import plugin. + Vyberte importný plugin. + + + + You must provide a file to import from. + Musíte zadaÅ¥ súbor, z ktorého sa budú importovaÅ¥ dáta. + + + + The file '%1' does not exist. + Súbor %1 neexistuje. + + + + Path you provided is a directory. A regular file is required. + Cesta, ktorú ste zadali je adresár. Prosím zadajte celú cestu. + + + + Pick file to import from + Výber súboru, z ktorého sa budú importovaÅ¥ dáta + + + + IndexDialog + + + + Index + Index + + + + Column + Stĺpec + + + + Sort + ZoradiÅ¥ + + + + Collation + Porovnávanie + + + + On table: + Na tabuľke: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + JedineÄný index + + + + Index name: + Názov indexu: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Vyberte minimálne jeden stĺpec. + + + + Enter a valid condition. + Zadajte platnú podmienku. + + + + default + index dialog + default + + + + Sort order + table constraints + ZoradiÅ¥ + + + + + Error + index dialog + Chyba + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Nemôžem vytvoriÅ¥ jedineÄný index, pretože hodnoty vo vybraných stĺpcoch nie sú jedineÄné. Chcete spustiÅ¥ dotaz SELECT na zobrazenie problematických hodnôt? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Jazyk + + + + Please choose language: + Prosím vyberte si jazyk: + + + + MainWindow + + + Database toolbar + Databázová liÅ¡ta + + + + Structure toolbar + LiÅ¡ta Å¡truktúr + + + + Tools + Nástroje + + + + Window list + LiÅ¡ta okien + + + + View toolbar + LiÅ¡ta pohľadov + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Editory dát + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Beží v ladiacom móde. Ladiace správy sú vypisované na Å¡tandardný výstup. + + + + You need to restart application to make the language change take effect. + Je potrebné reÅ¡tartovaÅ¥ aplikáciu aby sa zmena jazyka prejavila. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Nasledujúce okno + + + + Previous window + Predchádzajúce okno + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + OtvoriÅ¥ ladiacu konzolu + + + + Open CSS Console + OtvoriÅ¥ CSS konzolu + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + LiÅ¡ta okien + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Nemôžem nastaviÅ¥ Å¡týl: %1 + + + + Cannot export, because no export plugin is loaded. + Nemôžem exportovaÅ¥, lebo nebol naÄítaný žiaden plugin na export. + + + + Cannot import, because no import plugin is loaded. + Nemôžem importovaÅ¥, lebo nebol naÄítaný žiaden plugin na import. + + + + Rename window + PremenovaÅ¥ okno + + + + Enter new name for the window: + Zadajte nový názov pre okno: + + + + New updates are available. <a href="%1">Click here for details</a>. + Nové aktualizácie sú dostupné. <a href="%1">Kliknite sem pre zobrazenie detailov</a>. + + + + You're running the most recent version. No updates are available. + Niesú dostupné žiadne aktualizácie. Používate aktuálnu verziu. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Databáza prebratá z príkazového riadka (%1) už je v zozname pod názvom: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Databáza prebratá z príkazového riadka (%1) bola doÄasne pridaná do zoznamu pod názvom: %2 + + + + Could not add database %1 to list. + Nemôžem pridaÅ¥ databázu %1 do zoznamu. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + ZatvoriÅ¥ aj napriek tomu + + + + Don't close + NezatváraÅ¥ + + + + MultiEditor + + + Null value + multieditor + Hodnota null + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Cudzí klÃºÄ + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Iba na Äítanie + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Dátum + + + + MultiEditorDateTimePlugin + + + Date & time + Dátum a Äas + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Číslo + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Vystrihnúť + + + + Copy + KopírovaÅ¥ + + + + Paste + VložiÅ¥ + + + + Delete + VymazaÅ¥ + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + Nové obmedzenie + + + + + Primary Key + new constraint dialog + Primárny klÃºÄ + + + + + Foreign Key + new constraint dialog + Cudzí klÃºÄ + + + + + Unique + new constraint dialog + JedineÄnosÅ¥ + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Nie NULL + + + + Collate + new constraint dialog + Porovnanie + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Prednastavená hodnota + + + + NewVersionDialog + + + SQLiteStudio updates + Aktualizácie SQLiteStudia + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + KontrolovaÅ¥ aktualizácie pri Å¡tarte + + + + Not now. + Nie teraz. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + NaplniÅ¥ tabuľku + + + + Database + Databáza + + + + Table + Tabuľka + + + + Columns + Stĺpce + + + + Number of rows to populate: + PoÄet riadkov na naplnenie: + + + + Populate + populate dialog button + NaplniÅ¥ + + + + Abort + ZruÅ¡iÅ¥ + + + + Configure + KonfigurovaÅ¥ + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Vyberte databázu s tabuľkou na naplnenie + + + + Select table to populate + Vyberte tabuľku na naplnenie + + + + You have to select at least one column. + Musíte vybraÅ¥ minimálne jeden stĺpec. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + Pri konflikte: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + Názov porovnánavania: %1 + + + + Data grid view + Tabuľkové zobrazenie dát + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + KopírovaÅ¥ obsah buniek do schránky + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + VložiÅ¥ obsah buniek zo schránky + + + + Set empty value to selected cell(s) + VymazaÅ¥ hodnoty z vybraných buniek + + + + Set NULL value to selected cell(s) + NastaviÅ¥ NULL hodnotu vo vybraných bunkách + + + + Commit changes to cell(s) contents + PotvrdiÅ¥ zmeny v bunkách + + + + Rollback changes to cell(s) contents + VrátiÅ¥ späť zmeny v bunkách + + + + Delete selected data row + VymazaÅ¥ vybraný riadok + + + + Insert new data row + VložiÅ¥ nový riadok + + + + Open contents of selected cell in a separate editor + OtvoriÅ¥ obsah vybranej bunky v samostatnom editore + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Celkový poÄet strán: %1 + + + + Total rows loaded: %1 + Celkový poÄet riadkov: %1 + + + + Data view (both grid and form) + Zobrazenie dát (tabuľka a formulár) + + + + Refresh data + ObnoviÅ¥ dáta + + + + Switch to grid view of the data + Prepnúť na tabuľkové zobrazenie dát + + + + Switch to form view of the data + Prepnúť na formulárové zobrazenie dát + + + + Database list + Zoznam databáz + + + + Delete selected item + VymazaÅ¥ vybranú položku + + + + Clear filter contents + VymazaÅ¥ filter + + + + Refresh schema + ObnoviÅ¥ schému + + + + Refresh all schemas + ObnoviÅ¥ vÅ¡etky schémy + + + + Add database + PridaÅ¥ databázu + + + + Select all items + VybraÅ¥ vÅ¡etky položky + + + + Copy selected item(s) + KopírovaÅ¥ vybrané položky + + + + + + Paste from clipboard + VložiÅ¥ zo schránky + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tabuľky + + + + Indexes + Indexy + + + + Triggers + SpúšťaÄe + + + + Views + Pohľady + + + + Columns + Stĺpce + + + + Data form view + Formulárové zobrazenie dát + + + + Commit changes for current row + PotvrdiÅ¥ zmeny pre aktuálny riadok + + + + Rollback changes for current row + VrátiÅ¥ späť zmeny na aktuálnom riadku + + + + Go to first row on current page + PrejsÅ¥ na prvý riadok na aktuálnej strane + + + + Go to next row + PrejsÅ¥ na nasledujúci riadok + + + + Go to previous row + PrejsÅ¥ na predchádzajúci riadok + + + + Go to last row on current page + PrejsÅ¥ na posledný riadok na aktuálnej strane + + + + Insert new row + VložiÅ¥ nový riadok + + + + Delete current row + VymazaÅ¥ aktuálny riadok + + + + Main window + Hlavné okno + + + + Open SQL editor + OtvoriÅ¥ SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Predchádzajúce okno + + + + Next window + Nasledujúce okno + + + + Hide status area + SkryÅ¥ status okno + + + + Open user manual + Open user manual + + + + Open configuration dialog + OtvoriÅ¥ konfiguraÄné okno + + + + Open Debug Console + OtvoriÅ¥ ladiacu konzolu + + + + Open CSS Console + OtvoriÅ¥ CSS konzolu + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Úprava hodnôt v bunkách + + + + + Cut selected text + Vystrihnúť vybraný text + + + + + Copy selected text + KopírovaÅ¥ vybraný text + + + + + Delete selected text + VymazaÅ¥ vybraný text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + OznaÄiÅ¥ vÅ¡etko + + + + Save contents into a file + UložiÅ¥ SQL do súboru + + + + Load contents from a file + NaÄítaÅ¥ SQL zo súboru + + + + Find in text + NájsÅ¥ v SQL + + + + Find next + NájsÅ¥ Äalší + + + + Find previous + NájsÅ¥ predchádzajúci + + + + Replace in text + NahradiÅ¥ v SQL + + + + Delete current line + VymazaÅ¥ aktuálny riadok + + + + Request code assistant + OtvoriÅ¥ SQL pomocníka + + + + Format contents + FormátovaÅ¥ SQL + + + + Move selected block of text one line down + Presunúť blok kódu o riadok nižšie + + + + Move selected block of text one line up + Presunúť blok kódu o riadok vyššie + + + + Copy selected block of text and paste it a line below + KopírovaÅ¥ blok kódu a vložiÅ¥ ho na riadok nižšie + + + + Copy selected block of text and paste it a line above + KopírovaÅ¥ blok kódu a vložiÅ¥ ho na riadok vyššie + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + VÅ¡etky SQLite databázy + + + + All files + VÅ¡etky súbory + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + Okno SQL editora + + + + Execute query + VykonaÅ¥ dotaz + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + VykonaÅ¥ "%1" dotaz + + + + Switch current working database to previous on the list + Prepnúť sa na predchádzajúcu databázu v zozname + + + + Switch current working database to next on the list + Prepnúť sa na nasledujúcu databázu v zozname + + + + Go to next editor tab + Prechod na nasledujúcu záložku editora + + + + Go to previous editor tab + Prechod na predchádzajúcu záložku editora + + + + Move keyboard input focus to the results view below + Prepnúť kurzor na výsledky + + + + Move keyboard input focus to the SQL editor above + Prepnúť kurzor do editora + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Okno tabuľky + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + ObnoviÅ¥ Å¡truktúru tabuľky + + + + Add new column + PridaÅ¥ nový stĺpec + + + + Edit selected column + UpraviÅ¥ vybraný stĺpec + + + + Delete selected column + VymazaÅ¥ vybraný stĺpec + + + + Export table data + ExportovaÅ¥ dáta z tabuľky + + + + Import data to the table + ImportovaÅ¥ dáta do tabuľky + + + + Add new table constraint + PridaÅ¥ nové obmedzenie + + + + Edit selected table constraint + UpraviÅ¥ vybrané obmedzenie + + + + Delete selected table constraint + VymazaÅ¥ vybrané obmedzenie + + + + Refresh table index list + ObnoviÅ¥ zoznam indexov + + + + Add new index + PridaÅ¥ nový index + + + + Edit selected index + UpraviÅ¥ vybraný index + + + + Delete selected index + VymazaÅ¥ vybraný index + + + + Refresh table trigger list + ObnoviÅ¥ zoznam spúšťaÄov + + + + + Add new trigger + PridaÅ¥ nový spúšťaÄ + + + + + Edit selected trigger + UpraviÅ¥ vybraný spúšťaÄ + + + + + Delete selected trigger + VymazaÅ¥ vybraný spúšťaÄ + + + + + Go to next tab + Prechod na nasledujúcu záložku + + + + + Go to previous tab + Prechod na predchádzajúcu záložku + + + + A view window + Okno pohľadu + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + NájsÅ¥ alebo nahradiÅ¥ + + + + Find: + NájsÅ¥: + + + + Case sensitive + RozliÅ¡ovaÅ¥ veľké písmená + + + + Search backwards + HľadaÅ¥ spätne + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + NahradiÅ¥ a +nájsÅ¥ Äalší + + + + Replace with: + NahradiÅ¥ s: + + + + Replace all + NahradiÅ¥ vÅ¡etko + + + + Find + NájsÅ¥ + + + + SortDialog + + + Sort by columns + ZoradiÅ¥ podľa stĺpcov + + + + + Column + Stĺpec + + + + + Order + Usporiadanie + + + + Sort by: %1 + ZoradiÅ¥ podľa : %1 + + + + Move column up + Posunúť stĺpec hore + + + + Move column down + Posunúť stĺpec dole + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Vystrihnúť + + + + Copy + sql editor + KopírovaÅ¥ + + + + Paste + sql editor + VložiÅ¥ + + + + Delete + sql editor + VymazaÅ¥ + + + + Select all + sql editor + VybraÅ¥ vÅ¡etko + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + FormátovaÅ¥ SQL + + + + Save SQL to file + sql editor + UložiÅ¥ SQL do súboru + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + NaÄítaÅ¥ SQL zo súboru + + + + Delete line + sql editor + VymazaÅ¥ riadok + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + NájsÅ¥ + + + + Find next + sql editor + NájsÅ¥ Äalší + + + + Find previous + sql editor + NájsÅ¥ predchádzajúci + + + + Replace + sql editor + NahradiÅ¥ + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Nemôžem otvoriÅ¥ súbor '%1' pre zápis: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + UložiÅ¥ do súboru + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + OtvoriÅ¥ súbor + + + + Could not open file '%1' for reading: %2 + Nemôžem otvoriÅ¥ súbor '%1' na Äítanie: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Dosiahnutý koniec súboru. Kliknite na tlaÄidlo NájsÅ¥ pre hľadanie od zaÄiatku súboru. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Stĺpec: + + + + Data type: + data view + Datový typ: + + + + Table: + data view tooltip + Tabuľka: + + + + Constraints: + data view tooltip + Obmedzenia: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Nemôže byÅ¥ spustených viacero dotazov súÄasne. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Nemôžem potrdiÅ¥ dáta bunky, ktorá odkazuje na už uzatvorenú databázu. + + + + Could not begin transaction on the database. Details: %1 + Nemôžem zaÄaÅ¥ tranzakciu na databáze. Detaily: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + Vyskytla sa chyba poÄas vracania späť tranzakcie: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Nastal pokus o potvrdenie zmien v bunke, ktorú nieje možné upravovaÅ¥ (napriek tomu bola upravená a Äaká na potvrdenie)! Toto je chyba. Prosím nahláste ju. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Vyskytla sa chyba poÄas vykonávania SQL dotazu na databáze '%1': %2 + + + + Error while loading query results: %1 + Vyskytla sa chyba poÄas naÄítavania výsledkov dotazu: %1 + + + + Insert multiple rows + VložiÅ¥ viacero riadkov + + + + Number of rows to insert: + PoÄet vkládaných riadkov: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + KopírovaÅ¥ + + + + Copy with headers + Copy with headers + + + + Copy as... + KopírovaÅ¥ ako... + + + + Paste + VložiÅ¥ + + + + Paste as... + VložiÅ¥ ako... + + + + Set NULL values + NastaviÅ¥ null hodnoty + + + + Erase values + VymazaÅ¥ hodnoty + + + + Commit + PotvrdiÅ¥ + + + + Rollback + VrátiÅ¥ späť + + + + Commit selected cells + PotvrdiÅ¥ vybrané bunky + + + + Rollback selected cells + VrátiÅ¥ späť vybrané bunky + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + VybraÅ¥ stĺpce na zoradenie podľa + + + + Remove custom sorting + OdstrániÅ¥ užívateľské triedenie + + + + Insert row + VložiÅ¥ riadok + + + + Insert multiple rows + VložiÅ¥ viacero riadkov + + + + Delete selected row + VymazaÅ¥ viacero riadkov + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + UpraviÅ¥ hodnotu v editory + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + Neboli vybrané žiadne položky na vloženie obsahu schránky. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + UpraviÅ¥ hodnotu + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Vyskytla sa chyba poÄas mazania riadka z tabuľky %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databázy + + + + Register in all databases + RegistrovaÅ¥ vo vÅ¡etkých databázach + + + + Register in following databases: + RegistrovaÅ¥ v nasledujúcich databázach: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + VÅ¡etky súbory (*) + + + + Open file + OtvoriÅ¥ súbor + + + + StatusField + + + Status + Status + + + + Copy + KopírovaÅ¥ + + + + Clear + VymazaÅ¥ + + + + TableConstraintsModel + + + Type + table constraints + Typ + + + + Details + table constraints + Detaily + + + + Name + table constraints + Názov + + + + TableForeignKeyPanel + + + Foreign table: + Cudzia tabuľka: + + + + Columns + Stĺpce + + + + Local column + Lokálny stĺpec + + + + Foreign column + Cudzí stĺpec + + + + Reactions + Reakcie + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Pomenovanie obmedzenia + + + + Constraint name + Názov obmedzenia + + + + Pick the foreign column. + Vyberte cudzí stĺpec. + + + + Pick the foreign table. + Vyberte cudziu tabuľku. + + + + Select at least one foreign column. + Vyberte aspoň jeden cudzí stĺpec. + + + + Enter a name of the constraint. + Zadajte názov obmedzenia. + + + + Foreign column + table constraints + Cudzí stĺpec + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Stĺpce + + + + Column + Stĺpec + + + + Collation + Porovnávanie + + + + Sort + ZoradiÅ¥ + + + + Valid only for a single column with INTEGER data type + Platné len pre jeden stĺpec s datovým typom INTEGER + + + + Autoincrement + Autoinkrement + + + + Named constraint + Pomenovanie obmedzenia + + + + Constraint name + Názov obmedzenia + + + + On conflict + Pri konflikte + + + + Collate + table constraints + PorovnaÅ¥ + + + + Sort order + table constraints + ZoradiÅ¥ + + + + Select at least one column. + Vyberte minimálne jeden stĺpec. + + + + Enter a name of the constraint. + Zadajte názov obmedzenia. + + + + TableStructureModel + + + Name + table structure columns + Názov + + + + Data type + table structure columns + Datový typ + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Prednastavená hodnota + + + + TableWindow + + + Structure + Å truktúra + + + + Table name: + Názov tabuľky: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Dáta + + + + Constraints + ObmedzovaÄe + + + + Indexes + Indexy + + + + Triggers + SpúšťaÄe + + + + DDL + DDL + + + + Export table + table window + ExportovaÅ¥ tabuľku + + + + Import data to table + table window + ImportovaÅ¥ dáta do tabuľky + + + + Populate table + table window + NaplniÅ¥ tabuľku + + + + Refresh structure + table window + ObnoviÅ¥ Å¡truktúru + + + + Commit structure changes + table window + PotvrdiÅ¥ zmeny Å¡truktúr + + + + Rollback structure changes + table window + VrátiÅ¥ späť zmeny Å¡truktúr + + + + Add column + table window + PridaÅ¥ stĺpec + + + + Edit column + table window + UpraviÅ¥ stĺpec + + + + + Delete column + table window + VymazaÅ¥ stĺpec + + + + Move column up + table window + Posunúť stĺpec hore + + + + Move column down + table window + Posunúť stĺpec dole + + + + Create similar table + table window + VytvoriÅ¥ rovnakú tabuľku + + + + Reset autoincrement value + table window + ResetovaÅ¥ hodnotu autoinkrementu + + + + Add table constraint + table window + PridaÅ¥ obmedzenie + + + + Edit table constraint + table window + UpraviÅ¥ obmedzenie + + + + Delete table constraint + table window + VymazaÅ¥ obmedzenie + + + + Move table constraint up + table window + Posunúť obmedzenie hore + + + + Move table constraint down + table window + Posunúť obmedzenie dole + + + + Add table primary key + table window + PridaÅ¥ primárny kÄ¾ÃºÄ + + + + Add table foreign key + table window + PridaÅ¥ cudzí kÄ¾ÃºÄ + + + + Add table unique constraint + table window + PridaÅ¥ jedineÄné obmedzenie + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + ObnoviÅ¥ zoznam indexov + + + + + Create index + table window + VytvoriÅ¥ index + + + + Edit index + table window + UpraviÅ¥ index + + + + Delete index + table window + VymazaÅ¥ index + + + + Refresh trigger list + table window + ObnoviÅ¥ zoznam spúšťaÄov + + + + + Create trigger + table window + VytvoriÅ¥ spúšťaÄ + + + + Edit trigger + table window + UpraviÅ¥ spúšťaÄ + + + + Delete trigger + table window + VymazaÅ¥ spúšťaÄ + + + + Are you sure you want to delete column '%1'? + table window + Ste si istý, že chcete vymazaÅ¥ stĺpec '%1' ? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + ResetovaÅ¥ autoinkrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Ste si istý, že chcete zresetovaÅ¥ hodnotu autoinkrementu pre tabuľku %1 ? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + Vyskytla sa chyba pri pokuse o zresetovanie hodnoty autoinkrementu pre tebuľku '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Ste si istý, že chcete vymazaÅ¥ obmedzenie '%1'? + + + + Delete constraint + table window + VymazaÅ¥ obmedzenie + + + + Cannot export, because no export plugin is loaded. + Nemôžem exportovaÅ¥, lebo nebol naÄítaný žiaden plugin na export. + + + + Cannot import, because no import plugin is loaded. + Nemôžem importovaÅ¥, lebo nebol naÄítaný žiaden plugin na import. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + ChoÄ späť na záložku Å¡truktúr + + + + Commit modifications and browse data. + PotvrdiÅ¥ zmeny a prezeraÅ¥ dáta. + + + + Name + table window indexes + Názov + + + + Unique + table window indexes + JedineÄný + + + + Columns + table window indexes + Stĺpce + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Názov + + + + Event + table window triggers + UdalosÅ¥ + + + + Condition + table window triggers + Podmienka + + + + Details + table window triggers + Detaily + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + VybraÅ¥ vÅ¡etko + + + + Deselect all + ZruÅ¡iÅ¥ výber + + + + TriggerDialog + + + + Trigger + SpúšťaÄ + + + + On table: + Na tabuľke: + + + + Action: + Akcia: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Názov spúšťaÄa: + + + + When: + Kedy: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Kód: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Zadajte platnú podmienku. + + + + Enter a valid trigger code. + Zadajte validný kód spúšťaÄa. + + + + Error + trigger dialog + Chyba + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Dotaz + + + + View name: + Názov pohľadu: + + + + Output column names + Output column names + + + + + Data + Dáta + + + + Triggers + SpúšťaÄe + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + Nový pohľad %1 + + + + Database + Database + + + + Refresh the view + view window + ObnoviÅ¥ pohľad + + + + Commit the view changes + view window + PotvrdiÅ¥ zmeny v pohľade + + + + Rollback the view changes + view window + VrátiÅ¥ späť zmeny v pohľade + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + PridaÅ¥ stĺpec + + + + Edit column + view window + UpraviÅ¥ stĺpec + + + + Delete column + view window + VymazaÅ¥ stĺpec + + + + Move column up + view window + Posunúť stĺpec hore + + + + Move column down + view window + Posunúť stĺpec dole + + + + Refresh trigger list + view window + ObnoviÅ¥ zoznam spúšťaÄov + + + + Create new trigger + view window + VytvoriÅ¥ nový spúšťaÄ + + + + Edit selected trigger + view window + UpraviÅ¥ vybraný spúšťaÄ + + + + Delete selected trigger + view window + VymazaÅ¥ vybraný spúšťaÄ + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Nemôžem naÄítaÅ¥ dáta z pohľadu %1. Detaily chyby: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + ChoÄ späť na záložku Å¡truktúr + + + + Commit modifications and browse data. + PotvrdiÅ¥ zmeny a prezeraÅ¥ dáta. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Nemôžem potvrdiÅ¥ zmeny v pohľade. Chyba: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Názov + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Podmienka + + + + Details + table window triggers + Detaily + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + SELECT nemôže byÅ¥ analyzovaný. Prosím opravte dotaz a skúste to znovu. +Detaily: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sr_SP.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sr_SP.ts new file mode 100644 index 0000000..316dbbb --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sr_SP.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sv_SE.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sv_SE.ts new file mode 100644 index 0000000..694d5ea --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sv_SE.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_tr_TR.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_tr_TR.ts new file mode 100644 index 0000000..2decd12 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_tr_TR.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + SQLiteStudio hakkında ve lisanslar + + + + About + Hakkında + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Ücretsiz, açık kaynaklı, platform bağımsız SQLite veritabanı yöneticisi.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Yazar ve aktif sorumlu:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Lisanslar + + + + Environment + Ortam + + + + Icon directories + İkon dizinleri + + + + Form directories + Form dizinleri + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Eklenti dizinleri + + + + Configuration directory + Ayar dizini + + + + Application directory + Uygulama dizini + + + + Qt version: + Qt versiyonu: + + + + SQLite 3 version: + SQLite 3 versiyonu: + + + + Portable distribution. + Taşınabilir dağıtım. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + İşletim sistemi tarafından yönetilen dağıtım. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>İçindekiler:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Sorgu parametreleri + + + + Please provide values for query parameters + Sorgu parametreleri için deÄŸerleri giriniz + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + KarşılaÅŸtırmaları filtrele + + + + Databases + Veritabanları + + + + Register in all databases + Bütün veritabanlarına kayıt ol + + + + Register in following databases: + Åžu veritabanlarına kayıt ol: + + + + Implementation code: + Implementasyon kodu: + + + + Collation name: + KarşılaÅŸtırma adı: + + + + Implementation language: + Implementasyon dili: + + + + Collations editor + KarşılaÅŸtırma editörü + + + + Commit all collation changes + Bütün deÄŸiÅŸiklikleri iÅŸle + + + + Rollback all collation changes + Bütün karşılaÅŸtırma deÄŸiÅŸikliklerini geri al + + + + Create new collation + Yeni karşılaÅŸtırma yarat + + + + Delete selected collation + Seçili karşılaÅŸtırmayı sil + + + + Editing collations manual + KarşılaÅŸtırmaları düzenleme kılavuzu + + + + Enter a non-empty, unique name of the collation. + Yeni bir karşılaÅŸtırma adı giriniz. + + + + Pick the implementation language. + Uygulama dilini seçiniz. + + + + Enter a non-empty implementation code. + Implementasyon kodu giriniz. + + + + Collations editor window has uncommitted modifications. + KarşılaÅŸtırma editöründe kaydedilmemiÅŸ deÄŸiÅŸiklikler var. + + + + ColorButton + + + Pick a color + Renk seçin + + + + ColumnCollatePanel + + + Collation name: + KarşılaÅŸtırma adı: + + + + Named constraint: + Adlandırılmış constraint: + + + + Enter a name of the constraint. + Constraint adı girin. + + + + Enter a collation name. + KarşılaÅŸtırma adı giriniz. + + + + ColumnDefaultPanel + + + Default value: + Varsayılan deÄŸer: + + + + Named constraint: + Adlandırılmış constraint: + + + + Enter a default value expression. + Varsayılan deÄŸer girin. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Geçersiz varsayılan deÄŸer: %1. EÄŸer basit metin deÄŸerini kullanmak istiyorsanız, tırnak içinde yazmayı unutmayınız. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Geçersiz varsayılan deÄŸer. EÄŸer basit metin deÄŸerini kullanmak istiyorsanız, tırnak içinde yazmayı unutmayınız. + + + + Enter a name of the constraint. + Constraint adı girin. + + + + ColumnDialog + + + Column + Kolon + + + + Name and type + Adı ve tipi + + + + Scale + Ölçek + + + + Precision + Hassasiyet + + + + Data type: + Veri tipi: + + + + Column name: + Sütun ismi: + + + + Size: + Boyut: + + + + Constraints + Kısıtlamalar + + + + Generated value + Üretilen DeÄŸerler + + + + Unique + Benzersiz + + + + + + + + + + + Configure + Yapılandır + + + + Foreign Key + Yabancı Anahtar + + + + Collate + Harmanla + + + + Not NULL + BoÅŸ(null) olamaz + + + + Check condition + Durumu kontrol et + + + + Primary Key + Birincil Anahtar + + + + Default + Varsayılan + + + + Advanced mode + GeliÅŸmiÅŸ biçim + + + + Add constraint + column dialog + Kısıtlama ekle + + + + Edit constraint + column dialog + Kısıtlmayı düzenle + + + + + Delete constraint + column dialog + Kısıtlamayı Sil + + + + Move constraint up + column dialog + Kısıtlamayı yukarı taşı + + + + Move constraint down + column dialog + Kısıtlamayı aÅŸağı taşı + + + + Add a primary key + column dialog + Birincil anahtar ekle + + + + Add a foreign key + column dialog + Yabancı anahtar ekle + + + + Add an unique constraint + column dialog + Benzersiz kısıtlama ekle + + + + Add a check constraint + column dialog + Kontrol kısıtlaması ekle + + + + Add a not null constraint + column dialog + BoÅŸ olmayan kısıtlama ekle + + + + Add a collate constraint + column dialog + Harmanlama kısıtlaması ekle + + + + Add a generated value constraint + column dialog + OluÅŸturulan deÄŸer kısıtlaması ekle + + + + Add a default constraint + column dialog + Varsayılan kısıtlama ekle + + + + Are you sure you want to delete constraint '%1'? + column dialog + '%1' kısıtlamasını silmek istediÄŸinizden emin misiniz? + + + + Correct the constraint's configuration. + ' kısıtlama yapılandırmasını düzeltin. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + INTEGER PRIMARY KEY sütunları için ölçeÄŸe izin verilmez. + + + + Precision cannot be defined without the scale. + Hassasiyet, ölçek olmadan tanımlanamaz. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + BİRİNCİL ANAHTAR içinde OTOMATİK ARTIRMA etkinleÅŸtirilmiÅŸse, INTEGER dışında bir tür kullanılamaz. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + BİRİNCİL ANAHTAR içinde OTOMATİK ARTIRMA etkinleÅŸtirildiÄŸinden INTEGER türü uygulandı. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + TAMSAYI BİRİNCİL anahtar sütunları için kesinliÄŸe izin verilmez. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Tip + + + + Name + column dialog constraints + Ad + + + + Details + column dialog constraints + Detaylar + + + + ColumnForeignKeyPanel + + + Foreign table: + Yabancı tablo: + + + + Foreign column: + Yabancı sütun: + + + + Reactions + Tepkiler + + + + Deferred foreign key + ErtelenmiÅŸ yabancı anahtar + + + + Named constraint + Adlandırılmış kısıtlama + + + + Constraint name + Kısıtlama adı + + + + Pick the foreign table. + Yabancı tabloyu seç. + + + + Pick the foreign column. + Yabancı sütunu seç. + + + + Enter a name of the constraint. + Kısıtlamanın adını girin. + + + + ColumnGeneratedPanel + + + Generating code: + Kod oluÅŸturma: + + + + Explicit type: + Açık tip: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Adlandırılmış kısıtlama: + + + + Enter the column value generating expression. + Sütun deÄŸeri üreten ifadeyi girin. + + + + Invalid value generating expression: %1. + Geçersiz deÄŸer üreten ifade: %1. + + + + Invalid value generating expression. + Geçersiz deÄŸer üreten ifade. + + + + Enter a name of the constraint. + Kısıtlamanın adını girin. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Otomatik artış + + + + Sort order: + Sıralama düzeni: + + + + Named constraint: + Adlandırılmış kısıtlama: + + + + On conflict: + UyuÅŸmazlıkda: + + + + Enter a name of the constraint. + Kısıtlamanın adını girin. + + + + Descending order is not allowed with AUTOINCREMENT. + AUTOINCREMENT ile azalan sıraya izin verilmez. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Adlandırılmış kısıtlama: + + + + On conflict: + UyuÅŸmazlıkda: + + + + Enter a name of the constraint. + Kısıtlamanın adını girin. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Sütun: %1 + + + + Table: %1 + completer statusbar + Tablo: %1 + + + + Index: %1 + completer statusbar + Dizin: %1 + + + + Trigger: %1 + completer statusbar + Tetikleyici: %1 + + + + View: %1 + completer statusbar + Görünüm: %1 + + + + Database: %1 + completer statusbar + Veritabanı: %1 + + + + Keyword: %1 + completer statusbar + Anahtar Kelime: %1 + + + + Function: %1 + completer statusbar + Fonksyion: %1 + + + + Operator: %1 + completer statusbar + Operatör: %1 + + + + String + completer statusbar + Yazı + + + + Number + completer statusbar + Sayı + + + + Binary data + completer statusbar + İkili veri + + + + Collation: %1 + completer statusbar + KarşılaÅŸtırma: %1 + + + + Pragma function: %1 + completer statusbar + Pragma iÅŸlevi: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Yapılandırma + + + + Search + Ara + + + + General + Genel + + + + Keyboard shortcuts + Klavye kısayolları + + + + Look & feel + Görünüş & İzlenim + + + + Style + Biçim + + + + Fonts + Yazı tipi + + + + Code colors + Code colors + + + + + Database list + Veritabanı listesi + + + + Code assistant + Code assistant + + + + Data browsing + Veri tarama + + + + Data editors + Veri düzenleyicileri + + + + Plugins + Eklentiler + + + + Code formatters + Kod biçimleyici + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + Kapatılırsa, sütunlar CREATE TABLE deyimine yazıldıkları sıraya göre sıralanır. + + + + Sort table columns alphabetically + Tablo sütunlarını alfabetik olarak sırala + + + + Expand tables node when connected to a database + Bir veritabanına baÄŸlandığında tablolar düğümünü geniÅŸlet + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Listede ek etiketleri görüntüle + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + Düzenli tablolar için etiketler, her tablo için sütun, dizin ve tetikleyici sayısını gösterir. + + + + Display labels for regular tables + Normal tablolar için etiketleri görüntüle + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Sanal tablolar için etiketleri görüntüleme + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + GeçmiÅŸ boyutu: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Güncellemeler + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Oturum + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Eylem + + + + Key combination + Anahtar Kombinasyonu + + + + + Language + Dil + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Ön İzleme + + + + Enabled + Etkin + + + + Disabled + Devre dışı + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL düzenleyici yazı tipi + + + + Database list font + Veritabanı listesi yazı tipi + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Açıklama: + + + + Category: + plugin details + Kategori: + + + + Version: + plugin details + Sürüm: + + + + Author: + plugin details + Yazar: + + + + Internal name: + plugin details + Dahili İsim: + + + + Dependencies: + plugin details + Gereksinimler: + + + + Conflicts: + plugin details + Çakışmalar: + + + + Plugin details + Eklenti Ayrıntıları + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Detaylar + + + + No plugins in this category. + Bu kategoride hiçbir eklenti yok. + + + + Add new data type + Yeni veri türü ekle + + + + Rename selected data type + Seçili veri türünü yeniden adlandır + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Detaylar + + + + Name + table constraints + Ad + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS uç birimi + + + + DataView + + + Filter data + data view + Verilere filtrele + + + + Grid view + Izgara görünümü + + + + Form view + Form Görüntüsü + + + + Refresh table data + data view + Tablo verilerini Yenile + + + + First page + data view + İlk sayfa + + + + Previous page + data view + Önceki sayfa + + + + Next page + data view + Sonraki sayfa + + + + Last page + data view + Son sayfa + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Üstteki sekmeler + + + + Tabs at bottom + data view + Alttaki sekmeler + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Satır: %1 + + + + Filter + Filtre + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Düzenli ifadeleri filtrele + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Filtreyi uygula + + + + DbDialog + + + Database + Veritabanı + + + + Database type + Veritabanı türü + + + + Database driver + Veritabanı Sürücüsü + + + + + File + Dosya + + + + Name (on the list) + Name (on the list) + + + + Options + Åžeçenekler + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test baÄŸlantısı + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Gözat + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Tabloyu sil + + + + Are you sure you want to delete table %1? + %1 tabloyu silmek istediÄŸinizden emin misiniz? + + + + Delete index + Dizini sil + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_uk_UA.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_uk_UA.ts new file mode 100644 index 0000000..5ecb76a --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_uk_UA.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_vi_VN.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_vi_VN.ts new file mode 100644 index 0000000..d703c75 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_vi_VN.ts @@ -0,0 +1,7111 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + About SQLiteStudio and licenses + + + + About + About + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + Licenses + + + + Environment + Environment + + + + Icon directories + Icon directories + + + + Form directories + Form directories + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + Plugin directories + + + + Configuration directory + Configuration directory + + + + Application directory + Application directory + + + + Qt version: + Qt version: + + + + SQLite 3 version: + SQLite 3 version: + + + + Portable distribution. + Portable distribution. + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + Operating system managed distribution. + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>Table of contents:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + Query parameters + + + + Please provide values for query parameters + Please provide values for query parameters + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + Filter collations + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Implementation code: + Implementation code: + + + + Collation name: + Collation name: + + + + Implementation language: + Implementation language: + + + + Collations editor + Collations editor + + + + Commit all collation changes + Commit all collation changes + + + + Rollback all collation changes + Rollback all collation changes + + + + Create new collation + Create new collation + + + + Delete selected collation + Delete selected collation + + + + Editing collations manual + Editing collations manual + + + + Enter a non-empty, unique name of the collation. + Enter a non-empty, unique name of the collation. + + + + Pick the implementation language. + Pick the implementation language. + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + Collations editor window has uncommitted modifications. + Collations editor window has uncommitted modifications. + + + + ColorButton + + + Pick a color + Pick a color + + + + ColumnCollatePanel + + + Collation name: + Collation name: + + + + Named constraint: + Named constraint: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Enter a collation name. + Enter a collation name. + + + + ColumnDefaultPanel + + + Default value: + Default value: + + + + Named constraint: + Named constraint: + + + + Enter a default value expression. + Enter a default value expression. + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnDialog + + + Column + Column + + + + Name and type + Name and type + + + + Scale + Scale + + + + Precision + Precision + + + + Data type: + Data type: + + + + Column name: + Column name: + + + + Size: + Size: + + + + Constraints + Constraints + + + + Generated value + Generated value + + + + Unique + Unique + + + + + + + + + + + Configure + Configure + + + + Foreign Key + Foreign Key + + + + Collate + Collate + + + + Not NULL + Not NULL + + + + Check condition + Check condition + + + + Primary Key + Primary Key + + + + Default + Default + + + + Advanced mode + Advanced mode + + + + Add constraint + column dialog + Add constraint + + + + Edit constraint + column dialog + Edit constraint + + + + + Delete constraint + column dialog + Delete constraint + + + + Move constraint up + column dialog + Move constraint up + + + + Move constraint down + column dialog + Move constraint down + + + + Add a primary key + column dialog + Add a primary key + + + + Add a foreign key + column dialog + Add a foreign key + + + + Add an unique constraint + column dialog + Add an unique constraint + + + + Add a check constraint + column dialog + Add a check constraint + + + + Add a not null constraint + column dialog + Add a not null constraint + + + + Add a collate constraint + column dialog + Add a collate constraint + + + + Add a generated value constraint + column dialog + Add a generated value constraint + + + + Add a default constraint + column dialog + Add a default constraint + + + + Are you sure you want to delete constraint '%1'? + column dialog + Are you sure you want to delete constraint '%1'? + + + + Correct the constraint's configuration. + Correct the constraint's configuration. + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + Scale is not allowed for INTEGER PRIMARY KEY columns. + + + + Precision cannot be defined without the scale. + Precision cannot be defined without the scale. + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + Precision is not allowed for INTEGER PRIMARY KEY columns. + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + Type + + + + Name + column dialog constraints + Name + + + + Details + column dialog constraints + Details + + + + ColumnForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Foreign column: + Foreign column: + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign table. + Pick the foreign table. + + + + Pick the foreign column. + Pick the foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnGeneratedPanel + + + Generating code: + Generating code: + + + + Explicit type: + Explicit type: + + + + Use "GENERATED ALWAYS" keywords + Use "GENERATED ALWAYS" keywords + + + + Named constraint: + Named constraint: + + + + Enter the column value generating expression. + Enter the column value generating expression. + + + + Invalid value generating expression: %1. + Invalid value generating expression: %1. + + + + Invalid value generating expression. + Invalid value generating expression. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ColumnPrimaryKeyPanel + + + Autoincrement + Autoincrement + + + + Sort order: + Sort order: + + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Descending order is not allowed with AUTOINCREMENT. + Descending order is not allowed with AUTOINCREMENT. + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + Named constraint: + + + + On conflict: + On conflict: + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + CompleterWindow + + + Column: %1 + completer statusbar + Column: %1 + + + + Table: %1 + completer statusbar + Table: %1 + + + + Index: %1 + completer statusbar + Index: %1 + + + + Trigger: %1 + completer statusbar + Trigger: %1 + + + + View: %1 + completer statusbar + View: %1 + + + + Database: %1 + completer statusbar + Database: %1 + + + + Keyword: %1 + completer statusbar + Keyword: %1 + + + + Function: %1 + completer statusbar + Function: %1 + + + + Operator: %1 + completer statusbar + Operator: %1 + + + + String + completer statusbar + String + + + + Number + completer statusbar + Number + + + + Binary data + completer statusbar + Binary data + + + + Collation: %1 + completer statusbar + Collation: %1 + + + + Pragma function: %1 + completer statusbar + Pragma function: %1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + Configuration + + + + Search + Search + + + + General + General + + + + Keyboard shortcuts + Keyboard shortcuts + + + + Look & feel + Look & feel + + + + Style + Style + + + + Fonts + Fonts + + + + Code colors + Code colors + + + + + Database list + Database list + + + + Code assistant + Code assistant + + + + Data browsing + Data browsing + + + + Data editors + Data editors + + + + Plugins + Plugins + + + + Code formatters + Code formatters + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + + Sort table columns alphabetically + Sort table columns alphabetically + + + + Expand tables node when connected to a database + Expand tables node when connected to a database + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + + Display additional labels on the list + Display additional labels on the list + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + + Display labels for regular tables + Display labels for regular tables + + + + Virtual tables will be marked with a 'virtual' label. + Virtual tables will be marked with a 'virtual' label. + + + + Display labels for virtual tables + Display labels for virtual tables + + + + Expand views node when connected to a database + Expand views node when connected to a database + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + + Sort objects (tables, indexes, triggers and views) alphabetically + Sort objects (tables, indexes, triggers and views) alphabetically + + + + Display system tables and indexes on the list + Display system tables and indexes on the list + + + + Database dialog window + Database dialog window + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + + + + Do not mark database to be "permanent" by default + Do not mark database to be "permanent" by default + + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + + + + Try to bypass dialog completly when dropping database file onto the list + Try to bypass dialog completly when dropping database file onto the list + + + + Data browsing and editing + Data browsing and editing + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + + Number of memorized table populating configurations + Number of memorized table populating configurations + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + + + + Number of data rows per page: + Number of data rows per page: + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + + + + Show column and row details tooltip in data view + Show column and row details tooltip in data view + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + + + + Keep NULL value when entering empty value + Keep NULL value when entering empty value + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + Use DEFAULT value (if defined), when committing NULL value + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + + + + Limit number of rows for in case of dozens of columns + Limit number of rows for in case of dozens of columns + + + + Inserting new row in data grid + Inserting new row in data grid + + + + Before currently selected row + Before currently selected row + + + + After currently selected row + After currently selected row + + + + At the end of data view + At the end of data view + + + + Table windows + Table windows + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open Table Windows with the data tab for start + Open Table Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + + + + Place data tab as first tab in a Table Window + Place data tab as first tab in a Table Window + + + + View windows + View windows + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + + + + Open View Windows with the data tab for start + Open View Windows with the data tab for start + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + + + + Place data tab as first tab in a View Window + Place data tab as first tab in a View Window + + + + Data types + Data types + + + + Available editors: + Available editors: + + + + Editors selected for this data type: + Editors selected for this data type: + + + + Schema editing + Schema editing + + + + Number of DDL changes kept in history. + Number of DDL changes kept in history. + + + + DDL history size: + DDL history size: + + + + Don't show DDL preview dialog when committing schema changes + Don't show DDL preview dialog when committing schema changes + + + + SQL queries + SQL queries + + + + + Number of queries kept in the history. + Number of queries kept in the history. + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + History size: + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + + + + Execute only the query under the cursor + Execute only the query under the cursor + + + + Number of memorized query parameters + Number of memorized query parameters + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + Updates + + + + Automatically check for updates at startup + Automatically check for updates at startup + + + + Session + Session + + + + Restore last session (active MDI windows) after startup + Restore last session (active MDI windows) after startup + + + + Allow multiple instances of the application at the same time + Allow multiple instances of the application at the same time + + + + Status Field + Status Field + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + + + + Always open Status panel when new message is printed + Always open Status panel when new message is printed + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + Filter shortcuts by name or key combination + + + + Action + Action + + + + Key combination + Key combination + + + + + Language + Language + + + + Changing language requires application restart to take effect. + Changing language requires application restart to take effect. + + + + Compact layout + Compact layout + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + + + + Use compact layout + Use compact layout + + + + Main window dock areas + Main window dock areas + + + + Left and right areas occupy corners + Left and right areas occupy corners + + + + Top and bottom areas occupy corners + Top and bottom areas occupy corners + + + + Hide built-in plugins + Hide built-in plugins + + + + Current style: + Current style: + + + + Preview + Preview + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Active formatter plugin + Active formatter plugin + + + + SQL editor font + SQL editor font + + + + Database list font + Database list font + + + + Database list additional label font + Database list additional label font + + + + Data view font + Data view font + + + + Status field font + Status field font + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + Description: + + + + Category: + plugin details + Category: + + + + Version: + plugin details + Version: + + + + Author: + plugin details + Author: + + + + Internal name: + plugin details + Internal name: + + + + Dependencies: + plugin details + Dependencies: + + + + Conflicts: + plugin details + Conflicts: + + + + Plugin details + Plugin details + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (built-in) + + + + Details + Details + + + + No plugins in this category. + No plugins in this category. + + + + Add new data type + Add new data type + + + + Rename selected data type + Rename selected data type + + + + Delete selected data type + Delete selected data type + + + + Help for configuring data type editors + Help for configuring data type editors + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + The condition + + + + Named constraint: + Named constraint: + + + + On conflict + On conflict + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + ConstraintDialog + + + New constraint + constraint dialog + New constraint + + + + Create + constraint dialog + Create + + + + Edit constraint + dialog window + Edit constraint + + + + Apply + constraint dialog + Apply + + + + Primary key + table constraints + Primary key + + + + Foreign key + table constraints + Foreign key + + + + Unique + table constraints + Unique + + + + Not NULL + table constraints + Not NULL + + + + Check + table constraints + Check + + + + Generated + table constraints + Generated + + + + Collate + table constraints + Collate + + + + Default + table constraints + Default + + + + ConstraintTabModel + + + Table + table constraints + Table + + + + Column (%1) + table constraints + Column (%1) + + + + Scope + table constraints + Scope + + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS console + + + + DataView + + + Filter data + data view + Filter data + + + + Grid view + Grid view + + + + Form view + Form view + + + + Refresh table data + data view + Refresh table data + + + + First page + data view + First page + + + + Previous page + data view + Previous page + + + + Next page + data view + Next page + + + + Last page + data view + Last page + + + + Commit changes for selected cells + data view + Commit changes for selected cells + + + + Rollback changes for selected cells + data view + Rollback changes for selected cells + + + + Show grid view of results + data view + Show grid view of results + + + + Show form view of results + data view + Show form view of results + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + Tabs on top + + + + Tabs at bottom + data view + Tabs at bottom + + + + Place new rows above selected row + data view + Place new rows above selected row + + + + Place new rows below selected row + data view + Place new rows below selected row + + + + Place new rows at the end of the data view + data view + Place new rows at the end of the data view + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + + + + Row: %1 + Row: %1 + + + + Filter + Filter + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + + + + Filter by the Regular Expression + data view + Filter by the Regular Expression + + + + Filter by SQL expression + data view + Filter by SQL expression + + + + Show filter inputs per column + data view + Show filter inputs per column + + + + Apply filter + data view + Apply filter + + + + DbDialog + + + Database + Database + + + + Database type + Database type + + + + Database driver + Database driver + + + + + File + File + + + + Name (on the list) + Name (on the list) + + + + Options + Options + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + Permanent (keep it in configuration) + Permanent (keep it in configuration) + + + + Test connection + Test connection + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + Browse + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + Enter an unique database name. + + + + This name is already in use. Please enter unique name. + This name is already in use. Please enter unique name. + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + + + + Enter a database file path. + Enter a database file path. + + + + This database is already on the list under name: %1 + This database is already on the list under name: %1 + + + + Select a database type. + Select a database type. + + + + DbObjectDialogs + + + Delete table + Delete table + + + + Are you sure you want to delete table %1? + Are you sure you want to delete table %1? + + + + Delete index + Delete index + + + + Are you sure you want to delete index %1? + Are you sure you want to delete index %1? + + + + Delete trigger + Delete trigger + + + + Are you sure you want to delete trigger %1? + Are you sure you want to delete trigger %1? + + + + Delete view + Delete view + + + + Are you sure you want to delete view %1? + Are you sure you want to delete view %1? + + + + + Error while dropping %1: %2 + Error while dropping %1: %2 + + + + Delete objects + Delete objects + + + + Are you sure you want to delete following objects: +%1 + Are you sure you want to delete following objects: +%1 + + + + Cannot start transaction. Details: %1 + Cannot start transaction. Details: %1 + + + + Cannot commit transaction. Details: %1 + Cannot commit transaction. Details: %1 + + + + DbTree + + + Databases + Databases + + + + Filter by name + Filter by name + + + + Copy + Copy + + + + Paste + Paste + + + + Select all + Select all + + + + Create a group + Create a group + + + + Delete the group + Delete the group + + + + Rename the group + Rename the group + + + + &Add a database + &Add a database + + + + &Edit the database + &Edit the database + + + + &Remove the database + &Remove the database + + + + &Connect to the database + &Connect to the database + + + + &Disconnect from the database + &Disconnect from the database + + + + Import + Import + + + + &Export the database + &Export the database + + + + Vac&uum + Vac&uum + + + + &Integrity check + &Integrity check + + + + Create a &table + Create a &table + + + + Edit the t&able + Edit the t&able + + + + Delete the ta&ble + Delete the ta&ble + + + + Export the table + Export the table + + + + Import into the table + Import into the table + + + + Populate table + Populate table + + + + Create similar table + Create similar table + + + + Reset autoincrement sequence + Reset autoincrement sequence + + + + Create an &index + Create an &index + + + + Edit the i&ndex + Edit the i&ndex + + + + Delete the in&dex + Delete the in&dex + + + + Create a trig&ger + Create a trig&ger + + + + Edit the trigg&er + Edit the trigg&er + + + + Delete the trigge&r + Delete the trigge&r + + + + Create a &view + Create a &view + + + + Edit the v&iew + Edit the v&iew + + + + Delete the vi&ew + Delete the vi&ew + + + + Add a column + Add a column + + + + Edit the column + Edit the column + + + + Delete the column + Delete the column + + + + Delete selected items + Delete selected items + + + + Clear filter + Clear filter + + + + &Refresh all database schemas + &Refresh all database schemas + + + + Re&fresh selected database schema + Re&fresh selected database schema + + + + + Erase table data + Erase table data + + + + Open file's directory + Open file's directory + + + + Execute SQL from file + Execute SQL from file + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + Database + + + + Grouping + Grouping + + + + Generate query for table + Generate query for table + + + + + Create group + Create group + + + + Group name + Group name + + + + Entry with name %1 already exists in group %2. + Entry with name %1 already exists in group %2. + + + + Delete group + Delete group + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + + + + Are you sure you want to remove database '%1' from the list? + Are you sure you want to remove database '%1' from the list? + + + + Are you sure you want to remove following databases from the list: +%1 + Are you sure you want to remove following databases from the list: +%1 + + + + Remove database + Remove database + + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Vacuum (%1) + Vacuum (%1) + + + + Integrity check (%1) + Integrity check (%1) + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Are you sure you want to delete all data from table(s): %1? + Are you sure you want to delete all data from table(s): %1? + + + + An error occurred while trying to delete data from table '%1': %2 + An error occurred while trying to delete data from table '%1': %2 + + + + All data has been deleted for table '%1'. + All data has been deleted for table '%1'. + + + + Following objects will be deleted: %1. + Following objects will be deleted: %1. + + + + Following databases will be removed from list: %1. + Following databases will be removed from list: %1. + + + + Remainig objects from deleted group will be moved in place where the group used to be. + Remainig objects from deleted group will be moved in place where the group used to be. + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>Are you sure you want to continue? + + + + Delete objects + Delete objects + + + + DbTreeItemDelegate + + + error + dbtree labels + error + + + + (system table) + database tree label + (system table) + + + + (virtual) + virtual table label + (virtual) + + + + (system index) + database tree label + (system index) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + Database: %1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + Version: + + + + File size: + dbtree tooltip + File size: + + + + Encoding: + dbtree tooltip + Encoding: + + + + Error: + dbtree tooltip + Error: + + + + Table : %1 + dbtree tooltip + Table : %1 + + + + Columns (%1): + dbtree tooltip + Columns (%1): + + + + Indexes (%1): + dbtree tooltip + Indexes (%1): + + + + Triggers (%1): + dbtree tooltip + Triggers (%1): + + + + Copy + Copy + + + + Move + Move + + + + Include data + Include data + + + + Include indexes + Include indexes + + + + Include triggers + Include triggers + + + + Abort + Abort + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + Could not add dropped database file '%1' automatically. Manual setup is necessary. + + + + Referenced tables + Referenced tables + + + + Do you want to include following referenced tables as well: +%1 + Do you want to include following referenced tables as well: +%1 + + + + Name conflict + Name conflict + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + + + + SQL statements conversion + SQL statements conversion + + + + Following error occurred while converting SQL statements to the target SQLite version: + Following error occurred while converting SQL statements to the target SQLite version: + + + + Would you like to ignore those errors and proceed? + Would you like to ignore those errors and proceed? + + + + DdlHistoryWindow + + + Filter by database: + Filter by database: + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL history + + + + DdlPreviewDialog + + + Queries to be executed + Queries to be executed + + + + Don't show again + Don't show again + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio Debug Console + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + Query + + + + History + History + + + + Results in the separate tab + Results in the separate tab + + + + Results below the query + Results below the query + + + + + SQL editor %1 + SQL editor %1 + + + + + Results + Results + + + + Execute query + Execute query + + + + Explain query + Explain query + + + + Clear execution history + sql editor + Clear execution history + + + + Export results + sql editor + Export results + + + + Create view from query + sql editor + Create view from query + + + + Previous database + Previous database + + + + Next database + Next database + + + + Show next tab + sql editor + Show next tab + + + + Show previous tab + sql editor + Show previous tab + + + + Focus results below + sql editor + Focus results below + + + + Focus SQL editor above + sql editor + Focus SQL editor above + + + + Delete selected SQL history entries + sql editor + Delete selected SQL history entries + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + Active database (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + Query finished in %1 second(s). Rows affected: %2 + + + + Query finished in %1 second(s). + Query finished in %1 second(s). + + + + Clear execution history + Clear execution history + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + No database selected in the SQL editor. Cannot create a view for unknown database. + + + + Editor window "%1" has uncommitted data. + Editor window "%1" has uncommitted data. + + + + ErrorsConfirmDialog + + + Errors + Errors + + + + Following errors occured: + Following errors occured: + + + + Would you like to proceed? + Would you like to proceed? + + + + ExecFromFileDialog + + + Execute SQL from file + Execute SQL from file + + + + Input file + Input file + + + + Path to file + Path to file + + + + Browse for file + Browse for file + + + + Options + Options + + + + File encoding + File encoding + + + + Skip failing SQL statements + Skip failing SQL statements + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Execute SQL file + Execute SQL file + + + + Please provide file to be executed. + Please provide file to be executed. + + + + Provided file does not exist or cannot be read. + Provided file does not exist or cannot be read. + + + + ExportDialog + + + Export + Export + + + + What do you want to export? + What do you want to export? + + + + A database + A database + + + + A single table + A single table + + + + Query results + Query results + + + + Table to export + Table to export + + + + Database + Database + + + + Table + Table + + + + Options + Options + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + + Export table data + Export table data + + + + Export table indexes + Export table indexes + + + + Export table triggers + Export table triggers + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + + Select database objects to export + Select database objects to export + + + + Export data from tables + Export data from tables + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + + Database: + Database: + + + + Query to export results for + Query to export results for + + + + Query to be executed for results: + Query to be executed for results: + + + + Export format and options + Export format and options + + + + Export format + Export format + + + + Output + Output + + + + Exported file path + Exported file path + + + + Clipboard + Clipboard + + + + File + File + + + + Exported text encoding: + Exported text encoding: + + + + Export format options + Export format options + + + + Cancel + Cancel + + + + + + Select database to export. + Select database to export. + + + + Select table to export. + Select table to export. + + + + Enter valid query to export. + Enter valid query to export. + + + + Select at least one object to export. + Select at least one object to export. + + + + You must provide a file name to export to. + You must provide a file name to export to. + + + + Path you provided is an existing directory. You cannot overwrite it. + Path you provided is an existing directory. You cannot overwrite it. + + + + The directory '%1' does not exist. + The directory '%1' does not exist. + + + + The file '%1' exists and will be overwritten. + The file '%1' exists and will be overwritten. + + + + All files (*) + All files (*) + + + + Pick file to export to + Pick file to export to + + + + Internal error during export. This is a bug. Please report it. + Internal error during export. This is a bug. Please report it. + + + + FileExecErrorsDialog + + + Execution errors + Execution errors + + + + Following errors were encountered during execution of SQL statements from the file: + Following errors were encountered during execution of SQL statements from the file: + + + + SQL + SQL + + + + Error + Error + + + + Statements that were executed successfully were commited. + Statements that were executed successfully were commited. + + + + Statements that were executed successfully were rolled back. + Statements that were executed successfully were rolled back. + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + FontEdit + + + Choose font + font configuration + Choose font + + + + Form + + + Active SQL formatter plugin + Active SQL formatter plugin + + + + FormView + + + Commit row + form view + Commit row + + + + Rollback row + form view + Rollback row + + + + First row + form view + First row + + + + Previous row + form view + Previous row + + + + Next row + form view + Next row + + + + Last row + form view + Last row + + + + Insert new row + form view + Insert new row + + + + Delete current row + form view + Delete current row + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + Input arguments + + + + Undefined + Undefined + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Type: + Type: + + + + Function name: + Function name: + + + + Implementation language: + Implementation language: + + + + Deterministic + Deterministic + + + + Initialization code: + Initialization code: + + + + + Function implementation code: + Function implementation code: + + + + Final step implementation code: + Final step implementation code: + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + Commit all function changes + + + + Rollback all function changes + Rollback all function changes + + + + Create new function + Create new function + + + + Delete selected function + Delete selected function + + + + Custom SQL functions manual + Custom SQL functions manual + + + + Add function argument + Add function argument + + + + Rename function argument + Rename function argument + + + + Delete function argument + Delete function argument + + + + Move function argument up + Move function argument up + + + + Move function argument down + Move function argument down + + + + Scalar + Scalar + + + + Aggregate + Aggregate + + + + Enter a non-empty, unique name of the function. + Enter a non-empty, unique name of the function. + + + + Pick the implementation language. + Pick the implementation language. + + + + Per step code: + Per step code: + + + + Enter a non-empty implementation code. + Enter a non-empty implementation code. + + + + argument + new function argument name in function editor window + argument + + + + Functions editor window has uncommitted modifications. + Functions editor window has uncommitted modifications. + + + + ImportDialog + + + Import data + Import data + + + + Table to import to + Table to import to + + + + Table + Table + + + + Database + Database + + + + Data source to import from + Data source to import from + + + + Data source type + Data source type + + + + Options + Options + + + + Text encoding: + Text encoding: + + + + Input file: + Input file: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + + + + Ignore errors + Ignore errors + + + + Data source options + Data source options + + + + Cancel + Cancel + + + + If you type table name that doesn't exist, it will be created. + If you type table name that doesn't exist, it will be created. + + + + Enter the table name + Enter the table name + + + + Select import plugin. + Select import plugin. + + + + You must provide a file to import from. + You must provide a file to import from. + + + + The file '%1' does not exist. + The file '%1' does not exist. + + + + Path you provided is a directory. A regular file is required. + Path you provided is a directory. A regular file is required. + + + + Pick file to import from + Pick file to import from + + + + IndexDialog + + + + Index + Index + + + + Column + Column + + + + Sort + Sort + + + + Collation + Collation + + + + On table: + On table: + + + + Delete selected indexed expression + Delete selected indexed expression + + + + Moves selected index column up in the order, making it more significant in the index. + Moves selected index column up in the order, making it more significant in the index. + + + + Moves selected index column down in the order, making it less significant in the index. + Moves selected index column down in the order, making it less significant in the index. + + + + Partial index condition + Partial index condition + + + + Unique index + Unique index + + + + Index name: + Index name: + + + + Edit selected indexed expression + Edit selected indexed expression + + + + Add indexed expression + Add indexed expression + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + Tried to open index dialog for closed or inexisting database. + + + + Could not process index %1 correctly. Unable to open an index dialog. + Could not process index %1 correctly. Unable to open an index dialog. + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + + + + Pick the table for the index. + Pick the table for the index. + + + + Select at least one column. + Select at least one column. + + + + Enter a valid condition. + Enter a valid condition. + + + + default + index dialog + default + + + + Sort order + table constraints + Sort order + + + + + Error + index dialog + Error + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + IndexExprColumnDialog + + + Indexed expression + Indexed expression + + + + Expression to index + Expression to index + + + + This expression is already indexed by the index. + This expression is already indexed by the index. + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + It's forbidden to use 'SELECT' statements in indexed expressions. + + + + Enter an indexed expression. + Enter an indexed expression. + + + + Invalid expression. + Invalid expression. + + + + LanguageDialog + + + Language + Language + + + + Please choose language: + Please choose language: + + + + MainWindow + + + Database toolbar + Database toolbar + + + + Structure toolbar + Structure toolbar + + + + Tools + Tools + + + + Window list + Window list + + + + View toolbar + View toolbar + + + + Configuration widgets + Configuration widgets + + + + Syntax highlighting engines + Syntax highlighting engines + + + + Data editors + Data editors + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + + + + Running in debug mode. Debug messages are printed to the standard output. + Running in debug mode. Debug messages are printed to the standard output. + + + + You need to restart application to make the language change take effect. + You need to restart application to make the language change take effect. + + + + Open SQL &editor + Open SQL &editor + + + + Open DDL &history + Open DDL &history + + + + Open SQL &functions editor + Open SQL &functions editor + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + Open &collations editor + + + + Open ex&tension manager + Open ex&tension manager + + + + &Import + &Import + + + + E&xport + E&xport + + + + Open confi&guration dialog + Open confi&guration dialog + + + + &Tile windows + &Tile windows + + + + Tile windows &horizontally + Tile windows &horizontally + + + + Tile windows &vertically + Tile windows &vertically + + + + &Cascade windows + &Cascade windows + + + + Next window + Next window + + + + Previous window + Previous window + + + + Hide status field + Hide status field + + + + Close &all windows + Close &all windows + + + + Re&store recently closed window + Re&store recently closed window + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Report a &bug + Report a &bug + + + + D&onate + D&onate + + + + Propose a new &feature + Propose a new &feature + + + + &About + &About + + + + &Licenses + &Licenses + + + + Open home &page + Open home &page + + + + User &Manual + User &Manual + + + + SQLite &documentation + SQLite &documentation + + + + Bugs and feature &requests + Bugs and feature &requests + + + + Quit + Quit + + + + Check for &updates + Check for &updates + + + + &Database + menubar + &Database + + + + &Structure + menubar + &Structure + + + + &View + menubar + &View + + + + Window list + menubar view menu + Window list + + + + &Tools + menubar + &Tools + + + + &Help + &Help + + + + Could not set style: %1 + main window + Could not set style: %1 + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Rename window + Rename window + + + + Enter new name for the window: + Enter new name for the window: + + + + New updates are available. <a href="%1">Click here for details</a>. + New updates are available. <a href="%1">Click here for details</a>. + + + + You're running the most recent version. No updates are available. + You're running the most recent version. No updates are available. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + MdiWindow + + + Uncommitted changes + Uncommitted changes + + + + Close anyway + Close anyway + + + + Don't close + Don't close + + + + MultiEditor + + + Null value + multieditor + Null value + + + + Configure editors for this data type + Configure editors for this data type + + + + Open another tab + Open another tab + + + + Foreign Key + Foreign Key + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + Deleted + + + + Read only + multieditor + Read only + + + + MultiEditorBoolPlugin + + + Boolean + Boolean + + + + MultiEditorDatePlugin + + + Date + Date + + + + MultiEditorDateTimePlugin + + + Date & time + Date & time + + + + MultiEditorHexPlugin + + + Hex + Hex + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + Number + + + + MultiEditorText + + + Tab changes focus + Tab changes focus + + + + Cut + Cut + + + + Copy + Copy + + + + Paste + Paste + + + + Delete + Delete + + + + Undo + Undo + + + + Redo + Redo + + + + MultiEditorTextPlugin + + + Text + Text + + + + MultiEditorTimePlugin + + + Time + Time + + + + NewConstraintDialog + + + New constraint + New constraint + + + + + Primary Key + new constraint dialog + Primary Key + + + + + Foreign Key + new constraint dialog + Foreign Key + + + + + Unique + new constraint dialog + Unique + + + + + Check + new constraint dialog + Check + + + + Not NULL + new constraint dialog + Not NULL + + + + Collate + new constraint dialog + Collate + + + + Generated + new constraint dialog + Generated + + + + Default + new constraint dialog + Default + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio updates + + + + New version is available! + New version is available! + + + + Download new version! + Download new version! + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + New version package will be downloaded. It will be up to you to install it whenever you're ready. + + + + Open SQLiteStudio home page. + Open SQLiteStudio home page. + + + + Read release notes && download package yourself. + Read release notes && download package yourself. + + + + Just close this window. + Just close this window. + + + + Check for updates on startup + Check for updates on startup + + + + Not now. + Not now. + + + + PopulateConfigDialog + + + Populating configuration + Populating configuration + + + + Configuring <b>%1</b> for column <b>%2</b> + Configuring <b>%1</b> for column <b>%2</b> + + + + PopulateDialog + + + Populate table + Populate table + + + + Database + Database + + + + Table + Table + + + + Columns + Columns + + + + Number of rows to populate: + Number of rows to populate: + + + + Populate + populate dialog button + Populate + + + + Abort + Abort + + + + Configure + Configure + + + + Populating configuration for this column is invalid or incomplete. + Populating configuration for this column is invalid or incomplete. + + + + Select database with table to populate + Select database with table to populate + + + + Select table to populate + Select table to populate + + + + You have to select at least one column. + You have to select at least one column. + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + + + + Cannot edit results of query other than %1. + Cannot edit results of query other than %1. + + + + Cannot edit columns that are result of aggregated %1 statements. + Cannot edit columns that are result of aggregated %1 statements. + + + + Cannot edit columns that are result of %1 statement. + Cannot edit columns that are result of %1 statement. + + + + Cannot edit columns that are result of common table expression statement (%1). + Cannot edit columns that are result of common table expression statement (%1). + + + + Cannot edit table generated columns. + Cannot edit table generated columns. + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + on conflict: %1 + + + + references table %1, column %2 + data view tooltip + references table %1, column %2 + + + + condition: %1 + data view tooltip + condition: %1 + + + + collation name: %1 + data view tooltip + collation name: %1 + + + + Data grid view + Data grid view + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + Copy cell(s) contents to clipboard + + + + Copy cell(s) contents together with header to clipboard + Copy cell(s) contents together with header to clipboard + + + + Paste cell(s) contents from clipboard + Paste cell(s) contents from clipboard + + + + Set empty value to selected cell(s) + Set empty value to selected cell(s) + + + + Set NULL value to selected cell(s) + Set NULL value to selected cell(s) + + + + Commit changes to cell(s) contents + Commit changes to cell(s) contents + + + + Rollback changes to cell(s) contents + Rollback changes to cell(s) contents + + + + Delete selected data row + Delete selected data row + + + + Insert new data row + Insert new data row + + + + Open contents of selected cell in a separate editor + Open contents of selected cell in a separate editor + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + Total pages available: %1 + + + + Total rows loaded: %1 + Total rows loaded: %1 + + + + Data view (both grid and form) + Data view (both grid and form) + + + + Refresh data + Refresh data + + + + Switch to grid view of the data + Switch to grid view of the data + + + + Switch to form view of the data + Switch to form view of the data + + + + Database list + Database list + + + + Delete selected item + Delete selected item + + + + Clear filter contents + Clear filter contents + + + + Refresh schema + Refresh schema + + + + Refresh all schemas + Refresh all schemas + + + + Add database + Add database + + + + Select all items + Select all items + + + + Copy selected item(s) + Copy selected item(s) + + + + + + Paste from clipboard + Paste from clipboard + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + Tables + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + Views + Views + + + + Columns + Columns + + + + Data form view + Data form view + + + + Commit changes for current row + Commit changes for current row + + + + Rollback changes for current row + Rollback changes for current row + + + + Go to first row on current page + Go to first row on current page + + + + Go to next row + Go to next row + + + + Go to previous row + Go to previous row + + + + Go to last row on current page + Go to last row on current page + + + + Insert new row + Insert new row + + + + Delete current row + Delete current row + + + + Main window + Main window + + + + Open SQL editor + Open SQL editor + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + Previous window + + + + Next window + Next window + + + + Hide status area + Hide status area + + + + Open user manual + Open user manual + + + + Open configuration dialog + Open configuration dialog + + + + Open Debug Console + Open Debug Console + + + + Open CSS Console + Open CSS Console + + + + Open the About dialog + Open the About dialog + + + + Quit the application + Quit the application + + + + Cell text value editor + Cell text value editor + + + + + Cut selected text + Cut selected text + + + + + Copy selected text + Copy selected text + + + + + Delete selected text + Delete selected text + + + + + Undo + Undo + + + + + Redo + Redo + + + + SQL editor input field + SQL editor input field + + + + Select whole editor contents + Select whole editor contents + + + + Save contents into a file + Save contents into a file + + + + Load contents from a file + Load contents from a file + + + + Find in text + Find in text + + + + Find next + Find next + + + + Find previous + Find previous + + + + Replace in text + Replace in text + + + + Delete current line + Delete current line + + + + Request code assistant + Request code assistant + + + + Format contents + Format contents + + + + Move selected block of text one line down + Move selected block of text one line down + + + + Move selected block of text one line up + Move selected block of text one line up + + + + Copy selected block of text and paste it a line below + Copy selected block of text and paste it a line below + + + + Copy selected block of text and paste it a line above + Copy selected block of text and paste it a line above + + + + Toggle comment + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + All SQLite databases + + + + All files + All files + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL editor window + + + + Execute query + Execute query + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + Execute "%1" query + + + + Switch current working database to previous on the list + Switch current working database to previous on the list + + + + Switch current working database to next on the list + Switch current working database to next on the list + + + + Go to next editor tab + Go to next editor tab + + + + Go to previous editor tab + Go to previous editor tab + + + + Move keyboard input focus to the results view below + Move keyboard input focus to the results view below + + + + Move keyboard input focus to the SQL editor above + Move keyboard input focus to the SQL editor above + + + + Delete selected SQL history entries + Delete selected SQL history entries + + + + Table window + Table window + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + Refresh table structure + + + + Add new column + Add new column + + + + Edit selected column + Edit selected column + + + + Delete selected column + Delete selected column + + + + Export table data + Export table data + + + + Import data to the table + Import data to the table + + + + Add new table constraint + Add new table constraint + + + + Edit selected table constraint + Edit selected table constraint + + + + Delete selected table constraint + Delete selected table constraint + + + + Refresh table index list + Refresh table index list + + + + Add new index + Add new index + + + + Edit selected index + Edit selected index + + + + Delete selected index + Delete selected index + + + + Refresh table trigger list + Refresh table trigger list + + + + + Add new trigger + Add new trigger + + + + + Edit selected trigger + Edit selected trigger + + + + + Delete selected trigger + Delete selected trigger + + + + + Go to next tab + Go to next tab + + + + + Go to previous tab + Go to previous tab + + + + A view window + A view window + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + Refresh view trigger list + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + Uncommitted changes + + + + Are you sure you want to quit the application? + +Following items are pending: + Are you sure you want to quit the application? + +Following items are pending: + + + + SearchTextDialog + + + Find or replace + Find or replace + + + + Find: + Find: + + + + Case sensitive + Case sensitive + + + + Search backwards + Search backwards + + + + Regular expression matching + Regular expression matching + + + + Replace && +find next + Replace && +find next + + + + Replace with: + Replace with: + + + + Replace all + Replace all + + + + Find + Find + + + + SortDialog + + + Sort by columns + Sort by columns + + + + + Column + Column + + + + + Order + Order + + + + Sort by: %1 + Sort by: %1 + + + + Move column up + Move column up + + + + Move column down + Move column down + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + Cut + + + + Copy + sql editor + Copy + + + + Paste + sql editor + Paste + + + + Delete + sql editor + Delete + + + + Select all + sql editor + Select all + + + + Undo + sql editor + Undo + + + + Redo + sql editor + Redo + + + + Complete + sql editor + Complete + + + + Format SQL + sql editor + Format SQL + + + + Save SQL to file + sql editor + Save SQL to file + + + + Select file to save SQL + sql editor + Select file to save SQL + + + + Load SQL from file + sql editor + Load SQL from file + + + + Delete line + sql editor + Delete line + + + + Move block down + sql editor + Move block down + + + + Move block up + sql editor + Move block up + + + + Copy block down + sql editor + Copy block down + + + + Copy up down + sql editor + Copy up down + + + + Find + sql editor + Find + + + + Find next + sql editor + Find next + + + + Find previous + sql editor + Find previous + + + + Replace + sql editor + Replace + + + + Toggle comment + sql editor + Toggle comment + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + Could not open file '%1' for writing: %2 + + + + Saved SQL contents to file: %1 + Saved SQL contents to file: %1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + Syntax completion can be used only when a valid database is set for the SQL editor. + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + + + + Save to file + Save to file + + + + SQL scripts (*.sql);;All files (*) + SQL scripts (*.sql);;All files (*) + + + + Open file + Open file + + + + Could not open file '%1' for reading: %2 + Could not open file '%1' for reading: %2 + + + + Reached the end of document. Hit the find again to restart the search. + Reached the end of document. Hit the find again to restart the search. + + + + SqlQueryItem + + + Committing error: + data view tooltip + Committing error: + + + + Column: + data view tooltip + Column: + + + + Data type: + data view + Data type: + + + + Table: + data view tooltip + Table: + + + + Constraints: + data view tooltip + Constraints: + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + Cannot edit this cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + Structure of this table has changed since last data was loaded. Reload the data to proceed. + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + Only one query can be executed simultaneously. + + + + Cannot execute query on undefined or invalid database. + Cannot execute query on undefined or invalid database. + + + + Cannot execute empty query. + Cannot execute empty query. + + + + Uncommitted data + Uncommitted data + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + + + + Cannot commit the data for a cell that refers to the already closed database. + Cannot commit the data for a cell that refers to the already closed database. + + + + Could not begin transaction on the database. Details: %1 + Could not begin transaction on the database. Details: %1 + + + + An error occurred while committing the transaction: %1 + An error occurred while committing the transaction: %1 + + + + An error occurred while rolling back the transaction: %1 + An error occurred while rolling back the transaction: %1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + + + + An error occurred while committing the data: %1 + An error occurred while committing the data: %1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + + + + + Error while executing SQL query on database '%1': %2 + Error while executing SQL query on database '%1': %2 + + + + Error while loading query results: %1 + Error while loading query results: %1 + + + + Insert multiple rows + Insert multiple rows + + + + Number of rows to insert: + Number of rows to insert: + + + + Delete rows + Delete rows + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + + + + SqlQueryView + + + Go to referenced row in... + Go to referenced row in... + + + + Copy + Copy + + + + Copy with headers + Copy with headers + + + + Copy as... + Copy as... + + + + Paste + Paste + + + + Paste as... + Paste as... + + + + Set NULL values + Set NULL values + + + + Erase values + Erase values + + + + Commit + Commit + + + + Rollback + Rollback + + + + Commit selected cells + Commit selected cells + + + + Rollback selected cells + Rollback selected cells + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + Define columns to sort by + + + + Remove custom sorting + Remove custom sorting + + + + Insert row + Insert row + + + + Insert multiple rows + Insert multiple rows + + + + Delete selected row + Delete selected row + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + Edit value in editor + + + + Show value in a viewer + Show value in a viewer + + + + Generate query for selected cells + Generate query for selected cells + + + + No items selected to paste clipboard contents to. + No items selected to paste clipboard contents to. + + + + Cannot paste data. Details: %1 + Cannot paste data. Details: %1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + + + + Cannot paste to a cell. Details: %1 + Cannot paste to a cell. Details: %1 + + + + The row is marked for deletion. + The row is marked for deletion. + + + + Cannot paste to column %1. Details: %2 + Cannot paste to column %1. Details: %2 + + + + Go to referenced row in table '%1' + Go to referenced row in table '%1' + + + + table '%1' + table '%1' + + + + Referenced row (%1) + Referenced row (%1) + + + + Trim pasted text? + Trim pasted text? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + The pasted text contains leading or trailing white space. Trim it automatically? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + Edit value + + + + SqlTableModel + + + Error while committing new row: %1 + Error while committing new row: %1 + + + + Error while deleting row from table %1: %2 + Error while deleting row from table %1: %2 + + + + SqliteExtensionEditor + + + Filter extensions + Filter extensions + + + + Leave empty to use default function + Leave empty to use default function + + + + Extension file + Extension file + + + + Initialization function + Initialization function + + + + Databases + Databases + + + + Register in all databases + Register in all databases + + + + Register in following databases: + Register in following databases: + + + + Extension manager window has uncommitted modifications. + Extension manager window has uncommitted modifications. + + + + Extension manager + Extension manager + + + + Commit all extension changes + Commit all extension changes + + + + Rollback all extension changes + Rollback all extension changes + + + + Add new extension + Add new extension + + + + Remove selected extension + Remove selected extension + + + + Editing extensions manual + Editing extensions manual + + + + File with given path does not exist or is not readable. + File with given path does not exist or is not readable. + + + + Unable to load extension: %1 + Unable to load extension: %1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + + + + Dynamic link libraries (*.dll);;All files (*) + Dynamic link libraries (*.dll);;All files (*) + + + + Shared objects (*.so);;All files (*) + Shared objects (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + Dynamic libraries (*.dylib);;All files (*) + + + + All files (*) + All files (*) + + + + Open file + Open file + + + + StatusField + + + Status + Status + + + + Copy + Copy + + + + Clear + Clear + + + + TableConstraintsModel + + + Type + table constraints + Type + + + + Details + table constraints + Details + + + + Name + table constraints + Name + + + + TableForeignKeyPanel + + + Foreign table: + Foreign table: + + + + Columns + Columns + + + + Local column + Local column + + + + Foreign column + Foreign column + + + + Reactions + Reactions + + + + Deferred foreign key + Deferred foreign key + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + Pick the foreign column. + Pick the foreign column. + + + + Pick the foreign table. + Pick the foreign table. + + + + Select at least one foreign column. + Select at least one foreign column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + Foreign column + table constraints + Foreign column + + + + TablePrimaryKeyAndUniquePanel + + + Columns + Columns + + + + Column + Column + + + + Collation + Collation + + + + Sort + Sort + + + + Valid only for a single column with INTEGER data type + Valid only for a single column with INTEGER data type + + + + Autoincrement + Autoincrement + + + + Named constraint + Named constraint + + + + Constraint name + Constraint name + + + + On conflict + On conflict + + + + Collate + table constraints + Collate + + + + Sort order + table constraints + Sort order + + + + Select at least one column. + Select at least one column. + + + + Enter a name of the constraint. + Enter a name of the constraint. + + + + TableStructureModel + + + Name + table structure columns + Name + + + + Data type + table structure columns + Data type + + + + Primary +Key + table structure columns + Primary +Key + + + + Foreign +Key + table structure columns + Foreign +Key + + + + Unique + table structure columns + Unique + + + + Check + table structure columns + Check + + + + Not +NULL + table structure columns + Not +NULL + + + + Collate + table structure columns + Collate + + + + Generated + table structure columns + Generated + + + + Default value + table structure columns + Default value + + + + TableWindow + + + Structure + Structure + + + + Table name: + Table name: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + Data + + + + Constraints + Constraints + + + + Indexes + Indexes + + + + Triggers + Triggers + + + + DDL + DDL + + + + Export table + table window + Export table + + + + Import data to table + table window + Import data to table + + + + Populate table + table window + Populate table + + + + Refresh structure + table window + Refresh structure + + + + Commit structure changes + table window + Commit structure changes + + + + Rollback structure changes + table window + Rollback structure changes + + + + Add column + table window + Add column + + + + Edit column + table window + Edit column + + + + + Delete column + table window + Delete column + + + + Move column up + table window + Move column up + + + + Move column down + table window + Move column down + + + + Create similar table + table window + Create similar table + + + + Reset autoincrement value + table window + Reset autoincrement value + + + + Add table constraint + table window + Add table constraint + + + + Edit table constraint + table window + Edit table constraint + + + + Delete table constraint + table window + Delete table constraint + + + + Move table constraint up + table window + Move table constraint up + + + + Move table constraint down + table window + Move table constraint down + + + + Add table primary key + table window + Add table primary key + + + + Add table foreign key + table window + Add table foreign key + + + + Add table unique constraint + table window + Add table unique constraint + + + + Add table check constraint + table window + Add table check constraint + + + + Refresh index list + table window + Refresh index list + + + + + Create index + table window + Create index + + + + Edit index + table window + Edit index + + + + Delete index + table window + Delete index + + + + Refresh trigger list + table window + Refresh trigger list + + + + + Create trigger + table window + Create trigger + + + + Edit trigger + table window + Edit trigger + + + + Delete trigger + table window + Delete trigger + + + + Are you sure you want to delete column '%1'? + table window + Are you sure you want to delete column '%1'? + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + Following problems will take place while modifying the table. +Would you like to proceed? + + + + Table modification + table window + Table modification + + + + Could not load data for table %1. Error details: %2 + Could not load data for table %1. Error details: %2 + + + + Could not process the %1 table correctly. Unable to open a table window. + Could not process the %1 table correctly. Unable to open a table window. + + + + Database + Database + + + + Could not restore window %1, because no database or table was stored in session for this window. + Could not restore window %1, because no database or table was stored in session for this window. + + + + Could not restore window '%1', because no database or table was stored in session for this window. + Could not restore window '%1', because no database or table was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + New table %1 + + + + Committed changes for table '%1' successfully. + Committed changes for table '%1' successfully. + + + + Committed changes for table '%1' (named before '%2') successfully. + Committed changes for table '%1' (named before '%2') successfully. + + + + Could not commit table structure. Error message: %1 + table window + Could not commit table structure. Error message: %1 + + + + Reset autoincrement + Reset autoincrement + + + + Are you sure you want to reset autoincrement value for table '%1'? + Are you sure you want to reset autoincrement value for table '%1'? + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + An error occurred while trying to reset autoincrement value for table '%1': %2 + + + + Autoincrement value for table '%1' has been reset successfully. + Autoincrement value for table '%1' has been reset successfully. + + + + Empty name + Empty name + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + + + + Cannot create a table without at least one column. + Cannot create a table without at least one column. + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + Are you sure you want to delete table constraint '%1'? + + + + Delete constraint + table window + Delete constraint + + + + Cannot export, because no export plugin is loaded. + Cannot export, because no export plugin is loaded. + + + + Cannot import, because no import plugin is loaded. + Cannot import, because no import plugin is loaded. + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + Name + table window indexes + Name + + + + Unique + table window indexes + Unique + + + + Columns + table window indexes + Columns + + + + Partial index condition + table window indexes + Partial index condition + + + + Name + table window triggers + Name + + + + Event + table window triggers + Event + + + + Condition + table window triggers + Condition + + + + Details + table window triggers + Details + + + + Table window "%1" has uncommitted structure modifications and data. + Table window "%1" has uncommitted structure modifications and data. + + + + Table window "%1" has uncommitted data. + Table window "%1" has uncommitted data. + + + + Table window "%1" has uncommitted structure modifications. + Table window "%1" has uncommitted structure modifications. + + + + TriggerColumnsDialog + + + Trigger columns + Trigger columns + + + + Triggering columns: + Triggering columns: + + + + Select all + Select all + + + + Deselect all + Deselect all + + + + TriggerDialog + + + + Trigger + Trigger + + + + On table: + On table: + + + + Action: + Action: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + Pre-condition: + Pre-condition: + + + + The scope is still not fully supported by the SQLite database. + The scope is still not fully supported by the SQLite database. + + + + Trigger name: + Trigger name: + + + + When: + When: + + + + List of columns for UPDATE OF action. + List of columns for UPDATE OF action. + + + + Scope: + Scope: + + + + Code: + Code: + + + + Trigger statements to be executed. + Trigger statements to be executed. + + + + DDL + DDL + + + + On view: + On view: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + Could not process trigger %1 correctly. Unable to open a trigger dialog. + + + + Enter a valid condition. + Enter a valid condition. + + + + Enter a valid trigger code. + Enter a valid trigger code. + + + + Error + trigger dialog + Error + + + + An error occurred while executing SQL statements: +%1 + An error occurred while executing SQL statements: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + Database version convert + + + + Following changes to the SQL statements will be made: + Following changes to the SQL statements will be made: + + + + Before + Before + + + + After + After + + + + ViewWindow + + + Query + Query + + + + View name: + View name: + + + + Output column names + Output column names + + + + + Data + Data + + + + Triggers + Triggers + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + Could not restore window '%1', because no database or view was stored in session for this window. + + + + Could not restore window '%1', because database %2 could not be resolved. + Could not restore window '%1', because database %2 could not be resolved. + + + + Could not restore window '%1', because database %2 could not be open. + Could not restore window '%1', because database %2 could not be open. + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + + + + + New view %1 + New view %1 + + + + Database + Database + + + + Refresh the view + view window + Refresh the view + + + + Commit the view changes + view window + Commit the view changes + + + + Rollback the view changes + view window + Rollback the view changes + + + + Explicit column names + Explicit column names + + + + Generate output column names automatically basing on result columns of the view. + Generate output column names automatically basing on result columns of the view. + + + + Add column + view window + Add column + + + + Edit column + view window + Edit column + + + + Delete column + view window + Delete column + + + + Move column up + view window + Move column up + + + + Move column down + view window + Move column down + + + + Refresh trigger list + view window + Refresh trigger list + + + + Create new trigger + view window + Create new trigger + + + + Edit selected trigger + view window + Edit selected trigger + + + + Delete selected trigger + view window + Delete selected trigger + + + + View window "%1" has uncommitted structure modifications and data. + View window "%1" has uncommitted structure modifications and data. + + + + View window "%1" has uncommitted data. + View window "%1" has uncommitted data. + + + + View window "%1" has uncommitted structure modifications. + View window "%1" has uncommitted structure modifications. + + + + Could not load data for view %1. Error details: %2 + Could not load data for view %1. Error details: %2 + + + + Uncommitted changes + Uncommitted changes + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + + + + Go back to structure tab + Go back to structure tab + + + + Commit modifications and browse data. + Commit modifications and browse data. + + + + View '%1' was committed successfully. + View '%1' was committed successfully. + + + + Committed changes for view '%1' successfully. + Committed changes for view '%1' successfully. + + + + Committed changes for view '%1' (named before '%2') successfully. + Committed changes for view '%1' (named before '%2') successfully. + + + + Could not commit view changes. Error message: %1 + view window + Could not commit view changes. Error message: %1 + + + + Override columns + Override columns + + + + Currently defined columns will be overriden. Do you want to continue? + Currently defined columns will be overriden. Do you want to continue? + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + + + + Name + view window triggers + Name + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + Condition + + + + Details + table window triggers + Details + + + + Could not process the %1 view correctly. Unable to open a view window. + Could not process the %1 view correctly. Unable to open a view window. + + + + Empty name + Empty name + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + The view could not be modified due to internal SQLiteStudio error. Please report this! + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + Following problems will take place while modifying the view. +Would you like to proceed? + + + + View modification + view window + View modification + + + + WidgetCover + + + Interrupt + Interrupt + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.qm b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.qm deleted file mode 100644 index 98db9d2..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.qm and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.ts index 8a046cd..a1d59c7 100644 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.ts +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.ts @@ -1,7189 +1,7106 @@ - - + + AboutDialog - - About SQLiteStudio and licenses - 关于 SQLiteStudio 和许å¯åè®® + + About SQLiteStudio and licenses + 关于 SQLiteStudio 和许å¯åè®® - - About - 关于 + + About + 关于 - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">自由,开æºï¼Œè·¨å¹³å°çš„ SQLite æ•°æ®åº“管ç†å·¥å…·ã€‚<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">作者和活跃维护人:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">自由ã€å¼€æºã€è·¨å¹³å°çš„ SQLite æ•°æ®åº“管ç†å·¥å…·ã€‚<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">作者和活跃维护人:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - - Licenses - 许å¯åè®® + + Licenses + 许å¯åè®® - - Environment - 环境 + + Environment + 环境 - - Icon directories - 图标目录 + + Icon directories + 图标目录 - - Form directories - 表格目录 + + Form directories + 表å•目录 - - Plugin directories - æ’件目录 + + SQLite extension directories + SQLite 扩展目录 - - Application directory - 应用程åºç›®å½• + + Plugin directories + æ’件目录 - - SQLite 3 version: - SQLite 3 版本: + + Configuration directory + é…置文件目录 - - Configuration directory - é…置文件目录 + + Application directory + 应用程åºç›®å½• - - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">自由,开æºï¼Œè·¨å¹³å°çš„ SQLite æ•°æ®åº“管ç†å·¥å…·ã€‚<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">作者和活跃维护人:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + Qt version: + Qt 版本: - - Qt version: - Qt 版本: + + SQLite 3 version: + SQLite 3 版本: - - Portable distribution. - 便æºç‰ˆã€‚ + + Portable distribution. + 便æºç‰ˆã€‚ - - MacOS X application boundle distribution. - MacOS X 应用版。 + + MacOS X application bundle distribution. + MacOS X application bundle distribution. - - Operating system managed distribution. - 系统æä¾›ç‰ˆã€‚ + + Operating system managed distribution. + 系统æä¾›ç‰ˆã€‚ - - Copy - å¤åˆ¶ + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>目录:</h3><ol>%2</ol> - - - <h3>Table of contents:</h3><ol>%2</ol> - <h3>目录:</h3><ol>%2</ol> - - - + + BindParamsDialog - - Query parameters - æŸ¥è¯¢å‚æ•° - - - - Please provide values for query parameters - 请æä¾›ä¸€ä¸ªå€¼ä½œä¸ºæŸ¥è¯¢å‚æ•° - - - - BugDialog - - Bugs and ideas - Bugs 和想法 - - - Reporter - 报告者 - - - E-mail address - E-mail åœ°å€ - - - Log in - 登录 - - - Short description - ç®€è¦æè¿° - - - Detailed description - 详细æè¿° - - - Show more details - æ›´å¤šè¯¦ç»†ä¿¡æ¯ - - - SQLiteStudio version - SQLiteStudio 版本 - - - Operating system - æ“作系统 - - - Loaded plugins - 已加载æ’ä»¶ - - - Send - å‘é€ - - - You can see all your reported bugs and ideas by selecting menu '%1' and then '%2'. - 您å¯ä»¥é€šè¿‡é€‰æ‹©èœå• “1%â€ä¸‹çš„“%2â€æ¥æŸ¥çœ‹å…¨éƒ¨æ‚¨æŠ¥å‘Šçš„bugs和想法。 - - - A bug report sent successfully. - Bug报告æäº¤æˆåŠŸã€‚ - - - An error occurred while sending a bug report: %1 -%2 - æäº¤bug报告时å‘生了错误:%1 -%2 - - - You can retry sending. The contents will be restored when you open a report dialog after an error like this. - 您å¯ä»¥é‡æ–°å‘é€è¯•试。当你在å‘生错误åŽé‡æ–°æ‰“å¼€ä¸ŠæŠ¥å¯¹è¯æ¡†æ—¶ï¼Œä½ ä¹‹å‰è¾“入的内容将会æ¢å¤ã€‚ - - - An idea proposal sent successfully. - æäº¤å»ºè®®æˆåŠŸã€‚ - - - An error occurred while sending an idea proposal: %1 -%2 - 在æäº¤å»ºè®®æ—¶å‘生错误:%1 -%2 - - - A bug report - Bug报告 - - - Describe problem in few words - ç®€è¦æè¿°ä¸€ä¸‹é—®é¢˜ - - - Describe problem and how to reproduce it - æè¿°ä¸€ä¸‹é—®é¢˜ï¼Œæ€Žä¹ˆå¤çŽ°é—®é¢˜ - - - A new feature idea - 新功能建议 - - - A title for your idea - 您的建议题目 - - - Describe your idea in more details - 仔细æè¿°ä¸€ä¸‹æ‚¨çš„æƒ³æ³• - - - Reporting as an unregistered user, using e-mail address. - 使用e-mail地å€ä»¥æœªæ³¨å†Œç”¨æˆ·èº«ä»½ä¸ŠæŠ¥ã€‚ - - - Reporting as a registered user. - 作为已注册用户报告。 - - - Log out - 退出 - - - Providing true email address will make it possible to contact you regarding your report. To learn more, press 'help' button on the right side. - 使用真实emailåœ°å€æœ‰åŠ©äºŽæ‚¨ä¸ŠæŠ¥åŽè”ç³»åˆ°æ‚¨ã€‚å¦‚æžœæƒ³äº†è§£æ›´å¤šï¼Œè¯·ç‚¹å‡»å³æ–¹çš„â€œå¸®åŠ©â€æŒ‰é’®ã€‚ - - - Enter vaild e-mail address, or log in. - 输入正确的e-mail地å€ï¼Œæˆ–者登录。 - - - Short description requires at least 10 characters, but not more than 100. Longer description can be entered in the field below. - ç®€è¦æè¿°è‡³å°‘10个字符,但ä¸è¶…过100个字符。更详细的æè¿°å†…容请在下é¢çš„区域填写。 + + Query parameters + æŸ¥è¯¢å‚æ•° - Long description requires at least 30 characters. - 详细æè¿°å†…容至少30个字符。 + + Please provide values for query parameters + 请æä¾›ä¸€ä¸ªå€¼ä½œä¸ºæŸ¥è¯¢å‚æ•° - - - BugReportHistoryWindow + + + CodeSnippetEditor - Title - 标题 + + Filter snippets + 筛选代ç ç‰‡æ®µ - Reported at - 报告时间 + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> - URL - URL + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> - Reports history - æŠ¥å‘ŠåŽ†å² + + Snippet name + 代ç ç‰‡æ®µåç§° - Clear reports history - æ¸…ç©ºæŠ¥å‘ŠåŽ†å² + + Code assistant shortcut + Code assistant shortcut - Delete selected entry - 删除选中项 + + Snippet code + 代ç ç‰‡æ®µ - Invalid response from server. - 无效的æœåŠ¡å™¨å›žåº”ã€‚ + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. - - - BugReportLoginDialog - Log in - 登录 + + Code Snippets editor + 代ç ç‰‡æ®µç¼–辑器 - Credentials - 认è¯ä¿¡æ¯ + + Commit all snippet changes + Commit all snippet changes - Login: - 登录å: + + Rollback all snippet changes + Rollback all snippet changes - Password: - 密ç ï¼š + + Create new snippet + Create new snippet - Validation - è¿™é‡Œä¸æ˜¯å¾ˆç¡®è®¤ã€‚Not sure about this translation. - 确认 + + Delete selected snippet + Delete selected snippet - Validate - not sure about this translation - 确认 + + Move the snippet up + Move the snippet up - Validation result message - 验è¯ä¿¡æ¯ + + Move the snippet down + Move the snippet down - Abort - 中止 + + Code snippets manual + 代ç ç‰‡æ®µæ‰‹å†Œ - A login must be at least 2 characters long. - 登录å至少2个字符。 + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. - A password must be at least 5 characters long. - 密ç è‡³å°‘5个字符。 + + Enter a non-empty snippet content. + Enter a non-empty snippet content. - Valid - å·²éªŒè¯ + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. - - + + CollationsEditor - - Filter collations - 筛选排åºè§„则 + + Filter collations + ç­›é€‰å­—ç¬¦åº - - Collation name: - 排åºè§„则å称: + + Databases + æ•°æ®åº“ - - Implementation language: - 实现语言: + + Register in all databases + 在所有数æ®åº“中注册 - - Databases - æ•°æ®åº“ + + Register in following databases: + 在下列数æ®åº“中注册: - - Register in all databases - 在所有数æ®åº“中注册 + + Implementation code: + 实现代ç ï¼š - - Register in following databases: - 在下列数æ®åº“中注册: + + Collation name: + 字符åºå称: - - Implementation code: - 实现代ç ï¼š + + Implementation language: + 实现语言: - - Collations editor - 排åºè§„则编辑器 + + Collations editor + 字符åºç¼–辑器 - - Commit all collation changes - æäº¤å…¨éƒ¨æŽ’åºè§„则更改 + + Commit all collation changes + æäº¤å…¨éƒ¨å­—ç¬¦åºæ›´æ”¹ - - Rollback all collation changes - 回滚所有排åºè§„则更改 + + Rollback all collation changes + å›žæ»šæ‰€æœ‰å­—ç¬¦åºæ›´æ”¹ - - Create new collation - 创建新排åºè§„则 + + Create new collation + åˆ›å»ºæ–°å­—ç¬¦åº - - Delete selected collation - 删除选中排åºè§„则 + + Delete selected collation + åˆ é™¤é€‰ä¸­å­—ç¬¦åº - - Editing collations manual - 手动更改排åºè§„则 + + Editing collations manual + æ‰‹åŠ¨ç¼–è¾‘å­—ç¬¦åº - - Enter a non-empty, unique name of the collation. - 为排åºè§„则输入一个éžç©ºå”¯ä¸€çš„å称。 + + Enter a non-empty, unique name of the collation. + 请为字符åºè¾“入一个éžç©ºä¸”唯一的å称。 - - Pick the implementation language. - 选择实现语言。 + + Pick the implementation language. + 选择实现语言。 - - Enter a non-empty implementation code. - 输入éžç©ºå®žçް代ç ã€‚ + + Enter a non-empty implementation code. + 请输入éžç©ºçš„实现代ç ã€‚ - - Collations editor window has uncommitted modifications. - 排åºè§„则编辑器存在未æäº¤çš„æ”¹åŠ¨ã€‚ + + Collations editor window has uncommitted modifications. + 字符åºç¼–辑器有未æäº¤çš„修改。 - - Collations editor window has uncommited modifications. - 排åºè§„则编辑器存在未æäº¤çš„æ”¹åŠ¨ã€‚ - - - + + ColorButton - - Pick a color - 选择一ç§é¢œè‰² + + Pick a color + 选择一ç§é¢œè‰² - - + + ColumnCollatePanel - - Collation name: - 排åºè§„则å称: + + Collation name: + 字符åºå称: - - Named constraint: - 已命å的约æŸï¼š + + Named constraint: + 命å的约æŸï¼š - - Enter a name of the constraint. - 输入约æŸå称。 + + Enter a name of the constraint. + 输入约æŸçš„å称。 - - Enter a collation name. - 输入排åºè§„则å称。 + + Enter a collation name. + 输入字符åºçš„å称。 - - + + ColumnDefaultPanel - - Default value: - 默认值: - - - - Named constraint: - 已命å的约æŸï¼š + + Default value: + 默认值: - - Enter a default value expression. - 输入默认值表达å¼ã€‚ + + Named constraint: + 命å的约æŸï¼š - - Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - 无效的默认值表达å¼ï¼š%1。如果你想使用简å•的字符串作为值,记得用引å·å°†å…¶æ¡†èµ·æ¥ã€‚ + + Enter a default value expression. + 输入默认值表达å¼ã€‚ - - Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - 无效的默认值表达å¼ã€‚如果你想使用简å•的字符串作为值,记得用引å·å°†å…¶æ¡†èµ·æ¥ã€‚ + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + 无效的默认值表达å¼ï¼š%1。如果你想使用简å•的字符串作为值,记得用引å·å°†å…¶æ¡†èµ·æ¥ã€‚ - Invalid default value expression: %1 - 无效的默认值表达å¼ï¼š%1 + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + 无效的默认值表达å¼ã€‚如果你想使用简å•的字符串作为值,记得用引å·å°†å…¶æ¡†èµ·æ¥ã€‚ - - Enter a name of the constraint. - 输入约æŸå: + + Enter a name of the constraint. + 输入约æŸå: - - + + ColumnDialog - - Column - 字段 + + Column + 列 - - Name and type - å称和类型 + + Name and type + å称和类型 - - Scale - å°æ•°é•¿åº¦ + + Scale + å°æ•°ä½æ•° - - Precision - 精度 + + Precision + 精度 - - Data type: - æ•°æ®ç±»åž‹ï¼š + + Data type: + æ•°æ®ç±»åž‹ï¼š - - Column name: - 字段å: + + Column name: + 列å: - - Size: - 大å°ï¼š + + Size: + 大å°ï¼š - - Constraints - çº¦æŸ + + Constraints + çº¦æŸ - - Unique - 唯一 + + Generated value + 生æˆçš„值 - - - - - - - - Configure - é…ç½® + + Unique + 唯一 - - Foreign Key - 外键 + + + + + + + + + Configure + é…ç½® - - Collate - 排åºè§„则 + + Foreign Key + 外键 - - Not NULL - éžç©º + + Collate + å­—ç¬¦åº - - Check condition - æ¡ä»¶ + + Not NULL + éžç©º - - Primary Key - 主键 + + Check condition + æ¡ä»¶ - - Default - 默认 + + Primary Key + 主键 - - Advanced mode - é«˜çº§æ¨¡å¼ + + Default + 默认 - - Add constraint - column dialog - æ·»åŠ çº¦æŸ + + Advanced mode + é«˜çº§æ¨¡å¼ - - Edit constraint - column dialog - ç¼–è¾‘çº¦æŸ + + Add constraint + column dialog + æ·»åŠ çº¦æŸ - - - Delete constraint - column dialog - åˆ é™¤çº¦æŸ + + Edit constraint + column dialog + ç¼–è¾‘çº¦æŸ - - Move constraint up - column dialog - ä¸Šç§»çº¦æŸ + + + Delete constraint + column dialog + åˆ é™¤çº¦æŸ - - Move constraint down - column dialog - ä¸‹ç§»çº¦æŸ + + Move constraint up + column dialog + ä¸Šç§»çº¦æŸ - - Add a primary key - column dialog - 添加主键 + + Move constraint down + column dialog + ä¸‹ç§»çº¦æŸ - - Add a foreign key - column dialog - 添加外键 + + Add a primary key + column dialog + 添加主键 - - Add an unique constraint - column dialog - æ·»åŠ å”¯ä¸€çº¦æŸ + + Add a foreign key + column dialog + 添加外键 - - Add a check constraint - column dialog - 添加æ¡ä»¶çº¦æŸ + + Add an unique constraint + column dialog + æ·»åŠ å”¯ä¸€çº¦æŸ - - Add a not null constraint - column dialog - 添加éžç©ºçº¦æŸ + + Add a check constraint + column dialog + 添加æ¡ä»¶çº¦æŸ - - Add a collate constraint - column dialog - 添加排åºè§„åˆ™çº¦æŸ + + Add a not null constraint + column dialog + 添加éžç©ºçº¦æŸ - - Add a default constraint - column dialog - æ·»åŠ é»˜è®¤çº¦æŸ + + Add a collate constraint + column dialog + 添加字符åºçº¦æŸ - - Are you sure you want to delete constraint '%1'? - column dialog - 您确定è¦åˆ é™¤çº¦æŸâ€œ%1â€å—? + + Add a generated value constraint + column dialog + 添加生æˆçš„å€¼çº¦æŸ - - Correct the constraint's configuration. - 修正约æŸé…置。 + + Add a default constraint + column dialog + æ·»åŠ é»˜è®¤çº¦æŸ - - This constraint is not officially supported by SQLite 2, -but it's okay to use it. - SQLite 2 没有官方支æŒè¯¥çº¦æŸï¼Œä½†æ˜¯å¯ä»¥ä½¿ç”¨ã€‚ + + Are you sure you want to delete constraint '%1'? + column dialog + 确定è¦åˆ é™¤çº¦æŸâ€œ%1â€å—? - - Scale is not allowed for INTEGER PRIMARY KEY columns. - å°æ•°é•¿åº¦åœ¨ INTEGER PRIMARY KEY 类型字段中ä¸è¢«å…许。 + + Correct the constraint's configuration. + 请纠正约æŸé…置。 - - Precision cannot be defined without the scale. - + + Scale is not allowed for INTEGER PRIMARY KEY columns. + INTEGER PRIMARY KEY 列中ä¸å…è®¸å°æ•°ä½æ•°ã€‚ - - Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - + + Precision cannot be defined without the scale. + æœ‰å°æ•°ä½æ•°æ‰èƒ½å®šä¹‰ç²¾åº¦ã€‚ - - INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + 主键(PRIMARY KEY)已å¯ç”¨è‡ªåŠ¨é€’å¢žï¼ˆAUTOINCREMENT),ä¸èƒ½ä½¿ç”¨ INTEGER 以外的类型。 - - Precision is not allowed for INTEGER PRIMARY KEY columns. - + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + 主键(PRIMARY KEY)已å¯ç”¨è‡ªåŠ¨é€’å¢žï¼ˆAUTOINCREMENT),强制使用 INTEGER 类型。 - - - ColumnDialogConstraintsModel - - Type - column dialog constraints - 类型 + + Precision is not allowed for INTEGER PRIMARY KEY columns. + ä¸å…许对整型主键(INTEGER PRIMARY KEY)设置精度。 - - Name - column dialog constraints - åç§° + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + ColumnDialogConstraintsModel - - Details - column dialog constraints - 详情 + + Type + column dialog constraints + 类型 - - - ColumnForeignKeyPanel - - Foreign table: - 外部表: + + Name + column dialog constraints + åç§° - - Foreign column: - 外部字段: + + Details + column dialog constraints + 详情 + + + ColumnForeignKeyPanel - - Reactions - å“应 + + Foreign table: + 外部表: - - Deferred foreign key - + + Foreign column: + 外部字段: - - Named constraint - 已命åçš„çº¦æŸ + + Reactions + å“应 - - Constraint name - 约æŸåç§° + + Deferred foreign key + å»¶è¿Ÿå¤–é”®çº¦æŸ - - Pick the foreign table. - 选择一个外部表。 + + Named constraint + 命åçš„çº¦æŸ - - Pick the foreign column. - 选择一个外部字段。 + + Constraint name + 约æŸåç§° - - Enter a name of the constraint. - 输入约æŸåç§° + + Pick the foreign table. + 选择一个外部表。 - - - ColumnPrimaryKeyPanel - - Autoincrement - Autoincrement + + Pick the foreign column. + 选择一个外部字段。 - - Sort order: - 排åºï¼š + + Enter a name of the constraint. + 输入约æŸåç§° + + + ColumnGeneratedPanel - - Named constraint: - 已命å的约æŸï¼š + + Generating code: + 生æˆä»£ç ï¼š - - On conflict: - å½“å†²çªæ—¶ï¼š + + Explicit type: + 显å¼ç±»åž‹ï¼š - - Enter a name of the constraint. - 输入约æŸåç§° + + Use "GENERATED ALWAYS" keywords + 使用 "GENERATED ALWAYS" 关键字 - Autoincrement (only for %1 type columns) - column primary key - Autoincrement (åªèƒ½%1类型的字段æ‰èƒ½æœ‰è¯¥å±žæ€§) + + Named constraint: + 命å的约æŸï¼š - - - ColumnUniqueAndNotNullPanel - - Named constraint: - 已命å的约æŸï¼š + + Enter the column value generating expression. + 请输入列值生æˆè¡¨è¾¾å¼ã€‚ - - On conflict: - å½“å†²çªæ—¶ï¼š + + Invalid value generating expression: %1. + 无效的值生æˆè¡¨è¾¾å¼ï¼š%1。 - - Enter a name of the constraint. - 输入约æŸå。 + + Invalid value generating expression. + 无效的值生æˆè¡¨è¾¾å¼ã€‚ - - - CompleterWindow - - Column: %1 - completer statusbar - 字段:%1 + + Enter a name of the constraint. + 输入约æŸå称。 + + + ColumnPrimaryKeyPanel - - Table: %1 - completer statusbar - 表:%1 + + Autoincrement + 自动递增 - - Index: %1 - completer statusbar - 索引:%1 + + Sort order: + 排åºï¼š - - Trigger: %1 - completer statusbar - 触å‘器:%1 + + Named constraint: + 命å的约æŸï¼š - - View: %1 - completer statusbar - 视图:%1 + + On conflict: + å½“å†²çªæ—¶ï¼š - - Database: %1 - completer statusbar - æ•°æ®åº“:%1 + + Enter a name of the constraint. + 请输入约æŸå称。 - - Keyword: %1 - completer statusbar - 关键字:%1 + + Descending order is not allowed with AUTOINCREMENT. + 自增ä¸å…许é™åºã€‚ + + + ColumnUniqueAndNotNullPanel - - Function: %1 - completer statusbar - 函数:%1 + + Named constraint: + 命å的约æŸï¼š - - Operator: %1 - completer statusbar - æ“作符:%1 + + On conflict: + å½“å†²çªæ—¶ï¼š - - String - completer statusbar - 字符串 + + Enter a name of the constraint. + 请输入约æŸå称。 + + + CompleterWindow - - Number - completer statusbar - 数值 + + Column: %1 + completer statusbar + 列:%1 - - Binary data - completer statusbar - äºŒè¿›åˆ¶æ•°æ® + + Table: %1 + completer statusbar + 表:%1 - - Collation: %1 - completer statusbar - 排åºè§„则:%1 + + Index: %1 + completer statusbar + 索引:%1 - - Pragma function: %1 - completer statusbar - + + Trigger: %1 + completer statusbar + 触å‘器:%1 - - - ConfigDialog - - - Configuration - é…ç½® + + View: %1 + completer statusbar + 视图:%1 - - Search - æœç´¢ + + Database: %1 + completer statusbar + æ•°æ®åº“:%1 - - General - 通用 + + Keyword: %1 + completer statusbar + 关键字:%1 - - Keyboard shortcuts - å¿«æ·é”® + + Function: %1 + completer statusbar + 函数:%1 - - Look & feel - 外观 + + Operator: %1 + completer statusbar + æ“作符:%1 - - Style - 风格 + + String + completer statusbar + 字符串 - - Fonts - 字体 + + Number + completer statusbar + 数值 - - Colors - 颜色 + + Binary data + completer statusbar + äºŒè¿›åˆ¶æ•°æ® - - Plugins - æ’ä»¶ + + Collation: %1 + completer statusbar + 字符åºï¼š%1 - - Code formatters - ä»£ç æ ¼å¼åŒ– + + Pragma function: %1 + completer statusbar + PRAGMA 函数:%1 - - Data browsing - æµè§ˆæ•°æ® + + Insert a code snippet + æ’入代ç ç‰‡æ®µ + + + ConfigDialog - - Data editors - æ•°æ®ç¼–辑器 + + + Configuration + é…ç½® - - Database dialog window - æ•°æ®åº“对è¯çª—å£ + + Search + æœç´¢ - - <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> - + + General + 通用 - - Do not mark database to be "permanent" by default - + + Keyboard shortcuts + å¿«æ·é”® - - <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> - + + Look & feel + 外观 - - Try to bypass dialog completly when dropping database file onto the list - + + Style + 风格 - - Data browsing and editing - æµè§ˆå’Œç¼–è¾‘æ•°æ® + + Fonts + 字体 - - Number of data rows per page: - æ¯é¡µçš„行数: + + Code colors + 代ç é¢œè‰² - - - <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> - <p>åœ¨ä»¥åˆ—è¡¨æ–¹å¼æ˜¾ç¤ºæ•°æ®æ—¶ï¼Œåˆ—宽度会自动调整。该值控制åˆå§‹åˆ—å®½åº¦ï¼Œä¹‹åŽæ‚¨å¯ä»¥æ‰‹åŠ¨è°ƒæ•´åˆ—å®½åº¦ï¼Œä¸å—æ­¤é™åˆ¶ã€‚</p> + + + Database list + æ•°æ®åº“列表 - - Limit initial data column width to (in pixels): - é™åˆ¶åˆå§‹æ•°æ®åˆ—宽度(å•ä½ï¼šåƒç´ ï¼‰ï¼š + + Code assistant + 代ç åŠ©æ‰‹ - - <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> - + + Data browsing + æµè§ˆæ•°æ® - - Show column and row details tooltip in data view - 在数æ®è§†å›¾ä¸­å±•示字段与行的细节 + + Data editors + æ•°æ®ç¼–辑器 - - <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> - + + Plugins + æ’ä»¶ - - Inserting new row in data grid - 在网格视图中æ’入新行 + + Code formatters + ä»£ç æ ¼å¼åŒ–器 - - Before currently selected row - åœ¨å·²é€‰åˆ—ä¹‹å‰ + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + 如果关闭,列将按照 CREATE TABLE ä¸­çš„é¡ºåºæŽ’åºã€‚ - - After currently selected row - åœ¨å·²é€‰åˆ—ä¹‹åŽ + + Sort table columns alphabetically + 按字æ¯é¡ºåºåˆ—出表的列 - - At the end of data view - åœ¨æ•°æ®æ˜¾ç¤ºåŒºåŸŸçš„æœ«å°¾ + + Expand tables node when connected to a database + 连接到数æ®åº“时展开表节点 - - <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - <p>å¯ç”¨åŽï¼Œè¡¨çª—å£å°†æ˜¾ç¤ºæ•°æ®é€‰é¡¹å¡ï¼Œè€Œä¸æ˜¯ç»“构选项å¡ã€‚</p> + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>附加标签是显示在数æ®åº“列表的åç§°æ—边的文本标签(除éžå¦æœ‰é…置,å¦åˆ™ä¸ºè“色)。å¯ç”¨æ­¤é€‰é¡¹å°†ä¸ºæ•°æ®åº“ã€æ— æ•ˆæ•°æ®åº“å’ŒèšåˆèŠ‚ç‚¹ï¼ˆåˆ—ç»„ã€ç´¢å¼•组ã€è§¦å‘器组)显示标签。更多标签è§ä¸‹æ–¹é€‰é¡¹ã€‚<p> - - <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - <p>å¯ç”¨åŽï¼Œâ€œæ•°æ®â€é€‰é¡¹å¡å°†ä½œä¸ºç¬¬ä¸€ä¸ªé€‰é¡¹å¡æ”¾ç½®åœ¨æ¯ä¸ªè¡¨çª—å£ä¸­ï¼Œè€Œä¸æ˜¯ä½äºŽç¬¬äºŒä½ã€‚</p> + + Display additional labels on the list + 在列表中显示附加标签 - - Place data tab as first tab in a Table Window - 将数æ®ä½œä¸ºè¡¨çª—å£çš„第一项 + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + 对于普通表,标签将显示æ¯ä¸ªè¡¨çš„列ã€ç´¢å¼•和触å‘器的数é‡ã€‚ - - <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - <p>å¯ç”¨åŽï¼Œè§†å›¾çª—å£å°†æ˜¾ç¤ºæ•°æ®é€‰é¡¹å¡ï¼Œè€Œä¸æ˜¯ç»“构选项å¡ã€‚</p> + + Display labels for regular tables + 为普通表显示标签 - - <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - <p>å¯ç”¨åŽï¼Œâ€œæ•°æ®â€é€‰é¡¹å¡å°†ä½œä¸ºç¬¬ä¸€ä¸ªé€‰é¡¹å¡æ”¾ç½®åœ¨æ¯ä¸ªè§†å›¾çª—å£ä¸­ï¼Œè€Œä¸æ˜¯ä½äºŽç¬¬äºŒä¸ªä½ç½®ã€‚</p> + + Virtual tables will be marked with a 'virtual' label. + è™šæ‹Ÿè¡¨å°†é™„ä»¥â€œè™šæ‹Ÿâ€æ ‡ç­¾ã€‚ - - Place data tab as first tab in a View Window - 将数æ®é€‰é¡¹å¡ä½œä¸ºè§†å›¾çª—å£çš„ç¬¬ä¸€é€‰é¡¹å¡ + + Display labels for virtual tables + 为虚拟表显示标签 - - Data types - æ•°æ®ç±»åž‹ + + Expand views node when connected to a database + 连接到数æ®åº“时展开视图节点 - - Available editors: - å¯ç”¨çš„编辑器: + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + 如果关闭此选项,则对象将按照 sqlite_master 表中的顺åºï¼ˆå³å®ƒä»¬è¢«åˆ›å»ºçš„顺åºï¼‰æŽ’列 - - Editors selected for this data type: - 已选的该数æ®ç±»åž‹ç¼–辑器: + + Sort objects (tables, indexes, triggers and views) alphabetically + 按字æ¯é¡ºåºæŽ’列对象(表ã€ç´¢å¼•ã€è§¦å‘器åŠè§†å›¾ï¼‰ - - Schema editing - 架构编辑 + + Display system tables and indexes on the list + 在列表中显示系统表和索引 - - Number of DDL changes kept in history. - æ•°æ®åº“定义(DDL)的更改历å²è®°å½•æ•°é‡ã€‚ + + Database dialog window + æ•°æ®åº“å¯¹è¯æ¡†çª—å£ - - DDL history size: - æ•°æ®åº“定义(DDL)历å²å¤§å°ï¼š + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>添加新的数æ®åº“时,默认会将其标记为“永久â€ï¼ˆå­˜å‚¨åœ¨é…置文件中)。选中此选项则新添加的数æ®åº“å°†é»˜è®¤ä¸æ ‡è®°ä¸ºâ€œæ°¸ä¹…â€ã€‚</p> - Don't show DDL preview dialog when commiting schema changes - 当æäº¤schemaå˜åŠ¨æ—¶ä¸æ˜¾ç¤ºæ•°æ®åº“定义(DDLï¼‰é¢„è§ˆå¯¹è¯æ¡† + + Do not mark database to be "permanent" by default + 默认ä¸å°†æ–°æ·»åŠ çš„æ•°æ®åº“标为“永久†- - SQL queries - SQL 查询 + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>å¯ç”¨æ­¤é€‰é¡¹åŽï¼Œå°†æ–‡ä»¶ä»Žæ–‡ä»¶ç®¡ç†å™¨æ‹–放到数æ®åº“列表中时,将自动绕过标准的数æ®åº“å¯¹è¯æ¡†ï¼Œç›´æŽ¥æ·»åŠ åˆ°åˆ—è¡¨ä¸­ã€‚å¦‚æžœç”±äºŽå„ç§åŽŸå› è‡ªåŠ¨å¤„ç†å¤±è´¥ï¼Œåˆ™è¿˜ä¼šå‘ç”¨æˆ·æ˜¾ç¤ºæ ‡å‡†å¯¹è¯æ¡†ã€‚</p> - - - Number of queries kept in the history. - 查询历å²è®°å½•æ•°é‡ã€‚ + + Try to bypass dialog completly when dropping database file onto the list + 拖放数æ®åº“文件到列表中时å°è¯•å®Œå…¨ç»•è¿‡å¯¹è¯æ¡† - - History size: - 历å²å¤§å°ï¼š + + Data browsing and editing + æµè§ˆå’Œç¼–è¾‘æ•°æ® - - <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> - <p>如果SQL编辑器中有多个语å¥ï¼Œå¦‚æžœå¯ç”¨è¯¥é€‰é¡¹ï¼Œåªæ‰§è¡Œå…‰æ ‡ä¸‹çš„语å¥ï¼›å之则执行全部语å¥ã€‚å¦å¤–您å¯ä»¥é€‰æ‹©éœ€è¦æ‰§è¡Œçš„è¯­å¥æ¥æ‰§è¡Œ</p> + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>在é…置文件中存储,“表填充â€å¯¹è¯æ¡†ä¸­çš„æœ€å¤§æ•°é‡ã€‚值 100 应已足够。</p> - - Execute only the query under the cursor - åªæ‰§è¡Œå…‰æ ‡ä¸‹çš„è¯­å¥ + + Number of memorized table populating configurations + 表填充é…置中的默认填充行数 - - Updates - æ›´æ–° + + Data column width + æ•°æ®åˆ—宽度 - - Automatically check for updates at startup - 在å¯åŠ¨æ—¶è‡ªå·±æ£€æŸ¥æ›´æ–° + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> - - Session - ä¼šè¯ + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width - - Restore last session (active MDI windows) after startup - å¯åŠ¨åŽæ¢å¤ä¸Šä¸€æ¬¡ä¼šè¯ã€‚ + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>当数æ®è¢«åŠ è½½åˆ°ç½‘æ ¼è§†å›¾æ—¶ï¼Œåˆ—å®½ä¼šè‡ªåŠ¨è°ƒæ•´ã€‚æ­¤å€¼é™åˆ¶åˆå§‹åŒ–æ—¶çš„åˆ—å®½åº¦ï¼Œä¹‹åŽæ‚¨ä»å¯ä»¥æ‰‹åŠ¨è°ƒæ•´åˆ—å®½ï¼Œä¸å—æ­¤é™åˆ¶ã€‚</p> - - Status Field - çŠ¶æ€æ  + + Number of data rows per page: + æ¯é¡µæ•°æ®è¡Œæ•°ï¼š - - <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> - + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>此项å¯ç”¨åŽï¼Œç”¨æˆ·é¼ æ ‡æ‚¬åœåœ¨ä»»æ„æ•°æ®è§†å›¾ï¼ˆæŸ¥è¯¢ç»“æžœã€è¡¨æ•°æ®ã€è§†å›¾æ•°æ®ï¼‰çš„å•元格上时,工具æç¤ºå°†æ˜¾ç¤ºè¯¥å•元格的详细信æ¯â€”—包括列数æ®ç±»åž‹ã€çº¦æŸã€ROWID 等。</p> - - Always open Status panel when new message is printed - 当有新信æ¯è¢«è¾“出时,总是打开状æ€é¢æ¿ + + Show column and row details tooltip in data view + 在数æ®è§†å›¾ä¸­å±•ç¤ºåˆ—ä¸Žè¡Œçš„è¯¦ç»†ä¿¡æ¯ - - Filter shortcuts by name or key combination - 以å称或按键组åˆç­›é€‰å¿«æ·é”® + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>编辑一个过去为 NULL 值的å•元格且新输入的值为空字符串时,此选项å¯ç”¨åˆ™è¯¥å•å…ƒæ ¼çš„å€¼ä¿æŒ NULL ä¸å˜ï¼Œæ­¤é€‰é¡¹æœªå¯ç”¨åˆ™ç©ºå­—符串覆盖原 NULL 值。</p> - - Action - æ“作 + + Keep NULL value when entering empty value + å½“è¾“å…¥ç©ºå€¼æ—¶ä¿æŒ NULL 值 - - Key combination - 按键编定 + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>å¯ç”¨æ­¤é€‰é¡¹åŽï¼Œæäº¤ä¸€ä¸ª NULL 值时,如果该列已定义 DEFAULT 值,å³ä½¿è¯¥åˆ—å…è®¸åŒ…å« NULL 值,也始终采用 DEFAULT 值。</p><p>ç¦ç”¨æ­¤é€‰é¡¹åˆ™ä»…在列有éžç©ºï¼ˆNOT NULLï¼‰çº¦æŸæ—¶å°† NULL 值转å˜ä¸º DEFAULT 值。</p></body></html> - - - Language - 语言 + + Use DEFAULT value (if defined), when committing NULL value + æäº¤ NULL 值时使用 DEFAULT 值(如果已定义) - - Changing language requires application restart to take effect. - 更改语言åŽï¼Œé‡å¯ç¨‹åºç”Ÿæ•ˆã€‚ + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>å¦‚æžœæŸ¥è¯¢ç»“æžœåŒ…å«æ•°å乃至上百个列,则加载å¯èƒ½å ç”¨æ•°ä¸ª GB 的空闲内存。SQLiteStudio åœ¨è¿™ç§æƒ…况下å¯èƒ½é™åˆ¶ä¸€é¡µä¸Šæ˜¾ç¤ºçš„结果数é‡ä»¥ä¿æŠ¤æ‚¨çš„计算机。如果您了解自己ä¸ä¼šåœ¨å¦‚此大的数æ®åº“上作业,则å¯ä»¥ç¦ç”¨æ­¤é™åˆ¶ä»¥å§‹ç»ˆåœ¨ä¸€é¡µä¸Šçœ‹åˆ°å¤§é‡çš„行。</p></body></html> - - Compact layout - 紧凑布局 + + Limit number of rows for in case of dozens of columns + 列数过多时é™åˆ¶æ¯é¡µæ˜¾ç¤ºçš„行数 - - <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - <p>紧凑布局会将 UI 中的边框与空白é™ä½Žåˆ°æœ€å°å€¼ï¼Œç„¶åŽç”¨è¿™äº›ç©ºç™½å±•示更多数æ®ã€‚这会使界é¢çœ‹èµ·æ¥æœ‰ä¸€ç‚¹ä¸ç¾Žè§‚,但是å…许一次展示更多数æ®ã€‚</p> + + Inserting new row in data grid + 网格视图中æ’入新行时 - - Use compact layout - 使用紧凑布局 + + Before currently selected row + åœ¨é€‰ä¸­è¡Œä¹‹å‰ - - - Database list - æ•°æ®åº“列表 + + After currently selected row + åœ¨é€‰ä¸­è¡Œä¹‹åŽ - - If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. - 如果关闭,将会以 CREATE TABLE 中的顺åºå¯¹åˆ—进行排åºã€‚ + + At the end of data view + 在数æ®è§†å›¾çš„æœ«å°¾ - - Sort table columns alphabetically - 按字æ¯å¯¹åˆ—æŽ’åº + + Table windows + è¡¨çª—å£ - - Expand tables node when connected to a database - 当连接到数æ®åº“时,展开数æ®åº“节点 + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>å¯ç”¨åŽï¼Œè¡¨çª—å£å°†æ˜¾ç¤ºæ•°æ®é€‰é¡¹å¡ï¼Œè€Œä¸æ˜¯ç»“构选项å¡ã€‚</p> - - <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> - + + Open Table Windows with the data tab for start + æ‰“å¼€è¡¨çª—å£æ—¶æ˜¾ç¤ºâ€œæ•°æ®â€é€‰é¡¹å¡ - - Display additional labels on the list - + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>å¯ç”¨åŽï¼Œâ€œæ•°æ®â€é€‰é¡¹å¡å°†ä½œä¸ºç¬¬ä¸€ä¸ªé€‰é¡¹å¡æ”¾ç½®åœ¨æ¯ä¸ªè¡¨çª—å£ä¸­ï¼Œè€Œä¸æ˜¯ä½äºŽç¬¬äºŒä½ã€‚</p> - - For regular tables labels will show number of columns, indexes and triggers for each of tables. - + + Place data tab as first tab in a Table Window + 将数æ®ä½œä¸ºè¡¨çª—å£çš„第一项 - - Display labels for regular tables - + + View windows + è§†å›¾çª—å£ - - Virtual tables will be marked with a 'virtual' label. - + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>å¯ç”¨åŽï¼Œè§†å›¾çª—å£å°†æ˜¾ç¤ºæ•°æ®é€‰é¡¹å¡ï¼Œè€Œä¸æ˜¯ç»“构选项å¡ã€‚</p> - - Display labels for virtual tables - + + Open View Windows with the data tab for start + æ‰“å¼€è§†å›¾çª—å£æ—¶æ˜¾ç¤ºâ€œæ•°æ®â€é€‰é¡¹å¡ - - Expand views node when connected to a database - 当连接到数æ®åº“时,展开视图节点 + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>å¯ç”¨åŽï¼Œâ€œæ•°æ®â€é€‰é¡¹å¡å°†ä½œä¸ºç¬¬ä¸€ä¸ªé€‰é¡¹å¡æ”¾ç½®åœ¨æ¯ä¸ªè§†å›¾çª—å£ä¸­ï¼Œè€Œä¸æ˜¯ä½äºŽç¬¬äºŒä¸ªä½ç½®ã€‚</p> - - If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) - + + Place data tab as first tab in a View Window + 将“数æ®â€é€‰é¡¹å¡æ”¾ç½®ä¸ºè§†å›¾çª—å£çš„é¦–ä¸ªé€‰é¡¹å¡ - - Sort objects (tables, indexes, triggers and views) alphabetically - 按字æ¯é¡ºåºæŽ’åºå¯¹è±¡ï¼ˆè¡¨ï¼Œç´¢å¼•,触å‘器与视图) + + Data types + æ•°æ®ç±»åž‹ - - Display system tables and indexes on the list - + + Available editors: + å¯ç”¨çš„编辑器: - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - + + Editors selected for this data type: + 已为该数æ®ç±»åž‹é€‰æ‹©çš„编辑器: - - Number of memorized table populating configurations - + + Schema editing + 结构编辑 - - Keep NULL value when entering empty value - å½“è¾“å…¥ç©ºå€¼æ—¶ä¿æŒ NULL 值 + + Number of DDL changes kept in history. + DDL å˜æ›´åކå²çš„记录数é‡ã€‚ - - <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> - + + DDL history size: + DDL 历å²å¤§å°ï¼š - - Use DEFAULT value (if defined), when committing NULL value - 当æäº¤ NULL 值时使用 DEFAULT 值(如果已被定义) + + Don't show DDL preview dialog when committing schema changes + æäº¤ç»“æž„æ›´æ”¹æ—¶ä¸æ˜¾ç¤º DDL é¢„è§ˆå¯¹è¯æ¡† - - Table windows - è¡¨çª—å£ + + SQL queries + SQL 查询 - - Open Table Windows with the data tab for start - + + + Number of queries kept in the history. + 查询历å²è®°å½•æ•°é‡ã€‚ - - View windows - è§†å›¾çª—å£ + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> - - Open View Windows with the data tab for start - + + History size: + 历å²å¤§å°ï¼š - - Don't show DDL preview dialog when committing schema changes - 当æäº¤ schema 更改时ä¸è¦å±•示 DDL é¢„è§ˆå¯¹è¯æ¡† + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>历å²è®°å½•ä¸­å­˜å‚¨æŸ¥è¯¢å‚æ•°ï¼ˆ:param, @param, $param, ?)的最大数é‡ã€‚å½“æ‚¨é‡æ–°åœ¨åŒä¸€åç§°/ä½ç½®ä¸‹ä½¿ç”¨å‚数时,SQLiteStudio 将使用最近记忆的值预填充åˆå§‹åŒ–它(ä»å¯ä¿®æ”¹ï¼‰ã€‚值 1000 应已足够。</p> - - - <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> - + + Execute only the query under the cursor + åªæ‰§è¡Œè¾“å…¥ç¬¦æ‰€åœ¨è¡Œçš„è¯­å¥ - - Number of memorized query parameters - + + Number of memorized query parameters + é»˜è®¤çš„æŸ¥è¯¢å‚æ•°æ•°é‡ - - Main window dock areas - 主窗å£åœé åŒºåŸŸ + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> - - Left and right areas occupy corners - + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view - - Top and bottom areas occupy corners - + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> - - Hide built-in plugins - éšè—内建æ’ä»¶ + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): - - Current style: - 当å‰é£Žæ ¼ï¼š + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> - - Preview - 预览 + + Keep at least the width to show complete column name + Keep at least the width to show complete column name - - Enabled - å·²å¯ç”¨ + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> - - Disabled - å·²ç¦ç”¨ + + Wrap lines in SQL editor + 在 SQL 编辑器中æ¢è¡Œ - - Active formatter plugin - å¯ç”¨æ ¼å¼åŒ–æ’ä»¶ + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> - - SQL editor font - SQL 编辑器字体 + + Highlight current query + é«˜äº®å½“å‰æŸ¥è¯¢ - - Database list font - æ•°æ®åº“字体 + + Updates + æ›´æ–° - - Database list additional label font - æ•°æ®åº“é¢å¤–标签字体 + + Automatically check for updates at startup + å¯åŠ¨æ—¶è‡ªåŠ¨æ£€æŸ¥æ›´æ–° - - Data view font - æ•°æ®æµè§ˆå­—体 + + Session + ä¼šè¯ - - Status field font - çŠ¶æ€æ å­—体 + + Restore last session (active MDI windows) after startup + å¯åŠ¨åŽæ¢å¤ä¸Šä¸€æ¬¡ä¼šè¯ - - SQL editor colors - SQL 编辑器颜色 + + Allow multiple instances of the application at the same time + å…è®¸åŒæ—¶æ‰“å¼€å¤šä¸ªæ­¤ç¨‹åº - - Current line background - 当å‰è¡Œçš„背景色 + + Status Field + çŠ¶æ€æ  - - <p>SQL strings are enclosed with single quote characters.</p> - <p>å•引å·å†…çš„ SQL 字符串</p> + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>在用户手动关闭了状æ€é¢æ¿åŽï¼Œæ­¤é€‰é¡¹èƒ½ç¡®ä¿æœ‰æ–°æ¶ˆæ¯è¢«å‡ºçŽ°æ—¶çŠ¶æ€é¢æ¿è‡ªåŠ¨è¢«é‡æ–°å¼€å¯ã€‚如果ç¦ç”¨ï¼Œçжæ€é¢æ¿åªèƒ½ç”±ç”¨æˆ·æ‰‹åŠ¨é€šè¿‡â€œè§†å›¾â€èœå•æ¥é‡æ–°å¼€å¯ã€‚</p> - - String foreground - 字符串颜色 + + Always open Status panel when new message is printed + 有新消æ¯è¾“出时就打开状æ€é¢æ¿ - - <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - + + Code syntax colors + Code syntax colors - - Bind parameter foreground - + + Keyword foreground + Keyword foreground - - Highlighted parenthesis background - + + Regular foreground + Regular foreground - - <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - + + String foreground + String foreground - - BLOB value foreground - BLOB 值的颜色 + + Comment foreground + Comment foreground - - Regular foreground - 背景色 + + Valid objects foreground + Valid objects foreground - - Line numbers area background - 行å·çš„背景色 + + Current query background + Current query background - - Keyword foreground - 关键字的颜色 + + Bind parameter foreground + Bind parameter foreground - - Number foreground - 数字颜色 + + Current line background + Current line background - - Comment foreground - 注释颜色 + + Matched parenthesis background + Matched parenthesis background - - <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> - + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> - - Valid objects foreground - åˆæ³•对象的颜色 + + Number foreground + Number foreground - - Data view colors - æ•°æ®è§†å›¾é¢œè‰² + + BLOB value foreground + BLOB value foreground - - <p>Any data changes will be outlined with this color, until they're committed to the database.</p> - + + Matched parenthesis foreground + Matched parenthesis foreground - - Uncommitted data outline color - 未æäº¤æ•°æ®çš„轮廓颜色 + + Reset to defaults + é‡ç½®ä¸ºé»˜è®¤å€¼ - - <p>In case of error while committing data changes, the problematic cell will be outlined with this color.</p> - + + Filter shortcuts by name or key combination + 以å称或按键组åˆç­›é€‰å¿«æ·é”® - - Commit error outline color - æäº¤é”™è¯¯çš„轮廓颜色 + + Action + æ“作 - - NULL value foreground - NULL 值的颜色 + + Key combination + æŒ‰é”®ç»„åˆ - - Deleted row background - 已删除行的背景色 + + + Language + 语言 - - Database list colors - æ•°æ®åº“列表颜色 + + Changing language requires application restart to take effect. + è¯­è¨€å˜æ›´åœ¨ç¨‹åºé‡å¯åŽç”Ÿæ•ˆã€‚ - - <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> - + + Compact layout + 紧凑布局 - - Additional labels foreground - + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>紧凑布局会将界é¢ä¸­çš„边框与留白å‡è‡³æœ€å°ï¼Œç„¶åŽç”¨è¿™äº›åŒºåŸŸå±•示更多数æ®ã€‚这会使界é¢çœ‹èµ·æ¥æœ‰ä¸€ç‚¹ä¸ç¾Žè§‚,但将å¯ä»¥åŒæ—¶å±•示更多的数æ®ã€‚</p> - - Status field colors - çŠ¶æ€æ é¢œè‰² + + Use compact layout + 使用紧凑布局 - - Information message foreground - ä¿¡æ¯é¢œè‰² + + Main window dock areas + 主窗å£åœé åŒºåŸŸ - - Warning message foreground - 警告信æ¯é¢œè‰² + + Left and right areas occupy corners + å·¦å³å¸ƒå±€ - - Error message foreground - 错误信æ¯é¢œè‰² + + Top and bottom areas occupy corners + 上下布局 - - Description: - plugin details - æè¿°ï¼š + + Hide built-in plugins + éšè—内置æ’ä»¶ - - Category: - plugin details - 分类: + + Current style: + 当å‰é£Žæ ¼ï¼š - - Version: - plugin details - 版本: + + Preview + 预览 - - Author: - plugin details - 作者: + + Enabled + å·²å¯ç”¨ - - Internal name: - plugin details - 内部å字: + + Disabled + å·²ç¦ç”¨ - - Dependencies: - plugin details - ä¾èµ–: + + Active formatter plugin + å¯ç”¨æ ¼å¼åŒ–æ’ä»¶ - - Conflicts: - plugin details - 冲çªï¼š + + SQL editor font + SQL 编辑器字体 - - Plugin details - æ’件详情 + + Database list font + æ•°æ®åº“列表字体 - - Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. - + + Database list additional label font + æ•°æ®åº“列表é¢å¤–ä¿¡æ¯å­—体 - - %1 (built-in) - plugins manager in configuration dialog - %1 (内建) + + Data view font + æ•°æ®è§†å›¾å­—体 - - Details - 详情 + + Status field font + çŠ¶æ€æ å­—体 - - No plugins in this category. - 该分类下没有æ’件。 + + Code assistant settings + Code assistant settings - - Add new data type - 添加新的数æ®ç±»åž‹ + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> - - Rename selected data type - é‡å‘½å选择的数æ®ç±»åž‹ + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name - - Delete selected data type - 删除已选数æ®ç±»åž‹ + + Description: + plugin details + æè¿°ï¼š - - Help for configuring data type editors - + + Category: + plugin details + 分类: - - - ConstraintCheckPanel - - The condition - æ¡ä»¶ + + Version: + plugin details + 版本: - - Named constraint: - 已命å的约æŸï¼š + + Author: + plugin details + 作者: - - On conflict - å½“å†²çªæ—¶ + + Internal name: + plugin details + 内部å称: - - Enter a valid condition. - è¾“å…¥ä¸€ä¸ªåˆæ³•çš„æ¡ä»¶ã€‚ + + Dependencies: + plugin details + ä¾èµ–: - - Enter a name of the constraint. - 输一个约æŸçš„å称。 + + Conflicts: + plugin details + 冲çªï¼š - - - ConstraintDialog - - New constraint - constraint dialog - æ–°çº¦æŸ + + Plugin details + æ’件详情 - - Create - constraint dialog - 创建 + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + åˆ‡æ¢æ—¶æ’件会立å³åŠ è½½/å¸è½½ã€‚但此列表的修改仅在点击确定按钮åŽè¢«ä¿å­˜ã€‚ - - Edit constraint - dialog window - ç¼–è¾‘çº¦æŸ + + %1 (built-in) + plugins manager in configuration dialog + %1(内置) - - Apply - constraint dialog - 应用 + + Details + 详情 - - Primary key - table constraints - 主键 + + No plugins in this category. + 没有此分类下的æ’件。 - - Foreign key - table constraints - 外键 + + Add new data type + 添加新的数æ®ç±»åž‹ - - Unique - table constraints - 唯一 + + Rename selected data type + é‡å‘½å所选数æ®ç±»åž‹ - - Not NULL - table constraints - éž NULL + + Delete selected data type + 删除所选数æ®ç±»åž‹ - - Check - table constraints - æ¡ä»¶ + + Help for configuring data type editors + é…置数æ®ç±»åž‹ç¼–辑器帮助 - - Collate - table constraints - 排åºè§„则 + + Clear hotkey for this action + Clear hotkey for this action - - Default - table constraints - 默认 + + Restore original hotkey for this action + Restore original hotkey for this action - - - ConstraintTabModel - - Table - table constraints - 表 + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + ConstraintCheckPanel - - Column (%1) - table constraints - 字段(%1) + + The condition + æ¡ä»¶ - - Scope - table constraints - 作用域 + + Named constraint: + 命å的约æŸï¼š - - Type - table constraints - 类型 + + On conflict + å½“å†²çªæ—¶ - - Details - table constraints - 详情 + + Enter a valid condition. + 输入一个有效的æ¡ä»¶ã€‚ - - Name - table constraints - åç§° + + Enter a name of the constraint. + 输入一个约æŸå称。 - - - CssDebugDialog + + + ConstraintDialog - - SQLiteStudio CSS console - SQLiteStudio CSS æŽ§åˆ¶å° + + New constraint + constraint dialog + æ–°å»ºçº¦æŸ - - - DataView - - Filter data - data view - ç­›é€‰æ•°æ® + + Create + constraint dialog + 创建 - - Grid view - 网格视图 + + Edit constraint + dialog window + ç¼–è¾‘çº¦æŸ - - Form view - 表格视图 + + Apply + constraint dialog + 应用 - - Refresh table data - data view - åˆ·æ–°è¡¨æ•°æ® + + Primary key + table constraints + 主键 - - First page - data view - 第一页 + + Foreign key + table constraints + 外键 - - Previous page - data view - 上一页 + + Unique + table constraints + 唯一 - - Next page - data view - 下一页 + + Not NULL + table constraints + éžç©º - - Last page - data view - 最åŽä¸€é¡µ + + Check + table constraints + æ¡ä»¶ - - Filter - 筛选 + + Generated + table constraints + ç”Ÿæˆ - - Hit Enter key or press "Apply filter" button on toolbar to apply new value. - 按下回车或点击工具æ ä¸Šçš„应用筛选按钮æ¥åº”用新值。 + + Collate + table constraints + å­—ç¬¦åº - - Show filter inputs per column - data view - 在æ¯ä¸€ä¸ªå­—段上展示筛选器输入 + + Default + table constraints + 默认 + + + ConstraintTabModel - - Apply filter - data view - 应用筛选 + + Table + table constraints + 表 - - Commit changes for selected cells - data view - æäº¤é€‰ä¸­å•元格的更改 + + Column (%1) + table constraints + 列(%1) - - Rollback changes for selected cells - data view - 回滚选中å•元格的修改 + + Scope + table constraints + 作用域 - - Show grid view of results - sql editor - 展示结果的网格视图 + + Type + table constraints + 类型 - - Show form view of results - sql editor - 展示结果的表格视图 + + Details + table constraints + 详情 - - Filter by text - data view - 以文本筛选 + + Name + table constraints + åç§° + + + CssDebugDialog - - Filter by the Regular Expression - data view - 以正则表达å¼ç­›é€‰ + + SQLiteStudio CSS console + SQLiteStudio CSS æŽ§åˆ¶å° + + + DataView - - Filter by SQL expression - data view - 以 SQL 表达å¼ç­›é€‰ + + Filter data + data view + ç­›é€‰æ•°æ® - - Tabs on top - data view - + + Grid view + 网格视图 - - Tabs at bottom - data view - + + Form view + 表å•视图 - - Place new rows above selected row - data view - 放置新行于选中行之上 + + Refresh table data + data view + åˆ·æ–°è¡¨æ•°æ® - - Place new rows below selected row - data view - 放置新行于选中行之下 + + First page + data view + 第一页 - - Place new rows at the end of the data view - data view - 放置新行于数æ®è§†å›¾æœ«å°¾ + + Previous page + data view + 上一页 - - Total number of rows is being counted. -Browsing other pages will be possible after the row counting is done. - + + Next page + data view + 下一页 - - Row: %1 - 行:%1 + + Last page + data view + 最åŽä¸€é¡µ - - - DbConverterDialog - - Convert database - è½¬æ¢æ•°æ®åº“ + + Commit changes for selected cells + data view + æäº¤é€‰ä¸­å•元格的更改 - - Source database - æºæ•°æ®åº“ + + Rollback changes for selected cells + data view + 回滚选中å•元格的修改 - - Source database version: - æºæ•°æ®åº“版本: + + Show grid view of results + data view + 显示结果的网格视图 - - Target database - 目的数æ®åº“ + + Show form view of results + data view + 显示结果的表格视图 - - Target version: - 目标版本: + + Filter by text (if contains) + data view + Filter by text (if contains) - - This is the file that will be created as a result of the conversion. - 此文件将会被创建,并作为转æ¢çš„结果。 + + Filter strictly by text (if equals) + data view + 严格按文本筛选(如果相等) - - Target file: - 目标文件: + + Tabs on top + data view + 顶部标签 - - Name of the new database: - æ–°æ•°æ®åº“çš„å称: + + Tabs at bottom + data view + 底部标签 - - This is the name that the converted database will be added to SQLiteStudio with. - + + Place new rows above selected row + data view + 放置新行于选中行之上 - - Select source database - é€‰æ‹©æºæ•°æ®åº“ + + Place new rows below selected row + data view + 放置新行于选中行之下 - - Enter valid and writable file path. - è¾“å…¥ä¸€ä¸ªåˆæ³•的且å¯å†™çš„æ–‡ä»¶çš„路径。 + + Place new rows at the end of the data view + data view + 放置新行于数æ®è§†å›¾æœ«å°¾ - - Entered file exists and will be overwritten. - 输入一个存在的且å¯è¦†å†™çš„æ–‡ä»¶ã€‚ + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + 正在统计总行数。 +请在此æ“作完æˆåŽå†æµè§ˆå…¶ä»–页é¢ã€‚ - - Enter a not empty, unique name (as in the list of databases on the left). - 输入一个éžç©ºï¼Œå”¯ä¸€çš„å称(用于左侧的数æ®åº“列表) + + Row: %1 + 行:%1 - - No valid target dialect available. Conversion not possible. - + + Filter + 筛选 - - Select valid target dialect. - + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + 按回车键或点击工具æ ä¸Šçš„â€œåº”ç”¨ç­›é€‰â€æŒ‰é’®æ¥åº”用新值。 - - Database %1 has been successfully converted and now is available under new name: %2 - + + Filter by the Regular Expression + data view + 以正则表达å¼ç­›é€‰ - - SQL statements conversion - + + Filter by SQL expression + data view + 以 SQL 表达å¼ç­›é€‰ - - Following error occurred while converting SQL statements to the target SQLite version: - + + Show filter inputs per column + data view + 在æ¯ä¸ªåˆ—上展示筛选器输入框 - - Would you like to ignore those errors and proceed? - 忽略错误并继续? + + Apply filter + data view + 应用筛选器 - - + + DbDialog - - Database - æ•°æ®åº“ - - - - Database type - æ•°æ®ç±»åž‹ - - - - Database driver - æ•°æ®åº“驱动 - - - - Options - 选项 + + Database + æ•°æ®åº“ - - Permanent (keep it in configuration) - è®°ä½è¯¥æ•°æ®åº“ + + Database type + æ•°æ®åº“类型 - - Test connection - 测试连接 + + Database driver + æ•°æ®åº“驱动 - Name - åç§° + + + File + 文件 - Type - 类型 + + Name (on the list) + å称(显示在列表中) - - Create new database file - 创建新数æ®åº“文件 + + Options + 选项 - - - File - 文件 + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>å¯ç”¨æ­¤é€‰é¡¹åŽï¼Œé…置文件中将记ä½è¯¥æ•°æ®åº“ï¼Œå¹¶åœ¨æ¯æ¬¡å¯åЍ SQLiteStudio 时还原(打开)它。</p> - - Name (on the list) - å称(显示在列表中) + + Permanent (keep it in configuration) + è®°ä½æ­¤æ•°æ®åº“ - - <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> - aasfd - <p>如果您想让这个数æ®åº“被存储在é…置文件中并且在 SQLiteStudio æ¯æ¬¡å¯åŠ¨ä¸­è¢«æ¢å¤ï¼Œè¯·å‹¾é€‰æ­¤é¡¹ã€‚</p> + + Test connection + 测试连接 - - Browse for existing database file on local computer - æµè§ˆè®¡ç®—上已存在的文件 + + Select new or existing file on local computer + 在本地计算机上选择新的或现有的文件 - - Browse - æµè§ˆ + + Browse + æµè§ˆ - - Enter an unique database name. - 请输入一个唯一的数æ®åº“å称。 + + Database type not selected. + 未选择数æ®åº“类型。 - - This name is already in use. Please enter unique name. - æ­¤å称已被使用,请输入一个唯一的å称。 + + Database path not specified. + 未指定数æ®åº“路径。 - - <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - <p>自动命å已被ç¦ç”¨ï¼Œå› ä¸ºå称已被手动编辑。为了æ¢å¤è‡ªåЍ命å,请删除å称中的所有内容。</p> + + Enter an unique database name. + 请输入一个唯一的数æ®åº“å称。 - - Enter a database file path. - 输入数æ®åº“文件ä½ç½®ã€‚ + + This name is already in use. Please enter unique name. + æ­¤å称已被使用,请输入一个未被å ç”¨çš„å称。 - - This database is already on the list under name: %1 - 该数æ®åº“已在列表中:%1 + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>å称已手动编辑,自动命åå·²ç¦ç”¨ã€‚清空åç§°æ ä¸­çš„内容将æ¢å¤è‡ªåЍ命å。</p> - - Select a database type. - 选择数æ®åº“类型。 + + Enter a database file path. + 输入一个数æ®åº“文件的路径。 - Auto-generated - 自动产生 + + This database is already on the list under name: %1 + 该数æ®åº“已在列表中,å为:%1 - Type the name - 输入åå­— + + Select a database type. + 请选择一个数æ®åº“类型。 - - + + DbObjectDialogs - - Delete table - 删除表 + + Delete table + 删除表 - - Are you sure you want to delete table %1? - 确定è¦åˆ é™¤è¡¨â€œ%1â€å—? + + Are you sure you want to delete table %1? + 确定è¦åˆ é™¤è¡¨â€œ%1â€å—? - - Delete index - 删除索引 + + Delete index + 删除索引 - - Are you sure you want to delete index %1? - 确定è¦åˆ é™¤ç´¢å¼•“%1â€å—? + + Are you sure you want to delete index %1? + 确定è¦åˆ é™¤ç´¢å¼•“%1â€å—? - - Delete trigger - 删除触å‘器 + + Delete trigger + 删除触å‘器 - - Are you sure you want to delete trigger %1? - 确定è¦åˆ é™¤è§¦å‘器“%1â€å—? + + Are you sure you want to delete trigger %1? + 确定è¦åˆ é™¤è§¦å‘器“%1â€å—? - - Delete view - 删除视图 + + Delete view + 删除视图 - - Are you sure you want to delete view %1? - 确定è¦åˆ é™¤è§†å›¾â€œ%1â€å—? + + Are you sure you want to delete view %1? + 确定è¦åˆ é™¤è§†å›¾â€œ%1â€å—? - - - Error while dropping %1: %2 - + + + Error while dropping %1: %2 + 丢弃 %1 时出错: %2 - - Delete objects - 删除对象 + + Delete objects + 删除对象 - - Are you sure you want to delete following objects: + + Are you sure you want to delete following objects: %1 - 您确认è¦åˆ é™¤ä»¥ä¸‹å¯¹è±¡å—: + 您确认è¦åˆ é™¤ä»¥ä¸‹å¯¹è±¡å—: %1 - - Cannot start transaction. Details: %1 - 无法开始事务。详情:%1 + + Cannot start transaction. Details: %1 + 无法开始事务。详情:%1 - - Cannot commit transaction. Details: %1 - 无法æäº¤äº‹åŠ¡ã€‚è¯¦æƒ…ï¼š%1 + + Cannot commit transaction. Details: %1 + 无法æäº¤äº‹åŠ¡ã€‚è¯¦æƒ…ï¼š%1 - - + + DbTree - - Databases - æ•°æ®åº“ - - - - Filter by name - 按å称过滤 - - - - Copy - å¤åˆ¶ - - - - Paste - 粘贴 - - - - Select all - 全选 - - - - Create a group - 创建分组 - - - - Delete the group - 删除分组 + + Databases + æ•°æ®åº“ - - Rename the group - é‡å‘½å分组 + + Filter by name + 按å称过滤 - Add a database - 添加数æ®åº“ + + Copy + å¤åˆ¶ - Edit the database - 编辑数æ®åº“ + + Paste + 粘贴 - Remove the database - 移除数æ®åº“ + + Select all + 全选 - Connect to the database - 连接到数æ®åº“ + + Create a group + 创建分组 - Disconnect from the database - 断开数æ®åº“连接 + + Delete the group + 删除分组 - - Import - 导入 + + Rename the group + é‡å‘½å分组 - Export the database - 导数该数æ®åº“ + + &Add a database + 添加数æ®åº“(&A) - Convert database type - è½¬æ¢æ•°æ®åº“类型 + + &Edit the database + 编辑数æ®åº“(&E) - Vacuum - æ¸…ç† + + &Remove the database + 移除数æ®åº“(&R) - Integrity check - 检查完整性 + + &Connect to the database + 连接到数æ®åº“(&C) - Create a table - 新建表 + + &Disconnect from the database + 断开数æ®åº“连接(&D) - Edit the table - 编辑该表 + + Import + 导入 - Delete the table - 删除该表 + + &Export the database + 导出数æ®åº“(&E) - - Export the table - 导出该表 + + Vac&uum + 释放空闲å ç”¨ï¼ˆVACUUM)(&U) - - Import into the table - 导入到该表 + + &Integrity check + 检查完整性(&I) - - Populate table - 填充表 + + Create a &table + 新建表(&T) - - Create similar table - 创建一个相似的表 + + Edit the t&able + 编辑表(&A) - - Reset autoincrement sequence - é‡è®¾ autoincrement + + Delete the ta&ble + 删除表(&B) - Create an index - 创建索引 + + Export the table + 导出表 - Edit the index - 编辑该索引 + + Import into the table + 导入到表 - Delete the index - 删除该索引 + + Populate table + 填充表 - Create a trigger - 创建触å‘器 + + Create similar table + 创建相似的表 - Edit the trigger - 编辑该触å‘器 + + Reset autoincrement sequence + é‡ç½®è‡ªåŠ¨é€’å¢žåºåˆ— - Delete the trigger - 删除该触å‘器 + + Create an &index + 创建索引(&I) - Create a view - 创建视图 + + Edit the i&ndex + 编辑索引(&N) - Edit the view - 编辑该视图 + + Delete the in&dex + 删除索引(&D) - Delete the view - 删除该视图 + + Create a trig&ger + 创建触å‘器(&G) - - Add a column - 添加字段 + + Edit the trigg&er + 编辑触å‘器(&E) - - Edit the column - 编辑该字段 + + Delete the trigge&r + 删除触å‘器(&R) - - Delete the column - 删除该字段 + + Create a &view + 创建视图(&V) - - Delete selected items - 删除已选项目 + + Edit the v&iew + 编辑视图(&I) - - Clear filter - 清除过滤器 + + Delete the vi&ew + 删除视图(&E) - Refresh all database schemas - 刷新全部数æ®åº“的结构 + + Add a column + 添加字段 - Refresh selected database schema - 刷新已选数æ®åº“的结构 + + Edit the column + 编辑字段 - - Execution from file cancelled. Any queries executed so far have been rolled back. - + + Delete the column + 删除字段 - - &Add a database - 添加数æ®åº“(&A) + + Delete selected items + 删除已选项目 - - &Edit the database - 编辑数æ®åº“(&E) + + Clear filter + 清除过滤器 - - &Remove the database - 移除数æ®åº“(&R) + + &Refresh all database schemas + 刷新全部数æ®åº“结构(&R) - - &Connect to the database - 连接到数æ®åº“(&C) + + Re&fresh selected database schema + 刷新已选数æ®åº“结构(&F) - - &Disconnect from the database - 断开数æ®åº“连接(&D) + + + Erase table data + æ“¦é™¤è¡¨æ•°æ® - - &Export the database - 导出该数æ®åº“(&E) + + Open file's directory + 打开文件所在目录 - - Con&vert database type - è½¬æ¢æ•°æ®åº“类型(&V) + + Execute SQL from file + 从文件执行 SQL - - Vac&uum - 清ç†(&U) + + Increase font size + database list + å¢žå¤§å­—å· - - &Integrity check - 检查完整性(&I) + + Decrease font size + database list + å‡å°å­—å· - - Create a &table - 新建表(&T) + + + Database + æ•°æ®åº“ - - Edit the t&able - 编辑该表(&A) + + Grouping + 分组 - - Delete the ta&ble - 删除该表(&B) + + Generate query for table + 生æˆå¯¹è¡¨çš„æŸ¥è¯¢ - - Create an &index - 创建索引(&I) + + + Create group + 创建分组 - - Edit the i&ndex - 编辑该索引(&N) + + Group name + 分组å - - Delete the in&dex - 删除该索引(&D) + + Entry with name %1 already exists in group %2. + åç§° %1 在分组 %2 中已存在。 - - Create a trig&ger - 创建触å‘器(&G) + + Delete group + 删除分组 - - Edit the trigg&er - 编辑该触å‘器(&E) - - - - Delete the trigge&r - 删除该触å‘器(&R) - - - - Create a &view - 创建视图(&V) - - - - Edit the v&iew - 编辑该视图(&I) - - - - Delete the vi&ew - 删除该视图(&E) + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + 确认删除组 %1 å—? +删除åŽè¯¥ç»„下的全部内容将被移动到其所属的父分组中。 - - &Refresh all database schemas - 刷新全部数æ®åº“的结构(&R) + + Are you sure you want to remove database '%1' from the list? + 确定è¦ç§»é™¤æ•°æ®åº“ %1 å—? - - Re&fresh selected database schema - 刷新已选数æ®åº“的结构(&F) + + Are you sure you want to remove following databases from the list: +%1 + 确定è¦ä»Žåˆ—表中移除下列数æ®åº“å—: +%1 - - - Erase table data - æ“¦é™¤è¯¥è¡¨çš„æ•°æ® + + Remove database + 移除数æ®åº“ - - Open file's directory - 打开文件目录 + + + Cannot import, because no import plugin is loaded. + 无法导入,没有加载导入æ’件。 - - Execute SQL from file - 从文件执行 SQL + + + Cannot export, because no export plugin is loaded. + 无法导出,没有加载导出æ’件。 - - - Database - æ•°æ®åº“ + + Vacuum (%1) + 释放空闲å ç”¨ï¼ˆVACUUM)(%1) - - Grouping - 分组 + + Integrity check (%1) + 完整性检查(%1) - - Generate query for table - 生æˆå¯¹è¡¨çš„æŸ¥è¯¢ + + Reset autoincrement + é‡ç½®è‡ªåŠ¨é€’å¢ž - - - Create group - 创建分组 + + Are you sure you want to reset autoincrement value for table '%1'? + 您确定è¦é‡ç½®è¡¨â€œ%1â€çš„自动递增值å—? - - Group name - 分组å + + An error occurred while trying to reset autoincrement value for table '%1': %2 + é‡ç½®è¡¨â€œ%1â€çš„自动递增值时出错:%2 - - Entry with name %1 already exists in group %2. - + + Autoincrement value for table '%1' has been reset successfully. + æˆåŠŸé‡ç½®è¡¨â€œ%1â€çš„自动递增值。 - - Delete group - 删除分组 + + Are you sure you want to delete all data from table(s): %1? + 您确定è¦åˆ é™¤è¡¨ %1 的所有数æ®å—? - - Are you sure you want to delete group %1? -All objects from this group will be moved to parent group. - 确认删除组 %1 å—? -删除åŽè¯¥ç»„下的全部内容将被移动到其所属的父分组中。 + + An error occurred while trying to delete data from table '%1': %2 + 删除表“%1â€ä¸­çš„æ•°æ®æ—¶å‡ºé”™ï¼š%2 - - Are you sure you want to remove database '%1' from the list? - 您确认è¦ç§»é™¤æ•°æ®åº“ %1 å—? + + All data has been deleted for table '%1'. + 表“%1â€ä¸­çš„全部数æ®å·²è¢«åˆ é™¤ã€‚ - - Are you sure you want to remove following databases from the list: -%1 - 您确认è¦ç§»é™¤ä»¥ä¸‹å­˜åœ¨äºŽåˆ—表中的数æ®åº“å—: -%1 + + Following objects will be deleted: %1. + 下列对象将被删除:%1。 - - Remove database - 移除数æ®åº“ + + Following databases will be removed from list: %1. + 下列数æ®åº“将从列表中移除:%1。 - - Vacuum (%1) - 清ç†ï¼ˆ%1) + + Remainig objects from deleted group will be moved in place where the group used to be. + 已删除分组中的剩余对象将被移开。 - - Autoincrement value for table '%1' has been reset successfully. - + + %1<br><br>Are you sure you want to continue? + %1<br><br>确定继续? - - Are you sure you want to delete all data from table(s): %1? - 您想è¦åˆ é™¤è¡¨ï¼š%1的所有数æ®å—? + + Delete objects + 删除对象 + + + DbTreeItemDelegate - - Could not execute SQL, because application has failed to start transaction: %1 - åº”ç”¨ç¨‹åºæ— æ³•开始事务,因此无法执行 SQL:%1 + + error + dbtree labels + 错误 - - Could not open file '%1' for reading: %2 - æ— æ³•ä»¥å†™æ¨¡å¼æ‰“开文件 %1:%2 + + (system table) + database tree label + (系统表) - - Could not execute SQL, because application has failed to commit the transaction: %1 - åº”ç”¨ç¨‹åºæ— æ³•æäº¤äº‹åŠ¡ï¼Œå› æ­¤æ— æ³•æ‰§è¡Œ SQL:%1 + + (virtual) + virtual table label + (虚拟) - - Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - 在 %2 ç§’å†…å®Œæˆæ‰§è¡Œ %1 个查询。 %3 个由于错误而没有被执行。 + + (system index) + database tree label + (系统索引) + + + DbTreeModel - - Finished executing %1 queries in %2 seconds. - 在 %2 ç§’å†…å®Œæˆ %1 个查询。 + + Database: %1 + dbtree tooltip + æ•°æ®åº“:%1 - - Could not execute SQL due to error. - 由于错误,无法执行 SQL。 + + URI: + dbtree tooltip + URI: - Delete database - 删除数æ®åº“ + + Version: + dbtree tooltip + 版本: - Are you sure you want to delete database '%1'? - 您确定è¦åˆ é™¤æ•°æ®åº““%1â€å—? + + File size: + dbtree tooltip + 文件大å°ï¼š - - - Cannot import, because no import plugin is loaded. - 未能导入,因为没有导入æ’件被加载。 + + Encoding: + dbtree tooltip + ç¼–ç ï¼š - - - Cannot export, because no export plugin is loaded. - 未能导出,因为没有导出æ’件被加载。 + + Error: + dbtree tooltip + 错误: - Error while executing VACUUM on the database %1: %2 - 在数æ®åº“%1上è¿è¡Œ VACUUM 命令时出错:%2 + + Table : %1 + dbtree tooltip + 表:%1 - VACUUM execution finished successfully. - VACUUM 命令执行完æˆã€‚ + + Columns (%1): + dbtree tooltip + 列(%1) - - Integrity check (%1) - 完整性检查(%1) + + Indexes (%1): + dbtree tooltip + 索引(%1) - - Reset autoincrement - é‡ç½®autoincrement + + Triggers (%1): + dbtree tooltip + 触å‘器(%1) - - Are you sure you want to reset autoincrement value for table '%1'? - 您确定è¦é‡è®¾â€œ%1â€çš„autoincrementå—? + + Copy + å¤åˆ¶ - - An error occurred while trying to reset autoincrement value for table '%1': %2 - 在é‡è®¾è¡¨â€œ%1â€çš„autoincrement时出现错误:%2 + + Move + 移动 - Autoincrement value for table '%1' has been reset successfly. - 表“%1â€çš„auincrementé‡è®¾æˆåŠŸã€‚ + + Include data + åŒ…å«æ•°æ® - Are you sure you want to delete all data from table '%1'? - 您确定è¦åˆ é™¤è¡¨â€œ%1â€ä¸­çš„全部数æ®å—? + + Include indexes + 包å«ç´¢å¼• - - An error occurred while trying to delete data from table '%1': %2 - 删除表“%1â€ä¸­çš„æ•°æ®æ—¶å‡ºé”™ï¼š%2 + + Include triggers + 包å«è§¦å‘器 - - All data has been deleted for table '%1'. - 表“%1â€ä¸­çš„æ•°æ®å…¨éƒ¨è¢«åˆ é™¤ã€‚ + + Abort + 中止 - - Following objects will be deleted: %1. - 以下内容将被删除:%1。 + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + 无法自动添加拖放的数æ®åº“文件 '%1'ã€‚éœ€è¦æ‰‹åŠ¨è®¾ç½®ã€‚ - - Following databases will be removed from list: %1. - 以下数æ®åº“将从列表中移除:%1。 + + Referenced tables + 引用的表 - - Remainig objects from deleted group will be moved in place where the group used to be. - + + Do you want to include following referenced tables as well: +%1 + 是å¦è¦åŒ…å«ä»¥ä¸‹å¼•用的表: +%1 - - %1<br><br>Are you sure you want to continue? - %1<br><br>ç»§ç»­? + + Name conflict + åç§°å†²çª - - Delete objects - 删除对象 + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + 下列对象已存在于目标数æ®åº“。 +请输入一个新的ä¸é‡å¤çš„å称,或按下 '%1' 中止æ“作: - - - DbTreeItemDelegate - - error - dbtree labels - 错误 + + SQL statements conversion + SQL 语å¥è½¬æ¢ - - (system table) - database tree label - (系统表) + + Following error occurred while converting SQL statements to the target SQLite version: + å°† SQL 语å¥è½¬æ¢ä¸ºç›®æ ‡ SQLite 版本时å‘生以下错误: - - (virtual) - virtual table label - (虚拟) + + Would you like to ignore those errors and proceed? + 是å¦å¿½ç•¥é”™è¯¯å¹¶ç»§ç»­ï¼Ÿ + + + DdlHistoryWindow - - (system index) - database tree label - (系统索引) + + Filter by database: + 按数æ®åº“过滤: - - - DbTreeModel - - Database: %1 - dbtree tooltip - æ•°æ®åº“:%1 + + Clear entire history + 清除全部历å²è®°å½• - - Version: - dbtree tooltip - 版本: + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- 在数æ®åº“ %1 (%2) 执行的查询 +-- 执行日期和时间:%3 +%4 - - File size: - dbtree tooltip - 文件大å°ï¼š + + Clear history + 清除历å²è®°å½• - - Encoding: - dbtree tooltip - ç¼–ç ï¼š + + Are you sure you want to erase entire DDL history? + ä½ ç¡®å®šè¦æ¸…除全部 DDL 历å²è®°å½•å—? - - Error: - dbtree tooltip - 错误: + + DDL history + DDL åŽ†å² + + + DdlPreviewDialog - - Table : %1 - dbtree tooltip - 表:%1 + + Queries to be executed + å°†è¦æ‰§è¡Œçš„è¯­å¥ - - Columns (%1): - dbtree tooltip - 字段(%1) + + Don't show again + ä¸å†æ˜¾ç¤º + + + DebugConsole - - Indexes (%1): - dbtree tooltip - 索引(%1) + + SQLiteStudio Debug Console + SQLiteStudio 调试终端 + + + EditorWindow - - Triggers (%1): - dbtree tooltip - 触å‘器(%1) + + SQL editor + SQL 编辑器 - - Copy - å¤åˆ¶ + + Query + 查询 - - Move - 移动 + + History + åŽ†å² - - Include data - åŒ…å«æ•°æ® + + Results in the separate tab + 在新选项å¡ä¸­æ˜¾ç¤ºç»“æžœ - - Include indexes - 包å«ç´¢å¼• + + Results below the query + 在查询下方显示结果 - - Include triggers - 包å«è§¦å‘器 + + + SQL editor %1 + SQL 编辑器 %1 - - Abort - 中止 + + + Results + 结果 - - Could not add dropped database file '%1' automatically. Manual setup is necessary. - + + Execute query + æ‰§è¡Œè¯­å¥ - - Referenced tables - å‚照表 + + Explain query + 解释查询 - - Do you want to include following referenced tables as well: -%1 - + + Clear execution history + sql editor + æ¸…é™¤æ‰§è¡ŒåŽ†å² - - Name conflict - åå­—å†²çª + + Export results + sql editor + 导出结果 - - Following object already exists in the target database. -Please enter new, unique name, or press '%1' to abort the operation: - 以下的对象已ç»å­˜åœ¨äºŽç›®æ ‡æ•°æ®åº“中。 -请输入一个新的,唯一的å称,或按下 '%1' 终止æ“作: + + Create view from query + sql editor + 从查询创建视图 - - SQL statements conversion - + + Previous database + å‰ä¸€ä¸ªæ•°æ®åº“ - - Following error occurred while converting SQL statements to the target SQLite version: - + + Next database + åŽä¸€ä¸ªæ•°æ®åº“ - - Would you like to ignore those errors and proceed? - 忽略错误并继续? + + Show next tab + sql editor + 显示下一个标签 - - - DdlHistoryWindow - - Filter by database: - æ•°æ®åº“过滤: + + Show previous tab + sql editor + 显示上一个标签 - - -- Queries executed on database %1 (%2) --- Date and time of execution: %3 -%4 - - - - - DDL history - DDL åŽ†å² + + Focus results below + sql editor + 切æ¢ç„¦ç‚¹åˆ°ä¸‹æ–¹çš„结果 - - - DdlPreviewDialog - - Queries to be executed - å°†è¦æ‰§è¡Œçš„è¯­å¥ + + Focus SQL editor above + sql editor + 切æ¢ç„¦ç‚¹åˆ°ä¸Šæ–¹çš„ SQL 编辑器 - - Don't show again - ä¸å†æ˜¾ç¤º + + Delete selected SQL history entries + sql editor + 删除选中的 SQL 历å²è®°å½•项 - - - DebugConsole - - SQLiteStudio Debug Console - SQLiteStudio 调试终端 + + Execute single query under cursor + 执行光标ä½ç½®çš„å•个查询 - - - EditorWindow - - Query - 查询 + + Execute all queries in editor + 在编辑器中执行所有查询 - - History - åŽ†å² + + Active database (%1/%2) + 活动数æ®åº“ (%1/%2) - - Results in the separate tab - 结果在新标签中打开 + + Query finished in %1 second(s). Rows affected: %2 + 查询用时 %1 秒。影å“行数:%2 - - Results below the query - 结果在当å‰é¡µæ‰“å¼€ + + Query finished in %1 second(s). + 查询用时 %1 秒。 - - - SQL editor %1 - SQL 编辑器 %1 + + Clear execution history + æ¸…é™¤æ‰§è¡ŒåŽ†å² - - Results - 结果 + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + 确定è¦åˆ é™¤å…¨éƒ¨çš„ SQL 执行历å²å—?删除åŽä¸èƒ½æ¢å¤ã€‚ - - Execute query - æ‰§è¡Œè¯­å¥ + + Cannot export, because no export plugin is loaded. + 无法导出,没有加载导出æ’件。 - - Explain query - + + No database selected in the SQL editor. Cannot create a view for unknown database. + 没有在 SQL 编辑器中选中数æ®åº“。无法为未知的数æ®åº“创建视图。 - - Clear execution history - sql editor - æ¸…é™¤æ‰§è¡ŒåŽ†å² + + Editor window "%1" has uncommitted data. + 编辑器窗å£â€œ%1â€å†…有未æäº¤çš„æ•°æ®ã€‚ + + + ErrorsConfirmDialog - - Export results - sql editor - 导出结果 + + Errors + 错误 - - Create view from query - sql editor - 从query中创建视图 + + Following errors occured: + å‘生了以下错误: - - Previous database - å‰ä¸€ä¸ªæ•°æ®åº“ + + Would you like to proceed? + ä»è¦ç»§ç»­å—? + + + ExecFromFileDialog - - Next database - 下一个数æ®åº“ + + Execute SQL from file + 从文件执行 SQL - - Show next tab - sql editor - 显示下一个标签 + + Input file + 输入文件 - - Show previous tab - sql editor - 显示上一个标签 + + Path to file + 文件路径 - - Focus results below - sql editor - + + Browse for file + æµè§ˆæ–‡ä»¶ - - Focus SQL editor above - sql editor - + + Options + 选项 - - Delete selected SQL history entries - sql editor - + + File encoding + æ–‡ä»¶ç¼–ç  - - Active database (%1/%2) - + + Skip failing SQL statements + 跳过失败的 SQL è¯­å¥ - - Query finished in %1 second(s). Rows affected: %2 - 查询在 %1 秒内完æˆã€‚å½±å“的行数:%2 + + SQL scripts (*.sql);;All files (*) + SQL 脚本 (*.sql);;所有文件 (*) - - Query finished in %1 second(s). - 查询在 %1 秒内完æˆã€‚ + + Execute SQL file + 执行 SQL 文件 - - Clear execution history - æ¸…é™¤æ‰§è¡ŒåŽ†å² + + Please provide file to be executed. + 请æä¾›ä¸€ä¸ªæ–‡ä»¶ä»¥ä¾›æ‰§è¡Œã€‚ - - Are you sure you want to erase the entire SQL execution history? This cannot be undone. - 确定è¦åˆ é™¤å…¨éƒ¨çš„ SQL 执行历å²å—?删除åŽä¸èƒ½æ¢å¤ã€‚ + + Provided file does not exist or cannot be read. + æä¾›çš„æ–‡ä»¶ä¸å­˜åœ¨æˆ–无法读å–。 + + + ExportDialog - - Cannot export, because no export plugin is loaded. - 未能导出,因为没有导出æ’件被加载。 + + Export + 导出 - - No database selected in the SQL editor. Cannot create a view for unknown database. - 没有在 SQL 编辑器中选中的数æ®åº“。无法为未知数æ®åº“创建视图。 + + What do you want to export? + 您想导出什么? - - Editor window "%1" has uncommitted data. - 编辑器“%1â€é‡Œæœ‰æœªæäº¤çš„æ•°æ®åº“。 + + A database + æ•°æ®åº“ - Editor window "%1" has uncommited data. - 编辑器“%1â€é‡Œæœ‰æœªæäº¤çš„æ•°æ®åº“。 + + A single table + 一张表 - - - ErrorsConfirmDialog - - Errors - 错误 + + Query results + 查询结果 - - Following errors occured: - å‘生了以下错误: + + Table to export + è¦å¯¼å‡ºçš„表 - - Would you like to proceed? - ä»ç„¶ç»§ç»­å—? + + Database + æ•°æ®åº“ - - - ExecFromFileDialog - - Execute SQL from file - 从文件执行 SQL + + Table + 表 - - Input file - 输入文件 + + Options + 选项 - - Path to file - 文件路径 + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + ä¸é€‰ä¸­æ­¤é€‰é¡¹æ—¶ï¼Œä»…导出表的 DDL(CREATE TABLE 语å¥ï¼‰ã€‚ - - Browse for file - æµè§ˆæ–‡ä»¶ + + Export table data + å¯¼å‡ºè¡¨çš„æ•°æ® - - Options - 选项 + + Export table indexes + 导出表的索引 - - File encoding - æ–‡ä»¶ç¼–ç  + + Export table triggers + 导出表的触å‘器 - - Skip failing SQL statements - 跳过失败的 SQL statements + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + 注æ„,æŸäº›è¾“出格å¼å¯èƒ½ä¸æ”¯æŒå¯¼å‡ºè¡¨ç´¢å¼•和触å‘器。 - - SQL scripts (*.sql);;All files (*) - SQL 脚本 (*.sql);;所有文件 (*) + + Select database objects to export + 选择è¦å¯¼å‡ºçš„æ•°æ®åº“对象 - - Execute SQL file - 执行 SQL 文件 + + Export data from tables + ä»Žè¡¨ä¸­å¯¼å‡ºæ•°æ® - - Please provide file to be executed. - 请æä¾›ä¸€ä¸ªæ–‡ä»¶ä»¥ä¾›æ‰§è¡Œã€‚ + + Select all + 全选 - - Provided file does not exist or cannot be read. - æä¾›çš„æ–‡ä»¶ä¸å­˜åœ¨æˆ–无法读å–。 + + Deselect all + å…¨ä¸é€‰ - - - ExportDialog - - Export - 导出 + + + Database: + æ•°æ®åº“: - - What do you want to export? - 您想导出什么? + + Query to export results for + 导出结果所需的查询 - - A database - æ•°æ®åº“ + + Query to be executed for results: + 对导出结果执行的查询: - - A single table - 一张表 + + Export format and options + 导出格å¼å’Œé€‰é¡¹ - - Query results - 查询结果 + + Export format + å¯¼å‡ºæ ¼å¼ - - Table to export - è¦å¯¼å‡ºçš„表 + + Output + 输出 - - Database - æ•°æ®åº“ + + Exported file path + 导出文件路径 - - Table - 表 + + Clipboard + å‰ªè´´æ¿ - - Options - 选项 + + File + 文件 - - When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. - + + Exported text encoding: + 导出文本编ç ï¼š - - Export table data - å¯¼å‡ºè¡¨é‡Œçš„æ•°æ® + + Export format options + 导出格å¼é€‰é¡¹ - - Export table indexes - 导出表的索引 + + Cancel + å–æ¶ˆ - - Export table triggers - 导出表的触å‘器 + + + + Select database to export. + 选择è¦å¯¼å‡ºçš„æ•°æ®åº“。 - - Note, that exporting table indexes and triggers may be unsupported by some output formats. - 注æ„,æŸäº›è¾“出格å¼å¯èƒ½ä¸æ”¯æŒå¯¼å‡ºè¡¨ç´¢å¼•与触å‘器。 + + Select table to export. + 选择è¦å¯¼å‡ºçš„表。 - - Select database objects to export - 选择数æ®åº“对象进行导出 + + Enter valid query to export. + 输入è¦å¯¼å‡ºçš„æœ‰æ•ˆæŸ¥è¯¢ã€‚ - - Export data from tables - ä»Žè¡¨ä¸­å¯¼å‡ºæ•°æ® + + Select at least one object to export. + 至少选择一个对象进行导出。 - - Select all - 全选 + + You must provide a file name to export to. + 您必须选择一个导出文件。 - - Deselect all - å…¨ä¸é€‰ + + Path you provided is an existing directory. You cannot overwrite it. + 您æä¾›çš„路径是一个现存的目录,ä¸èƒ½è¦†ç›–。 - - - Database: - æ•°æ®åº“: + + The directory '%1' does not exist. + 目录 '%1' ä¸å­˜åœ¨ã€‚ - - Query to export results for - + + The file '%1' exists and will be overwritten. + 文件“%1â€å­˜åœ¨ä¸”将被覆盖。 - - Query to be executed for results: - + + All files (*) + 所有文件 (*) - - Export format and options - 导出格å¼å’Œé€‰é¡¹ + + Pick file to export to + 选择文件导出到 - - Export format - å¯¼å‡ºæ ¼å¼ + + Internal error during export. This is a bug. Please report it. + 导出时å‘生内部错误。这是一个 Bug,请å馈。 + + + FileExecErrorsDialog - - Output - 输出 + + Execution errors + 执行错误 - - Exported file path - 导出文件路径 + + Following errors were encountered during execution of SQL statements from the file: + 从文件执行 SQL è¯­å¥æ—¶é‡åˆ°ä»¥ä¸‹é”™è¯¯ï¼š - - Clipboard - å‰ªè´´æ¿ + + SQL + SQL - - File - 文件 + + Error + 错误 - - Exported text encoding: - 导出编ç ï¼š + + Statements that were executed successfully were commited. + æˆåŠŸæ‰§è¡Œçš„è¯­å¥å·²æäº¤ã€‚ - - Export format options - 导出格å¼é€‰é¡¹ + + Statements that were executed successfully were rolled back. + æˆåŠŸæ‰§è¡Œçš„è¯­å¥å·²å›žæ»šã€‚ + + + FkComboBox - - Cancel - å–æ¶ˆ + + Cannot edit this cell. Details: %1 + 无法编辑此å•元格。详情:%1 + + + FontEdit - - - - Select database to export. - 选择è¦å¯¼å‡ºçš„æ•°æ®åº“。 + + Choose font + font configuration + 选择字体 + + + Form - - Select table to export. - 选择è¦å¯¼å‡ºçš„表。 + + Active SQL formatter plugin + 激活 SQL è¯­å¥æ ¼å¼åŒ–æ’ä»¶ + + + FormView - - Enter valid query to export. - + + Commit row + form view + æäº¤è¡Œ - - Select at least one object to export. - 至少选择一个对象进行导出。 + + Rollback row + form view + 回滚行 - - You must provide a file name to export to. - 您必须选择一个导出文件。 + + First row + form view + 首行 - - Path you provided is an existing directory. You cannot overwrite it. - 您æä¾›çš„路径是一个存在的目录,您ä¸èƒ½è¦†å†™å®ƒã€‚ + + Previous row + form view + 上一行 - - The directory '%1' does not exist. - 目录 '%1' ä¸å­˜åœ¨ã€‚ + + Next row + form view + 下一行 - - The file '%1' exists and will be overwritten. - 文件“%1â€å­˜åœ¨ä¸”将被覆写。 + + Last row + form view + 末行 - - All files (*) - 所有文件 (*) + + Insert new row + form view + æ’入新行 - - Pick file to export to - 选择一个导出文件 + + Delete current row + form view + 删除当å‰è¡Œ + + + FunctionsEditor - - Internal error during export. This is a bug. Please report it. - 导出时å‘生了内部错误,这是一个 Bug,请å馈它。 + + Filter functions + 筛选函数 - - - FileExecErrorsDialog - - Execution errors - 执行错误 + + Input arguments + è¾“å…¥å‚æ•° - - Following errors were encountered during execution of SQL statements from the file: - 从文件执行 SQL statements 期间é‡åˆ°ä»¥ä¸‹é”™è¯¯ï¼š + + Undefined + 未定义 - - SQL - SQL + + Databases + æ•°æ®åº“ - - Error - 错误 + + Register in all databases + 在所有数æ®åº“中注册 - - Statements that were executed successfully were commited. - æˆåŠŸæ‰§è¡Œçš„ Statements 已被æäº¤ã€‚ + + Register in following databases: + 在下列数æ®åº“中注册: - - Statements that were executed successfully were rolled back. - æˆåŠŸæ‰§è¡Œçš„ Statements 已被回滚。 + + Type: + 类型: - - - FontEdit - - Choose font - font configuration - 字体选择 + + Function name: + 函数å: - - - Form - - Active SQL formatter plugin - 激活 SQL è¯­å¥æ ¼å¼åŒ–æ’ä»¶ + + Implementation language: + 实现语言: - - - FormView - - Commit row - form view - æäº¤ + + Deterministic + Deterministic - - Rollback row - form view - 回滚 + + Initialization code: + åˆå§‹åŒ–代ç ï¼š - - First row - form view - 首行 + + + Function implementation code: + 函数实现代ç ï¼š - - Previous row - form view - å‰ä¸€è¡Œ + + Final step implementation code: + 最终一步实现代ç ï¼š - - Next row - form view - 下一行 + + SQL functions editor + SQL 函数编辑器 - - Last row - form view - 末行 + + Commit all function changes + æäº¤æ‰€æœ‰å¯¹å‡½æ•°çš„æ›´æ”¹ - - Insert new row - form view - æ’入新行 + + Rollback all function changes + 回滚所有对函数的更改 - - Delete current row - form view - 删除当å‰è¡Œ + + Create new function + 新建函数 - - - FunctionsEditor - - Filter funtions - 过滤函数 + + Delete selected function + 删除已选函数 - - Function name: - 函数å: + + Custom SQL functions manual + 自定义 SQL 函数手册 - - Implementation language: - 实现语言: + + Add function argument + æ·»åŠ å‡½æ•°å‚æ•° - - Type: - 类型: + + Rename function argument + é‡å‘½åå‡½æ•°å‚æ•° - - Input arguments - è¾“å…¥å‚æ•° + + Delete function argument + åˆ é™¤å‡½æ•°å‚æ•° - - Undefined - Undefined + + Move function argument up + ä¸Šç§»å‡½æ•°å‚æ•° - - Databases - æ•°æ®åº“ + + Move function argument down + ä¸‹ç§»å‡½æ•°å‚æ•° - - Register in all databases - 在所有数æ®åº“中注册 + + Scalar + æ ‡é‡å‡½æ•° - - Register in following databases: - 在下列数æ®åº“中注册: + + Aggregate + èšåˆå‡½æ•° - - Initialization code: - åˆå§‹åŒ–代ç ï¼š + + Enter a non-empty, unique name of the function. + 请输入éžç©ºä¸”唯一的函数å称。 - - - Function implementation code: - 函数实现代ç ï¼š + + Pick the implementation language. + 选择实现语言。 - - Final step implementation code: - 最终一步实现代ç ï¼š + + Per step code: + 步进代ç ï¼š - - SQL function editor - SQL函数编辑器 + + Enter a non-empty implementation code. + 请输入éžç©ºçš„实现代ç ã€‚ - - Commit all function changes - æäº¤æ‰€æœ‰å¯¹å‡½æ•°çš„æ›´æ”¹ + + argument + new function argument name in function editor window + 傿•° - - Rollback all function changes - 回滚所有对函数的更改 + + Functions editor window has uncommitted modifications. + å‡½æ•°ç¼–è¾‘å™¨çª—å£æœ‰æœªæäº¤çš„æ›´æ”¹ã€‚ + + + ImportDialog - - Create new function - 新建函数 + + Import data + å¯¼å…¥æ•°æ® - - Delete selected function - 删除已选函数 + + Table to import to + 导入目标 - - Custom SQL functions manual - 自定义SQL函数手册 + + Table + 表 - - Add function argument - æ·»åŠ å‡½æ•°å‚æ•° + + Database + æ•°æ®åº“ - - Rename function argument - é‡å‘½åå‡½æ•°å‚æ•° + + Data source to import from + å¯¼å…¥æ•°æ®æº - - Delete function argument - åˆ é™¤å‡½æ•°å‚æ•° + + Data source type + æ•°æ®æºç±»åž‹ - - Move function argument up - ä¸Šç§»å‡½æ•°å‚æ•° + + Options + 选项 - - Move function argument down - ä¸‹ç§»å‡½æ•°å‚æ•° + + Text encoding: + 文本编ç ï¼š - - Scalar - + + Input file: + 输入文件: - - Aggregate - + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>如果å¯ç”¨ï¼Œåˆ™å¯¼å…¥æœŸé—´é‡åˆ°çš„任何约æŸè¿èƒŒã€æ— æ•ˆæ•°æ®æ ¼å¼ï¼ˆé”™è¯¯åˆ—数)或其他问题都将被忽略,导入ä¸ä¸­æ­¢ã€‚</p> - - Enter a non-empty, unique name of the function. - 输入éžç©ºå”¯ä¸€çš„函数åç§° + + Ignore errors + 忽略错误 - - Pick the implementation language. - 选择实现语言。 + + Data source options + æ•°æ®æºé€‰é¡¹ - - Per step code: - æ¯ä¸€æ­¥çš„代ç ï¼š + + Cancel + å–æ¶ˆ - - Enter a non-empty implementation code. - 输入éžç©ºå®žçް代ç ã€‚ + + If you type table name that doesn't exist, it will be created. + 如果您输入的表ä¸å­˜åœ¨ï¼Œåˆ™å°†åˆ›å»ºè¯¥è¡¨ã€‚ - - argument - new function argument name in function editor window - + + Enter the table name + 输入表å - - Functions editor window has uncommitted modifications. - å‡½æ•°ç¼–è¾‘å™¨çª—å£æœ‰æœªæäº¤çš„æ›´æ”¹ã€‚ + + Select import plugin. + 选择导入æ’件。 - - - ImportDialog - - Import data - å¯¼å…¥æ•°æ® + + You must provide a file to import from. + 您必须æä¾›ä¸€ä¸ªå¯¼å…¥æºæ–‡ä»¶ã€‚ - - Table to import to - 目的表 + + The file '%1' does not exist. + 文件“%1â€ä¸å­˜åœ¨ã€‚ - - Table - 表 + + Path you provided is a directory. A regular file is required. + 您æä¾›çš„æ˜¯ä¸€ä¸ªç›®å½•,而需è¦çš„æ˜¯ä¸€ä¸ªä¸€èˆ¬æ–‡ä»¶ã€‚ - - Database - æ•°æ®åº“ + + Pick file to import from + 选择è¦å¯¼å…¥çš„æ–‡ä»¶ + + + IndexDialog - - Data source to import from - æ•°æ®æº + + + Index + 索引 - - Data source type - æ•°æ®æºç±»åž‹ + + Column + 列 - - Options - 选项 + + Sort + æŽ’åº - - Input file: - 输入文件: + + Collation + å­—ç¬¦åº - - Text encoding: - 文本编ç ï¼š + + On table: + 表: - - <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> - + + Delete selected indexed expression + åˆ é™¤é€‰å®šçš„ç´¢å¼•è¡¨è¾¾å¼ - - Ignore errors - 忽略错误 + + Moves selected index column up in the order, making it more significant in the index. + å‘上移动选中的索引,使它在索引中å˜å¾—æ›´é‡è¦ã€‚ - - Data source options - æ•°æ®æºé€‰é¡¹ + + Moves selected index column down in the order, making it less significant in the index. + å‘下移动选中的索引,使它在索引中å˜å¾—ä¸é‡è¦ã€‚ - - Cancel - å–æ¶ˆ + + Partial index condition + 部分索引æ¡ä»¶ - - If you type table name that doesn't exist, it will be created. - 如果输入的表ä¸å­˜åœ¨ï¼Œåˆ™æ–°å»ºè¯¥è¡¨ã€‚ + + Unique index + 唯一索引 - - Enter the table name - 输入表å + + Index name: + 索引å: - - Select import plugin. - 选择导入æ’件。 + + Edit selected indexed expression + ç¼–è¾‘é€‰å®šçš„ç´¢å¼•è¡¨è¾¾å¼ - - You must provide a file to import from. - å¿…é¡»æä¾›ä¸€ä¸ªå¯¼å…¥æ–‡ä»¶ã€‚ + + Add indexed expression + æ·»åŠ ç´¢å¼•è¡¨è¾¾å¼ - - The file '%1' does not exist. - 文件“%1â€ä¸å­˜åœ¨ã€‚ + + DDL + DDL - - Path you provided is a directory. A regular file is required. - ä½ æä¾›çš„æ˜¯ä¸€ä¸ªç›®å½•。我们需è¦çš„æ˜¯æ–‡ä»¶ã€‚ + + Tried to open index dialog for closed or inexisting database. + 试图打开已关闭或ä¸å­˜åœ¨çš„æ•°æ®åº“çš„ç´¢å¼•å¯¹è¯æ¡†ã€‚ - - Pick file to import from - 选择è¦å¯¼å…¥çš„æ–‡ä»¶ + + Could not process index %1 correctly. Unable to open an index dialog. + 无法正确处ç†ç´¢å¼• %1ã€‚æ— æ³•æ‰“å¼€ç´¢å¼•å¯¹è¯æ¡†ã€‚ - - - IndexDialog - - - Index - 索引 + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + 唯一索引ä¸èƒ½æœ‰ç´¢å¼•表达å¼ã€‚从下é¢çš„列表中删除表达å¼ï¼Œæˆ–å–æ¶ˆé€‰ä¸­æ­¤é€‰é¡¹ã€‚ - - On table: - 在表: + + Pick the table for the index. + 为索引选择一个表。 - - Index name: - 索引å: + + Select at least one column. + 选择至少一个列。 - - Partial index condition - + + Enter a valid condition. + 输入一个有效的æ¡ä»¶ã€‚ - - Unique index - 唯一索引 + + default + index dialog + 默认 - - Column - 字段 + + Sort order + table constraints + æŽ’åº - - Collation - 排åºè§„则 + + + Error + index dialog + 错误 - - Sort - æŽ’åº + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + æ— æ³•åˆ›å»ºå”¯ä¸€ç´¢å¼•ï¼Œå› ä¸ºé€‰ä¸­çš„åˆ—ä¸­çš„å€¼ä¸æ˜¯å”¯ä¸€çš„ã€‚æ‚¨æƒ³è¦æ‰§è¡Œ SELECT 查询以查看有问题的值å—? - - Delete selected indexed expression - + + An error occurred while executing SQL statements: +%1 + 在执行 SQL è¯­å¥æ—¶å‘生了错误:%1 + + + IndexExprColumnDialog - - Moves selected index column up in the order, making it more significant in the index. - å‘上移动选中的索引,使它在索引中å˜å¾—æ›´é‡è¦ã€‚ + + Indexed expression + ç´¢å¼•è¡¨è¾¾å¼ - - Moves selected index column down in the order, making it less significant in the index. - å‘下移动选中的索引,使它在索引中å˜å¾—ä¸é‡è¦ã€‚ + + Expression to index + ç´¢å¼•è¡¨è¾¾å¼ - - Edit selected indexed expression - + + This expression is already indexed by the index. + 此表达å¼å·²è¢«è¯¥ç´¢å¼•所索引。 - - Add indexed expression - + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + 列应该直接索引而éžé€šè¿‡è¡¨è¾¾å¼ã€‚æ‰©å±•æ­¤è¡¨è¾¾å¼æ¥åŒ…å«åˆ—åç§°ä»¥å¤–çš„å†…å®¹ï¼Œæˆ–è€…ä¸­æ­¢å¹¶åœ¨ç´¢å¼•å¯¹è¯æ¡†ä¸­ç›´æŽ¥é€‰æ‹©æ­¤åˆ—。 - - DDL - DDL + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + 列 '%1' ä¸å±žäºŽæ­¤ç´¢å¼•所在的表。索引表达å¼åªèƒ½å¼•用索引表中的列。 - - Tried to open index dialog for closed or inexisting database. - + + It's forbidden to use 'SELECT' statements in indexed expressions. + 索引表达å¼ä¸­ç¦æ­¢ä½¿ç”¨ 'SELECT' 语å¥ã€‚ - - Could not process index %1 correctly. Unable to open an index dialog. - + + Enter an indexed expression. + 请输入一个索引表达å¼ã€‚ - - Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. - + + Invalid expression. + 无效的表达å¼ã€‚ + + + LanguageDialog - - Pick the table for the index. - 为索引选择一个表。 + + Language + 语言 - - Select at least one column. - 至少选择一列 + + Please choose language: + 请选择一ç§è¯­è¨€ï¼š + + + MainWindow - - Enter a valid condition. - è¾“å…¥ä¸€ä¸ªåˆæ³•çš„æ¡ä»¶ã€‚ + + Database toolbar + æ•°æ®åº“å·¥å…·æ  - - default - index dialog - 默认 + + Structure toolbar + ç»“æž„å·¥å…·æ  - - Sort order - table constraints - æŽ’åº + + Tools + 工具 - - - Error - index dialog - 错误 + + Window list + 窗å£åˆ—表 - - Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? - + + View toolbar + è§†å›¾å·¥å…·æ  - - An error occurred while executing SQL statements: -%1 - 在执行 SQL è¯­å¥æ—¶å‘生了错误:%1 + + Configuration widgets + é…置部件 - - - IndexExprColumnDialog - - Indexed expression - + + Syntax highlighting engines + 语法高亮引擎 - - Expression to index - + + Data editors + æ•°æ®ç¼–辑器 - - This expression is already indexed by the index. - + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + 正在以调试模å¼è¿è¡Œã€‚按下 %1 或使用 帮助 - æ‰“å¼€è°ƒè¯•æŽ§åˆ¶å° èœå•æ¥æ‰“开调试控制å°ã€‚ - - Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. - + + Running in debug mode. Debug messages are printed to the standard output. + 正在以调试模å¼è¿è¡Œã€‚调试信æ¯å°†ä¼šè¢«è¾“出在标准输出中。 - - Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. - + + You need to restart application to make the language change take effect. + è¯­è¨€å˜æ›´åœ¨ç¨‹åºé‡å¯åŽç”Ÿæ•ˆã€‚ - - It's forbidden to use 'SELECT' statements in indexed expressions. - + + Open SQL &editor + 打开 SQL 编辑器(&E) - - Enter an indexed expression. - + + Open DDL &history + 打开 DDL 历å²(&H) - - Invalid expression. - 无效的表达å¼ã€‚ + + Open SQL &functions editor + 打开 SQL 函数编辑器(&F) - - - LanguageDialog - - Language - 语言 + + Open code &snippets editor + 打开代ç ç‰‡æ®µç¼–辑器 - - Please choose language: - 请选择一门语言: + + Open &collations editor + 打开字符åºç¼–辑器(&C) - - - MainWindow - - Database toolbar - æ•°æ®åº“å·¥å…·æ  + + Open ex&tension manager + 打开扩展管ç†å™¨(&T) - - Structure toolbar - ç»“æž„å·¥å…·æ  + + &Import + 导入(&I) - - Tools - 工具 + + E&xport + 导出(&X) - - Window list - 窗å£åˆ—表 + + Open confi&guration dialog + 打开é…ç½®å¯¹è¯æ¡†(&G) - - View toolbar - 查看这个è¯ï¼Œåœ¨åŽé¢çš„翻译中翻译起æ¥ï¼Œæœ‰äº›åœ°æ–¹çš„è¯­å¥æžå…¶ä¸é€šé¡ºï¼Œæ•…使用视图代替之。而且根æ®å…¶èœå•ç»“æž„ï¼Œä¸»è¦æ˜¯æŽ’布窗å£ï¼ŒæŽ§ä»¶ï¼Œæ¯”起查看,视图更佳 - è§†å›¾å·¥å…·æ  + + &Tile windows + 平铺窗å£(&T) - - Configuration widgets - é…置部件 + + Tile windows &horizontally + 水平排列窗å£(&H) - - Syntax highlighting engines - 语法高亮引擎 + + Tile windows &vertically + 垂直排列窗å£(&V) - - Data editors - æ•°æ®ç¼–辑器 + + &Cascade windows + 层å çª—å£(&C) - - Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - 正在以调试模å¼è¿è¡Œã€‚按下 %1 或使用 帮助/æ‰“å¼€è°ƒè¯•æŽ§åˆ¶å° èœå•æ¥æ‰“开调试控制å°ã€‚ + + Next window + ä¸‹ä¸€ä¸ªçª—å£ - - Running in debug mode. Debug messages are printed to the standard output. - 正在以调试模å¼è¿è¡Œã€‚调试信æ¯å°†ä¼šè¢«è¾“出在标准输出中。 + + Previous window + ä¸Šä¸€ä¸ªçª—å£ - - You need to restart application to make the language change take effect. - 更改语言åŽé‡å¯ç¨‹åºç”Ÿæ•ˆã€‚ + + Hide status field + éšè—çŠ¶æ€æ  - Open SQL editor - 打开SQL编辑器 + + Close &all windows + 关闭全部窗å£(&A) - Open DDL history - 打开数æ®åº“定义(DDLï¼‰åŽ†å² + + Re&store recently closed window + 还原最近关闭的窗å£(&S) - Open SQL functions editor - 打开SQL函数编辑器 + + Close current &window + 关闭当å‰çª—å£ - Import - 导入 + + Close &other windows + å…³é—­å…¶ä»–çª—å£ - Export - 导出 + + Close windows on the &left + å…³é—­å·¦ä¾§çª—å£ - Open configuration dialog - 打开é…ç½®å¯¹è¯æ¡† + + Close windows on the &right + 关闭å³ä¾§çª—å£ - Tile windows - å¹³é“ºçª—å£ + + Re&name selected window + é‡å‘½åé€‰ä¸­çª—å£ - Tile windows horizontally - æ°´å¹³æŽ’åˆ—çª—å£ + + Open Debug Console + 打开调试终端 - Tile windows vertically - åž‚ç›´æŽ’åˆ—çª—å£ + + Open CSS Console + 打开 CSS æŽ§åˆ¶å° - Cascade windows - 层å çª—å£ + + Report a &bug + ææŠ¥ Bug (&B) - - Next window - ä¸‹ä¸€ä¸ªçª—å£ + + D&onate + æèµ (&O) - - Previous window - ä¸Šä¸€ä¸ªçª—å£ + + Propose a new &feature + æè®®æ–°å¢žåŠŸèƒ½(&F) - - Hide status field - éšè—çŠ¶æ€æ  + + &About + 关于(&A) - Close selected window - 关闭当å‰çª—å£ + + &Licenses + 许å¯åè®®(&L) - Close all windows but selected - å…³é—­å…¶å®ƒçª—å£ + + Open home &page + 访问主页(&P) - Close all windows - å…³é—­å…¨éƒ¨çª—å£ + + User &Manual + 用户手册(&M) - Restore recently closed window - æ¢å¤æœ€è¿‘å…³é—­çš„çª—å£ + + SQLite &documentation + SQLite 文档(&D) - Rename selected window - é‡å‘½å当å‰çª—å£ + + Bugs and feature &requests + Bug 与功能请求(&R) - - Open Debug Console - 打开调试终端 + + Quit + 退出 - - Open CSS Console - 打开 CSS æŽ§åˆ¶å° + + Check for &updates + 检查更新(&U) - Report a bug - æäº¤Bug + + &Database + menubar + æ•°æ®åº“(&D) - Propose a new feature - æäº¤æ–°åŠŸèƒ½å»ºè®® + + &Structure + menubar + 结构(&S) - About - 关于 + + &View + menubar + 视图(&V) - Licenses - è®¸å¯ + + Window list + menubar view menu + 窗å£åˆ—表 - Open home page - 访问主页 + + &Tools + menubar + 工具(&T) - Open forum page - è®¿é—®è®ºå› + + &Help + 帮助(&H) - User Manual - 用户手册 + + Could not set style: %1 + main window + 未能设置风格:%1 - SQLite documentation - SQLite文档 + + Cannot export, because no export plugin is loaded. + 无法导出,没有加载导出æ’件。 - Report history - æŠ¥å‘ŠåŽ†å² + + Cannot import, because no import plugin is loaded. + 无法导入,没有加载导入æ’件。 - Check for updates - 检查更新 + + Rename window + é‡å‘½åçª—å£ - Database - menubar - æ•°æ®åº“ + + Enter new name for the window: + 请输入窗å£çš„æ–°å称: - Structure - menubar - 结构 + + New updates are available. <a href="%1">Click here for details</a>. + 有新更新 <a href="%1">点此查看更新详情</a>. - View - menubar - 查看 + + You're running the most recent version. No updates are available. + 您使用的是最新版,ä¸éœ€è¦æ›´æ–°ã€‚ - - Window list - menubar view menu - 窗å£åˆ—表 + + Database passed in command line parameters (%1) was already on the list under name: %2 + å‘½ä»¤è¡Œå‚æ•°ä¼ é€’的数æ®åº“(%1)已在列表中,å为:%2 - Tools - menubar - 工具 + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + å‘½ä»¤è¡Œå‚æ•°ä¼ é€’的数æ®åº“(%1)已临时添加到列表中,å为:%2 - Help - 帮助 + + Could not add database %1 to list. + æœªèƒ½å°†æ•°æ® %1 添加到列表 + + + MdiWindow - - Open SQL &editor - 打开 SQL 编辑器(&E) + + Uncommitted changes + 未æäº¤çš„æ›´æ”¹ - - Open DDL &history - 打开数æ®åº“定义(DDL)历å²(&H) + + Close anyway + ä»ç„¶å…³é—­ - - Open SQL &functions editor - 打开 SQL 函数编辑器(&F) + + Don't close + ä¸å…³é—­ + + + MultiEditor - - Open &collations editor - 打开排åºè§„则编辑器(&C) + + Null value + multieditor + Null 值 - - Open ex&tension manager - 打开扩展管ç†å™¨(&T) + + Configure editors for this data type + 为数æ®ç±»åž‹è®¾ç½®ç¼–辑器 - - &Import - 导入(&I) + + Open another tab + 打开å¦ä¸€ä¸ªé€‰é¡¹å¡ - - E&xport - 导出(&X) + + Foreign Key + 外键 - - Open confi&guration dialog - 打开é…ç½®å¯¹è¯æ¡†(&G) + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + æ•°æ®ç¼–辑器æ’ä»¶ '%1' 未加载,而它已被定义为编辑 '%1' æ•°æ®ç±»åž‹çš„编辑器。 {1'?} {2'?} {1' or 2'?} - - &Tile windows - 平铺窗å£(&T) + + Deleted + multieditor + 已删除 - - Tile windows &horizontally - 水平排列窗å£(&H) + + Read only + multieditor + åªè¯» + + + MultiEditorBoolPlugin - - Tile windows &vertically - 垂直排列窗å£(&V) + + Boolean + 布尔 + + + MultiEditorDatePlugin - - &Cascade windows - 层å çª—å£(&C) + + Date + 日期 + + + MultiEditorDateTimePlugin - - Close selected &window - 关闭当å‰çª—å£(&W) + + Date & time + 日期和时间 + + + MultiEditorHexPlugin - - Close all windows &but selected - 关闭其它窗å£(&B) + + Hex + å六进制 + + + MultiEditorNumericPlugin - - Close &all windows - 关闭全部窗å£(&A) + + Number + numeric multi editor tab name + 数值 + + + MultiEditorText - - Re&store recently closed window - æ¢å¤æœ€è¿‘关闭的窗å£(&S) + + Tab changes focus + Tab 键更改焦点 - - &Rename selected window - é‡å‘½å当å‰çª—å£(&R) + + Cut + 剪切 - - Report a &bug - æäº¤ Bug (&B) + + Copy + å¤åˆ¶ - - Propose a new &feature - æäº¤æ–°åŠŸèƒ½å»ºè®®(&F) + + Paste + 粘贴 - - &About - 关于(&A) + + Delete + 删除 - - &Licenses - 许å¯(&L) + + Undo + 撤销 - - Open home &page - 访问主页(&P) + + Redo + é‡åš + + + MultiEditorTextPlugin - - Open fo&rum page - 访问论å›(&R) + + Text + 文本 + + + MultiEditorTimePlugin - - User &Manual - 用户手册(&M) + + Time + æ—¶é—´ + + + NewConstraintDialog - - SQLite &documentation - SQLite 文档(&D) + + New constraint + æ–°çº¦æŸ - - Bugs and feature &requests - æäº¤ Bug 与请求新功能(&R) + + + Primary Key + new constraint dialog + 主键 - - Check for &updates - 检查更新(&U) + + + Foreign Key + new constraint dialog + 外键 - - &Database - menubar - æ•°æ®åº“(&D) + + + Unique + new constraint dialog + 唯一 - - &Structure - menubar - 结构(&S) + + + Check + new constraint dialog + æ¡ä»¶ - - &View - menubar - 视图(&V) + + Not NULL + new constraint dialog + éžç©º - - &Tools - menubar - 工具(&T) + + Collate + new constraint dialog + å­—ç¬¦åº - - &Help - 帮助(&H) + + Generated + new constraint dialog + ç”Ÿæˆ - - Could not set style: %1 - main window - 未能设置风格:%1 + + Default + new constraint dialog + 默认 + + + NewVersionDialog - - Cannot export, because no export plugin is loaded. - 未能导出,因为没有导出æ’件被加载。 + + SQLiteStudio updates + SQLiteStudio æ›´æ–° - - Cannot import, because no import plugin is loaded. - 未能导入,因为没有导入æ’件被加载。 + + New version is available! + 有新版本å¯ç”¨ï¼ - - Rename window - é‡å‘½åçª—å£ + + Download new version! + 下载新版本 - - Enter new name for the window: - 窗å£çš„æ–°å称: + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + 下载新版本程åºåŒ…。将在准备就绪时æé†’您安装。 - - New updates are available. <a href="%1">Click here for details</a>. - 有新更新 <a href="%1">点此查看更新详情</a>. + + Open SQLiteStudio home page. + 打开 SQLiteStudio 主页 - - You're running the most recent version. No updates are available. - 您使用的是最新版,ä¸éœ€è¦æ›´æ–°ã€‚ + + Read release notes && download package yourself. + 阅读å‘行说明和自行下载程åºåŒ…。 - - Database passed in command line parameters (%1) was already on the list under name: %2 - + + Just close this window. + 关闭此窗å£ã€‚ - - Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 - + + Check for updates on startup + 在å¯åŠ¨æ—¶æ£€æŸ¥æ›´æ–° - - Could not add database %1 to list. - æœªèƒ½å°†æ•°æ® %1 添加到列表 + + Not now. + æš‚ä¸æ›´æ–° - - - MdiWindow + + + PopulateConfigDialog - Uncommited changes - 未æäº¤çš„æ›´æ”¹ + + Populating configuration + é…置填充 - - Uncommitted changes - 未æäº¤çš„æ›´æ”¹ + + Configuring <b>%1</b> for column <b>%2</b> + 给字段 <b>%2</b> é…ç½® <b>%1</b> + + + PopulateDialog - - Close anyway - ä»ç„¶å…³é—­ + + Populate table + 填充表 - - Don't close - ä¸å…³é—­ + + Database + æ•°æ®åº“ - - - MultiEditor - - Null value - multieditor - Null 值 + + Table + 表 - - Configure editors for this data type - 为数æ®ç±»åž‹è®¾ç½®ç¼–辑器 + + Columns + 列 - - Open another tab - 打开å¦ä¸€ä¸ªé€‰é¡¹å¡ + + Number of rows to populate: + 填充的行数: - - Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. - æ•°æ®ç¼–辑器æ’ä»¶ '%1' 没有被加载,尽管它被定义为编辑 '%1' æ•°æ®ç±»åž‹ã€‚ + + Populate + populate dialog button + å¡«å…… - - Deleted - multieditor - 已删除 + + Abort + 中止 - - Read only - multieditor - åªè¯» + + Configure + é…ç½® - - - MultiEditorBool - Boolean - 布尔 + + Populating configuration for this column is invalid or incomplete. + 此列的填充é…置无效或ä¸å®Œæ•´ã€‚ - - - MultiEditorBoolPlugin - - Boolean - 布尔 + + Select database with table to populate + 选择è¦å¡«å……表的数æ®åº“ - - - MultiEditorDate - Date - 日期 + + Select table to populate + 选择è¦å¡«å……的表 - - - MultiEditorDatePlugin - - Date - 日期 + + You have to select at least one column. + 您须选择至少一个字段。 - - - MultiEditorDateTime + + + QObject - Date & time - 日期和时间 + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + 无法编辑列,它是å¤åˆ %1 语å¥ï¼ˆåŒ…å« %2ã€%3 或 %4 关键字)的结果。 - - - MultiEditorDateTimePlugin - - Date & time - 日期和时间 + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + 查询执行机制æå– ROWID 的属性时é‡åˆ°é—®é¢˜ã€‚è¿™å¯èƒ½æ˜¯è½¯ä»¶ä¸­çš„ bug,您å¯ä»¥æŠ¥å‘Šè¯¥é—®é¢˜ã€‚ - - - MultiEditorHex - Hex - å六进制 + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + 请求的列是一个 SQL 表达å¼çš„ç»“æžœï¼Œè€Œéžæ™®é€šçš„列。ä¸èƒ½ç¼–辑这些列。 - - - MultiEditorHexPlugin - - Hex - å六进制 + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + 请求的列属于å—é™åˆ¶çš„ SQLite 表。这些表ä¸èƒ½è¢«ç›´æŽ¥ç¼–辑。 - - - MultiEditorNumeric - Number - numeric multi editor tab name - 数值 + + Cannot edit results of query other than %1. + 无法编辑 %1 以外的查询结果。 - - - MultiEditorNumericPlugin - - Number - numeric multi editor tab name - 数值 + + Cannot edit columns that are result of aggregated %1 statements. + 无法编辑列,它是èšåˆçš„ %1 语å¥çš„结果。 - - - MultiEditorText - Text - 文本 + + Cannot edit columns that are result of %1 statement. + 无法编辑列,它是 %1 语å¥çš„结果。 - - Tab changes focus - + + Cannot edit columns that are result of common table expression statement (%1). + 无法编辑列,它是通用表生æˆè¯­å¥ %1 的结果。 - - Cut - 剪切 + + Cannot edit table generated columns. + 无法编辑表生æˆçš„列。 - - Copy - å¤åˆ¶ + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + 无法编辑此列,因为它是在å¦ä¸€ä¸ªå¤šçº§è§†å›¾ä¸Šæ‰§è¡ŒæŸ¥è¯¢çš„è§†å›¾ç»“æžœï¼ˆå³æŸ¥è¯¢å¦ä¸€ä¸ªè§†å›¾çš„视图)。 - - Paste - 粘贴 + + + + + on conflict: %1 + data view tooltip + å½“å†²çªæ—¶ï¼š%1 - - Delete - 删除 + + references table %1, column %2 + data view tooltip + 引用表 %1,列 %2 - - Undo - 撤销 + + condition: %1 + data view tooltip + 字符åºï¼š%1 - - Redo - æ¢å¤ + + collation name: %1 + data view tooltip + 字符åºå称:%1 - - - MultiEditorTextPlugin - - Text - 文本 + + Data grid view + æ•°æ®ç½‘格视图 - - - MultiEditorTime - Time - æ—¶é—´ + + Edit current cell inline + 编辑当å‰å•元格 - - - MultiEditorTimePlugin - - Time - æ—¶é—´ + + Copy cell(s) contents to clipboard + å¤åˆ¶å•å…ƒæ ¼å†…å®¹è‡³å‰ªè´´æ¿ - - - NewConstraintDialog - - New constraint - æ–°çº¦æŸ + + Copy cell(s) contents together with header to clipboard + å¤åˆ¶å•å…ƒæ ¼å†…å®¹ä¸Žè¡¨å¤´è‡³å‰ªè´´æ¿ - - - Primary Key - new constraint dialog - 主键 + + Paste cell(s) contents from clipboard + 粘贴剪贴æ¿ä¸­çš„å•元格内容 - - - Foreign Key - new constraint dialog - 外键 + + Set empty value to selected cell(s) + 设定选中å•元格为空值 - - - Unique - new constraint dialog - 唯一 + + Set NULL value to selected cell(s) + 设定选中å•元格为 NULL 值 - - - Check - new constraint dialog - æ¡ä»¶ + + Commit changes to cell(s) contents + æäº¤å•元格内容更改 - - Not NULL - new constraint dialog - éžç©º + + Rollback changes to cell(s) contents + 回滚å•元格内容更改 - - Collate - new constraint dialog - 排åºè§„则 + + Delete selected data row + 删除所选数æ®è¡Œ - - Default - new constraint dialog - 默认 + + Insert new data row + æ’入新数æ®è¡Œ - - - NewVersionDialog - - SQLiteStudio updates - SQLiteStudio æ›´æ–° + + Open contents of selected cell in a separate editor + 在å•独编辑器中打开所选å•元格的内容 - - New updates are available! - æœ‰æ–°æ›´æ–°ï¼ + + Toggle the height adjustment of rows + Toggle the height adjustment of rows - - Component - 组件 + + Increase font size + data view + å¢žå¤§å­—å· - - This application will be closed and the update installer will start to download and install all the updates. - æ­¤åº”ç”¨å°†ä¼šè¢«å…³é—­ï¼Œç„¶åŽæ›´æ–°å®‰è£…程åºå°†ä¼šå¯åŠ¨ï¼Œä¸‹è½½å¹¶ä¸”å®‰è£…æ‰€æœ‰æ›´æ–°ã€‚ + + Decrease font size + data view + å‡å°å­—å· - Current version - 当年版本 + + Total pages available: %1 + 总计å¯ç”¨é¡µæ•°ï¼š%1 - - Update version - 坿›´æ–°ç‰ˆæœ¬ + + Total rows loaded: %1 + 已加载行数:%1 - - Check for updates on startup - 在å¯åŠ¨æ—¶æ£€æŸ¥æ›´æ–° + + Data view (both grid and form) + æ•°æ®è§†å›¾ï¼ˆç½‘æ ¼ + 表格) - - Update to new version! - æ›´æ–°åˆ°æ–°ç‰ˆæœ¬ï¼ + + Refresh data + åˆ·æ–°æ•°æ® - The update will be automatically downloaded and installed. This will also restart application at the end. - 本次更新将会自动下载和安装。在更新åŽä¼šé‡å¯ç¨‹åºã€‚ + + Switch to grid view of the data + 切æ¢è‡³æ•°æ®çš„网格视图 - - Not now. - çŽ°åœ¨ä¸æ›´æ–°ã€‚ + + Switch to form view of the data + 切æ¢è‡³æ•°æ®çš„表å•视图 - - Don't install the update and close this window. - ä¸å®‰è£…更新并关闭本窗å£ã€‚ + + Database list + æ•°æ®åº“列表 - - - PopulateConfigDialog - - Populating configuration - é…置填充 + + Delete selected item + 删除选中项 - - Configuring <b>%1</b> for column <b>%2</b> - 给字段 <b>%2</b> é…ç½® <b>%1</b> + + Clear filter contents + 清空筛选器内容 - - - PopulateDialog - - Populate table - 填充表 + + Refresh schema + 刷新结构 - - Database - æ•°æ®åº“ + + Refresh all schemas + 刷新全部结构 - - Table - 表 + + Add database + 添加数æ®åº“ - - Columns - 字段 + + Select all items + 选中所有项 - - Number of rows to populate: - 填充的行数: + + Copy selected item(s) + å¤åˆ¶é€‰ä¸­é¡¹ - - Populate - populate dialog button - å¡«å…… + + + + Paste from clipboard + 从剪贴æ¿ç²˜è´´ - - Abort - 中止 + + Increase font size + database list + å¢žå¤§å­—å· - - Configure - é…ç½® + + Decrease font size + database list + å‡å°å­—å· - - Populating configuration for this column is invalid or incomplete. - + + Tables + 表 - - Select database with table to populate - + + Indexes + 索引 - - Select table to populate - 选择è¦å¡«å……的表 + + Triggers + 触å‘器 - - You have to select at least one column. - 您至少得选择一个字段。 + + Views + 视图 - - - QObject - - Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). - + + Columns + 列 - - The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. - + + Data form view + æ•°æ®è¡¨å•视图 - - Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. - + + Commit changes for current row + æäº¤å½“å‰è¡Œçš„æ›´æ”¹ - - Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. - + + Rollback changes for current row + 回滚当å‰è¡Œçš„æ›´æ”¹ - - Cannot edit results of query other than %1. - + + Go to first row on current page + å‰å¾€å½“å‰é¡µçš„第一行 - - Cannot edit columns that are result of aggregated %1 statements. - + + Go to next row + å‰å¾€ä¸‹ä¸€è¡Œ - - Cannot edit columns that are result of %1 statement. - + + Go to previous row + å‰å¾€ä¸Šä¸€è¡Œ - - Cannot edit columns that are result of common table expression statement (%1). - + + Go to last row on current page + å‰å¾€å½“å‰é¡µçš„æœ€åŽä¸€è¡Œ - - - - - on conflict: %1 - data view tooltip - + + Insert new row + æ’入新行 - - references table %1, column %2 - data view tooltip - + + Delete current row + 删除当å‰è¡Œ - - condition: %1 - data view tooltip - + + Main window + ä¸»çª—å£ - - collation name: %1 - data view tooltip - + + Open SQL editor + 打开 SQL 编辑器 - - Data grid view - æ•°æ®ç½‘格视图 + + Open DDL history window + 打开DDL历å²è®°å½•çª—å£ - - Copy cell(s) contents to clipboard - å¤åˆ¶å•å…ƒæ ¼å†…å®¹è‡³å‰ªè´´æ¿ + + Open snippets editor window + 打开代ç ç‰‡æ®µç¼–è¾‘å™¨çª—å£ - - Copy cell(s) contents together with header to clipboard - å¤åˆ¶å•å…ƒæ ¼å†…å®¹ä¸Žè¡¨å¤´è‡³å‰ªè´´æ¿ + + Open function editor window + æ‰“å¼€å‡½æ•°ç¼–è¾‘å™¨çª—å£ - - Paste cell(s) contents from clipboard - 从剪贴æ¿ç²˜è´´å•å…ƒæ ¼æ•°æ® + + Open collation editor window + æ‰“å¼€æ ¡éªŒç¼–è¾‘å™¨çª—å£ - - Set empty value to selected cell(s) - 将选中的å•元格设置为空值 + + Open extension manager window + 打开扩展管ç†çª—å£ - - Set NULL value to selected cell(s) - 将选中的å•元格设置为 NULL + + Previous window + ä¸Šä¸€ä¸ªçª—å£ - - Commit changes to cell(s) contents - + + Next window + ä¸‹ä¸€ä¸ªçª—å£ - - Rollback changes to cell(s) contents - + + Hide status area + éšè—çŠ¶æ€æ  - - Delete selected data row - 删除选中的数æ®è¡Œ + + Open user manual + 打开用户手册 - - Insert new data row - æ’入新数æ®è¡Œ + + Open configuration dialog + 打开é…ç½®å¯¹è¯æ¡† - - Open contents of selected cell in a separate editor - + + Open Debug Console + 打开调试终端 - - Total pages available: %1 - å¯ç”¨é¡µæ•°ï¼š%1 + + Open CSS Console + 打开 CSS æŽ§åˆ¶å° - - Total rows loaded: %1 - 已加载行数:%1 + + Open the About dialog + æ‰“å¼€å…³äºŽå¯¹è¯æ¡† - - Data view (both grid and form) - æ•°æ®è§†å›¾ï¼ˆç½‘æ ¼ + 表格) + + Quit the application + é€€å‡ºç¨‹åº - - Refresh data - åˆ·æ–°æ•°æ® + + Cell text value editor + å•元格文本值编辑器 - - Switch to grid view of the data - 切æ¢è‡³æ•°æ®çš„网格视图 + + + Cut selected text + 剪切选中文本 - - Switch to form view of the data - 切æ¢è‡³æ•°æ®çš„表格视图 + + + Copy selected text + å¤åˆ¶é€‰ä¸­æ–‡æœ¬ - - Database list - æ•°æ®åº“列表 + + + Delete selected text + 删除选中文本 - - Delete selected item - 删除选中项 + + + Undo + 撤销 - - Clear filter contents - 清除筛选器内容 + + + Redo + é‡åš - - Refresh schema - + + SQL editor input field + SQL 编辑器输入框 - - Refresh all schemas - + + Select whole editor contents + 选中整个编辑器的内容 - - Add database - 添加数æ®åº“ + + Save contents into a file + 将内容ä¿å­˜è‡³æ–‡ä»¶ - - Select all items - 选中所有项 + + Load contents from a file + 从文件加载内容 - - Copy selected item(s) - å¤åˆ¶é€‰ä¸­é¡¹ + + Find in text + 查找文本 - - - - Paste from clipboard - 从剪贴æ¿ç²˜è´´ + + Find next + 查找下一个 - - Tables - 表 + + Find previous + 查找上一个 - - Indexes - 索引 + + Replace in text + æ›¿æ¢æ–‡æœ¬ - - Triggers - 触å‘器 + + Delete current line + 删除当å‰è¡Œ - - Views - 视图 + + Request code assistant + 请求代ç è¾…助 - - Columns - 字段 + + Format contents + æ ¼å¼åŒ–内容 - - Data form view - æ•°æ®è¡¨æ ¼è§†å›¾ + + Move selected block of text one line down + 选中文本å—下移一行 - - Commit changes for current row - æäº¤å½“å‰è¡Œçš„æ›´æ”¹ + + Move selected block of text one line up + 选中文本å—上移一行 - - Rollback changes for current row - 回滚当å‰è¡Œçš„æ›´æ”¹ + + Copy selected block of text and paste it a line below + 选中文本å—å¤åˆ¶å¹¶ç²˜è´´åˆ°ä¸‹ä¸€è¡Œ - - Go to first row on current page - å‰å¾€å½“å‰é¡µçš„第一行 + + Copy selected block of text and paste it a line above + 选中文本å—å¤åˆ¶å¹¶ç²˜è´´åˆ°ä¸Šä¸€è¡Œ - - Go to next row - å‰å¾€ä¸‹ä¸€è¡Œ + + Toggle comment + åˆ‡æ¢æ³¨é‡Š - - Go to previous row - å‰å¾€ä¸Šä¸€è¡Œ + + Increase font size + sql editor + å¢žå¤§å­—å· - - Go to last row on current page - å‰å¾€å½“å‰é¡µçš„æœ€åŽä¸€è¡Œ + + Decrease font size + sql editor + å‡å°å­—å· - - Insert new row - æ’入新行 + + All SQLite databases + 所有 SQLite æ•°æ®åº“ - - Delete current row - 删除当å‰è¡Œ + + All files + 所有文件 - - Main window - ä¸»çª—å£ + + Select database file + 选择数æ®åº“文件 - - Open SQL editor - 打开 SQL 编辑器 + + Select + 选择 - - Previous window - ä¸Šä¸€ä¸ªçª—å£ + + File type + 文件类型 - - Next window - ä¸‹ä¸€ä¸ªçª—å£ + + SQL editor window + SQL ç¼–è¾‘å™¨çª—å£ - - Hide status area - éšè—çŠ¶æ€æ  + + Execute query + æ‰§è¡Œè¯­å¥ - - Open configuration dialog - 打开é…ç½®å¯¹è¯æ¡† + + Execute single query under cursor + 执行光标ä½ç½®çš„å•个查询 - - Open Debug Console - 打开调试终端 + + Execute all queries in editor + 在编辑器中执行所有查询 - - Open CSS Console - 打开 CSS æŽ§åˆ¶å° + + Execute "%1" query + 执行 "%1" 查询 - - Cell text value editor - + + Switch current working database to previous on the list + 切æ¢å½“å‰å·¥ä½œæ•°æ®åº“到列表中的上一个 - - - Cut selected text - 剪切选中文本 + + Switch current working database to next on the list + 切æ¢å½“å‰å·¥ä½œæ•°æ®åº“到列表中的下一个 - - - Copy selected text - å¤åˆ¶é€‰ä¸­æ–‡æœ¬ + + Go to next editor tab + å‰å¾€ä¸‹ä¸€ç¼–è¾‘å™¨é€‰é¡¹å¡ - - - Delete selected text - 删除选中文本 + + Go to previous editor tab + å‰å¾€ä¸Šä¸€ç¼–è¾‘å™¨é€‰é¡¹å¡ - - - Undo - 撤销 + + Move keyboard input focus to the results view below + 将键盘输入焦点移动到下é¢çš„结果视图 - - - Redo - é‡åš + + Move keyboard input focus to the SQL editor above + 将键盘输入焦点移到上é¢çš„ SQL 编辑器 - - SQL editor input field - + + Delete selected SQL history entries + 删除选中的 SQL 历å²è®°å½• - - Select whole editor contents - 选中整个编辑器的内容 + + Table window + è¡¨çª—å£ - - Save contents into a file - 将内容ä¿å­˜è‡³æ–‡ä»¶ + + Commit the table structure + æäº¤è¡¨ç»“æž„ - - Load contents from a file - 从文件加载内容 + + Rollback pending changes in the table structure + 回滚表结构中未确认的更改 - - Find in text - + + Refresh table structure + 刷新表结构 - - Find next - 查找下一个 + + Add new column + 添加新列 - - Find previous - 查找上一个 + + Edit selected column + 编辑选中列 - - Replace in text - + + Delete selected column + 删除选中列 - - Delete current line - 删除当å‰è¡Œ + + Export table data + å¯¼å‡ºè¡¨æ•°æ® - - Request code assistant - 请求代ç è¾…助 + + Import data to the table + 导入数æ®è‡³è¡¨ - - Format contents - æ ¼å¼åŒ–内容 + + Add new table constraint + æ–°å¢žè¡¨çº¦æŸ - - Move selected block of text one line down - + + Edit selected table constraint + ç¼–è¾‘é€‰ä¸­è¡¨çº¦æŸ - - Move selected block of text one line up - + + Delete selected table constraint + åˆ é™¤é€‰ä¸­è¡¨çº¦æŸ - - Copy selected block of text and paste it a line below - + + Refresh table index list + 刷新表索引列表 - - Copy selected block of text and paste it a line above - + + Add new index + 新增索引 - - Toggle comment - + + Edit selected index + 编辑选中索引 - - All SQLite databases - 所有 SQLite æ•°æ®åº“ + + Delete selected index + 删除选中索引 - - All files - 所有文件 + + Refresh table trigger list + 刷新表触å‘器列表 - - - Database file - æ•°æ®åº“文件 + + + Add new trigger + 添加新触å‘器 - Delete selected entry - 删除选中项 + + + Edit selected trigger + 编辑选中触å‘器 - - SQL editor window - SQL ç¼–è¾‘å™¨çª—å£ + + + Delete selected trigger + 删除选中触å‘器 - - Execute query - æ‰§è¡Œè¯­å¥ + + + Go to next tab + å‰å¾€ä¸‹ä¸€é€‰é¡¹å¡ - - Execute "%1" query - + + + Go to previous tab + å‰å¾€ä¸Šä¸€é€‰é¡¹å¡ - - Switch current working database to previous on the list - + + A view window + è§†å›¾çª—å£ - - Switch current working database to next on the list - + + Commit the view's query + æäº¤è§†å›¾æŸ¥è¯¢ - - Go to next editor tab - å‰å¾€ä¸‹ä¸€ç¼–è¾‘å™¨é€‰é¡¹å¡ + + Rollback pending changes in the view's query + 回滚视图查询中未确认的更改 - - Go to previous editor tab - å‰å¾€ä¸Šä¸€ç¼–è¾‘å™¨é€‰é¡¹å¡ + + Refresh view trigger list + 刷新视图触å‘器列表 - - Move keyboard input focus to the results view below - + + Execute the view's query + 执行视图查询 - - Move keyboard input focus to the SQL editor above - + + A code snippets editor window + 代ç ç‰‡æ®µç¼–è¾‘å™¨çª—å£ - - Delete selected SQL history entries - + + + + + Commit the pending changes + æäº¤æœªç¡®è®¤çš„æ›´æ”¹ - - Table window - è¡¨çª—å£ + + + + + Rollback the pending changes + 回滚未确认的更改 - - Refresh table structure - 刷新表结构 + + A collation editor window + æ ¡éªŒç¼–è¾‘å™¨çª—å£ - - Add new column - 添加新字段 + + A function editor window + å‡½æ•°ç¼–è¾‘å™¨çª—å£ - - Edit selected column - 编辑选中字段 + + A SQLite extension editor window + SQLite æ‰©å±•ç¼–è¾‘å™¨çª—å£ + + + QuitConfirmDialog - - Delete selected column - 删除选中字段 + + Uncommitted changes + 未æäº¤çš„æ›´æ”¹ - - Export table data - å¯¼å‡ºè¡¨é‡Œçš„æ•°æ® + + Are you sure you want to quit the application? + +Following items are pending: + 确定è¦é€€å‡ºæœ¬ç¨‹åºå—? + +下列项目ä»å¾…处置: + + + SearchTextDialog - - Import data to the table - 导入数æ®è‡³è¡¨ä¸­ + + Find or replace + æŸ¥æ‰¾ä¸Žæ›¿æ¢ - - Add new table constraint - æ·»åŠ æ–°çš„è¡¨çº¦æŸ + + Find: + 查找: - - Edit selected table constraint - ç¼–è¾‘é€‰ä¸­çš„è¡¨çº¦æŸ + + Case sensitive + 区分大å°å†™ - - Delete selected table constraint - åˆ é™¤é€‰ä¸­çš„è¡¨çº¦æŸ + + Search backwards + å呿œç´¢ - - Refresh table index list - 刷新表索引列表 + + Regular expression matching + 正则表达å¼åŒ¹é… - - Add new index - 添加新索引 + + Replace && +find next + 替æ¢å¹¶æŸ¥æ‰¾ä¸‹ä¸€é¡¹ - - Edit selected index - 编辑选中索引 + + Replace with: + 替æ¢ä¸ºï¼š - - Delete selected index - 删除选中索引 + + Replace all + å…¨éƒ¨æ›¿æ¢ - - Refresh table trigger list - 刷新表触å‘器列表 + + Find + 查找 + + + SortDialog - - - Add new trigger - 添加新触å‘器 + + Sort by columns + æŒ‰åˆ—æŽ’åº - - - Edit selected trigger - 编辑选中触å‘器 + + + Column + 列 - - - Delete selected trigger - 删除选中触å‘器 + + + Order + æŽ’åº - - - Go to next tab - å‰å¾€ä¸‹ä¸€é€‰é¡¹å¡ + + Sort by: %1 + æŽ’åºæŒ‰ç…§ %1 - - - Go to previous tab - å‰å¾€ä¸Šä¸€é€‰é¡¹å¡ + + Move column up + 上移列 - - A view window - + + Move column down + 下移列 + + + SqlEditor - - Refresh view trigger list - 刷新视图触å‘器列表 + + Wrap words + sql editor + 折行 - - - QuitConfirmDialog - Uncommited changes - 未æäº¤çš„æ›´æ”¹ + + Cut + sql editor + 剪切 - - Uncommitted changes - 未æäº¤çš„æ›´æ”¹ + + Copy + sql editor + å¤åˆ¶ - - Are you sure you want to quit the application? - -Following items are pending: - 您确定è¦é€€å‡ºæœ¬ç¨‹åºå—? + + Paste + sql editor + 粘贴 - - - SearchTextDialog - - Find or replace - æŸ¥æ‰¾ä¸Žæ›¿æ¢ + + Delete + sql editor + 删除 - - Find: - 查找: + + Select all + sql editor + 全选 - - Case sensitive - 大å°å†™æ•感 + + Undo + sql editor + 撤销 - - Search backwards - å呿œç´¢ + + Redo + sql editor + é‡åš - - Regular expression matching - æ­£åˆ™è¡¨è¾¾å¼ + + Complete + sql editor + å®Œæˆ - - Replace && -find next - 替æ¢å¹¶æŸ¥æ‰¾ä¸‹ä¸€ä¸ª + + Format SQL + sql editor + æ ¼å¼åŒ– SQL - - Replace with: - 替æ¢ä¸ºï¼š + + Save SQL to file + sql editor + ä¿å­˜ SQL 到文件 - - Replace all - å…¨éƒ¨æ›¿æ¢ + + Select file to save SQL + sql editor + 选择 SQL è¦ä¿å­˜åˆ°çš„æ–‡ä»¶ - - Find - 查找 + + Load SQL from file + sql editor + 从文件加载 SQL - - - SortDialog - - Sort by columns - æŒ‰å­—æ®µæŽ’åº + + Delete line + sql editor + 删除行 - - - Column - 字段 + + Move block down + sql editor + æ•´å—下移 - - - Order - æŽ’åº + + Move block up + sql editor + æ•´å—上移 - - Sort by: %1 - 按:%1 æŽ’åº + + Copy block down + sql editor + 副本贴上方 - - Move column up - 上移字段 + + Copy up down + sql editor + 副本贴下方 - - Move column down - 下移字段 + + Find + sql editor + 查找 - - - SqlEditor - - Cut - sql editor - 剪切 + + Find next + sql editor + 查找下一个 - - Copy - sql editor - å¤åˆ¶ + + Find previous + sql editor + 查找上一个 - - Paste - sql editor - 粘贴 + + Replace + sql editor + æ›¿æ¢ - - Delete - sql editor - 删除 + + Toggle comment + sql editor + åˆ‡æ¢æ³¨é‡Š - - Select all - sql editor - 全选 + + Increase font size + sql editor + å¢žå¤§å­—å· - - Undo - sql editor - 撤销 + + Decrease font size + sql editor + å‡å°å­—å· - - Redo - sql editor - æ¢å¤ + + Could not open file '%1' for writing: %2 + æ— æ³•ä»¥å†™æ¨¡å¼æ‰“开文件 %1:%2 - - Complete - sql editor - å®Œæˆ + + Saved SQL contents to file: %1 + ä¿å­˜ SQL 内容至文件:%1 - - Format SQL - sql editor - æ ¼å¼åŒ–SQL + + Syntax completion can be used only when a valid database is set for the SQL editor. + SQL 编辑器的语法补全功能仅当存在有效数æ®åº“æ—¶å¯ç”¨ã€‚ - - Save SQL to file - sql editor - ä¿å­˜SQL到文件 + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + SQL 编辑器有大é‡å†…容,因此错误检测和现有对象的高亮显示功能被暂时ç¦ç”¨ã€‚ - - Select file to save SQL - sql editor - 选择 SQL è¦ä¿å­˜åˆ°çš„æ–‡ä»¶ + + Save to file + ä¿å­˜åˆ°æ–‡ä»¶ - - Load SQL from file - sql editor - 从文件加载SQL + + SQL scripts (*.sql);;All files (*) + SQL文件 (*.sql);;所有文件 (*) - - Delete line - sql editor - 删除行 + + Open file + 打开文件 - - Move block down - sql editor - æ•´å—下移 + + Could not open file '%1' for reading: %2 + æ— æ³•ä»¥è¯»æ¨¡å¼æ‰“开文件 %1:%2 - - Move block up - sql editor - æ•´å—上移 + + Reached the end of document. Hit the find again to restart the search. + å·²åˆ°æ–‡æ¡£åº•éƒ¨ã€‚å†æ¬¡ç‚¹å‡»æŸ¥æ‰¾å°†ä»Žå¤´å¼€å§‹æœç´¢ã€‚ + + + SqlQueryItem - - Copy block down - sql editor - + + Committing error: + data view tooltip + æäº¤å‡ºé”™ï¼š - - Copy up down - sql editor - + + Column: + data view tooltip + 列: - - Find - sql editor - 查找 + + Data type: + data view + æ•°æ®ç±»åž‹ï¼š - - Find next - sql editor - 查找下一个 + + Table: + data view tooltip + 表: - - Find previous - sql editor - 查找上一个 + + Constraints: + data view tooltip + 约æŸï¼š + + + SqlQueryItemDelegate - - Replace - sql editor - æ›¿æ¢ + + + + + Cannot edit this cell. Details: %1 + 无法编辑此å•元格。详情:%1 - - Toggle comment - sql editor - + + The row is marked for deletion. + 该行已被标记为删除。 - - Saved SQL contents to file: %1 - ä¿å­˜ SQL 内容至文件:%1 + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + 在上次加载数æ®åŽï¼Œæ­¤è¡¨çš„ç»“æž„å·²æ›´æ”¹ã€‚é‡æ–°åŠ è½½æ•°æ®ä»¥ç»§ç»­ã€‚ - - Syntax completion can be used only when a valid database is set for the SQL editor. - + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + 在内è”å•å…ƒæ ¼ç¼–è¾‘å™¨ä¸­ç¼–è¾‘å¤§åž‹å†…å®¹ä¸æ˜¯ä¸€ä¸ªå¥½ä¸»æ„。它很å¯èƒ½å˜å¾—缓慢和ä¸ä¾¿ã€‚最好在表å•视图或者弹出å¼ç¼–辑器(从å³é”®èœå•打开)中编辑此类大内容。 - - Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. - + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + 列 %2 的外键有超过 %1 个å¯èƒ½çš„值。这太多而ä¸èƒ½æ˜¾ç¤ºåœ¨ä¸‹æ‹‰åˆ—è¡¨ã€‚æ‚¨éœ€è¦æ‰‹åŠ¨ç¼–è¾‘å€¼ã€‚ + + + SqlQueryModel - - Save to file - ä¿å­˜åˆ°æ–‡ä»¶ + + + Only one query can be executed simultaneously. + åªå…è®¸åŒæ—¶æ‰§è¡Œä¸€æ¡æŸ¥è¯¢ã€‚ - - Could not open file '%1' for writing: %2 - æ— æ³•ä»¥å†™æ¨¡å¼æ‰“开文件 %1:%2 + + Cannot execute query on undefined or invalid database. + 无法执行语å¥ï¼Œå› ä¸ºæ•°æ®åº“无效或未定义。 - - SQL scripts (*.sql);;All files (*) - SQL文件 (*.sql);;所有文件 (*) + + Cannot execute empty query. + 无法执行空的查询。 - - Open file - 打开文件 + + Uncommitted data + 未æäº¤çš„æ•°æ® - - Could not open file '%1' for reading: %2 - æ— æ³•ä»¥è¯»æ¨¡å¼æ‰“开文件 %1:%2 + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + 存在未æäº¤çš„æ•°æ®æ›´æ”¹ã€‚是å¦ä»è¦ç»§ç»­ï¼Ÿæ‰€æœ‰æœªæäº¤çš„æ›´æ”¹éƒ½å°†ä¸¢å¤±ã€‚ - - Reached the end of document. Hit the find again to restart the search. - å·²æœç´¢åˆ°æ–‡æ¡£åº•部。点击查找从头程åºå¼€å§‹æœç´¢ã€‚ + + Cannot commit the data for a cell that refers to the already closed database. + 无法从å•元格中加载数æ®ï¼Œå› ä¸ºå®ƒå¼•用了已ç»è¢«å…³é—­çš„æ•°æ®åº“。 - - - SqlQueryItem - - Column: - data view tooltip - 字段: + + Could not begin transaction on the database. Details: %1 + 无法在此数æ®åº“上开始事务。详细信æ¯ï¼š%1 - - Data type: - data view - æ•°æ®ç±»åž‹ï¼š + + An error occurred while committing the transaction: %1 + 在æäº¤äº‹åŠ¡æ—¶å‘生错误:%1 - - Table: - data view tooltip - 表: + + An error occurred while rolling back the transaction: %1 + 在回滚事务时å‘生错误:%1 - - Constraints: - data view tooltip - 约æŸï¼š + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + å°è¯•æäº¤ä¸å¯ç¼–辑的å•元格ï¼ï¼ˆæœ¬åº”该无法编辑)这是一个错误,请报告。 - - Cannot load the data for a cell that refers to the already closed database. - + + An error occurred while committing the data: %1 + 在æäº¤æ•°æ®æ—¶å‘生错误:%1 - - - SqlQueryItemDelegate - - The row is marked for deletion. - 该行被标记为删除。 + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + æ•°æ®è§†å›¾ä¸­ï¼Œå·²å› åˆ—数(%2)将æ¯é¡µè¡Œæ•°å‡å°ï¼ˆ%1)。 - - - - - - Cannot edit this cell. Details: %1 - 无法编辑此å•元格。详情:%1 + + + Error while executing SQL query on database '%1': %2 + 在数æ®åº““%1â€æ‰§è¡Œ SQL 查询时å‘生错误:%2 - - - Structure of this table has changed since last data was loaded. Reload the data to proceed. - + + Error while loading query results: %1 + 在加载查询结果时出错:%1 - - Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). - + + Insert multiple rows + æ’入多行 - - Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. - + + Number of rows to insert: + è¦æ’入的行数: - - - SqlQueryModel - - - Only one query can be executed simultaneously. - åªå…è®¸åŒæ—¶æ‰§è¡Œä¸€æ¡æŸ¥è¯¢ã€‚ + + Delete rows + 删除行 - - Cannot commit the data for a cell that refers to the already closed database. - + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + 您å³å°†åˆ é™¤æœªæäº¤çš„æ–°æ’入的行。行数:%1 +删除ä¸èƒ½æ’¤é”€ï¼Œç¡®å®šåˆ é™¤å—? + + + SqlQueryView - - Could not begin transaction on the database. Details: %1 - + + Go to referenced row in... + 转到引用的行... - - An error occurred while rolling back the transaction: %1 - + + Copy + å¤åˆ¶ - - Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. - + + Copy with headers + 带表头å¤åˆ¶ - - Uncommitted data - 未æäº¤çš„æ•°æ® + + Copy as... + å¤åˆ¶ä¸º... - - There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. - + + Paste + 粘贴 - - An error occurred while committing the transaction: %1 - 在æäº¤äº‹åŠ¡æ—¶å‘生错误:%1 + + Paste as... + 粘贴为... - - An error occurred while committing the data: %1 - 在æäº¤æ•°æ®æ—¶å‘生错误:%1 + + Set NULL values + 设为 NULL 值 - - Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - + + Erase values + 擦除值 - - - Error while executing SQL query on database '%1': %2 - 在数æ®åº““%1â€æ‰§è¡Œ SQL 查询时å‘生错误:%2 + + Commit + æäº¤ - - Error while loading query results: %1 - 在加载查询结果时出错:%1 + + Rollback + 回滚 - - Insert multiple rows - æ’入多行 + + Commit selected cells + æäº¤é€‰ä¸­å•元格 - - Number of rows to insert: - + + Rollback selected cells + 回滚选中å•元格 - - - SqlQueryView - - Go to referenced row in... - + + Edit current cell inline + 编辑当å‰å•元格 - - Copy - å¤åˆ¶ + + Define columns to sort by + å®šä¹‰åˆ—æŽ’åºæ–¹å¼ - - Copy as... - å¤åˆ¶ä¸º... + + Remove custom sorting + ç§»é™¤è‡ªå®šä¹‰æŽ’åº - - Paste - 粘贴 + + Insert row + æ’入行 - - Paste as... - 粘贴为... + + Insert multiple rows + æ’入多行 - - Set NULL values - 设置为NULL + + Delete selected row + 删除已选行 - - Erase values - 擦除 + + Adjust height of rows + 调整行高 - - Edit value in editor - 在编辑器中编辑数值 + + Increase font size + data view + å¢žå¤§å­—å· - - Commit - æäº¤ + + Decrease font size + data view + å‡å°å­—å· - - Copy with headers - 带表头å¤åˆ¶ + + Invert selection + data view + åå‘é€‰å– - - Rollback - 回滚 + + Edit value in editor + 在编辑器中编辑数值 - - Commit selected cells - æäº¤é€‰ä¸­å•元格 + + Show value in a viewer + 在查看器中显示值 - - Rollback selected cells - 回滚选中å•元格 + + Generate query for selected cells + 为选中å•å…ƒæ ¼ç”ŸæˆæŸ¥è¯¢ - - Define columns to sort by - + + No items selected to paste clipboard contents to. + 没有选择用æ¥ç²˜è´´å‰ªè´´æ¿å†…容的项。 - - Remove custom sorting - + + Cannot paste data. Details: %1 + 无法粘贴数æ®ã€‚详情:%1 - - Insert row - æ’入行 + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + 至少一个表的结构在上次的数æ®åŠ è½½åŽå·²å˜æ›´ã€‚釿–°åŠ è½½æ•°æ®ä»¥ç»§ç»­ã€‚ - - Insert multiple rows - æ’入多行 + + Cannot paste to a cell. Details: %1 + 无法粘贴到å•元格。详情:%1 - - Delete selected row - 删除已选行 + + The row is marked for deletion. + 该行已被标记为删除。 - - Show value in a viewer - + + Cannot paste to column %1. Details: %2 + 无法粘贴到列 %1。详情:%2 - - Generate query for selected cells - + + Go to referenced row in table '%1' + 转至表 '%1' 中的引用的行 - - No items selected to paste clipboard contents to. - + + table '%1' + 表“%1†- - Go to referenced row in table '%1' - + + Referenced row (%1) + 引用的行(%1) - - table '%1' - 表“%1†+ + Trim pasted text? + 移除粘贴文本两端的空白? - - Referenced row (%1) - + + The pasted text contains leading or trailing white space. Trim it automatically? + ç²˜è´´çš„æ–‡æœ¬ä¸¤ç«¯å«æœ‰ç©ºç™½ç¬¦å·ã€‚自动移除? - - Trim pasted text? - 移除粘贴文本两端的空格? + + Paste "NULL" as null value? + å°† "NULL" 粘贴为空值? - - The pasted text contains leading or trailing white space. Trim it automatically? - ç²˜è´´çš„æ–‡æœ¬ä¸¤ç«¯å«æœ‰ç©ºæ ¼ã€‚自动移除? + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + ç²˜è´´çš„æ–‡æœ¬åŒ…å« "NULL" 字符。è¦å°†å®ƒä»¬è§†ä¸ºç©ºå€¼å—? - - Edit value - 编辑值 + + Edit value + 编辑值 - - + + SqlTableModel - Error while commiting new row: %1 - 写入新行时å‘生了错误:%1 + + Error while committing new row: %1 + æäº¤æ–°è¡Œæ—¶å‘生错误:%1 - - Error while committing new row: %1 - æäº¤æ–°è¡Œæ—¶å‘生了错误:%1 + + Error while deleting row from table %1: %2 + 从表 %1 中删除行时å‘生错误:%2 - - - Error while deleting row from table %1: %2 - 删除行时å‘生了错误 %1:%2 - - - + + SqliteExtensionEditor - - Filter extensions - 筛选扩展 + + Filter extensions + 扩展筛选器 - - Leave empty to use default function - ä¿æŒç©ºç™½å°†ä½¿ç”¨é»˜è®¤å‡½æ•° + + Leave empty to use default function + 留空则使用默认函数 - - Extension file - 扩展文件 + + Extension file + 扩展文件 - - Initialization function - åˆå§‹åŒ–函数 + + Initialization function + åˆå§‹åŒ–函数 - - Databases - æ•°æ®åº“ + + Databases + æ•°æ®åº“ - - Register in all databases - 在所有数æ®åº“中注册 + + Register in all databases + 在所有数æ®åº“中注册 - - Register in following databases: - 在下列数æ®åº“中注册: + + Register in following databases: + 在下列数æ®åº“中注册: - - Extension manager window has uncommitted modifications. - 扩展管ç†çª—壿œ‰æœªæäº¤çš„æ›´æ”¹ã€‚ + + Extension manager window has uncommitted modifications. + 扩展管ç†çª—壿œ‰æœªæäº¤çš„æ›´æ”¹ã€‚ - - Extension manager - 扩展管ç†å™¨ + + Extension manager + 扩展管ç†å™¨ - - Commit all extension changes - æäº¤æ‰€æœ‰æ‰©å±•æ”¹å˜ + + Commit all extension changes + æäº¤æ‰€æœ‰æ‰©å±•更改 - - Rollback all extension changes - å›žæ»šæ‰€æœ‰æ‰©å±•æ”¹å˜ + + Rollback all extension changes + 回滚所有扩展更改 - - Add new extension - 添加新的扩展 + + Add new extension + 添加新扩展 - - Remove selected extension - 移除选中的扩展 + + Remove selected extension + 移除所选扩展 - - Editing extensions manual - 手动编辑扩展 + + Editing extensions manual + 手动编辑扩展 - - File with given path does not exist or is not readable. - + + File with given path does not exist or is not readable. + 指定路径的文件ä¸å­˜åœ¨æˆ–无法读å–。 - - Unable to load extension: %1 - 无法加载扩展:%1 + + Unable to load extension: %1 + 无法加载扩展:%1 - - Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + åˆå§‹åŒ–å‡½æ•°åæ— æ•ˆã€‚函数ååªèƒ½åŒ…å«å­—æ¯æ•°å­—字符和下划线。 - - Dynamic link libraries (*.dll);;All files (*) - 动æ€é“¾æŽ¥åº“ (*.dll);;所有文件 (*) + + Dynamic link libraries (*.dll);;All files (*) + 动æ€é“¾æŽ¥åº“ (*.dll);;所有文件 (*) - - Shared objects (*.so);;All files (*) - + + Shared objects (*.so);;All files (*) + 共享库 (*.so);;All files (*) - - Dynamic libraries (*.dylib);;All files (*) - 动æ€åº“ (*.dylib);;所有文件 (*) + + Dynamic libraries (*.dylib);;All files (*) + 动æ€åº“ (*.dylib);;所有文件 (*) - - All files (*) - 所有文件 (*) + + All files (*) + 所有文件 (*) - - Open file - 打开文件 + + Open file + 打开文件 - - + + StatusField - - Status - çŠ¶æ€ + + Status + çŠ¶æ€æ  - - Copy - å¤åˆ¶ + + Copy + å¤åˆ¶ - - Clear - 清除 + + Clear + 清除 - - + + TableConstraintsModel - - Type - table constraints - 类型 + + Type + table constraints + 类型 - - Details - table constraints - 详情 + + Details + table constraints + 详情 - - Name - table constraints - åç§° + + Name + table constraints + åç§° - - + + TableForeignKeyPanel - - Foreign table: - 外部表: - - - - SQLite 2 does not support foreign keys officially, -but it's okay to use them anyway. - SQLite 2 䏿­£å¼æ”¯æŒå¤–键, -但总之,å¯ä»¥ä½¿ç”¨å¤–键。 + + Foreign table: + 外部表: - - Columns - 字段 + + Columns + 列 - - Local column - 本地字段 + + Local column + 本地列 - - Foreign column - 外部字段 + + Foreign column + 外部列 - - Reactions - å“应 + + Reactions + å“应 - - Deferred foreign key - + + Deferred foreign key + å»¶è¿Ÿå¤–é”®çº¦æŸ - - Named constraint - + + Named constraint + 命åçš„çº¦æŸ - - Constraint name - 约æŸåç§° + + Constraint name + 约æŸåç§° - - Pick the foreign column. - 选择一个外部字段。 + + Pick the foreign column. + 选择一个外部列。 - - Pick the foreign table. - 选择一个外部表。 + + Pick the foreign table. + 选择一个外部表。 - - Select at least one foreign column. - 请至少选择一个外部字段。 + + Select at least one foreign column. + 请至少选择一个外部列。 - - Enter a name of the constraint. - 输入一个约æŸçš„å称。 + + Enter a name of the constraint. + 输入一个约æŸçš„å称。 - - Foreign column - table constraints - 外部字段 + + Foreign column + table constraints + 外部列 - - + + TablePrimaryKeyAndUniquePanel - - Columns - 字段 + + Columns + 列 - - Column - 字段 + + Column + 列 - - Collation - 排åºè§„则 + + Collation + å­—ç¬¦åº - - Sort - æŽ’åº + + Sort + æŽ’åº - - Valid only for a single column with INTEGER data type - + + Valid only for a single column with INTEGER data type + 仅对整数(INTEGER)数æ®ç±»åž‹çš„å•个列有效 - - Autoincrement - Autoincrement + + Autoincrement + 自动递增 - - Named constraint - + + Named constraint + 命åçš„çº¦æŸ - - Constraint name - 约æŸåç§° + + Constraint name + 约æŸåç§° - - On conflict - å½“å†²çªæ—¶ + + On conflict + å½“å†²çªæ—¶ - - Collate - table constraints - 排åºè§„则 + + Collate + table constraints + å­—ç¬¦åº - - Sort order - table constraints - æŽ’åº + + Sort order + table constraints + æŽ’åºæ–¹å¼ - - Select at least one column. - 至少选择一列。 + + Select at least one column. + 至少选择一个列。 - - Enter a name of the constraint. - 输入一个约æŸçš„å称。 + + Enter a name of the constraint. + 输入一个约æŸçš„å称。 - - + + TableStructureModel - - Name - table structure columns - åç§° + + Name + table structure columns + åç§° - - Data type - table structure columns - æ•°æ®ç±»åž‹ + + Data type + table structure columns + æ•°æ®ç±»åž‹ - - Primary + + Primary Key - table structure columns - 主键 + table structure columns + 主键 - - Foreign + + Foreign Key - table structure columns - 外键 + table structure columns + 外键 - - Unique - table structure columns - 唯一 + + Unique + table structure columns + 唯一 - - Check - table structure columns - æ¡ä»¶ + + Check + table structure columns + æ¡ä»¶ - - Not + + Not NULL - table structure columns - éž NULL + table structure columns + éžç©º - - Collate - table structure columns - 排åºè§„则 + + Collate + table structure columns + å­—ç¬¦åº - - Default value - table structure columns - 默认值 + + Generated + table structure columns + ç”Ÿæˆ - - + + + Default value + table structure columns + 默认值 + + + TableWindow - - Structure - 结构 + + Structure + 结构 + + + + Table name: + 表å: - - Table name: - 表å: + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> - - - Data - æ•°æ® + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> - - Constraints - çº¦æŸ + + + Data + æ•°æ® - - Indexes - 触å‘器 + + Constraints + çº¦æŸ - - Triggers - 触å‘器 + + Indexes + 索引 - - DDL - DDL + + Triggers + 触å‘器 - - Export table - table window - 导出表 + + DDL + DDL - - Import data to table - table window - 导入数æ®è‡³è¡¨ + + Export table + table window + 导出表 - - Populate table - table window - 填充表 + + Import data to table + table window + 导入数æ®è‡³è¡¨ - - Refresh structure - table window - 刷新结构 + + Populate table + table window + 填充表 - - Commit structure changes - table window - æäº¤ç»“构修改 + + Refresh structure + table window + 刷新结构 - - Rollback structure changes - table window - å›žæ»šç»“æž„æ”¹å˜ + + Commit structure changes + table window + æäº¤ç»“构更改 - - Add column - table window - 添加字段 + + Rollback structure changes + table window + 回滚结构更改 - - Edit column - table window - 编辑字段 + + Add column + table window + 添加列 - - - Delete column - table window - 删除字段 + + Edit column + table window + 编辑列 - - Move column up - table window - å‘上移动字段 + + + Delete column + table window + 删除列 - - Move column down - table window - å‘下移动字段 + + Move column up + table window + 上移列 - - Create similar table - table window - 创建一个相似的表 + + Move column down + table window + 下移列 - - Reset autoincrement value - table window - + + Create similar table + table window + 创建相似的表 - - Add table constraint - table window - æ·»åŠ è¡¨çº¦æŸæ¡ä»¶ + + Reset autoincrement value + table window + é‡ç½®è‡ªåŠ¨é€’å¢žå€¼ - - Edit table constraint - table window - ç¼–è¾‘è¡¨çº¦æŸ + + Add table constraint + table window + æ–°å¢žè¡¨çº¦æŸ - - Delete table constraint - table window - åˆ é™¤è¡¨çº¦æŸ + + Edit table constraint + table window + ç¼–è¾‘è¡¨çº¦æŸ - - Move table constraint up - table window - å‘ä¸Šç§»åŠ¨è¡¨çº¦æŸ + + Delete table constraint + table window + åˆ é™¤è¡¨çº¦æŸ - - Move table constraint down - table window - å‘ä¸‹ä¸€ç§»åŠ¨è¡¨çº¦æŸ + + Move table constraint up + table window + å‘ä¸Šç§»åŠ¨è¡¨çº¦æŸ - - Add table primary key - table window - 添加主键 + + Move table constraint down + table window + å‘ä¸‹ä¸€ç§»åŠ¨è¡¨çº¦æŸ - - Add table foreign key - table window - 添加外键 + + Add table primary key + table window + 添加主键 - - Add table unique constraint - table window - æ·»åŠ è¡¨å”¯ä¸€çº¦æŸ + + Add table foreign key + table window + 添加外键 - - Add table check constraint - table window - + + Add table unique constraint + table window + æ·»åŠ è¡¨å”¯ä¸€çº¦æŸ - - Refresh index list - table window - 刷新索引列表 + + Add table check constraint + table window + æ·»åŠ è¡¨æ£€æŸ¥çº¦æŸ - - Create index - table window - 创建索引 + + Refresh index list + table window + 刷新索引列表 - - Edit index - table window - 编辑索引 + + + Create index + table window + 创建索引 - - Delete index - table window - 删除索引 + + Edit index + table window + 编辑索引 - - Refresh trigger list - table window - 刷新触å‘器列表 + + Delete index + table window + 删除索引 - - Create trigger - table window - 创建触å‘器 + + Refresh trigger list + table window + 刷新触å‘器列表 - - Edit trigger - table window - 编辑触å‘器 + + + Create trigger + table window + 创建触å‘器 - - Delete trigger - table window - 删除触å‘器 + + Edit trigger + table window + 编辑触å‘器 - - Are you sure you want to delete column '%1'? - table window - 您确定è¦åˆ é™¤å­—段“%1â€å—? + + Delete trigger + table window + 删除触å‘器 - - Following problems will take place while modifying the table. + + Are you sure you want to delete column '%1'? + table window + 您确定è¦åˆ é™¤å­—段“%1â€å—? + + + + Following problems will take place while modifying the table. Would you like to proceed? - table window - + table window + 修改该表时将出现以下问题。 +是å¦ç»§ç»­ï¼Ÿ - - Table modification - table window - + + Table modification + table window + 修改表 - - Could not load data for table %1. Error details: %2 - 无法加载表 %1 的数æ®ã€‚错误详情:%2 + + Could not load data for table %1. Error details: %2 + 无法加载表 %1 的数æ®ã€‚错误详情:%2 - - Could not process the %1 table correctly. Unable to open a table window. - 无法正确处ç†è¡¨ %1。无法打开表窗å£ã€‚ + + Could not process the %1 table correctly. Unable to open a table window. + 无法正确处ç†è¡¨ %1。无法打开表窗å£ã€‚ - - Could not restore window %1, because no database or table was stored in session for this window. - + + Database + æ•°æ®åº“ - - Could not restore window '%1', because no database or table was stored in session for this window. - + + Could not restore window %1, because no database or table was stored in session for this window. + æ— æ³•è¿˜åŽŸçª—å£ %1,此窗å£ä¸­æ²¡æœ‰å­˜å‚¨æ•°æ®åº“或表的会è¯ã€‚ - - Could not restore window '%1', because database %2 could not be resolved. - + + Could not restore window '%1', because no database or table was stored in session for this window. + 无法还原窗å£â€œ%1â€ï¼Œæ­¤çª—å£ä¸­æ²¡æœ‰å­˜å‚¨æ•°æ®åº“或表的会è¯ã€‚ - - Could not restore window '%1'', because the table %2 doesn't exist in the database %3. - + + Could not restore window '%1', because database %2 could not be resolved. + 无法还原窗å£â€œ%1â€ï¼Œæ— æ³•è§£æžæ•°æ®åº“ %2。 - - - New table %1 - 新表 %1 + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + 无法还原窗å£â€œ%1â€ï¼Œæ•°æ®åº“ %3 中ä¸å­˜åœ¨è¡¨ %2。 - - Committed changes for table '%1' successfully. - æˆåŠŸæäº¤è¡¨ '%1' 的修改。 + + + New table %1 + 新表 %1 - - Committed changes for table '%1' (named before '%2') successfully. - + + Committed changes for table '%1' successfully. + æˆåŠŸæäº¤è¡¨ '%1' 的修改。 - - Autoincrement value for table '%1' has been reset successfully. - 表“%1â€çš„auincrementé‡è®¾æˆåŠŸã€‚ + + Committed changes for table '%1' (named before '%2') successfully. + æˆåŠŸæäº¤å¯¹è¡¨ '%1'(原å '%2')的更改。 - - Uncommitted changes - 未æäº¤çš„æ›´æ”¹ + + Could not commit table structure. Error message: %1 + table window + 无法æäº¤è¡¨ç»“构。错误信æ¯ï¼š%1 - - There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - + + Reset autoincrement + é‡ç½®è‡ªåŠ¨é€’å¢ž - - Table window "%1" has uncommitted structure modifications and data. - 表窗å£â€œ%1â€æœ‰æœªæäº¤çš„结构更改与数æ®ã€‚ + + Are you sure you want to reset autoincrement value for table '%1'? + 您确定è¦é‡ç½®è¡¨â€œ%1â€çš„自动递增值å—? - - Table window "%1" has uncommitted data. - 表窗å£â€œ%1â€æœ‰æœªæäº¤çš„æ•°æ®ã€‚ + + An error occurred while trying to reset autoincrement value for table '%1': %2 + é‡ç½®è¡¨â€œ%1â€çš„自动递增值时出错:%2 - - Table window "%1" has uncommitted structure modifications. - 表窗å£â€œ%1â€æœ‰æœªæäº¤çš„结构更改。 + + Autoincrement value for table '%1' has been reset successfully. + æˆåŠŸé‡ç½®è¡¨â€œ%1â€çš„自动递增值。 - - Could not commit table structure. Error message: %1 - table window - 无法æäº¤è¡¨ç»“构。错误信æ¯ï¼š%1 + + Empty name + 空åç§° - - Reset autoincrement - é‡ç½®autoincrement + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + SQLite å…许为表使用空白åç§°ï¼Œä½†ä¸æŽ¨è使用空白å称。 +您确定è¦åˆ›å»ºä¸€ä¸ªç©ºç™½å称的表? - - Are you sure you want to reset autoincrement value for table '%1'? - 您确定è¦é‡è®¾â€œ%1â€çš„autoincrementå—? + + Cannot create a table without at least one column. + 无法创建没有任何列的表。 - - An error occurred while trying to reset autoincrement value for table '%1': %2 - 在é‡è®¾è¡¨â€œ%1â€çš„autoincrement时出现错误:%2 + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + 无法创建表 %1ï¼Œæ²¡æœ‰å®šä¹‰ä¸»é”®ã€‚å–æ¶ˆé€‰ä¸­ %2 或者定义一个主键。 - Autoincrement value for table '%1' has been reset successfly. - 表“%1â€çš„auincrementé‡è®¾æˆåŠŸã€‚ + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + 使用 %1 è¯­å¥æ—¶æ— æ³•ä¸ºä¸»é”®ä½¿ç”¨è‡ªåŠ¨é€’å¢žã€‚å–æ¶ˆé€‰ä¸­ %2,或者将一个主键设为自动递增。 - - Empty name - + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + 以下列使用了ä¸ä¸¥æ ¼çš„æ•°æ®ç±»åž‹ï¼š%1。请ç¦ç”¨æ­¤è¡¨çš„严格模å¼ï¼Œæˆ–者修改列数æ®ç±»åž‹ã€‚有效的严格数æ®ç±»åž‹ä¸ºï¼š%2 - - A blank name for the table is allowed in SQLite, but it is not recommended. -Are you sure you want to create a table with blank name? - + + Are you sure you want to delete table constraint '%1'? + table window + 您确定è¦åˆ é™¤è¡¨çº¦æŸâ€œ%1â€å—? - - Cannot create a table without at least one column. - 无法创建没有任何字段的表。 + + Delete constraint + table window + åˆ é™¤çº¦æŸ - - Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. - + + Cannot export, because no export plugin is loaded. + 无法导出,没有加载导出æ’件。 - - Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. - + + Cannot import, because no import plugin is loaded. + 无法导入,没有加载导入æ’件。 - - Are you sure you want to delete table constraint '%1'? - table window - 您确定è¦åˆ é™¤è¡¨çº¦æŸâ€œ%1â€å—? + + Uncommitted changes + 未æäº¤çš„æ›´æ”¹ - - Delete constraint - table window - åˆ é™¤çº¦æŸ + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + 有未æäº¤çš„结构修改。您ä¸èƒ½æµè§ˆæˆ–编辑数æ®ï¼Œç›´è‡³è¡¨ç»“构完工。 +ç«‹å³æäº¤çŽ°æœ‰ç»“æž„ï¼Ÿæˆ–è€…è¿”å›žç»“æž„é€‰é¡¹å¡ï¼Ÿ - - Cannot export, because no export plugin is loaded. - 未能导出,因为没有导出æ’件被加载。 + + Go back to structure tab + è¿”å›žç»“æž„é€‰é¡¹å¡ - - Cannot import, because no import plugin is loaded. - 未能导入,因为没有导入æ’件被加载。 + + Commit modifications and browse data. + æäº¤ä¿®æ”¹å¹¶æµè§ˆæ•°æ®ã€‚ - Uncommited changes - 未æäº¤çš„æ›´æ”¹ + + Name + table window indexes + åç§° - - Go back to structure tab - åæ±‡ç»“æž„é€‰é¡¹å¡ + + Unique + table window indexes + 唯一 - - Commit modifications and browse data. - æäº¤ä¿®æ”¹å¹¶æµè§ˆæ•°æ®ã€‚ + + Columns + table window indexes + 列 - - Name - table window indexes - åç§° + + Partial index condition + table window indexes + 部分索引æ¡ä»¶ - - Unique - table window indexes - 唯一 + + Name + table window triggers + åç§° - - Columns - table window indexes - 字段 + + Event + table window triggers + 事件 - - Partial index condition - table window indexes - + + Condition + table window triggers + æ¡ä»¶ - - Name - table window triggers - åç§° + + Details + table window triggers + 详情 - - Event - table window triggers - 事件 + + Table window "%1" has uncommitted structure modifications and data. + 表窗å£â€œ%1â€æœ‰æœªæäº¤çš„结构更改与数æ®ã€‚ - - Condition - table window triggers - æ¡ä»¶ + + Table window "%1" has uncommitted data. + 表窗å£â€œ%1â€æœ‰æœªæäº¤çš„æ•°æ®ã€‚ - - Details - table window triggers - 详情 + + Table window "%1" has uncommitted structure modifications. + 表窗å£â€œ%1â€æœ‰æœªæäº¤çš„结构更改。 - - + + TriggerColumnsDialog - - Trigger columns - + + Trigger columns + 触å‘器列 - - Triggering columns: - + + Triggering columns: + 触å‘列: - - Select all - 全选 + + Select all + 全选 - - Deselect all - å…¨ä¸é€‰ + + Deselect all + å…¨ä¸é€‰ - - + + TriggerDialog - - - Trigger - 触å‘器 + + + Trigger + 触å‘器 - - On table: - 在表: + + On table: + 表: - - Action: - æ“作: + + Action: + 动作: - - - <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> - + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>用æ¥åˆ¤æ–­è§¦å‘å™¨ä»£ç æ˜¯å¦æ‰§è¡Œçš„ SQL æ¡ä»¶è¡¨è¾¾å¼ã€‚如果æ¡ä»¶è¿”回 false,则触å‘器ä¸ä¼šå¯¹è¯¥è¡Œç”Ÿæ•ˆã€‚</p> - - Pre-condition: - 剿æ¡ä»¶ï¼š + + Pre-condition: + 剿æ¡ä»¶ï¼š - - The scope is still not fully supported by the SQLite database. - ä½œç”¨åŸŸä»æ²¡æœ‰è¢« SQLite æ•°æ®åº“完整支æŒã€‚ + + The scope is still not fully supported by the SQLite database. + SQLite æ•°æ®åº“仿œªå®Œå…¨æ”¯æŒä½œç”¨åŸŸã€‚ - - Trigger name: - 触å‘器å称: + + Trigger name: + 触å‘器å称: - - When: - 当: + + When: + 当: - - List of columns for UPDATE OF action. - + + List of columns for UPDATE OF action. + UPDATE OF 动作的列列表 - - Scope: - 作用域: + + Scope: + 作用域: - - Code: - 代ç ï¼š + + Code: + 代ç ï¼š - - Trigger statements to be executed. - + + Trigger statements to be executed. + éœ€è¦æ‰§è¡Œçš„触å‘å™¨è¯­å¥ - - DDL - DDL + + DDL + DDL - - On view: - 在视图: + + On view: + 视图: - - Could not process trigger %1 correctly. Unable to open a trigger dialog. - + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + 无法正确处ç†è§¦å‘器 %1。无法打开触å‘å™¨å¯¹è¯æ¡†ã€‚ - - Enter a valid condition. - è¾“å…¥ä¸€ä¸ªåˆæ³•çš„æ¡ä»¶ã€‚ + + Enter a valid condition. + 请输入一个有效的æ¡ä»¶ã€‚ - - Enter a valid trigger code. - è¾“å…¥åˆæ³•的触å‘器代ç ã€‚ + + Enter a valid trigger code. + 请输入一个有效的触å‘器代ç ã€‚ - - Error - trigger dialog - 错误 + + Error + trigger dialog + 错误 - - An error occurred while executing SQL statements: + + An error occurred while executing SQL statements: %1 - 在执行SQL语å¥â€œ%1â€æ—¶å‘生了错误 + 执行下列 SQL è¯­å¥æ—¶å‡ºé”™ï¼š +%1 - - + + VersionConvertSummaryDialog - - Database version convert - æ•°æ®åº“ç‰ˆæœ¬è½¬æ¢ + + Database version convert + æ•°æ®åº“ç‰ˆæœ¬è½¬æ¢ - - Following changes to the SQL statements will be made: - + + Following changes to the SQL statements will be made: + SQL 语å¥å˜æ›´å¦‚下: - - Before - ä¹‹å‰ + + Before + ä¹‹å‰ - - After - ä¹‹åŽ + + After + ä¹‹åŽ - - + + ViewWindow - - Query - + + Query + 查询 - - View name: - 视图å称: + + View name: + 视图å称: - - Output column names - 输出字段åç§° + + Output column names + 输出列åç§° - - - Data - æ•°æ® + + + Data + æ•°æ® - - Triggers - 触å‘器 + + Triggers + 触å‘器 - - DDL - DDL + + DDL + DDL - - - Could not restore window '%1', because no database or view was stored in session for this window. - + + + Could not restore window '%1', because no database or view was stored in session for this window. + 无法还原窗å£â€œ%1â€ï¼Œæ­¤çª—å£ä¸­æ²¡æœ‰å­˜å‚¨æ•°æ®åº“或表的会è¯ã€‚ - - Could not restore window '%1', because database %2 could not be resolved. - + + Could not restore window '%1', because database %2 could not be resolved. + 无法还原窗å£â€œ%1â€ï¼Œæ— æ³•è§£æžæ•°æ®åº“ %2。 - - Could not restore window '%1', because database %2 could not be open. - 无法æ¢å¤çª—å£â€œ%1â€ï¼Œå› ä¸ºæ•°æ®åº“ %2 没有被打开。 + + Could not restore window '%1', because database %2 could not be open. + 无法还原窗å£â€œ%1â€ï¼Œæ•°æ®åº“ %2 没有被打开。 - - Could not restore window '%1', because the view %2 doesn't exist in the database %3. - 无法æ¢å¤çª—å£â€œ%1â€ï¼Œå› ä¸ºè§†å›¾ %2 ä¸å­˜åœ¨äºŽæ•°æ®åº“ %3 中。 + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + 无法还原窗å£â€œ%1â€ï¼Œæ•°æ®åº“ %3 中ä¸å­˜åœ¨è§†å›¾ %2。 - - - New view %1 - 新视图 %1 + + + New view %1 + 新视图 %1 - - Refresh the view - view window - 刷新视图 + + Database + æ•°æ®åº“ - - Commit the view changes - view window - æäº¤è§†å›¾æ›´æ”¹ + + Refresh the view + view window + 刷新视图 - - Rollback the view changes - view window - å›žæ»šè§†å›¾æ”¹å˜ + + Commit the view changes + view window + æäº¤è§†å›¾æ›´æ”¹ - - Explicit column names - + + Rollback the view changes + view window + 回滚视图更改 - - Generate output column names automatically basing on result columns of the view. - + + Explicit column names + 明确列åç§° - - Add column - view window - 添加字段 + + Generate output column names automatically basing on result columns of the view. + 基于视图的结果列自动生æˆè¾“出列å称。 - - Edit column - view window - 编辑字段 + + Add column + view window + 添加列 - - Delete column - view window - 删除字段 + + Edit column + view window + 编辑列 - - Move column up - view window - å‘上移动字段 + + Delete column + view window + 删除列 - - Move column down - view window - å‘下移动字段 + + Move column up + view window + 上移列 - - Refresh trigger list - view window - 刷新触å‘器列表 + + Move column down + view window + 下移列 - - Create new trigger - view window - 创建新触å‘器 + + Refresh trigger list + view window + 刷新触å‘器列表 - - Edit selected trigger - view window - 编辑选中的触å‘器 + + Create new trigger + view window + 创建新触å‘器 - - Delete selected trigger - view window - 删除选中的触å‘器 + + Edit selected trigger + view window + 编辑选中触å‘器 - - View window "%1" has uncommitted structure modifications and data. - 视图“%1â€æœ‰æœªæäº¤çš„结构更改和数æ®ã€‚ + + Delete selected trigger + view window + 删除选中触å‘器 - - View window "%1" has uncommitted data. - 视图“%1â€æœ‰æœªæäº¤çš„æ•°æ®ã€‚ + + View window "%1" has uncommitted structure modifications and data. + 视图“%1â€æœ‰æœªæäº¤çš„结构更改和数æ®ã€‚ - - View window "%1" has uncommitted structure modifications. - 视图“%1â€æœ‰æœªæäº¤çš„结构更改。 + + View window "%1" has uncommitted data. + 视图“%1â€æœ‰æœªæäº¤çš„æ•°æ®ã€‚ - - Uncommitted changes - 未æäº¤çš„æ›´æ”¹ + + View window "%1" has uncommitted structure modifications. + 视图“%1â€æœ‰æœªæäº¤çš„结构更改。 - - There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. -Do you want to commit the structure, or do you want to go back to the structure tab? - + + Could not load data for view %1. Error details: %2 + 无法加载视图 %1 的数æ®ã€‚错误详情:%2 - - Committed changes for view '%1' successfully. - æˆåŠŸæäº¤è§†å›¾â€œ%1â€çš„æ›´æ”¹ã€‚ + + Uncommitted changes + 未æäº¤çš„æ›´æ”¹ + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + 有未æäº¤çš„结构修改。您ä¸èƒ½æµè§ˆæˆ–编辑数æ®ï¼Œç›´è‡³è¡¨ç»“构完工。 +ç«‹å³æäº¤çŽ°æœ‰ç»“æž„ï¼Ÿæˆ–è€…è¿”å›žç»“æž„é€‰é¡¹å¡ï¼Ÿ - - Committed changes for view '%1' (named before '%2') successfully. - + + Go back to structure tab + è¿”å›žç»“æž„é€‰é¡¹å¡ - - Could not load data for view %1. Error details: %2 - 无法加载视图 %1 的数æ®ã€‚错误详情:%2 + + Commit modifications and browse data. + æäº¤æ›´æ”¹å¹¶æµè§ˆæ•°æ®ã€‚ - Uncommited changes - 未æäº¤çš„æ›´æ”¹ + + View '%1' was committed successfully. + æˆåŠŸæäº¤è§†å›¾â€œ%1â€ã€‚ - - Go back to structure tab - å›žåˆ°ç»“æž„é€‰é¡¹å¡ + + Committed changes for view '%1' successfully. + æˆåŠŸæäº¤å¯¹è§†å›¾â€œ%1â€çš„æ›´æ”¹ã€‚ - - Commit modifications and browse data. - æäº¤æ›´æ”¹å¹¶æµè§ˆæ•°æ®ã€‚ + + Committed changes for view '%1' (named before '%2') successfully. + æˆåŠŸæäº¤å¯¹è§†å›¾â€œ%1â€ï¼ˆåŽŸå“%2â€ï¼‰çš„æ›´æ”¹ã€‚ - - Could not commit view changes. Error message: %1 - view window - 无法æäº¤è§†å›¾æ›´æ”¹ã€‚错误信æ¯ï¼š%1 + + Could not commit view changes. Error message: %1 + view window + 无法æäº¤è§†å›¾æ›´æ”¹ã€‚错误信æ¯ï¼š%1 - - Override columns - + + Override columns + 覆盖列 - - Currently defined columns will be overriden. Do you want to continue? - 当å‰å®šä¹‰çš„字段将会被覆写,您è¦ç»§ç»­å—? + + Currently defined columns will be overriden. Do you want to continue? + 当å‰å®šä¹‰çš„列将被覆盖。您è¦ç»§ç»­å—? - - Could not determinate columns returned from the view. The query is problably incomplete or contains errors. - + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + 无法确定视图所返回的列。查询å¯èƒ½ä¸å®Œæ•´æˆ–包å«é”™è¯¯ã€‚ - - Name - view window triggers - åç§° + + Name + view window triggers + åç§° - - Instead of - view window triggers - + + Instead of + view window triggers + è€Œéž - - Condition - view window triggers - æ¡ä»¶ + + Condition + view window triggers + æ¡ä»¶ - - Details - table window triggers - 详情 + + Details + table window triggers + 详情 - - Could not process the %1 view correctly. Unable to open a view window. - 无法正确处ç†è§†å›¾ %1。无法打开视图窗å£ã€‚ + + Could not process the %1 view correctly. Unable to open a view window. + 无法正确处ç†è§†å›¾ %1。无法打开视图窗å£ã€‚ - - Empty name - + + Empty name + 空åç§° - - A blank name for the view is allowed in SQLite, but it is not recommended. + + A blank name for the view is allowed in SQLite, but it is not recommended. Are you sure you want to create a view with blank name? - + SQLite å…许为视图使用空白åç§°ï¼Œä½†ä¸æŽ¨è使用空白å称。 +您确定è¦åˆ›å»ºä¸€ä¸ªç©ºç™½å称的视图? - - The SELECT statement could not be parsed. Please correct the query and retry. + + The SELECT statement could not be parsed. Please correct the query and retry. Details: %1 - + SELECT 语å¥è§£æžå¤±è´¥ã€‚请更正查询并é‡è¯•。 +详情:%1 - - The view could not be modified due to internal SQLiteStudio error. Please report this! - + + The view could not be modified due to internal SQLiteStudio error. Please report this! + å›  SQLiteStudio å†…éƒ¨é”™è¯¯ï¼Œæ— æ³•ä¿®æ”¹è¯¥è§†å›¾ã€‚è¯·æŠ¥å‘Šè¯¥é—®é¢˜ï¼ - - The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. - + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + ç”¨äºŽæ‰§è¡Œçš„è§†å›¾ä»£ç æ— æ³•正确解æžã€‚这是 SQLiteStudio çš„ bug,请报告。 - - Following problems will take place while modifying the view. + + Following problems will take place while modifying the view. Would you like to proceed? - view window - + view window + 修改该视图时将出现以下问题。 +是å¦ç»§ç»­ï¼Ÿ - - View modification - view window - 视图更改 + + View modification + view window + 视图更改 - - + + WidgetCover - - Interrupt - 中断 + + Interrupt + åœæ­¢ - + diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_TW.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_TW.ts new file mode 100644 index 0000000..015cb4c --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_TW.ts @@ -0,0 +1,7106 @@ + + + + + AboutDialog + + + About SQLiteStudio and licenses + 關於 SQLiteStudio 和許å¯å”è­° + + + + About + 關於 + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">自由ã€é–‹æºã€è·¨å¹³è‡ºçš„ SQLite 資料庫管ç†å·¥å…·ã€‚<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">作者和活èºç¶­è­·äººï¼š<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> + + + + Licenses + 許å¯å”è­° + + + + Environment + 環境 + + + + Icon directories + 圖示目錄 + + + + Form directories + 表單目錄 + + + + SQLite extension directories + SQLite extension directories + + + + Plugin directories + 外掛目錄 + + + + Configuration directory + 設定檔檔案目錄 + + + + Application directory + 應用程å¼ç›®éŒ„ + + + + Qt version: + Qt 版本: + + + + SQLite 3 version: + SQLite 3 版本: + + + + Portable distribution. + 便攜版。 + + + + MacOS X application bundle distribution. + MacOS X application bundle distribution. + + + + Operating system managed distribution. + 系統æä¾›ç‰ˆã€‚ + + + + <h3>Table of contents:</h3><ol>%2</ol> + <h3>目錄:</h3><ol>%2</ol> + + + + BindParamsDialog + + + Query parameters + 查詢引數 + + + + Please provide values for query parameters + è«‹æä¾›ä¸€å€‹å€¼ä½œç‚ºæŸ¥è©¢å¼•數 + + + + CodeSnippetEditor + + + Filter snippets + Filter snippets + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + Snippet name + Snippet name + + + + Code assistant shortcut + Code assistant shortcut + + + + Snippet code + Snippet code + + + + Code Snippets editor window has uncommitted modifications. + Code Snippets editor window has uncommitted modifications. + + + + Code Snippets editor + Code Snippets editor + + + + Commit all snippet changes + Commit all snippet changes + + + + Rollback all snippet changes + Rollback all snippet changes + + + + Create new snippet + Create new snippet + + + + Delete selected snippet + Delete selected snippet + + + + Move the snippet up + Move the snippet up + + + + Move the snippet down + Move the snippet down + + + + Code snippets manual + Code snippets manual + + + + Enter a non-empty, unique name of the snippet. + Enter a non-empty, unique name of the snippet. + + + + Enter a non-empty snippet content. + Enter a non-empty snippet content. + + + + This hotkey is not unique in context of a code assistant. + This hotkey is not unique in context of a code assistant. + + + + CollationsEditor + + + Filter collations + 篩é¸å­—å…ƒåº + + + + Databases + 資料庫 + + + + Register in all databases + 在所有資料庫中註冊 + + + + Register in following databases: + 在下列資料庫中註冊: + + + + Implementation code: + 實ç¾ç¨‹å¼ç¢¼ï¼š + + + + Collation name: + å­—å…ƒåºå稱: + + + + Implementation language: + 實ç¾èªžè¨€ï¼š + + + + Collations editor + å­—å…ƒåºç·¨è¼¯å™¨ + + + + Commit all collation changes + æäº¤å…¨éƒ¨å­—å…ƒåºæ›´æ”¹ + + + + Rollback all collation changes + å›žæ»¾æ‰€æœ‰å­—å…ƒåºæ›´æ”¹ + + + + Create new collation + å»ºç«‹æ–°å­—å…ƒåº + + + + Delete selected collation + 刪除é¸ä¸­å­—å…ƒåº + + + + Editing collations manual + æ‰‹å‹•ç·¨è¼¯å­—å…ƒåº + + + + Enter a non-empty, unique name of the collation. + 請為字元åºè¼¸å…¥ä¸€å€‹éžç©ºä¸”唯一的å稱。 + + + + Pick the implementation language. + 鏿“‡å¯¦ç¾èªžè¨€ã€‚ + + + + Enter a non-empty implementation code. + 請輸入éžç©ºçš„實ç¾ç¨‹å¼ç¢¼ã€‚ + + + + Collations editor window has uncommitted modifications. + å­—å…ƒåºç·¨è¼¯å™¨æœ‰æœªæäº¤çš„修改。 + + + + ColorButton + + + Pick a color + 鏿“‡ä¸€ç¨®é¡è‰² + + + + ColumnCollatePanel + + + Collation name: + å­—å…ƒåºå稱: + + + + Named constraint: + 命åç´„æŸï¼š + + + + Enter a name of the constraint. + 輸入約æŸå稱。 + + + + Enter a collation name. + 輸入字元åºå稱。 + + + + ColumnDefaultPanel + + + Default value: + é è¨­å€¼ï¼š + + + + Named constraint: + 命åç´„æŸï¼š + + + + Enter a default value expression. + 輸入é è¨­å€¼è¡¨ç¤ºå¼ã€‚ + + + + Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. + 無效的é è¨­å€¼è¡¨ç¤ºå¼ï¼š%1。如果你想使用簡單的字串作為值,記得用引號將其框起來。 + + + + Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. + 無效的é è¨­å€¼è¡¨ç¤ºå¼ã€‚如果你想使用簡單的字串作為值,記得用引號將其框起來。 + + + + Enter a name of the constraint. + 輸入約æŸå: + + + + ColumnDialog + + + Column + 列 + + + + Name and type + å稱和型別 + + + + Scale + å°æ•¸ä½æ•¸ + + + + Precision + 精度 + + + + Data type: + 資料型別: + + + + Column name: + 列å: + + + + Size: + 大å°ï¼š + + + + Constraints + ç´„æŸ + + + + Generated value + 生æˆçš„值 + + + + Unique + 唯一 + + + + + + + + + + + Configure + 設定檔 + + + + Foreign Key + å¤–ä¾†éµ + + + + Collate + å­—å…ƒåº + + + + Not NULL + éžç©º + + + + Check condition + æ¢ä»¶ + + + + Primary Key + ä¸»éµ + + + + Default + é è¨­ + + + + Advanced mode + é«˜éšŽæ¨¡å¼ + + + + Add constraint + column dialog + æ–°å¢žç´„æŸ + + + + Edit constraint + column dialog + ç·¨è¼¯ç´„æŸ + + + + + Delete constraint + column dialog + åˆªé™¤ç´„æŸ + + + + Move constraint up + column dialog + ä¸Šç§»ç´„æŸ + + + + Move constraint down + column dialog + ä¸‹ç§»ç´„æŸ + + + + Add a primary key + column dialog + æ–°å¢žä¸»éµ + + + + Add a foreign key + column dialog + æ–°å¢žå¤–ä¾†éµ + + + + Add an unique constraint + column dialog + æ–°å¢žå”¯ä¸€ç´„æŸ + + + + Add a check constraint + column dialog + 新增æ¢ä»¶ç´„æŸ + + + + Add a not null constraint + column dialog + 新增éžç©ºç´„æŸ + + + + Add a collate constraint + column dialog + 新增字元åºç´„æŸ + + + + Add a generated value constraint + column dialog + 新增生æˆçš„å€¼ç´„æŸ + + + + Add a default constraint + column dialog + 新增é è¨­ç´„æŸ + + + + Are you sure you want to delete constraint '%1'? + column dialog + 確定è¦åˆªé™¤ç´„æŸâ€œ%1â€å—Žï¼Ÿ + + + + Correct the constraint's configuration. + 請糾正約æŸè¨­å®šæª”。 + + + + Scale is not allowed for INTEGER PRIMARY KEY columns. + INTEGER PRIMARY KEY 列中ä¸å…è¨±å°æ•¸ä½æ•¸ã€‚ + + + + Precision cannot be defined without the scale. + æœ‰å°æ•¸ä½æ•¸æ‰èƒ½å®šç¾©ç²¾åº¦ã€‚ + + + + Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. + ä¸»éµ (PRIMARY KEY) 已啟用自動éžå¢ž (AUTOINCREMENT),ä¸èƒ½ä½¿ç”¨ INTEGER 以外的型別。 + + + + INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. + ä¸»éµ (PRIMARY KEY) 已啟用自動éžå¢ž (AUTOINCREMENT),強制使用 INTEGER 型別。 + + + + Precision is not allowed for INTEGER PRIMARY KEY columns. + ä¸å…è¨±å°æ•´åž‹ä¸»éµ (INTEGER PRIMARY KEY) 設定精度。 + + + + Could not match valid STRICT table datatype from declared type: %1. + Could not match valid STRICT table datatype from declared type: %1. + + + + ColumnDialogConstraintsModel + + + Type + column dialog constraints + 型別 + + + + Name + column dialog constraints + å稱 + + + + Details + column dialog constraints + 詳情 + + + + ColumnForeignKeyPanel + + + Foreign table: + 外部表: + + + + Foreign column: + 外部欄ä½ï¼š + + + + Reactions + 響應 + + + + Deferred foreign key + å»¶é²å¤–來éµç´„æŸ + + + + Named constraint + 命åçš„ç´„æŸ + + + + Constraint name + ç´„æŸå稱 + + + + Pick the foreign table. + 鏿“‡ä¸€å€‹å¤–部表。 + + + + Pick the foreign column. + 鏿“‡ä¸€å€‹å¤–部欄ä½ã€‚ + + + + Enter a name of the constraint. + 輸入約æŸå稱 + + + + ColumnGeneratedPanel + + + Generating code: + 生æˆç¨‹å¼ç¢¼ï¼š + + + + Explicit type: + 顯å¼åž‹åˆ¥ï¼š + + + + Use "GENERATED ALWAYS" keywords + 使用 "GENERATED ALWAYS" é—œéµå­— + + + + Named constraint: + 命å的約æŸï¼š + + + + Enter the column value generating expression. + 請輸入列值生æˆè¡¨ç¤ºå¼ã€‚ + + + + Invalid value generating expression: %1. + 無效的值生æˆè¡¨ç¤ºå¼ï¼š%1。 + + + + Invalid value generating expression. + 無效的值生æˆè¡¨ç¤ºå¼ã€‚ + + + + Enter a name of the constraint. + 輸入約æŸå稱。 + + + + ColumnPrimaryKeyPanel + + + Autoincrement + 自動éžå¢ž + + + + Sort order: + 排åºï¼š + + + + Named constraint: + 命å的約æŸï¼š + + + + On conflict: + ç•¶è¡çªæ™‚: + + + + Enter a name of the constraint. + 請輸入約æŸå稱。 + + + + Descending order is not allowed with AUTOINCREMENT. + 自增ä¸å…許é™åºã€‚ + + + + ColumnUniqueAndNotNullPanel + + + Named constraint: + 命å的約æŸï¼š + + + + On conflict: + ç•¶è¡çªæ™‚: + + + + Enter a name of the constraint. + 請輸入約æŸå稱。 + + + + CompleterWindow + + + Column: %1 + completer statusbar + 列:%1 + + + + Table: %1 + completer statusbar + 表:%1 + + + + Index: %1 + completer statusbar + 索引:%1 + + + + Trigger: %1 + completer statusbar + 觸發器:%1 + + + + View: %1 + completer statusbar + 檢視:%1 + + + + Database: %1 + completer statusbar + 資料庫:%1 + + + + Keyword: %1 + completer statusbar + é—œéµå­—:%1 + + + + Function: %1 + completer statusbar + 函å¼ï¼š%1 + + + + Operator: %1 + completer statusbar + é‹ç®—å­ï¼š%1 + + + + String + completer statusbar + 字串 + + + + Number + completer statusbar + 數值 + + + + Binary data + completer statusbar + 二進ä½åˆ¶è³‡æ–™ + + + + Collation: %1 + completer statusbar + å­—å…ƒåºï¼š%1 + + + + Pragma function: %1 + completer statusbar + Pragma 函å¼ï¼š%1 + + + + Insert a code snippet + Insert a code snippet + + + + ConfigDialog + + + + Configuration + 設定檔 + + + + Search + æœå°‹ + + + + General + 通用 + + + + Keyboard shortcuts + å¿«æ·éµ + + + + Look & feel + 外觀 + + + + Style + 風格 + + + + Fonts + å­—åž‹ + + + + Code colors + Code colors + + + + + Database list + 資料庫清單 + + + + Code assistant + Code assistant + + + + Data browsing + ç€è¦½è³‡æ–™ + + + + Data editors + 資料編輯器 + + + + Plugins + 外掛 + + + + Code formatters + 程å¼ç¢¼æ ¼å¼åŒ–器 + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + 如果關閉,列將按照 CREATE TABLE ä¸­çš„é †åºæŽ’åºã€‚ + + + + Sort table columns alphabetically + 按字æ¯é †åºåˆ—出表的列 + + + + Expand tables node when connected to a database + 連線到資料庫時展開表節點 + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + <p>附加標籤是顯示在資料庫清單的å稱æ—邊的文字標籤 (除éžå¦æœ‰è¨­å®šæª”,å¦å‰‡ç‚ºè—色)。啟用此é¸é …將為資料庫ã€ç„¡æ•ˆè³‡æ–™åº«å’Œèšåˆç¯€é»ž (列組ã€ç´¢å¼•組ã€è§¸ç™¼å™¨çµ„) 顯示標籤。更多標籤見下方é¸é …。<p> + + + + Display additional labels on the list + 在清單中顯示附加標籤 + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + å°æ–¼æ™®é€šè¡¨ï¼Œæ¨™ç±¤å°‡é¡¯ç¤ºæ¯å€‹è¡¨çš„列ã€ç´¢å¼•和觸發器的數é‡ã€‚ + + + + Display labels for regular tables + 為普通表顯示標籤 + + + + Virtual tables will be marked with a 'virtual' label. + è™›æ“¬è¡¨å°‡é™„ä»¥â€œè™›æ“¬â€æ¨™ç±¤ã€‚ + + + + Display labels for virtual tables + 為虛擬表顯示標籤 + + + + Expand views node when connected to a database + 連線到資料庫時展開檢視節點 + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + 如果關閉此é¸é …,則物件將按照 sqlite_master è¡¨ä¸­çš„é †åº (å³å®ƒå€‘被建立的順åº) 排列 + + + + Sort objects (tables, indexes, triggers and views) alphabetically + 按字æ¯é †åºæŽ’列物件 (表ã€ç´¢å¼•ã€è§¸ç™¼å™¨åŠæª¢è¦–) + + + + Display system tables and indexes on the list + 在清單中顯示系統表和索引 + + + + Database dialog window + 資料庫å°è©±æ–¹å¡Šè¦–窗 + + + + <p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p> + <p>新增新的資料庫時,é è¨­æœƒå°‡å…¶æ¨™è¨˜ç‚ºâ€œæ°¸ä¹…â€(儲存在設定檔檔案中)。é¸ä¸­æ­¤é¸é …則新新增的資料庫將é è¨­ä¸æ¨™è¨˜ç‚ºâ€œæ°¸ä¹…â€ã€‚</p> + + + + Do not mark database to be "permanent" by default + é è¨­ä¸å°‡æ–°æ–°å¢žçš„資料庫標為“永久†+ + + + <p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p> + <p>啟用此é¸é …後,將檔案從檔案管ç†å™¨æ‹–æ”¾åˆ°è³‡æ–™åº«æ¸…å–®ä¸­æ™‚ï¼Œå°‡è‡ªå‹•ç¹žéŽæ¨™æº–的資料庫å°è©±æ–¹å¡Šï¼Œç›´æŽ¥æ–°å¢žåˆ°æ¸…單中。如果由於å„種原因自動處ç†å¤±æ•—,則還會å‘用戶顯示標準å°è©±æ–¹å¡Šã€‚</p> + + + + Try to bypass dialog completly when dropping database file onto the list + 拖放資料庫檔案到清單中時嘗試完全繞éŽå°è©±æ–¹å¡Š + + + + Data browsing and editing + ç€è¦½å’Œç·¨è¼¯è³‡æ–™ + + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + <p>在設定檔檔案中儲存,“表填充â€å°è©±æ–¹å¡Šä¸­çš„æœ€å¤§æ•¸é‡ã€‚值 100 應已足夠。</p> + + + + Number of memorized table populating configurations + 表填充設定檔中的é è¨­å¡«å……行數 + + + + Data column width + Data column width + + + + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + <html><head/><body><p>When user enters new value into column and the value is bigger than the current column width, the application will enlarge the column to fit the new value, but not wider than the limit defined in the option above.</p></body></html> + + + + Enlarge column when entering value longer than current width + Enlarge column when entering value longer than current width + + + + <p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p> + <p>當資料被載入到網格檢視時,列寬會自動調整。此值é™åˆ¶åˆå§‹åŒ–時的列寬度,之後您ä»å¯ä»¥æ‰‹å‹•調整列寬,ä¸å—æ­¤é™åˆ¶ã€‚</p> + + + + Number of data rows per page: + æ¯é è³‡æ–™è¡Œæ•¸ï¼š + + + + <p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p> + <p>此項啟用後,使用者滑鼠懸åœåœ¨ä»»æ„資料檢視 (æŸ¥è©¢çµæžœã€è¡¨è³‡æ–™ã€æª¢è¦–資料) 的單元格上時,工具æç¤ºå°‡é¡¯ç¤ºè©²å–®å…ƒæ ¼çš„詳細資訊——包括列資料型別ã€ç´„æŸã€ROWID 等。</p> + + + + Show column and row details tooltip in data view + 在資料檢視中展示列與行的詳細資訊 + + + + <p>When editing a cell which used to have NULL value and entering empty string as new value, then this option determinates whether the new value should remain NULL (have this option enabled), or should it be overwritten with empty string value (have this option disabled).</p> + <p>編輯一個éŽåŽ»ç‚º NULL 值的單元格且新輸入的值為空字串時,此é¸é …å•Ÿç”¨å‰‡è©²å–®å…ƒæ ¼çš„å€¼ä¿æŒ NULL ä¸è®Šï¼Œæ­¤é¸é …未啟用則空字串覆蓋原 NULL 值。</p> + + + + Keep NULL value when entering empty value + ç•¶è¼¸å…¥ç©ºå€¼æ™‚ä¿æŒ NULL 值 + + + + <html><head/><body><p>Enable this to always enforce DEFAULT value when committing a NULL value for a column that has DEFAULT value defined, even though the column is allowed to contain NULL values.</p><p>Disable this option to use DEFAULT value exclusively when NULL value is committed for column with NOT NULL constraint.</p></body></html> + <html><head/><body><p>啟用此é¸é …後,æäº¤ä¸€å€‹ NULL 值時,如果該列已定義 DEFAULT 值,å³ä½¿è©²åˆ—å…è¨±åŒ…å« NULL 值,也始終採用 DEFAULT 值。</p><p>ç¦ç”¨æ­¤é¸é …則僅在列有éžç©º (NOT NULL) ç´„æŸæ™‚å°‡ NULL 值轉變為 DEFAULT 值。</p></body></html> + + + + Use DEFAULT value (if defined), when committing NULL value + æäº¤ NULL 值時使用 DEFAULT 值 (如果已定義) + + + + <html><head/><body><p>If query results contain dozens (or hundreds) of columns, it is more likely that it will exhaust free memory of your computer by loading several gigabytes of data at once. SQLiteStudio may try to limit number of results displayed on one page in such cases to protect your computer. If you know that you don't work with big values in database, you can disable this limit and you will always see as many rows as defined per page.</p></body></html> + <html><head/><body><p>å¦‚æžœæŸ¥è©¢çµæžœåŒ…嫿•¸å乃至上百個列,則載入å¯èƒ½ä½”用數個 GB 的空閒記憶體。SQLiteStudio 在這種情æ³ä¸‹å¯èƒ½é™åˆ¶ä¸€é ä¸Šé¡¯ç¤ºçš„çµæžœæ•¸é‡ä»¥ä¿è­·æ‚¨çš„è¨ˆç®—æ©Ÿã€‚å¦‚æžœæ‚¨çž­è§£è‡ªå·±ä¸æœƒåœ¨å¦‚此大的資料庫上作業,則å¯ä»¥ç¦ç”¨æ­¤é™åˆ¶ä»¥å§‹çµ‚在一é ä¸Šçœ‹åˆ°å¤§é‡çš„行。</p></body></html> + + + + Limit number of rows for in case of dozens of columns + 列數éŽå¤šæ™‚é™åˆ¶æ¯é é¡¯ç¤ºçš„行數 + + + + Inserting new row in data grid + 網格檢視中æ’入新行時 + + + + Before currently selected row + 在é¸ä¸­è¡Œä¹‹å‰ + + + + After currently selected row + 在é¸ä¸­è¡Œä¹‹å¾Œ + + + + At the end of data view + 在資料檢視的末尾 + + + + Table windows + 表視窗 + + + + <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> + <p>啟用後,表視窗將顯示資料é¸é …å¡ï¼Œè€Œä¸æ˜¯çµæ§‹é¸é …å¡ã€‚</p> + + + + Open Table Windows with the data tab for start + 開啟表視窗時顯示“資料â€é¸é …å¡ + + + + <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> + <p>啟用後,“資料â€é¸é …å¡å°‡ä½œç‚ºç¬¬ä¸€å€‹é¸é …塿”¾ç½®åœ¨æ¯å€‹è¡¨è¦–çª—ä¸­ï¼Œè€Œä¸æ˜¯ä½æ–¼ç¬¬äºŒä½ã€‚</p> + + + + Place data tab as first tab in a Table Window + 將資料作為表視窗的第一項 + + + + View windows + 檢視視窗 + + + + <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> + <p>啟用後,檢視視窗將顯示資料é¸é …å¡ï¼Œè€Œä¸æ˜¯çµæ§‹é¸é …å¡ã€‚</p> + + + + Open View Windows with the data tab for start + 開啟檢視視窗時顯示“資料â€é¸é …å¡ + + + + <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> + <p>啟用後,“資料â€é¸é …å¡å°‡ä½œç‚ºç¬¬ä¸€å€‹é¸é …塿”¾ç½®åœ¨æ¯å€‹æª¢è¦–è¦–çª—ä¸­ï¼Œè€Œä¸æ˜¯ä½æ–¼ç¬¬äºŒå€‹ä½ç½®ã€‚</p> + + + + Place data tab as first tab in a View Window + 將“資料â€é¸é …塿”¾ç½®ç‚ºæª¢è¦–視窗的首個é¸é …å¡ + + + + Data types + 資料型別 + + + + Available editors: + å¯ç”¨çš„編輯器: + + + + Editors selected for this data type: + å·²ç‚ºè©²è³‡æ–™åž‹åˆ¥é¸æ“‡çš„編輯器: + + + + Schema editing + çµæ§‹ç·¨è¼¯ + + + + Number of DDL changes kept in history. + DDL 變更歷å²çš„記錄數é‡ã€‚ + + + + DDL history size: + DDL æ­·å²å¤§å°ï¼š + + + + Don't show DDL preview dialog when committing schema changes + æäº¤çµæ§‹æ›´æ”¹æ™‚ä¸é¡¯ç¤º DDL é è¦½å°è©±æ–¹å¡Š + + + + SQL queries + SQL 查詢 + + + + + Number of queries kept in the history. + 查詢歷å²è¨˜éŒ„數é‡ã€‚ + + + + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + <html><head/><body><p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute. You can also use dedicated shortcuts for executing in one mode or the other (currently configured to %1 for single query execution and %2 for all queries execution).</p></body></html> + + + + History size: + æ­·å²å¤§å°ï¼š + + + + + <p>Maximum number of query parameters (:param, @param, $param, ?) stored in history. When you re-use parameter with the same name/position, SQLiteStudio will pre-initialize it with most recent memorized value (you will still be able to change it). Value of 1000 should be sufficient.</p> + <p>æ­·å²è¨˜éŒ„中儲存查詢引數 (:param, @param, $param, ?) 的最大數é‡ã€‚ç•¶æ‚¨é‡æ–°åœ¨åŒä¸€å稱/ä½ç½®ä¸‹ä½¿ç”¨å¼•數時,SQLiteStudio 將使用最近記憶的值é å¡«å……åˆå§‹åŒ–它 (ä»å¯ä¿®æ”¹)。值 1000 應已足夠。</p> + + + + Execute only the query under the cursor + åªåŸ·è¡Œè¼¸å…¥ç¬¦æ‰€åœ¨è¡Œçš„èªžå¥ + + + + Number of memorized query parameters + é è¨­çš„æŸ¥è©¢å¼•æ•¸æ•¸é‡ + + + + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + <html><head/><body><p>By default (when this option is disabled) a real number is displayed in the format of decimals with decimal point. In some cases, when the number is really small (several places after decimal point), the default representation may appear inaccurate. In such case you may want to enable this option to use the scientific notation (i.e. <span style=" font-style:italic;">5.3e-21</span>).</p></body></html> + + + + Use scientific notation for real numbers in the grid view + Use scientific notation for real numbers in the grid view + + + + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + <html><head/><body><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit. This value is also used when enlarging column upon new, longer value entered by the user (see option below).</p></body></html> + + + + Limit automatic data column width to (in pixels): + Limit automatic data column width to (in pixels): + + + + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + <html><head/><body><p>Initial width of data columns will be set to at least show complete name of the column in the header. This can still be overwritten by the initial limit of column width specified in pixels (the setting above).</p></body></html> + + + + Keep at least the width to show complete column name + Keep at least the width to show complete column name + + + + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + <html><head/><body><p>If enabled, lines longer than the editor width will be wrapped, so horizontal scrolling will not be needed.</p></body></html> + + + + Wrap lines in SQL editor + Wrap lines in SQL editor + + + + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + <html><head/><body><p>Highlights entire query that is currently under the insertion cursor. It's the same query that will be executed when you hit &quot;Execute query&quot; hotkey or button (unless configured otherwise).</p></body></html> + + + + Highlight current query + Highlight current query + + + + Updates + æ›´æ–° + + + + Automatically check for updates at startup + 啟動時自動檢查更新 + + + + Session + 會話 + + + + Restore last session (active MDI windows) after startup + 啟動後æ¢å¾©ä¸Šä¸€æ¬¡æœƒè©± + + + + Allow multiple instances of the application at the same time + å…è¨±åŒæ™‚é–‹å•Ÿå¤šå€‹æ­¤ç¨‹å¼ + + + + Status Field + 狀態列 + + + + <p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p> + <p>åœ¨ä½¿ç”¨è€…æ‰‹å‹•é—œé–‰äº†ç‹€æ…‹é¢æ¿å¾Œï¼Œæ­¤é¸é …èƒ½ç¢ºä¿æœ‰æ–°è¨Šæ¯è¢«å‡ºç¾æ™‚ç‹€æ…‹é¢æ¿è‡ªå‹•è¢«é‡æ–°é–‹å•Ÿã€‚如果ç¦ç”¨ï¼Œç‹€æ…‹é¢æ¿åªèƒ½ç”±ä½¿ç”¨è€…手動é€éŽâ€œæª¢è¦–â€é¸å–®ä¾†é‡æ–°é–‹å•Ÿã€‚</p> + + + + Always open Status panel when new message is printed + 有新訊æ¯è¼¸å‡ºæ™‚å°±é–‹å•Ÿç‹€æ…‹é¢æ¿ + + + + Code syntax colors + Code syntax colors + + + + Keyword foreground + Keyword foreground + + + + Regular foreground + Regular foreground + + + + String foreground + String foreground + + + + Comment foreground + Comment foreground + + + + Valid objects foreground + Valid objects foreground + + + + Current query background + Current query background + + + + Bind parameter foreground + Bind parameter foreground + + + + Current line background + Current line background + + + + Matched parenthesis background + Matched parenthesis background + + + + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + <html><head/><body><p>You can disable current query highlighting entirely on the General settings page.</p></body></html> + + + + Number foreground + Number foreground + + + + BLOB value foreground + BLOB value foreground + + + + Matched parenthesis foreground + Matched parenthesis foreground + + + + Reset to defaults + Reset to defaults + + + + Filter shortcuts by name or key combination + 以å稱或按éµçµ„åˆç¯©é¸å¿«æ·éµ + + + + Action + æ“作 + + + + Key combination + 按éµçµ„åˆ + + + + + Language + 語言 + + + + Changing language requires application restart to take effect. + 語言變更在程å¼é‡å•Ÿå¾Œç”Ÿæ•ˆã€‚ + + + + Compact layout + 緊湊佈局 + + + + <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> + <p>緊湊佈局會將介é¢ä¸­çš„邊框與留白減至最å°ï¼Œç„¶å¾Œç”¨é€™äº›å€åŸŸå±•示更多資料。這會使介é¢çœ‹èµ·ä¾†æœ‰ä¸€é»žä¸ç¾Žè§€ï¼Œä½†å°‡å¯ä»¥åŒæ™‚展示更多的資料。</p> + + + + Use compact layout + 使用緊湊佈局 + + + + Main window dock areas + 主視窗åœé å€åŸŸ + + + + Left and right areas occupy corners + å·¦å³ä½ˆå±€ + + + + Top and bottom areas occupy corners + 上下佈局 + + + + Hide built-in plugins + éš±è—內建外掛 + + + + Current style: + ç•¶å‰é¢¨æ ¼ï¼š + + + + Preview + é è¦½ + + + + Enabled + 已啟用 + + + + Disabled + å·²ç¦ç”¨ + + + + Active formatter plugin + 啟用格å¼åŒ–外掛 + + + + SQL editor font + SQL 編輯器字型 + + + + Database list font + 資料庫清單字型 + + + + Database list additional label font + 資料庫清單é¡å¤–資訊字型 + + + + Data view font + 資料檢視字型 + + + + Status field font + 狀態列字型 + + + + Code assistant settings + Code assistant settings + + + + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + <html><head/><body><p>If this option is enabled, the code assistant will be triggered in cases, when user types for example <span style=" font-weight:700;">tableName.</span> to propose columns of the table. If the option is disabled, user will have to hit the assistant hotkey explicitly.</p></body></html> + + + + Automatically trigger the assistant after a dot is typed after an object name + Automatically trigger the assistant after a dot is typed after an object name + + + + Description: + plugin details + æè¿°ï¼š + + + + Category: + plugin details + 分類: + + + + Version: + plugin details + 版本: + + + + Author: + plugin details + 作者: + + + + Internal name: + plugin details + 內部å稱: + + + + Dependencies: + plugin details + ä¾è³´ï¼š + + + + Conflicts: + plugin details + è¡çªï¼š + + + + Plugin details + 外掛詳情 + + + + Plugins are loaded/unloaded immediately when checked/unchecked, but modified list of plugins to load at startup is not saved until you commit the whole configuration dialog. + åˆ‡æ›æ™‚外掛會立å³è¼‰å…¥/解除安è£ã€‚但此清單的修改僅在點é¸ç¢ºå®šæŒ‰éˆ•後被儲存。 + + + + %1 (built-in) + plugins manager in configuration dialog + %1 (內建) + + + + Details + 詳情 + + + + No plugins in this category. + 沒有此分類下的外掛。 + + + + Add new data type + 新增新的資料型別 + + + + Rename selected data type + 釿–°å‘½å所é¸è³‡æ–™åž‹åˆ¥ + + + + Delete selected data type + 刪除所é¸è³‡æ–™åž‹åˆ¥ + + + + Help for configuring data type editors + 設定檔資料型別編輯器幫助 + + + + Clear hotkey for this action + Clear hotkey for this action + + + + Restore original hotkey for this action + Restore original hotkey for this action + + + + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + Here you can configure colors for code syntax highlighting. They are shared across different languages - not only for SQL, but also JavaScript and others. By default a theme-based color is used. To define your own color, enable a custom color by selecting a checkbox next to a particular color. + + + + ConstraintCheckPanel + + + The condition + æ¢ä»¶ + + + + Named constraint: + 命å的約æŸï¼š + + + + On conflict + ç•¶è¡çªæ™‚ + + + + Enter a valid condition. + 輸入一個有效的æ¢ä»¶ã€‚ + + + + Enter a name of the constraint. + 輸入一個約æŸå稱。 + + + + ConstraintDialog + + + New constraint + constraint dialog + æ–°å»ºç´„æŸ + + + + Create + constraint dialog + 建立 + + + + Edit constraint + dialog window + ç·¨è¼¯ç´„æŸ + + + + Apply + constraint dialog + 應用 + + + + Primary key + table constraints + ä¸»éµ + + + + Foreign key + table constraints + å¤–ä¾†éµ + + + + Unique + table constraints + 唯一 + + + + Not NULL + table constraints + éžç©º + + + + Check + table constraints + æ¢ä»¶ + + + + Generated + table constraints + ç”Ÿæˆ + + + + Collate + table constraints + å­—å…ƒåº + + + + Default + table constraints + é è¨­ + + + + ConstraintTabModel + + + Table + table constraints + 表 + + + + Column (%1) + table constraints + 列 (%1) + + + + Scope + table constraints + 作用域 + + + + Type + table constraints + 型別 + + + + Details + table constraints + 詳情 + + + + Name + table constraints + å稱 + + + + CssDebugDialog + + + SQLiteStudio CSS console + SQLiteStudio CSS 控制檯 + + + + DataView + + + Filter data + data view + 篩é¸è³‡æ–™ + + + + Grid view + 網格檢視 + + + + Form view + 表單檢視 + + + + Refresh table data + data view + 釿–°æ•´ç†è¡¨è³‡æ–™ + + + + First page + data view + ç¬¬ä¸€é  + + + + Previous page + data view + ä¸Šä¸€é  + + + + Next page + data view + ä¸‹ä¸€é  + + + + Last page + data view + æœ€å¾Œä¸€é  + + + + Commit changes for selected cells + data view + æäº¤é¸ä¸­å–®å…ƒæ ¼çš„æ›´æ”¹ + + + + Rollback changes for selected cells + data view + 回滾é¸ä¸­å–®å…ƒæ ¼çš„修改 + + + + Show grid view of results + data view + é¡¯ç¤ºçµæžœçš„網格檢視 + + + + Show form view of results + data view + é¡¯ç¤ºçµæžœçš„表格檢視 + + + + Filter by text (if contains) + data view + Filter by text (if contains) + + + + Filter strictly by text (if equals) + data view + Filter strictly by text (if equals) + + + + Tabs on top + data view + 頂部標籤 + + + + Tabs at bottom + data view + 底部標籤 + + + + Place new rows above selected row + data view + 放置新行於é¸ä¸­è¡Œä¹‹ä¸Š + + + + Place new rows below selected row + data view + 放置新行於é¸ä¸­è¡Œä¹‹ä¸‹ + + + + Place new rows at the end of the data view + data view + 放置新行於資料檢視末尾 + + + + Total number of rows is being counted. +Browsing other pages will be possible after the row counting is done. + 正在統計總行數。 +請在此æ“作完æˆå¾Œå†ç€è¦½å…¶ä»–é é¢ã€‚ + + + + Row: %1 + 行:%1 + + + + Filter + ç¯©é¸ + + + + Hit Enter key or press "Apply filter" button on toolbar to apply new value. + æŒ‰å›žè»Šéµæˆ–點é¸å·¥å…·æ¬„上的“應用篩é¸â€æŒ‰éˆ•來應用新值。 + + + + Filter by the Regular Expression + data view + 以正則表示å¼ç¯©é¸ + + + + Filter by SQL expression + data view + 以 SQL 表示å¼ç¯©é¸ + + + + Show filter inputs per column + data view + 在æ¯å€‹åˆ—上展示篩é¸å™¨è¼¸å…¥æ¡† + + + + Apply filter + data view + 應用篩é¸å™¨ + + + + DbDialog + + + Database + 資料庫 + + + + Database type + 資料庫型別 + + + + Database driver + 資料庫驅動 + + + + + File + 檔案 + + + + Name (on the list) + å稱 (顯示在清單中) + + + + Options + é¸é … + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + aasfd + <p>啟用此é¸é …後,設定檔檔案中將記ä½è©²è³‡æ–™åº«ï¼Œä¸¦åœ¨æ¯æ¬¡å•Ÿå‹• SQLiteStudio 時還原 (開啟) 它。</p> + + + + Permanent (keep it in configuration) + è¨˜ä½æ­¤è³‡æ–™åº« + + + + Test connection + 測試連線 + + + + Select new or existing file on local computer + Select new or existing file on local computer + + + + Browse + ç€è¦½ + + + + Database type not selected. + Database type not selected. + + + + Database path not specified. + Database path not specified. + + + + Enter an unique database name. + 請輸入一個唯一的資料庫å稱。 + + + + This name is already in use. Please enter unique name. + æ­¤å稱已被使用,請輸入一個未被佔用的å稱。 + + + + <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> + <p>å稱已手動編輯,自動命åå·²ç¦ç”¨ã€‚清空å稱欄中的內容將æ¢å¾©è‡ªå‹•命å。</p> + + + + Enter a database file path. + 輸入一個數據庫檔案的路徑。 + + + + This database is already on the list under name: %1 + 該資料庫已在清單中,å為:%1 + + + + Select a database type. + è«‹é¸æ“‡ä¸€å€‹æ•¸æ“šåº«åž‹åˆ¥ã€‚ + + + + DbObjectDialogs + + + Delete table + 刪除表 + + + + Are you sure you want to delete table %1? + 確定è¦åˆªé™¤è¡¨â€œ%1â€å—Žï¼Ÿ + + + + Delete index + 刪除索引 + + + + Are you sure you want to delete index %1? + 確定è¦åˆªé™¤ç´¢å¼•“%1â€å—Žï¼Ÿ + + + + Delete trigger + 刪除觸發器 + + + + Are you sure you want to delete trigger %1? + 確定è¦åˆªé™¤è§¸ç™¼å™¨â€œ%1â€å—Žï¼Ÿ + + + + Delete view + 刪除檢視 + + + + Are you sure you want to delete view %1? + 確定è¦åˆªé™¤æª¢è¦–“%1â€å—Žï¼Ÿ + + + + + Error while dropping %1: %2 + 丟棄 %1 時出錯:%2 + + + + Delete objects + 刪除物件 + + + + Are you sure you want to delete following objects: +%1 + 您確èªè¦åˆªé™¤ä»¥ä¸‹ç‰©ä»¶å—Žï¼š +%1 + + + + Cannot start transaction. Details: %1 + 無法開始事務。詳細資訊:%1 + + + + Cannot commit transaction. Details: %1 + 無法æäº¤äº‹å‹™ã€‚詳細資訊:%1 + + + + DbTree + + + Databases + 資料庫 + + + + Filter by name + 按åç¨±éŽæ¿¾ + + + + Copy + 複製 + + + + Paste + 貼上 + + + + Select all + å…¨é¸ + + + + Create a group + 建立分組 + + + + Delete the group + 刪除分組 + + + + Rename the group + 釿–°å‘½å分組 + + + + &Add a database + 新增資料庫(&A) + + + + &Edit the database + 編輯資料庫(&E) + + + + &Remove the database + 移除資料庫(&R) + + + + &Connect to the database + 連線到資料庫(&C) + + + + &Disconnect from the database + 斷開資料庫連線(&D) + + + + Import + 匯入 + + + + &Export the database + 匯出資料庫(&E) + + + + Vac&uum + 釋放空閒佔用 (VACUUM) (&U) + + + + &Integrity check + 檢查完整性(&I) + + + + Create a &table + 新建表(&T) + + + + Edit the t&able + 編輯表(&A) + + + + Delete the ta&ble + 刪除表(&B) + + + + Export the table + 匯出表 + + + + Import into the table + 匯入到表 + + + + Populate table + 填充表 + + + + Create similar table + 建立相似的表 + + + + Reset autoincrement sequence + é‡ç½®è‡ªå‹•éžå¢žåºåˆ— + + + + Create an &index + 建立索引(&I) + + + + Edit the i&ndex + 編輯索引(&N) + + + + Delete the in&dex + 刪除索引(&D) + + + + Create a trig&ger + 建立觸發器(&G) + + + + Edit the trigg&er + 編輯觸發器(&E) + + + + Delete the trigge&r + 刪除觸發器(&R) + + + + Create a &view + 建立檢視(&V) + + + + Edit the v&iew + 編輯檢視(&I) + + + + Delete the vi&ew + 刪除檢視(&E) + + + + Add a column + æ–°å¢žæ¬„ä½ + + + + Edit the column + ç·¨è¼¯æ¬„ä½ + + + + Delete the column + åˆªé™¤æ¬„ä½ + + + + Delete selected items + 刪除已é¸å°ˆæ¡ˆ + + + + Clear filter + æ¸…é™¤éŽæ¿¾å™¨ + + + + &Refresh all database schemas + 釿–°æ•´ç†å…¨éƒ¨è³‡æ–™åº«çµæ§‹(&R) + + + + Re&fresh selected database schema + 釿–°æ•´ç†å·²é¸è³‡æ–™åº«çµæ§‹(&F) + + + + + Erase table data + 擦除表資料 + + + + Open file's directory + 開啟檔案所在目錄 + + + + Execute SQL from file + 從檔案執行 SQL + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + + Database + 資料庫 + + + + Grouping + 分組 + + + + Generate query for table + 生æˆå°éŒ¶çš„æŸ¥è©¢ + + + + + Create group + 建立分組 + + + + Group name + 分組å + + + + Entry with name %1 already exists in group %2. + å稱 %1 在分組 %2 中已存在。 + + + + Delete group + 刪除分組 + + + + Are you sure you want to delete group %1? +All objects from this group will be moved to parent group. + 確èªåˆªé™¤çµ„ %1 嗎? +刪除後該組下的全部內容將被移動到其所屬的父分組中。 + + + + Are you sure you want to remove database '%1' from the list? + 確定è¦ç§»é™¤è³‡æ–™åº« %1 嗎? + + + + Are you sure you want to remove following databases from the list: +%1 + 確定è¦å¾žæ¸…單中移除下列資料庫嗎: +%1 + + + + Remove database + 移除資料庫 + + + + + Cannot import, because no import plugin is loaded. + 無法匯入,沒有載入匯入外掛。 + + + + + Cannot export, because no export plugin is loaded. + 無法匯出,沒有載入匯出外掛。 + + + + Vacuum (%1) + 釋放空閒佔用 (VACUUM) (%1) + + + + Integrity check (%1) + 完整性檢查 (%1) + + + + Reset autoincrement + é‡ç½®è‡ªå‹•éžå¢ž + + + + Are you sure you want to reset autoincrement value for table '%1'? + 您確定è¦é‡ç½®è¡¨â€œ%1â€çš„自動éžå¢žå€¼å—Žï¼Ÿ + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + é‡ç½®è¡¨â€œ%1â€çš„自動éžå¢žå€¼æ™‚出錯:%2 + + + + Autoincrement value for table '%1' has been reset successfully. + æˆåŠŸé‡ç½®è¡¨â€œ%1â€çš„自動éžå¢žå€¼ã€‚ + + + + Are you sure you want to delete all data from table(s): %1? + 您確定è¦åˆªé™¤è¡¨ %1 的所有資料嗎? + + + + An error occurred while trying to delete data from table '%1': %2 + 刪除表“%1â€ä¸­çš„資料時出錯:%2 + + + + All data has been deleted for table '%1'. + 表“%1â€ä¸­çš„全部資料已被刪除。 + + + + Following objects will be deleted: %1. + 下列物件將被刪除:%1。 + + + + Following databases will be removed from list: %1. + 下列資料庫將從清單中移除:%1。 + + + + Remainig objects from deleted group will be moved in place where the group used to be. + 已刪除分組中的剩餘物件將被移開。 + + + + %1<br><br>Are you sure you want to continue? + %1<br><br>確定繼續? + + + + Delete objects + 刪除物件 + + + + DbTreeItemDelegate + + + error + dbtree labels + 錯誤 + + + + (system table) + database tree label + (系統表) + + + + (virtual) + virtual table label + (虛擬) + + + + (system index) + database tree label + (系統索引) + + + + DbTreeModel + + + Database: %1 + dbtree tooltip + 資料庫:%1 + + + + URI: + dbtree tooltip + URI: + + + + Version: + dbtree tooltip + 版本: + + + + File size: + dbtree tooltip + 檔案大å°ï¼š + + + + Encoding: + dbtree tooltip + 編碼: + + + + Error: + dbtree tooltip + 錯誤: + + + + Table : %1 + dbtree tooltip + 表:%1 + + + + Columns (%1): + dbtree tooltip + 列 (%1): + + + + Indexes (%1): + dbtree tooltip + 索引 (%1): + + + + Triggers (%1): + dbtree tooltip + 觸發器 (%1): + + + + Copy + 複製 + + + + Move + 移動 + + + + Include data + 包å«è³‡æ–™ + + + + Include indexes + 包å«ç´¢å¼• + + + + Include triggers + 包å«è§¸ç™¼å™¨ + + + + Abort + 中止 + + + + Could not add dropped database file '%1' automatically. Manual setup is necessary. + 無法自動新增拖放的資料庫檔案 '%1'ã€‚éœ€è¦æ‰‹å‹•設定。 + + + + Referenced tables + 引用的表 + + + + Do you want to include following referenced tables as well: +%1 + 是å¦è¦åŒ…å«ä»¥ä¸‹å¼•用的表: +%1 + + + + Name conflict + å稱è¡çª + + + + Following object already exists in the target database. +Please enter new, unique name, or press '%1' to abort the operation: + 下列物件已存在於目標資料庫。 +請輸入一個新的ä¸é‡è¤‡çš„å稱,或按下 '%1' 中止æ“作: + + + + SQL statements conversion + SQL 語å¥è½‰æ› + + + + Following error occurred while converting SQL statements to the target SQLite version: + å°‡ SQL 語å¥è½‰æ›ç‚ºç›®æ¨™ SQLite 版本時發生以下錯誤: + + + + Would you like to ignore those errors and proceed? + 是å¦å¿½ç•¥éŒ¯èª¤ä¸¦ç¹¼çºŒï¼Ÿ + + + + DdlHistoryWindow + + + Filter by database: + æŒ‰è³‡æ–™åº«éŽæ¿¾ï¼š + + + + Clear entire history + Clear entire history + + + + -- Queries executed on database %1 (%2) +-- Date and time of execution: %3 +%4 + -- 在資料庫 %1 (%2) 執行的查詢 +-- 執行日期和時間:%3 +%4 + + + + Clear history + Clear history + + + + Are you sure you want to erase entire DDL history? + Are you sure you want to erase entire DDL history? + + + + DDL history + DDL æ­·å² + + + + DdlPreviewDialog + + + Queries to be executed + å°‡è¦åŸ·è¡Œçš„èªžå¥ + + + + Don't show again + ä¸å†é¡¯ç¤º + + + + DebugConsole + + + SQLiteStudio Debug Console + SQLiteStudio 除錯終端 + + + + EditorWindow + + + SQL editor + SQL editor + + + + Query + 查詢 + + + + History + æ­·å² + + + + Results in the separate tab + 在新é¸é …å¡ä¸­é¡¯ç¤ºçµæžœ + + + + Results below the query + åœ¨æŸ¥è©¢ä¸‹æ–¹é¡¯ç¤ºçµæžœ + + + + + SQL editor %1 + SQL 編輯器 %1 + + + + + Results + çµæžœ + + + + Execute query + åŸ·è¡Œèªžå¥ + + + + Explain query + 解釋查詢 + + + + Clear execution history + sql editor + æ¸…é™¤åŸ·è¡Œæ­·å² + + + + Export results + sql editor + åŒ¯å‡ºçµæžœ + + + + Create view from query + sql editor + 從查詢建立檢視 + + + + Previous database + å‰ä¸€å€‹æ•¸æ“šåº« + + + + Next database + 後一個數據庫 + + + + Show next tab + sql editor + 顯示下一個標籤 + + + + Show previous tab + sql editor + 顯示上一個標籤 + + + + Focus results below + sql editor + 切æ›ç„¦é»žåˆ°ä¸‹æ–¹çš„çµæžœ + + + + Focus SQL editor above + sql editor + 切æ›ç„¦é»žåˆ°ä¸Šæ–¹çš„ SQL 編輯器 + + + + Delete selected SQL history entries + sql editor + 刪除é¸ä¸­çš„ SQL æ­·å²è¨˜éŒ„é … + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Active database (%1/%2) + 活動資料庫 (%1/%2) + + + + Query finished in %1 second(s). Rows affected: %2 + 查詢用時 %1 秒。影響行數:%2 + + + + Query finished in %1 second(s). + 查詢用時 %1 秒。 + + + + Clear execution history + æ¸…é™¤åŸ·è¡Œæ­·å² + + + + Are you sure you want to erase the entire SQL execution history? This cannot be undone. + 確定è¦åˆªé™¤å…¨éƒ¨çš„ SQL 執行歷å²å—Žï¼Ÿåˆªé™¤å¾Œä¸èƒ½æ¢å¾©ã€‚ + + + + Cannot export, because no export plugin is loaded. + 無法匯出,沒有載入匯出外掛。 + + + + No database selected in the SQL editor. Cannot create a view for unknown database. + 沒有在 SQL 編輯器中é¸ä¸­è³‡æ–™åº«ã€‚無法為未知的資料庫建立檢視。 + + + + Editor window "%1" has uncommitted data. + 編輯器視窗“%1â€å…§æœ‰æœªæäº¤çš„資料。 + + + + ErrorsConfirmDialog + + + Errors + 錯誤 + + + + Following errors occured: + 發生了以下錯誤: + + + + Would you like to proceed? + ä»è¦ç¹¼çºŒå—Žï¼Ÿ + + + + ExecFromFileDialog + + + Execute SQL from file + 從檔案執行 SQL + + + + Input file + 輸入檔案 + + + + Path to file + 檔案路徑 + + + + Browse for file + ç€è¦½æª”案 + + + + Options + é¸é … + + + + File encoding + 檔案編碼 + + + + Skip failing SQL statements + è·³éŽå¤±æ•—çš„ SQL èªžå¥ + + + + SQL scripts (*.sql);;All files (*) + SQL 指令碼 (*.sql);;所有檔案 (*) + + + + Execute SQL file + 執行 SQL 檔案 + + + + Please provide file to be executed. + è«‹æä¾›ä¸€å€‹æª”案以供執行。 + + + + Provided file does not exist or cannot be read. + æä¾›çš„æª”案ä¸å­˜åœ¨æˆ–無法讀å–。 + + + + ExportDialog + + + Export + 匯出 + + + + What do you want to export? + 您想匯出什麼? + + + + A database + 資料庫 + + + + A single table + 一張表 + + + + Query results + æŸ¥è©¢çµæžœ + + + + Table to export + è¦åŒ¯å‡ºçš„表 + + + + Database + 資料庫 + + + + Table + 表 + + + + Options + é¸é … + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + ä¸é¸ä¸­æ­¤é¸é …時,僅匯出表的 DDL (CREATE TABLE 語å¥)。 + + + + Export table data + 匯出表的資料 + + + + Export table indexes + 匯出表的索引 + + + + Export table triggers + 匯出表的觸發器 + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + 注æ„,æŸäº›è¼¸å‡ºæ ¼å¼å¯èƒ½ä¸æ”¯æ´åŒ¯å‡ºè¡¨ç´¢å¼•和觸發器。 + + + + Select database objects to export + 鏿“‡è¦åŒ¯å‡ºçš„資料庫物件 + + + + Export data from tables + 從表中匯出資料 + + + + Select all + å…¨é¸ + + + + Deselect all + å…¨ä¸é¸ + + + + + Database: + 資料庫: + + + + Query to export results for + åŒ¯å‡ºçµæžœæ‰€éœ€çš„æŸ¥è©¢ + + + + Query to be executed for results: + å°åŒ¯å‡ºçµæžœåŸ·è¡Œçš„æŸ¥è©¢ï¼š + + + + Export format and options + 匯出格å¼å’Œé¸é … + + + + Export format + åŒ¯å‡ºæ ¼å¼ + + + + Output + 輸出 + + + + Exported file path + 匯出檔案路徑 + + + + Clipboard + 剪貼簿 + + + + File + 檔案 + + + + Exported text encoding: + 匯出文字編碼: + + + + Export format options + 匯出格å¼é¸é … + + + + Cancel + å–æ¶ˆ + + + + + + Select database to export. + 鏿“‡è¦åŒ¯å‡ºçš„資料庫。 + + + + Select table to export. + 鏿“‡è¦åŒ¯å‡ºçš„表。 + + + + Enter valid query to export. + 輸入è¦åŒ¯å‡ºçš„æœ‰æ•ˆæŸ¥è©¢ã€‚ + + + + Select at least one object to export. + è‡³å°‘é¸æ“‡ä¸€å€‹ç‰©ä»¶é€²è¡ŒåŒ¯å‡ºã€‚ + + + + You must provide a file name to export to. + æ‚¨å¿…é ˆé¸æ“‡ä¸€å€‹åŒ¯å‡ºæª”案。 + + + + Path you provided is an existing directory. You cannot overwrite it. + 您æä¾›çš„路徑是一個ç¾å­˜çš„目錄,ä¸èƒ½è¦†è“‹ã€‚ + + + + The directory '%1' does not exist. + 目錄 '%1' ä¸å­˜åœ¨ã€‚ + + + + The file '%1' exists and will be overwritten. + 檔案“%1â€å­˜åœ¨ä¸”將被覆蓋。 + + + + All files (*) + 所有檔案 (*) + + + + Pick file to export to + 鏿“‡æª”案匯出到 + + + + Internal error during export. This is a bug. Please report it. + 匯出時發生內部錯誤。這是一個 Bug,請å饋。 + + + + FileExecErrorsDialog + + + Execution errors + 執行錯誤 + + + + Following errors were encountered during execution of SQL statements from the file: + 從檔案執行 SQL èªžå¥æ™‚é‡åˆ°ä»¥ä¸‹éŒ¯èª¤ï¼š + + + + SQL + SQL + + + + Error + 錯誤 + + + + Statements that were executed successfully were commited. + æˆåŠŸåŸ·è¡Œçš„èªžå¥å·²æäº¤ã€‚ + + + + Statements that were executed successfully were rolled back. + æˆåŠŸåŸ·è¡Œçš„èªžå¥å·²å›žæ»¾ã€‚ + + + + FkComboBox + + + Cannot edit this cell. Details: %1 + 無法編輯此單元格。詳情:%1 + + + + FontEdit + + + Choose font + font configuration + 鏿“‡å­—åž‹ + + + + Form + + + Active SQL formatter plugin + 啟用 SQL èªžå¥æ ¼å¼åŒ–外掛 + + + + FormView + + + Commit row + form view + æäº¤è¡Œ + + + + Rollback row + form view + 回滾行 + + + + First row + form view + 首行 + + + + Previous row + form view + 上一行 + + + + Next row + form view + 下一行 + + + + Last row + form view + 末行 + + + + Insert new row + form view + æ’入新行 + + + + Delete current row + form view + 刪除當å‰è¡Œ + + + + FunctionsEditor + + + Filter functions + Filter functions + + + + Input arguments + 輸入引數 + + + + Undefined + 未定義 + + + + Databases + 資料庫 + + + + Register in all databases + 在所有資料庫中註冊 + + + + Register in following databases: + 在下列資料庫中註冊: + + + + Type: + 型別: + + + + Function name: + 函å¼å: + + + + Implementation language: + 實ç¾èªžè¨€ï¼š + + + + Deterministic + Deterministic + + + + Initialization code: + åˆå§‹åŒ–程å¼ç¢¼ï¼š + + + + + Function implementation code: + 函å¼å¯¦ç¾ç¨‹å¼ç¢¼ï¼š + + + + Final step implementation code: + 最終一步實ç¾ç¨‹å¼ç¢¼ï¼š + + + + SQL functions editor + SQL functions editor + + + + Commit all function changes + æäº¤æ‰€æœ‰å°å‡½å¼çš„æ›´æ”¹ + + + + Rollback all function changes + 回滾所有å°å‡½å¼çš„æ›´æ”¹ + + + + Create new function + æ–°å»ºå‡½å¼ + + + + Delete selected function + 刪除已é¸å‡½å¼ + + + + Custom SQL functions manual + 自訂 SQL 函弿‰‹å†Š + + + + Add function argument + 新增函å¼å¼•數 + + + + Rename function argument + 釿–°å‘½å函å¼å¼•數 + + + + Delete function argument + 刪除函å¼å¼•數 + + + + Move function argument up + 上移函å¼å¼•數 + + + + Move function argument down + 下移函å¼å¼•數 + + + + Scalar + 標é‡å‡½å¼ + + + + Aggregate + èšåˆå‡½å¼ + + + + Enter a non-empty, unique name of the function. + 請輸入éžç©ºä¸”唯一的函å¼å稱。 + + + + Pick the implementation language. + 鏿“‡å¯¦ç¾èªžè¨€ã€‚ + + + + Per step code: + 步進程å¼ç¢¼ï¼š + + + + Enter a non-empty implementation code. + 請輸入éžç©ºçš„實ç¾ç¨‹å¼ç¢¼ã€‚ + + + + argument + new function argument name in function editor window + 引數 + + + + Functions editor window has uncommitted modifications. + 函å¼ç·¨è¼¯å™¨è¦–窗有未æäº¤çš„æ›´æ”¹ã€‚ + + + + ImportDialog + + + Import data + 匯入資料 + + + + Table to import to + 匯入目標 + + + + Table + 表 + + + + Database + 資料庫 + + + + Data source to import from + åŒ¯å…¥è³‡æ–™ä¾†æº + + + + Data source type + 資料來æºåž‹åˆ¥ + + + + Options + é¸é … + + + + Text encoding: + 文字編碼: + + + + Input file: + 輸入檔案: + + + + <p>If enabled, any constraint violation, or invalid data format (wrong column count), or any other problem encountered during import will be ignored and the importing will be continued.</p> + <p>如果啟用,則匯入期間é‡åˆ°çš„任何約æŸé•背ã€ç„¡æ•ˆè³‡æ–™æ ¼å¼ (錯誤列數) 或其他å•題都將被忽略,匯入ä¸ä¸­æ­¢ã€‚</p> + + + + Ignore errors + 忽略錯誤 + + + + Data source options + 資料來æºé¸é … + + + + Cancel + å–æ¶ˆ + + + + If you type table name that doesn't exist, it will be created. + 如果您輸入的表ä¸å­˜åœ¨ï¼Œå‰‡å°‡å»ºç«‹è©²è¡¨ã€‚ + + + + Enter the table name + 輸入表å + + + + Select import plugin. + 鏿“‡åŒ¯å…¥å¤–掛。 + + + + You must provide a file to import from. + 您必須æä¾›ä¸€å€‹åŒ¯å…¥åŽŸå§‹æª”ã€‚ + + + + The file '%1' does not exist. + 檔案“%1â€ä¸å­˜åœ¨ã€‚ + + + + Path you provided is a directory. A regular file is required. + 您æä¾›çš„æ˜¯ä¸€å€‹ç›®éŒ„,而需è¦çš„æ˜¯ä¸€å€‹ä¸€èˆ¬æª”案。 + + + + Pick file to import from + 鏿“‡è¦åŒ¯å…¥çš„æª”案 + + + + IndexDialog + + + + Index + 索引 + + + + Column + 列 + + + + Sort + æŽ’åº + + + + Collation + å­—å…ƒåº + + + + On table: + 表: + + + + Delete selected indexed expression + 刪除é¸å®šçš„ç´¢å¼•è¡¨ç¤ºå¼ + + + + Moves selected index column up in the order, making it more significant in the index. + å‘上移動é¸ä¸­çš„索引,使它在索引中變得更é‡è¦ã€‚ + + + + Moves selected index column down in the order, making it less significant in the index. + å‘下移動é¸ä¸­çš„索引,使它在索引中變得ä¸é‡è¦ã€‚ + + + + Partial index condition + 部分索引æ¢ä»¶ + + + + Unique index + 唯一索引 + + + + Index name: + 索引å: + + + + Edit selected indexed expression + 編輯é¸å®šçš„ç´¢å¼•è¡¨ç¤ºå¼ + + + + Add indexed expression + æ–°å¢žç´¢å¼•è¡¨ç¤ºå¼ + + + + DDL + DDL + + + + Tried to open index dialog for closed or inexisting database. + 試圖開啟已關閉或ä¸å­˜åœ¨çš„資料庫的索引å°è©±æ–¹å¡Šã€‚ + + + + Could not process index %1 correctly. Unable to open an index dialog. + 無法正確處ç†ç´¢å¼• %1。無法開啟索引å°è©±æ–¹å¡Šã€‚ + + + + Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option. + 唯一索引ä¸èƒ½æœ‰ç´¢å¼•表示å¼ã€‚從下é¢çš„æ¸…單中刪除表示å¼ï¼Œæˆ–å–æ¶ˆé¸ä¸­æ­¤é¸é …。 + + + + Pick the table for the index. + ç‚ºç´¢å¼•é¸æ“‡ä¸€å€‹è¡¨ã€‚ + + + + Select at least one column. + 鏿“‡è‡³å°‘一個列。 + + + + Enter a valid condition. + 輸入一個有效的æ¢ä»¶ã€‚ + + + + default + index dialog + é è¨­ + + + + Sort order + table constraints + æŽ’åº + + + + + Error + index dialog + 錯誤 + + + + Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values? + 無法建立唯一索引,因為é¸ä¸­çš„åˆ—ä¸­çš„å€¼ä¸æ˜¯å”¯ä¸€çš„。您想è¦åŸ·è¡Œ SELECT 查詢以檢視有å•題的值嗎? + + + + An error occurred while executing SQL statements: +%1 + 在執行 SQL èªžå¥æ™‚發生了錯誤:%1 + + + + IndexExprColumnDialog + + + Indexed expression + ç´¢å¼•è¡¨ç¤ºå¼ + + + + Expression to index + ç´¢å¼•è¡¨ç¤ºå¼ + + + + This expression is already indexed by the index. + 此表示å¼å·²è¢«è©²ç´¢å¼•所索引。 + + + + Column should be indexed directly, not by expression. Either extend this expression to contain something more than just column name, or abort and select this column in index dialog directly. + 列應該直接索引而éžé€éŽè¡¨ç¤ºå¼ã€‚擴充套件此表示å¼ä¾†åŒ…å«åˆ—å稱以外的內容,或者中止並在索引å°è©±æ–¹å¡Šä¸­ç›´æŽ¥é¸æ“‡æ­¤åˆ—。 + + + + Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table. + 列 '%1' ä¸å±¬æ–¼æ­¤ç´¢å¼•所在的表。索引表示å¼åªèƒ½å¼•用索引表中的列。 + + + + It's forbidden to use 'SELECT' statements in indexed expressions. + 索引表示å¼ä¸­ç¦æ­¢ä½¿ç”¨ 'SELECT' 語å¥ã€‚ + + + + Enter an indexed expression. + 請輸入一個索引表示å¼ã€‚ + + + + Invalid expression. + 無效的表示å¼ã€‚ + + + + LanguageDialog + + + Language + 語言 + + + + Please choose language: + è«‹é¸æ“‡ä¸€ç¨®èªžè¨€ï¼š + + + + MainWindow + + + Database toolbar + 資料庫工具欄 + + + + Structure toolbar + çµæ§‹å·¥å…·æ¬„ + + + + Tools + 工具 + + + + Window list + 視窗清單 + + + + View toolbar + 檢視工具欄 + + + + Configuration widgets + 設定檔部件 + + + + Syntax highlighting engines + 語法高亮引擎 + + + + Data editors + 資料編輯器 + + + + Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. + 正在以除錯模å¼åŸ·è¡Œã€‚按下 %1 或使用 幫助 - 開啟除錯控制檯 é¸å–®ä¾†é–‹å•Ÿé™¤éŒ¯æŽ§åˆ¶æª¯ã€‚ + + + + Running in debug mode. Debug messages are printed to the standard output. + 正在以除錯模å¼åŸ·è¡Œã€‚除錯資訊將會被輸出在標準輸出中。 + + + + You need to restart application to make the language change take effect. + 語言變更在程å¼é‡å•Ÿå¾Œç”Ÿæ•ˆã€‚ + + + + Open SQL &editor + 開啟 SQL 編輯器(&E) + + + + Open DDL &history + 開啟 DDL æ­·å²(&H) + + + + Open SQL &functions editor + 開啟 SQL 函å¼ç·¨è¼¯å™¨(&F) + + + + Open code &snippets editor + Open code &snippets editor + + + + Open &collations editor + 開啟字元åºç·¨è¼¯å™¨(&C) + + + + Open ex&tension manager + 開啟擴充套件管ç†å™¨(&T) + + + + &Import + 匯入(&I) + + + + E&xport + 匯出(&X) + + + + Open confi&guration dialog + 開啟設定檔å°è©±æ–¹å¡Š(&G) + + + + &Tile windows + 平鋪視窗(&T) + + + + Tile windows &horizontally + 水平排列視窗(&H) + + + + Tile windows &vertically + 垂直排列視窗(&V) + + + + &Cascade windows + 層疊視窗(&C) + + + + Next window + 下一個視窗 + + + + Previous window + 上一個視窗 + + + + Hide status field + éš±è—狀態列 + + + + Close &all windows + 關閉全部視窗(&A) + + + + Re&store recently closed window + 還原最近關閉的視窗(&S) + + + + Close current &window + Close current &window + + + + Close &other windows + Close &other windows + + + + Close windows on the &left + Close windows on the &left + + + + Close windows on the &right + Close windows on the &right + + + + Re&name selected window + Re&name selected window + + + + Open Debug Console + 開啟除錯終端 + + + + Open CSS Console + 開啟 CSS 控制檯 + + + + Report a &bug + æå ± Bug (&B) + + + + D&onate + æè´ˆ(&O) + + + + Propose a new &feature + æè­°æ–°å¢žåŠŸèƒ½(&F) + + + + &About + 關於(&A) + + + + &Licenses + 許å¯å”è­°(&L) + + + + Open home &page + 訪å•主é (&P) + + + + User &Manual + 使用者手冊(&M) + + + + SQLite &documentation + SQLite 文件(&D) + + + + Bugs and feature &requests + Bug 與功能請求(&R) + + + + Quit + 退出 + + + + Check for &updates + 檢查更新(&U) + + + + &Database + menubar + 資料庫(&D) + + + + &Structure + menubar + çµæ§‹(&S) + + + + &View + menubar + 檢視(&V) + + + + Window list + menubar view menu + 視窗清單 + + + + &Tools + menubar + 工具(&T) + + + + &Help + 幫助(&H) + + + + Could not set style: %1 + main window + 未能設定風格:%1 + + + + Cannot export, because no export plugin is loaded. + 無法匯出,沒有載入匯出外掛。 + + + + Cannot import, because no import plugin is loaded. + 無法匯入,沒有載入匯入外掛。 + + + + Rename window + 釿–°å‘½å視窗 + + + + Enter new name for the window: + 請輸入視窗的新å稱: + + + + New updates are available. <a href="%1">Click here for details</a>. + 有新更新 <a href="%1">點此檢視更新詳情</a>. + + + + You're running the most recent version. No updates are available. + 您使用的是最新版,ä¸éœ€è¦æ›´æ–°ã€‚ + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + 命令列引數傳éžçš„資料庫 (%1) 已在清單中,å為:%2 + + + + Database passed in command line parameters (%1) has been temporarily added to the list under name: %2 + 命令列引數傳éžçš„資料庫 (%1) 已臨時新增到清單中,å為:%2 + + + + Could not add database %1 to list. + 未能將資料 %1 新增到清單 + + + + MdiWindow + + + Uncommitted changes + 未æäº¤çš„æ›´æ”¹ + + + + Close anyway + ä»ç„¶é—œé–‰ + + + + Don't close + ä¸é—œé–‰ + + + + MultiEditor + + + Null value + multieditor + Null 值 + + + + Configure editors for this data type + 為資料型別設定編輯器 + + + + Open another tab + 開啟å¦ä¸€å€‹é¸é …å¡ + + + + Foreign Key + å¤–ä¾†éµ + + + + Data editor plugin '%1' not loaded, while it is defined for editing '%2' data type. + 資料編輯器外掛 '%1' 未載入,而它已被定義為編輯 '%1' 資料型別的編輯器。 {1'?} {2'?} {1' or 2'?} + + + + Deleted + multieditor + 已刪除 + + + + Read only + multieditor + åªè®€ + + + + MultiEditorBoolPlugin + + + Boolean + 布林 + + + + MultiEditorDatePlugin + + + Date + 日期 + + + + MultiEditorDateTimePlugin + + + Date & time + 日期和時間 + + + + MultiEditorHexPlugin + + + Hex + å六進ä½åˆ¶ + + + + MultiEditorNumericPlugin + + + Number + numeric multi editor tab name + 數值 + + + + MultiEditorText + + + Tab changes focus + Tab 鵿›´æ”¹ç„¦é»ž + + + + Cut + 剪下 + + + + Copy + 複製 + + + + Paste + 貼上 + + + + Delete + 刪除 + + + + Undo + 撤銷 + + + + Redo + é‡åš + + + + MultiEditorTextPlugin + + + Text + 文字 + + + + MultiEditorTimePlugin + + + Time + 時間 + + + + NewConstraintDialog + + + New constraint + æ–°ç´„æŸ + + + + + Primary Key + new constraint dialog + ä¸»éµ + + + + + Foreign Key + new constraint dialog + å¤–ä¾†éµ + + + + + Unique + new constraint dialog + 唯一 + + + + + Check + new constraint dialog + æ¢ä»¶ + + + + Not NULL + new constraint dialog + éžç©º + + + + Collate + new constraint dialog + å­—å…ƒåº + + + + Generated + new constraint dialog + ç”Ÿæˆ + + + + Default + new constraint dialog + é è¨­ + + + + NewVersionDialog + + + SQLiteStudio updates + SQLiteStudio æ›´æ–° + + + + New version is available! + 有新版本å¯ç”¨ï¼ + + + + Download new version! + 下載新版本 + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + 下載新版本程å¼åŒ…。將在準備就緒時æé†’您安è£ã€‚ + + + + Open SQLiteStudio home page. + 開啟 SQLiteStudio ä¸»é  + + + + Read release notes && download package yourself. + 閱讀發行說明和自行下載程å¼åŒ…。 + + + + Just close this window. + 關閉此視窗。 + + + + Check for updates on startup + 在啟動時檢查更新 + + + + Not now. + æš«ä¸æ›´æ–° + + + + PopulateConfigDialog + + + Populating configuration + 設定檔填充 + + + + Configuring <b>%1</b> for column <b>%2</b> + çµ¦æ¬„ä½ <b>%2</b> 設定檔 <b>%1</b> + + + + PopulateDialog + + + Populate table + 填充表 + + + + Database + 資料庫 + + + + Table + 表 + + + + Columns + 列 + + + + Number of rows to populate: + 填充的行數: + + + + Populate + populate dialog button + å¡«å…… + + + + Abort + 中止 + + + + Configure + 設定檔 + + + + Populating configuration for this column is invalid or incomplete. + 此列的填充設定檔無效或ä¸å®Œæ•´ã€‚ + + + + Select database with table to populate + 鏿“‡è¦å¡«å……表的資料庫 + + + + Select table to populate + 鏿“‡è¦å¡«å……的表 + + + + You have to select at least one column. + æ‚¨é ˆé¸æ“‡è‡³å°‘一個欄ä½ã€‚ + + + + QObject + + + Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords). + ç„¡æ³•ç·¨è¼¯åˆ—ï¼Œå®ƒæ˜¯è¤‡åˆ %1 èªžå¥ (åŒ…å« %2ã€%3 或 %4 é—œéµå­—) çš„çµæžœã€‚ + + + + The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this. + 查詢執行機制æå– ROWID 的屬性時é‡åˆ°å•題。這å¯èƒ½æ˜¯è»Ÿé«”中的 bug,您å¯ä»¥å ±å‘Šè©²å•題。 + + + + Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited. + 請求的列是一個 SQL 表示å¼çš„çµæžœï¼Œè€Œéžæ™®é€šçš„列。ä¸èƒ½ç·¨è¼¯é€™äº›åˆ—。 + + + + Requested column belongs to restricted SQLite table. Those tables cannot be edited directly. + 請求的列屬於å—é™åˆ¶çš„ SQLite 表。這些表ä¸èƒ½è¢«ç›´æŽ¥ç·¨è¼¯ã€‚ + + + + Cannot edit results of query other than %1. + 無法編輯 %1 ä»¥å¤–çš„æŸ¥è©¢çµæžœã€‚ + + + + Cannot edit columns that are result of aggregated %1 statements. + 無法編輯列,它是èšåˆçš„ %1 語å¥çš„çµæžœã€‚ + + + + Cannot edit columns that are result of %1 statement. + 無法編輯列,它是 %1 語å¥çš„çµæžœã€‚ + + + + Cannot edit columns that are result of common table expression statement (%1). + 無法編輯列,它是通用表生æˆèªžå¥ %1 çš„çµæžœã€‚ + + + + Cannot edit table generated columns. + 無法編輯表生æˆçš„列。 + + + + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + Cannot edit columns that are result of a view if the executed query reads from any multilevel views (i.e. a view that queries another view). + + + + + + + on conflict: %1 + data view tooltip + ç•¶è¡çªæ™‚:%1 + + + + references table %1, column %2 + data view tooltip + 引用表 %1,列 %2 + + + + condition: %1 + data view tooltip + å­—å…ƒåºï¼š%1 + + + + collation name: %1 + data view tooltip + å­—å…ƒåºå稱:%1 + + + + Data grid view + 資料網格檢視 + + + + Edit current cell inline + Edit current cell inline + + + + Copy cell(s) contents to clipboard + 複製單元格內容至剪貼簿 + + + + Copy cell(s) contents together with header to clipboard + 複製單元格內容與表頭至剪貼簿 + + + + Paste cell(s) contents from clipboard + 貼上剪貼簿中的單元格內容 + + + + Set empty value to selected cell(s) + 設定é¸ä¸­å–®å…ƒæ ¼ç‚ºç©ºå€¼ + + + + Set NULL value to selected cell(s) + 設定é¸ä¸­å–®å…ƒæ ¼ç‚º NULL 值 + + + + Commit changes to cell(s) contents + æäº¤å–®å…ƒæ ¼å…§å®¹æ›´æ”¹ + + + + Rollback changes to cell(s) contents + 回滾單元格內容更改 + + + + Delete selected data row + 刪除所é¸è³‡æ–™è¡Œ + + + + Insert new data row + æ’入新資料行 + + + + Open contents of selected cell in a separate editor + 在單ç¨ç·¨è¼¯å™¨ä¸­é–‹å•Ÿæ‰€é¸å–®å…ƒæ ¼çš„內容 + + + + Toggle the height adjustment of rows + Toggle the height adjustment of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Total pages available: %1 + 總計å¯ç”¨é æ•¸ï¼š%1 + + + + Total rows loaded: %1 + 已載入行數:%1 + + + + Data view (both grid and form) + 資料檢視 (網格 + 表格) + + + + Refresh data + 釿–°æ•´ç†è³‡æ–™ + + + + Switch to grid view of the data + 切æ›è‡³è³‡æ–™çš„網格檢視 + + + + Switch to form view of the data + 切æ›è‡³è³‡æ–™çš„表單檢視 + + + + Database list + 資料庫清單 + + + + Delete selected item + 刪除é¸ä¸­é … + + + + Clear filter contents + 清空篩é¸å™¨å…§å®¹ + + + + Refresh schema + 釿–°æ•´ç†çµæ§‹ + + + + Refresh all schemas + 釿–°æ•´ç†å…¨éƒ¨çµæ§‹ + + + + Add database + 新增資料庫 + + + + Select all items + é¸ä¸­æ‰€æœ‰é … + + + + Copy selected item(s) + 複製é¸ä¸­é … + + + + + + Paste from clipboard + 從剪貼簿貼上 + + + + Increase font size + database list + Increase font size + + + + Decrease font size + database list + Decrease font size + + + + Tables + 表 + + + + Indexes + 索引 + + + + Triggers + 觸發器 + + + + Views + 檢視 + + + + Columns + 列 + + + + Data form view + 資料表單檢視 + + + + Commit changes for current row + æäº¤ç•¶å‰è¡Œçš„æ›´æ”¹ + + + + Rollback changes for current row + 回滾當å‰è¡Œçš„æ›´æ”¹ + + + + Go to first row on current page + å‰å¾€ç•¶å‰é çš„第一行 + + + + Go to next row + å‰å¾€ä¸‹ä¸€è¡Œ + + + + Go to previous row + å‰å¾€ä¸Šä¸€è¡Œ + + + + Go to last row on current page + å‰å¾€ç•¶å‰é çš„æœ€å¾Œä¸€è¡Œ + + + + Insert new row + æ’入新行 + + + + Delete current row + 刪除當å‰è¡Œ + + + + Main window + 主視窗 + + + + Open SQL editor + 開啟 SQL 編輯器 + + + + Open DDL history window + Open DDL history window + + + + Open snippets editor window + Open snippets editor window + + + + Open function editor window + Open function editor window + + + + Open collation editor window + Open collation editor window + + + + Open extension manager window + Open extension manager window + + + + Previous window + 上一個視窗 + + + + Next window + 下一個視窗 + + + + Hide status area + éš±è—狀態列 + + + + Open user manual + Open user manual + + + + Open configuration dialog + 開啟設定檔å°è©±æ–¹å¡Š + + + + Open Debug Console + 開啟除錯終端 + + + + Open CSS Console + 開啟 CSS 控制檯 + + + + Open the About dialog + Open the About dialog + + + + Quit the application + é€€å‡ºç¨‹å¼ + + + + Cell text value editor + 單元格文字值編輯器 + + + + + Cut selected text + 剪下é¸ä¸­æ–‡å­— + + + + + Copy selected text + 複製é¸ä¸­æ–‡å­— + + + + + Delete selected text + 刪除é¸ä¸­æ–‡å­— + + + + + Undo + 撤銷 + + + + + Redo + é‡åš + + + + SQL editor input field + SQL 編輯器輸入框 + + + + Select whole editor contents + é¸ä¸­æ•´å€‹ç·¨è¼¯å™¨çš„內容 + + + + Save contents into a file + 將內容儲存至檔案 + + + + Load contents from a file + 從檔案載入內容 + + + + Find in text + 查詢文字 + + + + Find next + 查詢下一個 + + + + Find previous + 查詢上一個 + + + + Replace in text + æ›¿æ›æ–‡å­— + + + + Delete current line + 刪除當å‰è¡Œ + + + + Request code assistant + 請求程å¼ç¢¼è¼”助 + + + + Format contents + æ ¼å¼åŒ–內容 + + + + Move selected block of text one line down + é¸ä¸­æ–‡å­—塊下移一行 + + + + Move selected block of text one line up + é¸ä¸­æ–‡å­—塊上移一行 + + + + Copy selected block of text and paste it a line below + é¸ä¸­æ–‡å­—塊複製並貼上到下一行 + + + + Copy selected block of text and paste it a line above + é¸ä¸­æ–‡å­—塊複製並貼上到上一行 + + + + Toggle comment + 切æ›è¨»é‡‹ + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + All SQLite databases + 所有 SQLite 資料庫 + + + + All files + 所有檔案 + + + + Select database file + Select database file + + + + Select + Select + + + + File type + File type + + + + SQL editor window + SQL 編輯器視窗 + + + + Execute query + åŸ·è¡Œèªžå¥ + + + + Execute single query under cursor + Execute single query under cursor + + + + Execute all queries in editor + Execute all queries in editor + + + + Execute "%1" query + 執行 "%1" 查詢 + + + + Switch current working database to previous on the list + 切æ›ç•¶å‰å·¥ä½œè³‡æ–™åº«åˆ°æ¸…單中的上一個 + + + + Switch current working database to next on the list + 切æ›ç•¶å‰å·¥ä½œè³‡æ–™åº«åˆ°æ¸…單中的下一個 + + + + Go to next editor tab + å‰å¾€ä¸‹ä¸€ç·¨è¼¯å™¨é¸é …å¡ + + + + Go to previous editor tab + å‰å¾€ä¸Šä¸€ç·¨è¼¯å™¨é¸é …å¡ + + + + Move keyboard input focus to the results view below + å°‡éµç›¤è¼¸å…¥ç„¦é»žç§»å‹•到下é¢çš„çµæžœæª¢è¦– + + + + Move keyboard input focus to the SQL editor above + å°‡éµç›¤è¼¸å…¥ç„¦é»žç§»åˆ°ä¸Šé¢çš„ SQL 編輯器 + + + + Delete selected SQL history entries + 刪除é¸ä¸­çš„ SQL æ­·å²è¨˜éŒ„ + + + + Table window + 表視窗 + + + + Commit the table structure + Commit the table structure + + + + Rollback pending changes in the table structure + Rollback pending changes in the table structure + + + + Refresh table structure + 釿–°æ•´ç†è¡¨çµæ§‹ + + + + Add new column + 新增新列 + + + + Edit selected column + 編輯é¸ä¸­åˆ— + + + + Delete selected column + 刪除é¸ä¸­åˆ— + + + + Export table data + 匯出表資料 + + + + Import data to the table + 匯入資料至表 + + + + Add new table constraint + æ–°å¢žè¡¨ç´„æŸ + + + + Edit selected table constraint + 編輯é¸ä¸­è¡¨ç´„æŸ + + + + Delete selected table constraint + 刪除é¸ä¸­è¡¨ç´„æŸ + + + + Refresh table index list + 釿–°æ•´ç†è¡¨ç´¢å¼•清單 + + + + Add new index + 新增索引 + + + + Edit selected index + 編輯é¸ä¸­ç´¢å¼• + + + + Delete selected index + 刪除é¸ä¸­ç´¢å¼• + + + + Refresh table trigger list + 釿–°æ•´ç†è¡¨è§¸ç™¼å™¨æ¸…å–® + + + + + Add new trigger + 新增新觸發器 + + + + + Edit selected trigger + 編輯é¸ä¸­è§¸ç™¼å™¨ + + + + + Delete selected trigger + 刪除é¸ä¸­è§¸ç™¼å™¨ + + + + + Go to next tab + å‰å¾€ä¸‹ä¸€é¸é …å¡ + + + + + Go to previous tab + å‰å¾€ä¸Šä¸€é¸é …å¡ + + + + A view window + 檢視視窗 + + + + Commit the view's query + Commit the view's query + + + + Rollback pending changes in the view's query + Rollback pending changes in the view's query + + + + Refresh view trigger list + 釿–°æ•´ç†æª¢è¦–觸發器清單 + + + + Execute the view's query + Execute the view's query + + + + A code snippets editor window + A code snippets editor window + + + + + + + Commit the pending changes + Commit the pending changes + + + + + + + Rollback the pending changes + Rollback the pending changes + + + + A collation editor window + A collation editor window + + + + A function editor window + A function editor window + + + + A SQLite extension editor window + A SQLite extension editor window + + + + QuitConfirmDialog + + + Uncommitted changes + 未æäº¤çš„æ›´æ”¹ + + + + Are you sure you want to quit the application? + +Following items are pending: + 確定è¦é€€å‡ºæœ¬ç¨‹å¼å—Žï¼Ÿ + +下列專案ä»å¾…處置: + + + + SearchTextDialog + + + Find or replace + æŸ¥è©¢èˆ‡æ›¿æ› + + + + Find: + 查詢: + + + + Case sensitive + å€åˆ†å¤§å°å¯« + + + + Search backwards + å呿œå°‹ + + + + Regular expression matching + 正則表示å¼åŒ¹é… + + + + Replace && +find next + 替æ›ä¸¦æŸ¥è©¢ä¸‹ä¸€é … + + + + Replace with: + 替æ›ç‚ºï¼š + + + + Replace all + å…¨éƒ¨æ›¿æ› + + + + Find + 查詢 + + + + SortDialog + + + Sort by columns + æŒ‰åˆ—æŽ’åº + + + + + Column + 列 + + + + + Order + æŽ’åº + + + + Sort by: %1 + æŽ’åºæŒ‰ç…§ %1 + + + + Move column up + 上移列 + + + + Move column down + 下移列 + + + + SqlEditor + + + Wrap words + sql editor + Wrap words + + + + Cut + sql editor + 剪下 + + + + Copy + sql editor + 複製 + + + + Paste + sql editor + 貼上 + + + + Delete + sql editor + 刪除 + + + + Select all + sql editor + å…¨é¸ + + + + Undo + sql editor + 撤銷 + + + + Redo + sql editor + é‡åš + + + + Complete + sql editor + å®Œæˆ + + + + Format SQL + sql editor + æ ¼å¼åŒ– SQL + + + + Save SQL to file + sql editor + 儲存 SQL 到檔案 + + + + Select file to save SQL + sql editor + 鏿“‡ SQL è¦å„²å­˜åˆ°çš„æª”案 + + + + Load SQL from file + sql editor + 從檔案載入 SQL + + + + Delete line + sql editor + 刪除行 + + + + Move block down + sql editor + 整塊下移 + + + + Move block up + sql editor + 整塊上移 + + + + Copy block down + sql editor + 副本貼上方 + + + + Copy up down + sql editor + 副本貼下方 + + + + Find + sql editor + 查詢 + + + + Find next + sql editor + 查詢下一個 + + + + Find previous + sql editor + 查詢上一個 + + + + Replace + sql editor + æ›¿æ› + + + + Toggle comment + sql editor + 切æ›è¨»é‡‹ + + + + Increase font size + sql editor + Increase font size + + + + Decrease font size + sql editor + Decrease font size + + + + Could not open file '%1' for writing: %2 + 無法以寫模å¼é–‹å•Ÿæª”案 %1:%2 + + + + Saved SQL contents to file: %1 + 儲存 SQL 內容至檔案:%1 + + + + Syntax completion can be used only when a valid database is set for the SQL editor. + SQL 編輯器的語法補全功能僅當存在有效資料庫時å¯ç”¨ã€‚ + + + + Contents of the SQL editor are huge, so errors detecting and existing objects highlighting are temporarily disabled. + SQL 編輯器有大é‡å…§å®¹ï¼Œå› æ­¤éŒ¯èª¤åµæ¸¬å’Œç¾æœ‰ç‰©ä»¶çš„高亮顯示功能被暫時ç¦ç”¨ã€‚ + + + + Save to file + 儲存到檔案 + + + + SQL scripts (*.sql);;All files (*) + SQL檔案 (*.sql);;所有檔案 (*) + + + + Open file + 開啟檔案 + + + + Could not open file '%1' for reading: %2 + 無法以讀模å¼é–‹å•Ÿæª”案 %1:%2 + + + + Reached the end of document. Hit the find again to restart the search. + å·²åˆ°æ–‡ä»¶åº•éƒ¨ã€‚å†æ¬¡é»žé¸æŸ¥è©¢å°‡å¾žé ­é–‹å§‹æœå°‹ã€‚ + + + + SqlQueryItem + + + Committing error: + data view tooltip + æäº¤å‡ºéŒ¯ï¼š + + + + Column: + data view tooltip + 列: + + + + Data type: + data view + 資料型別: + + + + Table: + data view tooltip + 表: + + + + Constraints: + data view tooltip + ç´„æŸï¼š + + + + SqlQueryItemDelegate + + + + + + Cannot edit this cell. Details: %1 + 無法編輯此單元格。詳情:%1 + + + + The row is marked for deletion. + 該行已被標記為刪除。 + + + + + Structure of this table has changed since last data was loaded. Reload the data to proceed. + åœ¨ä¸Šæ¬¡è¼‰å…¥è³‡æ–™å¾Œï¼Œæ­¤è¡¨çš„çµæ§‹å·²æ›´æ”¹ã€‚釿–°è¼‰å…¥è³‡æ–™ä»¥ç¹¼çºŒã€‚ + + + + Editing a huge contents in an inline cell editor is not a good idea. It can become slow and inconvenient. It's better to edit such big contents in a Form View, or in popup editor (available under rick-click menu). + 在內è¯å–®å…ƒæ ¼ç·¨è¼¯å™¨ä¸­ç·¨è¼¯å¤§åž‹å…§å®¹ä¸æ˜¯ä¸€å€‹å¥½ä¸»æ„。它很å¯èƒ½è®Šå¾—緩慢和ä¸ä¾¿ã€‚最好在表單檢視或者彈出å¼ç·¨è¼¯å™¨ (從å³éµé¸å–®é–‹å•Ÿ) 中編輯此類大內容。 + + + + Foreign key for column %2 has more than %1 possible values. It's too much to display in drop down list. You need to edit value manually. + 列 %2 çš„å¤–ä¾†éµæœ‰è¶…éŽ %1 個å¯èƒ½çš„值。這太多而ä¸èƒ½é¡¯ç¤ºåœ¨ä¸‹æ‹‰æ¸…å–®ã€‚æ‚¨éœ€è¦æ‰‹å‹•編輯值。 + + + + SqlQueryModel + + + + Only one query can be executed simultaneously. + åªå…è¨±åŒæ™‚åŸ·è¡Œä¸€æ¢æŸ¥è©¢ã€‚ + + + + Cannot execute query on undefined or invalid database. + 無法執行語å¥ï¼Œå› ç‚ºè³‡æ–™åº«ç„¡æ•ˆæˆ–未定義。 + + + + Cannot execute empty query. + 無法執行空的查詢。 + + + + Uncommitted data + 未æäº¤çš„資料 + + + + There are uncommitted data changes. Do you want to proceed anyway? All uncommitted changes will be lost. + 存在未æäº¤çš„資料更改。是å¦ä»è¦ç¹¼çºŒï¼Ÿæ‰€æœ‰æœªæäº¤çš„æ›´æ”¹éƒ½å°‡ä¸Ÿå¤±ã€‚ + + + + Cannot commit the data for a cell that refers to the already closed database. + 無法從單元格中載入資料,因為它引用了已經被關閉的資料庫。 + + + + Could not begin transaction on the database. Details: %1 + 無法在此資料庫上開始事務。詳細資訊:%1 + + + + An error occurred while committing the transaction: %1 + 在æäº¤äº‹å‹™æ™‚發生錯誤:%1 + + + + An error occurred while rolling back the transaction: %1 + 在回滾事務時發生錯誤:%1 + + + + Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it. + 嘗試æäº¤ä¸å¯ç·¨è¼¯çš„單元格 (本應該無法編輯)ï¼é€™æ˜¯ä¸€å€‹éŒ¯èª¤ï¼Œè«‹å ±å‘Šã€‚ + + + + An error occurred while committing the data: %1 + 在æäº¤è³‡æ–™æ™‚發生錯誤:%1 + + + + Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. + 資料檢視中,已因列數 (%2) å°‡æ¯é è¡Œæ•¸æ¸›å° %1。 + + + + + Error while executing SQL query on database '%1': %2 + 在資料庫“%1â€åŸ·è¡Œ SQL 查詢時發生錯誤:%2 + + + + Error while loading query results: %1 + åœ¨è¼‰å…¥æŸ¥è©¢çµæžœæ™‚出錯:%1 + + + + Insert multiple rows + æ’入多行 + + + + Number of rows to insert: + è¦æ’入的行數: + + + + Delete rows + 刪除行 + + + + You're about to delete newly inserted rows that are not committed yet. Row numbers: %1 +Such deletion will be permanent. Are you sure you want to delete them? + 您å³å°‡åˆªé™¤æœªæäº¤çš„æ–°æ’入的行。行數:%1 +刪除ä¸èƒ½æ’¤éŠ·ï¼Œç¢ºå®šåˆªé™¤å—Žï¼Ÿ + + + + SqlQueryView + + + Go to referenced row in... + 轉到引用的行... + + + + Copy + 複製 + + + + Copy with headers + 帶表頭複製 + + + + Copy as... + 複製為... + + + + Paste + 貼上 + + + + Paste as... + 貼上為... + + + + Set NULL values + 設為 NULL 值 + + + + Erase values + 擦除值 + + + + Commit + æäº¤ + + + + Rollback + 回滾 + + + + Commit selected cells + æäº¤é¸ä¸­å–®å…ƒæ ¼ + + + + Rollback selected cells + 回滾é¸ä¸­å–®å…ƒæ ¼ + + + + Edit current cell inline + Edit current cell inline + + + + Define columns to sort by + å®šç¾©åˆ—æŽ’åºæ–¹å¼ + + + + Remove custom sorting + ç§»é™¤è‡ªè¨‚æŽ’åº + + + + Insert row + æ’入行 + + + + Insert multiple rows + æ’入多行 + + + + Delete selected row + 刪除已é¸è¡Œ + + + + Adjust height of rows + Adjust height of rows + + + + Increase font size + data view + Increase font size + + + + Decrease font size + data view + Decrease font size + + + + Invert selection + data view + Invert selection + + + + Edit value in editor + 在編輯器中編輯數值 + + + + Show value in a viewer + 在檢視器中顯示值 + + + + Generate query for selected cells + 為é¸ä¸­å–®å…ƒæ ¼ç”ŸæˆæŸ¥è©¢ + + + + No items selected to paste clipboard contents to. + æ²’æœ‰é¸æ“‡ç”¨ä¾†è²¼ä¸Šå‰ªè²¼ç°¿å…§å®¹çš„項。 + + + + Cannot paste data. Details: %1 + 無法貼上資料。詳情:%1 + + + + Structure of at least one table used has changed since last data was loaded. Reload the data to proceed. + è‡³å°‘ä¸€å€‹è¡¨çš„çµæ§‹åœ¨ä¸Šæ¬¡çš„è³‡æ–™è¼‰å…¥å¾Œå·²è®Šæ›´ã€‚é‡æ–°è¼‰å…¥è³‡æ–™ä»¥ç¹¼çºŒã€‚ + + + + Cannot paste to a cell. Details: %1 + 無法貼上到單元格。詳情:%1 + + + + The row is marked for deletion. + 該行已被標記為刪除。 + + + + Cannot paste to column %1. Details: %2 + 無法貼上到列 %1。詳情:%2 + + + + Go to referenced row in table '%1' + 轉至表 '%1' 中的引用的行 + + + + table '%1' + 表“%1†+ + + + Referenced row (%1) + 引用的行 (%1) + + + + Trim pasted text? + 移除貼上文字兩端的空白? + + + + The pasted text contains leading or trailing white space. Trim it automatically? + è²¼ä¸Šçš„æ–‡å­—å…©ç«¯å«æœ‰ç©ºç™½ç¬¦è™Ÿã€‚自動移除? + + + + Paste "NULL" as null value? + Paste "NULL" as null value? + + + + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + The pasted text contains "NULL" literals. Do you want to consider them as NULL values? + + + + Edit value + 編輯值 + + + + SqlTableModel + + + Error while committing new row: %1 + æäº¤æ–°è¡Œæ™‚發生錯誤:%1 + + + + Error while deleting row from table %1: %2 + 從表 %1 中刪除行時發生錯誤:%2 + + + + SqliteExtensionEditor + + + Filter extensions + 擴充套件篩é¸å™¨ + + + + Leave empty to use default function + 留空則使用é è¨­å‡½å¼ + + + + Extension file + 擴充套件檔案 + + + + Initialization function + åˆå§‹åŒ–å‡½å¼ + + + + Databases + 資料庫 + + + + Register in all databases + 在所有資料庫中註冊 + + + + Register in following databases: + 在下列資料庫中註冊: + + + + Extension manager window has uncommitted modifications. + 擴充套件管ç†è¦–窗有未æäº¤çš„æ›´æ”¹ã€‚ + + + + Extension manager + 擴充套件管ç†å™¨ + + + + Commit all extension changes + æäº¤æ‰€æœ‰æ“´å……套件更改 + + + + Rollback all extension changes + 回滾所有擴充套件更改 + + + + Add new extension + 新增新擴充套件 + + + + Remove selected extension + ç§»é™¤æ‰€é¸æ“´å……套件 + + + + Editing extensions manual + 手動編輯擴充套件 + + + + File with given path does not exist or is not readable. + 指定路徑的檔案ä¸å­˜åœ¨æˆ–無法讀å–。 + + + + Unable to load extension: %1 + 無法載入擴充套件:%1 + + + + Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. + åˆå§‹åŒ–函å¼å無效。函å¼ååªèƒ½åŒ…å«å­—æ¯æ•¸å­—字元和下劃線。 + + + + Dynamic link libraries (*.dll);;All files (*) + 動態連çµåº« (*.dll);;所有檔案 (*) + + + + Shared objects (*.so);;All files (*) + 共享庫 (*.so);;All files (*) + + + + Dynamic libraries (*.dylib);;All files (*) + 動態庫 (*.dylib);;所有檔案 (*) + + + + All files (*) + 所有檔案 (*) + + + + Open file + 開啟檔案 + + + + StatusField + + + Status + 狀態列 + + + + Copy + 複製 + + + + Clear + 清除 + + + + TableConstraintsModel + + + Type + table constraints + 型別 + + + + Details + table constraints + 詳情 + + + + Name + table constraints + å稱 + + + + TableForeignKeyPanel + + + Foreign table: + 外部表: + + + + Columns + 列 + + + + Local column + 本地列 + + + + Foreign column + 外部列 + + + + Reactions + 響應 + + + + Deferred foreign key + å»¶é²å¤–來éµç´„æŸ + + + + Named constraint + 命åçš„ç´„æŸ + + + + Constraint name + ç´„æŸå稱 + + + + Pick the foreign column. + 鏿“‡ä¸€å€‹å¤–部列。 + + + + Pick the foreign table. + 鏿“‡ä¸€å€‹å¤–部表。 + + + + Select at least one foreign column. + è«‹è‡³å°‘é¸æ“‡ä¸€å€‹å¤–部列。 + + + + Enter a name of the constraint. + 輸入一個約æŸçš„å稱。 + + + + Foreign column + table constraints + 外部列 + + + + TablePrimaryKeyAndUniquePanel + + + Columns + 列 + + + + Column + 列 + + + + Collation + å­—å…ƒåº + + + + Sort + æŽ’åº + + + + Valid only for a single column with INTEGER data type + åƒ…å°æ•´æ•¸ (INTEGER) 資料型別的單個列有效 + + + + Autoincrement + 自動éžå¢ž + + + + Named constraint + 命åçš„ç´„æŸ + + + + Constraint name + ç´„æŸå稱 + + + + On conflict + ç•¶è¡çªæ™‚ + + + + Collate + table constraints + å­—å…ƒåº + + + + Sort order + table constraints + æŽ’åºæ–¹å¼ + + + + Select at least one column. + è‡³å°‘é¸æ“‡ä¸€å€‹åˆ—。 + + + + Enter a name of the constraint. + 輸入一個約æŸçš„å稱。 + + + + TableStructureModel + + + Name + table structure columns + å稱 + + + + Data type + table structure columns + 資料型別 + + + + Primary +Key + table structure columns + ä¸»éµ + + + + Foreign +Key + table structure columns + å¤–ä¾†éµ + + + + Unique + table structure columns + 唯一 + + + + Check + table structure columns + æ¢ä»¶ + + + + Not +NULL + table structure columns + éžç©º + + + + Collate + table structure columns + å­—å…ƒåº + + + + Generated + table structure columns + ç”Ÿæˆ + + + + Default value + table structure columns + é è¨­å€¼ + + + + TableWindow + + + Structure + çµæ§‹ + + + + Table name: + 表å: + + + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + + + Data + 資料 + + + + Constraints + ç´„æŸ + + + + Indexes + 索引 + + + + Triggers + 觸發器 + + + + DDL + DDL + + + + Export table + table window + 匯出表 + + + + Import data to table + table window + 匯入資料至表 + + + + Populate table + table window + 填充表 + + + + Refresh structure + table window + 釿–°æ•´ç†çµæ§‹ + + + + Commit structure changes + table window + æäº¤çµæ§‹æ›´æ”¹ + + + + Rollback structure changes + table window + å›žæ»¾çµæ§‹æ›´æ”¹ + + + + Add column + table window + 新增列 + + + + Edit column + table window + 編輯列 + + + + + Delete column + table window + 刪除列 + + + + Move column up + table window + 上移列 + + + + Move column down + table window + 下移列 + + + + Create similar table + table window + 建立相似的表 + + + + Reset autoincrement value + table window + é‡ç½®è‡ªå‹•éžå¢žå€¼ + + + + Add table constraint + table window + æ–°å¢žè¡¨ç´„æŸ + + + + Edit table constraint + table window + ç·¨è¼¯è¡¨ç´„æŸ + + + + Delete table constraint + table window + åˆªé™¤è¡¨ç´„æŸ + + + + Move table constraint up + table window + å‘ä¸Šç§»å‹•è¡¨ç´„æŸ + + + + Move table constraint down + table window + å‘ä¸‹ä¸€ç§»å‹•è¡¨ç´„æŸ + + + + Add table primary key + table window + æ–°å¢žä¸»éµ + + + + Add table foreign key + table window + æ–°å¢žå¤–ä¾†éµ + + + + Add table unique constraint + table window + æ–°å¢žè¡¨å”¯ä¸€ç´„æŸ + + + + Add table check constraint + table window + æ–°å¢žè¡¨æª¢æŸ¥ç´„æŸ + + + + Refresh index list + table window + 釿–°æ•´ç†ç´¢å¼•清單 + + + + + Create index + table window + 建立索引 + + + + Edit index + table window + 編輯索引 + + + + Delete index + table window + 刪除索引 + + + + Refresh trigger list + table window + 釿–°æ•´ç†è§¸ç™¼å™¨æ¸…å–® + + + + + Create trigger + table window + 建立觸發器 + + + + Edit trigger + table window + 編輯觸發器 + + + + Delete trigger + table window + 刪除觸發器 + + + + Are you sure you want to delete column '%1'? + table window + 您確定è¦åˆªé™¤æ¬„ä½â€œ%1â€å—Žï¼Ÿ + + + + Following problems will take place while modifying the table. +Would you like to proceed? + table window + 修改該表時將出ç¾ä»¥ä¸‹å•題。 +是å¦ç¹¼çºŒï¼Ÿ + + + + Table modification + table window + 修改表 + + + + Could not load data for table %1. Error details: %2 + 無法載入表 %1 的資料。錯誤詳情:%2 + + + + Could not process the %1 table correctly. Unable to open a table window. + 無法正確處ç†è¡¨ %1。無法開啟表視窗。 + + + + Database + 資料庫 + + + + Could not restore window %1, because no database or table was stored in session for this window. + 無法還原視窗 %1,此視窗中沒有儲存資料庫或表的會話。 + + + + Could not restore window '%1', because no database or table was stored in session for this window. + 無法還原視窗“%1â€ï¼Œæ­¤è¦–窗中沒有儲存資料庫或表的會話。 + + + + Could not restore window '%1', because database %2 could not be resolved. + 無法還原視窗“%1â€ï¼Œç„¡æ³•è§£æžè³‡æ–™åº« %2。 + + + + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + Could not restore window '%1', because the table %2 doesn't exist in the database %3. + + + + + New table %1 + 新表 %1 + + + + Committed changes for table '%1' successfully. + æˆåŠŸæäº¤è¡¨ '%1' 的修改。 + + + + Committed changes for table '%1' (named before '%2') successfully. + æˆåŠŸæäº¤å°éŒ¶ '%1' (原å '%2') 的更改。 + + + + Could not commit table structure. Error message: %1 + table window + 無法æäº¤è¡¨çµæ§‹ã€‚錯誤資訊:%1 + + + + Reset autoincrement + é‡ç½®è‡ªå‹•éžå¢ž + + + + Are you sure you want to reset autoincrement value for table '%1'? + 您確定è¦é‡ç½®è¡¨â€œ%1â€çš„自動éžå¢žå€¼å—Žï¼Ÿ + + + + An error occurred while trying to reset autoincrement value for table '%1': %2 + é‡ç½®è¡¨â€œ%1â€çš„自動éžå¢žå€¼æ™‚出錯:%2 + + + + Autoincrement value for table '%1' has been reset successfully. + æˆåŠŸé‡ç½®è¡¨â€œ%1â€çš„自動éžå¢žå€¼ã€‚ + + + + Empty name + 空å稱 + + + + A blank name for the table is allowed in SQLite, but it is not recommended. +Are you sure you want to create a table with blank name? + SQLite å…許為表使用空白åç¨±ï¼Œä½†ä¸æŽ¨è–¦ä½¿ç”¨ç©ºç™½å稱。 +您確定è¦å»ºç«‹ä¸€å€‹ç©ºç™½å稱的表? + + + + Cannot create a table without at least one column. + 無法建立沒有任何列的表。 + + + + Cannot create table %1, if it has no primary key defined. Either uncheck the %2, or define a primary key. + 無法建立表 %1,沒有定義主éµã€‚å–æ¶ˆé¸ä¸­ %2 或者定義一個主éµã€‚ + + + + Cannot use autoincrement for primary key when %1 clause is used. Either uncheck the %2, or the autoincrement in a primary key. + 使用 %1 èªžå¥æ™‚無法為主éµä½¿ç”¨è‡ªå‹•éžå¢žã€‚å–æ¶ˆé¸ä¸­ %2,或者將一個主éµè¨­ç‚ºè‡ªå‹•éžå¢žã€‚ + + + + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + Following columns have non-strict data type: %1. Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2 + + + + Are you sure you want to delete table constraint '%1'? + table window + 您確定è¦åˆªé™¤è¡¨ç´„æŸâ€œ%1â€å—Žï¼Ÿ + + + + Delete constraint + table window + åˆªé™¤ç´„æŸ + + + + Cannot export, because no export plugin is loaded. + 無法匯出,沒有載入匯出外掛。 + + + + Cannot import, because no import plugin is loaded. + 無法匯入,沒有載入匯入外掛。 + + + + Uncommitted changes + 未æäº¤çš„æ›´æ”¹ + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have table structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + 有未æäº¤çš„çµæ§‹ä¿®æ”¹ã€‚您ä¸èƒ½ç€è¦½æˆ–ç·¨è¼¯è³‡æ–™ï¼Œç›´è‡³è¡¨çµæ§‹å®Œå·¥ã€‚ +ç«‹å³æäº¤ç¾æœ‰çµæ§‹ï¼Ÿæˆ–è€…è¿”å›žçµæ§‹é¸é …å¡ï¼Ÿ + + + + Go back to structure tab + è¿”å›žçµæ§‹é¸é …å¡ + + + + Commit modifications and browse data. + æäº¤ä¿®æ”¹ä¸¦ç€è¦½è³‡æ–™ã€‚ + + + + Name + table window indexes + å稱 + + + + Unique + table window indexes + 唯一 + + + + Columns + table window indexes + 列 + + + + Partial index condition + table window indexes + 部分索引æ¢ä»¶ + + + + Name + table window triggers + å稱 + + + + Event + table window triggers + 事件 + + + + Condition + table window triggers + æ¢ä»¶ + + + + Details + table window triggers + 詳情 + + + + Table window "%1" has uncommitted structure modifications and data. + 表視窗“%1â€æœ‰æœªæäº¤çš„çµæ§‹æ›´æ”¹èˆ‡è³‡æ–™ã€‚ + + + + Table window "%1" has uncommitted data. + 表視窗“%1â€æœ‰æœªæäº¤çš„資料。 + + + + Table window "%1" has uncommitted structure modifications. + 表視窗“%1â€æœ‰æœªæäº¤çš„çµæ§‹æ›´æ”¹ã€‚ + + + + TriggerColumnsDialog + + + Trigger columns + 觸發器列 + + + + Triggering columns: + 觸發列: + + + + Select all + å…¨é¸ + + + + Deselect all + å…¨ä¸é¸ + + + + TriggerDialog + + + + Trigger + 觸發器 + + + + On table: + 表: + + + + Action: + 動作: + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + <p>用來判斷觸發器程å¼ç¢¼æ˜¯å¦åŸ·è¡Œçš„ SQL æ¢ä»¶è¡¨ç¤ºå¼ã€‚如果æ¢ä»¶è¿”回 falseï¼Œå‰‡è§¸ç™¼å™¨ä¸æœƒå°è©²è¡Œç”Ÿæ•ˆã€‚</p> + + + + Pre-condition: + 剿æ¢ä»¶ï¼š + + + + The scope is still not fully supported by the SQLite database. + SQLite è³‡æ–™åº«ä»æœªå®Œå…¨æ”¯æ´ä½œç”¨åŸŸã€‚ + + + + Trigger name: + 觸發器å稱: + + + + When: + 當: + + + + List of columns for UPDATE OF action. + UPDATE OF 動作的列清單 + + + + Scope: + 作用域: + + + + Code: + 程å¼ç¢¼ï¼š + + + + Trigger statements to be executed. + 需è¦åŸ·è¡Œçš„è§¸ç™¼å™¨èªžå¥ + + + + DDL + DDL + + + + On view: + 檢視: + + + + Could not process trigger %1 correctly. Unable to open a trigger dialog. + 無法正確處ç†è§¸ç™¼å™¨ %1。無法開啟觸發器å°è©±æ–¹å¡Šã€‚ + + + + Enter a valid condition. + 請輸入一個有效的æ¢ä»¶ã€‚ + + + + Enter a valid trigger code. + 請輸入一個有效的觸發器程å¼ç¢¼ã€‚ + + + + Error + trigger dialog + 錯誤 + + + + An error occurred while executing SQL statements: +%1 + 執行下列 SQL èªžå¥æ™‚出錯: +%1 + + + + VersionConvertSummaryDialog + + + Database version convert + è³‡æ–™åº«ç‰ˆæœ¬è½‰æ› + + + + Following changes to the SQL statements will be made: + SQL 語å¥è®Šæ›´å¦‚下: + + + + Before + ä¹‹å‰ + + + + After + 之後 + + + + ViewWindow + + + Query + 查詢 + + + + View name: + 檢視å稱: + + + + Output column names + 輸出列å稱 + + + + + Data + 資料 + + + + Triggers + 觸發器 + + + + DDL + DDL + + + + + Could not restore window '%1', because no database or view was stored in session for this window. + 無法還原視窗“%1â€ï¼Œæ­¤è¦–窗中沒有儲存資料庫或表的會話。 + + + + Could not restore window '%1', because database %2 could not be resolved. + 無法還原視窗“%1â€ï¼Œç„¡æ³•è§£æžè³‡æ–™åº« %2。 + + + + Could not restore window '%1', because database %2 could not be open. + 無法還原視窗“%1â€ï¼Œè³‡æ–™åº« %2 沒有被開啟。 + + + + Could not restore window '%1', because the view %2 doesn't exist in the database %3. + 無法還原視窗“%1â€ï¼Œè³‡æ–™åº« %3 中ä¸å­˜åœ¨æª¢è¦– %2。 + + + + + New view %1 + 新檢視 %1 + + + + Database + 資料庫 + + + + Refresh the view + view window + 釿–°æ•´ç†æª¢è¦– + + + + Commit the view changes + view window + æäº¤æª¢è¦–更改 + + + + Rollback the view changes + view window + 回滾檢視更改 + + + + Explicit column names + 明確列å稱 + + + + Generate output column names automatically basing on result columns of the view. + åŸºæ–¼æª¢è¦–çš„çµæžœåˆ—自動生æˆè¼¸å‡ºåˆ—å稱。 + + + + Add column + view window + 新增列 + + + + Edit column + view window + 編輯列 + + + + Delete column + view window + 刪除列 + + + + Move column up + view window + 上移列 + + + + Move column down + view window + 下移列 + + + + Refresh trigger list + view window + 釿–°æ•´ç†è§¸ç™¼å™¨æ¸…å–® + + + + Create new trigger + view window + 建立新觸發器 + + + + Edit selected trigger + view window + 編輯é¸ä¸­è§¸ç™¼å™¨ + + + + Delete selected trigger + view window + 刪除é¸ä¸­è§¸ç™¼å™¨ + + + + View window "%1" has uncommitted structure modifications and data. + 檢視“%1â€æœ‰æœªæäº¤çš„çµæ§‹æ›´æ”¹å’Œè³‡æ–™ã€‚ + + + + View window "%1" has uncommitted data. + 檢視“%1â€æœ‰æœªæäº¤çš„資料。 + + + + View window "%1" has uncommitted structure modifications. + 檢視“%1â€æœ‰æœªæäº¤çš„çµæ§‹æ›´æ”¹ã€‚ + + + + Could not load data for view %1. Error details: %2 + 無法載入檢視 %1 的資料。錯誤詳情:%2 + + + + Uncommitted changes + 未æäº¤çš„æ›´æ”¹ + + + + There are uncommitted structure modifications. You cannot browse or edit data until you have the view structure settled. +Do you want to commit the structure, or do you want to go back to the structure tab? + 有未æäº¤çš„çµæ§‹ä¿®æ”¹ã€‚您ä¸èƒ½ç€è¦½æˆ–ç·¨è¼¯è³‡æ–™ï¼Œç›´è‡³è¡¨çµæ§‹å®Œå·¥ã€‚ +ç«‹å³æäº¤ç¾æœ‰çµæ§‹ï¼Ÿæˆ–è€…è¿”å›žçµæ§‹é¸é …å¡ï¼Ÿ + + + + Go back to structure tab + è¿”å›žçµæ§‹é¸é …å¡ + + + + Commit modifications and browse data. + æäº¤æ›´æ”¹ä¸¦ç€è¦½è³‡æ–™ã€‚ + + + + View '%1' was committed successfully. + æˆåŠŸæäº¤æª¢è¦–“%1â€ã€‚ + + + + Committed changes for view '%1' successfully. + æˆåŠŸæäº¤å°æª¢è¦–“%1â€çš„æ›´æ”¹ã€‚ + + + + Committed changes for view '%1' (named before '%2') successfully. + æˆåŠŸæäº¤å°æª¢è¦–“%1†(原å“%2â€)的更改。 + + + + Could not commit view changes. Error message: %1 + view window + 無法æäº¤æª¢è¦–更改。錯誤資訊:%1 + + + + Override columns + 覆蓋列 + + + + Currently defined columns will be overriden. Do you want to continue? + ç•¶å‰å®šç¾©çš„列將被覆蓋。您è¦ç¹¼çºŒå—Žï¼Ÿ + + + + Could not determinate columns returned from the view. The query is problably incomplete or contains errors. + 無法確定檢視所返回的列。查詢å¯èƒ½ä¸å®Œæ•´æˆ–包å«éŒ¯èª¤ã€‚ + + + + Name + view window triggers + å稱 + + + + Instead of + view window triggers + Instead of + + + + Condition + view window triggers + æ¢ä»¶ + + + + Details + table window triggers + 詳情 + + + + Could not process the %1 view correctly. Unable to open a view window. + ç„¡æ³•æ­£ç¢ºè™•ç†æª¢è¦– %1。無法開啟檢視視窗。 + + + + Empty name + 空å稱 + + + + A blank name for the view is allowed in SQLite, but it is not recommended. +Are you sure you want to create a view with blank name? + SQLite å…許為檢視使用空白åç¨±ï¼Œä½†ä¸æŽ¨è–¦ä½¿ç”¨ç©ºç™½å稱。 +您確定è¦å»ºç«‹ä¸€å€‹ç©ºç™½å稱的檢視? + + + + The SELECT statement could not be parsed. Please correct the query and retry. +Details: %1 + SELECT 語å¥è§£æžå¤±æ•—。請更正查詢並é‡è©¦ã€‚ +詳情:%1 + + + + The view could not be modified due to internal SQLiteStudio error. Please report this! + å›  SQLiteStudio 內部錯誤,無法修改該檢視。請報告該å•é¡Œï¼ + + + + The view code could not be parsed properly for execution. This is a SQLiteStudio's bug. Please report it. + 用於執行的檢視程å¼ç¢¼ç„¡æ³•正確解æžã€‚這是 SQLiteStudio çš„ bug,請報告。 + + + + Following problems will take place while modifying the view. +Would you like to proceed? + view window + 修改該檢視時將出ç¾ä»¥ä¸‹å•題。 +是å¦ç¹¼çºŒï¼Ÿ + + + + View modification + view window + 檢視更改 + + + + WidgetCover + + + Interrupt + åœæ­¢ + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/uiconfig.cpp b/SQLiteStudio3/guiSQLiteStudio/uiconfig.cpp index c2dfa8f..1d7d556 100644 --- a/SQLiteStudio3/guiSQLiteStudio/uiconfig.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/uiconfig.cpp @@ -1,9 +1,19 @@ #include "uiconfig.h" +#include "style.h" #include #include #include #include #include +#include + +#define DEFINE_COLOR_HELPER_FN(COLOR_NAME) \ + QColor get##COLOR_NAME() \ + { \ + return CFG_UI.Colors.COLOR_NAME##Custom.get() ? \ + CFG_UI.Colors.COLOR_NAME.get() : \ + getDefault##COLOR_NAME().value(); \ + } namespace Cfg { @@ -43,6 +53,78 @@ namespace Cfg return font; } + QVariant getDefaultSyntaxParenthesisBg() + { + return STYLE->standardPalette().windowText(); + } + + QVariant getDefaultSyntaxParenthesisFg() + { + return STYLE->standardPalette().window(); + } + + QVariant getDefaultSyntaxCurrentLineBg() + { + return STYLE->extendedPalette().editorLineBase(); + } + + QVariant getDefaultSyntaxCurrentQueryBg() + { + return STYLE->extendedPalette().editorCurrentQueryBase(); + } + + QVariant getDefaultSyntaxValidObject() + { + return STYLE->standardPalette().link(); + } + + QVariant getDefaultSyntaxForeground() + { + return STYLE->standardPalette().text(); + } + + QVariant getDefaultSyntaxStringFg() + { + return STYLE->extendedPalette().editorString(); + } + + QVariant getDefaultSyntaxKeywordFg() + { + return STYLE->standardPalette().windowText(); + } + + QVariant getDefaultSyntaxBindParamFg() + { + return STYLE->standardPalette().linkVisited(); + } + + QVariant getDefaultSyntaxBlobFg() + { + return STYLE->standardPalette().text(); + } + + QVariant getDefaultSyntaxCommentFg() + { + return STYLE->standardPalette().dark(); + } + + QVariant getDefaultSyntaxNumberFg() + { + return STYLE->standardPalette().text(); + } + + DEFINE_COLOR_HELPER_FN(SyntaxParenthesisBg) + DEFINE_COLOR_HELPER_FN(SyntaxParenthesisFg) + DEFINE_COLOR_HELPER_FN(SyntaxCurrentLineBg) + DEFINE_COLOR_HELPER_FN(SyntaxCurrentQueryBg) + DEFINE_COLOR_HELPER_FN(SyntaxValidObject) + DEFINE_COLOR_HELPER_FN(SyntaxForeground) + DEFINE_COLOR_HELPER_FN(SyntaxStringFg) + DEFINE_COLOR_HELPER_FN(SyntaxKeywordFg) + DEFINE_COLOR_HELPER_FN(SyntaxBindParamFg) + DEFINE_COLOR_HELPER_FN(SyntaxBlobFg) + DEFINE_COLOR_HELPER_FN(SyntaxCommentFg) + DEFINE_COLOR_HELPER_FN(SyntaxNumberFg) } CFG_DEFINE(Ui) diff --git a/SQLiteStudio3/guiSQLiteStudio/uiconfig.h b/SQLiteStudio3/guiSQLiteStudio/uiconfig.h index 8f16a1b..45fb2c2 100644 --- a/SQLiteStudio3/guiSQLiteStudio/uiconfig.h +++ b/SQLiteStudio3/guiSQLiteStudio/uiconfig.h @@ -7,12 +7,39 @@ #include #include +#define CFG_UI_CATEGORIES(Type,Body) _CFG_CATEGORIES_WITH_METANAME_AND_TITLE(Type,Body,"",QString(),GUI_API_EXPORT) + namespace Cfg { GUI_API_EXPORT QVariant getStyleDefaultValue(); GUI_API_EXPORT QVariant getDefaultTextEditorFont(); GUI_API_EXPORT QVariant getDefaultItemViewFont(); GUI_API_EXPORT QVariant getDefaultDbTreeLabelFont(); + QVariant getDefaultSyntaxParenthesisBg(); + QVariant getDefaultSyntaxParenthesisFg(); + QVariant getDefaultSyntaxCurrentLineBg(); + QVariant getDefaultSyntaxCurrentQueryBg(); + QVariant getDefaultSyntaxValidObject(); + QVariant getDefaultSyntaxForeground(); + QVariant getDefaultSyntaxStringFg(); + QVariant getDefaultSyntaxKeywordFg(); + QVariant getDefaultSyntaxBindParamFg(); + QVariant getDefaultSyntaxBlobFg(); + QVariant getDefaultSyntaxCommentFg(); + QVariant getDefaultSyntaxNumberFg(); + GUI_API_EXPORT QColor getSyntaxParenthesisBg(); + GUI_API_EXPORT QColor getSyntaxParenthesisFg(); + GUI_API_EXPORT QColor getSyntaxCurrentLineBg(); + GUI_API_EXPORT QColor getSyntaxCurrentQueryBg(); + GUI_API_EXPORT QColor getSyntaxValidObject(); + GUI_API_EXPORT QColor getSyntaxForeground(); + GUI_API_EXPORT QColor getSyntaxStringFg(); + GUI_API_EXPORT QColor getSyntaxKeywordFg(); + GUI_API_EXPORT QColor getSyntaxBindParamFg(); + GUI_API_EXPORT QColor getSyntaxBlobFg(); + GUI_API_EXPORT QColor getSyntaxCommentFg(); + GUI_API_EXPORT QColor getSyntaxNumberFg(); + typedef QHash Session; typedef QHash DataEditorsOrder; enum InsertRowPlacement @@ -23,7 +50,7 @@ namespace Cfg }; } -CFG_CATEGORIES(Ui, +CFG_UI_CATEGORIES(Ui, CFG_CATEGORY(Fonts, CFG_ENTRY(QFont, SqlEditor, &Cfg::getDefaultTextEditorFont) CFG_ENTRY(QFont, DataView, &Cfg::getDefaultItemViewFont) @@ -32,48 +59,80 @@ CFG_CATEGORIES(Ui, CFG_ENTRY(QFont, StatusField, &Cfg::getDefaultItemViewFont) ) + CFG_CATEGORY(Colors, + CFG_ENTRY(QColor, SyntaxParenthesisBg, &Cfg::getDefaultSyntaxParenthesisBg) + CFG_ENTRY(bool, SyntaxParenthesisBgCustom, false) + CFG_ENTRY(QColor, SyntaxParenthesisFg, &Cfg::getDefaultSyntaxParenthesisFg) + CFG_ENTRY(bool, SyntaxParenthesisFgCustom, false) + CFG_ENTRY(QColor, SyntaxCurrentLineBg, &Cfg::getDefaultSyntaxCurrentLineBg) + CFG_ENTRY(bool, SyntaxCurrentLineBgCustom, false) + CFG_ENTRY(QColor, SyntaxCurrentQueryBg, &Cfg::getDefaultSyntaxCurrentQueryBg) + CFG_ENTRY(bool, SyntaxCurrentQueryBgCustom, false) + CFG_ENTRY(QColor, SyntaxValidObject, &Cfg::getDefaultSyntaxValidObject) + CFG_ENTRY(bool, SyntaxValidObjectCustom, false) + CFG_ENTRY(QColor, SyntaxForeground, &Cfg::getDefaultSyntaxForeground) + CFG_ENTRY(bool, SyntaxForegroundCustom, false) + CFG_ENTRY(QColor, SyntaxStringFg, &Cfg::getDefaultSyntaxStringFg) + CFG_ENTRY(bool, SyntaxStringFgCustom, false) + CFG_ENTRY(QColor, SyntaxKeywordFg, &Cfg::getDefaultSyntaxKeywordFg) + CFG_ENTRY(bool, SyntaxKeywordFgCustom, false) + CFG_ENTRY(QColor, SyntaxBindParamFg, &Cfg::getDefaultSyntaxBindParamFg) + CFG_ENTRY(bool, SyntaxBindParamFgCustom, false) + CFG_ENTRY(QColor, SyntaxBlobFg, &Cfg::getDefaultSyntaxBlobFg) + CFG_ENTRY(bool, SyntaxBlobFgCustom, false) + CFG_ENTRY(QColor, SyntaxCommentFg, &Cfg::getDefaultSyntaxCommentFg) + CFG_ENTRY(bool, SyntaxCommentFgCustom, false) + CFG_ENTRY(QColor, SyntaxNumberFg, &Cfg::getDefaultSyntaxNumberFg) + CFG_ENTRY(bool, SyntaxNumberFgCustom, false) + ) + CFG_CATEGORY(DbList, ) CFG_CATEGORY(General, - CFG_ENTRY(QString, DataViewTabs, QString()) - CFG_ENTRY(QString, SqlEditorTabs, QString()) - CFG_ENTRY(QString, SqlEditorDbListOrder, "LikeDbTree") - CFG_ENTRY(bool, ExpandTables, true) - CFG_ENTRY(bool, ExpandViews, true) - CFG_ENTRY(bool, SortObjects, true) - CFG_ENTRY(bool, SortColumns, false) - CFG_ENTRY(bool, ExecuteCurrentQueryOnly, true) - CFG_ENTRY(bool, ShowSystemObjects, false) - CFG_ENTRY(bool, ShowDbTreeLabels, true) // any labels at all - CFG_ENTRY(bool, ShowRegularTableLabels, false) - CFG_ENTRY(bool, ShowVirtualTableLabels, true) - CFG_ENTRY(int, NumberOfRowsPerPage, 1000) - CFG_ENTRY(bool, LimitRowsForManyColumns, true) - CFG_ENTRY(QString, Style, &Cfg::getStyleDefaultValue) - CFG_ENTRY(Cfg::Session, Session, Cfg::Session()) - CFG_ENTRY(bool, AllowMultipleSessions, false) - CFG_ENTRY(bool, RestoreSession, true) - CFG_ENTRY(bool, DontShowDdlPreview, false) - CFG_ENTRY(bool, OpenTablesOnData, false) - CFG_ENTRY(bool, DataTabAsFirstInTables, false) - CFG_ENTRY(bool, OpenViewsOnData, false) - CFG_ENTRY(bool, DataTabAsFirstInViews, false) - CFG_ENTRY(bool, AutoOpenStatusField, true) - CFG_ENTRY(bool, NewDbNotPermanentByDefault, false) - CFG_ENTRY(bool, BypassDbDialogWhenDropped, false) - CFG_ENTRY(Cfg::DataEditorsOrder, DataEditorsOrder, Cfg::DataEditorsOrder()) - CFG_ENTRY(QString, FileDialogLastPath, QString()) - CFG_ENTRY(int, MaxInitialColumnWith, 600) - CFG_ENTRY(bool, LanguageAsked, false) - CFG_ENTRY(bool, OpenMaximized, true) - CFG_ENTRY(QString, DockLayout, "vertical") - CFG_ENTRY(QString, CustomCss, QString()) - CFG_ENTRY(bool, CompactLayout, true) - CFG_ENTRY(int, InsertRowPlacement, Cfg::BEFORE_CURRENT) - CFG_ENTRY(bool, ShowDataViewTooltips, true) - CFG_ENTRY(bool, KeepNullWhenEmptyValue, true) - CFG_ENTRY(bool, UseDefaultValueForNull, false) + CFG_ENTRY(QString, DataViewTabs, QString()) + CFG_ENTRY(QString, SqlEditorTabs, QString()) + CFG_ENTRY(QString, SqlEditorDbListOrder, "LikeDbTree") + CFG_ENTRY(bool, SqlEditorWrapWords, false) + CFG_ENTRY(bool, SqlEditorCurrQueryHighlight, true) + CFG_ENTRY(bool, ExpandTables, true) + CFG_ENTRY(bool, ExpandViews, true) + CFG_ENTRY(bool, SortObjects, true) + CFG_ENTRY(bool, SortColumns, false) + CFG_ENTRY(bool, ExecuteCurrentQueryOnly, true) + CFG_ENTRY(bool, ShowSystemObjects, false) + CFG_ENTRY(bool, ShowDbTreeLabels, true) // any labels at all + CFG_ENTRY(bool, ShowRegularTableLabels, false) + CFG_ENTRY(bool, ShowVirtualTableLabels, true) + CFG_ENTRY(int, NumberOfRowsPerPage, 1000) + CFG_ENTRY(bool, LimitRowsForManyColumns, true) + CFG_ENTRY(QString, Style, &Cfg::getStyleDefaultValue) + CFG_ENTRY(Cfg::Session, Session, Cfg::Session()) + CFG_ENTRY(bool, AllowMultipleSessions, false) + CFG_ENTRY(bool, RestoreSession, true) + CFG_ENTRY(bool, DontShowDdlPreview, false) + CFG_ENTRY(bool, OpenTablesOnData, false) + CFG_ENTRY(bool, DataTabAsFirstInTables, false) + CFG_ENTRY(bool, OpenViewsOnData, false) + CFG_ENTRY(bool, DataTabAsFirstInViews, false) + CFG_ENTRY(bool, AutoOpenStatusField, true) + CFG_ENTRY(bool, NewDbNotPermanentByDefault, false) + CFG_ENTRY(bool, BypassDbDialogWhenDropped, false) + CFG_ENTRY(Cfg::DataEditorsOrder, DataEditorsOrder, Cfg::DataEditorsOrder()) + CFG_ENTRY(QString, FileDialogLastPath, QString()) + CFG_ENTRY(int, MaxInitialColumnWith, 600) + CFG_ENTRY(bool, EnlargeColumnForValue, true) + CFG_ENTRY(bool, ColumnWidthForName, false) + CFG_ENTRY(bool, LanguageAsked, false) + CFG_ENTRY(bool, OpenMaximized, true) + CFG_ENTRY(QString, DockLayout, "vertical") + CFG_ENTRY(QString, CustomCss, QString()) + CFG_ENTRY(bool, CompactLayout, true) + CFG_ENTRY(int, InsertRowPlacement, Cfg::BEFORE_CURRENT) + CFG_ENTRY(bool, ShowDataViewTooltips, true) + CFG_ENTRY(bool, KeepNullWhenEmptyValue, true) + CFG_ENTRY(bool, UseDefaultValueForNull, false) + CFG_ENTRY(bool, UseSciFormatForDoubles, false) ) ) diff --git a/SQLiteStudio3/guiSQLiteStudio/uiutils.cpp b/SQLiteStudio3/guiSQLiteStudio/uiutils.cpp index fbdc4b3..a73b8ee 100644 --- a/SQLiteStudio3/guiSQLiteStudio/uiutils.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/uiutils.cpp @@ -17,30 +17,46 @@ #include #include #include - -const QStringList pageSizes = { - "A4", "B5", "Letter", "Legal", "Executive", "A0", "A1", "A2", "A3", "A5", "A6", "A7", "A8", "A9", "B0", "B1", - "B10", "B2", "B3", "B4", "B6", "B7", "B8", "B9", "C5E", "Comm10E", "DLE", "Folio", "Ledger", "Tabloid", "Custom" +#include +#include + +const QList pageSizeIds = { + QPageSize::A4, QPageSize::B5, QPageSize::Letter, QPageSize::Legal, QPageSize::Executive, QPageSize::A0, QPageSize::A1, + QPageSize::A2, QPageSize::A3, QPageSize::A5, QPageSize::A6, QPageSize::A7, QPageSize::A8, QPageSize::A9, QPageSize::B0, + QPageSize::B1, QPageSize::B10, QPageSize::B2, QPageSize::B3, QPageSize::B4, QPageSize::B6, QPageSize::B7, QPageSize::B8, + QPageSize::B9, QPageSize::C5E, QPageSize::Comm10E, QPageSize::DLE, QPageSize::Folio, QPageSize::Ledger, QPageSize::Tabloid, + QPageSize::Custom }; +const QStringList pageSizes = map(pageSizeIds, [](QPageSize::PageSizeId id) -> QString +{ + return QPageSize::name(id); +}); + const QStringList pageSizesWithDimensions; -QString getDbPath(bool newFileMode, const QString &startWith) +QString getDbPath(const QString &startWith) { QString dir = startWith; if (dir.isNull()) dir = CFG->get("dialogCache", "lastDbDir").toString(); - QStringList filters; - filters += QObject::tr("All SQLite databases")+" (*.db *.sdb *.sqlite *.db3 *.s3db *.sqlite3 *.sl3)"; - filters += "SQLite3 (*.db3 *.s3db *.sqlite3 *.sl3)"; - filters += QObject::tr("All files")+" (*)"; - QString filter = filters.join(";;"); + QStringList filters({ + QObject::tr("All SQLite databases")+" (*.db *.sdb *.sqlite *.db3 *.s3db *.sqlite3 *.sl3)", + "SQLite3 (*.db3 *.s3db *.sqlite3 *.sl3)", + QObject::tr("All files")+" (*)" + }); + + QFileDialog dialog(nullptr, QObject::tr("Select database file"), dir, QString()); + dialog.setAcceptMode(QFileDialog::AcceptOpen); + dialog.setOption(QFileDialog::DontConfirmOverwrite, true); + dialog.setLabelText(QFileDialog::Accept, QObject::tr("Select")); + dialog.setLabelText(QFileDialog::FileType, QObject::tr("File type")); + dialog.setNameFilters(filters); + if (dialog.exec() != QDialog::Accepted || dialog.selectedFiles().empty()) + return QString(); - if (newFileMode) - return QFileDialog::getSaveFileName(0, QObject::tr("Database file"), dir, filter, &filters[0], QFileDialog::DontConfirmOverwrite); - else - return QFileDialog::getOpenFileName(0, QObject::tr("Database file"), dir, filter, &filters[0]); + return dialog.selectedFiles().constFirst(); } void setValidState(QWidget *widget, bool valid, const QString& message) @@ -81,23 +97,14 @@ void setValidStateTooltip(QWidget* widget, const QString& tip) INDICATOR(widget)->setVisible(widget->isEnabled(), tip); } -QString convertPageSize(QPagedPaintDevice::PageSize size) +QString convertPageSize(QPageSize::PageSizeId size) { - const int pageSizesSize = pageSizes.size(); - - int idx = static_cast(size); - if (idx < 0 || idx >= pageSizesSize) - { - qDebug() << "Asked to convertPageSize() with page side enum value out of range:" << idx; - return QString(); - } - - return pageSizes[idx]; + return QPageSize::name(size); } -QPagedPaintDevice::PageSize convertPageSize(const QString& size) +QPageSize convertPageSize(const QString& size) { - return static_cast(indexOf(pageSizes, size, Qt::CaseInsensitive)); + return QPageSize(static_cast(indexOf(pageSizes, size, Qt::CaseInsensitive))); } const QStringList& getAllPageSizes() @@ -146,3 +153,21 @@ QBrush styleEditorLineColor() return palette.alternateBase(); } + +void fixToolbarTooltips(QToolBar* toolbar) +{ + for (QAction*& action : toolbar->actions()) + { + QToolButton* button = dynamic_cast(toolbar->widgetForAction(action)); + if (!button) + continue; + + QString text = action->text(); + text = text.replace(QRegExp("\\s?\\(&.\\)$"),""); // issue #4261, for Chinese + text = text.replace("&", ""); // issue #4218 + if (!action->shortcut().isEmpty()) + text += QString(" (%1)").arg(action->shortcut().toString()); + + button->setToolTip(text); + } +} diff --git a/SQLiteStudio3/guiSQLiteStudio/uiutils.h b/SQLiteStudio3/guiSQLiteStudio/uiutils.h index 8163cd1..324ed9e 100644 --- a/SQLiteStudio3/guiSQLiteStudio/uiutils.h +++ b/SQLiteStudio3/guiSQLiteStudio/uiutils.h @@ -6,21 +6,23 @@ #include class QWidget; +class QToolBar; -GUI_API_EXPORT QString getDbPath(bool newFileMode, const QString& startWith = QString()); +GUI_API_EXPORT QString getDbPath(const QString& startWith = QString()); GUI_API_EXPORT void setValidState(QWidget* widget, bool valid, const QString& message = QString()); GUI_API_EXPORT void setValidStateWihtTooltip(QWidget* widget, const QString& tooltip, bool valid, const QString& message = QString()); GUI_API_EXPORT void setValidStateWarning(QWidget* widget, const QString& warning); GUI_API_EXPORT void setValidStateInfo(QWidget* widget, const QString& info); GUI_API_EXPORT void setValidStateTooltip(QWidget* widget, const QString& tip); GUI_API_EXPORT const QStringList& getAllPageSizes(); -GUI_API_EXPORT QString convertPageSize(QPagedPaintDevice::PageSize size); -GUI_API_EXPORT QPagedPaintDevice::PageSize convertPageSize(const QString& size); +GUI_API_EXPORT QString convertPageSize(QPageSize::PageSizeId size); +GUI_API_EXPORT QPageSize convertPageSize(const QString& size); GUI_API_EXPORT QPixmap addOpacity(const QPixmap& input, float opacity); GUI_API_EXPORT void limitDialogWidth(QDialog* dialog); GUI_API_EXPORT void fixTextCursorSelectedText(QString& text); GUI_API_EXPORT QColor styleSyntaxStringColor(); GUI_API_EXPORT QBrush styleEditorLineColor(); +GUI_API_EXPORT void fixToolbarTooltips(QToolBar* toolbar); #define UI_PROP_COLUMN "column_name" diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.cpp new file mode 100644 index 0000000..82cade7 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.cpp @@ -0,0 +1,350 @@ +#include "codesnippeteditor.h" +#include "common/unused.h" +#include "ui_codesnippeteditor.h" +#include "uiconfig.h" +#include "windows/codesnippeteditormodel.h" +#include "sqlitestudio.h" +#include "iconmanager.h" +#include "uiutils.h" +#include "codesnippeteditormodel.h" +#include +#include +#include +#include + +CFG_KEYS_DEFINE(CodeSnippetEditor) + +CodeSnippetEditor::CodeSnippetEditor(QWidget *parent) : + MdiChild(parent), + ui(new Ui::CodeSnippetEditor) +{ + init(); +} + +CodeSnippetEditor::~CodeSnippetEditor() +{ + delete ui; +} + +bool CodeSnippetEditor::restoreSessionNextTime() +{ + return false; +} + +bool CodeSnippetEditor::isUncommitted() const +{ + return model->isModified() || currentModified; +} + +QString CodeSnippetEditor::getQuitUncommittedConfirmMessage() const +{ + return tr("Code Snippets editor window has uncommitted modifications."); +} + +QVariant CodeSnippetEditor::saveSession() +{ + return QVariant(); +} + +bool CodeSnippetEditor::restoreSession(const QVariant& sessionValue) +{ + UNUSED(sessionValue); + return true; +} + +Icon* CodeSnippetEditor::getIconNameForMdiWindow() +{ + return ICONS.CODE_SNIPPET; +} + +QString CodeSnippetEditor::getTitleForMdiWindow() +{ + return tr("Code Snippets editor"); +} + +void CodeSnippetEditor::createActions() +{ + createAction(COMMIT, ICONS.COMMIT, tr("Commit all snippet changes"), this, SLOT(commit()), ui->toolBar, this); + createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all snippet changes"), this, SLOT(rollback()), ui->toolBar, this); + ui->toolBar->addSeparator(); + createAction(ADD, ICONS.NEW_FUNCTION, tr("Create new snippet"), this, SLOT(newSnippet()), ui->toolBar, this); + createAction(DELETE, ICONS.DELETE_FUNCTION, tr("Delete selected snippet"), this, SLOT(deleteSnippet()), ui->toolBar, this); + ui->toolBar->addSeparator(); + createAction(MOVE_UP, ICONS.MOVE_UP, tr("Move the snippet up"), this, SLOT(moveSnippetUp()), ui->toolBar, this); + createAction(MOVE_DOWN, ICONS.MOVE_DOWN, tr("Move the snippet down"), this, SLOT(moveSnippetDown()), ui->toolBar, this); + ui->toolBar->addSeparator(); + createAction(HELP, ICONS.HELP, tr("Code snippets manual"), this, SLOT(help()), ui->toolBar, this); + +#ifdef Q_OS_MACX + QStyle *fusion = QStyleFactory::create("Fusion"); + ui->toolBar->setStyle(fusion); +#endif +} + +void CodeSnippetEditor::setupDefShortcuts() +{ + // Widget context + setShortcutContext({COMMIT, ROLLBACK}, Qt::WidgetWithChildrenShortcut); + BIND_SHORTCUTS(CodeSnippetEditor, Action); +} + +QToolBar* CodeSnippetEditor::getToolBar(int toolbar) const +{ + UNUSED(toolbar); + return ui->toolBar; +} + +void CodeSnippetEditor::init() +{ + ui->setupUi(this); + clearEdits(); + + ui->mainCodeEdit->setFont(CFG_UI.Fonts.SqlEditor.get()); + + model = new CodeSnippetEditorModel(this); + snippetFilterModel = new QSortFilterProxyModel(this); + snippetFilterModel->setSourceModel(model); + ui->list->setModel(snippetFilterModel); + + initActions(); + + connect(ui->list->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(snippetSelected(QItemSelection,QItemSelection))); + connect(ui->list->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(updateState())); + connect(ui->assistantShortcutEdit, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(updateModified())); + connect(ui->mainCodeEdit, SIGNAL(textChanged()), this, SLOT(updateModified())); + connect(ui->nameEdit, SIGNAL(textChanged(QString)), this, SLOT(updateModified())); + connect(CFG_UI.Fonts.SqlEditor, SIGNAL(changed(QVariant)), this, SLOT(changeFont(QVariant))); + connect(ui->clearAssistantShortcutButton, SIGNAL(clicked(bool)), this, SLOT(clearAssistantShortcutPressed())); + + model->setData(CODESNIPPETS->getSnippets()); + + updateCurrentSnippetState(); +} + +int CodeSnippetEditor::getCurrentSnippetRow() const +{ + QModelIndexList idxList = ui->list->selectionModel()->selectedIndexes(); + if (idxList.size() == 0) + return -1; + + return idxList.first().row(); +} + +void CodeSnippetEditor::snippetDeselected(int row) +{ + model->setName(row, ui->nameEdit->text()); + model->setCode(row, ui->mainCodeEdit->toPlainText()); + model->setHotkey(row, ui->assistantShortcutEdit->keySequence()); + model->setModified(row, currentModified); + model->validateNames(); +} + +void CodeSnippetEditor::snippetSelected(int row) +{ + updatesForSelection = true; + ui->nameEdit->setText(model->getName(row)); + ui->mainCodeEdit->setPlainText(model->getCode(row)); + ui->assistantShortcutEdit->setKeySequence(model->getHotkey(row)); + + updatesForSelection = false; + currentModified = model->isModified(row); + + updateCurrentSnippetState(); +} + +void CodeSnippetEditor::selectSnippet(int row, bool skipModelUpdates) +{ + if (!model->isValidRowIndex(row)) + return; + + if (skipModelUpdates) + skipModelUpdatesDuringSelection = true; + + ui->list->selectionModel()->setCurrentIndex(model->index(row), QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent); + + if (skipModelUpdates) + skipModelUpdatesDuringSelection = false; +} + +void CodeSnippetEditor::clearEdits() +{ + ui->nameEdit->clear(); + ui->assistantShortcutEdit->clear(); + ui->mainCodeEdit->setPlainText(QString()); +} + +void CodeSnippetEditor::commit() +{ + int row = getCurrentSnippetRow(); + if (model->isValidRowIndex(row)) + snippetDeselected(row); + + QList snippets = model->generateSnippets(); + + CODESNIPPETS->setSnippets(snippets); + model->clearModified(); + currentModified = false; + + if (model->isValidRowIndex(row)) + selectSnippet(row); + + updateState(); +} + +void CodeSnippetEditor::rollback() +{ + int selectedBefore = getCurrentSnippetRow(); + + model->setData(CODESNIPPETS->getSnippets()); + currentModified = false; + clearEdits(); + + if (model->isValidRowIndex(selectedBefore)) + selectSnippet(selectedBefore); + + updateState(); +} + +void CodeSnippetEditor::newSnippet() +{ + CodeSnippetManager::CodeSnippet* snip = new CodeSnippetManager::CodeSnippet(); + snip->name = generateUniqueName("snippet", model->getSnippetNames()); + + model->addSnippet(snip); + + selectSnippet(model->rowCount() - 1); +} + +void CodeSnippetEditor::deleteSnippet() +{ + int row = getCurrentSnippetRow(); + model->deleteSnippet(row); + clearEdits(); + + row = getCurrentSnippetRow(); + if (model->isValidRowIndex(row)) + snippetSelected(row); + + updateState(); +} + +void CodeSnippetEditor::moveSnippetUp() +{ + int row = getCurrentSnippetRow(); + int newRow = model->moveUp(row); + if (row != newRow) + selectSnippet(newRow, true); +} + +void CodeSnippetEditor::moveSnippetDown() +{ + int row = getCurrentSnippetRow(); + int newRow = model->moveDown(row); + if (row != newRow) + selectSnippet(newRow, true); +} + +void CodeSnippetEditor::updateModified() +{ + if (updatesForSelection) + return; + + int row = getCurrentSnippetRow(); + if (model->isValidRowIndex(row)) + { + bool nameDiff = model->getName(row) != ui->nameEdit->text(); + bool codeDiff = model->getCode(row) != ui->mainCodeEdit->toPlainText(); + bool hotkeyDiff = model->getHotkey(row) != ui->assistantShortcutEdit->keySequence(); + currentModified = (nameDiff || codeDiff || hotkeyDiff); + } + + updateCurrentSnippetState(); +} + +void CodeSnippetEditor::updateCurrentSnippetState() +{ + int row = getCurrentSnippetRow(); + bool validRow = model->isValidRowIndex(row); + ui->rightWidget->setEnabled(validRow); + if (!validRow) + { + setValidState(ui->nameEdit, true); + setValidState(ui->mainCodeEdit, true); + setValidState(ui->assistantShortcutEdit, true); + updateState(); + return; + } + + QString name = ui->nameEdit->text(); + bool nameOk = !name.trimmed().isEmpty() && model->isAllowedName(row, name); + setValidState(ui->nameEdit, nameOk, tr("Enter a non-empty, unique name of the snippet.")); + + bool codeOk = !ui->mainCodeEdit->toPlainText().trimmed().isEmpty(); + setValidState(ui->mainCodeEdit, codeOk, tr("Enter a non-empty snippet content.")); + + QKeySequence assistantHotkey = ui->assistantShortcutEdit->keySequence(); + bool hotkeyOk = assistantHotkey.isEmpty() || model->isAllowedHotkey(row, assistantHotkey); + setValidState(ui->assistantShortcutWidget, hotkeyOk, tr("This hotkey is not unique in context of a code assistant.")); + + model->setValid(row, codeOk && nameOk && hotkeyOk); + updateState(); +} + +void CodeSnippetEditor::updateState() +{ + bool modified = model->isModified() || currentModified; + bool valid = model->isValid(); + + actionMap[COMMIT]->setEnabled(modified && valid); + actionMap[ROLLBACK]->setEnabled(modified); + actionMap[DELETE]->setEnabled(ui->list->selectionModel()->selectedIndexes().size() > 0); +} + +void CodeSnippetEditor::snippetSelected(const QItemSelection& selected, const QItemSelection& deselected) +{ + if (skipModelUpdatesDuringSelection) + return; + + int deselCnt = deselected.indexes().size(); + int selCnt = selected.indexes().size(); + + if (deselCnt > 0) + snippetDeselected(deselected.indexes().first().row()); + + if (selCnt > 0) + snippetSelected(selected.indexes().first().row()); + + if (deselCnt > 0 && selCnt == 0) + { + currentModified = false; + clearEdits(); + } +} + +void CodeSnippetEditor::applyFilter(const QString& value) +{ + // The selection hack (clear & set) below is described in more details in FunctionsEditor::applyFilter(). + int row = getCurrentSnippetRow(); + ui->list->selectionModel()->clearSelection(); + + snippetFilterModel->setFilterFixedString(value); + + selectSnippet(row); +} + +void CodeSnippetEditor::changeFont(const QVariant& font) +{ + ui->mainCodeEdit->setFont(font.value()); +} + +void CodeSnippetEditor::clearAssistantShortcutPressed() +{ + ui->assistantShortcutEdit->clear(); + ui->assistantShortcutEdit->setFocus(); +} + +void CodeSnippetEditor::help() +{ + static const QString url = QStringLiteral("https://github.com/pawelsalawa/sqlitestudio/wiki/User_Manual#code-snippets"); + QDesktopServices::openUrl(QUrl(url, QUrl::StrictMode)); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.h b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.h new file mode 100644 index 0000000..2247960 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.h @@ -0,0 +1,91 @@ +#ifndef CODESNIPPETEDITOR_H +#define CODESNIPPETEDITOR_H + +#include "mdichild.h" +#include + +namespace Ui { +class CodeSnippetEditor; +} + +class CodeSnippetEditorModel; +class QSortFilterProxyModel; +class QSyntaxHighlighter; +class QItemSelection; + +CFG_KEY_LIST(CodeSnippetEditor, QObject::tr("A code snippets editor window"), + CFG_KEY_ENTRY(COMMIT, QKeySequence::Save, QObject::tr("Commit the pending changes")) + CFG_KEY_ENTRY(ROLLBACK, QKeySequence::Cancel, QObject::tr("Rollback the pending changes")) +) + +class CodeSnippetEditor : public MdiChild +{ + Q_OBJECT + + public: + enum Action + { + COMMIT, + ROLLBACK, + ADD, + DELETE, + MOVE_UP, + MOVE_DOWN, + HELP + }; + Q_ENUM(Action) + + enum ToolBar + { + TOOLBAR + }; + + explicit CodeSnippetEditor(QWidget *parent = nullptr); + ~CodeSnippetEditor(); + + bool restoreSessionNextTime(); + bool isUncommitted() const; + QString getQuitUncommittedConfirmMessage() const; + + protected: + QVariant saveSession(); + bool restoreSession(const QVariant &sessionValue); + Icon* getIconNameForMdiWindow(); + QString getTitleForMdiWindow(); + void createActions(); + void setupDefShortcuts(); + QToolBar* getToolBar(int toolbar) const; + + private: + void init(); + int getCurrentSnippetRow() const; + void snippetDeselected(int row); + void snippetSelected(int row); + void selectSnippet(int row, bool forRowMovement = false); + void clearEdits(); + + Ui::CodeSnippetEditor *ui; + CodeSnippetEditorModel* model = nullptr; + QSortFilterProxyModel* snippetFilterModel = nullptr; + bool currentModified = false; + bool updatesForSelection = false; + bool skipModelUpdatesDuringSelection = false; + + private slots: + void commit(); + void rollback(); + void newSnippet(); + void deleteSnippet(); + void moveSnippetUp(); + void moveSnippetDown(); + void updateModified(); + void updateCurrentSnippetState(); + void updateState(); + void snippetSelected(const QItemSelection& selected, const QItemSelection& deselected); + void applyFilter(const QString& value); + void changeFont(const QVariant& font); + void clearAssistantShortcutPressed(); + void help(); +}; + +#endif // CODESNIPPETEDITOR_H diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.ui b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.ui new file mode 100644 index 0000000..4a18b12 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.ui @@ -0,0 +1,250 @@ + + + CodeSnippetEditor + + + + 0 + 0 + 913 + 595 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + 1 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 150 + 0 + + + + Filter snippets + + + true + + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ScrollPerPixel + + + + + + + + + 4 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 1 + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + + + + + + + + + :/icons/img/clear_lineedit.png:/icons/img/clear_lineedit.png + + + + + + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + Snippet name + + + + + + + <html><head/><body><p>The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.</p></body></html> + + + + + + + <html><head/><body><p>Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.</p></body></html> + + + Code assistant shortcut + + + + + + + + + + + 0 + 2 + + + + Snippet code + + + + + + QPlainTextEdit::NoWrap + + + + + + + + + + + + + + + + + snippetFilterEdit + list + mainCodeEdit + + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.cpp new file mode 100644 index 0000000..585db7b --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.cpp @@ -0,0 +1,287 @@ +#include "codesnippeteditormodel.h" +#include "common/strhash.h" +#include "common/unused.h" +#include "iconmanager.h" + +#include + +#define SETTER(X, Y) \ +if (!isValidRowIndex(row) || X == Y) \ + return; \ + \ + X = Y; \ + emitDataChanged(row); + +#define GETTER(X, Y) \ +if (!isValidRowIndex(row)) \ + return Y; \ + \ + return X; + +CodeSnippetEditorModel::CodeSnippetEditorModel(QObject* parent) : + QAbstractListModel(parent) +{ +} + +void CodeSnippetEditorModel::clearModified() +{ + beginResetModel(); + for (Snippet*& snip : snippetList) + snip->modified = false; + + listModified = false; + originalSnippetList = snippetList; + + endResetModel(); +} + +bool CodeSnippetEditorModel::isModified() const +{ + if (snippetList != originalSnippetList) + return true; + + for (Snippet* snip : snippetList) + { + if (snip->modified) + return true; + } + return false; +} + +bool CodeSnippetEditorModel::isModified(int row) const +{ + GETTER(snippetList[row]->modified, false); +} + +void CodeSnippetEditorModel::setModified(int row, bool modified) +{ + SETTER(snippetList[row]->modified, modified); +} + +bool CodeSnippetEditorModel::isValid() const +{ + for (Snippet* snip : snippetList) + { + if (!snip->valid) + return false; + } + return true; +} + +bool CodeSnippetEditorModel::isValid(int row) const +{ + GETTER(snippetList[row]->valid, false); +} + +void CodeSnippetEditorModel::setValid(int row, bool valid) +{ + SETTER(snippetList[row]->valid, valid); +} + +void CodeSnippetEditorModel::setCode(int row, const QString& code) +{ + SETTER(snippetList[row]->data.code, code); +} + +QString CodeSnippetEditorModel::getCode(int row) const +{ + GETTER(snippetList[row]->data.code, QString()); +} + +void CodeSnippetEditorModel::setName(int row, const QString& newName) +{ + SETTER(snippetList[row]->data.name, newName); +} + +QString CodeSnippetEditorModel::getName(int row) const +{ + GETTER(snippetList[row]->data.name, QString()); +} + +void CodeSnippetEditorModel::setHotkey(int row, const QKeySequence& value) +{ + SETTER(snippetList[row]->data.hotkey, value.toString()); +} + +QKeySequence CodeSnippetEditorModel::getHotkey(int row) const +{ + GETTER(QKeySequence(snippetList[row]->data.hotkey), QKeySequence()); +} + +void CodeSnippetEditorModel::setData(const QList& snippets) +{ + beginResetModel(); + + for (Snippet*& snippetPtr : snippetList) + delete snippetPtr; + + snippetList.clear(); + + for (CodeSnippetManager::CodeSnippet* snip : snippets) + snippetList << new Snippet(snip); + + listModified = false; + originalSnippetList = snippetList; + + endResetModel(); +} + +void CodeSnippetEditorModel::addSnippet(CodeSnippetManager::CodeSnippet* snippet) +{ + int row = snippetList.size(); + + beginInsertRows(QModelIndex(), row, row); + + snippetList << new Snippet(snippet); + listModified = true; + + endInsertRows(); +} + +void CodeSnippetEditorModel::deleteSnippet(int row) +{ + if (!isValidRowIndex(row)) + return; + + beginRemoveRows(QModelIndex(), row, row); + + delete snippetList[row]; + snippetList.removeAt(row); + + listModified = true; + + endRemoveRows(); +} + +QList CodeSnippetEditorModel::generateSnippets() const +{ + QList results; + for (Snippet* snip: snippetList) + results << new CodeSnippetManager::CodeSnippet(snip->data); + + return results; +} + +QStringList CodeSnippetEditorModel::getSnippetNames() const +{ + QStringList names; + for (Snippet* snip : snippetList) + names << snip->data.name; + + return names; +} + +void CodeSnippetEditorModel::validateNames() +{ + StrHash> counter; + + int row = 0; + for (Snippet*& snip : snippetList) + { + snip->valid &= true; + counter[snip->data.name] << row++; + } + + QHashIterator> cntIt = counter.iterator(); + while (cntIt.hasNext()) + { + cntIt.next(); + if (cntIt.value().size() > 1) + { + for (int cntRow : cntIt.value()) + setValid(cntRow, false); + } + } + + QModelIndex idx; + for (int i = 0; i < snippetList.size(); i++) + { + idx = index(i); + emit dataChanged(idx, idx); + } +} + +bool CodeSnippetEditorModel::isAllowedName(int rowToSkip, const QString& nameToValidate) +{ + QStringList names = getSnippetNames(); + names.removeAt(rowToSkip); + return !names.contains(nameToValidate, Qt::CaseInsensitive); +} + +bool CodeSnippetEditorModel::isAllowedHotkey(int rowToSkip, const QKeySequence& hotkeyToValidate) +{ + QList keys; + for (Snippet*& snip : snippetList) + keys << snip->data.hotkey; + + keys.removeAt(rowToSkip); + return !keys.contains(hotkeyToValidate); +} + +bool CodeSnippetEditorModel::isValidRowIndex(int row) const +{ + return (row >= 0 && row < snippetList.size()); +} + +int CodeSnippetEditorModel::moveUp(int row) +{ + if (row <= 0 || row >= snippetList.size()) + return row; + + snippetList.move(row, row - 1); + return row - 1; +} + +int CodeSnippetEditorModel::moveDown(int row) +{ + if (row < 0 || row + 1 >= snippetList.size()) + return row; + + snippetList.move(row, row + 1); + return row + 1; +} + +int CodeSnippetEditorModel::rowCount(const QModelIndex& parent) const +{ + UNUSED(parent); + return snippetList.size(); +} + +QVariant CodeSnippetEditorModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid() || !isValidRowIndex(index.row())) + return QVariant(); + + if (role == Qt::DisplayRole) + { + Snippet* sn = snippetList[index.row()]; + return sn->data.name; + } + + if (role == Qt::DecorationRole) + { + QIcon icon = ICONS.CODE_SNIPPET; + if (!snippetList[index.row()]->valid) + icon = Icon::merge(icon, Icon::ERROR); + + return icon; + } + + return QVariant(); +} + +void CodeSnippetEditorModel::emitDataChanged(int row) +{ + QModelIndex idx = index(row); + emit dataChanged(idx, idx); +} + +CodeSnippetEditorModel::Snippet::Snippet() +{ +} + +CodeSnippetEditorModel::Snippet::Snippet(CodeSnippetManager::CodeSnippet* other) +{ + data = CodeSnippetManager::CodeSnippet(*other); + originalName = data.name; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.h b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.h new file mode 100644 index 0000000..ae5fa86 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.h @@ -0,0 +1,80 @@ +#ifndef CODESNIPPETEDITORMODEL_H +#define CODESNIPPETEDITORMODEL_H + +#include "guiSQLiteStudio_global.h" +#include "services/codesnippetmanager.h" +#include + +class GUI_API_EXPORT CodeSnippetEditorModel : public QAbstractListModel +{ + Q_OBJECT + + public: + using QAbstractItemModel::setData; + + enum Role + { + CODE = 1000, + MODIFIED = 1001, + VALID = 1002 + }; + + explicit CodeSnippetEditorModel(QObject *parent = 0); + + void clearModified(); + bool isModified() const; + bool isModified(int row) const; + void setModified(int row, bool modified); + bool isValid() const; + bool isValid(int row) const; + void setValid(int row, bool valid); + void setCode(int row, const QString& code); + QString getCode(int row) const; + void setName(int row, const QString& newName); + QString getName(int row) const; + void setHotkey(int row, const QKeySequence& value); + QKeySequence getHotkey(int row) const; + void setData(const QList& snippets); + void addSnippet(CodeSnippetManager::CodeSnippet* snippet); + void deleteSnippet(int row); + QList generateSnippets() const; + QStringList getSnippetNames() const; + void validateNames(); + bool isAllowedName(int rowToSkip, const QString& nameToValidate); + bool isAllowedHotkey(int rowToSkip, const QKeySequence& hotkeyToValidate); + bool isValidRowIndex(int row) const; + + int moveUp(int row); + int moveDown(int row); + int rowCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role) const; + + private: + struct Snippet + { + Snippet(); + Snippet(CodeSnippetManager::CodeSnippet* other); + + CodeSnippetManager::CodeSnippet data; + bool modified = false; + bool valid = true; + QString originalName; + }; + + void emitDataChanged(int row); + + QList snippetList; + + /** + * @brief List of snippets pointers before modifications. + * + * This list is kept to check for modifications in the overall list of snippets. + * Pointers on this list may be already deleted, so don't use them! + * It's only used to compare list of pointers to snippetList, so it can tell you + * if the list was modified in regards of adding or deleting functions. + */ + QList originalSnippetList; + bool listModified = false; +}; + +#endif // CODESNIPPETEDITORMODEL_H diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp index 5cf5be4..df72db2 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp @@ -15,6 +15,8 @@ #include #include +CFG_KEYS_DEFINE(CollationsEditor) + CollationsEditor::CollationsEditor(QWidget *parent) : MdiChild(parent), ui(new Ui::CollationsEditor) @@ -55,18 +57,20 @@ QString CollationsEditor::getTitleForMdiWindow() void CollationsEditor::createActions() { - createAction(COMMIT, ICONS.COMMIT, tr("Commit all collation changes"), this, SLOT(commit()), ui->toolbar); - createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all collation changes"), this, SLOT(rollback()), ui->toolbar); + createAction(COMMIT, ICONS.COMMIT, tr("Commit all collation changes"), this, SLOT(commit()), ui->toolbar, this); + createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all collation changes"), this, SLOT(rollback()), ui->toolbar, this); ui->toolbar->addSeparator(); - createAction(ADD, ICONS.NEW_COLLATION, tr("Create new collation"), this, SLOT(newCollation()), ui->toolbar); - createAction(DELETE, ICONS.DELETE_COLLATION, tr("Delete selected collation"), this, SLOT(deleteCollation()), ui->toolbar); + createAction(ADD, ICONS.NEW_COLLATION, tr("Create new collation"), this, SLOT(newCollation()), ui->toolbar, this); + createAction(DELETE, ICONS.DELETE_COLLATION, tr("Delete selected collation"), this, SLOT(deleteCollation()), ui->toolbar, this); ui->toolbar->addSeparator(); - createAction(HELP, ICONS.HELP, tr("Editing collations manual"), this, SLOT(help()), ui->toolbar); + createAction(HELP, ICONS.HELP, tr("Editing collations manual"), this, SLOT(help()), ui->toolbar, this); } void CollationsEditor::setupDefShortcuts() { - + // Widget context + setShortcutContext({COMMIT, ROLLBACK}, Qt::WidgetWithChildrenShortcut); + BIND_SHORTCUTS(CollationsEditor, Action); } QToolBar* CollationsEditor::getToolBar(int toolbar) const @@ -378,10 +382,9 @@ void CollationsEditor::changeFont(const QVariant& font) setFont(font.value()); } - bool CollationsEditor::isUncommitted() const { - return model->isModified(); + return model->isModified() || currentModified; } QString CollationsEditor::getQuitUncommittedConfirmMessage() const diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.h b/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.h index 7b2e469..61c9165 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.h @@ -17,6 +17,11 @@ class CollationsEditorModel; class QSortFilterProxyModel; class QSyntaxHighlighter; +CFG_KEY_LIST(CollationsEditor, QObject::tr("A collation editor window"), + CFG_KEY_ENTRY(COMMIT, QKeySequence::Save, QObject::tr("Commit the pending changes")) + CFG_KEY_ENTRY(ROLLBACK, QKeySequence::Cancel, QObject::tr("Rollback the pending changes")) +) + class GUI_API_EXPORT CollationsEditor : public MdiChild { Q_OBJECT @@ -30,6 +35,7 @@ class GUI_API_EXPORT CollationsEditor : public MdiChild DELETE, HELP }; + Q_ENUM(Action) enum ToolBar { diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.cpp index 795158a..c083cbd 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.cpp @@ -1,14 +1,12 @@ #include "ddlhistorywindow.h" #include "ui_ddlhistorywindow.h" #include "services/config.h" -#include "common/userinputfilter.h" -#include "common/extlineedit.h" -#include "dblistmodel.h" #include "ddlhistorymodel.h" #include "common/unused.h" #include "iconmanager.h" #include #include +#include #include DdlHistoryWindow::DdlHistoryWindow(QWidget *parent) : @@ -58,6 +56,7 @@ void DdlHistoryWindow::init() connect(ui->tableView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), this, SLOT(activated(QModelIndex,QModelIndex))); + connect(ui->clearButton, SIGNAL(clicked(bool)), this, SLOT(clearHistory())); } void DdlHistoryWindow::activated(const QModelIndex& current, const QModelIndex& previous) @@ -76,12 +75,8 @@ void DdlHistoryWindow::activated(const QModelIndex& current, const QModelIndex& QStringList contentEntries; QList entries = CFG->getDdlHistoryFor(dbName, dbFile, date); - for (Config::DdlHistoryEntryPtr entry : entries) - { - contentEntries << templ.arg(entry->dbName).arg(entry->dbFile) - .arg(entry->timestamp.toString("yyyy-MM-dd HH:mm:ss")) - .arg(entry->queries); - } + for (Config::DdlHistoryEntryPtr& entry : entries) + contentEntries << templ.arg(entry->dbName, entry->dbFile, entry->timestamp.toString("yyyy-MM-dd HH:mm:ss"), entry->queries); ui->ddlEdit->setPlainText(contentEntries.join("\n\n")); } @@ -98,6 +93,17 @@ void DdlHistoryWindow::refreshDbList() dbListModel->setStringList(dbList); } +void DdlHistoryWindow::clearHistory() +{ + QMessageBox::StandardButton result = QMessageBox::question(this, tr("Clear history"), tr("Are you sure you want to erase entire DDL history?")); + if (result != QMessageBox::Yes) + return; + + CFG->clearDdlHistory(); + dataModel->refresh(); + ui->ddlEdit->setPlainText(""); +} + bool DdlHistoryWindow::restoreSessionNextTime() { return false; diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.h index 1a04831..c8de66a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.h @@ -49,6 +49,7 @@ class GUI_API_EXPORT DdlHistoryWindow : public MdiChild void activated(const QModelIndex& current, const QModelIndex& previous); void applyFilter(const QString& filterValue); void refreshDbList(); + void clearHistory(); }; #endif // DDLHISTORYWINDOW_H diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.ui b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.ui index 33cdb66..77fa5ca 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.ui +++ b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.ui @@ -49,6 +49,20 @@ + + + + Clear entire history + + + + + + + :/icons/img/act_clear.png:/icons/img/act_clear.png + + + @@ -121,6 +135,8 @@
    sqlview.h
    - + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp index f0f980d..23cb651 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp @@ -113,6 +113,12 @@ void EditorWindow::init() resultsModel->setDb(currentDb); ui->sqlEdit->setDb(currentDb); + connect(CFG_UI.General.SqlEditorCurrQueryHighlight, SIGNAL(changed(QVariant)), this, SLOT(queryHighlightingConfigChanged(QVariant))); + if (CFG_UI.General.SqlEditorCurrQueryHighlight.get()) + ui->sqlEdit->setCurrentQueryHighlighting(true); + + connect(ui->sqlEdit, SIGNAL(textChanged()), this, SLOT(checkTextChangedForSession())); + connect(resultsModel, SIGNAL(executionSuccessful()), this, SLOT(executionSuccessful())); connect(resultsModel, SIGNAL(executionFailed(QString)), this, SLOT(executionFailed(QString))); connect(resultsModel, SIGNAL(storeExecutionInHistory()), this, SLOT(storeExecutionInHistory())); @@ -205,7 +211,7 @@ QAction* EditorWindow::getAction(EditorWindow::Action action) return ExtActionContainer::getAction(action); } -QString EditorWindow::getQueryToExecute(bool doSelectCurrentQuery) +QString EditorWindow::getQueryToExecute(bool doSelectCurrentQuery, QueryExecMode querySelectionMode) { QString sql; if (ui->sqlEdit->textCursor().hasSelection()) @@ -213,7 +219,11 @@ QString EditorWindow::getQueryToExecute(bool doSelectCurrentQuery) sql = ui->sqlEdit->textCursor().selectedText(); fixTextCursorSelectedText(sql); } - else if (CFG_UI.General.ExecuteCurrentQueryOnly.get()) + else if (querySelectionMode == ALL) + { + sql = ui->sqlEdit->toPlainText(); + } + else if (CFG_UI.General.ExecuteCurrentQueryOnly.get() || querySelectionMode == SINGLE) { ui->sqlEdit->saveSelection(); selectCurrentQuery(true); @@ -237,7 +247,9 @@ bool EditorWindow::setCurrentDb(Db *db) void EditorWindow::setContents(const QString &sql) { + settingSqlContents = true; ui->sqlEdit->setPlainText(sql); + settingSqlContents = false; } QString EditorWindow::getContents() const @@ -394,6 +406,8 @@ void EditorWindow::createActions() createAction(FOCUS_RESULTS_BELOW, tr("Focus results below", "sql editor"), this, SLOT(focusResultsBelow()), this); createAction(FOCUS_EDITOR_ABOVE, tr("Focus SQL editor above", "sql editor"), this, SLOT(focusEditorAbove()), this); createAction(DELETE_SINGLE_HISTORY_SQL, tr("Delete selected SQL history entries", "sql editor"), this, SLOT(deleteSelectedSqlHistory()), ui->historyList); + createAction(EXEC_ONE_QUERY, ICONS.EXEC_QUERY, tr("Execute single query under cursor"), this, SLOT(execOneQuery()), this); + createAction(EXEC_ALL_QUERIES, ICONS.EXEC_QUERY, tr("Execute all queries in editor"), this, SLOT(execAllQueries()), this); // Static action triggers connect(staticActions[RESULTS_IN_TAB], SIGNAL(triggered()), this, SLOT(updateResultsDisplayMode())); @@ -421,35 +435,19 @@ void EditorWindow::selectCurrentQuery(bool fallBackToPreviousIfNecessary) { QTextCursor cursor = ui->sqlEdit->textCursor(); int pos = cursor.position(); - int queryStartPos; - QString contents = ui->sqlEdit->toPlainText(); - QString query = getQueryWithPosition(contents, pos, &queryStartPos); - TokenList tokens = Lexer::tokenize(query); - tokens.trim(); - tokens.trimRight(Token::OPERATOR, ";"); - if (tokens.size() == 0 && fallBackToPreviousIfNecessary) - { - // Fallback - pos = contents.lastIndexOf(";", pos - 1); - if (pos > -1) - { - query = getQueryWithPosition(contents, pos, &queryStartPos); - tokens = Lexer::tokenize(query); - tokens.trim(); - tokens.trimRight(Token::OPERATOR, ";"); - } - } + QString contents = ui->sqlEdit->toPlainText(); + QPair boundries = getQueryBoundriesForPosition(contents, pos, fallBackToPreviousIfNecessary); - if (tokens.size() == 0) + if (boundries.second < 0) { qWarning() << "No tokens to select in EditorWindow::selectCurrentQuery()."; return; } cursor.clearSelection(); - cursor.setPosition(tokens.first()->start + queryStartPos); - cursor.setPosition(tokens.last()->end + 1 + queryStartPos, QTextCursor::KeepAnchor); + cursor.setPosition(boundries.first); + cursor.setPosition(boundries.second, QTextCursor::KeepAnchor); ui->sqlEdit->setTextCursor(cursor); } @@ -459,13 +457,13 @@ void EditorWindow::updateShortcutTips() { QString prevDbKey = actionMap[PREV_DB]->shortcut().toString(QKeySequence::NativeText); QString nextDbKey = actionMap[NEXT_DB]->shortcut().toString(QKeySequence::NativeText); - dbCombo->setToolTip(tr("Active database (%1/%2)").arg(prevDbKey).arg(nextDbKey)); + dbCombo->setToolTip(tr("Active database (%1/%2)").arg(prevDbKey, nextDbKey)); } } -void EditorWindow::execQuery(bool explain) +void EditorWindow::execQuery(bool explain, QueryExecMode querySelectionMode) { - QString sql = getQueryToExecute(true); + QString sql = getQueryToExecute(true, querySelectionMode); QHash bindParams; bool proceed = processBindParams(sql, bindParams); if (!proceed) @@ -487,6 +485,16 @@ void EditorWindow::execQuery(bool explain) } } +void EditorWindow::execOneQuery() +{ + execQuery(false, SINGLE); +} + +void EditorWindow::execAllQueries() +{ + execQuery(false, ALL); +} + void EditorWindow::explainQuery() { execQuery(true); @@ -504,7 +512,6 @@ bool EditorWindow::processBindParams(QString& sql, QHash& que // Process bind tokens, prepare list for a dialog. static_qstring(paramTpl, ":arg%1"); - QString arg; QVector bindParams; QHash namedBindParams; BindParam* bindParam = nullptr; @@ -539,14 +546,14 @@ bool EditorWindow::processBindParams(QString& sql, QHash& que // Transfer values from dialog to arguments for query if (accepted) { - for (BindParam* bindParam : bindParams) + for (BindParam*& bindParam : bindParams) queryParams[bindParam->newName] = bindParam->value; sql = tokens.detokenize(); } // Cleanup - for (BindParam* bindParam : bindParams) + for (BindParam*& bindParam : bindParams) delete bindParam; return accepted; @@ -676,7 +683,7 @@ void EditorWindow::deleteSelectedSqlHistory() return; QList ids; - for (const QModelIndex& idx : ui->historyList->selectionModel()->selectedRows(0)) + for (QModelIndex& idx : ui->historyList->selectionModel()->selectedRows(0)) ids += idx.data().toLongLong(); CFG->deleteSqlHistory(ids); @@ -747,6 +754,22 @@ void EditorWindow::updateState() actionMap[EXPLAIN_QUERY]->setEnabled(!executionInProgress); } +void EditorWindow::checkTextChangedForSession() +{ + if (!ui->sqlEdit->getHighlightingSyntax() && !settingSqlContents) + emit sessionValueChanged(); +} + +void EditorWindow::queryHighlightingConfigChanged(const QVariant& enabled) +{ + ui->sqlEdit->setCurrentQueryHighlighting(enabled.toBool()); +} + +void EditorWindow::refreshValidDbObjects() +{ + ui->sqlEdit->refreshValidObjects(); +} + int qHash(EditorWindow::ActionGroup actionGroup) { return static_cast(actionGroup); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h index 35a3b9b..977784e 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h @@ -26,6 +26,8 @@ class DbComboBox; CFG_KEY_LIST(EditorWindow, QObject::tr("SQL editor window"), CFG_KEY_ENTRY(EXEC_QUERY, Qt::Key_F9, QObject::tr("Execute query")) + CFG_KEY_ENTRY(EXEC_ONE_QUERY, Qt::CTRL + Qt::Key_F9, QObject::tr("Execute single query under cursor")) + CFG_KEY_ENTRY(EXEC_ALL_QUERIES, Qt::SHIFT + Qt::Key_F9, QObject::tr("Execute all queries in editor")) CFG_KEY_ENTRY(EXPLAIN_QUERY, Qt::Key_F8, QObject::tr("Execute \"%1\" query").arg("EXPLAIN")) CFG_KEY_ENTRY(PREV_DB, Qt::CTRL + Qt::Key_Up, QObject::tr("Switch current working database to previous on the list")) CFG_KEY_ENTRY(NEXT_DB, Qt::CTRL + Qt::Key_Down, QObject::tr("Switch current working database to next on the list")) @@ -38,8 +40,7 @@ CFG_KEY_LIST(EditorWindow, QObject::tr("SQL editor window"), class GUI_API_EXPORT EditorWindow : public MdiChild { - Q_OBJECT - Q_ENUMS(Action) + Q_OBJECT public: enum class ResultsDisplayMode @@ -51,6 +52,8 @@ class GUI_API_EXPORT EditorWindow : public MdiChild enum Action { EXEC_QUERY, + EXEC_ONE_QUERY, + EXEC_ALL_QUERIES, EXPLAIN_QUERY, RESULTS_IN_TAB, RESULTS_BELOW, @@ -66,6 +69,14 @@ class GUI_API_EXPORT EditorWindow : public MdiChild CREATE_VIEW_FROM_QUERY, DELETE_SINGLE_HISTORY_SQL }; + Q_ENUM(Action) + + enum QueryExecMode + { + DEFAULT, + SINGLE, + ALL + }; enum ToolBar { @@ -89,7 +100,7 @@ class GUI_API_EXPORT EditorWindow : public MdiChild QSize sizeHint() const; QAction* getAction(Action action); - QString getQueryToExecute(bool doSelectCurrentQuery = false); + QString getQueryToExecute(bool doSelectCurrentQuery = false, QueryExecMode querySelectionMode = DEFAULT); bool setCurrentDb(Db* db); void setContents(const QString& sql); QString getContents() const; @@ -98,6 +109,7 @@ class GUI_API_EXPORT EditorWindow : public MdiChild SqlEditor* getEditor() const; bool isUncommitted() const; QString getQuitUncommittedConfirmMessage() const; + Db* getCurrentDb(); protected: void changeEvent(QEvent *e); @@ -105,7 +117,6 @@ class GUI_API_EXPORT EditorWindow : public MdiChild bool restoreSession(const QVariant& sessionValue); Icon* getIconNameForMdiWindow(); QString getTitleForMdiWindow(); - Db* getCurrentDb(); private: static void createStaticActions(); @@ -134,9 +145,12 @@ class GUI_API_EXPORT EditorWindow : public MdiChild qint64 lastQueryHistoryId = 0; QString lastSuccessfulQuery; QMenu* sqlHistoryMenu = nullptr; + bool settingSqlContents = false; private slots: - void execQuery(bool explain = false); + void execQuery(bool explain = false, QueryExecMode querySelectionMode = DEFAULT); + void execOneQuery(); + void execAllQueries(); void explainQuery(); void dbChanged(); void executionSuccessful(); @@ -157,6 +171,11 @@ class GUI_API_EXPORT EditorWindow : public MdiChild void exportResults(); void createViewFromQuery(); void updateState(); + void checkTextChangedForSession(); + void queryHighlightingConfigChanged(const QVariant& enabled); + + public slots: + void refreshValidDbObjects(); }; GUI_API_EXPORT int qHash(EditorWindow::ActionGroup action); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.ui b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.ui index f3f44e3..94e3dba 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.ui +++ b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.ui @@ -11,7 +11,7 @@
    - SQL editor + SQL editor @@ -77,7 +77,7 @@ - Results + Results diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp index 3ac32ac..4d964c8 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp @@ -8,10 +8,8 @@ #include "services/pluginmanager.h" #include "dbtree/dbtree.h" #include "dbtree/dbtreemodel.h" -#include "dbtree/dbtreeitem.h" #include "iconmanager.h" #include "syntaxhighlighterplugin.h" -#include "sqlitesyntaxhighlighter.h" #include "plugins/scriptingplugin.h" #include "common/userinputfilter.h" #include "selectabledbmodel.h" @@ -19,9 +17,12 @@ #include #include #include +#include // TODO handle plugin loading/unloading to update editor state +CFG_KEYS_DEFINE(FunctionsEditor) + FunctionsEditor::FunctionsEditor(QWidget *parent) : MdiChild(parent), ui(new Ui::FunctionsEditor) @@ -52,26 +53,26 @@ Icon* FunctionsEditor::getIconNameForMdiWindow() QString FunctionsEditor::getTitleForMdiWindow() { - return tr("SQL function editor"); + return tr("SQL functions editor"); } void FunctionsEditor::createActions() { - createAction(COMMIT, ICONS.COMMIT, tr("Commit all function changes"), this, SLOT(commit()), ui->toolBar); - createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all function changes"), this, SLOT(rollback()), ui->toolBar); + createAction(COMMIT, ICONS.COMMIT, tr("Commit all function changes"), this, SLOT(commit()), ui->toolBar, this); + createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all function changes"), this, SLOT(rollback()), ui->toolBar, this); ui->toolBar->addSeparator(); - createAction(ADD, ICONS.NEW_FUNCTION, tr("Create new function"), this, SLOT(newFunction()), ui->toolBar); - createAction(DELETE, ICONS.DELETE_FUNCTION, tr("Delete selected function"), this, SLOT(deleteFunction()), ui->toolBar); + createAction(ADD, ICONS.NEW_FUNCTION, tr("Create new function"), this, SLOT(newFunction()), ui->toolBar, this); + createAction(DELETE, ICONS.DELETE_FUNCTION, tr("Delete selected function"), this, SLOT(deleteFunction()), ui->toolBar, this); ui->toolBar->addSeparator(); - createAction(HELP, ICONS.HELP, tr("Custom SQL functions manual"), this, SLOT(help()), ui->toolBar); + createAction(HELP, ICONS.HELP, tr("Custom SQL functions manual"), this, SLOT(help()), ui->toolBar, this); // Args toolbar - createAction(ARG_ADD, ICONS.INSERT_FN_ARG, tr("Add function argument"), this, SLOT(addFunctionArg()), ui->argsToolBar); - createAction(ARG_EDIT, ICONS.RENAME_FN_ARG, tr("Rename function argument"), this, SLOT(editFunctionArg()), ui->argsToolBar); - createAction(ARG_DEL, ICONS.DELETE_FN_ARG, tr("Delete function argument"), this, SLOT(delFunctionArg()), ui->argsToolBar); + createAction(ARG_ADD, ICONS.INSERT_FN_ARG, tr("Add function argument"), this, SLOT(addFunctionArg()), ui->argsToolBar, this); + createAction(ARG_EDIT, ICONS.RENAME_FN_ARG, tr("Rename function argument"), this, SLOT(editFunctionArg()), ui->argsToolBar, this); + createAction(ARG_DEL, ICONS.DELETE_FN_ARG, tr("Delete function argument"), this, SLOT(delFunctionArg()), ui->argsToolBar, this); ui->argsToolBar->addSeparator(); - createAction(ARG_MOVE_UP, ICONS.MOVE_UP, tr("Move function argument up"), this, SLOT(moveFunctionArgUp()), ui->argsToolBar); - createAction(ARG_MOVE_DOWN, ICONS.MOVE_DOWN, tr("Move function argument down"), this, SLOT(moveFunctionArgDown()), ui->argsToolBar); + createAction(ARG_MOVE_UP, ICONS.MOVE_UP, tr("Move function argument up"), this, SLOT(moveFunctionArgUp()), ui->argsToolBar, this); + createAction(ARG_MOVE_DOWN, ICONS.MOVE_DOWN, tr("Move function argument down"), this, SLOT(moveFunctionArgDown()), ui->argsToolBar, this); #ifdef Q_OS_MACX QStyle *fusion = QStyleFactory::create("Fusion"); @@ -82,6 +83,9 @@ void FunctionsEditor::createActions() void FunctionsEditor::setupDefShortcuts() { + // Widget context + setShortcutContext({COMMIT, ROLLBACK}, Qt::WidgetWithChildrenShortcut); + BIND_SHORTCUTS(FunctionsEditor, Action); } QToolBar* FunctionsEditor::getToolBar(int toolbar) const @@ -123,11 +127,12 @@ void FunctionsEditor::init() connect(ui->mainCodeEdit, SIGNAL(textChanged()), this, SLOT(updateModified())); connect(ui->finalCodeEdit, SIGNAL(textChanged()), this, SLOT(updateModified())); connect(ui->nameEdit, SIGNAL(textChanged(QString)), this, SLOT(updateModified())); - connect(ui->undefArgsCheck, SIGNAL(clicked()), this, SLOT(updateModified())); + connect(ui->undefArgsCheck, SIGNAL(toggled(bool)), this, SLOT(updateModified())); connect(ui->allDatabasesRadio, SIGNAL(clicked()), this, SLOT(updateModified())); connect(ui->selDatabasesRadio, SIGNAL(clicked()), this, SLOT(updateModified())); connect(ui->langCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateModified())); connect(ui->typeCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateModified())); + connect(ui->deterministicCheck, SIGNAL(toggled(bool)), this, SLOT(updateModified())); connect(ui->argsList->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(updateArgsState())); connect(ui->argsList->model(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(updateModified())); @@ -141,13 +146,13 @@ void FunctionsEditor::init() model->setData(FUNCTIONS->getAllScriptFunctions()); // Language plugins - for (ScriptingPlugin* plugin : PLUGINS->getLoadedPlugins()) + for (ScriptingPlugin*& plugin : PLUGINS->getLoadedPlugins()) scriptingPlugins[plugin->getLanguage()] = plugin; ui->langCombo->addItems(scriptingPlugins.keys()); // Syntax highlighting plugins - for (SyntaxHighlighterPlugin* plugin : PLUGINS->getLoadedPlugins()) + for (SyntaxHighlighterPlugin*& plugin : PLUGINS->getLoadedPlugins()) highlighterPlugins[plugin->getLanguageName()] = plugin; updateState(); @@ -170,6 +175,7 @@ void FunctionsEditor::functionDeselected(int row) model->setUndefinedArgs(row, ui->undefArgsCheck->isChecked()); model->setAllDatabases(row, ui->allDatabasesRadio->isChecked()); model->setCode(row, ui->mainCodeEdit->toPlainText()); + model->setDeterministic(row, ui->deterministicCheck->isChecked()); model->setModified(row, currentModified); if (model->isAggregate(row)) @@ -201,6 +207,7 @@ void FunctionsEditor::functionSelected(int row) ui->finalCodeEdit->setPlainText(model->getFinalCode(row)); ui->undefArgsCheck->setChecked(model->getUndefinedArgs(row)); ui->langCombo->setCurrentText(model->getLang(row)); + ui->deterministicCheck->setChecked(model->isDeterministic(row)); // Arguments ui->argsList->clear(); @@ -248,6 +255,7 @@ void FunctionsEditor::clearEdits() ui->allDatabasesRadio->setChecked(true); ui->typeCombo->setCurrentIndex(0); ui->langCombo->setCurrentIndex(-1); + ui->deterministicCheck->setChecked(false); } void FunctionsEditor::selectFunction(int row) @@ -374,9 +382,10 @@ void FunctionsEditor::updateModified() bool argDiff = getCurrentArgList() != model->getArguments(row); bool dbDiff = toSet(getCurrentDatabases()) != toSet(model->getDatabases(row)); // QSet to ignore order bool typeDiff = model->getType(row) != getCurrentFunctionType(); + bool deterministicDiff = model->isDeterministic(row) != ui->deterministicCheck->isChecked(); currentModified = (nameDiff || codeDiff || typeDiff || langDiff || undefArgsDiff || allDatabasesDiff || argDiff || dbDiff || - initCodeDiff || finalCodeDiff); + initCodeDiff || finalCodeDiff || deterministicDiff); } updateCurrentFunctionState(); @@ -415,6 +424,7 @@ void FunctionsEditor::updateCurrentFunctionState() ui->mainCodeGroup->setEnabled(langOk); ui->finalCodeGroup->setEnabled(langOk); ui->argsGroup->setEnabled(langOk); + ui->deterministicCheck->setEnabled(langOk); ui->databasesGroup->setEnabled(langOk); ui->nameEdit->setEnabled(langOk); ui->nameLabel->setEnabled(langOk); @@ -624,7 +634,7 @@ QVariant FunctionsEditor::saveSession() bool FunctionsEditor::isUncommitted() const { - return model->isModified(); + return model->isModified() || currentModified; } QString FunctionsEditor::getQuitUncommittedConfirmMessage() const diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.h b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.h index 72455d3..d3fcf12 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.h @@ -3,7 +3,6 @@ #include "mdichild.h" #include "common/extactioncontainer.h" -#include "services/config.h" #include "services/functionmanager.h" #include #include @@ -20,6 +19,11 @@ class QTreeWidgetItem; class QSyntaxHighlighter; class SelectableDbModel; +CFG_KEY_LIST(FunctionsEditor, QObject::tr("A function editor window"), + CFG_KEY_ENTRY(COMMIT, QKeySequence::Save, QObject::tr("Commit the pending changes")) + CFG_KEY_ENTRY(ROLLBACK, QKeySequence::Cancel, QObject::tr("Rollback the pending changes")) +) + class GUI_API_EXPORT FunctionsEditor : public MdiChild { Q_OBJECT @@ -38,6 +42,7 @@ class GUI_API_EXPORT FunctionsEditor : public MdiChild ARG_MOVE_DOWN, HELP }; + Q_ENUM(Action) enum ToolBar { diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.ui b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.ui index 17c3859..2c78fbe 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.ui +++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.ui @@ -78,7 +78,7 @@ - Filter funtions + Filter functions true @@ -228,7 +228,7 @@ false - 0 + 35
    @@ -247,13 +247,19 @@ 0 - + Type: + + + + + + @@ -264,18 +270,19 @@ - - - - + Implementation language: - - + + + + Deterministic + + diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp index 8d6d87c..46ae8d9 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp @@ -28,7 +28,7 @@ FunctionsEditorModel::FunctionsEditorModel(QObject *parent) : void FunctionsEditorModel::clearModified() { beginResetModel(); - for (Function* func : functionList) + for (Function*& func : functionList) func->modified = false; listModified = false; @@ -170,6 +170,16 @@ bool FunctionsEditorModel::isScalar(int row) const GETTER(functionList[row]->data.type == FunctionManager::ScriptFunction::SCALAR, false); } +void FunctionsEditorModel::setDeterministic(int row, bool value) +{ + SETTER(functionList[row]->data.deterministic, value); +} + +bool FunctionsEditorModel::isDeterministic(int row) const +{ + GETTER(functionList[row]->data.deterministic, false); +} + QStringList FunctionsEditorModel::getArguments(int row) const { GETTER(functionList[row]->data.arguments, QStringList()); @@ -194,7 +204,7 @@ void FunctionsEditorModel::setData(const QList { beginResetModel(); - for (Function* functionPtr : functionList) + for (Function*& functionPtr : functionList) delete functionPtr; functionList.clear(); @@ -259,7 +269,7 @@ void FunctionsEditorModel::validateNames() StrHash> counter; int row = 0; - for (Function* func : functionList) + for (Function*& func : functionList) { func->valid &= true; counter[func->data.name] << row++; @@ -322,7 +332,7 @@ QVariant FunctionsEditorModel::data(const QModelIndex& index, int role) const void FunctionsEditorModel::init() { - for (ScriptingPlugin* plugin : PLUGINS->getLoadedPlugins()) + for (ScriptingPlugin*& plugin : PLUGINS->getLoadedPlugins()) langToIcon[plugin->getLanguage()] = QIcon(plugin->getIconPath()); } diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.h b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.h index 79f073f..7caf06c 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.h @@ -1,7 +1,6 @@ #ifndef FUNCTIONSEDITORMODEL_H #define FUNCTIONSEDITORMODEL_H -#include "services/config.h" #include "services/functionmanager.h" #include "guiSQLiteStudio_global.h" #include @@ -49,6 +48,8 @@ class GUI_API_EXPORT FunctionsEditorModel : public QAbstractListModel void setType(int row, FunctionManager::ScriptFunction::Type type); bool isAggregate(int row) const; bool isScalar(int row) const; + void setDeterministic(int row, bool value); + bool isDeterministic(int row) const; bool getUndefinedArgs(int row) const; void setUndefinedArgs(int row, bool value); bool getAllDatabases(int row) const; diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp index 4351312..f8dd621 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp @@ -9,13 +9,14 @@ #include "uiconfig.h" #include "db/db.h" #include "services/dbmanager.h" -#include "services/notifymanager.h" #include "common/lazytrigger.h" #include "common/compatibility.h" #include #include #include +CFG_KEYS_DEFINE(SqliteExtensionEditor) + SqliteExtensionEditor::SqliteExtensionEditor(QWidget *parent) : MdiChild(parent), ui(new Ui::SqliteExtensionEditor) @@ -36,7 +37,7 @@ bool SqliteExtensionEditor::restoreSessionNextTime() bool SqliteExtensionEditor::isUncommitted() const { - return model->isModified(); + return model->isModified() || currentModified; } QString SqliteExtensionEditor::getQuitUncommittedConfirmMessage() const @@ -67,17 +68,20 @@ QString SqliteExtensionEditor::getTitleForMdiWindow() void SqliteExtensionEditor::createActions() { - createAction(COMMIT, ICONS.COMMIT, tr("Commit all extension changes"), this, SLOT(commit()), ui->toolbar); - createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all extension changes"), this, SLOT(rollback()), ui->toolbar); + createAction(COMMIT, ICONS.COMMIT, tr("Commit all extension changes"), this, SLOT(commit()), ui->toolbar, this); + createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all extension changes"), this, SLOT(rollback()), ui->toolbar, this); ui->toolbar->addSeparator(); - createAction(ADD, ICONS.EXTENSION_ADD, tr("Add new extension"), this, SLOT(newExtension()), ui->toolbar); - createAction(DELETE, ICONS.EXTENSION_DELETE, tr("Remove selected extension"), this, SLOT(deleteExtension()), ui->toolbar); + createAction(ADD, ICONS.EXTENSION_ADD, tr("Add new extension"), this, SLOT(newExtension()), ui->toolbar, this); + createAction(DELETE, ICONS.EXTENSION_DELETE, tr("Remove selected extension"), this, SLOT(deleteExtension()), ui->toolbar, this); ui->toolbar->addSeparator(); - createAction(HELP, ICONS.HELP, tr("Editing extensions manual"), this, SLOT(help()), ui->toolbar); + createAction(HELP, ICONS.HELP, tr("Editing extensions manual"), this, SLOT(help()), ui->toolbar, this); } void SqliteExtensionEditor::setupDefShortcuts() { + // Widget context + setShortcutContext({COMMIT, ROLLBACK}, Qt::WidgetWithChildrenShortcut); + BIND_SHORTCUTS(SqliteExtensionEditor, Action); } QToolBar* SqliteExtensionEditor::getToolBar(int toolbar) const @@ -224,6 +228,13 @@ bool SqliteExtensionEditor::validateExtension(int row) return validateExtension(filePath, initFunc, nullptr, nullptr, new QString); } +bool SqliteExtensionEditor::validateCurrentExtension() +{ + QString filePath = ui->fileEdit->text(); + QString initFunc = ui->initEdit->text(); + return validateExtension(filePath, initFunc, nullptr, nullptr, new QString); +} + bool SqliteExtensionEditor::validateExtension(const QString& filePath, const QString& initFunc, bool* fileOk, bool* initOk, QString* fileError) { bool localFileOk = true; @@ -326,7 +337,7 @@ void SqliteExtensionEditor::deleteExtension() void SqliteExtensionEditor::updateState() { bool modified = model->isModified() || currentModified; - bool valid = model->isValid(); + bool valid = model->isValid() && validateCurrentExtension(); actionMap[COMMIT]->setEnabled(modified && valid); actionMap[ROLLBACK]->setEnabled(modified); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h index 5d81085..8f67415 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h @@ -17,6 +17,11 @@ class SelectableDbModel; class Db; class LazyTrigger; +CFG_KEY_LIST(SqliteExtensionEditor, QObject::tr("A SQLite extension editor window"), + CFG_KEY_ENTRY(COMMIT, QKeySequence::Save, QObject::tr("Commit the pending changes")) + CFG_KEY_ENTRY(ROLLBACK, QKeySequence::Cancel, QObject::tr("Rollback the pending changes")) +) + class SqliteExtensionEditor : public MdiChild { Q_OBJECT @@ -30,6 +35,7 @@ class SqliteExtensionEditor : public MdiChild DELETE, HELP }; + Q_ENUM(Action) enum ToolBar { @@ -65,6 +71,7 @@ class SqliteExtensionEditor : public MdiChild bool* initOk = nullptr, QString* fileError = nullptr); bool validateExtension(int row); + bool validateCurrentExtension(); bool validateExtension(const QString& filePath, const QString& initFunc, bool* fileOk = nullptr, diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.ui b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.ui index 747aa7f..93e938d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.ui +++ b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.ui @@ -14,6 +14,18 @@ Form + + 0 + + + 0 + + + 0 + + + 0 + diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditormodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditormodel.cpp index 6e94a64..832875d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditormodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditormodel.cpp @@ -126,7 +126,7 @@ bool SqliteExtensionEditorModel::isValid() const { for (Extension* ext : extensionList) { - if (!ext->valid) + if (ext->modified && !ext->valid) return false; } return true; diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp index 41d6ed9..bf8c583 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp @@ -1,6 +1,8 @@ #include "tablestructuremodel.h" +#include "datagrid/sqlquerymodelcolumn.h" #include "iconmanager.h" #include "common/unused.h" +#include "parser/ast/sqlitecreatetable.h" #include #include #include @@ -45,6 +47,9 @@ QVariant TableStructureModel::data(const QModelIndex& index, int role) const if (createTable->columns.size() <= row) return QVariant(); + if (role == Qt::ToolTipRole) + return getToolTip(row, getHeaderColumn(index.column())); + switch (getHeaderColumn(index.column())) { case TableStructureModel::Columns::NAME: @@ -511,6 +516,98 @@ bool TableStructureModel::isColumnGenerate(SqliteCreateTable::Column* column) co return false; } +QString TableStructureModel::getToolTip(int row, Columns modelColumn) const +{ + static const QString tooltipTpl = "
    %2%3
    "; + + if (row >= createTable->columns.size()) + return QString(); + + SqliteCreateTable::Column* col = createTable->columns[row]; + if (col->constraints.isEmpty() && createTable->constraints.isEmpty()) + return QString(); + + SqliteCreateTable::Column::Constraint::Type lookFor; + SqliteCreateTable::Constraint::Type lookForTableType; + bool tableConstrDefined = false; + switch (modelColumn) + { + case Columns::PK: + lookFor = SqliteCreateTable::Column::Constraint::Type::PRIMARY_KEY; + lookForTableType = SqliteCreateTable::Constraint::Type::PRIMARY_KEY; + tableConstrDefined = true; + break; + case Columns::FK: + lookFor = SqliteCreateTable::Column::Constraint::Type::FOREIGN_KEY; + lookForTableType = SqliteCreateTable::Constraint::Type::FOREIGN_KEY; + tableConstrDefined = true; + break; + case Columns::UNIQUE: + lookFor = SqliteCreateTable::Column::Constraint::Type::UNIQUE; + lookForTableType = SqliteCreateTable::Constraint::Type::UNIQUE; + tableConstrDefined = true; + break; + case Columns::CHECK: + lookFor = SqliteCreateTable::Column::Constraint::Type::CHECK; + // Not defined for Table-level constraint, because it cannot (easily) determin if it's affected by table CHECK. + break; + case Columns::NOTNULL: + lookFor = SqliteCreateTable::Column::Constraint::Type::NOT_NULL; + break; + case Columns::COLLATE: + lookFor = SqliteCreateTable::Column::Constraint::Type::COLLATE; + break; + case Columns::GENERATED: + lookFor = SqliteCreateTable::Column::Constraint::Type::GENERATED; + break; + case Columns::DEFAULT: + lookFor = SqliteCreateTable::Column::Constraint::Type::DEFAULT; + break; + case Columns::NAME: + case Columns::TYPE: + return QString(); + } + + SqliteCreateTable::Column::Constraint* constraint = findFirst( + col->constraints, + [lookFor](SqliteCreateTable::Column::Constraint* constr) -> bool + { + return constr->type == lookFor; + } + ); + + SqliteCreateTable::Constraint* tableConstraint = nullptr; + if (tableConstrDefined) + { + tableConstraint = findFirst( + createTable->constraints, + [lookForTableType](SqliteCreateTable::Constraint* constr) -> bool + { + return constr->type == lookForTableType; + } + ); + } + + if (!constraint && !tableConstraint) + return QString(); + + if (constraint) + { + SqlQueryModelColumn::Constraint* constr = SqlQueryModelColumn::Constraint::create(constraint); + QString tooltip = tooltipTpl.arg(constr->getIcon()->toUrl(), constr->getTypeString(), constr->getDetails()); + delete constr; + return tooltip; + } + + SqlQueryModelColumn::Constraint* constr = SqlQueryModelColumn::Constraint::create(createTable->columns[row]->name, tableConstraint); + if (!constr) + return QString(); + + QString tooltip = tooltipTpl.arg(constr->getIcon()->toUrl(), constr->getTypeString(), constr->getDetails()); + delete constr; + return tooltip; +} + void TableStructureModel::setCreateTable(SqliteCreateTable* value) { beginResetModel(); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h index 088a964..d003715 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h @@ -75,6 +75,7 @@ class GUI_API_EXPORT TableStructureModel : public QAbstractTableModel bool isColumnNotNull(SqliteCreateTable::Column* column) const; bool isColumnCollate(SqliteCreateTable::Column* column) const; bool isColumnGenerate(SqliteCreateTable::Column* column) const; + QString getToolTip(int row, TableStructureModel::Columns modelColumn) const; static const constexpr char* mimeType = "application/x-sqlitestudio-tablestructuremodel-row-index"; diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp index c9356d8..86f48bf 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp @@ -2,21 +2,16 @@ #include "ui_tablewindow.h" #include "services/dbmanager.h" #include "services/notifymanager.h" -#include "sqlitestudio.h" #include "common/unused.h" #include "schemaresolver.h" #include "iconmanager.h" -#include "common/intvalidator.h" -#include "common/extlineedit.h" #include "datagrid/sqltablemodel.h" -#include "common/extaction.h" #include "mainwindow.h" #include "tablestructuremodel.h" #include "tableconstraintsmodel.h" #include "dialogs/columndialog.h" #include "dialogs/constraintdialog.h" #include "mdiarea.h" -#include "sqlitesyntaxhighlighter.h" #include "dialogs/newconstraintdialog.h" #include "db/chainexecutor.h" #include "common/widgetcover.h" @@ -37,7 +32,6 @@ #include "themetuner.h" #include "dialogs/importdialog.h" #include "dialogs/populatedialog.h" -#include "datagrid/sqlqueryitem.h" #include "common/dbcombobox.h" #include #include @@ -217,10 +211,10 @@ void TableWindow::createActions() void TableWindow::createStructureActions() { - createAction(REFRESH_STRUCTURE, ICONS.RELOAD, tr("Refresh structure", "table window"), this, SLOT(refreshStructure()), ui->structureToolBar); + createAction(REFRESH_STRUCTURE, ICONS.RELOAD, tr("Refresh structure", "table window"), this, SLOT(refreshStructure()), ui->structureToolBar, ui->structureView); ui->structureToolBar->addSeparator(); - createAction(COMMIT_STRUCTURE, ICONS.COMMIT, tr("Commit structure changes", "table window"), this, SLOT(commitStructure()), ui->structureToolBar); - createAction(ROLLBACK_STRUCTURE, ICONS.ROLLBACK, tr("Rollback structure changes", "table window"), this, SLOT(rollbackStructure()), ui->structureToolBar); + createAction(COMMIT_STRUCTURE, ICONS.COMMIT, tr("Commit structure changes", "table window"), this, SLOT(commitStructure()), ui->structureToolBar, ui->structureView); + createAction(ROLLBACK_STRUCTURE, ICONS.ROLLBACK, tr("Rollback structure changes", "table window"), this, SLOT(rollbackStructure()), ui->structureToolBar, ui->structureView); createAction(ADD_COLUMN, ICONS.TABLE_COLUMN_ADD, tr("Add column", "table window"), this, SLOT(addColumn()), ui->structureToolBar, ui->structureView); createAction(EDIT_COLUMN, ICONS.TABLE_COLUMN_EDIT, tr("Edit column", "table window"), this, SLOT(editColumn()), ui->structureToolBar, ui->structureView); createAction(DEL_COLUMN, ICONS.TABLE_COLUMN_DELETE, tr("Delete column", "table window"), this, SLOT(delColumn()), ui->structureToolBar, ui->structureView); @@ -348,10 +342,10 @@ void TableWindow::executeStructureChanges() MessageListDialog dialog(tr("Following problems will take place while modifying the table.\n" "Would you like to proceed?", "table window")); dialog.setWindowTitle(tr("Table modification", "table window")); - for (const QString& error : tableModifier->getErrors()) + for (QString& error : tableModifier->getErrors()) dialog.addError(error); - for (const QString& warn : tableModifier->getWarnings()) + for (QString& warn : tableModifier->getWarnings()) dialog.addWarning(warn); if (dialog.exec() != QDialog::Accepted) @@ -458,8 +452,9 @@ void TableWindow::setupDefShortcuts() { // Widget context setShortcutContext({ + COMMIT_STRUCTURE, + ROLLBACK_STRUCTURE, REFRESH_STRUCTURE, - REFRESH_INDEXES, REFRESH_TRIGGERS, ADD_COLUMN, EDIT_COLUMN, @@ -567,6 +562,7 @@ void TableWindow::initDbAndTable() ui->constraintsView->setModel(constraintTabModel); connect(ui->withoutRowIdCheck, SIGNAL(clicked()), this, SLOT(withOutRowIdChanged())); + connect(ui->strictTableCheck, SIGNAL(clicked()), this, SLOT(strictChanged())); parseDdl(); updateIndexes(); @@ -614,7 +610,8 @@ void TableWindow::parseDdl() structureModel->setCreateTable(createTable.data()); structureConstraintsModel->setCreateTable(createTable.data()); constraintTabModel->setCreateTable(createTable.data()); - ui->withoutRowIdCheck->setChecked(!createTable->withOutRowId.isNull()); + ui->withoutRowIdCheck->setChecked(createTable->withOutRowId); + ui->strictTableCheck->setChecked(createTable->strict); ui->tableConstraintsView->resizeColumnsToContents(); ui->structureView->resizeColumnsToContents(); ui->constraintsView->resizeColumnsToContents(); @@ -645,7 +642,7 @@ void TableWindow::changeEvent(QEvent *e) QVariant TableWindow::saveSession() { - if (!db || DBLIST->isTemporary(db)) + if (!db || DBLIST->isTemporary(db) || !existingTable) return QVariant(); QHash sessionValue; @@ -681,7 +678,7 @@ bool TableWindow::restoreSession(const QVariant& sessionValue) SchemaResolver resolver(db); if (!resolver.getTables(database).contains(table, Qt::CaseInsensitive)) { - notifyWarn(tr("Could not restore window '%1'', because the table %2 doesn't exist in the database %3.").arg(value["title"].toString(), table, db->getName())); + notifyWarn(tr("Could not restore window '%1', because the table %2 doesn't exist in the database %3.").arg(value["title"].toString(), table, db->getName())); return false; } @@ -759,6 +756,7 @@ void TableWindow::checkIfTableDeleted(const QString& database, const QString& ob if (object.compare(table, Qt::CaseInsensitive) == 0) { dbClosedFinalCleanup(); + MDIAREA->enforceCurrentTaskSelectionAfterWindowClose(); getMdiWindow()->close(); } } @@ -832,6 +830,8 @@ void TableWindow::changesSuccessfullyCommitted() updateNewTableState(); updateWindowTitle(); + emit sessionValueChanged(); + NotifyManager* notifyManager = NotifyManager::getInstance(); if (oldTable.compare(table, Qt::CaseInsensitive) == 0 || oldTable.isEmpty()) { @@ -883,6 +883,8 @@ void TableWindow::rollbackStructure() structureConstraintsModel->setCreateTable(createTable.data()); constraintTabModel->setCreateTable(createTable.data()); ui->tableNameEdit->setText(createTable->table); + ui->withoutRowIdCheck->setChecked(createTable->withOutRowId); + ui->strictTableCheck->setChecked(createTable->strict); updateStructureCommitState(); updateStructureToolbarState(); @@ -1040,6 +1042,26 @@ bool TableWindow::validate(bool skipWarning) } } + if (ui->strictTableCheck->isChecked()) + { + QStringList nonStrictColumns; + for (SqliteCreateTable::Column* column : createTable->columns) + { + if (DataType::isStrict(column->type->name)) + continue; + + nonStrictColumns << column->name; + } + + if (!nonStrictColumns.isEmpty()) + { + notifyError(tr("Following columns have non-strict data type: %1." + " Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2") + .arg(nonStrictColumns.join(", "), DataType::getStrictValueNames().join(", "))); + return false; + } + } + return true; } @@ -1049,7 +1071,8 @@ bool TableWindow::isModified() const (structureConstraintsModel && structureConstraintsModel->isModified()) || (originalCreateTable && (originalCreateTable->table != ui->tableNameEdit->text() || - originalCreateTable->withOutRowId != createTable->withOutRowId) + originalCreateTable->withOutRowId != createTable->withOutRowId || + originalCreateTable->strict != createTable->strict) ) || !existingTable; } @@ -1355,7 +1378,26 @@ void TableWindow::withOutRowIdChanged() if (!createTable) return; - createTable->withOutRowId = ui->withoutRowIdCheck->isChecked() ? QStringLiteral("ROWID") : QString(); + createTable->withOutRowId = ui->withoutRowIdCheck->isChecked(); + updateDdlTab(); + emit modifyStatusChanged(); +} + +void TableWindow::strictChanged() +{ + if (!createTable) + return; + + createTable->strict = ui->strictTableCheck->isChecked(); + if (createTable->strict) + { + for (SqliteCreateTable::Column* column : createTable->columns) + { + column->type->precision = QVariant(); + column->type->scale = QVariant(); + } + } + updateDdlTab(); emit modifyStatusChanged(); } @@ -1401,7 +1443,7 @@ void TableWindow::delIndex() return; DbObjectDialogs dialogs(db, this); - dialogs.dropObject(index); + dialogs.dropObject(DbObjectDialogs::Type::INDEX, index); updateIndexes(); } @@ -1448,7 +1490,7 @@ void TableWindow::delTrigger() return; DbObjectDialogs dialogs(db, this); - dialogs.dropObject(trigger); + dialogs.dropObject(DbObjectDialogs::Type::TRIGGER, trigger); updateTriggers(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h index fb4d67d..e71162d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h @@ -30,31 +30,32 @@ namespace Ui { } CFG_KEY_LIST(TableWindow, QObject::tr("Table window"), - CFG_KEY_ENTRY(REFRESH_STRUCTURE, Qt::Key_F5, QObject::tr("Refresh table structure")) - CFG_KEY_ENTRY(ADD_COLUMN, Qt::Key_Insert, QObject::tr("Add new column")) - CFG_KEY_ENTRY(EDIT_COLUMN, Qt::Key_Return, QObject::tr("Edit selected column")) - CFG_KEY_ENTRY(DEL_COLUMN, Qt::Key_Delete, QObject::tr("Delete selected column")) - CFG_KEY_ENTRY(EXPORT, Qt::CTRL + Qt::Key_E, QObject::tr("Export table data")) - CFG_KEY_ENTRY(IMPORT, Qt::CTRL + Qt::Key_I, QObject::tr("Import data to the table")) - CFG_KEY_ENTRY(ADD_TABLE_CONSTRAINT, Qt::Key_Insert, QObject::tr("Add new table constraint")) - CFG_KEY_ENTRY(EDIT_TABLE_CONSTRAINT, Qt::Key_Return, QObject::tr("Edit selected table constraint")) - CFG_KEY_ENTRY(DEL_TABLE_CONSTRAINT, Qt::Key_Delete, QObject::tr("Delete selected table constraint")) - CFG_KEY_ENTRY(REFRESH_INDEXES, Qt::Key_F5, QObject::tr("Refresh table index list")) - CFG_KEY_ENTRY(ADD_INDEX, Qt::Key_Insert, QObject::tr("Add new index")) - CFG_KEY_ENTRY(EDIT_INDEX, Qt::Key_Return, QObject::tr("Edit selected index")) - CFG_KEY_ENTRY(DEL_INDEX, Qt::Key_Delete, QObject::tr("Delete selected index")) - CFG_KEY_ENTRY(REFRESH_TRIGGERS, Qt::Key_F5, QObject::tr("Refresh table trigger list")) - CFG_KEY_ENTRY(ADD_TRIGGER, Qt::Key_Insert, QObject::tr("Add new trigger")) - CFG_KEY_ENTRY(EDIT_TRIGGER, Qt::Key_Return, QObject::tr("Edit selected trigger")) - CFG_KEY_ENTRY(DEL_TRIGGER, Qt::Key_Delete, QObject::tr("Delete selected trigger")) - CFG_KEY_ENTRY(NEXT_TAB, Qt::ALT + Qt::Key_Right, QObject::tr("Go to next tab")) - CFG_KEY_ENTRY(PREV_TAB, Qt::ALT + Qt::Key_Left, QObject::tr("Go to previous tab")) + CFG_KEY_ENTRY(COMMIT_STRUCTURE, QKeySequence::Save, QObject::tr("Commit the table structure")) + CFG_KEY_ENTRY(ROLLBACK_STRUCTURE, QKeySequence::Cancel, QObject::tr("Rollback pending changes in the table structure")) + CFG_KEY_ENTRY(REFRESH_STRUCTURE, Qt::Key_F5, QObject::tr("Refresh table structure")) + CFG_KEY_ENTRY(ADD_COLUMN, Qt::Key_Insert, QObject::tr("Add new column")) + CFG_KEY_ENTRY(EDIT_COLUMN, Qt::Key_Return, QObject::tr("Edit selected column")) + CFG_KEY_ENTRY(DEL_COLUMN, Qt::Key_Delete, QObject::tr("Delete selected column")) + CFG_KEY_ENTRY(EXPORT, Qt::CTRL + Qt::Key_E, QObject::tr("Export table data")) + CFG_KEY_ENTRY(IMPORT, Qt::CTRL + Qt::Key_I, QObject::tr("Import data to the table")) + CFG_KEY_ENTRY(ADD_TABLE_CONSTRAINT, Qt::Key_Insert, QObject::tr("Add new table constraint")) + CFG_KEY_ENTRY(EDIT_TABLE_CONSTRAINT, Qt::Key_Return, QObject::tr("Edit selected table constraint")) + CFG_KEY_ENTRY(DEL_TABLE_CONSTRAINT, Qt::Key_Delete, QObject::tr("Delete selected table constraint")) + CFG_KEY_ENTRY(REFRESH_INDEXES, Qt::Key_F5, QObject::tr("Refresh table index list")) + CFG_KEY_ENTRY(ADD_INDEX, Qt::Key_Insert, QObject::tr("Add new index")) + CFG_KEY_ENTRY(EDIT_INDEX, Qt::Key_Return, QObject::tr("Edit selected index")) + CFG_KEY_ENTRY(DEL_INDEX, Qt::Key_Delete, QObject::tr("Delete selected index")) + CFG_KEY_ENTRY(REFRESH_TRIGGERS, Qt::Key_F5, QObject::tr("Refresh table trigger list")) + CFG_KEY_ENTRY(ADD_TRIGGER, Qt::Key_Insert, QObject::tr("Add new trigger")) + CFG_KEY_ENTRY(EDIT_TRIGGER, Qt::Key_Return, QObject::tr("Edit selected trigger")) + CFG_KEY_ENTRY(DEL_TRIGGER, Qt::Key_Delete, QObject::tr("Delete selected trigger")) + CFG_KEY_ENTRY(NEXT_TAB, Qt::ALT + Qt::Key_Right, QObject::tr("Go to next tab")) + CFG_KEY_ENTRY(PREV_TAB, Qt::ALT + Qt::Key_Left, QObject::tr("Go to previous tab")) ) class GUI_API_EXPORT TableWindow : public MdiChild { - Q_OBJECT - Q_ENUMS(Action) + Q_OBJECT public: enum Action @@ -98,6 +99,7 @@ class GUI_API_EXPORT TableWindow : public MdiChild NEXT_TAB, PREV_TAB }; + Q_ENUM(Action) enum ToolBar { @@ -230,6 +232,7 @@ class GUI_API_EXPORT TableWindow : public MdiChild void constraintsViewDoubleClicked(const QModelIndex &index); void nameChanged(); void withOutRowIdChanged(); + void strictChanged(); void addIndex(); void editCurrentIndex(); void indexViewDoubleClicked(const QModelIndex& idx); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui index 4ae8bdf..851daf1 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui @@ -83,11 +83,24 @@
    + + <html><head/><body><p>Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &quot;rowid&quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.</p></body></html> + WITHOUT ROWID + + + + <html><head/><body><p>Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.</p></body></html> + + + STRICT + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp index 2181934..44025e5 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp @@ -6,7 +6,6 @@ #include "services/dbmanager.h" #include "mainwindow.h" #include "mdiarea.h" -#include "sqlitesyntaxhighlighter.h" #include "datagrid/sqlquerymodel.h" #include "common/utils_sql.h" #include "viewmodifier.h" @@ -180,10 +179,13 @@ void ViewWindow::setupDefShortcuts() { // Widget context setShortcutContext({ + COMMIT_QUERY, + ROLLBACK_QUERY, REFRESH_TRIGGERS, ADD_TRIGGER, EDIT_TRIGGER, DEL_TRIGGER, + EXECUTE_QUERY }, Qt::WidgetWithChildrenShortcut); @@ -223,6 +225,7 @@ void ViewWindow::init() ui->queryEdit->setVirtualSqlExpression("CREATE VIEW name AS %1"); ui->queryEdit->setDb(db); + ui->queryEdit->setOpenSaveActionsEnabled(false); connect(dataModel, SIGNAL(executionSuccessful()), this, SLOT(executionSuccessful())); connect(dataModel, SIGNAL(executionFailed(QString)), this, SLOT(executionFailed(QString))); @@ -233,11 +236,12 @@ void ViewWindow::init() connect(ui->triggersList, SIGNAL(itemSelectionChanged()), this, SLOT(updateTriggersState())); connect(ui->triggersList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(triggerViewDoubleClicked(QModelIndex))); connect(ui->outputColumnsTable, SIGNAL(currentRowChanged(int)), this, SLOT(updateColumnButtons())); - connect(ui->outputColumnsTable->model(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), this, SLOT(updateColumnButtons())); - connect(ui->outputColumnsTable->model(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), this, SLOT(updateQueryToolbarStatus())); + connect(ui->outputColumnsTable->model(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(updateColumnButtons())); + connect(ui->outputColumnsTable->model(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(updateQueryToolbarStatus())); connect(ui->outputColumnsTable, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(updateQueryToolbarStatus())); - connect(CFG_UI.General.DataTabAsFirstInViews, SIGNAL(changed(const QVariant&)), this, SLOT(updateTabsOrder())); + connect(CFG_UI.General.DataTabAsFirstInViews, SIGNAL(changed(QVariant)), this, SLOT(updateTabsOrder())); connect(CFG_UI.Fonts.DataView, SIGNAL(changed(QVariant)), this, SLOT(updateFont())); + connect(NotifyManager::getInstance(), SIGNAL(objectModified(Db*,QString,QString)), this, SLOT(handleObjectModified(Db*,QString,QString))); structureExecutor = new ChainExecutor(this); connect(structureExecutor, SIGNAL(success(SqlQueryPtr)), this, SLOT(changesSuccessfullyCommitted())); @@ -296,7 +300,7 @@ void ViewWindow::initView() { dataModel->setDb(db); dataModel->setQuery(originalCreateView->select->detokenize()); - dataModel->setView(view); + dataModel->setDatabaseAndView(database, view); ui->dbCombo->setDisabled(true); } ui->queryEdit->setDb(db); @@ -313,6 +317,8 @@ void ViewWindow::initView() refreshTriggers(); + // Disconnect first in case this method is (re)executed upon dependant table changes. + disconnect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType))); connect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType))); } @@ -325,10 +331,10 @@ void ViewWindow::setupCoverWidget() void ViewWindow::createQueryTabActions() { - createAction(REFRESH_QUERY, ICONS.RELOAD, tr("Refresh the view", "view window"), this, SLOT(refreshView()), ui->queryToolbar); + createAction(REFRESH_QUERY, ICONS.RELOAD, tr("Refresh the view", "view window"), this, SLOT(refreshView()), ui->queryToolbar, ui->queryEdit); ui->queryToolbar->addSeparator(); - createAction(COMMIT_QUERY, ICONS.COMMIT, tr("Commit the view changes", "view window"), this, SLOT(commitView()), ui->queryToolbar); - createAction(ROLLBACK_QUERY, ICONS.ROLLBACK, tr("Rollback the view changes", "view window"), this, SLOT(rollbackView()), ui->queryToolbar); + createAction(COMMIT_QUERY, ICONS.COMMIT, tr("Commit the view changes", "view window"), this, SLOT(commitView()), ui->queryToolbar, ui->queryEdit); + createAction(ROLLBACK_QUERY, ICONS.ROLLBACK, tr("Rollback the view changes", "view window"), this, SLOT(rollbackView()), ui->queryToolbar, ui->queryEdit); ui->queryToolbar->addSeparator(); ui->queryToolbar->addAction(ui->queryEdit->getAction(SqlEditor::FORMAT_SQL)); @@ -344,6 +350,7 @@ void ViewWindow::createQueryTabActions() createAction(DEL_COLUMN, ICONS.TABLE_COLUMN_DELETE, tr("Delete column", "view window"), this, SLOT(delColumn()), ui->queryToolbar); createAction(MOVE_COLUMN_UP, ICONS.MOVE_UP, tr("Move column up", "view window"), this, SLOT(moveColumnUp()), ui->queryToolbar); createAction(MOVE_COLUMN_DOWN, ICONS.MOVE_DOWN, tr("Move column down", "view window"), this, SLOT(moveColumnDown()), ui->queryToolbar); + createAction(EXECUTE_QUERY, QString(), this, SLOT(executeQuery()), this); } void ViewWindow::createTriggersTabActions() @@ -431,8 +438,23 @@ void ViewWindow::refreshView() updateTriggersState(); } -void ViewWindow::commitView(bool skipWarnings) +void ViewWindow::executeQuery() +{ + if (isModified()) + { + if (!actionMap[COMMIT_QUERY]->isEnabled()) + return; + + commitView(false, true); + return; + } + + switchToDataAndReload(); +} + +void ViewWindow::commitView(bool skipWarnings, bool loadDataAfterNextCommit) { + this->loadDataAfterNextCommit = loadDataAfterNextCommit; if (!isModified()) { qWarning() << "Called ViewWindow::commitView(), but isModified() returned false."; @@ -535,6 +557,12 @@ int ViewWindow::getDdlTabIdx() const return ui->tabWidget->indexOf(ui->ddlTab); } +void ViewWindow::switchToDataAndReload() +{ + ui->tabWidget->setCurrentWidget(ui->dataTab); + // Query execution will happen automatically upon changing tab to data tab. +} + void ViewWindow::addTrigger() { DbObjectDialogs dialogs(db, this); @@ -560,7 +588,7 @@ void ViewWindow::deleteTrigger() return; DbObjectDialogs dialogs(db, this); - dialogs.dropObject(trigger); + dialogs.dropObject(DbObjectDialogs::Type::TRIGGER, trigger); refreshTriggers(); } @@ -573,7 +601,7 @@ void ViewWindow::executionSuccessful() void ViewWindow::executionFailed(const QString& errorMessage) { modifyingThisView = false; - notifyError(tr("Could not load data for view %1. Error details: %2").arg(view).arg(errorMessage)); + notifyError(tr("Could not load data for view %1. Error details: %2").arg(view, errorMessage)); } void ViewWindow::tabChanged(int tabIdx) @@ -615,7 +643,8 @@ void ViewWindow::updateQueryToolbarStatus() { bool modified = isModified(); bool queryOk = ui->queryEdit->isSyntaxChecked() && !ui->queryEdit->haveErrors(); - actionMap[COMMIT_QUERY]->setEnabled(modified && queryOk); + bool dbOk = ui->dbCombo->currentIndex() > -1; + actionMap[COMMIT_QUERY]->setEnabled(modified && queryOk && dbOk); actionMap[ROLLBACK_QUERY]->setEnabled(modified && existingView); actionMap[REFRESH_QUERY]->setEnabled(existingView); } @@ -635,6 +664,8 @@ void ViewWindow::changesSuccessfullyCommitted() QString oldView = view; view = createView->view; + emit sessionValueChanged(); + if (!existingView) notifyInfo(tr("View '%1' was committed successfully.").arg(view)); else if (oldView.compare(view, Qt::CaseInsensitive) == 0) @@ -649,6 +680,12 @@ void ViewWindow::changesSuccessfullyCommitted() updateAfterInit(); DBTREE->refreshSchema(db); + + if (loadDataAfterNextCommit) + { + loadDataAfterNextCommit = false; + switchToDataAndReload(); + } } void ViewWindow::changesFailedToCommit(int errorCode, const QString& errorText) @@ -720,6 +757,7 @@ void ViewWindow::checkIfViewDeleted(const QString& database, const QString& obje if (object.compare(view, Qt::CaseInsensitive) == 0) { dbClosedFinalCleanup(); + MDIAREA->enforceCurrentTaskSelectionAfterWindowClose(); getMdiWindow()->close(); } } @@ -1075,11 +1113,28 @@ void ViewWindow::updateFont() void ViewWindow::dbChanged() { - disconnect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType))); + if (db) + disconnect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType))); db = ui->dbCombo->currentDb(); dataModel->setDb(db); ui->queryEdit->setDb(db); - connect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType))); + if (db) + connect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType))); +} + +void ViewWindow::handleObjectModified(Db* db, const QString& database, const QString& object) +{ + UNUSED(db); + UNUSED(database); + if (object.compare(view, Qt::CaseInsensitive) != 0) + return; + +// TODO uncomment below when dbnames are supported +// if (this->database != database) +// return; + + view = object; + refreshView(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h index a2ef4f7..642c11a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h @@ -21,18 +21,20 @@ class ViewModifier; class SqlViewModel; CFG_KEY_LIST(ViewWindow, QObject::tr("A view window"), - CFG_KEY_ENTRY(REFRESH_TRIGGERS, Qt::Key_F5, QObject::tr("Refresh view trigger list")) - CFG_KEY_ENTRY(ADD_TRIGGER, Qt::Key_Insert, QObject::tr("Add new trigger")) - CFG_KEY_ENTRY(EDIT_TRIGGER, Qt::Key_Return, QObject::tr("Edit selected trigger")) - CFG_KEY_ENTRY(DEL_TRIGGER, Qt::Key_Delete, QObject::tr("Delete selected trigger")) - CFG_KEY_ENTRY(NEXT_TAB, Qt::ALT + Qt::Key_Right, QObject::tr("Go to next tab")) - CFG_KEY_ENTRY(PREV_TAB, Qt::ALT + Qt::Key_Left, QObject::tr("Go to previous tab")) + CFG_KEY_ENTRY(COMMIT_QUERY, QKeySequence::Save, QObject::tr("Commit the view's query")) + CFG_KEY_ENTRY(ROLLBACK_QUERY, QKeySequence::Cancel, QObject::tr("Rollback pending changes in the view's query")) + CFG_KEY_ENTRY(REFRESH_TRIGGERS, Qt::Key_F5, QObject::tr("Refresh view trigger list")) + CFG_KEY_ENTRY(EXECUTE_QUERY, Qt::Key_F9, QObject::tr("Execute the view's query")) + CFG_KEY_ENTRY(ADD_TRIGGER, Qt::Key_Insert, QObject::tr("Add new trigger")) + CFG_KEY_ENTRY(EDIT_TRIGGER, Qt::Key_Return, QObject::tr("Edit selected trigger")) + CFG_KEY_ENTRY(DEL_TRIGGER, Qt::Key_Delete, QObject::tr("Delete selected trigger")) + CFG_KEY_ENTRY(NEXT_TAB, Qt::ALT + Qt::Key_Right, QObject::tr("Go to next tab")) + CFG_KEY_ENTRY(PREV_TAB, Qt::ALT + Qt::Key_Left, QObject::tr("Go to previous tab")) ) class GUI_API_EXPORT ViewWindow : public MdiChild { - Q_OBJECT - Q_ENUMS(Action) + Q_OBJECT public: enum Action @@ -54,8 +56,10 @@ class GUI_API_EXPORT ViewWindow : public MdiChild DEL_TRIGGER, // All tabs NEXT_TAB, - PREV_TAB + PREV_TAB, + EXECUTE_QUERY }; + Q_ENUM(Action) enum ToolBar { @@ -117,6 +121,7 @@ class GUI_API_EXPORT ViewWindow : public MdiChild int getDataTabIdx() const; int getQueryTabIdx() const; int getDdlTabIdx() const; + void switchToDataAndReload(); Db* db = nullptr; QString database; @@ -137,10 +142,12 @@ class GUI_API_EXPORT ViewWindow : public MdiChild QAction* outputColumnsCheck = nullptr; QAction* outputColumnsSeparator = nullptr; bool tabsMoving = false; + bool loadDataAfterNextCommit = false; private slots: void refreshView(); - void commitView(bool skipWarnings = false); + void executeQuery(); + void commitView(bool skipWarnings = false, bool loadDataAfterNextCommit = false); void rollbackView(); void addTrigger(); void editTrigger(); @@ -168,6 +175,7 @@ class GUI_API_EXPORT ViewWindow : public MdiChild void triggerViewDoubleClicked(const QModelIndex& idx); void updateFont(); void dbChanged(); + void handleObjectModified(Db* db, const QString& database, const QString& object); public slots: void refreshTriggers(); diff --git a/SQLiteStudio3/lang.tcl b/SQLiteStudio3/lang.tcl index 52661f3..cafe27a 100755 --- a/SQLiteStudio3/lang.tcl +++ b/SQLiteStudio3/lang.tcl @@ -1,9 +1,9 @@ #!/usr/bin/env tclsh proc usage {} { - puts "$::argv0 (add|remove) " - puts "$::argv0 add_plugin " - puts "$::argv0 (update|release|status)" + puts "$::argv0 add_lang " + puts "$::argv0 add_plugin " + puts "$::argv0 status" } lassign $argv op lang @@ -40,6 +40,16 @@ proc countstrings {data search} { set count } +proc findLangs {} { + set langs [list] + foreach f [find coreSQLiteStudio "*.ts"] { + set lang [lindex [regexp -inline {[^_]*_(\w+(\w+)?).ts$} $f] 1] + if {$lang == ""} continue + lappend langs $lang + } + return $langs +} + proc scanLangs {} { set langs [dict create] foreach f [find .. "*.ts"] { @@ -64,211 +74,173 @@ proc scanLangs {} { return $langs } -switch -- $op { - "update" - "release" { - if {$argc != 1} { - usage - exit 1 +proc finalTsFix {f} { + set fd [open $f r] + set data [read $fd] + close $fd + + set parts [lsort -unique [regexp -all -inline -- {>[^<]*\"[^<]*<} $data]] + foreach part $parts { + set fixedPart [string map [list \" """] $part] + set data [string trim [string map [list $part $fixedPart] $data]] } + + set data [string trim [string map [list ' "'" " " " "] $data]] + + set fd [open $f w+] + puts $fd $data + close $fd +} - set files [list] - foreach p [list coreSQLiteStudio guiSQLiteStudio sqlitestudio sqlitestudiocli] { - lappend files $p/$p.pro - } +switch -- $op { + "update" { + if {$argc != 1} { + usage + exit 1 + } - foreach d [glob -directory ../Plugins -tails -nocomplain *] { - if {![file isdirectory ../Plugins/$d]} continue - lappend files ../Plugins/$d/$d.pro - } - - foreach f $files { - catch { - if {$op == "update"} { - exec lupdate $f - } else { - #exec lrelease $f $::ERR_NULL - exec lrelease $f + set files [list] + foreach p [list coreSQLiteStudio guiSQLiteStudio sqlitestudio sqlitestudiocli] { + lappend files $p/$p.pro + } + + foreach d [glob -directory ../Plugins -tails -nocomplain *] { + if {![file isdirectory ../Plugins/$d]} continue + lappend files ../Plugins/$d/$d.pro } - } res - if {$op == "release"} { - puts $res - } else { - foreach line [split $res \n] { - if {[string first Q_OBJECT $line] > -1} { - puts $line - } - if {[regexp -- {^.*\w+\.ts.*$} $line]} { - puts -nonewline [lindex [regexp -inline -- {^.*"([\w\/\\\.]+\.ts)".*$} $line] 1] - puts -nonewline ": " - } - if {[regexp -- {^.*\d+[^\d]+\(\d+[^\d]+\d+.*\).*$} $line]} { - puts -nonewline [lindex [regexp -inline -- {\S+.*} $line] 0] - set new [lindex [regexp -inline -- {^.*\d+[^\d]+(\d+)[^\d]+\d+.*$} $line] 1] - if {$new > 0} { - puts -nonewline " <- !!!!!!!!!!!" + + foreach f $files { + catch { + puts "updating $f" + exec lupdate $f + } res + # foreach line [split $res \n] { + # if {[string first Q_OBJECT $line] > -1} { + # puts $line + # } + # if {[regexp -- {^.*\w+\.ts.*$} $line]} { + # puts -nonewline [lindex [regexp -inline -- {^.*"([\w\/\\\.]+\.ts)".*$} $line] 1] + # puts -nonewline ": " + # } + # if {[regexp -- {^.*\d+[^\d]+\(\d+[^\d]+\d+.*\).*$} $line]} { + # puts -nonewline [lindex [regexp -inline -- {\S+.*} $line] 0] + # set new [lindex [regexp -inline -- {^.*\d+[^\d]+(\d+)[^\d]+\d+.*$} $line] 1] + # if {$new > 0} { + # puts -nonewline " <- !!!!!!!!!!!" + # } + # puts "" + # } + # } + } + + set tsFiles [list] + foreach d [list coreSQLiteStudio guiSQLiteStudio sqlitestudio sqlitestudiocli] { + lappend tsFiles {*}[glob -directory $d/translations -nocomplain *.ts] + } + + foreach d [glob -directory ../Plugins -tails -nocomplain *] { + if {![file isdirectory ../Plugins/$d/translations]} continue + lappend tsFiles {*}[glob -directory ../Plugins/$d/translations -nocomplain *.ts] + } + + foreach f $tsFiles { + if {[catch { + puts "formatting $f" + exec xmllint --format -o $f $f + finalTsFix $f + } res]} { + puts $res } - puts "" - } } - } - } } "status" { - set langs [scanLangs] - foreach k [dict keys $langs] { - set lang [dict get $langs $k] - set tr [dict get $lang translated] - set untr [dict get $lang untranslated] - set all [expr {$tr + $untr}] - if {$all == 0} continue - - set perc [expr {round(double($tr)/$all * 1000)/10.0}] - - set lang [string tolower $lang] - puts "$k - ${perc}% ($tr / $all)" - } + set langs [scanLangs] + foreach k [dict keys $langs] { + set lang [dict get $langs $k] + set tr [dict get $lang translated] + set untr [dict get $lang untranslated] + set all [expr {$tr + $untr}] + if {$all == 0} continue + + set perc [expr {round(double($tr)/$all * 1000)/10.0}] + + set lang [string tolower $lang] + puts "$lang - ${perc}% ($tr / $all)" + } } "add_plugin" { - if {$argc != 2} { - usage - exit 1 - } - - set plug [lindex $argv 1] - set plugPro ../Plugins/$plug/$plug.pro - if {![file exists $plugPro]} { - puts "$plugPro does not exist." - exit 1 - } - - set fd [open ../Plugins/CsvImport/CsvImport.pro r] - set data [read $fd] - close $fd - - set langs [list] - set trData "\nTRANSLATIONS += " - foreach {all lang} [regexp -inline -all -- {CsvImport_(\w+)\.ts} $data] { - append trData "\\\n\t\t${plug}_$lang.ts" - lappend langs $lang - } - append trData "\n" - - set fd [open $plugPro a+] - puts $fd $trData - close $fd - puts "Added translation languages for plugin $plug:\n[join $langs \n]" + if {$argc != 2} { + usage + exit 1 + } + + set plug [lindex $argv 1] + set plugPro ../Plugins/$plug/$plug.pro + if {![file exists $plugPro]} { + puts "$plugPro does not exist." + exit 1 + } + + if {![file exists ../Plugins/$plug/translations]} { + file mkdir ../Plugins/$plug/translations + } + + set plugTs ../Plugins/$plug/translations/$plug.ts + if {![file exists $plugTs]} { + catch { + exec lupdate-pro $plugPro -ts $plugTs + } + if {![file exists $plugTs]} { + puts "Failed to create $plugTs." + exit 1 + } + } + + foreach lang [findLangs] { + set plugTs ../Plugins/$plug/translations/${plug}_${lang}.ts + if {![file exists $plugTs]} { + catch {exec lupdate-pro $plugPro -ts $plugTs} + } + } } - "add" - "remove" { - if {$argc != 2} { - usage - exit 1 - } - - foreach p [list coreSQLiteStudio guiSQLiteStudio sqlitestudio sqlitestudiocli] { - # pro file - set fd [open $p/$p.pro r] - set data [read $fd] - close $fd - - set ts "translations/${p}_$lang.ts" - if {$op == "add" && [string first $ts $data] == -1} { - set data [string map [list "TRANSLATIONS += " "TRANSLATIONS += $ts \\\n\t\t"] $data] - } elseif {$op == "remove" && [string first $ts $data] > -1} { - regsub -- "$ts\\s*(\\\\)?\n\\s*" $data "" data - } else { - continue - } - - set fd [open $p/$p.pro w+] - puts $fd $data - close $fd - - puts "Updated $p.pro" - } - - foreach p [list coreSQLiteStudio guiSQLiteStudio sqlitestudio sqlitestudiocli] { - # qrc file - set fd [open $p/$p.qrc r] - set data [read $fd] - close $fd - - set qm "translations/${p}_$lang.qm" - if {$op == "add" && [string first $qm $data] == -1} { - set data [string map [list "" "\n $qm"] $data] - } elseif {$op == "remove" && [string first $qm $data] > -1} { - regsub -- "\\s*$qm\\s*\n" $data "" data - } else { - continue - } - - set fd [open $p/$p.qrc w+] - puts $fd $data - close $fd - - puts "Updated $p.qrc" - } - - foreach d [glob -directory ../Plugins -tails -nocomplain *] { - if {![file isdirectory ../Plugins/$d]} continue - - # pro file - set fd [open ../Plugins/$d/$d.pro r] - set data [read $fd] - close $fd + "add_lang" { + if {$argc != 2} { + usage + exit 1 + } - if {[string first "TRANSLATIONS +=" $data] == -1} continue + foreach d [list coreSQLiteStudio guiSQLiteStudio sqlitestudio sqlitestudiocli] { + set pro $d/$d.pro + set ts "$d/translations/${d}_$lang.ts" + if {![file exists $ts]} { + catch {exec lupdate-pro $pro -ts $ts} + } + } - set ts "${d}_$lang.ts" - if {$op == "add" && [string first $ts $data] == -1} { - set data [string map [list "TRANSLATIONS += " "TRANSLATIONS += $ts \\\n\t\t"] $data] - } elseif {$op == "remove" && [string first $ts $data] > -1} { - regsub -- "$ts\\s*(\\\\)?\n\\s*" $data "" data - } else { - continue - } - - set fd [open ../Plugins/$d/$d.pro w+] - puts $fd $data - close $fd - - puts "Updated $d.pro" + foreach d [glob -directory ../Plugins -tails -nocomplain *] { + if {![file isdirectory ../Plugins/$d]} continue + set pro ../Plugins/$d/$d.pro + set ts "../Plugins/$d/translations/${d}_$lang.ts" + if {![file exists $ts]} { + catch {exec lupdate-pro $pro -ts $ts} + } + } } - - foreach d [glob -directory ../Plugins -tails -nocomplain *] { - # qrc file - if {![file isdirectory ../Plugins/$d]} continue - if {[file exists ../Plugins/$d/$d.qrc]} { - set fname ../Plugins/$d/$d.qrc - set fnameOnly $d.qrc - } elseif {[file exists ../Plugins/$d/[string tolower $d].qrc]} { - set fname ../Plugins/$d/[string tolower $d].qrc - set fnameOnly [string tolower $d].qrc - } else { - continue - } - - set fd [open $fname r] - set data [read $fd] - close $fd - - if {[string first "" $data] == -1} continue - - set qm "${d}_$lang.qm" - if {$op == "add" && [string first $qm $data] == -1} { - set data [string map [list "" "\n $qm"] $data] - } elseif {$op == "remove" && [string first $qm $data] > -1} { - regsub -- "\\s*$qm\\s*\n" $data "" data - } else { - continue - } - - set fd [open $fname w+] - puts $fd $data - close $fd - - puts "Updated $fnameOnly" + "validate" { + set ok 0 + set fail 0 + foreach f [find .. "*.ts"] { + catch {exec xmllint --noout --schema ts.xsd $f} out + set parts [split $out " "] + if {[lindex $parts 1] != "validates"} { + puts "$f: $out" + incr fail + } else { + incr ok + } + } + puts "Ok: $ok, Failed: $fail" } - } default { usage } diff --git a/SQLiteStudio3/plugins.pri b/SQLiteStudio3/plugins.pri index 0894050..2b08db5 100644 --- a/SQLiteStudio3/plugins.pri +++ b/SQLiteStudio3/plugins.pri @@ -1,4 +1,4 @@ -include($$PWD/dirs.pri) +include($$PWD/common.pri) CONFIG += c++17 plugin @@ -95,7 +95,7 @@ macx: { out_file = $$join(out_file_parts) lib_name_parts = lib $$1 .dylib lib_name = $$join(lib_name_parts) - QMAKE_POST_LINK += install_name_tool -change $$lib_name @loader_path/../PlugIns/$$lib_name $$out_file + QMAKE_POST_LINK += "install_name_tool -change $$lib_name @loader_path/../PlugIns/$$lib_name \"$$out_file\";" export(QMAKE_POST_LINK) linker_flag_parts = -l $$1 @@ -109,7 +109,7 @@ macx: { out_file = $$join(out_file_parts) lib_name_parts = lib $$1 .dylib lib_name = $$join(lib_name_parts) - QMAKE_POST_LINK += install_name_tool -change $$lib_name @loader_path/../Frameworks/$$lib_name $$out_file + QMAKE_POST_LINK += "install_name_tool -change $$lib_name @loader_path/../Frameworks/$$lib_name \"$$out_file\";" export(QMAKE_POST_LINK) linker_flag_parts = -l $$1 diff --git a/SQLiteStudio3/sqlitestudio/installscript.qs b/SQLiteStudio3/sqlitestudio/installscript.qs deleted file mode 100644 index 8e6b84b..0000000 --- a/SQLiteStudio3/sqlitestudio/installscript.qs +++ /dev/null @@ -1,64 +0,0 @@ -function Component() -{ - if (installer.value("os") === "win") { - component.loaded.connect(this, addOptionsCheckBoxForm); - component.fileTypes = ['db', 'db3', 'sqlite', 'sqlite3', 'sdb', 's3db']; - } -} - -addOptionsCheckBoxForm = function() -{ - // don't show when updating or uninstalling - if (installer.isInstaller()) { - installer.addWizardPageItem(component, "OptionsCheckBoxForm", QInstaller.TargetDirectory); - var form = component.userInterface("OptionsCheckBoxForm"); - - var assocCheckBox = form.RegisterFileCheckBox; - assocCheckBox.text = assocCheckBox.text + component.fileTypes.join(', '); - - var startMenuCheckbox = form.CreateStartMenuEntry; - startMenuCheckbox.stateChanged.connect(this, function() { - installer.setDefaultPageVisible(QInstaller.StartMenuSelection, startMenuCheckbox.checked); - }); - } -} - -Component.prototype.createOperations = function() -{ - // call default implementation to actually install the app - component.createOperations(); - - if (installer.value("os") === "win") { - var form = component.userInterface("OptionsCheckBoxForm"); - var isRegisterFileChecked = form.RegisterFileCheckBox.checked; - var isStartMenuEntryChecked = form.CreateStartMenuEntry.checked; - var forAllUsersChecked = form.CreateStartMenuEntry.ForAllUsers.checked; - - var executable = "@TargetDir@/SQLiteStudio.exe"; - - var linkPrefix = "@UserStartMenuProgramsPath@"; - if (forAllUsersChecked) { - linkPrefix = "@AllUsersStartMenuProgramsPath@"; - } - - if (isRegisterFileChecked) { - component.addOperation("CreateShortcut", executable, linkPrefix + "/@StartMenuDir@/SQLiteStudio.lnk", - "workingDirectory=@TargetDir@", "iconPath=@TargetDir@/SQLiteStudio.exe", - "iconId=0", "description=SQLiteStudio"); - } - - if (isRegisterFileChecked) { - component.fileTypes.forEach(function(fileType) { - component.addOperation( - "RegisterFileType", - fileType, - executable + " '%1'", - "SQLite database", - "application/octet-stream", - executable + ",0", - "ProgId=SQLiteStudio." + fileType - ); - }); - } - } -} diff --git a/SQLiteStudio3/sqlitestudio/main.cpp b/SQLiteStudio3/sqlitestudio/main.cpp index ae16132..c3b09e0 100644 --- a/SQLiteStudio3/sqlitestudio/main.cpp +++ b/SQLiteStudio3/sqlitestudio/main.cpp @@ -13,13 +13,10 @@ #include "multieditor/multieditortime.h" #include "multieditor/multieditordate.h" #include "multieditor/multieditorbool.h" -#include "uiconfig.h" -#include "sqlitestudio.h" #include "uidebug.h" #include "completionhelper.h" #include "services/updatemanager.h" #include "guiSQLiteStudio_global.h" -#include "coreSQLiteStudio_global.h" #include "log.h" #include "qio.h" #include "translations.h" @@ -27,6 +24,7 @@ #include "dialogs/triggerdialog.h" #include "services/pluginmanager.h" #include "singleapplication/singleapplication.h" +#include "services/impl/configimpl.h" #include #include #include @@ -113,6 +111,12 @@ void initHighDpi() #endif } +bool shouldAllowMultipleSessions() +{ + QVariant allowMultipleSessionsValue = Config::getSettings()->value(MainWindow::ALLOW_MULTIPLE_SESSIONS_SETTING); + return allowMultipleSessionsValue.isValid() && allowMultipleSessionsValue.toBool(); +} + int main(int argc, char *argv[]) { initHighDpi(); @@ -120,13 +124,9 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationName("SalSoft"); QCoreApplication::setApplicationVersion(SQLITESTUDIO->getVersionString()); - SingleApplication a(argc, argv, true, SingleApplication::ExcludeAppPath|SingleApplication::ExcludeAppVersion); - - QSettings sett; - QVariant allowMultipleSessionsValue = sett.value(MainWindow::ALLOW_MULTIPLE_SESSIONS_SETTING); - bool allowMultipleSessions = allowMultipleSessionsValue.isValid() && allowMultipleSessionsValue.toBool(); + SingleApplication a(argc, argv, true, SingleApplication::ExcludeAppPath|SingleApplication::ExcludeAppVersion|SingleApplication::User); - if (!allowMultipleSessions && a.isSecondary()) { + if (!shouldAllowMultipleSessions() && a.isSecondary()) { #ifdef Q_OS_WIN AllowSetForegroundWindow(DWORD( a.primaryPid())); #endif @@ -162,6 +162,7 @@ int main(int argc, char *argv[]) MultiEditorDate::staticInit(); MultiEditorBool::staticInit(); TriggerDialog::staticInit(); + SqlEditor::staticInit(); MainWindow* mainWin = MAINWINDOW; @@ -190,7 +191,7 @@ int main(int argc, char *argv[]) if (dialog.exec() == QDialog::Accepted) setDefaultLanguage(dialog.getSelectedLang()); - QProcess::startDetached(a.applicationFilePath(), QStringList()); + QProcess::startDetached(qApp->arguments().at(0), qApp->arguments().mid(1)); return 0; } diff --git a/SQLiteStudio3/sqlitestudio/register_file_types.ui b/SQLiteStudio3/sqlitestudio/register_file_types.ui deleted file mode 100644 index 24d896d..0000000 --- a/SQLiteStudio3/sqlitestudio/register_file_types.ui +++ /dev/null @@ -1,64 +0,0 @@ - - - OptionsCheckBoxForm - - - - 0 - 0 - 400 - 300 - - - - Form - - - - 20 - - - - - Register following file extensions with SQLiteStudio: - - - - true - - - - - - - Create Start menu entry - - - true - - - - - - Create for current user only - - - true - - - - - - - Create for all users in the system - - - - - - - - - - - diff --git a/SQLiteStudio3/sqlitestudio/sqlitestudio.pro b/SQLiteStudio3/sqlitestudio/sqlitestudio.pro index bdb2876..dc73271 100644 --- a/SQLiteStudio3/sqlitestudio/sqlitestudio.pro +++ b/SQLiteStudio3/sqlitestudio/sqlitestudio.pro @@ -6,7 +6,7 @@ QT += core gui widgets network -include($$PWD/../dirs.pri) +include($$PWD/../common.pri) include($$PWD/../utils.pri) OBJECTS_DIR = $$OBJECTS_DIR/sqlitestudio @@ -42,17 +42,6 @@ LIBS += -lcoreSQLiteStudio -lguiSQLiteStudio SOURCES += main.cpp \ singleapplication/singleapplication.cpp -TRANSLATIONS += translations/sqlitestudio_ro_RO.ts \ - translations/sqlitestudio_de.ts \ - translations/sqlitestudio_it.ts \ - translations/sqlitestudio_zh_CN.ts \ - translations/sqlitestudio_sk.ts \ - translations/sqlitestudio_ru.ts \ - translations/sqlitestudio_pt_BR.ts \ - translations/sqlitestudio_fr.ts \ - translations/sqlitestudio_es.ts \ - translations/sqlitestudio_pl.ts - win32 { RC_FILE = windows.rc msvc:LIBS += User32.lib @@ -87,6 +76,17 @@ HEADERS += \ + + + + + + + + + + + diff --git a/SQLiteStudio3/sqlitestudio/sqlitestudio.qrc b/SQLiteStudio3/sqlitestudio/sqlitestudio.qrc index 6431227..7646d2b 100644 --- a/SQLiteStudio3/sqlitestudio/sqlitestudio.qrc +++ b/SQLiteStudio3/sqlitestudio/sqlitestudio.qrc @@ -1,15 +1 @@ - - - translations/sqlitestudio_ro_RO.qm - translations/sqlitestudio_de.qm - translations/sqlitestudio_pl.qm - translations/sqlitestudio_ru.qm - translations/sqlitestudio_fr.qm - translations/sqlitestudio_sk.qm - translations/sqlitestudio_zh_CN.qm - - - - - - + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio.ts new file mode 100644 index 0000000..8f62df5 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + + + + + Enables debug messages in console (accessible with F12). + + + + + Redirects debug messages into standard output (forces debug mode). + + + + + Redirects debug messages into given file (forces debug mode). + + + + + log file + + + + + Enables Lemon parser debug messages for SQL code assistant. + + + + + Enables debugging of every single SQL query being sent to any database. + + + + + Limits SQL query messages to only the given <database>. + + + + + database + + + + + Enables debugging of SQLiteStudio's query executor. + + + + + Lists plugins installed in the SQLiteStudio and quits. + + + + + Points to the master configuration file. Read manual at wiki page for more details. + + + + + SQLiteStudio settings file + + + + + file + + + + + Database file to open + + + + + Select configuration directory + + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_af_ZA.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_af_ZA.ts new file mode 100644 index 0000000..8e0b089 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_af_ZA.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ar_SA.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ar_SA.ts new file mode 100644 index 0000000..01c3a3a --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ar_SA.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ca_ES.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ca_ES.ts new file mode 100644 index 0000000..6a437b3 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ca_ES.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_cs_CZ.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_cs_CZ.ts new file mode 100644 index 0000000..07e018c --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_cs_CZ.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_da_DK.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_da_DK.ts new file mode 100644 index 0000000..abf6784 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_da_DK.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de.qm b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de.qm deleted file mode 100644 index b319573..0000000 Binary files a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de.ts deleted file mode 100644 index a65829c..0000000 --- a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de.ts +++ /dev/null @@ -1,88 +0,0 @@ - - - - - QObject - - - GUI interface to SQLiteStudio, a SQLite manager. - GUI Schnittstelle zu SQLiteStudio, ein SQLite Manager. - - - - Enables debug messages in console (accessible with F12). - Aktiviert Debug-Meldungen in der Konsole (erreichbar über F12). - - - - Redirects debug messages into standard output (forces debug mode). - Leitet Debug-Meldungen in den Standardausgabekanal um (erzwingt den Debugmodus). - - - - Redirects debug messages into given file (forces debug mode). - Leitet Debug-Meldungen in die angegebene Datei um (erzwingt den Debugmodus). - - - - log file - Logdatei - - - - Enables Lemon parser debug messages for SQL code assistant. - Aktiviert Lemon Parser Debug-Meldungen für den SQL Codeassistenten. - - - - Enables debugging of every single SQL query being sent to any database. - Aktiviert das Debugging für jede SQL Abfrage, die an eine beliebige Datenbank gesendet wurde. - - - - Limits SQL query messages to only the given <database>. - Wird <database> bei Ausgabe der Meldung in die aktuelle Datenbankbezeichnung umgewandelt? - Begrenzt SQL Abfragemeldungen auf folgende Datenbank: <database>. - - - - database - Datenbank - - - - Enables debugging of SQLiteStudio's query executor. - - - - - Lists plugins installed in the SQLiteStudio and quits. - Was wird hier beendet? - Listet die in SQLiteStudio installierten Plugins auf und beendet. - - - - Points to the master configuration file. Read manual at wiki page for more details. - - - - - SQLiteStudio settings file - - - - - file - Datei - - - - Database file to open - Zu öffnende Datenbankdatei - - - Error - Fehler - - - diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de_DE.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de_DE.ts new file mode 100644 index 0000000..f1d5036 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_de_DE.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI Schnittstelle zu SQLiteStudio, ein SQLite Manager. + + + + Enables debug messages in console (accessible with F12). + Aktiviert Debug-Meldungen in der Konsole (erreichbar über F12). + + + + Redirects debug messages into standard output (forces debug mode). + Leitet Debug-Meldungen in den Standardausgabekanal um (erzwingt den Debugmodus). + + + + Redirects debug messages into given file (forces debug mode). + Leitet Debug-Meldungen in die angegebene Datei um (erzwingt den Debugmodus). + + + + log file + Logdatei + + + + Enables Lemon parser debug messages for SQL code assistant. + Aktiviert Lemon Parser Debug-Meldungen für den SQL Codeassistenten. + + + + Enables debugging of every single SQL query being sent to any database. + Aktiviert das Debugging für jede SQL Abfrage, die an eine beliebige Datenbank gesendet wurde. + + + + Limits SQL query messages to only the given <database>. + Begrenzt SQL Abfragemeldungen auf folgende Datenbank: <database>. + + + + database + Datenbank + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Listet die in SQLiteStudio installierten Plugins auf und beendet. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio Einstellungsdatei + + + + file + Datei + + + + Database file to open + Zu öffnende Datenbankdatei + + + + Select configuration directory + Konfigurationsverzeichnis auswählen + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_el_GR.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_el_GR.ts new file mode 100644 index 0000000..e795650 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_el_GR.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_en_US.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_en_US.ts new file mode 100644 index 0000000..c54a2af --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_en_US.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es.qm b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es.ts deleted file mode 100644 index ff56801..0000000 --- a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es.ts +++ /dev/null @@ -1,82 +0,0 @@ - - - - - QObject - - - GUI interface to SQLiteStudio, a SQLite manager. - - - - - Enables debug messages in console (accessible with F12). - - - - - Redirects debug messages into standard output (forces debug mode). - - - - - Redirects debug messages into given file (forces debug mode). - - - - - log file - - - - - Enables Lemon parser debug messages for SQL code assistant. - - - - - Enables debugging of every single SQL query being sent to any database. - - - - - Limits SQL query messages to only the given <database>. - - - - - database - - - - - Enables debugging of SQLiteStudio's query executor. - - - - - Lists plugins installed in the SQLiteStudio and quits. - - - - - Points to the master configuration file. Read manual at wiki page for more details. - - - - - SQLiteStudio settings file - - - - - file - - - - - Database file to open - - - - diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es_ES.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es_ES.ts new file mode 100644 index 0000000..e073410 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_es_ES.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + Interfaz GUI para SQLiteStudio, un gestor de SQLite. + + + + Enables debug messages in console (accessible with F12). + Habilita mensajes de depuración en consola (accesible con F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirige mensajes de depuración a salida estándar (fuerza el modo de depuración). + + + + Redirects debug messages into given file (forces debug mode). + Redirige los mensajes de depuración en un archivo determinado (fuerza el modo de depuración). + + + + log file + archivo log + + + + Enables Lemon parser debug messages for SQL code assistant. + Activa los mensajes de depuración de Lemon para el asistente de código SQL. + + + + Enables debugging of every single SQL query being sent to any database. + Permite la depuración de cada consulta SQL que se envía a cualquier base de datos. + + + + Limits SQL query messages to only the given <database>. + Limita los mensajes de consulta SQL a sólo la <database> dada. + + + + database + base de datos + + + + Enables debugging of SQLiteStudio's query executor. + Habilita la depuración del ejecutor de consultas de SQLiteStudio. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lista plugins instalados en SQLiteStudio y cierra la ventana. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Apunta al archivo de configuración maestro. Lee el manual en la página wiki para más detalles. + + + + SQLiteStudio settings file + Archivo de configuración de SQLiteStudio + + + + file + archivo + + + + Database file to open + Archivo de base de datos a abrir + + + + Select configuration directory + Elegir directorio de configuración + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fa_IR.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fa_IR.ts new file mode 100644 index 0000000..4d45909 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fa_IR.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fi_FI.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fi_FI.ts new file mode 100644 index 0000000..119d04c --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fi_FI.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr.qm b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr.qm deleted file mode 100644 index de3c6d6..0000000 Binary files a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr.ts deleted file mode 100644 index 087d5b8..0000000 --- a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr.ts +++ /dev/null @@ -1,86 +0,0 @@ - - - - - QObject - - - GUI interface to SQLiteStudio, a SQLite manager. - Interface GUI de SQLiteStudio, un outil pour SQLite. - - - - Enables debug messages in console (accessible with F12). - Messages de déboguage dans la console (accessible avec F12). - - - - Redirects debug messages into standard output (forces debug mode). - Messages de déboguage redirigés vers sortie standard (mode déboguage forcé). - - - - Redirects debug messages into given file (forces debug mode). - Messages de déboguage redirigés vers un fichier (mode déboguage forcé). - - - - log file - fichier log - - - - Enables Lemon parser debug messages for SQL code assistant. - Message de déboguage avec l’analyseur Lemon pour un assistant code SQL. - - - - Enables debugging of every single SQL query being sent to any database. - Déboguage pour chaque requête SQL envoyée à toute base de données. - - - - Limits SQL query messages to only the given <database>. - Limite les messages de la requête SQL à la <database>. - - - - database - Base de données - - - - Enables debugging of SQLiteStudio's query executor. - Déboguage de l'exécuteur de requêtes de SQLiteStudio. - - - - Lists plugins installed in the SQLiteStudio and quits. - Liste les plugins installés dans SQLiteStudio et quitte. - - - - Points to the master configuration file. Read manual at wiki page for more details. - - - - - SQLiteStudio settings file - - - - - file - Fichier - - - - Database file to open - Fichier de la base de données à ouvrir - - - Error - Erreur - - - diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr_FR.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr_FR.ts new file mode 100644 index 0000000..50a0889 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_fr_FR.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + Interface GUI de SQLiteStudio, un outil pour SQLite. + + + + Enables debug messages in console (accessible with F12). + Messages de déboguage dans la console (accessible avec F12). + + + + Redirects debug messages into standard output (forces debug mode). + Messages de déboguage redirigés vers sortie standard (mode déboguage forcé). + + + + Redirects debug messages into given file (forces debug mode). + Messages de déboguage redirigés vers un fichier (mode déboguage forcé). + + + + log file + fichier log + + + + Enables Lemon parser debug messages for SQL code assistant. + Message de déboguage avec l’analyseur Lemon pour un assistant code SQL. + + + + Enables debugging of every single SQL query being sent to any database. + Déboguage pour chaque requête SQL envoyée à toute base de données. + + + + Limits SQL query messages to only the given <database>. + Limite les messages de la requête SQL à la <database>. + + + + database + Base de données + + + + Enables debugging of SQLiteStudio's query executor. + Active le débogage de SQLiteStudio l'exécuteur de requête. + + + + Lists plugins installed in the SQLiteStudio and quits. + Liste les plugins installés dans SQLiteStudio et quitte. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Pointe vers le fichier de configuration maître. Lisez le manuel sur la page wiki pour plus de détails. + + + + SQLiteStudio settings file + Fichier de paramètres SQLiteStudio + + + + file + Fichier + + + + Database file to open + Fichier de la base de données à ouvrir + + + + Select configuration directory + Sélectionnez le répertoire de configuration + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_he_IL.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_he_IL.ts new file mode 100644 index 0000000..0dcf173 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_he_IL.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + מנשק משתמש גרפי SQLiteStudio, תכנת ניהול SQLite. + + + + Enables debug messages in console (accessible with F12). + ×פשור הודעות ניפוי ×ª×§×œ×™× ×‘×¢×ž×“×ªÖ¾×”×‘×§×¨×” (נגיש ב×מצעות מקש F12). + + + + Redirects debug messages into standard output (forces debug mode). + הפנית הודעות ניפוי ×ª×§×œ×™× ×œ×¤×œ×˜ תקני (×ילוץ מצב ניפוי תקלי×). + + + + Redirects debug messages into given file (forces debug mode). + הפנית הודעות ניפוי ×ª×§×œ×™× ×œ×§×•×‘×¥ נתון (×ילוץ מצב ניפוי תקלי×). + + + + log file + קובץ יומן + + + + Enables Lemon parser debug messages for SQL code assistant. + ×פשור הודעות ניפוי ×ª×§×œ×™× × ×™×ª×•×— תחביר Lemon לסייען קוד SQL. + + + + Enables debugging of every single SQL query being sent to any database. + ×פשור ניפוי ×ª×§×œ×™× ×œ×›×œ ש×ילתת SQL שנשלחת למסד × ×ª×•× ×™× ×›×œ×©×”×•. + + + + Limits SQL query messages to only the given <database>. + הגבלת הודעות ש×ילתות SQL למסד ×”× ×ª×•× ×™× ×”× ×ª×•×Ÿ בלבד. + + + + database + מסד × ×ª×•× ×™× + + + + Enables debugging of SQLiteStudio's query executor. + ×פשור ניפוי ×ª×§×œ×™× ×ž×¨×™×¥ הש×ילתות של SQLiteStudio. + + + + Lists plugins installed in the SQLiteStudio and quits. + הצגת רשימת ×ž×ª×§×¢×™× ×ž×•×ª×§× ×™× ×‘Ö¾SQLiteStudio ויצי××”. + + + + Points to the master configuration file. Read manual at wiki page for more details. + הפניה לקובץ התצורה הר×שי. ×¤×¨×˜×™× × ×•×¡×¤×™×, ניתן ×œ×ž×¦×•× ×‘×ž×“×¨×™×š למשתמש בעמוד הויקי. + + + + SQLiteStudio settings file + קובץ הגדרות SQLiteStudio + + + + file + קובץ + + + + Database file to open + קובץ מסד × ×ª×•× ×™× ×œ×¤×ª×™×—×” + + + + Select configuration directory + בחירת מחיצת תצור + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_hu_HU.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_hu_HU.ts new file mode 100644 index 0000000..1d0d851 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_hu_HU.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it.qm b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it.ts deleted file mode 100644 index ef01351..0000000 --- a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it.ts +++ /dev/null @@ -1,82 +0,0 @@ - - - - - QObject - - - GUI interface to SQLiteStudio, a SQLite manager. - - - - - Enables debug messages in console (accessible with F12). - - - - - Redirects debug messages into standard output (forces debug mode). - - - - - Redirects debug messages into given file (forces debug mode). - - - - - log file - - - - - Enables Lemon parser debug messages for SQL code assistant. - - - - - Enables debugging of every single SQL query being sent to any database. - - - - - Limits SQL query messages to only the given <database>. - - - - - database - - - - - Enables debugging of SQLiteStudio's query executor. - - - - - Lists plugins installed in the SQLiteStudio and quits. - - - - - Points to the master configuration file. Read manual at wiki page for more details. - - - - - SQLiteStudio settings file - - - - - file - - - - - Database file to open - - - - diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it_IT.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it_IT.ts new file mode 100644 index 0000000..cfbc90e --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_it_IT.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + Interfaccia GUI per SQLiteStudio, un gestore SQLite. + + + + Enables debug messages in console (accessible with F12). + Abilita i messaggi di debug nella console (accessibile con F12). + + + + Redirects debug messages into standard output (forces debug mode). + Reindirizza i messaggi di debug nello standard output (forza la modalità di debug). + + + + Redirects debug messages into given file (forces debug mode). + Reindirizza i messaggi di debug nel file specificato (forza la modalità di debug). + + + + log file + file di log + + + + Enables Lemon parser debug messages for SQL code assistant. + Abilita i messaggi di debug dell'analizzatore Lemon per l'assistente di codice SQL. + + + + Enables debugging of every single SQL query being sent to any database. + Abilita il debug di ogni singola query SQL inviata a qualsiasi database. + + + + Limits SQL query messages to only the given <database>. + Limita i messaggi di query SQL solamente per lo specificato <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Abilita il debug di SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Elenca i plugin installati in SQLiteStudio ed esce. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Punta al file di configurazione principale. Leggi il manuale alla pagina wiki per maggiori dettagli. + + + + SQLiteStudio settings file + File impostazioni SQLiteStudio + + + + file + file + + + + Database file to open + File database da aprire + + + + Select configuration directory + Seleziona la cartella della configurazione + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ja_JP.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ja_JP.ts new file mode 100644 index 0000000..568d724 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ja_JP.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + SQLiteStudioã¸ã® GUI インターフェイスã€SQLiteマãƒãƒ¼ã‚¸ãƒ£ãƒ¼ã€‚ + + + + Enables debug messages in console (accessible with F12). + コンソール(F12ã§ã‚¢ã‚¯ã‚»ã‚¹å¯èƒ½) ã§ãƒ‡ãƒãƒƒã‚°ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’有効ã«ã—ã¾ã™ã€‚ + + + + Redirects debug messages into standard output (forces debug mode). + 標準出力ã«ãƒ‡ãƒãƒƒã‚°ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’リダイレクトã—ã¾ã™(デãƒãƒƒã‚°ãƒ¢ãƒ¼ãƒ‰ã‚’強制ã—ã¾ã™)。 + + + + Redirects debug messages into given file (forces debug mode). + 標準出力ã«ãƒ‡ãƒãƒƒã‚°ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’リダイレクトã—ã¾ã™(デãƒãƒƒã‚°ãƒ¢ãƒ¼ãƒ‰ã‚’強制ã—ã¾ã™)。 + + + + log file + ログファイル + + + + Enables Lemon parser debug messages for SQL code assistant. + SQL補助ã®ãŸã‚レモンパーサーデãƒãƒƒã‚°ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’有効ã«ã—ã¾ã™ã€‚ + + + + Enables debugging of every single SQL query being sent to any database. + ä»»æ„ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«é€ä¿¡ã•れるã™ã¹ã¦ã® SQLクエリã®ãƒ‡ãƒãƒƒã‚°ã‚’有効ã«ã—ã¾ã™ã€‚ + + + + Limits SQL query messages to only the given <database>. + SQLクエリメッセージを与ãˆã‚‰ã‚ŒãŸ <database>ã ã‘ã«åˆ¶é™ã—ã¾ã™ã€‚ + + + + database + データベース + + + + Enables debugging of SQLiteStudio's query executor. + SQLiteStudioã®ã‚¯ã‚¨ãƒªãƒ‡ãƒãƒƒã‚°ã‚’有効ã«ã—ã¾ã™ã€‚ + + + + Lists plugins installed in the SQLiteStudio and quits. + SQLiteStudio ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„るプラグインを一覧表示ã—ã¦çµ‚了ã—ã¾ã™ã€‚ + + + + Points to the master configuration file. Read manual at wiki page for more details. + マスター設定ファイルã®è©³ç´°ã«ã¤ã„ã¦ã¯ã€wikiページã®ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«ã‚’ã”覧ãã ã•ã„。 + + + + SQLiteStudio settings file + SQLiteStudio設定ファイル + + + + file + ファイル + + + + Database file to open + é–‹ãデータベースファイル + + + + Select configuration directory + 設定ファイルã®ä¿å­˜å…ˆã‚’é¸æŠžã—ã¦ãã ã•ã„ + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_kaa.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_kaa.ts new file mode 100644 index 0000000..644c2a3 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_kaa.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ko_KR.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ko_KR.ts new file mode 100644 index 0000000..cfe06df --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ko_KR.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + 로그 íŒŒì¼ + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + ë°ì´í„°ë² ì´ìФ + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + íŒŒì¼ + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_nl_NL.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_nl_NL.ts new file mode 100644 index 0000000..45890c9 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_nl_NL.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_no_NO.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_no_NO.ts new file mode 100644 index 0000000..b2b5af6 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_no_NO.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl.qm b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl.qm deleted file mode 100644 index b444825..0000000 Binary files a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl.ts deleted file mode 100644 index 0add178..0000000 --- a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl.ts +++ /dev/null @@ -1,71 +0,0 @@ - - - - - QObject - - GUI interface to SQLiteStudio, a SQLite manager. - Interfejs graficzny dla SQLiteStudio, menadżera SQLite. - - - Enables debug messages in console (accessible with F12). - Włącza wiadomoÅ›ci debugujÄ…ce w konsoli (dostÄ™pnej przez F12). - - - Redirects debug messages into standard output (forces debug mode). - Przekierowuje wiadomoÅ›ci debugujÄ…ce na standardowe wyjÅ›cie (wymusza tryb debugujÄ…cy). - - - Enables Lemon parser debug messages for SQL code assistant. - Włącza wiadomoÅ›ci debugujÄ…ce analizatora Lemon dla asystenta kodu SQL. - - - Enables debugging of every single SQL query being sent to any database. - WÅ‚acza debugowanie każdego pojedynczego zapytania SQL, wykonywanego na dowolnej bazie danych. - - - Limits SQL query messages to only the given <database>. - Ogranicze wiadomoÅ›ci zapytaÅ„ SQL do podanej <bazy danych>. - - - database - baza danych - - - file - plik - - - Database file to open - Baza danych do otwarcia - - - Error - Błąd - - - Lists plugins installed in the SQLiteStudio and quits. - Wypisuje listÄ™ zainstalowanych w SQLiteStudio wtyczek i wychodzi. - - - Redirects debug messages into given file (forces debug mode). - Przekierowuje wiadomoÅ›ci debugujÄ…ce do danego pliku (wymusza tryb debugujÄ…cy). - - - log file - plik logów - - - Enables debugging of SQLiteStudio's query executor. - Włącza debugowanie wykonawcy zapytaÅ„ w SQLiteStudio. - - - Points to the master configuration file. Read manual at wiki page for more details. - Wskazuje na główny plik konfiguracyjny. WiÄ™cej szczegółów w podrÄ™czniku na stronie wiki. - - - SQLiteStudio settings file - Plik ustawieÅ„ SQLiteStudio - - - diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl_PL.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl_PL.ts new file mode 100644 index 0000000..f9daab8 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pl_PL.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + Interfejs graficzny dla SQLiteStudio, menadżera SQLite. + + + + Enables debug messages in console (accessible with F12). + Włącza wiadomoÅ›ci debugujÄ…ce w konsoli (dostÄ™pnej przez F12). + + + + Redirects debug messages into standard output (forces debug mode). + Przekierowuje wiadomoÅ›ci debugujÄ…ce na standardowe wyjÅ›cie (wymusza tryb debugujÄ…cy). + + + + Redirects debug messages into given file (forces debug mode). + Przekierowuje wiadomoÅ›ci debugujÄ…ce do danego pliku (wymusza tryb debugujÄ…cy). + + + + log file + plik logów + + + + Enables Lemon parser debug messages for SQL code assistant. + Włącza wiadomoÅ›ci debugujÄ…ce analizatora Lemon dla asystenta kodu SQL. + + + + Enables debugging of every single SQL query being sent to any database. + WÅ‚acza debugowanie każdego pojedynczego zapytania SQL, wykonywanego na dowolnej bazie danych. + + + + Limits SQL query messages to only the given <database>. + Ogranicze wiadomoÅ›ci zapytaÅ„ SQL do podanej <bazy danych>. + + + + database + baza danych + + + + Enables debugging of SQLiteStudio's query executor. + Włącza debugowanie wykonawcy zapytaÅ„ w SQLiteStudio. + + + + Lists plugins installed in the SQLiteStudio and quits. + Wypisuje listÄ™ zainstalowanych w SQLiteStudio wtyczek i wychodzi. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Wskazuje na główny plik konfiguracyjny. WiÄ™cej szczegółów w podrÄ™czniku na stronie wiki. + + + + SQLiteStudio settings file + Plik ustawieÅ„ SQLiteStudio + + + + file + plik + + + + Database file to open + Baza danych do otwarcia + + + + Select configuration directory + Wybierz katalog konfiguracyjny + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_BR.qm b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_BR.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_BR.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_BR.ts index 23c60cc..5bdb5f5 100644 --- a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_BR.ts +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_BR.ts @@ -1,82 +1,87 @@ - - + + QObject - - GUI interface to SQLiteStudio, a SQLite manager. - + + GUI interface to SQLiteStudio, a SQLite manager. + Interface para SQLiteStudio, um gerenciador de SQLite. - - Enables debug messages in console (accessible with F12). - + + Enables debug messages in console (accessible with F12). + Habilita mensagens de depuração no console (acessível com F12). - - Redirects debug messages into standard output (forces debug mode). - + + Redirects debug messages into standard output (forces debug mode). + Redireciona as mensagens de depuração na saída padrão (força o modo de depuração). - - Redirects debug messages into given file (forces debug mode). - + + Redirects debug messages into given file (forces debug mode). + Redireciona as mensagens de depuração em determinado arquivo (força o modo de depuração). - - log file - + + log file + arquivo de log - - Enables Lemon parser debug messages for SQL code assistant. - + + Enables Lemon parser debug messages for SQL code assistant. + Habilita Lemon para mensagens de debug para o assistente de código SQL. - - Enables debugging of every single SQL query being sent to any database. - + + Enables debugging of every single SQL query being sent to any database. + Permite a depuração de cada consulta SQL sendo enviada para qualquer banco de dados. - - Limits SQL query messages to only the given <database>. - + + Limits SQL query messages to only the given <database>. + Limita as mensagens de consulta SQL apenas para <database>. - - database - + + database + Banco de dados - - Enables debugging of SQLiteStudio's query executor. - + + Enables debugging of SQLiteStudio's query executor. + Habilita a depuração de uma consulta SQLiteStudio. - - Lists plugins installed in the SQLiteStudio and quits. - + + Lists plugins installed in the SQLiteStudio and quits. + Lista os plugins instalados no SQLiteStudio e encerrados. - - Points to the master configuration file. Read manual at wiki page for more details. - + + Points to the master configuration file. Read manual at wiki page for more details. + Ãtens do arquivo de configuração principal. Leia o manual na página wiki para mais detalhes. - - SQLiteStudio settings file - + + SQLiteStudio settings file + Arquivo de configurações SQLiteStudio - - file - + + file + arquivo - - Database file to open - + + Database file to open + Arquivo do banco de dados para abrir - + + + Select configuration directory + Selecionar diretório de configuração + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_PT.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_PT.ts new file mode 100644 index 0000000..4aa1362 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_pt_PT.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ro_RO.qm b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ro_RO.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ro_RO.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ro_RO.ts index b4b3802..188fab7 100644 --- a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ro_RO.ts +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ro_RO.ts @@ -1,82 +1,87 @@ - - + + QObject - - GUI interface to SQLiteStudio, a SQLite manager. - + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. - - Enables debug messages in console (accessible with F12). - + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). - - Redirects debug messages into standard output (forces debug mode). - + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). - - Redirects debug messages into given file (forces debug mode). - + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). - - log file - + + log file + log file - - Enables Lemon parser debug messages for SQL code assistant. - + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. - - Enables debugging of every single SQL query being sent to any database. - + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. - - Limits SQL query messages to only the given <database>. - + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. - - database - + + database + database - - Enables debugging of SQLiteStudio's query executor. - + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. - - Lists plugins installed in the SQLiteStudio and quits. - + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. - - Points to the master configuration file. Read manual at wiki page for more details. - + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. - - SQLiteStudio settings file - + + SQLiteStudio settings file + SQLiteStudio settings file - - file - + + file + file - - Database file to open - + + Database file to open + Database file to open - + + + Select configuration directory + Select configuration directory + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru.qm b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru.qm deleted file mode 100644 index 5541fe4..0000000 Binary files a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru.ts deleted file mode 100644 index f9d2eae..0000000 --- a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru.ts +++ /dev/null @@ -1,86 +0,0 @@ - - - - - QObject - - - GUI interface to SQLiteStudio, a SQLite manager. - ГрафичеÑкий Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð´Ð»Ñ SQLiteStudio, менеджера баз данных SQLite. - - - - Enables debug messages in console (accessible with F12). - Включает вывод отладочных Ñообщений в конÑоль (доÑтупную по нажатию F12). - - - - Redirects debug messages into standard output (forces debug mode). - ПеренаправлÑет отладочные ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð² Ñтандартный поток (принудительный отладочный режим). - - - - Redirects debug messages into given file (forces debug mode). - ПеренаправлÑет отладочные ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð² указанный файл (принудительный отладочный режим). - - - - log file - файл журнала - - - - Enables Lemon parser debug messages for SQL code assistant. - Включает вывод отладочных Ñообщений анализатора Lemon Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ SQL кода. - - - - Enables debugging of every single SQL query being sent to any database. - Включает отладку каждого запроÑа SQL, поÑылаемого к любой базе данных. - - - - Limits SQL query messages to only the given <database>. - Ограничивает ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов SQL только Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð¹ <базы данных>. - - - - database - база данных - - - - Enables debugging of SQLiteStudio's query executor. - Включает отладку обработчика запроÑов SQLiteStudio. - - - - Lists plugins installed in the SQLiteStudio and quits. - Выводит ÑпиÑок уÑтановленных в SQLiteStudio модулей и оÑущеÑтвлÑет выход. - - - - Points to the master configuration file. Read manual at wiki page for more details. - Указывает оÑновной файл конфигурации. Ð”ÐµÑ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ ÑодержитÑÑ Ð² инÑтрукции на wiki-Ñтранице. - - - - SQLiteStudio settings file - Файл наÑтроек SQLiteStudio - - - - file - файл - - - - Database file to open - Файл базы данных Ð´Ð»Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð¸Ñ - - - Error - Ошибка - - - diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru_RU.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru_RU.ts new file mode 100644 index 0000000..2bf3cff --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_ru_RU.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + ГрафичеÑкий Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð´Ð»Ñ SQLiteStudio, менеджера баз данных SQLite. + + + + Enables debug messages in console (accessible with F12). + Включает вывод отладочных Ñообщений в конÑоль (доÑтупную по нажатию F12). + + + + Redirects debug messages into standard output (forces debug mode). + ПеренаправлÑет отладочные ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð² Ñтандартный поток (принудительный отладочный режим). + + + + Redirects debug messages into given file (forces debug mode). + ПеренаправлÑет отладочные ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð² указанный файл (принудительный отладочный режим). + + + + log file + файл журнала + + + + Enables Lemon parser debug messages for SQL code assistant. + Включает вывод отладочных Ñообщений анализатора Lemon Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ SQL кода. + + + + Enables debugging of every single SQL query being sent to any database. + Включает отладку каждого запроÑа SQL, поÑылаемого к любой базе данных. + + + + Limits SQL query messages to only the given <database>. + Ограничивает ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов SQL только Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð¹ <базы данных>. + + + + database + база данных + + + + Enables debugging of SQLiteStudio's query executor. + Включает отладку обработчика запроÑов SQLiteStudio. + + + + Lists plugins installed in the SQLiteStudio and quits. + Выводит ÑпиÑок уÑтановленных в SQLiteStudio модулей и оÑущеÑтвлÑет выход. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Указывает оÑновной файл конфигурации. Ð”ÐµÑ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ ÑодержитÑÑ Ð² инÑтрукции на wiki-Ñтранице. + + + + SQLiteStudio settings file + Файл наÑтроек SQLiteStudio + + + + file + файл + + + + Database file to open + Файл базы данных Ð´Ð»Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð¸Ñ + + + + Select configuration directory + Выберите каталог конфигурации + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk.qm b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk.qm deleted file mode 100644 index 01f60b5..0000000 Binary files a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk.ts deleted file mode 100644 index d1ec165..0000000 --- a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk.ts +++ /dev/null @@ -1,86 +0,0 @@ - - - - - QObject - - - GUI interface to SQLiteStudio, a SQLite manager. - GUI rozhranie pre SQLiteStudio, SQLite manažér. - - - - Enables debug messages in console (accessible with F12). - Aktivuje ladiace správy v konzole (dostupné pomocou F12). - - - - Redirects debug messages into standard output (forces debug mode). - PresmerovaÅ¥ ladiace informácie na Å¡tandardný výstup (vynútený ladiaci mód). - - - - Redirects debug messages into given file (forces debug mode). - - - - - log file - - - - - Enables Lemon parser debug messages for SQL code assistant. - - - - - Enables debugging of every single SQL query being sent to any database. - - - - - Limits SQL query messages to only the given <database>. - - - - - database - databáza - - - - Enables debugging of SQLiteStudio's query executor. - - - - - Lists plugins installed in the SQLiteStudio and quits. - - - - - Points to the master configuration file. Read manual at wiki page for more details. - - - - - SQLiteStudio settings file - - - - - file - Súbor - - - - Database file to open - - - - Error - Chyba - - - diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk_SK.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk_SK.ts new file mode 100644 index 0000000..51babe5 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sk_SK.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI rozhranie pre SQLiteStudio, SQLite manažér. + + + + Enables debug messages in console (accessible with F12). + Aktivuje ladiace správy v konzole (dostupné pomocou F12). + + + + Redirects debug messages into standard output (forces debug mode). + PresmerovaÅ¥ ladiace informácie na Å¡tandardný výstup (vynútený ladiaci mód). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + databáza + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + Súbor + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sr_SP.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sr_SP.ts new file mode 100644 index 0000000..97ac651 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sr_SP.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sv_SE.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sv_SE.ts new file mode 100644 index 0000000..c8ed7a3 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_sv_SE.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_tr_TR.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_tr_TR.ts new file mode 100644 index 0000000..88da4ce --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_tr_TR.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + SQLite yöneticisi, SQLiteStudio için kullanıcı arayüzü + + + + Enables debug messages in console (accessible with F12). + Konsolda debug mesajlarını aktif hale getirir (F12 ile de ulaşılabilir). + + + + Redirects debug messages into standard output (forces debug mode). + Debug mesajlarını standart konsola yönlendirir (debug modunu zorunludur). + + + + Redirects debug messages into given file (forces debug mode). + Debug mesajlarını belirtilen dosyaya yönlendirir (debug modunu zorunludur). + + + + log file + log dosyası + + + + Enables Lemon parser debug messages for SQL code assistant. + SQL kod asistanı olarak mesajları debug etmek için Lemon parser'ı aktif hale getirir. + + + + Enables debugging of every single SQL query being sent to any database. + Veritabanına gönderilen her SQL sorgusu için debug yapabilmeyi aktif hale getirir. + + + + Limits SQL query messages to only the given <database>. + Sadece <veritabanı> için verilen SQL sorgu mesajlarını sınırlar. + + + + database + veritabanı + + + + Enables debugging of SQLiteStudio's query executor. + SQLiteStudio'nun sorgu çalıştırıcısının debugging özelliÄŸini aktif hale getirir. + + + + Lists plugins installed in the SQLiteStudio and quits. + SQLiteStudio içinde yüklü olan eklentileri listeler ve çıkış yapar. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Ana konfigürasyon dosyasını gösterir. Daha fazla detay için wiki sayfasındaki kullanım kılavuzunu okuyun. + + + + SQLiteStudio settings file + SQLiteStudio ayarlar dosyası + + + + file + dosya + + + + Database file to open + Açılacak veritabanı dosyası + + + + Select configuration directory + Yapılandırma dizinini seç + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_uk_UA.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_uk_UA.ts new file mode 100644 index 0000000..be290a1 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_uk_UA.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + Графічний Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð´Ð»Ñ SQLiteStudio, менеджера баз даних SQLite. + + + + Enables debug messages in console (accessible with F12). + Включає вивід налагоджувальних повідомлень в конÑоль (доÑтупну піÑÐ»Ñ Ð½Ð°Ñ‚Ð¸ÑÐºÐ°Ð½Ð½Ñ F12). + + + + Redirects debug messages into standard output (forces debug mode). + ПеренаправлÑÑ” налагоджувальні Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð² Ñтандартний потік (примуÑовий режим налагодженнÑ). + + + + Redirects debug messages into given file (forces debug mode). + ПеренаправлÑÑ” налагоджувальні Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð² вказаний файл (примуÑовий режим налагодженнÑ). + + + + log file + файл журналу + + + + Enables Lemon parser debug messages for SQL code assistant. + Включає вивід налагоджувальних повідомлень аналізатора Lemon Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ SQL коду. + + + + Enables debugging of every single SQL query being sent to any database. + Включає Ð½Ð°Ð»Ð°Ð³Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ запиту SQL, що поÑилаєтьÑÑ Ð´Ð¾ будь-Ñкої базі даних. + + + + Limits SQL query messages to only the given <database>. + Обмежує Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² SQL тільки Ð´Ð»Ñ Ð·Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾Ñ— <бази даних>. + + + + database + база даних + + + + Enables debugging of SQLiteStudio's query executor. + Включає Ð½Ð°Ð»Ð°Ð³Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ð¾Ð±Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ° запитів SQLiteStudio. + + + + Lists plugins installed in the SQLiteStudio and quits. + Виводить ÑпиÑок вÑтановлених в SQLiteStudio модулів Ñ– здійÑнює вихід. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Вказує оÑновний файл конфігурації. Детальна Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¼Ñ–ÑтитьÑÑ Ð² інÑтрукції на wiki-Ñторінці. + + + + SQLiteStudio settings file + Файл налаштувань SQLiteStudio + + + + file + файл + + + + Database file to open + Файл бази даних Ð´Ð»Ñ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ + + + + Select configuration directory + Виберіть директорію конфігурації + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_vi_VN.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_vi_VN.ts new file mode 100644 index 0000000..6087d60 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_vi_VN.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + GUI interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages in console (accessible with F12). + Enables debug messages in console (accessible with F12). + + + + Redirects debug messages into standard output (forces debug mode). + Redirects debug messages into standard output (forces debug mode). + + + + Redirects debug messages into given file (forces debug mode). + Redirects debug messages into given file (forces debug mode). + + + + log file + log file + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Enables debugging of every single SQL query being sent to any database. + Enables debugging of every single SQL query being sent to any database. + + + + Limits SQL query messages to only the given <database>. + Limits SQL query messages to only the given <database>. + + + + database + database + + + + Enables debugging of SQLiteStudio's query executor. + Enables debugging of SQLiteStudio's query executor. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Points to the master configuration file. Read manual at wiki page for more details. + Points to the master configuration file. Read manual at wiki page for more details. + + + + SQLiteStudio settings file + SQLiteStudio settings file + + + + file + file + + + + Database file to open + Database file to open + + + + Select configuration directory + Select configuration directory + + + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_CN.qm b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_CN.qm deleted file mode 100644 index 83024d2..0000000 Binary files a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_CN.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_CN.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_CN.ts index 14e3971..6091288 100644 --- a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_CN.ts +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_CN.ts @@ -1,86 +1,87 @@ - - + + QObject - - GUI interface to SQLiteStudio, a SQLite manager. - + + GUI interface to SQLiteStudio, a SQLite manager. + SQLiteStudio 图形界é¢ï¼Œä¸€æ¬¾ SQLite 管ç†è½¯ä»¶ã€‚ - - Enables debug messages in console (accessible with F12). - 在控制å°ä¸­å¯ç”¨è°ƒè¯•ä¿¡æ¯ï¼ˆå¯é€šè¿‡ F12 访问)。 + + Enables debug messages in console (accessible with F12). + å¯ç”¨æŽ§åˆ¶å°ä¸­çš„调试信æ¯ï¼ˆé€šè¿‡ F12 访问)。 - - Redirects debug messages into standard output (forces debug mode). - é‡å®šå‘调试信æ¯åˆ°æ ‡å‡†è¾“出中(强制调试模å¼ï¼‰ã€‚ + + Redirects debug messages into standard output (forces debug mode). + é‡å®šå‘调试信æ¯åˆ°æ ‡å‡†è¾“出(强制调试模å¼ï¼‰ã€‚ - - Redirects debug messages into given file (forces debug mode). - é‡å®šå‘调试信æ¯åˆ°æŒ‡å®šæ–‡ä»¶ï¼ˆå¼ºåˆ¶è°ƒè¯•模å¼ï¼‰ã€‚ + + Redirects debug messages into given file (forces debug mode). + é‡å®šå‘调试信æ¯åˆ°æŒ‡å®šæ–‡ä»¶ï¼ˆå¼ºåˆ¶è°ƒè¯•模å¼ï¼‰ã€‚ - - log file - 日志文件 + + log file + 日志文件 - - Enables Lemon parser debug messages for SQL code assistant. - + + Enables Lemon parser debug messages for SQL code assistant. + å¯ç”¨ SQL 代ç åŠ©ç†çš„ Lemon è§£æžå™¨è°ƒè¯•ä¿¡æ¯ã€‚ - - Enables debugging of every single SQL query being sent to any database. - + + Enables debugging of every single SQL query being sent to any database. + å¯ç”¨å‘é€åˆ°ä»»ä½•æ•°æ®åº“çš„æ¯æ¡ SQL 查询的调试。 - - Limits SQL query messages to only the given <database>. - + + Limits SQL query messages to only the given <database>. + å°† SQL 查询消æ¯é™åˆ¶ä¸ºæŒ‡å®šçš„ <æ•°æ®åº“>。 - - database - æ•°æ®åº“ + + database + æ•°æ®åº“ - - Enables debugging of SQLiteStudio's query executor. - + + Enables debugging of SQLiteStudio's query executor. + å¯ç”¨ SQLiteStudio 的查询执行器的调试。 - - Lists plugins installed in the SQLiteStudio and quits. - 列出已安装的æ’件并退出。 + + Lists plugins installed in the SQLiteStudio and quits. + 列出 SQLiteStudio 中已安装的æ’件并退出。 - - Points to the master configuration file. Read manual at wiki page for more details. - 指å‘主é…置文件。 阅读 Wiki 页é¢ä¸Šçš„æ‰‹å†Œä»¥èŽ·å–æ›´å¤šè¯¦ç»†ä¿¡æ¯ã€‚ + + Points to the master configuration file. Read manual at wiki page for more details. + 指å‘主é…ç½®æ–‡ä»¶ã€‚æ›´å¤šç»†èŠ‚è§ Wiki 页é¢ä¸Šçš„æ‰‹å†Œã€‚ - - SQLiteStudio settings file - SQLiteStudio é…置文件 + + SQLiteStudio settings file + SQLiteStudio 设置文件 - - file - 文件 + + file + 文件 - - Database file to open - è¦æ‰“开的数æ®åº“文件 + + Database file to open + è¦æ‰“开的数æ®åº“文件 - Error - 错误 + + Select configuration directory + 选择é…置目录 - + diff --git a/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_TW.ts b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_TW.ts new file mode 100644 index 0000000..333e236 --- /dev/null +++ b/SQLiteStudio3/sqlitestudio/translations/sqlitestudio_zh_TW.ts @@ -0,0 +1,87 @@ + + + + + QObject + + + GUI interface to SQLiteStudio, a SQLite manager. + SQLiteStudio 圖形介é¢ï¼Œä¸€æ¬¾ SQLite 管ç†è»Ÿé«”。 + + + + Enables debug messages in console (accessible with F12). + 啟用控制檯中的除錯資訊 (é€éŽ F12 訪å•)。 + + + + Redirects debug messages into standard output (forces debug mode). + é‡å®šå‘除錯資訊到標準輸出 (強制除錯模å¼)。 + + + + Redirects debug messages into given file (forces debug mode). + é‡å®šå‘除錯資訊到指定檔案 (強制除錯模å¼)。 + + + + log file + 日誌檔案 + + + + Enables Lemon parser debug messages for SQL code assistant. + 啟用 SQL 程å¼ç¢¼åŠ©ç†çš„ Lemon è§£æžå™¨é™¤éŒ¯è³‡è¨Šã€‚ + + + + Enables debugging of every single SQL query being sent to any database. + 啟用傳é€åˆ°ä»»ä½•è³‡æ–™åº«çš„æ¯æ¢ SQL 查詢的除錯。 + + + + Limits SQL query messages to only the given <database>. + å°‡ SQL 查詢訊æ¯é™åˆ¶ç‚ºæŒ‡å®šçš„<資料庫>。 + + + + database + 資料庫 + + + + Enables debugging of SQLiteStudio's query executor. + 啟用 SQLiteStudio 的查詢執行器的除錯。 + + + + Lists plugins installed in the SQLiteStudio and quits. + 列出 SQLiteStudio 中已安è£çš„外掛並退出。 + + + + Points to the master configuration file. Read manual at wiki page for more details. + 指å‘主設定檔檔案。更多細節見 Wiki é é¢ä¸Šçš„æ‰‹å†Šã€‚ + + + + SQLiteStudio settings file + SQLiteStudio 設定檔案 + + + + file + 檔案 + + + + Database file to open + è¦é–‹å•Ÿçš„資料庫檔案 + + + + Select configuration directory + 鏿“‡è¨­å®šæª”目錄 + + + diff --git a/SQLiteStudio3/sqlitestudiocli/cli.cpp b/SQLiteStudio3/sqlitestudiocli/cli.cpp index 30bf175..6251745 100644 --- a/SQLiteStudio3/sqlitestudiocli/cli.cpp +++ b/SQLiteStudio3/sqlitestudiocli/cli.cpp @@ -120,6 +120,7 @@ Db* CLI::getCurrentDb() const void CLI::exit() { + SQLITESTUDIO->cleanUp(); doExit = true; } @@ -245,23 +246,25 @@ void CLI::applyHistoryLimit() #endif } -void CLI::openDbFile(const QString& path) +bool CLI::openDbFile(const QString& path) { Db* db = DBLIST->getByPath(path); if (db) { println(tr("Database passed in command line parameters (%1) was already on the list under name: %2").arg(path, db->getName())); - return; + setCurrentDb(db); + return true; } QString name = DBLIST->quickAddDb(path, QHash()); if (name.isNull()) { println(tr("Could not add database %1 to list.").arg(path)); - return; + return false; } db = DBLIST->getByName(name); setCurrentDb(db); + return true; } void CLI::doWork() diff --git a/SQLiteStudio3/sqlitestudiocli/cli.h b/SQLiteStudio3/sqlitestudiocli/cli.h index 12f391b..5ee363f 100644 --- a/SQLiteStudio3/sqlitestudiocli/cli.h +++ b/SQLiteStudio3/sqlitestudiocli/cli.h @@ -59,7 +59,7 @@ class CLI : public QObject void done(); void executionComplete(); void clearHistory(); - void openDbFile(const QString& path); + bool openDbFile(const QString& path); signals: void execCommand(CliCommand* cmd); diff --git a/SQLiteStudio3/sqlitestudiocli/commands/clicommand.cpp b/SQLiteStudio3/sqlitestudiocli/commands/clicommand.cpp index e23a042..ee201e0 100644 --- a/SQLiteStudio3/sqlitestudiocli/commands/clicommand.cpp +++ b/SQLiteStudio3/sqlitestudiocli/commands/clicommand.cpp @@ -104,7 +104,7 @@ void CliCommand::printBox(const QString& str) void CliCommand::printUsage() { - println(tr("Usage: %1%2").arg(CFG_CLI.Console.CommandPrefixChar.get()).arg(usage())); + println(tr("Usage: %1%2").arg(CFG_CLI.Console.CommandPrefixChar.get(), usage())); println(""); } diff --git a/SQLiteStudio3/sqlitestudiocli/commands/clicommandcd.cpp b/SQLiteStudio3/sqlitestudiocli/commands/clicommandcd.cpp index 14e91d5..80de0fa 100644 --- a/SQLiteStudio3/sqlitestudiocli/commands/clicommandcd.cpp +++ b/SQLiteStudio3/sqlitestudiocli/commands/clicommandcd.cpp @@ -24,7 +24,7 @@ QString CliCommandCd::fullHelp() const "It requires a argument to be passed, therefore calling %1 will always cause a change of the directory. " "To learn what's the current working directory use %2 command and to list contents of the current working directory " "use %3 command." - ); + ).arg(cmdName("cd"), cmdName("pwd"), cmdName("ls")); } void CliCommandCd::defineSyntax() diff --git a/SQLiteStudio3/sqlitestudiocli/commands/clicommandclose.cpp b/SQLiteStudio3/sqlitestudiocli/commands/clicommandclose.cpp index 44fc72c..c2bb803 100644 --- a/SQLiteStudio3/sqlitestudiocli/commands/clicommandclose.cpp +++ b/SQLiteStudio3/sqlitestudiocli/commands/clicommandclose.cpp @@ -8,7 +8,7 @@ void CliCommandClose::execute() if (!syntax.isArgumentSet(DB_NAME) && !cli->getCurrentDb()) { println(tr("Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3.") - .arg(cmdName("close")).arg(cmdName("use")).arg(cmdName("close"))); + .arg(cmdName("close"), cmdName("use"), cmdName("close"))); return; } @@ -21,7 +21,7 @@ void CliCommandClose::execute() println(tr("Connection to database %1 closed.").arg(db->getName())); } else - println(tr("No such database: %1. Use %2 to see list of known databases.").arg(syntax.getArgument(DB_NAME)).arg(cmdName("dblist"))); + println(tr("No such database: %1. Use %2 to see list of known databases.").arg(syntax.getArgument(DB_NAME), cmdName("dblist"))); } else if (cli->getCurrentDb()) { @@ -41,7 +41,7 @@ QString CliCommandClose::fullHelp() const "Closes database connection. If the database was already closed, nothing happens. " "If is provided, it should be name of the database to close (as printed by %1 command). " "The the is not provided, then current working database is closed (see help for %2 for details)." - ).arg(cmdName("dblist")).arg(cmdName("use")); + ).arg(cmdName("dblist"), cmdName("use")); } void CliCommandClose::defineSyntax() diff --git a/SQLiteStudio3/sqlitestudiocli/commands/clicommanddblist.cpp b/SQLiteStudio3/sqlitestudiocli/commands/clicommanddblist.cpp index 271a44f..6e1f7d7 100644 --- a/SQLiteStudio3/sqlitestudiocli/commands/clicommanddblist.cpp +++ b/SQLiteStudio3/sqlitestudiocli/commands/clicommanddblist.cpp @@ -49,7 +49,7 @@ void CliCommandDbList::execute() for (Db* db : dbList) { bool open = db->isOpen(); - path = db->getPath(); + path = QDir::toNativeSeparators(db->getPath()); name = db->getName(); if (name == currentName) name.prepend("*"); diff --git a/SQLiteStudio3/sqlitestudiocli/commands/clicommanddesc.cpp b/SQLiteStudio3/sqlitestudiocli/commands/clicommanddesc.cpp index f32b75e..42bd5ce 100644 --- a/SQLiteStudio3/sqlitestudiocli/commands/clicommanddesc.cpp +++ b/SQLiteStudio3/sqlitestudiocli/commands/clicommanddesc.cpp @@ -15,7 +15,7 @@ void CliCommandDesc::execute() println(tr("No working database is set.\n" "Call %1 command to set working database.\n" "Call %2 to see list of all databases.") - .arg(cmdName("use")).arg(cmdName("dblist"))); + .arg(cmdName("use"), cmdName("dblist"))); return; } @@ -54,7 +54,7 @@ QString CliCommandDesc::shortHelp() const QString CliCommandDesc::fullHelp() const { - return QString(); + return shortHelp(); } void CliCommandDesc::defineSyntax() @@ -84,7 +84,7 @@ void CliCommandDesc::printTable(SqliteCreateTable *table) // Rows QString constrJoinStr = "\n" + pad("", 20, ' ') + "|" + pad("", 10, ' ') + "|"; QStringList constrList; - for (SqliteCreateTable::Column* column : table->columns) + for (SqliteCreateTable::Column*& column : table->columns) { msg = pad(column->name.left(20), 20, ' '); msg += "|"; diff --git a/SQLiteStudio3/sqlitestudiocli/commands/clicommandhelp.cpp b/SQLiteStudio3/sqlitestudiocli/commands/clicommandhelp.cpp index 5f92fd4..bb3f9a8 100644 --- a/SQLiteStudio3/sqlitestudiocli/commands/clicommandhelp.cpp +++ b/SQLiteStudio3/sqlitestudiocli/commands/clicommandhelp.cpp @@ -24,7 +24,7 @@ QString CliCommandHelp::fullHelp() const "When passing name, you can skip special prefix character ('%3').\n\n" "You can always execute any command with exactly single '--help' option to see help for that command. " "It's an alternative for typing: %1 ." - ).arg(cmdName("help")).arg(cmdName("help")).arg(CFG_CLI.Console.CommandPrefixChar.get()).arg(cmdName("help")); + ).arg(cmdName("help"), cmdName("help"), CFG_CLI.Console.CommandPrefixChar.get(), cmdName("help")); } void CliCommandHelp::defineSyntax() @@ -49,7 +49,7 @@ void CliCommandHelp::printHelp(const QString& cmd) QString prefix = CFG_CLI.Console.CommandPrefixChar.get(); QString msg; - msg += tr("Usage: %1%2").arg(prefix).arg(command->usage(cmdStr)); + msg += tr("Usage: %1%2").arg(prefix, command->usage(cmdStr)); msg += "\n"; if (aliases.size() > 0) { @@ -83,4 +83,5 @@ void CliCommandHelp::printHelp() delete allCommands[cmd]; } printBox(msgList.join("\n")); + printHelp("help"); } diff --git a/SQLiteStudio3/sqlitestudiocli/commands/clicommandmode.cpp b/SQLiteStudio3/sqlitestudiocli/commands/clicommandmode.cpp index b258045..b0419d5 100644 --- a/SQLiteStudio3/sqlitestudiocli/commands/clicommandmode.cpp +++ b/SQLiteStudio3/sqlitestudiocli/commands/clicommandmode.cpp @@ -49,7 +49,7 @@ QString CliCommandMode::fullHelp() const "The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, " "while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be " "cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window.\n" - "ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous " + "ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous " "to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory.\n" "\n" "The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode " diff --git a/SQLiteStudio3/sqlitestudiocli/commands/clicommandopen.cpp b/SQLiteStudio3/sqlitestudiocli/commands/clicommandopen.cpp index fef1737..a7eb004 100644 --- a/SQLiteStudio3/sqlitestudiocli/commands/clicommandopen.cpp +++ b/SQLiteStudio3/sqlitestudiocli/commands/clicommandopen.cpp @@ -10,7 +10,7 @@ void CliCommandOpen::execute() if (!syntax.isArgumentSet(DB_NAME_OR_FILE) && !cli->getCurrentDb()) { println(tr("Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3.") - .arg(cmdName("open")).arg(cmdName("use")).arg(cmdName("open"))); + .arg(cmdName("open"), cmdName("use"), cmdName("open"))); return; } @@ -36,7 +36,7 @@ void CliCommandOpen::execute() { println(tr("File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. " "To create a new database, use %4 command.").arg(arg).arg(QDir::currentPath()) - .arg(cmdName("open")).arg(cmdName("add"))); + .arg(cmdName("open"), cmdName("add"))); return; } } diff --git a/SQLiteStudio3/sqlitestudiocli/commands/clicommandpwd.cpp b/SQLiteStudio3/sqlitestudiocli/commands/clicommandpwd.cpp index f96cae4..6646196 100644 --- a/SQLiteStudio3/sqlitestudiocli/commands/clicommandpwd.cpp +++ b/SQLiteStudio3/sqlitestudiocli/commands/clicommandpwd.cpp @@ -19,7 +19,7 @@ QString CliCommandPwd::fullHelp() const "This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. " "It prints current working directory. You can change the current working directory with %1 command " "and you can also list contents of the current working directory with %2 command." - ).arg(cmdName("cd")).arg(cmdName("dir")); + ).arg(cmdName("cd"), cmdName("dir")); } void CliCommandPwd::defineSyntax() diff --git a/SQLiteStudio3/sqlitestudiocli/commands/clicommandsql.cpp b/SQLiteStudio3/sqlitestudiocli/commands/clicommandsql.cpp index cb89dfe..e24dbb2 100644 --- a/SQLiteStudio3/sqlitestudiocli/commands/clicommandsql.cpp +++ b/SQLiteStudio3/sqlitestudiocli/commands/clicommandsql.cpp @@ -1,8 +1,8 @@ #include "clicommandsql.h" #include "cli.h" -#include "parser/ast/sqliteselect.h" -#include "parser/parser.h" -#include "parser/parsererror.h" +//#include "parser/ast/sqliteselect.h" +//#include "parser/parser.h" +//#include "parser/parsererror.h" #include "db/queryexecutor.h" #include "qio.h" #include "common/unused.h" @@ -19,7 +19,7 @@ void CliCommandSql::execute() println(tr("No working database is set.\n" "Call %1 command to set working database.\n" "Call %2 to see list of all databases.") - .arg(cmdName("use")).arg(cmdName("dblist"))); + .arg(cmdName("use"), cmdName("dblist"))); return; } @@ -92,7 +92,7 @@ void CliCommandSql::printResultsClassic(QueryExecutor* executor, SqlQueryPtr res int resultColumnCount = executor->getResultColumns().size(); // Columns - for (const QueryExecutor::ResultColumnPtr& resCol : executor->getResultColumns()) + for (QueryExecutor::ResultColumnPtr& resCol : executor->getResultColumns()) qOut << resCol->displayName << "|"; qOut << "\n"; @@ -106,7 +106,7 @@ void CliCommandSql::printResultsClassic(QueryExecutor* executor, SqlQueryPtr res row = results->next(); i = 0; values = row->valueList().mid(0, resultColumnCount); - for (QVariant value : values) + for (QVariant& value : values) { qOut << getValueString(value); if ((i + 1) < resultColumnCount) @@ -149,7 +149,7 @@ void CliCommandSql::printResultsFixed(QueryExecutor* executor, SqlQueryPtr resul // Columns QStringList columns; - for (const QueryExecutor::ResultColumnPtr& resCol : executor->getResultColumns()) + for (QueryExecutor::ResultColumnPtr& resCol : executor->getResultColumns()) columns << resCol->displayName; printColumnHeader(widths, columns); @@ -184,7 +184,7 @@ void CliCommandSql::printResultsColumns(QueryExecutor* executor, SqlQueryPtr res // Get widths of each column in every data row, remember the longest ones QList columnWidths; SortedColumnWidth* colWidth = nullptr; - for (const QueryExecutor::ResultColumnPtr& resCol : resultColumns) + for (QueryExecutor::ResultColumnPtr& resCol : resultColumns) { colWidth = new SortedColumnWidth(); colWidth->setHeaderWidth(resCol->displayName.length()); @@ -204,7 +204,7 @@ void CliCommandSql::printResultsColumns(QueryExecutor* executor, SqlQueryPtr res // Calculate width as it would be required to display entire rows int totalWidth = 0; - for (SortedColumnWidth* colWd : columnWidths) + for (SortedColumnWidth*& colWd : columnWidths) totalWidth += colWd->getWidth(); totalWidth += (resultColumnsCount - 1); // column separators @@ -224,12 +224,12 @@ void CliCommandSql::printResultsColumns(QueryExecutor* executor, SqlQueryPtr res // Printing QList finalWidths; - for (SortedColumnWidth* colWd : columnWidths) + for (SortedColumnWidth*& colWd : columnWidths) finalWidths << colWd->getWidth(); printColumnHeader(finalWidths, headerNames); - for (SqlResultsRowPtr row : allRows) + for (SqlResultsRowPtr& row : allRows) printColumnDataRow(finalWidths, row, resultColumnsCount); qOut.flush(); @@ -240,14 +240,14 @@ void CliCommandSql::printResultsRowByRow(QueryExecutor* executor, SqlQueryPtr re // Columns int resultColumnCount = executor->getResultColumns().size(); int colWidth = 0; - for (const QueryExecutor::ResultColumnPtr& resCol : executor->getResultColumns()) + for (QueryExecutor::ResultColumnPtr& resCol : executor->getResultColumns()) { if (resCol->displayName.length() > colWidth) colWidth = resCol->displayName.length(); } QStringList columns; - for (const QueryExecutor::ResultColumnPtr& resCol : executor->getResultColumns()) + for (QueryExecutor::ResultColumnPtr& resCol : executor->getResultColumns()) columns << pad(resCol->displayName, -colWidth, ' '); // Data @@ -263,7 +263,7 @@ void CliCommandSql::printResultsRowByRow(QueryExecutor* executor, SqlQueryPtr re i = 0; rowCntString = " " + rowCntTemplate.arg(rowCnt) + " "; qOut << center(rowCntString, termWidth - 1, '-') << "\n"; - for (QVariant value : row->valueList().mid(0, resultColumnCount)) + for (QVariant& value : row->valueList().mid(0, resultColumnCount)) { qOut << columns[i] + ": " + getValueString(value) << "\n"; i++; @@ -294,7 +294,7 @@ void CliCommandSql::shrinkColumns(QList& colu sSort(columnWidths); // See if we can shrink headers only, or we already need to shrink the data - for (SortedColumnWidth* colWidth : columnWidths) + for (SortedColumnWidth*& colWidth : columnWidths) { if (colWidth->isHeaderLonger()) { @@ -381,7 +381,7 @@ void CliCommandSql::printColumnDataRow(const QList& widths, const SqlResult { int i = 0; QStringList line; - for (const QVariant& value : row->valueList().mid(0, resultColumnCount)) + for (QVariant& value : row->valueList().mid(0, resultColumnCount)) { line << pad(getValueString(value).left(widths[i]), widths[i], ' '); i++; diff --git a/SQLiteStudio3/sqlitestudiocli/main.cpp b/SQLiteStudio3/sqlitestudiocli/main.cpp index 3ffc2e3..14867cf 100644 --- a/SQLiteStudio3/sqlitestudiocli/main.cpp +++ b/SQLiteStudio3/sqlitestudiocli/main.cpp @@ -1,22 +1,28 @@ #include "cli.h" #include "clicommandexecutor.h" -#include "sqlitestudio.h" #include "commands/clicommand.h" #include "cli_config.h" #include "cliutils.h" #include "qio.h" #include "climsghandler.h" #include "completionhelper.h" -#include "services/updatemanager.h" #include "services/pluginmanager.h" +#include "sqlfileexecutor.h" #include #include #include #include -bool listPlugins = false; +namespace CliOpts +{ + bool listPlugins = false; + QString sqlScriptToExecute; + QString dbToOpen; + QString sqlScriptCodec; + bool ignoreErrors = false; +} -QString cliHandleCmdLineArgs() +bool cliHandleCmdLineArgs() { QCommandLineParser parser; parser.setApplicationDescription(QObject::tr("Command line interface to SQLiteStudio, a SQLite manager.")); @@ -25,10 +31,28 @@ QString cliHandleCmdLineArgs() QCommandLineOption debugOption({"d", "debug"}, QObject::tr("Enables debug messages on standard error output.")); QCommandLineOption lemonDebugOption("debug-lemon", QObject::tr("Enables Lemon parser debug messages for SQL code assistant.")); - QCommandLineOption listPluginsOption("list-plugins", QObject::tr("Lists plugins installed in the SQLiteStudio and quits.")); + QCommandLineOption listPluginsOption({"lp", "list-plugins"}, QObject::tr("Lists plugins installed in the SQLiteStudio and quits.")); + QCommandLineOption execSqlOption({"e", "execute-sql-file"}, + QObject::tr("Executes provided SQL file (including all rich features of SQLiteStudio's query executor) " + "on the specified database file and quits. " + "The database parameter becomes mandatory if this option is used."), + QObject::tr("SQL file")); + QCommandLineOption sqlFileCodecOption({"c", "file-codec"}, QObject::tr("Character encoding to use when reading SQL file (-e option). " + "Use -cl to list available codecs. " + "Defaults to %1.").arg(defaultCodecName()), + QObject::tr("codec")); + QCommandLineOption codecListOption({"lc", "list-codecs"}, QObject::tr("Lists available codecs to be used with -c option and quits.")); + QCommandLineOption ignoreErrorsOption({"ie", "ignore-errors"}, + QObject::tr("When used together with -e option, the execution will not stop on an error, " + "but rather continue until the end, ignoring errors.")); + parser.addOption(debugOption); parser.addOption(lemonDebugOption); parser.addOption(listPluginsOption); + parser.addOption(execSqlOption); + parser.addOption(sqlFileCodecOption); + parser.addOption(codecListOption); + parser.addOption(ignoreErrorsOption); parser.addPositionalArgument(QObject::tr("file"), QObject::tr("Database file to open")); @@ -37,16 +61,66 @@ QString cliHandleCmdLineArgs() if (parser.isSet(debugOption)) setCliDebug(true); + if (parser.isSet(codecListOption)) + { + for (QString& codec : textCodecNames()) + qOut << codec << "\n"; + + qOut.flush(); + return true; + } + + if (parser.isSet((sqlFileCodecOption))) + { + CliOpts::sqlScriptCodec = parser.value(sqlFileCodecOption); + if (!textCodecNames().contains(CliOpts::sqlScriptCodec)) + { + qErr << QObject::tr("Invalid codec: %1. Use -cl option to list available codecs.").arg(CliOpts::sqlScriptCodec) << "\n"; + qErr.flush(); + return true; + } + } + else + CliOpts::sqlScriptCodec = defaultCodecName(); + + if (parser.isSet(ignoreErrorsOption)) + CliOpts::ignoreErrors = true; + + if (parser.isSet(execSqlOption)) + CliOpts::sqlScriptToExecute = parser.value(execSqlOption); + if (parser.isSet(listPluginsOption)) - listPlugins = true; + CliOpts::listPlugins = true; CompletionHelper::enableLemonDebug = parser.isSet(lemonDebugOption); QStringList args = parser.positionalArguments(); if (args.size() > 0) - return args[0]; + CliOpts::dbToOpen = args[0]; - return QString(); + return false; +} + +int cliExecSqlFromFile(const QString& dbToOpen) +{ + if (dbToOpen.isEmpty()) + { + qErr << QObject::tr("Database file argument is mandatory when executing SQL file.") << "\n"; + qErr.flush(); + return 1; + } + if (!CLI::getInstance()->openDbFile(dbToOpen)) + { + qErr << QObject::tr("Could not open specified database for executing SQL file. You may try using -d option to find out more details.") << "\n"; + qErr.flush(); + return 1; + } + + Db* db = CLI::getInstance()->getCurrentDb(); + + SqlFileExecutor executor; + executor.execSqlFromFile(db, CliOpts::sqlScriptToExecute, CliOpts::ignoreErrors, CliOpts::sqlScriptCodec, false); + return 0; } int main(int argc, char *argv[]) @@ -58,30 +132,34 @@ int main(int argc, char *argv[]) qInstallMessageHandler(cliMessageHandler); - QString dbToOpen = cliHandleCmdLineArgs(); + if (cliHandleCmdLineArgs()) + return 0; - CliResultsDisplay::staticInit(); initCliUtils(); + CliResultsDisplay::staticInit(); SQLITESTUDIO->setInitialTranslationFiles({"coreSQLiteStudio", "sqlitestudiocli"}); SQLITESTUDIO->init(a.arguments(), false); SQLITESTUDIO->initPlugins(); - if (listPlugins) + if (CliOpts::listPlugins) { - for (const PluginManager::PluginDetails& details : PLUGINS->getAllPluginDetails()) + for (PluginManager::PluginDetails& details : PLUGINS->getAllPluginDetails()) qOut << details.name << " " << details.versionString << "\n"; return 0; } + if (!CliOpts::sqlScriptToExecute.isNull()) + return cliExecSqlFromFile(CliOpts::dbToOpen); + CliCommandExecutor executor; QObject::connect(CLI::getInstance(), &CLI::execCommand, &executor, &CliCommandExecutor::execCommand); QObject::connect(&executor, &CliCommandExecutor::executionComplete, CLI::getInstance(), &CLI::executionComplete); - if (!dbToOpen.isEmpty()) - CLI::getInstance()->openDbFile(dbToOpen); + if (!CliOpts::dbToOpen.isEmpty()) + CLI::getInstance()->openDbFile(CliOpts::dbToOpen); CLI::getInstance()->start(); int res = a.exec(); diff --git a/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.pro b/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.pro index e32eca3..9b38487 100644 --- a/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.pro +++ b/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.pro @@ -7,7 +7,7 @@ QT += core QT -= gui -include($$PWD/../dirs.pri) +include($$PWD/../common.pri) OBJECTS_DIR = $$OBJECTS_DIR/sqlitestudiocli MOC_DIR = $$MOC_DIR/sqlitestudiocli @@ -31,17 +31,6 @@ portable { DEFINES += PORTABLE_CONFIG } -TRANSLATIONS += translations/sqlitestudiocli_ro_RO.ts \ - translations/sqlitestudiocli_de.ts \ - translations/sqlitestudiocli_it.ts \ - translations/sqlitestudiocli_zh_CN.ts \ - translations/sqlitestudiocli_sk.ts \ - translations/sqlitestudiocli_ru.ts \ - translations/sqlitestudiocli_pt_BR.ts \ - translations/sqlitestudiocli_fr.ts \ - translations/sqlitestudiocli_es.ts \ - translations/sqlitestudiocli_pl.ts - SOURCES += main.cpp \ cli.cpp \ commands/clicommand.cpp \ @@ -79,7 +68,7 @@ win32: { } unix: { - LIBS += -lreadline -lcurses + LIBS += -lreadline } HEADERS += \ @@ -127,6 +116,17 @@ RESOURCES += \ + + + + + + + + + + + diff --git a/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.qrc b/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.qrc index 613e5bf..7646d2b 100644 --- a/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.qrc +++ b/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.qrc @@ -1,19 +1 @@ - - - translations/sqlitestudiocli_ro_RO.qm - translations/sqlitestudiocli_de.qm - - - translations/sqlitestudiocli_pl.qm - translations/sqlitestudiocli_ru.qm - translations/sqlitestudiocli_fr.qm - translations/sqlitestudiocli_sk.qm - translations/sqlitestudiocli_zh_CN.qm - - - - - - - - + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli.ts new file mode 100644 index 0000000..97abff6 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli.ts @@ -0,0 +1,833 @@ + + + + + CLI + + + Current database: %1 + + + + + No current working database is set. + + + + + Type %1 for help + + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + + Could not add database %1 to list. + + + + + closed + + + + + CliCommand + + + Usage: %1%2 + + + + + CliCommandAdd + + + Could not add database %1 to list. + + + + + Database added: %1 + + + + + adds new database to the list + + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + + name + CLI command syntax + + + + + path + CLI command syntax + + + + + CliCommandCd + + + Changed directory to: %1 + + + + + Could not change directory to: %1 + + + + + changes current working directory + + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + + path + CLI command syntax + + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + + Connection to database %1 closed. + + + + + No such database: %1. Use %2 to see list of known databases. + + + + + closes given (or current) database + + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + + name + CLI command syntax + + + + + CliCommandDbList + + + No current working database defined. + + + + + Databases: + + + + + + Name + CLI db name column + + + + + + Open + CLI connection state column + + + + + + Closed + CLI connection state column + + + + + + Connection + CLI connection state column + + + + + + Database file path + + + + + prints list of registered databases + + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + + Database is not open. + + + + + Cannot find table named: %1 + + + + + shows details about the table + + + + + table + + + + + Table: %1 + + + + + Column name + + + + + Data type + + + + + Constraints + + + + + Virtual table: %1 + + + + + Construction arguments: + + + + + No construction arguments were passed for this virtual table. + + + + + CliCommandDir + + + lists directories and files in current working directory + + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + + pattern + + + + + CliCommandExit + + + quits the application + + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + + CliCommandHelp + + + shows this help message + + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + + command + CLI command syntax + + + + + No such command: %1 + + + + + Type '%1' for list of available commands. + + + + + Usage: %1%2 + + + + + Aliases: %1 + + + + + CliCommandHistory + + + Current history limit is set to: %1 + + + + + prints history or erases it + + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + + number + + + + + Console history erased. + + + + + Invalid number: %1 + + + + + History limit set to %1 + + + + + CliCommandMode + + + Current results printing mode: %1 + + + + + Invalid results printing mode: %1 + + + + + New results printing mode: %1 + + + + + tells or changes the query results format + + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + + CliCommandNullValue + + + Current NULL representation string: %1 + + + + + tells or changes the NULL representation string + + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Could not add database %1 to list. + + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + + Database %1 has been open and set as the current working database. + + + + + opens database connection + + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + + name + CLI command syntax + + + + + path + CLI command syntax + + + + + CliCommandPwd + + + prints the current working directory + + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + + CliCommandRemove + + + No such database: %1 + + + + + Database removed: %1 + + + + + New current database set: + + + + + removes database from the list + + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + + name + CLI command syntax + + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + + Database is not open. + + + + + executes SQL query + + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + + sql + CLI command syntax + + + + + + Too many columns to display in %1 mode. + + + + + Row %1 + + + + + Query execution error: %1 + + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Database %1 is closed. + + + + + + Database + + + + + Table + + + + + prints list of tables in the database + + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + + database + CLI command syntax + + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + + + + + Tables + + + + + Views + + + + + Columns + + + + + Indexes + + + + + + Triggers + + + + + prints all objects in the database as a tree + + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + + CliCommandUse + + + No current database selected. + + + + + + Current database: %1 + + + + + No such database: %1 + + + + + changes default working database + + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + + name + CLI command syntax + + + + + QObject + + + Insufficient number of arguments. + + + + + Too many arguments. + + + + + Invalid argument value: %1. +Expected one of: %2 + + + + + Unknown option: %1 + CLI command syntax + + + + + Option %1 requires an argument. + CLI command syntax + + + + + string + CLI command syntax + + + + + Command line interface to SQLiteStudio, a SQLite manager. + + + + + Enables debug messages on standard error output. + + + + + Enables Lemon parser debug messages for SQL code assistant. + + + + + Lists plugins installed in the SQLiteStudio and quits. + + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + + SQL file + + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + + codec + + + + + Lists available codecs to be used with -c option and quits. + + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + + file + + + + + Database file to open + + + + + Invalid codec: %1. Use -cl option to list available codecs. + + + + + Database file argument is mandatory when executing SQL file. + + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_af_ZA.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_af_ZA.ts new file mode 100644 index 0000000..32871c9 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_af_ZA.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ar_SA.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ar_SA.ts new file mode 100644 index 0000000..8064520 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ar_SA.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ca_ES.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ca_ES.ts new file mode 100644 index 0000000..e8bbc91 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ca_ES.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_cs_CZ.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_cs_CZ.ts new file mode 100644 index 0000000..c385fd8 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_cs_CZ.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_da_DK.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_da_DK.ts new file mode 100644 index 0000000..ab3c350 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_da_DK.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de.qm b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de.ts deleted file mode 100644 index c00f2e6..0000000 --- a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de.ts +++ /dev/null @@ -1,788 +0,0 @@ - - - - - CLI - - - Current database: %1 - - - - - No current working database is set. - - - - - Type %1 for help - - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - - - - - Could not add database %1 to list. - - - - - closed - - - - - CliCommand - - - Usage: %1%2 - - - - - CliCommandAdd - - - Could not add database %1 to list. - - - - - Database added: %1 - - - - - adds new database to the list - - - - - Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. - - - - - name - CLI command syntax - - - - - path - CLI command syntax - - - - - CliCommandCd - - - Changed directory to: %1 - - - - - Could not change directory to: %1 - - - - - changes current working directory - - - - - Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - - - - - path - CLI command syntax - - - - - CliCommandClose - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - - Connection to database %1 closed. - - - - - No such database: %1. Use %2 to see list of known databases. - - - - - closes given (or current) database - - - - - Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). - - - - - name - CLI command syntax - - - - - CliCommandDbList - - - No current working database defined. - - - - - Databases: - - - - - - Name - CLI db name column - - - - - - Open - CLI connection state column - - - - - - Closed - CLI connection state column - - - - - - Connection - CLI connection state column - - - - - - Database file path - - - - - prints list of registered databases - - - - - Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - - - - - CliCommandDesc - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - - - - - Database is not open. - - - - - Cannot find table named: %1 - - - - - shows details about the table - - - - - table - - - - - Table: %1 - - - - - Column name - - - - - Data type - - - - - Constraints - - - - - Virtual table: %1 - - - - - Construction arguments: - - - - - No construction arguments were passed for this virtual table. - - - - - CliCommandDir - - - lists directories and files in current working directory - - - - - This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. - -You can pass <pattern> with wildcard characters to filter output. - - - - - pattern - - - - - CliCommandExit - - - quits the application - - - - - Quits the application. Settings are stored in configuration file and will be restored on next startup. - - - - - CliCommandHelp - - - shows this help message - - - - - Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. -To see list of supported commands, type %2 without any arguments. - -When passing <command> name, you can skip special prefix character ('%3'). - -You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. - - - - - command - CLI command syntax - - - - - No such command: %1 - - - - - Type '%1' for list of available commands. - - - - - Usage: %1%2 - - - - - Aliases: %1 - - - - - CliCommandHistory - - - Current history limit is set to: %1 - - - - - prints history or erases it - - - - - When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. - -When the -c or --clear option is passed, then the history gets erased. -When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. -Use -ql or --querylimit option to see the current limit value. - - - - - number - - - - - Console history erased. - - - - - Invalid number: %1 - - - - - History limit set to %1 - - - - - CliCommandMode - - - Current results printing mode: %1 - - - - - Invalid results printing mode: %1 - - - - - New results printing mode: %1 - - - - - tells or changes the query results format - - - - - When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: -- CLASSIC - columns are separated by a comma, not aligned, -- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, -- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), -- ROW - each column from the row is displayed in new line, so the full data is displayed. - -The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. - -The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. - -The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. -ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. - -The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - - - - - CliCommandNullValue - - - Current NULL representation string: %1 - - - - - tells or changes the NULL representation string - - - - - If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. - - - - - CliCommandOpen - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - Could not add database %1 to list. - - - - - File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. - - - - - Database %1 has been open and set as the current working database. - - - - - opens database connection - - - - - Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. - - - - - name - CLI command syntax - - - - - path - CLI command syntax - - - - - CliCommandPwd - - - prints the current working directory - - - - - This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. - - - - - CliCommandRemove - - - No such database: %1 - - - - - Database removed: %1 - - - - - New current database set: - - - - - removes database from the list - - - - - Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. - - - - - name - CLI command syntax - - - - - CliCommandSql - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - - - - - Database is not open. - - - - - executes SQL query - - - - - This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. - - - - - sql - CLI command syntax - - - - - - Too many columns to display in %1 mode. - - - - - Row %1 - - - - - Query execution error: %1 - - - - - CliCommandTables - - - No such database: %1. Use %2 to see list of known databases. - - - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - Database %1 is closed. - - - - - - Database - - - - - Table - - - - - prints list of tables in the database - - - - - Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. -When the -s option is given, then system tables are also listed. - - - - - database - CLI command syntax - - - - - CliCommandTree - - - No current working database is selected. Use %1 to define one and then run %2. - - - - - Tables - - - - - Views - - - - - Columns - - - - - Indexes - - - - - - Triggers - - - - - prints all objects in the database as a tree - - - - - Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. -When -c option is given, then also columns will be listed under each table. -When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). -The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. - - - - - CliCommandUse - - - No current database selected. - - - - - - Current database: %1 - - - - - No such database: %1 - - - - - changes default working database - - - - - Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. - -What is current working database? -When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). - -The default database can be selected in various ways: -- using %1 command, -- by passing database file name to the application startup parameters, -- by passing registered database name to the application startup parameters, -- by restoring previously selected default database from saved configuration, -- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. - - - - - name - CLI command syntax - - - - - QObject - - - Insufficient number of arguments. - - - - - Too many arguments. - - - - - Invalid argument value: %1. -Expected one of: %2 - - - - - Unknown option: %1 - CLI command syntax - - - - - Option %1 requires an argument. - CLI command syntax - - - - - string - CLI command syntax - - - - - Command line interface to SQLiteStudio, a SQLite manager. - - - - - Enables debug messages on standard error output. - - - - - Enables Lemon parser debug messages for SQL code assistant. - - - - - Lists plugins installed in the SQLiteStudio and quits. - - - - - file - - - - - Database file to open - - - - diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de_DE.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de_DE.ts new file mode 100644 index 0000000..bd8b6c2 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_de_DE.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_el_GR.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_el_GR.ts new file mode 100644 index 0000000..cd0a56d --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_el_GR.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_en_US.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_en_US.ts new file mode 100644 index 0000000..14795d1 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_en_US.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es.qm b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es.ts deleted file mode 100644 index 7c2d175..0000000 --- a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es.ts +++ /dev/null @@ -1,788 +0,0 @@ - - - - - CLI - - - Current database: %1 - - - - - No current working database is set. - - - - - Type %1 for help - - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - - - - - Could not add database %1 to list. - - - - - closed - - - - - CliCommand - - - Usage: %1%2 - - - - - CliCommandAdd - - - Could not add database %1 to list. - - - - - Database added: %1 - - - - - adds new database to the list - - - - - Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. - - - - - name - CLI command syntax - - - - - path - CLI command syntax - - - - - CliCommandCd - - - Changed directory to: %1 - - - - - Could not change directory to: %1 - - - - - changes current working directory - - - - - Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - - - - - path - CLI command syntax - - - - - CliCommandClose - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - - Connection to database %1 closed. - - - - - No such database: %1. Use %2 to see list of known databases. - - - - - closes given (or current) database - - - - - Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). - - - - - name - CLI command syntax - - - - - CliCommandDbList - - - No current working database defined. - - - - - Databases: - - - - - - Name - CLI db name column - - - - - - Open - CLI connection state column - - - - - - Closed - CLI connection state column - - - - - - Connection - CLI connection state column - - - - - - Database file path - - - - - prints list of registered databases - - - - - Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - - - - - CliCommandDesc - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - - - - - Database is not open. - - - - - Cannot find table named: %1 - - - - - shows details about the table - - - - - table - - - - - Table: %1 - - - - - Column name - - - - - Data type - - - - - Constraints - - - - - Virtual table: %1 - - - - - Construction arguments: - - - - - No construction arguments were passed for this virtual table. - - - - - CliCommandDir - - - lists directories and files in current working directory - - - - - This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. - -You can pass <pattern> with wildcard characters to filter output. - - - - - pattern - - - - - CliCommandExit - - - quits the application - - - - - Quits the application. Settings are stored in configuration file and will be restored on next startup. - - - - - CliCommandHelp - - - shows this help message - - - - - Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. -To see list of supported commands, type %2 without any arguments. - -When passing <command> name, you can skip special prefix character ('%3'). - -You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. - - - - - command - CLI command syntax - - - - - No such command: %1 - - - - - Type '%1' for list of available commands. - - - - - Usage: %1%2 - - - - - Aliases: %1 - - - - - CliCommandHistory - - - Current history limit is set to: %1 - - - - - prints history or erases it - - - - - When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. - -When the -c or --clear option is passed, then the history gets erased. -When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. -Use -ql or --querylimit option to see the current limit value. - - - - - number - - - - - Console history erased. - - - - - Invalid number: %1 - - - - - History limit set to %1 - - - - - CliCommandMode - - - Current results printing mode: %1 - - - - - Invalid results printing mode: %1 - - - - - New results printing mode: %1 - - - - - tells or changes the query results format - - - - - When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: -- CLASSIC - columns are separated by a comma, not aligned, -- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, -- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), -- ROW - each column from the row is displayed in new line, so the full data is displayed. - -The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. - -The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. - -The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. -ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. - -The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - - - - - CliCommandNullValue - - - Current NULL representation string: %1 - - - - - tells or changes the NULL representation string - - - - - If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. - - - - - CliCommandOpen - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - Could not add database %1 to list. - - - - - File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. - - - - - Database %1 has been open and set as the current working database. - - - - - opens database connection - - - - - Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. - - - - - name - CLI command syntax - - - - - path - CLI command syntax - - - - - CliCommandPwd - - - prints the current working directory - - - - - This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. - - - - - CliCommandRemove - - - No such database: %1 - - - - - Database removed: %1 - - - - - New current database set: - - - - - removes database from the list - - - - - Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. - - - - - name - CLI command syntax - - - - - CliCommandSql - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - - - - - Database is not open. - - - - - executes SQL query - - - - - This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. - - - - - sql - CLI command syntax - - - - - - Too many columns to display in %1 mode. - - - - - Row %1 - - - - - Query execution error: %1 - - - - - CliCommandTables - - - No such database: %1. Use %2 to see list of known databases. - - - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - Database %1 is closed. - - - - - - Database - - - - - Table - - - - - prints list of tables in the database - - - - - Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. -When the -s option is given, then system tables are also listed. - - - - - database - CLI command syntax - - - - - CliCommandTree - - - No current working database is selected. Use %1 to define one and then run %2. - - - - - Tables - - - - - Views - - - - - Columns - - - - - Indexes - - - - - - Triggers - - - - - prints all objects in the database as a tree - - - - - Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. -When -c option is given, then also columns will be listed under each table. -When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). -The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. - - - - - CliCommandUse - - - No current database selected. - - - - - - Current database: %1 - - - - - No such database: %1 - - - - - changes default working database - - - - - Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. - -What is current working database? -When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). - -The default database can be selected in various ways: -- using %1 command, -- by passing database file name to the application startup parameters, -- by passing registered database name to the application startup parameters, -- by restoring previously selected default database from saved configuration, -- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. - - - - - name - CLI command syntax - - - - - QObject - - - Insufficient number of arguments. - - - - - Too many arguments. - - - - - Invalid argument value: %1. -Expected one of: %2 - - - - - Unknown option: %1 - CLI command syntax - - - - - Option %1 requires an argument. - CLI command syntax - - - - - string - CLI command syntax - - - - - Command line interface to SQLiteStudio, a SQLite manager. - - - - - Enables debug messages on standard error output. - - - - - Enables Lemon parser debug messages for SQL code assistant. - - - - - Lists plugins installed in the SQLiteStudio and quits. - - - - - file - - - - - Database file to open - - - - diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es_ES.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es_ES.ts new file mode 100644 index 0000000..20725c9 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_es_ES.ts @@ -0,0 +1,875 @@ + + + + + CLI + + + Current database: %1 + Base de datos actual: %1 + + + + No current working database is set. + Sin base de datos en uso establecida. + + + + Type %1 for help + Escribe %1 para la ayuda + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + La base de datos que pasaste como parámetro de la línea de comandos (%1) ya estaba en la lista como: %2 + + + + Could not add database %1 to list. + No se pudo agregar la base de datos %1 a la lista. + + + + closed + cerrado + + + + CliCommand + + + Usage: %1%2 + Uso: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + No se pudo agregar la base de datos %1 a la lista. + + + + Database added: %1 + Base de datos añadida: %1 + + + + adds new database to the list + añade una nueva base de datos a la lista + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Agrega la base de datos indicada en la <ruta> con el <nombre> especificado para listar las bases de datos. El <nombre> sólo representa un nombre simbólico al cual puedes hacer referencia. Sólo elige cualquier nombre único. Para una lista de las bases de datos actualmente disponibles usa el comando %1. + + + + name + CLI command syntax + nombre + + + + path + CLI command syntax + ruta + + + + CliCommandCd + + + Changed directory to: %1 + Directorio cambiado a: %1 + + + + Could not change directory to: %1 + No se pudo cambiar el directorio a: %1 + + + + changes current working directory + cambia el directorio de trabajo actual + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Comando conocido en sistemas Unix y Windows muy parecido a 'cd'. Requiere pasar un argumento <ruta>, por lo que llamar a %1 ocasionará siempre un cambio del directorio. Para saber cuál es el directorio de trabajo actual usa el comando %2, y para listar los contenidos del directorio de trabajo actual usa el comando %3. + + + + path + CLI command syntax + ruta + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + No se puede llamar a %1 cuando no hay una base de datos actualmente establecida. Especifica la base de datos actual con el comando %2 o pasa el nombre de la base de datos a %3. + + + + + Connection to database %1 closed. + Conexión a la base de datos %1 cerrada. + + + + No such database: %1. Use %2 to see list of known databases. + No existe tal base de datos: %1. Usa %2 para ver una lista de bases de datos conocidas. + + + + closes given (or current) database + cierra la base de datos indicada (o actual) + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Cierra la conexión a la base de datos. Si la base de datos ya estaba cerrada, nada sucede. Si se indica un <nombre>, debería ser el nombre de la base de datos a cerrar (como lo muestra el comando %1). Si no se proporciona el <nombre>, entonces se cierra la base de datos actual (mira la ayuda para %2 para más detalles). + + + + name + CLI command syntax + nombre + + + + CliCommandDbList + + + No current working database defined. + Sin base de datos en uso definida. + + + + Databases: + Bases de datos: + + + + + Name + CLI db name column + Nombre + + + + + Open + CLI connection state column + Abierto + + + + + Closed + CLI connection state column + Cerrado + + + + + Connection + CLI connection state column + Conexión + + + + + Database file path + Ruta de archivo de la base de datos + + + + prints list of registered databases + imprime una lista de bases de datos registradas + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Imprime una lista de las bases de datos registradas en SQLiteStudio. Cada base de datos en la lista puede estar en un estado abierto o cerrado, y %1 te lo indica. La base de datos en uso actual (o base de datos por defecto) también está marcada en la lista con un '*' al comienzo de su nombre. Mira la ayuda del comando %2 para saber más sobre la base de datos por defecto. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No hay una base de datos en uso establecida. +Ejecuta %1 para establecer la base de datos a usar. +Ejecuta %2 para ver una lista de todas las bases de datos. + + + + Database is not open. + La base de datos no está abierta. + + + + Cannot find table named: %1 + No se puede encontrar la tabla %1 + + + + shows details about the table + muestra detalles sobre la tabla + + + + table + tabla + + + + Table: %1 + Tabla: %1 + + + + Column name + Nombre de columna + + + + Data type + Tipo de dato + + + + Constraints + Restricciones + + + + Virtual table: %1 + Tabla virtual: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + sale de la aplicación + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Demasiadas columnas para mostrar en el modo %1. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No existe tal base de datos: %1. Usa %2 para ver una lista de bases de datos conocidas. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + No se puede llamar a %1 cuando no hay una base de datos actualmente establecida. Especifica la base de datos actual con el comando %2 o pasa el nombre de la base de datos a %3. + + + + Database %1 is closed. + La base de datos %1 está cerrada. + + + + + Database + Base de datos + + + + Table + Tabla + + + + prints list of tables in the database + imprime una lista de tablas en la base de datos + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Imprime la lista de tablas de la <base de datos> dada, o de la base de datos actualmente en uso. Notar que, <base de datos> debería ser el nombre de la base de datos registrada (ver %1). La lista de la salida incluye todas las tablas de cualquier otra base de datos adjuntada a la base de datos consultada. Cuando se especifica la opción -s, las tablas del sistema también se listan. + + + + database + CLI command syntax + base de datos + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No hay una base de datos en uso seleccionada. Usa %1 para definir una y luego ejecuta %2. + + + + Tables + Tablas + + + + Views + Vistas + + + + Columns + Columnas + + + + Indexes + Ãndices + + + + + Triggers + Disparadores + + + + prints all objects in the database as a tree + imprime todos los objetos en la base de datos como un árbol + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Imprime todos los objetos (tablas, índices, disparadores y vistas) que están en la base de datos, como un árbol. El árbol es muy similar al que puedes ver en la IU de SQLiteStudio. +Cuando la opción -c se especifica, también se listarán las columnas debajo de cada tabla. +Cuando la opción -s se especifica, también los objetos del sistema se imprimirán (tablas sqlite_*, índices autoincrementables, etc). +El argumento database es opcional, y si se indica, entonces solamente se mostrará la base de datos especificada. Este no es un nombre de base de datos registrado, sino un nombre de base de datos interno de SQLite, como 'main', 'temp', o cualquier nombre de base de datos adjuntada. Para imprimir el árbol para otra base de datos registrada, ejecuta primero %1 para cambiar la base de datos en uso, y luego ejecuta el comando %2. + + + + CliCommandUse + + + No current database selected. + Sin base de datos actual seleccionada. + + + + + Current database: %1 + Base de datos actual: %1 + + + + No such database: %1 + No hay tal base de datos: %1 + + + + changes default working database + cambia la base de datos en uso por defecto + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Número insuficiente de argumentos. + + + + Too many arguments. + Demasiados argumentos. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fa_IR.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fa_IR.ts new file mode 100644 index 0000000..c0f7ca8 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fa_IR.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fi_FI.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fi_FI.ts new file mode 100644 index 0000000..8542654 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fi_FI.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr.qm b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr.qm deleted file mode 100644 index 00760ec..0000000 Binary files a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr.ts deleted file mode 100644 index 3e4c3e5..0000000 --- a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr.ts +++ /dev/null @@ -1,830 +0,0 @@ - - - - - CLI - - - Current database: %1 - Base de données actuelle : %1 - - - - No current working database is set. - Aucune base de données en cours n’est activée. - - - - Type %1 for help - Touche %1 pour l’aide - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - - - - - Could not add database %1 to list. - Impossible d’ajouter la base de données %1 à la liste. - - - - closed - Fermé - - - - CliCommand - - - Usage: %1%2 - Uttilisation : %1%2 - - - - CliCommandAdd - - - Could not add database %1 to list. - Impossible d’ajouter le base de données %1 à la liste. - - - - Database added: %1 - Base de données ajoutée : %1 - - - - adds new database to the list - Ajoutez la nouvelle base de données à la liste - - - - Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. - Ajoutez la base de données pointée par <chemin> nommée <nom> à la liste des baszs de données. Le <nom>est seulement un nom symbolique que vous pourrez y référer. Choississez un nom unique. Pour une base de données figuant dans la liste utilisez la commande %1. - - - - name - CLI command syntax - Nom - - - - path - CLI command syntax - Chemin - - - - CliCommandCd - - - Changed directory to: %1 - Renommer le repertoire en : %1 - - - - Could not change directory to: %1 - Impossible de renommer le répertoire en : %1 - - - - changes current working directory - Modifiezle répertoire de travail actuel - - - - Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - La commande « cd » est connu du système UNIX et Windows. Elle nécessite le paramètre <chemin> passé avant l’appel %1 qui occasionnera une modification du répertoire. Pour connaitre qu’elle est le répertoire courant utiliser la commande %2 et pour lister le contenu de celui-ci utilisez la commande %3. - - - - path - CLI command syntax - Chemin - - - - CliCommandClose - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - Impossible d’appeler %1 lorsqu’aucune base de données n’est active. Spécifiez la base de données active avec la commande %2 ou par le nom de la base de données par %3. - - - - - Connection to database %1 closed. - Connexion à la base de données %1 fermée. - - - - No such database: %1. Use %2 to see list of known databases. - Aucune base de données : %1. Utilisez %2 pour avoir la liste des bases de données connues. - - - - closes given (or current) database - Fermeture d’une de données sélectionnées (ou actuelle) - - - - Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). - Fermeture de base de données connectée. . Si la base est déjà fermée, aucune action. Si <name> est fourni, c’est celle ainsi qui sera close (as printed by %1 command). Si <name> n’est pas fourni, la base actuelle est close (voir l’aide %2 pour plus de détails). - - - - name - CLI command syntax - Nom - - - - CliCommandDbList - - - No current working database defined. - Aucune base de données actuelle n’est définie. - - - - Databases: - Base de données : - - - - - Name - CLI db name column - Nom - - - - - Open - CLI connection state column - Ouvrir - - - - - Closed - CLI connection state column - Fermer - - - - - Connection - CLI connection state column - Connexion - - - - - Database file path - Chemin de la base de données - - - - prints list of registered databases - Imprimer la liste des bases de données enregistrées - - - - Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - Imprimez la liste des bases de données enregistrées sous SQLiteStudio. Chaque base se données de la liste peut être ouverte ou close et %1 vous indique lesquellest.La base de données actuelle est aussi marquée dans la liste par « * » en début de nom. Voir l’aide la commande %2 pour en savoir plus sur la base de données actuelle. - - - - CliCommandDesc - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - Aucune base de données de travail n’est activée. -Appelez la commande %1 pour activer la base de données active. -Appelez %2 pour voir la liste de toutes les bases de données. - - - - Database is not open. - La base de données n’est pas ouverte. - - - - Cannot find table named: %1 - - - - - shows details about the table - Affichage des détails de la table - - - - table - Table - - - - Table: %1 - - - - - Column name - - - - - Data type - - - - - Constraints - - - - - Virtual table: %1 - - - - - Construction arguments: - - - - - No construction arguments were passed for this virtual table. - - - - - CliCommandDir - - - lists directories and files in current working directory - Listes des répertoires et fichiers dans le répertoire de travail - - - - This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. - -You can pass <pattern> with wildcard characters to filter output. - Ceci est très semblable à la commande « dir » de Windows et à la commande de « ls » de systèmes Unix. - -You pouvez utiliser les caractères de remplacement <pattern> npour filtrer la sortie. - - - - pattern - Modèle - - - - CliCommandExit - - - quits the application - Quitter l’application - - - - Quits the application. Settings are stored in configuration file and will be restored on next startup. - Quittez l’apllication. Le paramètrage est stocké dans la configuration et sera restauré au prochain lancement. - - - - CliCommandHelp - - - shows this help message - Affichagez l’aide du message - - - - Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. -To see list of supported commands, type %2 without any arguments. - -When passing <command> name, you can skip special prefix character ('%3'). - -You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. - Utilisez %1 pour connaitre certaines commandes supportées par la ligne de commande (CLI) de SQLiteStudio. -Pour voir les commandes supportées, saississez %2 sana arguments. - -En utilisant le nom de <command>, vous ajouter le caractère spécial(« %3 »). - -Vous pouvez exécuter n’importe quelle commande avec l’option « --help » pour voir l’aide pour cette commande. C’est une alternative à : %1 <commande>. - - - - command - CLI command syntax - CLI syntax de commandes - - - - No such command: %1 - Aucune telle commande : %1 - - - - Type '%1' for list of available commands. - Saisissez « %1 » pour la liste des commandes valides. - - - - Usage: %1%2 - Utilisation : %1%2 - - - - Aliases: %1 - Pseudomynes : %1 - - - - CliCommandHistory - - - Current history limit is set to: %1 - L’historique actuel est limité à : %1 - - - - prints history or erases it - Imprimez l’historique ou supprimez le - - - - When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. - -When the -c or --clear option is passed, then the history gets erased. -When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. -Use -ql or --querylimit option to see the current limit value. - Lorqu’aucun argument n’est passé,cette commande imprime l’histoirique. Chaque entrée est séparée par une ligne vide, permettant une lecture aisée. - -When the -c or --clear option is passed, then the history gets erased. -When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. -Use -ql or --querylimit option to see the current limit value. - - - - number - Nombre - - - - Console history erased. - Historique effacé. - - - - Invalid number: %1 - Nombre invalide : %1 - - - - History limit set to %1 - Historique limité à %1 - - - - CliCommandMode - - - Current results printing mode: %1 - Résultats actuels du mode d’ impression : %1 - - - - Invalid results printing mode: %1 - Résultats invalides du mode d’ impression : %1 - - - - New results printing mode: %1 - Résultats actuels du mode d’ impression : %1 - - - - tells or changes the query results format - Modifie le format du résultat de la requête - - - - When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: -- CLASSIC - columns are separated by a comma, not aligned, -- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, -- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), -- ROW - each column from the row is displayed in new line, so the full data is displayed. - -The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. - -The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. - -The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. -ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. - -The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - Sans argument, le format de sortie actuel de la requête est utilisé. Avec <mode>c'est un de ces mode qui est utilisé : -- CLASSIC - columns are separated by a comma, not aligned, -- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, -- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), -- ROW - each column from the row is displayed in new line, so the full data is displayed. - -The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. - -The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. - -The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. -ATTENTION ! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. - -The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - - - - CliCommandNullValue - - - Current NULL representation string: %1 - Représentation actuelle d’une chaine NULL : %1 - - - - tells or changes the NULL representation string - Modifiez la représentation d’une chaine NULL - - - - If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. - Si on n’a passé aucun argument, c’est la représentation de valeur NULL actuelle qui est utilisée (ce qui est imprimé à la place de valeurs NULL dans des résultats de requête). Si on donne un argument, il sera utilisé comme une nouvelle chaine représentant NULL. - - - - CliCommandOpen - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - Impossible d’appeler %1 lorsque aucune base de données n’est sélectionnée. Spécifiez la base de données actuelle avec la commande %2 ou nommez la base de données %3. - - - - Could not add database %1 to list. - Impossible d’ajouter la base de données %1 à la liste. - - - - File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. - Le fichier %1 n’existe pas dans %2. Impossible d’ouvrir une base de données avec la commande %3. Pour créer une nouvelle base de données utilisez la commande %4. - - - - Database %1 has been open and set as the current working database. - La base de données %1 a été ouverte et sélectionnée comme base de données actuelle. - - - - opens database connection - Ouvre la connexion de la base de données - - - - Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. - Ouvre la connexion de la base de données. Si aucun argument n’est passé, alors la connexion est ouverte comme base de données actuelle (voir l’aide %1 pour plus de détails). Cependant si on a passé un argument il peut être le <name> d’une base de données enregistrée, ou cela peut être le <chemin> du fichier de base de données. Dans le deuxième cas, le <chemin> est enregistré dans la liste avec un nom généré mais seulement pendant la période de la session actuelle. Après la reprise de la l’application une telle base de données n’est pas rétablie dans la liste. - - - - name - CLI command syntax - Nom - - - - path - CLI command syntax - Chemin - - - - CliCommandPwd - - - prints the current working directory - Imprime le répertoire de travail actuel - - - - This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. - C’est la même commande d’un système Unix « pwd » ou « cd » sans arguments de Windows. Ceci imprimele répertoire de travail courant. Vous pouvez changer le répertoire avec le commande %1 et avoir la liste des répertoire de travail avec la commande %2. - - - - CliCommandRemove - - - No such database: %1 - Aucune base de données : %1 - - - - Database removed: %1 - Base de données enlevée : %1 - - - - New current database set: - Nouvelle base de données actuelle : - - - - removes database from the list - Enleve la base de données de la liste - - - - Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. - Enlève la base de données <nom> de la liste des bases enregistrées.si la base de données n’est pas dans la liste (voir la commande %1), alors message d’erreur est imprimé sans aucunes autres lignes. - - - - name - CLI command syntax - Nom - - - - CliCommandSql - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - Aucune base de données de travail n’est activée. -Appelez la commande %1 pour activer la base de données active. -Appelez %2 pour voir la liste de toutes les bases de données. - - - - Database is not open. - La base de données n’est pas ouverte. - - - - executes SQL query - Exécute la requête SQL - - - - This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. - Cette commande est exécutée chaque fois vous saississez une requête SQL au prompt de commande. Il exécute la requête sur la base de données actuelle (voir l’aide %1 pour les détails). Il n’y a aucun sens dans l’exécution de cette commande explicitement. Instead just type the SQL query in the command prompt, without any command prefixed. - - - - sql - CLI command syntax - SQL - - - - - Too many columns to display in %1 mode. - Trop de colonnes à afficher avec le mode %1. - - - - Row %1 - Ligne %1 - - - - Query execution error: %1 - Erreur d’exécution de la requête : %1 - - - - CliCommandTables - - - No such database: %1. Use %2 to see list of known databases. - Aucune base de données : %1. Utilisez %2pour voir la liste des base de données connues. - - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - Impossible d’appeler %1 quand aucune base de données n’est active. Spécifiez la base de données active avec la commade %2 ou nommez la base de données avec %3. - - - - Database %1 is closed. - La base de données %1 est fermée. - - - - - Database - Base de données - - - - Table - Table - - - - prints list of tables in the database - Imprime la liste des tablesde la base de données - - - - Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. -When the -s option is given, then system tables are also listed. - Imprime la liste des tables de la <base de données> sélectionnée ou de la base de données actuelle. Notez que la <base de données> devrait être le nom enregistré de la base de données (voir %1). L’affichage de la liste inclus toutes les tables d’autres bases de données attachées à celle-ci. -Lorsque l’option « -s » est ajouté, les tables système sont aussi listées. - - - - database - CLI command syntax - Base de données - - - - CliCommandTree - - - No current working database is selected. Use %1 to define one and then run %2. - Aucune base de données actuelle n’est sélectionnée. Utilisez %1 pour en définir uneet lancez avec %2. - - - - Tables - Tables - - - - Views - Vues - - - - Columns - Colonnes - - - - Indexes - Index - - - - - Triggers - Déclancheurs - - - - prints all objects in the database as a tree - Imprime tous les objets de la base de données comme un arbre - - - - Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. -When -c option is given, then also columns will be listed under each table. -When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). -The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. - Imprime tous les objets (tables, index, déclencheurs et vues) qui sont dans la base de données comme un arbre. L’arbre est très semblable à celui que vous pouvez voir dans lGUI client de SQLiteStudio. -Quand on ajoute l’option -c, alors les colonnes seront aussi inscrites sous chaque table. -Quand on ajoute l’option -s, alors les objets de système seront aussi imprimés (sqlite_* tables, des index d’auto-incrément, etc). -L’argument de base de données est facultatif et si fourni, alors seulement la base de données indiquée sera imprimée. Ceci n’est pas un nom de base de données enregistré, mais au lieu de cela c’est un nom de base de données SQLite interne, comme « principal », « temporaire », ou n’importe quel nom de base de données attaché. Pour imprimer l’arbre pour d’autre base de données enregistrée, appelez %1 d’abord pour changer la base de données actuelleet utiliser la commande %2. - - - - CliCommandUse - - - No current database selected. - Aucune base de données active de sélectionnée. - - - - - Current database: %1 - Base de données actuelle : %1 - - - - No such database: %1 - Aucune base de données : %1 - - - - changes default working database - Change la base de données actelle par défaut - - - - Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. - -What is current working database? -When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). - -The default database can be selected in various ways: -- using %1 command, -- by passing database file name to the application startup parameters, -- by passing registered database name to the application startup parameters, -- by restoring previously selected default database from saved configuration, -- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. - Changet la base de données actuelle <nom>. Si le <nom > de la base de données n’est pas enregistrée dans l’application, le message d’erreur est imprimé et aucun changement n’est fait. - -Quel est la base de données actuelle ? -Quand vous saississez une requête SQL à exécuter, celle-ci est exécutée sur la base de données par défaut, que l’on connaît aussi comme la base de données actuelle. La plupart de commandes concernant la base de données utilise la base de données de défaut d’utilisation, si on n’a fourni aucune base de données dans leurs arguments. La base de données actuelle est toujours identifiée par la ligne de commande. La base de données par défaut est toujours définie (à moins qu’il n’y ait aucune base de données dans la liste). - -La base de données par défaut peut être choisie de diverses manières : -- Utilisation de la commande %1, -- En passant nom de fichier de base de données aux paramètres de démarrage d’application, -- En passantle nom la base de données enregistrée aux paramètres de démarrage d’application, -- En restaurant la base de données par défaut précédemment choisie dans la configuration sauvée, -- Ou quand la base de données par défaut n’a été choisie par aucun du susdit, l’alors première base de données de la liste de bases de données enregistrée devient le par défaut. - - - - name - CLI command syntax - Nom - - - - QObject - - - Insufficient number of arguments. - Nombre d’arguments insuffisant. - - - - Too many arguments. - Trop d’arguements. - - - - Invalid argument value: %1. -Expected one of: %2 - Valeur invalide de l’arguement %1. Excepté l’un d’eux : %2 - - - - Unknown option: %1 - CLI command syntax - Option %1 inconnue - - - - Option %1 requires an argument. - CLI command syntax - L’option %1 nécessite un argument. - - - - string - CLI command syntax - Chaîne - - - - Command line interface to SQLiteStudio, a SQLite manager. - Interface de ligne de commandes de SQLiteStudio, SQLite manager. - - - - Enables debug messages on standard error output. - Messages de débogage valides sur sortie d’erreur standard. - - - - Enables Lemon parser debug messages for SQL code assistant. - Permet le débogage avec l’analyseur syntaxique de Lemon pour l’assistant SQL. - - - - Lists plugins installed in the SQLiteStudio and quits. - Liste les plugins installés dans SQLiteStudio et quitte. - - - - file - Fichier - - - - Database file to open - Base de données à ouvrir - - - diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr_FR.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr_FR.ts new file mode 100644 index 0000000..2e73086 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_fr_FR.ts @@ -0,0 +1,875 @@ + + + + + CLI + + + Current database: %1 + Base de données actuelle : %1 + + + + No current working database is set. + Aucune base de données en cours n’est activée. + + + + Type %1 for help + Touche %1 pour l’aide + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + La base de données passée dans les paramètres de la ligne de commande (%1) était déjà dans la liste sous le nom : %2 + + + + Could not add database %1 to list. + Impossible d’ajouter la base de données %1 à la liste. + + + + closed + Fermé + + + + CliCommand + + + Usage: %1%2 + Uttilisation : %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Impossible d’ajouter le base de données %1 à la liste. + + + + Database added: %1 + Base de données ajoutée : %1 + + + + adds new database to the list + Ajoutez la nouvelle base de données à la liste + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Ajoutez la base de données pointée par <chemin> nommée <nom> à la liste des baszs de données. Le <nom>est seulement un nom symbolique que vous pourrez y référer. Choississez un nom unique. Pour une base de données figuant dans la liste utilisez la commande %1. + + + + name + CLI command syntax + Nom + + + + path + CLI command syntax + Chemin + + + + CliCommandCd + + + Changed directory to: %1 + Renommer le repertoire en : %1 + + + + Could not change directory to: %1 + Impossible de renommer le répertoire en : %1 + + + + changes current working directory + Modifiezle répertoire de travail actuel + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + La commande « cd » est connu du système UNIX et Windows. Elle nécessite le paramètre <chemin> passé avant l’appel %1 qui occasionnera une modification du répertoire. Pour connaitre qu’elle est le répertoire courant utiliser la commande %2 et pour lister le contenu de celui-ci utilisez la commande %3. + + + + path + CLI command syntax + Chemin + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Impossible d’appeler %1 lorsqu’aucune base de données n’est active. Spécifiez la base de données active avec la commande %2 ou par le nom de la base de données par %3. + + + + + Connection to database %1 closed. + Connexion à la base de données %1 fermée. + + + + No such database: %1. Use %2 to see list of known databases. + Aucune base de données : %1. Utilisez %2 pour avoir la liste des bases de données connues. + + + + closes given (or current) database + Fermeture d’une de données sélectionnées (ou actuelle) + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Fermeture de base de données connectée. . Si la base est déjà fermée, aucune action. Si <name> est fourni, c’est celle ainsi qui sera close (as printed by %1 command). Si <name> n’est pas fourni, la base actuelle est close (voir l’aide %2 pour plus de détails). + + + + name + CLI command syntax + Nom + + + + CliCommandDbList + + + No current working database defined. + Aucune base de données actuelle n’est définie. + + + + Databases: + Base de données : + + + + + Name + CLI db name column + Nom + + + + + Open + CLI connection state column + Ouvrir + + + + + Closed + CLI connection state column + Fermer + + + + + Connection + CLI connection state column + Connexion + + + + + Database file path + Chemin de la base de données + + + + prints list of registered databases + Imprimer la liste des bases de données enregistrées + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Imprimez la liste des bases de données enregistrées sous SQLiteStudio. Chaque base se données de la liste peut être ouverte ou close et %1 vous indique lesquellest.La base de données actuelle est aussi marquée dans la liste par « * » en début de nom. Voir l’aide la commande %2 pour en savoir plus sur la base de données actuelle. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + Aucune base de données de travail n’est activée. +Appelez la commande %1 pour activer la base de données active. +Appelez %2 pour voir la liste de toutes les bases de données. + + + + Database is not open. + La base de données n’est pas ouverte. + + + + Cannot find table named: %1 + Impossible de trouver le tableau nommé : %1 + + + + shows details about the table + Affichage des détails de la table + + + + table + Table + + + + Table: %1 + Tableau : %1 + + + + Column name + Nom de la colonne + + + + Data type + Type de données + + + + Constraints + Contraintes + + + + Virtual table: %1 + Tableau virtuel : %1 + + + + Construction arguments: + Arguments de construction : + + + + No construction arguments were passed for this virtual table. + Aucun argument de construction n'a été passé pour ce tableau virtuel. + + + + CliCommandDir + + + lists directories and files in current working directory + Listes des répertoires et fichiers dans le répertoire de travail + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + Ceci est très semblable à la commande « dir » de Windows et à la commande de « ls » de systèmes Unix. + +You pouvez utiliser les caractères de remplacement <pattern> npour filtrer la sortie. + + + + pattern + Modèle + + + + CliCommandExit + + + quits the application + Quitter l’application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quittez l’apllication. Le paramètrage est stocké dans la configuration et sera restauré au prochain lancement. + + + + CliCommandHelp + + + shows this help message + Affichagez l’aide du message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Utilisez %1 pour connaitre certaines commandes supportées par la ligne de commande (CLI) de SQLiteStudio. +Pour voir les commandes supportées, saississez %2 sana arguments. + +En utilisant le nom de <command>, vous ajouter le caractère spécial(« %3 »). + +Vous pouvez exécuter n’importe quelle commande avec l’option « --help » pour voir l’aide pour cette commande. C’est une alternative à : %1 <commande>. + + + + command + CLI command syntax + CLI syntax de commandes + + + + No such command: %1 + Aucune telle commande : %1 + + + + Type '%1' for list of available commands. + Saisissez « %1 » pour la liste des commandes valides. + + + + Usage: %1%2 + Utilisation : %1%2 + + + + Aliases: %1 + Pseudomynes : %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + L’historique actuel est limité à : %1 + + + + prints history or erases it + Imprimez l’historique ou supprimez le + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + Lorqu’aucun argument n’est passé,cette commande imprime l’histoirique. Chaque entrée est séparée par une ligne vide, permettant une lecture aisée. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + Nombre + + + + Console history erased. + Historique effacé. + + + + Invalid number: %1 + Nombre invalide : %1 + + + + History limit set to %1 + Historique limité à %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Résultats actuels du mode d’ impression : %1 + + + + Invalid results printing mode: %1 + Résultats invalides du mode d’ impression : %1 + + + + New results printing mode: %1 + Résultats actuels du mode d’ impression : %1 + + + + tells or changes the query results format + Modifie le format du résultat de la requête + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Représentation actuelle d’une chaine NULL : %1 + + + + tells or changes the NULL representation string + Modifiez la représentation d’une chaine NULL + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + Si on n’a passé aucun argument, c’est la représentation de valeur NULL actuelle qui est utilisée (ce qui est imprimé à la place de valeurs NULL dans des résultats de requête). Si on donne un argument, il sera utilisé comme une nouvelle chaine représentant NULL. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Impossible d’appeler %1 lorsque aucune base de données n’est sélectionnée. Spécifiez la base de données actuelle avec la commande %2 ou nommez la base de données %3. + + + + Could not add database %1 to list. + Impossible d’ajouter la base de données %1 à la liste. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + Le fichier %1 n’existe pas dans %2. Impossible d’ouvrir une base de données avec la commande %3. Pour créer une nouvelle base de données utilisez la commande %4. + + + + Database %1 has been open and set as the current working database. + La base de données %1 a été ouverte et sélectionnée comme base de données actuelle. + + + + opens database connection + Ouvre la connexion de la base de données + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Ouvre la connexion de la base de données. Si aucun argument n’est passé, alors la connexion est ouverte comme base de données actuelle (voir l’aide %1 pour plus de détails). Cependant si on a passé un argument il peut être le <name> d’une base de données enregistrée, ou cela peut être le <chemin> du fichier de base de données. Dans le deuxième cas, le <chemin> est enregistré dans la liste avec un nom généré mais seulement pendant la période de la session actuelle. Après la reprise de la l’application une telle base de données n’est pas rétablie dans la liste. + + + + name + CLI command syntax + Nom + + + + path + CLI command syntax + Chemin + + + + CliCommandPwd + + + prints the current working directory + Imprime le répertoire de travail actuel + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + C’est la même commande d’un système Unix « pwd » ou « cd » sans arguments de Windows. Ceci imprimele répertoire de travail courant. Vous pouvez changer le répertoire avec le commande %1 et avoir la liste des répertoire de travail avec la commande %2. + + + + CliCommandRemove + + + No such database: %1 + Aucune base de données : %1 + + + + Database removed: %1 + Base de données enlevée : %1 + + + + New current database set: + Nouvelle base de données actuelle : + + + + removes database from the list + Enleve la base de données de la liste + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Enlève la base de données <nom> de la liste des bases enregistrées.si la base de données n’est pas dans la liste (voir la commande %1), alors message d’erreur est imprimé sans aucunes autres lignes. + + + + name + CLI command syntax + Nom + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + Aucune base de données de travail n’est activée. +Appelez la commande %1 pour activer la base de données active. +Appelez %2 pour voir la liste de toutes les bases de données. + + + + Database is not open. + La base de données n’est pas ouverte. + + + + executes SQL query + Exécute la requête SQL + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + Cette commande est exécutée chaque fois vous saississez une requête SQL au prompt de commande. Il exécute la requête sur la base de données actuelle (voir l’aide %1 pour les détails). Il n’y a aucun sens dans l’exécution de cette commande explicitement. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + SQL + + + + + Too many columns to display in %1 mode. + Trop de colonnes à afficher avec le mode %1. + + + + Row %1 + Ligne %1 + + + + Query execution error: %1 + Erreur d’exécution de la requête : %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + Aucune base de données : %1. Utilisez %2pour voir la liste des base de données connues. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Impossible d’appeler %1 quand aucune base de données n’est active. Spécifiez la base de données active avec la commade %2 ou nommez la base de données avec %3. + + + + Database %1 is closed. + La base de données %1 est fermée. + + + + + Database + Base de données + + + + Table + Tableau + + + + prints list of tables in the database + Imprime la liste des tablesde la base de données + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Imprime la liste des tables de la <base de données> sélectionnée ou de la base de données actuelle. Notez que la <base de données> devrait être le nom enregistré de la base de données (voir %1). L’affichage de la liste inclus toutes les tables d’autres bases de données attachées à celle-ci. +Lorsque l’option « -s » est ajouté, les tables système sont aussi listées. + + + + database + CLI command syntax + Base de données + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + Aucune base de données actuelle n’est sélectionnée. Utilisez %1 pour en définir uneet lancez avec %2. + + + + Tables + Tableaux + + + + Views + Vues + + + + Columns + Colonnes + + + + Indexes + Index + + + + + Triggers + Déclancheurs + + + + prints all objects in the database as a tree + Imprime tous les objets de la base de données comme un arbre + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Imprime tous les objets (tables, index, déclencheurs et vues) qui sont dans la base de données comme un arbre. L’arbre est très semblable à celui que vous pouvez voir dans lGUI client de SQLiteStudio. +Quand on ajoute l’option -c, alors les colonnes seront aussi inscrites sous chaque table. +Quand on ajoute l’option -s, alors les objets de système seront aussi imprimés (sqlite_* tables, des index d’auto-incrément, etc). +L’argument de base de données est facultatif et si fourni, alors seulement la base de données indiquée sera imprimée. Ceci n’est pas un nom de base de données enregistré, mais au lieu de cela c’est un nom de base de données SQLite interne, comme « principal », « temporaire », ou n’importe quel nom de base de données attaché. Pour imprimer l’arbre pour d’autre base de données enregistrée, appelez %1 d’abord pour changer la base de données actuelleet utiliser la commande %2. + + + + CliCommandUse + + + No current database selected. + Aucune base de données active de sélectionnée. + + + + + Current database: %1 + Base de données actuelle : %1 + + + + No such database: %1 + Aucune base de données : %1 + + + + changes default working database + Change la base de données actelle par défaut + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changet la base de données actuelle <nom>. Si le <nom > de la base de données n’est pas enregistrée dans l’application, le message d’erreur est imprimé et aucun changement n’est fait. + +Quel est la base de données actuelle ? +Quand vous saississez une requête SQL à exécuter, celle-ci est exécutée sur la base de données par défaut, que l’on connaît aussi comme la base de données actuelle. La plupart de commandes concernant la base de données utilise la base de données de défaut d’utilisation, si on n’a fourni aucune base de données dans leurs arguments. La base de données actuelle est toujours identifiée par la ligne de commande. La base de données par défaut est toujours définie (à moins qu’il n’y ait aucune base de données dans la liste). + +La base de données par défaut peut être choisie de diverses manières : +- Utilisation de la commande %1, +- En passant nom de fichier de base de données aux paramètres de démarrage d’application, +- En passantle nom la base de données enregistrée aux paramètres de démarrage d’application, +- En restaurant la base de données par défaut précédemment choisie dans la configuration sauvée, +- Ou quand la base de données par défaut n’a été choisie par aucun du susdit, l’alors première base de données de la liste de bases de données enregistrée devient le par défaut. + + + + name + CLI command syntax + Nom + + + + QObject + + + Insufficient number of arguments. + Nombre d’arguments insuffisant. + + + + Too many arguments. + Trop d’arguements. + + + + Invalid argument value: %1. +Expected one of: %2 + Valeur invalide de l’arguement %1. Excepté l’un d’eux : %2 + + + + Unknown option: %1 + CLI command syntax + Option %1 inconnue + + + + Option %1 requires an argument. + CLI command syntax + L’option %1 nécessite un argument. + + + + string + CLI command syntax + Chaîne + + + + Command line interface to SQLiteStudio, a SQLite manager. + Interface de ligne de commandes de SQLiteStudio, SQLite manager. + + + + Enables debug messages on standard error output. + Messages de débogage valides sur sortie d’erreur standard. + + + + Enables Lemon parser debug messages for SQL code assistant. + Permet le débogage avec l’analyseur syntaxique de Lemon pour l’assistant SQL. + + + + Lists plugins installed in the SQLiteStudio and quits. + Liste les plugins installés dans SQLiteStudio et quitte. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + Fichier SQL + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Encodage de caractères à utiliser lors de la lecture de fichier SQL (-e option). Utilisez -cl pour lister les codecs disponibles. Par défaut, %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Liste les codecs disponibles à utiliser avec l'option -c et quitte. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + Lorsqu'elle est utilisée avec l'option -e, l'exécution ne s'arrêtera pas sur une erreur, mais se poursuivra jusqu'à la fin, ignorant les erreurs. + + + + file + Fichier + + + + Database file to open + Base de données à ouvrir + + + + Invalid codec: %1. Use -cl option to list available codecs. + Codec invalide : %1. Utilisez l'option -cl pour lister les codecs disponibles. + + + + Database file argument is mandatory when executing SQL file. + L'argument du fichier de la base de données est obligatoire lors de l'exécution du fichier SQL. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Impossible d'ouvrir la base de données spécifiée pour l'exécution de fichier SQL. Vous devriez essayer d'utiliser l'option -d pour trouver plus de détails. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_he_IL.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_he_IL.ts new file mode 100644 index 0000000..f0e145e --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_he_IL.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + מסד × ×ª×•× ×™× × ×•×›×—×™: %1 + + + + No current working database is set. + ×œ× × ×§×‘×¢ מסד × ×ª×•× ×™× × ×•×›×—×™ פעיל. + + + + Type %1 for help + הזנת %1 לעזרה + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + מסד ×”× ×ª×•× ×™× ×©×”×•×¢×‘×¨ ×‘×¤×¨×ž×˜×¨×™× ×©×œ שורת הפקודה (%1) כבר ×”×™×” ברשימה בש×: %2 + + + + Could not add database %1 to list. + ×œ× × ×™×ª×Ÿ להוסיף מסד × ×ª×•× ×™× %1 לרשימה. + + + + closed + סגור + + + + CliCommand + + + Usage: %1%2 + שימוש: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + ×œ× × ×™×ª×Ÿ להוסיף מסד × ×ª×•× ×™× %1 לרשימה. + + + + Database added: %1 + התווסף מסד נתוני×: %1 + + + + adds new database to the list + הוספת מסד × ×ª×•× ×™× ×—×“×© לרשימה + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + הוספת מסד × ×ª×•× ×™× × ×ª×•×Ÿ מ <path> ×‘×©× <name> לרשימת מסדי הנתוני×. ×”×©× <name> ×”×•× ×©× ×¡×ž×œ×™ ×ליו ניתן ×™×”×™×” להתייחס מ×וחר יותר. × × ×œ×‘×—×•×¨ בכל ×©× ×™×™×—×•×“×™. לרשימת מסדי × ×ª×•× ×™× ×”× ×ž×¦××™× ×›×‘×¨ ברשימה, × × ×œ×”×©×ª×ž×© בפקודה%1. + + + + name + CLI command syntax + ×©× + + + + path + CLI command syntax + נתיב + + + + CliCommandCd + + + Changed directory to: %1 + מחיצה שונתה ל: %1 + + + + Could not change directory to: %1 + ×œ× × ×™×ª×Ÿ לשנות מחיצה ל: %1 + + + + changes current working directory + שינוי מחיצת עבודה נוכחית + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + פקודה דומה מ×וד ל 'cd' המוכרת ממערכות יוניקס וחלונות. דורש <path> להעברת משתנה בלתי תלוי, לכן קרי××” ל-%1 ×ª×’×¨×•× ×ª×ž×™×“ לשינוי מחיצה. למצי×ת 'מחיצת העבודה הנוכחית, ניתן להשתמש בפקודה %2 ולהצגת תוכן מחיצת העבודה הנוכחית ניתן להשתמש בפקודה %3. + + + + path + CLI command syntax + נתיב + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + קישור למסד × ×ª×•× ×™× %1 נותק. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + סגירת מסד × ×ª×•× ×™× × ×ª×•×Ÿ (×ו נוכחי) + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + ×©× + + + + CliCommandDbList + + + No current working database defined. + ×œ× ×”×•×’×“×¨ מסד × ×ª×•× ×™× × ×•×›×—×™ פעיל. + + + + Databases: + מסד נתוני×: + + + + + Name + CLI db name column + ×©× + + + + + Open + CLI connection state column + פתיחה + + + + + Closed + CLI connection state column + סגירה + + + + + Connection + CLI connection state column + חיבור + + + + + Database file path + נתיב מסד ×”× ×ª×•× ×™× + + + + prints list of registered databases + הדפסת רשימה של מסדי × ×ª×•× ×™× ×¨×©×•×ž×™× + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + מסד ×”× ×ª×•× ×™× ×ינו פתוח. + + + + Cannot find table named: %1 + ×œ× × ×™×ª×Ÿ ×œ×ž×¦×•× ×˜×‘×œ×” בש×: %1 + + + + shows details about the table + הצגת ×¤×¨×˜×™× ×ודות הטבלה + + + + table + טבלה + + + + Table: %1 + טבלה: %1 + + + + Column name + ×©× ×¢×ž×•×“×” + + + + Data type + סוג × ×ª×•× ×™× + + + + Constraints + ××™×œ×•×¦×™× + + + + Virtual table: %1 + טבלה מדומה: %1 + + + + Construction arguments: + ×ž×©×ª× ×™× ×‘×œ×ª×™ ×ª×œ×•×™×™× ×ž×‘× ×™×™×: + + + + No construction arguments were passed for this virtual table. + ×œ× ×”×•×¢×‘×¨×• ×ž×©×ª× ×™× ×‘×œ×ª×™ ×ª×œ×•×™×™× ×ž×‘× ×™×™× ×œ×˜×‘×œ×” מדומה זו. + + + + CliCommandDir + + + lists directories and files in current working directory + הצגת מחיצות ×•×§×‘×¦×™× ×‘×ž×—×™×¦×ª העבודה הנוכחית + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + דפוס + + + + CliCommandExit + + + quits the application + יצי××” מהישומון + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + יצי××” מהיישומון. ההגדרות נשמרות בקובץ התצורה וישוחזרו בהפעלה הב××”. + + + + CliCommandHelp + + + shows this help message + הצגת הודעת עזרה זו + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + פקודה + + + + No such command: %1 + ×œ× ×§×™×™×ž×ª פקודה: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + שימוש: %1%2 + + + + Aliases: %1 + כינויי×: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + מגבלות היסטוריה נוכחית הוגדרה ל: %1 + + + + prints history or erases it + מציג היסטוריה ×ו מנקה ×ותה + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + מספר + + + + Console history erased. + היסטוריית המסוף נמחקה. + + + + Invalid number: %1 + מספר שגוי: %1 + + + + History limit set to %1 + מגבלות היסטוריה הוגדרו ל %1 + + + + CliCommandMode + + + Current results printing mode: %1 + מצב הדפסת תוצ×ות נוכחי:%1 + + + + Invalid results printing mode: %1 + מצב הדפסת תוצ×ות שגוי:%1 + + + + New results printing mode: %1 + מצב הדפסת תוצ×ות חדש:%1 + + + + tells or changes the query results format + מורה ×ו משנה ×ת תבנית תוצ×ות הש×ילתה + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + ×œ× × ×™×ª×Ÿ להוסיף מסד × ×ª×•× ×™× %1 לרשימה. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + פתיחת קישור למסד × ×ª×•× ×™× + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + פתיחת קישור למסד הנתוני×. ×× ×œ× ×”×•×¢×‘×¨×• ×ž×©×ª× ×™× ×‘×œ×ª×™ ×ª×œ×•×™×™× × ×•×¡×¤×™×, יתבצע קישור למסד × ×ª×•× ×™× ×‘×¨×™×¨×ª המחדל הנוכחי (×œ×¤×¨×˜×™× × ×•×¡×¤×™× ×•×œ×¢×–×¨×” %1). במידע והועבר משתנה בלתי תלוי, מסד ×”× ×ª×•× ×™× ×”×¨×©×•× <name> לפתיחה, ×ו <path> לקובץ מסד ×”× ×ª×•× ×™× ×œ×¤×ª×™×—×”. במקרה השני, הנתיב <path> יתווסף לרשימה בצרוף ×œ×©× ×©×™×—×•×œ×œ, ×ך רק למשך הפעולה הנוכחית של היישו×. ל×חר ×תחול ×”×™×™×©×•× ×ž×¡×“ × ×ª×•× ×™× ×–×” ×œ× ×™×©×ž×¨ ברשימה. + + + + name + CLI command syntax + ×©× + + + + path + CLI command syntax + נתיב + + + + CliCommandPwd + + + prints the current working directory + הדפסת מחיצת עבודה נוכחית + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + ×–×”×” לפקודה 'pwd' במערכות יוניקס ולפקודת 'cd' ×œ×œ× ×ž×©×ª× ×” בלתי תלוי בחלונות. הפקודה תציג על המסך ×ת מחיצת העבודה הנוכחית. ניתן לשנות ×ת ספריית העבודה הנוכחית ב×מצעות הפקודה%1 וניתן ×’× ×œ×”×¦×™×’ רשימה של תוכן מחיצת העבודה הנוכחית ב×מצעות הפקודה%2. + + + + CliCommandRemove + + + No such database: %1 + ×œ× ×§×™×™× ×ž×¡×“ נתוני×: %1 + + + + Database removed: %1 + מסד × ×ª×•× ×™× ×”×•×¡×¨: %1 + + + + New current database set: + הגדרת מסד × ×ª×•× ×™× × ×•×›×—×™ חדש: + + + + removes database from the list + הסרת מסד × ×ª×•× ×™× ×ž×”×¨×©×™×ž×” + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + ×©× + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + מספד ×”× ×ª×•× ×™× ×ינו פתוח. + + + + executes SQL query + הרצת ש×ילתת SQL + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + עמודות רבות מדי למצב תצוגה %1. + + + + Row %1 + שורה %1 + + + + Query execution error: %1 + שגי×ת ריצת ש×ילתה: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + מסד × ×ª×•× ×™× %1 סגור. + + + + + Database + מסד × ×ª×•× ×™× + + + + Table + טבלה + + + + prints list of tables in the database + הדפסת כל הטבל×ות במסד ×”× ×ª×•× ×™× + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + מסד × ×ª×•× ×™× + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + ×œ× × ×‘×—×¨ מסד × ×ª×•× ×™× × ×•×›×—×™ פעיל. × × ×”×©×ª×ž×© ב־%1 להגדרת ×חד, ל×חר מכן להריץ ×ת %2. + + + + Tables + טבל×ות + + + + Views + ×ž×¦×’×™× + + + + Columns + עמודות + + + + Indexes + ×žÖ´×¤Ö°×ªÖ¼Öµ×—Ö·×™× + + + + + Triggers + ×žÖ·×–Ö°× Öµ×§×™× + + + + prints all objects in the database as a tree + הדפסת כל עצמי מסד ×”× ×ª×•× ×™× ×›×¢×¥ + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + ×œ× × ×‘×—×¨ מסד × ×ª×•× ×™× × ×•×›×—×™. + + + + + Current database: %1 + מסד × ×ª×•× ×™× × ×•×›×—×™: %1 + + + + No such database: %1 + ×œ× ×§×™×™× ×ž×¡×“ נתוני×: %1 + + + + changes default working database + החלפת מסד × ×ª×•× ×™× ×¤×¢×™×œ ברירת מחדל + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + ×©× + + + + QObject + + + Insufficient number of arguments. + מספר ×ž×©×ª× ×™× ×‘×œ×ª×™ ×ª×œ×•×™×™× ×œ× ×ž×¡×¤×§×™×. + + + + Too many arguments. + ×ž×©×ª× ×™× ×‘×œ×ª×™ ×ª×œ×•×™×™× ×¨×‘×™× ×ž×™×“×™. + + + + Invalid argument value: %1. +Expected one of: %2 + משתנה בלתי תלוי שגוי: %1. +המערכת ציפתה ל×חד מ: %2 + + + + Unknown option: %1 + CLI command syntax + ×פשרות ×œ× ×™×“×•×¢×”: %1 + + + + Option %1 requires an argument. + CLI command syntax + ×פשרות %1 דורשת משתנה בלתי תלוי. + + + + string + CLI command syntax + מחרוזת + + + + Command line interface to SQLiteStudio, a SQLite manager. + מנשק משתמש שורת־פקודה SQLiteStudio, תכנת ניהול SQLite. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + ×פשור הודעות ניפוי ×ª×§×œ×™× × ×™×ª×•×— תחביר Lemon לסייען קוד SQL. + + + + Lists plugins installed in the SQLiteStudio and quits. + הצגת רשימת ×ž×ª×§×¢×™× ×ž×•×ª×§× ×™× ×‘Ö¾SQLiteStudio ויצי××”. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + קובץ + + + + Database file to open + קובץ מסד × ×ª×•× ×™× ×œ×¤×ª×™×—×” + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_hu_HU.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_hu_HU.ts new file mode 100644 index 0000000..ec507d6 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_hu_HU.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it.qm b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it.qm deleted file mode 100644 index 9dad8df..0000000 Binary files a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it.ts deleted file mode 100644 index ede41bc..0000000 --- a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it.ts +++ /dev/null @@ -1,788 +0,0 @@ - - - - - CLI - - - Current database: %1 - - - - - No current working database is set. - - - - - Type %1 for help - - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - - - - - Could not add database %1 to list. - - - - - closed - - - - - CliCommand - - - Usage: %1%2 - - - - - CliCommandAdd - - - Could not add database %1 to list. - - - - - Database added: %1 - - - - - adds new database to the list - - - - - Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. - - - - - name - CLI command syntax - - - - - path - CLI command syntax - - - - - CliCommandCd - - - Changed directory to: %1 - - - - - Could not change directory to: %1 - - - - - changes current working directory - - - - - Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - - - - - path - CLI command syntax - - - - - CliCommandClose - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - - Connection to database %1 closed. - - - - - No such database: %1. Use %2 to see list of known databases. - - - - - closes given (or current) database - - - - - Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). - - - - - name - CLI command syntax - - - - - CliCommandDbList - - - No current working database defined. - - - - - Databases: - - - - - - Name - CLI db name column - - - - - - Open - CLI connection state column - - - - - - Closed - CLI connection state column - - - - - - Connection - CLI connection state column - - - - - - Database file path - - - - - prints list of registered databases - - - - - Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - - - - - CliCommandDesc - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - - - - - Database is not open. - - - - - Cannot find table named: %1 - - - - - shows details about the table - - - - - table - - - - - Table: %1 - - - - - Column name - - - - - Data type - - - - - Constraints - - - - - Virtual table: %1 - - - - - Construction arguments: - - - - - No construction arguments were passed for this virtual table. - - - - - CliCommandDir - - - lists directories and files in current working directory - - - - - This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. - -You can pass <pattern> with wildcard characters to filter output. - - - - - pattern - - - - - CliCommandExit - - - quits the application - - - - - Quits the application. Settings are stored in configuration file and will be restored on next startup. - - - - - CliCommandHelp - - - shows this help message - - - - - Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. -To see list of supported commands, type %2 without any arguments. - -When passing <command> name, you can skip special prefix character ('%3'). - -You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. - - - - - command - CLI command syntax - - - - - No such command: %1 - - - - - Type '%1' for list of available commands. - - - - - Usage: %1%2 - - - - - Aliases: %1 - - - - - CliCommandHistory - - - Current history limit is set to: %1 - - - - - prints history or erases it - - - - - When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. - -When the -c or --clear option is passed, then the history gets erased. -When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. -Use -ql or --querylimit option to see the current limit value. - - - - - number - - - - - Console history erased. - - - - - Invalid number: %1 - - - - - History limit set to %1 - - - - - CliCommandMode - - - Current results printing mode: %1 - - - - - Invalid results printing mode: %1 - - - - - New results printing mode: %1 - - - - - tells or changes the query results format - - - - - When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: -- CLASSIC - columns are separated by a comma, not aligned, -- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, -- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), -- ROW - each column from the row is displayed in new line, so the full data is displayed. - -The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. - -The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. - -The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. -ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. - -The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - - - - - CliCommandNullValue - - - Current NULL representation string: %1 - - - - - tells or changes the NULL representation string - - - - - If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. - - - - - CliCommandOpen - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - Could not add database %1 to list. - - - - - File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. - - - - - Database %1 has been open and set as the current working database. - - - - - opens database connection - - - - - Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. - - - - - name - CLI command syntax - - - - - path - CLI command syntax - - - - - CliCommandPwd - - - prints the current working directory - - - - - This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. - - - - - CliCommandRemove - - - No such database: %1 - - - - - Database removed: %1 - - - - - New current database set: - - - - - removes database from the list - - - - - Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. - - - - - name - CLI command syntax - - - - - CliCommandSql - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - - - - - Database is not open. - - - - - executes SQL query - - - - - This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. - - - - - sql - CLI command syntax - - - - - - Too many columns to display in %1 mode. - - - - - Row %1 - - - - - Query execution error: %1 - - - - - CliCommandTables - - - No such database: %1. Use %2 to see list of known databases. - - - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - Database %1 is closed. - - - - - - Database - - - - - Table - - - - - prints list of tables in the database - - - - - Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. -When the -s option is given, then system tables are also listed. - - - - - database - CLI command syntax - - - - - CliCommandTree - - - No current working database is selected. Use %1 to define one and then run %2. - - - - - Tables - - - - - Views - - - - - Columns - - - - - Indexes - - - - - - Triggers - - - - - prints all objects in the database as a tree - - - - - Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. -When -c option is given, then also columns will be listed under each table. -When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). -The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. - - - - - CliCommandUse - - - No current database selected. - - - - - - Current database: %1 - - - - - No such database: %1 - - - - - changes default working database - - - - - Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. - -What is current working database? -When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). - -The default database can be selected in various ways: -- using %1 command, -- by passing database file name to the application startup parameters, -- by passing registered database name to the application startup parameters, -- by restoring previously selected default database from saved configuration, -- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. - - - - - name - CLI command syntax - - - - - QObject - - - Insufficient number of arguments. - - - - - Too many arguments. - - - - - Invalid argument value: %1. -Expected one of: %2 - - - - - Unknown option: %1 - CLI command syntax - - - - - Option %1 requires an argument. - CLI command syntax - - - - - string - CLI command syntax - - - - - Command line interface to SQLiteStudio, a SQLite manager. - - - - - Enables debug messages on standard error output. - - - - - Enables Lemon parser debug messages for SQL code assistant. - - - - - Lists plugins installed in the SQLiteStudio and quits. - - - - - file - - - - - Database file to open - - - - diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it_IT.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it_IT.ts new file mode 100644 index 0000000..3400d5a --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_it_IT.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Database attuale: %1 + + + + No current working database is set. + Nessun database di lavoro è stato impostato. + + + + Type %1 for help + Digita %1 per aiuto + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Il database passato nei parametri della riga di comando (%1) era già nell'elenco sotto nome: %2 + + + + Could not add database %1 to list. + Impossibile aggiungere il database %1 all'elenco. + + + + closed + chiuso + + + + CliCommand + + + Usage: %1%2 + Uso: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Impossibile aggiungere il database %1 all'elenco. + + + + Database added: %1 + Database aggiunto: %1 + + + + adds new database to the list + aggiunge un nuovo database alla lista + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Aggiunge il database dato puntato da <path> con dato <name> per elencare l'elenco dei database. Il <name> è solo un nome simbolico a cui puoi fare riferimento in seguito. Basta scegliere un nome univoco. Per l'elenco dei database già presenti nella lista usa il comando %1. + + + + name + CLI command syntax + nome + + + + path + CLI command syntax + percorso + + + + CliCommandCd + + + Changed directory to: %1 + Cartella modificata in: %1 + + + + Could not change directory to: %1 + Impossibile cambiare la directory in: %1 + + + + changes current working directory + cambia la directory di lavoro corrente + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Comando molto simile a 'cd' conosciuto da sistemi Unix e Windows. Richiede un argomento <path> da passare, quindi chiamare %1 causerà sempre un cambiamento della directory. Per imparare quale è la directory di lavoro corrente usa il comando %2 e per elencare i contenuti della directory di lavoro corrente usa il comando %3. + + + + path + CLI command syntax + percorso + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Impossibile chiamare %1 quando nessun database è impostato per essere corrente. Specificare il database corrente con il comando %2 o passare il nome del database a %3. + + + + + Connection to database %1 closed. + Connessione al database %1 chiusa. + + + + No such database: %1. Use %2 to see list of known databases. + Database inesistente: %1. Usa %2 per vedere l'elenco dei database conosciuti. + + + + closes given (or current) database + chiude il database specificato (o corrente) + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Chiude la connessione al database. Se il database era già chiuso, non succede nulla. Se il <name> è specificato, dovrebbe essere il nome del database da chiudere (come stampato dal comando %1). Poi se il <nome> non viene specificato, il database di lavoro corrente è chiuso (vedi aiuto per %2 per i dettagli). + + + + name + CLI command syntax + nome + + + + CliCommandDbList + + + No current working database defined. + Nessun database di lavoro è stato impostato. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Nome + + + + + Open + CLI connection state column + Apri + + + + + Closed + CLI connection state column + Chiuso + + + + + Connection + CLI connection state column + Connessione + + + + + Database file path + Percorso file database + + + + prints list of registered databases + stampa l'elenco dei database registrati + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Stampa la lista dei database registrati in SQLiteStudio. Ogni database nella lista può essere in stato di aperto o chiuso e %1 te lo dice. Anche il database di lavoro corrente (aka database predefinito) è contrassegnato nella lista con '*' all'inizio del suo nome. Vedi la guida per il comando %2 per conoscere il database predefinito. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + Non è stato impostato alcun database funzionante. +Esegui il comando %1 per impostare il database di lavoro. +Esegui %2 per vedere l'elenco di tutti i database. + + + + Database is not open. + Il database non è aperto. + + + + Cannot find table named: %1 + Impossibile trovare la tabella denominata: %1 + + + + shows details about the table + mostra i dettagli della tabella + + + + table + tabella + + + + Table: %1 + Tabella: %1 + + + + Column name + Nome colonna + + + + Data type + Tipo di dati + + + + Constraints + Vincoli + + + + Virtual table: %1 + Tabella virtuale: %1 + + + + Construction arguments: + Argomenti di costruzione: + + + + No construction arguments were passed for this virtual table. + Nessun argomento di costruzione è stato passato per questa tabella virtuale. + + + + CliCommandDir + + + lists directories and files in current working directory + elenca le directory e i file nella directory di lavoro corrente + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + Questo è molto simile a 'dir' comando conosciuto da Windows e 'ls' comando da sistemi Unix. + +Puoi passare <pattern> con caratteri jolly per filtrare l'output. + + + + pattern + modello + + + + CliCommandExit + + + quits the application + chiude l'applicazione + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Esce dall'applicazione. Le impostazioni sono memorizzate nel file di configurazione e saranno ripristinate al prossimo avvio. + + + + CliCommandHelp + + + shows this help message + mostra questo messaggio di aiuto + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Usa %1 per conoscere alcuni comandi supportati dall'interfaccia a linea di comando (CLI) di Sqlitestudio. +Per vedere l'elenco dei comandi supportati, digita %2 senza alcun argomento. + +Quando passi il nome <command>, puoi non scrivere il carattere prefisso speciale ('%3'). + +Puoi sempre eseguire qualsiasi comando con l'opzione'--help' per vedere l'aiuto per quel comando. E' una alternativa al dover scrivere: %1 <command>. + + + + command + CLI command syntax + comando + + + + No such command: %1 + Comando inesistente: %1 + + + + Type '%1' for list of available commands. + Digita '%1' per l'elenco dei comandi disponibili. + + + + Usage: %1%2 + Uso: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Il limite di cronologia corrente è impostato a: %1 + + + + prints history or erases it + stampa la cronologia o la cancella + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + Quando non è stato passato alcun argomento, questo comando stampa la cronologia della riga di comando. Ogni voce della cronologia è separata da una linea orizzontale, quindi le voci multilinea sono più facili da leggere. + +Quando viene passata l'opzione -c o --clear, la cronologia viene cancellata. +Quando l'opzione -l o --limit viene passata, imposta il limite delle nuove voci della cronologia. Richiede un ulteriore argomento che dice a quante voci vuoi che la cronologia sia limitata. +Usa l'opzione -ql o --querylimit per vedere il valore limite corrente. + + + + number + numero + + + + Console history erased. + Cronologia delle console cancellata. + + + + Invalid number: %1 + Numero non valido: %1 + + + + History limit set to %1 + Limite di cronologia impostato a %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Modalità di stampa dei risultati attuali: %1 + + + + Invalid results printing mode: %1 + Modalità di stampa risultati non valida: %1 + + + + New results printing mode: %1 + Nuova modalità di stampa risultati: %1 + + + + tells or changes the query results format + dice o cambia il formato dei risultati della query + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + Quando viene chiamato senza argomento, indica il formato di output corrente per i risultati di una interrogazione. Quando il <mode> viene passato, la modalità viene cambiata in quella specificata. Le modalità supportate sono: +- CLASSIC - le colonne sono separate da una virgola, non allineate, +- FIXED - le colonne hanno larghezza uguale e fissa, si adattano sempre alla larghezza della finestra del terminale, ma i dati in colonne possono essere tagliati, +- COLUMNS - come FIXED, ma più intelligente (non usare con enormi set di risultati, vedere i dettagli qui sotto), +- ROW - ogni colonna della riga viene visualizzata in una nuova riga, quindi vengono visualizzati i dati completi. + +La modalità CLASSIC è consigliata se si desidera visualizzare tutti i dati, ma non si vuole sprecare le linee per ogni colonna. Ogni riga visualizzerà i dati completi per ogni colonna, ma ciò significa anche che le colonne non saranno allineate l'una all'altra nelle righe successive. La modalità CLASSIC inoltre non rispetta la larghezza della finestra terminale (console), quindi se i valori nelle colonne sono più larghi della finestra, la riga verrà continuata nelle righe successive. + +La modalità FIXED è consigliata se si desidera un output leggibile e se si ha cura dei valori di dati lunghi. Le colonne saranno allineate, rendendo l'output come bella tabella. La larghezza delle colonne è calcolata a partire dalla larghezza della finestra della console e da un numero di colonne. + +La modalità COLUMNS è simile alla modalità FIXED, ma cerca di essere intelligente e di rendere più strette le colonne con valori più corti, mentre le colonne con valori più lunghi avranno più spazio. Le prime ad essere rimpicciolite sono le colonne con le intestazioni più lunghe (quindi i nomi delle intestazioni vengono tagliati per primi), poi vengono rimpicciolite le colonne con i valori più lunghi, fino al momento in cui tutte le colonne entrano nella finestra del terminale. +ATTENZIONE! La modalità COLONNE legge tutti i risultati della query in una volta sola per valutare la larghezza delle colonne, quindi è pericoloso usarla quando si lavora con insiemi di risultati enormi. Tenere presente che questa modalità carica l'intero insieme di risultati in memoria. + +La modalità ROW è consigliata se si ha bisogno di vedere i valori interi e non ci si aspetta che vengano visualizzate molte righe, perché questa modalità visualizza una riga di output per ogni colonna, quindi si otterranno 10 righe per singola riga con 10 colonne, quindi se si hanno 10 righe di questo tipo, si otterranno 100 righe di output (+1 riga in più per ogni riga, per separare le righe l'una dall'altra). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Stringa attuale per la rappresentazione del NULL: %1 + + + + tells or changes the NULL representation string + mostra o cambia la stringa di rappresentazione NULL + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + Se non è stato passato nessun argomento, indica quale è la rappresentazione attuale del valore NULL (cioè ciò che è stampato al posto dei valori NULL nei risultati della query). Se l'argomento è passato, allora viene usato come nuova stringa da usarsi per la rappresentazione NULL. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Impossibile chiamare %1 quando nessun database è impostato per essere corrente. Specificare il database corrente con il comando %2 o passare il nome del database a %3. + + + + Could not add database %1 to list. + Impossibile aggiungere il database %1 all'elenco. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + Il file %1 non esiste in %2. Impossibile aprire il database inesistente con il comando %3. Per creare un nuovo database, usa il comando %4. + + + + Database %1 has been open and set as the current working database. + Il database %1 è stato aperto e impostato come database di lavoro corrente. + + + + opens database connection + apre la connessione al database + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Apre la connessione al database. Se non è stato passato alcun argomento aggiuntivo, allora la connessione è aperta al database predefinito corrente (vedi aiuto per %1 per i dettagli). Tuttavia, se un argomento è stato passato, può essere il <name> del database registrato da aprire, o può essere il <path> al file del database da aprire. Nel secondo caso, il <path> viene registrato nella lista con un nome generato, ma solo per il periodo della sessione attuale dell'applicazione. Dopo aver riavviato l'applicazione, tale database non viene ripristinato nell'elenco. + + + + name + CLI command syntax + nome + + + + path + CLI command syntax + percorso + + + + CliCommandPwd + + + prints the current working directory + stampa la directory di lavoro corrente + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + Questo è lo stesso comando 'pwd' su sistemi Unix e 'cd' senza argomenti su Windows. Stampa la directory di lavoro corrente. È possibile modificare la directory di lavoro corrente con il comando %1 ed è anche possibile elencare i contenuti della directory di lavoro corrente con il comando %2. + + + + CliCommandRemove + + + No such database: %1 + Database inesistente: %1 + + + + Database removed: %1 + Database rimosso: %1 + + + + New current database set: + Nuovo database corrente impostato: + + + + removes database from the list + rimuove il database dalla lista + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Rimuove il database <name> dall'elenco dei database registrati. Se il database non era nella lista (vedere il comando %1), un messaggio di errore viene stampato e nessuna operazione verrà eseguita. + + + + name + CLI command syntax + nome + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + Non è stato impostato alcun database funzionante. +Esegui il comando %1 per impostare il database di lavoro. +Esegui %2 per vedere l'elenco di tutti i database. + + + + Database is not open. + Il database non è aperto. + + + + executes SQL query + esegue query SQL + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + Questo comando viene eseguito ogni volta che si inserisce la query SQL nel prompt dei comandi. Esegue la query nel database corrente di lavoro (consultare la guida per %1 per i dettagli). Non ha senso eseguire questo comando esplicitamente. Invece basta digitare la query SQL nel prompt dei comandi, senza alcun prefisso dei comandi. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Troppe colonne da visualizzare in modalità %1. + + + + Row %1 + Riga %1 + + + + Query execution error: %1 + Errore di esecuzione query: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + Database inesistente: %1. Usa %2 per vedere l'elenco dei database conosciuti. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Impossibile chiamare %1 quando nessun database è impostato per essere corrente. Specificare il database corrente con %2 comando o passare il nome del database a %3. + + + + Database %1 is closed. + Il database %1 è chiuso. + + + + + Database + Database + + + + Table + Tabella + + + + prints list of tables in the database + stampa l'elenco delle tabelle nel database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Stampa l'elenco delle tabelle nel <database> specificato o nel database di lavoro corrente. Nota, che il <database> dovrebbe essere il nome del database registrato (vedi %1). L'elenco di output include tutte le tabelle di qualsiasi altro database allegato al database interrogato. +Quando viene fornita l'opzione -s, vengono elencate anche le tabelle di sistema. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + Non è stato selezionato alcun database di lavoro corrente. Utilizzare %1 per definirne uno e quindi eseguire %2. + + + + Tables + Tabelle + + + + Views + Viste + + + + Columns + Colonne + + + + Indexes + Indici + + + + + Triggers + Trigger + + + + prints all objects in the database as a tree + stampa tutti gli oggetti nel database ad albero + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Stampa tutti gli oggetti (tabelle, indici, trigger e viste) che si trovano nel database come albero. L'albero è molto simile a quello che si può vedere nel client GUI di SQLiteStudio. +Quando viene fornita l'opzione -c, anche le colonne saranno elencate sotto ogni tabella. +Quando viene fornita l'opzione -s, anche gli oggetti di sistema verranno stampati (tabelle sqlite_*, indici di incremento automatico, ecc). +L'argomento database è opzionale e, se fornito, verrà stampato solo il database specificato. Questo non è un nome di database registrato, ma invece è un nome di database SQLite interno, come 'main', 'temp' o qualsiasi nome allegato al database. Per stampare un albero per altri database registrati, chiama %1 prima per cambiare il database di lavoro e poi usa il comando %2. + + + + CliCommandUse + + + No current database selected. + Nessun database corrente selezionato. + + + + + Current database: %1 + Database attuale: %1 + + + + No such database: %1 + Database inesistente: %1 + + + + changes default working database + modifica il database di lavoro predefinito + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Cambia il database di lavoro corrente in <name>. Se il database <name> non è registrato nell'applicazione, il messaggio di errore viene stampato e non viene apportata alcuna modifica. + +Cos'è il database di lavoro attuale? +Quando digiti una query SQL da eseguire, viene eseguita nel database predefinito, che è noto anche come database di lavoro corrente. La maggior parte dei comandi correlati al database può funzionare anche usando il database predefinito, se non è stato fornito alcun database nei loro argomenti. Il database corrente è sempre identificato dal prompt a riga di comando. Il database predefinito è sempre definito (a meno che non ci sia alcun database nella lista). + +Il database predefinito può essere selezionato in vari modi: +- usando il comando %1, +- passando il nome del file di database ai parametri di avvio dell'applicazione, +- passando il nome del database registrato ai parametri di avvio dell'applicazione, +- ripristinando il database predefinito precedentemente selezionato dalla configurazione salvata, +- o quando il database predefinito non è stato selezionato da nessuno dei precedenti, allora il primo database dell'elenco dei database registrati diventa quello predefinito. + + + + name + CLI command syntax + nome + + + + QObject + + + Insufficient number of arguments. + Numero insufficiente di argomenti. + + + + Too many arguments. + Troppi argomenti. + + + + Invalid argument value: %1. +Expected one of: %2 + Valore argomento non valido: %1. +Atteso uno di: %2 + + + + Unknown option: %1 + CLI command syntax + Opzione sconosciuta: %1 + + + + Option %1 requires an argument. + CLI command syntax + L'opzione %1 richiede un argomento. + + + + string + CLI command syntax + stringa + + + + Command line interface to SQLiteStudio, a SQLite manager. + Interfaccia a riga di comando per SQLiteStudio, un gestore SQLite. + + + + Enables debug messages on standard error output. + Abilita i messaggi di debug sullo standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Abilita i messaggi di debug dell'analizzatore Lemon per l'assistente di codice SQL. + + + + Lists plugins installed in the SQLiteStudio and quits. + Elenca i plugin installati in SQLiteStudio ed esce. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Esegue il file SQL fornito (incluse tutte le ricche caratteristiche dell'esecutore delle query di SQLiteStudio) sul file di database specificato e termina. Il parametro del database diventa obbligatorio se viene utilizzata questa opzione. + + + + SQL file + File SQL + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Codifica dei caratteri da usare quando si legge il file SQL (opzione -e). Usare -cl per elencare le codifiche disponibili. Predefinito a %1. + + + + codec + codifica + + + + Lists available codecs to be used with -c option and quits. + Elenca le codifiche disponibili da utilizzare con l'opzione -c ed esce. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + Quando utilizzato insieme all'opzione -e, l'esecuzione non si fermerà su un errore, ma piuttosto continuerà fino alla fine, ignorando gli errori. + + + + file + file + + + + Database file to open + File del database da aprire + + + + Invalid codec: %1. Use -cl option to list available codecs. + Codifica non valida: %1. Usa l'opzione -cl per elencare le codifiche disponibili. + + + + Database file argument is mandatory when executing SQL file. + L'argomento del file database è obbligatorio quando si esegue il file SQL. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Impossibile aprire il database specificato per eseguire il file SQL. Puoi provare a usare l'opzione -d per scoprire maggiori dettagli. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ja_JP.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ja_JP.ts new file mode 100644 index 0000000..ccd02e7 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ja_JP.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + ç¾åœ¨ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹: %1 + + + + No current working database is set. + ç¾åœ¨ã®ä½œæ¥­ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãŒè¨­å®šã•れã¦ã„ã¾ã›ã‚“。 + + + + Type %1 for help + %1ã¨å…¥åŠ›ã—ã¦ãƒ˜ãƒ«ãƒ—を表示 + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + コマンドラインパラメータ(%1) ã§æ¸¡ã•れãŸãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¯æ—¢ã«%2ã®ãƒªã‚¹ãƒˆã«ã‚りã¾ã—㟠+ + + + Could not add database %1 to list. + データベース %1 をリストã«è¿½åŠ ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ + + + + closed + é–‰ã˜ã¾ã—㟠+ + + + CliCommand + + + Usage: %1%2 + 使用方法: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + データベース %1 をリストã«è¿½åŠ ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ + + + + Database added: %1 + データベース%1を追加ã—ã¾ã—㟠+ + + + adds new database to the list + æ–°ã—ã„データベースをリストã«è¿½åŠ  + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + <パス>ãŒæŒ‡ã™ç‰¹å®šã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‚’追加ã—ã¾ã™ã€‚ 与ãˆã‚‰ã‚ŒãŸ<åå‰> ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®ãƒªã‚¹ãƒˆã‚’一覧表示ã—ã¾ã™ã€‚<åå‰> ã¯ã€å¾Œã§å‚ç…§ã§ãã‚‹å˜ãªã‚‹è¨˜å·åã§ã™ã€‚ 一æ„ã®åå‰ã‚’é¸æŠžã—ã¦ãã ã•ã„。 ã™ã§ã«ãƒªã‚¹ãƒˆã«ã‚るデータベースを表示ã™ã‚‹ã«ã¯ã€%1コマンドを使用ã—ã¦ãã ã•ã„。 + + + + name + CLI command syntax + åå‰ + + + + path + CLI command syntax + パス + + + + CliCommandCd + + + Changed directory to: %1 + ディレクトリを %1ã«å¤‰æ›´ã—ã¾ã—㟠+ + + + Could not change directory to: %1 + ディレクトリを%1ã«å¤‰æ›´ã§ãã¾ã›ã‚“ã§ã—㟠+ + + + changes current working directory + ç¾åœ¨ã®ä½œæ¥­ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’変更 + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_kaa.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_kaa.ts new file mode 100644 index 0000000..aace0aa --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_kaa.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ko_KR.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ko_KR.ts new file mode 100644 index 0000000..4eceff2 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ko_KR.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + 경로 + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_nl_NL.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_nl_NL.ts new file mode 100644 index 0000000..a141814 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_nl_NL.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_no_NO.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_no_NO.ts new file mode 100644 index 0000000..1bd5353 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_no_NO.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl.qm b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl.qm deleted file mode 100644 index 5334188..0000000 Binary files a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl.ts deleted file mode 100644 index 5b4d6fb..0000000 --- a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl.ts +++ /dev/null @@ -1,690 +0,0 @@ - - - - - CLI - - Current database: %1 - Bieżąca baza danych: %1 - - - No current working database is set. - Nie ustawiono bieżącej bazy danych. - - - Type %1 for help - Wpisz %1, aby uzyskać pomoc. - - - Could not add database %1 to list. - Nie udaÅ‚o siÄ™ dodać bazy danych %1 do listy. - - - closed - zamkniÄ™ta - - - Database passed in command line parameters (%1) was already on the list under name: %2 - Baz danych przekazana w parametrach linii poleceÅ„ (%1) byÅ‚a już na liÅ›cie pod nazwÄ…: %2 - - - - CliCommand - - Usage: %1%2 - Sposób użycia: %1%2 - - - - CliCommandAdd - - Could not add database %1 to list. - Nie udaÅ‚o siÄ™ dodać bazy danych %1 do listy. - - - Database added: %1 - Baza danych dodana: %1 - - - adds new database to the list - dodaje bazÄ™ danych do listy - - - Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. - Dodaje bazÄ™ danych wskazanÄ… przez <Å›cieżkÄ™> z danÄ… <nazwÄ…> do listy baz danych. <nazwa> jest tylko symbolicznÄ… nazwÄ…, do której możesz siÄ™ potem odwoÅ‚ywać. Po prostu wybierz dowolnÄ…, unikalnÄ… nazwÄ™. Aby wypisać listÄ™ baz danych, które sÄ… już na liÅ›cie, użyj polecenia %1. - - - name - CLI command syntax - nazwa - - - path - CLI command syntax - Å›cieżka - - - - CliCommandCd - - Changed directory to: %1 - Zmieniono katalog na: %1 - - - Could not change directory to: %1 - Nie udaÅ‚o siÄ™ zmienić katalogu na: %1 - - - Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - Bardzo podobne polecenie do 'cd' znanego z systemów Unixowych i Windowsa. Wymaga <Å›cieżki> jako argumentu, stÄ…d wywoÅ‚anie %1 zawsze spowoduje zmianÄ™ katalogu. Aby poznać jaki jest bieżący katalog, użyj polecenia %2, a żeby wypisać listÄ™ zawartoÅ›ci bieżącego katalogu użyj polecenia %3. - - - path - CLI command syntax - Å›cieżka - - - changes current working directory - zmienia bieżący katalog - - - - CliCommandClose - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - Nie można wywoÅ‚ać %1, gdy żadna z baz nie jest ustawiona jako bieżąca. OkreÅ› bieżącÄ… bazÄ™ używajÄ…c polecenia %2, lub podaj nazwÄ™ bazy do %3. - - - Connection to database %1 closed. - Połączenie z bazÄ… %1 zostaÅ‚o zamkniÄ™te. - - - No such database: %1. Use %2 to see list of known databases. - Nie znaleziono bazy danych: %1. Użyj %2 aby zonaczyć listÄ™ znanych baz danych. - - - closes given (or current) database - zamyka danÄ… (lub bieżącÄ…) bazÄ™ danych - - - Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). - Zamyka połączenie z bazÄ… danych. JeÅ›li baza danych byÅ‚a już zamkniÄ™ta, nic siÄ™ nie stanie. JeÅ›li <nazwa> jest podana, to powinna ona być nazwÄ… bazy danych do zamkniÄ™cia (wg. tego jak wyÅ›wietla jÄ… polecenie %1). Gdy <nazwa> nie jest podana, to bieżąca baza danych zostanie zamkniÄ™ta (wiÄ™cej szczegółów w pomocy dla %2). - - - name - CLI command syntax - nazwa - - - - CliCommandDbList - - No current working database defined. - Nie okreÅ›lono bieżącej bazy danych. - - - Databases: - Bazy danych: - - - Name - CLI db name column - Nazwa - - - Open - CLI connection state column - Otwarta - - - Closed - CLI connection state column - ZamkniÄ™ta - - - Connection - CLI connection state column - Połączenie - - - Database file path - Åšcieżka do pliku bazy danych - - - prints list of registered databases - wypisuje listÄ™ zarejestrowanych baz danych - - - Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - Wypisuje listÄ™ zarejestrowanych w SQLiteStudio baz danych. Każda baza danych na liÅ›cie może być otwarta, lub zamkniÄ™ta i %1 o tym mówi. Bieżąca baza danych (nazywana również domyÅ›lnÄ… bazÄ… danych) jest również zaznaczona na liÅ›cie znakiem '*' na poczÄ…tku swojej nazwy. Zobacz pomoc dla polecenia %2, żeby dowiedzieć siÄ™ wiÄ™cej o domyÅ›lnej bazie danych. - - - - CliCommandDesc - - shows details about the table - pokazuje szczegóły o tabeli - - - table - tabela - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - Nie wybrano domyÅ›lnej bazy danych. -Użyj polecenia %1, aby ustawić domyÅ›lnÄ… bazÄ™ danych. -Użyj polecenie %2, aby wypisać listÄ™ wszystkich baz. - - - Database is not open. - Baza danych nie jest otwarta. - - - Cannot find table named: %1 - Nie można znaleźć tabeli o nazwie: %1 - - - Table: %1 - Tabla: %1 - - - Column name - Nazwa kolumny - - - Data type - Typ danych - - - Constraints - Ograniczenia - - - Virtual table: %1 - Wirtualna tabela: %1 - - - Construction arguments: - Argumenty konstruujÄ…ce: - - - No construction arguments were passed for this virtual table. - Nie podano argumentów konstruujÄ…cych dla tej tabeli wirtualnej. - - - - CliCommandDir - - lists directories and files in current working directory - wypisuje listÄ™ katalogów i plików w bieżącym katalogu - - - This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. - -You can pass <pattern> with wildcard characters to filter output. - To jest polecenie bardzo podobne do 'dir' znanego z systemu Windows, oraz 'ls' znanego z systemów Unixowych. - -Możesz podać <wzorzec> ze znakami maskujÄ…cymi, aby filtrować wynik. - - - pattern - wzorzec - - - - CliCommandExit - - quits the application - zamyka aplikacjÄ™ - - - Quits the application. Settings are stored in configuration file and will be restored on next startup. - Zamyka aplikacjÄ™. Ustawienia sÄ… zapisywane w pliku konfiguracyjnym i zostanÄ… przywrócone przy nastÄ™pnym starcie. - - - - CliCommandHelp - - shows this help message - pokazuje tÄ… treść pomocy - - - Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. -To see list of supported commands, type %2 without any arguments. - -When passing <command> name, you can skip special prefix character ('%3'). - -You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. - Używaj %1 aby poznać poszczególne polecenia obsÅ‚ugiwane przez interfejs linii poleceÅ„ (CLI) SQLiteStudio. -Aby zobaczyć listÄ™ obsÅ‚ugiwanych poleceÅ„, wpisz %2 bez żadnych argumentów. - -Kiedy podaje siÄ™ nazwÄ™ <polecenia>, można pominąć specjalny znak przedrostka ('%3'). - -Zawsze możesz wywoÅ‚ać dowolne polecenie z dokÅ‚adnie jednÄ… opcjÄ… '--help', aby zobaczyć pomoc dla tego polecenia. Jest to alternatywa dla wpisywania: %1 <polecenie>. - - - command - CLI command syntax - polecenie - - - No such command: %1 - Nie ma takiego polecenia: %1 - - - Type '%1' for list of available commands. - Wpisz '%1' aby poznać listÄ™ dostÄ™pnych poleceÅ„. - - - Usage: %1%2 - Sposób użycia: %1%2 - - - Aliases: %1 - Aliasy: %1 - - - - CliCommandHistory - - Current history limit is set to: %1 - Bieżący limit historii jest ustawiony na: %1 - - - prints history or erases it - wyÅ›wietla historiÄ™ lub jÄ… kasuje - - - number - liczba - - - Console history erased. - Historia konsoli skasowana. - - - Invalid number: %1 - Niepoprawna liczba: %1 - - - History limit set to %1 - Limit historii ustawiono na %1 - - - When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. - -When the -c or --clear option is passed, then the history gets erased. -When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. -Use -ql or --querylimit option to see the current limit value. - Gdy nie poda siÄ™ żadnego argumentu, to polecenie wyÅ›wietla historiÄ™ linii poleceÅ„. Każdy wpis w historii jest oddzielony liniÄ… poziomÄ…, żeby Å‚atwiej byÅ‚o czytać wiÄ™cej wpisów. - -Kiedy poda siÄ™ opcjÄ™ -c lub --clear, to historia jest kasowana. -Kiedy poda siÄ™ opcjÄ™ -l lub --limit, to ustawiany jest nowy limit na historii. Wymaga to podania dodatkowego argumentu, mówiÄ…cego o tym, ile ma być wpisów przechowywanych w historii. -Użyj opcji -ql lub --querylimit, aby poznać aktualnÄ… wartość limitu. - - - - CliCommandMode - - Current results printing mode: %1 - Aktualny tryb wyÅ›wietlania wyników: %1 - - - Invalid results printing mode: %1 - Niepoprawny tryb wyÅ›wietlania wyników: %1 - - - New results printing mode: %1 - Nowy tryb wyÅ›wietlania wyników: %1 - - - tells or changes the query results format - wyÅ›wietla lub zmienia format wyników zapytania - - - When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: -- CLASSIC - columns are separated by a comma, not aligned, -- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, -- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), -- ROW - each column from the row is displayed in new line, so the full data is displayed. - -The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. - -The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. - -The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. -ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. - -The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - JeÅ›li nie poda siÄ™ argumentu, wyÅ›wietla aktualny format wyjÅ›ciowy dla wyników zapytania. Kiedy poda siÄ™ <tryb>, to jest on zmieniany na podany tryb. -ObsÅ‚ugiwane tryby to: -- CLASSIC - kolumny sÄ… oddzielone przecinkiem, bez wyrównania, -- FIXED - kolumny majÄ… równÄ… i ustalonÄ… szerokość, zawsze mieszczÄ… siÄ™ w oknie terminala, ale dane w kolumnach mogÄ… być uciÄ™te, -- COLUMNS - taki jak FIXED, ale sprytniejszy (nie używać z ogromnymi wynikami zapytaÅ„, szczegóły poniżej), -- ROW - każda kolumna w wierszu jest wyÅ›wietlana w osobnej linii, wiÄ™c peÅ‚ne dane sÄ… wyÅ›wietlane. - -Tryb CLASSIC jest zalecane, jeÅ›li chcesz widzieć wszystkie dane, ale nie chcesz marnować linii na każdÄ… kolumnÄ™. Każdy wiersz wyÅ›wietli caÅ‚e dane dla każdej kolumny, ale oznacza to również, że kolumny nie bÄ™dÄ… wyrównane do siebie wzglÄ™dem kolejnych wierszy. Tryb CLASSIC również nie patrzy na szerokość okna terminala (konsoli), wiÄ™c jeÅ›li wartoÅ›ci w kolumnach sÄ… szersze niż okno, to wiersz bÄ™dzie kontynuowany w nastÄ™pnej linii. - -Tryb FIXED jest zalecany, jeÅ›li chcesz czytelny wynik i nie przejmujesz siÄ™ zbyt dÅ‚ugimi danymi. Kolumny bÄ™dÄ… wyrównane, otrzymujÄ…c Å‚adnÄ… tabelÄ™. Szerokość kolumn jest obliczana dzielÄ…c szerokość okna konsoli przez liczbÄ™ kolumn. - -Tryb COLUMNS jest podobny do trybu FIXED, z tÄ… różnicÄ…, że póbuje być sprytny i szerokość kolumn zawierajÄ…cych krótsze dane jest mniejsza, niż kolumn z dÅ‚uższymi danymi. Pierwsze w kolejnoÅ›ci odchudzane sÄ… kolumny z najdÅ‚uższymi nagłówkami (wiÄ™c nazwy nagłówków sÄ… pierwsze w kolejce do obciÄ™cia), nastÄ™pnie kolumny z najdÅ‚uższymi wartoÅ›ciami, aż do momentu, kiedy wszystkie kolumny mieszczÄ… siÄ™ w oknie terminala. -UWAGA! Tryb COLUMNS od razu odczytuje wszystkie dane z wyników zapytania, aby okreÅ›lić szerokość kolumn, wiÄ™c jest to niebezpieczne, gdy pracuje siÄ™ z ogromnymi zestawami danych w wynikach. Miej na uwadze, że ten tryb wczyta wszystkie dane do pamiÄ™ci na raz. - -Tryb ROW jest zalecane, kiedy musisz widzieć caÅ‚e wartoÅ›ci i nie spodziewasz siÄ™ wielu wierszy do wyÅ›wietlenia, ponieważ ten tryb wyÅ›wietla liniÄ™ dla każdej kolumny, wiÄ™c dostaniesz 10 linii dla jednego wiersza z 10 kolumnami, nastÄ™pnie bÄ™dziesz miaÅ‚ 10 takich wierszy, wiÄ™c skoÅ„czysz ze 100 liniami wyjÅ›cia (+1 dodatkowa dla każdego wiersza danych, aby oddzielić je od siebie). - - - - CliCommandNullValue - - Current NULL representation string: %1 - Aktualny Å‚aÅ„cuch reprezentujÄ…cy wartość NULL: %1 - - - tells or changes the NULL representation string - wyÅ›wietla lub zmienia łąńcuch reprezentujÄ…cy wartość NULL - - - If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. - JeÅ›li nie poda siÄ™ argumentu, to wyÅ›wietlana jest aktualna reprezentacja wartoÅ›ci NULL (to znaczy to, co jest wyÅ›wietlane zamiast wartoÅ›ci NULL w wynikach zapytaÅ„). JeÅ›li podano argument, to staje siÄ™ on nowÄ… reprezentacjÄ… wartoÅ›ci NULL. - - - - CliCommandOpen - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - Nie można wywoÅ‚ać %1, gdy żadna z baz nie jest ustawiona jako bieżąca. OkreÅ› bieżącÄ… bazÄ™ używajÄ…c polecenia %2, lub podaj nazwÄ™ bazy do %3. - - - Could not add database %1 to list. - Nie udaÅ‚o siÄ™ dodać bazy danych %1 do listy. - - - File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. - - - - Database %1 has been open and set as the current working database. - - - - opens database connection - otwiera połączenie z bazÄ… - - - Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. - Otwiera połączenie do bazy. JeÅ›li nie podano dodatkowych argumentów, to połączenie jest nawiÄ…zywane z domyÅ›lnÄ… bazÄ… (wiÄ™cej szczegółów w pomocy dla polecenia %1). Natomiast gdy poda siÄ™ argument, to może to być albo <nazwa> zarejestrowanej bazy danych do otwarcia, lub może to być <Å›cieżka> do pliku bazy danych do otwarcia. W drugim przypadku <Å›cieżka> zostanie zarejestrowana na liÅ›cie baz danych z wygenerowanÄ… nazwÄ…, ale tylko na czas aktualnej sesji aplikacji. Po restarcie aplikacji taka baza danych nie jest przywracana na listÄ™. - - - name - CLI command syntax - nazwa - - - path - CLI command syntax - Å›cieżka - - - - CliCommandPwd - - prints the current working directory - wypisuje bieżący katalog - - - This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. - Jest to polecenie podobne do 'pwd' znanego z sytemów Unixowych oraz polecenia 'cd' bez argumentów dla systemów Windows. Wypisuje bieżący katalog. Możesz zmienić bieżący katalog za pomocÄ… polecenia %1, oraz możesz wypisać zawartość bieżącego katalogu za pomocÄ… polecenia %2. - - - - CliCommandRemove - - No such database: %1 - Nie ma takiej bazy danych: %1 - - - Database removed: %1 - Baza danych usuniÄ™ta: %1 - - - New current database set: - Nowa domyÅ›lna baza danych: - - - removes database from the list - usuwa bazÄ™ danych z listy - - - Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. - Usuwa <nazwanÄ…> bazÄ™ danych z listy zarejestrowanych baz danych. JeÅ›li baza nie byÅ‚a na liÅ›cie (patrz - polecenie %1), to zostanie wyÅ›wietlony komunikat błędu i nic siÄ™ nie stanie. - - - name - CLI command syntax - nazwa - - - - CliCommandSql - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - Nie wybrano domyÅ›lnej bazy danych. -Użyj polecenia %1, aby ustawić domyÅ›lnÄ… bazÄ™ danych. -Użyj polecenie %2, aby wypisać listÄ™ wszystkich baz. - - - Database is not open. - Baz danych nie jest otwarta. - - - executes SQL query - wykonuje zapytanie SQL - - - This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. - To polecenie jest wywoÅ‚ywane za każdym razem, kiedy wpisujesz zapytanie SQL w linii poleceÅ„. Wykonuje ono zapytanie na bieżącej bazie danych (wiÄ™cej szczegółów w pomocy dla %1). Nie ma sensu wywoÅ‚ywanie tego polecenia bezpoÅ›rednio. Zamiast tego po prostu wpisuj zapytania SQL w linii poleceÅ„, bez polecenia poprzedzajÄ…cego. - - - sql - CLI command syntax - sql - - - Too many columns to display in %1 mode. - Zbyt wiele kolumn, aby wyÅ›wietlić w trybie %1. - - - Row %1 - Wiersz %1 - - - Query execution error: %1 - Błąd wykonywania zapytania: %1 - - - - CliCommandTables - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - Nie można wywoÅ‚ać %1, gdy żadna z baz nie jest ustawiona jako bieżąca. OkreÅ› bieżącÄ… bazÄ™ używajÄ…c polecenia %2, lub podaj nazwÄ™ bazy do %3. - - - Database %1 is closed. - Baza danych %1 jest zamkniÄ™ta. - - - Database - Baza danych - - - Table - Tabela - - - prints list of tables in the database - wypisuje listÄ™ tabel w bazie danych - - - Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. -When the -s option is given, then system tables are also listed. - Wypisuje listÄ™ tabel w danej <bazie danych> lub w bieżącej bazie danych. <baza danych> powinna być nazwÄ… zarejestrowanej bazy danych (patrz %1). List wyjÅ›ciowa zawiera wszystkie tabele ze wszystkich baz dołączonych do odpytywanej bazy. Gdy podana jest opcja -s, to również systemowe tabele pojawiÄ… siÄ™ na liÅ›cie. - - - database - CLI command syntax - baza danych - - - No such database: %1. Use %2 to see list of known databases. - Nie znaleziono bazy danych: %1. Użyj %2 aby zonaczyć listÄ™ znanych baz danych. - - - - CliCommandTree - - No current working database is selected. Use %1 to define one and then run %2. - Nie wybrano bieżącej bazy danych. Użyj %1 aby takÄ… zdefiniować i wtedy uruchom %2. - - - Tables - Tabele - - - Views - Widoki - - - Columns - Kolumny - - - Indexes - Indeksy - - - Triggers - Wyzwalacze - - - prints all objects in the database as a tree - wypisuje wszystkie obiekty w bazie danych w postaci drzewa - - - Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. -When -c option is given, then also columns will be listed under each table. -When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). -The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. - Wypisuje wszystkie obiekty (tabele, indeksy, wyzwalacze i widoki) znajdujÄ…ce siÄ™ w bazie danych w postaci drzewa. Drzewo to jest podobne do tego, które można zobaczyć w interfejsie graficznym SQLiteStudio. -Kiedy poda siÄ™ opcjÄ™ -c, to pod każdÄ… tabelÄ… wylistowane zostanÄ… kolumny. -Kiedy poda siÄ™ opcjÄ™ -s, to również obiekty systemowe bÄ™dÄ… wypisane (tabele sqlite_*, indeksy autoinkrementacji, itp). -Argument bazy danych jest opcjonalny i gdy siÄ™ go poda, to tylko ta baza zostanie wypisana. Nie jest to nazwa zarejestrowanej nazwy, ale nazwa wewnÄ™trzna bazy SQLite, jak np 'main', 'temp', lub dowolna nazwa dołączonej bazy. Aby wypisać drzewo dla innej zarejestrowanej bazy, użyj najpierw %1, aby zmienić bieżącÄ… bazÄ™ danych i wtedy użyj polecenia %2. - - - - CliCommandUse - - No current database selected. - Bieżąca baza danych nie jest wybrana. - - - Current database: %1 - Bieżąca baza danych: %1 - - - No such database: %1 - Nie ma takiej bazy danych: %1 - - - changes default working database - zmienia domyÅ›lnÄ… bazÄ™ danych - - - Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. - -What is current working database? -When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). - -The default database can be selected in various ways: -- using %1 command, -- by passing database file name to the application startup parameters, -- by passing registered database name to the application startup parameters, -- by restoring previously selected default database from saved configuration, -- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. - Zmienia domyÅ›lnÄ… bazÄ™ danych na <nazwanÄ…>. JeÅ›li <nazwana> baza danych nie jest zarejestrowana w aplikacji, to wyÅ›wietlony zostanie komunikat błędu i żadne zmiany nie nastÄ…piÄ…. - -Czym jest domyÅ›lna baza danych? -Kiedy piszesz zapytanie SQL do wykonania, jest ono wykonywane na domyÅ›lnej bazie danych, która jest również nazywana bieżącÄ… bazÄ… danych. WiÄ™kszość poleceÅ„ zwiÄ…zanych z bazÄ… danych może pracować z użyciem domyÅ›lnej bazy danych, jeÅ›li nie poda siÄ™ bazy w ich argumentach. Bieżąca baza danych jest zawsze widoczna w wierszu poleceÅ„. DomyÅ›lna baza danych jest zawsze zdefiniowana (z wyjÄ…tkiem, gdy nie ma żadnej bazy na liÅ›cie). - -DomyÅ›lna baza danych może być wybrana na kilka sposobów: -- używajÄ…c polecenia %1, -- podajÄ…c plik bazy danych jako parametr do uruchomienia aplikacji, -- podajÄ…c nazwÄ™ zarejestrowanej bazy danych jako parametr do uruchomienia aplikacji, -- lub gdy domyÅ›lna baza nie zostaÅ‚a wybrana przez żadne z powyższych, to pierwsza baza z listy zarejestrowanych baz stanie siÄ™ domyÅ›lnÄ…. - - - name - CLI command syntax - nazwa - - - - QObject - - Insufficient number of arguments. - NiewystarajÄ…ca liczba arugmentów. - - - Too many arguments. - Za dużo argumentów. - - - Invalid argument value: %1. -Expected one of: %2 - Niepoprawna wartość argumentu: %1. -Oczekiwano jednej z: %2 - - - Unknown option: %1 - CLI command syntax - Nieznana opcja: %1 - - - Option %1 requires an argument. - CLI command syntax - Opcja %1 wymaga argumentu. - - - string - CLI command syntax - Å‚aÅ„cuch - - - Command line interface to SQLiteStudio, a SQLite manager. - Interfejs linii poleceÅ„ dla SQLiteStudio, menażera SQLite. - - - Enables debug messages on standard error output. - Włącza wiadomoÅ›ci debugujÄ…ce na standardowym wyjÅ›ciu błędów. - - - Enables Lemon parser debug messages for SQL code assistant. - Włącza wiadomoÅ›ci debugujÄ…ce analizatora Lemon dla asystenta kodu SQL. - - - file - plik - - - Database file to open - Baza danych do otwarcia - - - Lists plugins installed in the SQLiteStudio and quits. - Wypisuje listÄ™ zainstalowanych w SQLiteStudio wtyczek i wychodzi. - - - diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl_PL.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl_PL.ts new file mode 100644 index 0000000..680984e --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pl_PL.ts @@ -0,0 +1,874 @@ + + + + + CLI + + + Current database: %1 + Bieżąca baza danych: %1 + + + + No current working database is set. + Nie ustawiono bieżącej bazy danych. + + + + Type %1 for help + Wpisz %1, aby uzyskać pomoc. + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Baz danych przekazana w parametrach linii poleceÅ„ (%1) byÅ‚a już na liÅ›cie pod nazwÄ…: %2 + + + + Could not add database %1 to list. + Nie udaÅ‚o siÄ™ dodać bazy danych %1 do listy. + + + + closed + zamkniÄ™ta + + + + CliCommand + + + Usage: %1%2 + Sposób użycia: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Nie udaÅ‚o siÄ™ dodać bazy danych %1 do listy. + + + + Database added: %1 + Baza danych dodana: %1 + + + + adds new database to the list + dodaje bazÄ™ danych do listy + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Dodaje bazÄ™ danych wskazanÄ… przez <Å›cieżkÄ™> z danÄ… <nazwÄ…> do listy baz danych. <nazwa> jest tylko symbolicznÄ… nazwÄ…, do której możesz siÄ™ potem odwoÅ‚ywać. Po prostu wybierz dowolnÄ…, unikalnÄ… nazwÄ™. Aby wypisać listÄ™ baz danych, które sÄ… już na liÅ›cie, użyj polecenia %1. + + + + name + CLI command syntax + nazwa + + + + path + CLI command syntax + Å›cieżka + + + + CliCommandCd + + + Changed directory to: %1 + Zmieniono katalog na: %1 + + + + Could not change directory to: %1 + Nie udaÅ‚o siÄ™ zmienić katalogu na: %1 + + + + changes current working directory + zmienia bieżący katalog + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Bardzo podobne polecenie do 'cd' znanego z systemów Unixowych i Windowsa. Wymaga <Å›cieżki> jako argumentu, stÄ…d wywoÅ‚anie %1 zawsze spowoduje zmianÄ™ katalogu. Aby poznać jaki jest bieżący katalog, użyj polecenia %2, a żeby wypisać listÄ™ zawartoÅ›ci bieżącego katalogu użyj polecenia %3. + + + + path + CLI command syntax + Å›cieżka + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Nie można wywoÅ‚ać %1, gdy żadna z baz nie jest ustawiona jako bieżąca. OkreÅ› bieżącÄ… bazÄ™ używajÄ…c polecenia %2, lub podaj nazwÄ™ bazy do %3. + + + + + Connection to database %1 closed. + Połączenie z bazÄ… %1 zostaÅ‚o zamkniÄ™te. + + + + No such database: %1. Use %2 to see list of known databases. + Nie znaleziono bazy danych: %1. Użyj %2 aby zonaczyć listÄ™ znanych baz danych. + + + + closes given (or current) database + zamyka danÄ… (lub bieżącÄ…) bazÄ™ danych + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Zamyka połączenie z bazÄ… danych. JeÅ›li baza danych byÅ‚a już zamkniÄ™ta, nic siÄ™ nie stanie. JeÅ›li <nazwa> jest podana, to powinna ona być nazwÄ… bazy danych do zamkniÄ™cia (wg. tego jak wyÅ›wietla jÄ… polecenie %1). Gdy <nazwa> nie jest podana, to bieżąca baza danych zostanie zamkniÄ™ta (wiÄ™cej szczegółów w pomocy dla %2). + + + + name + CLI command syntax + nazwa + + + + CliCommandDbList + + + No current working database defined. + Nie okreÅ›lono bieżącej bazy danych. + + + + Databases: + Bazy danych: + + + + + Name + CLI db name column + Nazwa + + + + + Open + CLI connection state column + Otwarta + + + + + Closed + CLI connection state column + ZamkniÄ™ta + + + + + Connection + CLI connection state column + Połączenie + + + + + Database file path + Åšcieżka do pliku bazy danych + + + + prints list of registered databases + wypisuje listÄ™ zarejestrowanych baz danych + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Wypisuje listÄ™ zarejestrowanych w SQLiteStudio baz danych. Każda baza danych na liÅ›cie może być otwarta, lub zamkniÄ™ta i %1 o tym mówi. Bieżąca baza danych (nazywana również domyÅ›lnÄ… bazÄ… danych) jest również zaznaczona na liÅ›cie znakiem '*' na poczÄ…tku swojej nazwy. Zobacz pomoc dla polecenia %2, żeby dowiedzieć siÄ™ wiÄ™cej o domyÅ›lnej bazie danych. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + Nie wybrano domyÅ›lnej bazy danych. +Użyj polecenia %1, aby ustawić domyÅ›lnÄ… bazÄ™ danych. +Użyj polecenie %2, aby wypisać listÄ™ wszystkich baz. + + + + Database is not open. + Baza danych nie jest otwarta. + + + + Cannot find table named: %1 + Nie można znaleźć tabeli o nazwie: %1 + + + + shows details about the table + pokazuje szczegóły o tabeli + + + + table + tabela + + + + Table: %1 + Tabla: %1 + + + + Column name + Nazwa kolumny + + + + Data type + Typ danych + + + + Constraints + Ograniczenia + + + + Virtual table: %1 + Wirtualna tabela: %1 + + + + Construction arguments: + Argumenty konstruujÄ…ce: + + + + No construction arguments were passed for this virtual table. + Nie podano argumentów konstruujÄ…cych dla tej tabeli wirtualnej. + + + + CliCommandDir + + + lists directories and files in current working directory + wypisuje listÄ™ katalogów i plików w bieżącym katalogu + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + To jest polecenie bardzo podobne do 'dir' znanego z systemu Windows, oraz 'ls' znanego z systemów Unixowych. + +Możesz podać <wzorzec> ze znakami maskujÄ…cymi, aby filtrować wynik. + + + + pattern + wzorzec + + + + CliCommandExit + + + quits the application + zamyka aplikacjÄ™ + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Zamyka aplikacjÄ™. Ustawienia sÄ… zapisywane w pliku konfiguracyjnym i zostanÄ… przywrócone przy nastÄ™pnym starcie. + + + + CliCommandHelp + + + shows this help message + pokazuje tÄ… treść pomocy + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Używaj %1 aby poznać poszczególne polecenia obsÅ‚ugiwane przez interfejs linii poleceÅ„ (CLI) SQLiteStudio. +Aby zobaczyć listÄ™ obsÅ‚ugiwanych poleceÅ„, wpisz %2 bez żadnych argumentów. + +Kiedy podaje siÄ™ nazwÄ™ <polecenia>, można pominąć specjalny znak przedrostka ('%3'). + +Zawsze możesz wywoÅ‚ać dowolne polecenie z dokÅ‚adnie jednÄ… opcjÄ… '--help', aby zobaczyć pomoc dla tego polecenia. Jest to alternatywa dla wpisywania: %1 <polecenie>. + + + + command + CLI command syntax + polecenie + + + + No such command: %1 + Nie ma takiego polecenia: %1 + + + + Type '%1' for list of available commands. + Wpisz '%1' aby poznać listÄ™ dostÄ™pnych poleceÅ„. + + + + Usage: %1%2 + Sposób użycia: %1%2 + + + + Aliases: %1 + Aliasy: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Bieżący limit historii jest ustawiony na: %1 + + + + prints history or erases it + wyÅ›wietla historiÄ™ lub jÄ… kasuje + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + Gdy nie poda siÄ™ żadnego argumentu, to polecenie wyÅ›wietla historiÄ™ linii poleceÅ„. Każdy wpis w historii jest oddzielony liniÄ… poziomÄ…, żeby Å‚atwiej byÅ‚o czytać wiÄ™cej wpisów. + +Kiedy poda siÄ™ opcjÄ™ -c lub --clear, to historia jest kasowana. +Kiedy poda siÄ™ opcjÄ™ -l lub --limit, to ustawiany jest nowy limit na historii. Wymaga to podania dodatkowego argumentu, mówiÄ…cego o tym, ile ma być wpisów przechowywanych w historii. +Użyj opcji -ql lub --querylimit, aby poznać aktualnÄ… wartość limitu. + + + + number + liczba + + + + Console history erased. + Historia konsoli skasowana. + + + + Invalid number: %1 + Niepoprawna liczba: %1 + + + + History limit set to %1 + Limit historii ustawiono na %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Aktualny tryb wyÅ›wietlania wyników: %1 + + + + Invalid results printing mode: %1 + Niepoprawny tryb wyÅ›wietlania wyników: %1 + + + + New results printing mode: %1 + Nowy tryb wyÅ›wietlania wyników: %1 + + + + tells or changes the query results format + wyÅ›wietla lub zmienia format wyników zapytania + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + Gdy wywoÅ‚uje siÄ™ bez argumentu, wyÅ›wietla bieżący format wyjÅ›cia dla wyników zapytania. Po podaniu <tryb>, zostaje on zmieniony na podany tryb. ObsÅ‚ugiwane tryby to: +- CLASSIC - kolumny sÄ… oddzielone przecinkiem, nie wyrównane, +- FIXED - kolumny majÄ… takÄ… samÄ… i stałą szerokość, zawsze pasujÄ… do szerokoÅ›ci okna terminalu, ale dane w kolumnach mogÄ… być obciÄ™te, +- KOLUMNS - jak FIXED, ale mÄ…drzejsze (nie używaj z dużymi zestawami wyników, zobacz szczegóły poniżej), +- ROW - każda kolumna z wiersza jest wyÅ›wietlana w nowej linii, wiÄ™c wyÅ›wietlane sÄ… peÅ‚ne dane. + +Tryb CLASSIC jest zalecany, jeÅ›li chcesz zobaczyć wszystkie dane, ale nie chcesz marnować linii dla każdej kolumny. Każdy wiersz wyÅ›wietli peÅ‚ne dane dla każdej kolumny, ale oznacza to również, że kolumny nie bÄ™dÄ… wyrównane do siebie w kolejnych wierszach. Tryb CLASSIC również nie respektuje nie szerokoÅ›ci okna terminala (konsoli), wiÄ™c jeÅ›li wartoÅ›ci w kolumnach sÄ… wiÄ™ksze niż okno, wiersz bÄ™dzie kontynuowany w kolejnych wierszach. + +Tryb FIXED jest zalecany, jeÅ›li chcesz odczytać dane wyjÅ›ciowe i nie troszczysz siÄ™ o dÅ‚ugie wartoÅ›ci danych. Kolumny bÄ™dÄ… wyrównywane, co sprawi, że wyjÅ›cie bÄ™dzie Å‚adnÄ… tabelÄ…. Szerokość kolumn jest obliczana na podstawie szerokoÅ›ci okna konsoli i liczby kolumn. + +Tryb COLUMNS jest podobny do trybu FIXED, z tÄ… różnicÄ…, że próbuje być inteligentny i sprawić, żeby kolumny o krótszych wartoÅ›ciach byÅ‚y węższe, podczas gdy kolumny o dÅ‚uższych wartoÅ›ciach bÄ™dÄ… miaÅ‚y wiÄ™cej miejsca. W pierwszej kolejnoÅ›ci zmniejszane sÄ… kolumny z najdÅ‚uższymi nagłówkami (nazwy w nagłówkach należy skrócić jako pierwsze), nastÄ™pnie kolumny z najdÅ‚uższymi wartoÅ›ciami sÄ… zmniejszane, aż do chwili, gdy wszystkie kolumny pasujÄ… do okna koÅ„cowego. +UWAGA! Tryb COLUMNS odczytuje wszystkie wyniki zapytania na raz, aby ocenić szerokość kolumny, w zwiÄ…zku z tym korzystanie z tego trybu podczas pracy z ogromnymi zestawami wyników jest niebezpieczne. PamiÄ™taj, że ten tryb zaÅ‚aduje caÅ‚y wynik do pamiÄ™ci. + +Tryb ROW jest zalecany, jeÅ›li chcesz zobaczyć caÅ‚e wartoÅ›ci, a nie oczekujesz wyÅ›wietlania zbyt wielu wierszy, ponieważ ten tryb wyÅ›wietla liniÄ™ wyjÅ›cia dla każdej kolumny, wiÄ™c otrzymasz 10 linii dla pojedynczego wiersza z 10 kolumnami, a jeÅ›li masz 10 takich wierszy, otrzymasz 100 linii wyjÅ›cia (+1 dodatkowy wiersz, aby oddzielić wiersze od siebie). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Aktualny Å‚aÅ„cuch reprezentujÄ…cy wartość NULL: %1 + + + + tells or changes the NULL representation string + wyÅ›wietla lub zmienia łąńcuch reprezentujÄ…cy wartość NULL + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + JeÅ›li nie poda siÄ™ argumentu, to wyÅ›wietlana jest aktualna reprezentacja wartoÅ›ci NULL (to znaczy to, co jest wyÅ›wietlane zamiast wartoÅ›ci NULL w wynikach zapytaÅ„). JeÅ›li podano argument, to staje siÄ™ on nowÄ… reprezentacjÄ… wartoÅ›ci NULL. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Nie można wywoÅ‚ać %1, gdy żadna z baz nie jest ustawiona jako bieżąca. OkreÅ› bieżącÄ… bazÄ™ używajÄ…c polecenia %2, lub podaj nazwÄ™ bazy do %3. + + + + Could not add database %1 to list. + Nie udaÅ‚o siÄ™ dodać bazy danych %1 do listy. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + Plik %1 nie istnieje w %2. Nie można otworzyć nieistniejÄ…cej bazy poleceniem %3. Aby utworzyć nowÄ… bazÄ™ danych, użyj polecenia %4. + + + + Database %1 has been open and set as the current working database. + Baza %1 zostaÅ‚a otwarta i ustawiona jako bieżąca baza robocza. + + + + opens database connection + otwiera połączenie z bazÄ… + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Otwiera połączenie do bazy. JeÅ›li nie podano dodatkowych argumentów, to połączenie jest nawiÄ…zywane z domyÅ›lnÄ… bazÄ… (wiÄ™cej szczegółów w pomocy dla polecenia %1). Natomiast gdy poda siÄ™ argument, to może to być albo <nazwa> zarejestrowanej bazy danych do otwarcia, lub może to być <Å›cieżka> do pliku bazy danych do otwarcia. W drugim przypadku <Å›cieżka> zostanie zarejestrowana na liÅ›cie baz danych z wygenerowanÄ… nazwÄ…, ale tylko na czas aktualnej sesji aplikacji. Po restarcie aplikacji taka baza danych nie jest przywracana na listÄ™. + + + + name + CLI command syntax + nazwa + + + + path + CLI command syntax + Å›cieżka + + + + CliCommandPwd + + + prints the current working directory + wypisuje bieżący katalog + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + Jest to polecenie podobne do 'pwd' znanego z sytemów Unixowych oraz polecenia 'cd' bez argumentów dla systemów Windows. Wypisuje bieżący katalog. Możesz zmienić bieżący katalog za pomocÄ… polecenia %1, oraz możesz wypisać zawartość bieżącego katalogu za pomocÄ… polecenia %2. + + + + CliCommandRemove + + + No such database: %1 + Nie ma takiej bazy danych: %1 + + + + Database removed: %1 + Baza danych usuniÄ™ta: %1 + + + + New current database set: + Nowa domyÅ›lna baza danych: + + + + removes database from the list + usuwa bazÄ™ danych z listy + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Usuwa <nazwanÄ…> bazÄ™ danych z listy zarejestrowanych baz danych. JeÅ›li baza nie byÅ‚a na liÅ›cie (patrz - polecenie %1), to zostanie wyÅ›wietlony komunikat błędu i nic siÄ™ nie stanie. + + + + name + CLI command syntax + nazwa + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + Nie wybrano domyÅ›lnej bazy danych. +Użyj polecenia %1, aby ustawić domyÅ›lnÄ… bazÄ™ danych. +Użyj polecenie %2, aby wypisać listÄ™ wszystkich baz. + + + + Database is not open. + Baz danych nie jest otwarta. + + + + executes SQL query + wykonuje zapytanie SQL + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + To polecenie jest wywoÅ‚ywane za każdym razem, kiedy wpisujesz zapytanie SQL w linii poleceÅ„. Wykonuje ono zapytanie na bieżącej bazie danych (wiÄ™cej szczegółów w pomocy dla %1). Nie ma sensu wywoÅ‚ywanie tego polecenia bezpoÅ›rednio. Zamiast tego po prostu wpisuj zapytania SQL w linii poleceÅ„, bez polecenia poprzedzajÄ…cego. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Zbyt wiele kolumn, aby wyÅ›wietlić w trybie %1. + + + + Row %1 + Wiersz %1 + + + + Query execution error: %1 + Błąd wykonywania zapytania: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + Nie znaleziono bazy danych: %1. Użyj %2 aby zonaczyć listÄ™ znanych baz danych. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Nie można wywoÅ‚ać %1, gdy żadna z baz nie jest ustawiona jako bieżąca. OkreÅ› bieżącÄ… bazÄ™ używajÄ…c polecenia %2, lub podaj nazwÄ™ bazy do %3. + + + + Database %1 is closed. + Baza danych %1 jest zamkniÄ™ta. + + + + + Database + Baza danych + + + + Table + Tabela + + + + prints list of tables in the database + wypisuje listÄ™ tabel w bazie danych + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Wypisuje listÄ™ tabel w danej <bazie danych> lub w bieżącej bazie danych. <baza danych> powinna być nazwÄ… zarejestrowanej bazy danych (patrz %1). List wyjÅ›ciowa zawiera wszystkie tabele ze wszystkich baz dołączonych do odpytywanej bazy. Gdy podana jest opcja -s, to również systemowe tabele pojawiÄ… siÄ™ na liÅ›cie. + + + + database + CLI command syntax + baza danych + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + Nie wybrano bieżącej bazy danych. Użyj %1 aby takÄ… zdefiniować i wtedy uruchom %2. + + + + Tables + Tabele + + + + Views + Widoki + + + + Columns + Kolumny + + + + Indexes + Indeksy + + + + + Triggers + Wyzwalacze + + + + prints all objects in the database as a tree + wypisuje wszystkie obiekty w bazie danych w postaci drzewa + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Wypisuje wszystkie obiekty (tabele, indeksy, wyzwalacze i widoki) znajdujÄ…ce siÄ™ w bazie danych w postaci drzewa. Drzewo to jest podobne do tego, które można zobaczyć w interfejsie graficznym SQLiteStudio. +Kiedy poda siÄ™ opcjÄ™ -c, to pod każdÄ… tabelÄ… wylistowane zostanÄ… kolumny. +Kiedy poda siÄ™ opcjÄ™ -s, to również obiekty systemowe bÄ™dÄ… wypisane (tabele sqlite_*, indeksy autoinkrementacji, itp). +Argument bazy danych jest opcjonalny i gdy siÄ™ go poda, to tylko ta baza zostanie wypisana. Nie jest to nazwa zarejestrowanej nazwy, ale nazwa wewnÄ™trzna bazy SQLite, jak np 'main', 'temp', lub dowolna nazwa dołączonej bazy. Aby wypisać drzewo dla innej zarejestrowanej bazy, użyj najpierw %1, aby zmienić bieżącÄ… bazÄ™ danych i wtedy użyj polecenia %2. + + + + CliCommandUse + + + No current database selected. + Bieżąca baza danych nie jest wybrana. + + + + + Current database: %1 + Bieżąca baza danych: %1 + + + + No such database: %1 + Nie ma takiej bazy danych: %1 + + + + changes default working database + zmienia domyÅ›lnÄ… bazÄ™ danych + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Zmienia domyÅ›lnÄ… bazÄ™ danych na <nazwanÄ…>. JeÅ›li <nazwana> baza danych nie jest zarejestrowana w aplikacji, to wyÅ›wietlony zostanie komunikat błędu i żadne zmiany nie nastÄ…piÄ…. + +Czym jest domyÅ›lna baza danych? +Kiedy piszesz zapytanie SQL do wykonania, jest ono wykonywane na domyÅ›lnej bazie danych, która jest również nazywana bieżącÄ… bazÄ… danych. WiÄ™kszość poleceÅ„ zwiÄ…zanych z bazÄ… danych może pracować z użyciem domyÅ›lnej bazy danych, jeÅ›li nie poda siÄ™ bazy w ich argumentach. Bieżąca baza danych jest zawsze widoczna w wierszu poleceÅ„. DomyÅ›lna baza danych jest zawsze zdefiniowana (z wyjÄ…tkiem, gdy nie ma żadnej bazy na liÅ›cie). + +DomyÅ›lna baza danych może być wybrana na kilka sposobów: +- używajÄ…c polecenia %1, +- podajÄ…c plik bazy danych jako parametr do uruchomienia aplikacji, +- podajÄ…c nazwÄ™ zarejestrowanej bazy danych jako parametr do uruchomienia aplikacji, +- lub gdy domyÅ›lna baza nie zostaÅ‚a wybrana przez żadne z powyższych, to pierwsza baza z listy zarejestrowanych baz stanie siÄ™ domyÅ›lnÄ…. + + + + name + CLI command syntax + nazwa + + + + QObject + + + Insufficient number of arguments. + NiewystarajÄ…ca liczba arugmentów. + + + + Too many arguments. + Za dużo argumentów. + + + + Invalid argument value: %1. +Expected one of: %2 + Niepoprawna wartość argumentu: %1. +Oczekiwano jednej z: %2 + + + + Unknown option: %1 + CLI command syntax + Nieznana opcja: %1 + + + + Option %1 requires an argument. + CLI command syntax + Opcja %1 wymaga argumentu. + + + + string + CLI command syntax + Å‚aÅ„cuch + + + + Command line interface to SQLiteStudio, a SQLite manager. + Interfejs linii poleceÅ„ dla SQLiteStudio, menażera SQLite. + + + + Enables debug messages on standard error output. + Włącza wiadomoÅ›ci debugujÄ…ce na standardowym wyjÅ›ciu błędów. + + + + Enables Lemon parser debug messages for SQL code assistant. + Włącza wiadomoÅ›ci debugujÄ…ce analizatora Lemon dla asystenta kodu SQL. + + + + Lists plugins installed in the SQLiteStudio and quits. + Wypisuje listÄ™ zainstalowanych w SQLiteStudio wtyczek i wychodzi. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Wykonuje podany plik SQL (w tym wszystkie bogate funkcje wykonywania zapytaÅ„ w SQLiteStudio) na okreÅ›lonym pliku bazy danych i wychodzi z programu. Parametr bazy danych staje siÄ™ obowiÄ…zkowy, jeÅ›li ta opcja jest używana. + + + + SQL file + Plik SQL + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Kodowanie znaków do użycia podczas czytania pliku SQL (opcja -e). Użyj -cl aby wyÅ›wietlić dostÄ™pne kodowania. DomyÅ›lnie %1. + + + + codec + kodek + + + + Lists available codecs to be used with -c option and quits. + WyÅ›wietla dostÄ™pne kodowania do użycia z opcjÄ… -c i wychodzi z programu. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + Gdy używany jest razem z opcjÄ… -e, wykonanie nie zatrzyma siÄ™ na błędzie, ale bÄ™dzie kontynuowane do koÅ„ca, ignorujÄ…c błędy. + + + + file + plik + + + + Database file to open + Baza danych do otwarcia + + + + Invalid codec: %1. Use -cl option to list available codecs. + NieprawidÅ‚owe kodowanie: %1. Użyj opcji -cl, aby wyÅ›wietlić dostÄ™pne kodowania. + + + + Database file argument is mandatory when executing SQL file. + Argument pliku bazy danych jest obowiÄ…zkowy podczas wykonywania pliku SQL. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Nie można otworzyć okreÅ›lonej bazy danych do wykonania pliku SQL. Spróbuj użyć opcji -d, aby poznać wiÄ™cej szczegółów. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_BR.qm b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_BR.qm deleted file mode 100644 index c02994c..0000000 Binary files a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_BR.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_BR.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_BR.ts index 490addf..1b6bad9 100644 --- a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_BR.ts +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_BR.ts @@ -1,412 +1,425 @@ - - + + CLI - - Current database: %1 - + + Current database: %1 + Banco de dados atual: %1 - - No current working database is set. - + + No current working database is set. + Nenhuma banco de dados de trabalho atual está definido. - - Type %1 for help - + + Type %1 for help + Digite %1 para ajuda - - Database passed in command line parameters (%1) was already on the list under name: %2 - + + Database passed in command line parameters (%1) was already on the list under name: %2 + Banco de dados passado nos parâmetros da linha de comando (%1) já estava na lista com o nome: %2 - - Could not add database %1 to list. - + + Could not add database %1 to list. + Não foi possível adicionar o banco de dados %1 à lista. - - closed - + + closed + fechado - - + + CliCommand - - Usage: %1%2 - + + Usage: %1%2 + Uso: %1%2 - - + + CliCommandAdd - - Could not add database %1 to list. - + + Could not add database %1 to list. + Não foi possível adicionar o banco de dados %1 à lista. - - Database added: %1 - + + Database added: %1 + Banco de dados adicionado: %1 - - adds new database to the list - + + adds new database to the list + adiciona novo banco de dados à lista - - Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. - + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adiciona um banco de dados apontado por <path> com determinado <name> para listar a lista de bancos de dados. O <name> é apenas um nome simbólico que você pode referir mais tarde. Escolha qualquer nome exclusivo. Para lista de bancos de dados já estão na lista use o comando %1. - - name - CLI command syntax - + + name + CLI command syntax + nome - - path - CLI command syntax - + + path + CLI command syntax + caminho - - + + CliCommandCd - - Changed directory to: %1 - + + Changed directory to: %1 + Diretório alterado para: %1 - - Could not change directory to: %1 - + + Could not change directory to: %1 + Não foi possível mudar o diretório para: %1 - - changes current working directory - + + changes current working directory + muda o diretório atual de trabalho - - Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Um comando muito semelhante ao 'cd' conhecido do Unix system e Windows. É necessário que um argumento <path> seja aprovado, portanto chamar %1 sempre causará uma mudança do diretório. Para saber qual diretório de trabalho atual usa o comando %2 e para listar o conteúdo do diretório de trabalho atual use o comando %3. - - path - CLI command syntax - + + path + CLI command syntax + caminho - - + + CliCommandClose - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Não é possível chamar %1 quando nenhum banco de dados está definido como atual. Especifique o banco de dados atual com o comando %2 ou passe o nome do banco de dados para %3. - - - Connection to database %1 closed. - + + + Connection to database %1 closed. + Conexão ao banco de dados %1 fechado. - - No such database: %1. Use %2 to see list of known databases. - + + No such database: %1. Use %2 to see list of known databases. + Nenhum banco de dados: %1. Use %2 para ver a lista de bancos de dados conhecidos. - - closes given (or current) database - + + closes given (or current) database + fecha o banco de dados - - Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). - + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Fecha a conexão com a base de dados. Se o banco de dados já foi fechado, nada acontece. Se <name> for fornecido, deve ser o nome do banco de dados para fechar (como impresso pelo comando %1 ). O <name> não é fornecido, então a base de dados de trabalho atual está fechada (veja a ajuda %2 para detalhes). - - name - CLI command syntax - + + name + CLI command syntax + nome - - + + CliCommandDbList - - No current working database defined. - + + No current working database defined. + Nenhuma banco de dados de trabalho atual definido. - - Databases: - + + Databases: + Banco de dados: - - - Name - CLI db name column - + + + Name + CLI db name column + Nome - - - Open - CLI connection state column - + + + Open + CLI connection state column + Abrir - - - Closed - CLI connection state column - + + + Closed + CLI connection state column + Fechado - - - Connection - CLI connection state column - + + + Connection + CLI connection state column + Conexão - - - Database file path - + + + Database file path + Caminho do arquivo de banco de dados - - prints list of registered databases - + + prints list of registered databases + lista de bancos de dados registrados - - Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Mostra a lista de bancos de dados registrados no SQLiteStudio. Cada banco de dados da lista pode ser aberto ou fechado %1 avisa isso. O banco de dados de trabalho atual (conhecido como padrão de banco de dados) também está marcado na lista com '*' no início do seu nome. Consulte ajuda para usar o comando %2 para aprender sobre o banco de dados padrão. - - + + CliCommandDesc - - No working database is set. + + No working database is set. Call %1 command to set working database. Call %2 to see list of all databases. - + Nenhum banco de dados está definido. +Use %1 para definir o banco de dados ativo. +Use %2 para ver a lista de todos os bancos de dados. - - Database is not open. - + + Database is not open. + Banco de dados não está aberto. - - Cannot find table named: %1 - + + Cannot find table named: %1 + Não foi possível encontrar a tabela: %1 - - shows details about the table - + + shows details about the table + mostra detalhes sobre a tabela - - table - + + table + tabela - - Table: %1 - + + Table: %1 + Tabela: %1 - - Column name - + + Column name + Nome da coluna - - Data type - + + Data type + Tipo de dado - - Constraints - + + Constraints + Restrições - - Virtual table: %1 - + + Virtual table: %1 + Tabela virtual: %1 - - Construction arguments: - + + Construction arguments: + Argumentos de construção: - - No construction arguments were passed for this virtual table. - + + No construction arguments were passed for this virtual table. + Não foram apresentados argumentos de construção para esta tabela virtual. - - + + CliCommandDir - - lists directories and files in current working directory - + + lists directories and files in current working directory + lista diretórios e arquivos no diretório de trabalho atual - - This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. You can pass <pattern> with wildcard characters to filter output. - + Isso é muito semelhante ao comando 'dir' do Windows e 'ls' do sistema Unix. + +Você pode passar <pattern> como caracteres curinga para filtrar a saída. - - pattern - + + pattern + padrão - - + + CliCommandExit - - quits the application - + + quits the application + sair da aplicação - - Quits the application. Settings are stored in configuration file and will be restored on next startup. - + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Encerra o aplicativo. As configurações são armazenadas no arquivo de configuração e serão restauradas na próxima inicialização. - - + + CliCommandHelp - - shows this help message - + + shows this help message + mostra mensagem de ajuda - - Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. To see list of supported commands, type %2 without any arguments. When passing <command> name, you can skip special prefix character ('%3'). You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. - + Use %1 para aprender sobre certos comandos suportados pela interface de linha de comando (CLI) do SQLiteStudio. +Para ver a lista de comandos suportados, digite %2 sem quaisquer argumentos. + +Ao passar o nome <command> você pode pular o caractere de prefixo especial ('%3'). + +Você sempre pode executar qualquer comando com exatamente um único '--help' opção para ver a ajuda para esse comando. Uma alternativa para digitar: %1 <command>. - - command - CLI command syntax - + + command + CLI command syntax + comando - - No such command: %1 - + + No such command: %1 + Comando não encontrado: %1 - - Type '%1' for list of available commands. - + + Type '%1' for list of available commands. + Digite '%1' para a lista de comandos disponíveis. - - Usage: %1%2 - + + Usage: %1%2 + Uso: %1%2 - - Aliases: %1 - + + Aliases: %1 + Apelidos: %1 - - + + CliCommandHistory - - Current history limit is set to: %1 - + + Current history limit is set to: %1 + Limite do histórico atual está definido para: %1 - - prints history or erases it - + + prints history or erases it + mostrar ou apagar o histórico - - When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. When the -c or --clear option is passed, then the history gets erased. When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. Use -ql or --querylimit option to see the current limit value. - + Quando nenhum argumento foi utilizado, este comando imprime o histórico da linha de comando. Cada entrada do histórico é separada por uma linha horizontal, então as entradas multilinha são mais fáceis de ler. + +Quando a opção -c ou --clear é aprovada, então o histórico é apagado. +Quando a opção -l ou --limit é aprovada, ele define o novo limite de histórico. Requer um argumento adicional dizendo a quantas entradas você quer que a história se limite a +Use a opção -ql ou --querylimit para ver o valor limite atual. - - number - + + number + número - - Console history erased. - + + Console history erased. + Histórico apagado do console. - - Invalid number: %1 - + + Invalid number: %1 + Número inválido: %1 - - History limit set to %1 - + + History limit set to %1 + Limite de histórico definido para %1 - - + + CliCommandMode - - Current results printing mode: %1 - + + Current results printing mode: %1 + Modo de impressão atual de resultados: %1 - - Invalid results printing mode: %1 - + + Invalid results printing mode: %1 + Modo de impressão de resultados inválido: %1 - - New results printing mode: %1 - + + New results printing mode: %1 + Modo de impressão de novos resultados: %1 - - tells or changes the query results format - + + tells or changes the query results format + chama ou muda o formato dos resultados da consulta - - When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: - CLASSIC - columns are separated by a comma, not aligned, - FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, - COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), @@ -417,288 +430,308 @@ The CLASSIC mode is recommended if you want to see all the data, but you don&apo The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. -ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - + Quando chamado sem argumento, informa o formato de saída atual para os resultados de uma consulta. Quando o <mode> é passado, o modo é alterado para o dado. Os modos suportados são: +- CLASSIC - as colunas são separadas por vírgula, não alinhadas, +- FIXED - as colunas têm largura igual e fixa, sempre cabem na largura da janela do terminal, mas os dados nas colunas podem ser cortados, +- COLUMNS - como FIXED, mas mais inteligente (não use com grandes conjuntos de resultados, veja os detalhes abaixo), +- ROW - cada coluna da linha é exibida em nova linha, portanto, os dados completos são exibidos. + +O modo CLASSIC é recomendado se você quiser ver todos os dados, mas não quer desperdiçar linhas para cada coluna. Cada linha exibirá dados completos para cada coluna, mas isso também significa que as colunas não serão alinhadas entre si nas próximas linhas. O modo CLASSIC também não respeita a largura da janela do seu terminal (console), portanto, se os valores nas colunas forem mais largos que a janela, a linha será continuada nas próximas linhas. + +O modo FIXED é recomendado se você deseja uma saída legível e não se preocupa com valores de dados longos. As colunas serão alinhadas, tornando a saída uma boa tabela. A largura das colunas é calculada a partir da largura da janela do console e um número de colunas. + +O modo COLUMNS é semelhante ao modo FIXED, exceto que tenta ser inteligente e tornar as colunas com valores mais curtos mais finas, enquanto as colunas com valores mais longos obtêm mais espaço. As primeiras a encolher são as colunas com cabeçalhos mais longos (portanto, os nomes dos cabeçalhos devem ser cortados primeiro), depois as colunas com os valores mais longos são reduzidas, até o momento em que todas as colunas cabem na janela do terminal. +ATENÇÃO! O modo COLUMNS lê todos os resultados da consulta de uma vez para avaliar as larguras das colunas, portanto, é perigoso usar esse modo ao trabalhar com grandes conjuntos de resultados. Lembre-se de que este modo carregará todo o conjunto de resultados na memória. + +O modo ROW é recomendado se você precisa ver valores inteiros e não espera que muitas linhas sejam exibidas, porque este modo exibe uma linha de saída por cada coluna, então você obterá 10 linhas para uma única linha com 10 colunas, então, se você tiver 10 dessas linhas, obterá 100 linhas de saída (+1 linha extra por cada linha, para separar as linhas umas das outras). - - + + CliCommandNullValue - - Current NULL representation string: %1 - + + Current NULL representation string: %1 + String de denominação NULL atual: %1 - - tells or changes the NULL representation string - + + tells or changes the NULL representation string + chama ou muda a cadeia de representação NULL - - If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. - + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + Se nenhum argumento foi aprovado, utilizar a denominação de NULL atual (ou seja, o que é impresso no lugar de valores NULL nos resultados de consultas). Se o argumento é dado, então será usado como uma nova string a ser usada para denominação NULL. - - + + CliCommandOpen - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Não é possível chamar %1 quando nenhum banco de dados está definido como atual. Especifique o banco de dados atual com o comando %2 ou passe o nome do banco de dados para %3. - - Could not add database %1 to list. - + + Could not add database %1 to list. + Não foi possível adicionar o banco de dados %1 à lista. - - File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. - + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + O arquivo %1 não existe em %2. Não é possível abrir banco de dados inexistente com o comando %3. Para criar um novo banco de dados, use o comando %4. + - - Database %1 has been open and set as the current working database. - + + Database %1 has been open and set as the current working database. + Banco de dados %1 foi aberto e definido como a base de dados atual de trabalho. - - opens database connection - + + opens database connection + abre conexão com base de dados - - Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. - + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Abrir conexão com a base de dados. Se nenhum argumento adicional foi aprovado, então a conexão está aberta para o banco de dados padrão atual (veja a ajuda para %1 para detalhes). No entanto, se um argumento foi aprovado, pode ser <name> da base de dados registrada para abrir. ou pode ser <path> para o arquivo de banco de dados para abrir. No segundo caso, o <path> é registrado na lista com um nome gerado, mas apenas para o período da sessão de aplicação atual. Depois de reiniciar o aplicativo, esse banco de dados não será restaurado na lista. - - name - CLI command syntax - + + name + CLI command syntax + nome - - path - CLI command syntax - + + path + CLI command syntax + caminho - - + + CliCommandPwd - - prints the current working directory - + + prints the current working directory + mostra o diretório de trabalho atual - - This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. - + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + Isso é o mesmo que 'pwd' comando em sistemas Unix e 'cd' comando sem argumentos no Windows. Imprime o diretório de trabalho atual. Você pode alterar o diretório de trabalho atual com %1 comando e você também pode listar o conteúdo do diretório de trabalho atual com %2 comando. - - + + CliCommandRemove - - No such database: %1 - + + No such database: %1 + Banco de dados não existe: %1 - - Database removed: %1 - + + Database removed: %1 + Banco de dados removido: %1 - - New current database set: - + + New current database set: + Novo banco de dados definido: - - removes database from the list - + + removes database from the list + remove o banco de dados da lista - - Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. - + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Remove <name> do banco de dados da lista de bancos de dados registrados. Se o banco de dados não estava na lista (ver %1 comando), então a mensagem de erro é impressa e nada mais acontece. - - name - CLI command syntax - + + name + CLI command syntax + nome - - + + CliCommandSql - - No working database is set. + + No working database is set. Call %1 command to set working database. Call %2 to see list of all databases. - + Nenhum banco de dados está definido. +Execute %1 para definir o banco de dados ativo. +Execute %2 para ver a lista de todos os bancos de dados. - - Database is not open. - + + Database is not open. + Banco de dados não está aberto. - - executes SQL query - + + executes SQL query + executa consulta SQL - - This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. - + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + Este comando é executado toda vez que você digitar a consulta SQL no prompt de comando. Executa a consulta no banco de dados de trabalho atual (veja ajuda para %1 para detalhes). Não faz sentido executar este comando explicitamente. Em vez disso, digite a consulta SQL no prompt de comando, sem qualquer comando prefixado. - - sql - CLI command syntax - + + sql + CLI command syntax + sql - - - Too many columns to display in %1 mode. - + + + Too many columns to display in %1 mode. + Muitas colunas para serem exibidas no modo %1. - - Row %1 - + + Row %1 + Linha %1 - - Query execution error: %1 - + + Query execution error: %1 + Erro na execução da consulta: %1 - - + + CliCommandTables - - No such database: %1. Use %2 to see list of known databases. - + + No such database: %1. Use %2 to see list of known databases. + Nenhum banco de dados: %1. Use %2 para ver a lista de bancos de dados existentes. - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Não é possível chamar %1 quando nenhum banco de dados está definido como atual. Especifique o banco de dados atual com o comando %2 ou passe o nome do banco de dados para %3. - - Database %1 is closed. - + + Database %1 is closed. + Banco de dados %1 está fechado. - - - Database - + + + Database + Banco de dados - - Table - + + Table + Tabela - - prints list of tables in the database - + + prints list of tables in the database + lista as tabelas do banco de dados - - Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. When the -s option is given, then system tables are also listed. - + Mostra a lista das tabelas de acordo com <database> ou no atual banco de dados de trabalho. Note que o <database> deve ser o nome do banco de dados registrado (ver %1). A lista de saída inclui todas as tabelas de qualquer outro banco de dados anexado à base de dados solicitada. +Quando a opção -s é dada, então as tabelas do sistema também são listadas. - - database - CLI command syntax - + + database + CLI command syntax + banco de dados - - + + CliCommandTree - - No current working database is selected. Use %1 to define one and then run %2. - + + No current working database is selected. Use %1 to define one and then run %2. + Nenhum banco de dados selecionado. Use %1 para definir um banco de dados e depois execute %2. - - Tables - + + Tables + Tabelas - - Views - + + Views + Visualizações - - Columns - + + Columns + Colunas - - Indexes - + + Indexes + Ãndices - - - Triggers - + + + Triggers + Triggers - - prints all objects in the database as a tree - + + prints all objects in the database as a tree + mostra todos os objetos no banco do dados como uma árvore - - Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. When -c option is given, then also columns will be listed under each table. When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. - + Imprime todos os objetos (tabelas, indexes, gatilhos e visualizações) que estão no banco de dados como uma árvore. A árvore é muito parecida com a que você pode ver no cliente GUI do SQLiteStudio. +Quando a opção -c é dada, então as colunas também serão listadas sob cada tabela. +Quando a opção -s é dada, então também objetos do sistema serão impressos (sqlite_* tabelas, índices de auto-incremento, etc). +O argumento do banco de dados é opcional e, se fornecido, apenas o banco de dados informado será impresso. Este não é um nome de banco de dados registrado, mas em vez disso é um nome de banco de dados SQLite interno, como 'main', 'temp', ou qualquer nome de banco de dados anexado. Para imprimir árvore para outro banco de dados registrado, chame %1 primeiro para mudar o banco de dados de trabalho e, em seguida, use o comando %2. - - + + CliCommandUse - - No current database selected. - + + No current database selected. + Nenhum banco de dados selecionado. - - - Current database: %1 - + + + Current database: %1 + Banco de dados atual: %1 - - No such database: %1 - + + No such database: %1 + Banco de dados não existe: %1 - - changes default working database - + + changes default working database + definir o banco de dados padrão - - Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. What is current working database? When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). @@ -709,80 +742,136 @@ The default database can be selected in various ways: - by passing registered database name to the application startup parameters, - by restoring previously selected default database from saved configuration, - or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. - + Altera a base de dados atual de trabalho para <name>. Se o banco de dados <name> não estiver registrado, então a mensagem de erro é mostrada e nenhuma alteração é feita. + +O que é uma base de dados em funcionamento? +Quando você digita uma consulta SQL a ser executada, ela é executada no banco de dados padrão, que é também conhecida como a banco de dados de trabalho atual. A maioria dos comandos relacionados ao banco de dados também pode funcionar usando o banco de dados padrão, se nenhum banco de dados foi fornecido em seus argumentos. A base de dados atual é sempre identificada pela linha de comando. O banco de dados padrão é sempre definido (a menos que não haja nenhum banco de dados na lista). + +O banco de dados padrão pode ser selecionado de várias maneiras: +- usando o comando %1 +- passando o nome do arquivo de banco de dados para o aplicativo parâmetros de inicialização, +- passando o nome do banco de dados registrado para os parâmetros de inicialização do aplicativo, +- restaurando o banco de dados padrão selecionado anteriormente a partir da configuração salva, +- ou quando o banco de dados padrão não foi selecionado por nenhum dos itens acima então primeiro banco de dados da lista de bancos de dados registrados torna-se o padrão. - - name - CLI command syntax - + + name + CLI command syntax + nome - - + + QObject - - Insufficient number of arguments. - + + Insufficient number of arguments. + Número insuficiente de argumentos. - - Too many arguments. - + + Too many arguments. + Poucos argumentos. - - Invalid argument value: %1. + + Invalid argument value: %1. Expected one of: %2 - + Valor do argumento inválido: %1. +Espera-se um de: %2 + + + + Unknown option: %1 + CLI command syntax + Opção desconhecida: %1 + + + + Option %1 requires an argument. + CLI command syntax + A opção %1 requer um argumento. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Interface da linha de comando para SQLiteStudio, um gerenciador para SQLite. + + + + Enables debug messages on standard error output. + Habilita mensagens de depuração padrão na saída de erro. + + + + Enables Lemon parser debug messages for SQL code assistant. + Habilita mensagens de depuração do analisador Lemon no assistente de código SQL. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lista os plugins instalados no SQLiteStudio e encerrados. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executa o arquivo SQL fornecido (incluindo todos os recursos avançados do executor de consulta SQLiteStudio 's) no arquivo de banco de dados especificado e fecha a rotina. O parâmetro do banco de dados torna-se obrigatório se esta opção for utilizada. + + + + SQL file + Arquivo SQL - - Unknown option: %1 - CLI command syntax - + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Codificação de caracteres utilizada ao ler o arquivo SQL (-e option). Use -cl para listar os codecs disponíveis. O padrão é %1. - - Option %1 requires an argument. - CLI command syntax - + + codec + Codec - - string - CLI command syntax - + + Lists available codecs to be used with -c option and quits. + Lista de codecs disponíveis para serem usados com opção -c e encerramento. - - Command line interface to SQLiteStudio, a SQLite manager. - + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + Quando usado em conjunto com a opção -e, a execução não parará em um erro, mas sim continuará até o fim, ignorando erros. - - Enables debug messages on standard error output. - + + file + arquivo - - Enables Lemon parser debug messages for SQL code assistant. - + + Database file to open + Arquivo do banco de dados para abrir - - Lists plugins installed in the SQLiteStudio and quits. - + + Invalid codec: %1. Use -cl option to list available codecs. + Codec inválido: %1. Use a opção -cl para listar codecs disponíveis. - - file - + + Database file argument is mandatory when executing SQL file. + O argumento do arquivo de banco de dados é obrigatório para executar arquivo SQL. - - Database file to open - + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. - + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_PT.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_PT.ts new file mode 100644 index 0000000..91d4419 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_pt_PT.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ro_RO.qm b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ro_RO.qm deleted file mode 100644 index 2856eb9..0000000 Binary files a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ro_RO.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ro_RO.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ro_RO.ts index e4821ef..2c3b290 100644 --- a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ro_RO.ts +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ro_RO.ts @@ -1,412 +1,425 @@ - - + + CLI - - Current database: %1 - + + Current database: %1 + Current database: %1 - - No current working database is set. - + + No current working database is set. + No current working database is set. - - Type %1 for help - + + Type %1 for help + Type %1 for help - - Database passed in command line parameters (%1) was already on the list under name: %2 - + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 - - Could not add database %1 to list. - + + Could not add database %1 to list. + Could not add database %1 to list. - - closed - + + closed + closed - - + + CliCommand - - Usage: %1%2 - + + Usage: %1%2 + Usage: %1%2 - - + + CliCommandAdd - - Could not add database %1 to list. - + + Could not add database %1 to list. + Could not add database %1 to list. - - Database added: %1 - + + Database added: %1 + Database added: %1 - - adds new database to the list - + + adds new database to the list + adds new database to the list - - Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. - + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. - - name - CLI command syntax - + + name + CLI command syntax + name - - path - CLI command syntax - + + path + CLI command syntax + path - - + + CliCommandCd - - Changed directory to: %1 - + + Changed directory to: %1 + Changed directory to: %1 - - Could not change directory to: %1 - + + Could not change directory to: %1 + Could not change directory to: %1 - - changes current working directory - + + changes current working directory + changes current working directory - - Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - - path - CLI command syntax - + + path + CLI command syntax + path - - + + CliCommandClose - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - Connection to database %1 closed. - + + + Connection to database %1 closed. + Connection to database %1 closed. - - No such database: %1. Use %2 to see list of known databases. - + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. - - closes given (or current) database - + + closes given (or current) database + closes given (or current) database - - Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). - + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). - - name - CLI command syntax - + + name + CLI command syntax + name - - + + CliCommandDbList - - No current working database defined. - + + No current working database defined. + No current working database defined. - - Databases: - + + Databases: + Databases: - - - Name - CLI db name column - + + + Name + CLI db name column + Name - - - Open - CLI connection state column - + + + Open + CLI connection state column + Open - - - Closed - CLI connection state column - + + + Closed + CLI connection state column + Closed - - - Connection - CLI connection state column - + + + Connection + CLI connection state column + Connection - - - Database file path - + + + Database file path + Database file path - - prints list of registered databases - + + prints list of registered databases + prints list of registered databases - - Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - - + + CliCommandDesc - - No working database is set. + + No working database is set. Call %1 command to set working database. Call %2 to see list of all databases. - + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. - - Database is not open. - + + Database is not open. + Database is not open. - - Cannot find table named: %1 - + + Cannot find table named: %1 + Cannot find table named: %1 - - shows details about the table - + + shows details about the table + shows details about the table - - table - + + table + table - - Table: %1 - + + Table: %1 + Table: %1 - - Column name - + + Column name + Column name - - Data type - + + Data type + Data type - - Constraints - + + Constraints + Constraints - - Virtual table: %1 - + + Virtual table: %1 + Virtual table: %1 - - Construction arguments: - + + Construction arguments: + Construction arguments: - - No construction arguments were passed for this virtual table. - + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. - - + + CliCommandDir - - lists directories and files in current working directory - + + lists directories and files in current working directory + lists directories and files in current working directory - - This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. You can pass <pattern> with wildcard characters to filter output. - + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. - - pattern - + + pattern + pattern - - + + CliCommandExit - - quits the application - + + quits the application + quits the application - - Quits the application. Settings are stored in configuration file and will be restored on next startup. - + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. - - + + CliCommandHelp - - shows this help message - + + shows this help message + shows this help message - - Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. To see list of supported commands, type %2 without any arguments. When passing <command> name, you can skip special prefix character ('%3'). You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. - + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. - - command - CLI command syntax - + + command + CLI command syntax + command - - No such command: %1 - + + No such command: %1 + No such command: %1 - - Type '%1' for list of available commands. - + + Type '%1' for list of available commands. + Type '%1' for list of available commands. - - Usage: %1%2 - + + Usage: %1%2 + Usage: %1%2 - - Aliases: %1 - + + Aliases: %1 + Aliases: %1 - - + + CliCommandHistory - - Current history limit is set to: %1 - + + Current history limit is set to: %1 + Current history limit is set to: %1 - - prints history or erases it - + + prints history or erases it + prints history or erases it - - When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. When the -c or --clear option is passed, then the history gets erased. When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. Use -ql or --querylimit option to see the current limit value. - + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. - - number - + + number + number - - Console history erased. - + + Console history erased. + Console history erased. - - Invalid number: %1 - + + Invalid number: %1 + Invalid number: %1 - - History limit set to %1 - + + History limit set to %1 + History limit set to %1 - - + + CliCommandMode - - Current results printing mode: %1 - + + Current results printing mode: %1 + Current results printing mode: %1 - - Invalid results printing mode: %1 - + + Invalid results printing mode: %1 + Invalid results printing mode: %1 - - New results printing mode: %1 - + + New results printing mode: %1 + New results printing mode: %1 - - tells or changes the query results format - + + tells or changes the query results format + tells or changes the query results format - - When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: - CLASSIC - columns are separated by a comma, not aligned, - FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, - COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), @@ -417,288 +430,307 @@ The CLASSIC mode is recommended if you want to see all the data, but you don&apo The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. -ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - - + + CliCommandNullValue - - Current NULL representation string: %1 - + + Current NULL representation string: %1 + Current NULL representation string: %1 - - tells or changes the NULL representation string - + + tells or changes the NULL representation string + tells or changes the NULL representation string - - If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. - + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. - - + + CliCommandOpen - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - Could not add database %1 to list. - + + Could not add database %1 to list. + Could not add database %1 to list. - - File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. - + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. - - Database %1 has been open and set as the current working database. - + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. - - opens database connection - + + opens database connection + opens database connection - - Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. - + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. - - name - CLI command syntax - + + name + CLI command syntax + name - - path - CLI command syntax - + + path + CLI command syntax + path - - + + CliCommandPwd - - prints the current working directory - + + prints the current working directory + prints the current working directory - - This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. - + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. - - + + CliCommandRemove - - No such database: %1 - + + No such database: %1 + No such database: %1 - - Database removed: %1 - + + Database removed: %1 + Database removed: %1 - - New current database set: - + + New current database set: + New current database set: - - removes database from the list - + + removes database from the list + removes database from the list - - Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. - + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. - - name - CLI command syntax - + + name + CLI command syntax + name - - + + CliCommandSql - - No working database is set. + + No working database is set. Call %1 command to set working database. Call %2 to see list of all databases. - + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. - - Database is not open. - + + Database is not open. + Database is not open. - - executes SQL query - + + executes SQL query + executes SQL query - - This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. - + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. - - sql - CLI command syntax - + + sql + CLI command syntax + sql - - - Too many columns to display in %1 mode. - + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. - - Row %1 - + + Row %1 + Row %1 - - Query execution error: %1 - + + Query execution error: %1 + Query execution error: %1 - - + + CliCommandTables - - No such database: %1. Use %2 to see list of known databases. - + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - Database %1 is closed. - + + Database %1 is closed. + Database %1 is closed. - - - Database - + + + Database + Database - - Table - + + Table + Table - - prints list of tables in the database - + + prints list of tables in the database + prints list of tables in the database - - Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. When the -s option is given, then system tables are also listed. - + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. - - database - CLI command syntax - + + database + CLI command syntax + database - - + + CliCommandTree - - No current working database is selected. Use %1 to define one and then run %2. - + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. - - Tables - + + Tables + Tables - - Views - + + Views + Views - - Columns - + + Columns + Columns - - Indexes - + + Indexes + Indexes - - - Triggers - + + + Triggers + Triggers - - prints all objects in the database as a tree - + + prints all objects in the database as a tree + prints all objects in the database as a tree - - Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. When -c option is given, then also columns will be listed under each table. When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. - + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. - - + + CliCommandUse - - No current database selected. - + + No current database selected. + No current database selected. - - - Current database: %1 - + + + Current database: %1 + Current database: %1 - - No such database: %1 - + + No such database: %1 + No such database: %1 - - changes default working database - + + changes default working database + changes default working database - - Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. What is current working database? When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). @@ -709,80 +741,136 @@ The default database can be selected in various ways: - by passing registered database name to the application startup parameters, - by restoring previously selected default database from saved configuration, - or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. - + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. - - name - CLI command syntax - + + name + CLI command syntax + name - - + + QObject - - Insufficient number of arguments. - + + Insufficient number of arguments. + Insufficient number of arguments. - - Too many arguments. - + + Too many arguments. + Too many arguments. - - Invalid argument value: %1. + + Invalid argument value: %1. Expected one of: %2 - + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file - - Unknown option: %1 - CLI command syntax - + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. - - Option %1 requires an argument. - CLI command syntax - + + codec + codec - - string - CLI command syntax - + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. - - Command line interface to SQLiteStudio, a SQLite manager. - + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. - - Enables debug messages on standard error output. - + + file + file - - Enables Lemon parser debug messages for SQL code assistant. - + + Database file to open + Database file to open - - Lists plugins installed in the SQLiteStudio and quits. - + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. - - file - + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. - - Database file to open - + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. - + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru.qm b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru.qm deleted file mode 100644 index 9943370..0000000 Binary files a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru.ts deleted file mode 100644 index 379b0e0..0000000 --- a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru.ts +++ /dev/null @@ -1,829 +0,0 @@ - - - - - CLI - - - Current database: %1 - Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных: %1 - - - - No current working database is set. - Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных не определена. - - - - Type %1 for help - Введите %1 Ð´Ð»Ñ Ð²Ñ‹Ð·Ð¾Ð²Ð° Ñправки - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - База данных, Ð¿ÐµÑ€ÐµÐ´Ð°Ð½Ð½Ð°Ñ Ñ‡ÐµÑ€ÐµÐ· аргументы командной Ñтроки (%1), уже находитÑÑ Ð² ÑпиÑке под именем %2 - - - - Could not add database %1 to list. - Ðевозможно добавить базу данных %1 в ÑпиÑок. - - - - closed - закрыта - - - - CliCommand - - - Usage: %1%2 - ИÑпользование: %1%2 - - - - CliCommandAdd - - - Could not add database %1 to list. - Ðевозможно добавить базу данных %1 в ÑпиÑок. - - - - Database added: %1 - Добавлена база данных: %1 - - - - adds new database to the list - добавлÑет новую базу данных в ÑпиÑок - - - - Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. - ДобавлÑет базу данных, раÑположенную по указанному <пути> под указанным <именем> в ÑпиÑок баз данных. <имÑ> — Ñто обычное Ñимвольное имÑ, которое в дальнейшем можно будет иÑпользовать. Выберите любое уникальное имÑ. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ ÑпиÑка баз данных воÑпользуйтеÑÑŒ командой %1. - - - - name - CLI command syntax - Ð¸Ð¼Ñ - - - - path - CLI command syntax - путь - - - - CliCommandCd - - - Changed directory to: %1 - Изменён каталог на: %1 - - - - Could not change directory to: %1 - Ðевозможно Ñменить каталог на: %1 - - - - changes current working directory - изменение текущего рабочего каталога - - - - Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - Ðналог команды 'cd' из ÑиÑтем Unix и Windows. ТребуетÑÑ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ðµ параметра <путь>, поÑтому вызов %1 вÑегда приводит к изменению каталога. Ð”Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ рабочего каталога воÑпользуйтеÑÑŒ командой %2. Ð”Ð»Ñ Ð²Ñ‹Ð²Ð¾Ð´Ð° Ñодержимого текущего рабочего каталога воÑпользуйтеÑÑŒ командой %3. - - - - path - CLI command syntax - путь - - - - CliCommandClose - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - Ðевозможно вызвать %1, еÑли ни одна база данных не ÑвлÑетÑÑ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹. Укажите текущую базу данных, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñƒ %2 или укажите Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных при вызове %3. - - - - - Connection to database %1 closed. - Соединение Ñ Ð±Ð°Ð·Ð¾Ð¹ данных %1 закрыто. - - - - No such database: %1. Use %2 to see list of known databases. - Ðе найдена база данных: %1. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÑпиÑка доÑтупных баз данных воÑпользуйтеÑÑŒ командой %2. - - - - closes given (or current) database - закрывает указанную (или текущую) базу данных - - - - Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). - Закрывает Ñоединение Ñ Ð±Ð°Ð·Ð¾Ð¹ данных. ЕÑли база данных уже закрыта, ничего не произойдёт. ЕÑли указано <имÑ>, оно должно ÑоответÑтвовать имени закрываемой базы данных (которое выводитÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¾Ð¹ %1). ЕÑли Ð¸Ð¼Ñ Ð½Ðµ указано, будет закрыта Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных (Ñмотрите Ñправку по команде %2 Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñтей). - - - - name - CLI command syntax - Ð¸Ð¼Ñ - - - - CliCommandDbList - - - No current working database defined. - Ðе указана Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных - - - - Databases: - Базы данных: - - - - - Name - CLI db name column - Ð˜Ð¼Ñ - - - - - Open - CLI connection state column - Открыто - - - - - Closed - CLI connection state column - Закрыто - - - - - Connection - CLI connection state column - Соединение - - - - - Database file path - Путь к файлу базы данных - - - - prints list of registered databases - выводит ÑпиÑок зарегиÑтрированных баз данных - - - - Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - Выводит ÑпиÑок баз данных, зарегиÑтрированных в SQLiteStudio. ÐšÐ°Ð¶Ð´Ð°Ñ Ð±Ð°Ð·Ð° данных может быть либо открыта, либо закрыта, %1 Ñто также указывает. Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных (она же база данных по умолчанию) дополнительно отмечена Ñимволом '*' в начале имени. Смотрите Ñправку по команде %2 Ð´Ð»Ñ Ñведений о базе данных по умолчанию. - - - - CliCommandDesc - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - Ðе указана Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных. -Укажите рабочую базу данных командой %1. -Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñмотра ÑпиÑка баз данных воÑпользуйтеÑÑŒ командой %2. - - - - Database is not open. - База данных не открыта. - - - - Cannot find table named: %1 - Ðе удалоÑÑŒ найти таблицу Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ %1 - - - - shows details about the table - отображает ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ таблице - - - - table - таблица - - - - Table: %1 - Таблица: %1 - - - - Column name - Ð˜Ð¼Ñ Ñтолбца - - - - Data type - Тип данных - - - - Constraints - ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ - - - - Virtual table: %1 - Ð’Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°: %1 - - - - Construction arguments: - Параметры ÑозданиÑ: - - - - No construction arguments were passed for this virtual table. - Ðе указаны параметры ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð´Ð»Ñ Ñтой виртуальной таблицы. - - - - CliCommandDir - - - lists directories and files in current working directory - выводит ÑпиÑок каталогов и файлов в текущем рабочем каталоге - - - - This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. - -You can pass <pattern> with wildcard characters to filter output. - Ðналог команды 'dir' в Windows и 'ls' в ÑиÑтемах Unix. - -Ð’Ñ‹ можете указать <маÑку> c иÑпользованием подÑтановочных Ñимволов Ð´Ð»Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ð¸ вывода. - - - - pattern - маÑка - - - - CliCommandExit - - - quits the application - выход из Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ - - - - Quits the application. Settings are stored in configuration file and will be restored on next startup. - ОÑущеÑтвлÑет выход из приложениÑ. ÐаÑтройки ÑохранÑÑŽÑ‚ÑÑ Ð² конфигурационном файле и воÑÑтановÑÑ‚ÑÑ Ð¿Ñ€Ð¸ Ñледующем запуÑке. - - - - CliCommandHelp - - - shows this help message - вывод Ñтого ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ - - - - Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. -To see list of supported commands, type %2 without any arguments. - -When passing <command> name, you can skip special prefix character ('%3'). - -You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. - ИÑпользуйте %1 Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ñведений о командах, поддерживаемых интерфейÑом командной Ñтроки (CLI) SQLiteStudio. -Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñмотра ÑпиÑка доÑтупных команд, введите %2 без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð¾Ð². - -При указании имени <команды> можно не указывать префикÑный Ñимвол ('%3'). - -Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ñправки по команде вы также можете выполнить команду Ñ ÐµÐ´Ð¸Ð½Ñтвенным ключом '--help'. Это альтернатива вводу: %1 <команда>. - - - - command - CLI command syntax - команда - - - - No such command: %1 - Ðе найдена команда: %1 - - - - Type '%1' for list of available commands. - Введите '%1' Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÑпиÑка доÑтупных команд. - - - - Usage: %1%2 - ИÑпользование: %1%2 - - - - Aliases: %1 - ПÑевдонимы: %1 - - - - CliCommandHistory - - - Current history limit is set to: %1 - Текущий лимит иÑтории: %1 - - - - prints history or erases it - выводит иÑторию или очищает её - - - - When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. - -When the -c or --clear option is passed, then the history gets erased. -When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. -Use -ql or --querylimit option to see the current limit value. - При вызове без аргументов, Ð´Ð°Ð½Ð½Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° выводит иÑторию командной Ñтроки. ÐšÐ°Ð¶Ð´Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ иÑтории отделена горизонтальной линией Ð´Ð»Ñ Ð¾Ð±Ð»ÐµÐ³Ñ‡ÐµÐ½Ð¸Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¼Ð½Ð¾Ð³Ð¾Ñтрочных запиÑей. - -При вызове Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ -Ñ Ð¸Ð»Ð¸ --clear иÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¾Ñ‡Ð¸Ñ‰Ð°ÐµÑ‚ÑÑ. -При вызове Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ -l или --limit уÑтанавливаетÑÑ Ð½Ð¾Ð²Ñ‹Ð¹ лимит на количеÑтво запиÑей в иÑтории. Ðеобходим дополнительный аргумент, указывающий Ñколько запиÑей необходимо хранить в иÑтории. -Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñмотра текущего лимита запиÑей вызовите команду Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ -ql или --querylimit. - - - - number - количеÑтво - - - - Console history erased. - ИÑÑ‚Ð¾Ñ€Ð¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð¹ Ñтроки очищена. - - - - Invalid number: %1 - Ðекорректное количеÑтво: %1 - - - - History limit set to %1 - Лимит иÑтории уÑтановлен в количеÑтве %1 - - - - CliCommandMode - - - Current results printing mode: %1 - Текущий режим вывода результатов: %1 - - - - Invalid results printing mode: %1 - Ðекорректный режим вывода результатов: %1 - - - - New results printing mode: %1 - Ðовый режим вывода результатов: %1 - - - - tells or changes the query results format - отображает или изменÑет формат вывода результатов запроÑа - - - - When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: -- CLASSIC - columns are separated by a comma, not aligned, -- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, -- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), -- ROW - each column from the row is displayed in new line, so the full data is displayed. - -The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. - -The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. - -The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. -ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. - -The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - При вызове без аргументов отображает текущий формат вывода результатов запроÑа. ЕÑли указан <режим>, режим менÑетÑÑ Ð½Ð° переданный. Поддерживаемые режимы: -- CLASSIC - Ñтолбцы разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой, не выравниваютÑÑ, -- FIXED - ширина Ñтолбцов одинакова и зафикÑирована, они вÑегда умещаютÑÑ Ð² ширину окна терминала, однако данные в Ñтолбцах могут быть обрезаны, -- COLUMNS - аналогичен FIXED, но более умный (не иÑпользуйте при огромных размерах результатов, подробнее Ñм. ниже), -- ROW - каждый Ñтолбец Ñтроки выводитÑÑ Ñ Ð½Ð¾Ð²Ð¾Ð¹ Ñтрочки, так что отображаютÑÑ Ð¿Ð¾Ð»Ð½Ñ‹Ðµ данные. - -Режим CLASSIC рекомендован еÑли необходимо отобразить вÑе данные, не Ð·Ð°Ñ‚Ñ€Ð°Ñ‡Ð¸Ð²Ð°Ñ Ð¾Ñ‚Ð´ÐµÐ»ÑŒÐ½Ñ‹Ðµ Ñтрочки на каждый Ñтолбец. ÐšÐ°Ð¶Ð´Ð°Ñ Ñтрока будет Ñодержать полные данные каждого Ñтолбца, что приведёт к отÑутÑтвию Ð²Ñ‹Ñ€Ð°Ð²Ð½Ð¸Ð²Ð°Ð½Ð¸Ñ Ñтолбцов в Ñледующих Ñтроках. Также в режиме CLASSIC не учитываетÑÑ ÑˆÐ¸Ñ€Ð¸Ð½Ð° окна терминала (конÑоли), поÑтому еÑли Ñтолбцы шире окна, оÑтаток Ñтроки будет выведен на новых Ñтрочках. - -Режим FIXED рекомендован еÑли необходимо получить читабельный вывод, Ð½ÐµÐ²Ð·Ð¸Ñ€Ð°Ñ Ð½Ð° длинные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñтолбцов. Столбцы будут выровнены в аккуратную таблицу. Ширина Ñтолбцов будет раÑÑчитана иÑÑ…Ð¾Ð´Ñ Ð¸Ð· ширины окна конÑоли и чиÑла Ñтолбцов. - -Режим COLUMNS аналогичен режиму FIXED Ñ Ñ‚Ð¾Ð¹ разницей, что он умнее и Ñделает Ñтолбцы Ñ ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ¸Ð¼Ð¸ значениÑми поуже, оÑтавлÑÑ Ð±Ð¾Ð»ÑŒÑˆÐµ меÑта Ñтолбцам Ñ Ð´Ð»Ð¸Ð½Ð½Ñ‹Ð¼Ð¸ значениÑми. Первыми будут ужаты Ñтолбцы Ñ Ñамыми длинными заголовками (Ñ‚.е. длинные заголовки будут обрезаны в первую очередь), затем будут ужиматьÑÑ Ñтолбцы Ñ Ñамыми длинными значениÑми, пока таблица не впишетÑÑ Ð² окно терминала. -Ð’ÐИМÐÐИЕ! Ð”Ð»Ñ Ñ€Ð°ÑÑчёта ширины Ñтолбцов в режиме COLUMNS ÑчитываетÑÑ Ñразу веÑÑŒ результат запроÑа, поÑтому его опаÑно иÑпользовать при огромных размерах результатов. Учтите, что в Ñтом режиме веÑÑŒ результат запроÑа загружаетÑÑ Ð² оперативную памÑть. - -Режим ROW рекомендован еÑли необходимо отобразить вÑе данные, при Ñтом чиÑло выводимых Ñтрок невелико, так как в Ñтом режиме каждый Ñтолбец выводитÑÑ Ð½Ð° отдельной Ñтрочке; например вывод единÑтвенной Ñтроки из 10 Ñтолбцов займёт 10 Ñтрочек, 10 таких Ñтрок займут 100 Ñтрочек вывода (+1 Ñтрочка на каждую Ñтроку Ð´Ð»Ñ Ð²Ñ‹Ð²Ð¾Ð´Ð° Ñ€Ð°Ð·Ð´ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ Ñтрок). - - - - CliCommandNullValue - - - Current NULL representation string: %1 - Текущее предÑтавление Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL: %1 - - - - tells or changes the NULL representation string - отображает или уÑтанавливает предÑтавление Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL - - - - If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. - При вызове без аргументов отображает текущее предÑтавление Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL (Ñ‚.е. что выводитÑÑ Ð²Ð¼ÐµÑто Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL в результатах запроÑа). ЕÑли указан аргумент, он будет иÑпользован как Ñтрока Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL. - - - - CliCommandOpen - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - Ðевозможно вызвать %1, еÑли ни одна база данных не ÑвлÑетÑÑ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹. Укажите текущую базу данных, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñƒ %2 или укажите Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных при вызове %3. - - - - Could not add database %1 to list. - Ðевозможно добавить базу данных %1 в ÑпиÑок. - - - - File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. - Файл %1 не ÑущеÑтвует в %2. Ðевозможно открыть неÑущеÑтвующую базу данных командой %3. Ð”Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð¹ базы данных воÑпользуйтеÑÑŒ командой %4. - - - - Database %1 has been open and set as the current working database. - База данных %1 была открыта и уÑтановлена в качеÑтве текущей рабочей базы данных. - - - - opens database connection - открывает Ñоединение Ñ Ð±Ð°Ð·Ð¾Ð¹ данных - - - - Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. - Открывает Ñоединение Ñ Ð±Ð°Ð·Ð¾Ð¹ данных. При вызове без аргументов, Ñоединение открываетÑÑ Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ базы данных по умолчанию (Ñм. Ñправку по команде %1 Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñтей). ЕÑли же аргумент указан,он может быть <именем> зарегиÑтрированной базы данных или <путём> к файлу базы данных. Во втором Ñлучае, база данных по указанному <пути> будет зарегиÑтрирована в ÑпиÑке под Ñгенерированным именем, но только на Ð²Ñ€ÐµÐ¼Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ ÑеÑÑии в приложении. ПоÑле перезапуÑка Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° в ÑпиÑке воÑÑтановлена не будет. - - - - name - CLI command syntax - Ð¸Ð¼Ñ - - - - path - CLI command syntax - путь - - - - CliCommandPwd - - - prints the current working directory - отображение текущего рабочего каталога - - - - This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. - Ðналог команды 'pwd' в ÑиÑтемах Unix и команды 'cd' без аргументов в Windows. Команда отображает текущий рабочий каталог. Ð’Ñ‹ можете Ñменить текущий рабочий каталог командой %1, а также вывеÑти Ñодержимое текущего рабочего каталога командой %2. - - - - CliCommandRemove - - - No such database: %1 - Ðе найдена база данных: %1 - - - - Database removed: %1 - Удалена база данных: %1 - - - - New current database set: - УÑтановлена Ð½Ð¾Ð²Ð°Ñ Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных: - - - - removes database from the list - удаление базы данных из ÑпиÑка - - - - Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. - УдалÑет базу данных Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ð¼ <именем> из ÑпиÑка зарегиÑтрированных баз данных. ЕÑли указанной базы данных нет в ÑпиÑке (Ñм. команду %1), отображаетÑÑ Ñообщение об ошибке и больше ничего не проиÑходит. - - - - name - CLI command syntax - Ð¸Ð¼Ñ - - - - CliCommandSql - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - Ðе указана Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных. Укажите рабочую базу данных командой %1. Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñмотра ÑпиÑка баз данных воÑпользуйтеÑÑŒ командой %2. - - - - Database is not open. - База данных не открыта. - - - - executes SQL query - выполнение запроÑа SQL - - - - This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. - Эта команда выполнÑетÑÑ ÐºÐ°Ð¶Ð´Ñ‹Ð¹ раз, когда вы вводите Ð·Ð°Ð¿Ñ€Ð¾Ñ SQL в командную Ñтроку. Она выполнÑет Ð·Ð°Ð¿Ñ€Ð¾Ñ Ðº текущей рабочей базе данных (Ñм. Ñправку к команде %1 Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñтей). Ðе нужно Ñвно вызвать Ñту команду. ПроÑто вводите Ð·Ð°Ð¿Ñ€Ð¾Ñ SQL в командную Ñтроку без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹. - - - - sql - CLI command syntax - sql - - - - - Too many columns to display in %1 mode. - Слишком много Ñтолбцов Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð² режиме %1. - - - - Row %1 - Строка %1 - - - - Query execution error: %1 - Ошибка Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа: %1 - - - - CliCommandTables - - - No such database: %1. Use %2 to see list of known databases. - Ðе найдена база данных: %1. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÑпиÑка доÑтупных баз данных воÑпользуйтеÑÑŒ командой %2. - - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - Ðевозможно вызвать %1, еÑли ни одна база данных не ÑвлÑетÑÑ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹. Укажите текущую базу данных, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñƒ %2 или укажите Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных при вызове %3. - - - - Database %1 is closed. - База данных %1 закрыта. - - - - - Database - База данных - - - - Table - Таблица - - - - prints list of tables in the database - отображает ÑпиÑок таблиц в базе данных - - - - Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. -When the -s option is given, then system tables are also listed. - Отображает ÑпиÑок таблиц в указанной <базе данных> или в текущей рабочей базе данных. Учтите, что <база данных> должна быть именем зарегиÑтрированной базы данных (Ñм. %1). Ð’ ÑпиÑок тажк выводÑÑ‚ÑÑ Ð²Ñе таблицы из баз данных, приÑоединённых к запрашиваемой базе данных. -При указании ключа -s также выводÑÑ‚ÑÑ ÑиÑтемные таблицы. - - - - database - CLI command syntax - база данных - - - - CliCommandTree - - - No current working database is selected. Use %1 to define one and then run %2. - Ðе выбрана Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных. Укажите рабочую базу данных командой %1, затем выполните команду %2. - - - - Tables - Таблицы - - - - Views - ПредÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ - - - - Columns - Столбцы - - - - Indexes - ИндекÑÑ‹ - - - - - Triggers - Триггеры - - - - prints all objects in the database as a tree - отображение вÑех объектов базы данных в виде дерева - - - - Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. -When -c option is given, then also columns will be listed under each table. -When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). -The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. - Отображает вÑе объекты (таблицы, индекÑÑ‹, триггеры и предÑтавлениÑ) базы данных в виде дерева. Структура дерева аналогична тому, которое отображаетÑÑ Ð² GUI клиенте SQLiteStudio. -При вызове Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ -c также будут выведены Ñтолбцы под каждой таблицей. -При вызове Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ -s также будут выведены ÑиÑтемные объекты (таблицы sqlite_*, индекÑÑ‹ автоинкремента и Ñ‚.д.). -При вызове Ñ Ð½ÐµÐ¾Ð±Ñзательным аргументом 'база данных' будут выведены объекты только указнной базы данных. Под 'базой данных' подразумеваетÑÑ Ð½Ðµ зарегиÑтрированное Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных, а внутреннее Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных SQLite, например 'main', 'temp' или Ð¸Ð¼Ñ Ð¿Ñ€Ð¸Ñоединённной базы данных. Ð”Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´ÐµÑ€ÐµÐ²Ð° другой зарегиÑтрированной базы данных, Ñперва Ñмените рабочую базу данных командой %1, а затем воÑпользуйтеÑÑŒ командой %2. - - - - CliCommandUse - - - No current database selected. - Ðе выбрана Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных. - - - - - Current database: %1 - Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных: %1 - - - - No such database: %1 - Ðе найдена база данных: %1 - - - - changes default working database - изменение рабочей базы данных по умолчанию - - - - Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. - -What is current working database? -When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). - -The default database can be selected in various ways: -- using %1 command, -- by passing database file name to the application startup parameters, -- by passing registered database name to the application startup parameters, -- by restoring previously selected default database from saved configuration, -- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. - ИзменÑет текущую рабочую базы данных на базу данных Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ð¼ <именем>. ЕÑли <имÑ> базы данных не зарегиÑтрировано в приложении, отображаетÑÑ Ñообщение об ошибке и Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ðµ производÑÑÑ‚ÑÑ. - -Что такое Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных? -Когда вы вводите Ð·Ð°Ð¿Ñ€Ð¾Ñ SQL Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ, он выполнÑетÑÑ Ðº базе данных по умолчанию, также извеÑтной как текущей рабочей базе данных. БольшинÑтво отноÑÑщихÑÑ Ðº базам данных команд могут выполнÑтьÑÑ Ðº базе данных по умолчанию, еÑли Ð´Ñ€ÑƒÐ³Ð°Ñ Ð±Ð°Ð·Ð° данных не указана в качеÑтве аргумента. Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных вÑегда отображаетÑÑ Ð² приглашении командной Ñтроки. База данных по умолчанию вÑегда определена (еÑли ÑпиÑок баз данных не пуÑÑ‚). - -База данных по умолчанию может быть задана разными ÑпоÑобами: -- иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñƒ %1, -- указав путь к файлу базы данных в аргументах при запуÑке приложениÑ, -- указав Ð¸Ð¼Ñ Ð·Ð°Ñ€ÐµÐ³Ð¸Ñтрированной базы данных в аргументах при запуÑке приложениÑ, -- воÑÑтановив предыдущую выбранную базу данных из Ñохранённой конфигурации, -- или еÑли база данных по умолчанию не была выбрана любым их вышеуказанных ÑпоÑобов, базой данных по умолчанию ÑтановитÑÑ Ð¿ÐµÑ€Ð²Ð°Ñ Ð·Ð°Ñ€ÐµÐ³Ð¸ÑÑ‚Ñ€Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных в ÑпиÑке. - - - - name - CLI command syntax - Ð¸Ð¼Ñ - - - - QObject - - - Insufficient number of arguments. - ÐедоÑтаточное количеÑтво аргументов. - - - - Too many arguments. - Слишком много аргументов. - - - - Invalid argument value: %1. -Expected one of: %2 - Ðекорректное значение аргумента: %1. -ДопуÑтимые значениÑ: %2 - - - - Unknown option: %1 - CLI command syntax - ÐеизвеÑтный ключ: %1 - - - - Option %1 requires an argument. - CLI command syntax - Ключ %1 требует ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð°. - - - - string - CLI command syntax - Ñтрока - - - - Command line interface to SQLiteStudio, a SQLite manager. - Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð¹ Ñтроки Ð´Ð»Ñ SQLiteStudio, менеджера баз данных SQLite. - - - - Enables debug messages on standard error output. - Включает вывод отладочных Ñообщений в Ñтандартный поток ошибок. - - - - Enables Lemon parser debug messages for SQL code assistant. - Включает вывод отладочных Ñообщений анализатора Lemon Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ SQL кода. - - - - Lists plugins installed in the SQLiteStudio and quits. - Выводит ÑпиÑок уÑтановленных в SQLiteStudio модулей и оÑущеÑтвлÑет выход. - - - - file - файл - - - - Database file to open - Файл базы данных Ð´Ð»Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð¸Ñ - - - diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru_RU.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru_RU.ts new file mode 100644 index 0000000..0f84f9a --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_ru_RU.ts @@ -0,0 +1,874 @@ + + + + + CLI + + + Current database: %1 + Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных: %1 + + + + No current working database is set. + Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных не определена. + + + + Type %1 for help + Введите %1 Ð´Ð»Ñ Ð²Ñ‹Ð·Ð¾Ð²Ð° Ñправки + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + База данных, Ð¿ÐµÑ€ÐµÐ´Ð°Ð½Ð½Ð°Ñ Ñ‡ÐµÑ€ÐµÐ· аргументы командной Ñтроки (%1), уже находитÑÑ Ð² ÑпиÑке под именем %2 + + + + Could not add database %1 to list. + Ðевозможно добавить базу данных %1 в ÑпиÑок. + + + + closed + закрыта + + + + CliCommand + + + Usage: %1%2 + ИÑпользование: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Ðевозможно добавить базу данных %1 в ÑпиÑок. + + + + Database added: %1 + Добавлена база данных: %1 + + + + adds new database to the list + добавлÑет новую базу данных в ÑпиÑок + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + ДобавлÑет базу данных, раÑположенную по указанному <пути> под указанным <именем> в ÑпиÑок баз данных. <имÑ> — Ñто обычное Ñимвольное имÑ, которое в дальнейшем можно будет иÑпользовать. Выберите любое уникальное имÑ. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ ÑпиÑка баз данных воÑпользуйтеÑÑŒ командой %1. + + + + name + CLI command syntax + Ð¸Ð¼Ñ + + + + path + CLI command syntax + путь + + + + CliCommandCd + + + Changed directory to: %1 + Изменён каталог на: %1 + + + + Could not change directory to: %1 + Ðевозможно Ñменить каталог на: %1 + + + + changes current working directory + изменение текущего рабочего каталога + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Ðналог команды 'cd' из ÑиÑтем Unix и Windows. ТребуетÑÑ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ðµ параметра <путь>, поÑтому вызов %1 вÑегда приводит к изменению каталога. Ð”Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ рабочего каталога воÑпользуйтеÑÑŒ командой %2. Ð”Ð»Ñ Ð²Ñ‹Ð²Ð¾Ð´Ð° Ñодержимого текущего рабочего каталога воÑпользуйтеÑÑŒ командой %3. + + + + path + CLI command syntax + путь + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Ðевозможно вызвать %1, еÑли ни одна база данных не ÑвлÑетÑÑ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹. Укажите текущую базу данных, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñƒ %2 или укажите Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных при вызове %3. + + + + + Connection to database %1 closed. + Соединение Ñ Ð±Ð°Ð·Ð¾Ð¹ данных %1 закрыто. + + + + No such database: %1. Use %2 to see list of known databases. + Ðе найдена база данных: %1. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÑпиÑка доÑтупных баз данных воÑпользуйтеÑÑŒ командой %2. + + + + closes given (or current) database + закрывает указанную (или текущую) базу данных + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Закрывает Ñоединение Ñ Ð±Ð°Ð·Ð¾Ð¹ данных. ЕÑли база данных уже закрыта, ничего не произойдёт. ЕÑли указано <имÑ>, оно должно ÑоответÑтвовать имени закрываемой базы данных (которое выводитÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¾Ð¹ %1). ЕÑли Ð¸Ð¼Ñ Ð½Ðµ указано, будет закрыта Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных (Ñмотрите Ñправку по команде %2 Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñтей). + + + + name + CLI command syntax + Ð¸Ð¼Ñ + + + + CliCommandDbList + + + No current working database defined. + Ðе указана Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных. + + + + Databases: + Базы данных: + + + + + Name + CLI db name column + Ð˜Ð¼Ñ + + + + + Open + CLI connection state column + Открыто + + + + + Closed + CLI connection state column + Закрыто + + + + + Connection + CLI connection state column + Соединение + + + + + Database file path + Путь к файлу базы данных + + + + prints list of registered databases + выводит ÑпиÑок зарегиÑтрированных баз данных + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Выводит ÑпиÑок баз данных, зарегиÑтрированных в SQLiteStudio. ÐšÐ°Ð¶Ð´Ð°Ñ Ð±Ð°Ð·Ð° данных может быть либо открыта, либо закрыта, %1 Ñто также указывает. Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных (она же база данных по умолчанию) дополнительно отмечена Ñимволом '*' в начале имени. Смотрите Ñправку по команде %2 Ð´Ð»Ñ Ñведений о базе данных по умолчанию. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + Ðе указана Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных. +Укажите рабочую базу данных командой %1. +Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñмотра ÑпиÑка баз данных воÑпользуйтеÑÑŒ командой %2. + + + + Database is not open. + База данных не открыта. + + + + Cannot find table named: %1 + Ðе удалоÑÑŒ найти таблицу Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ %1 + + + + shows details about the table + отображает ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ таблице + + + + table + таблица + + + + Table: %1 + Таблица: %1 + + + + Column name + Ð˜Ð¼Ñ Ñтолбца + + + + Data type + Тип данных + + + + Constraints + ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ + + + + Virtual table: %1 + Ð’Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°: %1 + + + + Construction arguments: + Параметры ÑозданиÑ: + + + + No construction arguments were passed for this virtual table. + Ðе указаны параметры ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð´Ð»Ñ Ñтой виртуальной таблицы. + + + + CliCommandDir + + + lists directories and files in current working directory + выводит ÑпиÑок каталогов и файлов в текущем рабочем каталоге + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + Ðналог команды 'dir' в Windows и 'ls' в ÑиÑтемах Unix. + +Ð’Ñ‹ можете указать <маÑку> c иÑпользованием подÑтановочных Ñимволов Ð´Ð»Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ð¸ вывода. + + + + pattern + маÑка + + + + CliCommandExit + + + quits the application + выход из Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + ОÑущеÑтвлÑет выход из приложениÑ. ÐаÑтройки ÑохранÑÑŽÑ‚ÑÑ Ð² конфигурационном файле и воÑÑтановÑÑ‚ÑÑ Ð¿Ñ€Ð¸ Ñледующем запуÑке. + + + + CliCommandHelp + + + shows this help message + вывод Ñтого ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + ИÑпользуйте %1 Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ñведений о командах, поддерживаемых интерфейÑом командной Ñтроки (CLI) SQLiteStudio. +Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñмотра ÑпиÑка доÑтупных команд, введите %2 без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð¾Ð². + +При указании имени <команды> можно не указывать префикÑный Ñимвол ('%3'). + +Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ñправки по команде вы также можете выполнить команду Ñ ÐµÐ´Ð¸Ð½Ñтвенным ключом '--help'. Это альтернатива вводу: %1 <команда>. + + + + command + CLI command syntax + команда + + + + No such command: %1 + Ðе найдена команда: %1 + + + + Type '%1' for list of available commands. + Введите '%1' Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÑпиÑка доÑтупных команд. + + + + Usage: %1%2 + ИÑпользование: %1%2 + + + + Aliases: %1 + ПÑевдонимы: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Текущий лимит иÑтории: %1 + + + + prints history or erases it + выводит иÑторию или очищает её + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + При вызове без аргументов, Ð´Ð°Ð½Ð½Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° выводит иÑторию командной Ñтроки. ÐšÐ°Ð¶Ð´Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ иÑтории отделена горизонтальной линией Ð´Ð»Ñ Ð¾Ð±Ð»ÐµÐ³Ñ‡ÐµÐ½Ð¸Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¼Ð½Ð¾Ð³Ð¾Ñтрочных запиÑей. + +При вызове Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ -Ñ Ð¸Ð»Ð¸ --clear иÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¾Ñ‡Ð¸Ñ‰Ð°ÐµÑ‚ÑÑ. +При вызове Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ -l или --limit уÑтанавливаетÑÑ Ð½Ð¾Ð²Ñ‹Ð¹ лимит на количеÑтво запиÑей в иÑтории. Ðеобходим дополнительный аргумент, указывающий Ñколько запиÑей необходимо хранить в иÑтории. +Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñмотра текущего лимита запиÑей вызовите команду Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ -ql или --querylimit. + + + + number + количеÑтво + + + + Console history erased. + ИÑÑ‚Ð¾Ñ€Ð¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð¹ Ñтроки очищена. + + + + Invalid number: %1 + Ðекорректное количеÑтво: %1 + + + + History limit set to %1 + Лимит иÑтории уÑтановлен в количеÑтве %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Текущий режим вывода результатов: %1 + + + + Invalid results printing mode: %1 + Ðекорректный режим вывода результатов: %1 + + + + New results printing mode: %1 + Ðовый режим вывода результатов: %1 + + + + tells or changes the query results format + отображает или изменÑет формат вывода результатов запроÑа + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + При вызове без аргумента, отображает текущий формат вывода Ð´Ð»Ñ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð² запроÑа. При указании <mode> режим ÑменÑетÑÑ Ð½Ð° выбранный. ПоддерживаютÑÑ Ñледующие режимы: +- CLASSIC - Ñтолбцы разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой, без выравниваниÑ, +- FIXED - у Ñтолбцов Ñ€Ð°Ð²Ð½Ð°Ñ Ð¸ фикÑÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ ÑˆÐ¸Ñ€Ð¸Ð½Ð°, они вÑегда умещаютÑÑ Ð² окно терминала по ширине, но чаÑть данных в Ñтолбцах может быть обрезана, +- COLUMNS - как FIXED, но умнее (не иÑпользуйте при огромных размерах результата запроÑа, Ñм. подробноÑти ниже), +- ROW - каждый Ñтолбец каждой Ñтроки данных отображаетÑÑ Ð½Ð° отдельной Ñтроке терминала, данные отображаютÑÑ Ð¿Ð¾Ð»Ð½Ð¾Ñтью. + +Режим CLASSIC рекомендован еÑли вы хотите увидеть вÑе данные, но не хотите тратить новую Ñтроку в терминале Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ Ñтолбца. ÐšÐ°Ð¶Ð´Ð°Ñ Ñтрока будет отображать вÑе данные из вÑех Ñтолбцов, однако Ñтолбцы не будут выровнены между Ñобой по Ñтрокам. Режим CLASSIC также не учитывает ширину окна терминала (конÑоли), поÑтому еÑли Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð² Ñтолбцах шире окна, Ð²Ñ‹Ð²Ð¾Ð´Ð¸Ð¼Ð°Ñ Ñтрока данных будет продолжена в Ñледующих Ñтроках окна терминала. + +Режим FIXED рекомендован еÑли вам нужен читабельный вывод и Ð²Ð°Ñ Ð½Ðµ беÑпокоÑÑ‚ длинные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð² данных. Столбцы будут выровнены, Ð¾Ð±Ñ€Ð°Ð·ÑƒÑ Ð½Ð° выходе аккуратную таблицу. Ширина Ñтолбцов раÑÑчитываетÑÑ Ð¸ÑÑ…Ð¾Ð´Ñ Ð¸Ð· ширины окна конÑоли и чиÑла Ñтолбцов. + +Режим COLUMNS похож на режим FIXED Ñ Ñ‚ÐµÐ¼ иÑключением, что он ÑтараетÑÑ Ð¿Ð¾Ñтупать по-умному и делать Ñтолбцы Ñ ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ¸Ð¼Ð¸ значениÑми поуже, чтобы Ñтолбцам Ñ Ð´Ð»Ð¸Ð½Ð½Ñ‹Ð¼Ð¸ значениÑми доÑталоÑÑŒ больше меÑта. Сначала урезаютÑÑ Ñтолбцы Ñ Ñамыми длинными заголовками (так что имена Ñтолбцов будут урезаны в первую очередь), затем урезаютÑÑ Ñтолбцы Ñ Ñамыми длинными значениÑми, пока вÑе Ñтолбцы не умеÑÑ‚ÑÑ‚ÑÑ Ð² окне терминала. +Ð’ÐИМÐÐИЕ! Режим COLUMNS Ñразу Ñчитывает вÑе результаты запроÑа чтобы раÑÑчитать ширину Ñтолбцов, поÑтому опаÑно иÑпользовать Ñтот режим при работе Ñ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð°Ð¼Ð¸ огромных размеров. Учтите, что Ñтот режим загружает веÑÑŒ результат запроÑа в памÑть. + +Режим ROW рекомендован еÑли вам нужно видеть вÑе данные, и вы не ожидаете большого чиÑла Ñтрок данных в результате, поÑкольку в Ñтом режиме каждый Ñтолбец выводитÑÑ Ð½Ð° отдельной Ñтроке терминала, поÑтому вывод Ñтроки данных Ñ 10 Ñтолбцами займёт 10 Ñтрок в терминале, и еÑли таких Ñтрок Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸ будет 10, то получитÑÑ 100 Ñтрок вывода (+1 Ñтрока в терминале на каждую Ñтроку данных, Ð´Ð»Ñ Ñ€Ð°Ð·Ð´ÐµÐ»ÐµÐ½Ð¸Ñ). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Текущее предÑтавление Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL: %1 + + + + tells or changes the NULL representation string + отображает или уÑтанавливает предÑтавление Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + При вызове без аргументов отображает текущее предÑтавление Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL (Ñ‚.е. что выводитÑÑ Ð²Ð¼ÐµÑто Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL в результатах запроÑа). ЕÑли указан аргумент, он будет иÑпользован как Ñтрока Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Ðевозможно вызвать %1, еÑли ни одна база данных не ÑвлÑетÑÑ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹. Укажите текущую базу данных, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñƒ %2 или укажите Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных при вызове %3. + + + + Could not add database %1 to list. + Ðевозможно добавить базу данных %1 в ÑпиÑок. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + Файл %1 не ÑущеÑтвует в %2. Ðевозможно открыть неÑущеÑтвующую базу данных командой %3. Ð”Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð¹ базы данных воÑпользуйтеÑÑŒ командой %4. + + + + Database %1 has been open and set as the current working database. + База данных %1 была открыта и уÑтановлена в качеÑтве текущей рабочей базы данных. + + + + opens database connection + открывает Ñоединение Ñ Ð±Ð°Ð·Ð¾Ð¹ данных + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Открывает Ñоединение Ñ Ð±Ð°Ð·Ð¾Ð¹ данных. При вызове без аргументов, Ñоединение открываетÑÑ Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ базы данных по умолчанию (Ñм. Ñправку по команде %1 Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñтей). ЕÑли же аргумент указан,он может быть <именем> зарегиÑтрированной базы данных или <путём> к файлу базы данных. Во втором Ñлучае, база данных по указанному <пути> будет зарегиÑтрирована в ÑпиÑке под Ñгенерированным именем, но только на Ð²Ñ€ÐµÐ¼Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ ÑеÑÑии в приложении. ПоÑле перезапуÑка Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° в ÑпиÑке воÑÑтановлена не будет. + + + + name + CLI command syntax + Ð¸Ð¼Ñ + + + + path + CLI command syntax + путь + + + + CliCommandPwd + + + prints the current working directory + отображение текущего рабочего каталога + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + Ðналог команды 'pwd' в ÑиÑтемах Unix и команды 'cd' без аргументов в Windows. Команда отображает текущий рабочий каталог. Ð’Ñ‹ можете Ñменить текущий рабочий каталог командой %1, а также вывеÑти Ñодержимое текущего рабочего каталога командой %2. + + + + CliCommandRemove + + + No such database: %1 + Ðе найдена база данных: %1 + + + + Database removed: %1 + Удалена база данных: %1 + + + + New current database set: + УÑтановлена Ð½Ð¾Ð²Ð°Ñ Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных: + + + + removes database from the list + удаление базы данных из ÑпиÑка + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + УдалÑет базу данных Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ð¼ <именем> из ÑпиÑка зарегиÑтрированных баз данных. ЕÑли указанной базы данных нет в ÑпиÑке (Ñм. команду %1), отображаетÑÑ Ñообщение об ошибке и больше ничего не проиÑходит. + + + + name + CLI command syntax + Ð¸Ð¼Ñ + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + Ðе указана Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных. Укажите рабочую базу данных командой %1. Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñмотра ÑпиÑка баз данных воÑпользуйтеÑÑŒ командой %2. + + + + Database is not open. + База данных не открыта. + + + + executes SQL query + выполнение запроÑа SQL + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + Эта команда выполнÑетÑÑ ÐºÐ°Ð¶Ð´Ñ‹Ð¹ раз, когда вы вводите Ð·Ð°Ð¿Ñ€Ð¾Ñ SQL в командную Ñтроку. Она выполнÑет Ð·Ð°Ð¿Ñ€Ð¾Ñ Ðº текущей рабочей базе данных (Ñм. Ñправку к команде %1 Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñтей). Ðе нужно Ñвно вызвать Ñту команду. ПроÑто вводите Ð·Ð°Ð¿Ñ€Ð¾Ñ SQL в командную Ñтроку без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Слишком много Ñтолбцов Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð² режиме %1. + + + + Row %1 + Строка %1 + + + + Query execution error: %1 + Ошибка Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + Ðе найдена база данных: %1. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÑпиÑка доÑтупных баз данных воÑпользуйтеÑÑŒ командой %2. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Ðевозможно вызвать %1, еÑли ни одна база данных не ÑвлÑетÑÑ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹. Укажите текущую базу данных, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñƒ %2 или укажите Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных при вызове %3. + + + + Database %1 is closed. + База данных %1 закрыта. + + + + + Database + База данных + + + + Table + Таблица + + + + prints list of tables in the database + отображает ÑпиÑок таблиц в базе данных + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Отображает ÑпиÑок таблиц в указанной <базе данных> или в текущей рабочей базе данных. Учтите, что <база данных> должна быть именем зарегиÑтрированной базы данных (Ñм. %1). Ð’ ÑпиÑок тажк выводÑÑ‚ÑÑ Ð²Ñе таблицы из баз данных, приÑоединённых к запрашиваемой базе данных. +При указании ключа -s также выводÑÑ‚ÑÑ ÑиÑтемные таблицы. + + + + database + CLI command syntax + база данных + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + Ðе выбрана Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных. Укажите рабочую базу данных командой %1, затем выполните команду %2. + + + + Tables + Таблицы + + + + Views + ПредÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ + + + + Columns + Столбцы + + + + Indexes + ИндекÑÑ‹ + + + + + Triggers + Триггеры + + + + prints all objects in the database as a tree + отображение вÑех объектов базы данных в виде дерева + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Отображает вÑе объекты (таблицы, индекÑÑ‹, триггеры и предÑтавлениÑ) базы данных в виде дерева. Структура дерева аналогична тому, которое отображаетÑÑ Ð² GUI клиенте SQLiteStudio. +При вызове Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ -c также будут выведены Ñтолбцы под каждой таблицей. +При вызове Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ -s также будут выведены ÑиÑтемные объекты (таблицы sqlite_*, индекÑÑ‹ автоинкремента и Ñ‚.д.). +При вызове Ñ Ð½ÐµÐ¾Ð±Ñзательным аргументом 'база данных' будут выведены объекты только указнной базы данных. Под 'базой данных' подразумеваетÑÑ Ð½Ðµ зарегиÑтрированное Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных, а внутреннее Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных SQLite, например 'main', 'temp' или Ð¸Ð¼Ñ Ð¿Ñ€Ð¸Ñоединённной базы данных. Ð”Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´ÐµÑ€ÐµÐ²Ð° другой зарегиÑтрированной базы данных, Ñперва Ñмените рабочую базу данных командой %1, а затем воÑпользуйтеÑÑŒ командой %2. + + + + CliCommandUse + + + No current database selected. + Ðе выбрана Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных. + + + + + Current database: %1 + Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных: %1 + + + + No such database: %1 + Ðе найдена база данных: %1 + + + + changes default working database + изменение рабочей базы данных по умолчанию + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + ИзменÑет текущую рабочую базы данных на базу данных Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ð¼ <именем>. ЕÑли <имÑ> базы данных не зарегиÑтрировано в приложении, отображаетÑÑ Ñообщение об ошибке и Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ðµ производÑÑÑ‚ÑÑ. + +Что такое Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð±Ð°Ð·Ð° данных? +Когда вы вводите Ð·Ð°Ð¿Ñ€Ð¾Ñ SQL Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ, он выполнÑетÑÑ Ðº базе данных по умолчанию, также извеÑтной как текущей рабочей базе данных. БольшинÑтво отноÑÑщихÑÑ Ðº базам данных команд могут выполнÑтьÑÑ Ðº базе данных по умолчанию, еÑли Ð´Ñ€ÑƒÐ³Ð°Ñ Ð±Ð°Ð·Ð° данных не указана в качеÑтве аргумента. Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð±Ð°Ð·Ð° данных вÑегда отображаетÑÑ Ð² приглашении командной Ñтроки. База данных по умолчанию вÑегда определена (еÑли ÑпиÑок баз данных не пуÑÑ‚). + +База данных по умолчанию может быть задана разными ÑпоÑобами: +- иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñƒ %1, +- указав путь к файлу базы данных в аргументах при запуÑке приложениÑ, +- указав Ð¸Ð¼Ñ Ð·Ð°Ñ€ÐµÐ³Ð¸Ñтрированной базы данных в аргументах при запуÑке приложениÑ, +- воÑÑтановив предыдущую выбранную базу данных из Ñохранённой конфигурации, +- или еÑли база данных по умолчанию не была выбрана любым их вышеуказанных ÑпоÑобов, базой данных по умолчанию ÑтановитÑÑ Ð¿ÐµÑ€Ð²Ð°Ñ Ð·Ð°Ñ€ÐµÐ³Ð¸ÑÑ‚Ñ€Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных в ÑпиÑке. + + + + name + CLI command syntax + Ð¸Ð¼Ñ + + + + QObject + + + Insufficient number of arguments. + ÐедоÑтаточное количеÑтво аргументов. + + + + Too many arguments. + Слишком много аргументов. + + + + Invalid argument value: %1. +Expected one of: %2 + Ðекорректное значение аргумента: %1. +ДопуÑтимые значениÑ: %2 + + + + Unknown option: %1 + CLI command syntax + ÐеизвеÑтный ключ: %1 + + + + Option %1 requires an argument. + CLI command syntax + Ключ %1 требует ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð°. + + + + string + CLI command syntax + Ñтрока + + + + Command line interface to SQLiteStudio, a SQLite manager. + Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð¹ Ñтроки Ð´Ð»Ñ SQLiteStudio, менеджера баз данных SQLite. + + + + Enables debug messages on standard error output. + Включает вывод отладочных Ñообщений в Ñтандартный поток ошибок. + + + + Enables Lemon parser debug messages for SQL code assistant. + Включает вывод отладочных Ñообщений анализатора Lemon Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ SQL кода. + + + + Lists plugins installed in the SQLiteStudio and quits. + Выводит ÑпиÑок уÑтановленных в SQLiteStudio модулей и оÑущеÑтвлÑет выход. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + ВыполнÑет предоÑтавленный SQL-файл (иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð²Ñе многочиÑленные функции иÑÐ¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов SQLiteStudio) на указанном файле базы данных и выходит. При иÑпользовании Ñтой опции обÑзательно указывать параметр database. + + + + SQL file + Файл SQL + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Кодировка текÑта, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ð¿Ñ€Ð¸ чтении файла SQL (Ð¾Ð¿Ñ†Ð¸Ñ -e). ИÑпользуйте -cl Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÑпиÑка доÑтупных кодеков. По умолчанию иÑпользуетÑÑ %1. + + + + codec + кодек + + + + Lists available codecs to be used with -c option and quits. + Выводит ÑпиÑок доÑтупных кодеков Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ Ð¾Ð¿Ñ†Ð¸ÐµÐ¹ -c и выходит. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + При иÑпользовании вмеÑте Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð¼ -e выполнение не оÑтанавливаетÑÑ Ð¿Ñ€Ð¸ возникновении ошибки, а продолжаетÑÑ Ð´Ð¾ конца, Ð¸Ð³Ð½Ð¾Ñ€Ð¸Ñ€ÑƒÑ Ð²Ñе ошибки. + + + + file + файл + + + + Database file to open + Файл базы данных Ð´Ð»Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð¸Ñ + + + + Invalid codec: %1. Use -cl option to list available codecs. + Ðеверный кодек: %1. ИÑпользуйте параметр -cl Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÑпиÑка доÑтупных кодеков. + + + + Database file argument is mandatory when executing SQL file. + Ðргумент Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð¼ базы данных ÑвлÑетÑÑ Ð¾Ð±Ñзательным при выполнении SQL-файла. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Ðе удалоÑÑŒ открыть указанную базу данных Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð° SQL. Ð’Ñ‹ можете воÑпользоватьÑÑ Ð¾Ð¿Ñ†Ð¸ÐµÐ¹ -d, чтобы узнать больше деталей. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk.qm b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk.qm deleted file mode 100644 index 1776294..0000000 Binary files a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk.ts deleted file mode 100644 index e7e2933..0000000 --- a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk.ts +++ /dev/null @@ -1,788 +0,0 @@ - - - - - CLI - - - Current database: %1 - - - - - No current working database is set. - - - - - Type %1 for help - - - - - Database passed in command line parameters (%1) was already on the list under name: %2 - - - - - Could not add database %1 to list. - - - - - closed - - - - - CliCommand - - - Usage: %1%2 - - - - - CliCommandAdd - - - Could not add database %1 to list. - - - - - Database added: %1 - - - - - adds new database to the list - - - - - Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. - - - - - name - CLI command syntax - - - - - path - CLI command syntax - - - - - CliCommandCd - - - Changed directory to: %1 - - - - - Could not change directory to: %1 - - - - - changes current working directory - - - - - Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - - - - - path - CLI command syntax - - - - - CliCommandClose - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - - Connection to database %1 closed. - - - - - No such database: %1. Use %2 to see list of known databases. - - - - - closes given (or current) database - - - - - Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). - - - - - name - CLI command syntax - - - - - CliCommandDbList - - - No current working database defined. - - - - - Databases: - - - - - - Name - CLI db name column - - - - - - Open - CLI connection state column - - - - - - Closed - CLI connection state column - - - - - - Connection - CLI connection state column - - - - - - Database file path - - - - - prints list of registered databases - - - - - Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - - - - - CliCommandDesc - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - - - - - Database is not open. - - - - - Cannot find table named: %1 - - - - - shows details about the table - - - - - table - - - - - Table: %1 - - - - - Column name - - - - - Data type - - - - - Constraints - - - - - Virtual table: %1 - - - - - Construction arguments: - - - - - No construction arguments were passed for this virtual table. - - - - - CliCommandDir - - - lists directories and files in current working directory - - - - - This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. - -You can pass <pattern> with wildcard characters to filter output. - - - - - pattern - - - - - CliCommandExit - - - quits the application - - - - - Quits the application. Settings are stored in configuration file and will be restored on next startup. - - - - - CliCommandHelp - - - shows this help message - - - - - Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. -To see list of supported commands, type %2 without any arguments. - -When passing <command> name, you can skip special prefix character ('%3'). - -You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. - - - - - command - CLI command syntax - - - - - No such command: %1 - - - - - Type '%1' for list of available commands. - - - - - Usage: %1%2 - - - - - Aliases: %1 - - - - - CliCommandHistory - - - Current history limit is set to: %1 - - - - - prints history or erases it - - - - - When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. - -When the -c or --clear option is passed, then the history gets erased. -When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. -Use -ql or --querylimit option to see the current limit value. - - - - - number - - - - - Console history erased. - - - - - Invalid number: %1 - - - - - History limit set to %1 - - - - - CliCommandMode - - - Current results printing mode: %1 - - - - - Invalid results printing mode: %1 - - - - - New results printing mode: %1 - - - - - tells or changes the query results format - - - - - When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: -- CLASSIC - columns are separated by a comma, not aligned, -- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, -- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), -- ROW - each column from the row is displayed in new line, so the full data is displayed. - -The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. - -The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. - -The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. -ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. - -The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - - - - - CliCommandNullValue - - - Current NULL representation string: %1 - - - - - tells or changes the NULL representation string - - - - - If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. - - - - - CliCommandOpen - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - Could not add database %1 to list. - - - - - File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. - - - - - Database %1 has been open and set as the current working database. - - - - - opens database connection - - - - - Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. - - - - - name - CLI command syntax - - - - - path - CLI command syntax - - - - - CliCommandPwd - - - prints the current working directory - - - - - This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. - - - - - CliCommandRemove - - - No such database: %1 - - - - - Database removed: %1 - - - - - New current database set: - - - - - removes database from the list - - - - - Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. - - - - - name - CLI command syntax - - - - - CliCommandSql - - - No working database is set. -Call %1 command to set working database. -Call %2 to see list of all databases. - - - - - Database is not open. - - - - - executes SQL query - - - - - This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. - - - - - sql - CLI command syntax - - - - - - Too many columns to display in %1 mode. - - - - - Row %1 - - - - - Query execution error: %1 - - - - - CliCommandTables - - - No such database: %1. Use %2 to see list of known databases. - - - - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - - - - - Database %1 is closed. - - - - - - Database - - - - - Table - - - - - prints list of tables in the database - - - - - Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. -When the -s option is given, then system tables are also listed. - - - - - database - CLI command syntax - - - - - CliCommandTree - - - No current working database is selected. Use %1 to define one and then run %2. - - - - - Tables - - - - - Views - - - - - Columns - - - - - Indexes - - - - - - Triggers - - - - - prints all objects in the database as a tree - - - - - Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. -When -c option is given, then also columns will be listed under each table. -When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). -The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. - - - - - CliCommandUse - - - No current database selected. - - - - - - Current database: %1 - - - - - No such database: %1 - - - - - changes default working database - - - - - Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. - -What is current working database? -When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). - -The default database can be selected in various ways: -- using %1 command, -- by passing database file name to the application startup parameters, -- by passing registered database name to the application startup parameters, -- by restoring previously selected default database from saved configuration, -- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. - - - - - name - CLI command syntax - - - - - QObject - - - Insufficient number of arguments. - - - - - Too many arguments. - - - - - Invalid argument value: %1. -Expected one of: %2 - - - - - Unknown option: %1 - CLI command syntax - - - - - Option %1 requires an argument. - CLI command syntax - - - - - string - CLI command syntax - - - - - Command line interface to SQLiteStudio, a SQLite manager. - - - - - Enables debug messages on standard error output. - - - - - Enables Lemon parser debug messages for SQL code assistant. - - - - - Lists plugins installed in the SQLiteStudio and quits. - - - - - file - - - - - Database file to open - - - - diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk_SK.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk_SK.ts new file mode 100644 index 0000000..264090c --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sk_SK.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sr_SP.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sr_SP.ts new file mode 100644 index 0000000..aa4e66f --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sr_SP.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sv_SE.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sv_SE.ts new file mode 100644 index 0000000..e88d260 --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_sv_SE.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_tr_TR.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_tr_TR.ts new file mode 100644 index 0000000..fe14c9c --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_tr_TR.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Geçerli veritabanı: %1 + + + + No current working database is set. + Geçerli bir çalışma veritabanı ayarlanmamış. + + + + Type %1 for help + Yardım için %1'e basın + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + %1 veritabanı listeye eklenemedi. + + + + closed + kapalı + + + + CliCommand + + + Usage: %1%2 + Kullanım: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + %1 veritabanı listeye eklenemedi. + + + + Database added: %1 + Veritabanı eklendi: %1 + + + + adds new database to the list + yeni veritabanını listeye ekler + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + adı + + + + path + CLI command syntax + dizin + + + + CliCommandCd + + + Changed directory to: %1 + Dizin %1'e deÄŸiÅŸtirildi + + + + Could not change directory to: %1 + Çalışma dizini %1 olarak deÄŸiÅŸtirilemedi + + + + changes current working directory + çalışma dizinini deÄŸiÅŸtirir + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + dizin + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + %1 veritabanına baÄŸlantı kapatıldı. + + + + No such database: %1. Use %2 to see list of known databases. + Böyle bir veritabanı bulunamadı: %1. Bilinen veritabanları için: %2. + + + + closes given (or current) database + verilen (ya da güncel) veritabanını kapatır + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + adı + + + + CliCommandDbList + + + No current working database defined. + Geçerli bir çalışma veritabanı ayarlanmamış. + + + + Databases: + Veritabanları: + + + + + Name + CLI db name column + Adı + + + + + Open + CLI connection state column + Açık + + + + + Closed + CLI connection state column + Kapalı + + + + + Connection + CLI connection state column + BaÄŸlantı + + + + + Database file path + Veritabanı dosyası dizini + + + + prints list of registered databases + kayıtlı veritabanlarının listesini yazdırır + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Veritabanı açık deÄŸil. + + + + Cannot find table named: %1 + %1 isimli tablo bulunamadı + + + + shows details about the table + tablo hakkında detayları gösterir + + + + table + tablo + + + + Table: %1 + Tablo: %1 + + + + Column name + Kolon adı + + + + Data type + Veri tipi + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + desen + + + + CliCommandExit + + + quits the application + uygulamadan çıkar + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Uygulamadan çıkar. Konfigurasyon dosyasında kayıtlı olan ayarlar bir sonraki açılışta onarılacak. + + + + CliCommandHelp + + + shows this help message + bu yardım mesajını gösterir + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + komut + + + + No such command: %1 + Böyle bir komut yok: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Kullanım: %1%2 + + + + Aliases: %1 + DiÄŸer Adlar: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Güncel geçmiÅŸ limiti %1'e ayarlandı + + + + prints history or erases it + geçmiÅŸi yazdırır ya da siler + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + sayı + + + + Console history erased. + Konsol geçmiÅŸi silindi. + + + + Invalid number: %1 + Geçersiz sayı: %1 + + + + History limit set to %1 + GeçmiÅŸ limiti %1'e ayarlandı + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_uk_UA.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_uk_UA.ts new file mode 100644 index 0000000..20e35fd --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_uk_UA.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Поточна база даних: %1 + + + + No current working database is set. + Поточна робоча база даних не визначена. + + + + Type %1 for help + Введіть %1 Ð´Ð»Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÑƒ довідки + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + База даних, передана через аргументи командного Ñ€Ñдка (%1), вже знаходитьÑÑ Ð² ÑпиÑку під назвою %2 + + + + Could not add database %1 to list. + Ðе вдалоÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ базу даних %1 до ÑпиÑку. + + + + closed + закрито + + + + CliCommand + + + Usage: %1%2 + ВикориÑтаннÑ: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Ðе вдалоÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ базу даних %1 до ÑпиÑку. + + + + Database added: %1 + База даних додана: %1 + + + + adds new database to the list + додати нову базу даних до ÑпиÑку + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Додає базу даних, розташовану за вказаним <шлÑхом> під вказаним ім'Ñм <ім'Ñм> в ÑпиÑок баз даних. <ім'Ñ> - це звичайне Ñимвольне ім'Ñ, Ñке в подальшому можна буде викориÑтовувати. Виберіть будь-Ñкий унікальне ім'Ñ. Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ð³Ð¾ ÑпиÑку баз даних за допомогою команди %1. + + + + name + CLI command syntax + Ñ–Ð¼â€™Ñ + + + + path + CLI command syntax + шлÑÑ… + + + + CliCommandCd + + + Changed directory to: %1 + Теку змінено на: %1 + + + + Could not change directory to: %1 + Ðе вдалоÑÑ Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ директорію на: %1 + + + + changes current working directory + змінює поточну робочу директорію + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Дуже Ñхожа команда до 'cd' відомої від Unix ÑиÑтем Ñ– Windows. Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñ…Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ аргументу <path> необхідно перенеÑти аргумент, тому виклик %1 завжди може викликати зміну каталогу. Щоб дізнатиÑÑ Ñ‚Ðµ, що викориÑтовує поточну робочу папку, викориÑтовуйте %2 команду Ñ– щоб перерахувати вміÑÑ‚ поточної робочої директорії викориÑтовуйте %3. + + + + path + CLI command syntax + шлÑÑ… + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Ðеможливо викликати %1, коли база даних не налаштована на поточну. Вкажіть поточну базу даних з командою %2, або передайте ім'Ñ Ð±Ð°Ð·Ð¸ даних на %3. + + + + + Connection to database %1 closed. + ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ бази даних %1 закрито. + + + + No such database: %1. Use %2 to see list of known databases. + Ðемає такої бази даних: %1. ВикориÑтовуйте %2, щоб переглÑнути перелік відомих баз даних. + + + + closes given (or current) database + закриваєтьÑÑ Ð·Ð° вказаною (або поточною) базою даних + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Закриває зв'Ñзок з базою даних. Якщо база даних вже закрита, нічого не відбуваєтьÑÑ. Якщо <name> буде передбачено, то назва бази даних має бути закрита (Ñк надруковано командою %1). <name> не передбачено, поточна робоча база даних закрита (детальніше дивітьÑÑ Ð´Ð¾Ð²Ñ–Ð´ÐºÑƒ %2). + + + + name + CLI command syntax + назва + + + + CliCommandDbList + + + No current working database defined. + Поточна робоча база даних не визначена. + + + + Databases: + База даних: + + + + + Name + CLI db name column + Ðазва + + + + + Open + CLI connection state column + Відкрити + + + + + Closed + CLI connection state column + Закрити + + + + + Connection + CLI connection state column + ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ + + + + + Database file path + ШлÑÑ… до бази даних + + + + prints list of registered databases + друкує ÑпиÑок зареєÑтрованих баз даних + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Друкує перелік баз даних, зареєÑтрованих у SQLiteStudio. Кожна база даних в ÑпиÑку може бути у відкритому або закритому Ñтані Ñ– %1 повідомлÑÑ” вам це. Поточна робоча база даних (база даних за замовчуваннÑм) також позначена в ÑпиÑку з '*' на початку назви. ДивиÑÑŒ довідку Ð´Ð»Ñ %2 команди щоб дізнатиÑÑ Ð¿Ñ€Ð¾ типову базу даних. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + Робоча база даних не вÑтановлена. +Ðабрати %1 Ð´Ð»Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‡Ð¾Ñ— бази даних. +Ðабрати %2 Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду ÑпиÑку вÑÑ–Ñ… баз даних. + + + + Database is not open. + База даних не відкрита. + + + + Cannot find table named: %1 + Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ таблицю з назвою: %1 + + + + shows details about the table + показати деталі про таблицю + + + + table + Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ + + + + Table: %1 + ТаблицÑ: %1 + + + + Column name + Ðазва ÑÑ‚Ð¾Ð²Ð¿Ñ†Ñ + + + + Data type + Тип даних + + + + Constraints + ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ + + + + Virtual table: %1 + Віртуальна таблицÑ: %1 + + + + Construction arguments: + Параметри ÑтвореннÑ: + + + + No construction arguments were passed for this virtual table. + Ðе вказані параметри ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— віртуальної таблиці. + + + + CliCommandDir + + + lists directories and files in current working directory + перелічує каталоги та файли в поточній робочій каталозі + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + Це дуже Ñхоже на команду 'dir' з Windows та 'ls' на команду з Unix ÑиÑтем. + +Ви можете передати <pattern> Ñимволами, Ñкі міÑÑ‚Ñть шаблони, щоб відфільтрувати вивід. + + + + pattern + шаблон + + + + CliCommandExit + + + quits the application + вийти з додатку + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Вийти з додатку. ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°ÑŽÑ‚ÑŒÑÑ Ñƒ файлі конфігурації Ñ– будуть відновлені при наÑтупному запуÑку. + + + + CliCommandHelp + + + shows this help message + показати це довідкове Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + ВикориÑтовуйте %1, щоб дізнатиÑÑ Ð¿Ñ€Ð¾ деÑкі команди, Ñкі підтримуютьÑÑ Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñом командного Ñ€Ñдка (CLI) з SQLiteStudio. +Щоб переглÑнути ÑпиÑок підтримуваних команд, введіть %2 без будь-Ñких аргументів. + +У назві <command> можна не вказувати префікÑний Ñимвол ('%3'). + +Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð²Ñ–Ð´ÐºÐ¸ по команді ви також можете виконати команду з єдиним ключем '--help'. Це альтернатива введенню: %1 <command>. + + + + command + CLI command syntax + команда + + + + No such command: %1 + Ðемає такої команди: %1 + + + + Type '%1' for list of available commands. + Введіть '%1' Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ÑпиÑку доÑтупних команд. + + + + Usage: %1%2 + ВикориÑтаннÑ: %1%2 + + + + Aliases: %1 + ПÑевдоніми: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Поточний ліміт Ñ–Ñторії вÑтановлено на: %1 + + + + prints history or erases it + друкувати Ñ–Ñторію або видалити + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + Коли аргумент не був прийнÑтий, Ñ†Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° друкує Ñ–Ñторію командного Ñ€Ñдка. Кожен Ð·Ð°Ð¿Ð¸Ñ Ð² Ñ–Ñторії розділений по горизонтальній прÑмій, тож багаторічні запиÑи легше зчитувати. + +Коли параметр -c або --clear, Ñ–ÑÑ‚Ð¾Ñ€Ñ–Ñ ÑтираєтьÑÑ. +при проходженні параметра -l або --limitвÑтановлюєтьÑÑ Ð½Ð¾Ð²Ð¸Ð¹ ліміт на кількіÑть запиÑів в Ñ–Ñторії. Ðеобхідний додатковий аргумент, Ñкий вказує Ñкільки запиÑів необхідно зберігати в Ñ–Ñторії. +Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду поточного ліміту запиÑів викличте команду з ключем -ql або --querylimit. + + + + number + номер + + + + Console history erased. + ІÑторію конÑолі Ñтерто. + + + + Invalid number: %1 + ÐеприпуÑтиме чиÑло: %1 + + + + History limit set to %1 + Ліміт Ñ–Ñторії вÑтановлено на %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Поточний режим друку результатів: %1 + + + + Invalid results printing mode: %1 + ÐедійÑний режим друку результатів: %1 + + + + New results printing mode: %1 + Поточний режим друку результатів: %1 + + + + tells or changes the query results format + повідомлÑÑ” або змінює формат результатів запиту + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Поточний Ñ€Ñдок репрезентації NULL: %1 + + + + tells or changes the NULL representation string + повідомлÑÑ” або змінює Ñ€Ñдок предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ NULL + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + При виклику без аргументів відображає поточне предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ NULL (тобто що виводитьÑÑ Ð·Ð°Ð¼Ñ–Ñть Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ NULL у результатах запиту). Якщо вказано аргумент, його буде викориÑтано Ñк Ñ€Ñдок Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ NULL. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Ðеможливо викликати %1, коли база даних не вибрана. Виберіть поточну базу даних командою %2, або передайте ім'Ñ Ð±Ð°Ð·Ð¸ даних на %3. + + + + Could not add database %1 to list. + Ðе вдалоÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ базу даних %1 до ÑпиÑку. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + Файл %1 не Ñ–Ñнує в %2. Ðеможливо відкрити наÑвну базу даних з командою %3. Щоб Ñтворити нову базу даних, викориÑтовуйте команду %4. + + + + Database %1 has been open and set as the current working database. + База даних %1 була відкрита та вÑтановлена Ñк поточна робоча база даних. + + + + opens database connection + відкриває Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ бази даних + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Відкриває Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ бази даних. Якщо жодного додаткового аргументу не було передано, то Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ðµ до поточної бази даних за замовчуваннÑм (див. довідку %1 Ð´Ð»Ñ Ð´ÐµÑ‚Ð°Ð»ÐµÐ¹). Однак, Ñкщо аргумент був прийнÑтий, відкривати може бути або <name> з бази даних зареєÑтрованої або ж це може бути <path> в базі даних, щоб відкрити. Ð’ другому випадку, <path> реєÑтруєтьÑÑ Ð·Ñ– ÑпиÑком зі згенерованою назвою, але тільки за період поточного ÑеанÑу додатків. ПіÑÐ»Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑку такої бази даних не відновлено в ÑпиÑку. + + + + name + CLI command syntax + Ñ–Ð¼â€™Ñ + + + + path + CLI command syntax + шлÑÑ… + + + + CliCommandPwd + + + prints the current working directory + друкує поточну робочу директорію + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + Це те Ñаме, що команда 'pwd' в ÑиÑтемах Unix та команда 'cd' без аргументів у Windows. Він друкує поточний робочий каталог. Ви можете змінити поточний робочий каталог за допомогою команди %1, а також можете перерахувати вміÑÑ‚ поточного робочого каталогу за допомогою команди %2. + + + + CliCommandRemove + + + No such database: %1 + Ðемає такої бази даних: %1 + + + + Database removed: %1 + База даних видалена: %1 + + + + New current database set: + Ðову поточну базу даних вÑтановлено: + + + + removes database from the list + видалÑÑ” базу даних зі ÑпиÑку + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + ВидалÑÑ” з ÑпиÑку зареєÑтрованих баз даних <name> баз даних. Якщо бази даних немає в ÑпиÑку (див. команду %1), то Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ помилку надруковано Ñ– більше нічого не відбуваєтьÑÑ. + + + + name + CLI command syntax + Ñ–Ð¼â€™Ñ + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + Робоча база даних не вÑтановлена. +Ðабрати %1 Ð´Ð»Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‡Ð¾Ñ— бази даних. +Ðабрати %2 Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду ÑпиÑку вÑÑ–Ñ… баз даних. + + + + Database is not open. + База даних не відкрита. + + + + executes SQL query + виконуєтьÑÑ Ð·Ð°Ð¿Ð¸Ñ‚ SQL + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + Ð¦Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° виконуєтьÑÑ Ñ‰Ð¾Ñ€Ð°Ð·Ñƒ, коли ви вводите SQL запит у командному Ñ€Ñдку. Він виконує запит на поточну робочу базу даних (переглÑньте допомогу Ð´Ð»Ñ %1 Ð´Ð»Ñ Ð´Ð¾ÐºÐ»Ð°Ð´Ð½Ð¾Ñ— інформації). ІÑнує'не має ÑенÑу виконувати цю команду Ñвно. ЗаміÑть цього, проÑто введіть SQL запит у командний Ñ€Ñдок без жодного префікÑу команди. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Занадто багато Ñтовпців Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð² режимі %1. + + + + Row %1 + РÑдок %1 + + + + Query execution error: %1 + Помилка Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + Ðемає такої бази даних: %1. ВикориÑтовуйте %2, щоб переглÑнути перелік відомих баз даних. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Ðеможливо викликати %1, коли база даних не налаштована на поточну. Вкажіть поточну базу даних з командою %2, або передайте ім'Ñ Ð±Ð°Ð·Ð¸ даних на %3. + + + + Database %1 is closed. + База даних %1 закрита. + + + + + Database + База даних + + + + Table + Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ + + + + prints list of tables in the database + надрукує ÑпиÑок таблиць в базі даних + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Друкує перелік таблиць у даній <database> або в поточній базі даних. Зверніть увагу, що <database> має бути ім'Ñ Ð·Ð°Ñ€ÐµÑ”Ñтрованої бази даних (див. %1). Вихідний ÑпиÑок включає в Ñебе вÑÑ– таблиці з будь-Ñких інших баз даних, прикріплених до запитаної бази даних. +Якщо параметр -s вказано, то ÑиÑтемні таблиці також ÑпиÑані. + + + + database + CLI command syntax + база даних + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + Ðе вибрано жодної робочої бази даних. ВикориÑтовуйте %1, щоб визначити одне, а потім запуÑтити %2. + + + + Tables + Таблиці + + + + Views + Розріз даних + + + + Columns + Стовпці + + + + Indexes + ІндекÑи + + + + + Triggers + Тригери + + + + prints all objects in the database as a tree + друкує вÑÑ– об'єкти в базі даних Ñк дерево + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Відображає вÑÑ– об'єкти (таблиці, індекÑи, тригери та розріз даних) бази даних у виглÑді дерева. Структура дерева подібна до того, що відображаєтьÑÑ Ð² GUI клієнта SQLiteStudio. +При виклику з -c також будуть виведені Ñтовпці під кожною таблицею. +При виклику з ключем -s також буде виведено ÑиÑтемні об'єкти (таблиці sqlite_*, індекÑи автоінкремента тощо). +При виклику з необов'Ñзковим аргументом 'база даних' будуть виведені об'єкти лише вказаної бази даних. Під 'базою даних' маєтьÑÑ Ð½Ð° увазі не зареєÑтроване ім'Ñ Ð±Ð°Ð·Ð¸ даних, а внутрішнє ім'Ñ Ð±Ð°Ð·Ð¸ даних SQLite, наприклад 'main', 'temp' або ім'Ñ Ð¿Ñ€Ð¸Ñ”Ð´Ð½Ð°Ð½Ð¾Ñ— бази даних. Ð”Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´ÐµÑ€ÐµÐ²Ð° іншої зареєÑтрованої бази даних Ñпочатку змініть робочу базу даних командою %1, а потім ÑкориÑтайтеÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¾ÑŽ %2. + + + + CliCommandUse + + + No current database selected. + Ðе обрано поточної бази даних. + + + + + Current database: %1 + Поточна база даних: %1 + + + + No such database: %1 + Ðемає такої бази даних: %1 + + + + changes default working database + змінити типову робочу базу даних + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + Ñ–Ð¼â€™Ñ + + + + QObject + + + Insufficient number of arguments. + ÐедоÑтатньо кількоÑті аргументів. + + + + Too many arguments. + Забагато аргументів. + + + + Invalid argument value: %1. +Expected one of: %2 + ÐеприпуÑтиме Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ñƒ: %1. +очікувавÑÑ: %2 + + + + Unknown option: %1 + CLI command syntax + Ðевідомий параметр: %1 + + + + Option %1 requires an argument. + CLI command syntax + ÐžÐ¿Ñ†Ñ–Ñ %1 потребує аргументу. + + + + string + CLI command syntax + Ñ€Ñдок + + + + Command line interface to SQLiteStudio, a SQLite manager. + Ð†Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð³Ð¾ Ñ€Ñдка Ð´Ð»Ñ SQLiteStudio, менеджера баз даних SQLite. + + + + Enables debug messages on standard error output. + Вмикає Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð½Ð°Ð»Ð°Ð³Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸ звичайному виході помилки. + + + + Enables Lemon parser debug messages for SQL code assistant. + Включає вивід налагоджувальних повідомлень аналізатора Lemon Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ SQL коду. + + + + Lists plugins installed in the SQLiteStudio and quits. + Виводить ÑпиÑок вÑтановлених в SQLiteStudio модулів Ñ– здійÑнює вихід. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + файл + + + + Database file to open + Файл бази даних Ð´Ð»Ñ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_vi_VN.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_vi_VN.ts new file mode 100644 index 0000000..0890eba --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_vi_VN.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + Current database: %1 + + + + No current working database is set. + No current working database is set. + + + + Type %1 for help + Type %1 for help + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + Database passed in command line parameters (%1) was already on the list under name: %2 + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + closed + closed + + + + CliCommand + + + Usage: %1%2 + Usage: %1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + Database added: %1 + Database added: %1 + + + + adds new database to the list + adds new database to the list + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandCd + + + Changed directory to: %1 + Changed directory to: %1 + + + + Could not change directory to: %1 + Could not change directory to: %1 + + + + changes current working directory + changes current working directory + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + + + + path + CLI command syntax + path + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + + Connection to database %1 closed. + Connection to database %1 closed. + + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + closes given (or current) database + closes given (or current) database + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + + + + name + CLI command syntax + name + + + + CliCommandDbList + + + No current working database defined. + No current working database defined. + + + + Databases: + Databases: + + + + + Name + CLI db name column + Name + + + + + Open + CLI connection state column + Open + + + + + Closed + CLI connection state column + Closed + + + + + Connection + CLI connection state column + Connection + + + + + Database file path + Database file path + + + + prints list of registered databases + prints list of registered databases + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + Cannot find table named: %1 + Cannot find table named: %1 + + + + shows details about the table + shows details about the table + + + + table + table + + + + Table: %1 + Table: %1 + + + + Column name + Column name + + + + Data type + Data type + + + + Constraints + Constraints + + + + Virtual table: %1 + Virtual table: %1 + + + + Construction arguments: + Construction arguments: + + + + No construction arguments were passed for this virtual table. + No construction arguments were passed for this virtual table. + + + + CliCommandDir + + + lists directories and files in current working directory + lists directories and files in current working directory + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + + + + pattern + pattern + + + + CliCommandExit + + + quits the application + quits the application + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + Quits the application. Settings are stored in configuration file and will be restored on next startup. + + + + CliCommandHelp + + + shows this help message + shows this help message + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + + + + command + CLI command syntax + command + + + + No such command: %1 + No such command: %1 + + + + Type '%1' for list of available commands. + Type '%1' for list of available commands. + + + + Usage: %1%2 + Usage: %1%2 + + + + Aliases: %1 + Aliases: %1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + Current history limit is set to: %1 + + + + prints history or erases it + prints history or erases it + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + + + + number + number + + + + Console history erased. + Console history erased. + + + + Invalid number: %1 + Invalid number: %1 + + + + History limit set to %1 + History limit set to %1 + + + + CliCommandMode + + + Current results printing mode: %1 + Current results printing mode: %1 + + + + Invalid results printing mode: %1 + Invalid results printing mode: %1 + + + + New results printing mode: %1 + New results printing mode: %1 + + + + tells or changes the query results format + tells or changes the query results format + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + Current NULL representation string: %1 + + + + tells or changes the NULL representation string + tells or changes the NULL representation string + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Could not add database %1 to list. + Could not add database %1 to list. + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + + + + Database %1 has been open and set as the current working database. + Database %1 has been open and set as the current working database. + + + + opens database connection + opens database connection + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + + + + name + CLI command syntax + name + + + + path + CLI command syntax + path + + + + CliCommandPwd + + + prints the current working directory + prints the current working directory + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + + + + CliCommandRemove + + + No such database: %1 + No such database: %1 + + + + Database removed: %1 + Database removed: %1 + + + + New current database set: + New current database set: + + + + removes database from the list + removes database from the list + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + + + + name + CLI command syntax + name + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + + + + Database is not open. + Database is not open. + + + + executes SQL query + executes SQL query + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + Too many columns to display in %1 mode. + + + + Row %1 + Row %1 + + + + Query execution error: %1 + Query execution error: %1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + No such database: %1. Use %2 to see list of known databases. + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + + + + Database %1 is closed. + Database %1 is closed. + + + + + Database + Database + + + + Table + Table + + + + prints list of tables in the database + prints list of tables in the database + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + + + + database + CLI command syntax + database + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + No current working database is selected. Use %1 to define one and then run %2. + + + + Tables + Tables + + + + Views + Views + + + + Columns + Columns + + + + Indexes + Indexes + + + + + Triggers + Triggers + + + + prints all objects in the database as a tree + prints all objects in the database as a tree + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + + + + CliCommandUse + + + No current database selected. + No current database selected. + + + + + Current database: %1 + Current database: %1 + + + + No such database: %1 + No such database: %1 + + + + changes default working database + changes default working database + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + + + + name + CLI command syntax + name + + + + QObject + + + Insufficient number of arguments. + Insufficient number of arguments. + + + + Too many arguments. + Too many arguments. + + + + Invalid argument value: %1. +Expected one of: %2 + Invalid argument value: %1. +Expected one of: %2 + + + + Unknown option: %1 + CLI command syntax + Unknown option: %1 + + + + Option %1 requires an argument. + CLI command syntax + Option %1 requires an argument. + + + + string + CLI command syntax + string + + + + Command line interface to SQLiteStudio, a SQLite manager. + Command line interface to SQLiteStudio, a SQLite manager. + + + + Enables debug messages on standard error output. + Enables debug messages on standard error output. + + + + Enables Lemon parser debug messages for SQL code assistant. + Enables Lemon parser debug messages for SQL code assistant. + + + + Lists plugins installed in the SQLiteStudio and quits. + Lists plugins installed in the SQLiteStudio and quits. + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + file + + + + Database file to open + Database file to open + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_CN.qm b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_CN.qm deleted file mode 100644 index 1de3b17..0000000 Binary files a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_CN.qm and /dev/null differ diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_CN.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_CN.ts index ab95a5d..b1fce37 100644 --- a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_CN.ts +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_CN.ts @@ -1,412 +1,425 @@ - - + + CLI - - Current database: %1 - 当剿•°æ®åº“:%1 + + Current database: %1 + 当剿•°æ®åº“:%1 - - No current working database is set. - 当剿²¡æœ‰é€‰å®šå·¥ä½œæ•°æ®åº“。 + + No current working database is set. + ç›®å‰æœªè®¾å®šæ“作的数æ®åº“。 - - Type %1 for help - 输入 %1 获å–帮助 + + Type %1 for help + 输入 %1 获å–帮助 - - Database passed in command line parameters (%1) was already on the list under name: %2 - + + Database passed in command line parameters (%1) was already on the list under name: %2 + é€šè¿‡å‘½ä»¤è¡Œå‚æ•°ä¼ å…¥çš„æ•°æ®åº“(%1)已在列表中,å为:%2 - - Could not add database %1 to list. - 未能将数æ®åº““%1â€æ·»åŠ åˆ°åˆ—è¡¨ã€‚ + + Could not add database %1 to list. + 未将数æ®åº““%1â€æ·»åŠ åˆ°åˆ—è¡¨ã€‚ - - closed - 已关闭 + + closed + 已关闭 - - + + CliCommand - - Usage: %1%2 - 用法: %1%2 + + Usage: %1%2 + 用法:%1%2 - - + + CliCommandAdd - - Could not add database %1 to list. - 未能将数æ®åº““%1â€æ·»åŠ åˆ°åˆ—è¡¨ã€‚ + + Could not add database %1 to list. + 未将数æ®åº““%1â€æ·»åŠ åˆ°åˆ—è¡¨ã€‚ - - Database added: %1 - 已添加数æ®åº“:%1 + + Database added: %1 + 已添加数æ®åº“:%1 - - adds new database to the list - 添加新的数æ®åº“到列表 + + adds new database to the list + 添加新的数æ®åº“到列表 - - Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. - + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + 添加指定 <路径> 的数æ®åº“到数æ®åº“列表,用指定的 <åç§°>。<åç§°> 是您之åŽå¯ä»¥ç”¨æ¥å¼•用它的å称。选择一个ä¸é‡å¤çš„å称。查阅已在数æ®åº“列表中的数æ®åº“,请用 %1 命令。 - - name - CLI command syntax - + + name + CLI command syntax + åç§° - - path - CLI command syntax - 路径 + + path + CLI command syntax + 路径 - - + + CliCommandCd - - Changed directory to: %1 - 已切æ¢åˆ°ï¼š%1 + + Changed directory to: %1 + 目录已改为:%1 - - Could not change directory to: %1 - 未能切æ¢åˆ°ï¼š%1 + + Could not change directory to: %1 + 未能切æ¢åˆ°ç›®å½•:%1 - - changes current working directory - 更改当å‰å·¥ä½œç›®å½• + + changes current working directory + 更改当å‰çš„工作目录 - - Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + éžå¸¸ç±»ä¼¼ Unix å’Œ Windows 系统中的 'cd' 命令。需è¦ä¼ å…¥ä¸€ä¸ª <路径> 傿•°ï¼Œç„¶åŽè°ƒç”¨ %1 将始终 cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. - - path - CLI command syntax - 路径 + + path + CLI command syntax + 路径 - - + + CliCommandClose - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + æ²¡æœ‰è®¾å®šå½“å‰æ•°æ®åº“时无法调用 %1。使用 %2 å‘½ä»¤æŒ‡å®šå½“å‰æ•°æ®åº“,或者传递数æ®åº“å称到 %3。 - - - Connection to database %1 closed. - æ•°æ®åº“ %1 的连接已关闭。 + + + Connection to database %1 closed. + æ•°æ®åº“ %1 的连接已关闭。 - - No such database: %1. Use %2 to see list of known databases. - 没有这样的数æ®åº“:%1。使用 %2 去查看已知数æ®åº“列表。 + + No such database: %1. Use %2 to see list of known databases. + 没有这样的数æ®åº“:%1。使用 %2 查看已知数æ®åº“列表。 - - closes given (or current) database - 关闭给定的(或当å‰ï¼‰æ•°æ®åº“ + + closes given (or current) database + 关闭指定的或当å‰çš„æ•°æ®åº“ - - Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). - + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + 关闭数æ®åº“连接。如果数æ®åº“已关闭,什么也ä¸åšã€‚如果æä¾›äº† <åç§°>,则表示需è¦å…³é—­çš„æ•°æ®åº“çš„åç§°ï¼ˆè§ %1 命令的结果)。如果没有æä¾› <åç§°>ã€‚åˆ™å…³é—­å½“å‰æ“作的数æ®åº“ï¼ˆè¯¦è§ %2 帮助)。 - - name - CLI command syntax - åç§° + + name + CLI command syntax + åç§° - - + + CliCommandDbList - - No current working database defined. - + + No current working database defined. + ç›®å‰æœªå®šä¹‰æ“作的数æ®åº“。 - - Databases: - æ•°æ®åº“: + + Databases: + æ•°æ®åº“: - - - Name - CLI db name column - åç§° + + + Name + CLI db name column + åç§° - - - Open - CLI connection state column - 打开 + + + Open + CLI connection state column + 打开 - - - Closed - CLI connection state column - 关闭 + + + Closed + CLI connection state column + 关闭 - - - Connection - CLI connection state column - 连接 + + + Connection + CLI connection state column + 连接 - - - Database file path - æ•°æ®åº“文件路径 + + + Database file path + æ•°æ®åº“文件路径 - - prints list of registered databases - 打å°å·²æ³¨å†Œæ•°æ®åº“列表 + + prints list of registered databases + 打å°å·²æ³¨å†Œæ•°æ®åº“列表 - - Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + 列出在 SQLiteStudio 中注册的数æ®åº“的列表。. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. - - + + CliCommandDesc - - No working database is set. + + No working database is set. Call %1 command to set working database. Call %2 to see list of all databases. - + 没有设定æ“作的数æ®åº“。 +调用 %1 命令æ“作的数æ®åº“。 +调用 %2 查阅数æ®åº“列表。 - - Database is not open. - æ•°æ®åº“未被打开。 + + Database is not open. + æ•°æ®åº“未被打开。 - - Cannot find table named: %1 - 无法找到å为 %1 的表 + + Cannot find table named: %1 + 无法找到å为 %1 的表 - - shows details about the table - 显示表的详情 + + shows details about the table + æ˜¾ç¤ºä¸€ä¸ªè¡¨çš„è¯¦ç»†ä¿¡æ¯ - - table - 表 + + table + 表 - - Table: %1 - 表:%1 + + Table: %1 + 表:%1 - - Column name - 字段å + + Column name + 字段å - - Data type - æ•°æ®ç±»åž‹ + + Data type + æ•°æ®ç±»åž‹ - - Constraints - çº¦æŸæ¡ä»¶ + + Constraints + çº¦æŸæ¡ä»¶ - - Virtual table: %1 - + + Virtual table: %1 + 虚拟表:%1 - - Construction arguments: - + + Construction arguments: + æž„é€ å‚æ•°ï¼š - - No construction arguments were passed for this virtual table. - + + No construction arguments were passed for this virtual table. + æ²¡æœ‰ä¸ºæ­¤è™šæ‹Ÿè¡¨ä¼ é€’ç»“æž„å‚æ•°ã€‚ - - + + CliCommandDir - - lists directories and files in current working directory - 列出当å‰å·¥ä½œç›®å½•中的目录与文件 + + lists directories and files in current working directory + 列出当å‰å·¥ä½œç›®å½•中的目录与文件 - - This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. You can pass <pattern> with wildcard characters to filter output. - + è¿™éžå¸¸ç±»ä¼¼ Windows 中的 'dir' 命令与 Unix 中的 'ls' 命令。 + +å¯ä»¥ä¼ å…¥ä¸€ä¸ªå¸¦æœ‰é€šé…符的 <模å¼> æ¥è¿‡æ»¤è¾“出内容。 - - pattern - + + pattern + æ¨¡å¼ - - + + CliCommandExit - - quits the application - é€€å‡ºæœ¬ç¨‹åº + + quits the application + é€€å‡ºæœ¬ç¨‹åº - - Quits the application. Settings are stored in configuration file and will be restored on next startup. - 退出本程åºã€‚设置已被存储在é…置文件并且会在下一次å¯åŠ¨æ—¶æ¢å¤ã€‚ + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + 退出本程åºã€‚设置已被存储在é…置文件并且会在下一次å¯åŠ¨æ—¶æ¢å¤ã€‚ - - + + CliCommandHelp - - shows this help message - æ˜¾ç¤ºè¿™ä¸ªå¸®åŠ©ä¿¡æ¯ + + shows this help message + æ˜¾ç¤ºè¿™ä¸ªå¸®åŠ©ä¿¡æ¯ - - Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. To see list of supported commands, type %2 without any arguments. When passing <command> name, you can skip special prefix character ('%3'). You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. - + 使用 %1 了解 SQLiteStudio 的命令行接å£ï¼ˆCLI)所支æŒçš„特定命令。 +输入 %2 ä¸å¸¦ä»»ä½•傿•°æ¥æŸ¥çœ‹æ”¯æŒçš„命令列表。 + +ä¼ å…¥ <åç§°> å称时,您å¯ä»¥è·³è¿‡ç‰¹æ®Šçš„å‰ç¼€å­—符('%3')。 + +您å¯ä»¥ä¸ºä»»ä½•命令指定 '--help' é€‰é¡¹å¹¶æ‰§è¡Œæ¥æŸ¥çœ‹ç‰¹å®šå‘½ä»¤çš„帮助。å¦ä¸€ç§æ–¹æ³•:%1 <命令>。 - - command - CLI command syntax - 命令 + + command + CLI command syntax + 命令 - - No such command: %1 - 没有这个命令:%1 + + No such command: %1 + 没有这个命令:%1 - - Type '%1' for list of available commands. - 输入 '%1' 列出所有å¯ç”¨çš„命令。 + + Type '%1' for list of available commands. + 输入 '%1' 列出所有å¯ç”¨çš„命令。 - - Usage: %1%2 - 用法: %1%2 + + Usage: %1%2 + 用法: %1%2 - - Aliases: %1 - 别å:%1 + + Aliases: %1 + 别å:%1 - - + + CliCommandHistory - - Current history limit is set to: %1 - + + Current history limit is set to: %1 + 当å‰åކå²è®°å½•é™åˆ¶ä¸ºï¼š%1 - - prints history or erases it - 打å°åކ岿ˆ–擦除它 + + prints history or erases it + åˆ—å‡ºåŽ†å²æˆ–擦除 - - When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. When the -c or --clear option is passed, then the history gets erased. When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. Use -ql or --querylimit option to see the current limit value. - + æ²¡æœ‰ä¼ å…¥å‚æ•°æ—¶ï¼Œæ­¤å‘½ä»¤åˆ—出命令行历å²ã€‚æ¯æ¡åކå²ä»¥æ°´å¹³çº¿éš”å¼€ï¼Œä»¥ä½¿å¤šè¡Œå•æ¡æ›´æ˜“阅读。 + +ä¼ å…¥ -c 或 --clear 选项,历å²è®°å½•将被清空擦除。 +ä¼ å…¥ -l 或 --limit 选项,设置历å²è®°å½•æ¡æ•°é™åˆ¶ã€‚需è¦é™„上é¢å¤–傿•°ï¼ŒæŒ‡æ˜Žå°†åކå²è®°å½•é™åˆ¶ä¸ºæœ€å¤šå¤šå°‘æ¡ã€‚ +使用 -ql 或 --querylimit é€‰é¡¹ï¼Œå¯æŸ¥çœ‹å½“å‰çš„é™åˆ¶å€¼ã€‚ - - number - + + number + 数值 - - Console history erased. - 控制å°åކå²å·²æ“¦é™¤ã€‚ + + Console history erased. + 控制å°åކå²å·²æ“¦é™¤ã€‚ - - Invalid number: %1 - 无效的数字:%1 + + Invalid number: %1 + 无效数值:%1 - - History limit set to %1 - + + History limit set to %1 + 历å²è®°å½•é™åˆ¶å·²è®¾ä¸º %1 - - + + CliCommandMode - - Current results printing mode: %1 - 当å‰ç»“æžœæ‰“å°æ¨¡å¼ï¼š%1 + + Current results printing mode: %1 + 当å‰ç»“æžœæ‰“å°æ¨¡å¼ï¼š%1 - - Invalid results printing mode: %1 - æ— æ•ˆç»“æžœæ‰“å°æ¨¡å¼ï¼š%1 + + Invalid results printing mode: %1 + æ— æ•ˆç»“æžœæ‰“å°æ¨¡å¼ï¼š%1 - - New results printing mode: %1 - æ–°ç»“æžœæ‰“å°æ¨¡å¼ï¼š%1 + + New results printing mode: %1 + æ–°ç»“æžœæ‰“å°æ¨¡å¼ï¼š%1 - - tells or changes the query results format - + + tells or changes the query results format + è¯¢é—®æˆ–æ›´æ”¹æŸ¥è¯¢ç»“æžœçš„æ ¼å¼ - - When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: - CLASSIC - columns are separated by a comma, not aligned, - FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, - COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), @@ -417,290 +430,306 @@ The CLASSIC mode is recommended if you want to see all the data, but you don&apo The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. -ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widhts, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). - - + + CliCommandNullValue - - Current NULL representation string: %1 - + + Current NULL representation string: %1 + 当å‰è¡¨ç¤º NULL 的字符串:%1 - - tells or changes the NULL representation string - + + tells or changes the NULL representation string + 询问或更改表示 NULL 的字符串 - - If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. - + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + 如果ä¸ä¼ å…¥ä»»ä½•傿•°ï¼Œåˆ™ä¼šå‘ŠçŸ¥å½“å‰çš„ NULL å€¼è¡¨ç¤ºæ–¹æ³•ï¼ˆå³æŸ¥è¯¢ç»“果中以什么代表 NULL 值)。如果æä¾›äº†å‚æ•°ï¼Œåˆ™å‚æ•°å°†ä½œä¸ºæ–°çš„代表 NULL 值的字符串。 - - + + CliCommandOpen - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + æ²¡æœ‰è®¾å®šå½“å‰æ•°æ®åº“时无法调用 %1。使用 %2 å‘½ä»¤æŒ‡å®šå½“å‰æ•°æ®åº“,或者传递数æ®åº“å称到 %3。 - - Could not add database %1 to list. - 未能将数æ®åº““%1â€æ·»åŠ åˆ°åˆ—è¡¨ã€‚ + + Could not add database %1 to list. + 未能将数æ®åº““%1â€æ·»åŠ åˆ°åˆ—è¡¨ã€‚ - - File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. - 文件 %1 ä¸å­˜åœ¨äºŽ %2。无法使用 %3 命令打开ä¸å­˜åœ¨çš„æ•°æ®åº“。使用 %4 命令创建一个新数æ®åº“。 + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + 文件 %1 ä¸å­˜åœ¨äºŽ %2。无法使用 %3 命令打开ä¸å­˜åœ¨çš„æ•°æ®åº“。使用 %4 命令创建一个新数æ®åº“。 - - Database %1 has been open and set as the current working database. - æ•°æ®åº“ %1 已被打开并设为当å‰å·¥ä½œæ•°æ®åº“。 + + Database %1 has been open and set as the current working database. + 已打开数æ®åº“ %1 å¹¶å°†å…¶è®¾ä¸ºå½“å‰æ“作的数æ®åº“。 - - opens database connection - 打开数æ®åº“连接 + + opens database connection + 打开数æ®åº“连接 - - Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. - + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + 打开到数æ®åº“çš„è¿žæŽ¥ã€‚å¦‚æžœä¸æä¾›é¢å¤–çš„å‚æ•°ï¼Œåˆ™æ‰“开到当å‰çš„默认数æ®åº“ï¼ˆè¯¦è§ %1)的连接。如果æä¾›ä¸€ä¸ªå‚数,它å¯ä»¥æ˜¯å·²æ³¨å†Œçš„æ•°æ®åº“çš„ <name>,也å¯ä»¥æ˜¯è¦æ‰“开的数æ®åº“文件的 <path>ã€‚ç¬¬äºŒç§æƒ…况下,<path> 将使用自动生æˆçš„å称临时注册到数æ®åº“列表,并在应用程åºé€€å‡ºæ—¶ä»Žåˆ—表中消失。 - - name - CLI command syntax - åç§° + + name + CLI command syntax + åç§° - - path - CLI command syntax - 路径 + + path + CLI command syntax + 路径 - - + + CliCommandPwd - - prints the current working directory - 打å°å½“å‰å·¥ä½œç›®å½• + + prints the current working directory + 列出当å‰çš„工作目录 - - This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. - + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + 这与 Unix 系统上的 'pwd' å‘½ä»¤ä»¥åŠ Windows ç³»ç»Ÿä¸Šæ²¡æœ‰å‚æ•°çš„ 'cd' 命令作用相åŒã€‚将列出当å‰çš„工作目录。使用 %1 命令å¯ä»¥æ›´æ”¹å½“å‰çš„工作目录,您也å¯ä»¥ç”¨ %2 命令列出当å‰å·¥ä½œç›®å½•的内容。 - - + + CliCommandRemove - - No such database: %1 - 没有这样一个数æ®åº“:%1 + + No such database: %1 + 没有这样一个数æ®åº“:%1 - - Database removed: %1 - + + Database removed: %1 + æ•°æ®åº“已移除:%1 - - New current database set: - + + New current database set: + æ–°çš„å½“å‰æ•°æ®åº“设为: - - removes database from the list - 从列表中移除数æ®åº“ + + removes database from the list + 从列表中移除数æ®åº“ - - Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. - + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + 从已注册数æ®åº“列表中移除å为 <åç§°> 的数æ®åº“ã€‚å¦‚æžœåˆ—è¡¨ï¼ˆè§ %1 命令)中没有所指定的数æ®åº“ ,会给出错误消æ¯ã€‚ - - name - CLI command syntax - åç§° + + name + CLI command syntax + åç§° - - + + CliCommandSql - - No working database is set. + + No working database is set. Call %1 command to set working database. Call %2 to see list of all databases. - 没有设置工作数æ®åº“。 -调用 %1 命令去设置工作数æ®åº“。 -调用 %2 去æµè§ˆæ‰€æœ‰æ•°æ®åº“列表。 + 没有设定æ“作的数æ®åº“。 +调用 %1 命令æ“作的数æ®åº“。 +调用 %2 查阅数æ®åº“列表。 - - Database is not open. - æ•°æ®åº“没有打开。 + + Database is not open. + æ•°æ®åº“没有打开。 - - executes SQL query - 执行 SQL 查询 + + executes SQL query + 执行 SQL 查询 - - This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. - + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + æ‚¨æ¯æ¬¡åœ¨å‘½ä»¤è¡Œæç¤ºç¬¦ä¸­è¾“å…¥ SQL æŸ¥è¯¢æ—¶ä¼šæ‰§è¡Œæ­¤å‘½ä»¤ã€‚å®ƒè´Ÿè´£åœ¨å½“å‰æ“作的数æ®åº“ï¼ˆè¯¦è§ %1)上执行查询。专门执行此命令没有任何æ„义。您å¯ä»¥åœ¨å‘½ä»¤è¡Œæç¤ºç¬¦ä¸­ç›´æŽ¥è¾“å…¥ SQL 查询,无需添加命令å‰ç¼€ã€‚ - - sql - CLI command syntax - sql + + sql + CLI command syntax + sql - - - Too many columns to display in %1 mode. - + + + Too many columns to display in %1 mode. + 在 %1 模å¼ä¸‹æœ‰å¤ªå¤šåˆ—éœ€è¦æ˜¾ç¤ºã€‚ - - Row %1 - 行 %1 + + Row %1 + 行 %1 - - Query execution error: %1 - 查询执行错误:%1 + + Query execution error: %1 + 查询执行错误:%1 - - + + CliCommandTables - - No such database: %1. Use %2 to see list of known databases. - 没有这样一个数æ®åº“:%1。使用 %2 去查看已知的数æ®åº“列表。 + + No such database: %1. Use %2 to see list of known databases. + 没有这样一个数æ®åº“:%1。使用 %2 去查看已知的数æ®åº“列表。 - - Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. - + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + æ²¡æœ‰è®¾ç½®å½“å‰æ•°æ®åº“时无法调用 %1。用 %2 å‘½ä»¤æŒ‡å®šå½“å‰æ•°æ®åº“,或者传递数æ®åº“å称到 %3。 - - Database %1 is closed. - æ•°æ®åº“已被关闭 + + Database %1 is closed. + æ•°æ®åº“ %1 已关闭。 - - - Database - æ•°æ®åº“ + + + Database + æ•°æ®åº“ - - Table - 表 + + Table + 表 - - prints list of tables in the database - 列出数æ®åº“中的所有表 + + prints list of tables in the database + 列出数æ®åº“中的所有表 - - Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. When the -s option is given, then system tables are also listed. - + 列出指定的 <database> æˆ–å½“å‰æ“作的数æ®åº“的表。注æ„,<database> 应是已注册的数æ®åº“çš„åç§°ï¼ˆè§ %1ï¼‰ã€‚è¾“å‡ºçš„åˆ—è¡¨åŒæ—¶åŒ…å«å·²é™„加到被查询数æ®åº“的其他数æ®åº“的所有表。æä¾› -s é€‰é¡¹æ—¶ï¼Œå°†åŒæ—¶åˆ—出系统表。 - - database - CLI command syntax - æ•°æ®åº“ + + database + CLI command syntax + æ•°æ®åº“ - - + + CliCommandTree - - No current working database is selected. Use %1 to define one and then run %2. - + + No current working database is selected. Use %1 to define one and then run %2. + ç›®å‰æ²¡æœ‰é€‰æ‹©è¦æ“作的数æ®åº“。使用 %1 定义一个,然åŽè¿è¡Œ %2。 - - Tables - 表 + + Tables + 表 - - Views - 视图 + + Views + 视图 - - Columns - 字段 + + Columns + 列 - - Indexes - 索引 + + Indexes + 索引 - - - Triggers - 触å‘器 + + + Triggers + 触å‘器 - - prints all objects in the database as a tree - + + prints all objects in the database as a tree + 将数æ®åº“中的所有对象列为一个树 - - Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. When -c option is given, then also columns will be listed under each table. When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. - + 列出数æ®åº“中的所有对象(表ã€ç´¢å¼•ã€è§¦å‘器和视图)为一个树。此树éžå¸¸ç±»ä¼¼æ‚¨åœ¨ SQLiteStudio 的图形用户界é¢ï¼ˆGUI)版本中看到的效果。 +æä¾› -c é€‰é¡¹æ—¶ï¼Œä¼šåŒæ—¶åœ¨æ¯ä¸ªè¡¨ä¸‹åˆ—出它的列。 +æä¾› -s é€‰é¡¹æ—¶ï¼Œä¼šåŒæ—¶åˆ—出系统对象(sqlite_* 表ã€è‡ªåŠ¨å¢žé‡ç´¢å¼•等)。 +æ•°æ®åº“傿•°ä¸ºå¯é€‰ï¼Œå¦‚æžœæä¾›åˆ™ä»…列出所给出的数æ®åº“ã€‚è¿™ä¸æ˜¯æ•°æ®åº“在列表中注册的å称,而是其在 SQLIte æ•°æ®åº“内部的å称,例如 'main'ã€'temp' 等。如果è¦åˆ—出列表中注册的其他数æ®åº“,先调用 %1 切æ¢å½“剿“作的数æ®åº“,然åŽå†ä½¿ç”¨ %2 命令。 - - + + CliCommandUse - - No current database selected. - + + No current database selected. + ç›®å‰æ²¡æœ‰é€‰æ‹©æ•°æ®åº“。 - - - Current database: %1 - 当剿•°æ®åº“:%1 + + + Current database: %1 + 当剿•°æ®åº“:%1 - - No such database: %1 - 没有这样一个数æ®åº“:%1 + + No such database: %1 + 没有这样一个数æ®åº“:%1 - - changes default working database - 更改默认工作数æ®åº“ + + changes default working database + 更改默认æ“作的数æ®åº“ - - Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. What is current working database? When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). @@ -711,80 +740,136 @@ The default database can be selected in various ways: - by passing registered database name to the application startup parameters, - by restoring previously selected default database from saved configuration, - or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. - + æ›´æ”¹å½“å‰æ“作的数æ®åº“至 <name>。如果 <name> æ•°æ®åº“没有在本程åºä¸­æ³¨å†Œï¼Œå°†ç»™å‡ºé”™è¯¯æ¶ˆæ¯å¹¶ä¸”什么也ä¸åšã€‚ + +ä»€ä¹ˆæ˜¯å½“å‰æ“作的数æ®åº“。 +å½“æ‚¨è¾“å…¥ä¸€æ¡ SQL 查询以期执行时,它会在默认数æ®åº“ä¸Šæ‰§è¡Œï¼Œè¿™ä¹Ÿè¢«ç§°ä¸ºå½“å‰æ“作(或称作业)的数æ®åº“。大多数与数æ®åº“相关的命令也在没有é¢å¤–指明时使用默认数æ®åº“。当å‰çš„æ•°æ®åº“会始终在命令行中标明。会始终有一个默认数æ®åº“ï¼Œé™¤éžæ•°æ®åº“列表为空。 + +æœ‰å¤šç§æ–¹å¼é€‰æ‹©é»˜è®¤æ•°æ®åº“。 +- 使用 %1 命令; +- 本程åºå¯åŠ¨æ—¶å°†æ•°æ®åº“的文件å作为å¯åЍ傿•°ä¼ å…¥ï¼› +- 本程åºå¯åŠ¨æ—¶å°†å·²æ³¨å†Œçš„æ•°æ®åº“åç§°ï¼› +- 从已ä¿å­˜çš„é…置文件还原之å‰é€‰æ‹©çš„默认数æ®åº“ï¼› +- 未通过以上任何方å¼é€‰æ‹©é»˜è®¤æ•°æ®åº“时,注册的数æ®åº“列表中的第一个数æ®åº“将作为默认数æ®åº“。 - - name - CLI command syntax - åç§° + + name + CLI command syntax + åç§° - - + + QObject - - Insufficient number of arguments. - + + Insufficient number of arguments. + 傿•°æ•°é‡ä¸è¶³ã€‚ - - Too many arguments. - 傿•°è¿‡å¤šã€‚ + + Too many arguments. + 傿•°è¿‡å¤šã€‚ - - Invalid argument value: %1. + + Invalid argument value: %1. Expected one of: %2 - + æ— æ•ˆå‚æ•°å€¼ï¼š%1。 +预期å¯èƒ½æ˜¯ï¼š%2 + + + + Unknown option: %1 + CLI command syntax + 未知选项:%1 + + + + Option %1 requires an argument. + CLI command syntax + 选项 %1 è¦æ±‚ä¸€ä¸ªå‚æ•°ã€‚ + + + + string + CLI command syntax + 字符串 + + + + Command line interface to SQLiteStudio, a SQLite manager. + SQLite 管ç†å·¥å…· SQLiteStudio 的命令行接å£ã€‚ + + + + Enables debug messages on standard error output. + å¯ç”¨è°ƒè¯•消æ¯è¾“出到标准错误输出。 + + + + Enables Lemon parser debug messages for SQL code assistant. + å¯ç”¨ SQL 代ç åŠ©æ‰‹çš„ Lemon è§£æžå™¨è°ƒè¯•消æ¯ã€‚ + + + + Lists plugins installed in the SQLiteStudio and quits. + 列出 SQLiteStudio 中已安装的æ’ä»¶ç„¶åŽé€€å‡ºã€‚ + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL 文件 - - Unknown option: %1 - CLI command syntax - + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. - - Option %1 requires an argument. - CLI command syntax - + + codec + codec - - string - CLI command syntax - + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. - - Command line interface to SQLiteStudio, a SQLite manager. - + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. - - Enables debug messages on standard error output. - + + file + 文件 - - Enables Lemon parser debug messages for SQL code assistant. - + + Database file to open + è¦æ‰“开的数æ®åº“文件 - - Lists plugins installed in the SQLiteStudio and quits. - + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. - - file - 文件 + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. - - Database file to open - + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. - + diff --git a/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_TW.ts b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_TW.ts new file mode 100644 index 0000000..5f96eca --- /dev/null +++ b/SQLiteStudio3/sqlitestudiocli/translations/sqlitestudiocli_zh_TW.ts @@ -0,0 +1,876 @@ + + + + + CLI + + + Current database: %1 + ç•¶å‰è³‡æ–™åº«ï¼š%1 + + + + No current working database is set. + ç›®å‰æœªè¨­å®šæ“作的資料庫。 + + + + Type %1 for help + 輸入 %1 ç²å–幫助 + + + + Database passed in command line parameters (%1) was already on the list under name: %2 + é€éŽå‘½ä»¤åˆ—引數傳入的資料庫(%1)已在清單中,å為:%2 + + + + Could not add database %1 to list. + 未將資料庫“%1â€æ–°å¢žåˆ°æ¸…單。 + + + + closed + 已關閉 + + + + CliCommand + + + Usage: %1%2 + 用法:%1%2 + + + + CliCommandAdd + + + Could not add database %1 to list. + 未將資料庫“%1â€æ–°å¢žåˆ°æ¸…單。 + + + + Database added: %1 + 已新增資料庫:%1 + + + + adds new database to the list + 新增新的資料庫到清單 + + + + Adds given database pointed by <path> with given <name> to list the databases list. The <name> is just a symbolic name that you can later refer to. Just pick any unique name. For list of databases already on the list use %1 command. + 新增指定<路徑>的資料庫到資料庫清單,用指定的<å稱>。<å稱>是您之後å¯ä»¥ç”¨ä¾†å¼•用它的åç¨±ã€‚é¸æ“‡ä¸€å€‹ä¸é‡è¤‡çš„å稱。查閱已在資料庫清單中的資料庫,請用 %1 命令。 + + + + name + CLI command syntax + å稱 + + + + path + CLI command syntax + 路徑 + + + + CliCommandCd + + + Changed directory to: %1 + 目錄已改為:%1 + + + + Could not change directory to: %1 + 未能切æ›åˆ°ç›®éŒ„:%1 + + + + changes current working directory + 更改當å‰çš„工作目錄 + + + + Very similar command to 'cd' known from Unix systems and Windows. It requires a <path> argument to be passed, therefore calling %1 will always cause a change of the directory. To learn what's the current working directory use %2 command and to list contents of the current working directory use %3 command. + éžå¸¸é¡žä¼¼ Unix å’Œ Windows 系統中的 'cd' 命令。需è¦å‚³å…¥ä¸€å€‹<路徑>å¼•æ•¸ï¼Œå› æ­¤å‘¼å« %1 將始終導致目錄的更改。 è¦äº†è§£ç•¶å‰å·¥ä½œç›®éŒ„,請使用 %2 命令並使用 %3 命令列出當å‰å·¥ä½œç›®éŒ„的內容。 + + + + path + CLI command syntax + 路徑 + + + + CliCommandClose + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + 沒有設定當å‰è³‡æ–™åº«æ™‚ç„¡æ³•å‘¼å« %1。使用 %2 命令指定當å‰è³‡æ–™åº«ï¼Œæˆ–者傳éžè³‡æ–™åº«å稱到 %3。 + + + + + Connection to database %1 closed. + 資料庫 %1 的連線已關閉。 + + + + No such database: %1. Use %2 to see list of known databases. + 沒有這樣的資料庫:%1。使用 %2 檢視已知資料庫清單。 + + + + closes given (or current) database + 關閉指定的或當å‰çš„資料庫 + + + + Closes database connection. If the database was already closed, nothing happens. If <name> is provided, it should be name of the database to close (as printed by %1 command). The the <name> is not provided, then current working database is closed (see help for %2 for details). + 關閉資料庫連線。如果資料庫已關閉,什麼也ä¸åšã€‚如果æä¾›äº†<å稱>,則表示需è¦é—œé–‰çš„資料庫的å稱(見 %1 å‘½ä»¤çš„çµæžœ)。如果沒有æä¾›<å稱>ã€‚å‰‡é—œé–‰ç•¶å‰æ“作的資料庫(詳見 %2 的幫助)。 + + + + name + CLI command syntax + å稱 + + + + CliCommandDbList + + + No current working database defined. + ç›®å‰æœªå®šç¾©æ“作的資料庫。 + + + + Databases: + 資料庫: + + + + + Name + CLI db name column + å稱 + + + + + Open + CLI connection state column + 開啟 + + + + + Closed + CLI connection state column + 關閉 + + + + + Connection + CLI connection state column + 連線 + + + + + Database file path + 資料庫檔案路徑 + + + + prints list of registered databases + 列å°å·²è¨»å†Šè³‡æ–™åº«æ¸…å–® + + + + Prints list of databases registered in the SQLiteStudio. Each database on the list can be in open or closed state and %1 tells you that. The current working database (aka default database) is also marked on the list with '*' at the start of its name. See help for %2 command to learn about the default database. + 列出在 SQLiteStudio 中註冊的資料庫的清單。清單中的æ¯å€‹è³‡æ–™åº«éƒ½å¯ä»¥è™•於開啟或關閉狀態,用 %1 檢視狀態。 ç•¶å‰å·¥ä½œè³‡æ–™åº«(åˆåé è¨­è³‡æ–™åº«)也在清單中,在它å字的開頭用'*'標記。請åƒé–± %2 命令的幫助以瞭解é è¨­è³‡æ–™åº«ã€‚ + + + + CliCommandDesc + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + 沒有設定æ“作的資料庫。 +å‘¼å« %1 命令æ“作的資料庫。 +å‘¼å« %2 查閱資料庫清單。 + + + + Database is not open. + 資料庫未被開啟。 + + + + Cannot find table named: %1 + 無法找到å為 %1 的表 + + + + shows details about the table + 顯示一個表的詳細資訊 + + + + table + 表 + + + + Table: %1 + 表:%1 + + + + Column name + 欄ä½å + + + + Data type + 資料型別 + + + + Constraints + ç´„æŸæ¢ä»¶ + + + + Virtual table: %1 + 虛擬表:%1 + + + + Construction arguments: + 構造引數: + + + + No construction arguments were passed for this virtual table. + 沒有為此虛擬表傳éžçµæ§‹å¼•數。 + + + + CliCommandDir + + + lists directories and files in current working directory + 列出當å‰å·¥ä½œç›®éŒ„中的目錄與檔案 + + + + This is very similar to 'dir' command known from Windows and 'ls' command from Unix systems. + +You can pass <pattern> with wildcard characters to filter output. + 這éžå¸¸é¡žä¼¼ Windows 中的 'dir' 命令與 Unix 中的 'ls' 命令。 + +å¯ä»¥å‚³å…¥ä¸€å€‹å¸¶æœ‰è¬ç”¨å­—元的<模å¼>ä¾†éŽæ¿¾è¼¸å‡ºå…§å®¹ã€‚ + + + + pattern + æ¨¡å¼ + + + + CliCommandExit + + + quits the application + é€€å‡ºæœ¬ç¨‹å¼ + + + + Quits the application. Settings are stored in configuration file and will be restored on next startup. + 退出本程å¼ã€‚設定已儲存在設定檔檔案,將在下一次啟動時æ¢å¾©ã€‚ + + + + CliCommandHelp + + + shows this help message + 顯示這個幫助資訊 + + + + Use %1 to learn about certain commands supported by the command line interface (CLI) of the SQLiteStudio. +To see list of supported commands, type %2 without any arguments. + +When passing <command> name, you can skip special prefix character ('%3'). + +You can always execute any command with exactly single '--help' option to see help for that command. It's an alternative for typing: %1 <command>. + 使用 %1 çž­è§£ SQLiteStudio 的命令列介é¢(CLI)所支æ´çš„特定命令。 +輸入 %2 ä¸å¸¶ä»»ä½•引數來檢視支æ´çš„命令清單。 + +傳入<命令>å稱時,您å¯ä»¥è·³éŽç‰¹æ®Šçš„字首字元('%3')。 + +您å¯ä»¥ç‚ºä»»ä½•命令指定 '--help' é¸é …並執行來檢視特定命令的幫助。å¦ä¸€ç¨®æ–¹æ³•:%1 <命令>。 + + + + command + CLI command syntax + 命令 + + + + No such command: %1 + 沒有這個命令:%1 + + + + Type '%1' for list of available commands. + 輸入 '%1' 列出所有å¯ç”¨çš„命令。 + + + + Usage: %1%2 + 用法:%1%2 + + + + Aliases: %1 + 別å:%1 + + + + CliCommandHistory + + + Current history limit is set to: %1 + ç•¶å‰æ­·å²è¨˜éŒ„é™åˆ¶ç‚ºï¼š%1 + + + + prints history or erases it + åˆ—å‡ºæ­·å²æˆ–擦除 + + + + When no argument was passed, this command prints command line history. Every history entry is separated with a horizontal line, so multiline entries are easier to read. + +When the -c or --clear option is passed, then the history gets erased. +When the -l or --limit option is passed, it sets the new history entries limit. It requires an additional argument saying how many entries do you want the history to be limited to. +Use -ql or --querylimit option to see the current limit value. + 沒有傳入引數時,此命令列出命令列歷å²ã€‚æ¯æ¢æ­·å²ä»¥æ°´å¹³ç·šéš”é–‹ï¼Œä»¥ä½¿å¤šè¡Œå–®æ¢æ›´æ˜“閱讀。 + +傳入 -c 或 --clear é¸é …,歷å²è¨˜éŒ„將被清空擦除。 +傳入 -l 或 --limit é¸é …,設定歷å²è¨˜éŒ„æ¢æ•¸é™åˆ¶ã€‚需è¦é™„上é¡å¤–引數,指明將歷å²è¨˜éŒ„é™åˆ¶ç‚ºæœ€å¤šå¤šå°‘æ¢ã€‚ +使用 -ql 或 --querylimit é¸é …ï¼Œå¯æª¢è¦–ç•¶å‰çš„é™åˆ¶å€¼ã€‚ + + + + number + 數值 + + + + Console history erased. + 控制檯歷å²å·²æ“¦é™¤ã€‚ + + + + Invalid number: %1 + 無效數值:%1 + + + + History limit set to %1 + æ­·å²è¨˜éŒ„é™åˆ¶å·²è¨­ç‚º %1 + + + + CliCommandMode + + + Current results printing mode: %1 + ç•¶å‰çµæžœåˆ—å°æ¨¡å¼ï¼š%1 + + + + Invalid results printing mode: %1 + ç„¡æ•ˆçµæžœåˆ—å°æ¨¡å¼ï¼š%1 + + + + New results printing mode: %1 + æ–°çµæžœåˆ—å°æ¨¡å¼ï¼š%1 + + + + tells or changes the query results format + è©¢å•æˆ–æ›´æ”¹æŸ¥è©¢çµæžœçš„æ ¼å¼ + + + + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + When called without argument, tells the current output format for a query results. When the <mode> is passed, the mode is changed to the given one. Supported modes are: +- CLASSIC - columns are separated by a comma, not aligned, +- FIXED - columns have equal and fixed width, they always fit into terminal window width, but the data in columns can be cut off, +- COLUMNS - like FIXED, but smarter (do not use with huge result sets, see details below), +- ROW - each column from the row is displayed in new line, so the full data is displayed. + +The CLASSIC mode is recommended if you want to see all the data, but you don't want to waste lines for each column. Each row will display full data for every column, but this also means, that columns will not be aligned to each other in next rows. The CLASSIC mode also doesn't respect the width of your terminal (console) window, so if values in columns are wider than the window, the row will be continued in next lines. + +The FIXED mode is recommended if you want a readable output and you don't care about long data values. Columns will be aligned, making the output a nice table. The width of columns is calculated from width of the console window and a number of columns. + +The COLUMNS mode is similar to FIXED mode, except it tries to be smart and make columns with shorter values more thin, while columns with longer values get more space. First to shrink are columns with longest headers (so the header names are to be cut off as first), then columns with the longest values are shrinked, up to the moment when all columns fit into terminal window. +ATTENTION! The COLUMNS mode reads all the results from the query at once in order to evaluate column widths, therefore it is dangerous to use this mode when working with huge result sets. Keep in mind that this mode will load entire result set into memory. + +The ROW mode is recommended if you need to see whole values and you don't expect many rows to be displayed, because this mode displays a line of output per each column, so you'll get 10 lines for single row with 10 columns, then if you have 10 of such rows, you will get 100 lines of output (+1 extra line per each row, to separate rows from each other). + + + + CliCommandNullValue + + + Current NULL representation string: %1 + ç•¶å‰è¡¨ç¤º NULL 的字串:%1 + + + + tells or changes the NULL representation string + è©¢å•æˆ–更改表示 NULL 的字串 + + + + If no argument was passed, it tells what's the current NULL value representation (that is - what is printed in place of NULL values in query results). If the argument is given, then it's used as a new string to be used for NULL representation. + 如果ä¸å‚³å…¥ä»»ä½•引數,則會告知當å‰çš„ NULL 值表示方法(峿Ÿ¥è©¢çµæžœä¸­ä»¥ä»€éº¼ä»£è¡¨ NULL 值)。如果æä¾›äº†å¼•數,則引數將作為新的代表 NULL 值的字串。 + + + + CliCommandOpen + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + 沒有設定當å‰è³‡æ–™åº«æ™‚ç„¡æ³•å‘¼å« %1。使用 %2 命令指定當å‰è³‡æ–™åº«ï¼Œæˆ–者傳éžè³‡æ–™åº«å稱到 %3。 + + + + Could not add database %1 to list. + 未能將資料庫“%1â€æ–°å¢žåˆ°æ¸…單。 + + + + File %1 doesn't exist in %2. Cannot open inexisting database with %3 command. To create a new database, use %4 command. + 檔案 %1 ä¸å­˜åœ¨æ–¼ %2。無法使用 %3 命令開啟ä¸å­˜åœ¨çš„資料庫。使用 %4 命令建立一個新資料庫。 + + + + Database %1 has been open and set as the current working database. + 已開啟資料庫 %1 ä¸¦å°‡å…¶è¨­ç‚ºç•¶å‰æ“作的資料庫。 + + + + opens database connection + 開啟資料庫連線 + + + + Opens connection to the database. If no additional argument was passed, then the connection is open to the current default database (see help for %1 for details). However if an argument was passed, it can be either <name> of the registered database to open, or it can be <path> to the database file to open. In the second case, the <path> gets registered on the list with a generated name, but only for the period of current application session. After restarting application such database is not restored on the list. + é–‹å•Ÿåˆ°è³‡æ–™åº«çš„é€£ç·šã€‚å¦‚æžœä¸æä¾›é¡å¤–的引數,則開啟到當å‰çš„é è¨­è³‡æ–™åº«(詳見 %1)的連線。如果æä¾›ä¸€å€‹å¼•數,它å¯ä»¥æ˜¯å·²è¨»å†Šçš„資料庫的<å稱>,也å¯ä»¥æ˜¯è¦é–‹å•Ÿçš„資料庫檔案的<路徑>。第二種情æ³ä¸‹ï¼Œ<路徑>將使用自動生æˆçš„å稱臨時註冊到資料庫清單,並在應用程å¼é€€å‡ºæ™‚從清單中消失。 + + + + name + CLI command syntax + å稱 + + + + path + CLI command syntax + 路徑 + + + + CliCommandPwd + + + prints the current working directory + 列出當å‰çš„工作目錄 + + + + This is the same as 'pwd' command on Unix systems and 'cd' command without arguments on Windows. It prints current working directory. You can change the current working directory with %1 command and you can also list contents of the current working directory with %2 command. + 這與 Unix 系統上的 'pwd' å‘½ä»¤ä»¥åŠ Windows 系統上沒有引數的 'cd' 命令作用相åŒã€‚將列出當å‰çš„工作目錄。使用 %1 命令å¯ä»¥æ›´æ”¹ç•¶å‰çš„工作目錄,您也å¯ä»¥ç”¨ %2 命令列出當å‰å·¥ä½œç›®éŒ„的內容。 + + + + CliCommandRemove + + + No such database: %1 + 沒有這樣一個數據庫:%1 + + + + Database removed: %1 + 資料庫已移除:%1 + + + + New current database set: + 新的當å‰è³‡æ–™åº«è¨­ç‚ºï¼š + + + + removes database from the list + 從清單中移除資料庫 + + + + Removes <name> database from the list of registered databases. If the database was not on the list (see %1 command), then error message is printed and nothing more happens. + 從已註冊資料庫清單中移除å為<å稱>的資料庫。如果清單(見 %1 命令)中沒有所指定的資料庫 ,會給出錯誤訊æ¯ã€‚ + + + + name + CLI command syntax + å稱 + + + + CliCommandSql + + + No working database is set. +Call %1 command to set working database. +Call %2 to see list of all databases. + 沒有設定æ“作的資料庫。 +å‘¼å« %1 命令æ“作的資料庫。 +å‘¼å« %2 查閱資料庫清單。 + + + + Database is not open. + 資料庫沒有開啟。 + + + + executes SQL query + 執行 SQL 查詢 + + + + This command is executed every time you enter SQL query in command prompt. It executes the query on the current working database (see help for %1 for details). There's no sense in executing this command explicitly. Instead just type the SQL query in the command prompt, without any command prefixed. + æ‚¨æ¯æ¬¡åœ¨å‘½ä»¤åˆ—æç¤ºç¬¦ä¸­è¼¸å…¥ SQL æŸ¥è©¢æ™‚æœƒåŸ·è¡Œæ­¤å‘½ä»¤ã€‚å®ƒè² è²¬åœ¨ç•¶å‰æ“作的資料庫(詳見 %1)上執行查詢。專門執行此命令沒有任何æ„義。您å¯ä»¥åœ¨å‘½ä»¤åˆ—æç¤ºç¬¦ä¸­ç›´æŽ¥è¼¸å…¥ SQL 查詢,無需新增命令字首。 + + + + sql + CLI command syntax + sql + + + + + Too many columns to display in %1 mode. + 在 %1 模å¼ä¸‹æœ‰å¤ªå¤šåˆ—需è¦é¡¯ç¤ºã€‚ + + + + Row %1 + 行 %1 + + + + Query execution error: %1 + 查詢執行錯誤:%1 + + + + CliCommandTables + + + No such database: %1. Use %2 to see list of known databases. + 沒有這樣一個數據庫:%1。使用 %2 檢視已知的資料庫清單。 + + + + Cannot call %1 when no database is set to be current. Specify current database with %2 command or pass database name to %3. + 沒有設定當å‰è³‡æ–™åº«æ™‚ç„¡æ³•å‘¼å« %1。用 %2 命令指定當å‰è³‡æ–™åº«ï¼Œæˆ–者傳éžè³‡æ–™åº«å稱到 %3。 + + + + Database %1 is closed. + 資料庫 %1 已關閉。 + + + + + Database + 資料庫 + + + + Table + 表 + + + + prints list of tables in the database + 列出資料庫中的所有表 + + + + Prints list of tables in given <database> or in the current working database. Note, that the <database> should be the name of the registered database (see %1). The output list includes all tables from any other databases attached to the queried database. +When the -s option is given, then system tables are also listed. + 列出指定的<資料庫>æˆ–ç•¶å‰æ“作的資料庫的表。注æ„,<資料庫>應是已註冊的資料庫的å稱(見 %1)ã€‚è¼¸å‡ºçš„æ¸…å–®åŒæ™‚包å«å·²é™„加到被查詢資料庫的其他資料庫的所有表。 +æä¾› -s é¸é …æ™‚ï¼Œå°‡åŒæ™‚列出系統表。 + + + + database + CLI command syntax + 資料庫 + + + + CliCommandTree + + + No current working database is selected. Use %1 to define one and then run %2. + ç›®å‰æ²’æœ‰é¸æ“‡è¦æ“作的資料庫。使用 %1 定義一個,然後執行 %2。 + + + + Tables + 表 + + + + Views + 檢視 + + + + Columns + 列 + + + + Indexes + 索引 + + + + + Triggers + 觸發器 + + + + prints all objects in the database as a tree + 將資料庫中的所有物件列為一個樹 + + + + Prints all objects (tables, indexes, triggers and views) that are in the database as a tree. The tree is very similar to the one that you can see in GUI client of the SQLiteStudio. +When -c option is given, then also columns will be listed under each table. +When -s option is given, then also system objects will be printed (sqlite_* tables, autoincrement indexes, etc). +The database argument is optional and if provided, then only given database will be printed. This is not a registered database name, but instead it's an internal SQLite database name, like 'main', 'temp', or any attached database name. To print tree for other registered database, call %1 first to switch the working database, and then use %2 command. + 列出資料庫中的所有物件(表ã€ç´¢å¼•ã€è§¸ç™¼å™¨å’Œæª¢è¦–)為一個樹。此樹éžå¸¸é¡žä¼¼æ‚¨åœ¨ SQLiteStudio 的圖形使用者介é¢(GUI)版本中看到的效果。 +æä¾› -c é¸é …æ™‚ï¼ŒæœƒåŒæ™‚在æ¯å€‹è¡¨ä¸‹åˆ—出它的列。 +æä¾› -s é¸é …æ™‚ï¼ŒæœƒåŒæ™‚列出系統物件(sqlite_* 表ã€è‡ªå‹•增é‡ç´¢å¼•ç­‰)。 +資料庫引數為å¯é¸ï¼Œå¦‚æžœæä¾›å‰‡åƒ…åˆ—å‡ºæ‰€çµ¦å‡ºçš„è³‡æ–™åº«ã€‚é€™ä¸æ˜¯è³‡æ–™åº«åœ¨æ¸…單中註冊的å稱,而是其在 SQLIte 資料庫內部的å稱,例如 'main'ã€'temp' 等。如果è¦åˆ—å‡ºæ¸…å–®ä¸­è¨»å†Šçš„å…¶ä»–è³‡æ–™åº«ï¼Œå…ˆå‘¼å« %1 切æ›ç•¶å‰æ“作的資料庫,然後å†ä½¿ç”¨ %2 命令。 + + + + CliCommandUse + + + No current database selected. + ç›®å‰æ²’æœ‰é¸æ“‡è³‡æ–™åº«ã€‚ + + + + + Current database: %1 + ç•¶å‰è³‡æ–™åº«ï¼š%1 + + + + No such database: %1 + 沒有這樣一個數據庫:%1 + + + + changes default working database + 更改é è¨­æ“作的資料庫 + + + + Changes current working database to <name>. If the <name> database is not registered in the application, then the error message is printed and no change is made. + +What is current working database? +When you type a SQL query to be executed, it is executed on the default database, which is also known as the current working database. Most of database-related commands can also work using default database, if no database was provided in their arguments. The current database is always identified by command line prompt. The default database is always defined (unless there is no database on the list at all). + +The default database can be selected in various ways: +- using %1 command, +- by passing database file name to the application startup parameters, +- by passing registered database name to the application startup parameters, +- by restoring previously selected default database from saved configuration, +- or when default database was not selected by any of the above, then first database from the registered databases list becomes the default one. + æ›´æ”¹ç•¶å‰æ“作的資料庫至<å稱>。如果<å稱>資料庫沒有在本程å¼ä¸­è¨»å†Šï¼Œå°‡çµ¦å‡ºéŒ¯èª¤è¨Šæ¯ä¸¦ä¸”什麼也ä¸åšã€‚ + +ä»€éº¼æ˜¯ç•¶å‰æ“作的資料庫? +ç•¶æ‚¨è¼¸å…¥ä¸€æ¢ SQL 查詢以期執行時,它會在é è¨­è³‡æ–™åº«ä¸ŠåŸ·è¡Œï¼Œé€™ä¹Ÿè¢«ç¨±ç‚ºç•¶å‰æ“作(或稱作業)的資料庫。大多數與資料庫相關的命令也在沒有é¡å¤–指明時使用é è¨­è³‡æ–™åº«ã€‚ç•¶å‰çš„資料庫會始終在命令列中標明。會始終有一個é è¨­è³‡æ–™åº«ï¼Œé™¤éžè³‡æ–™åº«æ¸…單為空。 + +有多種方å¼é¸æ“‡é è¨­è³‡æ–™åº«ã€‚ +- 使用 %1 命令; +- 本程å¼å•Ÿå‹•時將資料庫的檔å作為啟動引數傳入; +- 本程å¼å•Ÿå‹•時將已註冊的資料庫å稱; +- 從已儲存的設定檔檔案還原之å‰é¸æ“‡çš„é è¨­è³‡æ–™åº«ï¼› +- 未é€éŽä»¥ä¸Šä»»ä½•æ–¹å¼é¸æ“‡é è¨­è³‡æ–™åº«æ™‚,註冊的資料庫清單中的第一個資料庫將作為é è¨­è³‡æ–™åº«ã€‚ + + + + name + CLI command syntax + å稱 + + + + QObject + + + Insufficient number of arguments. + 引數數é‡ä¸è¶³ã€‚ + + + + Too many arguments. + 引數éŽå¤šã€‚ + + + + Invalid argument value: %1. +Expected one of: %2 + 無效引數值:%1。 +é æœŸå¯èƒ½æ˜¯ï¼š%2 + + + + Unknown option: %1 + CLI command syntax + 未知é¸é …:%1 + + + + Option %1 requires an argument. + CLI command syntax + é¸é … %1 è¦æ±‚一個引數。 + + + + string + CLI command syntax + 字串 + + + + Command line interface to SQLiteStudio, a SQLite manager. + SQLite 管ç†å·¥å…· SQLiteStudio 的命令列介é¢ã€‚ + + + + Enables debug messages on standard error output. + 啟用除錯訊æ¯è¼¸å‡ºåˆ°æ¨™æº–錯誤輸出。 + + + + Enables Lemon parser debug messages for SQL code assistant. + 啟用 SQL 程å¼ç¢¼åŠ©æ‰‹çš„ Lemon è§£æžå™¨é™¤éŒ¯è¨Šæ¯ã€‚ + + + + Lists plugins installed in the SQLiteStudio and quits. + 列出 SQLiteStudio 中已安è£çš„外掛然後退出。 + + + + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + Executes provided SQL file (including all rich features of SQLiteStudio's query executor) on the specified database file and quits. The database parameter becomes mandatory if this option is used. + + + + SQL file + SQL file + + + + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + Character encoding to use when reading SQL file (-e option). Use -cl to list available codecs. Defaults to %1. + + + + codec + codec + + + + Lists available codecs to be used with -c option and quits. + Lists available codecs to be used with -c option and quits. + + + + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + When used together with -e option, the execution will not stop on an error, but rather continue until the end, ignoring errors. + + + + file + 檔案 + + + + Database file to open + è¦é–‹å•Ÿçš„資料庫檔案 + + + + Invalid codec: %1. Use -cl option to list available codecs. + Invalid codec: %1. Use -cl option to list available codecs. + + + + Database file argument is mandatory when executing SQL file. + Database file argument is mandatory when executing SQL file. + + + + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + Could not open specified database for executing SQL file. You may try using -d option to find out more details. + + + diff --git a/SQLiteStudio3/ts.xsd b/SQLiteStudio3/ts.xsd new file mode 100644 index 0000000..8627b6b --- /dev/null +++ b/SQLiteStudio3/ts.xsd @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 0000000..c745d47 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,5 @@ +files: + - source: /**/*.ts + ignore: + - /**/*_*.ts + translation: /**/%file_name%_%locale_with_underscore%.%file_extension% diff --git a/release-checklist.md b/release-checklist.md index 1023292..b877183 100644 --- a/release-checklist.md +++ b/release-checklist.md @@ -1,16 +1,10 @@ - [x] Upgrade SQLCipher, System.Data.SQLite and WxSqlite versions. +- [x] Update translations - [x] Check if app compiles for different Qt versions - [x] Verify final release version - [x] Update ChangeLog -- [x] Update managed languages per component/plugin -- [x] Update messages -- [x] Update translations -- [x] Release translations -- [x] Build final binaries & package them -- [x] Prepare updates repository files -- [x] Upload packages to download page -- [x] Upload update packages to online repository +- [x] Make tag +- [x] Build binaries from tag +- [x] Upload packages to releases page - [x] Update file checksums on the page. -- [x] Regenerate ChangeLog on website - [x] Update News on homepage -- [x] Post news on FB diff --git a/scripts/installer/assemble.tcl b/scripts/installer/assemble.tcl deleted file mode 100644 index bb343f8..0000000 --- a/scripts/installer/assemble.tcl +++ /dev/null @@ -1,422 +0,0 @@ -#!/usr/bin/env tclsh - -# tclsh assemble.tcl c:/tmp/installer [--repo] - -package require platform -lassign [split [platform::generic] -] OS ARCH - -set cfgFiles { - config.xml - bg.png - logo.png - sqlitestudio.icns - sqlitestudio.ico - watermark.png - controller.qs -} - -switch $::OS { - "macosx" { - set mainPkgFiles { - SQLiteStudio.app/Contents/Frameworks/libcoreSQLiteStudio.1.0.0.dylib - SQLiteStudio.app/Contents/Frameworks/libguiSQLiteStudio.1.0.0.dylib - SQLiteStudio.app/Contents/Frameworks/libsqlite3.0.dylib - SQLiteStudio.app/Contents/MacOS/SQLiteStudio - SQLiteStudio.app/Contents/MacOS/sqlitestudiocli - SQLiteStudio.app/Contents/Resources - SQLiteStudio.app/Contents/Info.plist - SQLiteStudio.app/Contents/PkgInfo - } - set qtPkgFiles { - SQLiteStudio.app/Contents/Frameworks/QtCore.framework - SQLiteStudio.app/Contents/Frameworks/QtGui.framework - SQLiteStudio.app/Contents/Frameworks/QtNetwork.framework - SQLiteStudio.app/Contents/Frameworks/QtPrintSupport.framework - SQLiteStudio.app/Contents/Frameworks/QtScript.framework - SQLiteStudio.app/Contents/Frameworks/QtSvg.framework - SQLiteStudio.app/Contents/Frameworks/QtWidgets.framework - SQLiteStudio.app/Contents/Frameworks/QtXml.framework - SQLiteStudio.app/Contents/PlugIns/bearer - SQLiteStudio.app/Contents/PlugIns/iconengines - SQLiteStudio.app/Contents/PlugIns/imageformats - SQLiteStudio.app/Contents/PlugIns/platforms - SQLiteStudio.app/Contents/PlugIns/printsupport - SQLiteStudio.app/Contents/PlugIns/styles - } - array set pluginDeps { - } - } - "win32" { - set mainPkgFiles { - coreSQLiteStudio.dll - guiSQLiteStudio.dll - sqlite3.dll - SQLiteStudio.exe - sqlitestudiocli.exe - } - set qtPkgFiles { - iconengines - imageformats - platforms - printsupport - styles - libeay32.dll - libgcc_s_dw2-1.dll - libstdc++-6.dll - libwinpthread-1.dll - qt.conf - Qt5Core.dll - Qt5Gui.dll - Qt5Network.dll - Qt5PrintSupport.dll - Qt5Script.dll - Qt5Svg.dll - Qt5Widgets.dll - Qt5Xml.dll - } - array set pluginDeps { - ScriptingTcl { - tcl86.dll - zlib1.dll - } - DbSqlite2 { - sqlite.dll - } - } - } - default { - set mainPkgFiles { - lib/libcoreSQLiteStudio.so - lib/libguiSQLiteStudio.so - lib/libsqlite3.so - sqlitestudio - sqlitestudiocli - } - set qtPkgFiles { - iconengines/libqsvgicon.so - imageformats/libqgif.so - imageformats/libqicns.so - imageformats/libqico.so - imageformats/ibqjpeg.so - imageformats/libqsvg.so - imageformats/libqtga.so - imageformats/libqtiff.so - platforms/libqxcb.so - printsupport/libcupsprintersupport.so - lib/libQt5Concurrent.so - lib/libQt5Core.so - lib/libQt5DBus.so - lib/libQt5Gui.so - lib/libQt5Network.so - lib/libQt5PrintSupport.so - lib/libQt5Script.so - lib/libQt5Svg.so - lib/libQt5Widgets.so - lib/libQt5XcbQpa.so - lib/libQt5Xml.so - lib/libicudata.so - lib/libicui18n.so - lib/libicuuc.so.56 - } - array set pluginDeps { - DbSqlite2 { - lib/libsqlite.so - } - } - } -} - -proc defineGlobalVars {} { - lassign $::argv ::targetDir - set ::releaseDate [clock format [clock seconds] -format "%Y-%m-%d"] - - # Version - set verFile "../../SQLiteStudio3/coreSQLiteStudio/sqlitestudio.cpp" - set fd [open $verFile r] - set ver [lindex [regexp -inline {int\s+sqlitestudioVersion\s*\=\s*(\d+)} [read $fd]] 1] - ######################################################################################### VER +1 - #incr ver - set ::sqliteStudioVersion [toVersion $ver] - set data [read $fd] - close $fd - - switch $::OS { - "macosx" { - set ::portableDir [file normalize ../../output] - set ::finalExecutable "Contents/MacOS/SQLiteStudio" - set ::wizardStyle "Mac" - set ::initialTargetDir "/Applications/SQLiteStudio.app" - set ::libExt "dylib" - set ::updateArch "macosx" - set ::pluginsDir "SQLiteStudio.app/Contents/PlugIns" - set ::libPref "lib" - set ::dirsToSkipInPathBeginning 1 - set qtCoreFile "$::portableDir/SQLiteStudio/SQLiteStudio.app/Contents/Frameworks/QtCore.framework/QtCore" - set ::output [file normalize $::targetDir/InstallSQLiteStudio-${::sqliteStudioVersion}] - } - "win32" { - set ::portableDir [file normalize ../../output/portable] - set ::finalExecutable "SQLiteStudio.exe" - set ::wizardStyle "Modern" - set ::initialTargetDir "C:\\Program Files\\SQLiteStudio" - set ::libExt "dll" - set ::updateArch "windows" - set ::pluginsDir "plugins" - set ::libPref "" - set ::dirsToSkipInPathBeginning 0 - set qtCoreFile "$::portableDir/SQLiteStudio/Qt5Core.dll" - set ::output [file normalize $::targetDir/InstallSQLiteStudio-${::sqliteStudioVersion}.exe] - } - default { - set ::portableDir [file normalize ../../output/portable] - set ::finalExecutable "sqlitestudio" - set ::wizardStyle "Modern" - set ::initialTargetDir "/opt/SQLiteStudio" - set ::updateArch "linux" - set ::pluginsDir "plugins" - set ::libExt "so" - set ::libPref "lib" - set ::dirsToSkipInPathBeginning 0 - set qtCoreFile "$::portableDir/SQLiteStudio/lib/libQt5Core.so" - set ::output [file normalize $::targetDir/InstallSQLiteStudio-${::sqliteStudioVersion}] - } - } - - # Qt version - set fd [open $qtCoreFile r] - chan configure $fd -translation binary -encoding binary - set data [read $fd] - close $fd - set ::qtVer [lindex [regexp -inline -- {Qt\s+(\d\.\d{1,2}\.\d{1,2})} $data] 1] - ######################################################################################### VER +1 - #set ::qtVer 6.0.0 - - set ::startApp $::finalExecutable - - # Repository - set ::repoDir [file normalize $::targetDir/REPO] -} - -proc toVersion {ver} { - return [expr {$ver / 10000}].[expr {$ver / 100 % 100}].[expr {$ver % 100}] -} - -proc mapFile {f props} { - set fd [open $f r] - set data [read $fd] - close $fd - - set data [string map $props $data] - - set fd [open $f w+] - puts $fd $data - close $fd -} - -proc initDirs {targetDir} { - file delete -force $::output $::repoDir $targetDir - file mkdir $targetDir $targetDir/config $targetDir/packages -} - -proc archiveContentsOf {dir intoFile} { - set cdir [pwd] - cd $dir - set files [glob *] - exec archivegen $intoFile {*}$files - file delete -force {*}$files - cd $cdir -} - -proc copyFileWithLinks {fromDir toDir file} { - set targetFile [file join {*}[lrange [file split $file] $::dirsToSkipInPathBeginning end]] - file mkdir [file dirname $toDir/$targetFile] - switch -- $::OS { - "macosx" { - file copy -force $fromDir/$file $toDir/$targetFile - set fd [file dirname $fromDir/$file] - set td [file dirname $toDir/$targetFile] - set fileOnly [lindex [file split $file] end] - set filePrefix [lindex [split $fileOnly .] 0] - foreach f [glob -nocomplain -tails -directory $fd "${filePrefix}.*.$::libExt"] { - file copy -force $fd/$f $td/$f - } - } - "win32" { - file copy -force $fromDir/$file $toDir/$targetFile - } - default { - set fd [file dirname $fromDir/$file] - set td [file dirname $toDir/$targetFile] - set fileOnly [lindex [file split $file] end] - foreach f [glob -nocomplain -tails -directory $fd "${fileOnly}*"] { - file copy -force $fd/$f $td/$f - } - } - } -} - -proc copyMainPkg {targetDir} { - set pkgDir $targetDir/packages/pl.com.salsoft.sqlitestudio - - puts "Copying core app files." - file mkdir $pkgDir $pkgDir/meta $pkgDir/data - foreach f $::mainPkgFiles { - copyFileWithLinks $::portableDir/SQLiteStudio $pkgDir/data $f - } - file copy ../../SQLiteStudio3/sqlitestudio/package.xml ../../SQLiteStudio3/sqlitestudio/register_file_types.ui ../../SQLiteStudio3/sqlitestudio/installscript.qs $pkgDir/meta/ - - file mkdir $pkgDir/data/app_icon - file copy ../../SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio.ico $pkgDir/data/app_icon - file copy ../../SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio.icns $pkgDir/data/app_icon - file copy ../../SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio.svg $pkgDir/data/app_icon - file copy ../../SQLiteStudio3/guiSQLiteStudio/img/sqlitestudio_logo.png $pkgDir/data/app_icon/sqlitestudio.png - - mapFile $pkgDir/meta/package.xml [list %VERSION% $::sqliteStudioVersion %DATE% $::releaseDate] - - puts "Compressing core app files." - archiveContentsOf $pkgDir/data sqlitestudio.7z -} - -proc copyQtPkg {targetDir} { - set pkgDir $targetDir/packages/io.qt - - puts "Copying Qt." - file mkdir $pkgDir $pkgDir/meta $pkgDir/data - foreach f $::qtPkgFiles { - copyFileWithLinks $::portableDir/SQLiteStudio $pkgDir/data $f - } - file copy ../../SQLiteStudio3/qt_package.xml $pkgDir/meta/package.xml - - mapFile $pkgDir/meta/package.xml [list %VERSION% $::qtVer %DATE% $::releaseDate] - - puts "Compressing Qt." - archiveContentsOf $pkgDir/data qt.7z -} - -proc copyConfig {targetDir} { - foreach f $::cfgFiles { - file copy config/$f $targetDir/config/ - } - - mapFile $targetDir/config/config.xml [list %SQLITESTUDIO_VERSION% $::sqliteStudioVersion %FINAL_EXECUTABLE% $::finalExecutable \ - %WIZARD_STYLE% $::wizardStyle %TARGET_DIR% $::initialTargetDir %UPDATE_ARCH% $::updateArch] -} - -proc createOutputBinary {targetDir} { - if {$::OS == "macosx"} { - puts "Creating installer binary: $::output.dmg" - exec binarycreator -f -p $targetDir/packages -c $targetDir/config/config.xml $::output.dmg - puts "Creating installer binary: $::output" - exec binarycreator -f -p $targetDir/packages -c $targetDir/config/config.xml $::output - } else { - puts "Creating installer binary: $::output" - exec binarycreator -f -p $targetDir/packages -c $targetDir/config/config.xml $::output - } -} - -proc createOutputRepo {targetDir} { - puts "Creating update repository: $::repoDir" - exec repogen -p $targetDir/packages $::repoDir -} - -proc readPluginVersion {path} { - set fd [open $path r] - set ver [lindex [regexp -inline -- {\"version\"\s*\:\s*(\d+)} [read $fd]] 1] - close $fd - return [toVersion $ver] -} - -proc collectPlugins {} { - set mask "*.$::libExt" - set toRemove [expr {[string length $mask] - 1}] - set prefToRemove [string length $::libPref] - - set files [glob -tails -directory $::portableDir/SQLiteStudio/$::pluginsDir $mask] - set ::plugins [list] - foreach f $files { - set plugin [string range $f $prefToRemove end-$toRemove] - set ver [readPluginVersion ../../Plugins/$plugin/[string tolower $plugin].json] - ######################################################################################### VER +1 - #set ver 9.9.9 - lappend ::plugins [dict create name $plugin version $ver] - } -} - -proc readPluginPkgName {path} { - set fd [open $path r] - set pkg [lindex [regexp -inline -- {\(.*)\<\/Name\>} [read $fd]] 1] - close $fd - return $pkg -} - -proc copyPluginPkg {pluginDict targetDir} { - set name [dict get $pluginDict name] - set ver [dict get $pluginDict version] - set packageXml ../../Plugins/$name/package.xml - - if {![file exists $packageXml]} { - puts "Skipping plugin $name, because package.xml for this plugin does not exist." - return - } - - set pkgName [readPluginPkgName $packageXml] - set pkgDir $targetDir/packages/$pkgName - - set targetPluginsDir [file join {*}[lrange [file split $::pluginsDir] $::dirsToSkipInPathBeginning end]] - - puts "Copying plugin: $name ($ver)" - file mkdir $pkgDir $pkgDir/meta $pkgDir/data $pkgDir/data/$targetPluginsDir - file copy -force $::portableDir/SQLiteStudio/$::pluginsDir/$::libPref${name}.$::libExt $pkgDir/data/$targetPluginsDir/ - if {[info exists ::pluginDeps($name)]} { - foreach f $::pluginDeps($name) { - copyFileWithLinks $::portableDir/SQLiteStudio $pkgDir/data $f - } - } - file copy $packageXml $pkgDir/meta/ - - mapFile $pkgDir/meta/package.xml [list %VERSION% $ver %DATE% $::releaseDate] - - puts "Compressing $name." - archiveContentsOf $pkgDir/data $name.7z -} - -proc copyPluginPkgs {targetDir} { - set packageXml ../../Plugins/package.xml - set pluginsPkg $targetDir/packages/pl.com.salsoft.sqlitestudio.plugins - file mkdir $pluginsPkg $pluginsPkg/data $pluginsPkg/meta - file copy $packageXml $pluginsPkg/meta/ - mapFile $pluginsPkg/meta/package.xml [list %DATE% $::releaseDate] - - foreach p $::plugins { - copyPluginPkg $p $targetDir - } -} - -if {$argc < 1 || $argc > 2} { - puts stderr "Usage: $argv0 \[--repo]" - exit 1 -} - -if {[catch {exec archivegen --help} res] && [string first "Usage: archivegen" $res] == -1} { - puts stderr "archivegen (QtIFW) missing in PATH." - exit 1 -} - -defineGlobalVars - -if {![file exists $::portableDir]} { - puts stderr "[file normalize $::portableDir] does not exist" - exit 1 -} - -collectPlugins -initDirs $targetDir -copyConfig $targetDir -copyMainPkg $targetDir -copyQtPkg $targetDir -copyPluginPkgs $targetDir -createOutputBinary $targetDir - -if {$argc >= 2 && [lindex $argv 1] == "--repo"} { - createOutputRepo $targetDir -} diff --git a/scripts/installer/config/bg.png b/scripts/installer/config/bg.png deleted file mode 100644 index 9140aa2..0000000 Binary files a/scripts/installer/config/bg.png and /dev/null differ diff --git a/scripts/installer/config/config.xml b/scripts/installer/config/config.xml deleted file mode 100644 index f52ba25..0000000 --- a/scripts/installer/config/config.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - SQLiteStudio - %SQLITESTUDIO_VERSION% - SQLiteStudio Setup - SalSoft - http://sqlitestudio.pl - sqlitestudio - sqlitestudio - logo.png - bg.png - @TargetDir@/%FINAL_EXECUTABLE% - - - Run SQLiteStudio - SQLiteStudio - UpdateSQLiteStudio - true - %WIZARD_STYLE% - %TARGET_DIR% - controller.qs - - - http://sqlitestudio.pl/update-repo/%UPDATE_ARCH%/ - - - \ No newline at end of file diff --git a/scripts/installer/config/controller.qs b/scripts/installer/config/controller.qs deleted file mode 100644 index 839c499..0000000 --- a/scripts/installer/config/controller.qs +++ /dev/null @@ -1,5 +0,0 @@ -function Controller() { - if (installer.isInstaller()) { - installer.setDefaultPageVisible(QInstaller.StartMenuSelection, false); - } -} diff --git a/scripts/installer/config/logo.png b/scripts/installer/config/logo.png deleted file mode 100644 index f125df8..0000000 Binary files a/scripts/installer/config/logo.png and /dev/null differ diff --git a/scripts/installer/config/sqlitestudio.icns b/scripts/installer/config/sqlitestudio.icns deleted file mode 100644 index 70ac0cd..0000000 Binary files a/scripts/installer/config/sqlitestudio.icns and /dev/null differ diff --git a/scripts/installer/config/sqlitestudio.ico b/scripts/installer/config/sqlitestudio.ico deleted file mode 100644 index 0aef62d..0000000 Binary files a/scripts/installer/config/sqlitestudio.ico and /dev/null differ diff --git a/scripts/installer/config/watermark.png b/scripts/installer/config/watermark.png deleted file mode 100644 index 9140aa2..0000000 Binary files a/scripts/installer/config/watermark.png and /dev/null differ diff --git a/scripts/macosx/compile_build_bundle.sh b/scripts/macosx/compile_build_bundle.sh index 0165ebb..86c2982 100755 --- a/scripts/macosx/compile_build_bundle.sh +++ b/scripts/macosx/compile_build_bundle.sh @@ -13,7 +13,7 @@ if [ "$1" == "" ]; then esac fi else - QMAKE=$1 + QMAKE="$1" fi realpath() { @@ -22,41 +22,41 @@ realpath() { cdir=`pwd` cpu_cores=`sysctl -n hw.logicalcpu` -absolute_path=`realpath $0` -this_dir=`dirname $absolute_path` -parent_dir=`dirname $this_dir` -parent_dir=`dirname $parent_dir` +absolute_path=`realpath "$0"` +this_dir=`dirname "$absolute_path"` +parent_dir=`dirname "$this_dir"` +parent_dir=`dirname "$parent_dir"` if [ "$2" == "" ]; then read -p "Number of CPU cores to use for compiling (hit enter to use $cpu_cores): " new_cpu_cores case $new_cpu_cores in "" ) break;; - * ) cpu_cores=$new_cpu_cores; break;; + * ) cpu_cores="$new_cpu_cores"; break;; esac else - cpu_cores=$2 + cpu_cores="$2" fi -if [ -d $parent_dir/output ]; then +if [ -d "$parent_dir/output" ]; then read -p "Directory $parent_dir/output already exists. The script will delete and recreate it. Is that okay? (y/N) : " yn - case $yn in - [Yy]* ) rm -rf $parent_dir/output; break;; + case "$yn" in + [Yy]* ) rm -rf "$parent_dir/output"; break;; * ) echo "Aborted."; exit;; esac fi -cd $parent_dir -mkdir output output/build output/build/Plugins +cd "$parent_dir" +mkdir -p output/build/Plugins cd output/build $QMAKE CONFIG+=portable ../../SQLiteStudio3 -make -j $cpu_cores +make -j "$cpu_cores" || exit 1 cd Plugins $QMAKE CONFIG+=portable ../../../Plugins -make -j $cpu_cores +make -j "$cpu_cores" || exit 1 cd .. make bundle -cd $cdir +cd "$cdir" diff --git a/win_deps/win32_deps.zip b/win_deps/win32_deps.zip new file mode 100644 index 0000000..9a506ee Binary files /dev/null and b/win_deps/win32_deps.zip differ diff --git a/win_deps/win64_deps.zip b/win_deps/win64_deps.zip new file mode 100644 index 0000000..c9b4dbe Binary files /dev/null and b/win_deps/win64_deps.zip differ -- cgit v1.2.3